/* * This file is part of nzbget * * 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: 1250 $ * $Date: 2015-03-31 15:52:57 -0400 (Tue, 31 Mar 2015) $ * */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #ifdef WIN32 #include "win32.h" #endif #include #include #include #include #ifndef WIN32 #include #endif #include "nzbget.h" #include "XmlRpc.h" #include "Log.h" #include "Options.h" #include "Scanner.h" #include "FeedCoordinator.h" #include "ServerPool.h" #include "Util.h" #include "Maintenance.h" #include "StatMeter.h" #include "ArticleWriter.h" #include "DiskState.h" extern Options* g_pOptions; extern Scanner* g_pScanner; extern FeedCoordinator* g_pFeedCoordinator; extern ServerPool* g_pServerPool; extern Maintenance* g_pMaintenance; extern StatMeter* g_pStatMeter; extern ArticleCache* g_pArticleCache; extern DiskState* g_pDiskState; extern void ExitProc(); extern void Reload(); class ErrorXmlCommand: public XmlCommand { private: int m_iErrCode; const char* m_szErrText; public: ErrorXmlCommand(int iErrCode, const char* szErrText); virtual void Execute(); }; class PauseUnpauseXmlCommand: public XmlCommand { public: enum EPauseAction { paDownload, paPostProcess, paScan }; private: bool m_bPause; EPauseAction m_eEPauseAction; public: PauseUnpauseXmlCommand(bool bPause, EPauseAction eEPauseAction); virtual void Execute(); }; class ScheduleResumeXmlCommand: public XmlCommand { public: virtual void Execute(); }; class ShutdownXmlCommand: public XmlCommand { public: virtual void Execute(); }; class ReloadXmlCommand: public XmlCommand { public: virtual void Execute(); }; class VersionXmlCommand: public XmlCommand { public: virtual void Execute(); }; class DumpDebugXmlCommand: public XmlCommand { public: virtual void Execute(); }; class SetDownloadRateXmlCommand: public XmlCommand { public: virtual void Execute(); }; class StatusXmlCommand: public XmlCommand { public: virtual void Execute(); }; class LogXmlCommand: public XmlCommand { protected: int m_iIDFrom; int m_iNrEntries; virtual MessageList* LockMessages(); virtual void UnlockMessages(); public: virtual void Execute(); }; class NzbInfoXmlCommand: public XmlCommand { protected: void AppendNZBInfoFields(NZBInfo* pNZBInfo); void AppendPostInfoFields(PostInfo* pPostInfo, int iLogEntries, bool bPostQueue); }; class ListFilesXmlCommand: public XmlCommand { public: virtual void Execute(); }; class ListGroupsXmlCommand: public NzbInfoXmlCommand { private: const char* DetectStatus(NZBInfo* pNZBInfo); public: virtual void Execute(); }; class EditQueueXmlCommand: public XmlCommand { public: virtual void Execute(); }; class DownloadXmlCommand: public XmlCommand { public: virtual void Execute(); }; class PostQueueXmlCommand: public NzbInfoXmlCommand { public: virtual void Execute(); }; class WriteLogXmlCommand: public XmlCommand { public: virtual void Execute(); }; class ClearLogXmlCommand: public XmlCommand { public: virtual void Execute(); }; class ScanXmlCommand: public XmlCommand { public: virtual void Execute(); }; class HistoryXmlCommand: public NzbInfoXmlCommand { private: const char* DetectStatus(HistoryInfo* pHistoryInfo); public: virtual void Execute(); }; class UrlQueueXmlCommand: public XmlCommand { public: virtual void Execute(); }; class ConfigXmlCommand: public XmlCommand { public: virtual void Execute(); }; class LoadConfigXmlCommand: public XmlCommand { public: virtual void Execute(); }; class SaveConfigXmlCommand: public XmlCommand { public: virtual void Execute(); }; class ConfigTemplatesXmlCommand: public XmlCommand { public: virtual void Execute(); }; class ViewFeedXmlCommand: public XmlCommand { private: bool m_bPreview; public: ViewFeedXmlCommand(bool bPreview); virtual void Execute(); }; class FetchFeedXmlCommand: public XmlCommand { public: virtual void Execute(); }; class EditServerXmlCommand: public XmlCommand { public: virtual void Execute(); }; class ReadUrlXmlCommand: public XmlCommand { public: virtual void Execute(); }; class CheckUpdatesXmlCommand: public XmlCommand { public: virtual void Execute(); }; class StartUpdateXmlCommand: public XmlCommand { public: virtual void Execute(); }; class LogUpdateXmlCommand: public LogXmlCommand { protected: virtual MessageList* LockMessages(); virtual void UnlockMessages(); }; class ServerVolumesXmlCommand: public XmlCommand { public: virtual void Execute(); }; class ResetServerVolumeXmlCommand: public XmlCommand { public: virtual void Execute(); }; class LoadLogXmlCommand: public LogXmlCommand { private: MessageList m_messages; int m_iNZBID; NZBInfo* m_pNZBInfo; protected: virtual void Execute(); virtual MessageList* LockMessages(); virtual void UnlockMessages(); }; class TestServerXmlCommand: public XmlCommand { private: char* m_szErrText; class TestConnection : public NNTPConnection { protected: TestServerXmlCommand* m_pOwner; virtual void PrintError(const char* szErrMsg) { m_pOwner->PrintError(szErrMsg); } public: TestConnection(NewsServer* pNewsServer, TestServerXmlCommand* pOwner): NNTPConnection(pNewsServer), m_pOwner(pOwner) {} }; void PrintError(const char* szErrMsg); public: virtual void Execute(); }; //***************************************************************** // XmlRpcProcessor XmlRpcProcessor::XmlRpcProcessor() { m_szRequest = NULL; m_eProtocol = rpUndefined; m_eHttpMethod = hmPost; m_szUrl = NULL; m_szContentType = NULL; } XmlRpcProcessor::~XmlRpcProcessor() { free(m_szUrl); } void XmlRpcProcessor::SetUrl(const char* szUrl) { m_szUrl = strdup(szUrl); WebUtil::URLDecode(m_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'; char szRequestId[100]; szRequestId[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); iLen = iLen >= sizeof(szMethodName) ? sizeof(szMethodName) - 1 : iLen; 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)) { iValueLen = iValueLen >= sizeof(szMethodName) ? sizeof(szMethodName) - 1 : iValueLen; strncpy(szMethodName, szMethodPtr + 1, iValueLen - 2); szMethodName[iValueLen - 2] = '\0'; } if (const char* szRequestIdPtr = WebUtil::JsonFindField(m_szRequest, "id", &iValueLen)) { iValueLen = iValueLen >= sizeof(szRequestId) ? sizeof(szRequestId) - 1 : iValueLen; strncpy(szRequestId, szRequestIdPtr, iValueLen); szRequestId[iValueLen] = '\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->SetUserAccess(m_eUserAccess); command->PrepareParams(); command->Execute(); BuildResponse(command->GetResponse(), command->GetCallbackFunc(), command->GetFault(), szRequestId); 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(), NULL); delete command; } else { cStringBuilder.Append(""); BuildResponse(cStringBuilder.GetBuffer(), "", false, NULL); } } void XmlRpcProcessor::BuildResponse(const char* szResponse, const char* szCallbackFunc, bool bFault, const char* szRequestId) { 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_ID_OPEN[] = "\"id\" : "; const char JSON_ID_CLOSE[] = ",\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); if (!bXmlRpc && szRequestId && *szRequestId) { m_cResponse.Append(JSON_ID_OPEN); m_cResponse.Append(szRequestId); m_cResponse.Append(JSON_ID_CLOSE); } 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 (m_eUserAccess == uaAdd && !(!strcasecmp(szMethodName, "append") || !strcasecmp(szMethodName, "appendurl") || !strcasecmp(szMethodName, "version"))) { command = new ErrorXmlCommand(401, "Access denied"); warn("Received request \"%s\" from add-user, access denied", szMethodName); } else if (m_eUserAccess == uaRestricted && !strcasecmp(szMethodName, "saveconfig")) { command = new ErrorXmlCommand(401, "Access denied"); warn("Received request \"%s\" from restricted user, access denied", szMethodName); } else if (!strcasecmp(szMethodName, "pause") || !strcasecmp(szMethodName, "pausedownload") || !strcasecmp(szMethodName, "pausedownload2")) { command = new PauseUnpauseXmlCommand(true, PauseUnpauseXmlCommand::paDownload); } else if (!strcasecmp(szMethodName, "resume") || !strcasecmp(szMethodName, "resumedownload") || !strcasecmp(szMethodName, "resumedownload2")) { command = new PauseUnpauseXmlCommand(false, PauseUnpauseXmlCommand::paDownload); } 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") || !strcasecmp(szMethodName, "appendurl")) { 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, "loadlog")) { command = new LoadLogXmlCommand(); } 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, "scheduleresume")) { command = new ScheduleResumeXmlCommand(); } else if (!strcasecmp(szMethodName, "history")) { command = new HistoryXmlCommand(); } 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 if (!strcasecmp(szMethodName, "configtemplates")) { command = new ConfigTemplatesXmlCommand(); } else if (!strcasecmp(szMethodName, "viewfeed")) { command = new ViewFeedXmlCommand(false); } else if (!strcasecmp(szMethodName, "previewfeed")) { command = new ViewFeedXmlCommand(true); } else if (!strcasecmp(szMethodName, "fetchfeed")) { command = new FetchFeedXmlCommand(); } else if (!strcasecmp(szMethodName, "editserver")) { command = new EditServerXmlCommand(); } else if (!strcasecmp(szMethodName, "readurl")) { command = new ReadUrlXmlCommand(); } else if (!strcasecmp(szMethodName, "checkupdates")) { command = new CheckUpdatesXmlCommand(); } else if (!strcasecmp(szMethodName, "startupdate")) { command = new StartUpdateXmlCommand(); } else if (!strcasecmp(szMethodName, "logupdate")) { command = new LogUpdateXmlCommand(); } else if (!strcasecmp(szMethodName, "servervolumes")) { command = new ServerVolumesXmlCommand(); } else if (!strcasecmp(szMethodName, "resetservervolume")) { command = new ResetServerVolumeXmlCommand(); } else if (!strcasecmp(szMethodName, "testserver")) { command = new TestServerXmlCommand(); } 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::BuildIntResponse(int iValue) { const char* XML_RESPONSE_INT_BODY = "%i"; const char* JSON_RESPONSE_INT_BODY = "%i"; char szContent[1024]; snprintf(szContent, 1024, IsJson() ? JSON_RESPONSE_INT_BODY : XML_RESPONSE_INT_BODY, iValue); 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); } } char* XmlCommand::XmlNextValue(char* szXml, const char* szTag, int* pValueLength) { int iValueLen; const char* szValue = WebUtil::XmlFindTag(szXml, "value", &iValueLen); if (szValue) { char* szTagContent = (char*)WebUtil::XmlFindTag(szValue, szTag, pValueLength); if (szTagContent <= szValue + iValueLen) { return szTagContent; } } return NULL; } 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; while (strchr("-+0123456789&", *m_szRequestPtr)) { m_szRequestPtr++; } 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 = XmlNextValue(m_szRequestPtr, "i4", &iLen); if (!szParam) { szParam = XmlNextValue(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 (!strncmp(szParam, "true", 4)) { *bValue = true; return true; } else if (!strncmp(szParam, "false", 5)) { *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 = XmlNextValue(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(szParam, '&'); 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 = XmlNextValue(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() { 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; g_pOptions->SetResumeTime(0); switch (m_eEPauseAction) { case paDownload: g_pOptions->SetPauseDownload(m_bPause); break; case paPostProcess: g_pOptions->SetPausePostProcess(m_bPause); break; case paScan: g_pOptions->SetPauseScan(m_bPause); break; default: bOK = false; } BuildBoolResponse(bOK); } // bool scheduleresume(int Seconds) void ScheduleResumeXmlCommand::Execute() { if (!CheckSafeMethod()) { return; } int iSeconds = 0; if (!NextParamAsInt(&iSeconds) || iSeconds < 0) { BuildErrorResponse(2, "Invalid parameter"); return; } time_t tCurTime = time(NULL); g_pOptions->SetResumeTime(tCurTime + iSeconds); BuildBoolResponse(true); } 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_pLog->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(iRate * 1024); BuildBoolResponse(true); } void StatusXmlCommand::Execute() { const char* XML_STATUS_START = "\n" "RemainingSizeLo%u\n" "RemainingSizeHi%u\n" "RemainingSizeMB%i\n" "ForcedSizeLo%u\n" "ForcedSizeHi%u\n" "ForcedSizeMB%i\n" "DownloadedSizeLo%u\n" "DownloadedSizeHi%u\n" "DownloadedSizeMB%i\n" "ArticleCacheLo%u\n" "ArticleCacheHi%u\n" "ArticleCacheMB%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" // deprecated (same as DownloadPaused) "ServerStandBy%s\n" "PostPaused%s\n" "ScanPaused%s\n" "FreeDiskSpaceLo%u\n" "FreeDiskSpaceHi%u\n" "FreeDiskSpaceMB%i\n" "ServerTime%i\n" "ResumeTime%i\n" "FeedActive%s\n" "NewsServers\n"; const char* XML_STATUS_END = "\n" "\n"; const char* JSON_STATUS_START = "{\n" "\"RemainingSizeLo\" : %u,\n" "\"RemainingSizeHi\" : %u,\n" "\"RemainingSizeMB\" : %i,\n" "\"ForcedSizeLo\" : %u,\n" "\"ForcedSizeHi\" : %u,\n" "\"ForcedSizeMB\" : %i,\n" "\"DownloadedSizeLo\" : %u,\n" "\"DownloadedSizeHi\" : %u,\n" "\"DownloadedSizeMB\" : %i,\n" "\"ArticleCacheLo\" : %u,\n" "\"ArticleCacheHi\" : %u,\n" "\"ArticleCacheMB\" : %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" // deprecated (same as DownloadPaused) "\"ServerStandBy\" : %s,\n" "\"PostPaused\" : %s,\n" "\"ScanPaused\" : %s,\n" "\"FreeDiskSpaceLo\" : %u,\n" "\"FreeDiskSpaceHi\" : %u,\n" "\"FreeDiskSpaceMB\" : %i,\n" "\"ServerTime\" : %i,\n" "\"ResumeTime\" : %i,\n" "\"FeedActive\" : %s,\n" "\"NewsServers\" : [\n"; const char* JSON_STATUS_END = "]\n" "}"; const char* XML_NEWSSERVER_ITEM = "\n" "ID%i\n" "Active%s\n" "\n"; const char* JSON_NEWSSERVER_ITEM = "{\n" "\"ID\" : %i,\n" "\"Active\" : %s\n" "}"; DownloadQueue *pDownloadQueue = DownloadQueue::Lock(); int iPostJobCount = 0; int iUrlCount = 0; for (NZBList::iterator it = pDownloadQueue->GetQueue()->begin(); it != pDownloadQueue->GetQueue()->end(); it++) { NZBInfo* pNZBInfo = *it; iPostJobCount += pNZBInfo->GetPostInfo() ? 1 : 0; iUrlCount += pNZBInfo->GetKind() == NZBInfo::nkUrl ? 1 : 0; } long long iRemainingSize, iForcedSize; pDownloadQueue->CalcRemainingSize(&iRemainingSize, &iForcedSize); DownloadQueue::Unlock(); unsigned long iRemainingSizeHi, iRemainingSizeLo; Util::SplitInt64(iRemainingSize, &iRemainingSizeHi, &iRemainingSizeLo); int iRemainingMBytes = (int)(iRemainingSize / 1024 / 1024); unsigned long iForcedSizeHi, iForcedSizeLo; Util::SplitInt64(iForcedSize, &iForcedSizeHi, &iForcedSizeLo); int iForcedMBytes = (int)(iForcedSize / 1024 / 1024); long long iArticleCache = g_pArticleCache->GetAllocated(); unsigned long iArticleCacheHi, iArticleCacheLo; Util::SplitInt64(iArticleCache, &iArticleCacheHi, &iArticleCacheLo); int iArticleCacheMBytes = (int)(iArticleCache / 1024 / 1024); int iDownloadRate = (int)(g_pStatMeter->CalcCurrentDownloadSpeed()); int iDownloadLimit = (int)(g_pOptions->GetDownloadRate()); bool bDownloadPaused = g_pOptions->GetPauseDownload(); bool bPostPaused = g_pOptions->GetPausePostProcess(); bool bScanPaused = g_pOptions->GetPauseScan(); int iThreadCount = Thread::GetThreadCount() - 1; // not counting itself unsigned long iDownloadedSizeHi, iDownloadedSizeLo; int iUpTimeSec, iDownloadTimeSec; long long iAllBytes; bool bServerStandBy; g_pStatMeter->CalcTotalStat(&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); int iServerTime = time(NULL); int iResumeTime = g_pOptions->GetResumeTime(); bool bFeedActive = g_pFeedCoordinator->HasActiveDownloads(); char szContent[3072]; snprintf(szContent, 3072, IsJson() ? JSON_STATUS_START : XML_STATUS_START, iRemainingSizeLo, iRemainingSizeHi, iRemainingMBytes, iForcedSizeLo, iForcedSizeHi, iForcedMBytes, iDownloadedSizeLo, iDownloadedSizeHi, iDownloadedMBytes, iArticleCacheLo, iArticleCacheHi, iArticleCacheMBytes, iDownloadRate, iAverageDownloadRate, iDownloadLimit, iThreadCount, iPostJobCount, iPostJobCount, iUrlCount, iUpTimeSec, iDownloadTimeSec, BoolToStr(bDownloadPaused), BoolToStr(bDownloadPaused), BoolToStr(bDownloadPaused), BoolToStr(bServerStandBy), BoolToStr(bPostPaused), BoolToStr(bScanPaused), iFreeDiskSpaceLo, iFreeDiskSpaceHi, iFreeDiskSpaceMB, iServerTime, iResumeTime, BoolToStr(bFeedActive)); szContent[3072-1] = '\0'; AppendResponse(szContent); int index = 0; for (Servers::iterator it = g_pServerPool->GetServers()->begin(); it != g_pServerPool->GetServers()->end(); it++) { NewsServer* pServer = *it; snprintf(szContent, sizeof(szContent), IsJson() ? JSON_NEWSSERVER_ITEM : XML_NEWSSERVER_ITEM, pServer->GetID(), BoolToStr(pServer->GetActive())); szContent[3072-1] = '\0'; if (IsJson() && index++ > 0) { AppendResponse(",\n"); } AppendResponse(szContent); } AppendResponse(IsJson() ? JSON_STATUS_END : XML_STATUS_END); } // struct[] log(idfrom, entries) void LogXmlCommand::Execute() { m_iIDFrom = 0; m_iNrEntries = 0; if (!NextParamAsInt(&m_iIDFrom) || !NextParamAsInt(&m_iNrEntries) || (m_iNrEntries > 0 && m_iIDFrom > 0)) { BuildErrorResponse(2, "Invalid parameter"); return; } debug("iIDFrom=%i", m_iIDFrom); debug("iNrEntries=%i", m_iNrEntries); AppendResponse(IsJson() ? "[\n" : "\n"); MessageList* pMessages = LockMessages(); int iStart = pMessages->size(); if (m_iNrEntries > 0) { if (m_iNrEntries > (int)pMessages->size()) { m_iNrEntries = pMessages->size(); } iStart = pMessages->size() - m_iNrEntries; } if (m_iIDFrom > 0 && !pMessages->empty()) { m_iNrEntries = pMessages->size(); iStart = m_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 iItemBufSize = 10240; char* szItemBuf = (char*)malloc(iItemBufSize); 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, iItemBufSize, IsJson() ? JSON_LOG_ITEM : XML_LOG_ITEM, pMessage->GetID(), szMessageType[pMessage->GetKind()], pMessage->GetTime(), xmltext); szItemBuf[iItemBufSize-1] = '\0'; free(xmltext); if (IsJson() && index++ > 0) { AppendResponse(",\n"); } AppendResponse(szItemBuf); } free(szItemBuf); UnlockMessages(); AppendResponse(IsJson() ? "\n]" : "\n"); } MessageList* LogXmlCommand::LockMessages() { return g_pLog->LockMessages(); } void LogXmlCommand::UnlockMessages() { g_pLog->UnlockMessages(); } // 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 = DownloadQueue::Lock(); const char* XML_LIST_ITEM = "\n" "ID%i\n" "FileSizeLo%u\n" "FileSizeHi%u\n" "RemainingSizeLo%u\n" "RemainingSizeHi%u\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" // deprecated, use "Priority" of group instead "ActiveDownloads%i\n" "Progress%u\n" "\n"; const char* JSON_LIST_ITEM = "{\n" "\"ID\" : %i,\n" "\"FileSizeLo\" : %u,\n" "\"FileSizeHi\" : %u,\n" "\"RemainingSizeLo\" : %u,\n" "\"RemainingSizeHi\" : %u,\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" // deprecated, use "Priority" of group instead "\"ActiveDownloads\" : %i,\n" "\"Progress\" : %i\n" "}"; int iItemBufSize = 10240; char* szItemBuf = (char*)malloc(iItemBufSize); int index = 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; 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()); int iProgress = pFileInfo->GetFailedSize() == 0 && pFileInfo->GetSuccessSize() == 0 ? 0 : (int)(1000 - pFileInfo->GetRemainingSize() * 1000 / (pFileInfo->GetSize() - pFileInfo->GetMissedSize())); snprintf(szItemBuf, iItemBufSize, 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->GetNZBInfo()->GetPriority(), pFileInfo->GetActiveDownloads(), iProgress); szItemBuf[iItemBufSize-1] = '\0'; free(xmlNZBFilename); free(xmlSubject); free(xmlFilename); free(xmlDestDir); free(xmlCategory); free(xmlNZBNicename); if (IsJson() && index++ > 0) { AppendResponse(",\n"); } AppendResponse(szItemBuf); } } } free(szItemBuf); DownloadQueue::Unlock(); AppendResponse(IsJson() ? "\n]" : "\n"); } void NzbInfoXmlCommand::AppendNZBInfoFields(NZBInfo* pNZBInfo) { const char* XML_NZB_ITEM_START = "NZBID%i\n" "NZBName%s\n" "NZBNicename%s\n" // deprecated, use "NZBName" instead "Kind%s\n" "URL%s\n" "NZBFilename%s\n" "DestDir%s\n" "FinalDir%s\n" "Category%s\n" "ParStatus%s\n" "UnpackStatus%s\n" "MoveStatus%s\n" "ScriptStatus%s\n" "DeleteStatus%s\n" "MarkStatus%s\n" "UrlStatus%s\n" "FileSizeLo%u\n" "FileSizeHi%u\n" "FileSizeMB%i\n" "FileCount%i\n" "MinPostTime%i\n" "MaxPostTime%i\n" "TotalArticles%i\n" "SuccessArticles%i\n" "FailedArticles%i\n" "Health%i\n" "CriticalHealth%i\n" "DupeKey%s\n" "DupeScore%i\n" "DupeMode%s\n" "Deleted%s\n" // deprecated, use "DeleteStatus" instead "DownloadedSizeLo%u\n" "DownloadedSizeHi%u\n" "DownloadedSizeMB%i\n" "DownloadTimeSec%i\n" "PostTotalTimeSec%i\n" "ParTimeSec%i\n" "RepairTimeSec%i\n" "UnpackTimeSec%i\n" "MessageCount%i\n" "Parameters\n"; const char* XML_NZB_ITEM_SCRIPT_START = "\n" "ScriptStatuses\n"; const char* XML_NZB_ITEM_STATS_START = "\n" "ServerStats\n"; const char* XML_NZB_ITEM_END = "\n"; const char* JSON_NZB_ITEM_START = "\"NZBID\" : %i,\n" "\"NZBName\" : \"%s\",\n" "\"NZBNicename\" : \"%s\",\n" // deprecated, use NZBName instead "\"Kind\" : \"%s\",\n" "\"URL\" : \"%s\",\n" "\"NZBFilename\" : \"%s\",\n" "\"DestDir\" : \"%s\",\n" "\"FinalDir\" : \"%s\",\n" "\"Category\" : \"%s\",\n" "\"ParStatus\" : \"%s\",\n" "\"UnpackStatus\" : \"%s\",\n" "\"MoveStatus\" : \"%s\",\n" "\"ScriptStatus\" : \"%s\",\n" "\"DeleteStatus\" : \"%s\",\n" "\"MarkStatus\" : \"%s\",\n" "\"UrlStatus\" : \"%s\",\n" "\"FileSizeLo\" : %u,\n" "\"FileSizeHi\" : %u,\n" "\"FileSizeMB\" : %i,\n" "\"FileCount\" : %i,\n" "\"MinPostTime\" : %i,\n" "\"MaxPostTime\" : %i,\n" "\"TotalArticles\" : %i,\n" "\"SuccessArticles\" : %i,\n" "\"FailedArticles\" : %i,\n" "\"Health\" : %i,\n" "\"CriticalHealth\" : %i,\n" "\"DupeKey\" : \"%s\",\n" "\"DupeScore\" : %i,\n" "\"DupeMode\" : \"%s\",\n" "\"Deleted\" : %s,\n" // deprecated, use "DeleteStatus" instead "\"DownloadedSizeLo\" : %u,\n" "\"DownloadedSizeHi\" : %u,\n" "\"DownloadedSizeMB\" : %i,\n" "\"DownloadTimeSec\" : %i,\n" "\"PostTotalTimeSec\" : %i,\n" "\"ParTimeSec\" : %i,\n" "\"RepairTimeSec\" : %i,\n" "\"UnpackTimeSec\" : %i,\n" "\"MessageCount\" : %i,\n" "\"Parameters\" : [\n"; const char* JSON_NZB_ITEM_SCRIPT_START = "],\n" "\"ScriptStatuses\" : [\n"; const char* JSON_NZB_ITEM_STATS_START = "],\n" "\"ServerStats\" : [\n"; const char* JSON_NZB_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_SCRIPT_ITEM = "\n" "Name%s\n" "Status%s\n" "\n"; const char* JSON_SCRIPT_ITEM = "{\n" "\"Name\" : \"%s\",\n" "\"Status\" : \"%s\"\n" "}"; const char* XML_STAT_ITEM = "\n" "ServerID%i\n" "SuccessArticles%i\n" "FailedArticles%i\n" "\n"; const char* JSON_STAT_ITEM = "{\n" "\"ServerID\" : %i,\n" "\"SuccessArticles\" : %i,\n" "\"FailedArticles\" : %i\n" "}"; const char* szKindName[] = { "NZB", "URL" }; const char* szParStatusName[] = { "NONE", "NONE", "FAILURE", "SUCCESS", "REPAIR_POSSIBLE", "MANUAL" }; const char* szUnpackStatusName[] = { "NONE", "NONE", "FAILURE", "SUCCESS", "SPACE", "PASSWORD" }; const char* szMoveStatusName[] = { "NONE", "FAILURE", "SUCCESS" }; const char* szScriptStatusName[] = { "NONE", "FAILURE", "SUCCESS" }; const char* szDeleteStatusName[] = { "NONE", "MANUAL", "HEALTH", "DUPE", "BAD" }; const char* szMarkStatusName[] = { "NONE", "BAD", "GOOD", "SUCCESS" }; const char* szUrlStatusName[] = { "NONE", "UNKNOWN", "SUCCESS", "FAILURE", "UNKNOWN", "SCAN_SKIPPED", "SCAN_FAILURE" }; const char* szDupeModeName[] = { "SCORE", "ALL", "FORCE" }; int iItemBufSize = 10240; char* szItemBuf = (char*)malloc(iItemBufSize); unsigned long iFileSizeHi, iFileSizeLo, iFileSizeMB; Util::SplitInt64(pNZBInfo->GetSize(), &iFileSizeHi, &iFileSizeLo); iFileSizeMB = (int)(pNZBInfo->GetSize() / 1024 / 1024); unsigned long iDownloadedSizeHi, iDownloadedSizeLo, iDownloadedSizeMB; Util::SplitInt64(pNZBInfo->GetDownloadedSize(), &iDownloadedSizeHi, &iDownloadedSizeLo); iDownloadedSizeMB = (int)(pNZBInfo->GetDownloadedSize() / 1024 / 1024); int iMessageCount = pNZBInfo->GetMessageCount() > 0 ? pNZBInfo->GetMessageCount() : pNZBInfo->GetCachedMessageCount(); char* xmlURL = EncodeStr(pNZBInfo->GetURL()); char* xmlNZBFilename = EncodeStr(pNZBInfo->GetFilename()); char* xmlNZBNicename = EncodeStr(pNZBInfo->GetName()); char* xmlDestDir = EncodeStr(pNZBInfo->GetDestDir()); char* xmlFinalDir = EncodeStr(pNZBInfo->GetFinalDir()); char* xmlCategory = EncodeStr(pNZBInfo->GetCategory()); char* xmlDupeKey = EncodeStr(pNZBInfo->GetDupeKey()); snprintf(szItemBuf, iItemBufSize, IsJson() ? JSON_NZB_ITEM_START : XML_NZB_ITEM_START, pNZBInfo->GetID(), xmlNZBNicename, xmlNZBNicename, szKindName[pNZBInfo->GetKind()], xmlURL, xmlNZBFilename, xmlDestDir, xmlFinalDir, xmlCategory, szParStatusName[pNZBInfo->GetParStatus()], szUnpackStatusName[pNZBInfo->GetUnpackStatus()], szMoveStatusName[pNZBInfo->GetMoveStatus()], szScriptStatusName[pNZBInfo->GetScriptStatuses()->CalcTotalStatus()], szDeleteStatusName[pNZBInfo->GetDeleteStatus()], szMarkStatusName[pNZBInfo->GetMarkStatus()], szUrlStatusName[pNZBInfo->GetUrlStatus()], iFileSizeLo, iFileSizeHi, iFileSizeMB, pNZBInfo->GetFileCount(), pNZBInfo->GetMinTime(), pNZBInfo->GetMaxTime(), pNZBInfo->GetTotalArticles(), pNZBInfo->GetCurrentSuccessArticles(), pNZBInfo->GetCurrentFailedArticles(), pNZBInfo->CalcHealth(), pNZBInfo->CalcCriticalHealth(false), xmlDupeKey, pNZBInfo->GetDupeScore(), szDupeModeName[pNZBInfo->GetDupeMode()], BoolToStr(pNZBInfo->GetDeleteStatus() != NZBInfo::dsNone), iDownloadedSizeLo, iDownloadedSizeHi, iDownloadedSizeMB, pNZBInfo->GetDownloadSec(), pNZBInfo->GetPostInfo() && pNZBInfo->GetPostInfo()->GetStartTime() ? time(NULL) - pNZBInfo->GetPostInfo()->GetStartTime() : pNZBInfo->GetPostTotalSec(), pNZBInfo->GetParSec(), pNZBInfo->GetRepairSec(), pNZBInfo->GetUnpackSec(), iMessageCount); free(xmlURL); free(xmlNZBNicename); free(xmlNZBFilename); free(xmlCategory); free(xmlDestDir); free(xmlFinalDir); free(xmlDupeKey); szItemBuf[iItemBufSize-1] = '\0'; AppendResponse(szItemBuf); // 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, iItemBufSize, IsJson() ? JSON_PARAMETER_ITEM : XML_PARAMETER_ITEM, xmlName, xmlValue); szItemBuf[iItemBufSize-1] = '\0'; free(xmlName); free(xmlValue); if (IsJson() && iParamIndex++ > 0) { AppendResponse(",\n"); } AppendResponse(szItemBuf); } AppendResponse(IsJson() ? JSON_NZB_ITEM_SCRIPT_START : XML_NZB_ITEM_SCRIPT_START); // Script statuses int iScriptIndex = 0; for (ScriptStatusList::iterator it = pNZBInfo->GetScriptStatuses()->begin(); it != pNZBInfo->GetScriptStatuses()->end(); it++) { ScriptStatus* pScriptStatus = *it; char* xmlName = EncodeStr(pScriptStatus->GetName()); char* xmlStatus = EncodeStr(szScriptStatusName[pScriptStatus->GetStatus()]); snprintf(szItemBuf, iItemBufSize, IsJson() ? JSON_SCRIPT_ITEM : XML_SCRIPT_ITEM, xmlName, xmlStatus); szItemBuf[iItemBufSize-1] = '\0'; free(xmlName); free(xmlStatus); if (IsJson() && iScriptIndex++ > 0) { AppendResponse(",\n"); } AppendResponse(szItemBuf); } AppendResponse(IsJson() ? JSON_NZB_ITEM_STATS_START : XML_NZB_ITEM_STATS_START); // Server stats int iStatIndex = 0; for (ServerStatList::iterator it = pNZBInfo->GetCurrentServerStats()->begin(); it != pNZBInfo->GetCurrentServerStats()->end(); it++) { ServerStat* pServerStat = *it; snprintf(szItemBuf, iItemBufSize, IsJson() ? JSON_STAT_ITEM : XML_STAT_ITEM, pServerStat->GetServerID(), pServerStat->GetSuccessArticles(), pServerStat->GetFailedArticles()); szItemBuf[iItemBufSize-1] = '\0'; if (IsJson() && iStatIndex++ > 0) { AppendResponse(",\n"); } AppendResponse(szItemBuf); } AppendResponse(IsJson() ? JSON_NZB_ITEM_END : XML_NZB_ITEM_END); free(szItemBuf); } void NzbInfoXmlCommand::AppendPostInfoFields(PostInfo* pPostInfo, int iLogEntries, bool bPostQueue) { const char* XML_GROUPQUEUE_ITEM_START = "PostInfoText%s\n" "PostStageProgress%i\n" "PostStageTimeSec%i\n"; // PostTotalTimeSec is printed by method "AppendNZBInfoFields" const char* XML_POSTQUEUE_ITEM_START = "ProgressLabel%s\n" "StageProgress%i\n" "StageTimeSec%i\n" "TotalTimeSec%i\n"; const char* XML_LOG_START = "Log\n"; const char* XML_POSTQUEUE_ITEM_END = "\n"; const char* JSON_GROUPQUEUE_ITEM_START = "\"PostInfoText\" : \"%s\",\n" "\"PostStageProgress\" : %i,\n" "\"PostStageTimeSec\" : %i,\n"; const char* JSON_POSTQUEUE_ITEM_START = "\"ProgressLabel\" : \"%s\",\n" "\"StageProgress\" : %i,\n" "\"StageTimeSec\" : %i,\n" "\"TotalTimeSec\" : %i,\n"; const char* JSON_LOG_START = "\"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"}; time_t tCurTime = time(NULL); int iItemBufSize = 10240; char* szItemBuf = (char*)malloc(iItemBufSize); int index = 0; const char* szItemStart = bPostQueue ? IsJson() ? JSON_POSTQUEUE_ITEM_START : XML_POSTQUEUE_ITEM_START : IsJson() ? JSON_GROUPQUEUE_ITEM_START : XML_GROUPQUEUE_ITEM_START; if (pPostInfo) { char* xmlProgressLabel = EncodeStr(pPostInfo->GetProgressLabel()); snprintf(szItemBuf, iItemBufSize, szItemStart, xmlProgressLabel, pPostInfo->GetStageProgress(), pPostInfo->GetStageTime() ? tCurTime - pPostInfo->GetStageTime() : 0, pPostInfo->GetStartTime() ? tCurTime - pPostInfo->GetStartTime() : 0); free(xmlProgressLabel); } else { snprintf(szItemBuf, iItemBufSize, szItemStart, "NONE", "", 0, 0, 0, 0); } szItemBuf[iItemBufSize-1] = '\0'; if (IsJson() && index++ > 0) { AppendResponse(",\n"); } AppendResponse(szItemBuf); AppendResponse(IsJson() ? JSON_LOG_START : XML_LOG_START); if (iLogEntries > 0 && pPostInfo) { MessageList* pMessages = pPostInfo->GetNZBInfo()->LockCachedMessages(); if (!pMessages->empty()) { if (iLogEntries > (int)pMessages->size()) { iLogEntries = pMessages->size(); } int iStart = pMessages->size() - iLogEntries; 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, iItemBufSize, IsJson() ? JSON_LOG_ITEM : XML_LOG_ITEM, pMessage->GetID(), szMessageType[pMessage->GetKind()], pMessage->GetTime(), xmltext); szItemBuf[iItemBufSize-1] = '\0'; free(xmltext); if (IsJson() && index++ > 0) { AppendResponse(",\n"); } AppendResponse(szItemBuf); } } pPostInfo->GetNZBInfo()->UnlockCachedMessages(); } AppendResponse(IsJson() ? JSON_POSTQUEUE_ITEM_END : XML_POSTQUEUE_ITEM_END); free(szItemBuf); } // struct[] listgroups(int NumberOfLogEntries) void ListGroupsXmlCommand::Execute() { int iNrEntries = 0; NextParamAsInt(&iNrEntries); AppendResponse(IsJson() ? "[\n" : "\n"); const char* XML_LIST_ITEM_START = "\n" "FirstID%i\n" // deprecated, use "NZBID" instead "LastID%i\n" // deprecated, use "NZBID" instead "RemainingSizeLo%u\n" "RemainingSizeHi%u\n" "RemainingSizeMB%i\n" "PausedSizeLo%u\n" "PausedSizeHi%u\n" "PausedSizeMB%i\n" "RemainingFileCount%i\n" "RemainingParCount%i\n" "MinPriority%i\n" "MaxPriority%i\n" "ActiveDownloads%i\n" "Status%s\n"; const char* XML_LIST_ITEM_END = "\n"; const char* JSON_LIST_ITEM_START = "{\n" "\"FirstID\" : %i,\n" // deprecated, use "NZBID" instead "\"LastID\" : %i,\n" // deprecated, use "NZBID" instead "\"RemainingSizeLo\" : %u,\n" "\"RemainingSizeHi\" : %u,\n" "\"RemainingSizeMB\" : %i,\n" "\"PausedSizeLo\" : %u,\n" "\"PausedSizeHi\" : %u,\n" "\"PausedSizeMB\" : %i,\n" "\"RemainingFileCount\" : %i,\n" "\"RemainingParCount\" : %i,\n" "\"MinPriority\" : %i,\n" "\"MaxPriority\" : %i,\n" "\"ActiveDownloads\" : %i,\n" "\"Status\" : \"%s\",\n"; const char* JSON_LIST_ITEM_END = "}"; int iItemBufSize = 10240; char* szItemBuf = (char*)malloc(iItemBufSize); int index = 0; DownloadQueue* pDownloadQueue = DownloadQueue::Lock(); for (NZBList::iterator it = pDownloadQueue->GetQueue()->begin(); it != pDownloadQueue->GetQueue()->end(); it++) { NZBInfo* pNZBInfo = *it; unsigned long iRemainingSizeLo, iRemainingSizeHi, iRemainingSizeMB; unsigned long iPausedSizeLo, iPausedSizeHi, iPausedSizeMB; Util::SplitInt64(pNZBInfo->GetRemainingSize(), &iRemainingSizeHi, &iRemainingSizeLo); iRemainingSizeMB = (int)(pNZBInfo->GetRemainingSize() / 1024 / 1024); Util::SplitInt64(pNZBInfo->GetPausedSize(), &iPausedSizeHi, &iPausedSizeLo); iPausedSizeMB = (int)(pNZBInfo->GetPausedSize() / 1024 / 1024); const char* szStatus = DetectStatus(pNZBInfo); snprintf(szItemBuf, iItemBufSize, IsJson() ? JSON_LIST_ITEM_START : XML_LIST_ITEM_START, pNZBInfo->GetID(), pNZBInfo->GetID(), iRemainingSizeLo, iRemainingSizeHi, iRemainingSizeMB, iPausedSizeLo, iPausedSizeHi, iPausedSizeMB, (int)pNZBInfo->GetFileList()->size(), pNZBInfo->GetRemainingParCount(), pNZBInfo->GetPriority(), pNZBInfo->GetPriority(), pNZBInfo->GetActiveDownloads(), szStatus); szItemBuf[iItemBufSize-1] = '\0'; if (IsJson() && index++ > 0) { AppendResponse(",\n"); } AppendResponse(szItemBuf); AppendNZBInfoFields(pNZBInfo); if (IsJson()) { AppendResponse(",\n"); } AppendPostInfoFields(pNZBInfo->GetPostInfo(), iNrEntries, false); AppendResponse(IsJson() ? JSON_LIST_ITEM_END : XML_LIST_ITEM_END); } DownloadQueue::Unlock(); free(szItemBuf); AppendResponse(IsJson() ? "\n]" : "\n"); } const char* ListGroupsXmlCommand::DetectStatus(NZBInfo* pNZBInfo) { const char* szPostStageName[] = { "PP_QUEUED", "LOADING_PARS", "VERIFYING_SOURCES", "REPAIRING", "VERIFYING_REPAIRED", "RENAMING", "UNPACKING", "MOVING", "EXECUTING_SCRIPT", "PP_FINISHED" }; const char* szStatus = NULL; if (pNZBInfo->GetPostInfo()) { szStatus = szPostStageName[pNZBInfo->GetPostInfo()->GetStage()]; } else if (pNZBInfo->GetActiveDownloads() > 0) { szStatus = pNZBInfo->GetKind() == NZBInfo::nkUrl ? "FETCHING" : "DOWNLOADING"; } else if ((pNZBInfo->GetPausedSize() > 0) && (pNZBInfo->GetRemainingSize() == pNZBInfo->GetPausedSize())) { szStatus = "PAUSED"; } else { szStatus = "QUEUED"; } return szStatus; } typedef struct { int iActionID; const char* szActionName; } EditCommandEntry; EditCommandEntry EditCommandNameMap[] = { { DownloadQueue::eaFileMoveOffset, "FileMoveOffset" }, { DownloadQueue::eaFileMoveTop, "FileMoveTop" }, { DownloadQueue::eaFileMoveBottom, "FileMoveBottom" }, { DownloadQueue::eaFilePause, "FilePause" }, { DownloadQueue::eaFileResume, "FileResume" }, { DownloadQueue::eaFileDelete, "FileDelete" }, { DownloadQueue::eaFilePauseAllPars, "FilePauseAllPars" }, { DownloadQueue::eaFilePauseExtraPars, "FilePauseExtraPars" }, { DownloadQueue::eaFileReorder, "FileReorder" }, { DownloadQueue::eaFileSplit, "FileSplit" }, { DownloadQueue::eaGroupMoveOffset, "GroupMoveOffset" }, { DownloadQueue::eaGroupMoveTop, "GroupMoveTop" }, { DownloadQueue::eaGroupMoveBottom, "GroupMoveBottom" }, { DownloadQueue::eaGroupPause, "GroupPause" }, { DownloadQueue::eaGroupResume, "GroupResume" }, { DownloadQueue::eaGroupDelete, "GroupDelete" }, { DownloadQueue::eaGroupDupeDelete, "GroupDupeDelete" }, { DownloadQueue::eaGroupFinalDelete, "GroupFinalDelete" }, { DownloadQueue::eaGroupPauseAllPars, "GroupPauseAllPars" }, { DownloadQueue::eaGroupPauseExtraPars, "GroupPauseExtraPars" }, { DownloadQueue::eaGroupSetPriority, "GroupSetPriority" }, { DownloadQueue::eaGroupSetCategory, "GroupSetCategory" }, { DownloadQueue::eaGroupApplyCategory, "GroupApplyCategory" }, { DownloadQueue::eaGroupMerge, "GroupMerge" }, { DownloadQueue::eaGroupSetParameter, "GroupSetParameter" }, { DownloadQueue::eaGroupSetName, "GroupSetName" }, { DownloadQueue::eaGroupSetDupeKey, "GroupSetDupeKey" }, { DownloadQueue::eaGroupSetDupeScore, "GroupSetDupeScore" }, { DownloadQueue::eaGroupSetDupeMode, "GroupSetDupeMode" }, { DownloadQueue::eaGroupSort, "GroupSort" }, { DownloadQueue::eaPostDelete, "PostDelete" }, { DownloadQueue::eaHistoryDelete, "HistoryDelete" }, { DownloadQueue::eaHistoryFinalDelete, "HistoryFinalDelete" }, { DownloadQueue::eaHistoryReturn, "HistoryReturn" }, { DownloadQueue::eaHistoryProcess, "HistoryProcess" }, { DownloadQueue::eaHistoryRedownload, "HistoryRedownload" }, { DownloadQueue::eaHistorySetParameter, "HistorySetParameter" }, { DownloadQueue::eaHistorySetDupeKey, "HistorySetDupeKey" }, { DownloadQueue::eaHistorySetDupeScore, "HistorySetDupeScore" }, { DownloadQueue::eaHistorySetDupeMode, "HistorySetDupeMode" }, { DownloadQueue::eaHistorySetDupeBackup, "HistorySetDupeBackup" }, { DownloadQueue::eaHistoryMarkBad, "HistoryMarkBad" }, { DownloadQueue::eaHistoryMarkGood, "HistoryMarkGood" }, { DownloadQueue::eaHistoryMarkSuccess, "HistoryMarkSuccess" }, { DownloadQueue::eaHistorySetCategory, "HistorySetCategory" }, { DownloadQueue::eaHistorySetName, "HistorySetName" }, { 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); } DownloadQueue* pDownloadQueue = DownloadQueue::Lock(); bool bOK = pDownloadQueue->EditList(&cIDList, NULL, DownloadQueue::mmID, (DownloadQueue::EEditAction)iAction, iOffset, szEditText); DownloadQueue::Unlock(); BuildBoolResponse(bOK); } // v13 (new param order and new result type): // int append(string NZBFilename, string NZBContent, string Category, int Priority, bool AddToTop, bool AddPaused, string DupeKey, int DupeScore, string DupeMode) // v12 (backward compatible, some params are optional): // bool append(string NZBFilename, string Category, int Priority, bool AddToTop, string Content, bool AddPaused, string DupeKey, int DupeScore, string DupeMode) void DownloadXmlCommand::Execute() { if (!CheckSafeMethod()) { return; } bool bV13 = true; char* szNZBFilename; if (!NextParamAsStr(&szNZBFilename)) { BuildErrorResponse(2, "Invalid parameter (NZBFileName)"); return; } char* szNZBContent; if (!NextParamAsStr(&szNZBContent)) { BuildErrorResponse(2, "Invalid parameter (NZBContent)"); return; } char* szCategory; if (!NextParamAsStr(&szCategory)) { bV13 = false; szCategory = szNZBContent; } DecodeStr(szNZBFilename); DecodeStr(szCategory); debug("FileName=%s", szNZBFilename); // 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 (AddTop)"); return; } if (!bV13 && !NextParamAsStr(&szNZBContent)) { BuildErrorResponse(2, "Invalid parameter (FileContent)"); return; } DecodeStr(szNZBContent); bool bAddPaused = false; char* szDupeKey = NULL; int iDupeScore = 0; EDupeMode eDupeMode = dmScore; if (NextParamAsBool(&bAddPaused)) { if (!NextParamAsStr(&szDupeKey)) { BuildErrorResponse(2, "Invalid parameter (DupeKey)"); return; } DecodeStr(szDupeKey); if (!NextParamAsInt(&iDupeScore)) { BuildErrorResponse(2, "Invalid parameter (DupeScore)"); return; } char* szDupeMode = NULL; if (!NextParamAsStr(&szDupeMode) || (strcasecmp(szDupeMode, "score") && strcasecmp(szDupeMode, "all") && strcasecmp(szDupeMode, "force"))) { BuildErrorResponse(2, "Invalid parameter (DupeMode)"); return; } eDupeMode = !strcasecmp(szDupeMode, "all") ? dmAll : !strcasecmp(szDupeMode, "force") ? dmForce : dmScore; } else if (bV13) { BuildErrorResponse(2, "Invalid parameter (AddPaused)"); return; } if (!strncasecmp(szNZBContent, "http://", 6) || !strncasecmp(szNZBContent, "https://", 7)) { // add url NZBInfo* pNZBInfo = new NZBInfo(); pNZBInfo->SetKind(NZBInfo::nkUrl); pNZBInfo->SetURL(szNZBContent); pNZBInfo->SetFilename(szNZBFilename); pNZBInfo->SetCategory(szCategory); pNZBInfo->SetPriority(iPriority); pNZBInfo->SetAddUrlPaused(bAddPaused); pNZBInfo->SetDupeKey(szDupeKey ? szDupeKey : ""); pNZBInfo->SetDupeScore(iDupeScore); pNZBInfo->SetDupeMode(eDupeMode); int iNZBID = pNZBInfo->GetID(); char szNicename[1024]; pNZBInfo->MakeNiceUrlName(szNZBContent, szNZBFilename, szNicename, sizeof(szNicename)); info("Queue %s", szNicename); DownloadQueue* pDownloadQueue = DownloadQueue::Lock(); pDownloadQueue->GetQueue()->Add(pNZBInfo, bAddTop); pDownloadQueue->Save(); DownloadQueue::Unlock(); if (bV13) { BuildIntResponse(iNZBID); } else { BuildBoolResponse(true); } } else { // add file content int iLen = WebUtil::DecodeBase64(szNZBContent, 0, szNZBContent); szNZBContent[iLen] = '\0'; //debug("FileContent=%s", szFileContent); int iNZBID = 0; bool bOK = g_pScanner->AddExternalFile(szNZBFilename, szCategory, iPriority, szDupeKey, iDupeScore, eDupeMode, NULL, bAddTop, bAddPaused, NULL, NULL, szNZBContent, iLen, &iNZBID) != Scanner::asFailed; if (bV13) { BuildIntResponse(bOK ? iNZBID : -1); } else { BuildBoolResponse(bOK); } } } // deprecated void PostQueueXmlCommand::Execute() { int iNrEntries = 0; NextParamAsInt(&iNrEntries); AppendResponse(IsJson() ? "[\n" : "\n"); const char* XML_POSTQUEUE_ITEM_START = "\n" "ID%i\n" "InfoName%s\n" "ParFilename\n" // deprecated, always empty "Stage%s\n" "FileProgress%i\n"; const char* XML_POSTQUEUE_ITEM_END = "\n"; const char* JSON_POSTQUEUE_ITEM_START = "{\n" "\"ID\" : %i,\n" "\"InfoName\" : \"%s\",\n" "\"ParFilename\" : \"\",\n" // deprecated, always empty "\"Stage\" : \"%s\",\n" "\"FileProgress\" : %i,\n"; const char* JSON_POSTQUEUE_ITEM_END = "}"; const char* szPostStageName[] = { "QUEUED", "LOADING_PARS", "VERIFYING_SOURCES", "REPAIRING", "VERIFYING_REPAIRED", "RENAMING", "UNPACKING", "MOVING", "EXECUTING_SCRIPT", "FINISHED" }; NZBList* pNZBList = DownloadQueue::Lock()->GetQueue(); int iItemBufSize = 10240; char* szItemBuf = (char*)malloc(iItemBufSize); int index = 0; for (NZBList::iterator it = pNZBList->begin(); it != pNZBList->end(); it++) { NZBInfo* pNZBInfo = *it; PostInfo* pPostInfo = pNZBInfo->GetPostInfo(); if (!pPostInfo) { continue; } char* xmlInfoName = EncodeStr(pPostInfo->GetNZBInfo()->GetName()); snprintf(szItemBuf, iItemBufSize, IsJson() ? JSON_POSTQUEUE_ITEM_START : XML_POSTQUEUE_ITEM_START, pNZBInfo->GetID(), xmlInfoName, szPostStageName[pPostInfo->GetStage()], pPostInfo->GetFileProgress()); szItemBuf[iItemBufSize-1] = '\0'; free(xmlInfoName); if (IsJson() && index++ > 0) { AppendResponse(",\n"); } AppendResponse(szItemBuf); AppendNZBInfoFields(pPostInfo->GetNZBInfo()); if (IsJson()) { AppendResponse(",\n"); } AppendPostInfoFields(pPostInfo, iNrEntries, true); AppendResponse(IsJson() ? JSON_POSTQUEUE_ITEM_END : XML_POSTQUEUE_ITEM_END); } free(szItemBuf); DownloadQueue::Unlock(); 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_pScanner->ScanNZBDir(bSyncMode); BuildBoolResponse(true); } // struct[] history(bool hidden) // Parameter "hidden" is optional (new in v12) void HistoryXmlCommand::Execute() { AppendResponse(IsJson() ? "[\n" : "\n"); const char* XML_HISTORY_ITEM_START = "\n" "ID%i\n" // Deprecated, use "NZBID" instead "Name%s\n" "RemainingFileCount%i\n" "HistoryTime%i\n" "Status%s\n" "Log\n"; // Deprected, always empty const char* JSON_HISTORY_ITEM_START = "{\n" "\"ID\" : %i,\n" // Deprecated, use "NZBID" instead "\"Name\" : \"%s\",\n" "\"RemainingFileCount\" : %i,\n" "\"HistoryTime\" : %i,\n" "\"Status\" : \"%s\",\n" "\"Log\" : [],\n"; // Deprected, always empty const char* XML_HISTORY_ITEM_END = ""; const char* JSON_HISTORY_ITEM_END = "}"; const char* XML_HISTORY_DUP_ITEM = "\n" "ID%i\n" // Deprecated, use "NZBID" instead "NZBID%i\n" "Kind%s\n" "Name%s\n" "HistoryTime%i\n" "FileSizeLo%u\n" "FileSizeHi%u\n" "FileSizeMB%i\n" "DupeKey%s\n" "DupeScore%i\n" "DupeMode%s\n" "DupStatus%s\n" "Status%s\n"; const char* JSON_HISTORY_DUP_ITEM = "{\n" "\"ID\" : %i,\n" // Deprecated, use "NZBID" instead "\"NZBID\" : %i,\n" "\"Kind\" : \"%s\",\n" "\"Name\" : \"%s\",\n" "\"HistoryTime\" : %i,\n" "\"FileSizeLo\" : %i,\n" "\"FileSizeHi\" : %i,\n" "\"FileSizeMB\" : %i,\n" "\"DupeKey\" : \"%s\",\n" "\"DupeScore\" : %i,\n" "\"DupeMode\" : \"%s\",\n" "\"DupStatus\" : \"%s\",\n" "\"Status\" : \"%s\"\n"; const char* szDupStatusName[] = { "UNKNOWN", "SUCCESS", "FAILURE", "DELETED", "DUPE", "BAD", "GOOD" }; const char* szDupeModeName[] = { "SCORE", "ALL", "FORCE" }; bool bDup = false; NextParamAsBool(&bDup); DownloadQueue* pDownloadQueue = DownloadQueue::Lock(); int iItemBufSize = 10240; char* szItemBuf = (char*)malloc(iItemBufSize); int index = 0; for (HistoryList::iterator it = pDownloadQueue->GetHistory()->begin(); it != pDownloadQueue->GetHistory()->end(); it++) { HistoryInfo* pHistoryInfo = *it; if (pHistoryInfo->GetKind() == HistoryInfo::hkDup && !bDup) { continue; } NZBInfo* pNZBInfo = NULL; char szNicename[1024]; pHistoryInfo->GetName(szNicename, sizeof(szNicename)); char *xmlNicename = EncodeStr(szNicename); const char* szStatus = DetectStatus(pHistoryInfo); if (pHistoryInfo->GetKind() == HistoryInfo::hkNzb || pHistoryInfo->GetKind() == HistoryInfo::hkUrl) { pNZBInfo = pHistoryInfo->GetNZBInfo(); snprintf(szItemBuf, iItemBufSize, IsJson() ? JSON_HISTORY_ITEM_START : XML_HISTORY_ITEM_START, pHistoryInfo->GetID(), xmlNicename, pNZBInfo->GetParkedFileCount(), pHistoryInfo->GetTime(), szStatus); } else if (pHistoryInfo->GetKind() == HistoryInfo::hkDup) { DupInfo* pDupInfo = pHistoryInfo->GetDupInfo(); unsigned long iFileSizeHi, iFileSizeLo, iFileSizeMB; Util::SplitInt64(pDupInfo->GetSize(), &iFileSizeHi, &iFileSizeLo); iFileSizeMB = (int)(pDupInfo->GetSize() / 1024 / 1024); char* xmlDupeKey = EncodeStr(pDupInfo->GetDupeKey()); snprintf(szItemBuf, iItemBufSize, IsJson() ? JSON_HISTORY_DUP_ITEM : XML_HISTORY_DUP_ITEM, pHistoryInfo->GetID(), pHistoryInfo->GetID(), "DUP", xmlNicename, pHistoryInfo->GetTime(), iFileSizeLo, iFileSizeHi, iFileSizeMB, xmlDupeKey, pDupInfo->GetDupeScore(), szDupeModeName[pDupInfo->GetDupeMode()], szDupStatusName[pDupInfo->GetStatus()], szStatus); free(xmlDupeKey); } szItemBuf[iItemBufSize-1] = '\0'; free(xmlNicename); if (IsJson() && index++ > 0) { AppendResponse(",\n"); } AppendResponse(szItemBuf); if (pNZBInfo) { AppendNZBInfoFields(pNZBInfo); } AppendResponse(IsJson() ? JSON_HISTORY_ITEM_END : XML_HISTORY_ITEM_END); } free(szItemBuf); AppendResponse(IsJson() ? "\n]" : "\n"); DownloadQueue::Unlock(); } const char* HistoryXmlCommand::DetectStatus(HistoryInfo* pHistoryInfo) { const char* szStatus = "FAILURE/INTERNAL_ERROR"; if (pHistoryInfo->GetKind() == HistoryInfo::hkNzb || pHistoryInfo->GetKind() == HistoryInfo::hkUrl) { NZBInfo* pNZBInfo = pHistoryInfo->GetNZBInfo(); szStatus = pNZBInfo->MakeTextStatus(false); } else if (pHistoryInfo->GetKind() == HistoryInfo::hkDup) { DupInfo* pDupInfo = pHistoryInfo->GetDupInfo(); const char* szDupStatusName[] = { "FAILURE/INTERNAL_ERROR", "SUCCESS/HIDDEN", "FAILURE/HIDDEN", "DELETED/MANUAL", "DELETED/DUPE", "FAILURE/BAD", "SUCCESS/GOOD" }; szStatus = szDupStatusName[pDupInfo->GetStatus()]; } return szStatus; } // Deprecated in v13 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" "}"; DownloadQueue* pDownloadQueue = DownloadQueue::Lock(); int iItemBufSize = 10240; char* szItemBuf = (char*)malloc(iItemBufSize); int index = 0; for (NZBList::iterator it = pDownloadQueue->GetQueue()->begin(); it != pDownloadQueue->GetQueue()->end(); it++) { NZBInfo* pNZBInfo = *it; if (pNZBInfo->GetKind() == NZBInfo::nkUrl) { char* xmlNicename = EncodeStr(pNZBInfo->GetName()); char* xmlNZBFilename = EncodeStr(pNZBInfo->GetFilename()); char* xmlURL = EncodeStr(pNZBInfo->GetURL()); char* xmlCategory = EncodeStr(pNZBInfo->GetCategory()); snprintf(szItemBuf, iItemBufSize, IsJson() ? JSON_URLQUEUE_ITEM : XML_URLQUEUE_ITEM, pNZBInfo->GetID(), xmlNZBFilename, xmlURL, xmlNicename, xmlCategory, pNZBInfo->GetPriority()); szItemBuf[iItemBufSize-1] = '\0'; free(xmlNicename); free(xmlNZBFilename); free(xmlURL); free(xmlCategory); if (IsJson() && index++ > 0) { AppendResponse(",\n"); } AppendResponse(szItemBuf); } } free(szItemBuf); DownloadQueue::Unlock(); 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"); int iItemBufSize = 1024; char* szItemBuf = (char*)malloc(iItemBufSize); int index = 0; Options::OptEntries* pOptEntries = g_pOptions->LockOptEntries(); for (Options::OptEntries::iterator it = pOptEntries->begin(); it != pOptEntries->end(); it++) { Options::OptEntry* pOptEntry = *it; char* xmlName = EncodeStr(pOptEntry->GetName()); char* xmlValue = EncodeStr(m_eUserAccess == XmlRpcProcessor::uaRestricted && pOptEntry->Restricted() ? "***" : pOptEntry->GetValue()); // option values can sometimes have unlimited length int iValLen = strlen(xmlValue); if (iValLen > iItemBufSize - 500) { iItemBufSize = iValLen + 500; szItemBuf = (char*)realloc(szItemBuf, iItemBufSize); } snprintf(szItemBuf, iItemBufSize, IsJson() ? JSON_CONFIG_ITEM : XML_CONFIG_ITEM, xmlName, xmlValue); szItemBuf[iItemBufSize-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() 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" "}"; Options::OptEntries* pOptEntries = new Options::OptEntries(); if (!g_pOptions->LoadConfig(pOptEntries)) { BuildErrorResponse(3, "Could not read configuration file"); delete pOptEntries; return; } AppendResponse(IsJson() ? "[\n" : "\n"); int iItemBufSize = 1024; char* szItemBuf = (char*)malloc(iItemBufSize); 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(m_eUserAccess == XmlRpcProcessor::uaRestricted && pOptEntry->Restricted() ? "***" : pOptEntry->GetValue()); // option values can sometimes have unlimited length int iValLen = strlen(xmlValue); if (iValLen > iItemBufSize - 500) { iItemBufSize = iValLen + 500; szItemBuf = (char*)realloc(szItemBuf, iItemBufSize); } snprintf(szItemBuf, iItemBufSize, IsJson() ? JSON_CONFIG_ITEM : XML_CONFIG_ITEM, xmlName, xmlValue); szItemBuf[iItemBufSize-1] = '\0'; free(xmlName); free(xmlValue); if (IsJson() && index++ > 0) { AppendResponse(",\n"); } AppendResponse(szItemBuf); } delete pOptEntries; free(szItemBuf); AppendResponse(IsJson() ? "\n]" : "\n"); } // bool saveconfig(struct[] data) void SaveConfigXmlCommand::Execute() { Options::OptEntries* pOptEntries = new Options::OptEntries(); char* szName; char* szValue; char* szDummy; while ((IsJson() && NextParamAsStr(&szDummy) && NextParamAsStr(&szName) && NextParamAsStr(&szDummy) && NextParamAsStr(&szValue)) || (!IsJson() && NextParamAsStr(&szName) && NextParamAsStr(&szValue))) { DecodeStr(szName); DecodeStr(szValue); pOptEntries->push_back(new Options::OptEntry(szName, szValue)); } // save to config file bool bOK = g_pOptions->SaveConfig(pOptEntries); delete pOptEntries; BuildBoolResponse(bOK); } // struct[] configtemplates(bool loadFromDisk) // parameter "loadFromDisk" is optional (new in v14) void ConfigTemplatesXmlCommand::Execute() { const char* XML_CONFIG_ITEM = "\n" "Name%s\n" "DisplayName%s\n" "PostScript%s\n" "ScanScript%s\n" "QueueScript%s\n" "SchedulerScript%s\n" "Template%s\n" "\n"; const char* JSON_CONFIG_ITEM = "{\n" "\"Name\" : \"%s\",\n" "\"DisplayName\" : \"%s\",\n" "\"PostScript\" : %s,\n" "\"ScanScript\" : %s,\n" "\"QueueScript\" : %s,\n" "\"SchedulerScript\" : %s,\n" "\"Template\" : \"%s\"\n" "}"; bool bLoadFromDisk = false; NextParamAsBool(&bLoadFromDisk); Options::ConfigTemplates* pConfigTemplates = g_pOptions->GetConfigTemplates(); if (bLoadFromDisk) { pConfigTemplates = new Options::ConfigTemplates(); if (!g_pOptions->LoadConfigTemplates(pConfigTemplates)) { BuildErrorResponse(3, "Could not read configuration templates"); delete pConfigTemplates; return; } } AppendResponse(IsJson() ? "[\n" : "\n"); int index = 0; for (Options::ConfigTemplates::iterator it = pConfigTemplates->begin(); it != pConfigTemplates->end(); it++) { Options::ConfigTemplate* pConfigTemplate = *it; char* xmlName = EncodeStr(pConfigTemplate->GetScript() ? pConfigTemplate->GetScript()->GetName() : ""); char* xmlDisplayName = EncodeStr(pConfigTemplate->GetScript() ? pConfigTemplate->GetScript()->GetDisplayName() : ""); char* xmlTemplate = EncodeStr(pConfigTemplate->GetTemplate()); int iItemBufSize = strlen(xmlName) + strlen(xmlTemplate) + 1024; char* szItemBuf = (char*)malloc(iItemBufSize); snprintf(szItemBuf, iItemBufSize, IsJson() ? JSON_CONFIG_ITEM : XML_CONFIG_ITEM, xmlName, xmlDisplayName, BoolToStr(pConfigTemplate->GetScript() && pConfigTemplate->GetScript()->GetPostScript()), BoolToStr(pConfigTemplate->GetScript() && pConfigTemplate->GetScript()->GetScanScript()), BoolToStr(pConfigTemplate->GetScript() && pConfigTemplate->GetScript()->GetQueueScript()), BoolToStr(pConfigTemplate->GetScript() && pConfigTemplate->GetScript()->GetSchedulerScript()), xmlTemplate); szItemBuf[iItemBufSize-1] = '\0'; free(xmlName); free(xmlDisplayName); free(xmlTemplate); if (IsJson() && index++ > 0) { AppendResponse(",\n"); } AppendResponse(szItemBuf); free(szItemBuf); } if (bLoadFromDisk) { delete pConfigTemplates; } AppendResponse(IsJson() ? "\n]" : "\n"); } ViewFeedXmlCommand::ViewFeedXmlCommand(bool bPreview) { m_bPreview = bPreview; } // struct[] viewfeed(int id) // struct[] previewfeed(string name, string url, string filter, bool includeNonMatching) // struct[] previewfeed(string name, string url, string filter, bool pauseNzb, string category, int priority, // bool includeNonMatching, int cacheTimeSec, string cacheId) void ViewFeedXmlCommand::Execute() { bool bOK = false; bool bIncludeNonMatching = false; FeedItemInfos* pFeedItemInfos = NULL; if (m_bPreview) { char* szName; char* szUrl; char* szFilter; bool bPauseNzb; char* szCategory; int iPriority; char* szCacheId; int iCacheTimeSec; if (!NextParamAsStr(&szName) || !NextParamAsStr(&szUrl) || !NextParamAsStr(&szFilter) || !NextParamAsBool(&bPauseNzb) || !NextParamAsStr(&szCategory) || !NextParamAsInt(&iPriority) || !NextParamAsBool(&bIncludeNonMatching) || !NextParamAsInt(&iCacheTimeSec) || !NextParamAsStr(&szCacheId)) { BuildErrorResponse(2, "Invalid parameter"); return; } DecodeStr(szName); DecodeStr(szUrl); DecodeStr(szFilter); DecodeStr(szCacheId); DecodeStr(szCategory); debug("Url=%s", szUrl); debug("Filter=%s", szFilter); bOK = g_pFeedCoordinator->PreviewFeed(szName, szUrl, szFilter, bPauseNzb, szCategory, iPriority, iCacheTimeSec, szCacheId, &pFeedItemInfos); } else { int iID = 0; if (!NextParamAsInt(&iID) || !NextParamAsBool(&bIncludeNonMatching)) { BuildErrorResponse(2, "Invalid parameter"); return; } debug("ID=%i", iID); bOK = g_pFeedCoordinator->ViewFeed(iID, &pFeedItemInfos); } if (!bOK) { BuildErrorResponse(3, "Could not read feed"); return; } const char* XML_FEED_ITEM = "\n" "Title%s\n" "Filename%s\n" "URL%s\n" "SizeLo%i\n" "SizeHi%i\n" "SizeMB%i\n" "Category%s\n" "AddCategory%s\n" "PauseNzb%s\n" "Priority%i\n" "Time%i\n" "Match%s\n" "Rule%i\n" "DupeKey%s\n" "DupeScore%i\n" "DupeMode%s\n" "Status%s\n" "\n"; const char* JSON_FEED_ITEM = "{\n" "\"Title\" : \"%s\",\n" "\"Filename\" : \"%s\",\n" "\"URL\" : \"%s\",\n" "\"SizeLo\" : %i,\n" "\"SizeHi\" : %i,\n" "\"SizeMB\" : %i,\n" "\"Category\" : \"%s\",\n" "\"AddCategory\" : \"%s\",\n" "\"PauseNzb\" : %s,\n" "\"Priority\" : %i,\n" "\"Time\" : %i,\n" "\"Match\" : \"%s\",\n" "\"Rule\" : %i,\n" "\"DupeKey\" : \"%s\",\n" "\"DupeScore\" : %i,\n" "\"DupeMode\" : \"%s\",\n" "\"Status\" : \"%s\"\n" "}"; const char* szStatusType[] = { "UNKNOWN", "BACKLOG", "FETCHED", "NEW" }; const char* szMatchStatusType[] = { "IGNORED", "ACCEPTED", "REJECTED" }; const char* szDupeModeType[] = { "SCORE", "ALL", "FORCE" }; int iItemBufSize = 10240; char* szItemBuf = (char*)malloc(iItemBufSize); AppendResponse(IsJson() ? "[\n" : "\n"); int index = 0; for (FeedItemInfos::iterator it = pFeedItemInfos->begin(); it != pFeedItemInfos->end(); it++) { FeedItemInfo* pFeedItemInfo = *it; if (bIncludeNonMatching || pFeedItemInfo->GetMatchStatus() == FeedItemInfo::msAccepted) { unsigned long iSizeHi, iSizeLo; Util::SplitInt64(pFeedItemInfo->GetSize(), &iSizeHi, &iSizeLo); int iSizeMB = (int)(pFeedItemInfo->GetSize() / 1024 / 1024); char* xmltitle = EncodeStr(pFeedItemInfo->GetTitle()); char* xmlfilename = EncodeStr(pFeedItemInfo->GetFilename()); char* xmlurl = EncodeStr(pFeedItemInfo->GetUrl()); char* xmlcategory = EncodeStr(pFeedItemInfo->GetCategory()); char* xmladdcategory = EncodeStr(pFeedItemInfo->GetAddCategory()); char* xmldupekey = EncodeStr(pFeedItemInfo->GetDupeKey()); snprintf(szItemBuf, iItemBufSize, IsJson() ? JSON_FEED_ITEM : XML_FEED_ITEM, xmltitle, xmlfilename, xmlurl, iSizeLo, iSizeHi, iSizeMB, xmlcategory, xmladdcategory, BoolToStr(pFeedItemInfo->GetPauseNzb()), pFeedItemInfo->GetPriority(), pFeedItemInfo->GetTime(), szMatchStatusType[pFeedItemInfo->GetMatchStatus()], pFeedItemInfo->GetMatchRule(), xmldupekey, pFeedItemInfo->GetDupeScore(), szDupeModeType[pFeedItemInfo->GetDupeMode()], szStatusType[pFeedItemInfo->GetStatus()]); szItemBuf[iItemBufSize-1] = '\0'; free(xmltitle); free(xmlfilename); free(xmlurl); free(xmlcategory); free(xmladdcategory); free(xmldupekey); if (IsJson() && index++ > 0) { AppendResponse(",\n"); } AppendResponse(szItemBuf); } } free(szItemBuf); pFeedItemInfos->Release(); AppendResponse(IsJson() ? "\n]" : "\n"); } // bool fetchfeed(int ID) void FetchFeedXmlCommand::Execute() { if (!CheckSafeMethod()) { return; } int iID; if (!NextParamAsInt(&iID)) { BuildErrorResponse(2, "Invalid parameter (ID)"); return; } g_pFeedCoordinator->FetchFeed(iID); BuildBoolResponse(true); } // bool editserver(int ID, bool Active) void EditServerXmlCommand::Execute() { if (!CheckSafeMethod()) { return; } bool bOK = false; int bFirst = true; int iID; while (NextParamAsInt(&iID)) { bFirst = false; bool bActive; if (!NextParamAsBool(&bActive)) { BuildErrorResponse(2, "Invalid parameter"); return; } for (Servers::iterator it = g_pServerPool->GetServers()->begin(); it != g_pServerPool->GetServers()->end(); it++) { NewsServer* pServer = *it; if (pServer->GetID() == iID) { pServer->SetActive(bActive); bOK = true; } } } if (bFirst) { BuildErrorResponse(2, "Invalid parameter"); return; } if (bOK) { g_pServerPool->Changed(); } BuildBoolResponse(bOK); } // string readurl(string url, string infoname) void ReadUrlXmlCommand::Execute() { char* szURL; if (!NextParamAsStr(&szURL)) { BuildErrorResponse(2, "Invalid parameter (URL)"); return; } DecodeStr(szURL); char* szInfoName; if (!NextParamAsStr(&szInfoName)) { BuildErrorResponse(2, "Invalid parameter (InfoName)"); return; } DecodeStr(szInfoName); // generate temp file name char szTempFileName[1024]; int iNum = 1; while (iNum == 1 || Util::FileExists(szTempFileName)) { snprintf(szTempFileName, 1024, "%sreadurl-%i.tmp", g_pOptions->GetTempDir(), iNum); szTempFileName[1024-1] = '\0'; iNum++; } WebDownloader* pDownloader = new WebDownloader(); pDownloader->SetURL(szURL); pDownloader->SetForce(true); pDownloader->SetRetry(false); pDownloader->SetOutputFilename(szTempFileName); pDownloader->SetInfoName(szInfoName); // do sync download WebDownloader::EStatus eStatus = pDownloader->Download(); bool bOK = eStatus == WebDownloader::adFinished; delete pDownloader; if (bOK) { char* szFileContent = NULL; int iFileContentLen = 0; Util::LoadFileIntoBuffer(szTempFileName, &szFileContent, &iFileContentLen); char* xmlContent = EncodeStr(szFileContent); free(szFileContent); AppendResponse(IsJson() ? "\"" : ""); AppendResponse(xmlContent); AppendResponse(IsJson() ? "\"" : ""); free(xmlContent); } else { BuildErrorResponse(3, "Could not read url"); } remove(szTempFileName); } // string checkupdates() void CheckUpdatesXmlCommand::Execute() { char* szUpdateInfo = NULL; bool bOK = g_pMaintenance->CheckUpdates(&szUpdateInfo); if (bOK) { char* xmlContent = EncodeStr(szUpdateInfo); free(szUpdateInfo); AppendResponse(IsJson() ? "\"" : ""); AppendResponse(xmlContent); AppendResponse(IsJson() ? "\"" : ""); free(xmlContent); } else { BuildErrorResponse(3, "Could not read update info from update-info-script"); } } // bool startupdate(string branch) void StartUpdateXmlCommand::Execute() { if (!CheckSafeMethod()) { return; } char* szBranch; if (!NextParamAsStr(&szBranch)) { BuildErrorResponse(2, "Invalid parameter (Branch)"); return; } DecodeStr(szBranch); Maintenance::EBranch eBranch; if (!strcasecmp(szBranch, "stable")) { eBranch = Maintenance::brStable; } else if (!strcasecmp(szBranch, "testing")) { eBranch = Maintenance::brTesting; } else if (!strcasecmp(szBranch, "devel")) { eBranch = Maintenance::brDevel; } else { BuildErrorResponse(2, "Invalid parameter (Branch)"); return; } bool bOK = g_pMaintenance->StartUpdate(eBranch); BuildBoolResponse(bOK); } // struct[] logupdate(idfrom, entries) MessageList* LogUpdateXmlCommand::LockMessages() { return g_pMaintenance->LockMessages(); } void LogUpdateXmlCommand::UnlockMessages() { g_pMaintenance->UnlockMessages(); } // struct[] servervolumes() void ServerVolumesXmlCommand::Execute() { const char* XML_VOLUME_ITEM_START = "\n" "ServerID%i\n" "DataTime%i\n" "FirstDay%i\n" "TotalSizeLo%u\n" "TotalSizeHi%u\n" "TotalSizeMB%i\n" "CustomSizeLo%u\n" "CustomSizeHi%u\n" "CustomSizeMB%i\n" "CustomTime%i\n" "SecSlot%i\n" "MinSlot%i\n" "HourSlot%i\n" "DaySlot%i\n"; const char* XML_BYTES_ARRAY_START = "%s\n"; const char* XML_BYTES_ARRAY_ITEM = "\n" "SizeLo%u\n" "SizeHi%u\n" "SizeMB%i\n" "\n"; const char* XML_BYTES_ARRAY_END = "\n"; const char* XML_VOLUME_ITEM_END = "\n"; const char* JSON_VOLUME_ITEM_START = "{\n" "\"ServerID\" : %i,\n" "\"DataTime\" : %i,\n" "\"FirstDay\" : %i,\n" "\"TotalSizeLo\" : %i,\n" "\"TotalSizeHi\" : %i,\n" "\"TotalSizeMB\" : %i,\n" "\"CustomSizeLo\" : %i,\n" "\"CustomSizeHi\" : %i,\n" "\"CustomSizeMB\" : %i,\n" "\"CustomTime\" : %i,\n" "\"SecSlot\" : %i,\n" "\"MinSlot\" : %i,\n" "\"HourSlot\" : %i,\n" "\"DaySlot\" : %i,\n"; const char* JSON_BYTES_ARRAY_START = "\"%s\" : [\n"; const char* JSON_BYTES_ARRAY_ITEM = "{\n" "\"SizeLo\" : %u,\n" "\"SizeHi\" : %u,\n" "\"SizeMB\" : %i\n" "}"; const char* JSON_BYTES_ARRAY_END = "]\n"; const char* JSON_VOLUME_ITEM_END = "}\n"; AppendResponse(IsJson() ? "[\n" : "\n"); ServerVolumes* pServerVolumes = g_pStatMeter->LockServerVolumes(); const int iItemBufSize = 1024; char szItemBuf[iItemBufSize]; int index = 0; for (ServerVolumes::iterator it = pServerVolumes->begin(); it != pServerVolumes->end(); it++, index++) { ServerVolume* pServerVolume = *it; if (IsJson() && index > 0) { AppendResponse(",\n"); } unsigned long iTotalSizeHi, iTotalSizeLo, iTotalSizeMB; Util::SplitInt64(pServerVolume->GetTotalBytes(), &iTotalSizeHi, &iTotalSizeLo); iTotalSizeMB = (int)(pServerVolume->GetTotalBytes() / 1024 / 1024); unsigned long iCustomSizeHi, iCustomSizeLo, iCustomSizeMB; Util::SplitInt64(pServerVolume->GetCustomBytes(), &iCustomSizeHi, &iCustomSizeLo); iCustomSizeMB = (int)(pServerVolume->GetCustomBytes() / 1024 / 1024); snprintf(szItemBuf, iItemBufSize, IsJson() ? JSON_VOLUME_ITEM_START : XML_VOLUME_ITEM_START, index, (int)pServerVolume->GetDataTime(), pServerVolume->GetFirstDay(), iTotalSizeLo, iTotalSizeHi, iTotalSizeMB, iCustomSizeLo, iCustomSizeHi, iCustomSizeMB, (int)pServerVolume->GetCustomTime(), pServerVolume->GetSecSlot(), pServerVolume->GetMinSlot(), pServerVolume->GetHourSlot(), pServerVolume->GetDaySlot()); szItemBuf[iItemBufSize-1] = '\0'; AppendResponse(szItemBuf); ServerVolume::VolumeArray* VolumeArrays[] = { pServerVolume->BytesPerSeconds(), pServerVolume->BytesPerMinutes(), pServerVolume->BytesPerHours(), pServerVolume->BytesPerDays() }; const char* VolumeNames[] = { "BytesPerSeconds", "BytesPerMinutes", "BytesPerHours", "BytesPerDays" }; for (int i=0; i<4; i++) { ServerVolume::VolumeArray* pVolumeArray = VolumeArrays[i]; const char* szArrayName = VolumeNames[i]; snprintf(szItemBuf, iItemBufSize, IsJson() ? JSON_BYTES_ARRAY_START : XML_BYTES_ARRAY_START, szArrayName); szItemBuf[iItemBufSize-1] = '\0'; AppendResponse(szItemBuf); int index2 = 0; for (ServerVolume::VolumeArray::iterator it2 = pVolumeArray->begin(); it2 != pVolumeArray->end(); it2++) { long long lBytes = *it2; unsigned long iSizeHi, iSizeLo, iSizeMB; Util::SplitInt64(lBytes, &iSizeHi, &iSizeLo); iSizeMB = (int)(lBytes / 1024 / 1024); snprintf(szItemBuf, iItemBufSize, IsJson() ? JSON_BYTES_ARRAY_ITEM : XML_BYTES_ARRAY_ITEM, iSizeLo, iSizeHi, iSizeMB); szItemBuf[iItemBufSize-1] = '\0'; if (IsJson() && index2++ > 0) { AppendResponse(",\n"); } AppendResponse(szItemBuf); } AppendResponse(IsJson() ? JSON_BYTES_ARRAY_END : XML_BYTES_ARRAY_END); if (IsJson() && i < 3) { AppendResponse(",\n"); } } AppendResponse(IsJson() ? JSON_VOLUME_ITEM_END : XML_VOLUME_ITEM_END); } g_pStatMeter->UnlockServerVolumes(); AppendResponse(IsJson() ? "\n]" : "\n"); } // bool resetservervolume(int serverid, string counter); void ResetServerVolumeXmlCommand::Execute() { if (!CheckSafeMethod()) { return; } int iServerId; char* szCounter; if (!NextParamAsInt(&iServerId) || !NextParamAsStr(&szCounter)) { BuildErrorResponse(2, "Invalid parameter"); return; } if (strcmp(szCounter, "CUSTOM")) { BuildErrorResponse(3, "Invalid Counter"); return; } bool bOK = false; ServerVolumes* pServerVolumes = g_pStatMeter->LockServerVolumes(); int index = 0; for (ServerVolumes::iterator it = pServerVolumes->begin(); it != pServerVolumes->end(); it++, index++) { ServerVolume* pServerVolume = *it; if (index == iServerId || iServerId == -1) { pServerVolume->ResetCustom(); bOK = true; } } g_pStatMeter->UnlockServerVolumes(); BuildBoolResponse(bOK); } // struct[] loadlog(nzbid, logidfrom, logentries) void LoadLogXmlCommand::Execute() { m_pNZBInfo = NULL; m_iNZBID = 0; if (!NextParamAsInt(&m_iNZBID)) { BuildErrorResponse(2, "Invalid parameter"); return; } LogXmlCommand::Execute(); } MessageList* LoadLogXmlCommand::LockMessages() { // TODO: optimize for m_iIDFrom and m_iNrEntries g_pDiskState->LoadNZBMessages(m_iNZBID, &m_messages); if (m_messages.empty()) { DownloadQueue* pDownloadQueue = DownloadQueue::Lock(); m_pNZBInfo = pDownloadQueue->GetQueue()->Find(m_iNZBID); if (m_pNZBInfo) { return m_pNZBInfo->LockCachedMessages(); } else { DownloadQueue::Unlock(); } } return &m_messages; } void LoadLogXmlCommand::UnlockMessages() { if (m_pNZBInfo) { m_pNZBInfo->UnlockCachedMessages(); DownloadQueue::Unlock(); } } // string testserver(string host, int port, string username, string password, bool encryption, string cipher, int timeout); void TestServerXmlCommand::Execute() { const char* XML_RESPONSE_STR_BODY = "%s"; const char* JSON_RESPONSE_STR_BODY = "\"%s\""; if (!CheckSafeMethod()) { return; } char* szHost; int iPort; char* szUsername; char* szPassword; bool bEncryption; char* szCipher; int iTimeout; if (!NextParamAsStr(&szHost) || !NextParamAsInt(&iPort) || !NextParamAsStr(&szUsername) || !NextParamAsStr(&szPassword) || !NextParamAsBool(&bEncryption) || !NextParamAsStr(&szCipher) || !NextParamAsInt(&iTimeout)) { BuildErrorResponse(2, "Invalid parameter"); return; } NewsServer server(0, true, "test server", szHost, iPort, szUsername, szPassword, false, bEncryption, szCipher, 1, 0, 0, 0); TestConnection* pConnection = new TestConnection(&server, this); pConnection->SetTimeout(iTimeout == 0 ? g_pOptions->GetArticleTimeout() : iTimeout); pConnection->SetSuppressErrors(false); m_szErrText = NULL; bool bOK = pConnection->Connect(); char szContent[1024]; snprintf(szContent, 1024, IsJson() ? JSON_RESPONSE_STR_BODY : XML_RESPONSE_STR_BODY, bOK ? "" : Util::EmptyStr(m_szErrText) ? "Unknown error" : m_szErrText); szContent[1024-1] = '\0'; AppendResponse(szContent); delete pConnection; } void TestServerXmlCommand::PrintError(const char* szErrMsg) { if (!m_szErrText) { m_szErrText = EncodeStr(szErrMsg); } }