mirror of
https://github.com/nzbget/nzbget.git
synced 2026-01-05 20:48:09 -05:00
Compare commits
184 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d6a9c08dc7 | ||
|
|
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 |
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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>
|
||||
@@ -47,9 +47,13 @@ public:
|
||||
adRunning,
|
||||
adFinished,
|
||||
adFailed,
|
||||
adDecodeError,
|
||||
adCrcError,
|
||||
adDecoding,
|
||||
adJoining,
|
||||
adJoined,
|
||||
adNotFound,
|
||||
adConnectError,
|
||||
adFatalError
|
||||
};
|
||||
|
||||
@@ -63,20 +67,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 +91,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 +98,12 @@ 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; }
|
||||
void SetConnection(NNTPConnection* pConnection) { m_pConnection = pConnection; }
|
||||
|
||||
void LogDebugInfo();
|
||||
};
|
||||
@@ -115,6 +113,7 @@ class DownloadSpeedMeter
|
||||
public:
|
||||
virtual ~DownloadSpeedMeter() {};
|
||||
virtual float CalcCurrentDownloadSpeed() = 0;
|
||||
virtual void AddSpeedReading(int iBytes) = 0;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
774
BinRpc.cpp
Normal file
774
BinRpc.cpp
Normal file
@@ -0,0 +1,774 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cstdio>
|
||||
#include <fstream>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "BinRpc.h"
|
||||
#include "Log.h"
|
||||
#include "Options.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "QueueEditor.h"
|
||||
#include "PrePostProcessor.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
extern PrePostProcessor* g_pPrePostProcessor;
|
||||
extern void ExitProc();
|
||||
|
||||
const char* g_szMessageRequestNames[] =
|
||||
{ "N/A", "Download", "Pause/Unpause", "List", "Set download rate", "Dump debug",
|
||||
"Edit queue", "Log", "Quit", "Version", "Post-queue" };
|
||||
|
||||
const unsigned int g_iMessageRequestSizes[] =
|
||||
{ 0,
|
||||
sizeof(SNZBDownloadRequest),
|
||||
sizeof(SNZBPauseUnpauseRequest),
|
||||
sizeof(SNZBListRequest),
|
||||
sizeof(SNZBSetDownloadRateRequest),
|
||||
sizeof(SNZBDumpDebugRequest),
|
||||
sizeof(SNZBEditQueueRequest),
|
||||
sizeof(SNZBLogRequest),
|
||||
sizeof(SNZBShutdownRequest),
|
||||
sizeof(SNZBVersionRequest),
|
||||
sizeof(SNZBPostQueueRequest),
|
||||
};
|
||||
|
||||
//*****************************************************************
|
||||
// BinProcessor
|
||||
|
||||
void BinRpcProcessor::Execute()
|
||||
{
|
||||
// Read the first package which needs to be a request
|
||||
int iBytesReceived = recv(m_iSocket, ((char*)&m_MessageBase) + sizeof(m_MessageBase.m_iSignature), sizeof(m_MessageBase) - sizeof(m_MessageBase.m_iSignature), 0);
|
||||
if (iBytesReceived < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure this is a nzbget request from a client
|
||||
if ((int)ntohl(m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE)
|
||||
{
|
||||
warn("Non-nzbget request received on port %i from %s", g_pOptions->GetServerPort(), m_szClientIP);
|
||||
return;
|
||||
}
|
||||
|
||||
if (strcmp(m_MessageBase.m_szPassword, g_pOptions->GetServerPassword()))
|
||||
{
|
||||
warn("nzbget request received on port %i from %s, but password invalid", g_pOptions->GetServerPort(), m_szClientIP);
|
||||
return;
|
||||
}
|
||||
|
||||
debug("%s request received from %s", g_szMessageRequestNames[ntohl(m_MessageBase.m_iType)], m_szClientIP);
|
||||
|
||||
Dispatch();
|
||||
}
|
||||
|
||||
void BinRpcProcessor::Dispatch()
|
||||
{
|
||||
if (ntohl(m_MessageBase.m_iType) >= (int)eRemoteRequestDownload &&
|
||||
ntohl(m_MessageBase.m_iType) <= (int)eRemoteRequestPostQueue &&
|
||||
g_iMessageRequestSizes[ntohl(m_MessageBase.m_iType)] != ntohl(m_MessageBase.m_iStructSize))
|
||||
{
|
||||
error("Invalid size of request: needed %i Bytes, but received %i Bytes",
|
||||
g_iMessageRequestSizes[ntohl(m_MessageBase.m_iType)], ntohl(m_MessageBase.m_iStructSize));
|
||||
return;
|
||||
}
|
||||
|
||||
BinCommand* command = NULL;
|
||||
|
||||
switch (ntohl(m_MessageBase.m_iType))
|
||||
{
|
||||
case eRemoteRequestDownload:
|
||||
{
|
||||
command = new DownloadBinCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestList:
|
||||
{
|
||||
command = new ListBinCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestLog:
|
||||
{
|
||||
command = new LogBinCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestPauseUnpause:
|
||||
{
|
||||
command = new PauseUnpauseBinCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestEditQueue:
|
||||
{
|
||||
command = new EditQueueBinCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestSetDownloadRate:
|
||||
{
|
||||
command = new SetDownloadRateBinCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestDumpDebug:
|
||||
{
|
||||
command = new DumpDebugBinCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestShutdown:
|
||||
{
|
||||
command = new ShutdownBinCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestVersion:
|
||||
{
|
||||
command = new VersionBinCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestPostQueue:
|
||||
{
|
||||
command = new PostQueueBinCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
case eRemoteRequestWriteLog:
|
||||
{
|
||||
command = new WriteLogBinCommand();
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
error("Received unsupported request %i", ntohl(m_MessageBase.m_iType));
|
||||
break;
|
||||
}
|
||||
|
||||
if (command)
|
||||
{
|
||||
command->SetSocket(m_iSocket);
|
||||
command->SetMessageBase(&m_MessageBase);
|
||||
command->Execute();
|
||||
delete command;
|
||||
}
|
||||
}
|
||||
|
||||
//*****************************************************************
|
||||
// Commands
|
||||
|
||||
void BinCommand::SendBoolResponse(bool bSuccess, const char* szText)
|
||||
{
|
||||
// all bool-responses have the same format of structure, we use SNZBDownloadResponse here
|
||||
SNZBDownloadResponse BoolResponse;
|
||||
memset(&BoolResponse, 0, sizeof(BoolResponse));
|
||||
BoolResponse.m_MessageBase.m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
|
||||
BoolResponse.m_MessageBase.m_iStructSize = htonl(sizeof(BoolResponse));
|
||||
BoolResponse.m_bSuccess = htonl(bSuccess);
|
||||
int iTextLen = strlen(szText) + 1;
|
||||
BoolResponse.m_iTrailingDataLength = htonl(iTextLen);
|
||||
|
||||
// Send the request answer
|
||||
send(m_iSocket, (char*) &BoolResponse, sizeof(BoolResponse), 0);
|
||||
send(m_iSocket, (char*)szText, iTextLen, 0);
|
||||
}
|
||||
|
||||
bool BinCommand::ReceiveRequest(void* pBuffer, int iSize)
|
||||
{
|
||||
memcpy(pBuffer, m_pMessageBase, sizeof(SNZBRequestBase));
|
||||
iSize -= sizeof(SNZBRequestBase);
|
||||
if (iSize > 0)
|
||||
{
|
||||
int iBytesReceived = recv(m_iSocket, ((char*)pBuffer) + sizeof(SNZBRequestBase), iSize, 0);
|
||||
if (iBytesReceived != iSize)
|
||||
{
|
||||
error("invalid request");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void PauseUnpauseBinCommand::Execute()
|
||||
{
|
||||
SNZBPauseUnpauseRequest PauseUnpauseRequest;
|
||||
if (!ReceiveRequest(&PauseUnpauseRequest, sizeof(PauseUnpauseRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_pOptions->SetPause(ntohl(PauseUnpauseRequest.m_bPause));
|
||||
SendBoolResponse(true, "Pause-/Unpause-Command completed successfully");
|
||||
}
|
||||
|
||||
void SetDownloadRateBinCommand::Execute()
|
||||
{
|
||||
SNZBSetDownloadRateRequest SetDownloadRequest;
|
||||
if (!ReceiveRequest(&SetDownloadRequest, sizeof(SetDownloadRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_pOptions->SetDownloadRate(ntohl(SetDownloadRequest.m_iDownloadRate) / 1024.0f);
|
||||
SendBoolResponse(true, "Rate-Command completed successfully");
|
||||
}
|
||||
|
||||
void DumpDebugBinCommand::Execute()
|
||||
{
|
||||
SNZBDumpDebugRequest DumpDebugRequest;
|
||||
if (!ReceiveRequest(&DumpDebugRequest, sizeof(DumpDebugRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->LogDebugInfo();
|
||||
SendBoolResponse(true, "Debug-Command completed successfully");
|
||||
}
|
||||
|
||||
void ShutdownBinCommand::Execute()
|
||||
{
|
||||
SNZBShutdownRequest ShutdownRequest;
|
||||
if (!ReceiveRequest(&ShutdownRequest, sizeof(ShutdownRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SendBoolResponse(true, "Stopping server");
|
||||
ExitProc();
|
||||
}
|
||||
|
||||
void VersionBinCommand::Execute()
|
||||
{
|
||||
SNZBVersionRequest VersionRequest;
|
||||
if (!ReceiveRequest(&VersionRequest, sizeof(VersionRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SendBoolResponse(true, VERSION);
|
||||
}
|
||||
|
||||
void DownloadBinCommand::Execute()
|
||||
{
|
||||
SNZBDownloadRequest DownloadRequest;
|
||||
if (!ReceiveRequest(&DownloadRequest, sizeof(DownloadRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char* pRecvBuffer = (char*)malloc(ntohl(DownloadRequest.m_iTrailingDataLength) + 1);
|
||||
char* pBufPtr = pRecvBuffer;
|
||||
|
||||
// Read from the socket until nothing remains
|
||||
int iResult = 0;
|
||||
int NeedBytes = ntohl(DownloadRequest.m_iTrailingDataLength);
|
||||
while (NeedBytes > 0)
|
||||
{
|
||||
iResult = recv(m_iSocket, pBufPtr, NeedBytes, 0);
|
||||
// Did the recv succeed?
|
||||
if (iResult <= 0)
|
||||
{
|
||||
error("invalid request");
|
||||
break;
|
||||
}
|
||||
pBufPtr += iResult;
|
||||
NeedBytes -= iResult;
|
||||
}
|
||||
|
||||
if (NeedBytes == 0)
|
||||
{
|
||||
NZBFile* pNZBFile = NZBFile::CreateFromBuffer(DownloadRequest.m_szFilename, pRecvBuffer, ntohl(DownloadRequest.m_iTrailingDataLength));
|
||||
|
||||
if (pNZBFile)
|
||||
{
|
||||
info("Request: Queue collection %s", DownloadRequest.m_szFilename);
|
||||
g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, ntohl(DownloadRequest.m_bAddFirst));
|
||||
delete pNZBFile;
|
||||
|
||||
char tmp[1024];
|
||||
snprintf(tmp, 1024, "Collection %s added to queue", Util::BaseFileName(DownloadRequest.m_szFilename));
|
||||
tmp[1024-1] = '\0';
|
||||
SendBoolResponse(true, tmp);
|
||||
}
|
||||
else
|
||||
{
|
||||
char tmp[1024];
|
||||
snprintf(tmp, 1024, "Download Request failed for %s", Util::BaseFileName(DownloadRequest.m_szFilename));
|
||||
tmp[1024-1] = '\0';
|
||||
SendBoolResponse(false, tmp);
|
||||
}
|
||||
}
|
||||
|
||||
free(pRecvBuffer);
|
||||
}
|
||||
|
||||
void ListBinCommand::Execute()
|
||||
{
|
||||
SNZBListRequest ListRequest;
|
||||
if (!ReceiveRequest(&ListRequest, sizeof(ListRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SNZBListResponse ListResponse;
|
||||
memset(&ListResponse, 0, sizeof(ListResponse));
|
||||
ListResponse.m_MessageBase.m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
|
||||
ListResponse.m_MessageBase.m_iStructSize = htonl(sizeof(ListResponse));
|
||||
ListResponse.m_iEntrySize = htonl(sizeof(SNZBListResponseEntry));
|
||||
|
||||
char* buf = NULL;
|
||||
int bufsize = 0;
|
||||
|
||||
if (ntohl(ListRequest.m_bFileList))
|
||||
{
|
||||
// Make a data structure and copy all the elements of the list into it
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
|
||||
int NrEntries = pDownloadQueue->size();
|
||||
|
||||
// calculate required buffer size
|
||||
bufsize = NrEntries * sizeof(SNZBListResponseEntry);
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
bufsize += strlen(pFileInfo->GetNZBInfo()->GetFilename()) + 1;
|
||||
bufsize += strlen(pFileInfo->GetSubject()) + 1;
|
||||
bufsize += strlen(pFileInfo->GetFilename()) + 1;
|
||||
bufsize += strlen(pFileInfo->GetNZBInfo()->GetDestDir()) + 1;
|
||||
// align struct to 4-bytes, needed by ARM-processor (and may be others)
|
||||
bufsize += bufsize % 4 > 0 ? 4 - bufsize % 4 : 0;
|
||||
}
|
||||
|
||||
buf = (char*) malloc(bufsize);
|
||||
char* bufptr = buf;
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
unsigned long iSizeHi, iSizeLo;
|
||||
FileInfo* pFileInfo = *it;
|
||||
SNZBListResponseEntry* pListAnswer = (SNZBListResponseEntry*) bufptr;
|
||||
pListAnswer->m_iID = htonl(pFileInfo->GetID());
|
||||
Util::SplitInt64(pFileInfo->GetSize(), &iSizeHi, &iSizeLo);
|
||||
pListAnswer->m_iFileSizeLo = htonl(iSizeLo);
|
||||
pListAnswer->m_iFileSizeHi = htonl(iSizeHi);
|
||||
Util::SplitInt64(pFileInfo->GetRemainingSize(), &iSizeHi, &iSizeLo);
|
||||
pListAnswer->m_iRemainingSizeLo = htonl(iSizeLo);
|
||||
pListAnswer->m_iRemainingSizeHi = htonl(iSizeHi);
|
||||
pListAnswer->m_bFilenameConfirmed = htonl(pFileInfo->GetFilenameConfirmed());
|
||||
pListAnswer->m_bPaused = htonl(pFileInfo->GetPaused());
|
||||
pListAnswer->m_iNZBFilenameLen = htonl(strlen(pFileInfo->GetNZBInfo()->GetFilename()) + 1);
|
||||
pListAnswer->m_iSubjectLen = htonl(strlen(pFileInfo->GetSubject()) + 1);
|
||||
pListAnswer->m_iFilenameLen = htonl(strlen(pFileInfo->GetFilename()) + 1);
|
||||
pListAnswer->m_iDestDirLen = htonl(strlen(pFileInfo->GetNZBInfo()->GetDestDir()) + 1);
|
||||
bufptr += sizeof(SNZBListResponseEntry);
|
||||
strcpy(bufptr, pFileInfo->GetNZBInfo()->GetFilename());
|
||||
bufptr += ntohl(pListAnswer->m_iNZBFilenameLen);
|
||||
strcpy(bufptr, pFileInfo->GetSubject());
|
||||
bufptr += ntohl(pListAnswer->m_iSubjectLen);
|
||||
strcpy(bufptr, pFileInfo->GetFilename());
|
||||
bufptr += ntohl(pListAnswer->m_iFilenameLen);
|
||||
strcpy(bufptr, pFileInfo->GetNZBInfo()->GetDestDir());
|
||||
bufptr += ntohl(pListAnswer->m_iDestDirLen);
|
||||
// align struct to 4-bytes, needed by ARM-processor (and may be others)
|
||||
if ((size_t)bufptr % 4 > 0)
|
||||
{
|
||||
pListAnswer->m_iDestDirLen = htonl(ntohl(pListAnswer->m_iDestDirLen) + 4 - (size_t)bufptr % 4);
|
||||
bufptr += 4 - (size_t)bufptr % 4;
|
||||
}
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
|
||||
ListResponse.m_iNrTrailingEntries = htonl(NrEntries);
|
||||
ListResponse.m_iTrailingDataLength = htonl(bufsize);
|
||||
}
|
||||
|
||||
if (htonl(ListRequest.m_bServerState))
|
||||
{
|
||||
unsigned long iSizeHi, iSizeLo;
|
||||
ListResponse.m_iDownloadRate = htonl((int)(g_pQueueCoordinator->CalcCurrentDownloadSpeed() * 1024));
|
||||
Util::SplitInt64(g_pQueueCoordinator->CalcRemainingSize(), &iSizeHi, &iSizeLo);
|
||||
ListResponse.m_iRemainingSizeHi = htonl(iSizeHi);
|
||||
ListResponse.m_iRemainingSizeLo = htonl(iSizeLo);
|
||||
ListResponse.m_iDownloadLimit = htonl((int)(g_pOptions->GetDownloadRate() * 1024));
|
||||
ListResponse.m_bServerPaused = htonl(g_pOptions->GetPause());
|
||||
ListResponse.m_iThreadCount = htonl(Thread::GetThreadCount() - 1); // not counting itself
|
||||
PostQueue* pPostQueue = g_pPrePostProcessor->LockPostQueue();
|
||||
ListResponse.m_iPostJobCount = htonl(pPostQueue->size());
|
||||
g_pPrePostProcessor->UnlockPostQueue();
|
||||
|
||||
int iUpTimeSec, iDnTimeSec;
|
||||
long long iAllBytes;
|
||||
bool bStandBy;
|
||||
g_pQueueCoordinator->CalcStat(&iUpTimeSec, &iDnTimeSec, &iAllBytes, &bStandBy);
|
||||
ListResponse.m_iUpTimeSec = htonl(iUpTimeSec);
|
||||
ListResponse.m_iDownloadTimeSec = htonl(iDnTimeSec);
|
||||
ListResponse.m_bServerStandBy = htonl(bStandBy);
|
||||
Util::SplitInt64(iAllBytes, &iSizeHi, &iSizeLo);
|
||||
ListResponse.m_iDownloadedBytesHi = htonl(iSizeHi);
|
||||
ListResponse.m_iDownloadedBytesLo = htonl(iSizeLo);
|
||||
}
|
||||
|
||||
// Send the request answer
|
||||
send(m_iSocket, (char*) &ListResponse, sizeof(ListResponse), 0);
|
||||
|
||||
// Send the data
|
||||
if (bufsize > 0)
|
||||
{
|
||||
send(m_iSocket, buf, bufsize, 0);
|
||||
}
|
||||
|
||||
if (buf)
|
||||
{
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
|
||||
void LogBinCommand::Execute()
|
||||
{
|
||||
SNZBLogRequest LogRequest;
|
||||
if (!ReceiveRequest(&LogRequest, sizeof(LogRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Log::Messages* pMessages = g_pLog->LockMessages();
|
||||
|
||||
int iNrEntries = ntohl(LogRequest.m_iLines);
|
||||
unsigned int iIDFrom = ntohl(LogRequest.m_iIDFrom);
|
||||
int iStart = pMessages->size();
|
||||
if (iNrEntries > 0)
|
||||
{
|
||||
if (iNrEntries > (int)pMessages->size())
|
||||
{
|
||||
iNrEntries = pMessages->size();
|
||||
}
|
||||
iStart = pMessages->size() - iNrEntries;
|
||||
}
|
||||
if (iIDFrom > 0 && !pMessages->empty())
|
||||
{
|
||||
iStart = iIDFrom - pMessages->front()->GetID();
|
||||
if (iStart < 0)
|
||||
{
|
||||
iStart = 0;
|
||||
}
|
||||
iNrEntries = pMessages->size() - iStart;
|
||||
if (iNrEntries < 0)
|
||||
{
|
||||
iNrEntries = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// calculate required buffer size
|
||||
int bufsize = iNrEntries * sizeof(SNZBLogResponseEntry);
|
||||
for (unsigned int i = (unsigned int)iStart; i < pMessages->size(); i++)
|
||||
{
|
||||
Message* pMessage = (*pMessages)[i];
|
||||
bufsize += strlen(pMessage->GetText()) + 1;
|
||||
// align struct to 4-bytes, needed by ARM-processor (and may be others)
|
||||
bufsize += bufsize % 4 > 0 ? 4 - bufsize % 4 : 0;
|
||||
}
|
||||
|
||||
char* buf = (char*) malloc(bufsize);
|
||||
char* bufptr = buf;
|
||||
for (unsigned int i = (unsigned int)iStart; i < pMessages->size(); i++)
|
||||
{
|
||||
Message* pMessage = (*pMessages)[i];
|
||||
SNZBLogResponseEntry* pLogAnswer = (SNZBLogResponseEntry*) bufptr;
|
||||
pLogAnswer->m_iID = htonl(pMessage->GetID());
|
||||
pLogAnswer->m_iKind = htonl(pMessage->GetKind());
|
||||
pLogAnswer->m_tTime = htonl((int)pMessage->GetTime());
|
||||
pLogAnswer->m_iTextLen = htonl(strlen(pMessage->GetText()) + 1);
|
||||
bufptr += sizeof(SNZBLogResponseEntry);
|
||||
strcpy(bufptr, pMessage->GetText());
|
||||
bufptr += ntohl(pLogAnswer->m_iTextLen);
|
||||
// align struct to 4-bytes, needed by ARM-processor (and may be others)
|
||||
if ((size_t)bufptr % 4 > 0)
|
||||
{
|
||||
pLogAnswer->m_iTextLen = htonl(ntohl(pLogAnswer->m_iTextLen) + 4 - (size_t)bufptr % 4);
|
||||
bufptr += 4 - (size_t)bufptr % 4;
|
||||
}
|
||||
}
|
||||
|
||||
g_pLog->UnlockMessages();
|
||||
|
||||
SNZBLogResponse LogResponse;
|
||||
LogResponse.m_MessageBase.m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
|
||||
LogResponse.m_MessageBase.m_iStructSize = htonl(sizeof(LogResponse));
|
||||
LogResponse.m_iEntrySize = htonl(sizeof(SNZBLogResponseEntry));
|
||||
LogResponse.m_iNrTrailingEntries = htonl(iNrEntries);
|
||||
LogResponse.m_iTrailingDataLength = htonl(bufsize);
|
||||
|
||||
// Send the request answer
|
||||
send(m_iSocket, (char*) &LogResponse, sizeof(LogResponse), 0);
|
||||
|
||||
// Send the data
|
||||
if (bufsize > 0)
|
||||
{
|
||||
send(m_iSocket, buf, bufsize, 0);
|
||||
}
|
||||
|
||||
free(buf);
|
||||
}
|
||||
|
||||
void EditQueueBinCommand::Execute()
|
||||
{
|
||||
SNZBEditQueueRequest EditQueueRequest;
|
||||
if (!ReceiveRequest(&EditQueueRequest, sizeof(EditQueueRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int iNrEntries = ntohl(EditQueueRequest.m_iNrTrailingEntries);
|
||||
int iAction = ntohl(EditQueueRequest.m_iAction);
|
||||
int iOffset = ntohl(EditQueueRequest.m_iOffset);
|
||||
bool bSmartOrder = ntohl(EditQueueRequest.m_bSmartOrder);
|
||||
unsigned int iBufLength = ntohl(EditQueueRequest.m_iTrailingDataLength);
|
||||
|
||||
if (iNrEntries * sizeof(int32_t) != iBufLength)
|
||||
{
|
||||
error("Invalid struct size");
|
||||
return;
|
||||
}
|
||||
|
||||
if (iNrEntries <= 0)
|
||||
{
|
||||
SendBoolResponse(false, "Edit-Command failed: no IDs specified");
|
||||
return;
|
||||
}
|
||||
|
||||
int32_t* pIDs = (int32_t*)malloc(iBufLength);
|
||||
|
||||
// Read from the socket until nothing remains
|
||||
char* pBufPtr = (char*)pIDs;
|
||||
int NeedBytes = iBufLength;
|
||||
int iResult = 0;
|
||||
while (NeedBytes > 0)
|
||||
{
|
||||
iResult = recv(m_iSocket, pBufPtr, NeedBytes, 0);
|
||||
// Did the recv succeed?
|
||||
if (iResult <= 0)
|
||||
{
|
||||
error("invalid request");
|
||||
break;
|
||||
}
|
||||
pBufPtr += iResult;
|
||||
NeedBytes -= iResult;
|
||||
}
|
||||
|
||||
QueueEditor::IDList cIDList;
|
||||
cIDList.reserve(iNrEntries);
|
||||
for (int i = 0; i < iNrEntries; i++)
|
||||
{
|
||||
cIDList.push_back(ntohl(pIDs[i]));
|
||||
}
|
||||
|
||||
free(pIDs);
|
||||
|
||||
bool bOK = g_pQueueCoordinator->GetQueueEditor()->EditList(&cIDList, bSmartOrder, (QueueEditor::EEditAction)iAction, iOffset);
|
||||
|
||||
if (bOK)
|
||||
{
|
||||
SendBoolResponse(true, "Edit-Command completed successfully");
|
||||
}
|
||||
else
|
||||
{
|
||||
SendBoolResponse(false, "Edit-Command failed");
|
||||
}
|
||||
}
|
||||
|
||||
void PostQueueBinCommand::Execute()
|
||||
{
|
||||
SNZBPostQueueRequest PostQueueRequest;
|
||||
if (!ReceiveRequest(&PostQueueRequest, sizeof(PostQueueRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
SNZBPostQueueResponse PostQueueResponse;
|
||||
memset(&PostQueueResponse, 0, sizeof(PostQueueResponse));
|
||||
PostQueueResponse.m_MessageBase.m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
|
||||
PostQueueResponse.m_MessageBase.m_iStructSize = htonl(sizeof(PostQueueResponse));
|
||||
PostQueueResponse.m_iEntrySize = htonl(sizeof(SNZBPostQueueResponseEntry));
|
||||
|
||||
char* buf = NULL;
|
||||
int bufsize = 0;
|
||||
|
||||
// Make a data structure and copy all the elements of the list into it
|
||||
PostQueue* pPostQueue = g_pPrePostProcessor->LockPostQueue();
|
||||
|
||||
int NrEntries = pPostQueue->size();
|
||||
|
||||
// calculate required buffer size
|
||||
bufsize = NrEntries * sizeof(SNZBPostQueueResponseEntry);
|
||||
for (PostQueue::iterator it = pPostQueue->begin(); it != pPostQueue->end(); it++)
|
||||
{
|
||||
PostInfo* pPostInfo = *it;
|
||||
bufsize += strlen(pPostInfo->GetNZBFilename()) + 1;
|
||||
bufsize += strlen(pPostInfo->GetParFilename()) + 1;
|
||||
bufsize += strlen(pPostInfo->GetInfoName()) + 1;
|
||||
bufsize += strlen(pPostInfo->GetDestDir()) + 1;
|
||||
bufsize += strlen(pPostInfo->GetProgressLabel()) + 1;
|
||||
// align struct to 4-bytes, needed by ARM-processor (and may be others)
|
||||
bufsize += bufsize % 4 > 0 ? 4 - bufsize % 4 : 0;
|
||||
}
|
||||
|
||||
time_t tCurTime = time(NULL);
|
||||
buf = (char*) malloc(bufsize);
|
||||
char* bufptr = buf;
|
||||
|
||||
for (PostQueue::iterator it = pPostQueue->begin(); it != pPostQueue->end(); it++)
|
||||
{
|
||||
PostInfo* pPostInfo = *it;
|
||||
SNZBPostQueueResponseEntry* pPostQueueAnswer = (SNZBPostQueueResponseEntry*) bufptr;
|
||||
pPostQueueAnswer->m_iID = htonl(pPostInfo->GetID());
|
||||
pPostQueueAnswer->m_iStage = htonl(pPostInfo->GetStage());
|
||||
pPostQueueAnswer->m_iStageProgress = htonl(pPostInfo->GetStageProgress());
|
||||
pPostQueueAnswer->m_iFileProgress = htonl(pPostInfo->GetFileProgress());
|
||||
pPostQueueAnswer->m_iTotalTimeSec = htonl((int)(pPostInfo->GetStartTime() ? tCurTime - pPostInfo->GetStartTime() : 0));
|
||||
pPostQueueAnswer->m_iStageTimeSec = htonl((int)(pPostInfo->GetStageTime() ? tCurTime - pPostInfo->GetStageTime() : 0));
|
||||
pPostQueueAnswer->m_iNZBFilenameLen = htonl(strlen(pPostInfo->GetNZBFilename()) + 1);
|
||||
pPostQueueAnswer->m_iParFilename = htonl(strlen(pPostInfo->GetParFilename()) + 1);
|
||||
pPostQueueAnswer->m_iInfoNameLen = htonl(strlen(pPostInfo->GetInfoName()) + 1);
|
||||
pPostQueueAnswer->m_iDestDirLen = htonl(strlen(pPostInfo->GetDestDir()) + 1);
|
||||
pPostQueueAnswer->m_iProgressLabelLen = htonl(strlen(pPostInfo->GetProgressLabel()) + 1);
|
||||
bufptr += sizeof(SNZBPostQueueResponseEntry);
|
||||
strcpy(bufptr, pPostInfo->GetNZBFilename());
|
||||
bufptr += ntohl(pPostQueueAnswer->m_iNZBFilenameLen);
|
||||
strcpy(bufptr, pPostInfo->GetParFilename());
|
||||
bufptr += ntohl(pPostQueueAnswer->m_iParFilename);
|
||||
strcpy(bufptr, pPostInfo->GetInfoName());
|
||||
bufptr += ntohl(pPostQueueAnswer->m_iInfoNameLen);
|
||||
strcpy(bufptr, pPostInfo->GetDestDir());
|
||||
bufptr += ntohl(pPostQueueAnswer->m_iDestDirLen);
|
||||
strcpy(bufptr, pPostInfo->GetProgressLabel());
|
||||
bufptr += ntohl(pPostQueueAnswer->m_iProgressLabelLen);
|
||||
// align struct to 4-bytes, needed by ARM-processor (and may be others)
|
||||
if ((size_t)bufptr % 4 > 0)
|
||||
{
|
||||
pPostQueueAnswer->m_iProgressLabelLen = htonl(ntohl(pPostQueueAnswer->m_iProgressLabelLen) + 4 - (size_t)bufptr % 4);
|
||||
bufptr += 4 - (size_t)bufptr % 4;
|
||||
}
|
||||
}
|
||||
|
||||
g_pPrePostProcessor->UnlockPostQueue();
|
||||
|
||||
PostQueueResponse.m_iNrTrailingEntries = htonl(NrEntries);
|
||||
PostQueueResponse.m_iTrailingDataLength = htonl(bufsize);
|
||||
|
||||
// Send the request answer
|
||||
send(m_iSocket, (char*) &PostQueueResponse, sizeof(PostQueueResponse), 0);
|
||||
|
||||
// Send the data
|
||||
if (bufsize > 0)
|
||||
{
|
||||
send(m_iSocket, buf, bufsize, 0);
|
||||
}
|
||||
|
||||
if (buf)
|
||||
{
|
||||
free(buf);
|
||||
}
|
||||
}
|
||||
|
||||
void WriteLogBinCommand::Execute()
|
||||
{
|
||||
SNZBWriteLogRequest WriteLogRequest;
|
||||
if (!ReceiveRequest(&WriteLogRequest, sizeof(WriteLogRequest)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
char* pRecvBuffer = (char*)malloc(ntohl(WriteLogRequest.m_iTrailingDataLength) + 1);
|
||||
char* pBufPtr = pRecvBuffer;
|
||||
|
||||
// Read from the socket until nothing remains
|
||||
int iResult = 0;
|
||||
int NeedBytes = ntohl(WriteLogRequest.m_iTrailingDataLength);
|
||||
pRecvBuffer[NeedBytes] = '\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)
|
||||
{
|
||||
bool OK = true;
|
||||
switch ((Message::EKind)ntohl(WriteLogRequest.m_iKind))
|
||||
{
|
||||
case Message::mkDetail:
|
||||
detail(pRecvBuffer);
|
||||
break;
|
||||
case Message::mkInfo:
|
||||
info(pRecvBuffer);
|
||||
break;
|
||||
case Message::mkWarning:
|
||||
warn(pRecvBuffer);
|
||||
break;
|
||||
case Message::mkError:
|
||||
error(pRecvBuffer);
|
||||
break;
|
||||
case Message::mkDebug:
|
||||
debug(pRecvBuffer);
|
||||
break;
|
||||
default:
|
||||
OK = false;
|
||||
}
|
||||
SendBoolResponse(OK, OK ? "Message added to log" : "Invalid message-kind");
|
||||
}
|
||||
|
||||
free(pRecvBuffer);
|
||||
}
|
||||
131
BinRpc.h
Normal file
131
BinRpc.h
Normal file
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2005 Bo Cordes Petersen <placebodk@sourceforge.net>
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#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 VersionBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class PostQueueBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class WriteLogBinCommand: public BinCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
#endif
|
||||
171
ChangeLog
171
ChangeLog
@@ -1,3 +1,170 @@
|
||||
nzbget-0.4.1:
|
||||
- to avoid accidental deletion of file in curses-frontend the key <D>
|
||||
not 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 useses 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
|
||||
@@ -109,7 +276,7 @@ nzbget-0.3.0:
|
||||
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
|
||||
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.
|
||||
@@ -136,7 +303,7 @@ nzbget-0.3.0:
|
||||
- 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;
|
||||
revised.
|
||||
|
||||
nzbget-0.2.3
|
||||
- Fixed problem with losing connection to newsserver after too long idle time
|
||||
|
||||
@@ -41,6 +41,7 @@
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "ColoredFrontend.h"
|
||||
#include "Util.h"
|
||||
|
||||
ColoredFrontend::ColoredFrontend()
|
||||
{
|
||||
@@ -73,52 +74,47 @@ 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_bPause)
|
||||
{
|
||||
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, %.0f KB/s, %.2f MB remaining%s%s%s%s%s\n",
|
||||
m_iThreadCount, fCurrentDownloadSpeed, (float)(Util::Int64ToFloat(m_lRemainingSize) / 1024.0 / 1024.0),
|
||||
timeString, szPostStatus, m_bPause ? (m_bStandBy ? ", Paused" : ", Pausing") : "", szDownloadLimit, szControlSeq);
|
||||
tmp[1024-1] = '\0';
|
||||
printf("%s", tmp);
|
||||
m_bNeedGoBack = true;
|
||||
@@ -126,7 +122,6 @@ void ColoredFrontend::PrintStatus()
|
||||
|
||||
void ColoredFrontend::PrintMessage(Message * pMessage)
|
||||
{
|
||||
const char* msg = pMessage->GetText();
|
||||
#ifdef WIN32
|
||||
switch (pMessage->GetKind())
|
||||
{
|
||||
@@ -146,10 +141,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 +167,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
|
||||
}
|
||||
|
||||
116
Connection.cpp
116
Connection.cpp
@@ -34,6 +34,7 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cstdio>
|
||||
#ifndef WIN32
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
@@ -47,6 +48,8 @@
|
||||
#include "Connection.h"
|
||||
#include "Log.h"
|
||||
|
||||
static const int CONNECTION_READBUFFER_SIZE = 1024;
|
||||
|
||||
void Connection::Init()
|
||||
{
|
||||
debug("Intiializing global connection data");
|
||||
@@ -86,18 +89,35 @@ Connection::Connection(NetAddress* pNetAddress)
|
||||
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;
|
||||
}
|
||||
|
||||
Connection::Connection(SOCKET iSocket, bool bAutoClose)
|
||||
{
|
||||
debug("Creating Connection");
|
||||
|
||||
m_pNetAddress = NULL;
|
||||
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;
|
||||
}
|
||||
|
||||
Connection::~Connection()
|
||||
{
|
||||
debug("Destroying Connection");
|
||||
|
||||
if (m_eStatus == csConnected)
|
||||
if (m_eStatus == csConnected && m_bAutoClose)
|
||||
{
|
||||
Disconnect();
|
||||
}
|
||||
free(m_szReadBuf);
|
||||
}
|
||||
|
||||
int Connection::Connect()
|
||||
@@ -127,6 +147,8 @@ int Connection::Disconnect()
|
||||
int iRes = DoDisconnect();
|
||||
|
||||
m_eStatus = csDisconnected;
|
||||
m_iSocket = INVALID_SOCKET;
|
||||
m_iBufAvail = 0;
|
||||
|
||||
return iRes;
|
||||
}
|
||||
@@ -142,12 +164,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 +181,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 +200,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;
|
||||
}
|
||||
@@ -231,6 +250,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)
|
||||
{
|
||||
@@ -301,7 +331,7 @@ unsigned int Connection::ResolveHostAddr(const char* szHost)
|
||||
{
|
||||
struct hostent* hinfo;
|
||||
bool err = false;
|
||||
int h_errnop;
|
||||
int h_errnop = 0;
|
||||
#ifdef WIN32
|
||||
hinfo = gethostbyname(szHost);
|
||||
err = hinfo == NULL;
|
||||
@@ -312,6 +342,7 @@ unsigned int Connection::ResolveHostAddr(const char* szHost)
|
||||
char* strbuf = (char*)malloc(strbuflen);
|
||||
#ifdef HAVE_GETHOSTBYNAME_R_6
|
||||
err = gethostbyname_r(szHost, &hinfobuf, strbuf, strbuflen, &hinfo, &h_errnop);
|
||||
err = err || (hinfo == NULL); // error on null hinfo (means 'no entry')
|
||||
#else
|
||||
hinfo = gethostbyname_r(szHost, &hinfobuf, strbuf, strbuflen, &h_errnop);
|
||||
err = hinfo == NULL;
|
||||
@@ -348,24 +379,25 @@ int Connection::DoDisconnect()
|
||||
return 0;
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -383,7 +415,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 +431,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;
|
||||
@@ -432,7 +470,18 @@ int Connection::DoBind()
|
||||
struct sockaddr_in sSocketAddress;
|
||||
memset(&sSocketAddress, '\0', sizeof(sSocketAddress));
|
||||
sSocketAddress.sin_family = AF_INET;
|
||||
sSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
if (!m_pNetAddress->GetHost() || strlen(m_pNetAddress->GetHost()) == 0)
|
||||
{
|
||||
sSocketAddress.sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
}
|
||||
else
|
||||
{
|
||||
sSocketAddress.sin_addr.s_addr = ResolveHostAddr(m_pNetAddress->GetHost());
|
||||
if (sSocketAddress.sin_addr.s_addr == (unsigned int)-1)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
sSocketAddress.sin_port = htons(m_pNetAddress->GetPort());
|
||||
int opt = 1;
|
||||
setsockopt(m_iSocket, SOL_SOCKET, SO_REUSEADDR, (char*)&opt, sizeof(opt));
|
||||
@@ -461,7 +510,7 @@ SOCKET Connection::DoAccept()
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -472,15 +521,14 @@ 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);
|
||||
}
|
||||
m_eStatus = csCancelled;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -499,9 +547,23 @@ void Connection::ReportError(const char* szMsgPrefix, const char* szMsgArg, int
|
||||
snprintf(szErrPrefix, 1024, szMsgPrefix, szMsgArg);
|
||||
szErrPrefix[1024-1] = '\0';
|
||||
#ifdef WIN32
|
||||
debug("%s: ErrNo %i", szErrPrefix, ErrCode);
|
||||
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);
|
||||
if (m_bSuppressErrors)
|
||||
{
|
||||
debug("%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("%s: ErrNo %i, %s", szErrPrefix, ErrCode, szErrMsg);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
31
Connection.h
31
Connection.h
@@ -43,42 +43,45 @@ public:
|
||||
protected:
|
||||
NetAddress* m_pNetAddress;
|
||||
SOCKET m_iSocket;
|
||||
static const int ReadBufLen = 1024;
|
||||
char m_szReadBuf[ReadBufLen + 1];
|
||||
char* m_szReadBuf;
|
||||
int m_iBufAvail;
|
||||
char* m_szBufPtr;
|
||||
EStatus m_eStatus;
|
||||
bool m_bCanceling;
|
||||
int m_iTimeout;
|
||||
bool m_bSuppressErrors;
|
||||
bool m_bAutoClose;
|
||||
|
||||
unsigned int ResolveHostAddr(const char* szHost);
|
||||
void ReportError(const char* szMsgPrefix, const char* szMsgArg, int ErrCode);
|
||||
virtual int DoConnect();
|
||||
virtual int DoDisconnect();
|
||||
int DoBind();
|
||||
int DoWriteLine(const char* pBuffer);
|
||||
char* DoReadLine(char* pBuffer, int iSize, int* pBytesRead);
|
||||
SOCKET DoAccept();
|
||||
|
||||
public:
|
||||
Connection(NetAddress* pNetAddress);
|
||||
Connection(SOCKET iSocket, bool bAutoClose);
|
||||
virtual ~Connection();
|
||||
static void Init();
|
||||
static void Final();
|
||||
int Connect();
|
||||
int 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);
|
||||
int WriteLine(const char* pBuffer);
|
||||
SOCKET Accept();
|
||||
void Cancel();
|
||||
NetAddress* GetServer() { return m_pNetAddress; }
|
||||
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; }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
578
Decoder.cpp
578
Decoder.cpp
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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>
|
||||
@@ -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 = 0;
|
||||
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 == 0 || !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;
|
||||
}
|
||||
|
||||
107
Decoder.h
107
Decoder.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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>
|
||||
@@ -29,44 +29,93 @@
|
||||
|
||||
#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
|
||||
|
||||
438
DiskState.cpp
438
DiskState.cpp
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
@@ -34,6 +34,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "DiskState.h"
|
||||
@@ -43,16 +44,42 @@
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
static const char* FORMATVERSION_SIGNATURE = "nzbget diskstate file version 3\n";
|
||||
|
||||
/* Save Download Queue to Disk.
|
||||
* The Disk State consists of file "queue", which contains the order of files
|
||||
* and of one diskstate-file for each file in download queue.
|
||||
* If parameter "OnlyOrder" is set to true, only the file "queue" will
|
||||
* be written to disk (It useful, if only the order of files in queue was changed).
|
||||
* This function saves file "queue" and files with NZB-info. It does not
|
||||
* save file-infos.
|
||||
*/
|
||||
bool DiskState::Save(DownloadQueue* pDownloadQueue, bool OnlyOrder)
|
||||
bool DiskState::SaveDownloadQueue(DownloadQueue* pDownloadQueue)
|
||||
{
|
||||
debug("Saving queue to disk");
|
||||
|
||||
// prepare list of nzb-infos
|
||||
|
||||
typedef std::deque<NZBInfo*> NZBList;
|
||||
NZBList cNZBList;
|
||||
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
bool inlist = false;
|
||||
for (NZBList::iterator it = cNZBList.begin(); it != cNZBList.end(); it++)
|
||||
{
|
||||
NZBInfo* pNZBInfo = *it;
|
||||
if (pNZBInfo == pFileInfo->GetNZBInfo())
|
||||
{
|
||||
inlist = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!inlist)
|
||||
{
|
||||
cNZBList.push_back(pFileInfo->GetNZBInfo());
|
||||
}
|
||||
}
|
||||
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "queue");
|
||||
fileName[1024-1] = '\0';
|
||||
@@ -66,28 +93,45 @@ bool DiskState::Save(DownloadQueue* pDownloadQueue, bool OnlyOrder)
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(outfile, "nzbget diskstate file version 1\n");
|
||||
fprintf(outfile, FORMATVERSION_SIGNATURE);
|
||||
|
||||
int cnt = 0;
|
||||
// save nzb-infos
|
||||
fprintf(outfile, "%i\n", cNZBList.size());
|
||||
for (NZBList::iterator it = cNZBList.begin(); it != cNZBList.end(); it++)
|
||||
{
|
||||
NZBInfo* pNZBInfo = *it;
|
||||
fprintf(outfile, "%s\n", pNZBInfo->GetFilename());
|
||||
fprintf(outfile, "%s\n", pNZBInfo->GetDestDir());
|
||||
fprintf(outfile, "%i\n", pNZBInfo->GetFileCount());
|
||||
unsigned long High, Low;
|
||||
Util::SplitInt64(pNZBInfo->GetSize(), &High, &Low);
|
||||
fprintf(outfile, "%lu,%lu\n", High, Low);
|
||||
}
|
||||
|
||||
// save file-infos
|
||||
fprintf(outfile, "%i\n", pDownloadQueue->size());
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (!pFileInfo->GetDeleted())
|
||||
{
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), pFileInfo->GetID());
|
||||
fileName[1024-1] = '\0';
|
||||
fprintf(outfile, "%i,%i\n", pFileInfo->GetID(), (int)pFileInfo->GetPaused());
|
||||
if (!OnlyOrder)
|
||||
// find index of nzb-info
|
||||
int iNZBIndex = 0;
|
||||
for (unsigned int i = 0; i < cNZBList.size(); i++)
|
||||
{
|
||||
SaveFileInfo(pFileInfo, fileName);
|
||||
iNZBIndex++;
|
||||
if (cNZBList[i] == pFileInfo->GetNZBInfo())
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
cnt++;
|
||||
|
||||
fprintf(outfile, "%i,%i,%i\n", pFileInfo->GetID(), iNZBIndex, (int)pFileInfo->GetPaused());
|
||||
}
|
||||
}
|
||||
fclose(outfile);
|
||||
|
||||
if (cnt == 0)
|
||||
if (pDownloadQueue->empty())
|
||||
{
|
||||
remove(fileName);
|
||||
}
|
||||
@@ -95,10 +139,13 @@ bool DiskState::Save(DownloadQueue* pDownloadQueue, bool OnlyOrder)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DiskState::Load(DownloadQueue* pDownloadQueue)
|
||||
bool DiskState::LoadDownloadQueue(DownloadQueue* pDownloadQueue)
|
||||
{
|
||||
debug("Loading queue from disk");
|
||||
|
||||
typedef std::deque<NZBInfo*> NZBList;
|
||||
NZBList cNZBList;
|
||||
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "queue");
|
||||
fileName[1024-1] = '\0';
|
||||
@@ -111,42 +158,84 @@ bool DiskState::Load(DownloadQueue* pDownloadQueue)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool res = false;
|
||||
char FileSignatur[128];
|
||||
fgets(FileSignatur, sizeof(FileSignatur), infile);
|
||||
if (!strcmp(FileSignatur, "nzbget diskstate file version 1\n"))
|
||||
{
|
||||
int id, paused;
|
||||
while (fscanf(infile, "%i,%i\n", &id, &paused) != EOF)
|
||||
{
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), id);
|
||||
fileName[1024-1] = '\0';
|
||||
FileInfo* pFileInfo = new FileInfo();
|
||||
bool res = LoadFileInfo(pFileInfo, fileName);
|
||||
if (res)
|
||||
{
|
||||
pFileInfo->SetID(id);
|
||||
pFileInfo->SetPaused(paused);
|
||||
pDownloadQueue->push_back(pFileInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Could not load diskstate for file %s", fileName);
|
||||
delete pFileInfo;
|
||||
}
|
||||
}
|
||||
res = true;
|
||||
}
|
||||
else
|
||||
if (strcmp(FileSignatur, FORMATVERSION_SIGNATURE))
|
||||
{
|
||||
error("Could not load diskstate due file version mismatch");
|
||||
res = false;
|
||||
fclose(infile);
|
||||
return false;
|
||||
}
|
||||
|
||||
int size;
|
||||
char buf[1024];
|
||||
|
||||
// load nzb-infos
|
||||
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
NZBInfo* pNZBInfo = new NZBInfo();
|
||||
cNZBList.push_back(pNZBInfo);
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
pNZBInfo->SetFilename(buf);
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
pNZBInfo->SetDestDir(buf);
|
||||
|
||||
int iFileCount;
|
||||
if (fscanf(infile, "%i\n", &iFileCount) != 1) goto error;
|
||||
pNZBInfo->SetFileCount(iFileCount);
|
||||
|
||||
unsigned long High, Low;
|
||||
if (fscanf(infile, "%lu,%lu\n", &High, &Low) != 2) goto error;
|
||||
pNZBInfo->SetSize(Util::JoinInt64(High, Low));
|
||||
}
|
||||
|
||||
// load file-infos
|
||||
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
unsigned int id, iNZBIndex, paused;
|
||||
if (fscanf(infile, "%i,%i,%i\n", &id, &iNZBIndex, &paused) != 3) goto error;
|
||||
if (iNZBIndex < 0 || iNZBIndex > cNZBList.size()) goto error;
|
||||
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), id);
|
||||
fileName[1024-1] = '\0';
|
||||
FileInfo* pFileInfo = new FileInfo();
|
||||
bool res = LoadFileInfo(pFileInfo, fileName, true, false);
|
||||
if (res)
|
||||
{
|
||||
pFileInfo->SetID(id);
|
||||
pFileInfo->SetPaused(paused);
|
||||
pFileInfo->SetNZBInfo(cNZBList[iNZBIndex - 1]);
|
||||
pDownloadQueue->push_back(pFileInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Could not load diskstate for file %s", fileName);
|
||||
delete pFileInfo;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(infile);
|
||||
return true;
|
||||
|
||||
return res;
|
||||
error:
|
||||
fclose(infile);
|
||||
error("Error reading diskstate for file %s", fileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DiskState::SaveFile(FileInfo* pFileInfo)
|
||||
{
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), pFileInfo->GetID());
|
||||
fileName[1024-1] = '\0';
|
||||
return SaveFileInfo(pFileInfo, fileName);
|
||||
}
|
||||
|
||||
bool DiskState::SaveFileInfo(FileInfo* pFileInfo, const char* szFilename)
|
||||
@@ -161,13 +250,12 @@ bool DiskState::SaveFileInfo(FileInfo* pFileInfo, const char* szFilename)
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(outfile, "%s\n", pFileInfo->GetNZBFilename());
|
||||
fprintf(outfile, "%s\n", pFileInfo->GetSubject());
|
||||
fprintf(outfile, "%s\n", pFileInfo->GetDestDir());
|
||||
fprintf(outfile, "%s\n", pFileInfo->GetFilename());
|
||||
fprintf(outfile, "%i\n", pFileInfo->GetFilenameConfirmed());
|
||||
|
||||
fprintf(outfile, "%lu,%lu\n", (unsigned long)(pFileInfo->GetSize() >> 32), (unsigned long)(pFileInfo->GetSize()));
|
||||
unsigned long High, Low;
|
||||
Util::SplitInt64(pFileInfo->GetSize(), &High, &Low);
|
||||
fprintf(outfile, "%lu,%lu\n", High, Low);
|
||||
|
||||
fprintf(outfile, "%i\n", pFileInfo->GetGroups()->size());
|
||||
for (FileInfo::Groups::iterator it = pFileInfo->GetGroups()->begin(); it != pFileInfo->GetGroups()->end(); it++)
|
||||
@@ -187,7 +275,15 @@ bool DiskState::SaveFileInfo(FileInfo* pFileInfo, const char* szFilename)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DiskState::LoadFileInfo(FileInfo* pFileInfo, const char * szFilename)
|
||||
bool DiskState::LoadArticles(FileInfo* pFileInfo)
|
||||
{
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), pFileInfo->GetID());
|
||||
fileName[1024-1] = '\0';
|
||||
return LoadFileInfo(pFileInfo, fileName, false, true);
|
||||
}
|
||||
|
||||
bool DiskState::LoadFileInfo(FileInfo* pFileInfo, const char * szFilename, bool bFileSummary, bool bArticles)
|
||||
{
|
||||
debug("Loading FileInfo from disk");
|
||||
|
||||
@@ -203,28 +299,20 @@ bool DiskState::LoadFileInfo(FileInfo* pFileInfo, const char * szFilename)
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
pFileInfo->SetNZBFilename(buf);
|
||||
if (bFileSummary) pFileInfo->SetSubject(buf);
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
pFileInfo->SetSubject(buf);
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
pFileInfo->SetDestDir(buf);
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
pFileInfo->SetFilename(buf);
|
||||
if (bFileSummary) pFileInfo->SetFilename(buf);
|
||||
|
||||
int iFilenameConfirmed;
|
||||
if (fscanf(infile, "%i\n", &iFilenameConfirmed) != 1) goto error;
|
||||
pFileInfo->SetFilenameConfirmed(iFilenameConfirmed);
|
||||
if (bFileSummary) pFileInfo->SetFilenameConfirmed(iFilenameConfirmed);
|
||||
|
||||
unsigned long High, Low;
|
||||
if (fscanf(infile, "%lu,%lu\n", &High, &Low) != 2) goto error;
|
||||
pFileInfo->SetSize((((unsigned long long)High) << 32) + Low);
|
||||
pFileInfo->SetRemainingSize(pFileInfo->GetSize());
|
||||
if (bFileSummary) pFileInfo->SetSize(Util::JoinInt64(High, Low));
|
||||
if (bFileSummary) pFileInfo->SetRemainingSize(pFileInfo->GetSize());
|
||||
|
||||
int size;
|
||||
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
||||
@@ -232,23 +320,26 @@ bool DiskState::LoadFileInfo(FileInfo* pFileInfo, const char * szFilename)
|
||||
{
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
pFileInfo->GetGroups()->push_back(strdup(buf));
|
||||
if (bFileSummary) pFileInfo->GetGroups()->push_back(strdup(buf));
|
||||
}
|
||||
|
||||
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
||||
for (int i = 0; i < size; i++)
|
||||
if (bArticles)
|
||||
{
|
||||
int PartNumber, PartSize;
|
||||
if (fscanf(infile, "%i,%i\n", &PartNumber, &PartSize) != 2) goto error;
|
||||
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
int PartNumber, PartSize;
|
||||
if (fscanf(infile, "%i,%i\n", &PartNumber, &PartSize) != 2) goto error;
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
|
||||
ArticleInfo* pArticleInfo = new ArticleInfo();
|
||||
pArticleInfo->SetPartNumber(PartNumber);
|
||||
pArticleInfo->SetSize(PartSize);
|
||||
pArticleInfo->SetMessageID(buf);
|
||||
pFileInfo->GetArticles()->push_back(pArticleInfo);
|
||||
ArticleInfo* pArticleInfo = new ArticleInfo();
|
||||
pArticleInfo->SetPartNumber(PartNumber);
|
||||
pArticleInfo->SetSize(PartSize);
|
||||
pArticleInfo->SetMessageID(buf);
|
||||
pFileInfo->GetArticles()->push_back(pArticleInfo);
|
||||
}
|
||||
}
|
||||
|
||||
fclose(infile);
|
||||
@@ -260,11 +351,128 @@ error:
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DiskState::SavePostQueue(PostQueue* pPostQueue, bool bCompleted)
|
||||
{
|
||||
debug("Saving post-queue to disk");
|
||||
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), bCompleted ? "postc" : "postq");
|
||||
fileName[1024-1] = '\0';
|
||||
|
||||
FILE* outfile = fopen(fileName, "w");
|
||||
|
||||
if (!outfile)
|
||||
{
|
||||
error("Could not create file %s", fileName);
|
||||
perror(fileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
fprintf(outfile, FORMATVERSION_SIGNATURE);
|
||||
|
||||
fprintf(outfile, "%i\n", pPostQueue->size());
|
||||
for (PostQueue::iterator it = pPostQueue->begin(); it != pPostQueue->end(); it++)
|
||||
{
|
||||
PostInfo* pPostInfo = *it;
|
||||
fprintf(outfile, "%s\n", pPostInfo->GetNZBFilename());
|
||||
fprintf(outfile, "%s\n", pPostInfo->GetDestDir());
|
||||
fprintf(outfile, "%s\n", pPostInfo->GetParFilename());
|
||||
fprintf(outfile, "%s\n", pPostInfo->GetInfoName());
|
||||
fprintf(outfile, "%i\n", (int)pPostInfo->GetParCheck());
|
||||
fprintf(outfile, "%i\n", (int)pPostInfo->GetParStatus());
|
||||
fprintf(outfile, "%i\n", (int)pPostInfo->GetParFailed());
|
||||
fprintf(outfile, "%i\n", (int)pPostInfo->GetStage());
|
||||
}
|
||||
fclose(outfile);
|
||||
|
||||
if (pPostQueue->empty())
|
||||
{
|
||||
remove(fileName);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DiskState::LoadPostQueue(PostQueue* pPostQueue, bool bCompleted)
|
||||
{
|
||||
debug("Loading post-queue from disk");
|
||||
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), bCompleted ? "postc" : "postq");
|
||||
fileName[1024-1] = '\0';
|
||||
|
||||
FILE* infile = fopen(fileName, "r");
|
||||
|
||||
if (!infile)
|
||||
{
|
||||
error("Could not open file %s", fileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
char FileSignatur[128];
|
||||
fgets(FileSignatur, sizeof(FileSignatur), infile);
|
||||
if (strcmp(FileSignatur, FORMATVERSION_SIGNATURE))
|
||||
{
|
||||
error("Could not load diskstate due file version mismatch");
|
||||
fclose(infile);
|
||||
return false;
|
||||
}
|
||||
|
||||
int size;
|
||||
char buf[1024];
|
||||
int iIntValue;
|
||||
|
||||
// load file-infos
|
||||
if (fscanf(infile, "%i\n", &size) != 1) goto error;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
PostInfo* pPostInfo = new PostInfo();
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
pPostInfo->SetNZBFilename(buf);
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
pPostInfo->SetDestDir(buf);
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
pPostInfo->SetParFilename(buf);
|
||||
|
||||
if (!fgets(buf, sizeof(buf), infile)) goto error;
|
||||
if (buf[0] != 0) buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
pPostInfo->SetInfoName(buf);
|
||||
|
||||
if (fscanf(infile, "%i\n", &iIntValue) != 1) goto error;
|
||||
pPostInfo->SetParCheck(iIntValue);
|
||||
|
||||
if (fscanf(infile, "%i\n", &iIntValue) != 1) goto error;
|
||||
pPostInfo->SetParStatus(iIntValue);
|
||||
|
||||
if (fscanf(infile, "%i\n", &iIntValue) != 1) goto error;
|
||||
pPostInfo->SetParFailed(iIntValue);
|
||||
|
||||
if (fscanf(infile, "%i\n", &iIntValue) != 1) goto error;
|
||||
pPostInfo->SetStage((PostInfo::EStage)iIntValue);
|
||||
|
||||
pPostQueue->push_back(pPostInfo);
|
||||
}
|
||||
|
||||
fclose(infile);
|
||||
return true;
|
||||
|
||||
error:
|
||||
fclose(infile);
|
||||
error("Error reading diskstate for file %s", fileName);
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Delete all files from Queue.
|
||||
* Returns true if successful, false if not
|
||||
*/
|
||||
bool DiskState::Discard()
|
||||
bool DiskState::DiscardDownloadQueue()
|
||||
{
|
||||
debug("Discarding queue");
|
||||
|
||||
@@ -283,15 +491,31 @@ bool DiskState::Discard()
|
||||
bool res = false;
|
||||
char FileSignatur[128];
|
||||
fgets(FileSignatur, sizeof(FileSignatur), infile);
|
||||
if (!strcmp(FileSignatur, "nzbget diskstate file version 1\n"))
|
||||
if (!strcmp(FileSignatur, FORMATVERSION_SIGNATURE))
|
||||
{
|
||||
int id, paused;
|
||||
while (fscanf(infile, "%i,%i\n", &id, &paused) == 2)
|
||||
// skip nzb-infos
|
||||
int size = 0;
|
||||
char buf[1024];
|
||||
fscanf(infile, "%i\n", &size);
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), id);
|
||||
fileName[1024-1] = '\0';
|
||||
remove(fileName);
|
||||
if (!fgets(buf, sizeof(buf), infile)) break;
|
||||
if (!fgets(buf, sizeof(buf), infile)) break;
|
||||
if (!fgets(buf, sizeof(buf), infile)) break;
|
||||
if (!fgets(buf, sizeof(buf), infile)) break;
|
||||
}
|
||||
|
||||
fscanf(infile, "%i\n", &size);
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
int id, group, paused;
|
||||
if (fscanf(infile, "%i,%i,%i\n", &id, &group, &paused) == 3)
|
||||
{
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), id);
|
||||
fileName[1024-1] = '\0';
|
||||
remove(fileName);
|
||||
}
|
||||
}
|
||||
res = true;
|
||||
}
|
||||
@@ -310,27 +534,56 @@ bool DiskState::Discard()
|
||||
return res;
|
||||
}
|
||||
|
||||
bool DiskState::Exists()
|
||||
/*
|
||||
* Delete all files from Queue.
|
||||
* Returns true if successful, false if not
|
||||
*/
|
||||
bool DiskState::DiscardPostQueue()
|
||||
{
|
||||
debug("Discarding post-queue");
|
||||
|
||||
char fileName[1024];
|
||||
|
||||
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "postq");
|
||||
fileName[1024-1] = '\0';
|
||||
remove(fileName);
|
||||
|
||||
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "postc");
|
||||
fileName[1024-1] = '\0';
|
||||
remove(fileName);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DiskState::DownloadQueueExists()
|
||||
{
|
||||
debug("Checking if a saved queue exists on disk");
|
||||
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), "queue");
|
||||
fileName[1024-1] = '\0';
|
||||
struct stat buffer;
|
||||
bool fileExists = !stat(fileName, &buffer);
|
||||
return fileExists;
|
||||
return Util::FileExists(fileName);
|
||||
}
|
||||
|
||||
bool DiskState::DiscardFileInfo(DownloadQueue* pDownloadQueue, FileInfo * pFileInfo)
|
||||
bool DiskState::PostQueueExists(bool bCompleted)
|
||||
{
|
||||
// delete diskstate-file
|
||||
debug("Checking if a saved queue exists on disk");
|
||||
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%s", g_pOptions->GetQueueDir(), bCompleted ? "postc" : "postq");
|
||||
fileName[1024-1] = '\0';
|
||||
return Util::FileExists(fileName);
|
||||
}
|
||||
|
||||
bool DiskState::DiscardFile(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo)
|
||||
{
|
||||
// delete diskstate-file for file-info
|
||||
char fileName[1024];
|
||||
snprintf(fileName, 1024, "%s%i", g_pOptions->GetQueueDir(), pFileInfo->GetID());
|
||||
fileName[1024-1] = '\0';
|
||||
remove(fileName);
|
||||
|
||||
return Save(pDownloadQueue, true);
|
||||
return !pDownloadQueue || SaveDownloadQueue(pDownloadQueue);
|
||||
}
|
||||
|
||||
void DiskState::CleanupTempDir(DownloadQueue* pDownloadQueue)
|
||||
@@ -349,11 +602,14 @@ void DiskState::CleanupTempDir(DownloadQueue* pDownloadQueue)
|
||||
DirBrowser dir(g_pOptions->GetTempDir());
|
||||
while (const char* filename = dir.Next())
|
||||
{
|
||||
bool del = strstr(filename, ".tmp") || strstr(filename, ".dec");
|
||||
int id, part;
|
||||
bool del = strstr(filename, ".tmp") || strstr(filename, ".dec") ||
|
||||
((sscanf(filename, "%i.out", &id) == 1) &&
|
||||
!(g_pOptions->GetContinuePartial() && g_pOptions->GetDirectWrite()));
|
||||
if (!del)
|
||||
{
|
||||
int id, part;
|
||||
if (sscanf(filename, "%i.%i", &id, &part) == 2)
|
||||
if ((sscanf(filename, "%i.%i", &id, &part) == 2) ||
|
||||
(sscanf(filename, "%i.out", &id) == 1))
|
||||
{
|
||||
del = true;
|
||||
ptr = ids;
|
||||
|
||||
21
DiskState.h
21
DiskState.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
@@ -27,19 +27,26 @@
|
||||
#define DISKSTATE_H
|
||||
|
||||
#include "DownloadInfo.h"
|
||||
#include "PostInfo.h"
|
||||
|
||||
class DiskState
|
||||
{
|
||||
private:
|
||||
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);
|
||||
|
||||
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 SavePostQueue(PostQueue* pPostQueue, bool bCompleted);
|
||||
bool LoadPostQueue(PostQueue* pPostQueue, bool bCompleted);
|
||||
bool DiscardDownloadQueue();
|
||||
bool DiscardPostQueue();
|
||||
bool DiscardFile(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
|
||||
void CleanupTempDir(DownloadQueue* pDownloadQueue);
|
||||
};
|
||||
|
||||
|
||||
338
DownloadInfo.cpp
338
DownloadInfo.cpp
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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>
|
||||
@@ -34,21 +34,116 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <sys/stat.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
int FileInfo::m_iIDGen = 0;
|
||||
|
||||
NZBInfo::NZBInfo()
|
||||
{
|
||||
debug("Creating NZBInfo");
|
||||
|
||||
m_szFilename = NULL;
|
||||
m_szDestDir = NULL;
|
||||
m_iFileCount = 0;
|
||||
m_lSize = 0;
|
||||
m_iRefCount = 0;
|
||||
}
|
||||
|
||||
NZBInfo::~NZBInfo()
|
||||
{
|
||||
debug("Destroying NZBInfo");
|
||||
|
||||
if (m_szFilename)
|
||||
{
|
||||
free(m_szFilename);
|
||||
}
|
||||
if (m_szDestDir)
|
||||
{
|
||||
free(m_szDestDir);
|
||||
}
|
||||
}
|
||||
|
||||
void NZBInfo::AddReference()
|
||||
{
|
||||
m_iRefCount++;
|
||||
}
|
||||
|
||||
void NZBInfo::Release()
|
||||
{
|
||||
m_iRefCount--;
|
||||
if (m_iRefCount <= 0)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
}
|
||||
|
||||
void NZBInfo::SetDestDir(const char* szDestDir)
|
||||
{
|
||||
m_szDestDir = strdup(szDestDir);
|
||||
}
|
||||
|
||||
void NZBInfo::SetFilename(const char * szFilename)
|
||||
{
|
||||
m_szFilename = strdup(szFilename);
|
||||
}
|
||||
|
||||
void NZBInfo::GetNiceNZBName(char* szBuffer, int iSize)
|
||||
{
|
||||
MakeNiceNZBName(m_szFilename, szBuffer, iSize);
|
||||
}
|
||||
|
||||
void NZBInfo::MakeNiceNZBName(const char * szNZBFilename, char * szBuffer, int iSize)
|
||||
{
|
||||
char postname[1024];
|
||||
const char* szBaseName = Util::BaseFileName(szNZBFilename);
|
||||
|
||||
// if .nzb file has a certain structure, try to strip out certain elements
|
||||
if (sscanf(szBaseName, "msgid_%*d_%1023s", postname) == 1)
|
||||
{
|
||||
// OK, using stripped name
|
||||
}
|
||||
else
|
||||
{
|
||||
// using complete filename
|
||||
strncpy(postname, szBaseName, 1024);
|
||||
postname[1024-1] = '\0';
|
||||
}
|
||||
|
||||
// wipe out ".nzb"
|
||||
if (char* p = strrchr(postname, '.')) *p = '\0';
|
||||
|
||||
Util::MakeValidFilename(postname, '_');
|
||||
|
||||
// if the resulting name is empty, use basename without cleaning up "msgid_"
|
||||
if (strlen(postname) == 0)
|
||||
{
|
||||
// using complete filename
|
||||
strncpy(postname, szBaseName, 1024);
|
||||
postname[1024-1] = '\0';
|
||||
|
||||
// wipe out ".nzb"
|
||||
if (char* p = strrchr(postname, '.')) *p = '\0';
|
||||
|
||||
Util::MakeValidFilename(postname, '_');
|
||||
|
||||
// if the resulting name is STILL empty, use "noname"
|
||||
if (strlen(postname) == 0)
|
||||
{
|
||||
strncpy(postname, "noname", 1024);
|
||||
}
|
||||
}
|
||||
|
||||
strncpy(szBuffer, postname, iSize);
|
||||
szBuffer[iSize-1] = '\0';
|
||||
}
|
||||
|
||||
ArticleInfo::ArticleInfo()
|
||||
{
|
||||
//debug("Creating ArticleInfo");
|
||||
@@ -92,13 +187,13 @@ FileInfo::FileInfo()
|
||||
m_szSubject = NULL;
|
||||
m_szFilename = NULL;
|
||||
m_bFilenameConfirmed = false;
|
||||
m_szDestDir = NULL;
|
||||
m_szNZBFilename = NULL;
|
||||
m_lSize = 0;
|
||||
m_lRemainingSize = 0;
|
||||
m_bPaused = false;
|
||||
m_bDeleted = false;
|
||||
m_iCompleted = 0;
|
||||
m_bOutputInitialized = false;
|
||||
m_pNZBInfo = NULL;
|
||||
m_iIDGen++;
|
||||
m_iID = m_iIDGen;
|
||||
}
|
||||
@@ -115,26 +210,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 +243,17 @@ void FileInfo::SetID(int s)
|
||||
}
|
||||
}
|
||||
|
||||
void FileInfo::SetNZBInfo(NZBInfo* pNZBInfo)
|
||||
{
|
||||
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 +263,104 @@ void FileInfo::SetFilename(const char* szFilename)
|
||||
m_szFilename = strdup(szFilename);
|
||||
}
|
||||
|
||||
void FileInfo::BuildDestDirName(const char* szNZBFilename)
|
||||
void FileInfo::MakeValidFilename()
|
||||
{
|
||||
char szBuffer[1024];
|
||||
|
||||
if (g_pOptions->GetAppendNZBDir())
|
||||
{
|
||||
char postname[1024];
|
||||
const char* szBaseName = BaseFileName(szNZBFilename);
|
||||
|
||||
// if .nzb file has a certain structure, try to strip out certain elements
|
||||
if (sscanf(szBaseName, "msgid_%*d_%1023s", postname) == 1)
|
||||
{
|
||||
// wipe out certain structure
|
||||
memset(strrchr(postname, '.'), 0, postname + strlen(postname) - strrchr(postname, '.'));
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(postname, szBaseName, 1024);
|
||||
postname[1024-1] = '\0';
|
||||
}
|
||||
// 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';
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(szBuffer, g_pOptions->GetDestDir(), 1024);
|
||||
szBuffer[1024-1] = '\0'; // trim the last slash, always returned by GetDestDir()
|
||||
}
|
||||
|
||||
m_szDestDir = strdup(szBuffer);
|
||||
Util::MakeValidFilename(m_szFilename, '_');
|
||||
}
|
||||
|
||||
bool FileInfo::IsDupe()
|
||||
void FileInfo::LockOutputFile()
|
||||
{
|
||||
debug("Checking if the file was already downloaded or queued");
|
||||
m_mutexOutputFile.Lock();
|
||||
}
|
||||
|
||||
struct stat buffer;
|
||||
void FileInfo::UnlockOutputFile()
|
||||
{
|
||||
m_mutexOutputFile.Unlock();
|
||||
}
|
||||
|
||||
bool FileInfo::IsDupe(const char* szFilename)
|
||||
{
|
||||
char fileName[1024];
|
||||
bool exists = false;
|
||||
|
||||
snprintf(fileName, 1024, "%s%c%s", GetDestDir(), (int)PATH_SEPARATOR, GetFilename());
|
||||
snprintf(fileName, 1024, "%s%c%s", m_pNZBInfo->GetDestDir(), (int)PATH_SEPARATOR, szFilename);
|
||||
fileName[1024-1] = '\0';
|
||||
exists = !stat(fileName, &buffer);
|
||||
if (!exists)
|
||||
if (Util::FileExists(fileName))
|
||||
{
|
||||
snprintf(fileName, 1024, "%s%c%s_broken", GetDestDir(), (int)PATH_SEPARATOR, GetFilename());
|
||||
fileName[1024-1] = '\0';
|
||||
exists = !stat(fileName, &buffer);
|
||||
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;
|
||||
}
|
||||
|
||||
return exists;
|
||||
return false;
|
||||
}
|
||||
|
||||
GroupInfo::GroupInfo()
|
||||
{
|
||||
m_iFirstID = 0;
|
||||
m_iLastID = 0;
|
||||
m_iRemainingFileCount = 0;
|
||||
m_lRemainingSize = 0;
|
||||
m_lPausedSize = 0;
|
||||
m_iRemainingParCount = 0;
|
||||
}
|
||||
|
||||
GroupInfo::~GroupInfo()
|
||||
{
|
||||
if (m_pNZBInfo)
|
||||
{
|
||||
m_pNZBInfo->Release();
|
||||
}
|
||||
}
|
||||
|
||||
void GroupInfo::BuildGroups(DownloadQueue* pDownloadQueue, GroupQueue* pGroupQueue)
|
||||
{
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
GroupInfo* pGroupInfo = NULL;
|
||||
for (GroupQueue::iterator itg = pGroupQueue->begin(); itg != pGroupQueue->end(); itg++)
|
||||
{
|
||||
GroupInfo* pGroupInfo1 = *itg;
|
||||
if (pGroupInfo1->GetNZBInfo() == pFileInfo->GetNZBInfo())
|
||||
{
|
||||
pGroupInfo = pGroupInfo1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!pGroupInfo)
|
||||
{
|
||||
pGroupInfo = new GroupInfo();
|
||||
pGroupInfo->m_pNZBInfo = pFileInfo->GetNZBInfo();
|
||||
pGroupInfo->m_pNZBInfo->AddReference();
|
||||
pGroupInfo->m_iFirstID = pFileInfo->GetID();
|
||||
pGroupInfo->m_iLastID = pFileInfo->GetID();
|
||||
pGroupQueue->push_back(pGroupInfo);
|
||||
}
|
||||
if (pFileInfo->GetID() < pGroupInfo->GetFirstID())
|
||||
{
|
||||
pGroupInfo->m_iFirstID = pFileInfo->GetID();
|
||||
}
|
||||
if (pFileInfo->GetID() > pGroupInfo->GetLastID())
|
||||
{
|
||||
pGroupInfo->m_iLastID = pFileInfo->GetID();
|
||||
}
|
||||
pGroupInfo->m_iRemainingFileCount++;
|
||||
pGroupInfo->m_lRemainingSize += pFileInfo->GetRemainingSize();
|
||||
if (pFileInfo->GetPaused())
|
||||
{
|
||||
pGroupInfo->m_lPausedSize += pFileInfo->GetRemainingSize();
|
||||
}
|
||||
|
||||
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++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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>
|
||||
@@ -30,6 +30,34 @@
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
|
||||
#include "Thread.h"
|
||||
|
||||
class NZBInfo
|
||||
{
|
||||
private:
|
||||
int m_iRefCount;
|
||||
char* m_szFilename;
|
||||
char* m_szDestDir;
|
||||
int m_iFileCount;
|
||||
long long m_lSize;
|
||||
|
||||
public:
|
||||
NZBInfo();
|
||||
~NZBInfo();
|
||||
void AddReference();
|
||||
void Release();
|
||||
const char* GetFilename() { return m_szFilename; }
|
||||
void SetFilename(const char* szFilename);
|
||||
void GetNiceNZBName(char* szBuffer, int iSize);
|
||||
static void MakeNiceNZBName(const char* szNZBFilename, char* szBuffer, int iSize);
|
||||
const char* GetDestDir() { return m_szDestDir; }
|
||||
void SetDestDir(const char* szDestDir);
|
||||
long long GetSize() { return m_lSize; }
|
||||
void SetSize(long long s) { m_lSize = s; }
|
||||
int GetFileCount() { return m_iFileCount; }
|
||||
void SetFileCount(int s) { m_iFileCount = s; }
|
||||
};
|
||||
|
||||
class ArticleInfo
|
||||
{
|
||||
public:
|
||||
@@ -71,18 +99,19 @@ 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;
|
||||
bool m_bPaused;
|
||||
bool m_bDeleted;
|
||||
bool m_bFilenameConfirmed;
|
||||
int m_iCompleted;
|
||||
bool m_bOutputInitialized;
|
||||
Mutex m_mutexOutputFile;
|
||||
|
||||
static int m_iIDGen;
|
||||
|
||||
@@ -91,16 +120,15 @@ 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; }
|
||||
@@ -111,15 +139,44 @@ public:
|
||||
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);
|
||||
};
|
||||
|
||||
typedef std::deque<FileInfo*> DownloadQueue;
|
||||
|
||||
class GroupInfo;
|
||||
typedef std::deque<GroupInfo*> GroupQueue;
|
||||
|
||||
class GroupInfo
|
||||
{
|
||||
private:
|
||||
NZBInfo* m_pNZBInfo;
|
||||
int m_iFirstID;
|
||||
int m_iLastID;
|
||||
int m_iRemainingFileCount;
|
||||
long long m_lRemainingSize;
|
||||
long long m_lPausedSize;
|
||||
int m_iRemainingParCount;
|
||||
|
||||
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 GetRemainingParCount() { return m_iRemainingParCount; }
|
||||
|
||||
static void BuildGroups(DownloadQueue* pDownloadQueue, GroupQueue* pGroupQueue);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
206
Frontend.cpp
206
Frontend.cpp
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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>
|
||||
@@ -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"
|
||||
@@ -45,9 +49,12 @@
|
||||
#include "Connection.h"
|
||||
#include "MessageBase.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "PrePostProcessor.h"
|
||||
#include "RemoteClient.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
extern PrePostProcessor* g_pPrePostProcessor;
|
||||
extern Options* g_pOptions;
|
||||
|
||||
Frontend::Frontend()
|
||||
@@ -63,8 +70,14 @@ Frontend::Frontend()
|
||||
m_bPause = false;
|
||||
m_fDownloadLimit = 0;
|
||||
m_iThreadCount = 0;
|
||||
m_iPostJobCount = 0;
|
||||
m_iUpTimeSec = 0;
|
||||
m_iDnTimeSec = 0;
|
||||
m_iAllBytes = 0;
|
||||
m_bStandBy = 0;
|
||||
m_RemoteMessages.clear();
|
||||
m_RemoteQueue.clear();
|
||||
m_iUpdateInterval = g_pOptions->GetUpdateInterval();
|
||||
}
|
||||
|
||||
bool Frontend::PrepareData()
|
||||
@@ -77,7 +90,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->GetServerIP(), g_pOptions->GetServerPort());
|
||||
Stop();
|
||||
return false;
|
||||
}
|
||||
@@ -91,6 +104,10 @@ bool Frontend::PrepareData()
|
||||
m_bPause = g_pOptions->GetPause();
|
||||
m_fDownloadLimit = g_pOptions->GetDownloadRate();
|
||||
m_iThreadCount = Thread::GetThreadCount();
|
||||
PostQueue* pPostQueue = g_pPrePostProcessor->LockPostQueue();
|
||||
m_iPostJobCount = pPostQueue->size();
|
||||
g_pPrePostProcessor->UnlockPostQueue();
|
||||
g_pQueueCoordinator->CalcStat(&m_iUpTimeSec, &m_iDnTimeSec, &m_iAllBytes, &m_bStandBy);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -195,68 +212,24 @@ 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(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);
|
||||
}
|
||||
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;
|
||||
pMessageBase->m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
|
||||
pMessageBase->m_iType = htonl(iRequest);
|
||||
pMessageBase->m_iStructSize = htonl(iSize);
|
||||
strncpy(pMessageBase->m_szPassword, g_pOptions->GetServerPassword(), NZBREQUESTPASSWORDSIZE);
|
||||
pMessageBase->m_szPassword[NZBREQUESTPASSWORDSIZE - 1] = '\0';
|
||||
}
|
||||
@@ -273,11 +246,11 @@ bool Frontend::RequestMessages()
|
||||
}
|
||||
|
||||
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 +263,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 +285,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);
|
||||
@@ -342,9 +318,9 @@ bool Frontend::RequestFileList()
|
||||
}
|
||||
|
||||
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 +328,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,40 +352,67 @@ 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_bPause = ntohl(ListResponse.m_bServerPaused);
|
||||
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_bServerStandBy);
|
||||
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;
|
||||
typedef std::deque<NZBInfo*> NZBList;
|
||||
NZBList cNZBList;
|
||||
|
||||
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;
|
||||
char* pBufPtr = (char*)pBuf;
|
||||
for (unsigned int i = 0; i < ntohl(ListResponse.m_iNrTrailingEntries); i++)
|
||||
{
|
||||
SNZBListResponseEntry* pListAnswer = (SNZBListResponseEntry*) pBufPtr;
|
||||
|
||||
char* szNZBFilename = pBufPtr + sizeof(SNZBListResponseEntry);
|
||||
char* szSubject = pBufPtr + sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen);
|
||||
char* szFileName = pBufPtr + sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) + ntohl(pListAnswer->m_iSubjectLen);
|
||||
char* szDestDir = pBufPtr + sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) + ntohl(pListAnswer->m_iSubjectLen) + ntohl(pListAnswer->m_iFilenameLen);
|
||||
|
||||
FileInfo* pFileInfo = new FileInfo();
|
||||
pFileInfo->SetID(pListAnswer->m_iID);
|
||||
pFileInfo->SetSize(pListAnswer->m_iFileSize);
|
||||
pFileInfo->SetRemainingSize(pListAnswer->m_iRemainingSize);
|
||||
pFileInfo->SetPaused(pListAnswer->m_bPaused);
|
||||
pFileInfo->SetNZBFilename(szNZBFilename);
|
||||
pFileInfo->SetID(ntohl(pListAnswer->m_iID));
|
||||
pFileInfo->SetSize(Util::JoinInt64(ntohl(pListAnswer->m_iFileSizeHi), ntohl(pListAnswer->m_iFileSizeLo)));
|
||||
pFileInfo->SetRemainingSize(Util::JoinInt64(ntohl(pListAnswer->m_iRemainingSizeHi), ntohl(pListAnswer->m_iRemainingSizeLo)));
|
||||
pFileInfo->SetPaused(ntohl(pListAnswer->m_bPaused));
|
||||
pFileInfo->SetSubject(szSubject);
|
||||
pFileInfo->SetFilename(szFileName);
|
||||
pFileInfo->SetFilenameConfirmed(pListAnswer->m_bFilenameConfirmed);
|
||||
pFileInfo->SetDestDir(szDestDir);
|
||||
pFileInfo->SetFilenameConfirmed(ntohl(pListAnswer->m_bFilenameConfirmed));
|
||||
|
||||
// find nzb-info or create new
|
||||
NZBInfo* pNZBInfo = NULL;
|
||||
for (NZBList::iterator it = cNZBList.begin(); it != cNZBList.end(); it++)
|
||||
{
|
||||
NZBInfo* pNZBInfo2 = *it;
|
||||
if (!strcmp(pNZBInfo2->GetFilename(), szNZBFilename))
|
||||
{
|
||||
pNZBInfo = pNZBInfo2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!pNZBInfo)
|
||||
{
|
||||
pNZBInfo = new NZBInfo();
|
||||
pNZBInfo->SetFilename(szNZBFilename);
|
||||
pNZBInfo->SetDestDir(szDestDir);
|
||||
cNZBList.push_back(pNZBInfo);
|
||||
}
|
||||
|
||||
pFileInfo->SetNZBInfo(pNZBInfo);
|
||||
|
||||
m_RemoteQueue.push_back(pFileInfo);
|
||||
|
||||
pBufPtr += sizeof(SNZBListRequestAnswerEntry) + pListAnswer->m_iNZBFilenameLen + pListAnswer->m_iSubjectLen +
|
||||
pListAnswer->m_iFilenameLen + pListAnswer->m_iDestDirLen;
|
||||
pBufPtr += sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) +
|
||||
ntohl(pListAnswer->m_iSubjectLen) + ntohl(pListAnswer->m_iFilenameLen) + ntohl(pListAnswer->m_iDestDirLen);
|
||||
}
|
||||
}
|
||||
if (pBuf)
|
||||
@@ -438,9 +444,9 @@ bool Frontend::RequestDumpDebug()
|
||||
return client.RequestServerDumpDebug();
|
||||
}
|
||||
|
||||
bool Frontend::RequestEditQueue(int iAction, int iOffset, int iIDFrom, int iIDTo)
|
||||
bool Frontend::RequestEditQueue(int iAction, int iOffset, int iID)
|
||||
{
|
||||
RemoteClient client;
|
||||
client.SetVerbose(false);
|
||||
return client.RequestServerEditQueue(iAction, iOffset, iIDFrom, iIDTo);
|
||||
return client.RequestServerEditQueue(iAction, iOffset, &iID, 1, false);
|
||||
}
|
||||
|
||||
30
Frontend.h
30
Frontend.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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>
|
||||
@@ -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,8 +45,9 @@ 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;
|
||||
@@ -64,6 +55,11 @@ protected:
|
||||
bool m_bPause;
|
||||
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 +68,15 @@ protected:
|
||||
DownloadQueue* LockQueue();
|
||||
void UnlockQueue();
|
||||
bool IsRemoteMode();
|
||||
void InitMessageBase(SNZBMessageBase* pMessageBase, int iRequest, int iSize);
|
||||
void InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize);
|
||||
void ServerPauseUnpause(bool bPause);
|
||||
bool RequestPauseUnpause(bool bPause);
|
||||
void ServerSetDownloadRate(float fRate);
|
||||
bool RequestSetDownloadRate(float fRate);
|
||||
void ServerDumpDebug();
|
||||
bool RequestDumpDebug();
|
||||
bool ServerEditQueue(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(int iAction, int iOffset, int iID);
|
||||
|
||||
public:
|
||||
Frontend();
|
||||
|
||||
37
Log.cpp
37
Log.cpp
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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>
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdarg.h>
|
||||
#include <cstdio>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Options.h"
|
||||
@@ -50,8 +51,7 @@ Log::Log()
|
||||
m_iIDGen = 0;
|
||||
m_szLogFilename = NULL;
|
||||
#ifdef DEBUG
|
||||
struct stat buffer;
|
||||
m_bExtraDebug = !stat("extradebug", &buffer);
|
||||
m_bExtraDebug = Util::FileExists("extradebug");
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -144,11 +144,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);
|
||||
@@ -257,6 +257,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->GetDetailTarget();
|
||||
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 +294,7 @@ void abort(const char* msg, ...)
|
||||
|
||||
g_pLog->m_mutexLog.Lock();
|
||||
|
||||
printf("%s", tmp2);
|
||||
printf("\n%s", tmp2);
|
||||
|
||||
g_pLog->Filelog(tmp2);
|
||||
|
||||
|
||||
7
Log.h
7
Log.h
@@ -35,6 +35,7 @@
|
||||
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 HAVE_VARIADIC_MACROS
|
||||
@@ -49,10 +50,11 @@ class Message
|
||||
public:
|
||||
enum EKind
|
||||
{
|
||||
mkInfo,
|
||||
mkInfo,
|
||||
mkWarning,
|
||||
mkError,
|
||||
mkDebug
|
||||
mkDebug,
|
||||
mkDetail
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -91,6 +93,7 @@ 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 HAVE_VARIADIC_MACROS
|
||||
friend void debug(const char* szFilename, const char* szFuncname, int iLineNr, const char* msg, ...);
|
||||
#else
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
17
Makefile.am
17
Makefile.am
@@ -2,17 +2,18 @@ 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
|
||||
LoggableFrontend.cpp LoggableFrontend.h MessageBase.h NCursesFrontend.cpp NCursesFrontend.h \
|
||||
NNTPConnection.cpp NNTPConnection.h NZBFile.cpp NZBFile.h NetAddress.cpp NetAddress.h \
|
||||
NewsServer.cpp NewsServer.h Observer.cpp Observer.h Options.cpp Options.h \
|
||||
ParChecker.cpp ParChecker.h PrePostProcessor.cpp PrePostProcessor.h \
|
||||
QueueCoordinator.cpp QueueCoordinator.h QueueEditor.cpp QueueEditor.h RemoteClient.cpp \
|
||||
RemoteClient.h RemoteServer.cpp RemoteServer.h ServerPool.cpp ServerPool.h Thread.cpp \
|
||||
Thread.h Util.cpp Util.h nzbget.cpp nzbget.h BinRpc.cpp BinRpc.h XmlRpc.cpp XmlRpc.h \
|
||||
PostInfo.cpp PostInfo.h ScriptController.cpp ScriptController.h
|
||||
|
||||
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
|
||||
Makefile.cvs nzbget.kdevelop nzbget.sln nzbget.vcproj
|
||||
|
||||
clean-bak: rm *~
|
||||
|
||||
8
Makefile.cvs
Normal file
8
Makefile.cvs
Normal file
@@ -0,0 +1,8 @@
|
||||
default: all
|
||||
|
||||
all:
|
||||
aclocal
|
||||
autoheader
|
||||
automake
|
||||
autoconf
|
||||
|
||||
144
Makefile.in
144
Makefile.in
@@ -1,8 +1,8 @@
|
||||
# Makefile.in generated by automake 1.9.5 from Makefile.am.
|
||||
# Makefile.in generated by automake 1.10 from Makefile.am.
|
||||
# @configure_input@
|
||||
|
||||
# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
|
||||
# 2003, 2004, 2005 Free Software Foundation, Inc.
|
||||
# 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
|
||||
# This Makefile.in is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
@@ -14,17 +14,11 @@
|
||||
|
||||
@SET_MAKE@
|
||||
|
||||
SOURCES = $(nzbget_SOURCES)
|
||||
|
||||
srcdir = @srcdir@
|
||||
top_srcdir = @top_srcdir@
|
||||
VPATH = @srcdir@
|
||||
pkgdatadir = $(datadir)/@PACKAGE@
|
||||
pkglibdir = $(libdir)/@PACKAGE@
|
||||
pkgincludedir = $(includedir)/@PACKAGE@
|
||||
top_builddir = .
|
||||
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
|
||||
INSTALL = @INSTALL@
|
||||
install_sh_DATA = $(install_sh) -c -m 644
|
||||
install_sh_PROGRAM = $(install_sh) -c
|
||||
install_sh_SCRIPT = $(install_sh) -c
|
||||
@@ -39,17 +33,17 @@ POST_UNINSTALL = :
|
||||
build_triplet = @build@
|
||||
host_triplet = @host@
|
||||
bin_PROGRAMS = nzbget$(EXEEXT)
|
||||
subdir = .
|
||||
DIST_COMMON = README $(am__configure_deps) $(srcdir)/Makefile.am \
|
||||
$(srcdir)/Makefile.in $(srcdir)/config.h.in \
|
||||
$(top_srcdir)/configure AUTHORS COPYING ChangeLog INSTALL NEWS \
|
||||
config.guess config.sub depcomp install-sh missing
|
||||
subdir = .
|
||||
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
|
||||
am__aclocal_m4_deps = $(top_srcdir)/configure.ac
|
||||
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
|
||||
$(ACLOCAL_M4)
|
||||
am__CONFIG_DISTCLEAN_FILES = config.status config.cache config.log \
|
||||
configure.lineno configure.status.lineno
|
||||
configure.lineno config.status.lineno
|
||||
mkinstalldirs = $(install_sh) -d
|
||||
CONFIG_HEADER = config.h
|
||||
CONFIG_CLEAN_FILES =
|
||||
@@ -60,16 +54,17 @@ am_nzbget_OBJECTS = ArticleDownloader.$(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) \
|
||||
NCursesFrontend.$(OBJEXT) NNTPConnection.$(OBJEXT) \
|
||||
NZBFile.$(OBJEXT) NetAddress.$(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) ServerPool.$(OBJEXT) Thread.$(OBJEXT) \
|
||||
Util.$(OBJEXT) nzbget.$(OBJEXT) BinRpc.$(OBJEXT) \
|
||||
XmlRpc.$(OBJEXT) PostInfo.$(OBJEXT) ScriptController.$(OBJEXT)
|
||||
nzbget_OBJECTS = $(am_nzbget_OBJECTS)
|
||||
nzbget_LDADD = $(LDADD)
|
||||
DEFAULT_INCLUDES = -I. -I$(srcdir) -I.
|
||||
DEFAULT_INCLUDES = -I.@am__isrc@
|
||||
depcomp = $(SHELL) $(top_srcdir)/depcomp
|
||||
am__depfiles_maybe = depfiles
|
||||
CXXCOMPILE = $(CXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \
|
||||
@@ -98,8 +93,6 @@ distuninstallcheck_listfiles = find . -type f -print
|
||||
distcleancheck_listfiles = find . -type f -print
|
||||
ACLOCAL = @ACLOCAL@
|
||||
ADDSRCS = @ADDSRCS@
|
||||
AMDEP_FALSE = @AMDEP_FALSE@
|
||||
AMDEP_TRUE = @AMDEP_TRUE@
|
||||
AMTAR = @AMTAR@
|
||||
AR = @AR@
|
||||
AUTOCONF = @AUTOCONF@
|
||||
@@ -125,6 +118,8 @@ ECHO_T = @ECHO_T@
|
||||
EGREP = @EGREP@
|
||||
EXEEXT = @EXEEXT@
|
||||
FALSE = @FALSE@
|
||||
GREP = @GREP@
|
||||
INSTALL = @INSTALL@
|
||||
INSTALL_DATA = @INSTALL_DATA@
|
||||
INSTALL_PROGRAM = @INSTALL_PROGRAM@
|
||||
INSTALL_SCRIPT = @INSTALL_SCRIPT@
|
||||
@@ -137,6 +132,7 @@ LTLIBOBJS = @LTLIBOBJS@
|
||||
MAKE = @MAKE@
|
||||
MAKEINFO = @MAKEINFO@
|
||||
MKDIR = @MKDIR@
|
||||
MKDIR_P = @MKDIR_P@
|
||||
MV = @MV@
|
||||
OBJEXT = @OBJEXT@
|
||||
PACKAGE = @PACKAGE@
|
||||
@@ -154,14 +150,12 @@ STRIP = @STRIP@
|
||||
TAR = @TAR@
|
||||
TRUE = @TRUE@
|
||||
VERSION = @VERSION@
|
||||
abs_builddir = @abs_builddir@
|
||||
abs_srcdir = @abs_srcdir@
|
||||
abs_top_builddir = @abs_top_builddir@
|
||||
abs_top_srcdir = @abs_top_srcdir@
|
||||
ac_ct_CC = @ac_ct_CC@
|
||||
ac_ct_CXX = @ac_ct_CXX@
|
||||
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@
|
||||
am__leading_dot = @am__leading_dot@
|
||||
am__quote = @am__quote@
|
||||
@@ -173,43 +167,55 @@ build_alias = @build_alias@
|
||||
build_cpu = @build_cpu@
|
||||
build_os = @build_os@
|
||||
build_vendor = @build_vendor@
|
||||
builddir = @builddir@
|
||||
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@
|
||||
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@
|
||||
srcdir = @srcdir@
|
||||
sysconfdir = @sysconfdir@
|
||||
target_alias = @target_alias@
|
||||
top_builddir = @top_builddir@
|
||||
top_srcdir = @top_srcdir@
|
||||
nzbget_SOURCES = ArticleDownloader.cpp ArticleDownloader.h ColoredFrontend.cpp \
|
||||
ColoredFrontend.h Connection.cpp Connection.h Decoder.cpp Decoder.h DiskState.cpp \
|
||||
DiskState.h DownloadInfo.cpp DownloadInfo.h Frontend.cpp Frontend.h Log.cpp Log.h \
|
||||
LoggableFrontend.cpp LoggableFrontend.h MessageBase.h 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
|
||||
LoggableFrontend.cpp LoggableFrontend.h MessageBase.h NCursesFrontend.cpp NCursesFrontend.h \
|
||||
NNTPConnection.cpp NNTPConnection.h NZBFile.cpp NZBFile.h NetAddress.cpp NetAddress.h \
|
||||
NewsServer.cpp NewsServer.h Observer.cpp Observer.h Options.cpp Options.h \
|
||||
ParChecker.cpp ParChecker.h PrePostProcessor.cpp PrePostProcessor.h \
|
||||
QueueCoordinator.cpp QueueCoordinator.h QueueEditor.cpp QueueEditor.h RemoteClient.cpp \
|
||||
RemoteClient.h RemoteServer.cpp RemoteServer.h ServerPool.cpp ServerPool.h Thread.cpp \
|
||||
Thread.h Util.cpp Util.h nzbget.cpp nzbget.h BinRpc.cpp BinRpc.h XmlRpc.cpp XmlRpc.h \
|
||||
PostInfo.cpp PostInfo.h ScriptController.cpp ScriptController.h
|
||||
|
||||
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
|
||||
Makefile.cvs nzbget.kdevelop nzbget.sln nzbget.vcproj
|
||||
|
||||
all: config.h
|
||||
$(MAKE) $(AM_MAKEFLAGS) all-am
|
||||
@@ -253,7 +259,7 @@ $(ACLOCAL_M4): $(am__aclocal_m4_deps)
|
||||
config.h: stamp-h1
|
||||
@if test ! -f $@; then \
|
||||
rm -f stamp-h1; \
|
||||
$(MAKE) stamp-h1; \
|
||||
$(MAKE) $(AM_MAKEFLAGS) stamp-h1; \
|
||||
else :; fi
|
||||
|
||||
stamp-h1: $(srcdir)/config.h.in $(top_builddir)/config.status
|
||||
@@ -268,7 +274,7 @@ distclean-hdr:
|
||||
-rm -f config.h stamp-h1
|
||||
install-binPROGRAMS: $(bin_PROGRAMS)
|
||||
@$(NORMAL_INSTALL)
|
||||
test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)"
|
||||
test -z "$(bindir)" || $(MKDIR_P) "$(DESTDIR)$(bindir)"
|
||||
@list='$(bin_PROGRAMS)'; for p in $$list; do \
|
||||
p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
|
||||
if test -f $$p \
|
||||
@@ -291,7 +297,7 @@ clean-binPROGRAMS:
|
||||
-test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
|
||||
nzbget$(EXEEXT): $(nzbget_OBJECTS) $(nzbget_DEPENDENCIES)
|
||||
@rm -f nzbget$(EXEEXT)
|
||||
$(CXXLINK) $(nzbget_LDFLAGS) $(nzbget_OBJECTS) $(nzbget_LDADD) $(LIBS)
|
||||
$(CXXLINK) $(nzbget_OBJECTS) $(nzbget_LDADD) $(LIBS)
|
||||
|
||||
mostlyclean-compile:
|
||||
-rm -f *.$(OBJEXT)
|
||||
@@ -300,6 +306,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@
|
||||
@@ -316,29 +323,32 @@ distclean-compile:
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Observer.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Options.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ParChecker.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/PostInfo.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/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)/ScriptController.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ServerPool.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Thread.Po@am__quote@
|
||||
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/Util.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@
|
||||
|
||||
.cpp.o:
|
||||
@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
|
||||
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
|
||||
@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ $<
|
||||
@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ $<
|
||||
|
||||
.cpp.obj:
|
||||
@am__fastdepCXX_TRUE@ if $(CXXCOMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
|
||||
@am__fastdepCXX_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
|
||||
@am__fastdepCXX_TRUE@ $(CXXCOMPILE) -MT $@ -MD -MP -MF $(DEPDIR)/$*.Tpo -c -o $@ `$(CYGPATH_W) '$<'`
|
||||
@am__fastdepCXX_TRUE@ mv -f $(DEPDIR)/$*.Tpo $(DEPDIR)/$*.Po
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
|
||||
@AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@
|
||||
@am__fastdepCXX_FALSE@ $(CXXCOMPILE) -c -o $@ `$(CYGPATH_W) '$<'`
|
||||
uninstall-info-am:
|
||||
|
||||
ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
|
||||
list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
|
||||
@@ -390,23 +400,22 @@ distclean-tags:
|
||||
|
||||
distdir: $(DISTFILES)
|
||||
$(am__remove_distdir)
|
||||
mkdir $(distdir)
|
||||
@srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
|
||||
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
|
||||
list='$(DISTFILES)'; for file in $$list; do \
|
||||
case $$file in \
|
||||
$(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
|
||||
$(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
|
||||
esac; \
|
||||
test -d $(distdir) || mkdir $(distdir)
|
||||
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
|
||||
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
|
||||
list='$(DISTFILES)'; \
|
||||
dist_files=`for file in $$list; do echo $$file; done | \
|
||||
sed -e "s|^$$srcdirstrip/||;t" \
|
||||
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
|
||||
case $$dist_files in \
|
||||
*/*) $(MKDIR_P) `echo "$$dist_files" | \
|
||||
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
|
||||
sort -u` ;; \
|
||||
esac; \
|
||||
for file in $$dist_files; do \
|
||||
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
|
||||
dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
|
||||
if test "$$dir" != "$$file" && test "$$dir" != "."; then \
|
||||
dir="/$$dir"; \
|
||||
$(mkdir_p) "$(distdir)$$dir"; \
|
||||
else \
|
||||
dir=''; \
|
||||
fi; \
|
||||
if test -d $$d/$$file; then \
|
||||
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
|
||||
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
|
||||
cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
|
||||
fi; \
|
||||
@@ -420,7 +429,7 @@ distdir: $(DISTFILES)
|
||||
-find $(distdir) -type d ! -perm -777 -exec chmod a+rwx {} \; -o \
|
||||
! -type d ! -perm -444 -links 1 -exec chmod a+r {} \; -o \
|
||||
! -type d ! -perm -400 -exec chmod a+r {} \; -o \
|
||||
! -type d ! -perm -444 -exec $(SHELL) $(install_sh) -c -m a+r {} {} \; \
|
||||
! -type d ! -perm -444 -exec $(install_sh) -c -m a+r {} {} \; \
|
||||
|| chmod -R a+r $(distdir)
|
||||
dist-gzip: distdir
|
||||
tardir=$(distdir) && $(am__tar) | GZIP=$(GZIP_ENV) gzip -c >$(distdir).tar.gz
|
||||
@@ -495,7 +504,7 @@ distcheck: dist
|
||||
$(am__remove_distdir)
|
||||
@(echo "$(distdir) archives ready for distribution: "; \
|
||||
list='$(DIST_ARCHIVES)'; for i in $$list; do echo $$i; done) | \
|
||||
sed -e '1{h;s/./=/g;p;x;}' -e '$${p;x;}'
|
||||
sed -e 1h -e 1s/./=/g -e 1p -e 1x -e '$$p' -e '$$x'
|
||||
distuninstallcheck:
|
||||
@cd $(distuninstallcheck_dir) \
|
||||
&& test `$(distuninstallcheck_listfiles) | wc -l` -le 1 \
|
||||
@@ -519,7 +528,7 @@ check: check-am
|
||||
all-am: Makefile $(PROGRAMS) config.h
|
||||
installdirs:
|
||||
for dir in "$(DESTDIR)$(bindir)"; do \
|
||||
test -z "$$dir" || $(mkdir_p) "$$dir"; \
|
||||
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
|
||||
done
|
||||
install: install-am
|
||||
install-exec: install-exec-am
|
||||
@@ -568,12 +577,20 @@ info-am:
|
||||
|
||||
install-data-am:
|
||||
|
||||
install-dvi: install-dvi-am
|
||||
|
||||
install-exec-am: install-binPROGRAMS
|
||||
|
||||
install-html: install-html-am
|
||||
|
||||
install-info: install-info-am
|
||||
|
||||
install-man:
|
||||
|
||||
install-pdf: install-pdf-am
|
||||
|
||||
install-ps: install-ps-am
|
||||
|
||||
installcheck-am:
|
||||
|
||||
maintainer-clean: maintainer-clean-am
|
||||
@@ -595,7 +612,9 @@ ps: ps-am
|
||||
|
||||
ps-am:
|
||||
|
||||
uninstall-am: uninstall-binPROGRAMS uninstall-info-am
|
||||
uninstall-am: uninstall-binPROGRAMS
|
||||
|
||||
.MAKE: install-am install-strip
|
||||
|
||||
.PHONY: CTAGS GTAGS all all-am am--refresh check check-am clean \
|
||||
clean-binPROGRAMS clean-generic ctags dist dist-all dist-bzip2 \
|
||||
@@ -603,13 +622,14 @@ uninstall-am: uninstall-binPROGRAMS uninstall-info-am
|
||||
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-binPROGRAMS install-data install-data-am install-dvi \
|
||||
install-dvi-am install-exec install-exec-am install-html \
|
||||
install-html-am install-info install-info-am install-man \
|
||||
install-pdf install-pdf-am install-ps install-ps-am \
|
||||
install-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
|
||||
tags uninstall uninstall-am uninstall-binPROGRAMS
|
||||
|
||||
|
||||
clean-bak: rm *~
|
||||
|
||||
357
MessageBase.h
357
MessageBase.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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>
|
||||
@@ -27,161 +27,322 @@
|
||||
#ifndef MESSAGEBASE_H
|
||||
#define MESSAGEBASE_H
|
||||
|
||||
static const int32_t NZBMESSAGE_SIGNATURE = 0x6E7A6201; // = "nzb"-version-1
|
||||
static const int32_t NZBMESSAGE_SIGNATURE = 0x6E7A6202; // = "nzb2" (nzb version 2)
|
||||
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.
|
||||
*/
|
||||
|
||||
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,
|
||||
eRemoteRequestVersion,
|
||||
eRemoteRequestPostQueue,
|
||||
eRemoteRequestWriteLog
|
||||
};
|
||||
|
||||
// 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 to m_iOffset relative to the current position in queue
|
||||
eRemoteEditActionFileMoveTop, // move to top of queue
|
||||
eRemoteEditActionFileMoveBottom, // move to bottom of queue
|
||||
eRemoteEditActionFilePause, // pause
|
||||
eRemoteEditActionFileResume, // resume (unpause)
|
||||
eRemoteEditActionFileDelete, // delete
|
||||
eRemoteEditActionFilePauseAllPars, // pause only (all) pars (does not affect other files)
|
||||
eRemoteEditActionFilePauseExtraPars, // pause only (almost all) pars, except main par-file (does not affect other files)
|
||||
eRemoteEditActionGroupMoveOffset, // move to m_iOffset relative to the current position in queue
|
||||
eRemoteEditActionGroupMoveTop, // move to top of queue
|
||||
eRemoteEditActionGroupMoveBottom, // move to bottom of queue
|
||||
eRemoteEditActionGroupPause, // pause
|
||||
eRemoteEditActionGroupResume, // resume (unpause)
|
||||
eRemoteEditActionGroupDelete, // delete
|
||||
eRemoteEditActionGroupPauseAllPars, // pause only (all) pars (does not affect other files)
|
||||
eRemoteEditActionGroupPauseExtraPars // pause only (almost all) pars, except main par-file (does not affect other files)
|
||||
};
|
||||
}
|
||||
|
||||
// The basic NZBMessageBase struct
|
||||
struct SNZBMessageBase
|
||||
// The basic SNZBRequestBase struct, used in all requests
|
||||
struct SNZBRequestBase
|
||||
{
|
||||
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
|
||||
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
|
||||
int32_t m_bAddFirst; // 1 - add file to the top of download queue
|
||||
int32_t m_iTrailingDataLength; // Length of nzb-file in bytes
|
||||
//char m_szContent[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// A 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
|
||||
};
|
||||
|
||||
// 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_bServerPaused; // 1 - server is currently in paused-state
|
||||
int32_t m_iThreadCount; // Number of threads running
|
||||
int32_t m_iPostJobCount; // Number of jobs in post-processor queue (including current job)
|
||||
int32_t m_iUpTimeSec; // Server up time in seconds
|
||||
int32_t m_iDownloadTimeSec; // Server download time in seconds (up_time - standby_time)
|
||||
int32_t m_iDownloadedBytesLo; // Amount of data downloaded since server start, Low 32-bits of 64-bit value
|
||||
int32_t m_iDownloadedBytesHi; // Amount of data downloaded since server start, High 32-bits of 64-bit value
|
||||
int32_t m_bServerStandBy; // 0 - there are currently downloads running, 1 - no downloads in progress (server paused or all jobs completed)
|
||||
int32_t m_iNrTrailingEntries; // Number of List-entries, following to this structure
|
||||
int32_t m_iTrailingDataLength; // Length of all List-entries, following to this structure
|
||||
// SNZBListResponseEntry m_Entries[m_iNrTrailingEntries] // variable sized
|
||||
};
|
||||
|
||||
// A list request-answer entry
|
||||
struct SNZBListRequestAnswerEntry
|
||||
// A list response entry
|
||||
struct SNZBListResponseEntry
|
||||
{
|
||||
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_iID; // Entry-ID
|
||||
int32_t m_iFileSizeLo; // Filesize in bytes, Low 32-bits of 64-bit value
|
||||
int32_t m_iFileSizeHi; // Filesize in bytes, High 32-bits of 64-bit value
|
||||
int32_t m_iRemainingSizeLo; // Remaining size in bytes, Low 32-bits of 64-bit value
|
||||
int32_t m_iRemainingSizeHi; // Remaining size in bytes, High 32-bits of 64-bit value
|
||||
int32_t m_bPaused; // 1 - file is paused
|
||||
int32_t m_bFilenameConfirmed; // 1 - Filename confirmed (read from article body), 0 - Filename parsed from subject (can be changed after reading of article)
|
||||
int32_t m_iNZBFilenameLen; // Length of NZBFileName-string (m_szNZBFilename), following to this record
|
||||
int32_t m_iSubjectLen; // Length of Subject-string (m_szSubject), following to this record
|
||||
int32_t m_iFilenameLen; // Length of Filename-string (m_szFilename), following to this record
|
||||
int32_t m_iDestDirLen; // Length of DestDir-string (m_szDestDir), following to this record
|
||||
//char m_szNZBFilename[m_iNZBFilenameLen]; // variable sized, may contain full path (local path on client) or only filename
|
||||
//char m_szSubject[m_iSubjectLen]; // variable sized
|
||||
//char m_szFilename[m_iFilenameLen]; // variable sized
|
||||
//char m_szDestDir[m_iDestDirLen]; // variable sized
|
||||
};
|
||||
|
||||
// A log request
|
||||
struct SNZBLogRequest
|
||||
{
|
||||
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
|
||||
};
|
||||
|
||||
// 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
|
||||
};
|
||||
|
||||
// An 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 in NZBMessageRequest-namespace
|
||||
int32_t m_iOffset; // Offset to move (for m_iAction = 0)
|
||||
int32_t m_bSmartOrder; // For Move-Actions: 0 - execute action for each ID in order they are placed in array;
|
||||
// 1 - smart execute to ensure that the relative order of all affected IDs are not changed.
|
||||
int32_t m_iNrTrailingEntries; // Number of ID-entries, following to this structure
|
||||
int32_t m_iTrailingDataLength; // Length of all ID-entries, following to this structure
|
||||
//int32_t m_iIDs[m_iNrTrailingEntries]; // variable sized array of IDs. For File-Actions - ID of file, for Group-Actions - ID of any file belonging to group
|
||||
};
|
||||
|
||||
// An edit queue response
|
||||
struct SNZBEditQueueResponse
|
||||
{
|
||||
SNZBResponseBase m_MessageBase; // Must be the first in the struct
|
||||
int32_t m_bSuccess; // 0 - command failed, 1 - command executed successfully
|
||||
int32_t m_iTrailingDataLength; // Length of Text-string (m_szText), following to this record
|
||||
//char m_szText[m_iTrailingDataLength]; // variable sized
|
||||
};
|
||||
|
||||
// Request dumping of debug info
|
||||
struct SNZBDumpDebugRequest
|
||||
{
|
||||
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
|
||||
};
|
||||
|
||||
// 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
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -46,22 +46,28 @@ 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;
|
||||
|
||||
// Inputting numbres
|
||||
int m_iInputNumberIndex;
|
||||
int m_iInputValue;
|
||||
// Inputting numbers
|
||||
int m_iInputNumberIndex;
|
||||
int m_iInputValue;
|
||||
|
||||
#ifdef WIN32
|
||||
CHAR_INFO* m_pScreenBuffer;
|
||||
@@ -69,12 +75,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 +91,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 PrintGroupname(GroupInfo * pGroupInfo, int iRow, bool bSelected);
|
||||
void PrepareGroupQueue();
|
||||
void PrintTopHeader(char* szHeader, int iLineNr, bool bUpTime);
|
||||
void ClearGroupQueue();
|
||||
int PrintMessage(Message* Msg, int iRow, int iMaxLines);
|
||||
void PrintKeyInputBar();
|
||||
void PrintStatus();
|
||||
void UpdateInput();
|
||||
void Update();
|
||||
void 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,5 +1,5 @@
|
||||
/*
|
||||
* 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>
|
||||
@@ -34,6 +34,7 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cstdio>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Log.h"
|
||||
@@ -41,42 +42,37 @@
|
||||
#include "Connection.h"
|
||||
#include "NewsServer.h"
|
||||
|
||||
static const int CONNECTION_LINEBUFFER_SIZE = 1024*10;
|
||||
|
||||
NNTPConnection::NNTPConnection(NewsServer* server) : Connection(server)
|
||||
{
|
||||
m_UnavailableGroups.clear();
|
||||
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)
|
||||
{
|
||||
@@ -88,56 +84,56 @@ char* NNTPConnection::Request(char* req)
|
||||
debug("%s requested authorization", m_pNetAddress->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 (!((NewsServer*)m_pNetAddress)->GetUser() ||
|
||||
!((NewsServer*)m_pNetAddress)->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());
|
||||
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.", m_pNetAddress->GetHost(), 0);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!strncmp(answer, "281", 3))
|
||||
{
|
||||
debug("authorization for %s successful", m_pNetAddress->GetHost());
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
else if (!strncmp(answer, "381", 3))
|
||||
{
|
||||
@@ -145,44 +141,92 @@ 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)
|
||||
{
|
||||
error("authorization for %s failed (Answer: %s)", m_pNetAddress->GetHost(), 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());
|
||||
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;
|
||||
return false;
|
||||
}
|
||||
else if (!strncmp(szAnswer, "2", 1))
|
||||
else if (!strncmp(answer, "2", 1))
|
||||
{
|
||||
debug("authorization for %s successful", m_pNetAddress->GetHost());
|
||||
return 0;
|
||||
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)
|
||||
{
|
||||
error("authorization for %s failed (Answer: %s)", m_pNetAddress->GetHost(), answer);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
const char* NNTPConnection::JoinGroup(const char* grp)
|
||||
{
|
||||
if (m_szActiveGroup && !strcmp(m_szActiveGroup, grp))
|
||||
{
|
||||
// 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';
|
||||
|
||||
const char* answer = Request(tmp);
|
||||
if (m_bAuthError)
|
||||
{
|
||||
return answer;
|
||||
}
|
||||
|
||||
if (answer && !strncmp(answer, "2", 1))
|
||||
{
|
||||
debug("Changed group to %s on %s", grp, GetServer()->GetHost());
|
||||
|
||||
if (m_szActiveGroup)
|
||||
{
|
||||
free(m_szActiveGroup);
|
||||
}
|
||||
m_szActiveGroup = strdup(grp);
|
||||
}
|
||||
else
|
||||
{
|
||||
debug("Error changing group on %s to %s: %s.",
|
||||
GetServer()->GetHost(), grp, answer);
|
||||
}
|
||||
|
||||
return answer;
|
||||
}
|
||||
|
||||
int NNTPConnection::DoConnect()
|
||||
@@ -190,8 +234,11 @@ int NNTPConnection::DoConnect()
|
||||
debug("Opening connection to %s", GetServer()->GetHost());
|
||||
int res = Connection::DoConnect();
|
||||
if (res < 0)
|
||||
{
|
||||
return res;
|
||||
char* answer = DoReadLine(m_szLineBuf, LineBufSize);
|
||||
}
|
||||
|
||||
char* answer = DoReadLine(m_szLineBuf, CONNECTION_LINEBUFFER_SIZE, NULL);
|
||||
|
||||
if (!answer)
|
||||
{
|
||||
@@ -199,7 +246,6 @@ int NNTPConnection::DoConnect()
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
if (strncmp(answer, "2", 1))
|
||||
{
|
||||
error("Connection to %s failed. Answer: ", m_pNetAddress->GetHost(), answer);
|
||||
@@ -215,60 +261,11 @@ int NNTPConnection::DoDisconnect()
|
||||
if (m_eStatus == csConnected)
|
||||
{
|
||||
Request("quit\r\n");
|
||||
if (m_szActiveGroup)
|
||||
{
|
||||
free(m_szActiveGroup);
|
||||
m_szActiveGroup = NULL;
|
||||
}
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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)))
|
||||
{
|
||||
debug("Changed group to %s on %s", grp, GetServer()->GetHost());
|
||||
|
||||
if (m_szActiveGroup)
|
||||
free(m_szActiveGroup);
|
||||
|
||||
m_szActiveGroup = strdup(grp);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!answer)
|
||||
{
|
||||
warn("Error changing group on %s: Connection closed by remote host.",
|
||||
GetServer()->GetHost());
|
||||
return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
warn("Error changing group on %s to %s: Answer was \"%s\".",
|
||||
GetServer()->GetHost(), grp, answer);
|
||||
m_UnavailableGroups.push_back(strdup(grp));
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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>
|
||||
@@ -27,32 +27,30 @@
|
||||
#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;
|
||||
char* m_szActiveGroup;
|
||||
static const int LineBufSize = 1024*10;
|
||||
char* m_szLineBuf;
|
||||
bool m_bAuthError;
|
||||
|
||||
virtual int DoConnect();
|
||||
virtual int DoDisconnect();
|
||||
void Clear();
|
||||
|
||||
public:
|
||||
NNTPConnection(NewsServer* server);
|
||||
~NNTPConnection();
|
||||
NewsServer* GetNewsServer() { return(NewsServer*)m_pNetAddress; }
|
||||
char* Request(char* req);
|
||||
int Authenticate();
|
||||
int AuthInfoUser(int iRecur = 0);
|
||||
int AuthInfoPass(int iRecur = 0);
|
||||
int JoinGroup(char* grp);
|
||||
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; }
|
||||
};
|
||||
|
||||
|
||||
|
||||
256
NTService.cpp
256
NTService.cpp
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
@@ -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,5 +1,5 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
|
||||
303
NZBFile.cpp
303
NZBFile.cpp
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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>
|
||||
@@ -32,9 +32,8 @@
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
#include <list>
|
||||
#ifdef WIN32
|
||||
#include <comutil.h>
|
||||
#import "MSXML.dll" named_guids
|
||||
@@ -48,17 +47,22 @@ using namespace MSXML;
|
||||
#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)
|
||||
{
|
||||
debug("Creating NZBFile");
|
||||
|
||||
m_szFileName = strdup(szFileName);
|
||||
m_pNZBInfo = new NZBInfo();
|
||||
m_pNZBInfo->AddReference();
|
||||
m_pNZBInfo->SetFilename(szFileName);
|
||||
BuildDestDirName();
|
||||
|
||||
m_FileInfos.clear();
|
||||
}
|
||||
@@ -73,11 +77,16 @@ 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();
|
||||
}
|
||||
}
|
||||
|
||||
void NZBFile::LogDebugInfo()
|
||||
@@ -90,38 +99,6 @@ void NZBFile::DetachFileInfos()
|
||||
m_FileInfos.clear();
|
||||
}
|
||||
|
||||
bool NZBFile::LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength)
|
||||
{
|
||||
FILE* pFile = fopen(szFileName, "r");
|
||||
if (!pFile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// obtain file size.
|
||||
fseek(pFile , 0 , SEEK_END);
|
||||
int iSize = ftell(pFile);
|
||||
rewind(pFile);
|
||||
|
||||
// allocate memory to contain the whole file.
|
||||
*pBuffer = (char*) malloc(iSize + 1);
|
||||
if (!*pBuffer)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// copy the file into the buffer.
|
||||
fread(*pBuffer, 1, iSize, pFile);
|
||||
|
||||
fclose(pFile);
|
||||
|
||||
(*pBuffer)[iSize] = 0;
|
||||
|
||||
*pBufferLength = iSize + 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
NZBFile* NZBFile::CreateFromBuffer(const char* szFileName, const char* szBuffer, int iSize)
|
||||
{
|
||||
return Create(szFileName, szBuffer, iSize, true);
|
||||
@@ -129,19 +106,7 @@ NZBFile* NZBFile::CreateFromBuffer(const char* szFileName, const char* szBuffer,
|
||||
|
||||
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, NULL, 0, false);
|
||||
}
|
||||
|
||||
void NZBFile::AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo)
|
||||
@@ -153,8 +118,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,6 +136,177 @@ void NZBFile::DeleteEmptyArticles(FileInfo* pFileInfo)
|
||||
i++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pArticles->empty())
|
||||
{
|
||||
ParseSubject(pFileInfo);
|
||||
m_FileInfos.push_back(pFileInfo);
|
||||
pFileInfo->SetNZBInfo(m_pNZBInfo);
|
||||
m_pNZBInfo->SetSize(m_pNZBInfo->GetSize() + pFileInfo->GetSize());
|
||||
m_pNZBInfo->SetFileCount(m_pNZBInfo->GetFileCount() + 1);
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->SaveFile(pFileInfo);
|
||||
pFileInfo->ClearArticles();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
delete pFileInfo;
|
||||
}
|
||||
}
|
||||
|
||||
void NZBFile::ParseSubject(FileInfo* pFileInfo)
|
||||
{
|
||||
// tokenize subject, considering spaces as separators and quotation
|
||||
// marks as non separatable token delimiters.
|
||||
// then take the last token containing dot (".") as a filename
|
||||
|
||||
typedef std::list<char*> TokenList;
|
||||
TokenList tokens;
|
||||
tokens.clear();
|
||||
|
||||
// tokenizing
|
||||
char* p = (char*)pFileInfo->GetSubject();
|
||||
char* start = p;
|
||||
bool quot = false;
|
||||
while (true)
|
||||
{
|
||||
char ch = *p;
|
||||
bool sep = (ch == '\"') || (!quot && ch == ' ') || (ch == '\0');
|
||||
if (sep)
|
||||
{
|
||||
// end of token
|
||||
int len = (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());
|
||||
}
|
||||
|
||||
pFileInfo->MakeValidFilename();
|
||||
}
|
||||
|
||||
void NZBFile::BuildDestDirName()
|
||||
{
|
||||
char szBuffer[1024];
|
||||
|
||||
if (g_pOptions->GetAppendNZBDir())
|
||||
{
|
||||
char szNiceNZBName[1024];
|
||||
m_pNZBInfo->GetNiceNZBName(szNiceNZBName, 1024);
|
||||
snprintf(szBuffer, 1024, "%s%s", g_pOptions->GetDestDir(), szNiceNZBName);
|
||||
szBuffer[1024-1] = '\0';
|
||||
}
|
||||
else
|
||||
{
|
||||
strncpy(szBuffer, g_pOptions->GetDestDir(), 1024);
|
||||
szBuffer[1024-1] = '\0'; // trim the last slash, always returned by GetDestDir()
|
||||
}
|
||||
|
||||
m_pNZBInfo->SetDestDir(szBuffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the parsing of subject was correct
|
||||
*/
|
||||
void NZBFile::CheckFilenames()
|
||||
{
|
||||
for (FileInfos::iterator it = m_FileInfos.begin(); it != m_FileInfos.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo1 = *it;
|
||||
int iDupe = 0;
|
||||
for (FileInfos::iterator it2 = it + 1; it2 != m_FileInfos.end(); it2++)
|
||||
{
|
||||
FileInfo* pFileInfo2 = *it2;
|
||||
if (!strcmp(pFileInfo1->GetFilename(), pFileInfo2->GetFilename()) &&
|
||||
strcmp(pFileInfo1->GetSubject(), pFileInfo2->GetSubject()))
|
||||
{
|
||||
iDupe++;
|
||||
}
|
||||
}
|
||||
|
||||
// If more than two files have the same parsed filename but different subjects,
|
||||
// this means, that the parsing was not correct.
|
||||
// in this case we take subjects as filenames to prevent
|
||||
// false "duplicate files"-alarm.
|
||||
// It's Ok for just two files to have the same filename, this is
|
||||
// an often case by posting-errors to repost bad files
|
||||
if (iDupe > 2 || (iDupe == 2 && m_FileInfos.size() == 2))
|
||||
{
|
||||
for (FileInfos::iterator it2 = it; it2 != m_FileInfos.end(); it2++)
|
||||
{
|
||||
FileInfo* pFileInfo2 = *it2;
|
||||
pFileInfo2->SetFilename(pFileInfo2->GetSubject());
|
||||
pFileInfo2->MakeValidFilename();
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->LoadArticles(pFileInfo2);
|
||||
g_pDiskState->SaveFile(pFileInfo2);
|
||||
pFileInfo2->ClearArticles();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
@@ -197,7 +334,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)
|
||||
@@ -209,7 +351,11 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize
|
||||
}
|
||||
|
||||
NZBFile* pFile = new NZBFile(szFileName);
|
||||
if (!pFile->parseNZB(doc))
|
||||
if (pFile->ParseNZB(doc))
|
||||
{
|
||||
pFile->CheckFilenames();
|
||||
}
|
||||
else
|
||||
{
|
||||
delete pFile;
|
||||
pFile = NULL;
|
||||
@@ -218,7 +364,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 +399,7 @@ 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();
|
||||
|
||||
MSXML::IXMLDOMNodeListPtr groupList = node->selectNodes("groups/group");
|
||||
for (int g = 0; g < groupList->Getlength(); g++)
|
||||
@@ -274,8 +440,7 @@ bool NZBFile::parseNZB(IUnknown* nzb)
|
||||
}
|
||||
}
|
||||
|
||||
DeleteEmptyArticles(pFileInfo);
|
||||
m_FileInfos.push_back(pFileInfo);
|
||||
AddFileInfo(pFileInfo);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -287,7 +452,7 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize
|
||||
xmlTextReaderPtr doc;
|
||||
if (bFromBuffer)
|
||||
{
|
||||
doc = xmlReaderForMemory(szBuffer,iSize-1, "", NULL, 0);
|
||||
doc = xmlReaderForMemory(szBuffer, iSize-1, "", NULL, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -299,7 +464,11 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize
|
||||
}
|
||||
|
||||
NZBFile* pFile = new NZBFile(szFileName);
|
||||
if (!pFile->parseNZB(doc))
|
||||
if (pFile->ParseNZB(doc))
|
||||
{
|
||||
pFile->CheckFilenames();
|
||||
}
|
||||
else
|
||||
{
|
||||
delete pFile;
|
||||
pFile = NULL;
|
||||
@@ -310,7 +479,7 @@ NZBFile* NZBFile::Create(const char* szFileName, const char* szBuffer, int iSize
|
||||
return pFile;
|
||||
}
|
||||
|
||||
bool NZBFile::parseNZB(void* nzb)
|
||||
bool NZBFile::ParseNZB(void* nzb)
|
||||
{
|
||||
FileInfo* pFileInfo = NULL;
|
||||
xmlTextReaderPtr node = (xmlTextReaderPtr)nzb;
|
||||
@@ -334,7 +503,7 @@ bool NZBFile::parseNZB(void* nzb)
|
||||
if (!strcmp("file", (char*)name))
|
||||
{
|
||||
pFileInfo = new FileInfo();
|
||||
pFileInfo->SetNZBFilename(m_szFileName);
|
||||
pFileInfo->SetFilename(m_szFileName);
|
||||
|
||||
while (xmlTextReaderMoveToNextAttribute(node))
|
||||
{
|
||||
@@ -345,7 +514,6 @@ bool NZBFile::parseNZB(void* nzb)
|
||||
xmlFree(value);
|
||||
value = xmlTextReaderValue(node);
|
||||
pFileInfo->SetSubject((char*)value);
|
||||
pFileInfo->ParseSubject();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -405,8 +573,7 @@ bool NZBFile::parseNZB(void* nzb)
|
||||
/* Close the file element, add the new file to file-list */
|
||||
if (!strcmp("file",(char*)name))
|
||||
{
|
||||
DeleteEmptyArticles(pFileInfo);
|
||||
m_FileInfos.push_back(pFileInfo);
|
||||
AddFileInfo(pFileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
14
NZBFile.h
14
NZBFile.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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>
|
||||
@@ -38,15 +38,20 @@ public:
|
||||
|
||||
private:
|
||||
FileInfos m_FileInfos;
|
||||
NZBInfo* m_pNZBInfo;
|
||||
char* m_szFileName;
|
||||
|
||||
NZBFile(const char* szFileName);
|
||||
void AddArticle(FileInfo* pFileInfo, ArticleInfo* pArticleInfo);
|
||||
void DeleteEmptyArticles(FileInfo* pFileInfo);
|
||||
void AddFileInfo(FileInfo* pFileInfo);
|
||||
void ParseSubject(FileInfo* pFileInfo);
|
||||
void BuildDestDirName();
|
||||
void CheckFilenames();
|
||||
#ifdef WIN32
|
||||
bool parseNZB(IUnknown* nzb);
|
||||
bool ParseNZB(IUnknown* nzb);
|
||||
static void EncodeURL(const char* szFilename, char* szURL);
|
||||
#else
|
||||
bool parseNZB(void* nzb);
|
||||
bool ParseNZB(void* nzb);
|
||||
#endif
|
||||
static NZBFile* Create(const char* szFileName, const char* szBuffer, int iSize, bool bFromBuffer);
|
||||
|
||||
@@ -54,7 +59,6 @@ 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);
|
||||
const char* GetFileName() const { return m_szFileName; }
|
||||
FileInfos* GetFileInfos() { return &m_FileInfos; }
|
||||
void DetachFileInfos();
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "NetAddress.h"
|
||||
|
||||
NetAddress::NetAddress(const char* szHost, int iPort)
|
||||
|
||||
@@ -35,6 +35,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "NewsServer.h"
|
||||
#include "Log.h"
|
||||
|
||||
@@ -57,8 +58,12 @@ NewsServer::NewsServer(const char* host, int port, const char* user, const char*
|
||||
|
||||
NewsServer::~NewsServer()
|
||||
{
|
||||
free(m_szUser);
|
||||
m_szUser = NULL;
|
||||
free(m_szPassword);
|
||||
m_szPassword = NULL;
|
||||
if (m_szUser)
|
||||
{
|
||||
free(m_szUser);
|
||||
}
|
||||
if (m_szPassword)
|
||||
{
|
||||
free(m_szPassword);
|
||||
}
|
||||
}
|
||||
|
||||
606
Options.cpp
606
Options.cpp
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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>
|
||||
@@ -35,6 +35,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <cstdio>
|
||||
#ifdef WIN32
|
||||
#include <direct.h>
|
||||
#else
|
||||
@@ -51,7 +52,6 @@
|
||||
#include "NewsServer.h"
|
||||
#include "MessageBase.h"
|
||||
|
||||
extern float g_fDownloadRate;
|
||||
extern ServerPool* g_pServerPool;
|
||||
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
@@ -64,6 +64,7 @@ static struct option long_options[] =
|
||||
{"server", no_argument, 0, 's' },
|
||||
{"daemon", no_argument, 0, 'D' },
|
||||
{"version", no_argument, 0, 'v'},
|
||||
{"serverversion", no_argument, 0, 'V'},
|
||||
{"option", required_argument, 0, 'o'},
|
||||
{"append", no_argument, 0, 'A'},
|
||||
{"list", no_argument, 0, 'L'},
|
||||
@@ -74,54 +75,71 @@ static struct option long_options[] =
|
||||
{"log", required_argument, 0, 'G'},
|
||||
{"top", no_argument, 0, 'T'},
|
||||
{"edit", required_argument, 0, 'E'},
|
||||
{"fileid", required_argument, 0, 'I'},
|
||||
{"connect", no_argument, 0, 'C'},
|
||||
{"quit", no_argument, 0, 'Q'},
|
||||
#ifdef DEBUG
|
||||
{"test", no_argument, 0, 't'},
|
||||
#endif
|
||||
{"post", no_argument, 0, 'O'},
|
||||
{"write", required_argument, 0, 'W'},
|
||||
{0, 0, 0, 0}
|
||||
};
|
||||
#endif
|
||||
|
||||
static char short_options[] = "c:hno:psvABDCG:LPUR:TE:I:Q";
|
||||
static char short_options[] = "c:hno:psvABDCE:G:LOPR:TUQVW:";
|
||||
|
||||
// Program options
|
||||
static const char* OPTION_DESTDIR = "destdir";
|
||||
static const char* OPTION_TEMPDIR = "tempdir";
|
||||
static const char* OPTION_QUEUEDIR = "queuedir";
|
||||
static const char* OPTION_NZBDIR = "nzbdir";
|
||||
static const char* OPTION_CREATELOG = "createlog";
|
||||
static const char* OPTION_LOGFILE = "logfile";
|
||||
static const char* OPTION_APPENDNZBDIR = "appendnzbdir";
|
||||
static const char* OPTION_LOCKFILE = "lockfile";
|
||||
static const char* OPTION_OUTPUTMODE = "outputmode";
|
||||
static const char* OPTION_DUPECHECK = "dupecheck";
|
||||
static const char* OPTION_DOWNLOADRATE = "downloadrate";
|
||||
static const char* OPTION_RENAMEBROKEN = "renamebroken";
|
||||
static const char* OPTION_SERVERIP = "serverip";
|
||||
static const char* OPTION_SERVERPORT = "serverport";
|
||||
static const char* OPTION_SERVERPASSWORD = "serverpassword";
|
||||
static const char* OPTION_CONNECTIONTIMEOUT = "connectiontimeout";
|
||||
static const char* OPTION_SAVEQUEUE = "savequeue";
|
||||
static const char* OPTION_RELOADQUEUE = "reloadqueue";
|
||||
static const char* OPTION_CREATEBROKENLOG = "createbrokenlog";
|
||||
static const char* OPTION_RESETLOG = "resetlog";
|
||||
static const char* OPTION_DECODER = "decoder";
|
||||
static const char* OPTION_RETRIES = "retries";
|
||||
static const char* OPTION_RETRYINTERVAL = "retryinterval";
|
||||
static const char* OPTION_TERMINATETIMEOUT = "terminatetimeout";
|
||||
static const char* OPTION_CONTINUEPARTIAL = "continuepartial";
|
||||
static const char* OPTION_LOGBUFFERSIZE = "logbuffersize";
|
||||
static const char* OPTION_INFOTARGET = "infotarget";
|
||||
static const char* OPTION_WARNINGTARGET = "warningtarget";
|
||||
static const char* OPTION_ERRORTARGET = "errortarget";
|
||||
static const char* OPTION_DEBUGTARGET = "debugtarget";
|
||||
static const char* OPTION_LOADPARS = "loadpars";
|
||||
static const char* OPTION_PARCHECK = "parcheck";
|
||||
static const char* OPTION_PARREPAIR = "parrepair";
|
||||
static const char* OPTION_POSTPROCESS = "postprocess";
|
||||
static const char* OPTION_STRICTPARNAME = "strictparname";
|
||||
static const char* OPTION_DESTDIR = "DestDir";
|
||||
static const char* OPTION_TEMPDIR = "TempDir";
|
||||
static const char* OPTION_QUEUEDIR = "QueueDir";
|
||||
static const char* OPTION_NZBDIR = "NzbDir";
|
||||
static const char* OPTION_CREATELOG = "CreateLog";
|
||||
static const char* OPTION_LOGFILE = "LogFile";
|
||||
static const char* OPTION_APPENDNZBDIR = "AppendNzbDir";
|
||||
static const char* OPTION_LOCKFILE = "LockFile";
|
||||
static const char* OPTION_DAEMONUSERNAME = "DaemonUserName";
|
||||
static const char* OPTION_OUTPUTMODE = "OutputMode";
|
||||
static const char* OPTION_DUPECHECK = "DupeCheck";
|
||||
static const char* OPTION_DOWNLOADRATE = "DownloadRate";
|
||||
static const char* OPTION_RENAMEBROKEN = "RenameBroken";
|
||||
static const char* OPTION_SERVERIP = "ServerIp";
|
||||
static const char* OPTION_SERVERPORT = "ServerPort";
|
||||
static const char* OPTION_SERVERPASSWORD = "ServerPassword";
|
||||
static const char* OPTION_CONNECTIONTIMEOUT = "ConnectionTimeout";
|
||||
static const char* OPTION_SAVEQUEUE = "SaveQueue";
|
||||
static const char* OPTION_RELOADQUEUE = "ReloadQueue";
|
||||
static const char* OPTION_RELOADPOSTQUEUE = "ReloadPostQueue";
|
||||
static const char* OPTION_CREATEBROKENLOG = "CreateBrokenLog";
|
||||
static const char* OPTION_RESETLOG = "ResetLog";
|
||||
static const char* OPTION_DECODE = "Decode";
|
||||
static const char* OPTION_RETRIES = "Retries";
|
||||
static const char* OPTION_RETRYINTERVAL = "RetryInterval";
|
||||
static const char* OPTION_TERMINATETIMEOUT = "TerminateTimeout";
|
||||
static const char* OPTION_CONTINUEPARTIAL = "ContinuePartial";
|
||||
static const char* OPTION_LOGBUFFERSIZE = "LogBufferSize";
|
||||
static const char* OPTION_INFOTARGET = "InfoTarget";
|
||||
static const char* OPTION_WARNINGTARGET = "WarningTarget";
|
||||
static const char* OPTION_ERRORTARGET = "ErrorTarget";
|
||||
static const char* OPTION_DEBUGTARGET = "DebugTarget";
|
||||
static const char* OPTION_DETAILTARGET = "DetailTarget";
|
||||
static const char* OPTION_LOADPARS = "LoadPars";
|
||||
static const char* OPTION_PARCHECK = "ParCheck";
|
||||
static const char* OPTION_PARREPAIR = "ParRepair";
|
||||
static const char* OPTION_POSTPROCESS = "PostProcess";
|
||||
static const char* OPTION_STRICTPARNAME = "StrictParName";
|
||||
static const char* OPTION_UMASK = "UMask";
|
||||
static const char* OPTION_UPDATEINTERVAL = "UpdateInterval";
|
||||
static const char* OPTION_CURSESNZBNAME = "CursesNzbName";
|
||||
static const char* OPTION_CURSESTIME = "CursesTime";
|
||||
static const char* OPTION_CURSESGROUP = "CursesGroup";
|
||||
static const char* OPTION_CRCCHECK = "CrcCheck";
|
||||
static const char* OPTION_RETRYONCRCERROR = "RetryOnCrcError";
|
||||
static const char* OPTION_THREADLIMIT = "ThreadLimit";
|
||||
static const char* OPTION_DIRECTWRITE = "DirectWrite";
|
||||
static const char* OPTION_WRITEBUFFERSIZE = "WriteBufferSize";
|
||||
static const char* OPTION_NZBDIRINTERVAL = "NzbDirInterval";
|
||||
static const char* OPTION_NZBDIRFILEAGE = "NzbDirFileAge";
|
||||
static const char* OPTION_PARCLEANUPQUEUE = "ParCleanupQueue";
|
||||
static const char* OPTION_DISKSPACE = "DiskSpace";
|
||||
static const char* OPTION_POSTLOGKIND = "PostLogKind";
|
||||
static const char* OPTION_ALLOWREPROCESS = "AllowReProcess";
|
||||
|
||||
#ifndef WIN32
|
||||
const char* PossibleConfigLocations[] =
|
||||
@@ -139,6 +157,7 @@ Options::Options(int argc, char* argv[])
|
||||
{
|
||||
// initialize options with default values
|
||||
|
||||
m_bConfigInitialized = false;
|
||||
m_szConfigFilename = NULL;
|
||||
m_szDestDir = NULL;
|
||||
m_szTempDir = NULL;
|
||||
@@ -148,16 +167,18 @@ Options::Options(int argc, char* argv[])
|
||||
m_eWarningTarget = mtScreen;
|
||||
m_eErrorTarget = mtScreen;
|
||||
m_eDebugTarget = mtScreen;
|
||||
m_eDecoder = dcUulib;
|
||||
m_eDetailTarget = mtScreen;
|
||||
m_bDecode = true;
|
||||
m_bPause = false;
|
||||
m_bCreateBrokenLog = false;
|
||||
m_bResetLog = false;
|
||||
m_fDownloadRate = 0;
|
||||
m_iEditQueueAction = 0;
|
||||
m_iEditQueueIDFrom = 0;
|
||||
m_iEditQueueIDTo = 0;
|
||||
m_pEditQueueIDList = NULL;
|
||||
m_iEditQueueIDCount = 0;
|
||||
m_iEditQueueOffset = 0;
|
||||
m_szArgFilename = NULL;
|
||||
m_szLastArg = NULL;
|
||||
m_iConnectionTimeout = 0;
|
||||
m_iTerminateTimeout = 0;
|
||||
m_bServerMode = false;
|
||||
@@ -176,30 +197,50 @@ Options::Options(int argc, char* argv[])
|
||||
m_szServerIP = NULL;
|
||||
m_szServerPassword = NULL;
|
||||
m_szLockFile = NULL;
|
||||
m_szDaemonUserName = NULL;
|
||||
m_eOutputMode = omLoggable;
|
||||
m_bReloadQueue = false;
|
||||
m_bReloadPostQueue = false;
|
||||
m_iLogBufferSize = 0;
|
||||
m_iLogLines = 0;
|
||||
m_iWriteLogKind = 0;
|
||||
m_bCreateLog = false;
|
||||
m_szLogFile = NULL;
|
||||
m_eLoadPars = plAll;
|
||||
m_eLoadPars = lpAll;
|
||||
m_bParCheck = false;
|
||||
m_bParRepair = false;
|
||||
m_bTest = false;
|
||||
m_szPostProcess = NULL;
|
||||
m_bStrictParName = false;
|
||||
m_bNoConfig = false;
|
||||
m_iUMask = 0;
|
||||
m_iUpdateInterval = 0;
|
||||
m_bCursesNZBName = false;
|
||||
m_bCursesTime = false;
|
||||
m_bCursesGroup = false;
|
||||
m_bCrcCheck = false;
|
||||
m_bRetryOnCrcError = false;
|
||||
m_bDirectWrite = false;
|
||||
m_iThreadLimit = 0;
|
||||
m_iWriteBufferSize = 0;
|
||||
m_iNzbDirInterval = 0;
|
||||
m_iNzbDirFileAge = 0;
|
||||
m_bParCleanupQueue = false;
|
||||
m_iDiskSpace = 0;
|
||||
m_ePostLogKind = plNone;
|
||||
|
||||
char szFilename[MAX_PATH + 1];
|
||||
#ifdef WIN32
|
||||
GetModuleFileName(NULL, szFilename, MAX_PATH + 1);
|
||||
#else
|
||||
strncpy(szFilename, argv[0], MAX_PATH + 1);
|
||||
#endif
|
||||
szFilename[MAX_PATH] = '\0';
|
||||
NormalizePathSeparators(szFilename);
|
||||
Util::NormalizePathSeparators(szFilename);
|
||||
char* end = strrchr(szFilename, PATH_SEPARATOR);
|
||||
if (end) *end = '\0';
|
||||
SetOption("APPDIR", szFilename);
|
||||
|
||||
InitDefault();
|
||||
InitOptFile(argc, argv);
|
||||
InitCommandLine(argc, argv);
|
||||
|
||||
if (m_bPrintOptions)
|
||||
@@ -269,6 +310,10 @@ Options::~Options()
|
||||
{
|
||||
free(m_szArgFilename);
|
||||
}
|
||||
if (m_szLastArg)
|
||||
{
|
||||
free(m_szLastArg);
|
||||
}
|
||||
if (m_szServerIP)
|
||||
{
|
||||
free(m_szServerIP);
|
||||
@@ -285,10 +330,18 @@ Options::~Options()
|
||||
{
|
||||
free(m_szLockFile);
|
||||
}
|
||||
if (m_szDaemonUserName)
|
||||
{
|
||||
free(m_szDaemonUserName);
|
||||
}
|
||||
if (m_szPostProcess)
|
||||
{
|
||||
free(m_szPostProcess);
|
||||
}
|
||||
if (m_pEditQueueIDList)
|
||||
{
|
||||
free(m_pEditQueueIDList);
|
||||
}
|
||||
|
||||
for (unsigned int i = 0; i < optEntries.size(); i++)
|
||||
{
|
||||
@@ -338,10 +391,11 @@ void Options::InitDefault()
|
||||
SetOption(OPTION_SERVERPORT, "6789");
|
||||
SetOption(OPTION_CONNECTIONTIMEOUT, "60");
|
||||
SetOption(OPTION_SAVEQUEUE, "yes");
|
||||
SetOption(OPTION_RELOADQUEUE, "ask");
|
||||
SetOption(OPTION_RELOADQUEUE, "yes");
|
||||
SetOption(OPTION_RELOADPOSTQUEUE, "yes");
|
||||
SetOption(OPTION_CREATEBROKENLOG, "no");
|
||||
SetOption(OPTION_RESETLOG, "no");
|
||||
SetOption(OPTION_DECODER, "yEnc");
|
||||
SetOption(OPTION_DECODE, "yes");
|
||||
SetOption(OPTION_RETRIES, "5");
|
||||
SetOption(OPTION_RETRYINTERVAL, "10");
|
||||
SetOption(OPTION_TERMINATETIMEOUT, "600");
|
||||
@@ -351,57 +405,51 @@ void Options::InitDefault()
|
||||
SetOption(OPTION_WARNINGTARGET, "both");
|
||||
SetOption(OPTION_ERRORTARGET, "both");
|
||||
SetOption(OPTION_DEBUGTARGET, "none");
|
||||
SetOption(OPTION_DETAILTARGET, "both");
|
||||
SetOption(OPTION_LOADPARS, "all");
|
||||
SetOption(OPTION_PARCHECK, "no");
|
||||
SetOption(OPTION_PARREPAIR, "no");
|
||||
SetOption(OPTION_POSTPROCESS, "");
|
||||
SetOption(OPTION_STRICTPARNAME, "yes");
|
||||
SetOption(OPTION_DAEMONUSERNAME, "root");
|
||||
SetOption(OPTION_UMASK, "1000");
|
||||
SetOption(OPTION_UPDATEINTERVAL, "200");
|
||||
SetOption(OPTION_CURSESNZBNAME, "yes");
|
||||
SetOption(OPTION_CURSESTIME, "no");
|
||||
SetOption(OPTION_CURSESGROUP, "no");
|
||||
SetOption(OPTION_CRCCHECK, "yes");
|
||||
SetOption(OPTION_RETRYONCRCERROR, "no");
|
||||
SetOption(OPTION_THREADLIMIT, "100");
|
||||
SetOption(OPTION_DIRECTWRITE, "no");
|
||||
SetOption(OPTION_WRITEBUFFERSIZE, "0");
|
||||
SetOption(OPTION_NZBDIRINTERVAL, "5");
|
||||
SetOption(OPTION_NZBDIRFILEAGE, "60");
|
||||
SetOption(OPTION_PARCLEANUPQUEUE, "no");
|
||||
SetOption(OPTION_DISKSPACE, "0");
|
||||
SetOption(OPTION_POSTLOGKIND, "none");
|
||||
SetOption(OPTION_ALLOWREPROCESS, "no");
|
||||
}
|
||||
|
||||
void Options::InitOptFile(int argc, char* argv[])
|
||||
void Options::InitOptFile()
|
||||
{
|
||||
while (true)
|
||||
if (m_bConfigInitialized)
|
||||
{
|
||||
int c;
|
||||
|
||||
#ifdef HAVE_GETOPT_LONG
|
||||
int option_index = 0;
|
||||
c = getopt_long(argc, argv, short_options, long_options, &option_index);
|
||||
#else
|
||||
c = getopt(argc, argv, short_options);
|
||||
#endif
|
||||
|
||||
if (c == -1) break;
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case 'c':
|
||||
m_szConfigFilename = strdup(optarg);
|
||||
break;
|
||||
case 'n':
|
||||
m_szConfigFilename = NULL;
|
||||
m_bNoConfig = true;
|
||||
return;
|
||||
case '?':
|
||||
exit(-1);
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (!m_szConfigFilename)
|
||||
if (!m_szConfigFilename && !m_bNoConfig)
|
||||
{
|
||||
// search for config file in default locations
|
||||
#ifdef WIN32
|
||||
char szFilename[MAX_PATH + 1];
|
||||
strncpy(szFilename, argv[0], MAX_PATH + 1);
|
||||
GetModuleFileName(NULL, szFilename, MAX_PATH + 1);
|
||||
szFilename[MAX_PATH] = '\0';
|
||||
NormalizePathSeparators(szFilename);
|
||||
Util::NormalizePathSeparators(szFilename);
|
||||
char* end = strrchr(szFilename, PATH_SEPARATOR);
|
||||
if (end) end[1] = '\0';
|
||||
strcat(szFilename, "nzbget.conf");
|
||||
|
||||
struct stat buffer;
|
||||
if (!stat(szFilename, &buffer) && S_ISREG(buffer.st_mode))
|
||||
if (Util::FileExists(szFilename))
|
||||
{
|
||||
m_szConfigFilename = strdup(szFilename);
|
||||
}
|
||||
@@ -413,8 +461,7 @@ void Options::InitOptFile(int argc, char* argv[])
|
||||
SetOption("$CONFIGFILENAME", szFilename);
|
||||
szFilename = GetOption("$CONFIGFILENAME");
|
||||
|
||||
struct stat buffer;
|
||||
if (!stat(szFilename, &buffer) && S_ISREG(buffer.st_mode))
|
||||
if (Util::FileExists(szFilename))
|
||||
{
|
||||
m_szConfigFilename = strdup(szFilename);
|
||||
DelOption("$CONFIGFILENAME");
|
||||
@@ -429,6 +476,8 @@ void Options::InitOptFile(int argc, char* argv[])
|
||||
{
|
||||
LoadConfig(m_szConfigFilename);
|
||||
}
|
||||
|
||||
m_bConfigInitialized = true;
|
||||
}
|
||||
|
||||
void Options::CheckDir(char** dir, const char* szOptionName)
|
||||
@@ -450,14 +499,17 @@ void Options::CheckDir(char** dir, const char* szOptionName)
|
||||
usedir[len] = PATH_SEPARATOR;
|
||||
usedir[len + 1] = '\0';
|
||||
}
|
||||
NormalizePathSeparators(usedir);
|
||||
Util::NormalizePathSeparators(usedir);
|
||||
}
|
||||
else
|
||||
{
|
||||
abort("FATAL ERROR: Wrong value for option \"%s\"\n", szOptionName);
|
||||
}
|
||||
// Ensure the dir is created
|
||||
mkdir(usedir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
|
||||
if (!Util::ForceDirectories(usedir))
|
||||
{
|
||||
abort("FATAL ERROR: Directory \"%s\" (option \"%s\") does not exist and could not be created\n", usedir, szOptionName);
|
||||
}
|
||||
*dir = usedir;
|
||||
}
|
||||
|
||||
@@ -466,7 +518,7 @@ void Options::InitOptions()
|
||||
CheckDir(&m_szDestDir, OPTION_DESTDIR);
|
||||
CheckDir(&m_szTempDir, OPTION_TEMPDIR);
|
||||
CheckDir(&m_szQueueDir, OPTION_QUEUEDIR);
|
||||
m_szNzbDir = strdup(GetOption(OPTION_NZBDIR));
|
||||
|
||||
m_szPostProcess = strdup(GetOption(OPTION_POSTPROCESS));
|
||||
|
||||
m_fDownloadRate = (float)atof(GetOption(OPTION_DOWNLOADRATE));
|
||||
@@ -478,47 +530,71 @@ void Options::InitOptions()
|
||||
m_szServerIP = strdup(GetOption(OPTION_SERVERIP));
|
||||
m_szServerPassword = strdup(GetOption(OPTION_SERVERPASSWORD));
|
||||
m_szLockFile = strdup(GetOption(OPTION_LOCKFILE));
|
||||
m_szDaemonUserName = strdup(GetOption(OPTION_DAEMONUSERNAME));
|
||||
m_iLogBufferSize = atoi(GetOption(OPTION_LOGBUFFERSIZE));
|
||||
m_szLogFile = strdup(GetOption(OPTION_LOGFILE));
|
||||
|
||||
const char* BoolNames[] = { "yes", "no", "true", "false", "1", "0", "on", "off", "enable", "disable" };
|
||||
const int BoolValues[] = { 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 };
|
||||
const int BoolCount = 10;
|
||||
m_bCreateBrokenLog = (bool)ParseOptionValue(OPTION_CREATEBROKENLOG, BoolCount, BoolNames, (const int*)BoolValues);
|
||||
m_bResetLog = (bool)ParseOptionValue(OPTION_RESETLOG, BoolCount, BoolNames, (const int*)BoolValues);
|
||||
m_bAppendNZBDir = (bool)ParseOptionValue(OPTION_APPENDNZBDIR, BoolCount, BoolNames, (const int*)BoolValues);
|
||||
m_bContinuePartial = (bool)ParseOptionValue(OPTION_CONTINUEPARTIAL, BoolCount, BoolNames, (const int*)BoolValues);
|
||||
m_bRenameBroken = (bool)ParseOptionValue(OPTION_RENAMEBROKEN, BoolCount, BoolNames, (const int*)BoolValues);
|
||||
m_bSaveQueue = (bool)ParseOptionValue(OPTION_SAVEQUEUE, BoolCount, BoolNames, (const int*)BoolValues);
|
||||
m_bDupeCheck = (bool)ParseOptionValue(OPTION_DUPECHECK, BoolCount, BoolNames, (const int*)BoolValues);
|
||||
m_bCreateLog = (bool)ParseOptionValue(OPTION_CREATELOG, BoolCount, BoolNames, (const int*)BoolValues);
|
||||
m_bParCheck = (bool)ParseOptionValue(OPTION_PARCHECK, BoolCount, BoolNames, (const int*)BoolValues);
|
||||
m_bParRepair = (bool)ParseOptionValue(OPTION_PARREPAIR, BoolCount, BoolNames, (const int*)BoolValues);
|
||||
m_bStrictParName = (bool)ParseOptionValue(OPTION_STRICTPARNAME, BoolCount, BoolNames, (const int*)BoolValues);
|
||||
m_bReloadQueue = (bool)ParseOptionValue(OPTION_RELOADQUEUE, BoolCount, BoolNames, (const int*)BoolValues);
|
||||
m_iUMask = strtol(GetOption(OPTION_UMASK), NULL, 8);
|
||||
m_iUpdateInterval = atoi(GetOption(OPTION_UPDATEINTERVAL));
|
||||
m_iThreadLimit = atoi(GetOption(OPTION_THREADLIMIT));
|
||||
m_iWriteBufferSize = atoi(GetOption(OPTION_WRITEBUFFERSIZE));
|
||||
m_iNzbDirInterval = atoi(GetOption(OPTION_NZBDIRINTERVAL));
|
||||
m_iNzbDirFileAge = atoi(GetOption(OPTION_NZBDIRFILEAGE));
|
||||
m_iDiskSpace = atoi(GetOption(OPTION_DISKSPACE));
|
||||
|
||||
if (m_iNzbDirInterval > 0)
|
||||
{
|
||||
CheckDir(&m_szNzbDir, OPTION_NZBDIR);
|
||||
}
|
||||
|
||||
const char* BoolNames[] = { "yes", "no", "true", "false", "1", "0", "on", "off", "enable", "disable", "enabled", "disabled" };
|
||||
const int BoolValues[] = { 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0 };
|
||||
const int BoolCount = 12;
|
||||
m_bCreateBrokenLog = (bool)ParseOptionValue(OPTION_CREATEBROKENLOG, BoolCount, BoolNames, BoolValues);
|
||||
m_bResetLog = (bool)ParseOptionValue(OPTION_RESETLOG, BoolCount, BoolNames, BoolValues);
|
||||
m_bAppendNZBDir = (bool)ParseOptionValue(OPTION_APPENDNZBDIR, BoolCount, BoolNames, BoolValues);
|
||||
m_bContinuePartial = (bool)ParseOptionValue(OPTION_CONTINUEPARTIAL, BoolCount, BoolNames, BoolValues);
|
||||
m_bRenameBroken = (bool)ParseOptionValue(OPTION_RENAMEBROKEN, BoolCount, BoolNames, BoolValues);
|
||||
m_bSaveQueue = (bool)ParseOptionValue(OPTION_SAVEQUEUE, BoolCount, BoolNames, BoolValues);
|
||||
m_bDupeCheck = (bool)ParseOptionValue(OPTION_DUPECHECK, BoolCount, BoolNames, BoolValues);
|
||||
m_bCreateLog = (bool)ParseOptionValue(OPTION_CREATELOG, BoolCount, BoolNames, BoolValues);
|
||||
m_bParCheck = (bool)ParseOptionValue(OPTION_PARCHECK, BoolCount, BoolNames, BoolValues);
|
||||
m_bParRepair = (bool)ParseOptionValue(OPTION_PARREPAIR, BoolCount, BoolNames, BoolValues);
|
||||
m_bStrictParName = (bool)ParseOptionValue(OPTION_STRICTPARNAME, BoolCount, BoolNames, BoolValues);
|
||||
m_bReloadQueue = (bool)ParseOptionValue(OPTION_RELOADQUEUE, BoolCount, BoolNames, BoolValues);
|
||||
m_bReloadPostQueue = (bool)ParseOptionValue(OPTION_RELOADPOSTQUEUE, BoolCount, BoolNames, BoolValues);
|
||||
m_bCursesNZBName = (bool)ParseOptionValue(OPTION_CURSESNZBNAME, BoolCount, BoolNames, BoolValues);
|
||||
m_bCursesTime = (bool)ParseOptionValue(OPTION_CURSESTIME, BoolCount, BoolNames, BoolValues);
|
||||
m_bCursesGroup = (bool)ParseOptionValue(OPTION_CURSESGROUP, BoolCount, BoolNames, BoolValues);
|
||||
m_bCrcCheck = (bool)ParseOptionValue(OPTION_CRCCHECK, BoolCount, BoolNames, BoolValues);
|
||||
m_bRetryOnCrcError = (bool)ParseOptionValue(OPTION_RETRYONCRCERROR, BoolCount, BoolNames, BoolValues);
|
||||
m_bDirectWrite = (bool)ParseOptionValue(OPTION_DIRECTWRITE, BoolCount, BoolNames, BoolValues);
|
||||
m_bParCleanupQueue = (bool)ParseOptionValue(OPTION_PARCLEANUPQUEUE, BoolCount, BoolNames, BoolValues);
|
||||
m_bDecode = (bool)ParseOptionValue(OPTION_DECODE, BoolCount, BoolNames, BoolValues);
|
||||
m_bAllowReProcess = (bool)ParseOptionValue(OPTION_ALLOWREPROCESS, BoolCount, BoolNames, BoolValues);
|
||||
|
||||
const char* OutputModeNames[] = { "loggable", "logable", "log", "colored", "color", "ncurses", "curses" };
|
||||
const EOutputMode OutputModeValues[] = { omLoggable, omLoggable, omLoggable, omColored, omColored, omNCurses, omNCurses };
|
||||
const int OutputModeValues[] = { omLoggable, omLoggable, omLoggable, omColored, omColored, omNCurses, omNCurses };
|
||||
const int OutputModeCount = 7;
|
||||
m_eOutputMode = (EOutputMode)ParseOptionValue(OPTION_OUTPUTMODE, OutputModeCount, OutputModeNames, (const int*)OutputModeValues);
|
||||
m_eOutputMode = (EOutputMode)ParseOptionValue(OPTION_OUTPUTMODE, OutputModeCount, OutputModeNames, OutputModeValues);
|
||||
|
||||
const char* DecoderNames[] = { "uulib", "yenc", "none", "ydec", "ydecoder" };
|
||||
const EDecoder DecoderValues[] = { dcUulib, dcYenc, dcNone, dcYenc, dcYenc };
|
||||
const int DecoderCount = 5;
|
||||
m_eDecoder = (EDecoder)ParseOptionValue(OPTION_DECODER, DecoderCount, DecoderNames, (const int*)DecoderValues);
|
||||
|
||||
const char* LoadParsNames[] = { "none", "one", "all", "1", "0" };
|
||||
const ELoadPars LoadParsValues[] = { plNone, plOne, plAll, plOne, plNone };
|
||||
const int LoadParsValues[] = { lpNone, lpOne, lpAll, lpOne, lpNone };
|
||||
const int LoadParsCount = 4;
|
||||
m_eLoadPars = (ELoadPars)ParseOptionValue(OPTION_LOADPARS, LoadParsCount, LoadParsNames, (const int*)LoadParsValues);
|
||||
m_eLoadPars = (ELoadPars)ParseOptionValue(OPTION_LOADPARS, LoadParsCount, LoadParsNames, LoadParsValues);
|
||||
|
||||
const char* TargetNames[] = { "screen", "log", "both", "none" };
|
||||
const EMessageTarget TargetValues[] = { mtScreen, mtLog, mtBoth, mtNone };
|
||||
const int TargetValues[] = { mtScreen, mtLog, mtBoth, mtNone };
|
||||
const int TargetCount = 4;
|
||||
m_eInfoTarget = (EMessageTarget)ParseOptionValue(OPTION_INFOTARGET, TargetCount, TargetNames, (const int*)TargetValues);
|
||||
m_eWarningTarget = (EMessageTarget)ParseOptionValue(OPTION_WARNINGTARGET, TargetCount, TargetNames, (const int*)TargetValues);
|
||||
m_eErrorTarget = (EMessageTarget)ParseOptionValue(OPTION_ERRORTARGET, TargetCount, TargetNames, (const int*)TargetValues);
|
||||
m_eDebugTarget = (EMessageTarget)ParseOptionValue(OPTION_DEBUGTARGET, TargetCount, TargetNames, (const int*)TargetValues);
|
||||
m_eInfoTarget = (EMessageTarget)ParseOptionValue(OPTION_INFOTARGET, TargetCount, TargetNames, TargetValues);
|
||||
m_eWarningTarget = (EMessageTarget)ParseOptionValue(OPTION_WARNINGTARGET, TargetCount, TargetNames, TargetValues);
|
||||
m_eErrorTarget = (EMessageTarget)ParseOptionValue(OPTION_ERRORTARGET, TargetCount, TargetNames, TargetValues);
|
||||
m_eDebugTarget = (EMessageTarget)ParseOptionValue(OPTION_DEBUGTARGET, TargetCount, TargetNames, TargetValues);
|
||||
m_eDetailTarget = (EMessageTarget)ParseOptionValue(OPTION_DETAILTARGET, TargetCount, TargetNames, TargetValues);
|
||||
|
||||
const char* PostLogKindNames[] = { "none", "detail", "info", "warning", "error", "debug" };
|
||||
const int PostLogKindValues[] = { plNone, plDetail, plInfo, plWarning, plError, plDebug };
|
||||
const int PostLogKindCount = 6;
|
||||
m_ePostLogKind = (EPostLogKind)ParseOptionValue(OPTION_POSTLOGKIND, PostLogKindCount, PostLogKindNames, PostLogKindValues);
|
||||
}
|
||||
|
||||
int Options::ParseOptionValue(const char * OptName, int argc, const char * argn[], const int argv[])
|
||||
@@ -563,6 +639,13 @@ void Options::InitCommandLine(int argc, char* argv[])
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case 'c':
|
||||
m_szConfigFilename = strdup(optarg);
|
||||
break;
|
||||
case 'n':
|
||||
m_szConfigFilename = NULL;
|
||||
m_bNoConfig = true;
|
||||
break;
|
||||
case 'h':
|
||||
PrintUsage(argv[0]);
|
||||
exit(0);
|
||||
@@ -575,6 +658,7 @@ void Options::InitCommandLine(int argc, char* argv[])
|
||||
m_bPrintOptions = true;
|
||||
break;
|
||||
case 'o':
|
||||
InitOptFile();
|
||||
if (!SetOptionString(optarg))
|
||||
{
|
||||
abort("FATAL ERROR: could not set option: %s\n", optarg);
|
||||
@@ -623,25 +707,44 @@ void Options::InitCommandLine(int argc, char* argv[])
|
||||
case 'E':
|
||||
{
|
||||
m_eClientOperation = opClientRequestEditQueue;
|
||||
bool bGroup = !strcasecmp(optarg, "G");
|
||||
if (bGroup)
|
||||
{
|
||||
optind++;
|
||||
if (optind > argc)
|
||||
{
|
||||
abort("FATAL ERROR: Could not parse value of option 'E'\n");
|
||||
}
|
||||
optarg = argv[optind-1];
|
||||
}
|
||||
|
||||
if (!strcasecmp(optarg, "T"))
|
||||
{
|
||||
m_iEditQueueAction = NZBMessageRequest::eActionMoveTop;
|
||||
m_iEditQueueAction = bGroup ? eRemoteEditActionGroupMoveTop : eRemoteEditActionFileMoveTop;
|
||||
}
|
||||
else if (!strcasecmp(optarg, "B"))
|
||||
{
|
||||
m_iEditQueueAction = NZBMessageRequest::eActionMoveBottom;
|
||||
m_iEditQueueAction = bGroup ? eRemoteEditActionGroupMoveBottom : eRemoteEditActionFileMoveBottom;
|
||||
}
|
||||
else if (!strcasecmp(optarg, "P"))
|
||||
{
|
||||
m_iEditQueueAction = NZBMessageRequest::eActionPause;
|
||||
m_iEditQueueAction = bGroup ? eRemoteEditActionGroupPause : eRemoteEditActionFilePause;
|
||||
}
|
||||
else if (!strcasecmp(optarg, "A"))
|
||||
{
|
||||
m_iEditQueueAction = bGroup ? eRemoteEditActionGroupPauseAllPars : eRemoteEditActionFilePauseAllPars;
|
||||
}
|
||||
else if (!strcasecmp(optarg, "R"))
|
||||
{
|
||||
m_iEditQueueAction = bGroup ? eRemoteEditActionGroupPauseExtraPars : eRemoteEditActionFilePauseExtraPars;
|
||||
}
|
||||
else if (!strcasecmp(optarg, "U"))
|
||||
{
|
||||
m_iEditQueueAction = NZBMessageRequest::eActionResume;
|
||||
m_iEditQueueAction = bGroup ? eRemoteEditActionGroupResume : eRemoteEditActionFileResume;
|
||||
}
|
||||
else if (!strcasecmp(optarg, "D"))
|
||||
{
|
||||
m_iEditQueueAction = NZBMessageRequest::eActionDelete;
|
||||
m_iEditQueueAction = bGroup ? eRemoteEditActionGroupDelete : eRemoteEditActionFileDelete;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -650,90 +753,94 @@ void Options::InitCommandLine(int argc, char* argv[])
|
||||
{
|
||||
abort("FATAL ERROR: Could not parse value of option 'E'\n");
|
||||
}
|
||||
m_iEditQueueAction = NZBMessageRequest::eActionMoveOffset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'I':
|
||||
{
|
||||
const char* p = strchr(optarg, '-');
|
||||
if (p)
|
||||
{
|
||||
char buf[101];
|
||||
int maxlen = p - optarg < 100 ? p - optarg : 100;
|
||||
strncpy(buf, optarg, maxlen);
|
||||
buf[maxlen] = '\0';
|
||||
m_iEditQueueIDFrom = atoi(buf);
|
||||
m_iEditQueueIDTo = atoi(p + 1);
|
||||
if (m_iEditQueueIDFrom <= 0 || m_iEditQueueIDTo <= 0 ||
|
||||
m_iEditQueueIDFrom > m_iEditQueueIDTo)
|
||||
{
|
||||
abort("FATAL ERROR: wrong value for option 'I'\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_iEditQueueIDFrom = atoi(optarg);
|
||||
if (m_iEditQueueIDFrom <= 0)
|
||||
{
|
||||
abort("FATAL ERROR: wrong value for option 'I'\n");
|
||||
}
|
||||
m_iEditQueueIDTo = m_iEditQueueIDFrom;
|
||||
m_iEditQueueAction = bGroup ? eRemoteEditActionGroupMoveOffset : eRemoteEditActionFileMoveOffset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'Q':
|
||||
m_eClientOperation = opClientRequestShutdown;
|
||||
break;
|
||||
#ifdef DEBUG
|
||||
case 't':
|
||||
m_bTest = true;
|
||||
case 'V':
|
||||
m_eClientOperation = opClientRequestVersion;
|
||||
break;
|
||||
case 'O':
|
||||
m_eClientOperation = opClientRequestPostQueue;
|
||||
break;
|
||||
case 'W':
|
||||
m_eClientOperation = opClientRequestWriteLog;
|
||||
if (!strcmp(optarg, "I")) {
|
||||
m_iWriteLogKind = (int)Message::mkInfo;
|
||||
}
|
||||
else if (!strcmp(optarg, "W")) {
|
||||
m_iWriteLogKind = (int)Message::mkWarning;
|
||||
}
|
||||
else if (!strcmp(optarg, "E")) {
|
||||
m_iWriteLogKind = (int)Message::mkError;
|
||||
}
|
||||
else if (!strcmp(optarg, "D")) {
|
||||
m_iWriteLogKind = (int)Message::mkDetail;
|
||||
}
|
||||
else if (!strcmp(optarg, "G")) {
|
||||
m_iWriteLogKind = (int)Message::mkDebug;
|
||||
}
|
||||
else
|
||||
{
|
||||
abort("FATAL ERROR: Could not parse value of option 'W'\n");
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
case '?':
|
||||
exit(-1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
InitOptFile();
|
||||
}
|
||||
|
||||
void Options::PrintUsage(char* com)
|
||||
{
|
||||
printf("Usage: %s [switches] [<nzb-file>]\n"
|
||||
"Switches:\n"
|
||||
" -h, --help Print this help-message\n"
|
||||
" -v, --version Print version and exit\n"
|
||||
" -c, --configfile <file> Filename of configuration-file\n"
|
||||
" -n, --noconfigfile Prevent loading of configuration-file\n"
|
||||
" (required options must be passed with --option)\n"
|
||||
" -p, --printconfig Print configuration and exit\n"
|
||||
" -o, --option <name=value> Set or override option in configuration-file\n"
|
||||
" -s, --server Start nzbget as a server in console-mode\n"
|
||||
" -D, --daemon Start nzbget as a server in daemon-mode\n"
|
||||
" -Q, --quit Shutdown the server\n"
|
||||
" -A, --append Send file to the server's download queue\n"
|
||||
" -C, --connect Attach client to server\n"
|
||||
" -L, --list Request list of downloads from the server\n"
|
||||
" -P, --pause Pause downloading on the server\n"
|
||||
" -U, --unpause Unpause downloading on the server\n"
|
||||
" -R, --rate Set the download rate on the server\n"
|
||||
" -T, --top Add file to the top (begining) of queue\n"
|
||||
" (should be used with switch --append)\n"
|
||||
" -G, --log <lines> Request last <lines> lines from server's screen-log\n"
|
||||
" -E, --edit <action> Edit queue on the server\n"
|
||||
" (must be used with switch --fileid):\n"
|
||||
" where <action> is one of:\n"
|
||||
" <+offset|-offset> Move file(s) in queue relative to current position\n"
|
||||
" offset is an integer number\n"
|
||||
" T Move file(s) to the top of queue\n"
|
||||
" B Move file(s) to the bottom of queue\n"
|
||||
" P Pause file(s)\n"
|
||||
" U Resume (unpause) file(s)\n"
|
||||
" D Delete file(s)\n"
|
||||
" -I, --fileid <FileID|FileIDFrom-FileIDTo> File-id(s) for switch '-E',\n"
|
||||
" as printed by switch --list\n"
|
||||
"",
|
||||
com);
|
||||
printf("Usage:\n"
|
||||
" %s [switches]\n\n"
|
||||
"Switches:\n"
|
||||
" -h, --help Print this help-message\n"
|
||||
" -v, --version Print version and exit\n"
|
||||
" -c, --configfile <file> Filename of configuration-file\n"
|
||||
" -n, --noconfigfile Prevent loading of configuration-file\n"
|
||||
" (required options must be passed with --option)\n"
|
||||
" -p, --printconfig Print configuration and exit\n"
|
||||
" -o, --option <name=value> Set or override option in configuration-file\n"
|
||||
" -s, --server Start nzbget as a server in console-mode\n"
|
||||
#ifndef WIN32
|
||||
" -D, --daemon Start nzbget as a server in daemon-mode\n"
|
||||
#endif
|
||||
" -V, --serverversion Print server's version and exit\n"
|
||||
" -Q, --quit Shutdown the server\n"
|
||||
" -A, --append <nzb-file> Send file to the server's download queue\n"
|
||||
" -C, --connect Attach client to server\n"
|
||||
" -L, --list Request list of downloads from the server\n"
|
||||
" -P, --pause Pause downloading on the server\n"
|
||||
" -U, --unpause Unpause downloading on the server\n"
|
||||
" -R, --rate Set the download rate on the server\n"
|
||||
" -T, --top Add file to the top (begining) of queue\n"
|
||||
" (should be used with switch --append)\n"
|
||||
" -G, --log <lines> Request last <lines> lines from server's screen-log\n"
|
||||
" -W, --write <D|I|W|E|G> \"Text\" Send text to server's log\n"
|
||||
" -O, --post Request post-processor-queue from server\n"
|
||||
" -E, --edit [G] <action> <IDs> Edit queue on the server\n"
|
||||
" <G> Affect all files in the group (same nzb-file)\n"
|
||||
" <action> is one of:\n"
|
||||
" <+offset|-offset> Move file(s) in queue relative to current position,\n"
|
||||
" offset is an integer value\n"
|
||||
" T Move file(s) to the top of queue\n"
|
||||
" B Move file(s) to the bottom of queue\n"
|
||||
" P Pause file(s)\n"
|
||||
" U Resume (unpause) file(s)\n"
|
||||
" A Pause all pars (for groups)\n"
|
||||
" R Pause extra pars (for groups)\n"
|
||||
" D Delete file(s)\n"
|
||||
" <IDs> Comma-separated list of file-ids or ranges\n"
|
||||
" of file-ids, e. g.: 1-5,3,10-22\n",
|
||||
Util::BaseFileName(com));
|
||||
}
|
||||
|
||||
void Options::InitFileArg(int argc, char* argv[])
|
||||
@@ -743,14 +850,28 @@ void Options::InitFileArg(int argc, char* argv[])
|
||||
// no nzb-file passed
|
||||
if (!m_bServerMode && !m_bRemoteClientMode &&
|
||||
(m_eClientOperation == opClientNoOperation ||
|
||||
m_eClientOperation == opClientRequestDownload))
|
||||
m_eClientOperation == opClientRequestDownload ||
|
||||
m_eClientOperation == opClientRequestWriteLog))
|
||||
{
|
||||
printf("nzb-file not specified\n");
|
||||
if (m_eClientOperation == opClientRequestWriteLog)
|
||||
{
|
||||
printf("Log-text not specified\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Nzb-file not specified\n");
|
||||
}
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
else if (m_eClientOperation == opClientRequestEditQueue)
|
||||
{
|
||||
ParseFileIDList(argc, argv, optind);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_szLastArg = strdup(argv[optind]);
|
||||
|
||||
// Check if the file-name is a relative path or an absolute path
|
||||
// If the path starts with '/' its an absolute, else relative
|
||||
const char* szFileName = argv[optind];
|
||||
@@ -774,10 +895,11 @@ void Options::InitFileArg(int argc, char* argv[])
|
||||
#endif
|
||||
|
||||
if (m_bServerMode || m_bRemoteClientMode ||
|
||||
!((m_eClientOperation == opClientRequestDownload)
|
||||
|| (m_eClientOperation == opClientNoOperation)))
|
||||
!(m_eClientOperation == opClientNoOperation ||
|
||||
m_eClientOperation == opClientRequestDownload ||
|
||||
m_eClientOperation == opClientRequestWriteLog))
|
||||
{
|
||||
printf("nzb-file not needed for this command\n");
|
||||
printf("Too many arguments\n");
|
||||
exit(-1);
|
||||
}
|
||||
}
|
||||
@@ -924,7 +1046,7 @@ void Options::InitServers()
|
||||
const char* nconnections = GetOption(optname);
|
||||
|
||||
bool definition = nlevel || nhost || nport || nusername || npassword || nconnections;
|
||||
bool completed = nlevel && nhost && nport && nusername && npassword && nconnections;
|
||||
bool completed = nlevel && nhost && nport && nconnections;
|
||||
|
||||
if (!definition)
|
||||
{
|
||||
@@ -966,6 +1088,10 @@ void Options::LoadConfig(const char * configfile)
|
||||
{
|
||||
buf[strlen(buf)-1] = 0; // remove traling '\n'
|
||||
}
|
||||
if (buf[0] != 0 && buf[strlen(buf)-1] == '\r')
|
||||
{
|
||||
buf[strlen(buf)-1] = 0; // remove traling '\r' (for windows line endings)
|
||||
}
|
||||
|
||||
if (buf[0] == 0 || buf[0] == '#' || strspn(buf, " ") == strlen(buf))
|
||||
{
|
||||
@@ -994,7 +1120,7 @@ bool Options::SetOptionString(const char * option)
|
||||
{
|
||||
char optname[1001];
|
||||
char optvalue[1001];
|
||||
int maxlen = eq - option < 1000 ? eq - option : 1000;
|
||||
int maxlen = (int)(eq - option < 1000 ? eq - option : 1000);
|
||||
strncpy(optname, option, maxlen);
|
||||
optname[maxlen] = '\0';
|
||||
strncpy(optvalue, eq + 1, 1000);
|
||||
@@ -1066,4 +1192,90 @@ void Options::CheckOptions()
|
||||
abort("FATAL ERROR: Program was compiled without curses-support. Can not use \"curses\" frontend (option \"%s\")\n", OPTION_OUTPUTMODE);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!m_bDecode)
|
||||
{
|
||||
m_bDirectWrite = false;
|
||||
}
|
||||
}
|
||||
|
||||
void Options::ParseFileIDList(int argc, char* argv[], int optind)
|
||||
{
|
||||
std::vector<int> IDs;
|
||||
IDs.clear();
|
||||
|
||||
while (optind < argc)
|
||||
{
|
||||
char* szWritableFileIDList = strdup(argv[optind++]);
|
||||
|
||||
char* optarg = strtok(szWritableFileIDList, ", ");
|
||||
while (optarg)
|
||||
{
|
||||
int iEditQueueIDFrom = 0;
|
||||
int iEditQueueIDTo = 0;
|
||||
const char* p = strchr(optarg, '-');
|
||||
if (p)
|
||||
{
|
||||
char buf[101];
|
||||
int maxlen = (int)(p - optarg < 100 ? p - optarg : 100);
|
||||
strncpy(buf, optarg, maxlen);
|
||||
buf[maxlen] = '\0';
|
||||
iEditQueueIDFrom = atoi(buf);
|
||||
iEditQueueIDTo = atoi(p + 1);
|
||||
if (iEditQueueIDFrom <= 0 || iEditQueueIDTo <= 0)
|
||||
{
|
||||
abort("FATAL ERROR: invalid list of file IDs\n");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
iEditQueueIDFrom = atoi(optarg);
|
||||
if (iEditQueueIDFrom <= 0)
|
||||
{
|
||||
abort("FATAL ERROR: invalid list of file IDs\n");
|
||||
}
|
||||
iEditQueueIDTo = iEditQueueIDFrom;
|
||||
}
|
||||
|
||||
int iEditQueueIDCount = 0;
|
||||
if (iEditQueueIDTo != 0)
|
||||
{
|
||||
if (iEditQueueIDFrom < iEditQueueIDTo)
|
||||
{
|
||||
iEditQueueIDCount = iEditQueueIDTo - iEditQueueIDFrom + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
iEditQueueIDCount = iEditQueueIDFrom - iEditQueueIDTo + 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
iEditQueueIDCount = 1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < iEditQueueIDCount; i++)
|
||||
{
|
||||
if (iEditQueueIDFrom < iEditQueueIDTo || iEditQueueIDTo == 0)
|
||||
{
|
||||
IDs.push_back(iEditQueueIDFrom + i);
|
||||
}
|
||||
else
|
||||
{
|
||||
IDs.push_back(iEditQueueIDFrom - i);
|
||||
}
|
||||
}
|
||||
|
||||
optarg = strtok(NULL, ", ");
|
||||
}
|
||||
|
||||
free(szWritableFileIDList);
|
||||
}
|
||||
|
||||
m_iEditQueueIDCount = IDs.size();
|
||||
m_pEditQueueIDList = (int*)malloc(sizeof(int) * m_iEditQueueIDCount);
|
||||
for (int i = 0; i < m_iEditQueueIDCount; i++)
|
||||
{
|
||||
m_pEditQueueIDList[i] = IDs[i];
|
||||
}
|
||||
}
|
||||
|
||||
128
Options.h
128
Options.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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>
|
||||
@@ -34,41 +34,48 @@ class Options
|
||||
public:
|
||||
enum EClientOperation
|
||||
{
|
||||
opClientNoOperation,
|
||||
opClientRequestDownload,
|
||||
opClientRequestList,
|
||||
opClientRequestPause,
|
||||
opClientRequestUnpause,
|
||||
opClientRequestSetRate,
|
||||
opClientRequestDumpDebug,
|
||||
opClientRequestEditQueue,
|
||||
opClientRequestLog,
|
||||
opClientRequestShutdown
|
||||
opClientNoOperation,
|
||||
opClientRequestDownload,
|
||||
opClientRequestList,
|
||||
opClientRequestPause,
|
||||
opClientRequestUnpause,
|
||||
opClientRequestSetRate,
|
||||
opClientRequestDumpDebug,
|
||||
opClientRequestEditQueue,
|
||||
opClientRequestLog,
|
||||
opClientRequestShutdown,
|
||||
opClientRequestVersion,
|
||||
opClientRequestPostQueue,
|
||||
opClientRequestWriteLog
|
||||
};
|
||||
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 EPostLogKind
|
||||
{
|
||||
plNone,
|
||||
plDetail,
|
||||
plInfo,
|
||||
plWarning,
|
||||
plError,
|
||||
plDebug
|
||||
};
|
||||
|
||||
private:
|
||||
@@ -80,6 +87,8 @@ private:
|
||||
|
||||
std::vector< struct OptEntry > optEntries;
|
||||
|
||||
bool m_bConfigInitialized;
|
||||
|
||||
// Options
|
||||
char* m_szConfigFilename;
|
||||
char* m_szDestDir;
|
||||
@@ -90,7 +99,8 @@ private:
|
||||
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;
|
||||
@@ -106,8 +116,10 @@ private:
|
||||
char* m_szServerPassword;
|
||||
int m_szServerPort;
|
||||
char* m_szLockFile;
|
||||
char* m_szDaemonUserName;
|
||||
EOutputMode m_eOutputMode;
|
||||
bool m_bReloadQueue;
|
||||
bool m_bReloadPostQueue;
|
||||
int m_iLogBufferSize;
|
||||
bool m_bCreateLog;
|
||||
char* m_szLogFile;
|
||||
@@ -117,6 +129,22 @@ private:
|
||||
char* m_szPostProcess;
|
||||
bool m_bStrictParName;
|
||||
bool m_bNoConfig;
|
||||
int m_iUMask;
|
||||
int m_iUpdateInterval;
|
||||
bool m_bCursesNZBName;
|
||||
bool m_bCursesTime;
|
||||
bool m_bCursesGroup;
|
||||
bool m_bCrcCheck;
|
||||
bool m_bRetryOnCrcError;
|
||||
int m_iThreadLimit;
|
||||
bool m_bDirectWrite;
|
||||
int m_iWriteBufferSize;
|
||||
int m_iNzbDirInterval;
|
||||
int m_iNzbDirFileAge;
|
||||
bool m_bParCleanupQueue;
|
||||
int m_iDiskSpace;
|
||||
EPostLogKind m_ePostLogKind;
|
||||
bool m_bAllowReProcess;
|
||||
|
||||
// Parsed command-line parameters
|
||||
bool m_bServerMode;
|
||||
@@ -124,14 +152,15 @@ private:
|
||||
bool m_bRemoteClientMode;
|
||||
int m_iEditQueueAction;
|
||||
int m_iEditQueueOffset;
|
||||
int m_iEditQueueIDFrom;
|
||||
int m_iEditQueueIDTo;
|
||||
int* m_pEditQueueIDList;
|
||||
int m_iEditQueueIDCount;
|
||||
char* m_szArgFilename;
|
||||
char* m_szLastArg;
|
||||
bool m_bPrintOptions;
|
||||
bool m_bAddTop;
|
||||
float m_fSetRate;
|
||||
int m_iLogLines;
|
||||
bool m_bTest;
|
||||
int m_iWriteLogKind;
|
||||
|
||||
// Current state
|
||||
bool m_bPause;
|
||||
@@ -139,7 +168,7 @@ private:
|
||||
EClientOperation m_eClientOperation;
|
||||
|
||||
void InitDefault();
|
||||
void InitOptFile(int argc, char* argv[]);
|
||||
void InitOptFile();
|
||||
void InitCommandLine(int argc, char* argv[]);
|
||||
void InitOptions();
|
||||
void InitFileArg(int argc, char* argv[]);
|
||||
@@ -155,10 +184,11 @@ private:
|
||||
bool ValidateOptionName(const char* optname);
|
||||
void LoadConfig(const char* configfile);
|
||||
void CheckDir(char** dir, const char* szOptionName);
|
||||
void ParseFileIDList(int argc, char* argv[], int optind);
|
||||
|
||||
public:
|
||||
Options(int argc, char* argv[]);
|
||||
~Options();
|
||||
Options(int argc, char* argv[]);
|
||||
~Options();
|
||||
|
||||
// Options
|
||||
const char* GetDestDir() { return m_szDestDir; }
|
||||
@@ -171,9 +201,10 @@ public:
|
||||
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 GetContinuePartial() { return m_bContinuePartial; }
|
||||
bool GetRenameBroken() { return m_bRenameBroken; }
|
||||
@@ -185,8 +216,10 @@ public:
|
||||
char* GetServerPassword() { return m_szServerPassword; }
|
||||
int GetServerPort() { return m_szServerPort; }
|
||||
char* GetLockFile() { return m_szLockFile; }
|
||||
char* GetDaemonUserName() { return m_szDaemonUserName; }
|
||||
EOutputMode GetOutputMode() { return m_eOutputMode; }
|
||||
bool GetReloadQueue() { return m_bReloadQueue; }
|
||||
bool GetReloadPostQueue() { return m_bReloadPostQueue; }
|
||||
int GetLogBufferSize() { return m_iLogBufferSize; }
|
||||
bool GetCreateLog() { return m_bCreateLog; }
|
||||
char* GetLogFile() { return m_szLogFile; }
|
||||
@@ -195,21 +228,38 @@ public:
|
||||
bool GetParRepair() { return m_bParRepair; }
|
||||
const char* GetPostProcess() { return m_szPostProcess; }
|
||||
bool GetStrictParName() { return m_bStrictParName; }
|
||||
int GetUMask() { return m_iUMask; }
|
||||
int GetUpdateInterval() {return m_iUpdateInterval; }
|
||||
bool GetCursesNZBName() { return m_bCursesNZBName; }
|
||||
bool GetCursesTime() { return m_bCursesTime; }
|
||||
bool GetCursesGroup() { return m_bCursesGroup; }
|
||||
bool GetCrcCheck() { return m_bCrcCheck; }
|
||||
bool GetRetryOnCrcError() { return m_bRetryOnCrcError; }
|
||||
int GetThreadLimit() { return m_iThreadLimit; }
|
||||
bool GetDirectWrite() { return m_bDirectWrite; }
|
||||
int GetWriteBufferSize() { return m_iWriteBufferSize; }
|
||||
int GetNzbDirInterval() { return m_iNzbDirInterval; }
|
||||
int GetNzbDirFileAge() { return m_iNzbDirFileAge; }
|
||||
bool GetParCleanupQueue() { return m_bParCleanupQueue; }
|
||||
int GetDiskSpace() { return m_iDiskSpace; }
|
||||
EPostLogKind GetPostLogKind() { return m_ePostLogKind; }
|
||||
bool GetAllowReProcess() { return m_bAllowReProcess; }
|
||||
|
||||
// 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; }
|
||||
const char* GetArgFilename() { return m_szArgFilename; }
|
||||
const char* GetLastArg() { return m_szLastArg; }
|
||||
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; }
|
||||
|
||||
// Current state
|
||||
void SetPause(bool bOnOff) { m_bPause = bOnOff; }
|
||||
|
||||
488
ParChecker.cpp
488
ParChecker.cpp
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <ctype.h>
|
||||
#include <fstream>
|
||||
#ifdef WIN32
|
||||
#include <par2cmdline.h>
|
||||
#include <par2repairer.h>
|
||||
@@ -47,11 +48,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,11 +63,13 @@ const char* Par2CmdLineErrStr[] = { "OK",
|
||||
"internal error occurred",
|
||||
"out of memory" };
|
||||
|
||||
|
||||
class Repairer : public Par2Repairer
|
||||
{
|
||||
friend class ParChecker;
|
||||
};
|
||||
|
||||
|
||||
ParChecker::ParChecker()
|
||||
{
|
||||
debug("Creating ParChecker");
|
||||
@@ -78,6 +79,12 @@ ParChecker::ParChecker()
|
||||
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_eStage = ptLoadingPars;
|
||||
m_QueuedParFiles.clear();
|
||||
}
|
||||
|
||||
@@ -101,6 +108,7 @@ ParChecker::~ParChecker()
|
||||
{
|
||||
free(m_szErrMsg);
|
||||
}
|
||||
free(m_szProgressLabel);
|
||||
|
||||
for (QueuedParFiles::iterator it = m_QueuedParFiles.begin(); it != m_QueuedParFiles.end() ;it++)
|
||||
{
|
||||
@@ -144,12 +152,24 @@ 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;
|
||||
|
||||
info("Verifying %s", m_szInfoName);
|
||||
SetStatus(psWorking);
|
||||
|
||||
|
||||
snprintf(m_szProgressLabel, 1024, "Verifying %s", m_szInfoName);
|
||||
m_szProgressLabel[1024-1] = '\0';
|
||||
m_iFileProgress = 0;
|
||||
m_iStageProgress = 0;
|
||||
UpdateProgress();
|
||||
|
||||
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 +178,23 @@ void ParChecker::Run()
|
||||
}
|
||||
|
||||
Result res;
|
||||
Repairer* repairer = new Repairer();
|
||||
#ifdef ENABLE_PARPROGRESS
|
||||
repairer->sig_filename.connect(sigc::mem_fun(*this, &ParChecker::signal_filename));
|
||||
#endif
|
||||
|
||||
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));
|
||||
|
||||
res = repairer->PreProcess(commandLine);
|
||||
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 +206,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,6 +234,13 @@ 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();
|
||||
@@ -224,18 +265,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 +291,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)
|
||||
{
|
||||
@@ -278,214 +329,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,17 +343,19 @@ 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)
|
||||
@@ -524,9 +373,172 @@ void ParChecker::QueueChanged()
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
48
ParChecker.h
48
ParChecker.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
@@ -44,32 +44,57 @@ 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;
|
||||
int m_iProcessedFiles;
|
||||
int m_iFilesToRepair;
|
||||
int m_iExtraFiles;
|
||||
bool m_bVerifyingExtraFiles;
|
||||
char* m_szProgressLabel;
|
||||
int m_iFileProgress;
|
||||
int m_iStageProgress;
|
||||
|
||||
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();
|
||||
@@ -86,7 +111,6 @@ public:
|
||||
bool GetRepairNotNeeded() { return m_bRepairNotNeeded; }
|
||||
void AddParFile(const char* szParFilename);
|
||||
void QueueChanged();
|
||||
static bool ParseParFilename(const char* szParFilename, int* iBaseNameLen, int* iBlocks);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
154
PostInfo.cpp
Normal file
154
PostInfo.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "PostInfo.h"
|
||||
#include "Options.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
int PostInfo::m_iIDGen = 0;
|
||||
|
||||
PostInfo::PostInfo()
|
||||
{
|
||||
debug("Creating PostInfo");
|
||||
|
||||
m_szNZBFilename = NULL;
|
||||
m_szDestDir = NULL;
|
||||
m_szParFilename = NULL;
|
||||
m_szInfoName = NULL;
|
||||
m_bWorking = false;
|
||||
m_bParCheck = false;
|
||||
m_iParStatus = 0;
|
||||
m_bParFailed = false;
|
||||
m_szProgressLabel = strdup("");
|
||||
m_iFileProgress = 0;
|
||||
m_iStageProgress = 0;
|
||||
m_tStartTime = 0;
|
||||
m_tStageTime = 0;
|
||||
m_eStage = ptQueued;
|
||||
m_pScriptThread = NULL;
|
||||
m_Messages.clear();
|
||||
m_iIDGen++;
|
||||
m_iID = m_iIDGen;
|
||||
}
|
||||
|
||||
PostInfo::~ PostInfo()
|
||||
{
|
||||
debug("Destroying PostInfo");
|
||||
|
||||
if (m_szNZBFilename)
|
||||
{
|
||||
free(m_szNZBFilename);
|
||||
}
|
||||
if (m_szDestDir)
|
||||
{
|
||||
free(m_szDestDir);
|
||||
}
|
||||
if (m_szParFilename)
|
||||
{
|
||||
free(m_szParFilename);
|
||||
}
|
||||
if (m_szInfoName)
|
||||
{
|
||||
free(m_szInfoName);
|
||||
}
|
||||
if (m_szProgressLabel)
|
||||
{
|
||||
free(m_szProgressLabel);
|
||||
}
|
||||
for (Messages::iterator it = m_Messages.begin(); it != m_Messages.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
}
|
||||
m_Messages.clear();
|
||||
}
|
||||
|
||||
void PostInfo::SetNZBFilename(const char* szNZBFilename)
|
||||
{
|
||||
m_szNZBFilename = strdup(szNZBFilename);
|
||||
}
|
||||
|
||||
void PostInfo::SetDestDir(const char* szDestDir)
|
||||
{
|
||||
m_szDestDir = strdup(szDestDir);
|
||||
}
|
||||
|
||||
void PostInfo::SetParFilename(const char* szParFilename)
|
||||
{
|
||||
m_szParFilename = strdup(szParFilename);
|
||||
}
|
||||
|
||||
void PostInfo::SetInfoName(const char* szInfoName)
|
||||
{
|
||||
m_szInfoName = strdup(szInfoName);
|
||||
}
|
||||
|
||||
void PostInfo::SetProgressLabel(const char* szProgressLabel)
|
||||
{
|
||||
if (m_szProgressLabel)
|
||||
{
|
||||
free(m_szProgressLabel);
|
||||
}
|
||||
m_szProgressLabel = strdup(szProgressLabel);
|
||||
}
|
||||
|
||||
PostInfo::Messages* PostInfo::LockMessages()
|
||||
{
|
||||
m_mutexLog.Lock();
|
||||
return &m_Messages;
|
||||
}
|
||||
|
||||
void PostInfo::UnlockMessages()
|
||||
{
|
||||
m_mutexLog.Unlock();
|
||||
}
|
||||
|
||||
void PostInfo::AppendMessage(Message::EKind eKind, const char * szText)
|
||||
{
|
||||
Message* pMessage = new Message(++m_iIDGen, eKind, time(NULL), szText);
|
||||
|
||||
m_mutexLog.Lock();
|
||||
m_Messages.push_back(pMessage);
|
||||
|
||||
while (m_Messages.size() > (unsigned int)g_pOptions->GetLogBufferSize())
|
||||
{
|
||||
Message* pMessage = m_Messages.front();
|
||||
delete pMessage;
|
||||
m_Messages.pop_front();
|
||||
}
|
||||
m_mutexLog.Unlock();
|
||||
}
|
||||
113
PostInfo.h
Normal file
113
PostInfo.h
Normal file
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef POSTINFO_H
|
||||
#define POSTINFO_H
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include "Log.h"
|
||||
#include "Thread.h"
|
||||
|
||||
class PostInfo
|
||||
{
|
||||
public:
|
||||
enum EStage
|
||||
{
|
||||
ptQueued,
|
||||
ptLoadingPars,
|
||||
ptVerifyingSources,
|
||||
ptRepairing,
|
||||
ptVerifyingRepaired,
|
||||
ptExecutingScript,
|
||||
ptFinished
|
||||
};
|
||||
|
||||
typedef std::deque<Message*> Messages;
|
||||
|
||||
private:
|
||||
int m_iID;
|
||||
char* m_szNZBFilename;
|
||||
char* m_szDestDir;
|
||||
char* m_szParFilename;
|
||||
char* m_szInfoName;
|
||||
bool m_bWorking;
|
||||
bool m_bParCheck;
|
||||
int m_iParStatus;
|
||||
bool m_bParFailed;
|
||||
EStage m_eStage;
|
||||
char* m_szProgressLabel;
|
||||
int m_iFileProgress;
|
||||
int m_iStageProgress;
|
||||
time_t m_tStartTime;
|
||||
time_t m_tStageTime;
|
||||
Thread* m_pScriptThread;
|
||||
|
||||
Mutex m_mutexLog;
|
||||
Messages m_Messages;
|
||||
static int m_iIDGen;
|
||||
|
||||
public:
|
||||
PostInfo();
|
||||
~PostInfo();
|
||||
int GetID() { return m_iID; }
|
||||
const char* GetNZBFilename() { return m_szNZBFilename; }
|
||||
void SetNZBFilename(const char* szNZBFilename);
|
||||
const char* GetDestDir() { return m_szDestDir; }
|
||||
void SetDestDir(const char* szDestDir);
|
||||
const char* GetParFilename() { return m_szParFilename; }
|
||||
void SetParFilename(const char* szParFilename);
|
||||
const char* GetInfoName() { return m_szInfoName; }
|
||||
void SetInfoName(const char* szInfoName);
|
||||
EStage GetStage() { return m_eStage; }
|
||||
void SetStage(EStage eStage) { m_eStage = eStage; }
|
||||
void SetProgressLabel(const char* szProgressLabel);
|
||||
const char* GetProgressLabel() { return m_szProgressLabel; }
|
||||
int GetFileProgress() { return m_iFileProgress; }
|
||||
void SetFileProgress(int iFileProgress) { m_iFileProgress = iFileProgress; }
|
||||
int GetStageProgress() { return m_iStageProgress; }
|
||||
void SetStageProgress(int iStageProgress) { m_iStageProgress = iStageProgress; }
|
||||
time_t GetStartTime() { return m_tStartTime; }
|
||||
void SetStartTime(time_t tStartTime) { m_tStartTime = tStartTime; }
|
||||
time_t GetStageTime() { return m_tStageTime; }
|
||||
void SetStageTime(time_t tStageTime) { m_tStageTime = tStageTime; }
|
||||
bool GetWorking() { return m_bWorking; }
|
||||
void SetWorking(bool bWorking) { m_bWorking = bWorking; }
|
||||
bool GetParCheck() { return m_bParCheck; }
|
||||
void SetParCheck(bool bParCheck) { m_bParCheck = bParCheck; }
|
||||
int GetParStatus() { return m_iParStatus; }
|
||||
void SetParStatus(int iParStatus) { m_iParStatus = iParStatus; }
|
||||
bool GetParFailed() { return m_bParFailed; }
|
||||
void SetParFailed(bool bParFailed) { m_bParFailed = bParFailed; }
|
||||
void AppendMessage(Message::EKind eKind, const char* szText);
|
||||
Thread* GetScriptThread() { return m_pScriptThread; }
|
||||
void SetScriptThread(Thread* pScriptThread) { m_pScriptThread = pScriptThread; }
|
||||
Messages* LockMessages();
|
||||
void UnlockMessages();
|
||||
};
|
||||
|
||||
typedef std::deque<PostInfo*> PostQueue;
|
||||
|
||||
#endif
|
||||
1137
PrePostProcessor.cpp
1137
PrePostProcessor.cpp
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "Thread.h"
|
||||
#include "Observer.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "PostInfo.h"
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
#include "ParChecker.h"
|
||||
@@ -55,47 +56,68 @@ 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;
|
||||
|
||||
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; }
|
||||
PrePostProcessor* m_Owner;
|
||||
protected:
|
||||
virtual bool RequestMorePars(int iBlockNeeded, int* pBlockFound);
|
||||
virtual void UpdateProgress();
|
||||
|
||||
friend class PrePostProcessor;
|
||||
};
|
||||
|
||||
typedef std::deque<QueuedFile*> ParQueue;
|
||||
|
||||
struct BlockInfo
|
||||
{
|
||||
FileInfo* m_pFileInfo;
|
||||
int m_iBlockCount;
|
||||
};
|
||||
|
||||
typedef std::deque<BlockInfo*> Blocks;
|
||||
#endif
|
||||
|
||||
private:
|
||||
bool m_bCheckIncomingNZBs;
|
||||
QueueCoordinatorObserver m_QueueCoordinatorObserver;
|
||||
bool m_bHasMoreJobs;
|
||||
bool m_bPostScript;
|
||||
|
||||
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);
|
||||
|
||||
bool IsNZBFileCompleted(DownloadQueue* pDownloadQueue, const char* szNZBFilename,
|
||||
bool bIgnoreFirstInPostQueue, bool bIgnorePaused, bool bCheckPostQueue);
|
||||
bool CheckScript(FileInfo* pFileInfo);
|
||||
bool JobExists(PostQueue* pPostQueue, const char* szNZBFilename);
|
||||
bool ClearCompletedJobs(const char* szNZBFilename);
|
||||
void CheckPostQueue();
|
||||
void JobCompleted(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
|
||||
void StartScriptJob(DownloadQueue* pDownloadQueue, PostInfo* pPostInfo);
|
||||
void SavePostQueue();
|
||||
void SanitisePostQueue();
|
||||
void CheckDiskSpace();
|
||||
|
||||
Mutex m_mutexQueue;
|
||||
PostQueue m_PostQueue;
|
||||
PostQueue m_CompletedJobs;
|
||||
|
||||
#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 CheckPars(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
|
||||
bool AddPar(FileInfo* pFileInfo, bool bDeleted);
|
||||
bool SameParCollection(const char* szFilename1, const char* szFilename2);
|
||||
bool FindMainPars(const char* szPath, FileList* pFileList);
|
||||
int GetParCleanupQueueGroup(DownloadQueue* pDownloadQueue, const char* szNZBFilename);
|
||||
bool HasFailedParJobs(const char* szNZBFilename);
|
||||
bool ParJobExists(PostQueue* pPostQueue, const char* szParFilename);
|
||||
bool ParseParFilename(const char* szParFilename, int* iBaseNameLen, int* iBlocks);
|
||||
bool RequestMorePars(const char* szNZBFilename, const char* szParFilename, int iBlockNeeded, int* pBlockFound);
|
||||
void FindPars(DownloadQueue* pDownloadQueue, const char* szNZBFilename, const char* szParFilename,
|
||||
Blocks* pBlocks, bool bStrictParName, bool bExactParName, int* pBlockFound);
|
||||
void UpdateParProgress();
|
||||
void StartParJob(PostInfo* pPostInfo);
|
||||
#endif
|
||||
|
||||
public:
|
||||
@@ -105,6 +127,8 @@ public:
|
||||
virtual void Stop();
|
||||
void QueueCoordinatorUpdate(Subject* Caller, void* Aspect);
|
||||
bool HasMoreJobs() { return m_bHasMoreJobs; }
|
||||
PostQueue* LockPostQueue();
|
||||
void UnlockPostQueue();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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>
|
||||
@@ -34,6 +34,8 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cstdio>
|
||||
#include <sys/stat.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
@@ -44,11 +46,14 @@
|
||||
#include "Options.h"
|
||||
#include "ServerPool.h"
|
||||
#include "ArticleDownloader.h"
|
||||
#include "DiskState.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
#include "Decoder.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
extern ServerPool* g_pServerPool;
|
||||
extern DiskState* g_pDiskState;
|
||||
|
||||
QueueCoordinator::QueueCoordinator()
|
||||
{
|
||||
@@ -57,8 +62,15 @@ QueueCoordinator::QueueCoordinator()
|
||||
m_bHasMoreJobs = true;
|
||||
m_DownloadQueue.clear();
|
||||
m_ActiveDownloads.clear();
|
||||
ResetSpeedStat();
|
||||
|
||||
Decoder::Init();
|
||||
m_iAllBytes = 0;
|
||||
m_tStartServer = 0;
|
||||
m_tStartDownload = 0;
|
||||
m_tPausedFrom = 0;
|
||||
m_bStandBy = true;
|
||||
|
||||
YDecoder::Init();
|
||||
}
|
||||
|
||||
QueueCoordinator::~QueueCoordinator()
|
||||
@@ -80,7 +92,7 @@ QueueCoordinator::~QueueCoordinator()
|
||||
}
|
||||
m_ActiveDownloads.clear();
|
||||
|
||||
Decoder::Final();
|
||||
YDecoder::Final();
|
||||
|
||||
debug("QueueCoordinator destroyed");
|
||||
}
|
||||
@@ -91,60 +103,82 @@ void QueueCoordinator::Run()
|
||||
|
||||
m_mutexDownloadQueue.Lock();
|
||||
|
||||
m_DiskState.CleanupTempDir(&m_DownloadQueue);
|
||||
|
||||
if (g_pOptions->GetServerMode() && g_pOptions->GetSaveQueue() && m_DiskState.Exists())
|
||||
if (g_pOptions->GetServerMode() && g_pOptions->GetSaveQueue() && g_pDiskState->DownloadQueueExists())
|
||||
{
|
||||
if (g_pOptions->GetReloadQueue())
|
||||
{
|
||||
m_DiskState.Load(&m_DownloadQueue);
|
||||
g_pDiskState->LoadDownloadQueue(&m_DownloadQueue);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_DiskState.Discard();
|
||||
g_pDiskState->DiscardDownloadQueue();
|
||||
}
|
||||
}
|
||||
|
||||
g_pDiskState->CleanupTempDir(&m_DownloadQueue);
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
|
||||
m_tStartServer = time(NULL);
|
||||
bool bWasStandBy = true;
|
||||
bool bArticeDownloadsRunning = false;
|
||||
int iResetCounter = 0;
|
||||
|
||||
while (!IsStopped())
|
||||
{
|
||||
while (g_pOptions->GetPause() && !IsStopped())
|
||||
if (!g_pOptions->GetPause())
|
||||
{
|
||||
// Sleep for a while
|
||||
usleep(500 * 1000);
|
||||
}
|
||||
|
||||
if (g_pServerPool->HasFreeConnection())
|
||||
{
|
||||
// start download for next article
|
||||
FileInfo* pFileInfo;
|
||||
ArticleInfo* pArticleInfo;
|
||||
|
||||
m_mutexDownloadQueue.Lock();
|
||||
bool bHasMoreArticles = GetNextArticle(pFileInfo, pArticleInfo);
|
||||
m_bHasMoreJobs = bHasMoreArticles || !m_ActiveDownloads.empty();
|
||||
if (bHasMoreArticles && !IsStopped())
|
||||
NNTPConnection* pConnection = g_pServerPool->GetConnection(0, false);
|
||||
if (pConnection)
|
||||
{
|
||||
StartArticleDownload(pFileInfo, pArticleInfo);
|
||||
}
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
// start download for next article
|
||||
FileInfo* pFileInfo;
|
||||
ArticleInfo* pArticleInfo;
|
||||
|
||||
if (!IsStopped())
|
||||
{
|
||||
// two possibilities:
|
||||
// 1) hasMoreArticles==false: there are no jobs, waiting for a while
|
||||
// 2) hasMoreArticles==true: the pause prevents starting of many threads, before the download-thread locks the connection
|
||||
usleep(100 * 1000);
|
||||
m_mutexDownloadQueue.Lock();
|
||||
bool bHasMoreArticles = GetNextArticle(pFileInfo, pArticleInfo);
|
||||
bArticeDownloadsRunning = !m_ActiveDownloads.empty();
|
||||
m_bHasMoreJobs = bHasMoreArticles || bArticeDownloadsRunning;
|
||||
if (bHasMoreArticles && !IsStopped() && Thread::GetThreadCount() < g_pOptions->GetThreadLimit())
|
||||
{
|
||||
StartArticleDownload(pFileInfo, pArticleInfo, pConnection);
|
||||
bArticeDownloadsRunning = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_pServerPool->FreeConnection(pConnection, false);
|
||||
}
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// there are no free connection available, waiting for a while
|
||||
usleep(100 * 1000);
|
||||
m_mutexDownloadQueue.Lock();
|
||||
bArticeDownloadsRunning = !m_ActiveDownloads.empty();
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
}
|
||||
|
||||
ResetHangingDownloads();
|
||||
bool bStandBy = !bArticeDownloadsRunning;
|
||||
if (bStandBy ^ bWasStandBy)
|
||||
{
|
||||
EnterLeaveStandBy(bStandBy);
|
||||
bWasStandBy = bStandBy;
|
||||
}
|
||||
|
||||
// sleep longer in StandBy
|
||||
int iSleepInterval = bStandBy ? 100 : 5;
|
||||
usleep(iSleepInterval * 1000);
|
||||
|
||||
AddSpeedReading(0);
|
||||
|
||||
iResetCounter+= iSleepInterval;
|
||||
if (iResetCounter >= 1000)
|
||||
{
|
||||
// this code should not be called too often, once per second is OK
|
||||
g_pServerPool->CloseUnusedConnections();
|
||||
ResetHangingDownloads();
|
||||
iResetCounter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// waiting for downloads
|
||||
@@ -170,34 +204,76 @@ void QueueCoordinator::AddNZBFileToQueue(NZBFile* pNZBFile, bool bAddFirst)
|
||||
m_mutexDownloadQueue.Lock();
|
||||
|
||||
DownloadQueue tmpDownloadQueue;
|
||||
tmpDownloadQueue.clear();
|
||||
DownloadQueue DupeList;
|
||||
DupeList.clear();
|
||||
|
||||
int index1 = 0;
|
||||
for (NZBFile::FileInfos::iterator it = pNZBFile->GetFileInfos()->begin(); it != pNZBFile->GetFileInfos()->end(); it++)
|
||||
{
|
||||
index1++;
|
||||
FileInfo* pFileInfo = *it;
|
||||
pFileInfo->BuildDestDirName(pNZBFile->GetFileName());
|
||||
if (g_pOptions->GetDupeCheck() && IsDupe(pFileInfo))
|
||||
|
||||
if (g_pOptions->GetDupeCheck())
|
||||
{
|
||||
warn("File \"%s\" seems to be duplicate, skipping", pFileInfo->GetFilename());
|
||||
bool dupe = false;
|
||||
if (IsDupe(pFileInfo))
|
||||
{
|
||||
warn("File \"%s\" seems to be duplicate, skipping", pFileInfo->GetFilename());
|
||||
dupe = true;
|
||||
}
|
||||
int index2 = 0;
|
||||
for (NZBFile::FileInfos::iterator it2 = pNZBFile->GetFileInfos()->begin(); it2 != pNZBFile->GetFileInfos()->end(); it2++)
|
||||
{
|
||||
index2++;
|
||||
FileInfo* pFileInfo2 = *it2;
|
||||
if (pFileInfo != pFileInfo2 &&
|
||||
!strcmp(pFileInfo->GetFilename(), pFileInfo2->GetFilename()) &&
|
||||
(pFileInfo->GetSize() < pFileInfo2->GetSize() ||
|
||||
(pFileInfo->GetSize() == pFileInfo2->GetSize() && index2 < index1)))
|
||||
{
|
||||
warn("File \"%s\" appears twice in nzb-request, adding only the biggest file", pFileInfo->GetFilename());
|
||||
dupe = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (dupe)
|
||||
{
|
||||
DupeList.push_back(pFileInfo);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (bAddFirst)
|
||||
{
|
||||
tmpDownloadQueue.push_front(pFileInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (bAddFirst)
|
||||
{
|
||||
tmpDownloadQueue.push_front(pFileInfo);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_DownloadQueue.push_back(pFileInfo);
|
||||
}
|
||||
tmpDownloadQueue.push_back(pFileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
if (bAddFirst)
|
||||
for (DownloadQueue::iterator it = tmpDownloadQueue.begin(); it != tmpDownloadQueue.end(); it++)
|
||||
{
|
||||
for (DownloadQueue::iterator it = tmpDownloadQueue.begin(); it != tmpDownloadQueue.end(); it++)
|
||||
if (bAddFirst)
|
||||
{
|
||||
m_DownloadQueue.push_front(*it);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_DownloadQueue.push_back(*it);
|
||||
}
|
||||
}
|
||||
|
||||
for (DownloadQueue::iterator it = DupeList.begin(); it != DupeList.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->DiscardFile(NULL, pFileInfo);
|
||||
}
|
||||
delete pFileInfo;
|
||||
}
|
||||
|
||||
pNZBFile->DetachFileInfos();
|
||||
@@ -207,7 +283,7 @@ void QueueCoordinator::AddNZBFileToQueue(NZBFile* pNZBFile, bool bAddFirst)
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
m_DiskState.Save(&m_DownloadQueue, false);
|
||||
g_pDiskState->SaveDownloadQueue(&m_DownloadQueue);
|
||||
}
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
@@ -224,7 +300,7 @@ bool QueueCoordinator::AddFileToQueue(const char* szFileName)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Add NZBFile to Qeue
|
||||
// Add NZBFile to Queue
|
||||
AddNZBFileToQueue(pNZBFile, false);
|
||||
|
||||
delete pNZBFile;
|
||||
@@ -232,45 +308,73 @@ bool QueueCoordinator::AddFileToQueue(const char* szFileName)
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* NOTE: see note to "AddSpeedReading"
|
||||
*/
|
||||
float QueueCoordinator::CalcCurrentDownloadSpeed()
|
||||
{
|
||||
float fSpeedAllDownloads = 0;
|
||||
int iTimeDiff = (int)time(NULL) - m_iSpeedStartTime * SPEEDMETER_SLOTSIZE;
|
||||
if (iTimeDiff == 0)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
float fSpeed = m_iSpeedTotalBytes / 1024.0f / iTimeDiff;
|
||||
return fSpeed;
|
||||
}
|
||||
|
||||
m_mutexDownloadQueue.Lock();
|
||||
/*
|
||||
* NOTE: we should use mutex by access to m_iSpeedBytes and m_iSpeedBytesIndex,
|
||||
* but this would results in a big performance loss (the function
|
||||
* "AddSpeedReading" is called extremly often), so we better agree with calculation
|
||||
* errors possible because of simultaneuos access from several threads.
|
||||
* The used algorithm is able to recover after few seconds.
|
||||
* In any case the calculation errors can not result in fatal system
|
||||
* errors (segmentation faults).
|
||||
*/
|
||||
void QueueCoordinator::AddSpeedReading(int iBytes)
|
||||
{
|
||||
int iNowSlot = (int)time(NULL) / SPEEDMETER_SLOTSIZE;
|
||||
|
||||
struct _timeval curtime;
|
||||
gettimeofday(&curtime, 0);
|
||||
if (iNowSlot > m_iSpeedTime[m_iSpeedBytesIndex])
|
||||
{
|
||||
//record bytes in next slot
|
||||
m_iSpeedBytesIndex++;
|
||||
if (m_iSpeedBytesIndex >= SPEEDMETER_SLOTS)
|
||||
{
|
||||
m_iSpeedBytesIndex = 0;
|
||||
}
|
||||
//Adjust counters with outging information.
|
||||
m_iSpeedTotalBytes -= m_iSpeedBytes[m_iSpeedBytesIndex];
|
||||
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
//Note we should really use the start time of the next slot
|
||||
//but its easier to just use the outgoing slot time. This
|
||||
//will result in a small error.
|
||||
m_iSpeedStartTime = m_iSpeedTime[m_iSpeedBytesIndex];
|
||||
|
||||
//Now reset.
|
||||
m_iSpeedBytes[m_iSpeedBytesIndex] = 0;
|
||||
m_iSpeedTime[m_iSpeedBytesIndex] = iNowSlot;
|
||||
}
|
||||
|
||||
if (m_iSpeedTotalBytes == 0)
|
||||
{
|
||||
m_iSpeedStartTime = iNowSlot;
|
||||
}
|
||||
m_iSpeedBytes[m_iSpeedBytesIndex] += iBytes;
|
||||
m_iSpeedTotalBytes += iBytes;
|
||||
m_iAllBytes += iBytes;
|
||||
}
|
||||
|
||||
void QueueCoordinator::ResetSpeedStat()
|
||||
{
|
||||
m_iSpeedStartTime = (int)time(NULL) / SPEEDMETER_SLOTSIZE;
|
||||
for (int i = 0; i < SPEEDMETER_SLOTS; i++)
|
||||
{
|
||||
ArticleDownloader* pArticleDownloader = *it;
|
||||
|
||||
float fSpeed = 0.0f;
|
||||
struct _timeval* arttime = pArticleDownloader->GetStartTime();
|
||||
|
||||
#ifdef WIN32
|
||||
if (arttime->time != 0)
|
||||
#else
|
||||
if (arttime->tv_sec != 0)
|
||||
#endif
|
||||
{
|
||||
#ifdef WIN32
|
||||
float tdiff = (float)((curtime.time - arttime->time) + (curtime.millitm - arttime->millitm) / 1000.0);
|
||||
#else
|
||||
float tdiff = (float)((curtime.tv_sec - arttime->tv_sec) + (curtime.tv_usec - arttime->tv_usec) / 1000000.0);
|
||||
#endif
|
||||
if (tdiff > 0)
|
||||
{
|
||||
fSpeed = (pArticleDownloader->GetBytes() / tdiff / 1024);
|
||||
}
|
||||
}
|
||||
|
||||
fSpeedAllDownloads += fSpeed;
|
||||
m_iSpeedBytes[i] = 0;
|
||||
m_iSpeedTime[i] = m_iSpeedStartTime;
|
||||
}
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
|
||||
return fSpeedAllDownloads;
|
||||
m_iSpeedBytesIndex = 0;
|
||||
m_iSpeedTotalBytes = 0;
|
||||
}
|
||||
|
||||
long long QueueCoordinator::CalcRemainingSize()
|
||||
@@ -291,153 +395,32 @@ long long QueueCoordinator::CalcRemainingSize()
|
||||
return lRemainingSize;
|
||||
}
|
||||
|
||||
int QueueCoordinator::GetFileInfoID(unsigned int iEntry)
|
||||
{
|
||||
int ID = 0;
|
||||
m_mutexDownloadQueue.Lock();
|
||||
|
||||
if (iEntry < m_DownloadQueue.size())
|
||||
{
|
||||
ID = m_DownloadQueue[iEntry]->GetID();
|
||||
}
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
return ID;
|
||||
}
|
||||
|
||||
FileInfo* QueueCoordinator::FindFileInfo(int iID)
|
||||
{
|
||||
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (pFileInfo->GetID() == iID)
|
||||
{
|
||||
return pFileInfo;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int QueueCoordinator::GetFileInfoEntry(int iID)
|
||||
{
|
||||
int iEntry = 0;
|
||||
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (pFileInfo->GetID() == iID)
|
||||
{
|
||||
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
|
||||
* NOTE: DownloadQueue must be locked prior to call of this function
|
||||
* Returns True if Entry was deleted from Queue or False if it was scheduled for Deletion.
|
||||
* NOTE: "False" does not mean unsuccess; the entry is (or will be) deleted in any case.
|
||||
*/
|
||||
bool QueueCoordinator::EditQueuePauseUnpauseEntry(int iID, bool bPause)
|
||||
bool QueueCoordinator::DeleteQueueEntry(FileInfo* pFileInfo)
|
||||
{
|
||||
bool res = false;
|
||||
m_mutexDownloadQueue.Lock();
|
||||
|
||||
FileInfo* pFileInfo = FindFileInfo(iID);
|
||||
|
||||
if (pFileInfo)
|
||||
pFileInfo->SetDeleted(true);
|
||||
bool hasDownloads = false;
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
pFileInfo->SetPaused(bPause);
|
||||
res = true;
|
||||
}
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
m_DiskState.Save(&m_DownloadQueue, true);
|
||||
}
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes entry with index iEntry
|
||||
* returns true if successful, false if operation is not possible
|
||||
*/
|
||||
bool QueueCoordinator::EditQueueDeleteEntry(int iID)
|
||||
{
|
||||
debug("Deleting queue entry");
|
||||
|
||||
bool res = false;
|
||||
m_mutexDownloadQueue.Lock();
|
||||
|
||||
FileInfo* pFileInfo = FindFileInfo(iID);
|
||||
if (pFileInfo)
|
||||
{
|
||||
info("Deleting file %s from download queue", pFileInfo->GetFilename());
|
||||
pFileInfo->SetDeleted(true);
|
||||
bool hasDownloads = false;
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
ArticleDownloader* pArticleDownloader = *it;
|
||||
if (pArticleDownloader->GetFileInfo() == pFileInfo)
|
||||
{
|
||||
ArticleDownloader* pArticleDownloader = *it;
|
||||
if (pArticleDownloader->GetFileInfo() == pFileInfo)
|
||||
{
|
||||
hasDownloads = true;
|
||||
pArticleDownloader->Stop();
|
||||
}
|
||||
}
|
||||
if (!hasDownloads)
|
||||
{
|
||||
DeleteFileInfo(pFileInfo);
|
||||
}
|
||||
res = true;
|
||||
}
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
|
||||
debug("Queue entry deleted");
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
/*
|
||||
* Moves entry identified with iID in the queue
|
||||
* returns true if successful, false if operation is not possible
|
||||
*/
|
||||
bool QueueCoordinator::EditQueueMoveEntry(int iID, int iOffset, bool bAutoCorrection)
|
||||
{
|
||||
bool res = false;
|
||||
m_mutexDownloadQueue.Lock();
|
||||
|
||||
int iEntry = GetFileInfoEntry(iID);
|
||||
if (iEntry >= 0)
|
||||
{
|
||||
int iNewEntry = iEntry + iOffset;
|
||||
|
||||
if (bAutoCorrection && iNewEntry < 0)
|
||||
{
|
||||
iNewEntry = 0;
|
||||
}
|
||||
if (bAutoCorrection && (unsigned int)iNewEntry > m_DownloadQueue.size() - 1)
|
||||
{
|
||||
iNewEntry = (int)m_DownloadQueue.size() - 1;
|
||||
}
|
||||
|
||||
if (iNewEntry >= 0 && (unsigned int)iNewEntry <= m_DownloadQueue.size() - 1)
|
||||
{
|
||||
FileInfo* fi = m_DownloadQueue[iEntry];
|
||||
m_DownloadQueue.erase(m_DownloadQueue.begin() + iEntry);
|
||||
m_DownloadQueue.insert(m_DownloadQueue.begin() + iNewEntry, fi);
|
||||
res = true;
|
||||
hasDownloads = true;
|
||||
pArticleDownloader->Stop();
|
||||
}
|
||||
}
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
if (!hasDownloads)
|
||||
{
|
||||
m_DiskState.Save(&m_DownloadQueue, true);
|
||||
}
|
||||
Aspect aspect = { eaFileDeleted, pFileInfo, &m_DownloadQueue, NULL };
|
||||
Notify(&aspect);
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
return res;
|
||||
DeleteFileInfo(pFileInfo, false);
|
||||
}
|
||||
return hasDownloads;
|
||||
}
|
||||
|
||||
void QueueCoordinator::Stop()
|
||||
@@ -463,6 +446,10 @@ bool QueueCoordinator::GetNextArticle(FileInfo* &pFileInfo, ArticleInfo* &pArtic
|
||||
pFileInfo = *it;
|
||||
if (!pFileInfo->GetPaused() && !pFileInfo->GetDeleted())
|
||||
{
|
||||
if (pFileInfo->GetArticles()->empty() && g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->LoadArticles(pFileInfo);
|
||||
}
|
||||
for (FileInfo::Articles::iterator at = pFileInfo->GetArticles()->begin(); at != pFileInfo->GetArticles()->end(); at++)
|
||||
{
|
||||
pArticleInfo = *at;
|
||||
@@ -477,7 +464,7 @@ bool QueueCoordinator::GetNextArticle(FileInfo* &pFileInfo, ArticleInfo* &pArtic
|
||||
return false;
|
||||
}
|
||||
|
||||
void QueueCoordinator::StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pArticleInfo)
|
||||
void QueueCoordinator::StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pArticleInfo, NNTPConnection* pConnection)
|
||||
{
|
||||
debug("Starting new ArticleDownloader");
|
||||
|
||||
@@ -486,13 +473,13 @@ void QueueCoordinator::StartArticleDownload(FileInfo* pFileInfo, ArticleInfo* pA
|
||||
pArticleDownloader->Attach(this);
|
||||
pArticleDownloader->SetFileInfo(pFileInfo);
|
||||
pArticleDownloader->SetArticleInfo(pArticleInfo);
|
||||
pArticleDownloader->SetConnection(pConnection);
|
||||
BuildArticleFilename(pArticleDownloader, pFileInfo, pArticleInfo);
|
||||
|
||||
pArticleInfo->SetStatus(ArticleInfo::aiRunning);
|
||||
|
||||
m_ActiveDownloads.push_back(pArticleDownloader);
|
||||
pArticleDownloader->Start();
|
||||
pArticleDownloader->WaitInit();
|
||||
}
|
||||
|
||||
void QueueCoordinator::BuildArticleFilename(ArticleDownloader* pArticleDownloader, FileInfo* pFileInfo, ArticleInfo* pArticleInfo)
|
||||
@@ -502,18 +489,25 @@ void QueueCoordinator::BuildArticleFilename(ArticleDownloader* pArticleDownloade
|
||||
snprintf(name, 1024, "%s%i.%03i", g_pOptions->GetTempDir(), pFileInfo->GetID(), pArticleInfo->GetPartNumber());
|
||||
name[1024-1] = '\0';
|
||||
pArticleInfo->SetResultFilename(name);
|
||||
|
||||
|
||||
char tmpname[1024];
|
||||
snprintf(tmpname, 1024, "%s.tmp", name);
|
||||
tmpname[1024-1] = '\0';
|
||||
pArticleDownloader->SetTempFilename(tmpname);
|
||||
|
||||
char szNZBNiceName[1024];
|
||||
pFileInfo->GetNiceNZBName(szNZBNiceName, 1024);
|
||||
pFileInfo->GetNZBInfo()->GetNiceNZBName(szNZBNiceName, 1024);
|
||||
|
||||
snprintf(name, 1024, "%s%c%s [%i/%i]", szNZBNiceName, (int)PATH_SEPARATOR, pFileInfo->GetFilename(), pArticleInfo->GetPartNumber(), pFileInfo->GetArticles()->size());
|
||||
name[1024-1] = '\0';
|
||||
pArticleDownloader->SetInfoName(name);
|
||||
|
||||
if (g_pOptions->GetDirectWrite())
|
||||
{
|
||||
snprintf(name, 1024, "%s%i.out", g_pOptions->GetTempDir(), pFileInfo->GetID());
|
||||
name[1024-1] = '\0';
|
||||
pArticleDownloader->SetOutputFilename(name);
|
||||
}
|
||||
}
|
||||
|
||||
DownloadQueue* QueueCoordinator::LockQueue()
|
||||
@@ -562,26 +556,23 @@ void QueueCoordinator::ArticleCompleted(ArticleDownloader* pArticleDownloader)
|
||||
bool fileCompleted = (int)pFileInfo->GetArticles()->size() == pFileInfo->GetCompleted();
|
||||
|
||||
if (!pFileInfo->GetFilenameConfirmed() &&
|
||||
pArticleDownloader->GetStatus() == ArticleDownloader::adFinished &&
|
||||
pArticleDownloader->GetArticleFilename())
|
||||
pArticleDownloader->GetStatus() == ArticleDownloader::adFinished &&
|
||||
pArticleDownloader->GetArticleFilename())
|
||||
{
|
||||
pFileInfo->SetFilename(pArticleDownloader->GetArticleFilename());
|
||||
pFileInfo->SetFilenameConfirmed(true);
|
||||
if (g_pOptions->GetDupeCheck() && pFileInfo->IsDupe(pFileInfo->GetFilename()))
|
||||
{
|
||||
warn("File \"%s\" seems to be duplicate, cancelling download and deleting file from queue", pFileInfo->GetFilename());
|
||||
fileCompleted = false;
|
||||
DeleteQueueEntry(pFileInfo);
|
||||
}
|
||||
}
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
|
||||
bool deleteFileObj = false;
|
||||
|
||||
if (fileCompleted && !IsStopped() && !pFileInfo->GetDeleted())
|
||||
if (pFileInfo->GetDeleted())
|
||||
{
|
||||
// all jobs done
|
||||
pArticleDownloader->CompleteFileParts();
|
||||
deleteFileObj = true;
|
||||
}
|
||||
else if (pFileInfo->GetDeleted())
|
||||
{
|
||||
m_mutexDownloadQueue.Lock();
|
||||
int cnt = 0;
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
@@ -590,7 +581,6 @@ void QueueCoordinator::ArticleCompleted(ArticleDownloader* pArticleDownloader)
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
if (cnt == 1)
|
||||
{
|
||||
// this was the last Download for a file deleted from queue
|
||||
@@ -598,8 +588,16 @@ void QueueCoordinator::ArticleCompleted(ArticleDownloader* pArticleDownloader)
|
||||
}
|
||||
}
|
||||
|
||||
if (fileCompleted && !IsStopped() && !pFileInfo->GetDeleted())
|
||||
{
|
||||
// all jobs done
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
pArticleDownloader->CompleteFileParts();
|
||||
m_mutexDownloadQueue.Lock();
|
||||
deleteFileObj = true;
|
||||
}
|
||||
|
||||
// delete Download from Queue
|
||||
m_mutexDownloadQueue.Lock();
|
||||
for (ActiveDownloads::iterator it = m_ActiveDownloads.begin(); it != m_ActiveDownloads.end(); it++)
|
||||
{
|
||||
ArticleDownloader* pa = *it;
|
||||
@@ -611,18 +609,20 @@ void QueueCoordinator::ArticleCompleted(ArticleDownloader* pArticleDownloader)
|
||||
}
|
||||
if (deleteFileObj)
|
||||
{
|
||||
bool fileDeleted = pFileInfo->GetDeleted();
|
||||
// delete File from Queue
|
||||
pFileInfo->SetDeleted(true);
|
||||
|
||||
Aspect aspect = { fileCompleted ? eaFileCompleted : eaFileDeleted, pFileInfo, &m_DownloadQueue, NULL };
|
||||
Aspect aspect = { fileCompleted && !fileDeleted ? eaFileCompleted : eaFileDeleted, pFileInfo, &m_DownloadQueue, NULL };
|
||||
Notify(&aspect);
|
||||
|
||||
DeleteFileInfo(pFileInfo);
|
||||
DeleteFileInfo(pFileInfo, fileCompleted);
|
||||
}
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
}
|
||||
|
||||
void QueueCoordinator::DeleteFileInfo(FileInfo* pFileInfo)
|
||||
void QueueCoordinator::DeleteFileInfo(FileInfo* pFileInfo, bool bCompleted)
|
||||
{
|
||||
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
|
||||
{
|
||||
@@ -636,7 +636,32 @@ void QueueCoordinator::DeleteFileInfo(FileInfo* pFileInfo)
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
m_DiskState.DiscardFileInfo(&m_DownloadQueue, pFileInfo);
|
||||
g_pDiskState->DiscardFile(&m_DownloadQueue, pFileInfo);
|
||||
}
|
||||
|
||||
if (!bCompleted)
|
||||
{
|
||||
// deleting temporary files
|
||||
|
||||
if (!g_pOptions->GetDirectWrite() || g_pOptions->GetContinuePartial())
|
||||
{
|
||||
for (FileInfo::Articles::iterator it = pFileInfo->GetArticles()->begin(); it != pFileInfo->GetArticles()->end(); it++)
|
||||
{
|
||||
ArticleInfo* pa = *it;
|
||||
if (pa->GetResultFilename())
|
||||
{
|
||||
remove(pa->GetResultFilename());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (g_pOptions->GetDirectWrite())
|
||||
{
|
||||
char name[1024];
|
||||
snprintf(name, 1024, "%s%i.out", g_pOptions->GetTempDir(), pFileInfo->GetID());
|
||||
name[1024-1] = '\0';
|
||||
remove(name);
|
||||
}
|
||||
}
|
||||
|
||||
delete pFileInfo;
|
||||
@@ -646,16 +671,19 @@ bool QueueCoordinator::IsDupe(FileInfo* pFileInfo)
|
||||
{
|
||||
debug("Checking if the file is already queued");
|
||||
|
||||
if (pFileInfo->IsDupe())
|
||||
// checking on disk
|
||||
if (pFileInfo->IsDupe(pFileInfo->GetFilename()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// checking in queue
|
||||
for (DownloadQueue::iterator it = m_DownloadQueue.begin(); it != m_DownloadQueue.end(); it++)
|
||||
{
|
||||
FileInfo* pQueueEntry = *it;
|
||||
if (!strcmp(pFileInfo->GetDestDir(), pQueueEntry->GetDestDir()) &&
|
||||
!strcmp(pFileInfo->GetFilename(), pQueueEntry->GetFilename()))
|
||||
if (!strcmp(pFileInfo->GetNZBInfo()->GetDestDir(), pQueueEntry->GetNZBInfo()->GetDestDir()) &&
|
||||
!strcmp(pFileInfo->GetFilename(), pQueueEntry->GetFilename()) &&
|
||||
pFileInfo != pQueueEntry)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@@ -713,7 +741,7 @@ void QueueCoordinator::ResetHangingDownloads()
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not terminate hanging download %s", BaseFileName(pArticleInfo->GetResultFilename()));
|
||||
error("Could not terminate hanging download %s", Util::BaseFileName(pArticleInfo->GetResultFilename()));
|
||||
}
|
||||
m_ActiveDownloads.erase(it);
|
||||
// it's not safe to destroy pArticleDownloader, because the state of object is unknown
|
||||
@@ -726,3 +754,51 @@ void QueueCoordinator::ResetHangingDownloads()
|
||||
|
||||
m_mutexDownloadQueue.Unlock();
|
||||
}
|
||||
|
||||
void QueueCoordinator::EnterLeaveStandBy(bool bEnter)
|
||||
{
|
||||
m_mutexStat.Lock();
|
||||
m_bStandBy = bEnter;
|
||||
if (bEnter)
|
||||
{
|
||||
m_tPausedFrom = time(NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_tStartDownload == 0)
|
||||
{
|
||||
m_tStartDownload = time(NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_tStartDownload += time(NULL) - m_tPausedFrom;
|
||||
}
|
||||
m_tPausedFrom = 0;
|
||||
ResetSpeedStat();
|
||||
}
|
||||
m_mutexStat.Unlock();
|
||||
}
|
||||
|
||||
void QueueCoordinator::CalcStat(int* iUpTimeSec, int* iDnTimeSec, long long* iAllBytes, bool* bStandBy)
|
||||
{
|
||||
m_mutexStat.Lock();
|
||||
if (m_tStartServer > 0)
|
||||
{
|
||||
*iUpTimeSec = (int)(time(NULL) - m_tStartServer);
|
||||
}
|
||||
else
|
||||
{
|
||||
*iUpTimeSec = 0;
|
||||
}
|
||||
*bStandBy = m_bStandBy;
|
||||
if (m_bStandBy)
|
||||
{
|
||||
*iDnTimeSec = (int)(m_tPausedFrom - m_tStartDownload);
|
||||
}
|
||||
else
|
||||
{
|
||||
*iDnTimeSec = (int)(time(NULL) - m_tStartDownload);
|
||||
}
|
||||
*iAllBytes = m_iAllBytes;
|
||||
m_mutexStat.Unlock();
|
||||
}
|
||||
|
||||
@@ -29,25 +29,30 @@
|
||||
|
||||
#include <deque>
|
||||
#include <list>
|
||||
#include <time.h>
|
||||
#ifdef WIN32
|
||||
#include <sys/timeb.h>
|
||||
#endif
|
||||
|
||||
#include "Thread.h"
|
||||
#include "NZBFile.h"
|
||||
#include "ArticleDownloader.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Observer.h"
|
||||
#include "DiskState.h"
|
||||
#include "QueueEditor.h"
|
||||
#include "NNTPConnection.h"
|
||||
|
||||
class QueueCoordinator : public Thread, public Observer, public Subject, public DownloadSpeedMeter
|
||||
{
|
||||
public:
|
||||
typedef std::list<ArticleDownloader*> ActiveDownloads;
|
||||
typedef enum EAspectAction
|
||||
enum EAspectAction
|
||||
{
|
||||
eaNZBFileAdded,
|
||||
eaFileCompleted,
|
||||
eaFileDeleted
|
||||
};
|
||||
typedef struct Aspect
|
||||
struct Aspect
|
||||
{
|
||||
EAspectAction eAction;
|
||||
FileInfo* pFileInfo;
|
||||
@@ -58,39 +63,58 @@ public:
|
||||
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;
|
||||
|
||||
int m_iSpeedBytesIndex;
|
||||
long long m_iAllBytes;
|
||||
time_t m_tStartServer;
|
||||
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);
|
||||
|
||||
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);
|
||||
bool HasMoreJobs() { return m_bHasMoreJobs; }
|
||||
bool GetStandBy() { return m_bStandBy; }
|
||||
bool DeleteQueueEntry(FileInfo* pFileInfo);
|
||||
QueueEditor* GetQueueEditor() { return &m_QueueEditor; }
|
||||
|
||||
void LogDebugInfo();
|
||||
};
|
||||
|
||||
658
QueueEditor.cpp
Normal file
658
QueueEditor.cpp
Normal file
@@ -0,0 +1,658 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cctype>
|
||||
#include <cstdio>
|
||||
#include <sys/stat.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "QueueEditor.h"
|
||||
#include "QueueCoordinator.h"
|
||||
#include "DiskState.h"
|
||||
#include "Options.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
extern QueueCoordinator* g_pQueueCoordinator;
|
||||
extern Options* g_pOptions;
|
||||
extern DiskState* g_pDiskState;
|
||||
|
||||
const int MAX_ID = 100000000;
|
||||
|
||||
QueueEditor::EditItem::EditItem(FileInfo* pFileInfo, int iOffset)
|
||||
{
|
||||
m_pFileInfo = pFileInfo;
|
||||
m_iOffset = iOffset;
|
||||
}
|
||||
|
||||
QueueEditor::QueueEditor()
|
||||
{
|
||||
debug("Creating QueueEditor");
|
||||
}
|
||||
|
||||
QueueEditor::~QueueEditor()
|
||||
{
|
||||
debug("Destroying QueueEditor");
|
||||
}
|
||||
|
||||
FileInfo* QueueEditor::FindFileInfo(DownloadQueue* pDownloadQueue, int iID)
|
||||
{
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
if (pFileInfo->GetID() == iID)
|
||||
{
|
||||
return pFileInfo;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int QueueEditor::FindFileInfoEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo)
|
||||
{
|
||||
int iEntry = 0;
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo2 = *it;
|
||||
if (pFileInfo2 == pFileInfo)
|
||||
{
|
||||
return iEntry;
|
||||
}
|
||||
iEntry ++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Set the pause flag of the specific entry in the queue
|
||||
* returns true if successful, false if operation is not possible
|
||||
*/
|
||||
void QueueEditor::PauseUnpauseEntry(FileInfo* pFileInfo, bool bPause)
|
||||
{
|
||||
pFileInfo->SetPaused(bPause);
|
||||
}
|
||||
|
||||
/*
|
||||
* Removes entry with index iEntry
|
||||
* returns true if successful, false if operation is not possible
|
||||
*/
|
||||
void QueueEditor::DeleteEntry(FileInfo* pFileInfo)
|
||||
{
|
||||
info("Deleting file %s from download queue", pFileInfo->GetFilename());
|
||||
g_pQueueCoordinator->DeleteQueueEntry(pFileInfo);
|
||||
}
|
||||
|
||||
/*
|
||||
* Moves entry identified with iID in the queue
|
||||
* returns true if successful, false if operation is not possible
|
||||
*/
|
||||
void QueueEditor::MoveEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, int iOffset)
|
||||
{
|
||||
int iEntry = FindFileInfoEntry(pDownloadQueue, pFileInfo);
|
||||
if (iEntry > -1)
|
||||
{
|
||||
int iNewEntry = iEntry + iOffset;
|
||||
|
||||
if (iNewEntry < 0)
|
||||
{
|
||||
iNewEntry = 0;
|
||||
}
|
||||
if ((unsigned int)iNewEntry > pDownloadQueue->size() - 1)
|
||||
{
|
||||
iNewEntry = (int)pDownloadQueue->size() - 1;
|
||||
}
|
||||
|
||||
if (iNewEntry >= 0 && (unsigned int)iNewEntry <= pDownloadQueue->size() - 1)
|
||||
{
|
||||
FileInfo* fi = (*pDownloadQueue)[iEntry];
|
||||
pDownloadQueue->erase(pDownloadQueue->begin() + iEntry);
|
||||
pDownloadQueue->insert(pDownloadQueue->begin() + iNewEntry, fi);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool QueueEditor::EditEntry(int ID, bool bSmartOrder, EEditAction eAction, int iOffset)
|
||||
{
|
||||
IDList cIDList;
|
||||
cIDList.clear();
|
||||
cIDList.push_back(ID);
|
||||
return EditList(&cIDList, bSmartOrder, eAction, iOffset);
|
||||
}
|
||||
|
||||
bool QueueEditor::LockedEditEntry(DownloadQueue* pDownloadQueue, int ID, bool bSmartOrder, EEditAction eAction, int iOffset)
|
||||
{
|
||||
IDList cIDList;
|
||||
cIDList.clear();
|
||||
cIDList.push_back(ID);
|
||||
return InternEditList(pDownloadQueue, &cIDList, bSmartOrder, eAction, iOffset);
|
||||
}
|
||||
|
||||
bool QueueEditor::EditList(IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset)
|
||||
{
|
||||
DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue();
|
||||
|
||||
bool bOK = InternEditList(pDownloadQueue, pIDList, bSmartOrder, eAction, iOffset);
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState->SaveDownloadQueue(pDownloadQueue);
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->UnlockQueue();
|
||||
|
||||
return bOK;
|
||||
}
|
||||
|
||||
bool QueueEditor::InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset)
|
||||
{
|
||||
if (eAction == eaGroupMoveOffset)
|
||||
{
|
||||
AlignAffectedGroups(pDownloadQueue, pIDList, bSmartOrder, iOffset);
|
||||
}
|
||||
|
||||
ItemList cItemList;
|
||||
PrepareList(pDownloadQueue, &cItemList, pIDList, bSmartOrder, eAction, iOffset);
|
||||
|
||||
if (eAction == eaFilePauseAllPars || eAction == eaFilePauseExtraPars)
|
||||
{
|
||||
PauseParsInGroups(&cItemList, eAction == eaFilePauseExtraPars);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (ItemList::iterator it = cItemList.begin(); it != cItemList.end(); it++)
|
||||
{
|
||||
EditItem* pItem = *it;
|
||||
switch (eAction)
|
||||
{
|
||||
case eaFilePause:
|
||||
PauseUnpauseEntry(pItem->m_pFileInfo, true);
|
||||
break;
|
||||
|
||||
case eaFileResume:
|
||||
PauseUnpauseEntry(pItem->m_pFileInfo, false);
|
||||
break;
|
||||
|
||||
case eaFileMoveOffset:
|
||||
case eaFileMoveTop:
|
||||
case eaFileMoveBottom:
|
||||
MoveEntry(pDownloadQueue, pItem->m_pFileInfo, pItem->m_iOffset);
|
||||
break;
|
||||
|
||||
case eaFileDelete:
|
||||
DeleteEntry(pItem->m_pFileInfo);
|
||||
break;
|
||||
|
||||
case eaFilePauseAllPars:
|
||||
case eaFilePauseExtraPars:
|
||||
// remove compiler warning "enumeration not handled in switch"
|
||||
break;
|
||||
|
||||
case eaGroupPause:
|
||||
case eaGroupResume:
|
||||
case eaGroupDelete:
|
||||
case eaGroupMoveTop:
|
||||
case eaGroupMoveBottom:
|
||||
case eaGroupMoveOffset:
|
||||
case eaGroupPauseAllPars:
|
||||
case eaGroupPauseExtraPars:
|
||||
EditGroup(pDownloadQueue, pItem->m_pFileInfo, eAction, iOffset);
|
||||
break;
|
||||
}
|
||||
delete pItem;
|
||||
}
|
||||
}
|
||||
|
||||
return cItemList.size() > 0;
|
||||
}
|
||||
|
||||
void QueueEditor::PrepareList(DownloadQueue* pDownloadQueue, ItemList* pItemList, IDList* pIDList, bool bSmartOrder,
|
||||
EEditAction EEditAction, int iOffset)
|
||||
{
|
||||
if (EEditAction == eaFileMoveTop)
|
||||
{
|
||||
iOffset = -MAX_ID;
|
||||
}
|
||||
else if (EEditAction == eaFileMoveBottom)
|
||||
{
|
||||
iOffset = MAX_ID;
|
||||
}
|
||||
|
||||
pItemList->reserve(pIDList->size());
|
||||
if (bSmartOrder && iOffset != 0 &&
|
||||
(EEditAction == eaFileMoveOffset || EEditAction == eaFileMoveTop || EEditAction == eaFileMoveBottom))
|
||||
{
|
||||
//add IDs to list in order they currently have in download queue
|
||||
int iLastDestPos = -1;
|
||||
int iStart, iEnd, iStep;
|
||||
if (iOffset < 0)
|
||||
{
|
||||
iStart = 0;
|
||||
iEnd = pDownloadQueue->size();
|
||||
iStep = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
iStart = pDownloadQueue->size() - 1;
|
||||
iEnd = -1;
|
||||
iStep = -1;
|
||||
}
|
||||
for (int iIndex = iStart; iIndex != iEnd; iIndex += iStep)
|
||||
{
|
||||
FileInfo* pFileInfo = (*pDownloadQueue)[iIndex];
|
||||
int iID = pFileInfo->GetID();
|
||||
for (IDList::iterator it = pIDList->begin(); it != pIDList->end(); it++)
|
||||
{
|
||||
if (iID == *it)
|
||||
{
|
||||
int iWorkOffset = iOffset;
|
||||
int iDestPos = iIndex + iWorkOffset;
|
||||
if (iLastDestPos == -1)
|
||||
{
|
||||
if (iDestPos < 0)
|
||||
{
|
||||
iWorkOffset = -iIndex;
|
||||
}
|
||||
else if (iDestPos > int(pDownloadQueue->size()) - 1)
|
||||
{
|
||||
iWorkOffset = int(pDownloadQueue->size()) - 1 - iIndex;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (iWorkOffset < 0 && iDestPos <= iLastDestPos)
|
||||
{
|
||||
iWorkOffset = iLastDestPos - iIndex + 1;
|
||||
}
|
||||
else if (iWorkOffset > 0 && iDestPos >= iLastDestPos)
|
||||
{
|
||||
iWorkOffset = iLastDestPos - iIndex - 1;
|
||||
}
|
||||
}
|
||||
iLastDestPos = iIndex + iWorkOffset;
|
||||
pItemList->push_back(new EditItem(pFileInfo, iWorkOffset));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// check ID range
|
||||
int iMaxID = 0;
|
||||
int iMinID = MAX_ID;
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
int ID = pFileInfo->GetID();
|
||||
if (ID > iMaxID)
|
||||
{
|
||||
iMaxID = ID;
|
||||
}
|
||||
if (ID < iMinID)
|
||||
{
|
||||
iMinID = ID;
|
||||
}
|
||||
}
|
||||
|
||||
//add IDs to list in order they were transmitted in command
|
||||
for (IDList::iterator it = pIDList->begin(); it != pIDList->end(); it++)
|
||||
{
|
||||
int iID = *it;
|
||||
if (iMinID <= iID && iID <= iMaxID)
|
||||
{
|
||||
FileInfo* pFileInfo = FindFileInfo(pDownloadQueue, iID);
|
||||
if (pFileInfo)
|
||||
{
|
||||
pItemList->push_back(new EditItem(pFileInfo, iOffset));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool QueueEditor::EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, EEditAction eAction, int iOffset)
|
||||
{
|
||||
IDList cIDList;
|
||||
cIDList.clear();
|
||||
|
||||
// collecting files belonging to group
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo2 = *it;
|
||||
if (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;
|
||||
}
|
||||
|
||||
EEditAction GroupToFileMap[] = { (EEditAction)0, eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause, eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars,
|
||||
eaFileMoveOffset, eaFileMoveTop, eaFileMoveBottom, eaFilePause, eaFileResume, eaFileDelete, eaFilePauseAllPars, eaFilePauseExtraPars };
|
||||
|
||||
return InternEditList(pDownloadQueue, &cIDList, true, GroupToFileMap[eAction], iOffset);
|
||||
}
|
||||
|
||||
void QueueEditor::BuildGroupList(DownloadQueue* pDownloadQueue, FileList* pGroupList)
|
||||
{
|
||||
pGroupList->clear();
|
||||
for (DownloadQueue::iterator it = pDownloadQueue->begin(); it != pDownloadQueue->end(); it++)
|
||||
{
|
||||
FileInfo* pFileInfo = *it;
|
||||
FileInfo* pGroupInfo = NULL;
|
||||
for (FileList::iterator itg = pGroupList->begin(); itg != pGroupList->end(); itg++)
|
||||
{
|
||||
FileInfo* pGroupInfo1 = *itg;
|
||||
if (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);
|
||||
}
|
||||
}
|
||||
|
||||
void QueueEditor::AlignGroup(DownloadQueue* pDownloadQueue, FileInfo* pFirstFileInfo)
|
||||
{
|
||||
FileInfo* pLastFileInfo = NULL;
|
||||
unsigned int iLastNum = 0;
|
||||
unsigned int iNum = 0;
|
||||
while (iNum < pDownloadQueue->size())
|
||||
{
|
||||
FileInfo* pFileInfo = (*pDownloadQueue)[iNum];
|
||||
if (pFirstFileInfo->GetNZBInfo() == pFileInfo->GetNZBInfo())
|
||||
{
|
||||
if (pLastFileInfo && iNum - iLastNum > 1)
|
||||
{
|
||||
pDownloadQueue->erase(pDownloadQueue->begin() + iNum);
|
||||
pDownloadQueue->insert(pDownloadQueue->begin() + iLastNum + 1, pFileInfo);
|
||||
iLastNum++;
|
||||
}
|
||||
else
|
||||
{
|
||||
iLastNum = iNum;
|
||||
}
|
||||
pLastFileInfo = pFileInfo;
|
||||
}
|
||||
iNum++;
|
||||
}
|
||||
}
|
||||
|
||||
void QueueEditor::PauseParsInGroups(ItemList* pItemList, bool bExtraParsOnly)
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
FileList GroupFileList;
|
||||
GroupFileList.clear();
|
||||
FileInfo* pFirstFileInfo = NULL;
|
||||
|
||||
for (ItemList::iterator it = pItemList->begin(); it != pItemList->end(); )
|
||||
{
|
||||
EditItem* pItem = *it;
|
||||
if (!pFirstFileInfo ||
|
||||
(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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
98
QueueEditor.h
Normal file
98
QueueEditor.h
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef QUEUEEDITOR_H
|
||||
#define QUEUEEDITOR_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "DownloadInfo.h"
|
||||
|
||||
class QueueEditor
|
||||
{
|
||||
public:
|
||||
typedef std::vector<int> IDList;
|
||||
|
||||
enum EEditAction
|
||||
{
|
||||
eaFileMoveOffset = 1, // move to m_iOffset relative to the current position in queue
|
||||
eaFileMoveTop,
|
||||
eaFileMoveBottom,
|
||||
eaFilePause,
|
||||
eaFileResume,
|
||||
eaFileDelete,
|
||||
eaFilePauseAllPars,
|
||||
eaFilePauseExtraPars,
|
||||
eaGroupMoveOffset, // move to m_iOffset relative to the current position in queue
|
||||
eaGroupMoveTop,
|
||||
eaGroupMoveBottom,
|
||||
eaGroupPause,
|
||||
eaGroupResume,
|
||||
eaGroupDelete,
|
||||
eaGroupPauseAllPars,
|
||||
eaGroupPauseExtraPars
|
||||
};
|
||||
|
||||
private:
|
||||
class EditItem
|
||||
{
|
||||
public:
|
||||
int m_iOffset;
|
||||
FileInfo* m_pFileInfo;
|
||||
|
||||
EditItem(FileInfo* pFileInfo, int iOffset);
|
||||
};
|
||||
|
||||
typedef std::vector<EditItem*> ItemList;
|
||||
typedef std::vector<FileInfo*> FileList;
|
||||
|
||||
private:
|
||||
FileInfo* FindFileInfo(DownloadQueue* pDownloadQueue, int iID);
|
||||
int FindFileInfoEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo);
|
||||
bool InternEditList(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset);
|
||||
void PrepareList(DownloadQueue* pDownloadQueue, ItemList* pItemList, IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset);
|
||||
bool EditGroup(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, EEditAction eAction, int iOffset);
|
||||
void BuildGroupList(DownloadQueue* pDownloadQueue, FileList* pGroupList);
|
||||
void AlignAffectedGroups(DownloadQueue* pDownloadQueue, IDList* pIDList, bool bSmartOrder, int iOffset);
|
||||
bool ItemExists(FileList* pFileList, FileInfo* pFileInfo);
|
||||
void AlignGroup(DownloadQueue* pDownloadQueue, FileInfo* pFirstFileInfo);
|
||||
void PauseParsInGroups(ItemList* pItemList, bool bExtraParsOnly);
|
||||
void PausePars(FileList* pFileList, bool bExtraParsOnly);
|
||||
|
||||
void PauseUnpauseEntry(FileInfo* pFileInfo, bool bPause);
|
||||
void DeleteEntry(FileInfo* pFileInfo);
|
||||
void MoveEntry(DownloadQueue* pDownloadQueue, FileInfo* pFileInfo, int iOffset);
|
||||
|
||||
public:
|
||||
QueueEditor();
|
||||
~QueueEditor();
|
||||
|
||||
bool EditEntry(int ID, bool bSmartOrder, EEditAction eAction, int iOffset);
|
||||
bool EditList(IDList* pIDList, bool bSmartOrder, EEditAction eAction, int iOffset);
|
||||
|
||||
bool LockedEditEntry(DownloadQueue* pDownloadQueue, int ID, bool bSmartOrder, EEditAction eAction, int iOffset);
|
||||
};
|
||||
|
||||
#endif
|
||||
140
README
140
README
@@ -30,32 +30,36 @@ Then you use client to send requests to server. The sample requests
|
||||
are: download nzb-file, list files in queue, etc.
|
||||
|
||||
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;
|
||||
The current version (0.4.1) should run at least on:
|
||||
- Linux Debian 4.0 on x86;
|
||||
- Linux BusyBox with uClibc on MIPSEL;
|
||||
- PC-BSD 1.4 (based on FreeBSD 6.2) on x86;
|
||||
- Solaris 10 on x86;
|
||||
- Windows XP SP2 on x86.
|
||||
|
||||
The previous version (0.3.0) was also tested on:
|
||||
- Linux Debian 3.1 on x86;
|
||||
- Solaris 10 on x86;
|
||||
- Linux Debian 3.1 on SPARC (QEmu).
|
||||
|
||||
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 +69,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,22 +78,21 @@ 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 support of encoding-formats other than yEnc:
|
||||
- libuu (http://www.fpx.de/fp/Software/UUDeview)
|
||||
|
||||
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
|
||||
@@ -105,11 +107,10 @@ Well, the usual stuff:
|
||||
- configure it via
|
||||
./configure
|
||||
(maybe you have to tell configure, where to find some libraries.
|
||||
./configure --help is your friend! ;-)
|
||||
./configure --help is your friend!)
|
||||
also see "Configure-options" later.)
|
||||
- compile it via
|
||||
make
|
||||
(you may get some warnings concerning 'mktemp', simply ignore them!)
|
||||
- become root via
|
||||
su
|
||||
- install it via
|
||||
@@ -119,80 +120,26 @@ 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++.
|
||||
|
||||
--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 +148,17 @@ compile additional library. Following configure-parameters may be usefull:
|
||||
|
||||
The library libsigc++ must be installed first, since libpar2 requires it.
|
||||
|
||||
If you are not able to use libpar2 or libsigc++ or do not want them you can
|
||||
make the program without support for par-check using option
|
||||
"--disable-parcheck":
|
||||
|
||||
./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
|
||||
@@ -311,34 +263,48 @@ mode, not to daemon mode).
|
||||
|
||||
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:
|
||||
|
||||
443
RemoteClient.cpp
443
RemoteClient.cpp
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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>
|
||||
@@ -34,17 +34,20 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <cstdio>
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netinet/in.h>
|
||||
#endif
|
||||
#include <stdarg.h>
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "RemoteClient.h"
|
||||
#include "DownloadInfo.h"
|
||||
#include "Options.h"
|
||||
#include "NZBFile.h"
|
||||
#include "Log.h"
|
||||
#include "Util.h"
|
||||
|
||||
@@ -57,14 +60,14 @@ RemoteClient::RemoteClient()
|
||||
m_bVerbose = true;
|
||||
|
||||
/*
|
||||
printf("sizeof(SNZBMessageBase)=%i\n", sizeof(SNZBMessageBase));
|
||||
printf("sizeof(SNZBRequestBase)=%i\n", sizeof(SNZBRequestBase));
|
||||
printf("sizeof(SNZBDownloadRequest)=%i\n", sizeof(SNZBDownloadRequest));
|
||||
printf("sizeof(SNZBListRequest)=%i\n", sizeof(SNZBListRequest));
|
||||
printf("sizeof(SNZBListRequestAnswer)=%i\n", sizeof(SNZBListRequestAnswer));
|
||||
printf("sizeof(SNZBListRequestAnswerEntry)=%i\n", sizeof(SNZBListRequestAnswerEntry));
|
||||
printf("sizeof(SNZBListResponse)=%i\n", sizeof(SNZBListResponse));
|
||||
printf("sizeof(SNZBListResponseEntry)=%i\n", sizeof(SNZBListResponseEntry));
|
||||
printf("sizeof(SNZBLogRequest)=%i\n", sizeof(SNZBLogRequest));
|
||||
printf("sizeof(SNZBLogRequestAnswer)=%i\n", sizeof(SNZBLogRequestAnswer));
|
||||
printf("sizeof(SNZBLogRequestAnswerEntry)=%i\n", sizeof(SNZBLogRequestAnswerEntry));
|
||||
printf("sizeof(SNZBLogResponse)=%i\n", sizeof(SNZBLogResponse));
|
||||
printf("sizeof(SNZBLogResponseEntry)=%i\n", sizeof(SNZBLogResponseEntry));
|
||||
printf("sizeof(SNZBPauseUnpauseRequest)=%i\n", sizeof(SNZBPauseUnpauseRequest));
|
||||
printf("sizeof(SNZBSetDownloadRateRequest)=%i\n", sizeof(SNZBSetDownloadRateRequest));
|
||||
printf("sizeof(SNZBEditQueueRequest)=%i\n", sizeof(SNZBEditQueueRequest));
|
||||
@@ -85,7 +88,7 @@ RemoteClient::~RemoteClient()
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteClient::printf(char * msg,...)
|
||||
void RemoteClient::printf(const char * msg,...)
|
||||
{
|
||||
if (m_bVerbose)
|
||||
{
|
||||
@@ -96,7 +99,7 @@ void RemoteClient::printf(char * msg,...)
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteClient::perror(char * msg)
|
||||
void RemoteClient::perror(const char * msg)
|
||||
{
|
||||
if (m_bVerbose)
|
||||
{
|
||||
@@ -118,22 +121,58 @@ bool RemoteClient::InitConnection()
|
||||
return OK;
|
||||
}
|
||||
|
||||
void RemoteClient::InitMessageBase(SNZBMessageBase* pMessageBase, int iRequest, int iSize)
|
||||
void RemoteClient::InitMessageBase(SNZBRequestBase* pMessageBase, int iRequest, int iSize)
|
||||
{
|
||||
pMessageBase->m_iId = NZBMESSAGE_SIGNATURE;
|
||||
pMessageBase->m_iType = iRequest;
|
||||
pMessageBase->m_iSize = iSize;
|
||||
pMessageBase->m_iSignature = htonl(NZBMESSAGE_SIGNATURE);
|
||||
pMessageBase->m_iType = htonl(iRequest);
|
||||
pMessageBase->m_iStructSize = htonl(iSize);
|
||||
strncpy(pMessageBase->m_szPassword, g_pOptions->GetServerPassword(), NZBREQUESTPASSWORDSIZE - 1);
|
||||
pMessageBase->m_szPassword[NZBREQUESTPASSWORDSIZE - 1] = '\0';
|
||||
}
|
||||
|
||||
void RemoteClient::ReceiveCommandResult()
|
||||
bool RemoteClient::ReceiveBoolResponse()
|
||||
{
|
||||
char szAnswer[1024];
|
||||
strcpy(szAnswer, "N/A");
|
||||
printf("request sent\n");
|
||||
m_pConnection->Recv(szAnswer, 1024);
|
||||
printf("nzbserver returned: %s\n", szAnswer);
|
||||
printf("Request sent\n");
|
||||
|
||||
// all bool-responses have the same format of structure, we use SNZBDownloadResponse here
|
||||
SNZBDownloadResponse BoolResponse;
|
||||
memset(&BoolResponse, 0, sizeof(BoolResponse));
|
||||
|
||||
int iResponseLen = m_pConnection->Recv((char*)&BoolResponse, sizeof(BoolResponse));
|
||||
if (iResponseLen != sizeof(BoolResponse) ||
|
||||
(int)ntohl(BoolResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
|
||||
ntohl(BoolResponse.m_MessageBase.m_iStructSize) != sizeof(BoolResponse))
|
||||
{
|
||||
if (iResponseLen < 0)
|
||||
{
|
||||
printf("No response received (timeout)\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Invalid response received: either not nzbget-server or wrong server version\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int iTextLen = ntohl(BoolResponse.m_iTrailingDataLength);
|
||||
char* buf = (char*)malloc(iTextLen);
|
||||
iResponseLen = m_pConnection->Recv(buf, iTextLen);
|
||||
if (iResponseLen != iTextLen)
|
||||
{
|
||||
if (iResponseLen < 0)
|
||||
{
|
||||
printf("No response received (timeout)\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Invalid response received: either not nzbget-server or wrong server version\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("server returned: %s\n", buf);
|
||||
free(buf);
|
||||
return ntohl(BoolResponse.m_bSuccess);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -144,7 +183,7 @@ bool RemoteClient::RequestServerDownload(const char* szName, bool bAddFirst)
|
||||
// Read the file into the buffer
|
||||
char* szBuffer = NULL;
|
||||
int iLength = 0;
|
||||
if (!NZBFile::LoadFileIntoBuffer(szName, &szBuffer, &iLength))
|
||||
if (!Util::LoadFileIntoBuffer(szName, &szBuffer, &iLength))
|
||||
{
|
||||
printf("Could not load file %s\n", szName);
|
||||
return false;
|
||||
@@ -154,9 +193,9 @@ bool RemoteClient::RequestServerDownload(const char* szName, bool bAddFirst)
|
||||
if (OK)
|
||||
{
|
||||
SNZBDownloadRequest DownloadRequest;
|
||||
InitMessageBase(&DownloadRequest.m_MessageBase, NZBMessageRequest::eRequestDownload, sizeof(DownloadRequest));
|
||||
DownloadRequest.m_bAddFirst = bAddFirst;
|
||||
DownloadRequest.m_iTrailingDataLength = iLength;
|
||||
InitMessageBase(&DownloadRequest.m_MessageBase, eRemoteRequestDownload, sizeof(DownloadRequest));
|
||||
DownloadRequest.m_bAddFirst = htonl(bAddFirst);
|
||||
DownloadRequest.m_iTrailingDataLength = htonl(iLength);
|
||||
|
||||
strncpy(DownloadRequest.m_szFilename, szName, NZBREQUESTFILENAMESIZE - 1);
|
||||
DownloadRequest.m_szFilename[NZBREQUESTFILENAMESIZE-1] = '\0';
|
||||
@@ -169,7 +208,7 @@ bool RemoteClient::RequestServerDownload(const char* szName, bool bAddFirst)
|
||||
else
|
||||
{
|
||||
m_pConnection->Send(szBuffer, iLength);
|
||||
ReceiveCommandResult();
|
||||
OK = ReceiveBoolResponse();
|
||||
m_pConnection->Disconnect();
|
||||
}
|
||||
}
|
||||
@@ -188,9 +227,9 @@ bool RemoteClient::RequestServerList()
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBListRequest ListRequest;
|
||||
InitMessageBase(&ListRequest.m_MessageBase, NZBMessageRequest::eRequestList, sizeof(ListRequest));
|
||||
ListRequest.m_bFileList = true;
|
||||
ListRequest.m_bServerState = true;
|
||||
InitMessageBase(&ListRequest.m_MessageBase, eRemoteRequestList, sizeof(ListRequest));
|
||||
ListRequest.m_bFileList = htonl(true);
|
||||
ListRequest.m_bServerState = htonl(true);
|
||||
|
||||
if (m_pConnection->Send((char*)(&ListRequest), sizeof(ListRequest)) < 0)
|
||||
{
|
||||
@@ -198,18 +237,31 @@ bool RemoteClient::RequestServerList()
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("Request sent\n");
|
||||
|
||||
// Now listen for the returned list
|
||||
SNZBListRequestAnswer ListRequestAnswer;
|
||||
if (m_pConnection->Recv((char*) &ListRequestAnswer, sizeof(ListRequestAnswer)) < 0)
|
||||
SNZBListResponse ListResponse;
|
||||
int iResponseLen = m_pConnection->Recv((char*) &ListResponse, sizeof(ListResponse));
|
||||
if (iResponseLen != sizeof(ListResponse) ||
|
||||
(int)ntohl(ListResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
|
||||
ntohl(ListResponse.m_MessageBase.m_iStructSize) != sizeof(ListResponse))
|
||||
{
|
||||
if (iResponseLen < 0)
|
||||
{
|
||||
printf("No response received (timeout)\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Invalid response received: either not nzbget-server or wrong server version\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
char* pBuf = NULL;
|
||||
if (ListRequestAnswer.m_iTrailingDataLength > 0)
|
||||
if (ntohl(ListResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
pBuf = (char*)malloc(ListRequestAnswer.m_iTrailingDataLength);
|
||||
if (!m_pConnection->RecvAll(pBuf, ListRequestAnswer.m_iTrailingDataLength))
|
||||
pBuf = (char*)malloc(ntohl(ListResponse.m_iTrailingDataLength));
|
||||
if (!m_pConnection->RecvAll(pBuf, ntohl(ListResponse.m_iTrailingDataLength)))
|
||||
{
|
||||
free(pBuf);
|
||||
return false;
|
||||
@@ -218,7 +270,7 @@ bool RemoteClient::RequestServerList()
|
||||
|
||||
m_pConnection->Disconnect();
|
||||
|
||||
if (ListRequestAnswer.m_iTrailingDataLength == 0)
|
||||
if (ntohl(ListResponse.m_iTrailingDataLength) == 0)
|
||||
{
|
||||
printf("Server has no files queued for download\n");
|
||||
}
|
||||
@@ -230,66 +282,96 @@ bool RemoteClient::RequestServerList()
|
||||
long long lRemaining = 0;
|
||||
long long lPaused = 0;
|
||||
char* pBufPtr = (char*)pBuf;
|
||||
for (int i = 0; i < ListRequestAnswer.m_iNrTrailingEntries; i++)
|
||||
for (unsigned int i = 0; i < ntohl(ListResponse.m_iNrTrailingEntries); i++)
|
||||
{
|
||||
SNZBListRequestAnswerEntry* pListAnswer = (SNZBListRequestAnswerEntry*) pBufPtr;
|
||||
SNZBListResponseEntry* pListAnswer = (SNZBListResponseEntry*) pBufPtr;
|
||||
|
||||
long long lFileSize = Util::JoinInt64(ntohl(pListAnswer->m_iFileSizeHi), ntohl(pListAnswer->m_iFileSizeLo));
|
||||
long long lRemainingSize = Util::JoinInt64(ntohl(pListAnswer->m_iRemainingSizeHi), ntohl(pListAnswer->m_iRemainingSizeLo));
|
||||
|
||||
char szCompleted[100];
|
||||
szCompleted[0] = '\0';
|
||||
if (pListAnswer->m_iRemainingSize < pListAnswer->m_iFileSize)
|
||||
if (lRemainingSize < lFileSize)
|
||||
{
|
||||
sprintf(szCompleted, ", %i%s", (int)(100 - pListAnswer->m_iRemainingSize * 100.0 / pListAnswer->m_iFileSize), "\%");
|
||||
sprintf(szCompleted, ", %i%s", (int)(100 - Util::Int64ToFloat(lRemainingSize) * 100.0 / Util::Int64ToFloat(lFileSize)), "%");
|
||||
}
|
||||
char szStatus[100];
|
||||
if (pListAnswer->m_bPaused)
|
||||
if (ntohl(pListAnswer->m_bPaused))
|
||||
{
|
||||
sprintf(szStatus, " (paused)");
|
||||
lPaused += pListAnswer->m_iRemainingSize;
|
||||
lPaused += lRemainingSize;
|
||||
}
|
||||
else
|
||||
{
|
||||
szStatus[0] = '\0';
|
||||
lRemaining += pListAnswer->m_iRemainingSize;
|
||||
lRemaining += lRemainingSize;
|
||||
}
|
||||
char* szNZBFilename = pBufPtr + sizeof(SNZBListRequestAnswerEntry);
|
||||
char* szFilename = pBufPtr + sizeof(SNZBListRequestAnswerEntry) + pListAnswer->m_iNZBFilenameLen + pListAnswer->m_iSubjectLen;
|
||||
char* szNZBFilename = pBufPtr + sizeof(SNZBListResponseEntry);
|
||||
char* szFilename = pBufPtr + sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) + ntohl(pListAnswer->m_iSubjectLen);
|
||||
|
||||
char szNZBNiceName[1024];
|
||||
strncpy(szNZBNiceName, BaseFileName(szNZBFilename), 1024);
|
||||
szNZBNiceName[1024-1] = '\0';
|
||||
if (char* p = strrchr(szNZBNiceName, '.')) *p = '\0';
|
||||
NZBInfo::MakeNiceNZBName(szNZBFilename, szNZBNiceName, 1024);
|
||||
|
||||
printf("[%i] %s%c%s (%.2f MB%s)%s\n", pListAnswer->m_iID, szNZBNiceName, (int)PATH_SEPARATOR, szFilename, pListAnswer->m_iFileSize / 1024.0 / 1024.0, szCompleted, szStatus);
|
||||
printf("[%i] %s%c%s (%.2f MB%s)%s\n", ntohl(pListAnswer->m_iID), szNZBNiceName, (int)PATH_SEPARATOR, szFilename,
|
||||
(float)(Util::Int64ToFloat(lFileSize) / 1024.0 / 1024.0), szCompleted, szStatus);
|
||||
|
||||
pBufPtr += sizeof(SNZBListRequestAnswerEntry) + pListAnswer->m_iNZBFilenameLen + pListAnswer->m_iSubjectLen +
|
||||
pListAnswer->m_iFilenameLen + pListAnswer->m_iDestDirLen;
|
||||
pBufPtr += sizeof(SNZBListResponseEntry) + ntohl(pListAnswer->m_iNZBFilenameLen) +
|
||||
ntohl(pListAnswer->m_iSubjectLen) + ntohl(pListAnswer->m_iFilenameLen) + ntohl(pListAnswer->m_iDestDirLen);
|
||||
}
|
||||
|
||||
printf("-----------------------------------\n");
|
||||
printf("Files: %i\n", ListRequestAnswer.m_iNrTrailingEntries);
|
||||
printf("Files: %i\n", ntohl(ListResponse.m_iNrTrailingEntries));
|
||||
if (lPaused > 0)
|
||||
{
|
||||
printf("Remaining size: %.2f MB (+%.2f MB paused)\n", lRemaining / 1024.0 / 1024.0, lPaused / 1024.0 / 1024.0);
|
||||
printf("Remaining size: %.2f MB (+%.2f MB paused)\n", (float)(Util::Int64ToFloat(lRemaining) / 1024.0 / 1024.0),
|
||||
(float)(Util::Int64ToFloat(lPaused) / 1024.0 / 1024.0));
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Remaining size: %.2f MB\n", lRemaining / 1024.0 / 1024.0);
|
||||
printf("Remaining size: %.2f MB\n", (float)(Util::Int64ToFloat(lRemaining) / 1024.0 / 1024.0));
|
||||
}
|
||||
printf("Download rate: %.1f KB/s\n", ListRequestAnswer.m_fDownloadRate);
|
||||
printf("Current download rate: %.1f KB/s\n", (float)(ntohl(ListResponse.m_iDownloadRate) / 1024.0));
|
||||
|
||||
free(pBuf);
|
||||
}
|
||||
|
||||
printf("Threads running: %i\n", ListRequestAnswer.m_iThreadCount);
|
||||
printf("Server state: %s\n", ListRequestAnswer.m_bServerPaused ? "Paused" : "Running");
|
||||
long long iAllBytes = Util::JoinInt64(ntohl(ListResponse.m_iDownloadedBytesHi), ntohl(ListResponse.m_iDownloadedBytesLo));
|
||||
float fAverageSpeed = Util::Int64ToFloat(ntohl(ListResponse.m_iDownloadTimeSec) > 0 ? iAllBytes / ntohl(ListResponse.m_iDownloadTimeSec) : 0);
|
||||
printf("Session download rate: %.1f KB/s\n", (float)(fAverageSpeed / 1024.0));
|
||||
|
||||
if (ListRequestAnswer.m_fDownloadLimit > 0)
|
||||
if (ntohl(ListResponse.m_iDownloadLimit) > 0)
|
||||
{
|
||||
printf("Speed limit: %.1f KB/s\n", ListRequestAnswer.m_fDownloadLimit);
|
||||
printf("Speed limit: %.1f KB/s\n", (float)(ntohl(ListResponse.m_iDownloadLimit) / 1024.0));
|
||||
}
|
||||
|
||||
int sec = ntohl(ListResponse.m_iUpTimeSec);
|
||||
int h = sec / 3600;
|
||||
int m = (sec % 3600) / 60;
|
||||
int s = sec % 60;
|
||||
printf("Up time: %.2d:%.2d:%.2d\n", h, m, s);
|
||||
|
||||
sec = ntohl(ListResponse.m_iDownloadTimeSec);
|
||||
h = sec / 3600;
|
||||
m = (sec % 3600) / 60;
|
||||
s = sec % 60;
|
||||
printf("Download time: %.2d:%.2d:%.2d\n", h, m, s);
|
||||
|
||||
printf("Downloaded: %.2f MB\n", (float)(Util::Int64ToFloat(iAllBytes) / 1024.0 / 1024.0));
|
||||
|
||||
printf("Threads running: %i\n", ntohl(ListResponse.m_iThreadCount));
|
||||
|
||||
if (ntohl(ListResponse.m_iPostJobCount) > 0)
|
||||
{
|
||||
printf("Post-jobs: %i\n", (int)ntohl(ListResponse.m_iPostJobCount));
|
||||
}
|
||||
|
||||
if (ntohl(ListResponse.m_bServerStandBy))
|
||||
{
|
||||
printf("Server state: Stand-By\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Speed limit: Unlimited\n");
|
||||
printf("Server state: %s\n", ntohl(ListResponse.m_bServerPaused) ? "Paused" : "Downloading");
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -300,8 +382,8 @@ bool RemoteClient::RequestServerLog(int iLines)
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBLogRequest LogRequest;
|
||||
InitMessageBase(&LogRequest.m_MessageBase, NZBMessageRequest::eRequestLog, sizeof(LogRequest));
|
||||
LogRequest.m_iLines = iLines;
|
||||
InitMessageBase(&LogRequest.m_MessageBase, eRemoteRequestLog, sizeof(LogRequest));
|
||||
LogRequest.m_iLines = htonl(iLines);
|
||||
LogRequest.m_iIDFrom = 0;
|
||||
|
||||
if (m_pConnection->Send((char*)(&LogRequest), sizeof(LogRequest)) < 0)
|
||||
@@ -310,18 +392,31 @@ bool RemoteClient::RequestServerLog(int iLines)
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("Request sent\n");
|
||||
|
||||
// Now listen for the returned log
|
||||
SNZBLogRequestAnswer LogRequestAnswer;
|
||||
if (m_pConnection->Recv((char*) &LogRequestAnswer, sizeof(LogRequestAnswer)) < 0)
|
||||
SNZBLogResponse LogResponse;
|
||||
int iResponseLen = m_pConnection->Recv((char*) &LogResponse, sizeof(LogResponse));
|
||||
if (iResponseLen != sizeof(LogResponse) ||
|
||||
(int)ntohl(LogResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
|
||||
ntohl(LogResponse.m_MessageBase.m_iStructSize) != sizeof(LogResponse))
|
||||
{
|
||||
if (iResponseLen < 0)
|
||||
{
|
||||
printf("No response received (timeout)\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Invalid response received: either not nzbget-server or wrong server version\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
char* pBuf = NULL;
|
||||
if (LogRequestAnswer.m_iTrailingDataLength > 0)
|
||||
if (ntohl(LogResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
pBuf = (char*)malloc(LogRequestAnswer.m_iTrailingDataLength);
|
||||
if (!m_pConnection->RecvAll(pBuf, LogRequestAnswer.m_iTrailingDataLength))
|
||||
pBuf = (char*)malloc(ntohl(LogResponse.m_iTrailingDataLength));
|
||||
if (!m_pConnection->RecvAll(pBuf, ntohl(LogResponse.m_iTrailingDataLength)))
|
||||
{
|
||||
free(pBuf);
|
||||
return false;
|
||||
@@ -330,22 +425,22 @@ bool RemoteClient::RequestServerLog(int iLines)
|
||||
|
||||
m_pConnection->Disconnect();
|
||||
|
||||
if (LogRequestAnswer.m_iTrailingDataLength == 0)
|
||||
if (LogResponse.m_iTrailingDataLength == 0)
|
||||
{
|
||||
printf("Log is empty\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Log (last %i entries)\n", LogRequestAnswer.m_iNrTrailingEntries);
|
||||
printf("Log (last %i entries)\n", ntohl(LogResponse.m_iNrTrailingEntries));
|
||||
printf("-----------------------------------\n");
|
||||
|
||||
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);
|
||||
switch (pLogAnswer->m_iKind)
|
||||
char* szText = pBufPtr + sizeof(SNZBLogResponseEntry);
|
||||
switch (ntohl(pLogAnswer->m_iKind))
|
||||
{
|
||||
case Message::mkDebug:
|
||||
printf("[DEBUG] %s\n", szText);
|
||||
@@ -359,9 +454,12 @@ bool RemoteClient::RequestServerLog(int iLines)
|
||||
case Message::mkInfo:
|
||||
printf("[INFO] %s\n", szText);
|
||||
break;
|
||||
case Message::mkDetail:
|
||||
printf("[DETAIL] %s\n", szText);
|
||||
break;
|
||||
}
|
||||
|
||||
pBufPtr += sizeof(SNZBLogRequestAnswerEntry) + pLogAnswer->m_iTextLen;
|
||||
pBufPtr += sizeof(SNZBLogResponseEntry) + ntohl(pLogAnswer->m_iTextLen);
|
||||
}
|
||||
|
||||
printf("-----------------------------------\n");
|
||||
@@ -377,8 +475,8 @@ bool RemoteClient::RequestServerPauseUnpause(bool bPause)
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBPauseUnpauseRequest PauseUnpauseRequest;
|
||||
InitMessageBase(&PauseUnpauseRequest.m_MessageBase, NZBMessageRequest::eRequestPauseUnpause, sizeof(PauseUnpauseRequest));
|
||||
PauseUnpauseRequest.m_bPause = bPause;
|
||||
InitMessageBase(&PauseUnpauseRequest.m_MessageBase, eRemoteRequestPauseUnpause, sizeof(PauseUnpauseRequest));
|
||||
PauseUnpauseRequest.m_bPause = htonl(bPause);
|
||||
|
||||
if (m_pConnection->Send((char*)(&PauseUnpauseRequest), sizeof(PauseUnpauseRequest)) < 0)
|
||||
{
|
||||
@@ -387,10 +485,10 @@ bool RemoteClient::RequestServerPauseUnpause(bool bPause)
|
||||
return false;
|
||||
}
|
||||
|
||||
ReceiveCommandResult();
|
||||
bool OK = ReceiveBoolResponse();
|
||||
m_pConnection->Disconnect();
|
||||
|
||||
return true;
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerSetDownloadRate(float fRate)
|
||||
@@ -398,8 +496,8 @@ bool RemoteClient::RequestServerSetDownloadRate(float fRate)
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBSetDownloadRateRequest SetDownloadRateRequest;
|
||||
InitMessageBase(&SetDownloadRateRequest.m_MessageBase, NZBMessageRequest::eRequestSetDownloadRate, sizeof(SetDownloadRateRequest));
|
||||
SetDownloadRateRequest.m_fDownloadRate = fRate;
|
||||
InitMessageBase(&SetDownloadRateRequest.m_MessageBase, eRemoteRequestSetDownloadRate, sizeof(SetDownloadRateRequest));
|
||||
SetDownloadRateRequest.m_iDownloadRate = htonl((unsigned int)(fRate * 1024));
|
||||
|
||||
if (m_pConnection->Send((char*)(&SetDownloadRateRequest), sizeof(SetDownloadRateRequest)) < 0)
|
||||
{
|
||||
@@ -408,10 +506,10 @@ bool RemoteClient::RequestServerSetDownloadRate(float fRate)
|
||||
return false;
|
||||
}
|
||||
|
||||
ReceiveCommandResult();
|
||||
bool OK = ReceiveBoolResponse();
|
||||
m_pConnection->Disconnect();
|
||||
|
||||
return true;
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerDumpDebug()
|
||||
@@ -419,8 +517,7 @@ bool RemoteClient::RequestServerDumpDebug()
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBDumpDebugRequest DumpDebugInfo;
|
||||
InitMessageBase(&DumpDebugInfo.m_MessageBase, NZBMessageRequest::eRequestDumpDebug, sizeof(DumpDebugInfo));
|
||||
DumpDebugInfo.m_iLevel = 0;
|
||||
InitMessageBase(&DumpDebugInfo.m_MessageBase, eRemoteRequestDumpDebug, sizeof(DumpDebugInfo));
|
||||
|
||||
if (m_pConnection->Send((char*)(&DumpDebugInfo), sizeof(DumpDebugInfo)) < 0)
|
||||
{
|
||||
@@ -429,38 +526,52 @@ bool RemoteClient::RequestServerDumpDebug()
|
||||
return false;
|
||||
}
|
||||
|
||||
ReceiveCommandResult();
|
||||
bool OK = ReceiveBoolResponse();
|
||||
m_pConnection->Disconnect();
|
||||
|
||||
return true;
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerEditQueue(int iAction, int iOffset, int iIDFrom, int iIDTo)
|
||||
bool RemoteClient::RequestServerEditQueue(int iAction, int iOffset, int* pIDList, int iIDCount, bool bSmartOrder)
|
||||
{
|
||||
if (iIDTo <= 0)
|
||||
if (iIDCount <= 0 || pIDList == NULL)
|
||||
{
|
||||
printf("File(s) not specified (use option -I)\n");
|
||||
printf("File(s) not specified\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBEditQueueRequest EditQueueRequest;
|
||||
InitMessageBase(&EditQueueRequest.m_MessageBase, NZBMessageRequest::eRequestEditQueue, sizeof(EditQueueRequest));
|
||||
EditQueueRequest.m_iAction = iAction;
|
||||
EditQueueRequest.m_iOffset = iOffset;
|
||||
EditQueueRequest.m_iIDFrom = iIDFrom;
|
||||
EditQueueRequest.m_iIDTo = iIDTo;
|
||||
int iLength = sizeof(int32_t) * iIDCount;
|
||||
|
||||
bool OK = m_pConnection->Send((char*)(&EditQueueRequest), sizeof(EditQueueRequest)) >= 0;
|
||||
if (OK)
|
||||
SNZBEditQueueRequest EditQueueRequest;
|
||||
InitMessageBase(&EditQueueRequest.m_MessageBase, eRemoteRequestEditQueue, sizeof(EditQueueRequest));
|
||||
EditQueueRequest.m_iAction = htonl(iAction);
|
||||
EditQueueRequest.m_iOffset = htonl((int)iOffset);
|
||||
EditQueueRequest.m_bSmartOrder = htonl(bSmartOrder);
|
||||
EditQueueRequest.m_iNrTrailingEntries = htonl(iIDCount);
|
||||
EditQueueRequest.m_iTrailingDataLength = htonl(iLength);
|
||||
|
||||
int32_t* pIDs = (int32_t*)malloc(iLength);
|
||||
|
||||
for (int i = 0; i < iIDCount; i++)
|
||||
{
|
||||
ReceiveCommandResult();
|
||||
pIDs[i] = htonl(pIDList[i]);
|
||||
}
|
||||
else
|
||||
|
||||
bool OK = false;
|
||||
if (m_pConnection->Send((char*)(&EditQueueRequest), sizeof(EditQueueRequest)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_pConnection->Send((char*)pIDs, iLength);
|
||||
OK = ReceiveBoolResponse();
|
||||
m_pConnection->Disconnect();
|
||||
}
|
||||
|
||||
free(pIDs);
|
||||
|
||||
m_pConnection->Disconnect();
|
||||
return OK;
|
||||
@@ -470,14 +581,13 @@ bool RemoteClient::RequestServerShutdown()
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBMessageBase QuitRequest;
|
||||
SNZBShutdownRequest ShutdownRequest;
|
||||
InitMessageBase(&ShutdownRequest.m_MessageBase, eRemoteRequestShutdown, sizeof(ShutdownRequest));
|
||||
|
||||
InitMessageBase(&QuitRequest, NZBMessageRequest::eRequestShutdown, sizeof(QuitRequest));
|
||||
|
||||
bool OK = m_pConnection->Send((char*)(&QuitRequest), sizeof(QuitRequest)) >= 0;
|
||||
bool OK = m_pConnection->Send((char*)(&ShutdownRequest), sizeof(ShutdownRequest)) >= 0;
|
||||
if (OK)
|
||||
{
|
||||
ReceiveCommandResult();
|
||||
OK = ReceiveBoolResponse();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -487,3 +597,134 @@ bool RemoteClient::RequestServerShutdown()
|
||||
m_pConnection->Disconnect();
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestServerVersion()
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBVersionRequest VersionRequest;
|
||||
InitMessageBase(&VersionRequest.m_MessageBase, eRemoteRequestVersion, sizeof(VersionRequest));
|
||||
|
||||
bool OK = m_pConnection->Send((char*)(&VersionRequest), sizeof(VersionRequest)) >= 0;
|
||||
if (OK)
|
||||
{
|
||||
OK = ReceiveBoolResponse();
|
||||
}
|
||||
else
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
}
|
||||
|
||||
m_pConnection->Disconnect();
|
||||
return OK;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestPostQueue()
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBPostQueueRequest PostQueueRequest;
|
||||
InitMessageBase(&PostQueueRequest.m_MessageBase, eRemoteRequestPostQueue, sizeof(PostQueueRequest));
|
||||
|
||||
if (m_pConnection->Send((char*)(&PostQueueRequest), sizeof(PostQueueRequest)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
return false;
|
||||
}
|
||||
|
||||
printf("Request sent\n");
|
||||
|
||||
// Now listen for the returned list
|
||||
SNZBPostQueueResponse PostQueueResponse;
|
||||
int iResponseLen = m_pConnection->Recv((char*) &PostQueueResponse, sizeof(PostQueueResponse));
|
||||
if (iResponseLen != sizeof(PostQueueResponse) ||
|
||||
(int)ntohl(PostQueueResponse.m_MessageBase.m_iSignature) != (int)NZBMESSAGE_SIGNATURE ||
|
||||
ntohl(PostQueueResponse.m_MessageBase.m_iStructSize) != sizeof(PostQueueResponse))
|
||||
{
|
||||
if (iResponseLen < 0)
|
||||
{
|
||||
printf("No response received (timeout)\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Invalid response received: either not nzbget-server or wrong server version\n");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
char* pBuf = NULL;
|
||||
if (ntohl(PostQueueResponse.m_iTrailingDataLength) > 0)
|
||||
{
|
||||
pBuf = (char*)malloc(ntohl(PostQueueResponse.m_iTrailingDataLength));
|
||||
if (!m_pConnection->RecvAll(pBuf, ntohl(PostQueueResponse.m_iTrailingDataLength)))
|
||||
{
|
||||
free(pBuf);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
m_pConnection->Disconnect();
|
||||
|
||||
if (ntohl(PostQueueResponse.m_iTrailingDataLength) == 0)
|
||||
{
|
||||
printf("Server has no files queued for post-processing\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("Post-Processing List\n");
|
||||
printf("-----------------------------------\n");
|
||||
|
||||
char* pBufPtr = (char*)pBuf;
|
||||
for (unsigned int i = 0; i < ntohl(PostQueueResponse.m_iNrTrailingEntries); i++)
|
||||
{
|
||||
SNZBPostQueueResponseEntry* pPostQueueAnswer = (SNZBPostQueueResponseEntry*) pBufPtr;
|
||||
|
||||
int iStageProgress = ntohl(pPostQueueAnswer->m_iStageProgress);
|
||||
|
||||
static const int EXECUTING_SCRIPT = 5;
|
||||
char szCompleted[100];
|
||||
szCompleted[0] = '\0';
|
||||
if (iStageProgress > 0 && (int)ntohl(pPostQueueAnswer->m_iStage) != EXECUTING_SCRIPT)
|
||||
{
|
||||
sprintf(szCompleted, ", %i%s", (int)(iStageProgress / 10), "%");
|
||||
}
|
||||
|
||||
const char* szPostStageName[] = { "", ", Loading Pars", ", Verifying source files", ", Repairing", ", Verifying repaired files", ", Executing postprocess-script", "" };
|
||||
char* szInfoName = pBufPtr + sizeof(SNZBPostQueueResponseEntry) + ntohl(pPostQueueAnswer->m_iNZBFilenameLen) + ntohl(pPostQueueAnswer->m_iParFilename);
|
||||
|
||||
printf("[%i] %s%s%s\n", ntohl(pPostQueueAnswer->m_iID), szInfoName, szPostStageName[ntohl(pPostQueueAnswer->m_iStage)], szCompleted);
|
||||
|
||||
pBufPtr += sizeof(SNZBPostQueueResponseEntry) + ntohl(pPostQueueAnswer->m_iNZBFilenameLen) +
|
||||
ntohl(pPostQueueAnswer->m_iParFilename) + ntohl(pPostQueueAnswer->m_iInfoNameLen) +
|
||||
ntohl(pPostQueueAnswer->m_iDestDirLen) + ntohl(pPostQueueAnswer->m_iProgressLabelLen);
|
||||
}
|
||||
|
||||
free(pBuf);
|
||||
|
||||
printf("-----------------------------------\n");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RemoteClient::RequestWriteLog(int iKind, const char* szText)
|
||||
{
|
||||
if (!InitConnection()) return false;
|
||||
|
||||
SNZBWriteLogRequest WriteLogRequest;
|
||||
InitMessageBase(&WriteLogRequest.m_MessageBase, eRemoteRequestWriteLog, sizeof(WriteLogRequest));
|
||||
WriteLogRequest.m_iKind = htonl(iKind);
|
||||
int iLength = strlen(szText) + 1;
|
||||
WriteLogRequest.m_iTrailingDataLength = htonl(iLength);
|
||||
|
||||
if (m_pConnection->Send((char*)(&WriteLogRequest), sizeof(WriteLogRequest)) < 0)
|
||||
{
|
||||
perror("m_pConnection->Send");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_pConnection->Send(szText, iLength);
|
||||
bool OK = ReceiveBoolResponse();
|
||||
m_pConnection->Disconnect();
|
||||
return OK;
|
||||
}
|
||||
|
||||
@@ -39,10 +39,10 @@ private:
|
||||
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();
|
||||
@@ -53,9 +53,12 @@ public:
|
||||
bool RequestServerPauseUnpause(bool bPause);
|
||||
bool RequestServerSetDownloadRate(float fRate);
|
||||
bool RequestServerDumpDebug();
|
||||
bool RequestServerEditQueue(int iAction, int iOffset, int iIDFrom, int iIDTo);
|
||||
bool RequestServerEditQueue(int iAction, int iOffset, int* pIDList, int iIDCount, bool bSmartOrder);
|
||||
bool RequestServerLog(int iLines);
|
||||
bool RequestServerShutdown();
|
||||
bool RequestServerVersion();
|
||||
bool RequestPostQueue();
|
||||
bool RequestWriteLog(int iKind, const char* szText);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
515
RemoteServer.cpp
515
RemoteServer.cpp
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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>
|
||||
@@ -43,19 +43,12 @@
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "RemoteServer.h"
|
||||
#include "BinRpc.h"
|
||||
#include "XmlRpc.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
|
||||
@@ -65,49 +58,64 @@ 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");
|
||||
|
||||
if (m_pConnection)
|
||||
{
|
||||
delete m_pConnection;
|
||||
}
|
||||
delete m_pNetAddress;
|
||||
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(m_pNetAddress);
|
||||
m_pConnection->SetTimeout(g_pOptions->GetConnectionTimeout());
|
||||
m_pConnection->SetSuppressErrors(false);
|
||||
bBind = m_pConnection->Bind() == 0;
|
||||
}
|
||||
|
||||
// Accept connections and store the "new" socket value
|
||||
SOCKET iSocket = INVALID_SOCKET;
|
||||
if (bBind)
|
||||
{
|
||||
iSocket = m_pConnection->Accept();
|
||||
}
|
||||
if (!bBind || iSocket == INVALID_SOCKET)
|
||||
{
|
||||
// Remote server could not bind or accept connection, waiting 1/2 sec and try again
|
||||
if (IsStopped())
|
||||
{
|
||||
break;
|
||||
}
|
||||
// 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 +123,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)
|
||||
{
|
||||
#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))
|
||||
{
|
||||
// XML-RPC or JSON-RPC request received
|
||||
Connection con(m_iSocket, false);
|
||||
char szBuffer[1024];
|
||||
if (con.ReadLine(szBuffer, sizeof(szBuffer), NULL))
|
||||
{
|
||||
XmlRpcProcessor::EHttpMethod eHttpMethod = XmlRpcProcessor::hmGet;
|
||||
char* szUrl = szBuffer;
|
||||
if (!strncmp((char*)&iSignature, "POST", 4))
|
||||
{
|
||||
eHttpMethod = XmlRpcProcessor::hmPost;
|
||||
szUrl++;
|
||||
}
|
||||
if (char* p = strchr(szUrl, ' '))
|
||||
{
|
||||
*p = '\0';
|
||||
}
|
||||
|
||||
ProcessRequest();
|
||||
XmlRpcProcessor::ERpcProtocol eProtocol = XmlRpcProcessor::rpUndefined;
|
||||
if (!strcmp(szUrl, "/xmlrpc") || !strncmp(szUrl, "/xmlrpc/", 8))
|
||||
{
|
||||
eProtocol = XmlRpcProcessor::rpXmlRpc;
|
||||
}
|
||||
else if (!strcmp(szUrl, "/jsonrpc") || !strncmp(szUrl, "/jsonrpc/", 9))
|
||||
{
|
||||
eProtocol = XmlRpcProcessor::rpJsonRpc;
|
||||
}
|
||||
|
||||
if (eProtocol != XmlRpcProcessor::rpUndefined)
|
||||
{
|
||||
XmlRpcProcessor processor;
|
||||
processor.SetConnection(&con);
|
||||
processor.SetClientIP(ip);
|
||||
processor.SetProtocol(eProtocol);
|
||||
processor.SetHttpMethod(eHttpMethod);
|
||||
processor.SetUrl(szUrl);
|
||||
processor.Execute();
|
||||
bOK = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!bOK)
|
||||
{
|
||||
warn("Non-nzbget request received on port %i from %s", g_pOptions->GetServerPort(), ip);
|
||||
}
|
||||
|
||||
// Close the socket
|
||||
closesocket(m_iSocket);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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>
|
||||
@@ -30,9 +30,6 @@
|
||||
#include "Thread.h"
|
||||
#include "NetAddress.h"
|
||||
#include "Connection.h"
|
||||
#include "MessageBase.h"
|
||||
|
||||
static int const REQUESTBUFFERSIZE = 8192;
|
||||
|
||||
class RemoteServer : public Thread
|
||||
{
|
||||
@@ -47,23 +44,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
|
||||
|
||||
390
ScriptController.cpp
Normal file
390
ScriptController.cpp
Normal file
@@ -0,0 +1,390 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <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"
|
||||
#include "Options.h"
|
||||
|
||||
extern Options* g_pOptions;
|
||||
|
||||
void ScriptController::StartScriptJob(PostInfo* pPostInfo, const char* szScript, bool bNZBFileCompleted, bool bHasFailedParJobs)
|
||||
{
|
||||
if (!Util::FileExists(szScript))
|
||||
{
|
||||
error("Could not start post-process-script: could not find file %s", szScript);
|
||||
pPostInfo->SetStage(PostInfo::ptFinished);
|
||||
pPostInfo->SetWorking(false);
|
||||
return;
|
||||
}
|
||||
|
||||
info("Executing post-process-script for %s", pPostInfo->GetInfoName());
|
||||
|
||||
ScriptController* pScriptController = new ScriptController();
|
||||
pScriptController->m_pPostInfo = pPostInfo;
|
||||
pScriptController->m_szScript = szScript;
|
||||
pScriptController->m_bNZBFileCompleted = bNZBFileCompleted;
|
||||
pScriptController->m_bHasFailedParJobs = bHasFailedParJobs;
|
||||
pScriptController->SetAutoDestroy(false);
|
||||
|
||||
pPostInfo->SetScriptThread(pScriptController);
|
||||
|
||||
pScriptController->Start();
|
||||
}
|
||||
|
||||
void ScriptController::Run()
|
||||
{
|
||||
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';
|
||||
|
||||
int pipein;
|
||||
|
||||
#ifdef WIN32
|
||||
char szCmdLine[2048];
|
||||
snprintf(szCmdLine, 2048, "\"%s\" \"%s\" \"%s\" \"%s\" %s %s %s", m_szScript, m_pPostInfo->GetDestDir(),
|
||||
m_pPostInfo->GetNZBFilename(), m_pPostInfo->GetParFilename(), szParStatus, szCollectionCompleted, szHasFailedParJobs);
|
||||
szCmdLine[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));
|
||||
|
||||
BOOL bOK = CreateProcess(NULL, szCmdLine, NULL, NULL, TRUE, NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW, NULL, m_pPostInfo->GetDestDir(), &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 post-process-script: %s", szErrMsg);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not start post-process-script: error %i", dwErrCode);
|
||||
}
|
||||
m_pPostInfo->SetStage(PostInfo::ptFinished);
|
||||
m_pPostInfo->SetWorking(false);
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
char szDestDir[1024];
|
||||
strncpy(szDestDir, m_pPostInfo->GetDestDir(), 1024);
|
||||
szDestDir[1024-1] = '\0';
|
||||
|
||||
char szNZBFilename[1024];
|
||||
strncpy(szNZBFilename, m_pPostInfo->GetNZBFilename(), 1024);
|
||||
szNZBFilename[1024-1] = '\0';
|
||||
|
||||
char szParFilename[1024];
|
||||
strncpy(szParFilename, m_pPostInfo->GetParFilename(), 1024);
|
||||
szParFilename[1024-1] = '\0';
|
||||
|
||||
int p[2];
|
||||
int pipeout;
|
||||
|
||||
// create the pipe
|
||||
if (pipe(p))
|
||||
{
|
||||
error("Could not open pipe: errno %i", errno);
|
||||
m_pPostInfo->SetStage(PostInfo::ptFinished);
|
||||
m_pPostInfo->SetWorking(false);
|
||||
return;
|
||||
}
|
||||
|
||||
pipein = p[0];
|
||||
pipeout = p[1];
|
||||
|
||||
debug("forking");
|
||||
pid_t pid = fork();
|
||||
|
||||
if (pid == -1)
|
||||
{
|
||||
error("Could not start post-process-script: errno %i", errno);
|
||||
m_pPostInfo->SetStage(PostInfo::ptFinished);
|
||||
m_pPostInfo->SetWorking(false);
|
||||
return;
|
||||
}
|
||||
else if (pid == 0)
|
||||
{
|
||||
// here goes the second instance
|
||||
|
||||
// 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);
|
||||
|
||||
execlp(m_szScript, m_szScript, szDestDir, szNZBFilename, szParFilename,
|
||||
szParStatus, szCollectionCompleted, szHasFailedParJobs, NULL);
|
||||
fprintf(stdout, "[ERROR] Could not start post-process-script: %s", strerror(errno));
|
||||
fflush(stdout);
|
||||
_exit(-1);
|
||||
}
|
||||
|
||||
// continue the first instance
|
||||
debug("forked");
|
||||
debug("Child Process-ID: %i", (int)pid);
|
||||
|
||||
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 post-process-script");
|
||||
m_pPostInfo->SetStage(PostInfo::ptFinished);
|
||||
m_pPostInfo->SetWorking(false);
|
||||
return;
|
||||
}
|
||||
|
||||
char* buf = (char*)malloc(10240);
|
||||
|
||||
debug("Entering pipe-loop");
|
||||
while (!feof(readpipe) && !IsStopped())
|
||||
{
|
||||
if (fgets(buf, 10240, readpipe))
|
||||
{
|
||||
AddMessage(buf);
|
||||
}
|
||||
}
|
||||
debug("Exited pipe-loop");
|
||||
|
||||
free(buf);
|
||||
fclose(readpipe);
|
||||
|
||||
if (IsStopped())
|
||||
{
|
||||
warn("Interrupted post-process-script for %s", m_pPostInfo->GetInfoName());
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
WaitForSingleObject(m_hProcess, INFINITE);
|
||||
#else
|
||||
waitpid(m_hProcess, NULL, 0);
|
||||
#endif
|
||||
|
||||
if (!IsStopped())
|
||||
{
|
||||
info("Completed post-process-script for %s", m_pPostInfo->GetInfoName());
|
||||
}
|
||||
|
||||
m_pPostInfo->SetStage(PostInfo::ptFinished);
|
||||
m_pPostInfo->SetWorking(false);
|
||||
}
|
||||
|
||||
void ScriptController::AddMessage(char* szText)
|
||||
{
|
||||
debug("Adding message received from post-process-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))
|
||||
{
|
||||
info(szText + 7);
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetInfoTarget();
|
||||
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
m_pPostInfo->AppendMessage(Message::mkInfo, szText + 7);
|
||||
}
|
||||
}
|
||||
else if (!strncmp(szText, "[WARNING] ", 10))
|
||||
{
|
||||
warn(szText + 10);
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetWarningTarget();
|
||||
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
m_pPostInfo->AppendMessage(Message::mkWarning, szText + 10);
|
||||
}
|
||||
}
|
||||
else if (!strncmp(szText, "[ERROR] ", 8))
|
||||
{
|
||||
error(szText + 8);
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetErrorTarget();
|
||||
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
m_pPostInfo->AppendMessage(Message::mkError, szText + 8);
|
||||
}
|
||||
}
|
||||
else if (!strncmp(szText, "[DETAIL] ", 9))
|
||||
{
|
||||
detail(szText + 9);
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetDetailTarget();
|
||||
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
m_pPostInfo->AppendMessage(Message::mkDetail, szText + 9);
|
||||
}
|
||||
}
|
||||
else if (!strncmp(szText, "[DEBUG] ", 8))
|
||||
{
|
||||
debug(szText + 8);
|
||||
Options::EMessageTarget eMessageTarget = g_pOptions->GetDebugTarget();
|
||||
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
m_pPostInfo->AppendMessage(Message::mkDebug, szText + 8);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Options::EMessageTarget eMessageTarget = Options::mtNone;
|
||||
Message::EKind eKind = Message::mkDebug;
|
||||
switch (g_pOptions->GetPostLogKind())
|
||||
{
|
||||
case Options::plNone:
|
||||
break;
|
||||
|
||||
case Options::plDetail:
|
||||
detail("Post-Process: %s", szText);
|
||||
eMessageTarget = g_pOptions->GetDetailTarget();
|
||||
eKind = Message::mkDetail;
|
||||
break;
|
||||
|
||||
case Options::plInfo:
|
||||
info("Post-Process: %s", szText);
|
||||
eMessageTarget = g_pOptions->GetInfoTarget();
|
||||
eKind = Message::mkInfo;
|
||||
break;
|
||||
|
||||
case Options::plWarning:
|
||||
warn("Post-Process: %s", szText);
|
||||
eMessageTarget = g_pOptions->GetWarningTarget();
|
||||
eKind = Message::mkWarning;
|
||||
break;
|
||||
|
||||
case Options::plError:
|
||||
error("Post-Process: %s", szText);
|
||||
eMessageTarget = g_pOptions->GetErrorTarget();
|
||||
eKind = Message::mkError;
|
||||
break;
|
||||
|
||||
case Options::plDebug:
|
||||
debug("Post-Process: %s", szText);
|
||||
eMessageTarget = g_pOptions->GetDebugTarget();
|
||||
eKind = Message::mkDebug;
|
||||
break;
|
||||
}
|
||||
if (eMessageTarget == Options::mtScreen || eMessageTarget == Options::mtBoth)
|
||||
{
|
||||
m_pPostInfo->AppendMessage(eKind, szText);
|
||||
}
|
||||
}
|
||||
|
||||
debug("Adding message received from post-process-script - completed");
|
||||
}
|
||||
|
||||
void ScriptController::Stop()
|
||||
{
|
||||
debug("Stopping post-process-script");
|
||||
Thread::Stop();
|
||||
|
||||
#ifdef WIN32
|
||||
BOOL bOK = TerminateProcess(m_hProcess, -1);
|
||||
#else
|
||||
bool bOK = kill(m_hProcess, 9) == 0;
|
||||
#endif
|
||||
|
||||
if (bOK)
|
||||
{
|
||||
debug("Terminated post-process-script");
|
||||
}
|
||||
else
|
||||
{
|
||||
error("Could not terminate post-process-script");
|
||||
}
|
||||
|
||||
debug("Post-process-script stopped");
|
||||
}
|
||||
54
ScriptController.h
Normal file
54
ScriptController.h
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef SCRIPTCONTROLLER_H
|
||||
#define SCRIPTCONTROLLER_H
|
||||
|
||||
#include "Thread.h"
|
||||
#include "PostInfo.h"
|
||||
|
||||
class ScriptController : public Thread
|
||||
{
|
||||
private:
|
||||
PostInfo* m_pPostInfo;
|
||||
const char* m_szScript;
|
||||
bool m_bNZBFileCompleted;
|
||||
bool m_bHasFailedParJobs;
|
||||
#ifdef WIN32
|
||||
HANDLE m_hProcess;
|
||||
#else
|
||||
pid_t m_hProcess;
|
||||
#endif
|
||||
|
||||
void AddMessage(char* szText);
|
||||
|
||||
public:
|
||||
virtual void Run();
|
||||
virtual void Stop();
|
||||
static void StartScriptJob(PostInfo* pPostInfo, const char* szScript,
|
||||
bool bNZBFileCompleted, bool bHasFailedParJobs);
|
||||
};
|
||||
|
||||
#endif
|
||||
111
ServerPool.cpp
111
ServerPool.cpp
@@ -51,6 +51,14 @@
|
||||
#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");
|
||||
@@ -58,7 +66,7 @@ ServerPool::ServerPool()
|
||||
m_iMaxLevel = 0;
|
||||
m_iTimeout = 60;
|
||||
m_Servers.clear();
|
||||
m_FreeConnections.clear();
|
||||
m_Connections.clear();
|
||||
m_Semaphores.clear();
|
||||
}
|
||||
|
||||
@@ -66,8 +74,6 @@ ServerPool::~ ServerPool()
|
||||
{
|
||||
debug("Destroying ServerPool");
|
||||
|
||||
m_FreeConnections.clear();
|
||||
|
||||
for (Semaphores::iterator it = m_Semaphores.begin(); it != m_Semaphores.end(); it++)
|
||||
{
|
||||
delete *it;
|
||||
@@ -79,6 +85,12 @@ ServerPool::~ ServerPool()
|
||||
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 +114,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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,38 +137,41 @@ void ServerPool::InitConnections()
|
||||
}
|
||||
}
|
||||
|
||||
NNTPConnection* ServerPool::GetConnection(int level)
|
||||
NNTPConnection* ServerPool::GetConnection(int iLevel, bool bWait)
|
||||
{
|
||||
debug("Getting connection");
|
||||
|
||||
debug("sem_wait...");
|
||||
// decrease m_Semaphore counter or block
|
||||
bool bWaitVal = m_Semaphores[level]->Wait();
|
||||
debug("sem_wait...OK");
|
||||
bool bWaitVal = false;
|
||||
if (bWait)
|
||||
{
|
||||
debug("Getting connection (wait)");
|
||||
bWaitVal = m_Semaphores[iLevel]->Wait();
|
||||
}
|
||||
else
|
||||
{
|
||||
bWaitVal = m_Semaphores[iLevel]->TryWait();
|
||||
}
|
||||
|
||||
if (!bWaitVal)
|
||||
{
|
||||
debug("semaphore error: %i", errno);
|
||||
// signal received or wait timeout
|
||||
return NULL;
|
||||
}
|
||||
|
||||
m_mutexFree.Lock();
|
||||
m_mutexConnections.Lock();
|
||||
|
||||
NNTPConnection* pConnection = NULL;
|
||||
for (Servers::iterator it = m_FreeConnections.begin(); it != m_FreeConnections.end(); it++)
|
||||
PooledConnection* pConnection = NULL;
|
||||
for (Connections::iterator it = m_Connections.begin(); it != m_Connections.end(); it++)
|
||||
{
|
||||
NewsServer* server = *it;
|
||||
if (server->GetLevel() == level)
|
||||
PooledConnection* pConnection1 = *it;
|
||||
if (!pConnection1->GetInUse() && pConnection1->GetNewsServer()->GetLevel() == iLevel)
|
||||
{
|
||||
// free connection found, take it!
|
||||
pConnection = new NNTPConnection(server);
|
||||
pConnection->SetTimeout(m_iTimeout);
|
||||
m_FreeConnections.erase(it);
|
||||
pConnection = pConnection1;
|
||||
pConnection->SetInUse(true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
m_mutexFree.Unlock();
|
||||
m_mutexConnections.Unlock();
|
||||
|
||||
if (!pConnection)
|
||||
{
|
||||
@@ -164,22 +181,46 @@ NNTPConnection* ServerPool::GetConnection(int level)
|
||||
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_mutexConnections.Lock();
|
||||
|
||||
((PooledConnection*)pConnection)->SetInUse(false);
|
||||
if (bUsed)
|
||||
{
|
||||
((PooledConnection*)pConnection)->SetFreeTimeNow();
|
||||
}
|
||||
m_Semaphores[pConnection->GetNewsServer()->GetLevel()]->Post();
|
||||
m_mutexFree.Unlock();
|
||||
|
||||
delete pConnection;
|
||||
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->GetNewsServer()->GetHost());
|
||||
pConnection->Disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_mutexConnections.Unlock();
|
||||
}
|
||||
|
||||
void ServerPool::LogDebugInfo()
|
||||
@@ -189,12 +230,12 @@ 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());
|
||||
@@ -206,5 +247,5 @@ void ServerPool::LogDebugInfo()
|
||||
debug(" Semaphore: level=%i, value=%i", iLevel, iSemValue);
|
||||
}
|
||||
*/
|
||||
m_mutexFree.Unlock();
|
||||
m_mutexConnections.Unlock();
|
||||
}
|
||||
|
||||
27
ServerPool.h
27
ServerPool.h
@@ -28,7 +28,8 @@
|
||||
#ifndef SERVERPOOL_H
|
||||
#define SERVERPOOL_H
|
||||
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <time.h>
|
||||
|
||||
#include "Thread.h"
|
||||
#include "NewsServer.h"
|
||||
@@ -37,14 +38,28 @@
|
||||
class ServerPool
|
||||
{
|
||||
private:
|
||||
class PooledConnection : public NNTPConnection
|
||||
{
|
||||
private:
|
||||
bool m_bInUse;
|
||||
time_t m_tFreeTime;
|
||||
public:
|
||||
PooledConnection(NewsServer* server);
|
||||
bool GetInUse() { return m_bInUse; }
|
||||
void SetInUse(bool bInUse) { m_bInUse = bInUse; }
|
||||
time_t GetFreeTime() { return m_tFreeTime; }
|
||||
void SetFreeTimeNow() { m_tFreeTime = ::time(NULL); }
|
||||
};
|
||||
|
||||
typedef std::vector<NewsServer*> Servers;
|
||||
typedef std::vector<Semaphore*> Semaphores;
|
||||
typedef std::vector<PooledConnection*> Connections;
|
||||
|
||||
Servers m_Servers;
|
||||
Servers m_FreeConnections;
|
||||
Connections m_Connections;
|
||||
Semaphores m_Semaphores;
|
||||
int m_iMaxLevel;
|
||||
Mutex m_mutexFree;
|
||||
Mutex m_mutexConnections;
|
||||
int m_iTimeout;
|
||||
|
||||
public:
|
||||
@@ -54,9 +69,9 @@ public:
|
||||
void AddServer(NewsServer *s);
|
||||
void InitConnections();
|
||||
int GetMaxLevel() { return m_iMaxLevel; }
|
||||
NNTPConnection* GetConnection(int level);
|
||||
bool HasFreeConnection();
|
||||
void FreeConnection(NNTPConnection* con);
|
||||
NNTPConnection* GetConnection(int iLevel, bool bWait);
|
||||
void FreeConnection(NNTPConnection* pConnection, bool bUsed);
|
||||
void CloseUnusedConnections();
|
||||
|
||||
void LogDebugInfo();
|
||||
};
|
||||
|
||||
29
Thread.cpp
29
Thread.cpp
@@ -35,6 +35,10 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <process.h>
|
||||
#endif
|
||||
|
||||
#include "Log.h"
|
||||
#include "Thread.h"
|
||||
|
||||
@@ -63,6 +67,14 @@ void Mutex::Lock()
|
||||
{
|
||||
#ifdef WIN32
|
||||
EnterCriticalSection(&m_mutexObj);
|
||||
#ifdef DEBUG
|
||||
// CriticalSections on Windows can be locked many times from the same thread,
|
||||
// but we do not want this and must treat such situations as errors and detect them.
|
||||
if (m_mutexObj.RecursionCount > 1)
|
||||
{
|
||||
error("Internal program error: inconsistent thread-lock detected");
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
pthread_mutex_lock(&m_mutexObj);
|
||||
#endif
|
||||
@@ -123,6 +135,15 @@ bool Semaphore::Wait()
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Semaphore::TryWait()
|
||||
{
|
||||
#ifdef WIN32
|
||||
return WaitForSingleObject(m_semObj, 0) == WAIT_OBJECT_0;
|
||||
#else
|
||||
return sem_trywait(&m_semObj) == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Semaphore::TimedWait(int iMSec)
|
||||
{
|
||||
#ifdef WIN32
|
||||
@@ -195,8 +216,7 @@ void Thread::Start()
|
||||
m_mutexThread.Lock();
|
||||
|
||||
#ifdef WIN32
|
||||
DWORD ThreadId;
|
||||
m_Thread = CreateThread(NULL, 0, Thread::thread_handler, (void *) this, 0, &ThreadId);
|
||||
m_Thread = (HANDLE)_beginthread(Thread::thread_handler, 0, (void *)this);
|
||||
m_bRunning = m_Thread != NULL;
|
||||
#else
|
||||
pthread_attr_t m_Attr;
|
||||
@@ -238,7 +258,7 @@ bool Thread::Kill()
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
DWORD WINAPI Thread::thread_handler(void* pObject)
|
||||
void __cdecl Thread::thread_handler(void* pObject)
|
||||
#else
|
||||
void* Thread::thread_handler(void* pObject)
|
||||
#endif
|
||||
@@ -267,7 +287,9 @@ void* Thread::thread_handler(void* pObject)
|
||||
m_iThreadCount--;
|
||||
m_mutexThread.Unlock();
|
||||
|
||||
#ifndef WIN32
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
int Thread::GetThreadCount()
|
||||
@@ -277,4 +299,3 @@ int Thread::GetThreadCount()
|
||||
m_mutexThread.Unlock();
|
||||
return iThreadCount;
|
||||
}
|
||||
|
||||
|
||||
3
Thread.h
3
Thread.h
@@ -64,6 +64,7 @@ public:
|
||||
~Semaphore();
|
||||
void Post();
|
||||
bool Wait();
|
||||
bool TryWait();
|
||||
bool TimedWait(int iMSec);
|
||||
bool IsLocked();
|
||||
};
|
||||
@@ -84,7 +85,7 @@ private:
|
||||
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
|
||||
|
||||
838
Util.cpp
838
Util.cpp
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
@@ -27,38 +27,25 @@
|
||||
#include <config.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include "win32.h"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/stat.h>
|
||||
#include <stdio.h>
|
||||
#include <errno.h>
|
||||
#ifdef WIN32
|
||||
#include <direct.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <sys/statvfs.h>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
#include "Util.h"
|
||||
|
||||
char* BaseFileName(const char* filename)
|
||||
{
|
||||
char* p = (char*)strrchr(filename, PATH_SEPARATOR);
|
||||
char* p1 = (char*)strrchr(filename, ALT_PATH_SEPARATOR);
|
||||
if (p1)
|
||||
{
|
||||
if ((p && p < p1) || !p)
|
||||
{
|
||||
p = p1;
|
||||
}
|
||||
}
|
||||
if (p)
|
||||
{
|
||||
return p + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (char*)filename;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
// getopt for WIN32:
|
||||
@@ -109,7 +96,10 @@ int getopt(int argc, char *argv[], char *optstring)
|
||||
char *cp = strchr(optstring, c);
|
||||
|
||||
if (cp == NULL || c == ':')
|
||||
{
|
||||
fprintf(stderr, "Invalid option %c", c);
|
||||
return '?';
|
||||
}
|
||||
|
||||
cp++;
|
||||
if (*cp == ':')
|
||||
@@ -126,6 +116,7 @@ int getopt(int argc, char *argv[], char *optstring)
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "Option %c needs an argument", c);
|
||||
return '?';
|
||||
}
|
||||
}
|
||||
@@ -207,9 +198,30 @@ const char* DirBrowser::Next()
|
||||
|
||||
#endif
|
||||
|
||||
void NormalizePathSeparators(char* Path)
|
||||
char* Util::BaseFileName(const char* filename)
|
||||
{
|
||||
for (char* p = Path; *p; p++)
|
||||
char* p = (char*)strrchr(filename, PATH_SEPARATOR);
|
||||
char* p1 = (char*)strrchr(filename, ALT_PATH_SEPARATOR);
|
||||
if (p1)
|
||||
{
|
||||
if ((p && p < p1) || !p)
|
||||
{
|
||||
p = p1;
|
||||
}
|
||||
}
|
||||
if (p)
|
||||
{
|
||||
return p + 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
return (char*)filename;
|
||||
}
|
||||
}
|
||||
|
||||
void Util::NormalizePathSeparators(char* szPath)
|
||||
{
|
||||
for (char* p = szPath; *p; p++)
|
||||
{
|
||||
if (*p == ALT_PATH_SEPARATOR)
|
||||
{
|
||||
@@ -217,3 +229,777 @@ void NormalizePathSeparators(char* Path)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Util::ForceDirectories(const char* szPath)
|
||||
{
|
||||
char* szNormPath = strdup(szPath);
|
||||
NormalizePathSeparators(szNormPath);
|
||||
int iLen = strlen(szNormPath);
|
||||
if ((iLen > 0) && szNormPath[iLen-1] == PATH_SEPARATOR
|
||||
#ifdef WIN32
|
||||
&& iLen > 3
|
||||
#endif
|
||||
)
|
||||
{
|
||||
szNormPath[iLen-1] = '\0';
|
||||
}
|
||||
|
||||
struct stat buffer;
|
||||
bool bOK = !stat(szNormPath, &buffer) && S_ISDIR(buffer.st_mode);
|
||||
if (!bOK
|
||||
#ifdef WIN32
|
||||
&& strlen(szNormPath) > 2
|
||||
#endif
|
||||
)
|
||||
{
|
||||
char* szParentPath = strdup(szNormPath);
|
||||
bOK = true;
|
||||
char* p = (char*)strrchr(szParentPath, PATH_SEPARATOR);
|
||||
if (p)
|
||||
{
|
||||
#ifdef WIN32
|
||||
if (p - szParentPath == 2 && szParentPath[1] == ':' && strlen(szParentPath) > 2)
|
||||
{
|
||||
szParentPath[3] = '\0';
|
||||
}
|
||||
else
|
||||
#endif
|
||||
{
|
||||
*p = '\0';
|
||||
}
|
||||
if (strlen(szParentPath) != strlen(szPath))
|
||||
{
|
||||
bOK = ForceDirectories(szParentPath);
|
||||
}
|
||||
}
|
||||
if (bOK)
|
||||
{
|
||||
mkdir(szNormPath, S_DIRMODE);
|
||||
bOK = !stat(szNormPath, &buffer) && S_ISDIR(buffer.st_mode);
|
||||
}
|
||||
free(szParentPath);
|
||||
}
|
||||
free(szNormPath);
|
||||
return bOK;
|
||||
}
|
||||
|
||||
bool Util::LoadFileIntoBuffer(const char* szFileName, char** pBuffer, int* pBufferLength)
|
||||
{
|
||||
FILE* pFile = fopen(szFileName, "r");
|
||||
if (!pFile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// obtain file size.
|
||||
fseek(pFile , 0 , SEEK_END);
|
||||
int iSize = ftell(pFile);
|
||||
rewind(pFile);
|
||||
|
||||
// allocate memory to contain the whole file.
|
||||
*pBuffer = (char*) malloc(iSize + 1);
|
||||
if (!*pBuffer)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// copy the file into the buffer.
|
||||
fread(*pBuffer, 1, iSize, pFile);
|
||||
|
||||
fclose(pFile);
|
||||
|
||||
(*pBuffer)[iSize] = 0;
|
||||
|
||||
*pBufferLength = iSize + 1;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Util::SetFileSize(const char* szFilename, int iSize)
|
||||
{
|
||||
bool bOK = false;
|
||||
#ifdef WIN32
|
||||
FILE* pFile = fopen(szFilename, "a");
|
||||
if (pFile)
|
||||
{
|
||||
bOK = _chsize_s(pFile->_file, iSize) == 0;
|
||||
fclose(pFile);
|
||||
}
|
||||
#else
|
||||
// create file
|
||||
FILE* pFile = fopen(szFilename, "a");
|
||||
if (pFile)
|
||||
{
|
||||
fclose(pFile);
|
||||
}
|
||||
// there are no reliable function to expand file on POSIX, so we must try different approaches,
|
||||
// starting with the fastest one and hoping it will work
|
||||
// 1) set file size using function "truncate" (it is fast, if works)
|
||||
truncate(szFilename, iSize);
|
||||
// check if it worked
|
||||
pFile = fopen(szFilename, "a");
|
||||
if (pFile)
|
||||
{
|
||||
fseek(pFile, 0, SEEK_END);
|
||||
bOK = ftell(pFile) == iSize;
|
||||
if (!bOK)
|
||||
{
|
||||
// 2) truncate did not work, expanding the file by writing in it (it is slow)
|
||||
fclose(pFile);
|
||||
truncate(szFilename, 0);
|
||||
pFile = fopen(szFilename, "a");
|
||||
char c = '0';
|
||||
fwrite(&c, 1, iSize, pFile);
|
||||
bOK = ftell(pFile) == iSize;
|
||||
}
|
||||
fclose(pFile);
|
||||
}
|
||||
#endif
|
||||
return bOK;
|
||||
}
|
||||
|
||||
//replace bad chars in filename
|
||||
void Util::MakeValidFilename(char* szFilename, char cReplaceChar)
|
||||
{
|
||||
char* p = szFilename;
|
||||
while (*p)
|
||||
{
|
||||
if (strchr("\\/:*?\"><'\n\r\t", *p))
|
||||
{
|
||||
*p = cReplaceChar;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
// remove trailing dots and spaces. they are not allowed in directory names on windows,
|
||||
// but we remove them on posix also, in a case the directory is accessed from windows via samba.
|
||||
for (int iLen = strlen(szFilename); iLen > 0 && (szFilename[iLen - 1] == '.' || szFilename[iLen - 1] == ' '); iLen--)
|
||||
{
|
||||
szFilename[iLen - 1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
long long Util::JoinInt64(unsigned long Hi, unsigned long Lo)
|
||||
{
|
||||
return (((long long)Hi) << 32) + Lo;
|
||||
}
|
||||
|
||||
void Util::SplitInt64(long long Int64, unsigned long* Hi, unsigned long* Lo)
|
||||
{
|
||||
*Hi = (unsigned long)(Int64 >> 32);
|
||||
*Lo = (unsigned long)Int64;
|
||||
}
|
||||
|
||||
float Util::Int64ToFloat(long long Int64)
|
||||
{
|
||||
unsigned long Hi = (unsigned long)(Int64 >> 32);
|
||||
unsigned long Lo = (unsigned long)Int64;
|
||||
return ((unsigned long)(1 << 30)) * 4.0f * Hi + Lo;
|
||||
}
|
||||
|
||||
float Util::EqualTime(_timeval* t1, _timeval* t2)
|
||||
{
|
||||
#ifdef WIN32
|
||||
return t1->time == t2->time && t1->millitm == t2->millitm;
|
||||
#else
|
||||
return t1->tv_sec == t2->tv_sec && t1->tv_usec == t2->tv_usec;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Util::EmptyTime(_timeval* t)
|
||||
{
|
||||
#ifdef WIN32
|
||||
return t->time == 0 && t->millitm == 0;
|
||||
#else
|
||||
return t->tv_sec == 0 && t->tv_usec == 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
float Util::DiffTime(_timeval* t1, _timeval* t2)
|
||||
{
|
||||
#ifdef WIN32
|
||||
return ((t1->time - t2->time) + (t1->millitm - t2->millitm) / 1000.0f);
|
||||
#else
|
||||
return (float)((t1->tv_sec - t2->tv_sec) + (t1->tv_usec - t2->tv_usec) / 1000000.0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/* Base64 decryption is taken from
|
||||
* Article "BASE 64 Decoding and Encoding Class 2003" by Jan Raddatz
|
||||
* http://www.codeguru.com/cpp/cpp/algorithms/article.php/c5099/
|
||||
*/
|
||||
|
||||
const static char BASE64_DEALPHABET [128] =
|
||||
{
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 9
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10 - 19
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20 - 29
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 30 - 39
|
||||
0, 0, 0, 62, 0, 0, 0, 63, 52, 53, // 40 - 49
|
||||
54, 55, 56, 57, 58, 59, 60, 61, 0, 0, // 50 - 59
|
||||
0, 61, 0, 0, 0, 0, 1, 2, 3, 4, // 60 - 69
|
||||
5, 6, 7, 8, 9, 10, 11, 12, 13, 14, // 70 - 79
|
||||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, // 80 - 89
|
||||
25, 0, 0, 0, 0, 0, 0, 26, 27, 28, // 90 - 99
|
||||
29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // 100 - 109
|
||||
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, // 110 - 119
|
||||
49, 50, 51, 0, 0, 0, 0, 0 // 120 - 127
|
||||
};
|
||||
|
||||
unsigned int DecodeByteQuartet(char* szInputBuffer, char* szOutputBuffer)
|
||||
{
|
||||
unsigned int buffer = 0;
|
||||
|
||||
if (szInputBuffer[3] == '=')
|
||||
{
|
||||
if (szInputBuffer[2] == '=')
|
||||
{
|
||||
buffer = (buffer | BASE64_DEALPHABET [(int)szInputBuffer[0]]) << 6;
|
||||
buffer = (buffer | BASE64_DEALPHABET [(int)szInputBuffer[1]]) << 6;
|
||||
buffer = buffer << 14;
|
||||
|
||||
szOutputBuffer [0] = (char)(buffer >> 24);
|
||||
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer = (buffer | BASE64_DEALPHABET [(int)szInputBuffer[0]]) << 6;
|
||||
buffer = (buffer | BASE64_DEALPHABET [(int)szInputBuffer[1]]) << 6;
|
||||
buffer = (buffer | BASE64_DEALPHABET [(int)szInputBuffer[2]]) << 6;
|
||||
buffer = buffer << 8;
|
||||
|
||||
szOutputBuffer [0] = (char)(buffer >> 24);
|
||||
szOutputBuffer [1] = (char)(buffer >> 16);
|
||||
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer = (buffer | BASE64_DEALPHABET [(int)szInputBuffer[0]]) << 6;
|
||||
buffer = (buffer | BASE64_DEALPHABET [(int)szInputBuffer[1]]) << 6;
|
||||
buffer = (buffer | BASE64_DEALPHABET [(int)szInputBuffer[2]]) << 6;
|
||||
buffer = (buffer | BASE64_DEALPHABET [(int)szInputBuffer[3]]) << 6;
|
||||
buffer = buffer << 2;
|
||||
|
||||
szOutputBuffer [0] = (char)(buffer >> 24);
|
||||
szOutputBuffer [1] = (char)(buffer >> 16);
|
||||
szOutputBuffer [2] = (char)(buffer >> 8);
|
||||
|
||||
return 3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
unsigned int Util::DecodeBase64(char* szInputBuffer, int iInputBufferLength, char* szOutputBuffer)
|
||||
{
|
||||
unsigned int InputBufferIndex = 0;
|
||||
unsigned int OutputBufferIndex = 0;
|
||||
unsigned int InputBufferLength = iInputBufferLength > 0 ? iInputBufferLength : strlen(szInputBuffer);
|
||||
|
||||
char ByteQuartet [4];
|
||||
|
||||
while (InputBufferIndex < InputBufferLength)
|
||||
{
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
ByteQuartet [i] = szInputBuffer[InputBufferIndex];
|
||||
|
||||
// Ignore all characters except the ones in BASE64_ALPHABET
|
||||
if (!((ByteQuartet [i] >= 48 && ByteQuartet [i] <= 57) ||
|
||||
(ByteQuartet [i] >= 65 && ByteQuartet [i] <= 90) ||
|
||||
(ByteQuartet [i] >= 97 && ByteQuartet [i] <= 122) ||
|
||||
ByteQuartet [i] == '+' || ByteQuartet [i] == '/' || ByteQuartet [i] == '='))
|
||||
{
|
||||
// Invalid character
|
||||
i--;
|
||||
}
|
||||
|
||||
InputBufferIndex++;
|
||||
}
|
||||
|
||||
OutputBufferIndex += DecodeByteQuartet(ByteQuartet, szOutputBuffer + OutputBufferIndex);
|
||||
}
|
||||
|
||||
// OutputBufferIndex gives us the next position of the next decoded character
|
||||
// inside our output buffer and thus represents the number of decoded characters
|
||||
// in our buffer.
|
||||
return OutputBufferIndex;
|
||||
}
|
||||
|
||||
/* END - Base64
|
||||
*/
|
||||
|
||||
char* Util::XmlEncode(const char* raw)
|
||||
{
|
||||
// calculate the required outputstring-size based on number of xml-entities and their sizes
|
||||
int iReqSize = strlen(raw);
|
||||
for (const char* p = raw; *p; p++)
|
||||
{
|
||||
unsigned char ch = *p;
|
||||
switch (ch)
|
||||
{
|
||||
case '>':
|
||||
case '<':
|
||||
iReqSize += 4;
|
||||
break;
|
||||
case '&':
|
||||
iReqSize += 5;
|
||||
break;
|
||||
case '\'':
|
||||
case '\"':
|
||||
iReqSize += 6;
|
||||
break;
|
||||
default:
|
||||
if (ch >= 0x80)
|
||||
{
|
||||
iReqSize += 6;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char* result = (char*)malloc(iReqSize + 1);
|
||||
|
||||
// copy string
|
||||
char* output = result;
|
||||
for (const char* p = raw; ; p++)
|
||||
{
|
||||
unsigned char ch = *p;
|
||||
switch (ch)
|
||||
{
|
||||
case '\0':
|
||||
goto BreakLoop;
|
||||
case '<':
|
||||
strcpy(output, "<");
|
||||
output += 4;
|
||||
break;
|
||||
case '>':
|
||||
strcpy(output, ">");
|
||||
output += 4;
|
||||
break;
|
||||
case '&':
|
||||
strcpy(output, "&");
|
||||
output += 5;
|
||||
break;
|
||||
case '\'':
|
||||
strcpy(output, "'");
|
||||
output += 6;
|
||||
break;
|
||||
case '\"':
|
||||
strcpy(output, """);
|
||||
output += 6;
|
||||
break;
|
||||
default:
|
||||
if (ch >= 0x80)
|
||||
{
|
||||
sprintf(output, "&#%i;", ch);
|
||||
output += 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
*output++ = ch;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
BreakLoop:
|
||||
|
||||
*output = '\0';
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Util::XmlDecode(char* raw)
|
||||
{
|
||||
char* output = raw;
|
||||
for (char* p = raw;;)
|
||||
{
|
||||
switch (*p)
|
||||
{
|
||||
case '\0':
|
||||
goto BreakLoop;
|
||||
case '&':
|
||||
{
|
||||
p++;
|
||||
if (!strncmp(p, "lt;", 3))
|
||||
{
|
||||
*output++ = '<';
|
||||
p += 3;
|
||||
}
|
||||
else if (!strncmp(p, "gt;", 3))
|
||||
{
|
||||
*output++ = '>';
|
||||
p += 3;
|
||||
}
|
||||
else if (!strncmp(p, "amp;", 4))
|
||||
{
|
||||
*output++ = '&';
|
||||
p += 4;
|
||||
}
|
||||
else if (!strncmp(p, "apos;", 5))
|
||||
{
|
||||
*output++ = '\'';
|
||||
p += 5;
|
||||
}
|
||||
else if (!strncmp(p, "quot;", 5))
|
||||
{
|
||||
*output++ = '\"';
|
||||
p += 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
// unknown entity
|
||||
*output++ = *(p-1);
|
||||
p++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
*output++ = *p++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
BreakLoop:
|
||||
|
||||
*output = '\0';
|
||||
}
|
||||
|
||||
const char* Util::XmlFindTag(const char* szXml, const char* szTag, int* pValueLength)
|
||||
{
|
||||
char szOpenTag[100];
|
||||
snprintf(szOpenTag, 100, "<%s>", szTag);
|
||||
szOpenTag[100-1] = '\0';
|
||||
|
||||
char szCloseTag[100];
|
||||
snprintf(szCloseTag, 100, "</%s>", szTag);
|
||||
szCloseTag[100-1] = '\0';
|
||||
|
||||
const char* pstart = strstr(szXml, szOpenTag);
|
||||
if (!pstart) return NULL;
|
||||
|
||||
const char* pend = strstr(pstart, szCloseTag);
|
||||
if (!pend) return NULL;
|
||||
|
||||
int iTagLen = strlen(szOpenTag);
|
||||
*pValueLength = (int)(pend - pstart - iTagLen);
|
||||
|
||||
return pstart + iTagLen;
|
||||
}
|
||||
|
||||
bool Util::XmlParseTagValue(const char* szXml, const char* szTag, char* szValueBuf, int iValueBufSize, const char** pTagEnd)
|
||||
{
|
||||
int iValueLen = 0;
|
||||
const char* szValue = XmlFindTag(szXml, szTag, &iValueLen);
|
||||
if (!szValue)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
int iLen = iValueLen < iValueBufSize ? iValueLen : iValueBufSize - 1;
|
||||
strncpy(szValueBuf, szValue, iLen);
|
||||
szValueBuf[iLen] = '\0';
|
||||
if (pTagEnd)
|
||||
{
|
||||
*pTagEnd = szValue + iValueLen;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Util::MoveFile(const char* szSrcFilename, const char* szDstFilename)
|
||||
{
|
||||
bool bOK = rename(szSrcFilename, szDstFilename) == 0;
|
||||
|
||||
#ifndef WIN32
|
||||
if (!bOK && (errno == EXDEV))
|
||||
{
|
||||
FILE* infile = fopen(szSrcFilename, "r");
|
||||
if (!infile)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
FILE* outfile = fopen(szDstFilename, "w+");
|
||||
if (!outfile)
|
||||
{
|
||||
fclose(infile);
|
||||
return false;
|
||||
}
|
||||
|
||||
static const int BUFFER_SIZE = 1024 * 50;
|
||||
char* buffer = (char*)malloc(BUFFER_SIZE);
|
||||
|
||||
int cnt = BUFFER_SIZE;
|
||||
while (cnt == BUFFER_SIZE)
|
||||
{
|
||||
cnt = (int)fread(buffer, 1, BUFFER_SIZE, infile);
|
||||
fwrite(buffer, 1, cnt, outfile);
|
||||
}
|
||||
|
||||
fclose(infile);
|
||||
fclose(outfile);
|
||||
free(buffer);
|
||||
|
||||
bOK = remove(szSrcFilename) == 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
return bOK;
|
||||
}
|
||||
|
||||
bool Util::FileExists(const char* szFilename)
|
||||
{
|
||||
struct stat buffer;
|
||||
bool bExists = !stat(szFilename, &buffer) && S_ISREG(buffer.st_mode);
|
||||
return bExists;
|
||||
}
|
||||
|
||||
bool Util::DirectoryExists(const char* szDirFilename)
|
||||
{
|
||||
struct stat buffer;
|
||||
bool bExists = !stat(szDirFilename, &buffer) && S_ISDIR(buffer.st_mode);
|
||||
return bExists;
|
||||
}
|
||||
|
||||
bool Util::CreateDirectory(const char* szDirFilename)
|
||||
{
|
||||
mkdir(szDirFilename, S_DIRMODE);
|
||||
return DirectoryExists(szDirFilename);
|
||||
}
|
||||
|
||||
long long Util::FileSize(const char* szFilename)
|
||||
{
|
||||
#ifdef WIN32
|
||||
struct _stat32i64 buffer;
|
||||
_stat32i64(szFilename, &buffer);
|
||||
#else
|
||||
#ifdef HAVE_STAT64
|
||||
struct stat64 buffer;
|
||||
stat64(szFilename, &buffer);
|
||||
#else
|
||||
struct stat buffer;
|
||||
stat(szFilename, &buffer);
|
||||
#endif
|
||||
#endif
|
||||
return buffer.st_size;
|
||||
}
|
||||
|
||||
char* Util::JsonEncode(const char* raw)
|
||||
{
|
||||
// calculate the required outputstring-size based on number of escape-entities and their sizes
|
||||
int iReqSize = strlen(raw);
|
||||
for (const char* p = raw; *p; p++)
|
||||
{
|
||||
unsigned char ch = *p;
|
||||
switch (ch)
|
||||
{
|
||||
case '\"':
|
||||
case '\\':
|
||||
case '/':
|
||||
case '\b':
|
||||
case '\f':
|
||||
case '\n':
|
||||
case '\r':
|
||||
case '\t':
|
||||
iReqSize += 1;
|
||||
default:
|
||||
if (ch >= 0x80)
|
||||
{
|
||||
iReqSize += 6;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char* result = (char*)malloc(iReqSize + 1);
|
||||
|
||||
// copy string
|
||||
char* output = result;
|
||||
for (const char* p = raw; ; p++)
|
||||
{
|
||||
unsigned char ch = *p;
|
||||
switch (ch)
|
||||
{
|
||||
case '\0':
|
||||
goto BreakLoop;
|
||||
case '"':
|
||||
strcpy(output, "\\\"");
|
||||
output += 2;
|
||||
break;
|
||||
case '\\':
|
||||
strcpy(output, "\\\\");
|
||||
output += 2;
|
||||
break;
|
||||
case '/':
|
||||
strcpy(output, "\\/");
|
||||
output += 2;
|
||||
break;
|
||||
case '\b':
|
||||
strcpy(output, "\\b");
|
||||
output += 2;
|
||||
break;
|
||||
case '\f':
|
||||
strcpy(output, "\\f");
|
||||
output += 2;
|
||||
break;
|
||||
case '\n':
|
||||
strcpy(output, "\\n");
|
||||
output += 2;
|
||||
break;
|
||||
case '\r':
|
||||
strcpy(output, "\\r");
|
||||
output += 2;
|
||||
break;
|
||||
case '\t':
|
||||
strcpy(output, "\\t");
|
||||
output += 2;
|
||||
break;
|
||||
default:
|
||||
if (ch >= 0x80)
|
||||
{
|
||||
sprintf(output, "\\u%04x", ch);
|
||||
output += 6;
|
||||
}
|
||||
else
|
||||
{
|
||||
*output++ = ch;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
BreakLoop:
|
||||
|
||||
*output = '\0';
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Util::JsonDecode(char* raw)
|
||||
{
|
||||
char* output = raw;
|
||||
for (char* p = raw;;)
|
||||
{
|
||||
switch (*p)
|
||||
{
|
||||
case '\0':
|
||||
goto BreakLoop;
|
||||
case '\\':
|
||||
{
|
||||
p++;
|
||||
switch (*p)
|
||||
{
|
||||
case '"':
|
||||
*output++ = '"';
|
||||
break;
|
||||
case '\\':
|
||||
*output++ = '\\';
|
||||
break;
|
||||
case '/':
|
||||
*output++ = '/';
|
||||
break;
|
||||
case 'b':
|
||||
*output++ = '\b';
|
||||
break;
|
||||
case 'f':
|
||||
*output++ = '\f';
|
||||
break;
|
||||
case 'n':
|
||||
*output++ = '\n';
|
||||
break;
|
||||
case 'r':
|
||||
*output++ = '\r';
|
||||
break;
|
||||
case 't':
|
||||
*output++ = '\t';
|
||||
break;
|
||||
case 'u':
|
||||
*output++ = (char)strtol(p + 1, NULL, 16);
|
||||
p += 4;
|
||||
break;
|
||||
default:
|
||||
// unknown escape-sequence, should never occur
|
||||
*output++ = *p;
|
||||
break;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
default:
|
||||
*output++ = *p++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
BreakLoop:
|
||||
|
||||
*output = '\0';
|
||||
}
|
||||
|
||||
const char* Util::JsonFindField(const char* szJsonText, const char* szFieldName, int* pValueLength)
|
||||
{
|
||||
char szOpenTag[100];
|
||||
snprintf(szOpenTag, 100, "\"%s\"", szFieldName);
|
||||
szOpenTag[100-1] = '\0';
|
||||
|
||||
const char* pstart = strstr(szJsonText, szOpenTag);
|
||||
if (!pstart) return NULL;
|
||||
|
||||
pstart += strlen(szOpenTag);
|
||||
|
||||
return JsonNextValue(pstart, pValueLength);
|
||||
}
|
||||
|
||||
const char* Util::JsonNextValue(const char* szJsonText, int* pValueLength)
|
||||
{
|
||||
const char* pstart = szJsonText;
|
||||
|
||||
while (*pstart && strchr(" ,[{:\r\n\t\f", *pstart)) pstart++;
|
||||
if (!*pstart) return NULL;
|
||||
|
||||
const char* pend = pstart;
|
||||
|
||||
char ch = *pend;
|
||||
bool bStr = ch == '"';
|
||||
if (bStr)
|
||||
{
|
||||
ch = *++pend;
|
||||
}
|
||||
while (ch)
|
||||
{
|
||||
if (ch == '\\')
|
||||
{
|
||||
if (!*++pend || !*++pend) return NULL;
|
||||
ch = *pend;
|
||||
}
|
||||
if (bStr && ch == '"')
|
||||
{
|
||||
pend++;
|
||||
break;
|
||||
}
|
||||
else if (!bStr && strchr(" ,]}\r\n\t\f", ch))
|
||||
{
|
||||
break;
|
||||
}
|
||||
ch = *++pend;
|
||||
}
|
||||
|
||||
*pValueLength = (int)(pend - pstart);
|
||||
return pstart;
|
||||
}
|
||||
|
||||
long long Util::FreeDiskSize(const char* szPath)
|
||||
{
|
||||
#ifdef WIN32
|
||||
ULARGE_INTEGER lFree, lDummy;
|
||||
if (GetDiskFreeSpaceEx(szPath, &lFree, &lDummy, &lDummy))
|
||||
{
|
||||
return lFree.QuadPart;
|
||||
}
|
||||
#else
|
||||
struct statvfs diskdata;
|
||||
if (!statvfs(szPath, &diskdata))
|
||||
{
|
||||
return (long long)diskdata.f_bsize * (long long)diskdata.f_bavail;
|
||||
}
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
84
Util.h
84
Util.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
@@ -29,6 +29,7 @@
|
||||
#ifdef WIN32
|
||||
#include <stdio.h>
|
||||
#include <io.h>
|
||||
#include <sys/timeb.h>
|
||||
#else
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
@@ -57,8 +58,85 @@ 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 SetFileSize(const char* szFilename, int iSize);
|
||||
static void MakeValidFilename(char* szFilename, char cReplaceChar);
|
||||
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 long long FileSize(const char* szFilename);
|
||||
static long long FreeDiskSize(const char* szPath);
|
||||
|
||||
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 float EqualTime(_timeval* t1, _timeval* t2);
|
||||
static bool EmptyTime(_timeval* t);
|
||||
static float DiffTime(_timeval* t1, _timeval* t2);
|
||||
|
||||
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);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
1444
XmlRpc.cpp
Normal file
1444
XmlRpc.cpp
Normal file
File diff suppressed because it is too large
Load Diff
215
XmlRpc.h
Normal file
215
XmlRpc.h
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
*
|
||||
* $Revision$
|
||||
* $Date$
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
#ifndef XMLRPC_H
|
||||
#define XMLRPC_H
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "Connection.h"
|
||||
|
||||
class StringBuilder
|
||||
{
|
||||
private:
|
||||
char* m_szBuffer;
|
||||
int m_iBufferSize;
|
||||
int m_iUsedSize;
|
||||
public:
|
||||
StringBuilder();
|
||||
~StringBuilder();
|
||||
void Append(const char* szStr);
|
||||
const char* GetBuffer() { return m_szBuffer; }
|
||||
};
|
||||
|
||||
class XmlCommand;
|
||||
|
||||
class XmlRpcProcessor
|
||||
{
|
||||
public:
|
||||
enum ERpcProtocol
|
||||
{
|
||||
rpUndefined,
|
||||
rpXmlRpc,
|
||||
rpJsonRpc
|
||||
};
|
||||
|
||||
enum EHttpMethod
|
||||
{
|
||||
hmPost,
|
||||
hmGet
|
||||
};
|
||||
|
||||
private:
|
||||
Connection* m_pConnection;
|
||||
const char* m_szClientIP;
|
||||
char* m_szRequest;
|
||||
ERpcProtocol m_eProtocol;
|
||||
EHttpMethod m_eHttpMethod;
|
||||
char* m_szUrl;
|
||||
|
||||
void Dispatch();
|
||||
void SendResponse(const char* szResponse, bool bFault);
|
||||
XmlCommand* CreateCommand(const char* szMethodName);
|
||||
void MutliCall();
|
||||
|
||||
public:
|
||||
XmlRpcProcessor();
|
||||
~XmlRpcProcessor();
|
||||
void Execute();
|
||||
void SetConnection(Connection* pConnection) { m_pConnection = pConnection; }
|
||||
void SetProtocol(ERpcProtocol eProtocol) { m_eProtocol = eProtocol; }
|
||||
void SetHttpMethod(EHttpMethod eHttpMethod) { m_eHttpMethod = eHttpMethod; }
|
||||
void SetUrl(const char* szUrl);
|
||||
void SetClientIP(const char* szClientIP) { m_szClientIP = szClientIP; }
|
||||
};
|
||||
|
||||
class XmlCommand
|
||||
{
|
||||
protected:
|
||||
char* m_szRequest;
|
||||
char* m_szRequestPtr;
|
||||
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() { return m_eProtocol == XmlRpcProcessor::rpJsonRpc; }
|
||||
bool NextParamAsInt(int* iValue);
|
||||
bool NextParamAsBool(bool* bValue);
|
||||
bool NextParamAsStr(char** szValueBuf);
|
||||
const char* BoolToStr(bool bValue);
|
||||
char* EncodeStr(const 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(); }
|
||||
bool GetFault() { return m_bFault; }
|
||||
};
|
||||
|
||||
class ErrorXmlCommand: public XmlCommand
|
||||
{
|
||||
private:
|
||||
int m_iErrCode;
|
||||
const char* m_szErrText;
|
||||
|
||||
public:
|
||||
ErrorXmlCommand(int iErrCode, const char* szErrText);
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class PauseXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class UnPauseXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ShutdownXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class VersionXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class DumpDebugXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class SetDownloadRateXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class StatusXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class LogXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ListFilesXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class ListGroupsXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class EditQueueXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class DownloadXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class PostQueueXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
class WriteLogXmlCommand: public XmlCommand
|
||||
{
|
||||
public:
|
||||
virtual void Execute();
|
||||
};
|
||||
|
||||
#endif
|
||||
174
aclocal.m4
vendored
174
aclocal.m4
vendored
@@ -1,7 +1,7 @@
|
||||
# generated automatically by aclocal 1.9.5 -*- Autoconf -*-
|
||||
# generated automatically by aclocal 1.10 -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
|
||||
# 2005 Free Software Foundation, Inc.
|
||||
# 2005, 2006 Free Software Foundation, Inc.
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
@@ -11,7 +11,12 @@
|
||||
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||
# PARTICULAR PURPOSE.
|
||||
|
||||
# Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc.
|
||||
m4_if(m4_PACKAGE_VERSION, [2.61],,
|
||||
[m4_fatal([this file was generated for autoconf 2.61.
|
||||
You have another version of autoconf. If you want to use that,
|
||||
you should regenerate the build system entirely.], [63])])
|
||||
|
||||
# Copyright (C) 2002, 2003, 2005, 2006 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
@@ -21,14 +26,29 @@
|
||||
# ----------------------------
|
||||
# Automake X.Y traces this macro to ensure aclocal.m4 has been
|
||||
# generated from the m4 files accompanying Automake X.Y.
|
||||
AC_DEFUN([AM_AUTOMAKE_VERSION], [am__api_version="1.9"])
|
||||
# (This private macro should not be called outside this file.)
|
||||
AC_DEFUN([AM_AUTOMAKE_VERSION],
|
||||
[am__api_version='1.10'
|
||||
dnl Some users find AM_AUTOMAKE_VERSION and mistake it for a way to
|
||||
dnl require some minimum version. Point them to the right macro.
|
||||
m4_if([$1], [1.10], [],
|
||||
[AC_FATAL([Do not call $0, use AM_INIT_AUTOMAKE([$1]).])])dnl
|
||||
])
|
||||
|
||||
# _AM_AUTOCONF_VERSION(VERSION)
|
||||
# -----------------------------
|
||||
# aclocal traces this macro to find the Autoconf version.
|
||||
# This is a private macro too. Using m4_define simplifies
|
||||
# the logic in aclocal, which can simply ignore this definition.
|
||||
m4_define([_AM_AUTOCONF_VERSION], [])
|
||||
|
||||
# AM_SET_CURRENT_AUTOMAKE_VERSION
|
||||
# -------------------------------
|
||||
# Call AM_AUTOMAKE_VERSION so it can be traced.
|
||||
# Call AM_AUTOMAKE_VERSION and AM_AUTOMAKE_VERSION so they 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.10])dnl
|
||||
_AM_AUTOCONF_VERSION(m4_PACKAGE_VERSION)])
|
||||
|
||||
# AM_AUX_DIR_EXPAND -*- Autoconf -*-
|
||||
|
||||
@@ -85,14 +105,14 @@ am_aux_dir=`cd $ac_aux_dir && pwd`
|
||||
|
||||
# AM_CONDITIONAL -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005
|
||||
# Copyright (C) 1997, 2000, 2001, 2003, 2004, 2005, 2006
|
||||
# Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
# serial 7
|
||||
# serial 8
|
||||
|
||||
# AM_CONDITIONAL(NAME, SHELL-CONDITION)
|
||||
# -------------------------------------
|
||||
@@ -101,8 +121,10 @@ AC_DEFUN([AM_CONDITIONAL],
|
||||
[AC_PREREQ(2.52)dnl
|
||||
ifelse([$1], [TRUE], [AC_FATAL([$0: invalid condition: $1])],
|
||||
[$1], [FALSE], [AC_FATAL([$0: invalid condition: $1])])dnl
|
||||
AC_SUBST([$1_TRUE])
|
||||
AC_SUBST([$1_FALSE])
|
||||
AC_SUBST([$1_TRUE])dnl
|
||||
AC_SUBST([$1_FALSE])dnl
|
||||
_AM_SUBST_NOTMAKE([$1_TRUE])dnl
|
||||
_AM_SUBST_NOTMAKE([$1_FALSE])dnl
|
||||
if $2; then
|
||||
$1_TRUE=
|
||||
$1_FALSE='#'
|
||||
@@ -116,15 +138,14 @@ AC_CONFIG_COMMANDS_PRE(
|
||||
Usually this means the macro was only invoked conditionally.]])
|
||||
fi])])
|
||||
|
||||
|
||||
# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005
|
||||
# Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
|
||||
# Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
# serial 8
|
||||
# serial 9
|
||||
|
||||
# There are a few dirty hacks below to avoid letting `AC_PROG_CC' be
|
||||
# written in clear, in which case automake, when reading aclocal.m4,
|
||||
@@ -152,6 +173,7 @@ AC_REQUIRE([AM_DEP_TRACK])dnl
|
||||
ifelse([$1], CC, [depcc="$CC" am_compiler_list=],
|
||||
[$1], CXX, [depcc="$CXX" am_compiler_list=],
|
||||
[$1], OBJC, [depcc="$OBJC" am_compiler_list='gcc3 gcc'],
|
||||
[$1], UPC, [depcc="$UPC" am_compiler_list=],
|
||||
[$1], GCJ, [depcc="$GCJ" am_compiler_list='gcc3 gcc'],
|
||||
[depcc="$$1" am_compiler_list=])
|
||||
|
||||
@@ -217,6 +239,7 @@ AC_CACHE_CHECK([dependency style of $depcc],
|
||||
depfile=sub/conftest.Po tmpdepfile=sub/conftest.TPo \
|
||||
$SHELL ./depcomp $depcc -c -o sub/conftest.${OBJEXT-o} sub/conftest.c \
|
||||
>/dev/null 2>conftest.err &&
|
||||
grep sub/conftst1.h sub/conftest.Po > /dev/null 2>&1 &&
|
||||
grep sub/conftst6.h sub/conftest.Po > /dev/null 2>&1 &&
|
||||
grep sub/conftest.${OBJEXT-o} sub/conftest.Po > /dev/null 2>&1 &&
|
||||
${MAKE-make} -s -f confmf > /dev/null 2>&1; then
|
||||
@@ -269,7 +292,8 @@ if test "x$enable_dependency_tracking" != xno; then
|
||||
AMDEPBACKSLASH='\'
|
||||
fi
|
||||
AM_CONDITIONAL([AMDEP], [test "x$enable_dependency_tracking" != xno])
|
||||
AC_SUBST([AMDEPBACKSLASH])
|
||||
AC_SUBST([AMDEPBACKSLASH])dnl
|
||||
_AM_SUBST_NOTMAKE([AMDEPBACKSLASH])dnl
|
||||
])
|
||||
|
||||
# Generate code to set up dependency tracking. -*- Autoconf -*-
|
||||
@@ -294,8 +318,9 @@ AC_DEFUN([_AM_OUTPUT_DEPENDENCY_COMMANDS],
|
||||
# some people rename them; so instead we look at the file content.
|
||||
# Grep'ing the first line is not enough: some people post-process
|
||||
# each Makefile.in and add a new line on top of each file to say so.
|
||||
# So let's grep whole file.
|
||||
if grep '^#.*generated by automake' $mf > /dev/null 2>&1; then
|
||||
# Grep'ing the whole file is not good either: AIX grep has a line
|
||||
# limit of 2048, but all sed's we know have understand at least 4000.
|
||||
if sed 10q "$mf" | grep '^#.*generated by automake' > /dev/null 2>&1; then
|
||||
dirpart=`AS_DIRNAME("$mf")`
|
||||
else
|
||||
continue
|
||||
@@ -342,8 +367,8 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
|
||||
|
||||
# Do all the work for Automake. -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
|
||||
# Free Software Foundation, Inc.
|
||||
# Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004,
|
||||
# 2005, 2006 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
@@ -366,16 +391,20 @@ AC_DEFUN([AM_OUTPUT_DEPENDENCY_COMMANDS],
|
||||
# arguments mandatory, and then we can depend on a new Autoconf
|
||||
# release and drop the old call support.
|
||||
AC_DEFUN([AM_INIT_AUTOMAKE],
|
||||
[AC_PREREQ([2.58])dnl
|
||||
[AC_PREREQ([2.60])dnl
|
||||
dnl Autoconf wants to disallow AM_ names. We explicitly allow
|
||||
dnl the ones we care about.
|
||||
m4_pattern_allow([^AM_[A-Z]+FLAGS$])dnl
|
||||
AC_REQUIRE([AM_SET_CURRENT_AUTOMAKE_VERSION])dnl
|
||||
AC_REQUIRE([AC_PROG_INSTALL])dnl
|
||||
# test to see if srcdir already configured
|
||||
if test "`cd $srcdir && pwd`" != "`pwd`" &&
|
||||
test -f $srcdir/config.status; then
|
||||
AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
|
||||
if test "`cd $srcdir && pwd`" != "`pwd`"; then
|
||||
# Use -I$(srcdir) only when $(srcdir) != ., so that make's output
|
||||
# is not polluted with repeated "-I."
|
||||
AC_SUBST([am__isrc], [' -I$(srcdir)'])_AM_SUBST_NOTMAKE([am__isrc])dnl
|
||||
# test to see if srcdir already configured
|
||||
if test -f $srcdir/config.status; then
|
||||
AC_MSG_ERROR([source directory already configured; run "make distclean" there first])
|
||||
fi
|
||||
fi
|
||||
|
||||
# test whether we have cygpath
|
||||
@@ -395,6 +424,9 @@ m4_ifval([$2],
|
||||
AC_SUBST([PACKAGE], [$1])dnl
|
||||
AC_SUBST([VERSION], [$2])],
|
||||
[_AM_SET_OPTIONS([$1])dnl
|
||||
dnl Diagnose old-style AC_INIT with new-style AM_AUTOMAKE_INIT.
|
||||
m4_if(m4_ifdef([AC_PACKAGE_NAME], 1)m4_ifdef([AC_PACKAGE_VERSION], 1), 11,,
|
||||
[m4_fatal([AC_INIT should be called with package and version arguments])])dnl
|
||||
AC_SUBST([PACKAGE], ['AC_PACKAGE_TARNAME'])dnl
|
||||
AC_SUBST([VERSION], ['AC_PACKAGE_VERSION'])])dnl
|
||||
|
||||
@@ -430,6 +462,10 @@ AC_PROVIDE_IFELSE([AC_PROG_CXX],
|
||||
[_AM_DEPENDENCIES(CXX)],
|
||||
[define([AC_PROG_CXX],
|
||||
defn([AC_PROG_CXX])[_AM_DEPENDENCIES(CXX)])])dnl
|
||||
AC_PROVIDE_IFELSE([AC_PROG_OBJC],
|
||||
[_AM_DEPENDENCIES(OBJC)],
|
||||
[define([AC_PROG_OBJC],
|
||||
defn([AC_PROG_OBJC])[_AM_DEPENDENCIES(OBJC)])])dnl
|
||||
])
|
||||
])
|
||||
|
||||
@@ -465,7 +501,7 @@ echo "timestamp for $1" >`AS_DIRNAME([$1])`/stamp-h[]$_am_stamp_count])
|
||||
# Define $install_sh.
|
||||
AC_DEFUN([AM_PROG_INSTALL_SH],
|
||||
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
|
||||
install_sh=${install_sh-"$am_aux_dir/install-sh"}
|
||||
install_sh=${install_sh-"\$(SHELL) $am_aux_dir/install-sh"}
|
||||
AC_SUBST(install_sh)])
|
||||
|
||||
# Copyright (C) 2003, 2005 Free Software Foundation, Inc.
|
||||
@@ -543,14 +579,14 @@ rm -f confinc confmf
|
||||
|
||||
# Fake the existence of programs that GNU maintainers use. -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2005
|
||||
# Copyright (C) 1997, 1999, 2000, 2001, 2003, 2004, 2005
|
||||
# Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
# serial 4
|
||||
# serial 5
|
||||
|
||||
# AM_MISSING_PROG(NAME, PROGRAM)
|
||||
# ------------------------------
|
||||
@@ -566,6 +602,7 @@ AC_SUBST($1)])
|
||||
# If it does, set am_missing_run to use it, otherwise, to nothing.
|
||||
AC_DEFUN([AM_MISSING_HAS_RUN],
|
||||
[AC_REQUIRE([AM_AUX_DIR_EXPAND])dnl
|
||||
AC_REQUIRE_AUX_FILE([missing])dnl
|
||||
test x"${MISSING+set}" = xset || MISSING="\${SHELL} $am_aux_dir/missing"
|
||||
# Use eval to expand $SHELL
|
||||
if eval "$MISSING --run true"; then
|
||||
@@ -576,7 +613,7 @@ else
|
||||
fi
|
||||
])
|
||||
|
||||
# Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
|
||||
# Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
@@ -584,60 +621,23 @@ fi
|
||||
|
||||
# AM_PROG_MKDIR_P
|
||||
# ---------------
|
||||
# Check whether `mkdir -p' is supported, fallback to mkinstalldirs otherwise.
|
||||
#
|
||||
# Automake 1.8 used `mkdir -m 0755 -p --' to ensure that directories
|
||||
# created by `make install' are always world readable, even if the
|
||||
# installer happens to have an overly restrictive umask (e.g. 077).
|
||||
# This was a mistake. There are at least two reasons why we must not
|
||||
# use `-m 0755':
|
||||
# - it causes special bits like SGID to be ignored,
|
||||
# - it may be too restrictive (some setups expect 775 directories).
|
||||
#
|
||||
# Do not use -m 0755 and let people choose whatever they expect by
|
||||
# setting umask.
|
||||
#
|
||||
# We cannot accept any implementation of `mkdir' that recognizes `-p'.
|
||||
# Some implementations (such as Solaris 8's) are not thread-safe: if a
|
||||
# parallel make tries to run `mkdir -p a/b' and `mkdir -p a/c'
|
||||
# concurrently, both version can detect that a/ is missing, but only
|
||||
# one can create it and the other will error out. Consequently we
|
||||
# restrict ourselves to GNU make (using the --version option ensures
|
||||
# this.)
|
||||
# Check for `mkdir -p'.
|
||||
AC_DEFUN([AM_PROG_MKDIR_P],
|
||||
[if mkdir -p --version . >/dev/null 2>&1 && test ! -d ./--version; then
|
||||
# We used to keeping the `.' as first argument, in order to
|
||||
# allow $(mkdir_p) to be used without argument. As in
|
||||
# $(mkdir_p) $(somedir)
|
||||
# where $(somedir) is conditionally defined. However this is wrong
|
||||
# for two reasons:
|
||||
# 1. if the package is installed by a user who cannot write `.'
|
||||
# make install will fail,
|
||||
# 2. the above comment should most certainly read
|
||||
# $(mkdir_p) $(DESTDIR)$(somedir)
|
||||
# so it does not work when $(somedir) is undefined and
|
||||
# $(DESTDIR) is not.
|
||||
# To support the latter case, we have to write
|
||||
# test -z "$(somedir)" || $(mkdir_p) $(DESTDIR)$(somedir),
|
||||
# so the `.' trick is pointless.
|
||||
mkdir_p='mkdir -p --'
|
||||
else
|
||||
# On NextStep and OpenStep, the `mkdir' command does not
|
||||
# recognize any option. It will interpret all options as
|
||||
# directories to create, and then abort because `.' already
|
||||
# exists.
|
||||
for d in ./-p ./--version;
|
||||
do
|
||||
test -d $d && rmdir $d
|
||||
done
|
||||
# $(mkinstalldirs) is defined by Automake if mkinstalldirs exists.
|
||||
if test -f "$ac_aux_dir/mkinstalldirs"; then
|
||||
mkdir_p='$(mkinstalldirs)'
|
||||
else
|
||||
mkdir_p='$(install_sh) -d'
|
||||
fi
|
||||
fi
|
||||
AC_SUBST([mkdir_p])])
|
||||
[AC_PREREQ([2.60])dnl
|
||||
AC_REQUIRE([AC_PROG_MKDIR_P])dnl
|
||||
dnl Automake 1.8 to 1.9.6 used to define mkdir_p. We now use MKDIR_P,
|
||||
dnl while keeping a definition of mkdir_p for backward compatibility.
|
||||
dnl @MKDIR_P@ is magic: AC_OUTPUT adjusts its value for each Makefile.
|
||||
dnl However we cannot define mkdir_p as $(MKDIR_P) for the sake of
|
||||
dnl Makefile.ins that do not define MKDIR_P, so we do our own
|
||||
dnl adjustment using top_builddir (which is defined more often than
|
||||
dnl MKDIR_P).
|
||||
AC_SUBST([mkdir_p], ["$MKDIR_P"])dnl
|
||||
case $mkdir_p in
|
||||
[[\\/$]]* | ?:[[\\/]]*) ;;
|
||||
*/*) mkdir_p="\$(top_builddir)/$mkdir_p" ;;
|
||||
esac
|
||||
])
|
||||
|
||||
# Helper functions for option handling. -*- Autoconf -*-
|
||||
|
||||
@@ -749,9 +749,21 @@ dnl Don't test for $cross_compiling = yes, because it might be `maybe'.
|
||||
if test "$cross_compiling" != no; then
|
||||
AC_CHECK_TOOL([STRIP], [strip], :)
|
||||
fi
|
||||
INSTALL_STRIP_PROGRAM="\${SHELL} \$(install_sh) -c -s"
|
||||
INSTALL_STRIP_PROGRAM="\$(install_sh) -c -s"
|
||||
AC_SUBST([INSTALL_STRIP_PROGRAM])])
|
||||
|
||||
# Copyright (C) 2006 Free Software Foundation, Inc.
|
||||
#
|
||||
# This file is free software; the Free Software Foundation
|
||||
# gives unlimited permission to copy and/or distribute it,
|
||||
# with or without modifications, as long as this notice is preserved.
|
||||
|
||||
# _AM_SUBST_NOTMAKE(VARIABLE)
|
||||
# ---------------------------
|
||||
# Prevent Automake from outputing VARIABLE = @VARIABLE@ in Makefile.in.
|
||||
# This macro is traced by Automake.
|
||||
AC_DEFUN([_AM_SUBST_NOTMAKE])
|
||||
|
||||
# Check how to create a tarball. -*- Autoconf -*-
|
||||
|
||||
# Copyright (C) 2004, 2005 Free Software Foundation, Inc.
|
||||
|
||||
11
config.h.in
11
config.h.in
@@ -9,13 +9,6 @@
|
||||
/* 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 the name of macro which returns the name of function being
|
||||
compiled */
|
||||
#undef FUNCTION_MACRO_NAME
|
||||
@@ -50,8 +43,8 @@
|
||||
/* Define to 1 if you have the <ncurses/ncurses.h> header file. */
|
||||
#undef HAVE_NCURSES_NCURSES_H
|
||||
|
||||
/* Define to 1 to use pragma pack directive in MessageBase.h */
|
||||
#undef HAVE_PRAGMA_PACK
|
||||
/* Define to 1 if stat64 is supported */
|
||||
#undef HAVE_STAT64
|
||||
|
||||
/* Define to 1 if you have the <stdint.h> header file. */
|
||||
#undef HAVE_STDINT_H
|
||||
|
||||
82
configure.ac
82
configure.ac
@@ -2,8 +2,8 @@
|
||||
# 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, 0.4.1, hugbug@users.sourceforge.net)
|
||||
AM_INIT_AUTOMAKE(nzbget, 0.4.1)
|
||||
AC_CONFIG_SRCDIR([nzbget.cpp])
|
||||
AC_CONFIG_HEADERS([config.h])
|
||||
|
||||
@@ -13,12 +13,9 @@ 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"
|
||||
@@ -32,12 +29,6 @@ esac
|
||||
if test "$LIBPREF" = ""; then
|
||||
LIBPREF="$LIBPREF1"
|
||||
fi
|
||||
if test "$CFLAGS" = ""; then
|
||||
CFLAGS="$CFLAGS1"
|
||||
fi
|
||||
if test "$CPPFLAGS" = ""; then
|
||||
CPPFLAGS="$CPPFLAGS1"
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
@@ -87,6 +78,14 @@ AC_CHECK_FUNC(getopt_long,
|
||||
[AC_LIBOBJ(getopt) AC_LIBOBJ(getopt1)])
|
||||
|
||||
|
||||
dnl
|
||||
dnl stat64
|
||||
dnl
|
||||
AC_CHECK_FUNC(stat64,
|
||||
[AC_DEFINE([HAVE_STAT64], 1, [Define to 1 if stat64 is supported])],
|
||||
[AC_LIBOBJ(stat64)])
|
||||
|
||||
|
||||
dnl
|
||||
dnl check ctime_r
|
||||
dnl
|
||||
@@ -166,17 +165,6 @@ if test "$HAVE_FUNCTION_MACRO" != "yes"; then
|
||||
fi
|
||||
|
||||
|
||||
dnl
|
||||
dnl check for pragma pack
|
||||
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]))
|
||||
|
||||
|
||||
dnl
|
||||
dnl checks for libxml2 includes and libraries.
|
||||
dnl
|
||||
@@ -245,42 +233,6 @@ else
|
||||
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
|
||||
@@ -342,20 +294,6 @@ if test "$ENABLEPARCHECK" = "yes"; then
|
||||
dnl AC_MSG_ERROR("libpar2 library not found in $LIBVAL.")
|
||||
dnl fi
|
||||
|
||||
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
|
||||
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
|
||||
|
||||
else
|
||||
AC_DEFINE([DISABLE_PARCHECK],1,[Define to 1 to disable smart par-verification and restoration])
|
||||
fi
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,16 +1,23 @@
|
||||
# Sample configuration file for nzbget
|
||||
# Put this file to one of the following locations:
|
||||
#
|
||||
# On POSIX 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
|
||||
#
|
||||
# On Windows put this file in program's directory.
|
||||
#
|
||||
# You can also put the file into any location, if you specify the path to it
|
||||
# using switch "-c", e.g:
|
||||
# nzbget -c /home/user/myconig.txt
|
||||
|
||||
# For quick start change the option MAINDIR and configure one news-server
|
||||
# See section NEWS-SERVERS.
|
||||
|
||||
|
||||
# PROGRAM OPTIONS
|
||||
##############################################################################
|
||||
### PATHS ###
|
||||
|
||||
# Root directory for all related tasks
|
||||
# MAINDIR is a variable and therefore starts with "$"
|
||||
@@ -19,162 +26,26 @@
|
||||
$MAINDIR=~/download
|
||||
|
||||
# Destination-directory to store the downloaded files
|
||||
destdir=${MAINDIR}/dst
|
||||
|
||||
# Directory to store download queue
|
||||
queuedir=${MAINDIR}/queue
|
||||
DestDir=${MAINDIR}/dst
|
||||
|
||||
# Directory to monitor for incoming nzb-jobs
|
||||
nzbdir=${MAINDIR}/nzb
|
||||
NzbDir=${MAINDIR}/nzb
|
||||
|
||||
# Directory to store download queue
|
||||
QueueDir=${MAINDIR}/queue
|
||||
|
||||
# Directory to store temporary files
|
||||
tempdir=${MAINDIR}/tmp
|
||||
TempDir=${MAINDIR}/tmp
|
||||
|
||||
# Lock-file for daemon-mode, contains process-id (PID)
|
||||
lockfile=/tmp/nzbget.lock
|
||||
# Lock-file for daemon-mode, contains process-id (PID) (POSIX only)
|
||||
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
|
||||
# Where to store log file, if it needs to be created (see "CreateLog")
|
||||
LogFile=${destdir}/nzbget.log
|
||||
|
||||
|
||||
# NEWS-SERVERS
|
||||
##############################################################################
|
||||
### NEWS-SERVERS ###
|
||||
|
||||
# This section defines which servers nzbget should connect to.
|
||||
# The servers will be ordered by their level, i.e. nzbget will at
|
||||
@@ -182,8 +53,9 @@ strictparname=yes
|
||||
# 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.
|
||||
# NOTE: Do not leave out a level in your server-list and start with level 0!
|
||||
# NOTE: Several servers with the same level may be used, they will have
|
||||
# the same priority.
|
||||
|
||||
# First server, on level 0
|
||||
# Level of newsserver
|
||||
@@ -214,3 +86,408 @@ server1.connections=4
|
||||
#server3.username=me2
|
||||
#server3.password=mypass2
|
||||
#server3.connections=1
|
||||
|
||||
|
||||
##############################################################################
|
||||
### PERMISSIONS (POSIX ONLY) ###
|
||||
|
||||
# User name for daemon-mode (POSIX in daemon-mode only).
|
||||
# Set the user that the daemon normally runs at.
|
||||
# Set $MAINDIR with an absolute path to be sure where it will write.
|
||||
# This allows nzbget daemon to be launched in rc.local (at boot), and
|
||||
# download items as a specific user id.
|
||||
# NOTE: This option has effect only if the program was started from
|
||||
# root-account, otherwise it is ignored and the daemon runs under
|
||||
# current user id
|
||||
DaemonUserName=root
|
||||
|
||||
# Specify default umask (affects file permissions) for newly created
|
||||
# files (POSIX only).
|
||||
# The value should be written in octal form (the same as for "umask" shell
|
||||
# command). If umask not specified (or a value greater than 0777 used, useful
|
||||
# to disable current config-setting via command-line parameter) the umask-mode
|
||||
# will not be set and current umask-mode (set via shell) will be used
|
||||
# NOTE: do not forget to uncomment the next line
|
||||
#UMask=022
|
||||
|
||||
|
||||
##############################################################################
|
||||
### DOWNLOAD QUEUE ###
|
||||
|
||||
# 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
|
||||
|
||||
# Reuse articles saved in temp-directory from previous program start (yes, no)
|
||||
# This allows to continue download of file, if program was exited before
|
||||
# the file was completed.
|
||||
ContinuePartial=yes
|
||||
|
||||
# Create subdirectory with nzb-filename in destination-directory (yes, no)
|
||||
AppendNzbDir=yes
|
||||
|
||||
# How often incoming-directory (option "NzbDir") must be checked for new
|
||||
# nzb-files, in seconds.
|
||||
# Value "0" disables the check.
|
||||
NzbDirInterval=5
|
||||
|
||||
# How old nzb-file should at least be for it to be loaded to queue, in seconds.
|
||||
# Nzbget checks if nzb-file was not modified in last few seconds, defined by
|
||||
# this option. That safety interval prevents the loading of files, which
|
||||
# were not yet completely saved to disk, for example if they are still being
|
||||
# downloaded in web-browser.
|
||||
NzbDirFileAge=60
|
||||
|
||||
# Check for duplicate files (yes, no)
|
||||
# If this option is enabled the program checks by adding of a new nzb-file:
|
||||
# 1) if nzb-file contains duplicate entries. This check aims on detecting
|
||||
# of reposted files (if first file was not fully uploaded);
|
||||
# If the program find two files with identical names, only the
|
||||
# biggest of these files will be added to queue;
|
||||
# 2) if download queue already contains file with the same name;
|
||||
# 3) if destination file on disk already exists.
|
||||
# In last two cases: if the file exists it will not be added to queue;
|
||||
# If this option is disabled, all files are downloaded and duplicate files
|
||||
# are renamed to "filename_duplicate1".
|
||||
# Existing files are never deleted or overwritten.
|
||||
DupeCheck=no
|
||||
|
||||
# Visibly rename broken files on download appending "_broken" (yes, no)
|
||||
# Do not activate this option if par-check is enabled.
|
||||
RenameBroken=no
|
||||
|
||||
# Decode articles (yes, no)
|
||||
# yes - decode articles using internal decoder (supports yEnc and UU formats).
|
||||
# no - the articles will not be decoded and joined. External programs
|
||||
# (like "uudeview") can be used to decode and join downloaded articles.
|
||||
# Also useful for debugging to look at article's source text.
|
||||
Decode=yes
|
||||
|
||||
# Write decoded articles directly into destination output file (yes, no)
|
||||
# With this option enabled the program at first creates the output
|
||||
# destination file with required size (total size of all articles),
|
||||
# then writes on the fly decoded articles directly to the file
|
||||
# without creating of any temporary files, even for decoded articles.
|
||||
# This may results in major performance improvement, but this higly
|
||||
# depends on OS and filesystem used.
|
||||
# Can improve performance on a very fast internet connections,
|
||||
# but you need to test if it works in your case.
|
||||
# INFO: Tests showed, that on Linux with EXT3-partition activating of
|
||||
# this option results in up to 20% better performance, but on Windows with NTFS
|
||||
# or Linux with FAT32-partitions the performance were decreased.
|
||||
# The possible reason is that on EXT3-partition Linux can create large files
|
||||
# very fast (if the content of file does not need to be initialized),
|
||||
# but Windows on NTFS-partition and also Linux on FAT32-partition need to
|
||||
# initialize created large file with nulls, resulting in a big performace
|
||||
# degradation.
|
||||
# NOTE: for testing try to download few big files (with total size 500-1000MB)
|
||||
# and measure required time. Do not rely on the program's speed indicator.
|
||||
# NOTE: if both options "DirectWrite" and "ContinuePartial" are enabled,
|
||||
# the program will create empty articles-files in temp-directrory. They
|
||||
# are used to continue download of file on a next program start. To minimize
|
||||
# disk-io it is recommended to disable option "ContinuePartial", if
|
||||
# "DirectWrite" is enabled. Especially on a fast connections (where you
|
||||
# would want to activate "DirectWrite") it should not be a problem to
|
||||
# redownload the interrupted file.
|
||||
DirectWrite=no
|
||||
|
||||
# Check CRC of downloaded and decoded articles (yes, no)
|
||||
# Normally this option should be enabled for better detecting of download
|
||||
# errors. However checking of CRC needs about the same CPU time as
|
||||
# decoding of articles. On a fast connections with slow CPUs disabling of
|
||||
# CPU-check may slightly improve performance (if CPU is a limiting factor).
|
||||
CrcCheck=yes
|
||||
|
||||
# How much retries should be attempted if a download error occurs
|
||||
Retries=4
|
||||
|
||||
# Set the interval between retries, in seconds
|
||||
RetryInterval=10
|
||||
|
||||
# Redownload article if CRC-check fails (yes, no)
|
||||
# Helps to minimize number of broken files, but may be effective
|
||||
# only if you have multiple download servers (even from the same provider
|
||||
# but from different locations (e.g. europe, usa)).
|
||||
# In any case the option increases your traffic.
|
||||
# For slow connections loading of extra par-blocks may be more effective
|
||||
# The option "CrcCheck" must be enabled for option "RetryOnCrcError" to work.
|
||||
RetryOnCrcError=no
|
||||
|
||||
# Set connection timeout, in seconds
|
||||
ConnectionTimeout=60
|
||||
|
||||
# Timeout until a download-thread is killed, in seconds
|
||||
# This can help on hanging downloads, but is dangerous.
|
||||
# Do not use small values!
|
||||
TerminateTimeout=600
|
||||
|
||||
# Set the maximum number of threads program may create.
|
||||
# Connection errors or fast connection with slow cpu can cause
|
||||
# the creating of many (thousands) threads, which results in program crash.
|
||||
# Limiting the number of threads helps in such situations.
|
||||
# The option affects only download threads, so the number of existing threads
|
||||
# may be slightly more than set by the option.
|
||||
ThreadLimit=100
|
||||
|
||||
# Set the maximum download rate in KB/s, "0" means no speed control
|
||||
DownloadRate=0
|
||||
|
||||
# Set the size of memory buffer used by writing the articles, in Bytes.
|
||||
# Bigger values decrease disk-io, but increase memory usage.
|
||||
# Value "0" causes the OS-dependend default value to be used.
|
||||
# With value "-1" (which means "max/auto") the program sets the size of
|
||||
# buffer according to the size of current article (typically less than 500K).
|
||||
# NOTE: the value must be written in bytes, do not use postfixes "K" or "M".
|
||||
# NOTE: to calculate the memory usage multiply WriteBufferSize by max number
|
||||
# of connections, configured in section "NEWS-SERVERS".
|
||||
# NOTE: typical article's size not exceed 500000 bytes, so using bigger values
|
||||
# (like several megabytes) will just waste memory.
|
||||
# NOTE: for desktop computers with large amount of memory value "-1" (max/auto)
|
||||
# is recommended, but for computers with very low memory (routers, NAS)
|
||||
# value "0" (default OS-dependend size) could be better alternative.
|
||||
# NOTE: write-buffer is managed by OS (system libraries) and therefore
|
||||
# the effect of the option is highly OS-dependend.
|
||||
WriteBufferSize=0
|
||||
|
||||
# Pause if disk space gets below this value, in MegaBytes.
|
||||
# Value "0" disables the check.
|
||||
# Only the disk space on the drive with "DestDir" is checked.
|
||||
# The drive with "TempDir" is not checked.
|
||||
DiskSpace=250
|
||||
|
||||
|
||||
##############################################################################
|
||||
### LOGGING ###
|
||||
|
||||
# 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 printed only if the programm was compiled in
|
||||
# debug-mode: "./configure --enable-debug"
|
||||
ErrorTarget=both
|
||||
WarningTarget=both
|
||||
InfoTarget=both
|
||||
DetailTarget=both
|
||||
DebugTarget=both
|
||||
|
||||
# Number of messages stored in buffer and available for remote clients
|
||||
LogBufferSize=1000
|
||||
|
||||
# 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
|
||||
|
||||
# See also option "logfile" in secion "PATHS"
|
||||
|
||||
|
||||
##############################################################################
|
||||
### DISPLAY ###
|
||||
|
||||
# Set screen-outputmode (loggable, colored, curses)
|
||||
# loggable - only messages will be printed to standard output;
|
||||
# colored - prints messages (with simple coloring for messages categories)
|
||||
# and download progress info; uses escape-sequenses to move cursor;
|
||||
# curses - advanced interactive iterface with the ability to edit
|
||||
# download queue and variaous output options;
|
||||
OutputMode=curses
|
||||
|
||||
# Shows NZB-Filename in file list in curses-outputmode (yes, no)
|
||||
# This option controls the initial state of curses-frontend,
|
||||
# it can be switched on/off in run-time with Z-key
|
||||
CursesNzbName=yes
|
||||
|
||||
# Show files in groups (NZB-files) in queue list in curses-outputmode (yes, no)
|
||||
# This option controls the initial state of curses-frontend,
|
||||
# it can be switched on/off in run-time with G-key
|
||||
CursesGroup=no
|
||||
|
||||
# Show timestamps in message list in curses-outputmode (yes, no)
|
||||
# This option controls the initial state of curses-frontend,
|
||||
# it can be switched on/off in run-time with T-key
|
||||
CursesTime=no
|
||||
|
||||
# Update interval for Frontend-output in MSec (min value 25)
|
||||
# Bigger values reduce CPU usage (especially in curses-outputmode)
|
||||
# and network traffic in remote-client mode
|
||||
UpdateInterval=200
|
||||
|
||||
|
||||
##############################################################################
|
||||
### CLIENT/SERVER COMMUNICATION ###
|
||||
|
||||
# Set the IP on which the server listen and which client uses to contact
|
||||
# the server. It could be dns-hostname or ip-address (more effective since
|
||||
# does not require dns-lookup).
|
||||
# If you want the server to listen to all interfaces, use "0.0.0.0"
|
||||
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
|
||||
|
||||
# See also option "logbuffersize" in section "LOGGING"
|
||||
|
||||
|
||||
##############################################################################
|
||||
### PAR CHECK/REPAIR AND POSTPROCESSING ###
|
||||
|
||||
# Reload Post-processor-queue on start, if it exists (yes, no)
|
||||
# For this option to work the options "SaveQueue" and "ReloadQueue" must
|
||||
# be also enabled.
|
||||
ReloadPostQueue=yes
|
||||
|
||||
# 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=one
|
||||
|
||||
# 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 fail
|
||||
ParCheck=no
|
||||
|
||||
# Automatic par-repair (yes, no)
|
||||
# If option "parcheck" is enabled and "parrepair" is not, the program
|
||||
# only verifies 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 for par2-files
|
||||
# in download queue, which can be unpaused and used for restore.
|
||||
# These par2-files should have the same base name as the main par2-file,
|
||||
# currently loaded in par-checker. Sometimes extra par files (especially if
|
||||
# they were uploaded from a different poster) have not matching names.
|
||||
# Normally par-checker does not use these files, but you can allow it
|
||||
# to use these files 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
|
||||
# and the option "strictparname" does not change this behavior
|
||||
StrictParName=yes
|
||||
|
||||
# Cleanup download queue after successful check/repair (yes, no)
|
||||
# Enable this option for automatic deletion of unneeded (paused) par-files
|
||||
# from download queue after successful check/repair.
|
||||
# NOTE: before cleaning up the program checks if all paused files are par-files.
|
||||
# If there are paused non-par-files (this means that you have paused them
|
||||
# manually), the cleanup will be skipped for this collection.
|
||||
ParCleanupQueue=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 and nzb-file contains
|
||||
# multiple collections; see note below for the definition of "collection")
|
||||
# is completed and possibly par-checked/repaired.
|
||||
# Arguments passed to that program:
|
||||
# 1 - path to destination dir, where downloaded files are located;
|
||||
# 2 - name of nzb-file processed;
|
||||
# 3 - name of par-file processed (if par-checked) or empty string (if not);
|
||||
# 4 - 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 sucessfully repaired;
|
||||
# 3 - checked and can be repaired but repair is disabled;
|
||||
# 5 - state of nzb-job:
|
||||
# 0 - there are more collections in this nzb-file queued;
|
||||
# 1 - this was the last collection in nzb-file;
|
||||
# 6 - 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;
|
||||
# NOTE: The parameter "state of nzb-job" is very important and MUST be checked
|
||||
# even in the simplest scripts.
|
||||
# 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 unpack files or 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 (not reliable,
|
||||
# because par-files sometimes have different names than rar-files);
|
||||
# 2) or you just check the parameters "state of nzb-job" and "indication of
|
||||
# failed par-jobs" and do the processing, only if they are set to "1"
|
||||
# (which means, that this was the last collection in nzb-file and all files
|
||||
# are now completed) and to "0" (no failed par-jobs) respectively;
|
||||
# NOTE 2: if the option "ParCheck" is disabled nzbget calls PostProcess
|
||||
# only once, not after every collection, because the detection of collection
|
||||
# is disabled in this case;
|
||||
# NOTE 3: the term "collection" in the above description actually means
|
||||
# "par-set". To determine what "collections" are present in nzb-file nzbget
|
||||
# looks for par-sets. If any collection of files within nzb-file does
|
||||
# not have any par-files, this collection will not be detected.
|
||||
# For example, for nzb-file containing three collections but only two par-sets,
|
||||
# the postprocess will be called two times - after processing of each par-set.
|
||||
# NOTE 4: do not forget to uncomment the next line
|
||||
#PostProcess=~/myscript.sh
|
||||
|
||||
# Allow multiple post-processing for the same nzb-file (yes,no)
|
||||
# After the post-processing (par-check and call of a postprocess-script) is
|
||||
# completed, nzbget adds the nzb-file to a list of completed-jobs. The nzb-file
|
||||
# stays in the list until the last file from that nzb-file is deleted from
|
||||
# the download queue (it occurs straight away if the par-check was successful
|
||||
# and the option "ParCleanupQueue" was set).
|
||||
# So, if there were paused files in queue and they will be unpaused (manually
|
||||
# or from a post-process-script) nzbget will not post-process nzb-file again.
|
||||
# This prevents the unwanted multiple post-processing of the same nzb-file.
|
||||
# But it might be needed if the par-check/-repair are performed not directly
|
||||
# by nzbget but from a post-process-script.
|
||||
# NOTE 1: it is recommended to keep the option disabled. You should enable it
|
||||
# only if it is suggested by a post-process-script's author.
|
||||
# NOTE 2: by enabling "AllowReProcess" you should disable the option "ParCheck"
|
||||
# to prevent multiple par-checking.
|
||||
AllowReProcess=no
|
||||
|
||||
# Set the default message-kind for output received from postprocess-script
|
||||
# (None, Detail, Info, Warning, Error, Debug).
|
||||
# NZBGet checks if the line written by the script to stdout or stderr starts
|
||||
# with special character-sequence, determining the message-kind, e.g.:
|
||||
# [INFO] bla-bla
|
||||
# [DETAIL] bla-bla
|
||||
# [WARNING] bla-bla
|
||||
# [ERROR] bla-bla
|
||||
# [DEBUG] bla-bla
|
||||
# If the message-kind was detected the text is added to log with detected type.
|
||||
# Otherwise the message becomes the default kind, specified in this option.
|
||||
PostLogKind=Detail
|
||||
|
||||
|
||||
##############################################################################
|
||||
### PERFORMANCE ###
|
||||
|
||||
# On a very fast connection and slow CPU and/or drive the following
|
||||
# settings may improve performance:
|
||||
# 1) Disable par-checking and -repairing ("ParCheck=no"). VERY important,
|
||||
# because par-checking/repairing needs a lot of CPU-power and
|
||||
# significantly increases disk usage;
|
||||
# 2) Try to activate option "DirectWrite" ("DirectWrite=yes");
|
||||
# 3) Disable option "CrcCheck" ("CrcCheck=no");
|
||||
# 4) Disable option "ContinuePartial" ("ContinuePartial=no");
|
||||
# 5) Do not limit download rate ("DownloadRate=0"), because the bandwidth
|
||||
# throttling eats CPU time;
|
||||
# 6) Disable logging for info- and debug-messages ("InfoTarget=none",
|
||||
# "DebugTarget=none");
|
||||
# 7) Run the program in daemon (Posix) or service (Windows) mode and use
|
||||
# remote client for short periods of time needed for controlling of
|
||||
# download process on server. Daemon/Service mode eats less CPU
|
||||
# resources due to not updating of output on screen.
|
||||
# 8) Increase the value of option "WriteBufferSize" or better set it to
|
||||
# "-1" (max/auto) if you have spare 5-20 MB of memory.
|
||||
|
||||
181
nzbget.cpp
181
nzbget.cpp
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* 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>
|
||||
@@ -38,17 +38,16 @@
|
||||
#include <winsvc.h>
|
||||
#else
|
||||
#include <unistd.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.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>
|
||||
#endif
|
||||
#include <iostream>
|
||||
#endif
|
||||
|
||||
#include "nzbget.h"
|
||||
@@ -63,6 +62,7 @@
|
||||
#include "RemoteServer.h"
|
||||
#include "RemoteClient.h"
|
||||
#include "MessageBase.h"
|
||||
#include "DiskState.h"
|
||||
#include "PrePostProcessor.h"
|
||||
#include "ParChecker.h"
|
||||
#ifdef WIN32
|
||||
@@ -77,35 +77,61 @@ void ProcessClientRequest();
|
||||
void InstallSignalHandlers();
|
||||
void Daemonize();
|
||||
#endif
|
||||
#ifdef DEBUG
|
||||
void DoTest();
|
||||
#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;
|
||||
RemoteServer* g_pRemoteServer = NULL;
|
||||
DownloadSpeedMeter* g_pDownloadSpeedMeter = NULL;
|
||||
Log* g_pLog = NULL;
|
||||
PrePostProcessor* g_pPrePostProcessor;
|
||||
PrePostProcessor* g_pPrePostProcessor = NULL;
|
||||
DiskState* g_pDiskState = NULL;
|
||||
|
||||
|
||||
/*
|
||||
* Main loop
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
#ifdef WIN32
|
||||
#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
|
||||
|
||||
#ifdef WIN32
|
||||
_set_fmode(_O_BINARY);
|
||||
InstallUninstallServiceCheck(argc, argv);
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_PARCHECK
|
||||
DisableCout();
|
||||
#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);
|
||||
|
||||
#ifndef WIN32
|
||||
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");
|
||||
@@ -114,12 +140,13 @@ int main(int argc, char *argv[])
|
||||
|
||||
if (g_pOptions->GetDaemonMode())
|
||||
{
|
||||
info("nzbget daemon-mode");
|
||||
#ifdef WIN32
|
||||
info("nzbget service-mode");
|
||||
StartService(Run);
|
||||
return 0;
|
||||
#else
|
||||
Daemonize();
|
||||
info("nzbget daemon-mode");
|
||||
#endif
|
||||
}
|
||||
else if (g_pOptions->GetServerMode())
|
||||
@@ -132,6 +159,13 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
Run();
|
||||
|
||||
#ifdef WIN32
|
||||
#ifdef _DEBUG
|
||||
_CrtDumpMemoryLeaks();
|
||||
#endif
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -153,7 +187,7 @@ 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;
|
||||
@@ -166,7 +200,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,13 +231,9 @@ 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()))
|
||||
{
|
||||
@@ -205,7 +241,13 @@ void Run()
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_pOptions->GetSaveQueue() && g_pOptions->GetServerMode())
|
||||
{
|
||||
g_pDiskState = new DiskState();
|
||||
}
|
||||
|
||||
g_pQueueCoordinator->Start();
|
||||
g_pPrePostProcessor->Start();
|
||||
|
||||
// enter main program-loop
|
||||
while (g_pQueueCoordinator->IsRunning() || g_pPrePostProcessor->IsRunning())
|
||||
@@ -249,14 +291,6 @@ void Run()
|
||||
debug("RemoteServer stopped");
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
if (g_pOptions->GetTest())
|
||||
{
|
||||
DoTest();
|
||||
}
|
||||
#endif
|
||||
|
||||
// Stop Frontend
|
||||
if (g_pFrontend)
|
||||
{
|
||||
@@ -302,7 +336,7 @@ void ProcessClientRequest()
|
||||
else if (g_pOptions->GetClientOperation() == Options::opClientRequestEditQueue)
|
||||
{
|
||||
Client->RequestServerEditQueue(g_pOptions->GetEditQueueAction(), g_pOptions->GetEditQueueOffset(),
|
||||
g_pOptions->GetEditQueueIDFrom(), g_pOptions->GetEditQueueIDTo());
|
||||
g_pOptions->GetEditQueueIDList(), g_pOptions->GetEditQueueIDCount(), true);
|
||||
}
|
||||
else if (g_pOptions->GetClientOperation() == Options::opClientRequestLog)
|
||||
{
|
||||
@@ -316,6 +350,18 @@ void ProcessClientRequest()
|
||||
{
|
||||
Client->RequestServerDownload(g_pOptions->GetArgFilename(), g_pOptions->GetAddTop());
|
||||
}
|
||||
else if (g_pOptions->GetClientOperation() == Options::opClientRequestVersion)
|
||||
{
|
||||
Client->RequestServerVersion();
|
||||
}
|
||||
else if (g_pOptions->GetClientOperation() == Options::opClientRequestPostQueue)
|
||||
{
|
||||
Client->RequestPostQueue();
|
||||
}
|
||||
else if (g_pOptions->GetClientOperation() == Options::opClientRequestWriteLog)
|
||||
{
|
||||
Client->RequestWriteLog(g_pOptions->GetWriteLogKind(), g_pOptions->GetLastArg());
|
||||
}
|
||||
|
||||
delete Client;
|
||||
}
|
||||
@@ -357,23 +403,30 @@ 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");
|
||||
// ignoring
|
||||
break;
|
||||
|
||||
case SIGCHLD:
|
||||
// ignoring
|
||||
break;
|
||||
|
||||
case SIGSEGV:
|
||||
signal(SIGSEGV, SIG_DFL); // Reset the signal handler
|
||||
debug("SIGSEGV received");
|
||||
break;
|
||||
|
||||
default:
|
||||
debug("Signal %i received", iSignal);
|
||||
// printf("Signal %i received\n", iSignal);
|
||||
if (SignalProcList[iSignal - 1])
|
||||
{
|
||||
SignalProcList[iSignal - 1](iSignal);
|
||||
@@ -435,6 +488,14 @@ void Cleanup()
|
||||
}
|
||||
debug("Frontend deleted");
|
||||
|
||||
debug("Deleting DiskState");
|
||||
if (g_pDiskState)
|
||||
{
|
||||
delete g_pDiskState;
|
||||
g_pDiskState = NULL;
|
||||
}
|
||||
debug("DiskState deleted");
|
||||
|
||||
debug("Deleting Options");
|
||||
if (g_pOptions)
|
||||
{
|
||||
@@ -480,11 +541,26 @@ 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)
|
||||
{
|
||||
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 +571,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
|
||||
|
||||
13
nzbget.h
13
nzbget.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
@@ -31,19 +31,23 @@
|
||||
// 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 int32_t __int32
|
||||
#define mkdir(dir, flags) _mkdir(dir)
|
||||
#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)
|
||||
#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
|
||||
|
||||
#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)
|
||||
@@ -64,6 +68,7 @@
|
||||
#define PATH_SEPARATOR '/'
|
||||
#define ALT_PATH_SEPARATOR '\\'
|
||||
#define MAX_PATH 1024
|
||||
#define S_DIRMODE (S_IRWXU | S_IRWXG | S_IRWXO)
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -131,7 +131,7 @@
|
||||
<Tool
|
||||
Name="VCLinkerTool"
|
||||
AdditionalDependencies="WS2_32.Lib ole32.lib OleAut32.Lib comsuppwd.lib Advapi32.lib sigc-2.0.lib par2.lib"
|
||||
LinkIncremental="2"
|
||||
LinkIncremental="0"
|
||||
GenerateDebugInformation="true"
|
||||
SubSystem="1"
|
||||
OptimizeReferences="2"
|
||||
@@ -175,6 +175,14 @@
|
||||
RelativePath=".\ArticleDownloader.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\BinRpc.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\BinRpc.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\ColoredFrontend.cpp"
|
||||
>
|
||||
@@ -323,6 +331,14 @@
|
||||
RelativePath=".\ParChecker.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\PostInfo.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\PostInfo.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\PrePostProcessor.cpp"
|
||||
>
|
||||
@@ -339,6 +355,14 @@
|
||||
RelativePath=".\QueueCoordinator.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\QueueEditor.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\QueueEditor.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\RemoteClient.cpp"
|
||||
>
|
||||
@@ -355,6 +379,14 @@
|
||||
RelativePath=".\RemoteServer.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\ScriptController.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\ScriptController.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\ServerPool.cpp"
|
||||
>
|
||||
@@ -383,6 +415,14 @@
|
||||
RelativePath=".\win32.h"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\XmlRpc.cpp"
|
||||
>
|
||||
</File>
|
||||
<File
|
||||
RelativePath=".\XmlRpc.h"
|
||||
>
|
||||
</File>
|
||||
</Files>
|
||||
<Globals>
|
||||
</Globals>
|
||||
|
||||
21
win32.h
21
win32.h
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* This file if part of nzbget
|
||||
* This file is part of nzbget
|
||||
*
|
||||
* Copyright (C) 2007 Andrei Prygounkov <hugbug@users.sourceforge.net>
|
||||
*
|
||||
@@ -30,13 +30,6 @@
|
||||
/* 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) */
|
||||
#define ENABLE_PARPROGRESS
|
||||
|
||||
/* Define to 1 to include support for uulib */
|
||||
#undef ENABLE_UULIB
|
||||
|
||||
/* Define to the name of macro which returns the name of function being
|
||||
compiled */
|
||||
#define FUNCTION_MACRO_NAME __FUNCTION__
|
||||
@@ -50,13 +43,10 @@
|
||||
/* Define to 1 if getopt_long is supported */
|
||||
#undef HAVE_GETOPT_LONG
|
||||
|
||||
/* Define to 1 to use pragma pack directive in MessageBase.h */
|
||||
#define HAVE_PRAGMA_PACK
|
||||
|
||||
/* Define to 1 if variadic macros are supported */
|
||||
#define HAVE_VARIADIC_MACROS
|
||||
|
||||
#define VERSION "0.3.0"
|
||||
#define VERSION "0.4.1"
|
||||
|
||||
/* Suppress warnings */
|
||||
#define _CRT_SECURE_NO_DEPRECATE
|
||||
@@ -66,6 +56,13 @@
|
||||
|
||||
#define _USE_32BIT_TIME_T
|
||||
|
||||
#ifdef _DEBUG
|
||||
// detection of memory leaks
|
||||
#define _CRTDBG_MAP_ALLOC
|
||||
#include <stdlib.h>
|
||||
#include <crtdbg.h>
|
||||
#endif
|
||||
|
||||
#include <windows.h>
|
||||
#include <winbase.h>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user