mirror of
https://github.com/nzbget/nzbget.git
synced 2026-01-03 19:47:44 -05:00
Compare commits
735 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fada56ab2e | ||
|
|
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 |
2
AUTHORS
2
AUTHORS
@@ -1,4 +1,4 @@
|
||||
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.*)
|
||||
Andrey Prygunkov <hugbug@users.sourceforge.net> (versions 0.3.0 and later)
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* 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-2013 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -34,6 +34,7 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#ifdef WIN32
|
||||
#include <direct.h>
|
||||
#else
|
||||
@@ -52,6 +53,7 @@
|
||||
#include "Util.h"
|
||||
|
||||
extern DownloadSpeedMeter* g_pDownloadSpeedMeter;
|
||||
extern DownloadQueueHolder* g_pDownloadQueueHolder;
|
||||
extern Options* g_pOptions;
|
||||
extern ServerPool* g_pServerPool;
|
||||
|
||||
@@ -75,22 +77,10 @@ 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);
|
||||
}
|
||||
free(m_szTempFilename);
|
||||
free(m_szArticleFilename);
|
||||
free(m_szInfoName);
|
||||
free(m_szOutputFilename);
|
||||
}
|
||||
|
||||
void ArticleDownloader::SetTempFilename(const char* v)
|
||||
@@ -108,17 +98,35 @@ void ArticleDownloader::SetInfoName(const char * v)
|
||||
m_szInfoName = strdup(v);
|
||||
}
|
||||
|
||||
void ArticleDownloader::SetStatus(EStatus eStatus)
|
||||
{
|
||||
m_eStatus = eStatus;
|
||||
Notify(NULL);
|
||||
}
|
||||
/*
|
||||
* How server management (for one particular article) works:
|
||||
- there is a list of failed servers which is initially empty;
|
||||
- level is initially 0;
|
||||
|
||||
<loop>
|
||||
- request a connection from server pool for current level;
|
||||
Exception: this step is skipped for the very first download attempt, because a
|
||||
level-0 connection is initially passed from queue manager;
|
||||
- try to download from server;
|
||||
- if connection to server cannot be established or download fails due to interrupted connection,
|
||||
try again (as many times as needed without limit) the same server until connection is OK;
|
||||
- if download fails with error "Not-Found" (article or group not found) or with CRC error,
|
||||
add the server to failed server list;
|
||||
- if download fails with general failure error (article incomplete, other unknown error
|
||||
codes), try the same server again as many times as defined by option <Retries>; if all attempts
|
||||
fail, add the server to failed server list;
|
||||
- if all servers from current level were tried, increase level;
|
||||
- if all servers from all levels were tried, break the loop with failure status.
|
||||
<end-loop>
|
||||
*/
|
||||
void ArticleDownloader::Run()
|
||||
{
|
||||
debug("Entering ArticleDownloader-loop");
|
||||
|
||||
SetStatus(adRunning);
|
||||
|
||||
BuildOutputFilename();
|
||||
|
||||
m_szResultFilename = m_pArticleInfo->GetResultFilename();
|
||||
|
||||
if (g_pOptions->GetContinuePartial())
|
||||
@@ -127,54 +135,61 @@ void ArticleDownloader::Run()
|
||||
{
|
||||
// file exists from previous program's start
|
||||
detail("Article %s already downloaded, skipping", m_szInfoName);
|
||||
SetStatus(adFinished);
|
||||
FreeConnection(true);
|
||||
SetStatus(adFinished);
|
||||
Notify(NULL);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
detail("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();
|
||||
int iRetries = g_pOptions->GetRetries() > 0 ? g_pOptions->GetRetries() : 1;
|
||||
int iRemainedRetries = iRetries;
|
||||
Servers failedServers;
|
||||
failedServers.reserve(g_pServerPool->GetServers()->size());
|
||||
NewsServer* pWantServer = NULL;
|
||||
NewsServer* pLastServer = NULL;
|
||||
int iLevel = 0;
|
||||
int iServerConfigGeneration = g_pServerPool->GetGeneration();
|
||||
|
||||
while (!IsStopped())
|
||||
{
|
||||
Status = adFailed;
|
||||
|
||||
if (!m_pConnection)
|
||||
SetStatus(adWaiting);
|
||||
while (!m_pConnection && !(IsStopped() || iServerConfigGeneration != g_pServerPool->GetGeneration()))
|
||||
{
|
||||
m_pConnection = g_pServerPool->GetConnection(level, true);
|
||||
m_pConnection = g_pServerPool->GetConnection(iLevel, pWantServer, &failedServers);
|
||||
usleep(5 * 1000);
|
||||
}
|
||||
SetLastUpdateTimeNow();
|
||||
SetStatus(adRunning);
|
||||
|
||||
if (IsStopped())
|
||||
if (IsStopped() || g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2() ||
|
||||
iServerConfigGeneration != g_pServerPool->GetGeneration())
|
||||
{
|
||||
Status = adFailed;
|
||||
Status = adRetry;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!m_pConnection)
|
||||
{
|
||||
debug("m_pConnection is NULL");
|
||||
error("Serious error: Connection is NULL");
|
||||
}
|
||||
|
||||
pLastServer = m_pConnection->GetNewsServer();
|
||||
|
||||
m_pConnection->SetSuppressErrors(false);
|
||||
|
||||
// test connection
|
||||
bool bConnected = m_pConnection && m_pConnection->Connect() >= 0;
|
||||
bool bConnected = m_pConnection && m_pConnection->Connect();
|
||||
if (bConnected && !IsStopped())
|
||||
{
|
||||
// Okay, we got a Connection. Now start downloading.
|
||||
NewsServer* pNewsServer = m_pConnection->GetNewsServer();
|
||||
detail("Downloading %s @ %s (%s)", m_szInfoName, pNewsServer->GetName(), m_pConnection->GetHost());
|
||||
|
||||
Status = Download();
|
||||
|
||||
if (Status == adFinished || Status == adFailed || Status == adNotFound || Status == adCrcError)
|
||||
{
|
||||
m_ServerStats.SetStat(pNewsServer->GetID(), Status == adFinished ? 1 : 0, Status == adFinished ? 0 : 1, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (bConnected)
|
||||
@@ -193,93 +208,132 @@ void ArticleDownloader::Run()
|
||||
// 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);
|
||||
FreeConnection(Status == adFinished || Status == adNotFound);
|
||||
}
|
||||
}
|
||||
|
||||
if (((Status == adFailed) || (Status == adCrcError && g_pOptions->GetRetryOnCrcError())) &&
|
||||
(retry > 1 || !bConnected) && !IsStopped())
|
||||
if (Status == adFinished || Status == adFatalError)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
pWantServer = NULL;
|
||||
|
||||
if (bConnected && Status == adFailed)
|
||||
{
|
||||
iRemainedRetries--;
|
||||
}
|
||||
|
||||
if (!bConnected || (Status == adFailed && iRemainedRetries > 0))
|
||||
{
|
||||
pWantServer = pLastServer;
|
||||
}
|
||||
|
||||
if (pWantServer &&
|
||||
!(IsStopped() || g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2() ||
|
||||
iServerConfigGeneration != g_pServerPool->GetGeneration()))
|
||||
{
|
||||
detail("Waiting %i sec to retry", g_pOptions->GetRetryInterval());
|
||||
SetStatus(adWaiting);
|
||||
int msec = 0;
|
||||
while (!IsStopped() && (msec < g_pOptions->GetRetryInterval() * 1000))
|
||||
while (!(IsStopped() || g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2() ||
|
||||
iServerConfigGeneration != g_pServerPool->GetGeneration()) &&
|
||||
msec < g_pOptions->GetRetryInterval() * 1000)
|
||||
{
|
||||
usleep(100 * 1000);
|
||||
msec += 100;
|
||||
}
|
||||
SetLastUpdateTimeNow();
|
||||
SetStatus(adRunning);
|
||||
}
|
||||
|
||||
if (IsStopped())
|
||||
{
|
||||
Status = adFailed;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((Status == adFinished) || (Status == adFatalError) ||
|
||||
(Status == adCrcError && !g_pOptions->GetRetryOnCrcError()))
|
||||
if (IsStopped() || g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2() ||
|
||||
iServerConfigGeneration != g_pServerPool->GetGeneration())
|
||||
{
|
||||
Status = adRetry;
|
||||
break;
|
||||
}
|
||||
|
||||
LevelStatus[level] = Status;
|
||||
if (!pWantServer)
|
||||
{
|
||||
failedServers.push_back(pLastServer);
|
||||
|
||||
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;
|
||||
}
|
||||
// if all servers from current level were tried, increase level
|
||||
// if all servers from all levels were tried, break the loop with failure status
|
||||
|
||||
// do not count connect-errors, only article- and group-errors
|
||||
if (bConnected)
|
||||
{
|
||||
level++;
|
||||
if (level > iMaxLevel)
|
||||
bool bAllServersOnLevelFailed = true;
|
||||
for (Servers::iterator it = g_pServerPool->GetServers()->begin(); it != g_pServerPool->GetServers()->end(); it++)
|
||||
{
|
||||
level = 0;
|
||||
NewsServer* pCandidateServer = *it;
|
||||
if (pCandidateServer->GetNormLevel() == iLevel)
|
||||
{
|
||||
bool bServerFailed = !pCandidateServer->GetActive() || pCandidateServer->GetMaxConnections() == 0;
|
||||
if (!bServerFailed)
|
||||
{
|
||||
for (Servers::iterator it = failedServers.begin(); it != failedServers.end(); it++)
|
||||
{
|
||||
NewsServer* pIgnoreServer = *it;
|
||||
if (pIgnoreServer == pCandidateServer ||
|
||||
(pIgnoreServer->GetGroup() > 0 && pIgnoreServer->GetGroup() == pCandidateServer->GetGroup() &&
|
||||
pIgnoreServer->GetNormLevel() == pCandidateServer->GetNormLevel()))
|
||||
{
|
||||
bServerFailed = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!bServerFailed)
|
||||
{
|
||||
bAllServersOnLevelFailed = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
retry--;
|
||||
|
||||
if (bAllServersOnLevelFailed)
|
||||
{
|
||||
if (iLevel < g_pServerPool->GetMaxNormLevel())
|
||||
{
|
||||
detail("Article %s @ all level %i servers failed, increasing level", m_szInfoName, iLevel);
|
||||
iLevel++;
|
||||
}
|
||||
else
|
||||
{
|
||||
detail("Article %s @ all servers failed", m_szInfoName);
|
||||
Status = adFailed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
iRemainedRetries = iRetries;
|
||||
}
|
||||
}
|
||||
|
||||
FreeConnection(Status == adFinished);
|
||||
|
||||
free(LevelStatus);
|
||||
|
||||
if (m_bDuplicate)
|
||||
{
|
||||
Status = adFinished;
|
||||
}
|
||||
|
||||
if (Status != adFinished)
|
||||
if (Status != adFinished && Status != adRetry)
|
||||
{
|
||||
Status = adFailed;
|
||||
}
|
||||
|
||||
if (IsStopped())
|
||||
{
|
||||
detail("Download %s cancelled", m_szInfoName);
|
||||
Status = adRetry;
|
||||
}
|
||||
|
||||
if (Status == adFailed)
|
||||
{
|
||||
if (IsStopped())
|
||||
{
|
||||
detail("Download %s cancelled", m_szInfoName);
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Download %s failed", m_szInfoName);
|
||||
}
|
||||
detail("Download %s failed", m_szInfoName);
|
||||
}
|
||||
|
||||
SetStatus(Status);
|
||||
Notify(NULL);
|
||||
|
||||
debug("Exiting ArticleDownloader-loop");
|
||||
}
|
||||
@@ -289,20 +343,23 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
|
||||
const char* szResponse = NULL;
|
||||
EStatus Status = adRunning;
|
||||
|
||||
// at first, change group
|
||||
for (FileInfo::Groups::iterator it = m_pFileInfo->GetGroups()->begin(); it != m_pFileInfo->GetGroups()->end(); it++)
|
||||
if (m_pConnection->GetNewsServer()->GetJoinGroup())
|
||||
{
|
||||
szResponse = m_pConnection->JoinGroup(*it);
|
||||
if (szResponse && !strncmp(szResponse, "2", 1))
|
||||
// change group
|
||||
for (FileInfo::Groups::iterator it = m_pFileInfo->GetGroups()->begin(); it != m_pFileInfo->GetGroups()->end(); it++)
|
||||
{
|
||||
break;
|
||||
szResponse = m_pConnection->JoinGroup(*it);
|
||||
if (szResponse && !strncmp(szResponse, "2", 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Status = CheckResponse(szResponse, "could not join group");
|
||||
if (Status != adFinished)
|
||||
{
|
||||
return Status;
|
||||
Status = CheckResponse(szResponse, "could not join group");
|
||||
if (Status != adFinished)
|
||||
{
|
||||
return Status;
|
||||
}
|
||||
}
|
||||
|
||||
// retrieve article
|
||||
@@ -313,7 +370,7 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
|
||||
for (int retry = 3; retry > 0; retry--)
|
||||
{
|
||||
szResponse = m_pConnection->Request(tmp);
|
||||
if (szResponse && !strncmp(szResponse, "2", 1))
|
||||
if ((szResponse && !strncmp(szResponse, "2", 1)) || m_pConnection->GetAuthError())
|
||||
{
|
||||
break;
|
||||
}
|
||||
@@ -352,7 +409,7 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
|
||||
(g_pDownloadSpeedMeter->CalcCurrentDownloadSpeed() > g_pOptions->GetDownloadRate()))
|
||||
{
|
||||
SetLastUpdateTimeNow();
|
||||
usleep(200 * 1000);
|
||||
usleep(10 * 1000);
|
||||
}
|
||||
|
||||
int iLen = 0;
|
||||
@@ -364,7 +421,8 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
|
||||
{
|
||||
if (!IsStopped())
|
||||
{
|
||||
warn("Article %s @ %s failed: Unexpected end of article", m_szInfoName, m_pConnection->GetServer()->GetHost());
|
||||
detail("Article %s @ %s (%s) failed: Unexpected end of article", m_szInfoName,
|
||||
m_pConnection->GetNewsServer()->GetName(), m_pConnection->GetHost());
|
||||
}
|
||||
Status = adFailed;
|
||||
break;
|
||||
@@ -381,6 +439,7 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
|
||||
if (!strncmp(line, "..", 2))
|
||||
{
|
||||
line++;
|
||||
iLen--;
|
||||
}
|
||||
|
||||
if (!bBody)
|
||||
@@ -397,7 +456,8 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
|
||||
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);
|
||||
detail("Article %s @ %s (%s) failed: Wrong message-id, expected %s, returned %s", m_szInfoName,
|
||||
m_pConnection->GetNewsServer()->GetName(), m_pConnection->GetHost(), m_pArticleInfo->GetMessageID(), p);
|
||||
Status = adFailed;
|
||||
break;
|
||||
}
|
||||
@@ -425,7 +485,8 @@ ArticleDownloader::EStatus ArticleDownloader::Download()
|
||||
|
||||
if (!bEnd && Status == adRunning && !IsStopped())
|
||||
{
|
||||
warn("Article %s @ %s failed: article incomplete", m_szInfoName, m_pConnection->GetServer()->GetHost());
|
||||
detail("Article %s @ %s (%s) failed: article incomplete", m_szInfoName,
|
||||
m_pConnection->GetNewsServer()->GetName(), m_pConnection->GetHost());
|
||||
Status = adFailed;
|
||||
}
|
||||
|
||||
@@ -452,18 +513,21 @@ ArticleDownloader::EStatus ArticleDownloader::CheckResponse(const char* szRespon
|
||||
{
|
||||
if (!IsStopped())
|
||||
{
|
||||
warn("Article %s @ %s failed, %s: Connection closed by remote host", m_szInfoName, m_pConnection->GetServer()->GetHost(), szComment);
|
||||
detail("Article %s @ %s (%s) failed, %s: Connection closed by remote host", m_szInfoName,
|
||||
m_pConnection->GetNewsServer()->GetName(), m_pConnection->GetHost(), szComment);
|
||||
}
|
||||
return adConnectError;
|
||||
}
|
||||
else if (m_pConnection->GetAuthError() || !strncmp(szResponse, "400", 3) || !strncmp(szResponse, "499", 3))
|
||||
{
|
||||
warn("Article %s @ %s failed, %s: %s", m_szInfoName, m_pConnection->GetServer()->GetHost(), szComment, szResponse);
|
||||
detail("Article %s @ %s (%s) failed, %s: %s", m_szInfoName,
|
||||
m_pConnection->GetNewsServer()->GetName(), m_pConnection->GetHost(), szComment, szResponse);
|
||||
return adConnectError;
|
||||
}
|
||||
else if (!strncmp(szResponse, "41", 2) || !strncmp(szResponse, "42", 2))
|
||||
else if (!strncmp(szResponse, "41", 2) || !strncmp(szResponse, "42", 2) || !strncmp(szResponse, "43", 2))
|
||||
{
|
||||
warn("Article %s @ %s failed, %s: %s", m_szInfoName, m_pConnection->GetServer()->GetHost(), szComment, szResponse);
|
||||
detail("Article %s @ %s (%s) failed, %s: %s", m_szInfoName,
|
||||
m_pConnection->GetNewsServer()->GetName(), m_pConnection->GetHost(), szComment, szResponse);
|
||||
return adNotFound;
|
||||
}
|
||||
else if (!strncmp(szResponse, "2", 1))
|
||||
@@ -474,7 +538,8 @@ ArticleDownloader::EStatus ArticleDownloader::CheckResponse(const char* szRespon
|
||||
else
|
||||
{
|
||||
// unknown error, no special handling
|
||||
warn("Article %s @ %s failed, %s: %s", m_szInfoName, m_pConnection->GetServer()->GetHost(), szComment, szResponse);
|
||||
detail("Article %s @ %s (%s) failed, %s: %s", m_szInfoName,
|
||||
m_pConnection->GetNewsServer()->GetName(), m_pConnection->GetHost(), szComment, szResponse);
|
||||
return adFailed;
|
||||
}
|
||||
}
|
||||
@@ -488,18 +553,26 @@ bool ArticleDownloader::Write(char* szLine, int iLen)
|
||||
|
||||
if (g_pOptions->GetDecode())
|
||||
{
|
||||
bool bOK = false;
|
||||
if (m_eFormat == Decoder::efYenc)
|
||||
{
|
||||
return m_YDecoder.Write(szLine, iLen, m_pOutFile);
|
||||
bOK = m_YDecoder.Write(szLine, iLen, m_pOutFile);
|
||||
}
|
||||
else if (m_eFormat == Decoder::efUx)
|
||||
{
|
||||
return m_UDecoder.Write(szLine, iLen, m_pOutFile);
|
||||
bOK = m_UDecoder.Write(szLine, iLen, m_pOutFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
detail("Decoding %s failed: unsupported encoding", m_szInfoName);
|
||||
return false;
|
||||
}
|
||||
if (!bOK)
|
||||
{
|
||||
debug("Failed line: %s", szLine);
|
||||
detail("Decoding %s failed", m_szInfoName);
|
||||
}
|
||||
return bOK;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -516,10 +589,13 @@ bool ArticleDownloader::PrepareFile(char* szLine)
|
||||
{
|
||||
if (!strncmp(szLine, "=ybegin ", 8))
|
||||
{
|
||||
if (g_pOptions->GetDupeCheck())
|
||||
if (g_pOptions->GetDupeCheck() &&
|
||||
m_pFileInfo->GetNZBInfo()->GetDupeMode() != dmForce &&
|
||||
!m_pFileInfo->GetNZBInfo()->GetManyDupeFiles())
|
||||
{
|
||||
m_pFileInfo->LockOutputFile();
|
||||
if (!m_pFileInfo->GetOutputInitialized())
|
||||
bool bOutputInitialized = m_pFileInfo->GetOutputInitialized();
|
||||
if (!bOutputInitialized)
|
||||
{
|
||||
char* pb = strstr(szLine, " name=");
|
||||
if (pb)
|
||||
@@ -533,11 +609,6 @@ bool ArticleDownloader::PrepareFile(char* szLine)
|
||||
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())
|
||||
@@ -545,6 +616,12 @@ bool ArticleDownloader::PrepareFile(char* szLine)
|
||||
m_pFileInfo->SetOutputInitialized(true);
|
||||
}
|
||||
m_pFileInfo->UnlockOutputFile();
|
||||
if (!bOutputInitialized && m_szArticleFilename &&
|
||||
Util::FileExists(m_pFileInfo->GetNZBInfo()->GetDestDir(), m_szArticleFilename))
|
||||
{
|
||||
m_bDuplicate = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_pOptions->GetDirectWrite())
|
||||
@@ -557,9 +634,9 @@ bool ArticleDownloader::PrepareFile(char* szLine)
|
||||
{
|
||||
pb += 6; //=strlen(" size=")
|
||||
long iArticleFilesize = atol(pb);
|
||||
if (!Util::SetFileSize(m_szOutputFilename, iArticleFilesize))
|
||||
if (!CreateOutputFile(iArticleFilesize))
|
||||
{
|
||||
error("Could not create file %s!", m_szOutputFilename);
|
||||
m_pFileInfo->UnlockOutputFile();
|
||||
return false;
|
||||
}
|
||||
m_pFileInfo->SetOutputInitialized(true);
|
||||
@@ -583,10 +660,12 @@ bool ArticleDownloader::PrepareFile(char* szLine)
|
||||
{
|
||||
bool bDirectWrite = g_pOptions->GetDirectWrite() && m_eFormat == Decoder::efYenc;
|
||||
const char* szFilename = bDirectWrite ? m_szOutputFilename : m_szTempFilename;
|
||||
m_pOutFile = fopen(szFilename, bDirectWrite ? "r+" : "w");
|
||||
m_pOutFile = fopen(szFilename, bDirectWrite ? "rb+" : "wb");
|
||||
if (!m_pOutFile)
|
||||
{
|
||||
error("Could not %s file %s", bDirectWrite ? "open" : "create", szFilename);
|
||||
char szSysErrStr[256];
|
||||
error("Could not %s file %s! Errcode: %i, %s", bDirectWrite ? "open" : "create", szFilename,
|
||||
errno, Util::GetLastErrorMessage(szSysErrStr, sizeof(szSysErrStr)));
|
||||
return false;
|
||||
}
|
||||
if (g_pOptions->GetWriteBufferSize() == -1)
|
||||
@@ -602,6 +681,77 @@ bool ArticleDownloader::PrepareFile(char* szLine)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* creates output file and subdirectores */
|
||||
bool ArticleDownloader::CreateOutputFile(int iSize)
|
||||
{
|
||||
if (g_pOptions->GetDirectWrite() && Util::FileExists(m_szOutputFilename) &&
|
||||
Util::FileSize(m_szOutputFilename) == iSize)
|
||||
{
|
||||
// keep existing old file from previous program session
|
||||
return true;
|
||||
}
|
||||
|
||||
// delete eventually existing old file from previous program session
|
||||
remove(m_szOutputFilename);
|
||||
|
||||
// ensure the directory exist
|
||||
char szDestDir[1024];
|
||||
int iMaxlen = Util::BaseFileName(m_szOutputFilename) - m_szOutputFilename;
|
||||
if (iMaxlen > 1024-1) iMaxlen = 1024-1;
|
||||
strncpy(szDestDir, m_szOutputFilename, iMaxlen);
|
||||
szDestDir[iMaxlen] = '\0';
|
||||
char szErrBuf[1024];
|
||||
|
||||
if (!Util::ForceDirectories(szDestDir, szErrBuf, sizeof(szErrBuf)))
|
||||
{
|
||||
error("Could not create directory %s: %s", szDestDir, szErrBuf);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Util::CreateSparseFile(m_szOutputFilename, iSize))
|
||||
{
|
||||
error("Could not create file %s", m_szOutputFilename);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ArticleDownloader::BuildOutputFilename()
|
||||
{
|
||||
char szFilename[1024];
|
||||
|
||||
snprintf(szFilename, 1024, "%s%i.%03i", g_pOptions->GetTempDir(), m_pFileInfo->GetID(), m_pArticleInfo->GetPartNumber());
|
||||
szFilename[1024-1] = '\0';
|
||||
m_pArticleInfo->SetResultFilename(szFilename);
|
||||
|
||||
char tmpname[1024];
|
||||
snprintf(tmpname, 1024, "%s.tmp", szFilename);
|
||||
tmpname[1024-1] = '\0';
|
||||
SetTempFilename(tmpname);
|
||||
|
||||
if (g_pOptions->GetDirectWrite())
|
||||
{
|
||||
m_pFileInfo->LockOutputFile();
|
||||
|
||||
if (m_pFileInfo->GetOutputFilename())
|
||||
{
|
||||
strncpy(szFilename, m_pFileInfo->GetOutputFilename(), 1024);
|
||||
szFilename[1024-1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(szFilename, 1024, "%s%c%i.out.tmp", m_pFileInfo->GetNZBInfo()->GetDestDir(), (int)PATH_SEPARATOR, m_pFileInfo->GetID());
|
||||
szFilename[1024-1] = '\0';
|
||||
m_pFileInfo->SetOutputFilename(szFilename);
|
||||
}
|
||||
|
||||
m_pFileInfo->UnlockOutputFile();
|
||||
|
||||
SetOutputFilename(szFilename);
|
||||
}
|
||||
}
|
||||
|
||||
ArticleDownloader::EStatus ArticleDownloader::DecodeCheck()
|
||||
{
|
||||
bool bDirectWrite = g_pOptions->GetDirectWrite() && m_eFormat == Decoder::efYenc;
|
||||
@@ -622,8 +772,8 @@ ArticleDownloader::EStatus ArticleDownloader::DecodeCheck()
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Decoding %s failed: no binary data or unsupported encoding format", m_szInfoName);
|
||||
return adFatalError;
|
||||
detail("Decoding %s failed: no binary data or unsupported encoding format", m_szInfoName);
|
||||
return adFailed;
|
||||
}
|
||||
|
||||
Decoder::EStatus eStatus = pDecoder->Check();
|
||||
@@ -651,7 +801,7 @@ ArticleDownloader::EStatus ArticleDownloader::DecodeCheck()
|
||||
if (bDirectWrite && g_pOptions->GetContinuePartial())
|
||||
{
|
||||
// create empty flag-file to indicate that the artcile was downloaded
|
||||
FILE* flagfile = fopen(m_szResultFilename, "w");
|
||||
FILE* flagfile = fopen(m_szResultFilename, "wb");
|
||||
if (!flagfile)
|
||||
{
|
||||
error("Could not create file %s", m_szResultFilename);
|
||||
@@ -667,27 +817,27 @@ ArticleDownloader::EStatus ArticleDownloader::DecodeCheck()
|
||||
remove(m_szResultFilename);
|
||||
if (eStatus == Decoder::eCrcError)
|
||||
{
|
||||
warn("Decoding %s failed: CRC-Error", m_szInfoName);
|
||||
detail("Decoding %s failed: CRC-Error", m_szInfoName);
|
||||
return adCrcError;
|
||||
}
|
||||
else if (eStatus == Decoder::eArticleIncomplete)
|
||||
{
|
||||
warn("Decoding %s failed: article incomplete", m_szInfoName);
|
||||
detail("Decoding %s failed: article incomplete", m_szInfoName);
|
||||
return adFailed;
|
||||
}
|
||||
else if (eStatus == Decoder::eInvalidSize)
|
||||
{
|
||||
warn("Decoding %s failed: size mismatch", m_szInfoName);
|
||||
detail("Decoding %s failed: size mismatch", m_szInfoName);
|
||||
return adFailed;
|
||||
}
|
||||
else if (eStatus == Decoder::eNoBinaryData)
|
||||
{
|
||||
warn("Decoding %s failed: no binary data found", m_szInfoName);
|
||||
detail("Decoding %s failed: no binary data found", m_szInfoName);
|
||||
return adFailed;
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Decoding %s failed", m_szInfoName);
|
||||
detail("Decoding %s failed", m_szInfoName);
|
||||
return adFailed;
|
||||
}
|
||||
}
|
||||
@@ -726,6 +876,7 @@ void ArticleDownloader::Stop()
|
||||
m_mutexConnection.Lock();
|
||||
if (m_pConnection)
|
||||
{
|
||||
m_pConnection->SetSuppressErrors(true);
|
||||
m_pConnection->Cancel();
|
||||
}
|
||||
m_mutexConnection.Unlock();
|
||||
@@ -739,6 +890,7 @@ bool ArticleDownloader::Terminate()
|
||||
if (terminated && pConnection)
|
||||
{
|
||||
debug("Terminating connection");
|
||||
pConnection->SetSuppressErrors(true);
|
||||
pConnection->Cancel();
|
||||
pConnection->Disconnect();
|
||||
g_pServerPool->FreeConnection(pConnection, true);
|
||||
@@ -771,11 +923,18 @@ void ArticleDownloader::CompleteFileParts()
|
||||
|
||||
bool bDirectWrite = g_pOptions->GetDirectWrite() && m_pFileInfo->GetOutputInitialized();
|
||||
|
||||
char szNZBNiceName[1024];
|
||||
m_pFileInfo->GetNZBInfo()->GetNiceNZBName(szNZBNiceName, 1024);
|
||||
char szNZBName[1024];
|
||||
char szNZBDestDir[1024];
|
||||
// the locking is needed for accessing the memebers of NZBInfo
|
||||
g_pDownloadQueueHolder->LockQueue();
|
||||
strncpy(szNZBName, m_pFileInfo->GetNZBInfo()->GetName(), 1024);
|
||||
strncpy(szNZBDestDir, m_pFileInfo->GetNZBInfo()->GetDestDir(), 1024);
|
||||
g_pDownloadQueueHolder->UnlockQueue();
|
||||
szNZBName[1024-1] = '\0';
|
||||
szNZBDestDir[1024-1] = '\0';
|
||||
|
||||
char InfoFilename[1024];
|
||||
snprintf(InfoFilename, 1024, "%s%c%s", szNZBNiceName, (int)PATH_SEPARATOR, m_pFileInfo->GetFilename());
|
||||
snprintf(InfoFilename, 1024, "%s%c%s", szNZBName, (int)PATH_SEPARATOR, m_pFileInfo->GetFilename());
|
||||
InfoFilename[1024-1] = '\0';
|
||||
|
||||
if (!g_pOptions->GetDecode())
|
||||
@@ -791,26 +950,17 @@ void ArticleDownloader::CompleteFileParts()
|
||||
detail("Joining articles for %s", InfoFilename);
|
||||
}
|
||||
|
||||
char ofn[1024];
|
||||
snprintf(ofn, 1024, "%s%c%s", m_pFileInfo->GetNZBInfo()->GetDestDir(), (int)PATH_SEPARATOR, m_pFileInfo->GetFilename());
|
||||
ofn[1024-1] = '\0';
|
||||
|
||||
// Ensure the DstDir is created
|
||||
if (!Util::CreateDirectory(m_pFileInfo->GetNZBInfo()->GetDestDir()))
|
||||
char szErrBuf[1024];
|
||||
if (!Util::ForceDirectories(szNZBDestDir, szErrBuf, sizeof(szErrBuf)))
|
||||
{
|
||||
error("Could not create directory %s! Errcode: %i", m_pFileInfo->GetNZBInfo()->GetDestDir(), errno);
|
||||
error("Could not create directory %s: %s", szNZBDestDir, szErrBuf);
|
||||
SetStatus(adJoined);
|
||||
return;
|
||||
}
|
||||
|
||||
// prevent overwriting existing files
|
||||
int dupcount = 0;
|
||||
while (Util::FileExists(ofn))
|
||||
{
|
||||
dupcount++;
|
||||
snprintf(ofn, 1024, "%s%c%s_duplicate%d", m_pFileInfo->GetNZBInfo()->GetDestDir(), (int)PATH_SEPARATOR, m_pFileInfo->GetFilename(), dupcount);
|
||||
ofn[1024-1] = '\0';
|
||||
}
|
||||
char ofn[1024];
|
||||
Util::MakeUniqueFilename(ofn, 1024, szNZBDestDir, m_pFileInfo->GetFilename());
|
||||
|
||||
FILE* outfile = NULL;
|
||||
char tmpdestfile[1024];
|
||||
@@ -820,7 +970,7 @@ void ArticleDownloader::CompleteFileParts()
|
||||
if (g_pOptions->GetDecode() && !bDirectWrite)
|
||||
{
|
||||
remove(tmpdestfile);
|
||||
outfile = fopen(tmpdestfile, "w+");
|
||||
outfile = fopen(tmpdestfile, "wb+");
|
||||
if (!outfile)
|
||||
{
|
||||
error("Could not create file %s!", tmpdestfile);
|
||||
@@ -870,7 +1020,7 @@ void ArticleDownloader::CompleteFileParts()
|
||||
FILE* infile;
|
||||
const char* fn = pa->GetResultFilename();
|
||||
|
||||
infile = fopen(fn, "r");
|
||||
infile = fopen(fn, "rb");
|
||||
if (infile)
|
||||
{
|
||||
int cnt = BUFFER_SIZE;
|
||||
@@ -904,10 +1054,7 @@ void ArticleDownloader::CompleteFileParts()
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer)
|
||||
{
|
||||
free(buffer);
|
||||
}
|
||||
free(buffer);
|
||||
|
||||
if (outfile)
|
||||
{
|
||||
@@ -924,6 +1071,24 @@ void ArticleDownloader::CompleteFileParts()
|
||||
{
|
||||
error("Could not move file %s to %s! Errcode: %i", m_szOutputFilename, ofn, errno);
|
||||
}
|
||||
|
||||
// if destination directory was changed delete the old directory (if empty)
|
||||
int iLen = strlen(szNZBDestDir);
|
||||
if (!(!strncmp(szNZBDestDir, m_szOutputFilename, iLen) &&
|
||||
(m_szOutputFilename[iLen] == PATH_SEPARATOR || m_szOutputFilename[iLen] == ALT_PATH_SEPARATOR)))
|
||||
{
|
||||
debug("Checking old dir for: %s", m_szOutputFilename);
|
||||
char szOldDestDir[1024];
|
||||
int iMaxlen = Util::BaseFileName(m_szOutputFilename) - m_szOutputFilename;
|
||||
if (iMaxlen > 1024-1) iMaxlen = 1024-1;
|
||||
strncpy(szOldDestDir, m_szOutputFilename, iMaxlen);
|
||||
szOldDestDir[iMaxlen] = '\0';
|
||||
if (Util::DirEmpty(szOldDestDir))
|
||||
{
|
||||
debug("Deleting old dir: %s", szOldDestDir);
|
||||
rmdir(szOldDestDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!bDirectWrite || g_pOptions->GetContinuePartial())
|
||||
@@ -941,37 +1106,141 @@ void ArticleDownloader::CompleteFileParts()
|
||||
}
|
||||
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';
|
||||
if (Util::MoveFile(ofn, brokenfn))
|
||||
{
|
||||
detail("Renaming broken file from %s to %s", ofn, brokenfn);
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Renaming broken file from %s to %s failed", ofn, brokenfn);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
detail("Not renaming broken file %s", ofn);
|
||||
}
|
||||
warn("%i of %i article downloads failed for \"%s\"",
|
||||
iBrokenCount + m_pFileInfo->GetMissedArticles(),
|
||||
m_pFileInfo->GetTotalArticles(), InfoFilename);
|
||||
|
||||
if (g_pOptions->GetCreateBrokenLog())
|
||||
{
|
||||
char szBrokenLogName[1024];
|
||||
snprintf(szBrokenLogName, 1024, "%s%c_brokenlog.txt", m_pFileInfo->GetNZBInfo()->GetDestDir(), (int)PATH_SEPARATOR);
|
||||
snprintf(szBrokenLogName, 1024, "%s%c_brokenlog.txt", szNZBDestDir, (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());
|
||||
FILE* file = fopen(szBrokenLogName, "ab");
|
||||
fprintf(file, "%s (%i/%i)%s", m_pFileInfo->GetFilename(),
|
||||
m_pFileInfo->GetTotalArticles() - iBrokenCount - m_pFileInfo->GetMissedArticles(),
|
||||
m_pFileInfo->GetTotalArticles(), LINE_ENDING);
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
|
||||
// the locking is needed for accessing the members of NZBInfo
|
||||
g_pDownloadQueueHolder->LockQueue();
|
||||
m_pFileInfo->GetNZBInfo()->GetCompletedFiles()->push_back(strdup(ofn));
|
||||
if (strcmp(m_pFileInfo->GetNZBInfo()->GetDestDir(), szNZBDestDir))
|
||||
{
|
||||
// destination directory was changed during completion, need to move the file
|
||||
MoveCompletedFiles(m_pFileInfo->GetNZBInfo(), szNZBDestDir);
|
||||
}
|
||||
g_pDownloadQueueHolder->UnlockQueue();
|
||||
|
||||
SetStatus(adJoined);
|
||||
}
|
||||
|
||||
bool ArticleDownloader::MoveCompletedFiles(NZBInfo* pNZBInfo, const char* szOldDestDir)
|
||||
{
|
||||
if (pNZBInfo->GetCompletedFiles()->empty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Ensure the DstDir is created
|
||||
char szErrBuf[1024];
|
||||
if (!Util::ForceDirectories(pNZBInfo->GetDestDir(), szErrBuf, sizeof(szErrBuf)))
|
||||
{
|
||||
error("Could not create directory %s: %s", pNZBInfo->GetDestDir(), szErrBuf);
|
||||
return false;
|
||||
}
|
||||
|
||||
// move already downloaded files to new destination
|
||||
for (NZBInfo::Files::iterator it = pNZBInfo->GetCompletedFiles()->begin(); it != pNZBInfo->GetCompletedFiles()->end(); it++)
|
||||
{
|
||||
char* szFileName = *it;
|
||||
char szNewFileName[1024];
|
||||
snprintf(szNewFileName, 1024, "%s%c%s", pNZBInfo->GetDestDir(), (int)PATH_SEPARATOR, Util::BaseFileName(szFileName));
|
||||
szNewFileName[1024-1] = '\0';
|
||||
|
||||
// check if file was not moved already
|
||||
if (strcmp(szFileName, szNewFileName))
|
||||
{
|
||||
// prevent overwriting of existing files
|
||||
Util::MakeUniqueFilename(szNewFileName, 1024, pNZBInfo->GetDestDir(), Util::BaseFileName(szFileName));
|
||||
|
||||
detail("Moving file %s to %s", szFileName, szNewFileName);
|
||||
if (Util::MoveFile(szFileName, szNewFileName))
|
||||
{
|
||||
free(szFileName);
|
||||
*it = strdup(szNewFileName);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not move file %s to %s! Errcode: %i", szFileName, szNewFileName, errno);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// move brokenlog.txt
|
||||
if (g_pOptions->GetCreateBrokenLog())
|
||||
{
|
||||
char szOldBrokenLogName[1024];
|
||||
snprintf(szOldBrokenLogName, 1024, "%s%c_brokenlog.txt", szOldDestDir, (int)PATH_SEPARATOR);
|
||||
szOldBrokenLogName[1024-1] = '\0';
|
||||
if (Util::FileExists(szOldBrokenLogName))
|
||||
{
|
||||
char szBrokenLogName[1024];
|
||||
snprintf(szBrokenLogName, 1024, "%s%c_brokenlog.txt", pNZBInfo->GetDestDir(), (int)PATH_SEPARATOR);
|
||||
szBrokenLogName[1024-1] = '\0';
|
||||
|
||||
detail("Moving file %s to %s", szOldBrokenLogName, szBrokenLogName);
|
||||
if (Util::FileExists(szBrokenLogName))
|
||||
{
|
||||
// copy content to existing new file, then delete old file
|
||||
FILE* outfile;
|
||||
outfile = fopen(szBrokenLogName, "ab");
|
||||
if (outfile)
|
||||
{
|
||||
FILE* infile;
|
||||
infile = fopen(szOldBrokenLogName, "rb");
|
||||
if (infile)
|
||||
{
|
||||
static const int BUFFER_SIZE = 1024 * 50;
|
||||
int cnt = BUFFER_SIZE;
|
||||
char* buffer = (char*)malloc(BUFFER_SIZE);
|
||||
while (cnt == BUFFER_SIZE)
|
||||
{
|
||||
cnt = (int)fread(buffer, 1, BUFFER_SIZE, infile);
|
||||
fwrite(buffer, 1, cnt, outfile);
|
||||
}
|
||||
fclose(infile);
|
||||
free(buffer);
|
||||
remove(szOldBrokenLogName);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not open file %s", szOldBrokenLogName);
|
||||
}
|
||||
fclose(outfile);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not open file %s", szBrokenLogName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// move to new destination
|
||||
if (!Util::MoveFile(szOldBrokenLogName, szBrokenLogName))
|
||||
{
|
||||
error("Could not move file %s to %s! Errcode: %i", szOldBrokenLogName, szBrokenLogName, errno);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// delete old directory (if empty)
|
||||
if (Util::DirEmpty(szOldDestDir))
|
||||
{
|
||||
rmdir(szOldDestDir);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* 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-2013 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -28,9 +28,6 @@
|
||||
#define ARTICLEDOWNLOADER_H
|
||||
|
||||
#include <time.h>
|
||||
#ifdef WIN32
|
||||
#include <sys/timeb.h>
|
||||
#endif
|
||||
|
||||
#include "Observer.h"
|
||||
#include "DownloadInfo.h"
|
||||
@@ -45,9 +42,10 @@ public:
|
||||
{
|
||||
adUndefined,
|
||||
adRunning,
|
||||
adWaiting,
|
||||
adFinished,
|
||||
adFailed,
|
||||
adDecodeError,
|
||||
adRetry,
|
||||
adCrcError,
|
||||
adDecoding,
|
||||
adJoining,
|
||||
@@ -74,13 +72,20 @@ private:
|
||||
UDecoder m_UDecoder;
|
||||
FILE* m_pOutFile;
|
||||
bool m_bDuplicate;
|
||||
ServerStatList m_ServerStats;
|
||||
|
||||
EStatus Download();
|
||||
bool Write(char* szLine, int iLen);
|
||||
bool PrepareFile(char* szLine);
|
||||
bool CreateOutputFile(int iSize);
|
||||
void BuildOutputFilename();
|
||||
EStatus DecodeCheck();
|
||||
void FreeConnection(bool bKeepConnected);
|
||||
EStatus CheckResponse(const char* szResponse, const char* szComment);
|
||||
void SetStatus(EStatus eStatus) { m_eStatus = eStatus; }
|
||||
const char* GetTempFilename() { return m_szTempFilename; }
|
||||
void SetTempFilename(const char* v);
|
||||
void SetOutputFilename(const char* v);
|
||||
|
||||
public:
|
||||
ArticleDownloader();
|
||||
@@ -89,20 +94,18 @@ public:
|
||||
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; }
|
||||
ServerStatList* GetServerStats() { return &m_ServerStats; }
|
||||
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();
|
||||
static bool MoveCompletedFiles(NZBInfo* pNZBInfo, const char* szOldDestDir);
|
||||
void SetConnection(NNTPConnection* pConnection) { m_pConnection = pConnection; }
|
||||
|
||||
void LogDebugInfo();
|
||||
@@ -112,8 +115,8 @@ class DownloadSpeedMeter
|
||||
{
|
||||
public:
|
||||
virtual ~DownloadSpeedMeter() {};
|
||||
virtual float CalcCurrentDownloadSpeed() = 0;
|
||||
virtual int CalcCurrentDownloadSpeed() = 0;
|
||||
virtual void AddSpeedReading(int iBytes) = 0;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
821
BinRpc.cpp
821
BinRpc.cpp
File diff suppressed because it is too large
Load Diff
48
BinRpc.h
48
BinRpc.h
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
|
||||
* Copyright (C) 2007-2009 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -33,23 +33,21 @@
|
||||
class BinRpcProcessor
|
||||
{
|
||||
private:
|
||||
SOCKET m_iSocket;
|
||||
SNZBRequestBase m_MessageBase;
|
||||
const char* m_szClientIP;
|
||||
Connection* m_pConnection;
|
||||
|
||||
void Dispatch();
|
||||
|
||||
public:
|
||||
BinRpcProcessor();
|
||||
void Execute();
|
||||
void SetSocket(SOCKET iSocket) { m_iSocket = iSocket; }
|
||||
void SetSignature(int iSignature) { m_MessageBase.m_iSignature = iSignature; }
|
||||
void SetClientIP(const char* szClientIP) { m_szClientIP = szClientIP; }
|
||||
void SetConnection(Connection* pConnection) { m_pConnection = pConnection; }
|
||||
};
|
||||
|
||||
class BinCommand
|
||||
{
|
||||
protected:
|
||||
SOCKET m_iSocket;
|
||||
Connection* m_pConnection;
|
||||
SNZBRequestBase* m_pMessageBase;
|
||||
|
||||
bool ReceiveRequest(void* pBuffer, int iSize);
|
||||
@@ -58,7 +56,7 @@ protected:
|
||||
public:
|
||||
virtual ~BinCommand() {}
|
||||
virtual void Execute() = 0;
|
||||
void SetSocket(SOCKET iSocket) { m_iSocket = iSocket; }
|
||||
void SetConnection(Connection* pConnection) { m_pConnection = pConnection; }
|
||||
void SetMessageBase(SNZBRequestBase* pMessageBase) { m_pMessageBase = pMessageBase; }
|
||||
};
|
||||
|
||||
@@ -110,6 +108,12 @@ public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ReloadBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class VersionBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
@@ -128,4 +132,28 @@ public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ScanBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class HistoryBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class DownloadUrlBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class UrlQueueBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* 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-2010 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -74,11 +74,11 @@ void ColoredFrontend::PrintStatus()
|
||||
char tmp[1024];
|
||||
char timeString[100];
|
||||
timeString[0] = '\0';
|
||||
float fCurrentDownloadSpeed = m_bStandBy ? 0 : m_fCurrentDownloadSpeed;
|
||||
int iCurrentDownloadSpeed = m_bStandBy ? 0 : m_iCurrentDownloadSpeed;
|
||||
|
||||
if (fCurrentDownloadSpeed > 0.0 && !m_bPause)
|
||||
if (iCurrentDownloadSpeed > 0 && !(m_bPauseDownload || m_bPauseDownload2))
|
||||
{
|
||||
long long remain_sec = m_lRemainingSize / ((long long)(fCurrentDownloadSpeed * 1024));
|
||||
long long remain_sec = (long long)(m_lRemainingSize / iCurrentDownloadSpeed);
|
||||
int h = (int)(remain_sec / 3600);
|
||||
int m = (int)((remain_sec % 3600) / 60);
|
||||
int s = (int)(remain_sec % 60);
|
||||
@@ -86,9 +86,9 @@ void ColoredFrontend::PrintStatus()
|
||||
}
|
||||
|
||||
char szDownloadLimit[128];
|
||||
if (m_fDownloadLimit > 0.0f)
|
||||
if (m_iDownloadLimit > 0)
|
||||
{
|
||||
sprintf(szDownloadLimit, ", Limit %.0f KB/S", m_fDownloadLimit);
|
||||
sprintf(szDownloadLimit, ", Limit %.0f KB/s", (float)m_iDownloadLimit / 1024.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -109,12 +109,14 @@ void ColoredFrontend::PrintStatus()
|
||||
char* szControlSeq = "";
|
||||
#else
|
||||
printf("\033[s");
|
||||
char* szControlSeq = "\033[K";
|
||||
const 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)(Util::Int64ToFloat(m_lRemainingSize) / 1024.0 / 1024.0),
|
||||
timeString, szPostStatus, m_bPause ? (m_bStandBy ? ", Paused" : ", Pausing") : "", szDownloadLimit, szControlSeq);
|
||||
snprintf(tmp, 1024, " %d threads, %.*f KB/s, %.2f MB remaining%s%s%s%s%s\n",
|
||||
m_iThreadCount, (iCurrentDownloadSpeed >= 10*1024 ? 0 : 1), (float)iCurrentDownloadSpeed / 1024.0,
|
||||
(float)(Util::Int64ToFloat(m_lRemainingSize) / 1024.0 / 1024.0), timeString, szPostStatus,
|
||||
m_bPauseDownload || m_bPauseDownload2 ? (m_bStandBy ? ", Paused" : ", Pausing") : "",
|
||||
szDownloadLimit, szControlSeq);
|
||||
tmp[1024-1] = '\0';
|
||||
printf("%s", tmp);
|
||||
m_bNeedGoBack = true;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
|
||||
832
Connection.cpp
832
Connection.cpp
File diff suppressed because it is too large
Load Diff
84
Connection.h
84
Connection.h
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* 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-2013 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -27,7 +27,14 @@
|
||||
#ifndef CONNECTION_H
|
||||
#define CONNECTION_H
|
||||
|
||||
#include "NetAddress.h"
|
||||
#ifndef HAVE_GETADDRINFO
|
||||
#ifndef HAVE_GETHOSTBYNAME_R
|
||||
#include "Thread.h"
|
||||
#endif
|
||||
#endif
|
||||
#ifndef DISABLE_TLS
|
||||
#include "TLS.h"
|
||||
#endif
|
||||
|
||||
class Connection
|
||||
{
|
||||
@@ -39,49 +46,72 @@ public:
|
||||
csListening,
|
||||
csCancelled
|
||||
};
|
||||
|
||||
|
||||
protected:
|
||||
NetAddress* m_pNetAddress;
|
||||
char* m_szHost;
|
||||
int m_iPort;
|
||||
SOCKET m_iSocket;
|
||||
bool m_bTLS;
|
||||
char* m_szCipher;
|
||||
char* m_szReadBuf;
|
||||
int m_iBufAvail;
|
||||
char* m_szBufPtr;
|
||||
EStatus m_eStatus;
|
||||
int m_iTimeout;
|
||||
bool m_bSuppressErrors;
|
||||
bool m_bAutoClose;
|
||||
char m_szRemoteAddr[20];
|
||||
#ifndef DISABLE_TLS
|
||||
TLSSocket* m_pTLSSocket;
|
||||
bool m_bTLSError;
|
||||
#endif
|
||||
#ifndef HAVE_GETADDRINFO
|
||||
#ifndef HAVE_GETHOSTBYNAME_R
|
||||
static Mutex* m_pMutexGetHostByName;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Connection(SOCKET iSocket, bool bTLS);
|
||||
void ReportError(const char* szMsgPrefix, const char* szMsgArg, bool PrintErrCode, int herrno);
|
||||
bool DoConnect();
|
||||
bool DoDisconnect();
|
||||
#ifndef HAVE_GETADDRINFO
|
||||
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(const char* pBuffer);
|
||||
char* DoReadLine(char* pBuffer, int iSize, int* pBytesRead);
|
||||
SOCKET DoAccept();
|
||||
#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
|
||||
|
||||
public:
|
||||
Connection(NetAddress* pNetAddress);
|
||||
Connection(SOCKET iSocket, bool bAutoClose);
|
||||
Connection(const char* szHost, int iPort, bool bTLS);
|
||||
virtual ~Connection();
|
||||
static void Init();
|
||||
static void Final();
|
||||
int Connect();
|
||||
int Disconnect();
|
||||
int Bind();
|
||||
int Send(const char* pBuffer, int iSize);
|
||||
int Recv(char* pBuffer, int iSize);
|
||||
bool RecvAll(char* pBuffer, int iSize);
|
||||
virtual bool Connect();
|
||||
virtual bool Disconnect();
|
||||
bool Bind();
|
||||
bool Send(const char* pBuffer, int iSize);
|
||||
bool Recv(char* pBuffer, int iSize);
|
||||
int TryRecv(char* pBuffer, int iSize);
|
||||
char* ReadLine(char* pBuffer, int iSize, int* pBytesRead);
|
||||
void ReadBuffer(char** pBuffer, int *iBufLen);
|
||||
int WriteLine(const char* pBuffer);
|
||||
SOCKET Accept();
|
||||
Connection* Accept();
|
||||
void Cancel();
|
||||
NetAddress* GetServer() { return m_pNetAddress; }
|
||||
SOCKET GetSocket() { return m_iSocket; }
|
||||
const char* GetHost() { return m_szHost; }
|
||||
int GetPort() { return m_iPort; }
|
||||
bool GetTLS() { return m_bTLS; }
|
||||
const char* GetCipher() { return m_szCipher; }
|
||||
void SetCipher(const char* szCipher);
|
||||
void SetTimeout(int iTimeout) { m_iTimeout = iTimeout; }
|
||||
EStatus GetStatus() { return m_eStatus; }
|
||||
void SetSuppressErrors(bool bSuppressErrors) { m_bSuppressErrors = bSuppressErrors; }
|
||||
void SetSuppressErrors(bool bSuppressErrors);
|
||||
bool GetSuppressErrors() { return m_bSuppressErrors; }
|
||||
const char* GetRemoteAddr();
|
||||
#ifndef DISABLE_TLS
|
||||
bool StartTLS(bool bIsClient, const char* szCertFile, const char* szKeyFile);
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
30
Decoder.cpp
30
Decoder.cpp
@@ -2,7 +2,7 @@
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -60,18 +60,12 @@ Decoder::~ Decoder()
|
||||
{
|
||||
debug("Destroying Decoder");
|
||||
|
||||
if (m_szArticleFilename)
|
||||
{
|
||||
free(m_szArticleFilename);
|
||||
}
|
||||
free(m_szArticleFilename);
|
||||
}
|
||||
|
||||
void Decoder::Clear()
|
||||
{
|
||||
if (m_szArticleFilename)
|
||||
{
|
||||
free(m_szArticleFilename);
|
||||
}
|
||||
free(m_szArticleFilename);
|
||||
m_szArticleFilename = NULL;
|
||||
}
|
||||
|
||||
@@ -141,7 +135,7 @@ void YDecoder::Clear()
|
||||
m_lExpectedCRC = 0;
|
||||
m_lCalculatedCRC = 0xFFFFFFFF;
|
||||
m_iBegin = 0;
|
||||
m_iEnd = 0;
|
||||
m_iEnd = 0xFFFFFFFF;
|
||||
m_iSize = 0;
|
||||
m_iEndSize = 0;
|
||||
m_bAutoSeek = false;
|
||||
@@ -268,10 +262,7 @@ BreakLoop:
|
||||
pb += 6; //=strlen(" name=")
|
||||
char* pe;
|
||||
for (pe = pb; *pe != '\0' && *pe != '\n' && *pe != '\r'; pe++) ;
|
||||
if (m_szArticleFilename)
|
||||
{
|
||||
free(m_szArticleFilename);
|
||||
}
|
||||
free(m_szArticleFilename);
|
||||
m_szArticleFilename = (char*)malloc(pe - pb + 1);
|
||||
strncpy(m_szArticleFilename, pb, pe - pb);
|
||||
m_szArticleFilename[pe - pb] = '\0';
|
||||
@@ -319,7 +310,7 @@ bool YDecoder::Write(char* buffer, int len, FILE* outfile)
|
||||
{
|
||||
if (m_bNeedSetPos)
|
||||
{
|
||||
if (m_iBegin == 0 || m_iEnd == 0 || !outfile)
|
||||
if (m_iBegin == 0 || m_iEnd == 0xFFFFFFFF || !outfile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -404,10 +395,7 @@ unsigned int UDecoder::DecodeBuffer(char* buffer, int len)
|
||||
// extracting filename
|
||||
char* pe;
|
||||
for (pe = pb; *pe != '\0' && *pe != '\n' && *pe != '\r'; pe++) ;
|
||||
if (m_szArticleFilename)
|
||||
{
|
||||
free(m_szArticleFilename);
|
||||
}
|
||||
free(m_szArticleFilename);
|
||||
m_szArticleFilename = (char*)malloc(pe - pb + 1);
|
||||
strncpy(m_szArticleFilename, pb, pe - pb);
|
||||
m_szArticleFilename[pe - pb] = '\0';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* 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-2008 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -27,8 +27,6 @@
|
||||
#ifndef DECODER_H
|
||||
#define DECODER_H
|
||||
|
||||
#include "Thread.h"
|
||||
|
||||
class Decoder
|
||||
{
|
||||
public:
|
||||
|
||||
2032
DiskState.cpp
2032
DiskState.cpp
File diff suppressed because it is too large
Load Diff
47
DiskState.h
47
DiskState.h
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2013 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,7 +15,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -27,26 +27,53 @@
|
||||
#define DISKSTATE_H
|
||||
|
||||
#include "DownloadInfo.h"
|
||||
#include "PostInfo.h"
|
||||
#include "FeedInfo.h"
|
||||
#include "NewsServer.h"
|
||||
|
||||
class DiskState
|
||||
{
|
||||
private:
|
||||
int fscanf(FILE* infile, const char* Format, ...);
|
||||
int ParseFormatVersion(const char* szFormatSignature);
|
||||
bool SaveFileInfo(FileInfo* pFileInfo, const char* szFilename);
|
||||
bool LoadFileInfo(FileInfo* pFileInfo, const char* szFilename, bool bFileSummary, bool bArticles);
|
||||
|
||||
void SaveNZBList(DownloadQueue* pDownloadQueue, FILE* outfile);
|
||||
bool LoadNZBList(DownloadQueue* pDownloadQueue, FILE* infile, int iFormatVersion);
|
||||
void SaveFileQueue(DownloadQueue* pDownloadQueue, FileQueue* pFileQueue, FILE* outfile);
|
||||
bool LoadFileQueue(DownloadQueue* pDownloadQueue, FileQueue* pFileQueue, FILE* infile, int iFormatVersion);
|
||||
void SavePostQueue(DownloadQueue* pDownloadQueue, FILE* outfile);
|
||||
bool LoadPostQueue(DownloadQueue* pDownloadQueue, FILE* infile, int iFormatVersion);
|
||||
bool LoadOldPostQueue(DownloadQueue* pDownloadQueue);
|
||||
void SaveUrlQueue(DownloadQueue* pDownloadQueue, FILE* outfile);
|
||||
bool LoadUrlQueue(DownloadQueue* pDownloadQueue, FILE* infile, int iFormatVersion);
|
||||
void SaveUrlInfo(UrlInfo* pUrlInfo, FILE* outfile);
|
||||
bool LoadUrlInfo(UrlInfo* pUrlInfo, FILE* infile, int iFormatVersion);
|
||||
void SaveDupInfo(DupInfo* pDupInfo, FILE* outfile);
|
||||
bool LoadDupInfo(DupInfo* pDupInfo, FILE* infile, int iFormatVersion);
|
||||
void SaveHistory(DownloadQueue* pDownloadQueue, FILE* outfile);
|
||||
bool LoadHistory(DownloadQueue* pDownloadQueue, FILE* infile, int iFormatVersion);
|
||||
int FindNZBInfoIndex(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
|
||||
bool SaveFeedStatus(Feeds* pFeeds, FILE* outfile);
|
||||
bool LoadFeedStatus(Feeds* pFeeds, FILE* infile, int iFormatVersion);
|
||||
bool SaveFeedHistory(FeedHistory* pFeedHistory, FILE* outfile);
|
||||
bool LoadFeedHistory(FeedHistory* pFeedHistory, FILE* infile, int iFormatVersion);
|
||||
void CalcCriticalHealth(DownloadQueue* pDownloadQueue);
|
||||
bool SaveServerStats(Servers* pServers, FILE* outfile);
|
||||
bool LoadServerStats(Servers* pServers, FILE* infile, int iFormatVersion);
|
||||
void ConvertDupeKey(char* buf, int bufsize);
|
||||
|
||||
public:
|
||||
bool DownloadQueueExists();
|
||||
bool PostQueueExists(bool bCompleted);
|
||||
bool SaveDownloadQueue(DownloadQueue* pDownloadQueue);
|
||||
bool LoadDownloadQueue(DownloadQueue* pDownloadQueue);
|
||||
bool SaveFile(FileInfo* pFileInfo);
|
||||
bool LoadArticles(FileInfo* pFileInfo);
|
||||
bool SavePostQueue(PostQueue* pPostQueue, bool bCompleted);
|
||||
bool LoadPostQueue(PostQueue* pPostQueue, bool bCompleted);
|
||||
bool DiscardDownloadQueue();
|
||||
bool DiscardPostQueue();
|
||||
bool DiscardFile(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
|
||||
void DiscardDownloadQueue();
|
||||
bool DiscardFile(FileInfo* pFileInfo);
|
||||
bool SaveFeeds(Feeds* pFeeds, FeedHistory* pFeedHistory);
|
||||
bool LoadFeeds(Feeds* pFeeds, FeedHistory* pFeedHistory);
|
||||
bool SaveStats(Servers* pServers);
|
||||
bool LoadStats(Servers* pServers);
|
||||
void CleanupTempDir(DownloadQueue* pDownloadQueue);
|
||||
};
|
||||
|
||||
|
||||
1050
DownloadInfo.cpp
1050
DownloadInfo.cpp
File diff suppressed because it is too large
Load Diff
716
DownloadInfo.h
716
DownloadInfo.h
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* 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-2013 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -29,34 +29,13 @@
|
||||
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <time.h>
|
||||
|
||||
#include "Log.h"
|
||||
#include "Thread.h"
|
||||
|
||||
class NZBInfo
|
||||
{
|
||||
private:
|
||||
int m_iRefCount;
|
||||
char* m_szFilename;
|
||||
char* m_szDestDir;
|
||||
int m_iFileCount;
|
||||
long long m_lSize;
|
||||
|
||||
public:
|
||||
NZBInfo();
|
||||
~NZBInfo();
|
||||
void AddReference();
|
||||
void Release();
|
||||
const char* GetFilename() { return m_szFilename; }
|
||||
void SetFilename(const char* szFilename);
|
||||
void GetNiceNZBName(char* szBuffer, int iSize);
|
||||
static void MakeNiceNZBName(const char* szNZBFilename, char* szBuffer, int iSize);
|
||||
const char* GetDestDir() { return m_szDestDir; }
|
||||
void SetDestDir(const char* szDestDir);
|
||||
long long GetSize() { return m_lSize; }
|
||||
void SetSize(long long s) { m_lSize = s; }
|
||||
int GetFileCount() { return m_iFileCount; }
|
||||
void SetFileCount(int s) { m_iFileCount = s; }
|
||||
};
|
||||
class NZBInfo;
|
||||
class DownloadQueue;
|
||||
|
||||
class ArticleInfo
|
||||
{
|
||||
@@ -106,20 +85,36 @@ private:
|
||||
char* m_szFilename;
|
||||
long long m_lSize;
|
||||
long long m_lRemainingSize;
|
||||
long long m_lSuccessSize;
|
||||
long long m_lFailedSize;
|
||||
long long m_lMissedSize;
|
||||
int m_iTotalArticles;
|
||||
int m_iMissedArticles;
|
||||
int m_iFailedArticles;
|
||||
int m_iSuccessArticles;
|
||||
time_t m_tTime;
|
||||
bool m_bPaused;
|
||||
bool m_bDeleted;
|
||||
bool m_bFilenameConfirmed;
|
||||
bool m_bParFile;
|
||||
int m_iCompleted;
|
||||
bool m_bOutputInitialized;
|
||||
Mutex m_mutexOutputFile;
|
||||
char* m_szOutputFilename;
|
||||
Mutex* m_pMutexOutputFile;
|
||||
int m_iPriority;
|
||||
bool m_bExtraPriority;
|
||||
int m_iActiveDownloads;
|
||||
bool m_bAutoDeleted;
|
||||
|
||||
static int m_iIDGen;
|
||||
static int m_iIDMax;
|
||||
|
||||
public:
|
||||
FileInfo();
|
||||
~FileInfo();
|
||||
int GetID() { return m_iID; }
|
||||
void SetID(int s);
|
||||
void SetID(int iID);
|
||||
static void ResetGenID(bool bMax);
|
||||
NZBInfo* GetNZBInfo() { return m_pNZBInfo; }
|
||||
void SetNZBInfo(NZBInfo* pNZBInfo);
|
||||
Articles* GetArticles() { return &m_Articles; }
|
||||
@@ -131,28 +126,52 @@ public:
|
||||
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; }
|
||||
void SetSize(long long lSize) { m_lSize = lSize; m_lRemainingSize = lSize; }
|
||||
long long GetSize() { return m_lSize; }
|
||||
long long GetRemainingSize() { return m_lRemainingSize; }
|
||||
void SetRemainingSize(long long s) { m_lRemainingSize = s; }
|
||||
void SetRemainingSize(long long lRemainingSize) { m_lRemainingSize = lRemainingSize; }
|
||||
long long GetMissedSize() { return m_lMissedSize; }
|
||||
void SetMissedSize(long long lMissedSize) { m_lMissedSize = lMissedSize; }
|
||||
long long GetSuccessSize() { return m_lSuccessSize; }
|
||||
void SetSuccessSize(long long lSuccessSize) { m_lSuccessSize = lSuccessSize; }
|
||||
long long GetFailedSize() { return m_lFailedSize; }
|
||||
void SetFailedSize(long long lFailedSize) { m_lFailedSize = lFailedSize; }
|
||||
int GetTotalArticles() { return m_iTotalArticles; }
|
||||
void SetTotalArticles(int iTotalArticles) { m_iTotalArticles = iTotalArticles; }
|
||||
int GetMissedArticles() { return m_iMissedArticles; }
|
||||
void SetMissedArticles(int iMissedArticles) { m_iMissedArticles = iMissedArticles; }
|
||||
int GetFailedArticles() { return m_iFailedArticles; }
|
||||
void SetFailedArticles(int iFailedArticles) { m_iFailedArticles = iFailedArticles; }
|
||||
int GetSuccessArticles() { return m_iSuccessArticles; }
|
||||
void SetSuccessArticles(int iSuccessArticles) { m_iSuccessArticles = iSuccessArticles; }
|
||||
time_t GetTime() { return m_tTime; }
|
||||
void SetTime(time_t tTime) { m_tTime = tTime; }
|
||||
bool GetPaused() { return m_bPaused; }
|
||||
void SetPaused(bool Paused) { m_bPaused = Paused; }
|
||||
bool GetDeleted() { return m_bDeleted; }
|
||||
void SetDeleted(bool Deleted) { m_bDeleted = Deleted; }
|
||||
int GetCompleted() { return m_iCompleted; }
|
||||
void SetCompleted(int s) { m_iCompleted = s; }
|
||||
void SetCompleted(int iCompleted) { m_iCompleted = iCompleted; }
|
||||
bool GetParFile() { return m_bParFile; }
|
||||
void SetParFile(bool bParFile) { m_bParFile = bParFile; }
|
||||
void ClearArticles();
|
||||
void LockOutputFile();
|
||||
void UnlockOutputFile();
|
||||
const char* GetOutputFilename() { return m_szOutputFilename; }
|
||||
void SetOutputFilename(const char* szOutputFilename);
|
||||
bool GetOutputInitialized() { return m_bOutputInitialized; }
|
||||
void SetOutputInitialized(bool bOutputInitialized) { m_bOutputInitialized = bOutputInitialized; }
|
||||
bool IsDupe(const char* szFilename);
|
||||
int GetPriority() { return m_iPriority; }
|
||||
void SetPriority(int iPriority) { m_iPriority = iPriority; }
|
||||
bool GetExtraPriority() { return m_bExtraPriority; }
|
||||
void SetExtraPriority(bool bExtraPriority) { m_bExtraPriority = bExtraPriority; };
|
||||
int GetActiveDownloads() { return m_iActiveDownloads; }
|
||||
void SetActiveDownloads(int iActiveDownloads);
|
||||
bool GetAutoDeleted() { return m_bAutoDeleted; }
|
||||
void SetAutoDeleted(bool bAutoDeleted) { m_bAutoDeleted = bAutoDeleted; }
|
||||
};
|
||||
|
||||
typedef std::deque<FileInfo*> DownloadQueue;
|
||||
|
||||
class GroupInfo;
|
||||
typedef std::deque<GroupInfo*> GroupQueue;
|
||||
typedef std::deque<FileInfo*> FileQueue;
|
||||
|
||||
class GroupInfo
|
||||
{
|
||||
@@ -161,9 +180,17 @@ private:
|
||||
int m_iFirstID;
|
||||
int m_iLastID;
|
||||
int m_iRemainingFileCount;
|
||||
int m_iPausedFileCount;
|
||||
long long m_lRemainingSize;
|
||||
long long m_lPausedSize;
|
||||
int m_iRemainingParCount;
|
||||
time_t m_tMinTime;
|
||||
time_t m_tMaxTime;
|
||||
int m_iMinPriority;
|
||||
int m_iMaxPriority;
|
||||
int m_iActiveDownloads;
|
||||
|
||||
friend class DownloadQueue;
|
||||
|
||||
public:
|
||||
GroupInfo();
|
||||
@@ -174,9 +201,620 @@ public:
|
||||
long long GetRemainingSize() { return m_lRemainingSize; }
|
||||
long long GetPausedSize() { return m_lPausedSize; }
|
||||
int GetRemainingFileCount() { return m_iRemainingFileCount; }
|
||||
int GetPausedFileCount() { return m_iPausedFileCount; }
|
||||
int GetRemainingParCount() { return m_iRemainingParCount; }
|
||||
time_t GetMinTime() { return m_tMinTime; }
|
||||
time_t GetMaxTime() { return m_tMaxTime; }
|
||||
int GetMinPriority() { return m_iMinPriority; }
|
||||
int GetMaxPriority() { return m_iMaxPriority; }
|
||||
int GetActiveDownloads() { return m_iActiveDownloads; }
|
||||
};
|
||||
|
||||
static void BuildGroups(DownloadQueue* pDownloadQueue, GroupQueue* pGroupQueue);
|
||||
typedef std::deque<GroupInfo*> GroupQueueBase;
|
||||
|
||||
class GroupQueue : public GroupQueueBase
|
||||
{
|
||||
public:
|
||||
~GroupQueue();
|
||||
void Clear();
|
||||
};
|
||||
|
||||
|
||||
class NZBParameter
|
||||
{
|
||||
private:
|
||||
char* m_szName;
|
||||
char* m_szValue;
|
||||
|
||||
void SetValue(const char* szValue);
|
||||
|
||||
friend class NZBParameterList;
|
||||
|
||||
public:
|
||||
NZBParameter(const char* szName);
|
||||
~NZBParameter();
|
||||
const char* GetName() { return m_szName; }
|
||||
const char* GetValue() { return m_szValue; }
|
||||
};
|
||||
|
||||
typedef std::deque<NZBParameter*> NZBParameterListBase;
|
||||
|
||||
class NZBParameterList : public NZBParameterListBase
|
||||
{
|
||||
public:
|
||||
~NZBParameterList();
|
||||
void SetParameter(const char* szName, const char* szValue);
|
||||
NZBParameter* Find(const char* szName, bool bCaseSensitive);
|
||||
void Clear();
|
||||
void CopyFrom(NZBParameterList* pSourceParameters);
|
||||
};
|
||||
|
||||
class ScriptStatus
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
srNone,
|
||||
srFailure,
|
||||
srSuccess
|
||||
};
|
||||
|
||||
private:
|
||||
char* m_szName;
|
||||
EStatus m_eStatus;
|
||||
|
||||
friend class ScriptStatusList;
|
||||
|
||||
public:
|
||||
ScriptStatus(const char* szName, EStatus eStatus);
|
||||
~ScriptStatus();
|
||||
const char* GetName() { return m_szName; }
|
||||
EStatus GetStatus() { return m_eStatus; }
|
||||
};
|
||||
|
||||
typedef std::deque<ScriptStatus*> ScriptStatusListBase;
|
||||
|
||||
class ScriptStatusList : public ScriptStatusListBase
|
||||
{
|
||||
public:
|
||||
~ScriptStatusList();
|
||||
void Add(const char* szScriptName, ScriptStatus::EStatus eStatus);
|
||||
void Clear();
|
||||
ScriptStatus::EStatus CalcTotalStatus();
|
||||
};
|
||||
|
||||
class ServerStat
|
||||
{
|
||||
private:
|
||||
int m_iServerID;
|
||||
int m_iSuccessArticles;
|
||||
int m_iFailedArticles;
|
||||
|
||||
public:
|
||||
ServerStat(int iServerID);
|
||||
int GetServerID() { return m_iServerID; }
|
||||
int GetSuccessArticles() { return m_iSuccessArticles; }
|
||||
void SetSuccessArticles(int iSuccessArticles) { m_iSuccessArticles = iSuccessArticles; }
|
||||
int GetFailedArticles() { return m_iFailedArticles; }
|
||||
void SetFailedArticles(int iFailedArticles) { m_iFailedArticles = iFailedArticles; }
|
||||
};
|
||||
|
||||
typedef std::vector<ServerStat*> ServerStatListBase;
|
||||
|
||||
class ServerStatList : public ServerStatListBase
|
||||
{
|
||||
public:
|
||||
~ServerStatList();
|
||||
void SetStat(int iServerID, int iSuccessArticles, int iFailedArticles, bool bAdd);
|
||||
void Add(ServerStatList* pServerStats);
|
||||
void Clear();
|
||||
};
|
||||
|
||||
enum EDupeMode
|
||||
{
|
||||
dmScore,
|
||||
dmAll,
|
||||
dmForce
|
||||
};
|
||||
|
||||
class NZBInfoList;
|
||||
|
||||
class NZBInfo
|
||||
{
|
||||
public:
|
||||
enum ERenameStatus
|
||||
{
|
||||
rsNone,
|
||||
rsSkipped,
|
||||
rsFailure,
|
||||
rsSuccess
|
||||
};
|
||||
|
||||
enum EParStatus
|
||||
{
|
||||
psNone,
|
||||
psSkipped,
|
||||
psFailure,
|
||||
psSuccess,
|
||||
psRepairPossible,
|
||||
psManual
|
||||
};
|
||||
|
||||
enum EUnpackStatus
|
||||
{
|
||||
usNone,
|
||||
usSkipped,
|
||||
usFailure,
|
||||
usSuccess,
|
||||
usSpace,
|
||||
usPassword
|
||||
};
|
||||
|
||||
enum ECleanupStatus
|
||||
{
|
||||
csNone,
|
||||
csFailure,
|
||||
csSuccess
|
||||
};
|
||||
|
||||
enum EMoveStatus
|
||||
{
|
||||
msNone,
|
||||
msFailure,
|
||||
msSuccess
|
||||
};
|
||||
|
||||
enum EDeleteStatus
|
||||
{
|
||||
dsNone,
|
||||
dsManual,
|
||||
dsHealth,
|
||||
dsDupe
|
||||
};
|
||||
|
||||
enum EMarkStatus
|
||||
{
|
||||
ksNone,
|
||||
ksBad,
|
||||
ksGood
|
||||
};
|
||||
|
||||
typedef std::vector<char*> Files;
|
||||
typedef std::deque<Message*> Messages;
|
||||
|
||||
private:
|
||||
int m_iID;
|
||||
int m_iRefCount;
|
||||
char* m_szFilename;
|
||||
char* m_szName;
|
||||
char* m_szDestDir;
|
||||
char* m_szFinalDir;
|
||||
char* m_szCategory;
|
||||
int m_iFileCount;
|
||||
int m_iParkedFileCount;
|
||||
long long m_lSize;
|
||||
long long m_lSuccessSize;
|
||||
long long m_lFailedSize;
|
||||
long long m_lCurrentSuccessSize;
|
||||
long long m_lCurrentFailedSize;
|
||||
long long m_lParSize;
|
||||
long long m_lParSuccessSize;
|
||||
long long m_lParFailedSize;
|
||||
long long m_lParCurrentSuccessSize;
|
||||
long long m_lParCurrentFailedSize;
|
||||
int m_iTotalArticles;
|
||||
int m_iSuccessArticles;
|
||||
int m_iFailedArticles;
|
||||
Files m_completedFiles;
|
||||
bool m_bPostProcess;
|
||||
ERenameStatus m_eRenameStatus;
|
||||
EParStatus m_eParStatus;
|
||||
EUnpackStatus m_eUnpackStatus;
|
||||
ECleanupStatus m_eCleanupStatus;
|
||||
EMoveStatus m_eMoveStatus;
|
||||
EDeleteStatus m_eDeleteStatus;
|
||||
EMarkStatus m_eMarkStatus;
|
||||
bool m_bDeletePaused;
|
||||
bool m_bManyDupeFiles;
|
||||
char* m_szQueuedFilename;
|
||||
bool m_bDeleting;
|
||||
bool m_bAvoidHistory;
|
||||
bool m_bHealthPaused;
|
||||
bool m_bParCleanup;
|
||||
bool m_bParManual;
|
||||
bool m_bCleanupDisk;
|
||||
bool m_bUnpackCleanedUpDisk;
|
||||
char* m_szDupeKey;
|
||||
int m_iDupeScore;
|
||||
EDupeMode m_eDupeMode;
|
||||
unsigned int m_iFullContentHash;
|
||||
unsigned int m_iFilteredContentHash;
|
||||
NZBInfoList* m_Owner;
|
||||
NZBParameterList m_ppParameters;
|
||||
ScriptStatusList m_scriptStatuses;
|
||||
ServerStatList m_ServerStats;
|
||||
Mutex m_mutexLog;
|
||||
Messages m_Messages;
|
||||
int m_iIDMessageGen;
|
||||
|
||||
static int m_iIDGen;
|
||||
static int m_iIDMax;
|
||||
|
||||
friend class NZBInfoList;
|
||||
|
||||
public:
|
||||
NZBInfo(bool bPersistent = true);
|
||||
~NZBInfo();
|
||||
void Retain();
|
||||
void Release();
|
||||
int GetID() { return m_iID; }
|
||||
void SetID(int iID);
|
||||
static void ResetGenID(bool bMax);
|
||||
const char* GetFilename() { return m_szFilename; }
|
||||
void SetFilename(const char* szFilename);
|
||||
static void MakeNiceNZBName(const char* szNZBFilename, char* szBuffer, int iSize, bool bRemoveExt);
|
||||
const char* GetDestDir() { return m_szDestDir; } // needs locking (for shared objects)
|
||||
void SetDestDir(const char* szDestDir); // needs locking (for shared objects)
|
||||
const char* GetFinalDir() { return m_szFinalDir; } // needs locking (for shared objects)
|
||||
void SetFinalDir(const char* szFinalDir); // needs locking (for shared objects)
|
||||
const char* GetCategory() { return m_szCategory; } // needs locking (for shared objects)
|
||||
void SetCategory(const char* szCategory); // needs locking (for shared objects)
|
||||
const char* GetName() { return m_szName; } // needs locking (for shared objects)
|
||||
void SetName(const char* szName); // needs locking (for shared objects)
|
||||
int GetFileCount() { return m_iFileCount; }
|
||||
void SetFileCount(int iFileCount) { m_iFileCount = iFileCount; }
|
||||
int GetParkedFileCount() { return m_iParkedFileCount; }
|
||||
void SetParkedFileCount(int iParkedFileCount) { m_iParkedFileCount = iParkedFileCount; }
|
||||
long long GetSize() { return m_lSize; }
|
||||
void SetSize(long long lSize) { m_lSize = lSize; }
|
||||
long long GetSuccessSize() { return m_lSuccessSize; }
|
||||
void SetSuccessSize(long long lSuccessSize) { m_lSuccessSize = lSuccessSize; }
|
||||
long long GetFailedSize() { return m_lFailedSize; }
|
||||
void SetFailedSize(long long lFailedSize) { m_lFailedSize = lFailedSize; }
|
||||
long long GetCurrentSuccessSize() { return m_lCurrentSuccessSize; }
|
||||
void SetCurrentSuccessSize(long long lCurrentSuccessSize) { m_lCurrentSuccessSize = lCurrentSuccessSize; }
|
||||
long long GetCurrentFailedSize() { return m_lCurrentFailedSize; }
|
||||
void SetCurrentFailedSize(long long lCurrentFailedSize) { m_lCurrentFailedSize = lCurrentFailedSize; }
|
||||
long long GetParSize() { return m_lParSize; }
|
||||
void SetParSize(long long lParSize) { m_lParSize = lParSize; }
|
||||
long long GetParSuccessSize() { return m_lParSuccessSize; }
|
||||
void SetParSuccessSize(long long lParSuccessSize) { m_lParSuccessSize = lParSuccessSize; }
|
||||
long long GetParFailedSize() { return m_lParFailedSize; }
|
||||
void SetParFailedSize(long long lParFailedSize) { m_lParFailedSize = lParFailedSize; }
|
||||
long long GetParCurrentSuccessSize() { return m_lParCurrentSuccessSize; }
|
||||
void SetParCurrentSuccessSize(long long lParCurrentSuccessSize) { m_lParCurrentSuccessSize = lParCurrentSuccessSize; }
|
||||
long long GetParCurrentFailedSize() { return m_lParCurrentFailedSize; }
|
||||
void SetParCurrentFailedSize(long long lParCurrentFailedSize) { m_lParCurrentFailedSize = lParCurrentFailedSize; }
|
||||
int GetTotalArticles() { return m_iTotalArticles; }
|
||||
void SetTotalArticles(int iTotalArticles) { m_iTotalArticles = iTotalArticles; }
|
||||
int GetSuccessArticles() { return m_iSuccessArticles; }
|
||||
void SetSuccessArticles(int iSuccessArticles) { m_iSuccessArticles = iSuccessArticles; }
|
||||
int GetFailedArticles() { return m_iFailedArticles; }
|
||||
void SetFailedArticles(int iFailedArticles) { m_iFailedArticles = iFailedArticles; }
|
||||
void BuildDestDirName();
|
||||
void BuildFinalDirName(char* szFinalDirBuf, int iBufSize);
|
||||
Files* GetCompletedFiles() { return &m_completedFiles; } // needs locking (for shared objects)
|
||||
void ClearCompletedFiles();
|
||||
bool GetPostProcess() { return m_bPostProcess; }
|
||||
void SetPostProcess(bool bPostProcess) { m_bPostProcess = bPostProcess; }
|
||||
ERenameStatus GetRenameStatus() { return m_eRenameStatus; }
|
||||
void SetRenameStatus(ERenameStatus eRenameStatus) { m_eRenameStatus = eRenameStatus; }
|
||||
EParStatus GetParStatus() { return m_eParStatus; }
|
||||
void SetParStatus(EParStatus eParStatus) { m_eParStatus = eParStatus; }
|
||||
EUnpackStatus GetUnpackStatus() { return m_eUnpackStatus; }
|
||||
void SetUnpackStatus(EUnpackStatus eUnpackStatus) { m_eUnpackStatus = eUnpackStatus; }
|
||||
ECleanupStatus GetCleanupStatus() { return m_eCleanupStatus; }
|
||||
void SetCleanupStatus(ECleanupStatus eCleanupStatus) { m_eCleanupStatus = eCleanupStatus; }
|
||||
EMoveStatus GetMoveStatus() { return m_eMoveStatus; }
|
||||
void SetMoveStatus(EMoveStatus eMoveStatus) { m_eMoveStatus = eMoveStatus; }
|
||||
EDeleteStatus GetDeleteStatus() { return m_eDeleteStatus; }
|
||||
void SetDeleteStatus(EDeleteStatus eDeleteStatus) { m_eDeleteStatus = eDeleteStatus; }
|
||||
EMarkStatus GetMarkStatus() { return m_eMarkStatus; }
|
||||
void SetMarkStatus(EMarkStatus eMarkStatus) { m_eMarkStatus = eMarkStatus; }
|
||||
const char* GetQueuedFilename() { return m_szQueuedFilename; }
|
||||
void SetQueuedFilename(const char* szQueuedFilename);
|
||||
bool GetDeleting() { return m_bDeleting; }
|
||||
void SetDeleting(bool bDeleting) { m_bDeleting = bDeleting; }
|
||||
bool GetDeletePaused() { return m_bDeletePaused; }
|
||||
void SetDeletePaused(bool bDeletePaused) { m_bDeletePaused = bDeletePaused; }
|
||||
bool GetManyDupeFiles() { return m_bManyDupeFiles; }
|
||||
void SetManyDupeFiles(bool bManyDupeFiles) { m_bManyDupeFiles = bManyDupeFiles; }
|
||||
bool GetAvoidHistory() { return m_bAvoidHistory; }
|
||||
void SetAvoidHistory(bool bAvoidHistory) { m_bAvoidHistory = bAvoidHistory; }
|
||||
bool GetHealthPaused() { return m_bHealthPaused; }
|
||||
void SetHealthPaused(bool bHealthPaused) { m_bHealthPaused = bHealthPaused; }
|
||||
bool GetParCleanup() { return m_bParCleanup; }
|
||||
void SetParCleanup(bool bParCleanup) { m_bParCleanup = bParCleanup; }
|
||||
bool GetCleanupDisk() { return m_bCleanupDisk; }
|
||||
void SetCleanupDisk(bool bCleanupDisk) { m_bCleanupDisk = bCleanupDisk; }
|
||||
bool GetUnpackCleanedUpDisk() { return m_bUnpackCleanedUpDisk; }
|
||||
void SetUnpackCleanedUpDisk(bool bUnpackCleanedUpDisk) { m_bUnpackCleanedUpDisk = bUnpackCleanedUpDisk; }
|
||||
NZBParameterList* GetParameters() { return &m_ppParameters; } // needs locking (for shared objects)
|
||||
ScriptStatusList* GetScriptStatuses() { return &m_scriptStatuses; } // needs locking (for shared objects)
|
||||
ServerStatList* GetServerStats() { return &m_ServerStats; }
|
||||
int CalcHealth();
|
||||
int CalcCriticalHealth();
|
||||
const char* GetDupeKey() { return m_szDupeKey; } // needs locking (for shared objects)
|
||||
void SetDupeKey(const char* szDupeKey); // needs locking (for shared objects)
|
||||
int GetDupeScore() { return m_iDupeScore; }
|
||||
void SetDupeScore(int iDupeScore) { m_iDupeScore = iDupeScore; }
|
||||
EDupeMode GetDupeMode() { return m_eDupeMode; }
|
||||
void SetDupeMode(EDupeMode eDupeMode) { m_eDupeMode = eDupeMode; }
|
||||
unsigned int GetFullContentHash() { return m_iFullContentHash; }
|
||||
void SetFullContentHash(unsigned int iFullContentHash) { m_iFullContentHash = iFullContentHash; }
|
||||
unsigned int GetFilteredContentHash() { return m_iFilteredContentHash; }
|
||||
void SetFilteredContentHash(unsigned int iFilteredContentHash) { m_iFilteredContentHash = iFilteredContentHash; }
|
||||
void AppendMessage(Message::EKind eKind, time_t tTime, const char* szText);
|
||||
Messages* LockMessages();
|
||||
void UnlockMessages();
|
||||
};
|
||||
|
||||
typedef std::deque<NZBInfo*> NZBInfoListBase;
|
||||
|
||||
class NZBInfoList : public NZBInfoListBase
|
||||
{
|
||||
public:
|
||||
void Add(NZBInfo* pNZBInfo);
|
||||
void Remove(NZBInfo* pNZBInfo);
|
||||
void ReleaseAll();
|
||||
};
|
||||
|
||||
class PostInfo
|
||||
{
|
||||
public:
|
||||
enum EStage
|
||||
{
|
||||
ptQueued,
|
||||
ptLoadingPars,
|
||||
ptVerifyingSources,
|
||||
ptRepairing,
|
||||
ptVerifyingRepaired,
|
||||
ptRenaming,
|
||||
ptUnpacking,
|
||||
ptMoving,
|
||||
ptExecutingScript,
|
||||
ptFinished
|
||||
};
|
||||
|
||||
typedef std::deque<Message*> Messages;
|
||||
|
||||
private:
|
||||
int m_iID;
|
||||
NZBInfo* m_pNZBInfo;
|
||||
char* m_szInfoName;
|
||||
bool m_bWorking;
|
||||
bool m_bDeleted;
|
||||
bool m_bRequestParCheck;
|
||||
EStage m_eStage;
|
||||
char* m_szProgressLabel;
|
||||
int m_iFileProgress;
|
||||
int m_iStageProgress;
|
||||
time_t m_tStartTime;
|
||||
time_t m_tStageTime;
|
||||
Thread* m_pPostThread;
|
||||
|
||||
Mutex m_mutexLog;
|
||||
Messages m_Messages;
|
||||
int m_iIDMessageGen;
|
||||
|
||||
static int m_iIDGen;
|
||||
static int m_iIDMax;
|
||||
|
||||
public:
|
||||
PostInfo();
|
||||
~PostInfo();
|
||||
int GetID() { return m_iID; }
|
||||
NZBInfo* GetNZBInfo() { return m_pNZBInfo; }
|
||||
void SetNZBInfo(NZBInfo* pNZBInfo);
|
||||
const char* GetInfoName() { return m_szInfoName; }
|
||||
void SetInfoName(const char* szInfoName);
|
||||
EStage GetStage() { return m_eStage; }
|
||||
void SetStage(EStage eStage) { m_eStage = eStage; }
|
||||
void SetProgressLabel(const char* szProgressLabel);
|
||||
const char* GetProgressLabel() { return m_szProgressLabel; }
|
||||
int GetFileProgress() { return m_iFileProgress; }
|
||||
void SetFileProgress(int iFileProgress) { m_iFileProgress = iFileProgress; }
|
||||
int GetStageProgress() { return m_iStageProgress; }
|
||||
void SetStageProgress(int iStageProgress) { m_iStageProgress = iStageProgress; }
|
||||
time_t GetStartTime() { return m_tStartTime; }
|
||||
void SetStartTime(time_t tStartTime) { m_tStartTime = tStartTime; }
|
||||
time_t GetStageTime() { return m_tStageTime; }
|
||||
void SetStageTime(time_t tStageTime) { m_tStageTime = tStageTime; }
|
||||
bool GetWorking() { return m_bWorking; }
|
||||
void SetWorking(bool bWorking) { m_bWorking = bWorking; }
|
||||
bool GetDeleted() { return m_bDeleted; }
|
||||
void SetDeleted(bool bDeleted) { m_bDeleted = bDeleted; }
|
||||
bool GetRequestParCheck() { return m_bRequestParCheck; }
|
||||
void SetRequestParCheck(bool bRequestParCheck) { m_bRequestParCheck = bRequestParCheck; }
|
||||
void AppendMessage(Message::EKind eKind, const char* szText);
|
||||
Thread* GetPostThread() { return m_pPostThread; }
|
||||
void SetPostThread(Thread* pPostThread) { m_pPostThread = pPostThread; }
|
||||
Messages* LockMessages();
|
||||
void UnlockMessages();
|
||||
};
|
||||
|
||||
typedef std::deque<PostInfo*> PostQueue;
|
||||
|
||||
typedef std::vector<int> IDList;
|
||||
|
||||
typedef std::vector<char*> NameList;
|
||||
|
||||
class UrlInfo
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
aiUndefined,
|
||||
aiRunning,
|
||||
aiFinished,
|
||||
aiFailed,
|
||||
aiRetry,
|
||||
aiScanSkipped,
|
||||
aiScanFailed
|
||||
};
|
||||
|
||||
private:
|
||||
int m_iID;
|
||||
char* m_szURL;
|
||||
char* m_szNZBFilename;
|
||||
char* m_szCategory;
|
||||
int m_iPriority;
|
||||
char* m_szDupeKey;
|
||||
int m_iDupeScore;
|
||||
EDupeMode m_eDupeMode;
|
||||
bool m_bAddTop;
|
||||
bool m_bAddPaused;
|
||||
bool m_bForce;
|
||||
EStatus m_eStatus;
|
||||
|
||||
static int m_iIDGen;
|
||||
static int m_iIDMax;
|
||||
|
||||
public:
|
||||
UrlInfo();
|
||||
~UrlInfo();
|
||||
int GetID() { return m_iID; }
|
||||
void SetID(int iID);
|
||||
static void ResetGenID(bool bMax);
|
||||
const char* GetURL() { return m_szURL; } // needs locking (for shared objects)
|
||||
void SetURL(const char* szURL); // needs locking (for shared objects)
|
||||
const char* GetNZBFilename() { return m_szNZBFilename; } // needs locking (for shared objects)
|
||||
void SetNZBFilename(const char* szNZBFilename); // needs locking (for shared objects)
|
||||
const char* GetCategory() { return m_szCategory; } // needs locking (for shared objects)
|
||||
void SetCategory(const char* szCategory); // needs locking (for shared objects)
|
||||
int GetPriority() { return m_iPriority; }
|
||||
void SetPriority(int iPriority) { m_iPriority = iPriority; }
|
||||
const char* GetDupeKey() { return m_szDupeKey; }
|
||||
void SetDupeKey(const char* szDupeKey);
|
||||
int GetDupeScore() { return m_iDupeScore; }
|
||||
void SetDupeScore(int iDupeScore) { m_iDupeScore = iDupeScore; }
|
||||
EDupeMode GetDupeMode() { return m_eDupeMode; }
|
||||
void SetDupeMode(EDupeMode eDupeMode) { m_eDupeMode = eDupeMode; }
|
||||
bool GetAddTop() { return m_bAddTop; }
|
||||
void SetAddTop(bool bAddTop) { m_bAddTop = bAddTop; }
|
||||
bool GetAddPaused() { return m_bAddPaused; }
|
||||
void SetAddPaused(bool bAddPaused) { m_bAddPaused = bAddPaused; }
|
||||
void GetName(char* szBuffer, int iSize); // needs locking (for shared objects)
|
||||
static void MakeNiceName(const char* szURL, const char* szNZBFilename, char* szBuffer, int iSize);
|
||||
bool GetForce() { return m_bForce; }
|
||||
void SetForce(bool bForce) { m_bForce = bForce; }
|
||||
EStatus GetStatus() { return m_eStatus; }
|
||||
void SetStatus(EStatus Status) { m_eStatus = Status; }
|
||||
};
|
||||
|
||||
typedef std::deque<UrlInfo*> UrlQueue;
|
||||
|
||||
class DupInfo
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
dsUndefined,
|
||||
dsSuccess,
|
||||
dsFailed,
|
||||
dsDeleted,
|
||||
dsDupe,
|
||||
dsBad,
|
||||
dsGood
|
||||
};
|
||||
|
||||
private:
|
||||
char* m_szName;
|
||||
char* m_szDupeKey;
|
||||
int m_iDupeScore;
|
||||
EDupeMode m_eDupeMode;
|
||||
long long m_lSize;
|
||||
unsigned int m_iFullContentHash;
|
||||
unsigned int m_iFilteredContentHash;
|
||||
EStatus m_eStatus;
|
||||
|
||||
public:
|
||||
DupInfo();
|
||||
~DupInfo();
|
||||
const char* GetName() { return m_szName; } // needs locking (for shared objects)
|
||||
void SetName(const char* szName); // needs locking (for shared objects)
|
||||
const char* GetDupeKey() { return m_szDupeKey; } // needs locking (for shared objects)
|
||||
void SetDupeKey(const char* szDupeKey); // needs locking (for shared objects)
|
||||
int GetDupeScore() { return m_iDupeScore; }
|
||||
void SetDupeScore(int iDupeScore) { m_iDupeScore = iDupeScore; }
|
||||
EDupeMode GetDupeMode() { return m_eDupeMode; }
|
||||
void SetDupeMode(EDupeMode eDupeMode) { m_eDupeMode = eDupeMode; }
|
||||
long long GetSize() { return m_lSize; }
|
||||
void SetSize(long long lSize) { m_lSize = lSize; }
|
||||
unsigned int GetFullContentHash() { return m_iFullContentHash; }
|
||||
void SetFullContentHash(unsigned int iFullContentHash) { m_iFullContentHash = iFullContentHash; }
|
||||
unsigned int GetFilteredContentHash() { return m_iFilteredContentHash; }
|
||||
void SetFilteredContentHash(unsigned int iFilteredContentHash) { m_iFilteredContentHash = iFilteredContentHash; }
|
||||
EStatus GetStatus() { return m_eStatus; }
|
||||
void SetStatus(EStatus Status) { m_eStatus = Status; }
|
||||
};
|
||||
|
||||
class HistoryInfo
|
||||
{
|
||||
public:
|
||||
enum EKind
|
||||
{
|
||||
hkUnknown,
|
||||
hkNZBInfo,
|
||||
hkUrlInfo,
|
||||
hkDupInfo
|
||||
};
|
||||
|
||||
private:
|
||||
int m_iID;
|
||||
EKind m_eKind;
|
||||
void* m_pInfo;
|
||||
time_t m_tTime;
|
||||
|
||||
static int m_iIDGen;
|
||||
static int m_iIDMax;
|
||||
|
||||
public:
|
||||
HistoryInfo(NZBInfo* pNZBInfo);
|
||||
HistoryInfo(UrlInfo* pUrlInfo);
|
||||
HistoryInfo(DupInfo* pDupInfo);
|
||||
~HistoryInfo();
|
||||
int GetID() { return m_iID; }
|
||||
void SetID(int iID);
|
||||
static void ResetGenID(bool bMax);
|
||||
EKind GetKind() { return m_eKind; }
|
||||
NZBInfo* GetNZBInfo() { return (NZBInfo*)m_pInfo; }
|
||||
UrlInfo* GetUrlInfo() { return (UrlInfo*)m_pInfo; }
|
||||
DupInfo* GetDupInfo() { return (DupInfo*)m_pInfo; }
|
||||
void DiscardUrlInfo() { m_pInfo = NULL; }
|
||||
time_t GetTime() { return m_tTime; }
|
||||
void SetTime(time_t tTime) { m_tTime = tTime; }
|
||||
void GetName(char* szBuffer, int iSize); // needs locking (for shared objects)
|
||||
};
|
||||
|
||||
typedef std::deque<HistoryInfo*> HistoryList;
|
||||
|
||||
class DownloadQueue
|
||||
{
|
||||
protected:
|
||||
NZBInfoList m_NZBInfoList;
|
||||
FileQueue m_FileQueue;
|
||||
PostQueue m_PostQueue;
|
||||
HistoryList m_HistoryList;
|
||||
FileQueue m_ParkedFiles;
|
||||
UrlQueue m_UrlQueue;
|
||||
|
||||
public:
|
||||
NZBInfoList* GetNZBInfoList() { return &m_NZBInfoList; }
|
||||
FileQueue* GetFileQueue() { return &m_FileQueue; }
|
||||
PostQueue* GetPostQueue() { return &m_PostQueue; }
|
||||
HistoryList* GetHistoryList() { return &m_HistoryList; }
|
||||
FileQueue* GetParkedFiles() { return &m_ParkedFiles; }
|
||||
UrlQueue* GetUrlQueue() { return &m_UrlQueue; }
|
||||
void BuildGroups(GroupQueue* pGroupQueue);
|
||||
};
|
||||
|
||||
class DownloadQueueHolder
|
||||
{
|
||||
public:
|
||||
virtual ~DownloadQueueHolder() {};
|
||||
virtual DownloadQueue* LockQueue() = 0;
|
||||
virtual void UnlockQueue() = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
583
DupeCoordinator.cpp
Normal file
583
DupeCoordinator.cpp
Normal file
@@ -0,0 +1,583 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007-2013 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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>
|
||||
#ifdef WIN32
|
||||
#include <direct.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <set>
|
||||
#include <algorithm>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include "DiskState.h"
|
||||
#include "NZBFile.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "DupeCoordinator.h"
|
||||
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
extern Options* g_pOptions;
|
||||
extern DiskState* g_pDiskState;
|
||||
|
||||
bool DupeCoordinator::IsDupeSuccess(NZBInfo* pNZBInfo)
|
||||
{
|
||||
bool bFailure =
|
||||
pNZBInfo->GetDeleteStatus() != NZBInfo::dsNone ||
|
||||
pNZBInfo->GetMarkStatus() == NZBInfo::ksBad ||
|
||||
pNZBInfo->GetParStatus() == NZBInfo::psFailure ||
|
||||
pNZBInfo->GetUnpackStatus() == NZBInfo::usFailure ||
|
||||
pNZBInfo->GetUnpackStatus() == NZBInfo::usPassword ||
|
||||
(pNZBInfo->GetParStatus() == NZBInfo::psSkipped &&
|
||||
pNZBInfo->GetUnpackStatus() == NZBInfo::usSkipped &&
|
||||
pNZBInfo->CalcHealth() < pNZBInfo->CalcCriticalHealth());
|
||||
return !bFailure;
|
||||
}
|
||||
|
||||
bool DupeCoordinator::SameNameOrKey(const char* szName1, const char* szDupeKey1,
|
||||
const char* szName2, const char* szDupeKey2)
|
||||
{
|
||||
bool bHasDupeKeys = !Util::EmptyStr(szDupeKey1) && !Util::EmptyStr(szDupeKey2);
|
||||
return (bHasDupeKeys && !strcmp(szDupeKey1, szDupeKey2)) ||
|
||||
(!bHasDupeKeys && !strcmp(szName1, szName2));
|
||||
}
|
||||
|
||||
/**
|
||||
Check if the title was already downloaded or is already queued:
|
||||
- if there is a duplicate with exactly same content (via hash-check)
|
||||
in queue or in history - the new item is skipped;
|
||||
- if there is a duplicate marked as good in history - the new item is skipped;
|
||||
- if there is a duplicate with success-status in dup-history but
|
||||
there are no duplicates in recent history - the new item is skipped;
|
||||
- if queue has a duplicate with the same or higher score - the new item
|
||||
is moved to history as dupe-backup;
|
||||
- if queue has a duplicate with lower score - the existing item is moved
|
||||
to history as dupe-backup (unless it is in post-processing stage) and
|
||||
the new item is added to queue;
|
||||
- if queue doesn't have duplicates - the new item is added to queue.
|
||||
*/
|
||||
void DupeCoordinator::NZBFound(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
|
||||
{
|
||||
debug("Checking duplicates for %s", pNZBInfo->GetName());
|
||||
|
||||
GroupQueue groupQueue;
|
||||
pDownloadQueue->BuildGroups(&groupQueue);
|
||||
|
||||
// find duplicates in download queue with exactly same content
|
||||
for (GroupQueue::iterator it = groupQueue.begin(); it != groupQueue.end(); it++)
|
||||
{
|
||||
GroupInfo* pGroupInfo = *it;
|
||||
NZBInfo* pGroupNZBInfo = pGroupInfo->GetNZBInfo();
|
||||
bool bSameContent = (pNZBInfo->GetFullContentHash() > 0 &&
|
||||
pNZBInfo->GetFullContentHash() == pGroupNZBInfo->GetFullContentHash()) ||
|
||||
(pNZBInfo->GetFilteredContentHash() > 0 &&
|
||||
pNZBInfo->GetFilteredContentHash() == pGroupNZBInfo->GetFilteredContentHash());
|
||||
|
||||
// if there is a duplicate with exactly same content (via hash-check)
|
||||
// in queue - the new item is skipped
|
||||
if (pGroupNZBInfo != pNZBInfo && bSameContent)
|
||||
{
|
||||
if (!strcmp(pNZBInfo->GetName(), pGroupNZBInfo->GetName()))
|
||||
{
|
||||
warn("Skipping duplicate %s, already queued", pNZBInfo->GetName());
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Skipping duplicate %s, already queued as %s",
|
||||
pNZBInfo->GetName(), pGroupNZBInfo->GetName());
|
||||
}
|
||||
// Flag saying QueueCoordinator to skip nzb-file
|
||||
pNZBInfo->SetDeleteStatus(NZBInfo::dsManual);
|
||||
DeleteQueuedFile(pNZBInfo->GetQueuedFilename());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// find duplicates in post queue with exactly same content
|
||||
for (PostQueue::iterator it = pDownloadQueue->GetPostQueue()->begin(); it != pDownloadQueue->GetPostQueue()->end(); it++)
|
||||
{
|
||||
PostInfo* pPostInfo = *it;
|
||||
bool bSameContent = (pNZBInfo->GetFullContentHash() > 0 &&
|
||||
pNZBInfo->GetFullContentHash() == pPostInfo->GetNZBInfo()->GetFullContentHash()) ||
|
||||
(pNZBInfo->GetFilteredContentHash() > 0 &&
|
||||
pNZBInfo->GetFilteredContentHash() == pPostInfo->GetNZBInfo()->GetFilteredContentHash());
|
||||
|
||||
// if there is a duplicate with exactly same content (via hash-check)
|
||||
// in queue - the new item is skipped;
|
||||
if (bSameContent)
|
||||
{
|
||||
if (!strcmp(pNZBInfo->GetName(), pPostInfo->GetNZBInfo()->GetName()))
|
||||
{
|
||||
warn("Skipping duplicate %s, already queued", pNZBInfo->GetName());
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Skipping duplicate %s, already queued as %s",
|
||||
pNZBInfo->GetName(), pPostInfo->GetNZBInfo()->GetName());
|
||||
}
|
||||
// Flag saying QueueCoordinator to skip nzb-file
|
||||
pNZBInfo->SetDeleteStatus(NZBInfo::dsManual);
|
||||
DeleteQueuedFile(pNZBInfo->GetQueuedFilename());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// find duplicates in history
|
||||
|
||||
bool bSkip = false;
|
||||
bool bGood = false;
|
||||
bool bSameContent = false;
|
||||
const char* szDupeName = NULL;
|
||||
|
||||
// find duplicates in queue having exactly same content
|
||||
// also: nzb-files having duplicates marked as good are skipped
|
||||
// also (only in score mode): nzb-files having success-duplicates in dup-history but don't having duplicates in recent history are skipped
|
||||
for (HistoryList::iterator it = pDownloadQueue->GetHistoryList()->begin(); it != pDownloadQueue->GetHistoryList()->end(); it++)
|
||||
{
|
||||
HistoryInfo* pHistoryInfo = *it;
|
||||
|
||||
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo &&
|
||||
((pNZBInfo->GetFullContentHash() > 0 &&
|
||||
pNZBInfo->GetFullContentHash() == pHistoryInfo->GetNZBInfo()->GetFullContentHash()) ||
|
||||
(pNZBInfo->GetFilteredContentHash() > 0 &&
|
||||
pNZBInfo->GetFilteredContentHash() == pHistoryInfo->GetNZBInfo()->GetFilteredContentHash())))
|
||||
{
|
||||
bSkip = true;
|
||||
bSameContent = true;
|
||||
szDupeName = pHistoryInfo->GetNZBInfo()->GetName();
|
||||
break;
|
||||
}
|
||||
|
||||
if (pHistoryInfo->GetKind() == HistoryInfo::hkDupInfo &&
|
||||
((pNZBInfo->GetFullContentHash() > 0 &&
|
||||
pNZBInfo->GetFullContentHash() == pHistoryInfo->GetDupInfo()->GetFullContentHash()) ||
|
||||
(pNZBInfo->GetFilteredContentHash() > 0 &&
|
||||
pNZBInfo->GetFilteredContentHash() == pHistoryInfo->GetDupInfo()->GetFilteredContentHash())))
|
||||
{
|
||||
bSkip = true;
|
||||
bSameContent = true;
|
||||
szDupeName = pHistoryInfo->GetDupInfo()->GetName();
|
||||
break;
|
||||
}
|
||||
|
||||
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo &&
|
||||
pHistoryInfo->GetNZBInfo()->GetDupeMode() != dmForce &&
|
||||
pHistoryInfo->GetNZBInfo()->GetMarkStatus() == NZBInfo::ksGood &&
|
||||
SameNameOrKey(pHistoryInfo->GetNZBInfo()->GetName(), pHistoryInfo->GetNZBInfo()->GetDupeKey(),
|
||||
pNZBInfo->GetName(), pNZBInfo->GetDupeKey()))
|
||||
{
|
||||
bSkip = true;
|
||||
bGood = true;
|
||||
szDupeName = pHistoryInfo->GetNZBInfo()->GetName();
|
||||
break;
|
||||
}
|
||||
|
||||
if (pHistoryInfo->GetKind() == HistoryInfo::hkDupInfo &&
|
||||
pHistoryInfo->GetDupInfo()->GetDupeMode() != dmForce &&
|
||||
(pHistoryInfo->GetDupInfo()->GetStatus() == DupInfo::dsGood ||
|
||||
(pNZBInfo->GetDupeMode() == dmScore &&
|
||||
pHistoryInfo->GetDupInfo()->GetStatus() == DupInfo::dsSuccess &&
|
||||
pNZBInfo->GetDupeScore() <= pHistoryInfo->GetDupInfo()->GetDupeScore())) &&
|
||||
SameNameOrKey(pHistoryInfo->GetDupInfo()->GetName(), pHistoryInfo->GetDupInfo()->GetDupeKey(),
|
||||
pNZBInfo->GetName(), pNZBInfo->GetDupeKey()))
|
||||
{
|
||||
bSkip = true;
|
||||
bGood = pHistoryInfo->GetDupInfo()->GetStatus() == DupInfo::dsGood;
|
||||
szDupeName = pHistoryInfo->GetDupInfo()->GetName();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bSameContent && !bGood && pNZBInfo->GetDupeMode() == dmScore)
|
||||
{
|
||||
// nzb-files having success-duplicates in recent history (with different content) are added to history for backup
|
||||
for (HistoryList::iterator it = pDownloadQueue->GetHistoryList()->begin(); it != pDownloadQueue->GetHistoryList()->end(); it++)
|
||||
{
|
||||
HistoryInfo* pHistoryInfo = *it;
|
||||
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo &&
|
||||
pHistoryInfo->GetNZBInfo()->GetDupeMode() != dmForce &&
|
||||
SameNameOrKey(pHistoryInfo->GetNZBInfo()->GetName(), pHistoryInfo->GetNZBInfo()->GetDupeKey(),
|
||||
pNZBInfo->GetName(), pNZBInfo->GetDupeKey()) &&
|
||||
pNZBInfo->GetDupeScore() <= pHistoryInfo->GetNZBInfo()->GetDupeScore() &&
|
||||
IsDupeSuccess(pHistoryInfo->GetNZBInfo()))
|
||||
{
|
||||
// Flag saying QueueCoordinator to skip nzb-file
|
||||
pNZBInfo->SetDeleteStatus(NZBInfo::dsDupe);
|
||||
info("Collection %s is duplicate to %s", pNZBInfo->GetName(), pHistoryInfo->GetNZBInfo()->GetName());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bSkip)
|
||||
{
|
||||
if (!strcmp(pNZBInfo->GetName(), szDupeName))
|
||||
{
|
||||
warn("Skipping duplicate %s, found in history with %s", pNZBInfo->GetName(),
|
||||
bSameContent ? "exactly same content" : bGood ? "good status" : "success status");
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Skipping duplicate %s, found in history %s with %s",
|
||||
pNZBInfo->GetName(), szDupeName,
|
||||
bSameContent ? "exactly same content" : bGood ? "good status" : "success status");
|
||||
}
|
||||
|
||||
// Flag saying QueueCoordinator to skip nzb-file
|
||||
pNZBInfo->SetDeleteStatus(NZBInfo::dsManual);
|
||||
DeleteQueuedFile(pNZBInfo->GetQueuedFilename());
|
||||
return;
|
||||
}
|
||||
|
||||
// find duplicates in download queue and post-queue and handle both items according to their scores:
|
||||
// only one item remains in queue and another one is moved to history as dupe-backup
|
||||
if (pNZBInfo->GetDupeMode() == dmScore)
|
||||
{
|
||||
// find duplicates in download queue
|
||||
for (GroupQueue::iterator it = groupQueue.begin(); it != groupQueue.end(); it++)
|
||||
{
|
||||
GroupInfo* pGroupInfo = *it;
|
||||
NZBInfo* pGroupNZBInfo = pGroupInfo->GetNZBInfo();
|
||||
|
||||
if (pGroupNZBInfo != pNZBInfo &&
|
||||
pGroupNZBInfo->GetDupeMode() != dmForce &&
|
||||
SameNameOrKey(pGroupNZBInfo->GetName(), pGroupNZBInfo->GetDupeKey(),
|
||||
pNZBInfo->GetName(), pNZBInfo->GetDupeKey()))
|
||||
{
|
||||
// if queue has a duplicate with the same or higher score - the new item
|
||||
// is moved to history as dupe-backup
|
||||
if (pNZBInfo->GetDupeScore() <= pGroupNZBInfo->GetDupeScore())
|
||||
{
|
||||
// Flag saying QueueCoordinator to skip nzb-file
|
||||
pNZBInfo->SetDeleteStatus(NZBInfo::dsDupe);
|
||||
info("Collection %s is duplicate to %s", pNZBInfo->GetName(), pGroupNZBInfo->GetName());
|
||||
return;
|
||||
}
|
||||
// if queue has a duplicate with lower score - the existing item is moved
|
||||
// to history as dupe-backup (unless it is in post-processing stage) and
|
||||
// the new item is added to queue
|
||||
else
|
||||
{
|
||||
// unless it is in post-processing stage
|
||||
bool bPostProcess = false;
|
||||
for (PostQueue::iterator it = pDownloadQueue->GetPostQueue()->begin(); it != pDownloadQueue->GetPostQueue()->end(); it++)
|
||||
{
|
||||
PostInfo* pPostInfo = *it;
|
||||
if (pPostInfo->GetNZBInfo() == pGroupNZBInfo)
|
||||
{
|
||||
bPostProcess = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!bPostProcess)
|
||||
{
|
||||
// the existing queue item is moved to history as dupe-backup
|
||||
info("Moving collection %s with lower duplicate score to history", pGroupNZBInfo->GetName());
|
||||
pGroupNZBInfo->SetDeleteStatus(NZBInfo::dsDupe);
|
||||
g_pQueueCoordinator->GetQueueEditor()->LockedEditEntry(pDownloadQueue, pGroupInfo->GetLastID(), false, QueueEditor::eaGroupDelete, 0, NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// find duplicates in post queue
|
||||
for (PostQueue::iterator it = pDownloadQueue->GetPostQueue()->begin(); it != pDownloadQueue->GetPostQueue()->end(); it++)
|
||||
{
|
||||
PostInfo* pPostInfo = *it;
|
||||
// if queue has a duplicate with the same or higher score - the new item
|
||||
// is moved to history as dupe-backup;
|
||||
if (pPostInfo->GetNZBInfo()->GetDupeMode() != dmForce &&
|
||||
pNZBInfo->GetDupeScore() <= pPostInfo->GetNZBInfo()->GetDupeScore() &&
|
||||
SameNameOrKey(pPostInfo->GetNZBInfo()->GetName(), pPostInfo->GetNZBInfo()->GetDupeKey(),
|
||||
pNZBInfo->GetName(), pNZBInfo->GetDupeKey()))
|
||||
{
|
||||
// Flag saying QueueCoordinator to skip nzb-file
|
||||
pNZBInfo->SetDeleteStatus(NZBInfo::dsDupe);
|
||||
info("Collection %s is duplicate to %s", pNZBInfo->GetName(), pPostInfo->GetNZBInfo()->GetName());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
- if download of an item fails and there are duplicates in history -
|
||||
return the best duplicate from historyto queue for download;
|
||||
- if download of an item completes successfully - nothing extra needs to be done;
|
||||
*/
|
||||
void DupeCoordinator::NZBCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
|
||||
{
|
||||
debug("Processing duplicates for %s", pNZBInfo->GetName());
|
||||
|
||||
if (pNZBInfo->GetDupeMode() == dmScore && !IsDupeSuccess(pNZBInfo))
|
||||
{
|
||||
ReturnBestDupe(pDownloadQueue, pNZBInfo, pNZBInfo->GetName(), pNZBInfo->GetDupeKey());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the best duplicate from history to download queue.
|
||||
*/
|
||||
void DupeCoordinator::ReturnBestDupe(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, const char* szNZBName, const char* szDupeKey)
|
||||
{
|
||||
// check if history (recent or dup) has other success-duplicates or good-duplicates
|
||||
bool bHistoryDupe = false;
|
||||
int iHistoryScore = 0;
|
||||
for (HistoryList::iterator it = pDownloadQueue->GetHistoryList()->begin(); it != pDownloadQueue->GetHistoryList()->end(); it++)
|
||||
{
|
||||
HistoryInfo* pHistoryInfo = *it;
|
||||
bool bGoodDupe = false;
|
||||
|
||||
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo &&
|
||||
pHistoryInfo->GetNZBInfo()->GetDupeMode() != dmForce &&
|
||||
(IsDupeSuccess(pHistoryInfo->GetNZBInfo()) ||
|
||||
pHistoryInfo->GetNZBInfo()->GetMarkStatus() == NZBInfo::ksGood) &&
|
||||
SameNameOrKey(pHistoryInfo->GetNZBInfo()->GetName(), pHistoryInfo->GetNZBInfo()->GetDupeKey(), szNZBName, szDupeKey))
|
||||
{
|
||||
if (!bHistoryDupe || pHistoryInfo->GetNZBInfo()->GetDupeScore() > iHistoryScore)
|
||||
{
|
||||
iHistoryScore = pHistoryInfo->GetNZBInfo()->GetDupeScore();
|
||||
}
|
||||
bHistoryDupe = true;
|
||||
bGoodDupe = pHistoryInfo->GetNZBInfo()->GetMarkStatus() == NZBInfo::ksGood;
|
||||
}
|
||||
|
||||
if (pHistoryInfo->GetKind() == HistoryInfo::hkDupInfo &&
|
||||
pHistoryInfo->GetDupInfo()->GetDupeMode() != dmForce &&
|
||||
(pHistoryInfo->GetDupInfo()->GetStatus() == DupInfo::dsSuccess ||
|
||||
pHistoryInfo->GetDupInfo()->GetStatus() == DupInfo::dsGood) &&
|
||||
SameNameOrKey(pHistoryInfo->GetDupInfo()->GetName(), pHistoryInfo->GetDupInfo()->GetDupeKey(), szNZBName, szDupeKey))
|
||||
{
|
||||
if (!bHistoryDupe || pHistoryInfo->GetDupInfo()->GetDupeScore() > iHistoryScore)
|
||||
{
|
||||
iHistoryScore = pHistoryInfo->GetDupInfo()->GetDupeScore();
|
||||
}
|
||||
bHistoryDupe = true;
|
||||
bGoodDupe = pHistoryInfo->GetDupInfo()->GetStatus() == DupInfo::dsGood;
|
||||
}
|
||||
|
||||
if (bGoodDupe)
|
||||
{
|
||||
// another duplicate with good-status exists - exit without moving other dupes to queue
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// check if duplicates exist in post-processing queue
|
||||
bool bPostDupe = false;
|
||||
int iPostScore = 0;
|
||||
for (PostQueue::iterator it = pDownloadQueue->GetPostQueue()->begin(); it != pDownloadQueue->GetPostQueue()->end(); it++)
|
||||
{
|
||||
PostInfo* pPostInfo = *it;
|
||||
if (pPostInfo->GetNZBInfo() != pNZBInfo &&
|
||||
pPostInfo->GetNZBInfo()->GetDupeMode() != dmForce &&
|
||||
SameNameOrKey(pPostInfo->GetNZBInfo()->GetName(), pPostInfo->GetNZBInfo()->GetDupeKey(), szNZBName, szDupeKey) &&
|
||||
(!bPostDupe || pPostInfo->GetNZBInfo()->GetDupeScore() > iPostScore))
|
||||
{
|
||||
iPostScore = pPostInfo->GetNZBInfo()->GetDupeScore();
|
||||
bPostDupe = true;
|
||||
}
|
||||
}
|
||||
|
||||
// check if duplicates exist in download queue
|
||||
GroupQueue groupQueue;
|
||||
pDownloadQueue->BuildGroups(&groupQueue);
|
||||
bool bQueueDupe = false;
|
||||
int iQueueScore = 0;
|
||||
for (GroupQueue::iterator it = groupQueue.begin(); it != groupQueue.end(); it++)
|
||||
{
|
||||
GroupInfo* pGroupInfo = *it;
|
||||
NZBInfo* pGroupNZBInfo = pGroupInfo->GetNZBInfo();
|
||||
if (pGroupNZBInfo != pNZBInfo &&
|
||||
pGroupNZBInfo->GetDupeMode() != dmForce &&
|
||||
SameNameOrKey(pGroupNZBInfo->GetName(), pGroupNZBInfo->GetDupeKey(), szNZBName, szDupeKey) &&
|
||||
(!bQueueDupe || pGroupNZBInfo->GetDupeScore() > iQueueScore))
|
||||
{
|
||||
iQueueScore = pGroupNZBInfo->GetDupeScore();
|
||||
bQueueDupe = true;
|
||||
}
|
||||
}
|
||||
|
||||
// find dupe-backup with highest score, whose score is also higher than other
|
||||
// success-duplicates and higher than already queued items
|
||||
HistoryInfo* pHistoryDupe = NULL;
|
||||
for (HistoryList::iterator it = pDownloadQueue->GetHistoryList()->begin(); it != pDownloadQueue->GetHistoryList()->end(); it++)
|
||||
{
|
||||
HistoryInfo* pHistoryInfo = *it;
|
||||
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo &&
|
||||
pHistoryInfo->GetNZBInfo()->GetDupeMode() != dmForce &&
|
||||
pHistoryInfo->GetNZBInfo()->GetDeleteStatus() == NZBInfo::dsDupe &&
|
||||
pHistoryInfo->GetNZBInfo()->CalcHealth() >= pHistoryInfo->GetNZBInfo()->CalcCriticalHealth() &&
|
||||
pHistoryInfo->GetNZBInfo()->GetMarkStatus() != NZBInfo::ksBad &&
|
||||
(!bHistoryDupe || pHistoryInfo->GetNZBInfo()->GetDupeScore() > iHistoryScore) &&
|
||||
(!bPostDupe || pHistoryInfo->GetNZBInfo()->GetDupeScore() > iPostScore) &&
|
||||
(!bQueueDupe || pHistoryInfo->GetNZBInfo()->GetDupeScore() > iQueueScore) &&
|
||||
(!pHistoryDupe || pHistoryInfo->GetNZBInfo()->GetDupeScore() > pHistoryDupe->GetNZBInfo()->GetDupeScore()) &&
|
||||
SameNameOrKey(pHistoryInfo->GetNZBInfo()->GetName(), pHistoryInfo->GetNZBInfo()->GetDupeKey(), szNZBName, szDupeKey))
|
||||
{
|
||||
pHistoryDupe = pHistoryInfo;
|
||||
}
|
||||
}
|
||||
|
||||
// move that dupe-backup from history to download queue
|
||||
if (pHistoryDupe)
|
||||
{
|
||||
info("Found duplicate %s for %s", pHistoryDupe->GetNZBInfo()->GetName(), szNZBName);
|
||||
HistoryRedownload(pDownloadQueue, pHistoryDupe);
|
||||
}
|
||||
}
|
||||
|
||||
void DupeCoordinator::HistoryMark(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo, bool bGood)
|
||||
{
|
||||
char szNZBName[1024];
|
||||
pHistoryInfo->GetName(szNZBName, 1024);
|
||||
|
||||
info("Marking %s as %s", szNZBName, (bGood ? "good" : "bad"));
|
||||
|
||||
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo)
|
||||
{
|
||||
pHistoryInfo->GetNZBInfo()->SetMarkStatus(bGood ? NZBInfo::ksGood : NZBInfo::ksBad);
|
||||
}
|
||||
else if (pHistoryInfo->GetKind() == HistoryInfo::hkDupInfo)
|
||||
{
|
||||
pHistoryInfo->GetDupInfo()->SetStatus(bGood ? DupInfo::dsGood : DupInfo::dsBad);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not mark %s as bad: history item has wrong type", szNZBName);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_pOptions->GetDupeCheck() ||
|
||||
(pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo &&
|
||||
pHistoryInfo->GetNZBInfo()->GetDupeMode() == dmForce) ||
|
||||
(pHistoryInfo->GetKind() == HistoryInfo::hkDupInfo &&
|
||||
pHistoryInfo->GetDupInfo()->GetDupeMode() == dmForce))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (bGood)
|
||||
{
|
||||
// mark as good
|
||||
// moving all duplicates from history to dup-history
|
||||
HistoryCleanup(pDownloadQueue, pHistoryInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
// mark as bad
|
||||
const char* szDupeKey = pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo ? pHistoryInfo->GetNZBInfo()->GetDupeKey() :
|
||||
pHistoryInfo->GetKind() == HistoryInfo::hkDupInfo ? pHistoryInfo->GetDupInfo()->GetDupeKey() :
|
||||
NULL;
|
||||
ReturnBestDupe(pDownloadQueue, NULL, szNZBName, szDupeKey);
|
||||
}
|
||||
}
|
||||
|
||||
void DupeCoordinator::HistoryCleanup(DownloadQueue* pDownloadQueue, HistoryInfo* pMarkHistoryInfo)
|
||||
{
|
||||
const char* szDupeKey = pMarkHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo ? pMarkHistoryInfo->GetNZBInfo()->GetDupeKey() :
|
||||
pMarkHistoryInfo->GetKind() == HistoryInfo::hkDupInfo ? pMarkHistoryInfo->GetDupInfo()->GetDupeKey() :
|
||||
NULL;
|
||||
const char* szNZBName = pMarkHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo ? pMarkHistoryInfo->GetNZBInfo()->GetName() :
|
||||
pMarkHistoryInfo->GetKind() == HistoryInfo::hkDupInfo ? pMarkHistoryInfo->GetDupInfo()->GetName() :
|
||||
NULL;
|
||||
bool bChanged = false;
|
||||
int index = 0;
|
||||
|
||||
// traversing in a reverse order to delete items in order they were added to history
|
||||
// (just to produce the log-messages in a more logical order)
|
||||
for (HistoryList::reverse_iterator it = pDownloadQueue->GetHistoryList()->rbegin(); it != pDownloadQueue->GetHistoryList()->rend(); )
|
||||
{
|
||||
HistoryInfo* pHistoryInfo = *it;
|
||||
|
||||
if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo &&
|
||||
pHistoryInfo->GetNZBInfo()->GetDupeMode() != dmForce &&
|
||||
pHistoryInfo->GetNZBInfo()->GetDeleteStatus() == NZBInfo::dsDupe &&
|
||||
pHistoryInfo != pMarkHistoryInfo &&
|
||||
SameNameOrKey(pHistoryInfo->GetNZBInfo()->GetName(), pHistoryInfo->GetNZBInfo()->GetDupeKey(), szNZBName, szDupeKey))
|
||||
{
|
||||
HistoryTransformToDup(pDownloadQueue, pHistoryInfo, index);
|
||||
index++;
|
||||
it = pDownloadQueue->GetHistoryList()->rbegin() + index;
|
||||
bChanged = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
if (bChanged && g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
|
||||
}
|
||||
}
|
||||
|
||||
void DupeCoordinator::HistoryTransformToDup(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo, int rindex)
|
||||
{
|
||||
char szNiceName[1024];
|
||||
pHistoryInfo->GetName(szNiceName, 1024);
|
||||
|
||||
// replace history element
|
||||
DupInfo* pDupInfo = new DupInfo();
|
||||
pDupInfo->SetName(pHistoryInfo->GetNZBInfo()->GetName());
|
||||
pDupInfo->SetDupeKey(pHistoryInfo->GetNZBInfo()->GetDupeKey());
|
||||
pDupInfo->SetDupeScore(pHistoryInfo->GetNZBInfo()->GetDupeScore());
|
||||
pDupInfo->SetDupeMode(pHistoryInfo->GetNZBInfo()->GetDupeMode());
|
||||
pDupInfo->SetSize(pHistoryInfo->GetNZBInfo()->GetSize());
|
||||
pDupInfo->SetFullContentHash(pHistoryInfo->GetNZBInfo()->GetFullContentHash());
|
||||
pDupInfo->SetFilteredContentHash(pHistoryInfo->GetNZBInfo()->GetFilteredContentHash());
|
||||
|
||||
pDupInfo->SetStatus(
|
||||
pHistoryInfo->GetNZBInfo()->GetMarkStatus() == NZBInfo::ksGood ? DupInfo::dsGood :
|
||||
pHistoryInfo->GetNZBInfo()->GetMarkStatus() == NZBInfo::ksBad ? DupInfo::dsBad :
|
||||
pHistoryInfo->GetNZBInfo()->GetDeleteStatus() == NZBInfo::dsDupe ? DupInfo::dsDupe :
|
||||
pHistoryInfo->GetNZBInfo()->GetDeleteStatus() == NZBInfo::dsManual ? DupInfo::dsDeleted :
|
||||
IsDupeSuccess(pHistoryInfo->GetNZBInfo()) ? DupInfo::dsSuccess :
|
||||
DupInfo::dsFailed);
|
||||
|
||||
HistoryInfo* pNewHistoryInfo = new HistoryInfo(pDupInfo);
|
||||
pNewHistoryInfo->SetTime(pHistoryInfo->GetTime());
|
||||
(*pDownloadQueue->GetHistoryList())[pDownloadQueue->GetHistoryList()->size() - 1 - rindex] = pNewHistoryInfo;
|
||||
|
||||
DeleteQueuedFile(pHistoryInfo->GetNZBInfo()->GetQueuedFilename());
|
||||
|
||||
delete pHistoryInfo;
|
||||
info("Collection %s removed from history", szNiceName);
|
||||
}
|
||||
53
DupeCoordinator.h
Normal file
53
DupeCoordinator.h
Normal file
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007-2013 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef DUPECOORDINATOR_H
|
||||
#define DUPECOORDINATOR_H
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
class DupeCoordinator
|
||||
{
|
||||
private:
|
||||
bool IsDupeSuccess(NZBInfo* pNZBInfo);
|
||||
void ReturnBestDupe(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, const char* szNZBName, const char* szDupeKey);
|
||||
void HistoryReturnDupe(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo);
|
||||
void HistoryCleanup(DownloadQueue* pDownloadQueue, HistoryInfo* pMarkHistoryInfo);
|
||||
bool SameNameOrKey(const char* szName1, const char* szDupeKey1, const char* szName2, const char* szDupeKey2);
|
||||
|
||||
protected:
|
||||
virtual void HistoryRedownload(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo) = 0;
|
||||
virtual void DeleteQueuedFile(const char* szQueuedFile) = 0;
|
||||
|
||||
public:
|
||||
void NZBCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
|
||||
void NZBFound(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
|
||||
void HistoryMark(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo, bool bGood);
|
||||
void HistoryTransformToDup(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo, int rindex);
|
||||
};
|
||||
|
||||
#endif
|
||||
742
FeedCoordinator.cpp
Normal file
742
FeedCoordinator.cpp
Normal file
@@ -0,0 +1,742 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2013 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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>
|
||||
#include <sys/stat.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "FeedCoordinator.h"
|
||||
#include "Options.h"
|
||||
#include "WebDownloader.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include "FeedFile.h"
|
||||
#include "FeedFilter.h"
|
||||
#include "UrlCoordinator.h"
|
||||
#include "DiskState.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
extern UrlCoordinator* g_pUrlCoordinator;
|
||||
extern DiskState* g_pDiskState;
|
||||
|
||||
|
||||
FeedCoordinator::FeedCacheItem::FeedCacheItem(const char* szUrl, int iCacheTimeSec,const char* szCacheId,
|
||||
time_t tLastUsage, FeedItemInfos* pFeedItemInfos)
|
||||
{
|
||||
m_szUrl = strdup(szUrl);
|
||||
m_iCacheTimeSec = iCacheTimeSec;
|
||||
m_szCacheId = strdup(szCacheId);
|
||||
m_tLastUsage = tLastUsage;
|
||||
m_pFeedItemInfos = pFeedItemInfos;
|
||||
m_pFeedItemInfos->Retain();
|
||||
}
|
||||
|
||||
FeedCoordinator::FeedCacheItem::~FeedCacheItem()
|
||||
{
|
||||
free(m_szUrl);
|
||||
free(m_szCacheId);
|
||||
m_pFeedItemInfos->Release();
|
||||
}
|
||||
|
||||
FeedCoordinator::FeedCoordinator()
|
||||
{
|
||||
debug("Creating FeedCoordinator");
|
||||
m_bForce = false;
|
||||
m_bSave = false;
|
||||
|
||||
m_UrlCoordinatorObserver.m_pOwner = this;
|
||||
g_pUrlCoordinator->Attach(&m_UrlCoordinatorObserver);
|
||||
}
|
||||
|
||||
FeedCoordinator::~FeedCoordinator()
|
||||
{
|
||||
debug("Destroying FeedCoordinator");
|
||||
// Cleanup
|
||||
|
||||
debug("Deleting FeedDownloaders");
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_ActiveDownloads.clear();
|
||||
|
||||
debug("Deleting Feeds");
|
||||
for (Feeds::iterator it = m_Feeds.begin(); it != m_Feeds.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Feeds.clear();
|
||||
|
||||
debug("Deleting FeedCache");
|
||||
for (FeedCache::iterator it = m_FeedCache.begin(); it != m_FeedCache.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_FeedCache.clear();
|
||||
|
||||
debug("FeedCoordinator destroyed");
|
||||
}
|
||||
|
||||
void FeedCoordinator::AddFeed(FeedInfo* pFeedInfo)
|
||||
{
|
||||
m_Feeds.push_back(pFeedInfo);
|
||||
}
|
||||
|
||||
void FeedCoordinator::Run()
|
||||
{
|
||||
debug("Entering FeedCoordinator-loop");
|
||||
|
||||
m_mutexDownloads.Lock();
|
||||
if (g_pOptions->GetServerMode() && g_pOptions->GetSaveQueue() && g_pOptions->GetReloadQueue())
|
||||
{
|
||||
g_pDiskState->LoadFeeds(&m_Feeds, &m_FeedHistory);
|
||||
}
|
||||
m_mutexDownloads.Unlock();
|
||||
|
||||
int iSleepInterval = 100;
|
||||
int iUpdateCounter = 0;
|
||||
int iCleanupCounter = 60000;
|
||||
|
||||
while (!IsStopped())
|
||||
{
|
||||
usleep(iSleepInterval * 1000);
|
||||
|
||||
iUpdateCounter += iSleepInterval;
|
||||
if (iUpdateCounter >= 1000)
|
||||
{
|
||||
// this code should not be called too often, once per second is OK
|
||||
|
||||
if (!(g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2()) || m_bForce || g_pOptions->GetUrlForce())
|
||||
{
|
||||
m_mutexDownloads.Lock();
|
||||
time_t tCurrent = time(NULL);
|
||||
if ((int)m_ActiveDownloads.size() < g_pOptions->GetUrlConnections())
|
||||
{
|
||||
m_bForce = false;
|
||||
// check feed list and update feeds
|
||||
for (Feeds::iterator it = m_Feeds.begin(); it != m_Feeds.end(); it++)
|
||||
{
|
||||
FeedInfo* pFeedInfo = *it;
|
||||
if (((pFeedInfo->GetInterval() > 0 &&
|
||||
(tCurrent - pFeedInfo->GetLastUpdate() >= pFeedInfo->GetInterval() * 60 ||
|
||||
tCurrent < pFeedInfo->GetLastUpdate())) ||
|
||||
pFeedInfo->GetFetch()) &&
|
||||
pFeedInfo->GetStatus() != FeedInfo::fsRunning)
|
||||
{
|
||||
StartFeedDownload(pFeedInfo, pFeedInfo->GetFetch());
|
||||
}
|
||||
else if (pFeedInfo->GetFetch())
|
||||
{
|
||||
m_bForce = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_mutexDownloads.Unlock();
|
||||
}
|
||||
|
||||
CheckSaveFeeds();
|
||||
ResetHangingDownloads();
|
||||
iUpdateCounter = 0;
|
||||
}
|
||||
|
||||
iCleanupCounter += iSleepInterval;
|
||||
if (iCleanupCounter >= 60000)
|
||||
{
|
||||
// clean up feed history once a minute
|
||||
CleanupHistory();
|
||||
CleanupCache();
|
||||
CheckSaveFeeds();
|
||||
iCleanupCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// waiting for downloads
|
||||
debug("FeedCoordinator: waiting for Downloads to complete");
|
||||
bool completed = false;
|
||||
while (!completed)
|
||||
{
|
||||
m_mutexDownloads.Lock();
|
||||
completed = m_ActiveDownloads.size() == 0;
|
||||
m_mutexDownloads.Unlock();
|
||||
CheckSaveFeeds();
|
||||
usleep(100 * 1000);
|
||||
ResetHangingDownloads();
|
||||
}
|
||||
debug("FeedCoordinator: Downloads are completed");
|
||||
|
||||
debug("Exiting FeedCoordinator-loop");
|
||||
}
|
||||
|
||||
void FeedCoordinator::Stop()
|
||||
{
|
||||
Thread::Stop();
|
||||
|
||||
debug("Stopping UrlDownloads");
|
||||
m_mutexDownloads.Lock();
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
(*it)->Stop();
|
||||
}
|
||||
m_mutexDownloads.Unlock();
|
||||
debug("UrlDownloads are notified");
|
||||
}
|
||||
|
||||
void FeedCoordinator::ResetHangingDownloads()
|
||||
{
|
||||
const int TimeOut = g_pOptions->GetTerminateTimeout();
|
||||
if (TimeOut == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_mutexDownloads.Lock();
|
||||
time_t tm = ::time(NULL);
|
||||
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end();)
|
||||
{
|
||||
FeedDownloader* pFeedDownloader = *it;
|
||||
if (tm - pFeedDownloader->GetLastUpdateTime() > TimeOut &&
|
||||
pFeedDownloader->GetStatus() == FeedDownloader::adRunning)
|
||||
{
|
||||
debug("Terminating hanging download %s", pFeedDownloader->GetInfoName());
|
||||
if (pFeedDownloader->Terminate())
|
||||
{
|
||||
error("Terminated hanging download %s", pFeedDownloader->GetInfoName());
|
||||
pFeedDownloader->GetFeedInfo()->SetStatus(FeedInfo::fsUndefined);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not terminate hanging download %s", pFeedDownloader->GetInfoName());
|
||||
}
|
||||
m_ActiveDownloads.erase(it);
|
||||
// it's not safe to destroy pFeedDownloader, because the state of object is unknown
|
||||
delete pFeedDownloader;
|
||||
it = m_ActiveDownloads.begin();
|
||||
continue;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
m_mutexDownloads.Unlock();
|
||||
}
|
||||
|
||||
void FeedCoordinator::LogDebugInfo()
|
||||
{
|
||||
debug(" FeedCoordinator");
|
||||
debug(" ----------------");
|
||||
|
||||
m_mutexDownloads.Lock();
|
||||
debug(" Active Downloads: %i", m_ActiveDownloads.size());
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
FeedDownloader* pFeedDownloader = *it;
|
||||
pFeedDownloader->LogDebugInfo();
|
||||
}
|
||||
m_mutexDownloads.Unlock();
|
||||
}
|
||||
|
||||
void FeedCoordinator::StartFeedDownload(FeedInfo* pFeedInfo, bool bForce)
|
||||
{
|
||||
debug("Starting new FeedDownloader for %", pFeedInfo->GetName());
|
||||
|
||||
FeedDownloader* pFeedDownloader = new FeedDownloader();
|
||||
pFeedDownloader->SetAutoDestroy(true);
|
||||
pFeedDownloader->Attach(this);
|
||||
pFeedDownloader->SetFeedInfo(pFeedInfo);
|
||||
pFeedDownloader->SetURL(pFeedInfo->GetUrl());
|
||||
if (strlen(pFeedInfo->GetName()) > 0)
|
||||
{
|
||||
pFeedDownloader->SetInfoName(pFeedInfo->GetName());
|
||||
}
|
||||
else
|
||||
{
|
||||
char szUrlName[1024];
|
||||
UrlInfo::MakeNiceName(pFeedInfo->GetUrl(), "", szUrlName, sizeof(szUrlName));
|
||||
pFeedDownloader->SetInfoName(szUrlName);
|
||||
}
|
||||
pFeedDownloader->SetForce(bForce || g_pOptions->GetUrlForce());
|
||||
|
||||
char tmp[1024];
|
||||
|
||||
if (pFeedInfo->GetID() > 0)
|
||||
{
|
||||
snprintf(tmp, 1024, "%sfeed-%i.tmp", g_pOptions->GetTempDir(), pFeedInfo->GetID());
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(tmp, 1024, "%sfeed-%i-%i.tmp", g_pOptions->GetTempDir(), (int)time(NULL), rand());
|
||||
}
|
||||
|
||||
tmp[1024-1] = '\0';
|
||||
pFeedDownloader->SetOutputFilename(tmp);
|
||||
|
||||
pFeedInfo->SetStatus(FeedInfo::fsRunning);
|
||||
pFeedInfo->SetForce(bForce);
|
||||
pFeedInfo->SetFetch(false);
|
||||
|
||||
m_ActiveDownloads.push_back(pFeedDownloader);
|
||||
pFeedDownloader->Start();
|
||||
}
|
||||
|
||||
void FeedCoordinator::Update(Subject* pCaller, void* pAspect)
|
||||
{
|
||||
debug("Notification from FeedDownloader received");
|
||||
|
||||
FeedDownloader* pFeedDownloader = (FeedDownloader*) pCaller;
|
||||
if ((pFeedDownloader->GetStatus() == WebDownloader::adFinished) ||
|
||||
(pFeedDownloader->GetStatus() == WebDownloader::adFailed) ||
|
||||
(pFeedDownloader->GetStatus() == WebDownloader::adRetry))
|
||||
{
|
||||
FeedCompleted(pFeedDownloader);
|
||||
}
|
||||
}
|
||||
|
||||
void FeedCoordinator::FeedCompleted(FeedDownloader* pFeedDownloader)
|
||||
{
|
||||
debug("Feed downloaded");
|
||||
|
||||
FeedInfo* pFeedInfo = pFeedDownloader->GetFeedInfo();
|
||||
bool bStatusOK = pFeedDownloader->GetStatus() == WebDownloader::adFinished;
|
||||
if (bStatusOK)
|
||||
{
|
||||
pFeedInfo->SetOutputFilename(pFeedDownloader->GetOutputFilename());
|
||||
}
|
||||
|
||||
// delete Download from Queue
|
||||
m_mutexDownloads.Lock();
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
FeedDownloader* pa = *it;
|
||||
if (pa == pFeedDownloader)
|
||||
{
|
||||
m_ActiveDownloads.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_mutexDownloads.Unlock();
|
||||
|
||||
if (bStatusOK)
|
||||
{
|
||||
if (!pFeedInfo->GetPreview())
|
||||
{
|
||||
FeedFile* pFeedFile = FeedFile::Create(pFeedInfo->GetOutputFilename());
|
||||
remove(pFeedInfo->GetOutputFilename());
|
||||
|
||||
m_mutexDownloads.Lock();
|
||||
|
||||
if (pFeedFile)
|
||||
{
|
||||
ProcessFeed(pFeedInfo, pFeedFile->GetFeedItemInfos());
|
||||
delete pFeedFile;
|
||||
}
|
||||
|
||||
pFeedInfo->SetLastUpdate(time(NULL));
|
||||
pFeedInfo->SetForce(false);
|
||||
|
||||
m_bSave = true;
|
||||
m_mutexDownloads.Unlock();
|
||||
}
|
||||
pFeedInfo->SetStatus(FeedInfo::fsFinished);
|
||||
}
|
||||
else
|
||||
{
|
||||
pFeedInfo->SetStatus(FeedInfo::fsFailed);
|
||||
}
|
||||
}
|
||||
|
||||
void FeedCoordinator::FilterFeed(FeedInfo* pFeedInfo, FeedItemInfos* pFeedItemInfos)
|
||||
{
|
||||
debug("Filtering feed %s", pFeedInfo->GetName());
|
||||
|
||||
FeedFilter* pFeedFilter = NULL;
|
||||
if (pFeedInfo->GetFilter() && strlen(pFeedInfo->GetFilter()) > 0)
|
||||
{
|
||||
pFeedFilter = new FeedFilter(pFeedInfo->GetFilter());
|
||||
}
|
||||
|
||||
for (FeedItemInfos::iterator it = pFeedItemInfos->begin(); it != pFeedItemInfos->end(); it++)
|
||||
{
|
||||
FeedItemInfo* pFeedItemInfo = *it;
|
||||
pFeedItemInfo->SetMatchStatus(FeedItemInfo::msAccepted);
|
||||
pFeedItemInfo->SetMatchRule(0);
|
||||
pFeedItemInfo->SetPauseNzb(pFeedInfo->GetPauseNzb());
|
||||
pFeedItemInfo->SetPriority(pFeedInfo->GetPriority());
|
||||
pFeedItemInfo->SetAddCategory(pFeedInfo->GetCategory());
|
||||
pFeedItemInfo->SetDupeScore(0);
|
||||
pFeedItemInfo->SetDupeMode(dmScore);
|
||||
pFeedItemInfo->BuildDupeKey(NULL, NULL);
|
||||
if (pFeedFilter)
|
||||
{
|
||||
pFeedFilter->Match(pFeedItemInfo);
|
||||
}
|
||||
}
|
||||
|
||||
delete pFeedFilter;
|
||||
}
|
||||
|
||||
void FeedCoordinator::ProcessFeed(FeedInfo* pFeedInfo, FeedItemInfos* pFeedItemInfos)
|
||||
{
|
||||
debug("Process feed %s", pFeedInfo->GetName());
|
||||
|
||||
FilterFeed(pFeedInfo, pFeedItemInfos);
|
||||
|
||||
bool bFirstFetch = pFeedInfo->GetLastUpdate() == 0;
|
||||
int iAdded = 0;
|
||||
|
||||
for (FeedItemInfos::iterator it = pFeedItemInfos->begin(); it != pFeedItemInfos->end(); it++)
|
||||
{
|
||||
FeedItemInfo* pFeedItemInfo = *it;
|
||||
if (pFeedItemInfo->GetMatchStatus() == FeedItemInfo::msAccepted)
|
||||
{
|
||||
FeedHistoryInfo* pFeedHistoryInfo = m_FeedHistory.Find(pFeedItemInfo->GetUrl());
|
||||
FeedHistoryInfo::EStatus eStatus = FeedHistoryInfo::hsUnknown;
|
||||
if (bFirstFetch)
|
||||
{
|
||||
eStatus = FeedHistoryInfo::hsBacklog;
|
||||
}
|
||||
else if (!pFeedHistoryInfo)
|
||||
{
|
||||
DownloadItem(pFeedInfo, pFeedItemInfo);
|
||||
eStatus = FeedHistoryInfo::hsFetched;
|
||||
iAdded++;
|
||||
}
|
||||
|
||||
if (pFeedHistoryInfo)
|
||||
{
|
||||
pFeedHistoryInfo->SetLastSeen(time(NULL));
|
||||
}
|
||||
else
|
||||
{
|
||||
m_FeedHistory.Add(pFeedItemInfo->GetUrl(), eStatus, time(NULL));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (iAdded)
|
||||
{
|
||||
info("%s has %i new item(s)", pFeedInfo->GetName(), iAdded);
|
||||
}
|
||||
else
|
||||
{
|
||||
detail("%s has no new items", pFeedInfo->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
void FeedCoordinator::DownloadItem(FeedInfo* pFeedInfo, FeedItemInfo* pFeedItemInfo)
|
||||
{
|
||||
debug("Download %s from %s", pFeedItemInfo->GetUrl(), pFeedInfo->GetName());
|
||||
|
||||
UrlInfo* pUrlInfo = new UrlInfo();
|
||||
pUrlInfo->SetURL(pFeedItemInfo->GetUrl());
|
||||
|
||||
// add .nzb-extension if not present
|
||||
char szNZBName[1024];
|
||||
strncpy(szNZBName, pFeedItemInfo->GetFilename(), 1024);
|
||||
szNZBName[1024-1] = '\0';
|
||||
char* ext = strrchr(szNZBName, '.');
|
||||
if (ext && !strcasecmp(ext, ".nzb"))
|
||||
{
|
||||
*ext = '\0';
|
||||
}
|
||||
char szNZBName2[1024];
|
||||
snprintf(szNZBName2, 1024, "%s.nzb", szNZBName);
|
||||
Util::MakeValidFilename(szNZBName2, '_', false);
|
||||
if (strlen(szNZBName) > 0)
|
||||
{
|
||||
pUrlInfo->SetNZBFilename(szNZBName2);
|
||||
}
|
||||
|
||||
pUrlInfo->SetCategory(pFeedItemInfo->GetAddCategory());
|
||||
pUrlInfo->SetPriority(pFeedItemInfo->GetPriority());
|
||||
pUrlInfo->SetAddPaused(pFeedItemInfo->GetPauseNzb());
|
||||
pUrlInfo->SetDupeKey(pFeedItemInfo->GetDupeKey());
|
||||
pUrlInfo->SetDupeScore(pFeedItemInfo->GetDupeScore());
|
||||
pUrlInfo->SetDupeMode(pFeedItemInfo->GetDupeMode());
|
||||
pUrlInfo->SetForce(pFeedInfo->GetForce() || g_pOptions->GetUrlForce());
|
||||
g_pUrlCoordinator->AddUrlToQueue(pUrlInfo, false);
|
||||
}
|
||||
|
||||
bool FeedCoordinator::ViewFeed(int iID, FeedItemInfos** ppFeedItemInfos)
|
||||
{
|
||||
if (iID < 1 || iID > (int)m_Feeds.size())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FeedInfo* pFeedInfo = m_Feeds.at(iID - 1);
|
||||
|
||||
return PreviewFeed(pFeedInfo->GetName(), pFeedInfo->GetUrl(), pFeedInfo->GetFilter(),
|
||||
pFeedInfo->GetPauseNzb(), pFeedInfo->GetCategory(), pFeedInfo->GetPriority(),
|
||||
0, NULL, ppFeedItemInfos);
|
||||
}
|
||||
|
||||
bool FeedCoordinator::PreviewFeed(const char* szName, const char* szUrl, const char* szFilter,
|
||||
bool bPauseNzb, const char* szCategory, int iPriority,
|
||||
int iCacheTimeSec, const char* szCacheId, FeedItemInfos** ppFeedItemInfos)
|
||||
{
|
||||
debug("Preview feed %s", szName);
|
||||
|
||||
FeedInfo* pFeedInfo = new FeedInfo(0, szName, szUrl, 0, szFilter, bPauseNzb, szCategory, iPriority);
|
||||
pFeedInfo->SetPreview(true);
|
||||
|
||||
FeedItemInfos* pFeedItemInfos = NULL;
|
||||
bool bHasCache = false;
|
||||
if (iCacheTimeSec > 0 && *szCacheId != '\0')
|
||||
{
|
||||
m_mutexDownloads.Lock();
|
||||
for (FeedCache::iterator it = m_FeedCache.begin(); it != m_FeedCache.end(); it++)
|
||||
{
|
||||
FeedCacheItem* pFeedCacheItem = *it;
|
||||
if (!strcmp(pFeedCacheItem->GetCacheId(), szCacheId))
|
||||
{
|
||||
pFeedCacheItem->SetLastUsage(time(NULL));
|
||||
pFeedItemInfos = pFeedCacheItem->GetFeedItemInfos();
|
||||
pFeedItemInfos->Retain();
|
||||
bHasCache = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_mutexDownloads.Unlock();
|
||||
}
|
||||
|
||||
if (!bHasCache)
|
||||
{
|
||||
m_mutexDownloads.Lock();
|
||||
|
||||
bool bFirstFetch = true;
|
||||
for (Feeds::iterator it = m_Feeds.begin(); it != m_Feeds.end(); it++)
|
||||
{
|
||||
FeedInfo* pFeedInfo2 = *it;
|
||||
if (!strcmp(pFeedInfo2->GetUrl(), pFeedInfo->GetUrl()) &&
|
||||
!strcmp(pFeedInfo2->GetFilter(), pFeedInfo->GetFilter()) &&
|
||||
pFeedInfo2->GetLastUpdate() > 0)
|
||||
{
|
||||
bFirstFetch = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
StartFeedDownload(pFeedInfo, true);
|
||||
m_mutexDownloads.Unlock();
|
||||
|
||||
// wait until the download in a separate thread completes
|
||||
while (pFeedInfo->GetStatus() == FeedInfo::fsRunning)
|
||||
{
|
||||
usleep(100 * 1000);
|
||||
}
|
||||
|
||||
// now can process the feed
|
||||
|
||||
FeedFile* pFeedFile = NULL;
|
||||
|
||||
if (pFeedInfo->GetStatus() == FeedInfo::fsFinished)
|
||||
{
|
||||
pFeedFile = FeedFile::Create(pFeedInfo->GetOutputFilename());
|
||||
}
|
||||
|
||||
remove(pFeedInfo->GetOutputFilename());
|
||||
|
||||
if (!pFeedFile)
|
||||
{
|
||||
delete pFeedInfo;
|
||||
return false;
|
||||
}
|
||||
|
||||
pFeedItemInfos = pFeedFile->GetFeedItemInfos();
|
||||
pFeedItemInfos->Retain();
|
||||
delete pFeedFile;
|
||||
|
||||
for (FeedItemInfos::iterator it = pFeedItemInfos->begin(); it != pFeedItemInfos->end(); it++)
|
||||
{
|
||||
FeedItemInfo* pFeedItemInfo = *it;
|
||||
pFeedItemInfo->SetStatus(bFirstFetch ? FeedItemInfo::isBacklog : FeedItemInfo::isNew);
|
||||
FeedHistoryInfo* pFeedHistoryInfo = m_FeedHistory.Find(pFeedItemInfo->GetUrl());
|
||||
if (pFeedHistoryInfo)
|
||||
{
|
||||
pFeedItemInfo->SetStatus((FeedItemInfo::EStatus)pFeedHistoryInfo->GetStatus());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FilterFeed(pFeedInfo, pFeedItemInfos);
|
||||
delete pFeedInfo;
|
||||
|
||||
if (iCacheTimeSec > 0 && *szCacheId != '\0' && !bHasCache)
|
||||
{
|
||||
FeedCacheItem* pFeedCacheItem = new FeedCacheItem(szUrl, iCacheTimeSec, szCacheId, time(NULL), pFeedItemInfos);
|
||||
m_mutexDownloads.Lock();
|
||||
m_FeedCache.push_back(pFeedCacheItem);
|
||||
m_mutexDownloads.Unlock();
|
||||
}
|
||||
|
||||
*ppFeedItemInfos = pFeedItemInfos;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void FeedCoordinator::FetchFeed(int iID)
|
||||
{
|
||||
debug("FetchFeeds");
|
||||
|
||||
m_mutexDownloads.Lock();
|
||||
for (Feeds::iterator it = m_Feeds.begin(); it != m_Feeds.end(); it++)
|
||||
{
|
||||
FeedInfo* pFeedInfo = *it;
|
||||
if (pFeedInfo->GetID() == iID || iID == 0)
|
||||
{
|
||||
pFeedInfo->SetFetch(true);
|
||||
m_bForce = true;
|
||||
}
|
||||
}
|
||||
m_mutexDownloads.Unlock();
|
||||
}
|
||||
|
||||
void FeedCoordinator::UrlCoordinatorUpdate(Subject* pCaller, void* pAspect)
|
||||
{
|
||||
debug("Notification from URL-Coordinator received");
|
||||
|
||||
UrlCoordinator::Aspect* pUrlAspect = (UrlCoordinator::Aspect*)pAspect;
|
||||
if (pUrlAspect->eAction == UrlCoordinator::eaUrlCompleted)
|
||||
{
|
||||
m_mutexDownloads.Lock();
|
||||
FeedHistoryInfo* pFeedHistoryInfo = m_FeedHistory.Find(pUrlAspect->pUrlInfo->GetURL());
|
||||
if (pFeedHistoryInfo)
|
||||
{
|
||||
pFeedHistoryInfo->SetStatus(FeedHistoryInfo::hsFetched);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_FeedHistory.Add(pUrlAspect->pUrlInfo->GetURL(), FeedHistoryInfo::hsFetched, time(NULL));
|
||||
}
|
||||
m_bSave = true;
|
||||
m_mutexDownloads.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
bool FeedCoordinator::HasActiveDownloads()
|
||||
{
|
||||
m_mutexDownloads.Lock();
|
||||
bool bActive = !m_ActiveDownloads.empty();
|
||||
m_mutexDownloads.Unlock();
|
||||
return bActive;
|
||||
}
|
||||
|
||||
void FeedCoordinator::CheckSaveFeeds()
|
||||
{
|
||||
debug("CheckSaveFeeds");
|
||||
m_mutexDownloads.Lock();
|
||||
if (m_bSave)
|
||||
{
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->SaveFeeds(&m_Feeds, &m_FeedHistory);
|
||||
}
|
||||
m_bSave = false;
|
||||
}
|
||||
m_mutexDownloads.Unlock();
|
||||
}
|
||||
|
||||
void FeedCoordinator::CleanupHistory()
|
||||
{
|
||||
debug("CleanupHistory");
|
||||
|
||||
m_mutexDownloads.Lock();
|
||||
|
||||
time_t tOldestUpdate = time(NULL);
|
||||
|
||||
for (Feeds::iterator it = m_Feeds.begin(); it != m_Feeds.end(); it++)
|
||||
{
|
||||
FeedInfo* pFeedInfo = *it;
|
||||
if (pFeedInfo->GetLastUpdate() < tOldestUpdate)
|
||||
{
|
||||
tOldestUpdate = pFeedInfo->GetLastUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
time_t tBorderDate = tOldestUpdate - g_pOptions->GetFeedHistory();
|
||||
int i = 0;
|
||||
for (FeedHistory::iterator it = m_FeedHistory.begin(); it != m_FeedHistory.end(); )
|
||||
{
|
||||
FeedHistoryInfo* pFeedHistoryInfo = *it;
|
||||
if (pFeedHistoryInfo->GetLastSeen() < tBorderDate)
|
||||
{
|
||||
detail("Deleting %s from feed history", pFeedHistoryInfo->GetUrl());
|
||||
delete pFeedHistoryInfo;
|
||||
m_FeedHistory.erase(it);
|
||||
it = m_FeedHistory.begin() + i;
|
||||
m_bSave = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
m_mutexDownloads.Unlock();
|
||||
}
|
||||
|
||||
void FeedCoordinator::CleanupCache()
|
||||
{
|
||||
debug("CleanupCache");
|
||||
|
||||
m_mutexDownloads.Lock();
|
||||
|
||||
time_t tCurTime = time(NULL);
|
||||
int i = 0;
|
||||
for (FeedCache::iterator it = m_FeedCache.begin(); it != m_FeedCache.end(); )
|
||||
{
|
||||
FeedCacheItem* pFeedCacheItem = *it;
|
||||
if (pFeedCacheItem->GetLastUsage() + pFeedCacheItem->GetCacheTimeSec() < tCurTime ||
|
||||
pFeedCacheItem->GetLastUsage() > tCurTime)
|
||||
{
|
||||
debug("Deleting %s from feed cache", pFeedCacheItem->GetUrl());
|
||||
delete pFeedCacheItem;
|
||||
m_FeedCache.erase(it);
|
||||
it = m_FeedCache.begin() + i;
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
m_mutexDownloads.Unlock();
|
||||
}
|
||||
127
FeedCoordinator.h
Normal file
127
FeedCoordinator.h
Normal file
@@ -0,0 +1,127 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2013 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef FEEDCOORDINATOR_H
|
||||
#define FEEDCOORDINATOR_H
|
||||
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <time.h>
|
||||
|
||||
#include "Thread.h"
|
||||
#include "WebDownloader.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "FeedInfo.h"
|
||||
#include "Observer.h"
|
||||
|
||||
|
||||
class FeedDownloader;
|
||||
|
||||
class FeedCoordinator : public Thread, public Observer, public Subject
|
||||
{
|
||||
public:
|
||||
typedef std::list<FeedDownloader*> ActiveDownloads;
|
||||
|
||||
private:
|
||||
class UrlCoordinatorObserver: public Observer
|
||||
{
|
||||
public:
|
||||
FeedCoordinator* m_pOwner;
|
||||
virtual void Update(Subject* pCaller, void* pAspect) { m_pOwner->UrlCoordinatorUpdate(pCaller, pAspect); }
|
||||
};
|
||||
|
||||
class FeedCacheItem
|
||||
{
|
||||
private:
|
||||
char* m_szUrl;
|
||||
int m_iCacheTimeSec;
|
||||
char* m_szCacheId;
|
||||
time_t m_tLastUsage;
|
||||
FeedItemInfos* m_pFeedItemInfos;
|
||||
|
||||
public:
|
||||
FeedCacheItem(const char* szUrl, int iCacheTimeSec,const char* szCacheId,
|
||||
time_t tLastUsage, FeedItemInfos* pFeedItemInfos);
|
||||
~FeedCacheItem();
|
||||
const char* GetUrl() { return m_szUrl; }
|
||||
int GetCacheTimeSec() { return m_iCacheTimeSec; }
|
||||
const char* GetCacheId() { return m_szCacheId; }
|
||||
time_t GetLastUsage() { return m_tLastUsage; }
|
||||
void SetLastUsage(time_t tLastUsage) { m_tLastUsage = tLastUsage; }
|
||||
FeedItemInfos* GetFeedItemInfos() { return m_pFeedItemInfos; }
|
||||
};
|
||||
|
||||
typedef std::deque<FeedCacheItem*> FeedCache;
|
||||
|
||||
private:
|
||||
Feeds m_Feeds;
|
||||
ActiveDownloads m_ActiveDownloads;
|
||||
FeedHistory m_FeedHistory;
|
||||
Mutex m_mutexDownloads;
|
||||
UrlCoordinatorObserver m_UrlCoordinatorObserver;
|
||||
bool m_bForce;
|
||||
bool m_bSave;
|
||||
FeedCache m_FeedCache;
|
||||
|
||||
void StartFeedDownload(FeedInfo* pFeedInfo, bool bForce);
|
||||
void FeedCompleted(FeedDownloader* pFeedDownloader);
|
||||
void FilterFeed(FeedInfo* pFeedInfo, FeedItemInfos* pFeedItemInfos);
|
||||
void ProcessFeed(FeedInfo* pFeedInfo, FeedItemInfos* pFeedItemInfos);
|
||||
void DownloadItem(FeedInfo* pFeedInfo, FeedItemInfo* pFeedItemInfo);
|
||||
void ResetHangingDownloads();
|
||||
void UrlCoordinatorUpdate(Subject* pCaller, void* pAspect);
|
||||
void CleanupHistory();
|
||||
void CleanupCache();
|
||||
void CheckSaveFeeds();
|
||||
|
||||
public:
|
||||
FeedCoordinator();
|
||||
virtual ~FeedCoordinator();
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
void Update(Subject* pCaller, void* pAspect);
|
||||
void AddFeed(FeedInfo* pFeedInfo);
|
||||
bool PreviewFeed(const char* szName, const char* szUrl, const char* szFilter,
|
||||
bool bPauseNzb, const char* szCategory, int iPriority,
|
||||
int iCacheTimeSec, const char* szCacheId, FeedItemInfos** ppFeedItemInfos);
|
||||
bool ViewFeed(int iID, FeedItemInfos** ppFeedItemInfos);
|
||||
void FetchFeed(int iID);
|
||||
bool HasActiveDownloads();
|
||||
Feeds* GetFeeds() { return &m_Feeds; }
|
||||
|
||||
void LogDebugInfo();
|
||||
};
|
||||
|
||||
class FeedDownloader : public WebDownloader
|
||||
{
|
||||
private:
|
||||
FeedInfo* m_pFeedInfo;
|
||||
|
||||
public:
|
||||
void SetFeedInfo(FeedInfo* pFeedInfo) { m_pFeedInfo = pFeedInfo; }
|
||||
FeedInfo* GetFeedInfo() { return m_pFeedInfo; }
|
||||
};
|
||||
|
||||
#endif
|
||||
609
FeedFile.cpp
Normal file
609
FeedFile.cpp
Normal file
@@ -0,0 +1,609 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2013 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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.tlb> named_guids
|
||||
using namespace MSXML;
|
||||
#else
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/xmlreader.h>
|
||||
#include <libxml/xmlerror.h>
|
||||
#include <libxml/entities.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "FeedFile.h"
|
||||
#include "Log.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Options.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
FeedFile::FeedFile(const char* szFileName)
|
||||
{
|
||||
debug("Creating FeedFile");
|
||||
|
||||
m_szFileName = strdup(szFileName);
|
||||
m_pFeedItemInfos = new FeedItemInfos();
|
||||
m_pFeedItemInfos->Retain();
|
||||
|
||||
#ifndef WIN32
|
||||
m_pFeedItemInfo = NULL;
|
||||
m_szTagContent = NULL;
|
||||
m_iTagContentLen = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
FeedFile::~FeedFile()
|
||||
{
|
||||
debug("Destroying FeedFile");
|
||||
|
||||
// Cleanup
|
||||
free(m_szFileName);
|
||||
m_pFeedItemInfos->Release();
|
||||
|
||||
#ifndef WIN32
|
||||
delete m_pFeedItemInfo;
|
||||
free(m_szTagContent);
|
||||
#endif
|
||||
}
|
||||
|
||||
void FeedFile::LogDebugInfo()
|
||||
{
|
||||
debug(" FeedFile %s", m_szFileName);
|
||||
}
|
||||
|
||||
void FeedFile::AddItem(FeedItemInfo* pFeedItemInfo)
|
||||
{
|
||||
m_pFeedItemInfos->Add(pFeedItemInfo);
|
||||
}
|
||||
|
||||
void FeedFile::ParseSubject(FeedItemInfo* pFeedItemInfo)
|
||||
{
|
||||
// if title has quatation marks we use only part within quatation marks
|
||||
char* p = (char*)pFeedItemInfo->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)
|
||||
{
|
||||
char* filename = (char*)malloc(len + 1);
|
||||
strncpy(filename, start, len);
|
||||
filename[len] = '\0';
|
||||
|
||||
char* ext = strrchr(filename, '.');
|
||||
if (ext && !strcasecmp(ext, ".par2"))
|
||||
{
|
||||
*ext = '\0';
|
||||
}
|
||||
|
||||
pFeedItemInfo->SetFilename(filename);
|
||||
free(filename);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pFeedItemInfo->SetFilename(pFeedItemInfo->GetTitle());
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
FeedFile* FeedFile::Create(const char* szFileName)
|
||||
{
|
||||
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);
|
||||
|
||||
// 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);
|
||||
|
||||
VARIANT_BOOL success = doc->load(v);
|
||||
if (success == VARIANT_FALSE)
|
||||
{
|
||||
_bstr_t r(doc->GetparseError()->reason);
|
||||
const char* szErrMsg = r;
|
||||
error("Error parsing rss feed: %s", szErrMsg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
FeedFile* pFile = new FeedFile(szFileName);
|
||||
if (!pFile->ParseFeed(doc))
|
||||
{
|
||||
delete pFile;
|
||||
pFile = NULL;
|
||||
}
|
||||
|
||||
return pFile;
|
||||
}
|
||||
|
||||
void FeedFile::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 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);
|
||||
|
||||
FeedItemInfo* pFeedItemInfo = new FeedItemInfo();
|
||||
AddItem(pFeedItemInfo);
|
||||
|
||||
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());
|
||||
pFeedItemInfo->SetTitle(title);
|
||||
ParseSubject(pFeedItemInfo);
|
||||
|
||||
// <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)
|
||||
{
|
||||
pFeedItemInfo->SetTime(unixtime);
|
||||
}
|
||||
}
|
||||
|
||||
// <category>Movies > HD</category>
|
||||
tag = node->selectSingleNode("category");
|
||||
if (tag)
|
||||
{
|
||||
_bstr_t category(tag->Gettext());
|
||||
pFeedItemInfo->SetCategory(category);
|
||||
}
|
||||
|
||||
// <description>long text</description>
|
||||
tag = node->selectSingleNode("description");
|
||||
if (tag)
|
||||
{
|
||||
_bstr_t description(tag->Gettext());
|
||||
pFeedItemInfo->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());
|
||||
pFeedItemInfo->SetUrl(url);
|
||||
}
|
||||
|
||||
attr = tag->Getattributes()->getNamedItem("length");
|
||||
if (attr)
|
||||
{
|
||||
_bstr_t size(attr->Gettext());
|
||||
long long lSize = atoll(size);
|
||||
pFeedItemInfo->SetSize(lSize);
|
||||
}
|
||||
}
|
||||
|
||||
if (!pFeedItemInfo->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());
|
||||
pFeedItemInfo->SetUrl(link);
|
||||
}
|
||||
|
||||
|
||||
// newznab special
|
||||
|
||||
//<newznab:attr name="size" value="5423523453534" />
|
||||
if (pFeedItemInfo->GetSize() == 0)
|
||||
{
|
||||
tag = node->selectSingleNode("newznab:attr[@name='size']");
|
||||
if (tag)
|
||||
{
|
||||
attr = tag->Getattributes()->getNamedItem("value");
|
||||
if (attr)
|
||||
{
|
||||
_bstr_t size(attr->Gettext());
|
||||
long long lSize = atoll(size);
|
||||
pFeedItemInfo->SetSize(lSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//<newznab:attr name="imdb" value="1588173"/>
|
||||
tag = node->selectSingleNode("newznab:attr[@name='imdb']");
|
||||
if (tag)
|
||||
{
|
||||
attr = tag->Getattributes()->getNamedItem("value");
|
||||
if (attr)
|
||||
{
|
||||
_bstr_t val(attr->Gettext());
|
||||
int iVal = atoi(val);
|
||||
pFeedItemInfo->SetImdbId(iVal);
|
||||
}
|
||||
}
|
||||
|
||||
//<newznab:attr name="rageid" value="33877"/>
|
||||
tag = node->selectSingleNode("newznab:attr[@name='rageid']");
|
||||
if (tag)
|
||||
{
|
||||
attr = tag->Getattributes()->getNamedItem("value");
|
||||
if (attr)
|
||||
{
|
||||
_bstr_t val(attr->Gettext());
|
||||
int iVal = atoi(val);
|
||||
pFeedItemInfo->SetRageId(iVal);
|
||||
}
|
||||
}
|
||||
|
||||
//<newznab:attr name="episode" value="E09"/>
|
||||
//<newznab:attr name="episode" value="9"/>
|
||||
tag = node->selectSingleNode("newznab:attr[@name='episode']");
|
||||
if (tag)
|
||||
{
|
||||
attr = tag->Getattributes()->getNamedItem("value");
|
||||
if (attr)
|
||||
{
|
||||
_bstr_t val(attr->Gettext());
|
||||
pFeedItemInfo->SetEpisode(val);
|
||||
}
|
||||
}
|
||||
|
||||
//<newznab:attr name="season" value="S03"/>
|
||||
//<newznab:attr name="season" value="3"/>
|
||||
tag = node->selectSingleNode("newznab:attr[@name='season']");
|
||||
if (tag)
|
||||
{
|
||||
attr = tag->Getattributes()->getNamedItem("value");
|
||||
if (attr)
|
||||
{
|
||||
_bstr_t val(attr->Gettext());
|
||||
pFeedItemInfo->SetSeason(val);
|
||||
}
|
||||
}
|
||||
|
||||
MSXML::IXMLDOMNodeListPtr itemList = node->selectNodes("newznab: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 name(name->Gettext());
|
||||
_bstr_t val(value->Gettext());
|
||||
pFeedItemInfo->GetAttributes()->Add(name, val);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
FeedFile* FeedFile::Create(const char* szFileName)
|
||||
{
|
||||
FeedFile* pFile = new FeedFile(szFileName);
|
||||
|
||||
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);
|
||||
|
||||
pFile->m_bIgnoreNextError = false;
|
||||
|
||||
int ret = xmlSAXUserParseFile(&SAX_handler, pFile, szFileName);
|
||||
|
||||
if (ret != 0)
|
||||
{
|
||||
error("Failed to parse rss feed");
|
||||
delete pFile;
|
||||
pFile = NULL;
|
||||
}
|
||||
|
||||
return pFile;
|
||||
}
|
||||
|
||||
void FeedFile::Parse_StartElement(const char *name, const char **atts)
|
||||
{
|
||||
ResetTagContent();
|
||||
|
||||
if (!strcmp("item", name))
|
||||
{
|
||||
delete m_pFeedItemInfo;
|
||||
m_pFeedItemInfo = new FeedItemInfo();
|
||||
}
|
||||
else if (!strcmp("enclosure", name) && m_pFeedItemInfo)
|
||||
{
|
||||
//<enclosure url="http://myindexer.com/fetch/9eeb264aecce961a6e0d" length="150263340" type="application/x-nzb" />
|
||||
for (; *atts; atts+=2)
|
||||
{
|
||||
if (!strcmp("url", atts[0]))
|
||||
{
|
||||
char* szUrl = strdup(atts[1]);
|
||||
WebUtil::XmlDecode(szUrl);
|
||||
m_pFeedItemInfo->SetUrl(szUrl);
|
||||
free(szUrl);
|
||||
}
|
||||
else if (!strcmp("length", atts[0]))
|
||||
{
|
||||
long long lSize = atoll(atts[1]);
|
||||
m_pFeedItemInfo->SetSize(lSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m_pFeedItemInfo && !strcmp("newznab:attr", name) &&
|
||||
atts[0] && atts[1] && atts[2] && atts[3] &&
|
||||
!strcmp("name", atts[0]) && !strcmp("value", atts[2]))
|
||||
{
|
||||
m_pFeedItemInfo->GetAttributes()->Add(atts[1], atts[3]);
|
||||
|
||||
//<newznab:attr name="size" value="5423523453534" />
|
||||
if (m_pFeedItemInfo->GetSize() == 0 &&
|
||||
!strcmp("size", atts[1]))
|
||||
{
|
||||
long long lSize = atoll(atts[3]);
|
||||
m_pFeedItemInfo->SetSize(lSize);
|
||||
}
|
||||
|
||||
//<newznab:attr name="imdb" value="1588173"/>
|
||||
else if (!strcmp("imdb", atts[1]))
|
||||
{
|
||||
m_pFeedItemInfo->SetImdbId(atoi(atts[3]));
|
||||
}
|
||||
|
||||
//<newznab:attr name="rageid" value="33877"/>
|
||||
else if (!strcmp("rageid", atts[1]))
|
||||
{
|
||||
m_pFeedItemInfo->SetRageId(atoi(atts[3]));
|
||||
}
|
||||
|
||||
//<newznab:attr name="episode" value="E09"/>
|
||||
//<newznab:attr name="episode" value="9"/>
|
||||
else if (!strcmp("episode", atts[1]))
|
||||
{
|
||||
m_pFeedItemInfo->SetEpisode(atts[3]);
|
||||
}
|
||||
|
||||
//<newznab:attr name="season" value="S03"/>
|
||||
//<newznab:attr name="season" value="3"/>
|
||||
else if (!strcmp("season", atts[1]))
|
||||
{
|
||||
m_pFeedItemInfo->SetSeason(atts[3]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FeedFile::Parse_EndElement(const char *name)
|
||||
{
|
||||
if (!strcmp("item", name))
|
||||
{
|
||||
// Close the file element, add the new file to file-list
|
||||
AddItem(m_pFeedItemInfo);
|
||||
m_pFeedItemInfo = NULL;
|
||||
}
|
||||
else if (!strcmp("title", name) && m_pFeedItemInfo)
|
||||
{
|
||||
m_pFeedItemInfo->SetTitle(m_szTagContent);
|
||||
ResetTagContent();
|
||||
ParseSubject(m_pFeedItemInfo);
|
||||
}
|
||||
else if (!strcmp("link", name) && m_pFeedItemInfo &&
|
||||
(!m_pFeedItemInfo->GetUrl() || strlen(m_pFeedItemInfo->GetUrl()) == 0))
|
||||
{
|
||||
m_pFeedItemInfo->SetUrl(m_szTagContent);
|
||||
ResetTagContent();
|
||||
}
|
||||
else if (!strcmp("category", name) && m_pFeedItemInfo)
|
||||
{
|
||||
m_pFeedItemInfo->SetCategory(m_szTagContent);
|
||||
ResetTagContent();
|
||||
}
|
||||
else if (!strcmp("description", name) && m_pFeedItemInfo)
|
||||
{
|
||||
m_pFeedItemInfo->SetDescription(m_szTagContent);
|
||||
ResetTagContent();
|
||||
}
|
||||
else if (!strcmp("pubDate", name) && m_pFeedItemInfo)
|
||||
{
|
||||
time_t unixtime = WebUtil::ParseRfc822DateTime(m_szTagContent);
|
||||
if (unixtime > 0)
|
||||
{
|
||||
m_pFeedItemInfo->SetTime(unixtime);
|
||||
}
|
||||
ResetTagContent();
|
||||
}
|
||||
}
|
||||
|
||||
void FeedFile::Parse_Content(const char *buf, int len)
|
||||
{
|
||||
m_szTagContent = (char*)realloc(m_szTagContent, m_iTagContentLen + len + 1);
|
||||
strncpy(m_szTagContent + m_iTagContentLen, buf, len);
|
||||
m_iTagContentLen += len;
|
||||
m_szTagContent[m_iTagContentLen] = '\0';
|
||||
}
|
||||
|
||||
void FeedFile::ResetTagContent()
|
||||
{
|
||||
free(m_szTagContent);
|
||||
m_szTagContent = NULL;
|
||||
m_iTagContentLen = 0;
|
||||
}
|
||||
|
||||
void FeedFile::SAX_StartElement(FeedFile* pFile, const char *name, const char **atts)
|
||||
{
|
||||
pFile->Parse_StartElement(name, atts);
|
||||
}
|
||||
|
||||
void FeedFile::SAX_EndElement(FeedFile* pFile, const char *name)
|
||||
{
|
||||
pFile->Parse_EndElement(name);
|
||||
}
|
||||
|
||||
void FeedFile::SAX_characters(FeedFile* pFile, 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
|
||||
pFile->Parse_Content(str + off, newlen);
|
||||
}
|
||||
}
|
||||
|
||||
void* FeedFile::SAX_getEntity(FeedFile* pFile, const char * name)
|
||||
{
|
||||
xmlEntityPtr e = xmlGetPredefinedEntity((xmlChar* )name);
|
||||
if (!e)
|
||||
{
|
||||
warn("entity not found");
|
||||
pFile->m_bIgnoreNextError = true;
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
void FeedFile::SAX_error(FeedFile* pFile, const char *msg, ...)
|
||||
{
|
||||
if (pFile->m_bIgnoreNextError)
|
||||
{
|
||||
pFile->m_bIgnoreNextError = false;
|
||||
return;
|
||||
}
|
||||
|
||||
va_list argp;
|
||||
va_start(argp, msg);
|
||||
char szErrMsg[1024];
|
||||
vsnprintf(szErrMsg, sizeof(szErrMsg), msg, argp);
|
||||
szErrMsg[1024-1] = '\0';
|
||||
va_end(argp);
|
||||
|
||||
// remove trailing CRLF
|
||||
for (char* pend = szErrMsg + strlen(szErrMsg) - 1; pend >= szErrMsg && (*pend == '\n' || *pend == '\r' || *pend == ' '); pend--) *pend = '\0';
|
||||
error("Error parsing rss feed: %s", szErrMsg);
|
||||
}
|
||||
#endif
|
||||
70
FeedFile.h
Normal file
70
FeedFile.h
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2013 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef FEEDFILE_H
|
||||
#define FEEDFILE_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "FeedInfo.h"
|
||||
|
||||
class FeedFile
|
||||
{
|
||||
private:
|
||||
FeedItemInfos* m_pFeedItemInfos;
|
||||
char* m_szFileName;
|
||||
|
||||
FeedFile(const char* szFileName);
|
||||
void AddItem(FeedItemInfo* pFeedItemInfo);
|
||||
void ParseSubject(FeedItemInfo* pFeedItemInfo);
|
||||
#ifdef WIN32
|
||||
bool ParseFeed(IUnknown* nzb);
|
||||
static void EncodeURL(const char* szFilename, char* szURL);
|
||||
#else
|
||||
FeedItemInfo* m_pFeedItemInfo;
|
||||
char* m_szTagContent;
|
||||
int m_iTagContentLen;
|
||||
bool m_bIgnoreNextError;
|
||||
|
||||
static void SAX_StartElement(FeedFile* pFile, const char *name, const char **atts);
|
||||
static void SAX_EndElement(FeedFile* pFile, const char *name);
|
||||
static void SAX_characters(FeedFile* pFile, const char * xmlstr, int len);
|
||||
static void* SAX_getEntity(FeedFile* pFile, const char * name);
|
||||
static void SAX_error(FeedFile* pFile, 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
|
||||
|
||||
public:
|
||||
virtual ~FeedFile();
|
||||
static FeedFile* Create(const char* szFileName);
|
||||
FeedItemInfos* GetFeedItemInfos() { return m_pFeedItemInfos; }
|
||||
|
||||
void LogDebugInfo();
|
||||
};
|
||||
|
||||
#endif
|
||||
1192
FeedFilter.cpp
Normal file
1192
FeedFilter.cpp
Normal file
File diff suppressed because it is too large
Load Diff
186
FeedFilter.h
Normal file
186
FeedFilter.h
Normal file
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2013 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef FEEDFILTER_H
|
||||
#define FEEDFILTER_H
|
||||
|
||||
#include "DownloadInfo.h"
|
||||
#include "FeedInfo.h"
|
||||
#include "Util.h"
|
||||
|
||||
class FeedFilter
|
||||
{
|
||||
private:
|
||||
typedef std::deque<char*> RefValues;
|
||||
|
||||
enum ETermCommand
|
||||
{
|
||||
fcText,
|
||||
fcRegex,
|
||||
fcEqual,
|
||||
fcLess,
|
||||
fcLessEqual,
|
||||
fcGreater,
|
||||
fcGreaterEqual,
|
||||
fcOpeningBrace,
|
||||
fcClosingBrace,
|
||||
fcOrOperator
|
||||
};
|
||||
|
||||
class Term
|
||||
{
|
||||
private:
|
||||
bool m_bPositive;
|
||||
char* m_szField;
|
||||
ETermCommand m_eCommand;
|
||||
char* m_szParam;
|
||||
long long m_iIntParam;
|
||||
double m_fFloatParam;
|
||||
bool m_bFloat;
|
||||
RegEx* m_pRegEx;
|
||||
RefValues* m_pRefValues;
|
||||
|
||||
bool GetFieldData(const char* szField, FeedItemInfo* pFeedItemInfo,
|
||||
const char** StrValue, long long* IntValue);
|
||||
bool ParseParam(const char* szField, const char* szParam);
|
||||
bool ParseSizeParam(const char* szParam);
|
||||
bool ParseAgeParam(const char* szParam);
|
||||
bool ParseNumericParam(const char* szParam);
|
||||
bool MatchValue(const char* szStrValue, long long iIntValue);
|
||||
bool MatchText(const char* szStrValue);
|
||||
bool MatchRegex(const char* szStrValue);
|
||||
void FillWildMaskRefValues(const char* szStrValue, WildMask* pMask, int iRefOffset);
|
||||
void FillRegExRefValues(const char* szStrValue, RegEx* pRegEx);
|
||||
|
||||
public:
|
||||
Term();
|
||||
~Term();
|
||||
void SetRefValues(RefValues* pRefValues) { m_pRefValues = pRefValues; }
|
||||
bool Compile(char* szToken);
|
||||
bool Match(FeedItemInfo* pFeedItemInfo);
|
||||
ETermCommand GetCommand() { return m_eCommand; }
|
||||
};
|
||||
|
||||
typedef std::deque<Term*> TermList;
|
||||
|
||||
enum ERuleCommand
|
||||
{
|
||||
frAccept,
|
||||
frReject,
|
||||
frRequire,
|
||||
frOptions,
|
||||
frComment
|
||||
};
|
||||
|
||||
class Rule
|
||||
{
|
||||
private:
|
||||
bool m_bIsValid;
|
||||
ERuleCommand m_eCommand;
|
||||
char* m_szCategory;
|
||||
int m_iPriority;
|
||||
int m_iAddPriority;
|
||||
bool m_bPause;
|
||||
int m_iDupeScore;
|
||||
int m_iAddDupeScore;
|
||||
char* m_szDupeKey;
|
||||
char* m_szAddDupeKey;
|
||||
EDupeMode m_eDupeMode;
|
||||
char* m_szSeries;
|
||||
char* m_szRageId;
|
||||
bool m_bHasCategory;
|
||||
bool m_bHasPriority;
|
||||
bool m_bHasAddPriority;
|
||||
bool m_bHasPause;
|
||||
bool m_bHasDupeScore;
|
||||
bool m_bHasAddDupeScore;
|
||||
bool m_bHasDupeKey;
|
||||
bool m_bHasAddDupeKey;
|
||||
bool m_bHasDupeMode;
|
||||
bool m_bPatCategory;
|
||||
bool m_bPatDupeKey;
|
||||
bool m_bPatAddDupeKey;
|
||||
bool m_bHasSeries;
|
||||
bool m_bHasRageId;
|
||||
char* m_szPatCategory;
|
||||
char* m_szPatDupeKey;
|
||||
char* m_szPatAddDupeKey;
|
||||
TermList m_Terms;
|
||||
RefValues m_RefValues;
|
||||
|
||||
char* CompileCommand(char* szRule);
|
||||
char* CompileOptions(char* szRule);
|
||||
bool CompileTerm(char* szTerm);
|
||||
bool MatchExpression(FeedItemInfo* pFeedItemInfo);
|
||||
|
||||
public:
|
||||
Rule();
|
||||
~Rule();
|
||||
void Compile(char* szRule);
|
||||
bool IsValid() { return m_bIsValid; }
|
||||
ERuleCommand GetCommand() { return m_eCommand; }
|
||||
const char* GetCategory() { return m_szCategory; }
|
||||
int GetPriority() { return m_iPriority; }
|
||||
int GetAddPriority() { return m_iAddPriority; }
|
||||
bool GetPause() { return m_bPause; }
|
||||
const char* GetDupeKey() { return m_szDupeKey; }
|
||||
const char* GetAddDupeKey() { return m_szAddDupeKey; }
|
||||
int GetDupeScore() { return m_iDupeScore; }
|
||||
int GetAddDupeScore() { return m_iAddDupeScore; }
|
||||
EDupeMode GetDupeMode() { return m_eDupeMode; }
|
||||
const char* GetRageId() { return m_szRageId; }
|
||||
const char* GetSeries() { return m_szSeries; }
|
||||
bool HasCategory() { return m_bHasCategory; }
|
||||
bool HasPriority() { return m_bHasPriority; }
|
||||
bool HasAddPriority() { return m_bHasAddPriority; }
|
||||
bool HasPause() { return m_bHasPause; }
|
||||
bool HasDupeScore() { return m_bHasDupeScore; }
|
||||
bool HasAddDupeScore() { return m_bHasAddDupeScore; }
|
||||
bool HasDupeKey() { return m_bHasDupeKey; }
|
||||
bool HasAddDupeKey() { return m_bHasAddDupeKey; }
|
||||
bool HasDupeMode() { return m_bHasDupeMode; }
|
||||
bool HasRageId() { return m_bHasRageId; }
|
||||
bool HasSeries() { return m_bHasSeries; }
|
||||
bool Match(FeedItemInfo* pFeedItemInfo);
|
||||
void ExpandRefValues(FeedItemInfo* pFeedItemInfo, char** pDestStr, char* pPatStr);
|
||||
const char* GetRefValue(FeedItemInfo* pFeedItemInfo, const char* szVarName);
|
||||
};
|
||||
|
||||
typedef std::deque<Rule*> RuleList;
|
||||
|
||||
private:
|
||||
RuleList m_Rules;
|
||||
|
||||
void Compile(const char* szFilter);
|
||||
void CompileRule(char* szRule);
|
||||
void ApplyOptions(Rule* pRule, FeedItemInfo* pFeedItemInfo);
|
||||
|
||||
public:
|
||||
FeedFilter(const char* szFilter);
|
||||
~FeedFilter();
|
||||
void Match(FeedItemInfo* pFeedItemInfo);
|
||||
};
|
||||
|
||||
#endif
|
||||
440
FeedInfo.cpp
Normal file
440
FeedInfo.cpp
Normal file
@@ -0,0 +1,440 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007-2013 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision: 0 $
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "FeedInfo.h"
|
||||
#include "Util.h"
|
||||
|
||||
|
||||
FeedInfo::FeedInfo(int iID, const char* szName, const char* szUrl, int iInterval,
|
||||
const char* szFilter, bool bPauseNzb, const char* szCategory, int iPriority)
|
||||
{
|
||||
m_iID = iID;
|
||||
m_szName = strdup(szName ? szName : "");
|
||||
m_szUrl = strdup(szUrl ? szUrl : "");
|
||||
m_szFilter = strdup(szFilter ? szFilter : "");
|
||||
m_iFilterHash = Util::HashBJ96(szFilter, strlen(szFilter), 0);
|
||||
m_szCategory = strdup(szCategory ? szCategory : "");
|
||||
m_iInterval = iInterval;
|
||||
m_bPauseNzb = bPauseNzb;
|
||||
m_iPriority = iPriority;
|
||||
m_tLastUpdate = 0;
|
||||
m_bPreview = false;
|
||||
m_eStatus = fsUndefined;
|
||||
m_szOutputFilename = NULL;
|
||||
m_bFetch = false;
|
||||
m_bForce = false;
|
||||
}
|
||||
|
||||
FeedInfo::~FeedInfo()
|
||||
{
|
||||
free(m_szName);
|
||||
free(m_szUrl);
|
||||
free(m_szFilter);
|
||||
free(m_szCategory);
|
||||
free(m_szOutputFilename);
|
||||
}
|
||||
|
||||
void FeedInfo::SetOutputFilename(const char* szOutputFilename)
|
||||
{
|
||||
free(m_szOutputFilename);
|
||||
m_szOutputFilename = strdup(szOutputFilename);
|
||||
}
|
||||
|
||||
|
||||
FeedItemInfo::Attr::Attr(const char* szName, const char* szValue)
|
||||
{
|
||||
m_szName = strdup(szName ? szName : "");
|
||||
m_szValue = strdup(szValue ? szValue : "");
|
||||
}
|
||||
|
||||
FeedItemInfo::Attr::~Attr()
|
||||
{
|
||||
free(m_szName);
|
||||
free(m_szValue);
|
||||
}
|
||||
|
||||
|
||||
FeedItemInfo::Attributes::~Attributes()
|
||||
{
|
||||
for (iterator it = begin(); it != end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
}
|
||||
|
||||
void FeedItemInfo::Attributes::Add(const char* szName, const char* szValue)
|
||||
{
|
||||
push_back(new Attr(szName, szValue));
|
||||
}
|
||||
|
||||
FeedItemInfo::Attr* FeedItemInfo::Attributes::Find(const char* szName)
|
||||
{
|
||||
for (iterator it = begin(); it != end(); it++)
|
||||
{
|
||||
Attr* pAttr = *it;
|
||||
if (!strcasecmp(pAttr->GetName(), szName))
|
||||
{
|
||||
return pAttr;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
FeedItemInfo::FeedItemInfo()
|
||||
{
|
||||
m_pSharedFeedData = NULL;
|
||||
m_szTitle = NULL;
|
||||
m_szFilename = NULL;
|
||||
m_szUrl = NULL;
|
||||
m_szCategory = strdup("");
|
||||
m_lSize = 0;
|
||||
m_tTime = 0;
|
||||
m_iImdbId = 0;
|
||||
m_iRageId = 0;
|
||||
m_szDescription = strdup("");
|
||||
m_szSeason = NULL;
|
||||
m_szEpisode = NULL;
|
||||
m_iSeasonNum = 0;
|
||||
m_iEpisodeNum = 0;
|
||||
m_bSeasonEpisodeParsed = false;
|
||||
m_szAddCategory = strdup("");
|
||||
m_bPauseNzb = false;
|
||||
m_iPriority = 0;
|
||||
m_eStatus = isUnknown;
|
||||
m_eMatchStatus = msIgnored;
|
||||
m_iMatchRule = 0;
|
||||
m_szDupeKey = NULL;
|
||||
m_iDupeScore = 0;
|
||||
m_eDupeMode = dmScore;
|
||||
}
|
||||
|
||||
FeedItemInfo::~FeedItemInfo()
|
||||
{
|
||||
free(m_szTitle);
|
||||
free(m_szFilename);
|
||||
free(m_szUrl);
|
||||
free(m_szCategory);
|
||||
free(m_szDescription);
|
||||
free(m_szSeason);
|
||||
free(m_szEpisode);
|
||||
free(m_szAddCategory);
|
||||
free(m_szDupeKey);
|
||||
}
|
||||
|
||||
void FeedItemInfo::SetTitle(const char* szTitle)
|
||||
{
|
||||
free(m_szTitle);
|
||||
m_szTitle = szTitle ? strdup(szTitle) : NULL;
|
||||
}
|
||||
|
||||
void FeedItemInfo::SetFilename(const char* szFilename)
|
||||
{
|
||||
free(m_szFilename);
|
||||
m_szFilename = szFilename ? strdup(szFilename) : NULL;
|
||||
}
|
||||
|
||||
void FeedItemInfo::SetUrl(const char* szUrl)
|
||||
{
|
||||
free(m_szUrl);
|
||||
m_szUrl = szUrl ? strdup(szUrl) : NULL;
|
||||
}
|
||||
|
||||
void FeedItemInfo::SetCategory(const char* szCategory)
|
||||
{
|
||||
free(m_szCategory);
|
||||
m_szCategory = strdup(szCategory ? szCategory: "");
|
||||
}
|
||||
|
||||
void FeedItemInfo::SetDescription(const char* szDescription)
|
||||
{
|
||||
free(m_szDescription);
|
||||
m_szDescription = strdup(szDescription ? szDescription: "");
|
||||
}
|
||||
|
||||
void FeedItemInfo::SetSeason(const char* szSeason)
|
||||
{
|
||||
free(m_szSeason);
|
||||
m_szSeason = szSeason ? strdup(szSeason) : NULL;
|
||||
m_iSeasonNum = szSeason ? ParsePrefixedInt(szSeason) : 0;
|
||||
}
|
||||
|
||||
void FeedItemInfo::SetEpisode(const char* szEpisode)
|
||||
{
|
||||
free(m_szEpisode);
|
||||
m_szEpisode = szEpisode ? strdup(szEpisode) : NULL;
|
||||
m_iEpisodeNum = szEpisode ? ParsePrefixedInt(szEpisode) : 0;
|
||||
}
|
||||
|
||||
int FeedItemInfo::ParsePrefixedInt(const char *szValue)
|
||||
{
|
||||
const char* szVal = szValue;
|
||||
if (!strchr("0123456789", *szVal))
|
||||
{
|
||||
szVal++;
|
||||
}
|
||||
return atoi(szVal);
|
||||
}
|
||||
|
||||
void FeedItemInfo::SetAddCategory(const char* szAddCategory)
|
||||
{
|
||||
free(m_szAddCategory);
|
||||
m_szAddCategory = strdup(szAddCategory ? szAddCategory : "");
|
||||
}
|
||||
|
||||
void FeedItemInfo::SetDupeKey(const char* szDupeKey)
|
||||
{
|
||||
free(m_szDupeKey);
|
||||
m_szDupeKey = strdup(szDupeKey ? szDupeKey : "");
|
||||
}
|
||||
|
||||
void FeedItemInfo::AppendDupeKey(const char* szExtraDupeKey)
|
||||
{
|
||||
if (!m_szDupeKey || *m_szDupeKey == '\0' || !szExtraDupeKey || *szExtraDupeKey == '\0')
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int iLen = (m_szDupeKey ? strlen(m_szDupeKey) : 0) + 1 + strlen(szExtraDupeKey) + 1;
|
||||
char* szNewKey = (char*)malloc(iLen);
|
||||
snprintf(szNewKey, iLen, "%s-%s", m_szDupeKey, szExtraDupeKey);
|
||||
szNewKey[iLen - 1] = '\0';
|
||||
|
||||
free(m_szDupeKey);
|
||||
m_szDupeKey = szNewKey;
|
||||
}
|
||||
|
||||
void FeedItemInfo::BuildDupeKey(const char* szRageId, const char* szSeries)
|
||||
{
|
||||
int iRageId = szRageId && *szRageId ? atoi(szRageId) : m_iRageId;
|
||||
|
||||
free(m_szDupeKey);
|
||||
|
||||
if (m_iImdbId != 0)
|
||||
{
|
||||
m_szDupeKey = (char*)malloc(20);
|
||||
snprintf(m_szDupeKey, 20, "imdb=%i", m_iImdbId);
|
||||
}
|
||||
else if (szSeries && *szSeries && GetSeasonNum() != 0 && GetEpisodeNum() != 0)
|
||||
{
|
||||
int iLen = strlen(szSeries) + 50;
|
||||
m_szDupeKey = (char*)malloc(iLen);
|
||||
snprintf(m_szDupeKey, iLen, "series=%s-%s-%s", szSeries, m_szSeason, m_szEpisode);
|
||||
m_szDupeKey[iLen-1] = '\0';
|
||||
}
|
||||
else if (iRageId != 0 && GetSeasonNum() != 0 && GetEpisodeNum() != 0)
|
||||
{
|
||||
m_szDupeKey = (char*)malloc(100);
|
||||
snprintf(m_szDupeKey, 100, "rageid=%i-%s-%s", iRageId, m_szSeason, m_szEpisode);
|
||||
m_szDupeKey[100-1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
m_szDupeKey = strdup("");
|
||||
}
|
||||
}
|
||||
|
||||
int FeedItemInfo::GetSeasonNum()
|
||||
{
|
||||
if (!m_szSeason && !m_bSeasonEpisodeParsed)
|
||||
{
|
||||
ParseSeasonEpisode();
|
||||
}
|
||||
|
||||
return m_iSeasonNum;
|
||||
}
|
||||
|
||||
int FeedItemInfo::GetEpisodeNum()
|
||||
{
|
||||
if (!m_szEpisode && !m_bSeasonEpisodeParsed)
|
||||
{
|
||||
ParseSeasonEpisode();
|
||||
}
|
||||
|
||||
return m_iEpisodeNum;
|
||||
}
|
||||
|
||||
void FeedItemInfo::ParseSeasonEpisode()
|
||||
{
|
||||
m_bSeasonEpisodeParsed = true;
|
||||
|
||||
RegEx* pRegEx = m_pSharedFeedData->GetSeasonEpisodeRegEx();
|
||||
|
||||
if (pRegEx->Match(m_szTitle))
|
||||
{
|
||||
char szRegValue[100];
|
||||
char szValue[100];
|
||||
|
||||
snprintf(szValue, 100, "S%02d", atoi(m_szTitle + pRegEx->GetMatchStart(1)));
|
||||
szValue[100-1] = '\0';
|
||||
SetSeason(szValue);
|
||||
|
||||
int iLen = pRegEx->GetMatchLen(2);
|
||||
iLen = iLen < 99 ? iLen : 99;
|
||||
strncpy(szRegValue, m_szTitle + pRegEx->GetMatchStart(2), pRegEx->GetMatchLen(2));
|
||||
szRegValue[iLen] = '\0';
|
||||
snprintf(szValue, 100, "E%s", szRegValue);
|
||||
szValue[100-1] = '\0';
|
||||
Util::ReduceStr(szValue, "-", "");
|
||||
for (char* p = szValue; *p; p++) *p = toupper(*p); // convert string to uppercase e02 -> E02
|
||||
SetEpisode(szValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
FeedHistoryInfo::FeedHistoryInfo(const char* szUrl, FeedHistoryInfo::EStatus eStatus, time_t tLastSeen)
|
||||
{
|
||||
m_szUrl = szUrl ? strdup(szUrl) : NULL;
|
||||
m_eStatus = eStatus;
|
||||
m_tLastSeen = tLastSeen;
|
||||
}
|
||||
|
||||
FeedHistoryInfo::~FeedHistoryInfo()
|
||||
{
|
||||
free(m_szUrl);
|
||||
}
|
||||
|
||||
|
||||
FeedHistory::~FeedHistory()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
void FeedHistory::Clear()
|
||||
{
|
||||
for (iterator it = begin(); it != end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
clear();
|
||||
}
|
||||
|
||||
void FeedHistory::Add(const char* szUrl, FeedHistoryInfo::EStatus eStatus, time_t tLastSeen)
|
||||
{
|
||||
push_back(new FeedHistoryInfo(szUrl, eStatus, tLastSeen));
|
||||
}
|
||||
|
||||
void FeedHistory::Remove(const char* szUrl)
|
||||
{
|
||||
for (iterator it = begin(); it != end(); it++)
|
||||
{
|
||||
FeedHistoryInfo* pFeedHistoryInfo = *it;
|
||||
if (!strcmp(pFeedHistoryInfo->GetUrl(), szUrl))
|
||||
{
|
||||
delete pFeedHistoryInfo;
|
||||
erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FeedHistoryInfo* FeedHistory::Find(const char* szUrl)
|
||||
{
|
||||
for (iterator it = begin(); it != end(); it++)
|
||||
{
|
||||
FeedHistoryInfo* pFeedHistoryInfo = *it;
|
||||
if (!strcmp(pFeedHistoryInfo->GetUrl(), szUrl))
|
||||
{
|
||||
return pFeedHistoryInfo;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
FeedItemInfos::FeedItemInfos()
|
||||
{
|
||||
debug("Creating FeedItemInfos");
|
||||
|
||||
m_iRefCount = 0;
|
||||
}
|
||||
|
||||
FeedItemInfos::~FeedItemInfos()
|
||||
{
|
||||
debug("Destroing FeedItemInfos");
|
||||
|
||||
for (iterator it = begin(); it != end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
}
|
||||
|
||||
void FeedItemInfos::Retain()
|
||||
{
|
||||
m_iRefCount++;
|
||||
}
|
||||
|
||||
void FeedItemInfos::Release()
|
||||
{
|
||||
m_iRefCount--;
|
||||
if (m_iRefCount <= 0)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
void FeedItemInfos::Add(FeedItemInfo* pFeedItemInfo)
|
||||
{
|
||||
push_back(pFeedItemInfo);
|
||||
pFeedItemInfo->SetSharedFeedData(&m_SharedFeedData);
|
||||
}
|
||||
|
||||
|
||||
SharedFeedData::SharedFeedData()
|
||||
{
|
||||
m_pSeasonEpisodeRegEx = NULL;
|
||||
}
|
||||
|
||||
SharedFeedData::~SharedFeedData()
|
||||
{
|
||||
delete m_pSeasonEpisodeRegEx;
|
||||
}
|
||||
|
||||
RegEx* SharedFeedData::GetSeasonEpisodeRegEx()
|
||||
{
|
||||
if (!m_pSeasonEpisodeRegEx)
|
||||
{
|
||||
m_pSeasonEpisodeRegEx = new RegEx("[^[:alnum:]]s?([0-9]+)[ex]([0-9]+(-?e[0-9]+)?)[^[:alnum:]]", 10);
|
||||
}
|
||||
|
||||
return m_pSeasonEpisodeRegEx;
|
||||
}
|
||||
278
FeedInfo.h
Normal file
278
FeedInfo.h
Normal file
@@ -0,0 +1,278 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007-2013 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision: 0 $
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef FEEDINFO_H
|
||||
#define FEEDINFO_H
|
||||
|
||||
#include <deque>
|
||||
#include <time.h>
|
||||
|
||||
#include "Util.h"
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
|
||||
class FeedInfo
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
fsUndefined,
|
||||
fsRunning,
|
||||
fsFinished,
|
||||
fsFailed
|
||||
};
|
||||
|
||||
private:
|
||||
int m_iID;
|
||||
char* m_szName;
|
||||
char* m_szUrl;
|
||||
int m_iInterval;
|
||||
char* m_szFilter;
|
||||
unsigned int m_iFilterHash;
|
||||
bool m_bPauseNzb;
|
||||
char* m_szCategory;
|
||||
int m_iPriority;
|
||||
time_t m_tLastUpdate;
|
||||
bool m_bPreview;
|
||||
EStatus m_eStatus;
|
||||
char* m_szOutputFilename;
|
||||
bool m_bFetch;
|
||||
bool m_bForce;
|
||||
|
||||
public:
|
||||
FeedInfo(int iID, const char* szName, const char* szUrl, int iInterval,
|
||||
const char* szFilter, bool bPauseNzb, const char* szCategory, int iPriority);
|
||||
~FeedInfo();
|
||||
int GetID() { return m_iID; }
|
||||
const char* GetName() { return m_szName; }
|
||||
const char* GetUrl() { return m_szUrl; }
|
||||
int GetInterval() { return m_iInterval; }
|
||||
const char* GetFilter() { return m_szFilter; }
|
||||
unsigned int GetFilterHash() { return m_iFilterHash; }
|
||||
bool GetPauseNzb() { return m_bPauseNzb; }
|
||||
const char* GetCategory() { return m_szCategory; }
|
||||
int GetPriority() { return m_iPriority; }
|
||||
time_t GetLastUpdate() { return m_tLastUpdate; }
|
||||
void SetLastUpdate(time_t tLastUpdate) { m_tLastUpdate = tLastUpdate; }
|
||||
bool GetPreview() { return m_bPreview; }
|
||||
void SetPreview(bool bPreview) { m_bPreview = bPreview; }
|
||||
EStatus GetStatus() { return m_eStatus; }
|
||||
void SetStatus(EStatus Status) { m_eStatus = Status; }
|
||||
const char* GetOutputFilename() { return m_szOutputFilename; }
|
||||
void SetOutputFilename(const char* szOutputFilename);
|
||||
bool GetFetch() { return m_bFetch; }
|
||||
void SetFetch(bool bFetch) { m_bFetch = bFetch; }
|
||||
bool GetForce() { return m_bForce; }
|
||||
void SetForce(bool bForce) { m_bForce = bForce; }
|
||||
};
|
||||
|
||||
typedef std::deque<FeedInfo*> Feeds;
|
||||
|
||||
class SharedFeedData
|
||||
{
|
||||
private:
|
||||
RegEx* m_pSeasonEpisodeRegEx;
|
||||
|
||||
public:
|
||||
SharedFeedData();
|
||||
~SharedFeedData();
|
||||
RegEx* GetSeasonEpisodeRegEx();
|
||||
};
|
||||
|
||||
class FeedItemInfo
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
isUnknown,
|
||||
isBacklog,
|
||||
isFetched,
|
||||
isNew
|
||||
};
|
||||
|
||||
enum EMatchStatus
|
||||
{
|
||||
msIgnored,
|
||||
msAccepted,
|
||||
msRejected
|
||||
};
|
||||
|
||||
class Attr
|
||||
{
|
||||
private:
|
||||
char* m_szName;
|
||||
char* m_szValue;
|
||||
public:
|
||||
Attr(const char* szName, const char* szValue);
|
||||
~Attr();
|
||||
const char* GetName() { return m_szName; }
|
||||
const char* GetValue() { return m_szValue; }
|
||||
};
|
||||
|
||||
typedef std::deque<Attr*> AttributesBase;
|
||||
|
||||
class Attributes: public AttributesBase
|
||||
{
|
||||
public:
|
||||
~Attributes();
|
||||
void Add(const char* szName, const char* szValue);
|
||||
Attr* Find(const char* szName);
|
||||
};
|
||||
|
||||
private:
|
||||
char* m_szTitle;
|
||||
char* m_szFilename;
|
||||
char* m_szUrl;
|
||||
time_t m_tTime;
|
||||
long long m_lSize;
|
||||
char* m_szCategory;
|
||||
int m_iImdbId;
|
||||
int m_iRageId;
|
||||
char* m_szDescription;
|
||||
char* m_szSeason;
|
||||
char* m_szEpisode;
|
||||
int m_iSeasonNum;
|
||||
int m_iEpisodeNum;
|
||||
bool m_bSeasonEpisodeParsed;
|
||||
char* m_szAddCategory;
|
||||
bool m_bPauseNzb;
|
||||
int m_iPriority;
|
||||
EStatus m_eStatus;
|
||||
EMatchStatus m_eMatchStatus;
|
||||
int m_iMatchRule;
|
||||
char* m_szDupeKey;
|
||||
int m_iDupeScore;
|
||||
EDupeMode m_eDupeMode;
|
||||
SharedFeedData* m_pSharedFeedData;
|
||||
Attributes m_Attributes;
|
||||
|
||||
int ParsePrefixedInt(const char *szValue);
|
||||
void ParseSeasonEpisode();
|
||||
|
||||
public:
|
||||
FeedItemInfo();
|
||||
~FeedItemInfo();
|
||||
void SetSharedFeedData(SharedFeedData* pSharedFeedData) { m_pSharedFeedData = pSharedFeedData; }
|
||||
const char* GetTitle() { return m_szTitle; }
|
||||
void SetTitle(const char* szTitle);
|
||||
const char* GetFilename() { return m_szFilename; }
|
||||
void SetFilename(const char* szFilename);
|
||||
const char* GetUrl() { return m_szUrl; }
|
||||
void SetUrl(const char* szUrl);
|
||||
long long GetSize() { return m_lSize; }
|
||||
void SetSize(long long lSize) { m_lSize = lSize; }
|
||||
const char* GetCategory() { return m_szCategory; }
|
||||
void SetCategory(const char* szCategory);
|
||||
int GetImdbId() { return m_iImdbId; }
|
||||
void SetImdbId(int iImdbId) { m_iImdbId = iImdbId; }
|
||||
int GetRageId() { return m_iRageId; }
|
||||
void SetRageId(int iRageId) { m_iRageId = iRageId; }
|
||||
const char* GetDescription() { return m_szDescription; }
|
||||
void SetDescription(const char* szDescription);
|
||||
const char* GetSeason() { return m_szSeason; }
|
||||
void SetSeason(const char* szSeason);
|
||||
const char* GetEpisode() { return m_szEpisode; }
|
||||
void SetEpisode(const char* szEpisode);
|
||||
int GetSeasonNum();
|
||||
int GetEpisodeNum();
|
||||
const char* GetAddCategory() { return m_szAddCategory; }
|
||||
void SetAddCategory(const char* szAddCategory);
|
||||
bool GetPauseNzb() { return m_bPauseNzb; }
|
||||
void SetPauseNzb(bool bPauseNzb) { m_bPauseNzb = bPauseNzb; }
|
||||
int GetPriority() { return m_iPriority; }
|
||||
void SetPriority(int iPriority) { m_iPriority = iPriority; }
|
||||
time_t GetTime() { return m_tTime; }
|
||||
void SetTime(time_t tTime) { m_tTime = tTime; }
|
||||
EStatus GetStatus() { return m_eStatus; }
|
||||
void SetStatus(EStatus eStatus) { m_eStatus = eStatus; }
|
||||
EMatchStatus GetMatchStatus() { return m_eMatchStatus; }
|
||||
void SetMatchStatus(EMatchStatus eMatchStatus) { m_eMatchStatus = eMatchStatus; }
|
||||
int GetMatchRule() { return m_iMatchRule; }
|
||||
void SetMatchRule(int iMatchRule) { m_iMatchRule = iMatchRule; }
|
||||
const char* GetDupeKey() { return m_szDupeKey; }
|
||||
void SetDupeKey(const char* szDupeKey);
|
||||
void AppendDupeKey(const char* szExtraDupeKey);
|
||||
void BuildDupeKey(const char* szRageId, const char* szSeries);
|
||||
int GetDupeScore() { return m_iDupeScore; }
|
||||
void SetDupeScore(int iDupeScore) { m_iDupeScore = iDupeScore; }
|
||||
EDupeMode GetDupeMode() { return m_eDupeMode; }
|
||||
void SetDupeMode(EDupeMode eDupeMode) { m_eDupeMode = eDupeMode; }
|
||||
Attributes* GetAttributes() { return &m_Attributes; }
|
||||
};
|
||||
|
||||
typedef std::deque<FeedItemInfo*> FeedItemInfosBase;
|
||||
|
||||
class FeedItemInfos : public FeedItemInfosBase
|
||||
{
|
||||
private:
|
||||
int m_iRefCount;
|
||||
SharedFeedData m_SharedFeedData;
|
||||
|
||||
public:
|
||||
FeedItemInfos();
|
||||
~FeedItemInfos();
|
||||
void Retain();
|
||||
void Release();
|
||||
void Add(FeedItemInfo* pFeedItemInfo);
|
||||
};
|
||||
|
||||
class FeedHistoryInfo
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
hsUnknown,
|
||||
hsBacklog,
|
||||
hsFetched
|
||||
};
|
||||
|
||||
private:
|
||||
char* m_szUrl;
|
||||
EStatus m_eStatus;
|
||||
time_t m_tLastSeen;
|
||||
|
||||
public:
|
||||
FeedHistoryInfo(const char* szUrl, EStatus eStatus, time_t tLastSeen);
|
||||
~FeedHistoryInfo();
|
||||
const char* GetUrl() { return m_szUrl; }
|
||||
EStatus GetStatus() { return m_eStatus; }
|
||||
void SetStatus(EStatus Status) { m_eStatus = Status; }
|
||||
time_t GetLastSeen() { return m_tLastSeen; }
|
||||
void SetLastSeen(time_t tLastSeen) { m_tLastSeen = tLastSeen; }
|
||||
};
|
||||
|
||||
typedef std::deque<FeedHistoryInfo*> FeedHistoryBase;
|
||||
|
||||
class FeedHistory : public FeedHistoryBase
|
||||
{
|
||||
public:
|
||||
~FeedHistory();
|
||||
void Clear();
|
||||
void Add(const char* szUrl, FeedHistoryInfo::EStatus eStatus, time_t tLastSeen);
|
||||
void Remove(const char* szUrl);
|
||||
FeedHistoryInfo* Find(const char* szUrl);
|
||||
};
|
||||
|
||||
#endif
|
||||
166
Frontend.cpp
166
Frontend.cpp
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* 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-2013 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -34,6 +34,7 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
@@ -47,12 +48,10 @@
|
||||
#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()
|
||||
@@ -63,18 +62,17 @@ Frontend::Frontend()
|
||||
m_iNeededLogEntries = 0;
|
||||
m_bSummary = false;
|
||||
m_bFileList = false;
|
||||
m_fCurrentDownloadSpeed = 0;
|
||||
m_iCurrentDownloadSpeed = 0;
|
||||
m_lRemainingSize = 0;
|
||||
m_bPause = false;
|
||||
m_fDownloadLimit = 0;
|
||||
m_bPauseDownload = false;
|
||||
m_bPauseDownload2 = false;
|
||||
m_iDownloadLimit = 0;
|
||||
m_iThreadCount = 0;
|
||||
m_iPostJobCount = 0;
|
||||
m_iUpTimeSec = 0;
|
||||
m_iDnTimeSec = 0;
|
||||
m_iAllBytes = 0;
|
||||
m_bStandBy = 0;
|
||||
m_RemoteMessages.clear();
|
||||
m_RemoteQueue.clear();
|
||||
m_iUpdateInterval = g_pOptions->GetUpdateInterval();
|
||||
}
|
||||
|
||||
@@ -88,7 +86,7 @@ bool Frontend::PrepareData()
|
||||
}
|
||||
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());
|
||||
printf("\nUnable to send request to nzbget-server at %s (port %i) \n", g_pOptions->GetControlIP(), g_pOptions->GetControlPort());
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
@@ -97,14 +95,15 @@ bool Frontend::PrepareData()
|
||||
{
|
||||
if (m_bSummary)
|
||||
{
|
||||
m_fCurrentDownloadSpeed = g_pQueueCoordinator->CalcCurrentDownloadSpeed();
|
||||
m_iCurrentDownloadSpeed = g_pQueueCoordinator->CalcCurrentDownloadSpeed();
|
||||
m_lRemainingSize = g_pQueueCoordinator->CalcRemainingSize();
|
||||
m_bPause = g_pOptions->GetPause();
|
||||
m_fDownloadLimit = g_pOptions->GetDownloadRate();
|
||||
m_bPauseDownload = g_pOptions->GetPauseDownload();
|
||||
m_bPauseDownload2 = g_pOptions->GetPauseDownload2();
|
||||
m_iDownloadLimit = g_pOptions->GetDownloadRate();
|
||||
m_iThreadCount = Thread::GetThreadCount();
|
||||
PostQueue* pPostQueue = g_pPrePostProcessor->LockPostQueue();
|
||||
PostQueue* pPostQueue = g_pQueueCoordinator->LockQueue()->GetPostQueue();
|
||||
m_iPostJobCount = pPostQueue->size();
|
||||
g_pPrePostProcessor->UnlockPostQueue();
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
g_pQueueCoordinator->CalcStat(&m_iUpTimeSec, &m_iDnTimeSec, &m_iAllBytes, &m_bStandBy);
|
||||
}
|
||||
}
|
||||
@@ -121,11 +120,11 @@ void Frontend::FreeData()
|
||||
}
|
||||
m_RemoteMessages.clear();
|
||||
|
||||
for (DownloadQueue::iterator it = m_RemoteQueue.begin(); it != m_RemoteQueue.end(); it++)
|
||||
for (FileQueue::iterator it = m_RemoteQueue.GetFileQueue()->begin(); it != m_RemoteQueue.GetFileQueue()->end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_RemoteQueue.clear();
|
||||
m_RemoteQueue.GetFileQueue()->clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -149,7 +148,7 @@ void Frontend::UnlockMessages()
|
||||
}
|
||||
}
|
||||
|
||||
DownloadQueue * Frontend::LockQueue()
|
||||
DownloadQueue* Frontend::LockQueue()
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
@@ -174,27 +173,35 @@ bool Frontend::IsRemoteMode()
|
||||
return g_pOptions->GetRemoteClientMode();
|
||||
}
|
||||
|
||||
void Frontend::ServerPauseUnpause(bool bPause)
|
||||
void Frontend::ServerPauseUnpause(bool bPause, bool bSecondRegister)
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
RequestPauseUnpause(bPause);
|
||||
RequestPauseUnpause(bPause, bSecondRegister);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pOptions->SetPause(bPause);
|
||||
g_pOptions->SetResumeTime(0);
|
||||
if (bSecondRegister)
|
||||
{
|
||||
g_pOptions->SetPauseDownload2(bPause);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pOptions->SetPauseDownload(bPause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Frontend::ServerSetDownloadRate(float fRate)
|
||||
void Frontend::ServerSetDownloadRate(int iRate)
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
RequestSetDownloadRate(fRate);
|
||||
RequestSetDownloadRate(iRate);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pOptions->SetDownloadRate(fRate);
|
||||
g_pOptions->SetDownloadRate(iRate);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -214,11 +221,11 @@ bool Frontend::ServerEditQueue(QueueEditor::EEditAction eAction, int iOffset, in
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
return RequestEditQueue(eAction, iOffset, iID);
|
||||
return RequestEditQueue((eRemoteEditAction)eAction, iOffset, iID);
|
||||
}
|
||||
else
|
||||
{
|
||||
return g_pQueueCoordinator->GetQueueEditor()->EditEntry(iID, true, eAction, iOffset);
|
||||
return g_pQueueCoordinator->GetQueueEditor()->EditEntry(iID, true, eAction, iOffset, NULL);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -228,16 +235,19 @@ void Frontend::InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int
|
||||
pMessageBase->m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
|
||||
pMessageBase->m_iType = htonl(iRequest);
|
||||
pMessageBase->m_iStructSize = htonl(iSize);
|
||||
strncpy(pMessageBase->m_szPassword, g_pOptions->GetServerPassword(), NZBREQUESTPASSWORDSIZE);
|
||||
|
||||
strncpy(pMessageBase->m_szUsername, g_pOptions->GetControlUsername(), NZBREQUESTPASSWORDSIZE - 1);
|
||||
pMessageBase->m_szUsername[NZBREQUESTPASSWORDSIZE - 1] = '\0';
|
||||
|
||||
strncpy(pMessageBase->m_szPassword, g_pOptions->GetControlPassword(), NZBREQUESTPASSWORDSIZE);
|
||||
pMessageBase->m_szPassword[NZBREQUESTPASSWORDSIZE - 1] = '\0';
|
||||
}
|
||||
|
||||
bool Frontend::RequestMessages()
|
||||
{
|
||||
NetAddress netAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
Connection connection(&netAddress);
|
||||
Connection connection(g_pOptions->GetControlIP(), g_pOptions->GetControlPort(), false);
|
||||
|
||||
bool OK = connection.Connect() >= 0;
|
||||
bool OK = connection.Connect();
|
||||
if (!OK)
|
||||
{
|
||||
return false;
|
||||
@@ -255,15 +265,15 @@ bool Frontend::RequestMessages()
|
||||
LogRequest.m_iIDFrom = 0;
|
||||
}
|
||||
|
||||
if (connection.Send((char*)(&LogRequest), sizeof(LogRequest)) < 0)
|
||||
if (!connection.Send((char*)(&LogRequest), sizeof(LogRequest)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now listen for the returned log
|
||||
SNZBLogResponse LogResponse;
|
||||
int iResponseLen = connection.Recv((char*) &LogResponse, sizeof(LogResponse));
|
||||
if (iResponseLen != sizeof(LogResponse) ||
|
||||
bool bRead = connection.Recv((char*) &LogResponse, sizeof(LogResponse));
|
||||
if (!bRead ||
|
||||
(int)ntohl(LogResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
|
||||
ntohl(LogResponse.m_MessageBase.m_iStructSize) != sizeof(LogResponse))
|
||||
{
|
||||
@@ -274,7 +284,7 @@ bool Frontend::RequestMessages()
|
||||
if (ntohl(LogResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
pBuf = (char*)malloc(ntohl(LogResponse.m_iTrailingDataLength));
|
||||
if (!connection.RecvAll(pBuf, ntohl(LogResponse.m_iTrailingDataLength)))
|
||||
if (!connection.Recv(pBuf, ntohl(LogResponse.m_iTrailingDataLength)))
|
||||
{
|
||||
free(pBuf);
|
||||
return false;
|
||||
@@ -306,10 +316,9 @@ bool Frontend::RequestMessages()
|
||||
|
||||
bool Frontend::RequestFileList()
|
||||
{
|
||||
NetAddress netAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
Connection connection(&netAddress);
|
||||
Connection connection(g_pOptions->GetControlIP(), g_pOptions->GetControlPort(), false);
|
||||
|
||||
bool OK = connection.Connect() >= 0;
|
||||
bool OK = connection.Connect();
|
||||
if (!OK)
|
||||
{
|
||||
return false;
|
||||
@@ -320,15 +329,15 @@ bool Frontend::RequestFileList()
|
||||
ListRequest.m_bFileList = htonl(m_bFileList);
|
||||
ListRequest.m_bServerState = htonl(m_bSummary);
|
||||
|
||||
if (connection.Send((char*)(&ListRequest), sizeof(ListRequest)) < 0)
|
||||
if (!connection.Send((char*)(&ListRequest), sizeof(ListRequest)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now listen for the returned list
|
||||
SNZBListResponse ListResponse;
|
||||
int iResponseLen = connection.Recv((char*) &ListResponse, sizeof(ListResponse));
|
||||
if (iResponseLen != sizeof(ListResponse) ||
|
||||
bool bRead = connection.Recv((char*) &ListResponse, sizeof(ListResponse));
|
||||
if (!bRead ||
|
||||
(int)ntohl(ListResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
|
||||
ntohl(ListResponse.m_MessageBase.m_iStructSize) != sizeof(ListResponse))
|
||||
{
|
||||
@@ -339,7 +348,7 @@ bool Frontend::RequestFileList()
|
||||
if (ntohl(ListResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
pBuf = (char*)malloc(ntohl(ListResponse.m_iTrailingDataLength));
|
||||
if (!connection.RecvAll(pBuf, ntohl(ListResponse.m_iTrailingDataLength)))
|
||||
if (!connection.Recv(pBuf, ntohl(ListResponse.m_iTrailingDataLength)))
|
||||
{
|
||||
free(pBuf);
|
||||
return false;
|
||||
@@ -350,69 +359,26 @@ bool Frontend::RequestFileList()
|
||||
|
||||
if (m_bSummary)
|
||||
{
|
||||
m_bPause = ntohl(ListResponse.m_bServerPaused);
|
||||
m_bPauseDownload = ntohl(ListResponse.m_bDownloadPaused);
|
||||
m_bPauseDownload2 = ntohl(ListResponse.m_bDownload2Paused);
|
||||
m_lRemainingSize = Util::JoinInt64(ntohl(ListResponse.m_iRemainingSizeHi), ntohl(ListResponse.m_iRemainingSizeLo));
|
||||
m_fCurrentDownloadSpeed = ntohl(ListResponse.m_iDownloadRate) / 1024.0f;
|
||||
m_fDownloadLimit = ntohl(ListResponse.m_iDownloadLimit) / 1024.0f;
|
||||
m_iCurrentDownloadSpeed = ntohl(ListResponse.m_iDownloadRate);
|
||||
m_iDownloadLimit = ntohl(ListResponse.m_iDownloadLimit);
|
||||
m_iThreadCount = ntohl(ListResponse.m_iThreadCount);
|
||||
m_iPostJobCount = ntohl(ListResponse.m_iPostJobCount);
|
||||
m_iUpTimeSec = ntohl(ListResponse.m_iUpTimeSec);
|
||||
m_iDnTimeSec = ntohl(ListResponse.m_iDownloadTimeSec);
|
||||
m_bStandBy = ntohl(ListResponse.m_bServerStandBy);
|
||||
m_bStandBy = ntohl(ListResponse.m_bDownloadStandBy);
|
||||
m_iAllBytes = Util::JoinInt64(ntohl(ListResponse.m_iDownloadedBytesHi), ntohl(ListResponse.m_iDownloadedBytesLo));
|
||||
}
|
||||
|
||||
if (m_bFileList && ntohl(ListResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
typedef std::deque<NZBInfo*> NZBList;
|
||||
NZBList cNZBList;
|
||||
|
||||
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(Util::JoinInt64(ntohl(pListAnswer->m_iFileSizeHi), ntohl(pListAnswer->m_iFileSizeLo)));
|
||||
pFileInfo->SetRemainingSize(Util::JoinInt64(ntohl(pListAnswer->m_iRemainingSizeHi), ntohl(pListAnswer->m_iRemainingSizeLo)));
|
||||
pFileInfo->SetPaused(ntohl(pListAnswer->m_bPaused));
|
||||
pFileInfo->SetSubject(szSubject);
|
||||
pFileInfo->SetFilename(szFileName);
|
||||
pFileInfo->SetFilenameConfirmed(ntohl(pListAnswer->m_bFilenameConfirmed));
|
||||
|
||||
// find nzb-info or create new
|
||||
NZBInfo* pNZBInfo = NULL;
|
||||
for (NZBList::iterator it = cNZBList.begin(); it != cNZBList.end(); it++)
|
||||
{
|
||||
NZBInfo* pNZBInfo2 = *it;
|
||||
if (!strcmp(pNZBInfo2->GetFilename(), szNZBFilename))
|
||||
{
|
||||
pNZBInfo = pNZBInfo2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!pNZBInfo)
|
||||
{
|
||||
pNZBInfo = new NZBInfo();
|
||||
pNZBInfo->SetFilename(szNZBFilename);
|
||||
pNZBInfo->SetDestDir(szDestDir);
|
||||
cNZBList.push_back(pNZBInfo);
|
||||
}
|
||||
|
||||
pFileInfo->SetNZBInfo(pNZBInfo);
|
||||
|
||||
m_RemoteQueue.push_back(pFileInfo);
|
||||
|
||||
pBufPtr += sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) +
|
||||
ntohl(pListAnswer->m_iSubjectLen) + ntohl(pListAnswer->m_iFilenameLen) + ntohl(pListAnswer->m_iDestDirLen);
|
||||
}
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
client.BuildFileList(&ListResponse, pBuf, &m_RemoteQueue);
|
||||
}
|
||||
|
||||
if (pBuf)
|
||||
{
|
||||
free(pBuf);
|
||||
@@ -421,18 +387,18 @@ bool Frontend::RequestFileList()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Frontend::RequestPauseUnpause(bool bPause)
|
||||
bool Frontend::RequestPauseUnpause(bool bPause, bool bSecondRegister)
|
||||
{
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
return client.RequestServerPauseUnpause(bPause);
|
||||
return client.RequestServerPauseUnpause(bPause, bSecondRegister ? eRemotePauseUnpauseActionDownload2 : eRemotePauseUnpauseActionDownload);
|
||||
}
|
||||
|
||||
bool Frontend::RequestSetDownloadRate(float fRate)
|
||||
bool Frontend::RequestSetDownloadRate(int iRate)
|
||||
{
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
return client.RequestServerSetDownloadRate(fRate);
|
||||
return client.RequestServerSetDownloadRate(iRate);
|
||||
}
|
||||
|
||||
bool Frontend::RequestDumpDebug()
|
||||
@@ -442,9 +408,9 @@ bool Frontend::RequestDumpDebug()
|
||||
return client.RequestServerDumpDebug();
|
||||
}
|
||||
|
||||
bool Frontend::RequestEditQueue(int iAction, int iOffset, int iID)
|
||||
bool Frontend::RequestEditQueue(eRemoteEditAction iAction, int iOffset, int iID)
|
||||
{
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
return client.RequestServerEditQueue(iAction, iOffset, &iID, 1, false);
|
||||
return client.RequestServerEditQueue(iAction, iOffset, NULL, &iID, 1, NULL, eRemoteMatchModeID, false);
|
||||
}
|
||||
|
||||
23
Frontend.h
23
Frontend.h
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* 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-2010 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -50,10 +50,11 @@ protected:
|
||||
int m_iUpdateInterval;
|
||||
|
||||
// summary
|
||||
float m_fCurrentDownloadSpeed;
|
||||
int m_iCurrentDownloadSpeed;
|
||||
long long m_lRemainingSize;
|
||||
bool m_bPause;
|
||||
float m_fDownloadLimit;
|
||||
bool m_bPauseDownload;
|
||||
bool m_bPauseDownload2;
|
||||
int m_iDownloadLimit;
|
||||
int m_iThreadCount;
|
||||
int m_iPostJobCount;
|
||||
int m_iUpTimeSec;
|
||||
@@ -69,14 +70,14 @@ protected:
|
||||
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 ServerPauseUnpause(bool bPause, bool bSecondRegister);
|
||||
bool RequestPauseUnpause(bool bPause, bool bSecondRegister);
|
||||
void ServerSetDownloadRate(int iRate);
|
||||
bool RequestSetDownloadRate(int iRate);
|
||||
void ServerDumpDebug();
|
||||
bool RequestDumpDebug();
|
||||
bool ServerEditQueue(QueueEditor::EEditAction eAction, int iOffset, int iEntry);
|
||||
bool RequestEditQueue(int iAction, int iOffset, int iID);
|
||||
bool RequestEditQueue(eRemoteEditAction iAction, int iOffset, int iID);
|
||||
|
||||
public:
|
||||
Frontend();
|
||||
|
||||
150
Log.cpp
150
Log.cpp
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* 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-2013 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -30,12 +30,15 @@
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Options.h"
|
||||
@@ -56,31 +59,14 @@ Log::Log()
|
||||
|
||||
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);
|
||||
}
|
||||
Clear();
|
||||
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)
|
||||
{
|
||||
if (!m_szLogFilename)
|
||||
{
|
||||
m_szLogFilename = strdup(g_pOptions->GetLogFile());
|
||||
}
|
||||
|
||||
char tmp2[1024];
|
||||
|
||||
va_list ap;
|
||||
@@ -91,6 +77,7 @@ void Log::Filelog(const char* msg, ...)
|
||||
|
||||
time_t rawtime;
|
||||
time(&rawtime);
|
||||
rawtime += g_pOptions->GetTimeCorrection();
|
||||
|
||||
char szTime[50];
|
||||
#ifdef HAVE_CTIME_R_3
|
||||
@@ -101,7 +88,7 @@ void Log::Filelog(const char* msg, ...)
|
||||
szTime[50-1] = '\0';
|
||||
szTime[strlen(szTime) - 1] = '\0'; // trim LF
|
||||
|
||||
FILE* file = fopen(m_szLogFilename, "a+");
|
||||
FILE* file = fopen(m_szLogFilename, "ab+");
|
||||
if (file)
|
||||
{
|
||||
#ifdef WIN32
|
||||
@@ -110,9 +97,9 @@ void Log::Filelog(const char* msg, ...)
|
||||
unsigned long iThreadId = (unsigned long)pthread_self();
|
||||
#endif
|
||||
#ifdef DEBUG
|
||||
fprintf(file, "%s\t%lu\t%s\n", szTime, iThreadId, tmp2);
|
||||
fprintf(file, "%s\t%lu\t%s%s", szTime, iThreadId, tmp2, LINE_ENDING);
|
||||
#else
|
||||
fprintf(file, "%s\t%s\n", szTime, tmp2);
|
||||
fprintf(file, "%s\t%s%s", szTime, tmp2, LINE_ENDING);
|
||||
#endif
|
||||
fclose(file);
|
||||
}
|
||||
@@ -123,6 +110,7 @@ void Log::Filelog(const char* msg, ...)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
#undef debug
|
||||
#ifdef HAVE_VARIADIC_MACROS
|
||||
void debug(const char* szFilename, const char* szFuncname, int iLineNr, const char* msg, ...)
|
||||
@@ -130,7 +118,6 @@ void debug(const char* szFilename, const char* szFuncname, int iLineNr, const ch
|
||||
void debug(const char* msg, ...)
|
||||
#endif
|
||||
{
|
||||
#ifdef DEBUG
|
||||
char tmp1[1024];
|
||||
|
||||
va_list ap;
|
||||
@@ -156,18 +143,12 @@ void debug(const char* msg, ...)
|
||||
|
||||
g_pLog->m_mutexLog.Lock();
|
||||
|
||||
if (!g_pOptions)
|
||||
if (!g_pOptions && g_pLog->m_bExtraDebug)
|
||||
{
|
||||
if (g_pLog->m_bExtraDebug)
|
||||
{
|
||||
printf("%s\n", tmp2);
|
||||
g_pLog->Filelog("DEBUG\t%s", tmp2);
|
||||
}
|
||||
g_pLog->m_mutexLog.Unlock();
|
||||
return;
|
||||
printf("%s\n", tmp2);
|
||||
}
|
||||
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetDebugTarget();
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions ? g_pOptions->GetDebugTarget() : Options::mtScreen;
|
||||
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->Filelog("DEBUG\t%s", tmp2);
|
||||
@@ -178,8 +159,8 @@ void debug(const char* msg, ...)
|
||||
}
|
||||
|
||||
g_pLog->m_mutexLog.Unlock();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void error(const char* msg, ...)
|
||||
{
|
||||
@@ -193,7 +174,7 @@ void error(const char* msg, ...)
|
||||
|
||||
g_pLog->m_mutexLog.Lock();
|
||||
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetErrorTarget();
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions ? g_pOptions->GetErrorTarget() : Options::mtBoth;
|
||||
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->Filelog("ERROR\t%s", tmp2);
|
||||
@@ -218,7 +199,7 @@ void warn(const char* msg, ...)
|
||||
|
||||
g_pLog->m_mutexLog.Lock();
|
||||
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetWarningTarget();
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions ? g_pOptions->GetWarningTarget() : Options::mtScreen;
|
||||
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->Filelog("WARNING\t%s", tmp2);
|
||||
@@ -243,7 +224,7 @@ void info(const char* msg, ...)
|
||||
|
||||
g_pLog->m_mutexLog.Lock();
|
||||
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetInfoTarget();
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions ? g_pOptions->GetInfoTarget() : Options::mtScreen;
|
||||
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->Filelog("INFO\t%s", tmp2);
|
||||
@@ -268,7 +249,7 @@ void detail(const char* msg, ...)
|
||||
|
||||
g_pLog->m_mutexLog.Lock();
|
||||
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetDetailTarget();
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions ? g_pOptions->GetDetailTarget() : Options::mtScreen;
|
||||
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->Filelog("DETAIL\t%s", tmp2);
|
||||
@@ -322,10 +303,18 @@ Message::Message(unsigned int iID, EKind eKind, time_t tTime, const char* szText
|
||||
|
||||
Message::~ Message()
|
||||
{
|
||||
if (m_szText)
|
||||
free(m_szText);
|
||||
}
|
||||
|
||||
void Log::Clear()
|
||||
{
|
||||
m_mutexLog.Lock();
|
||||
for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
|
||||
{
|
||||
free(m_szText);
|
||||
delete *it;
|
||||
}
|
||||
m_Messages.clear();
|
||||
m_mutexLog.Unlock();
|
||||
}
|
||||
|
||||
void Log::AppendMessage(Message::EKind eKind, const char * szText)
|
||||
@@ -333,11 +322,14 @@ 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())
|
||||
if (g_pOptions)
|
||||
{
|
||||
Message* pMessage = m_Messages.front();
|
||||
delete pMessage;
|
||||
m_Messages.pop_front();
|
||||
while (m_Messages.size() > (unsigned int)g_pOptions->GetLogBufferSize())
|
||||
{
|
||||
Message* pMessage = m_Messages.front();
|
||||
delete pMessage;
|
||||
m_Messages.pop_front();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -356,3 +348,67 @@ void Log::ResetLog()
|
||||
{
|
||||
remove(g_pOptions->GetLogFile());
|
||||
}
|
||||
|
||||
/*
|
||||
* During intializing stage (when options were not read yet) all messages
|
||||
* are saved in screen log, even if they shouldn't (according to options).
|
||||
* Method "InitOptions()" check all messages added to screen log during
|
||||
* intializing stage and does three things:
|
||||
* 1) save the messages to log-file (if they should according to options);
|
||||
* 2) delete messages from screen log (if they should not be saved in screen log).
|
||||
* 3) renumerate IDs
|
||||
*/
|
||||
void Log::InitOptions()
|
||||
{
|
||||
const char* szMessageType[] = { "INFO", "WARNING", "ERROR", "DEBUG", "DETAIL"};
|
||||
|
||||
if (g_pOptions->GetCreateLog() && g_pOptions->GetLogFile())
|
||||
{
|
||||
m_szLogFilename = strdup(g_pOptions->GetLogFile());
|
||||
#ifdef WIN32
|
||||
WebUtil::Utf8ToAnsi(m_szLogFilename, strlen(m_szLogFilename) + 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
m_iIDGen = 0;
|
||||
|
||||
for (unsigned int i = 0; i < m_Messages.size(); )
|
||||
{
|
||||
Message* pMessage = m_Messages.at(i);
|
||||
Options::EMessageTarget eTarget = Options::mtNone;
|
||||
switch (pMessage->GetKind())
|
||||
{
|
||||
case Message::mkDebug:
|
||||
eTarget = g_pOptions->GetDebugTarget();
|
||||
break;
|
||||
case Message::mkDetail:
|
||||
eTarget = g_pOptions->GetDetailTarget();
|
||||
break;
|
||||
case Message::mkInfo:
|
||||
eTarget = g_pOptions->GetInfoTarget();
|
||||
break;
|
||||
case Message::mkWarning:
|
||||
eTarget = g_pOptions->GetWarningTarget();
|
||||
break;
|
||||
case Message::mkError:
|
||||
eTarget = g_pOptions->GetErrorTarget();
|
||||
break;
|
||||
}
|
||||
|
||||
if (eTarget == Options::mtLog || eTarget == Options::mtBoth)
|
||||
{
|
||||
Filelog("%s\t%s", szMessageType[pMessage->GetKind()], pMessage->GetText());
|
||||
}
|
||||
|
||||
if (eTarget == Options::mtLog || eTarget == Options::mtNone)
|
||||
{
|
||||
delete pMessage;
|
||||
m_Messages.erase(m_Messages.begin() + i);
|
||||
}
|
||||
else
|
||||
{
|
||||
pMessage->m_iID = ++m_iIDGen;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
35
Log.h
35
Log.h
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* 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-2009 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -38,12 +38,13 @@ void info(const char* msg, ...);
|
||||
void detail(const char* msg, ...);
|
||||
void abort(const char* msg, ...);
|
||||
|
||||
#ifdef 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
|
||||
|
||||
#endif
|
||||
|
||||
class Message
|
||||
{
|
||||
@@ -63,9 +64,11 @@ private:
|
||||
time_t m_tTime;
|
||||
char* m_szText;
|
||||
|
||||
friend class Log;
|
||||
|
||||
public:
|
||||
Message(unsigned int iID, EKind eKind, time_t tTime, const char* szText);
|
||||
~Message();
|
||||
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; }
|
||||
@@ -94,26 +97,30 @@ private:
|
||||
friend void info(const char* msg, ...);
|
||||
friend void abort(const char* msg, ...);
|
||||
friend void detail(const char* msg, ...);
|
||||
#ifdef DEBUG
|
||||
#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
|
||||
#endif
|
||||
|
||||
public:
|
||||
Log();
|
||||
~Log();
|
||||
Log();
|
||||
~Log();
|
||||
Messages* LockMessages();
|
||||
void UnlockMessages();
|
||||
void Clear();
|
||||
void ResetLog();
|
||||
void InitOptions();
|
||||
};
|
||||
|
||||
#ifdef DEBUG
|
||||
#ifdef HAVE_VARIADIC_MACROS
|
||||
#ifdef DEBUG
|
||||
#define debug(...) debug(__FILE__, FUNCTION_MACRO_NAME, __LINE__, __VA_ARGS__)
|
||||
#else
|
||||
#define debug(...) do { } while(0)
|
||||
#endif
|
||||
#define debug(...) debug(__FILE__, FUNCTION_MACRO_NAME, __LINE__, __VA_ARGS__)
|
||||
#endif
|
||||
#else
|
||||
#define debug(...) do { } while(0)
|
||||
#endif
|
||||
|
||||
extern Log* g_pLog;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
|
||||
325
Maintenance.cpp
Normal file
325
Maintenance.cpp
Normal file
@@ -0,0 +1,325 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2013 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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>
|
||||
#include <ctype.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include "Maintenance.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
extern Maintenance* g_pMaintenance;
|
||||
|
||||
|
||||
Maintenance::Maintenance()
|
||||
{
|
||||
m_iIDMessageGen = 0;
|
||||
m_UpdateScriptController = NULL;
|
||||
m_szUpdateScript = NULL;
|
||||
}
|
||||
|
||||
Maintenance::~Maintenance()
|
||||
{
|
||||
m_mutexController.Lock();
|
||||
if (m_UpdateScriptController)
|
||||
{
|
||||
m_UpdateScriptController->Detach();
|
||||
m_mutexController.Unlock();
|
||||
while (m_UpdateScriptController)
|
||||
{
|
||||
usleep(20*1000);
|
||||
}
|
||||
}
|
||||
|
||||
ClearMessages();
|
||||
|
||||
free(m_szUpdateScript);
|
||||
}
|
||||
|
||||
void Maintenance::ResetUpdateController()
|
||||
{
|
||||
m_mutexController.Lock();
|
||||
m_UpdateScriptController = NULL;
|
||||
m_mutexController.Unlock();
|
||||
}
|
||||
|
||||
void Maintenance::ClearMessages()
|
||||
{
|
||||
for (Log::Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Messages.clear();
|
||||
}
|
||||
|
||||
Log::Messages* Maintenance::LockMessages()
|
||||
{
|
||||
m_mutexLog.Lock();
|
||||
return &m_Messages;
|
||||
}
|
||||
|
||||
void Maintenance::UnlockMessages()
|
||||
{
|
||||
m_mutexLog.Unlock();
|
||||
}
|
||||
|
||||
void Maintenance::AppendMessage(Message::EKind eKind, time_t tTime, const char * szText)
|
||||
{
|
||||
if (tTime == 0)
|
||||
{
|
||||
tTime = time(NULL);
|
||||
}
|
||||
|
||||
m_mutexLog.Lock();
|
||||
Message* pMessage = new Message(++m_iIDMessageGen, eKind, tTime, szText);
|
||||
m_Messages.push_back(pMessage);
|
||||
m_mutexLog.Unlock();
|
||||
}
|
||||
|
||||
bool Maintenance::StartUpdate(EBranch eBranch)
|
||||
{
|
||||
m_mutexController.Lock();
|
||||
bool bAlreadyUpdating = m_UpdateScriptController != NULL;
|
||||
m_mutexController.Unlock();
|
||||
|
||||
if (bAlreadyUpdating)
|
||||
{
|
||||
error("Could not start update-script: update-script is already running");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_szUpdateScript)
|
||||
{
|
||||
free(m_szUpdateScript);
|
||||
m_szUpdateScript = NULL;
|
||||
}
|
||||
|
||||
if (!ReadPackageInfoStr("install-script", &m_szUpdateScript))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ClearMessages();
|
||||
|
||||
m_UpdateScriptController = new UpdateScriptController();
|
||||
m_UpdateScriptController->SetScript(m_szUpdateScript);
|
||||
m_UpdateScriptController->SetBranch(eBranch);
|
||||
m_UpdateScriptController->SetAutoDestroy(true);
|
||||
|
||||
m_UpdateScriptController->Start();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Maintenance::CheckUpdates(char** pUpdateInfo)
|
||||
{
|
||||
char* szUpdateInfoScript;
|
||||
if (!ReadPackageInfoStr("update-info-script", &szUpdateInfoScript))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
*pUpdateInfo = NULL;
|
||||
UpdateInfoScriptController::ExecuteScript(szUpdateInfoScript, pUpdateInfo);
|
||||
|
||||
free(szUpdateInfoScript);
|
||||
|
||||
return *pUpdateInfo;
|
||||
}
|
||||
|
||||
bool Maintenance::ReadPackageInfoStr(const char* szKey, char** pValue)
|
||||
{
|
||||
char szFileName[1024];
|
||||
snprintf(szFileName, 1024, "%s%cpackage-info.json", g_pOptions->GetWebDir(), PATH_SEPARATOR);
|
||||
szFileName[1024-1] = '\0';
|
||||
|
||||
char* szPackageInfo;
|
||||
int iPackageInfoLen;
|
||||
if (!Util::LoadFileIntoBuffer(szFileName, &szPackageInfo, &iPackageInfoLen))
|
||||
{
|
||||
error("Could not load file %s", szFileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
char szKeyStr[100];
|
||||
snprintf(szKeyStr, 100, "\"%s\"", szKey);
|
||||
szKeyStr[100-1] = '\0';
|
||||
|
||||
char* p = strstr(szPackageInfo, szKeyStr);
|
||||
if (!p)
|
||||
{
|
||||
error("Could not parse file %s", szFileName);
|
||||
free(szPackageInfo);
|
||||
return false;
|
||||
}
|
||||
|
||||
p = strchr(p + strlen(szKeyStr), '"');
|
||||
if (!p)
|
||||
{
|
||||
error("Could not parse file %s", szFileName);
|
||||
free(szPackageInfo);
|
||||
return false;
|
||||
}
|
||||
|
||||
p++;
|
||||
char* pend = strchr(p, '"');
|
||||
if (!pend)
|
||||
{
|
||||
error("Could not parse file %s", szFileName);
|
||||
free(szPackageInfo);
|
||||
return false;
|
||||
}
|
||||
|
||||
int iLen = pend - p;
|
||||
if (iLen >= sizeof(szFileName))
|
||||
{
|
||||
error("Could not parse file %s", szFileName);
|
||||
free(szPackageInfo);
|
||||
return false;
|
||||
}
|
||||
|
||||
*pValue = (char*)malloc(iLen+1);
|
||||
strncpy(*pValue, p, iLen);
|
||||
(*pValue)[iLen] = '\0';
|
||||
|
||||
WebUtil::JsonDecode(*pValue);
|
||||
|
||||
free(szPackageInfo);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UpdateScriptController::Run()
|
||||
{
|
||||
m_iPrefixLen = 0;
|
||||
PrintMessage(Message::mkInfo, "Executing update-script %s", GetScript());
|
||||
|
||||
char szInfoName[1024];
|
||||
snprintf(szInfoName, 1024, "update-script %s", Util::BaseFileName(GetScript()));
|
||||
szInfoName[1024-1] = '\0';
|
||||
SetInfoName(szInfoName);
|
||||
|
||||
const char* szBranchName[] = { "STABLE", "TESTING", "DEVEL" };
|
||||
SetEnvVar("NZBUP_BRANCH", szBranchName[m_eBranch]);
|
||||
|
||||
char szProcessID[20];
|
||||
#ifdef WIN32
|
||||
int pid = (int)GetCurrentProcessId();
|
||||
#else
|
||||
int pid = (int)getppid();
|
||||
#endif
|
||||
snprintf(szProcessID, 20, "%i", pid);
|
||||
szProcessID[20-1] = '\0';
|
||||
SetEnvVar("NZBUP_PROCESSID", szProcessID);
|
||||
|
||||
char szLogPrefix[100];
|
||||
strncpy(szLogPrefix, Util::BaseFileName(GetScript()), 100);
|
||||
szLogPrefix[100-1] = '\0';
|
||||
if (char* ext = strrchr(szLogPrefix, '.')) *ext = '\0'; // strip file extension
|
||||
SetLogPrefix(szLogPrefix);
|
||||
m_iPrefixLen = strlen(szLogPrefix) + 2; // 2 = strlen(": ");
|
||||
|
||||
Execute();
|
||||
|
||||
g_pMaintenance->ResetUpdateController();
|
||||
}
|
||||
|
||||
void UpdateScriptController::AddMessage(Message::EKind eKind, const char* szText)
|
||||
{
|
||||
szText = szText + m_iPrefixLen;
|
||||
|
||||
g_pMaintenance->AppendMessage(eKind, time(NULL), szText);
|
||||
ScriptController::AddMessage(eKind, szText);
|
||||
}
|
||||
|
||||
void UpdateInfoScriptController::ExecuteScript(const char* szScript, char** pUpdateInfo)
|
||||
{
|
||||
detail("Executing update-info-script %s", Util::BaseFileName(szScript));
|
||||
|
||||
UpdateInfoScriptController* pScriptController = new UpdateInfoScriptController();
|
||||
pScriptController->SetScript(szScript);
|
||||
|
||||
char szInfoName[1024];
|
||||
snprintf(szInfoName, 1024, "update-info-script %s", Util::BaseFileName(szScript));
|
||||
szInfoName[1024-1] = '\0';
|
||||
pScriptController->SetInfoName(szInfoName);
|
||||
|
||||
char szLogPrefix[1024];
|
||||
strncpy(szLogPrefix, Util::BaseFileName(szScript), 1024);
|
||||
szLogPrefix[1024-1] = '\0';
|
||||
if (char* ext = strrchr(szLogPrefix, '.')) *ext = '\0'; // strip file extension
|
||||
pScriptController->SetLogPrefix(szLogPrefix);
|
||||
pScriptController->m_iPrefixLen = strlen(szLogPrefix) + 2; // 2 = strlen(": ");
|
||||
|
||||
pScriptController->Execute();
|
||||
|
||||
if (pScriptController->m_UpdateInfo.GetBuffer())
|
||||
{
|
||||
int iLen = strlen(pScriptController->m_UpdateInfo.GetBuffer());
|
||||
*pUpdateInfo = (char*)malloc(iLen + 1);
|
||||
strncpy(*pUpdateInfo, pScriptController->m_UpdateInfo.GetBuffer(), iLen);
|
||||
(*pUpdateInfo)[iLen] = '\0';
|
||||
}
|
||||
|
||||
delete pScriptController;
|
||||
}
|
||||
|
||||
void UpdateInfoScriptController::AddMessage(Message::EKind eKind, const char* szText)
|
||||
{
|
||||
szText = szText + m_iPrefixLen;
|
||||
|
||||
if (!strncmp(szText, "[NZB] ", 6))
|
||||
{
|
||||
debug("Command %s detected", szText + 6);
|
||||
if (!strncmp(szText + 6, "[UPDATEINFO]", 12))
|
||||
{
|
||||
m_UpdateInfo.Append(szText + 6 + 12);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Invalid command \"%s\" received from %s", szText, GetInfoName());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ScriptController::AddMessage(eKind, szText);
|
||||
}
|
||||
}
|
||||
94
Maintenance.h
Normal file
94
Maintenance.h
Normal file
@@ -0,0 +1,94 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2013 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef MAINTENANCE_H
|
||||
#define MAINTENANCE_H
|
||||
|
||||
#include "Thread.h"
|
||||
#include "ScriptController.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
|
||||
class UpdateScriptController;
|
||||
|
||||
class Maintenance
|
||||
{
|
||||
private:
|
||||
Log::Messages m_Messages;
|
||||
Mutex m_mutexLog;
|
||||
Mutex m_mutexController;
|
||||
int m_iIDMessageGen;
|
||||
UpdateScriptController* m_UpdateScriptController;
|
||||
char* m_szUpdateScript;
|
||||
|
||||
bool ReadPackageInfoStr(const char* szKey, char** pValue);
|
||||
|
||||
public:
|
||||
enum EBranch
|
||||
{
|
||||
brStable,
|
||||
brTesting,
|
||||
brDevel
|
||||
};
|
||||
|
||||
Maintenance();
|
||||
~Maintenance();
|
||||
void ClearMessages();
|
||||
void AppendMessage(Message::EKind eKind, time_t tTime, const char* szText);
|
||||
Log::Messages* LockMessages();
|
||||
void UnlockMessages();
|
||||
bool StartUpdate(EBranch eBranch);
|
||||
void ResetUpdateController();
|
||||
bool CheckUpdates(char** pUpdateInfo);
|
||||
};
|
||||
|
||||
class UpdateScriptController : public Thread, public ScriptController
|
||||
{
|
||||
private:
|
||||
Maintenance::EBranch m_eBranch;
|
||||
int m_iPrefixLen;
|
||||
|
||||
protected:
|
||||
virtual void AddMessage(Message::EKind eKind, const char* szText);
|
||||
|
||||
public:
|
||||
virtual void Run();
|
||||
void SetBranch(Maintenance::EBranch eBranch) { m_eBranch = eBranch; }
|
||||
};
|
||||
|
||||
class UpdateInfoScriptController : public ScriptController
|
||||
{
|
||||
private:
|
||||
int m_iPrefixLen;
|
||||
StringBuilder m_UpdateInfo;
|
||||
|
||||
protected:
|
||||
virtual void AddMessage(Message::EKind eKind, const char* szText);
|
||||
|
||||
public:
|
||||
static void ExecuteScript(const char* szScript, char** pUpdateInfo);
|
||||
};
|
||||
|
||||
#endif
|
||||
196
Makefile.am
196
Makefile.am
@@ -1,19 +1,183 @@
|
||||
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 BinRpc.cpp BinRpc.h XmlRpc.cpp XmlRpc.h \
|
||||
PostInfo.cpp PostInfo.h ScriptController.cpp ScriptController.h
|
||||
#
|
||||
# This file if part of nzbget
|
||||
#
|
||||
# Copyright (C) 2008-2013 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, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
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 = \
|
||||
ArticleDownloader.cpp ArticleDownloader.h BinRpc.cpp BinRpc.h \
|
||||
ColoredFrontend.cpp ColoredFrontend.h Connection.cpp Connection.h Decoder.cpp Decoder.h \
|
||||
DiskState.cpp DiskState.h DownloadInfo.cpp DownloadInfo.h DupeCoordinator.cpp DupeCoordinator.h \
|
||||
Frontend.cpp Frontend.h FeedCoordinator.cpp FeedCoordinator.h FeedFile.cpp FeedFile.h \
|
||||
FeedFilter.cpp FeedFilter.h FeedInfo.cpp FeedInfo.h Log.cpp Log.h LoggableFrontend.cpp \
|
||||
LoggableFrontend.h Maintenance.cpp Maintenance.h MessageBase.h NCursesFrontend.cpp \
|
||||
NCursesFrontend.h NNTPConnection.cpp \
|
||||
NNTPConnection.h NZBFile.cpp NZBFile.h NewsServer.cpp NewsServer.h Observer.cpp \
|
||||
Observer.h Options.cpp Options.h ParChecker.cpp ParChecker.h ParRenamer.cpp ParRenamer.h \
|
||||
ParCoordinator.cpp ParCoordinator.h PrePostProcessor.cpp PrePostProcessor.h QueueCoordinator.cpp \
|
||||
QueueCoordinator.h QueueEditor.cpp QueueEditor.h RemoteClient.cpp RemoteClient.h \
|
||||
RemoteServer.cpp RemoteServer.h Scanner.cpp Scanner.h Scheduler.cpp Scheduler.h ScriptController.cpp \
|
||||
ScriptController.h ServerPool.cpp ServerPool.h svn_version.cpp TLS.cpp TLS.h Thread.cpp Thread.h \
|
||||
Util.cpp Util.h XmlRpc.cpp XmlRpc.h WebDownloader.cpp WebDownloader.h WebServer.cpp WebServer.h \
|
||||
UrlCoordinator.cpp UrlCoordinator.h Unpack.cpp Unpack.h nzbget.cpp nzbget.h
|
||||
|
||||
EXTRA_DIST = \
|
||||
Makefile.cvs nzbgetd \
|
||||
$(patches_FILES) $(windows_FILES) $(osx_FILES)
|
||||
|
||||
patches_FILES = \
|
||||
libpar2-0.2-bugfixes.patch libpar2-0.2-cancel.patch \
|
||||
libpar2-0.2-MSVC8.patch libsigc++-2.0.18-MSVC8.patch
|
||||
|
||||
windows_FILES = \
|
||||
win32.h NTService.cpp NTService.h nzbget.sln nzbget.vcproj nzbget-shell.bat
|
||||
|
||||
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/Images/statusicon-inv.png \
|
||||
osx/Resources/Images/statusicon-inv@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
|
||||
|
||||
doc_FILES = \
|
||||
README ChangeLog COPYING
|
||||
|
||||
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/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
|
||||
|
||||
ppscripts_FILES = \
|
||||
ppscripts/EMail.py ppscripts/Logger.py
|
||||
|
||||
# Install
|
||||
sbin_SCRIPTS = nzbgetd
|
||||
dist_doc_DATA = $(doc_FILES)
|
||||
exampleconfdir = $(datadir)/nzbget
|
||||
dist_exampleconf_DATA = $(exampleconf_FILES)
|
||||
webuidir = $(datadir)/nzbget
|
||||
nobase_dist_webui_DATA = $(webui_FILES)
|
||||
ppscriptsdir = $(datadir)/nzbget
|
||||
nobase_dist_ppscripts_SCRIPTS = $(ppscripts_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.
|
||||
|
||||
# Configure installed script
|
||||
install-exec-hook:
|
||||
rm -f "$(DESTDIR)$(sbindir)/nzbgetd.temp"
|
||||
cp "$(DESTDIR)$(sbindir)/nzbgetd" "$(DESTDIR)$(sbindir)/nzbgetd.temp"
|
||||
sed 's?/usr/local/bin?$(bindir)?' < "$(DESTDIR)$(sbindir)/nzbgetd.temp" > "$(DESTDIR)$(sbindir)/nzbgetd"
|
||||
rm "$(DESTDIR)$(sbindir)/nzbgetd.temp"
|
||||
|
||||
# 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/ppscripts:installed to $(ppscriptsdir)/ppscripts:' < "$(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 subversion revision:
|
||||
# 1) If directory ".svn" exists we take revision from it using program svnversion (part of subversion package)
|
||||
# File is recreated only if revision number was changed.
|
||||
# 2) If directory ".svn" doesn't exists we keep and reuse file "svn_version.cpp",
|
||||
# which was possibly created early.
|
||||
# 3) If neither directory ".svn" nor file "svn_version.cpp" are available
|
||||
# we create new file "svn_version.c" with empty revision number.
|
||||
svn_version.cpp: FORCE
|
||||
@ if test -d ./.svn ; then \
|
||||
V="$(shell svnversion -n .)"; \
|
||||
H="$(shell test -f ./svn_version.cpp && head -n 1 svn_version.cpp)"; \
|
||||
if test "/* $$V */" != "$$H" ; then \
|
||||
( \
|
||||
echo "/* $$V */" ;\
|
||||
echo "/* This file is automatically regenerated on each build. Do not edit it. */" ;\
|
||||
echo "const char* svn_version(void)" ;\
|
||||
echo "{" ;\
|
||||
echo " const char* SVN_Version = \"$$V\";" ;\
|
||||
echo " return SVN_Version;" ;\
|
||||
echo "}" ;\
|
||||
) > svn_version.cpp ; \
|
||||
fi \
|
||||
elif test -f ./svn_version.cpp ; then \
|
||||
test "ok, reuse existing file"; \
|
||||
else \
|
||||
( \
|
||||
echo "/* */" ;\
|
||||
echo "/* This file is automatically regenerated on each build. Do not edit it. */" ;\
|
||||
echo "const char* svn_version(void)" ;\
|
||||
echo "{" ;\
|
||||
echo " const char* SVN_Version = \"\";" ;\
|
||||
echo " return SVN_Version;" ;\
|
||||
echo "}" ;\
|
||||
) > svn_version.cpp ; \
|
||||
fi
|
||||
FORCE:
|
||||
|
||||
# Ignore "svn_version.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:
|
||||
chmod -x $(distdir)/*.cpp $(distdir)/*.h
|
||||
find $(distdir)/webui -type f -print -exec chmod -x {} \;
|
||||
|
||||
|
||||
515
Makefile.in
515
Makefile.in
@@ -1,8 +1,8 @@
|
||||
# Makefile.in generated by automake 1.10 from Makefile.am.
|
||||
# Makefile.in generated by automake 1.9.6 from Makefile.am.
|
||||
# @configure_input@
|
||||
|
||||
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
|
||||
# 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
|
||||
# 2003, 2004, 2005 Free Software Foundation, Inc.
|
||||
# This Makefile.in is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
@@ -14,11 +14,38 @@
|
||||
|
||||
@SET_MAKE@
|
||||
|
||||
#
|
||||
# This file if part of nzbget
|
||||
#
|
||||
# Copyright (C) 2008-2013 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, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
pkgdatadir = $(datadir)/@PACKAGE@
|
||||
pkglibdir = $(libdir)/@PACKAGE@
|
||||
pkgincludedir = $(includedir)/@PACKAGE@
|
||||
top_builddir = .
|
||||
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
|
||||
INSTALL = @INSTALL@
|
||||
install_sh_DATA = $(install_sh) -c -m 644
|
||||
install_sh_PROGRAM = $(install_sh) -c
|
||||
install_sh_SCRIPT = $(install_sh) -c
|
||||
@@ -32,39 +59,60 @@ PRE_UNINSTALL = :
|
||||
POST_UNINSTALL = :
|
||||
build_triplet = @build@
|
||||
host_triplet = @host@
|
||||
target_triplet = @target@
|
||||
bin_PROGRAMS = nzbget$(EXEEXT)
|
||||
subdir = .
|
||||
DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \
|
||||
DIST_COMMON = README $(am__configure_deps) $(dist_doc_DATA) \
|
||||
$(dist_exampleconf_DATA) $(nobase_dist_ppscripts_SCRIPTS) \
|
||||
$(nobase_dist_webui_DATA) $(srcdir)/Makefile.am \
|
||||
$(srcdir)/Makefile.in $(srcdir)/config.h.in \
|
||||
$(top_srcdir)/configure AUTHORS COPYING ChangeLog INSTALL NEWS \
|
||||
config.guess config.sub depcomp install-sh missing
|
||||
subdir = .
|
||||
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
|
||||
am__aclocal_m4_deps = $(top_srcdir)/configure.ac
|
||||
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
|
||||
$(ACLOCAL_M4)
|
||||
am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
|
||||
configure.lineno config.status.lineno
|
||||
configure.lineno configure.status.lineno
|
||||
mkinstalldirs = $(install_sh) -d
|
||||
CONFIG_HEADER = config.h
|
||||
CONFIG_CLEAN_FILES =
|
||||
am__installdirs = "$(DESTDIR)$(bindir)"
|
||||
am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(ppscriptsdir)" \
|
||||
"$(DESTDIR)$(sbindir)" "$(DESTDIR)$(docdir)" \
|
||||
"$(DESTDIR)$(exampleconfdir)" "$(DESTDIR)$(webuidir)"
|
||||
binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
|
||||
PROGRAMS = $(bin_PROGRAMS)
|
||||
am_nzbget_OBJECTS = ArticleDownloader.$(OBJEXT) \
|
||||
am_nzbget_OBJECTS = ArticleDownloader.$(OBJEXT) BinRpc.$(OBJEXT) \
|
||||
ColoredFrontend.$(OBJEXT) Connection.$(OBJEXT) \
|
||||
Decoder.$(OBJEXT) DiskState.$(OBJEXT) DownloadInfo.$(OBJEXT) \
|
||||
Frontend.$(OBJEXT) Log.$(OBJEXT) LoggableFrontend.$(OBJEXT) \
|
||||
DupeCoordinator.$(OBJEXT) Frontend.$(OBJEXT) \
|
||||
FeedCoordinator.$(OBJEXT) FeedFile.$(OBJEXT) \
|
||||
FeedFilter.$(OBJEXT) FeedInfo.$(OBJEXT) Log.$(OBJEXT) \
|
||||
LoggableFrontend.$(OBJEXT) Maintenance.$(OBJEXT) \
|
||||
NCursesFrontend.$(OBJEXT) NNTPConnection.$(OBJEXT) \
|
||||
NZBFile.$(OBJEXT) NetAddress.$(OBJEXT) NewsServer.$(OBJEXT) \
|
||||
Observer.$(OBJEXT) Options.$(OBJEXT) ParChecker.$(OBJEXT) \
|
||||
PrePostProcessor.$(OBJEXT) QueueCoordinator.$(OBJEXT) \
|
||||
QueueEditor.$(OBJEXT) RemoteClient.$(OBJEXT) \
|
||||
RemoteServer.$(OBJEXT) ServerPool.$(OBJEXT) Thread.$(OBJEXT) \
|
||||
Util.$(OBJEXT) nzbget.$(OBJEXT) BinRpc.$(OBJEXT) \
|
||||
XmlRpc.$(OBJEXT) PostInfo.$(OBJEXT) ScriptController.$(OBJEXT)
|
||||
NZBFile.$(OBJEXT) NewsServer.$(OBJEXT) Observer.$(OBJEXT) \
|
||||
Options.$(OBJEXT) ParChecker.$(OBJEXT) ParRenamer.$(OBJEXT) \
|
||||
ParCoordinator.$(OBJEXT) PrePostProcessor.$(OBJEXT) \
|
||||
QueueCoordinator.$(OBJEXT) QueueEditor.$(OBJEXT) \
|
||||
RemoteClient.$(OBJEXT) RemoteServer.$(OBJEXT) \
|
||||
Scanner.$(OBJEXT) Scheduler.$(OBJEXT) \
|
||||
ScriptController.$(OBJEXT) ServerPool.$(OBJEXT) \
|
||||
svn_version.$(OBJEXT) TLS.$(OBJEXT) Thread.$(OBJEXT) \
|
||||
Util.$(OBJEXT) XmlRpc.$(OBJEXT) WebDownloader.$(OBJEXT) \
|
||||
WebServer.$(OBJEXT) UrlCoordinator.$(OBJEXT) Unpack.$(OBJEXT) \
|
||||
nzbget.$(OBJEXT)
|
||||
nzbget_OBJECTS = $(am_nzbget_OBJECTS)
|
||||
nzbget_LDADD = $(LDADD)
|
||||
DEFAULT_INCLUDES = -I.@am__isrc@
|
||||
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
|
||||
am__vpath_adj = case $$p in \
|
||||
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
|
||||
*) f=$$p;; \
|
||||
esac;
|
||||
am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
|
||||
nobase_dist_ppscriptsSCRIPT_INSTALL = $(install_sh_SCRIPT)
|
||||
sbinSCRIPT_INSTALL = $(INSTALL_SCRIPT)
|
||||
SCRIPTS = $(nobase_dist_ppscripts_SCRIPTS) $(sbin_SCRIPTS)
|
||||
DEFAULT_INCLUDES = -I. -I$(srcdir) -I.
|
||||
depcomp = $(SHELL) $(top_srcdir)/depcomp
|
||||
am__depfiles_maybe = depfiles
|
||||
CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
|
||||
@@ -78,6 +126,11 @@ CCLD = $(CC)
|
||||
LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
|
||||
SOURCES = $(nzbget_SOURCES)
|
||||
DIST_SOURCES = $(nzbget_SOURCES)
|
||||
dist_docDATA_INSTALL = $(INSTALL_DATA)
|
||||
dist_exampleconfDATA_INSTALL = $(INSTALL_DATA)
|
||||
nobase_dist_webuiDATA_INSTALL = $(install_sh_DATA)
|
||||
DATA = $(dist_doc_DATA) $(dist_exampleconf_DATA) \
|
||||
$(nobase_dist_webui_DATA)
|
||||
ETAGS = etags
|
||||
CTAGS = ctags
|
||||
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
|
||||
@@ -90,20 +143,14 @@ am__remove_distdir = \
|
||||
DIST_ARCHIVES = $(distdir).tar.gz
|
||||
GZIP_ENV = --best
|
||||
distuninstallcheck_listfiles = find . -type f -print
|
||||
distcleancheck_listfiles = find . -type f -print
|
||||
ACLOCAL = @ACLOCAL@
|
||||
ADDSRCS = @ADDSRCS@
|
||||
AMDEP_FALSE = @AMDEP_FALSE@
|
||||
AMDEP_TRUE = @AMDEP_TRUE@
|
||||
AMTAR = @AMTAR@
|
||||
AR = @AR@
|
||||
AUTOCONF = @AUTOCONF@
|
||||
AUTOHEADER = @AUTOHEADER@
|
||||
AUTOMAKE = @AUTOMAKE@
|
||||
AWK = @AWK@
|
||||
CC = @CC@
|
||||
CCDEPMODE = @CCDEPMODE@
|
||||
CFLAGS = @CFLAGS@
|
||||
CP = @CP@
|
||||
CPP = @CPP@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
CXX = @CXX@
|
||||
CXXCPP = @CXXCPP@
|
||||
@@ -117,9 +164,7 @@ ECHO_N = @ECHO_N@
|
||||
ECHO_T = @ECHO_T@
|
||||
EGREP = @EGREP@
|
||||
EXEEXT = @EXEEXT@
|
||||
FALSE = @FALSE@
|
||||
GREP = @GREP@
|
||||
INSTALL = @INSTALL@
|
||||
INSTALL_DATA = @INSTALL_DATA@
|
||||
INSTALL_PROGRAM = @INSTALL_PROGRAM@
|
||||
INSTALL_SCRIPT = @INSTALL_SCRIPT@
|
||||
@@ -127,13 +172,9 @@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBOBJS = @LIBOBJS@
|
||||
LIBS = @LIBS@
|
||||
LN = @LN@
|
||||
LTLIBOBJS = @LTLIBOBJS@
|
||||
MAKE = @MAKE@
|
||||
MAKEINFO = @MAKEINFO@
|
||||
MKDIR = @MKDIR@
|
||||
MKDIR_P = @MKDIR_P@
|
||||
MV = @MV@
|
||||
OBJEXT = @OBJEXT@
|
||||
PACKAGE = @PACKAGE@
|
||||
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
|
||||
@@ -142,20 +183,15 @@ PACKAGE_STRING = @PACKAGE_STRING@
|
||||
PACKAGE_TARNAME = @PACKAGE_TARNAME@
|
||||
PACKAGE_VERSION = @PACKAGE_VERSION@
|
||||
PATH_SEPARATOR = @PATH_SEPARATOR@
|
||||
RANLIB = @RANLIB@
|
||||
RM = @RM@
|
||||
PKG_CONFIG = @PKG_CONFIG@
|
||||
SET_MAKE = @SET_MAKE@
|
||||
SHELL = @SHELL@
|
||||
STRIP = @STRIP@
|
||||
TAR = @TAR@
|
||||
TRUE = @TRUE@
|
||||
VERSION = @VERSION@
|
||||
abs_builddir = @abs_builddir@
|
||||
abs_srcdir = @abs_srcdir@
|
||||
abs_top_builddir = @abs_top_builddir@
|
||||
abs_top_srcdir = @abs_top_srcdir@
|
||||
ac_ct_CC = @ac_ct_CC@
|
||||
ac_ct_CXX = @ac_ct_CXX@
|
||||
am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
|
||||
am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
|
||||
am__include = @am__include@
|
||||
am__leading_dot = @am__leading_dot@
|
||||
am__quote = @am__quote@
|
||||
@@ -167,7 +203,6 @@ build_alias = @build_alias@
|
||||
build_cpu = @build_cpu@
|
||||
build_os = @build_os@
|
||||
build_vendor = @build_vendor@
|
||||
builddir = @builddir@
|
||||
datadir = @datadir@
|
||||
datarootdir = @datarootdir@
|
||||
docdir = @docdir@
|
||||
@@ -184,38 +219,107 @@ infodir = @infodir@
|
||||
install_sh = @install_sh@
|
||||
libdir = @libdir@
|
||||
libexecdir = @libexecdir@
|
||||
libsigc_CFLAGS = @libsigc_CFLAGS@
|
||||
libsigc_LIBS = @libsigc_LIBS@
|
||||
libxml2_CFLAGS = @libxml2_CFLAGS@
|
||||
libxml2_LIBS = @libxml2_LIBS@
|
||||
localedir = @localedir@
|
||||
localstatedir = @localstatedir@
|
||||
mandir = @mandir@
|
||||
mkdir_p = @mkdir_p@
|
||||
oldincludedir = @oldincludedir@
|
||||
openssl_CFLAGS = @openssl_CFLAGS@
|
||||
openssl_LIBS = @openssl_LIBS@
|
||||
pdfdir = @pdfdir@
|
||||
prefix = @prefix@
|
||||
program_transform_name = @program_transform_name@
|
||||
psdir = @psdir@
|
||||
sbindir = @sbindir@
|
||||
sharedstatedir = @sharedstatedir@
|
||||
srcdir = @srcdir@
|
||||
sysconfdir = @sysconfdir@
|
||||
target = @target@
|
||||
target_alias = @target_alias@
|
||||
top_builddir = @top_builddir@
|
||||
top_srcdir = @top_srcdir@
|
||||
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 BinRpc.cpp BinRpc.h XmlRpc.cpp XmlRpc.h \
|
||||
PostInfo.cpp PostInfo.h ScriptController.cpp ScriptController.h
|
||||
target_cpu = @target_cpu@
|
||||
target_os = @target_os@
|
||||
target_vendor = @target_vendor@
|
||||
nzbget_SOURCES = \
|
||||
ArticleDownloader.cpp ArticleDownloader.h BinRpc.cpp BinRpc.h \
|
||||
ColoredFrontend.cpp ColoredFrontend.h Connection.cpp Connection.h Decoder.cpp Decoder.h \
|
||||
DiskState.cpp DiskState.h DownloadInfo.cpp DownloadInfo.h DupeCoordinator.cpp DupeCoordinator.h \
|
||||
Frontend.cpp Frontend.h FeedCoordinator.cpp FeedCoordinator.h FeedFile.cpp FeedFile.h \
|
||||
FeedFilter.cpp FeedFilter.h FeedInfo.cpp FeedInfo.h Log.cpp Log.h LoggableFrontend.cpp \
|
||||
LoggableFrontend.h Maintenance.cpp Maintenance.h MessageBase.h NCursesFrontend.cpp \
|
||||
NCursesFrontend.h NNTPConnection.cpp \
|
||||
NNTPConnection.h NZBFile.cpp NZBFile.h NewsServer.cpp NewsServer.h Observer.cpp \
|
||||
Observer.h Options.cpp Options.h ParChecker.cpp ParChecker.h ParRenamer.cpp ParRenamer.h \
|
||||
ParCoordinator.cpp ParCoordinator.h PrePostProcessor.cpp PrePostProcessor.h QueueCoordinator.cpp \
|
||||
QueueCoordinator.h QueueEditor.cpp QueueEditor.h RemoteClient.cpp RemoteClient.h \
|
||||
RemoteServer.cpp RemoteServer.h Scanner.cpp Scanner.h Scheduler.cpp Scheduler.h ScriptController.cpp \
|
||||
ScriptController.h ServerPool.cpp ServerPool.h svn_version.cpp TLS.cpp TLS.h Thread.cpp Thread.h \
|
||||
Util.cpp Util.h XmlRpc.cpp XmlRpc.h WebDownloader.cpp WebDownloader.h WebServer.cpp WebServer.h \
|
||||
UrlCoordinator.cpp UrlCoordinator.h Unpack.cpp Unpack.h nzbget.cpp nzbget.h
|
||||
|
||||
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
|
||||
EXTRA_DIST = \
|
||||
Makefile.cvs nzbgetd \
|
||||
$(patches_FILES) $(windows_FILES) $(osx_FILES)
|
||||
|
||||
patches_FILES = \
|
||||
libpar2-0.2-bugfixes.patch libpar2-0.2-cancel.patch \
|
||||
libpar2-0.2-MSVC8.patch libsigc++-2.0.18-MSVC8.patch
|
||||
|
||||
windows_FILES = \
|
||||
win32.h NTService.cpp NTService.h nzbget.sln nzbget.vcproj nzbget-shell.bat
|
||||
|
||||
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/Images/statusicon-inv.png \
|
||||
osx/Resources/Images/statusicon-inv@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
|
||||
|
||||
doc_FILES = \
|
||||
README ChangeLog COPYING
|
||||
|
||||
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/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
|
||||
|
||||
ppscripts_FILES = \
|
||||
ppscripts/EMail.py ppscripts/Logger.py
|
||||
|
||||
|
||||
# Install
|
||||
sbin_SCRIPTS = nzbgetd
|
||||
dist_doc_DATA = $(doc_FILES)
|
||||
exampleconfdir = $(datadir)/nzbget
|
||||
dist_exampleconf_DATA = $(exampleconf_FILES)
|
||||
webuidir = $(datadir)/nzbget
|
||||
nobase_dist_webui_DATA = $(webui_FILES)
|
||||
ppscriptsdir = $(datadir)/nzbget
|
||||
nobase_dist_ppscripts_SCRIPTS = $(ppscripts_FILES)
|
||||
|
||||
# Ignore "svn_version.cpp" in distcleancheck
|
||||
distcleancheck_listfiles = \
|
||||
find . -type f -exec sh -c 'test -f $(srcdir)/$$1 || echo $$1' \
|
||||
sh '{}' ';'
|
||||
|
||||
all: config.h
|
||||
$(MAKE) $(AM_MAKEFLAGS) all-am
|
||||
@@ -259,7 +363,7 @@ $(ACLOCAL_M4): $(am__aclocal_m4_deps)
|
||||
config.h: stamp-h1
|
||||
@if test ! -f $@; then \
|
||||
rm -f stamp-h1; \
|
||||
$(MAKE) $(AM_MAKEFLAGS) stamp-h1; \
|
||||
$(MAKE) stamp-h1; \
|
||||
else :; fi
|
||||
|
||||
stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
|
||||
@@ -274,7 +378,7 @@ distclean-hdr:
|
||||
-rm -f config.h stamp-h1
|
||||
install-binPROGRAMS: $(bin_PROGRAMS)
|
||||
@$(NORMAL_INSTALL)
|
||||
test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
|
||||
test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)"
|
||||
@list='$(bin_PROGRAMS)'; for p in $$list; do \
|
||||
p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
|
||||
if test -f $$p \
|
||||
@@ -297,7 +401,51 @@ clean-binPROGRAMS:
|
||||
-test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
|
||||
nzbget$(EXEEXT): $(nzbget_OBJECTS) $(nzbget_DEPENDENCIES)
|
||||
@rm -f nzbget$(EXEEXT)
|
||||
$(CXXLINK) $(nzbget_OBJECTS) $(nzbget_LDADD) $(LIBS)
|
||||
$(CXXLINK) $(nzbget_LDFLAGS) $(nzbget_OBJECTS) $(nzbget_LDADD) $(LIBS)
|
||||
install-nobase_dist_ppscriptsSCRIPTS: $(nobase_dist_ppscripts_SCRIPTS)
|
||||
@$(NORMAL_INSTALL)
|
||||
test -z "$(ppscriptsdir)" || $(mkdir_p) "$(DESTDIR)$(ppscriptsdir)"
|
||||
@$(am__vpath_adj_setup) \
|
||||
list='$(nobase_dist_ppscripts_SCRIPTS)'; for p in $$list; do \
|
||||
$(am__vpath_adj) p=$$f; \
|
||||
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
|
||||
if test -f $$d$$p; then \
|
||||
f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
|
||||
f=`echo "$$p" | sed 's|[^/]*$$||'`"$$f"; \
|
||||
echo " $(nobase_dist_ppscriptsSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(ppscriptsdir)/$$f'"; \
|
||||
$(nobase_dist_ppscriptsSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(ppscriptsdir)/$$f"; \
|
||||
else :; fi; \
|
||||
done
|
||||
|
||||
uninstall-nobase_dist_ppscriptsSCRIPTS:
|
||||
@$(NORMAL_UNINSTALL)
|
||||
@$(am__vpath_adj_setup) \
|
||||
list='$(nobase_dist_ppscripts_SCRIPTS)'; for p in $$list; do \
|
||||
$(am__vpath_adj) p=$$f; \
|
||||
f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
|
||||
f=`echo "$$p" | sed 's|[^/]*$$||'`"$$f"; \
|
||||
echo " rm -f '$(DESTDIR)$(ppscriptsdir)/$$f'"; \
|
||||
rm -f "$(DESTDIR)$(ppscriptsdir)/$$f"; \
|
||||
done
|
||||
install-sbinSCRIPTS: $(sbin_SCRIPTS)
|
||||
@$(NORMAL_INSTALL)
|
||||
test -z "$(sbindir)" || $(mkdir_p) "$(DESTDIR)$(sbindir)"
|
||||
@list='$(sbin_SCRIPTS)'; for p in $$list; do \
|
||||
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
|
||||
if test -f $$d$$p; then \
|
||||
f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
|
||||
echo " $(sbinSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(sbindir)/$$f'"; \
|
||||
$(sbinSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(sbindir)/$$f"; \
|
||||
else :; fi; \
|
||||
done
|
||||
|
||||
uninstall-sbinSCRIPTS:
|
||||
@$(NORMAL_UNINSTALL)
|
||||
@list='$(sbin_SCRIPTS)'; for p in $$list; do \
|
||||
f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
|
||||
echo " rm -f '$(DESTDIR)$(sbindir)/$$f'"; \
|
||||
rm -f "$(DESTDIR)$(sbindir)/$$f"; \
|
||||
done
|
||||
|
||||
mostlyclean-compile:
|
||||
-rm -f *.$(OBJEXT)
|
||||
@@ -312,43 +460,111 @@ distclean-compile:
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Decoder.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DiskState.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DownloadInfo.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/DupeCoordinator.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FeedCoordinator.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FeedFile.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FeedFilter.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/FeedInfo.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Frontend.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Log.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/LoggableFrontend.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Maintenance.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NCursesFrontend.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NNTPConnection.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NZBFile.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetAddress.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NewsServer.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Observer.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Options.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ParChecker.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PostInfo.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ParCoordinator.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ParRenamer.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PrePostProcessor.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/QueueCoordinator.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/QueueEditor.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RemoteClient.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RemoteServer.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Scanner.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Scheduler.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ScriptController.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ServerPool.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TLS.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Thread.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Unpack.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UrlCoordinator.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Util.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/WebDownloader.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/WebServer.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/XmlRpc.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nzbget.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svn_version.Po@am__quote@
|
||||
|
||||
.cpp.o:
|
||||
@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
|
||||
@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
|
||||
@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
|
||||
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
|
||||
|
||||
.cpp.obj:
|
||||
@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
|
||||
@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
|
||||
@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
|
||||
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
|
||||
uninstall-info-am:
|
||||
install-dist_docDATA: $(dist_doc_DATA)
|
||||
@$(NORMAL_INSTALL)
|
||||
test -z "$(docdir)" || $(mkdir_p) "$(DESTDIR)$(docdir)"
|
||||
@list='$(dist_doc_DATA)'; for p in $$list; do \
|
||||
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
|
||||
f=$(am__strip_dir) \
|
||||
echo " $(dist_docDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(docdir)/$$f'"; \
|
||||
$(dist_docDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(docdir)/$$f"; \
|
||||
done
|
||||
|
||||
uninstall-dist_docDATA:
|
||||
@$(NORMAL_UNINSTALL)
|
||||
@list='$(dist_doc_DATA)'; for p in $$list; do \
|
||||
f=$(am__strip_dir) \
|
||||
echo " rm -f '$(DESTDIR)$(docdir)/$$f'"; \
|
||||
rm -f "$(DESTDIR)$(docdir)/$$f"; \
|
||||
done
|
||||
install-dist_exampleconfDATA: $(dist_exampleconf_DATA)
|
||||
@$(NORMAL_INSTALL)
|
||||
test -z "$(exampleconfdir)" || $(mkdir_p) "$(DESTDIR)$(exampleconfdir)"
|
||||
@list='$(dist_exampleconf_DATA)'; for p in $$list; do \
|
||||
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
|
||||
f=$(am__strip_dir) \
|
||||
echo " $(dist_exampleconfDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(exampleconfdir)/$$f'"; \
|
||||
$(dist_exampleconfDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(exampleconfdir)/$$f"; \
|
||||
done
|
||||
|
||||
uninstall-dist_exampleconfDATA:
|
||||
@$(NORMAL_UNINSTALL)
|
||||
@list='$(dist_exampleconf_DATA)'; for p in $$list; do \
|
||||
f=$(am__strip_dir) \
|
||||
echo " rm -f '$(DESTDIR)$(exampleconfdir)/$$f'"; \
|
||||
rm -f "$(DESTDIR)$(exampleconfdir)/$$f"; \
|
||||
done
|
||||
install-nobase_dist_webuiDATA: $(nobase_dist_webui_DATA)
|
||||
@$(NORMAL_INSTALL)
|
||||
test -z "$(webuidir)" || $(mkdir_p) "$(DESTDIR)$(webuidir)"
|
||||
@$(am__vpath_adj_setup) \
|
||||
list='$(nobase_dist_webui_DATA)'; for p in $$list; do \
|
||||
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
|
||||
$(am__vpath_adj) \
|
||||
echo " $(nobase_dist_webuiDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(webuidir)/$$f'"; \
|
||||
$(nobase_dist_webuiDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(webuidir)/$$f"; \
|
||||
done
|
||||
|
||||
uninstall-nobase_dist_webuiDATA:
|
||||
@$(NORMAL_UNINSTALL)
|
||||
@$(am__vpath_adj_setup) \
|
||||
list='$(nobase_dist_webui_DATA)'; for p in $$list; do \
|
||||
$(am__vpath_adj) \
|
||||
echo " rm -f '$(DESTDIR)$(webuidir)/$$f'"; \
|
||||
rm -f "$(DESTDIR)$(webuidir)/$$f"; \
|
||||
done
|
||||
|
||||
ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
|
||||
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
|
||||
@@ -400,22 +616,24 @@ distclean-tags:
|
||||
|
||||
distdir: $(DISTFILES)
|
||||
$(am__remove_distdir)
|
||||
test -d $(distdir) || mkdir $(distdir)
|
||||
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
|
||||
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
|
||||
list='$(DISTFILES)'; \
|
||||
dist_files=`for file in $$list; do echo $$file; done | \
|
||||
sed -e "s|^$$srcdirstrip/||;t" \
|
||||
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
|
||||
case $$dist_files in \
|
||||
*/*) $(MKDIR_P) `echo "$$dist_files" | \
|
||||
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
|
||||
sort -u` ;; \
|
||||
esac; \
|
||||
for file in $$dist_files; do \
|
||||
mkdir $(distdir)
|
||||
$(mkdir_p) $(distdir)/osx $(distdir)/osx/NZBGet.xcodeproj $(distdir)/osx/Resources $(distdir)/osx/Resources/Images $(distdir)/osx/Resources/licenses $(distdir)/ppscripts $(distdir)/webui $(distdir)/webui/img $(distdir)/webui/lib
|
||||
@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
|
||||
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
|
||||
list='$(DISTFILES)'; for file in $$list; do \
|
||||
case $$file in \
|
||||
$(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
|
||||
$(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
|
||||
esac; \
|
||||
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
|
||||
dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
|
||||
if test "$$dir" != "$$file" && test "$$dir" != "."; then \
|
||||
dir="/$$dir"; \
|
||||
$(mkdir_p) "$(distdir)$$dir"; \
|
||||
else \
|
||||
dir=''; \
|
||||
fi; \
|
||||
if test -d $$d/$$file; then \
|
||||
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
|
||||
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
|
||||
cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
|
||||
fi; \
|
||||
@@ -426,10 +644,13 @@ distdir: $(DISTFILES)
|
||||
|| exit 1; \
|
||||
fi; \
|
||||
done
|
||||
$(MAKE) $(AM_MAKEFLAGS) \
|
||||
top_distdir="$(top_distdir)" distdir="$(distdir)" \
|
||||
dist-hook
|
||||
-find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \
|
||||
! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
|
||||
! -type d ! -perm -400 -exec chmod a+r {} \; -o \
|
||||
! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \
|
||||
! -type d ! -perm -444 -exec $(SHELL) $(install_sh) -c -m a+r {} {} \; \
|
||||
|| chmod -R a+r $(distdir)
|
||||
dist-gzip: distdir
|
||||
tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
|
||||
@@ -504,7 +725,7 @@ distcheck: dist
|
||||
$(am__remove_distdir)
|
||||
@(echo "$(distdir) archives ready for distribution: "; \
|
||||
list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
|
||||
sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'
|
||||
sed -e '1{h;s/./=/g;p;x;}' -e '$${p;x;}'
|
||||
distuninstallcheck:
|
||||
@cd $(distuninstallcheck_dir) \
|
||||
&& test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \
|
||||
@@ -525,10 +746,10 @@ distcleancheck: distclean
|
||||
exit 1; } >&2
|
||||
check-am: all-am
|
||||
check: check-am
|
||||
all-am: Makefile $(PROGRAMS) config.h
|
||||
all-am: Makefile $(PROGRAMS) $(SCRIPTS) $(DATA) config.h
|
||||
installdirs:
|
||||
for dir in "$(DESTDIR)$(bindir)"; do \
|
||||
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
|
||||
for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(ppscriptsdir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(docdir)" "$(DESTDIR)$(exampleconfdir)" "$(DESTDIR)$(webuidir)"; do \
|
||||
test -z "$$dir" || $(mkdir_p) "$$dir"; \
|
||||
done
|
||||
install: install-am
|
||||
install-exec: install-exec-am
|
||||
@@ -575,22 +796,20 @@ info: info-am
|
||||
|
||||
info-am:
|
||||
|
||||
install-data-am:
|
||||
install-data-am: install-dist_docDATA install-dist_exampleconfDATA \
|
||||
install-nobase_dist_ppscriptsSCRIPTS \
|
||||
install-nobase_dist_webuiDATA
|
||||
@$(NORMAL_INSTALL)
|
||||
$(MAKE) $(AM_MAKEFLAGS) install-data-hook
|
||||
|
||||
install-dvi: install-dvi-am
|
||||
|
||||
install-exec-am: install-binPROGRAMS
|
||||
|
||||
install-html: install-html-am
|
||||
install-exec-am: install-binPROGRAMS install-sbinSCRIPTS
|
||||
@$(NORMAL_INSTALL)
|
||||
$(MAKE) $(AM_MAKEFLAGS) install-exec-hook
|
||||
|
||||
install-info: install-info-am
|
||||
|
||||
install-man:
|
||||
|
||||
install-pdf: install-pdf-am
|
||||
|
||||
install-ps: install-ps-am
|
||||
|
||||
installcheck-am:
|
||||
|
||||
maintainer-clean: maintainer-clean-am
|
||||
@@ -612,27 +831,113 @@ ps: ps-am
|
||||
|
||||
ps-am:
|
||||
|
||||
uninstall-am: uninstall-binPROGRAMS
|
||||
|
||||
.MAKE: install-am install-strip
|
||||
uninstall-am: uninstall-binPROGRAMS uninstall-dist_docDATA \
|
||||
uninstall-dist_exampleconfDATA uninstall-info-am \
|
||||
uninstall-nobase_dist_ppscriptsSCRIPTS \
|
||||
uninstall-nobase_dist_webuiDATA uninstall-sbinSCRIPTS
|
||||
|
||||
.PHONY: CTAGS GTAGS all all-am am--refresh check check-am clean \
|
||||
clean-binPROGRAMS clean-generic ctags dist dist-all dist-bzip2 \
|
||||
dist-gzip dist-shar dist-tarZ dist-zip distcheck distclean \
|
||||
distclean-compile distclean-generic distclean-hdr \
|
||||
dist-gzip dist-hook dist-shar dist-tarZ dist-zip distcheck \
|
||||
distclean distclean-compile distclean-generic distclean-hdr \
|
||||
distclean-tags distcleancheck distdir distuninstallcheck dvi \
|
||||
dvi-am html html-am info info-am install install-am \
|
||||
install-binPROGRAMS install-data install-data-am install-dvi \
|
||||
install-dvi-am install-exec install-exec-am install-html \
|
||||
install-html-am install-info install-info-am install-man \
|
||||
install-pdf install-pdf-am install-ps install-ps-am \
|
||||
install-binPROGRAMS install-data install-data-am \
|
||||
install-data-hook install-dist_docDATA \
|
||||
install-dist_exampleconfDATA install-exec install-exec-am \
|
||||
install-exec-hook install-info install-info-am install-man \
|
||||
install-nobase_dist_ppscriptsSCRIPTS \
|
||||
install-nobase_dist_webuiDATA install-sbinSCRIPTS \
|
||||
install-strip installcheck installcheck-am installdirs \
|
||||
maintainer-clean maintainer-clean-generic mostlyclean \
|
||||
mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \
|
||||
tags uninstall uninstall-am uninstall-binPROGRAMS
|
||||
tags uninstall uninstall-am uninstall-binPROGRAMS \
|
||||
uninstall-dist_docDATA uninstall-dist_exampleconfDATA \
|
||||
uninstall-info-am uninstall-nobase_dist_ppscriptsSCRIPTS \
|
||||
uninstall-nobase_dist_webuiDATA uninstall-sbinSCRIPTS
|
||||
|
||||
|
||||
# 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.
|
||||
|
||||
# Configure installed script
|
||||
install-exec-hook:
|
||||
rm -f "$(DESTDIR)$(sbindir)/nzbgetd.temp"
|
||||
cp "$(DESTDIR)$(sbindir)/nzbgetd" "$(DESTDIR)$(sbindir)/nzbgetd.temp"
|
||||
sed 's?/usr/local/bin?$(bindir)?' < "$(DESTDIR)$(sbindir)/nzbgetd.temp" > "$(DESTDIR)$(sbindir)/nzbgetd"
|
||||
rm "$(DESTDIR)$(sbindir)/nzbgetd.temp"
|
||||
|
||||
# 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/ppscripts:installed to $(ppscriptsdir)/ppscripts:' < "$(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 subversion revision:
|
||||
# 1) If directory ".svn" exists we take revision from it using program svnversion (part of subversion package)
|
||||
# File is recreated only if revision number was changed.
|
||||
# 2) If directory ".svn" doesn't exists we keep and reuse file "svn_version.cpp",
|
||||
# which was possibly created early.
|
||||
# 3) If neither directory ".svn" nor file "svn_version.cpp" are available
|
||||
# we create new file "svn_version.c" with empty revision number.
|
||||
svn_version.cpp: FORCE
|
||||
@ if test -d ./.svn ; then \
|
||||
V="$(shell svnversion -n .)"; \
|
||||
H="$(shell test -f ./svn_version.cpp && head -n 1 svn_version.cpp)"; \
|
||||
if test "/* $$V */" != "$$H" ; then \
|
||||
( \
|
||||
echo "/* $$V */" ;\
|
||||
echo "/* This file is automatically regenerated on each build. Do not edit it. */" ;\
|
||||
echo "const char* svn_version(void)" ;\
|
||||
echo "{" ;\
|
||||
echo " const char* SVN_Version = \"$$V\";" ;\
|
||||
echo " return SVN_Version;" ;\
|
||||
echo "}" ;\
|
||||
) > svn_version.cpp ; \
|
||||
fi \
|
||||
elif test -f ./svn_version.cpp ; then \
|
||||
test "ok, reuse existing file"; \
|
||||
else \
|
||||
( \
|
||||
echo "/* */" ;\
|
||||
echo "/* This file is automatically regenerated on each build. Do not edit it. */" ;\
|
||||
echo "const char* svn_version(void)" ;\
|
||||
echo "{" ;\
|
||||
echo " const char* SVN_Version = \"\";" ;\
|
||||
echo " return SVN_Version;" ;\
|
||||
echo "}" ;\
|
||||
) > svn_version.cpp ; \
|
||||
fi
|
||||
FORCE:
|
||||
|
||||
clean-bak: rm *~
|
||||
|
||||
# Fix premissions
|
||||
dist-hook:
|
||||
chmod -x $(distdir)/*.cpp $(distdir)/*.h
|
||||
find $(distdir)/webui -type f -print -exec chmod -x {} \;
|
||||
# Tell versions [3.59,3.63) of GNU make to not export all variables.
|
||||
# Otherwise a system limit (for SysV at least) may be exceeded.
|
||||
.NOEXPORT:
|
||||
|
||||
298
MessageBase.h
298
MessageBase.h
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2013 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -27,7 +27,7 @@
|
||||
#ifndef MESSAGEBASE_H
|
||||
#define MESSAGEBASE_H
|
||||
|
||||
static const int32_t NZBMESSAGE_SIGNATURE = 0x6E7A6202; // = "nzb2" (nzb version 2)
|
||||
static const int32_t NZBMESSAGE_SIGNATURE = 0x6E7A621B; // = "nzb-XX" (protocol version)
|
||||
static const int NZBREQUESTFILENAMESIZE = 512;
|
||||
static const int NZBREQUESTPASSWORDSIZE = 32;
|
||||
|
||||
@@ -35,8 +35,14 @@ 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.
|
||||
* (host) byte order.
|
||||
* All char-strings ends with NULL-char.
|
||||
*
|
||||
* NOTE:
|
||||
* NZBGet communication protocol is intended for usage only by NZBGet itself.
|
||||
* The communication works only if server and client has the same version.
|
||||
* The compatibility with previous program versions is not provided.
|
||||
* Third-party programs should use JSON-RPC or XML-RPC to communicate with NZBGet.
|
||||
*/
|
||||
|
||||
// Possible values for field "m_iType" of struct "SNZBRequestBase":
|
||||
@@ -50,9 +56,14 @@ enum eRemoteRequest
|
||||
eRemoteRequestEditQueue,
|
||||
eRemoteRequestLog,
|
||||
eRemoteRequestShutdown,
|
||||
eRemoteRequestReload,
|
||||
eRemoteRequestVersion,
|
||||
eRemoteRequestPostQueue,
|
||||
eRemoteRequestWriteLog
|
||||
eRemoteRequestWriteLog,
|
||||
eRemoteRequestScan,
|
||||
eRemoteRequestHistory,
|
||||
eRemoteRequestDownloadUrl,
|
||||
eRemoteRequestUrlQueue
|
||||
};
|
||||
|
||||
// Possible values for field "m_iAction" of struct "SNZBEditQueueRequest":
|
||||
@@ -60,22 +71,68 @@ enum eRemoteRequest
|
||||
// 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
|
||||
eRemoteEditActionFileMoveOffset = 1, // move files to m_iOffset relative to the current position in download-queue
|
||||
eRemoteEditActionFileMoveTop, // move files to the top of download-queue
|
||||
eRemoteEditActionFileMoveBottom, // move files to the bottom of download-queue
|
||||
eRemoteEditActionFilePause, // pause files
|
||||
eRemoteEditActionFileResume, // resume (unpause) files
|
||||
eRemoteEditActionFileDelete, // delete files
|
||||
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)
|
||||
eRemoteEditActionFileSetPriority, // set priority for files
|
||||
eRemoteEditActionFileReorder, // (not supported)
|
||||
eRemoteEditActionFileSplit, // split - create new group from selected files
|
||||
eRemoteEditActionGroupMoveOffset, // move group to m_iOffset relative to the current position in download-queue
|
||||
eRemoteEditActionGroupMoveTop, // move group to the top of download-queue
|
||||
eRemoteEditActionGroupMoveBottom, // move group to the bottom of download-queue
|
||||
eRemoteEditActionGroupPause, // pause group
|
||||
eRemoteEditActionGroupResume, // resume (unpause) group
|
||||
eRemoteEditActionGroupDelete, // delete group
|
||||
eRemoteEditActionGroupDupeDelete, // delete group
|
||||
eRemoteEditActionGroupFinalDelete, // delete group
|
||||
eRemoteEditActionGroupPauseAllPars, // pause only (all) pars (does not affect other files) in group
|
||||
eRemoteEditActionGroupPauseExtraPars, // pause only (almost all) pars in group, except main par-file (does not affect other files)
|
||||
eRemoteEditActionGroupSetPriority, // set priority for groups
|
||||
eRemoteEditActionGroupSetCategory, // set or change category for a group
|
||||
eRemoteEditActionGroupMerge, // merge group
|
||||
eRemoteEditActionGroupSetParameter, // set post-process parameter for group
|
||||
eRemoteEditActionGroupSetName, // set group name (rename group)
|
||||
eRemoteEditActionGroupSetDupeKey, // (reserved)
|
||||
eRemoteEditActionGroupSetDupeScore, // (reserved)
|
||||
eRemoteEditActionGroupSetDupeMode, // (reserved)
|
||||
eRemoteEditActionPostMoveOffset = 51, // move post-job to m_iOffset relative to the current position in post-queue
|
||||
eRemoteEditActionPostMoveTop, // move post-job to the top of post-queue
|
||||
eRemoteEditActionPostMoveBottom, // move post-job to the bottom of post-queue
|
||||
eRemoteEditActionPostDelete, // delete post-job
|
||||
eRemoteEditActionHistoryDelete, // hide history-item
|
||||
eRemoteEditActionHistoryFinalDelete, // delete history-item
|
||||
eRemoteEditActionHistoryReturn, // move history-item back to download queue
|
||||
eRemoteEditActionHistoryProcess, // move history-item back to download queue and start postprocessing
|
||||
eRemoteEditActionHistoryRedownload, // move history-item back to download queue for redownload
|
||||
eRemoteEditActionHistorySetParameter, // set post-process parameter for history-item
|
||||
eRemoteEditActionHistorySetDupeKey, // (reserved)
|
||||
eRemoteEditActionHistorySetDupeScore, // (reserved)
|
||||
eRemoteEditActionHistorySetDupeMode, // (reserved)
|
||||
eRemoteEditActionHistorySetDupeBackup, // (reserved)
|
||||
eRemoteEditActionHistoryMarkBad, // mark history-item as bad (and download other duplicate)
|
||||
eRemoteEditActionHistoryMarkGood // mark history-item as good (and push it into dup-history)
|
||||
};
|
||||
|
||||
// Possible values for field "m_iAction" of struct "SNZBPauseUnpauseRequest":
|
||||
enum eRemotePauseUnpauseAction
|
||||
{
|
||||
eRemotePauseUnpauseActionDownload = 1, // pause/unpause download queue
|
||||
eRemotePauseUnpauseActionDownload2, // pause/unpause download queue (second pause-register)
|
||||
eRemotePauseUnpauseActionPostProcess, // pause/unpause post-processor queue
|
||||
eRemotePauseUnpauseActionScan // pause/unpause scan of incoming nzb-directory
|
||||
};
|
||||
|
||||
// Possible values for field "m_iMatchMode" of struct "SNZBEditQueueRequest":
|
||||
enum eRemoteMatchMode
|
||||
{
|
||||
eRemoteMatchModeID = 1, // ID
|
||||
eRemoteMatchModeName, // Name
|
||||
eRemoteMatchModeRegEx, // RegEx
|
||||
};
|
||||
|
||||
// The basic SNZBRequestBase struct, used in all requests
|
||||
@@ -84,7 +141,8 @@ 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
|
||||
char m_szUsername[NZBREQUESTPASSWORDSIZE]; // User name
|
||||
char m_szPassword[NZBREQUESTPASSWORDSIZE]; // Password
|
||||
};
|
||||
|
||||
// The basic SNZBResposneBase struct, used in all responses
|
||||
@@ -99,7 +157,10 @@ 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
|
||||
char m_szCategory[NZBREQUESTFILENAMESIZE]; // Category, can be empty
|
||||
int32_t m_bAddFirst; // 1 - add file to the top of download queue
|
||||
int32_t m_bAddPaused; // 1 - pause added files
|
||||
int32_t m_iPriority; // Priority for files (0 - default)
|
||||
int32_t m_iTrailingDataLength; // Length of nzb-file in bytes
|
||||
//char m_szContent[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
@@ -119,6 +180,9 @@ 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
|
||||
int32_t m_iMatchMode; // File/Group match mode, see enum eRemoteMatchMode (only values eRemoteMatchModeID (no filter) and eRemoteMatchModeRegEx are allowed)
|
||||
int32_t m_bMatchGroup; // 0 - match files; 1 - match nzbs (when m_iMatchMode == eRemoteMatchModeRegEx)
|
||||
char m_szPattern[NZBREQUESTFILENAMESIZE]; // RegEx Pattern (when m_iMatchMode == eRemoteMatchModeRegEx)
|
||||
};
|
||||
|
||||
// A list response
|
||||
@@ -130,37 +194,73 @@ struct SNZBListResponse
|
||||
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_bDownloadPaused; // 1 - download queue is currently in paused-state
|
||||
int32_t m_bDownload2Paused; // 1 - download queue is currently in paused-state (second pause-register)
|
||||
int32_t m_bDownloadStandBy; // 0 - there are currently downloads running, 1 - no downloads in progress (download queue paused or all download jobs completed)
|
||||
int32_t m_bPostPaused; // 1 - post-processor queue is currently in paused-state
|
||||
int32_t m_bScanPaused; // 1 - scaning of incoming directory is currently in paused-state
|
||||
int32_t m_iThreadCount; // Number of threads running
|
||||
int32_t m_iPostJobCount; // Number of jobs in post-processor queue (including current job)
|
||||
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
|
||||
int32_t m_bRegExValid; // 0 - error in RegEx-pattern, 1 - RegEx-pattern is valid (only when Request has eRemoteMatchModeRegEx)
|
||||
int32_t m_iNrTrailingNZBEntries; // Number of List-NZB-entries, following to this structure
|
||||
int32_t m_iNrTrailingPPPEntries; // Number of List-PPP-entries, following to this structure
|
||||
int32_t m_iNrTrailingFileEntries; // Number of List-File-entries, following to this structure
|
||||
int32_t m_iTrailingDataLength; // Length of all List-entries, following to this structure
|
||||
// SNZBListResponseEntry m_NZBEntries[m_iNrTrailingNZBEntries] // variable sized
|
||||
// SNZBListResponseEntry m_PPPEntries[m_iNrTrailingPPPEntries] // variable sized
|
||||
// SNZBListResponseEntry m_FileEntries[m_iNrTrailingFileEntries] // variable sized
|
||||
};
|
||||
|
||||
// A list response entry
|
||||
struct SNZBListResponseEntry
|
||||
// A list response nzb entry
|
||||
struct SNZBListResponseNZBEntry
|
||||
{
|
||||
int32_t m_iSizeLo; // Size of all files in bytes, Low 32-bits of 64-bit value
|
||||
int32_t m_iSizeHi; // Size of all files in bytes, High 32-bits of 64-bit value
|
||||
int32_t m_bMatch; // 1 - group matches the pattern (only when Request has eRemoteMatchModeRegEx)
|
||||
int32_t m_iFilenameLen; // Length of Filename-string (m_szFilename), following to this record
|
||||
int32_t m_iNameLen; // Length of Name-string (m_szName), following to this record
|
||||
int32_t m_iDestDirLen; // Length of DestDir-string (m_szDestDir), following to this record
|
||||
int32_t m_iCategoryLen; // Length of Category-string (m_szCategory), following to this record
|
||||
int32_t m_iQueuedFilenameLen; // Length of queued file name (m_szQueuedFilename), following to this record
|
||||
//char m_szFilename[m_iFilenameLen]; // variable sized
|
||||
//char m_szName[m_iNameLen]; // variable sized
|
||||
//char m_szDestDir[m_iDestDirLen]; // variable sized
|
||||
//char m_szCategory[m_iCategoryLen]; // variable sized
|
||||
//char m_szQueuedFilename[m_iQueuedFilenameLen]; // variable sized
|
||||
};
|
||||
|
||||
// A list response pp-parameter entry
|
||||
struct SNZBListResponsePPPEntry
|
||||
{
|
||||
int32_t m_iNZBIndex; // Index of NZB-Entry in m_NZBEntries-list
|
||||
int32_t m_iNameLen; // Length of Name-string (m_szName), following to this record
|
||||
int32_t m_iValueLen; // Length of Value-string (m_szValue), following to this record
|
||||
//char m_szName[m_iNameLen]; // variable sized
|
||||
//char m_szValue[m_iValueLen]; // variable sized
|
||||
};
|
||||
|
||||
// A list response file entry
|
||||
struct SNZBListResponseFileEntry
|
||||
{
|
||||
int32_t m_iID; // Entry-ID
|
||||
int32_t m_iNZBIndex; // Index of NZB-Entry in m_NZBEntries-list
|
||||
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_iPriority; // Download priority
|
||||
int32_t m_iActiveDownloads; // Number of active downloads for this file
|
||||
int32_t m_bMatch; // 1 - file matches the pattern (only when Request has eRemoteMatchModeRegEx)
|
||||
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
|
||||
@@ -196,6 +296,7 @@ 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
|
||||
int32_t m_iAction; // Action to be executed, see enum eRemotePauseUnpauseAction
|
||||
};
|
||||
|
||||
// A Pause/Unpause response
|
||||
@@ -223,17 +324,23 @@ struct SNZBSetDownloadRateResponse
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// An edit queue request
|
||||
// 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_iAction; // Action to be executed, see enum eRemoteEditAction
|
||||
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
|
||||
int32_t m_iMatchMode; // File/Group match mode, see enum eRemoteMatchMode
|
||||
int32_t m_iNrTrailingIDEntries; // Number of ID-entries, following to this structure
|
||||
int32_t m_iNrTrailingNameEntries; // Number of Name-entries, following to this structure
|
||||
int32_t m_iTrailingNameEntriesLen; // Length of all Name-entries, following to this structure
|
||||
int32_t m_iTextLen; // Length of Text-string (m_szText), following to this record
|
||||
int32_t m_iTrailingDataLength; // Length of Text-string and all ID-entries, following to this structure
|
||||
//char m_szText[m_iTextLen]; // variable sized
|
||||
//int32_t m_iIDs[m_iNrTrailingIDEntries]; // variable sized array of IDs. For File-Actions - ID of file, for Group-Actions - ID of any file belonging to group
|
||||
//char* m_szNames[m_iNrTrailingNameEntries]; // variable sized array of strings. For File-Actions - name of file incl. nzb-name as path, for Group-Actions - name of group
|
||||
};
|
||||
|
||||
// An edit queue response
|
||||
@@ -275,6 +382,21 @@ struct SNZBShutdownResponse
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// Reload server request
|
||||
struct SNZBReloadRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
};
|
||||
|
||||
// Reload server response
|
||||
struct SNZBReloadResponse
|
||||
{
|
||||
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
|
||||
{
|
||||
@@ -316,12 +438,10 @@ struct SNZBPostQueueResponseEntry
|
||||
int32_t m_iTotalTimeSec; // Number of seconds this post-job is beeing processed (after it first changed the state from QUEUED).
|
||||
int32_t m_iStageTimeSec; // Number of seconds the current stage is beeing processed.
|
||||
int32_t m_iNZBFilenameLen; // Length of NZBFileName-string (m_szNZBFilename), following to this record
|
||||
int32_t m_iParFilename; // Length of ParFilename-string (m_szParFilename), following to this record
|
||||
int32_t m_iInfoNameLen; // Length of Filename-string (m_szFilename), following to this record
|
||||
int32_t m_iDestDirLen; // Length of DestDir-string (m_szDestDir), following to this record
|
||||
int32_t m_iProgressLabelLen; // Length of ProgressLabel-string (m_szProgressLabel), following to this record
|
||||
//char m_szNZBFilename[m_iNZBFilenameLen]; // variable sized, may contain full path (local path on client) or only filename
|
||||
//char m_szParFilename[m_iParFilename]; // variable sized
|
||||
//char m_szInfoName[m_iInfoNameLen]; // variable sized
|
||||
//char m_szDestDir[m_iDestDirLen]; // variable sized
|
||||
//char m_szProgressLabel[m_iProgressLabelLen]; // variable sized
|
||||
@@ -336,7 +456,7 @@ struct SNZBWriteLogRequest
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// Write log response
|
||||
// Write log response
|
||||
struct SNZBWriteLogResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
@@ -345,4 +465,102 @@ struct SNZBWriteLogResponse
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// Scan nzb directory request
|
||||
struct SNZBScanRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bSyncMode; // 0 - asynchronous Scan (the command returns immediately), 1 - synchronous Scan (the command returns when the scan is completed)
|
||||
};
|
||||
|
||||
// Scan nzb directory response
|
||||
struct SNZBScanResponse
|
||||
{
|
||||
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 history request
|
||||
struct SNZBHistoryRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
};
|
||||
|
||||
// history response
|
||||
struct SNZBHistoryResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iEntrySize; // Size of the SNZBHistoryResponseEntry-struct
|
||||
int32_t m_iNrTrailingEntries; // Number of History-entries, following to this structure
|
||||
int32_t m_iTrailingDataLength; // Length of all History-entries, following to this structure
|
||||
// SNZBHistoryResponseEntry m_Entries[m_iNrTrailingEntries] // variable sized
|
||||
};
|
||||
|
||||
// history entry
|
||||
struct SNZBHistoryResponseEntry
|
||||
{
|
||||
int32_t m_iID; // History-ID
|
||||
int32_t m_iKind; // Kind of Item: 1 - Collection (NZB), 2 - URL
|
||||
int32_t m_tTime; // When the item was added to history. time since the Epoch (00:00:00 UTC, January 1, 1970), measured in seconds.
|
||||
int32_t m_iNicenameLen; // Length of Nicename-string (m_szNicename), following to this record
|
||||
// for Collection items (m_iKind = 1)
|
||||
int32_t m_iSizeLo; // Size of all files in bytes, Low 32-bits of 64-bit value
|
||||
int32_t m_iSizeHi; // Size of all files in bytes, High 32-bits of 64-bit value
|
||||
int32_t m_iFileCount; // Initial number of files included in NZB-file
|
||||
int32_t m_iParStatus; // See NZBInfo::EParStatus
|
||||
int32_t m_iScriptStatus; // See NZBInfo::EScriptStatus
|
||||
// for URL items (m_iKind = 2)
|
||||
int32_t m_iUrlStatus; // See UrlInfo::EStatus
|
||||
// trailing data
|
||||
//char m_szNicename[m_iNicenameLen]; // variable sized
|
||||
};
|
||||
|
||||
// download url request
|
||||
struct SNZBDownloadUrlRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
char m_szURL[NZBREQUESTFILENAMESIZE]; // url to nzb-file
|
||||
char m_szNZBFilename[NZBREQUESTFILENAMESIZE];// Name of nzb-file. Can be empty, then the filename is read from URL download response
|
||||
char m_szCategory[NZBREQUESTFILENAMESIZE]; // Category, can be empty
|
||||
int32_t m_bAddFirst; // 1 - add url to the top of download queue
|
||||
int32_t m_bAddPaused; // 1 - pause added files
|
||||
int32_t m_iPriority; // Priority for files (0 - default)
|
||||
};
|
||||
|
||||
// download url response
|
||||
struct SNZBDownloadUrlResponse
|
||||
{
|
||||
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
|
||||
};
|
||||
|
||||
// UrlQueue request
|
||||
struct SNZBUrlQueueRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
};
|
||||
|
||||
// UrlQueue response
|
||||
struct SNZBUrlQueueResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iEntrySize; // Size of the SNZBUrlQueueResponseEntry-struct
|
||||
int32_t m_iNrTrailingEntries; // Number of UrlQueue-entries, following to this structure
|
||||
int32_t m_iTrailingDataLength; // Length of all UrlQueue-entries, following to this structure
|
||||
// SNZBUrlQueueResponseEntry m_Entries[m_iNrTrailingEntries] // variable sized
|
||||
};
|
||||
|
||||
// UrlQueue response entry
|
||||
struct SNZBUrlQueueResponseEntry
|
||||
{
|
||||
int32_t m_iID; // ID of Url-entry
|
||||
int32_t m_iURLLen; // Length of URL-string (m_szURL), following to this record
|
||||
int32_t m_iNZBFilenameLen; // Length of NZBFilename-string (m_szNZBFilename), following to this record
|
||||
//char m_szURL[m_iURLLen]; // variable sized
|
||||
//char m_szNZBFilename[m_iNZBFilenameLen]; // variable sized
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* 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-2011 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -60,6 +60,18 @@
|
||||
#undef erase
|
||||
#endif
|
||||
|
||||
#ifndef WIN32
|
||||
// curses.h on Solaris declares "clear()" via DEFINE. That causes problems, because
|
||||
// it also affects calls to deque's method "clear()", producing compiler errors.
|
||||
// We use function "curses_clear()" to call macro "clear" of curses, then
|
||||
// undefine macro "clear".
|
||||
void curses_clear()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
#undef clear
|
||||
#endif
|
||||
|
||||
extern Options* g_pOptions;
|
||||
extern void ExitProc();
|
||||
|
||||
@@ -74,6 +86,7 @@ static const int NCURSES_COLORPAIR_KEYBAR = 8;
|
||||
static const int NCURSES_COLORPAIR_INFOLINE = 9;
|
||||
static const int NCURSES_COLORPAIR_TEXTHIGHL = 10;
|
||||
static const int NCURSES_COLORPAIR_CURSOR = 11;
|
||||
static const int NCURSES_COLORPAIR_HINT = 12;
|
||||
|
||||
static const int MAX_SCREEN_WIDTH = 512;
|
||||
|
||||
@@ -125,10 +138,10 @@ NCursesFrontend::NCursesFrontend()
|
||||
m_bGroupFiles = g_pOptions->GetCursesGroup();
|
||||
m_QueueWindowPercentage = 0.5f;
|
||||
m_iDataUpdatePos = 0;
|
||||
m_bUpdateNextTime = false;
|
||||
m_iLastEditEntry = -1;
|
||||
m_bLastPausePars = false;
|
||||
|
||||
m_groupQueue.clear();
|
||||
m_szHint = NULL;
|
||||
|
||||
// Setup curses
|
||||
#ifdef WIN32
|
||||
@@ -182,20 +195,16 @@ NCursesFrontend::NCursesFrontend()
|
||||
init_pair(NCURSES_COLORPAIR_INFOLINE, COLOR_WHITE, COLOR_BLUE);
|
||||
init_pair(NCURSES_COLORPAIR_TEXTHIGHL, COLOR_BLACK, COLOR_CYAN);
|
||||
init_pair(NCURSES_COLORPAIR_CURSOR, COLOR_BLACK, COLOR_YELLOW);
|
||||
init_pair(NCURSES_COLORPAIR_HINT, COLOR_WHITE, COLOR_RED);
|
||||
}
|
||||
}
|
||||
|
||||
NCursesFrontend::~NCursesFrontend()
|
||||
{
|
||||
#ifdef WIN32
|
||||
if (m_pScreenBuffer)
|
||||
{
|
||||
free(m_pScreenBuffer);
|
||||
}
|
||||
if (m_pOldScreenBuffer)
|
||||
{
|
||||
free(m_pOldScreenBuffer);
|
||||
}
|
||||
free(m_pScreenBuffer);
|
||||
free(m_pOldScreenBuffer);
|
||||
|
||||
m_ColorAttr.clear();
|
||||
|
||||
HANDLE hConsole = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
@@ -210,32 +219,52 @@ NCursesFrontend::~NCursesFrontend()
|
||||
endwin();
|
||||
#endif
|
||||
printf("\n");
|
||||
SetHint(NULL);
|
||||
}
|
||||
|
||||
void NCursesFrontend::Run()
|
||||
{
|
||||
debug("Entering NCursesFrontend-loop");
|
||||
|
||||
int iScreenUpdateInterval = 25;
|
||||
int iScreenUpdatePos = 0;
|
||||
m_iDataUpdatePos = 0;
|
||||
|
||||
while (!IsStopped())
|
||||
{
|
||||
// The data (queue and log) is updated each m_iUpdateInterval msec,
|
||||
// but the window is updated more often for better reaction on user's input
|
||||
if (iScreenUpdatePos <= 0 || m_iDataUpdatePos <= 0)
|
||||
|
||||
bool updateNow = false;
|
||||
int iKey = ReadConsoleKey();
|
||||
|
||||
if (iKey != READKEY_EMPTY)
|
||||
{
|
||||
iScreenUpdatePos = iScreenUpdateInterval;
|
||||
Update();
|
||||
if (m_iDataUpdatePos <= 0)
|
||||
{
|
||||
m_iDataUpdatePos = m_iUpdateInterval;
|
||||
}
|
||||
// Update now and next if a key is pressed.
|
||||
updateNow = true;
|
||||
m_bUpdateNextTime = true;
|
||||
}
|
||||
else if (m_bUpdateNextTime)
|
||||
{
|
||||
// Update due to key being pressed during previous call.
|
||||
updateNow = true;
|
||||
m_bUpdateNextTime = false;
|
||||
}
|
||||
else if (m_iDataUpdatePos <= 0)
|
||||
{
|
||||
updateNow = true;
|
||||
m_bUpdateNextTime = false;
|
||||
}
|
||||
|
||||
if (updateNow)
|
||||
{
|
||||
Update(iKey);
|
||||
}
|
||||
|
||||
if (m_iDataUpdatePos <= 0)
|
||||
{
|
||||
m_iDataUpdatePos = m_iUpdateInterval;
|
||||
}
|
||||
|
||||
usleep(10 * 1000);
|
||||
iScreenUpdatePos -= 10;
|
||||
m_iDataUpdatePos -= 10;
|
||||
}
|
||||
|
||||
@@ -248,9 +277,10 @@ void NCursesFrontend::Run()
|
||||
void NCursesFrontend::NeedUpdateData()
|
||||
{
|
||||
m_iDataUpdatePos = 10;
|
||||
m_bUpdateNextTime = true;
|
||||
}
|
||||
|
||||
void NCursesFrontend::Update()
|
||||
void NCursesFrontend::Update(int iKey)
|
||||
{
|
||||
// Figure out how big the screen is
|
||||
CalcWindowSizes();
|
||||
@@ -265,6 +295,9 @@ void NCursesFrontend::Update()
|
||||
return;
|
||||
}
|
||||
PrepareGroupQueue();
|
||||
|
||||
// recalculate frame sizes
|
||||
CalcWindowSizes();
|
||||
}
|
||||
|
||||
if (m_eInputMode == eEditQueue)
|
||||
@@ -278,7 +311,7 @@ void NCursesFrontend::Update()
|
||||
}
|
||||
|
||||
//------------------------------------------
|
||||
// Print Current NZBQueue
|
||||
// Print Current NZBInfoList
|
||||
//------------------------------------------
|
||||
if (m_iQueueWinHeight > 0)
|
||||
{
|
||||
@@ -297,8 +330,7 @@ void NCursesFrontend::Update()
|
||||
|
||||
PrintKeyInputBar();
|
||||
|
||||
// Update the input
|
||||
UpdateInput();
|
||||
UpdateInput(iKey);
|
||||
|
||||
RefreshScreen();
|
||||
}
|
||||
@@ -324,7 +356,7 @@ void NCursesFrontend::CalcWindowSizes()
|
||||
m_pOldScreenBuffer = (CHAR_INFO*)realloc(m_pOldScreenBuffer, m_iScreenBufferSize);
|
||||
memset(m_pOldScreenBuffer, 0, m_iScreenBufferSize);
|
||||
#else
|
||||
clear();
|
||||
curses_clear();
|
||||
#endif
|
||||
m_iScreenHeight = iNrRows;
|
||||
m_iScreenWidth = iNrColumns;
|
||||
@@ -362,7 +394,7 @@ int NCursesFrontend::CalcQueueSize()
|
||||
else
|
||||
{
|
||||
DownloadQueue* pDownloadQueue = LockQueue();
|
||||
int iQueueSize = pDownloadQueue->size();
|
||||
int iQueueSize = pDownloadQueue->GetFileQueue()->size();
|
||||
UnlockQueue();
|
||||
return iQueueSize;
|
||||
}
|
||||
@@ -501,7 +533,7 @@ void NCursesFrontend::PrintMessages()
|
||||
|
||||
int NCursesFrontend::PrintMessage(Message* Msg, int iRow, int iMaxLines)
|
||||
{
|
||||
char* szMessageType[] = { "INFO ", "WARNING ", "ERROR ", "DEBUG ", "DETAIL "};
|
||||
const char* szMessageType[] = { "INFO ", "WARNING ", "ERROR ", "DEBUG ", "DETAIL "};
|
||||
const int iMessageTypeColor[] = { NCURSES_COLORPAIR_INFO, NCURSES_COLORPAIR_WARNING,
|
||||
NCURSES_COLORPAIR_ERROR, NCURSES_COLORPAIR_DEBUG, NCURSES_COLORPAIR_DETAIL };
|
||||
|
||||
@@ -513,6 +545,8 @@ int NCursesFrontend::PrintMessage(Message* Msg, int iRow, int iMaxLines)
|
||||
szText = (char*)malloc(iLen);
|
||||
|
||||
time_t rawtime = Msg->GetTime();
|
||||
rawtime += g_pOptions->GetTimeCorrection();
|
||||
|
||||
char szTime[50];
|
||||
#ifdef HAVE_CTIME_R_3
|
||||
ctime_r(&rawtime, szTime, 50);
|
||||
@@ -576,10 +610,10 @@ void NCursesFrontend::PrintStatus()
|
||||
char timeString[100];
|
||||
timeString[0] = '\0';
|
||||
|
||||
float fCurrentDownloadSpeed = m_bStandBy ? 0 : m_fCurrentDownloadSpeed;
|
||||
if (fCurrentDownloadSpeed > 0.0 && !m_bPause)
|
||||
int iCurrentDownloadSpeed = m_bStandBy ? 0 : m_iCurrentDownloadSpeed;
|
||||
if (iCurrentDownloadSpeed > 0 && !(m_bPauseDownload || m_bPauseDownload2))
|
||||
{
|
||||
long long remain_sec = (long long)(m_lRemainingSize / (fCurrentDownloadSpeed * 1024));
|
||||
long long remain_sec = (long long)(m_lRemainingSize / iCurrentDownloadSpeed);
|
||||
int h = (int)(remain_sec / 3600);
|
||||
int m = (int)((remain_sec % 3600) / 60);
|
||||
int s = (int)(remain_sec % 60);
|
||||
@@ -587,9 +621,9 @@ void NCursesFrontend::PrintStatus()
|
||||
}
|
||||
|
||||
char szDownloadLimit[128];
|
||||
if (m_fDownloadLimit > 0.0f)
|
||||
if (m_iDownloadLimit > 0)
|
||||
{
|
||||
sprintf(szDownloadLimit, ", Limit %.0f KB/s", m_fDownloadLimit);
|
||||
sprintf(szDownloadLimit, ", Limit %.0f KB/s", (float)m_iDownloadLimit / 1024.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -606,11 +640,15 @@ void NCursesFrontend::PrintStatus()
|
||||
szPostStatus[0] = 0;
|
||||
}
|
||||
|
||||
float fAverageSpeed = Util::Int64ToFloat(m_iDnTimeSec > 0 ? m_iAllBytes / m_iDnTimeSec / 1024 : 0);
|
||||
float fAverageSpeed = (float)(Util::Int64ToFloat(m_iDnTimeSec > 0 ? m_iAllBytes / m_iDnTimeSec : 0) / 1024.0);
|
||||
|
||||
snprintf(tmp, MAX_SCREEN_WIDTH, " %d threads, %.0f KB/s, %.2f MB remaining%s%s%s%s, Avg. %.0f KB/s",
|
||||
m_iThreadCount, fCurrentDownloadSpeed, (float)(Util::Int64ToFloat(m_lRemainingSize) / 1024.0 / 1024.0), timeString,
|
||||
szPostStatus, m_bPause ? (m_bStandBy ? ", Paused" : ", Pausing") : "", szDownloadLimit, fAverageSpeed);
|
||||
snprintf(tmp, MAX_SCREEN_WIDTH, " %d threads, %.*f KB/s, %.2f MB remaining%s%s%s%s%s, Avg. %.*f KB/s",
|
||||
m_iThreadCount, (iCurrentDownloadSpeed >= 10*1024 ? 0 : 1), (float)iCurrentDownloadSpeed / 1024.0,
|
||||
(float)(Util::Int64ToFloat(m_lRemainingSize) / 1024.0 / 1024.0), timeString, szPostStatus,
|
||||
m_bPauseDownload || m_bPauseDownload2 ? (m_bStandBy ? ", Paused" : ", Pausing") : "",
|
||||
m_bPauseDownload || m_bPauseDownload2 ?
|
||||
(m_bPauseDownload && m_bPauseDownload2 ? " (+2)" : m_bPauseDownload2 ? " (2)" : "") : "",
|
||||
szDownloadLimit, (fAverageSpeed >= 10 ? 0 : 1), fAverageSpeed);
|
||||
tmp[MAX_SCREEN_WIDTH - 1] = '\0';
|
||||
PlotLine(tmp, iStatusRow, 0, NCURSES_COLORPAIR_STATUS);
|
||||
}
|
||||
@@ -620,6 +658,20 @@ void NCursesFrontend::PrintKeyInputBar()
|
||||
int iQueueSize = CalcQueueSize();
|
||||
int iInputBarRow = m_iScreenHeight - 1;
|
||||
|
||||
if (m_szHint)
|
||||
{
|
||||
time_t tTime = time(NULL);
|
||||
if (tTime - m_tStartHint < 5)
|
||||
{
|
||||
PlotLine(m_szHint, iInputBarRow, 0, NCURSES_COLORPAIR_HINT);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
SetHint(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
switch (m_eInputMode)
|
||||
{
|
||||
case eNormal:
|
||||
@@ -634,7 +686,7 @@ void NCursesFrontend::PrintKeyInputBar()
|
||||
break;
|
||||
case eEditQueue:
|
||||
{
|
||||
char* szStatus = NULL;
|
||||
const char* szStatus = NULL;
|
||||
if (m_iSelectedQueueEntry > 0 && iQueueSize > 1 && m_iSelectedQueueEntry == iQueueSize - 1)
|
||||
{
|
||||
szStatus = "(Q)uit | (E)xit | (P)ause | (D)elete | (U)p/(T)op";
|
||||
@@ -666,6 +718,17 @@ void NCursesFrontend::PrintKeyInputBar()
|
||||
}
|
||||
}
|
||||
|
||||
void NCursesFrontend::SetHint(const char* szHint)
|
||||
{
|
||||
free(m_szHint);
|
||||
m_szHint = NULL;
|
||||
if (szHint)
|
||||
{
|
||||
m_szHint = strdup(szHint);
|
||||
m_tStartHint = time(NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void NCursesFrontend::PrintQueue()
|
||||
{
|
||||
if (m_bGroupFiles)
|
||||
@@ -683,7 +746,7 @@ void NCursesFrontend::PrintFileQueue()
|
||||
int iLineNr = m_iQueueWinTop;
|
||||
|
||||
DownloadQueue* pDownloadQueue = LockQueue();
|
||||
if (pDownloadQueue->empty())
|
||||
if (pDownloadQueue->GetFileQueue()->empty())
|
||||
{
|
||||
char szBuffer[MAX_SCREEN_WIDTH];
|
||||
snprintf(szBuffer, sizeof(szBuffer), "%s Files for downloading", m_bUseColor ? "" : "*** ");
|
||||
@@ -698,7 +761,7 @@ void NCursesFrontend::PrintFileQueue()
|
||||
long long lPaused = 0;
|
||||
int iPausedFiles = 0;
|
||||
int i = 0;
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++, i++)
|
||||
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++, i++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
|
||||
@@ -716,14 +779,15 @@ void NCursesFrontend::PrintFileQueue()
|
||||
}
|
||||
|
||||
char szRemaining[20];
|
||||
FormatFileSize(szRemaining, sizeof(szRemaining), lRemaining);
|
||||
Util::FormatFileSize(szRemaining, sizeof(szRemaining), lRemaining);
|
||||
|
||||
char szUnpaused[20];
|
||||
FormatFileSize(szUnpaused, sizeof(szUnpaused), lRemaining - lPaused);
|
||||
Util::FormatFileSize(szUnpaused, sizeof(szUnpaused), lRemaining - lPaused);
|
||||
|
||||
char szBuffer[MAX_SCREEN_WIDTH];
|
||||
snprintf(szBuffer, sizeof(szBuffer), " %sFiles for downloading - %i / %i files in queue - %s / %s",
|
||||
m_bUseColor ? "" : "*** ", pDownloadQueue->size(), pDownloadQueue->size() - iPausedFiles, szRemaining, szUnpaused);
|
||||
m_bUseColor ? "" : "*** ", (int)pDownloadQueue->GetFileQueue()->size(),
|
||||
(int)pDownloadQueue->GetFileQueue()->size() - iPausedFiles, szRemaining, szUnpaused);
|
||||
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
|
||||
PrintTopHeader(szBuffer, m_iQueueWinTop, true);
|
||||
}
|
||||
@@ -749,6 +813,19 @@ void NCursesFrontend::PrintFilename(FileInfo * pFileInfo, int iRow, bool bSelect
|
||||
color = NCURSES_COLORPAIR_TEXT;
|
||||
}
|
||||
|
||||
const char* szDownloading = "";
|
||||
if (pFileInfo->GetActiveDownloads() > 0)
|
||||
{
|
||||
szDownloading = " *";
|
||||
}
|
||||
|
||||
char szPriority[100];
|
||||
szPriority[0] = '\0';
|
||||
if (pFileInfo->GetPriority() != 0)
|
||||
{
|
||||
sprintf(szPriority, " [%+i]", pFileInfo->GetPriority());
|
||||
}
|
||||
|
||||
char szCompleted[20];
|
||||
szCompleted[0] = '\0';
|
||||
if (pFileInfo->GetRemainingSize() < pFileInfo->GetSize())
|
||||
@@ -759,7 +836,7 @@ void NCursesFrontend::PrintFilename(FileInfo * pFileInfo, int iRow, bool bSelect
|
||||
char szNZBNiceName[1024];
|
||||
if (m_bShowNZBname)
|
||||
{
|
||||
pFileInfo->GetNZBInfo()->GetNiceNZBName(szNZBNiceName, 1023);
|
||||
strncpy(szNZBNiceName, pFileInfo->GetNZBInfo()->GetName(), 1023);
|
||||
int len = strlen(szNZBNiceName);
|
||||
szNZBNiceName[len] = PATH_SEPARATOR;
|
||||
szNZBNiceName[len + 1] = '\0';
|
||||
@@ -770,35 +847,15 @@ void NCursesFrontend::PrintFilename(FileInfo * pFileInfo, int iRow, bool bSelect
|
||||
}
|
||||
|
||||
char szBuffer[MAX_SCREEN_WIDTH];
|
||||
snprintf(szBuffer, MAX_SCREEN_WIDTH, "%s%i%s %s%s (%.2f MB%s)%s", Brace1, pFileInfo->GetID(),
|
||||
Brace2, szNZBNiceName, pFileInfo->GetFilename(), (float)(Util::Int64ToFloat(pFileInfo->GetSize()) / 1024.0 / 1024.0),
|
||||
snprintf(szBuffer, MAX_SCREEN_WIDTH, "%s%i%s%s%s %s%s (%.2f MB%s)%s", Brace1, pFileInfo->GetID(),
|
||||
Brace2, szPriority, szDownloading, szNZBNiceName, pFileInfo->GetFilename(),
|
||||
(float)(Util::Int64ToFloat(pFileInfo->GetSize()) / 1024.0 / 1024.0),
|
||||
szCompleted, pFileInfo->GetPaused() ? " (paused)" : "");
|
||||
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
|
||||
|
||||
PlotLine(szBuffer, iRow, 0, color);
|
||||
}
|
||||
|
||||
void NCursesFrontend::FormatFileSize(char * szBuffer, int iBufLen, long long lFileSize)
|
||||
{
|
||||
if (lFileSize > 1024 * 1024 * 1024)
|
||||
{
|
||||
snprintf(szBuffer, iBufLen, "%.2f GB", (float)(Util::Int64ToFloat(lFileSize) / 1024 / 1024 / 1024));
|
||||
}
|
||||
else if (lFileSize > 1024 * 1024)
|
||||
{
|
||||
snprintf(szBuffer, iBufLen, "%.2f MB", (float)(Util::Int64ToFloat(lFileSize) / 1024 / 1024));
|
||||
}
|
||||
else if (lFileSize > 1024)
|
||||
{
|
||||
snprintf(szBuffer, iBufLen, "%.2f KB", (float)(Util::Int64ToFloat(lFileSize) / 1024));
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(szBuffer, iBufLen, "%i", (int)lFileSize);
|
||||
}
|
||||
szBuffer[iBufLen - 1] = '\0';
|
||||
}
|
||||
|
||||
void NCursesFrontend::PrintTopHeader(char* szHeader, int iLineNr, bool bUpTime)
|
||||
{
|
||||
char szBuffer[MAX_SCREEN_WIDTH];
|
||||
@@ -863,6 +920,7 @@ void NCursesFrontend::PrintGroupQueue()
|
||||
{
|
||||
int iLineNr = m_iQueueWinTop;
|
||||
|
||||
LockQueue();
|
||||
GroupQueue* pGroupQueue = &m_groupQueue;
|
||||
if (pGroupQueue->empty())
|
||||
{
|
||||
@@ -875,8 +933,9 @@ void NCursesFrontend::PrintGroupQueue()
|
||||
else
|
||||
{
|
||||
iLineNr++;
|
||||
long long lRemaining = 0;
|
||||
long long lPaused = 0;
|
||||
|
||||
ResetColWidths();
|
||||
int iCalcLineNr = iLineNr;
|
||||
int i = 0;
|
||||
for (GroupQueue::iterator it = pGroupQueue->begin(); it != pGroupQueue->end(); it++, i++)
|
||||
{
|
||||
@@ -884,7 +943,20 @@ void NCursesFrontend::PrintGroupQueue()
|
||||
|
||||
if (i >= m_iQueueScrollOffset && i < m_iQueueScrollOffset + m_iQueueWinHeight -1)
|
||||
{
|
||||
PrintGroupname(pGroupInfo, iLineNr++, i == m_iSelectedQueueEntry);
|
||||
PrintGroupname(pGroupInfo, iCalcLineNr++, false, true);
|
||||
}
|
||||
}
|
||||
|
||||
long long lRemaining = 0;
|
||||
long long lPaused = 0;
|
||||
i = 0;
|
||||
for (GroupQueue::iterator it = pGroupQueue->begin(); it != pGroupQueue->end(); it++, i++)
|
||||
{
|
||||
GroupInfo* pGroupInfo = *it;
|
||||
|
||||
if (i >= m_iQueueScrollOffset && i < m_iQueueScrollOffset + m_iQueueWinHeight -1)
|
||||
{
|
||||
PrintGroupname(pGroupInfo, iLineNr++, i == m_iSelectedQueueEntry, false);
|
||||
}
|
||||
|
||||
lRemaining += pGroupInfo->GetRemainingSize();
|
||||
@@ -892,61 +964,151 @@ void NCursesFrontend::PrintGroupQueue()
|
||||
}
|
||||
|
||||
char szRemaining[20];
|
||||
FormatFileSize(szRemaining, sizeof(szRemaining), lRemaining);
|
||||
Util::FormatFileSize(szRemaining, sizeof(szRemaining), lRemaining);
|
||||
|
||||
char szUnpaused[20];
|
||||
FormatFileSize(szUnpaused, sizeof(szUnpaused), lRemaining - lPaused);
|
||||
Util::FormatFileSize(szUnpaused, sizeof(szUnpaused), lRemaining - lPaused);
|
||||
|
||||
char szBuffer[MAX_SCREEN_WIDTH];
|
||||
snprintf(szBuffer, sizeof(szBuffer), " %sNZBs for downloading - %i NZBs in queue - %s / %s",
|
||||
m_bUseColor ? "" : "*** ", pGroupQueue->size(), szRemaining, szUnpaused);
|
||||
m_bUseColor ? "" : "*** ", (int)pGroupQueue->size(), szRemaining, szUnpaused);
|
||||
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
|
||||
PrintTopHeader(szBuffer, m_iQueueWinTop, false);
|
||||
}
|
||||
UnlockQueue();
|
||||
}
|
||||
|
||||
void NCursesFrontend::PrintGroupname(GroupInfo * pGroupInfo, int iRow, bool bSelected)
|
||||
void NCursesFrontend::ResetColWidths()
|
||||
{
|
||||
int color = 0;
|
||||
const char* Brace1 = "[";
|
||||
const char* Brace2 = "]";
|
||||
m_iColWidthFiles = 0;
|
||||
m_iColWidthTotal = 0;
|
||||
m_iColWidthLeft = 0;
|
||||
}
|
||||
|
||||
void NCursesFrontend::PrintGroupname(GroupInfo * pGroupInfo, int iRow, bool bSelected, bool bCalcColWidth)
|
||||
{
|
||||
int color = NCURSES_COLORPAIR_TEXT;
|
||||
char chBrace1 = '[';
|
||||
char chBrace2 = ']';
|
||||
if (m_eInputMode == eEditQueue && bSelected)
|
||||
{
|
||||
color = NCURSES_COLORPAIR_TEXTHIGHL;
|
||||
if (!m_bUseColor)
|
||||
{
|
||||
Brace1 = "<";
|
||||
Brace2 = ">";
|
||||
chBrace1 = '<';
|
||||
chBrace2 = '>';
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
const char* szDownloading = "";
|
||||
if (pGroupInfo->GetActiveDownloads() > 0)
|
||||
{
|
||||
color = NCURSES_COLORPAIR_TEXT;
|
||||
szDownloading = " *";
|
||||
}
|
||||
|
||||
long long lUnpausedRemainingSize = pGroupInfo->GetRemainingSize() - pGroupInfo->GetPausedSize();
|
||||
|
||||
char szRemaining[20];
|
||||
FormatFileSize(szRemaining, sizeof(szRemaining), lUnpausedRemainingSize);
|
||||
Util::FormatFileSize(szRemaining, sizeof(szRemaining), lUnpausedRemainingSize);
|
||||
|
||||
char szPaused[20];
|
||||
szPaused[0] = '\0';
|
||||
if (pGroupInfo->GetPausedSize() > 0)
|
||||
char szPriority[100];
|
||||
szPriority[0] = '\0';
|
||||
if (pGroupInfo->GetMinPriority() != 0 || pGroupInfo->GetMaxPriority() != 0)
|
||||
{
|
||||
char szPausedSize[20];
|
||||
FormatFileSize(szPausedSize, sizeof(szPausedSize), pGroupInfo->GetPausedSize());
|
||||
sprintf(szPaused, " + %s paused", szPausedSize);
|
||||
if (pGroupInfo->GetMinPriority() == pGroupInfo->GetMaxPriority())
|
||||
{
|
||||
sprintf(szPriority, " [%+i]", pGroupInfo->GetMinPriority());
|
||||
}
|
||||
else
|
||||
{
|
||||
sprintf(szPriority, " [%+i..%+i]", pGroupInfo->GetMinPriority(), pGroupInfo->GetMaxPriority());
|
||||
}
|
||||
}
|
||||
|
||||
char szNZBNiceName[1024];
|
||||
pGroupInfo->GetNZBInfo()->GetNiceNZBName(szNZBNiceName, 1023);
|
||||
|
||||
char szBuffer[MAX_SCREEN_WIDTH];
|
||||
snprintf(szBuffer, MAX_SCREEN_WIDTH, "%s%i-%i%s %s (%i file%s, %s%s)", Brace1, pGroupInfo->GetFirstID(), pGroupInfo->GetLastID(), Brace2, szNZBNiceName,
|
||||
pGroupInfo->GetRemainingFileCount(), pGroupInfo->GetRemainingFileCount() > 1 ? "s" : "", szRemaining, szPaused);
|
||||
|
||||
// Format:
|
||||
// [id - id] Name Left-Files/Paused Total Left Time
|
||||
// [1-2] Nzb-name 999/999 999.99 MB 999.99 MB 00:00:00
|
||||
|
||||
int iNameLen = 0;
|
||||
if (bCalcColWidth)
|
||||
{
|
||||
iNameLen = m_iScreenWidth - 1 - 9 - 11 - 11 - 9;
|
||||
}
|
||||
else
|
||||
{
|
||||
iNameLen = m_iScreenWidth - 1 - m_iColWidthFiles - 2 - m_iColWidthTotal - 2 - m_iColWidthLeft - 2 - 9;
|
||||
}
|
||||
|
||||
bool bPrintFormatted = iNameLen > 20;
|
||||
|
||||
if (bPrintFormatted)
|
||||
{
|
||||
char szFiles[20];
|
||||
snprintf(szFiles, 20, "%i/%i", pGroupInfo->GetRemainingFileCount(), pGroupInfo->GetPausedFileCount());
|
||||
szFiles[20-1] = '\0';
|
||||
|
||||
char szTotal[20];
|
||||
Util::FormatFileSize(szTotal, sizeof(szTotal), pGroupInfo->GetNZBInfo()->GetSize());
|
||||
|
||||
char szNameWithIds[1024];
|
||||
snprintf(szNameWithIds, 1024, "%c%i-%i%c%s%s %s", chBrace1, pGroupInfo->GetFirstID(), pGroupInfo->GetLastID(), chBrace2,
|
||||
szPriority, szDownloading, pGroupInfo->GetNZBInfo()->GetName());
|
||||
szNameWithIds[iNameLen] = '\0';
|
||||
|
||||
char szTime[100];
|
||||
szTime[0] = '\0';
|
||||
int iCurrentDownloadSpeed = m_bStandBy ? 0 : m_iCurrentDownloadSpeed;
|
||||
if (pGroupInfo->GetPausedSize() > 0 && lUnpausedRemainingSize == 0)
|
||||
{
|
||||
snprintf(szTime, 100, "[paused]");
|
||||
Util::FormatFileSize(szRemaining, sizeof(szRemaining), pGroupInfo->GetRemainingSize());
|
||||
}
|
||||
else if (iCurrentDownloadSpeed > 0 && !(m_bPauseDownload || m_bPauseDownload2))
|
||||
{
|
||||
long long remain_sec = (long long)(lUnpausedRemainingSize / iCurrentDownloadSpeed);
|
||||
int h = (int)(remain_sec / 3600);
|
||||
int m = (int)((remain_sec % 3600) / 60);
|
||||
int s = (int)(remain_sec % 60);
|
||||
if (h < 100)
|
||||
{
|
||||
snprintf(szTime, 100, "%.2d:%.2d:%.2d", h, m, s);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(szTime, 100, "99:99:99");
|
||||
}
|
||||
}
|
||||
|
||||
if (bCalcColWidth)
|
||||
{
|
||||
int iColWidthFiles = strlen(szFiles);
|
||||
m_iColWidthFiles = iColWidthFiles > m_iColWidthFiles ? iColWidthFiles : m_iColWidthFiles;
|
||||
|
||||
int iColWidthTotal = strlen(szTotal);
|
||||
m_iColWidthTotal = iColWidthTotal > m_iColWidthTotal ? iColWidthTotal : m_iColWidthTotal;
|
||||
|
||||
int iColWidthLeft = strlen(szRemaining);
|
||||
m_iColWidthLeft = iColWidthLeft > m_iColWidthLeft ? iColWidthLeft : m_iColWidthLeft;
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(szBuffer, MAX_SCREEN_WIDTH, "%-*s %*s %*s %*s %8s", iNameLen, szNameWithIds, m_iColWidthFiles, szFiles, m_iColWidthTotal, szTotal, m_iColWidthLeft, szRemaining, szTime);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(szBuffer, MAX_SCREEN_WIDTH, "%c%i-%i%c%s %s", chBrace1, pGroupInfo->GetFirstID(),
|
||||
pGroupInfo->GetLastID(), chBrace2, szDownloading, pGroupInfo->GetNZBInfo()->GetName());
|
||||
}
|
||||
|
||||
szBuffer[MAX_SCREEN_WIDTH - 1] = '\0';
|
||||
|
||||
PlotLine(szBuffer, iRow, 0, color);
|
||||
if (!bCalcColWidth)
|
||||
{
|
||||
PlotLine(szBuffer, iRow, 0, color);
|
||||
}
|
||||
}
|
||||
|
||||
void NCursesFrontend::PrepareGroupQueue()
|
||||
@@ -954,17 +1116,13 @@ void NCursesFrontend::PrepareGroupQueue()
|
||||
m_groupQueue.clear();
|
||||
|
||||
DownloadQueue* pDownloadQueue = LockQueue();
|
||||
GroupInfo::BuildGroups(pDownloadQueue, &m_groupQueue);
|
||||
pDownloadQueue->BuildGroups(&m_groupQueue);
|
||||
UnlockQueue();
|
||||
}
|
||||
|
||||
void NCursesFrontend::ClearGroupQueue()
|
||||
{
|
||||
for (GroupQueue::iterator it = m_groupQueue.begin(); it != m_groupQueue.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_groupQueue.clear();
|
||||
m_groupQueue.Clear();
|
||||
}
|
||||
|
||||
bool NCursesFrontend::EditQueue(QueueEditor::EEditAction eAction, int iOffset)
|
||||
@@ -1013,9 +1171,9 @@ bool NCursesFrontend::EditQueue(QueueEditor::EEditAction eAction, int iOffset)
|
||||
else
|
||||
{
|
||||
DownloadQueue* pDownloadQueue = LockQueue();
|
||||
if (m_iSelectedQueueEntry >= 0 && m_iSelectedQueueEntry < (int)pDownloadQueue->size())
|
||||
if (m_iSelectedQueueEntry >= 0 && m_iSelectedQueueEntry < (int)pDownloadQueue->GetFileQueue()->size())
|
||||
{
|
||||
FileInfo* pFileInfo = (*pDownloadQueue)[m_iSelectedQueueEntry];
|
||||
FileInfo* pFileInfo = pDownloadQueue->GetFileQueue()->at(m_iSelectedQueueEntry);
|
||||
ID = pFileInfo->GetID();
|
||||
if (eAction == QueueEditor::eaFilePause)
|
||||
{
|
||||
@@ -1078,10 +1236,15 @@ void NCursesFrontend::SetCurrentQueueEntry(int iEntry)
|
||||
m_iSelectedQueueEntry = iEntry;
|
||||
}
|
||||
|
||||
void NCursesFrontend::UpdateInput()
|
||||
|
||||
/*
|
||||
* Process keystrokes starting with the initialKey, which must not be
|
||||
* READKEY_EMPTY but has alread been set via ReadConsoleKey.
|
||||
*/
|
||||
void NCursesFrontend::UpdateInput(int initialKey)
|
||||
{
|
||||
int iKey;
|
||||
while ((iKey = ReadConsoleKey()) != READKEY_EMPTY)
|
||||
int iKey = initialKey;
|
||||
while (iKey != READKEY_EMPTY)
|
||||
{
|
||||
int iQueueSize = CalcQueueSize();
|
||||
|
||||
@@ -1133,9 +1296,9 @@ void NCursesFrontend::UpdateInput()
|
||||
// Key 'p' for pause
|
||||
if (!IsRemoteMode())
|
||||
{
|
||||
info(m_bPause ? "Unpausing download" : "Pausing download");
|
||||
info(m_bPauseDownload || m_bPauseDownload2 ? "Unpausing download" : "Pausing download");
|
||||
}
|
||||
ServerPauseUnpause(!m_bPause);
|
||||
ServerPauseUnpause(!(m_bPauseDownload || m_bPauseDownload2), m_bPauseDownload2 && !m_bPauseDownload);
|
||||
break;
|
||||
case '\'':
|
||||
ServerDumpDebug();
|
||||
@@ -1227,6 +1390,9 @@ void NCursesFrontend::UpdateInput()
|
||||
EditQueue(QueueEditor::eaFilePause, 0);
|
||||
break;
|
||||
case 'd':
|
||||
SetHint(" Use Uppercase \"D\" for delete");
|
||||
break;
|
||||
case 'D':
|
||||
// Delete entry
|
||||
if (EditQueue(QueueEditor::eaFileDelete, 0))
|
||||
{
|
||||
@@ -1272,7 +1438,7 @@ void NCursesFrontend::UpdateInput()
|
||||
// Enter
|
||||
else if (iKey == 10 || iKey == 13)
|
||||
{
|
||||
ServerSetDownloadRate((float)m_iInputValue);
|
||||
ServerSetDownloadRate(m_iInputValue * 1024);
|
||||
m_eInputMode = eNormal;
|
||||
return;
|
||||
}
|
||||
@@ -1291,6 +1457,8 @@ void NCursesFrontend::UpdateInput()
|
||||
m_iInputNumberIndex--;
|
||||
}
|
||||
}
|
||||
|
||||
iKey = ReadConsoleKey();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1311,7 +1479,13 @@ int NCursesFrontend::ReadConsoleKey()
|
||||
InputRecord.EventType == KEY_EVENT &&
|
||||
InputRecord.Event.KeyEvent.bKeyDown)
|
||||
{
|
||||
return tolower(InputRecord.Event.KeyEvent.wVirtualKeyCode);
|
||||
char c = tolower(InputRecord.Event.KeyEvent.wVirtualKeyCode);
|
||||
if (bool(InputRecord.Event.KeyEvent.dwControlKeyState & CAPSLOCK_ON) ^
|
||||
bool(InputRecord.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED))
|
||||
{
|
||||
c = toupper(c);
|
||||
}
|
||||
return c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* 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-2009 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -30,6 +30,7 @@
|
||||
#ifndef DISABLE_CURSES
|
||||
|
||||
#include <vector>
|
||||
#include <time.h>
|
||||
|
||||
#include "Frontend.h"
|
||||
#include "Log.h"
|
||||
@@ -48,6 +49,7 @@ private:
|
||||
|
||||
bool m_bUseColor;
|
||||
int m_iDataUpdatePos;
|
||||
bool m_bUpdateNextTime;
|
||||
int m_iScreenHeight;
|
||||
int m_iScreenWidth;
|
||||
int m_iQueueWinTop;
|
||||
@@ -61,8 +63,13 @@ private:
|
||||
bool m_bLastPausePars;
|
||||
int m_iQueueScrollOffset;
|
||||
GroupQueue m_groupQueue;
|
||||
char* m_szHint;
|
||||
time_t m_tStartHint;
|
||||
int m_iColWidthFiles;
|
||||
int m_iColWidthTotal;
|
||||
int m_iColWidthLeft;
|
||||
|
||||
// Inputting numbres
|
||||
// Inputting numbers
|
||||
int m_iInputNumberIndex;
|
||||
int m_iInputValue;
|
||||
|
||||
@@ -91,23 +98,24 @@ private:
|
||||
void PrintFileQueue();
|
||||
void PrintFilename(FileInfo* pFileInfo, int iRow, bool bSelected);
|
||||
void PrintGroupQueue();
|
||||
void PrintGroupname(GroupInfo * pGroupInfo, int iRow, bool bSelected);
|
||||
void ResetColWidths();
|
||||
void PrintGroupname(GroupInfo * pGroupInfo, int iRow, bool bSelected, bool bCalcColWidth);
|
||||
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 UpdateInput(int initialKey);
|
||||
void Update(int iKey);
|
||||
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);
|
||||
void SetHint(const char* szHint);
|
||||
|
||||
protected:
|
||||
virtual void Run();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* 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-2013 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -34,6 +34,8 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Log.h"
|
||||
@@ -43,24 +45,22 @@
|
||||
|
||||
static const int CONNECTION_LINEBUFFER_SIZE = 1024*10;
|
||||
|
||||
NNTPConnection::NNTPConnection(NewsServer* server) : Connection(server)
|
||||
NNTPConnection::NNTPConnection(NewsServer* pNewsServer) : Connection(pNewsServer->GetHost(), pNewsServer->GetPort(), pNewsServer->GetTLS())
|
||||
{
|
||||
m_pNewsServer = pNewsServer;
|
||||
m_szActiveGroup = NULL;
|
||||
m_szLineBuf = (char*)malloc(CONNECTION_LINEBUFFER_SIZE);
|
||||
m_bAuthError = false;
|
||||
SetCipher(pNewsServer->GetCipher());
|
||||
}
|
||||
|
||||
NNTPConnection::~NNTPConnection()
|
||||
{
|
||||
if (m_szActiveGroup)
|
||||
{
|
||||
free(m_szActiveGroup);
|
||||
m_szActiveGroup = NULL;
|
||||
}
|
||||
free(m_szActiveGroup);
|
||||
free(m_szLineBuf);
|
||||
}
|
||||
|
||||
const char* NNTPConnection::Request(char* req)
|
||||
const char* NNTPConnection::Request(const char* req)
|
||||
{
|
||||
if (!req)
|
||||
{
|
||||
@@ -80,19 +80,16 @@ const char* NNTPConnection::Request(char* req)
|
||||
|
||||
if (!strncmp(answer, "480", 3))
|
||||
{
|
||||
debug("%s requested authorization", m_pNetAddress->GetHost());
|
||||
debug("%s requested authorization", 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;
|
||||
@@ -100,13 +97,16 @@ const char* NNTPConnection::Request(char* req)
|
||||
|
||||
bool NNTPConnection::Authenticate()
|
||||
{
|
||||
if (!((NewsServer*)m_pNetAddress)->GetUser() ||
|
||||
!((NewsServer*)m_pNetAddress)->GetPassword())
|
||||
if (strlen(m_pNewsServer->GetUser()) == 0 || strlen(m_pNewsServer->GetPassword()) == 0)
|
||||
{
|
||||
error("%c%s (%s) requested authorization but username/password are not set in settings",
|
||||
toupper(m_pNewsServer->GetName()[0]), m_pNewsServer->GetName() + 1, m_pNewsServer->GetHost());
|
||||
m_bAuthError = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return AuthInfoUser();
|
||||
m_bAuthError = !AuthInfoUser(0);
|
||||
return !m_bAuthError;
|
||||
}
|
||||
|
||||
bool NNTPConnection::AuthInfoUser(int iRecur)
|
||||
@@ -117,7 +117,7 @@ bool NNTPConnection::AuthInfoUser(int iRecur)
|
||||
}
|
||||
|
||||
char tmp[1024];
|
||||
snprintf(tmp, 1024, "AUTHINFO USER %s\r\n", ((NewsServer*)m_pNetAddress)->GetUser());
|
||||
snprintf(tmp, 1024, "AUTHINFO USER %s\r\n", m_pNewsServer->GetUser());
|
||||
tmp[1024-1] = '\0';
|
||||
|
||||
WriteLine(tmp);
|
||||
@@ -125,13 +125,13 @@ bool NNTPConnection::AuthInfoUser(int iRecur)
|
||||
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);
|
||||
ReportErrorAnswer("Authorization for server%i (%s) failed: Connection closed by remote host", NULL);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!strncmp(answer, "281", 3))
|
||||
{
|
||||
debug("authorization for %s successful", m_pNetAddress->GetHost());
|
||||
debug("Authorization for %s successful", GetHost());
|
||||
return true;
|
||||
}
|
||||
else if (!strncmp(answer, "381", 3))
|
||||
@@ -147,7 +147,7 @@ bool NNTPConnection::AuthInfoUser(int iRecur)
|
||||
|
||||
if (GetStatus() != csCancelled)
|
||||
{
|
||||
error("authorization for %s failed (Answer: %s)", m_pNetAddress->GetHost(), answer);
|
||||
ReportErrorAnswer("Authorization for server%i (%s) failed (Answer: %s)", answer);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -160,7 +160,7 @@ bool NNTPConnection::AuthInfoPass(int iRecur)
|
||||
}
|
||||
|
||||
char tmp[1024];
|
||||
snprintf(tmp, 1024, "AUTHINFO PASS %s\r\n", ((NewsServer*)m_pNetAddress)->GetPassword());
|
||||
snprintf(tmp, 1024, "AUTHINFO PASS %s\r\n", m_pNewsServer->GetPassword());
|
||||
tmp[1024-1] = '\0';
|
||||
|
||||
WriteLine(tmp);
|
||||
@@ -168,12 +168,12 @@ bool NNTPConnection::AuthInfoPass(int iRecur)
|
||||
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);
|
||||
ReportErrorAnswer("Authorization for server%i (%s) failed: Connection closed by remote host", NULL);
|
||||
return false;
|
||||
}
|
||||
else if (!strncmp(answer, "2", 1))
|
||||
{
|
||||
debug("authorization for %s successful", m_pNetAddress->GetHost());
|
||||
debug("Authorization for %s successful", GetHost());
|
||||
return true;
|
||||
}
|
||||
else if (!strncmp(answer, "381", 3))
|
||||
@@ -185,7 +185,7 @@ bool NNTPConnection::AuthInfoPass(int iRecur)
|
||||
|
||||
if (GetStatus() != csCancelled)
|
||||
{
|
||||
error("authorization for %s failed (Answer: %s)", m_pNetAddress->GetHost(), answer);
|
||||
ReportErrorAnswer("Authorization for server%i (%s) failed (Answer: %s)", answer);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@@ -204,67 +204,78 @@ const char* NNTPConnection::JoinGroup(const char* grp)
|
||||
tmp[1024-1] = '\0';
|
||||
|
||||
const char* answer = Request(tmp);
|
||||
if (m_bAuthError)
|
||||
{
|
||||
return answer;
|
||||
}
|
||||
|
||||
if (answer && !strncmp(answer, "2", 1))
|
||||
{
|
||||
debug("Changed group to %s on %s", grp, GetServer()->GetHost());
|
||||
|
||||
if (m_szActiveGroup)
|
||||
{
|
||||
free(m_szActiveGroup);
|
||||
}
|
||||
debug("Changed group to %s on %s", grp, GetHost());
|
||||
free(m_szActiveGroup);
|
||||
m_szActiveGroup = strdup(grp);
|
||||
}
|
||||
else
|
||||
{
|
||||
debug("Error changing group on %s to %s: %s.",
|
||||
GetServer()->GetHost(), grp, answer);
|
||||
debug("Error changing group on %s to %s: %s.", GetHost(), grp, answer);
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
int NNTPConnection::DoConnect()
|
||||
bool NNTPConnection::Connect()
|
||||
{
|
||||
debug("Opening connection to %s", GetServer()->GetHost());
|
||||
int res = Connection::DoConnect();
|
||||
if (res < 0)
|
||||
debug("Opening connection to %s", GetHost());
|
||||
|
||||
if (m_eStatus == csConnected)
|
||||
{
|
||||
return res;
|
||||
return true;
|
||||
}
|
||||
|
||||
char* answer = DoReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
|
||||
if (!Connection::Connect())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
|
||||
|
||||
if (!answer)
|
||||
{
|
||||
ReportError("Connection to %s failed: Connection closed by remote host.", m_pNetAddress->GetHost(), 0);
|
||||
return -1;
|
||||
ReportErrorAnswer("Connection to server%i (%s) failed: Connection closed by remote host", NULL);
|
||||
Disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strncmp(answer, "2", 1))
|
||||
{
|
||||
error("Connection to %s failed. Answer: ", m_pNetAddress->GetHost(), answer);
|
||||
return -1;
|
||||
ReportErrorAnswer("Connection to server%i (%s) failed (Answer: %s)", answer);
|
||||
Disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
debug("Connection to %s established", GetServer()->GetHost());
|
||||
return 0;
|
||||
if ((strlen(m_pNewsServer->GetUser()) > 0 && strlen(m_pNewsServer->GetPassword()) > 0) &&
|
||||
!Authenticate())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
debug("Connection to %s established", GetHost());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int NNTPConnection::DoDisconnect()
|
||||
bool NNTPConnection::Disconnect()
|
||||
{
|
||||
if (m_eStatus == csConnected)
|
||||
{
|
||||
Request("quit\r\n");
|
||||
if (m_szActiveGroup)
|
||||
{
|
||||
free(m_szActiveGroup);
|
||||
m_szActiveGroup = NULL;
|
||||
}
|
||||
free(m_szActiveGroup);
|
||||
m_szActiveGroup = NULL;
|
||||
}
|
||||
return Connection::DoDisconnect();
|
||||
return Connection::Disconnect();
|
||||
}
|
||||
|
||||
void NNTPConnection::ReportErrorAnswer(const char* szMsgPrefix, const char* szAnswer)
|
||||
{
|
||||
char szErrStr[1024];
|
||||
snprintf(szErrStr, 1024, szMsgPrefix, m_pNewsServer->GetID(), m_pNewsServer->GetHost(), szAnswer);
|
||||
szErrStr[1024-1] = '\0';
|
||||
|
||||
ReportError(szErrStr, NULL, false, 0);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* 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-2008 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -33,26 +33,27 @@
|
||||
class NNTPConnection : public Connection
|
||||
{
|
||||
private:
|
||||
char* m_szActiveGroup;
|
||||
char* m_szLineBuf;
|
||||
bool m_bAuthError;
|
||||
NewsServer* m_pNewsServer;
|
||||
char* m_szActiveGroup;
|
||||
char* m_szLineBuf;
|
||||
bool m_bAuthError;
|
||||
|
||||
virtual int DoConnect();
|
||||
virtual int DoDisconnect();
|
||||
void Clear();
|
||||
void Clear();
|
||||
void ReportErrorAnswer(const char* szMsgPrefix, const char* szAnswer);
|
||||
bool Authenticate();
|
||||
bool AuthInfoUser(int iRecur);
|
||||
bool AuthInfoPass(int iRecur);
|
||||
|
||||
public:
|
||||
NNTPConnection(NewsServer* server);
|
||||
~NNTPConnection();
|
||||
NewsServer* GetNewsServer() { return(NewsServer*)m_pNetAddress; }
|
||||
const char* Request(char* req);
|
||||
bool Authenticate();
|
||||
bool AuthInfoUser(int iRecur = 0);
|
||||
bool AuthInfoPass(int iRecur = 0);
|
||||
const char* JoinGroup(const char* grp);
|
||||
bool GetAuthError() { return m_bAuthError; }
|
||||
NNTPConnection(NewsServer* pNewsServer);
|
||||
virtual ~NNTPConnection();
|
||||
virtual bool Connect();
|
||||
virtual bool Disconnect();
|
||||
NewsServer* GetNewsServer() { return m_pNewsServer; }
|
||||
const char* Request(const char* req);
|
||||
const char* JoinGroup(const char* grp);
|
||||
bool GetAuthError() { return m_bAuthError; }
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007 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,7 +15,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007 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,7 +15,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
|
||||
787
NZBFile.cpp
787
NZBFile.cpp
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* 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-2013 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -34,13 +34,16 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <list>
|
||||
#include <ctype.h>
|
||||
#ifdef WIN32
|
||||
#include <comutil.h>
|
||||
#import "MSXML.dll" named_guids
|
||||
#import <msxml.tlb> named_guids
|
||||
using namespace MSXML;
|
||||
#else
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/xmlreader.h>
|
||||
#include <libxml/xmlerror.h>
|
||||
#include <libxml/entities.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
@@ -54,15 +57,25 @@ using namespace MSXML;
|
||||
extern Options* g_pOptions;
|
||||
extern DiskState* g_pDiskState;
|
||||
|
||||
NZBFile::NZBFile(const char* szFileName)
|
||||
NZBFile::NZBFile(const char* szFileName, const char* szCategory)
|
||||
{
|
||||
debug("Creating NZBFile");
|
||||
|
||||
m_szFileName = strdup(szFileName);
|
||||
m_szPassword = NULL;
|
||||
m_pNZBInfo = new NZBInfo();
|
||||
m_pNZBInfo->AddReference();
|
||||
m_pNZBInfo->Retain();
|
||||
m_pNZBInfo->SetFilename(szFileName);
|
||||
BuildDestDirName();
|
||||
m_pNZBInfo->SetCategory(szCategory);
|
||||
m_pNZBInfo->BuildDestDirName();
|
||||
|
||||
#ifndef WIN32
|
||||
m_bPassword = false;
|
||||
m_pFileInfo = NULL;
|
||||
m_pArticle = NULL;
|
||||
m_szTagContent = NULL;
|
||||
m_iTagContentLen = 0;
|
||||
#endif
|
||||
|
||||
m_FileInfos.clear();
|
||||
}
|
||||
@@ -72,10 +85,8 @@ NZBFile::~NZBFile()
|
||||
debug("Destroying NZBFile");
|
||||
|
||||
// Cleanup
|
||||
if (m_szFileName)
|
||||
{
|
||||
free(m_szFileName);
|
||||
}
|
||||
free(m_szFileName);
|
||||
free(m_szPassword);
|
||||
|
||||
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
|
||||
{
|
||||
@@ -87,6 +98,11 @@ NZBFile::~NZBFile()
|
||||
{
|
||||
m_pNZBInfo->Release();
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
delete m_pFileInfo;
|
||||
free(m_szTagContent);
|
||||
#endif
|
||||
}
|
||||
|
||||
void NZBFile::LogDebugInfo()
|
||||
@@ -99,66 +115,106 @@ 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;
|
||||
int index = pArticleInfo->GetPartNumber() - 1;
|
||||
if ((*pFileInfo->GetArticles())[index])
|
||||
{
|
||||
delete (*pFileInfo->GetArticles())[index];
|
||||
}
|
||||
(*pFileInfo->GetArticles())[index] = pArticleInfo;
|
||||
}
|
||||
|
||||
void NZBFile::AddFileInfo(FileInfo* pFileInfo)
|
||||
{
|
||||
// deleting empty articles
|
||||
// calculate file size and delete empty articles
|
||||
|
||||
long long lSize = 0;
|
||||
long long lMissedSize = 0;
|
||||
long long lOneSize = 0;
|
||||
int iUncountedArticles = 0;
|
||||
int iMissedArticles = 0;
|
||||
FileInfo::Articles* pArticles = pFileInfo->GetArticles();
|
||||
int iTotalArticles = (int)pArticles->size();
|
||||
int i = 0;
|
||||
for (FileInfo::Articles::iterator it = pArticles->begin(); it != pArticles->end();)
|
||||
for (FileInfo::Articles::iterator it = pArticles->begin(); it != pArticles->end(); )
|
||||
{
|
||||
if (*it == NULL)
|
||||
ArticleInfo* pArticle = *it;
|
||||
if (!pArticle)
|
||||
{
|
||||
pArticles->erase(it);
|
||||
it = pArticles->begin() + i;
|
||||
iMissedArticles++;
|
||||
if (lOneSize > 0)
|
||||
{
|
||||
lMissedSize += lOneSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
iUncountedArticles++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
lSize += pArticle->GetSize();
|
||||
if (lOneSize == 0)
|
||||
{
|
||||
lOneSize = pArticle->GetSize();
|
||||
}
|
||||
it++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pArticles->empty())
|
||||
if (pArticles->empty())
|
||||
{
|
||||
ParseSubject(pFileInfo);
|
||||
m_FileInfos.push_back(pFileInfo);
|
||||
pFileInfo->SetNZBInfo(m_pNZBInfo);
|
||||
m_pNZBInfo->SetSize(m_pNZBInfo->GetSize() + pFileInfo->GetSize());
|
||||
m_pNZBInfo->SetFileCount(m_pNZBInfo->GetFileCount() + 1);
|
||||
delete pFileInfo;
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->SaveFile(pFileInfo);
|
||||
pFileInfo->ClearArticles();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
delete pFileInfo;
|
||||
}
|
||||
lMissedSize += iUncountedArticles * lOneSize;
|
||||
lSize += lMissedSize;
|
||||
m_FileInfos.push_back(pFileInfo);
|
||||
pFileInfo->SetNZBInfo(m_pNZBInfo);
|
||||
pFileInfo->SetSize(lSize);
|
||||
pFileInfo->SetRemainingSize(lSize - lMissedSize);
|
||||
pFileInfo->SetMissedSize(lMissedSize);
|
||||
pFileInfo->SetTotalArticles(iTotalArticles);
|
||||
pFileInfo->SetMissedArticles(iMissedArticles);
|
||||
}
|
||||
|
||||
void NZBFile::ParseSubject(FileInfo* pFileInfo)
|
||||
void NZBFile::ParseSubject(FileInfo* pFileInfo, bool TryQuotes)
|
||||
{
|
||||
if (TryQuotes)
|
||||
{
|
||||
// try to use the filename in quatation marks
|
||||
char* p = (char*)pFileInfo->GetSubject();
|
||||
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)
|
||||
{
|
||||
char* filename = (char*)malloc(len + 1);
|
||||
strncpy(filename, start, len);
|
||||
filename[len] = '\0';
|
||||
pFileInfo->SetFilename(filename);
|
||||
free(filename);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// tokenize subject, considering spaces as separators and quotation
|
||||
// marks as non separatable token delimiters.
|
||||
// then take the last token containing dot (".") as a filename
|
||||
@@ -241,39 +297,14 @@ void NZBFile::ParseSubject(FileInfo* pFileInfo)
|
||||
debug("Could not extract Filename from Subject: %s. Using Subject as Filename", pFileInfo->GetSubject());
|
||||
pFileInfo->SetFilename(pFileInfo->GetSubject());
|
||||
}
|
||||
|
||||
pFileInfo->MakeValidFilename();
|
||||
}
|
||||
|
||||
void NZBFile::BuildDestDirName()
|
||||
bool NZBFile::HasDuplicateFilenames()
|
||||
{
|
||||
char szBuffer[1024];
|
||||
|
||||
if (g_pOptions->GetAppendNZBDir())
|
||||
{
|
||||
char szNiceNZBName[1024];
|
||||
m_pNZBInfo->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()
|
||||
}
|
||||
|
||||
m_pNZBInfo->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++)
|
||||
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo1 = *it;
|
||||
int iDupe = 0;
|
||||
int iDupe = 1;
|
||||
for (FileInfos::iterator it2 = it + 1; it2 != m_FileInfos.end(); it2++)
|
||||
{
|
||||
FileInfo* pFileInfo2 = *it2;
|
||||
@@ -292,25 +323,218 @@ void NZBFile::CheckFilenames()
|
||||
// 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();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->LoadArticles(pFileInfo2);
|
||||
g_pDiskState->SaveFile(pFileInfo2);
|
||||
pFileInfo2->ClearArticles();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate filenames from subjects and check if the parsing of subject was correct
|
||||
*/
|
||||
void NZBFile::BuildFilenames()
|
||||
{
|
||||
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
ParseSubject(pFileInfo, true);
|
||||
}
|
||||
|
||||
if (HasDuplicateFilenames())
|
||||
{
|
||||
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
ParseSubject(pFileInfo, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (HasDuplicateFilenames())
|
||||
{
|
||||
m_pNZBInfo->SetManyDupeFiles(true);
|
||||
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
pFileInfo->SetFilename(pFileInfo->GetSubject());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CompareFileInfo(FileInfo* pFirst, FileInfo* pSecond)
|
||||
{
|
||||
return strcmp(pFirst->GetFilename(), pSecond->GetFilename()) > 0;
|
||||
}
|
||||
|
||||
void NZBFile::CalcHashes()
|
||||
{
|
||||
FileInfoList fileList;
|
||||
|
||||
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
|
||||
{
|
||||
fileList.push_back(*it);
|
||||
}
|
||||
|
||||
fileList.sort(CompareFileInfo);
|
||||
|
||||
// split ExtCleanupDisk into tokens and create a list
|
||||
ExtList extList;
|
||||
char* szExtCleanupDisk = strdup(g_pOptions->GetExtCleanupDisk());
|
||||
char* saveptr;
|
||||
char* szExt = strtok_r(szExtCleanupDisk, ",; ", &saveptr);
|
||||
while (szExt)
|
||||
{
|
||||
extList.push_back(szExt);
|
||||
szExt = strtok_r(NULL, ",; ", &saveptr);
|
||||
}
|
||||
|
||||
unsigned int iFullContentHash = 0;
|
||||
unsigned int iFilteredContentHash = 0;
|
||||
int iUseForFilteredCount = 0;
|
||||
|
||||
for (FileInfoList::iterator it = fileList.begin(); it != fileList.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
|
||||
// check file extension
|
||||
int iFilenameLen = strlen(pFileInfo->GetFilename());
|
||||
bool bSkip = false;
|
||||
for (ExtList::iterator it = extList.begin(); it != extList.end(); it++)
|
||||
{
|
||||
const char* szExt = *it;
|
||||
int iExtLen = strlen(szExt);
|
||||
if (iFilenameLen >= iExtLen && !strcasecmp(szExt, pFileInfo->GetFilename() + iFilenameLen - iExtLen))
|
||||
{
|
||||
bSkip = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bSkip = bSkip && !pFileInfo->GetParFile();
|
||||
|
||||
for (FileInfo::Articles::iterator it = pFileInfo->GetArticles()->begin(); it != pFileInfo->GetArticles()->end(); it++)
|
||||
{
|
||||
ArticleInfo* pArticle = *it;
|
||||
int iLen = strlen(pArticle->GetMessageID());
|
||||
iFullContentHash = Util::HashBJ96(pArticle->GetMessageID(), iLen, iFullContentHash);
|
||||
if (!bSkip)
|
||||
{
|
||||
iFilteredContentHash = Util::HashBJ96(pArticle->GetMessageID(), iLen, iFilteredContentHash);
|
||||
iUseForFilteredCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(szExtCleanupDisk);
|
||||
|
||||
// if filtered hash is based on less than a half of files - do not use filtered hash at all
|
||||
if (iUseForFilteredCount < (int)fileList.size() / 2)
|
||||
{
|
||||
iFilteredContentHash = 0;
|
||||
}
|
||||
|
||||
m_pNZBInfo->SetFullContentHash(iFullContentHash);
|
||||
m_pNZBInfo->SetFilteredContentHash(iFilteredContentHash);
|
||||
}
|
||||
|
||||
void NZBFile::ProcessFiles()
|
||||
{
|
||||
BuildFilenames();
|
||||
|
||||
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
pFileInfo->MakeValidFilename();
|
||||
|
||||
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
|
||||
bool bParFile = strstr(szLoFileName, ".par2");
|
||||
|
||||
m_pNZBInfo->SetFileCount(m_pNZBInfo->GetFileCount() + 1);
|
||||
m_pNZBInfo->SetTotalArticles(m_pNZBInfo->GetTotalArticles() + pFileInfo->GetTotalArticles());
|
||||
m_pNZBInfo->SetSize(m_pNZBInfo->GetSize() + pFileInfo->GetSize());
|
||||
m_pNZBInfo->SetFailedSize(m_pNZBInfo->GetFailedSize() + pFileInfo->GetMissedSize());
|
||||
m_pNZBInfo->SetCurrentFailedSize(m_pNZBInfo->GetFailedSize());
|
||||
|
||||
pFileInfo->SetParFile(bParFile);
|
||||
if (bParFile)
|
||||
{
|
||||
m_pNZBInfo->SetParSize(m_pNZBInfo->GetParSize() + pFileInfo->GetSize());
|
||||
m_pNZBInfo->SetParFailedSize(m_pNZBInfo->GetParFailedSize() + pFileInfo->GetMissedSize());
|
||||
m_pNZBInfo->SetParCurrentFailedSize(m_pNZBInfo->GetParFailedSize());
|
||||
}
|
||||
}
|
||||
|
||||
CalcHashes();
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
g_pDiskState->SaveFile(pFileInfo);
|
||||
pFileInfo->ClearArticles();
|
||||
}
|
||||
}
|
||||
|
||||
if (m_szPassword)
|
||||
{
|
||||
ReadPassword();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Password read using XML-parser may have special characters (such as TAB) stripped.
|
||||
* This function rereads password directly from file to keep all characters intact.
|
||||
*/
|
||||
void NZBFile::ReadPassword()
|
||||
{
|
||||
FILE* pFile = fopen(m_szFileName, "rb");
|
||||
if (!pFile)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// obtain file size.
|
||||
fseek(pFile , 0 , SEEK_END);
|
||||
int iSize = ftell(pFile);
|
||||
rewind(pFile);
|
||||
|
||||
// reading first 4KB of the file
|
||||
|
||||
// allocate memory to contain the whole file.
|
||||
char* buf = (char*)malloc(4096);
|
||||
|
||||
iSize = iSize < 4096 ? iSize : 4096;
|
||||
|
||||
// copy the file into the buffer.
|
||||
fread(buf, 1, iSize, pFile);
|
||||
|
||||
fclose(pFile);
|
||||
|
||||
buf[iSize-1] = '\0';
|
||||
|
||||
char* szMetaPassword = strstr(buf, "<meta type=\"password\">");
|
||||
if (szMetaPassword)
|
||||
{
|
||||
szMetaPassword += 22; // length of '<meta type="password">'
|
||||
char* szEnd = strstr(szMetaPassword, "</meta>");
|
||||
if (szEnd)
|
||||
{
|
||||
*szEnd = '\0';
|
||||
WebUtil::XmlDecode(szMetaPassword);
|
||||
free(m_szPassword);
|
||||
m_szPassword = strdup(szMetaPassword);
|
||||
}
|
||||
}
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize, bool bFromBuffer)
|
||||
NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory)
|
||||
{
|
||||
CoInitialize(NULL);
|
||||
|
||||
@@ -327,21 +551,15 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize
|
||||
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);
|
||||
}
|
||||
|
||||
// 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);
|
||||
|
||||
VARIANT_BOOL success = doc->load(v);
|
||||
if (success == VARIANT_FALSE)
|
||||
{
|
||||
_bstr_t r(doc->GetparseError()->reason);
|
||||
@@ -350,10 +568,10 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NZBFile* pFile = new NZBFile(szFileName);
|
||||
NZBFile* pFile = new NZBFile(szFileName, szCategory);
|
||||
if (pFile->ParseNZB(doc))
|
||||
{
|
||||
pFile->CheckFilenames();
|
||||
pFile->ProcessFiles();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -377,7 +595,7 @@ void NZBFile::EncodeURL(const char* szFilename, char* szURL)
|
||||
else
|
||||
{
|
||||
*szURL++ = '%';
|
||||
int a = ch >> 4;
|
||||
int a = (unsigned char)ch >> 4;
|
||||
*szURL++ = a > 9 ? a - 10 + 'a' : a + '0';
|
||||
a = ch & 0xF;
|
||||
*szURL++ = a > 9 ? a - 10 + 'a' : a + '0';
|
||||
@@ -391,16 +609,30 @@ bool NZBFile::ParseNZB(IUnknown* nzb)
|
||||
MSXML::IXMLDOMDocumentPtr doc = nzb;
|
||||
MSXML::IXMLDOMNodePtr root = doc->documentElement;
|
||||
|
||||
MSXML::IXMLDOMNodePtr node = root->selectSingleNode("/nzb/head/meta[@type='password']");
|
||||
if (node)
|
||||
{
|
||||
_bstr_t password(node->Gettext());
|
||||
m_szPassword = strdup(password);
|
||||
}
|
||||
|
||||
MSXML::IXMLDOMNodeListPtr fileList = root->selectNodes("/nzb/file");
|
||||
for (int i = 0; i < fileList->Getlength(); i++)
|
||||
{
|
||||
MSXML::IXMLDOMNodePtr node = fileList->Getitem(i);
|
||||
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->SetSubject(subject);
|
||||
|
||||
attribute = node->Getattributes()->getNamedItem("date");
|
||||
if (attribute)
|
||||
{
|
||||
_bstr_t date(attribute->Gettext());
|
||||
pFileInfo->SetTime(atoi(date));
|
||||
}
|
||||
|
||||
MSXML::IXMLDOMNodeListPtr groupList = node->selectNodes("groups/group");
|
||||
for (int g = 0; g < groupList->Getlength(); g++)
|
||||
{
|
||||
@@ -428,16 +660,14 @@ bool NZBFile::ParseNZB(IUnknown* nzb)
|
||||
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);
|
||||
}
|
||||
if (partNumber > 0)
|
||||
{
|
||||
ArticleInfo* pArticle = new ArticleInfo();
|
||||
pArticle->SetPartNumber(partNumber);
|
||||
pArticle->SetMessageID(szId);
|
||||
pArticle->SetSize(lsize);
|
||||
AddArticle(pFileInfo, pArticle);
|
||||
}
|
||||
}
|
||||
|
||||
AddFileInfo(pFileInfo);
|
||||
@@ -447,146 +677,233 @@ bool NZBFile::ParseNZB(IUnknown* nzb)
|
||||
|
||||
#else
|
||||
|
||||
NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize, bool bFromBuffer)
|
||||
NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory)
|
||||
{
|
||||
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, szCategory);
|
||||
|
||||
NZBFile* pFile = new NZBFile(szFileName);
|
||||
if (pFile->ParseNZB(doc))
|
||||
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);
|
||||
|
||||
pFile->m_bIgnoreNextError = false;
|
||||
|
||||
int ret = xmlSAXUserParseFile(&SAX_handler, pFile, szFileName);
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
pFile->CheckFilenames();
|
||||
pFile->ProcessFiles();
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Failed to parse nzb-file");
|
||||
delete pFile;
|
||||
pFile = NULL;
|
||||
}
|
||||
|
||||
xmlFreeTextReader(doc);
|
||||
|
||||
return pFile;
|
||||
|
||||
return pFile;
|
||||
}
|
||||
|
||||
bool NZBFile::ParseNZB(void* nzb)
|
||||
void NZBFile::Parse_StartElement(const char *name, const char **atts)
|
||||
{
|
||||
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;
|
||||
if (m_szTagContent)
|
||||
{
|
||||
free(m_szTagContent);
|
||||
m_szTagContent = NULL;
|
||||
m_iTagContentLen = 0;
|
||||
}
|
||||
|
||||
if (!strcmp("file", name))
|
||||
{
|
||||
m_pFileInfo = new FileInfo();
|
||||
m_pFileInfo->SetFilename(m_szFileName);
|
||||
|
||||
name = xmlTextReaderName(node);
|
||||
if (name == NULL)
|
||||
{
|
||||
name = xmlStrdup(BAD_CAST "--");
|
||||
}
|
||||
value = xmlTextReaderValue(node);
|
||||
for (int i = 0; atts[i]; i += 2)
|
||||
{
|
||||
const char* attrname = atts[i];
|
||||
const char* attrvalue = atts[i + 1];
|
||||
if (!strcmp("subject", attrname))
|
||||
{
|
||||
m_pFileInfo->SetSubject(attrvalue);
|
||||
}
|
||||
if (!strcmp("date", attrname))
|
||||
{
|
||||
m_pFileInfo->SetTime(atoi(attrvalue));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!strcmp("segment", name))
|
||||
{
|
||||
if (!m_pFileInfo)
|
||||
{
|
||||
// error: bad nzb-file
|
||||
return;
|
||||
}
|
||||
|
||||
long long lsize = -1;
|
||||
int partNumber = -1;
|
||||
|
||||
if (xmlTextReaderNodeType(node) == 1)
|
||||
{
|
||||
if (!strcmp("file", (char*)name))
|
||||
{
|
||||
pFileInfo = new FileInfo();
|
||||
pFileInfo->SetFilename(m_szFileName);
|
||||
for (int i = 0; atts[i]; i += 2)
|
||||
{
|
||||
const char* attrname = atts[i];
|
||||
const char* attrvalue = atts[i + 1];
|
||||
if (!strcmp("bytes", attrname))
|
||||
{
|
||||
lsize = atol(attrvalue);
|
||||
}
|
||||
if (!strcmp("number", attrname))
|
||||
{
|
||||
partNumber = atol(attrvalue);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
if (partNumber > 0)
|
||||
{
|
||||
// new segment, add it!
|
||||
m_pArticle = new ArticleInfo();
|
||||
m_pArticle->SetPartNumber(partNumber);
|
||||
m_pArticle->SetSize(lsize);
|
||||
AddArticle(m_pFileInfo, m_pArticle);
|
||||
}
|
||||
}
|
||||
else if (!strcmp("meta", name))
|
||||
{
|
||||
m_bPassword = atts[0] && atts[1] && !strcmp("type", atts[0]) && !strcmp("password", atts[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);
|
||||
void NZBFile::Parse_EndElement(const char *name)
|
||||
{
|
||||
if (!strcmp("file", name))
|
||||
{
|
||||
// Close the file element, add the new file to file-list
|
||||
AddFileInfo(m_pFileInfo);
|
||||
m_pFileInfo = NULL;
|
||||
m_pArticle = NULL;
|
||||
}
|
||||
else if (!strcmp("group", name))
|
||||
{
|
||||
if (!m_pFileInfo)
|
||||
{
|
||||
// error: bad nzb-file
|
||||
return;
|
||||
}
|
||||
|
||||
m_pFileInfo->GetGroups()->push_back(m_szTagContent);
|
||||
m_szTagContent = NULL;
|
||||
m_iTagContentLen = 0;
|
||||
}
|
||||
else if (!strcmp("segment", name))
|
||||
{
|
||||
if (!m_pFileInfo || !m_pArticle)
|
||||
{
|
||||
// error: bad nzb-file
|
||||
return;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
// Get the #text part
|
||||
char ID[2048];
|
||||
snprintf(ID, 2048, "<%s>", m_szTagContent);
|
||||
m_pArticle->SetMessageID(ID);
|
||||
m_pArticle = NULL;
|
||||
}
|
||||
else if (!strcmp("meta", name) && m_bPassword)
|
||||
{
|
||||
m_szPassword = strdup(m_szTagContent);
|
||||
}
|
||||
}
|
||||
|
||||
if (xmlTextReaderNodeType(node) == 15)
|
||||
{
|
||||
/* Close the file element, add the new file to file-list */
|
||||
if (!strcmp("file",(char*)name))
|
||||
{
|
||||
AddFileInfo(pFileInfo);
|
||||
}
|
||||
}
|
||||
void NZBFile::Parse_Content(const char *buf, int len)
|
||||
{
|
||||
m_szTagContent = (char*)realloc(m_szTagContent, m_iTagContentLen + len + 1);
|
||||
strncpy(m_szTagContent + m_iTagContentLen, buf, len);
|
||||
m_iTagContentLen += len;
|
||||
m_szTagContent[m_iTagContentLen] = '\0';
|
||||
}
|
||||
|
||||
xmlFree(name);
|
||||
xmlFree(value);
|
||||
}
|
||||
ret = xmlTextReaderRead(node);
|
||||
}
|
||||
if (ret != 0)
|
||||
{
|
||||
error("Failed to parse nzb-file\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
void NZBFile::SAX_StartElement(NZBFile* pFile, const char *name, const char **atts)
|
||||
{
|
||||
pFile->Parse_StartElement(name, atts);
|
||||
}
|
||||
|
||||
void NZBFile::SAX_EndElement(NZBFile* pFile, const char *name)
|
||||
{
|
||||
pFile->Parse_EndElement(name);
|
||||
}
|
||||
|
||||
void NZBFile::SAX_characters(NZBFile* pFile, 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
|
||||
pFile->Parse_Content(str + off, newlen);
|
||||
}
|
||||
}
|
||||
|
||||
void* NZBFile::SAX_getEntity(NZBFile* pFile, const char * name)
|
||||
{
|
||||
xmlEntityPtr e = xmlGetPredefinedEntity((xmlChar* )name);
|
||||
if (!e)
|
||||
{
|
||||
warn("entity not found");
|
||||
pFile->m_bIgnoreNextError = true;
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
void NZBFile::SAX_error(NZBFile* pFile, const char *msg, ...)
|
||||
{
|
||||
if (pFile->m_bIgnoreNextError)
|
||||
{
|
||||
pFile->m_bIgnoreNextError = false;
|
||||
return;
|
||||
}
|
||||
|
||||
va_list argp;
|
||||
va_start(argp, msg);
|
||||
char szErrMsg[1024];
|
||||
vsnprintf(szErrMsg, sizeof(szErrMsg), msg, argp);
|
||||
szErrMsg[1024-1] = '\0';
|
||||
va_end(argp);
|
||||
|
||||
// remove trailing CRLF
|
||||
for (char* pend = szErrMsg + strlen(szErrMsg) - 1; pend >= szErrMsg && (*pend == '\n' || *pend == '\r' || *pend == ' '); pend--) *pend = '\0';
|
||||
error("Error parsing nzb-file: %s", szErrMsg);
|
||||
}
|
||||
#endif
|
||||
|
||||
43
NZBFile.h
43
NZBFile.h
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* 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-2013 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -28,6 +28,7 @@
|
||||
#define NZBFILE_H
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
@@ -35,32 +36,52 @@ class NZBFile
|
||||
{
|
||||
public:
|
||||
typedef std::vector<FileInfo*> FileInfos;
|
||||
typedef std::list<FileInfo*> FileInfoList;
|
||||
typedef std::list<char*> ExtList;
|
||||
|
||||
private:
|
||||
FileInfos m_FileInfos;
|
||||
NZBInfo* m_pNZBInfo;
|
||||
char* m_szFileName;
|
||||
char* m_szPassword;
|
||||
|
||||
NZBFile(const char* szFileName);
|
||||
NZBFile(const char* szFileName, const char* szCategory);
|
||||
void AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo);
|
||||
void AddFileInfo(FileInfo* pFileInfo);
|
||||
void ParseSubject(FileInfo* pFileInfo);
|
||||
void BuildDestDirName();
|
||||
void CheckFilenames();
|
||||
void ParseSubject(FileInfo* pFileInfo, bool TryQuotes);
|
||||
void BuildFilenames();
|
||||
void ProcessFiles();
|
||||
void CalcHashes();
|
||||
bool HasDuplicateFilenames();
|
||||
void ReadPassword();
|
||||
#ifdef WIN32
|
||||
bool ParseNZB(IUnknown* nzb);
|
||||
static void EncodeURL(const char* szFilename, char* szURL);
|
||||
#else
|
||||
bool ParseNZB(void* nzb);
|
||||
FileInfo* m_pFileInfo;
|
||||
ArticleInfo* m_pArticle;
|
||||
char* m_szTagContent;
|
||||
int m_iTagContentLen;
|
||||
bool m_bIgnoreNextError;
|
||||
bool m_bPassword;
|
||||
|
||||
static void SAX_StartElement(NZBFile* pFile, const char *name, const char **atts);
|
||||
static void SAX_EndElement(NZBFile* pFile, const char *name);
|
||||
static void SAX_characters(NZBFile* pFile, const char * xmlstr, int len);
|
||||
static void* SAX_getEntity(NZBFile* pFile, const char * name);
|
||||
static void SAX_error(NZBFile* pFile, 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);
|
||||
#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);
|
||||
static NZBFile* Create(const char* szFileName, const char* szCategory);
|
||||
const char* GetFileName() const { return m_szFileName; }
|
||||
FileInfos* GetFileInfos() { return &m_FileInfos; }
|
||||
NZBInfo* GetNZBInfo() { return m_pNZBInfo; }
|
||||
const char* GetPassword() { return m_szPassword; }
|
||||
void DetachFileInfos();
|
||||
|
||||
void LogDebugInfo();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* 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-2013 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -34,32 +34,47 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.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)
|
||||
NewsServer::NewsServer(int iID, bool bActive, const char* szName, const char* szHost, int iPort,
|
||||
const char* szUser, const char* szPass, bool bJoinGroup, bool bTLS,
|
||||
const char* szCipher, int iMaxConnections, int iLevel, int iGroup)
|
||||
{
|
||||
m_szUser = NULL;
|
||||
m_szPassword = NULL;
|
||||
m_iLevel = level;
|
||||
m_iMaxConnections = maxConnections;
|
||||
m_iID = iID;
|
||||
m_iStateID = 0;
|
||||
m_bActive = bActive;
|
||||
m_iPort = iPort;
|
||||
m_iLevel = iLevel;
|
||||
m_iNormLevel = iLevel;
|
||||
m_iGroup = iGroup;
|
||||
m_iMaxConnections = iMaxConnections;
|
||||
m_bJoinGroup = bJoinGroup;
|
||||
m_bTLS = bTLS;
|
||||
m_szHost = strdup(szHost ? szHost : "");
|
||||
m_szUser = strdup(szUser ? szUser : "");
|
||||
m_szPassword = strdup(szPass ? szPass : "");
|
||||
m_szCipher = strdup(szCipher ? szCipher : "");
|
||||
|
||||
if (pass)
|
||||
if (szName && strlen(szName) > 0)
|
||||
{
|
||||
m_szPassword = strdup(pass);
|
||||
m_szName = strdup(szName);
|
||||
}
|
||||
if (user)
|
||||
else
|
||||
{
|
||||
m_szUser = strdup(user);
|
||||
m_szName = (char*)malloc(20);
|
||||
snprintf(m_szName, 20, "server%i", iID);
|
||||
m_szName[20-1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
NewsServer::~NewsServer()
|
||||
{
|
||||
free(m_szName);
|
||||
free(m_szHost);
|
||||
free(m_szUser);
|
||||
m_szUser = NULL;
|
||||
free(m_szPassword);
|
||||
m_szPassword = NULL;
|
||||
free(m_szCipher);
|
||||
}
|
||||
|
||||
51
NewsServer.h
51
NewsServer.h
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* 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-2013 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -27,23 +27,52 @@
|
||||
#ifndef NEWSSERVER_H
|
||||
#define NEWSSERVER_H
|
||||
|
||||
#include "NetAddress.h"
|
||||
#include <vector>
|
||||
|
||||
class NewsServer : public NetAddress
|
||||
class NewsServer
|
||||
{
|
||||
private:
|
||||
char* m_szUser;
|
||||
char* m_szPassword;
|
||||
int m_iMaxConnections;
|
||||
int m_iLevel;
|
||||
int m_iID;
|
||||
int m_iStateID;
|
||||
bool m_bActive;
|
||||
char* m_szName;
|
||||
int m_iGroup;
|
||||
char* m_szHost;
|
||||
int m_iPort;
|
||||
char* m_szUser;
|
||||
char* m_szPassword;
|
||||
int m_iMaxConnections;
|
||||
int m_iLevel;
|
||||
int m_iNormLevel;
|
||||
bool m_bJoinGroup;
|
||||
bool m_bTLS;
|
||||
char* m_szCipher;
|
||||
|
||||
public:
|
||||
NewsServer(const char* host, int port, const char* user, const char* pass, int maxConnections, int level);
|
||||
virtual ~NewsServer();
|
||||
NewsServer(int iID, bool bActive, const char* szName, const char* szHost, int iPort,
|
||||
const char* szUser, const char* szPass, bool bJoinGroup,
|
||||
bool bTLS, const char* szCipher, int iMaxConnections, int iLevel, int iGroup);
|
||||
~NewsServer();
|
||||
int GetID() { return m_iID; }
|
||||
int GetStateID() { return m_iStateID; }
|
||||
void SetStateID(int iStateID) { m_iStateID = iStateID; }
|
||||
bool GetActive() { return m_bActive; }
|
||||
void SetActive(bool bActive) { m_bActive = bActive; }
|
||||
const char* GetName() { return m_szName; }
|
||||
int GetGroup() { return m_iGroup; }
|
||||
const char* GetHost() { return m_szHost; }
|
||||
int GetPort() { return m_iPort; }
|
||||
const char* GetUser() { return m_szUser; }
|
||||
const char* GetPassword() { return m_szPassword; }
|
||||
int GetMaxConnections() { return m_iMaxConnections; }
|
||||
int GetLevel() { return m_iLevel; }
|
||||
int GetNormLevel() { return m_iNormLevel; }
|
||||
void SetNormLevel(int iLevel) { m_iNormLevel = iLevel; }
|
||||
int GetJoinGroup() { return m_bJoinGroup; }
|
||||
bool GetTLS() { return m_bTLS; }
|
||||
const char* GetCipher() { return m_szCipher; }
|
||||
};
|
||||
|
||||
typedef std::vector<NewsServer*> Servers;
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
|
||||
2814
Options.cpp
2814
Options.cpp
File diff suppressed because it is too large
Load Diff
392
Options.h
392
Options.h
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* 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-2013 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -28,6 +28,11 @@
|
||||
#define OPTIONS_H
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <time.h>
|
||||
|
||||
#include "Thread.h"
|
||||
#include "Util.h"
|
||||
|
||||
class Options
|
||||
{
|
||||
@@ -36,17 +41,31 @@ public:
|
||||
{
|
||||
opClientNoOperation,
|
||||
opClientRequestDownload,
|
||||
opClientRequestList,
|
||||
opClientRequestPause,
|
||||
opClientRequestUnpause,
|
||||
opClientRequestListFiles,
|
||||
opClientRequestListGroups,
|
||||
opClientRequestListStatus,
|
||||
opClientRequestSetRate,
|
||||
opClientRequestDumpDebug,
|
||||
opClientRequestEditQueue,
|
||||
opClientRequestLog,
|
||||
opClientRequestShutdown,
|
||||
opClientRequestReload,
|
||||
opClientRequestVersion,
|
||||
opClientRequestPostQueue,
|
||||
opClientRequestWriteLog
|
||||
opClientRequestWriteLog,
|
||||
opClientRequestScanSync,
|
||||
opClientRequestScanAsync,
|
||||
opClientRequestDownloadPause,
|
||||
opClientRequestDownloadUnpause,
|
||||
opClientRequestDownload2Pause,
|
||||
opClientRequestDownload2Unpause,
|
||||
opClientRequestPostPause,
|
||||
opClientRequestPostUnpause,
|
||||
opClientRequestScanPause,
|
||||
opClientRequestScanUnpause,
|
||||
opClientRequestHistory,
|
||||
opClientRequestDownloadUrl,
|
||||
opClientRequestUrlQueue
|
||||
};
|
||||
enum EMessageTarget
|
||||
{
|
||||
@@ -61,40 +80,171 @@ public:
|
||||
omColored,
|
||||
omNCurses
|
||||
};
|
||||
enum ELoadPars
|
||||
enum EParCheck
|
||||
{
|
||||
lpNone,
|
||||
lpOne,
|
||||
lpAll
|
||||
pcAuto,
|
||||
pcForce,
|
||||
pcManual
|
||||
};
|
||||
enum EParScan
|
||||
{
|
||||
psLimited,
|
||||
psFull,
|
||||
psAuto
|
||||
};
|
||||
enum EHealthCheck
|
||||
{
|
||||
hcPause,
|
||||
hcDelete,
|
||||
hcNone
|
||||
};
|
||||
enum EScriptLogKind
|
||||
{
|
||||
slNone,
|
||||
slDetail,
|
||||
slInfo,
|
||||
slWarning,
|
||||
slError,
|
||||
slDebug
|
||||
};
|
||||
enum EMatchMode
|
||||
{
|
||||
mmID = 1,
|
||||
mmName,
|
||||
mmRegEx
|
||||
};
|
||||
|
||||
enum EPostLogKind
|
||||
class OptEntry
|
||||
{
|
||||
plNone,
|
||||
plDetail,
|
||||
plInfo,
|
||||
plWarning,
|
||||
plError,
|
||||
plDebug
|
||||
private:
|
||||
char* m_szName;
|
||||
char* m_szValue;
|
||||
char* m_szDefValue;
|
||||
int m_iLineNo;
|
||||
|
||||
void SetName(const char* szName);
|
||||
void SetValue(const char* szValue);
|
||||
void SetLineNo(int iLineNo) { m_iLineNo = iLineNo; }
|
||||
|
||||
friend class Options;
|
||||
|
||||
public:
|
||||
OptEntry();
|
||||
OptEntry(const char* szName, const char* szValue);
|
||||
~OptEntry();
|
||||
const char* GetName() { return m_szName; }
|
||||
const char* GetValue() { return m_szValue; }
|
||||
const char* GetDefValue() { return m_szDefValue; }
|
||||
int GetLineNo() { return m_iLineNo; }
|
||||
};
|
||||
|
||||
typedef std::vector<OptEntry*> OptEntriesBase;
|
||||
|
||||
class OptEntries: public OptEntriesBase
|
||||
{
|
||||
public:
|
||||
~OptEntries();
|
||||
OptEntry* FindOption(const char* szName);
|
||||
};
|
||||
|
||||
class ConfigTemplate
|
||||
{
|
||||
private:
|
||||
char* m_szName;
|
||||
char* m_szDisplayName;
|
||||
char* m_szTemplate;
|
||||
|
||||
friend class Options;
|
||||
|
||||
public:
|
||||
ConfigTemplate(const char* szName, const char* szDisplayName, const char* szTemplate);
|
||||
~ConfigTemplate();
|
||||
const char* GetName() { return m_szName; }
|
||||
const char* GetDisplayName() { return m_szDisplayName; }
|
||||
const char* GetTemplate() { return m_szTemplate; }
|
||||
};
|
||||
|
||||
typedef std::vector<ConfigTemplate*> ConfigTemplatesBase;
|
||||
|
||||
class ConfigTemplates: public ConfigTemplatesBase
|
||||
{
|
||||
public:
|
||||
~ConfigTemplates();
|
||||
};
|
||||
|
||||
typedef std::vector<char*> NameList;
|
||||
|
||||
class Category
|
||||
{
|
||||
private:
|
||||
char* m_szName;
|
||||
char* m_szDestDir;
|
||||
bool m_bUnpack;
|
||||
char* m_szDefScript;
|
||||
NameList m_Aliases;
|
||||
|
||||
public:
|
||||
Category(const char* szName, const char* szDestDir, bool bUnpack, const char* szDefScript);
|
||||
~Category();
|
||||
const char* GetName() { return m_szName; }
|
||||
const char* GetDestDir() { return m_szDestDir; }
|
||||
bool GetUnpack() { return m_bUnpack; }
|
||||
const char* GetDefScript() { return m_szDefScript; }
|
||||
NameList* GetAliases() { return &m_Aliases; }
|
||||
};
|
||||
|
||||
typedef std::vector<Category*> CategoriesBase;
|
||||
|
||||
class Categories: public CategoriesBase
|
||||
{
|
||||
public:
|
||||
~Categories();
|
||||
Category* FindCategory(const char* szName, bool bSearchAliases);
|
||||
};
|
||||
|
||||
class Script
|
||||
{
|
||||
private:
|
||||
char* m_szName;
|
||||
char* m_szLocation;
|
||||
char* m_szDisplayName;
|
||||
|
||||
public:
|
||||
Script(const char* szName, const char* szLocation);
|
||||
~Script();
|
||||
const char* GetName() { return m_szName; }
|
||||
const char* GetLocation() { return m_szLocation; }
|
||||
void SetDisplayName(const char* szDisplayName);
|
||||
const char* GetDisplayName() { return m_szDisplayName; }
|
||||
};
|
||||
|
||||
typedef std::list<Script*> ScriptListBase;
|
||||
|
||||
class ScriptList: public ScriptListBase
|
||||
{
|
||||
public:
|
||||
~ScriptList();
|
||||
Script* Find(const char* szName);
|
||||
};
|
||||
|
||||
private:
|
||||
struct OptEntry
|
||||
{
|
||||
char* name;
|
||||
char* value;
|
||||
};
|
||||
|
||||
std::vector< struct OptEntry > optEntries;
|
||||
|
||||
OptEntries m_OptEntries;
|
||||
bool m_bConfigInitialized;
|
||||
Mutex m_mutexOptEntries;
|
||||
Categories m_Categories;
|
||||
|
||||
// Options
|
||||
bool m_bConfigErrors;
|
||||
int m_iConfigLine;
|
||||
char* m_szConfigFilename;
|
||||
char* m_szDestDir;
|
||||
char* m_szInterDir;
|
||||
char* m_szTempDir;
|
||||
char* m_szQueueDir;
|
||||
char* m_szNzbDir;
|
||||
char* m_szWebDir;
|
||||
char* m_szConfigTemplate;
|
||||
char* m_szScriptDir;
|
||||
EMessageTarget m_eInfoTarget;
|
||||
EMessageTarget m_eWarningTarget;
|
||||
EMessageTarget m_eErrorTarget;
|
||||
@@ -105,29 +255,40 @@ private:
|
||||
bool m_bResetLog;
|
||||
int m_iConnectionTimeout;
|
||||
int m_iTerminateTimeout;
|
||||
bool m_bAppendNZBDir;
|
||||
bool m_bAppendCategoryDir;
|
||||
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_szControlIP;
|
||||
char* m_szControlUsername;
|
||||
char* m_szControlPassword;
|
||||
int m_iControlPort;
|
||||
bool m_bSecureControl;
|
||||
int m_iSecurePort;
|
||||
char* m_szSecureCert;
|
||||
char* m_szSecureKey;
|
||||
char* m_szAuthorizedIP;
|
||||
char* m_szLockFile;
|
||||
char* m_szDaemonUserName;
|
||||
char* m_szDaemonUsername;
|
||||
EOutputMode m_eOutputMode;
|
||||
bool m_bReloadQueue;
|
||||
bool m_bReloadUrlQueue;
|
||||
bool m_bReloadPostQueue;
|
||||
int m_iUrlConnections;
|
||||
int m_iLogBufferSize;
|
||||
bool m_bCreateLog;
|
||||
char* m_szLogFile;
|
||||
ELoadPars m_eLoadPars;
|
||||
bool m_bParCheck;
|
||||
EParCheck m_eParCheck;
|
||||
bool m_bParRepair;
|
||||
char* m_szPostProcess;
|
||||
bool m_bStrictParName;
|
||||
EParScan m_eParScan;
|
||||
bool m_bParRename;
|
||||
EHealthCheck m_eHealthCheck;
|
||||
char* m_szDefScript;
|
||||
char* m_szScriptOrder;
|
||||
char* m_szNZBProcess;
|
||||
char* m_szNZBAddedProcess;
|
||||
bool m_bNoConfig;
|
||||
int m_iUMask;
|
||||
int m_iUpdateInterval;
|
||||
@@ -135,15 +296,30 @@ private:
|
||||
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;
|
||||
bool m_bParCleanupQueue;
|
||||
int m_iDiskSpace;
|
||||
EPostLogKind m_ePostLogKind;
|
||||
bool m_bTLS;
|
||||
bool m_bDumpCore;
|
||||
bool m_bParPauseQueue;
|
||||
bool m_bScriptPauseQueue;
|
||||
bool m_bNzbCleanupDisk;
|
||||
bool m_bDeleteCleanupDisk;
|
||||
int m_iParTimeLimit;
|
||||
int m_iKeepHistory;
|
||||
bool m_bAccurateRate;
|
||||
bool m_bUnpack;
|
||||
bool m_bUnpackCleanupDisk;
|
||||
char* m_szUnrarCmd;
|
||||
char* m_szSevenZipCmd;
|
||||
bool m_bUnpackPauseQueue;
|
||||
char* m_szExtCleanupDisk;
|
||||
int m_iFeedHistory;
|
||||
bool m_bUrlForce;
|
||||
int m_iTimeCorrection;
|
||||
|
||||
// Parsed command-line parameters
|
||||
bool m_bServerMode;
|
||||
@@ -153,18 +329,30 @@ private:
|
||||
int m_iEditQueueOffset;
|
||||
int* m_pEditQueueIDList;
|
||||
int m_iEditQueueIDCount;
|
||||
NameList m_EditQueueNameList;
|
||||
EMatchMode m_EMatchMode;
|
||||
char* m_szEditQueueText;
|
||||
char* m_szArgFilename;
|
||||
char* m_szAddCategory;
|
||||
int m_iAddPriority;
|
||||
bool m_bAddPaused;
|
||||
char* m_szAddNZBFilename;
|
||||
char* m_szLastArg;
|
||||
bool m_bPrintOptions;
|
||||
bool m_bAddTop;
|
||||
float m_fSetRate;
|
||||
int m_iSetRate;
|
||||
int m_iLogLines;
|
||||
int m_iWriteLogKind;
|
||||
bool m_bTestBacktrace;
|
||||
|
||||
// Current state
|
||||
bool m_bPause;
|
||||
float m_fDownloadRate;
|
||||
bool m_bPauseDownload;
|
||||
bool m_bPauseDownload2;
|
||||
bool m_bPausePostProcess;
|
||||
bool m_bPauseScan;
|
||||
int m_iDownloadRate;
|
||||
EClientOperation m_eClientOperation;
|
||||
time_t m_tResumeTime;
|
||||
|
||||
void InitDefault();
|
||||
void InitOptFile();
|
||||
@@ -172,28 +360,56 @@ private:
|
||||
void InitOptions();
|
||||
void InitFileArg(int argc, char* argv[]);
|
||||
void InitServers();
|
||||
void InitCategories();
|
||||
void InitScheduler();
|
||||
void InitFeeds();
|
||||
void CheckOptions();
|
||||
void PrintUsage(char* com);
|
||||
void Dump();
|
||||
int ParseOptionValue(const char* OptName, int argc, const char* argn[], const int argv[]);
|
||||
int ParseEnumValue(const char* OptName, int argc, const char* argn[], const int argv[]);
|
||||
int ParseIntValue(const char* OptName, int iBase);
|
||||
float ParseFloatValue(const char* OptName);
|
||||
OptEntry* FindOption(const char* optname);
|
||||
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 SplitOptionString(const char* option, char** pOptName, char** pOptValue);
|
||||
bool ValidateOptionName(const char* optname);
|
||||
void LoadConfig(const char* configfile);
|
||||
void CheckDir(char** dir, const char* szOptionName);
|
||||
void LoadConfigFile();
|
||||
void CheckDir(char** dir, const char* szOptionName, bool bAllowEmpty, bool bCreate);
|
||||
void ParseFileIDList(int argc, char* argv[], int optind);
|
||||
void ParseFileNameList(int argc, char* argv[], int optind);
|
||||
bool ParseTime(const char** pTime, int* pHours, int* pMinutes);
|
||||
bool ParseWeekDays(const char* szWeekDays, int* pWeekDaysBits);
|
||||
void ConfigError(const char* msg, ...);
|
||||
void ConfigWarn(const char* msg, ...);
|
||||
void LocateOptionSrcPos(const char *szOptionName);
|
||||
void ConvertOldOption(char *szOption, int iOptionBufLen, char *szValue, int iValueBufLen);
|
||||
static bool CompareScripts(Script* pScript1, Script* pScript2);
|
||||
void LoadScriptDir(ScriptList* pScriptList, const char* szDirectory, bool bIsSubDir);
|
||||
void BuildScriptDisplayNames(ScriptList* pScriptList);
|
||||
|
||||
public:
|
||||
Options(int argc, char* argv[]);
|
||||
~Options();
|
||||
|
||||
bool LoadConfig(OptEntries* pOptEntries);
|
||||
bool SaveConfig(OptEntries* pOptEntries);
|
||||
bool LoadConfigTemplates(ConfigTemplates* pConfigTemplates);
|
||||
void LoadScriptList(ScriptList* pScriptList);
|
||||
|
||||
// Options
|
||||
OptEntries* LockOptEntries();
|
||||
void UnlockOptEntries();
|
||||
const char* GetConfigFilename() { return m_szConfigFilename; }
|
||||
const char* GetDestDir() { return m_szDestDir; }
|
||||
const char* GetInterDir() { return m_szInterDir; }
|
||||
const char* GetTempDir() { return m_szTempDir; }
|
||||
const char* GetQueueDir() { return m_szQueueDir; }
|
||||
const char* GetNzbDir() { return m_szNzbDir; }
|
||||
const char* GetWebDir() { return m_szWebDir; }
|
||||
const char* GetConfigTemplate() { return m_szConfigTemplate; }
|
||||
const char* GetScriptDir() { return m_szScriptDir; }
|
||||
bool GetCreateBrokenLog() const { return m_bCreateBrokenLog; }
|
||||
bool GetResetLog() const { return m_bResetLog; }
|
||||
EMessageTarget GetInfoTarget() const { return m_eInfoTarget; }
|
||||
@@ -204,44 +420,72 @@ public:
|
||||
int GetConnectionTimeout() { return m_iConnectionTimeout; }
|
||||
int GetTerminateTimeout() { return m_iTerminateTimeout; }
|
||||
bool GetDecode() { return m_bDecode; };
|
||||
bool GetAppendNZBDir() { return m_bAppendNZBDir; }
|
||||
bool GetAppendCategoryDir() { return m_bAppendCategoryDir; }
|
||||
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; }
|
||||
const char* GetControlIP() { return m_szControlIP; }
|
||||
const char* GetControlUsername() { return m_szControlUsername; }
|
||||
const char* GetControlPassword() { return m_szControlPassword; }
|
||||
int GetControlPort() { return m_iControlPort; }
|
||||
bool GetSecureControl() { return m_bSecureControl; }
|
||||
int GetSecurePort() { return m_iSecurePort; }
|
||||
const char* GetSecureCert() { return m_szSecureCert; }
|
||||
const char* GetSecureKey() { return m_szSecureKey; }
|
||||
const char* GetAuthorizedIP() { return m_szAuthorizedIP; }
|
||||
const char* GetLockFile() { return m_szLockFile; }
|
||||
const char* GetDaemonUsername() { return m_szDaemonUsername; }
|
||||
EOutputMode GetOutputMode() { return m_eOutputMode; }
|
||||
bool GetReloadQueue() { return m_bReloadQueue; }
|
||||
bool GetReloadUrlQueue() { return m_bReloadUrlQueue; }
|
||||
bool GetReloadPostQueue() { return m_bReloadPostQueue; }
|
||||
int GetUrlConnections() { return m_iUrlConnections; }
|
||||
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; }
|
||||
const char* GetLogFile() { return m_szLogFile; }
|
||||
EParCheck GetParCheck() { return m_eParCheck; }
|
||||
bool GetParRepair() { return m_bParRepair; }
|
||||
const char* GetPostProcess() { return m_szPostProcess; }
|
||||
bool GetStrictParName() { return m_bStrictParName; }
|
||||
EParScan GetParScan() { return m_eParScan; }
|
||||
bool GetParRename() { return m_bParRename; }
|
||||
EHealthCheck GetHealthCheck() { return m_eHealthCheck; }
|
||||
const char* GetScriptOrder() { return m_szScriptOrder; }
|
||||
const char* GetDefScript() { return m_szDefScript; }
|
||||
const char* GetNZBProcess() { return m_szNZBProcess; }
|
||||
const char* GetNZBAddedProcess() { return m_szNZBAddedProcess; }
|
||||
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; }
|
||||
bool GetParCleanupQueue() { return m_bParCleanupQueue; }
|
||||
int GetDiskSpace() { return m_iDiskSpace; }
|
||||
EPostLogKind GetPostLogKind() { return m_ePostLogKind; }
|
||||
bool GetTLS() { return m_bTLS; }
|
||||
bool GetDumpCore() { return m_bDumpCore; }
|
||||
bool GetParPauseQueue() { return m_bParPauseQueue; }
|
||||
bool GetScriptPauseQueue() { return m_bScriptPauseQueue; }
|
||||
bool GetNzbCleanupDisk() { return m_bNzbCleanupDisk; }
|
||||
bool GetDeleteCleanupDisk() { return m_bDeleteCleanupDisk; }
|
||||
int GetParTimeLimit() { return m_iParTimeLimit; }
|
||||
int GetKeepHistory() { return m_iKeepHistory; }
|
||||
bool GetAccurateRate() { return m_bAccurateRate; }
|
||||
bool GetUnpack() { return m_bUnpack; }
|
||||
bool GetUnpackCleanupDisk() { return m_bUnpackCleanupDisk; }
|
||||
const char* GetUnrarCmd() { return m_szUnrarCmd; }
|
||||
const char* GetSevenZipCmd() { return m_szSevenZipCmd; }
|
||||
bool GetUnpackPauseQueue() { return m_bUnpackPauseQueue; }
|
||||
const char* GetExtCleanupDisk() { return m_szExtCleanupDisk; }
|
||||
int GetFeedHistory() { return m_iFeedHistory; }
|
||||
bool GetUrlForce() { return m_bUrlForce; }
|
||||
int GetTimeCorrection() { return m_iTimeCorrection; }
|
||||
|
||||
Category* FindCategory(const char* szName, bool bSearchAliases) { return m_Categories.FindCategory(szName, bSearchAliases); }
|
||||
|
||||
// Parsed command-line parameters
|
||||
bool GetServerMode() { return m_bServerMode; }
|
||||
@@ -252,18 +496,34 @@ public:
|
||||
int GetEditQueueOffset() { return m_iEditQueueOffset; }
|
||||
int* GetEditQueueIDList() { return m_pEditQueueIDList; }
|
||||
int GetEditQueueIDCount() { return m_iEditQueueIDCount; }
|
||||
NameList* GetEditQueueNameList() { return &m_EditQueueNameList; }
|
||||
EMatchMode GetMatchMode() { return m_EMatchMode; }
|
||||
const char* GetEditQueueText() { return m_szEditQueueText; }
|
||||
const char* GetArgFilename() { return m_szArgFilename; }
|
||||
const char* GetAddCategory() { return m_szAddCategory; }
|
||||
bool GetAddPaused() { return m_bAddPaused; }
|
||||
const char* GetLastArg() { return m_szLastArg; }
|
||||
int GetAddPriority() { return m_iAddPriority; }
|
||||
char* GetAddNZBFilename() { return m_szAddNZBFilename; }
|
||||
bool GetAddTop() { return m_bAddTop; }
|
||||
float GetSetRate() { return m_fSetRate; }
|
||||
int GetSetRate() { return m_iSetRate; }
|
||||
int GetLogLines() { return m_iLogLines; }
|
||||
int GetWriteLogKind() { return m_iWriteLogKind; }
|
||||
bool GetTestBacktrace() { return m_bTestBacktrace; }
|
||||
|
||||
// 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; }
|
||||
void SetPauseDownload(bool bPauseDownload) { m_bPauseDownload = bPauseDownload; }
|
||||
bool GetPauseDownload() const { return m_bPauseDownload; }
|
||||
void SetPauseDownload2(bool bPauseDownload2) { m_bPauseDownload2 = bPauseDownload2; }
|
||||
bool GetPauseDownload2() const { return m_bPauseDownload2; }
|
||||
void SetPausePostProcess(bool bPausePostProcess) { m_bPausePostProcess = bPausePostProcess; }
|
||||
bool GetPausePostProcess() const { return m_bPausePostProcess; }
|
||||
void SetPauseScan(bool bPauseScan) { m_bPauseScan = bPauseScan; }
|
||||
bool GetPauseScan() const { return m_bPauseScan; }
|
||||
void SetDownloadRate(int iRate) { m_iDownloadRate = iRate; }
|
||||
int GetDownloadRate() const { return m_iDownloadRate; }
|
||||
void SetResumeTime(time_t tResumeTime) { m_tResumeTime = tResumeTime; }
|
||||
time_t GetResumeTime() const { return m_tResumeTime; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
786
ParChecker.cpp
786
ParChecker.cpp
File diff suppressed because it is too large
Load Diff
53
ParChecker.h
53
ParChecker.h
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2013 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,7 +15,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -29,20 +29,20 @@
|
||||
#ifndef DISABLE_PARCHECK
|
||||
|
||||
#include <deque>
|
||||
#include <string>
|
||||
|
||||
#include "Thread.h"
|
||||
#include "Observer.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Log.h"
|
||||
|
||||
class ParChecker : public Thread, public Subject
|
||||
class ParChecker : public Thread
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
psUndefined,
|
||||
psWorking,
|
||||
psFailed,
|
||||
psFinished
|
||||
psRepairPossible,
|
||||
psRepaired,
|
||||
psRepairNotNeeded
|
||||
};
|
||||
|
||||
enum EStage
|
||||
@@ -53,20 +53,22 @@ public:
|
||||
ptVerifyingRepaired,
|
||||
};
|
||||
|
||||
typedef std::deque<char*> QueuedParFiles;
|
||||
typedef std::deque<char*> FileList;
|
||||
typedef std::deque<void*> SourceList;
|
||||
|
||||
private:
|
||||
char* m_szInfoName;
|
||||
char* m_szNZBFilename;
|
||||
char* m_szParFilename;
|
||||
char* m_szDestDir;
|
||||
char* m_szNZBName;
|
||||
const char* m_szParFilename;
|
||||
EStatus m_eStatus;
|
||||
EStage m_eStage;
|
||||
void* m_pRepairer; // declared as void* to prevent the including of libpar2-headers into this header-file
|
||||
char* m_szErrMsg;
|
||||
bool m_bRepairNotNeeded;
|
||||
QueuedParFiles m_QueuedParFiles;
|
||||
FileList m_QueuedParFiles;
|
||||
Mutex m_mutexQueuedParFiles;
|
||||
Semaphore m_semNeedMoreFiles;
|
||||
bool m_bQueuedParFilesChanged;
|
||||
FileList m_ProcessedFiles;
|
||||
int m_iProcessedFiles;
|
||||
int m_iFilesToRepair;
|
||||
int m_iExtraFiles;
|
||||
@@ -74,10 +76,21 @@ private:
|
||||
char* m_szProgressLabel;
|
||||
int m_iFileProgress;
|
||||
int m_iStageProgress;
|
||||
bool m_bCancelled;
|
||||
SourceList m_sourceFiles;
|
||||
|
||||
void Cleanup();
|
||||
EStatus RunParCheck(const char* szParFilename);
|
||||
int PreProcessPar();
|
||||
bool LoadMainParBak();
|
||||
int ProcessMorePars();
|
||||
bool LoadMorePars();
|
||||
bool CheckSplittedFragments();
|
||||
bool AddSplittedFragments(const char* szFilename);
|
||||
bool AddMissingFiles();
|
||||
void WriteBrokenLog(EStatus eStatus);
|
||||
void SaveSourceList();
|
||||
void DeleteLeftovers();
|
||||
void signal_filename(std::string str);
|
||||
void signal_progress(double progress);
|
||||
void signal_done(std::string str, int available, int total);
|
||||
@@ -90,6 +103,8 @@ protected:
|
||||
*/
|
||||
virtual bool RequestMorePars(int iBlockNeeded, int* pBlockFound) = 0;
|
||||
virtual void UpdateProgress() {}
|
||||
virtual void Completed() {}
|
||||
virtual void PrintMessage(Message::EKind eKind, const char* szFormat, ...) {}
|
||||
EStage GetStage() { return m_eStage; }
|
||||
const char* GetProgressLabel() { return m_szProgressLabel; }
|
||||
int GetFileProgress() { return m_iFileProgress; }
|
||||
@@ -99,18 +114,16 @@ public:
|
||||
ParChecker();
|
||||
virtual ~ParChecker();
|
||||
virtual void Run();
|
||||
void SetDestDir(const char* szDestDir);
|
||||
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);
|
||||
void SetNZBName(const char* szNZBName);
|
||||
EStatus GetStatus() { return m_eStatus; }
|
||||
const char* GetErrMsg() { return m_szErrMsg; }
|
||||
bool GetRepairNotNeeded() { return m_bRepairNotNeeded; }
|
||||
void AddParFile(const char* szParFilename);
|
||||
void QueueChanged();
|
||||
void Cancel();
|
||||
bool GetCancelled() { return m_bCancelled; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
742
ParCoordinator.cpp
Normal file
742
ParCoordinator.cpp
Normal file
@@ -0,0 +1,742 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007-2013 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
#ifdef WIN32
|
||||
#include <direct.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "ParCoordinator.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "DiskState.h"
|
||||
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
extern Options* g_pOptions;
|
||||
extern DiskState* g_pDiskState;
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
bool ParCoordinator::PostParChecker::RequestMorePars(int iBlockNeeded, int* pBlockFound)
|
||||
{
|
||||
return m_pOwner->RequestMorePars(m_pPostInfo->GetNZBInfo(), GetParFilename(), iBlockNeeded, pBlockFound);
|
||||
}
|
||||
|
||||
void ParCoordinator::PostParChecker::UpdateProgress()
|
||||
{
|
||||
m_pOwner->UpdateParCheckProgress();
|
||||
}
|
||||
|
||||
void ParCoordinator::PostParChecker::PrintMessage(Message::EKind eKind, const char* szFormat, ...)
|
||||
{
|
||||
char szText[1024];
|
||||
va_list args;
|
||||
va_start(args, szFormat);
|
||||
vsnprintf(szText, 1024, szFormat, args);
|
||||
va_end(args);
|
||||
szText[1024-1] = '\0';
|
||||
|
||||
m_pOwner->PrintMessage(m_pPostInfo, eKind, "%s", szText);
|
||||
}
|
||||
|
||||
void ParCoordinator::PostParRenamer::UpdateProgress()
|
||||
{
|
||||
m_pOwner->UpdateParRenameProgress();
|
||||
}
|
||||
|
||||
void ParCoordinator::PostParRenamer::PrintMessage(Message::EKind eKind, const char* szFormat, ...)
|
||||
{
|
||||
char szText[1024];
|
||||
va_list args;
|
||||
va_start(args, szFormat);
|
||||
vsnprintf(szText, 1024, szFormat, args);
|
||||
va_end(args);
|
||||
szText[1024-1] = '\0';
|
||||
|
||||
m_pOwner->PrintMessage(m_pPostInfo, eKind, "%s", szText);
|
||||
}
|
||||
#endif
|
||||
|
||||
ParCoordinator::ParCoordinator()
|
||||
{
|
||||
debug("Creating ParCoordinator");
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
m_bStopped = false;
|
||||
m_ParChecker.m_pOwner = this;
|
||||
m_ParRenamer.m_pOwner = this;
|
||||
#endif
|
||||
}
|
||||
|
||||
ParCoordinator::~ParCoordinator()
|
||||
{
|
||||
debug("Destroying ParCoordinator");
|
||||
}
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
void ParCoordinator::Stop()
|
||||
{
|
||||
debug("Stopping ParCoordinator");
|
||||
|
||||
m_bStopped = true;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void ParCoordinator::PausePars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
|
||||
{
|
||||
debug("ParCoordinator: Pausing pars");
|
||||
|
||||
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (pFileInfo->GetNZBInfo() == pNZBInfo)
|
||||
{
|
||||
g_pQueueCoordinator->GetQueueEditor()->LockedEditEntry(pDownloadQueue, pFileInfo->GetID(), false,
|
||||
QueueEditor::eaGroupPauseExtraPars, 0, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ParCoordinator::FindMainPars(const char* szPath, FileList* pFileList)
|
||||
{
|
||||
if (pFileList)
|
||||
{
|
||||
pFileList->clear();
|
||||
}
|
||||
|
||||
DirBrowser dir(szPath);
|
||||
while (const char* filename = dir.Next())
|
||||
{
|
||||
int iBaseLen = 0;
|
||||
if (ParseParFilename(filename, &iBaseLen, NULL))
|
||||
{
|
||||
if (!pFileList)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// 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 && !pFileList->empty();
|
||||
}
|
||||
|
||||
bool ParCoordinator::SameParCollection(const char* szFilename1, const char* szFilename2)
|
||||
{
|
||||
int iBaseLen1 = 0, iBaseLen2 = 0;
|
||||
return ParseParFilename(szFilename1, &iBaseLen1, NULL) &&
|
||||
ParseParFilename(szFilename2, &iBaseLen2, NULL) &&
|
||||
iBaseLen1 == iBaseLen2 &&
|
||||
!strncasecmp(szFilename1, szFilename2, iBaseLen1);
|
||||
}
|
||||
|
||||
bool ParCoordinator::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 (iLen < 6)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
|
||||
/**
|
||||
* DownloadQueue must be locked prior to call of this function.
|
||||
*/
|
||||
void ParCoordinator::StartParCheckJob(PostInfo* pPostInfo)
|
||||
{
|
||||
m_eCurrentJob = jkParCheck;
|
||||
m_ParChecker.SetPostInfo(pPostInfo);
|
||||
m_ParChecker.SetDestDir(pPostInfo->GetNZBInfo()->GetDestDir());
|
||||
m_ParChecker.SetNZBName(pPostInfo->GetNZBInfo()->GetName());
|
||||
m_ParChecker.PrintMessage(Message::mkInfo, "Checking pars for %s", pPostInfo->GetInfoName());
|
||||
pPostInfo->SetWorking(true);
|
||||
m_ParChecker.Start();
|
||||
}
|
||||
|
||||
/**
|
||||
* DownloadQueue must be locked prior to call of this function.
|
||||
*/
|
||||
void ParCoordinator::StartParRenameJob(PostInfo* pPostInfo)
|
||||
{
|
||||
const char* szDestDir = pPostInfo->GetNZBInfo()->GetDestDir();
|
||||
|
||||
char szFinalDir[1024];
|
||||
if (pPostInfo->GetNZBInfo()->GetUnpackStatus() == NZBInfo::usSuccess)
|
||||
{
|
||||
pPostInfo->GetNZBInfo()->BuildFinalDirName(szFinalDir, 1024);
|
||||
szFinalDir[1024-1] = '\0';
|
||||
szDestDir = szFinalDir;
|
||||
}
|
||||
|
||||
m_eCurrentJob = jkParRename;
|
||||
m_ParRenamer.SetPostInfo(pPostInfo);
|
||||
m_ParRenamer.SetDestDir(szDestDir);
|
||||
m_ParRenamer.SetInfoName(pPostInfo->GetNZBInfo()->GetName());
|
||||
m_ParRenamer.PrintMessage(Message::mkInfo, "Checking renamed files for %s", pPostInfo->GetNZBInfo()->GetName());
|
||||
pPostInfo->SetWorking(true);
|
||||
m_ParRenamer.Start();
|
||||
}
|
||||
|
||||
bool ParCoordinator::Cancel()
|
||||
{
|
||||
if (m_eCurrentJob == jkParCheck)
|
||||
{
|
||||
#ifdef HAVE_PAR2_CANCEL
|
||||
if (!m_ParChecker.GetCancelled())
|
||||
{
|
||||
debug("Cancelling par-repair for %s", m_ParChecker.GetInfoName());
|
||||
m_ParChecker.Cancel();
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
warn("Cannot cancel par-repair for %s, used version of libpar2 does not support cancelling", m_ParChecker.GetInfoName());
|
||||
#endif
|
||||
}
|
||||
else if (m_eCurrentJob == jkParRename)
|
||||
{
|
||||
if (!m_ParRenamer.GetCancelled())
|
||||
{
|
||||
debug("Cancelling par-rename for %s", m_ParRenamer.GetInfoName());
|
||||
m_ParRenamer.Cancel();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* DownloadQueue must be locked prior to call of this function.
|
||||
*/
|
||||
bool ParCoordinator::AddPar(FileInfo* pFileInfo, bool bDeleted)
|
||||
{
|
||||
bool bSameCollection = m_ParChecker.IsRunning() &&
|
||||
pFileInfo->GetNZBInfo() == m_ParChecker.GetPostInfo()->GetNZBInfo() &&
|
||||
SameParCollection(pFileInfo->GetFilename(), Util::BaseFileName(m_ParChecker.GetParFilename()));
|
||||
if (bSameCollection && !bDeleted)
|
||||
{
|
||||
char szFullFilename[1024];
|
||||
snprintf(szFullFilename, 1024, "%s%c%s", pFileInfo->GetNZBInfo()->GetDestDir(), (int)PATH_SEPARATOR, pFileInfo->GetFilename());
|
||||
szFullFilename[1024-1] = '\0';
|
||||
m_ParChecker.AddParFile(szFullFilename);
|
||||
|
||||
if (g_pOptions->GetParPauseQueue())
|
||||
{
|
||||
PauseDownload();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ParChecker.QueueChanged();
|
||||
}
|
||||
return bSameCollection;
|
||||
}
|
||||
|
||||
void ParCoordinator::ParCheckCompleted()
|
||||
{
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
|
||||
PostInfo* pPostInfo = m_ParChecker.GetPostInfo();
|
||||
|
||||
// Update ParStatus (accumulate result)
|
||||
if ((m_ParChecker.GetStatus() == ParChecker::psRepaired ||
|
||||
m_ParChecker.GetStatus() == ParChecker::psRepairNotNeeded) &&
|
||||
pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped)
|
||||
{
|
||||
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psSuccess);
|
||||
}
|
||||
else if (m_ParChecker.GetStatus() == ParChecker::psRepairPossible &&
|
||||
pPostInfo->GetNZBInfo()->GetParStatus() != NZBInfo::psFailure)
|
||||
{
|
||||
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psRepairPossible);
|
||||
}
|
||||
else
|
||||
{
|
||||
pPostInfo->GetNZBInfo()->SetParStatus(NZBInfo::psFailure);
|
||||
}
|
||||
|
||||
pPostInfo->SetWorking(false);
|
||||
pPostInfo->SetStage(PostInfo::ptQueued);
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 ParCoordinator::RequestMorePars(NZBInfo* pNZBInfo, const char* szParFilename, int iBlockNeeded, int* pBlockFound)
|
||||
{
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
|
||||
Blocks blocks;
|
||||
blocks.clear();
|
||||
int iBlockFound = 0;
|
||||
int iCurBlockFound = 0;
|
||||
|
||||
FindPars(pDownloadQueue, pNZBInfo, szParFilename, &blocks, true, true, &iCurBlockFound);
|
||||
iBlockFound += iCurBlockFound;
|
||||
if (iBlockFound < iBlockNeeded)
|
||||
{
|
||||
FindPars(pDownloadQueue, pNZBInfo, szParFilename, &blocks, true, false, &iCurBlockFound);
|
||||
iBlockFound += iCurBlockFound;
|
||||
}
|
||||
if (iBlockFound < iBlockNeeded)
|
||||
{
|
||||
FindPars(pDownloadQueue, pNZBInfo, szParFilename, &blocks, false, false, &iCurBlockFound);
|
||||
iBlockFound += iCurBlockFound;
|
||||
}
|
||||
|
||||
if (iBlockFound >= iBlockNeeded)
|
||||
{
|
||||
// 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())
|
||||
{
|
||||
m_ParChecker.PrintMessage(Message::mkInfo, "Unpausing %s%c%s for par-recovery", pNZBInfo->GetName(), (int)PATH_SEPARATOR, pBestBlockInfo->m_pFileInfo->GetFilename());
|
||||
pBestBlockInfo->m_pFileInfo->SetPaused(false);
|
||||
pBestBlockInfo->m_pFileInfo->SetExtraPriority(true);
|
||||
}
|
||||
iBlockNeeded -= pBestBlockInfo->m_iBlockCount;
|
||||
blocks.remove(pBestBlockInfo);
|
||||
delete pBestBlockInfo;
|
||||
}
|
||||
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())
|
||||
{
|
||||
m_ParChecker.PrintMessage(Message::mkInfo, "Unpausing %s%c%s for par-recovery", pNZBInfo->GetName(), (int)PATH_SEPARATOR, pBlockInfo->m_pFileInfo->GetFilename());
|
||||
pBlockInfo->m_pFileInfo->SetPaused(false);
|
||||
pBlockInfo->m_pFileInfo->SetExtraPriority(true);
|
||||
}
|
||||
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();
|
||||
|
||||
bool bOK = iBlockNeeded <= 0;
|
||||
|
||||
if (bOK && g_pOptions->GetParPauseQueue())
|
||||
{
|
||||
UnpauseDownload();
|
||||
}
|
||||
|
||||
return bOK;
|
||||
}
|
||||
|
||||
void ParCoordinator::FindPars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, const char* szParFilename,
|
||||
Blocks* pBlocks, bool bStrictParName, bool bExactParName, int* pBlockFound)
|
||||
{
|
||||
*pBlockFound = 0;
|
||||
|
||||
// extract base name from m_szParFilename (trim .par2-extension and possible .vol-part)
|
||||
char* szBaseParFilename = Util::BaseFileName(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 (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
int iBlocks = 0;
|
||||
if (pFileInfo->GetNZBInfo() == pNZBInfo &&
|
||||
ParseParFilename(pFileInfo->GetFilename(), NULL, &iBlocks) &&
|
||||
iBlocks > 0)
|
||||
{
|
||||
bool bUseFile = true;
|
||||
|
||||
if (bExactParName)
|
||||
{
|
||||
bUseFile = SameParCollection(pFileInfo->GetFilename(), Util::BaseFileName(szParFilename));
|
||||
}
|
||||
else 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';
|
||||
bUseFile = strstr(szLoFileName, szCandidateFileName);
|
||||
}
|
||||
}
|
||||
|
||||
bool bAlreadyAdded = false;
|
||||
// check if file is not in the list already
|
||||
if (bUseFile)
|
||||
{
|
||||
for (Blocks::iterator it = pBlocks->begin(); it != pBlocks->end(); it++)
|
||||
{
|
||||
BlockInfo* pBlockInfo = *it;
|
||||
if (pBlockInfo->m_pFileInfo == pFileInfo)
|
||||
{
|
||||
bAlreadyAdded = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
if (bUseFile && !bAlreadyAdded)
|
||||
{
|
||||
BlockInfo* pBlockInfo = new BlockInfo();
|
||||
pBlockInfo->m_pFileInfo = pFileInfo;
|
||||
pBlockInfo->m_iBlockCount = iBlocks;
|
||||
pBlocks->push_back(pBlockInfo);
|
||||
*pBlockFound += iBlocks;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParCoordinator::UpdateParCheckProgress()
|
||||
{
|
||||
g_pQueueCoordinator->LockQueue();
|
||||
|
||||
PostInfo* pPostInfo = m_ParChecker.GetPostInfo();
|
||||
if (m_ParChecker.GetFileProgress() == 0)
|
||||
{
|
||||
pPostInfo->SetProgressLabel(m_ParChecker.GetProgressLabel());
|
||||
}
|
||||
pPostInfo->SetFileProgress(m_ParChecker.GetFileProgress());
|
||||
pPostInfo->SetStageProgress(m_ParChecker.GetStageProgress());
|
||||
PostInfo::EStage StageKind[] = { PostInfo::ptLoadingPars, PostInfo::ptVerifyingSources, PostInfo::ptRepairing, PostInfo::ptVerifyingRepaired };
|
||||
PostInfo::EStage eStage = StageKind[m_ParChecker.GetStage()];
|
||||
time_t tCurrent = time(NULL);
|
||||
|
||||
if (!pPostInfo->GetStartTime())
|
||||
{
|
||||
pPostInfo->SetStartTime(tCurrent);
|
||||
}
|
||||
|
||||
if (pPostInfo->GetStage() != eStage)
|
||||
{
|
||||
pPostInfo->SetStage(eStage);
|
||||
pPostInfo->SetStageTime(tCurrent);
|
||||
}
|
||||
|
||||
bool bParCancel = false;
|
||||
#ifdef HAVE_PAR2_CANCEL
|
||||
if (!m_ParChecker.GetCancelled())
|
||||
{
|
||||
if ((g_pOptions->GetParTimeLimit() > 0) &&
|
||||
m_ParChecker.GetStage() == ParChecker::ptRepairing &&
|
||||
((g_pOptions->GetParTimeLimit() > 5 && tCurrent - pPostInfo->GetStageTime() > 5 * 60) ||
|
||||
(g_pOptions->GetParTimeLimit() <= 5 && tCurrent - pPostInfo->GetStageTime() > 1 * 60)))
|
||||
{
|
||||
// first five (or one) minutes elapsed, now can check the estimated time
|
||||
int iEstimatedRepairTime = (int)((tCurrent - pPostInfo->GetStartTime()) * 1000 /
|
||||
(pPostInfo->GetStageProgress() > 0 ? pPostInfo->GetStageProgress() : 1));
|
||||
if (iEstimatedRepairTime > g_pOptions->GetParTimeLimit() * 60)
|
||||
{
|
||||
debug("Estimated repair time %i seconds", iEstimatedRepairTime);
|
||||
m_ParChecker.PrintMessage(Message::mkWarning, "Cancelling par-repair for %s, estimated repair time (%i minutes) exceeds allowed repair time", m_ParChecker.GetInfoName(), iEstimatedRepairTime / 60);
|
||||
bParCancel = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (bParCancel)
|
||||
{
|
||||
m_ParChecker.Cancel();
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
|
||||
CheckPauseState(pPostInfo);
|
||||
}
|
||||
|
||||
void ParCoordinator::CheckPauseState(PostInfo* pPostInfo)
|
||||
{
|
||||
if (g_pOptions->GetPausePostProcess())
|
||||
{
|
||||
time_t tStageTime = pPostInfo->GetStageTime();
|
||||
time_t tStartTime = pPostInfo->GetStartTime();
|
||||
time_t tWaitTime = time(NULL);
|
||||
|
||||
// wait until Post-processor is unpaused
|
||||
while (g_pOptions->GetPausePostProcess() && !m_bStopped)
|
||||
{
|
||||
usleep(100 * 1000);
|
||||
|
||||
// update time stamps
|
||||
|
||||
time_t tDelta = time(NULL) - tWaitTime;
|
||||
|
||||
if (tStageTime > 0)
|
||||
{
|
||||
pPostInfo->SetStageTime(tStageTime + tDelta);
|
||||
}
|
||||
|
||||
if (tStartTime > 0)
|
||||
{
|
||||
pPostInfo->SetStartTime(tStartTime + tDelta);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParCoordinator::ParRenameCompleted()
|
||||
{
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
|
||||
PostInfo* pPostInfo = m_ParRenamer.GetPostInfo();
|
||||
pPostInfo->GetNZBInfo()->SetRenameStatus(m_ParRenamer.GetStatus() == ParRenamer::psSuccess ? NZBInfo::rsSuccess : NZBInfo::rsFailure);
|
||||
pPostInfo->SetWorking(false);
|
||||
pPostInfo->SetStage(PostInfo::ptQueued);
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
}
|
||||
|
||||
void ParCoordinator::UpdateParRenameProgress()
|
||||
{
|
||||
g_pQueueCoordinator->LockQueue();
|
||||
|
||||
PostInfo* pPostInfo = m_ParRenamer.GetPostInfo();
|
||||
pPostInfo->SetProgressLabel(m_ParRenamer.GetProgressLabel());
|
||||
pPostInfo->SetStageProgress(m_ParRenamer.GetStageProgress());
|
||||
time_t tCurrent = time(NULL);
|
||||
|
||||
if (!pPostInfo->GetStartTime())
|
||||
{
|
||||
pPostInfo->SetStartTime(tCurrent);
|
||||
}
|
||||
|
||||
if (pPostInfo->GetStage() != PostInfo::ptRenaming)
|
||||
{
|
||||
pPostInfo->SetStage(PostInfo::ptRenaming);
|
||||
pPostInfo->SetStageTime(tCurrent);
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
|
||||
CheckPauseState(pPostInfo);
|
||||
}
|
||||
|
||||
void ParCoordinator::PrintMessage(PostInfo* pPostInfo, Message::EKind eKind, const char* szFormat, ...)
|
||||
{
|
||||
char szText[1024];
|
||||
va_list args;
|
||||
va_start(args, szFormat);
|
||||
vsnprintf(szText, 1024, szFormat, args);
|
||||
va_end(args);
|
||||
szText[1024-1] = '\0';
|
||||
|
||||
pPostInfo->AppendMessage(eKind, szText);
|
||||
|
||||
switch (eKind)
|
||||
{
|
||||
case Message::mkDetail:
|
||||
detail("%s", szText);
|
||||
break;
|
||||
|
||||
case Message::mkInfo:
|
||||
info("%s", szText);
|
||||
break;
|
||||
|
||||
case Message::mkWarning:
|
||||
warn("%s", szText);
|
||||
break;
|
||||
|
||||
case Message::mkError:
|
||||
error("%s", szText);
|
||||
break;
|
||||
|
||||
case Message::mkDebug:
|
||||
debug("%s", szText);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
130
ParCoordinator.h
Normal file
130
ParCoordinator.h
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007-2013 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef PARCOORDINATOR_H
|
||||
#define PARCOORDINATOR_H
|
||||
|
||||
#include <list>
|
||||
#include <deque>
|
||||
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
#include "ParChecker.h"
|
||||
#include "ParRenamer.h"
|
||||
#endif
|
||||
|
||||
class ParCoordinator
|
||||
{
|
||||
private:
|
||||
#ifndef DISABLE_PARCHECK
|
||||
class PostParChecker: public ParChecker
|
||||
{
|
||||
private:
|
||||
ParCoordinator* m_pOwner;
|
||||
PostInfo* m_pPostInfo;
|
||||
protected:
|
||||
virtual bool RequestMorePars(int iBlockNeeded, int* pBlockFound);
|
||||
virtual void UpdateProgress();
|
||||
virtual void Completed() { m_pOwner->ParCheckCompleted(); }
|
||||
virtual void PrintMessage(Message::EKind eKind, const char* szFormat, ...);
|
||||
public:
|
||||
PostInfo* GetPostInfo() { return m_pPostInfo; }
|
||||
void SetPostInfo(PostInfo* pPostInfo) { m_pPostInfo = pPostInfo; }
|
||||
|
||||
friend class ParCoordinator;
|
||||
};
|
||||
|
||||
class PostParRenamer: public ParRenamer
|
||||
{
|
||||
private:
|
||||
ParCoordinator* m_pOwner;
|
||||
PostInfo* m_pPostInfo;
|
||||
protected:
|
||||
virtual void UpdateProgress();
|
||||
virtual void Completed() { m_pOwner->ParRenameCompleted(); }
|
||||
virtual void PrintMessage(Message::EKind eKind, const char* szFormat, ...);
|
||||
public:
|
||||
PostInfo* GetPostInfo() { return m_pPostInfo; }
|
||||
void SetPostInfo(PostInfo* pPostInfo) { m_pPostInfo = pPostInfo; }
|
||||
|
||||
friend class ParCoordinator;
|
||||
};
|
||||
|
||||
struct BlockInfo
|
||||
{
|
||||
FileInfo* m_pFileInfo;
|
||||
int m_iBlockCount;
|
||||
};
|
||||
|
||||
typedef std::list<BlockInfo*> Blocks;
|
||||
|
||||
enum EJobKind
|
||||
{
|
||||
jkParCheck,
|
||||
jkParRename
|
||||
};
|
||||
|
||||
private:
|
||||
PostParChecker m_ParChecker;
|
||||
bool m_bStopped;
|
||||
PostParRenamer m_ParRenamer;
|
||||
EJobKind m_eCurrentJob;
|
||||
|
||||
protected:
|
||||
virtual bool PauseDownload() = 0;
|
||||
virtual bool UnpauseDownload() = 0;
|
||||
void UpdateParCheckProgress();
|
||||
void UpdateParRenameProgress();
|
||||
void ParCheckCompleted();
|
||||
void ParRenameCompleted();
|
||||
void CheckPauseState(PostInfo* pPostInfo);
|
||||
bool RequestMorePars(NZBInfo* pNZBInfo, const char* szParFilename, int iBlockNeeded, int* pBlockFound);
|
||||
void PrintMessage(PostInfo* pPostInfo, Message::EKind eKind, const char* szFormat, ...);
|
||||
#endif
|
||||
|
||||
public:
|
||||
typedef std::deque<char*> FileList;
|
||||
|
||||
public:
|
||||
ParCoordinator();
|
||||
virtual ~ParCoordinator();
|
||||
static bool FindMainPars(const char* szPath, FileList* pFileList);
|
||||
static bool ParseParFilename(const char* szParFilename, int* iBaseNameLen, int* iBlocks);
|
||||
static bool SameParCollection(const char* szFilename1, const char* szFilename2);
|
||||
void PausePars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
bool AddPar(FileInfo* pFileInfo, bool bDeleted);
|
||||
void FindPars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, const char* szParFilename,
|
||||
Blocks* pBlocks, bool bStrictParName, bool bExactParName, int* pBlockFound);
|
||||
void StartParCheckJob(PostInfo* pPostInfo);
|
||||
void StartParRenameJob(PostInfo* pPostInfo);
|
||||
void Stop();
|
||||
bool Cancel();
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
345
ParRenamer.cpp
Normal file
345
ParRenamer.cpp
Normal file
@@ -0,0 +1,345 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2013 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 <stdio.h>
|
||||
#include <ctype.h>
|
||||
#ifdef WIN32
|
||||
#include <par2cmdline.h>
|
||||
#include <par2repairer.h>
|
||||
#include <md5.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <libpar2/par2cmdline.h>
|
||||
#include <libpar2/par2repairer.h>
|
||||
#include <libpar2/md5.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "ParRenamer.h"
|
||||
#include "ParCoordinator.h"
|
||||
#include "Log.h"
|
||||
#include "Options.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
class ParRenamerRepairer : public Par2Repairer
|
||||
{
|
||||
public:
|
||||
friend class ParRenamer;
|
||||
};
|
||||
|
||||
ParRenamer::FileHash::FileHash(const char* szFilename, const char* szHash)
|
||||
{
|
||||
m_szFilename = strdup(szFilename);
|
||||
m_szHash = strdup(szHash);
|
||||
}
|
||||
|
||||
ParRenamer::FileHash::~FileHash()
|
||||
{
|
||||
free(m_szFilename);
|
||||
free(m_szHash);
|
||||
}
|
||||
|
||||
ParRenamer::ParRenamer()
|
||||
{
|
||||
debug("Creating ParRenamer");
|
||||
|
||||
m_eStatus = psFailed;
|
||||
m_szDestDir = NULL;
|
||||
m_szInfoName = NULL;
|
||||
m_szProgressLabel = (char*)malloc(1024);
|
||||
m_iStageProgress = 0;
|
||||
m_bCancelled = false;
|
||||
}
|
||||
|
||||
ParRenamer::~ParRenamer()
|
||||
{
|
||||
debug("Destroying ParRenamer");
|
||||
|
||||
free(m_szDestDir);
|
||||
free(m_szInfoName);
|
||||
free(m_szProgressLabel);
|
||||
|
||||
Cleanup();
|
||||
}
|
||||
|
||||
void ParRenamer::Cleanup()
|
||||
{
|
||||
ClearHashList();
|
||||
|
||||
for (DirList::iterator it = m_DirList.begin(); it != m_DirList.end(); it++)
|
||||
{
|
||||
free(*it);
|
||||
}
|
||||
m_DirList.clear();
|
||||
}
|
||||
|
||||
void ParRenamer::ClearHashList()
|
||||
{
|
||||
for (FileHashList::iterator it = m_FileHashList.begin(); it != m_FileHashList.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_FileHashList.clear();
|
||||
}
|
||||
|
||||
void ParRenamer::SetDestDir(const char * szDestDir)
|
||||
{
|
||||
free(m_szDestDir);
|
||||
m_szDestDir = strdup(szDestDir);
|
||||
}
|
||||
|
||||
void ParRenamer::SetInfoName(const char * szInfoName)
|
||||
{
|
||||
free(m_szInfoName);
|
||||
m_szInfoName = strdup(szInfoName);
|
||||
}
|
||||
|
||||
void ParRenamer::Cancel()
|
||||
{
|
||||
m_bCancelled = true;
|
||||
}
|
||||
|
||||
void ParRenamer::Run()
|
||||
{
|
||||
Cleanup();
|
||||
m_bCancelled = false;
|
||||
m_iFileCount = 0;
|
||||
m_iCurFile = 0;
|
||||
m_iRenamedCount = 0;
|
||||
m_eStatus = psFailed;
|
||||
|
||||
snprintf(m_szProgressLabel, 1024, "Checking renamed files for %s", m_szInfoName);
|
||||
m_szProgressLabel[1024-1] = '\0';
|
||||
m_iStageProgress = 0;
|
||||
UpdateProgress();
|
||||
|
||||
BuildDirList(m_szDestDir);
|
||||
|
||||
for (DirList::iterator it = m_DirList.begin(); it != m_DirList.end(); it++)
|
||||
{
|
||||
char* szDestDir = *it;
|
||||
debug("Checking %s", szDestDir);
|
||||
ClearHashList();
|
||||
LoadParFiles(szDestDir);
|
||||
CheckFiles(szDestDir);
|
||||
}
|
||||
|
||||
if (m_bCancelled)
|
||||
{
|
||||
PrintMessage(Message::mkWarning, "Renaming cancelled for %s", m_szInfoName);
|
||||
}
|
||||
else if (m_iRenamedCount > 0)
|
||||
{
|
||||
PrintMessage(Message::mkInfo, "Successfully renamed %i file(s) for %s", m_iRenamedCount, m_szInfoName);
|
||||
m_eStatus = psSuccess;
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintMessage(Message::mkInfo, "No renamed files found for %s", m_szInfoName);
|
||||
}
|
||||
|
||||
Cleanup();
|
||||
Completed();
|
||||
}
|
||||
|
||||
void ParRenamer::BuildDirList(const char* szDestDir)
|
||||
{
|
||||
m_DirList.push_back(strdup(szDestDir));
|
||||
|
||||
char* szFullFilename = (char*)malloc(1024);
|
||||
DirBrowser* pDirBrowser = new DirBrowser(szDestDir);
|
||||
|
||||
while (const char* filename = pDirBrowser->Next())
|
||||
{
|
||||
if (strcmp(filename, ".") && strcmp(filename, "..") && !m_bCancelled)
|
||||
{
|
||||
snprintf(szFullFilename, 1024, "%s%c%s", szDestDir, PATH_SEPARATOR, filename);
|
||||
szFullFilename[1024-1] = '\0';
|
||||
|
||||
if (Util::DirectoryExists(szFullFilename))
|
||||
{
|
||||
BuildDirList(szFullFilename);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_iFileCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(szFullFilename);
|
||||
delete pDirBrowser;
|
||||
}
|
||||
|
||||
void ParRenamer::LoadParFiles(const char* szDestDir)
|
||||
{
|
||||
ParCoordinator::FileList parFileList;
|
||||
ParCoordinator::FindMainPars(szDestDir, &parFileList);
|
||||
|
||||
for (ParCoordinator::FileList::iterator it = parFileList.begin(); it != parFileList.end(); it++)
|
||||
{
|
||||
char* szParFilename = *it;
|
||||
|
||||
char szFullParFilename[1024];
|
||||
snprintf(szFullParFilename, 1024, "%s%c%s", szDestDir, PATH_SEPARATOR, szParFilename);
|
||||
szFullParFilename[1024-1] = '\0';
|
||||
|
||||
LoadParFile(szFullParFilename);
|
||||
|
||||
free(*it);
|
||||
}
|
||||
}
|
||||
|
||||
void ParRenamer::LoadParFile(const char* szParFilename)
|
||||
{
|
||||
ParRenamerRepairer* pRepairer = new ParRenamerRepairer();
|
||||
|
||||
if (!pRepairer->LoadPacketsFromFile(szParFilename))
|
||||
{
|
||||
PrintMessage(Message::mkWarning, "Could not load par2-file %s", szParFilename);
|
||||
delete pRepairer;
|
||||
return;
|
||||
}
|
||||
|
||||
for (map<MD5Hash, Par2RepairerSourceFile*>::iterator it = pRepairer->sourcefilemap.begin(); it != pRepairer->sourcefilemap.end(); it++)
|
||||
{
|
||||
if (m_bCancelled)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
Par2RepairerSourceFile* sourceFile = (*it).second;
|
||||
m_FileHashList.push_back(new FileHash(sourceFile->GetDescriptionPacket()->FileName().c_str(),
|
||||
sourceFile->GetDescriptionPacket()->Hash16k().print().c_str()));
|
||||
}
|
||||
|
||||
delete pRepairer;
|
||||
}
|
||||
|
||||
void ParRenamer::CheckFiles(const char* szDestDir)
|
||||
{
|
||||
DirBrowser dir(szDestDir);
|
||||
while (const char* filename = dir.Next())
|
||||
{
|
||||
if (strcmp(filename, ".") && strcmp(filename, "..") && !m_bCancelled)
|
||||
{
|
||||
char szFullFilename[1024];
|
||||
snprintf(szFullFilename, 1024, "%s%c%s", szDestDir, PATH_SEPARATOR, filename);
|
||||
szFullFilename[1024-1] = '\0';
|
||||
|
||||
if (!Util::DirectoryExists(szFullFilename))
|
||||
{
|
||||
snprintf(m_szProgressLabel, 1024, "Checking file %s", filename);
|
||||
m_szProgressLabel[1024-1] = '\0';
|
||||
m_iStageProgress = m_iCurFile * 1000 / m_iFileCount;
|
||||
UpdateProgress();
|
||||
m_iCurFile++;
|
||||
|
||||
CheckFile(szDestDir, szFullFilename);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParRenamer::CheckFile(const char* szDestDir, const char* szFilename)
|
||||
{
|
||||
debug("Computing hash for %s", szFilename);
|
||||
|
||||
const int iBlockSize = 16*1024;
|
||||
|
||||
FILE* pFile = fopen(szFilename, "rb");
|
||||
if (!pFile)
|
||||
{
|
||||
PrintMessage(Message::mkError, "Could not open file %s", szFilename);
|
||||
return;
|
||||
}
|
||||
|
||||
// load first 16K of the file into buffer
|
||||
|
||||
void* pBuffer = malloc(iBlockSize);
|
||||
|
||||
int iReadBytes = fread(pBuffer, 1, iBlockSize, pFile);
|
||||
int iError = ferror(pFile);
|
||||
if (iReadBytes != iBlockSize && iError)
|
||||
{
|
||||
PrintMessage(Message::mkError, "Could not read file %s", szFilename);
|
||||
return;
|
||||
}
|
||||
|
||||
fclose(pFile);
|
||||
|
||||
MD5Hash hash16k;
|
||||
MD5Context context;
|
||||
context.Update(pBuffer, iReadBytes);
|
||||
context.Final(hash16k);
|
||||
|
||||
free(pBuffer);
|
||||
|
||||
debug("file: %s; hash16k: %s", Util::BaseFileName(szFilename), hash16k.print().c_str());
|
||||
|
||||
for (FileHashList::iterator it = m_FileHashList.begin(); it != m_FileHashList.end(); it++)
|
||||
{
|
||||
FileHash* pFileHash = *it;
|
||||
if (!strcmp(pFileHash->GetHash(), hash16k.print().c_str()))
|
||||
{
|
||||
debug("Found correct filename: %s", pFileHash->GetFilename());
|
||||
|
||||
char szDstFilename[1024];
|
||||
snprintf(szDstFilename, 1024, "%s%c%s", szDestDir, PATH_SEPARATOR, pFileHash->GetFilename());
|
||||
szDstFilename[1024-1] = '\0';
|
||||
|
||||
if (!Util::FileExists(szDstFilename))
|
||||
{
|
||||
PrintMessage(Message::mkInfo, "Renaming %s to %s", Util::BaseFileName(szFilename), pFileHash->GetFilename());
|
||||
if (Util::MoveFile(szFilename, szDstFilename))
|
||||
{
|
||||
m_iRenamedCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintMessage(Message::mkError, "Could not rename %s to %s", szFilename, szDstFilename);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
106
ParRenamer.h
Normal file
106
ParRenamer.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2013 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef PARRENAMER_H
|
||||
#define PARRENAMER_H
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include "Thread.h"
|
||||
#include "Log.h"
|
||||
|
||||
class ParRenamer : public Thread
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
psFailed,
|
||||
psSuccess
|
||||
};
|
||||
|
||||
class FileHash
|
||||
{
|
||||
private:
|
||||
char* m_szFilename;
|
||||
char* m_szHash;
|
||||
|
||||
public:
|
||||
FileHash(const char* szFilename, const char* szHash);
|
||||
~FileHash();
|
||||
const char* GetFilename() { return m_szFilename; }
|
||||
const char* GetHash() { return m_szHash; }
|
||||
};
|
||||
|
||||
typedef std::deque<FileHash*> FileHashList;
|
||||
|
||||
typedef std::deque<char*> DirList;
|
||||
|
||||
private:
|
||||
char* m_szInfoName;
|
||||
char* m_szDestDir;
|
||||
EStatus m_eStatus;
|
||||
char* m_szProgressLabel;
|
||||
int m_iStageProgress;
|
||||
bool m_bCancelled;
|
||||
DirList m_DirList;
|
||||
FileHashList m_FileHashList;
|
||||
int m_iFileCount;
|
||||
int m_iCurFile;
|
||||
int m_iRenamedCount;
|
||||
|
||||
void Cleanup();
|
||||
void ClearHashList();
|
||||
void BuildDirList(const char* szDestDir);
|
||||
void CheckDir(const char* szDestDir);
|
||||
void LoadParFiles(const char* szDestDir);
|
||||
void LoadParFile(const char* szParFilename);
|
||||
void CheckFiles(const char* szDestDir);
|
||||
void CheckFile(const char* szDestDir, const char* szFilename);
|
||||
|
||||
protected:
|
||||
virtual void UpdateProgress() {}
|
||||
virtual void Completed() {}
|
||||
virtual void PrintMessage(Message::EKind eKind, const char* szFormat, ...) {}
|
||||
const char* GetProgressLabel() { return m_szProgressLabel; }
|
||||
int GetStageProgress() { return m_iStageProgress; }
|
||||
|
||||
public:
|
||||
ParRenamer();
|
||||
virtual ~ParRenamer();
|
||||
virtual void Run();
|
||||
void SetDestDir(const char* szDestDir);
|
||||
const char* GetInfoName() { return m_szInfoName; }
|
||||
void SetInfoName(const char* szInfoName);
|
||||
void SetStatus(EStatus eStatus);
|
||||
EStatus GetStatus() { return m_eStatus; }
|
||||
void Cancel();
|
||||
bool GetCancelled() { return m_bCancelled; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
154
PostInfo.cpp
154
PostInfo.cpp
@@ -1,154 +0,0 @@
|
||||
/*
|
||||
* This file is 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 "nzbget.h"
|
||||
#include "PostInfo.h"
|
||||
#include "Options.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
int PostInfo::m_iIDGen = 0;
|
||||
|
||||
PostInfo::PostInfo()
|
||||
{
|
||||
debug("Creating PostInfo");
|
||||
|
||||
m_szNZBFilename = NULL;
|
||||
m_szDestDir = NULL;
|
||||
m_szParFilename = NULL;
|
||||
m_szInfoName = NULL;
|
||||
m_bWorking = false;
|
||||
m_bParCheck = false;
|
||||
m_iParStatus = 0;
|
||||
m_bParFailed = false;
|
||||
m_szProgressLabel = strdup("");
|
||||
m_iFileProgress = 0;
|
||||
m_iStageProgress = 0;
|
||||
m_tStartTime = 0;
|
||||
m_tStageTime = 0;
|
||||
m_eStage = ptQueued;
|
||||
m_pScriptThread = NULL;
|
||||
m_Messages.clear();
|
||||
m_iIDGen++;
|
||||
m_iID = m_iIDGen;
|
||||
}
|
||||
|
||||
PostInfo::~ PostInfo()
|
||||
{
|
||||
debug("Destroying PostInfo");
|
||||
|
||||
if (m_szNZBFilename)
|
||||
{
|
||||
free(m_szNZBFilename);
|
||||
}
|
||||
if (m_szDestDir)
|
||||
{
|
||||
free(m_szDestDir);
|
||||
}
|
||||
if (m_szParFilename)
|
||||
{
|
||||
free(m_szParFilename);
|
||||
}
|
||||
if (m_szInfoName)
|
||||
{
|
||||
free(m_szInfoName);
|
||||
}
|
||||
if (m_szProgressLabel)
|
||||
{
|
||||
free(m_szProgressLabel);
|
||||
}
|
||||
for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Messages.clear();
|
||||
}
|
||||
|
||||
void PostInfo::SetNZBFilename(const char* szNZBFilename)
|
||||
{
|
||||
m_szNZBFilename = strdup(szNZBFilename);
|
||||
}
|
||||
|
||||
void PostInfo::SetDestDir(const char* szDestDir)
|
||||
{
|
||||
m_szDestDir = strdup(szDestDir);
|
||||
}
|
||||
|
||||
void PostInfo::SetParFilename(const char* szParFilename)
|
||||
{
|
||||
m_szParFilename = strdup(szParFilename);
|
||||
}
|
||||
|
||||
void PostInfo::SetInfoName(const char* szInfoName)
|
||||
{
|
||||
m_szInfoName = strdup(szInfoName);
|
||||
}
|
||||
|
||||
void PostInfo::SetProgressLabel(const char* szProgressLabel)
|
||||
{
|
||||
if (m_szProgressLabel)
|
||||
{
|
||||
free(m_szProgressLabel);
|
||||
}
|
||||
m_szProgressLabel = strdup(szProgressLabel);
|
||||
}
|
||||
|
||||
PostInfo::Messages* PostInfo::LockMessages()
|
||||
{
|
||||
m_mutexLog.Lock();
|
||||
return &m_Messages;
|
||||
}
|
||||
|
||||
void PostInfo::UnlockMessages()
|
||||
{
|
||||
m_mutexLog.Unlock();
|
||||
}
|
||||
|
||||
void PostInfo::AppendMessage(Message::EKind eKind, const char * szText)
|
||||
{
|
||||
Message* pMessage = new Message(++m_iIDGen, eKind, time(NULL), szText);
|
||||
|
||||
m_mutexLog.Lock();
|
||||
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();
|
||||
}
|
||||
m_mutexLog.Unlock();
|
||||
}
|
||||
113
PostInfo.h
113
PostInfo.h
@@ -1,113 +0,0 @@
|
||||
/*
|
||||
* This file is 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 POSTINFO_H
|
||||
#define POSTINFO_H
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include "Log.h"
|
||||
#include "Thread.h"
|
||||
|
||||
class PostInfo
|
||||
{
|
||||
public:
|
||||
enum EStage
|
||||
{
|
||||
ptQueued,
|
||||
ptLoadingPars,
|
||||
ptVerifyingSources,
|
||||
ptRepairing,
|
||||
ptVerifyingRepaired,
|
||||
ptExecutingScript,
|
||||
ptFinished
|
||||
};
|
||||
|
||||
typedef std::deque<Message*> Messages;
|
||||
|
||||
private:
|
||||
int m_iID;
|
||||
char* m_szNZBFilename;
|
||||
char* m_szDestDir;
|
||||
char* m_szParFilename;
|
||||
char* m_szInfoName;
|
||||
bool m_bWorking;
|
||||
bool m_bParCheck;
|
||||
int m_iParStatus;
|
||||
bool m_bParFailed;
|
||||
EStage m_eStage;
|
||||
char* m_szProgressLabel;
|
||||
int m_iFileProgress;
|
||||
int m_iStageProgress;
|
||||
time_t m_tStartTime;
|
||||
time_t m_tStageTime;
|
||||
Thread* m_pScriptThread;
|
||||
|
||||
Mutex m_mutexLog;
|
||||
Messages m_Messages;
|
||||
static int m_iIDGen;
|
||||
|
||||
public:
|
||||
PostInfo();
|
||||
~PostInfo();
|
||||
int GetID() { return m_iID; }
|
||||
const char* GetNZBFilename() { return m_szNZBFilename; }
|
||||
void SetNZBFilename(const char* szNZBFilename);
|
||||
const char* GetDestDir() { return m_szDestDir; }
|
||||
void SetDestDir(const char* szDestDir);
|
||||
const char* GetParFilename() { return m_szParFilename; }
|
||||
void SetParFilename(const char* szParFilename);
|
||||
const char* GetInfoName() { return m_szInfoName; }
|
||||
void SetInfoName(const char* szInfoName);
|
||||
EStage GetStage() { return m_eStage; }
|
||||
void SetStage(EStage eStage) { m_eStage = eStage; }
|
||||
void SetProgressLabel(const char* szProgressLabel);
|
||||
const char* GetProgressLabel() { return m_szProgressLabel; }
|
||||
int GetFileProgress() { return m_iFileProgress; }
|
||||
void SetFileProgress(int iFileProgress) { m_iFileProgress = iFileProgress; }
|
||||
int GetStageProgress() { return m_iStageProgress; }
|
||||
void SetStageProgress(int iStageProgress) { m_iStageProgress = iStageProgress; }
|
||||
time_t GetStartTime() { return m_tStartTime; }
|
||||
void SetStartTime(time_t tStartTime) { m_tStartTime = tStartTime; }
|
||||
time_t GetStageTime() { return m_tStageTime; }
|
||||
void SetStageTime(time_t tStageTime) { m_tStageTime = tStageTime; }
|
||||
bool GetWorking() { return m_bWorking; }
|
||||
void SetWorking(bool bWorking) { m_bWorking = bWorking; }
|
||||
bool GetParCheck() { return m_bParCheck; }
|
||||
void SetParCheck(bool bParCheck) { m_bParCheck = bParCheck; }
|
||||
int GetParStatus() { return m_iParStatus; }
|
||||
void SetParStatus(int iParStatus) { m_iParStatus = iParStatus; }
|
||||
bool GetParFailed() { return m_bParFailed; }
|
||||
void SetParFailed(bool bParFailed) { m_bParFailed = bParFailed; }
|
||||
void AppendMessage(Message::EKind eKind, const char* szText);
|
||||
Thread* GetScriptThread() { return m_pScriptThread; }
|
||||
void SetScriptThread(Thread* pScriptThread) { m_pScriptThread = pScriptThread; }
|
||||
Messages* LockMessages();
|
||||
void UnlockMessages();
|
||||
};
|
||||
|
||||
typedef std::deque<PostInfo*> PostQueue;
|
||||
|
||||
#endif
|
||||
2058
PrePostProcessor.cpp
2058
PrePostProcessor.cpp
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2013 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,7 +15,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -31,95 +31,105 @@
|
||||
#include "Thread.h"
|
||||
#include "Observer.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "PostInfo.h"
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
#include "ParChecker.h"
|
||||
#endif
|
||||
#include "ParCoordinator.h"
|
||||
#include "DupeCoordinator.h"
|
||||
|
||||
class PrePostProcessor : public Thread
|
||||
{
|
||||
private:
|
||||
typedef std::deque<char*> FileList;
|
||||
public:
|
||||
// NOTE: changes to this enum must be synced with "eRemoteEditAction" in unit "MessageBase.h"
|
||||
enum EEditAction
|
||||
{
|
||||
eaPostMoveOffset = 51, // move post to m_iOffset relative to the current position in post-queue
|
||||
eaPostMoveTop,
|
||||
eaPostMoveBottom,
|
||||
eaPostDelete,
|
||||
eaHistoryDelete,
|
||||
eaHistoryFinalDelete,
|
||||
eaHistoryReturn,
|
||||
eaHistoryProcess,
|
||||
eaHistoryRedownload,
|
||||
eaHistorySetParameter,
|
||||
eaHistorySetDupeKey,
|
||||
eaHistorySetDupeScore,
|
||||
eaHistorySetDupeMode,
|
||||
eaHistorySetDupeBackup,
|
||||
eaHistoryMarkBad,
|
||||
eaHistoryMarkGood
|
||||
};
|
||||
|
||||
private:
|
||||
class QueueCoordinatorObserver: public Observer
|
||||
{
|
||||
public:
|
||||
PrePostProcessor* owner;
|
||||
virtual void Update(Subject* Caller, void* Aspect) { owner->QueueCoordinatorUpdate(Caller, Aspect); }
|
||||
PrePostProcessor* m_pOwner;
|
||||
virtual void Update(Subject* Caller, void* Aspect) { m_pOwner->QueueCoordinatorUpdate(Caller, Aspect); }
|
||||
};
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
class ParCheckerObserver: public Observer
|
||||
{
|
||||
public:
|
||||
PrePostProcessor* owner;
|
||||
virtual void Update(Subject* Caller, void* Aspect) { owner->ParCheckerUpdate(Caller, Aspect); }
|
||||
};
|
||||
|
||||
class PostParChecker: public ParChecker
|
||||
class PostParCoordinator: public ParCoordinator
|
||||
{
|
||||
private:
|
||||
PrePostProcessor* m_Owner;
|
||||
PrePostProcessor* m_pOwner;
|
||||
protected:
|
||||
virtual bool RequestMorePars(int iBlockNeeded, int* pBlockFound);
|
||||
virtual void UpdateProgress();
|
||||
|
||||
virtual bool PauseDownload() { return m_pOwner->PauseDownload(); }
|
||||
virtual bool UnpauseDownload() { return m_pOwner->UnpauseDownload(); }
|
||||
friend class PrePostProcessor;
|
||||
};
|
||||
|
||||
struct BlockInfo
|
||||
class PostDupeCoordinator: public DupeCoordinator
|
||||
{
|
||||
FileInfo* m_pFileInfo;
|
||||
int m_iBlockCount;
|
||||
private:
|
||||
PrePostProcessor* m_pOwner;
|
||||
protected:
|
||||
virtual void HistoryRedownload(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo);
|
||||
virtual void DeleteQueuedFile(const char* szQueuedFile) { m_pOwner->DeleteQueuedFile(szQueuedFile); }
|
||||
friend class PrePostProcessor;
|
||||
};
|
||||
|
||||
typedef std::deque<BlockInfo*> Blocks;
|
||||
#endif
|
||||
|
||||
private:
|
||||
PostParCoordinator m_ParCoordinator;
|
||||
PostDupeCoordinator m_DupeCoordinator;
|
||||
QueueCoordinatorObserver m_QueueCoordinatorObserver;
|
||||
bool m_bHasMoreJobs;
|
||||
bool m_bPostScript;
|
||||
bool m_bSchedulerPauseChanged;
|
||||
bool m_bSchedulerPause;
|
||||
bool m_bPostPause;
|
||||
const char* m_szPauseReason;
|
||||
|
||||
void PausePars(DownloadQueue* pDownloadQueue, const char* szNZBFilename);
|
||||
void CheckIncomingNZBs();
|
||||
bool IsNZBFileCompleted(DownloadQueue* pDownloadQueue, const char* szNZBFilename,
|
||||
bool bIgnoreFirstInPostQueue, bool bIgnorePaused, bool bCheckPostQueue);
|
||||
bool CheckScript(FileInfo* pFileInfo);
|
||||
bool JobExists(PostQueue* pPostQueue, const char* szNZBFilename);
|
||||
bool ClearCompletedJobs(const char* szNZBFilename);
|
||||
bool IsNZBFileCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo,
|
||||
bool bIgnorePausedPars, bool bAllowOnlyOneDeleted);
|
||||
void CheckPostQueue();
|
||||
void JobCompleted(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
|
||||
void StartScriptJob(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
|
||||
void SavePostQueue();
|
||||
void SanitisePostQueue();
|
||||
void StartJob(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
|
||||
void SaveQueue(DownloadQueue* pDownloadQueue);
|
||||
void SanitisePostQueue(PostQueue* pPostQueue);
|
||||
void CheckDiskSpace();
|
||||
void ApplySchedulerState();
|
||||
void CheckScheduledResume();
|
||||
void UpdatePauseState(bool bNeedPause, const char* szReason);
|
||||
bool PauseDownload();
|
||||
bool UnpauseDownload();
|
||||
void NZBFound(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
|
||||
void NZBAdded(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
|
||||
void NZBDownloaded(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
|
||||
void NZBDeleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
|
||||
void NZBCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, bool bSaveQueue);
|
||||
void DeleteQueuedFile(const char* szQueuedFile);
|
||||
int FindGroupID(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
|
||||
bool PostQueueMove(IDList* pIDList, EEditAction eAction, int iOffset);
|
||||
bool PostQueueDelete(IDList* pIDList);
|
||||
bool HistoryEdit(IDList* pIDList, EEditAction eAction, int iOffset, const char* szText);
|
||||
void HistoryDelete(DownloadQueue* pDownloadQueue, HistoryList::iterator itHistory, HistoryInfo* pHistoryInfo, bool bFinal);
|
||||
void HistoryReturn(DownloadQueue* pDownloadQueue, HistoryList::iterator itHistory, HistoryInfo* pHistoryInfo, bool bReprocess);
|
||||
void HistoryRedownload(DownloadQueue* pDownloadQueue, HistoryList::iterator itHistory, HistoryInfo* pHistoryInfo, bool bRestorePauseState);
|
||||
void HistorySetParameter(HistoryInfo* pHistoryInfo, const char* szText);
|
||||
void HistorySetDupeParam(HistoryInfo* pHistoryInfo, EEditAction eAction, const char* szText);
|
||||
void HistoryTransformToDup(DownloadQueue* pDownloadQueue, HistoryInfo* pHistoryInfo, int rindex);
|
||||
void CheckHistory();
|
||||
void Cleanup();
|
||||
FileInfo* GetQueueGroup(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
|
||||
void DeletePostThread(PostInfo* pPostInfo);
|
||||
|
||||
Mutex m_mutexQueue;
|
||||
PostQueue m_PostQueue;
|
||||
PostQueue m_CompletedJobs;
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
PostParChecker m_ParChecker;
|
||||
ParCheckerObserver m_ParCheckerObserver;
|
||||
|
||||
void ParCheckerUpdate(Subject* Caller, void* Aspect);
|
||||
bool 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);
|
||||
int GetParCleanupQueueGroup(DownloadQueue* pDownloadQueue, const char* szNZBFilename);
|
||||
bool HasFailedParJobs(const char* szNZBFilename);
|
||||
bool ParJobExists(PostQueue* pPostQueue, const char* szParFilename);
|
||||
bool ParseParFilename(const char* szParFilename, int* iBaseNameLen, int* iBlocks);
|
||||
bool RequestMorePars(const char* szNZBFilename, const char* szParFilename, int iBlockNeeded, int* pBlockFound);
|
||||
void FindPars(DownloadQueue* pDownloadQueue, const char* szNZBFilename, const char* szParFilename,
|
||||
Blocks* pBlocks, bool bStrictParName, bool bExactParName, int* pBlockFound);
|
||||
void UpdateParProgress();
|
||||
void StartParJob(PostInfo* pPostInfo);
|
||||
#endif
|
||||
|
||||
public:
|
||||
PrePostProcessor();
|
||||
virtual ~PrePostProcessor();
|
||||
@@ -127,8 +137,7 @@ public:
|
||||
virtual void Stop();
|
||||
void QueueCoordinatorUpdate(Subject* Caller, void* Aspect);
|
||||
bool HasMoreJobs() { return m_bHasMoreJobs; }
|
||||
PostQueue* LockPostQueue();
|
||||
void UnlockPostQueue();
|
||||
bool QueueEditList(IDList* pIDList, EEditAction eAction, int iOffset, const char* szText);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* 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-2013 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -30,9 +30,6 @@
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <time.h>
|
||||
#ifdef WIN32
|
||||
#include <sys/timeb.h>
|
||||
#endif
|
||||
|
||||
#include "Thread.h"
|
||||
#include "NZBFile.h"
|
||||
@@ -42,22 +39,25 @@
|
||||
#include "QueueEditor.h"
|
||||
#include "NNTPConnection.h"
|
||||
|
||||
class QueueCoordinator : public Thread, public Observer, public Subject, public DownloadSpeedMeter
|
||||
class QueueCoordinator : public Thread, public Observer, public Subject, public DownloadSpeedMeter, public DownloadQueueHolder
|
||||
{
|
||||
public:
|
||||
typedef std::list<ArticleDownloader*> ActiveDownloads;
|
||||
typedef enum EAspectAction
|
||||
|
||||
enum EAspectAction
|
||||
{
|
||||
eaNZBFileFound,
|
||||
eaNZBFileAdded,
|
||||
eaFileCompleted,
|
||||
eaFileDeleted
|
||||
};
|
||||
typedef struct Aspect
|
||||
|
||||
struct Aspect
|
||||
{
|
||||
EAspectAction eAction;
|
||||
FileInfo* pFileInfo;
|
||||
DownloadQueue* pDownloadQueue;
|
||||
const char* szNZBFilename;
|
||||
NZBInfo* pNZBInfo;
|
||||
FileInfo* pFileInfo;
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -66,26 +66,43 @@ private:
|
||||
QueueEditor m_QueueEditor;
|
||||
Mutex m_mutexDownloadQueue;
|
||||
bool m_bHasMoreJobs;
|
||||
int m_iDownloadsLimit;
|
||||
int m_iServerConfigGeneration;
|
||||
|
||||
// statistics
|
||||
static const int SPEEDMETER_SECONDS = 5;
|
||||
int m_iSpeedBytes[SPEEDMETER_SECONDS];
|
||||
static const int SPEEDMETER_SLOTS = 30;
|
||||
static const int SPEEDMETER_SLOTSIZE = 1; //Split elapsed time into this number of secs.
|
||||
int m_iSpeedBytes[SPEEDMETER_SLOTS];
|
||||
int m_iSpeedTotalBytes;
|
||||
int m_iSpeedTime[SPEEDMETER_SLOTS];
|
||||
int m_iSpeedStartTime;
|
||||
time_t m_tSpeedCorrection;
|
||||
#ifdef HAVE_SPINLOCK
|
||||
SpinLock m_spinlockSpeed;
|
||||
#else
|
||||
Mutex m_mutexSpeed;
|
||||
#endif
|
||||
|
||||
int m_iSpeedBytesIndex;
|
||||
long long m_iAllBytes;
|
||||
time_t m_tStartServer;
|
||||
time_t m_tLastCheck;
|
||||
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 StatFileInfo(FileInfo* pFileInfo, bool bCompleted);
|
||||
void CheckHealth(FileInfo* pFileInfo);
|
||||
void ResetHangingDownloads();
|
||||
void ResetSpeedStat();
|
||||
void EnterLeaveStandBy(bool bEnter);
|
||||
void AdjustStartTime();
|
||||
void AdjustDownloadsLimit();
|
||||
|
||||
public:
|
||||
QueueCoordinator();
|
||||
@@ -96,20 +113,25 @@ public:
|
||||
|
||||
// statistics
|
||||
long long CalcRemainingSize();
|
||||
virtual float CalcCurrentDownloadSpeed();
|
||||
virtual int 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);
|
||||
void AddNZBFileToQueue(NZBFile* pNZBFile, bool bAddFirst);
|
||||
void AddFileInfosToFileQueue(NZBFile* pNZBFile, FileQueue* pFileQueue, bool bAddFirst);
|
||||
bool HasMoreJobs() { return m_bHasMoreJobs; }
|
||||
bool GetStandBy() { return m_bStandBy; }
|
||||
bool DeleteQueueEntry(FileInfo* pFileInfo);
|
||||
bool SetQueueEntryNZBCategory(NZBInfo* pNZBInfo, const char* szCategory);
|
||||
bool SetQueueEntryNZBName(NZBInfo* pNZBInfo, const char* szName);
|
||||
bool MergeQueueEntries(NZBInfo* pDestNZBInfo, NZBInfo* pSrcNZBInfo);
|
||||
bool SplitQueueEntries(FileQueue* pFileList, const char* szName, NZBInfo** pNewNZBInfo);
|
||||
void DiscardDiskFile(FileInfo* pFileInfo);
|
||||
QueueEditor* GetQueueEditor() { return &m_QueueEditor; }
|
||||
|
||||
|
||||
void LogDebugInfo();
|
||||
};
|
||||
|
||||
|
||||
536
QueueEditor.cpp
536
QueueEditor.cpp
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2013 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,7 +15,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -33,6 +33,10 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/stat.h>
|
||||
#include <set>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
@@ -71,7 +75,7 @@ QueueEditor::~QueueEditor()
|
||||
|
||||
FileInfo* QueueEditor::FindFileInfo(DownloadQueue* pDownloadQueue, int iID)
|
||||
{
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (pFileInfo->GetID() == iID)
|
||||
@@ -85,7 +89,7 @@ FileInfo* QueueEditor::FindFileInfo(DownloadQueue* pDownloadQueue, int iID)
|
||||
int QueueEditor::FindFileInfoEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo)
|
||||
{
|
||||
int iEntry = 0;
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo2 = *it;
|
||||
if (pFileInfo2 == pFileInfo)
|
||||
@@ -107,17 +111,24 @@ void QueueEditor::PauseUnpauseEntry(FileInfo* pFileInfo, bool bPause)
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes entry with index iEntry
|
||||
* Removes entry
|
||||
* returns true if successful, false if operation is not possible
|
||||
*/
|
||||
void QueueEditor::DeleteEntry(FileInfo* pFileInfo)
|
||||
{
|
||||
info("Deleting file %s from download queue", pFileInfo->GetFilename());
|
||||
if (pFileInfo->GetNZBInfo()->GetDeleting())
|
||||
{
|
||||
detail("Deleting file %s from download queue", pFileInfo->GetFilename());
|
||||
}
|
||||
else
|
||||
{
|
||||
info("Deleting file %s from download queue", pFileInfo->GetFilename());
|
||||
}
|
||||
g_pQueueCoordinator->DeleteQueueEntry(pFileInfo);
|
||||
}
|
||||
|
||||
/*
|
||||
* Moves entry identified with iID in the queue
|
||||
* Moves entry in the queue
|
||||
* returns true if successful, false if operation is not possible
|
||||
*/
|
||||
void QueueEditor::MoveEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, int iOffset)
|
||||
@@ -131,41 +142,61 @@ void QueueEditor::MoveEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo,
|
||||
{
|
||||
iNewEntry = 0;
|
||||
}
|
||||
if ((unsigned int)iNewEntry > pDownloadQueue->size() - 1)
|
||||
if ((unsigned int)iNewEntry > pDownloadQueue->GetFileQueue()->size() - 1)
|
||||
{
|
||||
iNewEntry = (int)pDownloadQueue->size() - 1;
|
||||
iNewEntry = (int)pDownloadQueue->GetFileQueue()->size() - 1;
|
||||
}
|
||||
|
||||
if (iNewEntry >= 0 && (unsigned int)iNewEntry <= pDownloadQueue->size() - 1)
|
||||
if (iNewEntry >= 0 && (unsigned int)iNewEntry <= pDownloadQueue->GetFileQueue()->size() - 1)
|
||||
{
|
||||
FileInfo* fi = (*pDownloadQueue)[iEntry];
|
||||
pDownloadQueue->erase(pDownloadQueue->begin() + iEntry);
|
||||
pDownloadQueue->insert(pDownloadQueue->begin() + iNewEntry, fi);
|
||||
FileInfo* fi = pDownloadQueue->GetFileQueue()->at(iEntry);
|
||||
pDownloadQueue->GetFileQueue()->erase(pDownloadQueue->GetFileQueue()->begin() + iEntry);
|
||||
pDownloadQueue->GetFileQueue()->insert(pDownloadQueue->GetFileQueue()->begin() + iNewEntry, fi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool QueueEditor::EditEntry(int ID, bool bSmartOrder, EEditAction eAction, int iOffset)
|
||||
/*
|
||||
* Set priority for entry
|
||||
* returns true if successful, false if operation is not possible
|
||||
*/
|
||||
void QueueEditor::SetPriorityEntry(FileInfo* pFileInfo, const char* szPriority)
|
||||
{
|
||||
debug("Setting priority %s for file %s", szPriority, pFileInfo->GetFilename());
|
||||
int iPriority = atoi(szPriority);
|
||||
pFileInfo->SetPriority(iPriority);
|
||||
}
|
||||
|
||||
bool QueueEditor::EditEntry(int ID, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText)
|
||||
{
|
||||
IDList cIDList;
|
||||
cIDList.clear();
|
||||
cIDList.push_back(ID);
|
||||
return EditList(&cIDList, bSmartOrder, eAction, iOffset);
|
||||
return EditList(&cIDList, NULL, mmID, bSmartOrder, eAction, iOffset, szText);
|
||||
}
|
||||
|
||||
bool QueueEditor::LockedEditEntry(DownloadQueue* pDownloadQueue, int ID, bool bSmartOrder, EEditAction eAction, int iOffset)
|
||||
bool QueueEditor::LockedEditEntry(DownloadQueue* pDownloadQueue, int ID, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText)
|
||||
{
|
||||
IDList cIDList;
|
||||
cIDList.clear();
|
||||
cIDList.push_back(ID);
|
||||
return InternEditList(pDownloadQueue, &cIDList, bSmartOrder, eAction, iOffset);
|
||||
return InternEditList(pDownloadQueue, &cIDList, bSmartOrder, eAction, iOffset, szText);
|
||||
}
|
||||
|
||||
bool QueueEditor::EditList(IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset)
|
||||
bool QueueEditor::EditList(IDList* pIDList, NameList* pNameList, EMatchMode eMatchMode, bool bSmartOrder,
|
||||
EEditAction eAction, int iOffset, const char* szText)
|
||||
{
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
|
||||
bool bOK = InternEditList(pDownloadQueue, pIDList, bSmartOrder, eAction, iOffset);
|
||||
bool bOK = true;
|
||||
|
||||
if (pNameList)
|
||||
{
|
||||
pIDList = new IDList();
|
||||
bOK = BuildIDListFromNameList(pDownloadQueue, pIDList, pNameList, eMatchMode, eAction);
|
||||
}
|
||||
|
||||
bOK = bOK && (InternEditList(pDownloadQueue, pIDList, bSmartOrder, eAction, iOffset, szText) || eMatchMode == mmRegEx);
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
@@ -174,10 +205,20 @@ bool QueueEditor::EditList(IDList* pIDList, bool bSmartOrder, EEditAction eActio
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
|
||||
if (pNameList)
|
||||
{
|
||||
delete pIDList;
|
||||
}
|
||||
|
||||
return bOK;
|
||||
}
|
||||
|
||||
bool QueueEditor::InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset)
|
||||
bool QueueEditor::LockedEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText)
|
||||
{
|
||||
return InternEditList(pDownloadQueue, pIDList, bSmartOrder, eAction, iOffset, szText);
|
||||
}
|
||||
|
||||
bool QueueEditor::InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText)
|
||||
{
|
||||
if (eAction == eaGroupMoveOffset)
|
||||
{
|
||||
@@ -187,53 +228,93 @@ bool QueueEditor::InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList,
|
||||
ItemList cItemList;
|
||||
PrepareList(pDownloadQueue, &cItemList, pIDList, bSmartOrder, eAction, iOffset);
|
||||
|
||||
if (eAction == eaFilePauseAllPars || eAction == eaFilePauseExtraPars)
|
||||
switch (eAction)
|
||||
{
|
||||
PauseParsInGroups(&cItemList, eAction == eaFilePauseExtraPars);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (ItemList::iterator it = cItemList.begin(); it != cItemList.end(); it++)
|
||||
{
|
||||
EditItem* pItem = *it;
|
||||
switch (eAction)
|
||||
case eaFilePauseAllPars:
|
||||
case eaFilePauseExtraPars:
|
||||
PauseParsInGroups(&cItemList, eAction == eaFilePauseExtraPars);
|
||||
break;
|
||||
|
||||
case eaGroupMerge:
|
||||
return MergeGroups(pDownloadQueue, &cItemList);
|
||||
|
||||
case eaFileSplit:
|
||||
return SplitGroup(pDownloadQueue, &cItemList, szText);
|
||||
|
||||
case eaFileReorder:
|
||||
ReorderFiles(pDownloadQueue, &cItemList);
|
||||
break;
|
||||
|
||||
default:
|
||||
for (ItemList::iterator it = cItemList.begin(); it != cItemList.end(); it++)
|
||||
{
|
||||
case eaFilePause:
|
||||
PauseUnpauseEntry(pItem->m_pFileInfo, true);
|
||||
break;
|
||||
EditItem* pItem = *it;
|
||||
switch (eAction)
|
||||
{
|
||||
case eaFilePause:
|
||||
PauseUnpauseEntry(pItem->m_pFileInfo, true);
|
||||
break;
|
||||
|
||||
case eaFileResume:
|
||||
PauseUnpauseEntry(pItem->m_pFileInfo, false);
|
||||
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 eaFileMoveOffset:
|
||||
case eaFileMoveTop:
|
||||
case eaFileMoveBottom:
|
||||
MoveEntry(pDownloadQueue, pItem->m_pFileInfo, pItem->m_iOffset);
|
||||
break;
|
||||
|
||||
case eaFileDelete:
|
||||
DeleteEntry(pItem->m_pFileInfo);
|
||||
break;
|
||||
case eaFileDelete:
|
||||
DeleteEntry(pItem->m_pFileInfo);
|
||||
break;
|
||||
|
||||
case eaFilePauseAllPars:
|
||||
case eaFilePauseExtraPars:
|
||||
// remove compiler warning "enumeration not handled in switch"
|
||||
break;
|
||||
case eaFileSetPriority:
|
||||
SetPriorityEntry(pItem->m_pFileInfo, szText);
|
||||
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;
|
||||
case eaGroupSetCategory:
|
||||
SetNZBCategory(pItem->m_pFileInfo->GetNZBInfo(), szText);
|
||||
break;
|
||||
|
||||
case eaGroupSetName:
|
||||
SetNZBName(pItem->m_pFileInfo->GetNZBInfo(), szText);
|
||||
break;
|
||||
|
||||
case eaGroupSetDupeKey:
|
||||
case eaGroupSetDupeScore:
|
||||
case eaGroupSetDupeMode:
|
||||
SetNZBDupeParam(pItem->m_pFileInfo->GetNZBInfo(), eAction, szText);
|
||||
break;
|
||||
|
||||
case eaGroupSetParameter:
|
||||
SetNZBParameter(pItem->m_pFileInfo->GetNZBInfo(), szText);
|
||||
break;
|
||||
|
||||
case eaGroupPause:
|
||||
case eaGroupResume:
|
||||
case eaGroupDelete:
|
||||
case eaGroupDupeDelete:
|
||||
case eaGroupFinalDelete:
|
||||
case eaGroupMoveTop:
|
||||
case eaGroupMoveBottom:
|
||||
case eaGroupMoveOffset:
|
||||
case eaGroupPauseAllPars:
|
||||
case eaGroupPauseExtraPars:
|
||||
case eaGroupSetPriority:
|
||||
EditGroup(pDownloadQueue, pItem->m_pFileInfo, eAction, iOffset, szText);
|
||||
break;
|
||||
|
||||
case eaFilePauseAllPars:
|
||||
case eaFilePauseExtraPars:
|
||||
case eaGroupMerge:
|
||||
case eaFileReorder:
|
||||
case eaFileSplit:
|
||||
// remove compiler warning "enumeration not handled in switch"
|
||||
break;
|
||||
}
|
||||
delete pItem;
|
||||
}
|
||||
delete pItem;
|
||||
}
|
||||
}
|
||||
|
||||
return cItemList.size() > 0;
|
||||
@@ -261,18 +342,18 @@ void QueueEditor::PrepareList(DownloadQueue* pDownloadQueue, ItemList* pItemList
|
||||
if (iOffset < 0)
|
||||
{
|
||||
iStart = 0;
|
||||
iEnd = pDownloadQueue->size();
|
||||
iEnd = pDownloadQueue->GetFileQueue()->size();
|
||||
iStep = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
iStart = pDownloadQueue->size() - 1;
|
||||
iStart = pDownloadQueue->GetFileQueue()->size() - 1;
|
||||
iEnd = -1;
|
||||
iStep = -1;
|
||||
}
|
||||
for (int iIndex = iStart; iIndex != iEnd; iIndex += iStep)
|
||||
{
|
||||
FileInfo* pFileInfo = (*pDownloadQueue)[iIndex];
|
||||
FileInfo* pFileInfo = pDownloadQueue->GetFileQueue()->at(iIndex);
|
||||
int iID = pFileInfo->GetID();
|
||||
for (IDList::iterator it = pIDList->begin(); it != pIDList->end(); it++)
|
||||
{
|
||||
@@ -286,9 +367,9 @@ void QueueEditor::PrepareList(DownloadQueue* pDownloadQueue, ItemList* pItemList
|
||||
{
|
||||
iWorkOffset = -iIndex;
|
||||
}
|
||||
else if (iDestPos > int(pDownloadQueue->size()) - 1)
|
||||
else if (iDestPos > int(pDownloadQueue->GetFileQueue()->size()) - 1)
|
||||
{
|
||||
iWorkOffset = int(pDownloadQueue->size()) - 1 - iIndex;
|
||||
iWorkOffset = int(pDownloadQueue->GetFileQueue()->size()) - 1 - iIndex;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -314,7 +395,7 @@ void QueueEditor::PrepareList(DownloadQueue* pDownloadQueue, ItemList* pItemList
|
||||
// check ID range
|
||||
int iMaxID = 0;
|
||||
int iMinID = MAX_ID;
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
int ID = pFileInfo->GetID();
|
||||
@@ -344,18 +425,89 @@ void QueueEditor::PrepareList(DownloadQueue* pDownloadQueue, ItemList* pItemList
|
||||
}
|
||||
}
|
||||
|
||||
bool QueueEditor::EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, EEditAction eAction, int iOffset)
|
||||
bool QueueEditor::BuildIDListFromNameList(DownloadQueue* pDownloadQueue, IDList* pIDList, NameList* pNameList, EMatchMode eMatchMode, EEditAction eAction)
|
||||
{
|
||||
#ifndef HAVE_REGEX_H
|
||||
if (eMatchMode == mmRegEx)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::set<int> uniqueIDs;
|
||||
|
||||
for (NameList::iterator it = pNameList->begin(); it != pNameList->end(); it++)
|
||||
{
|
||||
const char* szName = *it;
|
||||
|
||||
RegEx *pRegEx = NULL;
|
||||
if (eMatchMode == mmRegEx)
|
||||
{
|
||||
pRegEx = new RegEx(szName);
|
||||
if (!pRegEx->IsValid())
|
||||
{
|
||||
delete pRegEx;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool bFound = false;
|
||||
|
||||
for (FileQueue::iterator it2 = pDownloadQueue->GetFileQueue()->begin(); it2 != pDownloadQueue->GetFileQueue()->end(); it2++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it2;
|
||||
if (eAction < eaGroupMoveOffset)
|
||||
{
|
||||
// file action
|
||||
char szFilename[MAX_PATH];
|
||||
snprintf(szFilename, sizeof(szFilename) - 1, "%s/%s", pFileInfo->GetNZBInfo()->GetName(), Util::BaseFileName(pFileInfo->GetFilename()));
|
||||
if (((!pRegEx && !strcmp(szFilename, szName)) || (pRegEx && pRegEx->Match(szFilename))) &&
|
||||
(uniqueIDs.find(pFileInfo->GetID()) == uniqueIDs.end()))
|
||||
{
|
||||
uniqueIDs.insert(pFileInfo->GetID());
|
||||
pIDList->push_back(pFileInfo->GetID());
|
||||
bFound = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// group action
|
||||
const char *szFilename = pFileInfo->GetNZBInfo()->GetName();
|
||||
if (((!pRegEx && !strcmp(szFilename, szName)) || (pRegEx && pRegEx->Match(szFilename))) &&
|
||||
(uniqueIDs.find(pFileInfo->GetNZBInfo()->GetID()) == uniqueIDs.end()))
|
||||
{
|
||||
uniqueIDs.insert(pFileInfo->GetNZBInfo()->GetID());
|
||||
pIDList->push_back(pFileInfo->GetID());
|
||||
bFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delete pRegEx;
|
||||
|
||||
if (!bFound && (eMatchMode == mmName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QueueEditor::EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, EEditAction eAction, int iOffset, const char* szText)
|
||||
{
|
||||
IDList cIDList;
|
||||
cIDList.clear();
|
||||
bool bAllPaused = true;
|
||||
|
||||
// collecting files belonging to group
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo2 = *it;
|
||||
if (pFileInfo2->GetNZBInfo() == pFileInfo->GetNZBInfo())
|
||||
{
|
||||
cIDList.push_back(pFileInfo2->GetID());
|
||||
bAllPaused &= pFileInfo2->GetPaused();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -404,17 +556,31 @@ bool QueueEditor::EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo,
|
||||
}
|
||||
iOffset = iFileOffset;
|
||||
}
|
||||
else if (eAction == eaGroupDelete || eAction == eaGroupDupeDelete || eAction == eaGroupFinalDelete)
|
||||
{
|
||||
pFileInfo->GetNZBInfo()->SetDeleting(true);
|
||||
pFileInfo->GetNZBInfo()->SetAvoidHistory(eAction == eaGroupFinalDelete);
|
||||
pFileInfo->GetNZBInfo()->SetDeletePaused(bAllPaused);
|
||||
if (eAction == eaGroupDupeDelete)
|
||||
{
|
||||
pFileInfo->GetNZBInfo()->SetDeleteStatus(NZBInfo::dsDupe);
|
||||
}
|
||||
pFileInfo->GetNZBInfo()->SetCleanupDisk(CanCleanupDisk(pDownloadQueue, pFileInfo->GetNZBInfo()));
|
||||
}
|
||||
|
||||
EEditAction GroupToFileMap[] = { (EEditAction)0, eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause, eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars,
|
||||
eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause, eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars };
|
||||
EEditAction GroupToFileMap[] = { (EEditAction)0, eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause,
|
||||
eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars, eaFileSetPriority, eaFileReorder, eaFileSplit,
|
||||
eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause, eaFileResume, eaFileDelete, eaFileDelete, eaFileDelete,
|
||||
eaFilePauseAllPars, eaFilePauseExtraPars, eaFileSetPriority,
|
||||
(EEditAction)0, (EEditAction)0, (EEditAction)0 };
|
||||
|
||||
return InternEditList(pDownloadQueue, &cIDList, true, GroupToFileMap[eAction], iOffset);
|
||||
return InternEditList(pDownloadQueue, &cIDList, true, GroupToFileMap[eAction], iOffset, szText);
|
||||
}
|
||||
|
||||
void QueueEditor::BuildGroupList(DownloadQueue* pDownloadQueue, FileList* pGroupList)
|
||||
{
|
||||
pGroupList->clear();
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
FileInfo* pGroupInfo = NULL;
|
||||
@@ -483,7 +649,7 @@ void QueueEditor::AlignAffectedGroups(DownloadQueue* pDownloadQueue, IDList* pID
|
||||
}
|
||||
if (iOffset > 0)
|
||||
{
|
||||
for (unsigned int i = iNum + 1; i <= cGroupList.size() - iOffset; i++)
|
||||
for (int i = iNum + 1; i <= (int)cGroupList.size() - iOffset; i++)
|
||||
{
|
||||
if (!ItemExists(&cAffectedGroupList, cGroupList[i]))
|
||||
{
|
||||
@@ -507,24 +673,24 @@ void QueueEditor::AlignAffectedGroups(DownloadQueue* pDownloadQueue, IDList* pID
|
||||
for (FileList::iterator it = cAffectedGroupList.begin(); it != cAffectedGroupList.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
AlignGroup(pDownloadQueue, pFileInfo);
|
||||
AlignGroup(pDownloadQueue, pFileInfo->GetNZBInfo());
|
||||
}
|
||||
}
|
||||
|
||||
void QueueEditor::AlignGroup(DownloadQueue* pDownloadQueue, FileInfo* pFirstFileInfo)
|
||||
void QueueEditor::AlignGroup(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
|
||||
{
|
||||
FileInfo* pLastFileInfo = NULL;
|
||||
unsigned int iLastNum = 0;
|
||||
unsigned int iNum = 0;
|
||||
while (iNum < pDownloadQueue->size())
|
||||
while (iNum < pDownloadQueue->GetFileQueue()->size())
|
||||
{
|
||||
FileInfo* pFileInfo = (*pDownloadQueue)[iNum];
|
||||
if (pFirstFileInfo->GetNZBInfo() == pFileInfo->GetNZBInfo())
|
||||
FileInfo* pFileInfo = pDownloadQueue->GetFileQueue()->at(iNum);
|
||||
if (pFileInfo->GetNZBInfo() == pNZBInfo)
|
||||
{
|
||||
if (pLastFileInfo && iNum - iLastNum > 1)
|
||||
{
|
||||
pDownloadQueue->erase(pDownloadQueue->begin() + iNum);
|
||||
pDownloadQueue->insert(pDownloadQueue->begin() + iLastNum + 1, pFileInfo);
|
||||
pDownloadQueue->GetFileQueue()->erase(pDownloadQueue->GetFileQueue()->begin() + iNum);
|
||||
pDownloadQueue->GetFileQueue()->insert(pDownloadQueue->GetFileQueue()->begin() + iLastNum + 1, pFileInfo);
|
||||
iLastNum++;
|
||||
}
|
||||
else
|
||||
@@ -653,3 +819,213 @@ void QueueEditor::PausePars(FileList* pFileList, bool bExtraParsOnly)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QueueEditor::SetNZBCategory(NZBInfo* pNZBInfo, const char* szCategory)
|
||||
{
|
||||
debug("QueueEditor: setting category '%s' for '%s'", szCategory, Util::BaseFileName(pNZBInfo->GetFilename()));
|
||||
|
||||
g_pQueueCoordinator->SetQueueEntryNZBCategory(pNZBInfo, szCategory);
|
||||
}
|
||||
|
||||
void QueueEditor::SetNZBName(NZBInfo* pNZBInfo, const char* szName)
|
||||
{
|
||||
debug("QueueEditor: renaming '%s' to '%s'", Util::BaseFileName(pNZBInfo->GetFilename()), szName);
|
||||
|
||||
g_pQueueCoordinator->SetQueueEntryNZBName(pNZBInfo, szName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if deletion of already downloaded files is possible (when nzb is deleted from queue).
|
||||
* The deletion is most always possible, except the case if all remaining files in queue
|
||||
* (belonging to this nzb-file) are PARS.
|
||||
*/
|
||||
bool QueueEditor::CanCleanupDisk(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
|
||||
{
|
||||
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (pFileInfo->GetNZBInfo() == pNZBInfo)
|
||||
{
|
||||
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"))
|
||||
{
|
||||
// non-par file found
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool QueueEditor::MergeGroups(DownloadQueue* pDownloadQueue, ItemList* pItemList)
|
||||
{
|
||||
if (pItemList->size() == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bOK = true;
|
||||
|
||||
EditItem* pDestItem = pItemList->front();
|
||||
|
||||
for (ItemList::iterator it = pItemList->begin() + 1; it != pItemList->end(); it++)
|
||||
{
|
||||
EditItem* pItem = *it;
|
||||
if (pItem->m_pFileInfo->GetNZBInfo() != pDestItem->m_pFileInfo->GetNZBInfo())
|
||||
{
|
||||
debug("merge %s to %s", pItem->m_pFileInfo->GetNZBInfo()->GetFilename(), pDestItem->m_pFileInfo->GetNZBInfo()->GetFilename());
|
||||
if (g_pQueueCoordinator->MergeQueueEntries(pDestItem->m_pFileInfo->GetNZBInfo(), pItem->m_pFileInfo->GetNZBInfo()))
|
||||
{
|
||||
bOK = false;
|
||||
}
|
||||
}
|
||||
delete pItem;
|
||||
}
|
||||
|
||||
AlignGroup(pDownloadQueue, pDestItem->m_pFileInfo->GetNZBInfo());
|
||||
|
||||
delete pDestItem;
|
||||
return bOK;
|
||||
}
|
||||
|
||||
bool QueueEditor::SplitGroup(DownloadQueue* pDownloadQueue, ItemList* pItemList, const char* szName)
|
||||
{
|
||||
if (pItemList->size() == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FileQueue* pFileList = new FileQueue();
|
||||
|
||||
for (ItemList::iterator it = pItemList->begin(); it != pItemList->end(); it++)
|
||||
{
|
||||
EditItem* pItem = *it;
|
||||
pFileList->push_back(pItem->m_pFileInfo);
|
||||
delete pItem;
|
||||
}
|
||||
|
||||
NZBInfo* pNewNZBInfo = NULL;
|
||||
bool bOK = g_pQueueCoordinator->SplitQueueEntries(pFileList, szName, &pNewNZBInfo);
|
||||
if (bOK)
|
||||
{
|
||||
AlignGroup(pDownloadQueue, pNewNZBInfo);
|
||||
}
|
||||
|
||||
delete pFileList;
|
||||
return bOK;
|
||||
}
|
||||
|
||||
void QueueEditor::ReorderFiles(DownloadQueue* pDownloadQueue, ItemList* pItemList)
|
||||
{
|
||||
if (pItemList->size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EditItem* pFirstItem = pItemList->front();
|
||||
NZBInfo* pNZBInfo = pFirstItem->m_pFileInfo->GetNZBInfo();
|
||||
unsigned int iInsertPos = 0;
|
||||
|
||||
// find first file of the group
|
||||
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (pFileInfo->GetNZBInfo() == pNZBInfo)
|
||||
{
|
||||
break;
|
||||
}
|
||||
iInsertPos++;
|
||||
}
|
||||
|
||||
// now can reorder
|
||||
for (ItemList::iterator it = pItemList->begin(); it != pItemList->end(); it++)
|
||||
{
|
||||
EditItem* pItem = *it;
|
||||
FileInfo* pFileInfo = pItem->m_pFileInfo;
|
||||
|
||||
// move file item
|
||||
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo1 = *it;
|
||||
if (pFileInfo1 == pFileInfo)
|
||||
{
|
||||
pDownloadQueue->GetFileQueue()->erase(it);
|
||||
pDownloadQueue->GetFileQueue()->insert(pDownloadQueue->GetFileQueue()->begin() + iInsertPos, pFileInfo);
|
||||
iInsertPos++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
delete pItem;
|
||||
}
|
||||
}
|
||||
|
||||
void QueueEditor::SetNZBParameter(NZBInfo* pNZBInfo, const char* szParamString)
|
||||
{
|
||||
debug("QueueEditor: setting nzb parameter '%s' for '%s'", szParamString, Util::BaseFileName(pNZBInfo->GetFilename()));
|
||||
|
||||
char* szStr = strdup(szParamString);
|
||||
|
||||
char* szValue = strchr(szStr, '=');
|
||||
if (szValue)
|
||||
{
|
||||
*szValue = '\0';
|
||||
szValue++;
|
||||
pNZBInfo->GetParameters()->SetParameter(szStr, szValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not set nzb parameter for %s: invalid argument: %s", pNZBInfo->GetName(), szParamString);
|
||||
}
|
||||
|
||||
free(szStr);
|
||||
}
|
||||
|
||||
void QueueEditor::SetNZBDupeParam(NZBInfo* pNZBInfo, EEditAction eAction, const char* szText)
|
||||
{
|
||||
debug("QueueEditor: setting dupe parameter %i='%s' for '%s'", (int)eAction, szText, pNZBInfo->GetName());
|
||||
|
||||
switch (eAction)
|
||||
{
|
||||
case eaGroupSetDupeKey:
|
||||
pNZBInfo->SetDupeKey(szText);
|
||||
break;
|
||||
|
||||
case eaGroupSetDupeScore:
|
||||
pNZBInfo->SetDupeScore(atoi(szText));
|
||||
break;
|
||||
|
||||
case eaGroupSetDupeMode:
|
||||
{
|
||||
EDupeMode eMode = dmScore;
|
||||
if (!strcasecmp(szText, "SCORE"))
|
||||
{
|
||||
eMode = dmScore;
|
||||
}
|
||||
else if (!strcasecmp(szText, "ALL"))
|
||||
{
|
||||
eMode = dmAll;
|
||||
}
|
||||
else if (!strcasecmp(szText, "FORCE"))
|
||||
{
|
||||
eMode = dmForce;
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not set duplicate mode for %s: incorrect mode (%s)", pNZBInfo->GetName(), szText);
|
||||
return;
|
||||
}
|
||||
pNZBInfo->SetDupeMode(eMode);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
// suppress compiler warning
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2013 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,7 +15,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -33,8 +33,7 @@
|
||||
class QueueEditor
|
||||
{
|
||||
public:
|
||||
typedef std::vector<int> IDList;
|
||||
|
||||
// NOTE: changes to this enum must be synced with "eRemoteEditAction" in unit "MessageBase.h"
|
||||
enum EEditAction
|
||||
{
|
||||
eaFileMoveOffset = 1, // move to m_iOffset relative to the current position in queue
|
||||
@@ -45,14 +44,34 @@ public:
|
||||
eaFileDelete,
|
||||
eaFilePauseAllPars,
|
||||
eaFilePauseExtraPars,
|
||||
eaFileSetPriority,
|
||||
eaFileReorder,
|
||||
eaFileSplit,
|
||||
eaGroupMoveOffset, // move to m_iOffset relative to the current position in queue
|
||||
eaGroupMoveTop,
|
||||
eaGroupMoveBottom,
|
||||
eaGroupPause,
|
||||
eaGroupResume,
|
||||
eaGroupDelete,
|
||||
eaGroupDupeDelete,
|
||||
eaGroupFinalDelete,
|
||||
eaGroupPauseAllPars,
|
||||
eaGroupPauseExtraPars
|
||||
eaGroupPauseExtraPars,
|
||||
eaGroupSetPriority,
|
||||
eaGroupSetCategory,
|
||||
eaGroupMerge,
|
||||
eaGroupSetParameter,
|
||||
eaGroupSetName,
|
||||
eaGroupSetDupeKey,
|
||||
eaGroupSetDupeScore,
|
||||
eaGroupSetDupeMode
|
||||
};
|
||||
|
||||
enum EMatchMode
|
||||
{
|
||||
mmID = 1,
|
||||
mmName,
|
||||
mmRegEx
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -71,28 +90,39 @@ private:
|
||||
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);
|
||||
bool InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);
|
||||
void PrepareList(DownloadQueue* pDownloadQueue, ItemList* pItemList, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset);
|
||||
bool EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, EEditAction eAction, int iOffset);
|
||||
bool BuildIDListFromNameList(DownloadQueue* pDownloadQueue, IDList* pIDList, NameList* pNameList, EMatchMode eMatchMode, EEditAction eAction);
|
||||
bool EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, EEditAction eAction, int iOffset, const char* szText);
|
||||
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 AlignGroup(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
|
||||
void PauseParsInGroups(ItemList* pItemList, bool bExtraParsOnly);
|
||||
void PausePars(FileList* pFileList, bool bExtraParsOnly);
|
||||
void SetNZBCategory(NZBInfo* pNZBInfo, const char* szCategory);
|
||||
void SetNZBName(NZBInfo* pNZBInfo, const char* szName);
|
||||
bool CanCleanupDisk(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
|
||||
bool MergeGroups(DownloadQueue* pDownloadQueue, ItemList* pItemList);
|
||||
bool SplitGroup(DownloadQueue* pDownloadQueue, ItemList* pItemList, const char* szName);
|
||||
void ReorderFiles(DownloadQueue* pDownloadQueue, ItemList* pItemList);
|
||||
void SetNZBParameter(NZBInfo* pNZBInfo, const char* szParamString);
|
||||
void SetNZBDupeParam(NZBInfo* pNZBInfo, EEditAction eAction, const char* szText);
|
||||
|
||||
void PauseUnpauseEntry(FileInfo* pFileInfo, bool bPause);
|
||||
void DeleteEntry(FileInfo* pFileInfo);
|
||||
void MoveEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, int iOffset);
|
||||
void SetPriorityEntry(FileInfo* pFileInfo, const char* szPriority);
|
||||
|
||||
public:
|
||||
QueueEditor();
|
||||
~QueueEditor();
|
||||
|
||||
bool EditEntry(int ID, bool bSmartOrder, EEditAction eAction, int iOffset);
|
||||
bool EditList(IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset);
|
||||
bool EditEntry(int ID, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);
|
||||
bool EditList(IDList* pIDList, NameList* pNameList, EMatchMode eMatchMode, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);
|
||||
|
||||
bool LockedEditEntry(DownloadQueue* pDownloadQueue, int ID, bool bSmartOrder, EEditAction eAction, int iOffset);
|
||||
bool LockedEditEntry(DownloadQueue* pDownloadQueue, int ID, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);
|
||||
bool LockedEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
241
README
241
README
@@ -2,6 +2,10 @@
|
||||
NZBGet ReadMe
|
||||
=====================================
|
||||
|
||||
This is a short documentation. For more information please
|
||||
visit NZBGet home page at
|
||||
http://nzbget.sourceforge.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.
|
||||
@@ -41,16 +48,12 @@ 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.4.0) 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).
|
||||
It should run at least on:
|
||||
- Linux Debian 5.0 on x86;
|
||||
- Linux with uClibc on MIPSEL and ARM;
|
||||
- OpenBSD 5.0 on x86;
|
||||
- Mac OS X 10.7 Lion on x64;
|
||||
- Windows XP SP3 on x86 and Windows 7 on x64.
|
||||
|
||||
Clients and servers running on different OS'es may communicate with
|
||||
each other. For example, you can use NZBGet as client on Windows to
|
||||
@@ -86,6 +89,14 @@ And the following libraries are optional:
|
||||
- for par-check and -repair (enabled by default):
|
||||
- libpar2 (http://parchive.sourceforge.net)
|
||||
- libsigc++ (http://libsigc.sourceforge.net)
|
||||
|
||||
- for encrypted connections (TLS/SSL):
|
||||
- GnuTLS (http://www.gnu.org/software/gnutls)
|
||||
or
|
||||
- OpenSSL (http://www.openssl.org)
|
||||
|
||||
- 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
|
||||
should be available as installable packages. Please note that you also
|
||||
@@ -93,29 +104,45 @@ 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
|
||||
- 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:
|
||||
@@ -125,6 +152,15 @@ You may run configure with additional arguments:
|
||||
|
||||
--disable-parcheck - to make without parcheck-support. Use this option
|
||||
if you can not use libpar2 or libsigc++.
|
||||
|
||||
--with-tlslib=(GnuTLS, OpenSSL) - 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 GnuTLS nor OpenSSL.
|
||||
|
||||
--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.
|
||||
@@ -136,8 +172,6 @@ it uses library par2 (libpar2), which needs sigc++ on its part.
|
||||
|
||||
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 packages you can compile them yourself.
|
||||
Following configure-parameters may be usefull:
|
||||
|
||||
@@ -148,12 +182,26 @@ Following configure-parameters may be usefull:
|
||||
|
||||
The library libsigc++ must be installed first, since libpar2 requires it.
|
||||
|
||||
If you use nzbget on a very slow computer like NAS-device, it may be good to
|
||||
limit the time allowed for par-repair (option "ParTimeLimit" in nzbget
|
||||
configuration file). This feature requires a patched version of libpar2.
|
||||
To compile that version download the original source code of libpar2
|
||||
(version 0.2) and apply patches "libpar2-0.2-bugfixes.patch" and
|
||||
"libpar2-0.2-cancel.patch", provided with nzbget:
|
||||
|
||||
cd libpar2-0.2
|
||||
cp ~/nzbget/libpar2-0.2-*.patch .
|
||||
patch < libpar2-0.2-bugfixes.patch
|
||||
patch < libpar2-0.2-cancel.patch
|
||||
./configure
|
||||
make
|
||||
make install
|
||||
|
||||
If you are not able to use libpar2 or libsigc++ or do not want them you can
|
||||
make the program without support for par-check using option
|
||||
"--disable-parcheck":
|
||||
make nzbget without support for par-check using option "--disable-parcheck":
|
||||
|
||||
./configure --disable-parcheck
|
||||
|
||||
|
||||
Optional package: curses
|
||||
-------------------------
|
||||
For curses-outputmode you need ncurses or curses on your system.
|
||||
@@ -168,6 +216,29 @@ 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: GnuTLS or OpenSSL.
|
||||
Configure-script checks which library is installed and use it. If both are
|
||||
avialable it gives the precedence to GnuTLS. You may override that with
|
||||
the option --with-tlslib=(GnuTLS, OpenSSL). For example to build whith OpenSSL:
|
||||
|
||||
./configure --with-tlslib=OpenSSL
|
||||
|
||||
Following configure-parameters may be usefull:
|
||||
|
||||
--with-libtls-includes
|
||||
--with-libtls-libraries
|
||||
|
||||
--with-openssl-includes
|
||||
--with-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
|
||||
=====================================
|
||||
@@ -181,31 +252,49 @@ libraries:
|
||||
|
||||
- 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.
|
||||
To compile the program with TLS/SSL support you also need the library:
|
||||
|
||||
- GnuTLS (http://www.gnu.org/software/gnutls)
|
||||
|
||||
Download a precompiled version of GnuTLS from http://josefsson.org/gnutls4win
|
||||
and create lib-file as described there in section "Using the GnuTLS DLL from
|
||||
your Visual Studio program".
|
||||
|
||||
After libsigc++ and libpar2 are compiled in static libraries (.lib), the
|
||||
library for GnuTLS is created and include- and libraries-paths are configured
|
||||
in MS Visual C++ 2005 you should be able to compile NZBGet.
|
||||
|
||||
=====================================
|
||||
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:
|
||||
|
||||
~/.nzbget
|
||||
/etc/nzbget.conf
|
||||
/usr/etc/nzbget.conf
|
||||
@@ -229,6 +318,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:
|
||||
----------------
|
||||
|
||||
@@ -256,10 +351,16 @@ 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
|
||||
@@ -321,7 +422,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)
|
||||
|
||||
@@ -337,36 +438,90 @@ 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 "ppscripts".
|
||||
|
||||
To use the scripts copy them into your local directory and set options
|
||||
<ScriptDir>, <DefScript> 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 "nzbget" (predefined and not changeable)
|
||||
and the password from the option "ControlPassword" (default is 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/nzbget: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.
|
||||
|
||||
=====================================
|
||||
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.
|
||||
|
||||
Additional exemption: compiling, linking, and/or using OpenSSL is allowed.
|
||||
|
||||
Binary distribution for Windows contains code from the following libraries:
|
||||
|
||||
- libpar2 (http://parchive.sourceforge.net)
|
||||
- libsigc++ (http://libsigc.sourceforge.net)
|
||||
- GnuTLS (http://www.gnu.org/software/gnutls)
|
||||
|
||||
libpar2 is distributed under GPL and libsigc++ under LGPL.
|
||||
libpar2 is distributed under GPL; libsigc++ and GnuTLS - under LGPL.
|
||||
|
||||
=====================================
|
||||
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.sourceforge.net/forum
|
||||
|
||||
or contact me at
|
||||
|
||||
|
||||
912
RemoteClient.cpp
912
RemoteClient.cpp
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2009 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -30,35 +30,54 @@
|
||||
#include "Options.h"
|
||||
#include "MessageBase.h"
|
||||
#include "Connection.h"
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
class RemoteClient
|
||||
{
|
||||
private:
|
||||
class MatchedNZBInfo: public NZBInfo
|
||||
{
|
||||
public:
|
||||
bool m_bMatch;
|
||||
};
|
||||
|
||||
class MatchedFileInfo: public FileInfo
|
||||
{
|
||||
public:
|
||||
bool m_bMatch;
|
||||
};
|
||||
|
||||
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);
|
||||
void printf(const char* msg, ...);
|
||||
void perror(const char* msg);
|
||||
|
||||
public:
|
||||
RemoteClient();
|
||||
~RemoteClient();
|
||||
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 RequestServerDownload(const char* szFilename, const char* szCategory, bool bAddFirst, bool bAddPaused, int iPriority);
|
||||
bool RequestServerList(bool bFiles, bool bGroups, const char* szPattern);
|
||||
bool RequestServerPauseUnpause(bool bPause, eRemotePauseUnpauseAction iAction);
|
||||
bool RequestServerSetDownloadRate(int iRate);
|
||||
bool RequestServerDumpDebug();
|
||||
bool RequestServerEditQueue(int iAction, int iOffset, int* pIDList, int iIDCount, bool bSmartOrder);
|
||||
bool RequestServerEditQueue(eRemoteEditAction iAction, int iOffset, const char* szText,
|
||||
int* pIDList, int iIDCount, NameList* pNameList, eRemoteMatchMode iMatchMode, bool bSmartOrder);
|
||||
bool RequestServerLog(int iLines);
|
||||
bool RequestServerShutdown();
|
||||
bool RequestServerReload();
|
||||
bool RequestServerVersion();
|
||||
bool RequestPostQueue();
|
||||
bool RequestWriteLog(int iKind, const char* szText);
|
||||
bool RequestScan(bool bSyncMode);
|
||||
bool RequestHistory();
|
||||
bool RequestServerDownloadUrl(const char* szURL, const char* szNZBFilename, const char* szCategory, bool bAddFirst, bool bAddPaused, int iPriority);
|
||||
bool RequestUrlQueue();
|
||||
void BuildFileList(SNZBListResponse* pListResponse, const char* pTrailingData, DownloadQueue* pDownloadQueue);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
145
RemoteServer.cpp
145
RemoteServer.cpp
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
|
||||
* Copyright (C) 2007-2013 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -44,20 +44,21 @@
|
||||
#include "nzbget.h"
|
||||
#include "RemoteServer.h"
|
||||
#include "BinRpc.h"
|
||||
#include "XmlRpc.h"
|
||||
#include "WebServer.h"
|
||||
#include "Log.h"
|
||||
#include "Options.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
//*****************************************************************
|
||||
// RemoteServer
|
||||
|
||||
RemoteServer::RemoteServer()
|
||||
RemoteServer::RemoteServer(bool bTLS)
|
||||
{
|
||||
debug("Creating RemoteServer");
|
||||
|
||||
m_pNetAddress = new NetAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
m_bTLS = bTLS;
|
||||
m_pConnection = NULL;
|
||||
}
|
||||
|
||||
@@ -65,36 +66,51 @@ RemoteServer::~RemoteServer()
|
||||
{
|
||||
debug("Destroying RemoteServer");
|
||||
|
||||
if (m_pConnection)
|
||||
{
|
||||
delete m_pConnection;
|
||||
}
|
||||
delete m_pNetAddress;
|
||||
delete m_pConnection;
|
||||
}
|
||||
|
||||
void RemoteServer::Run()
|
||||
{
|
||||
debug("Entering RemoteServer-loop");
|
||||
|
||||
#ifndef DISABLE_TLS
|
||||
if (m_bTLS)
|
||||
{
|
||||
if (strlen(g_pOptions->GetSecureCert()) == 0 || !Util::FileExists(g_pOptions->GetSecureCert()))
|
||||
{
|
||||
error("Could not initialize TLS, secure certificate is not configured or the cert-file was not found. Check option <SecureCert>");
|
||||
return;
|
||||
}
|
||||
|
||||
if (strlen(g_pOptions->GetSecureKey()) == 0 || !Util::FileExists(g_pOptions->GetSecureKey()))
|
||||
{
|
||||
error("Could not initialize TLS, secure key is not configured or the key-file was not found. Check option <SecureKey>");
|
||||
return;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
while (!IsStopped())
|
||||
{
|
||||
bool bBind = true;
|
||||
|
||||
if (!m_pConnection)
|
||||
{
|
||||
m_pConnection = new Connection(m_pNetAddress);
|
||||
m_pConnection = new Connection(g_pOptions->GetControlIP(),
|
||||
m_bTLS ? g_pOptions->GetSecurePort() : g_pOptions->GetControlPort(),
|
||||
m_bTLS);
|
||||
m_pConnection->SetTimeout(g_pOptions->GetConnectionTimeout());
|
||||
m_pConnection->SetSuppressErrors(false);
|
||||
bBind = m_pConnection->Bind() == 0;
|
||||
bBind = m_pConnection->Bind();
|
||||
}
|
||||
|
||||
// Accept connections and store the "new" socket value
|
||||
SOCKET iSocket = INVALID_SOCKET;
|
||||
// Accept connections and store the new Connection
|
||||
Connection* pAcceptedConnection = NULL;
|
||||
if (bBind)
|
||||
{
|
||||
iSocket = m_pConnection->Accept();
|
||||
pAcceptedConnection = m_pConnection->Accept();
|
||||
}
|
||||
if (!bBind || iSocket == INVALID_SOCKET)
|
||||
if (!bBind || pAcceptedConnection == NULL)
|
||||
{
|
||||
// Remote server could not bind or accept connection, waiting 1/2 sec and try again
|
||||
if (IsStopped())
|
||||
@@ -109,9 +125,13 @@ void RemoteServer::Run()
|
||||
|
||||
RequestProcessor* commandThread = new RequestProcessor();
|
||||
commandThread->SetAutoDestroy(true);
|
||||
commandThread->SetSocket(iSocket);
|
||||
commandThread->SetConnection(pAcceptedConnection);
|
||||
#ifndef DISABLE_TLS
|
||||
commandThread->SetTLS(m_bTLS);
|
||||
#endif
|
||||
commandThread->Start();
|
||||
}
|
||||
|
||||
if (m_pConnection)
|
||||
{
|
||||
m_pConnection->Disconnect();
|
||||
@@ -136,32 +156,32 @@ void RemoteServer::Stop()
|
||||
//*****************************************************************
|
||||
// RequestProcessor
|
||||
|
||||
RequestProcessor::~RequestProcessor()
|
||||
{
|
||||
m_pConnection->Disconnect();
|
||||
delete m_pConnection;
|
||||
}
|
||||
|
||||
void RequestProcessor::Run()
|
||||
{
|
||||
// Read the first 4 bytes to determine request type
|
||||
bool bOK = false;
|
||||
int iSignature = 0;
|
||||
int iBytesReceived = recv(m_iSocket, (char*)&iSignature, sizeof(iSignature), 0);
|
||||
if (iBytesReceived < 0)
|
||||
|
||||
m_pConnection->SetSuppressErrors(true);
|
||||
|
||||
#ifndef DISABLE_TLS
|
||||
if (m_bTLS && !m_pConnection->StartTLS(false, g_pOptions->GetSecureCert(), g_pOptions->GetSecureKey()))
|
||||
{
|
||||
debug("Could not establish secure connection to web-client: Start TLS failed");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Info - connection received
|
||||
#ifdef WIN32
|
||||
char* ip = NULL;
|
||||
#else
|
||||
char ip[20];
|
||||
#endif
|
||||
struct sockaddr_in PeerName;
|
||||
int iPeerNameLength = sizeof(PeerName);
|
||||
if (getpeername(m_iSocket, (struct sockaddr*)&PeerName, (socklen_t*) &iPeerNameLength) >= 0)
|
||||
// Read the first 4 bytes to determine request type
|
||||
int iSignature = 0;
|
||||
if (!m_pConnection->Recv((char*)&iSignature, 4))
|
||||
{
|
||||
#ifdef WIN32
|
||||
ip = inet_ntoa(PeerName.sin_addr);
|
||||
#else
|
||||
inet_ntop(AF_INET, &PeerName.sin_addr, ip, sizeof(ip));
|
||||
#endif
|
||||
debug("Could not read request signature, request received on port %i from %s", m_bTLS ? g_pOptions->GetSecurePort() : g_pOptions->GetControlPort(), m_pConnection->GetRemoteAddr());
|
||||
return;
|
||||
}
|
||||
|
||||
if ((int)ntohl(iSignature) == (int)NZBMESSAGE_SIGNATURE)
|
||||
@@ -169,58 +189,47 @@ void RequestProcessor::Run()
|
||||
// binary request received
|
||||
bOK = true;
|
||||
BinRpcProcessor processor;
|
||||
processor.SetSocket(m_iSocket);
|
||||
processor.SetSignature(iSignature);
|
||||
processor.SetClientIP(ip);
|
||||
processor.SetConnection(m_pConnection);
|
||||
processor.Execute();
|
||||
}
|
||||
else if (!strncmp((char*)&iSignature, "POST", 4) || !strncmp((char*)&iSignature, "GET ", 4))
|
||||
else if (!strncmp((char*)&iSignature, "POST", 4) ||
|
||||
!strncmp((char*)&iSignature, "GET ", 4) ||
|
||||
!strncmp((char*)&iSignature, "OPTI", 4))
|
||||
{
|
||||
// XML-RPC or JSON-RPC request received
|
||||
Connection con(m_iSocket, false);
|
||||
// HTTP request received
|
||||
char szBuffer[1024];
|
||||
if (con.ReadLine(szBuffer, sizeof(szBuffer), NULL))
|
||||
if (m_pConnection->ReadLine(szBuffer, sizeof(szBuffer), NULL))
|
||||
{
|
||||
XmlRpcProcessor::EHttpMethod eHttpMethod = XmlRpcProcessor::hmGet;
|
||||
WebProcessor::EHttpMethod eHttpMethod = WebProcessor::hmGet;
|
||||
char* szUrl = szBuffer;
|
||||
if (!strncmp((char*)&iSignature, "POST", 4))
|
||||
{
|
||||
eHttpMethod = XmlRpcProcessor::hmPost;
|
||||
eHttpMethod = WebProcessor::hmPost;
|
||||
szUrl++;
|
||||
}
|
||||
if (!strncmp((char*)&iSignature, "OPTI", 4) && strlen(szUrl) > 4)
|
||||
{
|
||||
eHttpMethod = WebProcessor::hmOptions;
|
||||
szUrl += 4;
|
||||
}
|
||||
if (char* p = strchr(szUrl, ' '))
|
||||
{
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
XmlRpcProcessor::ERpcProtocol eProtocol = XmlRpcProcessor::rpUndefined;
|
||||
if (!strcmp(szUrl, "/xmlrpc") || !strncmp(szUrl, "/xmlrpc/", 8))
|
||||
{
|
||||
eProtocol = XmlRpcProcessor::rpXmlRpc;
|
||||
}
|
||||
else if (!strcmp(szUrl, "/jsonrpc") || !strncmp(szUrl, "/jsonrpc/", 9))
|
||||
{
|
||||
eProtocol = XmlRpcProcessor::rpJsonRpc;
|
||||
}
|
||||
debug("url: %s", szUrl);
|
||||
|
||||
if (eProtocol != XmlRpcProcessor::rpUndefined)
|
||||
{
|
||||
XmlRpcProcessor processor;
|
||||
processor.SetConnection(&con);
|
||||
processor.SetClientIP(ip);
|
||||
processor.SetProtocol(eProtocol);
|
||||
processor.SetHttpMethod(eHttpMethod);
|
||||
processor.SetUrl(szUrl);
|
||||
processor.Execute();
|
||||
bOK = true;
|
||||
}
|
||||
WebProcessor processor;
|
||||
processor.SetConnection(m_pConnection);
|
||||
processor.SetUrl(szUrl);
|
||||
processor.SetHttpMethod(eHttpMethod);
|
||||
processor.Execute();
|
||||
bOK = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bOK)
|
||||
{
|
||||
warn("Non-nzbget request received on port %i from %s", g_pOptions->GetServerPort(), ip);
|
||||
warn("Non-nzbget request received on port %i from %s", m_bTLS ? g_pOptions->GetSecurePort() : g_pOptions->GetControlPort(), m_pConnection->GetRemoteAddr());
|
||||
}
|
||||
|
||||
closesocket(m_iSocket);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2013 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -28,17 +28,16 @@
|
||||
#define REMOTESERVER_H
|
||||
|
||||
#include "Thread.h"
|
||||
#include "NetAddress.h"
|
||||
#include "Connection.h"
|
||||
|
||||
class RemoteServer : public Thread
|
||||
{
|
||||
private:
|
||||
NetAddress* m_pNetAddress;
|
||||
bool m_bTLS;
|
||||
Connection* m_pConnection;
|
||||
|
||||
public:
|
||||
RemoteServer();
|
||||
RemoteServer(bool bTLS);
|
||||
~RemoteServer();
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
@@ -47,11 +46,14 @@ public:
|
||||
class RequestProcessor : public Thread
|
||||
{
|
||||
private:
|
||||
SOCKET m_iSocket;
|
||||
bool m_bTLS;
|
||||
Connection* m_pConnection;
|
||||
|
||||
public:
|
||||
~RequestProcessor();
|
||||
virtual void Run();
|
||||
void SetSocket(SOCKET iSocket) { m_iSocket = iSocket; };
|
||||
void SetTLS(bool bTLS) { m_bTLS = bTLS; }
|
||||
void SetConnection(Connection* pConnection) { m_pConnection = pConnection; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
651
Scanner.cpp
Normal file
651
Scanner.cpp
Normal file
@@ -0,0 +1,651 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007-2013 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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>
|
||||
#ifdef WIN32
|
||||
#include <direct.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Scanner.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "ScriptController.h"
|
||||
#include "DiskState.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
extern Options* g_pOptions;
|
||||
extern DiskState* g_pDiskState;
|
||||
|
||||
Scanner::FileData::FileData(const char* szFilename)
|
||||
{
|
||||
m_szFilename = strdup(szFilename);
|
||||
m_iSize = 0;
|
||||
m_tLastChange = 0;
|
||||
}
|
||||
|
||||
Scanner::FileData::~FileData()
|
||||
{
|
||||
free(m_szFilename);
|
||||
}
|
||||
|
||||
|
||||
Scanner::QueueData::QueueData(const char* szFilename, const char* szNZBName, const char* szCategory,
|
||||
int iPriority, const char* szDupeKey, int iDupeScore, EDupeMode eDupeMode,
|
||||
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused, EAddStatus* pAddStatus)
|
||||
{
|
||||
m_szFilename = strdup(szFilename);
|
||||
m_szNZBName = strdup(szNZBName);
|
||||
m_szCategory = strdup(szCategory ? szCategory : "");
|
||||
m_iPriority = iPriority;
|
||||
m_szDupeKey = strdup(szDupeKey ? szDupeKey : "");
|
||||
m_iDupeScore = iDupeScore;
|
||||
m_eDupeMode = eDupeMode;
|
||||
m_bAddTop = bAddTop;
|
||||
m_bAddPaused = bAddPaused;
|
||||
m_pAddStatus = pAddStatus;
|
||||
|
||||
if (pParameters)
|
||||
{
|
||||
m_Parameters.CopyFrom(pParameters);
|
||||
}
|
||||
}
|
||||
|
||||
Scanner::QueueData::~QueueData()
|
||||
{
|
||||
free(m_szFilename);
|
||||
free(m_szNZBName);
|
||||
free(m_szCategory);
|
||||
free(m_szDupeKey);
|
||||
}
|
||||
|
||||
void Scanner::QueueData::SetAddStatus(EAddStatus eAddStatus)
|
||||
{
|
||||
if (m_pAddStatus)
|
||||
{
|
||||
*m_pAddStatus = eAddStatus;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Scanner::Scanner()
|
||||
{
|
||||
debug("Creating Scanner");
|
||||
|
||||
m_bRequestedNZBDirScan = false;
|
||||
m_bScanning = false;
|
||||
m_iNZBDirInterval = g_pOptions->GetNzbDirInterval() * 1000;
|
||||
m_iPass = 0;
|
||||
|
||||
const char* szNZBScript = g_pOptions->GetNZBProcess();
|
||||
m_bNZBScript = szNZBScript && strlen(szNZBScript) > 0;
|
||||
}
|
||||
|
||||
Scanner::~Scanner()
|
||||
{
|
||||
debug("Destroying Scanner");
|
||||
|
||||
for (FileList::iterator it = m_FileList.begin(); it != m_FileList.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_FileList.clear();
|
||||
|
||||
ClearQueueList();
|
||||
}
|
||||
|
||||
void Scanner::ClearQueueList()
|
||||
{
|
||||
for (QueueList::iterator it = m_QueueList.begin(); it != m_QueueList.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_QueueList.clear();
|
||||
}
|
||||
|
||||
void Scanner::Check()
|
||||
{
|
||||
m_mutexScan.Lock();
|
||||
|
||||
if (m_bRequestedNZBDirScan ||
|
||||
(!g_pOptions->GetPauseScan() && g_pOptions->GetNzbDirInterval() > 0 &&
|
||||
m_iNZBDirInterval >= g_pOptions->GetNzbDirInterval() * 1000))
|
||||
{
|
||||
// check nzbdir every g_pOptions->GetNzbDirInterval() seconds or if requested
|
||||
bool bCheckStat = !m_bRequestedNZBDirScan;
|
||||
m_bRequestedNZBDirScan = false;
|
||||
m_bScanning = true;
|
||||
CheckIncomingNZBs(g_pOptions->GetNzbDir(), "", bCheckStat);
|
||||
if (!bCheckStat && m_bNZBScript)
|
||||
{
|
||||
// if immediate scan requested, we need second scan to process files extracted by NzbProcess-script
|
||||
CheckIncomingNZBs(g_pOptions->GetNzbDir(), "", bCheckStat);
|
||||
}
|
||||
m_bScanning = false;
|
||||
m_iNZBDirInterval = 0;
|
||||
|
||||
// if NzbDirFileAge is less than NzbDirInterval (that can happen if NzbDirInterval
|
||||
// is set for rare scans like once per hour) we make 4 scans:
|
||||
// - one additional scan is neccessary to check sizes of detected files;
|
||||
// - another scan is required to check files which were extracted by NzbProcess-script;
|
||||
// - third scan is needed to check sizes of extracted files.
|
||||
if (g_pOptions->GetNzbDirInterval() > 0 && g_pOptions->GetNzbDirFileAge() < g_pOptions->GetNzbDirInterval())
|
||||
{
|
||||
int iMaxPass = m_bNZBScript ? 3 : 1;
|
||||
if (m_iPass < iMaxPass)
|
||||
{
|
||||
// scheduling another scan of incoming directory in NzbDirFileAge seconds.
|
||||
m_iNZBDirInterval = (g_pOptions->GetNzbDirInterval() - g_pOptions->GetNzbDirFileAge()) * 1000;
|
||||
m_iPass++;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_iPass = 0;
|
||||
}
|
||||
}
|
||||
|
||||
DropOldFiles();
|
||||
ClearQueueList();
|
||||
}
|
||||
m_iNZBDirInterval += 200;
|
||||
|
||||
m_mutexScan.Unlock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are files in directory for incoming nzb-files
|
||||
* and add them to download queue
|
||||
*/
|
||||
void Scanner::CheckIncomingNZBs(const char* szDirectory, const char* szCategory, bool bCheckStat)
|
||||
{
|
||||
DirBrowser dir(szDirectory);
|
||||
while (const char* filename = dir.Next())
|
||||
{
|
||||
struct stat buffer;
|
||||
char fullfilename[1023 + 1]; // one char reserved for the trailing slash (if needed)
|
||||
snprintf(fullfilename, 1023, "%s%s", szDirectory, filename);
|
||||
fullfilename[1023 - 1] = '\0';
|
||||
if (!stat(fullfilename, &buffer))
|
||||
{
|
||||
// check subfolders
|
||||
if ((buffer.st_mode & S_IFDIR) != 0 && strcmp(filename, ".") && strcmp(filename, ".."))
|
||||
{
|
||||
fullfilename[strlen(fullfilename) + 1] = '\0';
|
||||
fullfilename[strlen(fullfilename)] = PATH_SEPARATOR;
|
||||
const char* szUseCategory = filename;
|
||||
char szSubCategory[1024];
|
||||
if (strlen(szCategory) > 0)
|
||||
{
|
||||
snprintf(szSubCategory, 1023, "%s%c%s", szCategory, PATH_SEPARATOR, filename);
|
||||
szSubCategory[1024 - 1] = '\0';
|
||||
szUseCategory = szSubCategory;
|
||||
}
|
||||
CheckIncomingNZBs(fullfilename, szUseCategory, bCheckStat);
|
||||
}
|
||||
else if ((buffer.st_mode & S_IFDIR) == 0 && CanProcessFile(fullfilename, bCheckStat))
|
||||
{
|
||||
ProcessIncomingFile(szDirectory, filename, fullfilename, szCategory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Only files which were not changed during last g_pOptions->GetNzbDirFileAge() seconds
|
||||
* can be processed. That prevents the processing of files, which are currently being
|
||||
* copied into nzb-directory (eg. being downloaded in web-browser).
|
||||
*/
|
||||
bool Scanner::CanProcessFile(const char* szFullFilename, bool bCheckStat)
|
||||
{
|
||||
const char* szExtension = strrchr(szFullFilename, '.');
|
||||
if (!szExtension ||
|
||||
!strcasecmp(szExtension, ".queued") ||
|
||||
!strcasecmp(szExtension, ".error") ||
|
||||
!strcasecmp(szExtension, ".processed"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bCheckStat)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
long long lSize = Util::FileSize(szFullFilename);
|
||||
time_t tCurrent = time(NULL);
|
||||
bool bCanProcess = false;
|
||||
bool bInList = false;
|
||||
|
||||
for (FileList::iterator it = m_FileList.begin(); it != m_FileList.end(); it++)
|
||||
{
|
||||
FileData* pFileData = *it;
|
||||
if (!strcmp(pFileData->GetFilename(), szFullFilename))
|
||||
{
|
||||
bInList = true;
|
||||
if (pFileData->GetSize() == lSize &&
|
||||
tCurrent - pFileData->GetLastChange() >= g_pOptions->GetNzbDirFileAge())
|
||||
{
|
||||
bCanProcess = true;
|
||||
delete pFileData;
|
||||
m_FileList.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
pFileData->SetSize(lSize);
|
||||
if (pFileData->GetSize() != lSize)
|
||||
{
|
||||
pFileData->SetLastChange(tCurrent);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bInList)
|
||||
{
|
||||
FileData* pFileData = new FileData(szFullFilename);
|
||||
pFileData->SetSize(lSize);
|
||||
pFileData->SetLastChange(tCurrent);
|
||||
m_FileList.push_back(pFileData);
|
||||
}
|
||||
|
||||
return bCanProcess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove old files from the list of monitored files.
|
||||
* Normally these files are deleted from the list when they are processed.
|
||||
* However if a file was detected by function "CanProcessFile" once but wasn't
|
||||
* processed later (for example if the user deleted it), it will stay in the list,
|
||||
* until we remove it here.
|
||||
*/
|
||||
void Scanner::DropOldFiles()
|
||||
{
|
||||
time_t tCurrent = time(NULL);
|
||||
|
||||
int i = 0;
|
||||
for (FileList::iterator it = m_FileList.begin(); it != m_FileList.end(); )
|
||||
{
|
||||
FileData* pFileData = *it;
|
||||
if ((tCurrent - pFileData->GetLastChange() >=
|
||||
(g_pOptions->GetNzbDirInterval() + g_pOptions->GetNzbDirFileAge()) * 2) ||
|
||||
// can occur if the system clock was adjusted
|
||||
tCurrent < pFileData->GetLastChange())
|
||||
{
|
||||
debug("Removing file %s from scan file list", pFileData->GetFilename());
|
||||
|
||||
delete pFileData;
|
||||
m_FileList.erase(it);
|
||||
it = m_FileList.begin() + i;
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Scanner::ProcessIncomingFile(const char* szDirectory, const char* szBaseFilename,
|
||||
const char* szFullFilename, const char* szCategory)
|
||||
{
|
||||
const char* szExtension = strrchr(szBaseFilename, '.');
|
||||
if (!szExtension)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char* szNZBName = strdup("");
|
||||
char* szNZBCategory = strdup(szCategory);
|
||||
NZBParameterList* pParameters = new NZBParameterList();
|
||||
int iPriority = 0;
|
||||
bool bAddTop = false;
|
||||
bool bAddPaused = false;
|
||||
const char* szDupeKey = NULL;
|
||||
int iDupeScore = 0;
|
||||
EDupeMode eDupeMode = dmScore;
|
||||
EAddStatus eAddStatus = asSkipped;
|
||||
bool bAdded = false;
|
||||
QueueData* pQueueData = NULL;
|
||||
|
||||
for (QueueList::iterator it = m_QueueList.begin(); it != m_QueueList.end(); it++)
|
||||
{
|
||||
QueueData* pQueueData1 = *it;
|
||||
if (Util::SameFilename(pQueueData1->GetFilename(), szFullFilename))
|
||||
{
|
||||
pQueueData = pQueueData1;
|
||||
free(szNZBName);
|
||||
szNZBName = strdup(pQueueData->GetNZBName());
|
||||
free(szNZBCategory);
|
||||
szNZBCategory = strdup(pQueueData->GetCategory());
|
||||
iPriority = pQueueData->GetPriority();
|
||||
szDupeKey = pQueueData->GetDupeKey();
|
||||
iDupeScore = pQueueData->GetDupeScore();
|
||||
eDupeMode = pQueueData->GetDupeMode();
|
||||
bAddTop = pQueueData->GetAddTop();
|
||||
bAddPaused = pQueueData->GetAddPaused();
|
||||
pParameters->CopyFrom(pQueueData->GetParameters());
|
||||
}
|
||||
}
|
||||
|
||||
InitPPParameters(szNZBCategory, pParameters);
|
||||
|
||||
bool bExists = true;
|
||||
|
||||
if (m_bNZBScript && strcasecmp(szExtension, ".nzb_processed"))
|
||||
{
|
||||
NZBScriptController::ExecuteScript(g_pOptions->GetNZBProcess(), szFullFilename, szDirectory,
|
||||
&szNZBName, &szNZBCategory, &iPriority, pParameters, &bAddTop, &bAddPaused);
|
||||
bExists = Util::FileExists(szFullFilename);
|
||||
if (bExists && strcasecmp(szExtension, ".nzb"))
|
||||
{
|
||||
char bakname2[1024];
|
||||
bool bRenameOK = Util::RenameBak(szFullFilename, "processed", false, bakname2, 1024);
|
||||
if (!bRenameOK)
|
||||
{
|
||||
char szSysErrStr[256];
|
||||
error("Could not rename file %s to %s: %s", szFullFilename, bakname2, Util::GetLastErrorMessage(szSysErrStr, sizeof(szSysErrStr)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!strcasecmp(szExtension, ".nzb_processed"))
|
||||
{
|
||||
char szRenamedName[1024];
|
||||
bool bRenameOK = Util::RenameBak(szFullFilename, "nzb", true, szRenamedName, 1024);
|
||||
if (bRenameOK)
|
||||
{
|
||||
bAdded = AddFileToQueue(szRenamedName, szNZBName, szNZBCategory, iPriority,
|
||||
szDupeKey, iDupeScore, eDupeMode, pParameters, bAddTop, bAddPaused);
|
||||
}
|
||||
else
|
||||
{
|
||||
char szSysErrStr[256];
|
||||
error("Could not rename file %s to %s: %s", szFullFilename, szRenamedName, Util::GetLastErrorMessage(szSysErrStr, sizeof(szSysErrStr)));
|
||||
eAddStatus = asFailed;
|
||||
}
|
||||
}
|
||||
else if (bExists && !strcasecmp(szExtension, ".nzb"))
|
||||
{
|
||||
bAdded = AddFileToQueue(szFullFilename, szNZBName, szNZBCategory, iPriority,
|
||||
szDupeKey, iDupeScore, eDupeMode, pParameters, bAddTop, bAddPaused);
|
||||
}
|
||||
|
||||
delete pParameters;
|
||||
|
||||
free(szNZBName);
|
||||
free(szNZBCategory);
|
||||
|
||||
if (pQueueData)
|
||||
{
|
||||
pQueueData->SetAddStatus(eAddStatus == asFailed ? asFailed : bAdded ? asSuccess : asSkipped);
|
||||
}
|
||||
}
|
||||
|
||||
void Scanner::InitPPParameters(const char* szCategory, NZBParameterList* pParameters)
|
||||
{
|
||||
bool bUnpack = g_pOptions->GetUnpack();
|
||||
const char* szDefScript = g_pOptions->GetDefScript();
|
||||
|
||||
if (szCategory && *szCategory)
|
||||
{
|
||||
Options::Category* pCategory = g_pOptions->FindCategory(szCategory, false);
|
||||
if (pCategory)
|
||||
{
|
||||
bUnpack = pCategory->GetUnpack();
|
||||
if (pCategory->GetDefScript() && *pCategory->GetDefScript())
|
||||
{
|
||||
szDefScript = pCategory->GetDefScript();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pParameters->SetParameter("*Unpack:", bUnpack ? "yes" : "no");
|
||||
|
||||
if (szDefScript && *szDefScript)
|
||||
{
|
||||
// split szDefScript into tokens and create pp-parameter for each token
|
||||
char* szDefScript2 = strdup(szDefScript);
|
||||
char* saveptr;
|
||||
char* szScriptName = strtok_r(szDefScript2, ",;", &saveptr);
|
||||
while (szScriptName)
|
||||
{
|
||||
szScriptName = Util::Trim(szScriptName);
|
||||
if (szScriptName[0] != '\0')
|
||||
{
|
||||
char szParam[1024];
|
||||
snprintf(szParam, 1024, "%s:", szScriptName);
|
||||
szParam[1024-1] = '\0';
|
||||
pParameters->SetParameter(szParam, "yes");
|
||||
}
|
||||
szScriptName = strtok_r(NULL, ",;", &saveptr);
|
||||
}
|
||||
free(szDefScript2);
|
||||
}
|
||||
}
|
||||
|
||||
bool Scanner::AddFileToQueue(const char* szFilename, const char* szNZBName, const char* szCategory,
|
||||
int iPriority, const char* szDupeKey, int iDupeScore, EDupeMode eDupeMode,
|
||||
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused)
|
||||
{
|
||||
const char* szBasename = Util::BaseFileName(szFilename);
|
||||
|
||||
info("Collection %s found", szBasename);
|
||||
|
||||
NZBFile* pNZBFile = NZBFile::Create(szFilename, szCategory);
|
||||
bool bOK = pNZBFile != NULL;
|
||||
if (!bOK)
|
||||
{
|
||||
error("Could not add collection %s to queue", szBasename);
|
||||
}
|
||||
|
||||
char bakname2[1024];
|
||||
if (!Util::RenameBak(szFilename, pNZBFile ? "queued" : "error", false, bakname2, 1024))
|
||||
{
|
||||
bOK = false;
|
||||
char szSysErrStr[256];
|
||||
error("Could not rename file %s to %s: %s", szFilename, bakname2, Util::GetLastErrorMessage(szSysErrStr, sizeof(szSysErrStr)));
|
||||
}
|
||||
|
||||
if (bOK)
|
||||
{
|
||||
pNZBFile->GetNZBInfo()->SetQueuedFilename(bakname2);
|
||||
|
||||
if (szNZBName && strlen(szNZBName) > 0)
|
||||
{
|
||||
pNZBFile->GetNZBInfo()->SetName(NULL);
|
||||
#ifdef WIN32
|
||||
char* szAnsiFilename = strdup(szNZBName);
|
||||
WebUtil::Utf8ToAnsi(szAnsiFilename, strlen(szAnsiFilename) + 1);
|
||||
pNZBFile->GetNZBInfo()->SetFilename(szAnsiFilename);
|
||||
free(szAnsiFilename);
|
||||
#else
|
||||
pNZBFile->GetNZBInfo()->SetFilename(szNZBName);
|
||||
#endif
|
||||
pNZBFile->GetNZBInfo()->BuildDestDirName();
|
||||
}
|
||||
|
||||
pNZBFile->GetNZBInfo()->SetDupeKey(szDupeKey);
|
||||
pNZBFile->GetNZBInfo()->SetDupeScore(iDupeScore);
|
||||
pNZBFile->GetNZBInfo()->SetDupeMode(eDupeMode);
|
||||
|
||||
if (pNZBFile->GetPassword())
|
||||
{
|
||||
pNZBFile->GetNZBInfo()->GetParameters()->SetParameter("*Unpack:Password", pNZBFile->GetPassword());
|
||||
}
|
||||
|
||||
pNZBFile->GetNZBInfo()->GetParameters()->CopyFrom(pParameters);
|
||||
|
||||
for (NZBFile::FileInfos::iterator it = pNZBFile->GetFileInfos()->begin(); it != pNZBFile->GetFileInfos()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
pFileInfo->SetPriority(iPriority);
|
||||
pFileInfo->SetPaused(bAddPaused);
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, bAddTop);
|
||||
}
|
||||
|
||||
delete pNZBFile;
|
||||
|
||||
return bOK;
|
||||
}
|
||||
|
||||
void Scanner::ScanNZBDir(bool bSyncMode)
|
||||
{
|
||||
m_mutexScan.Lock();
|
||||
m_bScanning = true;
|
||||
m_bRequestedNZBDirScan = true;
|
||||
m_mutexScan.Unlock();
|
||||
|
||||
while (bSyncMode && (m_bScanning || m_bRequestedNZBDirScan))
|
||||
{
|
||||
usleep(100 * 1000);
|
||||
}
|
||||
}
|
||||
|
||||
Scanner::EAddStatus Scanner::AddExternalFile(const char* szNZBName, const char* szCategory,
|
||||
int iPriority, const char* szDupeKey, int iDupeScore, EDupeMode eDupeMode,
|
||||
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused,
|
||||
const char* szFileName, const char* szBuffer, int iBufSize)
|
||||
{
|
||||
bool bNZB = false;
|
||||
char szTempFileName[1024];
|
||||
|
||||
if (szFileName)
|
||||
{
|
||||
strncpy(szTempFileName, szFileName, 1024);
|
||||
szTempFileName[1024-1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
int iNum = 1;
|
||||
while (iNum == 1 || Util::FileExists(szTempFileName))
|
||||
{
|
||||
snprintf(szTempFileName, 1024, "%snzb-%i.tmp", g_pOptions->GetTempDir(), iNum);
|
||||
szTempFileName[1024-1] = '\0';
|
||||
iNum++;
|
||||
}
|
||||
|
||||
if (!Util::SaveBufferIntoFile(szTempFileName, szBuffer, iBufSize))
|
||||
{
|
||||
error("Could not create file %s", szTempFileName);
|
||||
return asFailed;
|
||||
}
|
||||
|
||||
char buf[1024];
|
||||
strncpy(buf, szBuffer, 1024);
|
||||
buf[1024-1] = '\0';
|
||||
bNZB = !strncmp(buf, "<?xml", 5) && strstr(buf, "<nzb");
|
||||
}
|
||||
|
||||
// move file into NzbDir, make sure the file name is unique
|
||||
char szValidNZBName[1024];
|
||||
strncpy(szValidNZBName, Util::BaseFileName(szNZBName), 1024);
|
||||
szValidNZBName[1024-1] = '\0';
|
||||
Util::MakeValidFilename(szValidNZBName, '_', false);
|
||||
|
||||
#ifdef WIN32
|
||||
WebUtil::Utf8ToAnsi(szValidNZBName, 1024);
|
||||
#endif
|
||||
|
||||
const char* szExtension = strrchr(szNZBName, '.');
|
||||
if (bNZB && (!szExtension || strcasecmp(szExtension, ".nzb")))
|
||||
{
|
||||
strncat(szValidNZBName, ".nzb", 1024 - strlen(szValidNZBName) - 1);
|
||||
}
|
||||
|
||||
char szScanFileName[1024];
|
||||
snprintf(szScanFileName, 1024, "%s%s", g_pOptions->GetNzbDir(), szValidNZBName);
|
||||
|
||||
char *szExt = strrchr(szValidNZBName, '.');
|
||||
if (szExt)
|
||||
{
|
||||
*szExt = '\0';
|
||||
szExt++;
|
||||
}
|
||||
|
||||
int iNum = 2;
|
||||
while (Util::FileExists(szScanFileName))
|
||||
{
|
||||
if (szExt)
|
||||
{
|
||||
snprintf(szScanFileName, 1024, "%s%s_%i.%s", g_pOptions->GetNzbDir(), szValidNZBName, iNum, szExt);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(szScanFileName, 1024, "%s%s_%i", g_pOptions->GetNzbDir(), szValidNZBName, iNum);
|
||||
}
|
||||
szScanFileName[1024-1] = '\0';
|
||||
iNum++;
|
||||
}
|
||||
|
||||
m_mutexScan.Lock();
|
||||
|
||||
if (!Util::MoveFile(szTempFileName, szScanFileName))
|
||||
{
|
||||
char szSysErrStr[256];
|
||||
error("Could not move file %s to %s: %s", szTempFileName, szScanFileName, Util::GetLastErrorMessage(szSysErrStr, sizeof(szSysErrStr)));
|
||||
remove(szTempFileName);
|
||||
m_mutexScan.Unlock(); // UNLOCK
|
||||
return asFailed;
|
||||
}
|
||||
|
||||
char* szUseCategory = strdup(szCategory ? szCategory : "");
|
||||
Options::Category *pCategory = g_pOptions->FindCategory(szCategory, true);
|
||||
if (pCategory && strcmp(szCategory, pCategory->GetName()))
|
||||
{
|
||||
free(szUseCategory);
|
||||
szUseCategory = strdup(pCategory->GetName());
|
||||
detail("Category %s matched to %s for %s", szCategory, szUseCategory, szNZBName);
|
||||
}
|
||||
|
||||
EAddStatus eAddStatus = asSkipped;
|
||||
QueueData* pQueueData = new QueueData(szScanFileName, szNZBName, szUseCategory,
|
||||
iPriority, szDupeKey, iDupeScore, eDupeMode, pParameters, bAddTop, bAddPaused, &eAddStatus);
|
||||
free(szUseCategory);
|
||||
m_QueueList.push_back(pQueueData);
|
||||
|
||||
m_mutexScan.Unlock();
|
||||
|
||||
ScanNZBDir(true);
|
||||
|
||||
return eAddStatus;
|
||||
}
|
||||
130
Scanner.h
Normal file
130
Scanner.h
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007-2013 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SCANNER_H
|
||||
#define SCANNER_H
|
||||
|
||||
#include <deque>
|
||||
#include <time.h>
|
||||
#include "DownloadInfo.h"
|
||||
#include "Thread.h"
|
||||
|
||||
class Scanner
|
||||
{
|
||||
public:
|
||||
enum EAddStatus
|
||||
{
|
||||
asSkipped,
|
||||
asSuccess,
|
||||
asFailed
|
||||
};
|
||||
|
||||
private:
|
||||
class FileData
|
||||
{
|
||||
private:
|
||||
char* m_szFilename;
|
||||
long long m_iSize;
|
||||
time_t m_tLastChange;
|
||||
|
||||
public:
|
||||
FileData(const char* szFilename);
|
||||
~FileData();
|
||||
const char* GetFilename() { return m_szFilename; }
|
||||
long long GetSize() { return m_iSize; }
|
||||
void SetSize(long long lSize) { m_iSize = lSize; }
|
||||
time_t GetLastChange() { return m_tLastChange; }
|
||||
void SetLastChange(time_t tLastChange) { m_tLastChange = tLastChange; }
|
||||
};
|
||||
|
||||
typedef std::deque<FileData*> FileList;
|
||||
|
||||
class QueueData
|
||||
{
|
||||
private:
|
||||
char* m_szFilename;
|
||||
char* m_szNZBName;
|
||||
char* m_szCategory;
|
||||
int m_iPriority;
|
||||
char* m_szDupeKey;
|
||||
int m_iDupeScore;
|
||||
EDupeMode m_eDupeMode;
|
||||
NZBParameterList m_Parameters;
|
||||
bool m_bAddTop;
|
||||
bool m_bAddPaused;
|
||||
EAddStatus* m_pAddStatus;
|
||||
|
||||
public:
|
||||
QueueData(const char* szFilename, const char* szNZBName, const char* szCategory,
|
||||
int iPriority, const char* szDupeKey, int iDupeScore, EDupeMode eDupeMode,
|
||||
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused, EAddStatus* pAddStatus);
|
||||
~QueueData();
|
||||
const char* GetFilename() { return m_szFilename; }
|
||||
const char* GetNZBName() { return m_szNZBName; }
|
||||
const char* GetCategory() { return m_szCategory; }
|
||||
int GetPriority() { return m_iPriority; }
|
||||
const char* GetDupeKey() { return m_szDupeKey; }
|
||||
int GetDupeScore() { return m_iDupeScore; }
|
||||
EDupeMode GetDupeMode() { return m_eDupeMode; }
|
||||
NZBParameterList* GetParameters() { return &m_Parameters; }
|
||||
bool GetAddTop() { return m_bAddTop; }
|
||||
bool GetAddPaused() { return m_bAddPaused; }
|
||||
void SetAddStatus(EAddStatus eAddStatus);
|
||||
};
|
||||
|
||||
typedef std::deque<QueueData*> QueueList;
|
||||
|
||||
bool m_bRequestedNZBDirScan;
|
||||
int m_iNZBDirInterval;
|
||||
bool m_bNZBScript;
|
||||
int m_iPass;
|
||||
FileList m_FileList;
|
||||
QueueList m_QueueList;
|
||||
bool m_bScanning;
|
||||
Mutex m_mutexScan;
|
||||
|
||||
void CheckIncomingNZBs(const char* szDirectory, const char* szCategory, bool bCheckStat);
|
||||
bool AddFileToQueue(const char* szFilename, const char* szNZBName, const char* szCategory,
|
||||
int iPriority, const char* szDupeKey, int iDupeScore, EDupeMode eDupeMode,
|
||||
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused);
|
||||
void ProcessIncomingFile(const char* szDirectory, const char* szBaseFilename,
|
||||
const char* szFullFilename, const char* szCategory);
|
||||
bool CanProcessFile(const char* szFullFilename, bool bCheckStat);
|
||||
void InitPPParameters(const char* szCategory, NZBParameterList* pParameters);
|
||||
void DropOldFiles();
|
||||
void ClearQueueList();
|
||||
|
||||
public:
|
||||
Scanner();
|
||||
~Scanner();
|
||||
void ScanNZBDir(bool bSyncMode);
|
||||
void Check();
|
||||
EAddStatus AddExternalFile(const char* szNZBName, const char* szCategory, int iPriority,
|
||||
const char* szDupeKey, int iDupeScore, EDupeMode eDupeMode,
|
||||
NZBParameterList* pParameters, bool bAddTop, bool bAddPaused,
|
||||
const char* szFileName, const char* szBuffer, int iBufSize);
|
||||
};
|
||||
|
||||
#endif
|
||||
352
Scheduler.cpp
Normal file
352
Scheduler.cpp
Normal file
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2008-2013 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Scheduler.h"
|
||||
#include "ScriptController.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "NewsServer.h"
|
||||
#include "ServerPool.h"
|
||||
#include "FeedInfo.h"
|
||||
#include "FeedCoordinator.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
extern ServerPool* g_pServerPool;
|
||||
extern FeedCoordinator* g_pFeedCoordinator;
|
||||
|
||||
Scheduler::Task::Task(int iHours, int iMinutes, int iWeekDaysBits, ECommand eCommand, const char* szParam)
|
||||
{
|
||||
m_iHours = iHours;
|
||||
m_iMinutes = iMinutes;
|
||||
m_iWeekDaysBits = iWeekDaysBits;
|
||||
m_eCommand = eCommand;
|
||||
m_szParam = szParam ? strdup(szParam) : NULL;
|
||||
m_tLastExecuted = 0;
|
||||
}
|
||||
|
||||
Scheduler::Task::~Task()
|
||||
{
|
||||
free(m_szParam);
|
||||
}
|
||||
|
||||
|
||||
Scheduler::Scheduler()
|
||||
{
|
||||
debug("Creating Scheduler");
|
||||
|
||||
m_tLastCheck = 0;
|
||||
m_TaskList.clear();
|
||||
}
|
||||
|
||||
Scheduler::~Scheduler()
|
||||
{
|
||||
debug("Destroying Scheduler");
|
||||
|
||||
for (TaskList::iterator it = m_TaskList.begin(); it != m_TaskList.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
}
|
||||
|
||||
void Scheduler::AddTask(Task* pTask)
|
||||
{
|
||||
m_mutexTaskList.Lock();
|
||||
m_TaskList.push_back(pTask);
|
||||
m_mutexTaskList.Unlock();
|
||||
}
|
||||
|
||||
bool Scheduler::CompareTasks(Scheduler::Task* pTask1, Scheduler::Task* pTask2)
|
||||
{
|
||||
return (pTask1->m_iHours < pTask2->m_iHours) ||
|
||||
((pTask1->m_iHours == pTask2->m_iHours) && (pTask1->m_iMinutes < pTask2->m_iMinutes));
|
||||
}
|
||||
|
||||
void Scheduler::FirstCheck()
|
||||
{
|
||||
m_mutexTaskList.Lock();
|
||||
m_TaskList.sort(CompareTasks);
|
||||
m_mutexTaskList.Unlock();
|
||||
|
||||
// check all tasks for the last week
|
||||
time_t tCurrent = time(NULL);
|
||||
m_tLastCheck = tCurrent - 60*60*24*7;
|
||||
m_bDetectClockChanges = false;
|
||||
m_bExecuteProcess = false;
|
||||
CheckTasks();
|
||||
}
|
||||
|
||||
void Scheduler::IntervalCheck()
|
||||
{
|
||||
m_bDetectClockChanges = true;
|
||||
m_bExecuteProcess = true;
|
||||
CheckTasks();
|
||||
}
|
||||
|
||||
void Scheduler::CheckTasks()
|
||||
{
|
||||
PrepareLog();
|
||||
|
||||
m_mutexTaskList.Lock();
|
||||
|
||||
time_t tCurrent = time(NULL);
|
||||
|
||||
if (m_bDetectClockChanges)
|
||||
{
|
||||
// Detect large step changes of system time
|
||||
time_t tDiff = tCurrent - m_tLastCheck;
|
||||
if (tDiff > 60*90 || tDiff < -60*90)
|
||||
{
|
||||
debug("Reset scheduled tasks (detected clock adjustment greater than 90 minutes)");
|
||||
m_bExecuteProcess = false;
|
||||
m_tLastCheck = tCurrent;
|
||||
|
||||
for (TaskList::iterator it = m_TaskList.begin(); it != m_TaskList.end(); it++)
|
||||
{
|
||||
Task* pTask = *it;
|
||||
pTask->m_tLastExecuted = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tm tmCurrent;
|
||||
localtime_r(&tCurrent, &tmCurrent);
|
||||
tm tmLastCheck;
|
||||
localtime_r(&m_tLastCheck, &tmLastCheck);
|
||||
|
||||
tm tmLoop;
|
||||
memcpy(&tmLoop, &tmLastCheck, sizeof(tmLastCheck));
|
||||
tmLoop.tm_hour = tmCurrent.tm_hour;
|
||||
tmLoop.tm_min = tmCurrent.tm_min;
|
||||
tmLoop.tm_sec = tmCurrent.tm_sec;
|
||||
time_t tLoop = mktime(&tmLoop);
|
||||
|
||||
while (tLoop <= tCurrent)
|
||||
{
|
||||
for (TaskList::iterator it = m_TaskList.begin(); it != m_TaskList.end(); it++)
|
||||
{
|
||||
Task* pTask = *it;
|
||||
if (pTask->m_tLastExecuted != tLoop)
|
||||
{
|
||||
tm tmAppoint;
|
||||
memcpy(&tmAppoint, &tmLoop, sizeof(tmLoop));
|
||||
tmAppoint.tm_hour = pTask->m_iHours;
|
||||
tmAppoint.tm_min = pTask->m_iMinutes;
|
||||
tmAppoint.tm_sec = 0;
|
||||
|
||||
time_t tAppoint = mktime(&tmAppoint);
|
||||
tAppoint -= g_pOptions->GetTimeCorrection();
|
||||
|
||||
int iWeekDay = tmAppoint.tm_wday;
|
||||
if (iWeekDay == 0)
|
||||
{
|
||||
iWeekDay = 7;
|
||||
}
|
||||
|
||||
bool bWeekDayOK = pTask->m_iWeekDaysBits == 0 || (pTask->m_iWeekDaysBits & (1 << (iWeekDay - 1)));
|
||||
bool bDoTask = bWeekDayOK && m_tLastCheck < tAppoint && tAppoint <= tCurrent;
|
||||
|
||||
//debug("TEMP: 1) m_tLastCheck=%i, tCurrent=%i, tLoop=%i, tAppoint=%i, bWeekDayOK=%i, bDoTask=%i", m_tLastCheck, tCurrent, tLoop, tAppoint, (int)bWeekDayOK, (int)bDoTask);
|
||||
|
||||
if (bDoTask)
|
||||
{
|
||||
ExecuteTask(pTask);
|
||||
pTask->m_tLastExecuted = tLoop;
|
||||
}
|
||||
}
|
||||
}
|
||||
tLoop += 60*60*24; // inc day
|
||||
localtime_r(&tLoop, &tmLoop);
|
||||
}
|
||||
|
||||
m_tLastCheck = tCurrent;
|
||||
|
||||
m_mutexTaskList.Unlock();
|
||||
|
||||
PrintLog();
|
||||
}
|
||||
|
||||
void Scheduler::ExecuteTask(Task* pTask)
|
||||
{
|
||||
const char* szCommandName[] = { "Pause", "Unpause", "Set download rate", "Execute program", "Pause Scan", "Unpause Scan",
|
||||
"Enable Server", "Disable Server", "Fetch Feed" };
|
||||
debug("Executing scheduled command: %s", szCommandName[pTask->m_eCommand]);
|
||||
|
||||
switch (pTask->m_eCommand)
|
||||
{
|
||||
case scDownloadRate:
|
||||
if (!Util::EmptyStr(pTask->m_szParam))
|
||||
{
|
||||
g_pOptions->SetDownloadRate(atoi(pTask->m_szParam) * 1024);
|
||||
m_bDownloadRateChanged = true;
|
||||
}
|
||||
break;
|
||||
|
||||
case scPauseDownload:
|
||||
case scUnpauseDownload:
|
||||
m_bPauseDownload = pTask->m_eCommand == scPauseDownload;
|
||||
m_bPauseDownloadChanged = true;
|
||||
break;
|
||||
|
||||
case scProcess:
|
||||
if (m_bExecuteProcess)
|
||||
{
|
||||
SchedulerScriptController::StartScript(pTask->m_szParam);
|
||||
}
|
||||
break;
|
||||
|
||||
case scPauseScan:
|
||||
case scUnpauseScan:
|
||||
g_pOptions->SetPauseScan(pTask->m_eCommand == scPauseScan);
|
||||
m_bPauseScanChanged = true;
|
||||
break;
|
||||
|
||||
case scActivateServer:
|
||||
case scDeactivateServer:
|
||||
EditServer(pTask->m_eCommand == scActivateServer, pTask->m_szParam);
|
||||
break;
|
||||
|
||||
case scFetchFeed:
|
||||
if (m_bExecuteProcess)
|
||||
{
|
||||
FetchFeed(pTask->m_szParam);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Scheduler::PrepareLog()
|
||||
{
|
||||
m_bDownloadRateChanged = false;
|
||||
m_bPauseDownloadChanged = false;
|
||||
m_bPauseScanChanged = false;
|
||||
m_bServerChanged = false;
|
||||
}
|
||||
|
||||
void Scheduler::PrintLog()
|
||||
{
|
||||
if (m_bDownloadRateChanged)
|
||||
{
|
||||
info("Scheduler: setting download rate to %i KB/s", g_pOptions->GetDownloadRate() / 1024);
|
||||
}
|
||||
if (m_bPauseScanChanged)
|
||||
{
|
||||
info("Scheduler: %s scan", g_pOptions->GetPauseScan() ? "pausing" : "unpausing");
|
||||
}
|
||||
if (m_bServerChanged)
|
||||
{
|
||||
int index = 0;
|
||||
for (Servers::iterator it = g_pServerPool->GetServers()->begin(); it != g_pServerPool->GetServers()->end(); it++, index++)
|
||||
{
|
||||
NewsServer* pServer = *it;
|
||||
if (pServer->GetActive() != m_ServerStatusList[index])
|
||||
{
|
||||
info("Scheduler: %s %s", pServer->GetActive() ? "activating" : "deactivating", pServer->GetName());
|
||||
}
|
||||
}
|
||||
g_pServerPool->Changed();
|
||||
}
|
||||
}
|
||||
|
||||
void Scheduler::EditServer(bool bActive, const char* szServerList)
|
||||
{
|
||||
char* szServerList2 = strdup(szServerList);
|
||||
char* saveptr;
|
||||
char* szServer = strtok_r(szServerList2, ",;", &saveptr);
|
||||
while (szServer)
|
||||
{
|
||||
szServer = Util::Trim(szServer);
|
||||
if (!Util::EmptyStr(szServer))
|
||||
{
|
||||
int iID = atoi(szServer);
|
||||
for (Servers::iterator it = g_pServerPool->GetServers()->begin(); it != g_pServerPool->GetServers()->end(); it++)
|
||||
{
|
||||
NewsServer* pServer = *it;
|
||||
if ((iID > 0 && pServer->GetID() == iID) ||
|
||||
!strcasecmp(pServer->GetName(), szServer))
|
||||
{
|
||||
if (!m_bServerChanged)
|
||||
{
|
||||
// store old server status for logging
|
||||
m_ServerStatusList.clear();
|
||||
m_ServerStatusList.reserve(g_pServerPool->GetServers()->size());
|
||||
for (Servers::iterator it2 = g_pServerPool->GetServers()->begin(); it2 != g_pServerPool->GetServers()->end(); it2++)
|
||||
{
|
||||
NewsServer* pServer2 = *it2;
|
||||
m_ServerStatusList.push_back(pServer2->GetActive());
|
||||
}
|
||||
}
|
||||
m_bServerChanged = true;
|
||||
pServer->SetActive(bActive);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
szServer = strtok_r(NULL, ",;", &saveptr);
|
||||
}
|
||||
free(szServerList2);
|
||||
}
|
||||
|
||||
void Scheduler::FetchFeed(const char* szFeedList)
|
||||
{
|
||||
char* szFeedList2 = strdup(szFeedList);
|
||||
char* saveptr;
|
||||
char* szFeed = strtok_r(szFeedList2, ",;", &saveptr);
|
||||
while (szFeed)
|
||||
{
|
||||
szFeed = Util::Trim(szFeed);
|
||||
if (!Util::EmptyStr(szFeed))
|
||||
{
|
||||
int iID = atoi(szFeed);
|
||||
for (Feeds::iterator it = g_pFeedCoordinator->GetFeeds()->begin(); it != g_pFeedCoordinator->GetFeeds()->end(); it++)
|
||||
{
|
||||
FeedInfo* pFeed = *it;
|
||||
if (pFeed->GetID() == iID ||
|
||||
!strcasecmp(pFeed->GetName(), szFeed) ||
|
||||
!strcasecmp("0", szFeed))
|
||||
{
|
||||
g_pFeedCoordinator->FetchFeed(pFeed->GetID());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
szFeed = strtok_r(NULL, ",;", &saveptr);
|
||||
}
|
||||
free(szFeedList2);
|
||||
}
|
||||
102
Scheduler.h
Normal file
102
Scheduler.h
Normal file
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2008-2013 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SCHEDULER_H
|
||||
#define SCHEDULER_H
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <time.h>
|
||||
|
||||
#include "Thread.h"
|
||||
|
||||
class Scheduler
|
||||
{
|
||||
public:
|
||||
enum ECommand
|
||||
{
|
||||
scPauseDownload,
|
||||
scUnpauseDownload,
|
||||
scDownloadRate,
|
||||
scProcess,
|
||||
scPauseScan,
|
||||
scUnpauseScan,
|
||||
scActivateServer,
|
||||
scDeactivateServer,
|
||||
scFetchFeed
|
||||
};
|
||||
|
||||
class Task
|
||||
{
|
||||
private:
|
||||
int m_iHours;
|
||||
int m_iMinutes;
|
||||
int m_iWeekDaysBits;
|
||||
ECommand m_eCommand;
|
||||
char* m_szParam;
|
||||
time_t m_tLastExecuted;
|
||||
|
||||
public:
|
||||
Task(int iHours, int iMinutes, int iWeekDaysBits, ECommand eCommand,
|
||||
const char* szParam);
|
||||
~Task();
|
||||
friend class Scheduler;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
typedef std::list<Task*> TaskList;
|
||||
typedef std::vector<bool> ServerStatusList;
|
||||
|
||||
TaskList m_TaskList;
|
||||
Mutex m_mutexTaskList;
|
||||
time_t m_tLastCheck;
|
||||
bool m_bDetectClockChanges;
|
||||
bool m_bDownloadRateChanged;
|
||||
bool m_bExecuteProcess;
|
||||
bool m_bPauseDownloadChanged;
|
||||
bool m_bPauseDownload;
|
||||
bool m_bPauseScanChanged;
|
||||
bool m_bServerChanged;
|
||||
ServerStatusList m_ServerStatusList;
|
||||
void ExecuteTask(Task* pTask);
|
||||
void CheckTasks();
|
||||
static bool CompareTasks(Scheduler::Task* pTask1, Scheduler::Task* pTask2);
|
||||
void PrepareLog();
|
||||
void PrintLog();
|
||||
void EditServer(bool bActive, const char* szServerList);
|
||||
void FetchFeed(const char* szFeedList);
|
||||
|
||||
public:
|
||||
Scheduler();
|
||||
~Scheduler();
|
||||
void AddTask(Task* pTask);
|
||||
void FirstCheck();
|
||||
void IntervalCheck();
|
||||
bool GetPauseDownloadChanged() { return m_bPauseDownloadChanged; }
|
||||
bool GetPauseDownload() { return m_bPauseDownload; }
|
||||
};
|
||||
|
||||
#endif
|
||||
1202
ScriptController.cpp
1202
ScriptController.cpp
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2013 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,7 +15,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -26,29 +26,142 @@
|
||||
#ifndef SCRIPTCONTROLLER_H
|
||||
#define SCRIPTCONTROLLER_H
|
||||
|
||||
#include "Thread.h"
|
||||
#include "PostInfo.h"
|
||||
#include <list>
|
||||
|
||||
class ScriptController : public Thread
|
||||
#include "Log.h"
|
||||
#include "Thread.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Options.h"
|
||||
|
||||
class EnvironmentStrings
|
||||
{
|
||||
private:
|
||||
typedef std::vector<char*> Strings;
|
||||
|
||||
Strings m_strings;
|
||||
|
||||
public:
|
||||
EnvironmentStrings();
|
||||
~EnvironmentStrings();
|
||||
void Clear();
|
||||
void InitFromCurrentProcess();
|
||||
void Append(char* szString);
|
||||
#ifdef WIN32
|
||||
char* GetStrings();
|
||||
#else
|
||||
char** GetStrings();
|
||||
#endif
|
||||
};
|
||||
|
||||
class ScriptController
|
||||
{
|
||||
private:
|
||||
PostInfo* m_pPostInfo;
|
||||
const char* m_szScript;
|
||||
bool m_bNZBFileCompleted;
|
||||
bool m_bHasFailedParJobs;
|
||||
const char* m_szWorkingDir;
|
||||
const char** m_szArgs;
|
||||
bool m_bFreeArgs;
|
||||
const char* m_szStdArgs[2];
|
||||
const char* m_szInfoName;
|
||||
const char* m_szLogPrefix;
|
||||
EnvironmentStrings m_environmentStrings;
|
||||
bool m_bTerminated;
|
||||
bool m_bDetached;
|
||||
FILE* m_pReadpipe;
|
||||
#ifdef WIN32
|
||||
HANDLE m_hProcess;
|
||||
char m_szCmdLine[2048];
|
||||
#else
|
||||
pid_t m_hProcess;
|
||||
#endif
|
||||
|
||||
void AddMessage(char* szText);
|
||||
protected:
|
||||
void ProcessOutput(char* szText);
|
||||
virtual bool ReadLine(char* szBuf, int iBufSize, FILE* pStream);
|
||||
void PrintMessage(Message::EKind eKind, const char* szFormat, ...);
|
||||
virtual void AddMessage(Message::EKind eKind, const char* szText);
|
||||
bool GetTerminated() { return m_bTerminated; }
|
||||
void ResetEnv();
|
||||
void PrepareEnvOptions(const char* szStripPrefix);
|
||||
void PrepareEnvParameters(NZBInfo* pNZBInfo, const char* szStripPrefix);
|
||||
void PrepareArgs();
|
||||
|
||||
public:
|
||||
ScriptController();
|
||||
virtual ~ScriptController();
|
||||
int Execute();
|
||||
void Terminate();
|
||||
void Detach();
|
||||
|
||||
void SetScript(const char* szScript) { m_szScript = szScript; }
|
||||
const char* GetScript() { return m_szScript; }
|
||||
void SetWorkingDir(const char* szWorkingDir) { m_szWorkingDir = szWorkingDir; }
|
||||
void SetArgs(const char** szArgs, bool bFreeArgs) { m_szArgs = szArgs; m_bFreeArgs = bFreeArgs; }
|
||||
void SetInfoName(const char* szInfoName) { m_szInfoName = szInfoName; }
|
||||
const char* GetInfoName() { return m_szInfoName; }
|
||||
void SetLogPrefix(const char* szLogPrefix) { m_szLogPrefix = szLogPrefix; }
|
||||
void SetEnvVar(const char* szName, const char* szValue);
|
||||
void SetEnvVarSpecial(const char* szPrefix, const char* szName, const char* szValue);
|
||||
void SetIntEnvVar(const char* szName, int iValue);
|
||||
};
|
||||
|
||||
class PostScriptController : public Thread, public ScriptController
|
||||
{
|
||||
private:
|
||||
PostInfo* m_pPostInfo;
|
||||
char m_szNZBName[1024];
|
||||
int m_iPrefixLen;
|
||||
|
||||
void ExecuteScript(const char* szScriptName, const char* szDisplayName, const char* szLocation);
|
||||
void PrepareParams(const char* szScriptName);
|
||||
ScriptStatus::EStatus AnalyseExitCode(int iExitCode);
|
||||
|
||||
typedef std::deque<char*> FileList;
|
||||
|
||||
protected:
|
||||
virtual void AddMessage(Message::EKind eKind, const char* szText);
|
||||
|
||||
public:
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
static void StartScriptJob(PostInfo* pPostInfo, const char* szScript,
|
||||
bool bNZBFileCompleted, bool bHasFailedParJobs);
|
||||
static void StartJob(PostInfo* pPostInfo);
|
||||
static void InitParamsForNewNZB(NZBInfo* pNZBInfo);
|
||||
};
|
||||
|
||||
class NZBScriptController : public ScriptController
|
||||
{
|
||||
private:
|
||||
char** m_pNZBName;
|
||||
char** m_pCategory;
|
||||
int* m_iPriority;
|
||||
NZBParameterList* m_pParameters;
|
||||
bool* m_bAddTop;
|
||||
bool* m_bAddPaused;
|
||||
int m_iPrefixLen;
|
||||
|
||||
protected:
|
||||
virtual void AddMessage(Message::EKind eKind, const char* szText);
|
||||
|
||||
public:
|
||||
static void ExecuteScript(const char* szScript, const char* szNZBFilename, const char* szDirectory,
|
||||
char** pNZBName, char** pCategory, int* iPriority, NZBParameterList* pParameters,
|
||||
bool* bAddTop, bool* bAddPaused);
|
||||
};
|
||||
|
||||
class NZBAddedScriptController : public Thread, public ScriptController
|
||||
{
|
||||
private:
|
||||
char* m_szNZBName;
|
||||
|
||||
public:
|
||||
virtual void Run();
|
||||
static void StartScript(DownloadQueue* pDownloadQueue, NZBInfo *pNZBInfo, const char* szScript);
|
||||
};
|
||||
|
||||
class SchedulerScriptController : public Thread, public ScriptController
|
||||
{
|
||||
public:
|
||||
virtual void Run();
|
||||
static void StartScript(const char* szCommandLine);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
297
ServerPool.cpp
297
ServerPool.cpp
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* 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-2013 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
|
||||
@@ -16,24 +16,16 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -46,6 +38,7 @@
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#endif
|
||||
#include <algorithm>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "ServerPool.h"
|
||||
@@ -63,28 +56,23 @@ ServerPool::ServerPool()
|
||||
{
|
||||
debug("Creating ServerPool");
|
||||
|
||||
m_iMaxLevel = 0;
|
||||
m_iMaxNormLevel = 0;
|
||||
m_iTimeout = 60;
|
||||
m_Servers.clear();
|
||||
m_Connections.clear();
|
||||
m_Semaphores.clear();
|
||||
m_iGeneration = 0;
|
||||
}
|
||||
|
||||
ServerPool::~ ServerPool()
|
||||
{
|
||||
debug("Destroying ServerPool");
|
||||
|
||||
for (Semaphores::iterator it = m_Semaphores.begin(); it != m_Semaphores.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Semaphores.clear();
|
||||
m_Levels.clear();
|
||||
|
||||
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Servers.clear();
|
||||
m_SortedServers.clear();
|
||||
|
||||
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
|
||||
{
|
||||
@@ -98,86 +86,164 @@ void ServerPool::AddServer(NewsServer* pNewsServer)
|
||||
debug("Adding server to ServerPool");
|
||||
|
||||
m_Servers.push_back(pNewsServer);
|
||||
m_SortedServers.push_back(pNewsServer);
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate normalized levels for all servers.
|
||||
* Normalized Level means: starting from 0 with step 1.
|
||||
* The servers of minimum Level must be always used even if they are not active;
|
||||
* this is to prevent backup servers to act as main servers.
|
||||
**/
|
||||
void ServerPool::NormalizeLevels()
|
||||
{
|
||||
if (m_Servers.empty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::sort(m_SortedServers.begin(), m_SortedServers.end(), CompareServers);
|
||||
|
||||
// find minimum level
|
||||
int iMinLevel = m_SortedServers.front()->GetLevel();
|
||||
for (Servers::iterator it = m_SortedServers.begin(); it != m_SortedServers.end(); it++)
|
||||
{
|
||||
NewsServer* pNewsServer = *it;
|
||||
if (pNewsServer->GetLevel() < iMinLevel)
|
||||
{
|
||||
iMinLevel = pNewsServer->GetLevel();
|
||||
}
|
||||
}
|
||||
|
||||
m_iMaxNormLevel = 0;
|
||||
int iLastLevel = iMinLevel;
|
||||
for (Servers::iterator it = m_SortedServers.begin(); it != m_SortedServers.end(); it++)
|
||||
{
|
||||
NewsServer* pNewsServer = *it;
|
||||
if ((pNewsServer->GetActive() && pNewsServer->GetMaxConnections() > 0) ||
|
||||
(pNewsServer->GetLevel() == iMinLevel))
|
||||
{
|
||||
if (pNewsServer->GetLevel() != iLastLevel)
|
||||
{
|
||||
m_iMaxNormLevel++;
|
||||
}
|
||||
pNewsServer->SetNormLevel(m_iMaxNormLevel);
|
||||
iLastLevel = pNewsServer->GetLevel();
|
||||
}
|
||||
else
|
||||
{
|
||||
pNewsServer->SetNormLevel(-1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ServerPool::CompareServers(NewsServer* pServer1, NewsServer* pServer2)
|
||||
{
|
||||
return pServer1->GetLevel() < pServer2->GetLevel();
|
||||
}
|
||||
|
||||
void ServerPool::InitConnections()
|
||||
{
|
||||
debug("Initializing connections in ServerPool");
|
||||
|
||||
m_iMaxLevel = 0;
|
||||
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
|
||||
m_mutexConnections.Lock();
|
||||
|
||||
NormalizeLevels();
|
||||
m_Levels.clear();
|
||||
|
||||
for (Servers::iterator it = m_SortedServers.begin(); it != m_SortedServers.end(); it++)
|
||||
{
|
||||
NewsServer* pNewsServer = *it;
|
||||
if (m_iMaxLevel < pNewsServer->GetLevel())
|
||||
int iNormLevel = pNewsServer->GetNormLevel();
|
||||
if (pNewsServer->GetNormLevel() > -1)
|
||||
{
|
||||
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())
|
||||
if ((int)m_Levels.size() <= iNormLevel)
|
||||
{
|
||||
iMaxConnectionsForLevel += pNewsServer->GetMaxConnections();
|
||||
m_Levels.push_back(0);
|
||||
}
|
||||
|
||||
if (pNewsServer->GetActive())
|
||||
{
|
||||
int iConnections = 0;
|
||||
|
||||
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
|
||||
{
|
||||
PooledConnection* pConnection = *it;
|
||||
if (pConnection->GetNewsServer() == pNewsServer)
|
||||
{
|
||||
iConnections++;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = iConnections; i < pNewsServer->GetMaxConnections(); i++)
|
||||
{
|
||||
PooledConnection* pConnection = new PooledConnection(pNewsServer);
|
||||
pConnection->SetTimeout(m_iTimeout);
|
||||
m_Connections.push_back(pConnection);
|
||||
iConnections++;
|
||||
}
|
||||
|
||||
m_Levels[iNormLevel] += iConnections;
|
||||
}
|
||||
}
|
||||
|
||||
Semaphore* sem = new Semaphore(iMaxConnectionsForLevel);
|
||||
m_Semaphores.push_back(sem);
|
||||
}
|
||||
|
||||
m_iGeneration++;
|
||||
|
||||
m_mutexConnections.Unlock();
|
||||
}
|
||||
|
||||
NNTPConnection* ServerPool::GetConnection(int iLevel, bool bWait)
|
||||
NNTPConnection* ServerPool::GetConnection(int iLevel, NewsServer* pWantServer, Servers* pIgnoreServers)
|
||||
{
|
||||
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;
|
||||
}
|
||||
PooledConnection* pConnection = NULL;
|
||||
|
||||
m_mutexConnections.Lock();
|
||||
|
||||
PooledConnection* pConnection = NULL;
|
||||
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
|
||||
if (iLevel < (int)m_Levels.size() && m_Levels[iLevel] > 0)
|
||||
{
|
||||
PooledConnection* pConnection1 = *it;
|
||||
if (!pConnection1->GetInUse() && pConnection1->GetNewsServer()->GetLevel() == iLevel)
|
||||
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
|
||||
{
|
||||
// free connection found, take it!
|
||||
pConnection = pConnection1;
|
||||
pConnection->SetInUse(true);
|
||||
break;
|
||||
PooledConnection* pCandidateConnection = *it;
|
||||
NewsServer* pCandidateServer = pCandidateConnection->GetNewsServer();
|
||||
if (!pCandidateConnection->GetInUse() && pCandidateServer->GetActive() &&
|
||||
pCandidateServer->GetNormLevel() == iLevel &&
|
||||
(!pWantServer || pCandidateServer == pWantServer ||
|
||||
(pWantServer->GetGroup() > 0 && pWantServer->GetGroup() == pCandidateServer->GetGroup())))
|
||||
{
|
||||
// free connection found, check if it's not from the server which should be ignored
|
||||
bool bUseConnection = true;
|
||||
if (pIgnoreServers && !pWantServer)
|
||||
{
|
||||
for (Servers::iterator it = pIgnoreServers->begin(); it != pIgnoreServers->end(); it++)
|
||||
{
|
||||
NewsServer* pIgnoreServer = *it;
|
||||
if (pIgnoreServer == pCandidateServer ||
|
||||
(pIgnoreServer->GetGroup() > 0 && pIgnoreServer->GetGroup() == pCandidateServer->GetGroup() &&
|
||||
pIgnoreServer->GetNormLevel() == pCandidateServer->GetNormLevel()))
|
||||
{
|
||||
bUseConnection = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bUseConnection)
|
||||
{
|
||||
pConnection = pCandidateConnection;
|
||||
pConnection->SetInUse(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pConnection)
|
||||
{
|
||||
m_Levels[iLevel]--;
|
||||
}
|
||||
}
|
||||
|
||||
m_mutexConnections.Unlock();
|
||||
|
||||
if (!pConnection)
|
||||
{
|
||||
error("ServerPool: serious error, no free connection found, but there should be one.");
|
||||
}
|
||||
|
||||
return pConnection;
|
||||
}
|
||||
|
||||
@@ -195,7 +261,11 @@ void ServerPool::FreeConnection(NNTPConnection* pConnection, bool bUsed)
|
||||
{
|
||||
((PooledConnection*)pConnection)->SetFreeTimeNow();
|
||||
}
|
||||
m_Semaphores[pConnection->GetNewsServer()->GetLevel()]->Post();
|
||||
|
||||
if (pConnection->GetNewsServer()->GetNormLevel() > -1 && pConnection->GetNewsServer()->GetActive())
|
||||
{
|
||||
m_Levels[pConnection->GetNewsServer()->GetNormLevel()]++;
|
||||
}
|
||||
|
||||
m_mutexConnections.Unlock();
|
||||
}
|
||||
@@ -206,46 +276,89 @@ void ServerPool::CloseUnusedConnections()
|
||||
|
||||
time_t curtime = ::time(NULL);
|
||||
|
||||
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
|
||||
int i = 0;
|
||||
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); )
|
||||
{
|
||||
PooledConnection* pConnection = *it;
|
||||
if (!pConnection->GetInUse() && pConnection->GetStatus() == Connection::csConnected)
|
||||
bool bDeleted = false;
|
||||
|
||||
if (!pConnection->GetInUse() &&
|
||||
(pConnection->GetNewsServer()->GetNormLevel() == -1 ||
|
||||
!pConnection->GetNewsServer()->GetActive()))
|
||||
{
|
||||
debug("Closing (and deleting) unused connection to server%i", pConnection->GetNewsServer()->GetID());
|
||||
if (pConnection->GetStatus() == Connection::csConnected)
|
||||
{
|
||||
pConnection->Disconnect();
|
||||
}
|
||||
delete pConnection;
|
||||
m_Connections.erase(it);
|
||||
it = m_Connections.begin() + i;
|
||||
bDeleted = true;
|
||||
}
|
||||
|
||||
if (!bDeleted && !pConnection->GetInUse() && pConnection->GetStatus() == Connection::csConnected)
|
||||
{
|
||||
int tdiff = (int)(curtime - pConnection->GetFreeTime());
|
||||
if (tdiff > CONNECTION_HOLD_SECODNS)
|
||||
{
|
||||
debug("Closing unused connection to %s", pConnection->GetNewsServer()->GetHost());
|
||||
debug("Closing (and keeping) unused connection to server%i", pConnection->GetNewsServer()->GetID());
|
||||
pConnection->Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
if (!bDeleted)
|
||||
{
|
||||
it++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
m_mutexConnections.Unlock();
|
||||
}
|
||||
|
||||
void ServerPool::Changed()
|
||||
{
|
||||
debug("Server config has been changed");
|
||||
|
||||
InitConnections();
|
||||
CloseUnusedConnections();
|
||||
}
|
||||
|
||||
void ServerPool::LogDebugInfo()
|
||||
{
|
||||
debug(" ServerPool");
|
||||
debug(" ----------------");
|
||||
|
||||
debug(" Max-Level: %i", m_iMaxLevel);
|
||||
debug(" Max-Level: %i", m_iMaxNormLevel);
|
||||
|
||||
m_mutexConnections.Lock();
|
||||
|
||||
debug(" Servers: %i", m_Servers.size());
|
||||
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
|
||||
{
|
||||
NewsServer* pNewsServer = *it;
|
||||
debug(" %i) %s (%s): Level=%i, NormLevel=%i", pNewsServer->GetID(), pNewsServer->GetName(),
|
||||
pNewsServer->GetHost(), pNewsServer->GetLevel(), pNewsServer->GetNormLevel());
|
||||
}
|
||||
|
||||
debug(" Levels: %i", m_Levels.size());
|
||||
int index = 0;
|
||||
for (Levels::iterator it = m_Levels.begin(); it != m_Levels.end(); it++, index++)
|
||||
{
|
||||
int iSize = *it;
|
||||
debug(" %i: Size=%i", index, iSize);
|
||||
}
|
||||
|
||||
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());
|
||||
PooledConnection* pConnection = *it;
|
||||
debug(" %i) %s (%s): Level=%i, NormLevel=%i, InUse:%i", pConnection->GetNewsServer()->GetID(),
|
||||
pConnection->GetNewsServer()->GetName(), pConnection->GetNewsServer()->GetHost(),
|
||||
pConnection->GetNewsServer()->GetLevel(), pConnection->GetNewsServer()->GetNormLevel(),
|
||||
(int)pConnection->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();
|
||||
}
|
||||
|
||||
34
ServerPool.h
34
ServerPool.h
@@ -1,9 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is 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>
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2013 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
|
||||
@@ -17,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -51,27 +50,34 @@ private:
|
||||
void SetFreeTimeNow() { m_tFreeTime = ::time(NULL); }
|
||||
};
|
||||
|
||||
typedef std::vector<NewsServer*> Servers;
|
||||
typedef std::vector<Semaphore*> Semaphores;
|
||||
typedef std::vector<int> Levels;
|
||||
typedef std::vector<PooledConnection*> Connections;
|
||||
|
||||
Servers m_Servers;
|
||||
Servers m_SortedServers;
|
||||
Connections m_Connections;
|
||||
Semaphores m_Semaphores;
|
||||
int m_iMaxLevel;
|
||||
Levels m_Levels;
|
||||
int m_iMaxNormLevel;
|
||||
Mutex m_mutexConnections;
|
||||
int m_iTimeout;
|
||||
int m_iGeneration;
|
||||
|
||||
void NormalizeLevels();
|
||||
static bool CompareServers(NewsServer* pServer1, NewsServer* pServer2);
|
||||
|
||||
public:
|
||||
ServerPool();
|
||||
~ServerPool();
|
||||
ServerPool();
|
||||
~ServerPool();
|
||||
void SetTimeout(int iTimeout) { m_iTimeout = iTimeout; }
|
||||
void AddServer(NewsServer *s);
|
||||
void AddServer(NewsServer* pNewsServer);
|
||||
void InitConnections();
|
||||
int GetMaxLevel() { return m_iMaxLevel; }
|
||||
NNTPConnection* GetConnection(int iLevel, bool bWait);
|
||||
int GetMaxNormLevel() { return m_iMaxNormLevel; }
|
||||
Servers* GetServers() { return &m_Servers; } // Only for read access (no lockings)
|
||||
NNTPConnection* GetConnection(int iLevel, NewsServer* pWantServer, Servers* pIgnoreServers);
|
||||
void FreeConnection(NNTPConnection* pConnection, bool bUsed);
|
||||
void CloseUnusedConnections();
|
||||
void Changed();
|
||||
int GetGeneration() { return m_iGeneration; }
|
||||
|
||||
void LogDebugInfo();
|
||||
};
|
||||
|
||||
553
TLS.cpp
Normal file
553
TLS.cpp
Normal file
@@ -0,0 +1,553 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2008-2013 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#define SKIP_DEFAULT_WINDOWS_HEADERS
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_TLS
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <strings.h>
|
||||
#endif
|
||||
#include <ctype.h>
|
||||
#include <limits.h>
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <list>
|
||||
|
||||
#ifdef WIN32
|
||||
#include "nzbget.h"
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
#include <gnutls/gnutls.h>
|
||||
#if GNUTLS_VERSION_NUMBER <= 0x020b00
|
||||
#define NEED_GCRYPT_LOCKING
|
||||
#endif
|
||||
#ifdef NEED_GCRYPT_LOCKING
|
||||
#include <gcrypt.h>
|
||||
#endif /* NEED_GCRYPT_LOCKING */
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
#ifdef HAVE_OPENSSL
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/err.h>
|
||||
#endif /* HAVE_OPENSSL */
|
||||
|
||||
#ifndef WIN32
|
||||
#include "nzbget.h"
|
||||
#endif
|
||||
|
||||
#include "TLS.h"
|
||||
#include "Thread.h"
|
||||
#include "Log.h"
|
||||
|
||||
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
#ifdef NEED_GCRYPT_LOCKING
|
||||
|
||||
/**
|
||||
* Mutexes for gcryptlib
|
||||
*/
|
||||
|
||||
typedef std::list<Mutex*> Mutexes;
|
||||
Mutexes* g_pGCryptLibMutexes;
|
||||
|
||||
static int gcry_mutex_init(void **priv)
|
||||
{
|
||||
Mutex* pMutex = new Mutex();
|
||||
g_pGCryptLibMutexes->push_back(pMutex);
|
||||
*priv = pMutex;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gcry_mutex_destroy(void **lock)
|
||||
{
|
||||
Mutex* pMutex = ((Mutex*)*lock);
|
||||
g_pGCryptLibMutexes->remove(pMutex);
|
||||
delete pMutex;
|
||||
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, NULL,
|
||||
gcry_mutex_init, gcry_mutex_destroy,
|
||||
gcry_mutex_lock, gcry_mutex_unlock,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
|
||||
};
|
||||
|
||||
#endif /* NEED_GCRYPT_LOCKING */
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
|
||||
/**
|
||||
* Mutexes for OpenSSL
|
||||
*/
|
||||
|
||||
Mutex* *g_pOpenSSLMutexes;
|
||||
|
||||
static void openssl_locking(int mode, int n, const char *file, int line)
|
||||
{
|
||||
Mutex* mutex = g_pOpenSSLMutexes[n];
|
||||
if (mode & CRYPTO_LOCK)
|
||||
{
|
||||
mutex->Lock();
|
||||
}
|
||||
else
|
||||
{
|
||||
mutex->Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
static unsigned long openssl_thread_id(void)
|
||||
{
|
||||
#ifdef WIN32
|
||||
return (unsigned long)GetCurrentThreadId();
|
||||
#else
|
||||
return (unsigned long)pthread_self();
|
||||
#endif
|
||||
}
|
||||
*/
|
||||
|
||||
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 /* HAVE_OPENSSL */
|
||||
|
||||
|
||||
void TLSSocket::Init()
|
||||
{
|
||||
debug("Initializing TLS library");
|
||||
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
#ifdef NEED_GCRYPT_LOCKING
|
||||
g_pGCryptLibMutexes = new Mutexes();
|
||||
#endif /* NEED_GCRYPT_LOCKING */
|
||||
|
||||
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
|
||||
int iMaxMutexes = CRYPTO_num_locks();
|
||||
g_pOpenSSLMutexes = (Mutex**)malloc(sizeof(Mutex*)*iMaxMutexes);
|
||||
for (int i=0; i < iMaxMutexes; i++)
|
||||
{
|
||||
g_pOpenSSLMutexes[i] = new Mutex();
|
||||
}
|
||||
|
||||
SSL_load_error_strings();
|
||||
SSL_library_init();
|
||||
OpenSSL_add_all_algorithms();
|
||||
|
||||
CRYPTO_set_locking_callback(openssl_locking);
|
||||
//CRYPTO_set_id_callback(openssl_thread_id);
|
||||
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 /* HAVE_OPENSSL */
|
||||
}
|
||||
|
||||
void TLSSocket::Final()
|
||||
{
|
||||
debug("Finalizing TLS library");
|
||||
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
gnutls_global_deinit();
|
||||
|
||||
#ifdef NEED_GCRYPT_LOCKING
|
||||
// fixing memory leak in gcryptlib
|
||||
for (Mutexes::iterator it = g_pGCryptLibMutexes->begin(); it != g_pGCryptLibMutexes->end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
delete g_pGCryptLibMutexes;
|
||||
#endif /* NEED_GCRYPT_LOCKING */
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
int iMaxMutexes = CRYPTO_num_locks();
|
||||
for (int i=0; i < iMaxMutexes; i++)
|
||||
{
|
||||
delete g_pOpenSSLMutexes[i];
|
||||
}
|
||||
free(g_pOpenSSLMutexes);
|
||||
#endif /* HAVE_OPENSSL */
|
||||
}
|
||||
|
||||
TLSSocket::TLSSocket(SOCKET iSocket, bool bIsClient, const char* szCertFile, const char* szKeyFile, const char* szCipher)
|
||||
{
|
||||
m_iSocket = iSocket;
|
||||
m_bIsClient = bIsClient;
|
||||
m_szCertFile = szCertFile ? strdup(szCertFile) : NULL;
|
||||
m_szKeyFile = szKeyFile ? strdup(szKeyFile) : NULL;
|
||||
m_szCipher = szCipher && strlen(szCipher) > 0 ? strdup(szCipher) : NULL;
|
||||
m_pContext = NULL;
|
||||
m_pSession = NULL;
|
||||
m_bSuppressErrors = false;
|
||||
m_bInitialized = false;
|
||||
m_bConnected = false;
|
||||
}
|
||||
|
||||
TLSSocket::~TLSSocket()
|
||||
{
|
||||
free(m_szCertFile);
|
||||
free(m_szKeyFile);
|
||||
free(m_szCipher);
|
||||
Close();
|
||||
}
|
||||
|
||||
void TLSSocket::ReportError(const char* szErrMsg)
|
||||
{
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
const char* errstr = gnutls_strerror(m_iRetCode);
|
||||
if (m_bSuppressErrors)
|
||||
{
|
||||
debug("%s: %s", szErrMsg, errstr);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("%s: %s", szErrMsg, errstr);
|
||||
}
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
int errcode;
|
||||
do
|
||||
{
|
||||
errcode = ERR_get_error();
|
||||
|
||||
char errstr[1024];
|
||||
ERR_error_string_n(errcode, errstr, sizeof(errstr));
|
||||
errstr[1024-1] = '\0';
|
||||
|
||||
if (m_bSuppressErrors)
|
||||
{
|
||||
debug("%s: %s", szErrMsg, errstr);
|
||||
}
|
||||
else if (errcode != 0)
|
||||
{
|
||||
error("%s: %s", szErrMsg, errstr);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("%s", szErrMsg);
|
||||
}
|
||||
} while (errcode);
|
||||
#endif /* HAVE_OPENSSL */
|
||||
}
|
||||
|
||||
bool TLSSocket::Start()
|
||||
{
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
gnutls_certificate_credentials_t cred;
|
||||
m_iRetCode = gnutls_certificate_allocate_credentials(&cred);
|
||||
if (m_iRetCode != 0)
|
||||
{
|
||||
ReportError("Could not create TLS context");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_pContext = cred;
|
||||
|
||||
if (m_szCertFile && m_szKeyFile)
|
||||
{
|
||||
m_iRetCode = gnutls_certificate_set_x509_key_file((gnutls_certificate_credentials_t)m_pContext,
|
||||
m_szCertFile, m_szKeyFile, GNUTLS_X509_FMT_PEM);
|
||||
if (m_iRetCode != 0)
|
||||
{
|
||||
ReportError("Could not load certificate or key file");
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
gnutls_session_t sess;
|
||||
m_iRetCode = gnutls_init(&sess, m_bIsClient ? GNUTLS_CLIENT : GNUTLS_SERVER);
|
||||
if (m_iRetCode != 0)
|
||||
{
|
||||
ReportError("Could not create TLS session");
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_pSession = sess;
|
||||
|
||||
m_bInitialized = true;
|
||||
|
||||
const char* szPriority = m_szCipher ? m_szCipher : "NORMAL";
|
||||
|
||||
m_iRetCode = gnutls_priority_set_direct((gnutls_session_t)m_pSession, szPriority, NULL);
|
||||
if (m_iRetCode != 0)
|
||||
{
|
||||
ReportError("Could not select cipher for TLS session");
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_iRetCode = gnutls_credentials_set((gnutls_session_t)m_pSession, GNUTLS_CRD_CERTIFICATE,
|
||||
(gnutls_certificate_credentials_t*)m_pContext);
|
||||
if (m_iRetCode != 0)
|
||||
{
|
||||
ReportError("Could not initialize TLS session");
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
gnutls_transport_set_ptr((gnutls_session_t)m_pSession, (gnutls_transport_ptr_t)(size_t)m_iSocket);
|
||||
|
||||
m_iRetCode = gnutls_handshake((gnutls_session_t)m_pSession);
|
||||
if (m_iRetCode != 0)
|
||||
{
|
||||
ReportError("TLS handshake failed");
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_bConnected = true;
|
||||
return true;
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
m_pContext = SSL_CTX_new(SSLv23_method());
|
||||
|
||||
if (!m_pContext)
|
||||
{
|
||||
ReportError("Could not create TLS context");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_szCertFile && m_szKeyFile)
|
||||
{
|
||||
if (SSL_CTX_use_certificate_file((SSL_CTX*)m_pContext, m_szCertFile, SSL_FILETYPE_PEM) != 1)
|
||||
{
|
||||
ReportError("Could not load certificate file");
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
if (SSL_CTX_use_PrivateKey_file((SSL_CTX*)m_pContext, m_szKeyFile, SSL_FILETYPE_PEM) != 1)
|
||||
{
|
||||
ReportError("Could not load key file");
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_pSession = SSL_new((SSL_CTX*)m_pContext);
|
||||
if (!m_pSession)
|
||||
{
|
||||
ReportError("Could not create TLS session");
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_szCipher && !SSL_set_cipher_list((SSL*)m_pSession, m_szCipher))
|
||||
{
|
||||
ReportError("Could not select cipher for TLS");
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!SSL_set_fd((SSL*)m_pSession, m_iSocket))
|
||||
{
|
||||
ReportError("Could not set the file descriptor for TLS");
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
int error_code = m_bIsClient ? SSL_connect((SSL*)m_pSession) : SSL_accept((SSL*)m_pSession);
|
||||
if (error_code < 1)
|
||||
{
|
||||
ReportError("TLS handshake failed");
|
||||
Close();
|
||||
return false;
|
||||
}
|
||||
|
||||
m_bConnected = true;
|
||||
return true;
|
||||
#endif /* HAVE_OPENSSL */
|
||||
}
|
||||
|
||||
void TLSSocket::Close()
|
||||
{
|
||||
if (m_pSession)
|
||||
{
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
if (m_bConnected)
|
||||
{
|
||||
gnutls_bye((gnutls_session_t)m_pSession, GNUTLS_SHUT_WR);
|
||||
}
|
||||
if (m_bInitialized)
|
||||
{
|
||||
gnutls_deinit((gnutls_session_t)m_pSession);
|
||||
}
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
if (m_bConnected)
|
||||
{
|
||||
SSL_shutdown((SSL*)m_pSession);
|
||||
}
|
||||
SSL_free((SSL*)m_pSession);
|
||||
#endif /* HAVE_OPENSSL */
|
||||
|
||||
m_pSession = NULL;
|
||||
}
|
||||
|
||||
if (m_pContext)
|
||||
{
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
gnutls_certificate_free_credentials((gnutls_certificate_credentials_t)m_pContext);
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
SSL_CTX_free((SSL_CTX*)m_pContext);
|
||||
#endif /* HAVE_OPENSSL */
|
||||
|
||||
m_pContext = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
int TLSSocket::Send(const char* pBuffer, int iSize)
|
||||
{
|
||||
int ret;
|
||||
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
ret = gnutls_record_send((gnutls_session_t)m_pSession, pBuffer, iSize);
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
ret = SSL_write((SSL*)m_pSession, pBuffer, iSize);
|
||||
#endif /* HAVE_OPENSSL */
|
||||
|
||||
if (ret < 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 ret;
|
||||
}
|
||||
|
||||
int TLSSocket::Recv(char* pBuffer, int iSize)
|
||||
{
|
||||
int ret;
|
||||
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
ret = gnutls_record_recv((gnutls_session_t)m_pSession, pBuffer, iSize);
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
ret = SSL_read((SSL*)m_pSession, pBuffer, iSize);
|
||||
#endif /* HAVE_OPENSSL */
|
||||
|
||||
if (ret < 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 ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
62
TLS.h
Normal file
62
TLS.h
Normal file
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2008-2013 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef TLS_H
|
||||
#define TLS_H
|
||||
|
||||
#ifndef DISABLE_TLS
|
||||
|
||||
class TLSSocket
|
||||
{
|
||||
private:
|
||||
bool m_bIsClient;
|
||||
char* m_szCertFile;
|
||||
char* m_szKeyFile;
|
||||
char* m_szCipher;
|
||||
SOCKET m_iSocket;
|
||||
bool m_bSuppressErrors;
|
||||
int m_iRetCode;
|
||||
bool m_bInitialized;
|
||||
bool m_bConnected;
|
||||
|
||||
// using "void*" to prevent the including of GnuTLS/OpenSSL header files into TLS.h
|
||||
void* m_pContext;
|
||||
void* m_pSession;
|
||||
|
||||
void ReportError(const char* szErrMsg);
|
||||
|
||||
public:
|
||||
TLSSocket(SOCKET iSocket, bool bIsClient, const char* szCertFile, const char* szKeyFile, const char* szCipher);
|
||||
~TLSSocket();
|
||||
static void Init();
|
||||
static void Final();
|
||||
bool Start();
|
||||
void Close();
|
||||
int Send(const char* pBuffer, int iSize);
|
||||
int Recv(char* pBuffer, int iSize);
|
||||
void SetSuppressErrors(bool bSuppressErrors) { m_bSuppressErrors = bSuppressErrors; }
|
||||
};
|
||||
|
||||
#endif
|
||||
#endif
|
||||
172
Thread.cpp
172
Thread.cpp
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* 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-2010 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -34,160 +34,136 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <process.h>
|
||||
#else
|
||||
#include <fcntl.h>
|
||||
#include <pthread.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* Thread::m_pMutexThread;
|
||||
|
||||
|
||||
Mutex::Mutex()
|
||||
{
|
||||
#ifdef WIN32
|
||||
InitializeCriticalSection(&m_mutexObj);
|
||||
m_pMutexObj = (CRITICAL_SECTION*)malloc(sizeof(CRITICAL_SECTION));
|
||||
InitializeCriticalSection((CRITICAL_SECTION*)m_pMutexObj);
|
||||
#else
|
||||
pthread_mutex_init(&m_mutexObj, NULL);
|
||||
m_pMutexObj = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));
|
||||
pthread_mutex_init((pthread_mutex_t*)m_pMutexObj, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
Mutex::~ Mutex()
|
||||
{
|
||||
#ifdef WIN32
|
||||
DeleteCriticalSection(&m_mutexObj);
|
||||
DeleteCriticalSection((CRITICAL_SECTION*)m_pMutexObj);
|
||||
#else
|
||||
pthread_mutex_destroy(&m_mutexObj);
|
||||
pthread_mutex_destroy((pthread_mutex_t*)m_pMutexObj);
|
||||
#endif
|
||||
free(m_pMutexObj);
|
||||
}
|
||||
|
||||
void Mutex::Lock()
|
||||
{
|
||||
#ifdef WIN32
|
||||
EnterCriticalSection(&m_mutexObj);
|
||||
EnterCriticalSection((CRITICAL_SECTION*)m_pMutexObj);
|
||||
#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)
|
||||
if (((CRITICAL_SECTION*)m_pMutexObj)->RecursionCount > 1)
|
||||
{
|
||||
error("Internal program error: inconsistent thread-lock detected");
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
pthread_mutex_lock(&m_mutexObj);
|
||||
pthread_mutex_lock((pthread_mutex_t*)m_pMutexObj);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Mutex::Unlock()
|
||||
{
|
||||
#ifdef WIN32
|
||||
LeaveCriticalSection(&m_mutexObj);
|
||||
LeaveCriticalSection((CRITICAL_SECTION*)m_pMutexObj);
|
||||
#else
|
||||
pthread_mutex_unlock(&m_mutexObj);
|
||||
pthread_mutex_unlock((pthread_mutex_t*)m_pMutexObj);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Semaphore::Semaphore()
|
||||
#ifdef HAVE_SPINLOCK
|
||||
SpinLock::SpinLock()
|
||||
{
|
||||
#ifdef WIN32
|
||||
m_semObj = CreateSemaphore(NULL, 0, 1, NULL);
|
||||
m_pSpinLockObj = (CRITICAL_SECTION *)malloc(sizeof(CRITICAL_SECTION));
|
||||
InitializeCriticalSectionAndSpinCount((CRITICAL_SECTION *)m_pSpinLockObj, 0x00FFFFFF);
|
||||
#else
|
||||
sem_init(&m_semObj, 0, 0);
|
||||
m_pSpinLockObj = (pthread_spinlock_t *)malloc(sizeof(pthread_spinlock_t));
|
||||
pthread_spin_init((pthread_spinlock_t *)m_pSpinLockObj, PTHREAD_PROCESS_PRIVATE);
|
||||
#endif
|
||||
}
|
||||
|
||||
Semaphore::Semaphore(int iValue)
|
||||
SpinLock::~SpinLock()
|
||||
{
|
||||
#ifdef WIN32
|
||||
m_semObj = CreateSemaphore(NULL, iValue, iValue, NULL);
|
||||
DeleteCriticalSection((CRITICAL_SECTION *)m_pSpinLockObj);
|
||||
#else
|
||||
sem_init(&m_semObj, 0, iValue);
|
||||
pthread_spin_destroy((pthread_spinlock_t *)m_pSpinLockObj);
|
||||
#endif
|
||||
free((void*)m_pSpinLockObj);
|
||||
}
|
||||
|
||||
void SpinLock::Lock()
|
||||
{
|
||||
#ifdef WIN32
|
||||
EnterCriticalSection((CRITICAL_SECTION *)m_pSpinLockObj);
|
||||
#else
|
||||
pthread_spin_lock((pthread_spinlock_t *)m_pSpinLockObj);
|
||||
#endif
|
||||
}
|
||||
|
||||
Semaphore::~ Semaphore()
|
||||
void SpinLock::Unlock()
|
||||
{
|
||||
#ifdef WIN32
|
||||
CloseHandle(m_semObj);
|
||||
LeaveCriticalSection((CRITICAL_SECTION *)m_pSpinLockObj);
|
||||
#else
|
||||
sem_destroy(&m_semObj);
|
||||
pthread_spin_unlock((pthread_spinlock_t *)m_pSpinLockObj);
|
||||
#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");
|
||||
|
||||
m_pMutexThread = new Mutex();
|
||||
}
|
||||
|
||||
void Thread::Final()
|
||||
{
|
||||
debug("Finalizing global thread data");
|
||||
|
||||
delete m_pMutexThread;
|
||||
}
|
||||
|
||||
Thread::Thread()
|
||||
{
|
||||
debug("Creating Thread");
|
||||
|
||||
m_Thread = 0;
|
||||
#ifdef WIN32
|
||||
m_pThreadObj = NULL;
|
||||
#else
|
||||
m_pThreadObj = (pthread_t*)malloc(sizeof(pthread_t));
|
||||
*((pthread_t*)m_pThreadObj) = 0;
|
||||
#endif
|
||||
m_bRunning = false;
|
||||
m_bStopped = false;
|
||||
m_bAutoDestroy = false;
|
||||
@@ -196,6 +172,9 @@ Thread::Thread()
|
||||
Thread::~Thread()
|
||||
{
|
||||
debug("Destroying Thread");
|
||||
#ifndef WIN32
|
||||
free(m_pThreadObj);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Thread::Start()
|
||||
@@ -204,30 +183,29 @@ void Thread::Start()
|
||||
|
||||
m_bRunning = true;
|
||||
|
||||
// NOTE: we must garantee, that in a time we setting m_bRunning
|
||||
// NOTE: we must guarantee, that in a time we set 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
|
||||
// back to allow the started thread to complete its job and terminate.
|
||||
// We lock mutex m_pMutexThread 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();
|
||||
m_pMutexThread->Lock();
|
||||
|
||||
#ifdef WIN32
|
||||
m_Thread = (HANDLE)_beginthread(Thread::thread_handler, 0, (void *)this);
|
||||
m_bRunning = m_Thread != NULL;
|
||||
m_pThreadObj = (HANDLE)_beginthread(Thread::thread_handler, 0, (void *)this);
|
||||
m_bRunning = m_pThreadObj != 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);
|
||||
m_bRunning = !pthread_create((pthread_t*)m_pThreadObj, &m_Attr, Thread::thread_handler, (void *) this);
|
||||
pthread_attr_destroy(&m_Attr);
|
||||
#endif
|
||||
|
||||
m_mutexThread.Unlock();
|
||||
m_pMutexThread->Unlock();
|
||||
}
|
||||
|
||||
void Thread::Stop()
|
||||
@@ -241,19 +219,19 @@ bool Thread::Kill()
|
||||
{
|
||||
debug("Killing Thread");
|
||||
|
||||
m_mutexThread.Lock();
|
||||
m_pMutexThread->Lock();
|
||||
|
||||
#ifdef WIN32
|
||||
bool terminated = TerminateThread(m_Thread, 0) != 0;
|
||||
bool terminated = TerminateThread((HANDLE)m_pThreadObj, 0) != 0;
|
||||
#else
|
||||
bool terminated = pthread_cancel(m_Thread) == 0;
|
||||
bool terminated = pthread_cancel(*(pthread_t*)m_pThreadObj) == 0;
|
||||
#endif
|
||||
|
||||
if (terminated)
|
||||
{
|
||||
m_iThreadCount--;
|
||||
}
|
||||
m_mutexThread.Unlock();
|
||||
m_pMutexThread->Unlock();
|
||||
return terminated;
|
||||
}
|
||||
|
||||
@@ -263,9 +241,9 @@ void __cdecl Thread::thread_handler(void* pObject)
|
||||
void* Thread::thread_handler(void* pObject)
|
||||
#endif
|
||||
{
|
||||
m_mutexThread.Lock();
|
||||
m_pMutexThread->Lock();
|
||||
m_iThreadCount++;
|
||||
m_mutexThread.Unlock();
|
||||
m_pMutexThread->Unlock();
|
||||
|
||||
debug("Entering Thread-func");
|
||||
|
||||
@@ -283,9 +261,9 @@ void* Thread::thread_handler(void* pObject)
|
||||
delete pThread;
|
||||
}
|
||||
|
||||
m_mutexThread.Lock();
|
||||
m_pMutexThread->Lock();
|
||||
m_iThreadCount--;
|
||||
m_mutexThread.Unlock();
|
||||
m_pMutexThread->Unlock();
|
||||
|
||||
#ifndef WIN32
|
||||
return NULL;
|
||||
@@ -294,8 +272,8 @@ void* Thread::thread_handler(void* pObject)
|
||||
|
||||
int Thread::GetThreadCount()
|
||||
{
|
||||
m_mutexThread.Lock();
|
||||
m_pMutexThread->Lock();
|
||||
int iThreadCount = m_iThreadCount;
|
||||
m_mutexThread.Unlock();
|
||||
m_pMutexThread->Unlock();
|
||||
return iThreadCount;
|
||||
}
|
||||
|
||||
56
Thread.h
56
Thread.h
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* 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-2010 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
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -27,19 +27,10 @@
|
||||
#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
|
||||
void* m_pMutexObj;
|
||||
|
||||
public:
|
||||
Mutex();
|
||||
@@ -48,38 +39,30 @@ public:
|
||||
void Unlock();
|
||||
};
|
||||
|
||||
|
||||
class Semaphore
|
||||
#ifdef HAVE_SPINLOCK
|
||||
class SpinLock
|
||||
{
|
||||
private:
|
||||
#ifdef WIN32
|
||||
HANDLE m_semObj;
|
||||
void* m_pSpinLockObj;
|
||||
#else
|
||||
sem_t m_semObj;
|
||||
volatile void* m_pSpinLockObj;
|
||||
#endif
|
||||
|
||||
public:
|
||||
Semaphore();
|
||||
Semaphore(int iValue);
|
||||
~Semaphore();
|
||||
void Post();
|
||||
bool Wait();
|
||||
bool TryWait();
|
||||
bool TimedWait(int iMSec);
|
||||
bool IsLocked();
|
||||
SpinLock();
|
||||
~SpinLock();
|
||||
void Lock();
|
||||
void Unlock();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
class Thread
|
||||
{
|
||||
private:
|
||||
static Mutex m_mutexThread;
|
||||
static Mutex* m_pMutexThread;
|
||||
static int m_iThreadCount;
|
||||
#ifdef WIN32
|
||||
HANDLE m_Thread;
|
||||
#else
|
||||
pthread_t m_Thread;
|
||||
#endif
|
||||
void* m_pThreadObj;
|
||||
bool m_bRunning;
|
||||
bool m_bStopped;
|
||||
bool m_bAutoDestroy;
|
||||
@@ -91,8 +74,10 @@ private:
|
||||
#endif
|
||||
|
||||
public:
|
||||
Thread();
|
||||
Thread();
|
||||
virtual ~Thread();
|
||||
static void Init();
|
||||
static void Final();
|
||||
|
||||
virtual void Start();
|
||||
virtual void Stop();
|
||||
@@ -105,9 +90,6 @@ public:
|
||||
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
|
||||
};
|
||||
|
||||
860
Unpack.cpp
Normal file
860
Unpack.cpp
Normal file
@@ -0,0 +1,860 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2013 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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>
|
||||
#include <ctype.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <errno.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Unpack.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include "ParCoordinator.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
extern DownloadQueueHolder* g_pDownloadQueueHolder;
|
||||
|
||||
void UnpackController::FileList::Clear()
|
||||
{
|
||||
for (iterator it = begin(); it != end(); it++)
|
||||
{
|
||||
free(*it);
|
||||
}
|
||||
clear();
|
||||
}
|
||||
|
||||
bool UnpackController::FileList::Exists(const char* szFilename)
|
||||
{
|
||||
for (iterator it = begin(); it != end(); it++)
|
||||
{
|
||||
char* szFilename1 = *it;
|
||||
if (!strcmp(szFilename1, szFilename))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void UnpackController::StartJob(PostInfo* pPostInfo)
|
||||
{
|
||||
UnpackController* pUnpackController = new UnpackController();
|
||||
pUnpackController->m_pPostInfo = pPostInfo;
|
||||
pUnpackController->SetAutoDestroy(false);
|
||||
|
||||
pPostInfo->SetPostThread(pUnpackController);
|
||||
|
||||
pUnpackController->Start();
|
||||
}
|
||||
|
||||
void UnpackController::Run()
|
||||
{
|
||||
// the locking is needed for accessing the members of NZBInfo
|
||||
g_pDownloadQueueHolder->LockQueue();
|
||||
|
||||
strncpy(m_szDestDir, m_pPostInfo->GetNZBInfo()->GetDestDir(), 1024);
|
||||
m_szDestDir[1024-1] = '\0';
|
||||
|
||||
strncpy(m_szName, m_pPostInfo->GetNZBInfo()->GetName(), 1024);
|
||||
m_szName[1024-1] = '\0';
|
||||
|
||||
m_bCleanedUpDisk = false;
|
||||
m_szPassword[0] = '\0';
|
||||
m_szFinalDir[0] = '\0';
|
||||
m_bFinalDirCreated = false;
|
||||
|
||||
NZBParameter* pParameter = m_pPostInfo->GetNZBInfo()->GetParameters()->Find("*Unpack:", false);
|
||||
bool bUnpack = !(pParameter && !strcasecmp(pParameter->GetValue(), "no"));
|
||||
|
||||
pParameter = m_pPostInfo->GetNZBInfo()->GetParameters()->Find("*Unpack:Password", false);
|
||||
if (pParameter)
|
||||
{
|
||||
strncpy(m_szPassword, pParameter->GetValue(), 1024-1);
|
||||
m_szPassword[1024-1] = '\0';
|
||||
}
|
||||
|
||||
g_pDownloadQueueHolder->UnlockQueue();
|
||||
|
||||
snprintf(m_szInfoName, 1024, "unpack for %s", m_szName);
|
||||
m_szInfoName[1024-1] = '\0';
|
||||
|
||||
snprintf(m_szInfoNameUp, 1024, "Unpack for %s", m_szName); // first letter in upper case
|
||||
m_szInfoNameUp[1024-1] = '\0';
|
||||
|
||||
m_bHasParFiles = ParCoordinator::FindMainPars(m_szDestDir, NULL);
|
||||
|
||||
if (bUnpack)
|
||||
{
|
||||
bool bScanNonStdFiles = m_pPostInfo->GetNZBInfo()->GetRenameStatus() > NZBInfo::rsSkipped ||
|
||||
m_pPostInfo->GetNZBInfo()->GetParStatus() == NZBInfo::psSuccess ||
|
||||
!m_bHasParFiles;
|
||||
CheckArchiveFiles(bScanNonStdFiles);
|
||||
}
|
||||
|
||||
if (bUnpack && (m_bHasRarFiles || m_bHasNonStdRarFiles || m_bHasSevenZipFiles || m_bHasSevenZipMultiFiles))
|
||||
{
|
||||
SetInfoName(m_szInfoName);
|
||||
SetWorkingDir(m_szDestDir);
|
||||
|
||||
PrintMessage(Message::mkInfo, "Unpacking %s", m_szName);
|
||||
|
||||
CreateUnpackDir();
|
||||
|
||||
m_bUnpackOK = true;
|
||||
m_bUnpackStartError = false;
|
||||
m_bUnpackSpaceError = false;
|
||||
m_bUnpackPasswordError = false;
|
||||
|
||||
if (m_bHasRarFiles || m_bHasNonStdRarFiles)
|
||||
{
|
||||
ExecuteUnrar();
|
||||
}
|
||||
|
||||
if (m_bHasSevenZipFiles && m_bUnpackOK)
|
||||
{
|
||||
ExecuteSevenZip(false);
|
||||
}
|
||||
|
||||
if (m_bHasSevenZipMultiFiles && m_bUnpackOK)
|
||||
{
|
||||
ExecuteSevenZip(true);
|
||||
}
|
||||
|
||||
Completed();
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintMessage(Message::mkInfo, (bUnpack ? "Nothing to unpack for %s" : "Unpack for %s skipped"), m_szName);
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
if (bUnpack && m_pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped &&
|
||||
m_pPostInfo->GetNZBInfo()->GetRenameStatus() <= NZBInfo::rsSkipped && m_bHasParFiles)
|
||||
{
|
||||
RequestParCheck();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
m_pPostInfo->GetNZBInfo()->SetUnpackStatus(NZBInfo::usSkipped);
|
||||
m_pPostInfo->SetStage(PostInfo::ptQueued);
|
||||
}
|
||||
}
|
||||
|
||||
m_pPostInfo->SetWorking(false);
|
||||
}
|
||||
|
||||
void UnpackController::ExecuteUnrar()
|
||||
{
|
||||
// Format:
|
||||
// unrar x -y -p- -o+ *.rar ./_unpack
|
||||
|
||||
char szPasswordParam[1024];
|
||||
const char* szArgs[8];
|
||||
szArgs[0] = g_pOptions->GetUnrarCmd();
|
||||
szArgs[1] = "x";
|
||||
szArgs[2] = "-y";
|
||||
szArgs[3] = "-p-";
|
||||
if (strlen(m_szPassword) > 0)
|
||||
{
|
||||
snprintf(szPasswordParam, 1024, "-p%s", m_szPassword);
|
||||
szArgs[3] = szPasswordParam;
|
||||
}
|
||||
szArgs[4] = "-o+";
|
||||
szArgs[5] = m_bHasNonStdRarFiles ? "*.*" : "*.rar";
|
||||
szArgs[6] = m_szUnpackDir;
|
||||
szArgs[7] = NULL;
|
||||
SetArgs(szArgs, false);
|
||||
|
||||
SetScript(g_pOptions->GetUnrarCmd());
|
||||
SetLogPrefix("Unrar");
|
||||
|
||||
m_bAllOKMessageReceived = false;
|
||||
m_eUnpacker = upUnrar;
|
||||
|
||||
SetProgressLabel("");
|
||||
int iExitCode = Execute();
|
||||
SetLogPrefix(NULL);
|
||||
SetProgressLabel("");
|
||||
|
||||
m_bUnpackOK = iExitCode == 0 && m_bAllOKMessageReceived && !GetTerminated();
|
||||
m_bUnpackStartError = iExitCode == -1;
|
||||
m_bUnpackSpaceError = iExitCode == 5;
|
||||
m_bUnpackPasswordError = iExitCode == 11; // only for rar5-archives
|
||||
|
||||
if (!m_bUnpackOK && iExitCode > 0)
|
||||
{
|
||||
PrintMessage(Message::mkError, "Unrar error code: %i", iExitCode);
|
||||
}
|
||||
}
|
||||
|
||||
void UnpackController::ExecuteSevenZip(bool bMultiVolumes)
|
||||
{
|
||||
// Format:
|
||||
// 7z x -y -p- -o./_unpack *.7z
|
||||
// OR
|
||||
// 7z x -y -p- -o./_unpack *.7z.001
|
||||
|
||||
char szPasswordParam[1024];
|
||||
const char* szArgs[7];
|
||||
szArgs[0] = g_pOptions->GetSevenZipCmd();
|
||||
szArgs[1] = "x";
|
||||
szArgs[2] = "-y";
|
||||
|
||||
szArgs[3] = "-p-";
|
||||
if (strlen(m_szPassword) > 0)
|
||||
{
|
||||
snprintf(szPasswordParam, 1024, "-p%s", m_szPassword);
|
||||
szArgs[3] = szPasswordParam;
|
||||
}
|
||||
|
||||
char szUnpackDirParam[1024];
|
||||
snprintf(szUnpackDirParam, 1024, "-o%s", m_szUnpackDir);
|
||||
szArgs[4] = szUnpackDirParam;
|
||||
|
||||
szArgs[5] = bMultiVolumes ? "*.7z.001" : "*.7z";
|
||||
szArgs[6] = NULL;
|
||||
SetArgs(szArgs, false);
|
||||
|
||||
SetScript(g_pOptions->GetSevenZipCmd());
|
||||
|
||||
m_bAllOKMessageReceived = false;
|
||||
m_eUnpacker = upSevenZip;
|
||||
|
||||
PrintMessage(Message::mkInfo, "Executing 7-Zip");
|
||||
SetLogPrefix("7-Zip");
|
||||
SetProgressLabel("");
|
||||
int iExitCode = Execute();
|
||||
SetLogPrefix(NULL);
|
||||
SetProgressLabel("");
|
||||
|
||||
m_bUnpackOK = iExitCode == 0 && m_bAllOKMessageReceived && !GetTerminated();
|
||||
m_bUnpackStartError = iExitCode == -1;
|
||||
|
||||
if (!m_bUnpackOK && iExitCode > 0)
|
||||
{
|
||||
PrintMessage(Message::mkError, "7-Zip error code: %i", iExitCode);
|
||||
}
|
||||
}
|
||||
|
||||
void UnpackController::Completed()
|
||||
{
|
||||
bool bCleanupSuccess = Cleanup();
|
||||
|
||||
if (m_bUnpackOK && bCleanupSuccess)
|
||||
{
|
||||
PrintMessage(Message::mkInfo, "%s %s", m_szInfoNameUp, "successful");
|
||||
m_pPostInfo->GetNZBInfo()->SetUnpackStatus(NZBInfo::usSuccess);
|
||||
m_pPostInfo->GetNZBInfo()->SetUnpackCleanedUpDisk(m_bCleanedUpDisk);
|
||||
if (g_pOptions->GetParRename())
|
||||
{
|
||||
//request par-rename check for extracted files
|
||||
m_pPostInfo->GetNZBInfo()->SetRenameStatus(NZBInfo::rsNone);
|
||||
}
|
||||
m_pPostInfo->SetStage(PostInfo::ptQueued);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifndef DISABLE_PARCHECK
|
||||
if (!m_bUnpackOK && m_pPostInfo->GetNZBInfo()->GetParStatus() <= NZBInfo::psSkipped &&
|
||||
!m_bUnpackStartError && !m_bUnpackSpaceError && !m_bUnpackPasswordError &&
|
||||
!GetTerminated() && m_bHasParFiles)
|
||||
{
|
||||
RequestParCheck();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
PrintMessage(Message::mkError, "%s failed", m_szInfoNameUp);
|
||||
m_pPostInfo->GetNZBInfo()->SetUnpackStatus(
|
||||
m_bUnpackSpaceError ? NZBInfo::usSpace :
|
||||
m_bUnpackPasswordError ? NZBInfo::usPassword :
|
||||
NZBInfo::usFailure);
|
||||
m_pPostInfo->SetStage(PostInfo::ptQueued);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
void UnpackController::RequestParCheck()
|
||||
{
|
||||
PrintMessage(Message::mkInfo, "%s requested par-check/repair", m_szInfoNameUp);
|
||||
m_pPostInfo->SetRequestParCheck(true);
|
||||
m_pPostInfo->SetStage(PostInfo::ptFinished);
|
||||
}
|
||||
#endif
|
||||
|
||||
void UnpackController::CreateUnpackDir()
|
||||
{
|
||||
m_bInterDir = strlen(g_pOptions->GetInterDir()) > 0 &&
|
||||
!strncmp(m_szDestDir, g_pOptions->GetInterDir(), strlen(g_pOptions->GetInterDir()));
|
||||
if (m_bInterDir)
|
||||
{
|
||||
m_pPostInfo->GetNZBInfo()->BuildFinalDirName(m_szFinalDir, 1024);
|
||||
m_szFinalDir[1024-1] = '\0';
|
||||
snprintf(m_szUnpackDir, 1024, "%s%c%s", m_szFinalDir, PATH_SEPARATOR, "_unpack");
|
||||
m_bFinalDirCreated = !Util::DirectoryExists(m_szFinalDir);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(m_szUnpackDir, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, "_unpack");
|
||||
}
|
||||
m_szUnpackDir[1024-1] = '\0';
|
||||
|
||||
char szErrBuf[1024];
|
||||
if (!Util::ForceDirectories(m_szUnpackDir, szErrBuf, sizeof(szErrBuf)))
|
||||
{
|
||||
error("Could not create directory %s: %s", m_szUnpackDir, szErrBuf);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void UnpackController::CheckArchiveFiles(bool bScanNonStdFiles)
|
||||
{
|
||||
m_bHasRarFiles = false;
|
||||
m_bHasNonStdRarFiles = false;
|
||||
m_bHasSevenZipFiles = false;
|
||||
m_bHasSevenZipMultiFiles = false;
|
||||
|
||||
RegEx regExRar(".*\\.rar$");
|
||||
RegEx regExRarMultiSeq(".*\\.(r|s)[0-9][0-9]$");
|
||||
RegEx regExSevenZip(".*\\.7z$");
|
||||
RegEx regExSevenZipMulti(".*\\.7z\\.[0-9]+$");
|
||||
RegEx regExNumExt(".*\\.[0-9]+$");
|
||||
|
||||
DirBrowser dir(m_szDestDir);
|
||||
while (const char* filename = dir.Next())
|
||||
{
|
||||
char szFullFilename[1024];
|
||||
snprintf(szFullFilename, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, filename);
|
||||
szFullFilename[1024-1] = '\0';
|
||||
|
||||
if (strcmp(filename, ".") && strcmp(filename, "..") && !Util::DirectoryExists(szFullFilename))
|
||||
{
|
||||
if (regExRar.Match(filename))
|
||||
{
|
||||
m_bHasRarFiles = true;
|
||||
}
|
||||
else if (regExSevenZip.Match(filename))
|
||||
{
|
||||
m_bHasSevenZipFiles = true;
|
||||
}
|
||||
else if (regExSevenZipMulti.Match(filename))
|
||||
{
|
||||
m_bHasSevenZipMultiFiles = true;
|
||||
}
|
||||
else if (bScanNonStdFiles && !m_bHasNonStdRarFiles &&
|
||||
!regExRarMultiSeq.Match(filename) && regExNumExt.Match(filename) &&
|
||||
FileHasRarSignature(szFullFilename))
|
||||
{
|
||||
m_bHasNonStdRarFiles = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool UnpackController::FileHasRarSignature(const char* szFilename)
|
||||
{
|
||||
char rar4Signature[] = { 0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x00 };
|
||||
char rar5Signature[] = { 0x52, 0x61, 0x72, 0x21, 0x1A, 0x07, 0x01, 0x00 };
|
||||
|
||||
char fileSignature[8];
|
||||
|
||||
int cnt = 0;
|
||||
FILE* infile;
|
||||
infile = fopen(szFilename, "rb");
|
||||
if (infile)
|
||||
{
|
||||
cnt = (int)fread(fileSignature, 1, sizeof(fileSignature), infile);
|
||||
fclose(infile);
|
||||
}
|
||||
|
||||
bool bRar = cnt == sizeof(fileSignature) &&
|
||||
(!strcmp(rar4Signature, fileSignature) || !strcmp(rar5Signature, fileSignature));
|
||||
return bRar;
|
||||
}
|
||||
|
||||
bool UnpackController::Cleanup()
|
||||
{
|
||||
// By success:
|
||||
// - move unpacked files to destination dir;
|
||||
// - remove _unpack-dir;
|
||||
// - delete archive-files.
|
||||
// By failure:
|
||||
// - remove _unpack-dir.
|
||||
|
||||
bool bOK = true;
|
||||
|
||||
FileList extractedFiles;
|
||||
|
||||
if (m_bUnpackOK)
|
||||
{
|
||||
// moving files back
|
||||
DirBrowser dir(m_szUnpackDir);
|
||||
while (const char* filename = dir.Next())
|
||||
{
|
||||
if (strcmp(filename, ".") && strcmp(filename, "..") &&
|
||||
strcmp(filename, ".AppleDouble") && strcmp(filename, ".DS_Store"))
|
||||
{
|
||||
char szSrcFile[1024];
|
||||
snprintf(szSrcFile, 1024, "%s%c%s", m_szUnpackDir, PATH_SEPARATOR, filename);
|
||||
szSrcFile[1024-1] = '\0';
|
||||
|
||||
char szDstFile[1024];
|
||||
snprintf(szDstFile, 1024, "%s%c%s", m_szFinalDir[0] != '\0' ? m_szFinalDir : m_szDestDir, PATH_SEPARATOR, filename);
|
||||
szDstFile[1024-1] = '\0';
|
||||
|
||||
// silently overwrite existing files
|
||||
remove(szDstFile);
|
||||
|
||||
if (!Util::MoveFile(szSrcFile, szDstFile))
|
||||
{
|
||||
PrintMessage(Message::mkError, "Could not move file %s to %s", szSrcFile, szDstFile);
|
||||
bOK = false;
|
||||
}
|
||||
|
||||
extractedFiles.push_back(strdup(filename));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bOK && !Util::DeleteDirectoryWithContent(m_szUnpackDir))
|
||||
{
|
||||
PrintMessage(Message::mkError, "Could not remove temporary directory %s", m_szUnpackDir);
|
||||
}
|
||||
|
||||
if (!m_bUnpackOK && m_bFinalDirCreated)
|
||||
{
|
||||
Util::RemoveDirectory(m_szFinalDir);
|
||||
}
|
||||
|
||||
if (m_bUnpackOK && bOK && g_pOptions->GetUnpackCleanupDisk())
|
||||
{
|
||||
PrintMessage(Message::mkInfo, "Deleting archive files");
|
||||
|
||||
RegEx regExRar(".*\\.rar$");
|
||||
RegEx regExRarMultiSeq(".*\\.[r-z][0-9][0-9]$");
|
||||
RegEx regExSevenZip(".*\\.7z$|.*\\.7z\\.[0-9]+$");
|
||||
RegEx regExNumExt(".*\\.[0-9]+$");
|
||||
|
||||
DirBrowser dir(m_szDestDir);
|
||||
while (const char* filename = dir.Next())
|
||||
{
|
||||
char szFullFilename[1024];
|
||||
snprintf(szFullFilename, 1024, "%s%c%s", m_szDestDir, PATH_SEPARATOR, filename);
|
||||
szFullFilename[1024-1] = '\0';
|
||||
|
||||
if (strcmp(filename, ".") && strcmp(filename, "..") &&
|
||||
!Util::DirectoryExists(szFullFilename) &&
|
||||
(m_bInterDir || !extractedFiles.Exists(filename)) &&
|
||||
(regExRar.Match(filename) || regExSevenZip.Match(filename) ||
|
||||
(regExRarMultiSeq.Match(filename) && FileHasRarSignature(szFullFilename)) ||
|
||||
(m_bHasNonStdRarFiles && regExNumExt.Match(filename) && FileHasRarSignature(szFullFilename))))
|
||||
{
|
||||
PrintMessage(Message::mkInfo, "Deleting file %s", filename);
|
||||
|
||||
if (remove(szFullFilename) != 0)
|
||||
{
|
||||
PrintMessage(Message::mkError, "Could not delete file %s", szFullFilename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_bCleanedUpDisk = true;
|
||||
}
|
||||
|
||||
extractedFiles.Clear();
|
||||
|
||||
return bOK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unrar prints progress information into the same line using backspace control character.
|
||||
* In order to print progress continuously we analyze the output after every char
|
||||
* and update post-job progress information.
|
||||
*/
|
||||
bool UnpackController::ReadLine(char* szBuf, int iBufSize, FILE* pStream)
|
||||
{
|
||||
bool bPrinted = false;
|
||||
|
||||
int i = 0;
|
||||
|
||||
for (; i < iBufSize - 1; i++)
|
||||
{
|
||||
int ch = fgetc(pStream);
|
||||
szBuf[i] = ch;
|
||||
szBuf[i+1] = '\0';
|
||||
if (ch == EOF)
|
||||
{
|
||||
break;
|
||||
}
|
||||
if (ch == '\n')
|
||||
{
|
||||
i++;
|
||||
break;
|
||||
}
|
||||
|
||||
char* szBackspace = strrchr(szBuf, '\b');
|
||||
if (szBackspace)
|
||||
{
|
||||
if (!bPrinted)
|
||||
{
|
||||
char tmp[1024];
|
||||
strncpy(tmp, szBuf, 1024);
|
||||
tmp[1024-1] = '\0';
|
||||
char* szTmpPercent = strrchr(tmp, '\b');
|
||||
if (szTmpPercent)
|
||||
{
|
||||
*szTmpPercent = '\0';
|
||||
}
|
||||
if (strncmp(szBuf, "...", 3))
|
||||
{
|
||||
ProcessOutput(tmp);
|
||||
}
|
||||
bPrinted = true;
|
||||
}
|
||||
if (strchr(szBackspace, '%'))
|
||||
{
|
||||
int iPercent = atoi(szBackspace + 1);
|
||||
m_pPostInfo->SetStageProgress(iPercent * 10);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
szBuf[i] = '\0';
|
||||
|
||||
if (bPrinted)
|
||||
{
|
||||
szBuf[0] = '\0';
|
||||
}
|
||||
|
||||
return i > 0;
|
||||
}
|
||||
|
||||
void UnpackController::AddMessage(Message::EKind eKind, const char* szText)
|
||||
{
|
||||
char szMsgText[1024];
|
||||
strncpy(szMsgText, szText, 1024);
|
||||
szMsgText[1024-1] = '\0';
|
||||
|
||||
// Modify unrar messages for better readability:
|
||||
// remove the destination path part from message "Extracting file.xxx"
|
||||
if (m_eUnpacker == upUnrar && !strncmp(szText, "Unrar: Extracting ", 19) &&
|
||||
!strncmp(szText + 19, m_szUnpackDir, strlen(m_szUnpackDir)))
|
||||
{
|
||||
snprintf(szMsgText, 1024, "Unrar: Extracting %s", szText + 19 + strlen(m_szUnpackDir) + 1);
|
||||
szMsgText[1024-1] = '\0';
|
||||
}
|
||||
|
||||
ScriptController::AddMessage(eKind, szMsgText);
|
||||
m_pPostInfo->AppendMessage(eKind, szMsgText);
|
||||
|
||||
if (m_eUnpacker == upUnrar && !strncmp(szMsgText, "Unrar: UNRAR ", 6) &&
|
||||
strstr(szMsgText, " Copyright ") && strstr(szMsgText, " Alexander Roshal"))
|
||||
{
|
||||
// reset start time for a case if user uses unpack-script to do some things
|
||||
// (like sending Wake-On-Lan message) before executing unrar
|
||||
m_pPostInfo->SetStageTime(time(NULL));
|
||||
}
|
||||
|
||||
if (m_eUnpacker == upUnrar && !strncmp(szMsgText, "Unrar: Extracting ", 18))
|
||||
{
|
||||
SetProgressLabel(szMsgText + 7);
|
||||
}
|
||||
|
||||
if (m_eUnpacker == upUnrar && !strncmp(szText, "Unrar: Extracting from ", 23))
|
||||
{
|
||||
const char *szFilename = szText + 23;
|
||||
debug("Filename: %s", szFilename);
|
||||
SetProgressLabel(szText + 7);
|
||||
}
|
||||
|
||||
if ((m_eUnpacker == upUnrar && !strncmp(szText, "Unrar: All OK", 13)) ||
|
||||
(m_eUnpacker == upSevenZip && !strncmp(szText, "7-Zip: Everything is Ok", 23)))
|
||||
{
|
||||
m_bAllOKMessageReceived = true;
|
||||
}
|
||||
}
|
||||
|
||||
void UnpackController::Stop()
|
||||
{
|
||||
debug("Stopping unpack");
|
||||
Thread::Stop();
|
||||
Terminate();
|
||||
}
|
||||
|
||||
void UnpackController::SetProgressLabel(const char* szProgressLabel)
|
||||
{
|
||||
g_pDownloadQueueHolder->LockQueue();
|
||||
m_pPostInfo->SetProgressLabel(szProgressLabel);
|
||||
g_pDownloadQueueHolder->UnlockQueue();
|
||||
}
|
||||
|
||||
|
||||
void MoveController::StartJob(PostInfo* pPostInfo)
|
||||
{
|
||||
MoveController* pMoveController = new MoveController();
|
||||
pMoveController->m_pPostInfo = pPostInfo;
|
||||
pMoveController->SetAutoDestroy(false);
|
||||
|
||||
pPostInfo->SetPostThread(pMoveController);
|
||||
|
||||
pMoveController->Start();
|
||||
}
|
||||
|
||||
void MoveController::Run()
|
||||
{
|
||||
// the locking is needed for accessing the members of NZBInfo
|
||||
g_pDownloadQueueHolder->LockQueue();
|
||||
|
||||
char szNZBName[1024];
|
||||
strncpy(szNZBName, m_pPostInfo->GetNZBInfo()->GetName(), 1024);
|
||||
szNZBName[1024-1] = '\0';
|
||||
|
||||
char szInfoName[1024];
|
||||
snprintf(szInfoName, 1024, "move for %s", m_pPostInfo->GetNZBInfo()->GetName());
|
||||
szInfoName[1024-1] = '\0';
|
||||
SetInfoName(szInfoName);
|
||||
|
||||
strncpy(m_szInterDir, m_pPostInfo->GetNZBInfo()->GetDestDir(), 1024);
|
||||
m_szInterDir[1024-1] = '\0';
|
||||
|
||||
m_pPostInfo->GetNZBInfo()->BuildFinalDirName(m_szDestDir, 1024);
|
||||
m_szDestDir[1024-1] = '\0';
|
||||
|
||||
g_pDownloadQueueHolder->UnlockQueue();
|
||||
|
||||
info("Moving completed files for %s", szNZBName);
|
||||
|
||||
bool bOK = MoveFiles();
|
||||
|
||||
szInfoName[0] = 'M'; // uppercase
|
||||
|
||||
if (bOK)
|
||||
{
|
||||
info("%s successful", szInfoName);
|
||||
// save new dest dir
|
||||
g_pDownloadQueueHolder->LockQueue();
|
||||
m_pPostInfo->GetNZBInfo()->SetDestDir(m_szDestDir);
|
||||
m_pPostInfo->GetNZBInfo()->SetMoveStatus(NZBInfo::msSuccess);
|
||||
g_pDownloadQueueHolder->UnlockQueue();
|
||||
}
|
||||
else
|
||||
{
|
||||
error("%s failed", szInfoName);
|
||||
m_pPostInfo->GetNZBInfo()->SetMoveStatus(NZBInfo::msFailure);
|
||||
}
|
||||
|
||||
m_pPostInfo->SetStage(PostInfo::ptQueued);
|
||||
m_pPostInfo->SetWorking(false);
|
||||
}
|
||||
|
||||
bool MoveController::MoveFiles()
|
||||
{
|
||||
char szErrBuf[1024];
|
||||
if (!Util::ForceDirectories(m_szDestDir, szErrBuf, sizeof(szErrBuf)))
|
||||
{
|
||||
error("Could not create directory %s: %s", m_szDestDir, szErrBuf);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bOK = true;
|
||||
DirBrowser dir(m_szInterDir);
|
||||
while (const char* filename = dir.Next())
|
||||
{
|
||||
if (strcmp(filename, ".") && strcmp(filename, "..") &&
|
||||
strcmp(filename, ".AppleDouble") && strcmp(filename, ".DS_Store"))
|
||||
{
|
||||
char szSrcFile[1024];
|
||||
snprintf(szSrcFile, 1024, "%s%c%s", m_szInterDir, PATH_SEPARATOR, filename);
|
||||
szSrcFile[1024-1] = '\0';
|
||||
|
||||
char szDstFile[1024];
|
||||
Util::MakeUniqueFilename(szDstFile, 1024, m_szDestDir, filename);
|
||||
|
||||
PrintMessage(Message::mkInfo, "Moving file %s to %s", Util::BaseFileName(szSrcFile), m_szDestDir);
|
||||
if (!Util::MoveFile(szSrcFile, szDstFile))
|
||||
{
|
||||
PrintMessage(Message::mkError, "Could not move file %s to %s! Errcode: %i", szSrcFile, szDstFile, errno);
|
||||
bOK = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bOK && !Util::DeleteDirectoryWithContent(m_szInterDir))
|
||||
{
|
||||
PrintMessage(Message::mkError, "Could not remove intermediate directory %s", m_szInterDir);
|
||||
}
|
||||
|
||||
return bOK;
|
||||
}
|
||||
|
||||
|
||||
void CleanupController::StartJob(PostInfo* pPostInfo)
|
||||
{
|
||||
CleanupController* pCleanupController = new CleanupController();
|
||||
pCleanupController->m_pPostInfo = pPostInfo;
|
||||
pCleanupController->SetAutoDestroy(false);
|
||||
|
||||
pPostInfo->SetPostThread(pCleanupController);
|
||||
|
||||
pCleanupController->Start();
|
||||
}
|
||||
|
||||
void CleanupController::Run()
|
||||
{
|
||||
// the locking is needed for accessing the members of NZBInfo
|
||||
g_pDownloadQueueHolder->LockQueue();
|
||||
|
||||
char szNZBName[1024];
|
||||
strncpy(szNZBName, m_pPostInfo->GetNZBInfo()->GetName(), 1024);
|
||||
szNZBName[1024-1] = '\0';
|
||||
|
||||
char szInfoName[1024];
|
||||
snprintf(szInfoName, 1024, "cleanup for %s", m_pPostInfo->GetNZBInfo()->GetName());
|
||||
szInfoName[1024-1] = '\0';
|
||||
SetInfoName(szInfoName);
|
||||
|
||||
strncpy(m_szDestDir, m_pPostInfo->GetNZBInfo()->GetDestDir(), 1024);
|
||||
m_szDestDir[1024-1] = '\0';
|
||||
|
||||
bool bInterDir = strlen(g_pOptions->GetInterDir()) > 0 &&
|
||||
!strncmp(m_szDestDir, g_pOptions->GetInterDir(), strlen(g_pOptions->GetInterDir()));
|
||||
if (bInterDir)
|
||||
{
|
||||
m_pPostInfo->GetNZBInfo()->BuildFinalDirName(m_szFinalDir, 1024);
|
||||
m_szFinalDir[1024-1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
m_szFinalDir[0] = '\0';
|
||||
}
|
||||
|
||||
g_pDownloadQueueHolder->UnlockQueue();
|
||||
|
||||
info("Cleaning up %s", szNZBName);
|
||||
|
||||
bool bDeleted = false;
|
||||
bool bOK = Cleanup(m_szDestDir, &bDeleted);
|
||||
|
||||
if (bOK && m_szFinalDir[0] != '\0')
|
||||
{
|
||||
bool bDeleted2 = false;
|
||||
bOK = Cleanup(m_szFinalDir, &bDeleted2);
|
||||
bDeleted = bDeleted || bDeleted2;
|
||||
}
|
||||
|
||||
szInfoName[0] = 'C'; // uppercase
|
||||
|
||||
if (bOK && bDeleted)
|
||||
{
|
||||
info("%s successful", szInfoName);
|
||||
m_pPostInfo->GetNZBInfo()->SetCleanupStatus(NZBInfo::csSuccess);
|
||||
}
|
||||
else if (bOK)
|
||||
{
|
||||
info("Nothing to cleanup for %s", szNZBName);
|
||||
m_pPostInfo->GetNZBInfo()->SetCleanupStatus(NZBInfo::csSuccess);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("%s failed", szInfoName);
|
||||
m_pPostInfo->GetNZBInfo()->SetCleanupStatus(NZBInfo::csFailure);
|
||||
}
|
||||
|
||||
m_pPostInfo->SetStage(PostInfo::ptQueued);
|
||||
m_pPostInfo->SetWorking(false);
|
||||
}
|
||||
|
||||
bool CleanupController::Cleanup(const char* szDestDir, bool *bDeleted)
|
||||
{
|
||||
*bDeleted = false;
|
||||
bool bOK = true;
|
||||
|
||||
ExtList extList;
|
||||
|
||||
// split ExtCleanupDisk into tokens and create a list
|
||||
char* szExtCleanupDisk = strdup(g_pOptions->GetExtCleanupDisk());
|
||||
|
||||
char* saveptr;
|
||||
char* szExt = strtok_r(szExtCleanupDisk, ",; ", &saveptr);
|
||||
while (szExt)
|
||||
{
|
||||
extList.push_back(szExt);
|
||||
szExt = strtok_r(NULL, ",; ", &saveptr);
|
||||
}
|
||||
|
||||
DirBrowser dir(szDestDir);
|
||||
while (const char* filename = dir.Next())
|
||||
{
|
||||
// check file extension
|
||||
|
||||
int iFilenameLen = strlen(filename);
|
||||
bool bDeleteIt = false;
|
||||
for (ExtList::iterator it = extList.begin(); it != extList.end(); it++)
|
||||
{
|
||||
const char* szExt = *it;
|
||||
int iExtLen = strlen(szExt);
|
||||
if (iFilenameLen >= iExtLen && !strcasecmp(szExt, filename + iFilenameLen - iExtLen))
|
||||
{
|
||||
bDeleteIt = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bDeleteIt)
|
||||
{
|
||||
char szFullFilename[1024];
|
||||
snprintf(szFullFilename, 1024, "%s%c%s", szDestDir, PATH_SEPARATOR, filename);
|
||||
szFullFilename[1024-1] = '\0';
|
||||
|
||||
PrintMessage(Message::mkInfo, "Deleting file %s", filename);
|
||||
if (remove(szFullFilename) != 0)
|
||||
{
|
||||
PrintMessage(Message::mkError, "Could not delete file %s! Errcode: %i", szFullFilename, errno);
|
||||
bOK = false;
|
||||
}
|
||||
|
||||
*bDeleted = true;
|
||||
}
|
||||
}
|
||||
|
||||
free(szExtCleanupDisk);
|
||||
|
||||
return bOK;
|
||||
}
|
||||
129
Unpack.h
Normal file
129
Unpack.h
Normal file
@@ -0,0 +1,129 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2013 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef UNPACK_H
|
||||
#define UNPACK_H
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include "Log.h"
|
||||
#include "Thread.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "ScriptController.h"
|
||||
|
||||
class UnpackController : public Thread, public ScriptController
|
||||
{
|
||||
private:
|
||||
enum EUnpacker
|
||||
{
|
||||
upUnrar,
|
||||
upSevenZip
|
||||
};
|
||||
|
||||
typedef std::deque<char*> FileListBase;
|
||||
class FileList : public FileListBase
|
||||
{
|
||||
public:
|
||||
void Clear();
|
||||
bool Exists(const char* szFilename);
|
||||
};
|
||||
|
||||
private:
|
||||
PostInfo* m_pPostInfo;
|
||||
char m_szName[1024];
|
||||
char m_szInfoName[1024];
|
||||
char m_szInfoNameUp[1024];
|
||||
char m_szDestDir[1024];
|
||||
char m_szFinalDir[1024];
|
||||
char m_szUnpackDir[1024];
|
||||
char m_szPassword[1024];
|
||||
bool m_bInterDir;
|
||||
bool m_bAllOKMessageReceived;
|
||||
bool m_bNoFilesMessageReceived;
|
||||
bool m_bHasParFiles;
|
||||
bool m_bHasRarFiles;
|
||||
bool m_bHasNonStdRarFiles;
|
||||
bool m_bHasSevenZipFiles;
|
||||
bool m_bHasSevenZipMultiFiles;
|
||||
bool m_bUnpackOK;
|
||||
bool m_bUnpackStartError;
|
||||
bool m_bUnpackSpaceError;
|
||||
bool m_bUnpackPasswordError;
|
||||
bool m_bCleanedUpDisk;
|
||||
EUnpacker m_eUnpacker;
|
||||
bool m_bFinalDirCreated;
|
||||
|
||||
protected:
|
||||
virtual bool ReadLine(char* szBuf, int iBufSize, FILE* pStream);
|
||||
virtual void AddMessage(Message::EKind eKind, const char* szText);
|
||||
void ExecuteUnrar();
|
||||
void ExecuteSevenZip(bool bMultiVolumes);
|
||||
void Completed();
|
||||
void CreateUnpackDir();
|
||||
bool Cleanup();
|
||||
void CheckArchiveFiles(bool bScanNonStdFiles);
|
||||
void SetProgressLabel(const char* szProgressLabel);
|
||||
#ifndef DISABLE_PARCHECK
|
||||
void RequestParCheck();
|
||||
#endif
|
||||
bool FileHasRarSignature(const char* szFilename);
|
||||
|
||||
public:
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
static void StartJob(PostInfo* pPostInfo);
|
||||
};
|
||||
|
||||
class MoveController : public Thread, public ScriptController
|
||||
{
|
||||
private:
|
||||
PostInfo* m_pPostInfo;
|
||||
char m_szInterDir[1024];
|
||||
char m_szDestDir[1024];
|
||||
|
||||
bool MoveFiles();
|
||||
|
||||
public:
|
||||
virtual void Run();
|
||||
static void StartJob(PostInfo* pPostInfo);
|
||||
};
|
||||
|
||||
class CleanupController : public Thread, public ScriptController
|
||||
{
|
||||
private:
|
||||
PostInfo* m_pPostInfo;
|
||||
char m_szDestDir[1024];
|
||||
char m_szFinalDir[1024];
|
||||
|
||||
bool Cleanup(const char* szDestDir, bool *bDeleted);
|
||||
|
||||
typedef std::deque<char*> ExtList;
|
||||
|
||||
public:
|
||||
virtual void Run();
|
||||
static void StartJob(PostInfo* pPostInfo);
|
||||
};
|
||||
|
||||
#endif
|
||||
454
UrlCoordinator.cpp
Normal file
454
UrlCoordinator.cpp
Normal file
@@ -0,0 +1,454 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2012-2013 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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>
|
||||
#include <sys/stat.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "UrlCoordinator.h"
|
||||
#include "Options.h"
|
||||
#include "WebDownloader.h"
|
||||
#include "DiskState.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include "NZBFile.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "Scanner.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
extern DiskState* g_pDiskState;
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
extern Scanner* g_pScanner;
|
||||
|
||||
|
||||
UrlDownloader::UrlDownloader() : WebDownloader()
|
||||
{
|
||||
m_szCategory = NULL;
|
||||
}
|
||||
|
||||
UrlDownloader::~UrlDownloader()
|
||||
{
|
||||
free(m_szCategory);
|
||||
}
|
||||
|
||||
void UrlDownloader::ProcessHeader(const char* szLine)
|
||||
{
|
||||
WebDownloader::ProcessHeader(szLine);
|
||||
|
||||
if (!strncmp(szLine, "X-DNZB-Category:", 16))
|
||||
{
|
||||
free(m_szCategory);
|
||||
char* szCategory = strdup(szLine + 16);
|
||||
m_szCategory = strdup(Util::Trim(szCategory));
|
||||
free(szCategory);
|
||||
|
||||
debug("Category: %s", m_szCategory);
|
||||
}
|
||||
else if (!strncmp(szLine, "X-DNZB-", 7))
|
||||
{
|
||||
char* szModLine = strdup(szLine);
|
||||
char* szValue = strchr(szModLine, ':');
|
||||
if (szValue)
|
||||
{
|
||||
*szValue = NULL;
|
||||
szValue++;
|
||||
while (*szValue == ' ') szValue++;
|
||||
Util::Trim(szValue);
|
||||
|
||||
debug("X-DNZB: %s", szModLine);
|
||||
debug("Value: %s", szValue);
|
||||
|
||||
char szParamName[100];
|
||||
snprintf(szParamName, 100, "*DNZB:%s", szModLine + 7);
|
||||
szParamName[100-1] = '\0';
|
||||
|
||||
char* szVal = WebUtil::Latin1ToUtf8(szValue);
|
||||
m_ppParameters.SetParameter(szParamName, szVal);
|
||||
free(szVal);
|
||||
}
|
||||
free(szModLine);
|
||||
}
|
||||
}
|
||||
|
||||
UrlCoordinator::UrlCoordinator()
|
||||
{
|
||||
debug("Creating UrlCoordinator");
|
||||
|
||||
m_bHasMoreJobs = true;
|
||||
m_bForce = false;
|
||||
}
|
||||
|
||||
UrlCoordinator::~UrlCoordinator()
|
||||
{
|
||||
debug("Destroying UrlCoordinator");
|
||||
// Cleanup
|
||||
|
||||
debug("Deleting UrlDownloaders");
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_ActiveDownloads.clear();
|
||||
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
for (UrlQueue::iterator it = pDownloadQueue->GetUrlQueue()->begin(); it != pDownloadQueue->GetUrlQueue()->end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
pDownloadQueue->GetUrlQueue()->clear();
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
|
||||
debug("UrlCoordinator destroyed");
|
||||
}
|
||||
|
||||
void UrlCoordinator::Run()
|
||||
{
|
||||
debug("Entering UrlCoordinator-loop");
|
||||
|
||||
int iResetCounter = 0;
|
||||
|
||||
while (!IsStopped())
|
||||
{
|
||||
if (!(g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2()) || m_bForce || g_pOptions->GetUrlForce())
|
||||
{
|
||||
// start download for next URL
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
|
||||
if ((int)m_ActiveDownloads.size() < g_pOptions->GetUrlConnections())
|
||||
{
|
||||
UrlInfo* pUrlInfo;
|
||||
bool bHasMoreUrls = GetNextUrl(pDownloadQueue, pUrlInfo);
|
||||
bool bUrlDownloadsRunning = !m_ActiveDownloads.empty();
|
||||
m_bHasMoreJobs = bHasMoreUrls || bUrlDownloadsRunning;
|
||||
if (bHasMoreUrls && !IsStopped())
|
||||
{
|
||||
StartUrlDownload(pUrlInfo);
|
||||
}
|
||||
if (!bHasMoreUrls)
|
||||
{
|
||||
m_bForce = false;
|
||||
}
|
||||
}
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
}
|
||||
|
||||
int iSleepInterval = 100;
|
||||
usleep(iSleepInterval * 1000);
|
||||
|
||||
iResetCounter += iSleepInterval;
|
||||
if (iResetCounter >= 1000)
|
||||
{
|
||||
// this code should not be called too often, once per second is OK
|
||||
ResetHangingDownloads();
|
||||
iResetCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// waiting for downloads
|
||||
debug("UrlCoordinator: waiting for Downloads to complete");
|
||||
bool completed = false;
|
||||
while (!completed)
|
||||
{
|
||||
g_pQueueCoordinator->LockQueue();
|
||||
completed = m_ActiveDownloads.size() == 0;
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
usleep(100 * 1000);
|
||||
ResetHangingDownloads();
|
||||
}
|
||||
debug("UrlCoordinator: Downloads are completed");
|
||||
|
||||
debug("Exiting UrlCoordinator-loop");
|
||||
}
|
||||
|
||||
void UrlCoordinator::Stop()
|
||||
{
|
||||
Thread::Stop();
|
||||
|
||||
debug("Stopping UrlDownloads");
|
||||
g_pQueueCoordinator->LockQueue();
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
(*it)->Stop();
|
||||
}
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
debug("UrlDownloads are notified");
|
||||
}
|
||||
|
||||
void UrlCoordinator::ResetHangingDownloads()
|
||||
{
|
||||
const int TimeOut = g_pOptions->GetTerminateTimeout();
|
||||
if (TimeOut == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->LockQueue();
|
||||
time_t tm = ::time(NULL);
|
||||
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end();)
|
||||
{
|
||||
UrlDownloader* pUrlDownloader = *it;
|
||||
if (tm - pUrlDownloader->GetLastUpdateTime() > TimeOut &&
|
||||
pUrlDownloader->GetStatus() == UrlDownloader::adRunning)
|
||||
{
|
||||
UrlInfo* pUrlInfo = pUrlDownloader->GetUrlInfo();
|
||||
debug("Terminating hanging download %s", pUrlDownloader->GetInfoName());
|
||||
if (pUrlDownloader->Terminate())
|
||||
{
|
||||
error("Terminated hanging download %s", pUrlDownloader->GetInfoName());
|
||||
pUrlInfo->SetStatus(UrlInfo::aiUndefined);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not terminate hanging download %s", pUrlDownloader->GetInfoName());
|
||||
}
|
||||
m_ActiveDownloads.erase(it);
|
||||
// it's not safe to destroy pUrlDownloader, because the state of object is unknown
|
||||
delete pUrlDownloader;
|
||||
it = m_ActiveDownloads.begin();
|
||||
continue;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
}
|
||||
|
||||
void UrlCoordinator::LogDebugInfo()
|
||||
{
|
||||
debug(" UrlCoordinator");
|
||||
debug(" ----------------");
|
||||
|
||||
g_pQueueCoordinator->LockQueue();
|
||||
debug(" Active Downloads: %i", m_ActiveDownloads.size());
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
UrlDownloader* pUrlDownloader = *it;
|
||||
pUrlDownloader->LogDebugInfo();
|
||||
}
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
}
|
||||
|
||||
void UrlCoordinator::AddUrlToQueue(UrlInfo* pUrlInfo, bool AddFirst)
|
||||
{
|
||||
debug("Adding NZB-URL to queue");
|
||||
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
|
||||
pDownloadQueue->GetUrlQueue()->push_back(pUrlInfo);
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
|
||||
}
|
||||
|
||||
if (pUrlInfo->GetForce())
|
||||
{
|
||||
m_bForce = true;
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns next URL for download.
|
||||
*/
|
||||
bool UrlCoordinator::GetNextUrl(DownloadQueue* pDownloadQueue, UrlInfo* &pUrlInfo)
|
||||
{
|
||||
bool bPauseDownload = g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2();
|
||||
|
||||
for (UrlQueue::iterator at = pDownloadQueue->GetUrlQueue()->begin(); at != pDownloadQueue->GetUrlQueue()->end(); at++)
|
||||
{
|
||||
pUrlInfo = *at;
|
||||
if (pUrlInfo->GetStatus() == 0 && (!bPauseDownload || pUrlInfo->GetForce() || g_pOptions->GetUrlForce()))
|
||||
{
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void UrlCoordinator::StartUrlDownload(UrlInfo* pUrlInfo)
|
||||
{
|
||||
debug("Starting new UrlDownloader");
|
||||
|
||||
UrlDownloader* pUrlDownloader = new UrlDownloader();
|
||||
pUrlDownloader->SetAutoDestroy(true);
|
||||
pUrlDownloader->Attach(this);
|
||||
pUrlDownloader->SetUrlInfo(pUrlInfo);
|
||||
pUrlDownloader->SetURL(pUrlInfo->GetURL());
|
||||
pUrlDownloader->SetForce(pUrlInfo->GetForce() || g_pOptions->GetUrlForce());
|
||||
|
||||
char tmp[1024];
|
||||
|
||||
pUrlInfo->GetName(tmp, 1024);
|
||||
pUrlDownloader->SetInfoName(tmp);
|
||||
|
||||
snprintf(tmp, 1024, "%surl-%i.tmp", g_pOptions->GetTempDir(), pUrlInfo->GetID());
|
||||
tmp[1024-1] = '\0';
|
||||
pUrlDownloader->SetOutputFilename(tmp);
|
||||
|
||||
pUrlInfo->SetStatus(UrlInfo::aiRunning);
|
||||
|
||||
m_ActiveDownloads.push_back(pUrlDownloader);
|
||||
pUrlDownloader->Start();
|
||||
}
|
||||
|
||||
void UrlCoordinator::Update(Subject* pCaller, void* pAspect)
|
||||
{
|
||||
debug("Notification from UrlDownloader received");
|
||||
|
||||
UrlDownloader* pUrlDownloader = (UrlDownloader*) pCaller;
|
||||
if ((pUrlDownloader->GetStatus() == WebDownloader::adFinished) ||
|
||||
(pUrlDownloader->GetStatus() == WebDownloader::adFailed) ||
|
||||
(pUrlDownloader->GetStatus() == WebDownloader::adRetry))
|
||||
{
|
||||
UrlCompleted(pUrlDownloader);
|
||||
}
|
||||
}
|
||||
|
||||
void UrlCoordinator::UrlCompleted(UrlDownloader* pUrlDownloader)
|
||||
{
|
||||
debug("URL downloaded");
|
||||
|
||||
UrlInfo* pUrlInfo = pUrlDownloader->GetUrlInfo();
|
||||
|
||||
if (pUrlDownloader->GetStatus() == WebDownloader::adFinished)
|
||||
{
|
||||
pUrlInfo->SetStatus(UrlInfo::aiFinished);
|
||||
}
|
||||
else if (pUrlDownloader->GetStatus() == WebDownloader::adFailed)
|
||||
{
|
||||
pUrlInfo->SetStatus(UrlInfo::aiFailed);
|
||||
}
|
||||
else if (pUrlDownloader->GetStatus() == WebDownloader::adRetry)
|
||||
{
|
||||
pUrlInfo->SetStatus(UrlInfo::aiUndefined);
|
||||
}
|
||||
|
||||
char filename[1024];
|
||||
if (pUrlDownloader->GetOriginalFilename())
|
||||
{
|
||||
strncpy(filename, pUrlDownloader->GetOriginalFilename(), 1024);
|
||||
filename[1024-1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(filename, Util::BaseFileName(pUrlInfo->GetURL()), 1024);
|
||||
filename[1024-1] = '\0';
|
||||
|
||||
// TODO: decode URL escaping
|
||||
}
|
||||
|
||||
Util::MakeValidFilename(filename, '_', false);
|
||||
|
||||
debug("Filename: [%s]", filename);
|
||||
|
||||
// delete Download from active jobs
|
||||
g_pQueueCoordinator->LockQueue();
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
UrlDownloader* pa = *it;
|
||||
if (pa == pUrlDownloader)
|
||||
{
|
||||
m_ActiveDownloads.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
|
||||
Aspect aspect = { eaUrlCompleted, pUrlInfo };
|
||||
Notify(&aspect);
|
||||
|
||||
if (pUrlInfo->GetStatus() == UrlInfo::aiFinished)
|
||||
{
|
||||
// add nzb-file to download queue
|
||||
Scanner::EAddStatus eAddStatus = g_pScanner->AddExternalFile(
|
||||
pUrlInfo->GetNZBFilename() && strlen(pUrlInfo->GetNZBFilename()) > 0 ? pUrlInfo->GetNZBFilename() : filename,
|
||||
strlen(pUrlInfo->GetCategory()) > 0 ? pUrlInfo->GetCategory() : pUrlDownloader->GetCategory(),
|
||||
pUrlInfo->GetPriority(), pUrlInfo->GetDupeKey(), pUrlInfo->GetDupeScore(), pUrlInfo->GetDupeMode(),
|
||||
pUrlDownloader->GetParameters(), pUrlInfo->GetAddTop(), pUrlInfo->GetAddPaused(),
|
||||
pUrlDownloader->GetOutputFilename(), NULL, 0);
|
||||
|
||||
if (eAddStatus != Scanner::asSuccess)
|
||||
{
|
||||
pUrlInfo->SetStatus(eAddStatus == Scanner::asFailed ? UrlInfo::aiScanFailed : UrlInfo::aiScanSkipped);
|
||||
}
|
||||
}
|
||||
|
||||
// delete Download from Url Queue
|
||||
if (pUrlInfo->GetStatus() != UrlInfo::aiRetry)
|
||||
{
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
|
||||
for (UrlQueue::iterator it = pDownloadQueue->GetUrlQueue()->begin(); it != pDownloadQueue->GetUrlQueue()->end(); it++)
|
||||
{
|
||||
UrlInfo* pa = *it;
|
||||
if (pa == pUrlInfo)
|
||||
{
|
||||
pDownloadQueue->GetUrlQueue()->erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool bDeleteObj = true;
|
||||
|
||||
if (g_pOptions->GetKeepHistory() > 0 && pUrlInfo->GetStatus() != UrlInfo::aiFinished)
|
||||
{
|
||||
HistoryInfo* pHistoryInfo = new HistoryInfo(pUrlInfo);
|
||||
pHistoryInfo->SetTime(time(NULL));
|
||||
pDownloadQueue->GetHistoryList()->push_front(pHistoryInfo);
|
||||
bDeleteObj = false;
|
||||
}
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
|
||||
if (bDeleteObj)
|
||||
{
|
||||
delete pUrlInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
98
UrlCoordinator.h
Normal file
98
UrlCoordinator.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2012-2013 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef URLCOORDINATOR_H
|
||||
#define URLCOORDINATOR_H
|
||||
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <time.h>
|
||||
|
||||
#include "Thread.h"
|
||||
#include "WebDownloader.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Observer.h"
|
||||
|
||||
class UrlDownloader;
|
||||
|
||||
class UrlCoordinator : public Thread, public Observer, public Subject
|
||||
{
|
||||
public:
|
||||
typedef std::list<UrlDownloader*> ActiveDownloads;
|
||||
enum EAspectAction
|
||||
{
|
||||
eaUrlAdded,
|
||||
eaUrlCompleted
|
||||
};
|
||||
struct Aspect
|
||||
{
|
||||
EAspectAction eAction;
|
||||
UrlInfo* pUrlInfo;
|
||||
};
|
||||
|
||||
private:
|
||||
ActiveDownloads m_ActiveDownloads;
|
||||
bool m_bHasMoreJobs;
|
||||
bool m_bForce;
|
||||
|
||||
bool GetNextUrl(DownloadQueue* pDownloadQueue, UrlInfo* &pUrlInfo);
|
||||
void StartUrlDownload(UrlInfo* pUrlInfo);
|
||||
void UrlCompleted(UrlDownloader* pUrlDownloader);
|
||||
void ResetHangingDownloads();
|
||||
|
||||
public:
|
||||
UrlCoordinator();
|
||||
virtual ~UrlCoordinator();
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
void Update(Subject* pCaller, void* pAspect);
|
||||
|
||||
// Editing the queue
|
||||
void AddUrlToQueue(UrlInfo* pUrlInfo, bool AddFirst);
|
||||
bool HasMoreJobs() { return m_bHasMoreJobs; }
|
||||
|
||||
void LogDebugInfo();
|
||||
};
|
||||
|
||||
class UrlDownloader : public WebDownloader
|
||||
{
|
||||
private:
|
||||
UrlInfo* m_pUrlInfo;
|
||||
char* m_szCategory;
|
||||
NZBParameterList m_ppParameters;
|
||||
|
||||
protected:
|
||||
virtual void ProcessHeader(const char* szLine);
|
||||
|
||||
public:
|
||||
UrlDownloader();
|
||||
~UrlDownloader();
|
||||
void SetUrlInfo(UrlInfo* pUrlInfo) { m_pUrlInfo = pUrlInfo; }
|
||||
UrlInfo* GetUrlInfo() { return m_pUrlInfo; }
|
||||
const char* GetCategory() { return m_szCategory; }
|
||||
NZBParameterList* GetParameters() { return &m_ppParameters; }
|
||||
};
|
||||
|
||||
#endif
|
||||
224
Util.h
224
Util.h
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2013 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,7 +15,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -29,7 +29,6 @@
|
||||
#ifdef WIN32
|
||||
#include <stdio.h>
|
||||
#include <io.h>
|
||||
#include <sys/timeb.h>
|
||||
#else
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
@@ -58,6 +57,19 @@ public:
|
||||
const char* Next();
|
||||
};
|
||||
|
||||
class StringBuilder
|
||||
{
|
||||
private:
|
||||
char* m_szBuffer;
|
||||
int m_iBufferSize;
|
||||
int m_iUsedSize;
|
||||
public:
|
||||
StringBuilder();
|
||||
~StringBuilder();
|
||||
void Append(const char* szStr);
|
||||
const char* GetBuffer() { return m_szBuffer; }
|
||||
};
|
||||
|
||||
class Util
|
||||
{
|
||||
public:
|
||||
@@ -65,15 +77,47 @@ public:
|
||||
static char* BaseFileName(const char* filename);
|
||||
static void NormalizePathSeparators(char* szPath);
|
||||
static bool LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength);
|
||||
static bool SetFileSize(const char* szFilename, int iSize);
|
||||
static void MakeValidFilename(char* szFilename, char cReplaceChar);
|
||||
static bool SaveBufferIntoFile(const char* szFileName, const char* szBuffer, int iBufLen);
|
||||
static bool CreateSparseFile(const char* szFilename, int iSize);
|
||||
static bool TruncateFile(const char* szFilename, int iSize);
|
||||
static void MakeValidFilename(char* szFilename, char cReplaceChar, bool bAllowSlashes);
|
||||
static bool MakeUniqueFilename(char* szDestBufFilename, int iDestBufSize, const char* szDestDir, const char* szBasename);
|
||||
static bool MoveFile(const char* szSrcFilename, const char* szDstFilename);
|
||||
static bool FileExists(const char* szFilename);
|
||||
static bool FileExists(const char* szPath, const char* szFilenameWithoutPath);
|
||||
static bool DirectoryExists(const char* szDirFilename);
|
||||
static bool CreateDirectory(const char* szDirFilename);
|
||||
static bool ForceDirectories(const char* szPath);
|
||||
static bool RemoveDirectory(const char* szDirFilename);
|
||||
static bool DeleteDirectoryWithContent(const char* szDirFilename);
|
||||
static bool ForceDirectories(const char* szPath, char* szErrBuf, int iBufSize);
|
||||
static bool GetCurrentDirectory(char* szBuffer, int iBufSize);
|
||||
static bool SetCurrentDirectory(const char* szDirFilename);
|
||||
static long long FileSize(const char* szFilename);
|
||||
static long long FreeDiskSize(const char* szPath);
|
||||
static bool DirEmpty(const char* szDirFilename);
|
||||
static bool RenameBak(const char* szFilename, const char* szBakPart, bool bRemoveOldExtension, char* szNewNameBuf, int iNewNameBufSize);
|
||||
#ifndef WIN32
|
||||
static bool ExpandHomePath(const char* szFilename, char* szBuffer, int iBufSize);
|
||||
static void FixExecPermission(const char* szFilename);
|
||||
#endif
|
||||
static void ExpandFileName(const char* szFilename, char* szBuffer, int iBufSize);
|
||||
static void FormatFileSize(char* szBuffer, int iBufLen, long long lFileSize);
|
||||
static bool SameFilename(const char* szFilename1, const char* szFilename2);
|
||||
static char* GetLastErrorMessage(char* szBuffer, int iBufLen);
|
||||
|
||||
/*
|
||||
* Split command line int arguments.
|
||||
* Uses spaces and single quotation marks as separators.
|
||||
* Returns bool if sucessful or false if bad escaping was detected.
|
||||
* Parameter "argv" may be NULL if only a syntax check is needed.
|
||||
* Parsed parameters returned in Array "argv", which contains at least one element.
|
||||
* The last element in array is NULL.
|
||||
* Restrictions: the number of arguments is limited to 100 and each arguments must
|
||||
* be maximum 1024 chars long.
|
||||
* If these restrictions are exceeded, only first 100 arguments and only first 1024
|
||||
* for each argument are returned (the functions still returns "true").
|
||||
*/
|
||||
static bool SplitCommandLine(const char* szCommandLine, char*** argv);
|
||||
|
||||
static long long JoinInt64(unsigned long Hi, unsigned long Lo);
|
||||
static void SplitInt64(long long Int64, unsigned long* Hi, unsigned long* Lo);
|
||||
@@ -85,10 +129,39 @@ public:
|
||||
*/
|
||||
static float Int64ToFloat(long long Int64);
|
||||
|
||||
static float EqualTime(_timeval* t1, _timeval* t2);
|
||||
static bool EmptyTime(_timeval* t);
|
||||
static float DiffTime(_timeval* t1, _timeval* t2);
|
||||
static void TrimRight(char* szStr);
|
||||
static char* Trim(char* szStr);
|
||||
static bool EmptyStr(const char* szStr) { return !szStr || !*szStr; }
|
||||
|
||||
/* replace all occurences of szFrom to szTo in string szStr with a limitation that szTo must be shorter than szFrom */
|
||||
static char* ReduceStr(char* szStr, const char* szFrom, const char* szTo);
|
||||
|
||||
/* Calculate Hash using Bob Jenkins (1996) algorithm */
|
||||
static unsigned int HashBJ96(const char* szBuffer, int iBufSize, unsigned int iInitValue);
|
||||
|
||||
#ifdef WIN32
|
||||
static bool RegReadStr(HKEY hKey, const char* szKeyName, const char* szValueName, char* szBuffer, int* iBufLen);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Returns program version and revision number as string formatted like "0.7.0-r295".
|
||||
* If revision number is not available only version is returned ("0.7.0").
|
||||
*/
|
||||
static const char* VersionRevision() { return VersionRevisionBuf; };
|
||||
|
||||
/*
|
||||
* Initialize buffer for program version and revision number.
|
||||
* This function must be called during program initialization before any
|
||||
* call to "VersionRevision()".
|
||||
*/
|
||||
static void InitVersionRevision();
|
||||
|
||||
static char VersionRevisionBuf[40];
|
||||
};
|
||||
|
||||
class WebUtil
|
||||
{
|
||||
public:
|
||||
static unsigned int DecodeBase64(char* szInputBuffer, int iInputBufferLength, char* szOutputBuffer);
|
||||
|
||||
/*
|
||||
@@ -137,6 +210,139 @@ public:
|
||||
* The returned pointer points to the part of source-string, no additional strings are allocated.
|
||||
*/
|
||||
static const char* JsonNextValue(const char* szJsonText, int* pValueLength);
|
||||
|
||||
/*
|
||||
* Unquote http quoted string.
|
||||
* The string is decoded on the place overwriting the content of raw-data.
|
||||
*/
|
||||
static void HttpUnquote(char* raw);
|
||||
|
||||
#ifdef WIN32
|
||||
static bool Utf8ToAnsi(char* szBuffer, int iBufLen);
|
||||
static bool AnsiToUtf8(char* szBuffer, int iBufLen);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Converts ISO-8859-1 (aka Latin-1) into UTF-8.
|
||||
* Returns new string allocated with malloc, it needs to be freed by caller.
|
||||
*/
|
||||
static char* Latin1ToUtf8(const char* szStr);
|
||||
|
||||
static time_t ParseRfc822DateTime(const char* szDateTimeStr);
|
||||
};
|
||||
|
||||
class URL
|
||||
{
|
||||
private:
|
||||
char* m_szAddress;
|
||||
char* m_szProtocol;
|
||||
char* m_szUser;
|
||||
char* m_szPassword;
|
||||
char* m_szHost;
|
||||
char* m_szResource;
|
||||
int m_iPort;
|
||||
bool m_bTLS;
|
||||
bool m_bValid;
|
||||
void ParseURL();
|
||||
|
||||
public:
|
||||
URL(const char* szAddress);
|
||||
~URL();
|
||||
bool IsValid() { return m_bValid; }
|
||||
const char* GetAddress() { return m_szAddress; }
|
||||
const char* GetProtocol() { return m_szProtocol; }
|
||||
const char* GetUser() { return m_szUser; }
|
||||
const char* GetPassword() { return m_szPassword; }
|
||||
const char* GetHost() { return m_szHost; }
|
||||
const char* GetResource() { return m_szResource; }
|
||||
int GetPort() { return m_iPort; }
|
||||
bool GetTLS() { return m_bTLS; }
|
||||
};
|
||||
|
||||
class RegEx
|
||||
{
|
||||
private:
|
||||
void* m_pContext;
|
||||
bool m_bValid;
|
||||
void* m_pMatches;
|
||||
int m_iMatchBufSize;
|
||||
|
||||
public:
|
||||
RegEx(const char *szPattern, int iMatchBufSize = 100);
|
||||
~RegEx();
|
||||
bool IsValid() { return m_bValid; }
|
||||
bool Match(const char *szStr);
|
||||
int GetMatchCount();
|
||||
int GetMatchStart(int index);
|
||||
int GetMatchLen(int index);
|
||||
};
|
||||
|
||||
class WildMask
|
||||
{
|
||||
private:
|
||||
char* m_szPattern;
|
||||
bool m_bWantsPositions;
|
||||
int m_iWildCount;
|
||||
int* m_WildStart;
|
||||
int* m_WildLen;
|
||||
int m_iArrLen;
|
||||
|
||||
void ExpandArray();
|
||||
|
||||
public:
|
||||
WildMask(const char *szPattern, bool bWantsPositions = false);
|
||||
~WildMask();
|
||||
bool Match(const char *szStr);
|
||||
int GetMatchCount() { return m_iWildCount; }
|
||||
int GetMatchStart(int index) { return m_WildStart[index]; }
|
||||
int GetMatchLen(int index) { return m_WildLen[index]; }
|
||||
};
|
||||
|
||||
#ifndef DISABLE_GZIP
|
||||
class ZLib
|
||||
{
|
||||
public:
|
||||
/*
|
||||
* calculates the size required for output buffer
|
||||
*/
|
||||
static unsigned int GZipLen(int iInputBufferLength);
|
||||
|
||||
/*
|
||||
* returns the size of bytes written to szOutputBuffer or 0 if the buffer is too small or an error occured.
|
||||
*/
|
||||
static unsigned int GZip(const void* szInputBuffer, int iInputBufferLength, void* szOutputBuffer, int iOutputBufferLength);
|
||||
};
|
||||
|
||||
class GUnzipStream
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
zlError,
|
||||
zlFinished,
|
||||
zlOK
|
||||
};
|
||||
|
||||
private:
|
||||
void* m_pZStream;
|
||||
void* m_pOutputBuffer;
|
||||
int m_iBufferSize;
|
||||
|
||||
public:
|
||||
GUnzipStream(int BufferSize);
|
||||
~GUnzipStream();
|
||||
|
||||
/*
|
||||
* set next memory block for uncompression
|
||||
*/
|
||||
void Write(const void *pInputBuffer, int iInputBufferLength);
|
||||
|
||||
/*
|
||||
* get next uncompressed memory block.
|
||||
* iOutputBufferLength - the size of uncompressed block. if it is "0" the next compressed block must be provided via "Write".
|
||||
*/
|
||||
EStatus Read(const void **pOutputBuffer, int *iOutputBufferLength);
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
722
WebDownloader.cpp
Normal file
722
WebDownloader.cpp
Normal file
@@ -0,0 +1,722 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2012-2013 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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>
|
||||
#ifdef WIN32
|
||||
#include <direct.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "WebDownloader.h"
|
||||
#include "Log.h"
|
||||
#include "Options.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
WebDownloader::WebDownloader()
|
||||
{
|
||||
debug("Creating WebDownloader");
|
||||
|
||||
m_szURL = NULL;
|
||||
m_szOutputFilename = NULL;
|
||||
m_pConnection = NULL;
|
||||
m_szInfoName = NULL;
|
||||
m_bConfirmedLength = false;
|
||||
m_eStatus = adUndefined;
|
||||
m_szOriginalFilename = NULL;
|
||||
m_bForce = false;
|
||||
m_bRetry = true;
|
||||
SetLastUpdateTimeNow();
|
||||
}
|
||||
|
||||
WebDownloader::~WebDownloader()
|
||||
{
|
||||
debug("Destroying WebDownloader");
|
||||
|
||||
free(m_szURL);
|
||||
free(m_szInfoName);
|
||||
free(m_szOutputFilename);
|
||||
free(m_szOriginalFilename);
|
||||
}
|
||||
|
||||
void WebDownloader::SetOutputFilename(const char* v)
|
||||
{
|
||||
m_szOutputFilename = strdup(v);
|
||||
}
|
||||
|
||||
void WebDownloader::SetInfoName(const char* v)
|
||||
{
|
||||
m_szInfoName = strdup(v);
|
||||
}
|
||||
|
||||
void WebDownloader::SetURL(const char * szURL)
|
||||
{
|
||||
free(m_szURL);
|
||||
m_szURL = strdup(szURL);
|
||||
}
|
||||
|
||||
void WebDownloader::SetStatus(EStatus eStatus)
|
||||
{
|
||||
m_eStatus = eStatus;
|
||||
Notify(NULL);
|
||||
}
|
||||
|
||||
void WebDownloader::Run()
|
||||
{
|
||||
debug("Entering WebDownloader-loop");
|
||||
|
||||
SetStatus(adRunning);
|
||||
|
||||
int iRemainedDownloadRetries = g_pOptions->GetRetries() > 0 ? g_pOptions->GetRetries() : 1;
|
||||
int iRemainedConnectRetries = iRemainedDownloadRetries > 10 ? iRemainedDownloadRetries : 10;
|
||||
if (!m_bRetry)
|
||||
{
|
||||
iRemainedDownloadRetries = 1;
|
||||
iRemainedConnectRetries = 1;
|
||||
}
|
||||
|
||||
m_iRedirects = 0;
|
||||
EStatus Status = adFailed;
|
||||
|
||||
while (!IsStopped() && iRemainedDownloadRetries > 0 && iRemainedConnectRetries > 0)
|
||||
{
|
||||
SetLastUpdateTimeNow();
|
||||
|
||||
Status = Download();
|
||||
|
||||
if ((((Status == adFailed) && (iRemainedDownloadRetries > 1)) ||
|
||||
((Status == adConnectError) && (iRemainedConnectRetries > 1)))
|
||||
&& !IsStopped() && !(!m_bForce && (g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2())))
|
||||
{
|
||||
detail("Waiting %i sec to retry", g_pOptions->GetRetryInterval());
|
||||
int msec = 0;
|
||||
while (!IsStopped() && (msec < g_pOptions->GetRetryInterval() * 1000) &&
|
||||
!(!m_bForce && (g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2())))
|
||||
{
|
||||
usleep(100 * 1000);
|
||||
msec += 100;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsStopped() || (!m_bForce && (g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2())))
|
||||
{
|
||||
Status = adRetry;
|
||||
break;
|
||||
}
|
||||
|
||||
if (Status == adFinished || Status == adFatalError || Status == adNotFound)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (Status == adRedirect)
|
||||
{
|
||||
m_iRedirects++;
|
||||
if (m_iRedirects > 5)
|
||||
{
|
||||
warn("Too many redirects for %s", m_szInfoName);
|
||||
Status = adFailed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (Status != adConnectError)
|
||||
{
|
||||
iRemainedDownloadRetries--;
|
||||
}
|
||||
else
|
||||
{
|
||||
iRemainedConnectRetries--;
|
||||
}
|
||||
}
|
||||
|
||||
if (Status != adFinished && Status != adRetry)
|
||||
{
|
||||
Status = adFailed;
|
||||
}
|
||||
|
||||
if (Status == adFailed)
|
||||
{
|
||||
if (IsStopped())
|
||||
{
|
||||
detail("Download %s cancelled", m_szInfoName);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Download %s failed", m_szInfoName);
|
||||
}
|
||||
}
|
||||
|
||||
if (Status == adFinished)
|
||||
{
|
||||
detail("Download %s completed", m_szInfoName);
|
||||
}
|
||||
|
||||
SetStatus(Status);
|
||||
|
||||
debug("Exiting WebDownloader-loop");
|
||||
}
|
||||
|
||||
WebDownloader::EStatus WebDownloader::Download()
|
||||
{
|
||||
EStatus Status = adRunning;
|
||||
|
||||
URL url(m_szURL);
|
||||
|
||||
Status = CreateConnection(&url);
|
||||
if (Status != adRunning)
|
||||
{
|
||||
return Status;
|
||||
}
|
||||
|
||||
m_pConnection->SetSuppressErrors(false);
|
||||
|
||||
// connection
|
||||
bool bConnected = m_pConnection->Connect();
|
||||
if (!bConnected || IsStopped())
|
||||
{
|
||||
FreeConnection();
|
||||
return adConnectError;
|
||||
}
|
||||
|
||||
// Okay, we got a Connection. Now start downloading.
|
||||
detail("Downloading %s", m_szInfoName);
|
||||
|
||||
SendHeaders(&url);
|
||||
|
||||
Status = DownloadHeaders();
|
||||
|
||||
if (Status == adRunning)
|
||||
{
|
||||
Status = DownloadBody();
|
||||
}
|
||||
|
||||
if (IsStopped())
|
||||
{
|
||||
Status = adFailed;
|
||||
}
|
||||
|
||||
FreeConnection();
|
||||
|
||||
if (Status != adFinished)
|
||||
{
|
||||
// Download failed, delete broken output file
|
||||
remove(m_szOutputFilename);
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
|
||||
WebDownloader::EStatus WebDownloader::CreateConnection(URL *pUrl)
|
||||
{
|
||||
if (!pUrl->IsValid())
|
||||
{
|
||||
error("URL is not valid: %s", pUrl->GetAddress());
|
||||
return adFatalError;
|
||||
}
|
||||
|
||||
int iPort = pUrl->GetPort();
|
||||
if (iPort == 0 && !strcasecmp(pUrl->GetProtocol(), "http"))
|
||||
{
|
||||
iPort = 80;
|
||||
}
|
||||
if (iPort == 0 && !strcasecmp(pUrl->GetProtocol(), "https"))
|
||||
{
|
||||
iPort = 443;
|
||||
}
|
||||
|
||||
if (strcasecmp(pUrl->GetProtocol(), "http") && strcasecmp(pUrl->GetProtocol(), "https"))
|
||||
{
|
||||
error("Unsupported protocol in URL: %s", pUrl->GetAddress());
|
||||
return adFatalError;
|
||||
}
|
||||
|
||||
#ifdef DISABLE_TLS
|
||||
if (!strcasecmp(pUrl->GetProtocol(), "https"))
|
||||
{
|
||||
error("Program was compiled without TLS/SSL-support. Cannot download using https protocol. URL: %s", pUrl->GetAddress());
|
||||
return adFatalError;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool bTLS = !strcasecmp(pUrl->GetProtocol(), "https");
|
||||
|
||||
m_pConnection = new Connection(pUrl->GetHost(), iPort, bTLS);
|
||||
|
||||
return adRunning;
|
||||
}
|
||||
|
||||
void WebDownloader::SendHeaders(URL *pUrl)
|
||||
{
|
||||
char tmp[1024];
|
||||
|
||||
// retrieve file
|
||||
snprintf(tmp, 1024, "GET %s HTTP/1.0\r\n", pUrl->GetResource());
|
||||
tmp[1024-1] = '\0';
|
||||
m_pConnection->WriteLine(tmp);
|
||||
|
||||
snprintf(tmp, 1024, "User-Agent: nzbget/%s\r\n", Util::VersionRevision());
|
||||
tmp[1024-1] = '\0';
|
||||
m_pConnection->WriteLine(tmp);
|
||||
|
||||
snprintf(tmp, 1024, "Host: %s\r\n", pUrl->GetHost());
|
||||
tmp[1024-1] = '\0';
|
||||
m_pConnection->WriteLine(tmp);
|
||||
|
||||
m_pConnection->WriteLine("Accept: */*\r\n");
|
||||
#ifndef DISABLE_GZIP
|
||||
m_pConnection->WriteLine("Accept-Encoding: gzip\r\n");
|
||||
#endif
|
||||
m_pConnection->WriteLine("Connection: close\r\n");
|
||||
m_pConnection->WriteLine("\r\n");
|
||||
}
|
||||
|
||||
WebDownloader::EStatus WebDownloader::DownloadHeaders()
|
||||
{
|
||||
EStatus Status = adRunning;
|
||||
|
||||
m_bConfirmedLength = false;
|
||||
const int LineBufSize = 1024*10;
|
||||
char* szLineBuf = (char*)malloc(LineBufSize);
|
||||
m_iContentLen = -1;
|
||||
bool bFirstLine = true;
|
||||
m_bGZip = false;
|
||||
m_bRedirecting = false;
|
||||
m_bRedirected = false;
|
||||
|
||||
// Headers
|
||||
while (!IsStopped())
|
||||
{
|
||||
SetLastUpdateTimeNow();
|
||||
|
||||
int iLen = 0;
|
||||
char* line = m_pConnection->ReadLine(szLineBuf, LineBufSize, &iLen);
|
||||
|
||||
if (bFirstLine)
|
||||
{
|
||||
Status = CheckResponse(szLineBuf);
|
||||
if (Status != adRunning)
|
||||
{
|
||||
break;
|
||||
}
|
||||
bFirstLine = false;
|
||||
}
|
||||
|
||||
// Have we encountered a timeout?
|
||||
if (!line)
|
||||
{
|
||||
if (!IsStopped())
|
||||
{
|
||||
warn("URL %s failed: Unexpected end of file", m_szInfoName);
|
||||
}
|
||||
Status = adFailed;
|
||||
break;
|
||||
}
|
||||
|
||||
debug("Header: %s", line);
|
||||
|
||||
// detect body of response
|
||||
if (*line == '\r' || *line == '\n')
|
||||
{
|
||||
if (m_iContentLen == -1 && !m_bGZip)
|
||||
{
|
||||
warn("URL %s: Content-Length is not submitted by server, cannot verify whether the file is complete", m_szInfoName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Util::TrimRight(line);
|
||||
ProcessHeader(line);
|
||||
|
||||
if (m_bRedirected)
|
||||
{
|
||||
Status = adRedirect;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(szLineBuf);
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
WebDownloader::EStatus WebDownloader::DownloadBody()
|
||||
{
|
||||
EStatus Status = adRunning;
|
||||
|
||||
m_pOutFile = NULL;
|
||||
bool bEnd = false;
|
||||
const int LineBufSize = 1024*10;
|
||||
char* szLineBuf = (char*)malloc(LineBufSize);
|
||||
int iWrittenLen = 0;
|
||||
|
||||
#ifndef DISABLE_GZIP
|
||||
m_pGUnzipStream = NULL;
|
||||
if (m_bGZip)
|
||||
{
|
||||
m_pGUnzipStream = new GUnzipStream(1024*10);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Body
|
||||
while (!IsStopped())
|
||||
{
|
||||
SetLastUpdateTimeNow();
|
||||
|
||||
char* szBuffer;
|
||||
int iLen;
|
||||
m_pConnection->ReadBuffer(&szBuffer, &iLen);
|
||||
if (iLen == 0)
|
||||
{
|
||||
iLen = m_pConnection->TryRecv(szLineBuf, LineBufSize);
|
||||
szBuffer = szLineBuf;
|
||||
}
|
||||
|
||||
// Have we encountered a timeout?
|
||||
if (iLen <= 0)
|
||||
{
|
||||
if (m_iContentLen == -1 && iWrittenLen > 0)
|
||||
{
|
||||
bEnd = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!IsStopped())
|
||||
{
|
||||
warn("URL %s failed: Unexpected end of file", m_szInfoName);
|
||||
}
|
||||
Status = adFailed;
|
||||
break;
|
||||
}
|
||||
|
||||
// write to output file
|
||||
if (!Write(szBuffer, iLen))
|
||||
{
|
||||
Status = adFatalError;
|
||||
break;
|
||||
}
|
||||
iWrittenLen += iLen;
|
||||
|
||||
//detect end of file
|
||||
if (iWrittenLen == m_iContentLen || (m_iContentLen == -1 && m_bGZip && m_bConfirmedLength))
|
||||
{
|
||||
bEnd = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(szLineBuf);
|
||||
|
||||
#ifndef DISABLE_GZIP
|
||||
delete m_pGUnzipStream;
|
||||
#endif
|
||||
|
||||
if (m_pOutFile)
|
||||
{
|
||||
fclose(m_pOutFile);
|
||||
}
|
||||
|
||||
if (!bEnd && Status == adRunning && !IsStopped())
|
||||
{
|
||||
warn("URL %s failed: file incomplete", m_szInfoName);
|
||||
Status = adFailed;
|
||||
}
|
||||
|
||||
if (bEnd)
|
||||
{
|
||||
Status = adFinished;
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
WebDownloader::EStatus WebDownloader::CheckResponse(const char* szResponse)
|
||||
{
|
||||
if (!szResponse)
|
||||
{
|
||||
if (!IsStopped())
|
||||
{
|
||||
warn("URL %s: Connection closed by remote host", m_szInfoName);
|
||||
}
|
||||
return adConnectError;
|
||||
}
|
||||
|
||||
const char* szHTTPResponse = strchr(szResponse, ' ');
|
||||
if (strncmp(szResponse, "HTTP", 4) || !szHTTPResponse)
|
||||
{
|
||||
warn("URL %s failed: %s", m_szInfoName, szResponse);
|
||||
return adFailed;
|
||||
}
|
||||
|
||||
szHTTPResponse++;
|
||||
|
||||
if (!strncmp(szHTTPResponse, "400", 3) || !strncmp(szHTTPResponse, "499", 3))
|
||||
{
|
||||
warn("URL %s failed: %s", m_szInfoName, szHTTPResponse);
|
||||
return adConnectError;
|
||||
}
|
||||
else if (!strncmp(szHTTPResponse, "404", 3))
|
||||
{
|
||||
warn("URL %s failed: %s", m_szInfoName, szHTTPResponse);
|
||||
return adNotFound;
|
||||
}
|
||||
else if (!strncmp(szHTTPResponse, "301", 3) || !strncmp(szHTTPResponse, "302", 3))
|
||||
{
|
||||
m_bRedirecting = true;
|
||||
return adRunning;
|
||||
}
|
||||
else if (!strncmp(szHTTPResponse, "200", 3))
|
||||
{
|
||||
// OK
|
||||
return adRunning;
|
||||
}
|
||||
else
|
||||
{
|
||||
// unknown error, no special handling
|
||||
warn("URL %s failed: %s", m_szInfoName, szResponse);
|
||||
return adFailed;
|
||||
}
|
||||
}
|
||||
|
||||
void WebDownloader::ProcessHeader(const char* szLine)
|
||||
{
|
||||
if (!strncasecmp(szLine, "Content-Length: ", 16))
|
||||
{
|
||||
m_iContentLen = atoi(szLine + 16);
|
||||
m_bConfirmedLength = true;
|
||||
}
|
||||
else if (!strncasecmp(szLine, "Content-Encoding: gzip", 22))
|
||||
{
|
||||
m_bGZip = true;
|
||||
}
|
||||
else if (!strncasecmp(szLine, "Content-Disposition: ", 21))
|
||||
{
|
||||
ParseFilename(szLine);
|
||||
}
|
||||
else if (m_bRedirecting && !strncasecmp(szLine, "Location: ", 10))
|
||||
{
|
||||
ParseRedirect(szLine + 10);
|
||||
m_bRedirected = true;
|
||||
}
|
||||
}
|
||||
|
||||
void WebDownloader::ParseFilename(const char* szContentDisposition)
|
||||
{
|
||||
// Examples:
|
||||
// Content-Disposition: attachment; filename="fname.ext"
|
||||
// Content-Disposition: attachement;filename=fname.ext
|
||||
// Content-Disposition: attachement;filename=fname.ext;
|
||||
const char *p = strstr(szContentDisposition, "filename");
|
||||
if (!p)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
p = strchr(p, '=');
|
||||
if (!p)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
p++;
|
||||
|
||||
while (*p == ' ') p++;
|
||||
|
||||
char fname[1024];
|
||||
strncpy(fname, p, 1024);
|
||||
fname[1024-1] = '\0';
|
||||
|
||||
char *pe = fname + strlen(fname) - 1;
|
||||
while ((*pe == ' ' || *pe == '\n' || *pe == '\r' || *pe == ';') && pe > fname) {
|
||||
*pe = '\0';
|
||||
pe--;
|
||||
}
|
||||
|
||||
WebUtil::HttpUnquote(fname);
|
||||
|
||||
free(m_szOriginalFilename);
|
||||
m_szOriginalFilename = strdup(Util::BaseFileName(fname));
|
||||
|
||||
debug("OriginalFilename: %s", m_szOriginalFilename);
|
||||
}
|
||||
|
||||
void WebDownloader::ParseRedirect(const char* szLocation)
|
||||
{
|
||||
const char* szNewURL = szLocation;
|
||||
char szUrlBuf[1024];
|
||||
URL newUrl(szNewURL);
|
||||
if (!newUrl.IsValid())
|
||||
{
|
||||
// relative address
|
||||
URL oldUrl(m_szURL);
|
||||
if (oldUrl.GetPort() > 0)
|
||||
{
|
||||
snprintf(szUrlBuf, 1024, "%s://%s:%i%s", oldUrl.GetProtocol(), oldUrl.GetHost(), oldUrl.GetPort(), szNewURL);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(szUrlBuf, 1024, "%s://%s%s", oldUrl.GetProtocol(), oldUrl.GetHost(), szNewURL);
|
||||
}
|
||||
szUrlBuf[1024-1] = '\0';
|
||||
szNewURL = szUrlBuf;
|
||||
}
|
||||
detail("URL %s redirected to %s", m_szURL, szNewURL);
|
||||
SetURL(szNewURL);
|
||||
}
|
||||
|
||||
bool WebDownloader::Write(void* pBuffer, int iLen)
|
||||
{
|
||||
if (!m_pOutFile && !PrepareFile())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_GZIP
|
||||
if (m_bGZip)
|
||||
{
|
||||
m_pGUnzipStream->Write(pBuffer, iLen);
|
||||
const void *pOutBuf;
|
||||
int iOutLen = 1;
|
||||
while (iOutLen > 0)
|
||||
{
|
||||
GUnzipStream::EStatus eGZStatus = m_pGUnzipStream->Read(&pOutBuf, &iOutLen);
|
||||
|
||||
if (eGZStatus == GUnzipStream::zlError)
|
||||
{
|
||||
error("URL %s: GUnzip failed", m_szInfoName);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (iOutLen > 0 && fwrite(pOutBuf, 1, iOutLen, m_pOutFile) <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (eGZStatus == GUnzipStream::zlFinished)
|
||||
{
|
||||
m_bConfirmedLength = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
||||
return fwrite(pBuffer, 1, iLen, m_pOutFile) > 0;
|
||||
}
|
||||
|
||||
bool WebDownloader::PrepareFile()
|
||||
{
|
||||
// prepare file for writing
|
||||
|
||||
const char* szFilename = m_szOutputFilename;
|
||||
m_pOutFile = fopen(szFilename, "wb");
|
||||
if (!m_pOutFile)
|
||||
{
|
||||
error("Could not %s file %s", "create", szFilename);
|
||||
return false;
|
||||
}
|
||||
if (g_pOptions->GetWriteBufferSize() > 0)
|
||||
{
|
||||
setvbuf(m_pOutFile, (char *)NULL, _IOFBF, g_pOptions->GetWriteBufferSize());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WebDownloader::LogDebugInfo()
|
||||
{
|
||||
char szTime[50];
|
||||
#ifdef HAVE_CTIME_R_3
|
||||
ctime_r(&m_tLastUpdateTime, szTime, 50);
|
||||
#else
|
||||
ctime_r(&m_tLastUpdateTime, szTime);
|
||||
#endif
|
||||
|
||||
debug(" Web-Download: status=%i, LastUpdateTime=%s, filename=%s", m_eStatus, szTime, Util::BaseFileName(m_szOutputFilename));
|
||||
}
|
||||
|
||||
void WebDownloader::Stop()
|
||||
{
|
||||
debug("Trying to stop WebDownloader");
|
||||
Thread::Stop();
|
||||
m_mutexConnection.Lock();
|
||||
if (m_pConnection)
|
||||
{
|
||||
m_pConnection->SetSuppressErrors(true);
|
||||
m_pConnection->Cancel();
|
||||
}
|
||||
m_mutexConnection.Unlock();
|
||||
debug("WebDownloader stopped successfully");
|
||||
}
|
||||
|
||||
bool WebDownloader::Terminate()
|
||||
{
|
||||
Connection* pConnection = m_pConnection;
|
||||
bool terminated = Kill();
|
||||
if (terminated && pConnection)
|
||||
{
|
||||
debug("Terminating connection");
|
||||
pConnection->SetSuppressErrors(true);
|
||||
pConnection->Cancel();
|
||||
pConnection->Disconnect();
|
||||
delete pConnection;
|
||||
}
|
||||
return terminated;
|
||||
}
|
||||
|
||||
void WebDownloader::FreeConnection()
|
||||
{
|
||||
if (m_pConnection)
|
||||
{
|
||||
debug("Releasing connection");
|
||||
m_mutexConnection.Lock();
|
||||
if (m_pConnection->GetStatus() == Connection::csCancelled)
|
||||
{
|
||||
m_pConnection->Disconnect();
|
||||
}
|
||||
delete m_pConnection;
|
||||
m_pConnection = NULL;
|
||||
m_mutexConnection.Unlock();
|
||||
}
|
||||
}
|
||||
112
WebDownloader.h
Normal file
112
WebDownloader.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2012-2013 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef WEBDOWNLOADER_H
|
||||
#define WEBDOWNLOADER_H
|
||||
|
||||
#include <time.h>
|
||||
|
||||
#include "Observer.h"
|
||||
#include "Thread.h"
|
||||
#include "Connection.h"
|
||||
#include "Util.h"
|
||||
|
||||
class WebDownloader : public Thread, public Subject
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
adUndefined,
|
||||
adRunning,
|
||||
adFinished,
|
||||
adFailed,
|
||||
adRetry,
|
||||
adNotFound,
|
||||
adRedirect,
|
||||
adConnectError,
|
||||
adFatalError
|
||||
};
|
||||
|
||||
private:
|
||||
char* m_szURL;
|
||||
char* m_szOutputFilename;
|
||||
Connection* m_pConnection;
|
||||
Mutex m_mutexConnection;
|
||||
EStatus m_eStatus;
|
||||
time_t m_tLastUpdateTime;
|
||||
char* m_szInfoName;
|
||||
FILE* m_pOutFile;
|
||||
int m_iContentLen;
|
||||
bool m_bConfirmedLength;
|
||||
char* m_szOriginalFilename;
|
||||
bool m_bForce;
|
||||
bool m_bRedirecting;
|
||||
bool m_bRedirected;
|
||||
int m_iRedirects;
|
||||
bool m_bGZip;
|
||||
bool m_bRetry;
|
||||
#ifndef DISABLE_GZIP
|
||||
GUnzipStream* m_pGUnzipStream;
|
||||
#endif
|
||||
|
||||
void SetStatus(EStatus eStatus);
|
||||
bool Write(void* pBuffer, int iLen);
|
||||
bool PrepareFile();
|
||||
void FreeConnection();
|
||||
EStatus CheckResponse(const char* szResponse);
|
||||
EStatus CreateConnection(URL *pUrl);
|
||||
void ParseFilename(const char* szContentDisposition);
|
||||
void SendHeaders(URL *pUrl);
|
||||
EStatus DownloadHeaders();
|
||||
EStatus DownloadBody();
|
||||
void ParseRedirect(const char* szLocation);
|
||||
|
||||
protected:
|
||||
virtual void ProcessHeader(const char* szLine);
|
||||
|
||||
public:
|
||||
WebDownloader();
|
||||
~WebDownloader();
|
||||
EStatus GetStatus() { return m_eStatus; }
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
EStatus Download();
|
||||
bool Terminate();
|
||||
void SetInfoName(const char* v);
|
||||
const char* GetInfoName() { return m_szInfoName; }
|
||||
void SetURL(const char* szURL);
|
||||
const char* GetOutputFilename() { return m_szOutputFilename; }
|
||||
void SetOutputFilename(const char* v);
|
||||
time_t GetLastUpdateTime() { return m_tLastUpdateTime; }
|
||||
void SetLastUpdateTimeNow() { m_tLastUpdateTime = ::time(NULL); }
|
||||
bool GetConfirmedLength() { return m_bConfirmedLength; }
|
||||
const char* GetOriginalFilename() { return m_szOriginalFilename; }
|
||||
void SetForce(bool bForce) { m_bForce = bForce; }
|
||||
void SetRetry(bool bRetry) { m_bRetry = bRetry; }
|
||||
|
||||
void LogDebugInfo();
|
||||
};
|
||||
|
||||
#endif
|
||||
498
WebServer.cpp
Normal file
498
WebServer.cpp
Normal file
@@ -0,0 +1,498 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2012-2013 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, 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 "WebServer.h"
|
||||
#include "XmlRpc.h"
|
||||
#include "Log.h"
|
||||
#include "Options.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
static const char* ERR_HTTP_BAD_REQUEST = "400 Bad Request";
|
||||
static const char* ERR_HTTP_NOT_FOUND = "404 Not Found";
|
||||
static const char* ERR_HTTP_SERVICE_UNAVAILABLE = "503 Service Unavailable";
|
||||
|
||||
static const int MAX_UNCOMPRESSED_SIZE = 500;
|
||||
|
||||
//*****************************************************************
|
||||
// WebProcessor
|
||||
|
||||
WebProcessor::WebProcessor()
|
||||
{
|
||||
m_pConnection = NULL;
|
||||
m_szRequest = NULL;
|
||||
m_szUrl = NULL;
|
||||
m_szOrigin = NULL;
|
||||
}
|
||||
|
||||
WebProcessor::~WebProcessor()
|
||||
{
|
||||
free(m_szRequest);
|
||||
free(m_szUrl);
|
||||
free(m_szOrigin);
|
||||
}
|
||||
|
||||
void WebProcessor::SetUrl(const char* szUrl)
|
||||
{
|
||||
m_szUrl = strdup(szUrl);
|
||||
}
|
||||
|
||||
void WebProcessor::Execute()
|
||||
{
|
||||
m_bGZip =false;
|
||||
char szAuthInfo[1024];
|
||||
szAuthInfo[0] = '\0';
|
||||
|
||||
// reading http header
|
||||
char szBuffer[1024];
|
||||
int iContentLen = 0;
|
||||
while (char* p = m_pConnection->ReadLine(szBuffer, sizeof(szBuffer), NULL))
|
||||
{
|
||||
if (char* pe = strrchr(p, '\r')) *pe = '\0';
|
||||
debug("header=%s", p);
|
||||
if (!strncasecmp(p, "Content-Length: ", 16))
|
||||
{
|
||||
iContentLen = atoi(p + 16);
|
||||
}
|
||||
if (!strncasecmp(p, "Authorization: Basic ", 21))
|
||||
{
|
||||
char* szAuthInfo64 = p + 21;
|
||||
if (strlen(szAuthInfo64) > sizeof(szAuthInfo))
|
||||
{
|
||||
error("Invalid-request: auth-info too big");
|
||||
return;
|
||||
}
|
||||
szAuthInfo[WebUtil::DecodeBase64(szAuthInfo64, 0, szAuthInfo)] = '\0';
|
||||
}
|
||||
if (!strncasecmp(p, "Accept-Encoding: ", 17))
|
||||
{
|
||||
m_bGZip = strstr(p, "gzip");
|
||||
}
|
||||
if (!strncasecmp(p, "Origin: ", 8))
|
||||
{
|
||||
m_szOrigin = strdup(p + 8);
|
||||
}
|
||||
if (*p == '\0')
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
debug("URL=%s", m_szUrl);
|
||||
debug("Authorization=%s", szAuthInfo);
|
||||
|
||||
if (m_eHttpMethod == hmPost && iContentLen <= 0)
|
||||
{
|
||||
error("Invalid-request: content length is 0");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_eHttpMethod == hmOptions)
|
||||
{
|
||||
SendOptionsResponse();
|
||||
return;
|
||||
}
|
||||
|
||||
// remove subfolder "nzbget" from the path (if exists)
|
||||
// http://localhost:6789/nzbget/username:password/jsonrpc -> http://localhost:6789/username:password/jsonrpc
|
||||
if (!strncmp(m_szUrl, "/nzbget/", 8))
|
||||
{
|
||||
char* sz_OldUrl = m_szUrl;
|
||||
m_szUrl = strdup(m_szUrl + 7);
|
||||
free(sz_OldUrl);
|
||||
}
|
||||
// http://localhost:6789/nzbget -> http://localhost:6789
|
||||
if (!strcmp(m_szUrl, "/nzbget"))
|
||||
{
|
||||
char szRedirectURL[1024];
|
||||
snprintf(szRedirectURL, 1024, "%s/", m_szUrl);
|
||||
szRedirectURL[1024-1] = '\0';
|
||||
SendRedirectResponse(szRedirectURL);
|
||||
return;
|
||||
}
|
||||
|
||||
// authorization via URL in format:
|
||||
// http://localhost:6789/username:password/jsonrpc
|
||||
char* pauth1 = strchr(m_szUrl + 1, ':');
|
||||
char* pauth2 = strchr(m_szUrl + 1, '/');
|
||||
if (pauth1 && pauth1 < pauth2)
|
||||
{
|
||||
char* pstart = m_szUrl + 1;
|
||||
int iLen = 0;
|
||||
char* pend = strchr(pstart + 1, '/');
|
||||
if (pend)
|
||||
{
|
||||
iLen = (int)(pend - pstart < (int)sizeof(szAuthInfo) - 1 ? pend - pstart : (int)sizeof(szAuthInfo) - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
iLen = strlen(pstart);
|
||||
}
|
||||
strncpy(szAuthInfo, pstart, iLen);
|
||||
szAuthInfo[iLen] = '\0';
|
||||
char* sz_OldUrl = m_szUrl;
|
||||
m_szUrl = strdup(pend);
|
||||
free(sz_OldUrl);
|
||||
}
|
||||
|
||||
debug("Final URL=%s", m_szUrl);
|
||||
|
||||
if (strlen(g_pOptions->GetControlPassword()) > 0 &&
|
||||
!(strlen(g_pOptions->GetAuthorizedIP()) > 0 && IsAuthorizedIP(m_pConnection->GetRemoteAddr())))
|
||||
{
|
||||
if (strlen(szAuthInfo) == 0)
|
||||
{
|
||||
SendAuthResponse();
|
||||
return;
|
||||
}
|
||||
|
||||
// Authorization
|
||||
char* pw = strchr(szAuthInfo, ':');
|
||||
if (pw) *pw++ = '\0';
|
||||
if ((strlen(g_pOptions->GetControlUsername()) > 0 && strcmp(szAuthInfo, g_pOptions->GetControlUsername())) ||
|
||||
strcmp(pw, g_pOptions->GetControlPassword()))
|
||||
{
|
||||
warn("Request received on port %i from %s, but username or password invalid (%s:%s)",
|
||||
g_pOptions->GetControlPort(), m_pConnection->GetRemoteAddr(), szAuthInfo, pw);
|
||||
SendAuthResponse();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_eHttpMethod == hmPost)
|
||||
{
|
||||
// reading http body (request content)
|
||||
m_szRequest = (char*)malloc(iContentLen + 1);
|
||||
m_szRequest[iContentLen] = '\0';
|
||||
|
||||
if (!m_pConnection->Recv(m_szRequest, iContentLen))
|
||||
{
|
||||
error("Invalid-request: could not read data");
|
||||
return;
|
||||
}
|
||||
debug("Request=%s", m_szRequest);
|
||||
}
|
||||
|
||||
debug("request received from %s", m_pConnection->GetRemoteAddr());
|
||||
|
||||
Dispatch();
|
||||
}
|
||||
|
||||
bool WebProcessor::IsAuthorizedIP(const char* szRemoteAddr)
|
||||
{
|
||||
const char* szRemoteIP = m_pConnection->GetRemoteAddr();
|
||||
|
||||
// split option AuthorizedIP into tokens and check each token
|
||||
bool bAuthorized = false;
|
||||
char* szAuthorizedIP = strdup(g_pOptions->GetAuthorizedIP());
|
||||
char* saveptr;
|
||||
char* szIP = strtok_r(szAuthorizedIP, ",;", &saveptr);
|
||||
while (szIP)
|
||||
{
|
||||
szIP = Util::Trim(szIP);
|
||||
if (szIP[0] != '\0' && !strcmp(szIP, szRemoteIP))
|
||||
{
|
||||
bAuthorized = true;
|
||||
break;
|
||||
}
|
||||
szIP = strtok_r(NULL, ",;", &saveptr);
|
||||
}
|
||||
free(szAuthorizedIP);
|
||||
|
||||
return bAuthorized;
|
||||
}
|
||||
|
||||
void WebProcessor::Dispatch()
|
||||
{
|
||||
if (*m_szUrl != '/')
|
||||
{
|
||||
SendErrorResponse(ERR_HTTP_BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
|
||||
if (XmlRpcProcessor::IsRpcRequest(m_szUrl))
|
||||
{
|
||||
XmlRpcProcessor processor;
|
||||
processor.SetRequest(m_szRequest);
|
||||
processor.SetHttpMethod(m_eHttpMethod == hmGet ? XmlRpcProcessor::hmGet : XmlRpcProcessor::hmPost);
|
||||
processor.SetUrl(m_szUrl);
|
||||
processor.Execute();
|
||||
SendBodyResponse(processor.GetResponse(), strlen(processor.GetResponse()), processor.GetContentType());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_pOptions->GetWebDir() || strlen(g_pOptions->GetWebDir()) == 0)
|
||||
{
|
||||
SendErrorResponse(ERR_HTTP_SERVICE_UNAVAILABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_eHttpMethod != hmGet)
|
||||
{
|
||||
SendErrorResponse(ERR_HTTP_BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
|
||||
// for security reasons we allow only characters "0..9 A..Z a..z . - _ /" in the URLs
|
||||
// we also don't allow ".." in the URLs
|
||||
for (char *p = m_szUrl; *p; p++)
|
||||
{
|
||||
if (!((*p >= '0' && *p <= '9') || (*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') ||
|
||||
*p == '.' || *p == '-' || *p == '_' || *p == '/') || (*p == '.' && p[1] == '.'))
|
||||
{
|
||||
SendErrorResponse(ERR_HTTP_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const char *szDefRes = "";
|
||||
if (m_szUrl[strlen(m_szUrl)-1] == '/')
|
||||
{
|
||||
// default file in directory (if not specified) is "index.html"
|
||||
szDefRes = "index.html";
|
||||
}
|
||||
|
||||
char disk_filename[1024];
|
||||
snprintf(disk_filename, sizeof(disk_filename), "%s%s%s", g_pOptions->GetWebDir(), m_szUrl + 1, szDefRes);
|
||||
|
||||
disk_filename[sizeof(disk_filename)-1] = '\0';
|
||||
|
||||
SendFileResponse(disk_filename);
|
||||
}
|
||||
|
||||
void WebProcessor::SendAuthResponse()
|
||||
{
|
||||
const char* AUTH_RESPONSE_HEADER =
|
||||
"HTTP/1.0 401 Unauthorized\r\n"
|
||||
"WWW-Authenticate: Basic realm=\"NZBGet\"\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Server: nzbget-%s\r\n"
|
||||
"\r\n";
|
||||
char szResponseHeader[1024];
|
||||
snprintf(szResponseHeader, 1024, AUTH_RESPONSE_HEADER, Util::VersionRevision());
|
||||
|
||||
// Send the response answer
|
||||
debug("ResponseHeader=%s", szResponseHeader);
|
||||
m_pConnection->Send(szResponseHeader, strlen(szResponseHeader));
|
||||
}
|
||||
|
||||
void WebProcessor::SendOptionsResponse()
|
||||
{
|
||||
const char* OPTIONS_RESPONSE_HEADER =
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Connection: close\r\n"
|
||||
//"Content-Type: plain/text\r\n"
|
||||
"Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n"
|
||||
"Access-Control-Allow-Origin: %s\r\n"
|
||||
"Access-Control-Allow-Credentials: true\r\n"
|
||||
"Access-Control-Max-Age: 86400\r\n"
|
||||
"Access-Control-Allow-Headers: Content-Type, Authorization\r\n"
|
||||
"Server: nzbget-%s\r\n"
|
||||
"\r\n";
|
||||
char szResponseHeader[1024];
|
||||
snprintf(szResponseHeader, 1024, OPTIONS_RESPONSE_HEADER,
|
||||
m_szOrigin ? m_szOrigin : "",
|
||||
Util::VersionRevision());
|
||||
|
||||
// Send the response answer
|
||||
debug("ResponseHeader=%s", szResponseHeader);
|
||||
m_pConnection->Send(szResponseHeader, strlen(szResponseHeader));
|
||||
}
|
||||
|
||||
void WebProcessor::SendErrorResponse(const char* szErrCode)
|
||||
{
|
||||
const char* RESPONSE_HEADER =
|
||||
"HTTP/1.0 %s\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Content-Length: %i\r\n"
|
||||
"Content-Type: text/html\r\n"
|
||||
"Server: nzbget-%s\r\n"
|
||||
"\r\n";
|
||||
|
||||
warn("Web-Server: %s, Resource: %s", szErrCode, m_szUrl);
|
||||
|
||||
char szResponseBody[1024];
|
||||
snprintf(szResponseBody, 1024, "<html><head><title>%s</title></head><body>Error: %s</body></html>", szErrCode, szErrCode);
|
||||
int iPageContentLen = strlen(szResponseBody);
|
||||
|
||||
char szResponseHeader[1024];
|
||||
snprintf(szResponseHeader, 1024, RESPONSE_HEADER, szErrCode, iPageContentLen, Util::VersionRevision());
|
||||
|
||||
// Send the response answer
|
||||
m_pConnection->Send(szResponseHeader, strlen(szResponseHeader));
|
||||
m_pConnection->Send(szResponseBody, iPageContentLen);
|
||||
}
|
||||
|
||||
void WebProcessor::SendRedirectResponse(const char* szURL)
|
||||
{
|
||||
const char* REDIRECT_RESPONSE_HEADER =
|
||||
"HTTP/1.0 301 Moved Permanently\r\n"
|
||||
"Location: %s\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Server: nzbget-%s\r\n"
|
||||
"\r\n";
|
||||
char szResponseHeader[1024];
|
||||
snprintf(szResponseHeader, 1024, REDIRECT_RESPONSE_HEADER, szURL, Util::VersionRevision());
|
||||
|
||||
// Send the response answer
|
||||
debug("ResponseHeader=%s", szResponseHeader);
|
||||
m_pConnection->Send(szResponseHeader, strlen(szResponseHeader));
|
||||
}
|
||||
|
||||
void WebProcessor::SendBodyResponse(const char* szBody, int iBodyLen, const char* szContentType)
|
||||
{
|
||||
const char* RESPONSE_HEADER =
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n"
|
||||
"Access-Control-Allow-Origin: %s\r\n"
|
||||
"Access-Control-Allow-Credentials: true\r\n"
|
||||
"Access-Control-Max-Age: 86400\r\n"
|
||||
"Access-Control-Allow-Headers: Content-Type, Authorization\r\n"
|
||||
"Content-Length: %i\r\n"
|
||||
"%s" // Content-Type: xxx
|
||||
"%s" // Content-Encoding: gzip
|
||||
"Server: nzbget-%s\r\n"
|
||||
"\r\n";
|
||||
|
||||
#ifndef DISABLE_GZIP
|
||||
char *szGBuf = NULL;
|
||||
bool bGZip = m_bGZip && iBodyLen > MAX_UNCOMPRESSED_SIZE;
|
||||
if (bGZip)
|
||||
{
|
||||
unsigned int iOutLen = ZLib::GZipLen(iBodyLen);
|
||||
szGBuf = (char*)malloc(iOutLen);
|
||||
int iGZippedLen = ZLib::GZip(szBody, iBodyLen, szGBuf, iOutLen);
|
||||
if (iGZippedLen > 0 && iGZippedLen < iBodyLen)
|
||||
{
|
||||
szBody = szGBuf;
|
||||
iBodyLen = iGZippedLen;
|
||||
}
|
||||
else
|
||||
{
|
||||
free(szGBuf);
|
||||
szGBuf = NULL;
|
||||
bGZip = false;
|
||||
}
|
||||
}
|
||||
#else
|
||||
bool bGZip = false;
|
||||
#endif
|
||||
|
||||
char szContentTypeHeader[1024];
|
||||
if (szContentType)
|
||||
{
|
||||
snprintf(szContentTypeHeader, 1024, "Content-Type: %s\r\n", szContentType);
|
||||
}
|
||||
else
|
||||
{
|
||||
szContentTypeHeader[0] = '\0';
|
||||
}
|
||||
|
||||
char szResponseHeader[1024];
|
||||
snprintf(szResponseHeader, 1024, RESPONSE_HEADER,
|
||||
m_szOrigin ? m_szOrigin : "",
|
||||
iBodyLen, szContentTypeHeader,
|
||||
bGZip ? "Content-Encoding: gzip\r\n" : "",
|
||||
Util::VersionRevision());
|
||||
|
||||
// Send the request answer
|
||||
m_pConnection->Send(szResponseHeader, strlen(szResponseHeader));
|
||||
m_pConnection->Send(szBody, iBodyLen);
|
||||
|
||||
#ifndef DISABLE_GZIP
|
||||
free(szGBuf);
|
||||
#endif
|
||||
}
|
||||
|
||||
void WebProcessor::SendFileResponse(const char* szFilename)
|
||||
{
|
||||
debug("serving file: %s", szFilename);
|
||||
|
||||
char *szBody;
|
||||
int iBodyLen;
|
||||
if (!Util::LoadFileIntoBuffer(szFilename, &szBody, &iBodyLen))
|
||||
{
|
||||
SendErrorResponse(ERR_HTTP_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
// "LoadFileIntoBuffer" adds a trailing NULL, which we don't need here
|
||||
iBodyLen--;
|
||||
|
||||
SendBodyResponse(szBody, iBodyLen, DetectContentType(szFilename));
|
||||
|
||||
free(szBody);
|
||||
}
|
||||
|
||||
const char* WebProcessor::DetectContentType(const char* szFilename)
|
||||
{
|
||||
if (const char *szExt = strrchr(szFilename, '.'))
|
||||
{
|
||||
if (!strcasecmp(szExt, ".css"))
|
||||
{
|
||||
return "text/css";
|
||||
}
|
||||
else if (!strcasecmp(szExt, ".html"))
|
||||
{
|
||||
return "text/html";
|
||||
}
|
||||
else if (!strcasecmp(szExt, ".js"))
|
||||
{
|
||||
return "application/javascript";
|
||||
}
|
||||
else if (!strcasecmp(szExt, ".png"))
|
||||
{
|
||||
return "image/png";
|
||||
}
|
||||
else if (!strcasecmp(szExt, ".jpeg"))
|
||||
{
|
||||
return "image/jpeg";
|
||||
}
|
||||
else if (!strcasecmp(szExt, ".gif"))
|
||||
{
|
||||
return "image/gif";
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
68
WebServer.h
Normal file
68
WebServer.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2012 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, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef WEBSERVER_H
|
||||
#define WEBSERVER_H
|
||||
|
||||
#include "Connection.h"
|
||||
|
||||
class WebProcessor
|
||||
{
|
||||
public:
|
||||
enum EHttpMethod
|
||||
{
|
||||
hmPost,
|
||||
hmGet,
|
||||
hmOptions
|
||||
};
|
||||
|
||||
private:
|
||||
Connection* m_pConnection;
|
||||
char* m_szRequest;
|
||||
char* m_szUrl;
|
||||
EHttpMethod m_eHttpMethod;
|
||||
bool m_bGZip;
|
||||
char* m_szOrigin;
|
||||
|
||||
void Dispatch();
|
||||
void SendAuthResponse();
|
||||
void SendOptionsResponse();
|
||||
void SendErrorResponse(const char* szErrCode);
|
||||
void SendFileResponse(const char* szFilename);
|
||||
void SendBodyResponse(const char* szBody, int iBodyLen, const char* szContentType);
|
||||
void SendRedirectResponse(const char* szURL);
|
||||
const char* DetectContentType(const char* szFilename);
|
||||
bool IsAuthorizedIP(const char* szRemoteAddr);
|
||||
|
||||
public:
|
||||
WebProcessor();
|
||||
~WebProcessor();
|
||||
void Execute();
|
||||
void SetConnection(Connection* pConnection) { m_pConnection = pConnection; }
|
||||
void SetUrl(const char* szUrl);
|
||||
void SetHttpMethod(EHttpMethod eHttpMethod) { m_eHttpMethod = eHttpMethod; }
|
||||
};
|
||||
|
||||
#endif
|
||||
2514
XmlRpc.cpp
2514
XmlRpc.cpp
File diff suppressed because it is too large
Load Diff
139
XmlRpc.h
139
XmlRpc.h
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2013 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,7 +15,7 @@
|
||||
*
|
||||
* 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.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -26,22 +26,8 @@
|
||||
#ifndef XMLRPC_H
|
||||
#define XMLRPC_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "Connection.h"
|
||||
|
||||
class StringBuilder
|
||||
{
|
||||
private:
|
||||
char* m_szBuffer;
|
||||
int m_iBufferSize;
|
||||
int m_iUsedSize;
|
||||
public:
|
||||
StringBuilder();
|
||||
~StringBuilder();
|
||||
void Append(const char* szStr);
|
||||
const char* GetBuffer() { return m_szBuffer; }
|
||||
};
|
||||
#include "Util.h"
|
||||
|
||||
class XmlCommand;
|
||||
|
||||
@@ -52,7 +38,8 @@ public:
|
||||
{
|
||||
rpUndefined,
|
||||
rpXmlRpc,
|
||||
rpJsonRpc
|
||||
rpJsonRpc,
|
||||
rpJsonPRpc
|
||||
};
|
||||
|
||||
enum EHttpMethod
|
||||
@@ -62,27 +49,28 @@ public:
|
||||
};
|
||||
|
||||
private:
|
||||
Connection* m_pConnection;
|
||||
const char* m_szClientIP;
|
||||
char* m_szRequest;
|
||||
const char* m_szContentType;
|
||||
ERpcProtocol m_eProtocol;
|
||||
EHttpMethod m_eHttpMethod;
|
||||
char* m_szUrl;
|
||||
StringBuilder m_cResponse;
|
||||
|
||||
void Dispatch();
|
||||
void SendResponse(const char* szResponse, bool bFault);
|
||||
XmlCommand* CreateCommand(const char* szMethodName);
|
||||
void MutliCall();
|
||||
void BuildResponse(const char* szResponse, const char* szCallbackFunc, bool bFault);
|
||||
|
||||
public:
|
||||
XmlRpcProcessor();
|
||||
~XmlRpcProcessor();
|
||||
void Execute();
|
||||
void SetConnection(Connection* pConnection) { m_pConnection = pConnection; }
|
||||
void SetProtocol(ERpcProtocol eProtocol) { m_eProtocol = eProtocol; }
|
||||
void SetHttpMethod(EHttpMethod eHttpMethod) { m_eHttpMethod = eHttpMethod; }
|
||||
void SetUrl(const char* szUrl);
|
||||
void SetClientIP(const char* szClientIP) { m_szClientIP = szClientIP; }
|
||||
void SetRequest(char* szRequest) { m_szRequest = szRequest; }
|
||||
const char* GetResponse() { return m_cResponse.GetBuffer(); }
|
||||
const char* GetContentType() { return m_szContentType; }
|
||||
static bool IsRpcRequest(const char* szUrl);
|
||||
};
|
||||
|
||||
class XmlCommand
|
||||
@@ -90,20 +78,23 @@ class XmlCommand
|
||||
protected:
|
||||
char* m_szRequest;
|
||||
char* m_szRequestPtr;
|
||||
char* m_szCallbackFunc;
|
||||
StringBuilder m_StringBuilder;
|
||||
bool m_bFault;
|
||||
XmlRpcProcessor::ERpcProtocol m_eProtocol;
|
||||
XmlRpcProcessor::EHttpMethod m_eHttpMethod;
|
||||
|
||||
void BuildErrorResponse(int iErrCode, const char* szErrText);
|
||||
void BuildErrorResponse(int iErrCode, const char* szErrText, ...);
|
||||
void BuildBoolResponse(bool bOK);
|
||||
void AppendResponse(const char* szPart);
|
||||
bool IsJson() { return m_eProtocol == XmlRpcProcessor::rpJsonRpc; }
|
||||
bool IsJson();
|
||||
bool CheckSafeMethod();
|
||||
bool NextParamAsInt(int* iValue);
|
||||
bool NextParamAsBool(bool* bValue);
|
||||
bool NextParamAsStr(char** szValueBuf);
|
||||
const char* BoolToStr(bool bValue);
|
||||
char* EncodeStr(const char* szStr);
|
||||
void DecodeStr(char* szStr);
|
||||
|
||||
public:
|
||||
XmlCommand();
|
||||
@@ -114,102 +105,8 @@ public:
|
||||
void SetProtocol(XmlRpcProcessor::ERpcProtocol eProtocol) { m_eProtocol = eProtocol; }
|
||||
void SetHttpMethod(XmlRpcProcessor::EHttpMethod eHttpMethod) { m_eHttpMethod = eHttpMethod; }
|
||||
const char* GetResponse() { return m_StringBuilder.GetBuffer(); }
|
||||
const char* GetCallbackFunc() { return m_szCallbackFunc; }
|
||||
bool GetFault() { return m_bFault; }
|
||||
};
|
||||
|
||||
class ErrorXmlCommand: public XmlCommand
|
||||
{
|
||||
private:
|
||||
int m_iErrCode;
|
||||
const char* m_szErrText;
|
||||
|
||||
public:
|
||||
ErrorXmlCommand(int iErrCode, const char* szErrText);
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class PauseXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class UnPauseXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ShutdownXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class VersionXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class DumpDebugXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class SetDownloadRateXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class StatusXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class LogXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ListFilesXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ListGroupsXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class EditQueueXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class DownloadXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class PostQueueXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class WriteLogXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
330
aclocal.m4
vendored
330
aclocal.m4
vendored
@@ -1,7 +1,7 @@
|
||||
# generated automatically by aclocal 1.10 -*- Autoconf -*-
|
||||
# generated automatically by aclocal 1.9.6 -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
|
||||
# 2005, 2006 Free Software Foundation, Inc.
|
||||
# 2005 Free Software Foundation, Inc.
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
@@ -11,12 +11,165 @@
|
||||
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
# PARTICULAR PURPOSE.
|
||||
|
||||
m4_if(m4_PACKAGE_VERSION, [2.61],,
|
||||
[m4_fatal([this file was generated for autoconf 2.61.
|
||||
You have another version of autoconf. If you want to use that,
|
||||
you should regenerate the build system entirely.], [63])])
|
||||
# pkg.m4 - Macros to locate and utilise pkg-config. -*- Autoconf -*-
|
||||
#
|
||||
# Copyright © 2004 Scott James Remnant <scott@netsplit.com>.
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful, but
|
||||
# WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
# General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
#
|
||||
# As a special exception to the GNU General Public License, if you
|
||||
# distribute this file as part of a program that contains a
|
||||
# configuration script generated by Autoconf, you may include it under
|
||||
# the same distribution terms that you use for the rest of that program.
|
||||
|
||||
# Copyright (C) 2002, 2003, 2005, 2006 Free Software Foundation, Inc.
|
||||
# PKG_PROG_PKG_CONFIG([MIN-VERSION])
|
||||
# ----------------------------------
|
||||
AC_DEFUN([PKG_PROG_PKG_CONFIG],
|
||||
[m4_pattern_forbid([^_?PKG_[A-Z_]+$])
|
||||
m4_pattern_allow([^PKG_CONFIG(_PATH)?$])
|
||||
AC_ARG_VAR([PKG_CONFIG], [path to pkg-config utility])dnl
|
||||
if test "x$ac_cv_env_PKG_CONFIG_set" != "xset"; then
|
||||
AC_PATH_TOOL([PKG_CONFIG], [pkg-config])
|
||||
fi
|
||||
if test -n "$PKG_CONFIG"; then
|
||||
_pkg_min_version=m4_default([$1], [0.9.0])
|
||||
AC_MSG_CHECKING([pkg-config is at least version $_pkg_min_version])
|
||||
if $PKG_CONFIG --atleast-pkgconfig-version $_pkg_min_version; then
|
||||
AC_MSG_RESULT([yes])
|
||||
else
|
||||
AC_MSG_RESULT([no])
|
||||
PKG_CONFIG=""
|
||||
fi
|
||||
|
||||
fi[]dnl
|
||||
])# PKG_PROG_PKG_CONFIG
|
||||
|
||||
# PKG_CHECK_EXISTS(MODULES, [ACTION-IF-FOUND], [ACTION-IF-NOT-FOUND])
|
||||
#
|
||||
# Check to see whether a particular set of modules exists. Similar
|
||||
# to PKG_CHECK_MODULES(), but does not set variables or print errors.
|
||||
#
|
||||
#
|
||||
# Similar to PKG_CHECK_MODULES, make sure that the first instance of
|
||||
# this or PKG_CHECK_MODULES is called, or make sure to call
|
||||
# PKG_CHECK_EXISTS manually
|
||||
# --------------------------------------------------------------
|
||||
AC_DEFUN([PKG_CHECK_EXISTS],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||
if test -n "$PKG_CONFIG" && \
|
||||
AC_RUN_LOG([$PKG_CONFIG --exists --print-errors "$1"]); then
|
||||
m4_ifval([$2], [$2], [:])
|
||||
m4_ifvaln([$3], [else
|
||||
$3])dnl
|
||||
fi])
|
||||
|
||||
|
||||
# _PKG_CONFIG([VARIABLE], [COMMAND], [MODULES])
|
||||
# ---------------------------------------------
|
||||
m4_define([_PKG_CONFIG],
|
||||
[if test -n "$PKG_CONFIG"; then
|
||||
if test -n "$$1"; then
|
||||
pkg_cv_[]$1="$$1"
|
||||
else
|
||||
PKG_CHECK_EXISTS([$3],
|
||||
[pkg_cv_[]$1=`$PKG_CONFIG --[]$2 "$3" 2>/dev/null`],
|
||||
[pkg_failed=yes])
|
||||
fi
|
||||
else
|
||||
pkg_failed=untried
|
||||
fi[]dnl
|
||||
])# _PKG_CONFIG
|
||||
|
||||
# _PKG_SHORT_ERRORS_SUPPORTED
|
||||
# -----------------------------
|
||||
AC_DEFUN([_PKG_SHORT_ERRORS_SUPPORTED],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])
|
||||
if $PKG_CONFIG --atleast-pkgconfig-version 0.20; then
|
||||
_pkg_short_errors_supported=yes
|
||||
else
|
||||
_pkg_short_errors_supported=no
|
||||
fi[]dnl
|
||||
])# _PKG_SHORT_ERRORS_SUPPORTED
|
||||
|
||||
|
||||
# PKG_CHECK_MODULES(VARIABLE-PREFIX, MODULES, [ACTION-IF-FOUND],
|
||||
# [ACTION-IF-NOT-FOUND])
|
||||
#
|
||||
#
|
||||
# Note that if there is a possibility the first call to
|
||||
# PKG_CHECK_MODULES might not happen, you should be sure to include an
|
||||
# explicit call to PKG_PROG_PKG_CONFIG in your configure.ac
|
||||
#
|
||||
#
|
||||
# --------------------------------------------------------------
|
||||
AC_DEFUN([PKG_CHECK_MODULES],
|
||||
[AC_REQUIRE([PKG_PROG_PKG_CONFIG])dnl
|
||||
AC_ARG_VAR([$1][_CFLAGS], [C compiler flags for $1, overriding pkg-config])dnl
|
||||
AC_ARG_VAR([$1][_LIBS], [linker flags for $1, overriding pkg-config])dnl
|
||||
|
||||
pkg_failed=no
|
||||
AC_MSG_CHECKING([for $1])
|
||||
|
||||
_PKG_CONFIG([$1][_CFLAGS], [cflags], [$2])
|
||||
_PKG_CONFIG([$1][_LIBS], [libs], [$2])
|
||||
|
||||
m4_define([_PKG_TEXT], [Alternatively, you may set the environment variables $1[]_CFLAGS
|
||||
and $1[]_LIBS to avoid the need to call pkg-config.
|
||||
See the pkg-config man page for more details.])
|
||||
|
||||
if test $pkg_failed = yes; then
|
||||
_PKG_SHORT_ERRORS_SUPPORTED
|
||||
if test $_pkg_short_errors_supported = yes; then
|
||||
$1[]_PKG_ERRORS=`$PKG_CONFIG --short-errors --errors-to-stdout --print-errors "$2"`
|
||||
else
|
||||
$1[]_PKG_ERRORS=`$PKG_CONFIG --errors-to-stdout --print-errors "$2"`
|
||||
fi
|
||||
# Put the nasty error message in config.log where it belongs
|
||||
echo "$$1[]_PKG_ERRORS" >&AS_MESSAGE_LOG_FD
|
||||
|
||||
ifelse([$4], , [AC_MSG_ERROR(dnl
|
||||
[Package requirements ($2) were not met:
|
||||
|
||||
$$1_PKG_ERRORS
|
||||
|
||||
Consider adjusting the PKG_CONFIG_PATH environment variable if you
|
||||
installed software in a non-standard prefix.
|
||||
|
||||
_PKG_TEXT
|
||||
])],
|
||||
[AC_MSG_RESULT([no])
|
||||
$4])
|
||||
elif test $pkg_failed = untried; then
|
||||
ifelse([$4], , [AC_MSG_FAILURE(dnl
|
||||
[The pkg-config script could not be found or is too old. Make sure it
|
||||
is in your PATH or set the PKG_CONFIG environment variable to the full
|
||||
path to pkg-config.
|
||||
|
||||
_PKG_TEXT
|
||||
|
||||
To get pkg-config, see <http://pkg-config.freedesktop.org/>.])],
|
||||
[$4])
|
||||
else
|
||||
$1[]_CFLAGS=$pkg_cv_[]$1[]_CFLAGS
|
||||
$1[]_LIBS=$pkg_cv_[]$1[]_LIBS
|
||||
AC_MSG_RESULT([yes])
|
||||
ifelse([$3], , :, [$3])
|
||||
fi[]dnl
|
||||
])# PKG_CHECK_MODULES
|
||||
|
||||
# Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
@@ -26,29 +179,14 @@ you should regenerate the build system entirely.], [63])])
|
||||
# ----------------------------
|
||||
# Automake X.Y traces this macro to ensure aclocal.m4 has been
|
||||
# generated from the m4 files accompanying Automake X.Y.
|
||||
# (This private macro should not be called outside this file.)
|
||||
AC_DEFUN([AM_AUTOMAKE_VERSION],
|
||||
[am__api_version='1.10'
|
||||
dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
|
||||
dnl require some minimum version. Point them to the right macro.
|
||||
m4_if([$1], [1.10], [],
|
||||
[AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
|
||||
])
|
||||
|
||||
# _AM_AUTOCONF_VERSION(VERSION)
|
||||
# -----------------------------
|
||||
# aclocal traces this macro to find the Autoconf version.
|
||||
# This is a private macro too. Using m4_define simplifies
|
||||
# the logic in aclocal, which can simply ignore this definition.
|
||||
m4_define([_AM_AUTOCONF_VERSION], [])
|
||||
AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version="1.9"])
|
||||
|
||||
# AM_SET_CURRENT_AUTOMAKE_VERSION
|
||||
# -------------------------------
|
||||
# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they can be traced.
|
||||
# Call AM_AUTOMAKE_VERSION so it can be traced.
|
||||
# This function is AC_REQUIREd by AC_INIT_AUTOMAKE.
|
||||
AC_DEFUN([AM_SET_CURRENT_AUTOMAKE_VERSION],
|
||||
[AM_AUTOMAKE_VERSION([1.10])dnl
|
||||
_AM_AUTOCONF_VERSION(m4_PACKAGE_VERSION)])
|
||||
[AM_AUTOMAKE_VERSION([1.9.6])])
|
||||
|
||||
# AM_AUX_DIR_EXPAND -*- Autoconf -*-
|
||||
|
||||
@@ -105,14 +243,14 @@ am_aux_dir=`cd $ac_aux_dir && pwd`
|
||||
|
||||
# AM_CONDITIONAL -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005, 2006
|
||||
# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005
|
||||
# Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
# serial 8
|
||||
# serial 7
|
||||
|
||||
# AM_CONDITIONAL(NAME, SHELL-CONDITION)
|
||||
# -------------------------------------
|
||||
@@ -121,10 +259,8 @@ AC_DEFUN([AM_CONDITIONAL],
|
||||
[AC_PREREQ(2.52)dnl
|
||||
ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])],
|
||||
[$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
|
||||
AC_SUBST([$1_TRUE])dnl
|
||||
AC_SUBST([$1_FALSE])dnl
|
||||
_AM_SUBST_NOTMAKE([$1_TRUE])dnl
|
||||
_AM_SUBST_NOTMAKE([$1_FALSE])dnl
|
||||
AC_SUBST([$1_TRUE])
|
||||
AC_SUBST([$1_FALSE])
|
||||
if $2; then
|
||||
$1_TRUE=
|
||||
$1_FALSE='#'
|
||||
@@ -138,14 +274,15 @@ AC_CONFIG_COMMANDS_PRE(
|
||||
Usually this means the macro was only invoked conditionally.]])
|
||||
fi])])
|
||||
|
||||
# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
|
||||
|
||||
# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005
|
||||
# Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
# serial 9
|
||||
# serial 8
|
||||
|
||||
# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be
|
||||
# written in clear, in which case automake, when reading aclocal.m4,
|
||||
@@ -173,7 +310,6 @@ AC_REQUIRE([AM_DEP_TRACK])dnl
|
||||
ifelse([$1], CC, [depcc="$CC" am_compiler_list=],
|
||||
[$1], CXX, [depcc="$CXX" am_compiler_list=],
|
||||
[$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
|
||||
[$1], UPC, [depcc="$UPC" am_compiler_list=],
|
||||
[$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'],
|
||||
[depcc="$$1" am_compiler_list=])
|
||||
|
||||
@@ -239,7 +375,6 @@ AC_CACHE_CHECK([dependency style of $depcc],
|
||||
depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
|
||||
$SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \
|
||||
>/dev/null 2>conftest.err &&
|
||||
grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
|
||||
grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
|
||||
grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 &&
|
||||
${MAKE-make} -s -f confmf > /dev/null 2>&1; then
|
||||
@@ -292,8 +427,7 @@ if test "x$enable_dependency_tracking" != xno; then
|
||||
AMDEPBACKSLASH='\'
|
||||
fi
|
||||
AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
|
||||
AC_SUBST([AMDEPBACKSLASH])dnl
|
||||
_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl
|
||||
AC_SUBST([AMDEPBACKSLASH])
|
||||
])
|
||||
|
||||
# Generate code to set up dependency tracking. -*- Autoconf -*-
|
||||
@@ -318,9 +452,8 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
|
||||
# some people rename them; so instead we look at the file content.
|
||||
# Grep'ing the first line is not enough: some people post-process
|
||||
# each Makefile.in and add a new line on top of each file to say so.
|
||||
# Grep'ing the whole file is not good either: AIX grep has a line
|
||||
# limit of 2048, but all sed's we know have understand at least 4000.
|
||||
if sed 10q "$mf" | grep '^#.*generated by automake' > /dev/null 2>&1; then
|
||||
# So let's grep whole file.
|
||||
if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then
|
||||
dirpart=`AS_DIRNAME("$mf")`
|
||||
else
|
||||
continue
|
||||
@@ -367,8 +500,8 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
|
||||
|
||||
# Do all the work for Automake. -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
|
||||
# 2005, 2006 Free Software Foundation, Inc.
|
||||
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
|
||||
# Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
@@ -391,20 +524,16 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
|
||||
# arguments mandatory, and then we can depend on a new Autoconf
|
||||
# release and drop the old call support.
|
||||
AC_DEFUN([AM_INIT_AUTOMAKE],
|
||||
[AC_PREREQ([2.60])dnl
|
||||
[AC_PREREQ([2.58])dnl
|
||||
dnl Autoconf wants to disallow AM_ names. We explicitly allow
|
||||
dnl the ones we care about.
|
||||
m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
|
||||
AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
|
||||
AC_REQUIRE([AC_PROG_INSTALL])dnl
|
||||
if test "`cd $srcdir && pwd`" != "`pwd`"; then
|
||||
# Use -I$(srcdir) only when $(srcdir) != ., so that make's output
|
||||
# is not polluted with repeated "-I."
|
||||
AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
|
||||
# test to see if srcdir already configured
|
||||
if test -f $srcdir/config.status; then
|
||||
AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
|
||||
fi
|
||||
# test to see if srcdir already configured
|
||||
if test "`cd $srcdir && pwd`" != "`pwd`" &&
|
||||
test -f $srcdir/config.status; then
|
||||
AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
|
||||
fi
|
||||
|
||||
# test whether we have cygpath
|
||||
@@ -424,9 +553,6 @@ m4_ifval([$2],
|
||||
AC_SUBST([PACKAGE], [$1])dnl
|
||||
AC_SUBST([VERSION], [$2])],
|
||||
[_AM_SET_OPTIONS([$1])dnl
|
||||
dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
|
||||
m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,,
|
||||
[m4_fatal([AC_INIT should be called with package and version arguments])])dnl
|
||||
AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
|
||||
AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
|
||||
|
||||
@@ -462,10 +588,6 @@ AC_PROVIDE_IFELSE([AC_PROG_CXX],
|
||||
[_AM_DEPENDENCIES(CXX)],
|
||||
[define([AC_PROG_CXX],
|
||||
defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl
|
||||
AC_PROVIDE_IFELSE([AC_PROG_OBJC],
|
||||
[_AM_DEPENDENCIES(OBJC)],
|
||||
[define([AC_PROG_OBJC],
|
||||
defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl
|
||||
])
|
||||
])
|
||||
|
||||
@@ -501,7 +623,7 @@ echo "timestamp for $1" >`AS_DIRNAME([$1])`/stamp-h[]$_am_stamp_count])
|
||||
# Define $install_sh.
|
||||
AC_DEFUN([AM_PROG_INSTALL_SH],
|
||||
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
|
||||
install_sh=${install_sh-"\$(SHELL) $am_aux_dir/install-sh"}
|
||||
install_sh=${install_sh-"$am_aux_dir/install-sh"}
|
||||
AC_SUBST(install_sh)])
|
||||
|
||||
# Copyright (C) 2003, 2005 Free Software Foundation, Inc.
|
||||
@@ -579,14 +701,14 @@ rm -f confinc confmf
|
||||
|
||||
# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005
|
||||
# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2005
|
||||
# Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
# serial 5
|
||||
# serial 4
|
||||
|
||||
# AM_MISSING_PROG(NAME, PROGRAM)
|
||||
# ------------------------------
|
||||
@@ -602,7 +724,6 @@ AC_SUBST($1)])
|
||||
# If it does, set am_missing_run to use it, otherwise, to nothing.
|
||||
AC_DEFUN([AM_MISSING_HAS_RUN],
|
||||
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
|
||||
AC_REQUIRE_AUX_FILE([missing])dnl
|
||||
test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing"
|
||||
# Use eval to expand $SHELL
|
||||
if eval "$MISSING --run true"; then
|
||||
@@ -613,7 +734,7 @@ else
|
||||
fi
|
||||
])
|
||||
|
||||
# Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
|
||||
# Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
@@ -621,23 +742,60 @@ fi
|
||||
|
||||
# AM_PROG_MKDIR_P
|
||||
# ---------------
|
||||
# Check for `mkdir -p'.
|
||||
# Check whether `mkdir -p' is supported, fallback to mkinstalldirs otherwise.
|
||||
#
|
||||
# Automake 1.8 used `mkdir -m 0755 -p --' to ensure that directories
|
||||
# created by `make install' are always world readable, even if the
|
||||
# installer happens to have an overly restrictive umask (e.g. 077).
|
||||
# This was a mistake. There are at least two reasons why we must not
|
||||
# use `-m 0755':
|
||||
# - it causes special bits like SGID to be ignored,
|
||||
# - it may be too restrictive (some setups expect 775 directories).
|
||||
#
|
||||
# Do not use -m 0755 and let people choose whatever they expect by
|
||||
# setting umask.
|
||||
#
|
||||
# We cannot accept any implementation of `mkdir' that recognizes `-p'.
|
||||
# Some implementations (such as Solaris 8's) are not thread-safe: if a
|
||||
# parallel make tries to run `mkdir -p a/b' and `mkdir -p a/c'
|
||||
# concurrently, both version can detect that a/ is missing, but only
|
||||
# one can create it and the other will error out. Consequently we
|
||||
# restrict ourselves to GNU make (using the --version option ensures
|
||||
# this.)
|
||||
AC_DEFUN([AM_PROG_MKDIR_P],
|
||||
[AC_PREREQ([2.60])dnl
|
||||
AC_REQUIRE([AC_PROG_MKDIR_P])dnl
|
||||
dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P,
|
||||
dnl while keeping a definition of mkdir_p for backward compatibility.
|
||||
dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile.
|
||||
dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of
|
||||
dnl Makefile.ins that do not define MKDIR_P, so we do our own
|
||||
dnl adjustment using top_builddir (which is defined more often than
|
||||
dnl MKDIR_P).
|
||||
AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl
|
||||
case $mkdir_p in
|
||||
[[\\/$]]* | ?:[[\\/]]*) ;;
|
||||
*/*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
|
||||
esac
|
||||
])
|
||||
[if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then
|
||||
# We used to keeping the `.' as first argument, in order to
|
||||
# allow $(mkdir_p) to be used without argument. As in
|
||||
# $(mkdir_p) $(somedir)
|
||||
# where $(somedir) is conditionally defined. However this is wrong
|
||||
# for two reasons:
|
||||
# 1. if the package is installed by a user who cannot write `.'
|
||||
# make install will fail,
|
||||
# 2. the above comment should most certainly read
|
||||
# $(mkdir_p) $(DESTDIR)$(somedir)
|
||||
# so it does not work when $(somedir) is undefined and
|
||||
# $(DESTDIR) is not.
|
||||
# To support the latter case, we have to write
|
||||
# test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir),
|
||||
# so the `.' trick is pointless.
|
||||
mkdir_p='mkdir -p --'
|
||||
else
|
||||
# On NextStep and OpenStep, the `mkdir' command does not
|
||||
# recognize any option. It will interpret all options as
|
||||
# directories to create, and then abort because `.' already
|
||||
# exists.
|
||||
for d in ./-p ./--version;
|
||||
do
|
||||
test -d $d && rmdir $d
|
||||
done
|
||||
# $(mkinstalldirs) is defined by Automake if mkinstalldirs exists.
|
||||
if test -f "$ac_aux_dir/mkinstalldirs"; then
|
||||
mkdir_p='$(mkinstalldirs)'
|
||||
else
|
||||
mkdir_p='$(install_sh) -d'
|
||||
fi
|
||||
fi
|
||||
AC_SUBST([mkdir_p])])
|
||||
|
||||
# Helper functions for option handling. -*- Autoconf -*-
|
||||
|
||||
@@ -749,21 +907,9 @@ dnl Don't test for $cross_compiling = yes, because it might be `maybe'.
|
||||
if test "$cross_compiling" != no; then
|
||||
AC_CHECK_TOOL([STRIP], [strip], :)
|
||||
fi
|
||||
INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
|
||||
INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s"
|
||||
AC_SUBST([INSTALL_STRIP_PROGRAM])])
|
||||
|
||||
# Copyright (C) 2006 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
# _AM_SUBST_NOTMAKE(VARIABLE)
|
||||
# ---------------------------
|
||||
# Prevent Automake from outputing VARIABLE = @VARIABLE@ in Makefile.in.
|
||||
# This macro is traced by Automake.
|
||||
AC_DEFUN([_AM_SUBST_NOTMAKE])
|
||||
|
||||
# Check how to create a tarball. -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 2004, 2005 Free Software Foundation, Inc.
|
||||
|
||||
52
config.h.in
52
config.h.in
@@ -6,13 +6,22 @@
|
||||
/* Define to 1 to not use curses */
|
||||
#undef DISABLE_CURSES
|
||||
|
||||
/* Define to 1 to disable gzip-support */
|
||||
#undef DISABLE_GZIP
|
||||
|
||||
/* Define to 1 to disable smart par-verification and restoration */
|
||||
#undef DISABLE_PARCHECK
|
||||
|
||||
/* Define to 1 to not use TLS/SSL */
|
||||
#undef DISABLE_TLS
|
||||
|
||||
/* 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
|
||||
|
||||
@@ -22,6 +31,15 @@
|
||||
/* Define to 1 if you have the <curses.h> header file. */
|
||||
#undef HAVE_CURSES_H
|
||||
|
||||
/* 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
|
||||
|
||||
@@ -34,6 +52,9 @@
|
||||
/* 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 you have the <memory.h> header file. */
|
||||
#undef HAVE_MEMORY_H
|
||||
|
||||
@@ -43,8 +64,20 @@
|
||||
/* Define to 1 if you have the <ncurses/ncurses.h> header file. */
|
||||
#undef HAVE_NCURSES_NCURSES_H
|
||||
|
||||
/* Define to 1 if stat64 is supported */
|
||||
#undef HAVE_STAT64
|
||||
/* Define to 1 to use OpenSSL library for TLS/SSL-support. */
|
||||
#undef HAVE_OPENSSL
|
||||
|
||||
/* Define to 1 if libpar2 has recent bugfixes-patch (version 2) */
|
||||
#undef HAVE_PAR2_BUGFIXES_V2
|
||||
|
||||
/* Define to 1 if libpar2 supports cancelling (needs a special patch) */
|
||||
#undef HAVE_PAR2_CANCEL
|
||||
|
||||
/* Define to 1 if you have the <regex.h> header file. */
|
||||
#undef HAVE_REGEX_H
|
||||
|
||||
/* Define to 1 if spinlocks are supported */
|
||||
#undef HAVE_SPINLOCK
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#undef HAVE_STDINT_H
|
||||
@@ -58,6 +91,9 @@
|
||||
/* 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
|
||||
|
||||
@@ -88,8 +124,20 @@
|
||||
/* 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
|
||||
|
||||
/* Number of bits in a file offset, on hosts where this is settable. */
|
||||
#undef _FILE_OFFSET_BITS
|
||||
|
||||
/* Define for large files, on AIX-style hosts. */
|
||||
#undef _LARGE_FILES
|
||||
|
||||
581
configure.ac
581
configure.ac
@@ -2,41 +2,18 @@
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ(2.59)
|
||||
AC_INIT(nzbget, 0.4.0, hugbug@users.sourceforge.net)
|
||||
AM_INIT_AUTOMAKE(nzbget, 0.4.0)
|
||||
AC_INIT(nzbget, 12.0, hugbug@users.sourceforge.net)
|
||||
AC_CANONICAL_SYSTEM
|
||||
AM_INIT_AUTOMAKE(nzbget, 12.0)
|
||||
AC_CONFIG_SRCDIR([nzbget.cpp])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
|
||||
|
||||
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
|
||||
|
||||
|
||||
dnl
|
||||
dnl Set default library path, if not specified in environment variable "LIBPREF".
|
||||
dnl
|
||||
if test "$LIBPREF" = ""; then
|
||||
LIBPREF="$LIBPREF1"
|
||||
fi
|
||||
if test "$CFLAGS" = ""; then
|
||||
CFLAGS="$CFLAGS1"
|
||||
fi
|
||||
if test "$CPPFLAGS" = ""; then
|
||||
CPPFLAGS="$CPPFLAGS1"
|
||||
LIBPREF="/usr"
|
||||
fi
|
||||
|
||||
|
||||
@@ -44,30 +21,23 @@ 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 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)
|
||||
AC_CHECK_HEADERS(regex.h)
|
||||
|
||||
|
||||
dnl
|
||||
dnl Check for libs
|
||||
@@ -75,7 +45,6 @@ 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])
|
||||
|
||||
|
||||
@@ -83,16 +52,13 @@ 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 stat64
|
||||
dnl use 64-Bits for file sizes
|
||||
dnl
|
||||
AC_CHECK_FUNC(stat64,
|
||||
[AC_DEFINE([HAVE_STAT64], 1, [Define to 1 if stat64 is supported])],
|
||||
[AC_LIBOBJ(stat64)])
|
||||
AC_SYS_LARGEFILE
|
||||
|
||||
|
||||
dnl
|
||||
@@ -103,6 +69,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
|
||||
@@ -116,82 +83,125 @@ 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 cCheck if spinlocks are available
|
||||
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_CHECK_FUNC(pthread_spin_init,
|
||||
[AC_DEFINE([HAVE_SPINLOCK], 1, [Define to 1 if spinlocks are supported])]
|
||||
AC_SEARCH_LIBS([pthread_spin_init], [pthread]),)
|
||||
|
||||
|
||||
dnl
|
||||
dnl Determine what socket length (socklen_t) data type is
|
||||
dnl
|
||||
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 checks for libxml2 includes and libraries.
|
||||
dnl
|
||||
INCVAL="${LIBPREF}/include/libxml2"
|
||||
LIBVAL="${LIBPREF}/lib"
|
||||
dnl
|
||||
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."))
|
||||
[AS_HELP_STRING([--with-libxml2-includes=DIR], [libxml2 include directory])],
|
||||
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
|
||||
[INCVAL="yes"],
|
||||
[INCVAL="no"])
|
||||
AC_ARG_WITH(libxml2_libraries,
|
||||
[ --with-libxml2-libraries=DIR libxml2 library directory],
|
||||
[LIBVAL="$withval"])
|
||||
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
|
||||
[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"])
|
||||
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 in $LIBVAL."))
|
||||
AC_MSG_ERROR("libxml2 library not found"))
|
||||
|
||||
|
||||
dnl
|
||||
@@ -199,20 +209,19 @@ 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],
|
||||
[AS_HELP_STRING([--with-libcurses-includes=DIR], [libcurses include directory])],
|
||||
[INCVAL="$withval"])
|
||||
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
|
||||
CFLAGS="${CFLAGS} -I${INCVAL}"
|
||||
AC_ARG_WITH(libcurses_libraries,
|
||||
[ --with-libcurses-libraries=DIR libcurses library directory],
|
||||
[AS_HELP_STRING([--with-libcurses-libraries=DIR], [libcurses library directory])],
|
||||
[LIBVAL="$withval"])
|
||||
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
|
||||
|
||||
@@ -233,7 +242,7 @@ 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]))
|
||||
@@ -243,11 +252,11 @@ fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl Use lib2par for par-checking. Deafult: no
|
||||
dnl Use libpar2 for par-checking. Deafult: no
|
||||
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 (removes dependency from libpar2- and libsigc-libraries)])],
|
||||
[ ENABLEPARCHECK=$enableval ],
|
||||
[ ENABLEPARCHECK=yes] )
|
||||
AC_MSG_RESULT($ENABLEPARCHECK)
|
||||
@@ -256,23 +265,26 @@ if test "$ENABLEPARCHECK" = "yes"; then
|
||||
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"])
|
||||
|
||||
[AS_HELP_STRING([--with-libsigc-includes=DIR], [libsigc++-2.0 include directory])],
|
||||
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
|
||||
[INCVAL="yes"],
|
||||
[INCVAL="no"])
|
||||
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++)
|
||||
[AS_HELP_STRING([--with-libsigc-libraries=DIR], [libsigc++-2.0 library directory])],
|
||||
[LDFLAGS="${LDFLAGS} -L${withval}"]
|
||||
[CPPFLAGS="${CPPFLAGS} -I${withval}/sigc++-2.0/include"]
|
||||
[LIBVAL="yes"],
|
||||
[LIBVAL="no"])
|
||||
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
|
||||
PKG_CHECK_MODULES(libsigc, sigc++-2.0,
|
||||
[LIBS="${LIBS} $libsigc_LIBS"]
|
||||
[CPPFLAGS="${CPPFLAGS} $libsigc_CFLAGS"])
|
||||
fi
|
||||
|
||||
AC_CHECK_HEADER(sigc++/type_traits.h,,
|
||||
AC_MSG_ERROR("libsigc++-2.0 header files were not found in $INCVAL."))
|
||||
AC_LANG_POP(C++)
|
||||
AC_MSG_ERROR("libsigc++-2.0 header files not found"))
|
||||
|
||||
dnl
|
||||
dnl checks for libpar2 includes and libraries.
|
||||
@@ -280,53 +292,277 @@ if test "$ENABLEPARCHECK" = "yes"; then
|
||||
INCVAL="${LIBPREF}/include"
|
||||
LIBVAL="${LIBPREF}/lib"
|
||||
AC_ARG_WITH(libpar2_includes,
|
||||
[ --with-libpar2-includes=DIR libpar2 include directory],
|
||||
[AS_HELP_STRING([--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_MSG_ERROR("libpar2 header files not found"))
|
||||
|
||||
AC_ARG_WITH(libpar2_libraries,
|
||||
[ --with-libpar2-libraries=DIR libpar2 library directory],
|
||||
[AS_HELP_STRING([--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
|
||||
AC_SEARCH_LIBS([_ZN12Par2RepairerC1Ev], [par2], ,
|
||||
AC_MSG_ERROR("libpar2 library not found"))
|
||||
|
||||
dnl
|
||||
dnl check if libpar2 library is linkable
|
||||
dnl
|
||||
AC_MSG_CHECKING(for libpar2 linking)
|
||||
AC_TRY_LINK(
|
||||
[#include <libpar2/par2cmdline.h>]
|
||||
[#include <libpar2/par2repairer.h>]
|
||||
[ class Repairer : public Par2Repairer { }; ],
|
||||
[ Repairer* p = new Repairer(); ],
|
||||
AC_MSG_RESULT([[yes]]),
|
||||
AC_MSG_RESULT([[no]])
|
||||
AC_MSG_ERROR("libpar2 library not found"))
|
||||
|
||||
dnl
|
||||
dnl check if libpar2 has support for cancelling
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether libpar2 supports cancelling)
|
||||
AC_TRY_COMPILE(
|
||||
[#include <libpar2/par2cmdline.h>]
|
||||
[#include <libpar2/par2repairer.h>]
|
||||
[ class Repairer : public Par2Repairer { void test() { cancelled = true; } }; ],
|
||||
[],
|
||||
AC_MSG_RESULT([[yes]])
|
||||
AC_DEFINE([HAVE_PAR2_CANCEL], 1, [Define to 1 if libpar2 supports cancelling (needs a special patch)]),
|
||||
AC_MSG_RESULT([[no]]))
|
||||
|
||||
dnl
|
||||
dnl check if libpar2 has recent bugfixes-patch
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether libpar2 has recent bugfixes-patch (version 2))
|
||||
AC_TRY_COMPILE(
|
||||
[#include <libpar2/par2cmdline.h>]
|
||||
[#include <libpar2/par2repairer.h>]
|
||||
[ class Repairer : public Par2Repairer { void test() { BugfixesPatchVersion2(); } }; ],
|
||||
[],
|
||||
AC_MSG_RESULT([[yes]])
|
||||
PAR2PATCHV2=yes
|
||||
AC_DEFINE([HAVE_PAR2_BUGFIXES_V2], 1, [Define to 1 if libpar2 has recent bugfixes-patch (version 2)]),
|
||||
AC_MSG_RESULT([[no]])
|
||||
PAR2PATCHV2=no)
|
||||
|
||||
if test "$PAR2PATCHV2" = "no" ; then
|
||||
AC_ARG_ENABLE(libpar2-bugfixes-check,
|
||||
[AS_HELP_STRING([--disable-libpar2-bugfixes-check], [do not check if libpar2 has recent bugfixes-patch applied])],
|
||||
[ PAR2PATCHCHECK=$enableval ],
|
||||
[ PAR2PATCHCHECK=yes] )
|
||||
if test "$PAR2PATCHCHECK" = "yes"; then
|
||||
AC_ERROR([Your version of libpar2 doesn't include the recent bugfixes-patch (version 2, updated Dec 3, 2012). Please patch libpar2 with the patches supplied with NZBGet (see README for details). If you cannot patch libpar2, you can use configure parameter --disable-libpar2-bugfixes-check to suppress the check. Please note however that in this case the program may crash during par-check/repair. The patch is highly recommended!])
|
||||
fi
|
||||
fi
|
||||
|
||||
else
|
||||
AC_DEFINE([DISABLE_PARCHECK],1,[Define to 1 to disable smart par-verification and restoration])
|
||||
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=(GnuTLS, OpenSSL)], [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" = "GnuTLS" -o "$TLSLIB" = ""; then
|
||||
INCVAL="${LIBPREF}/include"
|
||||
LIBVAL="${LIBPREF}/lib"
|
||||
AC_ARG_WITH(libgnutls_includes,
|
||||
[AS_HELP_STRING([--with-libgnutls-includes=DIR], [GnuTLS include directory])],
|
||||
[INCVAL="$withval"])
|
||||
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
|
||||
AC_ARG_WITH(libgnutls_libraries,
|
||||
[AS_HELP_STRING([--with-libgnutls-libraries=DIR], [GnuTLS library directory])],
|
||||
[LIBVAL="$withval"])
|
||||
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
|
||||
|
||||
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],
|
||||
AC_SEARCH_LIBS([gcry_control], [gnutls gcrypt],
|
||||
FOUND=yes,
|
||||
FOUND=no),
|
||||
FOUND=no)
|
||||
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
|
||||
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([CRYPTO_set_locking_callback], [crypto],
|
||||
AC_SEARCH_LIBS([SSL_library_init], [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.])
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$TLSLIB" = ""; then
|
||||
if test "$TLSHEADERS" = ""; then
|
||||
AC_MSG_ERROR([Couldn't find neither GnuTLS nor OpenSSL headers (gnutls.h or ssl.h)])
|
||||
else
|
||||
AC_MSG_ERROR([Couldn't find neither GnuTLS nor OpenSSL 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
|
||||
INCVAL="${LIBPREF}/include"
|
||||
LIBVAL="${LIBPREF}/lib"
|
||||
AC_ARG_WITH(zlib_includes,
|
||||
[AS_HELP_STRING([--with-zlib-includes=DIR], [zlib include directory])],
|
||||
[INCVAL="$withval"])
|
||||
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
|
||||
AC_ARG_WITH(zlib_libraries,
|
||||
[AS_HELP_STRING([--with-zlib-libraries=DIR], [zlib library directory])],
|
||||
[LIBVAL="$withval"])
|
||||
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
|
||||
|
||||
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 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
|
||||
|
||||
|
||||
dnl
|
||||
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 Set debug flags for gcc (if gcc is used)
|
||||
dnl
|
||||
if test "$CC" = "gcc"; then
|
||||
CXXFLAGS="-g -Wall"
|
||||
else
|
||||
CXXFLAGS=""
|
||||
fi
|
||||
|
||||
|
||||
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
|
||||
@@ -341,14 +577,39 @@ AC_COMPILE_IFELSE([
|
||||
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
|
||||
fi
|
||||
|
||||
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
|
||||
60
libpar2-0.2-bugfixes.patch
Normal file
60
libpar2-0.2-bugfixes.patch
Normal file
@@ -0,0 +1,60 @@
|
||||
diff -aud -U 5 ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repairer.cpp
|
||||
--- ../libpar2-0.2-original/par2repairer.cpp 2006-01-20 18:25:20.000000000 +0100
|
||||
+++ ../libpar2-0.2/par2repairer.cpp 2012-11-30 14:23:31.000000000 +0100
|
||||
@@ -76,10 +76,11 @@
|
||||
++sf;
|
||||
}
|
||||
|
||||
delete mainpacket;
|
||||
delete creatorpacket;
|
||||
+ delete headers;
|
||||
}
|
||||
|
||||
|
||||
Result Par2Repairer::PreProcess(const CommandLine &commandline)
|
||||
{
|
||||
@@ -1259,11 +1260,11 @@
|
||||
string path;
|
||||
string name;
|
||||
DiskFile::SplitFilename(filename, path, name);
|
||||
|
||||
cout << "Target: \"" << name << "\" - missing." << endl;
|
||||
- sig_done.emit(name, 0, sourcefile->GetVerificationPacket()->BlockCount());
|
||||
+ sig_done.emit(name, 0, sourcefile->GetVerificationPacket() ? sourcefile->GetVerificationPacket()->BlockCount() : 0);
|
||||
}
|
||||
}
|
||||
|
||||
++sf;
|
||||
}
|
||||
@@ -1802,11 +1803,11 @@
|
||||
<< "\" - no data found."
|
||||
<< endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
- sig_done.emit(name,count,sourcefile->GetVerificationPacket()->BlockCount());
|
||||
+ sig_done.emit(name,count, count>0 && sourcefile->GetVerificationPacket() ? sourcefile->GetVerificationPacket()->BlockCount() : 0);
|
||||
sig_progress.emit(1000.0);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Find out how much data we have found
|
||||
diff -aud -U 5 ../libpar2-0.2-original/par2repairer.h ../libpar2-0.2/par2repairer.h
|
||||
--- ../libpar2-0.2-original/par2repairer.h 2006-01-20 00:38:27.000000000 +0100
|
||||
+++ ../libpar2-0.2/par2repairer.h 2012-11-30 14:24:46.000000000 +0100
|
||||
@@ -34,10 +34,15 @@
|
||||
sigc::signal<void, std::string> sig_filename;
|
||||
sigc::signal<void, double> sig_progress;
|
||||
sigc::signal<void, ParHeaders*> sig_headers;
|
||||
sigc::signal<void, std::string, int, int> sig_done;
|
||||
|
||||
+ // This method allows to determine whether libpar2 includes the patches
|
||||
+ // ("libpar2-0.2-bugfixes.patch") submitted to libpar2 project.
|
||||
+ // Use the method in configure scripts for detection.
|
||||
+ void BugfixesPatchVersion2() { }
|
||||
+
|
||||
protected:
|
||||
// Steps in verifying and repairing files:
|
||||
|
||||
// Load packets from the specified file
|
||||
bool LoadPacketsFromFile(string filename);
|
||||
236
libpar2-0.2-cancel.patch
Normal file
236
libpar2-0.2-cancel.patch
Normal file
@@ -0,0 +1,236 @@
|
||||
diff -aud -U 5 ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repairer.cpp
|
||||
--- ../libpar2-0.2-original/par2repairer.cpp 2012-12-03 10:47:04.000000000 +0100
|
||||
+++ ../libpar2-0.2/par2repairer.cpp 2012-12-03 10:48:13.000000000 +0100
|
||||
@@ -50,10 +50,12 @@
|
||||
outputbuffer = 0;
|
||||
|
||||
noiselevel = CommandLine::nlNormal;
|
||||
headers = new ParHeaders;
|
||||
alreadyloaded = false;
|
||||
+
|
||||
+ cancelled = false;
|
||||
}
|
||||
|
||||
Par2Repairer::~Par2Repairer(void)
|
||||
{
|
||||
delete [] (u8*)inputbuffer;
|
||||
@@ -404,10 +406,14 @@
|
||||
{
|
||||
cout << "Loading: " << newfraction/10 << '.' << newfraction%10 << "%\r" << flush;
|
||||
progress = offset;
|
||||
sig_progress.emit(newfraction);
|
||||
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ break;
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
// Attempt to read the next packet header
|
||||
PACKET_HEADER header;
|
||||
@@ -582,10 +588,15 @@
|
||||
if (noiselevel > CommandLine::nlQuiet)
|
||||
cout << "No new packets found" << endl;
|
||||
delete diskfile;
|
||||
}
|
||||
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
return true;
|
||||
}
|
||||
|
||||
// Finish loading a recovery packet
|
||||
bool Par2Repairer::LoadRecoveryPacket(DiskFile *diskfile, u64 offset, PACKET_HEADER &header)
|
||||
@@ -831,26 +842,42 @@
|
||||
|
||||
// Load packets from each file that was found
|
||||
for (list<string>::const_iterator s=files->begin(); s!=files->end(); ++s)
|
||||
{
|
||||
LoadPacketsFromFile(*s);
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ break;
|
||||
+ }
|
||||
}
|
||||
|
||||
delete files;
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ return false;
|
||||
+ }
|
||||
}
|
||||
|
||||
{
|
||||
string wildcard = name.empty() ? "*.PAR2" : name + ".*.PAR2";
|
||||
list<string> *files = DiskFile::FindFiles(path, wildcard);
|
||||
|
||||
// Load packets from each file that was found
|
||||
for (list<string>::const_iterator s=files->begin(); s!=files->end(); ++s)
|
||||
{
|
||||
LoadPacketsFromFile(*s);
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ break;
|
||||
+ }
|
||||
}
|
||||
|
||||
delete files;
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ return false;
|
||||
+ }
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -864,13 +891,22 @@
|
||||
// If the filename contains ".par2" anywhere
|
||||
if (string::npos != filename.find(".par2") ||
|
||||
string::npos != filename.find(".PAR2"))
|
||||
{
|
||||
LoadPacketsFromFile(filename);
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ break;
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
return true;
|
||||
}
|
||||
|
||||
// Check that the packets are consistent and discard any that are not
|
||||
bool Par2Repairer::CheckPacketConsistency(void)
|
||||
@@ -1208,10 +1244,15 @@
|
||||
|
||||
// Start verifying the files
|
||||
sf = sortedfiles.begin();
|
||||
while (sf != sortedfiles.end())
|
||||
{
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
// Do we have a source file
|
||||
Par2RepairerSourceFile *sourcefile = *sf;
|
||||
|
||||
// What filename does the file use
|
||||
string filename = sourcefile->TargetFileName();
|
||||
@@ -1560,10 +1601,14 @@
|
||||
if (oldfraction != newfraction)
|
||||
{
|
||||
cout << "Scanning: \"" << shortname << "\": " << newfraction/10 << '.' << newfraction%10 << "%\r" << flush;
|
||||
sig_progress.emit(newfraction);
|
||||
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ break;
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
// If we fail to find a match, it might be because it was a duplicate of a block
|
||||
// that we have already found.
|
||||
@@ -1649,10 +1694,15 @@
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
// Get the Full and 16k hash values of the file
|
||||
filechecksummer.GetFileHashes(hashfull, hash16k);
|
||||
|
||||
// Did we make any matches at all
|
||||
if (count > 0)
|
||||
@@ -2289,14 +2339,23 @@
|
||||
if (oldfraction != newfraction)
|
||||
{
|
||||
cout << "Repairing: " << newfraction/10 << '.' << newfraction%10 << "%\r" << flush;
|
||||
sig_progress.emit(newfraction);
|
||||
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ break;
|
||||
+ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
++inputblock;
|
||||
++inputindex;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -2346,13 +2405,22 @@
|
||||
if (oldfraction != newfraction)
|
||||
{
|
||||
cout << "Processing: " << newfraction/10 << '.' << newfraction%10 << "%\r" << flush;
|
||||
sig_progress.emit(newfraction);
|
||||
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ break;
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
++copyblock;
|
||||
++inputblock;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2360,10 +2428,15 @@
|
||||
if (lastopenfile != NULL)
|
||||
{
|
||||
lastopenfile->Close();
|
||||
}
|
||||
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
if (noiselevel > CommandLine::nlQuiet)
|
||||
cout << "Writing recovered data\r";
|
||||
|
||||
// For each output block that has been recomputed
|
||||
vector<DataBlock*>::iterator outputblock = outputblocks.begin();
|
||||
diff -aud -U 5 ../libpar2-0.2-with-bugfixes-patch/par2repairer.h ../libpar2-0.2/par2repairer.h
|
||||
--- ../libpar2-0.2-original/par2repairer.h 2012-12-03 10:47:04.000000000 +0100
|
||||
+++ ../libpar2-0.2/par2repairer.h 2012-12-03 10:48:13.000000000 +0100
|
||||
@@ -186,8 +186,9 @@
|
||||
|
||||
u64 progress; // How much data has been processed.
|
||||
u64 totaldata; // Total amount of data to be processed.
|
||||
u64 totalsize; // Total data size
|
||||
|
||||
+ bool cancelled; // repair cancelled
|
||||
};
|
||||
|
||||
#endif // __PAR2REPAIRER_H__
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user