mirror of
https://github.com/nzbget/nzbget.git
synced 2026-01-03 11:37:46 -05:00
Compare commits
510 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
896a7c1988 | ||
|
|
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 | ||
|
|
0204d0849d | ||
|
|
179f9e9ed6 | ||
|
|
d5b0a8dd8d | ||
|
|
4744559b46 | ||
|
|
51b6549671 | ||
|
|
ec61b13269 | ||
|
|
d898fff913 | ||
|
|
fc19d48538 | ||
|
|
fe512e830c | ||
|
|
21be45e89a | ||
|
|
1eddc76630 | ||
|
|
c4cc0cb745 | ||
|
|
1944ea7273 | ||
|
|
7da406ed4f | ||
|
|
6a0c1031fa | ||
|
|
15b016ef7b | ||
|
|
63b8c11ab5 | ||
|
|
5ca2279af8 | ||
|
|
d8cf6263de | ||
|
|
ebdd0fd1b8 | ||
|
|
9fa4cace8e | ||
|
|
27d5f058eb | ||
|
|
849db2a9dc | ||
|
|
71531b0077 | ||
|
|
b254a6b6c9 | ||
|
|
1cbf435468 | ||
|
|
abb33b763b | ||
|
|
f79bdab2cd | ||
|
|
1d294f5037 | ||
|
|
40b7b335b1 | ||
|
|
c18a3b1d7c | ||
|
|
3d782c985f | ||
|
|
d35a2d2f04 | ||
|
|
b85a36944d | ||
|
|
e15063208a | ||
|
|
255ebb8ccd | ||
|
|
07d04d0e65 | ||
|
|
b3cebce074 | ||
|
|
c03f79155d | ||
|
|
d449a26ae0 | ||
|
|
904227ebfc | ||
|
|
bff48cd97e | ||
|
|
c72c8df5e9 | ||
|
|
8a33307b6c | ||
|
|
5f19177902 | ||
|
|
2cb33bafc4 | ||
|
|
78baf30339 | ||
|
|
ea4a48dd20 | ||
|
|
1b8d9e9bfa | ||
|
|
ad9a66e971 | ||
|
|
bb5c660e1f | ||
|
|
4f29ed2e4c | ||
|
|
6d5929a611 | ||
|
|
c34ad991c4 | ||
|
|
8eceb70626 | ||
|
|
f3f609f747 | ||
|
|
c02074e7ec | ||
|
|
6697737405 | ||
|
|
dd3ca56564 | ||
|
|
71805e11f7 | ||
|
|
e480781905 | ||
|
|
4df72eafd2 | ||
|
|
cdbfd3b96d | ||
|
|
cee6aa53f4 | ||
|
|
2d9bca1d1b | ||
|
|
80685270e0 | ||
|
|
5695eaf3f4 | ||
|
|
8d46ee3236 | ||
|
|
702aad905f | ||
|
|
1da4976fea | ||
|
|
94b81f9ce4 | ||
|
|
eec1a797b3 | ||
|
|
ca7a90adab | ||
|
|
adedd66dac | ||
|
|
0a43829772 | ||
|
|
a35ff448b5 | ||
|
|
74db2f7785 | ||
|
|
4760198c23 | ||
|
|
078c6c037e | ||
|
|
56d4fcf045 | ||
|
|
24c4cd87d7 | ||
|
|
1b80cdd9b4 | ||
|
|
b66266cf82 |
2
AUTHORS
2
AUTHORS
@@ -1,4 +1,4 @@
|
||||
nzbget:
|
||||
Sven Henkel <sidddy@users.sourceforge.net> (versions 0.1.0 - ?)
|
||||
Bo Cordes Petersen <placebodk@users.sourceforge.net> (versions ? - 0.2.3)
|
||||
Andrei Prygounkov <hugbug@users.sourceforge.net> (versions 0.3.0 - 0.3.*)
|
||||
Andrey Prygunkov <hugbug@users.sourceforge.net> (versions 0.3.0 and later)
|
||||
|
||||
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-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -28,9 +28,6 @@
|
||||
#define ARTICLEDOWNLOADER_H
|
||||
|
||||
#include <time.h>
|
||||
#ifdef WIN32
|
||||
#include <sys/timeb.h>
|
||||
#endif
|
||||
|
||||
#include "Observer.h"
|
||||
#include "DownloadInfo.h"
|
||||
@@ -47,9 +44,14 @@ public:
|
||||
adRunning,
|
||||
adFinished,
|
||||
adFailed,
|
||||
adRetry,
|
||||
adDecodeError,
|
||||
adCrcError,
|
||||
adDecoding,
|
||||
adJoining,
|
||||
adJoined,
|
||||
adNotFound,
|
||||
adConnectError,
|
||||
adFatalError
|
||||
};
|
||||
|
||||
@@ -63,20 +65,20 @@ private:
|
||||
char* m_szTempFilename;
|
||||
char* m_szArticleFilename;
|
||||
char* m_szInfoName;
|
||||
char* m_szOutputFilename;
|
||||
time_t m_tLastUpdateTime;
|
||||
Decoder* m_pDecoder;
|
||||
Semaphore m_semInitialized;
|
||||
Semaphore m_semWaited;
|
||||
static const char* m_szJobStatus[];
|
||||
#ifdef WIN32
|
||||
struct _timeb m_tStartTime;
|
||||
#else
|
||||
struct timeval m_tStartTime;
|
||||
#endif
|
||||
int m_iBytes;
|
||||
Decoder::EFormat m_eFormat;
|
||||
YDecoder m_YDecoder;
|
||||
UDecoder m_UDecoder;
|
||||
FILE* m_pOutFile;
|
||||
bool m_bDuplicate;
|
||||
|
||||
EStatus Download();
|
||||
void FreeConnection();
|
||||
bool Write(char* szLine, int iLen);
|
||||
bool PrepareFile(char* szLine);
|
||||
EStatus DecodeCheck();
|
||||
void FreeConnection(bool bKeepConnected);
|
||||
EStatus CheckResponse(const char* szResponse, const char* szComment);
|
||||
|
||||
public:
|
||||
ArticleDownloader();
|
||||
@@ -87,7 +89,6 @@ public:
|
||||
ArticleInfo* GetArticleInfo() { return m_pArticleInfo; }
|
||||
void SetStatus(EStatus eStatus);
|
||||
EStatus GetStatus() { return m_eStatus; }
|
||||
const char* GetStatusText() { return m_szJobStatus[m_eStatus]; }
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
bool Terminate();
|
||||
@@ -95,17 +96,13 @@ public:
|
||||
void SetLastUpdateTimeNow() { m_tLastUpdateTime = ::time(NULL); }
|
||||
const char* GetTempFilename() { return m_szTempFilename; }
|
||||
void SetTempFilename(const char* v);
|
||||
void SetOutputFilename(const char* v);
|
||||
const char* GetArticleFilename() { return m_szArticleFilename; }
|
||||
void SetInfoName(const char* v);
|
||||
const char* GetInfoName() { return m_szInfoName; }
|
||||
void CompleteFileParts();
|
||||
void WaitInit();
|
||||
#ifdef WIN32
|
||||
struct _timeb* GetStartTime() { return &m_tStartTime; }
|
||||
#else
|
||||
struct timeval* GetStartTime() { return &m_tStartTime; }
|
||||
#endif
|
||||
int GetBytes() { return m_iBytes; }
|
||||
static bool MoveCompletedFiles(NZBInfo* pNZBInfo, const char* szOldDestDir);
|
||||
void SetConnection(NNTPConnection* pConnection) { m_pConnection = pConnection; }
|
||||
|
||||
void LogDebugInfo();
|
||||
};
|
||||
@@ -115,6 +112,7 @@ class DownloadSpeedMeter
|
||||
public:
|
||||
virtual ~DownloadSpeedMeter() {};
|
||||
virtual float CalcCurrentDownloadSpeed() = 0;
|
||||
virtual void AddSpeedReading(int iBytes) = 0;
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
1214
BinRpc.cpp
Normal file
1214
BinRpc.cpp
Normal file
File diff suppressed because it is too large
Load Diff
161
BinRpc.h
Normal file
161
BinRpc.h
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
|
||||
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* 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 BINRPC_H
|
||||
#define BINRPC_H
|
||||
|
||||
#include "Connection.h"
|
||||
#include "MessageBase.h"
|
||||
|
||||
class BinRpcProcessor
|
||||
{
|
||||
private:
|
||||
SOCKET m_iSocket;
|
||||
SNZBRequestBase m_MessageBase;
|
||||
const char* m_szClientIP;
|
||||
|
||||
void Dispatch();
|
||||
|
||||
public:
|
||||
void Execute();
|
||||
void SetSocket(SOCKET iSocket) { m_iSocket = iSocket; }
|
||||
void SetSignature(int iSignature) { m_MessageBase.m_iSignature = iSignature; }
|
||||
void SetClientIP(const char* szClientIP) { m_szClientIP = szClientIP; }
|
||||
};
|
||||
|
||||
class BinCommand
|
||||
{
|
||||
protected:
|
||||
SOCKET m_iSocket;
|
||||
SNZBRequestBase* m_pMessageBase;
|
||||
|
||||
bool ReceiveRequest(void* pBuffer, int iSize);
|
||||
void SendBoolResponse(bool bSuccess, const char* szText);
|
||||
|
||||
public:
|
||||
virtual ~BinCommand() {}
|
||||
virtual void Execute() = 0;
|
||||
void SetSocket(SOCKET iSocket) { m_iSocket = iSocket; }
|
||||
void SetMessageBase(SNZBRequestBase* pMessageBase) { m_pMessageBase = pMessageBase; }
|
||||
};
|
||||
|
||||
class DownloadBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ListBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class LogBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class PauseUnpauseBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class EditQueueBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class SetDownloadRateBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class DumpDebugBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ShutdownBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ReloadBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class VersionBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class PostQueueBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class WriteLogBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ScanBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class HistoryBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class DownloadUrlBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class UrlQueueBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
#endif
|
||||
979
ChangeLog
979
ChangeLog
@@ -1,188 +1,793 @@
|
||||
nzbget-0.3.0:
|
||||
- The download queue now contains newsgroup-files to be downloaded instead of
|
||||
nzb-jobs. By adding a new job, the nzb-file is immediately parsed and each
|
||||
newsgroup-file is added to download queue. Each file can therefore be
|
||||
managed separately (paused, deleted or moved);
|
||||
- Current queue state is saved after every change (file is completed or the
|
||||
queue is changed - entries paused, deleted or moved). The state is saved on
|
||||
disk using internal format, which allows fast loading on next program start
|
||||
(no need to parse xml-files again);
|
||||
- The remaining download-size is updated after every article is completed to
|
||||
indicate the correct remaining size and time for total files in queue;
|
||||
- Downloaded articles, which are saved in temp-directory, can be reused on
|
||||
next program start, if the file was not completed (option "continuepartial"
|
||||
in config-file);
|
||||
- Along with uulib the program has internal decoder for yEnc-format. This
|
||||
decoder was necessary, because uulib is so slow, that it prevents using of
|
||||
the program on not so powerful systems like linux-routers (MIPSEL CPU 200
|
||||
MHz). The new decoder is very fast. It is controlled over option "decoder"
|
||||
in config-file;
|
||||
- The decoder can be completely disabled. In this case all downloaded articles
|
||||
are saved in unaltered form and can be joined with an external program;
|
||||
UUDeview is one of them;
|
||||
- If download of article fails, the program attempts to download it again so
|
||||
many times, what the option "retries" in config-file says. This works even
|
||||
if no servers with level higher than "0" defined. After each retry the next
|
||||
server-level is used, if there are no more levels, the program switches to
|
||||
level "0" again. The pause between retries can be set with config-option
|
||||
"retryinterval";
|
||||
- If despite of a stated connection-timeout (it can be changed via
|
||||
config-option "connectiontimeout") connection hangs, the program tries to
|
||||
cancel the connection (after "terminatetimeout" seconds). If it doesn't
|
||||
work the download thread is killed and the article will be redownloaded in
|
||||
a new thread. This ensures, that there are no long-time hanging connections
|
||||
and all articles are downloaded, when a time to rejoin file comes;
|
||||
- Automatic par-checking and repairing. Only reuired par-files are downloaded.
|
||||
The program uses libpar2 and does not require any external tools. The big
|
||||
advantage of library is, that it allows to continue par-check after new
|
||||
par-blocks were downloaded. This were not possible with external
|
||||
par2cmdline-tool;
|
||||
- There is a daemon-mode now (command-line switch "-D" (--daemon)). In this
|
||||
mode a lock-file (default location "/tmp/nzbget.lock", can be changed via
|
||||
option "lockfile") contains PID of daemon;
|
||||
- The format of configuration-file was changed from xml to more common
|
||||
text-format. It allows also using of variables like
|
||||
"tempdir=${MAINDIR}/tmp";
|
||||
- Any option of config-file can be overwritten via command-line switch
|
||||
"-o" (--option). This includes also the definition of servers.
|
||||
This means that the program can now be started without a configuration-file
|
||||
at all (all required options must be passed via command-line);
|
||||
- The command-line switches were revised. The dedicated switches to change
|
||||
options in config-file were eliminated, since any option can now be changed
|
||||
via switch "-o" (--option);
|
||||
- If the name of configuration-file was not passed via command-line the
|
||||
program search it in following locations: "~/.nzbget", "/etc/nzbget.conf",
|
||||
"/usr/etc/nzbget.conf", "/usr/local/etc/nzbget.conf",
|
||||
"/opt/etc/nzbget.conf";
|
||||
- The new command-line switch "-n" (--noconfigfile) prevents the loading of
|
||||
a config-file. All required config-options must be passed via command-line
|
||||
(switch "-o" (--option));
|
||||
- To start the program in server mode either "-s" (--server) or
|
||||
"-D" (--daemon) switch must be used. If the program started without any
|
||||
parameters it prints help-screen. There is no a dedicated switch to start
|
||||
in a standalone mode. If switches "-s" and "-D" are omitted and none of
|
||||
client-request-switches used the standalone mode is default. This usage
|
||||
of switches is more common to programs like "wget". To add a file to
|
||||
server's download queue use switch "-A" (--append) and a name of nzb-file
|
||||
as last command-line parameter;
|
||||
- There is a new switch "-Q" (--quit) to gracefully stop server. BTW the
|
||||
SIGKIL-signal is now handled appropriately, so "killall nzbget" is also OK,
|
||||
where "killall -9 nzbget" terminates server immediately (helpful if it
|
||||
hangs, but it shouldn't);
|
||||
- With new switch "-T" (--top) the file will be added to the top of download
|
||||
queue. Use it with switch "-A" (--append);
|
||||
- The download queue can be edited via switch "-E" (--edit). It is possible
|
||||
to pause, unpause, delete and move files in queue. The IDs of file(s)
|
||||
to be affected are passed via switch "-I" (fileid), either one ID or a
|
||||
range in a form "IDForm-IDTo". This also means, that every file in queue
|
||||
have ID now;
|
||||
- The switch "-L" (--list) prints IDs of files consequently. It prints also
|
||||
name, size, percentage of completing and state (paused or not) of each file.
|
||||
Plus summary info: number of files, total remaining size and size of
|
||||
paused files, server state (paused or running), number of threads on
|
||||
server, current speed limit;
|
||||
- With new switch "-G" (--log) the last N lines printed to server's
|
||||
screen-log, can be printed on client. The max number of lines which can
|
||||
be returned from servers depends on option "logbuffersize";
|
||||
- The redesigned Frontends (known as outputmodes "loggable", "colored" and
|
||||
"curses") can connect to (remote) server and behave as if you were running
|
||||
server-instance of program itself (command-line switch "-C" (--connect)).
|
||||
The log-output updates constantly and even all control-functions in
|
||||
ncurses-mode works: pause/unpause server, set download rate limit, edit of
|
||||
queue (pause/unpause, delete, move entries). The number of connected
|
||||
clients is not limited. The "outputmode" on a client can be set
|
||||
independently from server. The client-mode is especially useful if the
|
||||
server runs as a daemon;
|
||||
- The writing to log-file can be disabled via option "createlog".
|
||||
The location of log-file controls the option "log-file";
|
||||
- Switch "-p" (--printconfig) prints the name of configuration file being
|
||||
used and all option/value-pairs, taking into account all used
|
||||
"-o" (--option) - switches;
|
||||
- The communication protocol between server and client was optimized to
|
||||
minimize the size of transferred data. Instead of fixing the size for
|
||||
Filenames in data-structures to 512 bytes only in fact used data
|
||||
are transferred;
|
||||
- Extensions in ncurses-outputmode: scrolling in queue-list works better,
|
||||
navigation in queue with keys Up, Down, PgUp, PgDn, Home, End.
|
||||
Keys to move entries are "U" (move up), "N" (move down), "T" (move to top),
|
||||
"B" (move to bottom). "P" to pause/unpause file. The size, percentage
|
||||
of completing and state (paused or not) for every file is printed.
|
||||
The header of queue shows number of total files, number of unpaused
|
||||
files and size for all and unpaused files. Better using of screen estate
|
||||
space <20> no more empty lines and separate header for status (total seven
|
||||
lines gain). The messages are printed on several lines (if they not fill
|
||||
in one line) without trimming now;
|
||||
- configure.ac-file updated to work with recent versions of autoconf/automake.
|
||||
There are new configure-options now: "--disable-uulib" to compile the
|
||||
program without uulib; "--disable-ncurses" to disable ncurses-support
|
||||
(eliminates necessity of ncurses-libs), useful on embedded systems with
|
||||
little resources; "--disable-parcheck" to compile without par-check;
|
||||
- The algorithm for parsing of nzb-files now uses XMLReader instead of
|
||||
DOM-Parser to minimize memory usage (no mor needs to build complete DOM-tree
|
||||
in memory). Thanks to Thierry MERLE <merlum@users.sourceforge.net> for
|
||||
the patch;
|
||||
- The log-file contains now thread-ID for all entry-types and additionally
|
||||
for debug-entries: filename, line number and function's name of source
|
||||
code, where the message was printed. Debug-messages can be disabled in
|
||||
config-file (option "debugtarget") like other messages;
|
||||
- The program is now compatible with windows. Project file for MS Visual
|
||||
C++ 2005 is included. Use "nzbget -install" and "nzbget -remove" to
|
||||
install/remove NZBGet-Service. Servers and clients can run on diferrent
|
||||
operating systems;
|
||||
- Improved compatibility with POSIX systems; Tested on:
|
||||
- Linux Debian 3.1 on x86;
|
||||
- Linux BusyBox with uClibc on MIPSEL;
|
||||
- PC-BSD 1.4 (based on FreeBSD 6.2) on x86;
|
||||
- Solaris 10 on x86;
|
||||
- Many memory-leaks and thread issues were fixed;
|
||||
- The program was thoroughly worked over. Almost every line of code was
|
||||
revised;
|
||||
nzbget-9.0:
|
||||
- changed version naming scheme by removing the leading zero: current
|
||||
version is now called 9.0 instead of 0.9.0 (it's really the 9th major
|
||||
version of the program);
|
||||
- added built-in web-interface:
|
||||
- completely new designed and written from scratch;
|
||||
- doesn't require a separate web-server;
|
||||
- doesn't require PHP;
|
||||
- 100% Javascript application; the built-in web-server hosts only
|
||||
static files; the javascript app communicates with NZBGet via
|
||||
JSON-RPC;
|
||||
- very efficient usage of server resources (CPU and memory);
|
||||
- easy installation. Since neither a separate web-server nor PHP
|
||||
are needed the installation of new web-interface is very easy.
|
||||
Actually it is performed automatically when you "make install"
|
||||
or "ipkg install nzbget";
|
||||
- modern look: better layout, popup dialogs, nice animations,
|
||||
hi-def icons;
|
||||
- built-in phone-theme (activates automatically);
|
||||
- combined view for "currently downloading", "queued", "currently
|
||||
processing" and "queued for processing";
|
||||
- renaming of nzb-files;
|
||||
- multiselect with multiedit or merge of downloads;
|
||||
- fast paging in the lists (downloads, history, messages);
|
||||
- search box for filtering in the lists (downloads, history, messages)
|
||||
and in settings;
|
||||
- adding nzb-files to download queue was improved in several ways:
|
||||
- add multiple files at once. The "select files dialog" allows
|
||||
to select multiple files;
|
||||
- add files using drag and drop. Just drop the files from your
|
||||
file manager directly into the web-browser;
|
||||
- add files via URLs. Put the URL and NZBGet downloads the
|
||||
nzb-file and adds it to download queue automatically;
|
||||
- the priority of nzb-file can now be set when adding local-files
|
||||
or URLs;
|
||||
- the history can be cleared completely or selected items can be removed;
|
||||
- file mode is now nzb-file related;
|
||||
- added the ability to queue URLs:
|
||||
- the program automatically downloads nzb-files from given URLs
|
||||
and put them to download queue.
|
||||
- when multiple URLs are added in a short time, they are put
|
||||
into a special URL-queue.
|
||||
- the number of simultaneous URL-downloads are controlled via
|
||||
new option UrlConnections.
|
||||
- with the new option ReloadUrlQueue can be controlled if the URL-queue
|
||||
should be reloaded after the program is restarted (if the URL-queue
|
||||
was not empty).
|
||||
- new switch <-U> for remote-command <--append/-A> to queue an URL.
|
||||
- new subcommand <-U> in the remote command <--list/-L> prints the
|
||||
current URL-queue.
|
||||
- if URL-download fails, the URL is moved into history.
|
||||
- with subcommand <-R> of command <--edit> the failed URL can be
|
||||
returned to URL-queue for redownload.
|
||||
- the remote command <--list/-L> for history can now print the infos
|
||||
for URL history items.
|
||||
- new XML/JSON-RPC command <appendurl> to add an URL or multiple
|
||||
URLs for download.
|
||||
- new XML/JSON-RPC command <urlqueue> returns the items from the
|
||||
URL-queue.
|
||||
- the XML/JSON-RPC command <history> was extended to provide
|
||||
infos about URL history items.
|
||||
- the URL-queue obeys the pause-state of download queue.
|
||||
- the URL-downloads support HTTP and HTTPS protocols;
|
||||
- added new field <name> to nzb-info-object.
|
||||
- it is initially set to the cleaned up name of the nzb-file.
|
||||
- the renaming of the group changes this field.
|
||||
- all RPC-methods related to nzb-object return the new field, the
|
||||
old field <NZBNicename> is now deprecated.
|
||||
- the option <MergeNZB> now checks the <name>-field instead of
|
||||
<nzbfilename> (the latter is not changed when the nzb is renamed).
|
||||
- new env-var-parameter <NZBPP_NZBNAME> for post-processing script;
|
||||
- added options <GN> and <FN> for remote command <--edit/-E>. With these
|
||||
options the name of group or file can be used in edit-command instead
|
||||
of file ID;
|
||||
- added support for regular expressions (POSIX ERE Syntax) in remote
|
||||
commands <--list/-L> and <--edit/-E> using new subcommands <GR> and <FR>;
|
||||
- improved performance of RPC-command <listgroups>;
|
||||
- added new command <FileReorder> to RPC-method <editqueue> to set the
|
||||
order of individual files in the group;
|
||||
- added gzip-support to built-in web-server (including RPC);
|
||||
- added processing of http-request <OPTIONS> in RPC-server for better
|
||||
support of cross domain requests;
|
||||
- renamed example configuration file and postprocessing script to make
|
||||
the installation easier;
|
||||
- improved the automatic installation (<make install>) to install all
|
||||
necessary files (not only the binary as it was before);
|
||||
- improved handling of configuration errors: the program now does not
|
||||
terminate on errors but rather logs all of them and uses default option values;
|
||||
- added new XML/JSON-RPC methods <config>, <loadconfig> and <saveconfig>;
|
||||
- with active option <AllowReProcess> the NZB considered completed even if
|
||||
there are paused non-par-files (the paused non-par-files are treated the
|
||||
same way as paused par-files): as a result the reprocessable script is called;
|
||||
- added subcommand <W> to remote command <-S/--scan> to scan synchronously
|
||||
(wait until scan completed);
|
||||
- added parameter <SyncMode> to XML/JSON-RPC method <scan>;
|
||||
- the command <Scan> in web-interface now waits for completing of scan
|
||||
before reporting the status;
|
||||
- added remote command <--reload/-O> and JSON/XML-RPC method <reload> to
|
||||
reload configuration from disk and reintialize the program; the reload
|
||||
can be performed from web-interface;
|
||||
- JSON/XML-RPC method <append> extended with parameter <priority>;
|
||||
- categories available in web-interface are now configured in program
|
||||
configuration file (nzbget.conf) and can be managed via web-interface
|
||||
on settings page;
|
||||
- updated descriptions in example configuration file;
|
||||
- changes in configuration file:
|
||||
- renamed options <ServerIP>, <ServerPort> and <ServerPassword> to
|
||||
<ControlIP>, <ControlPort> and <ControlPassword> to avoid confusion
|
||||
with news-server options <ServerX.Host>, <ServerX.Port> and
|
||||
<ServerX.Password>;
|
||||
- the old option names are still recognized and are automatically
|
||||
renamed when the configuration is saved from web-interface;
|
||||
- also renamed option <$MAINDIR> to <MainDir>;
|
||||
- extended remote command <--append/-A> with optional parameters:
|
||||
- <T> - adds the file/URL to the top of queue;
|
||||
- <P> - pauses added files;
|
||||
- <C category-name> - sets category for added nzb-file/URL;
|
||||
- <N nzb-name> - sets nzb filename for added URL;
|
||||
- the old switches <--category/-K> and <--top/-T> are deprecated
|
||||
but still supported for compatibility;
|
||||
- renamed subcommand <K> of command <--edit/-E> to <C> (the old
|
||||
subcommand is still supported for compatibility);
|
||||
- added new option <NzbAddedProcess> to setup a script called after
|
||||
a nzb-file is added to queue;
|
||||
- added debug messages for speed meter;
|
||||
- improved the startup script <nzbgetd> so it can be directly used in
|
||||
</etc/init.d> without modifications;
|
||||
- fixed: after renaming of a group, the new name was not displayed
|
||||
by remote commands <-L G> and <-C in curses mode>;
|
||||
- fixed incompatibility with OpenSLL 1.0 (thanks to OpenWRT team
|
||||
for the patch);
|
||||
- fixed: RPC-method <log(0, IdFrom)> could return wrong results if
|
||||
the log was filtered with options <XXXTarget>;
|
||||
- fixed: free disk space calculated incorrectly on some OSes;
|
||||
- fixed: unrar failure was not always properly detected causing the
|
||||
post-processing to delete not yet unpacked rar-files;
|
||||
- fixed compilation error on recent linux versions;
|
||||
- fixed compilation error on older systems;
|
||||
|
||||
nzbget-0.2.3
|
||||
- Fixed problem with losing connection to newsserver after too long idle time
|
||||
- Added functionality for dumping lots of debug info
|
||||
|
||||
nzbget-0.2.2
|
||||
- Added Florian Penzkofers fix for FreeBSD, exchanging base functionality in
|
||||
SingleServerPool.cpp with a more elegant solution
|
||||
- Added functionality for forcing answer to reloading queue upon startup of
|
||||
server
|
||||
+ use -y option to force from command-line
|
||||
+ use "reloadqueue" option in nzbget.cfg to control behavior
|
||||
- Added nzbget.cfg options to control where info, warnings and errors get
|
||||
directed to (either screen, log or both)
|
||||
- Added option "createbrokenfilelog" in nzbget.cfg
|
||||
|
||||
nzbget-0.2.1
|
||||
- Changed and extended the TCP client/server interface
|
||||
- Added timeout on sockets which prevents certain types of nzbget hanging
|
||||
- Added Kristian Hermansen's patch for renaming broken files
|
||||
|
||||
nzbget-0.2.0
|
||||
- Moved 0.1.2-alt4 to a official release as 0.2.0
|
||||
- Small fixes
|
||||
|
||||
nzbget-0.1.2-alt4
|
||||
- implemented tcp/ip communication between client & server (removing the
|
||||
rather defunct System V IPC)
|
||||
- added queue editing functionality in server-mode
|
||||
|
||||
nzbget-0.1.2-alt1
|
||||
- added new ncurses frontend
|
||||
- added server/client-mode (using System V IPC)
|
||||
- added functionality for queueing download requests
|
||||
|
||||
nzbget-0.1.2
|
||||
- performance-improvements
|
||||
- commandline-options
|
||||
- fixes
|
||||
|
||||
nzbget-0.1.1
|
||||
- new output
|
||||
- fixes
|
||||
|
||||
nzbget-0.1.0a
|
||||
- compiling-fixes
|
||||
|
||||
nzbget-0.1.0
|
||||
- initial release
|
||||
nzbget-0.8.0:
|
||||
- added priorities; new action <I> for remote command <--edit/-E> to set
|
||||
priorities for groups or individual files; new actions <SetGroupPriority>
|
||||
and <SetFilePriority> of RPC-command <editqueue>; remote command
|
||||
<--list/-L> prints priorities and indicates files or groups being
|
||||
downloaded; ncurses-frontend prints priorities and indicates files or
|
||||
groups being download; new command <PRIORITY> to set priority of nzb-file
|
||||
from nzbprocess-script; RPC-commands <listgroups> and <listfiles> return
|
||||
priorities and indicate files or groups being downloaded;
|
||||
- added renaming of groups; new subcommand <N> for command <--edit/-E>; new
|
||||
action <SetName> for RPC-method <editqueue>;
|
||||
- added new option <AccurateRate>, which enables syncronisation in speed
|
||||
meter; that makes the indicated speed more accurate by eliminating
|
||||
measurement errors possible due thread conflicts; thanks to anonymous
|
||||
nzbget user for the patch;
|
||||
- improved the parsing of filename from article subject;
|
||||
- option <DirectWrite> now efficiently works on Windows with NTFS partitions;
|
||||
- added URL-based-authentication as alternative to HTTP-header authentication
|
||||
for XML- and JSON-RPC;
|
||||
- fixed: nzb-files containing umlauts and other special characters could not
|
||||
be parsed - replaced XML-Reader with SAX-Parser - only on POSIX (not on
|
||||
Windows);
|
||||
- fixed incorrect displaying of group sizes bigger than 4GB on many 64-bit
|
||||
OSes;
|
||||
- fixed a bug causing error on decoding of input data in JSON-RPC;
|
||||
- fixed a compilation error on some windows versions;
|
||||
- fixed: par-repair could fail when the filenames were not correctly parsed
|
||||
from article subjects;
|
||||
- fixed a compatibility issue with OpenBSD (and possibly other BSD based
|
||||
systems); added the automatic configuring of required signal handling logic
|
||||
to better support BSD without breaking the compatibility with certain Linux
|
||||
systems;
|
||||
- corrected the address of Free Software Foundation in copyright notice.
|
||||
|
||||
nzbget-0.7.0:
|
||||
- added history: new option <KeepHistory>, new remote subcommand <H> for
|
||||
commands <L> (list history entries) and <E> (delete history entries,
|
||||
return history item, postprocess history item), new RPC-command <History>
|
||||
and subcommands <HistoryDelete>, <HistoryReturn>, <HistoryProcess> for
|
||||
command <EditQueue>;
|
||||
- added support for JSON-P (extension of JSON-RPC);
|
||||
- changed the result code returning status <ERROR> for postprocessing script
|
||||
from <1> to <94> (needed to show the proper script status in history);
|
||||
- improved the detection of new files in incoming nzb directory: now the
|
||||
scanner does not rely on system datum, but tracks the changing of file
|
||||
sizes during a last few (<NzbDirFileAge>) seconds instead;
|
||||
- improvements in example postprocessing script: 1) if download contains
|
||||
only par2-files the script do not delete them during cleanup;
|
||||
2) if download contains only nzb-files the script moves them to incoming
|
||||
nzb-directory for further download;
|
||||
- improved formatting of groups and added time info in curses output mode;
|
||||
- added second pause register, which is independent of main pause-state and
|
||||
therfore is intended for usage from external scripts;
|
||||
that allows to pause download without interfering with options
|
||||
<ParPauseQueue> and <PostPauseQueue> and scheduler tasks <PauseDownload>
|
||||
and <UnpauseDownload> - they all work with first (default) pause register;
|
||||
new subcommand <D2> for commands <--pause/-P> and <--unpause/-U>;
|
||||
new RPC-command <pausedownload2> and <resumedownload2>;
|
||||
existing RPC-commands <pause> und <resume> renamed to <pausedownload> and
|
||||
<resumedownload>;
|
||||
new field <Download2Paused> in result struct for RPC-command <status>;
|
||||
existing fields <ServerPaused> and <ParJobCount> renamed to
|
||||
<DownloadPaused> and <PostJobCount>;
|
||||
old RPC-commands and fields still exist for compatibility;
|
||||
the status output of command <--list/-L> indicates the state of second
|
||||
pause register;
|
||||
key <P> in curses-frontend can unpause second pause-register;
|
||||
- nzbprocess-script (option <NZBProcess>) can now set category and
|
||||
post-processing parameters for nzb-file;
|
||||
- redesigned server pool and par-checker to avoid using of semaphores
|
||||
(which are very platform specific);
|
||||
- added subcommand <S> to remote commands <--pause/-P> and <--unpause/-U> to
|
||||
pause/unpause the scanning of incoming nzb-directory;
|
||||
- added commands <PauseScan> and <UnpauseScan> for scheduler option
|
||||
<TaskX.Command>;
|
||||
- added remote commands <PauseScan> and <ResumeScan> for XML-/JSON-RPC;
|
||||
- command <pause post-processing> now not only pauses the post-processing
|
||||
queue but also pauses the current post-processing job (par-job or
|
||||
script-job);
|
||||
however the script-job can be paused only after the next line printed to
|
||||
screen;
|
||||
- improved error reporting while parsing nzb-files;
|
||||
- added field <NZBID> to NZBInfo; the field is now returned by XML-/JSON-RPC
|
||||
methods <listfiles>, <listgroups> and <postqueue>;
|
||||
- improvements in configure script;
|
||||
- added support for platforms without IPv6 (they do not have <getaddrinfo>);
|
||||
- debug-messages generated on early stages during initializing are now
|
||||
printed to screen/log-file;
|
||||
- messages about obsolete options are now printed to screen/log-file;
|
||||
- imporved example postprocessing script: added support for external
|
||||
configuration file, postprocessing parameters and configuration via
|
||||
web-interface;
|
||||
- option <TaskX.Process> now can contain parameters which must be passed
|
||||
to the script;
|
||||
- added pausing/resuming for post-processor queue;
|
||||
added new modifier <O> to remote commands <--pause/-P> and <--unpause/-U>;
|
||||
added new commands <postpause> and <postresume> to XML-/JSON-RPC;
|
||||
extended output of remote command <--list/-L> to indicate paused state
|
||||
of post-processor queue; extended command <status> of XML-/JSON-RPC
|
||||
with field <PostPause>;
|
||||
- changed the command line syntax for requesting of post-processor queue
|
||||
from <-O> to <-L O> for consistency with other post-queue related
|
||||
commands (<-P O>, <-U O> and <-E O>);
|
||||
- improved example post-processing script: added support for delayed
|
||||
par-check (try unrar first, par-repair if unrar failed);
|
||||
- added modifier <O> to command <-E/--edit> for editing of
|
||||
post-processor-queue;
|
||||
following subcommands are supported: <+/-offset>, <T>, <B>, <D>;
|
||||
subcommand <D> supports deletion of queued post-jobs and active job as well;
|
||||
deletion of active job means the cancelling of par-check/repair or
|
||||
terminating of post-processing-script (including child processes of the
|
||||
script);
|
||||
updated remote-server to support new edit-subcommands in XML/JSON-RPC;
|
||||
- extended the syntax of option <TaskX.Time> in two ways:
|
||||
1) it now accepts multiple comma-separated values;
|
||||
2) an asterix as hours-part means <every hour>;
|
||||
- added svn revision number to version string (commands <-v> and <-V>,
|
||||
startup log entry);
|
||||
svn revision is automatically read from svn-repository on each build;
|
||||
- added estimated remaining time and better distinguishing of server state
|
||||
in command <--list/-L>;
|
||||
- added new return code (93) for post-processing script to indicate
|
||||
successful processing; that results in cleaning up of download queue
|
||||
if option <ParCleanupQueue> is active;
|
||||
- added readonly options <AppBin>, <ConfigFile> and <Version> for usage
|
||||
in processing scripts (options are available as environment variables
|
||||
<NZBOP_APPBIN>, <NZBOP_CONFIGFILE> and <NZBOP_VERSION>);
|
||||
- renamed ParStatus constant <FAILED> to <FAILURE> for a consistence with
|
||||
ScriptStatus constant <FAILURE>, that also affects the results of
|
||||
RPC-command <history>;
|
||||
- added a new return code <95/POSTPROCESS_NONE> for post-processing scripts
|
||||
for cases when pp-script skips all post-processing work (typically upon
|
||||
a user's request via a pp-parameter);
|
||||
modified the example post-processing script to return the new code
|
||||
instead of a error code when a pp-parameter <PostProcess> was set to <no>;
|
||||
- added field <PostTime> to result of RPC-Command <listfiles> and fields
|
||||
<MinPostTime> and <MaxPostTime> for command <listgroups>;
|
||||
- in <curses> and <colored> output-modes the download speed is now printed
|
||||
with one decimal digit when the speed is lower than 10 KB/s;
|
||||
- improvement in example post-processing script: added check for existence
|
||||
of <unrar> and command <wc>;
|
||||
- added shell batch file for windows (nzbget-shell.bat);
|
||||
thanks to orbisvicis (orbisvicis@users.sourceforge.net) for the script;
|
||||
- added debian style init script (nzbgetd);
|
||||
thanks to orbisvicis (orbisvicis@users.sourceforge.net) for the script;
|
||||
- added the returning of a proper HTTP error code if the authorization was
|
||||
failed on RPC-calls;
|
||||
thanks to jdembski (jdembski@users.sourceforge.net) for the patch;
|
||||
- changed the sleep-time during the throttling of bandwidth from 200ms to
|
||||
10ms in order to achieve better uniformity;
|
||||
- modified example postprocessing script to not use the command <dirname>,
|
||||
which is not always available;
|
||||
thanks to Ger Teunis for the patch;
|
||||
- improved example post-processing script: added the check for existence
|
||||
of destination directory to return a proper ERROR-code (important for
|
||||
reprocessing of history items);
|
||||
- by saving the queue to disk now using relative paths for the list of
|
||||
compeled files to reduce the file's size;
|
||||
- eliminated few compiler warnings on GCC;
|
||||
- fixed: when option <DaemonUserName> was specified and nzbget was
|
||||
started as root, the lockfile was not removed;
|
||||
- fixed: nothing was downloaded when the option <Retries> was set to <0>;
|
||||
- fixed: base64 decoding function used by RPC-method <append> sometimes
|
||||
failed, in particular when called from Ruby-language;
|
||||
- fixed: JSON-RPC-commands failed, if parameters were placed before method
|
||||
name in the request;
|
||||
- fixed: RPC-method <append> did not work properly on Posix systems
|
||||
(it worked only on Windows);
|
||||
- fixed compilation error when using native curses library on OpenSolaris;
|
||||
- fixed linking error on OpenSolaris when using GnuTLS;
|
||||
- fixed: option <ContinuePartial> did not work;
|
||||
- fixed: seg. fault in service mode on program start (Windows only);
|
||||
- fixed: environment block was not passed correctly to child process,
|
||||
what could result in seg faults (windows only);
|
||||
- fixed: returning the postprocessing exit code <92 - par-check all
|
||||
collections> when there were no par-files results in endless calling
|
||||
of postprocessing script;
|
||||
- fixed compatibility issues with OS/2.
|
||||
|
||||
nzbget-0.6.0:
|
||||
- added scheduler; new options <TaskX.Time>, <TaskX.WeekDays>,
|
||||
<TaskX.Command>, <TaskX.DownloadRate> and <TaskX.Process>;
|
||||
- added support for postprocess-parameters; new subcommand <O> of remote
|
||||
command <E> to add/modify pp-parameter for group (nzb-file); new
|
||||
XML-/JSON-RPC-subcommand <GroupSetParameter> of method <editqueue> for
|
||||
the same purpose; updated example configuration file and example
|
||||
postprocess-script to indicate new method of passing arguments via
|
||||
environment variables;
|
||||
- added subcommands <F>, <G> and <S> to command line switch <-L/--list>,
|
||||
which prints list of files, groups or only status info respectively;
|
||||
extended binary communication protocol to transfer nzb-infos in addition
|
||||
to file-infos;
|
||||
- added new subcommand <M> to edit-command <E> for merging of two (or more)
|
||||
groups (useful after adding pars from a separate nzb-file);
|
||||
- added option <MergeNzb> to automatically merge nzb-files with the same
|
||||
filename (useful by adding pars from a different source);
|
||||
- added script-processing of files in incoming directory to allow automatic
|
||||
unpacking and queueing of compressed nzb-files; new option <NzbProcess>;
|
||||
- added the printing of post-process-parameters for groups in command
|
||||
<--list G>;
|
||||
- added the printing of nzbget version into the log-file on start;
|
||||
- added option <DeleteCleanupDisk> to automatically delete already downloaded
|
||||
files from disk if nzb-file was deleted from queue (the download was
|
||||
cancelled);
|
||||
- added option <ParTimeLimit> to define the max time allowed for par-repair;
|
||||
- added command <--scan/-S> to execute the scan of nzb-directory on remote
|
||||
server;
|
||||
- changed the method to pass arguments to postprocess/nzbprocess: now using
|
||||
environment variables (old method is still supported for compatibility with
|
||||
existing scripts);
|
||||
- added the passing of nzbget-options to postprocess/nzbprocess scripts as
|
||||
environment variables;
|
||||
- extended the communication between nzbget and post-process-script:
|
||||
collections are now detected even if parcheck is disabled;
|
||||
- added support for delayed par-check/repair: post-process-script can request
|
||||
par-check/repair using special exit codes to repair current collection or
|
||||
all collections;
|
||||
- implemented the normalizing of option names and values in option list; the
|
||||
command <-p> also prints normalized names and values now; that makes the
|
||||
parsing of output of command <-p> for external scripts easier;
|
||||
- replaced option <PostLogKind> with new option <ProcessLogKind> which is now
|
||||
used by all scripts (PostProcess, NzbProcess, TaskX.Process);
|
||||
- improved entering to paused state on connection errors (do not retry failed
|
||||
downloads if pause was activated);
|
||||
- improved error reporting on decoding failures;
|
||||
- improved compatibility of yenc-decoder;
|
||||
- improved the speed of deleting of groups from download queue (by avoiding
|
||||
the saving of queue after the deleting of each individual file);
|
||||
- updated configure-script for better compatibility with FreeBSD;
|
||||
- cleaning up of download queue (option <ParCleanupQueue>) and deletion of
|
||||
source nzb-file (option <NzbCleanupDisk>) after par-repair now works also
|
||||
if par-repair was cancelled (option <ParTimeLimit>); since required
|
||||
par-files were already downloaded the repair in an external tool is
|
||||
possible;
|
||||
- added workaround to avoid hangs in child processes (by starting of
|
||||
postprocess or nzbprocess), observed on uClibC based systems;
|
||||
- fixed: TLS/SSL didn't work in standalone mode;
|
||||
- fixed compatibility issues with Mac OS X;
|
||||
- fixed: not all necessary par2-files were unpaused on first request for
|
||||
par-blocks (although harmless, because additional files were unpaused
|
||||
later anyway);
|
||||
- fixed small memory leak appeared if process-script could not be started;
|
||||
- fixed: configure-script could not detect the right syntax for function
|
||||
<ctime_r> on OpenSolaris.
|
||||
- fixed: files downloaded with disabled decoder (option decode=no) sometimes
|
||||
were malformed and could not be decoded;
|
||||
- fixed: empty string parameters did not always work in XML-RPC.
|
||||
|
||||
nzbget-0.5.1:
|
||||
- improved the check of server responses to prevent unnecessary retrying
|
||||
if the article does not exist on server;
|
||||
- fixed: seg.fault in standalone mode if used without specifying the
|
||||
category (e.g. without switch <-K>);
|
||||
- fixed: download speed indicator could report not-null values in
|
||||
standby-mode (when paused);
|
||||
- fixed: parameter <category> in JSON/XML-RPC was not properly decoded by
|
||||
server, making the setting of a nested category (containing slash or
|
||||
backslash character) via nzbgetweb not possible;
|
||||
|
||||
nzbget-0.5.0:
|
||||
- added TLS/SSL-support for encrypted communication with news-servers;
|
||||
- added IPv6-support for communication with news-servers as well as for
|
||||
communication between nzbget-server and nzbget-client;
|
||||
- added support for categories to organize downloaded files;
|
||||
- new option <AppendCategoryDir> to create the subdirectory for each category;
|
||||
- new switch <-K> for usage with switch <-A> to define a category during
|
||||
the adding a file to download queue;
|
||||
- new command <K> in switch <-E> to change the category of nzb-file in
|
||||
download queue; the already downloaded files are automatically moved to new
|
||||
directory if the option <AppendCategoryDir> is active;
|
||||
- new parameter <Category> in XML-/JSON-RPC-command <editqueue> to allow the
|
||||
changing of category via those protocols;
|
||||
- new parameter in a call to post-process-script with category name;
|
||||
- scanning of subdirectories inside incoming nzb-directory to automatically
|
||||
assign category names; nested categories are supported;
|
||||
- added option <ServerX.JoinGroup> to connect to servers, that do not accept
|
||||
<GROUP>-command;
|
||||
- added example post-process script for unraring of downloaded files
|
||||
(POSIX only);
|
||||
- added options <ParPauseQueue> and <PostPauseQueue> useful on slow CPUs;
|
||||
- added option <NzbCleanupDisk> to delete source nzb-file after successful
|
||||
download and parcheck;
|
||||
- switch <-P> can now be used together with switches <-s> and <-D> to start
|
||||
server/daemon in paused state;
|
||||
- changed the type of messages logged in a case of connection errors from
|
||||
<DEBUG> to <ERROR> to provide better error reporting;
|
||||
- now using OS-specific line-endings in log-file and brokenlog-file: LF on
|
||||
Posix and CRLF on Windows;
|
||||
- added detection of adjusting of system clock to correct uptime/download
|
||||
time (for NAS-devices, that do not have internal clock and set time from
|
||||
internet after booting, while nzbget may be already running);
|
||||
- added the printing of stack on segmentation faults (if configured with
|
||||
<--enable-debug>, POSIX only);
|
||||
- added option <DumpCore> for better debugging on Linux in a case of abnormal
|
||||
program termination;
|
||||
- fixed: configure-script could not automatically find libsigc++ on 64-bit
|
||||
systems;
|
||||
- few other small fixes;
|
||||
|
||||
nzbget-0.4.1:
|
||||
- to avoid accidental deletion of file in curses-frontend the key <D>
|
||||
now must be pressed in uppercase;
|
||||
- options <username> and <password> in news-server's configuration are now
|
||||
optional;
|
||||
- added the server's name to the detail-log-message, displayed on the start
|
||||
of article's download;
|
||||
- added the option <AllowReProcess> to help to post-process-scripts, which
|
||||
make par-check/-repair on it's own;
|
||||
- improved download-speed-meter: it uses now a little bit less cpu and
|
||||
calculates the speed for the last 30 seconds (instead of 5 seconds),
|
||||
providing better accuracy; Thanks to ydrol <ydrol@users.sourceforge.net>
|
||||
for the patch;
|
||||
- reduced CPU-usage in curses-outputmode; Thanks to ydrol for the patch
|
||||
<ydrol@users.sourceforge.net>;
|
||||
- fixed: line-endings in windows-style (CR-LF) in config-file were not
|
||||
read properly;
|
||||
- fixed: trailing spaces in nzb-filenames (before the file's extension)
|
||||
caused errors on windows. Now they will be trimmed;
|
||||
- fixed: XML-RPC and JSON-RPC did not work on Big-Endian-CPUs (ARM, PPC, etc),
|
||||
preventing the using of web-interface;
|
||||
- fixed: umask-option did not allow to enable write-permissions for <group>
|
||||
and <others>;
|
||||
- fixed: in curses-outputmode the remote-client showed on first screen-update
|
||||
only one item of queue;
|
||||
- fixed: edit-commands with negative offset did not work via XML-RPC
|
||||
(but worked via JSON-RPC);
|
||||
- fixed incompatibility issues with gcc 4.3; Thanks to Paul Bredbury
|
||||
<brebs@users.sourceforge.net> for the patch;
|
||||
- fixed: segmentation fault if a file listed in nzb-file does not have any
|
||||
segments (articles);
|
||||
|
||||
nzbget-0.4.0:
|
||||
- added the support for XML-RPC and JSON-RPC to easier control the server
|
||||
from other applications;
|
||||
- added web-interface - it is available for download from NZBGet-project's
|
||||
home page as a separate package "web-interface";
|
||||
- added the automatic cleaning up of the download queue (deletion of unneeded
|
||||
paused par-files) after successful par-check/repair - new
|
||||
option <ParCleanupQueue>;
|
||||
- added option <DetailTarget> to allow to filter the (not so important)
|
||||
log-messages from articles' downloads (they have now the type <detail>
|
||||
instead of <info>);
|
||||
- added the gathering of progress-information during par-check; it is
|
||||
available via XML-RPC or JSON-RPC; it is also showed in web-interface;
|
||||
- improvements in internal decoder: added support for yEnc-files without
|
||||
ypart-statement (sometimes used for small files); added support for
|
||||
UU-format;
|
||||
- removed support for uulib-decoder (it did not work well anyway);
|
||||
- replaced the option <decoder (yenc, uulib, none)> with the option
|
||||
<decode (yes, no)>;
|
||||
- added detection of errors <server busy> and <remote server not available>
|
||||
(special case for NNTPCache-server) to consider them as connect-errors
|
||||
(and therefore not count as retries);
|
||||
- added check for incomplete articles (also mostly for NNTPCache-server) to
|
||||
differ such errors from CrcErrors (better error reporting);
|
||||
- improved error-reporting on moving of completed files from tmp- to
|
||||
dst-directory and added code to move files across drives if renaming fails;
|
||||
- improved handling of nzb-files with multiple collections in par-checker;
|
||||
- improved the parchecker: added the detection and processing of files
|
||||
splitted after parring;
|
||||
- added the queueing of post-process-scripts and waiting for script's
|
||||
completion before starting of a next job in postprocessor (par-job or
|
||||
script) to provide more balanced cpu utilization;
|
||||
- added the redirecting of post-process-script's output to log; new option
|
||||
<PostLogKind> to specify the default message-kind for unformatted
|
||||
log-messages;
|
||||
- added the returning of script-output by command <postqueue> via XML-RPC
|
||||
and JSON-RPC; the script-output is also showed in web-interface;
|
||||
- added the saving and restoring of the post-processor-queue (if server was
|
||||
stopped before all items were processed); new option <ReloadPostQueue>;
|
||||
- added new parameter to postprocess-script to indicate if any of par-jobs
|
||||
for the same nzb-file failed;
|
||||
- added remote command (switch O/--post) to request the post-processor-queue
|
||||
from server;
|
||||
- added remote command (switch -W/--write) to write messages to server's log;
|
||||
- added option <DiskSpace> to automatically pause the download on low disk
|
||||
space;
|
||||
- fixed few incompatibility-issues with unslung-platform on nslu2 (ARM);
|
||||
- fixed: articles with trailing text after binary data caused the decode
|
||||
failures and the reporting of CRC-errors;
|
||||
- fixed: dupecheck could cause seg.faults when all articles for a file failed;
|
||||
- fixed: by dupe-checking of files contained in nzb-file the files with the
|
||||
same size were ignored (not deleted from queue);
|
||||
- updated libpar2-patch for msvc to fix a segfault in libpar2 (windows only);
|
||||
- fixed: by registering the service on windows the fullpath to nzbget.exe
|
||||
was not always added to service's exename, making the registered service
|
||||
unusable;
|
||||
- fixed: the pausing of a group could cause the start of post-processing for
|
||||
that group;
|
||||
- fixed: too many info-messages <Need more N blocks> could be printed during
|
||||
par-check (appeared on posix only);
|
||||
|
||||
nzbget-0.3.1:
|
||||
- Greatly reduced the memory consumption by keeping articles' info on disk
|
||||
until the file download starts;
|
||||
- Implemented decode-on-the-fly-technique to reduce disk-io; downloaded
|
||||
and decoded data can also be saved directly to the destination file
|
||||
(without any intermediate files at all); this eliminates the necessity
|
||||
of joining of articles later (option "DirectWrite");
|
||||
- Improved communication with news-servers: connections are now keeped open
|
||||
until all files are downloaded (or server paused); this eliminates the
|
||||
need for establishing of connections and authorizations for each
|
||||
article and improves overal download speed;
|
||||
- Significantly better download speed is now possible on fast connection;
|
||||
it was limited by delays on starting of new articles' downloads;
|
||||
the synchronisation mechanism was reworked to fix this issue;
|
||||
- Download speed meter is much more accurate, especially on fast connections;
|
||||
this also means better speed throttling;
|
||||
- Speed optimisations in internal decoder (up to 25% faster);
|
||||
- CRC-calculation can be bypassed to increase performance on slow CPUs
|
||||
(option "CrcCheck");
|
||||
- Improved parsing of artcile's subject for better extracting of filename
|
||||
part from it and implemented a fallback-option if the parsing was incorrect;
|
||||
- Improved dupe check for files from the same nzb-request to detect reposted
|
||||
files and download only the best from them (if option "DupeCheck" is on);
|
||||
- Articles with incorrect CRC can be treated as "possibly recoverable errors"
|
||||
and relaunched for download (option "RetryOnCrcError"), it is useful if
|
||||
multiple servers are available;
|
||||
- Improved error-check for downloaded articles (crc-check and check for
|
||||
received message-id) decreases the number of broken files;
|
||||
- Extensions in curses-outputmode: added group-view-mode (key "G") to show
|
||||
items in download queue as groups, where one group represents all files
|
||||
from the same nzb-file; the editing of queue works also in group-mode
|
||||
(for all files in this group): pause/unpause/delete/move of groups;
|
||||
- Other extensions in curses-outputmode: key "T" toggles timestamps in log;
|
||||
added output of statistical data: uptime, download-time, average session
|
||||
download speed;
|
||||
- Edit-command accepts more than one ID or range of IDs.
|
||||
E.g: "nzbget -E P 2,6-10,33-39"; The switch "-I" is not used anymore;
|
||||
- Move-actions in Edit-command affect files in a smart order to guarantee
|
||||
that the relative order of files in queue is not changed after the moving;
|
||||
- Extended syntax of edit-command to edit groups (pause/unpause/delete/move
|
||||
of groups). E.g: "nzbget -E G P 2";
|
||||
- Added option "DaemonUserName" to set the user that the daemon (POSIX only)
|
||||
normally runs at. This allows nzbget daemon to be launched in rc.local
|
||||
(at boot), and download items as a specific user id; Thanks to Thierry
|
||||
MERLE <merlum@users.sourceforge.net> for the patch;
|
||||
- Added option "UMask" to specify permissions for newly created files and dirs
|
||||
(POSIX only);
|
||||
- Communication protocol used between server and client was revised to define
|
||||
the byte order for transferred data. This allows hosts with different
|
||||
endianness to communicate with each other;
|
||||
- Added options "CursesNzbName", "CursesGroup" and "CursesTime" to define
|
||||
initial state of curses-outputmode;
|
||||
- Added option "UpdateInterval" to adjust update interval for Frontend-output
|
||||
(useful in remote-mode to reduce network usage);
|
||||
- Added option "WriteBufferSize" to reduce disk-io (but it could slightly
|
||||
increase memory usage and therefore disabled by default);
|
||||
- List-command prints additional statistical info: uptime, download-time,
|
||||
total amount of downloaded data and average session download speed;
|
||||
- The creation of necessary directories on program's start was extended
|
||||
with automatic creation of all parent directories or error reporting
|
||||
if it was not possible;
|
||||
- Printed messages are now translated to oem-codepage to correctly print
|
||||
filenames with non-english characters (windows only);
|
||||
- Added remote-command "-V (--serverversion)" to print the server's version;
|
||||
- Added option "ThreadLimit" to prevent program from crash if it wants to
|
||||
create too many threads (sometimes could occur in special cases);
|
||||
- Added options "NzbDirInterval" and "NzbDirFileAge" to adjust interval and
|
||||
delay by monitoring of incoming-directory for new nzb-files;
|
||||
- Fixed error on parsing of nzb-files containing percent and other special
|
||||
characters in their names (bug appeared on windows only);
|
||||
- Reformated sample configuration file and changed default optionnames
|
||||
from lowercase to MixedCase for better readability;
|
||||
- Few bugs (seg faults) were fixed.
|
||||
|
||||
nzbget-0.3.0:
|
||||
- The download queue now contains newsgroup-files to be downloaded instead of
|
||||
nzb-jobs. By adding a new job, the nzb-file is immediately parsed and each
|
||||
newsgroup-file is added to download queue. Each file can therefore be
|
||||
managed separately (paused, deleted or moved);
|
||||
- Current queue state is saved after every change (file is completed or the
|
||||
queue is changed - entries paused, deleted or moved). The state is saved on
|
||||
disk using internal format, which allows fast loading on next program start
|
||||
(no need to parse xml-files again);
|
||||
- The remaining download-size is updated after every article is completed to
|
||||
indicate the correct remaining size and time for total files in queue;
|
||||
- Downloaded articles, which are saved in temp-directory, can be reused on
|
||||
next program start, if the file was not completed (option "continuepartial"
|
||||
in config-file);
|
||||
- Along with uulib the program has internal decoder for yEnc-format. This
|
||||
decoder was necessary, because uulib is so slow, that it prevents using of
|
||||
the program on not so powerful systems like linux-routers (MIPSEL CPU 200
|
||||
MHz). The new decoder is very fast. It is controlled over option "decoder"
|
||||
in config-file;
|
||||
- The decoder can be completely disabled. In this case all downloaded articles
|
||||
are saved in unaltered form and can be joined with an external program;
|
||||
UUDeview is one of them;
|
||||
- If download of article fails, the program attempts to download it again so
|
||||
many times, what the option "retries" in config-file says. This works even
|
||||
if no servers with level higher than "0" defined. After each retry the next
|
||||
server-level is used, if there are no more levels, the program switches to
|
||||
level "0" again. The pause between retries can be set with config-option
|
||||
"retryinterval";
|
||||
- If despite of a stated connection-timeout (it can be changed via
|
||||
config-option "connectiontimeout") connection hangs, the program tries to
|
||||
cancel the connection (after "terminatetimeout" seconds). If it doesn't
|
||||
work the download thread is killed and the article will be redownloaded in
|
||||
a new thread. This ensures, that there are no long-time hanging connections
|
||||
and all articles are downloaded, when a time to rejoin file comes;
|
||||
- Automatic par-checking and repairing. Only reuired par-files are downloaded.
|
||||
The program uses libpar2 and does not require any external tools. The big
|
||||
advantage of library is, that it allows to continue par-check after new
|
||||
par-blocks were downloaded. This were not possible with external
|
||||
par2cmdline-tool;
|
||||
- There is a daemon-mode now (command-line switch "-D" (--daemon)). In this
|
||||
mode a lock-file (default location "/tmp/nzbget.lock", can be changed via
|
||||
option "lockfile") contains PID of daemon;
|
||||
- The format of configuration-file was changed from xml to more common
|
||||
text-format. It allows also using of variables like
|
||||
"tempdir=${MAINDIR}/tmp";
|
||||
- Any option of config-file can be overwritten via command-line switch
|
||||
"-o" (--option). This includes also the definition of servers.
|
||||
This means that the program can now be started without a configuration-file
|
||||
at all (all required options must be passed via command-line);
|
||||
- The command-line switches were revised. The dedicated switches to change
|
||||
options in config-file were eliminated, since any option can now be changed
|
||||
via switch "-o" (--option);
|
||||
- If the name of configuration-file was not passed via command-line the
|
||||
program search it in following locations: "~/.nzbget", "/etc/nzbget.conf",
|
||||
"/usr/etc/nzbget.conf", "/usr/local/etc/nzbget.conf",
|
||||
"/opt/etc/nzbget.conf";
|
||||
- The new command-line switch "-n" (--noconfigfile) prevents the loading of
|
||||
a config-file. All required config-options must be passed via command-line
|
||||
(switch "-o" (--option));
|
||||
- To start the program in server mode either "-s" (--server) or
|
||||
"-D" (--daemon) switch must be used. If the program started without any
|
||||
parameters it prints help-screen. There is no a dedicated switch to start
|
||||
in a standalone mode. If switches "-s" and "-D" are omitted and none of
|
||||
client-request-switches used the standalone mode is default. This usage
|
||||
of switches is more common to programs like "wget". To add a file to
|
||||
server's download queue use switch "-A" (--append) and a name of nzb-file
|
||||
as last command-line parameter;
|
||||
- There is a new switch "-Q" (--quit) to gracefully stop server. BTW the
|
||||
SIGKIL-signal is now handled appropriately, so "killall nzbget" is also OK,
|
||||
where "killall -9 nzbget" terminates server immediately (helpful if it
|
||||
hangs, but it shouldn't);
|
||||
- With new switch "-T" (--top) the file will be added to the top of download
|
||||
queue. Use it with switch "-A" (--append);
|
||||
- The download queue can be edited via switch "-E" (--edit). It is possible
|
||||
to pause, unpause, delete and move files in queue. The IDs of file(s)
|
||||
to be affected are passed via switch "-I" (fileid), either one ID or a
|
||||
range in a form "IDForm-IDTo". This also means, that every file in queue
|
||||
have ID now;
|
||||
- The switch "-L" (--list) prints IDs of files consequently. It prints also
|
||||
name, size, percentage of completing and state (paused or not) of each file.
|
||||
Plus summary info: number of files, total remaining size and size of
|
||||
paused files, server state (paused or running), number of threads on
|
||||
server, current speed limit;
|
||||
- With new switch "-G" (--log) the last N lines printed to server's
|
||||
screen-log, can be printed on client. The max number of lines which can
|
||||
be returned from servers depends on option "logbuffersize";
|
||||
- The redesigned Frontends (known as outputmodes "loggable", "colored" and
|
||||
"curses") can connect to (remote) server and behave as if you were running
|
||||
server-instance of program itself (command-line switch "-C" (--connect)).
|
||||
The log-output updates constantly and even all control-functions in
|
||||
ncurses-mode works: pause/unpause server, set download rate limit, edit of
|
||||
queue (pause/unpause, delete, move entries). The number of connected
|
||||
clients is not limited. The "outputmode" on a client can be set
|
||||
independently from server. The client-mode is especially useful if the
|
||||
server runs as a daemon;
|
||||
- The writing to log-file can be disabled via option "createlog".
|
||||
The location of log-file controls the option "log-file";
|
||||
- Switch "-p" (--printconfig) prints the name of configuration file being
|
||||
used and all option/value-pairs, taking into account all used
|
||||
"-o" (--option) - switches;
|
||||
- The communication protocol between server and client was optimized to
|
||||
minimize the size of transferred data. Instead of fixing the size for
|
||||
Filenames in data-structures to 512 bytes only in fact used data
|
||||
are transferred;
|
||||
- Extensions in ncurses-outputmode: scrolling in queue-list works better,
|
||||
navigation in queue with keys Up, Down, PgUp, PgDn, Home, End.
|
||||
Keys to move entries are "U" (move up), "N" (move down), "T" (move to top),
|
||||
"B" (move to bottom). "P" to pause/unpause file. The size, percentage
|
||||
of completing and state (paused or not) for every file is printed.
|
||||
The header of queue shows number of total files, number of unpaused
|
||||
files and size for all and unpaused files. Better using of screen estate
|
||||
space <20> no more empty lines and separate header for status (total seven
|
||||
lines gain). The messages are printed on several lines (if they not fill
|
||||
in one line) without trimming now;
|
||||
- configure.ac-file updated to work with recent versions of autoconf/automake.
|
||||
There are new configure-options now: "--disable-uulib" to compile the
|
||||
program without uulib; "--disable-ncurses" to disable ncurses-support
|
||||
(eliminates necessity of ncurses-libs), useful on embedded systems with
|
||||
little resources; "--disable-parcheck" to compile without par-check;
|
||||
- The algorithm for parsing of nzb-files now uses XMLReader instead of
|
||||
DOM-Parser to minimize memory usage (no mor needs to build complete DOM-tree
|
||||
in memory). Thanks to Thierry MERLE <merlum@users.sourceforge.net> for
|
||||
the patch;
|
||||
- The log-file contains now thread-ID for all entry-types and additionally
|
||||
for debug-entries: filename, line number and function's name of source
|
||||
code, where the message was printed. Debug-messages can be disabled in
|
||||
config-file (option "debugtarget") like other messages;
|
||||
- The program is now compatible with windows. Project file for MS Visual
|
||||
C++ 2005 is included. Use "nzbget -install" and "nzbget -remove" to
|
||||
install/remove NZBGet-Service. Servers and clients can run on diferrent
|
||||
operating systems;
|
||||
- Improved compatibility with POSIX systems; Tested on:
|
||||
- Linux Debian 3.1 on x86;
|
||||
- Linux BusyBox with uClibc on MIPSEL;
|
||||
- PC-BSD 1.4 (based on FreeBSD 6.2) on x86;
|
||||
- Solaris 10 on x86;
|
||||
- Many memory-leaks and thread issues were fixed;
|
||||
- The program was thoroughly worked over. Almost every line of code was
|
||||
revised.
|
||||
|
||||
nzbget-0.2.3
|
||||
- Fixed problem with losing connection to newsserver after too long idle time
|
||||
- Added functionality for dumping lots of debug info
|
||||
|
||||
nzbget-0.2.2
|
||||
- Added Florian Penzkofers fix for FreeBSD, exchanging base functionality in
|
||||
SingleServerPool.cpp with a more elegant solution
|
||||
- Added functionality for forcing answer to reloading queue upon startup of
|
||||
server
|
||||
+ use -y option to force from command-line
|
||||
+ use "reloadqueue" option in nzbget.cfg to control behavior
|
||||
- Added nzbget.cfg options to control where info, warnings and errors get
|
||||
directed to (either screen, log or both)
|
||||
- Added option "createbrokenfilelog" in nzbget.cfg
|
||||
|
||||
nzbget-0.2.1
|
||||
- Changed and extended the TCP client/server interface
|
||||
- Added timeout on sockets which prevents certain types of nzbget hanging
|
||||
- Added Kristian Hermansen's patch for renaming broken files
|
||||
|
||||
nzbget-0.2.0
|
||||
- Moved 0.1.2-alt4 to a official release as 0.2.0
|
||||
- Small fixes
|
||||
|
||||
nzbget-0.1.2-alt4
|
||||
- implemented tcp/ip communication between client & server (removing the
|
||||
rather defunct System V IPC)
|
||||
- added queue editing functionality in server-mode
|
||||
|
||||
nzbget-0.1.2-alt1
|
||||
- added new ncurses frontend
|
||||
- added server/client-mode (using System V IPC)
|
||||
- added functionality for queueing download requests
|
||||
|
||||
nzbget-0.1.2
|
||||
- performance-improvements
|
||||
- commandline-options
|
||||
- fixes
|
||||
|
||||
nzbget-0.1.1
|
||||
- new output
|
||||
- fixes
|
||||
|
||||
nzbget-0.1.0a
|
||||
- compiling-fixes
|
||||
|
||||
nzbget-0.1.0
|
||||
- initial release
|
||||
|
||||
@@ -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,52 +74,49 @@ void ColoredFrontend::PrintStatus()
|
||||
char tmp[1024];
|
||||
char timeString[100];
|
||||
timeString[0] = '\0';
|
||||
float fCurrentDownloadSpeed = m_bStandBy ? 0 : m_fCurrentDownloadSpeed;
|
||||
|
||||
if (m_fCurrentDownloadSpeed > 0.0f)
|
||||
if (fCurrentDownloadSpeed > 0.0 && !(m_bPauseDownload || m_bPauseDownload2))
|
||||
{
|
||||
long long remain_sec = m_lRemainingSize / ((long long int)(m_fCurrentDownloadSpeed * 1024));
|
||||
int h = 0;
|
||||
int m = 0;
|
||||
int s = 0;
|
||||
while (remain_sec > 3600)
|
||||
{
|
||||
h++;
|
||||
remain_sec -= 3600;
|
||||
}
|
||||
|
||||
while (remain_sec > 60)
|
||||
{
|
||||
m++;
|
||||
remain_sec -= 60;
|
||||
}
|
||||
|
||||
s = remain_sec;
|
||||
|
||||
sprintf(timeString, "(~ %.2d:%.2d:%.2d)", h, m, s);
|
||||
long long remain_sec = m_lRemainingSize / ((long long)(fCurrentDownloadSpeed * 1024));
|
||||
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);
|
||||
}
|
||||
|
||||
const char* szPause[] = { "Paused", "" };
|
||||
int iPauseIdx = m_bPause ? 0 : 1;
|
||||
|
||||
char szDownloadLimit[128];
|
||||
if (m_fDownloadLimit > 0.0f)
|
||||
{
|
||||
sprintf(szDownloadLimit, "Limit %.0f KB/S", m_fDownloadLimit);
|
||||
sprintf(szDownloadLimit, ", Limit %.0f KB/s", m_fDownloadLimit);
|
||||
}
|
||||
else
|
||||
{
|
||||
szDownloadLimit[0] = 0;
|
||||
}
|
||||
|
||||
char szPostStatus[128];
|
||||
if (m_iPostJobCount > 0)
|
||||
{
|
||||
sprintf(szPostStatus, ", %i post-job%s", m_iPostJobCount, m_iPostJobCount > 1 ? "s" : "");
|
||||
}
|
||||
else
|
||||
{
|
||||
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 running, %.0f KB/s, %.2f MB remaining %s %s %s%s\n",
|
||||
m_iThreadCount, m_fCurrentDownloadSpeed, (float)(m_lRemainingSize / 1024.0 / 1024.0),
|
||||
timeString, szPause[iPauseIdx], szDownloadLimit, szControlSeq);
|
||||
|
||||
snprintf(tmp, 1024, " %d threads, %.*f KB/s, %.2f MB remaining%s%s%s%s%s\n",
|
||||
m_iThreadCount, (fCurrentDownloadSpeed >= 10 ? 0 : 1), fCurrentDownloadSpeed,
|
||||
(float)(Util::Int64ToFloat(m_lRemainingSize) / 1024.0 / 1024.0), timeString, szPostStatus,
|
||||
m_bPauseDownload || m_bPauseDownload2 ? (m_bStandBy ? ", Paused" : ", Pausing") : "",
|
||||
szDownloadLimit, szControlSeq);
|
||||
tmp[1024-1] = '\0';
|
||||
printf("%s", tmp);
|
||||
m_bNeedGoBack = true;
|
||||
@@ -126,7 +124,6 @@ void ColoredFrontend::PrintStatus()
|
||||
|
||||
void ColoredFrontend::PrintMessage(Message * pMessage)
|
||||
{
|
||||
const char* msg = pMessage->GetText();
|
||||
#ifdef WIN32
|
||||
switch (pMessage->GetKind())
|
||||
{
|
||||
@@ -146,10 +143,18 @@ 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());
|
||||
CharToOem(msg, msg);
|
||||
printf(" %s\n", msg);
|
||||
free(msg);
|
||||
#else
|
||||
const char* msg = pMessage->GetText();
|
||||
switch (pMessage->GetKind())
|
||||
{
|
||||
case Message::mkDebug:
|
||||
@@ -164,6 +169,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$
|
||||
|
||||
651
Connection.cpp
651
Connection.cpp
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -25,20 +25,28 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
// SKIP_DEFAULT_WINDOWS_HEADERS prevents the including of <windows.h>, which includes "winsock.h",
|
||||
// but we need "winsock2.h" here (they conflicts with each other)
|
||||
#define SKIP_DEFAULT_WINDOWS_HEADERS
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifndef WIN32
|
||||
#include <cstdio>
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/time.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
@@ -46,10 +54,21 @@
|
||||
#include "nzbget.h"
|
||||
#include "Connection.h"
|
||||
#include "Log.h"
|
||||
#include "TLS.h"
|
||||
|
||||
static const int CONNECTION_READBUFFER_SIZE = 1024;
|
||||
#ifndef DISABLE_TLS
|
||||
bool Connection::bTLSLibInitialized = false;
|
||||
#endif
|
||||
#ifndef HAVE_GETADDRINFO
|
||||
#ifndef HAVE_GETHOSTBYNAME_R
|
||||
Mutex* Connection::m_pMutexGetHostByName = NULL;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void Connection::Init()
|
||||
{
|
||||
debug("Intiializing global connection data");
|
||||
debug("Initializing global connection data");
|
||||
|
||||
#ifdef WIN32
|
||||
WSADATA wsaData;
|
||||
@@ -66,6 +85,27 @@ void Connection::Init()
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_TLS
|
||||
debug("Initializing TLS library");
|
||||
char* szErrStr;
|
||||
int iRes = tls_lib_init(&szErrStr);
|
||||
bTLSLibInitialized = iRes == TLS_EOK;
|
||||
if (!bTLSLibInitialized)
|
||||
{
|
||||
error("Could not initialize TLS library: %s", szErrStr ? szErrStr : "unknown error");
|
||||
if (szErrStr)
|
||||
{
|
||||
free(szErrStr);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_GETADDRINFO
|
||||
#ifndef HAVE_GETHOSTBYNAME_R
|
||||
m_pMutexGetHostByName = new Mutex();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
void Connection::Final()
|
||||
@@ -75,60 +115,126 @@ void Connection::Final()
|
||||
#ifdef WIN32
|
||||
WSACleanup();
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_TLS
|
||||
if (bTLSLibInitialized)
|
||||
{
|
||||
debug("Finalizing TLS library");
|
||||
tls_lib_deinit();
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_GETADDRINFO
|
||||
#ifndef HAVE_GETHOSTBYNAME_R
|
||||
delete m_pMutexGetHostByName;
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Connection::Connection(NetAddress* pNetAddress)
|
||||
Connection::Connection(const char* szHost, int iPort, bool bTLS)
|
||||
{
|
||||
debug("Creating Connection");
|
||||
|
||||
m_pNetAddress = pNetAddress;
|
||||
m_szHost = NULL;
|
||||
m_iPort = iPort;
|
||||
m_bTLS = bTLS;
|
||||
m_eStatus = csDisconnected;
|
||||
m_iSocket = INVALID_SOCKET;
|
||||
m_iBufAvail = 0;
|
||||
m_bCanceling = false;
|
||||
m_iTimeout = 60;
|
||||
m_bSuppressErrors = true;
|
||||
m_szReadBuf = (char*)malloc(CONNECTION_READBUFFER_SIZE + 1);
|
||||
m_bAutoClose = true;
|
||||
#ifndef DISABLE_TLS
|
||||
m_pTLS = NULL;
|
||||
m_bTLSError = false;
|
||||
#endif
|
||||
|
||||
if (szHost)
|
||||
{
|
||||
m_szHost = strdup(szHost);
|
||||
}
|
||||
}
|
||||
|
||||
Connection::Connection(SOCKET iSocket, bool bAutoClose)
|
||||
{
|
||||
debug("Creating Connection");
|
||||
|
||||
m_szHost = NULL;
|
||||
m_iPort = 0;
|
||||
m_bTLS = false;
|
||||
m_eStatus = csConnected;
|
||||
m_iSocket = iSocket;
|
||||
m_iBufAvail = 0;
|
||||
m_iTimeout = 60;
|
||||
m_bSuppressErrors = true;
|
||||
m_szReadBuf = (char*)malloc(CONNECTION_READBUFFER_SIZE + 1);
|
||||
m_bAutoClose = bAutoClose;
|
||||
#ifndef DISABLE_TLS
|
||||
m_pTLS = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
Connection::~Connection()
|
||||
{
|
||||
debug("Destroying Connection");
|
||||
|
||||
if (m_eStatus == csConnected)
|
||||
if (m_szHost)
|
||||
{
|
||||
free(m_szHost);
|
||||
}
|
||||
if (m_bAutoClose)
|
||||
{
|
||||
Disconnect();
|
||||
}
|
||||
free(m_szReadBuf);
|
||||
#ifndef DISABLE_TLS
|
||||
if (m_pTLS)
|
||||
{
|
||||
free(m_pTLS);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
int Connection::Connect()
|
||||
bool Connection::Connect()
|
||||
{
|
||||
debug("Connecting");
|
||||
|
||||
if (m_eStatus == csConnected)
|
||||
return 0;
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int iRes = DoConnect();
|
||||
bool bRes = DoConnect();
|
||||
|
||||
if (iRes >= 0)
|
||||
if (bRes)
|
||||
{
|
||||
m_eStatus = csConnected;
|
||||
}
|
||||
else
|
||||
{
|
||||
Connection::DoDisconnect();
|
||||
}
|
||||
|
||||
return iRes;
|
||||
return bRes;
|
||||
}
|
||||
|
||||
int Connection::Disconnect()
|
||||
bool Connection::Disconnect()
|
||||
{
|
||||
debug("Disconnecting");
|
||||
|
||||
if (m_eStatus == csDisconnected)
|
||||
return 0;
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
int iRes = DoDisconnect();
|
||||
bool bRes = DoDisconnect();
|
||||
|
||||
m_eStatus = csDisconnected;
|
||||
m_iSocket = INVALID_SOCKET;
|
||||
m_iBufAvail = 0;
|
||||
|
||||
return iRes;
|
||||
return bRes;
|
||||
}
|
||||
|
||||
int Connection::Bind()
|
||||
@@ -142,12 +248,15 @@ int Connection::Bind()
|
||||
|
||||
int iRes = DoBind();
|
||||
|
||||
m_eStatus = csListening;
|
||||
if (iRes == 0)
|
||||
{
|
||||
m_eStatus = csListening;
|
||||
}
|
||||
|
||||
return iRes;
|
||||
}
|
||||
|
||||
int Connection::WriteLine(char* line)
|
||||
int Connection::WriteLine(const char* pBuffer)
|
||||
{
|
||||
//debug("Connection::write(char* line)");
|
||||
|
||||
@@ -156,15 +265,12 @@ int Connection::WriteLine(char* line)
|
||||
return -1;
|
||||
}
|
||||
|
||||
int iRes = DoWriteLine(line);
|
||||
|
||||
if (iRes == EOF)
|
||||
Connection::DoDisconnect();
|
||||
int iRes = DoWriteLine(pBuffer);
|
||||
|
||||
return iRes;
|
||||
}
|
||||
|
||||
int Connection::Send(char* pBuffer, int iSize)
|
||||
int Connection::Send(const char* pBuffer, int iSize)
|
||||
{
|
||||
debug("Sending data");
|
||||
|
||||
@@ -178,17 +284,14 @@ int Connection::Send(char* pBuffer, int iSize)
|
||||
return iRes;
|
||||
}
|
||||
|
||||
char* Connection::ReadLine(char* pBuffer, int iSize)
|
||||
char* Connection::ReadLine(char* pBuffer, int iSize, int* pBytesRead)
|
||||
{
|
||||
if (m_eStatus != csConnected)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char* res = DoReadLine(pBuffer, iSize);
|
||||
|
||||
if (res == NULL)
|
||||
Connection::DoDisconnect();
|
||||
char* res = DoReadLine(pBuffer, iSize, pBytesRead);
|
||||
|
||||
return res;
|
||||
}
|
||||
@@ -217,7 +320,7 @@ int Connection::Recv(char* pBuffer, int iSize)
|
||||
|
||||
if (iReceived < 0)
|
||||
{
|
||||
ReportError("Could not receive data on socket", NULL, 0);
|
||||
ReportError("Could not receive data on socket", NULL, true, 0);
|
||||
}
|
||||
|
||||
return iReceived;
|
||||
@@ -231,6 +334,17 @@ bool Connection::RecvAll(char * pBuffer, int iSize)
|
||||
|
||||
char* pBufPtr = (char*)pBuffer;
|
||||
int NeedBytes = iSize;
|
||||
|
||||
if (m_iBufAvail > 0)
|
||||
{
|
||||
int len = iSize > m_iBufAvail ? m_iBufAvail : iSize;
|
||||
memcpy(pBufPtr, m_szBufPtr, len);
|
||||
pBufPtr += len;
|
||||
m_szBufPtr += len;
|
||||
m_iBufAvail -= len;
|
||||
NeedBytes -= len;
|
||||
}
|
||||
|
||||
// Read from the socket until nothing remains
|
||||
while (NeedBytes > 0)
|
||||
{
|
||||
@@ -238,7 +352,7 @@ bool Connection::RecvAll(char * pBuffer, int iSize)
|
||||
// Did the recv succeed?
|
||||
if (iReceived <= 0)
|
||||
{
|
||||
ReportError("Could not receive data on socket", NULL, 0);
|
||||
ReportError("Could not receive data on socket", NULL, true, 0);
|
||||
return false;
|
||||
}
|
||||
pBufPtr += iReceived;
|
||||
@@ -247,35 +361,81 @@ bool Connection::RecvAll(char * pBuffer, int iSize)
|
||||
return true;
|
||||
}
|
||||
|
||||
int Connection::DoConnect()
|
||||
bool Connection::DoConnect()
|
||||
{
|
||||
debug("Do connecting");
|
||||
|
||||
m_iSocket = INVALID_SOCKET;
|
||||
|
||||
#ifdef HAVE_GETADDRINFO
|
||||
struct addrinfo addr_hints, *addr_list, *addr;
|
||||
char iPortStr[sizeof(int) * 4 + 1]; //is enough to hold any converted int
|
||||
|
||||
memset(&addr_hints, 0, sizeof(addr_hints));
|
||||
addr_hints.ai_family = AF_UNSPEC; /* Allow IPv4 or IPv6 */
|
||||
addr_hints.ai_socktype = SOCK_STREAM,
|
||||
|
||||
sprintf(iPortStr, "%d", m_iPort);
|
||||
|
||||
int res = getaddrinfo(m_szHost, iPortStr, &addr_hints, &addr_list);
|
||||
if (res != 0)
|
||||
{
|
||||
ReportError("Could not resolve hostname %s", m_szHost, true, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (addr = addr_list; addr != NULL; addr = addr->ai_next)
|
||||
{
|
||||
m_iSocket = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
|
||||
if (m_iSocket != INVALID_SOCKET)
|
||||
{
|
||||
res = connect(m_iSocket , addr->ai_addr, addr->ai_addrlen);
|
||||
if (res != -1)
|
||||
{
|
||||
// Connection established
|
||||
break;
|
||||
}
|
||||
// Connection failed
|
||||
closesocket(m_iSocket);
|
||||
m_iSocket = INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
|
||||
freeaddrinfo(addr_list);
|
||||
|
||||
#else
|
||||
|
||||
struct sockaddr_in sSocketAddress;
|
||||
memset(&sSocketAddress, '\0', sizeof(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());
|
||||
sSocketAddress.sin_port = htons(m_iPort);
|
||||
sSocketAddress.sin_addr.s_addr = ResolveHostAddr(m_szHost);
|
||||
if (sSocketAddress.sin_addr.s_addr == (unsigned int)-1)
|
||||
{
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
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;
|
||||
ReportError("Socket creation failed for %s", m_szHost, true, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
int res = connect(m_iSocket , (struct sockaddr *) & sSocketAddress, sizeof(sSocketAddress));
|
||||
|
||||
if (res < 0)
|
||||
if (res == -1)
|
||||
{
|
||||
ReportError("Connection to %s failed!", m_pNetAddress->GetHost(), 0);
|
||||
return -1;
|
||||
// Connection failed
|
||||
closesocket(m_iSocket);
|
||||
m_iSocket = INVALID_SOCKET;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_iSocket == INVALID_SOCKET)
|
||||
{
|
||||
ReportError("Connection to %s failed", m_szHost, true, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
int MSecVal = m_iTimeout * 1000;
|
||||
@@ -288,87 +448,61 @@ int Connection::DoConnect()
|
||||
#endif
|
||||
if (err != 0)
|
||||
{
|
||||
ReportError("setsockopt failed", NULL, 0);
|
||||
ReportError("setsockopt failed", NULL, true, 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int Connection::ResolveHostAddr(const char* szHost)
|
||||
{
|
||||
unsigned int uaddr = inet_addr(szHost);
|
||||
if (uaddr == (unsigned int)-1)
|
||||
#ifndef DISABLE_TLS
|
||||
if (m_bTLS && !StartTLS())
|
||||
{
|
||||
struct hostent* hinfo;
|
||||
bool err = false;
|
||||
int h_errnop;
|
||||
#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);
|
||||
#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 false;
|
||||
}
|
||||
return uaddr;
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int Connection::DoDisconnect()
|
||||
bool Connection::DoDisconnect()
|
||||
{
|
||||
debug("Do disconnecting");
|
||||
|
||||
if (m_iSocket > 0)
|
||||
if (m_iSocket != INVALID_SOCKET)
|
||||
{
|
||||
closesocket(m_iSocket);
|
||||
m_iSocket = INVALID_SOCKET;
|
||||
#ifndef DISABLE_TLS
|
||||
if (m_pTLS)
|
||||
{
|
||||
CloseTLS();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
m_eStatus = csDisconnected;
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
int Connection::DoWriteLine(char* szText)
|
||||
int Connection::DoWriteLine(const char* pBuffer)
|
||||
{
|
||||
//debug("Connection::doWrite()");
|
||||
return send(m_iSocket, szText, strlen(szText), 0);
|
||||
return send(m_iSocket, pBuffer, strlen(pBuffer), 0);
|
||||
}
|
||||
|
||||
char* Connection::DoReadLine(char* pBuffer, int iSize)
|
||||
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, ReadBufLen, 0);
|
||||
iBufAvail = recv(m_iSocket, m_szReadBuf, CONNECTION_READBUFFER_SIZE, 0);
|
||||
if (iBufAvail < 0)
|
||||
{
|
||||
ReportError("Could not receive data on socket", NULL, 0);
|
||||
ReportError("Could not receive data on socket", NULL, true, 0);
|
||||
break;
|
||||
}
|
||||
else if (iBufAvail == 0)
|
||||
@@ -383,7 +517,7 @@ char* Connection::DoReadLine(char* pBuffer, int iSize)
|
||||
char* p = (char*)memchr(szBufPtr, '\n', iBufAvail);
|
||||
if (p)
|
||||
{
|
||||
len = p - szBufPtr + 1;
|
||||
len = (int)(p - szBufPtr + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -399,18 +533,24 @@ char* Connection::DoReadLine(char* pBuffer, int iSize)
|
||||
pBufPtr += len;
|
||||
szBufPtr += len;
|
||||
iBufAvail -= len;
|
||||
iBytesRead += len;
|
||||
iSize -= len;
|
||||
|
||||
if (p)
|
||||
{
|
||||
break;
|
||||
}
|
||||
iSize--;
|
||||
}
|
||||
*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;
|
||||
@@ -418,34 +558,106 @@ char* Connection::DoReadLine(char* pBuffer, int iSize)
|
||||
return pBuffer;
|
||||
}
|
||||
|
||||
|
||||
void Connection::ReadBuffer(char** pBuffer, int *iBufLen)
|
||||
{
|
||||
*iBufLen = m_iBufAvail;
|
||||
*pBuffer = m_szBufPtr;
|
||||
m_iBufAvail = 0;
|
||||
};
|
||||
|
||||
|
||||
int Connection::DoBind()
|
||||
{
|
||||
debug("Do binding");
|
||||
|
||||
m_iSocket = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (m_iSocket == INVALID_SOCKET)
|
||||
#ifdef HAVE_GETADDRINFO
|
||||
struct addrinfo addr_hints, *addr_list, *addr;
|
||||
char iPortStr[sizeof(int) * 4 + 1]; // is enough to hold any converted int
|
||||
|
||||
memset(&addr_hints, 0, sizeof(addr_hints));
|
||||
addr_hints.ai_family = AF_UNSPEC; // Allow IPv4 or IPv6
|
||||
addr_hints.ai_socktype = SOCK_STREAM,
|
||||
addr_hints.ai_flags = AI_PASSIVE; // For wildcard IP address
|
||||
|
||||
sprintf(iPortStr, "%d", m_iPort);
|
||||
|
||||
int res = getaddrinfo(m_szHost, iPortStr, &addr_hints, &addr_list);
|
||||
if (res != 0)
|
||||
{
|
||||
ReportError("Socket creation failed for %s!", m_pNetAddress->GetHost(), 0);
|
||||
error("Could not resolve hostname %s", m_szHost);
|
||||
return -1;
|
||||
}
|
||||
|
||||
m_iSocket = INVALID_SOCKET;
|
||||
for (addr = addr_list; addr != NULL; addr = addr->ai_next)
|
||||
{
|
||||
m_iSocket = socket(addr->ai_family, addr->ai_socktype, addr->ai_protocol);
|
||||
if (m_iSocket != INVALID_SOCKET)
|
||||
{
|
||||
int opt = 1;
|
||||
setsockopt(m_iSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));
|
||||
res = bind(m_iSocket, addr->ai_addr, addr->ai_addrlen);
|
||||
if (res != -1)
|
||||
{
|
||||
// Connection established
|
||||
break;
|
||||
}
|
||||
// Connection failed
|
||||
closesocket(m_iSocket);
|
||||
m_iSocket = INVALID_SOCKET;
|
||||
}
|
||||
}
|
||||
|
||||
freeaddrinfo(addr_list);
|
||||
|
||||
#else
|
||||
|
||||
struct sockaddr_in sSocketAddress;
|
||||
memset(&sSocketAddress, '\0', sizeof(sSocketAddress));
|
||||
memset(&sSocketAddress, 0, sizeof(sSocketAddress));
|
||||
sSocketAddress.sin_family = AF_INET;
|
||||
sSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
sSocketAddress.sin_port = htons(m_pNetAddress->GetPort());
|
||||
if (!m_szHost || strlen(m_szHost) == 0)
|
||||
{
|
||||
sSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
}
|
||||
else
|
||||
{
|
||||
sSocketAddress.sin_addr.s_addr = ResolveHostAddr(m_szHost);
|
||||
if (sSocketAddress.sin_addr.s_addr == (unsigned int)-1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
sSocketAddress.sin_port = htons(m_iPort);
|
||||
|
||||
m_iSocket = socket(PF_INET, SOCK_STREAM, 0);
|
||||
if (m_iSocket == INVALID_SOCKET)
|
||||
{
|
||||
ReportError("Socket creation failed for %s", m_szHost, true, 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int opt = 1;
|
||||
setsockopt(m_iSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));
|
||||
|
||||
if (bind(m_iSocket, (struct sockaddr *) &sSocketAddress, sizeof(sSocketAddress)) < 0)
|
||||
int res = bind(m_iSocket, (struct sockaddr *) &sSocketAddress, sizeof(sSocketAddress));
|
||||
if (res == -1)
|
||||
{
|
||||
ReportError("Binding socket failed for %s", m_pNetAddress->GetHost(), 0);
|
||||
// Connection failed
|
||||
closesocket(m_iSocket);
|
||||
m_iSocket = INVALID_SOCKET;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_iSocket == INVALID_SOCKET)
|
||||
{
|
||||
ReportError("Binding socket failed for %s", m_szHost, true, 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (listen(m_iSocket, 10) < 0)
|
||||
{
|
||||
ReportError("Listen on socket failed for %s", m_pNetAddress->GetHost(), 0);
|
||||
ReportError("Listen on socket failed for %s", m_szHost, true, 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -454,16 +666,11 @@ int Connection::DoBind()
|
||||
|
||||
SOCKET Connection::DoAccept()
|
||||
{
|
||||
struct sockaddr_in ClientAddress;
|
||||
socklen_t SockLen;
|
||||
SOCKET iSocket = accept(GetSocket(), NULL, NULL);
|
||||
|
||||
SockLen = sizeof(ClientAddress);
|
||||
|
||||
SOCKET iSocket = accept(GetSocket(), (struct sockaddr *) & ClientAddress, &SockLen);
|
||||
|
||||
if (iSocket == INVALID_SOCKET && !m_bCanceling)
|
||||
if (iSocket == INVALID_SOCKET && m_eStatus != csCancelled)
|
||||
{
|
||||
ReportError("Could not accept connection", NULL, 0);
|
||||
ReportError("Could not accept connection", NULL, true, 0);
|
||||
}
|
||||
|
||||
return iSocket;
|
||||
@@ -472,36 +679,226 @@ SOCKET Connection::DoAccept()
|
||||
void Connection::Cancel()
|
||||
{
|
||||
debug("Cancelling connection");
|
||||
m_bCanceling = true;
|
||||
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);
|
||||
ReportError("Could not shutdown connection", NULL, true, 0);
|
||||
}
|
||||
m_eStatus = csCancelled;
|
||||
}
|
||||
}
|
||||
|
||||
void Connection::ReportError(const char* szMsgPrefix, const char* szMsgArg, int ErrCode)
|
||||
void Connection::ReportError(const char* szMsgPrefix, const char* szMsgArg, bool PrintErrCode, int herrno)
|
||||
{
|
||||
if (ErrCode == 0)
|
||||
#ifndef DISABLE_TLS
|
||||
if (m_bTLSError)
|
||||
{
|
||||
#ifdef WIN32
|
||||
ErrCode = WSAGetLastError();
|
||||
#else
|
||||
ErrCode = errno;
|
||||
#endif
|
||||
// TLS-Error was already reported
|
||||
m_bTLSError = false;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
char szErrPrefix[1024];
|
||||
snprintf(szErrPrefix, 1024, szMsgPrefix, szMsgArg);
|
||||
szErrPrefix[1024-1] = '\0';
|
||||
|
||||
if (PrintErrCode)
|
||||
{
|
||||
#ifdef WIN32
|
||||
debug("%s: ErrNo %i", szErrPrefix, ErrCode);
|
||||
int ErrCode = WSAGetLastError();
|
||||
if (m_bSuppressErrors)
|
||||
{
|
||||
debug("%s: ErrNo %i", szErrPrefix, ErrCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("%s: ErrNo %i", szErrPrefix, ErrCode);
|
||||
}
|
||||
#else
|
||||
const char* szErrMsg = hstrerror(ErrCode);
|
||||
debug("%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
|
||||
const char *szErrMsg = NULL;
|
||||
int ErrCode = herrno;
|
||||
if (herrno == 0)
|
||||
{
|
||||
ErrCode = errno;
|
||||
szErrMsg = strerror(ErrCode);
|
||||
}
|
||||
else
|
||||
{
|
||||
szErrMsg = hstrerror(ErrCode);
|
||||
}
|
||||
|
||||
if (m_bSuppressErrors)
|
||||
{
|
||||
debug("%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_bSuppressErrors)
|
||||
{
|
||||
debug(szErrPrefix);
|
||||
}
|
||||
else
|
||||
{
|
||||
error(szErrPrefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef DISABLE_TLS
|
||||
bool Connection::CheckTLSResult(int iResultCode, char* szErrStr, const char* szErrMsgPrefix)
|
||||
{
|
||||
bool bOK = iResultCode == TLS_EOK;
|
||||
if (!bOK)
|
||||
{
|
||||
ReportError(szErrMsgPrefix, szErrStr ? szErrStr : "unknown error", false, 0);
|
||||
if (szErrStr)
|
||||
{
|
||||
free(szErrStr);
|
||||
}
|
||||
}
|
||||
return bOK;
|
||||
}
|
||||
|
||||
bool Connection::StartTLS()
|
||||
{
|
||||
debug("Starting TLS");
|
||||
|
||||
if (m_pTLS)
|
||||
{
|
||||
free(m_pTLS);
|
||||
}
|
||||
|
||||
m_pTLS = malloc(sizeof(tls_t));
|
||||
tls_t* pTLS = (tls_t*)m_pTLS;
|
||||
memset(pTLS, 0, sizeof(tls_t));
|
||||
tls_clear(pTLS);
|
||||
|
||||
char* szErrStr;
|
||||
int iRes;
|
||||
|
||||
iRes = tls_init(pTLS, NULL, NULL, NULL, 0, &szErrStr);
|
||||
if (!CheckTLSResult(iRes, szErrStr, "Could not initialize TLS-object: %s"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
debug("tls_start...");
|
||||
iRes = tls_start(pTLS, (int)m_iSocket, NULL, 1, NULL, &szErrStr);
|
||||
debug("tls_start...%i", iRes);
|
||||
if (!CheckTLSResult(iRes, szErrStr, "Could not establish secure connection: %s"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Connection::CloseTLS()
|
||||
{
|
||||
tls_close((tls_t*)m_pTLS);
|
||||
free(m_pTLS);
|
||||
m_pTLS = NULL;
|
||||
}
|
||||
|
||||
int Connection::recv(SOCKET s, char* buf, int len, int flags)
|
||||
{
|
||||
size_t iReceived = 0;
|
||||
|
||||
if (m_pTLS)
|
||||
{
|
||||
m_bTLSError = false;
|
||||
char* szErrStr;
|
||||
int iRes = tls_getbuf((tls_t*)m_pTLS, buf, len, &iReceived, &szErrStr);
|
||||
if (!CheckTLSResult(iRes, szErrStr, "Could not read from TLS-socket: %s"))
|
||||
{
|
||||
m_bTLSError = true;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
iReceived = ::recv(s, buf, len, flags);
|
||||
}
|
||||
return iReceived;
|
||||
}
|
||||
|
||||
int Connection::send(SOCKET s, const char* buf, int len, int flags)
|
||||
{
|
||||
if (m_pTLS)
|
||||
{
|
||||
m_bTLSError = false;
|
||||
char* szErrStr;
|
||||
int iRes = tls_putbuf((tls_t*)m_pTLS, buf, len, &szErrStr);
|
||||
if (!CheckTLSResult(iRes, szErrStr, "Could not send to TLS-socket: %s"))
|
||||
{
|
||||
m_bTLSError = true;
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int iRet = ::send(s, buf, len, flags);
|
||||
return iRet;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_GETADDRINFO
|
||||
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 HAVE_GETHOSTBYNAME_R
|
||||
struct hostent hinfobuf;
|
||||
char strbuf[1024];
|
||||
#ifdef HAVE_GETHOSTBYNAME_R_6
|
||||
err = gethostbyname_r(szHost, &hinfobuf, strbuf, sizeof(strbuf), &hinfo, &h_errnop);
|
||||
err = err || (hinfo == NULL); // error on null hinfo (means 'no entry')
|
||||
#endif
|
||||
#ifdef HAVE_GETHOSTBYNAME_R_5
|
||||
hinfo = gethostbyname_r(szHost, &hinfobuf, strbuf, sizeof(strbuf), &h_errnop);
|
||||
err = hinfo == NULL;
|
||||
#endif
|
||||
#ifdef HAVE_GETHOSTBYNAME_R_3
|
||||
//NOTE: gethostbyname_r with three parameters were not tested
|
||||
struct hostent_data hinfo_data;
|
||||
hinfo = gethostbyname_r((char*)szHost, (struct hostent*)hinfobuf, &hinfo_data);
|
||||
err = hinfo == NULL;
|
||||
#endif
|
||||
#else
|
||||
m_pMutexGetHostByName->Lock();
|
||||
hinfo = gethostbyname(szHost);
|
||||
err = hinfo == NULL;
|
||||
#endif
|
||||
if (err)
|
||||
{
|
||||
#ifndef HAVE_GETHOSTBYNAME_R
|
||||
m_pMutexGetHostByName->Unlock();
|
||||
#endif
|
||||
ReportError("Could not resolve hostname %s", szHost, true, h_errnop);
|
||||
return (unsigned int)-1;
|
||||
}
|
||||
|
||||
memcpy(&uaddr, hinfo->h_addr_list[0], sizeof(uaddr));
|
||||
|
||||
#ifndef HAVE_GETHOSTBYNAME_R
|
||||
m_pMutexGetHostByName->Unlock();
|
||||
#endif
|
||||
}
|
||||
return uaddr;
|
||||
}
|
||||
#endif
|
||||
|
||||
83
Connection.h
83
Connection.h
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -27,7 +27,11 @@
|
||||
#ifndef CONNECTION_H
|
||||
#define CONNECTION_H
|
||||
|
||||
#include "NetAddress.h"
|
||||
#ifndef HAVE_GETADDRINFO
|
||||
#ifndef HAVE_GETHOSTBYNAME_R
|
||||
#include "Thread.h"
|
||||
#endif
|
||||
#endif
|
||||
|
||||
class Connection
|
||||
{
|
||||
@@ -41,44 +45,73 @@ public:
|
||||
};
|
||||
|
||||
protected:
|
||||
NetAddress* m_pNetAddress;
|
||||
char* m_szHost;
|
||||
int m_iPort;
|
||||
SOCKET m_iSocket;
|
||||
static const int ReadBufLen = 1024;
|
||||
char m_szReadBuf[ReadBufLen + 1];
|
||||
bool m_bTLS;
|
||||
char* m_szReadBuf;
|
||||
int m_iBufAvail;
|
||||
char* m_szBufPtr;
|
||||
EStatus m_eStatus;
|
||||
bool m_bCanceling;
|
||||
int m_iTimeout;
|
||||
bool m_bSuppressErrors;
|
||||
bool m_bAutoClose;
|
||||
#ifndef DISABLE_TLS
|
||||
void* m_pTLS;
|
||||
static bool bTLSLibInitialized;
|
||||
bool m_bTLSError;
|
||||
#endif
|
||||
#ifndef HAVE_GETADDRINFO
|
||||
#ifndef HAVE_GETHOSTBYNAME_R
|
||||
static Mutex* m_pMutexGetHostByName;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
void ReportError(const char* szMsgPrefix, const char* szMsgArg, bool PrintErrCode, int herrno);
|
||||
virtual bool DoConnect();
|
||||
virtual bool DoDisconnect();
|
||||
int DoBind();
|
||||
int DoWriteLine(const char* pBuffer);
|
||||
char* DoReadLine(char* pBuffer, int iSize, int* pBytesRead);
|
||||
SOCKET DoAccept();
|
||||
#ifndef HAVE_GETADDRINFO
|
||||
unsigned int ResolveHostAddr(const char* szHost);
|
||||
void ReportError(const char* szMsgPrefix, const char* szMsgArg, int ErrCode);
|
||||
#endif
|
||||
#ifndef DISABLE_TLS
|
||||
bool CheckTLSResult(int iResultCode, char* szErrStr, const char* szErrMsgPrefix);
|
||||
int recv(SOCKET s, char* buf, int len, int flags);
|
||||
int send(SOCKET s, const char* buf, int len, int flags);
|
||||
void CloseTLS();
|
||||
#endif
|
||||
|
||||
public:
|
||||
Connection(NetAddress* pNetAddress);
|
||||
Connection(const char* szHost, int iPort, bool bTLS);
|
||||
Connection(SOCKET iSocket, bool bAutoClose);
|
||||
virtual ~Connection();
|
||||
static void Init();
|
||||
static void Final();
|
||||
int Connect();
|
||||
int Disconnect();
|
||||
bool Connect();
|
||||
bool Disconnect();
|
||||
int Bind();
|
||||
int Send(char* pBuffer, int iSize);
|
||||
int Send(const char* pBuffer, int iSize);
|
||||
int Recv(char* pBuffer, int iSize);
|
||||
bool RecvAll(char* pBuffer, int iSize);
|
||||
char* ReadLine(char* pBuffer, int iSize);
|
||||
int WriteLine(char* text);
|
||||
char* ReadLine(char* pBuffer, int iSize, int* pBytesRead);
|
||||
void ReadBuffer(char** pBuffer, int *iBufLen);
|
||||
int WriteLine(const char* pBuffer);
|
||||
SOCKET Accept();
|
||||
void Cancel();
|
||||
NetAddress* GetServer() { return m_pNetAddress; }
|
||||
const char* GetHost() { return m_szHost; }
|
||||
int GetPort() { return m_iPort; }
|
||||
bool GetTLS() { return m_bTLS; }
|
||||
SOCKET GetSocket() { return m_iSocket; }
|
||||
void SetTimeout(int iTimeout) { m_iTimeout = iTimeout; }
|
||||
|
||||
protected:
|
||||
virtual int DoConnect();
|
||||
virtual int DoDisconnect();
|
||||
int DoBind();
|
||||
int DoWriteLine(char* text);
|
||||
char* DoReadLine(char* pBuffer, int iSize);
|
||||
SOCKET DoAccept();
|
||||
EStatus GetStatus() { return m_eStatus; }
|
||||
void SetSuppressErrors(bool bSuppressErrors) { m_bSuppressErrors = bSuppressErrors; }
|
||||
bool GetSuppressErrors() { return m_bSuppressErrors; }
|
||||
#ifndef DISABLE_TLS
|
||||
bool StartTLS();
|
||||
#endif
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
584
Decoder.cpp
584
Decoder.cpp
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -40,39 +40,12 @@
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
|
||||
#ifdef ENABLE_UULIB
|
||||
#ifndef PROTOTYPES
|
||||
#define PROTOTYPES
|
||||
#endif
|
||||
#include <uudeview.h>
|
||||
#endif
|
||||
|
||||
//#define USEEXTERNALDECODER // not working
|
||||
//#define DEBUGDECODER
|
||||
|
||||
#include "Decoder.h"
|
||||
#include "Log.h"
|
||||
#include "Options.h"
|
||||
#include "Util.h"
|
||||
|
||||
#ifdef DEBUGDECODER
|
||||
int g_iDecoderID = 0;
|
||||
#endif
|
||||
|
||||
Mutex Decoder::m_mutexDecoder;
|
||||
unsigned int Decoder::crc_tab[256];
|
||||
|
||||
void Decoder::Init()
|
||||
{
|
||||
debug("Initializing global decoder");
|
||||
crc32gentab();
|
||||
}
|
||||
|
||||
void Decoder::Final()
|
||||
{
|
||||
debug("Finalizing global Decoder");
|
||||
}
|
||||
const char* Decoder::FormatNames[] = { "Unknown", "yEnc", "UU" };
|
||||
unsigned int YDecoder::crc_tab[256];
|
||||
|
||||
Decoder::Decoder()
|
||||
{
|
||||
@@ -81,9 +54,6 @@ Decoder::Decoder()
|
||||
m_szSrcFilename = NULL;
|
||||
m_szDestFilename = NULL;
|
||||
m_szArticleFilename = NULL;
|
||||
m_eKind = dcYenc;
|
||||
m_iDebugStatus = 0;
|
||||
m_iDebugLines = 0;
|
||||
}
|
||||
|
||||
Decoder::~ Decoder()
|
||||
@@ -96,227 +66,87 @@ Decoder::~ Decoder()
|
||||
}
|
||||
}
|
||||
|
||||
bool Decoder::Execute()
|
||||
void Decoder::Clear()
|
||||
{
|
||||
if (m_eKind == dcUulib)
|
||||
if (m_szArticleFilename)
|
||||
{
|
||||
return DecodeUulib();
|
||||
}
|
||||
else
|
||||
{
|
||||
return DecodeYenc();
|
||||
free(m_szArticleFilename);
|
||||
}
|
||||
m_szArticleFilename = NULL;
|
||||
}
|
||||
|
||||
bool Decoder::DecodeUulib()
|
||||
Decoder::EFormat Decoder::DetectFormat(const char* buffer, int len)
|
||||
{
|
||||
bool res = false;
|
||||
|
||||
#ifndef ENABLE_UULIB
|
||||
error("Program was compiled without option ENABLE_UULIB defined. uulib-Decoder is not available.");
|
||||
#else
|
||||
|
||||
m_mutexDecoder.Lock();
|
||||
|
||||
#ifdef DEBUGDECODER
|
||||
debug("Decoding ID %i (%s)", g_iDecoderID, szSrcFilename);
|
||||
#endif
|
||||
|
||||
#ifndef USEEXTERNALDECODER
|
||||
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++)
|
||||
if (!strncmp(buffer, "=ybegin ", 8))
|
||||
{
|
||||
uulist* att_tmp = UUGetFileListItem(i);
|
||||
return efYenc;
|
||||
}
|
||||
|
||||
if ((len == 62 || len == 63) && (buffer[62] == '\n' || buffer[62] == '\r') && *buffer == 'M')
|
||||
{
|
||||
return efUx;
|
||||
}
|
||||
|
||||
if (!att_tmp)
|
||||
if (!strncmp(buffer, "begin ", 6))
|
||||
{
|
||||
bool bOK = true;
|
||||
buffer += 6; //strlen("begin ")
|
||||
while (*buffer && *buffer != ' ')
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if ((att_tmp) && (att_tmp->haveparts))
|
||||
{
|
||||
if (!attachment)
|
||||
char ch = *buffer++;
|
||||
if (ch < '0' || ch > '7')
|
||||
{
|
||||
attachment = att_tmp;
|
||||
}
|
||||
else
|
||||
{
|
||||
//f**k, multiple attachments!? Can't handle this.
|
||||
attachment = NULL;
|
||||
bOK = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (attachment)
|
||||
{
|
||||
// okay, we got only one attachment, perfect!
|
||||
if ((attachment->haveparts) && (attachment->haveparts[0])) // && (!attachment->haveparts[1])) FUCK UULIB
|
||||
if (bOK)
|
||||
{
|
||||
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");
|
||||
return efUx;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
error("[ERROR] Wrong number of attachments!\n");
|
||||
}
|
||||
|
||||
UUCleanUp();
|
||||
#else
|
||||
execl("/usr/local/bin", "uudeview", szSrcFilename, szDestFilename);
|
||||
#endif
|
||||
|
||||
#ifdef DEBUGDECODER
|
||||
debug("Finished decoding ID %i (%s)", g_iDecoderID++, szDestFilename);
|
||||
#endif
|
||||
|
||||
m_mutexDecoder.Unlock();
|
||||
|
||||
#endif // ENABLE_UULIB
|
||||
|
||||
return res;
|
||||
return efUnknown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Very primitive (but fast) implementation of yEnc-Decoder
|
||||
* YDecoder: fast implementation of yEnc-Decoder
|
||||
*/
|
||||
bool Decoder::DecodeYenc()
|
||||
|
||||
void YDecoder::Init()
|
||||
{
|
||||
FILE* infile = fopen(m_szSrcFilename, "r");
|
||||
if (!infile)
|
||||
{
|
||||
error("Could not open file \"%s\"", m_szSrcFilename);
|
||||
return false;
|
||||
}
|
||||
debug("Initializing global decoder");
|
||||
crc32gentab();
|
||||
}
|
||||
|
||||
FILE* outfile = fopen(m_szDestFilename, "w");
|
||||
if (!outfile)
|
||||
{
|
||||
error("Could not create file \"%s\"", m_szDestFilename);
|
||||
fclose(infile);
|
||||
return false;
|
||||
}
|
||||
void YDecoder::Final()
|
||||
{
|
||||
debug("Finalizing global Decoder");
|
||||
}
|
||||
|
||||
static const int MAX_LINE_LEN = 1024;
|
||||
char buffer[MAX_LINE_LEN];
|
||||
bool body = false;
|
||||
bool end = false;
|
||||
unsigned long expectedCRC = 0;
|
||||
unsigned long calculatedCRC = 0xFFFFFFFF;
|
||||
m_iDebugStatus = 1;
|
||||
bool eof = !fgets(buffer, sizeof(buffer), infile);
|
||||
m_iDebugLines++;
|
||||
m_iDebugStatus = 2;
|
||||
while (!eof)
|
||||
{
|
||||
if (body)
|
||||
{
|
||||
if (strstr(buffer, "=yend size="))
|
||||
{
|
||||
end = true;
|
||||
m_iDebugStatus = 3;
|
||||
char* pc = strstr(buffer, "pcrc32=");
|
||||
if (pc)
|
||||
{
|
||||
pc += 7; //=strlen("pcrc32=")
|
||||
expectedCRC = strtoul(pc, NULL, 16);
|
||||
}
|
||||
break;
|
||||
}
|
||||
m_iDebugStatus = 4;
|
||||
char* iptr = buffer;
|
||||
char* optr = buffer;
|
||||
while (*iptr)
|
||||
{
|
||||
switch (*iptr)
|
||||
{
|
||||
case '=': //escape-sequence
|
||||
iptr++;
|
||||
*optr = *iptr - 64 - 42;
|
||||
*optr++;
|
||||
break;
|
||||
case '\n': // ignored char
|
||||
case '\r': // ignored char
|
||||
break;
|
||||
default: // normal char
|
||||
*optr = *iptr - 42;
|
||||
*optr++;
|
||||
break;
|
||||
}
|
||||
iptr++;
|
||||
}
|
||||
m_iDebugStatus = 5;
|
||||
calculatedCRC = crc32m(calculatedCRC, (unsigned char *)buffer, optr - buffer);
|
||||
fwrite(buffer, 1, optr - buffer, outfile);
|
||||
m_iDebugStatus = 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (strstr(buffer, "=ypart begin="))
|
||||
{
|
||||
m_iDebugStatus = 7;
|
||||
body = true;
|
||||
}
|
||||
else if (strstr(buffer, "=ybegin part="))
|
||||
{
|
||||
m_iDebugStatus = 8;
|
||||
char* pb = strstr(buffer, "name=");
|
||||
if (pb)
|
||||
{
|
||||
m_iDebugStatus = 9;
|
||||
pb += 5; //=strlen("name=")
|
||||
char* pe;
|
||||
for (pe = pb; *pe != '\0' && *pe != '\n' && *pe != '\r'; pe++) ;
|
||||
m_szArticleFilename = (char*)malloc(pe - pb + 1);
|
||||
strncpy(m_szArticleFilename, pb, pe - pb);
|
||||
m_szArticleFilename[pe - pb] = '\0';
|
||||
m_iDebugStatus = 10;
|
||||
}
|
||||
m_iDebugStatus = 11;
|
||||
}
|
||||
}
|
||||
m_iDebugStatus = 12;
|
||||
eof = !fgets(buffer, sizeof(buffer), infile);
|
||||
m_iDebugStatus = 13;
|
||||
m_iDebugLines++;
|
||||
}
|
||||
m_iDebugStatus = 14;
|
||||
YDecoder::YDecoder()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
calculatedCRC ^= 0xFFFFFFFF;
|
||||
void YDecoder::Clear()
|
||||
{
|
||||
Decoder::Clear();
|
||||
|
||||
debug("Expected pcrc32=%x", expectedCRC);
|
||||
debug("Calculated pcrc32=%x", calculatedCRC);
|
||||
if (expectedCRC != calculatedCRC)
|
||||
{
|
||||
warn("CRC-Error for \"%s\"", m_szDestFilename);
|
||||
}
|
||||
|
||||
fclose(infile);
|
||||
fclose(outfile);
|
||||
|
||||
return body && end;
|
||||
m_bBody = false;
|
||||
m_bBegin = false;
|
||||
m_bPart = false;
|
||||
m_bEnd = false;
|
||||
m_bCrc = false;
|
||||
m_lExpectedCRC = 0;
|
||||
m_lCalculatedCRC = 0xFFFFFFFF;
|
||||
m_iBegin = 0;
|
||||
m_iEnd = 0xFFFFFFFF;
|
||||
m_iSize = 0;
|
||||
m_iEndSize = 0;
|
||||
m_bAutoSeek = false;
|
||||
m_bNeedSetPos = false;
|
||||
m_bCrcCheck = false;
|
||||
}
|
||||
|
||||
/* from crc32.c (http://www.koders.com/c/fid699AFE0A656F0022C9D6B9D1743E697B69CE5815.aspx)
|
||||
@@ -329,7 +159,7 @@ bool Decoder::DecodeYenc()
|
||||
* calculate the crcTable for crc32-checksums.
|
||||
* it is generated to the polynom [..]
|
||||
*/
|
||||
void Decoder::crc32gentab()
|
||||
void YDecoder::crc32gentab()
|
||||
{
|
||||
unsigned long crc, poly;
|
||||
int i, j;
|
||||
@@ -363,21 +193,293 @@ void Decoder::crc32gentab()
|
||||
* reached. the crc32-checksum will be
|
||||
* the result.
|
||||
*/
|
||||
unsigned long Decoder::crc32m(unsigned long startCrc, unsigned char *block, unsigned int length)
|
||||
unsigned long YDecoder::crc32m(unsigned long startCrc, unsigned char *block, unsigned int length)
|
||||
{
|
||||
register unsigned long crc;
|
||||
unsigned long i;
|
||||
|
||||
crc = startCrc;
|
||||
for (i = 0; i < length; i++)
|
||||
register unsigned long crc = startCrc;
|
||||
for (unsigned long i = 0; i < length; i++)
|
||||
{
|
||||
crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_tab[(crc ^ *block++) & 0xFF];
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
void Decoder::LogDebugInfo()
|
||||
unsigned int YDecoder::DecodeBuffer(char* buffer)
|
||||
{
|
||||
debug(" Decoder: status=%i, lines=%i, filename=%s, ArticleFileName=%s",
|
||||
m_iDebugStatus, m_iDebugLines, BaseFileName(m_szSrcFilename), m_szArticleFilename);
|
||||
if (m_bBody && !m_bEnd)
|
||||
{
|
||||
if (!strncmp(buffer, "=yend ", 6))
|
||||
{
|
||||
m_bEnd = true;
|
||||
char* pb = strstr(buffer, m_bPart ? " pcrc32=" : " crc32=");
|
||||
if (pb)
|
||||
{
|
||||
m_bCrc = true;
|
||||
pb += 7 + (int)m_bPart; //=strlen(" crc32=") or strlen(" pcrc32=")
|
||||
m_lExpectedCRC = strtoul(pb, NULL, 16);
|
||||
}
|
||||
pb = strstr(buffer, " size=");
|
||||
if (pb)
|
||||
{
|
||||
pb += 6; //=strlen(" size=")
|
||||
m_iEndSize = (int)atoi(pb);
|
||||
}
|
||||
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, (unsigned int)(optr - buffer));
|
||||
}
|
||||
return (unsigned int)(optr - buffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!m_bPart && !strncmp(buffer, "=ybegin ", 8))
|
||||
{
|
||||
m_bBegin = true;
|
||||
char* pb = strstr(buffer, " name=");
|
||||
if (pb)
|
||||
{
|
||||
pb += 6; //=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';
|
||||
}
|
||||
pb = strstr(buffer, " size=");
|
||||
if (pb)
|
||||
{
|
||||
pb += 6; //=strlen(" size=")
|
||||
m_iSize = (int)atoi(pb);
|
||||
}
|
||||
m_bPart = strstr(buffer, " part=");
|
||||
if (!m_bPart)
|
||||
{
|
||||
m_bBody = true;
|
||||
m_iBegin = 1;
|
||||
m_iEnd = m_iSize;
|
||||
}
|
||||
}
|
||||
else if (m_bPart && !strncmp(buffer, "=ypart ", 7))
|
||||
{
|
||||
m_bPart = true;
|
||||
m_bBody = true;
|
||||
char* pb = strstr(buffer, " begin=");
|
||||
if (pb)
|
||||
{
|
||||
pb += 7; //=strlen(" begin=")
|
||||
m_iBegin = (int)atoi(pb);
|
||||
}
|
||||
pb = strstr(buffer, " end=");
|
||||
if (pb)
|
||||
{
|
||||
pb += 5; //=strlen(" end=")
|
||||
m_iEnd = (int)atoi(pb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool YDecoder::Write(char* buffer, int len, FILE* outfile)
|
||||
{
|
||||
unsigned int wcnt = DecodeBuffer(buffer);
|
||||
if (wcnt > 0)
|
||||
{
|
||||
if (m_bNeedSetPos)
|
||||
{
|
||||
if (m_iBegin == 0 || m_iEnd == 0xFFFFFFFF || !outfile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (fseek(outfile, m_iBegin - 1, SEEK_SET))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
m_bNeedSetPos = false;
|
||||
}
|
||||
fwrite(buffer, 1, wcnt, outfile);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Decoder::EStatus YDecoder::Check()
|
||||
{
|
||||
m_lCalculatedCRC ^= 0xFFFFFFFF;
|
||||
|
||||
debug("Expected crc32=%x", m_lExpectedCRC);
|
||||
debug("Calculated crc32=%x", m_lCalculatedCRC);
|
||||
|
||||
if (!m_bBegin)
|
||||
{
|
||||
return eNoBinaryData;
|
||||
}
|
||||
else if (!m_bEnd)
|
||||
{
|
||||
return eArticleIncomplete;
|
||||
}
|
||||
else if (!m_bPart && m_iSize != m_iEndSize)
|
||||
{
|
||||
return eInvalidSize;
|
||||
}
|
||||
else if (m_bCrcCheck && m_bCrc && (m_lExpectedCRC != m_lCalculatedCRC))
|
||||
{
|
||||
return eCrcError;
|
||||
}
|
||||
|
||||
return eFinished;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* UDecoder: supports UU encoding formats
|
||||
*/
|
||||
|
||||
UDecoder::UDecoder()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UDecoder::Clear()
|
||||
{
|
||||
Decoder::Clear();
|
||||
|
||||
m_bBody = false;
|
||||
m_bEnd = false;
|
||||
}
|
||||
|
||||
/* DecodeBuffer-function uses portions of code from tool UUDECODE by Clem Dye
|
||||
* UUDECODE.c (http://www.bastet.com/uue.zip)
|
||||
* Copyright (C) 1998 Clem Dye
|
||||
*
|
||||
* Released under GPL (thanks)
|
||||
*/
|
||||
|
||||
#define UU_DECODE_CHAR(c) (c == '`' ? 0 : (((c) - ' ') & 077))
|
||||
|
||||
unsigned int UDecoder::DecodeBuffer(char* buffer, int len)
|
||||
{
|
||||
if (!m_bBody)
|
||||
{
|
||||
if (!strncmp(buffer, "begin ", 6))
|
||||
{
|
||||
char* pb = buffer;
|
||||
pb += 6; //strlen("begin ")
|
||||
|
||||
// skip file-permissions
|
||||
for (; *pb != ' ' && *pb != '\0' && *pb != '\n' && *pb != '\r'; pb++) ;
|
||||
pb++;
|
||||
|
||||
// extracting filename
|
||||
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';
|
||||
|
||||
m_bBody = true;
|
||||
return 0;
|
||||
}
|
||||
else if ((len == 62 || len == 63) && (buffer[62] == '\n' || buffer[62] == '\r') && *buffer == 'M')
|
||||
{
|
||||
m_bBody = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_bBody && (!strncmp(buffer, "end ", 4) || *buffer == '`'))
|
||||
{
|
||||
m_bEnd = true;
|
||||
}
|
||||
|
||||
if (m_bBody && !m_bEnd)
|
||||
{
|
||||
int iEffLen = UU_DECODE_CHAR(buffer[0]);
|
||||
if (iEffLen > len)
|
||||
{
|
||||
// error;
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* iptr = buffer;
|
||||
char* optr = buffer;
|
||||
for (++iptr; iEffLen > 0; iptr += 4, iEffLen -= 3)
|
||||
{
|
||||
if (iEffLen >= 3)
|
||||
{
|
||||
*optr++ = UU_DECODE_CHAR (iptr[0]) << 2 | UU_DECODE_CHAR (iptr[1]) >> 4;
|
||||
*optr++ = UU_DECODE_CHAR (iptr[1]) << 4 | UU_DECODE_CHAR (iptr[2]) >> 2;
|
||||
*optr++ = UU_DECODE_CHAR (iptr[2]) << 6 | UU_DECODE_CHAR (iptr[3]);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (iEffLen >= 1)
|
||||
{
|
||||
*optr++ = UU_DECODE_CHAR (iptr[0]) << 2 | UU_DECODE_CHAR (iptr[1]) >> 4;
|
||||
}
|
||||
if (iEffLen >= 2)
|
||||
{
|
||||
*optr++ = UU_DECODE_CHAR (iptr[1]) << 4 | UU_DECODE_CHAR (iptr[2]) >> 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return (unsigned int)(optr - buffer);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool UDecoder::Write(char* buffer, int len, FILE* outfile)
|
||||
{
|
||||
unsigned int wcnt = DecodeBuffer(buffer, len);
|
||||
if (wcnt > 0)
|
||||
{
|
||||
fwrite(buffer, 1, wcnt, outfile);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Decoder::EStatus UDecoder::Check()
|
||||
{
|
||||
if (!m_bBody)
|
||||
{
|
||||
return eNoBinaryData;
|
||||
}
|
||||
|
||||
return eFinished;
|
||||
}
|
||||
|
||||
115
Decoder.h
115
Decoder.h
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2008 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -27,46 +27,93 @@
|
||||
#ifndef DECODER_H
|
||||
#define DECODER_H
|
||||
|
||||
#include "Thread.h"
|
||||
|
||||
//#define DECODER_INTERNAL_FGETS
|
||||
|
||||
class Decoder
|
||||
{
|
||||
public:
|
||||
enum EKind
|
||||
enum EStatus
|
||||
{
|
||||
dcUulib,
|
||||
dcYenc
|
||||
eUnknownError,
|
||||
eFinished,
|
||||
eArticleIncomplete,
|
||||
eCrcError,
|
||||
eInvalidSize,
|
||||
eNoBinaryData
|
||||
};
|
||||
|
||||
private:
|
||||
static Mutex m_mutexDecoder;
|
||||
static unsigned int crc_tab[256];
|
||||
EKind m_eKind;
|
||||
const char* m_szSrcFilename;
|
||||
const char* m_szDestFilename;
|
||||
char* m_szArticleFilename;
|
||||
int m_iDebugStatus;
|
||||
int m_iDebugLines;
|
||||
enum EFormat
|
||||
{
|
||||
efUnknown,
|
||||
efYenc,
|
||||
efUx,
|
||||
};
|
||||
|
||||
bool DecodeUulib();
|
||||
bool DecodeYenc();
|
||||
static void crc32gentab();
|
||||
unsigned long crc32m(unsigned long startCrc, unsigned char *block, unsigned int length);
|
||||
static const char* FormatNames[];
|
||||
|
||||
protected:
|
||||
const char* m_szSrcFilename;
|
||||
const char* m_szDestFilename;
|
||||
char* m_szArticleFilename;
|
||||
|
||||
public:
|
||||
Decoder();
|
||||
~Decoder();
|
||||
bool Execute();
|
||||
void SetKind(EKind eKind) { m_eKind = eKind; }
|
||||
void SetSrcFilename(const char* szSrcFilename) { m_szSrcFilename = szSrcFilename; }
|
||||
void SetDestFilename(const char* szDestFilename) { m_szDestFilename = szDestFilename; }
|
||||
const char* GetArticleFilename() { return m_szArticleFilename; }
|
||||
void LogDebugInfo();
|
||||
Decoder();
|
||||
virtual ~Decoder();
|
||||
virtual EStatus Check() = 0;
|
||||
virtual void Clear();
|
||||
virtual bool Write(char* buffer, int len, FILE* outfile) = 0;
|
||||
void SetSrcFilename(const char* szSrcFilename) { m_szSrcFilename = szSrcFilename; }
|
||||
void SetDestFilename(const char* szDestFilename) { m_szDestFilename = szDestFilename; }
|
||||
const char* GetArticleFilename() { return m_szArticleFilename; }
|
||||
static EFormat DetectFormat(const char* buffer, int len);
|
||||
};
|
||||
|
||||
static void Init();
|
||||
static void Final();
|
||||
class YDecoder: public Decoder
|
||||
{
|
||||
protected:
|
||||
static unsigned int crc_tab[256];
|
||||
bool m_bBegin;
|
||||
bool m_bPart;
|
||||
bool m_bBody;
|
||||
bool m_bEnd;
|
||||
bool m_bCrc;
|
||||
unsigned long m_lExpectedCRC;
|
||||
unsigned long m_lCalculatedCRC;
|
||||
unsigned long m_iBegin;
|
||||
unsigned long m_iEnd;
|
||||
unsigned long m_iSize;
|
||||
unsigned long m_iEndSize;
|
||||
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 EStatus Check();
|
||||
virtual void Clear();
|
||||
virtual bool Write(char* buffer, int len, FILE* outfile);
|
||||
void SetAutoSeek(bool bAutoSeek) { m_bAutoSeek = m_bNeedSetPos = bAutoSeek; }
|
||||
void SetCrcCheck(bool bCrcCheck) { m_bCrcCheck = bCrcCheck; }
|
||||
|
||||
static void Init();
|
||||
static void Final();
|
||||
};
|
||||
|
||||
class UDecoder: public Decoder
|
||||
{
|
||||
private:
|
||||
bool m_bBody;
|
||||
bool m_bEnd;
|
||||
|
||||
unsigned int DecodeBuffer(char* buffer, int len);
|
||||
|
||||
public:
|
||||
UDecoder();
|
||||
virtual EStatus Check();
|
||||
virtual void Clear();
|
||||
virtual bool Write(char* buffer, int len, FILE* outfile);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
1021
DiskState.cpp
1021
DiskState.cpp
File diff suppressed because it is too large
Load Diff
38
DiskState.h
38
DiskState.h
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,7 +15,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -31,15 +31,33 @@
|
||||
class DiskState
|
||||
{
|
||||
private:
|
||||
int ParseFormatVersion(const char* szFormatSignature);
|
||||
bool SaveFileInfo(FileInfo* pFileInfo, const char* szFilename);
|
||||
bool LoadFileInfo(FileInfo* pFileInfo, const char* szFilename);
|
||||
|
||||
bool LoadFileInfo(FileInfo* pFileInfo, const char* szFilename, bool bFileSummary, bool bArticles);
|
||||
void SaveNZBList(DownloadQueue* pDownloadQueue, FILE* outfile);
|
||||
bool LoadNZBList(DownloadQueue* pDownloadQueue, FILE* infile, int iFormatVersion);
|
||||
void SaveFileQueue(DownloadQueue* pDownloadQueue, FileQueue* pFileQueue, FILE* outfile);
|
||||
bool LoadFileQueue(DownloadQueue* pDownloadQueue, FileQueue* pFileQueue, FILE* infile, int iFormatVersion);
|
||||
void SavePostQueue(DownloadQueue* pDownloadQueue, FILE* outfile);
|
||||
bool LoadPostQueue(DownloadQueue* pDownloadQueue, FILE* infile);
|
||||
bool LoadOldPostQueue(DownloadQueue* pDownloadQueue);
|
||||
void SaveUrlQueue(DownloadQueue* pDownloadQueue, FILE* outfile);
|
||||
bool LoadUrlQueue(DownloadQueue* pDownloadQueue, FILE* infile, int iFormatVersion);
|
||||
void SaveUrlInfo(UrlInfo* pUrlInfo, FILE* outfile);
|
||||
bool LoadUrlInfo(UrlInfo* pUrlInfo, FILE* infile, int iFormatVersion);
|
||||
void SaveHistory(DownloadQueue* pDownloadQueue, FILE* outfile);
|
||||
bool LoadHistory(DownloadQueue* pDownloadQueue, FILE* infile, int iFormatVersion);
|
||||
int FindNZBInfoIndex(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
|
||||
|
||||
public:
|
||||
bool Exists();
|
||||
bool Save(DownloadQueue* pDownloadQueue, bool OnlyOrder);
|
||||
bool Load(DownloadQueue* pDownloadQueue);
|
||||
bool Discard();
|
||||
bool DiscardFileInfo(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
|
||||
bool DownloadQueueExists();
|
||||
bool PostQueueExists(bool bCompleted);
|
||||
bool SaveDownloadQueue(DownloadQueue* pDownloadQueue);
|
||||
bool LoadDownloadQueue(DownloadQueue* pDownloadQueue);
|
||||
bool SaveFile(FileInfo* pFileInfo);
|
||||
bool LoadArticles(FileInfo* pFileInfo);
|
||||
bool DiscardDownloadQueue();
|
||||
bool DiscardFile(FileInfo* pFileInfo);
|
||||
void CleanupTempDir(DownloadQueue* pDownloadQueue);
|
||||
};
|
||||
|
||||
|
||||
873
DownloadInfo.cpp
873
DownloadInfo.cpp
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -34,10 +34,10 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <sys/stat.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "DownloadInfo.h"
|
||||
@@ -48,6 +48,351 @@
|
||||
extern Options* g_pOptions;
|
||||
|
||||
int FileInfo::m_iIDGen = 0;
|
||||
int NZBInfo::m_iIDGen = 0;
|
||||
int PostInfo::m_iIDGen = 0;
|
||||
int UrlInfo::m_iIDGen = 0;
|
||||
int HistoryInfo::m_iIDGen = 0;
|
||||
|
||||
NZBParameter::NZBParameter(const char* szName)
|
||||
{
|
||||
m_szName = strdup(szName);
|
||||
m_szValue = NULL;
|
||||
}
|
||||
|
||||
NZBParameter::~NZBParameter()
|
||||
{
|
||||
if (m_szName)
|
||||
{
|
||||
free(m_szName);
|
||||
}
|
||||
if (m_szValue)
|
||||
{
|
||||
free(m_szValue);
|
||||
}
|
||||
}
|
||||
|
||||
void NZBParameter::SetValue(const char* szValue)
|
||||
{
|
||||
if (m_szValue)
|
||||
{
|
||||
free(m_szValue);
|
||||
}
|
||||
m_szValue = strdup(szValue);
|
||||
}
|
||||
|
||||
|
||||
void NZBParameterList::SetParameter(const char* szName, const char* szValue)
|
||||
{
|
||||
NZBParameter* pParameter = NULL;
|
||||
bool bDelete = !szValue || !*szValue;
|
||||
|
||||
for (iterator it = begin(); it != end(); it++)
|
||||
{
|
||||
NZBParameter* pLookupParameter = *it;
|
||||
if (!strcmp(pLookupParameter->GetName(), szName))
|
||||
{
|
||||
if (bDelete)
|
||||
{
|
||||
delete pLookupParameter;
|
||||
erase(it);
|
||||
return;
|
||||
}
|
||||
pParameter = pLookupParameter;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bDelete)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!pParameter)
|
||||
{
|
||||
pParameter = new NZBParameter(szName);
|
||||
push_back(pParameter);
|
||||
}
|
||||
|
||||
pParameter->SetValue(szValue);
|
||||
}
|
||||
|
||||
|
||||
NZBInfo::NZBInfo()
|
||||
{
|
||||
debug("Creating NZBInfo");
|
||||
|
||||
m_szFilename = NULL;
|
||||
m_szDestDir = NULL;
|
||||
m_szCategory = strdup("");
|
||||
m_szName = NULL;
|
||||
m_iFileCount = 0;
|
||||
m_iParkedFileCount = 0;
|
||||
m_lSize = 0;
|
||||
m_iRefCount = 0;
|
||||
m_bPostProcess = false;
|
||||
m_eParStatus = prNone;
|
||||
m_eScriptStatus = srNone;
|
||||
m_bDeleted = false;
|
||||
m_bParCleanup = false;
|
||||
m_bCleanupDisk = false;
|
||||
m_szQueuedFilename = strdup("");
|
||||
m_Owner = NULL;
|
||||
m_Messages.clear();
|
||||
m_iIDMessageGen = 0;
|
||||
m_iIDGen++;
|
||||
m_iID = m_iIDGen;
|
||||
}
|
||||
|
||||
NZBInfo::~NZBInfo()
|
||||
{
|
||||
debug("Destroying NZBInfo");
|
||||
|
||||
if (m_szFilename)
|
||||
{
|
||||
free(m_szFilename);
|
||||
}
|
||||
if (m_szDestDir)
|
||||
{
|
||||
free(m_szDestDir);
|
||||
}
|
||||
if (m_szCategory)
|
||||
{
|
||||
free(m_szCategory);
|
||||
}
|
||||
if (m_szName)
|
||||
{
|
||||
free(m_szName);
|
||||
}
|
||||
if (m_szQueuedFilename)
|
||||
{
|
||||
free(m_szQueuedFilename);
|
||||
}
|
||||
|
||||
ClearCompletedFiles();
|
||||
|
||||
for (NZBParameterList::iterator it = m_ppParameters.begin(); it != m_ppParameters.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_ppParameters.clear();
|
||||
|
||||
for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Messages.clear();
|
||||
|
||||
if (m_Owner)
|
||||
{
|
||||
m_Owner->Remove(this);
|
||||
}
|
||||
}
|
||||
|
||||
void NZBInfo::AddReference()
|
||||
{
|
||||
m_iRefCount++;
|
||||
}
|
||||
|
||||
void NZBInfo::Release()
|
||||
{
|
||||
m_iRefCount--;
|
||||
if (m_iRefCount <= 0)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
void NZBInfo::ClearCompletedFiles()
|
||||
{
|
||||
for (Files::iterator it = m_completedFiles.begin(); it != m_completedFiles.end(); it++)
|
||||
{
|
||||
free(*it);
|
||||
}
|
||||
m_completedFiles.clear();
|
||||
}
|
||||
|
||||
void NZBInfo::SetDestDir(const char* szDestDir)
|
||||
{
|
||||
if (m_szDestDir)
|
||||
{
|
||||
free(m_szDestDir);
|
||||
}
|
||||
m_szDestDir = strdup(szDestDir);
|
||||
}
|
||||
|
||||
void NZBInfo::SetFilename(const char * szFilename)
|
||||
{
|
||||
if (m_szFilename)
|
||||
{
|
||||
free(m_szFilename);
|
||||
}
|
||||
m_szFilename = strdup(szFilename);
|
||||
|
||||
if (!m_szName)
|
||||
{
|
||||
char szNZBNicename[1024];
|
||||
MakeNiceNZBName(m_szFilename, szNZBNicename, sizeof(szNZBNicename), true);
|
||||
szNZBNicename[1024-1] = '\0';
|
||||
SetName(szNZBNicename);
|
||||
}
|
||||
}
|
||||
|
||||
void NZBInfo::SetName(const char* szName)
|
||||
{
|
||||
if (m_szName)
|
||||
{
|
||||
free(m_szName);
|
||||
}
|
||||
m_szName = szName ? strdup(szName) : NULL;
|
||||
}
|
||||
|
||||
void NZBInfo::SetCategory(const char* szCategory)
|
||||
{
|
||||
if (m_szCategory)
|
||||
{
|
||||
free(m_szCategory);
|
||||
}
|
||||
m_szCategory = strdup(szCategory);
|
||||
}
|
||||
|
||||
void NZBInfo::SetQueuedFilename(const char * szQueuedFilename)
|
||||
{
|
||||
if (m_szQueuedFilename)
|
||||
{
|
||||
free(m_szQueuedFilename);
|
||||
}
|
||||
m_szQueuedFilename = strdup(szQueuedFilename);
|
||||
}
|
||||
|
||||
void NZBInfo::MakeNiceNZBName(const char * szNZBFilename, char * szBuffer, int iSize, bool bRemoveExt)
|
||||
{
|
||||
char postname[1024];
|
||||
const char* szBaseName = Util::BaseFileName(szNZBFilename);
|
||||
|
||||
strncpy(postname, szBaseName, 1024);
|
||||
postname[1024-1] = '\0';
|
||||
|
||||
if (bRemoveExt)
|
||||
{
|
||||
// wipe out ".nzb"
|
||||
char* p = strrchr(postname, '.');
|
||||
if (p && !strcasecmp(p, ".nzb")) *p = '\0';
|
||||
}
|
||||
|
||||
Util::MakeValidFilename(postname, '_', false);
|
||||
|
||||
strncpy(szBuffer, postname, iSize);
|
||||
szBuffer[iSize-1] = '\0';
|
||||
}
|
||||
|
||||
void NZBInfo::BuildDestDirName()
|
||||
{
|
||||
char szBuffer[1024];
|
||||
char szCategory[1024];
|
||||
bool bHasCategory = m_szCategory && m_szCategory[0] != '\0';
|
||||
if (g_pOptions->GetAppendCategoryDir() && bHasCategory)
|
||||
{
|
||||
strncpy(szCategory, m_szCategory, 1024);
|
||||
szCategory[1024 - 1] = '\0';
|
||||
Util::MakeValidFilename(szCategory, '_', true);
|
||||
}
|
||||
|
||||
if (g_pOptions->GetAppendNZBDir())
|
||||
{
|
||||
if (g_pOptions->GetAppendCategoryDir() && bHasCategory)
|
||||
{
|
||||
snprintf(szBuffer, 1024, "%s%s%c%s", g_pOptions->GetDestDir(), szCategory, PATH_SEPARATOR, GetName());
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(szBuffer, 1024, "%s%s", g_pOptions->GetDestDir(), GetName());
|
||||
}
|
||||
szBuffer[1024-1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
if (g_pOptions->GetAppendCategoryDir() && bHasCategory)
|
||||
{
|
||||
snprintf(szBuffer, 1024, "%s%s", g_pOptions->GetDestDir(), szCategory);
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(szBuffer, g_pOptions->GetDestDir(), 1024);
|
||||
}
|
||||
szBuffer[1024-1] = '\0'; // trim the last slash, always returned by GetDestDir()
|
||||
}
|
||||
|
||||
SetDestDir(szBuffer);
|
||||
}
|
||||
|
||||
void NZBInfo::SetParameter(const char* szName, const char* szValue)
|
||||
{
|
||||
m_ppParameters.SetParameter(szName, szValue);
|
||||
}
|
||||
|
||||
NZBInfo::Messages* NZBInfo::LockMessages()
|
||||
{
|
||||
m_mutexLog.Lock();
|
||||
return &m_Messages;
|
||||
}
|
||||
|
||||
void NZBInfo::UnlockMessages()
|
||||
{
|
||||
m_mutexLog.Unlock();
|
||||
}
|
||||
|
||||
void NZBInfo::AppendMessage(Message::EKind eKind, time_t tTime, const char * szText)
|
||||
{
|
||||
if (tTime == 0)
|
||||
{
|
||||
tTime = time(NULL);
|
||||
}
|
||||
|
||||
Message* pMessage = new Message(++m_iIDMessageGen, eKind, tTime, szText);
|
||||
|
||||
m_mutexLog.Lock();
|
||||
m_Messages.push_back(pMessage);
|
||||
m_mutexLog.Unlock();
|
||||
}
|
||||
|
||||
void NZBInfoList::Add(NZBInfo* pNZBInfo)
|
||||
{
|
||||
pNZBInfo->m_Owner = this;
|
||||
push_back(pNZBInfo);
|
||||
}
|
||||
|
||||
void NZBInfoList::Remove(NZBInfo* pNZBInfo)
|
||||
{
|
||||
for (iterator it = begin(); it != end(); it++)
|
||||
{
|
||||
NZBInfo* pNZBInfo2 = *it;
|
||||
if (pNZBInfo2 == pNZBInfo)
|
||||
{
|
||||
erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NZBInfoList::ReleaseAll()
|
||||
{
|
||||
int i = 0;
|
||||
for (iterator it = begin(); it != end(); )
|
||||
{
|
||||
NZBInfo* pNZBInfo = *it;
|
||||
bool bObjDeleted = pNZBInfo->m_iRefCount == 1;
|
||||
pNZBInfo->Release();
|
||||
if (bObjDeleted)
|
||||
{
|
||||
it = begin() + i;
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ArticleInfo::ArticleInfo()
|
||||
{
|
||||
@@ -79,6 +424,10 @@ void ArticleInfo::SetMessageID(const char * szMessageID)
|
||||
|
||||
void ArticleInfo::SetResultFilename(const char * v)
|
||||
{
|
||||
if (m_szResultFilename)
|
||||
{
|
||||
free(m_szResultFilename);
|
||||
}
|
||||
m_szResultFilename = strdup(v);
|
||||
}
|
||||
|
||||
@@ -92,13 +441,16 @@ FileInfo::FileInfo()
|
||||
m_szSubject = NULL;
|
||||
m_szFilename = NULL;
|
||||
m_bFilenameConfirmed = false;
|
||||
m_szDestDir = NULL;
|
||||
m_szNZBFilename = NULL;
|
||||
m_lSize = 0;
|
||||
m_lRemainingSize = 0;
|
||||
m_tTime = 0;
|
||||
m_bPaused = false;
|
||||
m_bDeleted = false;
|
||||
m_iCompleted = 0;
|
||||
m_bOutputInitialized = false;
|
||||
m_pNZBInfo = NULL;
|
||||
m_iPriority = 0;
|
||||
m_iActiveDownloads = 0;
|
||||
m_iIDGen++;
|
||||
m_iID = m_iIDGen;
|
||||
}
|
||||
@@ -115,26 +467,28 @@ FileInfo::~ FileInfo()
|
||||
{
|
||||
free(m_szFilename);
|
||||
}
|
||||
if (m_szDestDir)
|
||||
{
|
||||
free(m_szDestDir);
|
||||
}
|
||||
if (m_szNZBFilename)
|
||||
{
|
||||
free(m_szNZBFilename);
|
||||
}
|
||||
|
||||
for (Articles::iterator it = m_Articles.begin(); it != m_Articles.end() ;it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Articles.clear();
|
||||
|
||||
for (Groups::iterator it = m_Groups.begin(); it != m_Groups.end() ;it++)
|
||||
{
|
||||
free(*it);
|
||||
}
|
||||
m_Groups.clear();
|
||||
|
||||
ClearArticles();
|
||||
|
||||
if (m_pNZBInfo)
|
||||
{
|
||||
m_pNZBInfo->Release();
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -146,67 +500,21 @@ void FileInfo::SetID(int s)
|
||||
}
|
||||
}
|
||||
|
||||
void FileInfo::SetNZBInfo(NZBInfo* pNZBInfo)
|
||||
{
|
||||
if (m_pNZBInfo)
|
||||
{
|
||||
m_pNZBInfo->Release();
|
||||
}
|
||||
m_pNZBInfo = pNZBInfo;
|
||||
m_pNZBInfo->AddReference();
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
strncpy(szBuffer, BaseFileName(szNZBFilename), iSize);
|
||||
szBuffer[iSize-1] = '\0';
|
||||
if (char* p = strrchr(szBuffer, '.')) *p = '\0';
|
||||
}
|
||||
|
||||
void FileInfo::ParseSubject()
|
||||
{
|
||||
char* fnstart = strstr(m_szSubject, "\"");
|
||||
char* fnend = NULL;
|
||||
if (fnstart)
|
||||
{
|
||||
fnstart++;
|
||||
fnend = strstr(fnstart, "\"");
|
||||
}
|
||||
if (fnend)
|
||||
{
|
||||
char fn[1024];
|
||||
strncpy(fn, fnstart, fnend - fnstart);
|
||||
fn[fnend - fnstart] = '\0';
|
||||
m_szFilename = strdup(fn);
|
||||
}
|
||||
else
|
||||
{
|
||||
debug("Could not extract Filename from Subject: %s. Using Subject as Filename", m_szSubject);
|
||||
m_szFilename = strdup(m_szSubject);
|
||||
}
|
||||
|
||||
//replace bad chars in filename
|
||||
char* p = m_szFilename;
|
||||
while (*p)
|
||||
{
|
||||
if (strchr("\\/:*?\"><'\n\r\t", *p))
|
||||
{
|
||||
*p = '_';
|
||||
}
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
void FileInfo::SetFilename(const char* szFilename)
|
||||
{
|
||||
if (m_szFilename)
|
||||
@@ -216,57 +524,388 @@ void FileInfo::SetFilename(const char* szFilename)
|
||||
m_szFilename = strdup(szFilename);
|
||||
}
|
||||
|
||||
void FileInfo::BuildDestDirName(const char* szNZBFilename)
|
||||
void FileInfo::MakeValidFilename()
|
||||
{
|
||||
char szBuffer[1024];
|
||||
Util::MakeValidFilename(m_szFilename, '_', false);
|
||||
}
|
||||
|
||||
if (g_pOptions->GetAppendNZBDir())
|
||||
void FileInfo::LockOutputFile()
|
||||
{
|
||||
m_mutexOutputFile.Lock();
|
||||
}
|
||||
|
||||
void FileInfo::UnlockOutputFile()
|
||||
{
|
||||
m_mutexOutputFile.Unlock();
|
||||
}
|
||||
|
||||
bool FileInfo::IsDupe(const char* szFilename)
|
||||
{
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%c%s", m_pNZBInfo->GetDestDir(), (int)PATH_SEPARATOR, szFilename);
|
||||
fileName[1024-1] = '\0';
|
||||
if (Util::FileExists(fileName))
|
||||
{
|
||||
char postname[1024];
|
||||
const char* szBaseName = BaseFileName(szNZBFilename);
|
||||
return true;
|
||||
}
|
||||
snprintf(fileName, 1024, "%s%c%s_broken", m_pNZBInfo->GetDestDir(), (int)PATH_SEPARATOR, szFilename);
|
||||
fileName[1024-1] = '\0';
|
||||
if (Util::FileExists(fileName))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// if .nzb file has a certain structure, try to strip out certain elements
|
||||
if (sscanf(szBaseName, "msgid_%*d_%1023s", postname) == 1)
|
||||
return false;
|
||||
}
|
||||
|
||||
GroupInfo::GroupInfo()
|
||||
{
|
||||
m_iFirstID = 0;
|
||||
m_iLastID = 0;
|
||||
m_iRemainingFileCount = 0;
|
||||
m_iPausedFileCount = 0;
|
||||
m_lRemainingSize = 0;
|
||||
m_lPausedSize = 0;
|
||||
m_iRemainingParCount = 0;
|
||||
m_tMinTime = 0;
|
||||
m_tMaxTime = 0;
|
||||
m_iMinPriority = 0;
|
||||
m_iMaxPriority = 0;
|
||||
m_iActiveDownloads = 0;
|
||||
}
|
||||
|
||||
GroupInfo::~GroupInfo()
|
||||
{
|
||||
if (m_pNZBInfo)
|
||||
{
|
||||
m_pNZBInfo->Release();
|
||||
}
|
||||
}
|
||||
|
||||
PostInfo::PostInfo()
|
||||
{
|
||||
debug("Creating PostInfo");
|
||||
|
||||
m_pNZBInfo = NULL;
|
||||
m_szParFilename = NULL;
|
||||
m_szInfoName = NULL;
|
||||
m_bWorking = false;
|
||||
m_bDeleted = false;
|
||||
m_bParCheck = false;
|
||||
m_eParStatus = psNone;
|
||||
m_eRequestParCheck = rpNone;
|
||||
m_eScriptStatus = srNone;
|
||||
m_szProgressLabel = strdup("");
|
||||
m_iFileProgress = 0;
|
||||
m_iStageProgress = 0;
|
||||
m_tStartTime = 0;
|
||||
m_tStageTime = 0;
|
||||
m_eStage = ptQueued;
|
||||
m_pScriptThread = NULL;
|
||||
m_Messages.clear();
|
||||
m_iIDMessageGen = 0;
|
||||
m_iIDGen++;
|
||||
m_iID = m_iIDGen;
|
||||
}
|
||||
|
||||
PostInfo::~ PostInfo()
|
||||
{
|
||||
debug("Destroying PostInfo");
|
||||
|
||||
if (m_szParFilename)
|
||||
{
|
||||
free(m_szParFilename);
|
||||
}
|
||||
if (m_szInfoName)
|
||||
{
|
||||
free(m_szInfoName);
|
||||
}
|
||||
if (m_szProgressLabel)
|
||||
{
|
||||
free(m_szProgressLabel);
|
||||
}
|
||||
|
||||
for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Messages.clear();
|
||||
|
||||
if (m_pNZBInfo)
|
||||
{
|
||||
m_pNZBInfo->Release();
|
||||
}
|
||||
}
|
||||
|
||||
void PostInfo::SetNZBInfo(NZBInfo* pNZBInfo)
|
||||
{
|
||||
if (m_pNZBInfo)
|
||||
{
|
||||
m_pNZBInfo->Release();
|
||||
}
|
||||
m_pNZBInfo = pNZBInfo;
|
||||
m_pNZBInfo->AddReference();
|
||||
}
|
||||
|
||||
void PostInfo::SetParFilename(const char* szParFilename)
|
||||
{
|
||||
m_szParFilename = strdup(szParFilename);
|
||||
}
|
||||
|
||||
void PostInfo::SetInfoName(const char* szInfoName)
|
||||
{
|
||||
m_szInfoName = strdup(szInfoName);
|
||||
}
|
||||
|
||||
void PostInfo::SetProgressLabel(const char* szProgressLabel)
|
||||
{
|
||||
if (m_szProgressLabel)
|
||||
{
|
||||
free(m_szProgressLabel);
|
||||
}
|
||||
m_szProgressLabel = strdup(szProgressLabel);
|
||||
}
|
||||
|
||||
PostInfo::Messages* PostInfo::LockMessages()
|
||||
{
|
||||
m_mutexLog.Lock();
|
||||
return &m_Messages;
|
||||
}
|
||||
|
||||
void PostInfo::UnlockMessages()
|
||||
{
|
||||
m_mutexLog.Unlock();
|
||||
}
|
||||
|
||||
void PostInfo::AppendMessage(Message::EKind eKind, const char * szText)
|
||||
{
|
||||
Message* pMessage = new Message(++m_iIDMessageGen, eKind, time(NULL), szText);
|
||||
|
||||
m_mutexLog.Lock();
|
||||
m_Messages.push_back(pMessage);
|
||||
|
||||
while (m_Messages.size() > (unsigned int)g_pOptions->GetLogBufferSize())
|
||||
{
|
||||
Message* pMessage = m_Messages.front();
|
||||
delete pMessage;
|
||||
m_Messages.pop_front();
|
||||
}
|
||||
m_mutexLog.Unlock();
|
||||
}
|
||||
|
||||
void DownloadQueue::BuildGroups(GroupQueue* pGroupQueue)
|
||||
{
|
||||
std::map<int, GroupInfo*> groupMap;
|
||||
|
||||
for (FileQueue::iterator it = GetFileQueue()->begin(); it != GetFileQueue()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
GroupInfo *&pGroupInfo = groupMap[pFileInfo->GetNZBInfo()->GetID()];
|
||||
if (!pGroupInfo)
|
||||
{
|
||||
// wipe out certain structure
|
||||
memset(strrchr(postname, '.'), 0, postname + strlen(postname) - strrchr(postname, '.'));
|
||||
pGroupInfo = new GroupInfo();
|
||||
pGroupInfo->m_pNZBInfo = pFileInfo->GetNZBInfo();
|
||||
pGroupInfo->m_pNZBInfo->AddReference();
|
||||
pGroupInfo->m_iFirstID = pFileInfo->GetID();
|
||||
pGroupInfo->m_iLastID = pFileInfo->GetID();
|
||||
pGroupInfo->m_tMinTime = pFileInfo->GetTime();
|
||||
pGroupInfo->m_tMaxTime = pFileInfo->GetTime();
|
||||
pGroupInfo->m_iMinPriority = pFileInfo->GetPriority();
|
||||
pGroupInfo->m_iMaxPriority = pFileInfo->GetPriority();
|
||||
pGroupQueue->push_back(pGroupInfo);
|
||||
}
|
||||
else
|
||||
if (pFileInfo->GetID() < pGroupInfo->GetFirstID())
|
||||
{
|
||||
strncpy(postname, szBaseName, 1024);
|
||||
postname[1024-1] = '\0';
|
||||
pGroupInfo->m_iFirstID = pFileInfo->GetID();
|
||||
}
|
||||
// wipe out ".nzb"
|
||||
memset(strrchr(postname, '.'), 0, postname + strlen(postname) - strrchr(postname, '.'));
|
||||
snprintf(szBuffer, 1024, "%s%s", g_pOptions->GetDestDir(), postname);
|
||||
szBuffer[1024-1] = '\0';
|
||||
if (pFileInfo->GetID() > pGroupInfo->GetLastID())
|
||||
{
|
||||
pGroupInfo->m_iLastID = pFileInfo->GetID();
|
||||
}
|
||||
if (pFileInfo->GetTime() > 0)
|
||||
{
|
||||
if (pFileInfo->GetTime() < pGroupInfo->GetMinTime())
|
||||
{
|
||||
pGroupInfo->m_tMinTime = pFileInfo->GetTime();
|
||||
}
|
||||
if (pFileInfo->GetTime() > pGroupInfo->GetMaxTime())
|
||||
{
|
||||
pGroupInfo->m_tMaxTime = pFileInfo->GetTime();
|
||||
}
|
||||
}
|
||||
if (pFileInfo->GetPriority() < pGroupInfo->GetMinPriority())
|
||||
{
|
||||
pGroupInfo->m_iMinPriority = pFileInfo->GetPriority();
|
||||
}
|
||||
if (pFileInfo->GetPriority() > pGroupInfo->GetMaxPriority())
|
||||
{
|
||||
pGroupInfo->m_iMaxPriority = pFileInfo->GetPriority();
|
||||
}
|
||||
|
||||
pGroupInfo->m_iActiveDownloads += pFileInfo->GetActiveDownloads();
|
||||
pGroupInfo->m_iRemainingFileCount++;
|
||||
pGroupInfo->m_lRemainingSize += pFileInfo->GetRemainingSize();
|
||||
if (pFileInfo->GetPaused())
|
||||
{
|
||||
pGroupInfo->m_lPausedSize += pFileInfo->GetRemainingSize();
|
||||
pGroupInfo->m_iPausedFileCount++;
|
||||
}
|
||||
|
||||
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"))
|
||||
{
|
||||
pGroupInfo->m_iRemainingParCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
UrlInfo::UrlInfo()
|
||||
{
|
||||
//debug("Creating ArticleInfo");
|
||||
m_szURL = NULL;
|
||||
m_szNZBFilename = strdup("");
|
||||
m_szCategory = strdup("");
|
||||
m_iPriority = 0;
|
||||
m_bAddTop = false;
|
||||
m_bAddPaused = false;
|
||||
m_eStatus = aiUndefined;
|
||||
m_iIDGen++;
|
||||
m_iID = m_iIDGen;
|
||||
}
|
||||
|
||||
UrlInfo::~ UrlInfo()
|
||||
{
|
||||
if (m_szURL)
|
||||
{
|
||||
free(m_szURL);
|
||||
}
|
||||
if (m_szNZBFilename)
|
||||
{
|
||||
free(m_szNZBFilename);
|
||||
}
|
||||
if (m_szCategory)
|
||||
{
|
||||
free(m_szCategory);
|
||||
}
|
||||
}
|
||||
|
||||
void UrlInfo::SetURL(const char* szURL)
|
||||
{
|
||||
if (m_szURL)
|
||||
{
|
||||
free(m_szURL);
|
||||
}
|
||||
m_szURL = strdup(szURL);
|
||||
}
|
||||
|
||||
void UrlInfo::SetID(int s)
|
||||
{
|
||||
m_iID = s;
|
||||
if (m_iIDGen < m_iID)
|
||||
{
|
||||
m_iIDGen = m_iID;
|
||||
}
|
||||
}
|
||||
|
||||
void UrlInfo::SetNZBFilename(const char* szNZBFilename)
|
||||
{
|
||||
if (m_szNZBFilename)
|
||||
{
|
||||
free(m_szNZBFilename);
|
||||
}
|
||||
m_szNZBFilename = strdup(szNZBFilename);
|
||||
}
|
||||
|
||||
void UrlInfo::SetCategory(const char* szCategory)
|
||||
{
|
||||
if (m_szCategory)
|
||||
{
|
||||
free(m_szCategory);
|
||||
}
|
||||
m_szCategory = strdup(szCategory);
|
||||
}
|
||||
|
||||
void UrlInfo::GetName(char* szBuffer, int iSize)
|
||||
{
|
||||
MakeNiceName(m_szURL, m_szNZBFilename, szBuffer, iSize);
|
||||
}
|
||||
|
||||
void UrlInfo::MakeNiceName(const char* szURL, const char* szNZBFilename, char* szBuffer, int iSize)
|
||||
{
|
||||
URL url(szURL);
|
||||
|
||||
if (strlen(szNZBFilename) > 0)
|
||||
{
|
||||
char szNZBNicename[1024];
|
||||
NZBInfo::MakeNiceNZBName(szNZBFilename, szNZBNicename, sizeof(szNZBNicename), true);
|
||||
snprintf(szBuffer, iSize, "%s @ %s", szNZBNicename, url.GetHost());
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(szBuffer, g_pOptions->GetDestDir(), 1024);
|
||||
szBuffer[1024-1] = '\0'; // trim the last slash, always returned by GetDestDir()
|
||||
snprintf(szBuffer, iSize, "%s%s", url.GetHost(), url.GetResource());
|
||||
}
|
||||
|
||||
m_szDestDir = strdup(szBuffer);
|
||||
szBuffer[iSize-1] = '\0';
|
||||
}
|
||||
|
||||
bool FileInfo::IsDupe()
|
||||
|
||||
HistoryInfo::HistoryInfo(NZBInfo* pNZBInfo)
|
||||
{
|
||||
debug("Checking if the file was already downloaded or queued");
|
||||
|
||||
struct stat buffer;
|
||||
char fileName[1024];
|
||||
bool exists = false;
|
||||
|
||||
snprintf(fileName, 1024, "%s%c%s", GetDestDir(), (int)PATH_SEPARATOR, GetFilename());
|
||||
fileName[1024-1] = '\0';
|
||||
exists = !stat(fileName, &buffer);
|
||||
if (!exists)
|
||||
{
|
||||
snprintf(fileName, 1024, "%s%c%s_broken", GetDestDir(), (int)PATH_SEPARATOR, GetFilename());
|
||||
fileName[1024-1] = '\0';
|
||||
exists = !stat(fileName, &buffer);
|
||||
}
|
||||
|
||||
return exists;
|
||||
m_eKind = hkNZBInfo;
|
||||
m_pInfo = pNZBInfo;
|
||||
pNZBInfo->AddReference();
|
||||
m_tTime = 0;
|
||||
m_iIDGen++;
|
||||
m_iID = m_iIDGen;
|
||||
}
|
||||
|
||||
HistoryInfo::HistoryInfo(UrlInfo* pUrlInfo)
|
||||
{
|
||||
m_eKind = hkUrlInfo;
|
||||
m_pInfo = pUrlInfo;
|
||||
m_tTime = 0;
|
||||
m_iIDGen++;
|
||||
m_iID = m_iIDGen;
|
||||
}
|
||||
|
||||
HistoryInfo::~HistoryInfo()
|
||||
{
|
||||
if (m_eKind == hkNZBInfo && m_pInfo)
|
||||
{
|
||||
((NZBInfo*)m_pInfo)->Release();
|
||||
}
|
||||
else if (m_eKind == hkUrlInfo && m_pInfo)
|
||||
{
|
||||
delete (UrlInfo*)m_pInfo;
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryInfo::SetID(int s)
|
||||
{
|
||||
m_iID = s;
|
||||
if (m_iIDGen < m_iID)
|
||||
{
|
||||
m_iIDGen = m_iID;
|
||||
}
|
||||
}
|
||||
|
||||
void HistoryInfo::GetName(char* szBuffer, int iSize)
|
||||
{
|
||||
if (m_eKind == hkNZBInfo)
|
||||
{
|
||||
strncpy(szBuffer, GetNZBInfo()->GetName(), iSize);
|
||||
szBuffer[iSize-1] = '\0';
|
||||
}
|
||||
else if (m_eKind == hkUrlInfo)
|
||||
{
|
||||
GetUrlInfo()->GetName(szBuffer, iSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(szBuffer, "<unknown>", iSize);
|
||||
}
|
||||
}
|
||||
|
||||
443
DownloadInfo.h
443
DownloadInfo.h
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -29,6 +29,13 @@
|
||||
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <time.h>
|
||||
|
||||
#include "Log.h"
|
||||
#include "Thread.h"
|
||||
|
||||
class NZBInfo;
|
||||
class DownloadQueue;
|
||||
|
||||
class ArticleInfo
|
||||
{
|
||||
@@ -71,18 +78,22 @@ public:
|
||||
|
||||
private:
|
||||
int m_iID;
|
||||
NZBInfo* m_pNZBInfo;
|
||||
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;
|
||||
time_t m_tTime;
|
||||
bool m_bPaused;
|
||||
bool m_bDeleted;
|
||||
bool m_bFilenameConfirmed;
|
||||
int m_iCompleted;
|
||||
bool m_bOutputInitialized;
|
||||
Mutex m_mutexOutputFile;
|
||||
int m_iPriority;
|
||||
int m_iActiveDownloads;
|
||||
|
||||
static int m_iIDGen;
|
||||
|
||||
@@ -91,35 +102,435 @@ public:
|
||||
~FileInfo();
|
||||
int GetID() { return m_iID; }
|
||||
void SetID(int s);
|
||||
NZBInfo* GetNZBInfo() { return m_pNZBInfo; }
|
||||
void SetNZBInfo(NZBInfo* pNZBInfo);
|
||||
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; }
|
||||
time_t GetTime() { return m_tTime; }
|
||||
void SetTime(time_t tTime) { m_tTime = tTime; }
|
||||
bool GetPaused() { return m_bPaused; }
|
||||
void SetPaused(bool Paused) { m_bPaused = Paused; }
|
||||
bool GetDeleted() { return m_bDeleted; }
|
||||
void SetDeleted(bool Deleted) { m_bDeleted = Deleted; }
|
||||
void BuildDestDirName(const char* szNZBFilename);
|
||||
const char* GetDestDir() { return m_szDestDir; }
|
||||
void SetDestDir(const char* szDestDir);
|
||||
int GetCompleted() { return m_iCompleted; }
|
||||
void SetCompleted(int s) { m_iCompleted = s; }
|
||||
void ParseSubject();
|
||||
bool IsDupe();
|
||||
void ClearArticles();
|
||||
void LockOutputFile();
|
||||
void UnlockOutputFile();
|
||||
bool GetOutputInitialized() { return m_bOutputInitialized; }
|
||||
void SetOutputInitialized(bool bOutputInitialized) { m_bOutputInitialized = bOutputInitialized; }
|
||||
bool IsDupe(const char* szFilename);
|
||||
int GetPriority() { return m_iPriority; }
|
||||
void SetPriority(int iPriority) { m_iPriority = iPriority; }
|
||||
int GetActiveDownloads() { return m_iActiveDownloads; }
|
||||
void SetActiveDownloads(int iActiveDownloads) { m_iActiveDownloads = iActiveDownloads; }
|
||||
};
|
||||
|
||||
typedef std::deque<FileInfo*> DownloadQueue;
|
||||
typedef std::deque<FileInfo*> FileQueue;
|
||||
|
||||
class GroupInfo
|
||||
{
|
||||
private:
|
||||
NZBInfo* m_pNZBInfo;
|
||||
int m_iFirstID;
|
||||
int m_iLastID;
|
||||
int m_iRemainingFileCount;
|
||||
int m_iPausedFileCount;
|
||||
long long m_lRemainingSize;
|
||||
long long m_lPausedSize;
|
||||
int m_iRemainingParCount;
|
||||
time_t m_tMinTime;
|
||||
time_t m_tMaxTime;
|
||||
int m_iMinPriority;
|
||||
int m_iMaxPriority;
|
||||
int m_iActiveDownloads;
|
||||
|
||||
friend class DownloadQueue;
|
||||
|
||||
public:
|
||||
GroupInfo();
|
||||
~GroupInfo();
|
||||
NZBInfo* GetNZBInfo() { return m_pNZBInfo; }
|
||||
int GetFirstID() { return m_iFirstID; }
|
||||
int GetLastID() { return m_iLastID; }
|
||||
long long GetRemainingSize() { return m_lRemainingSize; }
|
||||
long long GetPausedSize() { return m_lPausedSize; }
|
||||
int GetRemainingFileCount() { return m_iRemainingFileCount; }
|
||||
int GetPausedFileCount() { return m_iPausedFileCount; }
|
||||
int GetRemainingParCount() { return m_iRemainingParCount; }
|
||||
time_t GetMinTime() { return m_tMinTime; }
|
||||
time_t GetMaxTime() { return m_tMaxTime; }
|
||||
int GetMinPriority() { return m_iMinPriority; }
|
||||
int GetMaxPriority() { return m_iMaxPriority; }
|
||||
int GetActiveDownloads() { return m_iActiveDownloads; }
|
||||
};
|
||||
|
||||
typedef std::deque<GroupInfo*> GroupQueue;
|
||||
|
||||
class NZBParameter
|
||||
{
|
||||
private:
|
||||
char* m_szName;
|
||||
char* m_szValue;
|
||||
|
||||
void SetValue(const char* szValue);
|
||||
|
||||
friend class NZBParameterList;
|
||||
|
||||
public:
|
||||
NZBParameter(const char* szName);
|
||||
~NZBParameter();
|
||||
const char* GetName() { return m_szName; }
|
||||
const char* GetValue() { return m_szValue; }
|
||||
};
|
||||
|
||||
typedef std::deque<NZBParameter*> NZBParameterListBase;
|
||||
|
||||
class NZBParameterList : public NZBParameterListBase
|
||||
{
|
||||
public:
|
||||
void SetParameter(const char* szName, const char* szValue);
|
||||
};
|
||||
|
||||
class NZBInfoList;
|
||||
|
||||
class NZBInfo
|
||||
{
|
||||
public:
|
||||
enum EParStatus
|
||||
{
|
||||
prNone,
|
||||
prFailure,
|
||||
prRepairPossible,
|
||||
prSuccess
|
||||
};
|
||||
|
||||
enum EScriptStatus
|
||||
{
|
||||
srNone,
|
||||
srUnknown,
|
||||
srFailure,
|
||||
srSuccess
|
||||
};
|
||||
|
||||
typedef std::vector<char*> Files;
|
||||
typedef std::deque<Message*> Messages;
|
||||
|
||||
private:
|
||||
int m_iID;
|
||||
int m_iRefCount;
|
||||
char* m_szFilename;
|
||||
char* m_szName;
|
||||
char* m_szDestDir;
|
||||
char* m_szCategory;
|
||||
int m_iFileCount;
|
||||
int m_iParkedFileCount;
|
||||
long long m_lSize;
|
||||
Files m_completedFiles;
|
||||
bool m_bPostProcess;
|
||||
EParStatus m_eParStatus;
|
||||
EScriptStatus m_eScriptStatus;
|
||||
char* m_szQueuedFilename;
|
||||
bool m_bDeleted;
|
||||
bool m_bParCleanup;
|
||||
bool m_bCleanupDisk;
|
||||
NZBInfoList* m_Owner;
|
||||
NZBParameterList m_ppParameters;
|
||||
Mutex m_mutexLog;
|
||||
Messages m_Messages;
|
||||
int m_iIDMessageGen;
|
||||
|
||||
static int m_iIDGen;
|
||||
|
||||
friend class NZBInfoList;
|
||||
|
||||
public:
|
||||
NZBInfo();
|
||||
~NZBInfo();
|
||||
void AddReference();
|
||||
void Release();
|
||||
int GetID() { return m_iID; }
|
||||
const char* GetFilename() { return m_szFilename; }
|
||||
void SetFilename(const char* szFilename);
|
||||
static void MakeNiceNZBName(const char* szNZBFilename, char* szBuffer, int iSize, bool bRemoveExt);
|
||||
const char* GetDestDir() { return m_szDestDir; } // needs locking (for shared objects)
|
||||
void SetDestDir(const char* szDestDir); // needs locking (for shared objects)
|
||||
const char* GetCategory() { return m_szCategory; } // needs locking (for shared objects)
|
||||
void SetCategory(const char* szCategory); // needs locking (for shared objects)
|
||||
const char* GetName() { return m_szName; } // needs locking (for shared objects)
|
||||
void SetName(const char* szName); // needs locking (for shared objects)
|
||||
long long GetSize() { return m_lSize; }
|
||||
void SetSize(long long lSize) { m_lSize = lSize; }
|
||||
int GetFileCount() { return m_iFileCount; }
|
||||
void SetFileCount(int iFileCount) { m_iFileCount = iFileCount; }
|
||||
int GetParkedFileCount() { return m_iParkedFileCount; }
|
||||
void SetParkedFileCount(int iParkedFileCount) { m_iParkedFileCount = iParkedFileCount; }
|
||||
void BuildDestDirName();
|
||||
Files* GetCompletedFiles() { return &m_completedFiles; } // needs locking (for shared objects)
|
||||
void ClearCompletedFiles();
|
||||
bool GetPostProcess() { return m_bPostProcess; }
|
||||
void SetPostProcess(bool bPostProcess) { m_bPostProcess = bPostProcess; }
|
||||
EParStatus GetParStatus() { return m_eParStatus; }
|
||||
void SetParStatus(EParStatus eParStatus) { m_eParStatus = eParStatus; }
|
||||
EScriptStatus GetScriptStatus() { return m_eScriptStatus; }
|
||||
void SetScriptStatus(EScriptStatus eScriptStatus) { m_eScriptStatus = eScriptStatus; }
|
||||
const char* GetQueuedFilename() { return m_szQueuedFilename; }
|
||||
void SetQueuedFilename(const char* szQueuedFilename);
|
||||
bool GetDeleted() { return m_bDeleted; }
|
||||
void SetDeleted(bool bDeleted) { m_bDeleted = bDeleted; }
|
||||
bool GetParCleanup() { return m_bParCleanup; }
|
||||
void SetParCleanup(bool bParCleanup) { m_bParCleanup = bParCleanup; }
|
||||
bool GetCleanupDisk() { return m_bCleanupDisk; }
|
||||
void SetCleanupDisk(bool bCleanupDisk) { m_bCleanupDisk = bCleanupDisk; }
|
||||
NZBParameterList* GetParameters() { return &m_ppParameters; } // needs locking (for shared objects)
|
||||
void SetParameter(const char* szName, const char* szValue); // needs locking (for shared objects)
|
||||
void AppendMessage(Message::EKind eKind, time_t tTime, const char* szText);
|
||||
Messages* LockMessages();
|
||||
void UnlockMessages();
|
||||
};
|
||||
|
||||
typedef std::deque<NZBInfo*> NZBInfoListBase;
|
||||
|
||||
class NZBInfoList : public NZBInfoListBase
|
||||
{
|
||||
public:
|
||||
void Add(NZBInfo* pNZBInfo);
|
||||
void Remove(NZBInfo* pNZBInfo);
|
||||
void ReleaseAll();
|
||||
};
|
||||
|
||||
class PostInfo
|
||||
{
|
||||
public:
|
||||
enum EStage
|
||||
{
|
||||
ptQueued,
|
||||
ptLoadingPars,
|
||||
ptVerifyingSources,
|
||||
ptRepairing,
|
||||
ptVerifyingRepaired,
|
||||
ptExecutingScript,
|
||||
ptFinished
|
||||
};
|
||||
|
||||
enum EParStatus
|
||||
{
|
||||
psNone,
|
||||
psFailure,
|
||||
psSuccess,
|
||||
psRepairPossible
|
||||
};
|
||||
|
||||
enum ERequestParCheck
|
||||
{
|
||||
rpNone,
|
||||
rpCurrent,
|
||||
rpAll
|
||||
};
|
||||
|
||||
enum EScriptStatus
|
||||
{
|
||||
srNone,
|
||||
srUnknown,
|
||||
srFailure,
|
||||
srSuccess
|
||||
};
|
||||
|
||||
typedef std::deque<Message*> Messages;
|
||||
|
||||
private:
|
||||
int m_iID;
|
||||
NZBInfo* m_pNZBInfo;
|
||||
char* m_szParFilename;
|
||||
char* m_szInfoName;
|
||||
bool m_bWorking;
|
||||
bool m_bDeleted;
|
||||
bool m_bParCheck;
|
||||
EParStatus m_eParStatus;
|
||||
EScriptStatus m_eScriptStatus;
|
||||
ERequestParCheck m_eRequestParCheck;
|
||||
EStage m_eStage;
|
||||
char* m_szProgressLabel;
|
||||
int m_iFileProgress;
|
||||
int m_iStageProgress;
|
||||
time_t m_tStartTime;
|
||||
time_t m_tStageTime;
|
||||
Thread* m_pScriptThread;
|
||||
|
||||
Mutex m_mutexLog;
|
||||
Messages m_Messages;
|
||||
int m_iIDMessageGen;
|
||||
|
||||
static int m_iIDGen;
|
||||
|
||||
public:
|
||||
PostInfo();
|
||||
~PostInfo();
|
||||
int GetID() { return m_iID; }
|
||||
NZBInfo* GetNZBInfo() { return m_pNZBInfo; }
|
||||
void SetNZBInfo(NZBInfo* pNZBInfo);
|
||||
const char* GetParFilename() { return m_szParFilename; }
|
||||
void SetParFilename(const char* szParFilename);
|
||||
const char* GetInfoName() { return m_szInfoName; }
|
||||
void SetInfoName(const char* szInfoName);
|
||||
EStage GetStage() { return m_eStage; }
|
||||
void SetStage(EStage eStage) { m_eStage = eStage; }
|
||||
void SetProgressLabel(const char* szProgressLabel);
|
||||
const char* GetProgressLabel() { return m_szProgressLabel; }
|
||||
int GetFileProgress() { return m_iFileProgress; }
|
||||
void SetFileProgress(int iFileProgress) { m_iFileProgress = iFileProgress; }
|
||||
int GetStageProgress() { return m_iStageProgress; }
|
||||
void SetStageProgress(int iStageProgress) { m_iStageProgress = iStageProgress; }
|
||||
time_t GetStartTime() { return m_tStartTime; }
|
||||
void SetStartTime(time_t tStartTime) { m_tStartTime = tStartTime; }
|
||||
time_t GetStageTime() { return m_tStageTime; }
|
||||
void SetStageTime(time_t tStageTime) { m_tStageTime = tStageTime; }
|
||||
bool GetWorking() { return m_bWorking; }
|
||||
void SetWorking(bool bWorking) { m_bWorking = bWorking; }
|
||||
bool GetDeleted() { return m_bDeleted; }
|
||||
void SetDeleted(bool bDeleted) { m_bDeleted = bDeleted; }
|
||||
bool GetParCheck() { return m_bParCheck; }
|
||||
void SetParCheck(bool bParCheck) { m_bParCheck = bParCheck; }
|
||||
EParStatus GetParStatus() { return m_eParStatus; }
|
||||
void SetParStatus(EParStatus eParStatus) { m_eParStatus = eParStatus; }
|
||||
ERequestParCheck GetRequestParCheck() { return m_eRequestParCheck; }
|
||||
void SetRequestParCheck(ERequestParCheck eRequestParCheck) { m_eRequestParCheck = eRequestParCheck; }
|
||||
EScriptStatus GetScriptStatus() { return m_eScriptStatus; }
|
||||
void SetScriptStatus(EScriptStatus eScriptStatus) { m_eScriptStatus = eScriptStatus; }
|
||||
void AppendMessage(Message::EKind eKind, const char* szText);
|
||||
Thread* GetScriptThread() { return m_pScriptThread; }
|
||||
void SetScriptThread(Thread* pScriptThread) { m_pScriptThread = pScriptThread; }
|
||||
Messages* LockMessages();
|
||||
void UnlockMessages();
|
||||
};
|
||||
|
||||
typedef std::deque<PostInfo*> PostQueue;
|
||||
|
||||
typedef std::vector<int> IDList;
|
||||
|
||||
typedef std::vector<char*> NameList;
|
||||
|
||||
class UrlInfo
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
aiUndefined,
|
||||
aiRunning,
|
||||
aiFinished,
|
||||
aiFailed,
|
||||
aiRetry
|
||||
};
|
||||
|
||||
private:
|
||||
int m_iID;
|
||||
char* m_szURL;
|
||||
char* m_szNZBFilename;
|
||||
char* m_szCategory;
|
||||
int m_iPriority;
|
||||
bool m_bAddTop;
|
||||
bool m_bAddPaused;
|
||||
EStatus m_eStatus;
|
||||
|
||||
static int m_iIDGen;
|
||||
|
||||
public:
|
||||
UrlInfo();
|
||||
~UrlInfo();
|
||||
int GetID() { return m_iID; }
|
||||
void SetID(int s);
|
||||
const char* GetURL() { return m_szURL; } // needs locking (for shared objects)
|
||||
void SetURL(const char* szURL); // needs locking (for shared objects)
|
||||
const char* GetNZBFilename() { return m_szNZBFilename; } // needs locking (for shared objects)
|
||||
void SetNZBFilename(const char* szNZBFilename); // needs locking (for shared objects)
|
||||
const char* GetCategory() { return m_szCategory; } // needs locking (for shared objects)
|
||||
void SetCategory(const char* szCategory); // needs locking (for shared objects)
|
||||
int GetPriority() { return m_iPriority; }
|
||||
void SetPriority(int iPriority) { m_iPriority = iPriority; }
|
||||
bool GetAddTop() { return m_bAddTop; }
|
||||
void SetAddTop(bool bAddTop) { m_bAddTop = bAddTop; }
|
||||
bool GetAddPaused() { return m_bAddPaused; }
|
||||
void SetAddPaused(bool bAddPaused) { m_bAddPaused = bAddPaused; }
|
||||
void GetName(char* szBuffer, int iSize); // needs locking (for shared objects)
|
||||
static void MakeNiceName(const char* szURL, const char* szNZBFilename, char* szBuffer, int iSize);
|
||||
EStatus GetStatus() { return m_eStatus; }
|
||||
void SetStatus(EStatus Status) { m_eStatus = Status; }
|
||||
};
|
||||
|
||||
typedef std::deque<UrlInfo*> UrlQueue;
|
||||
|
||||
class HistoryInfo
|
||||
{
|
||||
public:
|
||||
enum EKind
|
||||
{
|
||||
hkUnknown,
|
||||
hkNZBInfo,
|
||||
hkUrlInfo
|
||||
};
|
||||
|
||||
private:
|
||||
int m_iID;
|
||||
EKind m_eKind;
|
||||
void* m_pInfo;
|
||||
time_t m_tTime;
|
||||
|
||||
static int m_iIDGen;
|
||||
|
||||
public:
|
||||
HistoryInfo(NZBInfo* pNZBInfo);
|
||||
HistoryInfo(UrlInfo* pUrlInfo);
|
||||
~HistoryInfo();
|
||||
int GetID() { return m_iID; }
|
||||
void SetID(int s);
|
||||
EKind GetKind() { return m_eKind; }
|
||||
NZBInfo* GetNZBInfo() { return (NZBInfo*)m_pInfo; }
|
||||
UrlInfo* GetUrlInfo() { return (UrlInfo*)m_pInfo; }
|
||||
void DiscardUrlInfo() { m_pInfo = NULL; }
|
||||
time_t GetTime() { return m_tTime; }
|
||||
void SetTime(time_t tTime) { m_tTime = tTime; }
|
||||
void GetName(char* szBuffer, int iSize); // needs locking (for shared objects)
|
||||
};
|
||||
|
||||
typedef std::deque<HistoryInfo*> HistoryList;
|
||||
|
||||
class DownloadQueue
|
||||
{
|
||||
protected:
|
||||
NZBInfoList m_NZBInfoList;
|
||||
FileQueue m_FileQueue;
|
||||
PostQueue m_PostQueue;
|
||||
HistoryList m_HistoryList;
|
||||
FileQueue m_ParkedFiles;
|
||||
UrlQueue m_UrlQueue;
|
||||
|
||||
public:
|
||||
NZBInfoList* GetNZBInfoList() { return &m_NZBInfoList; }
|
||||
FileQueue* GetFileQueue() { return &m_FileQueue; }
|
||||
PostQueue* GetPostQueue() { return &m_PostQueue; }
|
||||
HistoryList* GetHistoryList() { return &m_HistoryList; }
|
||||
FileQueue* GetParkedFiles() { return &m_ParkedFiles; }
|
||||
UrlQueue* GetUrlQueue() { return &m_UrlQueue; }
|
||||
void BuildGroups(GroupQueue* pGroupQueue);
|
||||
};
|
||||
|
||||
class DownloadQueueHolder
|
||||
{
|
||||
public:
|
||||
virtual ~DownloadQueueHolder() {};
|
||||
virtual DownloadQueue* LockQueue() = 0;
|
||||
virtual void UnlockQueue() = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
228
Frontend.cpp
228
Frontend.cpp
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2010 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -34,8 +34,12 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
@@ -46,6 +50,7 @@
|
||||
#include "MessageBase.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "RemoteClient.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
extern Options* g_pOptions;
|
||||
@@ -60,11 +65,16 @@ Frontend::Frontend()
|
||||
m_bFileList = false;
|
||||
m_fCurrentDownloadSpeed = 0;
|
||||
m_lRemainingSize = 0;
|
||||
m_bPause = false;
|
||||
m_bPauseDownload = false;
|
||||
m_bPauseDownload2 = false;
|
||||
m_fDownloadLimit = 0;
|
||||
m_iThreadCount = 0;
|
||||
m_RemoteMessages.clear();
|
||||
m_RemoteQueue.clear();
|
||||
m_iPostJobCount = 0;
|
||||
m_iUpTimeSec = 0;
|
||||
m_iDnTimeSec = 0;
|
||||
m_iAllBytes = 0;
|
||||
m_bStandBy = 0;
|
||||
m_iUpdateInterval = g_pOptions->GetUpdateInterval();
|
||||
}
|
||||
|
||||
bool Frontend::PrepareData()
|
||||
@@ -77,7 +87,7 @@ bool Frontend::PrepareData()
|
||||
}
|
||||
if (!RequestMessages() || ((m_bSummary || m_bFileList) && !RequestFileList()))
|
||||
{
|
||||
printf("Unable to send request to nzbserver at %s (port %i) \n", g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
printf("\nUnable to send request to nzbget-server at %s (port %i) \n", g_pOptions->GetControlIP(), g_pOptions->GetControlPort());
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
@@ -88,9 +98,14 @@ bool Frontend::PrepareData()
|
||||
{
|
||||
m_fCurrentDownloadSpeed = g_pQueueCoordinator->CalcCurrentDownloadSpeed();
|
||||
m_lRemainingSize = g_pQueueCoordinator->CalcRemainingSize();
|
||||
m_bPause = g_pOptions->GetPause();
|
||||
m_bPauseDownload = g_pOptions->GetPauseDownload();
|
||||
m_bPauseDownload2 = g_pOptions->GetPauseDownload2();
|
||||
m_fDownloadLimit = g_pOptions->GetDownloadRate();
|
||||
m_iThreadCount = Thread::GetThreadCount();
|
||||
PostQueue* pPostQueue = g_pQueueCoordinator->LockQueue()->GetPostQueue();
|
||||
m_iPostJobCount = pPostQueue->size();
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
g_pQueueCoordinator->CalcStat(&m_iUpTimeSec, &m_iDnTimeSec, &m_iAllBytes, &m_bStandBy);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -106,11 +121,11 @@ void Frontend::FreeData()
|
||||
}
|
||||
m_RemoteMessages.clear();
|
||||
|
||||
for (DownloadQueue::iterator it = m_RemoteQueue.begin(); it != m_RemoteQueue.end(); it++)
|
||||
for (FileQueue::iterator it = m_RemoteQueue.GetFileQueue()->begin(); it != m_RemoteQueue.GetFileQueue()->end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_RemoteQueue.clear();
|
||||
m_RemoteQueue.GetFileQueue()->clear();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -134,7 +149,7 @@ void Frontend::UnlockMessages()
|
||||
}
|
||||
}
|
||||
|
||||
DownloadQueue * Frontend::LockQueue()
|
||||
DownloadQueue* Frontend::LockQueue()
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
@@ -159,15 +174,22 @@ bool Frontend::IsRemoteMode()
|
||||
return g_pOptions->GetRemoteClientMode();
|
||||
}
|
||||
|
||||
void Frontend::ServerPauseUnpause(bool bPause)
|
||||
void Frontend::ServerPauseUnpause(bool bPause, bool bSecondRegister)
|
||||
{
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
RequestPauseUnpause(bPause);
|
||||
RequestPauseUnpause(bPause, bSecondRegister);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pOptions->SetPause(bPause);
|
||||
if (bSecondRegister)
|
||||
{
|
||||
g_pOptions->SetPauseDownload2(bPause);
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pOptions->SetPauseDownload(bPause);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -195,89 +217,44 @@ void Frontend::ServerDumpDebug()
|
||||
}
|
||||
}
|
||||
|
||||
bool Frontend::ServerEditQueue(EEditAction eAction, int iEntry)
|
||||
bool Frontend::ServerEditQueue(QueueEditor::EEditAction eAction, int iOffset, int iID)
|
||||
{
|
||||
DownloadQueue* pDownloadQueue = LockQueue();
|
||||
int ID = 0;
|
||||
bool bPause = false;
|
||||
if (iEntry >= 0 && iEntry < (int)pDownloadQueue->size())
|
||||
{
|
||||
FileInfo* pFileInfo = (*pDownloadQueue)[iEntry];
|
||||
ID = pFileInfo->GetID();
|
||||
bPause = !pFileInfo->GetPaused();
|
||||
}
|
||||
UnlockQueue();
|
||||
|
||||
if (ID == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsRemoteMode())
|
||||
{
|
||||
switch (eAction)
|
||||
{
|
||||
case eaPauseUnpause:
|
||||
return RequestEditQueue(bPause ? NZBMessageRequest::eActionPause : NZBMessageRequest::eActionResume, 0, ID, ID);
|
||||
case eaDelete:
|
||||
return RequestEditQueue(NZBMessageRequest::eActionDelete, 0, ID, ID);
|
||||
case eaMoveUp:
|
||||
return RequestEditQueue(NZBMessageRequest::eActionMoveOffset, -1, ID, ID);
|
||||
case eaMoveDown:
|
||||
return RequestEditQueue(NZBMessageRequest::eActionMoveOffset, +1, ID, ID);
|
||||
case eaMoveTop:
|
||||
return RequestEditQueue(NZBMessageRequest::eActionMoveTop, 0, ID, ID);
|
||||
case eaMoveBottom:
|
||||
return RequestEditQueue(NZBMessageRequest::eActionMoveBottom, 0, ID, ID);
|
||||
}
|
||||
return RequestEditQueue((eRemoteEditAction)eAction, iOffset, iID);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (eAction)
|
||||
{
|
||||
case eaPauseUnpause:
|
||||
return g_pQueueCoordinator->EditQueuePauseUnpauseEntry(ID, bPause);
|
||||
case eaDelete:
|
||||
return g_pQueueCoordinator->EditQueueDeleteEntry(ID);
|
||||
case eaMoveUp:
|
||||
return g_pQueueCoordinator->EditQueueMoveEntry(ID, -1, false);
|
||||
case eaMoveDown:
|
||||
return g_pQueueCoordinator->EditQueueMoveEntry(ID, +1, false);
|
||||
case eaMoveTop:
|
||||
return g_pQueueCoordinator->EditQueueMoveEntry(ID, -1000000, true);
|
||||
case eaMoveBottom:
|
||||
return g_pQueueCoordinator->EditQueueMoveEntry(ID, +1000000, true);
|
||||
}
|
||||
return g_pQueueCoordinator->GetQueueEditor()->EditEntry(iID, true, eAction, iOffset, NULL);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void Frontend::InitMessageBase(SNZBMessageBase* pMessageBase, int iRequest, int iSize)
|
||||
void Frontend::InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize)
|
||||
{
|
||||
pMessageBase->m_iId = NZBMESSAGE_SIGNATURE;
|
||||
pMessageBase->m_iType = iRequest;
|
||||
pMessageBase->m_iSize = iSize;
|
||||
strncpy(pMessageBase->m_szPassword, g_pOptions->GetServerPassword(), NZBREQUESTPASSWORDSIZE);
|
||||
pMessageBase->m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
|
||||
pMessageBase->m_iType = htonl(iRequest);
|
||||
pMessageBase->m_iStructSize = htonl(iSize);
|
||||
strncpy(pMessageBase->m_szPassword, g_pOptions->GetControlPassword(), NZBREQUESTPASSWORDSIZE);
|
||||
pMessageBase->m_szPassword[NZBREQUESTPASSWORDSIZE - 1] = '\0';
|
||||
}
|
||||
|
||||
bool Frontend::RequestMessages()
|
||||
{
|
||||
NetAddress netAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
Connection connection(&netAddress);
|
||||
Connection connection(g_pOptions->GetControlIP(), g_pOptions->GetControlPort(), false);
|
||||
|
||||
bool OK = connection.Connect() >= 0;
|
||||
bool OK = connection.Connect();
|
||||
if (!OK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SNZBLogRequest LogRequest;
|
||||
InitMessageBase(&LogRequest.m_MessageBase, NZBMessageRequest::eRequestLog, sizeof(LogRequest));
|
||||
LogRequest.m_iLines = m_iNeededLogEntries;
|
||||
InitMessageBase(&LogRequest.m_MessageBase, eRemoteRequestLog, sizeof(LogRequest));
|
||||
LogRequest.m_iLines = htonl(m_iNeededLogEntries);
|
||||
if (m_iNeededLogEntries == 0)
|
||||
{
|
||||
LogRequest.m_iIDFrom = m_iNeededLogFirstID > 0 ? m_iNeededLogFirstID : 1;
|
||||
LogRequest.m_iIDFrom = htonl(m_iNeededLogFirstID > 0 ? m_iNeededLogFirstID : 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -290,17 +267,20 @@ bool Frontend::RequestMessages()
|
||||
}
|
||||
|
||||
// Now listen for the returned log
|
||||
SNZBLogRequestAnswer LogRequestAnswer;
|
||||
if (connection.Recv((char*) &LogRequestAnswer, sizeof(LogRequestAnswer)) < 0)
|
||||
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 (LogRequestAnswer.m_iTrailingDataLength > 0)
|
||||
if (ntohl(LogResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
pBuf = (char*)malloc(LogRequestAnswer.m_iTrailingDataLength);
|
||||
if (!connection.RecvAll(pBuf, LogRequestAnswer.m_iTrailingDataLength))
|
||||
pBuf = (char*)malloc(ntohl(LogResponse.m_iTrailingDataLength));
|
||||
if (!connection.RecvAll(pBuf, ntohl(LogResponse.m_iTrailingDataLength)))
|
||||
{
|
||||
free(pBuf);
|
||||
return false;
|
||||
@@ -309,19 +289,19 @@ bool Frontend::RequestMessages()
|
||||
|
||||
connection.Disconnect();
|
||||
|
||||
if (LogRequestAnswer.m_iTrailingDataLength > 0)
|
||||
if (ntohl(LogResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
char* pBufPtr = (char*)pBuf;
|
||||
for (int i = 0; i < LogRequestAnswer.m_iNrTrailingEntries; i++)
|
||||
for (unsigned int i = 0; i < ntohl(LogResponse.m_iNrTrailingEntries); i++)
|
||||
{
|
||||
SNZBLogRequestAnswerEntry* pLogAnswer = (SNZBLogRequestAnswerEntry*) pBufPtr;
|
||||
SNZBLogResponseEntry* pLogAnswer = (SNZBLogResponseEntry*) pBufPtr;
|
||||
|
||||
char* szText = pBufPtr + sizeof(SNZBLogRequestAnswerEntry);
|
||||
char* szText = pBufPtr + sizeof(SNZBLogResponseEntry);
|
||||
|
||||
Message* pMessage = new Message(pLogAnswer->m_iID, (Message::EKind)pLogAnswer->m_iKind, pLogAnswer->m_tTime, szText);
|
||||
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(SNZBLogRequestAnswerEntry) + pLogAnswer->m_iTextLen;
|
||||
pBufPtr += sizeof(SNZBLogResponseEntry) + ntohl(pLogAnswer->m_iTextLen);
|
||||
}
|
||||
|
||||
free(pBuf);
|
||||
@@ -332,19 +312,18 @@ bool Frontend::RequestMessages()
|
||||
|
||||
bool Frontend::RequestFileList()
|
||||
{
|
||||
NetAddress netAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
Connection connection(&netAddress);
|
||||
Connection connection(g_pOptions->GetControlIP(), g_pOptions->GetControlPort(), false);
|
||||
|
||||
bool OK = connection.Connect() >= 0;
|
||||
bool OK = connection.Connect();
|
||||
if (!OK)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
SNZBListRequest ListRequest;
|
||||
InitMessageBase(&ListRequest.m_MessageBase, NZBMessageRequest::eRequestList, sizeof(ListRequest));
|
||||
ListRequest.m_bFileList = m_bFileList;
|
||||
ListRequest.m_bServerState = m_bSummary;
|
||||
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)
|
||||
{
|
||||
@@ -352,17 +331,20 @@ bool Frontend::RequestFileList()
|
||||
}
|
||||
|
||||
// Now listen for the returned list
|
||||
SNZBListRequestAnswer ListRequestAnswer;
|
||||
if (connection.Recv((char*) &ListRequestAnswer, sizeof(ListRequestAnswer)) < 0)
|
||||
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 (ListRequestAnswer.m_iTrailingDataLength > 0)
|
||||
if (ntohl(ListResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
pBuf = (char*)malloc(ListRequestAnswer.m_iTrailingDataLength);
|
||||
if (!connection.RecvAll(pBuf, ListRequestAnswer.m_iTrailingDataLength))
|
||||
pBuf = (char*)malloc(ntohl(ListResponse.m_iTrailingDataLength));
|
||||
if (!connection.RecvAll(pBuf, ntohl(ListResponse.m_iTrailingDataLength)))
|
||||
{
|
||||
free(pBuf);
|
||||
return false;
|
||||
@@ -373,42 +355,26 @@ bool Frontend::RequestFileList()
|
||||
|
||||
if (m_bSummary)
|
||||
{
|
||||
m_bPause = ListRequestAnswer.m_bServerPaused;
|
||||
m_lRemainingSize = ListRequestAnswer.m_lRemainingSize;
|
||||
m_fCurrentDownloadSpeed = ListRequestAnswer.m_fDownloadRate;
|
||||
m_fDownloadLimit = ListRequestAnswer.m_fDownloadLimit;
|
||||
m_iThreadCount = ListRequestAnswer.m_iThreadCount;
|
||||
m_bPauseDownload = ntohl(ListResponse.m_bDownloadPaused);
|
||||
m_bPauseDownload2 = ntohl(ListResponse.m_bDownload2Paused);
|
||||
m_lRemainingSize = Util::JoinInt64(ntohl(ListResponse.m_iRemainingSizeHi), ntohl(ListResponse.m_iRemainingSizeLo));
|
||||
m_fCurrentDownloadSpeed = ntohl(ListResponse.m_iDownloadRate) / 1024.0f;
|
||||
m_fDownloadLimit = ntohl(ListResponse.m_iDownloadLimit) / 1024.0f;
|
||||
m_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 && ListRequestAnswer.m_iTrailingDataLength > 0)
|
||||
if (m_bFileList && ntohl(ListResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
char* pBufPtr = (char*)pBuf;
|
||||
for (int i = 0; i < ListRequestAnswer.m_iNrTrailingEntries; i++)
|
||||
{
|
||||
SNZBListRequestAnswerEntry* pListAnswer = (SNZBListRequestAnswerEntry*) pBufPtr;
|
||||
|
||||
char* szNZBFilename = pBufPtr + sizeof(SNZBListRequestAnswerEntry);
|
||||
char* szSubject = pBufPtr + sizeof(SNZBListRequestAnswerEntry) + pListAnswer->m_iNZBFilenameLen;
|
||||
char* szFileName = pBufPtr + sizeof(SNZBListRequestAnswerEntry) + pListAnswer->m_iNZBFilenameLen + pListAnswer->m_iSubjectLen;
|
||||
char* szDestDir = pBufPtr + sizeof(SNZBListRequestAnswerEntry) + pListAnswer->m_iNZBFilenameLen + pListAnswer->m_iSubjectLen + pListAnswer->m_iFilenameLen;
|
||||
|
||||
FileInfo* pFileInfo = new FileInfo();
|
||||
pFileInfo->SetID(pListAnswer->m_iID);
|
||||
pFileInfo->SetSize(pListAnswer->m_iFileSize);
|
||||
pFileInfo->SetRemainingSize(pListAnswer->m_iRemainingSize);
|
||||
pFileInfo->SetPaused(pListAnswer->m_bPaused);
|
||||
pFileInfo->SetNZBFilename(szNZBFilename);
|
||||
pFileInfo->SetSubject(szSubject);
|
||||
pFileInfo->SetFilename(szFileName);
|
||||
pFileInfo->SetFilenameConfirmed(pListAnswer->m_bFilenameConfirmed);
|
||||
pFileInfo->SetDestDir(szDestDir);
|
||||
|
||||
m_RemoteQueue.push_back(pFileInfo);
|
||||
|
||||
pBufPtr += sizeof(SNZBListRequestAnswerEntry) + pListAnswer->m_iNZBFilenameLen + pListAnswer->m_iSubjectLen +
|
||||
pListAnswer->m_iFilenameLen + pListAnswer->m_iDestDirLen;
|
||||
}
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
client.BuildFileList(&ListResponse, pBuf, &m_RemoteQueue);
|
||||
}
|
||||
|
||||
if (pBuf)
|
||||
{
|
||||
free(pBuf);
|
||||
@@ -417,11 +383,11 @@ bool Frontend::RequestFileList()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Frontend::RequestPauseUnpause(bool bPause)
|
||||
bool Frontend::RequestPauseUnpause(bool bPause, bool bSecondRegister)
|
||||
{
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
return client.RequestServerPauseUnpause(bPause);
|
||||
return client.RequestServerPauseUnpause(bPause, bSecondRegister ? eRemotePauseUnpauseActionDownload2 : eRemotePauseUnpauseActionDownload);
|
||||
}
|
||||
|
||||
bool Frontend::RequestSetDownloadRate(float fRate)
|
||||
@@ -438,9 +404,9 @@ bool Frontend::RequestDumpDebug()
|
||||
return client.RequestServerDumpDebug();
|
||||
}
|
||||
|
||||
bool Frontend::RequestEditQueue(int iAction, int iOffset, int iIDFrom, int iIDTo)
|
||||
bool Frontend::RequestEditQueue(eRemoteEditAction iAction, int iOffset, int iID)
|
||||
{
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
return client.RequestServerEditQueue(iAction, iOffset, iIDFrom, iIDTo);
|
||||
return client.RequestServerEditQueue(iAction, iOffset, NULL, &iID, 1, NULL, eRemoteMatchModeID, false);
|
||||
}
|
||||
|
||||
43
Frontend.h
43
Frontend.h
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2010 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -31,20 +31,10 @@
|
||||
#include "Log.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "MessageBase.h"
|
||||
#include "QueueEditor.h"
|
||||
|
||||
class Frontend : public Thread
|
||||
{
|
||||
public:
|
||||
enum EEditAction
|
||||
{
|
||||
eaPauseUnpause,
|
||||
eaDelete,
|
||||
eaMoveUp,
|
||||
eaMoveDown,
|
||||
eaMoveTop,
|
||||
eaMoveBottom
|
||||
};
|
||||
|
||||
private:
|
||||
Log::Messages m_RemoteMessages;
|
||||
DownloadQueue m_RemoteQueue;
|
||||
@@ -55,15 +45,22 @@ private:
|
||||
protected:
|
||||
bool m_bSummary;
|
||||
bool m_bFileList;
|
||||
unsigned int m_iNeededLogEntries;
|
||||
unsigned int m_iNeededLogFirstID;
|
||||
unsigned int m_iNeededLogEntries;
|
||||
unsigned int m_iNeededLogFirstID;
|
||||
int m_iUpdateInterval;
|
||||
|
||||
// summary
|
||||
float m_fCurrentDownloadSpeed;
|
||||
long long m_lRemainingSize;
|
||||
bool m_bPause;
|
||||
bool m_bPauseDownload;
|
||||
bool m_bPauseDownload2;
|
||||
float m_fDownloadLimit;
|
||||
int m_iThreadCount;
|
||||
int m_iPostJobCount;
|
||||
int m_iUpTimeSec;
|
||||
int m_iDnTimeSec;
|
||||
long long m_iAllBytes;
|
||||
bool m_bStandBy;
|
||||
|
||||
bool PrepareData();
|
||||
void FreeData();
|
||||
@@ -72,15 +69,15 @@ protected:
|
||||
DownloadQueue* LockQueue();
|
||||
void UnlockQueue();
|
||||
bool IsRemoteMode();
|
||||
void InitMessageBase(SNZBMessageBase* pMessageBase, int iRequest, int iSize);
|
||||
void ServerPauseUnpause(bool bPause);
|
||||
bool RequestPauseUnpause(bool bPause);
|
||||
void InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize);
|
||||
void ServerPauseUnpause(bool bPause, bool bSecondRegister);
|
||||
bool RequestPauseUnpause(bool bPause, bool bSecondRegister);
|
||||
void ServerSetDownloadRate(float fRate);
|
||||
bool RequestSetDownloadRate(float fRate);
|
||||
void ServerDumpDebug();
|
||||
bool RequestDumpDebug();
|
||||
bool ServerEditQueue(EEditAction eAction, int iEntry);
|
||||
bool RequestEditQueue(int iAction, int iOffset, int iIDFrom, int iIDTo);
|
||||
bool ServerEditQueue(QueueEditor::EEditAction eAction, int iOffset, int iEntry);
|
||||
bool RequestEditQueue(eRemoteEditAction iAction, int iOffset, int iID);
|
||||
|
||||
public:
|
||||
Frontend();
|
||||
|
||||
174
Log.cpp
174
Log.cpp
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -30,12 +30,15 @@
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdarg.h>
|
||||
#include <cstdio>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Options.h"
|
||||
@@ -50,18 +53,13 @@ Log::Log()
|
||||
m_iIDGen = 0;
|
||||
m_szLogFilename = NULL;
|
||||
#ifdef DEBUG
|
||||
struct stat buffer;
|
||||
m_bExtraDebug = !stat("extradebug", &buffer);
|
||||
m_bExtraDebug = Util::FileExists("extradebug");
|
||||
#endif
|
||||
}
|
||||
|
||||
Log::~Log()
|
||||
{
|
||||
for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Messages.clear();
|
||||
Clear();
|
||||
if (m_szLogFilename)
|
||||
{
|
||||
free(m_szLogFilename);
|
||||
@@ -70,18 +68,8 @@ Log::~Log()
|
||||
|
||||
void Log::Filelog(const char* msg, ...)
|
||||
{
|
||||
if (
|
||||
(g_pOptions && g_pOptions->GetCreateLog() && g_pOptions->GetLogFile())
|
||||
#ifdef DEBUG
|
||||
|| (m_szLogFilename && m_bExtraDebug)
|
||||
#endif
|
||||
)
|
||||
if (m_szLogFilename)
|
||||
{
|
||||
if (!m_szLogFilename)
|
||||
{
|
||||
m_szLogFilename = strdup(g_pOptions->GetLogFile());
|
||||
}
|
||||
|
||||
char tmp2[1024];
|
||||
|
||||
va_list ap;
|
||||
@@ -102,7 +90,7 @@ void Log::Filelog(const char* msg, ...)
|
||||
szTime[50-1] = '\0';
|
||||
szTime[strlen(szTime) - 1] = '\0'; // trim LF
|
||||
|
||||
FILE* file = fopen(m_szLogFilename, "a+");
|
||||
FILE* file = fopen(m_szLogFilename, "ab+");
|
||||
if (file)
|
||||
{
|
||||
#ifdef WIN32
|
||||
@@ -111,9 +99,9 @@ void Log::Filelog(const char* msg, ...)
|
||||
unsigned long iThreadId = (unsigned long)pthread_self();
|
||||
#endif
|
||||
#ifdef DEBUG
|
||||
fprintf(file, "%s\t%lu\t%s\n", szTime, iThreadId, tmp2);
|
||||
fprintf(file, "%s\t%lu\t%s%s", szTime, iThreadId, tmp2, LINE_ENDING);
|
||||
#else
|
||||
fprintf(file, "%s\t%s\n", szTime, tmp2);
|
||||
fprintf(file, "%s\t%s%s", szTime, tmp2, LINE_ENDING);
|
||||
#endif
|
||||
fclose(file);
|
||||
}
|
||||
@@ -124,6 +112,7 @@ void Log::Filelog(const char* msg, ...)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
#undef debug
|
||||
#ifdef HAVE_VARIADIC_MACROS
|
||||
void debug(const char* szFilename, const char* szFuncname, int iLineNr, const char* msg, ...)
|
||||
@@ -131,7 +120,6 @@ void debug(const char* szFilename, const char* szFuncname, int iLineNr, const ch
|
||||
void debug(const char* msg, ...)
|
||||
#endif
|
||||
{
|
||||
#ifdef DEBUG
|
||||
char tmp1[1024];
|
||||
|
||||
va_list ap;
|
||||
@@ -144,11 +132,11 @@ void debug(const char* msg, ...)
|
||||
#ifdef HAVE_VARIADIC_MACROS
|
||||
if (szFuncname)
|
||||
{
|
||||
snprintf(tmp2, 1024, "%s (%s:%i:%s)", tmp1, BaseFileName(szFilename), iLineNr, szFuncname);
|
||||
snprintf(tmp2, 1024, "%s (%s:%i:%s)", tmp1, Util::BaseFileName(szFilename), iLineNr, szFuncname);
|
||||
}
|
||||
else
|
||||
{
|
||||
snprintf(tmp2, 1024, "%s (%s:%i)", tmp1, BaseFileName(szFilename), iLineNr);
|
||||
snprintf(tmp2, 1024, "%s (%s:%i)", tmp1, Util::BaseFileName(szFilename), iLineNr);
|
||||
}
|
||||
#else
|
||||
snprintf(tmp2, 1024, "%s", tmp1);
|
||||
@@ -157,18 +145,12 @@ void debug(const char* msg, ...)
|
||||
|
||||
g_pLog->m_mutexLog.Lock();
|
||||
|
||||
if (!g_pOptions)
|
||||
if (!g_pOptions && g_pLog->m_bExtraDebug)
|
||||
{
|
||||
if (g_pLog->m_bExtraDebug)
|
||||
{
|
||||
printf("%s\n", tmp2);
|
||||
g_pLog->Filelog("DEBUG\t%s", tmp2);
|
||||
}
|
||||
g_pLog->m_mutexLog.Unlock();
|
||||
return;
|
||||
printf("%s\n", tmp2);
|
||||
}
|
||||
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetDebugTarget();
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions ? g_pOptions->GetDebugTarget() : Options::mtScreen;
|
||||
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->Filelog("DEBUG\t%s", tmp2);
|
||||
@@ -179,8 +161,8 @@ void debug(const char* msg, ...)
|
||||
}
|
||||
|
||||
g_pLog->m_mutexLog.Unlock();
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
void error(const char* msg, ...)
|
||||
{
|
||||
@@ -194,7 +176,7 @@ void error(const char* msg, ...)
|
||||
|
||||
g_pLog->m_mutexLog.Lock();
|
||||
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetErrorTarget();
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions ? g_pOptions->GetErrorTarget() : Options::mtBoth;
|
||||
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->Filelog("ERROR\t%s", tmp2);
|
||||
@@ -219,7 +201,7 @@ void warn(const char* msg, ...)
|
||||
|
||||
g_pLog->m_mutexLog.Lock();
|
||||
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetWarningTarget();
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions ? g_pOptions->GetWarningTarget() : Options::mtScreen;
|
||||
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->Filelog("WARNING\t%s", tmp2);
|
||||
@@ -244,7 +226,7 @@ void info(const char* msg, ...)
|
||||
|
||||
g_pLog->m_mutexLog.Lock();
|
||||
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetInfoTarget();
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions ? g_pOptions->GetInfoTarget() : Options::mtScreen;
|
||||
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->Filelog("INFO\t%s", tmp2);
|
||||
@@ -257,6 +239,31 @@ void info(const char* msg, ...)
|
||||
g_pLog->m_mutexLog.Unlock();
|
||||
}
|
||||
|
||||
void detail(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 ? g_pOptions->GetDetailTarget() : Options::mtScreen;
|
||||
if (eMessageTarget == Options::mtLog || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->Filelog("DETAIL\t%s", tmp2);
|
||||
}
|
||||
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
g_pLog->AppendMessage(Message::mkDetail, tmp2);
|
||||
}
|
||||
|
||||
g_pLog->m_mutexLog.Unlock();
|
||||
}
|
||||
|
||||
void abort(const char* msg, ...)
|
||||
{
|
||||
char tmp2[1024];
|
||||
@@ -269,7 +276,7 @@ void abort(const char* msg, ...)
|
||||
|
||||
g_pLog->m_mutexLog.Lock();
|
||||
|
||||
printf("%s", tmp2);
|
||||
printf("\n%s", tmp2);
|
||||
|
||||
g_pLog->Filelog(tmp2);
|
||||
|
||||
@@ -304,16 +311,30 @@ Message::~ Message()
|
||||
}
|
||||
}
|
||||
|
||||
void Log::Clear()
|
||||
{
|
||||
m_mutexLog.Lock();
|
||||
for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Messages.clear();
|
||||
m_mutexLog.Unlock();
|
||||
}
|
||||
|
||||
void Log::AppendMessage(Message::EKind eKind, const char * szText)
|
||||
{
|
||||
Message* pMessage = new Message(++m_iIDGen, eKind, time(NULL), szText);
|
||||
m_Messages.push_back(pMessage);
|
||||
|
||||
while (m_Messages.size() > (unsigned int)g_pOptions->GetLogBufferSize())
|
||||
if (g_pOptions)
|
||||
{
|
||||
Message* pMessage = m_Messages.front();
|
||||
delete pMessage;
|
||||
m_Messages.pop_front();
|
||||
while (m_Messages.size() > (unsigned int)g_pOptions->GetLogBufferSize())
|
||||
{
|
||||
Message* pMessage = m_Messages.front();
|
||||
delete pMessage;
|
||||
m_Messages.pop_front();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,3 +353,64 @@ void Log::ResetLog()
|
||||
{
|
||||
remove(g_pOptions->GetLogFile());
|
||||
}
|
||||
|
||||
/*
|
||||
* During intializing stage (when options were not read yet) all messages
|
||||
* are saved in screen log, even if they shouldn't (according to options).
|
||||
* Method "InitOptions()" check all messages added to screen log during
|
||||
* intializing stage and does three things:
|
||||
* 1) save the messages to log-file (if they should according to options);
|
||||
* 2) delete messages from screen log (if they should not be saved in screen log).
|
||||
* 3) renumerate IDs
|
||||
*/
|
||||
void Log::InitOptions()
|
||||
{
|
||||
const char* szMessageType[] = { "INFO", "WARNING", "ERROR", "DEBUG", "DETAIL"};
|
||||
|
||||
if (g_pOptions->GetCreateLog() && g_pOptions->GetLogFile())
|
||||
{
|
||||
m_szLogFilename = strdup(g_pOptions->GetLogFile());
|
||||
}
|
||||
|
||||
m_iIDGen = 0;
|
||||
|
||||
for (unsigned int i = 0; i < m_Messages.size(); )
|
||||
{
|
||||
Message* pMessage = m_Messages.at(i);
|
||||
Options::EMessageTarget eTarget = Options::mtNone;
|
||||
switch (pMessage->GetKind())
|
||||
{
|
||||
case Message::mkDebug:
|
||||
eTarget = g_pOptions->GetDebugTarget();
|
||||
break;
|
||||
case Message::mkDetail:
|
||||
eTarget = g_pOptions->GetDetailTarget();
|
||||
break;
|
||||
case Message::mkInfo:
|
||||
eTarget = g_pOptions->GetInfoTarget();
|
||||
break;
|
||||
case Message::mkWarning:
|
||||
eTarget = g_pOptions->GetWarningTarget();
|
||||
break;
|
||||
case Message::mkError:
|
||||
eTarget = g_pOptions->GetErrorTarget();
|
||||
break;
|
||||
}
|
||||
|
||||
if (eTarget == Options::mtLog || eTarget == Options::mtBoth)
|
||||
{
|
||||
Filelog("%s\t%s", szMessageType[pMessage->GetKind()], pMessage->GetText());
|
||||
}
|
||||
|
||||
if (eTarget == Options::mtLog || eTarget == Options::mtNone)
|
||||
{
|
||||
delete pMessage;
|
||||
m_Messages.erase(m_Messages.begin() + i);
|
||||
}
|
||||
else
|
||||
{
|
||||
pMessage->m_iID = ++m_iIDGen;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
42
Log.h
42
Log.h
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -35,24 +35,27 @@
|
||||
void error(const char* msg, ...);
|
||||
void warn(const char* msg, ...);
|
||||
void info(const char* msg, ...);
|
||||
void detail(const char* msg, ...);
|
||||
void abort(const char* msg, ...);
|
||||
|
||||
#ifdef DEBUG
|
||||
#ifdef HAVE_VARIADIC_MACROS
|
||||
void debug(const char* szFilename, const char* szFuncname, int iLineNr, const char* msg, ...);
|
||||
#else
|
||||
void debug(const char* msg, ...);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
class Message
|
||||
{
|
||||
public:
|
||||
enum EKind
|
||||
{
|
||||
mkInfo,
|
||||
mkInfo,
|
||||
mkWarning,
|
||||
mkError,
|
||||
mkDebug
|
||||
mkDebug,
|
||||
mkDetail
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -61,9 +64,11 @@ private:
|
||||
time_t m_tTime;
|
||||
char* m_szText;
|
||||
|
||||
friend class Log;
|
||||
|
||||
public:
|
||||
Message(unsigned int iID, EKind eKind, time_t tTime, const char* szText);
|
||||
~Message();
|
||||
Message(unsigned int iID, EKind eKind, time_t tTime, const char* szText);
|
||||
~Message();
|
||||
unsigned int GetID() { return m_iID; }
|
||||
EKind GetKind() { return m_eKind; }
|
||||
time_t GetTime() { return m_tTime; }
|
||||
@@ -91,26 +96,31 @@ private:
|
||||
friend void warn(const char* msg, ...);
|
||||
friend void info(const char* msg, ...);
|
||||
friend void abort(const char* msg, ...);
|
||||
friend void detail(const char* msg, ...);
|
||||
#ifdef DEBUG
|
||||
#ifdef HAVE_VARIADIC_MACROS
|
||||
friend void debug(const char* szFilename, const char* szFuncname, int iLineNr, const char* msg, ...);
|
||||
#else
|
||||
friend void debug(const char* msg, ...);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
public:
|
||||
Log();
|
||||
~Log();
|
||||
Log();
|
||||
~Log();
|
||||
Messages* LockMessages();
|
||||
void UnlockMessages();
|
||||
void Clear();
|
||||
void ResetLog();
|
||||
void InitOptions();
|
||||
};
|
||||
|
||||
#ifdef DEBUG
|
||||
#ifdef HAVE_VARIADIC_MACROS
|
||||
#ifdef DEBUG
|
||||
#define debug(...) debug(__FILE__, FUNCTION_MACRO_NAME, __LINE__, __VA_ARGS__)
|
||||
#else
|
||||
#define debug(...) do { } while(0)
|
||||
#endif
|
||||
#define debug(...) debug(__FILE__, FUNCTION_MACRO_NAME, __LINE__, __VA_ARGS__)
|
||||
#endif
|
||||
#else
|
||||
#define debug(...) do { } while(0)
|
||||
#endif
|
||||
|
||||
extern Log* g_pLog;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -59,7 +59,7 @@ void LoggableFrontend::Run()
|
||||
while (!IsStopped())
|
||||
{
|
||||
Update();
|
||||
usleep(200 * 1000);
|
||||
usleep(m_iUpdateInterval * 1000);
|
||||
}
|
||||
// Printing the last messages
|
||||
Update();
|
||||
@@ -106,25 +106,36 @@ void LoggableFrontend::Update()
|
||||
|
||||
void LoggableFrontend::PrintMessage(Message * pMessage)
|
||||
{
|
||||
#ifdef WIN32
|
||||
char* msg = strdup(pMessage->GetText());
|
||||
CharToOem(msg, msg);
|
||||
#else
|
||||
const char* msg = pMessage->GetText();
|
||||
#endif
|
||||
switch (pMessage->GetKind())
|
||||
{
|
||||
case Message::mkDebug:
|
||||
fprintf(stdout, "[DEBUG] %s\n", msg);
|
||||
printf("[DEBUG] %s\n", msg);
|
||||
break;
|
||||
case Message::mkError:
|
||||
fprintf(stdout, "[ERROR] %s\n", msg);
|
||||
printf("[ERROR] %s\n", msg);
|
||||
break;
|
||||
case Message::mkWarning:
|
||||
fprintf(stdout, "[WARNING] %s\n", msg);
|
||||
printf("[WARNING] %s\n", msg);
|
||||
break;
|
||||
case Message::mkInfo:
|
||||
fprintf(stdout, "[INFO] %s\n", msg);
|
||||
printf("[INFO] %s\n", msg);
|
||||
break;
|
||||
case Message::mkDetail:
|
||||
printf("[DETAIL] %s\n", msg);
|
||||
break;
|
||||
}
|
||||
#ifdef WIN32
|
||||
free(msg);
|
||||
#endif
|
||||
}
|
||||
|
||||
void LoggableFrontend::PrintSkip()
|
||||
{
|
||||
fprintf(stdout, ".....\n");
|
||||
printf(".....\n");
|
||||
}
|
||||
|
||||
@@ -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$
|
||||
|
||||
182
Makefile.am
182
Makefile.am
@@ -1,18 +1,170 @@
|
||||
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 RemoteServer.cpp RemoteServer.h \
|
||||
NCursesFrontend.cpp NCursesFrontend.h NNTPConnection.cpp NNTPConnection.h RemoteClient.cpp \
|
||||
RemoteClient.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 ServerPool.cpp ServerPool.h Thread.cpp Thread.h Util.cpp Util.h \
|
||||
nzbget.cpp nzbget.h
|
||||
#
|
||||
# This file if part of nzbget
|
||||
#
|
||||
# Copyright (C) 2008-2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
EXTRA_DIST = nzbget.conf.example \
|
||||
win32.h NTService.cpp NTService.h \
|
||||
libpar2-0.2-MSVC8.patch libsigc++-2.0.18-MSVC8.patch \
|
||||
nzbget.kdevelop nzbget.sln nzbget.vcproj
|
||||
bin_PROGRAMS = nzbget
|
||||
|
||||
nzbget_SOURCES = \
|
||||
ArticleDownloader.cpp ArticleDownloader.h BinRpc.cpp BinRpc.h \
|
||||
ColoredFrontend.cpp ColoredFrontend.h Connection.cpp Connection.h Decoder.cpp Decoder.h \
|
||||
DiskState.cpp DiskState.h DownloadInfo.cpp DownloadInfo.h 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 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 Scanner.cpp Scanner.h Scheduler.cpp Scheduler.h ScriptController.cpp \
|
||||
ScriptController.h ServerPool.cpp ServerPool.h svn_version.cpp TLS.cpp TLS.h Thread.cpp Thread.h \
|
||||
Util.cpp Util.h XmlRpc.cpp XmlRpc.h WebDownloader.cpp WebDownloader.h WebServer.cpp WebServer.h \
|
||||
UrlCoordinator.cpp UrlCoordinator.h nzbget.cpp nzbget.h
|
||||
|
||||
EXTRA_DIST = \
|
||||
Makefile.cvs nzbgetd nzbget-postprocess.sh \
|
||||
$(patches_FILES) $(windows_FILES)
|
||||
|
||||
patches_FILES = \
|
||||
libpar2-0.2-bugfixes.patch libpar2-0.2-cancel.patch \
|
||||
libpar2-0.2-MSVC8.patch libsigc++-2.0.18-MSVC8.patch
|
||||
|
||||
windows_FILES = \
|
||||
win32.h NTService.cpp NTService.h nzbget.sln nzbget.vcproj nzbget-shell.bat
|
||||
|
||||
doc_FILES = \
|
||||
README ChangeLog COPYING
|
||||
|
||||
exampleconf_FILES = \
|
||||
nzbget.conf nzbget-postprocess.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/lib/bootstrap.js webui/lib/bootstrap.min.js webui/lib/bootstrap.css \
|
||||
webui/lib/jquery.js webui/lib/jquery.min.js \
|
||||
webui/img/icons.png webui/img/icons-2x.png \
|
||||
webui/img/transmit.gif webui/img/transmit-file.gif webui/img/favicon.ico \
|
||||
webui/img/download-anim-green-2x.png webui/img/download-anim-orange-2x.png \
|
||||
webui/img/transmit-reload-2x.gif
|
||||
|
||||
# Install
|
||||
sbin_SCRIPTS = nzbgetd
|
||||
bin_SCRIPTS = nzbget-postprocess.sh
|
||||
dist_doc_DATA = $(doc_FILES)
|
||||
exampleconfdir = $(datadir)/nzbget
|
||||
dist_exampleconf_DATA = $(exampleconf_FILES)
|
||||
webuiconfdir = $(datadir)/nzbget/webui
|
||||
dist_webuiconf_DATA = $(exampleconf_FILES)
|
||||
webuidir = $(datadir)/nzbget
|
||||
nobase_dist_webui_DATA = $(webui_FILES)
|
||||
|
||||
# Note about "sed":
|
||||
# We need to make some changes in installed files.
|
||||
# On Linux "sed" has option "-i" for in-place-edit. Unfortunateley the BSD version of "sed"
|
||||
# has incompatible syntax. To solve the problem we perform in-place-edit in three steps:
|
||||
# 1) copy the original file to original.temp (delete existing original.temp, if any);
|
||||
# 2) sed < original.temp > original
|
||||
# 3) delete original.temp
|
||||
# These steps ensure that the output file has the same permissions as the original file.
|
||||
|
||||
# Configure installed script
|
||||
install-exec-hook:
|
||||
rm -f "$(DESTDIR)$(sbindir)/nzbgetd.temp"
|
||||
cp "$(DESTDIR)$(sbindir)/nzbgetd" "$(DESTDIR)$(sbindir)/nzbgetd.temp"
|
||||
sed 's?/usr/local/bin?$(bindir)?' < "$(DESTDIR)$(sbindir)/nzbgetd.temp" > "$(DESTDIR)$(sbindir)/nzbgetd"
|
||||
rm "$(DESTDIR)$(sbindir)/nzbgetd.temp"
|
||||
|
||||
# Prepare example configuration files
|
||||
install-data-hook:
|
||||
rm -f "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
|
||||
cp "$(DESTDIR)$(exampleconfdir)/nzbget.conf" "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
|
||||
sed 's:"nzbget-postprocess.sh":"nzbget-postprocess.sh" (installed into $(bindir)):' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
|
||||
sed 's:^WebDir=:WebDir=$(webuidir)/webui:' < "$(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" ; \
|
||||
rm -f "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" ; \
|
||||
cp "$(DESTDIR)$(sysconfdir)/nzbget.conf" "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" ; \
|
||||
sed 's:^PostProcess=:PostProcess=$(bindir)/nzbget-postprocess.sh:' < "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(sysconfdir)/nzbget.conf" ; \
|
||||
rm "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" ; \
|
||||
fi
|
||||
if test ! -f "$(DESTDIR)$(sysconfdir)/nzbget-postprocess.conf" ; then \
|
||||
$(mkinstalldirs) "$(DESTDIR)$(sysconfdir)" ; \
|
||||
cp "$(DESTDIR)$(exampleconfdir)/nzbget-postprocess.conf" "$(DESTDIR)$(sysconfdir)/nzbget-postprocess.conf" ; \
|
||||
fi
|
||||
|
||||
uninstall-conf:
|
||||
rm -f "$(DESTDIR)$(sysconfdir)/nzbget-postprocess.conf"
|
||||
rm -f "$(DESTDIR)$(sysconfdir)/nzbget.conf"
|
||||
|
||||
# Determining subversion revision:
|
||||
# 1) If directory ".svn" exists we take revision from it using program svnversion (part of subversion package)
|
||||
# File is recreated only if revision number was changed.
|
||||
# 2) If directory ".svn" doesn't exists we keep and reuse file "svn_version.cpp",
|
||||
# which was possibly created early.
|
||||
# 3) If neither directory ".svn" nor file "svn_version.cpp" are available
|
||||
# we create new file "svn_version.c" with empty revision number.
|
||||
svn_version.cpp: FORCE
|
||||
@ if test -d ./.svn ; then \
|
||||
V="$(shell svnversion -n .)"; \
|
||||
H="$(shell test -f ./svn_version.cpp && head -n 1 svn_version.cpp)"; \
|
||||
if test "/* $$V */" != "$$H" ; then \
|
||||
( \
|
||||
echo "/* $$V */" ;\
|
||||
echo "/* This file is automatically regenerated on each build. Do not edit it. */" ;\
|
||||
echo "const char* svn_version(void)" ;\
|
||||
echo "{" ;\
|
||||
echo " const char* SVN_Version = \"$$V\";" ;\
|
||||
echo " return SVN_Version;" ;\
|
||||
echo "}" ;\
|
||||
) > svn_version.cpp ; \
|
||||
fi \
|
||||
elif test -f ./svn_version.cpp ; then \
|
||||
test "ok, reuse existing file"; \
|
||||
else \
|
||||
( \
|
||||
echo "/* */" ;\
|
||||
echo "/* This file is automatically regenerated on each build. Do not edit it. */" ;\
|
||||
echo "const char* svn_version(void)" ;\
|
||||
echo "{" ;\
|
||||
echo " const char* SVN_Version = \"\";" ;\
|
||||
echo " return SVN_Version;" ;\
|
||||
echo "}" ;\
|
||||
) > svn_version.cpp ; \
|
||||
fi
|
||||
FORCE:
|
||||
|
||||
# Ignore "svn_version.cpp" in distcleancheck
|
||||
distcleancheck_listfiles = \
|
||||
find . -type f -exec sh -c 'test -f $(srcdir)/$$1 || echo $$1' \
|
||||
sh '{}' ';'
|
||||
|
||||
clean-bak: rm *~
|
||||
|
||||
# Fix premissions
|
||||
dist-hook:
|
||||
chmod -x $(distdir)/*.cpp $(distdir)/*.h
|
||||
find $(distdir)/webui -type f -print -exec chmod -x {} \;
|
||||
|
||||
|
||||
8
Makefile.cvs
Normal file
8
Makefile.cvs
Normal file
@@ -0,0 +1,8 @@
|
||||
default: all
|
||||
|
||||
all:
|
||||
aclocal
|
||||
autoheader
|
||||
automake
|
||||
autoconf
|
||||
|
||||
426
Makefile.in
426
Makefile.in
@@ -1,4 +1,4 @@
|
||||
# Makefile.in generated by automake 1.9.5 from Makefile.am.
|
||||
# Makefile.in generated by automake 1.9.6 from Makefile.am.
|
||||
# @configure_input@
|
||||
|
||||
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
|
||||
@@ -14,7 +14,28 @@
|
||||
|
||||
@SET_MAKE@
|
||||
|
||||
SOURCES = $(nzbget_SOURCES)
|
||||
#
|
||||
# This file if part of nzbget
|
||||
#
|
||||
# Copyright (C) 2008-2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
@@ -38,8 +59,11 @@ PRE_UNINSTALL = :
|
||||
POST_UNINSTALL = :
|
||||
build_triplet = @build@
|
||||
host_triplet = @host@
|
||||
target_triplet = @target@
|
||||
bin_PROGRAMS = nzbget$(EXEEXT)
|
||||
DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \
|
||||
DIST_COMMON = README $(am__configure_deps) $(dist_doc_DATA) \
|
||||
$(dist_exampleconf_DATA) $(dist_webuiconf_DATA) \
|
||||
$(nobase_dist_webui_DATA) $(srcdir)/Makefile.am \
|
||||
$(srcdir)/Makefile.in $(srcdir)/config.h.in \
|
||||
$(top_srcdir)/configure AUTHORS COPYING ChangeLog INSTALL NEWS \
|
||||
config.guess config.sub depcomp install-sh missing
|
||||
@@ -53,22 +77,31 @@ am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
|
||||
mkinstalldirs = $(install_sh) -d
|
||||
CONFIG_HEADER = config.h
|
||||
CONFIG_CLEAN_FILES =
|
||||
am__installdirs = "$(DESTDIR)$(bindir)"
|
||||
am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(bindir)" \
|
||||
"$(DESTDIR)$(sbindir)" "$(DESTDIR)$(docdir)" \
|
||||
"$(DESTDIR)$(exampleconfdir)" "$(DESTDIR)$(webuiconfdir)" \
|
||||
"$(DESTDIR)$(webuidir)"
|
||||
binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
|
||||
PROGRAMS = $(bin_PROGRAMS)
|
||||
am_nzbget_OBJECTS = ArticleDownloader.$(OBJEXT) \
|
||||
am_nzbget_OBJECTS = ArticleDownloader.$(OBJEXT) BinRpc.$(OBJEXT) \
|
||||
ColoredFrontend.$(OBJEXT) Connection.$(OBJEXT) \
|
||||
Decoder.$(OBJEXT) DiskState.$(OBJEXT) DownloadInfo.$(OBJEXT) \
|
||||
Frontend.$(OBJEXT) Log.$(OBJEXT) LoggableFrontend.$(OBJEXT) \
|
||||
RemoteServer.$(OBJEXT) NCursesFrontend.$(OBJEXT) \
|
||||
NNTPConnection.$(OBJEXT) RemoteClient.$(OBJEXT) \
|
||||
NZBFile.$(OBJEXT) NetAddress.$(OBJEXT) NewsServer.$(OBJEXT) \
|
||||
Observer.$(OBJEXT) Options.$(OBJEXT) ParChecker.$(OBJEXT) \
|
||||
NCursesFrontend.$(OBJEXT) NNTPConnection.$(OBJEXT) \
|
||||
NZBFile.$(OBJEXT) NewsServer.$(OBJEXT) Observer.$(OBJEXT) \
|
||||
Options.$(OBJEXT) ParChecker.$(OBJEXT) \
|
||||
PrePostProcessor.$(OBJEXT) QueueCoordinator.$(OBJEXT) \
|
||||
ServerPool.$(OBJEXT) Thread.$(OBJEXT) Util.$(OBJEXT) \
|
||||
nzbget.$(OBJEXT)
|
||||
QueueEditor.$(OBJEXT) RemoteClient.$(OBJEXT) \
|
||||
RemoteServer.$(OBJEXT) Scanner.$(OBJEXT) Scheduler.$(OBJEXT) \
|
||||
ScriptController.$(OBJEXT) ServerPool.$(OBJEXT) \
|
||||
svn_version.$(OBJEXT) TLS.$(OBJEXT) Thread.$(OBJEXT) \
|
||||
Util.$(OBJEXT) XmlRpc.$(OBJEXT) WebDownloader.$(OBJEXT) \
|
||||
WebServer.$(OBJEXT) UrlCoordinator.$(OBJEXT) nzbget.$(OBJEXT)
|
||||
nzbget_OBJECTS = $(am_nzbget_OBJECTS)
|
||||
nzbget_LDADD = $(LDADD)
|
||||
binSCRIPT_INSTALL = $(INSTALL_SCRIPT)
|
||||
sbinSCRIPT_INSTALL = $(INSTALL_SCRIPT)
|
||||
SCRIPTS = $(bin_SCRIPTS) $(sbin_SCRIPTS)
|
||||
DEFAULT_INCLUDES = -I. -I$(srcdir) -I.
|
||||
depcomp = $(SHELL) $(top_srcdir)/depcomp
|
||||
am__depfiles_maybe = depfiles
|
||||
@@ -83,6 +116,18 @@ CCLD = $(CC)
|
||||
LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
|
||||
SOURCES = $(nzbget_SOURCES)
|
||||
DIST_SOURCES = $(nzbget_SOURCES)
|
||||
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
|
||||
am__vpath_adj = case $$p in \
|
||||
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
|
||||
*) f=$$p;; \
|
||||
esac;
|
||||
am__strip_dir = `echo $$p | sed -e 's|^.*/||'`;
|
||||
dist_docDATA_INSTALL = $(INSTALL_DATA)
|
||||
dist_exampleconfDATA_INSTALL = $(INSTALL_DATA)
|
||||
dist_webuiconfDATA_INSTALL = $(INSTALL_DATA)
|
||||
nobase_dist_webuiDATA_INSTALL = $(install_sh_DATA)
|
||||
DATA = $(dist_doc_DATA) $(dist_exampleconf_DATA) \
|
||||
$(dist_webuiconf_DATA) $(nobase_dist_webui_DATA)
|
||||
ETAGS = etags
|
||||
CTAGS = ctags
|
||||
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
|
||||
@@ -95,7 +140,6 @@ am__remove_distdir = \
|
||||
DIST_ARCHIVES = $(distdir).tar.gz
|
||||
GZIP_ENV = --best
|
||||
distuninstallcheck_listfiles = find . -type f -print
|
||||
distcleancheck_listfiles = find . -type f -print
|
||||
ACLOCAL = @ACLOCAL@
|
||||
ADDSRCS = @ADDSRCS@
|
||||
AMDEP_FALSE = @AMDEP_FALSE@
|
||||
@@ -106,11 +150,7 @@ AUTOCONF = @AUTOCONF@
|
||||
AUTOHEADER = @AUTOHEADER@
|
||||
AUTOMAKE = @AUTOMAKE@
|
||||
AWK = @AWK@
|
||||
CC = @CC@
|
||||
CCDEPMODE = @CCDEPMODE@
|
||||
CFLAGS = @CFLAGS@
|
||||
CP = @CP@
|
||||
CPP = @CPP@
|
||||
CPPFLAGS = @CPPFLAGS@
|
||||
CXX = @CXX@
|
||||
CXXCPP = @CXXCPP@
|
||||
@@ -124,7 +164,7 @@ ECHO_N = @ECHO_N@
|
||||
ECHO_T = @ECHO_T@
|
||||
EGREP = @EGREP@
|
||||
EXEEXT = @EXEEXT@
|
||||
FALSE = @FALSE@
|
||||
GREP = @GREP@
|
||||
INSTALL_DATA = @INSTALL_DATA@
|
||||
INSTALL_PROGRAM = @INSTALL_PROGRAM@
|
||||
INSTALL_SCRIPT = @INSTALL_SCRIPT@
|
||||
@@ -132,12 +172,9 @@ INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
|
||||
LDFLAGS = @LDFLAGS@
|
||||
LIBOBJS = @LIBOBJS@
|
||||
LIBS = @LIBS@
|
||||
LN = @LN@
|
||||
LTLIBOBJS = @LTLIBOBJS@
|
||||
MAKE = @MAKE@
|
||||
MAKEINFO = @MAKEINFO@
|
||||
MKDIR = @MKDIR@
|
||||
MV = @MV@
|
||||
OBJEXT = @OBJEXT@
|
||||
PACKAGE = @PACKAGE@
|
||||
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
|
||||
@@ -146,20 +183,13 @@ PACKAGE_STRING = @PACKAGE_STRING@
|
||||
PACKAGE_TARNAME = @PACKAGE_TARNAME@
|
||||
PACKAGE_VERSION = @PACKAGE_VERSION@
|
||||
PATH_SEPARATOR = @PATH_SEPARATOR@
|
||||
RANLIB = @RANLIB@
|
||||
RM = @RM@
|
||||
PKG_CONFIG = @PKG_CONFIG@
|
||||
SET_MAKE = @SET_MAKE@
|
||||
SHELL = @SHELL@
|
||||
STRIP = @STRIP@
|
||||
TAR = @TAR@
|
||||
TRUE = @TRUE@
|
||||
VERSION = @VERSION@
|
||||
ac_ct_CC = @ac_ct_CC@
|
||||
ac_ct_CXX = @ac_ct_CXX@
|
||||
ac_ct_RANLIB = @ac_ct_RANLIB@
|
||||
ac_ct_STRIP = @ac_ct_STRIP@
|
||||
am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
|
||||
am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
|
||||
am__fastdepCXX_FALSE = @am__fastdepCXX_FALSE@
|
||||
am__fastdepCXX_TRUE = @am__fastdepCXX_TRUE@
|
||||
am__include = @am__include@
|
||||
@@ -174,42 +204,101 @@ build_cpu = @build_cpu@
|
||||
build_os = @build_os@
|
||||
build_vendor = @build_vendor@
|
||||
datadir = @datadir@
|
||||
datarootdir = @datarootdir@
|
||||
docdir = @docdir@
|
||||
dvidir = @dvidir@
|
||||
exec_prefix = @exec_prefix@
|
||||
host = @host@
|
||||
host_alias = @host_alias@
|
||||
host_cpu = @host_cpu@
|
||||
host_os = @host_os@
|
||||
host_vendor = @host_vendor@
|
||||
htmldir = @htmldir@
|
||||
includedir = @includedir@
|
||||
infodir = @infodir@
|
||||
install_sh = @install_sh@
|
||||
libdir = @libdir@
|
||||
libexecdir = @libexecdir@
|
||||
libsigc_CFLAGS = @libsigc_CFLAGS@
|
||||
libsigc_LIBS = @libsigc_LIBS@
|
||||
libxml2_CFLAGS = @libxml2_CFLAGS@
|
||||
libxml2_LIBS = @libxml2_LIBS@
|
||||
localedir = @localedir@
|
||||
localstatedir = @localstatedir@
|
||||
mandir = @mandir@
|
||||
mkdir_p = @mkdir_p@
|
||||
oldincludedir = @oldincludedir@
|
||||
pdfdir = @pdfdir@
|
||||
prefix = @prefix@
|
||||
program_transform_name = @program_transform_name@
|
||||
psdir = @psdir@
|
||||
sbindir = @sbindir@
|
||||
sharedstatedir = @sharedstatedir@
|
||||
sysconfdir = @sysconfdir@
|
||||
target = @target@
|
||||
target_alias = @target_alias@
|
||||
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 RemoteServer.cpp RemoteServer.h \
|
||||
NCursesFrontend.cpp NCursesFrontend.h NNTPConnection.cpp NNTPConnection.h RemoteClient.cpp \
|
||||
RemoteClient.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 ServerPool.cpp ServerPool.h Thread.cpp Thread.h Util.cpp Util.h \
|
||||
nzbget.cpp nzbget.h
|
||||
target_cpu = @target_cpu@
|
||||
target_os = @target_os@
|
||||
target_vendor = @target_vendor@
|
||||
nzbget_SOURCES = \
|
||||
ArticleDownloader.cpp ArticleDownloader.h BinRpc.cpp BinRpc.h \
|
||||
ColoredFrontend.cpp ColoredFrontend.h Connection.cpp Connection.h Decoder.cpp Decoder.h \
|
||||
DiskState.cpp DiskState.h DownloadInfo.cpp DownloadInfo.h 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 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 Scanner.cpp Scanner.h Scheduler.cpp Scheduler.h ScriptController.cpp \
|
||||
ScriptController.h ServerPool.cpp ServerPool.h svn_version.cpp TLS.cpp TLS.h Thread.cpp Thread.h \
|
||||
Util.cpp Util.h XmlRpc.cpp XmlRpc.h WebDownloader.cpp WebDownloader.h WebServer.cpp WebServer.h \
|
||||
UrlCoordinator.cpp UrlCoordinator.h nzbget.cpp nzbget.h
|
||||
|
||||
EXTRA_DIST = nzbget.conf.example \
|
||||
win32.h NTService.cpp NTService.h \
|
||||
libpar2-0.2-MSVC8.patch libsigc++-2.0.18-MSVC8.patch \
|
||||
nzbget.kdevelop nzbget.sln nzbget.vcproj
|
||||
EXTRA_DIST = \
|
||||
Makefile.cvs nzbgetd nzbget-postprocess.sh \
|
||||
$(patches_FILES) $(windows_FILES)
|
||||
|
||||
patches_FILES = \
|
||||
libpar2-0.2-bugfixes.patch libpar2-0.2-cancel.patch \
|
||||
libpar2-0.2-MSVC8.patch libsigc++-2.0.18-MSVC8.patch
|
||||
|
||||
windows_FILES = \
|
||||
win32.h NTService.cpp NTService.h nzbget.sln nzbget.vcproj nzbget-shell.bat
|
||||
|
||||
doc_FILES = \
|
||||
README ChangeLog COPYING
|
||||
|
||||
exampleconf_FILES = \
|
||||
nzbget.conf nzbget-postprocess.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/lib/bootstrap.js webui/lib/bootstrap.min.js webui/lib/bootstrap.css \
|
||||
webui/lib/jquery.js webui/lib/jquery.min.js \
|
||||
webui/img/icons.png webui/img/icons-2x.png \
|
||||
webui/img/transmit.gif webui/img/transmit-file.gif webui/img/favicon.ico \
|
||||
webui/img/download-anim-green-2x.png webui/img/download-anim-orange-2x.png \
|
||||
webui/img/transmit-reload-2x.gif
|
||||
|
||||
|
||||
# Install
|
||||
sbin_SCRIPTS = nzbgetd
|
||||
bin_SCRIPTS = nzbget-postprocess.sh
|
||||
dist_doc_DATA = $(doc_FILES)
|
||||
exampleconfdir = $(datadir)/nzbget
|
||||
dist_exampleconf_DATA = $(exampleconf_FILES)
|
||||
webuiconfdir = $(datadir)/nzbget/webui
|
||||
dist_webuiconf_DATA = $(exampleconf_FILES)
|
||||
webuidir = $(datadir)/nzbget
|
||||
nobase_dist_webui_DATA = $(webui_FILES)
|
||||
|
||||
# Ignore "svn_version.cpp" in distcleancheck
|
||||
distcleancheck_listfiles = \
|
||||
find . -type f -exec sh -c 'test -f $(srcdir)/$$1 || echo $$1' \
|
||||
sh '{}' ';'
|
||||
|
||||
all: config.h
|
||||
$(MAKE) $(AM_MAKEFLAGS) all-am
|
||||
@@ -292,6 +381,44 @@ clean-binPROGRAMS:
|
||||
nzbget$(EXEEXT): $(nzbget_OBJECTS) $(nzbget_DEPENDENCIES)
|
||||
@rm -f nzbget$(EXEEXT)
|
||||
$(CXXLINK) $(nzbget_LDFLAGS) $(nzbget_OBJECTS) $(nzbget_LDADD) $(LIBS)
|
||||
install-binSCRIPTS: $(bin_SCRIPTS)
|
||||
@$(NORMAL_INSTALL)
|
||||
test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)"
|
||||
@list='$(bin_SCRIPTS)'; for p in $$list; do \
|
||||
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
|
||||
if test -f $$d$$p; then \
|
||||
f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
|
||||
echo " $(binSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(bindir)/$$f'"; \
|
||||
$(binSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(bindir)/$$f"; \
|
||||
else :; fi; \
|
||||
done
|
||||
|
||||
uninstall-binSCRIPTS:
|
||||
@$(NORMAL_UNINSTALL)
|
||||
@list='$(bin_SCRIPTS)'; for p in $$list; do \
|
||||
f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
|
||||
echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
|
||||
rm -f "$(DESTDIR)$(bindir)/$$f"; \
|
||||
done
|
||||
install-sbinSCRIPTS: $(sbin_SCRIPTS)
|
||||
@$(NORMAL_INSTALL)
|
||||
test -z "$(sbindir)" || $(mkdir_p) "$(DESTDIR)$(sbindir)"
|
||||
@list='$(sbin_SCRIPTS)'; for p in $$list; do \
|
||||
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
|
||||
if test -f $$d$$p; then \
|
||||
f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
|
||||
echo " $(sbinSCRIPT_INSTALL) '$$d$$p' '$(DESTDIR)$(sbindir)/$$f'"; \
|
||||
$(sbinSCRIPT_INSTALL) "$$d$$p" "$(DESTDIR)$(sbindir)/$$f"; \
|
||||
else :; fi; \
|
||||
done
|
||||
|
||||
uninstall-sbinSCRIPTS:
|
||||
@$(NORMAL_UNINSTALL)
|
||||
@list='$(sbin_SCRIPTS)'; for p in $$list; do \
|
||||
f=`echo "$$p" | sed 's|^.*/||;$(transform)'`; \
|
||||
echo " rm -f '$(DESTDIR)$(sbindir)/$$f'"; \
|
||||
rm -f "$(DESTDIR)$(sbindir)/$$f"; \
|
||||
done
|
||||
|
||||
mostlyclean-compile:
|
||||
-rm -f *.$(OBJEXT)
|
||||
@@ -300,6 +427,7 @@ distclean-compile:
|
||||
-rm -f *.tab.c
|
||||
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ArticleDownloader.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/BinRpc.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ColoredFrontend.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Connection.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Decoder.Po@am__quote@
|
||||
@@ -311,19 +439,28 @@ distclean-compile:
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NCursesFrontend.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NNTPConnection.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NZBFile.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NetAddress.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/NewsServer.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Observer.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Options.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ParChecker.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PrePostProcessor.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/QueueCoordinator.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/QueueEditor.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RemoteClient.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/RemoteServer.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Scanner.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Scheduler.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ScriptController.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ServerPool.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/TLS.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Thread.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/UrlCoordinator.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Util.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/WebDownloader.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/WebServer.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/XmlRpc.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/nzbget.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/svn_version.Po@am__quote@
|
||||
|
||||
.cpp.o:
|
||||
@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
|
||||
@@ -339,6 +476,76 @@ distclean-compile:
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
|
||||
uninstall-info-am:
|
||||
install-dist_docDATA: $(dist_doc_DATA)
|
||||
@$(NORMAL_INSTALL)
|
||||
test -z "$(docdir)" || $(mkdir_p) "$(DESTDIR)$(docdir)"
|
||||
@list='$(dist_doc_DATA)'; for p in $$list; do \
|
||||
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
|
||||
f=$(am__strip_dir) \
|
||||
echo " $(dist_docDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(docdir)/$$f'"; \
|
||||
$(dist_docDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(docdir)/$$f"; \
|
||||
done
|
||||
|
||||
uninstall-dist_docDATA:
|
||||
@$(NORMAL_UNINSTALL)
|
||||
@list='$(dist_doc_DATA)'; for p in $$list; do \
|
||||
f=$(am__strip_dir) \
|
||||
echo " rm -f '$(DESTDIR)$(docdir)/$$f'"; \
|
||||
rm -f "$(DESTDIR)$(docdir)/$$f"; \
|
||||
done
|
||||
install-dist_exampleconfDATA: $(dist_exampleconf_DATA)
|
||||
@$(NORMAL_INSTALL)
|
||||
test -z "$(exampleconfdir)" || $(mkdir_p) "$(DESTDIR)$(exampleconfdir)"
|
||||
@list='$(dist_exampleconf_DATA)'; for p in $$list; do \
|
||||
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
|
||||
f=$(am__strip_dir) \
|
||||
echo " $(dist_exampleconfDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(exampleconfdir)/$$f'"; \
|
||||
$(dist_exampleconfDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(exampleconfdir)/$$f"; \
|
||||
done
|
||||
|
||||
uninstall-dist_exampleconfDATA:
|
||||
@$(NORMAL_UNINSTALL)
|
||||
@list='$(dist_exampleconf_DATA)'; for p in $$list; do \
|
||||
f=$(am__strip_dir) \
|
||||
echo " rm -f '$(DESTDIR)$(exampleconfdir)/$$f'"; \
|
||||
rm -f "$(DESTDIR)$(exampleconfdir)/$$f"; \
|
||||
done
|
||||
install-dist_webuiconfDATA: $(dist_webuiconf_DATA)
|
||||
@$(NORMAL_INSTALL)
|
||||
test -z "$(webuiconfdir)" || $(mkdir_p) "$(DESTDIR)$(webuiconfdir)"
|
||||
@list='$(dist_webuiconf_DATA)'; for p in $$list; do \
|
||||
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
|
||||
f=$(am__strip_dir) \
|
||||
echo " $(dist_webuiconfDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(webuiconfdir)/$$f'"; \
|
||||
$(dist_webuiconfDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(webuiconfdir)/$$f"; \
|
||||
done
|
||||
|
||||
uninstall-dist_webuiconfDATA:
|
||||
@$(NORMAL_UNINSTALL)
|
||||
@list='$(dist_webuiconf_DATA)'; for p in $$list; do \
|
||||
f=$(am__strip_dir) \
|
||||
echo " rm -f '$(DESTDIR)$(webuiconfdir)/$$f'"; \
|
||||
rm -f "$(DESTDIR)$(webuiconfdir)/$$f"; \
|
||||
done
|
||||
install-nobase_dist_webuiDATA: $(nobase_dist_webui_DATA)
|
||||
@$(NORMAL_INSTALL)
|
||||
test -z "$(webuidir)" || $(mkdir_p) "$(DESTDIR)$(webuidir)"
|
||||
@$(am__vpath_adj_setup) \
|
||||
list='$(nobase_dist_webui_DATA)'; for p in $$list; do \
|
||||
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
|
||||
$(am__vpath_adj) \
|
||||
echo " $(nobase_dist_webuiDATA_INSTALL) '$$d$$p' '$(DESTDIR)$(webuidir)/$$f'"; \
|
||||
$(nobase_dist_webuiDATA_INSTALL) "$$d$$p" "$(DESTDIR)$(webuidir)/$$f"; \
|
||||
done
|
||||
|
||||
uninstall-nobase_dist_webuiDATA:
|
||||
@$(NORMAL_UNINSTALL)
|
||||
@$(am__vpath_adj_setup) \
|
||||
list='$(nobase_dist_webui_DATA)'; for p in $$list; do \
|
||||
$(am__vpath_adj) \
|
||||
echo " rm -f '$(DESTDIR)$(webuidir)/$$f'"; \
|
||||
rm -f "$(DESTDIR)$(webuidir)/$$f"; \
|
||||
done
|
||||
|
||||
ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
|
||||
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
|
||||
@@ -391,6 +598,7 @@ distclean-tags:
|
||||
distdir: $(DISTFILES)
|
||||
$(am__remove_distdir)
|
||||
mkdir $(distdir)
|
||||
$(mkdir_p) $(distdir)/webui $(distdir)/webui/img $(distdir)/webui/lib
|
||||
@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
|
||||
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
|
||||
list='$(DISTFILES)'; for file in $$list; do \
|
||||
@@ -417,6 +625,9 @@ distdir: $(DISTFILES)
|
||||
|| exit 1; \
|
||||
fi; \
|
||||
done
|
||||
$(MAKE) $(AM_MAKEFLAGS) \
|
||||
top_distdir="$(top_distdir)" distdir="$(distdir)" \
|
||||
dist-hook
|
||||
-find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \
|
||||
! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
|
||||
! -type d ! -perm -400 -exec chmod a+r {} \; -o \
|
||||
@@ -516,9 +727,9 @@ distcleancheck: distclean
|
||||
exit 1; } >&2
|
||||
check-am: all-am
|
||||
check: check-am
|
||||
all-am: Makefile $(PROGRAMS) config.h
|
||||
all-am: Makefile $(PROGRAMS) $(SCRIPTS) $(DATA) config.h
|
||||
installdirs:
|
||||
for dir in "$(DESTDIR)$(bindir)"; do \
|
||||
for dir in "$(DESTDIR)$(bindir)" "$(DESTDIR)$(bindir)" "$(DESTDIR)$(sbindir)" "$(DESTDIR)$(docdir)" "$(DESTDIR)$(exampleconfdir)" "$(DESTDIR)$(webuiconfdir)" "$(DESTDIR)$(webuidir)"; do \
|
||||
test -z "$$dir" || $(mkdir_p) "$$dir"; \
|
||||
done
|
||||
install: install-am
|
||||
@@ -566,9 +777,15 @@ info: info-am
|
||||
|
||||
info-am:
|
||||
|
||||
install-data-am:
|
||||
install-data-am: install-dist_docDATA install-dist_exampleconfDATA \
|
||||
install-dist_webuiconfDATA install-nobase_dist_webuiDATA
|
||||
@$(NORMAL_INSTALL)
|
||||
$(MAKE) $(AM_MAKEFLAGS) install-data-hook
|
||||
|
||||
install-exec-am: install-binPROGRAMS
|
||||
install-exec-am: install-binPROGRAMS install-binSCRIPTS \
|
||||
install-sbinSCRIPTS
|
||||
@$(NORMAL_INSTALL)
|
||||
$(MAKE) $(AM_MAKEFLAGS) install-exec-hook
|
||||
|
||||
install-info: install-info-am
|
||||
|
||||
@@ -595,24 +812,119 @@ ps: ps-am
|
||||
|
||||
ps-am:
|
||||
|
||||
uninstall-am: uninstall-binPROGRAMS uninstall-info-am
|
||||
uninstall-am: uninstall-binPROGRAMS uninstall-binSCRIPTS \
|
||||
uninstall-dist_docDATA uninstall-dist_exampleconfDATA \
|
||||
uninstall-dist_webuiconfDATA uninstall-info-am \
|
||||
uninstall-nobase_dist_webuiDATA uninstall-sbinSCRIPTS
|
||||
|
||||
.PHONY: CTAGS GTAGS all all-am am--refresh check check-am clean \
|
||||
clean-binPROGRAMS clean-generic ctags dist dist-all dist-bzip2 \
|
||||
dist-gzip dist-shar dist-tarZ dist-zip distcheck distclean \
|
||||
distclean-compile distclean-generic distclean-hdr \
|
||||
dist-gzip dist-hook dist-shar dist-tarZ dist-zip distcheck \
|
||||
distclean distclean-compile distclean-generic distclean-hdr \
|
||||
distclean-tags distcleancheck distdir distuninstallcheck dvi \
|
||||
dvi-am html html-am info info-am install install-am \
|
||||
install-binPROGRAMS install-data install-data-am install-exec \
|
||||
install-exec-am install-info install-info-am install-man \
|
||||
install-strip installcheck installcheck-am installdirs \
|
||||
maintainer-clean maintainer-clean-generic mostlyclean \
|
||||
mostlyclean-compile mostlyclean-generic pdf pdf-am ps ps-am \
|
||||
tags uninstall uninstall-am uninstall-binPROGRAMS \
|
||||
uninstall-info-am
|
||||
install-binPROGRAMS install-binSCRIPTS install-data \
|
||||
install-data-am install-data-hook install-dist_docDATA \
|
||||
install-dist_exampleconfDATA install-dist_webuiconfDATA \
|
||||
install-exec install-exec-am install-exec-hook install-info \
|
||||
install-info-am install-man install-nobase_dist_webuiDATA \
|
||||
install-sbinSCRIPTS install-strip installcheck installcheck-am \
|
||||
installdirs maintainer-clean maintainer-clean-generic \
|
||||
mostlyclean mostlyclean-compile mostlyclean-generic pdf pdf-am \
|
||||
ps ps-am tags uninstall uninstall-am uninstall-binPROGRAMS \
|
||||
uninstall-binSCRIPTS uninstall-dist_docDATA \
|
||||
uninstall-dist_exampleconfDATA uninstall-dist_webuiconfDATA \
|
||||
uninstall-info-am uninstall-nobase_dist_webuiDATA \
|
||||
uninstall-sbinSCRIPTS
|
||||
|
||||
|
||||
# Note about "sed":
|
||||
# We need to make some changes in installed files.
|
||||
# On Linux "sed" has option "-i" for in-place-edit. Unfortunateley the BSD version of "sed"
|
||||
# has incompatible syntax. To solve the problem we perform in-place-edit in three steps:
|
||||
# 1) copy the original file to original.temp (delete existing original.temp, if any);
|
||||
# 2) sed < original.temp > original
|
||||
# 3) delete original.temp
|
||||
# These steps ensure that the output file has the same permissions as the original file.
|
||||
|
||||
# Configure installed script
|
||||
install-exec-hook:
|
||||
rm -f "$(DESTDIR)$(sbindir)/nzbgetd.temp"
|
||||
cp "$(DESTDIR)$(sbindir)/nzbgetd" "$(DESTDIR)$(sbindir)/nzbgetd.temp"
|
||||
sed 's?/usr/local/bin?$(bindir)?' < "$(DESTDIR)$(sbindir)/nzbgetd.temp" > "$(DESTDIR)$(sbindir)/nzbgetd"
|
||||
rm "$(DESTDIR)$(sbindir)/nzbgetd.temp"
|
||||
|
||||
# Prepare example configuration files
|
||||
install-data-hook:
|
||||
rm -f "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
|
||||
cp "$(DESTDIR)$(exampleconfdir)/nzbget.conf" "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
|
||||
sed 's:"nzbget-postprocess.sh":"nzbget-postprocess.sh" (installed into $(bindir)):' < "$(DESTDIR)$(exampleconfdir)/nzbget.conf" > "$(DESTDIR)$(exampleconfdir)/nzbget.conf.temp"
|
||||
sed 's:^WebDir=:WebDir=$(webuidir)/webui:' < "$(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" ; \
|
||||
rm -f "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" ; \
|
||||
cp "$(DESTDIR)$(sysconfdir)/nzbget.conf" "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" ; \
|
||||
sed 's:^PostProcess=:PostProcess=$(bindir)/nzbget-postprocess.sh:' < "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" > "$(DESTDIR)$(sysconfdir)/nzbget.conf" ; \
|
||||
rm "$(DESTDIR)$(sysconfdir)/nzbget.conf.temp" ; \
|
||||
fi
|
||||
if test ! -f "$(DESTDIR)$(sysconfdir)/nzbget-postprocess.conf" ; then \
|
||||
$(mkinstalldirs) "$(DESTDIR)$(sysconfdir)" ; \
|
||||
cp "$(DESTDIR)$(exampleconfdir)/nzbget-postprocess.conf" "$(DESTDIR)$(sysconfdir)/nzbget-postprocess.conf" ; \
|
||||
fi
|
||||
|
||||
uninstall-conf:
|
||||
rm -f "$(DESTDIR)$(sysconfdir)/nzbget-postprocess.conf"
|
||||
rm -f "$(DESTDIR)$(sysconfdir)/nzbget.conf"
|
||||
|
||||
# Determining subversion revision:
|
||||
# 1) If directory ".svn" exists we take revision from it using program svnversion (part of subversion package)
|
||||
# File is recreated only if revision number was changed.
|
||||
# 2) If directory ".svn" doesn't exists we keep and reuse file "svn_version.cpp",
|
||||
# which was possibly created early.
|
||||
# 3) If neither directory ".svn" nor file "svn_version.cpp" are available
|
||||
# we create new file "svn_version.c" with empty revision number.
|
||||
svn_version.cpp: FORCE
|
||||
@ if test -d ./.svn ; then \
|
||||
V="$(shell svnversion -n .)"; \
|
||||
H="$(shell test -f ./svn_version.cpp && head -n 1 svn_version.cpp)"; \
|
||||
if test "/* $$V */" != "$$H" ; then \
|
||||
( \
|
||||
echo "/* $$V */" ;\
|
||||
echo "/* This file is automatically regenerated on each build. Do not edit it. */" ;\
|
||||
echo "const char* svn_version(void)" ;\
|
||||
echo "{" ;\
|
||||
echo " const char* SVN_Version = \"$$V\";" ;\
|
||||
echo " return SVN_Version;" ;\
|
||||
echo "}" ;\
|
||||
) > svn_version.cpp ; \
|
||||
fi \
|
||||
elif test -f ./svn_version.cpp ; then \
|
||||
test "ok, reuse existing file"; \
|
||||
else \
|
||||
( \
|
||||
echo "/* */" ;\
|
||||
echo "/* This file is automatically regenerated on each build. Do not edit it. */" ;\
|
||||
echo "const char* svn_version(void)" ;\
|
||||
echo "{" ;\
|
||||
echo " const char* SVN_Version = \"\";" ;\
|
||||
echo " return SVN_Version;" ;\
|
||||
echo "}" ;\
|
||||
) > svn_version.cpp ; \
|
||||
fi
|
||||
FORCE:
|
||||
|
||||
clean-bak: rm *~
|
||||
|
||||
# Fix premissions
|
||||
dist-hook:
|
||||
chmod -x $(distdir)/*.cpp $(distdir)/*.h
|
||||
find $(distdir)/webui -type f -print -exec chmod -x {} \;
|
||||
# Tell versions [3.59,3.63) of GNU make to not export all variables.
|
||||
# Otherwise a system limit (for SysV at least) may be exceeded.
|
||||
.NOEXPORT:
|
||||
|
||||
567
MessageBase.h
567
MessageBase.h
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -27,161 +27,526 @@
|
||||
#ifndef MESSAGEBASE_H
|
||||
#define MESSAGEBASE_H
|
||||
|
||||
static const int32_t NZBMESSAGE_SIGNATURE = 0x6E7A6201; // = "nzb"-version-1
|
||||
static const int32_t NZBMESSAGE_SIGNATURE = 0x6E7A6212; // = "nzb-XX" (protocol version)
|
||||
static const int NZBREQUESTFILENAMESIZE = 512;
|
||||
static const int NZBREQUESTPASSWORDSIZE = 32;
|
||||
|
||||
// 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
|
||||
/**
|
||||
* 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 ends with NULL-char.
|
||||
*
|
||||
* NOTE:
|
||||
* NZBGet communication protocol is intended for usage only by NZBGet itself.
|
||||
* The communication works only if server and client has the same version.
|
||||
* The compatibility with previous program versions is not provided.
|
||||
* Third-party programs should use JSON-RPC or XML-RPC to communicate with NZBGet.
|
||||
*/
|
||||
|
||||
namespace NZBMessageRequest
|
||||
// Possible values for field "m_iType" of struct "SNZBRequestBase":
|
||||
enum eRemoteRequest
|
||||
{
|
||||
enum
|
||||
{
|
||||
eRequestDownload = 1,
|
||||
eRequestPauseUnpause,
|
||||
eRequestList,
|
||||
eRequestSetDownloadRate,
|
||||
eRequestDumpDebug,
|
||||
eRequestEditQueue,
|
||||
eRequestLog,
|
||||
eRequestShutdown
|
||||
eRemoteRequestDownload = 1,
|
||||
eRemoteRequestPauseUnpause,
|
||||
eRemoteRequestList,
|
||||
eRemoteRequestSetDownloadRate,
|
||||
eRemoteRequestDumpDebug,
|
||||
eRemoteRequestEditQueue,
|
||||
eRemoteRequestLog,
|
||||
eRemoteRequestShutdown,
|
||||
eRemoteRequestReload,
|
||||
eRemoteRequestVersion,
|
||||
eRemoteRequestPostQueue,
|
||||
eRemoteRequestWriteLog,
|
||||
eRemoteRequestScan,
|
||||
eRemoteRequestHistory,
|
||||
eRemoteRequestDownloadUrl,
|
||||
eRemoteRequestUrlQueue
|
||||
};
|
||||
|
||||
// Possible values for field "m_iAction" of struct "SNZBEditQueueRequest":
|
||||
enum
|
||||
// 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
|
||||
{
|
||||
eActionMoveOffset = 1, // move to m_iOffset relative to the current position in queue
|
||||
eActionMoveTop, // move to top of queue
|
||||
eActionMoveBottom, // move to bottom of queue
|
||||
eActionPause, // pause
|
||||
eActionResume, // resume (unpause)
|
||||
eActionDelete // delete
|
||||
eRemoteEditActionFileMoveOffset = 1, // move files to m_iOffset relative to the current position in download-queue
|
||||
eRemoteEditActionFileMoveTop, // move files to the top of download-queue
|
||||
eRemoteEditActionFileMoveBottom, // move files to the bottom of download-queue
|
||||
eRemoteEditActionFilePause, // pause files
|
||||
eRemoteEditActionFileResume, // resume (unpause) files
|
||||
eRemoteEditActionFileDelete, // delete files
|
||||
eRemoteEditActionFilePauseAllPars, // pause only (all) pars (does not affect other files)
|
||||
eRemoteEditActionFilePauseExtraPars, // pause only (almost all) pars, except main par-file (does not affect other files)
|
||||
eRemoteEditActionFileSetPriority, // set priority for files
|
||||
eRemoteEditActionFileReorder, // (not supported)
|
||||
eRemoteEditActionGroupMoveOffset, // move group to m_iOffset relative to the current position in download-queue
|
||||
eRemoteEditActionGroupMoveTop, // move group to the top of download-queue
|
||||
eRemoteEditActionGroupMoveBottom, // move group to the bottom of download-queue
|
||||
eRemoteEditActionGroupPause, // pause group
|
||||
eRemoteEditActionGroupResume, // resume (unpause) group
|
||||
eRemoteEditActionGroupDelete, // delete group
|
||||
eRemoteEditActionGroupPauseAllPars, // pause only (all) pars (does not affect other files) in group
|
||||
eRemoteEditActionGroupPauseExtraPars, // pause only (almost all) pars in group, except main par-file (does not affect other files)
|
||||
eRemoteEditActionGroupSetPriority, // set priority for groups
|
||||
eRemoteEditActionGroupSetCategory, // set or change category for a group
|
||||
eRemoteEditActionGroupMerge, // merge group
|
||||
eRemoteEditActionGroupSetParameter, // set post-process parameter for group
|
||||
eRemoteEditActionGroupSetName, // set group name (rename group)
|
||||
eRemoteEditActionPostMoveOffset = 51, // move post-job to m_iOffset relative to the current position in post-queue
|
||||
eRemoteEditActionPostMoveTop, // move post-job to the top of post-queue
|
||||
eRemoteEditActionPostMoveBottom, // move post-job to the bottom of post-queue
|
||||
eRemoteEditActionPostDelete, // delete post-job
|
||||
eRemoteEditActionHistoryDelete, // delete history-item
|
||||
eRemoteEditActionHistoryReturn, // move history-item back to download queue
|
||||
eRemoteEditActionHistoryProcess // move history-item back to download queue and start postprocessing
|
||||
};
|
||||
}
|
||||
|
||||
// The basic NZBMessageBase struct
|
||||
struct SNZBMessageBase
|
||||
// Possible values for field "m_iAction" of struct "SNZBPauseUnpauseRequest":
|
||||
enum eRemotePauseUnpauseAction
|
||||
{
|
||||
int32_t m_iId; // Id must be 'nzbg' in integer-value
|
||||
int32_t m_iType; // message type, must be > 0
|
||||
int32_t m_iSize; // Size of the entire struct
|
||||
char m_szPassword[ NZBREQUESTPASSWORDSIZE ]; // Password needs to be in every request
|
||||
eRemotePauseUnpauseActionDownload = 1, // pause/unpause download queue
|
||||
eRemotePauseUnpauseActionDownload2, // pause/unpause download queue (second pause-register)
|
||||
eRemotePauseUnpauseActionPostProcess, // pause/unpause post-processor queue
|
||||
eRemotePauseUnpauseActionScan // pause/unpause scan of incoming nzb-directory
|
||||
};
|
||||
|
||||
// Possible values for field "m_iMatchMode" of struct "SNZBEditQueueRequest":
|
||||
enum eRemoteMatchMode
|
||||
{
|
||||
eRemoteMatchModeID = 1, // ID
|
||||
eRemoteMatchModeName, // Name
|
||||
eRemoteMatchModeRegEx, // RegEx
|
||||
};
|
||||
|
||||
// The basic SNZBRequestBase struct, used in all requests
|
||||
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
|
||||
{
|
||||
SNZBMessageBase m_MessageBase; // Must be the first in the struct
|
||||
char m_szFilename[ NZBREQUESTFILENAMESIZE ];
|
||||
int32_t m_bAddFirst;
|
||||
int32_t m_iTrailingDataLength;
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
char m_szFilename[NZBREQUESTFILENAMESIZE]; // Name of nzb-file, may contain full path (local path on client) or only filename
|
||||
char m_szCategory[NZBREQUESTFILENAMESIZE]; // Category, can be empty
|
||||
int32_t m_bAddFirst; // 1 - add file to the top of download queue
|
||||
int32_t m_bAddPaused; // 1 - pause added files
|
||||
int32_t m_iPriority; // Priority for files (0 - default)
|
||||
int32_t m_iTrailingDataLength; // Length of nzb-file in bytes
|
||||
//char m_szContent[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// A list request
|
||||
// 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
|
||||
{
|
||||
SNZBMessageBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bFileList;
|
||||
int32_t m_bServerState;
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bFileList; // 1 - return file list
|
||||
int32_t m_bServerState; // 1 - return server state
|
||||
int32_t m_iMatchMode; // File/Group match mode, see enum eRemoteMatchMode (only values eRemoteMatchModeID (no filter) and eRemoteMatchModeRegEx are allowed)
|
||||
int32_t m_bMatchGroup; // 0 - match files; 1 - match nzbs (when m_iMatchMode == eRemoteMatchModeRegEx)
|
||||
char m_szPattern[NZBREQUESTFILENAMESIZE]; // RegEx Pattern (when m_iMatchMode == eRemoteMatchModeRegEx)
|
||||
};
|
||||
|
||||
// A list request-answer
|
||||
struct SNZBListRequestAnswer
|
||||
// A list response
|
||||
struct SNZBListResponse
|
||||
{
|
||||
int32_t m_iSize; // Size of the entire struct
|
||||
int32_t m_iEntrySize; // Size of the SNZBListRequestAnswerEntry-struct
|
||||
long long m_lRemainingSize;
|
||||
float m_fDownloadRate;
|
||||
float m_fDownloadLimit;
|
||||
int32_t m_bServerPaused;
|
||||
int32_t m_iThreadCount;
|
||||
int32_t m_iNrTrailingEntries;
|
||||
int32_t m_iTrailingDataLength;
|
||||
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_bDownloadPaused; // 1 - download queue is currently in paused-state
|
||||
int32_t m_bDownload2Paused; // 1 - download queue is currently in paused-state (second pause-register)
|
||||
int32_t m_bDownloadStandBy; // 0 - there are currently downloads running, 1 - no downloads in progress (download queue paused or all download jobs completed)
|
||||
int32_t m_bPostPaused; // 1 - post-processor queue is currently in paused-state
|
||||
int32_t m_bScanPaused; // 1 - scaning of incoming directory is currently in paused-state
|
||||
int32_t m_iThreadCount; // Number of threads running
|
||||
int32_t m_iPostJobCount; // Number of jobs in post-processor queue (including current job)
|
||||
int32_t m_iUpTimeSec; // Server up time in seconds
|
||||
int32_t m_iDownloadTimeSec; // Server download time in seconds (up_time - standby_time)
|
||||
int32_t m_iDownloadedBytesLo; // Amount of data downloaded since server start, Low 32-bits of 64-bit value
|
||||
int32_t m_iDownloadedBytesHi; // Amount of data downloaded since server start, High 32-bits of 64-bit value
|
||||
int32_t m_bRegExValid; // 0 - error in RegEx-pattern, 1 - RegEx-pattern is valid (only when Request has eRemoteMatchModeRegEx)
|
||||
int32_t m_iNrTrailingNZBEntries; // Number of List-NZB-entries, following to this structure
|
||||
int32_t m_iNrTrailingPPPEntries; // Number of List-PPP-entries, following to this structure
|
||||
int32_t m_iNrTrailingFileEntries; // Number of List-File-entries, following to this structure
|
||||
int32_t m_iTrailingDataLength; // Length of all List-entries, following to this structure
|
||||
// SNZBListResponseEntry m_NZBEntries[m_iNrTrailingNZBEntries] // variable sized
|
||||
// SNZBListResponseEntry m_PPPEntries[m_iNrTrailingPPPEntries] // variable sized
|
||||
// SNZBListResponseEntry m_FileEntries[m_iNrTrailingFileEntries] // variable sized
|
||||
};
|
||||
|
||||
// A list request-answer entry
|
||||
struct SNZBListRequestAnswerEntry
|
||||
// A list response nzb entry
|
||||
struct SNZBListResponseNZBEntry
|
||||
{
|
||||
int32_t m_iNZBFilenameLen;
|
||||
int32_t m_iSubjectLen;
|
||||
int32_t m_iFilenameLen;
|
||||
int32_t m_iDestDirLen;
|
||||
int32_t m_iFileSize;
|
||||
int32_t m_bFilenameConfirmed;
|
||||
int32_t m_iRemainingSize;
|
||||
int32_t m_iID;
|
||||
int32_t m_bPaused;
|
||||
//char m_szNZBFilename[0]; // variable sized
|
||||
//char m_szSubject[0]; // variable sized
|
||||
//char m_szFilename[0]; // variable sized
|
||||
//char m_szDestDir[0]; // variable sized
|
||||
int32_t m_iSizeLo; // Size of all files in bytes, Low 32-bits of 64-bit value
|
||||
int32_t m_iSizeHi; // Size of all files in bytes, High 32-bits of 64-bit value
|
||||
int32_t m_bMatch; // 1 - group matches the pattern (only when Request has eRemoteMatchModeRegEx)
|
||||
int32_t m_iFilenameLen; // Length of Filename-string (m_szFilename), following to this record
|
||||
int32_t m_iNameLen; // Length of Name-string (m_szName), following to this record
|
||||
int32_t m_iDestDirLen; // Length of DestDir-string (m_szDestDir), following to this record
|
||||
int32_t m_iCategoryLen; // Length of Category-string (m_szCategory), following to this record
|
||||
int32_t m_iQueuedFilenameLen; // Length of queued file name (m_szQueuedFilename), following to this record
|
||||
//char m_szFilename[m_iFilenameLen]; // variable sized
|
||||
//char m_szName[m_iNameLen]; // variable sized
|
||||
//char m_szDestDir[m_iDestDirLen]; // variable sized
|
||||
//char m_szCategory[m_iCategoryLen]; // variable sized
|
||||
//char m_szQueuedFilename[m_iQueuedFilenameLen]; // variable sized
|
||||
};
|
||||
|
||||
// A list response pp-parameter entry
|
||||
struct SNZBListResponsePPPEntry
|
||||
{
|
||||
int32_t m_iNZBIndex; // Index of NZB-Entry in m_NZBEntries-list
|
||||
int32_t m_iNameLen; // Length of Name-string (m_szName), following to this record
|
||||
int32_t m_iValueLen; // Length of Value-string (m_szValue), following to this record
|
||||
//char m_szName[m_iNameLen]; // variable sized
|
||||
//char m_szValue[m_iValueLen]; // variable sized
|
||||
};
|
||||
|
||||
// A list response file entry
|
||||
struct SNZBListResponseFileEntry
|
||||
{
|
||||
int32_t m_iID; // Entry-ID
|
||||
int32_t m_iNZBIndex; // Index of NZB-Entry in m_NZBEntries-list
|
||||
int32_t m_iFileSizeLo; // Filesize in bytes, Low 32-bits of 64-bit value
|
||||
int32_t m_iFileSizeHi; // Filesize in bytes, High 32-bits of 64-bit value
|
||||
int32_t m_iRemainingSizeLo; // Remaining size in bytes, Low 32-bits of 64-bit value
|
||||
int32_t m_iRemainingSizeHi; // Remaining size in bytes, High 32-bits of 64-bit value
|
||||
int32_t m_bPaused; // 1 - file is paused
|
||||
int32_t m_bFilenameConfirmed; // 1 - Filename confirmed (read from article body), 0 - Filename parsed from subject (can be changed after reading of article)
|
||||
int32_t m_iPriority; // Download priority
|
||||
int32_t m_iActiveDownloads; // Number of active downloads for this file
|
||||
int32_t m_bMatch; // 1 - file matches the pattern (only when Request has eRemoteMatchModeRegEx)
|
||||
int32_t m_iSubjectLen; // Length of Subject-string (m_szSubject), following to this record
|
||||
int32_t m_iFilenameLen; // Length of Filename-string (m_szFilename), following to this record
|
||||
//char m_szSubject[m_iSubjectLen]; // variable sized
|
||||
//char m_szFilename[m_iFilenameLen]; // variable sized
|
||||
};
|
||||
|
||||
// A log request
|
||||
struct SNZBLogRequest
|
||||
{
|
||||
SNZBMessageBase 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".
|
||||
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 request-answer
|
||||
struct SNZBLogRequestAnswer
|
||||
// A log response
|
||||
struct SNZBLogResponse
|
||||
{
|
||||
int32_t m_iSize; // Size of the entire struct
|
||||
int32_t m_iEntrySize; // Size of the SNZBLogRequestAnswerEntry-struct
|
||||
int32_t m_iNrTrailingEntries;
|
||||
int32_t m_iTrailingDataLength;
|
||||
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 request-answer entry
|
||||
struct SNZBLogRequestAnswerEntry
|
||||
// A log response entry
|
||||
struct SNZBLogResponseEntry
|
||||
{
|
||||
int32_t m_iTextLen;
|
||||
int32_t m_iID;
|
||||
int32_t m_iKind; // see Message::Kind in "Log.h"
|
||||
time_t m_tTime;
|
||||
//char m_szText[0]; // variable sized
|
||||
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
|
||||
{
|
||||
SNZBMessageBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bPause; // The value g_bPause should be set to
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bPause; // 1 - server must be paused, 0 - server must be unpaused
|
||||
int32_t m_iAction; // Action to be executed, see enum eRemotePauseUnpauseAction
|
||||
};
|
||||
|
||||
// A Pause/Unpause response
|
||||
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
|
||||
{
|
||||
SNZBMessageBase m_MessageBase; // Must be the first in the struct
|
||||
float m_fDownloadRate;
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iDownloadRate; // Speed limit, in Bytes pro Second
|
||||
};
|
||||
|
||||
// A download request
|
||||
// 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
|
||||
};
|
||||
|
||||
// edit queue request
|
||||
struct SNZBEditQueueRequest
|
||||
{
|
||||
SNZBMessageBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iIDFrom; // ID of the first file in the range
|
||||
int32_t m_iIDTo; // ID of the last file in the range, not used yet, must be same as m_iIDFrom
|
||||
int32_t m_iAction; // action to be done, see later
|
||||
int32_t m_iOffset; // Offset to move (for m_iAction = 0)
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iAction; // Action to be executed, see enum eRemoteEditAction
|
||||
int32_t m_iOffset; // Offset to move (for m_iAction = 0)
|
||||
int32_t m_bSmartOrder; // For Move-Actions: 0 - execute action for each ID in order they are placed in array;
|
||||
// 1 - smart execute to ensure that the relative order of all affected IDs are not changed.
|
||||
int32_t m_iMatchMode; // File/Group match mode, see enum eRemoteMatchMode
|
||||
int32_t m_iNrTrailingIDEntries; // Number of ID-entries, following to this structure
|
||||
int32_t m_iNrTrailingNameEntries; // Number of Name-entries, following to this structure
|
||||
int32_t m_iTrailingNameEntriesLen; // Length of all Name-entries, following to this structure
|
||||
int32_t m_iTextLen; // Length of Text-string (m_szText), following to this record
|
||||
int32_t m_iTrailingDataLength; // Length of Text-string and all ID-entries, following to this structure
|
||||
//char m_szText[m_iTextLen]; // variable sized
|
||||
//int32_t m_iIDs[m_iNrTrailingIDEntries]; // variable sized array of IDs. For File-Actions - ID of file, for Group-Actions - ID of any file belonging to group
|
||||
//char* m_szNames[m_iNrTrailingNameEntries]; // variable sized array of strings. For File-Actions - name of file incl. nzb-name as path, for Group-Actions - name of group
|
||||
};
|
||||
|
||||
// An edit queue response
|
||||
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
|
||||
{
|
||||
SNZBMessageBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iLevel; // Future use
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
};
|
||||
|
||||
#ifdef HAVE_PRAGMA_PACK
|
||||
#pragma pack()
|
||||
#endif
|
||||
// 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
|
||||
};
|
||||
|
||||
// Reload server request
|
||||
struct SNZBReloadRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
};
|
||||
|
||||
// Reload server response
|
||||
struct SNZBReloadResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
|
||||
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// Server version request
|
||||
struct SNZBVersionRequest
|
||||
{
|
||||
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
|
||||
};
|
||||
|
||||
// PostQueue request
|
||||
struct SNZBPostQueueRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
};
|
||||
|
||||
// A PostQueue response
|
||||
struct SNZBPostQueueResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iEntrySize; // Size of the SNZBPostQueueResponseEntry-struct
|
||||
int32_t m_iNrTrailingEntries; // Number of PostQueue-entries, following to this structure
|
||||
int32_t m_iTrailingDataLength; // Length of all PostQueue-entries, following to this structure
|
||||
// SNZBPostQueueResponseEntry m_Entries[m_iNrTrailingEntries] // variable sized
|
||||
};
|
||||
|
||||
// A PostQueue response entry
|
||||
struct SNZBPostQueueResponseEntry
|
||||
{
|
||||
int32_t m_iID; // ID of Post-entry
|
||||
int32_t m_iStage; // See PrePostProcessor::EPostJobStage
|
||||
int32_t m_iStageProgress; // Progress of current stage, value in range 0..1000
|
||||
int32_t m_iFileProgress; // Progress of current file, value in range 0..1000
|
||||
int32_t m_iTotalTimeSec; // Number of seconds this post-job is beeing processed (after it first changed the state from QUEUED).
|
||||
int32_t m_iStageTimeSec; // Number of seconds the current stage is beeing processed.
|
||||
int32_t m_iNZBFilenameLen; // Length of NZBFileName-string (m_szNZBFilename), following to this record
|
||||
int32_t m_iParFilename; // Length of ParFilename-string (m_szParFilename), following to this record
|
||||
int32_t m_iInfoNameLen; // Length of Filename-string (m_szFilename), following to this record
|
||||
int32_t m_iDestDirLen; // Length of DestDir-string (m_szDestDir), following to this record
|
||||
int32_t m_iProgressLabelLen; // Length of ProgressLabel-string (m_szProgressLabel), following to this record
|
||||
//char m_szNZBFilename[m_iNZBFilenameLen]; // variable sized, may contain full path (local path on client) or only filename
|
||||
//char m_szParFilename[m_iParFilename]; // variable sized
|
||||
//char m_szInfoName[m_iInfoNameLen]; // variable sized
|
||||
//char m_szDestDir[m_iDestDirLen]; // variable sized
|
||||
//char m_szProgressLabel[m_iProgressLabelLen]; // variable sized
|
||||
};
|
||||
|
||||
// Write log request
|
||||
struct SNZBWriteLogRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iKind; // see Message::Kind in "Log.h"
|
||||
int32_t m_iTrailingDataLength; // Length of nzb-file in bytes
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// Write log response
|
||||
struct SNZBWriteLogResponse
|
||||
{
|
||||
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
|
||||
};
|
||||
|
||||
// Scan nzb directory request
|
||||
struct SNZBScanRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bSyncMode; // 0 - asynchronous Scan (the command returns immediately), 1 - synchronous Scan (the command returns when the scan is completed)
|
||||
};
|
||||
|
||||
// Scan nzb directory response
|
||||
struct SNZBScanResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
|
||||
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// A history request
|
||||
struct SNZBHistoryRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
};
|
||||
|
||||
// history response
|
||||
struct SNZBHistoryResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iEntrySize; // Size of the SNZBHistoryResponseEntry-struct
|
||||
int32_t m_iNrTrailingEntries; // Number of History-entries, following to this structure
|
||||
int32_t m_iTrailingDataLength; // Length of all History-entries, following to this structure
|
||||
// SNZBHistoryResponseEntry m_Entries[m_iNrTrailingEntries] // variable sized
|
||||
};
|
||||
|
||||
// history entry
|
||||
struct SNZBHistoryResponseEntry
|
||||
{
|
||||
int32_t m_iID; // History-ID
|
||||
int32_t m_iKind; // Kind of Item: 1 - Collection (NZB), 2 - URL
|
||||
int32_t m_tTime; // When the item was added to history. time since the Epoch (00:00:00 UTC, January 1, 1970), measured in seconds.
|
||||
int32_t m_iNicenameLen; // Length of Nicename-string (m_szNicename), following to this record
|
||||
// for Collection items (m_iKind = 1)
|
||||
int32_t m_iSizeLo; // Size of all files in bytes, Low 32-bits of 64-bit value
|
||||
int32_t m_iSizeHi; // Size of all files in bytes, High 32-bits of 64-bit value
|
||||
int32_t m_iFileCount; // Initial number of files included in NZB-file
|
||||
int32_t m_iParStatus; // See NZBInfo::EParStatus
|
||||
int32_t m_iScriptStatus; // See NZBInfo::EScriptStatus
|
||||
// for URL items (m_iKind = 2)
|
||||
int32_t m_iUrlStatus; // See UrlInfo::EStatus
|
||||
// trailing data
|
||||
//char m_szNicename[m_iNicenameLen]; // variable sized
|
||||
};
|
||||
|
||||
// download url request
|
||||
struct SNZBDownloadUrlRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
char m_szURL[NZBREQUESTFILENAMESIZE]; // url to nzb-file
|
||||
char m_szNZBFilename[NZBREQUESTFILENAMESIZE];// Name of nzb-file. Can be empty, then the filename is read from URL download response
|
||||
char m_szCategory[NZBREQUESTFILENAMESIZE]; // Category, can be empty
|
||||
int32_t m_bAddFirst; // 1 - add url to the top of download queue
|
||||
int32_t m_bAddPaused; // 1 - pause added files
|
||||
int32_t m_iPriority; // Priority for files (0 - default)
|
||||
};
|
||||
|
||||
// download url response
|
||||
struct SNZBDownloadUrlResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
|
||||
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// UrlQueue request
|
||||
struct SNZBUrlQueueRequest
|
||||
{
|
||||
SNZBRequestBase m_MessageBase; // Must be the first in the struct
|
||||
};
|
||||
|
||||
// UrlQueue response
|
||||
struct SNZBUrlQueueResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_iEntrySize; // Size of the SNZBUrlQueueResponseEntry-struct
|
||||
int32_t m_iNrTrailingEntries; // Number of UrlQueue-entries, following to this structure
|
||||
int32_t m_iTrailingDataLength; // Length of all UrlQueue-entries, following to this structure
|
||||
// SNZBUrlQueueResponseEntry m_Entries[m_iNrTrailingEntries] // variable sized
|
||||
};
|
||||
|
||||
// UrlQueue response entry
|
||||
struct SNZBUrlQueueResponseEntry
|
||||
{
|
||||
int32_t m_iID; // ID of Url-entry
|
||||
int32_t m_iURLLen; // Length of URL-string (m_szURL), following to this record
|
||||
int32_t m_iNZBFilenameLen; // Length of NZBFilename-string (m_szNZBFilename), following to this record
|
||||
//char m_szURL[m_iURLLen]; // variable sized
|
||||
//char m_szNZBFilename[m_iNZBFilenameLen]; // variable sized
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
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-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -30,6 +30,7 @@
|
||||
#ifndef DISABLE_CURSES
|
||||
|
||||
#include <vector>
|
||||
#include <time.h>
|
||||
|
||||
#include "Frontend.h"
|
||||
#include "Log.h"
|
||||
@@ -46,22 +47,31 @@ private:
|
||||
eDownloadRate
|
||||
};
|
||||
|
||||
bool m_bUseColor;
|
||||
int m_iSkipUpdateData;
|
||||
int m_iScreenHeight;
|
||||
int m_iScreenWidth;
|
||||
int m_iQueueWinTop;
|
||||
int m_iQueueWinHeight;
|
||||
int m_iQueueWinClientHeight;
|
||||
int m_iMessagesWinTop;
|
||||
int m_iMessagesWinHeight;
|
||||
int m_iMessagesWinClientHeight;
|
||||
int m_iSelectedQueueEntry;
|
||||
int m_iQueueScrollOffset;
|
||||
bool m_bUseColor;
|
||||
int m_iDataUpdatePos;
|
||||
bool m_bUpdateNextTime;
|
||||
int m_iScreenHeight;
|
||||
int m_iScreenWidth;
|
||||
int m_iQueueWinTop;
|
||||
int m_iQueueWinHeight;
|
||||
int m_iQueueWinClientHeight;
|
||||
int m_iMessagesWinTop;
|
||||
int m_iMessagesWinHeight;
|
||||
int m_iMessagesWinClientHeight;
|
||||
int m_iSelectedQueueEntry;
|
||||
int m_iLastEditEntry;
|
||||
bool m_bLastPausePars;
|
||||
int m_iQueueScrollOffset;
|
||||
GroupQueue m_groupQueue;
|
||||
char* m_szHint;
|
||||
time_t m_tStartHint;
|
||||
int m_iColWidthFiles;
|
||||
int m_iColWidthTotal;
|
||||
int m_iColWidthLeft;
|
||||
|
||||
// Inputting numbres
|
||||
int m_iInputNumberIndex;
|
||||
int m_iInputValue;
|
||||
// Inputting numbers
|
||||
int m_iInputNumberIndex;
|
||||
int m_iInputValue;
|
||||
|
||||
#ifdef WIN32
|
||||
CHAR_INFO* m_pScreenBuffer;
|
||||
@@ -69,12 +79,14 @@ private:
|
||||
int m_iScreenBufferSize;
|
||||
std::vector<WORD> m_ColorAttr;
|
||||
#else
|
||||
void* m_pWindow; // WINDOW*
|
||||
void* m_pWindow; // WINDOW*
|
||||
#endif
|
||||
|
||||
EInputMode m_eInputMode;
|
||||
bool m_bShowNZBname;
|
||||
float m_QueueWindowPercentage;
|
||||
EInputMode m_eInputMode;
|
||||
bool m_bShowNZBname;
|
||||
bool m_bShowTimestamp;
|
||||
bool m_bGroupFiles;
|
||||
float m_QueueWindowPercentage;
|
||||
|
||||
#ifdef WIN32
|
||||
void init_pair(int iColorNumber, WORD wForeColor, WORD wBackColor);
|
||||
@@ -83,17 +95,27 @@ private:
|
||||
void PlotText(const char * szString, int iRow, int iPos, int iColorPair, bool bBlink);
|
||||
void PrintMessages();
|
||||
void PrintQueue();
|
||||
void PrintFileQueue();
|
||||
void PrintFilename(FileInfo* pFileInfo, int iRow, bool bSelected);
|
||||
void PrintGroupQueue();
|
||||
void ResetColWidths();
|
||||
void PrintGroupname(GroupInfo * pGroupInfo, int iRow, bool bSelected, bool bCalcColWidth);
|
||||
void PrepareGroupQueue();
|
||||
void PrintTopHeader(char* szHeader, int iLineNr, bool bUpTime);
|
||||
void ClearGroupQueue();
|
||||
int PrintMessage(Message* Msg, int iRow, int iMaxLines);
|
||||
void PrintKeyInputBar();
|
||||
void PrintStatus();
|
||||
void UpdateInput();
|
||||
void Update();
|
||||
void UpdateInput(int initialKey);
|
||||
void Update(int iKey);
|
||||
void SetCurrentQueueEntry(int iEntry);
|
||||
void CalcWindowSizes();
|
||||
void FormatFileSize(char* szBuffer, int iBufLen, long long lFileSize);
|
||||
void RefreshScreen();
|
||||
int ReadConsoleKey();
|
||||
int CalcQueueSize();
|
||||
void NeedUpdateData();
|
||||
bool EditQueue(QueueEditor::EEditAction eAction, int iOffset);
|
||||
void SetHint(const char* szHint);
|
||||
|
||||
protected:
|
||||
virtual void Run();
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file 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-2008 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -34,6 +34,7 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cstdio>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Log.h"
|
||||
@@ -41,42 +42,38 @@
|
||||
#include "Connection.h"
|
||||
#include "NewsServer.h"
|
||||
|
||||
NNTPConnection::NNTPConnection(NewsServer* server) : Connection(server)
|
||||
static const int CONNECTION_LINEBUFFER_SIZE = 1024*10;
|
||||
|
||||
NNTPConnection::NNTPConnection(NewsServer* pNewsServer) : Connection(pNewsServer->GetHost(), pNewsServer->GetPort(), pNewsServer->GetTLS())
|
||||
{
|
||||
m_UnavailableGroups.clear();
|
||||
m_pNewsServer = pNewsServer;
|
||||
m_szActiveGroup = NULL;
|
||||
m_szLineBuf = (char*)malloc(LineBufSize);
|
||||
m_szLineBuf = (char*)malloc(CONNECTION_LINEBUFFER_SIZE);
|
||||
m_bAuthError = false;
|
||||
}
|
||||
|
||||
NNTPConnection::~NNTPConnection()
|
||||
{
|
||||
for (unsigned int i = 0; i < m_UnavailableGroups.size(); i++)
|
||||
{
|
||||
free(m_UnavailableGroups[i]);
|
||||
m_UnavailableGroups[i] = NULL;
|
||||
}
|
||||
m_UnavailableGroups.clear();
|
||||
|
||||
if (m_szActiveGroup)
|
||||
{
|
||||
free(m_szActiveGroup);
|
||||
m_szActiveGroup = NULL;
|
||||
}
|
||||
if (m_szLineBuf)
|
||||
{
|
||||
free(m_szLineBuf);
|
||||
}
|
||||
free(m_szLineBuf);
|
||||
}
|
||||
|
||||
char* NNTPConnection::Request(char* req)
|
||||
const char* NNTPConnection::Request(const char* req)
|
||||
{
|
||||
if (!req)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
m_bAuthError = false;
|
||||
|
||||
WriteLine(req);
|
||||
|
||||
char* answer = ReadLine(m_szLineBuf, LineBufSize);
|
||||
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
|
||||
|
||||
if (!answer)
|
||||
{
|
||||
@@ -85,59 +82,59 @@ char* NNTPConnection::Request(char* req)
|
||||
|
||||
if (!strncmp(answer, "480", 3))
|
||||
{
|
||||
debug("%s requested authorization", m_pNetAddress->GetHost());
|
||||
debug("%s requested authorization", GetHost());
|
||||
|
||||
//authentication required!
|
||||
if (Authenticate() < 0)
|
||||
if (!Authenticate())
|
||||
{
|
||||
m_bAuthError = true;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
//try again
|
||||
WriteLine(req);
|
||||
answer = ReadLine(m_szLineBuf, LineBufSize);
|
||||
answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
|
||||
return answer;
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
int NNTPConnection::Authenticate()
|
||||
bool NNTPConnection::Authenticate()
|
||||
{
|
||||
if ((!((NewsServer*)m_pNetAddress)->GetUser()) ||
|
||||
(!((NewsServer*)m_pNetAddress)->GetPassword()))
|
||||
if (!(m_pNewsServer)->GetUser() ||
|
||||
!(m_pNewsServer)->GetPassword())
|
||||
{
|
||||
return -1;
|
||||
return true;
|
||||
}
|
||||
|
||||
return AuthInfoUser();
|
||||
}
|
||||
|
||||
int NNTPConnection::AuthInfoUser(int iRecur)
|
||||
bool NNTPConnection::AuthInfoUser(int iRecur)
|
||||
{
|
||||
if (iRecur > 10)
|
||||
{
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
char tmp[1024];
|
||||
|
||||
snprintf(tmp, 1024, "AUTHINFO USER %s\r\n", ((NewsServer*)m_pNetAddress)->GetUser());
|
||||
snprintf(tmp, 1024, "AUTHINFO USER %s\r\n", m_pNewsServer->GetUser());
|
||||
tmp[1024-1] = '\0';
|
||||
|
||||
WriteLine(tmp);
|
||||
|
||||
char* answer = ReadLine(m_szLineBuf, LineBufSize);
|
||||
|
||||
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
|
||||
if (!answer)
|
||||
{
|
||||
return -1;
|
||||
ReportError("Authorization for %s failed: Connection closed by remote host.", GetHost(), true, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!strncmp(answer, "281", 3))
|
||||
{
|
||||
debug("authorization for %s successful", m_pNetAddress->GetHost());
|
||||
return 0;
|
||||
debug("Authorization for %s successful", GetHost());
|
||||
return true;
|
||||
}
|
||||
else if (!strncmp(answer, "381", 3))
|
||||
{
|
||||
@@ -145,130 +142,140 @@ int NNTPConnection::AuthInfoUser(int iRecur)
|
||||
}
|
||||
else if (!strncmp(answer, "480", 3))
|
||||
{
|
||||
return AuthInfoUser();
|
||||
return AuthInfoUser(++iRecur);
|
||||
}
|
||||
|
||||
return -1;
|
||||
if (char* p = strrchr(answer, '\r')) *p = '\0'; // remove last CRLF from error message
|
||||
|
||||
if (GetStatus() != csCancelled)
|
||||
{
|
||||
ReportErrorAnswer("Authorization for %s failed (Answer: %s)", answer);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int NNTPConnection::AuthInfoPass(int iRecur)
|
||||
bool NNTPConnection::AuthInfoPass(int iRecur)
|
||||
{
|
||||
if (iRecur > 10)
|
||||
{
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
char tmp[1024];
|
||||
|
||||
snprintf(tmp, 1024, "AUTHINFO PASS %s\r\n", ((NewsServer*)m_pNetAddress)->GetPassword());
|
||||
snprintf(tmp, 1024, "AUTHINFO PASS %s\r\n", m_pNewsServer->GetPassword());
|
||||
tmp[1024-1] = '\0';
|
||||
|
||||
WriteLine(tmp);
|
||||
|
||||
char* szAnswer = ReadLine(m_szLineBuf, LineBufSize);
|
||||
if (!szAnswer)
|
||||
char* answer = ReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
|
||||
if (!answer)
|
||||
{
|
||||
ReportError("authorization for %s failed: Connection closed by remote host.", m_pNetAddress->GetHost(), 0);
|
||||
return -1;
|
||||
ReportError("Authorization for %s failed: Connection closed by remote host.", GetHost(), true, 0);
|
||||
return false;
|
||||
}
|
||||
else if (!strncmp(szAnswer, "2", 1))
|
||||
else if (!strncmp(answer, "2", 1))
|
||||
{
|
||||
debug("authorization for %s successful", m_pNetAddress->GetHost());
|
||||
return 0;
|
||||
debug("Authorization for %s successful", GetHost());
|
||||
return true;
|
||||
}
|
||||
else if (!strncmp(szAnswer, "381", 3))
|
||||
else if (!strncmp(answer, "381", 3))
|
||||
{
|
||||
return AuthInfoPass(++iRecur);
|
||||
}
|
||||
|
||||
error("authorization for %s failed (Answer: %s)", m_pNetAddress->GetHost(), szAnswer);
|
||||
return -1;
|
||||
if (char* p = strrchr(answer, '\r')) *p = '\0'; // remove last CRLF from error message
|
||||
|
||||
if (GetStatus() != csCancelled)
|
||||
{
|
||||
ReportErrorAnswer("Authorization for %s failed (Answer: %s)", answer);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int NNTPConnection::DoConnect()
|
||||
const char* NNTPConnection::JoinGroup(const char* grp)
|
||||
{
|
||||
debug("Opening connection to %s", GetServer()->GetHost());
|
||||
int res = Connection::DoConnect();
|
||||
if (res < 0)
|
||||
return res;
|
||||
char* answer = DoReadLine(m_szLineBuf, LineBufSize);
|
||||
|
||||
if (!answer)
|
||||
if (m_szActiveGroup && !strcmp(m_szActiveGroup, grp))
|
||||
{
|
||||
ReportError("Connection to %s failed: Connection closed by remote host.", m_pNetAddress->GetHost(), 0);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (strncmp(answer, "2", 1))
|
||||
{
|
||||
error("Connection to %s failed. Answer: ", m_pNetAddress->GetHost(), answer);
|
||||
return -1;
|
||||
}
|
||||
|
||||
debug("Connection to %s established", GetServer()->GetHost());
|
||||
return 0;
|
||||
}
|
||||
|
||||
int NNTPConnection::DoDisconnect()
|
||||
{
|
||||
if (m_eStatus == csConnected)
|
||||
{
|
||||
Request("quit\r\n");
|
||||
}
|
||||
return Connection::DoDisconnect();
|
||||
}
|
||||
|
||||
|
||||
int NNTPConnection::JoinGroup(char* grp)
|
||||
{
|
||||
if (!grp)
|
||||
{
|
||||
debug("joinGroup called with NULL-pointer!!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if ((m_szActiveGroup) && (!strcmp(m_szActiveGroup, grp)))
|
||||
return 0;
|
||||
|
||||
for (unsigned int i = 0; i < m_UnavailableGroups.size(); i++)
|
||||
{
|
||||
if (!strcmp(grp, m_UnavailableGroups[i]))
|
||||
{
|
||||
debug("Group %s unavailable on %s.", grp, this->GetServer()->GetHost());
|
||||
return -1;
|
||||
}
|
||||
// already in group
|
||||
strcpy(m_szLineBuf, "211 ");
|
||||
return m_szLineBuf;
|
||||
}
|
||||
|
||||
char tmp[1024];
|
||||
snprintf(tmp, 1024, "GROUP %s\r\n", grp);
|
||||
tmp[1024-1] = '\0';
|
||||
|
||||
char* answer = Request(tmp);
|
||||
|
||||
if ((answer) && (!strncmp(answer, "2", 1)))
|
||||
const char* answer = Request(tmp);
|
||||
if (m_bAuthError)
|
||||
{
|
||||
debug("Changed group to %s on %s", grp, GetServer()->GetHost());
|
||||
|
||||
if (m_szActiveGroup)
|
||||
free(m_szActiveGroup);
|
||||
|
||||
m_szActiveGroup = strdup(grp);
|
||||
return 0;
|
||||
return answer;
|
||||
}
|
||||
|
||||
if (!answer)
|
||||
if (answer && !strncmp(answer, "2", 1))
|
||||
{
|
||||
warn("Error changing group on %s: Connection closed by remote host.",
|
||||
GetServer()->GetHost());
|
||||
return -1;
|
||||
debug("Changed group to %s on %s", grp, GetHost());
|
||||
|
||||
if (m_szActiveGroup)
|
||||
{
|
||||
free(m_szActiveGroup);
|
||||
}
|
||||
m_szActiveGroup = strdup(grp);
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Error changing group on %s to %s: Answer was \"%s\".",
|
||||
GetServer()->GetHost(), grp, answer);
|
||||
m_UnavailableGroups.push_back(strdup(grp));
|
||||
debug("Error changing group on %s to %s: %s.", GetHost(), grp, answer);
|
||||
}
|
||||
|
||||
return -1;
|
||||
return answer;
|
||||
}
|
||||
|
||||
bool NNTPConnection::DoConnect()
|
||||
{
|
||||
debug("Opening connection to %s", GetHost());
|
||||
bool res = Connection::DoConnect();
|
||||
if (!res)
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
char* answer = DoReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
|
||||
|
||||
if (!answer)
|
||||
{
|
||||
ReportError("Connection to %s failed: Connection closed by remote host.", GetHost(), true, 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (strncmp(answer, "2", 1))
|
||||
{
|
||||
ReportErrorAnswer("Connection to %s failed (Answer: %s)", answer);
|
||||
return false;
|
||||
}
|
||||
|
||||
debug("Connection to %s established", GetHost());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NNTPConnection::DoDisconnect()
|
||||
{
|
||||
if (m_eStatus == csConnected)
|
||||
{
|
||||
Request("quit\r\n");
|
||||
if (m_szActiveGroup)
|
||||
{
|
||||
free(m_szActiveGroup);
|
||||
m_szActiveGroup = NULL;
|
||||
}
|
||||
}
|
||||
return Connection::DoDisconnect();
|
||||
}
|
||||
|
||||
void NNTPConnection::ReportErrorAnswer(const char* szMsgPrefix, const char* szAnswer)
|
||||
{
|
||||
char szErrStr[1024];
|
||||
snprintf(szErrStr, 1024, szMsgPrefix, GetHost(), szAnswer);
|
||||
szErrStr[1024-1] = '\0';
|
||||
|
||||
ReportError(szErrStr, NULL, false, 0);
|
||||
}
|
||||
|
||||
@@ -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-2008 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -27,34 +27,33 @@
|
||||
#ifndef NNTPCONNECTION_H
|
||||
#define NNTPCONNECTION_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <vector>
|
||||
|
||||
#include "NewsServer.h"
|
||||
#include "Connection.h"
|
||||
|
||||
class NNTPConnection : public Connection
|
||||
{
|
||||
private:
|
||||
std::vector <char*> m_UnavailableGroups;
|
||||
NewsServer* m_pNewsServer;
|
||||
char* m_szActiveGroup;
|
||||
static const int LineBufSize = 1024*10;
|
||||
char* m_szLineBuf;
|
||||
bool m_bAuthError;
|
||||
|
||||
virtual int DoConnect();
|
||||
virtual int DoDisconnect();
|
||||
virtual bool DoConnect();
|
||||
virtual bool DoDisconnect();
|
||||
void Clear();
|
||||
void ReportErrorAnswer(const char* szMsgPrefix, const char* szAnswer);
|
||||
|
||||
public:
|
||||
NNTPConnection(NewsServer* server);
|
||||
~NNTPConnection();
|
||||
NewsServer* GetNewsServer() { return(NewsServer*)m_pNetAddress; }
|
||||
char* Request(char* req);
|
||||
int Authenticate();
|
||||
int AuthInfoUser(int iRecur = 0);
|
||||
int AuthInfoPass(int iRecur = 0);
|
||||
int JoinGroup(char* grp);
|
||||
NNTPConnection(NewsServer* pNewsServer);
|
||||
virtual ~NNTPConnection();
|
||||
NewsServer* GetNewsServer() { return m_pNewsServer; }
|
||||
const char* Request(const char* req);
|
||||
bool Authenticate();
|
||||
bool AuthInfoUser(int iRecur = 0);
|
||||
bool AuthInfoPass(int iRecur = 0);
|
||||
const char* JoinGroup(const char* grp);
|
||||
bool GetAuthError() { return m_bAuthError; }
|
||||
};
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
260
NTService.cpp
260
NTService.cpp
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,7 +15,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -38,148 +38,152 @@
|
||||
extern void ExitProc();
|
||||
RunProc Run = NULL;
|
||||
|
||||
char* strServiceName = "NZBGet";
|
||||
SERVICE_STATUS_HANDLE nServiceStatusHandle;
|
||||
DWORD nServiceCurrentStatus;
|
||||
char* strServiceName = "NZBGet";
|
||||
SERVICE_STATUS_HANDLE nServiceStatusHandle;
|
||||
DWORD nServiceCurrentStatus;
|
||||
|
||||
BOOL UpdateServiceStatus(DWORD dwCurrentState, DWORD dwWaitHint)
|
||||
{
|
||||
BOOL success;
|
||||
SERVICE_STATUS nServiceStatus;
|
||||
nServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
||||
nServiceStatus.dwCurrentState = dwCurrentState;
|
||||
if (dwCurrentState == SERVICE_START_PENDING)
|
||||
{
|
||||
nServiceStatus.dwControlsAccepted = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
nServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
|
||||
}
|
||||
nServiceStatus.dwWin32ExitCode = NO_ERROR;
|
||||
nServiceStatus.dwServiceSpecificExitCode = 0;
|
||||
nServiceStatus.dwCheckPoint = 0;
|
||||
nServiceStatus.dwWaitHint = dwWaitHint;
|
||||
|
||||
success = SetServiceStatus(nServiceStatusHandle, &nServiceStatus);
|
||||
|
||||
return success;
|
||||
}
|
||||
BOOL UpdateServiceStatus(DWORD dwCurrentState, DWORD dwWaitHint)
|
||||
{
|
||||
BOOL success;
|
||||
SERVICE_STATUS nServiceStatus;
|
||||
nServiceStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
||||
nServiceStatus.dwCurrentState = dwCurrentState;
|
||||
if (dwCurrentState == SERVICE_START_PENDING)
|
||||
{
|
||||
nServiceStatus.dwControlsAccepted = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
nServiceStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
|
||||
}
|
||||
nServiceStatus.dwWin32ExitCode = NO_ERROR;
|
||||
nServiceStatus.dwServiceSpecificExitCode = 0;
|
||||
nServiceStatus.dwCheckPoint = 0;
|
||||
nServiceStatus.dwWaitHint = dwWaitHint;
|
||||
|
||||
void ServiceCtrlHandler(DWORD nControlCode)
|
||||
{
|
||||
switch(nControlCode)
|
||||
{
|
||||
case SERVICE_CONTROL_SHUTDOWN:
|
||||
case SERVICE_CONTROL_STOP:
|
||||
nServiceCurrentStatus = SERVICE_STOP_PENDING;
|
||||
UpdateServiceStatus(SERVICE_STOP_PENDING, 10000);
|
||||
ExitProc();
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
UpdateServiceStatus(nServiceCurrentStatus, 0);
|
||||
success = SetServiceStatus(nServiceStatusHandle, &nServiceStatus);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void ServiceMain(DWORD argc, LPTSTR *argv)
|
||||
{
|
||||
BOOL success;
|
||||
nServiceStatusHandle = RegisterServiceCtrlHandler(strServiceName,
|
||||
(LPHANDLER_FUNCTION)ServiceCtrlHandler);
|
||||
if(!nServiceStatusHandle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
success = UpdateServiceStatus(SERVICE_START_PENDING, 10000);
|
||||
if(!success)
|
||||
{
|
||||
return;
|
||||
}
|
||||
nServiceCurrentStatus=SERVICE_RUNNING;
|
||||
success=UpdateServiceStatus(SERVICE_RUNNING, 0);
|
||||
if(!success)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Run();
|
||||
|
||||
UpdateServiceStatus(SERVICE_STOPPED, 0);
|
||||
}
|
||||
void ServiceCtrlHandler(DWORD nControlCode)
|
||||
{
|
||||
switch(nControlCode)
|
||||
{
|
||||
case SERVICE_CONTROL_SHUTDOWN:
|
||||
case SERVICE_CONTROL_STOP:
|
||||
nServiceCurrentStatus = SERVICE_STOP_PENDING;
|
||||
UpdateServiceStatus(SERVICE_STOP_PENDING, 10000);
|
||||
ExitProc();
|
||||
return;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
UpdateServiceStatus(nServiceCurrentStatus, 0);
|
||||
}
|
||||
|
||||
void ServiceMain(DWORD argc, LPTSTR *argv)
|
||||
{
|
||||
BOOL success;
|
||||
nServiceStatusHandle = RegisterServiceCtrlHandler(strServiceName,
|
||||
(LPHANDLER_FUNCTION)ServiceCtrlHandler);
|
||||
if(!nServiceStatusHandle)
|
||||
{
|
||||
return;
|
||||
}
|
||||
success = UpdateServiceStatus(SERVICE_START_PENDING, 10000);
|
||||
if(!success)
|
||||
{
|
||||
return;
|
||||
}
|
||||
nServiceCurrentStatus=SERVICE_RUNNING;
|
||||
success=UpdateServiceStatus(SERVICE_RUNNING, 0);
|
||||
if(!success)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Run();
|
||||
|
||||
UpdateServiceStatus(SERVICE_STOPPED, 0);
|
||||
}
|
||||
|
||||
void StartService(RunProc RunProcPtr)
|
||||
{
|
||||
Run = RunProcPtr;
|
||||
|
||||
SERVICE_TABLE_ENTRY servicetable[]=
|
||||
{
|
||||
{strServiceName,(LPSERVICE_MAIN_FUNCTION)ServiceMain},
|
||||
{NULL,NULL}
|
||||
};
|
||||
BOOL success = StartServiceCtrlDispatcher(servicetable);
|
||||
if(!success)
|
||||
{
|
||||
error("Could not start service");
|
||||
}
|
||||
SERVICE_TABLE_ENTRY servicetable[]=
|
||||
{
|
||||
{strServiceName,(LPSERVICE_MAIN_FUNCTION)ServiceMain},
|
||||
{NULL,NULL}
|
||||
};
|
||||
BOOL success = StartServiceCtrlDispatcher(servicetable);
|
||||
if(!success)
|
||||
{
|
||||
error("Could not start service");
|
||||
}
|
||||
}
|
||||
|
||||
void InstallService(int argc, char *argv[])
|
||||
{
|
||||
SC_HANDLE scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
|
||||
if(!scm)
|
||||
{
|
||||
printf("Could not install service\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char szCmdLine[1024];
|
||||
snprintf(szCmdLine, 1024, "%s -D", argv[0]);
|
||||
szCmdLine[1024-1] = '\0';
|
||||
|
||||
SC_HANDLE hService = CreateService(scm, strServiceName,
|
||||
strServiceName,
|
||||
SERVICE_ALL_ACCESS,SERVICE_WIN32_OWN_PROCESS,SERVICE_DEMAND_START,
|
||||
SERVICE_ERROR_NORMAL,
|
||||
szCmdLine,
|
||||
0,0,0,0,0);
|
||||
if(!hService)
|
||||
{
|
||||
CloseServiceHandle(scm);
|
||||
printf("Could not install service\n");
|
||||
return;
|
||||
}
|
||||
CloseServiceHandle(hService);
|
||||
CloseServiceHandle(scm);
|
||||
printf("Service \"%s\" sucessfully installed\n", strServiceName);
|
||||
SC_HANDLE scm = OpenSCManager(0,0,SC_MANAGER_CREATE_SERVICE);
|
||||
if(!scm)
|
||||
{
|
||||
printf("Could not install service\n");
|
||||
return;
|
||||
}
|
||||
|
||||
char szExeName[1024];
|
||||
GetModuleFileName(NULL, szExeName, 1024);
|
||||
szExeName[1024-1] = '\0';
|
||||
|
||||
char szCmdLine[1024];
|
||||
snprintf(szCmdLine, 1024, "%s -D", szExeName);
|
||||
szCmdLine[1024-1] = '\0';
|
||||
|
||||
SC_HANDLE hService = CreateService(scm, strServiceName,
|
||||
strServiceName,
|
||||
SERVICE_ALL_ACCESS,SERVICE_WIN32_OWN_PROCESS,SERVICE_DEMAND_START,
|
||||
SERVICE_ERROR_NORMAL,
|
||||
szCmdLine,
|
||||
0,0,0,0,0);
|
||||
if(!hService)
|
||||
{
|
||||
CloseServiceHandle(scm);
|
||||
printf("Could not install service\n");
|
||||
return;
|
||||
}
|
||||
CloseServiceHandle(hService);
|
||||
CloseServiceHandle(scm);
|
||||
printf("Service \"%s\" sucessfully installed\n", strServiceName);
|
||||
}
|
||||
|
||||
void UnInstallService()
|
||||
{
|
||||
BOOL success;
|
||||
SC_HANDLE scm = OpenSCManager(0,0,SC_MANAGER_CONNECT);
|
||||
if(!scm)
|
||||
{
|
||||
printf("Could not uninstall service\n");
|
||||
return;
|
||||
}
|
||||
|
||||
SC_HANDLE hService = OpenService(scm, strServiceName, STANDARD_RIGHTS_REQUIRED);
|
||||
if(!hService)
|
||||
{
|
||||
CloseServiceHandle(scm);
|
||||
printf("Could not uninstall service\n");
|
||||
return;
|
||||
}
|
||||
|
||||
success = DeleteService(hService);
|
||||
if(!success)
|
||||
{
|
||||
error("Could not uninstall service");
|
||||
}
|
||||
CloseServiceHandle(hService);
|
||||
CloseServiceHandle(scm);
|
||||
printf("Service \"%s\" sucessfully uninstalled\n", strServiceName);
|
||||
BOOL success;
|
||||
SC_HANDLE scm = OpenSCManager(0,0,SC_MANAGER_CONNECT);
|
||||
if(!scm)
|
||||
{
|
||||
printf("Could not uninstall service\n");
|
||||
return;
|
||||
}
|
||||
|
||||
SC_HANDLE hService = OpenService(scm, strServiceName, STANDARD_RIGHTS_REQUIRED);
|
||||
if(!hService)
|
||||
{
|
||||
CloseServiceHandle(scm);
|
||||
printf("Could not uninstall service\n");
|
||||
return;
|
||||
}
|
||||
|
||||
success = DeleteService(hService);
|
||||
if(!success)
|
||||
{
|
||||
error("Could not uninstall service");
|
||||
}
|
||||
CloseServiceHandle(hService);
|
||||
CloseServiceHandle(scm);
|
||||
printf("Service \"%s\" sucessfully uninstalled\n", strServiceName);
|
||||
}
|
||||
|
||||
void InstallUninstallServiceCheck(int argc, char *argv[])
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,7 +15,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
|
||||
692
NZBFile.cpp
692
NZBFile.cpp
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2010 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -25,40 +25,54 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
#include <list>
|
||||
#ifdef WIN32
|
||||
#include <comutil.h>
|
||||
#import "MSXML.dll" named_guids
|
||||
#import <msxml.tlb> named_guids
|
||||
using namespace MSXML;
|
||||
#else
|
||||
#include <libxml/parser.h>
|
||||
#include <libxml/xmlreader.h>
|
||||
#include <libxml/xmlerror.h>
|
||||
#include <libxml/entities.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "NZBFile.h"
|
||||
#include "Log.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Options.h"
|
||||
#include "DiskState.h"
|
||||
#include "Util.h"
|
||||
|
||||
bool ArticleGreater(ArticleInfo* elem1, ArticleInfo* elem2)
|
||||
{
|
||||
return elem1->GetPartNumber() > elem2->GetPartNumber();
|
||||
}
|
||||
extern Options* g_pOptions;
|
||||
extern DiskState* g_pDiskState;
|
||||
|
||||
NZBFile::NZBFile(const char* szFileName)
|
||||
NZBFile::NZBFile(const char* szFileName, const char* szCategory)
|
||||
{
|
||||
debug("Creating NZBFile");
|
||||
|
||||
m_szFileName = strdup(szFileName);
|
||||
m_pNZBInfo = new NZBInfo();
|
||||
m_pNZBInfo->AddReference();
|
||||
m_pNZBInfo->SetFilename(szFileName);
|
||||
m_pNZBInfo->SetCategory(szCategory);
|
||||
m_pNZBInfo->BuildDestDirName();
|
||||
|
||||
#ifndef WIN32
|
||||
m_pFileInfo = NULL;
|
||||
m_pArticle = NULL;
|
||||
m_szTagContent = NULL;
|
||||
m_iTagContentLen = 0;
|
||||
#endif
|
||||
|
||||
m_FileInfos.clear();
|
||||
}
|
||||
@@ -73,11 +87,28 @@ NZBFile::~NZBFile()
|
||||
free(m_szFileName);
|
||||
}
|
||||
|
||||
for (std::vector<FileInfo*>::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
|
||||
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_FileInfos.clear();
|
||||
|
||||
if (m_pNZBInfo)
|
||||
{
|
||||
m_pNZBInfo->Release();
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
if (m_pFileInfo)
|
||||
{
|
||||
delete m_pFileInfo;
|
||||
}
|
||||
|
||||
if (m_szTagContent)
|
||||
{
|
||||
free(m_szTagContent);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void NZBFile::LogDebugInfo()
|
||||
@@ -90,58 +121,14 @@ void NZBFile::DetachFileInfos()
|
||||
m_FileInfos.clear();
|
||||
}
|
||||
|
||||
bool NZBFile::LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength)
|
||||
NZBFile* NZBFile::CreateFromBuffer(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize)
|
||||
{
|
||||
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;
|
||||
return Create(szFileName, szCategory, szBuffer, iSize, true);
|
||||
}
|
||||
|
||||
NZBFile* NZBFile::CreateFromBuffer(const char* szFileName, const char* szBuffer, int iSize)
|
||||
NZBFile* NZBFile::CreateFromFile(const char* szFileName, const char* szCategory)
|
||||
{
|
||||
return Create(szFileName, szBuffer, iSize, true);
|
||||
}
|
||||
|
||||
NZBFile* NZBFile::CreateFromFile(const char* szFileName)
|
||||
{
|
||||
//return Create(szFileName, NULL, 0, false);
|
||||
|
||||
// /*
|
||||
//TEST
|
||||
int iBufferLength = 0;
|
||||
char* szBuffer = NULL;
|
||||
|
||||
if (!NZBFile::LoadFileIntoBuffer(szFileName, &szBuffer, &iBufferLength))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return Create(szFileName, szBuffer, iBufferLength, true);
|
||||
// */
|
||||
return Create(szFileName, szCategory, NULL, 0, false);
|
||||
}
|
||||
|
||||
void NZBFile::AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo)
|
||||
@@ -153,8 +140,9 @@ void NZBFile::AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo)
|
||||
(*pFileInfo->GetArticles())[pArticleInfo->GetPartNumber() - 1] = pArticleInfo;
|
||||
}
|
||||
|
||||
void NZBFile::DeleteEmptyArticles(FileInfo* pFileInfo)
|
||||
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();)
|
||||
@@ -170,10 +158,206 @@ void NZBFile::DeleteEmptyArticles(FileInfo* pFileInfo)
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pArticles->empty())
|
||||
{
|
||||
m_FileInfos.push_back(pFileInfo);
|
||||
pFileInfo->SetNZBInfo(m_pNZBInfo);
|
||||
m_pNZBInfo->SetSize(m_pNZBInfo->GetSize() + pFileInfo->GetSize());
|
||||
m_pNZBInfo->SetFileCount(m_pNZBInfo->GetFileCount() + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
delete pFileInfo;
|
||||
}
|
||||
}
|
||||
|
||||
void NZBFile::ParseSubject(FileInfo* pFileInfo, bool TryQuotes)
|
||||
{
|
||||
if (TryQuotes)
|
||||
{
|
||||
// try to use the filename in quatation marks
|
||||
char* p = (char*)pFileInfo->GetSubject();
|
||||
char* start = strchr(p, '\"');
|
||||
if (start)
|
||||
{
|
||||
start++;
|
||||
char* end = strchr(start + 1, '\"');
|
||||
if (end)
|
||||
{
|
||||
int len = (int)(end - start);
|
||||
char* point = strchr(start + 1, '.');
|
||||
if (point && point < end)
|
||||
{
|
||||
char* filename = (char*)malloc(len + 1);
|
||||
strncpy(filename, start, len);
|
||||
filename[len] = '\0';
|
||||
pFileInfo->SetFilename(filename);
|
||||
free(filename);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// tokenize subject, considering spaces as separators and quotation
|
||||
// marks as non separatable token delimiters.
|
||||
// then take the last token containing dot (".") as a filename
|
||||
|
||||
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 = (int)(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());
|
||||
}
|
||||
}
|
||||
|
||||
bool NZBFile::HasDuplicateFilenames()
|
||||
{
|
||||
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo1 = *it;
|
||||
int iDupe = 1;
|
||||
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))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate filenames from subjects and check if the parsing of subject was correct
|
||||
*/
|
||||
void NZBFile::ProcessFilenames()
|
||||
{
|
||||
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
ParseSubject(pFileInfo, true);
|
||||
}
|
||||
|
||||
if (HasDuplicateFilenames())
|
||||
{
|
||||
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
ParseSubject(pFileInfo, false);
|
||||
}
|
||||
}
|
||||
|
||||
if (HasDuplicateFilenames())
|
||||
{
|
||||
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
pFileInfo->SetFilename(pFileInfo->GetSubject());
|
||||
}
|
||||
}
|
||||
|
||||
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
pFileInfo->MakeValidFilename();
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->SaveFile(pFileInfo);
|
||||
pFileInfo->ClearArticles();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize, bool bFromBuffer)
|
||||
NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize, bool bFromBuffer)
|
||||
{
|
||||
CoInitialize(NULL);
|
||||
|
||||
@@ -197,7 +381,12 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize
|
||||
}
|
||||
else
|
||||
{
|
||||
_variant_t v(szFileName);
|
||||
// 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)
|
||||
@@ -208,8 +397,12 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NZBFile* pFile = new NZBFile(szFileName);
|
||||
if (!pFile->parseNZB(doc))
|
||||
NZBFile* pFile = new NZBFile(szFileName, szCategory);
|
||||
if (pFile->ParseNZB(doc))
|
||||
{
|
||||
pFile->ProcessFilenames();
|
||||
}
|
||||
else
|
||||
{
|
||||
delete pFile;
|
||||
pFile = NULL;
|
||||
@@ -218,7 +411,29 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize
|
||||
return pFile;
|
||||
}
|
||||
|
||||
bool NZBFile::parseNZB(IUnknown* nzb)
|
||||
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;
|
||||
@@ -231,9 +446,14 @@ bool NZBFile::parseNZB(IUnknown* nzb)
|
||||
if (!attribute) return false;
|
||||
_bstr_t subject(attribute->Gettext());
|
||||
FileInfo* pFileInfo = new FileInfo();
|
||||
pFileInfo->SetNZBFilename(m_szFileName);
|
||||
pFileInfo->SetSubject(subject);
|
||||
pFileInfo->ParseSubject();
|
||||
|
||||
attribute = node->Getattributes()->getNamedItem("date");
|
||||
if (attribute)
|
||||
{
|
||||
_bstr_t date(attribute->Gettext());
|
||||
pFileInfo->SetTime(atoi(date));
|
||||
}
|
||||
|
||||
MSXML::IXMLDOMNodeListPtr groupList = node->selectNodes("groups/group");
|
||||
for (int g = 0; g < groupList->Getlength(); g++)
|
||||
@@ -274,152 +494,244 @@ bool NZBFile::parseNZB(IUnknown* nzb)
|
||||
}
|
||||
}
|
||||
|
||||
DeleteEmptyArticles(pFileInfo);
|
||||
m_FileInfos.push_back(pFileInfo);
|
||||
AddFileInfo(pFileInfo);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize, bool bFromBuffer)
|
||||
NZBFile* NZBFile::Create(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize, bool bFromBuffer)
|
||||
{
|
||||
xmlTextReaderPtr doc;
|
||||
NZBFile* pFile = new NZBFile(szFileName, szCategory);
|
||||
|
||||
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);
|
||||
|
||||
int ret = 0;
|
||||
pFile->m_bIgnoreNextError = false;
|
||||
|
||||
if (bFromBuffer)
|
||||
{
|
||||
doc = xmlReaderForMemory(szBuffer,iSize-1, "", NULL, 0);
|
||||
ret = xmlSAXUserParseMemory(&SAX_handler, pFile, szBuffer, iSize);
|
||||
}
|
||||
else
|
||||
{
|
||||
doc = xmlReaderForFile(szFileName, NULL, 0);
|
||||
ret = xmlSAXUserParseFile(&SAX_handler, pFile, szFileName);
|
||||
}
|
||||
if (!doc)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NZBFile* pFile = new NZBFile(szFileName);
|
||||
if (!pFile->parseNZB(doc))
|
||||
|
||||
if (ret == 0)
|
||||
{
|
||||
pFile->ProcessFilenames();
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Failed to parse nzb-file");
|
||||
delete pFile;
|
||||
pFile = NULL;
|
||||
}
|
||||
|
||||
xmlFreeTextReader(doc);
|
||||
|
||||
return pFile;
|
||||
|
||||
return pFile;
|
||||
}
|
||||
|
||||
bool NZBFile::parseNZB(void* nzb)
|
||||
void NZBFile::Parse_StartElement(const char *name, const char **atts)
|
||||
{
|
||||
FileInfo* pFileInfo = NULL;
|
||||
xmlTextReaderPtr node = (xmlTextReaderPtr)nzb;
|
||||
// walk through whole doc and search for segments-tags
|
||||
int ret = xmlTextReaderRead(node);
|
||||
while (ret == 1)
|
||||
{
|
||||
if (node)
|
||||
{
|
||||
xmlChar *name, *value;
|
||||
if (m_szTagContent)
|
||||
{
|
||||
free(m_szTagContent);
|
||||
m_szTagContent = NULL;
|
||||
m_iTagContentLen = 0;
|
||||
}
|
||||
|
||||
if (!strcmp("file", name))
|
||||
{
|
||||
m_pFileInfo = new FileInfo();
|
||||
m_pFileInfo->SetFilename(m_szFileName);
|
||||
|
||||
name = xmlTextReaderName(node);
|
||||
if (name == NULL)
|
||||
{
|
||||
name = xmlStrdup(BAD_CAST "--");
|
||||
}
|
||||
value = xmlTextReaderValue(node);
|
||||
for (int i = 0; atts[i]; i += 2)
|
||||
{
|
||||
const char* attrname = atts[i];
|
||||
const char* attrvalue = atts[i + 1];
|
||||
if (!strcmp("subject", attrname))
|
||||
{
|
||||
m_pFileInfo->SetSubject(attrvalue);
|
||||
}
|
||||
if (!strcmp("date", attrname))
|
||||
{
|
||||
m_pFileInfo->SetTime(atoi(attrvalue));
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!strcmp("segment", name))
|
||||
{
|
||||
if (!m_pFileInfo)
|
||||
{
|
||||
// error: bad nzb-file
|
||||
return;
|
||||
}
|
||||
|
||||
long long lsize = -1;
|
||||
int partNumber = -1;
|
||||
|
||||
if (xmlTextReaderNodeType(node) == 1)
|
||||
{
|
||||
if (!strcmp("file", (char*)name))
|
||||
{
|
||||
pFileInfo = new FileInfo();
|
||||
pFileInfo->SetNZBFilename(m_szFileName);
|
||||
for (int i = 0; atts[i]; i += 2)
|
||||
{
|
||||
const char* attrname = atts[i];
|
||||
const char* attrvalue = atts[i + 1];
|
||||
if (!strcmp("bytes", attrname))
|
||||
{
|
||||
lsize = atol(attrvalue);
|
||||
}
|
||||
if (!strcmp("number", attrname))
|
||||
{
|
||||
partNumber = atol(attrvalue);
|
||||
}
|
||||
}
|
||||
if (lsize > 0)
|
||||
{
|
||||
m_pFileInfo->SetSize(m_pFileInfo->GetSize() + lsize);
|
||||
}
|
||||
|
||||
while (xmlTextReaderMoveToNextAttribute(node))
|
||||
{
|
||||
xmlFree(name);
|
||||
name = xmlTextReaderName(node);
|
||||
if (!strcmp("subject",(char*)name))
|
||||
{
|
||||
xmlFree(value);
|
||||
value = xmlTextReaderValue(node);
|
||||
pFileInfo->SetSubject((char*)value);
|
||||
pFileInfo->ParseSubject();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!strcmp("segment",(char*)name))
|
||||
{
|
||||
long long lsize = -1;
|
||||
int partNumber = -1;
|
||||
if (partNumber > 0)
|
||||
{
|
||||
// new segment, add it!
|
||||
m_pArticle = new ArticleInfo();
|
||||
m_pArticle->SetPartNumber(partNumber);
|
||||
m_pArticle->SetSize(lsize);
|
||||
AddArticle(m_pFileInfo, m_pArticle);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
while (xmlTextReaderMoveToNextAttribute(node))
|
||||
{
|
||||
xmlFree(name);
|
||||
name = xmlTextReaderName(node);
|
||||
xmlFree(value);
|
||||
value = xmlTextReaderValue(node);
|
||||
if (!strcmp("bytes",(char*)name))
|
||||
{
|
||||
lsize = atol((char*)value);
|
||||
}
|
||||
if (!strcmp("number",(char*)name))
|
||||
{
|
||||
partNumber = atol((char*)value);
|
||||
}
|
||||
}
|
||||
if (lsize > 0)
|
||||
{
|
||||
pFileInfo->SetSize(pFileInfo->GetSize() + lsize);
|
||||
}
|
||||
|
||||
/* Get the #text part */
|
||||
ret = xmlTextReaderRead(node);
|
||||
void NZBFile::Parse_EndElement(const char *name)
|
||||
{
|
||||
if (!strcmp("file", name))
|
||||
{
|
||||
// Close the file element, add the new file to file-list
|
||||
AddFileInfo(m_pFileInfo);
|
||||
m_pFileInfo = NULL;
|
||||
m_pArticle = NULL;
|
||||
}
|
||||
else if (!strcmp("group", name))
|
||||
{
|
||||
if (!m_pFileInfo)
|
||||
{
|
||||
// error: bad nzb-file
|
||||
return;
|
||||
}
|
||||
|
||||
m_pFileInfo->GetGroups()->push_back(m_szTagContent);
|
||||
m_szTagContent = NULL;
|
||||
m_iTagContentLen = 0;
|
||||
}
|
||||
else if (!strcmp("segment", name))
|
||||
{
|
||||
if (!m_pFileInfo || !m_pArticle)
|
||||
{
|
||||
// error: bad nzb-file
|
||||
return;
|
||||
}
|
||||
|
||||
if (partNumber > 0)
|
||||
{
|
||||
// new segment, add it!
|
||||
xmlFree(value);
|
||||
value = xmlTextReaderValue(node);
|
||||
char tmp[2048];
|
||||
snprintf(tmp, 2048, "<%s>", (char*)value);
|
||||
ArticleInfo* pArticle = new ArticleInfo();
|
||||
pArticle->SetPartNumber(partNumber);
|
||||
pArticle->SetMessageID(tmp);
|
||||
pArticle->SetSize(lsize);
|
||||
AddArticle(pFileInfo, pArticle);
|
||||
}
|
||||
}
|
||||
else if (!strcmp("group",(char*)name))
|
||||
{
|
||||
ret = xmlTextReaderRead(node);
|
||||
xmlFree(value);
|
||||
value = xmlTextReaderValue(node);
|
||||
pFileInfo->GetGroups()->push_back(strdup((char*)value));
|
||||
}
|
||||
}
|
||||
// Get the #text part
|
||||
char ID[2048];
|
||||
snprintf(ID, 2048, "<%s>", m_szTagContent);
|
||||
m_pArticle->SetMessageID(ID);
|
||||
m_pArticle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if (xmlTextReaderNodeType(node) == 15)
|
||||
{
|
||||
/* Close the file element, add the new file to file-list */
|
||||
if (!strcmp("file",(char*)name))
|
||||
{
|
||||
DeleteEmptyArticles(pFileInfo);
|
||||
m_FileInfos.push_back(pFileInfo);
|
||||
}
|
||||
}
|
||||
void NZBFile::Parse_Content(const char *buf, int len)
|
||||
{
|
||||
m_szTagContent = (char*)realloc(m_szTagContent, m_iTagContentLen + len + 1);
|
||||
strncpy(m_szTagContent + m_iTagContentLen, buf, len);
|
||||
m_iTagContentLen += len;
|
||||
m_szTagContent[m_iTagContentLen] = '\0';
|
||||
}
|
||||
|
||||
xmlFree(name);
|
||||
xmlFree(value);
|
||||
}
|
||||
ret = xmlTextReaderRead(node);
|
||||
}
|
||||
if (ret != 0)
|
||||
{
|
||||
error("Failed to parse nzb-file\n");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
void NZBFile::SAX_StartElement(NZBFile* pFile, const char *name, const char **atts)
|
||||
{
|
||||
pFile->Parse_StartElement(name, atts);
|
||||
}
|
||||
|
||||
void NZBFile::SAX_EndElement(NZBFile* pFile, const char *name)
|
||||
{
|
||||
pFile->Parse_EndElement(name);
|
||||
}
|
||||
|
||||
void NZBFile::SAX_characters(NZBFile* pFile, const char * xmlstr, int len)
|
||||
{
|
||||
char* str = (char*)xmlstr;
|
||||
|
||||
// trim starting blanks
|
||||
int off = 0;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
char ch = str[i];
|
||||
if (ch == ' ' || ch == 10 || ch == 13 || ch == 9)
|
||||
{
|
||||
off++;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int newlen = len - off;
|
||||
|
||||
// trim ending blanks
|
||||
for (int i = len - 1; i >= off; i--)
|
||||
{
|
||||
char ch = str[i];
|
||||
if (ch == ' ' || ch == 10 || ch == 13 || ch == 9)
|
||||
{
|
||||
newlen--;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (newlen > 0)
|
||||
{
|
||||
// interpret tag content
|
||||
pFile->Parse_Content(str + off, newlen);
|
||||
}
|
||||
}
|
||||
|
||||
void* NZBFile::SAX_getEntity(NZBFile* pFile, const char * name)
|
||||
{
|
||||
xmlEntityPtr e = xmlGetPredefinedEntity((xmlChar* )name);
|
||||
if (!e)
|
||||
{
|
||||
warn("entity not found");
|
||||
pFile->m_bIgnoreNextError = true;
|
||||
}
|
||||
|
||||
return e;
|
||||
}
|
||||
|
||||
void NZBFile::SAX_error(NZBFile* pFile, const char *msg, ...)
|
||||
{
|
||||
if (pFile->m_bIgnoreNextError)
|
||||
{
|
||||
pFile->m_bIgnoreNextError = false;
|
||||
return;
|
||||
}
|
||||
|
||||
va_list argp;
|
||||
va_start(argp, msg);
|
||||
char szErrMsg[1024];
|
||||
vsnprintf(szErrMsg, sizeof(szErrMsg), msg, argp);
|
||||
szErrMsg[1024-1] = '\0';
|
||||
va_end(argp);
|
||||
|
||||
// remove trailing CRLF
|
||||
for (char* pend = szErrMsg + strlen(szErrMsg) - 1; pend >= szErrMsg && (*pend == '\n' || *pend == '\r' || *pend == ' '); pend--) *pend = '\0';
|
||||
error("Error parsing nzb-file: %s", szErrMsg);
|
||||
}
|
||||
#endif
|
||||
|
||||
42
NZBFile.h
42
NZBFile.h
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2010 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -38,25 +38,43 @@ public:
|
||||
|
||||
private:
|
||||
FileInfos m_FileInfos;
|
||||
NZBInfo* m_pNZBInfo;
|
||||
char* m_szFileName;
|
||||
|
||||
NZBFile(const char* szFileName);
|
||||
NZBFile(const char* szFileName, const char* szCategory);
|
||||
void AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo);
|
||||
void DeleteEmptyArticles(FileInfo* pFileInfo);
|
||||
void AddFileInfo(FileInfo* pFileInfo);
|
||||
void ParseSubject(FileInfo* pFileInfo, bool TryQuotes);
|
||||
void ProcessFilenames();
|
||||
bool HasDuplicateFilenames();
|
||||
#ifdef WIN32
|
||||
bool parseNZB(IUnknown* nzb);
|
||||
bool ParseNZB(IUnknown* nzb);
|
||||
static void EncodeURL(const char* szFilename, char* szURL);
|
||||
#else
|
||||
bool parseNZB(void* nzb);
|
||||
FileInfo* m_pFileInfo;
|
||||
ArticleInfo* m_pArticle;
|
||||
char* m_szTagContent;
|
||||
int m_iTagContentLen;
|
||||
bool m_bIgnoreNextError;
|
||||
|
||||
static void SAX_StartElement(NZBFile* pFile, const char *name, const char **atts);
|
||||
static void SAX_EndElement(NZBFile* pFile, const char *name);
|
||||
static void SAX_characters(NZBFile* pFile, const char * xmlstr, int len);
|
||||
static void* SAX_getEntity(NZBFile* pFile, const char * name);
|
||||
static void SAX_error(NZBFile* pFile, const char *msg, ...);
|
||||
void Parse_StartElement(const char *name, const char **atts);
|
||||
void Parse_EndElement(const char *name);
|
||||
void Parse_Content(const char *buf, int len);
|
||||
#endif
|
||||
static NZBFile* Create(const char* szFileName, const char* szBuffer, int iSize, bool bFromBuffer);
|
||||
static NZBFile* Create(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize, bool bFromBuffer);
|
||||
|
||||
public:
|
||||
virtual ~NZBFile();
|
||||
static NZBFile* CreateFromBuffer(const char* szFileName, const char* szBuffer, int iSize);
|
||||
static NZBFile* CreateFromFile(const char* szFileName);
|
||||
static bool LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength);
|
||||
static NZBFile* CreateFromBuffer(const char* szFileName, const char* szCategory, const char* szBuffer, int iSize);
|
||||
static NZBFile* CreateFromFile(const char* szFileName, const char* szCategory);
|
||||
const char* GetFileName() const { return m_szFileName; }
|
||||
FileInfos* GetFileInfos() { return &m_FileInfos; }
|
||||
NZBInfo* GetNZBInfo() { return m_pNZBInfo; }
|
||||
void DetachFileInfos();
|
||||
|
||||
void LogDebugInfo();
|
||||
|
||||
@@ -1,53 +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 "NetAddress.h"
|
||||
|
||||
NetAddress::NetAddress(const char* szHost, int iPort)
|
||||
{
|
||||
m_szHost = NULL;
|
||||
m_iPort = iPort;
|
||||
if (szHost)
|
||||
m_szHost = strdup(szHost);
|
||||
}
|
||||
|
||||
NetAddress::~NetAddress()
|
||||
{
|
||||
if (m_szHost)
|
||||
free(m_szHost);
|
||||
m_szHost = NULL;
|
||||
}
|
||||
44
NetAddress.h
44
NetAddress.h
@@ -1,44 +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 NETADDRESS_H
|
||||
#define NETADDRESS_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; }
|
||||
};
|
||||
|
||||
|
||||
#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-2008 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -35,30 +35,46 @@
|
||||
#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)
|
||||
NewsServer::NewsServer(const char* szHost, int iPort, const char* szUser, const char* szPass, bool bJoinGroup, bool bTLS, int iMaxConnections, int iLevel)
|
||||
{
|
||||
m_szHost = NULL;
|
||||
m_iPort = iPort;
|
||||
m_szUser = NULL;
|
||||
m_szPassword = NULL;
|
||||
m_iLevel = level;
|
||||
m_iMaxConnections = maxConnections;
|
||||
m_iLevel = iLevel;
|
||||
m_iMaxConnections = iMaxConnections;
|
||||
m_bJoinGroup = bJoinGroup;
|
||||
m_bTLS = bTLS;
|
||||
|
||||
if (pass)
|
||||
if (szHost)
|
||||
{
|
||||
m_szPassword = strdup(pass);
|
||||
m_szHost = strdup(szHost);
|
||||
}
|
||||
if (user)
|
||||
if (szUser)
|
||||
{
|
||||
m_szUser = strdup(user);
|
||||
m_szUser = strdup(szUser);
|
||||
}
|
||||
if (szPass)
|
||||
{
|
||||
m_szPassword = strdup(szPass);
|
||||
}
|
||||
}
|
||||
|
||||
NewsServer::~NewsServer()
|
||||
{
|
||||
free(m_szUser);
|
||||
m_szUser = NULL;
|
||||
free(m_szPassword);
|
||||
m_szPassword = NULL;
|
||||
if (m_szHost)
|
||||
{
|
||||
free(m_szHost);
|
||||
}
|
||||
if (m_szUser)
|
||||
{
|
||||
free(m_szUser);
|
||||
}
|
||||
if (m_szPassword)
|
||||
{
|
||||
free(m_szPassword);
|
||||
}
|
||||
}
|
||||
|
||||
30
NewsServer.h
30
NewsServer.h
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2008 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -27,23 +27,29 @@
|
||||
#ifndef NEWSSERVER_H
|
||||
#define NEWSSERVER_H
|
||||
|
||||
#include "NetAddress.h"
|
||||
|
||||
class NewsServer : public NetAddress
|
||||
class NewsServer
|
||||
{
|
||||
private:
|
||||
char* m_szUser;
|
||||
char* m_szPassword;
|
||||
int m_iMaxConnections;
|
||||
int m_iLevel;
|
||||
char* m_szHost;
|
||||
int m_iPort;
|
||||
char* m_szUser;
|
||||
char* m_szPassword;
|
||||
int m_iMaxConnections;
|
||||
int m_iLevel;
|
||||
bool m_bJoinGroup;
|
||||
bool m_bTLS;
|
||||
|
||||
public:
|
||||
NewsServer(const char* host, int port, const char* user, const char* pass, int maxConnections, int level);
|
||||
virtual ~NewsServer();
|
||||
NewsServer(const char* szHost, int iPort, const char* szUser, const char* szPass, bool bJoinGroup, bool bTLS, int iMaxConnections, int iLevel);
|
||||
~NewsServer();
|
||||
const char* GetHost() { return m_szHost; }
|
||||
int GetPort() { return m_iPort; }
|
||||
const char* GetUser() { return m_szUser; }
|
||||
const char* GetPassword() { return m_szPassword; }
|
||||
int GetMaxConnections() { return m_iMaxConnections; }
|
||||
int GetLevel() { return m_iLevel; }
|
||||
int GetJoinGroup() { return m_bJoinGroup; }
|
||||
bool GetTLS() { return m_bTLS; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
|
||||
2236
Options.cpp
2236
Options.cpp
File diff suppressed because it is too large
Load Diff
306
Options.h
306
Options.h
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2010 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -28,86 +28,160 @@
|
||||
#define OPTIONS_H
|
||||
|
||||
#include <vector>
|
||||
#include "Thread.h"
|
||||
|
||||
class Options
|
||||
{
|
||||
public:
|
||||
enum EClientOperation
|
||||
{
|
||||
opClientNoOperation,
|
||||
opClientRequestDownload,
|
||||
opClientRequestList,
|
||||
opClientRequestPause,
|
||||
opClientRequestUnpause,
|
||||
opClientRequestSetRate,
|
||||
opClientRequestDumpDebug,
|
||||
opClientRequestEditQueue,
|
||||
opClientRequestLog,
|
||||
opClientRequestShutdown
|
||||
opClientNoOperation,
|
||||
opClientRequestDownload,
|
||||
opClientRequestListFiles,
|
||||
opClientRequestListGroups,
|
||||
opClientRequestListStatus,
|
||||
opClientRequestSetRate,
|
||||
opClientRequestDumpDebug,
|
||||
opClientRequestEditQueue,
|
||||
opClientRequestLog,
|
||||
opClientRequestShutdown,
|
||||
opClientRequestReload,
|
||||
opClientRequestVersion,
|
||||
opClientRequestPostQueue,
|
||||
opClientRequestWriteLog,
|
||||
opClientRequestScanSync,
|
||||
opClientRequestScanAsync,
|
||||
opClientRequestDownloadPause,
|
||||
opClientRequestDownloadUnpause,
|
||||
opClientRequestDownload2Pause,
|
||||
opClientRequestDownload2Unpause,
|
||||
opClientRequestPostPause,
|
||||
opClientRequestPostUnpause,
|
||||
opClientRequestScanPause,
|
||||
opClientRequestScanUnpause,
|
||||
opClientRequestHistory,
|
||||
opClientRequestDownloadUrl,
|
||||
opClientRequestUrlQueue
|
||||
};
|
||||
enum EMessageTarget
|
||||
{
|
||||
mtNone,
|
||||
mtScreen,
|
||||
mtLog,
|
||||
mtBoth
|
||||
};
|
||||
enum EDecoder
|
||||
{
|
||||
dcNone,
|
||||
dcUulib,
|
||||
dcYenc
|
||||
mtNone,
|
||||
mtScreen,
|
||||
mtLog,
|
||||
mtBoth
|
||||
};
|
||||
enum EOutputMode
|
||||
{
|
||||
omLoggable,
|
||||
omColored,
|
||||
omNCurses
|
||||
omLoggable,
|
||||
omColored,
|
||||
omNCurses
|
||||
};
|
||||
enum ELoadPars
|
||||
{
|
||||
plNone,
|
||||
plOne,
|
||||
plAll
|
||||
lpNone,
|
||||
lpOne,
|
||||
lpAll
|
||||
};
|
||||
enum EScriptLogKind
|
||||
{
|
||||
slNone,
|
||||
slDetail,
|
||||
slInfo,
|
||||
slWarning,
|
||||
slError,
|
||||
slDebug
|
||||
};
|
||||
|
||||
private:
|
||||
struct OptEntry
|
||||
enum EMatchMode
|
||||
{
|
||||
char* name;
|
||||
char* value;
|
||||
mmID = 1,
|
||||
mmName,
|
||||
mmRegEx
|
||||
};
|
||||
|
||||
enum EDomain
|
||||
{
|
||||
dmServer = 1,
|
||||
dmPostProcess
|
||||
};
|
||||
|
||||
class OptEntry
|
||||
{
|
||||
private:
|
||||
char* m_szName;
|
||||
char* m_szValue;
|
||||
char* m_szDefValue;
|
||||
int m_iLineNo;
|
||||
|
||||
void SetName(const char* szName);
|
||||
void SetValue(const char* szValue);
|
||||
void SetLineNo(int iLineNo) { m_iLineNo = iLineNo; }
|
||||
|
||||
friend class Options;
|
||||
|
||||
public:
|
||||
OptEntry();
|
||||
OptEntry(const char* szName, const char* szValue);
|
||||
~OptEntry();
|
||||
const char* GetName() { return m_szName; }
|
||||
const char* GetValue() { return m_szValue; }
|
||||
const char* GetDefValue() { return m_szDefValue; }
|
||||
int GetLineNo() { return m_iLineNo; }
|
||||
};
|
||||
|
||||
std::vector< struct OptEntry > optEntries;
|
||||
typedef std::vector<OptEntry*> OptEntriesBase;
|
||||
|
||||
class OptEntries: public OptEntriesBase
|
||||
{
|
||||
public:
|
||||
~OptEntries();
|
||||
OptEntry* FindOption(const char* szName);
|
||||
};
|
||||
|
||||
typedef std::vector<char*> NameList;
|
||||
|
||||
private:
|
||||
OptEntries m_OptEntries;
|
||||
bool m_bConfigInitialized;
|
||||
Mutex m_mutexOptEntries;
|
||||
|
||||
// Options
|
||||
bool m_bConfigErrors;
|
||||
int m_iConfigLine;
|
||||
char* m_szConfigFilename;
|
||||
char* m_szDestDir;
|
||||
char* m_szTempDir;
|
||||
char* m_szQueueDir;
|
||||
char* m_szNzbDir;
|
||||
char* m_szWebDir;
|
||||
EMessageTarget m_eInfoTarget;
|
||||
EMessageTarget m_eWarningTarget;
|
||||
EMessageTarget m_eErrorTarget;
|
||||
EMessageTarget m_eDebugTarget;
|
||||
EDecoder m_eDecoder;
|
||||
EMessageTarget m_eDetailTarget;
|
||||
bool m_bDecode;
|
||||
bool m_bCreateBrokenLog;
|
||||
bool m_bResetLog;
|
||||
int m_iConnectionTimeout;
|
||||
int m_iTerminateTimeout;
|
||||
bool m_bAppendNZBDir;
|
||||
bool m_bAppendCategoryDir;
|
||||
bool m_bContinuePartial;
|
||||
bool m_bRenameBroken;
|
||||
int m_iRetries;
|
||||
int m_iRetryInterval;
|
||||
bool m_bSaveQueue;
|
||||
bool m_bDupeCheck;
|
||||
char* m_szServerIP;
|
||||
char* m_szServerPassword;
|
||||
int m_szServerPort;
|
||||
char* m_szControlIP;
|
||||
char* m_szControlPassword;
|
||||
int m_szControlPort;
|
||||
char* m_szLockFile;
|
||||
char* m_szDaemonUserName;
|
||||
EOutputMode m_eOutputMode;
|
||||
bool m_bReloadQueue;
|
||||
bool m_bReloadUrlQueue;
|
||||
bool m_bReloadPostQueue;
|
||||
int m_iUrlConnections;
|
||||
int m_iLogBufferSize;
|
||||
bool m_bCreateLog;
|
||||
char* m_szLogFile;
|
||||
@@ -115,8 +189,37 @@ private:
|
||||
bool m_bParCheck;
|
||||
bool m_bParRepair;
|
||||
char* m_szPostProcess;
|
||||
char* m_szPostConfigFilename;
|
||||
char* m_szNZBProcess;
|
||||
char* m_szNZBAddedProcess;
|
||||
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;
|
||||
bool m_bParCleanupQueue;
|
||||
int m_iDiskSpace;
|
||||
EScriptLogKind m_eProcessLogKind;
|
||||
bool m_bAllowReProcess;
|
||||
bool m_bTLS;
|
||||
bool m_bDumpCore;
|
||||
bool m_bParPauseQueue;
|
||||
bool m_bPostPauseQueue;
|
||||
bool m_bNzbCleanupDisk;
|
||||
bool m_bDeleteCleanupDisk;
|
||||
bool m_bMergeNzb;
|
||||
int m_iParTimeLimit;
|
||||
int m_iKeepHistory;
|
||||
bool m_bAccurateRate;
|
||||
|
||||
// Parsed command-line parameters
|
||||
bool m_bServerMode;
|
||||
@@ -124,96 +227,175 @@ private:
|
||||
bool m_bRemoteClientMode;
|
||||
int m_iEditQueueAction;
|
||||
int m_iEditQueueOffset;
|
||||
int m_iEditQueueIDFrom;
|
||||
int m_iEditQueueIDTo;
|
||||
int* m_pEditQueueIDList;
|
||||
int m_iEditQueueIDCount;
|
||||
NameList m_EditQueueNameList;
|
||||
EMatchMode m_EMatchMode;
|
||||
char* m_szEditQueueText;
|
||||
char* m_szArgFilename;
|
||||
char* m_szAddCategory;
|
||||
int m_iAddPriority;
|
||||
bool m_bAddPaused;
|
||||
char* m_szAddNZBFilename;
|
||||
char* m_szLastArg;
|
||||
bool m_bPrintOptions;
|
||||
bool m_bAddTop;
|
||||
float m_fSetRate;
|
||||
int m_iLogLines;
|
||||
bool m_bTest;
|
||||
int m_iWriteLogKind;
|
||||
bool m_bTestBacktrace;
|
||||
|
||||
// Current state
|
||||
bool m_bPause;
|
||||
bool m_bPauseDownload;
|
||||
bool m_bPauseDownload2;
|
||||
bool m_bPausePostProcess;
|
||||
bool m_bPauseScan;
|
||||
float m_fDownloadRate;
|
||||
EClientOperation m_eClientOperation;
|
||||
|
||||
void InitDefault();
|
||||
void InitOptFile(int argc, char* argv[]);
|
||||
void InitOptFile();
|
||||
void InitCommandLine(int argc, char* argv[]);
|
||||
void InitOptions();
|
||||
void InitPostConfig();
|
||||
void InitFileArg(int argc, char* argv[]);
|
||||
void InitServers();
|
||||
void InitScheduler();
|
||||
void CheckOptions();
|
||||
void PrintUsage(char* com);
|
||||
void Dump();
|
||||
int ParseOptionValue(const char* OptName, int argc, const char* argn[], const int argv[]);
|
||||
int ParseEnumValue(const char* OptName, int argc, const char* argn[], const int argv[]);
|
||||
int ParseIntValue(const char* OptName, int iBase);
|
||||
float ParseFloatValue(const char* OptName);
|
||||
OptEntry* FindOption(const char* optname);
|
||||
const char* GetOption(const char* optname);
|
||||
void DelOption(const char* optname);
|
||||
void SetOption(const char* optname, const char* value);
|
||||
bool SetOptionString(const char* option);
|
||||
bool ValidateOptionName(const char* optname);
|
||||
void LoadConfig(const char* configfile);
|
||||
void CheckDir(char** dir, const char* szOptionName);
|
||||
void LoadConfigFile();
|
||||
void CheckDir(char** dir, const char* szOptionName, bool bAllowEmpty, bool bCreate);
|
||||
void ParseFileIDList(int argc, char* argv[], int optind);
|
||||
void ParseFileNameList(int argc, char* argv[], int optind);
|
||||
bool ParseTime(const char** pTime, int* pHours, int* pMinutes);
|
||||
bool ParseWeekDays(const char* szWeekDays, int* pWeekDaysBits);
|
||||
void ConfigError(const char* msg, ...);
|
||||
void ConvertOldOptionName(char *szOption, int iBufLen);
|
||||
|
||||
public:
|
||||
Options(int argc, char* argv[]);
|
||||
~Options();
|
||||
Options(int argc, char* argv[]);
|
||||
~Options();
|
||||
|
||||
bool LoadConfig(EDomain eDomain, OptEntries* pOptEntries);
|
||||
bool SaveConfig(EDomain eDomain, OptEntries* pOptEntries);
|
||||
|
||||
// Options
|
||||
OptEntries* LockOptEntries();
|
||||
void UnlockOptEntries();
|
||||
const char* GetConfigFilename() { return m_szConfigFilename; }
|
||||
const char* GetDestDir() { return m_szDestDir; }
|
||||
const char* GetTempDir() { return m_szTempDir; }
|
||||
const char* GetQueueDir() { return m_szQueueDir; }
|
||||
const char* GetNzbDir() { return m_szNzbDir; }
|
||||
const char* GetWebDir() { return m_szWebDir; }
|
||||
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; }
|
||||
EMessageTarget GetDetailTarget() const { return m_eDetailTarget; }
|
||||
int GetConnectionTimeout() { return m_iConnectionTimeout; }
|
||||
int GetTerminateTimeout() { return m_iTerminateTimeout; }
|
||||
EDecoder GetDecoder() { return m_eDecoder; };
|
||||
bool GetDecode() { return m_bDecode; };
|
||||
bool GetAppendNZBDir() { return m_bAppendNZBDir; }
|
||||
bool GetAppendCategoryDir() { return m_bAppendCategoryDir; }
|
||||
bool GetContinuePartial() { return m_bContinuePartial; }
|
||||
bool GetRenameBroken() { return m_bRenameBroken; }
|
||||
int GetRetries() { return m_iRetries; }
|
||||
int GetRetryInterval() { return m_iRetryInterval; }
|
||||
bool GetSaveQueue() { return m_bSaveQueue; }
|
||||
bool GetDupeCheck() { return m_bDupeCheck; }
|
||||
char* GetServerIP() { return m_szServerIP; }
|
||||
char* GetServerPassword() { return m_szServerPassword; }
|
||||
int GetServerPort() { return m_szServerPort; }
|
||||
char* GetLockFile() { return m_szLockFile; }
|
||||
const char* GetControlIP() { return m_szControlIP; }
|
||||
const char* GetControlPassword() { return m_szControlPassword; }
|
||||
int GetControlPort() { return m_szControlPort; }
|
||||
const char* GetLockFile() { return m_szLockFile; }
|
||||
const char* GetDaemonUserName() { return m_szDaemonUserName; }
|
||||
EOutputMode GetOutputMode() { return m_eOutputMode; }
|
||||
bool GetReloadQueue() { return m_bReloadQueue; }
|
||||
bool GetReloadUrlQueue() { return m_bReloadUrlQueue; }
|
||||
bool GetReloadPostQueue() { return m_bReloadPostQueue; }
|
||||
int GetUrlConnections() { return m_iUrlConnections; }
|
||||
int GetLogBufferSize() { return m_iLogBufferSize; }
|
||||
bool GetCreateLog() { return m_bCreateLog; }
|
||||
char* GetLogFile() { return m_szLogFile; }
|
||||
const 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; }
|
||||
const char* GetPostConfigFilename() { return m_szPostConfigFilename; }
|
||||
const char* GetNZBProcess() { return m_szNZBProcess; }
|
||||
const char* GetNZBAddedProcess() { return m_szNZBAddedProcess; }
|
||||
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; }
|
||||
bool GetParCleanupQueue() { return m_bParCleanupQueue; }
|
||||
int GetDiskSpace() { return m_iDiskSpace; }
|
||||
EScriptLogKind GetProcessLogKind() { return m_eProcessLogKind; }
|
||||
bool GetAllowReProcess() { return m_bAllowReProcess; }
|
||||
bool GetTLS() { return m_bTLS; }
|
||||
bool GetDumpCore() { return m_bDumpCore; }
|
||||
bool GetParPauseQueue() { return m_bParPauseQueue; }
|
||||
bool GetPostPauseQueue() { return m_bPostPauseQueue; }
|
||||
bool GetNzbCleanupDisk() { return m_bNzbCleanupDisk; }
|
||||
bool GetDeleteCleanupDisk() { return m_bDeleteCleanupDisk; }
|
||||
bool GetMergeNzb() { return m_bMergeNzb; }
|
||||
int GetParTimeLimit() { return m_iParTimeLimit; }
|
||||
int GetKeepHistory() { return m_iKeepHistory; }
|
||||
bool GetAccurateRate() { return m_bAccurateRate; }
|
||||
|
||||
// Parsed command-line parameters
|
||||
bool GetServerMode() { return m_bServerMode; }
|
||||
bool GetDaemonMode() { return m_bDaemonMode; }
|
||||
bool GetRemoteClientMode() { return m_bRemoteClientMode; }
|
||||
EClientOperation GetClientOperation() { return m_eClientOperation; }
|
||||
EClientOperation GetClientOperation() { return m_eClientOperation; }
|
||||
int GetEditQueueAction() { return m_iEditQueueAction; }
|
||||
int GetEditQueueOffset() { return m_iEditQueueOffset; }
|
||||
int GetEditQueueIDFrom() { return m_iEditQueueIDFrom; }
|
||||
int GetEditQueueIDTo() { return m_iEditQueueIDTo; }
|
||||
int* GetEditQueueIDList() { return m_pEditQueueIDList; }
|
||||
int GetEditQueueIDCount() { return m_iEditQueueIDCount; }
|
||||
NameList* GetEditQueueNameList() { return &m_EditQueueNameList; }
|
||||
EMatchMode GetMatchMode() { return m_EMatchMode; }
|
||||
const char* GetEditQueueText() { return m_szEditQueueText; }
|
||||
const char* GetArgFilename() { return m_szArgFilename; }
|
||||
const char* GetAddCategory() { return m_szAddCategory; }
|
||||
bool GetAddPaused() { return m_bAddPaused; }
|
||||
const char* GetLastArg() { return m_szLastArg; }
|
||||
int GetAddPriority() { return m_iAddPriority; }
|
||||
char* GetAddNZBFilename() { return m_szAddNZBFilename; }
|
||||
bool GetAddTop() { return m_bAddTop; }
|
||||
float GetSetRate() { return m_fSetRate; }
|
||||
int GetLogLines() { return m_iLogLines; }
|
||||
bool GetTest() { return m_bTest; }
|
||||
int GetWriteLogKind() { return m_iWriteLogKind; }
|
||||
bool GetTestBacktrace() { return m_bTestBacktrace; }
|
||||
|
||||
// Current state
|
||||
void SetPause(bool bOnOff) { m_bPause = bOnOff; }
|
||||
bool GetPause() const { return m_bPause; }
|
||||
void SetPauseDownload(bool bPauseDownload) { m_bPauseDownload = bPauseDownload; }
|
||||
bool GetPauseDownload() const { return m_bPauseDownload; }
|
||||
void SetPauseDownload2(bool bPauseDownload2) { m_bPauseDownload2 = bPauseDownload2; }
|
||||
bool GetPauseDownload2() const { return m_bPauseDownload2; }
|
||||
void SetPausePostProcess(bool bPausePostProcess) { m_bPausePostProcess = bPausePostProcess; }
|
||||
bool GetPausePostProcess() const { return m_bPausePostProcess; }
|
||||
void SetPauseScan(bool bPauseScan) { m_bPauseScan = bPauseScan; }
|
||||
bool GetPauseScan() const { return m_bPauseScan; }
|
||||
void SetDownloadRate(float fRate) { m_fDownloadRate = fRate; }
|
||||
float GetDownloadRate() const { return m_fDownloadRate; }
|
||||
};
|
||||
|
||||
546
ParChecker.cpp
546
ParChecker.cpp
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,7 +15,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -36,10 +36,12 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <fstream>
|
||||
#ifdef WIN32
|
||||
#include <par2cmdline.h>
|
||||
#include <par2repairer.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <libpar2/par2cmdline.h>
|
||||
#include <libpar2/par2repairer.h>
|
||||
#endif
|
||||
@@ -47,11 +49,9 @@
|
||||
#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",
|
||||
@@ -64,20 +64,28 @@ const char* Par2CmdLineErrStr[] = { "OK",
|
||||
"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_szProgressLabel = (char*)malloc(1024);
|
||||
m_iFileProgress = 0;
|
||||
m_iStageProgress = 0;
|
||||
m_iExtraFiles = 0;
|
||||
m_bVerifyingExtraFiles = false;
|
||||
m_bCancelled = false;
|
||||
m_eStage = ptLoadingPars;
|
||||
m_QueuedParFiles.clear();
|
||||
}
|
||||
|
||||
@@ -89,10 +97,6 @@ ParChecker::~ParChecker()
|
||||
{
|
||||
free(m_szParFilename);
|
||||
}
|
||||
if (m_szNZBFilename)
|
||||
{
|
||||
free(m_szNZBFilename);
|
||||
}
|
||||
if (m_szInfoName)
|
||||
{
|
||||
free(m_szInfoName);
|
||||
@@ -101,6 +105,7 @@ ParChecker::~ParChecker()
|
||||
{
|
||||
free(m_szErrMsg);
|
||||
}
|
||||
free(m_szProgressLabel);
|
||||
|
||||
for (QueuedParFiles::iterator it = m_QueuedParFiles.begin(); it != m_QueuedParFiles.end() ;it++)
|
||||
{
|
||||
@@ -127,15 +132,6 @@ void ParChecker::SetInfoName(const char * 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;
|
||||
@@ -144,12 +140,19 @@ void ParChecker::SetStatus(EStatus eStatus)
|
||||
|
||||
void ParChecker::Run()
|
||||
{
|
||||
info("Verifying %s", m_szInfoName);
|
||||
m_bRepairNotNeeded = false;
|
||||
m_eStage = ptLoadingPars;
|
||||
m_iProcessedFiles = 0;
|
||||
m_iExtraFiles = 0;
|
||||
m_bVerifyingExtraFiles = false;
|
||||
m_bCancelled = false;
|
||||
|
||||
info("Verifying %s", m_szInfoName);
|
||||
SetStatus(psWorking);
|
||||
|
||||
|
||||
debug("par: %s", m_szParFilename);
|
||||
CommandLine commandLine;
|
||||
const char* argv[] = { "par2", "r", "-q", "-q", m_szParFilename };
|
||||
const char* argv[] = { "par2", "r", "-v", "-v", m_szParFilename };
|
||||
if (!commandLine.Parse(5, (char**)argv))
|
||||
{
|
||||
error("Could not start par-check for %s. Par-file: %s", m_szInfoName, m_szParFilename);
|
||||
@@ -158,19 +161,29 @@ void ParChecker::Run()
|
||||
}
|
||||
|
||||
Result res;
|
||||
Repairer* repairer = new Repairer();
|
||||
#ifdef ENABLE_PARPROGRESS
|
||||
repairer->sig_filename.connect(sigc::mem_fun(*this, &ParChecker::signal_filename));
|
||||
#endif
|
||||
|
||||
res = repairer->PreProcess(commandLine);
|
||||
|
||||
Repairer* pRepairer = new Repairer();
|
||||
m_pRepairer = pRepairer;
|
||||
|
||||
pRepairer->sig_filename.connect(sigc::mem_fun(*this, &ParChecker::signal_filename));
|
||||
pRepairer->sig_progress.connect(sigc::mem_fun(*this, &ParChecker::signal_progress));
|
||||
pRepairer->sig_done.connect(sigc::mem_fun(*this, &ParChecker::signal_done));
|
||||
|
||||
snprintf(m_szProgressLabel, 1024, "Verifying %s", m_szInfoName);
|
||||
m_szProgressLabel[1024-1] = '\0';
|
||||
m_iFileProgress = 0;
|
||||
m_iStageProgress = 0;
|
||||
UpdateProgress();
|
||||
|
||||
res = pRepairer->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");
|
||||
error("Could not verify %s: %s", m_szInfoName, IsStopped() ? "due stopping" : "par2-file could not be processed");
|
||||
m_szErrMsg = strdup("par2-file could not be processed");
|
||||
SetStatus(psFailed);
|
||||
delete repairer;
|
||||
delete pRepairer;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -182,15 +195,25 @@ void ParChecker::Run()
|
||||
}
|
||||
m_szErrMsg = NULL;
|
||||
|
||||
m_bRepairNotNeeded = false;
|
||||
m_bRepairing = false;
|
||||
res = repairer->Process(commandLine, false);
|
||||
m_eStage = ptVerifyingSources;
|
||||
res = pRepairer->Process(commandLine, false);
|
||||
debug("ParChecker: Process-result=%i", res);
|
||||
|
||||
|
||||
if (!IsStopped() && res == eRepairNotPossible && CheckSplittedFragments())
|
||||
{
|
||||
pRepairer->UpdateVerificationResults();
|
||||
res = pRepairer->Process(commandLine, false);
|
||||
debug("ParChecker: Process-result=%i", res);
|
||||
}
|
||||
|
||||
bool bMoreFilesLoaded = true;
|
||||
while (!IsStopped() && res == eRepairNotPossible)
|
||||
{
|
||||
int missingblockcount = repairer->missingblockcount - repairer->recoverypacketmap.size();
|
||||
info("Need more %i par-block(s) for %s", missingblockcount, m_szInfoName);
|
||||
int missingblockcount = pRepairer->missingblockcount - pRepairer->recoverypacketmap.size();
|
||||
if (bMoreFilesLoaded)
|
||||
{
|
||||
info("Need more %i par-block(s) for %s", missingblockcount, m_szInfoName);
|
||||
}
|
||||
|
||||
m_mutexQueuedParFiles.Lock();
|
||||
bool hasMorePars = !m_QueuedParFiles.empty();
|
||||
@@ -200,9 +223,17 @@ void ParChecker::Run()
|
||||
{
|
||||
int iBlockFound = 0;
|
||||
bool requested = RequestMorePars(missingblockcount, &iBlockFound);
|
||||
if (requested)
|
||||
{
|
||||
strncpy(m_szProgressLabel, "Awaiting additional par-files", 1024);
|
||||
m_szProgressLabel[1024-1] = '\0';
|
||||
m_iFileProgress = 0;
|
||||
UpdateProgress();
|
||||
}
|
||||
|
||||
m_mutexQueuedParFiles.Lock();
|
||||
hasMorePars = !m_QueuedParFiles.empty();
|
||||
m_bQueuedParFilesChanged = false;
|
||||
m_mutexQueuedParFiles.Unlock();
|
||||
|
||||
if (!requested && !hasMorePars)
|
||||
@@ -215,7 +246,15 @@ void ParChecker::Run()
|
||||
|
||||
if (!hasMorePars)
|
||||
{
|
||||
m_semNeedMoreFiles.Wait();
|
||||
// wait until new files are added by "AddParFile" or a change is signaled by "QueueChanged"
|
||||
bool bQueuedParFilesChanged = false;
|
||||
while (!bQueuedParFilesChanged && !IsStopped())
|
||||
{
|
||||
m_mutexQueuedParFiles.Lock();
|
||||
bQueuedParFilesChanged = m_bQueuedParFilesChanged;
|
||||
m_mutexQueuedParFiles.Unlock();
|
||||
usleep(100 * 1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,18 +263,19 @@ void ParChecker::Run()
|
||||
break;
|
||||
}
|
||||
|
||||
LoadMorePars(repairer);
|
||||
repairer->UpdateVerificationResults();
|
||||
|
||||
m_bRepairing = false;
|
||||
res = repairer->Process(commandLine, false);
|
||||
debug("ParChecker: Process-result=%i", res);
|
||||
bMoreFilesLoaded = LoadMorePars();
|
||||
if (bMoreFilesLoaded)
|
||||
{
|
||||
pRepairer->UpdateVerificationResults();
|
||||
res = pRepairer->Process(commandLine, false);
|
||||
debug("ParChecker: Process-result=%i", res);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsStopped())
|
||||
{
|
||||
SetStatus(psFailed);
|
||||
delete repairer;
|
||||
delete pRepairer;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -249,8 +289,17 @@ void ParChecker::Run()
|
||||
if (g_pOptions->GetParRepair())
|
||||
{
|
||||
info("Repairing %s", m_szInfoName);
|
||||
m_bRepairing = true;
|
||||
res = repairer->Process(commandLine, true);
|
||||
|
||||
snprintf(m_szProgressLabel, 1024, "Repairing %s", m_szInfoName);
|
||||
m_szProgressLabel[1024-1] = '\0';
|
||||
m_iFileProgress = 0;
|
||||
m_iStageProgress = 0;
|
||||
m_iProcessedFiles = 0;
|
||||
m_eStage = ptRepairing;
|
||||
m_iFilesToRepair = pRepairer->damagedfilecount + pRepairer->missingfilecount;
|
||||
UpdateProgress();
|
||||
|
||||
res = pRepairer->Process(commandLine, true);
|
||||
debug("ParChecker: Process-result=%i", res);
|
||||
if (res == eSuccess)
|
||||
{
|
||||
@@ -264,7 +313,13 @@ void ParChecker::Run()
|
||||
}
|
||||
}
|
||||
|
||||
if (res == eSuccess)
|
||||
if (m_bCancelled)
|
||||
{
|
||||
warn("Repair cancelled for %s", m_szInfoName);
|
||||
m_szErrMsg = strdup("repair cancelled");
|
||||
SetStatus(psFailed);
|
||||
}
|
||||
else if (res == eSuccess)
|
||||
{
|
||||
SetStatus(psFinished);
|
||||
}
|
||||
@@ -278,214 +333,10 @@ void ParChecker::Run()
|
||||
SetStatus(psFailed);
|
||||
}
|
||||
|
||||
delete repairer;
|
||||
delete pRepairer;
|
||||
}
|
||||
|
||||
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)
|
||||
bool ParChecker::LoadMorePars()
|
||||
{
|
||||
m_mutexQueuedParFiles.Lock();
|
||||
QueuedParFiles moreFiles;
|
||||
@@ -496,37 +347,212 @@ void ParChecker::LoadMorePars(void* repairer)
|
||||
for (QueuedParFiles::iterator it = moreFiles.begin(); it != moreFiles.end() ;it++)
|
||||
{
|
||||
char* szParFilename = *it;
|
||||
bool loadedOK = ((Repairer*)repairer)->LoadPacketsFromFile(szParFilename);
|
||||
bool loadedOK = ((Repairer*)m_pRepairer)->LoadPacketsFromFile(szParFilename);
|
||||
if (loadedOK)
|
||||
{
|
||||
info("File %s successfully loaded for par-check", BaseFileName(szParFilename), m_szInfoName);
|
||||
info("File %s successfully loaded for par-check", Util::BaseFileName(szParFilename), m_szInfoName);
|
||||
}
|
||||
else
|
||||
{
|
||||
info("Could not load file %s for par-check", BaseFileName(szParFilename), m_szInfoName);
|
||||
info("Could not load file %s for par-check", Util::BaseFileName(szParFilename), m_szInfoName);
|
||||
}
|
||||
free(szParFilename);
|
||||
}
|
||||
|
||||
return !moreFiles.empty();
|
||||
}
|
||||
|
||||
void ParChecker::AddParFile(const char * szParFilename)
|
||||
{
|
||||
m_mutexQueuedParFiles.Lock();
|
||||
m_QueuedParFiles.push_back(strdup(szParFilename));
|
||||
m_semNeedMoreFiles.Post();
|
||||
m_bQueuedParFilesChanged = true;
|
||||
m_mutexQueuedParFiles.Unlock();
|
||||
}
|
||||
|
||||
void ParChecker::QueueChanged()
|
||||
{
|
||||
m_mutexQueuedParFiles.Lock();
|
||||
m_semNeedMoreFiles.Post();
|
||||
m_bQueuedParFilesChanged = true;
|
||||
m_mutexQueuedParFiles.Unlock();
|
||||
}
|
||||
|
||||
bool ParChecker::CheckSplittedFragments()
|
||||
{
|
||||
bool bFragmentsAdded = false;
|
||||
|
||||
for (vector<Par2RepairerSourceFile*>::iterator it = ((Repairer*)m_pRepairer)->sourcefiles.begin();
|
||||
it != ((Repairer*)m_pRepairer)->sourcefiles.end(); it++)
|
||||
{
|
||||
Par2RepairerSourceFile *sourcefile = *it;
|
||||
if (!sourcefile->GetTargetExists() && AddSplittedFragments(sourcefile->TargetFileName().c_str()))
|
||||
{
|
||||
bFragmentsAdded = true;
|
||||
}
|
||||
}
|
||||
|
||||
return bFragmentsAdded;
|
||||
}
|
||||
|
||||
bool ParChecker::AddSplittedFragments(const char* szFilename)
|
||||
{
|
||||
char szDirectory[1024];
|
||||
strncpy(szDirectory, szFilename, 1024);
|
||||
szDirectory[1024-1] = '\0';
|
||||
|
||||
char* szBasename = Util::BaseFileName(szDirectory);
|
||||
if (szBasename == szDirectory)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
szBasename[-1] = '\0';
|
||||
int iBaseLen = strlen(szBasename);
|
||||
|
||||
list<CommandLine::ExtraFile> extrafiles;
|
||||
|
||||
DirBrowser dir(szDirectory);
|
||||
while (const char* filename = dir.Next())
|
||||
{
|
||||
if (!strncasecmp(filename, szBasename, iBaseLen))
|
||||
{
|
||||
const char* p = filename + iBaseLen;
|
||||
if (*p == '.')
|
||||
{
|
||||
for (p++; *p && strchr("0123456789", *p); p++) ;
|
||||
if (!*p)
|
||||
{
|
||||
debug("Found splitted fragment %s", filename);
|
||||
|
||||
char fullfilename[1024];
|
||||
snprintf(fullfilename, 1024, "%s%c%s", szDirectory, PATH_SEPARATOR, filename);
|
||||
fullfilename[1024-1] = '\0';
|
||||
|
||||
CommandLine::ExtraFile extrafile(fullfilename, Util::FileSize(fullfilename));
|
||||
extrafiles.push_back(extrafile);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool bFragmentsAdded = false;
|
||||
|
||||
if (!extrafiles.empty())
|
||||
{
|
||||
m_iExtraFiles = extrafiles.size();
|
||||
m_bVerifyingExtraFiles = true;
|
||||
bFragmentsAdded = ((Repairer*)m_pRepairer)->VerifyExtraFiles(extrafiles);
|
||||
m_bVerifyingExtraFiles = false;
|
||||
}
|
||||
|
||||
return bFragmentsAdded;
|
||||
}
|
||||
|
||||
void ParChecker::signal_filename(std::string str)
|
||||
{
|
||||
info("%s file %s", m_bRepairing ? "Repairing" : "Verifying", str.c_str());
|
||||
const char* szStageMessage[] = { "Loading file", "Verifying file", "Repairing file", "Verifying repaired file" };
|
||||
|
||||
if (m_eStage == ptRepairing)
|
||||
{
|
||||
m_eStage = ptVerifyingRepaired;
|
||||
}
|
||||
|
||||
info("%s %s", szStageMessage[m_eStage], str.c_str());
|
||||
|
||||
snprintf(m_szProgressLabel, 1024, "%s %s", szStageMessage[m_eStage], str.c_str());
|
||||
m_szProgressLabel[1024-1] = '\0';
|
||||
m_iFileProgress = 0;
|
||||
UpdateProgress();
|
||||
}
|
||||
|
||||
void ParChecker::signal_progress(double progress)
|
||||
{
|
||||
m_iFileProgress = (int)progress;
|
||||
|
||||
if (m_eStage == ptRepairing)
|
||||
{
|
||||
// calculating repair-data for all files
|
||||
m_iStageProgress = m_iFileProgress;
|
||||
}
|
||||
else
|
||||
{
|
||||
// processing individual files
|
||||
|
||||
int iTotalFiles = 0;
|
||||
if (m_eStage == ptVerifyingRepaired)
|
||||
{
|
||||
// repairing individual files
|
||||
iTotalFiles = m_iFilesToRepair;
|
||||
}
|
||||
else
|
||||
{
|
||||
// verifying individual files
|
||||
iTotalFiles = ((Repairer*)m_pRepairer)->sourcefiles.size() + m_iExtraFiles;
|
||||
}
|
||||
|
||||
if (iTotalFiles > 0)
|
||||
{
|
||||
if (m_iFileProgress < 1000)
|
||||
{
|
||||
m_iStageProgress = (m_iProcessedFiles * 1000 + m_iFileProgress) / iTotalFiles;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_iStageProgress = m_iProcessedFiles * 1000 / iTotalFiles;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_iStageProgress = 0;
|
||||
}
|
||||
}
|
||||
|
||||
debug("Current-progres: %i, Total-progress: %i", m_iFileProgress, m_iStageProgress);
|
||||
|
||||
UpdateProgress();
|
||||
}
|
||||
|
||||
void ParChecker::signal_done(std::string str, int available, int total)
|
||||
{
|
||||
m_iProcessedFiles++;
|
||||
|
||||
if (m_eStage == ptVerifyingSources)
|
||||
{
|
||||
if (available < total && !m_bVerifyingExtraFiles)
|
||||
{
|
||||
bool bFileExists = true;
|
||||
|
||||
for (vector<Par2RepairerSourceFile*>::iterator it = ((Repairer*)m_pRepairer)->sourcefiles.begin();
|
||||
it != ((Repairer*)m_pRepairer)->sourcefiles.end(); it++)
|
||||
{
|
||||
Par2RepairerSourceFile *sourcefile = *it;
|
||||
if (sourcefile && !strcmp(str.c_str(), Util::BaseFileName(sourcefile->TargetFileName().c_str())) &&
|
||||
!sourcefile->GetTargetExists())
|
||||
{
|
||||
bFileExists = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bFileExists)
|
||||
{
|
||||
warn("File %s has %i bad block(s) of total %i block(s)", str.c_str(), total - available, total);
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("File %s with %i block(s) is missing", str.c_str(), total);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ParChecker::Cancel()
|
||||
{
|
||||
#ifdef HAVE_PAR2_CANCEL
|
||||
((Repairer*)m_pRepairer)->cancelled = true;
|
||||
m_bCancelled = true;
|
||||
#else
|
||||
error("Could not cancel par-repair. The used version of libpar2 does not support the cancelling of par-repair. Libpar2 needs to be patched for that feature to work.");
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
61
ParChecker.h
61
ParChecker.h
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,7 +15,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -32,7 +32,6 @@
|
||||
|
||||
#include "Thread.h"
|
||||
#include "Observer.h"
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
class ParChecker : public Thread, public Subject
|
||||
{
|
||||
@@ -44,40 +43,63 @@ public:
|
||||
psFailed,
|
||||
psFinished
|
||||
};
|
||||
struct BlockInfo
|
||||
|
||||
enum EStage
|
||||
{
|
||||
FileInfo* m_pFileInfo;
|
||||
int m_iBlockCount;
|
||||
ptLoadingPars,
|
||||
ptVerifyingSources,
|
||||
ptRepairing,
|
||||
ptVerifyingRepaired,
|
||||
};
|
||||
|
||||
|
||||
typedef std::deque<char*> QueuedParFiles;
|
||||
typedef std::deque<BlockInfo*> Blocks;
|
||||
|
||||
private:
|
||||
char* m_szInfoName;
|
||||
char* m_szNZBFilename;
|
||||
char* m_szParFilename;
|
||||
EStatus m_eStatus;
|
||||
EStage m_eStage;
|
||||
void* m_pRepairer; // declared as void* to prevent the including of libpar2-headers into this header-file
|
||||
char* m_szErrMsg;
|
||||
bool m_bRepairNotNeeded;
|
||||
QueuedParFiles m_QueuedParFiles;
|
||||
Mutex m_mutexQueuedParFiles;
|
||||
Semaphore m_semNeedMoreFiles;
|
||||
bool m_bRepairing;
|
||||
bool m_bQueuedParFilesChanged;
|
||||
int m_iProcessedFiles;
|
||||
int m_iFilesToRepair;
|
||||
int m_iExtraFiles;
|
||||
bool m_bVerifyingExtraFiles;
|
||||
char* m_szProgressLabel;
|
||||
int m_iFileProgress;
|
||||
int m_iStageProgress;
|
||||
bool m_bCancelled;
|
||||
|
||||
bool RequestMorePars(int iBlockNeeded, int* pBlockFound);
|
||||
void FindPars(DownloadQueue* pDownloadQueue, Blocks* pBlocks, bool bStrictParName, int* pBlockFound);
|
||||
void LoadMorePars(void* repairer);
|
||||
bool LoadMorePars();
|
||||
bool CheckSplittedFragments();
|
||||
bool AddSplittedFragments(const char* szFilename);
|
||||
void signal_filename(std::string str);
|
||||
|
||||
void signal_progress(double progress);
|
||||
void signal_done(std::string str, int available, int total);
|
||||
|
||||
protected:
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
virtual bool RequestMorePars(int iBlockNeeded, int* pBlockFound) = 0;
|
||||
virtual void UpdateProgress() {}
|
||||
EStage GetStage() { return m_eStage; }
|
||||
const char* GetProgressLabel() { return m_szProgressLabel; }
|
||||
int GetFileProgress() { return m_iFileProgress; }
|
||||
int GetStageProgress() { return m_iStageProgress; }
|
||||
|
||||
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);
|
||||
@@ -86,7 +108,8 @@ public:
|
||||
bool GetRepairNotNeeded() { return m_bRepairNotNeeded; }
|
||||
void AddParFile(const char* szParFilename);
|
||||
void QueueChanged();
|
||||
static bool ParseParFilename(const char* szParFilename, int* iBaseNameLen, int* iBlocks);
|
||||
void Cancel();
|
||||
bool GetCancelled() { return m_bCancelled; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
1850
PrePostProcessor.cpp
1850
PrePostProcessor.cpp
File diff suppressed because it is too large
Load Diff
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,7 +15,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "Thread.h"
|
||||
#include "Observer.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Scanner.h"
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
#include "ParChecker.h"
|
||||
@@ -38,6 +39,18 @@
|
||||
|
||||
class PrePostProcessor : public Thread
|
||||
{
|
||||
public:
|
||||
enum EEditAction
|
||||
{
|
||||
eaPostMoveOffset = 51, // move post to m_iOffset relative to the current position in post-queue
|
||||
eaPostMoveTop,
|
||||
eaPostMoveBottom,
|
||||
eaPostDelete,
|
||||
eaHistoryDelete,
|
||||
eaHistoryReturn,
|
||||
eaHistoryProcess
|
||||
};
|
||||
|
||||
private:
|
||||
typedef std::deque<char*> FileList;
|
||||
|
||||
@@ -55,47 +68,81 @@ private:
|
||||
PrePostProcessor* owner;
|
||||
virtual void Update(Subject* Caller, void* Aspect) { owner->ParCheckerUpdate(Caller, Aspect); }
|
||||
};
|
||||
|
||||
class QueuedFile
|
||||
|
||||
class PostParChecker: public ParChecker
|
||||
{
|
||||
private:
|
||||
char* m_szNZBFilename;
|
||||
char* m_szParFilename;
|
||||
char* m_szInfoName;
|
||||
|
||||
PrePostProcessor* m_Owner;
|
||||
PostInfo* m_pPostInfo;
|
||||
protected:
|
||||
virtual bool RequestMorePars(int iBlockNeeded, int* pBlockFound);
|
||||
virtual void UpdateProgress();
|
||||
public:
|
||||
QueuedFile(const char* szNZBFilename, const char* szParFilename, const char* szInfoName);
|
||||
~QueuedFile();
|
||||
const char* GetNZBFilename() { return m_szNZBFilename; }
|
||||
const char* GetParFilename() { return m_szParFilename; }
|
||||
const char* GetInfoName() { return m_szInfoName; }
|
||||
PostInfo* GetPostInfo() { return m_pPostInfo; }
|
||||
void SetPostInfo(PostInfo* pPostInfo) { m_pPostInfo = pPostInfo; }
|
||||
|
||||
friend class PrePostProcessor;
|
||||
};
|
||||
|
||||
typedef std::deque<QueuedFile*> ParQueue;
|
||||
|
||||
struct BlockInfo
|
||||
{
|
||||
FileInfo* m_pFileInfo;
|
||||
int m_iBlockCount;
|
||||
};
|
||||
|
||||
typedef std::list<BlockInfo*> Blocks;
|
||||
#endif
|
||||
|
||||
private:
|
||||
bool m_bCheckIncomingNZBs;
|
||||
QueueCoordinatorObserver m_QueueCoordinatorObserver;
|
||||
bool m_bHasMoreJobs;
|
||||
bool m_bPostScript;
|
||||
bool m_bSchedulerPauseChanged;
|
||||
bool m_bSchedulerPause;
|
||||
bool m_bPostPause;
|
||||
Scanner m_Scanner;
|
||||
|
||||
bool IsNZBFileCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo,
|
||||
bool bIgnorePausedPars, bool bCheckPostQueue, bool bAllowOnlyOneDeleted);
|
||||
void CheckPostQueue();
|
||||
void JobCompleted(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
|
||||
void StartScriptJob(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
|
||||
void SaveQueue(DownloadQueue* pDownloadQueue);
|
||||
void SanitisePostQueue(PostQueue* pPostQueue);
|
||||
void CheckDiskSpace();
|
||||
void ApplySchedulerState();
|
||||
bool PauseDownload();
|
||||
bool UnpauseDownload();
|
||||
void NZBAdded(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
|
||||
void NZBDownloaded(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
|
||||
void NZBDeleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
|
||||
void NZBCompleted(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, bool bSaveQueue);
|
||||
bool FindMainPars(const char* szPath, FileList* pFileList);
|
||||
bool ParseParFilename(const char* szParFilename, int* iBaseNameLen, int* iBlocks);
|
||||
bool SameParCollection(const char* szFilename1, const char* szFilename2);
|
||||
bool CreatePostJobs(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, bool bParCheck, bool bPostScript, bool bAddTop);
|
||||
void DeleteQueuedFile(const char* szQueuedFile);
|
||||
void PausePars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
|
||||
NZBInfo* MergeGroups(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
|
||||
bool PostQueueMove(IDList* pIDList, EEditAction eAction, int iOffset);
|
||||
bool PostQueueDelete(IDList* pIDList);
|
||||
bool HistoryDelete(IDList* pIDList);
|
||||
bool HistoryReturn(IDList* pIDList, bool bReprocess);
|
||||
void Cleanup();
|
||||
FileInfo* GetQueueGroup(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
|
||||
void CheckHistory();
|
||||
|
||||
void PausePars(DownloadQueue* pDownloadQueue, const char* szNZBFilename);
|
||||
void CheckIncomingNZBs();
|
||||
bool WasLastUnpausedInCollection(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
|
||||
void ExecPostScript(const char* szPath, const char* szNZBFilename, const char * szParFilename, bool bParOK);
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
ParChecker m_ParChecker;
|
||||
Mutex m_mutexParChecker;
|
||||
ParQueue m_ParQueue;
|
||||
PostParChecker 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);
|
||||
bool RequestMorePars(NZBInfo* pNZBInfo, const char* szParFilename, int iBlockNeeded, int* pBlockFound);
|
||||
void FindPars(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo, const char* szParFilename,
|
||||
Blocks* pBlocks, bool bStrictParName, bool bExactParName, int* pBlockFound);
|
||||
void UpdateParProgress();
|
||||
void StartParJob(PostInfo* pPostInfo);
|
||||
#endif
|
||||
|
||||
public:
|
||||
@@ -105,6 +152,8 @@ public:
|
||||
virtual void Stop();
|
||||
void QueueCoordinatorUpdate(Subject* Caller, void* Aspect);
|
||||
bool HasMoreJobs() { return m_bHasMoreJobs; }
|
||||
void ScanNZBDir(bool bSyncMode);
|
||||
bool QueueEditList(IDList* pIDList, EEditAction eAction, int iOffset);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-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$
|
||||
@@ -29,69 +29,100 @@
|
||||
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <time.h>
|
||||
|
||||
#include "Thread.h"
|
||||
#include "NZBFile.h"
|
||||
#include "ArticleDownloader.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Observer.h"
|
||||
#include "DiskState.h"
|
||||
#include "QueueEditor.h"
|
||||
#include "NNTPConnection.h"
|
||||
|
||||
class QueueCoordinator : public Thread, public Observer, public Subject, public DownloadSpeedMeter
|
||||
class QueueCoordinator : public Thread, public Observer, public Subject, public DownloadSpeedMeter, public DownloadQueueHolder
|
||||
{
|
||||
public:
|
||||
typedef std::list<ArticleDownloader*> ActiveDownloads;
|
||||
typedef enum EAspectAction
|
||||
enum EAspectAction
|
||||
{
|
||||
eaNZBFileAdded,
|
||||
eaFileCompleted,
|
||||
eaFileDeleted
|
||||
};
|
||||
typedef struct Aspect
|
||||
struct Aspect
|
||||
{
|
||||
EAspectAction eAction;
|
||||
FileInfo* pFileInfo;
|
||||
DownloadQueue* pDownloadQueue;
|
||||
const char* szNZBFilename;
|
||||
NZBInfo* pNZBInfo;
|
||||
FileInfo* pFileInfo;
|
||||
};
|
||||
|
||||
private:
|
||||
DownloadQueue m_DownloadQueue;
|
||||
ActiveDownloads m_ActiveDownloads;
|
||||
DiskState m_DiskState;
|
||||
QueueEditor m_QueueEditor;
|
||||
Mutex m_mutexDownloadQueue;
|
||||
bool m_bHasMoreJobs;
|
||||
|
||||
// statistics
|
||||
static const int SPEEDMETER_SLOTS = 30;
|
||||
static const int SPEEDMETER_SLOTSIZE = 1; //Split elapsed time into this number of secs.
|
||||
int m_iSpeedBytes[SPEEDMETER_SLOTS];
|
||||
int m_iSpeedTotalBytes;
|
||||
int m_iSpeedTime[SPEEDMETER_SLOTS];
|
||||
int m_iSpeedStartTime;
|
||||
#ifdef HAVE_SPINLOCK
|
||||
SpinLock m_spinlockSpeed;
|
||||
#else
|
||||
Mutex m_mutexSpeed;
|
||||
#endif
|
||||
|
||||
int m_iSpeedBytesIndex;
|
||||
long long m_iAllBytes;
|
||||
time_t m_tStartServer;
|
||||
time_t m_tLastCheck;
|
||||
time_t m_tStartDownload;
|
||||
time_t m_tPausedFrom;
|
||||
bool m_bStandBy;
|
||||
Mutex m_mutexStat;
|
||||
|
||||
bool GetNextArticle(FileInfo* &pFileInfo, ArticleInfo* &pArticleInfo);
|
||||
void StartArticleDownload(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);
|
||||
FileInfo* FindFileInfo(int iID);
|
||||
int GetFileInfoEntry(int iID);
|
||||
void DeleteFileInfo(FileInfo* pFileInfo);
|
||||
void DeleteFileInfo(FileInfo* pFileInfo, bool bCompleted);
|
||||
void ResetHangingDownloads();
|
||||
void ResetSpeedStat();
|
||||
void EnterLeaveStandBy(bool bEnter);
|
||||
void AdjustStartTime();
|
||||
|
||||
public:
|
||||
QueueCoordinator();
|
||||
virtual ~QueueCoordinator();
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
long long CalcRemainingSize();
|
||||
virtual float CalcCurrentDownloadSpeed();
|
||||
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 EditQueuePauseUnpauseEntry(int iID, bool bPause);
|
||||
bool EditQueueDeleteEntry(int iID);
|
||||
bool EditQueueMoveEntry(int iID, int iOffset, bool bAutoCorrection);
|
||||
int GetFileInfoID(unsigned int iEntry);
|
||||
void AddNZBFileToQueue(NZBFile* pNZBFile, bool bAddFirst);
|
||||
bool HasMoreJobs() { return m_bHasMoreJobs; }
|
||||
|
||||
bool GetStandBy() { return m_bStandBy; }
|
||||
bool DeleteQueueEntry(FileInfo* pFileInfo);
|
||||
bool SetQueueEntryNZBCategory(NZBInfo* pNZBInfo, const char* szCategory);
|
||||
bool SetQueueEntryNZBName(NZBInfo* pNZBInfo, const char* szName);
|
||||
bool MergeQueueEntries(NZBInfo* pDestNZBInfo, NZBInfo* pSrcNZBInfo);
|
||||
void DiscardDiskFile(FileInfo* pFileInfo);
|
||||
QueueEditor* GetQueueEditor() { return &m_QueueEditor; }
|
||||
|
||||
void LogDebugInfo();
|
||||
};
|
||||
|
||||
|
||||
927
QueueEditor.cpp
Normal file
927
QueueEditor.cpp
Normal file
@@ -0,0 +1,927 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* 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 <cctype>
|
||||
#include <cstdio>
|
||||
#include <sys/stat.h>
|
||||
#include <set>
|
||||
#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 (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (pFileInfo->GetID() == iID)
|
||||
{
|
||||
return pFileInfo;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int QueueEditor::FindFileInfoEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo)
|
||||
{
|
||||
int iEntry = 0;
|
||||
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->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
|
||||
* 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 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->GetFileQueue()->size() - 1)
|
||||
{
|
||||
iNewEntry = (int)pDownloadQueue->GetFileQueue()->size() - 1;
|
||||
}
|
||||
|
||||
if (iNewEntry >= 0 && (unsigned int)iNewEntry <= pDownloadQueue->GetFileQueue()->size() - 1)
|
||||
{
|
||||
FileInfo* fi = pDownloadQueue->GetFileQueue()->at(iEntry);
|
||||
pDownloadQueue->GetFileQueue()->erase(pDownloadQueue->GetFileQueue()->begin() + iEntry);
|
||||
pDownloadQueue->GetFileQueue()->insert(pDownloadQueue->GetFileQueue()->begin() + iNewEntry, fi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Set priority for entry
|
||||
* returns true if successful, false if operation is not possible
|
||||
*/
|
||||
void QueueEditor::SetPriorityEntry(FileInfo* pFileInfo, const char* szPriority)
|
||||
{
|
||||
debug("Setting priority %s for file %s", szPriority, pFileInfo->GetFilename());
|
||||
int iPriority = atoi(szPriority);
|
||||
pFileInfo->SetPriority(iPriority);
|
||||
}
|
||||
|
||||
bool QueueEditor::EditEntry(int ID, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText)
|
||||
{
|
||||
IDList cIDList;
|
||||
cIDList.clear();
|
||||
cIDList.push_back(ID);
|
||||
return EditList(&cIDList, NULL, mmID, bSmartOrder, eAction, iOffset, szText);
|
||||
}
|
||||
|
||||
bool QueueEditor::LockedEditEntry(DownloadQueue* pDownloadQueue, int ID, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText)
|
||||
{
|
||||
IDList cIDList;
|
||||
cIDList.clear();
|
||||
cIDList.push_back(ID);
|
||||
return InternEditList(pDownloadQueue, &cIDList, bSmartOrder, eAction, iOffset, szText);
|
||||
}
|
||||
|
||||
bool QueueEditor::EditList(IDList* pIDList, NameList* pNameList, EMatchMode eMatchMode, bool bSmartOrder,
|
||||
EEditAction eAction, int iOffset, const char* szText)
|
||||
{
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
|
||||
bool bOK = true;
|
||||
|
||||
if (pNameList)
|
||||
{
|
||||
pIDList = new IDList();
|
||||
bOK = BuildIDListFromNameList(pDownloadQueue, pIDList, pNameList, eMatchMode, eAction);
|
||||
}
|
||||
|
||||
bOK = bOK && (InternEditList(pDownloadQueue, pIDList, bSmartOrder, eAction, iOffset, szText) || eMatchMode == mmRegEx);
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
|
||||
if (pNameList)
|
||||
{
|
||||
delete pIDList;
|
||||
}
|
||||
|
||||
return bOK;
|
||||
}
|
||||
|
||||
bool QueueEditor::LockedEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText)
|
||||
{
|
||||
return InternEditList(pDownloadQueue, pIDList, bSmartOrder, eAction, iOffset, szText);
|
||||
}
|
||||
|
||||
bool QueueEditor::InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText)
|
||||
{
|
||||
if (eAction == eaGroupMoveOffset)
|
||||
{
|
||||
AlignAffectedGroups(pDownloadQueue, pIDList, bSmartOrder, iOffset);
|
||||
}
|
||||
|
||||
ItemList cItemList;
|
||||
PrepareList(pDownloadQueue, &cItemList, pIDList, bSmartOrder, eAction, iOffset);
|
||||
|
||||
if (eAction == eaFilePauseAllPars || eAction == eaFilePauseExtraPars)
|
||||
{
|
||||
PauseParsInGroups(&cItemList, eAction == eaFilePauseExtraPars);
|
||||
}
|
||||
else if (eAction == eaGroupMerge)
|
||||
{
|
||||
MergeGroups(pDownloadQueue, &cItemList);
|
||||
}
|
||||
else if (eAction == eaFileReorder)
|
||||
{
|
||||
ReorderFiles(pDownloadQueue, &cItemList);
|
||||
}
|
||||
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 eaFileSetPriority:
|
||||
SetPriorityEntry(pItem->m_pFileInfo, szText);
|
||||
break;
|
||||
|
||||
case eaGroupSetCategory:
|
||||
SetNZBCategory(pItem->m_pFileInfo->GetNZBInfo(), szText);
|
||||
break;
|
||||
|
||||
case eaGroupSetName:
|
||||
SetNZBName(pItem->m_pFileInfo->GetNZBInfo(), szText);
|
||||
break;
|
||||
|
||||
case eaGroupSetParameter:
|
||||
SetNZBParameter(pItem->m_pFileInfo->GetNZBInfo(), szText);
|
||||
break;
|
||||
|
||||
case eaGroupPause:
|
||||
case eaGroupResume:
|
||||
case eaGroupDelete:
|
||||
case eaGroupMoveTop:
|
||||
case eaGroupMoveBottom:
|
||||
case eaGroupMoveOffset:
|
||||
case eaGroupPauseAllPars:
|
||||
case eaGroupPauseExtraPars:
|
||||
case eaGroupSetPriority:
|
||||
EditGroup(pDownloadQueue, pItem->m_pFileInfo, eAction, iOffset, szText);
|
||||
break;
|
||||
|
||||
case eaFilePauseAllPars:
|
||||
case eaFilePauseExtraPars:
|
||||
case eaGroupMerge:
|
||||
case eaFileReorder:
|
||||
// remove compiler warning "enumeration not handled in switch"
|
||||
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->GetFileQueue()->size();
|
||||
iStep = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
iStart = pDownloadQueue->GetFileQueue()->size() - 1;
|
||||
iEnd = -1;
|
||||
iStep = -1;
|
||||
}
|
||||
for (int iIndex = iStart; iIndex != iEnd; iIndex += iStep)
|
||||
{
|
||||
FileInfo* pFileInfo = pDownloadQueue->GetFileQueue()->at(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->GetFileQueue()->size()) - 1)
|
||||
{
|
||||
iWorkOffset = int(pDownloadQueue->GetFileQueue()->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 (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->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::BuildIDListFromNameList(DownloadQueue* pDownloadQueue, IDList* pIDList, NameList* pNameList, EMatchMode eMatchMode, EEditAction eAction)
|
||||
{
|
||||
#ifndef HAVE_REGEX_H
|
||||
if (eMatchMode == mmRegEx)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
std::set<int> uniqueIDs;
|
||||
|
||||
for (NameList::iterator it = pNameList->begin(); it != pNameList->end(); it++)
|
||||
{
|
||||
const char* szName = *it;
|
||||
|
||||
RegEx *pRegEx = NULL;
|
||||
if (eMatchMode == mmRegEx)
|
||||
{
|
||||
pRegEx = new RegEx(szName);
|
||||
if (!pRegEx->IsValid())
|
||||
{
|
||||
delete pRegEx;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool bFound = false;
|
||||
|
||||
for (FileQueue::iterator it2 = pDownloadQueue->GetFileQueue()->begin(); it2 != pDownloadQueue->GetFileQueue()->end(); it2++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it2;
|
||||
if (eAction < eaGroupMoveOffset)
|
||||
{
|
||||
// file action
|
||||
char szFilename[MAX_PATH];
|
||||
snprintf(szFilename, sizeof(szFilename) - 1, "%s/%s", pFileInfo->GetNZBInfo()->GetName(), Util::BaseFileName(pFileInfo->GetFilename()));
|
||||
if (((!pRegEx && !strcmp(szFilename, szName)) || (pRegEx && pRegEx->Match(szFilename))) &&
|
||||
(uniqueIDs.find(pFileInfo->GetID()) == uniqueIDs.end()))
|
||||
{
|
||||
uniqueIDs.insert(pFileInfo->GetID());
|
||||
pIDList->push_back(pFileInfo->GetID());
|
||||
bFound = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// group action
|
||||
const char *szFilename = pFileInfo->GetNZBInfo()->GetName();
|
||||
if (((!pRegEx && !strcmp(szFilename, szName)) || (pRegEx && pRegEx->Match(szFilename))) &&
|
||||
(uniqueIDs.find(pFileInfo->GetNZBInfo()->GetID()) == uniqueIDs.end()))
|
||||
{
|
||||
uniqueIDs.insert(pFileInfo->GetNZBInfo()->GetID());
|
||||
pIDList->push_back(pFileInfo->GetID());
|
||||
bFound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pRegEx)
|
||||
{
|
||||
delete pRegEx;
|
||||
}
|
||||
|
||||
if (!bFound && (eMatchMode == mmName))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool QueueEditor::EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, EEditAction eAction, int iOffset, const char* szText)
|
||||
{
|
||||
IDList cIDList;
|
||||
cIDList.clear();
|
||||
|
||||
// collecting files belonging to group
|
||||
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo2 = *it;
|
||||
if (pFileInfo2->GetNZBInfo() == pFileInfo->GetNZBInfo())
|
||||
{
|
||||
cIDList.push_back(pFileInfo2->GetID());
|
||||
}
|
||||
}
|
||||
|
||||
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 (pGroupInfo->GetNZBInfo() == pFileInfo->GetNZBInfo())
|
||||
{
|
||||
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;
|
||||
}
|
||||
else if (eAction == eaGroupDelete)
|
||||
{
|
||||
pFileInfo->GetNZBInfo()->SetDeleted(true);
|
||||
pFileInfo->GetNZBInfo()->SetCleanupDisk(CanCleanupDisk(pDownloadQueue, pFileInfo->GetNZBInfo()));
|
||||
}
|
||||
|
||||
EEditAction GroupToFileMap[] = { (EEditAction)0, eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom,
|
||||
eaFilePause, eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars, eaFileSetPriority, eaFileReorder,
|
||||
eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause, eaFileResume, eaFileDelete,
|
||||
eaFilePauseAllPars, eaFilePauseExtraPars, eaFileSetPriority,
|
||||
(EEditAction)0, (EEditAction)0, (EEditAction)0 };
|
||||
|
||||
return InternEditList(pDownloadQueue, &cIDList, true, GroupToFileMap[eAction], iOffset, szText);
|
||||
}
|
||||
|
||||
void QueueEditor::BuildGroupList(DownloadQueue* pDownloadQueue, FileList* pGroupList)
|
||||
{
|
||||
pGroupList->clear();
|
||||
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
FileInfo* pGroupInfo = NULL;
|
||||
for (FileList::iterator itg = pGroupList->begin(); itg != pGroupList->end(); itg++)
|
||||
{
|
||||
FileInfo* pGroupInfo1 = *itg;
|
||||
if (pGroupInfo1->GetNZBInfo() == pFileInfo->GetNZBInfo())
|
||||
{
|
||||
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 (pItem->m_pFileInfo->GetNZBInfo() == pFileInfo->GetNZBInfo())
|
||||
{
|
||||
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->GetNZBInfo());
|
||||
}
|
||||
}
|
||||
|
||||
void QueueEditor::AlignGroup(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
|
||||
{
|
||||
FileInfo* pLastFileInfo = NULL;
|
||||
unsigned int iLastNum = 0;
|
||||
unsigned int iNum = 0;
|
||||
while (iNum < pDownloadQueue->GetFileQueue()->size())
|
||||
{
|
||||
FileInfo* pFileInfo = pDownloadQueue->GetFileQueue()->at(iNum);
|
||||
if (pFileInfo->GetNZBInfo() == pNZBInfo)
|
||||
{
|
||||
if (pLastFileInfo && iNum - iLastNum > 1)
|
||||
{
|
||||
pDownloadQueue->GetFileQueue()->erase(pDownloadQueue->GetFileQueue()->begin() + iNum);
|
||||
pDownloadQueue->GetFileQueue()->insert(pDownloadQueue->GetFileQueue()->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 ||
|
||||
(pFirstFileInfo->GetNZBInfo() == pItem->m_pFileInfo->GetNZBInfo()))
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QueueEditor::SetNZBCategory(NZBInfo* pNZBInfo, const char* szCategory)
|
||||
{
|
||||
debug("QueueEditor: setting category '%s' for '%s'", szCategory, Util::BaseFileName(pNZBInfo->GetFilename()));
|
||||
|
||||
g_pQueueCoordinator->SetQueueEntryNZBCategory(pNZBInfo, szCategory);
|
||||
}
|
||||
|
||||
void QueueEditor::SetNZBName(NZBInfo* pNZBInfo, const char* szName)
|
||||
{
|
||||
debug("QueueEditor: renaming '%s' to '%s'", Util::BaseFileName(pNZBInfo->GetFilename()), szName);
|
||||
|
||||
g_pQueueCoordinator->SetQueueEntryNZBName(pNZBInfo, szName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if deletion of already downloaded files is possible (when nzb id deleted from queue).
|
||||
* The deletion is most always possible, except the case if all remaining files in queue
|
||||
* (belonging to this nzb-file) are PARS.
|
||||
*/
|
||||
bool QueueEditor::CanCleanupDisk(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo)
|
||||
{
|
||||
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
char szLoFileName[1024];
|
||||
strncpy(szLoFileName, pFileInfo->GetFilename(), 1024);
|
||||
szLoFileName[1024-1] = '\0';
|
||||
for (char* p = szLoFileName; *p; p++) *p = tolower(*p); // convert string to lowercase
|
||||
|
||||
if (!strstr(szLoFileName, ".par2"))
|
||||
{
|
||||
// non-par file found
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void QueueEditor::MergeGroups(DownloadQueue* pDownloadQueue, ItemList* pItemList)
|
||||
{
|
||||
if (pItemList->size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EditItem* pDestItem = pItemList->front();
|
||||
|
||||
for (ItemList::iterator it = pItemList->begin() + 1; it != pItemList->end(); it++)
|
||||
{
|
||||
EditItem* pItem = *it;
|
||||
if (pItem->m_pFileInfo->GetNZBInfo() != pDestItem->m_pFileInfo->GetNZBInfo())
|
||||
{
|
||||
debug("merge %s to %s", pItem->m_pFileInfo->GetNZBInfo()->GetFilename(), pDestItem->m_pFileInfo->GetNZBInfo()->GetFilename());
|
||||
g_pQueueCoordinator->MergeQueueEntries(pDestItem->m_pFileInfo->GetNZBInfo(), pItem->m_pFileInfo->GetNZBInfo());
|
||||
}
|
||||
delete pItem;
|
||||
}
|
||||
|
||||
// align group
|
||||
AlignGroup(pDownloadQueue, pDestItem->m_pFileInfo->GetNZBInfo());
|
||||
|
||||
delete pDestItem;
|
||||
}
|
||||
|
||||
void QueueEditor::ReorderFiles(DownloadQueue* pDownloadQueue, ItemList* pItemList)
|
||||
{
|
||||
if (pItemList->size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
EditItem* pFirstItem = pItemList->front();
|
||||
NZBInfo* pNZBInfo = pFirstItem->m_pFileInfo->GetNZBInfo();
|
||||
unsigned int iInsertPos = 0;
|
||||
|
||||
// find first file of the group
|
||||
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (pFileInfo->GetNZBInfo() == pNZBInfo)
|
||||
{
|
||||
break;
|
||||
}
|
||||
iInsertPos++;
|
||||
}
|
||||
|
||||
// now can reorder
|
||||
for (ItemList::iterator it = pItemList->begin(); it != pItemList->end(); it++)
|
||||
{
|
||||
EditItem* pItem = *it;
|
||||
FileInfo* pFileInfo = pItem->m_pFileInfo;
|
||||
|
||||
// move file item
|
||||
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo1 = *it;
|
||||
if (pFileInfo1 == pFileInfo)
|
||||
{
|
||||
pDownloadQueue->GetFileQueue()->erase(it);
|
||||
pDownloadQueue->GetFileQueue()->insert(pDownloadQueue->GetFileQueue()->begin() + iInsertPos, pFileInfo);
|
||||
iInsertPos++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
delete pItem;
|
||||
}
|
||||
}
|
||||
|
||||
void QueueEditor::SetNZBParameter(NZBInfo* pNZBInfo, const char* szParamString)
|
||||
{
|
||||
debug("QueueEditor: setting nzb parameter '%s' for '%s'", szParamString, Util::BaseFileName(pNZBInfo->GetFilename()));
|
||||
|
||||
char* szStr = strdup(szParamString);
|
||||
|
||||
char* szValue = strchr(szStr, '=');
|
||||
if (szValue)
|
||||
{
|
||||
*szValue = '\0';
|
||||
szValue++;
|
||||
pNZBInfo->SetParameter(szStr, szValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not set nzb parameter for %s: invalid argument: %s", pNZBInfo->GetName(), szParamString);
|
||||
}
|
||||
|
||||
free(szStr);
|
||||
}
|
||||
119
QueueEditor.h
Normal file
119
QueueEditor.h
Normal file
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* 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 QUEUEEDITOR_H
|
||||
#define QUEUEEDITOR_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
class QueueEditor
|
||||
{
|
||||
public:
|
||||
enum EEditAction
|
||||
{
|
||||
eaFileMoveOffset = 1, // move to m_iOffset relative to the current position in queue
|
||||
eaFileMoveTop,
|
||||
eaFileMoveBottom,
|
||||
eaFilePause,
|
||||
eaFileResume,
|
||||
eaFileDelete,
|
||||
eaFilePauseAllPars,
|
||||
eaFilePauseExtraPars,
|
||||
eaFileSetPriority,
|
||||
eaFileReorder,
|
||||
eaGroupMoveOffset, // move to m_iOffset relative to the current position in queue
|
||||
eaGroupMoveTop,
|
||||
eaGroupMoveBottom,
|
||||
eaGroupPause,
|
||||
eaGroupResume,
|
||||
eaGroupDelete,
|
||||
eaGroupPauseAllPars,
|
||||
eaGroupPauseExtraPars,
|
||||
eaGroupSetPriority,
|
||||
eaGroupSetCategory,
|
||||
eaGroupMerge,
|
||||
eaGroupSetParameter,
|
||||
eaGroupSetName
|
||||
};
|
||||
|
||||
enum EMatchMode
|
||||
{
|
||||
mmID = 1,
|
||||
mmName,
|
||||
mmRegEx
|
||||
};
|
||||
|
||||
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, const char* szText);
|
||||
void PrepareList(DownloadQueue* pDownloadQueue, ItemList* pItemList, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset);
|
||||
bool BuildIDListFromNameList(DownloadQueue* pDownloadQueue, IDList* pIDList, NameList* pNameList, EMatchMode eMatchMode, EEditAction eAction);
|
||||
bool EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, EEditAction eAction, int iOffset, const char* szText);
|
||||
void BuildGroupList(DownloadQueue* pDownloadQueue, FileList* pGroupList);
|
||||
void AlignAffectedGroups(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, int iOffset);
|
||||
bool ItemExists(FileList* pFileList, FileInfo* pFileInfo);
|
||||
void AlignGroup(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
|
||||
void PauseParsInGroups(ItemList* pItemList, bool bExtraParsOnly);
|
||||
void PausePars(FileList* pFileList, bool bExtraParsOnly);
|
||||
void SetNZBCategory(NZBInfo* pNZBInfo, const char* szCategory);
|
||||
void SetNZBName(NZBInfo* pNZBInfo, const char* szName);
|
||||
bool CanCleanupDisk(DownloadQueue* pDownloadQueue, NZBInfo* pNZBInfo);
|
||||
void MergeGroups(DownloadQueue* pDownloadQueue, ItemList* pItemList);
|
||||
void ReorderFiles(DownloadQueue* pDownloadQueue, ItemList* pItemList);
|
||||
void SetNZBParameter(NZBInfo* pNZBInfo, const char* szParamString);
|
||||
|
||||
void PauseUnpauseEntry(FileInfo* pFileInfo, bool bPause);
|
||||
void DeleteEntry(FileInfo* pFileInfo);
|
||||
void MoveEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, int iOffset);
|
||||
void SetPriorityEntry(FileInfo* pFileInfo, const char* szPriority);
|
||||
|
||||
public:
|
||||
QueueEditor();
|
||||
~QueueEditor();
|
||||
|
||||
bool EditEntry(int ID, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);
|
||||
bool EditList(IDList* pIDList, NameList* pNameList, EMatchMode eMatchMode, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);
|
||||
|
||||
bool LockedEditEntry(DownloadQueue* pDownloadQueue, int ID, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);
|
||||
bool LockedEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset, const char* szText);
|
||||
};
|
||||
|
||||
#endif
|
||||
369
README
369
README
@@ -2,6 +2,10 @@
|
||||
NZBGet ReadMe
|
||||
=====================================
|
||||
|
||||
This is a short documentation. For more information please
|
||||
visit NZBGet home page at
|
||||
http://nzbget.sourceforge.net
|
||||
|
||||
Contents
|
||||
--------
|
||||
1. About NZBGet
|
||||
@@ -29,33 +33,36 @@ 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
|
||||
executable file "nzbget". The mode in which the program works
|
||||
depends on command-line parameters passed to the program.
|
||||
|
||||
=====================================
|
||||
2. Supported OS
|
||||
=====================================
|
||||
|
||||
NZBGet is written in C++ and was initialy developen on Linux.
|
||||
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.0) should run at least on:
|
||||
|
||||
- Linux Debian 3.1 on x86;
|
||||
- Linux BusyBox with uClibc on MIPSEL;
|
||||
- PC-BSD 1.4 (based on FreeBSD 6.2) on x86;
|
||||
- Solaris 10 on x86;
|
||||
- Windows XP SP2 on x86.
|
||||
It should run at least on:
|
||||
- Linux Debian 5.0 on x86;
|
||||
- Linux with uClibc on MIPSEL and ARM;
|
||||
- OpenBSD 5.0 on x86;
|
||||
- Mac OS X 10.7 Lion on x64;
|
||||
- Windows XP SP3 on x86 and Windows 7 on x64.
|
||||
|
||||
Clients and servers running on different OS'es may communicate with
|
||||
each other. For example, you can use NZBGet as client on Windows to
|
||||
control your NZBGet-server running on Linux.
|
||||
|
||||
The download-section of NZBGet web-site provides binary files
|
||||
for few platforms (including Windows), but for most POSIX-systems
|
||||
you need to compile the program yourself.
|
||||
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.
|
||||
|
||||
If you have downloaded binaries you can just jump to section
|
||||
"Configuration".
|
||||
@@ -65,8 +72,7 @@ If you have downloaded binaries you can just jump to section
|
||||
=====================================
|
||||
|
||||
NZBGet is developed on a linux-system, but it should run on other
|
||||
POSIX platforms (tested on FreeBSD and Solaris for x86; although
|
||||
the par-support and uulib were not tested).
|
||||
POSIX platforms (see the list of tested platforms above).
|
||||
|
||||
NZBGet absolutely needs the following libraries:
|
||||
|
||||
@@ -75,124 +81,99 @@ NZBGet absolutely needs the following libraries:
|
||||
|
||||
And the following libraries are optional:
|
||||
|
||||
- for curses-output-mode:
|
||||
- for curses-output-mode (enabled by default):
|
||||
- libcurses (usually part of commercial systems)
|
||||
or (better)
|
||||
- libncurses (http://invisible-island.net/ncurses)
|
||||
|
||||
- for par-check and -repair:
|
||||
- for par-check and -repair (enabled by default):
|
||||
- libpar2 (http://parchive.sourceforge.net)
|
||||
- libsigc++ (http://libsigc.sourceforge.net)
|
||||
|
||||
- for encrypted connections (TLS/SSL):
|
||||
- GnuTLS (http://www.gnu.org/software/gnutls)
|
||||
or
|
||||
- OpenSSL (http://www.openssl.org)
|
||||
|
||||
- for support of encoding-formats other than yEnc:
|
||||
- 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
|
||||
should be available as installable packages. On other systems you
|
||||
may need to download the libraries at the given URLs and compile
|
||||
them (see hints below).
|
||||
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++.
|
||||
|
||||
--with-tlslib=(GnuTLS, OpenSSL) - to select which TLS/SSL library
|
||||
should be used for encrypted server connections.
|
||||
|
||||
--disable-tls - to make without TLS/SSL support. Use this option if
|
||||
you can not neither GnuTLS nor OpenSSL.
|
||||
|
||||
--disable-gzip - to make without gzip support. Use this option
|
||||
if you can not use zlib.
|
||||
|
||||
--disable-parprogress - to not show progress during par-check. This option
|
||||
may be needed on some systems, where sigc++ does not work very well
|
||||
(uClibc on MIPSEL is one of them). If par-check segfaults right after
|
||||
start, then you probably need this option.
|
||||
|
||||
--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.
|
||||
|
||||
To build with par-check use option "--enable-parcheck" while running
|
||||
configure:
|
||||
|
||||
./configure --enable-parcheck
|
||||
|
||||
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:
|
||||
If you do not have these packages you can compile them yourself.
|
||||
Following configure-parameters may be usefull:
|
||||
|
||||
--with-libpar2-includes
|
||||
--with-libpar2-libraries
|
||||
@@ -201,12 +182,31 @@ compile additional library. Following configure-parameters may be usefull:
|
||||
|
||||
The library libsigc++ must be installed first, since libpar2 requires it.
|
||||
|
||||
If you use nzbget on a very slow computer like NAS-device, it may be good to
|
||||
limit the time allowed for par-repair (option "ParTimeLimit" in nzbget
|
||||
configuration file). This feature requires a patched version of libpar2.
|
||||
To compile that version download the original source code of libpar2
|
||||
(version 0.2) and apply patches "libpar2-0.2-bugfixes.patch" and
|
||||
"libpar2-0.2-cancel.patch", provided with nzbget:
|
||||
|
||||
cd libpar2-0.2
|
||||
cp ~/nzbget/libpar2-0.2-*.patch .
|
||||
patch < libpar2-0.2-bugfixes.patch
|
||||
patch < libpar2-0.2-cancel.patch
|
||||
./configure
|
||||
make
|
||||
make install
|
||||
|
||||
If you are not able to use libpar2 or libsigc++ or do not want them you can
|
||||
make nzbget without support for par-check using option "--disable-parcheck":
|
||||
|
||||
./configure --disable-parcheck
|
||||
|
||||
Optional package: curses
|
||||
-------------------------
|
||||
For curses-outputmode you need ncurses or curses on your system.
|
||||
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 usefull:
|
||||
|
||||
--with-libcurses-includes
|
||||
--with-libcurses-libraries
|
||||
@@ -216,6 +216,29 @@ make the program without support for curses using option "--disable-curses":
|
||||
|
||||
./configure --disable-curses
|
||||
|
||||
Optional package: TLS
|
||||
-------------------------
|
||||
To enable encrypted server connections (TLS/SSL) you need to build the program
|
||||
with TLS/SSL support. NZBGet can use two libraries: GnuTLS or OpenSSL.
|
||||
Configure-script checks which library is installed and use it. If both are
|
||||
avialable it gives the precedence to GnuTLS. You may override that with
|
||||
the option --with-tlslib=(GnuTLS, OpenSSL). For example to build whith OpenSSL:
|
||||
|
||||
./configure --with-tlslib=OpenSSL
|
||||
|
||||
Following configure-parameters may be usefull:
|
||||
|
||||
--with-libtls-includes
|
||||
--with-libtls-libraries
|
||||
|
||||
--with-openssl-includes
|
||||
--with-openssl-libraries
|
||||
|
||||
If none of these libraries is available you can make the program without
|
||||
TLS/SSL support using option "--disable-tls":
|
||||
|
||||
./configure --disable-tls
|
||||
|
||||
=====================================
|
||||
5. Compiling on Windows
|
||||
=====================================
|
||||
@@ -229,31 +252,49 @@ libraries:
|
||||
|
||||
- libsigc++ (http://libsigc.sourceforge.net)
|
||||
- libpar2 (http://parchive.sourceforge.net)
|
||||
|
||||
|
||||
Download these libaries, then use patch-files provided with NZBGet to create
|
||||
preconfigured project files and solutions for each library.
|
||||
Look at http://gnuwin32.sourceforge.net/packages/patch.htm for info on how
|
||||
to use patch-files, if you do not familiar with this technique.
|
||||
|
||||
After libsigc++ and libpar2 are compiled in static libraries (.lib)
|
||||
and include- and libraries-paths are configured in MS Visual C++ 2005 you
|
||||
should be able to compile NZBGet.
|
||||
To compile the program with TLS/SSL support you also need the library:
|
||||
|
||||
- GnuTLS (http://www.gnu.org/software/gnutls)
|
||||
|
||||
Download a precompiled version of GnuTLS from http://josefsson.org/gnutls4win
|
||||
and create lib-file as described there in section "Using the GnuTLS DLL from
|
||||
your Visual Studio program".
|
||||
|
||||
After libsigc++ and libpar2 are compiled in static libraries (.lib), the
|
||||
library for GnuTLS is created and include- and libraries-paths are configured
|
||||
in MS Visual C++ 2005 you should be able to compile NZBGet.
|
||||
|
||||
=====================================
|
||||
6. Configuration
|
||||
=====================================
|
||||
|
||||
NZBGet needs a configuration-file to work properly.
|
||||
NZBGet needs a configuration file.
|
||||
|
||||
You need to set at least the option "MAINDIR" and one newsserver in
|
||||
configuration file. Have a look at the example in nzbget.conf.example,
|
||||
it has comments on how to use each option.
|
||||
An example configuration file is provided in "nzbget.conf", which
|
||||
is installed into "<prefix>/share/nzbget" (where <prefix> depends on
|
||||
system configuration and configure options - typically "/usr/local",
|
||||
"/usr" or "/opt"). The installer adjusts the file according to your
|
||||
system paths. If you have performed the installation step
|
||||
"make install-conf" this file is already copied to "<prefix>/etc" and
|
||||
NZBGet finds it automatically. If you install the program manually
|
||||
from a binary archive you have to copy the file from "<prefix>/share/nzbget"
|
||||
to one of the locations listed below.
|
||||
|
||||
Open the file in a text editor and modify it accodring to your needs.
|
||||
|
||||
You need to set at least the option "MAINDIR" and one news server in
|
||||
configuration file. The file has comments on how to use each option.
|
||||
|
||||
The program looks for configuration file in following standard
|
||||
locations (in this order):
|
||||
|
||||
On POSIX systems:
|
||||
|
||||
~/.nzbget
|
||||
/etc/nzbget.conf
|
||||
/usr/etc/nzbget.conf
|
||||
@@ -277,6 +318,12 @@ options via command-line.
|
||||
NZBGet can be used in either standalone mode which downloads a single file
|
||||
or as a server which is able to queue up numerous download requests.
|
||||
|
||||
TIP for Windows users: NZBGet is controlled via various command line
|
||||
parameters. For easier using there is a simple shell script included
|
||||
in "nzbget-shell.bat". Start this script from Windows Explorer and you will
|
||||
be running a command shell with PATH adjusted to find NZBGet executable.
|
||||
Then you can type all commands without full path to nzbget.exe.
|
||||
|
||||
Standalone mode:
|
||||
----------------
|
||||
|
||||
@@ -304,41 +351,61 @@ 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:
|
||||
|
||||
nzbget -C
|
||||
|
||||
The client have three different (display) outputmodes, which you can select
|
||||
in configuration file (on client computer) or in command line. Try them:
|
||||
|
||||
nzbget -o outputmode=log -C
|
||||
|
||||
nzbget -o outputmode=color -C
|
||||
|
||||
nzbget -o outputmode=curses -C
|
||||
|
||||
To list files in server's queue:
|
||||
|
||||
nzbget -L
|
||||
|
||||
It prints something like:
|
||||
|
||||
[1] nzbname\filename1.rar (50.00 MB)
|
||||
[2] nzbname\filename1.r01 (50.00 MB)
|
||||
[1] nzbname\filename1.rar (50.00 MB)
|
||||
[2] nzbname\filename1.r01 (50.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:
|
||||
|
||||
nzbget -E T -I 2
|
||||
nzbget -E T 2
|
||||
|
||||
or to pause files with IDs from 10 to 20:
|
||||
|
||||
nzbget -E P -I 10-20
|
||||
nzbget -E P 10-20
|
||||
|
||||
or to delete file from queue:
|
||||
or to delete files from queue:
|
||||
|
||||
nzbget -E D -I 3
|
||||
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:
|
||||
|
||||
nzbget -E G D 1
|
||||
|
||||
The switch "o" is useful to override options in configuration files.
|
||||
For example:
|
||||
@@ -355,7 +422,7 @@ Running client & server on seperate machines:
|
||||
Since nzbget communicates via TCP/IP it's possible to have a server running on
|
||||
one computer and adding downloads via a client on another computer.
|
||||
|
||||
Do this by setting the "serverip" option in the nzbget.conf file to point to the
|
||||
Do this by setting the "ControlIP" option in the nzbget.conf file to point to the
|
||||
IP of the server (default is localhost which means client and server runs on
|
||||
same computer)
|
||||
|
||||
@@ -371,36 +438,104 @@ 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
|
||||
script, defined in configuration file. See example configuration file for
|
||||
the description of parameters passed to the script (option "PostProcess").
|
||||
|
||||
An example script for unraring of downloaded files is provided in file
|
||||
"nzbget-postprocess.sh" installed into "<prefix>/bin". The script requires
|
||||
configuration file "nzbget-postprocess.conf". If you have installed the
|
||||
program with "make install" this file is copied to "<prefix>/etc",
|
||||
where the post-processing script finds it automatically. If you install
|
||||
the program manually from a binary archive you have to copy the file
|
||||
from "<prefix>/share/nzbget" to the directory where you have put the
|
||||
nzbget configuration file ("nzbget.conf").
|
||||
|
||||
Set the option "PostProcess" in "nzbget.conf" to point to the post-
|
||||
processing script.
|
||||
|
||||
Additional usage instructions are included in "nzbget-postprocess.sh",
|
||||
please open the file in a text editor to read them.
|
||||
|
||||
NOTE: The post-processing script "nzbget-postprocess.sh" is for
|
||||
POSIX systems and will not work on Windows.
|
||||
|
||||
Web-interface
|
||||
-------------
|
||||
|
||||
NZBGet has a built-in web-server providing the access to the program
|
||||
functions via web-interface.
|
||||
|
||||
To activate web-interface set the option "WebDir" to the path with
|
||||
web-interface files. If you install using "make install-conf" as
|
||||
described above the option is set automatically. If you install using
|
||||
binary files you should check if the option is set correctly.
|
||||
|
||||
To access web-interface from your web-browser use the server address
|
||||
and port defined in NZBGet configuration file in options "ControlIP" and
|
||||
"ControlPort". For example:
|
||||
|
||||
http://localhost:6789/
|
||||
|
||||
For login credentials type username "nzbget" (predefined and not changeable)
|
||||
and the password from the option "ControlPassword" (default is tegbzn6789).
|
||||
|
||||
In a case your browser forget credentials, to prevent typing them each
|
||||
time, there is a workaround - use URL in the form:
|
||||
|
||||
http://localhost:6789/nzbget:password/
|
||||
|
||||
Please note, that in this case the password is saved in a bookmark or in
|
||||
browser history in plain text and is easy to find by persons having
|
||||
access to your computer.
|
||||
|
||||
=====================================
|
||||
8. Authors
|
||||
=====================================
|
||||
|
||||
NZBGet was initialiy written by Sven Henkel (sidddy@users.sourceforge.net).
|
||||
Up to version 0.2.3 it was been developed and maintained by Bo Cordes Petersen
|
||||
(placebodk@users.sourceforge.net). Beginning at version 0.3.0 the program is
|
||||
being developed by Andrei Prygounkov (hugbug@users.sourceforge.net).
|
||||
NZBGet is developed and maintained by Andrey Prygunkov
|
||||
(hugbug@users.sourceforge.net).
|
||||
|
||||
The original project was initially created by Sven Henkel
|
||||
(sidddy@users.sourceforge.net) in 2004 and later developed by
|
||||
Bo Cordes Petersen (placebodk@users.sourceforge.net) until 2005.
|
||||
In 2007 the abandoned project was overtaken by Andrey Prygunkov.
|
||||
Since then the program has been completely rewritten.
|
||||
|
||||
Module TLS (TLS.c, TLS.h) is based on work by Martin Lambers
|
||||
(marlam@marlam.de).
|
||||
|
||||
=====================================
|
||||
9. Copyright
|
||||
=====================================
|
||||
|
||||
NZBGet is distributed under GNU General Pubic License Version 2.
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
The complete content of license is provided in file COPYING.
|
||||
|
||||
Additional exemption: compiling, linking, and/or using OpenSSL is allowed.
|
||||
|
||||
Binary distribution for Windows contains code from the following libraries:
|
||||
|
||||
- libpar2 (http://parchive.sourceforge.net)
|
||||
- libsigc++ (http://libsigc.sourceforge.net)
|
||||
- GnuTLS (http://www.gnu.org/software/gnutls)
|
||||
|
||||
libpar2 is distributed under GPL and libsigc++ under LGPL.
|
||||
libpar2 is distributed under GPL; libsigc++ and GnuTLS - under LGPL.
|
||||
|
||||
=====================================
|
||||
10. Contact
|
||||
=====================================
|
||||
|
||||
If you encounter any problem, feel free to use tracker/forums on
|
||||
If you encounter any problem, feel free to use the forum
|
||||
|
||||
sourceforge.net/projects/nzbget
|
||||
nzbget.sourceforge.net/forum
|
||||
|
||||
or contact me at
|
||||
|
||||
|
||||
1169
RemoteClient.cpp
1169
RemoteClient.cpp
File diff suppressed because it is too large
Load Diff
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -30,32 +30,54 @@
|
||||
#include "Options.h"
|
||||
#include "MessageBase.h"
|
||||
#include "Connection.h"
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
class RemoteClient
|
||||
{
|
||||
private:
|
||||
class MatchedNZBInfo: public NZBInfo
|
||||
{
|
||||
public:
|
||||
bool m_bMatch;
|
||||
};
|
||||
|
||||
class MatchedFileInfo: public FileInfo
|
||||
{
|
||||
public:
|
||||
bool m_bMatch;
|
||||
};
|
||||
|
||||
Connection* m_pConnection;
|
||||
NetAddress* m_pNetAddress;
|
||||
bool m_bVerbose;
|
||||
|
||||
bool InitConnection();
|
||||
void InitMessageBase(SNZBMessageBase* pMessageBase, int iRequest, int iSize);
|
||||
void ReceiveCommandResult();
|
||||
void printf(char* msg, ...);
|
||||
void perror(char* msg);
|
||||
void InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize);
|
||||
bool ReceiveBoolResponse();
|
||||
void printf(const char* msg, ...);
|
||||
void perror(const char* msg);
|
||||
|
||||
public:
|
||||
RemoteClient();
|
||||
~RemoteClient();
|
||||
RemoteClient();
|
||||
~RemoteClient();
|
||||
void SetVerbose(bool bVerbose) { m_bVerbose = bVerbose; };
|
||||
bool RequestServerDownload(const char* szName, bool bAddFirst);
|
||||
bool RequestServerList();
|
||||
bool RequestServerPauseUnpause(bool bPause);
|
||||
bool RequestServerDownload(const char* szFilename, const char* szCategory, bool bAddFirst, bool bAddPaused, int iPriority);
|
||||
bool RequestServerList(bool bFiles, bool bGroups, const char* szPattern);
|
||||
bool RequestServerPauseUnpause(bool bPause, eRemotePauseUnpauseAction iAction);
|
||||
bool RequestServerSetDownloadRate(float fRate);
|
||||
bool RequestServerDumpDebug();
|
||||
bool RequestServerEditQueue(int iAction, int iOffset, int iIDFrom, int iIDTo);
|
||||
bool RequestServerEditQueue(eRemoteEditAction iAction, int iOffset, const char* szText,
|
||||
int* pIDList, int iIDCount, NameList* pNameList, eRemoteMatchMode iMatchMode, bool bSmartOrder);
|
||||
bool RequestServerLog(int iLines);
|
||||
bool RequestServerShutdown();
|
||||
bool RequestServerReload();
|
||||
bool RequestServerVersion();
|
||||
bool RequestPostQueue();
|
||||
bool RequestWriteLog(int iKind, const char* szText);
|
||||
bool RequestScan(bool bSyncMode);
|
||||
bool RequestHistory();
|
||||
bool RequestServerDownloadUrl(const char* szURL, const char* szNZBFilename, const char* szCategory, bool bAddFirst, bool bAddPaused, int iPriority);
|
||||
bool RequestUrlQueue();
|
||||
void BuildFileList(SNZBListResponse* pListResponse, const char* pTrailingData, DownloadQueue* pDownloadQueue);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
525
RemoteServer.cpp
525
RemoteServer.cpp
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
|
||||
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -43,19 +43,12 @@
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "RemoteServer.h"
|
||||
#include "BinRpc.h"
|
||||
#include "WebServer.h"
|
||||
#include "Log.h"
|
||||
#include "Options.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
extern void ExitProc();
|
||||
|
||||
const char* g_szMessageRequests[] =
|
||||
{ "N/A", "Download", "Pause/Unpause", "List",
|
||||
"Set download rate", "Dump debug", "Edit queue", "Log", "Quit"
|
||||
};
|
||||
|
||||
//*****************************************************************
|
||||
// RemoteServer
|
||||
@@ -64,50 +57,63 @@ RemoteServer::RemoteServer()
|
||||
{
|
||||
debug("Creating RemoteServer");
|
||||
|
||||
m_pNetAddress = new NetAddress(g_pOptions->GetServerIP(), g_pOptions->GetServerPort());
|
||||
m_pConnection = new Connection(m_pNetAddress);
|
||||
m_pConnection->SetTimeout(g_pOptions->GetConnectionTimeout());
|
||||
m_pConnection = NULL;
|
||||
}
|
||||
|
||||
RemoteServer::~RemoteServer()
|
||||
{
|
||||
debug("Destroying RemoteServer");
|
||||
|
||||
delete m_pNetAddress;
|
||||
delete m_pConnection;
|
||||
if (m_pConnection)
|
||||
{
|
||||
delete m_pConnection;
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteServer::Run()
|
||||
{
|
||||
debug("Entering RemoteServer-loop");
|
||||
|
||||
m_pConnection->Bind();
|
||||
|
||||
while (!IsStopped())
|
||||
{
|
||||
// Accept connections and store the "new" socket value
|
||||
SOCKET iSocket = m_pConnection->Accept();
|
||||
if (iSocket == INVALID_SOCKET)
|
||||
bool bBind = true;
|
||||
|
||||
if (!m_pConnection)
|
||||
{
|
||||
m_pConnection = new Connection(g_pOptions->GetControlIP(), g_pOptions->GetControlPort(), false);
|
||||
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;
|
||||
}
|
||||
// error binding on port. wait 0.5 sec. and retry
|
||||
usleep(500 * 1000);
|
||||
delete m_pConnection;
|
||||
m_pConnection = new Connection(m_pNetAddress);
|
||||
m_pConnection->SetTimeout(g_pOptions->GetConnectionTimeout());
|
||||
m_pConnection->Bind();
|
||||
m_pConnection = NULL;
|
||||
continue;
|
||||
}
|
||||
|
||||
MessageCommand* commandThread = new MessageCommand();
|
||||
RequestProcessor* commandThread = new RequestProcessor();
|
||||
commandThread->SetAutoDestroy(true);
|
||||
commandThread->SetSocket(iSocket);
|
||||
commandThread->Start();
|
||||
}
|
||||
m_pConnection->Disconnect();
|
||||
if (m_pConnection)
|
||||
{
|
||||
m_pConnection->Disconnect();
|
||||
}
|
||||
|
||||
debug("Exiting RemoteServer-loop");
|
||||
}
|
||||
@@ -115,413 +121,104 @@ void RemoteServer::Run()
|
||||
void RemoteServer::Stop()
|
||||
{
|
||||
Thread::Stop();
|
||||
m_pConnection->Cancel();
|
||||
if (m_pConnection)
|
||||
{
|
||||
m_pConnection->SetSuppressErrors(true);
|
||||
m_pConnection->Cancel();
|
||||
#ifdef WIN32
|
||||
m_pConnection->Disconnect();
|
||||
m_pConnection->Disconnect();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
//*****************************************************************
|
||||
// MessageCommand
|
||||
// RequestProcessor
|
||||
|
||||
void MessageCommand::SendResponse(char* szAnswer)
|
||||
void RequestProcessor::Run()
|
||||
{
|
||||
send(m_iSocket, szAnswer, strlen(szAnswer), 0);
|
||||
}
|
||||
|
||||
void MessageCommand::ProcessRequest()
|
||||
{
|
||||
SNZBMessageBase* pMessageBase = (SNZBMessageBase*) & m_RequestBuffer;
|
||||
|
||||
switch (pMessageBase->m_iType)
|
||||
// Read the first 4 bytes to determine request type
|
||||
bool bOK = false;
|
||||
int iSignature = 0;
|
||||
int iBytesReceived = recv(m_iSocket, (char*)&iSignature, sizeof(iSignature), 0);
|
||||
if (iBytesReceived < 0)
|
||||
{
|
||||
case NZBMessageRequest::eRequestDownload:
|
||||
{
|
||||
RequestDownload();
|
||||
break;
|
||||
}
|
||||
|
||||
case NZBMessageRequest::eRequestList:
|
||||
{
|
||||
RequestList();
|
||||
break;
|
||||
}
|
||||
|
||||
case NZBMessageRequest::eRequestLog:
|
||||
{
|
||||
RequestLog();
|
||||
break;
|
||||
}
|
||||
|
||||
case NZBMessageRequest::eRequestPauseUnpause:
|
||||
{
|
||||
SNZBPauseUnpauseRequest* pPauseUnpauseRequest = (SNZBPauseUnpauseRequest*) & m_RequestBuffer;
|
||||
g_pOptions->SetPause(pPauseUnpauseRequest->m_bPause);
|
||||
SendResponse("Pause-/Unpause-Command completed successfully");
|
||||
break;
|
||||
}
|
||||
|
||||
case NZBMessageRequest::eRequestEditQueue:
|
||||
{
|
||||
RequestEditQueue();
|
||||
break;
|
||||
}
|
||||
|
||||
case NZBMessageRequest::eRequestSetDownloadRate:
|
||||
{
|
||||
SNZBSetDownloadRateRequest* pSetDownloadRequest = (SNZBSetDownloadRateRequest*) & m_RequestBuffer;
|
||||
g_pOptions->SetDownloadRate(pSetDownloadRequest->m_fDownloadRate);
|
||||
SendResponse("Rate-Command completed successfully");
|
||||
break;
|
||||
}
|
||||
|
||||
case NZBMessageRequest::eRequestDumpDebug:
|
||||
{
|
||||
g_pQueueCoordinator->LogDebugInfo();
|
||||
SendResponse("Debug-Command completed successfully");
|
||||
break;
|
||||
}
|
||||
|
||||
case NZBMessageRequest::eRequestShutdown:
|
||||
{
|
||||
SendResponse("Stopping server");
|
||||
ExitProc();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
error("NZB-Request not yet supported");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void MessageCommand::RequestDownload()
|
||||
{
|
||||
SNZBDownloadRequest* pDownloadRequest = (SNZBDownloadRequest*) & m_RequestBuffer;
|
||||
const char* pExtraData = (m_iExtraDataLength > 0) ? ((char*)pDownloadRequest + pDownloadRequest->m_MessageBase.m_iSize) : NULL;
|
||||
int NeedBytes = pDownloadRequest->m_iTrailingDataLength - m_iExtraDataLength;
|
||||
char* pRecvBuffer = (char*)malloc(pDownloadRequest->m_iTrailingDataLength + 1);
|
||||
memcpy(pRecvBuffer, pExtraData, m_iExtraDataLength);
|
||||
char* pBufPtr = pRecvBuffer + m_iExtraDataLength;
|
||||
|
||||
// Read from the socket until nothing remains
|
||||
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;
|
||||
}
|
||||
|
||||
if (NeedBytes == 0)
|
||||
{
|
||||
NZBFile* pNZBFile = NZBFile::CreateFromBuffer(pDownloadRequest->m_szFilename, pRecvBuffer, pDownloadRequest->m_iTrailingDataLength);
|
||||
|
||||
if (pNZBFile)
|
||||
{
|
||||
info("Request: Queue collection %s", pDownloadRequest->m_szFilename);
|
||||
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, pDownloadRequest->m_bAddFirst);
|
||||
delete pNZBFile;
|
||||
|
||||
char tmp[1024];
|
||||
snprintf(tmp, 1024, "Collection %s added to queue", BaseFileName(pDownloadRequest->m_szFilename));
|
||||
tmp[1024-1] = '\0';
|
||||
SendResponse(tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
char tmp[1024];
|
||||
snprintf(tmp, 1024, "Download Request failed for %s", BaseFileName(pDownloadRequest->m_szFilename));
|
||||
tmp[1024-1] = '\0';
|
||||
SendResponse(tmp);
|
||||
}
|
||||
}
|
||||
|
||||
free(pRecvBuffer);
|
||||
}
|
||||
|
||||
void MessageCommand::RequestList()
|
||||
{
|
||||
SNZBListRequest* pListRequest = (SNZBListRequest*) & m_RequestBuffer;
|
||||
|
||||
SNZBListRequestAnswer ListRequestAnswer;
|
||||
memset(&ListRequestAnswer, 0, sizeof(ListRequestAnswer));
|
||||
ListRequestAnswer.m_iSize = sizeof(ListRequestAnswer);
|
||||
ListRequestAnswer.m_iEntrySize = sizeof(SNZBListRequestAnswerEntry);
|
||||
|
||||
char* buf = NULL;
|
||||
int bufsize = 0;
|
||||
|
||||
if (pListRequest->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(SNZBListRequestAnswerEntry);
|
||||
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++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
SNZBListRequestAnswerEntry* pListAnswer = (SNZBListRequestAnswerEntry*) bufptr;
|
||||
pListAnswer->m_iID = pFileInfo->GetID();
|
||||
pListAnswer->m_iFileSize = (int)pFileInfo->GetSize();
|
||||
pListAnswer->m_bFilenameConfirmed = pFileInfo->GetFilenameConfirmed();
|
||||
pListAnswer->m_iRemainingSize = (int)pFileInfo->GetRemainingSize();
|
||||
pListAnswer->m_bPaused = pFileInfo->GetPaused();
|
||||
pListAnswer->m_iNZBFilenameLen = strlen(pFileInfo->GetNZBFilename()) + 1;
|
||||
pListAnswer->m_iSubjectLen = strlen(pFileInfo->GetSubject()) + 1;
|
||||
pListAnswer->m_iFilenameLen = strlen(pFileInfo->GetFilename()) + 1;
|
||||
pListAnswer->m_iDestDirLen = strlen(pFileInfo->GetDestDir()) + 1;
|
||||
bufptr += sizeof(SNZBListRequestAnswerEntry);
|
||||
strcpy(bufptr, pFileInfo->GetNZBFilename());
|
||||
bufptr += pListAnswer->m_iNZBFilenameLen;
|
||||
strcpy(bufptr, pFileInfo->GetSubject());
|
||||
bufptr += pListAnswer->m_iSubjectLen;
|
||||
strcpy(bufptr, pFileInfo->GetFilename());
|
||||
bufptr += pListAnswer->m_iFilenameLen;
|
||||
strcpy(bufptr, pFileInfo->GetDestDir());
|
||||
bufptr += pListAnswer->m_iDestDirLen;
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
|
||||
ListRequestAnswer.m_iNrTrailingEntries = NrEntries;
|
||||
ListRequestAnswer.m_iTrailingDataLength = bufsize;
|
||||
}
|
||||
|
||||
if (pListRequest->m_bServerState)
|
||||
{
|
||||
ListRequestAnswer.m_fDownloadRate = g_pQueueCoordinator->CalcCurrentDownloadSpeed();
|
||||
ListRequestAnswer.m_lRemainingSize = g_pQueueCoordinator->CalcRemainingSize();
|
||||
ListRequestAnswer.m_fDownloadLimit = g_pOptions->GetDownloadRate();
|
||||
ListRequestAnswer.m_bServerPaused = g_pOptions->GetPause();
|
||||
ListRequestAnswer.m_iThreadCount = Thread::GetThreadCount() - 1; // not counting itself
|
||||
}
|
||||
|
||||
// Send the request answer
|
||||
send(m_iSocket, (char*) &ListRequestAnswer, sizeof(ListRequestAnswer), 0);
|
||||
|
||||
// Send the data
|
||||
if (bufsize > 0)
|
||||
{
|
||||
send(m_iSocket, buf, bufsize, 0);
|
||||
}
|
||||
|
||||
if (buf)
|
||||
{
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
|
||||
void MessageCommand::RequestLog()
|
||||
{
|
||||
SNZBLogRequest* pLogRequest = (SNZBLogRequest*) & m_RequestBuffer;
|
||||
|
||||
Log::Messages* pMessages = g_pLog->LockMessages();
|
||||
|
||||
int iNrEntries = pLogRequest->m_iLines;
|
||||
unsigned int iIDFrom = pLogRequest->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(SNZBLogRequestAnswerEntry);
|
||||
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];
|
||||
SNZBLogRequestAnswerEntry* pLogAnswer = (SNZBLogRequestAnswerEntry*) bufptr;
|
||||
pLogAnswer->m_iID = pMessage->GetID();
|
||||
pLogAnswer->m_iKind = pMessage->GetKind();
|
||||
pLogAnswer->m_tTime = pMessage->GetTime();
|
||||
pLogAnswer->m_iTextLen = strlen(pMessage->GetText()) + 1;
|
||||
bufptr += sizeof(SNZBLogRequestAnswerEntry);
|
||||
strcpy(bufptr, pMessage->GetText());
|
||||
bufptr += pLogAnswer->m_iTextLen;
|
||||
}
|
||||
|
||||
g_pLog->UnlockMessages();
|
||||
|
||||
SNZBLogRequestAnswer LogRequestAnswer;
|
||||
LogRequestAnswer.m_iSize = sizeof(LogRequestAnswer);
|
||||
LogRequestAnswer.m_iEntrySize = sizeof(SNZBLogRequestAnswerEntry);
|
||||
LogRequestAnswer.m_iNrTrailingEntries = iNrEntries;
|
||||
LogRequestAnswer.m_iTrailingDataLength = bufsize;
|
||||
|
||||
// Send the request answer
|
||||
send(m_iSocket, (char*) &LogRequestAnswer, sizeof(LogRequestAnswer), 0);
|
||||
|
||||
// Send the data
|
||||
if (bufsize > 0)
|
||||
{
|
||||
send(m_iSocket, buf, bufsize, 0);
|
||||
}
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
void MessageCommand::RequestEditQueue()
|
||||
{
|
||||
SNZBEditQueueRequest* pEditQueueRequest = (SNZBEditQueueRequest*) & m_RequestBuffer;
|
||||
|
||||
int From = pEditQueueRequest->m_iIDFrom;
|
||||
int To = pEditQueueRequest->m_iIDTo;
|
||||
int Step = 1;
|
||||
if ((pEditQueueRequest->m_iAction == NZBMessageRequest::eActionMoveTop) ||
|
||||
((pEditQueueRequest->m_iAction == NZBMessageRequest::eActionMoveOffset) &&
|
||||
(pEditQueueRequest->m_iOffset < 0)))
|
||||
{
|
||||
Step = -1;
|
||||
int tmp = From; From = To; To = tmp;
|
||||
}
|
||||
|
||||
for (int ID = From; ID != To + Step; ID += Step)
|
||||
{
|
||||
switch (pEditQueueRequest->m_iAction)
|
||||
{
|
||||
case NZBMessageRequest::eActionPause:
|
||||
case NZBMessageRequest::eActionResume:
|
||||
{
|
||||
g_pQueueCoordinator->EditQueuePauseUnpauseEntry(ID, pEditQueueRequest->m_iAction == NZBMessageRequest::eActionPause);
|
||||
break;
|
||||
}
|
||||
|
||||
case NZBMessageRequest::eActionMoveOffset:
|
||||
{
|
||||
g_pQueueCoordinator->EditQueueMoveEntry(ID, pEditQueueRequest->m_iOffset, true);
|
||||
break;
|
||||
}
|
||||
|
||||
case NZBMessageRequest::eActionMoveTop:
|
||||
{
|
||||
g_pQueueCoordinator->EditQueueMoveEntry(ID, -1000000, true);
|
||||
break;
|
||||
}
|
||||
|
||||
case NZBMessageRequest::eActionMoveBottom:
|
||||
{
|
||||
g_pQueueCoordinator->EditQueueMoveEntry(ID, + 1000000, true);
|
||||
break;
|
||||
}
|
||||
|
||||
case NZBMessageRequest::eActionDelete:
|
||||
{
|
||||
g_pQueueCoordinator->EditQueueDeleteEntry(ID);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
SendResponse("Edit-Command completed successfully");
|
||||
}
|
||||
|
||||
void MessageCommand::SetSocket(SOCKET iSocket)
|
||||
{
|
||||
m_iSocket = iSocket;
|
||||
}
|
||||
|
||||
void MessageCommand::Run()
|
||||
{
|
||||
int iRequestReceived = 0;
|
||||
|
||||
// Read the first package which needs to be a request
|
||||
|
||||
iRequestReceived = recv(m_iSocket, (char*) & m_RequestBuffer, sizeof(m_RequestBuffer), 0);
|
||||
if (iRequestReceived < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SNZBMessageBase* pMessageBase = (SNZBMessageBase*) & m_RequestBuffer;
|
||||
|
||||
// Make sure this is a nzbget request from a client
|
||||
if (pMessageBase->m_iId != NZBMESSAGE_SIGNATURE)
|
||||
{
|
||||
warn("Non-nzbget request received on port %i", g_pOptions->GetServerPort());
|
||||
|
||||
if (m_iSocket > -1)
|
||||
{
|
||||
closesocket(m_iSocket);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(pMessageBase->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
|
||||
#ifdef WIN32
|
||||
char* ip = NULL;
|
||||
#else
|
||||
char ip[20];
|
||||
#endif
|
||||
struct sockaddr_in PeerName;
|
||||
int iPeerNameLength = sizeof(PeerName);
|
||||
if (getpeername(m_iSocket, (struct sockaddr*)&PeerName, (socklen_t*) &iPeerNameLength) >= 0)
|
||||
if (getpeername(m_iSocket, (struct sockaddr*)&PeerName, (SOCKLEN_T*) &iPeerNameLength) >= 0)
|
||||
{
|
||||
#ifdef WIN32
|
||||
char* ip = inet_ntoa(PeerName.sin_addr);
|
||||
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_szMessageRequests[pMessageBase->m_iType], ip);
|
||||
}
|
||||
|
||||
m_iExtraDataLength = iRequestReceived - pMessageBase->m_iSize;
|
||||
if ((int)ntohl(iSignature) == (int)NZBMESSAGE_SIGNATURE)
|
||||
{
|
||||
// binary request received
|
||||
bOK = true;
|
||||
BinRpcProcessor processor;
|
||||
processor.SetSocket(m_iSocket);
|
||||
processor.SetSignature(iSignature);
|
||||
processor.SetClientIP(ip);
|
||||
processor.Execute();
|
||||
}
|
||||
else if (!strncmp((char*)&iSignature, "POST", 4) ||
|
||||
!strncmp((char*)&iSignature, "GET ", 4) ||
|
||||
!strncmp((char*)&iSignature, "OPTI", 4))
|
||||
{
|
||||
// HTTP request received
|
||||
Connection con(m_iSocket, false);
|
||||
char szBuffer[1024];
|
||||
if (con.ReadLine(szBuffer, sizeof(szBuffer), NULL))
|
||||
{
|
||||
WebProcessor::EHttpMethod eHttpMethod = WebProcessor::hmGet;
|
||||
char* szUrl = szBuffer;
|
||||
if (!strncmp((char*)&iSignature, "POST", 4))
|
||||
{
|
||||
eHttpMethod = WebProcessor::hmPost;
|
||||
szUrl++;
|
||||
}
|
||||
if (!strncmp((char*)&iSignature, "OPTI", 4) && strlen(szUrl) > 4)
|
||||
{
|
||||
eHttpMethod = WebProcessor::hmOptions;
|
||||
szUrl += 4;
|
||||
}
|
||||
if (char* p = strchr(szUrl, ' '))
|
||||
{
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
ProcessRequest();
|
||||
debug("url: %s", szUrl);
|
||||
|
||||
WebProcessor processor;
|
||||
processor.SetConnection(&con);
|
||||
processor.SetClientIP(ip);
|
||||
processor.SetUrl(szUrl);
|
||||
processor.SetHttpMethod(eHttpMethod);
|
||||
processor.Execute();
|
||||
bOK = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bOK && iBytesReceived > 0)
|
||||
{
|
||||
warn("Non-nzbget request received on port %i from %s", g_pOptions->GetControlPort(), ip);
|
||||
}
|
||||
|
||||
if (!bOK && iBytesReceived == 0)
|
||||
{
|
||||
debug("empty request received on port %i from %s", g_pOptions->GetControlPort(), ip);
|
||||
}
|
||||
|
||||
// Close the socket
|
||||
closesocket(m_iSocket);
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 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$
|
||||
@@ -28,16 +28,11 @@
|
||||
#define REMOTESERVER_H
|
||||
|
||||
#include "Thread.h"
|
||||
#include "NetAddress.h"
|
||||
#include "Connection.h"
|
||||
#include "MessageBase.h"
|
||||
|
||||
static int const REQUESTBUFFERSIZE = 8192;
|
||||
|
||||
class RemoteServer : public Thread
|
||||
{
|
||||
private:
|
||||
NetAddress* m_pNetAddress;
|
||||
Connection* m_pConnection;
|
||||
|
||||
public:
|
||||
@@ -47,23 +42,14 @@ public:
|
||||
virtual void Stop();
|
||||
};
|
||||
|
||||
class MessageCommand : public Thread
|
||||
class RequestProcessor : public Thread
|
||||
{
|
||||
private:
|
||||
SOCKET m_iSocket;
|
||||
char m_RequestBuffer[REQUESTBUFFERSIZE];
|
||||
int m_iExtraDataLength;
|
||||
|
||||
void ProcessRequest();
|
||||
void RequestDownload();
|
||||
void RequestList();
|
||||
void RequestLog();
|
||||
void RequestEditQueue();
|
||||
void SendResponse(char* szAnswer);
|
||||
|
||||
public:
|
||||
void SetSocket(SOCKET iSocket);
|
||||
virtual void Run();
|
||||
void SetSocket(SOCKET iSocket) { m_iSocket = iSocket; };
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
387
Scanner.cpp
Normal file
387
Scanner.cpp
Normal file
@@ -0,0 +1,387 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* 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 <fstream>
|
||||
#ifdef WIN32
|
||||
#include <direct.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Scanner.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "ScriptController.h"
|
||||
#include "DiskState.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
extern Options* g_pOptions;
|
||||
extern DiskState* g_pDiskState;
|
||||
|
||||
Scanner::FileData::FileData(const char* szFilename)
|
||||
{
|
||||
m_szFilename = strdup(szFilename);
|
||||
m_iSize = 0;
|
||||
m_tLastChange = 0;
|
||||
}
|
||||
|
||||
Scanner::FileData::~FileData()
|
||||
{
|
||||
free(m_szFilename);
|
||||
}
|
||||
|
||||
Scanner::Scanner()
|
||||
{
|
||||
debug("Creating Scanner");
|
||||
|
||||
m_bRequestedNZBDirScan = false;
|
||||
m_bScanning = false;
|
||||
m_iNZBDirInterval = g_pOptions->GetNzbDirInterval() * 1000;
|
||||
m_iPass = 0;
|
||||
m_iStepMSec = 0;
|
||||
|
||||
const char* szNZBScript = g_pOptions->GetNZBProcess();
|
||||
m_bNZBScript = szNZBScript && strlen(szNZBScript) > 0;
|
||||
}
|
||||
|
||||
Scanner::~Scanner()
|
||||
{
|
||||
debug("Destroying Scanner");
|
||||
|
||||
for (FileList::iterator it = m_FileList.begin(); it != m_FileList.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_FileList.clear();
|
||||
}
|
||||
|
||||
void Scanner::Check()
|
||||
{
|
||||
if (g_pOptions->GetNzbDir() && (m_bRequestedNZBDirScan ||
|
||||
(!g_pOptions->GetPauseScan() && g_pOptions->GetNzbDirInterval() > 0 &&
|
||||
m_iNZBDirInterval >= g_pOptions->GetNzbDirInterval() * 1000)))
|
||||
{
|
||||
// check nzbdir every g_pOptions->GetNzbDirInterval() seconds or if requested
|
||||
bool bCheckStat = !m_bRequestedNZBDirScan;
|
||||
m_bRequestedNZBDirScan = false;
|
||||
m_bScanning = true;
|
||||
CheckIncomingNZBs(g_pOptions->GetNzbDir(), "", bCheckStat);
|
||||
if (!bCheckStat && m_bNZBScript)
|
||||
{
|
||||
// if immediate scan requesten, we need second scan to process files extracted by NzbProcess-script
|
||||
CheckIncomingNZBs(g_pOptions->GetNzbDir(), "", bCheckStat);
|
||||
}
|
||||
m_bScanning = false;
|
||||
m_iNZBDirInterval = 0;
|
||||
|
||||
// if NzbDirFileAge is less than NzbDirInterval (that can happen if NzbDirInterval
|
||||
// is set for rare scans like once per hour) we make 4 scans:
|
||||
// - one additional scan is neccessary to check sizes of detected files;
|
||||
// - another scan is required to check files which were extracted by NzbProcess-script;
|
||||
// - third scan is needed to check sizes of extracted files.
|
||||
if (g_pOptions->GetNzbDirFileAge() < g_pOptions->GetNzbDirInterval())
|
||||
{
|
||||
int iMaxPass = m_bNZBScript ? 3 : 1;
|
||||
if (m_iPass < iMaxPass)
|
||||
{
|
||||
// scheduling another scan of incoming directory in NzbDirFileAge seconds.
|
||||
m_iNZBDirInterval = (g_pOptions->GetNzbDirInterval() - g_pOptions->GetNzbDirFileAge()) * 1000;
|
||||
m_iPass++;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_iPass = 0;
|
||||
}
|
||||
}
|
||||
|
||||
DropOldFiles();
|
||||
}
|
||||
m_iNZBDirInterval += m_iStepMSec;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if there are files in directory for incoming nzb-files
|
||||
* and add them to download queue
|
||||
*/
|
||||
void Scanner::CheckIncomingNZBs(const char* szDirectory, const char* szCategory, bool bCheckStat)
|
||||
{
|
||||
DirBrowser dir(szDirectory);
|
||||
while (const char* filename = dir.Next())
|
||||
{
|
||||
struct stat buffer;
|
||||
char fullfilename[1023 + 1]; // one char reserved for the trailing slash (if needed)
|
||||
snprintf(fullfilename, 1023, "%s%s", szDirectory, filename);
|
||||
fullfilename[1023 - 1] = '\0';
|
||||
if (!stat(fullfilename, &buffer))
|
||||
{
|
||||
// check subfolders
|
||||
if ((buffer.st_mode & S_IFDIR) != 0 && strcmp(filename, ".") && strcmp(filename, ".."))
|
||||
{
|
||||
fullfilename[strlen(fullfilename) + 1] = '\0';
|
||||
fullfilename[strlen(fullfilename)] = PATH_SEPARATOR;
|
||||
const char* szUseCategory = filename;
|
||||
char szSubCategory[1024];
|
||||
if (strlen(szCategory) > 0)
|
||||
{
|
||||
snprintf(szSubCategory, 1023, "%s%c%s", szCategory, PATH_SEPARATOR, filename);
|
||||
szSubCategory[1024 - 1] = '\0';
|
||||
szUseCategory = szSubCategory;
|
||||
}
|
||||
CheckIncomingNZBs(fullfilename, szUseCategory, bCheckStat);
|
||||
}
|
||||
else if ((buffer.st_mode & S_IFDIR) == 0 && CanProcessFile(fullfilename, bCheckStat))
|
||||
{
|
||||
ProcessIncomingFile(szDirectory, filename, fullfilename, szCategory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Only files which were not changed during last g_pOptions->GetNzbDirFileAge() seconds
|
||||
* can be processed. That prevents the processing of files, which are currently being
|
||||
* copied into nzb-directory (eg. being downloaded in web-browser).
|
||||
*/
|
||||
bool Scanner::CanProcessFile(const char* szFullFilename, bool bCheckStat)
|
||||
{
|
||||
const char* szExtension = strrchr(szFullFilename, '.');
|
||||
if (!szExtension ||
|
||||
!strcasecmp(szExtension, ".queued") ||
|
||||
!strcasecmp(szExtension, ".error") ||
|
||||
!strcasecmp(szExtension, ".processed"))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!bCheckStat)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
long long lSize = Util::FileSize(szFullFilename);
|
||||
time_t tCurrent = time(NULL);
|
||||
bool bCanProcess = false;
|
||||
bool bInList = false;
|
||||
|
||||
for (FileList::iterator it = m_FileList.begin(); it != m_FileList.end(); it++)
|
||||
{
|
||||
FileData* pFileData = *it;
|
||||
if (!strcmp(pFileData->GetFilename(), szFullFilename))
|
||||
{
|
||||
bInList = true;
|
||||
if (pFileData->GetSize() == lSize &&
|
||||
tCurrent - pFileData->GetLastChange() >= g_pOptions->GetNzbDirFileAge())
|
||||
{
|
||||
bCanProcess = true;
|
||||
delete pFileData;
|
||||
m_FileList.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
pFileData->SetSize(lSize);
|
||||
if (pFileData->GetSize() != lSize)
|
||||
{
|
||||
pFileData->SetLastChange(tCurrent);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!bInList)
|
||||
{
|
||||
FileData* pFileData = new FileData(szFullFilename);
|
||||
pFileData->SetSize(lSize);
|
||||
pFileData->SetLastChange(tCurrent);
|
||||
m_FileList.push_back(pFileData);
|
||||
}
|
||||
|
||||
return bCanProcess;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove old files from the list of monitored files.
|
||||
* Normally these files are deleted from the list when they are processed.
|
||||
* However if a file was detected by function "CanProcessFile" once but wasn't
|
||||
* processed later (for example if the user deleted it), it will stay in the list,
|
||||
* until we remove it here.
|
||||
*/
|
||||
void Scanner::DropOldFiles()
|
||||
{
|
||||
time_t tCurrent = time(NULL);
|
||||
|
||||
int i = 0;
|
||||
for (FileList::iterator it = m_FileList.begin(); it != m_FileList.end(); )
|
||||
{
|
||||
FileData* pFileData = *it;
|
||||
if ((tCurrent - pFileData->GetLastChange() >=
|
||||
(g_pOptions->GetNzbDirInterval() + g_pOptions->GetNzbDirFileAge()) * 2) ||
|
||||
// can occur if the system clock was adjusted
|
||||
tCurrent < pFileData->GetLastChange())
|
||||
{
|
||||
debug("Removing file %s from scan file list", pFileData->GetFilename());
|
||||
|
||||
delete pFileData;
|
||||
m_FileList.erase(it);
|
||||
it = m_FileList.begin() + i;
|
||||
}
|
||||
else
|
||||
{
|
||||
it++;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Scanner::ProcessIncomingFile(const char* szDirectory, const char* szBaseFilename, const char* szFullFilename, const char* szCategory)
|
||||
{
|
||||
const char* szExtension = strrchr(szBaseFilename, '.');
|
||||
if (!szExtension)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char* szNZBCategory = strdup(szCategory);
|
||||
NZBParameterList* pParameterList = new NZBParameterList;
|
||||
int iPriority = 0;
|
||||
|
||||
bool bExists = true;
|
||||
|
||||
if (m_bNZBScript && strcasecmp(szExtension, ".nzb_processed"))
|
||||
{
|
||||
NZBScriptController::ExecuteScript(g_pOptions->GetNZBProcess(), szFullFilename, szDirectory,
|
||||
&szNZBCategory, &iPriority, pParameterList);
|
||||
bExists = Util::FileExists(szFullFilename);
|
||||
if (bExists && strcasecmp(szExtension, ".nzb"))
|
||||
{
|
||||
char bakname2[1024];
|
||||
bool bRenameOK = Util::RenameBak(szFullFilename, "processed", false, bakname2, 1024);
|
||||
if (!bRenameOK)
|
||||
{
|
||||
error("Could not rename file %s to %s! Errcode: %i", szFullFilename, bakname2, errno);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!strcasecmp(szExtension, ".nzb_processed"))
|
||||
{
|
||||
char szRenamedName[1024];
|
||||
bool bRenameOK = Util::RenameBak(szFullFilename, "nzb", true, szRenamedName, 1024);
|
||||
if (bRenameOK)
|
||||
{
|
||||
AddFileToQueue(szRenamedName, szNZBCategory, iPriority, pParameterList);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not rename file %s to %s! Errcode: %i", szFullFilename, szRenamedName, errno);
|
||||
}
|
||||
}
|
||||
else if (bExists && !strcasecmp(szExtension, ".nzb"))
|
||||
{
|
||||
AddFileToQueue(szFullFilename, szNZBCategory, iPriority, pParameterList);
|
||||
}
|
||||
|
||||
for (NZBParameterList::iterator it = pParameterList->begin(); it != pParameterList->end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
pParameterList->clear();
|
||||
delete pParameterList;
|
||||
|
||||
free(szNZBCategory);
|
||||
}
|
||||
|
||||
void Scanner::AddFileToQueue(const char* szFilename, const char* szCategory, int iPriority, NZBParameterList* pParameterList)
|
||||
{
|
||||
const char* szBasename = Util::BaseFileName(szFilename);
|
||||
|
||||
info("Collection %s found", szBasename);
|
||||
|
||||
NZBFile* pNZBFile = NZBFile::CreateFromFile(szFilename, szCategory);
|
||||
if (!pNZBFile)
|
||||
{
|
||||
error("Could not add collection %s to queue", szBasename);
|
||||
}
|
||||
|
||||
char bakname2[1024];
|
||||
bool bRenameOK = Util::RenameBak(szFilename, pNZBFile ? "queued" : "error", false, bakname2, 1024);
|
||||
if (!bRenameOK)
|
||||
{
|
||||
error("Could not rename file %s to %s! Errcode: %i", szFilename, bakname2, errno);
|
||||
}
|
||||
|
||||
if (pNZBFile && bRenameOK)
|
||||
{
|
||||
pNZBFile->GetNZBInfo()->SetQueuedFilename(bakname2);
|
||||
|
||||
for (NZBParameterList::iterator it = pParameterList->begin(); it != pParameterList->end(); it++)
|
||||
{
|
||||
NZBParameter* pParameter = *it;
|
||||
pNZBFile->GetNZBInfo()->SetParameter(pParameter->GetName(), pParameter->GetValue());
|
||||
}
|
||||
|
||||
for (NZBFile::FileInfos::iterator it = pNZBFile->GetFileInfos()->begin(); it != pNZBFile->GetFileInfos()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
pFileInfo->SetPriority(iPriority);
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, false);
|
||||
info("Collection %s added to queue", szBasename);
|
||||
}
|
||||
|
||||
if (pNZBFile)
|
||||
{
|
||||
delete pNZBFile;
|
||||
}
|
||||
}
|
||||
|
||||
void Scanner::ScanNZBDir(bool bSyncMode)
|
||||
{
|
||||
// ideally we should use mutex to access "m_bRequestedNZBDirScan",
|
||||
// but it's not critical here.
|
||||
m_bScanning = true;
|
||||
m_bRequestedNZBDirScan = true;
|
||||
|
||||
while (bSyncMode && (m_bScanning || m_bRequestedNZBDirScan))
|
||||
{
|
||||
usleep(100 * 1000);
|
||||
}
|
||||
}
|
||||
77
Scanner.h
Normal file
77
Scanner.h
Normal file
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SCANNER_H
|
||||
#define SCANNER_H
|
||||
|
||||
#include <deque>
|
||||
#include <time.h>
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
class Scanner
|
||||
{
|
||||
private:
|
||||
class FileData
|
||||
{
|
||||
private:
|
||||
char* m_szFilename;
|
||||
long long m_iSize;
|
||||
time_t m_tLastChange;
|
||||
|
||||
public:
|
||||
FileData(const char* szFilename);
|
||||
~FileData();
|
||||
const char* GetFilename() { return m_szFilename; }
|
||||
long long GetSize() { return m_iSize; }
|
||||
void SetSize(long long lSize) { m_iSize = lSize; }
|
||||
time_t GetLastChange() { return m_tLastChange; }
|
||||
void SetLastChange(time_t tLastChange) { m_tLastChange = tLastChange; }
|
||||
};
|
||||
|
||||
typedef std::deque<FileData*> FileList;
|
||||
|
||||
bool m_bRequestedNZBDirScan;
|
||||
int m_iNZBDirInterval;
|
||||
bool m_bNZBScript;
|
||||
int m_iPass;
|
||||
int m_iStepMSec;
|
||||
FileList m_FileList;
|
||||
bool m_bScanning;
|
||||
|
||||
void CheckIncomingNZBs(const char* szDirectory, const char* szCategory, bool bCheckStat);
|
||||
void AddFileToQueue(const char* szFilename, const char* szCategory, int iPriority, NZBParameterList* pParameterList);
|
||||
void ProcessIncomingFile(const char* szDirectory, const char* szBaseFilename, const char* szFullFilename, const char* szCategory);
|
||||
bool CanProcessFile(const char* szFullFilename, bool bCheckStat);
|
||||
void DropOldFiles();
|
||||
|
||||
public:
|
||||
Scanner();
|
||||
~Scanner();
|
||||
void SetStepInterval(int iStepMSec) { m_iStepMSec = iStepMSec; }
|
||||
void ScanNZBDir(bool bSyncMode);
|
||||
void Check();
|
||||
};
|
||||
|
||||
#endif
|
||||
246
Scheduler.cpp
Normal file
246
Scheduler.cpp
Normal file
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2008-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* 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 "nzbget.h"
|
||||
#include "Scheduler.h"
|
||||
#include "ScriptController.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
Scheduler::Task::Task(int iHours, int iMinutes, int iWeekDaysBits, ECommand eCommand,
|
||||
int iDownloadRate, const char* szProcess)
|
||||
{
|
||||
m_iHours = iHours;
|
||||
m_iMinutes = iMinutes;
|
||||
m_iWeekDaysBits = iWeekDaysBits;
|
||||
m_eCommand = eCommand;
|
||||
m_iDownloadRate = iDownloadRate;
|
||||
m_szProcess = NULL;
|
||||
if (szProcess)
|
||||
{
|
||||
m_szProcess = strdup(szProcess);
|
||||
}
|
||||
m_tLastExecuted = 0;
|
||||
}
|
||||
|
||||
Scheduler::Task::~Task()
|
||||
{
|
||||
if (m_szProcess)
|
||||
{
|
||||
free(m_szProcess);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Scheduler::Scheduler()
|
||||
{
|
||||
debug("Creating Scheduler");
|
||||
|
||||
m_tLastCheck = 0;
|
||||
m_TaskList.clear();
|
||||
}
|
||||
|
||||
Scheduler::~Scheduler()
|
||||
{
|
||||
debug("Destroying Scheduler");
|
||||
|
||||
for (TaskList::iterator it = m_TaskList.begin(); it != m_TaskList.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
}
|
||||
|
||||
void Scheduler::AddTask(Task* pTask)
|
||||
{
|
||||
m_mutexTaskList.Lock();
|
||||
m_TaskList.push_back(pTask);
|
||||
m_mutexTaskList.Unlock();
|
||||
}
|
||||
|
||||
bool Scheduler::CompareTasks(Scheduler::Task* pTask1, Scheduler::Task* pTask2)
|
||||
{
|
||||
return (pTask1->m_iHours < pTask2->m_iHours) ||
|
||||
((pTask1->m_iHours == pTask2->m_iHours) && (pTask1->m_iMinutes < pTask2->m_iMinutes));
|
||||
}
|
||||
|
||||
void Scheduler::FirstCheck()
|
||||
{
|
||||
m_mutexTaskList.Lock();
|
||||
m_TaskList.sort(CompareTasks);
|
||||
m_mutexTaskList.Unlock();
|
||||
|
||||
// check all tasks for the last week
|
||||
time_t tCurrent = time(NULL);
|
||||
m_tLastCheck = tCurrent - 60*60*24*7;
|
||||
m_bDetectClockChanges = false;
|
||||
m_bExecuteProcess = false;
|
||||
m_bDownloadRateChanged = false;
|
||||
m_bPauseDownloadChanged = false;
|
||||
m_bPauseScanChanged = false;
|
||||
CheckTasks();
|
||||
}
|
||||
|
||||
void Scheduler::IntervalCheck()
|
||||
{
|
||||
m_bDetectClockChanges = true;
|
||||
m_bExecuteProcess = true;
|
||||
m_bDownloadRateChanged = false;
|
||||
m_bPauseDownloadChanged = false;
|
||||
m_bPauseScanChanged = false;
|
||||
CheckTasks();
|
||||
}
|
||||
|
||||
void Scheduler::CheckTasks()
|
||||
{
|
||||
m_mutexTaskList.Lock();
|
||||
|
||||
time_t tCurrent = time(NULL);
|
||||
struct tm tmCurrent;
|
||||
localtime_r(&tCurrent, &tmCurrent);
|
||||
|
||||
struct tm tmLastCheck;
|
||||
|
||||
if (m_bDetectClockChanges)
|
||||
{
|
||||
// Detect large step changes of system time
|
||||
time_t tDiff = tCurrent - m_tLastCheck;
|
||||
if (tDiff > 60*90 || tDiff < -60*90)
|
||||
{
|
||||
debug("Reset scheduled tasks (detected clock adjustment greater than 90 minutes)");
|
||||
m_bExecuteProcess = false;
|
||||
m_tLastCheck = tCurrent;
|
||||
|
||||
for (TaskList::iterator it = m_TaskList.begin(); it != m_TaskList.end(); it++)
|
||||
{
|
||||
Task* pTask = *it;
|
||||
pTask->m_tLastExecuted = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
localtime_r(&m_tLastCheck, &tmLastCheck);
|
||||
|
||||
struct tm tmLoop;
|
||||
memcpy(&tmLoop, &tmLastCheck, sizeof(tmLastCheck));
|
||||
tmLoop.tm_hour = tmCurrent.tm_hour;
|
||||
tmLoop.tm_min = tmCurrent.tm_min;
|
||||
tmLoop.tm_sec = tmCurrent.tm_sec;
|
||||
time_t tLoop = mktime(&tmLoop);
|
||||
|
||||
while (tLoop <= tCurrent)
|
||||
{
|
||||
for (TaskList::iterator it = m_TaskList.begin(); it != m_TaskList.end(); it++)
|
||||
{
|
||||
Task* pTask = *it;
|
||||
if (pTask->m_tLastExecuted != tLoop)
|
||||
{
|
||||
struct tm tmAppoint;
|
||||
memcpy(&tmAppoint, &tmLoop, sizeof(tmLoop));
|
||||
tmAppoint.tm_hour = pTask->m_iHours;
|
||||
tmAppoint.tm_min = pTask->m_iMinutes;
|
||||
tmAppoint.tm_sec = 0;
|
||||
|
||||
time_t tAppoint = mktime(&tmAppoint);
|
||||
int iWeekDay = tmAppoint.tm_wday;
|
||||
if (iWeekDay == 0)
|
||||
{
|
||||
iWeekDay = 7;
|
||||
}
|
||||
|
||||
bool bWeekDayOK = pTask->m_iWeekDaysBits == 0 || (pTask->m_iWeekDaysBits & (1 << (iWeekDay - 1)));
|
||||
bool bDoTask = bWeekDayOK && m_tLastCheck < tAppoint && tAppoint <= tCurrent;
|
||||
|
||||
//debug("TEMP: 1) m_tLastCheck=%i, tCurrent=%i, tLoop=%i, tAppoint=%i, bWeekDayOK=%i, bDoTask=%i", m_tLastCheck, tCurrent, tLoop, tAppoint, (int)bWeekDayOK, (int)bDoTask);
|
||||
|
||||
if (bDoTask)
|
||||
{
|
||||
ExecuteTask(pTask);
|
||||
pTask->m_tLastExecuted = tLoop;
|
||||
}
|
||||
}
|
||||
}
|
||||
tLoop += 60*60*24; // inc day
|
||||
localtime_r(&tLoop, &tmLoop);
|
||||
}
|
||||
|
||||
m_tLastCheck = tCurrent;
|
||||
|
||||
m_mutexTaskList.Unlock();
|
||||
}
|
||||
|
||||
void Scheduler::ExecuteTask(Task* pTask)
|
||||
{
|
||||
if (pTask->m_eCommand == scDownloadRate)
|
||||
{
|
||||
debug("Executing scheduled command: Set download rate to %i", pTask->m_iDownloadRate);
|
||||
}
|
||||
else
|
||||
{
|
||||
const char* szCommandName[] = { "Pause", "Unpause", "Set download rate", "Execute program", "Pause Scan", "Unpause Scan" };
|
||||
debug("Executing scheduled command: %s", szCommandName[pTask->m_eCommand]);
|
||||
}
|
||||
|
||||
switch (pTask->m_eCommand)
|
||||
{
|
||||
case scDownloadRate:
|
||||
m_iDownloadRate = pTask->m_iDownloadRate;
|
||||
m_bDownloadRateChanged = true;
|
||||
break;
|
||||
|
||||
case scPauseDownload:
|
||||
case scUnpauseDownload:
|
||||
m_bPauseDownload = pTask->m_eCommand == scPauseDownload;
|
||||
m_bPauseDownloadChanged = true;
|
||||
break;
|
||||
|
||||
case scProcess:
|
||||
if (m_bExecuteProcess)
|
||||
{
|
||||
SchedulerScriptController::StartScript(pTask->m_szProcess);
|
||||
}
|
||||
break;
|
||||
|
||||
case scPauseScan:
|
||||
case scUnpauseScan:
|
||||
m_bPauseScan = pTask->m_eCommand == scPauseScan;
|
||||
m_bPauseScanChanged = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
98
Scheduler.h
Normal file
98
Scheduler.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2008-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* 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 <time.h>
|
||||
|
||||
#include "Thread.h"
|
||||
|
||||
class Scheduler
|
||||
{
|
||||
public:
|
||||
enum ECommand
|
||||
{
|
||||
scPauseDownload,
|
||||
scUnpauseDownload,
|
||||
scDownloadRate,
|
||||
scProcess,
|
||||
scPauseScan,
|
||||
scUnpauseScan
|
||||
};
|
||||
|
||||
class Task
|
||||
{
|
||||
private:
|
||||
int m_iHours;
|
||||
int m_iMinutes;
|
||||
int m_iWeekDaysBits;
|
||||
ECommand m_eCommand;
|
||||
int m_iDownloadRate;
|
||||
char* m_szProcess;
|
||||
time_t m_tLastExecuted;
|
||||
|
||||
public:
|
||||
Task(int iHours, int iMinutes, int iWeekDaysBits, ECommand eCommand,
|
||||
int iDownloadRate, const char* szProcess);
|
||||
~Task();
|
||||
friend class Scheduler;
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
typedef std::list<Task*> TaskList;
|
||||
|
||||
TaskList m_TaskList;
|
||||
Mutex m_mutexTaskList;
|
||||
time_t m_tLastCheck;
|
||||
bool m_bDetectClockChanges;
|
||||
bool m_bDownloadRateChanged;
|
||||
bool m_bExecuteProcess;
|
||||
int m_iDownloadRate;
|
||||
bool m_bPauseDownloadChanged;
|
||||
bool m_bPauseDownload;
|
||||
bool m_bPauseScanChanged;
|
||||
bool m_bPauseScan;
|
||||
void ExecuteTask(Task* pTask);
|
||||
void CheckTasks();
|
||||
static bool CompareTasks(Scheduler::Task* pTask1, Scheduler::Task* pTask2);
|
||||
|
||||
public:
|
||||
Scheduler();
|
||||
~Scheduler();
|
||||
void AddTask(Task* pTask);
|
||||
void FirstCheck();
|
||||
void IntervalCheck();
|
||||
bool GetDownloadRateChanged() { return m_bDownloadRateChanged; }
|
||||
int GetDownloadRate() { return m_iDownloadRate; }
|
||||
bool GetPauseDownloadChanged() { return m_bPauseDownloadChanged; }
|
||||
bool GetPauseDownload() { return m_bPauseDownload; }
|
||||
bool GetPauseScanChanged() { return m_bPauseScanChanged; }
|
||||
bool GetPauseScan() { return m_bPauseScan; }
|
||||
};
|
||||
|
||||
#endif
|
||||
996
ScriptController.cpp
Normal file
996
ScriptController.cpp
Normal file
@@ -0,0 +1,996 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* 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>
|
||||
#include <sys/wait.h>
|
||||
#include <signal.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "ScriptController.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
extern char* (*g_szEnvironmentVariables)[];
|
||||
extern DownloadQueueHolder* g_pDownloadQueueHolder;
|
||||
|
||||
static const int POSTPROCESS_PARCHECK_CURRENT = 91;
|
||||
static const int POSTPROCESS_PARCHECK_ALL = 92;
|
||||
static const int POSTPROCESS_SUCCESS = 93;
|
||||
static const int POSTPROCESS_ERROR = 94;
|
||||
static const int POSTPROCESS_NONE = 95;
|
||||
|
||||
#ifndef WIN32
|
||||
#define CHILD_WATCHDOG 1
|
||||
#endif
|
||||
|
||||
#ifdef CHILD_WATCHDOG
|
||||
/**
|
||||
* Sometimes the forked child process doesn't start properly and hangs
|
||||
* just during the starting. I didn't find any explanation about what
|
||||
* could cause that problem except of a general advice, that
|
||||
* "a forking in a multithread application is not recommended".
|
||||
*
|
||||
* Workaround:
|
||||
* 1) child process prints a line into stdout directly after the start;
|
||||
* 2) parent process waits for a line for 60 seconds. If it didn't receive it
|
||||
* the cild process assumed to hang and will be killed. Another attempt
|
||||
* will be made.
|
||||
*/
|
||||
|
||||
class ChildWatchDog : public Thread
|
||||
{
|
||||
private:
|
||||
pid_t m_hProcessID;
|
||||
protected:
|
||||
virtual void Run();
|
||||
public:
|
||||
void SetProcessID(pid_t hProcessID) { m_hProcessID = hProcessID; }
|
||||
};
|
||||
|
||||
void ChildWatchDog::Run()
|
||||
{
|
||||
static const int WAIT_SECONDS = 60;
|
||||
time_t tStart = time(NULL);
|
||||
while (!IsStopped() && (time(NULL) - tStart) < WAIT_SECONDS)
|
||||
{
|
||||
usleep(10 * 1000);
|
||||
}
|
||||
|
||||
if (!IsStopped())
|
||||
{
|
||||
info("Restarting hanging child process");
|
||||
kill(m_hProcessID, SIGKILL);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
EnvironmentStrings::EnvironmentStrings()
|
||||
{
|
||||
}
|
||||
|
||||
EnvironmentStrings::~EnvironmentStrings()
|
||||
{
|
||||
for (Strings::iterator it = m_strings.begin(); it != m_strings.end(); it++)
|
||||
{
|
||||
free(*it);
|
||||
}
|
||||
m_strings.clear();
|
||||
}
|
||||
|
||||
void EnvironmentStrings::InitFromCurrentProcess()
|
||||
{
|
||||
for (int i = 0; (*g_szEnvironmentVariables)[i]; i++)
|
||||
{
|
||||
char* szVar = (*g_szEnvironmentVariables)[i];
|
||||
Append(strdup(szVar));
|
||||
}
|
||||
}
|
||||
|
||||
void EnvironmentStrings::Append(char* szString)
|
||||
{
|
||||
m_strings.push_back(szString);
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
/*
|
||||
* Returns environment block in format suitable for using with CreateProcess.
|
||||
* The allocated memory must be freed by caller using "free()".
|
||||
*/
|
||||
char* EnvironmentStrings::GetStrings()
|
||||
{
|
||||
int iSize = 1;
|
||||
for (Strings::iterator it = m_strings.begin(); it != m_strings.end(); it++)
|
||||
{
|
||||
char* szVar = *it;
|
||||
iSize += strlen(szVar) + 1;
|
||||
}
|
||||
|
||||
char* szStrings = (char*)malloc(iSize);
|
||||
char* szPtr = szStrings;
|
||||
for (Strings::iterator it = m_strings.begin(); it != m_strings.end(); it++)
|
||||
{
|
||||
char* szVar = *it;
|
||||
strcpy(szPtr, szVar);
|
||||
szPtr += strlen(szVar) + 1;
|
||||
}
|
||||
*szPtr = '\0';
|
||||
|
||||
return szStrings;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
/*
|
||||
* Returns environment block in format suitable for using with execve
|
||||
* The allocated memory must be freed by caller using "free()".
|
||||
*/
|
||||
char** EnvironmentStrings::GetStrings()
|
||||
{
|
||||
char** pStrings = (char**)malloc((m_strings.size() + 1) * sizeof(char*));
|
||||
char** pPtr = pStrings;
|
||||
for (Strings::iterator it = m_strings.begin(); it != m_strings.end(); it++)
|
||||
{
|
||||
char* szVar = *it;
|
||||
*pPtr = szVar;
|
||||
pPtr++;
|
||||
}
|
||||
*pPtr = NULL;
|
||||
|
||||
return pStrings;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
ScriptController::ScriptController()
|
||||
{
|
||||
m_szScript = NULL;
|
||||
m_szWorkingDir = NULL;
|
||||
m_szArgs = NULL;
|
||||
m_bFreeArgs = false;
|
||||
m_szInfoName = NULL;
|
||||
m_szDefaultKindPrefix = NULL;
|
||||
m_bTerminated = false;
|
||||
m_environmentStrings.InitFromCurrentProcess();
|
||||
}
|
||||
|
||||
ScriptController::~ScriptController()
|
||||
{
|
||||
if (m_bFreeArgs)
|
||||
{
|
||||
for (const char** szArgPtr = m_szArgs; *szArgPtr; szArgPtr++)
|
||||
{
|
||||
free((char*)*szArgPtr);
|
||||
}
|
||||
free(m_szArgs);
|
||||
}
|
||||
}
|
||||
|
||||
void ScriptController::SetEnvVar(const char* szName, const char* szValue)
|
||||
{
|
||||
int iLen = strlen(szName) + strlen(szValue) + 2;
|
||||
char* szVar = (char*)malloc(iLen);
|
||||
snprintf(szVar, iLen, "%s=%s", szName, szValue);
|
||||
m_environmentStrings.Append(szVar);
|
||||
}
|
||||
|
||||
void ScriptController::PrepareEnvironmentStrings()
|
||||
{
|
||||
Options::OptEntries* pOptEntries = g_pOptions->LockOptEntries();
|
||||
|
||||
for (Options::OptEntries::iterator it = pOptEntries->begin(); it != pOptEntries->end(); it++)
|
||||
{
|
||||
Options::OptEntry* pOptEntry = *it;
|
||||
char szVarname[1024];
|
||||
snprintf(szVarname, sizeof(szVarname), "NZBOP_%s", pOptEntry->GetName());
|
||||
|
||||
// convert to upper case; replace "." with "_".
|
||||
for (char* szPtr = szVarname; *szPtr; szPtr++)
|
||||
{
|
||||
if (*szPtr == '.')
|
||||
{
|
||||
*szPtr = '_';
|
||||
}
|
||||
*szPtr = toupper(*szPtr);
|
||||
}
|
||||
|
||||
szVarname[1024-1] = '\0';
|
||||
SetEnvVar(szVarname, pOptEntry->GetValue());
|
||||
}
|
||||
|
||||
g_pOptions->UnlockOptEntries();
|
||||
}
|
||||
|
||||
int ScriptController::Execute()
|
||||
{
|
||||
if (!Util::FileExists(m_szScript))
|
||||
{
|
||||
error("Could not start %s: could not find file %s", m_szInfoName, m_szScript);
|
||||
return -1;
|
||||
}
|
||||
|
||||
PrepareEnvironmentStrings();
|
||||
|
||||
int iExitCode = 0;
|
||||
int pipein;
|
||||
|
||||
#ifdef CHILD_WATCHDOG
|
||||
bool bChildConfirmed = false;
|
||||
while (!bChildConfirmed && !m_bTerminated)
|
||||
{
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
// build command line
|
||||
char szCmdLine[2048];
|
||||
int iUsedLen = 0;
|
||||
for (const char** szArgPtr = m_szArgs; *szArgPtr; szArgPtr++)
|
||||
{
|
||||
snprintf(szCmdLine + iUsedLen, 2048 - iUsedLen, "\"%s\" ", *szArgPtr);
|
||||
iUsedLen += strlen(*szArgPtr) + 3;
|
||||
}
|
||||
szCmdLine[iUsedLen < 2048 ? iUsedLen - 1 : 2048 - 1] = '\0';
|
||||
|
||||
// create pipes to write and read data
|
||||
HANDLE hReadPipe, hWritePipe;
|
||||
SECURITY_ATTRIBUTES SecurityAttributes;
|
||||
memset(&SecurityAttributes, 0, sizeof(SecurityAttributes));
|
||||
SecurityAttributes.nLength = sizeof(SecurityAttributes);
|
||||
SecurityAttributes.bInheritHandle = TRUE;
|
||||
|
||||
CreatePipe(&hReadPipe, &hWritePipe, &SecurityAttributes, 0);
|
||||
|
||||
STARTUPINFO StartupInfo;
|
||||
memset(&StartupInfo, 0, sizeof(StartupInfo));
|
||||
StartupInfo.cb = sizeof(StartupInfo);
|
||||
StartupInfo.dwFlags = STARTF_USESTDHANDLES;
|
||||
StartupInfo.hStdInput = 0;
|
||||
StartupInfo.hStdOutput = hWritePipe;
|
||||
StartupInfo.hStdError = hWritePipe;
|
||||
|
||||
PROCESS_INFORMATION ProcessInfo;
|
||||
memset(&ProcessInfo, 0, sizeof(ProcessInfo));
|
||||
|
||||
char* szEnvironmentStrings = m_environmentStrings.GetStrings();
|
||||
|
||||
BOOL bOK = CreateProcess(NULL, szCmdLine, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW, szEnvironmentStrings, m_szWorkingDir, &StartupInfo, &ProcessInfo);
|
||||
if (!bOK)
|
||||
{
|
||||
DWORD dwErrCode = GetLastError();
|
||||
char szErrMsg[255];
|
||||
szErrMsg[255-1] = '\0';
|
||||
if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM || FORMAT_MESSAGE_IGNORE_INSERTS || FORMAT_MESSAGE_ARGUMENT_ARRAY,
|
||||
NULL, dwErrCode, 0, szErrMsg, 255, NULL))
|
||||
{
|
||||
error("Could not start %s: %s", m_szInfoName, szErrMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not start %s: error %i", m_szInfoName, dwErrCode);
|
||||
}
|
||||
free(szEnvironmentStrings);
|
||||
return -1;
|
||||
}
|
||||
|
||||
free(szEnvironmentStrings);
|
||||
|
||||
debug("Child Process-ID: %i", (int)ProcessInfo.dwProcessId);
|
||||
|
||||
m_hProcess = ProcessInfo.hProcess;
|
||||
|
||||
// close unused "write" end
|
||||
CloseHandle(hWritePipe);
|
||||
|
||||
pipein = _open_osfhandle((intptr_t)hReadPipe, _O_RDONLY);
|
||||
|
||||
#else
|
||||
|
||||
int p[2];
|
||||
int pipeout;
|
||||
|
||||
// create the pipe
|
||||
if (pipe(p))
|
||||
{
|
||||
error("Could not open pipe: errno %i", errno);
|
||||
return -1;
|
||||
}
|
||||
|
||||
char** pEnvironmentStrings = m_environmentStrings.GetStrings();
|
||||
|
||||
pipein = p[0];
|
||||
pipeout = p[1];
|
||||
|
||||
debug("forking");
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == -1)
|
||||
{
|
||||
error("Could not start %s: errno %i", m_szInfoName, errno);
|
||||
free(pEnvironmentStrings);
|
||||
return -1;
|
||||
}
|
||||
else if (pid == 0)
|
||||
{
|
||||
// here goes the second instance
|
||||
|
||||
// create new process group (see Terminate() where it is used)
|
||||
setsid();
|
||||
|
||||
// close up the "read" end
|
||||
close(pipein);
|
||||
|
||||
// make the pipeout to be the same as stdout and stderr
|
||||
dup2(pipeout, 1);
|
||||
dup2(pipeout, 2);
|
||||
|
||||
close(pipeout);
|
||||
|
||||
#ifdef CHILD_WATCHDOG
|
||||
fwrite("\n", 1, 1, stdout);
|
||||
fflush(stdout);
|
||||
#endif
|
||||
|
||||
execve(m_szScript, (char* const*)m_szArgs, (char* const*)pEnvironmentStrings);
|
||||
fprintf(stdout, "[ERROR] Could not start script: %s", strerror(errno));
|
||||
fflush(stdout);
|
||||
_exit(-1);
|
||||
}
|
||||
|
||||
// continue the first instance
|
||||
debug("forked");
|
||||
debug("Child Process-ID: %i", (int)pid);
|
||||
|
||||
free(pEnvironmentStrings);
|
||||
|
||||
m_hProcess = pid;
|
||||
|
||||
// close unused "write" end
|
||||
close(pipeout);
|
||||
#endif
|
||||
|
||||
// open the read end
|
||||
FILE* readpipe = fdopen(pipein, "r");
|
||||
if (!readpipe)
|
||||
{
|
||||
error("Could not open pipe to %s", m_szInfoName);
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef CHILD_WATCHDOG
|
||||
debug("Creating child watchdog");
|
||||
ChildWatchDog* pWatchDog = new ChildWatchDog();
|
||||
pWatchDog->SetAutoDestroy(false);
|
||||
pWatchDog->SetProcessID(pid);
|
||||
pWatchDog->Start();
|
||||
#endif
|
||||
|
||||
char* buf = (char*)malloc(10240);
|
||||
|
||||
debug("Entering pipe-loop");
|
||||
while (!feof(readpipe) && !m_bTerminated)
|
||||
{
|
||||
if (fgets(buf, 10240, readpipe))
|
||||
{
|
||||
#ifdef CHILD_WATCHDOG
|
||||
if (!bChildConfirmed)
|
||||
{
|
||||
bChildConfirmed = true;
|
||||
pWatchDog->Stop();
|
||||
debug("Child confirmed");
|
||||
}
|
||||
#endif
|
||||
ProcessOutput(buf);
|
||||
}
|
||||
}
|
||||
debug("Exited pipe-loop");
|
||||
|
||||
#ifdef CHILD_WATCHDOG
|
||||
debug("Destroying WatchDog");
|
||||
if (!bChildConfirmed)
|
||||
{
|
||||
pWatchDog->Stop();
|
||||
}
|
||||
while (pWatchDog->IsRunning())
|
||||
{
|
||||
usleep(1 * 1000);
|
||||
}
|
||||
delete pWatchDog;
|
||||
#endif
|
||||
|
||||
free(buf);
|
||||
fclose(readpipe);
|
||||
|
||||
if (m_bTerminated)
|
||||
{
|
||||
warn("Interrupted %s", m_szInfoName);
|
||||
}
|
||||
|
||||
iExitCode = 0;
|
||||
|
||||
#ifdef WIN32
|
||||
WaitForSingleObject(m_hProcess, INFINITE);
|
||||
DWORD dExitCode = 0;
|
||||
GetExitCodeProcess(m_hProcess, &dExitCode);
|
||||
iExitCode = dExitCode;
|
||||
#else
|
||||
int iStatus = 0;
|
||||
waitpid(m_hProcess, &iStatus, 0);
|
||||
if (WIFEXITED(iStatus))
|
||||
{
|
||||
iExitCode = WEXITSTATUS(iStatus);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CHILD_WATCHDOG
|
||||
} // while (!bChildConfirmed && !m_bTerminated)
|
||||
#endif
|
||||
|
||||
if (!m_bTerminated)
|
||||
{
|
||||
info("Completed %s", m_szInfoName);
|
||||
debug("Exit code %i", iExitCode);
|
||||
}
|
||||
|
||||
return iExitCode;
|
||||
}
|
||||
|
||||
void ScriptController::Terminate()
|
||||
{
|
||||
debug("Stopping %s", m_szInfoName);
|
||||
m_bTerminated = true;
|
||||
|
||||
#ifdef WIN32
|
||||
BOOL bOK = TerminateProcess(m_hProcess, -1);
|
||||
#else
|
||||
pid_t hKillProcess = m_hProcess;
|
||||
if (getpgid(hKillProcess) == hKillProcess)
|
||||
{
|
||||
// if the child process has its own group (setsid() was successful), kill the whole group
|
||||
hKillProcess = -hKillProcess;
|
||||
}
|
||||
bool bOK = kill(hKillProcess, SIGKILL) == 0;
|
||||
#endif
|
||||
|
||||
if (bOK)
|
||||
{
|
||||
debug("Terminated %s", m_szInfoName);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not terminate %s", m_szInfoName);
|
||||
}
|
||||
|
||||
debug("Stopped %s", m_szInfoName);
|
||||
}
|
||||
|
||||
void ScriptController::ProcessOutput(char* szText)
|
||||
{
|
||||
debug("Processing output received from script");
|
||||
|
||||
for (char* pend = szText + strlen(szText) - 1; pend >= szText && (*pend == '\n' || *pend == '\r' || *pend == ' '); pend--) *pend = '\0';
|
||||
|
||||
if (strlen(szText) == 0)
|
||||
{
|
||||
// skip empty lines
|
||||
return;
|
||||
}
|
||||
|
||||
if (!strncmp(szText, "[INFO] ", 7))
|
||||
{
|
||||
AddMessage(Message::mkInfo, false, g_pOptions->GetInfoTarget(), szText + 7);
|
||||
}
|
||||
else if (!strncmp(szText, "[WARNING] ", 10))
|
||||
{
|
||||
AddMessage(Message::mkWarning, false, g_pOptions->GetWarningTarget(), szText + 10);
|
||||
}
|
||||
else if (!strncmp(szText, "[ERROR] ", 8))
|
||||
{
|
||||
AddMessage(Message::mkError, false, g_pOptions->GetErrorTarget(), szText + 8);
|
||||
}
|
||||
else if (!strncmp(szText, "[DETAIL] ", 9))
|
||||
{
|
||||
AddMessage(Message::mkDetail, false, g_pOptions->GetDetailTarget(), szText + 9);
|
||||
}
|
||||
else if (!strncmp(szText, "[DEBUG] ", 8))
|
||||
{
|
||||
AddMessage(Message::mkDebug, false, g_pOptions->GetDebugTarget(), szText + 8);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (m_eDefaultLogKind)
|
||||
{
|
||||
case Options::slNone:
|
||||
break;
|
||||
|
||||
case Options::slDetail:
|
||||
AddMessage(Message::mkDetail, true, g_pOptions->GetDetailTarget(), szText);
|
||||
break;
|
||||
|
||||
case Options::slInfo:
|
||||
AddMessage(Message::mkInfo, true, g_pOptions->GetInfoTarget(), szText);
|
||||
break;
|
||||
|
||||
case Options::slWarning:
|
||||
AddMessage(Message::mkWarning, true, g_pOptions->GetWarningTarget(), szText);
|
||||
break;
|
||||
|
||||
case Options::slError:
|
||||
AddMessage(Message::mkError, true, g_pOptions->GetErrorTarget(), szText);
|
||||
break;
|
||||
|
||||
case Options::slDebug:
|
||||
AddMessage(Message::mkDebug, true, g_pOptions->GetDebugTarget(), szText);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
debug("Processing output received from script - completed");
|
||||
}
|
||||
|
||||
void ScriptController::AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText)
|
||||
{
|
||||
switch (eKind)
|
||||
{
|
||||
case Message::mkDetail:
|
||||
detail("%s%s", (bDefaultKind && m_szDefaultKindPrefix ? m_szDefaultKindPrefix : ""), szText);
|
||||
break;
|
||||
|
||||
case Message::mkInfo:
|
||||
info("%s%s", (bDefaultKind && m_szDefaultKindPrefix ? m_szDefaultKindPrefix : ""), szText);
|
||||
break;
|
||||
|
||||
case Message::mkWarning:
|
||||
warn("%s%s", (bDefaultKind && m_szDefaultKindPrefix ? m_szDefaultKindPrefix : ""), szText);
|
||||
break;
|
||||
|
||||
case Message::mkError:
|
||||
error("%s%s", (bDefaultKind && m_szDefaultKindPrefix ? m_szDefaultKindPrefix : ""), szText);
|
||||
break;
|
||||
|
||||
case Message::mkDebug:
|
||||
debug("%s%s", (bDefaultKind && m_szDefaultKindPrefix ? m_szDefaultKindPrefix : ""), szText);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void PostScriptController::StartScriptJob(PostInfo* pPostInfo, const char* szScript, bool bNZBFileCompleted, bool bHasFailedParJobs)
|
||||
{
|
||||
info("Executing post-process-script for %s", pPostInfo->GetInfoName());
|
||||
|
||||
PostScriptController* pScriptController = new PostScriptController();
|
||||
pScriptController->m_pPostInfo = pPostInfo;
|
||||
pScriptController->SetScript(szScript);
|
||||
pScriptController->SetWorkingDir(g_pOptions->GetDestDir());
|
||||
pScriptController->m_bNZBFileCompleted = bNZBFileCompleted;
|
||||
pScriptController->m_bHasFailedParJobs = bHasFailedParJobs;
|
||||
pScriptController->SetAutoDestroy(false);
|
||||
|
||||
pPostInfo->SetScriptThread(pScriptController);
|
||||
|
||||
pScriptController->Start();
|
||||
}
|
||||
|
||||
void PostScriptController::Run()
|
||||
{
|
||||
// the locking is needed for accessing the members of NZBInfo
|
||||
g_pDownloadQueueHolder->LockQueue();
|
||||
|
||||
char szNZBName[1024];
|
||||
strncpy(szNZBName, m_pPostInfo->GetNZBInfo()->GetName(), 1024);
|
||||
szNZBName[1024-1] = '\0';
|
||||
|
||||
char szParStatus[10];
|
||||
snprintf(szParStatus, 10, "%i", m_pPostInfo->GetParStatus());
|
||||
szParStatus[10-1] = '\0';
|
||||
|
||||
char szCollectionCompleted[10];
|
||||
snprintf(szCollectionCompleted, 10, "%i", (int)m_bNZBFileCompleted);
|
||||
szCollectionCompleted[10-1] = '\0';
|
||||
|
||||
char szHasFailedParJobs[10];
|
||||
snprintf(szHasFailedParJobs, 10, "%i", (int)m_bHasFailedParJobs);
|
||||
szHasFailedParJobs[10-1] = '\0';
|
||||
|
||||
char szDestDir[1024];
|
||||
strncpy(szDestDir, m_pPostInfo->GetNZBInfo()->GetDestDir(), 1024);
|
||||
szDestDir[1024-1] = '\0';
|
||||
|
||||
char szNZBFilename[1024];
|
||||
strncpy(szNZBFilename, m_pPostInfo->GetNZBInfo()->GetFilename(), 1024);
|
||||
szNZBFilename[1024-1] = '\0';
|
||||
|
||||
char szParFilename[1024];
|
||||
strncpy(szParFilename, m_pPostInfo->GetParFilename(), 1024);
|
||||
szParFilename[1024-1] = '\0';
|
||||
|
||||
char szCategory[1024];
|
||||
strncpy(szCategory, m_pPostInfo->GetNZBInfo()->GetCategory(), 1024);
|
||||
szCategory[1024-1] = '\0';
|
||||
|
||||
char szInfoName[1024];
|
||||
snprintf(szInfoName, 1024, "post-process-script for %s", m_pPostInfo->GetInfoName());
|
||||
szInfoName[1024-1] = '\0';
|
||||
SetInfoName(szInfoName);
|
||||
|
||||
SetDefaultKindPrefix("Post-Process: ");
|
||||
SetDefaultLogKind(g_pOptions->GetProcessLogKind());
|
||||
|
||||
const char* szArgs[9];
|
||||
szArgs[0] = GetScript();
|
||||
szArgs[1] = szDestDir;
|
||||
szArgs[2] = szNZBFilename;
|
||||
szArgs[3] = szParFilename;
|
||||
szArgs[4] = szParStatus;
|
||||
szArgs[5] = szCollectionCompleted;
|
||||
szArgs[6] = szHasFailedParJobs;
|
||||
szArgs[7] = szCategory;
|
||||
szArgs[8] = NULL;
|
||||
SetArgs(szArgs, false);
|
||||
|
||||
SetEnvVar("NZBPP_NZBNAME", szNZBName);
|
||||
SetEnvVar("NZBPP_DIRECTORY", szDestDir);
|
||||
SetEnvVar("NZBPP_NZBFILENAME", szNZBFilename);
|
||||
SetEnvVar("NZBPP_PARFILENAME", szParFilename);
|
||||
SetEnvVar("NZBPP_PARSTATUS", szParStatus);
|
||||
SetEnvVar("NZBPP_NZBCOMPLETED", szCollectionCompleted);
|
||||
SetEnvVar("NZBPP_PARFAILED", szHasFailedParJobs);
|
||||
SetEnvVar("NZBPP_CATEGORY", szCategory);
|
||||
|
||||
for (NZBParameterList::iterator it = m_pPostInfo->GetNZBInfo()->GetParameters()->begin(); it != m_pPostInfo->GetNZBInfo()->GetParameters()->end(); it++)
|
||||
{
|
||||
NZBParameter* pParameter = *it;
|
||||
char szVarname[1024];
|
||||
snprintf(szVarname, sizeof(szVarname), "NZBPR_%s", pParameter->GetName());
|
||||
szVarname[1024-1] = '\0';
|
||||
SetEnvVar(szVarname, pParameter->GetValue());
|
||||
}
|
||||
|
||||
g_pDownloadQueueHolder->UnlockQueue();
|
||||
|
||||
int iResult = Execute();
|
||||
|
||||
switch (iResult)
|
||||
{
|
||||
case POSTPROCESS_SUCCESS:
|
||||
info("%s sucessful", szInfoName);
|
||||
m_pPostInfo->SetScriptStatus(PostInfo::srSuccess);
|
||||
break;
|
||||
|
||||
case POSTPROCESS_ERROR:
|
||||
info("%s failed", szInfoName);
|
||||
m_pPostInfo->SetScriptStatus(PostInfo::srFailure);
|
||||
break;
|
||||
|
||||
case POSTPROCESS_NONE:
|
||||
info("%s skipped", szInfoName);
|
||||
m_pPostInfo->SetScriptStatus(PostInfo::srNone);
|
||||
break;
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
case POSTPROCESS_PARCHECK_ALL:
|
||||
if (m_pPostInfo->GetParCheck())
|
||||
{
|
||||
error("%s requested par-check/repair for all collections, but they were already checked", szInfoName);
|
||||
m_pPostInfo->SetScriptStatus(PostInfo::srFailure);
|
||||
}
|
||||
else if (!m_bNZBFileCompleted)
|
||||
{
|
||||
error("%s requested par-check/repair for all collections, but it was not the call for the last collection", szInfoName);
|
||||
m_pPostInfo->SetScriptStatus(PostInfo::srFailure);
|
||||
}
|
||||
else
|
||||
{
|
||||
info("%s requested par-check/repair for all collections", szInfoName);
|
||||
m_pPostInfo->SetRequestParCheck(PostInfo::rpAll);
|
||||
m_pPostInfo->SetScriptStatus(PostInfo::srSuccess);
|
||||
}
|
||||
break;
|
||||
|
||||
case POSTPROCESS_PARCHECK_CURRENT:
|
||||
if (m_pPostInfo->GetParCheck())
|
||||
{
|
||||
error("%s requested par-check/repair for current collection, but it was already checked", szInfoName);
|
||||
m_pPostInfo->SetScriptStatus(PostInfo::srFailure);
|
||||
}
|
||||
else if (strlen(m_pPostInfo->GetParFilename()) == 0)
|
||||
{
|
||||
error("%s requested par-check/repair for current collection, but it doesn't have any par-files", szInfoName);
|
||||
m_pPostInfo->SetScriptStatus(PostInfo::srFailure);
|
||||
}
|
||||
else
|
||||
{
|
||||
info("%s requested par-check/repair for current collection", szInfoName);
|
||||
m_pPostInfo->SetRequestParCheck(PostInfo::rpCurrent);
|
||||
m_pPostInfo->SetScriptStatus(PostInfo::srSuccess);
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
info("%s terminated with unknown status", szInfoName);
|
||||
m_pPostInfo->SetScriptStatus(PostInfo::srUnknown);
|
||||
}
|
||||
|
||||
m_pPostInfo->SetStage(PostInfo::ptFinished);
|
||||
m_pPostInfo->SetWorking(false);
|
||||
}
|
||||
|
||||
void PostScriptController::AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText)
|
||||
{
|
||||
if (!strncmp(szText, "[HISTORY] ", 10))
|
||||
{
|
||||
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
m_pPostInfo->GetNZBInfo()->AppendMessage(eKind, 0, szText + 10);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ScriptController::AddMessage(eKind, bDefaultKind, eMessageTarget, szText);
|
||||
|
||||
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
m_pPostInfo->AppendMessage(eKind, szText);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (g_pOptions->GetPausePostProcess())
|
||||
{
|
||||
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() && !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();
|
||||
}
|
||||
|
||||
void NZBScriptController::ExecuteScript(const char* szScript, const char* szNZBFilename,
|
||||
const char* szDirectory, char** pCategory, int* iPriority, NZBParameterList* pParameterList)
|
||||
{
|
||||
info("Executing nzb-process-script for %s", Util::BaseFileName(szNZBFilename));
|
||||
|
||||
NZBScriptController* pScriptController = new NZBScriptController();
|
||||
pScriptController->SetScript(szScript);
|
||||
pScriptController->m_pCategory = pCategory;
|
||||
pScriptController->m_pParameterList = pParameterList;
|
||||
pScriptController->m_iPriority = iPriority;
|
||||
|
||||
char szInfoName[1024];
|
||||
snprintf(szInfoName, 1024, "nzb-process-script for %s", Util::BaseFileName(szNZBFilename));
|
||||
szInfoName[1024-1] = '\0';
|
||||
pScriptController->SetInfoName(szInfoName);
|
||||
|
||||
// remove trailing slash
|
||||
char szDir[1024];
|
||||
strncpy(szDir, szDirectory, 1024);
|
||||
szDir[1024-1] = '\0';
|
||||
int iLen = strlen(szDir);
|
||||
if (szDir[iLen-1] == PATH_SEPARATOR)
|
||||
{
|
||||
szDir[iLen-1] = '\0';
|
||||
}
|
||||
|
||||
pScriptController->SetDefaultKindPrefix("NZB-Process: ");
|
||||
pScriptController->SetDefaultLogKind(g_pOptions->GetProcessLogKind());
|
||||
|
||||
const char* szArgs[4];
|
||||
szArgs[0] = szScript;
|
||||
szArgs[1] = szDir;
|
||||
szArgs[2] = szNZBFilename;
|
||||
szArgs[3] = NULL;
|
||||
pScriptController->SetArgs(szArgs, false);
|
||||
|
||||
pScriptController->SetEnvVar("NZBNP_DIRECTORY", szDir);
|
||||
pScriptController->SetEnvVar("NZBNP_FILENAME", szNZBFilename);
|
||||
|
||||
pScriptController->Execute();
|
||||
|
||||
delete pScriptController;
|
||||
}
|
||||
|
||||
void NZBScriptController::AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText)
|
||||
{
|
||||
if (!strncmp(szText, "[NZB] ", 6))
|
||||
{
|
||||
debug("Command %s detected", szText + 6);
|
||||
if (!strncmp(szText + 6, "CATEGORY=", 9))
|
||||
{
|
||||
free(*m_pCategory);
|
||||
*m_pCategory = strdup(szText + 6 + 9);
|
||||
}
|
||||
else if (!strncmp(szText + 6, "NZBPR_", 6))
|
||||
{
|
||||
char* szParam = strdup(szText + 6 + 6);
|
||||
char* szValue = strchr(szParam, '=');
|
||||
if (szValue)
|
||||
{
|
||||
*szValue = '\0';
|
||||
m_pParameterList->SetParameter(szParam, szValue + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Invalid command \"%s\" received from %s", szText, GetInfoName());
|
||||
}
|
||||
free(szParam);
|
||||
}
|
||||
else if (!strncmp(szText + 6, "PRIORITY=", 9))
|
||||
{
|
||||
*m_iPriority = atoi(szText + 6 + 9);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Invalid command \"%s\" received from %s", szText, GetInfoName());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ScriptController::AddMessage(eKind, bDefaultKind, eMessageTarget, szText);
|
||||
}
|
||||
}
|
||||
|
||||
void NZBAddedScriptController::StartScript(DownloadQueue* pDownloadQueue, NZBInfo *pNZBInfo, const char* szScript)
|
||||
{
|
||||
NZBAddedScriptController* pScriptController = new NZBAddedScriptController();
|
||||
pScriptController->SetScript(szScript);
|
||||
pScriptController->m_szNZBName = strdup(pNZBInfo->GetName());
|
||||
pScriptController->SetEnvVar("NZBNA_NAME", pNZBInfo->GetName());
|
||||
pScriptController->SetEnvVar("NZBNA_FILENAME", pNZBInfo->GetFilename());
|
||||
pScriptController->SetEnvVar("NZBNA_CATEGORY", pNZBInfo->GetCategory());
|
||||
|
||||
int iLastID = 0;
|
||||
int iMaxPriority = 0;
|
||||
|
||||
for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (pFileInfo->GetNZBInfo() == pNZBInfo && ( pFileInfo->GetPriority() > iMaxPriority || iLastID == 0))
|
||||
{
|
||||
iMaxPriority = pFileInfo->GetPriority();
|
||||
}
|
||||
if (pFileInfo->GetNZBInfo() == pNZBInfo && pFileInfo->GetID() > iLastID)
|
||||
{
|
||||
iLastID = pFileInfo->GetID();
|
||||
}
|
||||
}
|
||||
|
||||
char buf[100];
|
||||
|
||||
snprintf(buf, 100, "%i", iLastID);
|
||||
pScriptController->SetEnvVar("NZBNA_LASTID", buf);
|
||||
|
||||
snprintf(buf, 100, "%i", iMaxPriority);
|
||||
pScriptController->SetEnvVar("NZBNA_PRIORITY", buf);
|
||||
|
||||
for (NZBParameterList::iterator it = pNZBInfo->GetParameters()->begin(); it != pNZBInfo->GetParameters()->end(); it++)
|
||||
{
|
||||
NZBParameter* pParameter = *it;
|
||||
char szVarname[1024];
|
||||
snprintf(szVarname, sizeof(szVarname), "NZBPR_%s", pParameter->GetName());
|
||||
szVarname[1024-1] = '\0';
|
||||
pScriptController->SetEnvVar(szVarname, pParameter->GetValue());
|
||||
}
|
||||
|
||||
pScriptController->SetAutoDestroy(true);
|
||||
|
||||
pScriptController->Start();
|
||||
}
|
||||
|
||||
void NZBAddedScriptController::Run()
|
||||
{
|
||||
char szInfoName[1024];
|
||||
snprintf(szInfoName, 1024, "nzb-added process-script for %s", m_szNZBName);
|
||||
szInfoName[1024-1] = '\0';
|
||||
SetInfoName(szInfoName);
|
||||
|
||||
info("Executing %s", szInfoName);
|
||||
|
||||
SetDefaultKindPrefix("NZB-Added Process: ");
|
||||
SetDefaultLogKind(g_pOptions->GetProcessLogKind());
|
||||
|
||||
const char* szArgs[2];
|
||||
szArgs[0] = GetScript();
|
||||
szArgs[1] = NULL;
|
||||
SetArgs(szArgs, false);
|
||||
|
||||
Execute();
|
||||
|
||||
free(m_szNZBName);
|
||||
}
|
||||
|
||||
void SchedulerScriptController::StartScript(const char* szCommandLine)
|
||||
{
|
||||
char** argv = NULL;
|
||||
if (!Util::SplitCommandLine(szCommandLine, &argv))
|
||||
{
|
||||
error("Could not execute scheduled process-script, failed to parse command line: %s", szCommandLine);
|
||||
return;
|
||||
}
|
||||
|
||||
info("Executing scheduled process-script %s", Util::BaseFileName(argv[0]));
|
||||
|
||||
SchedulerScriptController* pScriptController = new SchedulerScriptController();
|
||||
pScriptController->SetScript(argv[0]);
|
||||
pScriptController->SetArgs((const char**)argv, true);
|
||||
pScriptController->SetAutoDestroy(true);
|
||||
|
||||
pScriptController->Start();
|
||||
}
|
||||
|
||||
void SchedulerScriptController::Run()
|
||||
{
|
||||
char szInfoName[1024];
|
||||
snprintf(szInfoName, 1024, "scheduled process-script %s", Util::BaseFileName(GetScript()));
|
||||
szInfoName[1024-1] = '\0';
|
||||
SetInfoName(szInfoName);
|
||||
|
||||
SetDefaultKindPrefix("Scheduled Process: ");
|
||||
SetDefaultLogKind(g_pOptions->GetProcessLogKind());
|
||||
|
||||
Execute();
|
||||
}
|
||||
144
ScriptController.h
Normal file
144
ScriptController.h
Normal file
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007-2011 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* 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 SCRIPTCONTROLLER_H
|
||||
#define SCRIPTCONTROLLER_H
|
||||
|
||||
#include <list>
|
||||
|
||||
#include "Log.h"
|
||||
#include "Thread.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Options.h"
|
||||
|
||||
class EnvironmentStrings
|
||||
{
|
||||
private:
|
||||
typedef std::vector<char*> Strings;
|
||||
|
||||
Strings m_strings;
|
||||
|
||||
public:
|
||||
EnvironmentStrings();
|
||||
~EnvironmentStrings();
|
||||
void InitFromCurrentProcess();
|
||||
void Append(char* szString);
|
||||
#ifdef WIN32
|
||||
char* GetStrings();
|
||||
#else
|
||||
char** GetStrings();
|
||||
#endif
|
||||
};
|
||||
|
||||
class ScriptController
|
||||
{
|
||||
private:
|
||||
const char* m_szScript;
|
||||
const char* m_szWorkingDir;
|
||||
const char** m_szArgs;
|
||||
bool m_bFreeArgs;
|
||||
const char* m_szInfoName;
|
||||
const char* m_szDefaultKindPrefix;
|
||||
EnvironmentStrings m_environmentStrings;
|
||||
Options::EScriptLogKind m_eDefaultLogKind;
|
||||
bool m_bTerminated;
|
||||
#ifdef WIN32
|
||||
HANDLE m_hProcess;
|
||||
#else
|
||||
pid_t m_hProcess;
|
||||
#endif
|
||||
|
||||
void ProcessOutput(char* szText);
|
||||
void PrepareEnvironmentStrings();
|
||||
|
||||
protected:
|
||||
virtual void AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText);
|
||||
|
||||
public:
|
||||
ScriptController();
|
||||
virtual ~ScriptController();
|
||||
int Execute();
|
||||
void Terminate();
|
||||
|
||||
void SetScript(const char* szScript) { m_szScript = szScript; }
|
||||
const char* GetScript() { return m_szScript; }
|
||||
void SetWorkingDir(const char* szWorkingDir) { m_szWorkingDir = szWorkingDir; }
|
||||
void SetArgs(const char** szArgs, bool bFreeArgs) { m_szArgs = szArgs; m_bFreeArgs = bFreeArgs; }
|
||||
void SetInfoName(const char* szInfoName) { m_szInfoName = szInfoName; }
|
||||
const char* GetInfoName() { return m_szInfoName; }
|
||||
void SetDefaultKindPrefix(const char* szDefaultKindPrefix) { m_szDefaultKindPrefix = szDefaultKindPrefix; }
|
||||
void SetDefaultLogKind(Options::EScriptLogKind eDefaultLogKind) { m_eDefaultLogKind = eDefaultLogKind; }
|
||||
void SetEnvVar(const char* szName, const char* szValue);
|
||||
};
|
||||
|
||||
class PostScriptController : public Thread, ScriptController
|
||||
{
|
||||
private:
|
||||
PostInfo* m_pPostInfo;
|
||||
bool m_bNZBFileCompleted;
|
||||
bool m_bHasFailedParJobs;
|
||||
|
||||
protected:
|
||||
virtual void AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText);
|
||||
|
||||
public:
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
static void StartScriptJob(PostInfo* pPostInfo, const char* szScript,
|
||||
bool bNZBFileCompleted, bool bHasFailedParJobs);
|
||||
};
|
||||
|
||||
class NZBScriptController : public ScriptController
|
||||
{
|
||||
private:
|
||||
char** m_pCategory;
|
||||
int* m_iPriority;
|
||||
NZBParameterList* m_pParameterList;
|
||||
|
||||
protected:
|
||||
virtual void AddMessage(Message::EKind eKind, bool bDefaultKind, Options::EMessageTarget eMessageTarget, const char* szText);
|
||||
|
||||
public:
|
||||
static void ExecuteScript(const char* szScript, const char* szNZBFilename, const char* szDirectory, char** pCategory, int* iPriority, NZBParameterList* pParameterList);
|
||||
};
|
||||
|
||||
class NZBAddedScriptController : public Thread, ScriptController
|
||||
{
|
||||
private:
|
||||
char* m_szNZBName;
|
||||
|
||||
public:
|
||||
virtual void Run();
|
||||
static void StartScript(DownloadQueue* pDownloadQueue, NZBInfo *pNZBInfo, const char* szScript);
|
||||
};
|
||||
|
||||
class SchedulerScriptController : public Thread, ScriptController
|
||||
{
|
||||
public:
|
||||
virtual void Run();
|
||||
static void StartScript(const char* szCommandLine);
|
||||
};
|
||||
|
||||
#endif
|
||||
165
ServerPool.cpp
165
ServerPool.cpp
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,24 +16,16 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
*
|
||||
* m_Semaphore Patch by Florian Penzkofer <f.penzkofer@sent.com>
|
||||
* The queue of mutexes that was used did not work for every
|
||||
* implementation of POSIX threads. Now a m_Semaphore is used.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -51,34 +43,39 @@
|
||||
#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_FreeConnections.clear();
|
||||
m_Semaphores.clear();
|
||||
}
|
||||
|
||||
ServerPool::~ ServerPool()
|
||||
{
|
||||
debug("Destroying ServerPool");
|
||||
|
||||
m_FreeConnections.clear();
|
||||
|
||||
for (Semaphores::iterator it = m_Semaphores.begin(); it != m_Semaphores.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Semaphores.clear();
|
||||
m_Levels.clear();
|
||||
|
||||
for (Servers::iterator it = m_Servers.begin(); it != m_Servers.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Servers.clear();
|
||||
|
||||
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Connections.clear();
|
||||
}
|
||||
|
||||
void ServerPool::AddServer(NewsServer* pNewsServer)
|
||||
@@ -102,7 +99,9 @@ void ServerPool::InitConnections()
|
||||
}
|
||||
for (int i = 0; i < pNewsServer->GetMaxConnections(); i++)
|
||||
{
|
||||
m_FreeConnections.push_back(pNewsServer);
|
||||
PooledConnection* pConnection = new PooledConnection(pNewsServer);
|
||||
pConnection->SetTimeout(m_iTimeout);
|
||||
m_Connections.push_back(pConnection);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,68 +117,83 @@ void ServerPool::InitConnections()
|
||||
}
|
||||
}
|
||||
|
||||
Semaphore* sem = new Semaphore(iMaxConnectionsForLevel);
|
||||
m_Semaphores.push_back(sem);
|
||||
m_Levels.push_back(iMaxConnectionsForLevel);
|
||||
}
|
||||
}
|
||||
|
||||
NNTPConnection* ServerPool::GetConnection(int level)
|
||||
NNTPConnection* ServerPool::GetConnection(int iLevel)
|
||||
{
|
||||
debug("Getting connection");
|
||||
PooledConnection* pConnection = NULL;
|
||||
|
||||
debug("sem_wait...");
|
||||
// decrease m_Semaphore counter or block
|
||||
bool bWaitVal = m_Semaphores[level]->Wait();
|
||||
debug("sem_wait...OK");
|
||||
m_mutexConnections.Lock();
|
||||
|
||||
if (!bWaitVal)
|
||||
if (m_Levels[iLevel] > 0)
|
||||
{
|
||||
debug("semaphore error: %i", errno);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
m_mutexFree.Lock();
|
||||
|
||||
NNTPConnection* pConnection = NULL;
|
||||
for (Servers::iterator it = m_FreeConnections.begin(); it != m_FreeConnections.end(); it++)
|
||||
{
|
||||
NewsServer* server = *it;
|
||||
if (server->GetLevel() == level)
|
||||
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
|
||||
{
|
||||
// free connection found, take it!
|
||||
pConnection = new NNTPConnection(server);
|
||||
pConnection->SetTimeout(m_iTimeout);
|
||||
m_FreeConnections.erase(it);
|
||||
break;
|
||||
PooledConnection* pConnection1 = *it;
|
||||
if (!pConnection1->GetInUse() && pConnection1->GetNewsServer()->GetLevel() == iLevel)
|
||||
{
|
||||
// free connection found, take it!
|
||||
pConnection = pConnection1;
|
||||
pConnection->SetInUse(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_Levels[iLevel]--;
|
||||
|
||||
if (!pConnection)
|
||||
{
|
||||
error("ServerPool: internal error, no free connection found, but there should be one");
|
||||
}
|
||||
}
|
||||
|
||||
m_mutexFree.Unlock();
|
||||
|
||||
if (!pConnection)
|
||||
{
|
||||
error("ServerPool: serious error, no free connection found, but there should be one.");
|
||||
}
|
||||
m_mutexConnections.Unlock();
|
||||
|
||||
return pConnection;
|
||||
}
|
||||
|
||||
void ServerPool::FreeConnection(NNTPConnection* pConnection)
|
||||
void ServerPool::FreeConnection(NNTPConnection* pConnection, bool bUsed)
|
||||
{
|
||||
debug("Freeing connection");
|
||||
if (bUsed)
|
||||
{
|
||||
debug("Freeing used connection");
|
||||
}
|
||||
|
||||
// give back free connection
|
||||
m_mutexFree.Lock();
|
||||
m_FreeConnections.push_back(pConnection->GetNewsServer());
|
||||
m_Semaphores[pConnection->GetNewsServer()->GetLevel()]->Post();
|
||||
m_mutexFree.Unlock();
|
||||
m_mutexConnections.Lock();
|
||||
|
||||
delete pConnection;
|
||||
((PooledConnection*)pConnection)->SetInUse(false);
|
||||
if (bUsed)
|
||||
{
|
||||
((PooledConnection*)pConnection)->SetFreeTimeNow();
|
||||
}
|
||||
m_Levels[pConnection->GetNewsServer()->GetLevel()]++;
|
||||
|
||||
m_mutexConnections.Unlock();
|
||||
}
|
||||
|
||||
bool ServerPool::HasFreeConnection()
|
||||
void ServerPool::CloseUnusedConnections()
|
||||
{
|
||||
return !m_Semaphores[0]->IsLocked();
|
||||
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 = (int)(curtime - pConnection->GetFreeTime());
|
||||
if (tdiff > CONNECTION_HOLD_SECODNS)
|
||||
{
|
||||
debug("Closing unused connection to %s", pConnection->GetHost());
|
||||
pConnection->Disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_mutexConnections.Unlock();
|
||||
}
|
||||
|
||||
void ServerPool::LogDebugInfo()
|
||||
@@ -189,22 +203,13 @@ void ServerPool::LogDebugInfo()
|
||||
|
||||
debug(" Max-Level: %i", m_iMaxLevel);
|
||||
|
||||
m_mutexFree.Lock();
|
||||
m_mutexConnections.Lock();
|
||||
|
||||
debug(" Free Connections: %i", m_FreeConnections.size());
|
||||
for (Servers::iterator it = m_FreeConnections.begin(); it != m_FreeConnections.end(); it++)
|
||||
debug(" Connections: %i", m_Connections.size());
|
||||
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
|
||||
{
|
||||
debug(" Free Connection: level=%i", (*it)->GetLevel());
|
||||
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_mutexFree.Unlock();
|
||||
|
||||
m_mutexConnections.Unlock();
|
||||
}
|
||||
|
||||
46
ServerPool.h
46
ServerPool.h
@@ -1,9 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2005 Florian Penzkofer <f.penzkofer@sent.com>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -17,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -28,7 +27,8 @@
|
||||
#ifndef SERVERPOOL_H
|
||||
#define SERVERPOOL_H
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <time.h>
|
||||
|
||||
#include "Thread.h"
|
||||
#include "NewsServer.h"
|
||||
@@ -37,26 +37,40 @@
|
||||
class ServerPool
|
||||
{
|
||||
private:
|
||||
class PooledConnection : public NNTPConnection
|
||||
{
|
||||
private:
|
||||
bool m_bInUse;
|
||||
time_t m_tFreeTime;
|
||||
public:
|
||||
PooledConnection(NewsServer* server);
|
||||
bool GetInUse() { return m_bInUse; }
|
||||
void SetInUse(bool bInUse) { m_bInUse = bInUse; }
|
||||
time_t GetFreeTime() { return m_tFreeTime; }
|
||||
void SetFreeTimeNow() { m_tFreeTime = ::time(NULL); }
|
||||
};
|
||||
|
||||
typedef std::vector<NewsServer*> Servers;
|
||||
typedef std::vector<Semaphore*> Semaphores;
|
||||
typedef std::vector<int> Levels;
|
||||
typedef std::vector<PooledConnection*> Connections;
|
||||
|
||||
Servers m_Servers;
|
||||
Servers m_FreeConnections;
|
||||
Semaphores m_Semaphores;
|
||||
Connections m_Connections;
|
||||
Levels m_Levels;
|
||||
int m_iMaxLevel;
|
||||
Mutex m_mutexFree;
|
||||
Mutex m_mutexConnections;
|
||||
int m_iTimeout;
|
||||
|
||||
public:
|
||||
ServerPool();
|
||||
~ServerPool();
|
||||
ServerPool();
|
||||
~ServerPool();
|
||||
void SetTimeout(int iTimeout) { m_iTimeout = iTimeout; }
|
||||
void AddServer(NewsServer *s);
|
||||
void AddServer(NewsServer* pNewsServer);
|
||||
void InitConnections();
|
||||
int GetMaxLevel() { return m_iMaxLevel; }
|
||||
NNTPConnection* GetConnection(int level);
|
||||
bool HasFreeConnection();
|
||||
void FreeConnection(NNTPConnection* con);
|
||||
NNTPConnection* GetConnection(int iLevel);
|
||||
void FreeConnection(NNTPConnection* pConnection, bool bUsed);
|
||||
void CloseUnusedConnections();
|
||||
|
||||
void LogDebugInfo();
|
||||
};
|
||||
|
||||
212
TLS.h
Normal file
212
TLS.h
Normal file
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Based on "tls.h" from project "mpop" by Martin Lambers
|
||||
* Original source code available on http://sourceforge.net/projects/mpop/
|
||||
*
|
||||
* Copyright (C) 2000, 2003, 2004, 2005, 2006, 2007
|
||||
* Martin Lambers <marlam@marlam.de>
|
||||
*
|
||||
* Copyright (C) 2008-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* 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
|
||||
|
||||
#include <time.h>
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
# include <gnutls/gnutls.h>
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
#ifdef HAVE_OPENSSL
|
||||
# include <openssl/ssl.h>
|
||||
#endif /* HAVE_OPENSSL */
|
||||
|
||||
|
||||
/*
|
||||
* If a function with an 'errstr' argument returns a value != TLS_EOK,
|
||||
* '*errstr' either points to an allocates string containing an error
|
||||
* description or is NULL.
|
||||
* If such a function returns TLS_EOK, 'errstr' will not be changed.
|
||||
*/
|
||||
#define TLS_EOK 0 /* no error */
|
||||
#define TLS_ELIBFAILED 1 /* The underlying library failed */
|
||||
#define TLS_ESEED 2 /* Cannot seed pseudo random number generator */
|
||||
#define TLS_ECERT 3 /* Certificate check or verification failed */
|
||||
#define TLS_EIO 4 /* Input/output error */
|
||||
#define TLS_EFILE 5 /* A file does not exist/cannot be read */
|
||||
#define TLS_EHANDSHAKE 6 /* TLS handshake failed */
|
||||
|
||||
|
||||
/*
|
||||
* Always use tls_clear() before using a tls_t!
|
||||
* Never call a tls_*() function with tls_t NULL!
|
||||
*/
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int have_trust_file;
|
||||
int is_active;
|
||||
#ifdef HAVE_LIBGNUTLS
|
||||
gnutls_session_t session;
|
||||
gnutls_certificate_credentials_t cred;
|
||||
#endif /* HAVE_LIBGNUTLS */
|
||||
#ifdef HAVE_OPENSSL
|
||||
SSL_CTX *ssl_ctx;
|
||||
SSL *ssl;
|
||||
#endif /* HAVE_OPENSSL */
|
||||
} tls_t;
|
||||
|
||||
/*
|
||||
* Information about a X509 certificate.
|
||||
* The 6 owner_info and issuer_info fields are:
|
||||
* Common Name
|
||||
* Organization
|
||||
* Organizational unit
|
||||
* Locality
|
||||
* State/Province
|
||||
* Country
|
||||
* Each of these entries may be NULL if it was not provided.
|
||||
*/
|
||||
typedef struct
|
||||
{
|
||||
unsigned char sha1_fingerprint[20];
|
||||
unsigned char md5_fingerprint[16];
|
||||
time_t activation_time;
|
||||
time_t expiration_time;
|
||||
char *owner_info[6];
|
||||
char *issuer_info[6];
|
||||
} tls_cert_info_t;
|
||||
|
||||
/*
|
||||
* tls_lib_init()
|
||||
*
|
||||
* Initialize underlying TLS library. If this function returns TLS_ELIBFAILED,
|
||||
* *errstr will always point to an error string.
|
||||
* Used error codes: TLS_ELIBFAILED
|
||||
*/
|
||||
int tls_lib_init(char **errstr);
|
||||
|
||||
/*
|
||||
* tls_clear()
|
||||
*
|
||||
* Clears a tls_t type (marks it inactive).
|
||||
*/
|
||||
void tls_clear(tls_t *tls);
|
||||
|
||||
/*
|
||||
* tls_init()
|
||||
*
|
||||
* Initializes a tls_t. If both 'key_file' and 'ca_file' are not NULL, they are
|
||||
* set to be used when the peer request a certificate. If 'trust_file' is not
|
||||
* NULL, it will be used to verify the peer certificate.
|
||||
* All files must be in PEM format.
|
||||
* If 'force_sslv3' is set, then only the SSLv3 protocol will be accepted. This
|
||||
* option might be needed to talk to some obsolete broken servers. Only use this
|
||||
* if you have to.
|
||||
* Used error codes: TLS_ELIBFAILED, TLS_EFILE
|
||||
*/
|
||||
int tls_init(tls_t *tls,
|
||||
const char *key_file, const char *ca_file, const char *trust_file,
|
||||
int force_sslv3, char **errstr);
|
||||
|
||||
/*
|
||||
* tls_start()
|
||||
*
|
||||
* Starts TLS encryption on a socket.
|
||||
* 'tls' must be initialized using tls_init().
|
||||
* If 'no_certcheck' is true, then no checks will be performed on the peer
|
||||
* certificate. If it is false and no trust file was set with tls_init(),
|
||||
* only sanity checks are performed on the peer certificate. If it is false
|
||||
* and a trust file was set, real verification of the peer certificate is
|
||||
* performed.
|
||||
* 'hostname' is the host to start TLS with. It is needed for sanity checks/
|
||||
* verification.
|
||||
* 'tci' must be allocated with tls_cert_info_new(). Information about the
|
||||
* peer's certificata will be stored in it. It can later be freed with
|
||||
* tls_cert_info_free(). 'tci' is allowed to be NULL; no certificate
|
||||
* information will be passed in this case.
|
||||
* Used error codes: TLS_ELIBFAILED, TLS_ECERT, TLS_EHANDSHAKE
|
||||
*/
|
||||
int tls_start(tls_t *tls, int fd, const char *hostname, int no_certcheck,
|
||||
tls_cert_info_t *tci, char **errstr);
|
||||
|
||||
/*
|
||||
* tls_is_active()
|
||||
*
|
||||
* Returns whether 'tls' is an active TLS connection.
|
||||
*/
|
||||
int tls_is_active(tls_t *tls);
|
||||
|
||||
/*
|
||||
* tls_cert_info_new()
|
||||
* Returns a new tls_cert_info_t
|
||||
*/
|
||||
tls_cert_info_t *tls_cert_info_new(void);
|
||||
|
||||
/*
|
||||
* tls_cert_info_free()
|
||||
* Frees a tls_cert_info_t
|
||||
*/
|
||||
void tls_cert_info_free(tls_cert_info_t *tci);
|
||||
|
||||
/*
|
||||
* tls_cert_info_get()
|
||||
*
|
||||
* Extracts certificate information from the TLS connection 'tls' and stores
|
||||
* it in 'tci'. See the description of tls_cert_info_t above.
|
||||
* Used error codes: TLS_ECERT
|
||||
*/
|
||||
int tls_cert_info_get(tls_t *tls, tls_cert_info_t *tci, char **errstr);
|
||||
|
||||
/*
|
||||
* tls_getbuf()
|
||||
*
|
||||
* Reads a buffer using TLS and stores it in 's'.
|
||||
* Used error codes: TLS_EIO
|
||||
*/
|
||||
int tls_getbuf(tls_t *tls, char* s, size_t len, size_t* readlen, char **errstr);
|
||||
|
||||
/*
|
||||
* tls_putbuf()
|
||||
*
|
||||
* Writes 'len' characters from the string 's' using TLS.
|
||||
* Used error codes: TLS_EIO
|
||||
*/
|
||||
int tls_putbuf(tls_t *tls, const char *s, size_t len, char **errstr);
|
||||
|
||||
/*
|
||||
* tls_close()
|
||||
*
|
||||
* Close a TLS connection and mark it inactive
|
||||
*/
|
||||
void tls_close(tls_t *tls);
|
||||
|
||||
/*
|
||||
* tls_lib_deinit()
|
||||
*
|
||||
* Deinit underlying TLS library.
|
||||
*/
|
||||
void tls_lib_deinit(void);
|
||||
|
||||
#endif
|
||||
#endif
|
||||
179
Thread.cpp
179
Thread.cpp
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2010 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -34,139 +34,136 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <process.h>
|
||||
#else
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
#include "Log.h"
|
||||
#include "Thread.h"
|
||||
|
||||
int Thread::m_iThreadCount = 1; // take the main program thread into account
|
||||
Mutex Thread::m_mutexThread;
|
||||
Mutex* Thread::m_pMutexThread;
|
||||
|
||||
|
||||
Mutex::Mutex()
|
||||
{
|
||||
#ifdef WIN32
|
||||
InitializeCriticalSection(&m_mutexObj);
|
||||
m_pMutexObj = (CRITICAL_SECTION*)malloc(sizeof(CRITICAL_SECTION));
|
||||
InitializeCriticalSection((CRITICAL_SECTION*)m_pMutexObj);
|
||||
#else
|
||||
pthread_mutex_init(&m_mutexObj, NULL);
|
||||
m_pMutexObj = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));
|
||||
pthread_mutex_init((pthread_mutex_t*)m_pMutexObj, NULL);
|
||||
#endif
|
||||
}
|
||||
|
||||
Mutex::~ Mutex()
|
||||
{
|
||||
#ifdef WIN32
|
||||
DeleteCriticalSection(&m_mutexObj);
|
||||
DeleteCriticalSection((CRITICAL_SECTION*)m_pMutexObj);
|
||||
#else
|
||||
pthread_mutex_destroy(&m_mutexObj);
|
||||
pthread_mutex_destroy((pthread_mutex_t*)m_pMutexObj);
|
||||
#endif
|
||||
free(m_pMutexObj);
|
||||
}
|
||||
|
||||
void Mutex::Lock()
|
||||
{
|
||||
#ifdef WIN32
|
||||
EnterCriticalSection(&m_mutexObj);
|
||||
EnterCriticalSection((CRITICAL_SECTION*)m_pMutexObj);
|
||||
#ifdef DEBUG
|
||||
// CriticalSections on Windows can be locked many times from the same thread,
|
||||
// but we do not want this and must treat such situations as errors and detect them.
|
||||
if (((CRITICAL_SECTION*)m_pMutexObj)->RecursionCount > 1)
|
||||
{
|
||||
error("Internal program error: inconsistent thread-lock detected");
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
pthread_mutex_lock(&m_mutexObj);
|
||||
pthread_mutex_lock((pthread_mutex_t*)m_pMutexObj);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Mutex::Unlock()
|
||||
{
|
||||
#ifdef WIN32
|
||||
LeaveCriticalSection(&m_mutexObj);
|
||||
LeaveCriticalSection((CRITICAL_SECTION*)m_pMutexObj);
|
||||
#else
|
||||
pthread_mutex_unlock(&m_mutexObj);
|
||||
pthread_mutex_unlock((pthread_mutex_t*)m_pMutexObj);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
Semaphore::Semaphore()
|
||||
#ifdef HAVE_SPINLOCK
|
||||
SpinLock::SpinLock()
|
||||
{
|
||||
#ifdef WIN32
|
||||
m_semObj = CreateSemaphore(NULL, 0, 1, NULL);
|
||||
m_pSpinLockObj = (CRITICAL_SECTION *)malloc(sizeof(CRITICAL_SECTION));
|
||||
InitializeCriticalSectionAndSpinCount((CRITICAL_SECTION *)m_pSpinLockObj, 0x00FFFFFF);
|
||||
#else
|
||||
sem_init(&m_semObj, 0, 0);
|
||||
m_pSpinLockObj = (pthread_spinlock_t *)malloc(sizeof(pthread_spinlock_t));
|
||||
pthread_spin_init((pthread_spinlock_t *)m_pSpinLockObj, PTHREAD_PROCESS_PRIVATE);
|
||||
#endif
|
||||
}
|
||||
|
||||
Semaphore::Semaphore(int iValue)
|
||||
SpinLock::~SpinLock()
|
||||
{
|
||||
#ifdef WIN32
|
||||
m_semObj = CreateSemaphore(NULL, iValue, iValue, NULL);
|
||||
DeleteCriticalSection((CRITICAL_SECTION *)m_pSpinLockObj);
|
||||
#else
|
||||
sem_init(&m_semObj, 0, iValue);
|
||||
pthread_spin_destroy((pthread_spinlock_t *)m_pSpinLockObj);
|
||||
#endif
|
||||
free((void*)m_pSpinLockObj);
|
||||
}
|
||||
|
||||
void SpinLock::Lock()
|
||||
{
|
||||
#ifdef WIN32
|
||||
EnterCriticalSection((CRITICAL_SECTION *)m_pSpinLockObj);
|
||||
#else
|
||||
pthread_spin_lock((pthread_spinlock_t *)m_pSpinLockObj);
|
||||
#endif
|
||||
}
|
||||
|
||||
Semaphore::~ Semaphore()
|
||||
void SpinLock::Unlock()
|
||||
{
|
||||
#ifdef WIN32
|
||||
CloseHandle(m_semObj);
|
||||
LeaveCriticalSection((CRITICAL_SECTION *)m_pSpinLockObj);
|
||||
#else
|
||||
sem_destroy(&m_semObj);
|
||||
pthread_spin_unlock((pthread_spinlock_t *)m_pSpinLockObj);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Semaphore::Post()
|
||||
{
|
||||
#ifdef WIN32
|
||||
ReleaseSemaphore(m_semObj, 1, NULL);
|
||||
#else
|
||||
sem_post(&m_semObj);
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Semaphore::Wait()
|
||||
{
|
||||
#ifdef WIN32
|
||||
return WaitForSingleObject(m_semObj, INFINITE) == WAIT_OBJECT_0;
|
||||
#else
|
||||
return sem_wait(&m_semObj) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Semaphore::TimedWait(int iMSec)
|
||||
{
|
||||
#ifdef WIN32
|
||||
return WaitForSingleObject(m_semObj, iMSec) == WAIT_OBJECT_0;
|
||||
#else
|
||||
struct timespec alarm;
|
||||
alarm.tv_sec = ::time(NULL) + iMSec / 1000;
|
||||
alarm.tv_nsec = (iMSec % 1000) * 1000;
|
||||
return sem_timedwait(&m_semObj, &alarm) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Semaphore::IsLocked()
|
||||
{
|
||||
#ifdef WIN32
|
||||
bool bCanLock = WaitForSingleObject(m_semObj, 0) == WAIT_OBJECT_0;
|
||||
if (bCanLock)
|
||||
{
|
||||
ReleaseSemaphore(m_semObj, 1, NULL);
|
||||
}
|
||||
return !bCanLock;
|
||||
#else
|
||||
int iSemValue;
|
||||
sem_getvalue(&m_semObj, &iSemValue);
|
||||
return iSemValue <= 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void Thread::Init()
|
||||
{
|
||||
debug("Initializing global thread data");
|
||||
|
||||
m_pMutexThread = new Mutex();
|
||||
}
|
||||
|
||||
void Thread::Final()
|
||||
{
|
||||
debug("Finalizing global thread data");
|
||||
|
||||
delete m_pMutexThread;
|
||||
}
|
||||
|
||||
Thread::Thread()
|
||||
{
|
||||
debug("Creating Thread");
|
||||
|
||||
m_Thread = 0;
|
||||
#ifdef WIN32
|
||||
m_pThreadObj = NULL;
|
||||
#else
|
||||
m_pThreadObj = (pthread_t*)malloc(sizeof(pthread_t));
|
||||
*((pthread_t*)m_pThreadObj) = 0;
|
||||
#endif
|
||||
m_bRunning = false;
|
||||
m_bStopped = false;
|
||||
m_bAutoDestroy = false;
|
||||
@@ -175,6 +172,9 @@ Thread::Thread()
|
||||
Thread::~Thread()
|
||||
{
|
||||
debug("Destroying Thread");
|
||||
#ifndef WIN32
|
||||
free(m_pThreadObj);
|
||||
#endif
|
||||
}
|
||||
|
||||
void Thread::Start()
|
||||
@@ -183,31 +183,29 @@ void Thread::Start()
|
||||
|
||||
m_bRunning = true;
|
||||
|
||||
// NOTE: we must garantee, that in a time we setting m_bRunning
|
||||
// NOTE: we must guarantee, that in a time we set m_bRunning
|
||||
// to value returned from pthread_create, the thread-object still exists.
|
||||
// This is not obviously!
|
||||
// pthread_create could wait long enough before returning result
|
||||
// back to allow the started thread to complete it job
|
||||
// and terminate.
|
||||
// We lock mutex m_mutexThread on calling pthread_create; the started thread
|
||||
// back to allow the started thread to complete its job and terminate.
|
||||
// We lock mutex m_pMutexThread on calling pthread_create; the started thread
|
||||
// then also try to lock the mutex (see thread_handler) and therefore
|
||||
// must wait until we unlock it
|
||||
m_mutexThread.Lock();
|
||||
m_pMutexThread->Lock();
|
||||
|
||||
#ifdef WIN32
|
||||
DWORD ThreadId;
|
||||
m_Thread = CreateThread(NULL, 0, Thread::thread_handler, (void *) this, 0, &ThreadId);
|
||||
m_bRunning = m_Thread != NULL;
|
||||
m_pThreadObj = (HANDLE)_beginthread(Thread::thread_handler, 0, (void *)this);
|
||||
m_bRunning = m_pThreadObj != NULL;
|
||||
#else
|
||||
pthread_attr_t m_Attr;
|
||||
pthread_attr_init(&m_Attr);
|
||||
pthread_attr_setdetachstate(&m_Attr, PTHREAD_CREATE_DETACHED);
|
||||
pthread_attr_setinheritsched(&m_Attr , PTHREAD_INHERIT_SCHED);
|
||||
m_bRunning = !pthread_create(&m_Thread, &m_Attr, Thread::thread_handler, (void *) this);
|
||||
m_bRunning = !pthread_create((pthread_t*)m_pThreadObj, &m_Attr, Thread::thread_handler, (void *) this);
|
||||
pthread_attr_destroy(&m_Attr);
|
||||
#endif
|
||||
|
||||
m_mutexThread.Unlock();
|
||||
m_pMutexThread->Unlock();
|
||||
}
|
||||
|
||||
void Thread::Stop()
|
||||
@@ -221,31 +219,31 @@ bool Thread::Kill()
|
||||
{
|
||||
debug("Killing Thread");
|
||||
|
||||
m_mutexThread.Lock();
|
||||
m_pMutexThread->Lock();
|
||||
|
||||
#ifdef WIN32
|
||||
bool terminated = TerminateThread(m_Thread, 0) != 0;
|
||||
bool terminated = TerminateThread((HANDLE)m_pThreadObj, 0) != 0;
|
||||
#else
|
||||
bool terminated = pthread_cancel(m_Thread) == 0;
|
||||
bool terminated = pthread_cancel(*(pthread_t*)m_pThreadObj) == 0;
|
||||
#endif
|
||||
|
||||
if (terminated)
|
||||
{
|
||||
m_iThreadCount--;
|
||||
}
|
||||
m_mutexThread.Unlock();
|
||||
m_pMutexThread->Unlock();
|
||||
return terminated;
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
DWORD WINAPI Thread::thread_handler(void* pObject)
|
||||
void __cdecl Thread::thread_handler(void* pObject)
|
||||
#else
|
||||
void* Thread::thread_handler(void* pObject)
|
||||
#endif
|
||||
{
|
||||
m_mutexThread.Lock();
|
||||
m_pMutexThread->Lock();
|
||||
m_iThreadCount++;
|
||||
m_mutexThread.Unlock();
|
||||
m_pMutexThread->Unlock();
|
||||
|
||||
debug("Entering Thread-func");
|
||||
|
||||
@@ -263,18 +261,19 @@ void* Thread::thread_handler(void* pObject)
|
||||
delete pThread;
|
||||
}
|
||||
|
||||
m_mutexThread.Lock();
|
||||
m_pMutexThread->Lock();
|
||||
m_iThreadCount--;
|
||||
m_mutexThread.Unlock();
|
||||
m_pMutexThread->Unlock();
|
||||
|
||||
#ifndef WIN32
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
int Thread::GetThreadCount()
|
||||
{
|
||||
m_mutexThread.Lock();
|
||||
m_pMutexThread->Lock();
|
||||
int iThreadCount = m_iThreadCount;
|
||||
m_mutexThread.Unlock();
|
||||
m_pMutexThread->Unlock();
|
||||
return iThreadCount;
|
||||
}
|
||||
|
||||
|
||||
57
Thread.h
57
Thread.h
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2010 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -27,19 +27,10 @@
|
||||
#ifndef THREAD_H
|
||||
#define THREAD_H
|
||||
|
||||
#ifndef WIN32
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#endif
|
||||
|
||||
class Mutex
|
||||
{
|
||||
private:
|
||||
#ifdef WIN32
|
||||
CRITICAL_SECTION m_mutexObj;
|
||||
#else
|
||||
pthread_mutex_t m_mutexObj;
|
||||
#endif
|
||||
void* m_pMutexObj;
|
||||
|
||||
public:
|
||||
Mutex();
|
||||
@@ -48,50 +39,45 @@ public:
|
||||
void Unlock();
|
||||
};
|
||||
|
||||
|
||||
class Semaphore
|
||||
#ifdef HAVE_SPINLOCK
|
||||
class SpinLock
|
||||
{
|
||||
private:
|
||||
#ifdef WIN32
|
||||
HANDLE m_semObj;
|
||||
void* m_pSpinLockObj;
|
||||
#else
|
||||
sem_t m_semObj;
|
||||
volatile void* m_pSpinLockObj;
|
||||
#endif
|
||||
|
||||
public:
|
||||
Semaphore();
|
||||
Semaphore(int iValue);
|
||||
~Semaphore();
|
||||
void Post();
|
||||
bool Wait();
|
||||
bool TimedWait(int iMSec);
|
||||
bool IsLocked();
|
||||
SpinLock();
|
||||
~SpinLock();
|
||||
void Lock();
|
||||
void Unlock();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
class Thread
|
||||
{
|
||||
private:
|
||||
static Mutex m_mutexThread;
|
||||
static Mutex* m_pMutexThread;
|
||||
static int m_iThreadCount;
|
||||
#ifdef WIN32
|
||||
HANDLE m_Thread;
|
||||
#else
|
||||
pthread_t m_Thread;
|
||||
#endif
|
||||
void* m_pThreadObj;
|
||||
bool m_bRunning;
|
||||
bool m_bStopped;
|
||||
bool m_bAutoDestroy;
|
||||
|
||||
#ifdef WIN32
|
||||
static DWORD WINAPI thread_handler(void* pObject);
|
||||
static void __cdecl thread_handler(void* pObject);
|
||||
#else
|
||||
static void *thread_handler(void* pObject);
|
||||
#endif
|
||||
|
||||
public:
|
||||
Thread();
|
||||
Thread();
|
||||
virtual ~Thread();
|
||||
static void Init();
|
||||
static void Final();
|
||||
|
||||
virtual void Start();
|
||||
virtual void Stop();
|
||||
@@ -104,9 +90,6 @@ public:
|
||||
void SetAutoDestroy(bool bAutoDestroy) { m_bAutoDestroy = bAutoDestroy; }
|
||||
static int GetThreadCount();
|
||||
|
||||
static void Init();
|
||||
static void Final();
|
||||
|
||||
protected:
|
||||
virtual void Run() {}; // Virtual function - override in derivatives
|
||||
};
|
||||
|
||||
455
UrlCoordinator.cpp
Normal file
455
UrlCoordinator.cpp
Normal file
@@ -0,0 +1,455 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cstdio>
|
||||
#include <sys/stat.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "UrlCoordinator.h"
|
||||
#include "Options.h"
|
||||
#include "WebDownloader.h"
|
||||
#include "DiskState.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include "NZBFile.h"
|
||||
#include "QueueCoordinator.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
extern DiskState* g_pDiskState;
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
|
||||
UrlDownloader::UrlDownloader() : WebDownloader()
|
||||
{
|
||||
m_szCategory = NULL;
|
||||
}
|
||||
|
||||
UrlDownloader::~UrlDownloader()
|
||||
{
|
||||
if (m_szCategory)
|
||||
{
|
||||
free(m_szCategory);
|
||||
}
|
||||
}
|
||||
|
||||
void UrlDownloader::ProcessHeader(const char* szLine)
|
||||
{
|
||||
WebDownloader::ProcessHeader(szLine);
|
||||
|
||||
if (!strncmp(szLine, "X-DNZB-Category: ", 17))
|
||||
{
|
||||
if (m_szCategory)
|
||||
{
|
||||
free(m_szCategory);
|
||||
}
|
||||
|
||||
const char *szCat = szLine + 17;
|
||||
|
||||
int iCatLen = strlen(szCat);
|
||||
|
||||
// trim trailing CR/LF/spaces
|
||||
while (iCatLen > 0 && (szCat[iCatLen-1] == '\n' || szCat[iCatLen-1] == '\r' || szCat[iCatLen-1] == ' ')) iCatLen--;
|
||||
|
||||
m_szCategory = (char*)malloc(iCatLen + 1);
|
||||
strncpy(m_szCategory, szCat, iCatLen);
|
||||
m_szCategory[iCatLen] = '\0';
|
||||
|
||||
debug("Category: %s", m_szCategory);
|
||||
}
|
||||
}
|
||||
|
||||
UrlCoordinator::UrlCoordinator()
|
||||
{
|
||||
debug("Creating UrlCoordinator");
|
||||
|
||||
m_bHasMoreJobs = true;
|
||||
}
|
||||
|
||||
UrlCoordinator::~UrlCoordinator()
|
||||
{
|
||||
debug("Destroying UrlCoordinator");
|
||||
// Cleanup
|
||||
|
||||
debug("Deleting UrlDownloaders");
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_ActiveDownloads.clear();
|
||||
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
for (UrlQueue::iterator it = pDownloadQueue->GetUrlQueue()->begin(); it != pDownloadQueue->GetUrlQueue()->end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
pDownloadQueue->GetUrlQueue()->clear();
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
|
||||
debug("UrlCoordinator destroyed");
|
||||
}
|
||||
|
||||
void UrlCoordinator::Run()
|
||||
{
|
||||
debug("Entering UrlCoordinator-loop");
|
||||
|
||||
int iResetCounter = 0;
|
||||
|
||||
while (!IsStopped())
|
||||
{
|
||||
if (!(g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2()))
|
||||
{
|
||||
// start download for next URL
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
|
||||
if ((int)m_ActiveDownloads.size() < g_pOptions->GetUrlConnections())
|
||||
{
|
||||
UrlInfo* pUrlInfo;
|
||||
bool bHasMoreUrls = GetNextUrl(pDownloadQueue, pUrlInfo);
|
||||
bool bUrlDownloadsRunning = !m_ActiveDownloads.empty();
|
||||
m_bHasMoreJobs = bHasMoreUrls || bUrlDownloadsRunning;
|
||||
if (bHasMoreUrls && !IsStopped() && Thread::GetThreadCount() < g_pOptions->GetThreadLimit())
|
||||
{
|
||||
StartUrlDownload(pUrlInfo);
|
||||
}
|
||||
}
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
}
|
||||
|
||||
int iSleepInterval = 100;
|
||||
usleep(iSleepInterval * 1000);
|
||||
|
||||
iResetCounter += iSleepInterval;
|
||||
if (iResetCounter >= 1000)
|
||||
{
|
||||
// this code should not be called too often, once per second is OK
|
||||
ResetHangingDownloads();
|
||||
iResetCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// waiting for downloads
|
||||
debug("UrlCoordinator: waiting for Downloads to complete");
|
||||
bool completed = false;
|
||||
while (!completed)
|
||||
{
|
||||
g_pQueueCoordinator->LockQueue();
|
||||
completed = m_ActiveDownloads.size() == 0;
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
usleep(100 * 1000);
|
||||
ResetHangingDownloads();
|
||||
}
|
||||
debug("UrlCoordinator: Downloads are completed");
|
||||
|
||||
debug("Exiting UrlCoordinator-loop");
|
||||
}
|
||||
|
||||
void UrlCoordinator::Stop()
|
||||
{
|
||||
Thread::Stop();
|
||||
|
||||
debug("Stopping UrlDownloads");
|
||||
g_pQueueCoordinator->LockQueue();
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
(*it)->Stop();
|
||||
}
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
debug("UrlDownloads are notified");
|
||||
}
|
||||
|
||||
void UrlCoordinator::ResetHangingDownloads()
|
||||
{
|
||||
const int TimeOut = g_pOptions->GetTerminateTimeout();
|
||||
if (TimeOut == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->LockQueue();
|
||||
time_t tm = ::time(NULL);
|
||||
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end();)
|
||||
{
|
||||
UrlDownloader* pUrlDownloader = *it;
|
||||
if (tm - pUrlDownloader->GetLastUpdateTime() > TimeOut &&
|
||||
pUrlDownloader->GetStatus() == UrlDownloader::adRunning)
|
||||
{
|
||||
UrlInfo* pUrlInfo = pUrlDownloader->GetUrlInfo();
|
||||
debug("Terminating hanging download %s", pUrlDownloader->GetInfoName());
|
||||
if (pUrlDownloader->Terminate())
|
||||
{
|
||||
error("Terminated hanging download %s", pUrlDownloader->GetInfoName());
|
||||
pUrlInfo->SetStatus(UrlInfo::aiUndefined);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not terminate hanging download %s", pUrlDownloader->GetInfoName());
|
||||
}
|
||||
m_ActiveDownloads.erase(it);
|
||||
// it's not safe to destroy pUrlDownloader, because the state of object is unknown
|
||||
delete pUrlDownloader;
|
||||
it = m_ActiveDownloads.begin();
|
||||
continue;
|
||||
}
|
||||
it++;
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
}
|
||||
|
||||
void UrlCoordinator::LogDebugInfo()
|
||||
{
|
||||
debug(" UrlCoordinator");
|
||||
debug(" ----------------");
|
||||
|
||||
g_pQueueCoordinator->LockQueue();
|
||||
debug(" Active Downloads: %i", m_ActiveDownloads.size());
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
UrlDownloader* pUrlDownloader = *it;
|
||||
pUrlDownloader->LogDebugInfo();
|
||||
}
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
}
|
||||
|
||||
void UrlCoordinator::AddUrlToQueue(UrlInfo* pUrlInfo, bool AddFirst)
|
||||
{
|
||||
debug("Adding NZB-URL to queue");
|
||||
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
|
||||
pDownloadQueue->GetUrlQueue()->push_back(pUrlInfo);
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns next URL for download.
|
||||
*/
|
||||
bool UrlCoordinator::GetNextUrl(DownloadQueue* pDownloadQueue, UrlInfo* &pUrlInfo)
|
||||
{
|
||||
bool bOK = false;
|
||||
|
||||
for (UrlQueue::iterator at = pDownloadQueue->GetUrlQueue()->begin(); at != pDownloadQueue->GetUrlQueue()->end(); at++)
|
||||
{
|
||||
pUrlInfo = *at;
|
||||
if (pUrlInfo->GetStatus() == 0)
|
||||
{
|
||||
bOK = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return bOK;
|
||||
}
|
||||
|
||||
void UrlCoordinator::StartUrlDownload(UrlInfo* pUrlInfo)
|
||||
{
|
||||
debug("Starting new UrlDownloader");
|
||||
|
||||
UrlDownloader* pUrlDownloader = new UrlDownloader();
|
||||
pUrlDownloader->SetAutoDestroy(true);
|
||||
pUrlDownloader->Attach(this);
|
||||
pUrlDownloader->SetUrlInfo(pUrlInfo);
|
||||
pUrlDownloader->SetURL(pUrlInfo->GetURL());
|
||||
|
||||
char tmp[1024];
|
||||
|
||||
pUrlInfo->GetName(tmp, 1024);
|
||||
pUrlDownloader->SetInfoName(tmp);
|
||||
|
||||
snprintf(tmp, 1024, "%surl-%i.tmp", g_pOptions->GetTempDir(), pUrlInfo->GetID());
|
||||
tmp[1024-1] = '\0';
|
||||
pUrlDownloader->SetOutputFilename(tmp);
|
||||
|
||||
pUrlInfo->SetStatus(UrlInfo::aiRunning);
|
||||
|
||||
m_ActiveDownloads.push_back(pUrlDownloader);
|
||||
pUrlDownloader->Start();
|
||||
}
|
||||
|
||||
void UrlCoordinator::Update(Subject* Caller, void* Aspect)
|
||||
{
|
||||
debug("Notification from UrlDownloader received");
|
||||
|
||||
UrlDownloader* pUrlDownloader = (UrlDownloader*) Caller;
|
||||
if ((pUrlDownloader->GetStatus() == WebDownloader::adFinished) ||
|
||||
(pUrlDownloader->GetStatus() == WebDownloader::adFailed) ||
|
||||
(pUrlDownloader->GetStatus() == WebDownloader::adRetry))
|
||||
{
|
||||
UrlCompleted(pUrlDownloader);
|
||||
}
|
||||
}
|
||||
|
||||
void UrlCoordinator::UrlCompleted(UrlDownloader* pUrlDownloader)
|
||||
{
|
||||
debug("URL downloaded");
|
||||
|
||||
UrlInfo* pUrlInfo = pUrlDownloader->GetUrlInfo();
|
||||
|
||||
if (pUrlDownloader->GetStatus() == WebDownloader::adFinished)
|
||||
{
|
||||
pUrlInfo->SetStatus(UrlInfo::aiFinished);
|
||||
}
|
||||
else if (pUrlDownloader->GetStatus() == WebDownloader::adFailed)
|
||||
{
|
||||
pUrlInfo->SetStatus(UrlInfo::aiFailed);
|
||||
}
|
||||
else if (pUrlDownloader->GetStatus() == WebDownloader::adRetry)
|
||||
{
|
||||
pUrlInfo->SetStatus(UrlInfo::aiUndefined);
|
||||
}
|
||||
|
||||
char filename[1024];
|
||||
if (pUrlDownloader->GetOriginalFilename())
|
||||
{
|
||||
strncpy(filename, pUrlDownloader->GetOriginalFilename(), 1024);
|
||||
filename[1024-1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(filename, Util::BaseFileName(pUrlInfo->GetURL()), 1024);
|
||||
filename[1024-1] = '\0';
|
||||
|
||||
// TODO: decode URL escaping
|
||||
}
|
||||
|
||||
Util::MakeValidFilename(filename, '_', false);
|
||||
|
||||
debug("Filename: [%s]", filename);
|
||||
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
|
||||
// delete Download from Queue
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
UrlDownloader* pa = *it;
|
||||
if (pa == pUrlDownloader)
|
||||
{
|
||||
m_ActiveDownloads.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool bDeleteObj = false;
|
||||
|
||||
if (pUrlInfo->GetStatus() == UrlInfo::aiFinished || pUrlInfo->GetStatus() == UrlInfo::aiFailed)
|
||||
{
|
||||
// delete UrlInfo from Queue
|
||||
for (UrlQueue::iterator it = pDownloadQueue->GetUrlQueue()->begin(); it != pDownloadQueue->GetUrlQueue()->end(); it++)
|
||||
{
|
||||
UrlInfo* pa = *it;
|
||||
if (pa == pUrlInfo)
|
||||
{
|
||||
pDownloadQueue->GetUrlQueue()->erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bDeleteObj = true;
|
||||
|
||||
if (g_pOptions->GetKeepHistory() > 0 && pUrlInfo->GetStatus() == UrlInfo::aiFailed)
|
||||
{
|
||||
HistoryInfo* pHistoryInfo = new HistoryInfo(pUrlInfo);
|
||||
pHistoryInfo->SetTime(time(NULL));
|
||||
pDownloadQueue->GetHistoryList()->push_front(pHistoryInfo);
|
||||
bDeleteObj = false;
|
||||
}
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
|
||||
}
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
|
||||
if (pUrlInfo->GetStatus() == UrlInfo::aiFinished)
|
||||
{
|
||||
// add nzb-file to download queue
|
||||
AddToNZBQueue(pUrlInfo, pUrlDownloader->GetOutputFilename(), filename, pUrlDownloader->GetCategory());
|
||||
}
|
||||
|
||||
if (bDeleteObj)
|
||||
{
|
||||
delete pUrlInfo;
|
||||
}
|
||||
}
|
||||
|
||||
void UrlCoordinator::AddToNZBQueue(UrlInfo* pUrlInfo, const char* szTempFilename, const char* szOriginalFilename, const char* szOriginalCategory)
|
||||
{
|
||||
info("Queue downloaded collection %s", szOriginalFilename);
|
||||
|
||||
NZBFile* pNZBFile = NZBFile::CreateFromFile(szTempFilename, pUrlInfo->GetCategory());
|
||||
if (pNZBFile)
|
||||
{
|
||||
pNZBFile->GetNZBInfo()->SetName(NULL);
|
||||
pNZBFile->GetNZBInfo()->SetFilename(pUrlInfo->GetNZBFilename() && strlen(pUrlInfo->GetNZBFilename()) > 0 ? pUrlInfo->GetNZBFilename() : szOriginalFilename);
|
||||
|
||||
if (strlen(pUrlInfo->GetCategory()) > 0)
|
||||
{
|
||||
pNZBFile->GetNZBInfo()->SetCategory(pUrlInfo->GetCategory());
|
||||
}
|
||||
else if (szOriginalCategory)
|
||||
{
|
||||
pNZBFile->GetNZBInfo()->SetCategory(szOriginalCategory);
|
||||
}
|
||||
|
||||
pNZBFile->GetNZBInfo()->BuildDestDirName();
|
||||
|
||||
for (NZBFile::FileInfos::iterator it = pNZBFile->GetFileInfos()->begin(); it != pNZBFile->GetFileInfos()->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
pFileInfo->SetPriority(pUrlInfo->GetPriority());
|
||||
pFileInfo->SetPaused(pUrlInfo->GetAddPaused());
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, pUrlInfo->GetAddTop());
|
||||
delete pNZBFile;
|
||||
info("Collection %s added to queue", szOriginalFilename);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not add downloaded collection %s to queue", szOriginalFilename);
|
||||
}
|
||||
}
|
||||
86
UrlCoordinator.h
Normal file
86
UrlCoordinator.h
Normal file
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef URLCOORDINATOR_H
|
||||
#define URLCOORDINATOR_H
|
||||
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <time.h>
|
||||
|
||||
#include "Thread.h"
|
||||
#include "WebDownloader.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Observer.h"
|
||||
|
||||
class UrlDownloader;
|
||||
|
||||
class UrlCoordinator : public Thread, public Observer, public Subject
|
||||
{
|
||||
public:
|
||||
typedef std::list<UrlDownloader*> ActiveDownloads;
|
||||
|
||||
private:
|
||||
ActiveDownloads m_ActiveDownloads;
|
||||
bool m_bHasMoreJobs;
|
||||
|
||||
bool GetNextUrl(DownloadQueue* pDownloadQueue, UrlInfo* &pUrlInfo);
|
||||
void StartUrlDownload(UrlInfo* pUrlInfo);
|
||||
void UrlCompleted(UrlDownloader* pUrlDownloader);
|
||||
void ResetHangingDownloads();
|
||||
void AddToNZBQueue(UrlInfo* pUrlInfo, const char* szTempFilename, const char* szOriginalFilename, const char* szOriginalCategory);
|
||||
|
||||
public:
|
||||
UrlCoordinator();
|
||||
virtual ~UrlCoordinator();
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
void Update(Subject* Caller, void* Aspect);
|
||||
|
||||
// Editing the queue
|
||||
void AddUrlToQueue(UrlInfo* pUrlInfo, bool AddFirst);
|
||||
bool HasMoreJobs() { return m_bHasMoreJobs; }
|
||||
|
||||
void LogDebugInfo();
|
||||
};
|
||||
|
||||
class UrlDownloader : public WebDownloader
|
||||
{
|
||||
private:
|
||||
UrlInfo* m_pUrlInfo;
|
||||
char* m_szCategory;
|
||||
|
||||
protected:
|
||||
virtual void ProcessHeader(const char* szLine);
|
||||
|
||||
public:
|
||||
UrlDownloader();
|
||||
~UrlDownloader();
|
||||
void SetUrlInfo(UrlInfo* pUrlInfo) { m_pUrlInfo = pUrlInfo; }
|
||||
UrlInfo* GetUrlInfo() { return m_pUrlInfo; }
|
||||
const char* GetCategory() { return m_szCategory; }
|
||||
};
|
||||
|
||||
#endif
|
||||
222
Util.h
222
Util.h
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -15,7 +15,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -57,8 +57,220 @@ public:
|
||||
const char* Next();
|
||||
};
|
||||
|
||||
char* BaseFileName(const char* filename);
|
||||
class Util
|
||||
{
|
||||
public:
|
||||
|
||||
void NormalizePathSeparators(char* Path);
|
||||
static char* BaseFileName(const char* filename);
|
||||
static void NormalizePathSeparators(char* szPath);
|
||||
static bool LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength);
|
||||
static bool CreateSparseFile(const char* szFilename, int iSize);
|
||||
static bool TruncateFile(const char* szFilename, int iSize);
|
||||
static void MakeValidFilename(char* szFilename, char cReplaceChar, bool bAllowSlashes);
|
||||
static bool MoveFile(const char* szSrcFilename, const char* szDstFilename);
|
||||
static bool FileExists(const char* szFilename);
|
||||
static bool DirectoryExists(const char* szDirFilename);
|
||||
static bool CreateDirectory(const char* szDirFilename);
|
||||
static bool ForceDirectories(const char* szPath);
|
||||
static bool GetCurrentDirectory(char* szBuffer, int iBufSize);
|
||||
static bool SetCurrentDirectory(const char* szDirFilename);
|
||||
static long long FileSize(const char* szFilename);
|
||||
static long long FreeDiskSize(const char* szPath);
|
||||
static bool DirEmpty(const char* szDirFilename);
|
||||
static bool RenameBak(const char* szFilename, const char* szBakPart, bool bRemoveOldExtension, char* szNewNameBuf, int iNewNameBufSize);
|
||||
#ifndef WIN32
|
||||
static bool ExpandHomePath(const char* szFilename, char* szBuffer, int iBufSize);
|
||||
#endif
|
||||
static void ExpandFileName(const char* szFilename, char* szBuffer, int iBufSize);
|
||||
static void FormatFileSize(char* szBuffer, int iBufLen, long long lFileSize);
|
||||
|
||||
/*
|
||||
* Split command line int arguments.
|
||||
* Uses spaces and single quotation marks as separators.
|
||||
* Returns bool if sucessful or false if bad escaping was detected.
|
||||
* Parameter "argv" may be NULL if only a syntax check is needed.
|
||||
* Parsed parameters returned in Array "argv", which contains at least one element.
|
||||
* The last element in array is NULL.
|
||||
* Restrictions: the number of arguments is limited to 100 and each arguments must
|
||||
* be maximum 1024 chars long.
|
||||
* If these restrictions are exceeded, only first 100 arguments and only first 1024
|
||||
* for each argument are returned (the functions still returns "true").
|
||||
*/
|
||||
static bool SplitCommandLine(const char* szCommandLine, char*** argv);
|
||||
|
||||
static long long JoinInt64(unsigned long Hi, unsigned long Lo);
|
||||
static void SplitInt64(long long Int64, unsigned long* Hi, unsigned long* Lo);
|
||||
|
||||
/**
|
||||
* Int64ToFloat converts Int64 to float.
|
||||
* Simple (float)Int64 does not work on all compilers,
|
||||
* for example on ARM for NSLU2 (unslung).
|
||||
*/
|
||||
static float Int64ToFloat(long long Int64);
|
||||
|
||||
static void TrimRight(char* szStr);
|
||||
|
||||
/*
|
||||
* Returns program version and revision number as string formatted like "0.7.0-r295".
|
||||
* If revision number is not available only version is returned ("0.7.0").
|
||||
*/
|
||||
static const char* VersionRevision() { return VersionRevisionBuf; };
|
||||
|
||||
/*
|
||||
* Initialize buffer for program version and revision number.
|
||||
* This function must be called during program initialization before any
|
||||
* call to "VersionRevision()".
|
||||
*/
|
||||
static void InitVersionRevision();
|
||||
|
||||
static char VersionRevisionBuf[40];
|
||||
};
|
||||
|
||||
class WebUtil
|
||||
{
|
||||
public:
|
||||
static unsigned int DecodeBase64(char* szInputBuffer, int iInputBufferLength, char* szOutputBuffer);
|
||||
|
||||
/*
|
||||
* Encodes string to be used as content of xml-tag.
|
||||
* Returns new string allocated with malloc, it need to be freed by caller.
|
||||
*/
|
||||
static char* XmlEncode(const char* raw);
|
||||
|
||||
/*
|
||||
* Decodes string from xml.
|
||||
* The string is decoded on the place overwriting the content of raw-data.
|
||||
*/
|
||||
static void XmlDecode(char* raw);
|
||||
|
||||
/*
|
||||
* Returns pointer to tag-content and length of content in iValueLength
|
||||
* The returned pointer points to the part of source-string, no additional strings are allocated.
|
||||
*/
|
||||
static const char* XmlFindTag(const char* szXml, const char* szTag, int* pValueLength);
|
||||
|
||||
/*
|
||||
* Parses tag-content into szValueBuf.
|
||||
*/
|
||||
static bool XmlParseTagValue(const char* szXml, const char* szTag, char* szValueBuf, int iValueBufSize, const char** pTagEnd);
|
||||
|
||||
/*
|
||||
* Creates JSON-string by replace the certain characters with escape-sequences.
|
||||
* Returns new string allocated with malloc, it need to be freed by caller.
|
||||
*/
|
||||
static char* JsonEncode(const char* raw);
|
||||
|
||||
/*
|
||||
* Decodes JSON-string.
|
||||
* The string is decoded on the place overwriting the content of raw-data.
|
||||
*/
|
||||
static void JsonDecode(char* raw);
|
||||
|
||||
/*
|
||||
* Returns pointer to field-content and length of content in iValueLength
|
||||
* The returned pointer points to the part of source-string, no additional strings are allocated.
|
||||
*/
|
||||
static const char* JsonFindField(const char* szJsonText, const char* szFieldName, int* pValueLength);
|
||||
|
||||
/*
|
||||
* Returns pointer to field-content and length of content in iValueLength
|
||||
* The returned pointer points to the part of source-string, no additional strings are allocated.
|
||||
*/
|
||||
static const char* JsonNextValue(const char* szJsonText, int* pValueLength);
|
||||
|
||||
/*
|
||||
* Unquote http quoted string.
|
||||
* The string is decoded on the place overwriting the content of raw-data.
|
||||
*/
|
||||
static void HttpUnquote(char* raw);
|
||||
};
|
||||
|
||||
class URL
|
||||
{
|
||||
private:
|
||||
char* m_szAddress;
|
||||
char* m_szProtocol;
|
||||
char* m_szUser;
|
||||
char* m_szPassword;
|
||||
char* m_szHost;
|
||||
char* m_szResource;
|
||||
int m_iPort;
|
||||
bool m_bTLS;
|
||||
bool m_bValid;
|
||||
void ParseURL();
|
||||
|
||||
public:
|
||||
URL(const char* szAddress);
|
||||
~URL();
|
||||
bool IsValid() { return m_bValid; }
|
||||
const char* GetAddress() { return m_szAddress; }
|
||||
const char* GetProtocol() { return m_szProtocol; }
|
||||
const char* GetUser() { return m_szUser; }
|
||||
const char* GetPassword() { return m_szPassword; }
|
||||
const char* GetHost() { return m_szHost; }
|
||||
const char* GetResource() { return m_szResource; }
|
||||
int GetPort() { return m_iPort; }
|
||||
bool GetTLS() { return m_bTLS; }
|
||||
};
|
||||
|
||||
class RegEx
|
||||
{
|
||||
private:
|
||||
void* m_pContext;
|
||||
bool m_bValid;
|
||||
|
||||
public:
|
||||
RegEx(const char *szPattern);
|
||||
~RegEx();
|
||||
bool IsValid() { return m_bValid; }
|
||||
bool Match(const char *szStr);
|
||||
};
|
||||
|
||||
#ifndef DISABLE_GZIP
|
||||
class ZLib
|
||||
{
|
||||
public:
|
||||
/*
|
||||
* calculates the size required for output buffer
|
||||
*/
|
||||
static unsigned int GZipLen(int iInputBufferLength);
|
||||
|
||||
/*
|
||||
* returns the size of bytes written to szOutputBuffer or 0 if the buffer is too small or an error occured.
|
||||
*/
|
||||
static unsigned int GZip(const void* szInputBuffer, int iInputBufferLength, void* szOutputBuffer, int iOutputBufferLength);
|
||||
};
|
||||
|
||||
class GUnzipStream
|
||||
{
|
||||
public:
|
||||
enum EStatus
|
||||
{
|
||||
zlError,
|
||||
zlFinished,
|
||||
zlOK
|
||||
};
|
||||
|
||||
private:
|
||||
void* m_pZStream;
|
||||
void* m_pOutputBuffer;
|
||||
int m_iBufferSize;
|
||||
|
||||
public:
|
||||
GUnzipStream(int BufferSize);
|
||||
~GUnzipStream();
|
||||
|
||||
/*
|
||||
* set next memory block for uncompression
|
||||
*/
|
||||
void Write(const void *pInputBuffer, int iInputBufferLength);
|
||||
|
||||
/*
|
||||
* get next uncompressed memory block.
|
||||
* iOutputBufferLength - the size of uncompressed block. if it is "0" the next compressed block must be provided via "Write".
|
||||
*/
|
||||
EStatus Read(const void **pOutputBuffer, int *iOutputBufferLength);
|
||||
};
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
679
WebDownloader.cpp
Normal file
679
WebDownloader.cpp
Normal file
@@ -0,0 +1,679 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cstdio>
|
||||
#ifdef WIN32
|
||||
#include <direct.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "WebDownloader.h"
|
||||
#include "Log.h"
|
||||
#include "Options.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
WebDownloader::WebDownloader()
|
||||
{
|
||||
debug("Creating WebDownloader");
|
||||
|
||||
m_szURL = NULL;
|
||||
m_szOutputFilename = NULL;
|
||||
m_pConnection = NULL;
|
||||
m_szInfoName = NULL;
|
||||
m_bConfirmedLength = false;
|
||||
m_eStatus = adUndefined;
|
||||
m_szOriginalFilename = NULL;
|
||||
SetLastUpdateTimeNow();
|
||||
}
|
||||
|
||||
WebDownloader::~WebDownloader()
|
||||
{
|
||||
debug("Destroying WebDownloader");
|
||||
|
||||
if (m_szURL)
|
||||
{
|
||||
free(m_szURL);
|
||||
}
|
||||
if (m_szInfoName)
|
||||
{
|
||||
free(m_szInfoName);
|
||||
}
|
||||
if (m_szOutputFilename)
|
||||
{
|
||||
free(m_szOutputFilename);
|
||||
}
|
||||
if (m_szOriginalFilename)
|
||||
{
|
||||
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)
|
||||
{
|
||||
m_szURL = strdup(szURL);
|
||||
}
|
||||
|
||||
void WebDownloader::SetStatus(EStatus eStatus)
|
||||
{
|
||||
m_eStatus = eStatus;
|
||||
Notify(NULL);
|
||||
}
|
||||
|
||||
void WebDownloader::Run()
|
||||
{
|
||||
debug("Entering WebDownloader-loop");
|
||||
|
||||
SetStatus(adRunning);
|
||||
|
||||
int iRemainedDownloadRetries = g_pOptions->GetRetries() > 0 ? g_pOptions->GetRetries() : 1;
|
||||
int iRemainedConnectRetries = iRemainedDownloadRetries > 10 ? iRemainedDownloadRetries : 10;
|
||||
|
||||
EStatus Status = adFailed;
|
||||
|
||||
while (!IsStopped() && iRemainedDownloadRetries > 0 && iRemainedConnectRetries > 0)
|
||||
{
|
||||
SetLastUpdateTimeNow();
|
||||
|
||||
Status = Download();
|
||||
|
||||
if ((((Status == adFailed) && (iRemainedDownloadRetries > 1)) ||
|
||||
((Status == adConnectError) && (iRemainedConnectRetries > 1)))
|
||||
&& !IsStopped() && !(g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2()))
|
||||
{
|
||||
detail("Waiting %i sec to retry", g_pOptions->GetRetryInterval());
|
||||
int msec = 0;
|
||||
while (!IsStopped() && (msec < g_pOptions->GetRetryInterval() * 1000) &&
|
||||
!(g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2()))
|
||||
{
|
||||
usleep(100 * 1000);
|
||||
msec += 100;
|
||||
}
|
||||
}
|
||||
|
||||
if (IsStopped() || g_pOptions->GetPauseDownload() || g_pOptions->GetPauseDownload2())
|
||||
{
|
||||
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->SetSuppressErrors(false);
|
||||
|
||||
// connection
|
||||
bool bConnected = m_pConnection->Connect();
|
||||
if (!bConnected || IsStopped())
|
||||
{
|
||||
FreeConnection();
|
||||
return adConnectError;
|
||||
}
|
||||
|
||||
// Okay, we got a Connection. Now start downloading.
|
||||
detail("Downloading %s", m_szInfoName);
|
||||
|
||||
SendHeaders(&url);
|
||||
|
||||
Status = DownloadHeaders();
|
||||
|
||||
if (Status == adRunning)
|
||||
{
|
||||
Status = DownloadBody();
|
||||
}
|
||||
|
||||
if (IsStopped())
|
||||
{
|
||||
Status = adFailed;
|
||||
}
|
||||
|
||||
FreeConnection();
|
||||
|
||||
if (Status != adFinished)
|
||||
{
|
||||
// Download failed, delete broken output file
|
||||
remove(m_szOutputFilename);
|
||||
}
|
||||
|
||||
return Status;
|
||||
}
|
||||
|
||||
|
||||
WebDownloader::EStatus WebDownloader::CreateConnection(URL *pUrl)
|
||||
{
|
||||
if (!pUrl->IsValid())
|
||||
{
|
||||
error("URL is not valid: %s", pUrl->GetAddress());
|
||||
return adFatalError;
|
||||
}
|
||||
|
||||
int iPort = pUrl->GetPort();
|
||||
if (iPort == 0 && !strcasecmp(pUrl->GetProtocol(), "http"))
|
||||
{
|
||||
iPort = 80;
|
||||
}
|
||||
if (iPort == 0 && !strcasecmp(pUrl->GetProtocol(), "https"))
|
||||
{
|
||||
iPort = 443;
|
||||
}
|
||||
|
||||
if (strcasecmp(pUrl->GetProtocol(), "http") && strcasecmp(pUrl->GetProtocol(), "https"))
|
||||
{
|
||||
error("Unsupported protocol in URL: %s", pUrl->GetAddress());
|
||||
return adFatalError;
|
||||
}
|
||||
|
||||
#ifdef DISABLE_TLS
|
||||
if (!strcasecmp(pUrl->GetProtocol(), "https"))
|
||||
{
|
||||
error("Program was compiled without TLS/SSL-support. Cannot download using https protocol. URL: %s", pUrl->GetAddress());
|
||||
return adFatalError;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool bTLS = !strcasecmp(pUrl->GetProtocol(), "https");
|
||||
|
||||
m_pConnection = new Connection(pUrl->GetHost(), iPort, bTLS);
|
||||
|
||||
return adRunning;
|
||||
}
|
||||
|
||||
void WebDownloader::SendHeaders(URL *pUrl)
|
||||
{
|
||||
char tmp[1024];
|
||||
|
||||
// retrieve file
|
||||
snprintf(tmp, 1024, "GET %s HTTP/1.0\r\n", pUrl->GetResource());
|
||||
tmp[1024-1] = '\0';
|
||||
m_pConnection->WriteLine(tmp);
|
||||
|
||||
snprintf(tmp, 1024, "User-Agent: nzbget/%s\r\n", Util::VersionRevision());
|
||||
tmp[1024-1] = '\0';
|
||||
m_pConnection->WriteLine(tmp);
|
||||
|
||||
snprintf(tmp, 1024, "Host: %s\r\n", pUrl->GetHost());
|
||||
tmp[1024-1] = '\0';
|
||||
m_pConnection->WriteLine(tmp);
|
||||
|
||||
m_pConnection->WriteLine("Accept: */*\r\n");
|
||||
#ifndef DISABLE_GZIP
|
||||
m_pConnection->WriteLine("Accept-Encoding: gzip\r\n");
|
||||
#endif
|
||||
m_pConnection->WriteLine("Connection: close\r\n");
|
||||
m_pConnection->WriteLine("\r\n");
|
||||
}
|
||||
|
||||
WebDownloader::EStatus WebDownloader::DownloadHeaders()
|
||||
{
|
||||
EStatus Status = adRunning;
|
||||
|
||||
m_bConfirmedLength = false;
|
||||
const int LineBufSize = 1024*10;
|
||||
char* szLineBuf = (char*)malloc(LineBufSize);
|
||||
m_iContentLen = -1;
|
||||
bool bFirstLine = true;
|
||||
m_bGZip = false;
|
||||
|
||||
// Headers
|
||||
while (!IsStopped())
|
||||
{
|
||||
SetLastUpdateTimeNow();
|
||||
|
||||
int iLen = 0;
|
||||
char* line = m_pConnection->ReadLine(szLineBuf, LineBufSize, &iLen);
|
||||
|
||||
if (bFirstLine)
|
||||
{
|
||||
Status = CheckResponse(szLineBuf);
|
||||
if (Status != adRunning)
|
||||
{
|
||||
break;
|
||||
}
|
||||
bFirstLine = false;
|
||||
}
|
||||
|
||||
// Have we encountered a timeout?
|
||||
if (!line)
|
||||
{
|
||||
if (!IsStopped())
|
||||
{
|
||||
warn("URL %s failed: Unexpected end of file", m_szInfoName);
|
||||
}
|
||||
Status = adFailed;
|
||||
break;
|
||||
}
|
||||
|
||||
debug("Header: %s", line);
|
||||
|
||||
// detect body of response
|
||||
if (*line == '\r' || *line == '\n')
|
||||
{
|
||||
if (m_iContentLen == -1 && !m_bGZip)
|
||||
{
|
||||
warn("URL %s: Content-Length is not submitted by server, cannot verify whether the file is complete", m_szInfoName);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
ProcessHeader(line);
|
||||
}
|
||||
|
||||
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->Recv(szLineBuf, LineBufSize);
|
||||
szBuffer = szLineBuf;
|
||||
}
|
||||
|
||||
// Have we encountered a timeout?
|
||||
if (iLen <= 0)
|
||||
{
|
||||
if (m_iContentLen == -1)
|
||||
{
|
||||
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
|
||||
if (m_pGUnzipStream)
|
||||
{
|
||||
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, "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 (!strncmp(szLine, "Content-Length: ", 16))
|
||||
{
|
||||
m_iContentLen = atoi(szLine + 16);
|
||||
m_bConfirmedLength = true;
|
||||
}
|
||||
|
||||
if (!strncmp(szLine, "Content-Encoding: gzip", 22))
|
||||
{
|
||||
m_bGZip = true;
|
||||
}
|
||||
|
||||
if (!strncmp(szLine, "Content-Disposition: ", 21))
|
||||
{
|
||||
ParseFilename(szLine);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
if (m_szOriginalFilename)
|
||||
{
|
||||
free(m_szOriginalFilename);
|
||||
}
|
||||
m_szOriginalFilename = strdup(Util::BaseFileName(fname));
|
||||
|
||||
debug("OriginalFilename: %s", m_szOriginalFilename);
|
||||
}
|
||||
|
||||
bool WebDownloader::Write(void* pBuffer, int iLen)
|
||||
{
|
||||
if (!m_pOutFile && !PrepareFile())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_GZIP
|
||||
if (m_bGZip)
|
||||
{
|
||||
m_pGUnzipStream->Write(pBuffer, iLen);
|
||||
const void *pOutBuf;
|
||||
int iOutLen = 1;
|
||||
while (iOutLen > 0)
|
||||
{
|
||||
GUnzipStream::EStatus eGZStatus = m_pGUnzipStream->Read(&pOutBuf, &iOutLen);
|
||||
|
||||
if (eGZStatus == GUnzipStream::zlError)
|
||||
{
|
||||
error("URL %s: GUnzip failed", m_szInfoName);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (iOutLen > 0 && fwrite(pOutBuf, 1, iOutLen, m_pOutFile) <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (eGZStatus == GUnzipStream::zlFinished)
|
||||
{
|
||||
m_bConfirmedLength = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
|
||||
return fwrite(pBuffer, 1, iLen, m_pOutFile) > 0;
|
||||
}
|
||||
|
||||
bool WebDownloader::PrepareFile()
|
||||
{
|
||||
// prepare file for writing
|
||||
|
||||
const char* szFilename = m_szOutputFilename;
|
||||
m_pOutFile = fopen(szFilename, "wb");
|
||||
if (!m_pOutFile)
|
||||
{
|
||||
error("Could not %s file %s", "create", szFilename);
|
||||
return false;
|
||||
}
|
||||
if (g_pOptions->GetWriteBufferSize() > 0)
|
||||
{
|
||||
setvbuf(m_pOutFile, (char *)NULL, _IOFBF, g_pOptions->GetWriteBufferSize());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void WebDownloader::LogDebugInfo()
|
||||
{
|
||||
char szTime[50];
|
||||
#ifdef HAVE_CTIME_R_3
|
||||
ctime_r(&m_tLastUpdateTime, szTime, 50);
|
||||
#else
|
||||
ctime_r(&m_tLastUpdateTime, szTime);
|
||||
#endif
|
||||
|
||||
debug(" Web-Download: status=%i, LastUpdateTime=%s, filename=%s", m_eStatus, szTime, Util::BaseFileName(m_szOutputFilename));
|
||||
}
|
||||
|
||||
void WebDownloader::Stop()
|
||||
{
|
||||
debug("Trying to stop WebDownloader");
|
||||
Thread::Stop();
|
||||
m_mutexConnection.Lock();
|
||||
if (m_pConnection)
|
||||
{
|
||||
m_pConnection->SetSuppressErrors(true);
|
||||
m_pConnection->Cancel();
|
||||
}
|
||||
m_mutexConnection.Unlock();
|
||||
debug("WebDownloader stopped successfully");
|
||||
}
|
||||
|
||||
bool WebDownloader::Terminate()
|
||||
{
|
||||
Connection* pConnection = m_pConnection;
|
||||
bool terminated = Kill();
|
||||
if (terminated && pConnection)
|
||||
{
|
||||
debug("Terminating connection");
|
||||
pConnection->SetSuppressErrors(true);
|
||||
pConnection->Cancel();
|
||||
pConnection->Disconnect();
|
||||
delete pConnection;
|
||||
}
|
||||
return terminated;
|
||||
}
|
||||
|
||||
void WebDownloader::FreeConnection()
|
||||
{
|
||||
if (m_pConnection)
|
||||
{
|
||||
debug("Releasing connection");
|
||||
m_mutexConnection.Lock();
|
||||
if (m_pConnection->GetStatus() == Connection::csCancelled)
|
||||
{
|
||||
m_pConnection->Disconnect();
|
||||
}
|
||||
delete m_pConnection;
|
||||
m_pConnection = NULL;
|
||||
m_mutexConnection.Unlock();
|
||||
}
|
||||
}
|
||||
103
WebDownloader.h
Normal file
103
WebDownloader.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef 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,
|
||||
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_bGZip;
|
||||
#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 Download();
|
||||
EStatus CreateConnection(URL *pUrl);
|
||||
void ParseFilename(const char* szContentDisposition);
|
||||
void SendHeaders(URL *pUrl);
|
||||
EStatus DownloadHeaders();
|
||||
EStatus DownloadBody();
|
||||
|
||||
protected:
|
||||
virtual void ProcessHeader(const char* szLine);
|
||||
|
||||
public:
|
||||
WebDownloader();
|
||||
~WebDownloader();
|
||||
EStatus GetStatus() { return m_eStatus; }
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
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 LogDebugInfo();
|
||||
};
|
||||
|
||||
#endif
|
||||
485
WebServer.cpp
Normal file
485
WebServer.cpp
Normal file
@@ -0,0 +1,485 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cstdio>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "WebServer.h"
|
||||
#include "XmlRpc.h"
|
||||
#include "Log.h"
|
||||
#include "Options.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
static const char* ERR_HTTP_BAD_REQUEST = "400 Bad Request";
|
||||
static const char* ERR_HTTP_NOT_FOUND = "404 Not Found";
|
||||
static const char* ERR_HTTP_SERVICE_UNAVAILABLE = "503 Service Unavailable";
|
||||
|
||||
static const int MAX_UNCOMPRESSED_SIZE = 500;
|
||||
|
||||
//*****************************************************************
|
||||
// WebProcessor
|
||||
|
||||
WebProcessor::WebProcessor()
|
||||
{
|
||||
m_pConnection = NULL;
|
||||
m_szClientIP = NULL;
|
||||
m_szRequest = NULL;
|
||||
m_szUrl = NULL;
|
||||
m_szOrigin = NULL;
|
||||
}
|
||||
|
||||
WebProcessor::~WebProcessor()
|
||||
{
|
||||
if (m_szRequest)
|
||||
{
|
||||
free(m_szRequest);
|
||||
}
|
||||
if (m_szUrl)
|
||||
{
|
||||
free(m_szUrl);
|
||||
}
|
||||
if (m_szOrigin)
|
||||
{
|
||||
free(m_szOrigin);
|
||||
}
|
||||
}
|
||||
|
||||
void WebProcessor::SetUrl(const char* szUrl)
|
||||
{
|
||||
m_szUrl = strdup(szUrl);
|
||||
}
|
||||
|
||||
void WebProcessor::Execute()
|
||||
{
|
||||
m_bGZip =false;
|
||||
char szAuthInfo[1024];
|
||||
szAuthInfo[0] = '\0';
|
||||
|
||||
// reading http header
|
||||
char szBuffer[1024];
|
||||
bool bBody = false;
|
||||
int iContentLen = 0;
|
||||
while (char* p = m_pConnection->ReadLine(szBuffer, sizeof(szBuffer), NULL))
|
||||
{
|
||||
if (char* pe = strrchr(p, '\r')) *pe = '\0';
|
||||
debug("header=%s", p);
|
||||
if (!strncasecmp(p, "Content-Length: ", 16))
|
||||
{
|
||||
iContentLen = atoi(p + 16);
|
||||
}
|
||||
if (!strncasecmp(p, "Authorization: Basic ", 21))
|
||||
{
|
||||
char* szAuthInfo64 = p + 21;
|
||||
if (strlen(szAuthInfo64) > sizeof(szAuthInfo))
|
||||
{
|
||||
error("invalid-request: auth-info too big");
|
||||
return;
|
||||
}
|
||||
szAuthInfo[WebUtil::DecodeBase64(szAuthInfo64, 0, szAuthInfo)] = '\0';
|
||||
}
|
||||
if (!strncasecmp(p, "Accept-Encoding: ", 17))
|
||||
{
|
||||
m_bGZip = strstr(p, "gzip");
|
||||
}
|
||||
if (!strncasecmp(p, "Origin: ", 8))
|
||||
{
|
||||
m_szOrigin = strdup(p + 8);
|
||||
}
|
||||
if (*p == '\0')
|
||||
{
|
||||
bBody = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
debug("URL=%s", m_szUrl);
|
||||
debug("Authorization=%s", szAuthInfo);
|
||||
|
||||
if (m_eHttpMethod == hmPost && iContentLen <= 0)
|
||||
{
|
||||
error("invalid-request: content length is 0");
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_eHttpMethod == hmOptions)
|
||||
{
|
||||
SendOptionsResponse();
|
||||
return;
|
||||
}
|
||||
|
||||
// remove subfolder "nzbget" from the path (if exists)
|
||||
// http://localhost:6789/nzbget/username:password/jsonrpc -> http://localhost:6789/username:password/jsonrpc
|
||||
if (!strncmp(m_szUrl, "/nzbget/", 8))
|
||||
{
|
||||
char* sz_OldUrl = m_szUrl;
|
||||
m_szUrl = strdup(m_szUrl + 7);
|
||||
free(sz_OldUrl);
|
||||
}
|
||||
// http://localhost:6789/nzbget -> http://localhost:6789
|
||||
if (!strcmp(m_szUrl, "/nzbget"))
|
||||
{
|
||||
char szRedirectURL[1024];
|
||||
snprintf(szRedirectURL, 1024, "%s/", m_szUrl);
|
||||
szRedirectURL[1024-1] = '\0';
|
||||
SendRedirectResponse(szRedirectURL);
|
||||
return;
|
||||
}
|
||||
|
||||
// authorization via URL in format:
|
||||
// http://localhost:6789/username:password/jsonrpc
|
||||
char* pauth1 = strchr(m_szUrl + 1, ':');
|
||||
char* pauth2 = strchr(m_szUrl + 1, '/');
|
||||
if (pauth1 && pauth1 < pauth2)
|
||||
{
|
||||
char* pstart = m_szUrl + 1;
|
||||
int iLen = 0;
|
||||
char* pend = strchr(pstart + 1, '/');
|
||||
if (pend)
|
||||
{
|
||||
iLen = (int)(pend - pstart < (int)sizeof(szAuthInfo) - 1 ? pend - pstart : (int)sizeof(szAuthInfo) - 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
iLen = strlen(pstart);
|
||||
}
|
||||
strncpy(szAuthInfo, pstart, iLen);
|
||||
szAuthInfo[iLen] = '\0';
|
||||
char* sz_OldUrl = m_szUrl;
|
||||
m_szUrl = strdup(pend);
|
||||
free(sz_OldUrl);
|
||||
}
|
||||
|
||||
debug("Final URL=%s", m_szUrl);
|
||||
|
||||
if (strlen(szAuthInfo) == 0)
|
||||
{
|
||||
SendAuthResponse();
|
||||
return;
|
||||
}
|
||||
|
||||
// Authorization
|
||||
char* pw = strchr(szAuthInfo, ':');
|
||||
if (pw) *pw++ = '\0';
|
||||
if (strcmp(szAuthInfo, "nzbget") || strcmp(pw, g_pOptions->GetControlPassword()))
|
||||
{
|
||||
warn("request received on port %i from %s, but password invalid", g_pOptions->GetControlPort(), m_szClientIP);
|
||||
SendAuthResponse();
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_eHttpMethod == hmPost)
|
||||
{
|
||||
// reading http body (request content)
|
||||
m_szRequest = (char*)malloc(iContentLen + 1);
|
||||
m_szRequest[iContentLen] = '\0';
|
||||
|
||||
if (!m_pConnection->RecvAll(m_szRequest, iContentLen))
|
||||
{
|
||||
free(m_szRequest);
|
||||
error("invalid-request: could not read data");
|
||||
return;
|
||||
}
|
||||
debug("Request=%s", m_szRequest);
|
||||
}
|
||||
|
||||
debug("request received from %s", m_szClientIP);
|
||||
|
||||
Dispatch();
|
||||
}
|
||||
|
||||
void WebProcessor::Dispatch()
|
||||
{
|
||||
if (*m_szUrl != '/')
|
||||
{
|
||||
SendErrorResponse(ERR_HTTP_BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
|
||||
if (XmlRpcProcessor::IsRpcRequest(m_szUrl))
|
||||
{
|
||||
XmlRpcProcessor processor;
|
||||
processor.SetRequest(m_szRequest);
|
||||
processor.SetClientIP(m_szClientIP);
|
||||
processor.SetHttpMethod(m_eHttpMethod == hmGet ? XmlRpcProcessor::hmGet : XmlRpcProcessor::hmPost);
|
||||
processor.SetUrl(m_szUrl);
|
||||
processor.Execute();
|
||||
SendBodyResponse(processor.GetResponse(), strlen(processor.GetResponse()), processor.GetContentType());
|
||||
return;
|
||||
}
|
||||
|
||||
if (!g_pOptions->GetWebDir() || strlen(g_pOptions->GetWebDir()) == 0)
|
||||
{
|
||||
SendErrorResponse(ERR_HTTP_SERVICE_UNAVAILABLE);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_eHttpMethod != hmGet)
|
||||
{
|
||||
SendErrorResponse(ERR_HTTP_BAD_REQUEST);
|
||||
return;
|
||||
}
|
||||
|
||||
// for security reasons we allow only characters "0..9 A..Z a..z . - _ /" in the URLs
|
||||
// we also don't allow ".." in the URLs
|
||||
for (char *p = m_szUrl; *p; p++)
|
||||
{
|
||||
if (!((*p >= '0' && *p <= '9') || (*p >= 'A' && *p <= 'Z') || (*p >= 'a' && *p <= 'z') ||
|
||||
*p == '.' || *p == '-' || *p == '_' || *p == '/') || (*p == '.' && p[1] == '.'))
|
||||
{
|
||||
SendErrorResponse(ERR_HTTP_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const char *szDefRes = "";
|
||||
if (m_szUrl[strlen(m_szUrl)-1] == '/')
|
||||
{
|
||||
// default file in directory (if not specified) is "index.html"
|
||||
szDefRes = "index.html";
|
||||
}
|
||||
|
||||
char disk_filename[1024];
|
||||
snprintf(disk_filename, sizeof(disk_filename), "%s%s%s", g_pOptions->GetWebDir(), m_szUrl + 1, szDefRes);
|
||||
|
||||
disk_filename[sizeof(disk_filename)-1] = '\0';
|
||||
|
||||
SendFileResponse(disk_filename);
|
||||
}
|
||||
|
||||
void WebProcessor::SendAuthResponse()
|
||||
{
|
||||
const char* AUTH_RESPONSE_HEADER =
|
||||
"HTTP/1.0 401 Unauthorized\r\n"
|
||||
"WWW-Authenticate: Basic realm=\"NZBGet\"\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Content-Type: text/plain\r\n"
|
||||
"Server: nzbget-%s\r\n"
|
||||
"\r\n";
|
||||
char szResponseHeader[1024];
|
||||
snprintf(szResponseHeader, 1024, AUTH_RESPONSE_HEADER, Util::VersionRevision());
|
||||
|
||||
// Send the response answer
|
||||
debug("ResponseHeader=%s", szResponseHeader);
|
||||
m_pConnection->Send(szResponseHeader, strlen(szResponseHeader));
|
||||
}
|
||||
|
||||
void WebProcessor::SendOptionsResponse()
|
||||
{
|
||||
const char* OPTIONS_RESPONSE_HEADER =
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Connection: close\r\n"
|
||||
//"Content-Type: plain/text\r\n"
|
||||
"Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n"
|
||||
"Access-Control-Allow-Origin: %s\r\n"
|
||||
"Access-Control-Allow-Credentials: true\r\n"
|
||||
"Access-Control-Max-Age: 86400\r\n"
|
||||
"Access-Control-Allow-Headers: Content-Type, Authorization\r\n"
|
||||
"Server: nzbget-%s\r\n"
|
||||
"\r\n";
|
||||
char szResponseHeader[1024];
|
||||
snprintf(szResponseHeader, 1024, OPTIONS_RESPONSE_HEADER,
|
||||
m_szOrigin ? m_szOrigin : "",
|
||||
Util::VersionRevision());
|
||||
|
||||
// Send the response answer
|
||||
debug("ResponseHeader=%s", szResponseHeader);
|
||||
m_pConnection->Send(szResponseHeader, strlen(szResponseHeader));
|
||||
}
|
||||
|
||||
void WebProcessor::SendErrorResponse(const char* szErrCode)
|
||||
{
|
||||
const char* RESPONSE_HEADER =
|
||||
"HTTP/1.0 %s\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Content-Length: %i\r\n"
|
||||
"Content-Type: text/html\r\n"
|
||||
"Server: nzbget-%s\r\n"
|
||||
"\r\n";
|
||||
|
||||
warn("Web-Server: %s, Resource: %s", szErrCode, m_szUrl);
|
||||
|
||||
char szResponseBody[1024];
|
||||
snprintf(szResponseBody, 1024, "<html><head><title>%s</title></head><body>Error: %s</body></html>", szErrCode, szErrCode);
|
||||
int iPageContentLen = strlen(szResponseBody);
|
||||
|
||||
char szResponseHeader[1024];
|
||||
snprintf(szResponseHeader, 1024, RESPONSE_HEADER, szErrCode, iPageContentLen, Util::VersionRevision());
|
||||
|
||||
// Send the response answer
|
||||
m_pConnection->Send(szResponseHeader, strlen(szResponseHeader));
|
||||
m_pConnection->Send(szResponseBody, iPageContentLen);
|
||||
}
|
||||
|
||||
void WebProcessor::SendRedirectResponse(const char* szURL)
|
||||
{
|
||||
const char* REDIRECT_RESPONSE_HEADER =
|
||||
"HTTP/1.0 301 Moved Permanently\r\n"
|
||||
"Location: %s\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Server: nzbget-%s\r\n"
|
||||
"\r\n";
|
||||
char szResponseHeader[1024];
|
||||
snprintf(szResponseHeader, 1024, REDIRECT_RESPONSE_HEADER, szURL, Util::VersionRevision());
|
||||
|
||||
// Send the response answer
|
||||
debug("ResponseHeader=%s", szResponseHeader);
|
||||
m_pConnection->Send(szResponseHeader, strlen(szResponseHeader));
|
||||
}
|
||||
|
||||
void WebProcessor::SendBodyResponse(const char* szBody, int iBodyLen, const char* szContentType)
|
||||
{
|
||||
const char* RESPONSE_HEADER =
|
||||
"HTTP/1.1 200 OK\r\n"
|
||||
"Connection: close\r\n"
|
||||
"Access-Control-Allow-Methods: GET, POST, OPTIONS\r\n"
|
||||
"Access-Control-Allow-Origin: %s\r\n"
|
||||
"Access-Control-Allow-Credentials: true\r\n"
|
||||
"Access-Control-Max-Age: 86400\r\n"
|
||||
"Access-Control-Allow-Headers: Content-Type, Authorization\r\n"
|
||||
"Content-Length: %i\r\n"
|
||||
"%s" // Content-Type: xxx
|
||||
"%s" // Content-Encoding: gzip
|
||||
"Server: nzbget-%s\r\n"
|
||||
"\r\n";
|
||||
|
||||
#ifndef DISABLE_GZIP
|
||||
char *szGBuf = NULL;
|
||||
bool bGZip = m_bGZip && iBodyLen > MAX_UNCOMPRESSED_SIZE;
|
||||
if (bGZip)
|
||||
{
|
||||
unsigned int iOutLen = ZLib::GZipLen(iBodyLen);
|
||||
szGBuf = (char*)malloc(iOutLen);
|
||||
int iGZippedLen = ZLib::GZip(szBody, iBodyLen, szGBuf, iOutLen);
|
||||
if (iGZippedLen > 0 && iGZippedLen < iBodyLen)
|
||||
{
|
||||
szBody = szGBuf;
|
||||
iBodyLen = iGZippedLen;
|
||||
}
|
||||
else
|
||||
{
|
||||
free(szGBuf);
|
||||
szGBuf = NULL;
|
||||
bGZip = false;
|
||||
}
|
||||
}
|
||||
#else
|
||||
bool bGZip = false;
|
||||
#endif
|
||||
|
||||
char szContentTypeHeader[1024];
|
||||
if (szContentType)
|
||||
{
|
||||
snprintf(szContentTypeHeader, 1024, "Content-Type: %s\r\n", szContentType);
|
||||
}
|
||||
else
|
||||
{
|
||||
szContentTypeHeader[0] = '\0';
|
||||
}
|
||||
|
||||
char szResponseHeader[1024];
|
||||
snprintf(szResponseHeader, 1024, RESPONSE_HEADER,
|
||||
m_szOrigin ? m_szOrigin : "",
|
||||
iBodyLen, szContentTypeHeader,
|
||||
bGZip ? "Content-Encoding: gzip\r\n" : "",
|
||||
Util::VersionRevision());
|
||||
|
||||
// Send the request answer
|
||||
m_pConnection->Send(szResponseHeader, strlen(szResponseHeader));
|
||||
m_pConnection->Send(szBody, iBodyLen);
|
||||
|
||||
#ifndef DISABLE_GZIP
|
||||
if (szGBuf)
|
||||
{
|
||||
free(szGBuf);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void WebProcessor::SendFileResponse(const char* szFilename)
|
||||
{
|
||||
debug("serving file: %s", szFilename);
|
||||
|
||||
char *szBody;
|
||||
int iBodyLen;
|
||||
if (!Util::LoadFileIntoBuffer(szFilename, &szBody, &iBodyLen))
|
||||
{
|
||||
SendErrorResponse(ERR_HTTP_NOT_FOUND);
|
||||
return;
|
||||
}
|
||||
|
||||
// "LoadFileIntoBuffer" adds a trailing NULL, which we don't need here
|
||||
iBodyLen--;
|
||||
|
||||
SendBodyResponse(szBody, iBodyLen, DetectContentType(szFilename));
|
||||
|
||||
free(szBody);
|
||||
}
|
||||
|
||||
const char* WebProcessor::DetectContentType(const char* szFilename)
|
||||
{
|
||||
if (const char *szExt = strrchr(szFilename, '.'))
|
||||
{
|
||||
if (!strcasecmp(szExt, ".css"))
|
||||
{
|
||||
return "text/css";
|
||||
}
|
||||
else if (!strcasecmp(szExt, ".html"))
|
||||
{
|
||||
return "text/html";
|
||||
}
|
||||
else if (!strcasecmp(szExt, ".js"))
|
||||
{
|
||||
return "application/javascript";
|
||||
}
|
||||
else if (!strcasecmp(szExt, ".png"))
|
||||
{
|
||||
return "image/png";
|
||||
}
|
||||
else if (!strcasecmp(szExt, ".jpeg"))
|
||||
{
|
||||
return "image/jpeg";
|
||||
}
|
||||
else if (!strcasecmp(szExt, ".gif"))
|
||||
{
|
||||
return "image/gif";
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
69
WebServer.h
Normal file
69
WebServer.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef WEBSERVER_H
|
||||
#define WEBSERVER_H
|
||||
|
||||
#include "Connection.h"
|
||||
|
||||
class WebProcessor
|
||||
{
|
||||
public:
|
||||
enum EHttpMethod
|
||||
{
|
||||
hmPost,
|
||||
hmGet,
|
||||
hmOptions
|
||||
};
|
||||
|
||||
private:
|
||||
Connection* m_pConnection;
|
||||
const char* m_szClientIP;
|
||||
char* m_szRequest;
|
||||
char* m_szUrl;
|
||||
EHttpMethod m_eHttpMethod;
|
||||
bool m_bGZip;
|
||||
char* m_szOrigin;
|
||||
|
||||
void Dispatch();
|
||||
void SendAuthResponse();
|
||||
void SendOptionsResponse();
|
||||
void SendErrorResponse(const char* szErrCode);
|
||||
void SendFileResponse(const char* szFilename);
|
||||
void SendBodyResponse(const char* szBody, int iBodyLen, const char* szContentType);
|
||||
void SendRedirectResponse(const char* szURL);
|
||||
const char* DetectContentType(const char* szFilename);
|
||||
|
||||
public:
|
||||
WebProcessor();
|
||||
~WebProcessor();
|
||||
void Execute();
|
||||
void SetConnection(Connection* pConnection) { m_pConnection = pConnection; }
|
||||
void SetUrl(const char* szUrl);
|
||||
void SetHttpMethod(EHttpMethod eHttpMethod) { m_eHttpMethod = eHttpMethod; }
|
||||
void SetClientIP(const char* szClientIP) { m_szClientIP = szClientIP; }
|
||||
};
|
||||
|
||||
#endif
|
||||
2284
XmlRpc.cpp
Normal file
2284
XmlRpc.cpp
Normal file
File diff suppressed because it is too large
Load Diff
284
XmlRpc.h
Normal file
284
XmlRpc.h
Normal file
@@ -0,0 +1,284 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* 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
|
||||
* 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 XMLRPC_H
|
||||
#define XMLRPC_H
|
||||
|
||||
#include "Connection.h"
|
||||
|
||||
class StringBuilder
|
||||
{
|
||||
private:
|
||||
char* m_szBuffer;
|
||||
int m_iBufferSize;
|
||||
int m_iUsedSize;
|
||||
public:
|
||||
StringBuilder();
|
||||
~StringBuilder();
|
||||
void Append(const char* szStr);
|
||||
const char* GetBuffer() { return m_szBuffer; }
|
||||
};
|
||||
|
||||
class XmlCommand;
|
||||
|
||||
class XmlRpcProcessor
|
||||
{
|
||||
public:
|
||||
enum ERpcProtocol
|
||||
{
|
||||
rpUndefined,
|
||||
rpXmlRpc,
|
||||
rpJsonRpc,
|
||||
rpJsonPRpc
|
||||
};
|
||||
|
||||
enum EHttpMethod
|
||||
{
|
||||
hmPost,
|
||||
hmGet
|
||||
};
|
||||
|
||||
private:
|
||||
const char* m_szClientIP;
|
||||
char* m_szRequest;
|
||||
const char* m_szContentType;
|
||||
ERpcProtocol m_eProtocol;
|
||||
EHttpMethod m_eHttpMethod;
|
||||
char* m_szUrl;
|
||||
StringBuilder m_cResponse;
|
||||
|
||||
void Dispatch();
|
||||
XmlCommand* CreateCommand(const char* szMethodName);
|
||||
void MutliCall();
|
||||
void BuildResponse(const char* szResponse, const char* szCallbackFunc, bool bFault);
|
||||
|
||||
public:
|
||||
XmlRpcProcessor();
|
||||
~XmlRpcProcessor();
|
||||
void Execute();
|
||||
void SetHttpMethod(EHttpMethod eHttpMethod) { m_eHttpMethod = eHttpMethod; }
|
||||
void SetUrl(const char* szUrl);
|
||||
void SetClientIP(const char* szClientIP) { m_szClientIP = szClientIP; }
|
||||
void SetRequest(char* szRequest) { m_szRequest = szRequest; }
|
||||
const char* GetResponse() { return m_cResponse.GetBuffer(); }
|
||||
const char* GetContentType() { return m_szContentType; }
|
||||
static bool IsRpcRequest(const char* szUrl);
|
||||
};
|
||||
|
||||
class XmlCommand
|
||||
{
|
||||
protected:
|
||||
char* m_szRequest;
|
||||
char* m_szRequestPtr;
|
||||
char* m_szCallbackFunc;
|
||||
StringBuilder m_StringBuilder;
|
||||
bool m_bFault;
|
||||
XmlRpcProcessor::ERpcProtocol m_eProtocol;
|
||||
XmlRpcProcessor::EHttpMethod m_eHttpMethod;
|
||||
|
||||
void BuildErrorResponse(int iErrCode, const char* szErrText, ...);
|
||||
void BuildBoolResponse(bool bOK);
|
||||
void AppendResponse(const char* szPart);
|
||||
bool IsJson();
|
||||
bool CheckSafeMethod();
|
||||
bool NextParamAsInt(int* iValue);
|
||||
bool NextParamAsBool(bool* bValue);
|
||||
bool NextParamAsStr(char** szValueBuf);
|
||||
const char* BoolToStr(bool bValue);
|
||||
char* EncodeStr(const char* szStr);
|
||||
void DecodeStr(char* szStr);
|
||||
|
||||
public:
|
||||
XmlCommand();
|
||||
virtual ~XmlCommand() {}
|
||||
virtual void Execute() = 0;
|
||||
void PrepareParams();
|
||||
void SetRequest(char* szRequest) { m_szRequest = szRequest; m_szRequestPtr = m_szRequest; }
|
||||
void SetProtocol(XmlRpcProcessor::ERpcProtocol eProtocol) { m_eProtocol = eProtocol; }
|
||||
void SetHttpMethod(XmlRpcProcessor::EHttpMethod eHttpMethod) { m_eHttpMethod = eHttpMethod; }
|
||||
const char* GetResponse() { return m_StringBuilder.GetBuffer(); }
|
||||
const char* GetCallbackFunc() { return m_szCallbackFunc; }
|
||||
bool GetFault() { return m_bFault; }
|
||||
};
|
||||
|
||||
class ErrorXmlCommand: public XmlCommand
|
||||
{
|
||||
private:
|
||||
int m_iErrCode;
|
||||
const char* m_szErrText;
|
||||
|
||||
public:
|
||||
ErrorXmlCommand(int iErrCode, const char* szErrText);
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class PauseUnpauseXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
enum EPauseAction
|
||||
{
|
||||
paDownload,
|
||||
paDownload2,
|
||||
paPostProcess,
|
||||
paScan
|
||||
};
|
||||
|
||||
private:
|
||||
bool m_bPause;
|
||||
EPauseAction m_eEPauseAction;
|
||||
|
||||
public:
|
||||
PauseUnpauseXmlCommand(bool bPause, EPauseAction eEPauseAction);
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ShutdownXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ReloadXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class VersionXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class DumpDebugXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class SetDownloadRateXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class StatusXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class LogXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ListFilesXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ListGroupsXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class EditQueueXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class DownloadXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class PostQueueXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class WriteLogXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ClearLogXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ScanXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class HistoryXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class DownloadUrlXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class UrlQueueXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ConfigXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class LoadConfigXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class SaveConfigXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
void Save(const char *szFilename);
|
||||
};
|
||||
|
||||
#endif
|
||||
162
aclocal.m4
vendored
162
aclocal.m4
vendored
@@ -1,4 +1,4 @@
|
||||
# generated automatically by aclocal 1.9.5 -*- Autoconf -*-
|
||||
# generated automatically by aclocal 1.9.6 -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
|
||||
# 2005 Free Software Foundation, Inc.
|
||||
@@ -11,6 +11,164 @@
|
||||
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
# PARTICULAR PURPOSE.
|
||||
|
||||
# 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.
|
||||
|
||||
# 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
|
||||
@@ -28,7 +186,7 @@ AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version="1.9"])
|
||||
# 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.9.5])])
|
||||
[AM_AUTOMAKE_VERSION([1.9.6])])
|
||||
|
||||
# AM_AUX_DIR_EXPAND -*- Autoconf -*-
|
||||
|
||||
|
||||
51
config.h.in
51
config.h.in
@@ -6,20 +6,22 @@
|
||||
/* Define to 1 to not use curses */
|
||||
#undef DISABLE_CURSES
|
||||
|
||||
/* Define to 1 to disable gzip-support */
|
||||
#undef DISABLE_GZIP
|
||||
|
||||
/* Define to 1 to disable smart par-verification and restoration */
|
||||
#undef DISABLE_PARCHECK
|
||||
|
||||
/* Define to 1 to show progress during par-check (it must be disabled if
|
||||
sigc++ doesn't work correctly) */
|
||||
#undef ENABLE_PARPROGRESS
|
||||
|
||||
/* Define to 1 to include support for uulib */
|
||||
#undef ENABLE_UULIB
|
||||
/* Define to 1 to not use TLS/SSL */
|
||||
#undef DISABLE_TLS
|
||||
|
||||
/* Define to the name of macro which returns the name of function being
|
||||
compiled */
|
||||
#undef FUNCTION_MACRO_NAME
|
||||
|
||||
/* Define to 1 to create stacktrace on segmentation faults */
|
||||
#undef HAVE_BACKTRACE
|
||||
|
||||
/* Define to 1 if ctime_r takes 2 arguments */
|
||||
#undef HAVE_CTIME_R_2
|
||||
|
||||
@@ -29,6 +31,15 @@
|
||||
/* Define to 1 if you have the <curses.h> header file. */
|
||||
#undef HAVE_CURSES_H
|
||||
|
||||
/* Define to 1 if getaddrinfo is supported */
|
||||
#undef HAVE_GETADDRINFO
|
||||
|
||||
/* Define to 1 if gethostbyname_r is supported */
|
||||
#undef HAVE_GETHOSTBYNAME_R
|
||||
|
||||
/* Define to 1 if gethostbyname_r takes 3 arguments */
|
||||
#undef HAVE_GETHOSTBYNAME_R_3
|
||||
|
||||
/* Define to 1 if gethostbyname_r takes 5 arguments */
|
||||
#undef HAVE_GETHOSTBYNAME_R_5
|
||||
|
||||
@@ -41,6 +52,9 @@
|
||||
/* Define to 1 if you have the <inttypes.h> header file. */
|
||||
#undef HAVE_INTTYPES_H
|
||||
|
||||
/* Define to 1 to use GnuTLS library for TLS/SSL-support. */
|
||||
#undef HAVE_LIBGNUTLS
|
||||
|
||||
/* Define to 1 if you have the <memory.h> header file. */
|
||||
#undef HAVE_MEMORY_H
|
||||
|
||||
@@ -50,8 +64,20 @@
|
||||
/* Define to 1 if you have the <ncurses/ncurses.h> header file. */
|
||||
#undef HAVE_NCURSES_NCURSES_H
|
||||
|
||||
/* Define to 1 to use pragma pack directive in MessageBase.h */
|
||||
#undef HAVE_PRAGMA_PACK
|
||||
/* Define to 1 to use OpenSSL library for TLS/SSL-support. */
|
||||
#undef HAVE_OPENSSL
|
||||
|
||||
/* Define to 1 if libpar2 supports cancelling (needs a special patch) */
|
||||
#undef HAVE_PAR2_CANCEL
|
||||
|
||||
/* Define to 1 if you have the <regex.h> header file. */
|
||||
#undef HAVE_REGEX_H
|
||||
|
||||
/* Define to 1 if spinlocks are supported */
|
||||
#undef HAVE_SPINLOCK
|
||||
|
||||
/* Define to 1 if stat64 is supported */
|
||||
#undef HAVE_STAT64
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#undef HAVE_STDINT_H
|
||||
@@ -65,6 +91,9 @@
|
||||
/* Define to 1 if you have the <string.h> header file. */
|
||||
#undef HAVE_STRING_H
|
||||
|
||||
/* Define to 1 if you have the <sys/prctl.h> header file. */
|
||||
#undef HAVE_SYS_PRCTL_H
|
||||
|
||||
/* Define to 1 if you have the <sys/stat.h> header file. */
|
||||
#undef HAVE_SYS_STAT_H
|
||||
|
||||
@@ -95,6 +124,12 @@
|
||||
/* 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
|
||||
|
||||
|
||||
604
configure.ac
604
configure.ac
@@ -2,41 +2,18 @@
|
||||
# Process this file with autoconf to produce a configure script.
|
||||
|
||||
AC_PREREQ(2.59)
|
||||
AC_INIT(nzbget, 0.3.0, hugbug@users.sourceforge.net)
|
||||
AM_INIT_AUTOMAKE(nzbget, 0.3.0)
|
||||
AC_INIT(nzbget, 9.0, hugbug@users.sourceforge.net)
|
||||
AC_CANONICAL_SYSTEM
|
||||
AM_INIT_AUTOMAKE(nzbget, 9.0)
|
||||
AC_CONFIG_SRCDIR([nzbget.cpp])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
|
||||
|
||||
dnl Architecture check
|
||||
AC_CANONICAL_HOST
|
||||
case "$host" in
|
||||
*86-*-linux*)
|
||||
LIBPREF1="/usr"
|
||||
CFLAGS1="${CFLAGS} -m486"
|
||||
CPPFLAGS1="${CPPFLAGS} -D_GNU_SOURCE"
|
||||
;;
|
||||
*-linux*)
|
||||
LIBPREF1="/usr"
|
||||
CPPFLAGS1="${CPPFLAGS} -D_GNU_SOURCE"
|
||||
;;
|
||||
*-freebsd*)
|
||||
LIBPREF1="/usr/local"
|
||||
;;
|
||||
*-solaris*)
|
||||
LIBPREF1="/usr"
|
||||
;;
|
||||
esac
|
||||
|
||||
|
||||
dnl
|
||||
dnl Set default library path, if not specified in environment variable "LIBPREF".
|
||||
dnl
|
||||
if test "$LIBPREF" = ""; then
|
||||
LIBPREF="$LIBPREF1"
|
||||
fi
|
||||
if test "$CFLAGS" = ""; then
|
||||
CFLAGS="$CFLAGS1"
|
||||
fi
|
||||
if test "$CPPFLAGS" = ""; then
|
||||
CPPFLAGS="$CPPFLAGS1"
|
||||
LIBPREF="/usr"
|
||||
fi
|
||||
|
||||
|
||||
@@ -44,30 +21,23 @@ dnl
|
||||
dnl Check for programs.
|
||||
dnl
|
||||
AC_PROG_CXX
|
||||
AC_PROG_CC
|
||||
AC_PROG_GCC_TRADITIONAL
|
||||
AC_PROG_RANLIB
|
||||
AC_PROG_MAKE_SET
|
||||
AC_PATH_PROG(FALSE, false, /usr/bin/false)
|
||||
AC_PATH_PROG(TRUE, true, /usr/bin/true)
|
||||
AC_PATH_PROG(RM, rm, $FALSE)
|
||||
AC_PATH_PROG(LN, ln, $FALSE)
|
||||
AC_PATH_PROG(TAR, tar, $FALSE)
|
||||
AC_PATH_PROG(AR, ar, $FALSE)
|
||||
AC_PATH_PROG(MAKE, make, $FALSE)
|
||||
AC_PATH_PROG(CXXCPP, cpp, $FALSE)
|
||||
AC_PATH_PROG(MV, mv, $FALSE)
|
||||
AC_PATH_PROG(MKDIR, mkdir, $FALSE)
|
||||
AC_PATH_PROG(CP, cp, $FALSE)
|
||||
AC_PROG_INSTALL
|
||||
|
||||
|
||||
dnl
|
||||
dnl Do all tests with c++ compiler.
|
||||
dnl
|
||||
AC_LANG(C++)
|
||||
|
||||
|
||||
dnl
|
||||
dnl Checks for header files.
|
||||
dnl
|
||||
dnl AC_CHECK_HEADERS(stdarg.h time.h stdlib.h stdio.h unistd.h errno.h string.h sys/stat.h sys/time.h)
|
||||
dnl AC_CHECK_HEADERS(libgen.h pwd.h getopt.h dirent.h fcntl.h pthread.h semaphore.h)
|
||||
dnl AC_CHECK_HEADERS(sys/socket.h sys/types.h netinet/in.h arpa/inet.h netdb.h)
|
||||
AC_CHECK_HEADERS(sys/prctl.h)
|
||||
AC_CHECK_HEADERS(regex.h)
|
||||
|
||||
|
||||
dnl
|
||||
dnl Check for libs
|
||||
@@ -75,7 +45,6 @@ dnl
|
||||
AC_SEARCH_LIBS([pthread_create], [pthread])
|
||||
AC_SEARCH_LIBS([socket], [socket])
|
||||
AC_SEARCH_LIBS([inet_addr], [nsl])
|
||||
AC_SEARCH_LIBS([gethostbyname_r], [nsl])
|
||||
AC_SEARCH_LIBS([hstrerror], [resolv])
|
||||
|
||||
|
||||
@@ -83,8 +52,14 @@ dnl
|
||||
dnl Getopt
|
||||
dnl
|
||||
AC_CHECK_FUNC(getopt_long,
|
||||
[AC_DEFINE([HAVE_GETOPT_LONG], 1, [Define to 1 if getopt_long is supported])],
|
||||
[AC_LIBOBJ(getopt) AC_LIBOBJ(getopt1)])
|
||||
[AC_DEFINE([HAVE_GETOPT_LONG], 1, [Define to 1 if getopt_long is supported])],)
|
||||
|
||||
|
||||
dnl
|
||||
dnl stat64
|
||||
dnl
|
||||
AC_CHECK_FUNC(stat64,
|
||||
[AC_DEFINE([HAVE_STAT64], 1, [Define to 1 if stat64 is supported])],)
|
||||
|
||||
|
||||
dnl
|
||||
@@ -95,6 +70,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 +84,127 @@ AC_TRY_COMPILE(
|
||||
fi
|
||||
if test "$FOUND" = "no"; then
|
||||
AC_MSG_RESULT([no])
|
||||
AC_MSG_ERROR("function ctime_r not found.")
|
||||
AC_MSG_ERROR("function ctime_r not found")
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl check gethostbyname_r
|
||||
dnl check getaddrinfo
|
||||
dnl
|
||||
AC_MSG_CHECKING(for gethostbyname_r)
|
||||
AC_TRY_COMPILE(
|
||||
[#include <netdb.h>],
|
||||
[ char* szHost; struct hostent hinfobuf; char* strbuf; int h_errnop;
|
||||
struct hostent* hinfo = gethostbyname_r(szHost, &hinfobuf, strbuf, 1024, &h_errnop); ],
|
||||
AC_MSG_RESULT([[yes, and it takes 5 arguments]])
|
||||
FOUND="yes"
|
||||
AC_DEFINE([HAVE_GETHOSTBYNAME_R_5], 1, [Define to 1 if gethostbyname_r takes 5 arguments]),
|
||||
FOUND="no")
|
||||
if test "$FOUND" = "no"; then
|
||||
AC_TRY_COMPILE(
|
||||
[#include <netdb.h>],
|
||||
[ char* szHost; struct hostent* hinfo; struct hostent hinfobuf; char* strbuf; int h_errnop;
|
||||
int err = gethostbyname_r(szHost, &hinfobuf, strbuf, 1024, &hinfo, &h_errnop); ],
|
||||
AC_MSG_RESULT([[yes, and it takes 6 arguments]])
|
||||
AC_CHECK_FUNC(getaddrinfo,
|
||||
FOUND="yes"
|
||||
AC_DEFINE([HAVE_GETHOSTBYNAME_R_6], 1, [Define to 1 if gethostbyname_r takes 6 arguments]),
|
||||
[AC_DEFINE([HAVE_GETADDRINFO], 1, [Define to 1 if getaddrinfo is supported])]
|
||||
AC_SEARCH_LIBS([getaddrinfo], [nsl]),
|
||||
FOUND="no")
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl check gethostbyname_r, if getaddrinfo is not available
|
||||
dnl
|
||||
if test "$FOUND" = "no"; then
|
||||
AC_MSG_RESULT([no])
|
||||
AC_MSG_ERROR("function gethostbyname_r not found.")
|
||||
AC_MSG_CHECKING(for gethostbyname_r)
|
||||
|
||||
AC_TRY_COMPILE(
|
||||
[#include <netdb.h>],
|
||||
[ char* szHost; struct hostent hinfobuf; char* strbuf; int h_errnop;
|
||||
struct hostent* hinfo = gethostbyname_r(szHost, &hinfobuf, strbuf, 1024, &h_errnop); ],
|
||||
AC_MSG_RESULT([[yes, and it takes 5 arguments]])
|
||||
FOUND="yes"
|
||||
AC_DEFINE([HAVE_GETHOSTBYNAME_R_5], 1, [Define to 1 if gethostbyname_r takes 5 arguments]),
|
||||
FOUND="no")
|
||||
|
||||
if test "$FOUND" = "no"; then
|
||||
AC_TRY_COMPILE(
|
||||
[#include <netdb.h>],
|
||||
[ char* szHost; struct hostent* hinfo; struct hostent hinfobuf; char* strbuf; int h_errnop;
|
||||
int err = gethostbyname_r(szHost, &hinfobuf, strbuf, 1024, &hinfo, &h_errnop); ],
|
||||
AC_MSG_RESULT([[yes, and it takes 6 arguments]])
|
||||
FOUND="yes"
|
||||
AC_DEFINE([HAVE_GETHOSTBYNAME_R_6], 1, [Define to 1 if gethostbyname_r takes 6 arguments]),
|
||||
FOUND="no")
|
||||
fi
|
||||
|
||||
if test "$FOUND" = "no"; then
|
||||
AC_TRY_COMPILE(
|
||||
[#include <netdb.h>],
|
||||
[ char* szHost; struct hostent hinfo; struct hostent_data hinfobuf;
|
||||
int err = gethostbyname_r(szHost, &hinfo, &hinfobuf); ],
|
||||
AC_MSG_RESULT([[yes, and it takes 3 arguments]])
|
||||
FOUND="yes"
|
||||
AC_DEFINE([HAVE_GETHOSTBYNAME_R_3], 1, [Define to 1 if gethostbyname_r takes 3 arguments]),
|
||||
AC_MSG_RESULT([[no]])
|
||||
FOUND="no")
|
||||
fi
|
||||
|
||||
if test "$FOUND" = "yes"; then
|
||||
AC_DEFINE([HAVE_GETHOSTBYNAME_R], 1, [Define to 1 if gethostbyname_r is supported])
|
||||
AC_SEARCH_LIBS([gethostbyname_r], [nsl])
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl check for __FUNCTION__ or __func__ macro
|
||||
dnl cCheck if spinlocks are available
|
||||
dnl
|
||||
AC_MSG_CHECKING(for __FUNCTION__ macro)
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_TRY_COMPILE([#include <stdio.h>], [printf("%s\n", __FUNCTION__);],
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_DEFINE([FUNCTION_MACRO_NAME],[__FUNCTION__],[Define to the name of macro which returns the name of funtion being compiled])
|
||||
HAVE_FUNCTION_MACRO=yes,
|
||||
AC_MSG_RESULT([no]))
|
||||
AC_LANG_POP(C++)
|
||||
if test "$HAVE_FUNCTION_MACRO" != "yes"; then
|
||||
AC_MSG_CHECKING(for __func__ macro)
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_TRY_COMPILE([#include <stdio.h>], [printf("%s\n", __func__);],
|
||||
AC_MSG_RESULT([yes])
|
||||
AC_DEFINE([FUNCTION_MACRO_NAME],[__func__],[Define to the name of macro which returns the name of function being compiled])
|
||||
HAVE_FUNCTION_MACRO=yes,
|
||||
AC_MSG_RESULT([no]))
|
||||
AC_LANG_POP(C++)
|
||||
fi
|
||||
if test "$HAVE_FUNCTION_MACRO" != "yes"; then
|
||||
AC_DEFINE([FUNCTION_MACRO_NAME],[NULL],[Define to the name of macro which returns the name of function being compiled])
|
||||
fi
|
||||
AC_CHECK_FUNC(pthread_spin_init,
|
||||
[AC_DEFINE([HAVE_SPINLOCK], 1, [Define to 1 if spinlocks are supported])]
|
||||
AC_SEARCH_LIBS([pthread_spin_init], [pthread]),)
|
||||
|
||||
|
||||
dnl
|
||||
dnl check for pragma pack
|
||||
dnl Determine what socket length (socklen_t) data type is
|
||||
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 type of socket length (socklen_t)])
|
||||
AC_TRY_COMPILE([
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>],[
|
||||
(void)getsockopt (1, 1, 1, NULL, (socklen_t*)NULL)],[
|
||||
AC_MSG_RESULT(socklen_t)
|
||||
SOCKLEN_T=socklen_t],[
|
||||
AC_TRY_COMPILE([
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>],[
|
||||
(void)getsockopt (1, 1, 1, NULL, (size_t*)NULL)],[
|
||||
AC_MSG_RESULT(size_t)
|
||||
SOCKLEN_T=size_t],[
|
||||
AC_TRY_COMPILE([
|
||||
#include <stddef.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>],[
|
||||
(void)getsockopt (1, 1, 1, NULL, (int*)NULL)],[
|
||||
AC_MSG_RESULT(int)
|
||||
SOCKLEN_T=int],[
|
||||
AC_MSG_WARN(could not determine)
|
||||
SOCKLEN_T=int])])])
|
||||
AC_DEFINE_UNQUOTED(SOCKLEN_T, $SOCKLEN_T, [Determine what socket length (socklen_t) data type is])
|
||||
|
||||
|
||||
dnl
|
||||
dnl checks for libxml2 includes and libraries.
|
||||
dnl
|
||||
INCVAL="${LIBPREF}/include/libxml2"
|
||||
LIBVAL="${LIBPREF}/lib"
|
||||
dnl
|
||||
AC_ARG_WITH(libxml2_includes,
|
||||
[ --with-libxml2-includes=DIR libxml2 include directory],
|
||||
[INCVAL="$withval"])
|
||||
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
|
||||
CFLAGS="${CFLAGS} -I${INCVAL}"
|
||||
AC_CHECK_HEADER(libxml/tree.h,,
|
||||
AC_MSG_ERROR("libxml2 header files were not found."))
|
||||
[AS_HELP_STRING([--with-libxml2-includes=DIR], [libxml2 include directory])],
|
||||
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
|
||||
[CFLAGS="${CFLAGS} -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,
|
||||
[LDFLAGS="${LDFLAGS} $libxml2_LIBS"]
|
||||
[CPPFLAGS="${CPPFLAGS} $libxml2_CFLAGS"]
|
||||
[CFLAGS="${CFLAGS} $libxml2_CFLAGS"])
|
||||
fi
|
||||
AC_CHECK_HEADER(libxml/tree.h,,
|
||||
AC_MSG_ERROR("libxml2 header files not found"))
|
||||
AC_SEARCH_LIBS([xmlNewNode], [xml2], ,
|
||||
AC_MSG_ERROR("libxml2 library not found in $LIBVAL."))
|
||||
AC_MSG_ERROR("libxml2 library not found"))
|
||||
|
||||
|
||||
dnl
|
||||
@@ -202,20 +212,20 @@ 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 +246,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,47 +256,11 @@ 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 libpar2 for par-checking. Deafult: no
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to include code for par-checking)
|
||||
AC_ARG_ENABLE(parcheck,
|
||||
[ --enable-parcheck include code for par-checking],
|
||||
[AS_HELP_STRING([--disable-parcheck], [do not include par-check/-repair-support (removes dependency from libpar2- and libsigc-libraries)])],
|
||||
[ ENABLEPARCHECK=$enableval ],
|
||||
[ ENABLEPARCHECK=yes] )
|
||||
AC_MSG_RESULT($ENABLEPARCHECK)
|
||||
@@ -295,23 +269,26 @@ if test "$ENABLEPARCHECK" = "yes"; then
|
||||
dnl
|
||||
dnl checks for libsigc++ includes and libraries (required for libpar2).
|
||||
dnl
|
||||
INCVAL="${LIBPREF}/include/sigc++-2.0"
|
||||
LIBVAL="${LIBPREF}/lib"
|
||||
|
||||
AC_ARG_WITH(libsigc_includes,
|
||||
[ --with-libsigc-includes=DIR libsigc++-2.0 include directory],
|
||||
[INCVAL="$withval"])
|
||||
|
||||
[AS_HELP_STRING([--with-libsigc-includes=DIR], [libsigc++-2.0 include directory])],
|
||||
[CPPFLAGS="${CPPFLAGS} -I${withval}"]
|
||||
[INCVAL="yes"],
|
||||
[INCVAL="no"])
|
||||
AC_ARG_WITH(libsigc_libraries,
|
||||
[ --with-libsigc-libraries=DIR libsigc++-2.0 library directory],
|
||||
[LIBVAL="$withval"])
|
||||
|
||||
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
|
||||
CPPFLAGS="${CPPFLAGS} -I${INCVAL} -I${LIBVAL}/sigc++-2.0/include"
|
||||
|
||||
AC_LANG_PUSH(C++)
|
||||
[AS_HELP_STRING([--with-libsigc-libraries=DIR], [libsigc++-2.0 library directory])],
|
||||
[LDFLAGS="${LDFLAGS} -L${withval}"]
|
||||
[CPPFLAGS="${CPPFLAGS} -I${withval}/sigc++-2.0/include"]
|
||||
[LIBVAL="yes"],
|
||||
[LIBVAL="no"])
|
||||
if test "$INCVAL" = "no" -o "$LIBVAL" = "no"; then
|
||||
PKG_CHECK_MODULES(libsigc, sigc++-2.0,
|
||||
[LDFLAGS="${LDFLAGS} $libsigc_LIBS"]
|
||||
[CPPFLAGS="${CPPFLAGS} $libsigc_CFLAGS"])
|
||||
fi
|
||||
|
||||
AC_CHECK_HEADER(sigc++/type_traits.h,,
|
||||
AC_MSG_ERROR("libsigc++-2.0 header files were not found in $INCVAL."))
|
||||
AC_LANG_POP(C++)
|
||||
AC_MSG_ERROR("libsigc++-2.0 header files not found"))
|
||||
|
||||
dnl
|
||||
dnl checks for libpar2 includes and libraries.
|
||||
@@ -319,67 +296,255 @@ if test "$ENABLEPARCHECK" = "yes"; then
|
||||
INCVAL="${LIBPREF}/include"
|
||||
LIBVAL="${LIBPREF}/lib"
|
||||
AC_ARG_WITH(libpar2_includes,
|
||||
[ --with-libpar2-includes=DIR libpar2 include directory],
|
||||
[AS_HELP_STRING([--with-libpar2-includes=DIR], [libpar2 include directory])],
|
||||
[INCVAL="$withval"])
|
||||
|
||||
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
|
||||
|
||||
AC_LANG_PUSH(C++)
|
||||
AC_CHECK_HEADER(libpar2/libpar2.h,,
|
||||
AC_MSG_ERROR("libpar2 header files were not found in $INCVAL."))
|
||||
AC_LANG_POP(C++)
|
||||
AC_MSG_ERROR("libpar2 header files not found"))
|
||||
|
||||
AC_ARG_WITH(libpar2_libraries,
|
||||
[ --with-libpar2-libraries=DIR libpar2 library directory],
|
||||
[AS_HELP_STRING([--with-libpar2-libraries=DIR], [libpar2 library directory])],
|
||||
[LIBVAL="$withval"])
|
||||
|
||||
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
|
||||
|
||||
dnl Q: How to check for c++-class in library?
|
||||
LIBS="${LIBS} -lpar2"
|
||||
dnl AC_CHECK_LIB(par2, GenerateCRC32Table, , FOUND=no)
|
||||
dnl if test "$FOUND" = "no"; then
|
||||
dnl AC_MSG_ERROR("libpar2 library not found in $LIBVAL.")
|
||||
dnl fi
|
||||
AC_SEARCH_LIBS([_ZN12Par2RepairerC1Ev], [par2], ,
|
||||
AC_MSG_ERROR("libpar2 library not found"))
|
||||
|
||||
dnl
|
||||
dnl Enable par-check-progress (via sigc++)? Deafult: yes
|
||||
dnl It doesn't work on WL500gP (uClibc), so we need a way to disable it
|
||||
dnl check if libpar2 library is linkable
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to show progress during par-check )
|
||||
AC_ARG_ENABLE(parprogress,
|
||||
[ --disable-parprogress do not show progress during par-check (it must be disabled if sigc++ doesn't work correctly)],
|
||||
[ USEPARPROGRESS=$enableval ],
|
||||
[ USEPARPROGRESS=yes] )
|
||||
AC_MSG_RESULT($USEPARPROGRESS)
|
||||
if test "$USEPARPROGRESS" = "yes"; then
|
||||
AC_DEFINE([ENABLE_PARPROGRESS],1,[Define to 1 to show progress during par-check (it must be disabled if sigc++ doesn't work correctly)])
|
||||
fi
|
||||
|
||||
AC_MSG_CHECKING(for libpar2 linking)
|
||||
AC_TRY_LINK(
|
||||
[#include <libpar2/par2cmdline.h>]
|
||||
[#include <libpar2/par2repairer.h>]
|
||||
[ class Repairer : public Par2Repairer { }; ],
|
||||
[ Repairer* p = new Repairer(); ],
|
||||
AC_MSG_RESULT([[yes]]),
|
||||
AC_MSG_RESULT([[no]])
|
||||
AC_MSG_ERROR("libpar2 library not found"))
|
||||
|
||||
dnl
|
||||
dnl check if libpar2 has support for cancelling
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether libpar2 supports cancelling)
|
||||
AC_TRY_COMPILE(
|
||||
[#include <libpar2/par2cmdline.h>]
|
||||
[#include <libpar2/par2repairer.h>]
|
||||
[ class Repairer : public Par2Repairer { void test() { cancelled = true; } }; ],
|
||||
[],
|
||||
AC_MSG_RESULT([[yes]])
|
||||
AC_DEFINE([HAVE_PAR2_CANCEL], 1, [Define to 1 if libpar2 supports cancelling (needs a special patch)]),
|
||||
AC_MSG_RESULT([[no]]))
|
||||
else
|
||||
AC_DEFINE([DISABLE_PARCHECK],1,[Define to 1 to disable smart par-verification and restoration])
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl Use TLS/SSL. Deafult: yes
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to use TLS/SSL)
|
||||
AC_ARG_ENABLE(tls,
|
||||
[AS_HELP_STRING([--disable-tls], [do not use TLS/SSL (removes dependency from TLS/SSL-libraries)])],
|
||||
[ USETLS=$enableval ],
|
||||
[ USETLS=yes] )
|
||||
AC_MSG_RESULT($USETLS)
|
||||
if test "$USETLS" = "yes"; then
|
||||
AC_ARG_WITH(tlslib,
|
||||
[AS_HELP_STRING([--with-tlslib=(GnuTLS, OpenSSL)], [TLS/SSL library to use])],
|
||||
[TLSLIB="$withval"])
|
||||
if test "$TLSLIB" != "GnuTLS" -a "$TLSLIB" != "OpenSSL" -a "$TLSLIB" != ""; then
|
||||
AC_MSG_ERROR([Invalid argument for option --with-tlslib])
|
||||
fi
|
||||
|
||||
if test "$TLSLIB" = "GnuTLS" -o "$TLSLIB" = ""; then
|
||||
INCVAL="${LIBPREF}/include"
|
||||
LIBVAL="${LIBPREF}/lib"
|
||||
AC_ARG_WITH(libgnutls_includes,
|
||||
[AS_HELP_STRING([--with-libgnutls-includes=DIR], [GnuTLS include directory])],
|
||||
[INCVAL="$withval"])
|
||||
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
|
||||
CFLAGS="${CFLAGS} -I${INCVAL}"
|
||||
AC_ARG_WITH(libgnutls_libraries,
|
||||
[AS_HELP_STRING([--with-libgnutls-libraries=DIR], [GnuTLS library directory])],
|
||||
[LIBVAL="$withval"])
|
||||
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
|
||||
|
||||
AC_CHECK_HEADER(gnutls/gnutls.h,
|
||||
FOUND=yes
|
||||
TLSHEADERS=yes,
|
||||
FOUND=no)
|
||||
if test "$FOUND" = "no" -a "$TLSLIB" = "GnuTLS"; then
|
||||
AC_MSG_ERROR([Couldn't find GnuTLS headers (gnutls.h)])
|
||||
fi
|
||||
if test "$FOUND" = "yes"; then
|
||||
AC_SEARCH_LIBS([gnutls_global_init], [gnutls],
|
||||
AC_SEARCH_LIBS([gcry_control], [gnutls gcrypt],
|
||||
FOUND=yes,
|
||||
FOUND=no),
|
||||
FOUND=no)
|
||||
if test "$FOUND" = "no" -a "$TLSLIB" = "GnuTLS"; then
|
||||
AC_MSG_ERROR([Couldn't find GnuTLS library])
|
||||
fi
|
||||
if test "$FOUND" = "yes"; then
|
||||
TLSLIB="GnuTLS"
|
||||
AC_DEFINE([HAVE_LIBGNUTLS],1,[Define to 1 to use GnuTLS library for TLS/SSL-support.])
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$TLSLIB" = "OpenSSL" -o "$TLSLIB" = ""; then
|
||||
INCVAL="${LIBPREF}/include"
|
||||
LIBVAL="${LIBPREF}/lib"
|
||||
AC_ARG_WITH(openssl_includes,
|
||||
[AS_HELP_STRING([--with-openssl-includes=DIR], [OpenSSL include directory])],
|
||||
[INCVAL="$withval"])
|
||||
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
|
||||
CFLAGS="${CFLAGS} -I${INCVAL}"
|
||||
AC_ARG_WITH(openssl_libraries,
|
||||
[AS_HELP_STRING([--with-openssl-libraries=DIR], [OpenSSL library directory])],
|
||||
[LIBVAL="$withval"])
|
||||
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
|
||||
|
||||
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([SSL_library_init], [ssl],
|
||||
FOUND=yes,
|
||||
FOUND=no)
|
||||
if test "$FOUND" = "no" -a "$TLSLIB" = "OpenSSL"; then
|
||||
AC_MSG_ERROR([Couldn't find OpenSSL library])
|
||||
fi
|
||||
if test "$FOUND" = "yes"; then
|
||||
TLSLIB="OpenSSL"
|
||||
AC_DEFINE([HAVE_OPENSSL],1,[Define to 1 to use OpenSSL library for TLS/SSL-support.])
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
if test "$TLSLIB" = ""; then
|
||||
if test "$TLSHEADERS" = ""; then
|
||||
AC_MSG_ERROR([Couldn't find neither GnuTLS nor OpenSSL headers (gnutls.h or ssl.h)])
|
||||
else
|
||||
AC_MSG_ERROR([Couldn't find neither GnuTLS nor OpenSSL library])
|
||||
fi
|
||||
fi
|
||||
else
|
||||
AC_DEFINE([DISABLE_TLS],1,[Define to 1 to not use TLS/SSL])
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl checks for zlib includes and libraries.
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to use gzip)
|
||||
AC_ARG_ENABLE(gzip,
|
||||
[AS_HELP_STRING([--disable-gzip], [disable gzip-compression/decompression (removes dependency from zlib-library)])],
|
||||
[USEZLIB=$enableval],
|
||||
[USEZLIB=yes] )
|
||||
AC_MSG_RESULT($USEZLIB)
|
||||
if test "$USEZLIB" = "yes"; then
|
||||
INCVAL="${LIBPREF}/include"
|
||||
LIBVAL="${LIBPREF}/lib"
|
||||
AC_ARG_WITH(zlib_includes,
|
||||
[AS_HELP_STRING([--with-zlib-includes=DIR], [zlib include directory])],
|
||||
[INCVAL="$withval"])
|
||||
CPPFLAGS="${CPPFLAGS} -I${INCVAL}"
|
||||
CFLAGS="${CFLAGS} -I${INCVAL}"
|
||||
AC_ARG_WITH(zlib_libraries,
|
||||
[AS_HELP_STRING([--with-zlib-libraries=DIR], [zlib library directory])],
|
||||
[LIBVAL="$withval"])
|
||||
LDFLAGS="${LDFLAGS} -L${LIBVAL}"
|
||||
|
||||
AC_CHECK_HEADER(zlib.h,,
|
||||
AC_MSG_ERROR("zlib header files not found"))
|
||||
AC_SEARCH_LIBS([deflateBound], [z], ,
|
||||
AC_MSG_ERROR("zlib library not found"))
|
||||
else
|
||||
AC_DEFINE([DISABLE_GZIP],1,[Define to 1 to disable gzip-support])
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl Some Linux systems require an empty signal handler for SIGCHLD
|
||||
dnl in order for exit codes to be correctly delivered to parent process.
|
||||
dnl Some BSD systems however may not function properly if the handler is installed.
|
||||
dnl The default behavior is to check the target and disable the handler on BSD but keep it enabled on other systems.
|
||||
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 is recommended for BSD)])],
|
||||
[SIGCHLDHANDLER=$enableval],
|
||||
[SIGCHLDHANDLER=auto])
|
||||
if test "$SIGCHLDHANDLER" = "auto"; then
|
||||
SIGCHLDHANDLER=yes
|
||||
case "$target" in
|
||||
*bsd*)
|
||||
SIGCHLDHANDLER=no
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
AC_MSG_RESULT($SIGCHLDHANDLER)
|
||||
if test "$SIGCHLDHANDLER" = "yes"; then
|
||||
AC_DEFINE([SIGCHLD_HANDLER], 1, [Define to 1 to install an empty signal handler for SIGCHLD])
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl Debugging. Default: no
|
||||
dnl
|
||||
AC_MSG_CHECKING(whether to include all debugging code)
|
||||
AC_ARG_ENABLE(debug,
|
||||
[ --enable-debug enable debugging],
|
||||
[AS_HELP_STRING([--enable-debug], [enable debugging])],
|
||||
[ ENABLEDEBUG=$enableval ],
|
||||
[ ENABLEDEBUG=no] )
|
||||
if test "$ENABLEDEBUG" = "yes"; then
|
||||
AC_DEFINE([DEBUG],1,Define to 1 to include debug-code)
|
||||
if test "$CC" = "gcc"; then
|
||||
CXXFLAGS="-g -Wall"
|
||||
else
|
||||
CXXFLAGS=""
|
||||
fi
|
||||
fi
|
||||
AC_MSG_RESULT($ENABLEDEBUG)
|
||||
|
||||
|
||||
if test "$ENABLEDEBUG" = "yes"; then
|
||||
|
||||
dnl
|
||||
dnl Begin of debugging code
|
||||
dnl
|
||||
|
||||
AC_DEFINE([DEBUG],1,Define to 1 to include debug-code)
|
||||
|
||||
|
||||
dnl
|
||||
dnl Set debug flags for gcc (if gcc is used)
|
||||
dnl
|
||||
if test "$CC" = "gcc"; then
|
||||
CXXFLAGS="-g -Wall"
|
||||
else
|
||||
CXXFLAGS=""
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl check for __FUNCTION__ or __func__ macro
|
||||
dnl
|
||||
AC_MSG_CHECKING(for macro returning current function name)
|
||||
AC_TRY_COMPILE(
|
||||
[#include <stdio.h>], [printf("%s\n", __FUNCTION__);],
|
||||
AC_MSG_RESULT(__FUNCTION__)
|
||||
FUNCTION_MACRO_NAME=__FUNCTION__,
|
||||
AC_TRY_COMPILE([#include <stdio.h>], [printf("%s\n", __func__);],
|
||||
AC_MSG_RESULT(__func__)
|
||||
FUNCTION_MACRO_NAME=__func__,
|
||||
AC_MSG_RESULT(none)))
|
||||
if test "$FUNCTION_MACRO_NAME" != ""; then
|
||||
AC_DEFINE_UNQUOTED(FUNCTION_MACRO_NAME, $FUNCTION_MACRO_NAME, [Define to the name of macro which returns the name of function being compiled])
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl variadic macros
|
||||
dnl
|
||||
@@ -394,6 +559,41 @@ AC_COMPILE_IFELSE([
|
||||
AC_MSG_RESULT([no]))
|
||||
|
||||
|
||||
dnl
|
||||
dnl Backtracing on segmentation faults
|
||||
dnl
|
||||
AC_MSG_CHECKING(for backtrace)
|
||||
AC_TRY_COMPILE(
|
||||
[#include <execinfo.h>]
|
||||
[#include <stdio.h>]
|
||||
[#include <stdlib.h>],
|
||||
[ void *array[100]; size_t size; char **strings; ]
|
||||
[ size = backtrace(array, 100); ]
|
||||
[ strings = backtrace_symbols(array, size); ],
|
||||
FOUND=yes
|
||||
AC_MSG_RESULT([[yes]])
|
||||
AC_DEFINE([HAVE_BACKTRACE], 1, [Define to 1 to create stacktrace on segmentation faults]),
|
||||
FOUND=no
|
||||
AC_MSG_RESULT([[no]]))
|
||||
|
||||
|
||||
dnl
|
||||
dnl "rdynamic" linker flag
|
||||
dnl
|
||||
AC_MSG_CHECKING(for rdynamic linker flag)
|
||||
old_LDFLAGS="$LDFLAGS"
|
||||
LDFLAGS="$LDFLAGS -rdynamic"
|
||||
AC_TRY_LINK([], [],
|
||||
AC_MSG_RESULT([[yes]]),
|
||||
AC_MSG_RESULT([[no]])
|
||||
[LDFLAGS="$old_LDFLAGS"])
|
||||
|
||||
dnl
|
||||
dnl End of debugging code
|
||||
dnl
|
||||
fi
|
||||
|
||||
|
||||
dnl Substitute flags.
|
||||
AC_SUBST(CFLAGS)
|
||||
AC_SUBST(CPPFLAGS)
|
||||
|
||||
@@ -423,3 +423,32 @@ diff -urN libpar2-0.2-original/par2cmdline.h libpar2-0.2-modified/par2cmdline.h
|
||||
|
||||
#else // WIN32
|
||||
#ifdef HAVE_CONFIG_H
|
||||
diff -urN libpar2-0.2-original/par2repairer.cpp libpar2-0.2-modified/par2repairer.cpp
|
||||
--- libpar2-0.2-original/par2repairer.cpp 2006-01-20 18:25:20.000000000 +0100
|
||||
+++ libpar2-0.2-modified/par2repairer.cpp 2008-02-13 15:37:59.899314300 +0100
|
||||
@@ -78,6 +78,7 @@
|
||||
|
||||
delete mainpacket;
|
||||
delete creatorpacket;
|
||||
+ delete headers;
|
||||
}
|
||||
|
||||
|
||||
@@ -1261,7 +1262,7 @@
|
||||
DiskFile::SplitFilename(filename, path, name);
|
||||
|
||||
cout << "Target: \"" << name << "\" - missing." << endl;
|
||||
- sig_done.emit(name, 0, sourcefile->GetVerificationPacket()->BlockCount());
|
||||
+ sig_done.emit(name, 0, sourcefile && sourcefile->GetVerificationPacket() ? sourcefile->GetVerificationPacket()->BlockCount() : 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1804,7 +1805,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
- sig_done.emit(name,count,sourcefile->GetVerificationPacket()->BlockCount());
|
||||
+ sig_done.emit(name,count, sourcefile && sourcefile->GetVerificationPacket() ? sourcefile->GetVerificationPacket()->BlockCount() : 0);
|
||||
sig_progress.emit(1000.0);
|
||||
return true;
|
||||
}
|
||||
|
||||
29
libpar2-0.2-bugfixes.patch
Normal file
29
libpar2-0.2-bugfixes.patch
Normal file
@@ -0,0 +1,29 @@
|
||||
diff -aud ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repairer.cpp
|
||||
--- ../libpar2-0.2-original/par2repairer.cpp 2006-01-20 18:25:20.000000000 +0100
|
||||
+++ ../libpar2-0.2/par2repairer.cpp 2008-02-06 12:02:53.226050300 +0100
|
||||
@@ -78,6 +78,7 @@
|
||||
|
||||
delete mainpacket;
|
||||
delete creatorpacket;
|
||||
+ delete headers;
|
||||
}
|
||||
|
||||
|
||||
@@ -1261,7 +1262,7 @@
|
||||
DiskFile::SplitFilename(filename, path, name);
|
||||
|
||||
cout << "Target: \"" << name << "\" - missing." << endl;
|
||||
- sig_done.emit(name, 0, sourcefile->GetVerificationPacket()->BlockCount());
|
||||
+ sig_done.emit(name, 0, sourcefile->GetVerificationPacket() ? sourcefile->GetVerificationPacket()->BlockCount() : 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1804,7 +1805,7 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
- sig_done.emit(name,count,sourcefile->GetVerificationPacket()->BlockCount());
|
||||
+ sig_done.emit(name,count, sourcefile->GetVerificationPacket() ? sourcefile->GetVerificationPacket()->BlockCount() : 0);
|
||||
sig_progress.emit(1000.0);
|
||||
return true;
|
||||
}
|
||||
187
libpar2-0.2-cancel.patch
Normal file
187
libpar2-0.2-cancel.patch
Normal file
@@ -0,0 +1,187 @@
|
||||
diff -aud ../libpar2-0.2-original/par2repairer.cpp ../libpar2-0.2/par2repairer.cpp
|
||||
--- ../libpar2-0.2-original/par2repairer.cpp 2008-10-26 19:54:33.000000000 +0100
|
||||
+++ ../libpar2-0.2/par2repairer.cpp 2008-10-29 10:24:48.000000000 +0100
|
||||
@@ -52,6 +52,8 @@
|
||||
noiselevel = CommandLine::nlNormal;
|
||||
headers = new ParHeaders;
|
||||
alreadyloaded = false;
|
||||
+
|
||||
+ cancelled = false;
|
||||
}
|
||||
|
||||
Par2Repairer::~Par2Repairer(void)
|
||||
@@ -406,6 +408,10 @@
|
||||
progress = offset;
|
||||
sig_progress.emit(newfraction);
|
||||
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ break;
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -584,6 +590,11 @@
|
||||
delete diskfile;
|
||||
}
|
||||
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -833,9 +844,17 @@
|
||||
for (list<string>::const_iterator s=files->begin(); s!=files->end(); ++s)
|
||||
{
|
||||
LoadPacketsFromFile(*s);
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ break;
|
||||
+ }
|
||||
}
|
||||
|
||||
delete files;
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ return false;
|
||||
+ }
|
||||
}
|
||||
|
||||
{
|
||||
@@ -846,9 +865,17 @@
|
||||
for (list<string>::const_iterator s=files->begin(); s!=files->end(); ++s)
|
||||
{
|
||||
LoadPacketsFromFile(*s);
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ break;
|
||||
+ }
|
||||
}
|
||||
|
||||
delete files;
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ return false;
|
||||
+ }
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -866,9 +893,18 @@
|
||||
string::npos != filename.find(".PAR2"))
|
||||
{
|
||||
LoadPacketsFromFile(filename);
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ break;
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1210,6 +1246,11 @@
|
||||
sf = sortedfiles.begin();
|
||||
while (sf != sortedfiles.end())
|
||||
{
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
// Do we have a source file
|
||||
Par2RepairerSourceFile *sourcefile = *sf;
|
||||
|
||||
@@ -1562,6 +1603,10 @@
|
||||
cout << "Scanning: \"" << shortname << "\": " << newfraction/10 << '.' << newfraction%10 << "%\r" << flush;
|
||||
sig_progress.emit(newfraction);
|
||||
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ break;
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1651,6 +1696,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
// Get the Full and 16k hash values of the file
|
||||
filechecksummer.GetFileHashes(hashfull, hash16k);
|
||||
|
||||
@@ -2291,10 +2341,19 @@
|
||||
cout << "Repairing: " << newfraction/10 << '.' << newfraction%10 << "%\r" << flush;
|
||||
sig_progress.emit(newfraction);
|
||||
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ break;
|
||||
+ }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
++inputblock;
|
||||
++inputindex;
|
||||
}
|
||||
@@ -2348,9 +2407,18 @@
|
||||
cout << "Processing: " << newfraction/10 << '.' << newfraction%10 << "%\r" << flush;
|
||||
sig_progress.emit(newfraction);
|
||||
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ break;
|
||||
+ }
|
||||
}
|
||||
}
|
||||
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
++copyblock;
|
||||
++inputblock;
|
||||
}
|
||||
@@ -2362,6 +2430,11 @@
|
||||
lastopenfile->Close();
|
||||
}
|
||||
|
||||
+ if (cancelled)
|
||||
+ {
|
||||
+ return false;
|
||||
+ }
|
||||
+
|
||||
if (noiselevel > CommandLine::nlQuiet)
|
||||
cout << "Writing recovered data\r";
|
||||
|
||||
diff -aud ../libpar2-0.2-original/par2repairer.h ../libpar2-0.2/par2repairer.h
|
||||
--- ../libpar2-0.2-original/par2repairer.h 2006-01-20 00:38:27.000000000 +0100
|
||||
+++ ../libpar2-0.2/par2repairer.h 2008-10-26 19:01:08.000000000 +0100
|
||||
@@ -183,6 +183,7 @@
|
||||
u64 totaldata; // Total amount of data to be processed.
|
||||
u64 totalsize; // Total data size
|
||||
|
||||
+ bool cancelled; // repair cancelled
|
||||
};
|
||||
|
||||
#endif // __PAR2REPAIRER_H__
|
||||
66
nzbget-postprocess.conf
Normal file
66
nzbget-postprocess.conf
Normal file
@@ -0,0 +1,66 @@
|
||||
#
|
||||
# This file if part of nzbget
|
||||
#
|
||||
# Template configuration file for postprocessing script "nzbget-postprocess.sh".
|
||||
# Please refer to "nzbget-postprocess.sh" for usage instructions.
|
||||
#
|
||||
# Copyright (C) 2008-2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
### PATHS ###
|
||||
|
||||
# Set the full path to unrar if it is not in your PATH.
|
||||
UnrarCmd=unrar
|
||||
|
||||
|
||||
##############################################################################
|
||||
### OPTIONS ###
|
||||
|
||||
# Delete rar-files after unpacking (yes, no).
|
||||
DeleteRarFiles=yes
|
||||
|
||||
# Rename img-files to iso (yes, no).
|
||||
RenameIMG=yes
|
||||
|
||||
# Joint TS-files (yes, no).
|
||||
JoinTS=no
|
||||
|
||||
##############################################################################
|
||||
### POSTPROCESSING-PARAMETERS ###
|
||||
|
||||
# This section defines parameters, which can be set for each nzb-file
|
||||
# individually using either web-interface or command line.
|
||||
# Example command line for setting parameter "password" to value "123" for
|
||||
# nzb-file with id=2:
|
||||
# nzbget -E G O Password=123 2
|
||||
|
||||
# Perform postprocessing (yes, no).
|
||||
#
|
||||
# Set to "no" to skip postprocessing for this nzb-file.
|
||||
PostProcess=yes
|
||||
|
||||
# Password for encrypted posts.
|
||||
#
|
||||
# If the post requires a password for unpacking.
|
||||
Password=
|
||||
|
||||
# Destination directory.
|
||||
#
|
||||
# NOTE: NZBGet must have write-access-rights for that directory.
|
||||
DestDir=
|
||||
321
nzbget-postprocess.sh
Executable file
321
nzbget-postprocess.sh
Executable file
@@ -0,0 +1,321 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# This file if part of nzbget
|
||||
#
|
||||
# Example postprocessing script for NZBGet
|
||||
#
|
||||
# Copyright (C) 2008 Peter Roubos <peterroubos@hotmail.com>
|
||||
# Copyright (C) 2008 Otmar Werner
|
||||
# Copyright (C) 2008-2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
####################### Usage instructions #######################
|
||||
# o Script will unrar downloaded rar files, join ts-files and rename img-files
|
||||
# to iso.
|
||||
#
|
||||
# o To use this script with nzbget set the option "PostProcess" in
|
||||
# nzbget configuration file to point to this script file. E.g.:
|
||||
# PostProcess=/home/user/nzbget/nzbget-postprocess.sh
|
||||
#
|
||||
# o The script needs a configuration file. An example configuration file
|
||||
# is provided in file "nzbget-postprocess.conf". Put the configuration file
|
||||
# into the directory where nzbget's configuration file (nzbget.conf) is located.
|
||||
# Then edit the configuration file in any text editor to adjust the settings.
|
||||
#
|
||||
# o You can also edit the script's configuration via web-interface.
|
||||
#
|
||||
# o There are few options, which can be ajdusted for each nzb-file individually.
|
||||
#
|
||||
# o The script supports the feature called "delayed par-check".
|
||||
# That means it can try to unpack downloaded files without par-checking
|
||||
# them fisrt. Only if unpack fails, the script schedules par-check,
|
||||
# then unpacks again.
|
||||
# To use delayed par-check set following options in nzbget configuration file:
|
||||
# ParCheck=no
|
||||
# ParRepair=yes
|
||||
# LoadPars=one (or) LoadPars=all
|
||||
#
|
||||
# o If you want to par-check/repair all files before trying to unpack them,
|
||||
# set option "ParCheck=yes".
|
||||
#
|
||||
####################### End of Usage instructions #######################
|
||||
|
||||
|
||||
# NZBGet passes following arguments to postprocess-programm as environment
|
||||
# variables:
|
||||
# NZBPP_DIRECTORY - path to destination dir for downloaded files;
|
||||
# NZBPP_NZBFILENAME - name of processed nzb-file;
|
||||
# NZBPP_PARFILENAME - name of par-file or empty string (if no collections were
|
||||
# found);
|
||||
# NZBPP_PARSTATUS - result of par-check:
|
||||
# 0 = not checked: par-check disabled or nzb-file does
|
||||
# not contain any par-files;
|
||||
# 1 = checked and failed to repair;
|
||||
# 2 = checked and successfully repaired;
|
||||
# 3 = checked and can be repaired but repair is disabled;
|
||||
# NZBPP_NZBCOMPLETED - state of nzb-job:
|
||||
# 0 = there are more collections in this nzb-file queued;
|
||||
# 1 = this was the last collection in nzb-file;
|
||||
# NZBPP_PARFAILED - indication of failed par-jobs for current nzb-file:
|
||||
# 0 = no failed par-jobs;
|
||||
# 1 = current par-job or any of the previous par-jobs for
|
||||
# the same nzb-files failed;
|
||||
# NZBPP_CATEGORY - category assigned to nzb-file (can be empty string).
|
||||
|
||||
|
||||
# Name of script's configuration file
|
||||
SCRIPT_CONFIG_FILE="nzbget-postprocess.conf"
|
||||
|
||||
# Exit codes
|
||||
POSTPROCESS_PARCHECK_CURRENT=91
|
||||
POSTPROCESS_PARCHECK_ALL=92
|
||||
POSTPROCESS_SUCCESS=93
|
||||
POSTPROCESS_ERROR=94
|
||||
POSTPROCESS_NONE=95
|
||||
|
||||
# Check if the script is called from nzbget
|
||||
if [ "$NZBPP_DIRECTORY" = "" -o "$NZBOP_CONFIGFILE" = "" ]; then
|
||||
echo "*** NZBGet post-process script ***"
|
||||
echo "This script is supposed to be called from nzbget (0.7.0 or later)."
|
||||
exit $POSTPROCESS_ERROR
|
||||
fi
|
||||
|
||||
# Check if postprocessing was disabled in postprocessing parameters
|
||||
# (for current nzb-file) via web-interface or via command line with
|
||||
# "nzbget -E G O PostProcess=no <ID>"
|
||||
if [ "$NZBPR_PostProcess" = "no" ]; then
|
||||
echo "[WARNING] Post-Process: Postprocessing disabled for this nzb-file, exiting"
|
||||
exit $POSTPROCESS_NONE
|
||||
fi
|
||||
|
||||
echo "[INFO] Post-Process: Post-process script successfully started"
|
||||
|
||||
# Determine the location of configuration file (it must be stored in
|
||||
# the directory with nzbget.conf or in this script's directory).
|
||||
ConfigDir="${NZBOP_CONFIGFILE%/*}"
|
||||
ScriptConfigFile="$ConfigDir/$SCRIPT_CONFIG_FILE"
|
||||
if [ ! -f "$ScriptConfigFile" ]; then
|
||||
ConfigDir="${0%/*}"
|
||||
ScriptConfigFile="$ConfigDir/$SCRIPT_CONFIG_FILE"
|
||||
fi
|
||||
if [ ! -f "$ScriptConfigFile" ]; then
|
||||
echo "[ERROR] Post-Process: Configuration file $ScriptConfigFile not found, exiting"
|
||||
exit $POSTPROCESS_ERROR
|
||||
fi
|
||||
|
||||
# Readg configuration file
|
||||
while read line; do eval "$line"; done < $ScriptConfigFile
|
||||
|
||||
# Check nzbget.conf options
|
||||
BadConfig=0
|
||||
|
||||
if [ "$NZBOP_ALLOWREPROCESS" = "yes" ]; then
|
||||
echo "[ERROR] Post-Process: Please disable option \"AllowReProcess\" in nzbget configuration file"
|
||||
BadConfig=1
|
||||
fi
|
||||
|
||||
if [ "$NZBOP_LOADPARS" = "none" ]; then
|
||||
echo "[ERROR] Post-Process: Please set option \"LoadPars\" to \"One\" or \"All\" in nzbget configuration file"
|
||||
BadConfig=1
|
||||
fi
|
||||
|
||||
if [ "$NZBOP_PARREPAIR" = "no" ]; then
|
||||
echo "[ERROR] Post-Process: Please set option \"ParRepair\" to \"Yes\" in nzbget configuration file"
|
||||
BadConfig=1
|
||||
fi
|
||||
|
||||
if [ "$BadConfig" -eq 1 ]; then
|
||||
echo "[ERROR] Post-Process: Exiting because of not compatible nzbget configuration"
|
||||
exit $POSTPROCESS_ERROR
|
||||
fi
|
||||
|
||||
# Check if all collections in nzb-file were downloaded
|
||||
if [ ! "$NZBPP_NZBCOMPLETED" -eq 1 ]; then
|
||||
echo "[INFO] Post-Process: Not the last collection in nzb-file, exiting"
|
||||
exit $POSTPROCESS_SUCCESS
|
||||
fi
|
||||
|
||||
# Check par status
|
||||
if [ "$NZBPP_PARSTATUS" -eq 1 -o "$NZBPP_PARSTATUS" -eq 3 -o "$NZBPP_PARFAILED" -eq 1 ]; then
|
||||
if [ "$NZBPP_PARSTATUS" -eq 3 ]; then
|
||||
echo "[WARNING] Post-Process: Par-check successful, but Par-repair disabled, exiting"
|
||||
else
|
||||
echo "[WARNING] Post-Process: Par-check failed, exiting"
|
||||
fi
|
||||
exit $POSTPROCESS_ERROR
|
||||
fi
|
||||
|
||||
# Check if destination directory exists (important for reprocessing of history items)
|
||||
if [ ! -d "$NZBPP_DIRECTORY" ]; then
|
||||
echo "[ERROR] Post-Process: Nothing to post-process: destination directory $NZBPP_DIRECTORY doesn't exist"
|
||||
exit $POSTPROCESS_ERROR
|
||||
fi
|
||||
|
||||
cd "$NZBPP_DIRECTORY"
|
||||
|
||||
# If not just repaired and file "_brokenlog.txt" exists, the collection is damaged
|
||||
# exiting with returning code $POSTPROCESS_PARCHECK_ALL to request par-repair
|
||||
if [ ! "$NZBPP_PARSTATUS" -eq 2 ]; then
|
||||
if [ -f "_brokenlog.txt" ]; then
|
||||
if (ls *.[pP][aA][rR]2 >/dev/null 2>&1); then
|
||||
echo "[INFO] Post-Process: Brokenlog found, requesting par-repair"
|
||||
exit $POSTPROCESS_PARCHECK_ALL
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# All checks done, now processing the files
|
||||
|
||||
# Flag indicates that something was unrared
|
||||
Unrared=0
|
||||
|
||||
# Unrar the files (if any) to the temporary directory, if there are no rar files this will do nothing
|
||||
if (ls *.rar >/dev/null 2>&1); then
|
||||
|
||||
# Check if unrar exists
|
||||
$UnrarCmd >/dev/null 2>&1
|
||||
if [ "$?" -eq 127 ]; then
|
||||
echo "[ERROR] Post-Process: Unrar not found. Set the path to unrar in script's configuration"
|
||||
exit $POSTPROCESS_ERROR
|
||||
fi
|
||||
|
||||
# Make a temporary directory to store the unrarred files
|
||||
ExtractedDirExists=0
|
||||
if [ -d extracted ]; then
|
||||
ExtractedDirExists=1
|
||||
else
|
||||
mkdir extracted
|
||||
fi
|
||||
|
||||
echo "[INFO] Post-Process: Unraring"
|
||||
rarpasswordparam=""
|
||||
if [ "$NZBPR_Password" != "" ]; then
|
||||
rarpasswordparam="-p$NZBPR_Password"
|
||||
fi
|
||||
|
||||
$UnrarCmd x -y -p- "$rarpasswordparam" -o+ "*.rar" ./extracted/
|
||||
if [ "$?" -ne 0 ]; then
|
||||
echo "[ERROR] Post-Process: Unrar failed"
|
||||
if [ "$ExtractedDirExists" -eq 0 ]; then
|
||||
rm -R extracted
|
||||
fi
|
||||
# for delayed par-check/-repair at least one par-file must be already downloaded
|
||||
if (ls *.[pP][aA][rR]2 >/dev/null 2>&1); then
|
||||
echo "[INFO] Post-Process: Requesting par-repair"
|
||||
exit $POSTPROCESS_PARCHECK_ALL
|
||||
fi
|
||||
exit $POSTPROCESS_ERROR
|
||||
fi
|
||||
Unrared=1
|
||||
|
||||
# Remove the rar files
|
||||
if [ "$DeleteRarFiles" = "yes" ]; then
|
||||
echo "[INFO] Post-Process: Deleting rar-files"
|
||||
rm *.r[0-9][0-9] >/dev/null 2>&1
|
||||
rm *.rar >/dev/null 2>&1
|
||||
rm *.s[0-9][0-9] >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
# Go to the temp directory and try to unrar again.
|
||||
# If there are any rars inside the extracted rars then these will no also be unrarred
|
||||
cd extracted
|
||||
if (ls *.rar >/dev/null 2>&1); then
|
||||
echo "[INFO] Post-Process: Unraring (second pass)"
|
||||
$UnrarCmd x -y -p- -o+ "*.rar"
|
||||
|
||||
if [ "$?" -ne 0 ]; then
|
||||
echo "[INFO] Post-Process: Unrar (second pass) failed"
|
||||
exit $POSTPROCESS_ERROR
|
||||
fi
|
||||
|
||||
# Delete the Rar files
|
||||
if [ "$DeleteRarFiles" = "yes" ]; then
|
||||
echo "[INFO] Post-Process: Deleting rar-files (second pass)"
|
||||
rm *.r[0-9][0-9] >/dev/null 2>&1
|
||||
rm *.rar >/dev/null 2>&1
|
||||
rm *.s[0-9][0-9] >/dev/null 2>&1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Move everything back to the Download folder
|
||||
mv * ..
|
||||
cd ..
|
||||
rmdir extracted
|
||||
fi
|
||||
|
||||
# If download contains only nzb-files move them into nzb-directory
|
||||
# for further download
|
||||
# Check if command "wc" exists
|
||||
wc -l . >/dev/null 2>&1
|
||||
if [ "$?" -ne 127 ]; then
|
||||
AllFilesCount=`ls -1 2>/dev/null | wc -l`
|
||||
NZBFilesCount=`ls -1 *.nzb 2>/dev/null | wc -l`
|
||||
if [ "$AllFilesCount" -eq "$NZBFilesCount" ]; then
|
||||
echo "[INFO] Moving downloaded nzb-files into incoming nzb-directory for further download"
|
||||
mv *.nzb $NZBOP_NZBDIR
|
||||
fi
|
||||
fi
|
||||
|
||||
# Clean up
|
||||
echo "[INFO] Post-Process: Cleaning up"
|
||||
chmod -R a+rw .
|
||||
rm *.nzb >/dev/null 2>&1
|
||||
rm *.sfv >/dev/null 2>&1
|
||||
rm *.1 >/dev/null 2>&1
|
||||
rm _brokenlog.txt >/dev/null 2>&1
|
||||
if [ "$Unrared" -eq 1 ]; then
|
||||
# Delete par2-file only if there were files for unpacking.
|
||||
rm *.[pP][aA][rR]2 >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
if [ "$JoinTS" = "yes" ]; then
|
||||
# Join any split .ts files if they are named xxxx.0000.ts xxxx.0001.ts
|
||||
# They will be joined together to a file called xxxx.0001.ts
|
||||
if (ls *.ts >/dev/null 2>&1); then
|
||||
echo "[INFO] Post-Process: Joining ts-files"
|
||||
tsname=`find . -name "*0001.ts" |awk -F/ '{print $NF}'`
|
||||
cat *0???.ts > ./$tsname
|
||||
fi
|
||||
|
||||
# Remove all the split .ts files
|
||||
echo "[INFO] Post-Process: Deleting source ts-files"
|
||||
rm *0???.ts >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
if [ "$RenameIMG" = "yes" ]; then
|
||||
# Rename img file to iso
|
||||
# It will be renamed to .img.iso so you can see that it has been renamed
|
||||
if (ls *.img >/dev/null 2>&1); then
|
||||
echo "[INFO] Post-Process: Renaming img-files to iso"
|
||||
imgname=`find . -name "*.img" |awk -F/ '{print $NF}'`
|
||||
mv $imgname $imgname.iso
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if destination directory was set in postprocessing parameters
|
||||
# (for current nzb-file) via web-interface or via command line with
|
||||
# "nzbget -E G O DestDir=/new/path <ID>"
|
||||
if [ "$NZBPR_DestDir" != "" ]; then
|
||||
mkdir $NZBPR_DestDir
|
||||
mv * $NZBPR_DestDir >/dev/null 2>&1
|
||||
cd ..
|
||||
rmdir $NZBPP_DIRECTORY
|
||||
fi
|
||||
|
||||
# All OK, requesting cleaning up of download queue
|
||||
exit $POSTPROCESS_SUCCESS
|
||||
35
nzbget-shell.bat
Normal file
35
nzbget-shell.bat
Normal file
@@ -0,0 +1,35 @@
|
||||
@echo off
|
||||
|
||||
rem
|
||||
rem Batch file to start nzbget shell
|
||||
rem
|
||||
rem Copyright (C) 2009 orbisvicis <orbisvicis@users.sourceforge.net>
|
||||
rem Copyright (C) 2009 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
rem
|
||||
rem This program is free software; you can redistribute it and/or modify
|
||||
rem it under the terms of the GNU General Public License as published by
|
||||
rem the Free Software Foundation; either version 2 of the License, or
|
||||
rem (at your option) any later version.
|
||||
rem
|
||||
rem This program is distributed in the hope that it will be useful,
|
||||
rem but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
rem MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
rem GNU General Public License for more details.
|
||||
rem
|
||||
rem You should have received a copy of the GNU General Public License
|
||||
rem along with this program; if not, write to the Free Software
|
||||
rem Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
rem
|
||||
|
||||
rem ####################### Usage instructions #######################
|
||||
rem
|
||||
rem After starting the batch file you can use all nzbget commands
|
||||
rem (like nzbget -s, nzbget -L, etc) without typing the full
|
||||
rem path to nzbget executable.
|
||||
rem
|
||||
rem ####################### End of Usage instructions #######################
|
||||
|
||||
|
||||
rem expression "%~dp0" means the location of an executing batch file
|
||||
set PATH=%PATH%;%~dp0
|
||||
cmd /U /K "cd %USERPROFILE% & nzbget"
|
||||
1024
nzbget.conf
Normal file
1024
nzbget.conf
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,216 +0,0 @@
|
||||
# Sample configuration file for nzbget
|
||||
# Put this file to one of the following locations:
|
||||
# ~/.nzbget
|
||||
# /etc/nzbget.conf
|
||||
# /usr/etc/nzbget.conf
|
||||
# /usr/local/etc/nzbget.conf
|
||||
# /opt/etc/nzbget.conf
|
||||
|
||||
# For quick start change the option MAINDIR and configure one news-server
|
||||
# See section NEWS-SERVERS.
|
||||
|
||||
|
||||
# PROGRAM OPTIONS
|
||||
|
||||
# Root directory for all related tasks
|
||||
# MAINDIR is a variable and therefore starts with "$"
|
||||
# The value "~/download" is example for POSIX. On Windows use
|
||||
# absolute paths, like "C:\Download".
|
||||
$MAINDIR=~/download
|
||||
|
||||
# Destination-directory to store the downloaded files
|
||||
destdir=${MAINDIR}/dst
|
||||
|
||||
# Directory to store download queue
|
||||
queuedir=${MAINDIR}/queue
|
||||
|
||||
# Directory to monitor for incoming nzb-jobs
|
||||
nzbdir=${MAINDIR}/nzb
|
||||
|
||||
# Directory to store temporary files
|
||||
tempdir=${MAINDIR}/tmp
|
||||
|
||||
# Lock-file for daemon-mode, contains process-id (PID)
|
||||
lockfile=/tmp/nzbget.lock
|
||||
|
||||
# Where to store log file, if it needs to be created (see "createlog")
|
||||
logfile=${destdir}/nzbget.log
|
||||
|
||||
# Save download queue to disk. This allows to reload it on next start (yes, no)
|
||||
savequeue=yes
|
||||
|
||||
# Reload download queue on start, if it exists (yes, no)
|
||||
reloadqueue=yes
|
||||
|
||||
# Create log file (yes, no)
|
||||
createlog=yes
|
||||
|
||||
# Delete log file upon server start (only in server-mode) (yes, no)
|
||||
resetlog=no
|
||||
|
||||
# How various messages must be printed (screen, log, both, none)
|
||||
# Debug-messages can be only printed if the programm was compiled in
|
||||
# debug-mode: "./configure --enable-debug"
|
||||
infotarget=both
|
||||
warningtarget=both
|
||||
errortarget=both
|
||||
debugtarget=both
|
||||
|
||||
# Create subdirectory with nzb-filename in destination-directory (yes, no)
|
||||
appendnzbdir=yes
|
||||
|
||||
# Set screen-outputmode (loggable, colored, curses)
|
||||
outputmode=colored
|
||||
|
||||
# Check if the destination file already exists (yes, no)
|
||||
# If the file exists it will not be added to queue.
|
||||
# If "no" the file will be downloaded and renamed to "filename_duplicate1",
|
||||
# no existing files are deleted or overwritten.
|
||||
dupecheck=no
|
||||
|
||||
# Set the maximum download rate in KB/s, "0" means no speed control
|
||||
downloadrate=0
|
||||
|
||||
# Visibly rename broken files on download appending "_broken" (yes, no)
|
||||
renamebroken=no
|
||||
|
||||
# Create a log of all broken files (yes ,no)
|
||||
# It is a text file placed near downloaded files, which contains
|
||||
# the names of broken files
|
||||
createbrokenlog=yes
|
||||
|
||||
# Set the IP on which the server listen and which client uses to contact the server
|
||||
# It could be ip-address oder dns-hostname
|
||||
serverip=127.0.0.1
|
||||
|
||||
# Set the port which the server & client use
|
||||
serverport=6789
|
||||
|
||||
# Set the password needed to succesfully queue a request
|
||||
serverpassword=tegbzn6789
|
||||
|
||||
# Determine how the articles should be decoded (uulib, yenc, none)
|
||||
# uulib - use uulib to decode files. Supports many encoding formats, but is slow.
|
||||
# yenc - use internal yEnc-Decoder. Supports only yEnc-format and is very fast.
|
||||
# none - the articles will not be decoded and joined. External programs
|
||||
# ("uudeview" is one of them) could be used to decode an join downloaded articles.
|
||||
decoder=yEnc
|
||||
|
||||
# How much retries should be attempted if a download error occurs
|
||||
retries=4
|
||||
|
||||
# Set the interval between retries, in seconds
|
||||
retryinterval=10
|
||||
|
||||
# Set connection timeout, in seconds
|
||||
connectiontimeout=60
|
||||
|
||||
# Timeout until a download-thread is killed (helps on hanging downloads), in seconds
|
||||
terminatetimeout=600
|
||||
|
||||
# How many par2-files to load (none, all, one)
|
||||
# none - all added par2-files must be automatically paused
|
||||
# all - all added par2-files must be downloaded
|
||||
# one - only one main par2-file must be dowloaded and other must be paused
|
||||
# Paused files remain in queue and should be deleted manually,
|
||||
# when they not needed anymore
|
||||
loadpars=all
|
||||
|
||||
# Automatic par-verification (yes, no)
|
||||
# To download only needed par2-files (smart par-files loading) set also
|
||||
# the option "loadpars" to "one". If option "loadpars" is set to "all",
|
||||
# all par2-files will be downloaded before verification and repair starts.
|
||||
# The option "renamebroken" must be set to "no", otherwise the par-checker
|
||||
# may not find renamed files and failed.
|
||||
parcheck=no
|
||||
|
||||
# Automatic par-repair (yes, no)
|
||||
# If option "parcheck" is enabled and "parrepair" is not, the program
|
||||
# only verify downloaded files and downloads needed par2-files, but do
|
||||
# not start repair-process. This is useful if the server does not have
|
||||
# enough CPU power, since repairing of large files may take too much
|
||||
# resources and time on a slow computers.
|
||||
# This option has effect only if the option "parcheck" is enabled.
|
||||
parrepair=yes
|
||||
|
||||
# Use only par2-files with matching names (yes, no)
|
||||
# If par-check needs extra par-blocks it searches a par2-files
|
||||
# in download queue, which can be unpauseed and used for restore. These par2-files
|
||||
# should have the same base name as a main par2-file, currently loaded in par-checker.
|
||||
# Sometimes extra par files (especially if they were uploaded not from original poster)
|
||||
# have not matching names. Normally par-checker does not use these files, but
|
||||
# you can allow it to use these file by setting "strictparname" to "no".
|
||||
# This has however a side effect: if NZB-file contains more than one collection
|
||||
# of files (with different par-sets), par-checker may download par-files from
|
||||
# a wrong collection. This increases you traffic (but not harm par-check).
|
||||
# NOTE: par-checker always uses only par-files added from the same NZB-file.
|
||||
# Option "strictparname" does not change this behavior.
|
||||
strictparname=yes
|
||||
|
||||
# Set path to program, that must be executed after the download of
|
||||
# nzb-file or one collection in nzb-file (if par-check enabled)
|
||||
# is completed and possibly par-checked/repaired.
|
||||
# Six arguments are being passed to this program:
|
||||
# - path to destination dir, where downloaded files are located;
|
||||
# - name of nzb-file processed;
|
||||
# - name of par-file processed (if par-checked) or empty string (if not);
|
||||
# - result of par-check:
|
||||
# 0 - not checked: par-check disabled or nzb-file does not contain any par-files;
|
||||
# 1 - checked and sucessfully repaired;
|
||||
# 2 - checked and failed to repair;
|
||||
# - state of nzb-job:
|
||||
# 0 - there are more collection in this nzb-file queued;
|
||||
# 1 - this was the last collection in nzb-file;
|
||||
# NOTE: if par-check is enabled and nzb-file contains more than one collection
|
||||
# of files the postprocess-program is called after each collection is completed
|
||||
# and par-checked. If you want to clean up the directory (delete par-files, etc.)
|
||||
# there are two possibilities, when you can do this:
|
||||
# 1) you parse the "name of par-file processed" to find out the base name
|
||||
# of collection and clean up only files from this collection;
|
||||
# 2) or you just check the parameter "state of nzb-job" and do clean up,
|
||||
# only if it is equal to "1" (which means, that this was the last
|
||||
# collection in nzb-file and all files are now completed);
|
||||
# NOTE: do not forget to uncomment next line.
|
||||
#postprocess=~/myscript.sh
|
||||
|
||||
|
||||
# NEWS-SERVERS
|
||||
|
||||
# This section defines which servers nzbget should connect to.
|
||||
# The servers will be ordered by their level, i.e. nzbget will at
|
||||
# first try to download an article from the level-0-server.
|
||||
# If that server fails, nzbget proceeds with the level-1-server, etc.
|
||||
# A good idea is surely to put your major download-server at level 0
|
||||
# and your fill-servers at levels 1,2,...
|
||||
# NOTE 1: Do not leave out a level in your server-list and start with level 0!
|
||||
# NOTE 2: Several servers with the same level may be used.
|
||||
|
||||
# First server, on level 0
|
||||
# Level of newsserver
|
||||
server1.level=0
|
||||
# Host-name of newsserver
|
||||
server1.host=my1.newsserver.com
|
||||
# Port to connect to (default 119 if not specified)
|
||||
server1.port=119
|
||||
# Username to use for authentication
|
||||
server1.username=user
|
||||
# Password to use for authentication
|
||||
server1.password=pass
|
||||
# Maximal number of simultaneous connections to this server
|
||||
server1.connections=4
|
||||
|
||||
# Second server, on level 0
|
||||
#server2.level=0
|
||||
#server2.host=my2.newsserver.com
|
||||
#server2.port=119
|
||||
#server2.username=me
|
||||
#server2.password=mypass
|
||||
#server2.connections=4
|
||||
|
||||
# Third server, on level 1
|
||||
#server3.level=1
|
||||
#server3.host=fills.newsserver.com
|
||||
#server3.port=119
|
||||
#server3.username=me2
|
||||
#server3.password=mypass2
|
||||
#server3.connections=1
|
||||
635
nzbget.cpp
635
nzbget.cpp
@@ -1,8 +1,8 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2004 Sven Henkel <sidddy@users.sourceforge.net>
|
||||
* Copyright (C) 2007-2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -25,7 +25,7 @@
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -38,17 +38,23 @@
|
||||
#include <winsvc.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <sys/resource.h>
|
||||
#ifdef HAVE_SYS_PRCTL_H
|
||||
#include <sys/prctl.h>
|
||||
#endif
|
||||
#include <signal.h>
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#ifdef DEBUG
|
||||
#ifndef DISABLE_PARCHECK
|
||||
#include <sigc++/sigc++.h>
|
||||
#include <iostream>
|
||||
#endif
|
||||
#ifdef HAVE_BACKTRACE
|
||||
#include <execinfo.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
@@ -60,89 +66,211 @@
|
||||
#include "ColoredFrontend.h"
|
||||
#include "NCursesFrontend.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "UrlCoordinator.h"
|
||||
#include "RemoteServer.h"
|
||||
#include "RemoteClient.h"
|
||||
#include "MessageBase.h"
|
||||
#include "DiskState.h"
|
||||
#include "PrePostProcessor.h"
|
||||
#include "ParChecker.h"
|
||||
#include "Scheduler.h"
|
||||
#include "Util.h"
|
||||
#ifdef WIN32
|
||||
#include "NTService.h"
|
||||
#endif
|
||||
|
||||
// Prototypes
|
||||
void Run();
|
||||
void RunMain();
|
||||
void Run(bool bReload);
|
||||
void Reload();
|
||||
void Cleanup();
|
||||
void ProcessClientRequest();
|
||||
#ifndef WIN32
|
||||
void InstallSignalHandlers();
|
||||
void Daemonize();
|
||||
void PrintBacktrace();
|
||||
#ifdef HAVE_SYS_PRCTL_H
|
||||
void EnableDumpCore();
|
||||
#endif
|
||||
#ifdef DEBUG
|
||||
void DoTest();
|
||||
void MakeSegFault();
|
||||
#endif
|
||||
#endif
|
||||
#ifndef DISABLE_PARCHECK
|
||||
void DisableCout();
|
||||
#endif
|
||||
|
||||
Thread* g_pFrontend = NULL;
|
||||
Options* g_pOptions = NULL;
|
||||
Thread* g_pFrontend = NULL;
|
||||
Options* g_pOptions = NULL;
|
||||
ServerPool* g_pServerPool = NULL;
|
||||
QueueCoordinator* g_pQueueCoordinator = NULL;
|
||||
UrlCoordinator* g_pUrlCoordinator = NULL;
|
||||
RemoteServer* g_pRemoteServer = NULL;
|
||||
DownloadSpeedMeter* g_pDownloadSpeedMeter = NULL;
|
||||
DownloadQueueHolder* g_pDownloadQueueHolder = NULL;
|
||||
Log* g_pLog = NULL;
|
||||
PrePostProcessor* g_pPrePostProcessor;
|
||||
PrePostProcessor* g_pPrePostProcessor = NULL;
|
||||
DiskState* g_pDiskState = NULL;
|
||||
Scheduler* g_pScheduler = NULL;
|
||||
int g_iArgumentCount;
|
||||
char* (*g_szEnvironmentVariables)[] = NULL;
|
||||
char* (*g_szArguments)[] = NULL;
|
||||
bool g_bReloading = true;
|
||||
|
||||
/*
|
||||
* Main loop
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
int main(int argc, char *argv[], char *argp[])
|
||||
{
|
||||
#ifdef WIN32
|
||||
_set_fmode(_O_BINARY);
|
||||
#ifdef _DEBUG
|
||||
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
|
||||
_CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
|
||||
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF
|
||||
#ifdef DEBUG_CRTMEMLEAKS
|
||||
| _CRTDBG_CHECK_CRT_DF | _CRTDBG_CHECK_ALWAYS_DF
|
||||
#endif
|
||||
);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Util::InitVersionRevision();
|
||||
|
||||
#ifdef WIN32
|
||||
InstallUninstallServiceCheck(argc, argv);
|
||||
#endif
|
||||
|
||||
// Init options & get the name of the .nzb file
|
||||
g_pLog = new Log();
|
||||
g_pServerPool = new ServerPool();
|
||||
debug("Options parsing");
|
||||
g_pOptions = new Options(argc, argv);
|
||||
|
||||
if (g_pOptions->GetServerMode() && g_pOptions->GetCreateLog() && g_pOptions->GetResetLog())
|
||||
{
|
||||
debug("deleting old log-file");
|
||||
g_pLog->ResetLog();
|
||||
}
|
||||
|
||||
if (g_pOptions->GetDaemonMode())
|
||||
{
|
||||
info("nzbget daemon-mode");
|
||||
#ifdef WIN32
|
||||
StartService(Run);
|
||||
return 0;
|
||||
#else
|
||||
Daemonize();
|
||||
#ifndef DISABLE_PARCHECK
|
||||
DisableCout();
|
||||
#endif
|
||||
|
||||
g_iArgumentCount = argc;
|
||||
g_szArguments = (char*(*)[])argv;
|
||||
g_szEnvironmentVariables = (char*(*)[])argp;
|
||||
|
||||
#ifdef WIN32
|
||||
for (int i=0; i < argc; i++)
|
||||
{
|
||||
if (!strcmp(argv[i], "-D"))
|
||||
{
|
||||
StartService(RunMain);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
RunMain();
|
||||
|
||||
#ifdef WIN32
|
||||
#ifdef _DEBUG
|
||||
_CrtDumpMemoryLeaks();
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
else if (g_pOptions->GetServerMode())
|
||||
{
|
||||
info("nzbget server-mode");
|
||||
}
|
||||
else if (g_pOptions->GetRemoteClientMode())
|
||||
{
|
||||
info("nzbget remote-mode");
|
||||
}
|
||||
|
||||
Run();
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Run()
|
||||
void RunMain()
|
||||
{
|
||||
// we need to save and later restore current directory each time
|
||||
// the program is reloaded (RPC-Method "reload") in order for
|
||||
// config to properly load in a case relative paths are used
|
||||
// in command line
|
||||
char szCurDir[MAX_PATH + 1];
|
||||
Util::GetCurrentDirectory(szCurDir, sizeof(szCurDir));
|
||||
|
||||
bool bReload = false;
|
||||
while (g_bReloading)
|
||||
{
|
||||
g_bReloading = false;
|
||||
Util::SetCurrentDirectory(szCurDir);
|
||||
Run(bReload);
|
||||
bReload = true;
|
||||
}
|
||||
}
|
||||
|
||||
void Run(bool bReload)
|
||||
{
|
||||
g_pLog = new Log();
|
||||
|
||||
debug("nzbget %s", Util::VersionRevision());
|
||||
|
||||
if (!bReload)
|
||||
{
|
||||
Thread::Init();
|
||||
}
|
||||
|
||||
g_pServerPool = new ServerPool();
|
||||
g_pScheduler = new Scheduler();
|
||||
|
||||
debug("Reading options");
|
||||
g_pOptions = new Options(g_iArgumentCount, *g_szArguments);
|
||||
|
||||
#ifndef WIN32
|
||||
InstallSignalHandlers();
|
||||
if (g_pOptions->GetUMask() < 01000)
|
||||
{
|
||||
/* set newly created file permissions */
|
||||
umask(g_pOptions->GetUMask());
|
||||
}
|
||||
#endif
|
||||
|
||||
if (g_pOptions->GetServerMode() && g_pOptions->GetCreateLog() && g_pOptions->GetResetLog())
|
||||
{
|
||||
debug("Deleting old log-file");
|
||||
g_pLog->ResetLog();
|
||||
}
|
||||
|
||||
g_pLog->InitOptions();
|
||||
|
||||
if (g_pOptions->GetDaemonMode() && !bReload)
|
||||
{
|
||||
#ifdef WIN32
|
||||
info("nzbget %s service-mode", Util::VersionRevision());
|
||||
#else
|
||||
Daemonize();
|
||||
info("nzbget %s daemon-mode", Util::VersionRevision());
|
||||
#endif
|
||||
}
|
||||
else if (g_pOptions->GetServerMode() && !bReload)
|
||||
{
|
||||
info("nzbget %s server-mode", Util::VersionRevision());
|
||||
}
|
||||
else if (g_pOptions->GetRemoteClientMode())
|
||||
{
|
||||
info("nzbget %s remote-mode", Util::VersionRevision());
|
||||
}
|
||||
|
||||
if (!bReload)
|
||||
{
|
||||
Connection::Init();
|
||||
}
|
||||
|
||||
if (!g_pOptions->GetRemoteClientMode())
|
||||
{
|
||||
g_pServerPool->InitConnections();
|
||||
#ifdef DEBUG
|
||||
g_pServerPool->LogDebugInfo();
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
#ifdef HAVE_SYS_PRCTL_H
|
||||
if (g_pOptions->GetDumpCore())
|
||||
{
|
||||
EnableDumpCore();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
Thread::Init();
|
||||
Connection::Init();
|
||||
#ifndef WIN32
|
||||
InstallSignalHandlers();
|
||||
#ifdef DEBUG
|
||||
if (g_pOptions->GetTestBacktrace())
|
||||
{
|
||||
MakeSegFault();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
// client request
|
||||
if (g_pOptions->GetClientOperation() != Options::opClientNoOperation)
|
||||
@@ -153,10 +281,13 @@ void Run()
|
||||
}
|
||||
|
||||
// Create the queue coordinator
|
||||
if (!g_pOptions->GetRemoteClientMode() && !g_pOptions->GetTest())
|
||||
if (!g_pOptions->GetRemoteClientMode())
|
||||
{
|
||||
g_pQueueCoordinator = new QueueCoordinator();
|
||||
g_pDownloadSpeedMeter = g_pQueueCoordinator;
|
||||
g_pDownloadQueueHolder = g_pQueueCoordinator;
|
||||
|
||||
g_pUrlCoordinator = new UrlCoordinator();
|
||||
}
|
||||
|
||||
// Setup the network-server
|
||||
@@ -166,7 +297,13 @@ void Run()
|
||||
g_pRemoteServer->Start();
|
||||
}
|
||||
|
||||
// Create the front-end
|
||||
// Creating PrePostProcessor
|
||||
if (!g_pOptions->GetRemoteClientMode())
|
||||
{
|
||||
g_pPrePostProcessor = new PrePostProcessor();
|
||||
}
|
||||
|
||||
// Create the frontend
|
||||
if (!g_pOptions->GetDaemonMode())
|
||||
{
|
||||
switch (g_pOptions->GetOutputMode())
|
||||
@@ -191,32 +328,50 @@ void Run()
|
||||
g_pFrontend->Start();
|
||||
}
|
||||
|
||||
// Start QueueCoordinator
|
||||
if (!g_pOptions->GetRemoteClientMode() && !g_pOptions->GetTest())
|
||||
// Starting QueueCoordinator and PrePostProcessor
|
||||
if (!g_pOptions->GetRemoteClientMode())
|
||||
{
|
||||
g_pPrePostProcessor = new PrePostProcessor();
|
||||
|
||||
g_pPrePostProcessor->Start();
|
||||
|
||||
// Standalone-mode
|
||||
if (!g_pOptions->GetServerMode() && !g_pQueueCoordinator->AddFileToQueue(g_pOptions->GetArgFilename()))
|
||||
if (!g_pOptions->GetServerMode())
|
||||
{
|
||||
abort("FATAL ERROR: Parsing NZB-document %s failed!!\n\n", g_pOptions->GetArgFilename() ? g_pOptions->GetArgFilename() : "N/A");
|
||||
return;
|
||||
NZBFile* pNZBFile = NZBFile::CreateFromFile(g_pOptions->GetArgFilename(), g_pOptions->GetAddCategory() ? g_pOptions->GetAddCategory() : "");
|
||||
if (!pNZBFile)
|
||||
{
|
||||
abort("FATAL ERROR: Parsing NZB-document %s failed\n\n", g_pOptions->GetArgFilename() ? g_pOptions->GetArgFilename() : "N/A");
|
||||
return;
|
||||
}
|
||||
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, false);
|
||||
delete pNZBFile;
|
||||
}
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState = new DiskState();
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->Start();
|
||||
g_pUrlCoordinator->Start();
|
||||
g_pPrePostProcessor->Start();
|
||||
|
||||
// enter main program-loop
|
||||
while (g_pQueueCoordinator->IsRunning() || g_pPrePostProcessor->IsRunning())
|
||||
while (g_pQueueCoordinator->IsRunning() ||
|
||||
g_pUrlCoordinator->IsRunning() ||
|
||||
g_pPrePostProcessor->IsRunning())
|
||||
{
|
||||
if (!g_pOptions->GetServerMode() && !g_pQueueCoordinator->HasMoreJobs() && !g_pPrePostProcessor->HasMoreJobs())
|
||||
if (!g_pOptions->GetServerMode() &&
|
||||
!g_pQueueCoordinator->HasMoreJobs() &&
|
||||
!g_pUrlCoordinator->HasMoreJobs() &&
|
||||
!g_pPrePostProcessor->HasMoreJobs())
|
||||
{
|
||||
// Standalone-mode: download completed
|
||||
if (!g_pQueueCoordinator->IsStopped())
|
||||
{
|
||||
g_pQueueCoordinator->Stop();
|
||||
}
|
||||
if (!g_pUrlCoordinator->IsStopped())
|
||||
{
|
||||
g_pUrlCoordinator->Stop();
|
||||
}
|
||||
if (!g_pPrePostProcessor->IsStopped())
|
||||
{
|
||||
g_pPrePostProcessor->Stop();
|
||||
@@ -227,6 +382,7 @@ void Run()
|
||||
|
||||
// main program-loop is terminated
|
||||
debug("QueueCoordinator stopped");
|
||||
debug("UrlCoordinator stopped");
|
||||
debug("PrePostProcessor stopped");
|
||||
}
|
||||
|
||||
@@ -248,14 +404,6 @@ void Run()
|
||||
}
|
||||
debug("RemoteServer stopped");
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
if (g_pOptions->GetTest())
|
||||
{
|
||||
DoTest();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Stop Frontend
|
||||
if (g_pFrontend)
|
||||
@@ -279,42 +427,116 @@ void ProcessClientRequest()
|
||||
{
|
||||
RemoteClient* Client = new RemoteClient();
|
||||
|
||||
if (g_pOptions->GetClientOperation() == Options::opClientRequestList)
|
||||
switch (g_pOptions->GetClientOperation())
|
||||
{
|
||||
Client->RequestServerList();
|
||||
}
|
||||
else if (g_pOptions->GetClientOperation() == Options::opClientRequestPause)
|
||||
{
|
||||
Client->RequestServerPauseUnpause(true);
|
||||
}
|
||||
else if (g_pOptions->GetClientOperation() == Options::opClientRequestUnpause)
|
||||
{
|
||||
Client->RequestServerPauseUnpause(false);
|
||||
}
|
||||
else if (g_pOptions->GetClientOperation() == Options::opClientRequestSetRate)
|
||||
{
|
||||
Client->RequestServerSetDownloadRate(g_pOptions->GetSetRate());
|
||||
}
|
||||
else if (g_pOptions->GetClientOperation() == Options::opClientRequestDumpDebug)
|
||||
{
|
||||
Client->RequestServerDumpDebug();
|
||||
}
|
||||
else if (g_pOptions->GetClientOperation() == Options::opClientRequestEditQueue)
|
||||
{
|
||||
Client->RequestServerEditQueue(g_pOptions->GetEditQueueAction(), g_pOptions->GetEditQueueOffset(),
|
||||
g_pOptions->GetEditQueueIDFrom(), g_pOptions->GetEditQueueIDTo());
|
||||
}
|
||||
else if (g_pOptions->GetClientOperation() == Options::opClientRequestLog)
|
||||
{
|
||||
Client->RequestServerLog(g_pOptions->GetLogLines());
|
||||
}
|
||||
else if (g_pOptions->GetClientOperation() == Options::opClientRequestShutdown)
|
||||
{
|
||||
Client->RequestServerShutdown();
|
||||
}
|
||||
else if (g_pOptions->GetClientOperation() == Options::opClientRequestDownload)
|
||||
{
|
||||
Client->RequestServerDownload(g_pOptions->GetArgFilename(), g_pOptions->GetAddTop());
|
||||
case Options::opClientRequestListFiles:
|
||||
Client->RequestServerList(true, false, g_pOptions->GetMatchMode() == Options::mmRegEx ? g_pOptions->GetEditQueueText() : NULL);
|
||||
break;
|
||||
|
||||
case Options::opClientRequestListGroups:
|
||||
Client->RequestServerList(false, true, g_pOptions->GetMatchMode() == Options::mmRegEx ? g_pOptions->GetEditQueueText() : NULL);
|
||||
break;
|
||||
|
||||
case Options::opClientRequestListStatus:
|
||||
Client->RequestServerList(false, false, NULL);
|
||||
break;
|
||||
|
||||
case Options::opClientRequestDownloadPause:
|
||||
Client->RequestServerPauseUnpause(true, eRemotePauseUnpauseActionDownload);
|
||||
break;
|
||||
|
||||
case Options::opClientRequestDownloadUnpause:
|
||||
Client->RequestServerPauseUnpause(false, eRemotePauseUnpauseActionDownload);
|
||||
break;
|
||||
|
||||
case Options::opClientRequestDownload2Pause:
|
||||
Client->RequestServerPauseUnpause(true, eRemotePauseUnpauseActionDownload2);
|
||||
break;
|
||||
|
||||
case Options::opClientRequestDownload2Unpause:
|
||||
Client->RequestServerPauseUnpause(false, eRemotePauseUnpauseActionDownload2);
|
||||
break;
|
||||
|
||||
case Options::opClientRequestSetRate:
|
||||
Client->RequestServerSetDownloadRate(g_pOptions->GetSetRate());
|
||||
break;
|
||||
|
||||
case Options::opClientRequestDumpDebug:
|
||||
Client->RequestServerDumpDebug();
|
||||
break;
|
||||
|
||||
case Options::opClientRequestEditQueue:
|
||||
Client->RequestServerEditQueue((eRemoteEditAction)g_pOptions->GetEditQueueAction(), g_pOptions->GetEditQueueOffset(),
|
||||
g_pOptions->GetEditQueueText(), g_pOptions->GetEditQueueIDList(), g_pOptions->GetEditQueueIDCount(),
|
||||
g_pOptions->GetEditQueueNameList(), (eRemoteMatchMode)g_pOptions->GetMatchMode(), true);
|
||||
break;
|
||||
|
||||
case Options::opClientRequestLog:
|
||||
Client->RequestServerLog(g_pOptions->GetLogLines());
|
||||
break;
|
||||
|
||||
case Options::opClientRequestShutdown:
|
||||
Client->RequestServerShutdown();
|
||||
break;
|
||||
|
||||
case Options::opClientRequestReload:
|
||||
Client->RequestServerReload();
|
||||
break;
|
||||
|
||||
case Options::opClientRequestDownload:
|
||||
Client->RequestServerDownload(g_pOptions->GetArgFilename(), g_pOptions->GetAddCategory(), g_pOptions->GetAddTop(), g_pOptions->GetAddPaused(), g_pOptions->GetAddPriority());
|
||||
break;
|
||||
|
||||
case Options::opClientRequestVersion:
|
||||
Client->RequestServerVersion();
|
||||
break;
|
||||
|
||||
case Options::opClientRequestPostQueue:
|
||||
Client->RequestPostQueue();
|
||||
break;
|
||||
|
||||
case Options::opClientRequestWriteLog:
|
||||
Client->RequestWriteLog(g_pOptions->GetWriteLogKind(), g_pOptions->GetLastArg());
|
||||
break;
|
||||
|
||||
case Options::opClientRequestScanAsync:
|
||||
Client->RequestScan(false);
|
||||
break;
|
||||
|
||||
case Options::opClientRequestScanSync:
|
||||
Client->RequestScan(true);
|
||||
break;
|
||||
|
||||
case Options::opClientRequestPostPause:
|
||||
Client->RequestServerPauseUnpause(true, eRemotePauseUnpauseActionPostProcess);
|
||||
break;
|
||||
|
||||
case Options::opClientRequestPostUnpause:
|
||||
Client->RequestServerPauseUnpause(false, eRemotePauseUnpauseActionPostProcess);
|
||||
break;
|
||||
|
||||
case Options::opClientRequestScanPause:
|
||||
Client->RequestServerPauseUnpause(true, eRemotePauseUnpauseActionScan);
|
||||
break;
|
||||
|
||||
case Options::opClientRequestScanUnpause:
|
||||
Client->RequestServerPauseUnpause(false, eRemotePauseUnpauseActionScan);
|
||||
break;
|
||||
|
||||
case Options::opClientRequestHistory:
|
||||
Client->RequestHistory();
|
||||
break;
|
||||
|
||||
case Options::opClientRequestDownloadUrl:
|
||||
Client->RequestServerDownloadUrl(g_pOptions->GetLastArg(), g_pOptions->GetAddNZBFilename(), g_pOptions->GetAddCategory(), g_pOptions->GetAddTop(), g_pOptions->GetAddPaused(), g_pOptions->GetAddPriority());
|
||||
break;
|
||||
|
||||
case Options::opClientRequestUrlQueue:
|
||||
Client->RequestUrlQueue();
|
||||
break;
|
||||
|
||||
case Options::opClientNoOperation:
|
||||
break;
|
||||
}
|
||||
|
||||
delete Client;
|
||||
@@ -322,7 +544,10 @@ void ProcessClientRequest()
|
||||
|
||||
void ExitProc()
|
||||
{
|
||||
info("Stopping, please wait...");
|
||||
if (!g_bReloading)
|
||||
{
|
||||
info("Stopping, please wait...");
|
||||
}
|
||||
if (g_pOptions->GetRemoteClientMode())
|
||||
{
|
||||
if (g_pFrontend)
|
||||
@@ -337,11 +562,19 @@ void ExitProc()
|
||||
{
|
||||
debug("Stopping QueueCoordinator");
|
||||
g_pQueueCoordinator->Stop();
|
||||
g_pUrlCoordinator->Stop();
|
||||
g_pPrePostProcessor->Stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Reload()
|
||||
{
|
||||
g_bReloading = true;
|
||||
info("Reloading...");
|
||||
ExitProc();
|
||||
}
|
||||
|
||||
#ifndef WIN32
|
||||
#ifdef DEBUG
|
||||
typedef void(*sighandler)(int);
|
||||
@@ -357,27 +590,22 @@ void SignalProc(int iSignal)
|
||||
{
|
||||
case SIGINT:
|
||||
signal(SIGINT, SIG_DFL); // Reset the signal handler
|
||||
debug("SIGINT received");
|
||||
ExitProc();
|
||||
break;
|
||||
|
||||
case SIGTERM:
|
||||
signal(SIGTERM, SIG_DFL); // Reset the signal handler
|
||||
debug("SIGTERM received");
|
||||
ExitProc();
|
||||
break;
|
||||
|
||||
#ifdef DEBUG
|
||||
case SIGPIPE:
|
||||
debug("SIGPIPE received, ignoring");
|
||||
case SIGCHLD:
|
||||
// ignoring
|
||||
break;
|
||||
|
||||
default:
|
||||
debug("Signal %i received", iSignal);
|
||||
if (SignalProcList[iSignal - 1])
|
||||
{
|
||||
SignalProcList[iSignal - 1](iSignal);
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
case SIGSEGV:
|
||||
signal(SIGSEGV, SIG_DFL); // Reset the signal handler
|
||||
PrintBacktrace();
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
@@ -389,27 +617,83 @@ void InstallSignalHandlers()
|
||||
signal(SIGTERM, SignalProc);
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
#ifdef DEBUG
|
||||
SignalProcList.clear();
|
||||
for (int i = 1; i <= 32; i++)
|
||||
{
|
||||
SignalProcList.push_back((sighandler)signal(i, SignalProc));
|
||||
}
|
||||
signal(SIGWINCH, SIG_DFL);
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void MakeSegFault()
|
||||
{
|
||||
char* N = NULL;
|
||||
strcpy(N, "");
|
||||
}
|
||||
#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
|
||||
#endif
|
||||
|
||||
void Cleanup()
|
||||
{
|
||||
debug("Cleaning up global objects");
|
||||
|
||||
debug("Deleting QueueCoordinator");
|
||||
if (g_pQueueCoordinator)
|
||||
debug("Deleting UrlCoordinator");
|
||||
if (g_pUrlCoordinator)
|
||||
{
|
||||
delete g_pQueueCoordinator;
|
||||
g_pQueueCoordinator = NULL;
|
||||
delete g_pUrlCoordinator;
|
||||
g_pUrlCoordinator = NULL;
|
||||
}
|
||||
debug("QueueCoordinator deleted");
|
||||
debug("UrlCoordinator deleted");
|
||||
|
||||
debug("Deleting RemoteServer");
|
||||
if (g_pRemoteServer)
|
||||
@@ -435,11 +719,28 @@ void Cleanup()
|
||||
}
|
||||
debug("Frontend deleted");
|
||||
|
||||
debug("Deleting QueueCoordinator");
|
||||
if (g_pQueueCoordinator)
|
||||
{
|
||||
delete g_pQueueCoordinator;
|
||||
g_pQueueCoordinator = NULL;
|
||||
}
|
||||
debug("QueueCoordinator deleted");
|
||||
|
||||
debug("Deleting DiskState");
|
||||
if (g_pDiskState)
|
||||
{
|
||||
delete g_pDiskState;
|
||||
g_pDiskState = NULL;
|
||||
}
|
||||
debug("DiskState deleted");
|
||||
|
||||
debug("Deleting Options");
|
||||
if (g_pOptions)
|
||||
{
|
||||
if (g_pOptions->GetDaemonMode())
|
||||
if (g_pOptions->GetDaemonMode() && !g_bReloading)
|
||||
{
|
||||
info("Deleting lock file");
|
||||
remove(g_pOptions->GetLockFile());
|
||||
}
|
||||
delete g_pOptions;
|
||||
@@ -455,8 +756,19 @@ void Cleanup()
|
||||
}
|
||||
debug("ServerPool deleted");
|
||||
|
||||
Thread::Final();
|
||||
Connection::Final();
|
||||
debug("Deleting Scheduler");
|
||||
if (g_pScheduler)
|
||||
{
|
||||
delete g_pScheduler;
|
||||
g_pScheduler = NULL;
|
||||
}
|
||||
debug("Scheduler deleted");
|
||||
|
||||
if (!g_bReloading)
|
||||
{
|
||||
Connection::Final();
|
||||
Thread::Final();
|
||||
}
|
||||
|
||||
debug("Global objects cleaned up");
|
||||
|
||||
@@ -480,11 +792,27 @@ void Daemonize()
|
||||
setsid(); /* obtain a new process group */
|
||||
for (i = getdtablesize();i >= 0;--i) close(i); /* close all descriptors */
|
||||
i = open("/dev/null", O_RDWR); dup(i); dup(i); /* handle standart I/O */
|
||||
umask(027); /* set newly created file permissions */
|
||||
chdir(g_pOptions->GetDestDir()); /* change running directory */
|
||||
lfp = open(g_pOptions->GetLockFile(), O_RDWR | O_CREAT, 0640);
|
||||
if (lfp < 0) exit(1); /* can not open */
|
||||
if (lockf(lfp, F_TLOCK, 0) < 0) exit(0); /* can not lock */
|
||||
|
||||
/* Drop user if there is one, and we were run as root */
|
||||
if ( getuid() == 0 || geteuid() == 0 )
|
||||
{
|
||||
struct passwd *pw = getpwnam(g_pOptions->GetDaemonUserName());
|
||||
if (pw)
|
||||
{
|
||||
fchown(lfp, pw->pw_uid, pw->pw_gid); /* change owner of lock file */
|
||||
setgroups( 0, (const gid_t*) 0 ); /* Set aux groups to null. */
|
||||
setgid(pw->pw_gid); /* Set primary group. */
|
||||
/* Try setting aux groups correctly - not critical if this fails. */
|
||||
initgroups( g_pOptions->GetDaemonUserName(),pw->pw_gid);
|
||||
/* Finally, set uid. */
|
||||
setuid(pw->pw_uid);
|
||||
}
|
||||
}
|
||||
|
||||
/* first instance continues */
|
||||
sprintf(str, "%d\n", getpid());
|
||||
write(lfp, str, strlen(str)); /* record pid to lockfile */
|
||||
@@ -495,35 +823,18 @@ void Daemonize()
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
#ifndef DISABLE_PARCHECK
|
||||
/*
|
||||
class T1 { public:
|
||||
sigc::signal<int, std::string> sig_filename;
|
||||
int Test1() { std::string str = "test"; return sig_filename.emit(str); }
|
||||
};
|
||||
class T2 { public:
|
||||
void Test1() {
|
||||
T1 t1;
|
||||
t1.sig_filename.connect(sigc::mem_fun(*this, &T2::signal_filename));
|
||||
if (t1.Test1() == 4) {
|
||||
printf("ok\n");
|
||||
//exit(0);
|
||||
} else {
|
||||
printf("error\n");
|
||||
//exit(-1);
|
||||
}
|
||||
}
|
||||
int signal_filename(std::string str) {
|
||||
printf("%s\n", str.c_str());
|
||||
return str.length();
|
||||
}
|
||||
};
|
||||
*/
|
||||
#endif
|
||||
|
||||
void DoTest()
|
||||
class NullStreamBuf : public std::streambuf
|
||||
{
|
||||
printf("testing\n");
|
||||
public:
|
||||
int sputc ( char c ) { return (int) c; }
|
||||
} NullStreamBufInstance;
|
||||
|
||||
void DisableCout()
|
||||
{
|
||||
// libpar2 prints messages to c++ standard output stream (std::cout).
|
||||
// However we do not want these messages to be printed.
|
||||
// Since we do not use std::cout in nzbget we just disable it.
|
||||
std::cout.rdbuf(&NullStreamBufInstance);
|
||||
}
|
||||
#endif
|
||||
|
||||
36
nzbget.h
36
nzbget.h
@@ -1,7 +1,7 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
* Copyright (C) 2007-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
|
||||
@@ -15,7 +15,7 @@
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
@@ -31,40 +31,54 @@
|
||||
// WIN32
|
||||
|
||||
#define snprintf _snprintf
|
||||
#ifndef strdup
|
||||
#define strdup _strdup
|
||||
#endif
|
||||
#define fdopen _fdopen
|
||||
#define ctime_r(timep, buf, bufsize) ctime_s(buf, bufsize, timep)
|
||||
#define localtime_r(time, tm) localtime_s(tm, time)
|
||||
#define int32_t __int32
|
||||
#define mkdir(dir, flags) _mkdir(dir)
|
||||
#define rmdir _rmdir
|
||||
#define strcasecmp(a, b) _stricmp(a, b)
|
||||
#define strncasecmp(a, b, c) _strnicmp(a, b, c)
|
||||
|
||||
#pragma warning(disable:4800)
|
||||
#pragma warning(disable:4267)
|
||||
#pragma warning(disable:4244)
|
||||
|
||||
#define ssize_t SSIZE_T
|
||||
#define __S_ISTYPE(mode, mask) (((mode) & _S_IFMT) == (mask))
|
||||
#define S_ISDIR(mode) __S_ISTYPE((mode), _S_IFDIR)
|
||||
#define S_ISREG(mode) __S_ISTYPE((mode), _S_IFREG)
|
||||
|
||||
#define S_DIRMODE NULL
|
||||
#define usleep(usec) Sleep((usec) / 1000)
|
||||
#define gettimeofday(tm, ignore) _ftime(tm)
|
||||
#define _timeval _timeb
|
||||
#define socklen_t int
|
||||
#define SHUT_RDWR 0x02
|
||||
#define PATH_SEPARATOR '\\'
|
||||
#define ALT_PATH_SEPARATOR '/'
|
||||
#define LINE_ENDING "\r\n"
|
||||
#define pid_t int
|
||||
#ifndef FSCTL_SET_SPARSE
|
||||
#define FSCTL_SET_SPARSE 590020
|
||||
#endif
|
||||
|
||||
#pragma warning(disable:4800) // 'type' : forcing value to bool 'true' or 'false' (performance warning)
|
||||
#pragma warning(disable:4267) // 'var' : conversion from 'size_t' to 'type', possible loss of data
|
||||
|
||||
#else
|
||||
|
||||
// POSIX
|
||||
|
||||
#define _timeval timeval
|
||||
#define closesocket(sock) close(sock)
|
||||
#define SOCKET int
|
||||
#define INVALID_SOCKET (-1)
|
||||
#define PATH_SEPARATOR '/'
|
||||
#define ALT_PATH_SEPARATOR '\\'
|
||||
#define MAX_PATH 1024
|
||||
#define S_DIRMODE (S_IRWXU | S_IRWXG | S_IRWXO)
|
||||
#define LINE_ENDING "\n"
|
||||
|
||||
#endif
|
||||
|
||||
#ifndef SHUT_RDWR
|
||||
#define SHUT_RDWR 2
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -6,11 +6,14 @@ EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Release (no TLS)|Win32 = Release (no TLS)|Win32
|
||||
Release|Win32 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{41BFB691-0127-4391-9629-F1BA6740DDFE}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{41BFB691-0127-4391-9629-F1BA6740DDFE}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{41BFB691-0127-4391-9629-F1BA6740DDFE}.Release (no TLS)|Win32.ActiveCfg = Release (no TLS)|Win32
|
||||
{41BFB691-0127-4391-9629-F1BA6740DDFE}.Release (no TLS)|Win32.Build.0 = Release (no TLS)|Win32
|
||||
{41BFB691-0127-4391-9629-F1BA6740DDFE}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{41BFB691-0127-4391-9629-F1BA6740DDFE}.Release|Win32.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
|
||||
174
nzbget.vcproj
174
nzbget.vcproj
@@ -38,7 +38,7 @@
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
Optimization="0"
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;DEBUG"
|
||||
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;DEBUG;_WIN32_WINNT=0x0403"
|
||||
MinimalRebuild="true"
|
||||
BasicRuntimeChecks="3"
|
||||
RuntimeLibrary="1"
|
||||
@@ -58,7 +58,7 @@
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="WS2_32.Lib ole32.lib OleAut32.Lib comsuppwd.lib Advapi32.lib sigc-2.0d.lib par2d.lib"
|
||||
AdditionalDependencies="WS2_32.lib ole32.lib OleAut32.Lib comsuppwd.lib Advapi32.lib sigc-2.0d.lib par2d.lib libgnutls-26.lib libgcrypt-11.lib regex.lib zlib.lib $(NOINHERIT)"
|
||||
LinkIncremental="2"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
@@ -112,7 +112,8 @@
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_WIN32_WINNT=0x0403"
|
||||
ExceptionHandling="1"
|
||||
RuntimeLibrary="0"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
@@ -130,8 +131,83 @@
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="WS2_32.Lib ole32.lib OleAut32.Lib comsuppwd.lib Advapi32.lib sigc-2.0.lib par2.lib"
|
||||
LinkIncremental="2"
|
||||
AdditionalDependencies="WS2_32.lib ole32.lib OleAut32.Lib comsuppwd.lib Advapi32.lib sigc-2.0.lib par2.lib libgnutls-26.lib libgcrypt-11.lib regex.lib zlib.lib $(NOINHERIT)"
|
||||
LinkIncremental="0"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
OptimizeReferences="2"
|
||||
EnableCOMDATFolding="2"
|
||||
TargetMachine="1"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCALinkTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManifestTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXDCMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCBscMakeTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCFxCopTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCAppVerifierTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebDeploymentTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPostBuildEventTool"
|
||||
/>
|
||||
</Configuration>
|
||||
<Configuration
|
||||
Name="Release (no TLS)|Win32"
|
||||
OutputDirectory="..\bin"
|
||||
IntermediateDirectory="..\bin\Release (no TLS)"
|
||||
ConfigurationType="1"
|
||||
>
|
||||
<Tool
|
||||
Name="VCPreBuildEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCustomBuildTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCXMLDataGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCWebServiceProxyGeneratorTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCMIDLTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCCLCompilerTool"
|
||||
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_WIN32_WINNT=0x0403;DISABLE_TLS"
|
||||
ExceptionHandling="1"
|
||||
RuntimeLibrary="0"
|
||||
UsePrecompiledHeader="0"
|
||||
WarningLevel="3"
|
||||
Detect64BitPortabilityProblems="true"
|
||||
DebugInformationFormat="3"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCManagedResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCResourceCompilerTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCPreLinkEventTool"
|
||||
/>
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="WS2_32.lib ole32.lib OleAut32.Lib comsuppwd.lib Advapi32.lib sigc-2.0.lib par2.lib regex.lib zlib.lib $(NOINHERIT)"
|
||||
LinkIncremental="0"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
OptimizeReferences="2"
|
||||
@@ -175,6 +251,14 @@
|
||||
RelativePath=".\ArticleDownloader.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\BinRpc.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\BinRpc.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\ColoredFrontend.cpp"
|
||||
>
|
||||
@@ -251,14 +335,6 @@
|
||||
RelativePath=".\NCursesFrontend.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NetAddress.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NetAddress.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\NewsServer.cpp"
|
||||
>
|
||||
@@ -339,6 +415,14 @@
|
||||
RelativePath=".\QueueCoordinator.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\QueueEditor.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\QueueEditor.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\RemoteClient.cpp"
|
||||
>
|
||||
@@ -355,6 +439,30 @@
|
||||
RelativePath=".\RemoteServer.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Scanner.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Scanner.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Scheduler.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Scheduler.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\ScriptController.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\ScriptController.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\ServerPool.cpp"
|
||||
>
|
||||
@@ -371,6 +479,22 @@
|
||||
RelativePath=".\Thread.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\TLS.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\TLS.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\UrlCoordinator.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\UrlCoordinator.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\Util.cpp"
|
||||
>
|
||||
@@ -379,10 +503,34 @@
|
||||
RelativePath=".\Util.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\WebDownloader.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\WebDownloader.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\WebServer.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\WebServer.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\win32.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\XmlRpc.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\XmlRpc.h"
|
||||
>
|
||||
</File>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
|
||||
75
nzbgetd
Executable file
75
nzbgetd
Executable file
@@ -0,0 +1,75 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Script used to start and stop the nzbget usenet service
|
||||
#
|
||||
# Copyright (C) 2009 orbisvicis <orbisvicis@users.sourceforge.net>
|
||||
# Copyright (C) 2009-2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
#
|
||||
# This program is free software; you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation; either version 2 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program; if not, write to the Free Software
|
||||
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
#
|
||||
#
|
||||
|
||||
# --- CONFIGURATION -----------------------------------------------
|
||||
# Location of the nzbget executable
|
||||
NZBGET_BINARY="/usr/local/bin/nzbget"
|
||||
|
||||
# Additional options, e. g. config file location:
|
||||
# NZBGET_OPTS="-c /mnt/hdd/tools/nzbget/conf/nzbget.conf"
|
||||
NZBGET_OPTS=""
|
||||
# -----------------------------------------------------------------
|
||||
|
||||
|
||||
if [ -z "$1" ] ; then
|
||||
case `echo "$0" | sed 's:^.*/\(.*\):\1:g'` in
|
||||
S??*) rc="start" ;;
|
||||
K??*) rc="stop" ;;
|
||||
*) rc="usage" ;;
|
||||
esac
|
||||
else
|
||||
rc="$1"
|
||||
fi
|
||||
|
||||
case "$rc" in
|
||||
start)
|
||||
"$NZBGET_BINARY" $NZBGET_OPTS -D
|
||||
;;
|
||||
stop)
|
||||
"$NZBGET_BINARY" $NZBGET_OPTS -Q
|
||||
;;
|
||||
restart)
|
||||
"$NZBGET_BINARY" $NZBGET_OPTS -Q
|
||||
sleep 10 # since stop is backgrounded
|
||||
"$NZBGET_BINARY" $NZBGET_OPTS -D
|
||||
;;
|
||||
status)
|
||||
"$NZBGET_BINARY" $NZBGET_OPTS -L S
|
||||
;;
|
||||
pstatus)
|
||||
retval=$(pgrep -l -f nzbget > /dev/null ; echo $?)
|
||||
if [ "$retval" = "0" ] ; then
|
||||
echo " ------- nzbget *is* running -------"
|
||||
ps -Ho user,pid,cmd:32,pcpu -C nzbget
|
||||
exit 0
|
||||
else
|
||||
echo " ------- nzbget is *not* running -------"
|
||||
exit 0
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart|status|pstatus|usage}"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
exit 0
|
||||
1421
webui/config.js
Normal file
1421
webui/config.js
Normal file
File diff suppressed because it is too large
Load Diff
683
webui/downloads.js
Normal file
683
webui/downloads.js
Normal file
@@ -0,0 +1,683 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* In this module:
|
||||
* 1) Download tab;
|
||||
* 2) Functions for html generation for downloads, also used from other modules (edit and add dialogs).
|
||||
*/
|
||||
|
||||
/*** DOWNLOADS TAB ***********************************************************/
|
||||
|
||||
var Downloads = (new function($)
|
||||
{
|
||||
'use strict';
|
||||
|
||||
// Controls
|
||||
var $DownloadsTable;
|
||||
var $DownloadsTabBadge;
|
||||
var $DownloadsTabBadgeEmpty;
|
||||
var $DownloadQueueEmpty;
|
||||
var $DownloadsRecordsPerPage;
|
||||
|
||||
// State
|
||||
var notification = null;
|
||||
var updateTabInfo;
|
||||
var groups;
|
||||
var urls;
|
||||
|
||||
this.init = function(options)
|
||||
{
|
||||
updateTabInfo = options.updateTabInfo;
|
||||
|
||||
$DownloadsTable = $('#DownloadsTable');
|
||||
$DownloadsTabBadge = $('#DownloadsTabBadge');
|
||||
$DownloadsTabBadgeEmpty = $('#DownloadsTabBadgeEmpty');
|
||||
$DownloadQueueEmpty = $('#DownloadQueueEmpty');
|
||||
$DownloadsRecordsPerPage = $('#DownloadsRecordsPerPage');
|
||||
|
||||
var recordsPerPage = UISettings.read('$DownloadsRecordsPerPage', 10);
|
||||
$DownloadsRecordsPerPage.val(recordsPerPage);
|
||||
|
||||
$DownloadsTable.fasttable(
|
||||
{
|
||||
filterInput: $('#DownloadsTable_filter'),
|
||||
filterClearButton: $("#DownloadsTable_clearfilter"),
|
||||
pagerContainer: $('#DownloadsTable_pager'),
|
||||
infoContainer: $('#DownloadsTable_info'),
|
||||
headerCheck: $('#DownloadsTable > thead > tr:first-child'),
|
||||
filterCaseSensitive: false,
|
||||
infoEmpty: ' ', // this is to disable default message "No records"
|
||||
pageSize: recordsPerPage,
|
||||
maxPages: UISettings.miniTheme ? 1 : 5,
|
||||
pageDots: !UISettings.miniTheme,
|
||||
fillFieldsCallback: fillFieldsCallback,
|
||||
renderCellCallback: renderCellCallback,
|
||||
updateInfoCallback: updateInfo
|
||||
});
|
||||
|
||||
$DownloadsTable.on('click', 'a', itemClick);
|
||||
$DownloadsTable.on('click', 'tbody div.check',
|
||||
function(event) { $DownloadsTable.fasttable('itemCheckClick', this.parentNode.parentNode, event); });
|
||||
$DownloadsTable.on('click', 'thead div.check',
|
||||
function() { $DownloadsTable.fasttable('titleCheckClick') });
|
||||
$DownloadsTable.on('mousedown', Util.disableShiftMouseDown);
|
||||
}
|
||||
|
||||
this.applyTheme = function()
|
||||
{
|
||||
$DownloadsTable.fasttable('setPageSize', UISettings.read('$DownloadsRecordsPerPage', 10),
|
||||
UISettings.miniTheme ? 1 : 5, !UISettings.miniTheme);
|
||||
}
|
||||
|
||||
this.update = function()
|
||||
{
|
||||
RPC.call('listgroups', [], groups_loaded);
|
||||
}
|
||||
|
||||
function groups_loaded(_groups)
|
||||
{
|
||||
groups = _groups;
|
||||
RPC.call('postqueue', [100], posts_loaded);
|
||||
}
|
||||
|
||||
function posts_loaded(posts)
|
||||
{
|
||||
mergequeues(posts);
|
||||
prepare();
|
||||
RPC.call('urlqueue', [], urls_loaded);
|
||||
}
|
||||
|
||||
function urls_loaded(_urls)
|
||||
{
|
||||
urls = _urls;
|
||||
RPC.next();
|
||||
}
|
||||
|
||||
function mergequeues(posts)
|
||||
{
|
||||
var lastPPItemIndex = -1;
|
||||
for (var i=0, il=posts.length; i < il; i++)
|
||||
{
|
||||
var post = posts[i];
|
||||
var found = false;
|
||||
for (var j=0, jl=groups.length; j < jl; j++)
|
||||
{
|
||||
var group = groups[j];
|
||||
if (group.NZBID === post.NZBID)
|
||||
{
|
||||
found = true;
|
||||
if (!group.post)
|
||||
{
|
||||
group.post = post;
|
||||
}
|
||||
lastPPItemIndex = j;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
// create a virtual group-item
|
||||
var group = {post: post};
|
||||
group.NZBID = post.NZBID;
|
||||
group.NZBName = post.NZBName;
|
||||
group.MaxPriority = 0;
|
||||
group.Category = '';
|
||||
group.LastID = 0;
|
||||
group.MinPostTime = 0;
|
||||
group.FileSizeMB = 0;
|
||||
group.FileSizeLo = 0;
|
||||
group.RemainingSizeMB = 0;
|
||||
group.RemainingSizeLo = 0;
|
||||
group.PausedSizeMB = 0;
|
||||
group.PausedSizeLo = 0;
|
||||
group.FileCount = 0;
|
||||
group.RemainingFileCount = 0;
|
||||
group.RemainingParCount = 0;
|
||||
|
||||
// insert it after the last pp-item
|
||||
if (lastPPItemIndex > -1)
|
||||
{
|
||||
groups.splice(lastPPItemIndex + 1, 0, group);
|
||||
}
|
||||
else
|
||||
{
|
||||
groups.unshift(group);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function prepare()
|
||||
{
|
||||
for (var j=0, jl=groups.length; j < jl; j++)
|
||||
{
|
||||
detectStatus(groups[j]);
|
||||
}
|
||||
}
|
||||
|
||||
this.redraw = function()
|
||||
{
|
||||
redraw_table();
|
||||
|
||||
Util.show($DownloadsTabBadge, groups.length > 0);
|
||||
Util.show($DownloadsTabBadgeEmpty, groups.length === 0 && UISettings.miniTheme);
|
||||
Util.show($DownloadQueueEmpty, groups.length === 0);
|
||||
}
|
||||
|
||||
/*** TABLE *************************************************************************/
|
||||
|
||||
function redraw_table()
|
||||
{
|
||||
var data = [];
|
||||
|
||||
for (var i=0; i < groups.length; i++)
|
||||
{
|
||||
var group = groups[i];
|
||||
|
||||
var nametext = group.NZBName;
|
||||
var priority = DownloadsUI.buildPriorityText(group.MaxPriority);
|
||||
var estimated = DownloadsUI.buildEstimated(group);
|
||||
var age = Util.formatAge(group.MinPostTime + UISettings.timeZoneCorrection*60*60);
|
||||
var size = Util.formatSizeMB(group.FileSizeMB, group.FileSizeLo);
|
||||
var remaining = Util.formatSizeMB(group.RemainingSizeMB-group.PausedSizeMB, group.RemainingSizeLo-group.PausedSizeLo);
|
||||
|
||||
var item =
|
||||
{
|
||||
id: group.NZBID,
|
||||
group: group,
|
||||
data: { age: age, estimated: estimated, size: size, remaining: remaining },
|
||||
search: group.status + ' ' + nametext + ' ' + priority + ' ' + group.Category + ' ' + age + ' ' + size + ' ' + remaining + ' ' + estimated
|
||||
};
|
||||
|
||||
data.push(item);
|
||||
}
|
||||
|
||||
$DownloadsTable.fasttable('update', data);
|
||||
}
|
||||
|
||||
function fillFieldsCallback(item)
|
||||
{
|
||||
var group = item.group;
|
||||
|
||||
var status = DownloadsUI.buildStatus(group);
|
||||
var priority = DownloadsUI.buildPriority(group.MaxPriority);
|
||||
var progresslabel = DownloadsUI.buildProgressLabel(group);
|
||||
var progress = DownloadsUI.buildProgress(group, item.data.size, item.data.remaining, item.data.estimated);
|
||||
|
||||
var name = '<a href="#" nzbid="' + group.NZBID + '">' + Util.textToHtml(Util.formatNZBName(group.NZBName)) + '</a>';
|
||||
var category = Util.textToHtml(group.Category);
|
||||
|
||||
if (!UISettings.miniTheme)
|
||||
{
|
||||
var info = name + ' ' + priority + progresslabel;
|
||||
item.fields = ['<div class="check img-check"></div>', status, info, category, item.data.age, progress, item.data.estimated];
|
||||
}
|
||||
else
|
||||
{
|
||||
var info = '<div class="check img-check"></div><span class="row-title">' + name + '</span>' +
|
||||
' ' + (group.status === 'queued' ? '' : status) + ' ' + priority;
|
||||
if (category)
|
||||
{
|
||||
info += ' <span class="label label-status">' + category + '</span>';
|
||||
}
|
||||
|
||||
if (progresslabel)
|
||||
{
|
||||
progress = '<div class="downloads-progresslabel">' + progresslabel + '</div>' + progress;
|
||||
}
|
||||
item.fields = [info, progress];
|
||||
}
|
||||
}
|
||||
|
||||
function renderCellCallback(cell, index, item)
|
||||
{
|
||||
if (4 <= index && index <= 7)
|
||||
{
|
||||
cell.className = 'text-right';
|
||||
}
|
||||
}
|
||||
|
||||
function detectStatus(group)
|
||||
{
|
||||
group.paused = (group.PausedSizeLo != 0) && (group.RemainingSizeLo == group.PausedSizeLo);
|
||||
group.postprocess = group.post !== undefined;
|
||||
if (group.postprocess)
|
||||
{
|
||||
switch (group.post.Stage)
|
||||
{
|
||||
case 'QUEUED': group.status = 'pp-queued'; break;
|
||||
case 'LOADING_PARS': group.status = 'checking'; break;
|
||||
case 'VERIFYING_SOURCES': group.status = 'checking'; break;
|
||||
case 'REPAIRING': group.status = 'repairing'; break;
|
||||
case 'VERIFYING_REPAIRED': group.status = 'verifying'; break;
|
||||
case 'EXECUTING_SCRIPT': group.status = 'unpacking'; break;
|
||||
case 'FINISHED': group.status = 'finished'; break;
|
||||
default: group.status = 'error: ' + group.post.Stage; break;
|
||||
}
|
||||
}
|
||||
else if (group.ActiveDownloads > 0)
|
||||
{
|
||||
group.status = 'downloading';
|
||||
}
|
||||
else if (group.paused)
|
||||
{
|
||||
group.status = 'paused';
|
||||
}
|
||||
else
|
||||
{
|
||||
group.status = 'queued';
|
||||
}
|
||||
}
|
||||
|
||||
this.recordsPerPageChange = function()
|
||||
{
|
||||
var val = $DownloadsRecordsPerPage.val();
|
||||
UISettings.write('$DownloadsRecordsPerPage', val);
|
||||
$DownloadsTable.fasttable('setPageSize', val);
|
||||
}
|
||||
|
||||
function updateInfo(stat)
|
||||
{
|
||||
updateTabInfo($DownloadsTabBadge, stat);
|
||||
}
|
||||
|
||||
/*** EDIT ******************************************************/
|
||||
|
||||
function itemClick()
|
||||
{
|
||||
var nzbid = $(this).attr('nzbid');
|
||||
$(this).blur();
|
||||
DownloadsEditDialog.showModal(nzbid, groups);
|
||||
}
|
||||
|
||||
function editCompleted()
|
||||
{
|
||||
Refresher.update();
|
||||
if (notification)
|
||||
{
|
||||
Notification.show(notification);
|
||||
notification = null;
|
||||
}
|
||||
}
|
||||
|
||||
/*** CHECKMARKS ******************************************************/
|
||||
|
||||
function checkBuildEditIDList(UseLastID)
|
||||
{
|
||||
var checkedRows = $DownloadsTable.fasttable('checkedRows');
|
||||
|
||||
var hasIDs = false;
|
||||
var checkedEditIDs = [];
|
||||
for (var i = 0; i < groups.length; i++)
|
||||
{
|
||||
var group = groups[i];
|
||||
if (checkedRows.indexOf(group.NZBID) > -1)
|
||||
{
|
||||
if (group.postprocess)
|
||||
{
|
||||
Notification.show('#Notif_Downloads_CheckPostProcess');
|
||||
return null;
|
||||
}
|
||||
|
||||
checkedEditIDs.push(UseLastID ? group.LastID : group.NZBID);
|
||||
}
|
||||
}
|
||||
|
||||
if (checkedEditIDs.length === 0)
|
||||
{
|
||||
Notification.show('#Notif_Downloads_Select');
|
||||
return null;
|
||||
}
|
||||
|
||||
return checkedEditIDs;
|
||||
}
|
||||
|
||||
/*** TOOLBAR: SELECTED ITEMS ******************************************************/
|
||||
|
||||
this.editClick = function()
|
||||
{
|
||||
var checkedEditIDs = checkBuildEditIDList(false);
|
||||
if (!checkedEditIDs)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkedEditIDs.length == 1)
|
||||
{
|
||||
DownloadsEditDialog.showModal(checkedEditIDs[0], groups);
|
||||
}
|
||||
else
|
||||
{
|
||||
DownloadsMultiDialog.showModal(checkedEditIDs, groups);
|
||||
}
|
||||
}
|
||||
|
||||
this.mergeClick = function()
|
||||
{
|
||||
var checkedEditIDs = checkBuildEditIDList(false);
|
||||
if (!checkedEditIDs)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (checkedEditIDs.length < 2)
|
||||
{
|
||||
Notification.show('#Notif_Downloads_SelectMulti');
|
||||
return;
|
||||
}
|
||||
|
||||
DownloadsMergeDialog.showModal(checkedEditIDs, groups);
|
||||
}
|
||||
|
||||
this.pauseClick = function()
|
||||
{
|
||||
var checkedEditIDs = checkBuildEditIDList(true);
|
||||
if (!checkedEditIDs)
|
||||
{
|
||||
return;
|
||||
}
|
||||
notification = '#Notif_Downloads_Paused';
|
||||
RPC.call('editqueue', ['GroupPause', 0, '', checkedEditIDs], editCompleted);
|
||||
}
|
||||
|
||||
this.resumeClick = function()
|
||||
{
|
||||
var checkedEditIDs = checkBuildEditIDList(true);
|
||||
if (!checkedEditIDs)
|
||||
{
|
||||
return;
|
||||
}
|
||||
notification = '#Notif_Downloads_Resumed';
|
||||
RPC.call('editqueue', ['GroupResume', 0, '', checkedEditIDs], function()
|
||||
{
|
||||
RPC.call('editqueue', ['GroupPauseExtraPars', 0, '', checkedEditIDs], editCompleted);
|
||||
});
|
||||
}
|
||||
|
||||
this.deleteClick = function()
|
||||
{
|
||||
var checkedRows = $DownloadsTable.fasttable('checkedRows');
|
||||
var downloadIDs = [];
|
||||
var postprocessIDs = [];
|
||||
for (var i = 0; i < groups.length; i++)
|
||||
{
|
||||
var group = groups[i];
|
||||
if (checkedRows.indexOf(group.NZBID) > -1)
|
||||
{
|
||||
if (group.postprocess)
|
||||
{
|
||||
postprocessIDs.push(group.post.ID);
|
||||
}
|
||||
if (group.LastID > 0)
|
||||
{
|
||||
downloadIDs.push(group.LastID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (downloadIDs.length === 0 && postprocessIDs.length === 0)
|
||||
{
|
||||
Notification.show('#Notif_Downloads_Select');
|
||||
return;
|
||||
}
|
||||
|
||||
notification = '#Notif_Downloads_Deleted';
|
||||
|
||||
var deletePosts = function()
|
||||
{
|
||||
if (postprocessIDs.length > 0)
|
||||
{
|
||||
RPC.call('editqueue', ['PostDelete', 0, '', postprocessIDs], editCompleted);
|
||||
}
|
||||
else
|
||||
{
|
||||
editCompleted();
|
||||
}
|
||||
};
|
||||
|
||||
var deleteGroups = function()
|
||||
{
|
||||
if (downloadIDs.length > 0)
|
||||
{
|
||||
RPC.call('editqueue', ['GroupDelete', 0, '', downloadIDs], deletePosts);
|
||||
}
|
||||
else
|
||||
{
|
||||
deletePosts();
|
||||
}
|
||||
};
|
||||
|
||||
ConfirmDialog.showModal('DownloadsDeleteConfirmDialog', deleteGroups);
|
||||
}
|
||||
|
||||
this.moveClick = function(action)
|
||||
{
|
||||
var checkedEditIDs = checkBuildEditIDList(true);
|
||||
if (!checkedEditIDs)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var EditAction = '';
|
||||
var EditOffset = 0;
|
||||
switch (action)
|
||||
{
|
||||
case 'top':
|
||||
EditAction = 'GroupMoveTop';
|
||||
checkedEditIDs.reverse();
|
||||
break;
|
||||
case 'bottom':
|
||||
EditAction = 'GroupMoveBottom';
|
||||
break;
|
||||
case 'up':
|
||||
EditAction = 'GroupMoveOffset';
|
||||
EditOffset = -1;
|
||||
break;
|
||||
case 'down':
|
||||
EditAction = 'GroupMoveOffset';
|
||||
EditOffset = 1;
|
||||
checkedEditIDs.reverse();
|
||||
break;
|
||||
}
|
||||
|
||||
notification = '';
|
||||
RPC.call('editqueue', [EditAction, EditOffset, '', checkedEditIDs], editCompleted);
|
||||
}
|
||||
}(jQuery));
|
||||
|
||||
|
||||
/*** FUNCTIONS FOR HTML GENERATION (also used from other modules) *****************************/
|
||||
|
||||
var DownloadsUI = (new function($)
|
||||
{
|
||||
'use strict';
|
||||
|
||||
this.fillPriorityCombo = function(combo)
|
||||
{
|
||||
combo.empty();
|
||||
combo.append('<option value="-100">very low</option>');
|
||||
combo.append('<option value="-50">low</option>');
|
||||
combo.append('<option value="0">normal</option>');
|
||||
combo.append('<option value="50">high</option>');
|
||||
combo.append('<option value="100">very high</option>');
|
||||
}
|
||||
|
||||
this.fillCategoryCombo = function(combo)
|
||||
{
|
||||
combo.empty();
|
||||
combo.append('<option></option>');
|
||||
|
||||
for (var i=0; i < Options.categories.length; i++)
|
||||
{
|
||||
combo.append($('<option></option>').text(Options.categories[i]));
|
||||
}
|
||||
}
|
||||
|
||||
this.buildStatus = function(group)
|
||||
{
|
||||
if (group.postprocess && group.status !== 'pp-queued')
|
||||
{
|
||||
if (Status.status.PostPaused)
|
||||
{
|
||||
return '<span class="label label-status label-warning">' + group.status + '</span>';
|
||||
}
|
||||
else
|
||||
{
|
||||
return '<span class="label label-status label-success">' + group.status + '</span>';
|
||||
}
|
||||
}
|
||||
switch (group.status)
|
||||
{
|
||||
case 'pp-queued': return '<span class="label label-status">pp-queued</span>';
|
||||
case 'downloading': return '<span class="label label-status label-success">downloading</span>';
|
||||
case 'paused': return '<span class="label label-status label-warning">paused</span>';
|
||||
case 'queued': return '<span class="label label-status">queued</span>';
|
||||
default: return '<span class="label label-status label-important">internal error(' + group.status + ')</span>';
|
||||
}
|
||||
}
|
||||
|
||||
this.buildProgress = function(group, totalsize, remaining, estimated)
|
||||
{
|
||||
if (group.status === 'downloading' || (group.postprocess && !Status.status.PostPaused))
|
||||
{
|
||||
var kind = 'progress-success';
|
||||
}
|
||||
else if (group.status === 'paused' || (group.postprocess && Status.status.PostPaused))
|
||||
{
|
||||
var kind = 'progress-warning';
|
||||
}
|
||||
else
|
||||
{
|
||||
var kind = 'progress-none';
|
||||
}
|
||||
|
||||
var totalMB = group.FileSizeMB-group.PausedSizeMB;
|
||||
var remainingMB = group.RemainingSizeMB-group.PausedSizeMB;
|
||||
var percent = Math.round((totalMB - remainingMB) / totalMB * 100);
|
||||
var progress = '';
|
||||
|
||||
if (group.postprocess)
|
||||
{
|
||||
totalsize = '';
|
||||
remaining = '';
|
||||
percent = Math.round(group.post.StageProgress / 10);
|
||||
}
|
||||
|
||||
if (!UISettings.miniTheme)
|
||||
{
|
||||
progress =
|
||||
'<div class="progress-block">'+
|
||||
'<div class="progress progress-striped ' + kind + '">'+
|
||||
'<div class="bar" style="width:' + percent + '%;"></div>'+
|
||||
'</div>'+
|
||||
'<div class="bar-text-left">' + totalsize + '</div>'+
|
||||
'<div class="bar-text-right">' + remaining + '</div>'+
|
||||
'</div>';
|
||||
}
|
||||
else
|
||||
{
|
||||
progress =
|
||||
'<div class="progress-block">'+
|
||||
'<div class="progress progress-striped ' + kind + '">'+
|
||||
'<div class="bar" style="width:' + percent + '%;"></div>'+
|
||||
'</div>'+
|
||||
'<div class="bar-text-left">' + (totalsize !== '' ? 'total ' : '') + totalsize + '</div>'+
|
||||
'<div class="bar-text-center">' + (estimated !== '' ? '[' + estimated + ']': '') + '</div>'+
|
||||
'<div class="bar-text-right">' + remaining + (remaining !== '' ? ' left' : '') + '</div>'+
|
||||
'</div>';
|
||||
}
|
||||
|
||||
return progress;
|
||||
}
|
||||
|
||||
this.buildEstimated = function(group)
|
||||
{
|
||||
if (group.postprocess)
|
||||
{
|
||||
if (group.post.StageProgress > 0)
|
||||
{
|
||||
return Util.formatTimeHMS(group.post.StageTimeSec / group.post.StageProgress * (1000 - group.post.StageProgress));
|
||||
}
|
||||
}
|
||||
else if (!group.paused && Status.status.DownloadRate > 0)
|
||||
{
|
||||
return Util.formatTimeLeft((group.RemainingSizeMB-group.PausedSizeMB)*1024/(Status.status.DownloadRate/1024));
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
this.buildProgressLabel = function(group)
|
||||
{
|
||||
var text = '';
|
||||
if (group.postprocess && !Status.status.PostPaused && group.post.Stage !== 'REPAIRING')
|
||||
{
|
||||
if (group.post.Log && group.post.Log.length > 0)
|
||||
{
|
||||
text = group.post.Log[group.post.Log.length-1].Text;
|
||||
}
|
||||
else if (group.post.ProgressLabel !== '')
|
||||
{
|
||||
text = group.post.ProgressLabel;
|
||||
}
|
||||
}
|
||||
|
||||
return text !== '' ? ' <span class="label label-success">' + text + '</span>' : '';
|
||||
}
|
||||
|
||||
this.buildPriorityText = function(priority)
|
||||
{
|
||||
switch (priority)
|
||||
{
|
||||
case 0: return '';
|
||||
case 100: return 'very high priority';
|
||||
case 50: return 'high priority';
|
||||
case -50: return 'low priority';
|
||||
case -100: return 'very low priority';
|
||||
default: return 'priority: ' + priority;
|
||||
}
|
||||
}
|
||||
|
||||
this.buildPriority = function(priority)
|
||||
{
|
||||
switch (priority)
|
||||
{
|
||||
case 0: return '';
|
||||
case 100: return ' <span class="label label-priority label-important">very high priority</span>';
|
||||
case 50: return ' <span class="label label-priority label-important">high priority</span>';
|
||||
case -50: return ' <span class="label label-priority label-info">low priority</span>';
|
||||
case -100: return ' <span class="label label-priority label-info">very low priority</span>';
|
||||
}
|
||||
if (priority > 0)
|
||||
{
|
||||
return ' <span class="label label-priority label-important">priority: ' + priority + '</span>';
|
||||
}
|
||||
else if (priority < 0)
|
||||
{
|
||||
return ' <span class="label label-priority label-info">priority: ' + priority + '</span>';
|
||||
}
|
||||
}
|
||||
}(jQuery));
|
||||
1048
webui/edit.js
Normal file
1048
webui/edit.js
Normal file
File diff suppressed because it is too large
Load Diff
787
webui/fasttable.js
Normal file
787
webui/fasttable.js
Normal file
@@ -0,0 +1,787 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* Some code was borrowed from:
|
||||
* 1. Greg Weber's uiTableFilter jQuery plugin (http://gregweber.info/projects/uitablefilter)
|
||||
* 2. Denny Ferrassoli & Charles Christolini's TypeWatch jQuery plugin (http://github.com/dennyferra/TypeWatch)
|
||||
* 3. Justin Britten's tablesorterFilter jQuery plugin (http://www.justinbritten.com/work/2008/08/tablesorter-filter-results-based-on-search-string/)
|
||||
* 4. Allan Jardine's Bootstrap Pagination jQuery plugin for DataTables (http://datatables.net/)
|
||||
*/
|
||||
|
||||
/*
|
||||
* In this module:
|
||||
* HTML tables with:
|
||||
* 1) very fast content updates;
|
||||
* 2) automatic pagination;
|
||||
* 3) search/filtering.
|
||||
*
|
||||
* What makes it unique and fast?
|
||||
* The tables are designed to be updated very often (up to 10 times per second). This has two challenges:
|
||||
* 1) updating of whole content is slow because the DOM updates are slow.
|
||||
* 2) if the DOM is updated during user interaction the user input is not processed correctly.
|
||||
* For example if the table is updated after the user pressed mouse key but before he/she released
|
||||
* the key, the click is not processed because the element, on which the click was performed,
|
||||
* doesn't exist after the update of DOM anymore.
|
||||
*
|
||||
* How Fasttable solves these problems? The solutions is to update only rows and cells,
|
||||
* which were changed by keeping the unchanged DOM-elements.
|
||||
*
|
||||
* Important: the UI of table must be designed in a way, that the cells which are frequently changed
|
||||
* (like remaining download size) should not be clickable, whereas the cells which are rarely changed
|
||||
* (e. g. Download name) can be clickable.
|
||||
*/
|
||||
|
||||
(function($) {
|
||||
|
||||
'use strict';
|
||||
|
||||
$.fn.fasttable = function(method)
|
||||
{
|
||||
if (methods[method])
|
||||
{
|
||||
return methods[method].apply( this, Array.prototype.slice.call( arguments, 1 ));
|
||||
}
|
||||
else if ( typeof method === 'object' || ! method )
|
||||
{
|
||||
return methods.init.apply( this, arguments );
|
||||
}
|
||||
else
|
||||
{
|
||||
$.error( 'Method ' + method + ' does not exist on jQuery.fasttable' );
|
||||
}
|
||||
};
|
||||
|
||||
var methods =
|
||||
{
|
||||
init : function(options)
|
||||
{
|
||||
return this.each(function()
|
||||
{
|
||||
var $this = $(this);
|
||||
var data = $this.data('fasttable');
|
||||
|
||||
// If the plugin hasn't been initialized yet
|
||||
if (!data)
|
||||
{
|
||||
/*
|
||||
Do more setup stuff here
|
||||
*/
|
||||
|
||||
var config = {};
|
||||
config = $.extend(config, defaults, options);
|
||||
|
||||
config.filterInput = $(config.filterInput);
|
||||
config.filterClearButton = $(config.filterClearButton);
|
||||
config.pagerContainer = $(config.pagerContainer);
|
||||
config.infoContainer = $(config.infoContainer);
|
||||
config.headerCheck = $(config.headerCheck);
|
||||
|
||||
// Create a timer which gets reset upon every keyup event.
|
||||
// Perform filter only when the timer's wait is reached (user finished typing or paused long enough to elapse the timer).
|
||||
// Do not perform the filter is the query has not changed.
|
||||
// Immediately perform the filter if the ENTER key is pressed.
|
||||
|
||||
var timer;
|
||||
|
||||
config.filterInput.keyup(function()
|
||||
{
|
||||
var timerWait = 500;
|
||||
var overrideBool = false;
|
||||
var inputBox = this;
|
||||
|
||||
// Was ENTER pushed?
|
||||
if (inputBox.keyCode == 13)
|
||||
{
|
||||
timerWait = 1;
|
||||
overrideBool = true;
|
||||
}
|
||||
|
||||
var timerCallback = function()
|
||||
{
|
||||
var value = inputBox.value;
|
||||
var data = $this.data('fasttable');
|
||||
|
||||
if ((value != data.lastFilter) || (overrideBool))
|
||||
{
|
||||
data.lastFilter = value;
|
||||
if (data.content)
|
||||
{
|
||||
data.curPage = 1;
|
||||
refresh(data);
|
||||
}
|
||||
if (data.config.filterInputCallback)
|
||||
{
|
||||
data.config.filterInputCallback(value);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Reset the timer
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(timerCallback, timerWait);
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
config.filterClearButton.click(function()
|
||||
{
|
||||
var data = $this.data('fasttable');
|
||||
data.lastFilter = '';
|
||||
data.config.filterInput.val('');
|
||||
if (data.content)
|
||||
{
|
||||
refresh(data);
|
||||
}
|
||||
if (data.config.filterClearCallback)
|
||||
{
|
||||
data.config.filterClearCallback();
|
||||
}
|
||||
});
|
||||
|
||||
config.pagerContainer.on('click', 'li', function (e)
|
||||
{
|
||||
e.preventDefault();
|
||||
var data = $this.data('fasttable');
|
||||
var pageNum = $(this).text();
|
||||
if (pageNum.indexOf('Prev') > -1)
|
||||
{
|
||||
data.curPage--;
|
||||
}
|
||||
else if (pageNum.indexOf('Next') > -1)
|
||||
{
|
||||
data.curPage++;
|
||||
}
|
||||
else if (isNaN(parseInt(pageNum)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.curPage = parseInt(pageNum);
|
||||
}
|
||||
refresh(data);
|
||||
});
|
||||
|
||||
$this.data('fasttable', {
|
||||
target : $this,
|
||||
config : config,
|
||||
pageSize : parseInt(config.pageSize),
|
||||
maxPages : parseInt(config.maxPages),
|
||||
pageDots : Util.parseBool(config.pageDots),
|
||||
curPage : 1,
|
||||
checkedRows: [],
|
||||
lastClickedRowID: 0
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
destroy : function()
|
||||
{
|
||||
return this.each(function()
|
||||
{
|
||||
var $this = $(this);
|
||||
var data = $this.data('fasttable');
|
||||
|
||||
// Namespacing FTW
|
||||
$(window).unbind('.fasttable');
|
||||
$this.removeData('fasttable');
|
||||
});
|
||||
},
|
||||
|
||||
update : updateContent,
|
||||
|
||||
setPageSize : setPageSize,
|
||||
|
||||
setCurPage : setCurPage,
|
||||
|
||||
filteredContent : function()
|
||||
{
|
||||
return $(this).data('fasttable').filteredContent;
|
||||
},
|
||||
|
||||
checkedRows : function()
|
||||
{
|
||||
return $(this).data('fasttable').checkedRows;
|
||||
},
|
||||
|
||||
itemCheckClick : itemCheckClick,
|
||||
|
||||
titleCheckClick : titleCheckClick
|
||||
};
|
||||
|
||||
function has_words(str, words, caseSensitive)
|
||||
{
|
||||
var text = caseSensitive ? str : str.toLowerCase();
|
||||
|
||||
for (var i = 0; i < words.length; i++)
|
||||
{
|
||||
if (text.indexOf(words[i]) === -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function updateContent(content)
|
||||
{
|
||||
var data = $(this).data('fasttable');
|
||||
if (content)
|
||||
{
|
||||
data.content = content;
|
||||
}
|
||||
refresh(data);
|
||||
}
|
||||
|
||||
function refresh(data)
|
||||
{
|
||||
refilter(data);
|
||||
validateChecks(data);
|
||||
updatePager(data);
|
||||
updateInfo(data);
|
||||
updateTable(data);
|
||||
}
|
||||
|
||||
function refilter(data)
|
||||
{
|
||||
var filterInput = data.config.filterInput;
|
||||
var phrase = filterInput.length > 0 ? filterInput.val() : '';
|
||||
var caseSensitive = data.config.filterCaseSensitive;
|
||||
var words = caseSensitive ? phrase.split(' ') : phrase.toLowerCase().split(' ');
|
||||
|
||||
if (words.length === 1 && words[0] === '')
|
||||
{
|
||||
data.filteredContent = data.content;
|
||||
}
|
||||
else
|
||||
{
|
||||
data.filteredContent = [];
|
||||
for (var i = 0; i < data.content.length; i++)
|
||||
{
|
||||
var item = data.content[i];
|
||||
if (item.search === undefined && data.config.fillSearchCallback)
|
||||
{
|
||||
data.config.fillSearchCallback(item);
|
||||
}
|
||||
|
||||
if (has_words(item.search, words, caseSensitive))
|
||||
{
|
||||
data.filteredContent.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function updateTable(data)
|
||||
{
|
||||
var oldTable = data.target[0];
|
||||
var newTable = buildTBody(data);
|
||||
updateTBody(data, oldTable, newTable);
|
||||
}
|
||||
|
||||
function buildTBody(data)
|
||||
{
|
||||
var table = $('<table><tbody></tbody></table>')[0];
|
||||
for (var i=0; i < data.pageContent.length; i++)
|
||||
{
|
||||
var item = data.pageContent[i];
|
||||
|
||||
var row = table.insertRow(table.rows.length);
|
||||
|
||||
row.fasttableID = item.id;
|
||||
if (data.checkedRows.indexOf(item.id) > -1)
|
||||
{
|
||||
row.className = 'checked';
|
||||
}
|
||||
if (data.config.renderRowCallback)
|
||||
{
|
||||
data.config.renderRowCallback(row, item);
|
||||
}
|
||||
|
||||
if (!item.fields)
|
||||
{
|
||||
if (data.config.fillFieldsCallback)
|
||||
{
|
||||
data.config.fillFieldsCallback(item);
|
||||
}
|
||||
else
|
||||
{
|
||||
item.fields = [];
|
||||
}
|
||||
}
|
||||
|
||||
for (var j=0; j < item.fields.length; j++)
|
||||
{
|
||||
var cell = row.insertCell(row.cells.length);
|
||||
cell.innerHTML = item.fields[j];
|
||||
if (data.config.renderCellCallback)
|
||||
{
|
||||
data.config.renderCellCallback(cell, j, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
titleCheckRedraw(data);
|
||||
|
||||
if (data.config.renderTableCallback)
|
||||
{
|
||||
data.config.renderTableCallback(table);
|
||||
}
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
function updateTBody(data, oldTable, newTable)
|
||||
{
|
||||
var oldTRs = oldTable.rows;
|
||||
var newTRs = newTable.rows;
|
||||
var oldTBody = $('tbody', oldTable)[0];
|
||||
var oldTRsLength = oldTRs.length - (data.config.hasHeader ? 1 : 0); // evlt. skip header row
|
||||
var newTRsLength = newTRs.length;
|
||||
|
||||
for (var i=0; i < newTRs.length; )
|
||||
{
|
||||
var newTR = newTRs[i];
|
||||
|
||||
if (i < oldTRsLength)
|
||||
{
|
||||
// update existing row
|
||||
var oldTR = oldTRs[i + (data.config.hasHeader ? 1 : 0)]; // evlt. skip header row
|
||||
var oldTDs = oldTR.cells;
|
||||
var newTDs = newTR.cells;
|
||||
|
||||
oldTR.className = newTR.className;
|
||||
oldTR.fasttableID = newTR.fasttableID;
|
||||
|
||||
for (var j=0, n = 0; j < oldTDs.length; j++, n++)
|
||||
{
|
||||
var oldTD = oldTDs[j];
|
||||
var newTD = newTDs[n];
|
||||
var oldHtml = oldTD.outerHTML;
|
||||
var newHtml = newTD.outerHTML;
|
||||
if (oldHtml !== newHtml)
|
||||
{
|
||||
oldTR.replaceChild(newTD, oldTD);
|
||||
n--;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// add new row
|
||||
oldTBody.appendChild(newTR);
|
||||
}
|
||||
}
|
||||
|
||||
var maxTRs = newTRsLength + (data.config.hasHeader ? 1 : 0); // evlt. skip header row;
|
||||
while (oldTRs.length > maxTRs)
|
||||
{
|
||||
oldTable.deleteRow(oldTRs.length - 1);
|
||||
}
|
||||
}
|
||||
|
||||
function updatePager(data)
|
||||
{
|
||||
data.pageCount = Math.ceil(data.filteredContent.length / data.pageSize);
|
||||
if (data.curPage < 1)
|
||||
{
|
||||
data.curPage = 1;
|
||||
}
|
||||
if (data.curPage > data.pageCount)
|
||||
{
|
||||
data.curPage = data.pageCount;
|
||||
}
|
||||
|
||||
var startIndex = (data.curPage - 1) * data.pageSize;
|
||||
data.pageContent = data.filteredContent.slice(startIndex, startIndex + data.pageSize);
|
||||
|
||||
var pagerObj = data.config.pagerContainer;
|
||||
var pagerHtml = buildPagerHtml(data);
|
||||
|
||||
var oldPager = pagerObj[0];
|
||||
var newPager = $(pagerHtml)[0];
|
||||
|
||||
updatePagerContent(data, oldPager, newPager);
|
||||
}
|
||||
|
||||
function buildPagerHtml(data)
|
||||
{
|
||||
var iListLength = data.maxPages;
|
||||
var iStart, iEnd, iHalf = Math.floor(iListLength/2);
|
||||
|
||||
if (data.pageCount < iListLength)
|
||||
{
|
||||
iStart = 1;
|
||||
iEnd = data.pageCount;
|
||||
}
|
||||
else if (data.curPage -1 <= iHalf)
|
||||
{
|
||||
iStart = 1;
|
||||
iEnd = iListLength;
|
||||
}
|
||||
else if (data.curPage - 1 >= (data.pageCount-iHalf))
|
||||
{
|
||||
iStart = data.pageCount - iListLength + 1;
|
||||
iEnd = data.pageCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
iStart = data.curPage - 1 - iHalf + 1;
|
||||
iEnd = iStart + iListLength - 1;
|
||||
}
|
||||
|
||||
var pager = '<ul>';
|
||||
pager += '<li' + (data.curPage === 1 || data.curPage === 0 ? ' class="disabled"' : '') + '><a href="#">← Prev</a></li>';
|
||||
|
||||
if (iStart > 1)
|
||||
{
|
||||
pager += '<li><a href="#">1</a></li>';
|
||||
if (iStart > 2 && data.pageDots)
|
||||
{
|
||||
pager += '<li class="disabled"><a href="#">…</a></li>';
|
||||
}
|
||||
}
|
||||
|
||||
for (var j=iStart; j<=iEnd; j++)
|
||||
{
|
||||
pager += '<li' + ((j===data.curPage) ? ' class="active"' : '') + '><a href="#">' + j + '</a></li>';
|
||||
}
|
||||
|
||||
if (iEnd != data.pageCount)
|
||||
{
|
||||
if (iEnd < data.pageCount - 1 && data.pageDots)
|
||||
{
|
||||
pager += '<li class="disabled"><a href="#">…</a></li>';
|
||||
}
|
||||
pager += '<li><a href="#">' + data.pageCount + '</a></li>';
|
||||
}
|
||||
|
||||
pager += '<li' + (data.curPage === data.pageCount || data.pageCount === 0 ? ' class="disabled"' : '') + '><a href="#">Next →</a></li>';
|
||||
pager += '</ul>';
|
||||
|
||||
return pager;
|
||||
}
|
||||
|
||||
function updatePagerContent(data, oldPager, newPager)
|
||||
{
|
||||
var oldLIs = oldPager.getElementsByTagName('li');
|
||||
var newLIs = newPager.getElementsByTagName('li');
|
||||
|
||||
var oldLIsLength = oldLIs.length;
|
||||
var newLIsLength = newLIs.length;
|
||||
|
||||
for (var i=0, n=0; i < newLIs.length; i++, n++)
|
||||
{
|
||||
var newLI = newLIs[i];
|
||||
|
||||
if (n < oldLIsLength)
|
||||
{
|
||||
// update existing LI
|
||||
var oldLI = oldLIs[n];
|
||||
|
||||
var oldHtml = oldLI.outerHTML;
|
||||
var newHtml = newLI.outerHTML;
|
||||
if (oldHtml !== newHtml)
|
||||
{
|
||||
oldPager.replaceChild(newLI, oldLI);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// add new LI
|
||||
oldPager.appendChild(newLI);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
while (oldLIs.length > newLIsLength)
|
||||
{
|
||||
oldPager.removeChild(oldPager.lastChild);
|
||||
}
|
||||
}
|
||||
|
||||
function updateInfo(data)
|
||||
{
|
||||
if (data.content.length === 0)
|
||||
{
|
||||
var infoText = data.config.infoEmpty;
|
||||
}
|
||||
else if (data.curPage === 0)
|
||||
{
|
||||
var infoText = 'No matching records found (total ' + data.content.length + ')';
|
||||
}
|
||||
else
|
||||
{
|
||||
var firstRecord = (data.curPage - 1) * data.pageSize + 1;
|
||||
var lastRecord = firstRecord + data.pageContent.length - 1;
|
||||
var infoText = 'Showing records ' + firstRecord + '-' + lastRecord + ' from ' + data.filteredContent.length;
|
||||
if (data.filteredContent != data.content)
|
||||
{
|
||||
infoText += ' filtered (total ' + data.content.length + ')';
|
||||
}
|
||||
}
|
||||
data.config.infoContainer.html(infoText);
|
||||
|
||||
if (data.config.updateInfoCallback)
|
||||
{
|
||||
data.config.updateInfoCallback({
|
||||
total: data.content.length,
|
||||
available: data.filteredContent.length,
|
||||
filter: data.filteredContent != data.content,
|
||||
firstRecord: firstRecord,
|
||||
lastRecord: lastRecord
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function setPageSize(pageSize, maxPages, pageDots)
|
||||
{
|
||||
var data = $(this).data('fasttable');
|
||||
data.pageSize = parseInt(pageSize);
|
||||
data.curPage = 1;
|
||||
if (maxPages !== undefined)
|
||||
{
|
||||
data.maxPages = maxPages;
|
||||
}
|
||||
if (pageDots !== undefined)
|
||||
{
|
||||
data.pageDots = pageDots;
|
||||
}
|
||||
refresh(data);
|
||||
}
|
||||
|
||||
function setCurPage(page)
|
||||
{
|
||||
var data = $(this).data('fasttable');
|
||||
data.curPage = parseInt(page);
|
||||
refresh(data);
|
||||
}
|
||||
|
||||
function titleCheckRedraw(data)
|
||||
{
|
||||
var filteredContent = data.filteredContent;
|
||||
var checkedRows = data.checkedRows;
|
||||
|
||||
var hasSelectedItems = false;
|
||||
var hasUnselectedItems = false;
|
||||
for (var i = 0; i < filteredContent.length; i++)
|
||||
{
|
||||
if (checkedRows.indexOf(filteredContent[i].id) === -1)
|
||||
{
|
||||
hasUnselectedItems = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
hasSelectedItems = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasSelectedItems && hasUnselectedItems)
|
||||
{
|
||||
data.config.headerCheck.removeClass('checked').addClass('checkremove');
|
||||
}
|
||||
else if (hasSelectedItems)
|
||||
{
|
||||
data.config.headerCheck.removeClass('checkremove').addClass('checked');
|
||||
}
|
||||
else
|
||||
{
|
||||
data.config.headerCheck.removeClass('checked').removeClass('checkremove');
|
||||
}
|
||||
}
|
||||
|
||||
function itemCheckClick(row, event)
|
||||
{
|
||||
var data = $(this).data('fasttable');
|
||||
var checkedRows = data.checkedRows;
|
||||
|
||||
var id = row.fasttableID;
|
||||
var doToggle = true;
|
||||
|
||||
if (event.shiftKey && data.lastClickedRowID > 0)
|
||||
{
|
||||
var checked = checkedRows.indexOf(id) > -1;
|
||||
doToggle = !checkRange(data, id, data.lastClickedRowID, !checked);
|
||||
}
|
||||
|
||||
if (doToggle)
|
||||
{
|
||||
toggleCheck(data, id);
|
||||
}
|
||||
|
||||
data.lastClickedRowID = id;
|
||||
|
||||
refresh(data);
|
||||
}
|
||||
|
||||
function titleCheckClick()
|
||||
{
|
||||
var data = $(this).data('fasttable');
|
||||
var filteredContent = data.filteredContent;
|
||||
var checkedRows = data.checkedRows;
|
||||
|
||||
var hasSelectedItems = false;
|
||||
for (var i = 0; i < filteredContent.length; i++)
|
||||
{
|
||||
if (checkedRows.indexOf(filteredContent[i].id) > -1)
|
||||
{
|
||||
hasSelectedItems = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
data.lastClickedRowID = 0;
|
||||
checkAll(data, !hasSelectedItems);
|
||||
}
|
||||
|
||||
function toggleCheck(data, id)
|
||||
{
|
||||
var checkedRows = data.checkedRows;
|
||||
var index = checkedRows.indexOf(id);
|
||||
if (index > -1)
|
||||
{
|
||||
checkedRows.splice(index, 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
checkedRows.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
function checkAll(data, checked)
|
||||
{
|
||||
var filteredContent = data.filteredContent;
|
||||
|
||||
for (var i = 0; i < filteredContent.length; i++)
|
||||
{
|
||||
checkRow(data, filteredContent[i].id, checked);
|
||||
}
|
||||
|
||||
refresh(data);
|
||||
}
|
||||
|
||||
function checkRange(data, from, to, checked)
|
||||
{
|
||||
var filteredContent = data.filteredContent;
|
||||
var indexFrom = indexOfID(filteredContent, from);
|
||||
var indexTo = indexOfID(filteredContent, to);
|
||||
if (indexFrom === -1 || indexTo === -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (indexTo < indexFrom)
|
||||
{
|
||||
var tmp = indexTo; indexTo = indexFrom; indexFrom = tmp;
|
||||
}
|
||||
|
||||
for (var i = indexFrom; i <= indexTo; i++)
|
||||
{
|
||||
checkRow(data, filteredContent[i].id, checked);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function checkRow(data, id, checked)
|
||||
{
|
||||
if (checked)
|
||||
{
|
||||
if (data.checkedRows.indexOf(id) === -1)
|
||||
{
|
||||
data.checkedRows.push(id);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var index = data.checkedRows.indexOf(id);
|
||||
if (index > -1)
|
||||
{
|
||||
data.checkedRows.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function indexOfID(content, id)
|
||||
{
|
||||
for (var i = 0; i < content.length; i++)
|
||||
{
|
||||
if (id === content[i].id)
|
||||
{
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function validateChecks(data)
|
||||
{
|
||||
var filteredContent = data.filteredContent;
|
||||
var checkedRows = data.checkedRows;
|
||||
|
||||
var ids = [];
|
||||
for (var i = 0; i < data.content.length; i++)
|
||||
{
|
||||
ids.push(data.content[i].id);
|
||||
}
|
||||
|
||||
for (var i = 0; i < checkedRows.length; i++)
|
||||
{
|
||||
if (ids.indexOf(checkedRows[i]) === -1)
|
||||
{
|
||||
checkedRows.splice(i, 1);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var defaults =
|
||||
{
|
||||
filterInput: '#table-filter',
|
||||
filterClearButton: '#table-clear',
|
||||
filterCaseSensitive: false,
|
||||
pagerContainer: '#table-pager',
|
||||
infoContainer: '#table-info',
|
||||
pageSize: 10,
|
||||
maxPages: 5,
|
||||
pageDots: true,
|
||||
hasHeader: true,
|
||||
infoEmpty: 'No records',
|
||||
renderRowCallback: undefined,
|
||||
renderCellCallback: undefined,
|
||||
renderTableCallback: undefined,
|
||||
fillFieldsCallback: undefined,
|
||||
updateInfoCallback: undefined,
|
||||
filterInputCallback: undefined,
|
||||
filterClearCallback: undefined,
|
||||
fillSearchCallback: undefined,
|
||||
headerCheck: '#table-header-check'
|
||||
};
|
||||
|
||||
})(jQuery);
|
||||
430
webui/history.js
Normal file
430
webui/history.js
Normal file
@@ -0,0 +1,430 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2012 Andrey Prygunkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
/*
|
||||
* In this module:
|
||||
* 1) History tab;
|
||||
* 2) History edit dialog.
|
||||
*/
|
||||
|
||||
/*** HISTORY TAB AND EDIT HISTORY DIALOG **********************************************/
|
||||
|
||||
var History = (new function($)
|
||||
{
|
||||
'use strict';
|
||||
|
||||
// Controls
|
||||
var $HistoryTable;
|
||||
var $HistoryTabBadge;
|
||||
var $HistoryTabBadgeEmpty;
|
||||
var $HistoryRecordsPerPage;
|
||||
|
||||
// State
|
||||
var history;
|
||||
var notification = null;
|
||||
var updateTabInfo;
|
||||
|
||||
this.init = function(options)
|
||||
{
|
||||
updateTabInfo = options.updateTabInfo;
|
||||
|
||||
$HistoryTable = $('#HistoryTable');
|
||||
$HistoryTabBadge = $('#HistoryTabBadge');
|
||||
$HistoryTabBadgeEmpty = $('#HistoryTabBadgeEmpty');
|
||||
$HistoryRecordsPerPage = $('#HistoryRecordsPerPage');
|
||||
|
||||
historyEditDialog.init();
|
||||
|
||||
var recordsPerPage = UISettings.read('HistoryRecordsPerPage', 10);
|
||||
$HistoryRecordsPerPage.val(recordsPerPage);
|
||||
|
||||
$HistoryTable.fasttable(
|
||||
{
|
||||
filterInput: $('#HistoryTable_filter'),
|
||||
filterClearButton: $("#HistoryTable_clearfilter"),
|
||||
pagerContainer: $('#HistoryTable_pager'),
|
||||
infoContainer: $('#HistoryTable_info'),
|
||||
headerCheck: $('#HistoryTable > thead > tr:first-child'),
|
||||
filterCaseSensitive: false,
|
||||
pageSize: recordsPerPage,
|
||||
maxPages: UISettings.miniTheme ? 1 : 5,
|
||||
pageDots: !UISettings.miniTheme,
|
||||
fillFieldsCallback: fillFieldsCallback,
|
||||
renderCellCallback: renderCellCallback,
|
||||
updateInfoCallback: updateInfo
|
||||
});
|
||||
|
||||
$HistoryTable.on('click', 'a', editClick);
|
||||
$HistoryTable.on('click', 'tbody div.check',
|
||||
function(event) { $HistoryTable.fasttable('itemCheckClick', this.parentNode.parentNode, event); });
|
||||
$HistoryTable.on('click', 'thead div.check',
|
||||
function() { $HistoryTable.fasttable('titleCheckClick') });
|
||||
$HistoryTable.on('mousedown', Util.disableShiftMouseDown);
|
||||
}
|
||||
|
||||
this.applyTheme = function()
|
||||
{
|
||||
$HistoryTable.fasttable('setPageSize', UISettings.read('HistoryRecordsPerPage', 10),
|
||||
UISettings.miniTheme ? 1 : 5, !UISettings.miniTheme);
|
||||
}
|
||||
|
||||
this.update = function()
|
||||
{
|
||||
RPC.call('history', [], loaded);
|
||||
}
|
||||
|
||||
function loaded(curHistory)
|
||||
{
|
||||
history = curHistory;
|
||||
prepare();
|
||||
RPC.next();
|
||||
}
|
||||
|
||||
function prepare()
|
||||
{
|
||||
for (var j=0, jl=history.length; j < jl; j++)
|
||||
{
|
||||
detectStatus(history[j]);
|
||||
}
|
||||
}
|
||||
|
||||
function detectStatus(hist)
|
||||
{
|
||||
if (hist.Kind === 'NZB')
|
||||
{
|
||||
switch (hist.ScriptStatus)
|
||||
{
|
||||
case 'SUCCESS': hist.status = 'success'; break;
|
||||
case 'FAILURE': hist.status = 'failure'; break;
|
||||
case 'UNKNOWN': hist.status = 'unknown'; break;
|
||||
case 'NONE':
|
||||
switch (hist.ParStatus)
|
||||
{
|
||||
case 'SUCCESS': hist.status = 'success'; break;
|
||||
case 'REPAIR_POSSIBLE': hist.status = 'repairable'; break;
|
||||
case 'FAILURE': hist.status = 'failure'; break;
|
||||
case 'NONE': hist.status = 'none'; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (hist.Kind === 'URL')
|
||||
{
|
||||
switch (hist.UrlStatus)
|
||||
{
|
||||
case 'SUCCESS': hist.status = 'success'; break;
|
||||
case 'FAILURE': hist.status = 'failure'; break;
|
||||
case 'UNKNOWN': hist.status = 'unknown'; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.redraw = function()
|
||||
{
|
||||
var data = [];
|
||||
|
||||
for (var i=0; i < history.length; i++)
|
||||
{
|
||||
var hist = history[i];
|
||||
|
||||
var kind = hist.Kind;
|
||||
var statustext = hist.status === 'none' ? 'unknown' : hist.status;
|
||||
var size = kind === 'NZB' ? Util.formatSizeMB(hist.FileSizeMB) : '';
|
||||
|
||||
var textname = hist.Name;
|
||||
if (kind === 'URL')
|
||||
{
|
||||
textname += ' URL';
|
||||
}
|
||||
|
||||
var time = Util.formatDateTime(hist.HistoryTime + UISettings.timeZoneCorrection*60*60);
|
||||
|
||||
var item =
|
||||
{
|
||||
id: hist.ID,
|
||||
hist: hist,
|
||||
data: {time: time, size: size},
|
||||
search: statustext + ' ' + time + ' ' + textname + ' ' + hist.Category + ' ' + size
|
||||
};
|
||||
|
||||
data.push(item);
|
||||
}
|
||||
|
||||
$HistoryTable.fasttable('update', data);
|
||||
|
||||
Util.show($HistoryTabBadge, history.length > 0);
|
||||
Util.show($HistoryTabBadgeEmpty, history.length === 0 && UISettings.miniTheme);
|
||||
}
|
||||
|
||||
function fillFieldsCallback(item)
|
||||
{
|
||||
var hist = item.hist;
|
||||
|
||||
var status = buildStatus(hist);
|
||||
|
||||
var name = '<a href="#" histid="' + hist.ID + '">' + Util.textToHtml(Util.formatNZBName(hist.Name)) + '</a>';
|
||||
var category = Util.textToHtml(hist.Category);
|
||||
|
||||
if (hist.Kind === 'URL')
|
||||
{
|
||||
name += ' <span class="label label-info">URL</span>';
|
||||
}
|
||||
|
||||
if (!UISettings.miniTheme)
|
||||
{
|
||||
item.fields = ['<div class="check img-check"></div>', status, item.data.time, name, category, item.data.size];
|
||||
}
|
||||
else
|
||||
{
|
||||
var info = '<div class="check img-check"></div><span class="row-title">' + name + '</span>' +
|
||||
' ' + status + ' <span class="label">' + item.data.time + '</span>';
|
||||
if (category)
|
||||
{
|
||||
info += ' <span class="label label-status">' + category + '</span>';
|
||||
}
|
||||
if (hist.Kind === 'NZB')
|
||||
{
|
||||
info += ' <span class="label">' + item.data.size + '</span>';
|
||||
}
|
||||
item.fields = [info];
|
||||
}
|
||||
}
|
||||
|
||||
function renderCellCallback(cell, index, item)
|
||||
{
|
||||
if (index === 2)
|
||||
{
|
||||
cell.className = 'text-center';
|
||||
}
|
||||
else if (index === 5)
|
||||
{
|
||||
cell.className = 'text-right';
|
||||
}
|
||||
}
|
||||
|
||||
function buildStatus(hist)
|
||||
{
|
||||
switch (hist.status)
|
||||
{
|
||||
case 'success': return '<span class="label label-status label-success">success</span>';
|
||||
case 'failure': return '<span class="label label-status label-important">failure</span>';
|
||||
case 'unknown': return '<span class="label label-status label-info">unknown</span>';
|
||||
case 'repairable': return '<span class="label label-status label-success">repairable</span>';
|
||||
case 'none': return '<span class="label label-status">unknown</span>';
|
||||
default: return '<span class="label label-status label-important">internal error(' + hist.status + ')</span>';
|
||||
}
|
||||
}
|
||||
|
||||
this.recordsPerPageChange = function()
|
||||
{
|
||||
var val = $HistoryRecordsPerPage.val();
|
||||
UISettings.write('HistoryRecordsPerPage', val);
|
||||
$HistoryTable.fasttable('setPageSize', val);
|
||||
}
|
||||
|
||||
function updateInfo(stat)
|
||||
{
|
||||
updateTabInfo($HistoryTabBadge, stat);
|
||||
}
|
||||
|
||||
this.deleteClick = function()
|
||||
{
|
||||
if (history.length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var checkedRows = $HistoryTable.fasttable('checkedRows');
|
||||
if (checkedRows.length > 0)
|
||||
{
|
||||
ConfirmDialog.showModal('HistoryDeleteConfirmDialog', historyDelete);
|
||||
}
|
||||
else
|
||||
{
|
||||
ConfirmDialog.showModal('HistoryClearConfirmDialog', historyClear);
|
||||
}
|
||||
}
|
||||
|
||||
function historyDelete()
|
||||
{
|
||||
Refresher.pause();
|
||||
|
||||
var IDs = $HistoryTable.fasttable('checkedRows');
|
||||
|
||||
RPC.call('editqueue', ['HistoryDelete', 0, '', [IDs]], function()
|
||||
{
|
||||
notification = '#Notif_History_Deleted';
|
||||
editCompleted();
|
||||
});
|
||||
}
|
||||
|
||||
function historyClear()
|
||||
{
|
||||
Refresher.pause();
|
||||
|
||||
var IDs = [];
|
||||
for (var i=0; i<history.length; i++)
|
||||
{
|
||||
IDs.push(history[i].ID);
|
||||
}
|
||||
|
||||
RPC.call('editqueue', ['HistoryDelete', 0, '', [IDs]], function()
|
||||
{
|
||||
notification = '#Notif_History_Cleared';
|
||||
editCompleted();
|
||||
});
|
||||
}
|
||||
|
||||
function editCompleted()
|
||||
{
|
||||
Refresher.update();
|
||||
if (notification)
|
||||
{
|
||||
Notification.show(notification);
|
||||
notification = null;
|
||||
}
|
||||
}
|
||||
|
||||
function editClick()
|
||||
{
|
||||
var histid = $(this).attr('histid');
|
||||
$(this).blur();
|
||||
historyEditDialog.showModal(histid);
|
||||
}
|
||||
|
||||
/*** EDIT HISTORY DIALOG *************************************************************************/
|
||||
|
||||
var historyEditDialog = new function()
|
||||
{
|
||||
// Controls
|
||||
var $HistoryEditDialog;
|
||||
|
||||
// State
|
||||
var curHist;
|
||||
|
||||
this.init = function()
|
||||
{
|
||||
$HistoryEditDialog = $('#HistoryEditDialog');
|
||||
|
||||
$('#HistoryEdit_Delete').click(itemDelete);
|
||||
$('#HistoryEdit_Return').click(itemReturn);
|
||||
$('#HistoryEdit_Reprocess').click(itemReprocess);
|
||||
|
||||
$HistoryEditDialog.on('hidden', function () {
|
||||
Refresher.resume();
|
||||
});
|
||||
}
|
||||
|
||||
this.showModal = function(histid)
|
||||
{
|
||||
var hist = null;
|
||||
|
||||
// find history object
|
||||
for (var i=0; i<history.length; i++)
|
||||
{
|
||||
var gr = history[i];
|
||||
if (gr.ID == histid)
|
||||
{
|
||||
hist = gr;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (hist == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Refresher.pause();
|
||||
|
||||
curHist = hist;
|
||||
|
||||
var status = buildStatus(hist);
|
||||
|
||||
$('#HistoryEdit_Title').text(Util.formatNZBName(hist.Name));
|
||||
if (hist.Kind === 'URL')
|
||||
{
|
||||
$('#HistoryEdit_Title').html($('#HistoryEdit_Title').html() + ' ' + '<span class="label label-info">URL</span>');
|
||||
}
|
||||
|
||||
$('#HistoryEdit_Status').html(status);
|
||||
$('#HistoryEdit_Category').text(hist.Category !== '' ? hist.Category : '<empty>');
|
||||
$('#HistoryEdit_Path').text(hist.DestDir);
|
||||
|
||||
var size = Util.formatSizeMB(hist.FileSizeMB, hist.FileSizeLo);
|
||||
|
||||
var table = '';
|
||||
table += '<tr><td>Total</td><td class="text-right">' + size + '</td></tr>';
|
||||
table += '<tr><td>Files (total/parked)</td><td class="text-right">' + hist.FileCount + '/' + hist.RemainingFileCount + '</td></tr>';
|
||||
$('#HistoryEdit_Statistics').html(table);
|
||||
|
||||
Util.show($('#HistoryEdit_ReturnGroup'), hist.RemainingFileCount > 0 || hist.Kind === 'URL');
|
||||
Util.show($('#HistoryEdit_PathGroup, #HistoryEdit_StatisticsGroup, #HistoryEdit_ReprocessGroup'), hist.Kind === 'NZB');
|
||||
|
||||
enableAllButtons();
|
||||
|
||||
$HistoryEditDialog.modal({backdrop: 'static'});
|
||||
}
|
||||
|
||||
function disableAllButtons()
|
||||
{
|
||||
$('#HistoryEditDialog .modal-footer .btn').attr('disabled', 'disabled');
|
||||
setTimeout(function()
|
||||
{
|
||||
$('#HistoryEdit_Transmit').show();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
function enableAllButtons()
|
||||
{
|
||||
$('#HistoryEditDialog .modal-footer .btn').removeAttr('disabled');
|
||||
$('#HistoryEdit_Transmit').hide();
|
||||
}
|
||||
|
||||
function itemDelete()
|
||||
{
|
||||
disableAllButtons();
|
||||
notification = '#Notif_History_Deleted';
|
||||
RPC.call('editqueue', ['HistoryDelete', 0, '', [curHist.ID]], completed);
|
||||
}
|
||||
|
||||
function itemReturn()
|
||||
{
|
||||
disableAllButtons();
|
||||
notification = '#Notif_History_Returned';
|
||||
RPC.call('editqueue', ['HistoryReturn', 0, '', [curHist.ID]], completed);
|
||||
}
|
||||
|
||||
function itemReprocess()
|
||||
{
|
||||
disableAllButtons();
|
||||
notification = '#Notif_History_Reproces';
|
||||
RPC.call('editqueue', ['HistoryProcess', 0, '', [curHist.ID]], completed);
|
||||
}
|
||||
|
||||
function completed()
|
||||
{
|
||||
$HistoryEditDialog.modal('hide');
|
||||
editCompleted();
|
||||
}
|
||||
}();
|
||||
}(jQuery));
|
||||
BIN
webui/img/download-anim-green-2x.png
Normal file
BIN
webui/img/download-anim-green-2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 14 KiB |
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user