mirror of
https://github.com/nzbget/nzbget.git
synced 2026-01-03 19:47:44 -05:00
Compare commits
1362 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dafb956e6e | ||
|
|
263f669873 | ||
|
|
ceec19c6fd | ||
|
|
a513dc0c01 | ||
|
|
1948dd2420 | ||
|
|
87f9cc68a0 | ||
|
|
d0897c2e09 | ||
|
|
5e392e98b6 | ||
|
|
caf2d919b4 | ||
|
|
111630b6b7 | ||
|
|
5418865a1b | ||
|
|
752d27ee08 | ||
|
|
afa32676ac | ||
|
|
e4d3773c22 | ||
|
|
04558bc25e | ||
|
|
12b6a2602a | ||
|
|
9e493c6c4d | ||
|
|
fdebae5cc2 | ||
|
|
ff8f2472a0 | ||
|
|
f732a0edc1 | ||
|
|
d48a16598f | ||
|
|
fb5a254b83 | ||
|
|
e01f1a37e5 | ||
|
|
4887941170 | ||
|
|
d57c895127 | ||
|
|
7385a1744b | ||
|
|
dc678b37f5 | ||
|
|
cdcbd783cd | ||
|
|
20b43ec736 | ||
|
|
cb41e3314c | ||
|
|
a9edcdf4fd | ||
|
|
f5daf39bb8 | ||
|
|
aeab407dc0 | ||
|
|
d2770d7a33 | ||
|
|
ab86d0efe2 | ||
|
|
5b4d3d8038 | ||
|
|
95fffe619d | ||
|
|
ea95a819c1 | ||
|
|
44197148d0 | ||
|
|
434ded22e2 | ||
|
|
46c8398942 | ||
|
|
1369b23974 | ||
|
|
d21badb6d5 | ||
|
|
4c51d2dc28 | ||
|
|
c0b3058f7c | ||
|
|
e5eb29be05 | ||
|
|
3d3fa4980e | ||
|
|
405ed766f4 | ||
|
|
cc62387292 | ||
|
|
1594345775 | ||
|
|
2d3d5af25e | ||
|
|
c6656cffbf | ||
|
|
e4d62ebbc8 | ||
|
|
af422050b6 | ||
|
|
5f52512a5f | ||
|
|
ca0af70d53 | ||
|
|
6e0a3e0ceb | ||
|
|
24fd4e8c15 | ||
|
|
1e64a7d453 | ||
|
|
8a03c64021 | ||
|
|
f8c1df1856 | ||
|
|
5b460b7512 | ||
|
|
45277c85e0 | ||
|
|
c38069443d | ||
|
|
6da5730356 | ||
|
|
2a9f9f8e98 | ||
|
|
a334a32b35 | ||
|
|
1583e84927 | ||
|
|
8a2fef7c46 | ||
|
|
6c3f2a9871 | ||
|
|
3ac2ccbc80 | ||
|
|
a11d22d3e0 | ||
|
|
d07b9af366 | ||
|
|
1a65409d0e | ||
|
|
941c2efb52 | ||
|
|
e99a0c5975 | ||
|
|
6b8c27fdcc | ||
|
|
820260cb6c | ||
|
|
96c73f05af | ||
|
|
c674405b44 | ||
|
|
6ac82f03d8 | ||
|
|
1a206457a2 | ||
|
|
6fd407b200 | ||
|
|
d8c6be9f52 | ||
|
|
46039698ca | ||
|
|
002547fde5 | ||
|
|
547b430c91 | ||
|
|
860f05eb70 | ||
|
|
4cc2beaaca | ||
|
|
d49ab2a087 | ||
|
|
c208eec5c3 | ||
|
|
c7047b1e33 | ||
|
|
659ed48652 | ||
|
|
a9a73b635c | ||
|
|
fc484ba0dd | ||
|
|
c6ad5523c7 | ||
|
|
0a8edc2388 | ||
|
|
3e5bb54ca4 | ||
|
|
7fc8238b23 | ||
|
|
06454eddcc | ||
|
|
c93c0e9dce | ||
|
|
4ec9f947c6 | ||
|
|
6436c3657d | ||
|
|
8d1ffa4947 | ||
|
|
5d6dab779e | ||
|
|
36d1378881 | ||
|
|
187679443f | ||
|
|
97e2776480 | ||
|
|
d7ab37ad31 | ||
|
|
1f7c15628a | ||
|
|
15e8a853fb | ||
|
|
1b248721e9 | ||
|
|
fde5f7e744 | ||
|
|
f28e1e76ff | ||
|
|
4daf01e683 | ||
|
|
3752a78fa1 | ||
|
|
bbc86a15a1 | ||
|
|
9864184606 | ||
|
|
a0730475f1 | ||
|
|
d2f6350fab | ||
|
|
d535ac781e | ||
|
|
332647c296 | ||
|
|
30f0051976 | ||
|
|
1efb67b60c | ||
|
|
4c3bec2a3f | ||
|
|
39063e4bcf | ||
|
|
da67342419 | ||
|
|
50155a0838 | ||
|
|
25f773efa8 | ||
|
|
c1fcf0b075 | ||
|
|
063d5a22ba | ||
|
|
c92c1c9a3d | ||
|
|
213eb2c7c1 | ||
|
|
b284dcc7ef | ||
|
|
9822eae8ff | ||
|
|
c3dd57abc6 | ||
|
|
3c65d13c00 | ||
|
|
b84bab52e0 | ||
|
|
059bd2b54e | ||
|
|
ec29f55f53 | ||
|
|
547e0e73de | ||
|
|
d4f1660a1a | ||
|
|
6c81365ee7 | ||
|
|
f0e779c9ea | ||
|
|
71bf3815c3 | ||
|
|
d7c14201ac | ||
|
|
e963ccefe5 | ||
|
|
37252faedc | ||
|
|
5144f8ba02 | ||
|
|
16f83417fe | ||
|
|
17a506009e | ||
|
|
0b0d7784e0 | ||
|
|
151204c8d1 | ||
|
|
ced1536195 | ||
|
|
cce367b83c | ||
|
|
8101780fa1 | ||
|
|
20fcd3436c | ||
|
|
215521b800 | ||
|
|
34f8b9f82c | ||
|
|
23fd1fb35e | ||
|
|
91e362ca15 | ||
|
|
0027df28a3 | ||
|
|
9f9fcaedee | ||
|
|
a91f296562 | ||
|
|
708b9d93ff | ||
|
|
08c9c8f5fb | ||
|
|
b12b51d17a | ||
|
|
7b99aadb3f | ||
|
|
c5b551d68e | ||
|
|
fdfb7ce628 | ||
|
|
eceb7bd14b | ||
|
|
1e527c900a | ||
|
|
517f860c6b | ||
|
|
0e4da5719c | ||
|
|
7f1f9d6394 | ||
|
|
d25f723ae2 | ||
|
|
dd81ffeb00 | ||
|
|
f225ea8905 | ||
|
|
5b6999232f | ||
|
|
17d2b0da7c | ||
|
|
7768035da2 | ||
|
|
d4b53ac007 | ||
|
|
67e07c8fcd | ||
|
|
2737c63903 | ||
|
|
a367698670 | ||
|
|
eb904280e9 | ||
|
|
2ec4ad331c | ||
|
|
393c9af054 | ||
|
|
0f28e3e689 | ||
|
|
c072ff570b | ||
|
|
ec47da608f | ||
|
|
734dbfc98b | ||
|
|
a78649773f | ||
|
|
dc2429d9b7 | ||
|
|
965dabc415 | ||
|
|
b04bdb0b9f | ||
|
|
9ce304ea52 | ||
|
|
77a351c94c | ||
|
|
5d24697b0c | ||
|
|
0cbeb2fc52 | ||
|
|
934c9e23ba | ||
|
|
2441cc208f | ||
|
|
27fd8989d8 | ||
|
|
a641bd8bd1 | ||
|
|
366cb2e456 | ||
|
|
31a7e133fa | ||
|
|
c808b38778 | ||
|
|
ac04dc248c | ||
|
|
15eb927137 | ||
|
|
91ab53a471 | ||
|
|
9656d5274c | ||
|
|
01fd805ee6 | ||
|
|
846b3b81ff | ||
|
|
5c921c2bff | ||
|
|
d3317a48c0 | ||
|
|
ac8b7610cf | ||
|
|
a06edd4c4e | ||
|
|
b9d6c11e63 | ||
|
|
ce9519c9cb | ||
|
|
dbda657e6e | ||
|
|
7d2b895bfb | ||
|
|
ad719a2c07 | ||
|
|
ae31139ffe | ||
|
|
bea0806a47 | ||
|
|
7394595abb | ||
|
|
d2ce6f826a | ||
|
|
2544ff5902 | ||
|
|
113306ac23 | ||
|
|
e2cedb5594 | ||
|
|
63cf2ec616 | ||
|
|
c99cc22138 | ||
|
|
b76f7bd3ba | ||
|
|
d37e9ea1c3 | ||
|
|
1d008961ab | ||
|
|
8a7224d4b5 | ||
|
|
bd95a7fc01 | ||
|
|
6a9cdc9e18 | ||
|
|
3a9fbf88bd | ||
|
|
d9ca826095 | ||
|
|
1d36fbba53 | ||
|
|
7d230aeebc | ||
|
|
4e573c46cb | ||
|
|
0c649c3959 | ||
|
|
a14a99b681 | ||
|
|
aefd87e3e5 | ||
|
|
109d56b55e | ||
|
|
4eed0b38f8 | ||
|
|
424ae68621 | ||
|
|
101b2e7bdf | ||
|
|
049dba4b06 | ||
|
|
3024d32257 | ||
|
|
784ed7f21b | ||
|
|
2de44bfd99 | ||
|
|
65e391a4a7 | ||
|
|
16057247c2 | ||
|
|
04506c1e1e | ||
|
|
7038e2a18e | ||
|
|
3429444c0c | ||
|
|
3c46953d47 | ||
|
|
6c7a1b5697 | ||
|
|
cc35644e24 | ||
|
|
ccf2b3bc5b | ||
|
|
698a46eb7b | ||
|
|
9e5de62841 | ||
|
|
70ac9029c6 | ||
|
|
b66b989de0 | ||
|
|
9c82b2ea34 | ||
|
|
d033088113 | ||
|
|
240ccdc65e | ||
|
|
975d632007 | ||
|
|
53371344ae | ||
|
|
e9356ebe79 | ||
|
|
bc0c8fc84a | ||
|
|
6252f2c8c1 | ||
|
|
708f819182 | ||
|
|
bad4c7ed34 | ||
|
|
7e6f8f19eb | ||
|
|
a6fd969ead | ||
|
|
7b5443d680 | ||
|
|
8de723d2aa | ||
|
|
82b252ce2e | ||
|
|
a2dfb26b36 | ||
|
|
f1b6492d1c | ||
|
|
19d297f934 | ||
|
|
a23128f25f | ||
|
|
567f7c3028 | ||
|
|
30af4cfc3d | ||
|
|
019fcf519a | ||
|
|
1645562d78 | ||
|
|
fab726482c | ||
|
|
351cb9835f | ||
|
|
d0d59469bc | ||
|
|
577d934ccd | ||
|
|
4438131d56 | ||
|
|
7b13c9a9ba | ||
|
|
7d60566f3c | ||
|
|
e9a7c2f184 | ||
|
|
3e07873575 | ||
|
|
2f17584ab4 | ||
|
|
02f87b23fb | ||
|
|
31032e29f5 | ||
|
|
11bfb57809 | ||
|
|
0e83ef32bb | ||
|
|
86ae9e94cd | ||
|
|
2388250dfa | ||
|
|
d947ea65a2 | ||
|
|
4a11c04742 | ||
|
|
14b24e6050 | ||
|
|
4402d6fbd6 | ||
|
|
c69b81404c | ||
|
|
241a3efacf | ||
|
|
185d52a9d4 | ||
|
|
6b933f18dd | ||
|
|
31dbbb546c | ||
|
|
ac4f8a30e5 | ||
|
|
a060531ae3 | ||
|
|
fb77937acd | ||
|
|
9d9a81710f | ||
|
|
c3b4438d1f | ||
|
|
eeb3679b82 | ||
|
|
d2d9bfb4bd | ||
|
|
2dcbe4628b | ||
|
|
634247676a | ||
|
|
1a01b323e5 | ||
|
|
c71a33eba0 | ||
|
|
0387c7a8e1 | ||
|
|
1ae0404592 | ||
|
|
6796bef261 | ||
|
|
a5bd6dc7c5 | ||
|
|
4e7b9290ac | ||
|
|
9acbee976d | ||
|
|
e6f4f8c05e | ||
|
|
c89cb3d287 | ||
|
|
c5cb95fd8c | ||
|
|
fa46714b19 | ||
|
|
bfbcde3b47 | ||
|
|
c6dc66cb45 | ||
|
|
a9e6912a2f | ||
|
|
eb8885b915 | ||
|
|
029c808458 | ||
|
|
9269f69a38 | ||
|
|
63d938ae04 | ||
|
|
a8aa110f43 | ||
|
|
6f7af5aef4 | ||
|
|
6afbade8f7 | ||
|
|
5ec38498f1 | ||
|
|
e206d3a833 | ||
|
|
6529cf6498 | ||
|
|
21f5de8de8 | ||
|
|
837d5c7f68 | ||
|
|
f90a53c2b0 | ||
|
|
e184e5b7c5 | ||
|
|
1ca1381e05 | ||
|
|
811f807de6 | ||
|
|
95b76bc586 | ||
|
|
90fac39a26 | ||
|
|
44cf680f14 | ||
|
|
d0754e022f | ||
|
|
ed7245c852 | ||
|
|
2b44618858 | ||
|
|
a3634d689e | ||
|
|
96e8cbd3c1 | ||
|
|
658d41f0fd | ||
|
|
9dab8fd7dc | ||
|
|
2cb9d81a3c | ||
|
|
2b4662856e | ||
|
|
44e949eafe | ||
|
|
aa3acd12a6 | ||
|
|
1c00e62d3e | ||
|
|
0d630d9ea3 | ||
|
|
7de78cd088 | ||
|
|
0f98c72f1e | ||
|
|
459a79a1f1 | ||
|
|
aaea8d9717 | ||
|
|
d5b99732d1 | ||
|
|
f5cef8a997 | ||
|
|
44907aa700 | ||
|
|
54303d464b | ||
|
|
4e83a68bf1 | ||
|
|
00893a6cca | ||
|
|
008768cea1 | ||
|
|
43e096c6dc | ||
|
|
b10b48f5e9 | ||
|
|
1a76c72bf3 | ||
|
|
74a1f6301a | ||
|
|
dd22ec68fc | ||
|
|
6ecdfc25fd | ||
|
|
f439f09c2e | ||
|
|
ebe955020c | ||
|
|
60119a89c0 | ||
|
|
6a14353391 | ||
|
|
9090fe5fc9 | ||
|
|
93bc9a4293 | ||
|
|
80b2e22d9d | ||
|
|
5a6a098990 | ||
|
|
c64ef201ff | ||
|
|
817ae02295 | ||
|
|
910dab98f1 | ||
|
|
b9c59ffad4 | ||
|
|
79426ec959 | ||
|
|
2e0ba0e3d1 | ||
|
|
0c3ce58ffa | ||
|
|
c482820746 | ||
|
|
195bc1f290 | ||
|
|
d8108f998b | ||
|
|
40de60dd8b | ||
|
|
c9981472a8 | ||
|
|
83b3789282 | ||
|
|
0078e9e225 | ||
|
|
a62966227a | ||
|
|
5f0ccf3257 | ||
|
|
61d0a1d498 | ||
|
|
c626528a83 | ||
|
|
2e0e8e18ef | ||
|
|
54d98a6cad | ||
|
|
0fe503658b | ||
|
|
5941464402 | ||
|
|
3074ea62dc | ||
|
|
312bf91003 | ||
|
|
a42c323343 | ||
|
|
39d9fe2794 | ||
|
|
7993e2971c | ||
|
|
ba9efe43be | ||
|
|
cfa5e7d19c | ||
|
|
7acd2ad884 | ||
|
|
1f474c3097 | ||
|
|
c8b4f6e985 | ||
|
|
fc20bcca91 | ||
|
|
702b635826 | ||
|
|
990c5f67e4 | ||
|
|
8ef4ca2ce8 | ||
|
|
b105ce6698 | ||
|
|
6c93b836f5 | ||
|
|
f0e60ee577 | ||
|
|
2cfbb2373a | ||
|
|
d26d04d92b | ||
|
|
0d6fe32246 | ||
|
|
36de8073f2 | ||
|
|
5aaaa1e6a7 | ||
|
|
a4126a52ce | ||
|
|
076017128e | ||
|
|
7240147418 | ||
|
|
0923f2bb5c | ||
|
|
5ce0b9985a | ||
|
|
cd76375d8e | ||
|
|
df2ef01494 | ||
|
|
1d3d875f3d | ||
|
|
48446367f4 | ||
|
|
fb1f293a17 | ||
|
|
f85533d608 | ||
|
|
e32faf6053 | ||
|
|
a429ea4679 | ||
|
|
9112d2277e | ||
|
|
a9050045f3 | ||
|
|
ed3cad6e9c | ||
|
|
deee5aff00 | ||
|
|
8c36a4d4c6 | ||
|
|
157074db29 | ||
|
|
14ff04d2e3 | ||
|
|
d0e2d439aa | ||
|
|
159340a396 | ||
|
|
de6625bcaf | ||
|
|
0d7ed691e6 | ||
|
|
0721f723be | ||
|
|
2da7239ac6 | ||
|
|
7b4c07c837 | ||
|
|
169c56f105 | ||
|
|
d51cdfd7c4 | ||
|
|
3c02b139e8 | ||
|
|
9d660b9d4e | ||
|
|
eaf7c61f01 | ||
|
|
1234c05690 | ||
|
|
fd5b6769fa | ||
|
|
63db34070e | ||
|
|
b41cd3ff97 | ||
|
|
f2406ee0e4 | ||
|
|
4712c6a372 | ||
|
|
56dc1b2b6c | ||
|
|
cb13d00844 | ||
|
|
7a11e8eb19 | ||
|
|
482af25c90 | ||
|
|
0c17e21b85 | ||
|
|
0acb6ac548 | ||
|
|
7f339860ad | ||
|
|
67cf38a291 | ||
|
|
cad28b9fd5 | ||
|
|
80ceca6e28 | ||
|
|
bdcb8864fb | ||
|
|
ced444282f | ||
|
|
5c7c11e3f4 | ||
|
|
2b4628fb43 | ||
|
|
a0dbd75f35 | ||
|
|
a20877ea80 | ||
|
|
e151691711 | ||
|
|
f42db27eaa | ||
|
|
724eab69d8 | ||
|
|
a83dbccc6c | ||
|
|
178e987650 | ||
|
|
3cd126f08d | ||
|
|
b109123a43 | ||
|
|
c97e97d2cc | ||
|
|
160d098510 | ||
|
|
a72e1924ca | ||
|
|
fd7508f152 | ||
|
|
1de995f9d5 | ||
|
|
47fbe6423e | ||
|
|
461c2a38a5 | ||
|
|
d89036f9f3 | ||
|
|
998cb16bfa | ||
|
|
4c2a8c2892 | ||
|
|
8d3afa0bb6 | ||
|
|
3fd7bbc0a3 | ||
|
|
bf66500aac | ||
|
|
e28da0d7fd | ||
|
|
f10bc886c4 | ||
|
|
df578ac78b | ||
|
|
18e1557cf3 | ||
|
|
30e6131cd7 | ||
|
|
44310fda20 | ||
|
|
fb7431abb5 | ||
|
|
5b109ea3ce | ||
|
|
a671e9f925 | ||
|
|
8168804f05 | ||
|
|
ec576ad0a9 | ||
|
|
fa3abcfdec | ||
|
|
33864614e7 | ||
|
|
f4bf68ee59 | ||
|
|
2b3d6f976d | ||
|
|
641a3313ea | ||
|
|
08e6665ffc | ||
|
|
e6a7af4ab3 | ||
|
|
a0030a7909 | ||
|
|
13c7a7986e | ||
|
|
bd1ea872be | ||
|
|
dfcd595bc1 | ||
|
|
f77c97c66a | ||
|
|
ca53391bdb | ||
|
|
d01dd904da | ||
|
|
bb885bddd4 | ||
|
|
3d8f2c62ea | ||
|
|
7cdb5e86c6 | ||
|
|
255b2b464d | ||
|
|
0ef771ca15 | ||
|
|
a3207496b6 | ||
|
|
741724973c | ||
|
|
3375c91b56 | ||
|
|
67da9d7233 | ||
|
|
4b228b32f0 | ||
|
|
f66189de6b | ||
|
|
743805ecd5 | ||
|
|
6c9dcea08c | ||
|
|
fa1944090d | ||
|
|
04cf428619 | ||
|
|
86d6b00886 | ||
|
|
38429d98df | ||
|
|
0c9667fe58 | ||
|
|
3c6bb7be4c | ||
|
|
40fa732122 | ||
|
|
01e2e25bce | ||
|
|
29e916dcdd | ||
|
|
1bfa7610ae | ||
|
|
fb94c32bb4 | ||
|
|
94ad26d818 | ||
|
|
f323addc1c | ||
|
|
5559c91c0e | ||
|
|
6cc5eab94b | ||
|
|
c2a3450c8f | ||
|
|
01e1ec0794 | ||
|
|
ea381cde90 | ||
|
|
26074c67c6 | ||
|
|
e2b13fcda5 | ||
|
|
0130852d9a | ||
|
|
a027af9e84 | ||
|
|
ce81b3d4da | ||
|
|
96d8ff3cb7 | ||
|
|
b67c354fdb | ||
|
|
9a610197ea | ||
|
|
1109c3423c | ||
|
|
18fcb8d0ad | ||
|
|
a5845ed0d9 | ||
|
|
3392fa59fe | ||
|
|
95e816572a | ||
|
|
3acb3aab9f | ||
|
|
509860d890 | ||
|
|
61dcc467ee | ||
|
|
ae6601d9e3 | ||
|
|
33733af3c5 | ||
|
|
da7c0ab7d6 | ||
|
|
afd156b51f | ||
|
|
5d549b7c60 | ||
|
|
1b5671dc87 | ||
|
|
87e2893505 | ||
|
|
89443e342f | ||
|
|
de5ed803ed | ||
|
|
528f9a7ec4 | ||
|
|
a1f7656fe4 | ||
|
|
a5703a55eb | ||
|
|
1275b85465 | ||
|
|
52dc2738a1 | ||
|
|
7c0f7cbdc2 | ||
|
|
c14ef8bd13 | ||
|
|
ce62ae9f50 | ||
|
|
133347f884 | ||
|
|
ca4f56cb04 | ||
|
|
f512ae973d | ||
|
|
04cceb314e | ||
|
|
4138e10788 | ||
|
|
5d11b9aa97 | ||
|
|
6033c2b3ce | ||
|
|
dfb28dc155 | ||
|
|
756b29d9ed | ||
|
|
dc7a3af768 | ||
|
|
baf3a2d915 | ||
|
|
25832ab2ea | ||
|
|
c95c0401eb | ||
|
|
936580a924 | ||
|
|
00df147688 | ||
|
|
8273dcfdfc | ||
|
|
81e2dc3635 | ||
|
|
94611cd80b | ||
|
|
28af81142f | ||
|
|
b3fd3ec0ba | ||
|
|
44518d5b33 | ||
|
|
323e74f50f | ||
|
|
a972e755d1 | ||
|
|
49b6292f7f | ||
|
|
dd27dc1503 | ||
|
|
547ed1fd26 | ||
|
|
c387b0d069 | ||
|
|
ba2af4d84d | ||
|
|
20b7c6a823 | ||
|
|
b2edab0452 | ||
|
|
a72dc67268 | ||
|
|
11b9745268 | ||
|
|
1d1d49a3c9 | ||
|
|
31a4e7a22c | ||
|
|
dca13f0749 | ||
|
|
d377eee11c | ||
|
|
196052efed | ||
|
|
694c5104fa | ||
|
|
acbf765851 | ||
|
|
ef06dfb7b3 | ||
|
|
613432ef2c | ||
|
|
512dc87b3f | ||
|
|
480f034301 | ||
|
|
39275ce133 | ||
|
|
c73fa0b42d | ||
|
|
3d4ed43337 | ||
|
|
74067fd515 | ||
|
|
9538771eef | ||
|
|
7ecb968e23 | ||
|
|
c9365732d9 | ||
|
|
d41c13ac29 | ||
|
|
1b2fa2b2e8 | ||
|
|
b9a9113abe | ||
|
|
1dd9dbec6c | ||
|
|
84efe5447b | ||
|
|
61bab55d11 | ||
|
|
85d05153f7 | ||
|
|
f9bc316c98 | ||
|
|
c4adc8d9be | ||
|
|
169719c62d | ||
|
|
a509a491af | ||
|
|
aed8e26062 | ||
|
|
cec414126d | ||
|
|
6bf96ab808 | ||
|
|
d8d9f72985 | ||
|
|
cecb2e8f4c | ||
|
|
d9ae28d3ed | ||
|
|
761078625e | ||
|
|
be37a75928 | ||
|
|
884e9fb3c9 | ||
|
|
8821502a81 | ||
|
|
f66c012df6 | ||
|
|
baf996fc06 | ||
|
|
e56a1d3274 | ||
|
|
b04af33fb5 | ||
|
|
1f53d32a62 | ||
|
|
534aeb3d07 | ||
|
|
a3693d0a45 | ||
|
|
967a6bd4a4 | ||
|
|
c589c9b9ec | ||
|
|
d10ad4835b | ||
|
|
38a273b195 | ||
|
|
9618f46188 | ||
|
|
2562b384bc | ||
|
|
bc8d133b69 | ||
|
|
beb9967ad0 | ||
|
|
433baf0923 | ||
|
|
423da8b785 | ||
|
|
f4708d2b1a | ||
|
|
b00b7f7c31 | ||
|
|
ec4110cb2c | ||
|
|
b2f02c7fa6 | ||
|
|
aeb561c240 | ||
|
|
97a6abca0e | ||
|
|
5aa3a29288 | ||
|
|
bc49e7c48e | ||
|
|
9ba10446e9 | ||
|
|
a4c686876f | ||
|
|
f31ba7dea3 | ||
|
|
897946c1ce | ||
|
|
802266e3aa | ||
|
|
d9f89f7457 | ||
|
|
b871d84379 | ||
|
|
0375309060 | ||
|
|
a5ca653df8 | ||
|
|
ec194a15fb | ||
|
|
eaf5d71b40 | ||
|
|
7a9ee279ed | ||
|
|
827acdadea | ||
|
|
ef99b2057a | ||
|
|
c938714b70 | ||
|
|
21e3dd30fd | ||
|
|
b271ab4721 | ||
|
|
3abe382f44 | ||
|
|
88a6b702d2 | ||
|
|
497d1af8bf | ||
|
|
cc4b6acd14 | ||
|
|
1714e2331c | ||
|
|
4e419ec16d | ||
|
|
5e0f214a8f | ||
|
|
da1727e5e4 | ||
|
|
101be2eeb1 | ||
|
|
e69015204a | ||
|
|
1b203e3292 | ||
|
|
1ad8bd212c | ||
|
|
3711f30d01 | ||
|
|
85d59d25df | ||
|
|
6d7f55a435 | ||
|
|
c22ca18a82 | ||
|
|
ec48959600 | ||
|
|
67634c4a71 | ||
|
|
c31d38a562 | ||
|
|
6b0499b82e | ||
|
|
046364283f | ||
|
|
85880f9bd1 | ||
|
|
5fd436e5c5 | ||
|
|
01533cbf9f | ||
|
|
f5c8276fdc | ||
|
|
05f2b81025 | ||
|
|
9dfd6cecad | ||
|
|
2febf837e5 | ||
|
|
ac954bba11 | ||
|
|
2bda4fef5b | ||
|
|
5a815592dc | ||
|
|
3f4c6ce144 | ||
|
|
19e0c53d1e | ||
|
|
95963b2289 | ||
|
|
4cd21cad9c | ||
|
|
fcf1d7d502 | ||
|
|
e92d04771d | ||
|
|
ce43190ca6 | ||
|
|
284dbbad24 | ||
|
|
1e115a5eab | ||
|
|
f18a355469 | ||
|
|
8ba95bb82a | ||
|
|
ee5a2a320e | ||
|
|
738fd3da58 | ||
|
|
decc08934c | ||
|
|
5f5b7f92cf | ||
|
|
20fa280171 | ||
|
|
e5fa2ef750 | ||
|
|
bd31e25757 | ||
|
|
5b3113d96b | ||
|
|
84b4f7695b | ||
|
|
fc8ea3bcd0 | ||
|
|
9051a4df4d | ||
|
|
a7b42b6c97 | ||
|
|
f7675b1e46 | ||
|
|
db1117d892 | ||
|
|
4b14e19229 | ||
|
|
606021fb8a | ||
|
|
b15499c1dd | ||
|
|
950588cb65 | ||
|
|
7991c06543 | ||
|
|
b335c4ca05 | ||
|
|
cf3773dd28 | ||
|
|
2a3740e49f | ||
|
|
bcbd30ff6e | ||
|
|
571ab9602f | ||
|
|
cfab6a3bb6 | ||
|
|
7c9ab59aff | ||
|
|
7b1d1129a8 | ||
|
|
bf3e8fe3a9 | ||
|
|
baeac17d5b | ||
|
|
68ce6dea4b | ||
|
|
00df4b8920 | ||
|
|
36814514b7 | ||
|
|
381a9a28b0 | ||
|
|
5c364896d3 | ||
|
|
07ce1d44a9 | ||
|
|
1348ac86f7 | ||
|
|
b4c4855a9b | ||
|
|
7a1001a70b | ||
|
|
340a8130e9 | ||
|
|
9eb8de27d2 | ||
|
|
476a43a5bf | ||
|
|
9ab955d026 | ||
|
|
bcedb32cf0 | ||
|
|
6bc266f13c | ||
|
|
ed36feeb0a | ||
|
|
85400cd8f6 | ||
|
|
2866697b32 | ||
|
|
f1a99e1194 | ||
|
|
db47ddf3dc | ||
|
|
2cc4dbd2ba | ||
|
|
a38eef2971 | ||
|
|
e3e197a917 | ||
|
|
361d0befb6 | ||
|
|
ba35c662ea | ||
|
|
45ce763c71 | ||
|
|
73c85a0013 | ||
|
|
26361630c2 | ||
|
|
96c30c509b | ||
|
|
d9b9786486 | ||
|
|
bb9cea260d | ||
|
|
958c2f97ec | ||
|
|
27651f17bf | ||
|
|
71621f7bb5 | ||
|
|
d8add46215 | ||
|
|
e459f570d5 | ||
|
|
5b5057dee0 | ||
|
|
f21becb37d | ||
|
|
9d03eb1ad4 | ||
|
|
cb90c5e616 | ||
|
|
77059f2db0 | ||
|
|
fb72c36a48 | ||
|
|
b2b215a061 | ||
|
|
8d313e4cf8 | ||
|
|
6bb760375e | ||
|
|
025cd043d3 | ||
|
|
3f368d4a8e | ||
|
|
449e41e435 | ||
|
|
bf0062be52 | ||
|
|
3c025c8b52 | ||
|
|
6dc3d954c5 | ||
|
|
c9b7a11a89 | ||
|
|
33cb2d108e | ||
|
|
e053e74b58 | ||
|
|
4e35fc2fbe | ||
|
|
4768d8e459 | ||
|
|
3598cc1d85 | ||
|
|
e3a895b88c | ||
|
|
61eff3ddf0 | ||
|
|
e9268984ae | ||
|
|
f28b35bd28 | ||
|
|
a86618c2c2 | ||
|
|
1f1a4b8fb8 | ||
|
|
a1d0be34c2 | ||
|
|
ef0a04cc1c | ||
|
|
284262b7da | ||
|
|
58b0a17986 | ||
|
|
57abe00c62 | ||
|
|
d014407ba4 | ||
|
|
2e8bfa16f9 | ||
|
|
bf34713b0c | ||
|
|
c46c1a96cd | ||
|
|
2693b62de4 | ||
|
|
18387f6d98 | ||
|
|
08b7356184 | ||
|
|
e30cdfc176 | ||
|
|
cc0ed38e68 | ||
|
|
5ec0d20286 | ||
|
|
e0aa69f605 | ||
|
|
c859f39036 | ||
|
|
5251f62665 | ||
|
|
2b87e2b221 | ||
|
|
c185e9b487 | ||
|
|
b5d9a99f10 | ||
|
|
fec67fe0ea | ||
|
|
987997a986 | ||
|
|
27ef79ca27 | ||
|
|
bcc1932a37 | ||
|
|
2e163e9986 | ||
|
|
5473e57b10 | ||
|
|
b970bad058 | ||
|
|
bea1814cb9 | ||
|
|
45e58f29b4 | ||
|
|
f7a3df635a | ||
|
|
3f67984929 | ||
|
|
bf82171baa | ||
|
|
4c49a7f003 | ||
|
|
57b5d40851 | ||
|
|
ecde2d1627 | ||
|
|
b48e9a31f6 | ||
|
|
f64e5241ed | ||
|
|
4e9d01055a | ||
|
|
9d4ca25499 | ||
|
|
f1ddf9dc2b | ||
|
|
e99b790d58 | ||
|
|
5e4a99c1ad | ||
|
|
c89824bf25 | ||
|
|
1230d9cdd4 | ||
|
|
d3dd8dc686 | ||
|
|
382faa49cb | ||
|
|
5cf4b4663f | ||
|
|
749b4d3083 | ||
|
|
02835d057e | ||
|
|
f5aaaecc48 | ||
|
|
184ff84f92 | ||
|
|
ef56bc1f55 | ||
|
|
9846f7509e | ||
|
|
1cf7cefe83 | ||
|
|
b5f1dbc47b | ||
|
|
37b85491c3 | ||
|
|
30d792b35b | ||
|
|
ac29412b2f | ||
|
|
01c170afaf | ||
|
|
539e0811c9 | ||
|
|
940448ffae | ||
|
|
68a73f96c4 | ||
|
|
87a93745cb | ||
|
|
3b08abca10 | ||
|
|
5e68096a2e | ||
|
|
e3ef11ceae | ||
|
|
575fe8379f | ||
|
|
60feae7e5b | ||
|
|
2b45ecaea1 | ||
|
|
7a3a430137 | ||
|
|
11c0563fe5 | ||
|
|
00018b3e89 | ||
|
|
de787a069d | ||
|
|
5f33ea6013 | ||
|
|
ee74c4c17f | ||
|
|
b031f52ee2 | ||
|
|
77bb01b18c | ||
|
|
d2fdc28c85 | ||
|
|
d34e985a92 | ||
|
|
f0c2c834c3 | ||
|
|
ca0cce9401 | ||
|
|
2a0e211daf | ||
|
|
ea89983e45 | ||
|
|
e4ed1c8fd7 | ||
|
|
d46155bf32 | ||
|
|
9bdf0d8937 | ||
|
|
f56c1226b6 | ||
|
|
bc3e4742f0 | ||
|
|
891b16ac76 | ||
|
|
11a32c3537 | ||
|
|
8afc96e1ad | ||
|
|
55d2c9e49c | ||
|
|
9baabee3fd | ||
|
|
ad20cb6644 | ||
|
|
04d2d92524 | ||
|
|
6630a8c2a5 | ||
|
|
67ee86eaeb | ||
|
|
2fcfbc2e1a | ||
|
|
2bb1162adf | ||
|
|
f5e0b67305 | ||
|
|
0b25fb5771 | ||
|
|
27ff29329f | ||
|
|
b52cfbb602 | ||
|
|
c5a1c64a35 | ||
|
|
cbedd9bec5 | ||
|
|
7a90844970 | ||
|
|
45dcb72178 | ||
|
|
d23b5bb58b | ||
|
|
bf7de99182 | ||
|
|
8ddfab4b47 | ||
|
|
57c2dc2d65 | ||
|
|
62236a38f3 | ||
|
|
25ab7bba02 | ||
|
|
fe14f3ee0e | ||
|
|
425120de94 | ||
|
|
2520c8d173 | ||
|
|
7b4ee1c44b | ||
|
|
58a1dcd141 | ||
|
|
3b4f44f276 | ||
|
|
ebcc06686c | ||
|
|
45b3a7dbcd | ||
|
|
16f04f2255 | ||
|
|
a23fcbd095 | ||
|
|
7491c0f7c4 | ||
|
|
83da75a5e5 | ||
|
|
2474c32f60 | ||
|
|
6910f1f0b7 | ||
|
|
c426aeac6a | ||
|
|
0e2716ba31 | ||
|
|
011239d45c | ||
|
|
5bb3d1a9e1 | ||
|
|
43766c7ab9 | ||
|
|
e12eeed65d | ||
|
|
711ecb4025 | ||
|
|
88957699c5 | ||
|
|
fcd6c51d55 | ||
|
|
adda02dd0d | ||
|
|
ab1cff2a7d | ||
|
|
0716a743d8 | ||
|
|
d0e17fde77 | ||
|
|
d6c0aa8a80 | ||
|
|
815bf9b390 | ||
|
|
0aa6e0a8b2 | ||
|
|
8b1aff33fe | ||
|
|
fdc9464576 | ||
|
|
dc6c1a0fe1 | ||
|
|
78a73ac15f | ||
|
|
48891ed7c7 | ||
|
|
d3fd5ba9ac | ||
|
|
2b6f575802 | ||
|
|
f604460d56 | ||
|
|
7472893e8e | ||
|
|
eff074faae | ||
|
|
07c04b40b1 | ||
|
|
754adb545e | ||
|
|
5fc04277c1 | ||
|
|
78f5fd3f71 | ||
|
|
0277c6b9bd | ||
|
|
e34b4b8ae7 | ||
|
|
c0de18f3aa | ||
|
|
4b78918347 | ||
|
|
6606a883c5 | ||
|
|
30c1a64d31 | ||
|
|
91dbcc40aa | ||
|
|
b0f5119ec0 | ||
|
|
ed9aba18b8 | ||
|
|
d507325378 | ||
|
|
0a0546168b | ||
|
|
5abbbe80d1 | ||
|
|
4a6413f654 | ||
|
|
6c60244b26 | ||
|
|
a384f0e6e9 | ||
|
|
2a56410543 | ||
|
|
7ef22fc1e0 | ||
|
|
5051d698c0 | ||
|
|
1a87c08bc2 | ||
|
|
ed0c5908ce | ||
|
|
f571ced9c5 | ||
|
|
3110181a9f | ||
|
|
fcb7966f70 | ||
|
|
be2945a16f | ||
|
|
6b3326ad42 | ||
|
|
3e81a03087 | ||
|
|
3778430ead | ||
|
|
31bd251f37 | ||
|
|
f49f01ec85 | ||
|
|
1bd6721af9 | ||
|
|
4a069266d8 | ||
|
|
d10d7f3f02 | ||
|
|
05adbb1325 | ||
|
|
ca8719b42b | ||
|
|
ec80850e76 | ||
|
|
ab75a8b3e5 | ||
|
|
d00c8119fa | ||
|
|
2b7d188677 | ||
|
|
12c09693bd | ||
|
|
87793b3dc3 | ||
|
|
7ce8c0b966 | ||
|
|
a831944b14 | ||
|
|
17533d2c61 | ||
|
|
bee1d6beed | ||
|
|
a49ae076a5 | ||
|
|
4856503a33 | ||
|
|
0d4560f54e | ||
|
|
c5f7c2ace7 | ||
|
|
f8c03fa48c | ||
|
|
8b6b34d7c0 | ||
|
|
1fd7001e0c | ||
|
|
2631550c2f | ||
|
|
01734fadcf | ||
|
|
33cead0b03 | ||
|
|
08f86cbcf9 | ||
|
|
a83b96c74d | ||
|
|
7cf0ddc81b | ||
|
|
41640b5215 | ||
|
|
e1abedbfa4 | ||
|
|
a1482b9781 | ||
|
|
8a6d6ae771 | ||
|
|
a67e8314af | ||
|
|
f5e7497913 | ||
|
|
e4c7c601d7 | ||
|
|
02fdc78066 | ||
|
|
d1a4521396 | ||
|
|
646ddb4ddb | ||
|
|
8578078f7c | ||
|
|
ab204281ed | ||
|
|
31940d8f58 | ||
|
|
98874790fc | ||
|
|
5cd476687e | ||
|
|
73449e4407 | ||
|
|
eae06a4145 | ||
|
|
5a8d56c2b4 | ||
|
|
da6ccb6310 | ||
|
|
b0a8d04f97 | ||
|
|
99acfc9641 | ||
|
|
a731b606c5 | ||
|
|
c99b369b56 | ||
|
|
0f41e6053d | ||
|
|
b2ffddd84d | ||
|
|
1a9451fe61 | ||
|
|
0859eef869 | ||
|
|
1ad28cb9ac | ||
|
|
0f8aa5d7e7 | ||
|
|
5bf680a1cf | ||
|
|
e9d599ab26 | ||
|
|
dc83e66f72 | ||
|
|
5cf16dc9fd | ||
|
|
e076ac1a9c | ||
|
|
7a28932569 | ||
|
|
5567c5abf6 | ||
|
|
b2e2c5c173 | ||
|
|
e41731ce23 | ||
|
|
b1b6d6ace8 | ||
|
|
97b5a4c304 | ||
|
|
0722417e0b | ||
|
|
dc9e938510 | ||
|
|
c37b8f37e2 | ||
|
|
80116b6687 | ||
|
|
7d5225b2ba | ||
|
|
f2f318b11e | ||
|
|
6bfe1b0cfd | ||
|
|
f807cca8c7 | ||
|
|
feadf59fa0 | ||
|
|
10e64e04fe | ||
|
|
c2cbd502ea | ||
|
|
cf5fd8064b | ||
|
|
1a417b9d63 | ||
|
|
123cfe6a38 | ||
|
|
43c0681d35 | ||
|
|
2ec5784a44 | ||
|
|
d59f4229d6 | ||
|
|
69c8ac3942 | ||
|
|
acbc9370f5 | ||
|
|
f3563e8b4a | ||
|
|
5484ccc0f0 | ||
|
|
34fe0f1077 | ||
|
|
5039cbb529 | ||
|
|
93a9876de0 | ||
|
|
ae311718da | ||
|
|
441945d7e6 | ||
|
|
419b7fcb2c | ||
|
|
0692547440 | ||
|
|
6471928f91 | ||
|
|
1866295d5d | ||
|
|
4b538b419a | ||
|
|
72c19c08b8 | ||
|
|
57b5afa676 | ||
|
|
a5b8dfc1a3 | ||
|
|
9b87a3b755 | ||
|
|
191b8531be | ||
|
|
b1f6735e87 | ||
|
|
900968a91c | ||
|
|
1e25d93a05 | ||
|
|
bbdfb2b4f2 | ||
|
|
93ebcbac6c | ||
|
|
8c261215d7 | ||
|
|
8059c4f1d5 | ||
|
|
e9f5cf2259 | ||
|
|
86bec2943b | ||
|
|
f812646267 | ||
|
|
390582e3f2 | ||
|
|
5d5c6b06b9 | ||
|
|
10269dc779 | ||
|
|
b28c2ae735 | ||
|
|
8810d18020 | ||
|
|
9b8841174e | ||
|
|
a3a56b2ee9 | ||
|
|
41db56c093 | ||
|
|
c8e7cc856f | ||
|
|
18d3e0db42 | ||
|
|
865e5c85b8 | ||
|
|
e61175af6b | ||
|
|
4f1f72e1dc | ||
|
|
0da593a008 | ||
|
|
8e78140259 | ||
|
|
1376a05c6a | ||
|
|
7d8ca6fdc7 | ||
|
|
f5ad09619f | ||
|
|
685d83bd1e | ||
|
|
64a132808d | ||
|
|
26e1f4001b | ||
|
|
1178138ad2 | ||
|
|
9d3d075524 | ||
|
|
d157bc4769 | ||
|
|
ff72ede2f4 | ||
|
|
d2631a7586 | ||
|
|
d271acc67e | ||
|
|
503fb61ee1 | ||
|
|
9dde5cd0b0 | ||
|
|
9f96d171f7 | ||
|
|
a69fc24b40 | ||
|
|
82ab166b94 | ||
|
|
8c4b5c7f6b | ||
|
|
7f22f6d2a6 | ||
|
|
d66c688910 | ||
|
|
986373c30d | ||
|
|
2c45a20ca7 | ||
|
|
599f083fe2 | ||
|
|
c572223147 | ||
|
|
a35fbc9de4 | ||
|
|
4f4e7f8b61 | ||
|
|
5993408eff | ||
|
|
e9dfcfbb2f | ||
|
|
18e3dfb448 | ||
|
|
b1e1b0f6ac | ||
|
|
42718d3f7a | ||
|
|
63cfccab40 | ||
|
|
583b36667f | ||
|
|
c7f55b88a6 | ||
|
|
df7503cb3b | ||
|
|
75066477cf | ||
|
|
7be063f104 | ||
|
|
5521fccf73 | ||
|
|
b823535880 | ||
|
|
0971ba5cee | ||
|
|
ab584366f2 | ||
|
|
8bfb1fb348 | ||
|
|
acaa23fe88 | ||
|
|
6aea979ca5 | ||
|
|
34c7eb612a | ||
|
|
b3d472f88d | ||
|
|
6801fea2b1 | ||
|
|
effe010223 | ||
|
|
394f4bbd49 | ||
|
|
f48e5396e4 | ||
|
|
08c6a9a73b | ||
|
|
3ad05c2424 | ||
|
|
d3b07cf012 | ||
|
|
c6d2ba92d9 | ||
|
|
8569205297 | ||
|
|
8fd31eeb4b | ||
|
|
5fea19aa88 | ||
|
|
b338a4bba3 | ||
|
|
a67eb5128f | ||
|
|
7ff36c1bdb | ||
|
|
2cf683fc0f | ||
|
|
b605619baf | ||
|
|
57a6dc9225 | ||
|
|
dad80c023f | ||
|
|
d4bb7a14e3 | ||
|
|
f72c6d54a4 | ||
|
|
e679849568 | ||
|
|
5ea312c64e | ||
|
|
f7ad051eef | ||
|
|
8c5ef66865 | ||
|
|
f130d0c3bf | ||
|
|
1c2d5300f1 | ||
|
|
66804e5a47 | ||
|
|
01c343a13a | ||
|
|
932447aab6 | ||
|
|
7394813151 | ||
|
|
01824c518f | ||
|
|
e38ab21d27 | ||
|
|
e9370792f5 | ||
|
|
896e037662 | ||
|
|
644cc5e06a | ||
|
|
9f4d2d8924 | ||
|
|
7ee0e867c8 | ||
|
|
b9c5e5f077 | ||
|
|
8631d06ca5 | ||
|
|
e7d9ddd8e6 | ||
|
|
a264e8d0c6 | ||
|
|
eb3a2e0dbf | ||
|
|
b7ee6018ad | ||
|
|
df151027cc | ||
|
|
cf3e27431e | ||
|
|
585cf775bd | ||
|
|
b686cce21b | ||
|
|
512e9789d9 | ||
|
|
09c27a433b | ||
|
|
e28aa2ee67 | ||
|
|
73e87410ac | ||
|
|
baece5113b | ||
|
|
230db979ed | ||
|
|
0e31bbdbd4 | ||
|
|
8399322238 | ||
|
|
03b057be64 | ||
|
|
5bd621c540 | ||
|
|
228a451284 | ||
|
|
3c67755c6b | ||
|
|
4d6ffa78f6 | ||
|
|
a9be4a9860 | ||
|
|
6275ddbcba | ||
|
|
365784bbed | ||
|
|
c1623e412d | ||
|
|
13f6cca318 | ||
|
|
1f46214363 | ||
|
|
c39a807766 | ||
|
|
3082c02fbe | ||
|
|
96a134e69e | ||
|
|
14fcebdde3 | ||
|
|
b4211a57e6 | ||
|
|
4a421c705a | ||
|
|
695e3a912c | ||
|
|
af190697c2 | ||
|
|
814ffbd468 | ||
|
|
cb10f39db3 | ||
|
|
c8a496faaa | ||
|
|
7c858007b3 | ||
|
|
3a9e8f1a79 | ||
|
|
dfa839c4e0 | ||
|
|
876fee3bc5 | ||
|
|
22ffd2e0bd | ||
|
|
8cd59b9ab5 | ||
|
|
988714f92a | ||
|
|
789d39bfb2 | ||
|
|
1c333e68c2 | ||
|
|
e9ff2e4fca | ||
|
|
173622fea1 | ||
|
|
9881f2c39e | ||
|
|
0970c0bc52 | ||
|
|
cb2686dc3c | ||
|
|
e0d79a4079 | ||
|
|
2e54a182c9 | ||
|
|
83a405db8b | ||
|
|
cdddecb834 | ||
|
|
faf528a94e | ||
|
|
aff3c978cb | ||
|
|
c09c787507 | ||
|
|
9b82667fb5 | ||
|
|
6a8d341ecc | ||
|
|
743ce9f07c | ||
|
|
fff550ca37 | ||
|
|
3c5c76eec4 | ||
|
|
ea05b09491 | ||
|
|
d6b8993eb3 | ||
|
|
0d61926a7e | ||
|
|
f8acf9278c | ||
|
|
23f6a778a5 | ||
|
|
e6e950e58c | ||
|
|
7162a19982 | ||
|
|
5a77dd8a96 | ||
|
|
4e89546923 | ||
|
|
0429a689a4 | ||
|
|
b7239b611a | ||
|
|
057df4d35c | ||
|
|
886a07896b | ||
|
|
60857b55f1 | ||
|
|
160590149f | ||
|
|
98c6ab5aac | ||
|
|
7c18cd1009 | ||
|
|
27e53bc363 | ||
|
|
f0e5c6efe7 | ||
|
|
cbd86d3810 | ||
|
|
6f9a2dd57f | ||
|
|
7f45bb22f5 | ||
|
|
9137ed6278 | ||
|
|
3cda6109e9 | ||
|
|
0ce6f07064 | ||
|
|
6bbad0a399 | ||
|
|
8c371cf8af | ||
|
|
d74e484752 | ||
|
|
8f315d9311 | ||
|
|
4a63f14c75 | ||
|
|
e42f7426ee | ||
|
|
e96a1d6bde | ||
|
|
d08805dca6 | ||
|
|
1ca268e36a | ||
|
|
865afb2f9a | ||
|
|
44f448493c | ||
|
|
cee5f769b6 | ||
|
|
ea05d5635a | ||
|
|
984f8b2dd9 | ||
|
|
737f9b3d1e | ||
|
|
8b3158de99 | ||
|
|
cf054c401e | ||
|
|
3a34fa7a8f | ||
|
|
9438f91547 | ||
|
|
157b694727 | ||
|
|
3caf2fc62e | ||
|
|
e3b144a374 | ||
|
|
f7987b5c3e |
25
.gitattributes
vendored
Normal file
25
.gitattributes
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
* text=auto
|
||||
|
||||
# Use CRLF for certain Windows files.
|
||||
*.vcproj eol=crlf
|
||||
*.sln eol=crlf
|
||||
*.bat eol=crlf
|
||||
README-WINDOWS.txt eol=crlf
|
||||
nzbget-setup.nsi eol=crlf
|
||||
windows/package-info.json eol=crlf
|
||||
windows/resources/resource.h eol=crlf
|
||||
windows/resources/nzbget.rc eol=crlf
|
||||
|
||||
# Configure GitHub's language detector
|
||||
lib/* linguist-vendored linguist-language=C++
|
||||
webui/lib/* linguist-vendored
|
||||
Makefile.in linguist-vendored
|
||||
configure linguist-vendored
|
||||
config.sub linguist-vendored
|
||||
aclocal.m4 linguist-vendored
|
||||
config.guess linguist-vendored
|
||||
depcomp linguist-vendored
|
||||
install-sh linguist-vendored
|
||||
missing linguist-vendored
|
||||
configure.ac linguist-vendored=false
|
||||
Makefile.am linguist-vendored=false
|
||||
63
.gitignore
vendored
Executable file
63
.gitignore
vendored
Executable file
@@ -0,0 +1,63 @@
|
||||
# Compiled Object files
|
||||
*.slo
|
||||
*.lo
|
||||
*.o
|
||||
*.obj
|
||||
|
||||
# Precompiled Headers
|
||||
*.gch
|
||||
*.pch
|
||||
|
||||
# Compiled Dynamic libraries
|
||||
*.so
|
||||
*.dylib
|
||||
*.dll
|
||||
|
||||
# Compiled Static libraries
|
||||
*.lai
|
||||
*.la
|
||||
*.a
|
||||
*.lib
|
||||
|
||||
# Executables
|
||||
*.exe
|
||||
*.out
|
||||
|
||||
# GNU Autotools
|
||||
.deps/
|
||||
config.h
|
||||
config.log
|
||||
config.status
|
||||
Makefile
|
||||
stamp-h1
|
||||
autom4te.cache/
|
||||
|
||||
# Visual Studio User-specific files
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
.vs/
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.ilk
|
||||
*.pch
|
||||
*.pdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
*.sln
|
||||
|
||||
# NZBGet specific
|
||||
nzbget
|
||||
code_revision.cpp
|
||||
4
AUTHORS
4
AUTHORS
@@ -1,4 +0,0 @@
|
||||
nzbget:
|
||||
Sven Henkel <sidddy@users.sourceforge.net> (versions 0.1.0 - ?)
|
||||
Bo Cordes Petersen <placebodk@users.sourceforge.net> (versions ? - 0.2.3)
|
||||
Andrei Prygounkov <hugbug@users.sourceforge.net> (versions 0.3.0 - 0.3.*)
|
||||
@@ -1,910 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef WIN32
|
||||
#include <direct.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "ArticleDownloader.h"
|
||||
#include "Decoder.h"
|
||||
#include "Log.h"
|
||||
#include "Options.h"
|
||||
#include "ServerPool.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern DownloadSpeedMeter* g_pDownloadSpeedMeter;
|
||||
extern Options* g_pOptions;
|
||||
extern ServerPool* g_pServerPool;
|
||||
|
||||
const char* ArticleDownloader::m_szJobStatus[] = { "WAITING", "RUNNING", "FINISHED", "FAILED", "DECODING", "JOINING", "NOT_FOUND", "FATAL_ERROR" };
|
||||
|
||||
ArticleDownloader::ArticleDownloader()
|
||||
{
|
||||
debug("Creating ArticleDownloader");
|
||||
|
||||
m_szResultFilename = NULL;
|
||||
m_szTempFilename = NULL;
|
||||
m_szArticleFilename = NULL;
|
||||
m_szInfoName = NULL;
|
||||
m_szOutputFilename = NULL;
|
||||
m_pConnection = NULL;
|
||||
m_eStatus = adUndefined;
|
||||
m_bDuplicate = false;
|
||||
SetLastUpdateTimeNow();
|
||||
}
|
||||
|
||||
ArticleDownloader::~ArticleDownloader()
|
||||
{
|
||||
debug("Destroying ArticleDownloader");
|
||||
|
||||
if (m_szTempFilename)
|
||||
{
|
||||
free(m_szTempFilename);
|
||||
}
|
||||
if (m_szArticleFilename)
|
||||
{
|
||||
free(m_szArticleFilename);
|
||||
}
|
||||
if (m_szInfoName)
|
||||
{
|
||||
free(m_szInfoName);
|
||||
}
|
||||
if (m_szOutputFilename)
|
||||
{
|
||||
free(m_szOutputFilename);
|
||||
}
|
||||
}
|
||||
|
||||
void ArticleDownloader::SetTempFilename(const char* v)
|
||||
{
|
||||
m_szTempFilename = strdup(v);
|
||||
}
|
||||
|
||||
void ArticleDownloader::SetOutputFilename(const char* v)
|
||||
{
|
||||
m_szOutputFilename = strdup(v);
|
||||
}
|
||||
|
||||
void ArticleDownloader::SetInfoName(const char * v)
|
||||
{
|
||||
m_szInfoName = strdup(v);
|
||||
}
|
||||
|
||||
void ArticleDownloader::SetStatus(EStatus eStatus)
|
||||
{
|
||||
m_eStatus = eStatus;
|
||||
Notify(NULL);
|
||||
}
|
||||
|
||||
void ArticleDownloader::Run()
|
||||
{
|
||||
debug("Entering ArticleDownloader-loop");
|
||||
|
||||
SetStatus(adRunning);
|
||||
m_szResultFilename = m_pArticleInfo->GetResultFilename();
|
||||
|
||||
if (g_pOptions->GetContinuePartial())
|
||||
{
|
||||
struct stat buffer;
|
||||
bool fileExists = !stat(m_szResultFilename, &buffer);
|
||||
if (fileExists)
|
||||
{
|
||||
// file exists from previous program's start
|
||||
info("Article %s already downloaded, skipping", m_szInfoName);
|
||||
SetStatus(adFinished);
|
||||
FreeConnection(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
info("Downloading %s", m_szInfoName);
|
||||
|
||||
int retry = g_pOptions->GetRetries();
|
||||
|
||||
EStatus Status = adFailed;
|
||||
int iMaxLevel = g_pServerPool->GetMaxLevel();
|
||||
int* LevelStatus = (int*)malloc((iMaxLevel + 1) * sizeof(int));
|
||||
for (int i = 0; i <= iMaxLevel; i++)
|
||||
{
|
||||
LevelStatus[i] = 0;
|
||||
}
|
||||
int level = 0;
|
||||
|
||||
while (!IsStopped() && (retry > 0))
|
||||
{
|
||||
SetLastUpdateTimeNow();
|
||||
|
||||
Status = adFailed;
|
||||
|
||||
if (!m_pConnection)
|
||||
{
|
||||
m_pConnection = g_pServerPool->GetConnection(level, true);
|
||||
}
|
||||
|
||||
if (IsStopped())
|
||||
{
|
||||
Status = adFailed;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!m_pConnection)
|
||||
{
|
||||
debug("m_pConnection is NULL");
|
||||
error("Serious error: Connection is NULL");
|
||||
}
|
||||
|
||||
// test connection
|
||||
bool connected = m_pConnection && m_pConnection->Connect() >= 0;
|
||||
if (connected && !IsStopped())
|
||||
{
|
||||
// Okay, we got a Connection. Now start downloading!!
|
||||
Status = Download();
|
||||
}
|
||||
|
||||
bool bAuthError = m_pConnection && m_pConnection->GetAuthError();
|
||||
|
||||
if (connected)
|
||||
{
|
||||
// freeing connection allows other threads to start.
|
||||
// we doing this only if the problem was with article or group.
|
||||
// if the problem occurs by Connect() we do not free the connection,
|
||||
// to prevent starting of thousands of threads (cause each of them
|
||||
// will also free it's connection after the same connect-error).
|
||||
|
||||
FreeConnection(Status == adFinished);
|
||||
}
|
||||
|
||||
if ((Status == adFailed || (Status == adCrcError && g_pOptions->GetRetryOnCrcError())) &&
|
||||
((retry > 1) || !connected || bAuthError) && !IsStopped())
|
||||
{
|
||||
info("Waiting %i sec to retry", g_pOptions->GetRetryInterval());
|
||||
int msec = 0;
|
||||
while (!IsStopped() && (msec < g_pOptions->GetRetryInterval() * 1000))
|
||||
{
|
||||
usleep(100 * 1000);
|
||||
msec += 100;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsStopped())
|
||||
{
|
||||
Status = adFailed;
|
||||
break;
|
||||
}
|
||||
|
||||
if ((Status == adFinished) || (Status == adFatalError) ||
|
||||
(Status == adCrcError && !g_pOptions->GetRetryOnCrcError()))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
LevelStatus[level] = Status;
|
||||
|
||||
bool bAllLevelNotFound = true;
|
||||
for (int lev = 0; lev <= iMaxLevel; lev++)
|
||||
{
|
||||
if (LevelStatus[lev] != adNotFound)
|
||||
{
|
||||
bAllLevelNotFound = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (bAllLevelNotFound)
|
||||
{
|
||||
if (iMaxLevel > 0)
|
||||
{
|
||||
warn("Article %s @ all servers failed: Article not found", m_szInfoName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// do not count connect-errors, only article- and group-errors
|
||||
if (connected && !bAuthError)
|
||||
{
|
||||
level++;
|
||||
if (level > iMaxLevel)
|
||||
{
|
||||
level = 0;
|
||||
}
|
||||
retry--;
|
||||
}
|
||||
}
|
||||
|
||||
FreeConnection(Status == adFinished);
|
||||
|
||||
free(LevelStatus);
|
||||
|
||||
if (m_bDuplicate)
|
||||
{
|
||||
Status = adFinished;
|
||||
}
|
||||
|
||||
if (Status != adFinished)
|
||||
{
|
||||
Status = adFailed;
|
||||
}
|
||||
|
||||
if (Status == adFailed)
|
||||
{
|
||||
if (IsStopped())
|
||||
{
|
||||
info("Download %s cancelled", m_szInfoName);
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Download %s failed", m_szInfoName);
|
||||
}
|
||||
}
|
||||
|
||||
SetStatus(Status);
|
||||
|
||||
debug("Exiting ArticleDownloader-loop");
|
||||
}
|
||||
|
||||
ArticleDownloader::EStatus ArticleDownloader::Download()
|
||||
{
|
||||
// at first, change group
|
||||
bool grpchanged = false;
|
||||
for (FileInfo::Groups::iterator it = m_pFileInfo->GetGroups()->begin(); it != m_pFileInfo->GetGroups()->end(); it++)
|
||||
{
|
||||
grpchanged = m_pConnection->JoinGroup(*it);
|
||||
if (grpchanged)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!grpchanged)
|
||||
{
|
||||
if (!m_pConnection->GetAuthError() && !IsStopped())
|
||||
{
|
||||
warn("Article %s @ %s failed: Could not join group", m_szInfoName, m_pConnection->GetServer()->GetHost());
|
||||
}
|
||||
return adFailed;
|
||||
}
|
||||
|
||||
// now, let's begin!
|
||||
char tmp[1024];
|
||||
snprintf(tmp, 1024, "ARTICLE %s\r\n", m_pArticleInfo->GetMessageID());
|
||||
tmp[1024-1] = '\0';
|
||||
|
||||
char* answer = NULL;
|
||||
|
||||
for (int retry = 3; retry > 0; retry--)
|
||||
{
|
||||
answer = m_pConnection->Request(tmp);
|
||||
if (answer && !strncmp(answer, "2", 1))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!answer)
|
||||
{
|
||||
if (!m_pConnection->GetAuthError() && !IsStopped())
|
||||
{
|
||||
warn("Article %s @ %s failed: Connection closed by remote host", m_szInfoName, m_pConnection->GetServer()->GetHost());
|
||||
}
|
||||
return adFailed;
|
||||
}
|
||||
|
||||
if (strncmp(answer, "2", 1))
|
||||
{
|
||||
warn("Article %s @ %s failed: %s", m_szInfoName, m_pConnection->GetServer()->GetHost(), answer);
|
||||
return (!strncmp(answer, "41", 2) || !strncmp(answer, "42", 2)) ? adNotFound : adFailed;
|
||||
}
|
||||
|
||||
// positive answer!
|
||||
|
||||
if (g_pOptions->GetDecoder() == Options::dcYenc)
|
||||
{
|
||||
m_YDecoder.Clear();
|
||||
m_YDecoder.SetAutoSeek(g_pOptions->GetDirectWrite());
|
||||
m_YDecoder.SetCrcCheck(g_pOptions->GetCrcCheck());
|
||||
}
|
||||
|
||||
m_pOutFile = NULL;
|
||||
EStatus Status = adRunning;
|
||||
bool bBody = false;
|
||||
const int LineBufSize = 1024*10;
|
||||
char* szLineBuf = (char*)malloc(LineBufSize);
|
||||
|
||||
while (!IsStopped())
|
||||
{
|
||||
SetLastUpdateTimeNow();
|
||||
|
||||
// Throttle the bandwidth
|
||||
while (!IsStopped() && (g_pOptions->GetDownloadRate() > 0.0f) &&
|
||||
(g_pDownloadSpeedMeter->CalcCurrentDownloadSpeed() > g_pOptions->GetDownloadRate()))
|
||||
{
|
||||
SetLastUpdateTimeNow();
|
||||
usleep(200 * 1000);
|
||||
}
|
||||
|
||||
int iLen = 0;
|
||||
char* line = m_pConnection->ReadLine(szLineBuf, LineBufSize, &iLen);
|
||||
g_pDownloadSpeedMeter->AddSpeedReading(iLen);
|
||||
|
||||
// Have we encountered a timeout?
|
||||
if (!line)
|
||||
{
|
||||
warn("Article %s @ %s failed: Unexpected end of article", m_szInfoName, m_pConnection->GetServer()->GetHost());
|
||||
Status = adFailed;
|
||||
break;
|
||||
}
|
||||
|
||||
//detect end of article
|
||||
if ((!strcmp(line, ".\r\n")) || (!strcmp(line, ".\n")))
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
//detect lines starting with "." (marked as "..")
|
||||
if (!strncmp(line, "..", 2))
|
||||
{
|
||||
line++;
|
||||
}
|
||||
|
||||
// check id of returned article
|
||||
if (!bBody)
|
||||
{
|
||||
if ((!strcmp(line, "\r\n")) || (!strcmp(line, "\n")))
|
||||
{
|
||||
bBody = true;
|
||||
}
|
||||
else if (!strncmp(line, "Message-ID: ", 12))
|
||||
{
|
||||
char* p = line + 12;
|
||||
if (strncmp(p, m_pArticleInfo->GetMessageID(), strlen(m_pArticleInfo->GetMessageID())))
|
||||
{
|
||||
if (char* e = strrchr(p, '\r')) *e = '\0'; // remove trailing CR-character
|
||||
warn("Article %s @ %s failed: Wrong message-id, expected %s, returned %s", m_szInfoName, m_pConnection->GetServer()->GetHost(), m_pArticleInfo->GetMessageID(), p);
|
||||
Status = adFailed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!Write(line, iLen))
|
||||
{
|
||||
Status = adFatalError;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
free(szLineBuf);
|
||||
|
||||
if (m_pOutFile)
|
||||
{
|
||||
fclose(m_pOutFile);
|
||||
}
|
||||
|
||||
if (IsStopped())
|
||||
{
|
||||
remove(m_szTempFilename);
|
||||
return adFailed;
|
||||
}
|
||||
|
||||
if (Status == adFailed)
|
||||
{
|
||||
remove(m_szTempFilename);
|
||||
return adFailed;
|
||||
}
|
||||
|
||||
if (Status == adRunning)
|
||||
{
|
||||
FreeConnection(true);
|
||||
return Decode();
|
||||
}
|
||||
else
|
||||
{
|
||||
return Status;
|
||||
}
|
||||
}
|
||||
|
||||
bool ArticleDownloader::Write(char* szLine, int iLen)
|
||||
{
|
||||
if (!m_pOutFile && !PrepareFile(szLine))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (g_pOptions->GetDecoder() == Options::dcYenc)
|
||||
{
|
||||
return m_YDecoder.Write(szLine, m_pOutFile);
|
||||
}
|
||||
else
|
||||
{
|
||||
return fwrite(szLine, 1, iLen, m_pOutFile) > 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool ArticleDownloader::PrepareFile(char* szLine)
|
||||
{
|
||||
bool bOpen = false;
|
||||
|
||||
// prepare file for writing
|
||||
if (g_pOptions->GetDecoder() == Options::dcYenc)
|
||||
{
|
||||
if (!strncmp(szLine, "=ybegin part=", 13))
|
||||
{
|
||||
if (g_pOptions->GetDupeCheck())
|
||||
{
|
||||
m_pFileInfo->LockOutputFile();
|
||||
if (!m_pFileInfo->GetOutputInitialized())
|
||||
{
|
||||
char* pb = strstr(szLine, "name=");
|
||||
if (pb)
|
||||
{
|
||||
pb += 5; //=strlen("name=")
|
||||
char* pe;
|
||||
for (pe = pb; *pe != '\0' && *pe != '\n' && *pe != '\r'; pe++) ;
|
||||
if (!m_szArticleFilename)
|
||||
{
|
||||
m_szArticleFilename = (char*)malloc(pe - pb + 1);
|
||||
strncpy(m_szArticleFilename, pb, pe - pb);
|
||||
m_szArticleFilename[pe - pb] = '\0';
|
||||
}
|
||||
if (m_pFileInfo->IsDupe(m_szArticleFilename))
|
||||
{
|
||||
m_bDuplicate = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!g_pOptions->GetDirectWrite())
|
||||
{
|
||||
m_pFileInfo->SetOutputInitialized(true);
|
||||
}
|
||||
m_pFileInfo->UnlockOutputFile();
|
||||
}
|
||||
|
||||
if (g_pOptions->GetDirectWrite())
|
||||
{
|
||||
char* pb = strstr(szLine, "size=");
|
||||
if (pb)
|
||||
{
|
||||
m_pFileInfo->LockOutputFile();
|
||||
if (!m_pFileInfo->GetOutputInitialized())
|
||||
{
|
||||
pb += 5; //=strlen("size=")
|
||||
long iArticleFilesize = atol(pb);
|
||||
if (!SetFileSize(m_szOutputFilename, iArticleFilesize))
|
||||
{
|
||||
error("Could not create file %s!", m_szOutputFilename);
|
||||
return false;
|
||||
}
|
||||
m_pFileInfo->SetOutputInitialized(true);
|
||||
}
|
||||
m_pFileInfo->UnlockOutputFile();
|
||||
bOpen = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bOpen = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
bOpen = true;
|
||||
}
|
||||
|
||||
if (bOpen)
|
||||
{
|
||||
const char* szFilename = g_pOptions->GetDirectWrite() ? m_szOutputFilename : m_szTempFilename;
|
||||
m_pOutFile = fopen(szFilename, g_pOptions->GetDirectWrite() ? "r+" : "w");
|
||||
if (!m_pOutFile)
|
||||
{
|
||||
error("Could not %s file %s", g_pOptions->GetDirectWrite() ? "open" : "create", szFilename);
|
||||
return false;
|
||||
}
|
||||
if (g_pOptions->GetWriteBufferSize() == -1)
|
||||
{
|
||||
setvbuf(m_pOutFile, (char *)NULL, _IOFBF, m_pArticleInfo->GetSize());
|
||||
}
|
||||
else if (g_pOptions->GetWriteBufferSize() > 0)
|
||||
{
|
||||
setvbuf(m_pOutFile, (char *)NULL, _IOFBF, g_pOptions->GetWriteBufferSize());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ArticleDownloader::EStatus ArticleDownloader::Decode()
|
||||
{
|
||||
if ((g_pOptions->GetDecoder() == Options::dcUulib) ||
|
||||
(g_pOptions->GetDecoder() == Options::dcYenc))
|
||||
{
|
||||
SetStatus(adDecoding);
|
||||
|
||||
char tmpdestfile[1024];
|
||||
char* szDecoderTempFilename = NULL;
|
||||
Decoder* pDecoder = NULL;
|
||||
|
||||
if (g_pOptions->GetDecoder() == Options::dcYenc)
|
||||
{
|
||||
pDecoder = &m_YDecoder;
|
||||
szDecoderTempFilename = m_szTempFilename;
|
||||
}
|
||||
else if (g_pOptions->GetDecoder() == Options::dcUulib)
|
||||
{
|
||||
pDecoder = new UULibDecoder();
|
||||
pDecoder->SetSrcFilename(m_szTempFilename);
|
||||
snprintf(tmpdestfile, 1024, "%s.dec", m_szResultFilename);
|
||||
tmpdestfile[1024-1] = '\0';
|
||||
szDecoderTempFilename = tmpdestfile;
|
||||
pDecoder->SetDestFilename(szDecoderTempFilename);
|
||||
}
|
||||
|
||||
bool bOK = pDecoder->Execute();
|
||||
|
||||
if (!g_pOptions->GetDirectWrite())
|
||||
{
|
||||
if (bOK)
|
||||
{
|
||||
rename(szDecoderTempFilename, m_szResultFilename);
|
||||
}
|
||||
else if (g_pOptions->GetDecoder() == Options::dcUulib)
|
||||
{
|
||||
remove(szDecoderTempFilename);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_szArticleFilename && pDecoder->GetArticleFilename())
|
||||
{
|
||||
m_szArticleFilename = strdup(pDecoder->GetArticleFilename());
|
||||
}
|
||||
|
||||
remove(m_szTempFilename);
|
||||
bool bCrcError = pDecoder->GetCrcError();
|
||||
if (pDecoder != &m_YDecoder)
|
||||
{
|
||||
delete pDecoder;
|
||||
}
|
||||
|
||||
if (bOK)
|
||||
{
|
||||
info("Successfully downloaded %s", m_szInfoName);
|
||||
|
||||
if (g_pOptions->GetDirectWrite() && g_pOptions->GetContinuePartial())
|
||||
{
|
||||
// create empty flag-file to indicate that the artcile was downloaded
|
||||
FILE* flagfile = fopen(m_szResultFilename, "w");
|
||||
if (!flagfile)
|
||||
{
|
||||
error("Could not create file %s", m_szResultFilename);
|
||||
// this error can be ignored
|
||||
}
|
||||
fclose(flagfile);
|
||||
}
|
||||
|
||||
return adFinished;
|
||||
}
|
||||
else
|
||||
{
|
||||
remove(m_szResultFilename);
|
||||
if (bCrcError)
|
||||
{
|
||||
warn("Decoding %s failed: CRC-Error", m_szInfoName);
|
||||
return adCrcError;
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Decoding %s failed", m_szInfoName);
|
||||
return adFailed;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (g_pOptions->GetDecoder() == Options::dcNone)
|
||||
{
|
||||
// rawmode
|
||||
rename(m_szTempFilename, m_szResultFilename);
|
||||
info("Article %s successfully downloaded", m_szInfoName);
|
||||
return adFinished;
|
||||
}
|
||||
else
|
||||
{
|
||||
// should not occur
|
||||
error("Internal error: Decoding %s failed", m_szInfoName);
|
||||
return adFatalError;
|
||||
}
|
||||
}
|
||||
|
||||
void ArticleDownloader::LogDebugInfo()
|
||||
{
|
||||
char szTime[50];
|
||||
#ifdef HAVE_CTIME_R_3
|
||||
ctime_r(&m_tLastUpdateTime, szTime, 50);
|
||||
#else
|
||||
ctime_r(&m_tLastUpdateTime, szTime);
|
||||
#endif
|
||||
|
||||
debug(" Download: status=%s, LastUpdateTime=%s, filename=%s", GetStatusText(), szTime, BaseFileName(GetTempFilename()));
|
||||
}
|
||||
|
||||
void ArticleDownloader::Stop()
|
||||
{
|
||||
debug("Trying to stop ArticleDownloader");
|
||||
Thread::Stop();
|
||||
m_mutexConnection.Lock();
|
||||
if (m_pConnection)
|
||||
{
|
||||
m_pConnection->Cancel();
|
||||
}
|
||||
m_mutexConnection.Unlock();
|
||||
debug("ArticleDownloader stopped successfully");
|
||||
}
|
||||
|
||||
bool ArticleDownloader::Terminate()
|
||||
{
|
||||
NNTPConnection* pConnection = m_pConnection;
|
||||
bool terminated = Kill();
|
||||
if (terminated && pConnection)
|
||||
{
|
||||
debug("Terminating connection");
|
||||
pConnection->Cancel();
|
||||
pConnection->Disconnect();
|
||||
g_pServerPool->FreeConnection(pConnection, true);
|
||||
}
|
||||
return terminated;
|
||||
}
|
||||
|
||||
void ArticleDownloader::FreeConnection(bool bKeepConnected)
|
||||
{
|
||||
if (m_pConnection)
|
||||
{
|
||||
debug("Releasing connection");
|
||||
m_mutexConnection.Lock();
|
||||
if (!bKeepConnected || m_pConnection->GetStatus() == Connection::csCancelled)
|
||||
{
|
||||
m_pConnection->Disconnect();
|
||||
}
|
||||
g_pServerPool->FreeConnection(m_pConnection, true);
|
||||
m_pConnection = NULL;
|
||||
m_mutexConnection.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
void ArticleDownloader::CompleteFileParts()
|
||||
{
|
||||
debug("Completing file parts");
|
||||
debug("ArticleFilename: %s", m_pFileInfo->GetFilename());
|
||||
|
||||
SetStatus(adJoining);
|
||||
|
||||
char szNZBNiceName[1024];
|
||||
m_pFileInfo->GetNiceNZBName(szNZBNiceName, 1024);
|
||||
|
||||
char InfoFilename[1024];
|
||||
snprintf(InfoFilename, 1024, "%s%c%s", szNZBNiceName, (int)PATH_SEPARATOR, m_pFileInfo->GetFilename());
|
||||
InfoFilename[1024-1] = '\0';
|
||||
|
||||
if (g_pOptions->GetDecoder() == Options::dcNone)
|
||||
{
|
||||
info("Moving articles for %s", InfoFilename);
|
||||
}
|
||||
else if (g_pOptions->GetDirectWrite())
|
||||
{
|
||||
info("Checking articles for %s", InfoFilename);
|
||||
}
|
||||
else
|
||||
{
|
||||
info("Joining articles for %s", InfoFilename);
|
||||
}
|
||||
|
||||
char ofn[1024];
|
||||
snprintf(ofn, 1024, "%s%c%s", m_pFileInfo->GetDestDir(), (int)PATH_SEPARATOR, m_pFileInfo->GetFilename());
|
||||
ofn[1024-1] = '\0';
|
||||
|
||||
// Ensure the DstDir is created
|
||||
mkdir(m_pFileInfo->GetDestDir(), S_DIRMODE);
|
||||
|
||||
// prevent overwriting existing files
|
||||
struct stat statbuf;
|
||||
int dupcount = 0;
|
||||
while (!stat(ofn, &statbuf))
|
||||
{
|
||||
dupcount++;
|
||||
snprintf(ofn, 1024, "%s%c%s_duplicate%d", m_pFileInfo->GetDestDir(), (int)PATH_SEPARATOR, m_pFileInfo->GetFilename(), dupcount);
|
||||
ofn[1024-1] = '\0';
|
||||
}
|
||||
|
||||
FILE* outfile = NULL;
|
||||
char tmpdestfile[1024];
|
||||
snprintf(tmpdestfile, 1024, "%s.tmp", ofn);
|
||||
tmpdestfile[1024-1] = '\0';
|
||||
|
||||
if (((g_pOptions->GetDecoder() == Options::dcUulib) ||
|
||||
(g_pOptions->GetDecoder() == Options::dcYenc)) &&
|
||||
!g_pOptions->GetDirectWrite())
|
||||
{
|
||||
remove(tmpdestfile);
|
||||
outfile = fopen(tmpdestfile, "w+");
|
||||
if (!outfile)
|
||||
{
|
||||
error("Could not create file %s!", tmpdestfile);
|
||||
SetStatus(adFinished);
|
||||
return;
|
||||
}
|
||||
if (g_pOptions->GetWriteBufferSize() == -1 && (*m_pFileInfo->GetArticles())[0])
|
||||
{
|
||||
setvbuf(outfile, (char *)NULL, _IOFBF, (*m_pFileInfo->GetArticles())[0]->GetSize());
|
||||
}
|
||||
else if (g_pOptions->GetWriteBufferSize() > 0)
|
||||
{
|
||||
setvbuf(outfile, (char *)NULL, _IOFBF, g_pOptions->GetWriteBufferSize());
|
||||
}
|
||||
}
|
||||
else if (g_pOptions->GetDecoder() == Options::dcNone)
|
||||
{
|
||||
remove(tmpdestfile);
|
||||
mkdir(ofn, S_DIRMODE);
|
||||
}
|
||||
|
||||
bool complete = true;
|
||||
int iBrokenCount = 0;
|
||||
static const int BUFFER_SIZE = 1024 * 50;
|
||||
char* buffer = NULL;
|
||||
|
||||
if (((g_pOptions->GetDecoder() == Options::dcUulib) ||
|
||||
(g_pOptions->GetDecoder() == Options::dcYenc)) &&
|
||||
!g_pOptions->GetDirectWrite())
|
||||
{
|
||||
buffer = (char*)malloc(BUFFER_SIZE);
|
||||
}
|
||||
|
||||
for (FileInfo::Articles::iterator it = m_pFileInfo->GetArticles()->begin(); it != m_pFileInfo->GetArticles()->end(); it++)
|
||||
{
|
||||
ArticleInfo* pa = *it;
|
||||
if (pa->GetStatus() != ArticleInfo::aiFinished)
|
||||
{
|
||||
iBrokenCount++;
|
||||
complete = false;
|
||||
}
|
||||
else if (((g_pOptions->GetDecoder() == Options::dcUulib) ||
|
||||
(g_pOptions->GetDecoder() == Options::dcYenc)) &&
|
||||
!g_pOptions->GetDirectWrite())
|
||||
{
|
||||
FILE* infile;
|
||||
const char* fn = pa->GetResultFilename();
|
||||
|
||||
infile = fopen(fn, "r");
|
||||
if (infile)
|
||||
{
|
||||
int cnt = BUFFER_SIZE;
|
||||
|
||||
while (cnt == BUFFER_SIZE)
|
||||
{
|
||||
cnt = (int)fread(buffer, 1, BUFFER_SIZE, infile);
|
||||
fwrite(buffer, 1, cnt, outfile);
|
||||
SetLastUpdateTimeNow();
|
||||
}
|
||||
|
||||
fclose(infile);
|
||||
}
|
||||
else
|
||||
{
|
||||
complete = false;
|
||||
iBrokenCount++;
|
||||
info("Could not find file %s. Status is broken", fn);
|
||||
}
|
||||
}
|
||||
else if (g_pOptions->GetDecoder() == Options::dcNone)
|
||||
{
|
||||
const char* fn = pa->GetResultFilename();
|
||||
char dstFileName[1024];
|
||||
snprintf(dstFileName, 1024, "%s%c%03i", ofn, (int)PATH_SEPARATOR, pa->GetPartNumber());
|
||||
dstFileName[1024-1] = '\0';
|
||||
rename(fn, dstFileName);
|
||||
}
|
||||
}
|
||||
|
||||
if (buffer)
|
||||
{
|
||||
free(buffer);
|
||||
}
|
||||
|
||||
if (outfile)
|
||||
{
|
||||
fclose(outfile);
|
||||
rename(tmpdestfile, ofn);
|
||||
}
|
||||
|
||||
if (g_pOptions->GetDirectWrite())
|
||||
{
|
||||
rename(m_szOutputFilename, ofn);
|
||||
}
|
||||
|
||||
if (!g_pOptions->GetDirectWrite() || g_pOptions->GetContinuePartial())
|
||||
{
|
||||
for (FileInfo::Articles::iterator it = m_pFileInfo->GetArticles()->begin(); it != m_pFileInfo->GetArticles()->end(); it++)
|
||||
{
|
||||
ArticleInfo* pa = *it;
|
||||
remove(pa->GetResultFilename());
|
||||
}
|
||||
}
|
||||
|
||||
if (complete)
|
||||
{
|
||||
info("Successfully downloaded %s", InfoFilename);
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("%i of %i article downloads failed for \"%s\"", iBrokenCount, m_pFileInfo->GetArticles()->size(), InfoFilename);
|
||||
|
||||
if (g_pOptions->GetRenameBroken())
|
||||
{
|
||||
char brokenfn[1024];
|
||||
snprintf(brokenfn, 1024, "%s_broken", ofn);
|
||||
brokenfn[1024-1] = '\0';
|
||||
bool OK = rename(ofn, brokenfn) == 0;
|
||||
if (OK)
|
||||
{
|
||||
info("Renaming broken file from %s to %s", ofn, brokenfn);
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Renaming broken file from %s to %s failed", ofn, brokenfn);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
info("Not renaming broken file %s", ofn);
|
||||
}
|
||||
|
||||
if (g_pOptions->GetCreateBrokenLog())
|
||||
{
|
||||
char szBrokenLogName[1024];
|
||||
snprintf(szBrokenLogName, 1024, "%s%c_brokenlog.txt", m_pFileInfo->GetDestDir(), (int)PATH_SEPARATOR);
|
||||
szBrokenLogName[1024-1] = '\0';
|
||||
FILE* file = fopen(szBrokenLogName, "a");
|
||||
fprintf(file, "%s (%i/%i)\n", m_pFileInfo->GetFilename(), m_pFileInfo->GetArticles()->size() - iBrokenCount, m_pFileInfo->GetArticles()->size());
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
warn("%s is incomplete!", InfoFilename);
|
||||
}
|
||||
|
||||
SetStatus(adFinished);
|
||||
}
|
||||
542
Connection.cpp
542
Connection.cpp
@@ -1,542 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef WIN32
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Connection.h"
|
||||
#include "Log.h"
|
||||
|
||||
static const int CONNECTION_READBUFFER_SIZE = 1024;
|
||||
|
||||
void Connection::Init()
|
||||
{
|
||||
debug("Intiializing global connection data");
|
||||
|
||||
#ifdef WIN32
|
||||
WSADATA wsaData;
|
||||
int err = WSAStartup(MAKEWORD(2, 0), &wsaData);
|
||||
if (err != 0)
|
||||
{
|
||||
error("Could not initialize socket library");
|
||||
return;
|
||||
}
|
||||
if (LOBYTE(wsaData.wVersion) != 2 || HIBYTE( wsaData.wVersion ) != 0)
|
||||
{
|
||||
error("Could not initialize socket library");
|
||||
WSACleanup();
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Connection::Final()
|
||||
{
|
||||
debug("Finalizing global connection data");
|
||||
|
||||
#ifdef WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Connection::Connection(NetAddress* pNetAddress)
|
||||
{
|
||||
debug("Creating Connection");
|
||||
|
||||
m_pNetAddress = pNetAddress;
|
||||
m_eStatus = csDisconnected;
|
||||
m_iSocket = INVALID_SOCKET;
|
||||
m_iBufAvail = 0;
|
||||
m_iTimeout = 60;
|
||||
m_bSuppressErrors = true;
|
||||
m_szReadBuf = (char*)malloc(CONNECTION_READBUFFER_SIZE + 1);
|
||||
}
|
||||
|
||||
Connection::~Connection()
|
||||
{
|
||||
debug("Destroying Connection");
|
||||
|
||||
if (m_eStatus == csConnected)
|
||||
{
|
||||
Disconnect();
|
||||
}
|
||||
free(m_szReadBuf);
|
||||
}
|
||||
|
||||
int Connection::Connect()
|
||||
{
|
||||
debug("Connecting");
|
||||
|
||||
if (m_eStatus == csConnected)
|
||||
return 0;
|
||||
|
||||
int iRes = DoConnect();
|
||||
|
||||
if (iRes >= 0)
|
||||
m_eStatus = csConnected;
|
||||
else
|
||||
Connection::DoDisconnect();
|
||||
|
||||
return iRes;
|
||||
}
|
||||
|
||||
int Connection::Disconnect()
|
||||
{
|
||||
debug("Disconnecting");
|
||||
|
||||
if (m_eStatus == csDisconnected)
|
||||
return 0;
|
||||
|
||||
int iRes = DoDisconnect();
|
||||
|
||||
m_eStatus = csDisconnected;
|
||||
m_iSocket = INVALID_SOCKET;
|
||||
m_iBufAvail = 0;
|
||||
|
||||
return iRes;
|
||||
}
|
||||
|
||||
int Connection::Bind()
|
||||
{
|
||||
debug("Binding");
|
||||
|
||||
if (m_eStatus == csListening)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int iRes = DoBind();
|
||||
|
||||
if (iRes == 0)
|
||||
{
|
||||
m_eStatus = csListening;
|
||||
}
|
||||
|
||||
return iRes;
|
||||
}
|
||||
|
||||
int Connection::WriteLine(char* line)
|
||||
{
|
||||
//debug("Connection::write(char* line)");
|
||||
|
||||
if (m_eStatus != csConnected)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int iRes = DoWriteLine(line);
|
||||
|
||||
return iRes;
|
||||
}
|
||||
|
||||
int Connection::Send(char* pBuffer, int iSize)
|
||||
{
|
||||
debug("Sending data");
|
||||
|
||||
if (m_eStatus != csConnected)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
int iRes = send(m_iSocket, pBuffer, iSize, 0);
|
||||
|
||||
return iRes;
|
||||
}
|
||||
|
||||
char* Connection::ReadLine(char* pBuffer, int iSize, int* pBytesRead)
|
||||
{
|
||||
if (m_eStatus != csConnected)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* res = DoReadLine(pBuffer, iSize, pBytesRead);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
SOCKET Connection::Accept()
|
||||
{
|
||||
debug("Accepting connection");
|
||||
|
||||
if (m_eStatus != csListening)
|
||||
{
|
||||
return INVALID_SOCKET;
|
||||
}
|
||||
|
||||
SOCKET iRes = DoAccept();
|
||||
|
||||
return iRes;
|
||||
}
|
||||
|
||||
int Connection::Recv(char* pBuffer, int iSize)
|
||||
{
|
||||
debug("Receiving data");
|
||||
|
||||
memset(pBuffer, 0, iSize);
|
||||
|
||||
int iReceived = recv(m_iSocket, pBuffer, iSize, 0);
|
||||
|
||||
if (iReceived < 0)
|
||||
{
|
||||
ReportError("Could not receive data on socket", NULL, 0);
|
||||
}
|
||||
|
||||
return iReceived;
|
||||
}
|
||||
|
||||
bool Connection::RecvAll(char * pBuffer, int iSize)
|
||||
{
|
||||
debug("Receiving data (full buffer)");
|
||||
|
||||
memset(pBuffer, 0, iSize);
|
||||
|
||||
char* pBufPtr = (char*)pBuffer;
|
||||
int NeedBytes = iSize;
|
||||
// Read from the socket until nothing remains
|
||||
while (NeedBytes > 0)
|
||||
{
|
||||
int iReceived = recv(m_iSocket, pBufPtr, NeedBytes, 0);
|
||||
// Did the recv succeed?
|
||||
if (iReceived <= 0)
|
||||
{
|
||||
ReportError("Could not receive data on socket", NULL, 0);
|
||||
return false;
|
||||
}
|
||||
pBufPtr += iReceived;
|
||||
NeedBytes -= iReceived;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int Connection::DoConnect()
|
||||
{
|
||||
debug("Do connecting");
|
||||
|
||||
struct sockaddr_in sSocketAddress;
|
||||
memset(&sSocketAddress, '\0', sizeof(sSocketAddress));
|
||||
sSocketAddress.sin_family = AF_INET;
|
||||
sSocketAddress.sin_port = htons(m_pNetAddress->GetPort());
|
||||
sSocketAddress.sin_addr.s_addr = ResolveHostAddr(m_pNetAddress->GetHost());
|
||||
if (sSocketAddress.sin_addr.s_addr == (unsigned int)-1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
m_iSocket = socket(PF_INET, SOCK_STREAM, 0);
|
||||
|
||||
if (m_iSocket == INVALID_SOCKET)
|
||||
{
|
||||
ReportError("Socket creation failed for %s!", m_pNetAddress->GetHost(), 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int res = connect(m_iSocket , (struct sockaddr *) & sSocketAddress, sizeof(sSocketAddress));
|
||||
|
||||
if (res < 0)
|
||||
{
|
||||
ReportError("Connection to %s failed!", m_pNetAddress->GetHost(), 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
int MSecVal = m_iTimeout * 1000;
|
||||
int err = setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&MSecVal, sizeof(MSecVal));
|
||||
#else
|
||||
struct timeval TimeVal;
|
||||
TimeVal.tv_sec = m_iTimeout;
|
||||
TimeVal.tv_usec = 0;
|
||||
int err = setsockopt(m_iSocket, SOL_SOCKET, SO_RCVTIMEO, (char*)&TimeVal, sizeof(TimeVal));
|
||||
#endif
|
||||
if (err != 0)
|
||||
{
|
||||
ReportError("setsockopt failed", NULL, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int Connection::ResolveHostAddr(const char* szHost)
|
||||
{
|
||||
unsigned int uaddr = inet_addr(szHost);
|
||||
if (uaddr == (unsigned int)-1)
|
||||
{
|
||||
struct hostent* hinfo;
|
||||
bool err = false;
|
||||
int h_errnop = 0;
|
||||
#ifdef WIN32
|
||||
hinfo = gethostbyname(szHost);
|
||||
err = hinfo == NULL;
|
||||
h_errnop = WSAGetLastError();
|
||||
#else
|
||||
struct hostent hinfobuf;
|
||||
static const int strbuflen = 1024;
|
||||
char* strbuf = (char*)malloc(strbuflen);
|
||||
#ifdef HAVE_GETHOSTBYNAME_R_6
|
||||
err = gethostbyname_r(szHost, &hinfobuf, strbuf, strbuflen, &hinfo, &h_errnop);
|
||||
err = err || (hinfo == NULL); // error on null hinfo (means 'no entry')
|
||||
#else
|
||||
hinfo = gethostbyname_r(szHost, &hinfobuf, strbuf, strbuflen, &h_errnop);
|
||||
err = hinfo == NULL;
|
||||
#endif
|
||||
#endif
|
||||
if (err)
|
||||
{
|
||||
ReportError("Could not resolve hostname %s", szHost, h_errnop);
|
||||
#ifndef WIN32
|
||||
free(strbuf);
|
||||
#endif
|
||||
return (unsigned int)-1;
|
||||
}
|
||||
|
||||
memcpy(&uaddr, hinfo->h_addr_list[0], sizeof(uaddr));
|
||||
#ifndef WIN32
|
||||
free(strbuf);
|
||||
#endif
|
||||
}
|
||||
return uaddr;
|
||||
}
|
||||
|
||||
int Connection::DoDisconnect()
|
||||
{
|
||||
debug("Do disconnecting");
|
||||
|
||||
if (m_iSocket > 0)
|
||||
{
|
||||
closesocket(m_iSocket);
|
||||
m_iSocket = INVALID_SOCKET;
|
||||
}
|
||||
|
||||
m_eStatus = csDisconnected;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Connection::DoWriteLine(char* szText)
|
||||
{
|
||||
//debug("Connection::doWrite()");
|
||||
return send(m_iSocket, szText, strlen(szText), 0);
|
||||
}
|
||||
|
||||
char* Connection::DoReadLine(char* pBuffer, int iSize, int* pBytesRead)
|
||||
{
|
||||
//debug( "Connection::DoReadLine()" );
|
||||
char* pBufPtr = pBuffer;
|
||||
iSize--; // for trailing '0'
|
||||
int iBytesRead = 0;
|
||||
int iBufAvail = m_iBufAvail; // local variable is faster
|
||||
char* szBufPtr = m_szBufPtr; // local variable is faster
|
||||
while (iSize)
|
||||
{
|
||||
if (!iBufAvail)
|
||||
{
|
||||
iBufAvail = recv(m_iSocket, m_szReadBuf, CONNECTION_READBUFFER_SIZE, 0);
|
||||
if (iBufAvail < 0)
|
||||
{
|
||||
ReportError("Could not receive data on socket", NULL, 0);
|
||||
break;
|
||||
}
|
||||
else if (iBufAvail == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
szBufPtr = m_szReadBuf;
|
||||
m_szReadBuf[iBufAvail] = '\0';
|
||||
}
|
||||
|
||||
int len = 0;
|
||||
char* p = (char*)memchr(szBufPtr, '\n', iBufAvail);
|
||||
if (p)
|
||||
{
|
||||
len = p - szBufPtr + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
len = iBufAvail;
|
||||
}
|
||||
|
||||
if (len > iSize)
|
||||
{
|
||||
len = iSize;
|
||||
}
|
||||
|
||||
memcpy(pBufPtr, szBufPtr, len);
|
||||
pBufPtr += len;
|
||||
szBufPtr += len;
|
||||
iBufAvail -= len;
|
||||
iBytesRead += len;
|
||||
iSize -= len;
|
||||
|
||||
if (p)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
*pBufPtr = '\0';
|
||||
|
||||
m_iBufAvail = iBufAvail > 0 ? iBufAvail : 0; // copy back to member
|
||||
m_szBufPtr = szBufPtr; // copy back to member
|
||||
|
||||
if (pBytesRead)
|
||||
{
|
||||
*pBytesRead = iBytesRead;
|
||||
}
|
||||
|
||||
if (pBufPtr == pBuffer)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
return pBuffer;
|
||||
}
|
||||
|
||||
int Connection::DoBind()
|
||||
{
|
||||
debug("Do binding");
|
||||
|
||||
m_iSocket = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (m_iSocket == INVALID_SOCKET)
|
||||
{
|
||||
ReportError("Socket creation failed for %s!", m_pNetAddress->GetHost(), 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
struct sockaddr_in sSocketAddress;
|
||||
memset(&sSocketAddress, '\0', sizeof(sSocketAddress));
|
||||
sSocketAddress.sin_family = AF_INET;
|
||||
if (!m_pNetAddress->GetHost() || strlen(m_pNetAddress->GetHost()) == 0)
|
||||
{
|
||||
sSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
}
|
||||
else
|
||||
{
|
||||
sSocketAddress.sin_addr.s_addr = ResolveHostAddr(m_pNetAddress->GetHost());
|
||||
if (sSocketAddress.sin_addr.s_addr == (unsigned int)-1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
sSocketAddress.sin_port = htons(m_pNetAddress->GetPort());
|
||||
int opt = 1;
|
||||
setsockopt(m_iSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));
|
||||
|
||||
if (bind(m_iSocket, (struct sockaddr *) &sSocketAddress, sizeof(sSocketAddress)) < 0)
|
||||
{
|
||||
ReportError("Binding socket failed for %s", m_pNetAddress->GetHost(), 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (listen(m_iSocket, 10) < 0)
|
||||
{
|
||||
ReportError("Listen on socket failed for %s", m_pNetAddress->GetHost(), 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
SOCKET Connection::DoAccept()
|
||||
{
|
||||
struct sockaddr_in ClientAddress;
|
||||
socklen_t SockLen;
|
||||
|
||||
SockLen = sizeof(ClientAddress);
|
||||
|
||||
SOCKET iSocket = accept(GetSocket(), (struct sockaddr *) & ClientAddress, &SockLen);
|
||||
|
||||
if (iSocket == INVALID_SOCKET && m_eStatus != csCancelled)
|
||||
{
|
||||
ReportError("Could not accept connection", NULL, 0);
|
||||
}
|
||||
|
||||
return iSocket;
|
||||
}
|
||||
|
||||
void Connection::Cancel()
|
||||
{
|
||||
debug("Cancelling connection");
|
||||
if (m_iSocket != INVALID_SOCKET)
|
||||
{
|
||||
m_eStatus = csCancelled;
|
||||
int r = shutdown(m_iSocket, SHUT_RDWR);
|
||||
if (r == -1)
|
||||
{
|
||||
ReportError("Could not shutdown connection", NULL, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::ReportError(const char* szMsgPrefix, const char* szMsgArg, int ErrCode)
|
||||
{
|
||||
if (ErrCode == 0)
|
||||
{
|
||||
#ifdef WIN32
|
||||
ErrCode = WSAGetLastError();
|
||||
#else
|
||||
ErrCode = errno;
|
||||
#endif
|
||||
}
|
||||
|
||||
char szErrPrefix[1024];
|
||||
snprintf(szErrPrefix, 1024, szMsgPrefix, szMsgArg);
|
||||
szErrPrefix[1024-1] = '\0';
|
||||
#ifdef WIN32
|
||||
if (m_bSuppressErrors)
|
||||
{
|
||||
debug("%s: ErrNo %i", szErrPrefix, ErrCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("%s: ErrNo %i", szErrPrefix, ErrCode);
|
||||
}
|
||||
#else
|
||||
const char* szErrMsg = hstrerror(ErrCode);
|
||||
if (m_bSuppressErrors)
|
||||
{
|
||||
debug("%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
85
Connection.h
85
Connection.h
@@ -1,85 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef CONNECTION_H
|
||||
#define CONNECTION_H
|
||||
|
||||
#include "NetAddress.h"
|
||||
|
||||
class Connection
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
csConnected,
|
||||
csDisconnected,
|
||||
csListening,
|
||||
csCancelled
|
||||
};
|
||||
|
||||
protected:
|
||||
NetAddress* m_pNetAddress;
|
||||
SOCKET m_iSocket;
|
||||
char* m_szReadBuf;
|
||||
int m_iBufAvail;
|
||||
char* m_szBufPtr;
|
||||
EStatus m_eStatus;
|
||||
int m_iTimeout;
|
||||
bool m_bSuppressErrors;
|
||||
|
||||
unsigned int ResolveHostAddr(const char* szHost);
|
||||
void ReportError(const char* szMsgPrefix, const char* szMsgArg, int ErrCode);
|
||||
virtual int DoConnect();
|
||||
virtual int DoDisconnect();
|
||||
int DoBind();
|
||||
int DoWriteLine(char* text);
|
||||
char* DoReadLine(char* pBuffer, int iSize, int* pBytesRead);
|
||||
SOCKET DoAccept();
|
||||
|
||||
public:
|
||||
Connection(NetAddress* pNetAddress);
|
||||
virtual ~Connection();
|
||||
static void Init();
|
||||
static void Final();
|
||||
int Connect();
|
||||
int Disconnect();
|
||||
int Bind();
|
||||
int Send(char* pBuffer, int iSize);
|
||||
int Recv(char* pBuffer, int iSize);
|
||||
bool RecvAll(char* pBuffer, int iSize);
|
||||
char* ReadLine(char* pBuffer, int iSize, int* pBytesRead);
|
||||
int WriteLine(char* text);
|
||||
SOCKET Accept();
|
||||
void Cancel();
|
||||
NetAddress* GetServer() { return m_pNetAddress; }
|
||||
SOCKET GetSocket() { return m_iSocket; }
|
||||
void SetTimeout(int iTimeout) { m_iTimeout = iTimeout; }
|
||||
EStatus GetStatus() { return m_eStatus; }
|
||||
void SetSuppressErrors(bool bSuppressErrors) { m_bSuppressErrors = bSuppressErrors; }
|
||||
bool GetSuppressErrors() { return m_bSuppressErrors; }
|
||||
};
|
||||
|
||||
#endif
|
||||
373
Decoder.cpp
373
Decoder.cpp
@@ -1,373 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
|
||||
#ifdef ENABLE_UULIB
|
||||
#ifndef PROTOTYPES
|
||||
#define PROTOTYPES
|
||||
#endif
|
||||
#include <uudeview.h>
|
||||
#endif
|
||||
|
||||
#include "Decoder.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
Mutex UULibDecoder::m_mutexDecoder;
|
||||
unsigned int YDecoder::crc_tab[256];
|
||||
|
||||
Decoder::Decoder()
|
||||
{
|
||||
debug("Creating Decoder");
|
||||
|
||||
m_szSrcFilename = NULL;
|
||||
m_szDestFilename = NULL;
|
||||
m_szArticleFilename = NULL;
|
||||
m_bCrcError = false;
|
||||
}
|
||||
|
||||
Decoder::~ Decoder()
|
||||
{
|
||||
debug("Destroying Decoder");
|
||||
|
||||
if (m_szArticleFilename)
|
||||
{
|
||||
free(m_szArticleFilename);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* UULibDecoder
|
||||
*/
|
||||
|
||||
bool UULibDecoder::Execute()
|
||||
{
|
||||
bool res = false;
|
||||
|
||||
#ifndef ENABLE_UULIB
|
||||
error("Program was compiled without option ENABLE_UULIB defined. uulib-Decoder is not available.");
|
||||
#else
|
||||
|
||||
m_mutexDecoder.Lock();
|
||||
|
||||
UUInitialize();
|
||||
|
||||
UUSetOption(UUOPT_DESPERATE, 1, NULL);
|
||||
// UUSetOption(UUOPT_DUMBNESS,1,NULL);
|
||||
// UUSetOption( UUOPT_SAVEPATH, 1, szDestDir );
|
||||
|
||||
UULoadFile((char*) m_szSrcFilename, NULL, 0);
|
||||
|
||||
// choose right attachment
|
||||
|
||||
uulist* attachment = NULL;
|
||||
|
||||
for (int i = 0; ; i++)
|
||||
{
|
||||
uulist* att_tmp = UUGetFileListItem(i);
|
||||
|
||||
if (!att_tmp)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if ((att_tmp) && (att_tmp->haveparts))
|
||||
{
|
||||
if (!attachment)
|
||||
{
|
||||
attachment = att_tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
//f**k, multiple attachments!? Can't handle this.
|
||||
attachment = NULL;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (attachment)
|
||||
{
|
||||
// okay, we got only one attachment, perfect!
|
||||
if ((attachment->haveparts) && (attachment->haveparts[0])) // && (!attachment->haveparts[1])) FUCK UULIB
|
||||
{
|
||||
int r = UUDecodeFile(attachment, (char*)m_szDestFilename);
|
||||
|
||||
if (r == UURET_OK)
|
||||
{
|
||||
// we did it!
|
||||
res = true;
|
||||
m_szArticleFilename = strdup(attachment->filename);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error("[ERROR] Wrong number of parts!\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error("[ERROR] Wrong number of attachments!\n");
|
||||
}
|
||||
|
||||
UUCleanUp();
|
||||
|
||||
m_mutexDecoder.Unlock();
|
||||
|
||||
#endif // ENABLE_UULIB
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* YDecoder
|
||||
* Very primitive (but fast) implementation of yEnc-Decoder
|
||||
*/
|
||||
|
||||
void YDecoder::Init()
|
||||
{
|
||||
debug("Initializing global decoder");
|
||||
crc32gentab();
|
||||
}
|
||||
|
||||
void YDecoder::Final()
|
||||
{
|
||||
debug("Finalizing global Decoder");
|
||||
}
|
||||
|
||||
YDecoder::YDecoder()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
void YDecoder::Clear()
|
||||
{
|
||||
m_bBody = false;
|
||||
m_bEnd = false;
|
||||
m_lExpectedCRC = 0;
|
||||
m_lCalculatedCRC = 0xFFFFFFFF;
|
||||
m_iBegin = 0;
|
||||
m_iEnd = 0;
|
||||
m_bAutoSeek = false;
|
||||
m_bNeedSetPos = false;
|
||||
m_bCrcCheck = false;
|
||||
if (m_szArticleFilename)
|
||||
{
|
||||
free(m_szArticleFilename);
|
||||
}
|
||||
m_szArticleFilename = NULL;
|
||||
}
|
||||
|
||||
/* from crc32.c (http://www.koders.com/c/fid699AFE0A656F0022C9D6B9D1743E697B69CE5815.aspx)
|
||||
*
|
||||
* (c) 1999,2000 Krzysztof Dabrowski
|
||||
* (c) 1999,2000 ElysiuM deeZine
|
||||
* Released under GPL (thanks)
|
||||
*
|
||||
* chksum_crc32gentab() -- to a global crc_tab[256], this one will
|
||||
* calculate the crcTable for crc32-checksums.
|
||||
* it is generated to the polynom [..]
|
||||
*/
|
||||
void YDecoder::crc32gentab()
|
||||
{
|
||||
unsigned long crc, poly;
|
||||
int i, j;
|
||||
|
||||
poly = 0xEDB88320L;
|
||||
for (i = 0; i < 256; i++)
|
||||
{
|
||||
crc = i;
|
||||
for (j = 8; j > 0; j--)
|
||||
{
|
||||
if (crc & 1)
|
||||
{
|
||||
crc = (crc >> 1) ^ poly;
|
||||
}
|
||||
else
|
||||
{
|
||||
crc >>= 1;
|
||||
}
|
||||
}
|
||||
crc_tab[i] = crc;
|
||||
}
|
||||
}
|
||||
|
||||
/* This is modified version of chksum_crc() from
|
||||
* crc32.c (http://www.koders.com/c/fid699AFE0A656F0022C9D6B9D1743E697B69CE5815.aspx)
|
||||
* (c) 1999,2000 Krzysztof Dabrowski
|
||||
* (c) 1999,2000 ElysiuM deeZine
|
||||
*
|
||||
* chksum_crc() -- to a given block, this one calculates the
|
||||
* crc32-checksum until the length is
|
||||
* reached. the crc32-checksum will be
|
||||
* the result.
|
||||
*/
|
||||
unsigned long YDecoder::crc32m(unsigned long startCrc, unsigned char *block, unsigned int length)
|
||||
{
|
||||
register unsigned long crc = startCrc;
|
||||
for (unsigned long i = 0; i < length; i++)
|
||||
{
|
||||
crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_tab[(crc ^ *block++) & 0xFF];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
unsigned int YDecoder::DecodeBuffer(char* buffer)
|
||||
{
|
||||
if (m_bBody)
|
||||
{
|
||||
if (!strncmp(buffer, "=yend size=", 11))
|
||||
{
|
||||
m_bEnd = true;
|
||||
char* pc = strstr(buffer, "pcrc32=");
|
||||
if (pc)
|
||||
{
|
||||
pc += 7; //=strlen("pcrc32=")
|
||||
m_lExpectedCRC = strtoul(pc, NULL, 16);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* iptr = buffer;
|
||||
char* optr = buffer;
|
||||
while (true)
|
||||
{
|
||||
switch (*iptr)
|
||||
{
|
||||
case '=': //escape-sequence
|
||||
iptr++;
|
||||
*optr = *iptr - 64 - 42;
|
||||
optr++;
|
||||
break;
|
||||
case '\n': // ignored char
|
||||
case '\r': // ignored char
|
||||
break;
|
||||
case '\0':
|
||||
goto BreakLoop;
|
||||
default: // normal char
|
||||
*optr = *iptr - 42;
|
||||
optr++;
|
||||
break;
|
||||
}
|
||||
iptr++;
|
||||
}
|
||||
BreakLoop:
|
||||
|
||||
if (m_bCrcCheck)
|
||||
{
|
||||
m_lCalculatedCRC = crc32m(m_lCalculatedCRC, (unsigned char *)buffer, optr - buffer);
|
||||
}
|
||||
return optr - buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!strncmp(buffer, "=ypart begin=", 13))
|
||||
{
|
||||
m_bBody = true;
|
||||
char* pb = strstr(buffer, "begin=");
|
||||
if (pb)
|
||||
{
|
||||
pb += 6; //=strlen("begin=")
|
||||
m_iBegin = (int)atoi(pb);
|
||||
}
|
||||
pb = strstr(buffer, "end=");
|
||||
if (pb)
|
||||
{
|
||||
pb += 4; //=strlen("end=")
|
||||
m_iEnd = (int)atoi(pb);
|
||||
}
|
||||
}
|
||||
else if (!strncmp(buffer, "=ybegin part=", 13))
|
||||
{
|
||||
char* pb = strstr(buffer, "name=");
|
||||
if (pb)
|
||||
{
|
||||
pb += 5; //=strlen("name=")
|
||||
char* pe;
|
||||
for (pe = pb; *pe != '\0' && *pe != '\n' && *pe != '\r'; pe++) ;
|
||||
if (m_szArticleFilename)
|
||||
{
|
||||
free(m_szArticleFilename);
|
||||
}
|
||||
m_szArticleFilename = (char*)malloc(pe - pb + 1);
|
||||
strncpy(m_szArticleFilename, pb, pe - pb);
|
||||
m_szArticleFilename[pe - pb] = '\0';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool YDecoder::Write(char* buffer, FILE* outfile)
|
||||
{
|
||||
unsigned int wcnt = DecodeBuffer(buffer);
|
||||
if (wcnt > 0)
|
||||
{
|
||||
if (m_bNeedSetPos)
|
||||
{
|
||||
if (m_iBegin == 0 || m_iEnd == 0 || !outfile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (fseek(outfile, m_iBegin - 1, SEEK_SET))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_bNeedSetPos = false;
|
||||
}
|
||||
fwrite(buffer, 1, wcnt, outfile);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool YDecoder::Execute()
|
||||
{
|
||||
m_lCalculatedCRC ^= 0xFFFFFFFF;
|
||||
|
||||
debug("Expected pcrc32=%x", m_lExpectedCRC);
|
||||
debug("Calculated pcrc32=%x", m_lCalculatedCRC);
|
||||
m_bCrcError = m_bCrcCheck && (m_lExpectedCRC != m_lCalculatedCRC);
|
||||
|
||||
return m_bBody && m_bEnd && !m_bCrcError;
|
||||
}
|
||||
89
Decoder.h
89
Decoder.h
@@ -1,89 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef DECODER_H
|
||||
#define DECODER_H
|
||||
|
||||
#include "Thread.h"
|
||||
|
||||
class Decoder
|
||||
{
|
||||
protected:
|
||||
const char* m_szSrcFilename;
|
||||
const char* m_szDestFilename;
|
||||
char* m_szArticleFilename;
|
||||
bool m_bCrcError;
|
||||
|
||||
public:
|
||||
Decoder();
|
||||
virtual ~Decoder();
|
||||
virtual bool Execute() = 0;
|
||||
void SetSrcFilename(const char* szSrcFilename) { m_szSrcFilename = szSrcFilename; }
|
||||
void SetDestFilename(const char* szDestFilename) { m_szDestFilename = szDestFilename; }
|
||||
const char* GetArticleFilename() { return m_szArticleFilename; }
|
||||
bool GetCrcError() { return m_bCrcError; }
|
||||
};
|
||||
|
||||
class UULibDecoder: public Decoder
|
||||
{
|
||||
private:
|
||||
static Mutex m_mutexDecoder;
|
||||
|
||||
public:
|
||||
virtual bool Execute();
|
||||
};
|
||||
|
||||
class YDecoder: public Decoder
|
||||
{
|
||||
protected:
|
||||
static unsigned int crc_tab[256];
|
||||
bool m_bBody;
|
||||
bool m_bEnd;
|
||||
unsigned long m_lExpectedCRC;
|
||||
unsigned long m_lCalculatedCRC;
|
||||
unsigned long m_iBegin;
|
||||
unsigned long m_iEnd;
|
||||
bool m_bAutoSeek;
|
||||
bool m_bNeedSetPos;
|
||||
bool m_bCrcCheck;
|
||||
|
||||
unsigned int DecodeBuffer(char* buffer);
|
||||
static void crc32gentab();
|
||||
unsigned long crc32m(unsigned long startCrc, unsigned char *block, unsigned int length);
|
||||
|
||||
public:
|
||||
YDecoder();
|
||||
virtual bool Execute();
|
||||
void Clear();
|
||||
bool Write(char* buffer, FILE* outfile);
|
||||
void SetAutoSeek(bool bAutoSeek) { m_bAutoSeek = m_bNeedSetPos = bAutoSeek; }
|
||||
void SetCrcCheck(bool bCrcCheck) { m_bCrcCheck = bCrcCheck; }
|
||||
|
||||
static void Init();
|
||||
static void Final();
|
||||
};
|
||||
|
||||
#endif
|
||||
396
DiskState.cpp
396
DiskState.cpp
@@ -1,396 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "DiskState.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
/* Save Download Queue to Disk.
|
||||
* The Disk State consists of file "queue", which contains the order of files
|
||||
* and of one diskstate-file for each file in download queue.
|
||||
* This function saves only file "queue".
|
||||
*/
|
||||
bool DiskState::Save(DownloadQueue* pDownloadQueue)
|
||||
{
|
||||
debug("Saving queue to disk");
|
||||
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "queue");
|
||||
fileName[1024-1] = '\0';
|
||||
|
||||
FILE* outfile = fopen(fileName, "w");
|
||||
|
||||
if (!outfile)
|
||||
{
|
||||
error("Could not create file %s", fileName);
|
||||
perror(fileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(outfile, "nzbget diskstate file version 1\n");
|
||||
|
||||
int cnt = 0;
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (!pFileInfo->GetDeleted())
|
||||
{
|
||||
fprintf(outfile, "%i,%i\n", pFileInfo->GetID(), (int)pFileInfo->GetPaused());
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
fclose(outfile);
|
||||
|
||||
if (cnt == 0)
|
||||
{
|
||||
remove(fileName);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DiskState::SaveFile(FileInfo* pFileInfo)
|
||||
{
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), pFileInfo->GetID());
|
||||
fileName[1024-1] = '\0';
|
||||
return SaveFileInfo(pFileInfo, fileName);
|
||||
}
|
||||
|
||||
bool DiskState::Load(DownloadQueue* pDownloadQueue)
|
||||
{
|
||||
debug("Loading queue from disk");
|
||||
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "queue");
|
||||
fileName[1024-1] = '\0';
|
||||
|
||||
FILE* infile = fopen(fileName, "r");
|
||||
|
||||
if (!infile)
|
||||
{
|
||||
error("Could not open file %s", fileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool res = false;
|
||||
char FileSignatur[128];
|
||||
fgets(FileSignatur, sizeof(FileSignatur), infile);
|
||||
if (!strcmp(FileSignatur, "nzbget diskstate file version 1\n"))
|
||||
{
|
||||
int id, paused;
|
||||
while (fscanf(infile, "%i,%i\n", &id, &paused) != EOF)
|
||||
{
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), id);
|
||||
fileName[1024-1] = '\0';
|
||||
FileInfo* pFileInfo = new FileInfo();
|
||||
bool res = LoadFileInfo(pFileInfo, fileName, true, false);
|
||||
if (res)
|
||||
{
|
||||
pFileInfo->SetID(id);
|
||||
pFileInfo->SetPaused(paused);
|
||||
pDownloadQueue->push_back(pFileInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Could not load diskstate for file %s", fileName);
|
||||
delete pFileInfo;
|
||||
}
|
||||
}
|
||||
res = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not load diskstate due file version mismatch");
|
||||
res = false;
|
||||
}
|
||||
|
||||
fclose(infile);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool DiskState::LoadArticles(FileInfo* pFileInfo)
|
||||
{
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), pFileInfo->GetID());
|
||||
fileName[1024-1] = '\0';
|
||||
return LoadFileInfo(pFileInfo, fileName, false, true);
|
||||
}
|
||||
|
||||
bool DiskState::SaveFileInfo(FileInfo* pFileInfo, const char* szFilename)
|
||||
{
|
||||
debug("Saving FileInfo to disk");
|
||||
|
||||
FILE* outfile = fopen(szFilename, "w");
|
||||
|
||||
if (!outfile)
|
||||
{
|
||||
error("Could not create file %s", szFilename);
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(outfile, "%s\n", pFileInfo->GetNZBFilename());
|
||||
fprintf(outfile, "%s\n", pFileInfo->GetSubject());
|
||||
fprintf(outfile, "%s\n", pFileInfo->GetDestDir());
|
||||
fprintf(outfile, "%s\n", pFileInfo->GetFilename());
|
||||
fprintf(outfile, "%i\n", pFileInfo->GetFilenameConfirmed());
|
||||
|
||||
fprintf(outfile, "%lu,%lu\n", (unsigned long)(pFileInfo->GetSize() >> 32), (unsigned long)(pFileInfo->GetSize()));
|
||||
|
||||
fprintf(outfile, "%i\n", pFileInfo->GetGroups()->size());
|
||||
for (FileInfo::Groups::iterator it = pFileInfo->GetGroups()->begin(); it != pFileInfo->GetGroups()->end(); it++)
|
||||
{
|
||||
fprintf(outfile, "%s\n", *it);
|
||||
}
|
||||
|
||||
fprintf(outfile, "%i\n", pFileInfo->GetArticles()->size());
|
||||
for (FileInfo::Articles::iterator it = pFileInfo->GetArticles()->begin(); it != pFileInfo->GetArticles()->end(); it++)
|
||||
{
|
||||
ArticleInfo* pArticleInfo = *it;
|
||||
fprintf(outfile, "%i,%i\n", pArticleInfo->GetPartNumber(), pArticleInfo->GetSize());
|
||||
fprintf(outfile, "%s\n", pArticleInfo->GetMessageID());
|
||||
}
|
||||
|
||||
fclose(outfile);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DiskState::LoadFileInfo(FileInfo* pFileInfo, const char * szFilename, bool bFileSummary, bool bArticles)
|
||||
{
|
||||
debug("Loading FileInfo from disk");
|
||||
|
||||
FILE* infile = fopen(szFilename, "r");
|
||||
|
||||
if (!infile)
|
||||
{
|
||||
error("Could not open file %s", szFilename);
|
||||
return false;
|
||||
}
|
||||
|
||||
char buf[1024];
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
if (bFileSummary) pFileInfo->SetNZBFilename(buf);
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
if (bFileSummary) pFileInfo->SetSubject(buf);
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
if (bFileSummary) pFileInfo->SetDestDir(buf);
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
if (bFileSummary) pFileInfo->SetFilename(buf);
|
||||
|
||||
int iFilenameConfirmed;
|
||||
if (fscanf(infile, "%i\n", &iFilenameConfirmed) != 1) goto error;
|
||||
if (bFileSummary) pFileInfo->SetFilenameConfirmed(iFilenameConfirmed);
|
||||
|
||||
unsigned long High, Low;
|
||||
if (fscanf(infile, "%lu,%lu\n", &High, &Low) != 2) goto error;
|
||||
if (bFileSummary) pFileInfo->SetSize((((unsigned long long)High) << 32) + Low);
|
||||
if (bFileSummary) pFileInfo->SetRemainingSize(pFileInfo->GetSize());
|
||||
|
||||
int size;
|
||||
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
if (bFileSummary) pFileInfo->GetGroups()->push_back(strdup(buf));
|
||||
}
|
||||
|
||||
if (bArticles)
|
||||
{
|
||||
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
int PartNumber, PartSize;
|
||||
if (fscanf(infile, "%i,%i\n", &PartNumber, &PartSize) != 2) goto error;
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
|
||||
ArticleInfo* pArticleInfo = new ArticleInfo();
|
||||
pArticleInfo->SetPartNumber(PartNumber);
|
||||
pArticleInfo->SetSize(PartSize);
|
||||
pArticleInfo->SetMessageID(buf);
|
||||
pFileInfo->GetArticles()->push_back(pArticleInfo);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(infile);
|
||||
return true;
|
||||
|
||||
error:
|
||||
fclose(infile);
|
||||
error("Error reading diskstate for file %s", szFilename);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete all files from Queue.
|
||||
* Returns true if successful, false if not
|
||||
*/
|
||||
bool DiskState::Discard()
|
||||
{
|
||||
debug("Discarding queue");
|
||||
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "queue");
|
||||
fileName[1024-1] = '\0';
|
||||
|
||||
FILE* infile = fopen(fileName, "r");
|
||||
|
||||
if (!infile)
|
||||
{
|
||||
error("Could not open file %s", fileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool res = false;
|
||||
char FileSignatur[128];
|
||||
fgets(FileSignatur, sizeof(FileSignatur), infile);
|
||||
if (!strcmp(FileSignatur, "nzbget diskstate file version 1\n"))
|
||||
{
|
||||
int id, paused;
|
||||
while (fscanf(infile, "%i,%i\n", &id, &paused) == 2)
|
||||
{
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), id);
|
||||
fileName[1024-1] = '\0';
|
||||
remove(fileName);
|
||||
}
|
||||
res = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not discard diskstate due file version mismatch");
|
||||
res = false;
|
||||
}
|
||||
|
||||
fclose(infile);
|
||||
if (res)
|
||||
{
|
||||
remove(fileName);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool DiskState::Exists()
|
||||
{
|
||||
debug("Checking if a saved queue exists on disk");
|
||||
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "queue");
|
||||
fileName[1024-1] = '\0';
|
||||
struct stat buffer;
|
||||
bool fileExists = !stat(fileName, &buffer);
|
||||
return fileExists;
|
||||
}
|
||||
|
||||
bool DiskState::DiscardFile(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo)
|
||||
{
|
||||
// delete diskstate-file
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), pFileInfo->GetID());
|
||||
fileName[1024-1] = '\0';
|
||||
remove(fileName);
|
||||
|
||||
return !pDownloadQueue || Save(pDownloadQueue);
|
||||
}
|
||||
|
||||
void DiskState::CleanupTempDir(DownloadQueue* pDownloadQueue)
|
||||
{
|
||||
// build array of IDs of files in queue for faster access
|
||||
int* ids = (int*)malloc(sizeof(int) * (pDownloadQueue->size() + 1));
|
||||
int* ptr = ids;
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
*ptr++ = pFileInfo->GetID();
|
||||
}
|
||||
*ptr = 0;
|
||||
|
||||
// read directory
|
||||
DirBrowser dir(g_pOptions->GetTempDir());
|
||||
while (const char* filename = dir.Next())
|
||||
{
|
||||
int id, part;
|
||||
bool del = strstr(filename, ".tmp") || strstr(filename, ".dec") ||
|
||||
((sscanf(filename, "%i.out", &id) == 1) &&
|
||||
!(g_pOptions->GetContinuePartial() && g_pOptions->GetDirectWrite()));
|
||||
if (!del)
|
||||
{
|
||||
if ((sscanf(filename, "%i.%i", &id, &part) == 2) ||
|
||||
(sscanf(filename, "%i.out", &id) == 1))
|
||||
{
|
||||
del = true;
|
||||
ptr = ids;
|
||||
while (*ptr)
|
||||
{
|
||||
if (*ptr == id)
|
||||
{
|
||||
del = false;
|
||||
break;
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (del)
|
||||
{
|
||||
char szFullFilename[1024];
|
||||
snprintf(szFullFilename, 1024, "%s%s", g_pOptions->GetTempDir(), filename);
|
||||
szFullFilename[1024-1] = '\0';
|
||||
remove(szFullFilename);
|
||||
}
|
||||
}
|
||||
|
||||
free(ids);
|
||||
}
|
||||
48
DiskState.h
48
DiskState.h
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef DISKSTATE_H
|
||||
#define DISKSTATE_H
|
||||
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
class DiskState
|
||||
{
|
||||
private:
|
||||
bool SaveFileInfo(FileInfo* pFileInfo, const char* szFilename);
|
||||
bool LoadFileInfo(FileInfo* pFileInfo, const char* szFilename, bool bFileSummary, bool bArticles);
|
||||
|
||||
public:
|
||||
bool Exists();
|
||||
bool Save(DownloadQueue* pDownloadQueue);
|
||||
bool Load(DownloadQueue* pDownloadQueue);
|
||||
bool SaveFile(FileInfo* pFileInfo);
|
||||
bool LoadArticles(FileInfo* pFileInfo);
|
||||
bool Discard();
|
||||
bool DiscardFile(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
|
||||
void CleanupTempDir(DownloadQueue* pDownloadQueue);
|
||||
};
|
||||
|
||||
#endif
|
||||
258
DownloadInfo.cpp
258
DownloadInfo.cpp
@@ -1,258 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
int FileInfo::m_iIDGen = 0;
|
||||
|
||||
ArticleInfo::ArticleInfo()
|
||||
{
|
||||
//debug("Creating ArticleInfo");
|
||||
m_szMessageID = NULL;
|
||||
m_iSize = 0;
|
||||
m_eStatus = aiUndefined;
|
||||
m_szResultFilename = NULL;
|
||||
}
|
||||
|
||||
ArticleInfo::~ ArticleInfo()
|
||||
{
|
||||
//debug("Destroying ArticleInfo");
|
||||
|
||||
if (m_szMessageID)
|
||||
{
|
||||
free(m_szMessageID);
|
||||
}
|
||||
if (m_szResultFilename)
|
||||
{
|
||||
free(m_szResultFilename);
|
||||
}
|
||||
}
|
||||
|
||||
void ArticleInfo::SetMessageID(const char * szMessageID)
|
||||
{
|
||||
m_szMessageID = strdup(szMessageID);
|
||||
}
|
||||
|
||||
void ArticleInfo::SetResultFilename(const char * v)
|
||||
{
|
||||
m_szResultFilename = strdup(v);
|
||||
}
|
||||
|
||||
|
||||
FileInfo::FileInfo()
|
||||
{
|
||||
debug("Creating FileInfo");
|
||||
|
||||
m_Articles.clear();
|
||||
m_Groups.clear();
|
||||
m_szSubject = NULL;
|
||||
m_szFilename = NULL;
|
||||
m_bFilenameConfirmed = false;
|
||||
m_szDestDir = NULL;
|
||||
m_szNZBFilename = NULL;
|
||||
m_lSize = 0;
|
||||
m_lRemainingSize = 0;
|
||||
m_bPaused = false;
|
||||
m_bDeleted = false;
|
||||
m_iCompleted = 0;
|
||||
m_bOutputInitialized = false;
|
||||
m_iIDGen++;
|
||||
m_iID = m_iIDGen;
|
||||
}
|
||||
|
||||
FileInfo::~ FileInfo()
|
||||
{
|
||||
debug("Destroying FileInfo");
|
||||
|
||||
if (m_szSubject)
|
||||
{
|
||||
free(m_szSubject);
|
||||
}
|
||||
if (m_szFilename)
|
||||
{
|
||||
free(m_szFilename);
|
||||
}
|
||||
if (m_szDestDir)
|
||||
{
|
||||
free(m_szDestDir);
|
||||
}
|
||||
if (m_szNZBFilename)
|
||||
{
|
||||
free(m_szNZBFilename);
|
||||
}
|
||||
|
||||
for (Groups::iterator it = m_Groups.begin(); it != m_Groups.end() ;it++)
|
||||
{
|
||||
free(*it);
|
||||
}
|
||||
m_Groups.clear();
|
||||
|
||||
ClearArticles();
|
||||
}
|
||||
|
||||
void FileInfo::ClearArticles()
|
||||
{
|
||||
for (Articles::iterator it = m_Articles.begin(); it != m_Articles.end() ;it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Articles.clear();
|
||||
}
|
||||
|
||||
void FileInfo::SetID(int s)
|
||||
{
|
||||
m_iID = s;
|
||||
if (m_iIDGen < m_iID)
|
||||
{
|
||||
m_iIDGen = m_iID;
|
||||
}
|
||||
}
|
||||
|
||||
void FileInfo::SetSubject(const char* szSubject)
|
||||
{
|
||||
m_szSubject = strdup(szSubject);
|
||||
}
|
||||
|
||||
void FileInfo::SetDestDir(const char* szDestDir)
|
||||
{
|
||||
m_szDestDir = strdup(szDestDir);
|
||||
}
|
||||
|
||||
void FileInfo::SetNZBFilename(const char * szNZBFilename)
|
||||
{
|
||||
m_szNZBFilename = strdup(szNZBFilename);
|
||||
}
|
||||
|
||||
void FileInfo::GetNiceNZBName(char* szBuffer, int iSize)
|
||||
{
|
||||
MakeNiceNZBName(m_szNZBFilename, szBuffer, iSize);
|
||||
}
|
||||
|
||||
void FileInfo::MakeNiceNZBName(const char * szNZBFilename, char * szBuffer, int iSize)
|
||||
{
|
||||
char postname[1024];
|
||||
const char* szBaseName = BaseFileName(szNZBFilename);
|
||||
|
||||
// if .nzb file has a certain structure, try to strip out certain elements
|
||||
if (sscanf(szBaseName, "msgid_%*d_%1023s", postname) == 1)
|
||||
{
|
||||
// OK, using stripped name
|
||||
}
|
||||
else
|
||||
{
|
||||
// using complete filename
|
||||
strncpy(postname, szBaseName, 1024);
|
||||
postname[1024-1] = '\0';
|
||||
}
|
||||
|
||||
// wipe out ".nzb"
|
||||
if (char* p = strrchr(postname, '.')) *p = '\0';
|
||||
|
||||
::MakeValidFilename(postname, '_');
|
||||
|
||||
// if the resulting name is empty, use basename without cleaing up "msgid_"
|
||||
if (strlen(postname) == 0)
|
||||
{
|
||||
// using complete filename
|
||||
strncpy(postname, szBaseName, 1024);
|
||||
postname[1024-1] = '\0';
|
||||
|
||||
// wipe out ".nzb"
|
||||
if (char* p = strrchr(postname, '.')) *p = '\0';
|
||||
|
||||
::MakeValidFilename(postname, '_');
|
||||
|
||||
// if the resulting name is STILL empty, use "noname"
|
||||
if (strlen(postname) == 0)
|
||||
{
|
||||
strncpy(postname, "noname", 1024);
|
||||
}
|
||||
}
|
||||
|
||||
strncpy(szBuffer, postname, iSize);
|
||||
szBuffer[iSize-1] = '\0';
|
||||
}
|
||||
|
||||
void FileInfo::SetFilename(const char* szFilename)
|
||||
{
|
||||
if (m_szFilename)
|
||||
{
|
||||
free(m_szFilename);
|
||||
}
|
||||
m_szFilename = strdup(szFilename);
|
||||
}
|
||||
|
||||
void FileInfo::MakeValidFilename()
|
||||
{
|
||||
::MakeValidFilename(m_szFilename, '_');
|
||||
}
|
||||
|
||||
void FileInfo::LockOutputFile()
|
||||
{
|
||||
m_mutexOutputFile.Lock();
|
||||
}
|
||||
|
||||
void FileInfo::UnlockOutputFile()
|
||||
{
|
||||
m_mutexOutputFile.Unlock();
|
||||
}
|
||||
|
||||
bool FileInfo::IsDupe(const char* szFilename)
|
||||
{
|
||||
struct stat buffer;
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%c%s", m_szDestDir, (int)PATH_SEPARATOR, szFilename);
|
||||
fileName[1024-1] = '\0';
|
||||
bool exists = !stat(fileName, &buffer);
|
||||
if (exists)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
snprintf(fileName, 1024, "%s%c%s_broken", m_szDestDir, (int)PATH_SEPARATOR, szFilename);
|
||||
fileName[1024-1] = '\0';
|
||||
exists = !stat(fileName, &buffer);
|
||||
if (exists)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
133
DownloadInfo.h
133
DownloadInfo.h
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef DOWNLOADINFO_H
|
||||
#define DOWNLOADINFO_H
|
||||
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
|
||||
#include "Thread.h"
|
||||
|
||||
class ArticleInfo
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
aiUndefined,
|
||||
aiRunning,
|
||||
aiFinished,
|
||||
aiFailed
|
||||
};
|
||||
|
||||
private:
|
||||
int m_iPartNumber;
|
||||
char* m_szMessageID;
|
||||
int m_iSize;
|
||||
EStatus m_eStatus;
|
||||
char* m_szResultFilename;
|
||||
|
||||
public:
|
||||
ArticleInfo();
|
||||
~ArticleInfo();
|
||||
void SetPartNumber(int s) { m_iPartNumber = s; }
|
||||
int GetPartNumber() { return m_iPartNumber; }
|
||||
const char* GetMessageID() { return m_szMessageID; }
|
||||
void SetMessageID(const char* szMessageID);
|
||||
void SetSize(int s) { m_iSize = s; }
|
||||
int GetSize() { return m_iSize; }
|
||||
EStatus GetStatus() { return m_eStatus; }
|
||||
void SetStatus(EStatus Status) { m_eStatus = Status; }
|
||||
const char* GetResultFilename() { return m_szResultFilename; }
|
||||
void SetResultFilename(const char* v);
|
||||
};
|
||||
|
||||
class FileInfo
|
||||
{
|
||||
public:
|
||||
typedef std::vector<ArticleInfo*> Articles;
|
||||
typedef std::vector<char*> Groups;
|
||||
|
||||
private:
|
||||
int m_iID;
|
||||
Articles m_Articles;
|
||||
Groups m_Groups;
|
||||
char* m_szNZBFilename;
|
||||
char* m_szSubject;
|
||||
char* m_szFilename;
|
||||
char* m_szDestDir;
|
||||
long long m_lSize;
|
||||
long long m_lRemainingSize;
|
||||
bool m_bPaused;
|
||||
bool m_bDeleted;
|
||||
bool m_bFilenameConfirmed;
|
||||
int m_iCompleted;
|
||||
bool m_bOutputInitialized;
|
||||
Mutex m_mutexOutputFile;
|
||||
|
||||
static int m_iIDGen;
|
||||
|
||||
public:
|
||||
FileInfo();
|
||||
~FileInfo();
|
||||
int GetID() { return m_iID; }
|
||||
void SetID(int s);
|
||||
Articles* GetArticles() { return &m_Articles; }
|
||||
Groups* GetGroups() { return &m_Groups; }
|
||||
const char* GetNZBFilename() { return m_szNZBFilename; }
|
||||
void SetNZBFilename(const char* szNZBFilename);
|
||||
void GetNiceNZBName(char* szBuffer, int iSize);
|
||||
static void MakeNiceNZBName(const char* szNZBFilename, char* szBuffer, int iSize);
|
||||
const char* GetSubject() { return m_szSubject; }
|
||||
void SetSubject(const char* szSubject);
|
||||
const char* GetFilename() { return m_szFilename; }
|
||||
void SetFilename(const char* szFilename);
|
||||
void MakeValidFilename();
|
||||
bool GetFilenameConfirmed() { return m_bFilenameConfirmed; }
|
||||
void SetFilenameConfirmed(bool bFilenameConfirmed) { m_bFilenameConfirmed = bFilenameConfirmed; }
|
||||
void SetSize(long long s) { m_lSize = s; m_lRemainingSize = s; }
|
||||
long long GetSize() { return m_lSize; }
|
||||
long long GetRemainingSize() { return m_lRemainingSize; }
|
||||
void SetRemainingSize(long long s) { m_lRemainingSize = s; }
|
||||
bool GetPaused() { return m_bPaused; }
|
||||
void SetPaused(bool Paused) { m_bPaused = Paused; }
|
||||
bool GetDeleted() { return m_bDeleted; }
|
||||
void SetDeleted(bool Deleted) { m_bDeleted = Deleted; }
|
||||
const char* GetDestDir() { return m_szDestDir; }
|
||||
void SetDestDir(const char* szDestDir);
|
||||
int GetCompleted() { return m_iCompleted; }
|
||||
void SetCompleted(int s) { m_iCompleted = s; }
|
||||
void ClearArticles();
|
||||
void LockOutputFile();
|
||||
void UnlockOutputFile();
|
||||
bool GetOutputInitialized() { return m_bOutputInitialized; }
|
||||
void SetOutputInitialized(bool bOutputInitialized) { m_bOutputInitialized = bOutputInitialized; }
|
||||
bool IsDupe(const char* szFilename);
|
||||
};
|
||||
|
||||
typedef std::deque<FileInfo*> DownloadQueue;
|
||||
|
||||
#endif
|
||||
428
Frontend.cpp
428
Frontend.cpp
@@ -1,428 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Options.h"
|
||||
#include "Frontend.h"
|
||||
#include "Log.h"
|
||||
#include "Connection.h"
|
||||
#include "MessageBase.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "PrePostProcessor.h"
|
||||
#include "RemoteClient.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
extern PrePostProcessor* g_pPrePostProcessor;
|
||||
extern Options* g_pOptions;
|
||||
|
||||
Frontend::Frontend()
|
||||
{
|
||||
debug("Creating Frontend");
|
||||
|
||||
m_iNeededLogFirstID = 0;
|
||||
m_iNeededLogEntries = 0;
|
||||
m_bSummary = false;
|
||||
m_bFileList = false;
|
||||
m_fCurrentDownloadSpeed = 0;
|
||||
m_lRemainingSize = 0;
|
||||
m_bPause = false;
|
||||
m_fDownloadLimit = 0;
|
||||
m_iThreadCount = 0;
|
||||
m_iParJobCount = 0;
|
||||
m_iUpTimeSec = 0;
|
||||
m_iDnTimeSec = 0;
|
||||
m_iAllBytes = 0;
|
||||
m_bStandBy = 0;
|
||||
m_RemoteMessages.clear();
|
||||
m_RemoteQueue.clear();
|
||||
m_iUpdateInterval = g_pOptions->GetUpdateInterval();
|
||||
}
|
||||
|
||||
bool Frontend::PrepareData()
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
if (IsStopped())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!RequestMessages() || ((m_bSummary || m_bFileList) && !RequestFileList()))
|
||||
{
|
||||
printf("\nUnable to send request to nzbget-server at %s (port %i) \n", g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_bSummary)
|
||||
{
|
||||
m_fCurrentDownloadSpeed = g_pQueueCoordinator->CalcCurrentDownloadSpeed();
|
||||
m_lRemainingSize = g_pQueueCoordinator->CalcRemainingSize();
|
||||
m_bPause = g_pOptions->GetPause();
|
||||
m_fDownloadLimit = g_pOptions->GetDownloadRate();
|
||||
m_iThreadCount = Thread::GetThreadCount();
|
||||
PrePostProcessor::ParQueue* pParQueue = g_pPrePostProcessor->LockParQueue();
|
||||
m_iParJobCount = pParQueue->size();
|
||||
g_pPrePostProcessor->UnlockParQueue();
|
||||
g_pQueueCoordinator->CalcStat(&m_iUpTimeSec, &m_iDnTimeSec, &m_iAllBytes, &m_bStandBy);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Frontend::FreeData()
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
for (Log::Messages::iterator it = m_RemoteMessages.begin(); it != m_RemoteMessages.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_RemoteMessages.clear();
|
||||
|
||||
for (DownloadQueue::iterator it = m_RemoteQueue.begin(); it != m_RemoteQueue.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_RemoteQueue.clear();
|
||||
}
|
||||
}
|
||||
|
||||
Log::Messages * Frontend::LockMessages()
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
return &m_RemoteMessages;
|
||||
}
|
||||
else
|
||||
{
|
||||
return g_pLog->LockMessages();
|
||||
}
|
||||
}
|
||||
|
||||
void Frontend::UnlockMessages()
|
||||
{
|
||||
if (!IsRemoteMode())
|
||||
{
|
||||
g_pLog->UnlockMessages();
|
||||
}
|
||||
}
|
||||
|
||||
DownloadQueue * Frontend::LockQueue()
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
return &m_RemoteQueue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return g_pQueueCoordinator->LockQueue();
|
||||
}
|
||||
}
|
||||
|
||||
void Frontend::UnlockQueue()
|
||||
{
|
||||
if (!IsRemoteMode())
|
||||
{
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
}
|
||||
}
|
||||
|
||||
bool Frontend::IsRemoteMode()
|
||||
{
|
||||
return g_pOptions->GetRemoteClientMode();
|
||||
}
|
||||
|
||||
void Frontend::ServerPauseUnpause(bool bPause)
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
RequestPauseUnpause(bPause);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pOptions->SetPause(bPause);
|
||||
}
|
||||
}
|
||||
|
||||
void Frontend::ServerSetDownloadRate(float fRate)
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
RequestSetDownloadRate(fRate);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pOptions->SetDownloadRate(fRate);
|
||||
}
|
||||
}
|
||||
|
||||
void Frontend::ServerDumpDebug()
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
RequestDumpDebug();
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pQueueCoordinator->LogDebugInfo();
|
||||
}
|
||||
}
|
||||
|
||||
bool Frontend::ServerEditQueue(QueueEditor::EEditAction eAction, int iOffset, int iID)
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
return RequestEditQueue(eAction, iOffset, iID);
|
||||
}
|
||||
else
|
||||
{
|
||||
return g_pQueueCoordinator->GetQueueEditor()->EditEntry(iID, true, eAction, iOffset);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Frontend::InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize)
|
||||
{
|
||||
pMessageBase->m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
|
||||
pMessageBase->m_iType = htonl(iRequest);
|
||||
pMessageBase->m_iStructSize = htonl(iSize);
|
||||
strncpy(pMessageBase->m_szPassword, g_pOptions->GetServerPassword(), NZBREQUESTPASSWORDSIZE);
|
||||
pMessageBase->m_szPassword[NZBREQUESTPASSWORDSIZE - 1] = '\0';
|
||||
}
|
||||
|
||||
bool Frontend::RequestMessages()
|
||||
{
|
||||
NetAddress netAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
Connection connection(&netAddress);
|
||||
|
||||
bool OK = connection.Connect() >= 0;
|
||||
if (!OK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SNZBLogRequest LogRequest;
|
||||
InitMessageBase(&LogRequest.m_MessageBase, eRemoteRequestLog, sizeof(LogRequest));
|
||||
LogRequest.m_iLines = htonl(m_iNeededLogEntries);
|
||||
if (m_iNeededLogEntries == 0)
|
||||
{
|
||||
LogRequest.m_iIDFrom = htonl(m_iNeededLogFirstID > 0 ? m_iNeededLogFirstID : 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogRequest.m_iIDFrom = 0;
|
||||
}
|
||||
|
||||
if (connection.Send((char*)(&LogRequest), sizeof(LogRequest)) < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now listen for the returned log
|
||||
SNZBLogResponse LogResponse;
|
||||
int iResponseLen = connection.Recv((char*) &LogResponse, sizeof(LogResponse));
|
||||
if (iResponseLen != sizeof(LogResponse) ||
|
||||
(int)ntohl(LogResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
|
||||
ntohl(LogResponse.m_MessageBase.m_iStructSize) != sizeof(LogResponse))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
char* pBuf = NULL;
|
||||
if (ntohl(LogResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
pBuf = (char*)malloc(ntohl(LogResponse.m_iTrailingDataLength));
|
||||
if (!connection.RecvAll(pBuf, ntohl(LogResponse.m_iTrailingDataLength)))
|
||||
{
|
||||
free(pBuf);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
connection.Disconnect();
|
||||
|
||||
if (ntohl(LogResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
char* pBufPtr = (char*)pBuf;
|
||||
for (unsigned int i = 0; i < ntohl(LogResponse.m_iNrTrailingEntries); i++)
|
||||
{
|
||||
SNZBLogResponseEntry* pLogAnswer = (SNZBLogResponseEntry*) pBufPtr;
|
||||
|
||||
char* szText = pBufPtr + sizeof(SNZBLogResponseEntry);
|
||||
|
||||
Message* pMessage = new Message(ntohl(pLogAnswer->m_iID), (Message::EKind)ntohl(pLogAnswer->m_iKind), ntohl(pLogAnswer->m_tTime), szText);
|
||||
m_RemoteMessages.push_back(pMessage);
|
||||
|
||||
pBufPtr += sizeof(SNZBLogResponseEntry) + ntohl(pLogAnswer->m_iTextLen);
|
||||
}
|
||||
|
||||
free(pBuf);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Frontend::RequestFileList()
|
||||
{
|
||||
NetAddress netAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
Connection connection(&netAddress);
|
||||
|
||||
bool OK = connection.Connect() >= 0;
|
||||
if (!OK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SNZBListRequest ListRequest;
|
||||
InitMessageBase(&ListRequest.m_MessageBase, eRemoteRequestList, sizeof(ListRequest));
|
||||
ListRequest.m_bFileList = htonl(m_bFileList);
|
||||
ListRequest.m_bServerState = htonl(m_bSummary);
|
||||
|
||||
if (connection.Send((char*)(&ListRequest), sizeof(ListRequest)) < 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now listen for the returned list
|
||||
SNZBListResponse ListResponse;
|
||||
int iResponseLen = connection.Recv((char*) &ListResponse, sizeof(ListResponse));
|
||||
if (iResponseLen != sizeof(ListResponse) ||
|
||||
(int)ntohl(ListResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
|
||||
ntohl(ListResponse.m_MessageBase.m_iStructSize) != sizeof(ListResponse))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
char* pBuf = NULL;
|
||||
if (ntohl(ListResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
pBuf = (char*)malloc(ntohl(ListResponse.m_iTrailingDataLength));
|
||||
if (!connection.RecvAll(pBuf, ntohl(ListResponse.m_iTrailingDataLength)))
|
||||
{
|
||||
free(pBuf);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
connection.Disconnect();
|
||||
|
||||
if (m_bSummary)
|
||||
{
|
||||
m_bPause = ntohl(ListResponse.m_bServerPaused);
|
||||
m_lRemainingSize = JoinInt64(ntohl(ListResponse.m_iRemainingSizeHi), ntohl(ListResponse.m_iRemainingSizeLo));
|
||||
m_fCurrentDownloadSpeed = ntohl(ListResponse.m_iDownloadRate) / 1024.0;
|
||||
m_fDownloadLimit = ntohl(ListResponse.m_iDownloadLimit) / 1024.0;
|
||||
m_iThreadCount = ntohl(ListResponse.m_iThreadCount);
|
||||
m_iParJobCount = ntohl(ListResponse.m_iParJobCount);
|
||||
m_iUpTimeSec = ntohl(ListResponse.m_iUpTimeSec);
|
||||
m_iDnTimeSec = ntohl(ListResponse.m_iDownloadTimeSec);
|
||||
m_bStandBy = ntohl(ListResponse.m_bServerStandBy);
|
||||
m_iAllBytes = JoinInt64(ntohl(ListResponse.m_iDownloadedBytesHi), ntohl(ListResponse.m_iDownloadedBytesLo));
|
||||
}
|
||||
|
||||
if (m_bFileList && ntohl(ListResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
char* pBufPtr = (char*)pBuf;
|
||||
for (unsigned int i = 0; i < ntohl(ListResponse.m_iNrTrailingEntries); i++)
|
||||
{
|
||||
SNZBListResponseEntry* pListAnswer = (SNZBListResponseEntry*) pBufPtr;
|
||||
|
||||
char* szNZBFilename = pBufPtr + sizeof(SNZBListResponseEntry);
|
||||
char* szSubject = pBufPtr + sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen);
|
||||
char* szFileName = pBufPtr + sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) + ntohl(pListAnswer->m_iSubjectLen);
|
||||
char* szDestDir = pBufPtr + sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) + ntohl(pListAnswer->m_iSubjectLen) + ntohl(pListAnswer->m_iFilenameLen);
|
||||
|
||||
FileInfo* pFileInfo = new FileInfo();
|
||||
pFileInfo->SetID(ntohl(pListAnswer->m_iID));
|
||||
pFileInfo->SetSize(JoinInt64(ntohl(pListAnswer->m_iFileSizeHi), ntohl(pListAnswer->m_iFileSizeLo)));
|
||||
pFileInfo->SetRemainingSize(JoinInt64(ntohl(pListAnswer->m_iRemainingSizeHi), ntohl(pListAnswer->m_iRemainingSizeLo)));
|
||||
pFileInfo->SetPaused(ntohl(pListAnswer->m_bPaused));
|
||||
pFileInfo->SetNZBFilename(szNZBFilename);
|
||||
pFileInfo->SetSubject(szSubject);
|
||||
pFileInfo->SetFilename(szFileName);
|
||||
pFileInfo->SetFilenameConfirmed(ntohl(pListAnswer->m_bFilenameConfirmed));
|
||||
pFileInfo->SetDestDir(szDestDir);
|
||||
|
||||
m_RemoteQueue.push_back(pFileInfo);
|
||||
|
||||
pBufPtr += sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) +
|
||||
ntohl(pListAnswer->m_iSubjectLen) + ntohl(pListAnswer->m_iFilenameLen) + ntohl(pListAnswer->m_iDestDirLen);
|
||||
}
|
||||
}
|
||||
if (pBuf)
|
||||
{
|
||||
free(pBuf);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Frontend::RequestPauseUnpause(bool bPause)
|
||||
{
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
return client.RequestServerPauseUnpause(bPause);
|
||||
}
|
||||
|
||||
bool Frontend::RequestSetDownloadRate(float fRate)
|
||||
{
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
return client.RequestServerSetDownloadRate(fRate);
|
||||
}
|
||||
|
||||
bool Frontend::RequestDumpDebug()
|
||||
{
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
return client.RequestServerDumpDebug();
|
||||
}
|
||||
|
||||
bool Frontend::RequestEditQueue(int iAction, int iOffset, int iID)
|
||||
{
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
return client.RequestServerEditQueue(iAction, iOffset, &iID, 1, false);
|
||||
}
|
||||
167
INSTALL
167
INSTALL
@@ -1,167 +0,0 @@
|
||||
Basic Installation
|
||||
==================
|
||||
|
||||
These are generic installation instructions.
|
||||
|
||||
The `configure' shell script attempts to guess correct values for
|
||||
various system-dependent variables used during compilation. It uses
|
||||
those values to create a `Makefile' in each directory of the package.
|
||||
It may also create one or more `.h' files containing system-dependent
|
||||
definitions. Finally, it creates a shell script `config.status' that
|
||||
you can run in the future to recreate the current configuration, a file
|
||||
`config.cache' that saves the results of its tests to speed up
|
||||
reconfiguring, and a file `config.log' containing compiler output
|
||||
(useful mainly for debugging `configure').
|
||||
|
||||
If you need to do unusual things to compile the package, please try
|
||||
to figure out how `configure' could check whether to do them, and mail
|
||||
diffs or instructions to the address given in the `README' so they can
|
||||
be considered for the next release. If at some point `config.cache'
|
||||
contains results you don't want to keep, you may remove or edit it.
|
||||
|
||||
The file `configure.in' is used to create `configure' by a program
|
||||
called `autoconf'. You only need `configure.in' if you want to change
|
||||
it or regenerate `configure' using a newer version of `autoconf'.
|
||||
|
||||
The simplest way to compile this package is:
|
||||
|
||||
1. `cd' to the directory containing the package's source code and type
|
||||
`./configure' to configure the package for your system. If you're
|
||||
using `csh' on an old version of System V, you might need to type
|
||||
`sh ./configure' instead to prevent `csh' from trying to execute
|
||||
`configure' itself.
|
||||
|
||||
Running `configure' takes a while. While running, it prints some
|
||||
messages telling which features it is checking for.
|
||||
|
||||
2. Type `make' to compile the package.
|
||||
|
||||
3. Type `make install' to install the programs and any data files and
|
||||
documentation.
|
||||
|
||||
4. You can remove the program binaries and object files from the
|
||||
source code directory by typing `make clean'.
|
||||
|
||||
Compilers and Options
|
||||
=====================
|
||||
|
||||
Some systems require unusual options for compilation or linking that
|
||||
the `configure' script does not know about. You can give `configure'
|
||||
initial values for variables by setting them in the environment. Using
|
||||
a Bourne-compatible shell, you can do that on the command line like
|
||||
this:
|
||||
CC=c89 CFLAGS=-O2 LIBS=-lposix ./configure
|
||||
|
||||
Or on systems that have the `env' program, you can do it like this:
|
||||
env CPPFLAGS=-I/usr/local/include LDFLAGS=-s ./configure
|
||||
|
||||
Compiling For Multiple Architectures
|
||||
====================================
|
||||
|
||||
You can compile the package for more than one kind of computer at the
|
||||
same time, by placing the object files for each architecture in their
|
||||
own directory. To do this, you must use a version of `make' that
|
||||
supports the `VPATH' variable, such as GNU `make'. `cd' to the
|
||||
directory where you want the object files and executables to go and run
|
||||
the `configure' script. `configure' automatically checks for the
|
||||
source code in the directory that `configure' is in and in `..'.
|
||||
|
||||
If you have to use a `make' that does not supports the `VPATH'
|
||||
variable, you have to compile the package for one architecture at a time
|
||||
in the source code directory. After you have installed the package for
|
||||
one architecture, use `make distclean' before reconfiguring for another
|
||||
architecture.
|
||||
|
||||
Installation Names
|
||||
==================
|
||||
|
||||
By default, `make install' will install the package's files in
|
||||
`/usr/local/bin', `/usr/local/man', etc. You can specify an
|
||||
installation prefix other than `/usr/local' by giving `configure' the
|
||||
option `--prefix=PATH'.
|
||||
|
||||
You can specify separate installation prefixes for
|
||||
architecture-specific files and architecture-independent files. If you
|
||||
give `configure' the option `--exec-prefix=PATH', the package will use
|
||||
PATH as the prefix for installing programs and libraries.
|
||||
Documentation and other data files will still use the regular prefix.
|
||||
|
||||
If the package supports it, you can cause programs to be installed
|
||||
with an extra prefix or suffix on their names by giving `configure' the
|
||||
option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'.
|
||||
|
||||
Optional Features
|
||||
=================
|
||||
|
||||
Some packages pay attention to `--enable-FEATURE' options to
|
||||
`configure', where FEATURE indicates an optional part of the package.
|
||||
They may also pay attention to `--with-PACKAGE' options, where PACKAGE
|
||||
is something like `gnu-as' or `x' (for the X Window System). The
|
||||
`README' should mention any `--enable-' and `--with-' options that the
|
||||
package recognizes.
|
||||
|
||||
For packages that use the X Window System, `configure' can usually
|
||||
find the X include and library files automatically, but if it doesn't,
|
||||
you can use the `configure' options `--x-includes=DIR' and
|
||||
`--x-libraries=DIR' to specify their locations.
|
||||
|
||||
Specifying the System Type
|
||||
==========================
|
||||
|
||||
There may be some features `configure' can not figure out
|
||||
automatically, but needs to determine by the type of host the package
|
||||
will run on. Usually `configure' can figure that out, but if it prints
|
||||
a message saying it can not guess the host type, give it the
|
||||
`--host=TYPE' option. TYPE can either be a short name for the system
|
||||
type, such as `sun4', or a canonical name with three fields:
|
||||
CPU-COMPANY-SYSTEM
|
||||
|
||||
See the file `config.sub' for the possible values of each field. If
|
||||
`config.sub' isn't included in this package, then this package doesn't
|
||||
need to know the host type.
|
||||
|
||||
If you are building compiler tools for cross-compiling, you can also
|
||||
use the `--target=TYPE' option to select the type of system they will
|
||||
produce code for and the `--build=TYPE' option to select the type of
|
||||
system on which you are compiling the package.
|
||||
|
||||
Sharing Defaults
|
||||
================
|
||||
|
||||
If you want to set default values for `configure' scripts to share,
|
||||
you can create a site shell script called `config.site' that gives
|
||||
default values for variables like `CC', `cache_file', and `prefix'.
|
||||
`configure' looks for `PREFIX/share/config.site' if it exists, then
|
||||
`PREFIX/etc/config.site' if it exists. Or, you can set the
|
||||
`CONFIG_SITE' environment variable to the location of the site script.
|
||||
A warning: not all `configure' scripts look for a site script.
|
||||
|
||||
Operation Controls
|
||||
==================
|
||||
|
||||
`configure' recognizes the following options to control how it
|
||||
operates.
|
||||
|
||||
`--cache-file=FILE'
|
||||
Use and save the results of the tests in FILE instead of
|
||||
`./config.cache'. Set FILE to `/dev/null' to disable caching, for
|
||||
debugging `configure'.
|
||||
|
||||
`--help'
|
||||
Print a summary of the options to `configure', and exit.
|
||||
|
||||
`--quiet'
|
||||
`--silent'
|
||||
`-q'
|
||||
Do not print messages saying which checks are being made.
|
||||
|
||||
`--srcdir=DIR'
|
||||
Look for the package's source code in directory DIR. Usually
|
||||
`configure' can determine that directory automatically.
|
||||
|
||||
`--version'
|
||||
Print the version of Autoconf used to generate the `configure'
|
||||
script, and exit.
|
||||
|
||||
`configure' also accepts some other, not widely useful, options.
|
||||
|
||||
334
Log.cpp
334
Log.cpp
@@ -1,334 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
Log::Log()
|
||||
{
|
||||
m_Messages.clear();
|
||||
m_iIDGen = 0;
|
||||
m_szLogFilename = NULL;
|
||||
#ifdef DEBUG
|
||||
struct stat buffer;
|
||||
m_bExtraDebug = !stat("extradebug", &buffer);
|
||||
#endif
|
||||
}
|
||||
|
||||
Log::~Log()
|
||||
{
|
||||
for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Messages.clear();
|
||||
if (m_szLogFilename)
|
||||
{
|
||||
free(m_szLogFilename);
|
||||
}
|
||||
}
|
||||
|
||||
void Log::Filelog(const char* msg, ...)
|
||||
{
|
||||
if (
|
||||
(g_pOptions && g_pOptions->GetCreateLog() && g_pOptions->GetLogFile())
|
||||
#ifdef DEBUG
|
||||
|| (m_szLogFilename && m_bExtraDebug)
|
||||
#endif
|
||||
)
|
||||
{
|
||||
if (!m_szLogFilename)
|
||||
{
|
||||
m_szLogFilename = strdup(g_pOptions->GetLogFile());
|
||||
}
|
||||
|
||||
char tmp2[1024];
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
vsnprintf(tmp2, 1024, msg, ap);
|
||||
tmp2[1024-1] = '\0';
|
||||
va_end(ap);
|
||||
|
||||
time_t rawtime;
|
||||
time(&rawtime);
|
||||
|
||||
char szTime[50];
|
||||
#ifdef HAVE_CTIME_R_3
|
||||
ctime_r(&rawtime, szTime, 50);
|
||||
#else
|
||||
ctime_r(&rawtime, szTime);
|
||||
#endif
|
||||
szTime[50-1] = '\0';
|
||||
szTime[strlen(szTime) - 1] = '\0'; // trim LF
|
||||
|
||||
FILE* file = fopen(m_szLogFilename, "a+");
|
||||
if (file)
|
||||
{
|
||||
#ifdef WIN32
|
||||
unsigned long iThreadId = GetCurrentThreadId();
|
||||
#else
|
||||
unsigned long iThreadId = (unsigned long)pthread_self();
|
||||
#endif
|
||||
#ifdef DEBUG
|
||||
fprintf(file, "%s\t%lu\t%s\n", szTime, iThreadId, tmp2);
|
||||
#else
|
||||
fprintf(file, "%s\t%s\n", szTime, tmp2);
|
||||
#endif
|
||||
fclose(file);
|
||||
}
|
||||
else
|
||||
{
|
||||
perror(m_szLogFilename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#undef debug
|
||||
#ifdef HAVE_VARIADIC_MACROS
|
||||
void debug(const char* szFilename, const char* szFuncname, int iLineNr, const char* msg, ...)
|
||||
#else
|
||||
void debug(const char* msg, ...)
|
||||
#endif
|
||||
{
|
||||
#ifdef DEBUG
|
||||
char tmp1[1024];
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
vsnprintf(tmp1, 1024, msg, ap);
|
||||
tmp1[1024-1] = '\0';
|
||||
va_end(ap);
|
||||
|
||||
char tmp2[1024];
|
||||
#ifdef HAVE_VARIADIC_MACROS
|
||||
if (szFuncname)
|
||||
{
|
||||
snprintf(tmp2, 1024, "%s (%s:%i:%s)", tmp1, BaseFileName(szFilename), iLineNr, szFuncname);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(tmp2, 1024, "%s (%s:%i)", tmp1, BaseFileName(szFilename), iLineNr);
|
||||
}
|
||||
#else
|
||||
snprintf(tmp2, 1024, "%s", tmp1);
|
||||
#endif
|
||||
tmp2[1024-1] = '\0';
|
||||
|
||||
g_pLog->m_mutexLog.Lock();
|
||||
|
||||
if (!g_pOptions)
|
||||
{
|
||||
if (g_pLog->m_bExtraDebug)
|
||||
{
|
||||
printf("%s\n", tmp2);
|
||||
g_pLog->Filelog("DEBUG\t%s", tmp2);
|
||||
}
|
||||
g_pLog->m_mutexLog.Unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetDebugTarget();
|
||||
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->Filelog("DEBUG\t%s", tmp2);
|
||||
}
|
||||
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->AppendMessage(Message::mkDebug, tmp2);
|
||||
}
|
||||
|
||||
g_pLog->m_mutexLog.Unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
void error(const char* msg, ...)
|
||||
{
|
||||
char tmp2[1024];
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
vsnprintf(tmp2, 1024, msg, ap);
|
||||
tmp2[1024-1] = '\0';
|
||||
va_end(ap);
|
||||
|
||||
g_pLog->m_mutexLog.Lock();
|
||||
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetErrorTarget();
|
||||
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->Filelog("ERROR\t%s", tmp2);
|
||||
}
|
||||
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->AppendMessage(Message::mkError, tmp2);
|
||||
}
|
||||
|
||||
g_pLog->m_mutexLog.Unlock();
|
||||
}
|
||||
|
||||
void warn(const char* msg, ...)
|
||||
{
|
||||
char tmp2[1024];
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
vsnprintf(tmp2, 1024, msg, ap);
|
||||
tmp2[1024-1] = '\0';
|
||||
va_end(ap);
|
||||
|
||||
g_pLog->m_mutexLog.Lock();
|
||||
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetWarningTarget();
|
||||
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->Filelog("WARNING\t%s", tmp2);
|
||||
}
|
||||
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->AppendMessage(Message::mkWarning, tmp2);
|
||||
}
|
||||
|
||||
g_pLog->m_mutexLog.Unlock();
|
||||
}
|
||||
|
||||
void info(const char* msg, ...)
|
||||
{
|
||||
char tmp2[1024];
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
vsnprintf(tmp2, 1024, msg, ap);
|
||||
tmp2[1024-1] = '\0';
|
||||
va_end(ap);
|
||||
|
||||
g_pLog->m_mutexLog.Lock();
|
||||
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetInfoTarget();
|
||||
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->Filelog("INFO\t%s", tmp2);
|
||||
}
|
||||
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->AppendMessage(Message::mkInfo, tmp2);
|
||||
}
|
||||
|
||||
g_pLog->m_mutexLog.Unlock();
|
||||
}
|
||||
|
||||
void abort(const char* msg, ...)
|
||||
{
|
||||
char tmp2[1024];
|
||||
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
vsnprintf(tmp2, 1024, msg, ap);
|
||||
tmp2[1024-1] = '\0';
|
||||
va_end(ap);
|
||||
|
||||
g_pLog->m_mutexLog.Lock();
|
||||
|
||||
printf("\n%s", tmp2);
|
||||
|
||||
g_pLog->Filelog(tmp2);
|
||||
|
||||
g_pLog->m_mutexLog.Unlock();
|
||||
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
//************************************************************
|
||||
// Message
|
||||
|
||||
Message::Message(unsigned int iID, EKind eKind, time_t tTime, const char* szText)
|
||||
{
|
||||
m_iID = iID;
|
||||
m_eKind = eKind;
|
||||
m_tTime = tTime;
|
||||
if (szText)
|
||||
{
|
||||
m_szText = strdup(szText);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_szText = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
Message::~ Message()
|
||||
{
|
||||
if (m_szText)
|
||||
{
|
||||
free(m_szText);
|
||||
}
|
||||
}
|
||||
|
||||
void Log::AppendMessage(Message::EKind eKind, const char * szText)
|
||||
{
|
||||
Message* pMessage = new Message(++m_iIDGen, eKind, time(NULL), szText);
|
||||
m_Messages.push_back(pMessage);
|
||||
|
||||
while (m_Messages.size() > (unsigned int)g_pOptions->GetLogBufferSize())
|
||||
{
|
||||
Message* pMessage = m_Messages.front();
|
||||
delete pMessage;
|
||||
m_Messages.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
Log::Messages* Log::LockMessages()
|
||||
{
|
||||
m_mutexLog.Lock();
|
||||
return &m_Messages;
|
||||
}
|
||||
|
||||
void Log::UnlockMessages()
|
||||
{
|
||||
m_mutexLog.Unlock();
|
||||
}
|
||||
|
||||
void Log::ResetLog()
|
||||
{
|
||||
remove(g_pOptions->GetLogFile());
|
||||
}
|
||||
471
Makefile.am
471
Makefile.am
@@ -1,18 +1,459 @@
|
||||
bin_PROGRAMS = nzbget
|
||||
nzbget_SOURCES = ArticleDownloader.cpp ArticleDownloader.h ColoredFrontend.cpp \
|
||||
ColoredFrontend.h Connection.cpp Connection.h Decoder.cpp Decoder.h DiskState.cpp \
|
||||
DiskState.h DownloadInfo.cpp DownloadInfo.h Frontend.cpp Frontend.h Log.cpp Log.h \
|
||||
LoggableFrontend.cpp LoggableFrontend.h MessageBase.h NCursesFrontend.cpp NCursesFrontend.h \
|
||||
NNTPConnection.cpp NNTPConnection.h NZBFile.cpp NZBFile.h NetAddress.cpp NetAddress.h \
|
||||
NewsServer.cpp NewsServer.h Observer.cpp Observer.h Options.cpp Options.h \
|
||||
ParChecker.cpp ParChecker.h PrePostProcessor.cpp PrePostProcessor.h \
|
||||
QueueCoordinator.cpp QueueCoordinator.h QueueEditor.cpp QueueEditor.h RemoteClient.cpp \
|
||||
RemoteClient.h RemoteServer.cpp RemoteServer.h ServerPool.cpp ServerPool.h Thread.cpp \
|
||||
Thread.h Util.cpp Util.h nzbget.cpp nzbget.h
|
||||
#
|
||||
# This file is part of nzbget
|
||||
#
|
||||
# Copyright (C) 2008-2015 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 = \
|
||||
daemon/connect/Connection.cpp \
|
||||
daemon/connect/Connection.h \
|
||||
daemon/connect/TLS.cpp \
|
||||
daemon/connect/TLS.h \
|
||||
daemon/connect/WebDownloader.cpp \
|
||||
daemon/connect/WebDownloader.h \
|
||||
daemon/extension/FeedScript.cpp \
|
||||
daemon/extension/FeedScript.h \
|
||||
daemon/extension/NzbScript.cpp \
|
||||
daemon/extension/NzbScript.h \
|
||||
daemon/extension/PostScript.cpp \
|
||||
daemon/extension/PostScript.h \
|
||||
daemon/extension/QueueScript.cpp \
|
||||
daemon/extension/QueueScript.h \
|
||||
daemon/extension/ScanScript.cpp \
|
||||
daemon/extension/ScanScript.h \
|
||||
daemon/extension/SchedulerScript.cpp \
|
||||
daemon/extension/SchedulerScript.h \
|
||||
daemon/extension/ScriptConfig.cpp \
|
||||
daemon/extension/ScriptConfig.h \
|
||||
daemon/feed/FeedCoordinator.cpp \
|
||||
daemon/feed/FeedCoordinator.h \
|
||||
daemon/feed/FeedFile.cpp \
|
||||
daemon/feed/FeedFile.h \
|
||||
daemon/feed/FeedFilter.cpp \
|
||||
daemon/feed/FeedFilter.h \
|
||||
daemon/feed/FeedInfo.cpp \
|
||||
daemon/feed/FeedInfo.h \
|
||||
daemon/frontend/ColoredFrontend.cpp \
|
||||
daemon/frontend/ColoredFrontend.h \
|
||||
daemon/frontend/Frontend.cpp \
|
||||
daemon/frontend/Frontend.h \
|
||||
daemon/frontend/LoggableFrontend.cpp \
|
||||
daemon/frontend/LoggableFrontend.h \
|
||||
daemon/frontend/NCursesFrontend.cpp \
|
||||
daemon/frontend/NCursesFrontend.h \
|
||||
daemon/main/CommandLineParser.cpp \
|
||||
daemon/main/CommandLineParser.h \
|
||||
daemon/main/DiskService.cpp \
|
||||
daemon/main/DiskService.h \
|
||||
daemon/main/Maintenance.cpp \
|
||||
daemon/main/Maintenance.h \
|
||||
daemon/main/nzbget.cpp \
|
||||
daemon/main/nzbget.h \
|
||||
daemon/main/Options.cpp \
|
||||
daemon/main/Options.h \
|
||||
daemon/main/Scheduler.cpp \
|
||||
daemon/main/Scheduler.h \
|
||||
daemon/main/StackTrace.cpp \
|
||||
daemon/main/StackTrace.h \
|
||||
daemon/nntp/ArticleDownloader.cpp \
|
||||
daemon/nntp/ArticleDownloader.h \
|
||||
daemon/nntp/ArticleWriter.cpp \
|
||||
daemon/nntp/ArticleWriter.h \
|
||||
daemon/nntp/Decoder.cpp \
|
||||
daemon/nntp/Decoder.h \
|
||||
daemon/nntp/NewsServer.cpp \
|
||||
daemon/nntp/NewsServer.h \
|
||||
daemon/nntp/NNTPConnection.cpp \
|
||||
daemon/nntp/NNTPConnection.h \
|
||||
daemon/nntp/ServerPool.cpp \
|
||||
daemon/nntp/ServerPool.h \
|
||||
daemon/nntp/StatMeter.cpp \
|
||||
daemon/nntp/StatMeter.h \
|
||||
daemon/postprocess/Cleanup.cpp \
|
||||
daemon/postprocess/Cleanup.h \
|
||||
daemon/postprocess/DupeMatcher.cpp \
|
||||
daemon/postprocess/DupeMatcher.h \
|
||||
daemon/postprocess/ParChecker.cpp \
|
||||
daemon/postprocess/ParChecker.h \
|
||||
daemon/postprocess/ParCoordinator.cpp \
|
||||
daemon/postprocess/ParCoordinator.h \
|
||||
daemon/postprocess/ParParser.cpp \
|
||||
daemon/postprocess/ParParser.h \
|
||||
daemon/postprocess/ParRenamer.cpp \
|
||||
daemon/postprocess/ParRenamer.h \
|
||||
daemon/postprocess/PrePostProcessor.cpp \
|
||||
daemon/postprocess/PrePostProcessor.h \
|
||||
daemon/postprocess/Unpack.cpp \
|
||||
daemon/postprocess/Unpack.h \
|
||||
daemon/queue/DiskState.cpp \
|
||||
daemon/queue/DiskState.h \
|
||||
daemon/queue/DownloadInfo.cpp \
|
||||
daemon/queue/DownloadInfo.h \
|
||||
daemon/queue/DupeCoordinator.cpp \
|
||||
daemon/queue/DupeCoordinator.h \
|
||||
daemon/queue/HistoryCoordinator.cpp \
|
||||
daemon/queue/HistoryCoordinator.h \
|
||||
daemon/queue/NZBFile.cpp \
|
||||
daemon/queue/NZBFile.h \
|
||||
daemon/queue/QueueCoordinator.cpp \
|
||||
daemon/queue/QueueCoordinator.h \
|
||||
daemon/queue/QueueEditor.cpp \
|
||||
daemon/queue/QueueEditor.h \
|
||||
daemon/queue/Scanner.cpp \
|
||||
daemon/queue/Scanner.h \
|
||||
daemon/queue/UrlCoordinator.cpp \
|
||||
daemon/queue/UrlCoordinator.h \
|
||||
daemon/remote/BinRpc.cpp \
|
||||
daemon/remote/BinRpc.h \
|
||||
daemon/remote/MessageBase.h \
|
||||
daemon/remote/RemoteClient.cpp \
|
||||
daemon/remote/RemoteClient.h \
|
||||
daemon/remote/RemoteServer.cpp \
|
||||
daemon/remote/RemoteServer.h \
|
||||
daemon/remote/WebServer.cpp \
|
||||
daemon/remote/WebServer.h \
|
||||
daemon/remote/XmlRpc.cpp \
|
||||
daemon/remote/XmlRpc.h \
|
||||
daemon/util/Log.cpp \
|
||||
daemon/util/Log.h \
|
||||
daemon/util/Observer.cpp \
|
||||
daemon/util/Observer.h \
|
||||
daemon/util/Script.cpp \
|
||||
daemon/util/Script.h \
|
||||
daemon/util/Thread.cpp \
|
||||
daemon/util/Thread.h \
|
||||
daemon/util/Service.cpp \
|
||||
daemon/util/Service.h \
|
||||
daemon/util/Util.cpp \
|
||||
daemon/util/Util.h \
|
||||
code_revision.cpp
|
||||
|
||||
if WITH_PAR2
|
||||
nzbget_SOURCES += \
|
||||
lib/par2/commandline.cpp \
|
||||
lib/par2/commandline.h \
|
||||
lib/par2/crc.cpp \
|
||||
lib/par2/crc.h \
|
||||
lib/par2/creatorpacket.cpp \
|
||||
lib/par2/creatorpacket.h \
|
||||
lib/par2/criticalpacket.cpp \
|
||||
lib/par2/criticalpacket.h \
|
||||
lib/par2/datablock.cpp \
|
||||
lib/par2/datablock.h \
|
||||
lib/par2/descriptionpacket.cpp \
|
||||
lib/par2/descriptionpacket.h \
|
||||
lib/par2/diskfile.cpp \
|
||||
lib/par2/diskfile.h \
|
||||
lib/par2/filechecksummer.cpp \
|
||||
lib/par2/filechecksummer.h \
|
||||
lib/par2/galois.cpp \
|
||||
lib/par2/galois.h \
|
||||
lib/par2/letype.h \
|
||||
lib/par2/mainpacket.cpp \
|
||||
lib/par2/mainpacket.h \
|
||||
lib/par2/md5.cpp \
|
||||
lib/par2/md5.h \
|
||||
lib/par2/par2cmdline.h \
|
||||
lib/par2/par2creatorsourcefile.cpp \
|
||||
lib/par2/par2creatorsourcefile.h \
|
||||
lib/par2/par2fileformat.cpp \
|
||||
lib/par2/par2fileformat.h \
|
||||
lib/par2/par2repairer.cpp \
|
||||
lib/par2/par2repairer.h \
|
||||
lib/par2/par2repairersourcefile.cpp \
|
||||
lib/par2/par2repairersourcefile.h \
|
||||
lib/par2/parheaders.cpp \
|
||||
lib/par2/parheaders.h \
|
||||
lib/par2/recoverypacket.cpp \
|
||||
lib/par2/recoverypacket.h \
|
||||
lib/par2/reedsolomon.cpp \
|
||||
lib/par2/reedsolomon.h \
|
||||
lib/par2/verificationhashtable.cpp \
|
||||
lib/par2/verificationhashtable.h \
|
||||
lib/par2/verificationpacket.cpp \
|
||||
lib/par2/verificationpacket.h
|
||||
endif
|
||||
|
||||
AM_CPPFLAGS = \
|
||||
-I$(srcdir)/daemon/connect \
|
||||
-I$(srcdir)/daemon/extension \
|
||||
-I$(srcdir)/daemon/feed \
|
||||
-I$(srcdir)/daemon/frontend \
|
||||
-I$(srcdir)/daemon/main \
|
||||
-I$(srcdir)/daemon/nntp \
|
||||
-I$(srcdir)/daemon/postprocess \
|
||||
-I$(srcdir)/daemon/queue \
|
||||
-I$(srcdir)/daemon/remote \
|
||||
-I$(srcdir)/daemon/util \
|
||||
-I$(srcdir)/lib/par2
|
||||
|
||||
if WITH_TESTS
|
||||
nzbget_SOURCES += \
|
||||
lib/catch/catch.h \
|
||||
tests/suite/TestMain.cpp \
|
||||
tests/suite/TestMain.h \
|
||||
tests/suite/TestUtil.cpp \
|
||||
tests/suite/TestUtil.h \
|
||||
tests/main/CommandLineParserTest.cpp \
|
||||
tests/main/OptionsTest.cpp \
|
||||
tests/feed/FeedFilterTest.cpp \
|
||||
tests/postprocess/ParCheckerTest.cpp \
|
||||
tests/postprocess/ParRenamerTest.cpp \
|
||||
tests/queue/NZBFileTest.cpp \
|
||||
tests/util/UtilTest.cpp
|
||||
|
||||
AM_CPPFLAGS += \
|
||||
-I$(srcdir)/lib/catch \
|
||||
-I$(srcdir)/tests/suite
|
||||
endif
|
||||
|
||||
EXTRA_DIST = \
|
||||
$(windows_FILES) \
|
||||
$(osx_FILES) \
|
||||
$(linux_FILES) \
|
||||
$(testdata_FILES)
|
||||
|
||||
windows_FILES = \
|
||||
daemon/windows/NTService.cpp \
|
||||
daemon/windows/NTService.h \
|
||||
daemon/windows/win32.h \
|
||||
daemon/windows/WinConsole.cpp \
|
||||
daemon/windows/WinConsole.h \
|
||||
nzbget.vcproj \
|
||||
windows/nzbget-command-shell.bat \
|
||||
windows/install-update.bat \
|
||||
windows/README-WINDOWS.txt \
|
||||
windows/package-info.json \
|
||||
windows/resources/mainicon.ico \
|
||||
windows/resources/nzbget.rc \
|
||||
windows/resources/resource.h \
|
||||
windows/resources/trayicon_idle.ico \
|
||||
windows/resources/trayicon_paused.ico \
|
||||
windows/resources/trayicon_working.ico \
|
||||
windows/setup/nzbget-setup.nsi \
|
||||
windows/setup/install.bmp \
|
||||
windows/setup/uninstall.bmp
|
||||
|
||||
osx_FILES = \
|
||||
osx/App_Prefix.pch \
|
||||
osx/NZBGet-Info.plist \
|
||||
osx/DaemonController.h \
|
||||
osx/DaemonController.m \
|
||||
osx/MainApp.h \
|
||||
osx/MainApp.m \
|
||||
osx/MainApp.xib \
|
||||
osx/PFMoveApplication.h \
|
||||
osx/PFMoveApplication.m \
|
||||
osx/PreferencesDialog.h \
|
||||
osx/PreferencesDialog.m \
|
||||
osx/PreferencesDialog.xib \
|
||||
osx/RPC.h \
|
||||
osx/RPC.m \
|
||||
osx/WebClient.h \
|
||||
osx/WebClient.m \
|
||||
osx/WelcomeDialog.h \
|
||||
osx/WelcomeDialog.m \
|
||||
osx/WelcomeDialog.xib \
|
||||
osx/NZBGet.xcodeproj/project.pbxproj \
|
||||
osx/Resources/Images/mainicon.icns \
|
||||
osx/Resources/Images/statusicon.png \
|
||||
osx/Resources/Images/statusicon@2x.png \
|
||||
osx/Resources/licenses/license-bootstrap.txt \
|
||||
osx/Resources/licenses/license-jquery-GPL.txt \
|
||||
osx/Resources/licenses/license-jquery-MIT.txt \
|
||||
osx/Resources/Credits.rtf \
|
||||
osx/Resources/Localizable.strings \
|
||||
osx/Resources/Welcome.rtf
|
||||
|
||||
linux_FILES = \
|
||||
linux/installer.sh \
|
||||
linux/install-update.sh \
|
||||
linux/package-info.json \
|
||||
linux/build-info.txt \
|
||||
linux/build-nzbget \
|
||||
linux/build-unpack
|
||||
|
||||
doc_FILES = \
|
||||
lib/par2/AUTHORS \
|
||||
lib/par2/README \
|
||||
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/lib/raphael.js \
|
||||
webui/lib/raphael.min.js \
|
||||
webui/lib/elycharts.js \
|
||||
webui/lib/elycharts.min.js \
|
||||
webui/img/icons.png \
|
||||
webui/img/icons-2x.png \
|
||||
webui/img/transmit.gif \
|
||||
webui/img/transmit-file.gif \
|
||||
webui/img/favicon.ico \
|
||||
webui/img/download-anim-green-2x.png \
|
||||
webui/img/download-anim-orange-2x.png \
|
||||
webui/img/transmit-reload-2x.gif
|
||||
|
||||
scripts_FILES = \
|
||||
scripts/EMail.py \
|
||||
scripts/Logger.py
|
||||
|
||||
testdata_FILES = \
|
||||
tests/testdata/dupematcher1/testfile.part01.rar \
|
||||
tests/testdata/dupematcher1/testfile.part24.rar \
|
||||
tests/testdata/dupematcher2/testfile.part04.rar \
|
||||
tests/testdata/dupematcher2/testfile.part43.rar \
|
||||
tests/testdata/nzbfile/dotless.nzb \
|
||||
tests/testdata/nzbfile/dotless.txt \
|
||||
tests/testdata/nzbfile/plain.nzb \
|
||||
tests/testdata/nzbfile/plain.txt \
|
||||
tests/testdata/parchecker/crc.txt \
|
||||
tests/testdata/parchecker/testfile.dat \
|
||||
tests/testdata/parchecker/testfile.nfo \
|
||||
tests/testdata/parchecker/testfile.par2 \
|
||||
tests/testdata/parchecker/testfile.vol00+1.PAR2 \
|
||||
tests/testdata/parchecker/testfile.vol01+2.PAR2 \
|
||||
tests/testdata/parchecker/testfile.vol03+3.PAR2
|
||||
|
||||
# Install
|
||||
dist_doc_DATA = $(doc_FILES)
|
||||
exampleconfdir = $(datadir)/nzbget
|
||||
dist_exampleconf_DATA = $(exampleconf_FILES)
|
||||
webuidir = $(datadir)/nzbget
|
||||
nobase_dist_webui_DATA = $(webui_FILES)
|
||||
scriptsdir = $(datadir)/nzbget
|
||||
nobase_dist_scripts_SCRIPTS = $(scripts_FILES)
|
||||
|
||||
# Note about "sed":
|
||||
# We need to make some changes in installed files.
|
||||
# On Linux "sed" has option "-i" for in-place-edit. Unfortunateley the BSD version of "sed"
|
||||
# has incompatible syntax. To solve the problem we perform in-place-edit in three steps:
|
||||
# 1) copy the original file to original.temp (delete existing original.temp, if any);
|
||||
# 2) sed < original.temp > original
|
||||
# 3) delete original.temp
|
||||
# These steps ensure that the output file has the same permissions as the original file.
|
||||
|
||||
# Prepare example configuration file
|
||||
install-data-hook:
|
||||
rm -f "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
|
||||
cp "$(DESTDIR)$(exampleconfdir)/nzbget.conf" "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
|
||||
sed 's:^ConfigTemplate=:ConfigTemplate=$(exampleconfdir)/nzbget.conf:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
|
||||
sed 's:configuration file (typically installed:configuration file (installed:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
|
||||
sed 's:/usr/local/share/nzbget/nzbget.conf):$(exampleconfdir)/nzbget.conf):' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
|
||||
sed 's:^WebDir=:WebDir=$(webuidir)/webui:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
|
||||
sed 's:typically installed to /usr/local/share/nzbget/scripts:installed to $(scriptsdir)/scripts:' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf"
|
||||
rm "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
|
||||
|
||||
# Install configuration files into /etc
|
||||
# (only if they do not exist there to prevent override by update)
|
||||
install-conf:
|
||||
if test ! -f "$(DESTDIR)$(sysconfdir)/nzbget.conf" ; then \
|
||||
$(mkinstalldirs) "$(DESTDIR)$(sysconfdir)" ; \
|
||||
cp "$(DESTDIR)$(exampleconfdir)/nzbget.conf" "$(DESTDIR)$(sysconfdir)/nzbget.conf" ; \
|
||||
fi
|
||||
|
||||
uninstall-conf:
|
||||
rm -f "$(DESTDIR)$(sysconfdir)/nzbget.conf"
|
||||
|
||||
# Determining git revision:
|
||||
# 1) If directory ".git" exists we take revision from git log.
|
||||
# File is recreated only if revision number was changed.
|
||||
# 2) If directory ".git" doesn't exists we keep and reuse file "code_revision.cpp",
|
||||
# which was possibly created early.
|
||||
# 3) If neither directory ".git" nor file "code_revision.cpp" are available
|
||||
# we create new file "code_revision.c" with empty revision number.
|
||||
code_revision.cpp: FORCE
|
||||
@ if test -d ./.git ; then \
|
||||
B="$(shell git branch | sed -n -e 's/^\* \(.*\)/\1/p')"; \
|
||||
M="$(shell git status --porcelain)" ; \
|
||||
if test "$$M" != "" ; then \
|
||||
M="M" ; \
|
||||
fi ; \
|
||||
if test "$$B" = "master" ; then \
|
||||
V="$$M" ; \
|
||||
elif test "$$B" = "develop" ; then \
|
||||
V="$(shell git rev-list HEAD | wc -l | xargs)" ; \
|
||||
V="$${V}$$M" ; \
|
||||
else \
|
||||
V="$(shell git rev-list HEAD | wc -l | xargs)" ; \
|
||||
V="$${V}$$M ($$B)" ; \
|
||||
fi ; \
|
||||
H="$(shell test -f ./code_revision.cpp && head -n 1 code_revision.cpp)"; \
|
||||
if test "/* $$V */" != "$$H" ; then \
|
||||
( \
|
||||
echo "/* $$V */" ;\
|
||||
echo "/* This file is automatically regenerated on each build. Do not edit it. */" ;\
|
||||
echo "const char* code_revision(void)" ;\
|
||||
echo "{" ;\
|
||||
echo " const char* revision = \"$$V\";" ;\
|
||||
echo " return revision;" ;\
|
||||
echo "}" ;\
|
||||
) > code_revision.cpp ; \
|
||||
fi \
|
||||
elif test -f ./code_revision.cpp ; then \
|
||||
test "ok, reuse existing file"; \
|
||||
else \
|
||||
( \
|
||||
echo "/* */" ;\
|
||||
echo "/* This file is automatically regenerated on each build. Do not edit it. */" ;\
|
||||
echo "const char* code_revision(void)" ;\
|
||||
echo "{" ;\
|
||||
echo " const char* revision = \"\";" ;\
|
||||
echo " return revision;" ;\
|
||||
echo "}" ;\
|
||||
) > code_revision.cpp ; \
|
||||
fi
|
||||
FORCE:
|
||||
|
||||
# Ignore "code_revision.cpp" in distcleancheck
|
||||
distcleancheck_listfiles = \
|
||||
find . -type f -exec sh -c 'test -f $(srcdir)/$$1 || echo $$1' \
|
||||
sh '{}' ';'
|
||||
|
||||
clean-bak: rm *~
|
||||
|
||||
# Fix premissions
|
||||
dist-hook:
|
||||
find $(distdir)/daemon -type f -print -exec chmod -x {} \;
|
||||
find $(distdir)/webui -type f -print -exec chmod -x {} \;
|
||||
find $(distdir)/lib -type f -print -exec chmod -x {} \;
|
||||
find $(distdir)/tests -type f -print -exec chmod -x {} \;
|
||||
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
default: all
|
||||
|
||||
all:
|
||||
aclocal
|
||||
autoheader
|
||||
automake
|
||||
autoconf
|
||||
|
||||
2164
Makefile.in
vendored
2164
Makefile.in
vendored
File diff suppressed because it is too large
Load Diff
302
MessageBase.h
302
MessageBase.h
@@ -1,302 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef MESSAGEBASE_H
|
||||
#define MESSAGEBASE_H
|
||||
|
||||
static const int32_t NZBMESSAGE_SIGNATURE = 0x6E7A6202; // = "nzb2" (nzb version 2)
|
||||
static const int NZBREQUESTFILENAMESIZE = 512;
|
||||
static const int NZBREQUESTPASSWORDSIZE = 32;
|
||||
|
||||
/**
|
||||
* NZBGet communication protocol uses only two basic data types: integer and char.
|
||||
* Integer values are passed using network byte order (Big-Endian).
|
||||
* Use function "htonl" and "ntohl" to convert integers to/from machine
|
||||
' (host) byte order.
|
||||
* All char-strings must end with NULL-char.
|
||||
*/
|
||||
|
||||
// The pack-directive prevents aligning of structs.
|
||||
// This makes them more portable and allows to use together servers and clients
|
||||
// compiled on different cpu architectures
|
||||
#ifdef HAVE_PRAGMA_PACK
|
||||
#pragma pack(1)
|
||||
#endif
|
||||
|
||||
// Possible values for field "m_iType" of struct "SNZBRequestBase":
|
||||
enum eRemoteRequest
|
||||
{
|
||||
eRemoteRequestDownload = 1,
|
||||
eRemoteRequestPauseUnpause,
|
||||
eRemoteRequestList,
|
||||
eRemoteRequestSetDownloadRate,
|
||||
eRemoteRequestDumpDebug,
|
||||
eRemoteRequestEditQueue,
|
||||
eRemoteRequestLog,
|
||||
eRemoteRequestShutdown,
|
||||
eRemoteRequestVersion
|
||||
};
|
||||
|
||||
// Possible values for field "m_iAction" of struct "SNZBEditQueueRequest":
|
||||
// File-Actions affect one file, Group-Actions affect all files in group.
|
||||
// Group is a list of files, added to queue from one NZB-File.
|
||||
enum eRemoteEditAction
|
||||
{
|
||||
eRemoteEditActionFileMoveOffset = 1, // move to m_iOffset relative to the current position in queue
|
||||
eRemoteEditActionFileMoveTop, // move to top of queue
|
||||
eRemoteEditActionFileMoveBottom, // move to bottom of queue
|
||||
eRemoteEditActionFilePause, // pause
|
||||
eRemoteEditActionFileResume, // resume (unpause)
|
||||
eRemoteEditActionFileDelete, // delete
|
||||
eRemoteEditActionFilePauseAllPars, // pause only (all) pars (does not affect other files)
|
||||
eRemoteEditActionFilePauseExtraPars, // pause only (almost all) pars, except main par-file (does not affect other files)
|
||||
eRemoteEditActionGroupMoveOffset, // move to m_iOffset relative to the current position in queue
|
||||
eRemoteEditActionGroupMoveTop, // move to top of queue
|
||||
eRemoteEditActionGroupMoveBottom, // move to bottom of queue
|
||||
eRemoteEditActionGroupPause, // pause
|
||||
eRemoteEditActionGroupResume, // resume (unpause)
|
||||
eRemoteEditActionGroupDelete, // delete
|
||||
eRemoteEditActionGroupPauseAllPars, // pause only (all) pars (does not affect other files)
|
||||
eRemoteEditActionGroupPauseExtraPars // pause only (almost all) pars, except main par-file (does not affect other files)
|
||||
};
|
||||
|
||||
// The basic SNZBRequestBase struct, used in all requests
|
||||
struct SNZBRequestBase
|
||||
{
|
||||
int32_t m_iSignature; // Signature must be NZBMESSAGE_SIGNATURE in integer-value
|
||||
int32_t m_iStructSize; // Size of the entire struct
|
||||
int32_t m_iType; // Message type, see enum in NZBMessageRequest-namespace
|
||||
char m_szPassword[NZBREQUESTPASSWORDSIZE]; // Password needs to be in every request
|
||||
};
|
||||
|
||||
// The basic SNZBResposneBase struct, used in all responses
|
||||
struct SNZBResponseBase
|
||||
{
|
||||
int32_t m_iSignature; // Signature must be NZBMESSAGE_SIGNATURE in integer-value
|
||||
int32_t m_iStructSize; // Size of the entire struct
|
||||
};
|
||||
|
||||
// A download request
|
||||
struct SNZBDownloadRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
char m_szFilename[NZBREQUESTFILENAMESIZE]; // Name of nzb-file, may contain full path (local path on client) or only filename
|
||||
int32_t m_bAddFirst; // 1 - add file to the top of download queue
|
||||
int32_t m_iTrailingDataLength; // Length of nzb-file in bytes
|
||||
//char m_szContent[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// A download response
|
||||
struct SNZBDownloadResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
|
||||
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// A list and status request
|
||||
struct SNZBListRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bFileList; // 1 - return file list
|
||||
int32_t m_bServerState; // 1 - return server state
|
||||
};
|
||||
|
||||
// A list response
|
||||
struct SNZBListResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iEntrySize; // Size of the SNZBListResponseEntry-struct
|
||||
int32_t m_iRemainingSizeLo; // Remaining size in bytes, Low 32-bits of 64-bit value
|
||||
int32_t m_iRemainingSizeHi; // Remaining size in bytes, High 32-bits of 64-bit value
|
||||
int32_t m_iDownloadRate; // Current download speed, in Bytes pro Second
|
||||
int32_t m_iDownloadLimit; // Current download limit, in Bytes pro Second
|
||||
int32_t m_bServerPaused; // 1 - server is currently in paused-state
|
||||
int32_t m_iThreadCount; // Number of threads running
|
||||
int32_t m_iParJobCount; // Number of ParJobs in Par-Checker queue (including current file)
|
||||
int32_t m_iUpTimeSec; // Server up time in seconds
|
||||
int32_t m_iDownloadTimeSec; // Server download time in seconds (up_time - standby_time)
|
||||
int32_t m_iDownloadedBytesLo; // Amount of data downloaded since server start, Low 32-bits of 64-bit value
|
||||
int32_t m_iDownloadedBytesHi; // Amount of data downloaded since server start, High 32-bits of 64-bit value
|
||||
int32_t m_bServerStandBy; // 0 - there are currently downloads running, 1 - no downloads in progress (server paused or all jobs completed)
|
||||
int32_t m_iNrTrailingEntries; // Number of List-entries, following to this structure
|
||||
int32_t m_iTrailingDataLength; // Length of all List-entries, following to this structure
|
||||
// SNZBListResponseEntry m_Entries[m_iNrTrailingEntries] // variable sized
|
||||
};
|
||||
|
||||
// A list response entry
|
||||
struct SNZBListResponseEntry
|
||||
{
|
||||
int32_t m_iID; // Entry-ID
|
||||
int32_t m_iFileSizeLo; // Filesize in bytes, Low 32-bits of 64-bit value
|
||||
int32_t m_iFileSizeHi; // Filesize in bytes, High 32-bits of 64-bit value
|
||||
int32_t m_iRemainingSizeLo; // Remaining size in bytes, Low 32-bits of 64-bit value
|
||||
int32_t m_iRemainingSizeHi; // Remaining size in bytes, High 32-bits of 64-bit value
|
||||
int32_t m_bPaused; // 1 - file is paused
|
||||
int32_t m_bFilenameConfirmed; // 1 - Filename confirmed (read from article body), 0 - Filename parsed from subject (can be changed after reading of article)
|
||||
int32_t m_iNZBFilenameLen; // Length of NZBFileName-string (m_szNZBFilename), following to this record
|
||||
int32_t m_iSubjectLen; // Length of Subject-string (m_szSubject), following to this record
|
||||
int32_t m_iFilenameLen; // Length of Filename-string (m_szFilename), following to this record
|
||||
int32_t m_iDestDirLen; // Length of DestDir-string (m_szDestDir), following to this record
|
||||
//char m_szNZBFilename[m_iNZBFilenameLen]; // variable sized, may contain full path (local path on client) or only filename
|
||||
//char m_szSubject[m_iSubjectLen]; // variable sized
|
||||
//char m_szFilename[m_iFilenameLen]; // variable sized
|
||||
//char m_szDestDir[m_iDestDirLen]; // variable sized
|
||||
};
|
||||
|
||||
// A log request
|
||||
struct SNZBLogRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iIDFrom; // Only one of these two parameters
|
||||
int32_t m_iLines; // can be set. The another one must be set to "0".
|
||||
};
|
||||
|
||||
// A log response
|
||||
struct SNZBLogResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iEntrySize; // Size of the SNZBLogResponseEntry-struct
|
||||
int32_t m_iNrTrailingEntries; // Number of Log-entries, following to this structure
|
||||
int32_t m_iTrailingDataLength; // Length of all Log-entries, following to this structure
|
||||
// SNZBLogResponseEntry m_Entries[m_iNrTrailingEntries] // variable sized
|
||||
};
|
||||
|
||||
// A log response entry
|
||||
struct SNZBLogResponseEntry
|
||||
{
|
||||
int32_t m_iID; // ID of Log-entry
|
||||
int32_t m_iKind; // see Message::Kind in "Log.h"
|
||||
int32_t m_tTime; // time since the Epoch (00:00:00 UTC, January 1, 1970), measured in seconds.
|
||||
int32_t m_iTextLen; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTextLen]; // variable sized
|
||||
};
|
||||
|
||||
// A Pause/Unpause request
|
||||
struct SNZBPauseUnpauseRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bPause; // 1 - server must be paused, 0 - server must be unpaused
|
||||
};
|
||||
|
||||
// A Pause/Unpause response
|
||||
struct SNZBPauseUnpauseResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
|
||||
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// Request setting the download rate
|
||||
struct SNZBSetDownloadRateRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iDownloadRate; // Speed limit, in Bytes pro Second
|
||||
};
|
||||
|
||||
// A setting download rate response
|
||||
struct SNZBSetDownloadRateResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
|
||||
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// An edit queue request
|
||||
struct SNZBEditQueueRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iAction; // Action to be executed, see enum in NZBMessageRequest-namespace
|
||||
int32_t m_iOffset; // Offset to move (for m_iAction = 0)
|
||||
int32_t m_bSmartOrder; // For Move-Actions: 0 - execute action for each ID in order they are placed in array;
|
||||
// 1 - smart execute to ensure that the relative order of all affected IDs are not changed.
|
||||
int32_t m_iNrTrailingEntries; // Number of ID-entries, following to this structure
|
||||
int32_t m_iTrailingDataLength; // Length of all ID-entries, following to this structure
|
||||
//int32_t m_iIDs[m_iNrTrailingEntries]; // variable sized array of IDs. For File-Actions - ID of file, for Group-Actions - ID of any file belonging to group
|
||||
};
|
||||
|
||||
// An edit queue response
|
||||
struct SNZBEditQueueResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
|
||||
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// Request dumping of debug info
|
||||
struct SNZBDumpDebugRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
};
|
||||
|
||||
// Dumping of debug response
|
||||
struct SNZBDumpDebugResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
|
||||
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// Shutdown server request
|
||||
struct SNZBShutdownRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
};
|
||||
|
||||
// Shutdown server response
|
||||
struct SNZBShutdownResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
|
||||
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// Server version request
|
||||
struct SNZBVersionRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
};
|
||||
|
||||
// Server version response
|
||||
struct SNZBVersionResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
|
||||
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
#ifdef HAVE_PRAGMA_PACK
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,58 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef NNTPCONNECTION_H
|
||||
#define NNTPCONNECTION_H
|
||||
|
||||
#include "NewsServer.h"
|
||||
#include "Connection.h"
|
||||
|
||||
class NNTPConnection : public Connection
|
||||
{
|
||||
private:
|
||||
char* m_szActiveGroup;
|
||||
char* m_szLineBuf;
|
||||
bool m_bAuthError;
|
||||
|
||||
virtual int DoConnect();
|
||||
virtual int DoDisconnect();
|
||||
void Clear();
|
||||
|
||||
public:
|
||||
NNTPConnection(NewsServer* server);
|
||||
~NNTPConnection();
|
||||
NewsServer* GetNewsServer() { return(NewsServer*)m_pNetAddress; }
|
||||
char* Request(char* req);
|
||||
bool Authenticate();
|
||||
bool AuthInfoUser(int iRecur = 0);
|
||||
bool AuthInfoPass(int iRecur = 0);
|
||||
bool JoinGroup(char* grp);
|
||||
bool GetAuthError() { return m_bAuthError; }
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
582
NZBFile.cpp
582
NZBFile.cpp
@@ -1,582 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
#include <list>
|
||||
#ifdef WIN32
|
||||
#include <comutil.h>
|
||||
#import "MSXML.dll" named_guids
|
||||
using namespace MSXML;
|
||||
#else
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/xmlreader.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "NZBFile.h"
|
||||
#include "Log.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Options.h"
|
||||
#include "DiskState.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
extern DiskState* g_pDiskState;
|
||||
|
||||
NZBFile::NZBFile(const char* szFileName)
|
||||
{
|
||||
debug("Creating NZBFile");
|
||||
|
||||
m_szFileName = strdup(szFileName);
|
||||
|
||||
m_FileInfos.clear();
|
||||
}
|
||||
|
||||
NZBFile::~NZBFile()
|
||||
{
|
||||
debug("Destroying NZBFile");
|
||||
|
||||
// Cleanup
|
||||
if (m_szFileName)
|
||||
{
|
||||
free(m_szFileName);
|
||||
}
|
||||
|
||||
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_FileInfos.clear();
|
||||
}
|
||||
|
||||
void NZBFile::LogDebugInfo()
|
||||
{
|
||||
debug(" NZBFile %s", m_szFileName);
|
||||
}
|
||||
|
||||
void NZBFile::DetachFileInfos()
|
||||
{
|
||||
m_FileInfos.clear();
|
||||
}
|
||||
|
||||
NZBFile* NZBFile::CreateFromBuffer(const char* szFileName, const char* szBuffer, int iSize)
|
||||
{
|
||||
return Create(szFileName, szBuffer, iSize, true);
|
||||
}
|
||||
|
||||
NZBFile* NZBFile::CreateFromFile(const char* szFileName)
|
||||
{
|
||||
return Create(szFileName, NULL, 0, false);
|
||||
}
|
||||
|
||||
void NZBFile::AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo)
|
||||
{
|
||||
// make Article-List big enough
|
||||
while ((int)pFileInfo->GetArticles()->size() < pArticleInfo->GetPartNumber())
|
||||
pFileInfo->GetArticles()->push_back(NULL);
|
||||
|
||||
(*pFileInfo->GetArticles())[pArticleInfo->GetPartNumber() - 1] = pArticleInfo;
|
||||
}
|
||||
|
||||
void NZBFile::AddFileInfo(FileInfo* pFileInfo)
|
||||
{
|
||||
// deleting empty articles
|
||||
FileInfo::Articles* pArticles = pFileInfo->GetArticles();
|
||||
int i = 0;
|
||||
for (FileInfo::Articles::iterator it = pArticles->begin(); it != pArticles->end();)
|
||||
{
|
||||
if (*it == NULL)
|
||||
{
|
||||
pArticles->erase(it);
|
||||
it = pArticles->begin() + i;
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pArticles->empty())
|
||||
{
|
||||
ParseSubject(pFileInfo);
|
||||
BuildDestDirName(pFileInfo);
|
||||
m_FileInfos.push_back(pFileInfo);
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->SaveFile(pFileInfo);
|
||||
pFileInfo->ClearArticles();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
delete pFileInfo;
|
||||
}
|
||||
}
|
||||
|
||||
void NZBFile::ParseSubject(FileInfo* pFileInfo)
|
||||
{
|
||||
// tokenize subject, considering spaces as separators and quotation
|
||||
// marks as non separatable token delimiters.
|
||||
// then take the last token containing dot (".") as a filename
|
||||
|
||||
typedef std::list<char*> TokenList;
|
||||
TokenList tokens;
|
||||
tokens.clear();
|
||||
|
||||
// tokenizing
|
||||
char* p = (char*)pFileInfo->GetSubject();
|
||||
char* start = p;
|
||||
bool quot = false;
|
||||
while (true)
|
||||
{
|
||||
char ch = *p;
|
||||
bool sep = (ch == '\"') || (!quot && ch == ' ') || (ch == '\0');
|
||||
if (sep)
|
||||
{
|
||||
// end of token
|
||||
int len = p - start;
|
||||
if (len > 0)
|
||||
{
|
||||
char* token = (char*)malloc(len + 1);
|
||||
strncpy(token, start, len);
|
||||
token[len] = '\0';
|
||||
tokens.push_back(token);
|
||||
}
|
||||
start = p;
|
||||
if (ch != '\"' || quot)
|
||||
{
|
||||
start++;
|
||||
}
|
||||
quot = *start == '\"';
|
||||
if (quot)
|
||||
{
|
||||
start++;
|
||||
char* q = strchr(start, '\"');
|
||||
if (q)
|
||||
{
|
||||
p = q - 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
quot = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (ch == '\0')
|
||||
{
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
if (!tokens.empty())
|
||||
{
|
||||
// finding the best candidate for being a filename
|
||||
char* besttoken = tokens.back();
|
||||
for (TokenList::reverse_iterator it = tokens.rbegin(); it != tokens.rend(); it++)
|
||||
{
|
||||
char* s = *it;
|
||||
char* p = strchr(s, '.');
|
||||
if (p && (p[1] != '\0'))
|
||||
{
|
||||
besttoken = s;
|
||||
break;
|
||||
}
|
||||
}
|
||||
pFileInfo->SetFilename(besttoken);
|
||||
|
||||
// free mem
|
||||
for (TokenList::iterator it = tokens.begin(); it != tokens.end(); it++)
|
||||
{
|
||||
free(*it);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// subject is empty or contains only separators?
|
||||
debug("Could not extract Filename from Subject: %s. Using Subject as Filename", pFileInfo->GetSubject());
|
||||
pFileInfo->SetFilename(pFileInfo->GetSubject());
|
||||
}
|
||||
|
||||
pFileInfo->MakeValidFilename();
|
||||
}
|
||||
|
||||
void NZBFile::BuildDestDirName(FileInfo* pFileInfo)
|
||||
{
|
||||
char szBuffer[1024];
|
||||
|
||||
if (g_pOptions->GetAppendNZBDir())
|
||||
{
|
||||
char szNiceNZBName[1024];
|
||||
pFileInfo->GetNiceNZBName(szNiceNZBName, 1024);
|
||||
snprintf(szBuffer, 1024, "%s%s", g_pOptions->GetDestDir(), szNiceNZBName);
|
||||
szBuffer[1024-1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(szBuffer, g_pOptions->GetDestDir(), 1024);
|
||||
szBuffer[1024-1] = '\0'; // trim the last slash, always returned by GetDestDir()
|
||||
}
|
||||
|
||||
pFileInfo->SetDestDir(szBuffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the parsing of subject was correct
|
||||
*/
|
||||
void NZBFile::CheckFilenames()
|
||||
{
|
||||
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo1 = *it;
|
||||
int iDupe = 0;
|
||||
for (FileInfos::iterator it2 = it + 1; it2 != m_FileInfos.end(); it2++)
|
||||
{
|
||||
FileInfo* pFileInfo2 = *it2;
|
||||
if (!strcmp(pFileInfo1->GetFilename(), pFileInfo2->GetFilename()) &&
|
||||
strcmp(pFileInfo1->GetSubject(), pFileInfo2->GetSubject()))
|
||||
{
|
||||
iDupe++;
|
||||
}
|
||||
}
|
||||
|
||||
// If more than two files have the same parsed filename but different subjects,
|
||||
// this means, that the parsing was not correct.
|
||||
// in this case we take subjects as filenames to prevent
|
||||
// false "duplicate files"-alarm.
|
||||
// It's Ok for just two files to have the same filename, this is
|
||||
// an often case by posting-errors to repost bad files
|
||||
if (iDupe > 2 || (iDupe == 2 && m_FileInfos.size() == 2))
|
||||
{
|
||||
for (FileInfos::iterator it2 = it; it2 != m_FileInfos.end(); it2++)
|
||||
{
|
||||
FileInfo* pFileInfo2 = *it2;
|
||||
pFileInfo2->SetFilename(pFileInfo2->GetSubject());
|
||||
pFileInfo2->MakeValidFilename();
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->LoadArticles(pFileInfo2);
|
||||
g_pDiskState->SaveFile(pFileInfo2);
|
||||
pFileInfo2->ClearArticles();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize, bool bFromBuffer)
|
||||
{
|
||||
CoInitialize(NULL);
|
||||
|
||||
HRESULT hr;
|
||||
|
||||
MSXML::IXMLDOMDocumentPtr doc;
|
||||
hr = doc.CreateInstance(MSXML::CLSID_DOMDocument);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Load the XML document file...
|
||||
doc->put_resolveExternals(VARIANT_FALSE);
|
||||
doc->put_validateOnParse(VARIANT_FALSE);
|
||||
doc->put_async(VARIANT_FALSE);
|
||||
VARIANT_BOOL success;
|
||||
if (bFromBuffer)
|
||||
{
|
||||
success = doc->loadXML(szBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
// filename needs to be properly encoded
|
||||
char* szURL = (char*)malloc(strlen(szFileName)*3 + 1);
|
||||
EncodeURL(szFileName, szURL);
|
||||
debug("url=\"%s\"", szURL);
|
||||
_variant_t v(szURL);
|
||||
free(szURL);
|
||||
success = doc->load(v);
|
||||
}
|
||||
if (success == VARIANT_FALSE)
|
||||
{
|
||||
_bstr_t r(doc->GetparseError()->reason);
|
||||
const char* szErrMsg = r;
|
||||
error("Error parsing nzb-file: %s", szErrMsg);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NZBFile* pFile = new NZBFile(szFileName);
|
||||
if (pFile->ParseNZB(doc))
|
||||
{
|
||||
pFile->CheckFilenames();
|
||||
}
|
||||
else
|
||||
{
|
||||
delete pFile;
|
||||
pFile = NULL;
|
||||
}
|
||||
|
||||
return pFile;
|
||||
}
|
||||
|
||||
void NZBFile::EncodeURL(const char* szFilename, char* szURL)
|
||||
{
|
||||
while (char ch = *szFilename++)
|
||||
{
|
||||
if (('0' <= ch && ch <= '9') ||
|
||||
('a' <= ch && ch <= 'z') ||
|
||||
('A' <= ch && ch <= 'Z') )
|
||||
{
|
||||
*szURL++ = ch;
|
||||
}
|
||||
else
|
||||
{
|
||||
*szURL++ = '%';
|
||||
int a = ch >> 4;
|
||||
*szURL++ = a > 9 ? a - 10 + 'a' : a + '0';
|
||||
a = ch & 0xF;
|
||||
*szURL++ = a > 9 ? a - 10 + 'a' : a + '0';
|
||||
}
|
||||
}
|
||||
*szURL = NULL;
|
||||
}
|
||||
|
||||
bool NZBFile::ParseNZB(IUnknown* nzb)
|
||||
{
|
||||
MSXML::IXMLDOMDocumentPtr doc = nzb;
|
||||
MSXML::IXMLDOMNodePtr root = doc->documentElement;
|
||||
|
||||
MSXML::IXMLDOMNodeListPtr fileList = root->selectNodes("/nzb/file");
|
||||
for (int i = 0; i < fileList->Getlength(); i++)
|
||||
{
|
||||
MSXML::IXMLDOMNodePtr node = fileList->Getitem(i);
|
||||
MSXML::IXMLDOMNodePtr attribute = node->Getattributes()->getNamedItem("subject");
|
||||
if (!attribute) return false;
|
||||
_bstr_t subject(attribute->Gettext());
|
||||
FileInfo* pFileInfo = new FileInfo();
|
||||
pFileInfo->SetNZBFilename(m_szFileName);
|
||||
pFileInfo->SetSubject(subject);
|
||||
|
||||
MSXML::IXMLDOMNodeListPtr groupList = node->selectNodes("groups/group");
|
||||
for (int g = 0; g < groupList->Getlength(); g++)
|
||||
{
|
||||
MSXML::IXMLDOMNodePtr node = groupList->Getitem(g);
|
||||
_bstr_t group = node->Gettext();
|
||||
pFileInfo->GetGroups()->push_back(strdup((const char*)group));
|
||||
}
|
||||
|
||||
MSXML::IXMLDOMNodeListPtr segmentList = node->selectNodes("segments/segment");
|
||||
for (int g = 0; g < segmentList->Getlength(); g++)
|
||||
{
|
||||
MSXML::IXMLDOMNodePtr node = segmentList->Getitem(g);
|
||||
_bstr_t id = node->Gettext();
|
||||
char szId[2048];
|
||||
snprintf(szId, 2048, "<%s>", (const char*)id);
|
||||
|
||||
MSXML::IXMLDOMNodePtr attribute = node->Getattributes()->getNamedItem("number");
|
||||
if (!attribute) return false;
|
||||
_bstr_t number(attribute->Gettext());
|
||||
|
||||
attribute = node->Getattributes()->getNamedItem("bytes");
|
||||
if (!attribute) return false;
|
||||
_bstr_t bytes(attribute->Gettext());
|
||||
|
||||
int partNumber = atoi(number);
|
||||
int lsize = atoi(bytes);
|
||||
|
||||
ArticleInfo* pArticle = new ArticleInfo();
|
||||
pArticle->SetPartNumber(partNumber);
|
||||
pArticle->SetMessageID(szId);
|
||||
pArticle->SetSize(lsize);
|
||||
AddArticle(pFileInfo, pArticle);
|
||||
|
||||
if (lsize > 0)
|
||||
{
|
||||
pFileInfo->SetSize(pFileInfo->GetSize() + lsize);
|
||||
}
|
||||
}
|
||||
|
||||
AddFileInfo(pFileInfo);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize, bool bFromBuffer)
|
||||
{
|
||||
xmlTextReaderPtr doc;
|
||||
if (bFromBuffer)
|
||||
{
|
||||
doc = xmlReaderForMemory(szBuffer, iSize-1, "", NULL, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
doc = xmlReaderForFile(szFileName, NULL, 0);
|
||||
}
|
||||
if (!doc)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NZBFile* pFile = new NZBFile(szFileName);
|
||||
if (pFile->ParseNZB(doc))
|
||||
{
|
||||
pFile->CheckFilenames();
|
||||
}
|
||||
else
|
||||
{
|
||||
delete pFile;
|
||||
pFile = NULL;
|
||||
}
|
||||
|
||||
xmlFreeTextReader(doc);
|
||||
|
||||
return pFile;
|
||||
}
|
||||
|
||||
bool NZBFile::ParseNZB(void* nzb)
|
||||
{
|
||||
FileInfo* pFileInfo = NULL;
|
||||
xmlTextReaderPtr node = (xmlTextReaderPtr)nzb;
|
||||
// walk through whole doc and search for segments-tags
|
||||
int ret = xmlTextReaderRead(node);
|
||||
while (ret == 1)
|
||||
{
|
||||
if (node)
|
||||
{
|
||||
xmlChar *name, *value;
|
||||
|
||||
name = xmlTextReaderName(node);
|
||||
if (name == NULL)
|
||||
{
|
||||
name = xmlStrdup(BAD_CAST "--");
|
||||
}
|
||||
value = xmlTextReaderValue(node);
|
||||
|
||||
if (xmlTextReaderNodeType(node) == 1)
|
||||
{
|
||||
if (!strcmp("file", (char*)name))
|
||||
{
|
||||
pFileInfo = new FileInfo();
|
||||
pFileInfo->SetNZBFilename(m_szFileName);
|
||||
|
||||
while (xmlTextReaderMoveToNextAttribute(node))
|
||||
{
|
||||
xmlFree(name);
|
||||
name = xmlTextReaderName(node);
|
||||
if (!strcmp("subject",(char*)name))
|
||||
{
|
||||
xmlFree(value);
|
||||
value = xmlTextReaderValue(node);
|
||||
pFileInfo->SetSubject((char*)value);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!strcmp("segment",(char*)name))
|
||||
{
|
||||
long long lsize = -1;
|
||||
int partNumber = -1;
|
||||
|
||||
while (xmlTextReaderMoveToNextAttribute(node))
|
||||
{
|
||||
xmlFree(name);
|
||||
name = xmlTextReaderName(node);
|
||||
xmlFree(value);
|
||||
value = xmlTextReaderValue(node);
|
||||
if (!strcmp("bytes",(char*)name))
|
||||
{
|
||||
lsize = atol((char*)value);
|
||||
}
|
||||
if (!strcmp("number",(char*)name))
|
||||
{
|
||||
partNumber = atol((char*)value);
|
||||
}
|
||||
}
|
||||
if (lsize > 0)
|
||||
{
|
||||
pFileInfo->SetSize(pFileInfo->GetSize() + lsize);
|
||||
}
|
||||
|
||||
/* Get the #text part */
|
||||
ret = xmlTextReaderRead(node);
|
||||
|
||||
if (partNumber > 0)
|
||||
{
|
||||
// new segment, add it!
|
||||
xmlFree(value);
|
||||
value = xmlTextReaderValue(node);
|
||||
char tmp[2048];
|
||||
snprintf(tmp, 2048, "<%s>", (char*)value);
|
||||
ArticleInfo* pArticle = new ArticleInfo();
|
||||
pArticle->SetPartNumber(partNumber);
|
||||
pArticle->SetMessageID(tmp);
|
||||
pArticle->SetSize(lsize);
|
||||
AddArticle(pFileInfo, pArticle);
|
||||
}
|
||||
}
|
||||
else if (!strcmp("group",(char*)name))
|
||||
{
|
||||
ret = xmlTextReaderRead(node);
|
||||
xmlFree(value);
|
||||
value = xmlTextReaderValue(node);
|
||||
pFileInfo->GetGroups()->push_back(strdup((char*)value));
|
||||
}
|
||||
}
|
||||
|
||||
if (xmlTextReaderNodeType(node) == 15)
|
||||
{
|
||||
/* Close the file element, add the new file to file-list */
|
||||
if (!strcmp("file",(char*)name))
|
||||
{
|
||||
AddFileInfo(pFileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
xmlFree(name);
|
||||
xmlFree(value);
|
||||
}
|
||||
ret = xmlTextReaderRead(node);
|
||||
}
|
||||
if (ret != 0)
|
||||
{
|
||||
error("Failed to parse nzb-file\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
68
NZBFile.h
68
NZBFile.h
@@ -1,68 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef NZBFILE_H
|
||||
#define NZBFILE_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
class NZBFile
|
||||
{
|
||||
public:
|
||||
typedef std::vector<FileInfo*> FileInfos;
|
||||
|
||||
private:
|
||||
FileInfos m_FileInfos;
|
||||
char* m_szFileName;
|
||||
|
||||
NZBFile(const char* szFileName);
|
||||
void AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo);
|
||||
void AddFileInfo(FileInfo* pFileInfo);
|
||||
void ParseSubject(FileInfo* pFileInfo);
|
||||
void BuildDestDirName(FileInfo* pFileInfo);
|
||||
void CheckFilenames();
|
||||
#ifdef WIN32
|
||||
bool ParseNZB(IUnknown* nzb);
|
||||
static void EncodeURL(const char* szFilename, char* szURL);
|
||||
#else
|
||||
bool ParseNZB(void* nzb);
|
||||
#endif
|
||||
static NZBFile* Create(const char* szFileName, const char* szBuffer, int iSize, bool bFromBuffer);
|
||||
|
||||
public:
|
||||
virtual ~NZBFile();
|
||||
static NZBFile* CreateFromBuffer(const char* szFileName, const char* szBuffer, int iSize);
|
||||
static NZBFile* CreateFromFile(const char* szFileName);
|
||||
const char* GetFileName() const { return m_szFileName; }
|
||||
FileInfos* GetFileInfos() { return &m_FileInfos; }
|
||||
void DetachFileInfos();
|
||||
|
||||
void LogDebugInfo();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,65 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "NewsServer.h"
|
||||
#include "Log.h"
|
||||
|
||||
NewsServer::NewsServer(const char* host, int port, const char* user, const char* pass, int maxConnections, int level) : NetAddress(host, port)
|
||||
{
|
||||
m_szUser = NULL;
|
||||
m_szPassword = NULL;
|
||||
m_iLevel = level;
|
||||
m_iMaxConnections = maxConnections;
|
||||
|
||||
if (pass)
|
||||
{
|
||||
m_szPassword = strdup(pass);
|
||||
}
|
||||
if (user)
|
||||
{
|
||||
m_szUser = strdup(user);
|
||||
}
|
||||
}
|
||||
|
||||
NewsServer::~NewsServer()
|
||||
{
|
||||
free(m_szUser);
|
||||
m_szUser = NULL;
|
||||
free(m_szPassword);
|
||||
m_szPassword = NULL;
|
||||
}
|
||||
49
NewsServer.h
49
NewsServer.h
@@ -1,49 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef NEWSSERVER_H
|
||||
#define NEWSSERVER_H
|
||||
|
||||
#include "NetAddress.h"
|
||||
|
||||
class NewsServer : public NetAddress
|
||||
{
|
||||
private:
|
||||
char* m_szUser;
|
||||
char* m_szPassword;
|
||||
int m_iMaxConnections;
|
||||
int m_iLevel;
|
||||
|
||||
public:
|
||||
NewsServer(const char* host, int port, const char* user, const char* pass, int maxConnections, int level);
|
||||
virtual ~NewsServer();
|
||||
const char* GetUser() { return m_szUser; }
|
||||
const char* GetPassword() { return m_szPassword; }
|
||||
int GetMaxConnections() { return m_iMaxConnections; }
|
||||
int GetLevel() { return m_iLevel; }
|
||||
};
|
||||
|
||||
#endif
|
||||
1210
Options.cpp
1210
Options.cpp
File diff suppressed because it is too large
Load Diff
249
Options.h
249
Options.h
@@ -1,249 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef OPTIONS_H
|
||||
#define OPTIONS_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
class Options
|
||||
{
|
||||
public:
|
||||
enum EClientOperation
|
||||
{
|
||||
opClientNoOperation,
|
||||
opClientRequestDownload,
|
||||
opClientRequestList,
|
||||
opClientRequestPause,
|
||||
opClientRequestUnpause,
|
||||
opClientRequestSetRate,
|
||||
opClientRequestDumpDebug,
|
||||
opClientRequestEditQueue,
|
||||
opClientRequestLog,
|
||||
opClientRequestShutdown,
|
||||
opClientRequestVersion
|
||||
};
|
||||
enum EMessageTarget
|
||||
{
|
||||
mtNone,
|
||||
mtScreen,
|
||||
mtLog,
|
||||
mtBoth
|
||||
};
|
||||
enum EDecoder
|
||||
{
|
||||
dcNone,
|
||||
dcUulib,
|
||||
dcYenc
|
||||
};
|
||||
enum EOutputMode
|
||||
{
|
||||
omLoggable,
|
||||
omColored,
|
||||
omNCurses
|
||||
};
|
||||
enum ELoadPars
|
||||
{
|
||||
plNone,
|
||||
plOne,
|
||||
plAll
|
||||
};
|
||||
|
||||
private:
|
||||
struct OptEntry
|
||||
{
|
||||
char* name;
|
||||
char* value;
|
||||
};
|
||||
|
||||
std::vector< struct OptEntry > optEntries;
|
||||
|
||||
bool m_bConfigInitialized;
|
||||
|
||||
// Options
|
||||
char* m_szConfigFilename;
|
||||
char* m_szDestDir;
|
||||
char* m_szTempDir;
|
||||
char* m_szQueueDir;
|
||||
char* m_szNzbDir;
|
||||
EMessageTarget m_eInfoTarget;
|
||||
EMessageTarget m_eWarningTarget;
|
||||
EMessageTarget m_eErrorTarget;
|
||||
EMessageTarget m_eDebugTarget;
|
||||
EDecoder m_eDecoder;
|
||||
bool m_bCreateBrokenLog;
|
||||
bool m_bResetLog;
|
||||
int m_iConnectionTimeout;
|
||||
int m_iTerminateTimeout;
|
||||
bool m_bAppendNZBDir;
|
||||
bool m_bContinuePartial;
|
||||
bool m_bRenameBroken;
|
||||
int m_iRetries;
|
||||
int m_iRetryInterval;
|
||||
bool m_bSaveQueue;
|
||||
bool m_bDupeCheck;
|
||||
char* m_szServerIP;
|
||||
char* m_szServerPassword;
|
||||
int m_szServerPort;
|
||||
char* m_szLockFile;
|
||||
char* m_szDaemonUserName;
|
||||
EOutputMode m_eOutputMode;
|
||||
bool m_bReloadQueue;
|
||||
int m_iLogBufferSize;
|
||||
bool m_bCreateLog;
|
||||
char* m_szLogFile;
|
||||
ELoadPars m_eLoadPars;
|
||||
bool m_bParCheck;
|
||||
bool m_bParRepair;
|
||||
char* m_szPostProcess;
|
||||
bool m_bStrictParName;
|
||||
bool m_bNoConfig;
|
||||
int m_iUMask;
|
||||
int m_iUpdateInterval;
|
||||
bool m_bCursesNZBName;
|
||||
bool m_bCursesTime;
|
||||
bool m_bCursesGroup;
|
||||
bool m_bCrcCheck;
|
||||
bool m_bRetryOnCrcError;
|
||||
int m_iThreadLimit;
|
||||
bool m_bDirectWrite;
|
||||
int m_iWriteBufferSize;
|
||||
int m_iNzbDirInterval;
|
||||
int m_iNzbDirFileAge;
|
||||
|
||||
// Parsed command-line parameters
|
||||
bool m_bServerMode;
|
||||
bool m_bDaemonMode;
|
||||
bool m_bRemoteClientMode;
|
||||
int m_iEditQueueAction;
|
||||
int m_iEditQueueOffset;
|
||||
int* m_pEditQueueIDList;
|
||||
int m_iEditQueueIDCount;
|
||||
char* m_szArgFilename;
|
||||
bool m_bPrintOptions;
|
||||
bool m_bAddTop;
|
||||
float m_fSetRate;
|
||||
int m_iLogLines;
|
||||
|
||||
// Current state
|
||||
bool m_bPause;
|
||||
float m_fDownloadRate;
|
||||
EClientOperation m_eClientOperation;
|
||||
|
||||
void InitDefault();
|
||||
void InitOptFile();
|
||||
void InitCommandLine(int argc, char* argv[]);
|
||||
void InitOptions();
|
||||
void InitFileArg(int argc, char* argv[]);
|
||||
void InitServers();
|
||||
void CheckOptions();
|
||||
void PrintUsage(char* com);
|
||||
void Dump();
|
||||
int ParseOptionValue(const char* OptName, int argc, const char* argn[], const int argv[]);
|
||||
const char* GetOption(const char* optname);
|
||||
void DelOption(const char* optname);
|
||||
void SetOption(const char* optname, const char* value);
|
||||
bool SetOptionString(const char* option);
|
||||
bool ValidateOptionName(const char* optname);
|
||||
void LoadConfig(const char* configfile);
|
||||
void CheckDir(char** dir, const char* szOptionName);
|
||||
void ParseFileIDList(int argc, char* argv[], int optind);
|
||||
|
||||
public:
|
||||
Options(int argc, char* argv[]);
|
||||
~Options();
|
||||
|
||||
// Options
|
||||
const char* GetDestDir() { return m_szDestDir; }
|
||||
const char* GetTempDir() { return m_szTempDir; }
|
||||
const char* GetQueueDir() { return m_szQueueDir; }
|
||||
const char* GetNzbDir() { return m_szNzbDir; }
|
||||
bool GetCreateBrokenLog() const { return m_bCreateBrokenLog; }
|
||||
bool GetResetLog() const { return m_bResetLog; }
|
||||
EMessageTarget GetInfoTarget() const { return m_eInfoTarget; }
|
||||
EMessageTarget GetWarningTarget() const { return m_eWarningTarget; }
|
||||
EMessageTarget GetErrorTarget() const { return m_eErrorTarget; }
|
||||
EMessageTarget GetDebugTarget() const { return m_eDebugTarget; }
|
||||
int GetConnectionTimeout() { return m_iConnectionTimeout; }
|
||||
int GetTerminateTimeout() { return m_iTerminateTimeout; }
|
||||
EDecoder GetDecoder() { return m_eDecoder; };
|
||||
bool GetAppendNZBDir() { return m_bAppendNZBDir; }
|
||||
bool GetContinuePartial() { return m_bContinuePartial; }
|
||||
bool GetRenameBroken() { return m_bRenameBroken; }
|
||||
int GetRetries() { return m_iRetries; }
|
||||
int GetRetryInterval() { return m_iRetryInterval; }
|
||||
bool GetSaveQueue() { return m_bSaveQueue; }
|
||||
bool GetDupeCheck() { return m_bDupeCheck; }
|
||||
char* GetServerIP() { return m_szServerIP; }
|
||||
char* GetServerPassword() { return m_szServerPassword; }
|
||||
int GetServerPort() { return m_szServerPort; }
|
||||
char* GetLockFile() { return m_szLockFile; }
|
||||
char* GetDaemonUserName() { return m_szDaemonUserName; }
|
||||
EOutputMode GetOutputMode() { return m_eOutputMode; }
|
||||
bool GetReloadQueue() { return m_bReloadQueue; }
|
||||
int GetLogBufferSize() { return m_iLogBufferSize; }
|
||||
bool GetCreateLog() { return m_bCreateLog; }
|
||||
char* GetLogFile() { return m_szLogFile; }
|
||||
ELoadPars GetLoadPars() { return m_eLoadPars; }
|
||||
bool GetParCheck() { return m_bParCheck; }
|
||||
bool GetParRepair() { return m_bParRepair; }
|
||||
const char* GetPostProcess() { return m_szPostProcess; }
|
||||
bool GetStrictParName() { return m_bStrictParName; }
|
||||
int GetUMask() { return m_iUMask; }
|
||||
int GetUpdateInterval() {return m_iUpdateInterval; }
|
||||
bool GetCursesNZBName() { return m_bCursesNZBName; }
|
||||
bool GetCursesTime() { return m_bCursesTime; }
|
||||
bool GetCursesGroup() { return m_bCursesGroup; }
|
||||
bool GetCrcCheck() { return m_bCrcCheck; }
|
||||
bool GetRetryOnCrcError() { return m_bRetryOnCrcError; }
|
||||
int GetThreadLimit() { return m_iThreadLimit; }
|
||||
bool GetDirectWrite() { return m_bDirectWrite; }
|
||||
int GetWriteBufferSize() { return m_iWriteBufferSize; }
|
||||
int GetNzbDirInterval() { return m_iNzbDirInterval; }
|
||||
int GetNzbDirFileAge() { return m_iNzbDirFileAge; }
|
||||
|
||||
// Parsed command-line parameters
|
||||
bool GetServerMode() { return m_bServerMode; }
|
||||
bool GetDaemonMode() { return m_bDaemonMode; }
|
||||
bool GetRemoteClientMode() { return m_bRemoteClientMode; }
|
||||
EClientOperation GetClientOperation() { return m_eClientOperation; }
|
||||
int GetEditQueueAction() { return m_iEditQueueAction; }
|
||||
int GetEditQueueOffset() { return m_iEditQueueOffset; }
|
||||
int* GetEditQueueIDList() { return m_pEditQueueIDList; }
|
||||
int GetEditQueueIDCount() { return m_iEditQueueIDCount; }
|
||||
const char* GetArgFilename() { return m_szArgFilename; }
|
||||
bool GetAddTop() { return m_bAddTop; }
|
||||
float GetSetRate() { return m_fSetRate; }
|
||||
int GetLogLines() { return m_iLogLines; }
|
||||
|
||||
// Current state
|
||||
void SetPause(bool bOnOff) { m_bPause = bOnOff; }
|
||||
bool GetPause() const { return m_bPause; }
|
||||
void SetDownloadRate(float fRate) { m_fDownloadRate = fRate; }
|
||||
float GetDownloadRate() const { return m_fDownloadRate; }
|
||||
};
|
||||
|
||||
#endif
|
||||
530
ParChecker.cpp
530
ParChecker.cpp
@@ -1,530 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#ifdef WIN32
|
||||
#include <par2cmdline.h>
|
||||
#include <par2repairer.h>
|
||||
#else
|
||||
#include <libpar2/par2cmdline.h>
|
||||
#include <libpar2/par2repairer.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "ParChecker.h"
|
||||
#include "Log.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "Options.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
extern Options* g_pOptions;
|
||||
|
||||
const char* Par2CmdLineErrStr[] = { "OK",
|
||||
"data files are damaged and there is enough recovery data available to repair them",
|
||||
"data files are damaged and there is insufficient recovery data available to be able to repair them",
|
||||
"there was something wrong with the command line arguments",
|
||||
"the PAR2 files did not contain sufficient information about the data files to be able to verify them",
|
||||
"repair completed but the data files still appear to be damaged",
|
||||
"an error occured when accessing files",
|
||||
"internal error occurred",
|
||||
"out of memory" };
|
||||
|
||||
class Repairer : public Par2Repairer
|
||||
{
|
||||
friend class ParChecker;
|
||||
};
|
||||
|
||||
ParChecker::ParChecker()
|
||||
{
|
||||
debug("Creating ParChecker");
|
||||
|
||||
m_eStatus = psUndefined;
|
||||
m_szParFilename = NULL;
|
||||
m_szNZBFilename = NULL;
|
||||
m_szInfoName = NULL;
|
||||
m_szErrMsg = NULL;
|
||||
m_QueuedParFiles.clear();
|
||||
}
|
||||
|
||||
ParChecker::~ParChecker()
|
||||
{
|
||||
debug("Destroying ParChecker");
|
||||
|
||||
if (m_szParFilename)
|
||||
{
|
||||
free(m_szParFilename);
|
||||
}
|
||||
if (m_szNZBFilename)
|
||||
{
|
||||
free(m_szNZBFilename);
|
||||
}
|
||||
if (m_szInfoName)
|
||||
{
|
||||
free(m_szInfoName);
|
||||
}
|
||||
if (m_szErrMsg)
|
||||
{
|
||||
free(m_szErrMsg);
|
||||
}
|
||||
|
||||
for (QueuedParFiles::iterator it = m_QueuedParFiles.begin(); it != m_QueuedParFiles.end() ;it++)
|
||||
{
|
||||
free(*it);
|
||||
}
|
||||
m_QueuedParFiles.clear();
|
||||
}
|
||||
|
||||
void ParChecker::SetParFilename(const char * szParFilename)
|
||||
{
|
||||
if (m_szParFilename)
|
||||
{
|
||||
free(m_szParFilename);
|
||||
}
|
||||
m_szParFilename = strdup(szParFilename);
|
||||
}
|
||||
|
||||
void ParChecker::SetInfoName(const char * szInfoName)
|
||||
{
|
||||
if (m_szInfoName)
|
||||
{
|
||||
free(m_szInfoName);
|
||||
}
|
||||
m_szInfoName = strdup(szInfoName);
|
||||
}
|
||||
|
||||
void ParChecker::SetNZBFilename(const char * szNZBFilename)
|
||||
{
|
||||
if (m_szNZBFilename)
|
||||
{
|
||||
free(m_szNZBFilename);
|
||||
}
|
||||
m_szNZBFilename = strdup(szNZBFilename);
|
||||
}
|
||||
|
||||
void ParChecker::SetStatus(EStatus eStatus)
|
||||
{
|
||||
m_eStatus = eStatus;
|
||||
Notify(NULL);
|
||||
}
|
||||
|
||||
void ParChecker::Run()
|
||||
{
|
||||
info("Verifying %s", m_szInfoName);
|
||||
SetStatus(psWorking);
|
||||
|
||||
debug("par: %s", m_szParFilename);
|
||||
CommandLine commandLine;
|
||||
const char* argv[] = { "par2", "r", "-q", "-q", m_szParFilename };
|
||||
if (!commandLine.Parse(5, (char**)argv))
|
||||
{
|
||||
error("Could not start par-check for %s. Par-file: %s", m_szInfoName, m_szParFilename);
|
||||
SetStatus(psFailed);
|
||||
return;
|
||||
}
|
||||
|
||||
Result res;
|
||||
Repairer* repairer = new Repairer();
|
||||
repairer->sig_filename.connect(sigc::mem_fun(*this, &ParChecker::signal_filename));
|
||||
|
||||
res = repairer->PreProcess(commandLine);
|
||||
debug("ParChecker: PreProcess-result=%i", res);
|
||||
|
||||
if (res != eSuccess || IsStopped())
|
||||
{
|
||||
error("Could not verify %s: ", m_szInfoName, IsStopped() ? "due stopping" : "par2-file could not be processed");
|
||||
SetStatus(psFailed);
|
||||
delete repairer;
|
||||
return;
|
||||
}
|
||||
|
||||
char BufReason[1024];
|
||||
BufReason[0] = '\0';
|
||||
if (m_szErrMsg)
|
||||
{
|
||||
free(m_szErrMsg);
|
||||
}
|
||||
m_szErrMsg = NULL;
|
||||
|
||||
m_bRepairNotNeeded = false;
|
||||
m_bRepairing = false;
|
||||
res = repairer->Process(commandLine, false);
|
||||
debug("ParChecker: Process-result=%i", res);
|
||||
|
||||
while (!IsStopped() && res == eRepairNotPossible)
|
||||
{
|
||||
int missingblockcount = repairer->missingblockcount - repairer->recoverypacketmap.size();
|
||||
info("Need more %i par-block(s) for %s", missingblockcount, m_szInfoName);
|
||||
|
||||
m_mutexQueuedParFiles.Lock();
|
||||
bool hasMorePars = !m_QueuedParFiles.empty();
|
||||
m_mutexQueuedParFiles.Unlock();
|
||||
|
||||
if (!hasMorePars)
|
||||
{
|
||||
int iBlockFound = 0;
|
||||
bool requested = RequestMorePars(missingblockcount, &iBlockFound);
|
||||
|
||||
m_mutexQueuedParFiles.Lock();
|
||||
hasMorePars = !m_QueuedParFiles.empty();
|
||||
m_mutexQueuedParFiles.Unlock();
|
||||
|
||||
if (!requested && !hasMorePars)
|
||||
{
|
||||
snprintf(BufReason, 1024, "not enough par-blocks, %i block(s) needed, but %i block(s) available", missingblockcount, iBlockFound);
|
||||
BufReason[1024-1] = '\0';
|
||||
m_szErrMsg = strdup(BufReason);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!hasMorePars)
|
||||
{
|
||||
m_semNeedMoreFiles.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
if (IsStopped())
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
LoadMorePars(repairer);
|
||||
repairer->UpdateVerificationResults();
|
||||
|
||||
m_bRepairing = false;
|
||||
res = repairer->Process(commandLine, false);
|
||||
debug("ParChecker: Process-result=%i", res);
|
||||
}
|
||||
|
||||
if (IsStopped())
|
||||
{
|
||||
SetStatus(psFailed);
|
||||
delete repairer;
|
||||
return;
|
||||
}
|
||||
|
||||
if (res == eSuccess)
|
||||
{
|
||||
info("Repair not needed for %s", m_szInfoName);
|
||||
m_bRepairNotNeeded = true;
|
||||
}
|
||||
else if (res == eRepairPossible)
|
||||
{
|
||||
if (g_pOptions->GetParRepair())
|
||||
{
|
||||
info("Repairing %s", m_szInfoName);
|
||||
m_bRepairing = true;
|
||||
res = repairer->Process(commandLine, true);
|
||||
debug("ParChecker: Process-result=%i", res);
|
||||
if (res == eSuccess)
|
||||
{
|
||||
info("Successfully repaired %s", m_szInfoName);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
info("Repair possible for %s", m_szInfoName);
|
||||
res = eSuccess;
|
||||
}
|
||||
}
|
||||
|
||||
if (res == eSuccess)
|
||||
{
|
||||
SetStatus(psFinished);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!m_szErrMsg && (int)res >= 0 && (int)res <= 8)
|
||||
{
|
||||
m_szErrMsg = strdup(Par2CmdLineErrStr[res]);
|
||||
}
|
||||
error("Repair failed for %s: %s", m_szInfoName, m_szErrMsg ? m_szErrMsg : "");
|
||||
SetStatus(psFailed);
|
||||
}
|
||||
|
||||
delete repairer;
|
||||
}
|
||||
|
||||
bool ParChecker::ParseParFilename(const char * szParFilename, int* iBaseNameLen, int* iBlocks)
|
||||
{
|
||||
char szFilename[1024];
|
||||
strncpy(szFilename, szParFilename, 1024);
|
||||
szFilename[1024-1] = '\0';
|
||||
for (char* p = szFilename; *p; p++) *p = tolower(*p); // convert string to lowercase
|
||||
|
||||
int iLen = strlen(szFilename);
|
||||
if (iLen < 6)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// find last occurence of ".par2" and trim filename after it
|
||||
char* szEnd = szFilename;
|
||||
while (char* p = strstr(szEnd, ".par2")) szEnd = p + 5;
|
||||
*szEnd = '\0';
|
||||
iLen = strlen(szFilename);
|
||||
|
||||
if (strcasecmp(szFilename + iLen - 5, ".par2"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
*(szFilename + iLen - 5) = '\0';
|
||||
|
||||
int blockcnt = 0;
|
||||
char* p = strrchr(szFilename, '.');
|
||||
if (p && !strncasecmp(p, ".vol", 4))
|
||||
{
|
||||
char* b = strchr(p, '+');
|
||||
if (!b)
|
||||
{
|
||||
b = strchr(p, '-');
|
||||
}
|
||||
if (b)
|
||||
{
|
||||
blockcnt = atoi(b+1);
|
||||
*p = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
if (iBaseNameLen)
|
||||
{
|
||||
*iBaseNameLen = strlen(szFilename);
|
||||
}
|
||||
if (iBlocks)
|
||||
{
|
||||
*iBlocks = blockcnt;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpause par2-files
|
||||
* returns true, if the files with required number of blocks were unpaused,
|
||||
* or false if there are no more files in queue for this collection or not enough blocks
|
||||
*/
|
||||
bool ParChecker::RequestMorePars(int iBlockNeeded, int* pBlockFound)
|
||||
{
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
|
||||
Blocks blocks;
|
||||
blocks.clear();
|
||||
int iBlockFound = 0;
|
||||
|
||||
FindPars(pDownloadQueue, &blocks, true, &iBlockFound);
|
||||
if (iBlockFound == 0 && !g_pOptions->GetStrictParName())
|
||||
{
|
||||
FindPars(pDownloadQueue, &blocks, false, &iBlockFound);
|
||||
}
|
||||
|
||||
if (iBlockFound >= iBlockNeeded)
|
||||
{
|
||||
char szNZBNiceName[1024];
|
||||
FileInfo::MakeNiceNZBName(m_szNZBFilename, szNZBNiceName, 1024);
|
||||
|
||||
// 1. first unpause all files with par-blocks less or equal iBlockNeeded
|
||||
// starting from the file with max block count.
|
||||
// if par-collection was built exponentially and all par-files present,
|
||||
// this step selects par-files with exact number of blocks we need.
|
||||
while (iBlockNeeded > 0)
|
||||
{
|
||||
BlockInfo* pBestBlockInfo = NULL;
|
||||
for (Blocks::iterator it = blocks.begin(); it != blocks.end(); it++)
|
||||
{
|
||||
BlockInfo* pBlockInfo = *it;
|
||||
if (pBlockInfo->m_iBlockCount <= iBlockNeeded &&
|
||||
(!pBestBlockInfo || pBestBlockInfo->m_iBlockCount < pBlockInfo->m_iBlockCount))
|
||||
{
|
||||
pBestBlockInfo = pBlockInfo;
|
||||
}
|
||||
}
|
||||
if (pBestBlockInfo)
|
||||
{
|
||||
if (pBestBlockInfo->m_pFileInfo->GetPaused())
|
||||
{
|
||||
info("Unpausing %s%c%s for par-recovery", szNZBNiceName, (int)PATH_SEPARATOR, pBestBlockInfo->m_pFileInfo->GetFilename());
|
||||
pBestBlockInfo->m_pFileInfo->SetPaused(false);
|
||||
}
|
||||
iBlockNeeded -= pBestBlockInfo->m_iBlockCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. then unpause other files
|
||||
// this step only needed if the par-collection was built not exponentially
|
||||
// or not all par-files present (or some of them were corrupted)
|
||||
// this step is not optimal, but we hope, that the first step will work good
|
||||
// in most cases and we will not need the second step often
|
||||
while (iBlockNeeded > 0)
|
||||
{
|
||||
BlockInfo* pBlockInfo = blocks.front();
|
||||
if (pBlockInfo->m_pFileInfo->GetPaused())
|
||||
{
|
||||
info("Unpausing %s%c%s for par-recovery", szNZBNiceName, (int)PATH_SEPARATOR, pBlockInfo->m_pFileInfo->GetFilename());
|
||||
pBlockInfo->m_pFileInfo->SetPaused(false);
|
||||
}
|
||||
iBlockNeeded -= pBlockInfo->m_iBlockCount;
|
||||
}
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
|
||||
if (pBlockFound)
|
||||
{
|
||||
*pBlockFound = iBlockFound;
|
||||
}
|
||||
|
||||
for (Blocks::iterator it = blocks.begin(); it != blocks.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
blocks.clear();
|
||||
|
||||
return iBlockNeeded <= 0;
|
||||
}
|
||||
|
||||
void ParChecker::FindPars(DownloadQueue * pDownloadQueue, Blocks * pBlocks, bool bStrictParName, int* pBlockFound)
|
||||
{
|
||||
*pBlockFound = 0;
|
||||
|
||||
// extract base name from m_szParFilename (trim .par2-extension and possible .vol-part)
|
||||
char* szBaseParFilename = BaseFileName(m_szParFilename);
|
||||
char szMainBaseFilename[1024];
|
||||
int iMainBaseLen = 0;
|
||||
if (!ParseParFilename(szBaseParFilename, &iMainBaseLen, NULL))
|
||||
{
|
||||
// should not happen
|
||||
error("Internal error: could not parse filename %s", szBaseParFilename);
|
||||
return;
|
||||
}
|
||||
int maxlen = iMainBaseLen < 1024 ? iMainBaseLen : 1024 - 1;
|
||||
strncpy(szMainBaseFilename, szBaseParFilename, maxlen);
|
||||
szMainBaseFilename[maxlen] = '\0';
|
||||
for (char* p = szMainBaseFilename; *p; p++) *p = tolower(*p); // convert string to lowercase
|
||||
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
int iBlocks = 0;
|
||||
if (!strcmp(pFileInfo->GetNZBFilename(), m_szNZBFilename) &&
|
||||
ParseParFilename(pFileInfo->GetFilename(), NULL, &iBlocks) &&
|
||||
iBlocks > 0)
|
||||
{
|
||||
if (bStrictParName)
|
||||
{
|
||||
// the pFileInfo->GetFilename() may be not confirmed and may contain
|
||||
// additional texts if Subject could not be parsed correctly
|
||||
|
||||
char szLoFileName[1024];
|
||||
strncpy(szLoFileName, pFileInfo->GetFilename(), 1024);
|
||||
szLoFileName[1024-1] = '\0';
|
||||
for (char* p = szLoFileName; *p; p++) *p = tolower(*p); // convert string to lowercase
|
||||
|
||||
char szCandidateFileName[1024];
|
||||
snprintf(szCandidateFileName, 1024, "%s.par2", szMainBaseFilename);
|
||||
szCandidateFileName[1024-1] = '\0';
|
||||
if (!strstr(szLoFileName, szCandidateFileName))
|
||||
{
|
||||
snprintf(szCandidateFileName, 1024, "%s.vol", szMainBaseFilename);
|
||||
szCandidateFileName[1024-1] = '\0';
|
||||
if (!strstr(szLoFileName, szCandidateFileName))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if it is a par2-file with blocks and it was from the same NZB-request
|
||||
// and it belongs to the same file collection (same base name),
|
||||
// then OK, we can use it
|
||||
BlockInfo* pBlockInfo = new BlockInfo();
|
||||
pBlockInfo->m_pFileInfo = pFileInfo;
|
||||
pBlockInfo->m_iBlockCount = iBlocks;
|
||||
pBlocks->push_back(pBlockInfo);
|
||||
*pBlockFound += iBlocks;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParChecker::LoadMorePars(void* repairer)
|
||||
{
|
||||
m_mutexQueuedParFiles.Lock();
|
||||
QueuedParFiles moreFiles;
|
||||
moreFiles.assign(m_QueuedParFiles.begin(), m_QueuedParFiles.end());
|
||||
m_QueuedParFiles.clear();
|
||||
m_mutexQueuedParFiles.Unlock();
|
||||
|
||||
for (QueuedParFiles::iterator it = moreFiles.begin(); it != moreFiles.end() ;it++)
|
||||
{
|
||||
char* szParFilename = *it;
|
||||
bool loadedOK = ((Repairer*)repairer)->LoadPacketsFromFile(szParFilename);
|
||||
if (loadedOK)
|
||||
{
|
||||
info("File %s successfully loaded for par-check", BaseFileName(szParFilename), m_szInfoName);
|
||||
}
|
||||
else
|
||||
{
|
||||
info("Could not load file %s for par-check", BaseFileName(szParFilename), m_szInfoName);
|
||||
}
|
||||
free(szParFilename);
|
||||
}
|
||||
}
|
||||
|
||||
void ParChecker::AddParFile(const char * szParFilename)
|
||||
{
|
||||
m_mutexQueuedParFiles.Lock();
|
||||
m_QueuedParFiles.push_back(strdup(szParFilename));
|
||||
m_semNeedMoreFiles.Post();
|
||||
m_mutexQueuedParFiles.Unlock();
|
||||
}
|
||||
|
||||
void ParChecker::QueueChanged()
|
||||
{
|
||||
m_mutexQueuedParFiles.Lock();
|
||||
m_semNeedMoreFiles.Post();
|
||||
m_mutexQueuedParFiles.Unlock();
|
||||
}
|
||||
|
||||
void ParChecker::signal_filename(std::string str)
|
||||
{
|
||||
info("%s file %s", m_bRepairing ? "Repairing" : "Verifying", str.c_str());
|
||||
}
|
||||
|
||||
#endif
|
||||
94
ParChecker.h
94
ParChecker.h
@@ -1,94 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef PARCHECKER_H
|
||||
#define PARCHECKER_H
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include "Thread.h"
|
||||
#include "Observer.h"
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
class ParChecker : public Thread, public Subject
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
psUndefined,
|
||||
psWorking,
|
||||
psFailed,
|
||||
psFinished
|
||||
};
|
||||
struct BlockInfo
|
||||
{
|
||||
FileInfo* m_pFileInfo;
|
||||
int m_iBlockCount;
|
||||
};
|
||||
|
||||
typedef std::deque<char*> QueuedParFiles;
|
||||
typedef std::deque<BlockInfo*> Blocks;
|
||||
|
||||
private:
|
||||
char* m_szInfoName;
|
||||
char* m_szNZBFilename;
|
||||
char* m_szParFilename;
|
||||
EStatus m_eStatus;
|
||||
char* m_szErrMsg;
|
||||
bool m_bRepairNotNeeded;
|
||||
QueuedParFiles m_QueuedParFiles;
|
||||
Mutex m_mutexQueuedParFiles;
|
||||
Semaphore m_semNeedMoreFiles;
|
||||
bool m_bRepairing;
|
||||
|
||||
bool RequestMorePars(int iBlockNeeded, int* pBlockFound);
|
||||
void FindPars(DownloadQueue* pDownloadQueue, Blocks* pBlocks, bool bStrictParName, int* pBlockFound);
|
||||
void LoadMorePars(void* repairer);
|
||||
void signal_filename(std::string str);
|
||||
|
||||
public:
|
||||
ParChecker();
|
||||
virtual ~ParChecker();
|
||||
virtual void Run();
|
||||
const char* GetParFilename() { return m_szParFilename; }
|
||||
void SetParFilename(const char* szParFilename);
|
||||
const char* GetNZBFilename() { return m_szNZBFilename; }
|
||||
void SetNZBFilename(const char* szNZBFilename);
|
||||
const char* GetInfoName() { return m_szInfoName; }
|
||||
void SetInfoName(const char* szInfoName);
|
||||
void SetStatus(EStatus eStatus);
|
||||
EStatus GetStatus() { return m_eStatus; }
|
||||
const char* GetErrMsg() { return m_szErrMsg; }
|
||||
bool GetRepairNotNeeded() { return m_bRepairNotNeeded; }
|
||||
void AddParFile(const char* szParFilename);
|
||||
void QueueChanged();
|
||||
static bool ParseParFilename(const char* szParFilename, int* iBaseNameLen, int* iBlocks);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
@@ -1,601 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "PrePostProcessor.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
extern Options* g_pOptions;
|
||||
|
||||
static const int PARSTATUS_NOT_CHECKED = 0;
|
||||
static const int PARSTATUS_FAILED = 1;
|
||||
static const int PARSTATUS_REPAIRED = 2;
|
||||
static const int PARSTATUS_REPAIR_POSSIBLE = 3;
|
||||
|
||||
PrePostProcessor::ParJob::ParJob(const char * szNZBFilename, const char * szParFilename, const char * szInfoName)
|
||||
{
|
||||
m_szNZBFilename = strdup(szNZBFilename);
|
||||
m_szParFilename = strdup(szParFilename);
|
||||
m_szInfoName = strdup(szInfoName);
|
||||
}
|
||||
|
||||
PrePostProcessor::ParJob::~ ParJob()
|
||||
{
|
||||
if (m_szNZBFilename)
|
||||
{
|
||||
free(m_szNZBFilename);
|
||||
}
|
||||
if (m_szParFilename)
|
||||
{
|
||||
free(m_szParFilename);
|
||||
}
|
||||
if (m_szInfoName)
|
||||
{
|
||||
free(m_szInfoName);
|
||||
}
|
||||
}
|
||||
|
||||
PrePostProcessor::PrePostProcessor()
|
||||
{
|
||||
debug("Creating PrePostProcessor");
|
||||
|
||||
m_bHasMoreJobs = false;
|
||||
|
||||
m_QueueCoordinatorObserver.owner = this;
|
||||
g_pQueueCoordinator->Attach(&m_QueueCoordinatorObserver);
|
||||
|
||||
m_ParQueue.clear();
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
m_ParCheckerObserver.owner = this;
|
||||
m_ParChecker.Attach(&m_ParCheckerObserver);
|
||||
#endif
|
||||
}
|
||||
|
||||
PrePostProcessor::~PrePostProcessor()
|
||||
{
|
||||
debug("Destroying PrePostProcessor");
|
||||
|
||||
for (ParQueue::iterator it = m_ParQueue.begin(); it != m_ParQueue.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
}
|
||||
|
||||
void PrePostProcessor::Run()
|
||||
{
|
||||
debug("Entering PrePostProcessor-loop");
|
||||
|
||||
int iNZBDirInterval = 0;
|
||||
#ifndef DISABLE_PARCHECK
|
||||
int iParQueueInterval = 0;
|
||||
#endif
|
||||
while (!IsStopped())
|
||||
{
|
||||
if (g_pOptions->GetNzbDir() && g_pOptions->GetNzbDirInterval() > 0 &&
|
||||
iNZBDirInterval == g_pOptions->GetNzbDirInterval() * 1000)
|
||||
{
|
||||
// check nzbdir every g_pOptions->GetNzbDirInterval() seconds
|
||||
CheckIncomingNZBs();
|
||||
iNZBDirInterval = 0;
|
||||
}
|
||||
iNZBDirInterval += 200;
|
||||
#ifndef DISABLE_PARCHECK
|
||||
if (iParQueueInterval == 1000 && g_pOptions->GetParCheck())
|
||||
{
|
||||
// check par-queue every 1 second
|
||||
CheckParQueue();
|
||||
iParQueueInterval = 0;
|
||||
}
|
||||
iParQueueInterval += 200;
|
||||
#endif
|
||||
usleep(200 * 1000);
|
||||
}
|
||||
|
||||
debug("Exiting PrePostProcessor-loop");
|
||||
}
|
||||
|
||||
void PrePostProcessor::Stop()
|
||||
{
|
||||
Thread::Stop();
|
||||
#ifndef DISABLE_PARCHECK
|
||||
m_mutexParChecker.Lock();
|
||||
if (m_ParChecker.IsRunning())
|
||||
{
|
||||
m_ParChecker.Stop();
|
||||
int iMSecWait = 5000;
|
||||
while (m_ParChecker.IsRunning() && iMSecWait > 0)
|
||||
{
|
||||
usleep(50 * 1000);
|
||||
iMSecWait -= 50;
|
||||
}
|
||||
if (m_ParChecker.IsRunning())
|
||||
{
|
||||
warn("Terminating par-check for %s", m_ParChecker.GetInfoName());
|
||||
m_ParChecker.Kill();
|
||||
}
|
||||
}
|
||||
|
||||
m_mutexParChecker.Unlock();
|
||||
#endif
|
||||
}
|
||||
|
||||
void PrePostProcessor::QueueCoordinatorUpdate(Subject * Caller, void * Aspect)
|
||||
{
|
||||
if (IsStopped())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QueueCoordinator::Aspect* pAspect = (QueueCoordinator::Aspect*)Aspect;
|
||||
if (pAspect->eAction == QueueCoordinator::eaNZBFileAdded &&
|
||||
g_pOptions->GetLoadPars() != Options::plAll)
|
||||
{
|
||||
PausePars(pAspect->pDownloadQueue, pAspect->szNZBFilename);
|
||||
}
|
||||
else if ((pAspect->eAction == QueueCoordinator::eaFileCompleted ||
|
||||
pAspect->eAction == QueueCoordinator::eaFileDeleted))
|
||||
{
|
||||
if (
|
||||
#ifndef DISABLE_PARCHECK
|
||||
!AddPar(pAspect->pFileInfo, pAspect->eAction == QueueCoordinator::eaFileDeleted) &&
|
||||
#endif
|
||||
WasLastInCollection(pAspect->pDownloadQueue, pAspect->pFileInfo, true))
|
||||
{
|
||||
char szNZBNiceName[1024];
|
||||
pAspect->pFileInfo->GetNiceNZBName(szNZBNiceName, 1024);
|
||||
if (pAspect->eAction == QueueCoordinator::eaFileCompleted)
|
||||
{
|
||||
info("Collection %s completely downloaded", szNZBNiceName);
|
||||
}
|
||||
else if (WasLastInCollection(pAspect->pDownloadQueue, pAspect->pFileInfo, false))
|
||||
{
|
||||
info("Collection %s deleted from queue", szNZBNiceName);
|
||||
}
|
||||
#ifndef DISABLE_PARCHECK
|
||||
if (g_pOptions->GetParCheck() &&
|
||||
pAspect->eAction == QueueCoordinator::eaFileCompleted)
|
||||
{
|
||||
CheckPars(pAspect->pDownloadQueue, pAspect->pFileInfo);
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
ExecPostScript(pAspect->pFileInfo->GetDestDir(), pAspect->pFileInfo->GetNZBFilename(), "", PARSTATUS_NOT_CHECKED);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PrePostProcessor::PausePars(DownloadQueue* pDownloadQueue, const char* szNZBFilename)
|
||||
{
|
||||
debug("PrePostProcessor: Pausing pars");
|
||||
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (!strcmp(pFileInfo->GetNZBFilename(), szNZBFilename))
|
||||
{
|
||||
g_pQueueCoordinator->GetQueueEditor()->LockedEditEntry(pDownloadQueue, pFileInfo->GetID(), false,
|
||||
(g_pOptions->GetLoadPars() == Options::plOne ||
|
||||
(g_pOptions->GetLoadPars() == Options::plNone && g_pOptions->GetParCheck()))
|
||||
? QueueEditor::eaGroupPauseExtraPars : QueueEditor::eaGroupPauseAllPars,
|
||||
0);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are files in directory for incoming nzb-files
|
||||
* and add them to download queue
|
||||
*/
|
||||
void PrePostProcessor::CheckIncomingNZBs()
|
||||
{
|
||||
DirBrowser dir(g_pOptions->GetNzbDir());
|
||||
while (const char* filename = dir.Next())
|
||||
{
|
||||
int len = strlen(filename);
|
||||
if (len > 4 && !strcasecmp(filename + len - 4, ".nzb"))
|
||||
{
|
||||
// file found, checking modification-time
|
||||
struct stat buffer;
|
||||
char fullfilename[1024];
|
||||
snprintf(fullfilename, 1024, "%s%c%s", g_pOptions->GetNzbDir(), (int)PATH_SEPARATOR, filename);
|
||||
fullfilename[1024-1] = '\0';
|
||||
if (!stat(fullfilename, &buffer) &&
|
||||
time(NULL) - buffer.st_mtime > g_pOptions->GetNzbDirFileAge() &&
|
||||
time(NULL) - buffer.st_ctime > g_pOptions->GetNzbDirFileAge())
|
||||
{
|
||||
// the file is at least g_pOptions->GetNzbDirFileAge() seconds old, we can process it
|
||||
info("Collection %s found", filename);
|
||||
char bakname[1024];
|
||||
if (g_pQueueCoordinator->AddFileToQueue(fullfilename))
|
||||
{
|
||||
info("Collection %s added to queue", filename);
|
||||
snprintf(bakname, 1024, "%s.queued", fullfilename);
|
||||
bakname[1024-1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not add collection %s to queue", filename);
|
||||
snprintf(bakname, 1024, "%s.error", fullfilename);
|
||||
bakname[1024-1] = '\0';
|
||||
}
|
||||
|
||||
char bakname2[1024];
|
||||
strcpy(bakname2, bakname);
|
||||
int i = 2;
|
||||
while (!stat(bakname2, &buffer))
|
||||
{
|
||||
snprintf(bakname2, 1024, "%s%i", bakname, i++);
|
||||
bakname2[1024-1] = '\0';
|
||||
}
|
||||
|
||||
rename(fullfilename, bakname2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the completed file was last (unpaused, if bIgnorePaused is "true") file in nzb-collection
|
||||
*/
|
||||
bool PrePostProcessor::WasLastInCollection(DownloadQueue* pDownloadQueue, FileInfo * pFileInfo, bool bIgnorePaused)
|
||||
{
|
||||
debug("File %s completed or deleted", pFileInfo->GetFilename());
|
||||
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo2 = *it;
|
||||
if (pFileInfo2 != pFileInfo && (!bIgnorePaused || !pFileInfo2->GetPaused()) &&
|
||||
!strcmp(pFileInfo2->GetNZBFilename(), pFileInfo->GetNZBFilename()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
PrePostProcessor::ParQueue* PrePostProcessor::LockParQueue()
|
||||
{
|
||||
m_mutexParChecker.Lock();
|
||||
return &m_ParQueue;
|
||||
}
|
||||
|
||||
void PrePostProcessor::UnlockParQueue()
|
||||
{
|
||||
m_mutexParChecker.Unlock();
|
||||
}
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
|
||||
void PrePostProcessor::CheckPars(DownloadQueue * pDownloadQueue, FileInfo * pFileInfo)
|
||||
{
|
||||
char szNZBNiceName[1024];
|
||||
pFileInfo->GetNiceNZBName(szNZBNiceName, 1024);
|
||||
|
||||
m_mutexParChecker.Lock();
|
||||
|
||||
FileList fileList;
|
||||
if (FindMainPars(pFileInfo->GetDestDir(), &fileList))
|
||||
{
|
||||
for (FileList::iterator it = fileList.begin(); it != fileList.end(); it++)
|
||||
{
|
||||
char* szParFilename = *it;
|
||||
debug("Found par: %s", szParFilename);
|
||||
|
||||
char szFullFilename[1024];
|
||||
snprintf(szFullFilename, 1024, "%s%c%s", pFileInfo->GetDestDir(), (int)PATH_SEPARATOR, szParFilename);
|
||||
szFullFilename[1024-1] = '\0';
|
||||
|
||||
char szInfoName[1024];
|
||||
int iBaseLen = 0;
|
||||
ParChecker::ParseParFilename(szParFilename, &iBaseLen, NULL);
|
||||
int maxlen = iBaseLen < 1024 ? iBaseLen : 1024 - 1;
|
||||
strncpy(szInfoName, szParFilename, maxlen);
|
||||
szInfoName[maxlen] = '\0';
|
||||
|
||||
char szParInfoName[1024];
|
||||
snprintf(szParInfoName, 1024, "%s%c%s", szNZBNiceName, (int)PATH_SEPARATOR, szInfoName);
|
||||
szParInfoName[1024-1] = '\0';
|
||||
|
||||
info("Queueing %s%c%s for par-check", szNZBNiceName, (int)PATH_SEPARATOR, szInfoName);
|
||||
ParJob* pParJob = new ParJob(pFileInfo->GetNZBFilename(), szFullFilename, szParInfoName);
|
||||
m_ParQueue.push_back(pParJob);
|
||||
m_bHasMoreJobs = true;
|
||||
|
||||
free(szParFilename);
|
||||
}
|
||||
}
|
||||
|
||||
m_mutexParChecker.Unlock();
|
||||
}
|
||||
|
||||
bool PrePostProcessor::FindMainPars(const char * szPath, FileList * pFileList)
|
||||
{
|
||||
pFileList->clear();
|
||||
DirBrowser dir(szPath);
|
||||
while (const char* filename = dir.Next())
|
||||
{
|
||||
int iBaseLen = 0;
|
||||
if (ParChecker::ParseParFilename(filename, &iBaseLen, NULL))
|
||||
{
|
||||
// check if the base file already added to list
|
||||
bool exists = false;
|
||||
for (FileList::iterator it = pFileList->begin(); it != pFileList->end(); it++)
|
||||
{
|
||||
const char* filename2 = *it;
|
||||
exists = SameParCollection(filename, filename2);
|
||||
if (exists)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!exists)
|
||||
{
|
||||
pFileList->push_back(strdup(filename));
|
||||
}
|
||||
}
|
||||
}
|
||||
return !pFileList->empty();
|
||||
}
|
||||
|
||||
bool PrePostProcessor::AddPar(FileInfo * pFileInfo, bool bDeleted)
|
||||
{
|
||||
m_mutexParChecker.Lock();
|
||||
bool bSameCollection = m_ParChecker.IsRunning() &&
|
||||
!strcmp(pFileInfo->GetNZBFilename(), m_ParChecker.GetNZBFilename()) &&
|
||||
SameParCollection(pFileInfo->GetFilename(), BaseFileName(m_ParChecker.GetParFilename()));
|
||||
if (bSameCollection)
|
||||
{
|
||||
if (!bDeleted)
|
||||
{
|
||||
char szFullFilename[1024];
|
||||
snprintf(szFullFilename, 1024, "%s%c%s", pFileInfo->GetDestDir(), (int)PATH_SEPARATOR, pFileInfo->GetFilename());
|
||||
szFullFilename[1024-1] = '\0';
|
||||
m_ParChecker.AddParFile(szFullFilename);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_ParChecker.QueueChanged();
|
||||
}
|
||||
}
|
||||
m_mutexParChecker.Unlock();
|
||||
return bSameCollection;
|
||||
}
|
||||
|
||||
bool PrePostProcessor::SameParCollection(const char* szFilename1, const char* szFilename2)
|
||||
{
|
||||
int iBaseLen1 = 0, iBaseLen2 = 0;
|
||||
return ParChecker::ParseParFilename(szFilename1, &iBaseLen1, NULL) &&
|
||||
ParChecker::ParseParFilename(szFilename2, &iBaseLen2, NULL) &&
|
||||
iBaseLen1 == iBaseLen2 &&
|
||||
!strncasecmp(szFilename1, szFilename2, iBaseLen1);
|
||||
}
|
||||
|
||||
void PrePostProcessor::CheckParQueue()
|
||||
{
|
||||
m_mutexParChecker.Lock();
|
||||
|
||||
if (!m_ParChecker.IsRunning() && !m_ParQueue.empty())
|
||||
{
|
||||
ParJob* pParJob = m_ParQueue.front();
|
||||
|
||||
info("Checking pars for %s", pParJob->GetInfoName());
|
||||
m_ParChecker.SetNZBFilename(pParJob->GetNZBFilename());
|
||||
m_ParChecker.SetParFilename(pParJob->GetParFilename());
|
||||
m_ParChecker.SetInfoName(pParJob->GetInfoName());
|
||||
m_ParChecker.Start();
|
||||
}
|
||||
|
||||
m_mutexParChecker.Unlock();
|
||||
}
|
||||
|
||||
void PrePostProcessor::ParCheckerUpdate(Subject * Caller, void * Aspect)
|
||||
{
|
||||
if (m_ParChecker.GetStatus() == ParChecker::psFinished ||
|
||||
m_ParChecker.GetStatus() == ParChecker::psFailed)
|
||||
{
|
||||
char szPath[1024];
|
||||
strncpy(szPath, m_ParChecker.GetParFilename(), 1024);
|
||||
szPath[1024-1] = '\0';
|
||||
if (char* p = strrchr(szPath, PATH_SEPARATOR)) *p = '\0';
|
||||
|
||||
if (g_pOptions->GetCreateBrokenLog())
|
||||
{
|
||||
char szBrokenLogName[1024];
|
||||
snprintf(szBrokenLogName, 1024, "%s%c_brokenlog.txt", szPath, (int)PATH_SEPARATOR);
|
||||
szBrokenLogName[1024-1] = '\0';
|
||||
|
||||
bool bExists = false;
|
||||
if (m_ParChecker.GetRepairNotNeeded())
|
||||
{
|
||||
struct stat buffer;
|
||||
bExists = !stat(szBrokenLogName, &buffer);
|
||||
}
|
||||
if (!m_ParChecker.GetRepairNotNeeded() || bExists)
|
||||
{
|
||||
FILE* file = fopen(szBrokenLogName, "a");
|
||||
if (m_ParChecker.GetStatus() == ParChecker::psFailed)
|
||||
{
|
||||
fprintf(file, "Repair failed for %s: %s\n", m_ParChecker.GetInfoName(), m_ParChecker.GetErrMsg() ? m_ParChecker.GetErrMsg() : "");
|
||||
}
|
||||
else if (m_ParChecker.GetRepairNotNeeded())
|
||||
{
|
||||
fprintf(file, "Repair not needed for %s\n", m_ParChecker.GetInfoName());
|
||||
}
|
||||
else
|
||||
{
|
||||
if (g_pOptions->GetParRepair())
|
||||
{
|
||||
fprintf(file, "Successfully repaired %s\n", m_ParChecker.GetInfoName());
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(file, "Repair possible for %s\n", m_ParChecker.GetInfoName());
|
||||
}
|
||||
}
|
||||
fclose(file);
|
||||
}
|
||||
}
|
||||
|
||||
int iParStatus = 0;
|
||||
if (m_ParChecker.GetStatus() == ParChecker::psFailed)
|
||||
{
|
||||
iParStatus = PARSTATUS_FAILED;
|
||||
}
|
||||
else if (g_pOptions->GetParRepair() || m_ParChecker.GetRepairNotNeeded())
|
||||
{
|
||||
iParStatus = PARSTATUS_REPAIRED;
|
||||
}
|
||||
else
|
||||
{
|
||||
iParStatus = PARSTATUS_REPAIR_POSSIBLE;
|
||||
}
|
||||
|
||||
ExecPostScript(szPath, m_ParChecker.GetNZBFilename(), m_ParChecker.GetParFilename(), iParStatus);
|
||||
|
||||
m_mutexParChecker.Lock();
|
||||
ParJob* pParJob = m_ParQueue.front();
|
||||
m_ParQueue.pop_front();
|
||||
delete pParJob;
|
||||
m_bHasMoreJobs = !m_ParQueue.empty();
|
||||
m_mutexParChecker.Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void PrePostProcessor::ExecPostScript(const char * szPath, const char * szNZBFilename, const char * szParFilename, int iParStatus)
|
||||
{
|
||||
const char* szScript = g_pOptions->GetPostProcess();
|
||||
if (!szScript || strlen(szScript) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
info("Executing post-process for %s (%s)", szPath, BaseFileName(szNZBFilename));
|
||||
struct stat buffer;
|
||||
bool bExists = !stat(szScript, &buffer);
|
||||
if (!bExists)
|
||||
{
|
||||
error("Could not start post-process: could not find file %s", szScript);
|
||||
return;
|
||||
}
|
||||
|
||||
bool bCollectionCompleted = true;
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo2 = *it;
|
||||
if (!pFileInfo2->GetPaused() &&
|
||||
!strcmp(pFileInfo2->GetNZBFilename(), szNZBFilename))
|
||||
{
|
||||
bCollectionCompleted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
if (bCollectionCompleted)
|
||||
{
|
||||
m_mutexParChecker.Lock();
|
||||
for (ParQueue::iterator it = m_ParQueue.begin(); it != m_ParQueue.end(); it++)
|
||||
{
|
||||
ParJob* pParJob = *it;
|
||||
if (!strcmp(pParJob->GetNZBFilename(), szNZBFilename))
|
||||
{
|
||||
bCollectionCompleted = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
m_mutexParChecker.Unlock();
|
||||
}
|
||||
#endif
|
||||
|
||||
char szParStatus[10];
|
||||
snprintf(szParStatus, 10, "%i", iParStatus);
|
||||
szParStatus[10-1] = '\0';
|
||||
|
||||
char szCollectionCompleted[10];
|
||||
snprintf(szCollectionCompleted, 10, "%i", (int)bCollectionCompleted);
|
||||
szCollectionCompleted[10-1] = '\0';
|
||||
|
||||
#ifdef WIN32
|
||||
char szCmdLine[2048];
|
||||
snprintf(szCmdLine, 2048, "%s \"%s\" \"%s\" \"%s\" %s %s", szScript, szPath, szNZBFilename, szParFilename, szParStatus, szCollectionCompleted);
|
||||
szCmdLine[2048-1] = '\0';
|
||||
UINT ErrCode = WinExec(szCmdLine, SW_HIDE);
|
||||
if (ErrCode < 32)
|
||||
{
|
||||
char szErrMsg[255];
|
||||
szErrMsg[255-1] = '\0';
|
||||
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM || FORMAT_MESSAGE_IGNORE_INSERTS || FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
||||
NULL, ErrCode, 0, szErrMsg, 255, NULL);
|
||||
error("Could not start post-process: %s", szErrMsg);
|
||||
}
|
||||
#else
|
||||
if (fork())
|
||||
{
|
||||
// continue the first instance
|
||||
return;
|
||||
}
|
||||
|
||||
// here goes the second instance
|
||||
|
||||
int h;
|
||||
for (h = getdtablesize(); h >= 0;--h) close(h); /* close all descriptors */
|
||||
h = open("/dev/null", O_RDWR); dup(h); dup(h); /* handle standart I/O */
|
||||
|
||||
execlp(szScript, szScript, szPath, szNZBFilename, szParFilename, szParStatus, szCollectionCompleted, NULL);
|
||||
error("Could not start post-process: %s", strerror(errno));
|
||||
exit(-1);
|
||||
#endif
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef PREPOSTPROCESSOR_H
|
||||
#define PREPOSTPROCESSOR_H
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include "Thread.h"
|
||||
#include "Observer.h"
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
#include "ParChecker.h"
|
||||
#endif
|
||||
|
||||
class PrePostProcessor : public Thread
|
||||
{
|
||||
public:
|
||||
class ParJob
|
||||
{
|
||||
private:
|
||||
char* m_szNZBFilename;
|
||||
char* m_szParFilename;
|
||||
char* m_szInfoName;
|
||||
|
||||
public:
|
||||
ParJob(const char* szNZBFilename, const char* szParFilename, const char* szInfoName);
|
||||
~ParJob();
|
||||
const char* GetNZBFilename() { return m_szNZBFilename; }
|
||||
const char* GetParFilename() { return m_szParFilename; }
|
||||
const char* GetInfoName() { return m_szInfoName; }
|
||||
};
|
||||
|
||||
typedef std::deque<ParJob*> ParQueue;
|
||||
|
||||
private:
|
||||
typedef std::deque<char*> FileList;
|
||||
|
||||
class QueueCoordinatorObserver: public Observer
|
||||
{
|
||||
public:
|
||||
PrePostProcessor* owner;
|
||||
virtual void Update(Subject* Caller, void* Aspect) { owner->QueueCoordinatorUpdate(Caller, Aspect); }
|
||||
};
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
class ParCheckerObserver: public Observer
|
||||
{
|
||||
public:
|
||||
PrePostProcessor* owner;
|
||||
virtual void Update(Subject* Caller, void* Aspect) { owner->ParCheckerUpdate(Caller, Aspect); }
|
||||
};
|
||||
#endif
|
||||
|
||||
private:
|
||||
QueueCoordinatorObserver m_QueueCoordinatorObserver;
|
||||
bool m_bHasMoreJobs;
|
||||
|
||||
void PausePars(DownloadQueue* pDownloadQueue, const char* szNZBFilename);
|
||||
void CheckIncomingNZBs();
|
||||
bool WasLastInCollection(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, bool bIgnorePaused);
|
||||
void ExecPostScript(const char* szPath, const char* szNZBFilename, const char * szParFilename, int iParStatus);
|
||||
|
||||
|
||||
Mutex m_mutexParChecker;
|
||||
ParQueue m_ParQueue;
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
ParChecker m_ParChecker;
|
||||
ParCheckerObserver m_ParCheckerObserver;
|
||||
|
||||
void ParCheckerUpdate(Subject* Caller, void* Aspect);
|
||||
void CheckParQueue();
|
||||
void CheckPars(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
|
||||
bool AddPar(FileInfo* pFileInfo, bool bDeleted);
|
||||
bool SameParCollection(const char* szFilename1, const char* szFilename2);
|
||||
bool FindMainPars(const char* szPath, FileList* pFileList);
|
||||
#endif
|
||||
|
||||
public:
|
||||
PrePostProcessor();
|
||||
virtual ~PrePostProcessor();
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
void QueueCoordinatorUpdate(Subject* Caller, void* Aspect);
|
||||
bool HasMoreJobs() { return m_bHasMoreJobs; }
|
||||
ParQueue* LockParQueue();
|
||||
void UnlockParQueue();
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -1,781 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "Options.h"
|
||||
#include "ServerPool.h"
|
||||
#include "ArticleDownloader.h"
|
||||
#include "DiskState.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include "Decoder.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
extern ServerPool* g_pServerPool;
|
||||
extern DiskState* g_pDiskState;
|
||||
|
||||
QueueCoordinator::QueueCoordinator()
|
||||
{
|
||||
debug("Creating QueueCoordinator");
|
||||
|
||||
m_bHasMoreJobs = true;
|
||||
m_DownloadQueue.clear();
|
||||
m_ActiveDownloads.clear();
|
||||
|
||||
for (int i = 0; i < SPEEDMETER_SECONDS; i++)
|
||||
{
|
||||
m_iSpeedBytes[i] = 0;
|
||||
}
|
||||
m_iSpeedBytesIndex = 0;
|
||||
|
||||
m_iAllBytes = 0;
|
||||
m_tStartServer = 0;
|
||||
m_tStartDownload = 0;
|
||||
m_tPausedFrom = 0;
|
||||
m_bStandBy = true;
|
||||
|
||||
YDecoder::Init();
|
||||
}
|
||||
|
||||
QueueCoordinator::~QueueCoordinator()
|
||||
{
|
||||
debug("Destroying QueueCoordinator");
|
||||
// Cleanup
|
||||
|
||||
debug("Deleting DownloadQueue");
|
||||
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_DownloadQueue.clear();
|
||||
|
||||
debug("Deleting ArticleDownloaders");
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_ActiveDownloads.clear();
|
||||
|
||||
YDecoder::Final();
|
||||
|
||||
debug("QueueCoordinator destroyed");
|
||||
}
|
||||
|
||||
void QueueCoordinator::Run()
|
||||
{
|
||||
debug("Entering QueueCoordinator-loop");
|
||||
|
||||
m_mutexDownloadQueue.Lock();
|
||||
|
||||
if (g_pOptions->GetServerMode() && g_pOptions->GetSaveQueue() && g_pDiskState->Exists())
|
||||
{
|
||||
if (g_pOptions->GetReloadQueue())
|
||||
{
|
||||
g_pDiskState->Load(&m_DownloadQueue);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pDiskState->Discard();
|
||||
}
|
||||
}
|
||||
|
||||
g_pDiskState->CleanupTempDir(&m_DownloadQueue);
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
|
||||
m_tStartServer = time(NULL);
|
||||
bool bWasStandBy = true;
|
||||
bool bArticeDownloadsRunning = false;
|
||||
int iResetCounter = 0;
|
||||
|
||||
while (!IsStopped())
|
||||
{
|
||||
if (!g_pOptions->GetPause())
|
||||
{
|
||||
NNTPConnection* pConnection = g_pServerPool->GetConnection(0, false);
|
||||
if (pConnection)
|
||||
{
|
||||
// start download for next article
|
||||
FileInfo* pFileInfo;
|
||||
ArticleInfo* pArticleInfo;
|
||||
|
||||
m_mutexDownloadQueue.Lock();
|
||||
bool bHasMoreArticles = GetNextArticle(pFileInfo, pArticleInfo);
|
||||
bArticeDownloadsRunning = !m_ActiveDownloads.empty();
|
||||
m_bHasMoreJobs = bHasMoreArticles || bArticeDownloadsRunning;
|
||||
if (bHasMoreArticles && !IsStopped() && Thread::GetThreadCount() < g_pOptions->GetThreadLimit())
|
||||
{
|
||||
StartArticleDownload(pFileInfo, pArticleInfo, pConnection);
|
||||
bArticeDownloadsRunning = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pServerPool->FreeConnection(pConnection, false);
|
||||
}
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_mutexDownloadQueue.Lock();
|
||||
bArticeDownloadsRunning = !m_ActiveDownloads.empty();
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
}
|
||||
|
||||
bool bStandBy = !bArticeDownloadsRunning;
|
||||
if (bStandBy ^ bWasStandBy)
|
||||
{
|
||||
EnterLeaveStandBy(bStandBy);
|
||||
bWasStandBy = bStandBy;
|
||||
}
|
||||
|
||||
// sleep longer in StandBy
|
||||
int iSleepInterval = bStandBy ? 100 : 5;
|
||||
usleep(iSleepInterval * 1000);
|
||||
|
||||
AddSpeedReading(0);
|
||||
|
||||
iResetCounter+= iSleepInterval;
|
||||
if (iResetCounter >= 1000)
|
||||
{
|
||||
// this code should not be called too often, once per second is OK
|
||||
g_pServerPool->CloseUnusedConnections();
|
||||
ResetHangingDownloads();
|
||||
iResetCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// waiting for downloads
|
||||
debug("QueueCoordinator: waiting for Downloads to complete");
|
||||
bool completed = false;
|
||||
while (!completed)
|
||||
{
|
||||
m_mutexDownloadQueue.Lock();
|
||||
completed = m_ActiveDownloads.size() == 0;
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
usleep(100 * 1000);
|
||||
ResetHangingDownloads();
|
||||
}
|
||||
debug("QueueCoordinator: Downloads are completed");
|
||||
|
||||
debug("Exiting QueueCoordinator-loop");
|
||||
}
|
||||
|
||||
void QueueCoordinator::AddNZBFileToQueue(NZBFile* pNZBFile, bool bAddFirst)
|
||||
{
|
||||
debug("Adding NZBFile to queue");
|
||||
|
||||
m_mutexDownloadQueue.Lock();
|
||||
|
||||
DownloadQueue tmpDownloadQueue;
|
||||
tmpDownloadQueue.clear();
|
||||
DownloadQueue DupeList;
|
||||
DupeList.clear();
|
||||
|
||||
for (NZBFile::FileInfos::iterator it = pNZBFile->GetFileInfos()->begin(); it != pNZBFile->GetFileInfos()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
|
||||
if (g_pOptions->GetDupeCheck())
|
||||
{
|
||||
bool dupe = false;
|
||||
if (IsDupe(pFileInfo))
|
||||
{
|
||||
warn("File \"%s\" seems to be duplicate, skipping", pFileInfo->GetFilename());
|
||||
dupe = true;
|
||||
}
|
||||
for (NZBFile::FileInfos::iterator it2 = pNZBFile->GetFileInfos()->begin(); it2 != pNZBFile->GetFileInfos()->end(); it2++)
|
||||
{
|
||||
FileInfo* pFileInfo2 = *it2;
|
||||
if (!strcmp(pFileInfo->GetFilename(), pFileInfo2->GetFilename()) &&
|
||||
(pFileInfo->GetSize() < pFileInfo2->GetSize()))
|
||||
{
|
||||
warn("File \"%s\" appears twice in nzb-request, adding only the biggest file", pFileInfo->GetFilename());
|
||||
dupe = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dupe)
|
||||
{
|
||||
DupeList.push_back(pFileInfo);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (bAddFirst)
|
||||
{
|
||||
tmpDownloadQueue.push_front(pFileInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
tmpDownloadQueue.push_back(pFileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
for (DownloadQueue::iterator it = tmpDownloadQueue.begin(); it != tmpDownloadQueue.end(); it++)
|
||||
{
|
||||
if (bAddFirst)
|
||||
{
|
||||
m_DownloadQueue.push_front(*it);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_DownloadQueue.push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
for (DownloadQueue::iterator it = DupeList.begin(); it != DupeList.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
g_pDiskState->DiscardFile(NULL, pFileInfo);
|
||||
delete pFileInfo;
|
||||
}
|
||||
|
||||
pNZBFile->DetachFileInfos();
|
||||
|
||||
Aspect aspect = { eaNZBFileAdded, NULL, &m_DownloadQueue, pNZBFile->GetFileName() };
|
||||
Notify(&aspect);
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->Save(&m_DownloadQueue);
|
||||
}
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
}
|
||||
|
||||
bool QueueCoordinator::AddFileToQueue(const char* szFileName)
|
||||
{
|
||||
// Parse the buffer and make it into a NZBFile
|
||||
NZBFile* pNZBFile = NZBFile::CreateFromFile(szFileName);
|
||||
|
||||
// Did file parse correctly?
|
||||
if (!pNZBFile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add NZBFile to Queue
|
||||
AddNZBFileToQueue(pNZBFile, false);
|
||||
|
||||
delete pNZBFile;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: see note to "AddSpeedReading"
|
||||
*/
|
||||
float QueueCoordinator::CalcCurrentDownloadSpeed()
|
||||
{
|
||||
int iTotal = 0;
|
||||
|
||||
for (int i = 0; i < SPEEDMETER_SECONDS; i++)
|
||||
{
|
||||
iTotal += m_iSpeedBytes[i];
|
||||
}
|
||||
|
||||
float fSpeed = iTotal / 1024.0 / SPEEDMETER_SECONDS;
|
||||
|
||||
return fSpeed;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: we should use mutex by access to m_iSpeedBytes and m_iSpeedBytesIndex,
|
||||
* but this would results in a big performance loss (the function
|
||||
* "AddSpeedReading" is called extremly often), so we better agree with calculation
|
||||
* errors possible because of simultaneuos access from several threads.
|
||||
* The used algorithm is able to recover after few seconds.
|
||||
* In any case the calculation errors can not result in fatal system
|
||||
* errors (segmentation faults).
|
||||
*/
|
||||
void QueueCoordinator::AddSpeedReading(int iBytes)
|
||||
{
|
||||
int iIndex = time(NULL);
|
||||
|
||||
if (iIndex - m_iSpeedBytesIndex > SPEEDMETER_SECONDS)
|
||||
{
|
||||
m_iSpeedBytesIndex = iIndex - SPEEDMETER_SECONDS - 1;
|
||||
}
|
||||
|
||||
for (int i = m_iSpeedBytesIndex + 1; i < iIndex; i++)
|
||||
{
|
||||
m_iSpeedBytes[i % SPEEDMETER_SECONDS] = 0;
|
||||
}
|
||||
|
||||
if (iIndex > m_iSpeedBytesIndex)
|
||||
{
|
||||
m_iSpeedBytesIndex = iIndex;
|
||||
m_iSpeedBytes[iIndex % SPEEDMETER_SECONDS] = iBytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_iSpeedBytes[m_iSpeedBytesIndex % SPEEDMETER_SECONDS] += iBytes;
|
||||
}
|
||||
|
||||
m_iAllBytes += iBytes;
|
||||
}
|
||||
|
||||
long long QueueCoordinator::CalcRemainingSize()
|
||||
{
|
||||
long long lRemainingSize = 0;
|
||||
|
||||
m_mutexDownloadQueue.Lock();
|
||||
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (!pFileInfo->GetPaused() && !pFileInfo->GetDeleted())
|
||||
{
|
||||
lRemainingSize += pFileInfo->GetRemainingSize();
|
||||
}
|
||||
}
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
|
||||
return lRemainingSize;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: DownloadQueue must be locked prior to call of this function
|
||||
* Returns True if Entry was deleted from Queue or False if it was scheduled for Deletion.
|
||||
* NOTE: "False" does not mean unsuccess; the entry is (or will be) deleted in any case.
|
||||
*/
|
||||
bool QueueCoordinator::DeleteQueueEntry(FileInfo* pFileInfo)
|
||||
{
|
||||
pFileInfo->SetDeleted(true);
|
||||
bool hasDownloads = false;
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
ArticleDownloader* pArticleDownloader = *it;
|
||||
if (pArticleDownloader->GetFileInfo() == pFileInfo)
|
||||
{
|
||||
hasDownloads = true;
|
||||
pArticleDownloader->Stop();
|
||||
}
|
||||
}
|
||||
if (!hasDownloads)
|
||||
{
|
||||
Aspect aspect = { eaFileDeleted, pFileInfo, &m_DownloadQueue, NULL };
|
||||
Notify(&aspect);
|
||||
|
||||
DeleteFileInfo(pFileInfo, false);
|
||||
}
|
||||
return hasDownloads;
|
||||
}
|
||||
|
||||
void QueueCoordinator::Stop()
|
||||
{
|
||||
Thread::Stop();
|
||||
|
||||
debug("Stopping ArticleDownloads");
|
||||
m_mutexDownloadQueue.Lock();
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
(*it)->Stop();
|
||||
}
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
debug("ArticleDownloads are notified");
|
||||
}
|
||||
|
||||
bool QueueCoordinator::GetNextArticle(FileInfo* &pFileInfo, ArticleInfo* &pArticleInfo)
|
||||
{
|
||||
//debug("QueueCoordinator::GetNextArticle()");
|
||||
|
||||
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
|
||||
{
|
||||
pFileInfo = *it;
|
||||
if (!pFileInfo->GetPaused() && !pFileInfo->GetDeleted())
|
||||
{
|
||||
if (pFileInfo->GetArticles()->empty() && g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->LoadArticles(pFileInfo);
|
||||
}
|
||||
for (FileInfo::Articles::iterator at = pFileInfo->GetArticles()->begin(); at != pFileInfo->GetArticles()->end(); at++)
|
||||
{
|
||||
pArticleInfo = *at;
|
||||
if (pArticleInfo->GetStatus() == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void QueueCoordinator::StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pArticleInfo, NNTPConnection* pConnection)
|
||||
{
|
||||
debug("Starting new ArticleDownloader");
|
||||
|
||||
ArticleDownloader* pArticleDownloader = new ArticleDownloader();
|
||||
pArticleDownloader->SetAutoDestroy(true);
|
||||
pArticleDownloader->Attach(this);
|
||||
pArticleDownloader->SetFileInfo(pFileInfo);
|
||||
pArticleDownloader->SetArticleInfo(pArticleInfo);
|
||||
pArticleDownloader->SetConnection(pConnection);
|
||||
BuildArticleFilename(pArticleDownloader, pFileInfo, pArticleInfo);
|
||||
|
||||
pArticleInfo->SetStatus(ArticleInfo::aiRunning);
|
||||
|
||||
m_ActiveDownloads.push_back(pArticleDownloader);
|
||||
pArticleDownloader->Start();
|
||||
}
|
||||
|
||||
void QueueCoordinator::BuildArticleFilename(ArticleDownloader* pArticleDownloader, FileInfo* pFileInfo, ArticleInfo* pArticleInfo)
|
||||
{
|
||||
char name[1024];
|
||||
|
||||
snprintf(name, 1024, "%s%i.%03i", g_pOptions->GetTempDir(), pFileInfo->GetID(), pArticleInfo->GetPartNumber());
|
||||
name[1024-1] = '\0';
|
||||
pArticleInfo->SetResultFilename(name);
|
||||
|
||||
char tmpname[1024];
|
||||
snprintf(tmpname, 1024, "%s.tmp", name);
|
||||
tmpname[1024-1] = '\0';
|
||||
pArticleDownloader->SetTempFilename(tmpname);
|
||||
|
||||
char szNZBNiceName[1024];
|
||||
pFileInfo->GetNiceNZBName(szNZBNiceName, 1024);
|
||||
|
||||
snprintf(name, 1024, "%s%c%s [%i/%i]", szNZBNiceName, (int)PATH_SEPARATOR, pFileInfo->GetFilename(), pArticleInfo->GetPartNumber(), pFileInfo->GetArticles()->size());
|
||||
name[1024-1] = '\0';
|
||||
pArticleDownloader->SetInfoName(name);
|
||||
|
||||
if (g_pOptions->GetDirectWrite())
|
||||
{
|
||||
snprintf(name, 1024, "%s%i.out", g_pOptions->GetTempDir(), pFileInfo->GetID());
|
||||
name[1024-1] = '\0';
|
||||
pArticleDownloader->SetOutputFilename(name);
|
||||
}
|
||||
}
|
||||
|
||||
DownloadQueue* QueueCoordinator::LockQueue()
|
||||
{
|
||||
m_mutexDownloadQueue.Lock();
|
||||
return &m_DownloadQueue;
|
||||
}
|
||||
|
||||
void QueueCoordinator::UnlockQueue()
|
||||
{
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
}
|
||||
|
||||
void QueueCoordinator::Update(Subject* Caller, void* Aspect)
|
||||
{
|
||||
debug("Notification from ArticleDownloader received");
|
||||
|
||||
ArticleDownloader* pArticleDownloader = (ArticleDownloader*) Caller;
|
||||
if ((pArticleDownloader->GetStatus() == ArticleDownloader::adFinished) ||
|
||||
(pArticleDownloader->GetStatus() == ArticleDownloader::adFailed))
|
||||
{
|
||||
ArticleCompleted(pArticleDownloader);
|
||||
}
|
||||
}
|
||||
|
||||
void QueueCoordinator::ArticleCompleted(ArticleDownloader* pArticleDownloader)
|
||||
{
|
||||
debug("Article downloaded");
|
||||
|
||||
FileInfo* pFileInfo = pArticleDownloader->GetFileInfo();
|
||||
ArticleInfo* pArticleInfo = pArticleDownloader->GetArticleInfo();
|
||||
|
||||
m_mutexDownloadQueue.Lock();
|
||||
|
||||
if (pArticleDownloader->GetStatus() == ArticleDownloader::adFinished)
|
||||
{
|
||||
pArticleInfo->SetStatus(ArticleInfo::aiFinished);
|
||||
}
|
||||
else if (pArticleDownloader->GetStatus() == ArticleDownloader::adFailed)
|
||||
{
|
||||
pArticleInfo->SetStatus(ArticleInfo::aiFailed);
|
||||
}
|
||||
|
||||
pFileInfo->SetRemainingSize(pFileInfo->GetRemainingSize() - pArticleInfo->GetSize());
|
||||
pFileInfo->SetCompleted(pFileInfo->GetCompleted() + 1);
|
||||
bool fileCompleted = (int)pFileInfo->GetArticles()->size() == pFileInfo->GetCompleted();
|
||||
|
||||
if (!pFileInfo->GetFilenameConfirmed() &&
|
||||
pArticleDownloader->GetStatus() == ArticleDownloader::adFinished &&
|
||||
pArticleDownloader->GetArticleFilename())
|
||||
{
|
||||
pFileInfo->SetFilename(pArticleDownloader->GetArticleFilename());
|
||||
pFileInfo->SetFilenameConfirmed(true);
|
||||
if (g_pOptions->GetDupeCheck() && pFileInfo->IsDupe(pFileInfo->GetFilename()))
|
||||
{
|
||||
warn("File \"%s\" seems to be duplicate, cancelling download and deleting file from queue", pFileInfo->GetFilename());
|
||||
fileCompleted = false;
|
||||
DeleteQueueEntry(pFileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
bool deleteFileObj = false;
|
||||
|
||||
if (pFileInfo->GetDeleted())
|
||||
{
|
||||
int cnt = 0;
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
if ((*it)->GetFileInfo() == pFileInfo)
|
||||
{
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
if (cnt == 1)
|
||||
{
|
||||
// this was the last Download for a file deleted from queue
|
||||
deleteFileObj = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (fileCompleted && !IsStopped() && !pFileInfo->GetDeleted())
|
||||
{
|
||||
// all jobs done
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
pArticleDownloader->CompleteFileParts();
|
||||
m_mutexDownloadQueue.Lock();
|
||||
deleteFileObj = true;
|
||||
}
|
||||
|
||||
// delete Download from Queue
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
ArticleDownloader* pa = *it;
|
||||
if (pa == pArticleDownloader)
|
||||
{
|
||||
m_ActiveDownloads.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (deleteFileObj)
|
||||
{
|
||||
// delete File from Queue
|
||||
pFileInfo->SetDeleted(true);
|
||||
|
||||
Aspect aspect = { fileCompleted ? eaFileCompleted : eaFileDeleted, pFileInfo, &m_DownloadQueue, NULL };
|
||||
Notify(&aspect);
|
||||
|
||||
DeleteFileInfo(pFileInfo, fileCompleted);
|
||||
}
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
}
|
||||
|
||||
void QueueCoordinator::DeleteFileInfo(FileInfo* pFileInfo, bool bCompleted)
|
||||
{
|
||||
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
|
||||
{
|
||||
FileInfo* pa = *it;
|
||||
if (pa == pFileInfo)
|
||||
{
|
||||
m_DownloadQueue.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->DiscardFile(&m_DownloadQueue, pFileInfo);
|
||||
}
|
||||
|
||||
if (!bCompleted)
|
||||
{
|
||||
// deleting temporary files
|
||||
|
||||
if (!g_pOptions->GetDirectWrite() || g_pOptions->GetContinuePartial())
|
||||
{
|
||||
for (FileInfo::Articles::iterator it = pFileInfo->GetArticles()->begin(); it != pFileInfo->GetArticles()->end(); it++)
|
||||
{
|
||||
ArticleInfo* pa = *it;
|
||||
if (pa->GetResultFilename())
|
||||
{
|
||||
remove(pa->GetResultFilename());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (g_pOptions->GetDirectWrite())
|
||||
{
|
||||
char name[1024];
|
||||
snprintf(name, 1024, "%s%i.out", g_pOptions->GetTempDir(), pFileInfo->GetID());
|
||||
name[1024-1] = '\0';
|
||||
remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
delete pFileInfo;
|
||||
}
|
||||
|
||||
bool QueueCoordinator::IsDupe(FileInfo* pFileInfo)
|
||||
{
|
||||
debug("Checking if the file is already queued");
|
||||
|
||||
// checking on disk
|
||||
if (pFileInfo->IsDupe(pFileInfo->GetFilename()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// checking in queue
|
||||
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
|
||||
{
|
||||
FileInfo* pQueueEntry = *it;
|
||||
if (!strcmp(pFileInfo->GetDestDir(), pQueueEntry->GetDestDir()) &&
|
||||
!strcmp(pFileInfo->GetFilename(), pQueueEntry->GetFilename()) &&
|
||||
pFileInfo != pQueueEntry)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void QueueCoordinator::LogDebugInfo()
|
||||
{
|
||||
debug("--------------------------------------------");
|
||||
debug("Dumping debug info to log");
|
||||
debug("--------------------------------------------");
|
||||
|
||||
debug(" QueueCoordinator");
|
||||
debug(" ----------------");
|
||||
|
||||
m_mutexDownloadQueue.Lock();
|
||||
debug(" Active Downloads: %i", m_ActiveDownloads.size());
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
ArticleDownloader* pArticleDownloader = *it;
|
||||
pArticleDownloader->LogDebugInfo();
|
||||
}
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
|
||||
debug("");
|
||||
|
||||
g_pServerPool->LogDebugInfo();
|
||||
}
|
||||
|
||||
void QueueCoordinator::ResetHangingDownloads()
|
||||
{
|
||||
const int TimeOut = g_pOptions->GetTerminateTimeout();
|
||||
if (TimeOut == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_mutexDownloadQueue.Lock();
|
||||
time_t tm = ::time(NULL);
|
||||
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end();)
|
||||
{
|
||||
ArticleDownloader* pArticleDownloader = *it;
|
||||
if (tm - pArticleDownloader->GetLastUpdateTime() > TimeOut &&
|
||||
pArticleDownloader->GetStatus() == ArticleDownloader::adRunning)
|
||||
{
|
||||
ArticleInfo* pArticleInfo = pArticleDownloader->GetArticleInfo();
|
||||
debug("Terminating hanging download %s", pArticleDownloader->GetInfoName());
|
||||
if (pArticleDownloader->Terminate())
|
||||
{
|
||||
error("Terminated hanging download %s", pArticleDownloader->GetInfoName());
|
||||
pArticleInfo->SetStatus(ArticleInfo::aiUndefined);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not terminate hanging download %s", BaseFileName(pArticleInfo->GetResultFilename()));
|
||||
}
|
||||
m_ActiveDownloads.erase(it);
|
||||
// it's not safe to destroy pArticleDownloader, because the state of object is unknown
|
||||
delete pArticleDownloader;
|
||||
it = m_ActiveDownloads.begin();
|
||||
continue;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
}
|
||||
|
||||
void QueueCoordinator::EnterLeaveStandBy(bool bEnter)
|
||||
{
|
||||
m_mutexStat.Lock();
|
||||
m_bStandBy = bEnter;
|
||||
if (bEnter)
|
||||
{
|
||||
m_tPausedFrom = time(NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_tStartDownload == 0)
|
||||
{
|
||||
m_tStartDownload = time(NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_tStartDownload += time(NULL) - m_tPausedFrom;
|
||||
}
|
||||
m_tPausedFrom = 0;
|
||||
}
|
||||
m_mutexStat.Unlock();
|
||||
}
|
||||
|
||||
void QueueCoordinator::CalcStat(int* iUpTimeSec, int* iDnTimeSec, long long* iAllBytes, bool* bStandBy)
|
||||
{
|
||||
m_mutexStat.Lock();
|
||||
if (m_tStartServer > 0)
|
||||
{
|
||||
*iUpTimeSec = time(NULL) - m_tStartServer;
|
||||
}
|
||||
else
|
||||
{
|
||||
*iUpTimeSec = 0;
|
||||
}
|
||||
*bStandBy = m_bStandBy;
|
||||
if (m_bStandBy)
|
||||
{
|
||||
*iDnTimeSec = m_tPausedFrom - m_tStartDownload;
|
||||
}
|
||||
else
|
||||
{
|
||||
*iDnTimeSec = time(NULL) - m_tStartDownload;
|
||||
}
|
||||
*iAllBytes = m_iAllBytes;
|
||||
m_mutexStat.Unlock();
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef QUEUECOORDINATOR_H
|
||||
#define QUEUECOORDINATOR_H
|
||||
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <time.h>
|
||||
#ifdef WIN32
|
||||
#include <sys/timeb.h>
|
||||
#endif
|
||||
|
||||
#include "Thread.h"
|
||||
#include "NZBFile.h"
|
||||
#include "ArticleDownloader.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Observer.h"
|
||||
#include "QueueEditor.h"
|
||||
#include "NNTPConnection.h"
|
||||
|
||||
class QueueCoordinator : public Thread, public Observer, public Subject, public DownloadSpeedMeter
|
||||
{
|
||||
public:
|
||||
typedef std::list<ArticleDownloader*> ActiveDownloads;
|
||||
typedef enum EAspectAction
|
||||
{
|
||||
eaNZBFileAdded,
|
||||
eaFileCompleted,
|
||||
eaFileDeleted
|
||||
};
|
||||
typedef struct Aspect
|
||||
{
|
||||
EAspectAction eAction;
|
||||
FileInfo* pFileInfo;
|
||||
DownloadQueue* pDownloadQueue;
|
||||
const char* szNZBFilename;
|
||||
};
|
||||
|
||||
private:
|
||||
DownloadQueue m_DownloadQueue;
|
||||
ActiveDownloads m_ActiveDownloads;
|
||||
QueueEditor m_QueueEditor;
|
||||
Mutex m_mutexDownloadQueue;
|
||||
bool m_bHasMoreJobs;
|
||||
|
||||
// statistics
|
||||
static const int SPEEDMETER_SECONDS = 5;
|
||||
int m_iSpeedBytes[SPEEDMETER_SECONDS];
|
||||
int m_iSpeedBytesIndex;
|
||||
long long m_iAllBytes;
|
||||
time_t m_tStartServer;
|
||||
time_t m_tStartDownload;
|
||||
time_t m_tPausedFrom;
|
||||
void EnterLeaveStandBy(bool bEnter);
|
||||
bool m_bStandBy;
|
||||
Mutex m_mutexStat;
|
||||
|
||||
bool GetNextArticle(FileInfo* &pFileInfo, ArticleInfo* &pArticleInfo);
|
||||
void StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pArticleInfo, NNTPConnection* pConnection);
|
||||
void BuildArticleFilename(ArticleDownloader* pArticleDownloader, FileInfo* pFileInfo, ArticleInfo* pArticleInfo);
|
||||
bool IsDupe(FileInfo* pFileInfo);
|
||||
void ArticleCompleted(ArticleDownloader* pArticleDownloader);
|
||||
void DeleteFileInfo(FileInfo* pFileInfo, bool bCompleted);
|
||||
void ResetHangingDownloads();
|
||||
|
||||
public:
|
||||
QueueCoordinator();
|
||||
virtual ~QueueCoordinator();
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
void Update(Subject* Caller, void* Aspect);
|
||||
|
||||
// statistics
|
||||
long long CalcRemainingSize();
|
||||
virtual float CalcCurrentDownloadSpeed();
|
||||
virtual void AddSpeedReading(int iBytes);
|
||||
void CalcStat(int* iUpTimeSec, int* iDnTimeSec, long long* iAllBytes, bool* bStandBy);
|
||||
|
||||
// Editing the queue
|
||||
DownloadQueue* LockQueue();
|
||||
void UnlockQueue() ;
|
||||
void AddNZBFileToQueue(NZBFile* pNZBQueue, bool bAddFirst);
|
||||
bool AddFileToQueue(const char* szFileName);
|
||||
bool HasMoreJobs() { return m_bHasMoreJobs; }
|
||||
bool DeleteQueueEntry(FileInfo* pFileInfo);
|
||||
QueueEditor* GetQueueEditor() { return &m_QueueEditor; }
|
||||
|
||||
void LogDebugInfo();
|
||||
};
|
||||
|
||||
#endif
|
||||
655
QueueEditor.cpp
655
QueueEditor.cpp
@@ -1,655 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "QueueEditor.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "DiskState.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
extern Options* g_pOptions;
|
||||
extern DiskState* g_pDiskState;
|
||||
|
||||
const int MAX_ID = 100000000;
|
||||
|
||||
QueueEditor::EditItem::EditItem(FileInfo* pFileInfo, int iOffset)
|
||||
{
|
||||
m_pFileInfo = pFileInfo;
|
||||
m_iOffset = iOffset;
|
||||
}
|
||||
|
||||
QueueEditor::QueueEditor()
|
||||
{
|
||||
debug("Creating QueueEditor");
|
||||
}
|
||||
|
||||
QueueEditor::~QueueEditor()
|
||||
{
|
||||
debug("Destroying QueueEditor");
|
||||
}
|
||||
|
||||
FileInfo* QueueEditor::FindFileInfo(DownloadQueue* pDownloadQueue, int iID)
|
||||
{
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (pFileInfo->GetID() == iID)
|
||||
{
|
||||
return pFileInfo;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int QueueEditor::FindFileInfoEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo)
|
||||
{
|
||||
int iEntry = 0;
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo2 = *it;
|
||||
if (pFileInfo2 == pFileInfo)
|
||||
{
|
||||
return iEntry;
|
||||
}
|
||||
iEntry ++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the pause flag of the specific entry in the queue
|
||||
* returns true if successful, false if operation is not possible
|
||||
*/
|
||||
void QueueEditor::PauseUnpauseEntry(FileInfo* pFileInfo, bool bPause)
|
||||
{
|
||||
pFileInfo->SetPaused(bPause);
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes entry with index iEntry
|
||||
* returns true if successful, false if operation is not possible
|
||||
*/
|
||||
void QueueEditor::DeleteEntry(FileInfo* pFileInfo)
|
||||
{
|
||||
info("Deleting file %s from download queue", pFileInfo->GetFilename());
|
||||
g_pQueueCoordinator->DeleteQueueEntry(pFileInfo);
|
||||
}
|
||||
|
||||
/*
|
||||
* Moves entry identified with iID in the queue
|
||||
* returns true if successful, false if operation is not possible
|
||||
*/
|
||||
void QueueEditor::MoveEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, int iOffset)
|
||||
{
|
||||
int iEntry = FindFileInfoEntry(pDownloadQueue, pFileInfo);
|
||||
if (iEntry > -1)
|
||||
{
|
||||
int iNewEntry = iEntry + iOffset;
|
||||
|
||||
if (iNewEntry < 0)
|
||||
{
|
||||
iNewEntry = 0;
|
||||
}
|
||||
if ((unsigned int)iNewEntry > pDownloadQueue->size() - 1)
|
||||
{
|
||||
iNewEntry = (int)pDownloadQueue->size() - 1;
|
||||
}
|
||||
|
||||
if (iNewEntry >= 0 && (unsigned int)iNewEntry <= pDownloadQueue->size() - 1)
|
||||
{
|
||||
FileInfo* fi = (*pDownloadQueue)[iEntry];
|
||||
pDownloadQueue->erase(pDownloadQueue->begin() + iEntry);
|
||||
pDownloadQueue->insert(pDownloadQueue->begin() + iNewEntry, fi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool QueueEditor::EditEntry(int ID, bool bSmartOrder, EEditAction eAction, int iOffset)
|
||||
{
|
||||
IDList cIDList;
|
||||
cIDList.clear();
|
||||
cIDList.push_back(ID);
|
||||
return EditList(&cIDList, bSmartOrder, eAction, iOffset);
|
||||
}
|
||||
|
||||
bool QueueEditor::LockedEditEntry(DownloadQueue* pDownloadQueue, int ID, bool bSmartOrder, EEditAction eAction, int iOffset)
|
||||
{
|
||||
IDList cIDList;
|
||||
cIDList.clear();
|
||||
cIDList.push_back(ID);
|
||||
return InternEditList(pDownloadQueue, &cIDList, bSmartOrder, eAction, iOffset);
|
||||
}
|
||||
|
||||
bool QueueEditor::EditList(IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset)
|
||||
{
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
|
||||
bool bOK = InternEditList(pDownloadQueue, pIDList, bSmartOrder, eAction, iOffset);
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->Save(pDownloadQueue);
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
|
||||
return bOK;
|
||||
}
|
||||
|
||||
bool QueueEditor::InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset)
|
||||
{
|
||||
if (eAction == eaGroupMoveOffset)
|
||||
{
|
||||
AlignAffectedGroups(pDownloadQueue, pIDList, bSmartOrder, iOffset);
|
||||
}
|
||||
|
||||
ItemList cItemList;
|
||||
PrepareList(pDownloadQueue, &cItemList, pIDList, bSmartOrder, eAction, iOffset);
|
||||
|
||||
if (eAction == eaFilePauseAllPars || eAction == eaFilePauseExtraPars)
|
||||
{
|
||||
PauseParsInGroups(&cItemList, eAction == eaFilePauseExtraPars);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (ItemList::iterator it = cItemList.begin(); it != cItemList.end(); it++)
|
||||
{
|
||||
EditItem* pItem = *it;
|
||||
switch (eAction)
|
||||
{
|
||||
case eaFilePause:
|
||||
PauseUnpauseEntry(pItem->m_pFileInfo, true);
|
||||
break;
|
||||
|
||||
case eaFileResume:
|
||||
PauseUnpauseEntry(pItem->m_pFileInfo, false);
|
||||
break;
|
||||
|
||||
case eaFileMoveOffset:
|
||||
case eaFileMoveTop:
|
||||
case eaFileMoveBottom:
|
||||
MoveEntry(pDownloadQueue, pItem->m_pFileInfo, pItem->m_iOffset);
|
||||
break;
|
||||
|
||||
case eaFileDelete:
|
||||
DeleteEntry(pItem->m_pFileInfo);
|
||||
break;
|
||||
|
||||
case eaFilePauseAllPars:
|
||||
case eaFilePauseExtraPars:
|
||||
// remove compiler warning "enumeration not handled in switch"
|
||||
break;
|
||||
|
||||
case eaGroupPause:
|
||||
case eaGroupResume:
|
||||
case eaGroupDelete:
|
||||
case eaGroupMoveTop:
|
||||
case eaGroupMoveBottom:
|
||||
case eaGroupMoveOffset:
|
||||
case eaGroupPauseAllPars:
|
||||
case eaGroupPauseExtraPars:
|
||||
EditGroup(pDownloadQueue, pItem->m_pFileInfo, eAction, iOffset);
|
||||
break;
|
||||
}
|
||||
delete pItem;
|
||||
}
|
||||
}
|
||||
|
||||
return cItemList.size() > 0;
|
||||
}
|
||||
|
||||
void QueueEditor::PrepareList(DownloadQueue* pDownloadQueue, ItemList* pItemList, IDList* pIDList, bool bSmartOrder,
|
||||
EEditAction EEditAction, int iOffset)
|
||||
{
|
||||
if (EEditAction == eaFileMoveTop)
|
||||
{
|
||||
iOffset = -MAX_ID;
|
||||
}
|
||||
else if (EEditAction == eaFileMoveBottom)
|
||||
{
|
||||
iOffset = MAX_ID;
|
||||
}
|
||||
|
||||
pItemList->reserve(pIDList->size());
|
||||
if (bSmartOrder && iOffset != 0 &&
|
||||
(EEditAction == eaFileMoveOffset || EEditAction == eaFileMoveTop || EEditAction == eaFileMoveBottom))
|
||||
{
|
||||
//add IDs to list in order they currently have in download queue
|
||||
int iLastDestPos = -1;
|
||||
int iStart, iEnd, iStep;
|
||||
if (iOffset < 0)
|
||||
{
|
||||
iStart = 0;
|
||||
iEnd = pDownloadQueue->size();
|
||||
iStep = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
iStart = pDownloadQueue->size() - 1;
|
||||
iEnd = -1;
|
||||
iStep = -1;
|
||||
}
|
||||
for (int iIndex = iStart; iIndex != iEnd; iIndex += iStep)
|
||||
{
|
||||
FileInfo* pFileInfo = (*pDownloadQueue)[iIndex];
|
||||
int iID = pFileInfo->GetID();
|
||||
for (IDList::iterator it = pIDList->begin(); it != pIDList->end(); it++)
|
||||
{
|
||||
if (iID == *it)
|
||||
{
|
||||
int iWorkOffset = iOffset;
|
||||
int iDestPos = iIndex + iWorkOffset;
|
||||
if (iLastDestPos == -1)
|
||||
{
|
||||
if (iDestPos < 0)
|
||||
{
|
||||
iWorkOffset = -iIndex;
|
||||
}
|
||||
else if (iDestPos > int(pDownloadQueue->size()) - 1)
|
||||
{
|
||||
iWorkOffset = int(pDownloadQueue->size()) - 1 - iIndex;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (iWorkOffset < 0 && iDestPos <= iLastDestPos)
|
||||
{
|
||||
iWorkOffset = iLastDestPos - iIndex + 1;
|
||||
}
|
||||
else if (iWorkOffset > 0 && iDestPos >= iLastDestPos)
|
||||
{
|
||||
iWorkOffset = iLastDestPos - iIndex - 1;
|
||||
}
|
||||
}
|
||||
iLastDestPos = iIndex + iWorkOffset;
|
||||
pItemList->push_back(new EditItem(pFileInfo, iWorkOffset));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// check ID range
|
||||
int iMaxID = 0;
|
||||
int iMinID = MAX_ID;
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
int ID = pFileInfo->GetID();
|
||||
if (ID > iMaxID)
|
||||
{
|
||||
iMaxID = ID;
|
||||
}
|
||||
if (ID < iMinID)
|
||||
{
|
||||
iMinID = ID;
|
||||
}
|
||||
}
|
||||
|
||||
//add IDs to list in order they were transmitted in command
|
||||
for (IDList::iterator it = pIDList->begin(); it != pIDList->end(); it++)
|
||||
{
|
||||
int iID = *it;
|
||||
if (iMinID <= iID && iID <= iMaxID)
|
||||
{
|
||||
FileInfo* pFileInfo = FindFileInfo(pDownloadQueue, iID);
|
||||
if (pFileInfo)
|
||||
{
|
||||
pItemList->push_back(new EditItem(pFileInfo, iOffset));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool QueueEditor::EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, EEditAction eAction, int iOffset)
|
||||
{
|
||||
IDList cIDList;
|
||||
cIDList.clear();
|
||||
|
||||
// collecting files belonging to group
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo2 = *it;
|
||||
if (!strcmp(pFileInfo2->GetNZBFilename(), pFileInfo->GetNZBFilename()))
|
||||
{
|
||||
cIDList.push_back(pFileInfo2->GetID());
|
||||
}
|
||||
}
|
||||
|
||||
if (eAction == eaGroupMoveOffset)
|
||||
{
|
||||
// calculating offset in terms of files
|
||||
FileList cGroupList;
|
||||
BuildGroupList(pDownloadQueue, &cGroupList);
|
||||
unsigned int iNum = 0;
|
||||
for (FileList::iterator it = cGroupList.begin(); it != cGroupList.end(); it++, iNum++)
|
||||
{
|
||||
FileInfo* pGroupInfo = *it;
|
||||
if (!strcmp(pGroupInfo->GetNZBFilename(), pFileInfo->GetNZBFilename()))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
int iFileOffset = 0;
|
||||
if (iOffset > 0)
|
||||
{
|
||||
if (iNum + iOffset >= cGroupList.size() - 1)
|
||||
{
|
||||
eAction = eaGroupMoveBottom;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (unsigned int i = iNum + 2; i < cGroupList.size() && iOffset > 0; i++, iOffset--)
|
||||
{
|
||||
iFileOffset += FindFileInfoEntry(pDownloadQueue, cGroupList[i]) - FindFileInfoEntry(pDownloadQueue, cGroupList[i-1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (iNum + iOffset <= 0)
|
||||
{
|
||||
eAction = eaGroupMoveTop;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (unsigned int i = iNum; i > 0 && iOffset < 0; i--, iOffset++)
|
||||
{
|
||||
iFileOffset -= FindFileInfoEntry(pDownloadQueue, cGroupList[i]) - FindFileInfoEntry(pDownloadQueue, cGroupList[i-1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
iOffset = iFileOffset;
|
||||
}
|
||||
|
||||
EEditAction GroupToFileMap[] = { (EEditAction)0, eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause, eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars,
|
||||
eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause, eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars };
|
||||
|
||||
return InternEditList(pDownloadQueue, &cIDList, true, GroupToFileMap[eAction], iOffset);
|
||||
}
|
||||
|
||||
void QueueEditor::BuildGroupList(DownloadQueue* pDownloadQueue, FileList* pGroupList)
|
||||
{
|
||||
pGroupList->clear();
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
FileInfo* pGroupInfo = NULL;
|
||||
for (FileList::iterator itg = pGroupList->begin(); itg != pGroupList->end(); itg++)
|
||||
{
|
||||
FileInfo* pGroupInfo1 = *itg;
|
||||
if (!strcmp(pGroupInfo1->GetNZBFilename(), pFileInfo->GetNZBFilename()))
|
||||
{
|
||||
pGroupInfo = pGroupInfo1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!pGroupInfo)
|
||||
{
|
||||
pGroupList->push_back(pFileInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool QueueEditor::ItemExists(FileList* pFileList, FileInfo* pFileInfo)
|
||||
{
|
||||
for (FileList::iterator it = pFileList->begin(); it != pFileList->end(); it++)
|
||||
{
|
||||
if (*it == pFileInfo)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void QueueEditor::AlignAffectedGroups(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, int iOffset)
|
||||
{
|
||||
// Build list of all groups; List contains first file of each group
|
||||
FileList cGroupList;
|
||||
BuildGroupList(pDownloadQueue, &cGroupList);
|
||||
|
||||
// Find affected groups. It includes groups being moved and groups directly
|
||||
// above or under of these groups (those order is also changed)
|
||||
FileList cAffectedGroupList;
|
||||
cAffectedGroupList.clear();
|
||||
ItemList cItemList;
|
||||
PrepareList(pDownloadQueue, &cItemList, pIDList, bSmartOrder, eaFileMoveOffset, iOffset);
|
||||
for (ItemList::iterator it = cItemList.begin(); it != cItemList.end(); it++)
|
||||
{
|
||||
EditItem* pItem = *it;
|
||||
unsigned int iNum = 0;
|
||||
for (FileList::iterator it = cGroupList.begin(); it != cGroupList.end(); it++, iNum++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (!strcmp(pItem->m_pFileInfo->GetNZBFilename(), pFileInfo->GetNZBFilename()))
|
||||
{
|
||||
if (!ItemExists(&cAffectedGroupList, pFileInfo))
|
||||
{
|
||||
cAffectedGroupList.push_back(pFileInfo);
|
||||
}
|
||||
if (iOffset < 0)
|
||||
{
|
||||
for (int i = iNum - 1; i >= -iOffset-1; i--)
|
||||
{
|
||||
if (!ItemExists(&cAffectedGroupList, cGroupList[i]))
|
||||
{
|
||||
cAffectedGroupList.push_back(cGroupList[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (iOffset > 0)
|
||||
{
|
||||
for (unsigned int i = iNum + 1; i <= cGroupList.size() - iOffset; i++)
|
||||
{
|
||||
if (!ItemExists(&cAffectedGroupList, cGroupList[i]))
|
||||
{
|
||||
cAffectedGroupList.push_back(cGroupList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (iNum + 1 < cGroupList.size())
|
||||
{
|
||||
cAffectedGroupList.push_back(cGroupList[iNum + 1]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
delete pItem;
|
||||
}
|
||||
cGroupList.clear();
|
||||
|
||||
// Aligning groups
|
||||
for (FileList::iterator it = cAffectedGroupList.begin(); it != cAffectedGroupList.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
AlignGroup(pDownloadQueue, pFileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void QueueEditor::AlignGroup(DownloadQueue* pDownloadQueue, FileInfo* pFirstFileInfo)
|
||||
{
|
||||
FileInfo* pLastFileInfo = NULL;
|
||||
unsigned int iLastNum = 0;
|
||||
unsigned int iNum = 0;
|
||||
while (iNum < pDownloadQueue->size())
|
||||
{
|
||||
FileInfo* pFileInfo = (*pDownloadQueue)[iNum];
|
||||
if (!strcmp(pFirstFileInfo->GetNZBFilename(), pFileInfo->GetNZBFilename()))
|
||||
{
|
||||
if (pLastFileInfo && iNum - iLastNum > 1)
|
||||
{
|
||||
pDownloadQueue->erase(pDownloadQueue->begin() + iNum);
|
||||
pDownloadQueue->insert(pDownloadQueue->begin() + iLastNum + 1, pFileInfo);
|
||||
iLastNum++;
|
||||
}
|
||||
else
|
||||
{
|
||||
iLastNum = iNum;
|
||||
}
|
||||
pLastFileInfo = pFileInfo;
|
||||
}
|
||||
iNum++;
|
||||
}
|
||||
}
|
||||
|
||||
void QueueEditor::PauseParsInGroups(ItemList* pItemList, bool bExtraParsOnly)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
FileList GroupFileList;
|
||||
GroupFileList.clear();
|
||||
FileInfo* pFirstFileInfo = NULL;
|
||||
|
||||
for (ItemList::iterator it = pItemList->begin(); it != pItemList->end(); )
|
||||
{
|
||||
EditItem* pItem = *it;
|
||||
if (!pFirstFileInfo ||
|
||||
!strcmp(pFirstFileInfo->GetNZBFilename(), pItem->m_pFileInfo->GetNZBFilename()))
|
||||
{
|
||||
GroupFileList.push_back(pItem->m_pFileInfo);
|
||||
if (!pFirstFileInfo)
|
||||
{
|
||||
pFirstFileInfo = pItem->m_pFileInfo;
|
||||
}
|
||||
delete pItem;
|
||||
pItemList->erase(it);
|
||||
it = pItemList->begin();
|
||||
continue;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
if (!GroupFileList.empty())
|
||||
{
|
||||
PausePars(&GroupFileList, bExtraParsOnly);
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If the parameter "bExtraParsOnly" is set to "false", then we pause all par2-files.
|
||||
* If the parameter "bExtraParsOnly" is set to "true", we use the following strategy:
|
||||
* At first we find all par-files, which do not have "vol" in their names, then we pause
|
||||
* all vols and do not affect all just-pars.
|
||||
* In a case, if there are no just-pars, but only vols, we find the smallest vol-file
|
||||
* and do not affect it, but pause all other pars.
|
||||
*/
|
||||
void QueueEditor::PausePars(FileList* pFileList, bool bExtraParsOnly)
|
||||
{
|
||||
debug("QueueEditor: Pausing pars");
|
||||
|
||||
FileList Pars, Vols;
|
||||
Pars.clear();
|
||||
Vols.clear();
|
||||
|
||||
for (FileList::iterator it = pFileList->begin(); it != pFileList->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
char szLoFileName[1024];
|
||||
strncpy(szLoFileName, pFileInfo->GetFilename(), 1024);
|
||||
szLoFileName[1024-1] = '\0';
|
||||
for (char* p = szLoFileName; *p; p++) *p = tolower(*p); // convert string to lowercase
|
||||
|
||||
if (strstr(szLoFileName, ".par2"))
|
||||
{
|
||||
if (!bExtraParsOnly)
|
||||
{
|
||||
pFileInfo->SetPaused(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strstr(szLoFileName, ".vol"))
|
||||
{
|
||||
Vols.push_back(pFileInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
Pars.push_back(pFileInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bExtraParsOnly)
|
||||
{
|
||||
if (!Pars.empty())
|
||||
{
|
||||
for (FileList::iterator it = Vols.begin(); it != Vols.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
pFileInfo->SetPaused(true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// pausing all Vol-files except the smallest one
|
||||
FileInfo* pSmallest = NULL;
|
||||
for (FileList::iterator it = Vols.begin(); it != Vols.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (!pSmallest)
|
||||
{
|
||||
pSmallest = pFileInfo;
|
||||
}
|
||||
else if (pSmallest->GetSize() > pFileInfo->GetSize())
|
||||
{
|
||||
pSmallest->SetPaused(true);
|
||||
pSmallest = pFileInfo;
|
||||
}
|
||||
else
|
||||
{
|
||||
pFileInfo->SetPaused(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,98 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef QUEUEEDITOR_H
|
||||
#define QUEUEEDITOR_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
class QueueEditor
|
||||
{
|
||||
public:
|
||||
typedef std::vector<int> IDList;
|
||||
|
||||
enum EEditAction
|
||||
{
|
||||
eaFileMoveOffset = 1, // move to m_iOffset relative to the current position in queue
|
||||
eaFileMoveTop,
|
||||
eaFileMoveBottom,
|
||||
eaFilePause,
|
||||
eaFileResume,
|
||||
eaFileDelete,
|
||||
eaFilePauseAllPars,
|
||||
eaFilePauseExtraPars,
|
||||
eaGroupMoveOffset, // move to m_iOffset relative to the current position in queue
|
||||
eaGroupMoveTop,
|
||||
eaGroupMoveBottom,
|
||||
eaGroupPause,
|
||||
eaGroupResume,
|
||||
eaGroupDelete,
|
||||
eaGroupPauseAllPars,
|
||||
eaGroupPauseExtraPars
|
||||
};
|
||||
|
||||
private:
|
||||
class EditItem
|
||||
{
|
||||
public:
|
||||
int m_iOffset;
|
||||
FileInfo* m_pFileInfo;
|
||||
|
||||
EditItem(FileInfo* pFileInfo, int iOffset);
|
||||
};
|
||||
|
||||
typedef std::vector<EditItem*> ItemList;
|
||||
typedef std::vector<FileInfo*> FileList;
|
||||
|
||||
private:
|
||||
FileInfo* FindFileInfo(DownloadQueue* pDownloadQueue, int iID);
|
||||
int FindFileInfoEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
|
||||
bool InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset);
|
||||
void PrepareList(DownloadQueue* pDownloadQueue, ItemList* pItemList, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset);
|
||||
bool EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, EEditAction eAction, int iOffset);
|
||||
void BuildGroupList(DownloadQueue* pDownloadQueue, FileList* pGroupList);
|
||||
void AlignAffectedGroups(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, int iOffset);
|
||||
bool ItemExists(FileList* pFileList, FileInfo* pFileInfo);
|
||||
void AlignGroup(DownloadQueue* pDownloadQueue, FileInfo* pFirstFileInfo);
|
||||
void PauseParsInGroups(ItemList* pItemList, bool bExtraParsOnly);
|
||||
void PausePars(FileList* pFileList, bool bExtraParsOnly);
|
||||
|
||||
void PauseUnpauseEntry(FileInfo* pFileInfo, bool bPause);
|
||||
void DeleteEntry(FileInfo* pFileInfo);
|
||||
void MoveEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, int iOffset);
|
||||
|
||||
public:
|
||||
QueueEditor();
|
||||
~QueueEditor();
|
||||
|
||||
bool EditEntry(int ID, bool bSmartOrder, EEditAction eAction, int iOffset);
|
||||
bool EditList(IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset);
|
||||
|
||||
bool LockedEditEntry(DownloadQueue* pDownloadQueue, int ID, bool bSmartOrder, EEditAction eAction, int iOffset);
|
||||
};
|
||||
|
||||
#endif
|
||||
371
README
371
README
@@ -2,6 +2,10 @@
|
||||
NZBGet ReadMe
|
||||
=====================================
|
||||
|
||||
This is a short documentation. For more information please
|
||||
visit NZBGet home page at
|
||||
http://nzbget.net
|
||||
|
||||
Contents
|
||||
--------
|
||||
1. About NZBGet
|
||||
@@ -29,6 +33,9 @@ In server/client mode NZBGet runs as server in background.
|
||||
Then you use client to send requests to server. The sample requests
|
||||
are: download nzb-file, list files in queue, etc.
|
||||
|
||||
There is also a built-in web-interface. The server has RPC-support
|
||||
and can be controlled from third party applications too.
|
||||
|
||||
Standalone-tool, server and client are all contained in only one
|
||||
executable file "nzbget". The mode in which the program works
|
||||
depends on command-line parameters passed to the program.
|
||||
@@ -37,29 +44,16 @@ depends on command-line parameters passed to the program.
|
||||
2. Supported OS
|
||||
=====================================
|
||||
|
||||
NZBGet is written in C++ and was initialy developed on Linux.
|
||||
It was ported to Windows later and tested for compatibility with
|
||||
several POSIX-OS'es.
|
||||
|
||||
The current version (0.3.1) should run at least on:
|
||||
- Linux Debian 4.0 on x86;
|
||||
- Linux BusyBox with uClibc on MIPSEL;
|
||||
- PC-BSD 1.4 (based on FreeBSD 6.2) on x86;
|
||||
- Windows XP SP2 on x86.
|
||||
|
||||
The previous version (0.3.0) was also tested on:
|
||||
- Linux Debian 3.1 on x86;
|
||||
- Solaris 10 on x86;
|
||||
- Linux Debian 3.1 on SPARC (QEmu).
|
||||
NZBGet is written in C++ and works on Windows, OS X, Linux and
|
||||
most POSIX-conform OS'es.
|
||||
|
||||
Clients and servers running on different OS'es may communicate with
|
||||
each other. For example, you can use NZBGet as client on Windows to
|
||||
control your NZBGet-server running on Linux.
|
||||
|
||||
The download-section of NZBGet web-site provides binary files
|
||||
for Windows. The binary packages for many routers and NAS devices are
|
||||
also available in OPTWARE repository (http://www.nslu2-linux.org),
|
||||
but for most POSIX-systems you need to compile the program yourself.
|
||||
for Windows, OS X and Linux. For most POSIX-systems you need to compile
|
||||
the program yourself.
|
||||
|
||||
If you have downloaded binaries you can just jump to section
|
||||
"Configuration".
|
||||
@@ -68,8 +62,8 @@ If you have downloaded binaries you can just jump to section
|
||||
3. Prerequisites on POSIX
|
||||
=====================================
|
||||
|
||||
NZBGet is developed on a linux-system, but it should run on other
|
||||
POSIX platforms (see the list of tested platforms above).
|
||||
NZBGet is developed on a linux-system, but it runs on other
|
||||
POSIX platforms.
|
||||
|
||||
NZBGet absolutely needs the following libraries:
|
||||
|
||||
@@ -82,140 +76,132 @@ And the following libraries are optional:
|
||||
- libcurses (usually part of commercial systems)
|
||||
or (better)
|
||||
- libncurses (http://invisible-island.net/ncurses)
|
||||
|
||||
- for par-check and -repair (enabled by default):
|
||||
- libpar2 (http://parchive.sourceforge.net)
|
||||
- libsigc++ (http://libsigc.sourceforge.net)
|
||||
|
||||
- for encrypted connections (TLS/SSL):
|
||||
- OpenSSL (http://www.openssl.org)
|
||||
or
|
||||
- GnuTLS (http://www.gnu.org/software/gnutls)
|
||||
|
||||
- for support of encoding-formats other than yEnc (disabled by default):
|
||||
- libuu (http://www.fpx.de/fp/Software/UUDeview)
|
||||
- for gzip support in web-server and web-client (enabled by default):
|
||||
- zlib (http://www.zlib.net)
|
||||
|
||||
All these libraries are included in modern Linux distributions and
|
||||
All these libraries are included in modern POSIX distributions and
|
||||
should be available as installable packages. Please note that you also
|
||||
need the developer packages for these libraries too, they package names
|
||||
have often suffix "dev" or "devel". On other systems you may need to
|
||||
download the libraries at the given URLs and compile them (see hints below).
|
||||
|
||||
|
||||
=====================================
|
||||
4. Installation on POSIX
|
||||
=====================================
|
||||
|
||||
Well, the usual stuff:
|
||||
Installation from the source distribution archive (nzbget-VERSION.tar.gz):
|
||||
|
||||
- untar the nzbget-source via
|
||||
tar -zxf nzbget-VERSION.tar.gz
|
||||
|
||||
- change into nzbget-directory via
|
||||
cd nzbget-VERSION
|
||||
|
||||
- configure it via
|
||||
./configure
|
||||
|
||||
(maybe you have to tell configure, where to find some libraries.
|
||||
./configure --help is your friend! ;-)
|
||||
also see "Configure-options" later.)
|
||||
./configure --help is your friend!
|
||||
also see "Configure-options" later)
|
||||
|
||||
- in a case you don't have root access or want to install the program
|
||||
in your home directory use the configure parameter --prefix, e. g.:
|
||||
|
||||
./configure --prefix ~/usr
|
||||
|
||||
- compile it via
|
||||
make
|
||||
(you may get some warnings concerning 'mktemp', simply ignore them!)
|
||||
- become root via
|
||||
|
||||
- to install system wide become root via:
|
||||
su
|
||||
- install it via
|
||||
|
||||
- install it via:
|
||||
make install
|
||||
|
||||
- install configuration files into <prefix>/etc via:
|
||||
make install-conf
|
||||
|
||||
(you can skip this step if you intend to store configuration
|
||||
files in a non-standard location)
|
||||
|
||||
Configure-options
|
||||
-----------------
|
||||
You may run configure with additional arguments:
|
||||
|
||||
--enable-uulib - to make with uulib-library, in a case you want it
|
||||
(see later section "Optional package: uulib"). This option is not
|
||||
enabled by default.
|
||||
|
||||
--disable-curses - to make without curses-support. Use this option
|
||||
if you can not use curses/ncurses.
|
||||
|
||||
--disable-parcheck - to make without parcheck-support. Use this option
|
||||
if you can not use libpar2 or libsigc++.
|
||||
if you have troubles when compiling par2-module.
|
||||
|
||||
--with-tlslib=(OpenSSL, GnuTLS) - to select which TLS/SSL library
|
||||
should be used for encrypted server connections.
|
||||
|
||||
--disable-tls - to make without TLS/SSL support. Use this option if
|
||||
you can not neither OpenSSL nor GnuTLS.
|
||||
|
||||
--disable-gzip - to make without gzip support. Use this option
|
||||
if you can not use zlib.
|
||||
|
||||
--enable-debug - to build in debug-mode, if you want to see and log
|
||||
debug-messages.
|
||||
|
||||
Optional package: uulib
|
||||
-----------------------
|
||||
uulib is not required to compile and run nzbget, because nzbget includes
|
||||
internal decoder for yEnc-format. However, uulib supports many other formats,
|
||||
you may possibly want to have support for. In this case you can build the
|
||||
program with uulib-support enabled.
|
||||
|
||||
NOTE: enabling uulib does not disable internal decoder. The program built with
|
||||
uulib-support can use both decoders (internal and uulib), depending on option
|
||||
"decoder" in program's configuration file.
|
||||
|
||||
To build with uulib use option "--enable-uulib" while running configure:
|
||||
|
||||
./configure --enable-uulib
|
||||
|
||||
The uulib must be installed on your system. On most linux distributions
|
||||
the package uulib-dev is available. So you only need to install this package
|
||||
and run configure with parameter "--enable-uulib".
|
||||
If you do not have this package you can compile uulib yourself:
|
||||
|
||||
- download source code of uudeview from
|
||||
http://www.fpx.de/fp/Software/UUDeview;
|
||||
- build uudeview as usually:
|
||||
/.confugure
|
||||
make
|
||||
- start nzbget's configure-script with following parameters:
|
||||
./configure --enable-uulib \
|
||||
--with-uulib-includes=<path to uudeview>/uulib \
|
||||
--with-uulib-libraries=<path to uudeview>/uulib
|
||||
for example:
|
||||
./configure --enable-uulib \
|
||||
--with-uulib-includes=/home/user/uudeview-0.5.20/uulib \
|
||||
--with-uulib-libraries=/home/user/uudeview-0.5.20/uulib
|
||||
- now you can compile nzbget.
|
||||
|
||||
NOTE: after nzbget is compiled, the code of uulib-library is built into
|
||||
nzbget's executable. You do not need to have uulib on target system
|
||||
to run nzbget.
|
||||
|
||||
|
||||
Optional package: par-check
|
||||
---------------------------
|
||||
NZBGet can check and repair downloaded files for you. For this purpose
|
||||
it uses library par2 (libpar2), which needs sigc++ on its part.
|
||||
it uses library par2.
|
||||
|
||||
To build with par-check use option "--enable-parcheck" while running
|
||||
configure:
|
||||
|
||||
./configure --enable-parcheck
|
||||
For your convenience the source code of libpar2 is integrated into
|
||||
NZBGet’s source tree and is compiled automatically when you make NZBGet.
|
||||
|
||||
The libpar2 and libsigc++ (version 2 or later) must be installed on your
|
||||
system. On most linux distributions these libraries are available as packages.
|
||||
So you only need to install theme and run configure with parameter
|
||||
"--enable-parcheck".
|
||||
If you do not have these package you can compile them yourself. Please
|
||||
refer to section "Optional package: uulib" for an example on how to
|
||||
compile additional library. Following configure-parameters may be usefull:
|
||||
In a case errors occur during this process the inclusion of par2-module
|
||||
can be disabled using configure option "--disable-parcheck":
|
||||
|
||||
--with-libpar2-includes
|
||||
--with-libpar2-libraries
|
||||
--with-libsigc-includes
|
||||
--with-libsigc-libraries
|
||||
|
||||
The library libsigc++ must be installed first, since libpar2 requires it.
|
||||
./configure --disable-parcheck
|
||||
|
||||
Optional package: curses
|
||||
-------------------------
|
||||
For curses-outputmode you need ncurses or curses on your system.
|
||||
If you do not have one of them you can download and compile ncurses yourself.
|
||||
Please refer to section "Optional package: uulib" for an example on how to
|
||||
compile additional library. Following configure-parameters may be usefull:
|
||||
Following configure-parameters may be useful:
|
||||
|
||||
--with-libcurses-includes
|
||||
--with-libcurses-libraries
|
||||
--with-libcurses-includes=/path/to/curses/includes
|
||||
--with-libcurses-libraries=/path/to/curses/libraries
|
||||
|
||||
If you are not able to use curses or ncurses or do not want them you can
|
||||
make the program without support for curses using option "--disable-curses":
|
||||
|
||||
./configure --disable-curses
|
||||
|
||||
Optional package: TLS
|
||||
-------------------------
|
||||
To enable encrypted server connections (TLS/SSL) you need to build the program
|
||||
with TLS/SSL support. NZBGet can use two libraries: OpenSSL or GnuTLS.
|
||||
Configure-script checks which library is installed and use it. If both are
|
||||
available it gives the precedence to OpenSSL. You may override that with
|
||||
the option --with-tlslib=(OpenSSL, GnuTLS). For example to build with GnuTLS:
|
||||
|
||||
./configure --with-tlslib= GnuTLS
|
||||
|
||||
Following configure-parameters may be useful:
|
||||
|
||||
--with-libtls-includess=/path/to/gnutls/includes
|
||||
--with-libtls-libraries=/path/to/gnutls/libraries
|
||||
|
||||
--with-openssl-includess=/path/to/openssl/includes
|
||||
--with-openssl-libraries=/path/to/openssl/libraries
|
||||
|
||||
If none of these libraries is available you can make the program without
|
||||
TLS/SSL support using option "--disable-tls":
|
||||
|
||||
./configure --disable-tls
|
||||
|
||||
=====================================
|
||||
5. Compiling on Windows
|
||||
=====================================
|
||||
@@ -224,36 +210,41 @@ NZBGet is developed using MS Visual C++ 2005. The project file and solution
|
||||
are provided. If you use MS Visual C++ 2005 Express you need to download
|
||||
and install Platform SDK.
|
||||
|
||||
To compile the program with par-check-support you also need the following
|
||||
libraries:
|
||||
To compile the program with TLS/SSL support you need either OpenSSL or GnuTLS:
|
||||
- OpenSSL (http://www.openssl.org)
|
||||
or
|
||||
- GnuTLS (http://www.gnu.org/software/gnutls)
|
||||
|
||||
- libsigc++ (http://libsigc.sourceforge.net)
|
||||
- libpar2 (http://parchive.sourceforge.net)
|
||||
|
||||
Download these libaries, then use patch-files provided with NZBGet to create
|
||||
preconfigured project files and solutions for each library.
|
||||
Look at http://gnuwin32.sourceforge.net/packages/patch.htm for info on how
|
||||
to use patch-files, if you do not familiar with this technique.
|
||||
|
||||
After libsigc++ and libpar2 are compiled in static libraries (.lib)
|
||||
and include- and libraries-paths are configured in MS Visual C++ 2005 you
|
||||
should be able to compile NZBGet.
|
||||
Also required are:
|
||||
- Regex (http://gnuwin32.sourceforge.net/packages/regex.htm)
|
||||
- Zlib (http://gnuwin32.sourceforge.net/packages/zlib.htm)
|
||||
|
||||
=====================================
|
||||
6. Configuration
|
||||
=====================================
|
||||
|
||||
NZBGet needs a configuration-file to work properly.
|
||||
NZBGet needs a configuration file.
|
||||
|
||||
You need to set at least the option "MAINDIR" and one newsserver in
|
||||
configuration file. Have a look at the example in nzbget.conf.example,
|
||||
it has comments on how to use each option.
|
||||
An example configuration file is provided in "nzbget.conf", which
|
||||
is installed into "<prefix>/share/nzbget" (where <prefix> depends on
|
||||
system configuration and configure options - typically "/usr/local",
|
||||
"/usr" or "/opt"). The installer adjusts the file according to your
|
||||
system paths. If you have performed the installation step
|
||||
"make install-conf" this file is already copied to "<prefix>/etc" and
|
||||
NZBGet finds it automatically. If you install the program manually
|
||||
from a binary archive you have to copy the file from "<prefix>/share/nzbget"
|
||||
to one of the locations listed below.
|
||||
|
||||
Open the file in a text editor and modify it accodring to your needs.
|
||||
|
||||
You need to set at least the option "MAINDIR" and one news server in
|
||||
configuration file. The file has comments on how to use each option.
|
||||
|
||||
The program looks for configuration file in following standard
|
||||
locations (in this order):
|
||||
|
||||
On POSIX systems:
|
||||
|
||||
<EXE-DIR>/nzbget.conf
|
||||
~/.nzbget
|
||||
/etc/nzbget.conf
|
||||
/usr/etc/nzbget.conf
|
||||
@@ -277,6 +268,12 @@ options via command-line.
|
||||
NZBGet can be used in either standalone mode which downloads a single file
|
||||
or as a server which is able to queue up numerous download requests.
|
||||
|
||||
TIP for Windows users: NZBGet is controlled via various command line
|
||||
parameters. For easier using there is a simple shell script included
|
||||
in "nzbget-shell.bat". Start this script from Windows Explorer and you will
|
||||
be running a command shell with PATH adjusted to find NZBGet executable.
|
||||
Then you can type all commands without full path to nzbget.exe.
|
||||
|
||||
Standalone mode:
|
||||
----------------
|
||||
|
||||
@@ -304,15 +301,20 @@ To stop server use:
|
||||
|
||||
nzbget -Q
|
||||
|
||||
Depending on which frontend has been selected in the nzbget.conf file
|
||||
(option "outputmode") the server should display a message that
|
||||
it is ready to receive download requests (this applies only to console
|
||||
mode, not to daemon mode).
|
||||
TIP for POSIX users: with included script "nzbgetd" you can use standard
|
||||
commands to control daemon:
|
||||
|
||||
nzbgetd start
|
||||
nzbgetd stop
|
||||
etc.
|
||||
|
||||
When NZBGet is started in console server mode it displays a message that
|
||||
it is ready to receive download requests. In daemon mode it doesn't print any
|
||||
messages to console since it runs in background.
|
||||
|
||||
When the server is running it is possible to queue up downloads. This can be
|
||||
done either in terminal with "nzbget -A <nzb-file>" or by uploading
|
||||
a nzb-file into server's monitor-directory (<MAINDIR>/nzb by default, the
|
||||
directory must exist on server's start; otherwise it will not be monitored).
|
||||
a nzb-file into server's monitor-directory (<MAINDIR>/nzb by default).
|
||||
|
||||
To check the status of server start client and connect it to server:
|
||||
|
||||
@@ -335,9 +337,18 @@ It prints something like:
|
||||
|
||||
[1] nzbname\filename1.rar (50.00 MB)
|
||||
[2] nzbname\filename1.r01 (50.00 MB)
|
||||
[3] another-nzb\filename3.r01 (100.00 MB)
|
||||
[4] another-nzb\filename3.r02 (100.00 MB)
|
||||
|
||||
The numbers in square braces are ID's of files in queue. They can be used
|
||||
in edit-command. For example to move file with ID 2 to the top of queue:
|
||||
This is the list of individual files listed within nzb-file. To print
|
||||
the list of nzb-files (without content) add G-modifier to the list command:
|
||||
|
||||
[1] nzbname (4.56 GB)
|
||||
[2] another-nzb (4.20 GB)
|
||||
|
||||
The numbers in square braces are ID's of files or groups in queue.
|
||||
They can be used in edit-command. For example to move file with
|
||||
ID 2 to the top of queue:
|
||||
|
||||
nzbget -E T 2
|
||||
|
||||
@@ -350,8 +361,8 @@ or to delete files from queue:
|
||||
nzbget -E D 3 10-15 20-21 16
|
||||
|
||||
The edit-command has also a group-mode which affects all files from the
|
||||
same nzb-request. You need to pass one ID of any file in the group. For
|
||||
example to delete all files from the first nzb-request:
|
||||
same nzb-file. You need to pass an ID of the group. For example to delete
|
||||
the whole group 1:
|
||||
|
||||
nzbget -E G D 1
|
||||
|
||||
@@ -370,7 +381,7 @@ Running client & server on seperate machines:
|
||||
Since nzbget communicates via TCP/IP it's possible to have a server running on
|
||||
one computer and adding downloads via a client on another computer.
|
||||
|
||||
Do this by setting the "serverip" option in the nzbget.conf file to point to the
|
||||
Do this by setting the "ControlIP" option in the nzbget.conf file to point to the
|
||||
IP of the server (default is localhost which means client and server runs on
|
||||
same computer)
|
||||
|
||||
@@ -386,36 +397,112 @@ If you need to control server from WAN it is better to connect to server's
|
||||
terminal via SSH (POSIX) or remote desktop (Windows) and then run
|
||||
nzbget-client-commands in this terminal.
|
||||
|
||||
Post processing scripts
|
||||
-----------------------
|
||||
|
||||
After the download of nzb-file is completed nzbget can call post-processing
|
||||
scripts, defined in configuration file.
|
||||
|
||||
Example post-processing scripts are provided in directory "scripts".
|
||||
|
||||
To use the scripts copy them into your local directory and set options
|
||||
<ScriptDir>, <PostScript> and <ScriptOrder>.
|
||||
|
||||
For information on writing your own post-processing scripts please
|
||||
visit NZBGet web site.
|
||||
|
||||
Web-interface
|
||||
-------------
|
||||
|
||||
NZBGet has a built-in web-server providing the access to the program
|
||||
functions via web-interface.
|
||||
|
||||
To activate web-interface set the option "WebDir" to the path with
|
||||
web-interface files. If you install using "make install-conf" as
|
||||
described above the option is set automatically. If you install using
|
||||
binary files you should check if the option is set correctly.
|
||||
|
||||
To access web-interface from your web-browser use the server address
|
||||
and port defined in NZBGet configuration file in options "ControlIP" and
|
||||
"ControlPort". For example:
|
||||
|
||||
http://localhost:6789/
|
||||
|
||||
For login credentials type username and the password defined by
|
||||
options "ControlUsername" (default "nzbget") and "ControlPassword"
|
||||
(default "tegbzn6789").
|
||||
|
||||
In a case your browser forget credentials, to prevent typing them each
|
||||
time, there is a workaround - use URL in the form:
|
||||
|
||||
http://localhost:6789/username:password/
|
||||
|
||||
Please note, that in this case the password is saved in a bookmark or in
|
||||
browser history in plain text and is easy to find by persons having
|
||||
access to your computer.
|
||||
|
||||
=====================================
|
||||
8. Authors
|
||||
=====================================
|
||||
|
||||
NZBGet was initialiy written by Sven Henkel (sidddy@users.sourceforge.net).
|
||||
Up to version 0.2.3 it was been developed and maintained by Bo Cordes Petersen
|
||||
(placebodk@users.sourceforge.net). Beginning at version 0.3.0 the program is
|
||||
being developed by Andrei Prygounkov (hugbug@users.sourceforge.net).
|
||||
NZBGet is developed and maintained by Andrey Prygunkov
|
||||
(hugbug@users.sourceforge.net).
|
||||
|
||||
The original project was initially created by Sven Henkel
|
||||
(sidddy@users.sourceforge.net) in 2004 and later developed by
|
||||
Bo Cordes Petersen (placebodk@users.sourceforge.net) until 2005.
|
||||
In 2007 the abandoned project was overtaken by Andrey Prygunkov.
|
||||
Since then the program has been completely rewritten.
|
||||
|
||||
NZBGet distribution archive includes additional components
|
||||
written by other authors:
|
||||
|
||||
Par2:
|
||||
Peter Brian Clements <peterbclements@users.sourceforge.net>
|
||||
|
||||
Par2 library API:
|
||||
Francois Lesueur <flesueur@users.sourceforge.net>
|
||||
|
||||
Catch:
|
||||
Two Blue Cubes Ltd <https://github.com/philsquared/Catch>
|
||||
|
||||
jQuery:
|
||||
John Resig <http://jquery.com>
|
||||
The Dojo Foundation <http://sizzlejs.com>
|
||||
|
||||
Bootstrap:
|
||||
Twitter, Inc <http://twitter.github.com/bootstrap>
|
||||
|
||||
Raphaël:
|
||||
Dmitry Baranovskiy <http://raphaeljs.com>
|
||||
Sencha Labs <http://sencha.com>
|
||||
|
||||
Elycharts:
|
||||
Void Labs s.n.c. <http://void.it>
|
||||
|
||||
iconSweets:
|
||||
Yummygum <http://yummygum.com>
|
||||
|
||||
=====================================
|
||||
9. Copyright
|
||||
=====================================
|
||||
|
||||
NZBGet is distributed under GNU General Pubic License Version 2.
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
The complete content of license is provided in file COPYING.
|
||||
|
||||
Binary distribution for Windows contains code from the following libraries:
|
||||
|
||||
- libpar2 (http://parchive.sourceforge.net)
|
||||
- libsigc++ (http://libsigc.sourceforge.net)
|
||||
|
||||
libpar2 is distributed under GPL and libsigc++ under LGPL.
|
||||
Additional exemption: compiling, linking, and/or using OpenSSL is allowed.
|
||||
|
||||
=====================================
|
||||
10. Contact
|
||||
=====================================
|
||||
|
||||
If you encounter any problem, feel free to use tracker/forums on
|
||||
If you encounter any problem, feel free to use the forum
|
||||
|
||||
sourceforge.net/projects/nzbget
|
||||
nzbget.net/forum
|
||||
|
||||
or contact me at
|
||||
|
||||
|
||||
15
README.md
Normal file
15
README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# NZBGet #
|
||||
NZBGet is a binary downloader, which downloads files from Usenet
|
||||
based on information given in nzb-files.
|
||||
|
||||
NZBGet is written in C++ and is known for its extraordinary performance and efficiency.
|
||||
|
||||
NZBGet can be run at almost every platform - classic PCs, NAS, media players, SAT-receivers, WLAN-routers, etc.
|
||||
The download area provides precompiled binaries
|
||||
for Windows, Mac OS X and Linux (compatible with many CPUs and platform variants). For other platforms
|
||||
the program can be compiled from sources.
|
||||
|
||||
- [Home page (nzbget.net)](http://nzbget.net) - for first time visitors, learn more about NZBGet;
|
||||
- [Downloads](http://nzbget.net/download) - get the binaries and sources;
|
||||
- [Documentation](https://github.com/nzbget/nzbget/wiki) - installation manuals, HOW-TOs, API;
|
||||
- [Forum](http://forum.nzbget.net) - get support, share your ideas, scripts, add-ons.
|
||||
582
RemoteClient.cpp
582
RemoteClient.cpp
@@ -1,582 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "RemoteClient.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
RemoteClient::RemoteClient()
|
||||
{
|
||||
m_pConnection = NULL;
|
||||
m_pNetAddress = NULL;
|
||||
m_bVerbose = true;
|
||||
|
||||
/*
|
||||
printf("sizeof(SNZBRequestBase)=%i\n", sizeof(SNZBRequestBase));
|
||||
printf("sizeof(SNZBDownloadRequest)=%i\n", sizeof(SNZBDownloadRequest));
|
||||
printf("sizeof(SNZBListRequest)=%i\n", sizeof(SNZBListRequest));
|
||||
printf("sizeof(SNZBListResponse)=%i\n", sizeof(SNZBListResponse));
|
||||
printf("sizeof(SNZBListResponseEntry)=%i\n", sizeof(SNZBListResponseEntry));
|
||||
printf("sizeof(SNZBLogRequest)=%i\n", sizeof(SNZBLogRequest));
|
||||
printf("sizeof(SNZBLogResponse)=%i\n", sizeof(SNZBLogResponse));
|
||||
printf("sizeof(SNZBLogResponseEntry)=%i\n", sizeof(SNZBLogResponseEntry));
|
||||
printf("sizeof(SNZBPauseUnpauseRequest)=%i\n", sizeof(SNZBPauseUnpauseRequest));
|
||||
printf("sizeof(SNZBSetDownloadRateRequest)=%i\n", sizeof(SNZBSetDownloadRateRequest));
|
||||
printf("sizeof(SNZBEditQueueRequest)=%i\n", sizeof(SNZBEditQueueRequest));
|
||||
printf("sizeof(SNZBDumpDebugRequest)=%i\n", sizeof(SNZBDumpDebugRequest));
|
||||
*/
|
||||
}
|
||||
|
||||
RemoteClient::~RemoteClient()
|
||||
{
|
||||
if (m_pConnection)
|
||||
{
|
||||
delete m_pConnection;
|
||||
}
|
||||
|
||||
if (m_pNetAddress)
|
||||
{
|
||||
delete m_pNetAddress;
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteClient::printf(char * msg,...)
|
||||
{
|
||||
if (m_bVerbose)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, msg);
|
||||
::vprintf(msg, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteClient::perror(char * msg)
|
||||
{
|
||||
if (m_bVerbose)
|
||||
{
|
||||
::perror(msg);
|
||||
}
|
||||
}
|
||||
|
||||
bool RemoteClient::InitConnection()
|
||||
{
|
||||
// Create a connection to the server
|
||||
m_pNetAddress = new NetAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
m_pConnection = new Connection(m_pNetAddress);
|
||||
|
||||
bool OK = m_pConnection->Connect() >= 0;
|
||||
if (!OK)
|
||||
{
|
||||
printf("Unable to send request to nzbserver at %s (port %i)\n", g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
void RemoteClient::InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize)
|
||||
{
|
||||
pMessageBase->m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
|
||||
pMessageBase->m_iType = htonl(iRequest);
|
||||
pMessageBase->m_iStructSize = htonl(iSize);
|
||||
strncpy(pMessageBase->m_szPassword, g_pOptions->GetServerPassword(), NZBREQUESTPASSWORDSIZE - 1);
|
||||
pMessageBase->m_szPassword[NZBREQUESTPASSWORDSIZE - 1] = '\0';
|
||||
}
|
||||
|
||||
bool RemoteClient::ReceiveBoolResponse()
|
||||
{
|
||||
printf("request sent\n");
|
||||
|
||||
// all bool-responses have the same format of structure, we use SNZBDownloadResponse here
|
||||
SNZBDownloadResponse BoolResponse;
|
||||
memset(&BoolResponse, 0, sizeof(BoolResponse));
|
||||
|
||||
int iResponseLen = m_pConnection->Recv((char*)&BoolResponse, sizeof(BoolResponse));
|
||||
if (iResponseLen != sizeof(BoolResponse) ||
|
||||
(int)ntohl(BoolResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
|
||||
ntohl(BoolResponse.m_MessageBase.m_iStructSize) != sizeof(BoolResponse))
|
||||
{
|
||||
printf("invaid response received: either not nzbget-server or wrong server version\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
int iTextLen = ntohl(BoolResponse.m_iTrailingDataLength);
|
||||
char* buf = (char*)malloc(iTextLen);
|
||||
iResponseLen = m_pConnection->Recv(buf, iTextLen);
|
||||
if (iResponseLen != iTextLen)
|
||||
{
|
||||
printf("invaid response received: either not nzbget-server or wrong server version\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("server returned: %s\n", buf);
|
||||
free(buf);
|
||||
return ntohl(BoolResponse.m_bSuccess);
|
||||
}
|
||||
|
||||
/*
|
||||
* Sends a message to the running nzbget process.
|
||||
*/
|
||||
bool RemoteClient::RequestServerDownload(const char* szName, bool bAddFirst)
|
||||
{
|
||||
// Read the file into the buffer
|
||||
char* szBuffer = NULL;
|
||||
int iLength = 0;
|
||||
if (!LoadFileIntoBuffer(szName, &szBuffer, &iLength))
|
||||
{
|
||||
printf("Could not load file %s\n", szName);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OK = InitConnection();
|
||||
if (OK)
|
||||
{
|
||||
SNZBDownloadRequest DownloadRequest;
|
||||
InitMessageBase(&DownloadRequest.m_MessageBase, eRemoteRequestDownload, sizeof(DownloadRequest));
|
||||
DownloadRequest.m_bAddFirst = htonl(bAddFirst);
|
||||
DownloadRequest.m_iTrailingDataLength = htonl(iLength);
|
||||
|
||||
strncpy(DownloadRequest.m_szFilename, szName, NZBREQUESTFILENAMESIZE - 1);
|
||||
DownloadRequest.m_szFilename[NZBREQUESTFILENAMESIZE-1] = '\0';
|
||||
|
||||
if (m_pConnection->Send((char*)(&DownloadRequest), sizeof(DownloadRequest)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
OK = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pConnection->Send(szBuffer, iLength);
|
||||
OK = ReceiveBoolResponse();
|
||||
m_pConnection->Disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
if (szBuffer)
|
||||
{
|
||||
free(szBuffer);
|
||||
}
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerList()
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBListRequest ListRequest;
|
||||
InitMessageBase(&ListRequest.m_MessageBase, eRemoteRequestList, sizeof(ListRequest));
|
||||
ListRequest.m_bFileList = htonl(true);
|
||||
ListRequest.m_bServerState = htonl(true);
|
||||
|
||||
if (m_pConnection->Send((char*)(&ListRequest), sizeof(ListRequest)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now listen for the returned list
|
||||
SNZBListResponse ListResponse;
|
||||
int iResponseLen = m_pConnection->Recv((char*) &ListResponse, sizeof(ListResponse));
|
||||
if (iResponseLen != sizeof(ListResponse) ||
|
||||
(int)ntohl(ListResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
|
||||
ntohl(ListResponse.m_MessageBase.m_iStructSize) != sizeof(ListResponse))
|
||||
{
|
||||
printf("invaid response received: either not nzbget-server or wrong server version\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
char* pBuf = NULL;
|
||||
if (ntohl(ListResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
pBuf = (char*)malloc(ntohl(ListResponse.m_iTrailingDataLength));
|
||||
if (!m_pConnection->RecvAll(pBuf, ntohl(ListResponse.m_iTrailingDataLength)))
|
||||
{
|
||||
free(pBuf);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_pConnection->Disconnect();
|
||||
|
||||
if (ntohl(ListResponse.m_iTrailingDataLength) == 0)
|
||||
{
|
||||
printf("Server has no files queued for download\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Queue List\n");
|
||||
printf("-----------------------------------\n");
|
||||
|
||||
long long lRemaining = 0;
|
||||
long long lPaused = 0;
|
||||
char* pBufPtr = (char*)pBuf;
|
||||
for (unsigned int i = 0; i < ntohl(ListResponse.m_iNrTrailingEntries); i++)
|
||||
{
|
||||
SNZBListResponseEntry* pListAnswer = (SNZBListResponseEntry*) pBufPtr;
|
||||
|
||||
long long lFileSize = JoinInt64(ntohl(pListAnswer->m_iFileSizeHi), ntohl(pListAnswer->m_iFileSizeLo));
|
||||
long long lRemainingSize = JoinInt64(ntohl(pListAnswer->m_iRemainingSizeHi), ntohl(pListAnswer->m_iRemainingSizeLo));
|
||||
|
||||
char szCompleted[100];
|
||||
szCompleted[0] = '\0';
|
||||
if (lRemainingSize < lFileSize)
|
||||
{
|
||||
sprintf(szCompleted, ", %i%s", (int)(100 - lRemainingSize * 100.0 / lFileSize), "%");
|
||||
}
|
||||
char szStatus[100];
|
||||
if (ntohl(pListAnswer->m_bPaused))
|
||||
{
|
||||
sprintf(szStatus, " (paused)");
|
||||
lPaused += lRemainingSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
szStatus[0] = '\0';
|
||||
lRemaining += lRemainingSize;
|
||||
}
|
||||
char* szNZBFilename = pBufPtr + sizeof(SNZBListResponseEntry);
|
||||
char* szFilename = pBufPtr + sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) + ntohl(pListAnswer->m_iSubjectLen);
|
||||
|
||||
char szNZBNiceName[1024];
|
||||
FileInfo::MakeNiceNZBName(szNZBFilename, szNZBNiceName, 1024);
|
||||
|
||||
printf("[%i] %s%c%s (%.2f MB%s)%s\n", ntohl(pListAnswer->m_iID), szNZBNiceName, (int)PATH_SEPARATOR, szFilename, lFileSize / 1024.0 / 1024.0, szCompleted, szStatus);
|
||||
|
||||
pBufPtr += sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) +
|
||||
ntohl(pListAnswer->m_iSubjectLen) + ntohl(pListAnswer->m_iFilenameLen) + ntohl(pListAnswer->m_iDestDirLen);
|
||||
}
|
||||
|
||||
printf("-----------------------------------\n");
|
||||
printf("Files: %i\n", ntohl(ListResponse.m_iNrTrailingEntries));
|
||||
if (lPaused > 0)
|
||||
{
|
||||
printf("Remaining size: %.2f MB (+%.2f MB paused)\n", lRemaining / 1024.0 / 1024.0, lPaused / 1024.0 / 1024.0);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Remaining size: %.2f MB\n", lRemaining / 1024.0 / 1024.0);
|
||||
}
|
||||
printf("Current download rate: %.1f KB/s\n", (float)(ntohl(ListResponse.m_iDownloadRate) / 1024.0));
|
||||
|
||||
free(pBuf);
|
||||
}
|
||||
|
||||
long long iAllBytes = JoinInt64(ntohl(ListResponse.m_iDownloadedBytesHi), ntohl(ListResponse.m_iDownloadedBytesLo));
|
||||
float fAverageSpeed = ntohl(ListResponse.m_iDownloadTimeSec) > 0 ? iAllBytes / ntohl(ListResponse.m_iDownloadTimeSec) : 0;
|
||||
printf("Session download rate: %.1f KB/s\n", (float)(fAverageSpeed / 1024.0));
|
||||
|
||||
if (ntohl(ListResponse.m_iDownloadLimit) > 0)
|
||||
{
|
||||
printf("Speed limit: %.1f KB/s\n", (float)(ntohl(ListResponse.m_iDownloadLimit) / 1024.0));
|
||||
}
|
||||
|
||||
int sec = ntohl(ListResponse.m_iUpTimeSec);
|
||||
int h = sec / 3600;
|
||||
int m = (sec % 3600) / 60;
|
||||
int s = sec % 60;
|
||||
printf("Up time: %.2d:%.2d:%.2d\n", h, m, s);
|
||||
|
||||
sec = ntohl(ListResponse.m_iDownloadTimeSec);
|
||||
h = sec / 3600;
|
||||
m = (sec % 3600) / 60;
|
||||
s = sec % 60;
|
||||
printf("Download time: %.2d:%.2d:%.2d\n", h, m, s);
|
||||
|
||||
printf("Downloaded: %.2f MB\n", iAllBytes / 1024.0 / 1024.0);
|
||||
|
||||
printf("Threads running: %i\n", ntohl(ListResponse.m_iThreadCount));
|
||||
|
||||
if (ntohl(ListResponse.m_iParJobCount) > 0)
|
||||
{
|
||||
printf("Par-jobs: %i\n", (int)ntohl(ListResponse.m_iParJobCount));
|
||||
}
|
||||
|
||||
if (ntohl(ListResponse.m_bServerStandBy))
|
||||
{
|
||||
printf("Server state: Stand-By\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Server state: %s\n", ntohl(ListResponse.m_bServerPaused) ? "Paused" : "Downloading");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerLog(int iLines)
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBLogRequest LogRequest;
|
||||
InitMessageBase(&LogRequest.m_MessageBase, eRemoteRequestLog, sizeof(LogRequest));
|
||||
LogRequest.m_iLines = htonl(iLines);
|
||||
LogRequest.m_iIDFrom = 0;
|
||||
|
||||
if (m_pConnection->Send((char*)(&LogRequest), sizeof(LogRequest)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now listen for the returned log
|
||||
SNZBLogResponse LogResponse;
|
||||
int iResponseLen = m_pConnection->Recv((char*) &LogResponse, sizeof(LogResponse));
|
||||
if (iResponseLen != sizeof(LogResponse) ||
|
||||
(int)ntohl(LogResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
|
||||
ntohl(LogResponse.m_MessageBase.m_iStructSize) != sizeof(LogResponse))
|
||||
{
|
||||
printf("invaid response received: either not nzbget-server or wrong server version\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
char* pBuf = NULL;
|
||||
if (ntohl(LogResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
pBuf = (char*)malloc(ntohl(LogResponse.m_iTrailingDataLength));
|
||||
if (!m_pConnection->RecvAll(pBuf, ntohl(LogResponse.m_iTrailingDataLength)))
|
||||
{
|
||||
free(pBuf);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_pConnection->Disconnect();
|
||||
|
||||
if (LogResponse.m_iTrailingDataLength == 0)
|
||||
{
|
||||
printf("Log is empty\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Log (last %i entries)\n", ntohl(LogResponse.m_iNrTrailingEntries));
|
||||
printf("-----------------------------------\n");
|
||||
|
||||
char* pBufPtr = (char*)pBuf;
|
||||
for (unsigned int i = 0; i < ntohl(LogResponse.m_iNrTrailingEntries); i++)
|
||||
{
|
||||
SNZBLogResponseEntry* pLogAnswer = (SNZBLogResponseEntry*) pBufPtr;
|
||||
|
||||
char* szText = pBufPtr + sizeof(SNZBLogResponseEntry);
|
||||
switch (ntohl(pLogAnswer->m_iKind))
|
||||
{
|
||||
case Message::mkDebug:
|
||||
printf("[DEBUG] %s\n", szText);
|
||||
break;
|
||||
case Message::mkError:
|
||||
printf("[ERROR] %s\n", szText);
|
||||
break;
|
||||
case Message::mkWarning:
|
||||
printf("[WARNING] %s\n", szText);
|
||||
break;
|
||||
case Message::mkInfo:
|
||||
printf("[INFO] %s\n", szText);
|
||||
break;
|
||||
}
|
||||
|
||||
pBufPtr += sizeof(SNZBLogResponseEntry) + ntohl(pLogAnswer->m_iTextLen);
|
||||
}
|
||||
|
||||
printf("-----------------------------------\n");
|
||||
|
||||
free(pBuf);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerPauseUnpause(bool bPause)
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBPauseUnpauseRequest PauseUnpauseRequest;
|
||||
InitMessageBase(&PauseUnpauseRequest.m_MessageBase, eRemoteRequestPauseUnpause, sizeof(PauseUnpauseRequest));
|
||||
PauseUnpauseRequest.m_bPause = htonl(bPause);
|
||||
|
||||
if (m_pConnection->Send((char*)(&PauseUnpauseRequest), sizeof(PauseUnpauseRequest)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
m_pConnection->Disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OK = ReceiveBoolResponse();
|
||||
m_pConnection->Disconnect();
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerSetDownloadRate(float fRate)
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBSetDownloadRateRequest SetDownloadRateRequest;
|
||||
InitMessageBase(&SetDownloadRateRequest.m_MessageBase, eRemoteRequestSetDownloadRate, sizeof(SetDownloadRateRequest));
|
||||
SetDownloadRateRequest.m_iDownloadRate = htonl((unsigned int)(fRate * 1024));
|
||||
|
||||
if (m_pConnection->Send((char*)(&SetDownloadRateRequest), sizeof(SetDownloadRateRequest)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
m_pConnection->Disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OK = ReceiveBoolResponse();
|
||||
m_pConnection->Disconnect();
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerDumpDebug()
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBDumpDebugRequest DumpDebugInfo;
|
||||
InitMessageBase(&DumpDebugInfo.m_MessageBase, eRemoteRequestDumpDebug, sizeof(DumpDebugInfo));
|
||||
|
||||
if (m_pConnection->Send((char*)(&DumpDebugInfo), sizeof(DumpDebugInfo)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
m_pConnection->Disconnect();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OK = ReceiveBoolResponse();
|
||||
m_pConnection->Disconnect();
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerEditQueue(int iAction, int iOffset, int* pIDList, int iIDCount, bool bSmartOrder)
|
||||
{
|
||||
if (iIDCount <= 0 || pIDList == NULL)
|
||||
{
|
||||
printf("File(s) not specified\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
int iLength = sizeof(int32_t) * iIDCount;
|
||||
|
||||
SNZBEditQueueRequest EditQueueRequest;
|
||||
InitMessageBase(&EditQueueRequest.m_MessageBase, eRemoteRequestEditQueue, sizeof(EditQueueRequest));
|
||||
EditQueueRequest.m_iAction = htonl(iAction);
|
||||
EditQueueRequest.m_iOffset = htonl((int)iOffset);
|
||||
EditQueueRequest.m_bSmartOrder = htonl(bSmartOrder);
|
||||
EditQueueRequest.m_iNrTrailingEntries = htonl(iIDCount);
|
||||
EditQueueRequest.m_iTrailingDataLength = htonl(iLength);
|
||||
|
||||
int32_t* pIDs = (int32_t*)malloc(iLength);
|
||||
|
||||
for (int i = 0; i < iIDCount; i++)
|
||||
{
|
||||
pIDs[i] = htonl(pIDList[i]);
|
||||
}
|
||||
|
||||
bool OK = false;
|
||||
if (m_pConnection->Send((char*)(&EditQueueRequest), sizeof(EditQueueRequest)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pConnection->Send((char*)pIDs, iLength);
|
||||
OK = ReceiveBoolResponse();
|
||||
m_pConnection->Disconnect();
|
||||
}
|
||||
|
||||
free(pIDs);
|
||||
|
||||
m_pConnection->Disconnect();
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerShutdown()
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBShutdownRequest ShutdownRequest;
|
||||
InitMessageBase(&ShutdownRequest.m_MessageBase, eRemoteRequestShutdown, sizeof(ShutdownRequest));
|
||||
|
||||
bool OK = m_pConnection->Send((char*)(&ShutdownRequest), sizeof(ShutdownRequest)) >= 0;
|
||||
if (OK)
|
||||
{
|
||||
OK = ReceiveBoolResponse();
|
||||
}
|
||||
else
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
}
|
||||
|
||||
m_pConnection->Disconnect();
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerVersion()
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBVersionRequest VersionRequest;
|
||||
InitMessageBase(&VersionRequest.m_MessageBase, eRemoteRequestVersion, sizeof(VersionRequest));
|
||||
|
||||
bool OK = m_pConnection->Send((char*)(&VersionRequest), sizeof(VersionRequest)) >= 0;
|
||||
if (OK)
|
||||
{
|
||||
OK = ReceiveBoolResponse();
|
||||
}
|
||||
else
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
}
|
||||
|
||||
m_pConnection->Disconnect();
|
||||
return OK;
|
||||
}
|
||||
@@ -1,62 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef REMOTECLIENT_H
|
||||
#define REMOTECLIENT_H
|
||||
|
||||
#include "Options.h"
|
||||
#include "MessageBase.h"
|
||||
#include "Connection.h"
|
||||
|
||||
class RemoteClient
|
||||
{
|
||||
private:
|
||||
Connection* m_pConnection;
|
||||
NetAddress* m_pNetAddress;
|
||||
bool m_bVerbose;
|
||||
|
||||
bool InitConnection();
|
||||
void InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize);
|
||||
bool ReceiveBoolResponse();
|
||||
void printf(char* msg, ...);
|
||||
void perror(char* msg);
|
||||
|
||||
public:
|
||||
RemoteClient();
|
||||
~RemoteClient();
|
||||
void SetVerbose(bool bVerbose) { m_bVerbose = bVerbose; };
|
||||
bool RequestServerDownload(const char* szName, bool bAddFirst);
|
||||
bool RequestServerList();
|
||||
bool RequestServerPauseUnpause(bool bPause);
|
||||
bool RequestServerSetDownloadRate(float fRate);
|
||||
bool RequestServerDumpDebug();
|
||||
bool RequestServerEditQueue(int iAction, int iOffset, int* pIDList, int iIDCount, bool bSmartOrder);
|
||||
bool RequestServerLog(int iLines);
|
||||
bool RequestServerShutdown();
|
||||
bool RequestServerVersion();
|
||||
};
|
||||
|
||||
#endif
|
||||
705
RemoteServer.cpp
705
RemoteServer.cpp
@@ -1,705 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "RemoteServer.h"
|
||||
#include "Log.h"
|
||||
#include "Options.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "QueueEditor.h"
|
||||
#include "PrePostProcessor.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
extern PrePostProcessor* g_pPrePostProcessor;
|
||||
extern void ExitProc();
|
||||
|
||||
const char* g_szMessageRequestNames[] =
|
||||
{ "N/A", "Download", "Pause/Unpause", "List",
|
||||
"Set download rate", "Dump debug", "Edit queue", "Log", "Quit"
|
||||
};
|
||||
|
||||
const unsigned int g_iMessageRequestSizes[] =
|
||||
{ 0,
|
||||
sizeof(SNZBDownloadRequest),
|
||||
sizeof(SNZBPauseUnpauseRequest),
|
||||
sizeof(SNZBListRequest),
|
||||
sizeof(SNZBSetDownloadRateRequest),
|
||||
sizeof(SNZBDumpDebugRequest),
|
||||
sizeof(SNZBEditQueueRequest),
|
||||
sizeof(SNZBLogRequest),
|
||||
sizeof(SNZBRequestBase)
|
||||
};
|
||||
|
||||
//*****************************************************************
|
||||
// RemoteServer
|
||||
|
||||
RemoteServer::RemoteServer()
|
||||
{
|
||||
debug("Creating RemoteServer");
|
||||
|
||||
m_pNetAddress = new NetAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
m_pConnection = NULL;
|
||||
}
|
||||
|
||||
RemoteServer::~RemoteServer()
|
||||
{
|
||||
debug("Destroying RemoteServer");
|
||||
|
||||
if (m_pConnection)
|
||||
{
|
||||
delete m_pConnection;
|
||||
}
|
||||
delete m_pNetAddress;
|
||||
}
|
||||
|
||||
void RemoteServer::Run()
|
||||
{
|
||||
debug("Entering RemoteServer-loop");
|
||||
|
||||
while (!IsStopped())
|
||||
{
|
||||
bool bBind = true;
|
||||
|
||||
if (!m_pConnection)
|
||||
{
|
||||
m_pConnection = new Connection(m_pNetAddress);
|
||||
m_pConnection->SetTimeout(g_pOptions->GetConnectionTimeout());
|
||||
m_pConnection->SetSuppressErrors(false);
|
||||
bBind = m_pConnection->Bind() == 0;
|
||||
}
|
||||
|
||||
// Accept connections and store the "new" socket value
|
||||
SOCKET iSocket = INVALID_SOCKET;
|
||||
if (bBind)
|
||||
{
|
||||
iSocket = m_pConnection->Accept();
|
||||
}
|
||||
if (!bBind || iSocket == INVALID_SOCKET)
|
||||
{
|
||||
// Remote server could not bind or accept connection, waiting 1/2 sec and try again
|
||||
if (IsStopped())
|
||||
{
|
||||
break;
|
||||
}
|
||||
usleep(500 * 1000);
|
||||
delete m_pConnection;
|
||||
m_pConnection = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
RequestProcessor* commandThread = new RequestProcessor();
|
||||
commandThread->SetAutoDestroy(true);
|
||||
commandThread->SetSocket(iSocket);
|
||||
commandThread->Start();
|
||||
}
|
||||
if (m_pConnection)
|
||||
{
|
||||
m_pConnection->Disconnect();
|
||||
}
|
||||
|
||||
debug("Exiting RemoteServer-loop");
|
||||
}
|
||||
|
||||
void RemoteServer::Stop()
|
||||
{
|
||||
Thread::Stop();
|
||||
if (m_pConnection)
|
||||
{
|
||||
m_pConnection->SetSuppressErrors(true);
|
||||
m_pConnection->Cancel();
|
||||
#ifdef WIN32
|
||||
m_pConnection->Disconnect();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
//*****************************************************************
|
||||
// RequestProcessor
|
||||
|
||||
void RequestProcessor::Run()
|
||||
{
|
||||
int iBytesReceived = 0;
|
||||
|
||||
// Read the first package which needs to be a request
|
||||
|
||||
iBytesReceived = recv(m_iSocket, (char*) & m_MessageBase, sizeof(m_MessageBase), 0);
|
||||
if (iBytesReceived < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure this is a nzbget request from a client
|
||||
if ((int)ntohl(m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE)
|
||||
{
|
||||
warn("Non-nzbget request received on port %i", g_pOptions->GetServerPort());
|
||||
|
||||
if (m_iSocket > -1)
|
||||
{
|
||||
closesocket(m_iSocket);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(m_MessageBase.m_szPassword, g_pOptions->GetServerPassword()))
|
||||
{
|
||||
warn("nzbget request received on port %i, but password invalid", g_pOptions->GetServerPort());
|
||||
|
||||
if (m_iSocket > -1)
|
||||
{
|
||||
closesocket(m_iSocket);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Info - connection received
|
||||
struct sockaddr_in PeerName;
|
||||
int iPeerNameLength = sizeof(PeerName);
|
||||
if (getpeername(m_iSocket, (struct sockaddr*)&PeerName, (socklen_t*) &iPeerNameLength) >= 0)
|
||||
{
|
||||
#ifdef WIN32
|
||||
char* ip = inet_ntoa(PeerName.sin_addr);
|
||||
#else
|
||||
char ip[20];
|
||||
inet_ntop(AF_INET, &PeerName.sin_addr, ip, sizeof(ip));
|
||||
#endif
|
||||
debug("%s request received from %s", g_szMessageRequestNames[ntohl(m_MessageBase.m_iType)], ip);
|
||||
}
|
||||
|
||||
Dispatch();
|
||||
|
||||
// Close the socket
|
||||
closesocket(m_iSocket);
|
||||
}
|
||||
|
||||
void RequestProcessor::Dispatch()
|
||||
{
|
||||
if (ntohl(m_MessageBase.m_iType) >= (int)eRemoteRequestDownload &&
|
||||
ntohl(m_MessageBase.m_iType) <= (int)eRemoteRequestShutdown &&
|
||||
g_iMessageRequestSizes[ntohl(m_MessageBase.m_iType)] != ntohl(m_MessageBase.m_iStructSize))
|
||||
{
|
||||
error("Invalid size of request: needed %i Bytes, but received %i Bytes",
|
||||
g_iMessageRequestSizes[ntohl(m_MessageBase.m_iType)], ntohl(m_MessageBase.m_iStructSize));
|
||||
return;
|
||||
}
|
||||
|
||||
MessageCommand* command = NULL;
|
||||
|
||||
switch (ntohl(m_MessageBase.m_iType))
|
||||
{
|
||||
case eRemoteRequestDownload:
|
||||
{
|
||||
command = new DownloadCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestList:
|
||||
{
|
||||
command = new ListCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestLog:
|
||||
{
|
||||
command = new LogCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestPauseUnpause:
|
||||
{
|
||||
command = new PauseUnpauseCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestEditQueue:
|
||||
{
|
||||
command = new EditQueueCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestSetDownloadRate:
|
||||
{
|
||||
command = new SetDownloadRateCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestDumpDebug:
|
||||
{
|
||||
command = new DumpDebugCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestShutdown:
|
||||
{
|
||||
command = new ShutdownCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestVersion:
|
||||
{
|
||||
command = new VersionCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
error("Received unsupported request %i", ntohl(m_MessageBase.m_iType));
|
||||
break;
|
||||
}
|
||||
|
||||
if (command)
|
||||
{
|
||||
command->SetSocket(m_iSocket);
|
||||
command->SetMessageBase(&m_MessageBase);
|
||||
command->Execute();
|
||||
delete command;
|
||||
}
|
||||
}
|
||||
|
||||
//*****************************************************************
|
||||
// Commands
|
||||
|
||||
void MessageCommand::SendBoolResponse(bool bSuccess, const char* szText)
|
||||
{
|
||||
// all bool-responses have the same format of structure, we use SNZBDownloadResponse here
|
||||
SNZBDownloadResponse BoolResponse;
|
||||
memset(&BoolResponse, 0, sizeof(BoolResponse));
|
||||
BoolResponse.m_MessageBase.m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
|
||||
BoolResponse.m_MessageBase.m_iStructSize = htonl(sizeof(BoolResponse));
|
||||
BoolResponse.m_bSuccess = htonl(bSuccess);
|
||||
int iTextLen = strlen(szText) + 1;
|
||||
BoolResponse.m_iTrailingDataLength = htonl(iTextLen);
|
||||
|
||||
// Send the request answer
|
||||
send(m_iSocket, (char*) &BoolResponse, sizeof(BoolResponse), 0);
|
||||
send(m_iSocket, (char*)szText, iTextLen, 0);
|
||||
}
|
||||
|
||||
bool MessageCommand::ReceiveRequest(void* pBuffer, int iSize)
|
||||
{
|
||||
memcpy(pBuffer, m_pMessageBase, sizeof(SNZBRequestBase));
|
||||
iSize -= sizeof(SNZBRequestBase);
|
||||
if (iSize > 0)
|
||||
{
|
||||
int iBytesReceived = recv(m_iSocket, ((char*)pBuffer) + sizeof(SNZBRequestBase), iSize, 0);
|
||||
if (iBytesReceived != iSize)
|
||||
{
|
||||
error("invalid request");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PauseUnpauseCommand::Execute()
|
||||
{
|
||||
SNZBPauseUnpauseRequest PauseUnpauseRequest;
|
||||
if (!ReceiveRequest(&PauseUnpauseRequest, sizeof(PauseUnpauseRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_pOptions->SetPause(ntohl(PauseUnpauseRequest.m_bPause));
|
||||
SendBoolResponse(true, "Pause-/Unpause-Command completed successfully");
|
||||
}
|
||||
|
||||
void SetDownloadRateCommand::Execute()
|
||||
{
|
||||
SNZBSetDownloadRateRequest SetDownloadRequest;
|
||||
if (!ReceiveRequest(&SetDownloadRequest, sizeof(SetDownloadRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_pOptions->SetDownloadRate(ntohl(SetDownloadRequest.m_iDownloadRate) / 1024.0);
|
||||
SendBoolResponse(true, "Rate-Command completed successfully");
|
||||
}
|
||||
|
||||
void DumpDebugCommand::Execute()
|
||||
{
|
||||
SNZBDumpDebugRequest DumpDebugRequest;
|
||||
if (!ReceiveRequest(&DumpDebugRequest, sizeof(DumpDebugRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->LogDebugInfo();
|
||||
SendBoolResponse(true, "Debug-Command completed successfully");
|
||||
}
|
||||
|
||||
void ShutdownCommand::Execute()
|
||||
{
|
||||
SNZBShutdownRequest ShutdownRequest;
|
||||
if (!ReceiveRequest(&ShutdownRequest, sizeof(ShutdownRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SendBoolResponse(true, "Stopping server");
|
||||
ExitProc();
|
||||
}
|
||||
|
||||
void VersionCommand::Execute()
|
||||
{
|
||||
SNZBVersionRequest VersionRequest;
|
||||
if (!ReceiveRequest(&VersionRequest, sizeof(VersionRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SendBoolResponse(true, VERSION);
|
||||
}
|
||||
|
||||
void DownloadCommand::Execute()
|
||||
{
|
||||
SNZBDownloadRequest DownloadRequest;
|
||||
if (!ReceiveRequest(&DownloadRequest, sizeof(DownloadRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char* pRecvBuffer = (char*)malloc(ntohl(DownloadRequest.m_iTrailingDataLength) + 1);
|
||||
char* pBufPtr = pRecvBuffer;
|
||||
|
||||
// Read from the socket until nothing remains
|
||||
int iResult = 0;
|
||||
int NeedBytes = ntohl(DownloadRequest.m_iTrailingDataLength);
|
||||
while (NeedBytes > 0)
|
||||
{
|
||||
iResult = recv(m_iSocket, pBufPtr, NeedBytes, 0);
|
||||
// Did the recv succeed?
|
||||
if (iResult <= 0)
|
||||
{
|
||||
error("invalid request");
|
||||
break;
|
||||
}
|
||||
pBufPtr += iResult;
|
||||
NeedBytes -= iResult;
|
||||
}
|
||||
|
||||
if (NeedBytes == 0)
|
||||
{
|
||||
NZBFile* pNZBFile = NZBFile::CreateFromBuffer(DownloadRequest.m_szFilename, pRecvBuffer, ntohl(DownloadRequest.m_iTrailingDataLength));
|
||||
|
||||
if (pNZBFile)
|
||||
{
|
||||
info("Request: Queue collection %s", DownloadRequest.m_szFilename);
|
||||
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, ntohl(DownloadRequest.m_bAddFirst));
|
||||
delete pNZBFile;
|
||||
|
||||
char tmp[1024];
|
||||
snprintf(tmp, 1024, "Collection %s added to queue", BaseFileName(DownloadRequest.m_szFilename));
|
||||
tmp[1024-1] = '\0';
|
||||
SendBoolResponse(true, tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
char tmp[1024];
|
||||
snprintf(tmp, 1024, "Download Request failed for %s", BaseFileName(DownloadRequest.m_szFilename));
|
||||
tmp[1024-1] = '\0';
|
||||
SendBoolResponse(false, tmp);
|
||||
}
|
||||
}
|
||||
|
||||
free(pRecvBuffer);
|
||||
}
|
||||
|
||||
void ListCommand::Execute()
|
||||
{
|
||||
SNZBListRequest ListRequest;
|
||||
if (!ReceiveRequest(&ListRequest, sizeof(ListRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SNZBListResponse ListResponse;
|
||||
memset(&ListResponse, 0, sizeof(ListResponse));
|
||||
ListResponse.m_MessageBase.m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
|
||||
ListResponse.m_MessageBase.m_iStructSize = htonl(sizeof(ListResponse));
|
||||
ListResponse.m_iEntrySize = htonl(sizeof(SNZBListResponseEntry));
|
||||
|
||||
char* buf = NULL;
|
||||
int bufsize = 0;
|
||||
|
||||
if (ntohl(ListRequest.m_bFileList))
|
||||
{
|
||||
// Make a data structure and copy all the elements of the list into it
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
|
||||
int NrEntries = pDownloadQueue->size();
|
||||
|
||||
// calculate required buffer size
|
||||
bufsize = NrEntries * sizeof(SNZBListResponseEntry);
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
bufsize += strlen(pFileInfo->GetNZBFilename()) + 1;
|
||||
bufsize += strlen(pFileInfo->GetSubject()) + 1;
|
||||
bufsize += strlen(pFileInfo->GetFilename()) + 1;
|
||||
bufsize += strlen(pFileInfo->GetDestDir()) + 1;
|
||||
}
|
||||
|
||||
buf = (char*) malloc(bufsize);
|
||||
char* bufptr = buf;
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
unsigned int iSizeHi, iSizeLo;
|
||||
FileInfo* pFileInfo = *it;
|
||||
SNZBListResponseEntry* pListAnswer = (SNZBListResponseEntry*) bufptr;
|
||||
pListAnswer->m_iID = htonl(pFileInfo->GetID());
|
||||
SplitInt64(pFileInfo->GetSize(), &iSizeHi, &iSizeLo);
|
||||
pListAnswer->m_iFileSizeLo = htonl(iSizeLo);
|
||||
pListAnswer->m_iFileSizeHi = htonl(iSizeHi);
|
||||
SplitInt64(pFileInfo->GetRemainingSize(), &iSizeHi, &iSizeLo);
|
||||
pListAnswer->m_iRemainingSizeLo = htonl(iSizeLo);
|
||||
pListAnswer->m_iRemainingSizeHi = htonl(iSizeHi);
|
||||
pListAnswer->m_bFilenameConfirmed = htonl(pFileInfo->GetFilenameConfirmed());
|
||||
pListAnswer->m_bPaused = htonl(pFileInfo->GetPaused());
|
||||
pListAnswer->m_iNZBFilenameLen = htonl(strlen(pFileInfo->GetNZBFilename()) + 1);
|
||||
pListAnswer->m_iSubjectLen = htonl(strlen(pFileInfo->GetSubject()) + 1);
|
||||
pListAnswer->m_iFilenameLen = htonl(strlen(pFileInfo->GetFilename()) + 1);
|
||||
pListAnswer->m_iDestDirLen = htonl(strlen(pFileInfo->GetDestDir()) + 1);
|
||||
bufptr += sizeof(SNZBListResponseEntry);
|
||||
strcpy(bufptr, pFileInfo->GetNZBFilename());
|
||||
bufptr += ntohl(pListAnswer->m_iNZBFilenameLen);
|
||||
strcpy(bufptr, pFileInfo->GetSubject());
|
||||
bufptr += ntohl(pListAnswer->m_iSubjectLen);
|
||||
strcpy(bufptr, pFileInfo->GetFilename());
|
||||
bufptr += ntohl(pListAnswer->m_iFilenameLen);
|
||||
strcpy(bufptr, pFileInfo->GetDestDir());
|
||||
bufptr += ntohl(pListAnswer->m_iDestDirLen);
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
|
||||
ListResponse.m_iNrTrailingEntries = htonl(NrEntries);
|
||||
ListResponse.m_iTrailingDataLength = htonl(bufsize);
|
||||
}
|
||||
|
||||
if (htonl(ListRequest.m_bServerState))
|
||||
{
|
||||
unsigned int iSizeHi, iSizeLo;
|
||||
ListResponse.m_iDownloadRate = htonl((int)(g_pQueueCoordinator->CalcCurrentDownloadSpeed() * 1024));
|
||||
SplitInt64(g_pQueueCoordinator->CalcRemainingSize(), &iSizeHi, &iSizeLo);
|
||||
ListResponse.m_iRemainingSizeHi = htonl(iSizeHi);
|
||||
ListResponse.m_iRemainingSizeLo = htonl(iSizeLo);
|
||||
ListResponse.m_iDownloadLimit = htonl((int)(g_pOptions->GetDownloadRate() * 1024));
|
||||
ListResponse.m_bServerPaused = htonl(g_pOptions->GetPause());
|
||||
ListResponse.m_iThreadCount = htonl(Thread::GetThreadCount() - 1); // not counting itself
|
||||
PrePostProcessor::ParQueue* pParQueue = g_pPrePostProcessor->LockParQueue();
|
||||
ListResponse.m_iParJobCount = htonl(pParQueue->size());
|
||||
g_pPrePostProcessor->UnlockParQueue();
|
||||
|
||||
int iUpTimeSec, iDnTimeSec;
|
||||
long long iAllBytes;
|
||||
bool bStandBy;
|
||||
g_pQueueCoordinator->CalcStat(&iUpTimeSec, &iDnTimeSec, &iAllBytes, &bStandBy);
|
||||
ListResponse.m_iUpTimeSec = htonl(iUpTimeSec);
|
||||
ListResponse.m_iDownloadTimeSec = htonl(iDnTimeSec);
|
||||
ListResponse.m_bServerStandBy = htonl(bStandBy);
|
||||
SplitInt64(iAllBytes, &iSizeHi, &iSizeLo);
|
||||
ListResponse.m_iDownloadedBytesHi = htonl(iSizeHi);
|
||||
ListResponse.m_iDownloadedBytesLo = htonl(iSizeLo);
|
||||
}
|
||||
|
||||
// Send the request answer
|
||||
send(m_iSocket, (char*) &ListResponse, sizeof(ListResponse), 0);
|
||||
|
||||
// Send the data
|
||||
if (bufsize > 0)
|
||||
{
|
||||
send(m_iSocket, buf, bufsize, 0);
|
||||
}
|
||||
|
||||
if (buf)
|
||||
{
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
|
||||
void LogCommand::Execute()
|
||||
{
|
||||
SNZBLogRequest LogRequest;
|
||||
if (!ReceiveRequest(&LogRequest, sizeof(LogRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Log::Messages* pMessages = g_pLog->LockMessages();
|
||||
|
||||
int iNrEntries = ntohl(LogRequest.m_iLines);
|
||||
unsigned int iIDFrom = ntohl(LogRequest.m_iIDFrom);
|
||||
int iStart = pMessages->size();
|
||||
if (iNrEntries > 0)
|
||||
{
|
||||
if (iNrEntries > (int)pMessages->size())
|
||||
{
|
||||
iNrEntries = pMessages->size();
|
||||
}
|
||||
iStart = pMessages->size() - iNrEntries;
|
||||
}
|
||||
if (iIDFrom > 0 && !pMessages->empty())
|
||||
{
|
||||
iStart = iIDFrom - pMessages->front()->GetID();
|
||||
if (iStart < 0)
|
||||
{
|
||||
iStart = 0;
|
||||
}
|
||||
iNrEntries = pMessages->size() - iStart;
|
||||
if (iNrEntries < 0)
|
||||
{
|
||||
iNrEntries = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// calculate required buffer size
|
||||
int bufsize = iNrEntries * sizeof(SNZBLogResponseEntry);
|
||||
for (unsigned int i = (unsigned int)iStart; i < pMessages->size(); i++)
|
||||
{
|
||||
Message* pMessage = (*pMessages)[i];
|
||||
bufsize += strlen(pMessage->GetText()) + 1;
|
||||
}
|
||||
|
||||
char* buf = (char*) malloc(bufsize);
|
||||
char* bufptr = buf;
|
||||
for (unsigned int i = (unsigned int)iStart; i < pMessages->size(); i++)
|
||||
{
|
||||
Message* pMessage = (*pMessages)[i];
|
||||
SNZBLogResponseEntry* pLogAnswer = (SNZBLogResponseEntry*) bufptr;
|
||||
pLogAnswer->m_iID = htonl(pMessage->GetID());
|
||||
pLogAnswer->m_iKind = htonl(pMessage->GetKind());
|
||||
pLogAnswer->m_tTime = htonl(pMessage->GetTime());
|
||||
pLogAnswer->m_iTextLen = htonl(strlen(pMessage->GetText()) + 1);
|
||||
bufptr += sizeof(SNZBLogResponseEntry);
|
||||
strcpy(bufptr, pMessage->GetText());
|
||||
bufptr += ntohl(pLogAnswer->m_iTextLen);
|
||||
}
|
||||
|
||||
g_pLog->UnlockMessages();
|
||||
|
||||
SNZBLogResponse LogResponse;
|
||||
LogResponse.m_MessageBase.m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
|
||||
LogResponse.m_MessageBase.m_iStructSize = htonl(sizeof(LogResponse));
|
||||
LogResponse.m_iEntrySize = htonl(sizeof(SNZBLogResponseEntry));
|
||||
LogResponse.m_iNrTrailingEntries = htonl(iNrEntries);
|
||||
LogResponse.m_iTrailingDataLength = htonl(bufsize);
|
||||
|
||||
// Send the request answer
|
||||
send(m_iSocket, (char*) &LogResponse, sizeof(LogResponse), 0);
|
||||
|
||||
// Send the data
|
||||
if (bufsize > 0)
|
||||
{
|
||||
send(m_iSocket, buf, bufsize, 0);
|
||||
}
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
void EditQueueCommand::Execute()
|
||||
{
|
||||
SNZBEditQueueRequest EditQueueRequest;
|
||||
if (!ReceiveRequest(&EditQueueRequest, sizeof(EditQueueRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int iNrEntries = ntohl(EditQueueRequest.m_iNrTrailingEntries);
|
||||
int iAction = ntohl(EditQueueRequest.m_iAction);
|
||||
int iOffset = ntohl(EditQueueRequest.m_iOffset);
|
||||
bool bSmartOrder = ntohl(EditQueueRequest.m_bSmartOrder);
|
||||
unsigned int iBufLength = ntohl(EditQueueRequest.m_iTrailingDataLength);
|
||||
|
||||
if (iNrEntries * sizeof(int32_t) != iBufLength)
|
||||
{
|
||||
error("Invalid struct size");
|
||||
return;
|
||||
}
|
||||
|
||||
if (iNrEntries <= 0)
|
||||
{
|
||||
SendBoolResponse(false, "Edit-Command failed: no IDs specified");
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t* pIDs = (int32_t*)malloc(iBufLength);
|
||||
|
||||
// Read from the socket until nothing remains
|
||||
char* pBufPtr = (char*)pIDs;
|
||||
int NeedBytes = iBufLength;
|
||||
int iResult = 0;
|
||||
while (NeedBytes > 0)
|
||||
{
|
||||
iResult = recv(m_iSocket, pBufPtr, NeedBytes, 0);
|
||||
// Did the recv succeed?
|
||||
if (iResult <= 0)
|
||||
{
|
||||
error("invalid request");
|
||||
break;
|
||||
}
|
||||
pBufPtr += iResult;
|
||||
NeedBytes -= iResult;
|
||||
}
|
||||
|
||||
QueueEditor::IDList cIDList;
|
||||
cIDList.reserve(iNrEntries);
|
||||
for (int i = 0; i < iNrEntries; i++)
|
||||
{
|
||||
cIDList.push_back(ntohl(pIDs[i]));
|
||||
}
|
||||
|
||||
bool bOK = g_pQueueCoordinator->GetQueueEditor()->EditList(&cIDList, bSmartOrder, (QueueEditor::EEditAction)iAction, iOffset);
|
||||
|
||||
free(pIDs);
|
||||
|
||||
if (bOK)
|
||||
{
|
||||
SendBoolResponse(true, "Edit-Command completed successfully");
|
||||
}
|
||||
else
|
||||
{
|
||||
SendBoolResponse(false, "Edit-Command failed");
|
||||
}
|
||||
}
|
||||
133
RemoteServer.h
133
RemoteServer.h
@@ -1,133 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef REMOTESERVER_H
|
||||
#define REMOTESERVER_H
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "Thread.h"
|
||||
#include "NetAddress.h"
|
||||
#include "Connection.h"
|
||||
#include "MessageBase.h"
|
||||
|
||||
class RemoteServer : public Thread
|
||||
{
|
||||
private:
|
||||
NetAddress* m_pNetAddress;
|
||||
Connection* m_pConnection;
|
||||
|
||||
public:
|
||||
RemoteServer();
|
||||
~RemoteServer();
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
};
|
||||
|
||||
class RequestProcessor : public Thread
|
||||
{
|
||||
private:
|
||||
SOCKET m_iSocket;
|
||||
SNZBRequestBase m_MessageBase;
|
||||
|
||||
void Dispatch();
|
||||
|
||||
public:
|
||||
virtual void Run();
|
||||
void SetSocket(SOCKET iSocket) { m_iSocket = iSocket; };
|
||||
};
|
||||
|
||||
class MessageCommand
|
||||
{
|
||||
protected:
|
||||
SOCKET m_iSocket;
|
||||
SNZBRequestBase* m_pMessageBase;
|
||||
|
||||
bool ReceiveRequest(void* pBuffer, int iSize);
|
||||
void SendBoolResponse(bool bSuccess, const char* szText);
|
||||
|
||||
public:
|
||||
virtual ~MessageCommand() {};
|
||||
virtual void Execute() = 0;
|
||||
void SetSocket(SOCKET iSocket) { m_iSocket = iSocket; };
|
||||
void SetMessageBase(SNZBRequestBase* pMessageBase) { m_pMessageBase = pMessageBase; };
|
||||
};
|
||||
|
||||
class DownloadCommand: public MessageCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ListCommand: public MessageCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class LogCommand: public MessageCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class PauseUnpauseCommand: public MessageCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class EditQueueCommand: public MessageCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class SetDownloadRateCommand: public MessageCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class DumpDebugCommand: public MessageCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ShutdownCommand: public MessageCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class VersionCommand: public MessageCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
#endif
|
||||
251
ServerPool.cpp
251
ServerPool.cpp
@@ -1,251 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* m_Semaphore Patch by Florian Penzkofer <f.penzkofer@sent.com>
|
||||
* The queue of mutexes that was used did not work for every
|
||||
* implementation of POSIX threads. Now a m_Semaphore is used.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "ServerPool.h"
|
||||
#include "Log.h"
|
||||
|
||||
static const int CONNECTION_HOLD_SECODNS = 5;
|
||||
|
||||
ServerPool::PooledConnection::PooledConnection(NewsServer* server) : NNTPConnection(server)
|
||||
{
|
||||
m_bInUse = false;
|
||||
m_tFreeTime = 0;
|
||||
}
|
||||
|
||||
ServerPool::ServerPool()
|
||||
{
|
||||
debug("Creating ServerPool");
|
||||
|
||||
m_iMaxLevel = 0;
|
||||
m_iTimeout = 60;
|
||||
m_Servers.clear();
|
||||
m_Connections.clear();
|
||||
m_Semaphores.clear();
|
||||
}
|
||||
|
||||
ServerPool::~ ServerPool()
|
||||
{
|
||||
debug("Destroying ServerPool");
|
||||
|
||||
for (Semaphores::iterator it = m_Semaphores.begin(); it != m_Semaphores.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Semaphores.clear();
|
||||
|
||||
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Servers.clear();
|
||||
|
||||
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Connections.clear();
|
||||
}
|
||||
|
||||
void ServerPool::AddServer(NewsServer* pNewsServer)
|
||||
{
|
||||
debug("Adding server to ServerPool");
|
||||
|
||||
m_Servers.push_back(pNewsServer);
|
||||
}
|
||||
|
||||
void ServerPool::InitConnections()
|
||||
{
|
||||
debug("Initializing connections in ServerPool");
|
||||
|
||||
m_iMaxLevel = 0;
|
||||
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
|
||||
{
|
||||
NewsServer* pNewsServer = *it;
|
||||
if (m_iMaxLevel < pNewsServer->GetLevel())
|
||||
{
|
||||
m_iMaxLevel = pNewsServer->GetLevel();
|
||||
}
|
||||
for (int i = 0; i < pNewsServer->GetMaxConnections(); i++)
|
||||
{
|
||||
PooledConnection* pConnection = new PooledConnection(pNewsServer);
|
||||
pConnection->SetTimeout(m_iTimeout);
|
||||
m_Connections.push_back(pConnection);
|
||||
}
|
||||
}
|
||||
|
||||
for (int iLevel = 0; iLevel <= m_iMaxLevel; iLevel++)
|
||||
{
|
||||
int iMaxConnectionsForLevel = 0;
|
||||
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
|
||||
{
|
||||
NewsServer* pNewsServer = *it;
|
||||
if (iLevel == pNewsServer->GetLevel())
|
||||
{
|
||||
iMaxConnectionsForLevel += pNewsServer->GetMaxConnections();
|
||||
}
|
||||
}
|
||||
|
||||
Semaphore* sem = new Semaphore(iMaxConnectionsForLevel);
|
||||
m_Semaphores.push_back(sem);
|
||||
}
|
||||
}
|
||||
|
||||
NNTPConnection* ServerPool::GetConnection(int iLevel, bool bWait)
|
||||
{
|
||||
bool bWaitVal = false;
|
||||
if (bWait)
|
||||
{
|
||||
debug("Getting connection (wait)");
|
||||
bWaitVal = m_Semaphores[iLevel]->Wait();
|
||||
}
|
||||
else
|
||||
{
|
||||
bWaitVal = m_Semaphores[iLevel]->TryWait();
|
||||
}
|
||||
|
||||
if (!bWaitVal)
|
||||
{
|
||||
// signal received or wait timeout
|
||||
return NULL;
|
||||
}
|
||||
|
||||
m_mutexConnections.Lock();
|
||||
|
||||
PooledConnection* pConnection = NULL;
|
||||
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
|
||||
{
|
||||
PooledConnection* pConnection1 = *it;
|
||||
if (!pConnection1->GetInUse() && pConnection1->GetNewsServer()->GetLevel() == iLevel)
|
||||
{
|
||||
// free connection found, take it!
|
||||
pConnection = pConnection1;
|
||||
pConnection->SetInUse(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_mutexConnections.Unlock();
|
||||
|
||||
if (!pConnection)
|
||||
{
|
||||
error("ServerPool: serious error, no free connection found, but there should be one.");
|
||||
}
|
||||
|
||||
return pConnection;
|
||||
}
|
||||
|
||||
void ServerPool::FreeConnection(NNTPConnection* pConnection, bool bUsed)
|
||||
{
|
||||
if (bUsed)
|
||||
{
|
||||
debug("Freeing used connection");
|
||||
}
|
||||
|
||||
m_mutexConnections.Lock();
|
||||
|
||||
((PooledConnection*)pConnection)->SetInUse(false);
|
||||
if (bUsed)
|
||||
{
|
||||
((PooledConnection*)pConnection)->SetFreeTimeNow();
|
||||
}
|
||||
m_Semaphores[pConnection->GetNewsServer()->GetLevel()]->Post();
|
||||
|
||||
m_mutexConnections.Unlock();
|
||||
}
|
||||
|
||||
void ServerPool::CloseUnusedConnections()
|
||||
{
|
||||
m_mutexConnections.Lock();
|
||||
|
||||
time_t curtime = ::time(NULL);
|
||||
|
||||
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
|
||||
{
|
||||
PooledConnection* pConnection = *it;
|
||||
if (!pConnection->GetInUse() && pConnection->GetStatus() == Connection::csConnected)
|
||||
{
|
||||
int tdiff = curtime - pConnection->GetFreeTime();
|
||||
if (tdiff > CONNECTION_HOLD_SECODNS)
|
||||
{
|
||||
debug("Closing unused connection to %s", pConnection->GetNewsServer()->GetHost());
|
||||
pConnection->Disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_mutexConnections.Unlock();
|
||||
}
|
||||
|
||||
void ServerPool::LogDebugInfo()
|
||||
{
|
||||
debug(" ServerPool");
|
||||
debug(" ----------------");
|
||||
|
||||
debug(" Max-Level: %i", m_iMaxLevel);
|
||||
|
||||
m_mutexConnections.Lock();
|
||||
|
||||
debug(" Connections: %i", m_Connections.size());
|
||||
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
|
||||
{
|
||||
debug(" Connection: Level=%i, InUse:%i", (*it)->GetNewsServer()->GetLevel(), (int)(*it)->GetInUse());
|
||||
}
|
||||
/*
|
||||
debug(" Semaphores: %i", m_Semaphores.size());
|
||||
for (int iLevel = 0; iLevel <= m_iMaxLevel; iLevel++)
|
||||
{
|
||||
sem_t* sem = m_Semaphores[iLevel];
|
||||
int iSemValue;
|
||||
sem_getvalue(sem, &iSemValue);
|
||||
debug(" Semaphore: level=%i, value=%i", iLevel, iSemValue);
|
||||
}
|
||||
*/
|
||||
m_mutexConnections.Unlock();
|
||||
}
|
||||
416
Util.cpp
416
Util.cpp
@@ -1,416 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
#ifdef WIN32
|
||||
#include <direct.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Util.h"
|
||||
|
||||
char* BaseFileName(const char* filename)
|
||||
{
|
||||
char* p = (char*)strrchr(filename, PATH_SEPARATOR);
|
||||
char* p1 = (char*)strrchr(filename, ALT_PATH_SEPARATOR);
|
||||
if (p1)
|
||||
{
|
||||
if ((p && p < p1) || !p)
|
||||
{
|
||||
p = p1;
|
||||
}
|
||||
}
|
||||
if (p)
|
||||
{
|
||||
return p + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (char*)filename;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
// getopt for WIN32:
|
||||
// from http://www.codeproject.com/cpp/xgetopt.asp
|
||||
// Original Author: Hans Dietrich (hdietrich2@hotmail.com)
|
||||
// Released to public domain from author (thanks)
|
||||
// Slightly modified by Andrei Prygounkov
|
||||
|
||||
char *optarg; // global argument pointer
|
||||
int optind = 0; // global argv index
|
||||
|
||||
int getopt(int argc, char *argv[], char *optstring)
|
||||
{
|
||||
static char *next = NULL;
|
||||
if (optind == 0)
|
||||
next = NULL;
|
||||
|
||||
optarg = NULL;
|
||||
|
||||
if (next == NULL || *next == '\0')
|
||||
{
|
||||
if (optind == 0)
|
||||
optind++;
|
||||
|
||||
if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0')
|
||||
{
|
||||
optarg = NULL;
|
||||
if (optind < argc)
|
||||
optarg = argv[optind];
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (strcmp(argv[optind], "--") == 0)
|
||||
{
|
||||
optind++;
|
||||
optarg = NULL;
|
||||
if (optind < argc)
|
||||
optarg = argv[optind];
|
||||
return -1;
|
||||
}
|
||||
|
||||
next = argv[optind];
|
||||
next++; // skip past -
|
||||
optind++;
|
||||
}
|
||||
|
||||
char c = *next++;
|
||||
char *cp = strchr(optstring, c);
|
||||
|
||||
if (cp == NULL || c == ':')
|
||||
{
|
||||
fprintf(stderr, "Invalid option %c", c);
|
||||
return '?';
|
||||
}
|
||||
|
||||
cp++;
|
||||
if (*cp == ':')
|
||||
{
|
||||
if (*next != '\0')
|
||||
{
|
||||
optarg = next;
|
||||
next = NULL;
|
||||
}
|
||||
else if (optind < argc)
|
||||
{
|
||||
optarg = argv[optind];
|
||||
optind++;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Option %c needs an argument", c);
|
||||
return '?';
|
||||
}
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
DirBrowser::DirBrowser(const char* szPath)
|
||||
{
|
||||
char szMask[MAX_PATH + 1];
|
||||
int len = strlen(szPath);
|
||||
if (szPath[len] == '\\' || szPath[len] == '/')
|
||||
{
|
||||
snprintf(szMask, MAX_PATH + 1, "%s*.*", szPath);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(szMask, MAX_PATH + 1, "%s%c*.*", szPath, (int)PATH_SEPARATOR);
|
||||
}
|
||||
szMask[MAX_PATH] = '\0';
|
||||
m_hFile = _findfirst(szMask, &m_FindData);
|
||||
m_bFirst = true;
|
||||
}
|
||||
|
||||
DirBrowser::~DirBrowser()
|
||||
{
|
||||
if (m_hFile != -1L)
|
||||
{
|
||||
_findclose(m_hFile);
|
||||
}
|
||||
}
|
||||
|
||||
const char* DirBrowser::Next()
|
||||
{
|
||||
bool bOK = false;
|
||||
if (m_bFirst)
|
||||
{
|
||||
bOK = m_hFile != -1L;
|
||||
m_bFirst = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
bOK = _findnext(m_hFile, &m_FindData) == 0;
|
||||
}
|
||||
if (bOK)
|
||||
{
|
||||
return m_FindData.name;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
DirBrowser::DirBrowser(const char* szPath)
|
||||
{
|
||||
m_pDir = opendir(szPath);
|
||||
}
|
||||
|
||||
DirBrowser::~DirBrowser()
|
||||
{
|
||||
if (m_pDir)
|
||||
{
|
||||
closedir(m_pDir);
|
||||
}
|
||||
}
|
||||
|
||||
const char* DirBrowser::Next()
|
||||
{
|
||||
if (m_pDir)
|
||||
{
|
||||
m_pFindData = readdir(m_pDir);
|
||||
if (m_pFindData)
|
||||
{
|
||||
return m_pFindData->d_name;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
void NormalizePathSeparators(char* szPath)
|
||||
{
|
||||
for (char* p = szPath; *p; p++)
|
||||
{
|
||||
if (*p == ALT_PATH_SEPARATOR)
|
||||
{
|
||||
*p = PATH_SEPARATOR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool ForceDirectories(const char* szPath)
|
||||
{
|
||||
char* szNormPath = strdup(szPath);
|
||||
NormalizePathSeparators(szNormPath);
|
||||
int iLen = strlen(szNormPath);
|
||||
if ((iLen > 0) && szNormPath[iLen-1] == PATH_SEPARATOR
|
||||
#ifdef WIN32
|
||||
&& iLen > 3
|
||||
#endif
|
||||
)
|
||||
{
|
||||
szNormPath[iLen-1] = '\0';
|
||||
}
|
||||
|
||||
struct stat buffer;
|
||||
bool bOK = !stat(szNormPath, &buffer) && S_ISDIR(buffer.st_mode);
|
||||
if (!bOK
|
||||
#ifdef WIN32
|
||||
&& strlen(szNormPath) > 2
|
||||
#endif
|
||||
)
|
||||
{
|
||||
char* szParentPath = strdup(szNormPath);
|
||||
bOK = true;
|
||||
char* p = (char*)strrchr(szParentPath, PATH_SEPARATOR);
|
||||
if (p)
|
||||
{
|
||||
#ifdef WIN32
|
||||
if (p - szParentPath == 2 && szParentPath[1] == ':' && strlen(szParentPath) > 2)
|
||||
{
|
||||
szParentPath[3] = '\0';
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
*p = '\0';
|
||||
}
|
||||
if (strlen(szParentPath) != strlen(szPath))
|
||||
{
|
||||
bOK = ForceDirectories(szParentPath);
|
||||
}
|
||||
}
|
||||
if (bOK)
|
||||
{
|
||||
mkdir(szNormPath, S_DIRMODE);
|
||||
bOK = !stat(szNormPath, &buffer) && S_ISDIR(buffer.st_mode);
|
||||
}
|
||||
free(szParentPath);
|
||||
}
|
||||
free(szNormPath);
|
||||
return bOK;
|
||||
}
|
||||
|
||||
bool LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength)
|
||||
{
|
||||
FILE* pFile = fopen(szFileName, "r");
|
||||
if (!pFile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// obtain file size.
|
||||
fseek(pFile , 0 , SEEK_END);
|
||||
int iSize = ftell(pFile);
|
||||
rewind(pFile);
|
||||
|
||||
// allocate memory to contain the whole file.
|
||||
*pBuffer = (char*) malloc(iSize + 1);
|
||||
if (!*pBuffer)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// copy the file into the buffer.
|
||||
fread(*pBuffer, 1, iSize, pFile);
|
||||
|
||||
fclose(pFile);
|
||||
|
||||
(*pBuffer)[iSize] = 0;
|
||||
|
||||
*pBufferLength = iSize + 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool SetFileSize(const char* szFilename, int iSize)
|
||||
{
|
||||
bool bOK = false;
|
||||
#ifdef WIN32
|
||||
FILE* pFile = fopen(szFilename, "a");
|
||||
if (pFile)
|
||||
{
|
||||
bOK = _chsize_s(pFile->_file, iSize) == 0;
|
||||
fclose(pFile);
|
||||
}
|
||||
#else
|
||||
// create file
|
||||
FILE* pFile = fopen(szFilename, "a");
|
||||
if (pFile)
|
||||
{
|
||||
fclose(pFile);
|
||||
}
|
||||
// there no reliable function to expand file on POSIX, so we must try different approaches,
|
||||
// starting with the fastest one and hoping it will work
|
||||
// 1) set file size using function "truncate" (it is fast, if works)
|
||||
truncate(szFilename, iSize);
|
||||
// check if it worked
|
||||
pFile = fopen(szFilename, "a");
|
||||
if (pFile)
|
||||
{
|
||||
fseek(pFile, 0, SEEK_END);
|
||||
bOK = ftell(pFile) == iSize;
|
||||
if (!bOK)
|
||||
{
|
||||
// 2) truncate did not work, expanding the file by writing in it (it is slow)
|
||||
fclose(pFile);
|
||||
truncate(szFilename, 0);
|
||||
pFile = fopen(szFilename, "a");
|
||||
char c = '0';
|
||||
fwrite(&c, 1, iSize, pFile);
|
||||
bOK = ftell(pFile) == iSize;
|
||||
}
|
||||
fclose(pFile);
|
||||
}
|
||||
#endif
|
||||
return bOK;
|
||||
}
|
||||
|
||||
//replace bad chars in filename
|
||||
void MakeValidFilename(char* szFilename, char cReplaceChar)
|
||||
{
|
||||
char* p = szFilename;
|
||||
while (*p)
|
||||
{
|
||||
if (strchr("\\/:*?\"><'\n\r\t", *p))
|
||||
{
|
||||
*p = cReplaceChar;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
// remove trailing points. they are not allowed in directory names on windows,
|
||||
// but we remove them on posix also, in a case the directory is accessed from windows via samba.
|
||||
for (int iLen = strlen(szFilename); iLen > 0 && szFilename[iLen - 1] == '.'; iLen--)
|
||||
{
|
||||
szFilename[iLen - 1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
long long JoinInt64(unsigned int Hi, unsigned int Lo)
|
||||
{
|
||||
return (((long long)Hi) << 32) + Lo;
|
||||
}
|
||||
|
||||
void SplitInt64(long long Int64, unsigned int* Hi, unsigned int* Lo)
|
||||
{
|
||||
*Hi = (unsigned int)(Int64 >> 32);
|
||||
*Lo = (unsigned int)Int64;
|
||||
}
|
||||
|
||||
float EqualTime(_timeval* t1, _timeval* t2)
|
||||
{
|
||||
#ifdef WIN32
|
||||
return t1->time == t2->time && t1->millitm == t2->millitm;
|
||||
#else
|
||||
return t1->tv_sec == t2->tv_sec && t1->tv_usec == t2->tv_usec;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool EmptyTime(_timeval* t)
|
||||
{
|
||||
#ifdef WIN32
|
||||
return t->time == 0 && t->millitm == 0;
|
||||
#else
|
||||
return t->tv_sec == 0 && t->tv_usec == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
float DiffTime(_timeval* t1, _timeval* t2)
|
||||
{
|
||||
#ifdef WIN32
|
||||
return ((t1->time - t2->time) + (t1->millitm - t2->millitm) / 1000.0);
|
||||
#else
|
||||
return (float)((t1->tv_sec - t2->tv_sec) + (t1->tv_usec - t2->tv_usec) / 1000000.0);
|
||||
#endif
|
||||
}
|
||||
75
Util.h
75
Util.h
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef UTIL_H
|
||||
#define UTIL_H
|
||||
|
||||
#ifdef WIN32
|
||||
#include <stdio.h>
|
||||
#include <io.h>
|
||||
#include <sys/timeb.h>
|
||||
#else
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
extern int optind, opterr;
|
||||
extern char *optarg;
|
||||
int getopt(int argc, char *argv[], char *optstring);
|
||||
#endif
|
||||
|
||||
class DirBrowser
|
||||
{
|
||||
private:
|
||||
#ifdef WIN32
|
||||
struct _finddata_t m_FindData;
|
||||
intptr_t m_hFile;
|
||||
bool m_bFirst;
|
||||
#else
|
||||
DIR* m_pDir;
|
||||
struct dirent* m_pFindData;
|
||||
#endif
|
||||
|
||||
public:
|
||||
DirBrowser(const char* szPath);
|
||||
~DirBrowser();
|
||||
const char* Next();
|
||||
};
|
||||
|
||||
char* BaseFileName(const char* filename);
|
||||
void NormalizePathSeparators(char* szPath);
|
||||
bool ForceDirectories(const char* szPath);
|
||||
bool LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength);
|
||||
bool SetFileSize(const char* szFilename, int iSize);
|
||||
void MakeValidFilename(char* szFilename, char cReplaceChar);
|
||||
|
||||
long long JoinInt64(unsigned int Hi, unsigned int Lo);
|
||||
void SplitInt64(long long Int64, unsigned int* Hi, unsigned int* Lo);
|
||||
|
||||
float EqualTime(_timeval* t1, _timeval* t2);
|
||||
bool EmptyTime(_timeval* t);
|
||||
float DiffTime(_timeval* t1, _timeval* t2);
|
||||
|
||||
#endif
|
||||
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.
|
||||
|
||||
1500
config.guess
vendored
1500
config.guess
vendored
File diff suppressed because it is too large
Load Diff
132
config.h.in
132
config.h.in
@@ -3,19 +3,32 @@
|
||||
/* Define to 1 to include debug-code */
|
||||
#undef DEBUG
|
||||
|
||||
/* Define to 1 if deleting of files during reading of directory is not
|
||||
properly supported by OS */
|
||||
#undef DIRBROWSER_SNAPSHOT
|
||||
|
||||
/* Define to 1 to not use curses */
|
||||
#undef DISABLE_CURSES
|
||||
|
||||
/* Define to 1 to disable smart par-verification and restoration */
|
||||
/* Define to 1 to disable gzip-support */
|
||||
#undef DISABLE_GZIP
|
||||
|
||||
/* Define to 1 to disable par-verification and repair */
|
||||
#undef DISABLE_PARCHECK
|
||||
|
||||
/* Define to 1 to include support for uulib */
|
||||
#undef ENABLE_UULIB
|
||||
/* Define to 1 to not use TLS/SSL */
|
||||
#undef DISABLE_TLS
|
||||
|
||||
/* Define to 1 to enable unit and integration tests */
|
||||
#undef ENABLE_TESTS
|
||||
|
||||
/* Define to the name of macro which returns the name of function being
|
||||
compiled */
|
||||
#undef FUNCTION_MACRO_NAME
|
||||
|
||||
/* Define to 1 to create stacktrace on segmentation faults */
|
||||
#undef HAVE_BACKTRACE
|
||||
|
||||
/* Define to 1 if ctime_r takes 2 arguments */
|
||||
#undef HAVE_CTIME_R_2
|
||||
|
||||
@@ -25,18 +38,55 @@
|
||||
/* Define to 1 if you have the <curses.h> header file. */
|
||||
#undef HAVE_CURSES_H
|
||||
|
||||
/* Define to 1 if you have the <dirent.h> header file, and it defines `DIR'.
|
||||
*/
|
||||
#undef HAVE_DIRENT_H
|
||||
|
||||
/* Define to 1 if you have the <endian.h> header file. */
|
||||
#undef HAVE_ENDIAN_H
|
||||
|
||||
/* Define to 1 if fdatasync is supported */
|
||||
#undef HAVE_FDATASYNC
|
||||
|
||||
/* Define to 1 if fseeko (and presumably ftello) exists and is declared. */
|
||||
#undef HAVE_FSEEKO
|
||||
|
||||
/* Define to 1 if F_FULLFSYNC is supported */
|
||||
#undef HAVE_FULLFSYNC
|
||||
|
||||
/* Define to 1 if getaddrinfo is supported */
|
||||
#undef HAVE_GETADDRINFO
|
||||
|
||||
/* Define to 1 if gethostbyname_r is supported */
|
||||
#undef HAVE_GETHOSTBYNAME_R
|
||||
|
||||
/* Define to 1 if gethostbyname_r takes 3 arguments */
|
||||
#undef HAVE_GETHOSTBYNAME_R_3
|
||||
|
||||
/* Define to 1 if gethostbyname_r takes 5 arguments */
|
||||
#undef HAVE_GETHOSTBYNAME_R_5
|
||||
|
||||
/* Define to 1 if gethostbyname_r takes 6 arguments */
|
||||
#undef HAVE_GETHOSTBYNAME_R_6
|
||||
|
||||
/* Define to 1 if you have the `getopt' function. */
|
||||
#undef HAVE_GETOPT
|
||||
|
||||
/* Define to 1 if you have the <getopt.h> header file. */
|
||||
#undef HAVE_GETOPT_H
|
||||
|
||||
/* Define to 1 if getopt_long is supported */
|
||||
#undef HAVE_GETOPT_LONG
|
||||
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#undef HAVE_INTTYPES_H
|
||||
|
||||
/* Define to 1 to use GnuTLS library for TLS/SSL-support. */
|
||||
#undef HAVE_LIBGNUTLS
|
||||
|
||||
/* Define to 1 if you have the `memcpy' function. */
|
||||
#undef HAVE_MEMCPY
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#undef HAVE_MEMORY_H
|
||||
|
||||
@@ -46,21 +96,56 @@
|
||||
/* Define to 1 if you have the <ncurses/ncurses.h> header file. */
|
||||
#undef HAVE_NCURSES_NCURSES_H
|
||||
|
||||
/* Define to 1 to use pragma pack directive in MessageBase.h */
|
||||
#undef HAVE_PRAGMA_PACK
|
||||
/* Define to 1 if you have the <ndir.h> header file, and it defines `DIR'. */
|
||||
#undef HAVE_NDIR_H
|
||||
|
||||
/* Define to 1 to use OpenSSL library for TLS/SSL-support. */
|
||||
#undef HAVE_OPENSSL
|
||||
|
||||
/* Define to 1 if you have the <regex.h> header file. */
|
||||
#undef HAVE_REGEX_H
|
||||
|
||||
/* Define to 1 if _SC_NPROCESSORS_ONLN is present in unistd.h */
|
||||
#undef HAVE_SC_NPROCESSORS_ONLN
|
||||
|
||||
/* Define to 1 if stdbool.h conforms to C99. */
|
||||
#undef HAVE_STDBOOL_H
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#undef HAVE_STDINT_H
|
||||
|
||||
/* Define to 1 if you have the <stdio.h> header file. */
|
||||
#undef HAVE_STDIO_H
|
||||
|
||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||
#undef HAVE_STDLIB_H
|
||||
|
||||
/* Define to 1 if you have the `strcasecmp' function. */
|
||||
#undef HAVE_STRCASECMP
|
||||
|
||||
/* Define to 1 if you have the `strchr' function. */
|
||||
#undef HAVE_STRCHR
|
||||
|
||||
/* Define to 1 if you have the `stricmp' function. */
|
||||
#undef HAVE_STRICMP
|
||||
|
||||
/* Define to 1 if you have the <strings.h> header file. */
|
||||
#undef HAVE_STRINGS_H
|
||||
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#undef HAVE_STRING_H
|
||||
|
||||
/* Define to 1 if you have the <sys/dir.h> header file, and it defines `DIR'.
|
||||
*/
|
||||
#undef HAVE_SYS_DIR_H
|
||||
|
||||
/* Define to 1 if you have the <sys/ndir.h> header file, and it defines `DIR'.
|
||||
*/
|
||||
#undef HAVE_SYS_NDIR_H
|
||||
|
||||
/* Define to 1 if you have the <sys/prctl.h> header file. */
|
||||
#undef HAVE_SYS_PRCTL_H
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#undef HAVE_SYS_STAT_H
|
||||
|
||||
@@ -73,6 +158,12 @@
|
||||
/* Define to 1 if variadic macros are supported */
|
||||
#undef HAVE_VARIADIC_MACROS
|
||||
|
||||
/* Define to 1 if the system has the type `_Bool'. */
|
||||
#undef HAVE__BOOL
|
||||
|
||||
/* Define to 1 to exclude debug-code */
|
||||
#undef NDEBUG
|
||||
|
||||
/* Name of package */
|
||||
#undef PACKAGE
|
||||
|
||||
@@ -91,8 +182,39 @@
|
||||
/* 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
|
||||
|
||||
/* Define to 1 if your processor stores words with the most significant byte
|
||||
first (like Motorola and SPARC, unlike Intel and VAX). */
|
||||
#undef WORDS_BIGENDIAN
|
||||
|
||||
/* Number of bits in a file offset, on hosts where this is settable. */
|
||||
#undef _FILE_OFFSET_BITS
|
||||
|
||||
/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
|
||||
#undef _LARGEFILE_SOURCE
|
||||
|
||||
/* Define for large files, on AIX-style hosts. */
|
||||
#undef _LARGE_FILES
|
||||
|
||||
/* Define to empty if `const' does not conform to ANSI C. */
|
||||
#undef const
|
||||
|
||||
/* Define to `__inline__' or `__inline' if that's what the C compiler
|
||||
calls it, or to nothing if 'inline' is not supported under any name. */
|
||||
#ifndef __cplusplus
|
||||
#undef inline
|
||||
#endif
|
||||
|
||||
/* Define to `unsigned int' if <sys/types.h> does not define. */
|
||||
#undef size_t
|
||||
|
||||
1616
config.sub
vendored
1616
config.sub
vendored
File diff suppressed because it is too large
Load Diff
650
configure.ac
650
configure.ac
@@ -1,42 +1,40 @@
|
||||
#
|
||||
# This file is part of nzbget
|
||||
#
|
||||
# Copyright (C) 2008-2015 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.
|
||||
#
|
||||
#
|
||||
|
||||
# -*- Autoconf -*-
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ(2.59)
|
||||
AC_INIT(nzbget, 0.3.1, hugbug@users.sourceforge.net)
|
||||
AM_INIT_AUTOMAKE(nzbget, 0.3.1)
|
||||
AC_CONFIG_SRCDIR([nzbget.cpp])
|
||||
AC_INIT(nzbget, 16.0, hugbug@users.sourceforge.net)
|
||||
AC_CONFIG_AUX_DIR(posix)
|
||||
AM_INIT_AUTOMAKE([foreign])
|
||||
AC_CONFIG_SRCDIR([daemon/main/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 +42,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 +66,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,8 +73,21 @@ dnl
|
||||
dnl Getopt
|
||||
dnl
|
||||
AC_CHECK_FUNC(getopt_long,
|
||||
[AC_DEFINE([HAVE_GETOPT_LONG], 1, [Define to 1 if getopt_long is supported])],
|
||||
[AC_LIBOBJ(getopt) AC_LIBOBJ(getopt1)])
|
||||
[AC_DEFINE([HAVE_GETOPT_LONG], 1, [Define to 1 if getopt_long is supported])],)
|
||||
|
||||
|
||||
dnl
|
||||
dnl fsync
|
||||
dnl
|
||||
AC_CHECK_FUNC(fdatasync,
|
||||
[AC_DEFINE([HAVE_FDATASYNC], 1, [Define to 1 if fdatasync is supported])],)
|
||||
AC_CHECK_DECL(F_FULLFSYNC,
|
||||
[AC_DEFINE([HAVE_FULLFSYNC], 1, [Define to 1 if F_FULLFSYNC is supported])],,[#include <fcntl.h>])
|
||||
|
||||
dnl
|
||||
dnl use 64-Bits for file sizes
|
||||
dnl
|
||||
AC_SYS_LARGEFILE
|
||||
|
||||
|
||||
dnl
|
||||
@@ -95,6 +98,7 @@ AC_TRY_COMPILE(
|
||||
[#include <time.h>],
|
||||
[ time_t clock; char buf[26]; ctime_r(&clock, buf, 26); ],
|
||||
AC_MSG_RESULT([[yes, and it takes 3 arguments]])
|
||||
FOUND="yes"
|
||||
AC_DEFINE([HAVE_CTIME_R_3], 1, [Define to 1 if ctime_r takes 3 arguments]),
|
||||
FOUND="no")
|
||||
if test "$FOUND" = "no"; then
|
||||
@@ -108,93 +112,143 @@ AC_TRY_COMPILE(
|
||||
fi
|
||||
if test "$FOUND" = "no"; then
|
||||
AC_MSG_RESULT([no])
|
||||
AC_MSG_ERROR("function ctime_r not found.")
|
||||
AC_MSG_ERROR("function ctime_r not found")
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl check gethostbyname_r
|
||||
dnl check getaddrinfo
|
||||
dnl
|
||||
AC_MSG_CHECKING(for gethostbyname_r)
|
||||
AC_TRY_COMPILE(
|
||||
[#include <netdb.h>],
|
||||
[ char* szHost; struct hostent hinfobuf; char* strbuf; int h_errnop;
|
||||
struct hostent* hinfo = gethostbyname_r(szHost, &hinfobuf, strbuf, 1024, &h_errnop); ],
|
||||
AC_MSG_RESULT([[yes, and it takes 5 arguments]])
|
||||
FOUND="yes"
|
||||
AC_DEFINE([HAVE_GETHOSTBYNAME_R_5], 1, [Define to 1 if gethostbyname_r takes 5 arguments]),
|
||||
FOUND="no")
|
||||
if test "$FOUND" = "no"; then
|
||||
AC_TRY_COMPILE(
|
||||
[#include <netdb.h>],
|
||||
[ char* szHost; struct hostent* hinfo; struct hostent hinfobuf; char* strbuf; int h_errnop;
|
||||
int err = gethostbyname_r(szHost, &hinfobuf, strbuf, 1024, &hinfo, &h_errnop); ],
|
||||
AC_MSG_RESULT([[yes, and it takes 6 arguments]])
|
||||
AC_CHECK_FUNC(getaddrinfo,
|
||||
FOUND="yes"
|
||||
AC_DEFINE([HAVE_GETHOSTBYNAME_R_6], 1, [Define to 1 if gethostbyname_r takes 6 arguments]),
|
||||
[AC_DEFINE([HAVE_GETADDRINFO], 1, [Define to 1 if getaddrinfo is supported])]
|
||||
AC_SEARCH_LIBS([getaddrinfo], [nsl]),
|
||||
FOUND="no")
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl check gethostbyname_r, if getaddrinfo is not available
|
||||
dnl
|
||||
if test "$FOUND" = "no"; then
|
||||
AC_MSG_RESULT([no])
|
||||
AC_MSG_ERROR("function gethostbyname_r not found.")
|
||||
AC_MSG_CHECKING(for gethostbyname_r)
|
||||
|
||||
AC_TRY_COMPILE(
|
||||
[#include <netdb.h>],
|
||||
[ char* szHost; struct hostent hinfobuf; char* strbuf; int h_errnop;
|
||||
struct hostent* hinfo = gethostbyname_r(szHost, &hinfobuf, strbuf, 1024, &h_errnop); ],
|
||||
AC_MSG_RESULT([[yes, and it takes 5 arguments]])
|
||||
FOUND="yes"
|
||||
AC_DEFINE([HAVE_GETHOSTBYNAME_R_5], 1, [Define to 1 if gethostbyname_r takes 5 arguments]),
|
||||
FOUND="no")
|
||||
|
||||
if test "$FOUND" = "no"; then
|
||||
AC_TRY_COMPILE(
|
||||
[#include <netdb.h>],
|
||||
[ char* szHost; struct hostent* hinfo; struct hostent hinfobuf; char* strbuf; int h_errnop;
|
||||
int err = gethostbyname_r(szHost, &hinfobuf, strbuf, 1024, &hinfo, &h_errnop); ],
|
||||
AC_MSG_RESULT([[yes, and it takes 6 arguments]])
|
||||
FOUND="yes"
|
||||
AC_DEFINE([HAVE_GETHOSTBYNAME_R_6], 1, [Define to 1 if gethostbyname_r takes 6 arguments]),
|
||||
FOUND="no")
|
||||
fi
|
||||
|
||||
if test "$FOUND" = "no"; then
|
||||
AC_TRY_COMPILE(
|
||||
[#include <netdb.h>],
|
||||
[ char* szHost; struct hostent hinfo; struct hostent_data hinfobuf;
|
||||
int err = gethostbyname_r(szHost, &hinfo, &hinfobuf); ],
|
||||
AC_MSG_RESULT([[yes, and it takes 3 arguments]])
|
||||
FOUND="yes"
|
||||
AC_DEFINE([HAVE_GETHOSTBYNAME_R_3], 1, [Define to 1 if gethostbyname_r takes 3 arguments]),
|
||||
AC_MSG_RESULT([[no]])
|
||||
FOUND="no")
|
||||
fi
|
||||
|
||||
if test "$FOUND" = "yes"; then
|
||||
AC_DEFINE([HAVE_GETHOSTBYNAME_R], 1, [Define to 1 if gethostbyname_r is supported])
|
||||
AC_SEARCH_LIBS([gethostbyname_r], [nsl])
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl check for __FUNCTION__ or __func__ macro
|
||||
dnl Determine what socket length (socklen_t) data type is
|
||||
dnl
|
||||
AC_MSG_CHECKING(for __FUNCTION__ macro)
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_TRY_COMPILE([#include <stdio.h>], [printf("%s\n", __FUNCTION__);],
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_DEFINE([FUNCTION_MACRO_NAME],[__FUNCTION__],[Define to the name of macro which returns the name of funtion being compiled])
|
||||
HAVE_FUNCTION_MACRO=yes,
|
||||
AC_MSG_RESULT([no]))
|
||||
AC_LANG_POP(C++)
|
||||
if test "$HAVE_FUNCTION_MACRO" != "yes"; then
|
||||
AC_MSG_CHECKING(for __func__ macro)
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_TRY_COMPILE([#include <stdio.h>], [printf("%s\n", __func__);],
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_DEFINE([FUNCTION_MACRO_NAME],[__func__],[Define to the name of macro which returns the name of function being compiled])
|
||||
HAVE_FUNCTION_MACRO=yes,
|
||||
AC_MSG_RESULT([no]))
|
||||
AC_LANG_POP(C++)
|
||||
fi
|
||||
if test "$HAVE_FUNCTION_MACRO" != "yes"; then
|
||||
AC_DEFINE([FUNCTION_MACRO_NAME],[NULL],[Define to the name of macro which returns the name of function being compiled])
|
||||
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 Dir-browser's snapshot
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether dir-browser snapshot workaround is needed)
|
||||
if test "$target_vendor" == "apple"; then
|
||||
AC_MSG_RESULT([[yes]])
|
||||
AC_DEFINE([DIRBROWSER_SNAPSHOT], 1, [Define to 1 if deleting of files during reading of directory is not properly supported by OS])
|
||||
else
|
||||
AC_MSG_RESULT([[no]])
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl check for pragma pack
|
||||
dnl check cpu cores via sysconf
|
||||
dnl
|
||||
AC_MSG_CHECKING(for pragma pack)
|
||||
AC_TRY_COMPILE([#pragma pack(1)
|
||||
#pragma pack()],,
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_DEFINE([HAVE_PRAGMA_PACK],1,[Define to 1 to use pragma pack directive in MessageBase.h]),
|
||||
AC_MSG_RESULT([no]))
|
||||
AC_MSG_CHECKING(for cpu cores via sysconf)
|
||||
AC_TRY_COMPILE(
|
||||
[#include <unistd.h>],
|
||||
[ int a = _SC_NPROCESSORS_ONLN; ],
|
||||
FOUND="yes"
|
||||
AC_MSG_RESULT([[yes]])
|
||||
AC_DEFINE([HAVE_SC_NPROCESSORS_ONLN], 1, [Define to 1 if _SC_NPROCESSORS_ONLN is present in unistd.h]),
|
||||
FOUND="no")
|
||||
|
||||
|
||||
dnl
|
||||
dnl checks for libxml2 includes and libraries.
|
||||
dnl
|
||||
INCVAL="${LIBPREF}/include/libxml2"
|
||||
LIBVAL="${LIBPREF}/lib"
|
||||
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"],
|
||||
AC_MSG_ERROR("libxml2 library not found"))
|
||||
fi
|
||||
AC_CHECK_HEADER(libxml/tree.h,,
|
||||
AC_MSG_ERROR("libxml2 header files not found"))
|
||||
AC_SEARCH_LIBS([xmlNewNode], [xml2], ,
|
||||
AC_MSG_ERROR("libxml2 library not found in $LIBVAL."))
|
||||
AC_MSG_ERROR("libxml2 library not found"))
|
||||
|
||||
|
||||
dnl
|
||||
@@ -202,20 +256,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}"
|
||||
|
||||
@@ -236,7 +289,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]))
|
||||
@@ -246,104 +299,208 @@ fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl Use uulib. Deafult: no
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to use uulib for decoding and joining)
|
||||
AC_ARG_ENABLE(uulib,
|
||||
[ --enable-uulib use uulib for decoding and joining],
|
||||
[ ENABLEUULIB=$enableval ],
|
||||
[ ENABLEUULIB=no] )
|
||||
AC_MSG_RESULT($ENABLEUULIB)
|
||||
if test "$ENABLEUULIB" = "yes"; then
|
||||
AC_DEFINE([ENABLE_UULIB],1,[Define to 1 to include support for uulib])
|
||||
|
||||
dnl
|
||||
dnl checks for uulib includes and libraries.
|
||||
dnl
|
||||
INCVAL="${LIBPREF}/include"
|
||||
LIBVAL="${LIBPREF}/lib"
|
||||
AC_ARG_WITH(uulib_includes,
|
||||
[ --with-uulib-includes=DIR uulib include directory],
|
||||
[INCVAL="$withval"])
|
||||
|
||||
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
|
||||
AC_CHECK_HEADER($INCVAL/uudeview.h,,
|
||||
AC_MSG_ERROR("uulib header files were not found in $INCVAL."))
|
||||
|
||||
AC_ARG_WITH(uulib_libraries,
|
||||
[ --with-uulib-libraries=DIR uulib library directory],
|
||||
[LIBVAL="$withval"])
|
||||
|
||||
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
|
||||
|
||||
AC_SEARCH_LIBS([UUInitialize], [uu],,
|
||||
AC_MSG_ERROR("uulib library not found in $LIBVAL."))
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl Use lib2par for par-checking. Deafult: no
|
||||
dnl Use par-checking. Deafult: yes.
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to include code for par-checking)
|
||||
AC_ARG_ENABLE(parcheck,
|
||||
[ --enable-parcheck include code for par-checking],
|
||||
[AS_HELP_STRING([--disable-parcheck], [do not include par-check/-repair-support])],
|
||||
[ ENABLEPARCHECK=$enableval ],
|
||||
[ ENABLEPARCHECK=yes] )
|
||||
AC_MSG_RESULT($ENABLEPARCHECK)
|
||||
if test "$ENABLEPARCHECK" = "yes"; then
|
||||
|
||||
dnl PAR2 checks.
|
||||
dnl
|
||||
dnl checks for libsigc++ includes and libraries (required for libpar2).
|
||||
dnl
|
||||
INCVAL="${LIBPREF}/include/sigc++-2.0"
|
||||
LIBVAL="${LIBPREF}/lib"
|
||||
AC_ARG_WITH(libsigc_includes,
|
||||
[ --with-libsigc-includes=DIR libsigc++-2.0 include directory],
|
||||
[INCVAL="$withval"])
|
||||
|
||||
AC_ARG_WITH(libsigc_libraries,
|
||||
[ --with-libsigc-libraries=DIR libsigc++-2.0 library directory],
|
||||
[LIBVAL="$withval"])
|
||||
dnl Checks for header files.
|
||||
AC_HEADER_DIRENT
|
||||
AC_HEADER_STDBOOL
|
||||
AC_HEADER_STDC
|
||||
AC_CHECK_HEADERS([stdio.h] [endian.h] [getopt.h])
|
||||
dnl Checks for typedefs, structures, and compiler characteristics.
|
||||
AC_TYPE_SIZE_T
|
||||
AC_C_BIGENDIAN
|
||||
AC_C_CONST
|
||||
AC_C_INLINE
|
||||
AC_FUNC_FSEEKO
|
||||
dnl Checks for library functions.
|
||||
AC_FUNC_MEMCMP
|
||||
AC_CHECK_FUNCS([stricmp] [strcasecmp])
|
||||
AC_CHECK_FUNCS([strchr] [memcpy])
|
||||
AC_CHECK_FUNCS([getopt])
|
||||
AM_CONDITIONAL(WITH_PAR2, true)
|
||||
else
|
||||
AC_DEFINE([DISABLE_PARCHECK],1,[Define to 1 to disable par-verification and repair])
|
||||
AM_CONDITIONAL(WITH_PAR2, false)
|
||||
fi
|
||||
|
||||
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
|
||||
CPPFLAGS="${CPPFLAGS} -I${INCVAL} -I${LIBVAL}/sigc++-2.0/include"
|
||||
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_CHECK_HEADER(sigc++/type_traits.h,,
|
||||
AC_MSG_ERROR("libsigc++-2.0 header files were not found in $INCVAL."))
|
||||
AC_LANG_POP(C++)
|
||||
dnl
|
||||
dnl Use TLS/SSL. Deafult: yes
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to use TLS/SSL)
|
||||
AC_ARG_ENABLE(tls,
|
||||
[AS_HELP_STRING([--disable-tls], [do not use TLS/SSL (removes dependency from TLS/SSL-libraries)])],
|
||||
[ USETLS=$enableval ],
|
||||
[ USETLS=yes] )
|
||||
AC_MSG_RESULT($USETLS)
|
||||
if test "$USETLS" = "yes"; then
|
||||
AC_ARG_WITH(tlslib,
|
||||
[AS_HELP_STRING([--with-tlslib=(OpenSSL, GnuTLS)], [TLS/SSL library to use])],
|
||||
[TLSLIB="$withval"])
|
||||
if test "$TLSLIB" != "GnuTLS" -a "$TLSLIB" != "OpenSSL" -a "$TLSLIB" != ""; then
|
||||
AC_MSG_ERROR([Invalid argument for option --with-tlslib])
|
||||
fi
|
||||
|
||||
dnl
|
||||
dnl checks for libpar2 includes and libraries.
|
||||
dnl
|
||||
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"],
|
||||
FOUND=no)
|
||||
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" = "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],
|
||||
FOUND=yes,
|
||||
FOUND=no)
|
||||
if test "$FOUND" = "yes"; then
|
||||
dnl gcrypt is optional
|
||||
AC_MSG_CHECKING([whether gcrypt is needed])
|
||||
AC_TRY_COMPILE(
|
||||
[#include <gnutls/gnutls.h>]
|
||||
[#if GNUTLS_VERSION_NUMBER <= 0x020b00]
|
||||
[compile error]
|
||||
[#endif],
|
||||
[int a;],
|
||||
AC_MSG_RESULT([no])
|
||||
GCRYPT=no,
|
||||
AC_MSG_RESULT([yes])
|
||||
GCRYPT=yes)
|
||||
if test "$GCRYPT" = "yes"; then
|
||||
AC_CHECK_HEADER([gcrypt.h],
|
||||
AC_SEARCH_LIBS([gcry_control], [gnutls gcrypt],
|
||||
FOUND=yes,
|
||||
FOUND=no),
|
||||
FOUND=yes)
|
||||
fi
|
||||
fi
|
||||
if test "$FOUND" = "no" -a "$TLSLIB" = "GnuTLS"; then
|
||||
AC_MSG_ERROR([Couldn't find GnuTLS library])
|
||||
fi
|
||||
if test "$FOUND" = "yes"; then
|
||||
TLSLIB="GnuTLS"
|
||||
AC_DEFINE([HAVE_LIBGNUTLS],1,[Define to 1 to use GnuTLS library for TLS/SSL-support.])
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$TLSLIB" = ""; then
|
||||
if test "$TLSHEADERS" = ""; then
|
||||
AC_MSG_ERROR([Couldn't find neither OpenSSL nor GnuTLS headers (ssl.h or gnutls.h)])
|
||||
else
|
||||
AC_MSG_ERROR([Couldn't find neither OpenSSL nor GnuTLS library])
|
||||
fi
|
||||
fi
|
||||
else
|
||||
AC_DEFINE([DISABLE_TLS],1,[Define to 1 to not use TLS/SSL])
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl checks for zlib includes and libraries.
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to use gzip)
|
||||
AC_ARG_ENABLE(gzip,
|
||||
[AS_HELP_STRING([--disable-gzip], [disable gzip-compression/decompression (removes dependency from zlib-library)])],
|
||||
[USEZLIB=$enableval],
|
||||
[USEZLIB=yes] )
|
||||
AC_MSG_RESULT($USEZLIB)
|
||||
if test "$USEZLIB" = "yes"; then
|
||||
INCVAL="${LIBPREF}/include"
|
||||
LIBVAL="${LIBPREF}/lib"
|
||||
AC_ARG_WITH(libpar2_includes,
|
||||
[ --with-libpar2-includes=DIR libpar2 include directory],
|
||||
AC_ARG_WITH(zlib_includes,
|
||||
[AS_HELP_STRING([--with-zlib-includes=DIR], [zlib include directory])],
|
||||
[INCVAL="$withval"])
|
||||
|
||||
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
|
||||
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_CHECK_HEADER(libpar2/libpar2.h,,
|
||||
AC_MSG_ERROR("libpar2 header files were not found in $INCVAL."))
|
||||
AC_LANG_POP(C++)
|
||||
|
||||
AC_ARG_WITH(libpar2_libraries,
|
||||
[ --with-libpar2-libraries=DIR libpar2 library directory],
|
||||
AC_ARG_WITH(zlib_libraries,
|
||||
[AS_HELP_STRING([--with-zlib-libraries=DIR], [zlib 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_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_PARCHECK],1,[Define to 1 to disable smart par-verification and restoration])
|
||||
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
|
||||
|
||||
|
||||
@@ -352,20 +509,38 @@ dnl Debugging. Default: no
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to include all debugging code)
|
||||
AC_ARG_ENABLE(debug,
|
||||
[ --enable-debug enable debugging],
|
||||
[AS_HELP_STRING([--enable-debug], [enable debugging])],
|
||||
[ ENABLEDEBUG=$enableval ],
|
||||
[ ENABLEDEBUG=no] )
|
||||
if test "$ENABLEDEBUG" = "yes"; then
|
||||
AC_DEFINE([DEBUG],1,Define to 1 to include debug-code)
|
||||
if test "$CC" = "gcc"; then
|
||||
CXXFLAGS="-g -Wall"
|
||||
else
|
||||
CXXFLAGS=""
|
||||
fi
|
||||
fi
|
||||
AC_MSG_RESULT($ENABLEDEBUG)
|
||||
|
||||
|
||||
if test "$ENABLEDEBUG" = "yes"; then
|
||||
|
||||
dnl
|
||||
dnl Begin of debugging code
|
||||
dnl
|
||||
|
||||
AC_DEFINE([DEBUG],1,Define to 1 to include debug-code)
|
||||
|
||||
|
||||
dnl
|
||||
dnl check for __FUNCTION__ or __func__ macro
|
||||
dnl
|
||||
AC_MSG_CHECKING(for macro returning current function name)
|
||||
AC_TRY_COMPILE(
|
||||
[#include <stdio.h>], [printf("%s\n", __FUNCTION__);],
|
||||
AC_MSG_RESULT(__FUNCTION__)
|
||||
FUNCTION_MACRO_NAME=__FUNCTION__,
|
||||
AC_TRY_COMPILE([#include <stdio.h>], [printf("%s\n", __func__);],
|
||||
AC_MSG_RESULT(__func__)
|
||||
FUNCTION_MACRO_NAME=__func__,
|
||||
AC_MSG_RESULT(none)))
|
||||
if test "$FUNCTION_MACRO_NAME" != ""; then
|
||||
AC_DEFINE_UNQUOTED(FUNCTION_MACRO_NAME, $FUNCTION_MACRO_NAME, [Define to the name of macro which returns the name of function being compiled])
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl variadic macros
|
||||
dnl
|
||||
@@ -380,15 +555,58 @@ 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
|
||||
else
|
||||
AC_DEFINE([NDEBUG],1,Define to 1 to exclude debug-code)
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl Enable test suite. Deafult: no.
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to enable unit and integration tests)
|
||||
AC_ARG_ENABLE(tests,
|
||||
[AS_HELP_STRING([--enable-tests], [enable unit and integration tests])],
|
||||
[ ENABLETESTS=$enableval ],
|
||||
[ ENABLETESTS=no] )
|
||||
AC_MSG_RESULT($ENABLETESTS)
|
||||
if test "$ENABLETESTS" = "yes"; then
|
||||
AC_DEFINE([ENABLE_TESTS],1,[Define to 1 to enable unit and integration tests])
|
||||
AM_CONDITIONAL(WITH_TESTS, true)
|
||||
else
|
||||
AM_CONDITIONAL(WITH_TESTS, false)
|
||||
fi
|
||||
|
||||
AC_CONFIG_FILES([Makefile])
|
||||
AC_OUTPUT
|
||||
|
||||
1088
daemon/connect/Connection.cpp
Normal file
1088
daemon/connect/Connection.cpp
Normal file
File diff suppressed because it is too large
Load Diff
148
daemon/connect/Connection.h
Normal file
148
daemon/connect/Connection.h
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2015 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 CONNECTION_H
|
||||
#define CONNECTION_H
|
||||
|
||||
#ifndef HAVE_GETADDRINFO
|
||||
#ifndef HAVE_GETHOSTBYNAME_R
|
||||
#include "Thread.h"
|
||||
#endif
|
||||
#endif
|
||||
#ifndef DISABLE_TLS
|
||||
#include "TLS.h"
|
||||
#endif
|
||||
|
||||
class Connection
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
csConnected,
|
||||
csDisconnected,
|
||||
csListening,
|
||||
csCancelled
|
||||
};
|
||||
|
||||
protected:
|
||||
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;
|
||||
char m_szRemoteAddr[20];
|
||||
int m_iTotalBytesRead;
|
||||
bool m_bBroken;
|
||||
bool m_bGracefull;
|
||||
|
||||
struct SockAddr
|
||||
{
|
||||
int ai_family;
|
||||
int ai_socktype;
|
||||
int ai_protocol;
|
||||
bool operator==(const SockAddr& rhs) const
|
||||
{ return memcmp(this, &rhs, sizeof(SockAddr)) == 0; }
|
||||
};
|
||||
|
||||
#ifndef DISABLE_TLS
|
||||
class ConTLSSocket: public TLSSocket
|
||||
{
|
||||
private:
|
||||
Connection* m_pOwner;
|
||||
protected:
|
||||
virtual void PrintError(const char* szErrMsg) { m_pOwner->PrintError(szErrMsg); }
|
||||
public:
|
||||
ConTLSSocket(SOCKET iSocket, bool bIsClient, const char* szCertFile,
|
||||
const char* szKeyFile, const char* szCipher, Connection* pOwner):
|
||||
TLSSocket(iSocket, bIsClient, szCertFile, szKeyFile, szCipher), m_pOwner(pOwner) {}
|
||||
};
|
||||
|
||||
ConTLSSocket* 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);
|
||||
virtual void PrintError(const char* szErrMsg);
|
||||
bool DoConnect();
|
||||
bool DoDisconnect();
|
||||
bool InitSocketOpts();
|
||||
bool ConnectWithTimeout(void* address, int address_len);
|
||||
#ifndef HAVE_GETADDRINFO
|
||||
unsigned int ResolveHostAddr(const char* szHost);
|
||||
#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(const char* szHost, int iPort, bool bTLS);
|
||||
virtual ~Connection();
|
||||
static void Init();
|
||||
static void Final();
|
||||
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);
|
||||
Connection* Accept();
|
||||
void Cancel();
|
||||
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);
|
||||
bool GetSuppressErrors() { return m_bSuppressErrors; }
|
||||
const char* GetRemoteAddr();
|
||||
bool GetGracefull() { return m_bGracefull; }
|
||||
void SetGracefull(bool bGracefull) { m_bGracefull = bGracefull; }
|
||||
#ifndef DISABLE_TLS
|
||||
bool StartTLS(bool bIsClient, const char* szCertFile, const char* szKeyFile);
|
||||
#endif
|
||||
int FetchTotalBytesRead();
|
||||
};
|
||||
|
||||
#endif
|
||||
565
daemon/connect/TLS.cpp
Normal file
565
daemon/connect/TLS.cpp
Normal file
@@ -0,0 +1,565 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2008-2015 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>
|
||||
#include <stdio.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)
|
||||
{
|
||||
char szMessage[1024];
|
||||
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
const char* errstr = gnutls_strerror(m_iRetCode);
|
||||
if (m_bSuppressErrors)
|
||||
{
|
||||
debug("%s: %s", szErrMsg, errstr);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(szMessage, sizeof(szMessage), "%s: %s", szErrMsg, errstr);
|
||||
szMessage[sizeof(szMessage) - 1] = '\0';
|
||||
PrintError(szMessage);
|
||||
}
|
||||
#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)
|
||||
{
|
||||
snprintf(szMessage, sizeof(szMessage), "%s: %s", szErrMsg, errstr);
|
||||
szMessage[sizeof(szMessage) - 1] = '\0';
|
||||
PrintError(szMessage);
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintError(szErrMsg);
|
||||
}
|
||||
} while (errcode);
|
||||
#endif /* HAVE_OPENSSL */
|
||||
}
|
||||
|
||||
void TLSSocket::PrintError(const char* szErrMsg)
|
||||
{
|
||||
error("%s", szErrMsg);
|
||||
}
|
||||
|
||||
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
|
||||
65
daemon/connect/TLS.h
Normal file
65
daemon/connect/TLS.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2008-2015 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);
|
||||
|
||||
protected:
|
||||
virtual void PrintError(const char* szErrMsg);
|
||||
|
||||
public:
|
||||
TLSSocket(SOCKET iSocket, bool bIsClient, const char* szCertFile, const char* szKeyFile, const char* szCipher);
|
||||
virtual ~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
|
||||
762
daemon/connect/WebDownloader.cpp
Normal file
762
daemon/connect/WebDownloader.cpp
Normal file
@@ -0,0 +1,762 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2012-2015 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"
|
||||
|
||||
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 = WebUtil::URLEncode(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;
|
||||
}
|
||||
|
||||
EStatus Status = adFailed;
|
||||
|
||||
while (!IsStopped() && iRemainedDownloadRetries > 0 && iRemainedConnectRetries > 0)
|
||||
{
|
||||
SetLastUpdateTimeNow();
|
||||
|
||||
Status = DownloadWithRedirects(5);
|
||||
|
||||
if ((((Status == adFailed) && (iRemainedDownloadRetries > 1)) ||
|
||||
((Status == adConnectError) && (iRemainedConnectRetries > 1)))
|
||||
&& !IsStopped() && !(!m_bForce && g_pOptions->GetPauseDownload()))
|
||||
{
|
||||
detail("Waiting %i sec to retry", g_pOptions->GetRetryInterval());
|
||||
int msec = 0;
|
||||
while (!IsStopped() && (msec < g_pOptions->GetRetryInterval() * 1000) &&
|
||||
!(!m_bForce && g_pOptions->GetPauseDownload()))
|
||||
{
|
||||
usleep(100 * 1000);
|
||||
msec += 100;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsStopped() || (!m_bForce && g_pOptions->GetPauseDownload()))
|
||||
{
|
||||
Status = adRetry;
|
||||
break;
|
||||
}
|
||||
|
||||
if (Status == adFinished || Status == adFatalError || Status == adNotFound)
|
||||
{
|
||||
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->SetTimeout(g_pOptions->GetUrlTimeout());
|
||||
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::DownloadWithRedirects(int iMaxRedirects)
|
||||
{
|
||||
// do sync download, following redirects
|
||||
EStatus eStatus = adRedirect;
|
||||
while (eStatus == adRedirect && iMaxRedirects >= 0)
|
||||
{
|
||||
iMaxRedirects--;
|
||||
eStatus = Download();
|
||||
}
|
||||
|
||||
if (eStatus == adRedirect && iMaxRedirects < 0)
|
||||
{
|
||||
warn("Too many redirects for %s", m_szInfoName);
|
||||
eStatus = adFailed;
|
||||
}
|
||||
|
||||
return eStatus;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if ((!strcasecmp(pUrl->GetProtocol(), "http") && (pUrl->GetPort() == 80 || pUrl->GetPort() == 0)) ||
|
||||
(!strcasecmp(pUrl->GetProtocol(), "https") && (pUrl->GetPort() == 443 || pUrl->GetPort() == 0)))
|
||||
{
|
||||
snprintf(tmp, 1024, "Host: %s\r\n", pUrl->GetHost());
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(tmp, 1024, "Host: %s:%i\r\n", pUrl->GetHost(), pUrl->GetPort());
|
||||
}
|
||||
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')
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
// Connection closed or timeout?
|
||||
if (iLen <= 0)
|
||||
{
|
||||
if (iLen == 0 && 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())
|
||||
{
|
||||
// redirect within host
|
||||
|
||||
char szResource[1024];
|
||||
URL oldUrl(m_szURL);
|
||||
|
||||
if (*szLocation == '/')
|
||||
{
|
||||
// absolute path within host
|
||||
strncpy(szResource, szLocation, 1024);
|
||||
szResource[1024-1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
// relative path within host
|
||||
strncpy(szResource, oldUrl.GetResource(), 1024);
|
||||
szResource[1024-1] = '\0';
|
||||
|
||||
char* p = strchr(szResource, '?');
|
||||
if (p)
|
||||
{
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
p = strrchr(szResource, '/');
|
||||
if (p)
|
||||
{
|
||||
p[1] = '\0';
|
||||
}
|
||||
|
||||
strncat(szResource, szLocation, 1024 - strlen(szResource));
|
||||
szResource[1024-1] = '\0';
|
||||
}
|
||||
|
||||
if (oldUrl.GetPort() > 0)
|
||||
{
|
||||
snprintf(szUrlBuf, 1024, "%s://%s:%i%s", oldUrl.GetProtocol(), oldUrl.GetHost(), oldUrl.GetPort(), szResource);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(szUrlBuf, 1024, "%s://%s%s", oldUrl.GetProtocol(), oldUrl.GetHost(), szResource);
|
||||
}
|
||||
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, FOPEN_WB);
|
||||
if (!m_pOutFile)
|
||||
{
|
||||
error("Could not %s file %s", "create", szFilename);
|
||||
return false;
|
||||
}
|
||||
if (g_pOptions->GetWriteBuffer() > 0)
|
||||
{
|
||||
setvbuf(m_pOutFile, NULL, _IOFBF, g_pOptions->GetWriteBuffer() * 1024);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
info(" 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
daemon/connect/WebDownloader.h
Normal file
112
daemon/connect/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;
|
||||
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();
|
||||
virtual ~WebDownloader();
|
||||
EStatus GetStatus() { return m_eStatus; }
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
EStatus Download();
|
||||
EStatus DownloadWithRedirects(int iMaxRedirects);
|
||||
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
|
||||
95
daemon/extension/FeedScript.cpp
Normal file
95
daemon/extension/FeedScript.cpp
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2015 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 <ctype.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "FeedScript.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
|
||||
void FeedScriptController::ExecuteScripts(const char* szFeedScript, const char* szFeedFile, int iFeedID)
|
||||
{
|
||||
FeedScriptController* pScriptController = new FeedScriptController();
|
||||
|
||||
pScriptController->m_szFeedFile = szFeedFile;
|
||||
pScriptController->m_iFeedID = iFeedID;
|
||||
|
||||
pScriptController->ExecuteScriptList(szFeedScript);
|
||||
|
||||
delete pScriptController;
|
||||
}
|
||||
|
||||
void FeedScriptController::ExecuteScript(ScriptConfig::Script* pScript)
|
||||
{
|
||||
if (!pScript->GetFeedScript())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PrintMessage(Message::mkInfo, "Executing feed-script %s for Feed%i", pScript->GetName(), m_iFeedID);
|
||||
|
||||
SetScript(pScript->GetLocation());
|
||||
SetArgs(NULL, false);
|
||||
|
||||
char szInfoName[1024];
|
||||
snprintf(szInfoName, 1024, "feed-script %s for Feed%i", pScript->GetName(), m_iFeedID);
|
||||
szInfoName[1024-1] = '\0';
|
||||
SetInfoName(szInfoName);
|
||||
|
||||
SetLogPrefix(pScript->GetDisplayName());
|
||||
PrepareParams(pScript->GetName());
|
||||
|
||||
Execute();
|
||||
|
||||
SetLogPrefix(NULL);
|
||||
}
|
||||
|
||||
void FeedScriptController::PrepareParams(const char* szScriptName)
|
||||
{
|
||||
ResetEnv();
|
||||
|
||||
SetEnvVar("NZBFP_FILENAME", m_szFeedFile);
|
||||
SetIntEnvVar("NZBFP_FEEDID", m_iFeedID);
|
||||
|
||||
PrepareEnvScript(NULL, szScriptName);
|
||||
}
|
||||
@@ -1,8 +1,7 @@
|
||||
/*
|
||||
* 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) 2015 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 +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,31 +23,24 @@
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
#ifndef FEEDSCRIPT_H
|
||||
#define FEEDSCRIPT_H
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
#include "NzbScript.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "NetAddress.h"
|
||||
|
||||
NetAddress::NetAddress(const char* szHost, int iPort)
|
||||
class FeedScriptController : public NZBScriptController
|
||||
{
|
||||
m_szHost = NULL;
|
||||
m_iPort = iPort;
|
||||
if (szHost)
|
||||
m_szHost = strdup(szHost);
|
||||
}
|
||||
private:
|
||||
const char* m_szFeedFile;
|
||||
int m_iFeedID;
|
||||
|
||||
NetAddress::~NetAddress()
|
||||
{
|
||||
if (m_szHost)
|
||||
free(m_szHost);
|
||||
m_szHost = NULL;
|
||||
}
|
||||
void PrepareParams(const char* szScriptName);
|
||||
|
||||
protected:
|
||||
virtual void ExecuteScript(ScriptConfig::Script* pScript);
|
||||
|
||||
public:
|
||||
static void ExecuteScripts(const char* szFeedScript, const char* szFeedFile, int iFeedID);
|
||||
};
|
||||
|
||||
#endif
|
||||
125
daemon/extension/NzbScript.cpp
Normal file
125
daemon/extension/NzbScript.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007-2015 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 <ctype.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
#include <algorithm>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "NzbScript.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
|
||||
/**
|
||||
* If szStripPrefix is not NULL, only pp-parameters, whose names start with the prefix
|
||||
* are processed. The prefix is then stripped from the names.
|
||||
* If szStripPrefix is NULL, all pp-parameters are processed; without stripping.
|
||||
*/
|
||||
void NZBScriptController::PrepareEnvParameters(NZBParameterList* pParameters, const char* szStripPrefix)
|
||||
{
|
||||
int iPrefixLen = szStripPrefix ? strlen(szStripPrefix) : 0;
|
||||
|
||||
for (NZBParameterList::iterator it = pParameters->begin(); it != pParameters->end(); it++)
|
||||
{
|
||||
NZBParameter* pParameter = *it;
|
||||
const char* szValue = pParameter->GetValue();
|
||||
|
||||
#ifdef WIN32
|
||||
char* szAnsiValue = strdup(szValue);
|
||||
WebUtil::Utf8ToAnsi(szAnsiValue, strlen(szAnsiValue) + 1);
|
||||
szValue = szAnsiValue;
|
||||
#endif
|
||||
|
||||
if (szStripPrefix && !strncmp(pParameter->GetName(), szStripPrefix, iPrefixLen) && (int)strlen(pParameter->GetName()) > iPrefixLen)
|
||||
{
|
||||
SetEnvVarSpecial("NZBPR", pParameter->GetName() + iPrefixLen, szValue);
|
||||
}
|
||||
else if (!szStripPrefix)
|
||||
{
|
||||
SetEnvVarSpecial("NZBPR", pParameter->GetName(), szValue);
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
free(szAnsiValue);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void NZBScriptController::PrepareEnvScript(NZBParameterList* pParameters, const char* szScriptName)
|
||||
{
|
||||
if (pParameters)
|
||||
{
|
||||
PrepareEnvParameters(pParameters, NULL);
|
||||
}
|
||||
|
||||
char szParamPrefix[1024];
|
||||
snprintf(szParamPrefix, 1024, "%s:", szScriptName);
|
||||
szParamPrefix[1024-1] = '\0';
|
||||
|
||||
if (pParameters)
|
||||
{
|
||||
PrepareEnvParameters(pParameters, szParamPrefix);
|
||||
}
|
||||
|
||||
PrepareEnvOptions(szParamPrefix);
|
||||
}
|
||||
|
||||
void NZBScriptController::ExecuteScriptList(const char* szScriptList)
|
||||
{
|
||||
for (ScriptConfig::Scripts::iterator it = g_pScriptConfig->GetScripts()->begin(); it != g_pScriptConfig->GetScripts()->end(); it++)
|
||||
{
|
||||
ScriptConfig::Script* pScript = *it;
|
||||
|
||||
if (szScriptList && *szScriptList)
|
||||
{
|
||||
// split szScriptList into tokens
|
||||
Tokenizer tok(szScriptList, ",;");
|
||||
while (const char* szScriptName = tok.Next())
|
||||
{
|
||||
if (Util::SameFilename(szScriptName, pScript->GetName()))
|
||||
{
|
||||
ExecuteScript(pScript);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
42
daemon/extension/NzbScript.h
Normal file
42
daemon/extension/NzbScript.h
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007-2015 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 NZBSCRIPT_H
|
||||
#define NZBSCRIPT_H
|
||||
|
||||
#include "Script.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "ScriptConfig.h"
|
||||
|
||||
class NZBScriptController : public ScriptController
|
||||
{
|
||||
protected:
|
||||
void PrepareEnvParameters(NZBParameterList* pParameters, const char* szStripPrefix);
|
||||
void PrepareEnvScript(NZBParameterList* pParameters, const char* szScriptName);
|
||||
void ExecuteScriptList(const char* szScriptList);
|
||||
virtual void ExecuteScript(ScriptConfig::Script* pScript) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
351
daemon/extension/PostScript.cpp
Normal file
351
daemon/extension/PostScript.cpp
Normal file
@@ -0,0 +1,351 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007-2015 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 <ctype.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "PostScript.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include "Options.h"
|
||||
|
||||
static const int POSTPROCESS_PARCHECK = 92;
|
||||
static const int POSTPROCESS_SUCCESS = 93;
|
||||
static const int POSTPROCESS_ERROR = 94;
|
||||
static const int POSTPROCESS_NONE = 95;
|
||||
|
||||
void PostScriptController::StartJob(PostInfo* pPostInfo)
|
||||
{
|
||||
PostScriptController* pScriptController = new PostScriptController();
|
||||
pScriptController->m_pPostInfo = pPostInfo;
|
||||
pScriptController->SetWorkingDir(g_pOptions->GetDestDir());
|
||||
pScriptController->SetAutoDestroy(false);
|
||||
pScriptController->m_iPrefixLen = 0;
|
||||
|
||||
pPostInfo->SetPostThread(pScriptController);
|
||||
|
||||
pScriptController->Start();
|
||||
}
|
||||
|
||||
void PostScriptController::Run()
|
||||
{
|
||||
StringBuilder scriptCommaList;
|
||||
|
||||
// the locking is needed for accessing the members of NZBInfo
|
||||
DownloadQueue::Lock();
|
||||
for (NZBParameterList::iterator it = m_pPostInfo->GetNZBInfo()->GetParameters()->begin(); it != m_pPostInfo->GetNZBInfo()->GetParameters()->end(); it++)
|
||||
{
|
||||
NZBParameter* pParameter = *it;
|
||||
const char* szVarname = pParameter->GetName();
|
||||
if (strlen(szVarname) > 0 && szVarname[0] != '*' && szVarname[strlen(szVarname)-1] == ':' &&
|
||||
(!strcasecmp(pParameter->GetValue(), "yes") || !strcasecmp(pParameter->GetValue(), "on") || !strcasecmp(pParameter->GetValue(), "1")))
|
||||
{
|
||||
char* szScriptName = strdup(szVarname);
|
||||
szScriptName[strlen(szScriptName)-1] = '\0'; // remove trailing ':'
|
||||
scriptCommaList.Append(szScriptName);
|
||||
scriptCommaList.Append(",");
|
||||
free(szScriptName);
|
||||
}
|
||||
}
|
||||
m_pPostInfo->GetNZBInfo()->GetScriptStatuses()->Clear();
|
||||
DownloadQueue::Unlock();
|
||||
|
||||
ExecuteScriptList(scriptCommaList.GetBuffer());
|
||||
|
||||
m_pPostInfo->SetStage(PostInfo::ptFinished);
|
||||
m_pPostInfo->SetWorking(false);
|
||||
}
|
||||
|
||||
void PostScriptController::ExecuteScript(ScriptConfig::Script* pScript)
|
||||
{
|
||||
// if any script has requested par-check, do not execute other scripts
|
||||
if (!pScript->GetPostScript() || m_pPostInfo->GetRequestParCheck())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PrintMessage(Message::mkInfo, "Executing post-process-script %s for %s", pScript->GetName(), m_pPostInfo->GetNZBInfo()->GetName());
|
||||
|
||||
char szProgressLabel[1024];
|
||||
snprintf(szProgressLabel, 1024, "Executing post-process-script %s", pScript->GetName());
|
||||
szProgressLabel[1024-1] = '\0';
|
||||
|
||||
DownloadQueue::Lock();
|
||||
m_pPostInfo->SetProgressLabel(szProgressLabel);
|
||||
DownloadQueue::Unlock();
|
||||
|
||||
SetScript(pScript->GetLocation());
|
||||
SetArgs(NULL, false);
|
||||
|
||||
char szInfoName[1024];
|
||||
snprintf(szInfoName, 1024, "post-process-script %s for %s", pScript->GetName(), m_pPostInfo->GetNZBInfo()->GetName());
|
||||
szInfoName[1024-1] = '\0';
|
||||
SetInfoName(szInfoName);
|
||||
|
||||
m_pScript = pScript;
|
||||
SetLogPrefix(pScript->GetDisplayName());
|
||||
m_iPrefixLen = strlen(pScript->GetDisplayName()) + 2; // 2 = strlen(": ");
|
||||
PrepareParams(pScript->GetName());
|
||||
|
||||
int iExitCode = Execute();
|
||||
|
||||
szInfoName[0] = 'P'; // uppercase
|
||||
|
||||
SetLogPrefix(NULL);
|
||||
ScriptStatus::EStatus eStatus = AnalyseExitCode(iExitCode);
|
||||
|
||||
// the locking is needed for accessing the members of NZBInfo
|
||||
DownloadQueue::Lock();
|
||||
m_pPostInfo->GetNZBInfo()->GetScriptStatuses()->Add(pScript->GetName(), eStatus);
|
||||
DownloadQueue::Unlock();
|
||||
}
|
||||
|
||||
void PostScriptController::PrepareParams(const char* szScriptName)
|
||||
{
|
||||
// the locking is needed for accessing the members of NZBInfo
|
||||
DownloadQueue::Lock();
|
||||
|
||||
ResetEnv();
|
||||
|
||||
SetIntEnvVar("NZBPP_NZBID", m_pPostInfo->GetNZBInfo()->GetID());
|
||||
SetEnvVar("NZBPP_NZBNAME", m_pPostInfo->GetNZBInfo()->GetName());
|
||||
SetEnvVar("NZBPP_DIRECTORY", m_pPostInfo->GetNZBInfo()->GetDestDir());
|
||||
SetEnvVar("NZBPP_NZBFILENAME", m_pPostInfo->GetNZBInfo()->GetFilename());
|
||||
SetEnvVar("NZBPP_URL", m_pPostInfo->GetNZBInfo()->GetURL());
|
||||
SetEnvVar("NZBPP_FINALDIR", m_pPostInfo->GetNZBInfo()->GetFinalDir());
|
||||
SetEnvVar("NZBPP_CATEGORY", m_pPostInfo->GetNZBInfo()->GetCategory());
|
||||
SetIntEnvVar("NZBPP_HEALTH", m_pPostInfo->GetNZBInfo()->CalcHealth());
|
||||
SetIntEnvVar("NZBPP_CRITICALHEALTH", m_pPostInfo->GetNZBInfo()->CalcCriticalHealth(false));
|
||||
|
||||
SetEnvVar("NZBPP_DUPEKEY", m_pPostInfo->GetNZBInfo()->GetDupeKey());
|
||||
SetIntEnvVar("NZBPP_DUPESCORE", m_pPostInfo->GetNZBInfo()->GetDupeScore());
|
||||
|
||||
const char* szDupeModeName[] = { "SCORE", "ALL", "FORCE" };
|
||||
SetEnvVar("NZBPP_DUPEMODE", szDupeModeName[m_pPostInfo->GetNZBInfo()->GetDupeMode()]);
|
||||
|
||||
char szStatus[256];
|
||||
strncpy(szStatus, m_pPostInfo->GetNZBInfo()->MakeTextStatus(true), sizeof(szStatus));
|
||||
szStatus[256-1] = '\0';
|
||||
SetEnvVar("NZBPP_STATUS", szStatus);
|
||||
|
||||
char* szDetail = strchr(szStatus, '/');
|
||||
if (szDetail) *szDetail = '\0';
|
||||
SetEnvVar("NZBPP_TOTALSTATUS", szStatus);
|
||||
|
||||
const char* szScriptStatusName[] = { "NONE", "FAILURE", "SUCCESS" };
|
||||
SetEnvVar("NZBPP_SCRIPTSTATUS", szScriptStatusName[m_pPostInfo->GetNZBInfo()->GetScriptStatuses()->CalcTotalStatus()]);
|
||||
|
||||
// deprecated
|
||||
int iParStatus[] = { 0, 0, 1, 2, 3, 4 };
|
||||
NZBInfo::EParStatus eParStatus = m_pPostInfo->GetNZBInfo()->GetParStatus();
|
||||
// for downloads marked as bad and for deleted downloads pass par status "Failure"
|
||||
// for compatibility with older scripts which don't check "NZBPP_TOTALSTATUS"
|
||||
if (m_pPostInfo->GetNZBInfo()->GetDeleteStatus() != NZBInfo::dsNone ||
|
||||
m_pPostInfo->GetNZBInfo()->GetMarkStatus() == NZBInfo::ksBad)
|
||||
{
|
||||
eParStatus = NZBInfo::psFailure;
|
||||
}
|
||||
SetIntEnvVar("NZBPP_PARSTATUS", iParStatus[eParStatus]);
|
||||
|
||||
// deprecated
|
||||
int iUnpackStatus[] = { 0, 0, 1, 2, 3, 4 };
|
||||
SetIntEnvVar("NZBPP_UNPACKSTATUS", iUnpackStatus[m_pPostInfo->GetNZBInfo()->GetUnpackStatus()]);
|
||||
|
||||
// deprecated
|
||||
SetIntEnvVar("NZBPP_HEALTHDELETED", (int)m_pPostInfo->GetNZBInfo()->GetDeleteStatus() == NZBInfo::dsHealth);
|
||||
|
||||
SetIntEnvVar("NZBPP_TOTALARTICLES", (int)m_pPostInfo->GetNZBInfo()->GetTotalArticles());
|
||||
SetIntEnvVar("NZBPP_SUCCESSARTICLES", (int)m_pPostInfo->GetNZBInfo()->GetSuccessArticles());
|
||||
SetIntEnvVar("NZBPP_FAILEDARTICLES", (int)m_pPostInfo->GetNZBInfo()->GetFailedArticles());
|
||||
|
||||
for (ServerStatList::iterator it = m_pPostInfo->GetNZBInfo()->GetServerStats()->begin(); it != m_pPostInfo->GetNZBInfo()->GetServerStats()->end(); it++)
|
||||
{
|
||||
ServerStat* pServerStat = *it;
|
||||
|
||||
char szName[50];
|
||||
|
||||
snprintf(szName, 50, "NZBPP_SERVER%i_SUCCESSARTICLES", pServerStat->GetServerID());
|
||||
szName[50-1] = '\0';
|
||||
SetIntEnvVar(szName, pServerStat->GetSuccessArticles());
|
||||
|
||||
snprintf(szName, 50, "NZBPP_SERVER%i_FAILEDARTICLES", pServerStat->GetServerID());
|
||||
szName[50-1] = '\0';
|
||||
SetIntEnvVar(szName, pServerStat->GetFailedArticles());
|
||||
}
|
||||
|
||||
PrepareEnvScript(m_pPostInfo->GetNZBInfo()->GetParameters(), szScriptName);
|
||||
|
||||
DownloadQueue::Unlock();
|
||||
}
|
||||
|
||||
ScriptStatus::EStatus PostScriptController::AnalyseExitCode(int iExitCode)
|
||||
{
|
||||
// The ScriptStatus is accumulated for all scripts:
|
||||
// If any script has failed the status is "failure", etc.
|
||||
|
||||
switch (iExitCode)
|
||||
{
|
||||
case POSTPROCESS_SUCCESS:
|
||||
PrintMessage(Message::mkInfo, "%s successful", GetInfoName());
|
||||
return ScriptStatus::srSuccess;
|
||||
|
||||
case POSTPROCESS_ERROR:
|
||||
case -1: // Execute() returns -1 if the process could not be started (file not found or other problem)
|
||||
PrintMessage(Message::mkError, "%s failed", GetInfoName());
|
||||
return ScriptStatus::srFailure;
|
||||
|
||||
case POSTPROCESS_NONE:
|
||||
PrintMessage(Message::mkInfo, "%s skipped", GetInfoName());
|
||||
return ScriptStatus::srNone;
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
case POSTPROCESS_PARCHECK:
|
||||
if (m_pPostInfo->GetNZBInfo()->GetParStatus() > NZBInfo::psSkipped)
|
||||
{
|
||||
PrintMessage(Message::mkError, "%s requested par-check/repair, but the collection was already checked", GetInfoName());
|
||||
return ScriptStatus::srFailure;
|
||||
}
|
||||
else
|
||||
{
|
||||
PrintMessage(Message::mkInfo, "%s requested par-check/repair", GetInfoName());
|
||||
m_pPostInfo->SetRequestParCheck(true);
|
||||
m_pPostInfo->SetForceRepair(true);
|
||||
return ScriptStatus::srSuccess;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
PrintMessage(Message::mkError, "%s failed (terminated with unknown status)", GetInfoName());
|
||||
return ScriptStatus::srFailure;
|
||||
}
|
||||
}
|
||||
|
||||
void PostScriptController::AddMessage(Message::EKind eKind, const char* szText)
|
||||
{
|
||||
const char* szMsgText = szText + m_iPrefixLen;
|
||||
|
||||
if (!strncmp(szMsgText, "[NZB] ", 6))
|
||||
{
|
||||
debug("Command %s detected", szMsgText + 6);
|
||||
if (!strncmp(szMsgText + 6, "FINALDIR=", 9))
|
||||
{
|
||||
DownloadQueue::Lock();
|
||||
m_pPostInfo->GetNZBInfo()->SetFinalDir(szMsgText + 6 + 9);
|
||||
DownloadQueue::Unlock();
|
||||
}
|
||||
else if (!strncmp(szMsgText + 6, "DIRECTORY=", 10))
|
||||
{
|
||||
DownloadQueue::Lock();
|
||||
m_pPostInfo->GetNZBInfo()->SetDestDir(szMsgText + 6 + 10);
|
||||
DownloadQueue::Unlock();
|
||||
}
|
||||
else if (!strncmp(szMsgText + 6, "NZBPR_", 6))
|
||||
{
|
||||
char* szParam = strdup(szMsgText + 6 + 6);
|
||||
char* szValue = strchr(szParam, '=');
|
||||
if (szValue)
|
||||
{
|
||||
*szValue = '\0';
|
||||
DownloadQueue::Lock();
|
||||
m_pPostInfo->GetNZBInfo()->GetParameters()->SetParameter(szParam, szValue + 1);
|
||||
DownloadQueue::Unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pPostInfo->GetNZBInfo()->PrintMessage(Message::mkError,
|
||||
"Invalid command \"%s\" received from %s", szMsgText, GetInfoName());
|
||||
}
|
||||
free(szParam);
|
||||
}
|
||||
else if (!strncmp(szMsgText + 6, "MARK=BAD", 8))
|
||||
{
|
||||
SetLogPrefix(NULL);
|
||||
PrintMessage(Message::mkWarning, "Marking %s as bad", m_pPostInfo->GetNZBInfo()->GetName());
|
||||
SetLogPrefix(m_pScript->GetDisplayName());
|
||||
m_pPostInfo->GetNZBInfo()->SetMarkStatus(NZBInfo::ksBad);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pPostInfo->GetNZBInfo()->PrintMessage(Message::mkError,
|
||||
"Invalid command \"%s\" received from %s", szMsgText, GetInfoName());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pPostInfo->GetNZBInfo()->AddMessage(eKind, szText);
|
||||
DownloadQueue::Lock();
|
||||
m_pPostInfo->SetProgressLabel(szText);
|
||||
DownloadQueue::Unlock();
|
||||
}
|
||||
|
||||
if (g_pOptions->GetPausePostProcess() && !m_pPostInfo->GetNZBInfo()->GetForcePriority())
|
||||
{
|
||||
time_t tStageTime = m_pPostInfo->GetStageTime();
|
||||
time_t tStartTime = m_pPostInfo->GetStartTime();
|
||||
time_t tWaitTime = time(NULL);
|
||||
|
||||
// wait until Post-processor is unpaused
|
||||
while (g_pOptions->GetPausePostProcess() && !m_pPostInfo->GetNZBInfo()->GetForcePriority() && !IsStopped())
|
||||
{
|
||||
usleep(100 * 1000);
|
||||
|
||||
// update time stamps
|
||||
|
||||
time_t tDelta = time(NULL) - tWaitTime;
|
||||
|
||||
if (tStageTime > 0)
|
||||
{
|
||||
m_pPostInfo->SetStageTime(tStageTime + tDelta);
|
||||
}
|
||||
|
||||
if (tStartTime > 0)
|
||||
{
|
||||
m_pPostInfo->SetStartTime(tStartTime + tDelta);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PostScriptController::Stop()
|
||||
{
|
||||
debug("Stopping post-process-script");
|
||||
Thread::Stop();
|
||||
Terminate();
|
||||
}
|
||||
52
daemon/extension/PostScript.h
Normal file
52
daemon/extension/PostScript.h
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007-2015 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 POSTSCRIPT_H
|
||||
#define POSTSCRIPT_H
|
||||
|
||||
#include "Thread.h"
|
||||
#include "NzbScript.h"
|
||||
|
||||
class PostScriptController : public Thread, public NZBScriptController
|
||||
{
|
||||
private:
|
||||
PostInfo* m_pPostInfo;
|
||||
int m_iPrefixLen;
|
||||
ScriptConfig::Script* m_pScript;
|
||||
|
||||
void PrepareParams(const char* szScriptName);
|
||||
ScriptStatus::EStatus AnalyseExitCode(int iExitCode);
|
||||
|
||||
protected:
|
||||
virtual void ExecuteScript(ScriptConfig::Script* pScript);
|
||||
virtual void AddMessage(Message::EKind eKind, const char* szText);
|
||||
|
||||
public:
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
static void StartJob(PostInfo* pPostInfo);
|
||||
};
|
||||
|
||||
#endif
|
||||
525
daemon/extension/QueueScript.cpp
Normal file
525
daemon/extension/QueueScript.cpp
Normal file
@@ -0,0 +1,525 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007-2015 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 <ctype.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "QueueScript.h"
|
||||
#include "NzbScript.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
static const char* QUEUE_EVENT_NAMES[] = { "FILE_DOWNLOADED", "URL_COMPLETED", "NZB_ADDED", "NZB_DOWNLOADED", "NZB_DELETED" };
|
||||
|
||||
class QueueScriptController : public Thread, public NZBScriptController
|
||||
{
|
||||
private:
|
||||
char* m_szNZBName;
|
||||
char* m_szNZBFilename;
|
||||
char* m_szUrl;
|
||||
char* m_szCategory;
|
||||
char* m_szDestDir;
|
||||
int m_iID;
|
||||
int m_iPriority;
|
||||
char* m_szDupeKey;
|
||||
EDupeMode m_eDupeMode;
|
||||
int m_iDupeScore;
|
||||
NZBParameterList m_Parameters;
|
||||
int m_iPrefixLen;
|
||||
ScriptConfig::Script* m_pScript;
|
||||
QueueScriptCoordinator::EEvent m_eEvent;
|
||||
bool m_bMarkBad;
|
||||
NZBInfo::EDeleteStatus m_eDeleteStatus;
|
||||
NZBInfo::EUrlStatus m_eUrlStatus;
|
||||
|
||||
void PrepareParams(const char* szScriptName);
|
||||
|
||||
protected:
|
||||
virtual void ExecuteScript(ScriptConfig::Script* pScript);
|
||||
virtual void AddMessage(Message::EKind eKind, const char* szText);
|
||||
|
||||
public:
|
||||
virtual ~QueueScriptController();
|
||||
virtual void Run();
|
||||
static void StartScript(NZBInfo* pNZBInfo, ScriptConfig::Script* pScript, QueueScriptCoordinator::EEvent eEvent);
|
||||
};
|
||||
|
||||
|
||||
QueueScriptController::~QueueScriptController()
|
||||
{
|
||||
free(m_szNZBName);
|
||||
free(m_szNZBFilename);
|
||||
free(m_szUrl);
|
||||
free(m_szCategory);
|
||||
free(m_szDestDir);
|
||||
free(m_szDupeKey);
|
||||
}
|
||||
|
||||
void QueueScriptController::StartScript(NZBInfo* pNZBInfo, ScriptConfig::Script* pScript, QueueScriptCoordinator::EEvent eEvent)
|
||||
{
|
||||
QueueScriptController* pScriptController = new QueueScriptController();
|
||||
|
||||
pScriptController->m_szNZBName = strdup(pNZBInfo->GetName());
|
||||
pScriptController->m_szNZBFilename = strdup(pNZBInfo->GetFilename());
|
||||
pScriptController->m_szUrl = strdup(pNZBInfo->GetURL());
|
||||
pScriptController->m_szCategory = strdup(pNZBInfo->GetCategory());
|
||||
pScriptController->m_szDestDir = strdup(pNZBInfo->GetDestDir());
|
||||
pScriptController->m_iID = pNZBInfo->GetID();
|
||||
pScriptController->m_iPriority = pNZBInfo->GetPriority();
|
||||
pScriptController->m_szDupeKey = strdup(pNZBInfo->GetDupeKey());
|
||||
pScriptController->m_eDupeMode = pNZBInfo->GetDupeMode();
|
||||
pScriptController->m_iDupeScore = pNZBInfo->GetDupeScore();
|
||||
pScriptController->m_Parameters.CopyFrom(pNZBInfo->GetParameters());
|
||||
pScriptController->m_pScript = pScript;
|
||||
pScriptController->m_eEvent = eEvent;
|
||||
pScriptController->m_iPrefixLen = 0;
|
||||
pScriptController->m_bMarkBad = false;
|
||||
pScriptController->m_eDeleteStatus = pNZBInfo->GetDeleteStatus();
|
||||
pScriptController->m_eUrlStatus = pNZBInfo->GetUrlStatus();
|
||||
pScriptController->SetAutoDestroy(true);
|
||||
|
||||
pScriptController->Start();
|
||||
}
|
||||
|
||||
void QueueScriptController::Run()
|
||||
{
|
||||
ExecuteScript(m_pScript);
|
||||
|
||||
SetLogPrefix(NULL);
|
||||
|
||||
if (m_bMarkBad)
|
||||
{
|
||||
DownloadQueue* pDownloadQueue = DownloadQueue::Lock();
|
||||
NZBInfo* pNZBInfo = pDownloadQueue->GetQueue()->Find(m_iID);
|
||||
if (pNZBInfo)
|
||||
{
|
||||
PrintMessage(Message::mkWarning, "Cancelling download and deleting %s", m_szNZBName);
|
||||
pNZBInfo->SetDeleteStatus(NZBInfo::dsBad);
|
||||
pDownloadQueue->EditEntry(m_iID, DownloadQueue::eaGroupDelete, 0, NULL);
|
||||
}
|
||||
DownloadQueue::Unlock();
|
||||
}
|
||||
|
||||
g_pQueueScriptCoordinator->CheckQueue();
|
||||
}
|
||||
|
||||
void QueueScriptController::ExecuteScript(ScriptConfig::Script* pScript)
|
||||
{
|
||||
PrintMessage(m_eEvent == QueueScriptCoordinator::qeFileDownloaded ? Message::mkDetail : Message::mkInfo,
|
||||
"Executing queue-script %s for %s", pScript->GetName(), Util::BaseFileName(m_szNZBName));
|
||||
|
||||
SetScript(pScript->GetLocation());
|
||||
SetArgs(NULL, false);
|
||||
|
||||
char szInfoName[1024];
|
||||
snprintf(szInfoName, 1024, "queue-script %s for %s", pScript->GetName(), Util::BaseFileName(m_szNZBName));
|
||||
szInfoName[1024-1] = '\0';
|
||||
SetInfoName(szInfoName);
|
||||
|
||||
SetLogPrefix(pScript->GetDisplayName());
|
||||
m_iPrefixLen = strlen(pScript->GetDisplayName()) + 2; // 2 = strlen(": ");
|
||||
PrepareParams(pScript->GetName());
|
||||
|
||||
Execute();
|
||||
|
||||
SetLogPrefix(NULL);
|
||||
}
|
||||
|
||||
void QueueScriptController::PrepareParams(const char* szScriptName)
|
||||
{
|
||||
ResetEnv();
|
||||
|
||||
SetEnvVar("NZBNA_NZBNAME", m_szNZBName);
|
||||
SetIntEnvVar("NZBNA_NZBID", m_iID);
|
||||
SetEnvVar("NZBNA_FILENAME", m_szNZBFilename);
|
||||
SetEnvVar("NZBNA_DIRECTORY", m_szDestDir);
|
||||
SetEnvVar("NZBNA_URL", m_szUrl);
|
||||
SetEnvVar("NZBNA_CATEGORY", m_szCategory);
|
||||
SetIntEnvVar("NZBNA_PRIORITY", m_iPriority);
|
||||
SetIntEnvVar("NZBNA_LASTID", m_iID); // deprecated
|
||||
|
||||
SetEnvVar("NZBNA_DUPEKEY", m_szDupeKey);
|
||||
SetIntEnvVar("NZBNA_DUPESCORE", m_iDupeScore);
|
||||
|
||||
const char* szDupeModeName[] = { "SCORE", "ALL", "FORCE" };
|
||||
SetEnvVar("NZBNA_DUPEMODE", szDupeModeName[m_eDupeMode]);
|
||||
|
||||
SetEnvVar("NZBNA_EVENT", QUEUE_EVENT_NAMES[m_eEvent]);
|
||||
|
||||
const char* szDeleteStatusName[] = { "NONE", "MANUAL", "HEALTH", "DUPE", "BAD", "GOOD", "COPY", "SCAN" };
|
||||
SetEnvVar("NZBNA_DELETESTATUS", szDeleteStatusName[m_eDeleteStatus]);
|
||||
|
||||
const char* szUrlStatusName[] = { "NONE", "UNKNOWN", "SUCCESS", "FAILURE", "UNKNOWN", "SCAN_SKIPPED", "SCAN_FAILURE" };
|
||||
SetEnvVar("NZBNA_URLSTATUS", szUrlStatusName[m_eUrlStatus]);
|
||||
|
||||
PrepareEnvScript(&m_Parameters, szScriptName);
|
||||
}
|
||||
|
||||
void QueueScriptController::AddMessage(Message::EKind eKind, const char* szText)
|
||||
{
|
||||
const char* szMsgText = szText + m_iPrefixLen;
|
||||
|
||||
if (!strncmp(szMsgText, "[NZB] ", 6))
|
||||
{
|
||||
debug("Command %s detected", szMsgText + 6);
|
||||
if (!strncmp(szMsgText + 6, "NZBPR_", 6))
|
||||
{
|
||||
char* szParam = strdup(szMsgText + 6 + 6);
|
||||
char* szValue = strchr(szParam, '=');
|
||||
if (szValue)
|
||||
{
|
||||
*szValue = '\0';
|
||||
DownloadQueue* pDownloadQueue = DownloadQueue::Lock();
|
||||
NZBInfo* pNZBInfo = pDownloadQueue->GetQueue()->Find(m_iID);
|
||||
if (pNZBInfo)
|
||||
{
|
||||
pNZBInfo->GetParameters()->SetParameter(szParam, szValue + 1);
|
||||
}
|
||||
DownloadQueue::Unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Invalid command \"%s\" received from %s", szMsgText, GetInfoName());
|
||||
}
|
||||
free(szParam);
|
||||
}
|
||||
else if (!strncmp(szMsgText + 6, "MARK=BAD", 8))
|
||||
{
|
||||
m_bMarkBad = true;
|
||||
DownloadQueue* pDownloadQueue = DownloadQueue::Lock();
|
||||
NZBInfo* pNZBInfo = pDownloadQueue->GetQueue()->Find(m_iID);
|
||||
if (pNZBInfo)
|
||||
{
|
||||
SetLogPrefix(NULL);
|
||||
PrintMessage(Message::mkWarning, "Marking %s as bad", m_szNZBName);
|
||||
SetLogPrefix(m_pScript->GetDisplayName());
|
||||
pNZBInfo->SetMarkStatus(NZBInfo::ksBad);
|
||||
}
|
||||
DownloadQueue::Unlock();
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Invalid command \"%s\" received from %s", szMsgText, GetInfoName());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ScriptController::AddMessage(eKind, szText);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
QueueScriptCoordinator::QueueItem::QueueItem(int iNZBID, ScriptConfig::Script* pScript, EEvent eEvent)
|
||||
{
|
||||
m_iNZBID = iNZBID;
|
||||
m_pScript = pScript;
|
||||
m_eEvent = eEvent;
|
||||
}
|
||||
|
||||
QueueScriptCoordinator::QueueScriptCoordinator()
|
||||
{
|
||||
m_pCurItem = NULL;
|
||||
m_bStopped = false;
|
||||
}
|
||||
|
||||
QueueScriptCoordinator::~QueueScriptCoordinator()
|
||||
{
|
||||
delete m_pCurItem;
|
||||
for (Queue::iterator it = m_Queue.begin(); it != m_Queue.end(); it++ )
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
}
|
||||
|
||||
void QueueScriptCoordinator::InitOptions()
|
||||
{
|
||||
m_bHasQueueScripts = false;
|
||||
for (ScriptConfig::Scripts::iterator it = g_pScriptConfig->GetScripts()->begin(); it != g_pScriptConfig->GetScripts()->end(); it++)
|
||||
{
|
||||
ScriptConfig::Script* pScript = *it;
|
||||
if (pScript->GetQueueScript())
|
||||
{
|
||||
m_bHasQueueScripts = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QueueScriptCoordinator::EnqueueScript(NZBInfo* pNZBInfo, EEvent eEvent)
|
||||
{
|
||||
if (!m_bHasQueueScripts)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_mutexQueue.Lock();
|
||||
|
||||
if (eEvent == qeNzbDownloaded)
|
||||
{
|
||||
// delete all other queued scripts for this nzb
|
||||
for (Queue::iterator it = m_Queue.begin(); it != m_Queue.end(); )
|
||||
{
|
||||
QueueItem* pQueueItem = *it;
|
||||
if (pQueueItem->GetNZBID() == pNZBInfo->GetID())
|
||||
{
|
||||
delete pQueueItem;
|
||||
it = m_Queue.erase(it);
|
||||
continue;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
// respect option "EventInterval"
|
||||
time_t tCurTime = time(NULL);
|
||||
if (eEvent == qeFileDownloaded &&
|
||||
(g_pOptions->GetEventInterval() == -1 ||
|
||||
(g_pOptions->GetEventInterval() > 0 && tCurTime - pNZBInfo->GetQueueScriptTime() > 0 &&
|
||||
(int)(tCurTime - pNZBInfo->GetQueueScriptTime()) < g_pOptions->GetEventInterval())))
|
||||
{
|
||||
m_mutexQueue.Unlock();
|
||||
return;
|
||||
}
|
||||
|
||||
for (ScriptConfig::Scripts::iterator it = g_pScriptConfig->GetScripts()->begin(); it != g_pScriptConfig->GetScripts()->end(); it++)
|
||||
{
|
||||
ScriptConfig::Script* pScript = *it;
|
||||
|
||||
if (!pScript->GetQueueScript())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
bool bUseScript = false;
|
||||
|
||||
// check queue-scripts
|
||||
const char* szQueueScript = g_pOptions->GetQueueScript();
|
||||
if (!Util::EmptyStr(szQueueScript))
|
||||
{
|
||||
// split szQueueScript into tokens
|
||||
Tokenizer tok(szQueueScript, ",;");
|
||||
while (const char* szScriptName = tok.Next())
|
||||
{
|
||||
if (Util::SameFilename(szScriptName, pScript->GetName()))
|
||||
{
|
||||
bUseScript = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check post-processing-scripts
|
||||
if (!bUseScript)
|
||||
{
|
||||
for (NZBParameterList::iterator it = pNZBInfo->GetParameters()->begin(); it != pNZBInfo->GetParameters()->end(); it++)
|
||||
{
|
||||
NZBParameter* pParameter = *it;
|
||||
const char* szVarname = pParameter->GetName();
|
||||
if (strlen(szVarname) > 0 && szVarname[0] != '*' && szVarname[strlen(szVarname)-1] == ':' &&
|
||||
(!strcasecmp(pParameter->GetValue(), "yes") ||
|
||||
!strcasecmp(pParameter->GetValue(), "on") ||
|
||||
!strcasecmp(pParameter->GetValue(), "1")))
|
||||
{
|
||||
char szScriptName[1024];
|
||||
strncpy(szScriptName, szVarname, 1024);
|
||||
szScriptName[1024-1] = '\0';
|
||||
szScriptName[strlen(szScriptName)-1] = '\0'; // remove trailing ':'
|
||||
if (Util::SameFilename(szScriptName, pScript->GetName()))
|
||||
{
|
||||
bUseScript = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bUseScript &= Util::EmptyStr(pScript->GetQueueEvents()) || strstr(pScript->GetQueueEvents(), QUEUE_EVENT_NAMES[eEvent]);
|
||||
|
||||
if (bUseScript)
|
||||
{
|
||||
bool bAlreadyQueued = false;
|
||||
if (eEvent == qeFileDownloaded)
|
||||
{
|
||||
// check if this script is already queued for this nzb
|
||||
for (Queue::iterator it2 = m_Queue.begin(); it2 != m_Queue.end(); it2++)
|
||||
{
|
||||
QueueItem* pQueueItem = *it2;
|
||||
if (pQueueItem->GetNZBID() == pNZBInfo->GetID() && pQueueItem->GetScript() == pScript)
|
||||
{
|
||||
bAlreadyQueued = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!bAlreadyQueued)
|
||||
{
|
||||
QueueItem* pQueueItem = new QueueItem(pNZBInfo->GetID(), pScript, eEvent);
|
||||
if (m_pCurItem)
|
||||
{
|
||||
m_Queue.push_back(pQueueItem);
|
||||
}
|
||||
else
|
||||
{
|
||||
StartScript(pNZBInfo, pQueueItem);
|
||||
}
|
||||
}
|
||||
|
||||
pNZBInfo->SetQueueScriptTime(time(NULL));
|
||||
}
|
||||
}
|
||||
|
||||
m_mutexQueue.Unlock();
|
||||
}
|
||||
|
||||
NZBInfo* QueueScriptCoordinator::FindNZBInfo(DownloadQueue* pDownloadQueue, int iNZBID)
|
||||
{
|
||||
NZBInfo* pNZBInfo = pDownloadQueue->GetQueue()->Find(iNZBID);
|
||||
if (!pNZBInfo)
|
||||
{
|
||||
for (HistoryList::iterator it = pDownloadQueue->GetHistory()->begin(); it != pDownloadQueue->GetHistory()->end(); it++)
|
||||
{
|
||||
HistoryInfo* pHistoryInfo = *it;
|
||||
if (pHistoryInfo->GetNZBInfo() && pHistoryInfo->GetNZBInfo()->GetID() == iNZBID)
|
||||
{
|
||||
pNZBInfo = pHistoryInfo->GetNZBInfo();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return pNZBInfo;
|
||||
}
|
||||
|
||||
void QueueScriptCoordinator::CheckQueue()
|
||||
{
|
||||
if (m_bStopped)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
DownloadQueue* pDownloadQueue = DownloadQueue::Lock();
|
||||
m_mutexQueue.Lock();
|
||||
|
||||
delete m_pCurItem;
|
||||
|
||||
m_pCurItem = NULL;
|
||||
NZBInfo* pCurNZBInfo = NULL;
|
||||
Queue::iterator itCurItem = m_Queue.end();
|
||||
|
||||
for (Queue::iterator it = m_Queue.begin(); it != m_Queue.end(); )
|
||||
{
|
||||
QueueItem* pQueueItem = *it;
|
||||
|
||||
NZBInfo* pNZBInfo = FindNZBInfo(pDownloadQueue, pQueueItem->GetNZBID());
|
||||
|
||||
// in a case this nzb must not be processed further - delete queue script from queue
|
||||
if (!pNZBInfo ||
|
||||
(pNZBInfo->GetDeleteStatus() != NZBInfo::dsNone && pQueueItem->GetEvent() != qeNzbDeleted) ||
|
||||
pNZBInfo->GetMarkStatus() == NZBInfo::ksBad)
|
||||
{
|
||||
delete pQueueItem;
|
||||
it = m_Queue.erase(it);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!m_pCurItem || pQueueItem->GetEvent() > m_pCurItem->GetEvent())
|
||||
{
|
||||
m_pCurItem = pQueueItem;
|
||||
itCurItem = it;
|
||||
pCurNZBInfo = pNZBInfo;
|
||||
}
|
||||
|
||||
it++;
|
||||
}
|
||||
|
||||
if (m_pCurItem)
|
||||
{
|
||||
m_Queue.erase(itCurItem);
|
||||
StartScript(pCurNZBInfo, m_pCurItem);
|
||||
}
|
||||
|
||||
m_mutexQueue.Unlock();
|
||||
DownloadQueue::Unlock();
|
||||
}
|
||||
|
||||
void QueueScriptCoordinator::StartScript(NZBInfo* pNZBInfo, QueueItem* pQueueItem)
|
||||
{
|
||||
m_pCurItem = pQueueItem;
|
||||
QueueScriptController::StartScript(pNZBInfo, pQueueItem->GetScript(), pQueueItem->GetEvent());
|
||||
}
|
||||
|
||||
bool QueueScriptCoordinator::HasJob(int iNZBID, bool* pActive)
|
||||
{
|
||||
m_mutexQueue.Lock();
|
||||
bool bWorking = m_pCurItem && m_pCurItem->GetNZBID() == iNZBID;
|
||||
if (pActive)
|
||||
{
|
||||
*pActive = bWorking;
|
||||
}
|
||||
if (!bWorking)
|
||||
{
|
||||
for (Queue::iterator it = m_Queue.begin(); it != m_Queue.end(); it++)
|
||||
{
|
||||
QueueItem* pQueueItem = *it;
|
||||
bWorking = pQueueItem->GetNZBID() == iNZBID;
|
||||
if (bWorking)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
m_mutexQueue.Unlock();
|
||||
|
||||
return bWorking;
|
||||
}
|
||||
|
||||
int QueueScriptCoordinator::GetQueueSize()
|
||||
{
|
||||
m_mutexQueue.Lock();
|
||||
int iQueuedCount = m_Queue.size();
|
||||
if (m_pCurItem)
|
||||
{
|
||||
iQueuedCount++;
|
||||
}
|
||||
m_mutexQueue.Unlock();
|
||||
|
||||
return iQueuedCount;
|
||||
}
|
||||
84
daemon/extension/QueueScript.h
Normal file
84
daemon/extension/QueueScript.h
Normal file
@@ -0,0 +1,84 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007-2015 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 QUEUESCRIPT_H
|
||||
#define QUEUESCRIPT_H
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "DownloadInfo.h"
|
||||
#include "ScriptConfig.h"
|
||||
|
||||
class QueueScriptCoordinator
|
||||
{
|
||||
public:
|
||||
enum EEvent
|
||||
{
|
||||
qeFileDownloaded, // lowest priority
|
||||
qeUrlCompleted,
|
||||
qeNzbAdded,
|
||||
qeNzbDownloaded,
|
||||
qeNzbDeleted // highest priority
|
||||
};
|
||||
|
||||
private:
|
||||
class QueueItem
|
||||
{
|
||||
private:
|
||||
int m_iNZBID;
|
||||
ScriptConfig::Script* m_pScript;
|
||||
EEvent m_eEvent;
|
||||
public:
|
||||
QueueItem(int iNZBID, ScriptConfig::Script* pScript, EEvent eEvent);
|
||||
int GetNZBID() { return m_iNZBID; }
|
||||
ScriptConfig::Script* GetScript() { return m_pScript; }
|
||||
EEvent GetEvent() { return m_eEvent; }
|
||||
};
|
||||
|
||||
typedef std::list<QueueItem*> Queue;
|
||||
|
||||
Queue m_Queue;
|
||||
Mutex m_mutexQueue;
|
||||
QueueItem* m_pCurItem;
|
||||
bool m_bHasQueueScripts;
|
||||
bool m_bStopped;
|
||||
|
||||
void StartScript(NZBInfo* pNZBInfo, QueueItem* pQueueItem);
|
||||
NZBInfo* FindNZBInfo(DownloadQueue* pDownloadQueue, int iNZBID);
|
||||
|
||||
public:
|
||||
QueueScriptCoordinator();
|
||||
~QueueScriptCoordinator();
|
||||
void Stop() { m_bStopped = true; }
|
||||
void InitOptions();
|
||||
void EnqueueScript(NZBInfo* pNZBInfo, EEvent eEvent);
|
||||
void CheckQueue();
|
||||
bool HasJob(int iNZBID, bool* pActive);
|
||||
int GetQueueSize();
|
||||
};
|
||||
|
||||
extern QueueScriptCoordinator* g_pQueueScriptCoordinator;
|
||||
|
||||
#endif
|
||||
207
daemon/extension/ScanScript.cpp
Normal file
207
daemon/extension/ScanScript.cpp
Normal file
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007-2015 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 <ctype.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "ScanScript.h"
|
||||
#include "Scanner.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
void ScanScriptController::ExecuteScripts(const char* szNZBFilename,
|
||||
const char* szUrl, const char* szDirectory, char** pNZBName, char** pCategory,
|
||||
int* iPriority, NZBParameterList* pParameters, bool* bAddTop, bool* bAddPaused,
|
||||
char** pDupeKey, int* iDupeScore, EDupeMode* eDupeMode)
|
||||
{
|
||||
ScanScriptController* pScriptController = new ScanScriptController();
|
||||
|
||||
pScriptController->m_szNZBFilename = szNZBFilename;
|
||||
pScriptController->m_szUrl = szUrl;
|
||||
pScriptController->m_szDirectory = szDirectory;
|
||||
pScriptController->m_pNZBName = pNZBName;
|
||||
pScriptController->m_pCategory = pCategory;
|
||||
pScriptController->m_pParameters = pParameters;
|
||||
pScriptController->m_iPriority = iPriority;
|
||||
pScriptController->m_bAddTop = bAddTop;
|
||||
pScriptController->m_bAddPaused = bAddPaused;
|
||||
pScriptController->m_pDupeKey = pDupeKey;
|
||||
pScriptController->m_iDupeScore = iDupeScore;
|
||||
pScriptController->m_eDupeMode = eDupeMode;
|
||||
pScriptController->m_iPrefixLen = 0;
|
||||
|
||||
pScriptController->ExecuteScriptList(g_pOptions->GetScanScript());
|
||||
|
||||
delete pScriptController;
|
||||
}
|
||||
|
||||
void ScanScriptController::ExecuteScript(ScriptConfig::Script* pScript)
|
||||
{
|
||||
if (!pScript->GetScanScript() || !Util::FileExists(m_szNZBFilename))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PrintMessage(Message::mkInfo, "Executing scan-script %s for %s", pScript->GetName(), Util::BaseFileName(m_szNZBFilename));
|
||||
|
||||
SetScript(pScript->GetLocation());
|
||||
SetArgs(NULL, false);
|
||||
|
||||
char szInfoName[1024];
|
||||
snprintf(szInfoName, 1024, "scan-script %s for %s", pScript->GetName(), Util::BaseFileName(m_szNZBFilename));
|
||||
szInfoName[1024-1] = '\0';
|
||||
SetInfoName(szInfoName);
|
||||
|
||||
SetLogPrefix(pScript->GetDisplayName());
|
||||
m_iPrefixLen = strlen(pScript->GetDisplayName()) + 2; // 2 = strlen(": ");
|
||||
PrepareParams(pScript->GetName());
|
||||
|
||||
Execute();
|
||||
|
||||
SetLogPrefix(NULL);
|
||||
}
|
||||
|
||||
void ScanScriptController::PrepareParams(const char* szScriptName)
|
||||
{
|
||||
ResetEnv();
|
||||
|
||||
SetEnvVar("NZBNP_FILENAME", m_szNZBFilename);
|
||||
SetEnvVar("NZBNP_URL", m_szUrl);
|
||||
SetEnvVar("NZBNP_NZBNAME", strlen(*m_pNZBName) > 0 ? *m_pNZBName : Util::BaseFileName(m_szNZBFilename));
|
||||
SetEnvVar("NZBNP_CATEGORY", *m_pCategory);
|
||||
SetIntEnvVar("NZBNP_PRIORITY", *m_iPriority);
|
||||
SetIntEnvVar("NZBNP_TOP", *m_bAddTop ? 1 : 0);
|
||||
SetIntEnvVar("NZBNP_PAUSED", *m_bAddPaused ? 1 : 0);
|
||||
SetEnvVar("NZBNP_DUPEKEY", *m_pDupeKey);
|
||||
SetIntEnvVar("NZBNP_DUPESCORE", *m_iDupeScore);
|
||||
|
||||
const char* szDupeModeName[] = { "SCORE", "ALL", "FORCE" };
|
||||
SetEnvVar("NZBNP_DUPEMODE", szDupeModeName[*m_eDupeMode]);
|
||||
|
||||
// remove trailing slash
|
||||
char szDir[1024];
|
||||
strncpy(szDir, m_szDirectory, 1024);
|
||||
szDir[1024-1] = '\0';
|
||||
int iLen = strlen(szDir);
|
||||
if (szDir[iLen-1] == PATH_SEPARATOR)
|
||||
{
|
||||
szDir[iLen-1] = '\0';
|
||||
}
|
||||
SetEnvVar("NZBNP_DIRECTORY", szDir);
|
||||
|
||||
PrepareEnvScript(m_pParameters, szScriptName);
|
||||
}
|
||||
|
||||
void ScanScriptController::AddMessage(Message::EKind eKind, const char* szText)
|
||||
{
|
||||
const char* szMsgText = szText + m_iPrefixLen;
|
||||
|
||||
if (!strncmp(szMsgText, "[NZB] ", 6))
|
||||
{
|
||||
debug("Command %s detected", szMsgText + 6);
|
||||
if (!strncmp(szMsgText + 6, "NZBNAME=", 8))
|
||||
{
|
||||
free(*m_pNZBName);
|
||||
*m_pNZBName = strdup(szMsgText + 6 + 8);
|
||||
}
|
||||
else if (!strncmp(szMsgText + 6, "CATEGORY=", 9))
|
||||
{
|
||||
free(*m_pCategory);
|
||||
*m_pCategory = strdup(szMsgText + 6 + 9);
|
||||
g_pScanner->InitPPParameters(*m_pCategory, m_pParameters, true);
|
||||
}
|
||||
else if (!strncmp(szMsgText + 6, "NZBPR_", 6))
|
||||
{
|
||||
char* szParam = strdup(szMsgText + 6 + 6);
|
||||
char* szValue = strchr(szParam, '=');
|
||||
if (szValue)
|
||||
{
|
||||
*szValue = '\0';
|
||||
m_pParameters->SetParameter(szParam, szValue + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Invalid command \"%s\" received from %s", szMsgText, GetInfoName());
|
||||
}
|
||||
free(szParam);
|
||||
}
|
||||
else if (!strncmp(szMsgText + 6, "PRIORITY=", 9))
|
||||
{
|
||||
*m_iPriority = atoi(szMsgText + 6 + 9);
|
||||
}
|
||||
else if (!strncmp(szMsgText + 6, "TOP=", 4))
|
||||
{
|
||||
*m_bAddTop = atoi(szMsgText + 6 + 4) != 0;
|
||||
}
|
||||
else if (!strncmp(szMsgText + 6, "PAUSED=", 7))
|
||||
{
|
||||
*m_bAddPaused = atoi(szMsgText + 6 + 7) != 0;
|
||||
}
|
||||
else if (!strncmp(szMsgText + 6, "DUPEKEY=", 8))
|
||||
{
|
||||
free(*m_pDupeKey);
|
||||
*m_pDupeKey = strdup(szMsgText + 6 + 8);
|
||||
}
|
||||
else if (!strncmp(szMsgText + 6, "DUPESCORE=", 10))
|
||||
{
|
||||
*m_iDupeScore = atoi(szMsgText + 6 + 10);
|
||||
}
|
||||
else if (!strncmp(szMsgText + 6, "DUPEMODE=", 9))
|
||||
{
|
||||
const char* szDupeMode = szMsgText + 6 + 9;
|
||||
if (strcasecmp(szDupeMode, "score") && strcasecmp(szDupeMode, "all") && strcasecmp(szDupeMode, "force"))
|
||||
{
|
||||
error("Invalid value \"%s\" for command \"DUPEMODE\" received from %s", szDupeMode, GetInfoName());
|
||||
return;
|
||||
}
|
||||
*m_eDupeMode = !strcasecmp(szDupeMode, "all") ? dmAll :
|
||||
!strcasecmp(szDupeMode, "force") ? dmForce : dmScore;
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Invalid command \"%s\" received from %s", szMsgText, GetInfoName());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ScriptController::AddMessage(eKind, szText);
|
||||
}
|
||||
}
|
||||
61
daemon/extension/ScanScript.h
Normal file
61
daemon/extension/ScanScript.h
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007-2015 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 SCANSCRIPT_H
|
||||
#define SCANSCRIPT_H
|
||||
|
||||
#include "NzbScript.h"
|
||||
|
||||
class ScanScriptController : public NZBScriptController
|
||||
{
|
||||
private:
|
||||
const char* m_szNZBFilename;
|
||||
const char* m_szUrl;
|
||||
const char* m_szDirectory;
|
||||
char** m_pNZBName;
|
||||
char** m_pCategory;
|
||||
int* m_iPriority;
|
||||
NZBParameterList* m_pParameters;
|
||||
bool* m_bAddTop;
|
||||
bool* m_bAddPaused;
|
||||
char** m_pDupeKey;
|
||||
int* m_iDupeScore;
|
||||
EDupeMode* m_eDupeMode;
|
||||
int m_iPrefixLen;
|
||||
|
||||
void PrepareParams(const char* szScriptName);
|
||||
|
||||
protected:
|
||||
virtual void ExecuteScript(ScriptConfig::Script* pScript);
|
||||
virtual void AddMessage(Message::EKind eKind, const char* szText);
|
||||
|
||||
public:
|
||||
static void ExecuteScripts(const char* szNZBFilename, const char* szUrl,
|
||||
const char* szDirectory, char** pNZBName, char** pCategory, int* iPriority,
|
||||
NZBParameterList* pParameters, bool* bAddTop, bool* bAddPaused,
|
||||
char** pDupeKey, int* iDupeScore, EDupeMode* eDupeMode);
|
||||
};
|
||||
|
||||
#endif
|
||||
143
daemon/extension/SchedulerScript.cpp
Normal file
143
daemon/extension/SchedulerScript.cpp
Normal file
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007-2015 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 <ctype.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "SchedulerScript.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
|
||||
SchedulerScriptController::~SchedulerScriptController()
|
||||
{
|
||||
free(m_szScript);
|
||||
}
|
||||
|
||||
void SchedulerScriptController::StartScript(const char* szParam, bool bExternalProcess, int iTaskID)
|
||||
{
|
||||
char** argv = NULL;
|
||||
if (bExternalProcess && !Util::SplitCommandLine(szParam, &argv))
|
||||
{
|
||||
error("Could not execute scheduled process-script, failed to parse command line: %s", szParam);
|
||||
return;
|
||||
}
|
||||
|
||||
SchedulerScriptController* pScriptController = new SchedulerScriptController();
|
||||
|
||||
pScriptController->m_bExternalProcess = bExternalProcess;
|
||||
pScriptController->m_szScript = strdup(szParam);
|
||||
pScriptController->m_iTaskID = iTaskID;
|
||||
|
||||
if (bExternalProcess)
|
||||
{
|
||||
pScriptController->SetScript(argv[0]);
|
||||
pScriptController->SetArgs((const char**)argv, true);
|
||||
}
|
||||
|
||||
pScriptController->SetAutoDestroy(true);
|
||||
|
||||
pScriptController->Start();
|
||||
}
|
||||
|
||||
void SchedulerScriptController::Run()
|
||||
{
|
||||
if (m_bExternalProcess)
|
||||
{
|
||||
ExecuteExternalProcess();
|
||||
}
|
||||
else
|
||||
{
|
||||
ExecuteScriptList(m_szScript);
|
||||
}
|
||||
}
|
||||
|
||||
void SchedulerScriptController::ExecuteScript(ScriptConfig::Script* pScript)
|
||||
{
|
||||
if (!pScript->GetSchedulerScript())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
PrintMessage(Message::mkInfo, "Executing scheduler-script %s for Task%i", pScript->GetName(), m_iTaskID);
|
||||
|
||||
SetScript(pScript->GetLocation());
|
||||
SetArgs(NULL, false);
|
||||
|
||||
char szInfoName[1024];
|
||||
snprintf(szInfoName, 1024, "scheduler-script %s for Task%i", pScript->GetName(), m_iTaskID);
|
||||
szInfoName[1024-1] = '\0';
|
||||
SetInfoName(szInfoName);
|
||||
|
||||
SetLogPrefix(pScript->GetDisplayName());
|
||||
PrepareParams(pScript->GetName());
|
||||
|
||||
Execute();
|
||||
|
||||
SetLogPrefix(NULL);
|
||||
}
|
||||
|
||||
void SchedulerScriptController::PrepareParams(const char* szScriptName)
|
||||
{
|
||||
ResetEnv();
|
||||
|
||||
SetIntEnvVar("NZBSP_TASKID", m_iTaskID);
|
||||
|
||||
PrepareEnvScript(NULL, szScriptName);
|
||||
}
|
||||
|
||||
void SchedulerScriptController::ExecuteExternalProcess()
|
||||
{
|
||||
info("Executing scheduled process-script %s for Task%i", Util::BaseFileName(GetScript()), m_iTaskID);
|
||||
|
||||
char szInfoName[1024];
|
||||
snprintf(szInfoName, 1024, "scheduled process-script %s for Task%i", Util::BaseFileName(GetScript()), m_iTaskID);
|
||||
szInfoName[1024-1] = '\0';
|
||||
SetInfoName(szInfoName);
|
||||
|
||||
char szLogPrefix[1024];
|
||||
strncpy(szLogPrefix, Util::BaseFileName(GetScript()), 1024);
|
||||
szLogPrefix[1024-1] = '\0';
|
||||
if (char* ext = strrchr(szLogPrefix, '.')) *ext = '\0'; // strip file extension
|
||||
SetLogPrefix(szLogPrefix);
|
||||
|
||||
Execute();
|
||||
}
|
||||
50
daemon/extension/SchedulerScript.h
Normal file
50
daemon/extension/SchedulerScript.h
Normal file
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007-2015 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 SCHEDULERSCRIPT_H
|
||||
#define SCHEDULERSCRIPT_H
|
||||
|
||||
#include "NzbScript.h"
|
||||
|
||||
class SchedulerScriptController : public Thread, public NZBScriptController
|
||||
{
|
||||
private:
|
||||
char* m_szScript;
|
||||
bool m_bExternalProcess;
|
||||
int m_iTaskID;
|
||||
|
||||
void PrepareParams(const char* szScriptName);
|
||||
void ExecuteExternalProcess();
|
||||
|
||||
protected:
|
||||
virtual void ExecuteScript(ScriptConfig::Script* pScript);
|
||||
|
||||
public:
|
||||
virtual ~SchedulerScriptController();
|
||||
virtual void Run();
|
||||
static void StartScript(const char* szParam, bool bExternalProcess, int iTaskID);
|
||||
};
|
||||
|
||||
#endif
|
||||
559
daemon/extension/ScriptConfig.cpp
Normal file
559
daemon/extension/ScriptConfig.cpp
Normal file
@@ -0,0 +1,559 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2013-2015 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>
|
||||
#include <sys/stat.h>
|
||||
#include <set>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Util.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "ScriptConfig.h"
|
||||
|
||||
static const char* BEGIN_SCRIPT_SIGNATURE = "### NZBGET ";
|
||||
static const char* POST_SCRIPT_SIGNATURE = "POST-PROCESSING";
|
||||
static const char* SCAN_SCRIPT_SIGNATURE = "SCAN";
|
||||
static const char* QUEUE_SCRIPT_SIGNATURE = "QUEUE";
|
||||
static const char* SCHEDULER_SCRIPT_SIGNATURE = "SCHEDULER";
|
||||
static const char* FEED_SCRIPT_SIGNATURE = "FEED";
|
||||
static const char* END_SCRIPT_SIGNATURE = " SCRIPT";
|
||||
static const char* QUEUE_EVENTS_SIGNATURE = "### QUEUE EVENTS:";
|
||||
|
||||
ScriptConfig* g_pScriptConfig = NULL;
|
||||
|
||||
|
||||
ScriptConfig::ConfigTemplate::ConfigTemplate(Script* pScript, const char* szTemplate)
|
||||
{
|
||||
m_pScript = pScript;
|
||||
m_szTemplate = strdup(szTemplate ? szTemplate : "");
|
||||
}
|
||||
|
||||
ScriptConfig::ConfigTemplate::~ConfigTemplate()
|
||||
{
|
||||
delete m_pScript;
|
||||
free(m_szTemplate);
|
||||
}
|
||||
|
||||
ScriptConfig::ConfigTemplates::~ConfigTemplates()
|
||||
{
|
||||
for (iterator it = begin(); it != end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ScriptConfig::Script::Script(const char* szName, const char* szLocation)
|
||||
{
|
||||
m_szName = strdup(szName);
|
||||
m_szLocation = strdup(szLocation);
|
||||
m_szDisplayName = strdup(szName);
|
||||
m_bPostScript = false;
|
||||
m_bScanScript = false;
|
||||
m_bQueueScript = false;
|
||||
m_bSchedulerScript = false;
|
||||
m_bFeedScript = false;
|
||||
m_szQueueEvents = NULL;
|
||||
}
|
||||
|
||||
ScriptConfig::Script::~Script()
|
||||
{
|
||||
free(m_szName);
|
||||
free(m_szLocation);
|
||||
free(m_szDisplayName);
|
||||
free(m_szQueueEvents);
|
||||
}
|
||||
|
||||
void ScriptConfig::Script::SetDisplayName(const char* szDisplayName)
|
||||
{
|
||||
free(m_szDisplayName);
|
||||
m_szDisplayName = strdup(szDisplayName);
|
||||
}
|
||||
|
||||
void ScriptConfig::Script::SetQueueEvents(const char* szQueueEvents)
|
||||
{
|
||||
free(m_szQueueEvents);
|
||||
m_szQueueEvents = szQueueEvents ? strdup(szQueueEvents) : NULL;
|
||||
}
|
||||
|
||||
|
||||
ScriptConfig::Scripts::~Scripts()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
void ScriptConfig::Scripts::Clear()
|
||||
{
|
||||
for (iterator it = begin(); it != end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
clear();
|
||||
}
|
||||
|
||||
ScriptConfig::Script* ScriptConfig::Scripts::Find(const char* szName)
|
||||
{
|
||||
for (iterator it = begin(); it != end(); it++)
|
||||
{
|
||||
Script* pScript = *it;
|
||||
if (!strcmp(pScript->GetName(), szName))
|
||||
{
|
||||
return pScript;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
ScriptConfig::ScriptConfig()
|
||||
{
|
||||
InitScripts();
|
||||
InitConfigTemplates();
|
||||
}
|
||||
|
||||
ScriptConfig::~ScriptConfig()
|
||||
{
|
||||
}
|
||||
|
||||
bool ScriptConfig::LoadConfig(Options::OptEntries* pOptEntries)
|
||||
{
|
||||
// read config file
|
||||
FILE* infile = fopen(g_pOptions->GetConfigFilename(), FOPEN_RB);
|
||||
|
||||
if (!infile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
int iBufLen = (int)Util::FileSize(g_pOptions->GetConfigFilename()) + 1;
|
||||
char* buf = (char*)malloc(iBufLen);
|
||||
|
||||
while (fgets(buf, iBufLen - 1, infile))
|
||||
{
|
||||
// remove trailing '\n' and '\r' and spaces
|
||||
Util::TrimRight(buf);
|
||||
|
||||
// skip comments and empty lines
|
||||
if (buf[0] == 0 || buf[0] == '#' || strspn(buf, " ") == strlen(buf))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
char* optname;
|
||||
char* optvalue;
|
||||
if (g_pOptions->SplitOptionString(buf, &optname, &optvalue))
|
||||
{
|
||||
Options::OptEntry* pOptEntry = new Options::OptEntry();
|
||||
pOptEntry->SetName(optname);
|
||||
pOptEntry->SetValue(optvalue);
|
||||
pOptEntries->push_back(pOptEntry);
|
||||
|
||||
free(optname);
|
||||
free(optvalue);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(infile);
|
||||
free(buf);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScriptConfig::SaveConfig(Options::OptEntries* pOptEntries)
|
||||
{
|
||||
// save to config file
|
||||
FILE* infile = fopen(g_pOptions->GetConfigFilename(), FOPEN_RBP);
|
||||
|
||||
if (!infile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<char*> config;
|
||||
std::set<Options::OptEntry*> writtenOptions;
|
||||
|
||||
// read config file into memory array
|
||||
int iBufLen = (int)Util::FileSize(g_pOptions->GetConfigFilename()) + 1;
|
||||
char* buf = (char*)malloc(iBufLen);
|
||||
while (fgets(buf, iBufLen - 1, infile))
|
||||
{
|
||||
config.push_back(strdup(buf));
|
||||
}
|
||||
free(buf);
|
||||
|
||||
// write config file back to disk, replace old values of existing options with new values
|
||||
rewind(infile);
|
||||
for (std::vector<char*>::iterator it = config.begin(); it != config.end(); it++)
|
||||
{
|
||||
char* buf = *it;
|
||||
|
||||
const char* eq = strchr(buf, '=');
|
||||
if (eq && buf[0] != '#')
|
||||
{
|
||||
// remove trailing '\n' and '\r' and spaces
|
||||
Util::TrimRight(buf);
|
||||
|
||||
char* optname;
|
||||
char* optvalue;
|
||||
if (g_pOptions->SplitOptionString(buf, &optname, &optvalue))
|
||||
{
|
||||
Options::OptEntry *pOptEntry = pOptEntries->FindOption(optname);
|
||||
if (pOptEntry)
|
||||
{
|
||||
fputs(pOptEntry->GetName(), infile);
|
||||
fputs("=", infile);
|
||||
fputs(pOptEntry->GetValue(), infile);
|
||||
fputs("\n", infile);
|
||||
writtenOptions.insert(pOptEntry);
|
||||
}
|
||||
|
||||
free(optname);
|
||||
free(optvalue);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
fputs(buf, infile);
|
||||
}
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
// write new options
|
||||
for (Options::OptEntries::iterator it = pOptEntries->begin(); it != pOptEntries->end(); it++)
|
||||
{
|
||||
Options::OptEntry* pOptEntry = *it;
|
||||
std::set<Options::OptEntry*>::iterator fit = writtenOptions.find(pOptEntry);
|
||||
if (fit == writtenOptions.end())
|
||||
{
|
||||
fputs(pOptEntry->GetName(), infile);
|
||||
fputs("=", infile);
|
||||
fputs(pOptEntry->GetValue(), infile);
|
||||
fputs("\n", infile);
|
||||
}
|
||||
}
|
||||
|
||||
// close and truncate the file
|
||||
int pos = (int)ftell(infile);
|
||||
fclose(infile);
|
||||
|
||||
Util::TruncateFile(g_pOptions->GetConfigFilename(), pos);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ScriptConfig::LoadConfigTemplates(ConfigTemplates* pConfigTemplates)
|
||||
{
|
||||
char* szBuffer;
|
||||
int iLength;
|
||||
if (!Util::LoadFileIntoBuffer(g_pOptions->GetConfigTemplate(), &szBuffer, &iLength))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
ConfigTemplate* pConfigTemplate = new ConfigTemplate(NULL, szBuffer);
|
||||
pConfigTemplates->push_back(pConfigTemplate);
|
||||
free(szBuffer);
|
||||
|
||||
if (!g_pOptions->GetScriptDir())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Scripts scriptList;
|
||||
LoadScripts(&scriptList);
|
||||
|
||||
const int iBeginSignatureLen = strlen(BEGIN_SCRIPT_SIGNATURE);
|
||||
const int iQueueEventsSignatureLen = strlen(QUEUE_EVENTS_SIGNATURE);
|
||||
|
||||
for (Scripts::iterator it = scriptList.begin(); it != scriptList.end(); it++)
|
||||
{
|
||||
Script* pScript = *it;
|
||||
|
||||
FILE* infile = fopen(pScript->GetLocation(), FOPEN_RB);
|
||||
if (!infile)
|
||||
{
|
||||
ConfigTemplate* pConfigTemplate = new ConfigTemplate(pScript, "");
|
||||
pConfigTemplates->push_back(pConfigTemplate);
|
||||
continue;
|
||||
}
|
||||
|
||||
StringBuilder stringBuilder;
|
||||
char buf[1024];
|
||||
bool bInConfig = false;
|
||||
|
||||
while (fgets(buf, sizeof(buf) - 1, infile))
|
||||
{
|
||||
if (!strncmp(buf, BEGIN_SCRIPT_SIGNATURE, iBeginSignatureLen) &&
|
||||
strstr(buf, END_SCRIPT_SIGNATURE) &&
|
||||
(strstr(buf, POST_SCRIPT_SIGNATURE) ||
|
||||
strstr(buf, SCAN_SCRIPT_SIGNATURE) ||
|
||||
strstr(buf, QUEUE_SCRIPT_SIGNATURE) ||
|
||||
strstr(buf, SCHEDULER_SCRIPT_SIGNATURE) ||
|
||||
strstr(buf, FEED_SCRIPT_SIGNATURE)))
|
||||
{
|
||||
if (bInConfig)
|
||||
{
|
||||
break;
|
||||
}
|
||||
bInConfig = true;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool bSkip = !strncmp(buf, QUEUE_EVENTS_SIGNATURE, iQueueEventsSignatureLen);
|
||||
|
||||
if (bInConfig && !bSkip)
|
||||
{
|
||||
stringBuilder.Append(buf);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(infile);
|
||||
|
||||
ConfigTemplate* pConfigTemplate = new ConfigTemplate(pScript, stringBuilder.GetBuffer());
|
||||
pConfigTemplates->push_back(pConfigTemplate);
|
||||
}
|
||||
|
||||
// clearing the list without deleting of objects, which are in pConfigTemplates now
|
||||
scriptList.clear();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ScriptConfig::InitConfigTemplates()
|
||||
{
|
||||
if (!LoadConfigTemplates(&m_ConfigTemplates))
|
||||
{
|
||||
error("Could not read configuration templates");
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptConfig::InitScripts()
|
||||
{
|
||||
LoadScripts(&m_Scripts);
|
||||
}
|
||||
|
||||
void ScriptConfig::LoadScripts(Scripts* pScripts)
|
||||
{
|
||||
if (strlen(g_pOptions->GetScriptDir()) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Scripts tmpScripts;
|
||||
LoadScriptDir(&tmpScripts, g_pOptions->GetScriptDir(), false);
|
||||
tmpScripts.sort(CompareScripts);
|
||||
|
||||
// first add all scripts from m_szScriptOrder
|
||||
Tokenizer tok(g_pOptions->GetScriptOrder(), ",;");
|
||||
while (const char* szScriptName = tok.Next())
|
||||
{
|
||||
Script* pScript = tmpScripts.Find(szScriptName);
|
||||
if (pScript)
|
||||
{
|
||||
tmpScripts.remove(pScript);
|
||||
pScripts->push_back(pScript);
|
||||
}
|
||||
}
|
||||
|
||||
// second add all other scripts from scripts directory
|
||||
for (Scripts::iterator it = tmpScripts.begin(); it != tmpScripts.end(); it++)
|
||||
{
|
||||
Script* pScript = *it;
|
||||
if (!pScripts->Find(pScript->GetName()))
|
||||
{
|
||||
pScripts->push_back(pScript);
|
||||
}
|
||||
}
|
||||
|
||||
tmpScripts.clear();
|
||||
|
||||
BuildScriptDisplayNames(pScripts);
|
||||
}
|
||||
|
||||
void ScriptConfig::LoadScriptDir(Scripts* pScripts, const char* szDirectory, bool bIsSubDir)
|
||||
{
|
||||
int iBufSize = 1024*10;
|
||||
char* szBuffer = (char*)malloc(iBufSize+1);
|
||||
|
||||
const int iBeginSignatureLen = strlen(BEGIN_SCRIPT_SIGNATURE);
|
||||
const int iQueueEventsSignatureLen = strlen(QUEUE_EVENTS_SIGNATURE);
|
||||
|
||||
DirBrowser dir(szDirectory);
|
||||
while (const char* szFilename = dir.Next())
|
||||
{
|
||||
if (szFilename[0] != '.' && szFilename[0] != '_')
|
||||
{
|
||||
char szFullFilename[1024];
|
||||
snprintf(szFullFilename, 1024, "%s%s", szDirectory, szFilename);
|
||||
szFullFilename[1024-1] = '\0';
|
||||
|
||||
if (!Util::DirectoryExists(szFullFilename))
|
||||
{
|
||||
// check if the file contains pp-script-signature
|
||||
FILE* infile = fopen(szFullFilename, FOPEN_RB);
|
||||
if (infile)
|
||||
{
|
||||
// read first 10KB of the file and look for signature
|
||||
int iReadBytes = fread(szBuffer, 1, iBufSize, infile);
|
||||
fclose(infile);
|
||||
szBuffer[iReadBytes] = 0;
|
||||
|
||||
// split buffer into lines
|
||||
Tokenizer tok(szBuffer, "\n\r", true);
|
||||
while (char* szLine = tok.Next())
|
||||
{
|
||||
if (!strncmp(szLine, BEGIN_SCRIPT_SIGNATURE, iBeginSignatureLen) &&
|
||||
strstr(szLine, END_SCRIPT_SIGNATURE))
|
||||
{
|
||||
bool bPostScript = strstr(szLine, POST_SCRIPT_SIGNATURE);
|
||||
bool bScanScript = strstr(szLine, SCAN_SCRIPT_SIGNATURE);
|
||||
bool bQueueScript = strstr(szLine, QUEUE_SCRIPT_SIGNATURE);
|
||||
bool bSchedulerScript = strstr(szLine, SCHEDULER_SCRIPT_SIGNATURE);
|
||||
bool bFeedScript = strstr(szLine, FEED_SCRIPT_SIGNATURE);
|
||||
if (bPostScript || bScanScript || bQueueScript || bSchedulerScript || bFeedScript)
|
||||
{
|
||||
char szScriptName[1024];
|
||||
if (bIsSubDir)
|
||||
{
|
||||
char szDirectory2[1024];
|
||||
snprintf(szDirectory2, 1024, "%s", szDirectory);
|
||||
szDirectory2[1024-1] = '\0';
|
||||
int iLen = strlen(szDirectory2);
|
||||
if (szDirectory2[iLen-1] == PATH_SEPARATOR || szDirectory2[iLen-1] == ALT_PATH_SEPARATOR)
|
||||
{
|
||||
// trim last path-separator
|
||||
szDirectory2[iLen-1] = '\0';
|
||||
}
|
||||
|
||||
snprintf(szScriptName, 1024, "%s%c%s", Util::BaseFileName(szDirectory2), PATH_SEPARATOR, szFilename);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(szScriptName, 1024, "%s", szFilename);
|
||||
}
|
||||
szScriptName[1024-1] = '\0';
|
||||
|
||||
char* szQueueEvents = NULL;
|
||||
if (bQueueScript)
|
||||
{
|
||||
while (char* szLine = tok.Next())
|
||||
{
|
||||
if (!strncmp(szLine, QUEUE_EVENTS_SIGNATURE, iQueueEventsSignatureLen))
|
||||
{
|
||||
szQueueEvents = szLine + iQueueEventsSignatureLen;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Script* pScript = new Script(szScriptName, szFullFilename);
|
||||
pScript->SetPostScript(bPostScript);
|
||||
pScript->SetScanScript(bScanScript);
|
||||
pScript->SetQueueScript(bQueueScript);
|
||||
pScript->SetSchedulerScript(bSchedulerScript);
|
||||
pScript->SetFeedScript(bFeedScript);
|
||||
pScript->SetQueueEvents(szQueueEvents);
|
||||
pScripts->push_back(pScript);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!bIsSubDir)
|
||||
{
|
||||
snprintf(szFullFilename, 1024, "%s%s%c", szDirectory, szFilename, PATH_SEPARATOR);
|
||||
szFullFilename[1024-1] = '\0';
|
||||
|
||||
LoadScriptDir(pScripts, szFullFilename, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
free(szBuffer);
|
||||
}
|
||||
|
||||
bool ScriptConfig::CompareScripts(Script* pScript1, Script* pScript2)
|
||||
{
|
||||
return strcmp(pScript1->GetName(), pScript2->GetName()) < 0;
|
||||
}
|
||||
|
||||
void ScriptConfig::BuildScriptDisplayNames(Scripts* pScripts)
|
||||
{
|
||||
// trying to use short name without path and extension.
|
||||
// if there are other scripts with the same short name - using a longer name instead (with ot without extension)
|
||||
|
||||
for (Scripts::iterator it = pScripts->begin(); it != pScripts->end(); it++)
|
||||
{
|
||||
Script* pScript = *it;
|
||||
|
||||
char szShortName[256];
|
||||
strncpy(szShortName, pScript->GetName(), 256);
|
||||
szShortName[256-1] = '\0';
|
||||
if (char* ext = strrchr(szShortName, '.')) *ext = '\0'; // strip file extension
|
||||
|
||||
const char* szDisplayName = Util::BaseFileName(szShortName);
|
||||
|
||||
for (Scripts::iterator it2 = pScripts->begin(); it2 != pScripts->end(); it2++)
|
||||
{
|
||||
Script* pScript2 = *it2;
|
||||
|
||||
char szShortName2[256];
|
||||
strncpy(szShortName2, pScript2->GetName(), 256);
|
||||
szShortName2[256-1] = '\0';
|
||||
if (char* ext = strrchr(szShortName2, '.')) *ext = '\0'; // strip file extension
|
||||
|
||||
const char* szDisplayName2 = Util::BaseFileName(szShortName2);
|
||||
|
||||
if (!strcmp(szDisplayName, szDisplayName2) && pScript->GetName() != pScript2->GetName())
|
||||
{
|
||||
if (!strcmp(szShortName, szShortName2))
|
||||
{
|
||||
szDisplayName = pScript->GetName();
|
||||
}
|
||||
else
|
||||
{
|
||||
szDisplayName = szShortName;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
pScript->SetDisplayName(szDisplayName);
|
||||
}
|
||||
}
|
||||
128
daemon/extension/ScriptConfig.h
Normal file
128
daemon/extension/ScriptConfig.h
Normal file
@@ -0,0 +1,128 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2013-2015 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 SCRIPTCONFIG_H
|
||||
#define SCRIPTCONFIG_H
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <time.h>
|
||||
|
||||
#include "Options.h"
|
||||
|
||||
class ScriptConfig
|
||||
{
|
||||
public:
|
||||
class Script
|
||||
{
|
||||
private:
|
||||
char* m_szName;
|
||||
char* m_szLocation;
|
||||
char* m_szDisplayName;
|
||||
bool m_bPostScript;
|
||||
bool m_bScanScript;
|
||||
bool m_bQueueScript;
|
||||
bool m_bSchedulerScript;
|
||||
bool m_bFeedScript;
|
||||
char* m_szQueueEvents;
|
||||
|
||||
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; }
|
||||
bool GetPostScript() { return m_bPostScript; }
|
||||
void SetPostScript(bool bPostScript) { m_bPostScript = bPostScript; }
|
||||
bool GetScanScript() { return m_bScanScript; }
|
||||
void SetScanScript(bool bScanScript) { m_bScanScript = bScanScript; }
|
||||
bool GetQueueScript() { return m_bQueueScript; }
|
||||
void SetQueueScript(bool bQueueScript) { m_bQueueScript = bQueueScript; }
|
||||
bool GetSchedulerScript() { return m_bSchedulerScript; }
|
||||
void SetSchedulerScript(bool bSchedulerScript) { m_bSchedulerScript = bSchedulerScript; }
|
||||
bool GetFeedScript() { return m_bFeedScript; }
|
||||
void SetFeedScript(bool bFeedScript) { m_bFeedScript = bFeedScript; }
|
||||
void SetQueueEvents(const char* szQueueEvents);
|
||||
const char* GetQueueEvents() { return m_szQueueEvents; }
|
||||
};
|
||||
|
||||
typedef std::list<Script*> ScriptsBase;
|
||||
|
||||
class Scripts: public ScriptsBase
|
||||
{
|
||||
public:
|
||||
~Scripts();
|
||||
void Clear();
|
||||
Script* Find(const char* szName);
|
||||
};
|
||||
|
||||
class ConfigTemplate
|
||||
{
|
||||
private:
|
||||
Script* m_pScript;
|
||||
char* m_szTemplate;
|
||||
|
||||
friend class Options;
|
||||
|
||||
public:
|
||||
ConfigTemplate(Script* pScript, const char* szTemplate);
|
||||
~ConfigTemplate();
|
||||
Script* GetScript() { return m_pScript; }
|
||||
const char* GetTemplate() { return m_szTemplate; }
|
||||
};
|
||||
|
||||
typedef std::vector<ConfigTemplate*> ConfigTemplatesBase;
|
||||
|
||||
class ConfigTemplates: public ConfigTemplatesBase
|
||||
{
|
||||
public:
|
||||
~ConfigTemplates();
|
||||
};
|
||||
|
||||
private:
|
||||
Scripts m_Scripts;
|
||||
ConfigTemplates m_ConfigTemplates;
|
||||
|
||||
void InitScripts();
|
||||
void InitConfigTemplates();
|
||||
static bool CompareScripts(Script* pScript1, Script* pScript2);
|
||||
void LoadScriptDir(Scripts* pScripts, const char* szDirectory, bool bIsSubDir);
|
||||
void BuildScriptDisplayNames(Scripts* pScripts);
|
||||
void LoadScripts(Scripts* pScripts);
|
||||
|
||||
public:
|
||||
ScriptConfig();
|
||||
~ScriptConfig();
|
||||
Scripts* GetScripts() { return &m_Scripts; }
|
||||
bool LoadConfig(Options::OptEntries* pOptEntries);
|
||||
bool SaveConfig(Options::OptEntries* pOptEntries);
|
||||
bool LoadConfigTemplates(ConfigTemplates* pConfigTemplates);
|
||||
ConfigTemplates* GetConfigTemplates() { return &m_ConfigTemplates; }
|
||||
};
|
||||
|
||||
extern ScriptConfig* g_pScriptConfig;
|
||||
|
||||
#endif
|
||||
802
daemon/feed/FeedCoordinator.cpp
Normal file
802
daemon/feed/FeedCoordinator.cpp
Normal file
@@ -0,0 +1,802 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2013-2015 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 "Util.h"
|
||||
#include "FeedFile.h"
|
||||
#include "FeedFilter.h"
|
||||
#include "FeedScript.h"
|
||||
#include "DiskState.h"
|
||||
#include "DupeCoordinator.h"
|
||||
|
||||
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::FilterHelper::FilterHelper()
|
||||
{
|
||||
m_pSeasonEpisodeRegEx = NULL;
|
||||
}
|
||||
|
||||
FeedCoordinator::FilterHelper::~FilterHelper()
|
||||
{
|
||||
delete m_pSeasonEpisodeRegEx;
|
||||
}
|
||||
|
||||
void FeedCoordinator::FilterHelper::CalcDupeStatus(const char* szTitle, const char* szDupeKey, char* szStatusBuf, int iBufLen)
|
||||
{
|
||||
const char* szDupeStatusName[] = { "", "QUEUED", "DOWNLOADING", "3", "SUCCESS", "5", "6", "7", "WARNING",
|
||||
"9", "10", "11", "12", "13", "14", "15", "FAILURE" };
|
||||
char szStatuses[200];
|
||||
szStatuses[0] = '\0';
|
||||
|
||||
DownloadQueue* pDownloadQueue = DownloadQueue::Lock();
|
||||
DupeCoordinator::EDupeStatus eDupeStatus = g_pDupeCoordinator->GetDupeStatus(pDownloadQueue, szTitle, szDupeKey);
|
||||
DownloadQueue::Unlock();
|
||||
|
||||
for (int i = 1; i <= (int)DupeCoordinator::dsFailure; i = i << 1)
|
||||
{
|
||||
if (eDupeStatus & i)
|
||||
{
|
||||
if (*szStatuses)
|
||||
{
|
||||
strcat(szStatuses, ",");
|
||||
}
|
||||
strcat(szStatuses, szDupeStatusName[i]);
|
||||
}
|
||||
}
|
||||
|
||||
strncpy(szStatusBuf, szStatuses, iBufLen);
|
||||
}
|
||||
|
||||
FeedCoordinator::FeedCoordinator()
|
||||
{
|
||||
debug("Creating FeedCoordinator");
|
||||
m_bForce = false;
|
||||
m_bSave = false;
|
||||
|
||||
g_pLog->RegisterDebuggable(this);
|
||||
|
||||
m_DownloadQueueObserver.m_pOwner = this;
|
||||
DownloadQueue* pDownloadQueue = DownloadQueue::Lock();
|
||||
pDownloadQueue->Attach(&m_DownloadQueueObserver);
|
||||
DownloadQueue::Unlock();
|
||||
}
|
||||
|
||||
FeedCoordinator::~FeedCoordinator()
|
||||
{
|
||||
debug("Destroying FeedCoordinator");
|
||||
// Cleanup
|
||||
|
||||
g_pLog->UnregisterDebuggable(this);
|
||||
|
||||
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");
|
||||
|
||||
while (!DownloadQueue::IsLoaded())
|
||||
{
|
||||
usleep(20 * 1000);
|
||||
}
|
||||
|
||||
if (g_pOptions->GetServerMode() && g_pOptions->GetSaveQueue() && g_pOptions->GetReloadQueue())
|
||||
{
|
||||
m_mutexDownloads.Lock();
|
||||
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() || 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()
|
||||
{
|
||||
info(" ---------- FeedCoordinator");
|
||||
|
||||
m_mutexDownloads.Lock();
|
||||
info(" 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 %s", 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];
|
||||
NZBInfo::MakeNiceUrlName(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())
|
||||
{
|
||||
FeedScriptController::ExecuteScripts(
|
||||
!Util::EmptyStr(pFeedInfo->GetFeedScript()) ? pFeedInfo->GetFeedScript(): g_pOptions->GetFeedScript(),
|
||||
pFeedInfo->GetOutputFilename(), pFeedInfo->GetID());
|
||||
FeedFile* pFeedFile = FeedFile::Create(pFeedInfo->GetOutputFilename());
|
||||
remove(pFeedInfo->GetOutputFilename());
|
||||
|
||||
NZBList addedNZBs;
|
||||
|
||||
m_mutexDownloads.Lock();
|
||||
if (pFeedFile)
|
||||
{
|
||||
ProcessFeed(pFeedInfo, pFeedFile->GetFeedItemInfos(), &addedNZBs);
|
||||
delete pFeedFile;
|
||||
}
|
||||
pFeedInfo->SetLastUpdate(time(NULL));
|
||||
pFeedInfo->SetForce(false);
|
||||
m_bSave = true;
|
||||
m_mutexDownloads.Unlock();
|
||||
|
||||
DownloadQueue* pDownloadQueue = DownloadQueue::Lock();
|
||||
for (NZBList::iterator it = addedNZBs.begin(); it != addedNZBs.end(); it++)
|
||||
{
|
||||
NZBInfo* pNZBInfo = *it;
|
||||
pDownloadQueue->GetQueue()->Add(pNZBInfo, false);
|
||||
}
|
||||
pDownloadQueue->Save();
|
||||
DownloadQueue::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->SetFeedFilterHelper(&m_FilterHelper);
|
||||
pFeedItemInfo->BuildDupeKey(NULL, NULL);
|
||||
if (pFeedFilter)
|
||||
{
|
||||
pFeedFilter->Match(pFeedItemInfo);
|
||||
}
|
||||
}
|
||||
|
||||
delete pFeedFilter;
|
||||
}
|
||||
|
||||
void FeedCoordinator::ProcessFeed(FeedInfo* pFeedInfo, FeedItemInfos* pFeedItemInfos, NZBList* pAddedNZBs)
|
||||
{
|
||||
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 && pFeedInfo->GetBacklog())
|
||||
{
|
||||
eStatus = FeedHistoryInfo::hsBacklog;
|
||||
}
|
||||
else if (!pFeedHistoryInfo)
|
||||
{
|
||||
NZBInfo* pNZBInfo = CreateNZBInfo(pFeedInfo, pFeedItemInfo);
|
||||
pAddedNZBs->Add(pNZBInfo, false);
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
NZBInfo* FeedCoordinator::CreateNZBInfo(FeedInfo* pFeedInfo, FeedItemInfo* pFeedItemInfo)
|
||||
{
|
||||
debug("Download %s from %s", pFeedItemInfo->GetUrl(), pFeedInfo->GetName());
|
||||
|
||||
NZBInfo* pNZBInfo = new NZBInfo();
|
||||
pNZBInfo->SetKind(NZBInfo::nkUrl);
|
||||
pNZBInfo->SetFeedID(pFeedInfo->GetID());
|
||||
pNZBInfo->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)
|
||||
{
|
||||
pNZBInfo->SetFilename(szNZBName2);
|
||||
}
|
||||
|
||||
pNZBInfo->SetCategory(pFeedItemInfo->GetAddCategory());
|
||||
pNZBInfo->SetPriority(pFeedItemInfo->GetPriority());
|
||||
pNZBInfo->SetAddUrlPaused(pFeedItemInfo->GetPauseNzb());
|
||||
pNZBInfo->SetDupeKey(pFeedItemInfo->GetDupeKey());
|
||||
pNZBInfo->SetDupeScore(pFeedItemInfo->GetDupeScore());
|
||||
pNZBInfo->SetDupeMode(pFeedItemInfo->GetDupeMode());
|
||||
|
||||
return pNZBInfo;
|
||||
}
|
||||
|
||||
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->GetID(), pFeedInfo->GetName(), pFeedInfo->GetUrl(), pFeedInfo->GetFilter(),
|
||||
pFeedInfo->GetBacklog(), pFeedInfo->GetPauseNzb(), pFeedInfo->GetCategory(),
|
||||
pFeedInfo->GetPriority(), pFeedInfo->GetInterval(), pFeedInfo->GetFeedScript(), 0, NULL, ppFeedItemInfos);
|
||||
}
|
||||
|
||||
bool FeedCoordinator::PreviewFeed(int iID, const char* szName, const char* szUrl, const char* szFilter,
|
||||
bool bBacklog, bool bPauseNzb, const char* szCategory, int iPriority, int iInterval, const char* szFeedScript,
|
||||
int iCacheTimeSec, const char* szCacheId, FeedItemInfos** ppFeedItemInfos)
|
||||
{
|
||||
debug("Preview feed %s", szName);
|
||||
|
||||
FeedInfo* pFeedInfo = new FeedInfo(iID, szName, szUrl, bBacklog, iInterval,
|
||||
szFilter, bPauseNzb, szCategory, iPriority, szFeedScript);
|
||||
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)
|
||||
{
|
||||
FeedScriptController::ExecuteScripts(
|
||||
!Util::EmptyStr(pFeedInfo->GetFeedScript()) ? pFeedInfo->GetFeedScript(): g_pOptions->GetFeedScript(),
|
||||
pFeedInfo->GetOutputFilename(), pFeedInfo->GetID());
|
||||
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 && pFeedInfo->GetBacklog() ? 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::DownloadQueueUpdate(Subject* pCaller, void* pAspect)
|
||||
{
|
||||
debug("Notification from URL-Coordinator received");
|
||||
|
||||
DownloadQueue::Aspect* pQueueAspect = (DownloadQueue::Aspect*)pAspect;
|
||||
if (pQueueAspect->eAction == DownloadQueue::eaUrlCompleted)
|
||||
{
|
||||
m_mutexDownloads.Lock();
|
||||
FeedHistoryInfo* pFeedHistoryInfo = m_FeedHistory.Find(pQueueAspect->pNZBInfo->GetURL());
|
||||
if (pFeedHistoryInfo)
|
||||
{
|
||||
pFeedHistoryInfo->SetStatus(FeedHistoryInfo::hsFetched);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_FeedHistory.Add(pQueueAspect->pNZBInfo->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() * 60*60*24;
|
||||
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();
|
||||
}
|
||||
142
daemon/feed/FeedCoordinator.h
Normal file
142
daemon/feed/FeedCoordinator.h
Normal file
@@ -0,0 +1,142 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2013-2015 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 "Log.h"
|
||||
#include "Thread.h"
|
||||
#include "WebDownloader.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "FeedInfo.h"
|
||||
#include "Observer.h"
|
||||
#include "Util.h"
|
||||
|
||||
|
||||
class FeedDownloader;
|
||||
|
||||
class FeedCoordinator : public Thread, public Observer, public Subject, public Debuggable
|
||||
{
|
||||
private:
|
||||
class DownloadQueueObserver: public Observer
|
||||
{
|
||||
public:
|
||||
FeedCoordinator* m_pOwner;
|
||||
virtual void Update(Subject* pCaller, void* pAspect) { m_pOwner->DownloadQueueUpdate(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; }
|
||||
};
|
||||
|
||||
class FilterHelper : public FeedFilterHelper
|
||||
{
|
||||
private:
|
||||
RegEx* m_pSeasonEpisodeRegEx;
|
||||
public:
|
||||
FilterHelper();
|
||||
~FilterHelper();
|
||||
virtual RegEx** GetSeasonEpisodeRegEx() { return &m_pSeasonEpisodeRegEx; };
|
||||
virtual void CalcDupeStatus(const char* szTitle, const char* szDupeKey, char* szStatusBuf, int iBufLen);
|
||||
};
|
||||
|
||||
typedef std::deque<FeedCacheItem*> FeedCache;
|
||||
typedef std::list<FeedDownloader*> ActiveDownloads;
|
||||
|
||||
private:
|
||||
Feeds m_Feeds;
|
||||
ActiveDownloads m_ActiveDownloads;
|
||||
FeedHistory m_FeedHistory;
|
||||
Mutex m_mutexDownloads;
|
||||
DownloadQueueObserver m_DownloadQueueObserver;
|
||||
bool m_bForce;
|
||||
bool m_bSave;
|
||||
FeedCache m_FeedCache;
|
||||
FilterHelper m_FilterHelper;
|
||||
|
||||
void StartFeedDownload(FeedInfo* pFeedInfo, bool bForce);
|
||||
void FeedCompleted(FeedDownloader* pFeedDownloader);
|
||||
void FilterFeed(FeedInfo* pFeedInfo, FeedItemInfos* pFeedItemInfos);
|
||||
void ProcessFeed(FeedInfo* pFeedInfo, FeedItemInfos* pFeedItemInfos, NZBList* pAddedNZBs);
|
||||
NZBInfo* CreateNZBInfo(FeedInfo* pFeedInfo, FeedItemInfo* pFeedItemInfo);
|
||||
void ResetHangingDownloads();
|
||||
void DownloadQueueUpdate(Subject* pCaller, void* pAspect);
|
||||
void CleanupHistory();
|
||||
void CleanupCache();
|
||||
void CheckSaveFeeds();
|
||||
|
||||
protected:
|
||||
virtual void LogDebugInfo();
|
||||
|
||||
public:
|
||||
FeedCoordinator();
|
||||
virtual ~FeedCoordinator();
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
void Update(Subject* pCaller, void* pAspect);
|
||||
void AddFeed(FeedInfo* pFeedInfo);
|
||||
bool PreviewFeed(int iID, const char* szName, const char* szUrl, const char* szFilter, bool bBacklog,
|
||||
bool bPauseNzb, const char* szCategory, int iPriority, int iInterval, const char* szFeedScript,
|
||||
int iCacheTimeSec, const char* szCacheId, FeedItemInfos** ppFeedItemInfos);
|
||||
bool ViewFeed(int iID, FeedItemInfos** ppFeedItemInfos);
|
||||
void FetchFeed(int iID);
|
||||
bool HasActiveDownloads();
|
||||
Feeds* GetFeeds() { return &m_Feeds; }
|
||||
};
|
||||
|
||||
extern FeedCoordinator* g_pFeedCoordinator;
|
||||
|
||||
class FeedDownloader : public WebDownloader
|
||||
{
|
||||
private:
|
||||
FeedInfo* m_pFeedInfo;
|
||||
|
||||
public:
|
||||
void SetFeedInfo(FeedInfo* pFeedInfo) { m_pFeedInfo = pFeedInfo; }
|
||||
FeedInfo* GetFeedInfo() { return m_pFeedInfo; }
|
||||
};
|
||||
|
||||
#endif
|
||||
619
daemon/feed/FeedFile.cpp
Normal file
619
daemon/feed/FeedFile.cpp
Normal file
@@ -0,0 +1,619 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2013-2015 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"
|
||||
|
||||
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()
|
||||
{
|
||||
info(" 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());
|
||||
// cleanup CDATA
|
||||
char* szDescription = strdup((const char*)description);
|
||||
WebUtil::XmlStripTags(szDescription);
|
||||
WebUtil::XmlDecode(szDescription);
|
||||
WebUtil::XmlRemoveEntities(szDescription);
|
||||
pFeedItemInfo->SetDescription(szDescription);
|
||||
free(szDescription);
|
||||
}
|
||||
|
||||
//<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)
|
||||
{
|
||||
// cleanup CDATA
|
||||
char* szDescription = strdup(m_szTagContent);
|
||||
WebUtil::XmlStripTags(szDescription);
|
||||
WebUtil::XmlDecode(szDescription);
|
||||
WebUtil::XmlRemoveEntities(szDescription);
|
||||
m_pFeedItemInfo->SetDescription(szDescription);
|
||||
free(szDescription);
|
||||
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
daemon/feed/FeedFile.h
Normal file
70
daemon/feed/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
|
||||
1185
daemon/feed/FeedFilter.cpp
Normal file
1185
daemon/feed/FeedFilter.cpp
Normal file
File diff suppressed because it is too large
Load Diff
186
daemon/feed/FeedFilter.h
Normal file
186
daemon/feed/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
|
||||
439
daemon/feed/FeedInfo.cpp
Normal file
439
daemon/feed/FeedInfo.cpp
Normal file
@@ -0,0 +1,439 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2013-2015 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, bool bBacklog, int iInterval,
|
||||
const char* szFilter, bool bPauseNzb, const char* szCategory, int iPriority, const char* szFeedScript)
|
||||
{
|
||||
m_iID = iID;
|
||||
m_szName = strdup(szName ? szName : "");
|
||||
m_szUrl = strdup(szUrl ? szUrl : "");
|
||||
m_szFilter = strdup(szFilter ? szFilter : "");
|
||||
m_bBacklog = bBacklog;
|
||||
m_iFilterHash = Util::HashBJ96(m_szFilter, strlen(m_szFilter), 0);
|
||||
m_szCategory = strdup(szCategory ? szCategory : "");
|
||||
m_iInterval = iInterval;
|
||||
m_szFeedScript = strdup(szFeedScript ? szFeedScript : "");
|
||||
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);
|
||||
free(m_szFeedScript);
|
||||
}
|
||||
|
||||
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_pFeedFilterHelper = 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;
|
||||
m_szDupeStatus = NULL;
|
||||
}
|
||||
|
||||
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);
|
||||
free(m_szDupeStatus);
|
||||
}
|
||||
|
||||
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** ppRegEx = m_pFeedFilterHelper->GetSeasonEpisodeRegEx();
|
||||
if (!*ppRegEx)
|
||||
{
|
||||
*ppRegEx = new RegEx("[^[:alnum:]]s?([0-9]+)[ex]([0-9]+(-?e[0-9]+)?)[^[:alnum:]]", 10);
|
||||
}
|
||||
|
||||
if ((*ppRegEx)->Match(m_szTitle))
|
||||
{
|
||||
char szRegValue[100];
|
||||
char szValue[100];
|
||||
|
||||
snprintf(szValue, 100, "S%02d", atoi(m_szTitle + (*ppRegEx)->GetMatchStart(1)));
|
||||
szValue[100-1] = '\0';
|
||||
SetSeason(szValue);
|
||||
|
||||
int iLen = (*ppRegEx)->GetMatchLen(2);
|
||||
iLen = iLen < 99 ? iLen : 99;
|
||||
strncpy(szRegValue, m_szTitle + (*ppRegEx)->GetMatchStart(2), (*ppRegEx)->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);
|
||||
}
|
||||
}
|
||||
|
||||
const char* FeedItemInfo::GetDupeStatus()
|
||||
{
|
||||
if (!m_szDupeStatus)
|
||||
{
|
||||
char szStatuses[200];
|
||||
szStatuses[0] = '\0';
|
||||
m_pFeedFilterHelper->CalcDupeStatus(m_szTitle, m_szDupeKey, szStatuses, sizeof(szStatuses));
|
||||
m_szDupeStatus = strdup(szStatuses);
|
||||
}
|
||||
|
||||
return m_szDupeStatus;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
281
daemon/feed/FeedInfo.h
Normal file
281
daemon/feed/FeedInfo.h
Normal file
@@ -0,0 +1,281 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2013-2015 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;
|
||||
char* m_szFeedScript;
|
||||
int m_iPriority;
|
||||
time_t m_tLastUpdate;
|
||||
bool m_bPreview;
|
||||
EStatus m_eStatus;
|
||||
char* m_szOutputFilename;
|
||||
bool m_bFetch;
|
||||
bool m_bForce;
|
||||
bool m_bBacklog;
|
||||
|
||||
public:
|
||||
FeedInfo(int iID, const char* szName, const char* szUrl, bool bBacklog, int iInterval,
|
||||
const char* szFilter, bool bPauseNzb, const char* szCategory, int iPriority,
|
||||
const char* szFeedScript);
|
||||
~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; }
|
||||
const char* GetFeedScript() { return m_szFeedScript; }
|
||||
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; }
|
||||
bool GetBacklog() { return m_bBacklog; }
|
||||
void SetBacklog(bool bBacklog) { m_bBacklog = bBacklog; }
|
||||
};
|
||||
|
||||
typedef std::deque<FeedInfo*> Feeds;
|
||||
|
||||
class FeedFilterHelper
|
||||
{
|
||||
public:
|
||||
virtual RegEx** GetSeasonEpisodeRegEx() = 0;
|
||||
virtual void CalcDupeStatus(const char* szTitle, const char* szDupeKey, char* szStatusBuf, int iBufLen) = 0;
|
||||
};
|
||||
|
||||
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;
|
||||
char* m_szDupeStatus;
|
||||
FeedFilterHelper* m_pFeedFilterHelper;
|
||||
Attributes m_Attributes;
|
||||
|
||||
int ParsePrefixedInt(const char *szValue);
|
||||
void ParseSeasonEpisode();
|
||||
|
||||
public:
|
||||
FeedItemInfo();
|
||||
~FeedItemInfo();
|
||||
void SetFeedFilterHelper(FeedFilterHelper* pFeedFilterHelper) { m_pFeedFilterHelper = pFeedFilterHelper; }
|
||||
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; }
|
||||
const char* GetDupeStatus();
|
||||
Attributes* GetAttributes() { return &m_Attributes; }
|
||||
};
|
||||
|
||||
typedef std::deque<FeedItemInfo*> FeedItemInfosBase;
|
||||
|
||||
class FeedItemInfos : public FeedItemInfosBase
|
||||
{
|
||||
private:
|
||||
int m_iRefCount;
|
||||
|
||||
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
|
||||
@@ -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$
|
||||
@@ -41,6 +41,7 @@
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "ColoredFrontend.h"
|
||||
#include "Util.h"
|
||||
|
||||
ColoredFrontend::ColoredFrontend()
|
||||
{
|
||||
@@ -73,47 +74,51 @@ 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)
|
||||
{
|
||||
long long remain_sec = m_lRemainingSize / ((long long int)(fCurrentDownloadSpeed * 1024));
|
||||
int h = remain_sec / 3600;
|
||||
int m = (remain_sec % 3600) / 60;
|
||||
int s = remain_sec % 60;
|
||||
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);
|
||||
sprintf(timeString, " (~ %.2d:%.2d:%.2d)", h, m, s);
|
||||
}
|
||||
|
||||
char szDownloadLimit[128];
|
||||
if (m_fDownloadLimit > 0.0f)
|
||||
if (m_iDownloadLimit > 0)
|
||||
{
|
||||
sprintf(szDownloadLimit, ", Limit %.0f KB/S", m_fDownloadLimit);
|
||||
sprintf(szDownloadLimit, ", Limit %i KB/s", m_iDownloadLimit / 1024);
|
||||
}
|
||||
else
|
||||
{
|
||||
szDownloadLimit[0] = 0;
|
||||
}
|
||||
|
||||
char szParStatus[128];
|
||||
if (m_iParJobCount > 0)
|
||||
char szPostStatus[128];
|
||||
if (m_iPostJobCount > 0)
|
||||
{
|
||||
sprintf(szParStatus, ", %i par", m_iParJobCount);
|
||||
sprintf(szPostStatus, ", %i post-job%s", m_iPostJobCount, m_iPostJobCount > 1 ? "s" : "");
|
||||
}
|
||||
else
|
||||
{
|
||||
szParStatus[0] = 0;
|
||||
szPostStatus[0] = 0;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
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)(m_lRemainingSize / 1024.0 / 1024.0),
|
||||
timeString, szParStatus, m_bPause ? (m_bStandBy ? ", Paused" : ", Pausing") : "", szDownloadLimit, szControlSeq);
|
||||
char szFileSize[20];
|
||||
char szCurrendSpeed[20];
|
||||
snprintf(tmp, 1024, " %d threads, %s, %s remaining%s%s%s%s%s\n",
|
||||
m_iThreadCount, Util::FormatSpeed(szCurrendSpeed, sizeof(szCurrendSpeed), iCurrentDownloadSpeed),
|
||||
Util::FormatSize(szFileSize, sizeof(szFileSize), m_lRemainingSize),
|
||||
timeString, szPostStatus, m_bPauseDownload ? (m_bStandBy ? ", Paused" : ", Pausing") : "",
|
||||
szDownloadLimit, szControlSeq);
|
||||
tmp[1024-1] = '\0';
|
||||
printf("%s", tmp);
|
||||
m_bNeedGoBack = true;
|
||||
@@ -140,6 +145,10 @@ void ColoredFrontend::PrintMessage(Message * pMessage)
|
||||
SetConsoleTextAttribute(m_hConsole, 2);
|
||||
printf("[INFO]");
|
||||
break;
|
||||
case Message::mkDetail:
|
||||
SetConsoleTextAttribute(m_hConsole, 2);
|
||||
printf("[DETAIL]");
|
||||
break;
|
||||
}
|
||||
SetConsoleTextAttribute(m_hConsole, 7);
|
||||
char* msg = strdup(pMessage->GetText());
|
||||
@@ -162,6 +171,9 @@ void ColoredFrontend::PrintMessage(Message * pMessage)
|
||||
case Message::mkInfo:
|
||||
printf("\033[32m[INFO]\033[39m %s\033[K\n", msg);
|
||||
break;
|
||||
case Message::mkDetail:
|
||||
printf("\033[32m[DETAIL]\033[39m %s\033[K\n", msg);
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -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$
|
||||
384
daemon/frontend/Frontend.cpp
Normal file
384
daemon/frontend/Frontend.cpp
Normal file
@@ -0,0 +1,384 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2015 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>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Options.h"
|
||||
#include "Frontend.h"
|
||||
#include "Log.h"
|
||||
#include "Connection.h"
|
||||
#include "MessageBase.h"
|
||||
#include "RemoteClient.h"
|
||||
#include "Util.h"
|
||||
#include "StatMeter.h"
|
||||
|
||||
Frontend::Frontend()
|
||||
{
|
||||
debug("Creating Frontend");
|
||||
|
||||
m_iNeededLogFirstID = 0;
|
||||
m_iNeededLogEntries = 0;
|
||||
m_bSummary = false;
|
||||
m_bFileList = false;
|
||||
m_iCurrentDownloadSpeed = 0;
|
||||
m_lRemainingSize = 0;
|
||||
m_bPauseDownload = false;
|
||||
m_iDownloadLimit = 0;
|
||||
m_iThreadCount = 0;
|
||||
m_iPostJobCount = 0;
|
||||
m_iUpTimeSec = 0;
|
||||
m_iDnTimeSec = 0;
|
||||
m_iAllBytes = 0;
|
||||
m_bStandBy = 0;
|
||||
m_iUpdateInterval = g_pOptions->GetUpdateInterval();
|
||||
}
|
||||
|
||||
bool Frontend::PrepareData()
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
if (IsStopped())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (!RequestMessages() || ((m_bSummary || m_bFileList) && !RequestFileList()))
|
||||
{
|
||||
const char* szControlIP = !strcmp(g_pOptions->GetControlIP(), "0.0.0.0") ? "127.0.0.1" : g_pOptions->GetControlIP();
|
||||
printf("\nUnable to send request to nzbget-server at %s (port %i) \n", szControlIP, g_pOptions->GetControlPort());
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_bSummary)
|
||||
{
|
||||
m_iCurrentDownloadSpeed = g_pStatMeter->CalcCurrentDownloadSpeed();
|
||||
m_bPauseDownload = g_pOptions->GetPauseDownload();
|
||||
m_iDownloadLimit = g_pOptions->GetDownloadRate();
|
||||
m_iThreadCount = Thread::GetThreadCount();
|
||||
g_pStatMeter->CalcTotalStat(&m_iUpTimeSec, &m_iDnTimeSec, &m_iAllBytes, &m_bStandBy);
|
||||
|
||||
DownloadQueue *pDownloadQueue = DownloadQueue::Lock();
|
||||
m_iPostJobCount = 0;
|
||||
for (NZBList::iterator it = pDownloadQueue->GetQueue()->begin(); it != pDownloadQueue->GetQueue()->end(); it++)
|
||||
{
|
||||
NZBInfo* pNZBInfo = *it;
|
||||
m_iPostJobCount += pNZBInfo->GetPostInfo() ? 1 : 0;
|
||||
}
|
||||
pDownloadQueue->CalcRemainingSize(&m_lRemainingSize, NULL);
|
||||
DownloadQueue::Unlock();
|
||||
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Frontend::FreeData()
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
m_RemoteMessages.Clear();
|
||||
|
||||
DownloadQueue* pDownloadQueue = DownloadQueue::Lock();
|
||||
pDownloadQueue->GetQueue()->Clear();
|
||||
DownloadQueue::Unlock();
|
||||
}
|
||||
}
|
||||
|
||||
MessageList* Frontend::LockMessages()
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
return &m_RemoteMessages;
|
||||
}
|
||||
else
|
||||
{
|
||||
return g_pLog->LockMessages();
|
||||
}
|
||||
}
|
||||
|
||||
void Frontend::UnlockMessages()
|
||||
{
|
||||
if (!IsRemoteMode())
|
||||
{
|
||||
g_pLog->UnlockMessages();
|
||||
}
|
||||
}
|
||||
|
||||
DownloadQueue* Frontend::LockQueue()
|
||||
{
|
||||
return DownloadQueue::Lock();
|
||||
}
|
||||
|
||||
void Frontend::UnlockQueue()
|
||||
{
|
||||
DownloadQueue::Unlock();
|
||||
}
|
||||
|
||||
bool Frontend::IsRemoteMode()
|
||||
{
|
||||
return g_pOptions->GetRemoteClientMode();
|
||||
}
|
||||
|
||||
void Frontend::ServerPauseUnpause(bool bPause)
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
RequestPauseUnpause(bPause);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pOptions->SetResumeTime(0);
|
||||
g_pOptions->SetPauseDownload(bPause);
|
||||
}
|
||||
}
|
||||
|
||||
void Frontend::ServerSetDownloadRate(int iRate)
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
RequestSetDownloadRate(iRate);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pOptions->SetDownloadRate(iRate);
|
||||
}
|
||||
}
|
||||
|
||||
bool Frontend::ServerEditQueue(DownloadQueue::EEditAction eAction, int iOffset, int iID)
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
return RequestEditQueue(eAction, iOffset, iID);
|
||||
}
|
||||
else
|
||||
{
|
||||
DownloadQueue* pDownloadQueue = LockQueue();
|
||||
bool bOK = pDownloadQueue->EditEntry(iID, eAction, iOffset, NULL);
|
||||
UnlockQueue();
|
||||
return bOK;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Frontend::InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize)
|
||||
{
|
||||
pMessageBase->m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
|
||||
pMessageBase->m_iType = htonl(iRequest);
|
||||
pMessageBase->m_iStructSize = htonl(iSize);
|
||||
|
||||
strncpy(pMessageBase->m_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()
|
||||
{
|
||||
const char* szControlIP = !strcmp(g_pOptions->GetControlIP(), "0.0.0.0") ? "127.0.0.1" : g_pOptions->GetControlIP();
|
||||
Connection connection(szControlIP, g_pOptions->GetControlPort(), false);
|
||||
|
||||
bool OK = connection.Connect();
|
||||
if (!OK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SNZBLogRequest LogRequest;
|
||||
InitMessageBase(&LogRequest.m_MessageBase, eRemoteRequestLog, sizeof(LogRequest));
|
||||
LogRequest.m_iLines = htonl(m_iNeededLogEntries);
|
||||
if (m_iNeededLogEntries == 0)
|
||||
{
|
||||
LogRequest.m_iIDFrom = htonl(m_iNeededLogFirstID > 0 ? m_iNeededLogFirstID : 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
LogRequest.m_iIDFrom = 0;
|
||||
}
|
||||
|
||||
if (!connection.Send((char*)(&LogRequest), sizeof(LogRequest)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now listen for the returned log
|
||||
SNZBLogResponse 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))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
char* pBuf = NULL;
|
||||
if (ntohl(LogResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
pBuf = (char*)malloc(ntohl(LogResponse.m_iTrailingDataLength));
|
||||
if (!connection.Recv(pBuf, ntohl(LogResponse.m_iTrailingDataLength)))
|
||||
{
|
||||
free(pBuf);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
connection.Disconnect();
|
||||
|
||||
if (ntohl(LogResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
char* pBufPtr = (char*)pBuf;
|
||||
for (unsigned int i = 0; i < ntohl(LogResponse.m_iNrTrailingEntries); i++)
|
||||
{
|
||||
SNZBLogResponseEntry* pLogAnswer = (SNZBLogResponseEntry*) pBufPtr;
|
||||
|
||||
char* szText = pBufPtr + sizeof(SNZBLogResponseEntry);
|
||||
|
||||
Message* pMessage = new Message(ntohl(pLogAnswer->m_iID), (Message::EKind)ntohl(pLogAnswer->m_iKind), ntohl(pLogAnswer->m_tTime), szText);
|
||||
m_RemoteMessages.push_back(pMessage);
|
||||
|
||||
pBufPtr += sizeof(SNZBLogResponseEntry) + ntohl(pLogAnswer->m_iTextLen);
|
||||
}
|
||||
|
||||
free(pBuf);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Frontend::RequestFileList()
|
||||
{
|
||||
const char* szControlIP = !strcmp(g_pOptions->GetControlIP(), "0.0.0.0") ? "127.0.0.1" : g_pOptions->GetControlIP();
|
||||
Connection connection(szControlIP, g_pOptions->GetControlPort(), false);
|
||||
|
||||
bool OK = connection.Connect();
|
||||
if (!OK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SNZBListRequest ListRequest;
|
||||
InitMessageBase(&ListRequest.m_MessageBase, eRemoteRequestList, sizeof(ListRequest));
|
||||
ListRequest.m_bFileList = htonl(m_bFileList);
|
||||
ListRequest.m_bServerState = htonl(m_bSummary);
|
||||
|
||||
if (!connection.Send((char*)(&ListRequest), sizeof(ListRequest)))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Now listen for the returned list
|
||||
SNZBListResponse 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))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
char* pBuf = NULL;
|
||||
if (ntohl(ListResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
pBuf = (char*)malloc(ntohl(ListResponse.m_iTrailingDataLength));
|
||||
if (!connection.Recv(pBuf, ntohl(ListResponse.m_iTrailingDataLength)))
|
||||
{
|
||||
free(pBuf);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
connection.Disconnect();
|
||||
|
||||
if (m_bSummary)
|
||||
{
|
||||
m_bPauseDownload = ntohl(ListResponse.m_bDownloadPaused);
|
||||
m_lRemainingSize = Util::JoinInt64(ntohl(ListResponse.m_iRemainingSizeHi), ntohl(ListResponse.m_iRemainingSizeLo));
|
||||
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_bDownloadStandBy);
|
||||
m_iAllBytes = Util::JoinInt64(ntohl(ListResponse.m_iDownloadedBytesHi), ntohl(ListResponse.m_iDownloadedBytesLo));
|
||||
}
|
||||
|
||||
if (m_bFileList && ntohl(ListResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
|
||||
DownloadQueue* pDownloadQueue = LockQueue();
|
||||
client.BuildFileList(&ListResponse, pBuf, pDownloadQueue);
|
||||
UnlockQueue();
|
||||
}
|
||||
|
||||
if (pBuf)
|
||||
{
|
||||
free(pBuf);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Frontend::RequestPauseUnpause(bool bPause)
|
||||
{
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
return client.RequestServerPauseUnpause(bPause, eRemotePauseUnpauseActionDownload);
|
||||
}
|
||||
|
||||
bool Frontend::RequestSetDownloadRate(int iRate)
|
||||
{
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
return client.RequestServerSetDownloadRate(iRate);
|
||||
}
|
||||
|
||||
bool Frontend::RequestEditQueue(DownloadQueue::EEditAction eAction, int iOffset, int iID)
|
||||
{
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
return client.RequestServerEditQueue(eAction, iOffset, NULL, &iID, 1, NULL, eRemoteMatchModeID);
|
||||
}
|
||||
@@ -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-2015 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$
|
||||
@@ -36,8 +36,7 @@
|
||||
class Frontend : public Thread
|
||||
{
|
||||
private:
|
||||
Log::Messages m_RemoteMessages;
|
||||
DownloadQueue m_RemoteQueue;
|
||||
MessageList m_RemoteMessages;
|
||||
|
||||
bool RequestMessages();
|
||||
bool RequestFileList();
|
||||
@@ -50,12 +49,12 @@ protected:
|
||||
int m_iUpdateInterval;
|
||||
|
||||
// summary
|
||||
float m_fCurrentDownloadSpeed;
|
||||
int m_iCurrentDownloadSpeed;
|
||||
long long m_lRemainingSize;
|
||||
bool m_bPause;
|
||||
float m_fDownloadLimit;
|
||||
bool m_bPauseDownload;
|
||||
int m_iDownloadLimit;
|
||||
int m_iThreadCount;
|
||||
int m_iParJobCount;
|
||||
int m_iPostJobCount;
|
||||
int m_iUpTimeSec;
|
||||
int m_iDnTimeSec;
|
||||
long long m_iAllBytes;
|
||||
@@ -63,7 +62,7 @@ protected:
|
||||
|
||||
bool PrepareData();
|
||||
void FreeData();
|
||||
Log::Messages* LockMessages();
|
||||
MessageList* LockMessages();
|
||||
void UnlockMessages();
|
||||
DownloadQueue* LockQueue();
|
||||
void UnlockQueue();
|
||||
@@ -71,12 +70,10 @@ protected:
|
||||
void InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize);
|
||||
void ServerPauseUnpause(bool bPause);
|
||||
bool RequestPauseUnpause(bool bPause);
|
||||
void ServerSetDownloadRate(float fRate);
|
||||
bool RequestSetDownloadRate(float fRate);
|
||||
void ServerDumpDebug();
|
||||
bool RequestDumpDebug();
|
||||
bool ServerEditQueue(QueueEditor::EEditAction eAction, int iOffset, int iEntry);
|
||||
bool RequestEditQueue(int iAction, int iOffset, int iID);
|
||||
void ServerSetDownloadRate(int iRate);
|
||||
bool RequestSetDownloadRate(int iRate);
|
||||
bool ServerEditQueue(DownloadQueue::EEditAction eAction, int iOffset, int iEntry);
|
||||
bool RequestEditQueue(DownloadQueue::EEditAction eAction, int iOffset, int iID);
|
||||
|
||||
public:
|
||||
Frontend();
|
||||
@@ -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-2015 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$
|
||||
@@ -79,7 +79,7 @@ void LoggableFrontend::Update()
|
||||
|
||||
BeforePrint();
|
||||
|
||||
Log::Messages* pMessages = LockMessages();
|
||||
MessageList* pMessages = LockMessages();
|
||||
if (!pMessages->empty())
|
||||
{
|
||||
Message* pFirstMessage = pMessages->front();
|
||||
@@ -126,6 +126,9 @@ void LoggableFrontend::PrintMessage(Message * pMessage)
|
||||
case Message::mkInfo:
|
||||
printf("[INFO] %s\n", msg);
|
||||
break;
|
||||
case Message::mkDetail:
|
||||
printf("[DETAIL] %s\n", msg);
|
||||
break;
|
||||
}
|
||||
#ifdef WIN32
|
||||
free(msg);
|
||||
@@ -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$
|
||||
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-2014 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"
|
||||
@@ -46,33 +47,9 @@ private:
|
||||
eDownloadRate
|
||||
};
|
||||
|
||||
class GroupInfo
|
||||
{
|
||||
private:
|
||||
int m_iID;
|
||||
char* m_szNZBFilename;
|
||||
|
||||
public:
|
||||
int m_iFileCount;
|
||||
long long m_lSize;
|
||||
long long m_lRemainingSize;
|
||||
long long m_lPausedSize;
|
||||
int m_iParCount;
|
||||
|
||||
public:
|
||||
GroupInfo(int iID, const char* szNZBFilename);
|
||||
~GroupInfo();
|
||||
int GetID() { return m_iID; }
|
||||
const char* GetNZBFilename() { return m_szNZBFilename; }
|
||||
long long GetSize() { return m_lSize; }
|
||||
long long GetRemainingSize() { return m_lRemainingSize; }
|
||||
long long GetPausedSize() { return m_lPausedSize; }
|
||||
};
|
||||
|
||||
typedef std::deque<GroupInfo*> GroupQueue;
|
||||
|
||||
bool m_bUseColor;
|
||||
int m_iDataUpdatePos;
|
||||
bool m_bUpdateNextTime;
|
||||
int m_iScreenHeight;
|
||||
int m_iScreenWidth;
|
||||
int m_iQueueWinTop;
|
||||
@@ -85,9 +62,13 @@ private:
|
||||
int m_iLastEditEntry;
|
||||
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;
|
||||
|
||||
@@ -104,7 +85,7 @@ private:
|
||||
bool m_bShowNZBname;
|
||||
bool m_bShowTimestamp;
|
||||
bool m_bGroupFiles;
|
||||
float m_QueueWindowPercentage;
|
||||
int m_QueueWindowPercentage;
|
||||
|
||||
#ifdef WIN32
|
||||
void init_pair(int iColorNumber, WORD wForeColor, WORD wBackColor);
|
||||
@@ -116,23 +97,22 @@ private:
|
||||
void PrintFileQueue();
|
||||
void PrintFilename(FileInfo* pFileInfo, int iRow, bool bSelected);
|
||||
void PrintGroupQueue();
|
||||
void PrintGroupname(GroupInfo * pGroupInfo, int iRow, bool bSelected);
|
||||
void PrepareGroupQueue();
|
||||
void ResetColWidths();
|
||||
void PrintGroupname(NZBInfo* pNZBInfo, int iRow, bool bSelected, bool bCalcColWidth);
|
||||
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);
|
||||
bool EditQueue(DownloadQueue::EEditAction eAction, int iOffset);
|
||||
void SetHint(const char* szHint);
|
||||
|
||||
protected:
|
||||
virtual void Run();
|
||||
1062
daemon/main/CommandLineParser.cpp
Normal file
1062
daemon/main/CommandLineParser.cpp
Normal file
File diff suppressed because it is too large
Load Diff
169
daemon/main/CommandLineParser.h
Normal file
169
daemon/main/CommandLineParser.h
Normal file
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007-2015 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 COMMANDLINEPARSER_H
|
||||
#define COMMANDLINEPARSER_H
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <time.h>
|
||||
|
||||
class CommandLineParser
|
||||
{
|
||||
public:
|
||||
enum EClientOperation
|
||||
{
|
||||
opClientNoOperation,
|
||||
opClientRequestDownload,
|
||||
opClientRequestListFiles,
|
||||
opClientRequestListGroups,
|
||||
opClientRequestListStatus,
|
||||
opClientRequestSetRate,
|
||||
opClientRequestDumpDebug,
|
||||
opClientRequestEditQueue,
|
||||
opClientRequestLog,
|
||||
opClientRequestShutdown,
|
||||
opClientRequestReload,
|
||||
opClientRequestVersion,
|
||||
opClientRequestPostQueue,
|
||||
opClientRequestWriteLog,
|
||||
opClientRequestScanSync,
|
||||
opClientRequestScanAsync,
|
||||
opClientRequestDownloadPause,
|
||||
opClientRequestDownloadUnpause,
|
||||
opClientRequestPostPause,
|
||||
opClientRequestPostUnpause,
|
||||
opClientRequestScanPause,
|
||||
opClientRequestScanUnpause,
|
||||
opClientRequestHistory,
|
||||
opClientRequestHistoryAll
|
||||
};
|
||||
enum EMatchMode
|
||||
{
|
||||
mmID = 1,
|
||||
mmName,
|
||||
mmRegEx
|
||||
};
|
||||
|
||||
typedef std::vector<char*> NameList;
|
||||
|
||||
private:
|
||||
bool m_bNoConfig;
|
||||
char* m_szConfigFilename;
|
||||
|
||||
// Parsed command-line parameters
|
||||
bool m_bErrors;
|
||||
bool m_bPrintVersion;
|
||||
bool m_bPrintUsage;
|
||||
bool m_bServerMode;
|
||||
bool m_bDaemonMode;
|
||||
bool m_bRemoteClientMode;
|
||||
EClientOperation m_eClientOperation;
|
||||
NameList m_OptionList;
|
||||
int m_iEditQueueAction;
|
||||
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;
|
||||
char* m_szAddDupeKey;
|
||||
int m_iAddDupeScore;
|
||||
int m_iAddDupeMode;
|
||||
int m_iSetRate;
|
||||
int m_iLogLines;
|
||||
int m_iWriteLogKind;
|
||||
bool m_bTestBacktrace;
|
||||
bool m_bWebGet;
|
||||
char* m_szWebGetFilename;
|
||||
bool m_bSigVerify;
|
||||
char* m_szPubKeyFilename;
|
||||
char* m_szSigFilename;
|
||||
bool m_bPauseDownload;
|
||||
|
||||
void InitCommandLine(int argc, const char* argv[]);
|
||||
void InitFileArg(int argc, const char* argv[]);
|
||||
void ParseFileIDList(int argc, const char* argv[], int optind);
|
||||
void ParseFileNameList(int argc, const char* argv[], int optind);
|
||||
bool ParseTime(const char* szTime, int* pHours, int* pMinutes);
|
||||
void ReportError(const char* szErrMessage);
|
||||
|
||||
public:
|
||||
CommandLineParser(int argc, const char* argv[]);
|
||||
~CommandLineParser();
|
||||
|
||||
void PrintUsage(const char* com);
|
||||
|
||||
bool GetErrors() { return m_bErrors; }
|
||||
bool GetNoConfig() { return m_bNoConfig; }
|
||||
const char* GetConfigFilename() { return m_szConfigFilename; }
|
||||
bool GetServerMode() { return m_bServerMode; }
|
||||
bool GetDaemonMode() { return m_bDaemonMode; }
|
||||
bool GetRemoteClientMode() { return m_bRemoteClientMode; }
|
||||
EClientOperation GetClientOperation() { return m_eClientOperation; }
|
||||
NameList* GetOptionList() { return &m_OptionList; }
|
||||
int GetEditQueueAction() { return m_iEditQueueAction; }
|
||||
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; }
|
||||
const char* GetAddDupeKey() { return m_szAddDupeKey; }
|
||||
int GetAddDupeScore() { return m_iAddDupeScore; }
|
||||
int GetAddDupeMode() { return m_iAddDupeMode; }
|
||||
int GetSetRate() { return m_iSetRate; }
|
||||
int GetLogLines() { return m_iLogLines; }
|
||||
int GetWriteLogKind() { return m_iWriteLogKind; }
|
||||
bool GetTestBacktrace() { return m_bTestBacktrace; }
|
||||
bool GetWebGet() { return m_bWebGet; }
|
||||
const char* GetWebGetFilename() { return m_szWebGetFilename; }
|
||||
bool GetSigVerify() { return m_bSigVerify; }
|
||||
const char* GetPubKeyFilename() { return m_szPubKeyFilename; }
|
||||
const char* GetSigFilename() { return m_szSigFilename; }
|
||||
bool GetPrintOptions() { return m_bPrintOptions; }
|
||||
bool GetPrintVersion() { return m_bPrintVersion; }
|
||||
bool GetPrintUsage() { return m_bPrintUsage; }
|
||||
bool GetPauseDownload() const { return m_bPauseDownload; }
|
||||
};
|
||||
|
||||
extern CommandLineParser* g_pCommandLineParser;
|
||||
|
||||
#endif
|
||||
131
daemon/main/DiskService.cpp
Normal file
131
daemon/main/DiskService.cpp
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2015 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 "nzbget.h"
|
||||
#include "DiskService.h"
|
||||
#include "Options.h"
|
||||
#include "StatMeter.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
DiskService::DiskService()
|
||||
{
|
||||
m_iInterval = 0;
|
||||
m_bWaitingReported = false;
|
||||
m_bWaitingRequiredDir = true;
|
||||
}
|
||||
|
||||
void DiskService::ServiceWork()
|
||||
{
|
||||
m_iInterval++;
|
||||
if (m_iInterval == 5)
|
||||
{
|
||||
if (!g_pOptions->GetPauseDownload() &&
|
||||
g_pOptions->GetDiskSpace() > 0 && !g_pStatMeter->GetStandBy())
|
||||
{
|
||||
// check free disk space every 1 second
|
||||
CheckDiskSpace();
|
||||
}
|
||||
m_iInterval = 0;
|
||||
}
|
||||
|
||||
if (m_bWaitingRequiredDir)
|
||||
{
|
||||
CheckRequiredDir();
|
||||
}
|
||||
}
|
||||
|
||||
void DiskService::CheckDiskSpace()
|
||||
{
|
||||
long long lFreeSpace = Util::FreeDiskSize(g_pOptions->GetDestDir());
|
||||
if (lFreeSpace > -1 && lFreeSpace / 1024 / 1024 < g_pOptions->GetDiskSpace())
|
||||
{
|
||||
warn("Low disk space on %s. Pausing download", g_pOptions->GetDestDir());
|
||||
g_pOptions->SetPauseDownload(true);
|
||||
}
|
||||
|
||||
if (!Util::EmptyStr(g_pOptions->GetInterDir()))
|
||||
{
|
||||
lFreeSpace = Util::FreeDiskSize(g_pOptions->GetInterDir());
|
||||
if (lFreeSpace > -1 && lFreeSpace / 1024 / 1024 < g_pOptions->GetDiskSpace())
|
||||
{
|
||||
warn("Low disk space on %s. Pausing download", g_pOptions->GetInterDir());
|
||||
g_pOptions->SetPauseDownload(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DiskService::CheckRequiredDir()
|
||||
{
|
||||
if (!Util::EmptyStr(g_pOptions->GetRequiredDir()))
|
||||
{
|
||||
bool bAllExist = true;
|
||||
bool bWasWaitingReported = m_bWaitingReported;
|
||||
// split RequiredDir into tokens
|
||||
Tokenizer tok(g_pOptions->GetRequiredDir(), ",;");
|
||||
while (const char* szDir = tok.Next())
|
||||
{
|
||||
if (!Util::FileExists(szDir) && !Util::DirectoryExists(szDir))
|
||||
{
|
||||
if (!bWasWaitingReported)
|
||||
{
|
||||
info("Waiting for required directory %s", szDir);
|
||||
m_bWaitingReported = true;
|
||||
}
|
||||
bAllExist = false;
|
||||
}
|
||||
}
|
||||
if (!bAllExist)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_bWaitingReported)
|
||||
{
|
||||
info("All required directories available");
|
||||
}
|
||||
|
||||
g_pOptions->SetTempPauseDownload(false);
|
||||
g_pOptions->SetTempPausePostprocess(false);
|
||||
m_bWaitingRequiredDir = false;
|
||||
}
|
||||
49
daemon/main/DiskService.h
Normal file
49
daemon/main/DiskService.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2015 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 DISKSERVICE_H
|
||||
#define DISKSERVICE_H
|
||||
|
||||
#include "Service.h"
|
||||
|
||||
class DiskService : public Service
|
||||
{
|
||||
private:
|
||||
int m_iInterval;
|
||||
bool m_bWaitingRequiredDir;
|
||||
bool m_bWaitingReported;
|
||||
|
||||
void CheckDiskSpace();
|
||||
void CheckRequiredDir();
|
||||
|
||||
protected:
|
||||
virtual int ServiceInterval() { return 200; }
|
||||
virtual void ServiceWork();
|
||||
|
||||
public:
|
||||
DiskService();
|
||||
};
|
||||
|
||||
#endif
|
||||
507
daemon/main/Maintenance.cpp
Normal file
507
daemon/main/Maintenance.cpp
Normal file
@@ -0,0 +1,507 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2013-2015 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>
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
#include <openssl/rsa.h>
|
||||
#include <openssl/sha.h>
|
||||
#include <openssl/pem.h>
|
||||
#endif /* HAVE_OPENSSL */
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include "Maintenance.h"
|
||||
#include "Options.h"
|
||||
#include "CommandLineParser.h"
|
||||
|
||||
extern void ExitProc();
|
||||
extern int g_iArgumentCount;
|
||||
extern char* (*g_szArguments)[];
|
||||
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
class Signature
|
||||
{
|
||||
private:
|
||||
const char* m_szInFilename;
|
||||
const char* m_szSigFilename;
|
||||
const char* m_szPubKeyFilename;
|
||||
unsigned char m_InHash[SHA256_DIGEST_LENGTH];
|
||||
unsigned char m_Signature[256];
|
||||
RSA* m_pPubKey;
|
||||
|
||||
bool ReadSignature();
|
||||
bool ComputeInHash();
|
||||
bool ReadPubKey();
|
||||
|
||||
public:
|
||||
Signature(const char* szInFilename, const char* szSigFilename, const char* szPubKeyFilename);
|
||||
~Signature();
|
||||
bool Verify();
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
m_Messages.Clear();
|
||||
|
||||
free(m_szUpdateScript);
|
||||
}
|
||||
|
||||
void Maintenance::ResetUpdateController()
|
||||
{
|
||||
m_mutexController.Lock();
|
||||
m_UpdateScriptController = NULL;
|
||||
m_mutexController.Unlock();
|
||||
}
|
||||
|
||||
MessageList* Maintenance::LockMessages()
|
||||
{
|
||||
m_mutexLog.Lock();
|
||||
return &m_Messages;
|
||||
}
|
||||
|
||||
void Maintenance::UnlockMessages()
|
||||
{
|
||||
m_mutexLog.Unlock();
|
||||
}
|
||||
|
||||
void Maintenance::AddMessage(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;
|
||||
}
|
||||
|
||||
// make absolute path
|
||||
if (m_szUpdateScript[0] != PATH_SEPARATOR
|
||||
#ifdef WIN32
|
||||
&& !(strlen(m_szUpdateScript) > 2 && m_szUpdateScript[1] == ':')
|
||||
#endif
|
||||
)
|
||||
{
|
||||
char szFilename[MAX_PATH + 100];
|
||||
snprintf(szFilename, sizeof(szFilename), "%s%c%s", g_pOptions->GetAppDir(), PATH_SEPARATOR, m_szUpdateScript);
|
||||
free(m_szUpdateScript);
|
||||
m_szUpdateScript = strdup(szFilename);
|
||||
}
|
||||
|
||||
m_Messages.Clear();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
bool Maintenance::VerifySignature(const char* szInFilename, const char* szSigFilename, const char* szPubKeyFilename)
|
||||
{
|
||||
#ifdef HAVE_OPENSSL
|
||||
Signature signature(szInFilename, szSigFilename, szPubKeyFilename);
|
||||
return signature.Verify();
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
void UpdateScriptController::Run()
|
||||
{
|
||||
// the update-script should not be automatically terminated when the program quits
|
||||
UnregisterRunningScript();
|
||||
|
||||
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]);
|
||||
|
||||
SetEnvVar("NZBUP_RUNMODE", g_pCommandLineParser->GetDaemonMode() ? "DAEMON" : "SERVER");
|
||||
|
||||
for (int i = 0; i < g_iArgumentCount; i++)
|
||||
{
|
||||
char szEnvName[40];
|
||||
snprintf(szEnvName, 40, "NZBUP_CMDLINE%i", i);
|
||||
szInfoName[40-1] = '\0';
|
||||
SetEnvVar(szEnvName, (*g_szArguments)[i]);
|
||||
}
|
||||
|
||||
char szProcessID[20];
|
||||
#ifdef WIN32
|
||||
int pid = (int)GetCurrentProcessId();
|
||||
#else
|
||||
int pid = (int)getpid();
|
||||
#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;
|
||||
|
||||
if (!strncmp(szText, "[NZB] ", 6))
|
||||
{
|
||||
debug("Command %s detected", szText + 6);
|
||||
if (!strcmp(szText + 6, "QUIT"))
|
||||
{
|
||||
Detach();
|
||||
ExitProc();
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Invalid command \"%s\" received", szText);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pMaintenance->AddMessage(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);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENSSL
|
||||
Signature::Signature(const char *szInFilename, const char *szSigFilename, const char *szPubKeyFilename)
|
||||
{
|
||||
m_szInFilename = szInFilename;
|
||||
m_szSigFilename = szSigFilename;
|
||||
m_szPubKeyFilename = szPubKeyFilename;
|
||||
m_pPubKey = NULL;
|
||||
}
|
||||
|
||||
Signature::~Signature()
|
||||
{
|
||||
RSA_free(m_pPubKey);
|
||||
}
|
||||
|
||||
// Calculate SHA-256 for input file (m_szInFilename)
|
||||
bool Signature::ComputeInHash()
|
||||
{
|
||||
FILE* infile = fopen(m_szInFilename, FOPEN_RB);
|
||||
if (!infile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
SHA256_CTX sha256;
|
||||
SHA256_Init(&sha256);
|
||||
const int bufSize = 32*1024;
|
||||
char* buffer = (char*)malloc(bufSize);
|
||||
while(int bytesRead = fread(buffer, 1, bufSize, infile))
|
||||
{
|
||||
SHA256_Update(&sha256, buffer, bytesRead);
|
||||
}
|
||||
SHA256_Final(m_InHash, &sha256);
|
||||
free(buffer);
|
||||
fclose(infile);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Read signature from file (m_szSigFilename) into memory
|
||||
bool Signature::ReadSignature()
|
||||
{
|
||||
char szSigTitle[256];
|
||||
snprintf(szSigTitle, sizeof(szSigTitle), "\"RSA-SHA256(%s)\" : \"", Util::BaseFileName(m_szInFilename));
|
||||
szSigTitle[256-1] = '\0';
|
||||
|
||||
FILE* infile = fopen(m_szSigFilename, FOPEN_RB);
|
||||
if (!infile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bOK = false;
|
||||
int iTitLen = strlen(szSigTitle);
|
||||
char buf[1024];
|
||||
unsigned char* output = m_Signature;
|
||||
while (fgets(buf, sizeof(buf) - 1, infile))
|
||||
{
|
||||
if (!strncmp(buf, szSigTitle, iTitLen))
|
||||
{
|
||||
char* szHexSig = buf + iTitLen;
|
||||
int iSigLen = strlen(szHexSig);
|
||||
if (iSigLen > 2)
|
||||
{
|
||||
szHexSig[iSigLen - 2] = '\0'; // trim trailing ",
|
||||
}
|
||||
for (; *szHexSig && *(szHexSig+1);)
|
||||
{
|
||||
unsigned char c1 = *szHexSig++;
|
||||
unsigned char c2 = *szHexSig++;
|
||||
c1 = '0' <= c1 && c1 <= '9' ? c1 - '0' : 'A' <= c1 && c1 <= 'F' ? c1 - 'A' + 10 :
|
||||
'a' <= c1 && c1 <= 'f' ? c1 - 'a' + 10 : 0;
|
||||
c2 = '0' <= c2 && c2 <= '9' ? c2 - '0' : 'A' <= c2 && c2 <= 'F' ? c2 - 'A' + 10 :
|
||||
'a' <= c2 && c2 <= 'f' ? c2 - 'a' + 10 : 0;
|
||||
unsigned char ch = (c1 << 4) + c2;
|
||||
*output++ = (char)ch;
|
||||
}
|
||||
bOK = output == m_Signature + sizeof(m_Signature);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(infile);
|
||||
return bOK;
|
||||
}
|
||||
|
||||
// Read public key from file (m_szPubKeyFilename) into memory
|
||||
bool Signature::ReadPubKey()
|
||||
{
|
||||
char* keybuf;
|
||||
int keybuflen;
|
||||
if (!Util::LoadFileIntoBuffer(m_szPubKeyFilename, &keybuf, &keybuflen))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
BIO* mem = BIO_new_mem_buf(keybuf, keybuflen);
|
||||
m_pPubKey = PEM_read_bio_RSA_PUBKEY(mem, NULL, NULL, NULL);
|
||||
BIO_free(mem);
|
||||
free(keybuf);
|
||||
return m_pPubKey != NULL;
|
||||
}
|
||||
|
||||
bool Signature::Verify()
|
||||
{
|
||||
return ComputeInHash() && ReadSignature() && ReadPubKey() &&
|
||||
RSA_verify(NID_sha256, m_InHash, sizeof(m_InHash), m_Signature, sizeof(m_Signature), m_pPubKey) == 1;
|
||||
}
|
||||
#endif /* HAVE_OPENSSL */
|
||||
95
daemon/main/Maintenance.h
Normal file
95
daemon/main/Maintenance.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2013-2015 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 "Script.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
class UpdateScriptController;
|
||||
|
||||
class Maintenance
|
||||
{
|
||||
private:
|
||||
MessageList 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 AddMessage(Message::EKind eKind, time_t tTime, const char* szText);
|
||||
MessageList* LockMessages();
|
||||
void UnlockMessages();
|
||||
bool StartUpdate(EBranch eBranch);
|
||||
void ResetUpdateController();
|
||||
bool CheckUpdates(char** pUpdateInfo);
|
||||
static bool VerifySignature(const char* szInFilename, const char* szSigFilename, const char* szPubKeyFilename);
|
||||
};
|
||||
|
||||
extern Maintenance* g_pMaintenance;
|
||||
|
||||
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
|
||||
2098
daemon/main/Options.cpp
Normal file
2098
daemon/main/Options.cpp
Normal file
File diff suppressed because it is too large
Load Diff
470
daemon/main/Options.h
Normal file
470
daemon/main/Options.h
Normal file
@@ -0,0 +1,470 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2015 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 OPTIONS_H
|
||||
#define OPTIONS_H
|
||||
|
||||
#include <vector>
|
||||
#include <list>
|
||||
#include <time.h>
|
||||
|
||||
#include "Thread.h"
|
||||
#include "Util.h"
|
||||
|
||||
class Options
|
||||
{
|
||||
public:
|
||||
enum EWriteLog
|
||||
{
|
||||
wlNone,
|
||||
wlAppend,
|
||||
wlReset,
|
||||
wlRotate
|
||||
};
|
||||
enum EMessageTarget
|
||||
{
|
||||
mtNone,
|
||||
mtScreen,
|
||||
mtLog,
|
||||
mtBoth
|
||||
};
|
||||
enum EOutputMode
|
||||
{
|
||||
omLoggable,
|
||||
omColored,
|
||||
omNCurses
|
||||
};
|
||||
enum EParCheck
|
||||
{
|
||||
pcAuto,
|
||||
pcAlways,
|
||||
pcForce,
|
||||
pcManual
|
||||
};
|
||||
enum EParScan
|
||||
{
|
||||
psLimited,
|
||||
psExtended,
|
||||
psFull,
|
||||
psDupe
|
||||
};
|
||||
enum EHealthCheck
|
||||
{
|
||||
hcPause,
|
||||
hcDelete,
|
||||
hcNone
|
||||
};
|
||||
enum ESchedulerCommand
|
||||
{
|
||||
scPauseDownload,
|
||||
scUnpauseDownload,
|
||||
scPausePostProcess,
|
||||
scUnpausePostProcess,
|
||||
scDownloadRate,
|
||||
scScript,
|
||||
scProcess,
|
||||
scPauseScan,
|
||||
scUnpauseScan,
|
||||
scActivateServer,
|
||||
scDeactivateServer,
|
||||
scFetchFeed
|
||||
};
|
||||
|
||||
class OptEntry
|
||||
{
|
||||
private:
|
||||
char* m_szName;
|
||||
char* m_szValue;
|
||||
char* m_szDefValue;
|
||||
int m_iLineNo;
|
||||
|
||||
void SetLineNo(int iLineNo) { m_iLineNo = iLineNo; }
|
||||
|
||||
friend class Options;
|
||||
|
||||
public:
|
||||
OptEntry();
|
||||
OptEntry(const char* szName, const char* szValue);
|
||||
~OptEntry();
|
||||
void SetName(const char* szName);
|
||||
const char* GetName() { return m_szName; }
|
||||
void SetValue(const char* szValue);
|
||||
const char* GetValue() { return m_szValue; }
|
||||
const char* GetDefValue() { return m_szDefValue; }
|
||||
int GetLineNo() { return m_iLineNo; }
|
||||
bool Restricted();
|
||||
};
|
||||
|
||||
typedef std::vector<OptEntry*> OptEntriesBase;
|
||||
|
||||
class OptEntries: public OptEntriesBase
|
||||
{
|
||||
public:
|
||||
~OptEntries();
|
||||
OptEntry* FindOption(const char* szName);
|
||||
};
|
||||
|
||||
typedef std::vector<char*> NameList;
|
||||
typedef std::vector<const char*> CmdOptList;
|
||||
|
||||
class Category
|
||||
{
|
||||
private:
|
||||
char* m_szName;
|
||||
char* m_szDestDir;
|
||||
bool m_bUnpack;
|
||||
char* m_szPostScript;
|
||||
NameList m_Aliases;
|
||||
|
||||
public:
|
||||
Category(const char* szName, const char* szDestDir, bool bUnpack, const char* szPostScript);
|
||||
~Category();
|
||||
const char* GetName() { return m_szName; }
|
||||
const char* GetDestDir() { return m_szDestDir; }
|
||||
bool GetUnpack() { return m_bUnpack; }
|
||||
const char* GetPostScript() { return m_szPostScript; }
|
||||
NameList* GetAliases() { return &m_Aliases; }
|
||||
};
|
||||
|
||||
typedef std::vector<Category*> CategoriesBase;
|
||||
|
||||
class Categories: public CategoriesBase
|
||||
{
|
||||
public:
|
||||
~Categories();
|
||||
Category* FindCategory(const char* szName, bool bSearchAliases);
|
||||
};
|
||||
|
||||
class Extender
|
||||
{
|
||||
public:
|
||||
virtual void AddNewsServer(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 iRetention,
|
||||
int iLevel, int iGroup) = 0;
|
||||
virtual void AddFeed(int iID, const char* szName, const char* szUrl, int iInterval,
|
||||
const char* szFilter, bool bBacklog, bool bPauseNzb, const char* szCategory,
|
||||
int iPriority, const char* szFeedScript) {}
|
||||
virtual void AddTask(int iID, int iHours, int iMinutes, int iWeekDaysBits, ESchedulerCommand eCommand,
|
||||
const char* szParam) {}
|
||||
virtual void SetupFirstStart() {}
|
||||
};
|
||||
|
||||
private:
|
||||
OptEntries m_OptEntries;
|
||||
Mutex m_mutexOptEntries;
|
||||
Categories m_Categories;
|
||||
bool m_bNoDiskAccess;
|
||||
bool m_bFatalError;
|
||||
Extender* m_pExtender;
|
||||
|
||||
// Options
|
||||
bool m_bConfigErrors;
|
||||
int m_iConfigLine;
|
||||
char* m_szAppDir;
|
||||
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;
|
||||
char* m_szRequiredDir;
|
||||
EMessageTarget m_eInfoTarget;
|
||||
EMessageTarget m_eWarningTarget;
|
||||
EMessageTarget m_eErrorTarget;
|
||||
EMessageTarget m_eDebugTarget;
|
||||
EMessageTarget m_eDetailTarget;
|
||||
bool m_bDecode;
|
||||
bool m_bBrokenLog;
|
||||
bool m_bNzbLog;
|
||||
int m_iArticleTimeout;
|
||||
int m_iUrlTimeout;
|
||||
int m_iTerminateTimeout;
|
||||
bool m_bAppendCategoryDir;
|
||||
bool m_bContinuePartial;
|
||||
int m_iRetries;
|
||||
int m_iRetryInterval;
|
||||
bool m_bSaveQueue;
|
||||
bool m_bFlushQueue;
|
||||
bool m_bDupeCheck;
|
||||
char* m_szControlIP;
|
||||
char* m_szControlUsername;
|
||||
char* m_szControlPassword;
|
||||
char* m_szRestrictedUsername;
|
||||
char* m_szRestrictedPassword;
|
||||
char* m_szAddUsername;
|
||||
char* m_szAddPassword;
|
||||
int m_iControlPort;
|
||||
bool m_bSecureControl;
|
||||
int m_iSecurePort;
|
||||
char* m_szSecureCert;
|
||||
char* m_szSecureKey;
|
||||
char* m_szAuthorizedIP;
|
||||
char* m_szLockFile;
|
||||
char* m_szDaemonUsername;
|
||||
EOutputMode m_eOutputMode;
|
||||
bool m_bReloadQueue;
|
||||
int m_iUrlConnections;
|
||||
int m_iLogBufferSize;
|
||||
EWriteLog m_eWriteLog;
|
||||
int m_iRotateLog;
|
||||
char* m_szLogFile;
|
||||
EParCheck m_eParCheck;
|
||||
bool m_bParRepair;
|
||||
EParScan m_eParScan;
|
||||
bool m_bParQuick;
|
||||
bool m_bParRename;
|
||||
int m_iParBuffer;
|
||||
int m_iParThreads;
|
||||
EHealthCheck m_eHealthCheck;
|
||||
char* m_szPostScript;
|
||||
char* m_szScriptOrder;
|
||||
char* m_szScanScript;
|
||||
char* m_szQueueScript;
|
||||
char* m_szFeedScript;
|
||||
bool m_bNoConfig;
|
||||
int m_iUMask;
|
||||
int m_iUpdateInterval;
|
||||
bool m_bCursesNZBName;
|
||||
bool m_bCursesTime;
|
||||
bool m_bCursesGroup;
|
||||
bool m_bCrcCheck;
|
||||
bool m_bDirectWrite;
|
||||
int m_iWriteBuffer;
|
||||
int m_iNzbDirInterval;
|
||||
int m_iNzbDirFileAge;
|
||||
bool m_bParCleanupQueue;
|
||||
int m_iDiskSpace;
|
||||
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;
|
||||
char* m_szUnpackPassFile;
|
||||
bool m_bUnpackPauseQueue;
|
||||
char* m_szExtCleanupDisk;
|
||||
char* m_szParIgnoreExt;
|
||||
int m_iFeedHistory;
|
||||
bool m_bUrlForce;
|
||||
int m_iTimeCorrection;
|
||||
int m_iPropagationDelay;
|
||||
int m_iArticleCache;
|
||||
int m_iEventInterval;
|
||||
|
||||
// Current state
|
||||
bool m_bServerMode;
|
||||
bool m_bRemoteClientMode;
|
||||
bool m_bPauseDownload;
|
||||
bool m_bPausePostProcess;
|
||||
bool m_bPauseScan;
|
||||
bool m_bTempPauseDownload;
|
||||
int m_iDownloadRate;
|
||||
time_t m_tResumeTime;
|
||||
int m_iLocalTimeOffset;
|
||||
bool m_bTempPausePostprocess;
|
||||
|
||||
void Init(const char* szExeName, const char* szConfigFilename, bool bNoConfig,
|
||||
CmdOptList* pCommandLineOptions, bool bNoDiskAccess, Extender* pExtender);
|
||||
void InitDefaults();
|
||||
void InitOptions();
|
||||
void InitOptFile();
|
||||
void InitServers();
|
||||
void InitCategories();
|
||||
void InitScheduler();
|
||||
void InitFeeds();
|
||||
void InitCommandLineOptions(CmdOptList* pCommandLineOptions);
|
||||
void CheckOptions();
|
||||
void Dump();
|
||||
int ParseEnumValue(const char* OptName, int argc, const char* argn[], const int argv[]);
|
||||
int ParseIntValue(const char* OptName, int iBase);
|
||||
OptEntry* FindOption(const char* optname);
|
||||
const char* GetOption(const char* optname);
|
||||
void SetOption(const char* optname, const char* value);
|
||||
bool SetOptionString(const char* option);
|
||||
bool ValidateOptionName(const char* optname, const char* optvalue);
|
||||
void LoadConfigFile();
|
||||
void CheckDir(char** dir, const char* szOptionName, const char* szParentDir,
|
||||
bool bAllowEmpty, bool bCreate);
|
||||
bool ParseTime(const char* szTime, 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);
|
||||
|
||||
public:
|
||||
Options(const char* szExeName, const char* szConfigFilename, bool bNoConfig,
|
||||
CmdOptList* pCommandLineOptions, Extender* pExtender);
|
||||
Options(CmdOptList* pCommandLineOptions, Extender* pExtender);
|
||||
~Options();
|
||||
|
||||
bool SplitOptionString(const char* option, char** pOptName, char** pOptValue);
|
||||
bool GetFatalError() { return m_bFatalError; }
|
||||
OptEntries* LockOptEntries();
|
||||
void UnlockOptEntries();
|
||||
|
||||
// Options
|
||||
const char* GetConfigFilename() { return m_szConfigFilename; }
|
||||
bool GetConfigErrors() { return m_bConfigErrors; }
|
||||
const char* GetAppDir() { return m_szAppDir; }
|
||||
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; }
|
||||
const char* GetRequiredDir() { return m_szRequiredDir; }
|
||||
bool GetBrokenLog() const { return m_bBrokenLog; }
|
||||
bool GetNzbLog() const { return m_bNzbLog; }
|
||||
EMessageTarget GetInfoTarget() const { return m_eInfoTarget; }
|
||||
EMessageTarget GetWarningTarget() const { return m_eWarningTarget; }
|
||||
EMessageTarget GetErrorTarget() const { return m_eErrorTarget; }
|
||||
EMessageTarget GetDebugTarget() const { return m_eDebugTarget; }
|
||||
EMessageTarget GetDetailTarget() const { return m_eDetailTarget; }
|
||||
int GetArticleTimeout() { return m_iArticleTimeout; }
|
||||
int GetUrlTimeout() { return m_iUrlTimeout; }
|
||||
int GetTerminateTimeout() { return m_iTerminateTimeout; }
|
||||
bool GetDecode() { return m_bDecode; };
|
||||
bool GetAppendCategoryDir() { return m_bAppendCategoryDir; }
|
||||
bool GetContinuePartial() { return m_bContinuePartial; }
|
||||
int GetRetries() { return m_iRetries; }
|
||||
int GetRetryInterval() { return m_iRetryInterval; }
|
||||
bool GetSaveQueue() { return m_bSaveQueue; }
|
||||
bool GetFlushQueue() { return m_bFlushQueue; }
|
||||
bool GetDupeCheck() { return m_bDupeCheck; }
|
||||
const char* GetControlIP() { return m_szControlIP; }
|
||||
const char* GetControlUsername() { return m_szControlUsername; }
|
||||
const char* GetControlPassword() { return m_szControlPassword; }
|
||||
const char* GetRestrictedUsername() { return m_szRestrictedUsername; }
|
||||
const char* GetRestrictedPassword() { return m_szRestrictedPassword; }
|
||||
const char* GetAddUsername() { return m_szAddUsername; }
|
||||
const char* GetAddPassword() { return m_szAddPassword; }
|
||||
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; }
|
||||
int GetUrlConnections() { return m_iUrlConnections; }
|
||||
int GetLogBufferSize() { return m_iLogBufferSize; }
|
||||
EWriteLog GetWriteLog() { return m_eWriteLog; }
|
||||
const char* GetLogFile() { return m_szLogFile; }
|
||||
int GetRotateLog() { return m_iRotateLog; }
|
||||
EParCheck GetParCheck() { return m_eParCheck; }
|
||||
bool GetParRepair() { return m_bParRepair; }
|
||||
EParScan GetParScan() { return m_eParScan; }
|
||||
bool GetParQuick() { return m_bParQuick; }
|
||||
bool GetParRename() { return m_bParRename; }
|
||||
int GetParBuffer() { return m_iParBuffer; }
|
||||
int GetParThreads() { return m_iParThreads; }
|
||||
EHealthCheck GetHealthCheck() { return m_eHealthCheck; }
|
||||
const char* GetScriptOrder() { return m_szScriptOrder; }
|
||||
const char* GetPostScript() { return m_szPostScript; }
|
||||
const char* GetScanScript() { return m_szScanScript; }
|
||||
const char* GetQueueScript() { return m_szQueueScript; }
|
||||
const char* GetFeedScript() { return m_szFeedScript; }
|
||||
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 GetDirectWrite() { return m_bDirectWrite; }
|
||||
int GetWriteBuffer() { return m_iWriteBuffer; }
|
||||
int GetNzbDirInterval() { return m_iNzbDirInterval; }
|
||||
int GetNzbDirFileAge() { return m_iNzbDirFileAge; }
|
||||
bool GetParCleanupQueue() { return m_bParCleanupQueue; }
|
||||
int GetDiskSpace() { return m_iDiskSpace; }
|
||||
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; }
|
||||
const char* GetUnpackPassFile() { return m_szUnpackPassFile; }
|
||||
bool GetUnpackPauseQueue() { return m_bUnpackPauseQueue; }
|
||||
const char* GetExtCleanupDisk() { return m_szExtCleanupDisk; }
|
||||
const char* GetParIgnoreExt() { return m_szParIgnoreExt; }
|
||||
int GetFeedHistory() { return m_iFeedHistory; }
|
||||
bool GetUrlForce() { return m_bUrlForce; }
|
||||
int GetTimeCorrection() { return m_iTimeCorrection; }
|
||||
int GetPropagationDelay() { return m_iPropagationDelay; }
|
||||
int GetArticleCache() { return m_iArticleCache; }
|
||||
int GetEventInterval() { return m_iEventInterval; }
|
||||
|
||||
Categories* GetCategories() { return &m_Categories; }
|
||||
Category* FindCategory(const char* szName, bool bSearchAliases) { return m_Categories.FindCategory(szName, bSearchAliases); }
|
||||
|
||||
// Current state
|
||||
void SetServerMode(bool bServerMode) { m_bServerMode = bServerMode; }
|
||||
bool GetServerMode() { return m_bServerMode; }
|
||||
void SetRemoteClientMode(bool bRemoteClientMode) { m_bRemoteClientMode = bRemoteClientMode; }
|
||||
bool GetRemoteClientMode() { return m_bRemoteClientMode; }
|
||||
void SetPauseDownload(bool bPauseDownload) { m_bPauseDownload = bPauseDownload; }
|
||||
bool GetPauseDownload() const { return m_bPauseDownload; }
|
||||
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 SetTempPauseDownload(bool bTempPauseDownload) { m_bTempPauseDownload = bTempPauseDownload; }
|
||||
bool GetTempPauseDownload() const { return m_bTempPauseDownload; }
|
||||
bool GetTempPausePostprocess() const { return m_bTempPausePostprocess; }
|
||||
void SetTempPausePostprocess(bool bTempPausePostprocess) { m_bTempPausePostprocess = bTempPausePostprocess; }
|
||||
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; }
|
||||
void SetLocalTimeOffset(int iLocalTimeOffset) { m_iLocalTimeOffset = iLocalTimeOffset; }
|
||||
int GetLocalTimeOffset() { return m_iLocalTimeOffset; }
|
||||
};
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
#endif
|
||||
377
daemon/main/Scheduler.cpp
Normal file
377
daemon/main/Scheduler.cpp
Normal file
@@ -0,0 +1,377 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2008-2015 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 "Options.h"
|
||||
#include "Log.h"
|
||||
#include "NewsServer.h"
|
||||
#include "ServerPool.h"
|
||||
#include "FeedInfo.h"
|
||||
#include "FeedCoordinator.h"
|
||||
#include "SchedulerScript.h"
|
||||
|
||||
Scheduler::Task::Task(int iID, int iHours, int iMinutes, int iWeekDaysBits, ECommand eCommand, const char* szParam)
|
||||
{
|
||||
m_iID = iID;
|
||||
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_bFirstChecked = false;
|
||||
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
|
||||
CheckTasks();
|
||||
}
|
||||
|
||||
void Scheduler::ServiceWork()
|
||||
{
|
||||
if (!DownloadQueue::IsLoaded())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_bFirstChecked)
|
||||
{
|
||||
FirstCheck();
|
||||
m_bFirstChecked = true;
|
||||
return;
|
||||
}
|
||||
|
||||
m_bExecuteProcess = true;
|
||||
CheckTasks();
|
||||
CheckScheduledResume();
|
||||
}
|
||||
|
||||
void Scheduler::CheckTasks()
|
||||
{
|
||||
PrepareLog();
|
||||
|
||||
m_mutexTaskList.Lock();
|
||||
|
||||
time_t tCurrent = time(NULL);
|
||||
|
||||
if (!m_TaskList.empty())
|
||||
{
|
||||
// Detect large step changes of system time
|
||||
time_t tDiff = tCurrent - m_tLastCheck;
|
||||
if (tDiff > 60*90 || tDiff < 0)
|
||||
{
|
||||
debug("Reset scheduled tasks (detected clock change greater than 90 minutes or negative)");
|
||||
|
||||
// check all tasks for the last week
|
||||
m_tLastCheck = tCurrent - 60*60*24*7;
|
||||
m_bExecuteProcess = false;
|
||||
|
||||
for (TaskList::iterator it = m_TaskList.begin(); it != m_TaskList.end(); it++)
|
||||
{
|
||||
Task* pTask = *it;
|
||||
pTask->m_tLastExecuted = 0;
|
||||
}
|
||||
}
|
||||
|
||||
time_t tLocalCurrent = tCurrent + g_pOptions->GetLocalTimeOffset();
|
||||
time_t tLocalLastCheck = m_tLastCheck + g_pOptions->GetLocalTimeOffset();
|
||||
|
||||
tm tmCurrent;
|
||||
gmtime_r(&tLocalCurrent, &tmCurrent);
|
||||
tm tmLastCheck;
|
||||
gmtime_r(&tLocalLastCheck, &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 = Util::Timegm(&tmLoop);
|
||||
|
||||
while (tLoop <= tLocalCurrent)
|
||||
{
|
||||
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 = Util::Timegm(&tmAppoint);
|
||||
|
||||
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 && tLocalLastCheck < tAppoint && tAppoint <= tLocalCurrent;
|
||||
|
||||
//debug("TEMP: 1) m_tLastCheck=%i, tLocalCurrent=%i, tLoop=%i, tAppoint=%i, bWeekDayOK=%i, bDoTask=%i", m_tLastCheck, tLocalCurrent, tLoop, tAppoint, (int)bWeekDayOK, (int)bDoTask);
|
||||
|
||||
if (bDoTask)
|
||||
{
|
||||
ExecuteTask(pTask);
|
||||
pTask->m_tLastExecuted = tLoop;
|
||||
}
|
||||
}
|
||||
}
|
||||
tLoop += 60*60*24; // inc day
|
||||
gmtime_r(&tLoop, &tmLoop);
|
||||
}
|
||||
}
|
||||
|
||||
m_tLastCheck = tCurrent;
|
||||
|
||||
m_mutexTaskList.Unlock();
|
||||
|
||||
PrintLog();
|
||||
}
|
||||
|
||||
void Scheduler::ExecuteTask(Task* pTask)
|
||||
{
|
||||
const char* szCommandName[] = { "Pause", "Unpause", "Pause Post-processing", "Unpause Post-processing",
|
||||
"Set download rate", "Execute process", "Execute script",
|
||||
"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:
|
||||
g_pOptions->SetPauseDownload(pTask->m_eCommand == scPauseDownload);
|
||||
m_bPauseDownloadChanged = true;
|
||||
break;
|
||||
|
||||
case scPausePostProcess:
|
||||
case scUnpausePostProcess:
|
||||
g_pOptions->SetPausePostProcess(pTask->m_eCommand == scPausePostProcess);
|
||||
m_bPausePostProcessChanged = true;
|
||||
break;
|
||||
|
||||
case scPauseScan:
|
||||
case scUnpauseScan:
|
||||
g_pOptions->SetPauseScan(pTask->m_eCommand == scPauseScan);
|
||||
m_bPauseScanChanged = true;
|
||||
break;
|
||||
|
||||
case scScript:
|
||||
case scProcess:
|
||||
if (m_bExecuteProcess)
|
||||
{
|
||||
SchedulerScriptController::StartScript(pTask->m_szParam, pTask->m_eCommand == scProcess, pTask->m_iID);
|
||||
}
|
||||
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_bPausePostProcessChanged = 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_bPauseDownloadChanged)
|
||||
{
|
||||
info("Scheduler: %s download", g_pOptions->GetPauseDownload() ? "pausing" : "unpausing");
|
||||
}
|
||||
if (m_bPausePostProcessChanged)
|
||||
{
|
||||
info("Scheduler: %s post-processing", g_pOptions->GetPausePostProcess() ? "pausing" : "unpausing");
|
||||
}
|
||||
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)
|
||||
{
|
||||
Tokenizer tok(szServerList, ",;");
|
||||
while (const char* szServer = tok.Next())
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Scheduler::FetchFeed(const char* szFeedList)
|
||||
{
|
||||
Tokenizer tok(szFeedList, ",;");
|
||||
while (const char* szFeed = tok.Next())
|
||||
{
|
||||
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(!strcasecmp("0", szFeed) ? 0 : pFeed->GetID());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Scheduler::CheckScheduledResume()
|
||||
{
|
||||
time_t tResumeTime = g_pOptions->GetResumeTime();
|
||||
time_t tCurrentTime = time(NULL);
|
||||
if (tResumeTime > 0 && tCurrentTime >= tResumeTime)
|
||||
{
|
||||
info("Autoresume");
|
||||
g_pOptions->SetResumeTime(0);
|
||||
g_pOptions->SetPauseDownload(false);
|
||||
g_pOptions->SetPausePostProcess(false);
|
||||
g_pOptions->SetPauseScan(false);
|
||||
}
|
||||
}
|
||||
112
daemon/main/Scheduler.h
Normal file
112
daemon/main/Scheduler.h
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2008-2015 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"
|
||||
#include "Service.h"
|
||||
|
||||
class Scheduler : public Service
|
||||
{
|
||||
public:
|
||||
enum ECommand
|
||||
{
|
||||
scPauseDownload,
|
||||
scUnpauseDownload,
|
||||
scPausePostProcess,
|
||||
scUnpausePostProcess,
|
||||
scDownloadRate,
|
||||
scScript,
|
||||
scProcess,
|
||||
scPauseScan,
|
||||
scUnpauseScan,
|
||||
scActivateServer,
|
||||
scDeactivateServer,
|
||||
scFetchFeed
|
||||
};
|
||||
|
||||
class Task
|
||||
{
|
||||
private:
|
||||
int m_iID;
|
||||
int m_iHours;
|
||||
int m_iMinutes;
|
||||
int m_iWeekDaysBits;
|
||||
ECommand m_eCommand;
|
||||
char* m_szParam;
|
||||
time_t m_tLastExecuted;
|
||||
|
||||
public:
|
||||
Task(int iID, 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_bDownloadRateChanged;
|
||||
bool m_bExecuteProcess;
|
||||
bool m_bPauseDownloadChanged;
|
||||
bool m_bPausePostProcessChanged;
|
||||
bool m_bPauseScanChanged;
|
||||
bool m_bServerChanged;
|
||||
ServerStatusList m_ServerStatusList;
|
||||
bool m_bFirstChecked;
|
||||
|
||||
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);
|
||||
void CheckScheduledResume();
|
||||
void FirstCheck();
|
||||
|
||||
protected:
|
||||
virtual int ServiceInterval() { return 1000; }
|
||||
virtual void ServiceWork();
|
||||
|
||||
public:
|
||||
Scheduler();
|
||||
~Scheduler();
|
||||
void AddTask(Task* pTask);
|
||||
};
|
||||
|
||||
extern Scheduler* g_pScheduler;
|
||||
|
||||
#endif
|
||||
320
daemon/main/StackTrace.cpp
Normal file
320
daemon/main/StackTrace.cpp
Normal file
@@ -0,0 +1,320 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007-2015 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 <dbghelp.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/resource.h>
|
||||
#include <signal.h>
|
||||
#endif
|
||||
#ifdef HAVE_SYS_PRCTL_H
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
#ifdef HAVE_BACKTRACE
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Log.h"
|
||||
#include "Options.h"
|
||||
#include "StackTrace.h"
|
||||
|
||||
extern void ExitProc();
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
#ifdef DEBUG
|
||||
|
||||
void PrintBacktrace(PCONTEXT pContext)
|
||||
{
|
||||
HANDLE hProcess = GetCurrentProcess();
|
||||
HANDLE hThread = GetCurrentThread();
|
||||
|
||||
char szAppDir[MAX_PATH + 1];
|
||||
GetModuleFileName(NULL, szAppDir, sizeof(szAppDir));
|
||||
char* end = strrchr(szAppDir, PATH_SEPARATOR);
|
||||
if (end) *end = '\0';
|
||||
|
||||
SymSetOptions(SymGetOptions() | SYMOPT_LOAD_LINES | SYMOPT_FAIL_CRITICAL_ERRORS);
|
||||
|
||||
if (!SymInitialize(hProcess, szAppDir, TRUE))
|
||||
{
|
||||
warn("Could not obtain detailed exception information: SymInitialize failed");
|
||||
return;
|
||||
}
|
||||
|
||||
const int MAX_NAMELEN = 1024;
|
||||
IMAGEHLP_SYMBOL64* pSym = (IMAGEHLP_SYMBOL64 *) malloc(sizeof(IMAGEHLP_SYMBOL64) + MAX_NAMELEN);
|
||||
memset(pSym, 0, sizeof(IMAGEHLP_SYMBOL64) + MAX_NAMELEN);
|
||||
pSym->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
|
||||
pSym->MaxNameLength = MAX_NAMELEN;
|
||||
|
||||
IMAGEHLP_LINE64 ilLine;
|
||||
memset(&ilLine, 0, sizeof(ilLine));
|
||||
ilLine.SizeOfStruct = sizeof(ilLine);
|
||||
|
||||
STACKFRAME64 sfStackFrame;
|
||||
memset(&sfStackFrame, 0, sizeof(sfStackFrame));
|
||||
DWORD imageType;
|
||||
#ifdef _M_IX86
|
||||
imageType = IMAGE_FILE_MACHINE_I386;
|
||||
sfStackFrame.AddrPC.Offset = pContext->Eip;
|
||||
sfStackFrame.AddrPC.Mode = AddrModeFlat;
|
||||
sfStackFrame.AddrFrame.Offset = pContext->Ebp;
|
||||
sfStackFrame.AddrFrame.Mode = AddrModeFlat;
|
||||
sfStackFrame.AddrStack.Offset = pContext->Esp;
|
||||
sfStackFrame.AddrStack.Mode = AddrModeFlat;
|
||||
#elif _M_X64
|
||||
imageType = IMAGE_FILE_MACHINE_AMD64;
|
||||
sfStackFrame.AddrPC.Offset = pContext->Rip;
|
||||
sfStackFrame.AddrPC.Mode = AddrModeFlat;
|
||||
sfStackFrame.AddrFrame.Offset = pContext->Rsp;
|
||||
sfStackFrame.AddrFrame.Mode = AddrModeFlat;
|
||||
sfStackFrame.AddrStack.Offset = pContext->Rsp;
|
||||
sfStackFrame.AddrStack.Mode = AddrModeFlat;
|
||||
#else
|
||||
warn("Could not obtain detailed exception information: platform not supported");
|
||||
return;
|
||||
#endif
|
||||
|
||||
for (int frameNum = 0; ; frameNum++)
|
||||
{
|
||||
if (frameNum > 1000)
|
||||
{
|
||||
warn("Endless stack, abort tracing");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!StackWalk64(imageType, hProcess, hThread, &sfStackFrame, pContext, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
|
||||
{
|
||||
warn("Could not obtain detailed exception information: StackWalk64 failed");
|
||||
return;
|
||||
}
|
||||
|
||||
DWORD64 dwAddr = sfStackFrame.AddrPC.Offset;
|
||||
char szSymName[1024];
|
||||
char szSrcFileName[1024];
|
||||
int iLineNumber = 0;
|
||||
|
||||
DWORD64 dwSymbolDisplacement;
|
||||
if (SymGetSymFromAddr64(hProcess, dwAddr, &dwSymbolDisplacement, pSym))
|
||||
{
|
||||
UnDecorateSymbolName(pSym->Name, szSymName, sizeof(szSymName), UNDNAME_COMPLETE);
|
||||
szSymName[sizeof(szSymName) - 1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(szSymName, "<symbol not available>", sizeof(szSymName));
|
||||
}
|
||||
|
||||
DWORD dwLineDisplacement;
|
||||
if (SymGetLineFromAddr64(hProcess, dwAddr, &dwLineDisplacement, &ilLine))
|
||||
{
|
||||
iLineNumber = ilLine.LineNumber;
|
||||
char* szUseFileName = ilLine.FileName;
|
||||
char* szRoot = strstr(szUseFileName, "\\daemon\\");
|
||||
if (szRoot)
|
||||
{
|
||||
szUseFileName = szRoot;
|
||||
}
|
||||
strncpy(szSrcFileName, szUseFileName, sizeof(szSrcFileName));
|
||||
szSrcFileName[sizeof(szSrcFileName) - 1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(szSrcFileName, "<filename not available>", sizeof(szSymName));
|
||||
}
|
||||
|
||||
info("%s (%i) : %s", szSrcFileName, iLineNumber, szSymName);
|
||||
|
||||
if (sfStackFrame.AddrReturn.Offset == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
LONG __stdcall ExceptionFilter(EXCEPTION_POINTERS* pExPtrs)
|
||||
{
|
||||
error("Unhandled Exception: code: 0x%8.8X, flags: %d, address: 0x%8.8X",
|
||||
pExPtrs->ExceptionRecord->ExceptionCode,
|
||||
pExPtrs->ExceptionRecord->ExceptionFlags,
|
||||
pExPtrs->ExceptionRecord->ExceptionAddress);
|
||||
|
||||
#ifdef DEBUG
|
||||
PrintBacktrace(pExPtrs->ContextRecord);
|
||||
#else
|
||||
info("Detailed exception information can be printed by debug version of NZBGet (available from download page)");
|
||||
#endif
|
||||
|
||||
ExitProcess(-1);
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
void InstallErrorHandler()
|
||||
{
|
||||
SetUnhandledExceptionFilter(ExceptionFilter);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#ifdef DEBUG
|
||||
typedef void(*sighandler)(int);
|
||||
std::vector<sighandler> SignalProcList;
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYS_PRCTL_H
|
||||
/**
|
||||
* activates the creation of core-files
|
||||
*/
|
||||
void EnableDumpCore()
|
||||
{
|
||||
rlimit rlim;
|
||||
rlim.rlim_cur= RLIM_INFINITY;
|
||||
rlim.rlim_max= RLIM_INFINITY;
|
||||
setrlimit(RLIMIT_CORE, &rlim);
|
||||
prctl(PR_SET_DUMPABLE, 1);
|
||||
}
|
||||
#endif
|
||||
|
||||
void PrintBacktrace()
|
||||
{
|
||||
#ifdef HAVE_BACKTRACE
|
||||
printf("Segmentation fault, tracing...\n");
|
||||
|
||||
void *array[100];
|
||||
size_t size;
|
||||
char **strings;
|
||||
size_t i;
|
||||
|
||||
size = backtrace(array, 100);
|
||||
strings = backtrace_symbols(array, size);
|
||||
|
||||
// first trace to screen
|
||||
printf("Obtained %zd stack frames\n", size);
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
printf("%s\n", strings[i]);
|
||||
}
|
||||
|
||||
// then trace to log
|
||||
error("Segmentation fault, tracing...");
|
||||
error("Obtained %zd stack frames", size);
|
||||
for (i = 0; i < size; i++)
|
||||
{
|
||||
error("%s", strings[i]);
|
||||
}
|
||||
|
||||
free(strings);
|
||||
#else
|
||||
error("Segmentation fault");
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Signal handler
|
||||
*/
|
||||
void SignalProc(int iSignal)
|
||||
{
|
||||
switch (iSignal)
|
||||
{
|
||||
case SIGINT:
|
||||
signal(SIGINT, SIG_DFL); // Reset the signal handler
|
||||
ExitProc();
|
||||
break;
|
||||
|
||||
case SIGTERM:
|
||||
signal(SIGTERM, SIG_DFL); // Reset the signal handler
|
||||
ExitProc();
|
||||
break;
|
||||
|
||||
case SIGCHLD:
|
||||
// ignoring
|
||||
break;
|
||||
|
||||
#ifdef DEBUG
|
||||
case SIGSEGV:
|
||||
signal(SIGSEGV, SIG_DFL); // Reset the signal handler
|
||||
PrintBacktrace();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void InstallErrorHandler()
|
||||
{
|
||||
#ifdef HAVE_SYS_PRCTL_H
|
||||
if (g_pOptions->GetDumpCore())
|
||||
{
|
||||
EnableDumpCore();
|
||||
}
|
||||
#endif
|
||||
|
||||
signal(SIGINT, SignalProc);
|
||||
signal(SIGTERM, SignalProc);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#ifdef DEBUG
|
||||
signal(SIGSEGV, SignalProc);
|
||||
#endif
|
||||
#ifdef SIGCHLD_HANDLER
|
||||
// it could be necessary on some systems to activate a handler for SIGCHLD
|
||||
// however it make troubles on other systems and is deactivated by default
|
||||
signal(SIGCHLD, SignalProc);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
class SegFault
|
||||
{
|
||||
public:
|
||||
void DoSegFault()
|
||||
{
|
||||
char* N = NULL;
|
||||
strcpy(N, "");
|
||||
}
|
||||
};
|
||||
|
||||
void TestSegFault()
|
||||
{
|
||||
SegFault s;
|
||||
s.DoSegFault();
|
||||
}
|
||||
#endif
|
||||
@@ -1,8 +1,7 @@
|
||||
/*
|
||||
* 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) 2007-2014 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 +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,21 +23,13 @@
|
||||
*/
|
||||
|
||||
|
||||
#ifndef NETADDRESS_H
|
||||
#define NETADDRESS_H
|
||||
#ifndef STACKTRACE_H
|
||||
#define STACKTRACE_H
|
||||
|
||||
class NetAddress
|
||||
{
|
||||
private:
|
||||
char* m_szHost;
|
||||
int m_iPort;
|
||||
|
||||
public:
|
||||
NetAddress(const char* szHost, int iPort);
|
||||
virtual ~NetAddress();
|
||||
const char* GetHost() { return m_szHost; }
|
||||
int GetPort() { return m_iPort; }
|
||||
};
|
||||
void InstallErrorHandler();
|
||||
|
||||
#ifdef DEBUG
|
||||
void TestSegFault();
|
||||
#endif
|
||||
|
||||
#endif
|
||||
1000
daemon/main/nzbget.cpp
Normal file
1000
daemon/main/nzbget.cpp
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user