/* * This file is part of nzbget * * Copyright (C) 2005 Bo Cordes Petersen * Copyright (C) 2007-2015 Andrey Prygunkov * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * $Revision: 1221 $ * $Date: 2015-02-26 15:57:38 -0500 (Thu, 26 Feb 2015) $ * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef WIN32 #include "win32.h" #endif #include #include #include #ifndef WIN32 #include #include #include #include #endif #include "nzbget.h" #include "BinRpc.h" #include "Log.h" #include "Options.h" #include "QueueEditor.h" #include "Util.h" #include "DownloadInfo.h" #include "Scanner.h" #include "StatMeter.h" extern Options* g_pOptions; extern Scanner* g_pScanner; extern StatMeter* g_pStatMeter; extern void ExitProc(); extern void Reload(); const char* g_szMessageRequestNames[] = { "N/A", "Download", "Pause/Unpause", "List", "Set download rate", "Dump debug", "Edit queue", "Log", "Quit", "Reload", "Version", "Post-queue", "Write log", "Scan", "Pause/Unpause postprocessor", "History" }; const unsigned int g_iMessageRequestSizes[] = { 0, sizeof(SNZBDownloadRequest), sizeof(SNZBPauseUnpauseRequest), sizeof(SNZBListRequest), sizeof(SNZBSetDownloadRateRequest), sizeof(SNZBDumpDebugRequest), sizeof(SNZBEditQueueRequest), sizeof(SNZBLogRequest), sizeof(SNZBShutdownRequest), sizeof(SNZBReloadRequest), sizeof(SNZBVersionRequest), sizeof(SNZBPostQueueRequest), sizeof(SNZBWriteLogRequest), sizeof(SNZBScanRequest), sizeof(SNZBHistoryRequest) }; class BinCommand { protected: Connection* m_pConnection; SNZBRequestBase* m_pMessageBase; bool ReceiveRequest(void* pBuffer, int iSize); void SendBoolResponse(bool bSuccess, const char* szText); public: virtual ~BinCommand() {} virtual void Execute() = 0; void SetConnection(Connection* pConnection) { m_pConnection = pConnection; } void SetMessageBase(SNZBRequestBase* pMessageBase) { m_pMessageBase = pMessageBase; } }; class DownloadBinCommand: public BinCommand { public: virtual void Execute(); }; class ListBinCommand: public BinCommand { public: virtual void Execute(); }; class LogBinCommand: public BinCommand { public: virtual void Execute(); }; class PauseUnpauseBinCommand: public BinCommand { public: virtual void Execute(); }; class EditQueueBinCommand: public BinCommand { public: virtual void Execute(); }; class SetDownloadRateBinCommand: public BinCommand { public: virtual void Execute(); }; class DumpDebugBinCommand: public BinCommand { public: virtual void Execute(); }; class ShutdownBinCommand: public BinCommand { public: virtual void Execute(); }; class ReloadBinCommand: public BinCommand { public: virtual void Execute(); }; class VersionBinCommand: public BinCommand { public: virtual void Execute(); }; class PostQueueBinCommand: public BinCommand { public: virtual void Execute(); }; class WriteLogBinCommand: public BinCommand { public: virtual void Execute(); }; class ScanBinCommand: public BinCommand { public: virtual void Execute(); }; class HistoryBinCommand: public BinCommand { public: virtual void Execute(); }; class UrlQueueBinCommand: public BinCommand { public: virtual void Execute(); }; //***************************************************************** // BinProcessor BinRpcProcessor::BinRpcProcessor() { m_MessageBase.m_iSignature = (int)NZBMESSAGE_SIGNATURE; } void BinRpcProcessor::Execute() { // Read the first package which needs to be a request if (!m_pConnection->Recv(((char*)&m_MessageBase) + sizeof(m_MessageBase.m_iSignature), sizeof(m_MessageBase) - sizeof(m_MessageBase.m_iSignature))) { warn("Non-nzbget request received on port %i from %s", g_pOptions->GetControlPort(), m_pConnection->GetRemoteAddr()); return; } if ((strlen(g_pOptions->GetControlUsername()) > 0 && strcmp(m_MessageBase.m_szUsername, g_pOptions->GetControlUsername())) || strcmp(m_MessageBase.m_szPassword, g_pOptions->GetControlPassword())) { warn("nzbget request received on port %i from %s, but username or password invalid", g_pOptions->GetControlPort(), m_pConnection->GetRemoteAddr()); return; } debug("%s request received from %s", g_szMessageRequestNames[ntohl(m_MessageBase.m_iType)], m_pConnection->GetRemoteAddr()); Dispatch(); } void BinRpcProcessor::Dispatch() { if (ntohl(m_MessageBase.m_iType) >= (int)eRemoteRequestDownload && ntohl(m_MessageBase.m_iType) <= (int)eRemoteRequestHistory && g_iMessageRequestSizes[ntohl(m_MessageBase.m_iType)] != ntohl(m_MessageBase.m_iStructSize)) { error("Invalid size of request: expected %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 eRemoteRequestReload: command = new ReloadBinCommand(); break; case eRemoteRequestVersion: command = new VersionBinCommand(); break; case eRemoteRequestPostQueue: command = new PostQueueBinCommand(); break; case eRemoteRequestWriteLog: command = new WriteLogBinCommand(); break; case eRemoteRequestScan: command = new ScanBinCommand(); break; case eRemoteRequestHistory: command = new HistoryBinCommand(); break; default: error("Received unsupported request %i", ntohl(m_MessageBase.m_iType)); break; } if (command) { command->SetConnection(m_pConnection); 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 m_pConnection->Send((char*) &BoolResponse, sizeof(BoolResponse)); m_pConnection->Send((char*)szText, iTextLen); } bool BinCommand::ReceiveRequest(void* pBuffer, int iSize) { memcpy(pBuffer, m_pMessageBase, sizeof(SNZBRequestBase)); iSize -= sizeof(SNZBRequestBase); if (iSize > 0) { if (!m_pConnection->Recv(((char*)pBuffer) + sizeof(SNZBRequestBase), iSize)) { error("invalid request"); return false; } } return true; } void PauseUnpauseBinCommand::Execute() { SNZBPauseUnpauseRequest PauseUnpauseRequest; if (!ReceiveRequest(&PauseUnpauseRequest, sizeof(PauseUnpauseRequest))) { return; } g_pOptions->SetResumeTime(0); switch (ntohl(PauseUnpauseRequest.m_iAction)) { case eRemotePauseUnpauseActionDownload: g_pOptions->SetPauseDownload(ntohl(PauseUnpauseRequest.m_bPause)); break; case eRemotePauseUnpauseActionPostProcess: g_pOptions->SetPausePostProcess(ntohl(PauseUnpauseRequest.m_bPause)); break; case eRemotePauseUnpauseActionScan: g_pOptions->SetPauseScan(ntohl(PauseUnpauseRequest.m_bPause)); break; } 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)); SendBoolResponse(true, "Rate-Command completed successfully"); } void DumpDebugBinCommand::Execute() { SNZBDumpDebugRequest DumpDebugRequest; if (!ReceiveRequest(&DumpDebugRequest, sizeof(DumpDebugRequest))) { return; } g_pLog->LogDebugInfo(); SendBoolResponse(true, "Debug-Command completed successfully"); } void ShutdownBinCommand::Execute() { SNZBShutdownRequest ShutdownRequest; if (!ReceiveRequest(&ShutdownRequest, sizeof(ShutdownRequest))) { return; } SendBoolResponse(true, "Stopping server"); ExitProc(); } void ReloadBinCommand::Execute() { SNZBReloadRequest ReloadRequest; if (!ReceiveRequest(&ReloadRequest, sizeof(ReloadRequest))) { return; } SendBoolResponse(true, "Reloading server"); Reload(); } void VersionBinCommand::Execute() { SNZBVersionRequest VersionRequest; if (!ReceiveRequest(&VersionRequest, sizeof(VersionRequest))) { return; } SendBoolResponse(true, Util::VersionRevision()); } void DownloadBinCommand::Execute() { SNZBDownloadRequest DownloadRequest; if (!ReceiveRequest(&DownloadRequest, sizeof(DownloadRequest))) { return; } int iBufLen = ntohl(DownloadRequest.m_iTrailingDataLength); char* szNZBContent = (char*)malloc(iBufLen); if (!m_pConnection->Recv(szNZBContent, iBufLen)) { error("invalid request"); free(szNZBContent); return; } int iPriority = ntohl(DownloadRequest.m_iPriority); bool bAddPaused = ntohl(DownloadRequest.m_bAddPaused); bool bAddTop = ntohl(DownloadRequest.m_bAddFirst); int iDupeMode = ntohl(DownloadRequest.m_iDupeMode); int iDupeScore = ntohl(DownloadRequest.m_iDupeScore); bool bOK = false; if (!strncasecmp(szNZBContent, "http://", 6) || !strncasecmp(szNZBContent, "https://", 7)) { // add url NZBInfo* pNZBInfo = new NZBInfo(); pNZBInfo->SetKind(NZBInfo::nkUrl); pNZBInfo->SetURL(szNZBContent); pNZBInfo->SetFilename(DownloadRequest.m_szNZBFilename); pNZBInfo->SetCategory(DownloadRequest.m_szCategory); pNZBInfo->SetPriority(iPriority); pNZBInfo->SetAddUrlPaused(bAddPaused); pNZBInfo->SetDupeKey(DownloadRequest.m_szDupeKey); pNZBInfo->SetDupeScore(iDupeScore); pNZBInfo->SetDupeMode((EDupeMode)iDupeMode); DownloadQueue* pDownloadQueue = DownloadQueue::Lock(); pDownloadQueue->GetQueue()->Add(pNZBInfo, bAddTop); pDownloadQueue->Save(); DownloadQueue::Unlock(); bOK = true; } else { bOK = g_pScanner->AddExternalFile(DownloadRequest.m_szNZBFilename, DownloadRequest.m_szCategory, iPriority, DownloadRequest.m_szDupeKey, iDupeScore, (EDupeMode)iDupeMode, NULL, bAddTop, bAddPaused, NULL, NULL, szNZBContent, iBufLen, NULL) != Scanner::asFailed; } char tmp[1024]; snprintf(tmp, 1024, bOK ? "Collection %s added to queue" : "Download Request failed for %s", Util::BaseFileName(DownloadRequest.m_szNZBFilename)); tmp[1024-1] = '\0'; SendBoolResponse(bOK, tmp); free(szNZBContent); } 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(SNZBListResponseFileEntry)); ListResponse.m_bRegExValid = 0; char* buf = NULL; int bufsize = 0; if (ntohl(ListRequest.m_bFileList)) { eRemoteMatchMode eMatchMode = (eRemoteMatchMode)ntohl(ListRequest.m_iMatchMode); bool bMatchGroup = ntohl(ListRequest.m_bMatchGroup); const char* szPattern = ListRequest.m_szPattern; RegEx *pRegEx = NULL; if (eMatchMode == eRemoteMatchModeRegEx) { pRegEx = new RegEx(szPattern); ListResponse.m_bRegExValid = pRegEx->IsValid(); } // Make a data structure and copy all the elements of the list into it DownloadQueue* pDownloadQueue = DownloadQueue::Lock(); // calculate required buffer size for nzbs int iNrNZBEntries = pDownloadQueue->GetQueue()->size(); int iNrPPPEntries = 0; bufsize += iNrNZBEntries * sizeof(SNZBListResponseNZBEntry); for (NZBList::iterator it = pDownloadQueue->GetQueue()->begin(); it != pDownloadQueue->GetQueue()->end(); it++) { NZBInfo* pNZBInfo = *it; bufsize += strlen(pNZBInfo->GetFilename()) + 1; bufsize += strlen(pNZBInfo->GetName()) + 1; bufsize += strlen(pNZBInfo->GetDestDir()) + 1; bufsize += strlen(pNZBInfo->GetCategory()) + 1; bufsize += strlen(pNZBInfo->GetQueuedFilename()) + 1; // align struct to 4-bytes, needed by ARM-processor (and may be others) bufsize += bufsize % 4 > 0 ? 4 - bufsize % 4 : 0; // calculate required buffer size for pp-parameters for (NZBParameterList::iterator it = pNZBInfo->GetParameters()->begin(); it != pNZBInfo->GetParameters()->end(); it++) { NZBParameter* pNZBParameter = *it; bufsize += sizeof(SNZBListResponsePPPEntry); bufsize += strlen(pNZBParameter->GetName()) + 1; bufsize += strlen(pNZBParameter->GetValue()) + 1; // align struct to 4-bytes, needed by ARM-processor (and may be others) bufsize += bufsize % 4 > 0 ? 4 - bufsize % 4 : 0; iNrPPPEntries++; } } // calculate required buffer size for files int iNrFileEntries = 0; for (NZBList::iterator it = pDownloadQueue->GetQueue()->begin(); it != pDownloadQueue->GetQueue()->end(); it++) { NZBInfo* pNZBInfo = *it; for (FileList::iterator it2 = pNZBInfo->GetFileList()->begin(); it2 != pNZBInfo->GetFileList()->end(); it2++) { FileInfo* pFileInfo = *it2; iNrFileEntries++; bufsize += sizeof(SNZBListResponseFileEntry); bufsize += strlen(pFileInfo->GetSubject()) + 1; bufsize += strlen(pFileInfo->GetFilename()) + 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; // write nzb entries for (NZBList::iterator it = pDownloadQueue->GetQueue()->begin(); it != pDownloadQueue->GetQueue()->end(); it++) { NZBInfo* pNZBInfo = *it; SNZBListResponseNZBEntry* pListAnswer = (SNZBListResponseNZBEntry*) bufptr; unsigned long iSizeHi, iSizeLo, iRemainingSizeHi, iRemainingSizeLo, iPausedSizeHi, iPausedSizeLo; Util::SplitInt64(pNZBInfo->GetSize(), &iSizeHi, &iSizeLo); Util::SplitInt64(pNZBInfo->GetRemainingSize(), &iRemainingSizeHi, &iRemainingSizeLo); Util::SplitInt64(pNZBInfo->GetPausedSize(), &iPausedSizeHi, &iPausedSizeLo); pListAnswer->m_iID = htonl(pNZBInfo->GetID()); pListAnswer->m_iKind = htonl(pNZBInfo->GetKind()); pListAnswer->m_iSizeLo = htonl(iSizeLo); pListAnswer->m_iSizeHi = htonl(iSizeHi); pListAnswer->m_iRemainingSizeLo = htonl(iRemainingSizeLo); pListAnswer->m_iRemainingSizeHi = htonl(iRemainingSizeHi); pListAnswer->m_iPausedSizeLo = htonl(iPausedSizeLo); pListAnswer->m_iPausedSizeHi = htonl(iPausedSizeHi); pListAnswer->m_iPausedCount = htonl(pNZBInfo->GetPausedFileCount()); pListAnswer->m_iRemainingParCount = htonl(pNZBInfo->GetRemainingParCount()); pListAnswer->m_iPriority = htonl(pNZBInfo->GetPriority()); pListAnswer->m_bMatch = htonl(bMatchGroup && (!pRegEx || pRegEx->Match(pNZBInfo->GetName()))); pListAnswer->m_iFilenameLen = htonl(strlen(pNZBInfo->GetFilename()) + 1); pListAnswer->m_iNameLen = htonl(strlen(pNZBInfo->GetName()) + 1); pListAnswer->m_iDestDirLen = htonl(strlen(pNZBInfo->GetDestDir()) + 1); pListAnswer->m_iCategoryLen = htonl(strlen(pNZBInfo->GetCategory()) + 1); pListAnswer->m_iQueuedFilenameLen = htonl(strlen(pNZBInfo->GetQueuedFilename()) + 1); bufptr += sizeof(SNZBListResponseNZBEntry); strcpy(bufptr, pNZBInfo->GetFilename()); bufptr += ntohl(pListAnswer->m_iFilenameLen); strcpy(bufptr, pNZBInfo->GetName()); bufptr += ntohl(pListAnswer->m_iNameLen); strcpy(bufptr, pNZBInfo->GetDestDir()); bufptr += ntohl(pListAnswer->m_iDestDirLen); strcpy(bufptr, pNZBInfo->GetCategory()); bufptr += ntohl(pListAnswer->m_iCategoryLen); strcpy(bufptr, pNZBInfo->GetQueuedFilename()); bufptr += ntohl(pListAnswer->m_iQueuedFilenameLen); // align struct to 4-bytes, needed by ARM-processor (and may be others) if ((size_t)bufptr % 4 > 0) { pListAnswer->m_iQueuedFilenameLen = htonl(ntohl(pListAnswer->m_iQueuedFilenameLen) + 4 - (size_t)bufptr % 4); memset(bufptr, 0, 4 - (size_t)bufptr % 4); //suppress valgrind warning "uninitialized data" bufptr += 4 - (size_t)bufptr % 4; } } // write ppp entries int iNZBIndex = 1; for (NZBList::iterator it = pDownloadQueue->GetQueue()->begin(); it != pDownloadQueue->GetQueue()->end(); it++, iNZBIndex++) { NZBInfo* pNZBInfo = *it; for (NZBParameterList::iterator it = pNZBInfo->GetParameters()->begin(); it != pNZBInfo->GetParameters()->end(); it++) { NZBParameter* pNZBParameter = *it; SNZBListResponsePPPEntry* pListAnswer = (SNZBListResponsePPPEntry*) bufptr; pListAnswer->m_iNZBIndex = htonl(iNZBIndex); pListAnswer->m_iNameLen = htonl(strlen(pNZBParameter->GetName()) + 1); pListAnswer->m_iValueLen = htonl(strlen(pNZBParameter->GetValue()) + 1); bufptr += sizeof(SNZBListResponsePPPEntry); strcpy(bufptr, pNZBParameter->GetName()); bufptr += ntohl(pListAnswer->m_iNameLen); strcpy(bufptr, pNZBParameter->GetValue()); bufptr += ntohl(pListAnswer->m_iValueLen); // align struct to 4-bytes, needed by ARM-processor (and may be others) if ((size_t)bufptr % 4 > 0) { pListAnswer->m_iValueLen = htonl(ntohl(pListAnswer->m_iValueLen) + 4 - (size_t)bufptr % 4); memset(bufptr, 0, 4 - (size_t)bufptr % 4); //suppress valgrind warning "uninitialized data" bufptr += 4 - (size_t)bufptr % 4; } } } // write file entries for (NZBList::iterator it = pDownloadQueue->GetQueue()->begin(); it != pDownloadQueue->GetQueue()->end(); it++) { NZBInfo* pNZBInfo = *it; for (FileList::iterator it2 = pNZBInfo->GetFileList()->begin(); it2 != pNZBInfo->GetFileList()->end(); it2++) { FileInfo* pFileInfo = *it2; unsigned long iSizeHi, iSizeLo; SNZBListResponseFileEntry* pListAnswer = (SNZBListResponseFileEntry*) bufptr; pListAnswer->m_iID = htonl(pFileInfo->GetID()); int iNZBIndex = 0; for (unsigned int i = 0; i < pDownloadQueue->GetQueue()->size(); i++) { iNZBIndex++; if (pDownloadQueue->GetQueue()->at(i) == pFileInfo->GetNZBInfo()) { break; } } pListAnswer->m_iNZBIndex = htonl(iNZBIndex); if (pRegEx && !bMatchGroup) { char szFilename[MAX_PATH]; snprintf(szFilename, sizeof(szFilename) - 1, "%s/%s", pFileInfo->GetNZBInfo()->GetName(), Util::BaseFileName(pFileInfo->GetFilename())); pListAnswer->m_bMatch = htonl(pRegEx->Match(szFilename)); } 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_iActiveDownloads = htonl(pFileInfo->GetActiveDownloads()); pListAnswer->m_iSubjectLen = htonl(strlen(pFileInfo->GetSubject()) + 1); pListAnswer->m_iFilenameLen = htonl(strlen(pFileInfo->GetFilename()) + 1); bufptr += sizeof(SNZBListResponseFileEntry); strcpy(bufptr, pFileInfo->GetSubject()); bufptr += ntohl(pListAnswer->m_iSubjectLen); strcpy(bufptr, pFileInfo->GetFilename()); bufptr += ntohl(pListAnswer->m_iFilenameLen); // align struct to 4-bytes, needed by ARM-processor (and may be others) if ((size_t)bufptr % 4 > 0) { pListAnswer->m_iFilenameLen = htonl(ntohl(pListAnswer->m_iFilenameLen) + 4 - (size_t)bufptr % 4); memset(bufptr, 0, 4 - (size_t)bufptr % 4); //suppress valgrind warning "uninitialized data" bufptr += 4 - (size_t)bufptr % 4; } } } DownloadQueue::Unlock(); delete pRegEx; ListResponse.m_iNrTrailingNZBEntries = htonl(iNrNZBEntries); ListResponse.m_iNrTrailingPPPEntries = htonl(iNrPPPEntries); ListResponse.m_iNrTrailingFileEntries = htonl(iNrFileEntries); ListResponse.m_iTrailingDataLength = htonl(bufsize); } if (htonl(ListRequest.m_bServerState)) { DownloadQueue *pDownloadQueue = DownloadQueue::Lock(); int iPostJobCount = 0; for (NZBList::iterator it = pDownloadQueue->GetQueue()->begin(); it != pDownloadQueue->GetQueue()->end(); it++) { NZBInfo* pNZBInfo = *it; iPostJobCount += pNZBInfo->GetPostInfo() ? 1 : 0; } long long lRemainingSize; pDownloadQueue->CalcRemainingSize(&lRemainingSize, NULL); DownloadQueue::Unlock(); unsigned long iSizeHi, iSizeLo; ListResponse.m_iDownloadRate = htonl(g_pStatMeter->CalcCurrentDownloadSpeed()); Util::SplitInt64(lRemainingSize, &iSizeHi, &iSizeLo); ListResponse.m_iRemainingSizeHi = htonl(iSizeHi); ListResponse.m_iRemainingSizeLo = htonl(iSizeLo); ListResponse.m_iDownloadLimit = htonl(g_pOptions->GetDownloadRate()); ListResponse.m_bDownloadPaused = htonl(g_pOptions->GetPauseDownload()); ListResponse.m_bPostPaused = htonl(g_pOptions->GetPausePostProcess()); ListResponse.m_bScanPaused = htonl(g_pOptions->GetPauseScan()); ListResponse.m_iThreadCount = htonl(Thread::GetThreadCount() - 1); // not counting itself ListResponse.m_iPostJobCount = htonl(iPostJobCount); int iUpTimeSec, iDnTimeSec; long long iAllBytes; bool bStandBy; g_pStatMeter->CalcTotalStat(&iUpTimeSec, &iDnTimeSec, &iAllBytes, &bStandBy); ListResponse.m_iUpTimeSec = htonl(iUpTimeSec); ListResponse.m_iDownloadTimeSec = htonl(iDnTimeSec); ListResponse.m_bDownloadStandBy = htonl(bStandBy); Util::SplitInt64(iAllBytes, &iSizeHi, &iSizeLo); ListResponse.m_iDownloadedBytesHi = htonl(iSizeHi); ListResponse.m_iDownloadedBytesLo = htonl(iSizeLo); } // Send the request answer m_pConnection->Send((char*) &ListResponse, sizeof(ListResponse)); // Send the data if (bufsize > 0) { m_pConnection->Send(buf, bufsize); } free(buf); } void LogBinCommand::Execute() { SNZBLogRequest LogRequest; if (!ReceiveRequest(&LogRequest, sizeof(LogRequest))) { return; } MessageList* 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); memset(bufptr, 0, 4 - (size_t)bufptr % 4); //suppress valgrind warning "uninitialized data" 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 m_pConnection->Send((char*) &LogResponse, sizeof(LogResponse)); // Send the data if (bufsize > 0) { m_pConnection->Send(buf, bufsize); } free(buf); } void EditQueueBinCommand::Execute() { SNZBEditQueueRequest EditQueueRequest; if (!ReceiveRequest(&EditQueueRequest, sizeof(EditQueueRequest))) { return; } int iNrIDEntries = ntohl(EditQueueRequest.m_iNrTrailingIDEntries); int iNrNameEntries = ntohl(EditQueueRequest.m_iNrTrailingNameEntries); int iNameEntriesLen = ntohl(EditQueueRequest.m_iTrailingNameEntriesLen); int iAction = ntohl(EditQueueRequest.m_iAction); int iMatchMode = ntohl(EditQueueRequest.m_iMatchMode); int iOffset = ntohl(EditQueueRequest.m_iOffset); int iTextLen = ntohl(EditQueueRequest.m_iTextLen); unsigned int iBufLength = ntohl(EditQueueRequest.m_iTrailingDataLength); if (iNrIDEntries * sizeof(int32_t) + iTextLen + iNameEntriesLen != iBufLength) { error("Invalid struct size"); return; } char* pBuf = (char*)malloc(iBufLength); if (!m_pConnection->Recv(pBuf, iBufLength)) { error("invalid request"); free(pBuf); return; } if (iNrIDEntries <= 0 && iNrNameEntries <= 0) { SendBoolResponse(false, "Edit-Command failed: no IDs/Names specified"); return; } char* szText = iTextLen > 0 ? pBuf : NULL; int32_t* pIDs = (int32_t*)(pBuf + iTextLen); char* pNames = (pBuf + iTextLen + iNrIDEntries * sizeof(int32_t)); IDList cIDList; NameList cNameList; if (iNrIDEntries > 0) { cIDList.reserve(iNrIDEntries); for (int i = 0; i < iNrIDEntries; i++) { cIDList.push_back(ntohl(pIDs[i])); } } if (iNrNameEntries > 0) { cNameList.reserve(iNrNameEntries); for (int i = 0; i < iNrNameEntries; i++) { cNameList.push_back(pNames); pNames += strlen(pNames) + 1; } } DownloadQueue* pDownloadQueue = DownloadQueue::Lock(); bool bOK = pDownloadQueue->EditList( iNrIDEntries > 0 ? &cIDList : NULL, iNrNameEntries > 0 ? &cNameList : NULL, (DownloadQueue::EMatchMode)iMatchMode, (DownloadQueue::EEditAction)iAction, iOffset, szText); DownloadQueue::Unlock(); free(pBuf); if (bOK) { SendBoolResponse(true, "Edit-Command completed successfully"); } else { #ifndef HAVE_REGEX_H if ((QueueEditor::EMatchMode)iMatchMode == QueueEditor::mmRegEx) { SendBoolResponse(false, "Edit-Command failed: the program was compiled without RegEx-support"); return; } #endif 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 NZBList* pNZBList = DownloadQueue::Lock()->GetQueue(); // calculate required buffer size int NrEntries = 0; for (NZBList::iterator it = pNZBList->begin(); it != pNZBList->end(); it++) { NZBInfo* pNZBInfo = *it; PostInfo* pPostInfo = pNZBInfo->GetPostInfo(); if (!pPostInfo) { continue; } NrEntries++; bufsize += sizeof(SNZBPostQueueResponseEntry); bufsize += strlen(pPostInfo->GetNZBInfo()->GetFilename()) + 1; bufsize += strlen(pPostInfo->GetNZBInfo()->GetName()) + 1; bufsize += strlen(pPostInfo->GetNZBInfo()->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 (NZBList::iterator it = pNZBList->begin(); it != pNZBList->end(); it++) { NZBInfo* pNZBInfo = *it; PostInfo* pPostInfo = pNZBInfo->GetPostInfo(); if (!pPostInfo) { continue; } SNZBPostQueueResponseEntry* pPostQueueAnswer = (SNZBPostQueueResponseEntry*) bufptr; pPostQueueAnswer->m_iID = htonl(pNZBInfo->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->GetNZBInfo()->GetFilename()) + 1); pPostQueueAnswer->m_iInfoNameLen = htonl(strlen(pPostInfo->GetNZBInfo()->GetName()) + 1); pPostQueueAnswer->m_iDestDirLen = htonl(strlen(pPostInfo->GetNZBInfo()->GetDestDir()) + 1); pPostQueueAnswer->m_iProgressLabelLen = htonl(strlen(pPostInfo->GetProgressLabel()) + 1); bufptr += sizeof(SNZBPostQueueResponseEntry); strcpy(bufptr, pPostInfo->GetNZBInfo()->GetFilename()); bufptr += ntohl(pPostQueueAnswer->m_iNZBFilenameLen); strcpy(bufptr, pPostInfo->GetNZBInfo()->GetName()); bufptr += ntohl(pPostQueueAnswer->m_iInfoNameLen); strcpy(bufptr, pPostInfo->GetNZBInfo()->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); memset(bufptr, 0, 4 - (size_t)bufptr % 4); //suppress valgrind warning "uninitialized data" bufptr += 4 - (size_t)bufptr % 4; } } DownloadQueue::Unlock(); PostQueueResponse.m_iNrTrailingEntries = htonl(NrEntries); PostQueueResponse.m_iTrailingDataLength = htonl(bufsize); // Send the request answer m_pConnection->Send((char*) &PostQueueResponse, sizeof(PostQueueResponse)); // Send the data if (bufsize > 0) { m_pConnection->Send(buf, bufsize); } free(buf); } void WriteLogBinCommand::Execute() { SNZBWriteLogRequest WriteLogRequest; if (!ReceiveRequest(&WriteLogRequest, sizeof(WriteLogRequest))) { return; } char* pRecvBuffer = (char*)malloc(ntohl(WriteLogRequest.m_iTrailingDataLength) + 1); if (!m_pConnection->Recv(pRecvBuffer, ntohl(WriteLogRequest.m_iTrailingDataLength))) { error("invalid request"); free(pRecvBuffer); return; } 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); } void ScanBinCommand::Execute() { SNZBScanRequest ScanRequest; if (!ReceiveRequest(&ScanRequest, sizeof(ScanRequest))) { return; } bool bSyncMode = ntohl(ScanRequest.m_bSyncMode); g_pScanner->ScanNZBDir(bSyncMode); SendBoolResponse(true, bSyncMode ? "Scan-Command completed" : "Scan-Command scheduled successfully"); } void HistoryBinCommand::Execute() { SNZBHistoryRequest HistoryRequest; if (!ReceiveRequest(&HistoryRequest, sizeof(HistoryRequest))) { return; } bool bShowHidden = ntohl(HistoryRequest.m_bHidden); SNZBHistoryResponse HistoryResponse; memset(&HistoryResponse, 0, sizeof(HistoryResponse)); HistoryResponse.m_MessageBase.m_iSignature = htonl(NZBMESSAGE_SIGNATURE); HistoryResponse.m_MessageBase.m_iStructSize = htonl(sizeof(HistoryResponse)); HistoryResponse.m_iEntrySize = htonl(sizeof(SNZBHistoryResponseEntry)); char* buf = NULL; int bufsize = 0; // Make a data structure and copy all the elements of the list into it DownloadQueue* pDownloadQueue = DownloadQueue::Lock(); // calculate required buffer size for nzbs int iNrEntries = 0; for (HistoryList::iterator it = pDownloadQueue->GetHistory()->begin(); it != pDownloadQueue->GetHistory()->end(); it++) { HistoryInfo* pHistoryInfo = *it; if (pHistoryInfo->GetKind() != HistoryInfo::hkDup || bShowHidden) { iNrEntries++; } } bufsize += iNrEntries * sizeof(SNZBHistoryResponseEntry); for (HistoryList::iterator it = pDownloadQueue->GetHistory()->begin(); it != pDownloadQueue->GetHistory()->end(); it++) { HistoryInfo* pHistoryInfo = *it; if (pHistoryInfo->GetKind() != HistoryInfo::hkDup || bShowHidden) { char szNicename[1024]; pHistoryInfo->GetName(szNicename, sizeof(szNicename)); bufsize += strlen(szNicename) + 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; // write nzb entries for (HistoryList::iterator it = pDownloadQueue->GetHistory()->begin(); it != pDownloadQueue->GetHistory()->end(); it++) { HistoryInfo* pHistoryInfo = *it; if (pHistoryInfo->GetKind() != HistoryInfo::hkDup || bShowHidden) { SNZBHistoryResponseEntry* pListAnswer = (SNZBHistoryResponseEntry*) bufptr; pListAnswer->m_iID = htonl(pHistoryInfo->GetID()); pListAnswer->m_iKind = htonl((int)pHistoryInfo->GetKind()); pListAnswer->m_tTime = htonl((int)pHistoryInfo->GetTime()); char szNicename[1024]; pHistoryInfo->GetName(szNicename, sizeof(szNicename)); pListAnswer->m_iNicenameLen = htonl(strlen(szNicename) + 1); if (pHistoryInfo->GetKind() == HistoryInfo::hkNzb) { NZBInfo* pNZBInfo = pHistoryInfo->GetNZBInfo(); unsigned long iSizeHi, iSizeLo; Util::SplitInt64(pNZBInfo->GetSize(), &iSizeHi, &iSizeLo); pListAnswer->m_iSizeLo = htonl(iSizeLo); pListAnswer->m_iSizeHi = htonl(iSizeHi); pListAnswer->m_iFileCount = htonl(pNZBInfo->GetFileCount()); pListAnswer->m_iParStatus = htonl(pNZBInfo->GetParStatus()); pListAnswer->m_iScriptStatus = htonl(pNZBInfo->GetScriptStatuses()->CalcTotalStatus()); } else if (pHistoryInfo->GetKind() == HistoryInfo::hkDup && bShowHidden) { DupInfo* pDupInfo = pHistoryInfo->GetDupInfo(); unsigned long iSizeHi, iSizeLo; Util::SplitInt64(pDupInfo->GetSize(), &iSizeHi, &iSizeLo); pListAnswer->m_iSizeLo = htonl(iSizeLo); pListAnswer->m_iSizeHi = htonl(iSizeHi); } else if (pHistoryInfo->GetKind() == HistoryInfo::hkUrl) { NZBInfo* pNZBInfo = pHistoryInfo->GetNZBInfo(); pListAnswer->m_iUrlStatus = htonl(pNZBInfo->GetUrlStatus()); } bufptr += sizeof(SNZBHistoryResponseEntry); strcpy(bufptr, szNicename); bufptr += ntohl(pListAnswer->m_iNicenameLen); // align struct to 4-bytes, needed by ARM-processor (and may be others) if ((size_t)bufptr % 4 > 0) { pListAnswer->m_iNicenameLen = htonl(ntohl(pListAnswer->m_iNicenameLen) + 4 - (size_t)bufptr % 4); memset(bufptr, 0, 4 - (size_t)bufptr % 4); //suppress valgrind warning "uninitialized data" bufptr += 4 - (size_t)bufptr % 4; } } } DownloadQueue::Unlock(); HistoryResponse.m_iNrTrailingEntries = htonl(iNrEntries); HistoryResponse.m_iTrailingDataLength = htonl(bufsize); // Send the request answer m_pConnection->Send((char*) &HistoryResponse, sizeof(HistoryResponse)); // Send the data if (bufsize > 0) { m_pConnection->Send(buf, bufsize); } free(buf); }