/* * This file is part of nzbget * * Copyright (C) 2007-2011 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$ * $Date$ * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef WIN32 #include "win32.h" #endif #include #include #include #include #include #ifndef WIN32 #include #endif #include "nzbget.h" #include "XmlRpc.h" #include "Log.h" #include "Options.h" #include "QueueCoordinator.h" #include "UrlCoordinator.h" #include "QueueEditor.h" #include "PrePostProcessor.h" #include "Util.h" extern Options* g_pOptions; extern QueueCoordinator* g_pQueueCoordinator; extern UrlCoordinator* g_pUrlCoordinator; extern PrePostProcessor* g_pPrePostProcessor; extern void ExitProc(); extern void Reload(); //***************************************************************** // StringBuilder StringBuilder::StringBuilder() { m_szBuffer = NULL; m_iBufferSize = 0; m_iUsedSize = 0; } StringBuilder::~StringBuilder() { if (m_szBuffer) { free(m_szBuffer); } } void StringBuilder::Append(const char* szStr) { int iPartLen = strlen(szStr); if (m_iUsedSize + iPartLen + 1 > m_iBufferSize) { m_iBufferSize += iPartLen + 10240; m_szBuffer = (char*)realloc(m_szBuffer, m_iBufferSize); } strcpy(m_szBuffer + m_iUsedSize, szStr); m_iUsedSize += iPartLen; m_szBuffer[m_iUsedSize] = '\0'; } //***************************************************************** // XmlRpcProcessor XmlRpcProcessor::XmlRpcProcessor() { m_szRequest = NULL; m_eProtocol = rpUndefined; m_eHttpMethod = hmPost; m_szUrl = NULL; m_szContentType = NULL; } XmlRpcProcessor::~XmlRpcProcessor() { if (m_szUrl) { free(m_szUrl); } } void XmlRpcProcessor::SetUrl(const char* szUrl) { m_szUrl = strdup(szUrl); } bool XmlRpcProcessor::IsRpcRequest(const char* szUrl) { return !strcmp(szUrl, "/xmlrpc") || !strncmp(szUrl, "/xmlrpc/", 8) || !strcmp(szUrl, "/jsonrpc") || !strncmp(szUrl, "/jsonrpc/", 9) || !strcmp(szUrl, "/jsonprpc") || !strncmp(szUrl, "/jsonprpc/", 10); } void XmlRpcProcessor::Execute() { m_eProtocol = rpUndefined; if (!strcmp(m_szUrl, "/xmlrpc") || !strncmp(m_szUrl, "/xmlrpc/", 8)) { m_eProtocol = XmlRpcProcessor::rpXmlRpc; } else if (!strcmp(m_szUrl, "/jsonrpc") || !strncmp(m_szUrl, "/jsonrpc/", 9)) { m_eProtocol = rpJsonRpc; } else if (!strcmp(m_szUrl, "/jsonprpc") || !strncmp(m_szUrl, "/jsonprpc/", 10)) { m_eProtocol = rpJsonPRpc; } else { error("internal error: invalid rpc-request: %s", m_szUrl); return; } Dispatch(); } void XmlRpcProcessor::Dispatch() { char* szRequest = m_szRequest; char szMethodName[100]; szMethodName[0] = '\0'; if (m_eHttpMethod == hmGet) { szRequest = m_szUrl + 1; char* pstart = strchr(szRequest, '/'); if (pstart) { char* pend = strchr(pstart + 1, '?'); if (pend) { int iLen = (int)(pend - pstart - 1 < (int)sizeof(szMethodName) - 1 ? pend - pstart - 1 : (int)sizeof(szMethodName) - 1); strncpy(szMethodName, pstart + 1, iLen); szMethodName[iLen] = '\0'; szRequest = pend + 1; } else { strncpy(szMethodName, pstart + 1, sizeof(szMethodName)); szMethodName[sizeof(szMethodName) - 1] = '\0'; szRequest = szRequest + strlen(szRequest); } } } else if (m_eProtocol == rpXmlRpc) { WebUtil::XmlParseTagValue(m_szRequest, "methodName", szMethodName, sizeof(szMethodName), NULL); } else if (m_eProtocol == rpJsonRpc) { int iValueLen = 0; if (const char* szMethodPtr = WebUtil::JsonFindField(m_szRequest, "method", &iValueLen)) { strncpy(szMethodName, szMethodPtr + 1, iValueLen - 2); szMethodName[iValueLen - 2] = '\0'; } } debug("MethodName=%s", szMethodName); if (!strcasecmp(szMethodName, "system.multicall") && m_eProtocol == rpXmlRpc && m_eHttpMethod == hmPost) { MutliCall(); } else { XmlCommand* command = CreateCommand(szMethodName); command->SetRequest(szRequest); command->SetProtocol(m_eProtocol); command->SetHttpMethod(m_eHttpMethod); command->PrepareParams(); command->Execute(); BuildResponse(command->GetResponse(), command->GetCallbackFunc(), command->GetFault()); delete command; } } void XmlRpcProcessor::MutliCall() { bool bError = false; StringBuilder cStringBuilder; cStringBuilder.Append(""); char* szRequestPtr = m_szRequest; char* szCallEnd = strstr(szRequestPtr, ""); while (szCallEnd) { *szCallEnd = '\0'; debug("MutliCall, request=%s", szRequestPtr); char* szNameEnd = strstr(szRequestPtr, ""); if (!szNameEnd) { bError = true; break; } char szMethodName[100]; szMethodName[0] = '\0'; WebUtil::XmlParseTagValue(szNameEnd, "string", szMethodName, sizeof(szMethodName), NULL); debug("MutliCall, MethodName=%s", szMethodName); XmlCommand* command = CreateCommand(szMethodName); command->SetRequest(szRequestPtr); command->Execute(); debug("MutliCall, Response=%s", command->GetResponse()); bool bFault = !strncmp(command->GetResponse(), "", 7); bool bArray = !bFault && !strncmp(command->GetResponse(), "", 7); if (!bFault && !bArray) { cStringBuilder.Append(""); } cStringBuilder.Append(""); cStringBuilder.Append(command->GetResponse()); cStringBuilder.Append(""); if (!bFault && !bArray) { cStringBuilder.Append(""); } delete command; szRequestPtr = szCallEnd + 9; //strlen("") szCallEnd = strstr(szRequestPtr, ""); } if (bError) { XmlCommand* command = new ErrorXmlCommand(4, "Parse error"); command->SetRequest(m_szRequest); command->SetProtocol(rpXmlRpc); command->PrepareParams(); command->Execute(); BuildResponse(command->GetResponse(), "", command->GetFault()); delete command; } else { cStringBuilder.Append(""); BuildResponse(cStringBuilder.GetBuffer(), "", false); } } void XmlRpcProcessor::BuildResponse(const char* szResponse, const char* szCallbackFunc, bool bFault) { const char XML_HEADER[] = "\n\n"; const char XML_FOOTER[] = ""; const char XML_OK_OPEN[] = ""; const char XML_OK_CLOSE[] = "\n"; const char XML_FAULT_OPEN[] = ""; const char XML_FAULT_CLOSE[] = "\n"; const char JSON_HEADER[] = "{\n\"version\" : \"1.1\",\n"; const char JSON_FOOTER[] = "\n}"; const char JSON_OK_OPEN[] = "\"result\" : "; const char JSON_OK_CLOSE[] = ""; const char JSON_FAULT_OPEN[] = "\"error\" : "; const char JSON_FAULT_CLOSE[] = ""; const char JSONP_CALLBACK_HEADER[] = "("; const char JSONP_CALLBACK_FOOTER[] = ")"; bool bXmlRpc = m_eProtocol == rpXmlRpc; const char* szCallbackHeader = m_eProtocol == rpJsonPRpc ? JSONP_CALLBACK_HEADER : ""; const char* szHeader = bXmlRpc ? XML_HEADER : JSON_HEADER; const char* szFooter = bXmlRpc ? XML_FOOTER : JSON_FOOTER; const char* szOpenTag = bFault ? (bXmlRpc ? XML_FAULT_OPEN : JSON_FAULT_OPEN) : (bXmlRpc ? XML_OK_OPEN : JSON_OK_OPEN); const char* szCloseTag = bFault ? (bXmlRpc ? XML_FAULT_CLOSE : JSON_FAULT_CLOSE ) : (bXmlRpc ? XML_OK_CLOSE : JSON_OK_CLOSE); const char* szCallbackFooter = m_eProtocol == rpJsonPRpc ? JSONP_CALLBACK_FOOTER : ""; debug("Response=%s", szResponse); if (szCallbackFunc) { m_cResponse.Append(szCallbackFunc); } m_cResponse.Append(szCallbackHeader); m_cResponse.Append(szHeader); m_cResponse.Append(szOpenTag); m_cResponse.Append(szResponse); m_cResponse.Append(szCloseTag); m_cResponse.Append(szFooter); m_cResponse.Append(szCallbackFooter); m_szContentType = bXmlRpc ? "text/xml" : "application/json"; } XmlCommand* XmlRpcProcessor::CreateCommand(const char* szMethodName) { XmlCommand* command = NULL; if (!strcasecmp(szMethodName, "pause") || !strcasecmp(szMethodName, "pausedownload")) { command = new PauseUnpauseXmlCommand(true, PauseUnpauseXmlCommand::paDownload); } else if (!strcasecmp(szMethodName, "resume") || !strcasecmp(szMethodName, "resumedownload")) { command = new PauseUnpauseXmlCommand(false, PauseUnpauseXmlCommand::paDownload); } else if (!strcasecmp(szMethodName, "pausedownload2")) { command = new PauseUnpauseXmlCommand(true, PauseUnpauseXmlCommand::paDownload2); } else if (!strcasecmp(szMethodName, "resumedownload2")) { command = new PauseUnpauseXmlCommand(false, PauseUnpauseXmlCommand::paDownload2); } else if (!strcasecmp(szMethodName, "shutdown")) { command = new ShutdownXmlCommand(); } else if (!strcasecmp(szMethodName, "reload")) { command = new ReloadXmlCommand(); } else if (!strcasecmp(szMethodName, "version")) { command = new VersionXmlCommand(); } else if (!strcasecmp(szMethodName, "dump")) { command = new DumpDebugXmlCommand(); } else if (!strcasecmp(szMethodName, "rate")) { command = new SetDownloadRateXmlCommand(); } else if (!strcasecmp(szMethodName, "status")) { command = new StatusXmlCommand(); } else if (!strcasecmp(szMethodName, "log")) { command = new LogXmlCommand(); } else if (!strcasecmp(szMethodName, "listfiles")) { command = new ListFilesXmlCommand(); } else if (!strcasecmp(szMethodName, "listgroups")) { command = new ListGroupsXmlCommand(); } else if (!strcasecmp(szMethodName, "editqueue")) { command = new EditQueueXmlCommand(); } else if (!strcasecmp(szMethodName, "append")) { command = new DownloadXmlCommand(); } else if (!strcasecmp(szMethodName, "postqueue")) { command = new PostQueueXmlCommand(); } else if (!strcasecmp(szMethodName, "writelog")) { command = new WriteLogXmlCommand(); } else if (!strcasecmp(szMethodName, "clearlog")) { command = new ClearLogXmlCommand(); } else if (!strcasecmp(szMethodName, "scan")) { command = new ScanXmlCommand(); } else if (!strcasecmp(szMethodName, "pausepost")) { command = new PauseUnpauseXmlCommand(true, PauseUnpauseXmlCommand::paPostProcess); } else if (!strcasecmp(szMethodName, "resumepost")) { command = new PauseUnpauseXmlCommand(false, PauseUnpauseXmlCommand::paPostProcess); } else if (!strcasecmp(szMethodName, "pausescan")) { command = new PauseUnpauseXmlCommand(true, PauseUnpauseXmlCommand::paScan); } else if (!strcasecmp(szMethodName, "resumescan")) { command = new PauseUnpauseXmlCommand(false, PauseUnpauseXmlCommand::paScan); } else if (!strcasecmp(szMethodName, "history")) { command = new HistoryXmlCommand(); } else if (!strcasecmp(szMethodName, "appendurl")) { command = new DownloadUrlXmlCommand(); } else if (!strcasecmp(szMethodName, "urlqueue")) { command = new UrlQueueXmlCommand(); } else if (!strcasecmp(szMethodName, "config")) { command = new ConfigXmlCommand(); } else if (!strcasecmp(szMethodName, "loadconfig")) { command = new LoadConfigXmlCommand(); } else if (!strcasecmp(szMethodName, "saveconfig")) { command = new SaveConfigXmlCommand(); } else { command = new ErrorXmlCommand(1, "Invalid procedure"); } return command; } //***************************************************************** // Base command XmlCommand::XmlCommand() { m_szRequest = NULL; m_szRequestPtr = NULL; m_szCallbackFunc = NULL; m_bFault = false; m_eProtocol = XmlRpcProcessor::rpUndefined; } bool XmlCommand::IsJson() { return m_eProtocol == XmlRpcProcessor::rpJsonRpc || m_eProtocol == XmlRpcProcessor::rpJsonPRpc; } void XmlCommand::AppendResponse(const char* szPart) { m_StringBuilder.Append(szPart); } void XmlCommand::BuildErrorResponse(int iErrCode, const char* szErrText, ...) { const char* XML_RESPONSE_ERROR_BODY = "\n" "faultCode%i\n" "faultString%s\n" "\n"; const char* JSON_RESPONSE_ERROR_BODY = "{\n" "\"name\" : \"JSONRPCError\",\n" "\"code\" : %i,\n" "\"message\" : \"%s\"\n" "}"; char szFullText[1024]; va_list ap; va_start(ap, szErrText); vsnprintf(szFullText, 1024, szErrText, ap); szFullText[1024-1] = '\0'; va_end(ap); char* xmlText = EncodeStr(szFullText); char szContent[1024]; snprintf(szContent, 1024, IsJson() ? JSON_RESPONSE_ERROR_BODY : XML_RESPONSE_ERROR_BODY, iErrCode, xmlText); szContent[1024-1] = '\0'; free(xmlText); AppendResponse(szContent); m_bFault = true; } void XmlCommand::BuildBoolResponse(bool bOK) { const char* XML_RESPONSE_BOOL_BODY = "%s"; const char* JSON_RESPONSE_BOOL_BODY = "%s"; char szContent[1024]; snprintf(szContent, 1024, IsJson() ? JSON_RESPONSE_BOOL_BODY : XML_RESPONSE_BOOL_BODY, BoolToStr(bOK)); szContent[1024-1] = '\0'; AppendResponse(szContent); } void XmlCommand::PrepareParams() { if (IsJson() && m_eHttpMethod == XmlRpcProcessor::hmPost) { char* szParams = strstr(m_szRequestPtr, "\"params\""); if (!szParams) { m_szRequestPtr[0] = '\0'; return; } m_szRequestPtr = szParams + 8; // strlen("\"params\"") } if (m_eProtocol == XmlRpcProcessor::rpJsonPRpc) { NextParamAsStr(&m_szCallbackFunc); } } bool XmlCommand::NextParamAsInt(int* iValue) { if (m_eHttpMethod == XmlRpcProcessor::hmGet) { char* szParam = strchr(m_szRequestPtr, '='); if (!szParam) { return false; } *iValue = atoi(szParam + 1); m_szRequestPtr = szParam + 1; return true; } else if (IsJson()) { int iLen = 0; char* szParam = (char*)WebUtil::JsonNextValue(m_szRequestPtr, &iLen); if (!szParam || !strchr("-+0123456789", *szParam)) { return false; } *iValue = atoi(szParam); m_szRequestPtr = szParam + iLen + 1; return true; } else { int iLen = 0; int iTagLen = 4; //strlen(""); char* szParam = (char*)WebUtil::XmlFindTag(m_szRequestPtr, "i4", &iLen); if (!szParam) { szParam = (char*)WebUtil::XmlFindTag(m_szRequestPtr, "int", &iLen); iTagLen = 5; //strlen(""); } if (!szParam || !strchr("-+0123456789", *szParam)) { return false; } *iValue = atoi(szParam); m_szRequestPtr = szParam + iLen + iTagLen; return true; } } bool XmlCommand::NextParamAsBool(bool* bValue) { if (m_eHttpMethod == XmlRpcProcessor::hmGet) { char* szParam; if (!NextParamAsStr(&szParam)) { return false; } if (IsJson()) { if (!strcmp(szParam, "true")) { *bValue = true; return true; } else if (!strcmp(szParam, "false")) { *bValue = false; return true; } } else { *bValue = szParam[0] == '1'; return true; } return false; } else if (IsJson()) { int iLen = 0; char* szParam = (char*)WebUtil::JsonNextValue(m_szRequestPtr, &iLen); if (!szParam) { return false; } if (iLen == 4 && !strncmp(szParam, "true", 4)) { *bValue = true; m_szRequestPtr = szParam + iLen + 1; return true; } else if (iLen == 5 && !strncmp(szParam, "false", 5)) { *bValue = false; m_szRequestPtr = szParam + iLen + 1; return true; } else { return false; } } else { int iLen = 0; char* szParam = (char*)WebUtil::XmlFindTag(m_szRequestPtr, "boolean", &iLen); if (!szParam) { return false; } *bValue = szParam[0] == '1'; m_szRequestPtr = szParam + iLen + 9; //strlen(""); return true; } } bool XmlCommand::NextParamAsStr(char** szValue) { if (m_eHttpMethod == XmlRpcProcessor::hmGet) { char* szParam = strchr(m_szRequestPtr, '='); if (!szParam) { return false; } szParam++; // skip '=' int iLen = 0; char* szParamEnd = strchr(m_szRequestPtr, '&'); if (szParamEnd) { iLen = (int)(szParamEnd - szParam); szParam[iLen] = '\0'; } else { iLen = strlen(szParam) - 1; } m_szRequestPtr = szParam + iLen + 1; *szValue = szParam; return true; } else if (IsJson()) { int iLen = 0; char* szParam = (char*)WebUtil::JsonNextValue(m_szRequestPtr, &iLen); if (!szParam || iLen < 2 || szParam[0] != '"' || szParam[iLen - 1] != '"') { return false; } szParam++; // skip first '"' szParam[iLen - 2] = '\0'; // skip last '"' m_szRequestPtr = szParam + iLen; *szValue = szParam; return true; } else { int iLen = 0; char* szParam = (char*)WebUtil::XmlFindTag(m_szRequestPtr, "string", &iLen); if (!szParam) { return false; } szParam[iLen] = '\0'; m_szRequestPtr = szParam + iLen + 8; //strlen("") *szValue = szParam; return true; } } const char* XmlCommand::BoolToStr(bool bValue) { return IsJson() ? (bValue ? "true" : "false") : (bValue ? "1" : "0"); } char* XmlCommand::EncodeStr(const char* szStr) { if (!szStr) { return strdup(""); } if (IsJson()) { return WebUtil::JsonEncode(szStr); } else { return WebUtil::XmlEncode(szStr); } } void XmlCommand::DecodeStr(char* szStr) { if (IsJson()) { WebUtil::JsonDecode(szStr); } else { WebUtil::XmlDecode(szStr); } } bool XmlCommand::CheckSafeMethod() { bool bSafe = m_eHttpMethod == XmlRpcProcessor::hmPost || m_eProtocol == XmlRpcProcessor::rpJsonPRpc; if (!bSafe) { BuildErrorResponse(4, "Not safe procedure for HTTP-Method GET. Use Method POST instead"); } return bSafe; } //***************************************************************** // Commands ErrorXmlCommand::ErrorXmlCommand(int iErrCode, const char* szErrText) { m_iErrCode = iErrCode; m_szErrText = szErrText; } void ErrorXmlCommand::Execute() { error("Received unsupported request: %s", m_szErrText); BuildErrorResponse(m_iErrCode, m_szErrText); } PauseUnpauseXmlCommand::PauseUnpauseXmlCommand(bool bPause, EPauseAction eEPauseAction) { m_bPause = bPause; m_eEPauseAction = eEPauseAction; } void PauseUnpauseXmlCommand::Execute() { if (!CheckSafeMethod()) { return; } bool bOK = true; switch (m_eEPauseAction) { case paDownload: g_pOptions->SetPauseDownload(m_bPause); break; case paDownload2: g_pOptions->SetPauseDownload2(m_bPause); break; case paPostProcess: g_pOptions->SetPausePostProcess(m_bPause); break; case paScan: g_pOptions->SetPauseScan(m_bPause); break; default: bOK = false; } BuildBoolResponse(bOK); } void ShutdownXmlCommand::Execute() { if (!CheckSafeMethod()) { return; } BuildBoolResponse(true); ExitProc(); } void ReloadXmlCommand::Execute() { if (!CheckSafeMethod()) { return; } BuildBoolResponse(true); Reload(); } void VersionXmlCommand::Execute() { const char* XML_RESPONSE_STRING_BODY = "%s"; const char* JSON_RESPONSE_STRING_BODY = "\"%s\""; char szContent[1024]; snprintf(szContent, 1024, IsJson() ? JSON_RESPONSE_STRING_BODY : XML_RESPONSE_STRING_BODY, Util::VersionRevision()); szContent[1024-1] = '\0'; AppendResponse(szContent); } void DumpDebugXmlCommand::Execute() { g_pQueueCoordinator->LogDebugInfo(); g_pUrlCoordinator->LogDebugInfo(); BuildBoolResponse(true); } void SetDownloadRateXmlCommand::Execute() { if (!CheckSafeMethod()) { return; } int iRate = 0; if (!NextParamAsInt(&iRate) || iRate < 0) { BuildErrorResponse(2, "Invalid parameter"); return; } g_pOptions->SetDownloadRate((float)iRate); BuildBoolResponse(true); } void StatusXmlCommand::Execute() { const char* XML_RESPONSE_STATUS_BODY = "\n" "RemainingSizeLo%i\n" "RemainingSizeHi%i\n" "RemainingSizeMB%i\n" "DownloadedSizeLo%i\n" "DownloadedSizeHi%i\n" "DownloadedSizeMB%i\n" "DownloadRate%i\n" "AverageDownloadRate%i\n" "DownloadLimit%i\n" "ThreadCount%i\n" "ParJobCount%i\n" // deprecated (renamed to PostJobCount) "PostJobCount%i\n" "UrlCount%i\n" "UpTimeSec%i\n" "DownloadTimeSec%i\n" "ServerPaused%s\n" // deprecated (renamed to DownloadPaused) "DownloadPaused%s\n" "Download2Paused%s\n" "ServerStandBy%s\n" "PostPaused%s\n" "ScanPaused%s\n" "FreeDiskSpaceLo%i\n" "FreeDiskSpaceHi%i\n" "FreeDiskSpaceMB%i\n" "\n"; const char* JSON_RESPONSE_STATUS_BODY = "{\n" "\"RemainingSizeLo\" : %i,\n" "\"RemainingSizeHi\" : %i,\n" "\"RemainingSizeMB\" : %i,\n" "\"DownloadedSizeLo\" : %i,\n" "\"DownloadedSizeHi\" : %i,\n" "\"DownloadedSizeMB\" : %i,\n" "\"DownloadRate\" : %i,\n" "\"AverageDownloadRate\" : %i,\n" "\"DownloadLimit\" : %i,\n" "\"ThreadCount\" : %i,\n" "\"ParJobCount\" : %i,\n" // deprecated (renamed to PostJobCount) "\"PostJobCount\" : %i,\n" "\"UrlCount\" : %i,\n" "\"UpTimeSec\" : %i,\n" "\"DownloadTimeSec\" : %i,\n" "\"ServerPaused\" : %s,\n" // deprecated (renamed to DownloadPaused) "\"DownloadPaused\" : %s,\n" "\"Download2Paused\" : %s,\n" "\"ServerStandBy\" : %s,\n" "\"PostPaused\" : %s,\n" "\"ScanPaused\" : %s,\n" "\"FreeDiskSpaceLo\" : %i,\n" "\"FreeDiskSpaceHi\" : %i,\n" "\"FreeDiskSpaceMB\" : %i\n" "}\n"; unsigned long iRemainingSizeHi, iRemainingSizeLo; int iDownloadRate = (int)(g_pQueueCoordinator->CalcCurrentDownloadSpeed() * 1024); long long iRemainingSize = g_pQueueCoordinator->CalcRemainingSize(); Util::SplitInt64(iRemainingSize, &iRemainingSizeHi, &iRemainingSizeLo); int iRemainingMBytes = (int)(iRemainingSize / 1024 / 1024); int iDownloadLimit = (int)(g_pOptions->GetDownloadRate() * 1024); bool bDownloadPaused = g_pOptions->GetPauseDownload(); bool bDownload2Paused = g_pOptions->GetPauseDownload2(); bool bPostPaused = g_pOptions->GetPausePostProcess(); bool bScanPaused = g_pOptions->GetPauseScan(); int iThreadCount = Thread::GetThreadCount() - 1; // not counting itself DownloadQueue *pDownloadQueue = g_pQueueCoordinator->LockQueue(); int iPostJobCount = pDownloadQueue->GetPostQueue()->size(); int iUrlCount = pDownloadQueue->GetUrlQueue()->size(); g_pQueueCoordinator->UnlockQueue(); unsigned long iDownloadedSizeHi, iDownloadedSizeLo; int iUpTimeSec, iDownloadTimeSec; long long iAllBytes; bool bServerStandBy; g_pQueueCoordinator->CalcStat(&iUpTimeSec, &iDownloadTimeSec, &iAllBytes, &bServerStandBy); int iDownloadedMBytes = (int)(iAllBytes / 1024 / 1024); Util::SplitInt64(iAllBytes, &iDownloadedSizeHi, &iDownloadedSizeLo); int iAverageDownloadRate = (int)(iDownloadTimeSec > 0 ? iAllBytes / iDownloadTimeSec : 0); unsigned long iFreeDiskSpaceHi, iFreeDiskSpaceLo; long long iFreeDiskSpace = Util::FreeDiskSize(g_pOptions->GetDestDir()); Util::SplitInt64(iFreeDiskSpace, &iFreeDiskSpaceHi, &iFreeDiskSpaceLo); int iFreeDiskSpaceMB = (int)(iFreeDiskSpace / 1024 / 1024); char szContent[2048]; snprintf(szContent, 2048, IsJson() ? JSON_RESPONSE_STATUS_BODY : XML_RESPONSE_STATUS_BODY, iRemainingSizeLo, iRemainingSizeHi, iRemainingMBytes, iDownloadedSizeLo, iDownloadedSizeHi, iDownloadedMBytes, iDownloadRate, iAverageDownloadRate, iDownloadLimit, iThreadCount, iPostJobCount, iPostJobCount, iUrlCount, iUpTimeSec, iDownloadTimeSec, BoolToStr(bDownloadPaused), BoolToStr(bDownloadPaused), BoolToStr(bDownload2Paused), BoolToStr(bServerStandBy), BoolToStr(bPostPaused), BoolToStr(bScanPaused), iFreeDiskSpaceLo, iFreeDiskSpaceHi, iFreeDiskSpaceMB); szContent[2048-1] = '\0'; AppendResponse(szContent); } void LogXmlCommand::Execute() { int iIDFrom = 0; int iNrEntries = 0; if (!NextParamAsInt(&iIDFrom) || !NextParamAsInt(&iNrEntries) || (iNrEntries > 0 && iIDFrom > 0)) { BuildErrorResponse(2, "Invalid parameter"); return; } debug("iIDFrom=%i", iIDFrom); debug("iNrEntries=%i", iNrEntries); AppendResponse(IsJson() ? "[\n" : "\n"); Log::Messages* pMessages = g_pLog->LockMessages(); int iStart = pMessages->size(); if (iNrEntries > 0) { if (iNrEntries > (int)pMessages->size()) { iNrEntries = pMessages->size(); } iStart = pMessages->size() - iNrEntries; } if (iIDFrom > 0 && !pMessages->empty()) { iNrEntries = pMessages->size(); iStart = iIDFrom - pMessages->front()->GetID(); if (iStart < 0) { iStart = 0; } } const char* XML_LOG_ITEM = "\n" "ID%i\n" "Kind%s\n" "Time%i\n" "Text%s\n" "\n"; const char* JSON_LOG_ITEM = "{\n" "\"ID\" : %i,\n" "\"Kind\" : \"%s\",\n" "\"Time\" : %i,\n" "\"Text\" : \"%s\"\n" "}"; const char* szMessageType[] = { "INFO", "WARNING", "ERROR", "DEBUG", "DETAIL" }; int szItemBufSize = 10240; char* szItemBuf = (char*)malloc(szItemBufSize); int index = 0; for (unsigned int i = (unsigned int)iStart; i < pMessages->size(); i++) { Message* pMessage = (*pMessages)[i]; char* xmltext = EncodeStr(pMessage->GetText()); snprintf(szItemBuf, szItemBufSize, IsJson() ? JSON_LOG_ITEM : XML_LOG_ITEM, pMessage->GetID(), szMessageType[pMessage->GetKind()], pMessage->GetTime(), xmltext); szItemBuf[szItemBufSize-1] = '\0'; free(xmltext); if (IsJson() && index++ > 0) { AppendResponse(",\n"); } AppendResponse(szItemBuf); } free(szItemBuf); g_pLog->UnlockMessages(); AppendResponse(IsJson() ? "\n]" : "\n"); } // struct[] listfiles(int IDFrom, int IDTo, int NZBID) // For backward compatibility with 0.8 parameter "NZBID" is optional void ListFilesXmlCommand::Execute() { int iIDStart = 0; int iIDEnd = 0; if (NextParamAsInt(&iIDStart) && (!NextParamAsInt(&iIDEnd) || iIDEnd < iIDStart)) { BuildErrorResponse(2, "Invalid parameter"); return; } // For backward compatibility with 0.8 parameter "NZBID" is optional (error checking omitted) int iNZBID = 0; NextParamAsInt(&iNZBID); if (iNZBID > 0 && (iIDStart != 0 || iIDEnd != 0)) { BuildErrorResponse(2, "Invalid parameter"); return; } debug("iIDStart=%i", iIDStart); debug("iIDEnd=%i", iIDEnd); AppendResponse(IsJson() ? "[\n" : "\n"); DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue(); const char* XML_LIST_ITEM = "\n" "ID%i\n" "FileSizeLo%i\n" "FileSizeHi%i\n" "RemainingSizeLo%i\n" "RemainingSizeHi%i\n" "PostTime%i\n" "FilenameConfirmed%s\n" "Paused%s\n" "NZBID%i\n" "NZBName%s\n" "NZBNicename%s\n" // deprecated, use "NZBName" instead "NZBFilename%s\n" "Subject%s\n" "Filename%s\n" "DestDir%s\n" "Category%s\n" "Priority%i\n" "ActiveDownloads%i\n" "\n"; const char* JSON_LIST_ITEM = "{\n" "\"ID\" : %i,\n" "\"FileSizeLo\" : %i,\n" "\"FileSizeHi\" : %i,\n" "\"RemainingSizeLo\" : %i,\n" "\"RemainingSizeHi\" : %i,\n" "\"PostTime\" : %i,\n" "\"FilenameConfirmed\" : %s,\n" "\"Paused\" : %s,\n" "\"NZBID\" : %i,\n" "\"NZBName\" : \"%s\",\n" "\"NZBNicename\" : \"%s\",\n" // deprecated, use "NZBName" instead "\"NZBFilename\" : \"%s\",\n" "\"Subject\" : \"%s\",\n" "\"Filename\" : \"%s\",\n" "\"DestDir\" : \"%s\",\n" "\"Category\" : \"%s\",\n" "\"Priority\" : %i,\n" "\"ActiveDownloads\" : %i\n" "}"; int szItemBufSize = 10240; char* szItemBuf = (char*)malloc(szItemBufSize); int index = 0; for (FileQueue::iterator it = pDownloadQueue->GetFileQueue()->begin(); it != pDownloadQueue->GetFileQueue()->end(); it++) { FileInfo* pFileInfo = *it; if ((iNZBID > 0 && iNZBID == pFileInfo->GetNZBInfo()->GetID()) || (iNZBID == 0 && (iIDStart == 0 || (iIDStart <= pFileInfo->GetID() && pFileInfo->GetID() <= iIDEnd)))) { unsigned long iFileSizeHi, iFileSizeLo; unsigned long iRemainingSizeLo, iRemainingSizeHi; Util::SplitInt64(pFileInfo->GetSize(), &iFileSizeHi, &iFileSizeLo); Util::SplitInt64(pFileInfo->GetRemainingSize(), &iRemainingSizeHi, &iRemainingSizeLo); char* xmlNZBFilename = EncodeStr(pFileInfo->GetNZBInfo()->GetFilename()); char* xmlSubject = EncodeStr(pFileInfo->GetSubject()); char* xmlFilename = EncodeStr(pFileInfo->GetFilename()); char* xmlDestDir = EncodeStr(pFileInfo->GetNZBInfo()->GetDestDir()); char* xmlCategory = EncodeStr(pFileInfo->GetNZBInfo()->GetCategory()); char* xmlNZBNicename = EncodeStr(pFileInfo->GetNZBInfo()->GetName()); snprintf(szItemBuf, szItemBufSize, IsJson() ? JSON_LIST_ITEM : XML_LIST_ITEM, pFileInfo->GetID(), iFileSizeLo, iFileSizeHi, iRemainingSizeLo, iRemainingSizeHi, pFileInfo->GetTime(), BoolToStr(pFileInfo->GetFilenameConfirmed()), BoolToStr(pFileInfo->GetPaused()), pFileInfo->GetNZBInfo()->GetID(), xmlNZBNicename, xmlNZBNicename, xmlNZBFilename, xmlSubject, xmlFilename, xmlDestDir, xmlCategory, pFileInfo->GetPriority(), pFileInfo->GetActiveDownloads()); szItemBuf[szItemBufSize-1] = '\0'; free(xmlNZBFilename); free(xmlSubject); free(xmlFilename); free(xmlDestDir); free(xmlCategory); free(xmlNZBNicename); if (IsJson() && index++ > 0) { AppendResponse(",\n"); } AppendResponse(szItemBuf); } } free(szItemBuf); g_pQueueCoordinator->UnlockQueue(); AppendResponse(IsJson() ? "\n]" : "\n"); } void ListGroupsXmlCommand::Execute() { AppendResponse(IsJson() ? "[\n" : "\n"); const char* XML_LIST_ITEM_START = "\n" "FirstID%i\n" "LastID%i\n" "FileSizeLo%i\n" "FileSizeHi%i\n" "FileSizeMB%i\n" "RemainingSizeLo%i\n" "RemainingSizeHi%i\n" "RemainingSizeMB%i\n" "PausedSizeLo%i\n" "PausedSizeHi%i\n" "PausedSizeMB%i\n" "FileCount%i\n" "RemainingFileCount%i\n" "RemainingParCount%i\n" "MinPostTime%i\n" "MaxPostTime%i\n" "NZBID%i\n" "NZBName%s\n" "NZBNicename%s\n" // deprecated, use "NZBName" instead "NZBFilename%s\n" "DestDir%s\n" "Category%s\n" "MinPriority%i\n" "MaxPriority%i\n" "ActiveDownloads%i\n" "Parameters\n"; const char* XML_LIST_ITEM_END = "\n" "\n"; const char* JSON_LIST_ITEM_START = "{\n" "\"FirstID\" : %i,\n" "\"LastID\" : %i,\n" "\"FileSizeLo\" : %i,\n" "\"FileSizeHi\" : %i,\n" "\"FileSizeMB\" : %i,\n" "\"RemainingSizeLo\" : %i,\n" "\"RemainingSizeHi\" : %i,\n" "\"RemainingSizeMB\" : %i,\n" "\"PausedSizeLo\" : %i,\n" "\"PausedSizeHi\" : %i,\n" "\"PausedSizeMB\" : %i,\n" "\"FileCount\" : %i,\n" "\"RemainingFileCount\" : %i,\n" "\"RemainingParCount\" : %i,\n" "\"MinPostTime\" : %i,\n" "\"MaxPostTime\" : %i,\n" "\"NZBID\" : %i,\n" "\"NZBName\" : \"%s\",\n" "\"NZBNicename\" : \"%s\",\n" // deprecated, use "NZBName" instead "\"NZBFilename\" : \"%s\",\n" "\"DestDir\" : \"%s\",\n" "\"Category\" : \"%s\",\n" "\"MinPriority\" : %i,\n" "\"MaxPriority\" : %i,\n" "\"ActiveDownloads\" : %i,\n" "\"Parameters\" : [\n"; const char* JSON_LIST_ITEM_END = "]\n" "}"; const char* XML_PARAMETER_ITEM = "\n" "Name%s\n" "Value%s\n" "\n"; const char* JSON_PARAMETER_ITEM = "{\n" "\"Name\" : \"%s\",\n" "\"Value\" : \"%s\"\n" "}"; GroupQueue groupQueue; groupQueue.clear(); DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue(); pDownloadQueue->BuildGroups(&groupQueue); g_pQueueCoordinator->UnlockQueue(); int szItemBufSize = 10240; char* szItemBuf = (char*)malloc(szItemBufSize); int index = 0; for (GroupQueue::iterator it = groupQueue.begin(); it != groupQueue.end(); it++) { GroupInfo* pGroupInfo = *it; unsigned long iFileSizeHi, iFileSizeLo, iFileSizeMB; unsigned long iRemainingSizeLo, iRemainingSizeHi, iRemainingSizeMB; unsigned long iPausedSizeLo, iPausedSizeHi, iPausedSizeMB; Util::SplitInt64(pGroupInfo->GetNZBInfo()->GetSize(), &iFileSizeHi, &iFileSizeLo); iFileSizeMB = (int)(pGroupInfo->GetNZBInfo()->GetSize() / 1024 / 1024); Util::SplitInt64(pGroupInfo->GetRemainingSize(), &iRemainingSizeHi, &iRemainingSizeLo); iRemainingSizeMB = (int)(pGroupInfo->GetRemainingSize() / 1024 / 1024); Util::SplitInt64(pGroupInfo->GetPausedSize(), &iPausedSizeHi, &iPausedSizeLo); iPausedSizeMB = (int)(pGroupInfo->GetPausedSize() / 1024 / 1024); char* xmlNZBNicename = EncodeStr(pGroupInfo->GetNZBInfo()->GetName()); char* xmlNZBFilename = EncodeStr(pGroupInfo->GetNZBInfo()->GetFilename()); char* xmlDestDir = EncodeStr(pGroupInfo->GetNZBInfo()->GetDestDir()); char* xmlCategory = EncodeStr(pGroupInfo->GetNZBInfo()->GetCategory()); snprintf(szItemBuf, szItemBufSize, IsJson() ? JSON_LIST_ITEM_START : XML_LIST_ITEM_START, pGroupInfo->GetFirstID(), pGroupInfo->GetLastID(), iFileSizeLo, iFileSizeHi, iFileSizeMB, iRemainingSizeLo, iRemainingSizeHi, iRemainingSizeMB, iPausedSizeLo, iPausedSizeHi, iPausedSizeMB, pGroupInfo->GetNZBInfo()->GetFileCount(), pGroupInfo->GetRemainingFileCount(), pGroupInfo->GetRemainingParCount(), pGroupInfo->GetMinTime(), pGroupInfo->GetMaxTime(), pGroupInfo->GetNZBInfo()->GetID(), xmlNZBNicename, xmlNZBNicename, xmlNZBFilename, xmlDestDir, xmlCategory, pGroupInfo->GetMinPriority(), pGroupInfo->GetMaxPriority(), pGroupInfo->GetActiveDownloads()); szItemBuf[szItemBufSize-1] = '\0'; free(xmlNZBNicename); free(xmlNZBFilename); free(xmlDestDir); free(xmlCategory); if (IsJson() && index++ > 0) { AppendResponse(",\n"); } AppendResponse(szItemBuf); int iParamIndex = 0; for (NZBParameterList::iterator it = pGroupInfo->GetNZBInfo()->GetParameters()->begin(); it != pGroupInfo->GetNZBInfo()->GetParameters()->end(); it++) { NZBParameter* pParameter = *it; char* xmlName = EncodeStr(pParameter->GetName()); char* xmlValue = EncodeStr(pParameter->GetValue()); snprintf(szItemBuf, szItemBufSize, IsJson() ? JSON_PARAMETER_ITEM : XML_PARAMETER_ITEM, xmlName, xmlValue); szItemBuf[szItemBufSize-1] = '\0'; free(xmlName); free(xmlValue); if (IsJson() && iParamIndex++ > 0) { AppendResponse(",\n"); } AppendResponse(szItemBuf); } AppendResponse(IsJson() ? JSON_LIST_ITEM_END : XML_LIST_ITEM_END); } free(szItemBuf); AppendResponse(IsJson() ? "\n]" : "\n"); for (GroupQueue::iterator it = groupQueue.begin(); it != groupQueue.end(); it++) { delete *it; } groupQueue.clear(); } typedef struct { int iActionID; const char* szActionName; } EditCommandEntry; EditCommandEntry EditCommandNameMap[] = { { QueueEditor::eaFileMoveOffset, "FileMoveOffset" }, { QueueEditor::eaFileMoveTop, "FileMoveTop" }, { QueueEditor::eaFileMoveBottom, "FileMoveBottom" }, { QueueEditor::eaFilePause, "FilePause" }, { QueueEditor::eaFileResume, "FileResume" }, { QueueEditor::eaFileDelete, "FileDelete" }, { QueueEditor::eaFilePauseAllPars, "FilePauseAllPars" }, { QueueEditor::eaFilePauseExtraPars, "FilePauseExtraPars" }, { QueueEditor::eaFileSetPriority, "FileSetPriority" }, { QueueEditor::eaFileReorder, "FileReorder" }, { QueueEditor::eaGroupMoveOffset, "GroupMoveOffset" }, { QueueEditor::eaGroupMoveTop, "GroupMoveTop" }, { QueueEditor::eaGroupMoveBottom, "GroupMoveBottom" }, { QueueEditor::eaGroupPause, "GroupPause" }, { QueueEditor::eaGroupResume, "GroupResume" }, { QueueEditor::eaGroupDelete, "GroupDelete" }, { QueueEditor::eaGroupPauseAllPars, "GroupPauseAllPars" }, { QueueEditor::eaGroupPauseExtraPars, "GroupPauseExtraPars" }, { QueueEditor::eaGroupSetPriority, "GroupSetPriority" }, { QueueEditor::eaGroupSetCategory, "GroupSetCategory" }, { QueueEditor::eaGroupMerge, "GroupMerge" }, { QueueEditor::eaGroupSetParameter, "GroupSetParameter" }, { QueueEditor::eaGroupSetName, "GroupSetName" }, { PrePostProcessor::eaPostMoveOffset, "PostMoveOffset" }, { PrePostProcessor::eaPostMoveTop, "PostMoveTop" }, { PrePostProcessor::eaPostMoveBottom, "PostMoveBottom" }, { PrePostProcessor::eaPostDelete, "PostDelete" }, { PrePostProcessor::eaHistoryDelete, "HistoryDelete" }, { PrePostProcessor::eaHistoryReturn, "HistoryReturn" }, { PrePostProcessor::eaHistoryProcess, "HistoryProcess" }, { 0, NULL } }; void EditQueueXmlCommand::Execute() { if (!CheckSafeMethod()) { return; } char* szEditCommand; if (!NextParamAsStr(&szEditCommand)) { BuildErrorResponse(2, "Invalid parameter"); return; } debug("EditCommand=%s", szEditCommand); int iAction = -1; for (int i = 0; const char* szName = EditCommandNameMap[i].szActionName; i++) { if (!strcasecmp(szEditCommand, szName)) { iAction = EditCommandNameMap[i].iActionID; break; } } if (iAction == -1) { BuildErrorResponse(3, "Invalid action"); return; } int iOffset = 0; if (!NextParamAsInt(&iOffset)) { BuildErrorResponse(2, "Invalid parameter"); return; } char* szEditText; if (!NextParamAsStr(&szEditText)) { BuildErrorResponse(2, "Invalid parameter"); return; } debug("EditText=%s", szEditText); DecodeStr(szEditText); IDList cIDList; int iID = 0; while (NextParamAsInt(&iID)) { cIDList.push_back(iID); } bool bOK = false; if (iAction < PrePostProcessor::eaPostMoveOffset) { bOK = g_pQueueCoordinator->GetQueueEditor()->EditList(&cIDList, NULL, QueueEditor::mmID, true, (QueueEditor::EEditAction)iAction, iOffset, szEditText); } else { bOK = g_pPrePostProcessor->QueueEditList(&cIDList, (PrePostProcessor::EEditAction)iAction, iOffset); } BuildBoolResponse(bOK); } // bool append(string NZBFilename, string Category, int Priority, bool AddToTop, string Content) // For backward compatibility with 0.8 parameter "Priority" is optional void DownloadXmlCommand::Execute() { if (!CheckSafeMethod()) { return; } char* szFileName; if (!NextParamAsStr(&szFileName)) { BuildErrorResponse(2, "Invalid parameter"); return; } char* szCategory; if (!NextParamAsStr(&szCategory)) { BuildErrorResponse(2, "Invalid parameter"); return; } DecodeStr(szFileName); DecodeStr(szCategory); debug("FileName=%s", szFileName); // For backward compatibility with 0.8 parameter "Priority" is optional (error checking omitted) int iPriority = 0; NextParamAsInt(&iPriority); bool bAddTop; if (!NextParamAsBool(&bAddTop)) { BuildErrorResponse(2, "Invalid parameter"); return; } char* szFileContent; if (!NextParamAsStr(&szFileContent)) { BuildErrorResponse(2, "Invalid parameter"); return; } if (IsJson()) { // JSON-string may contain '/'-character used in Base64, which must be escaped in JSON WebUtil::JsonDecode(szFileContent); } int iLen = WebUtil::DecodeBase64(szFileContent, 0, szFileContent); szFileContent[iLen] = '\0'; //debug("FileContent=%s", szFileContent); NZBFile* pNZBFile = NZBFile::CreateFromBuffer(szFileName, szCategory, szFileContent, iLen + 1); if (pNZBFile) { info("Request: Queue collection %s", szFileName); for (NZBFile::FileInfos::iterator it = pNZBFile->GetFileInfos()->begin(); it != pNZBFile->GetFileInfos()->end(); it++) { FileInfo* pFileInfo = *it; pFileInfo->SetPriority(iPriority); } g_pQueueCoordinator->AddNZBFileToQueue(pNZBFile, bAddTop); delete pNZBFile; BuildBoolResponse(true); } else { BuildBoolResponse(false); } } void PostQueueXmlCommand::Execute() { int iNrEntries = 0; NextParamAsInt(&iNrEntries); AppendResponse(IsJson() ? "[\n" : "\n"); const char* XML_POSTQUEUE_ITEM_START = "\n" "ID%i\n" "NZBID%i\n" "NZBName%s\n" "NZBNicename%s\n" // deprecated, use "NZBName" instead "NZBFilename%s\n" "DestDir%s\n" "ParFilename%s\n" "InfoName%s\n" "Stage%s\n" "ProgressLabel%s\n" "FileProgress%i\n" "StageProgress%i\n" "TotalTimeSec%i\n" "StageTimeSec%i\n" "Log\n"; const char* XML_POSTQUEUE_ITEM_END = "\n" "\n"; const char* JSON_POSTQUEUE_ITEM_START = "{\n" "\"ID\" : %i,\n" "\"NZBID\" : %i,\n" "\"NZBName\" : \"%s\",\n" "\"NZBNicename\" : \"%s\",\n" // deprecated, use "NZBName" instead "\"NZBFilename\" : \"%s\",\n" "\"DestDir\" : \"%s\",\n" "\"ParFilename\" : \"%s\",\n" "\"InfoName\" : \"%s\",\n" "\"Stage\" : \"%s\",\n" "\"ProgressLabel\" : \"%s\",\n" "\"FileProgress\" : %i,\n" "\"StageProgress\" : %i,\n" "\"TotalTimeSec\" : %i,\n" "\"StageTimeSec\" : %i,\n" "\"Log\" : [\n"; const char* JSON_POSTQUEUE_ITEM_END = "]\n" "}"; const char* XML_LOG_ITEM = "\n" "ID%i\n" "Kind%s\n" "Time%i\n" "Text%s\n" "\n"; const char* JSON_LOG_ITEM = "{\n" "\"ID\" : %i,\n" "\"Kind\" : \"%s\",\n" "\"Time\" : %i,\n" "\"Text\" : \"%s\"\n" "}"; const char* szMessageType[] = { "INFO", "WARNING", "ERROR", "DEBUG", "DETAIL"}; PostQueue* pPostQueue = g_pQueueCoordinator->LockQueue()->GetPostQueue(); time_t tCurTime = time(NULL); int szItemBufSize = 10240; char* szItemBuf = (char*)malloc(szItemBufSize); int index = 0; for (PostQueue::iterator it = pPostQueue->begin(); it != pPostQueue->end(); it++) { PostInfo* pPostInfo = *it; const char* szPostStageName[] = { "QUEUED", "LOADING_PARS", "VERIFYING_SOURCES", "REPAIRING", "VERIFYING_REPAIRED", "EXECUTING_SCRIPT", "FINISHED" }; char* xmlNZBNicename = EncodeStr(pPostInfo->GetNZBInfo()->GetName()); char* xmlNZBFilename = EncodeStr(pPostInfo->GetNZBInfo()->GetFilename()); char* xmlDestDir = EncodeStr(pPostInfo->GetNZBInfo()->GetDestDir()); char* xmlParFilename = EncodeStr(pPostInfo->GetParFilename()); char* xmlInfoName = EncodeStr(pPostInfo->GetInfoName()); char* xmlProgressLabel = EncodeStr(pPostInfo->GetProgressLabel()); snprintf(szItemBuf, szItemBufSize, IsJson() ? JSON_POSTQUEUE_ITEM_START : XML_POSTQUEUE_ITEM_START, pPostInfo->GetID(), pPostInfo->GetNZBInfo()->GetID(), xmlNZBNicename, xmlNZBNicename, xmlNZBFilename, xmlDestDir, xmlParFilename, xmlInfoName, szPostStageName[pPostInfo->GetStage()], xmlProgressLabel, pPostInfo->GetFileProgress(), pPostInfo->GetStageProgress(), pPostInfo->GetStartTime() ? tCurTime - pPostInfo->GetStartTime() : 0, pPostInfo->GetStageTime() ? tCurTime - pPostInfo->GetStageTime() : 0); szItemBuf[szItemBufSize-1] = '\0'; free(xmlNZBNicename); free(xmlNZBFilename); free(xmlDestDir); free(xmlParFilename); free(xmlInfoName); free(xmlProgressLabel); if (IsJson() && index++ > 0) { AppendResponse(",\n"); } AppendResponse(szItemBuf); if (iNrEntries > 0) { PostInfo::Messages* pMessages = pPostInfo->LockMessages(); if (!pMessages->empty()) { int iStart = pMessages->size(); if (iNrEntries > (int)pMessages->size()) { iNrEntries = pMessages->size(); } iStart = pMessages->size() - iNrEntries; int index = 0; for (unsigned int i = (unsigned int)iStart; i < pMessages->size(); i++) { Message* pMessage = (*pMessages)[i]; char* xmltext = EncodeStr(pMessage->GetText()); snprintf(szItemBuf, szItemBufSize, IsJson() ? JSON_LOG_ITEM : XML_LOG_ITEM, pMessage->GetID(), szMessageType[pMessage->GetKind()], pMessage->GetTime(), xmltext); szItemBuf[szItemBufSize-1] = '\0'; free(xmltext); if (IsJson() && index++ > 0) { AppendResponse(",\n"); } AppendResponse(szItemBuf); } } pPostInfo->UnlockMessages(); } AppendResponse(IsJson() ? JSON_POSTQUEUE_ITEM_END : XML_POSTQUEUE_ITEM_END); } free(szItemBuf); g_pQueueCoordinator->UnlockQueue(); AppendResponse(IsJson() ? "\n]" : "\n"); } void WriteLogXmlCommand::Execute() { if (!CheckSafeMethod()) { return; } char* szKind; char* szText; if (!NextParamAsStr(&szKind) || !NextParamAsStr(&szText)) { BuildErrorResponse(2, "Invalid parameter"); return; } DecodeStr(szText); debug("Kind=%s, Text=%s", szKind, szText); if (!strcmp(szKind, "INFO")) { info(szText); } else if (!strcmp(szKind, "WARNING")) { warn(szText); } else if (!strcmp(szKind, "ERROR")) { error(szText); } else if (!strcmp(szKind, "DETAIL")) { detail(szText); } else if (!strcmp(szKind, "DEBUG")) { debug(szText); } else { BuildErrorResponse(3, "Invalid Kind"); return; } BuildBoolResponse(true); } void ClearLogXmlCommand::Execute() { if (!CheckSafeMethod()) { return; } g_pLog->Clear(); BuildBoolResponse(true); } void ScanXmlCommand::Execute() { if (!CheckSafeMethod()) { return; } bool bSyncMode = false; // optional parameter "SyncMode" NextParamAsBool(&bSyncMode); g_pPrePostProcessor->ScanNZBDir(bSyncMode); BuildBoolResponse(true); } void HistoryXmlCommand::Execute() { AppendResponse(IsJson() ? "[\n" : "\n"); const char* XML_HISTORY_ITEM_START = "\n" "ID%i\n" "NZBID%i\n" // deprecated, use ID instead "Kind%s\n" "Name%s\n" "NZBNicename%s\n" // deprecated, use Name instead "NZBFilename%s\n" "DestDir%s\n" "Category%s\n" "ParStatus%s\n" "ScriptStatus%s\n" "FileSizeLo%i\n" "FileSizeHi%i\n" "FileSizeMB%i\n" "FileCount%i\n" "RemainingFileCount%i\n" "HistoryTime%i\n" "URL%s\n" "UrlStatus%s\n" "Parameters\n"; const char* XML_HISTORY_ITEM_LOG_START = "\n" "Log\n"; const char* XML_HISTORY_ITEM_END = "\n" "\n"; const char* JSON_HISTORY_ITEM_START = "{\n" "\"ID\" : %i,\n" "\"NZBID\" : %i,\n" // deprecated, use ID instead "\"Kind\" : \"%s\",\n" "\"Name\" : \"%s\",\n" // deprecated, use Name instead "\"NZBNicename\" : \"%s\",\n" // deprecated, use Name instead "\"NZBFilename\" : \"%s\",\n" "\"DestDir\" : \"%s\",\n" "\"Category\" : \"%s\",\n" "\"ParStatus\" : \"%s\",\n" "\"ScriptStatus\" : \"%s\",\n" "\"FileSizeLo\" : %i,\n" "\"FileSizeHi\" : %i,\n" "\"FileSizeMB\" : %i,\n" "\"FileCount\" : %i,\n" "\"RemainingFileCount\" : %i,\n" "\"HistoryTime\" : %i,\n" "\"URL\" : \"%s\",\n" "\"UrlStatus\" : \"%s\",\n" "\"Parameters\" : [\n"; const char* JSON_HISTORY_ITEM_LOG_START = "],\n" "\"Log\" : [\n"; const char* JSON_HISTORY_ITEM_END = "]\n" "}"; const char* XML_PARAMETER_ITEM = "\n" "Name%s\n" "Value%s\n" "\n"; const char* JSON_PARAMETER_ITEM = "{\n" "\"Name\" : \"%s\",\n" "\"Value\" : \"%s\"\n" "}"; const char* XML_LOG_ITEM = "\n" "ID%i\n" "Kind%s\n" "Time%i\n" "Text%s\n" "\n"; const char* JSON_LOG_ITEM = "{\n" "\"ID\" : %i,\n" "\"Kind\" : \"%s\",\n" "\"Time\" : %i,\n" "\"Text\" : \"%s\"\n" "}"; const char* szParStatusName[] = { "NONE", "FAILURE", "REPAIR_POSSIBLE", "SUCCESS" }; const char* szScriptStatusName[] = { "NONE", "UNKNOWN", "FAILURE", "SUCCESS" }; const char* szUrlStatusName[] = { "UNKNOWN", "UNKNOWN", "SUCCESS", "FAILURE", "UNKNOWN" }; const char* szMessageType[] = { "INFO", "WARNING", "ERROR", "DEBUG", "DETAIL"}; DownloadQueue* pDownloadQueue = g_pQueueCoordinator->LockQueue(); int szItemBufSize = 10240; char* szItemBuf = (char*)malloc(szItemBufSize); int index = 0; for (HistoryList::iterator it = pDownloadQueue->GetHistoryList()->begin(); it != pDownloadQueue->GetHistoryList()->end(); it++) { HistoryInfo* pHistoryInfo = *it; NZBInfo* pNZBInfo = NULL; char szNicename[1024]; pHistoryInfo->GetName(szNicename, sizeof(szNicename)); char *xmlNicename = EncodeStr(szNicename); char *xmlNZBFilename, *xmlCategory; if (pHistoryInfo->GetKind() == HistoryInfo::hkNZBInfo) { pNZBInfo = pHistoryInfo->GetNZBInfo(); unsigned long iFileSizeHi, iFileSizeLo, iFileSizeMB; Util::SplitInt64(pNZBInfo->GetSize(), &iFileSizeHi, &iFileSizeLo); iFileSizeMB = (int)(pNZBInfo->GetSize() / 1024 / 1024); xmlNZBFilename = EncodeStr(pNZBInfo->GetFilename()); char* xmlDestDir = EncodeStr(pNZBInfo->GetDestDir()); xmlCategory = EncodeStr(pNZBInfo->GetCategory()); snprintf(szItemBuf, szItemBufSize, IsJson() ? JSON_HISTORY_ITEM_START : XML_HISTORY_ITEM_START, pHistoryInfo->GetID(), pHistoryInfo->GetID(), "NZB", xmlNicename, xmlNicename, xmlNZBFilename, xmlDestDir, xmlCategory, szParStatusName[pNZBInfo->GetParStatus()], szScriptStatusName[pNZBInfo->GetScriptStatus()], iFileSizeLo, iFileSizeHi, iFileSizeMB, pNZBInfo->GetFileCount(), pNZBInfo->GetParkedFileCount(), pHistoryInfo->GetTime(), "", ""); free(xmlDestDir); } else if (pHistoryInfo->GetKind() == HistoryInfo::hkUrlInfo) { UrlInfo* pUrlInfo = pHistoryInfo->GetUrlInfo(); xmlNZBFilename = EncodeStr(pUrlInfo->GetNZBFilename()); xmlCategory = EncodeStr(pUrlInfo->GetCategory()); char* xmlURL = EncodeStr(pUrlInfo->GetURL()); snprintf(szItemBuf, szItemBufSize, IsJson() ? JSON_HISTORY_ITEM_START : XML_HISTORY_ITEM_START, pHistoryInfo->GetID(), pHistoryInfo->GetID(), "URL", xmlNicename, xmlNicename, xmlNZBFilename, "", xmlCategory, "", "", 0, 0, 0, 0, 0, pHistoryInfo->GetTime(), xmlURL, szUrlStatusName[pUrlInfo->GetStatus()]); free(xmlURL); } szItemBuf[szItemBufSize-1] = '\0'; free(xmlNicename); free(xmlNZBFilename); free(xmlCategory); if (IsJson() && index++ > 0) { AppendResponse(",\n"); } AppendResponse(szItemBuf); if (pNZBInfo) { // Post-processing parameters int iParamIndex = 0; for (NZBParameterList::iterator it = pNZBInfo->GetParameters()->begin(); it != pNZBInfo->GetParameters()->end(); it++) { NZBParameter* pParameter = *it; char* xmlName = EncodeStr(pParameter->GetName()); char* xmlValue = EncodeStr(pParameter->GetValue()); snprintf(szItemBuf, szItemBufSize, IsJson() ? JSON_PARAMETER_ITEM : XML_PARAMETER_ITEM, xmlName, xmlValue); szItemBuf[szItemBufSize-1] = '\0'; free(xmlName); free(xmlValue); if (IsJson() && iParamIndex++ > 0) { AppendResponse(",\n"); } AppendResponse(szItemBuf); } } AppendResponse(IsJson() ? JSON_HISTORY_ITEM_LOG_START : XML_HISTORY_ITEM_LOG_START); if (pNZBInfo) { // Log-Messages NZBInfo::Messages* pMessages = pNZBInfo->LockMessages(); if (!pMessages->empty()) { int iLogIndex = 0; for (NZBInfo::Messages::iterator it = pMessages->begin(); it != pMessages->end(); it++) { Message* pMessage = *it; char* xmltext = EncodeStr(pMessage->GetText()); snprintf(szItemBuf, szItemBufSize, IsJson() ? JSON_LOG_ITEM : XML_LOG_ITEM, pMessage->GetID(), szMessageType[pMessage->GetKind()], pMessage->GetTime(), xmltext); szItemBuf[szItemBufSize-1] = '\0'; free(xmltext); if (IsJson() && iLogIndex++ > 0) { AppendResponse(",\n"); } AppendResponse(szItemBuf); } } pNZBInfo->UnlockMessages(); } AppendResponse(IsJson() ? JSON_HISTORY_ITEM_END : XML_HISTORY_ITEM_END); } free(szItemBuf); AppendResponse(IsJson() ? "\n]" : "\n"); g_pQueueCoordinator->UnlockQueue(); } // bool appendurl(string NZBFilename, string Category, int Priority, bool AddToTop, string URL) void DownloadUrlXmlCommand::Execute() { if (!CheckSafeMethod()) { return; } int bFirst = true; char* szNZBFileName; while (NextParamAsStr(&szNZBFileName)) { bFirst = false; char* szCategory; if (!NextParamAsStr(&szCategory)) { BuildErrorResponse(2, "Invalid parameter"); return; } int iPriority = 0; if (!NextParamAsInt(&iPriority)) { BuildErrorResponse(2, "Invalid parameter"); return; } bool bAddTop; if (!NextParamAsBool(&bAddTop)) { BuildErrorResponse(2, "Invalid parameter"); return; } char* szURL; if (!NextParamAsStr(&szURL)) { BuildErrorResponse(2, "Invalid parameter"); return; } DecodeStr(szNZBFileName); DecodeStr(szCategory); DecodeStr(szURL); debug("URL=%s", szURL); UrlInfo* pUrlInfo = new UrlInfo(); pUrlInfo->SetURL(szURL); pUrlInfo->SetNZBFilename(szNZBFileName); pUrlInfo->SetCategory(szCategory); pUrlInfo->SetPriority(iPriority); char szNicename[1024]; pUrlInfo->GetName(szNicename, sizeof(szNicename)); info("Request: Queue %s", szNicename); g_pUrlCoordinator->AddUrlToQueue(pUrlInfo, bAddTop); } if (bFirst) { BuildErrorResponse(2, "Invalid parameter"); return; } BuildBoolResponse(true); } void UrlQueueXmlCommand::Execute() { AppendResponse(IsJson() ? "[\n" : "\n"); const char* XML_URLQUEUE_ITEM = "\n" "ID%i\n" "NZBFilename%s\n" "URL%s\n" "Name%s\n" "Category%s\n" "Priority%i\n" "\n"; const char* JSON_URLQUEUE_ITEM = "{\n" "\"ID\" : %i,\n" "\"NZBFilename\" : \"%s\",\n" "\"URL\" : \"%s\",\n" "\"Name\" : \"%s\",\n" "\"Category\" : \"%s\",\n" "\"Priority\" : %i\n" "}"; UrlQueue* pUrlQueue = g_pQueueCoordinator->LockQueue()->GetUrlQueue(); int szItemBufSize = 10240; char* szItemBuf = (char*)malloc(szItemBufSize); int index = 0; for (UrlQueue::iterator it = pUrlQueue->begin(); it != pUrlQueue->end(); it++) { UrlInfo* pUrlInfo = *it; char szNicename[1024]; pUrlInfo->GetName(szNicename, sizeof(szNicename)); char* xmlNicename = EncodeStr(szNicename); char* xmlNZBFilename = EncodeStr(pUrlInfo->GetNZBFilename()); char* xmlURL = EncodeStr(pUrlInfo->GetURL()); char* xmlCategory = EncodeStr(pUrlInfo->GetCategory()); snprintf(szItemBuf, szItemBufSize, IsJson() ? JSON_URLQUEUE_ITEM : XML_URLQUEUE_ITEM, pUrlInfo->GetID(), xmlNZBFilename, xmlURL, xmlNicename, xmlCategory, pUrlInfo->GetPriority()); szItemBuf[szItemBufSize-1] = '\0'; free(xmlNicename); free(xmlNZBFilename); free(xmlURL); free(xmlCategory); if (IsJson() && index++ > 0) { AppendResponse(",\n"); } AppendResponse(szItemBuf); } free(szItemBuf); g_pQueueCoordinator->UnlockQueue(); AppendResponse(IsJson() ? "\n]" : "\n"); } // struct[] config() void ConfigXmlCommand::Execute() { const char* XML_CONFIG_ITEM = "\n" "Name%s\n" "Value%s\n" "\n"; const char* JSON_CONFIG_ITEM = "{\n" "\"Name\" : \"%s\",\n" "\"Value\" : \"%s\"\n" "}"; AppendResponse(IsJson() ? "[\n" : "\n"); Options::OptEntries* pOptEntries = g_pOptions->LockOptEntries(); int szItemBufSize = 10240; char* szItemBuf = (char*)malloc(szItemBufSize); int index = 0; for (Options::OptEntries::iterator it = pOptEntries->begin(); it != pOptEntries->end(); it++) { Options::OptEntry* pOptEntry = *it; char* xmlName = EncodeStr(pOptEntry->GetName()); char* xmlValue = EncodeStr(pOptEntry->GetValue()); snprintf(szItemBuf, szItemBufSize, IsJson() ? JSON_CONFIG_ITEM : XML_CONFIG_ITEM, xmlName, xmlValue); szItemBuf[szItemBufSize-1] = '\0'; free(xmlName); free(xmlValue); if (IsJson() && index++ > 0) { AppendResponse(",\n"); } AppendResponse(szItemBuf); } g_pOptions->UnlockOptEntries(); free(szItemBuf); AppendResponse(IsJson() ? "\n]" : "\n"); } // struct[] loadconfig(string domain) void LoadConfigXmlCommand::Execute() { const char* XML_CONFIG_ITEM = "\n" "Name%s\n" "Value%s\n" "\n"; const char* JSON_CONFIG_ITEM = "{\n" "\"Name\" : \"%s\",\n" "\"Value\" : \"%s\"\n" "}"; char* szDomain; if (!NextParamAsStr(&szDomain)) { BuildErrorResponse(2, "Invalid parameter"); return; } const char* szConfigFile = NULL; Options::EDomain eDomain; if (!strcasecmp(szDomain, "SERVER")) { eDomain = Options::dmServer; szConfigFile = g_pOptions->GetConfigFilename(); } else if (!strcasecmp(szDomain, "POST")) { eDomain = Options::dmPostProcess; szConfigFile = g_pOptions->GetPostConfigFilename(); } else { BuildErrorResponse(2, "Invalid parameter"); return; } Options::OptEntries* pOptEntries = new Options::OptEntries(); if (!g_pOptions->LoadConfig(eDomain, pOptEntries)) { BuildErrorResponse(3, "Could not read configuration file %s", szConfigFile); delete pOptEntries; return; } AppendResponse(IsJson() ? "[\n" : "\n"); int szItemBufSize = 10240; char* szItemBuf = (char*)malloc(szItemBufSize); int index = 0; for (Options::OptEntries::iterator it = pOptEntries->begin(); it != pOptEntries->end(); it++) { Options::OptEntry* pOptEntry = *it; char* xmlName = EncodeStr(pOptEntry->GetName()); char* xmlValue = EncodeStr(pOptEntry->GetValue()); snprintf(szItemBuf, szItemBufSize, IsJson() ? JSON_CONFIG_ITEM : XML_CONFIG_ITEM, xmlName, xmlValue); szItemBuf[szItemBufSize-1] = '\0'; free(xmlName); free(xmlValue); if (IsJson() && index++ > 0) { AppendResponse(",\n"); } AppendResponse(szItemBuf); } delete pOptEntries; free(szItemBuf); AppendResponse(IsJson() ? "\n]" : "\n"); } // bool saveconfig(string domain, struct[] data) void SaveConfigXmlCommand::Execute() { char* szDomain; if (!NextParamAsStr(&szDomain)) { BuildErrorResponse(2, "Invalid parameter"); return; } const char* szConfigFile = NULL; Options::EDomain eDomain; if (!strcasecmp(szDomain, "SERVER")) { eDomain = Options::dmServer; szConfigFile = g_pOptions->GetConfigFilename(); } else if (!strcasecmp(szDomain, "POST")) { eDomain = Options::dmPostProcess; szConfigFile = g_pOptions->GetPostConfigFilename(); if (!szConfigFile) { BuildErrorResponse(3, "Post-processing script configuration file is not defined"); return; } } else { BuildErrorResponse(2, "Invalid parameter"); return; } Options::OptEntries* pOptEntries = new Options::OptEntries(); char* szName; char* szValue; char* szDummy; while (NextParamAsStr(&szDummy) && NextParamAsStr(&szName) && NextParamAsStr(&szDummy) && NextParamAsStr(&szValue)) { DecodeStr(szName); DecodeStr(szValue); pOptEntries->push_back(new Options::OptEntry(szName, szValue)); } // save to config file bool bOK = g_pOptions->SaveConfig(eDomain, pOptEntries); delete pOptEntries; BuildBoolResponse(bOK); }