diff --git a/Frontend.cpp b/Frontend.cpp index 12247737..3ec0bc3a 100644 --- a/Frontend.cpp +++ b/Frontend.cpp @@ -221,17 +221,17 @@ bool Frontend::ServerEditQueue(EEditAction eAction, int iEntry) switch (eAction) { case eaPauseUnpause: - return RequestEditQueue(bPause ? NZBMessageRequest::eActionPause : NZBMessageRequest::eActionResume, 0, ID, ID); + return RequestEditQueue(bPause ? NZBMessageRequest::eActionPause : NZBMessageRequest::eActionResume, 0, ID); case eaDelete: - return RequestEditQueue(NZBMessageRequest::eActionDelete, 0, ID, ID); + return RequestEditQueue(NZBMessageRequest::eActionDelete, 0, ID); case eaMoveUp: - return RequestEditQueue(NZBMessageRequest::eActionMoveOffset, -1, ID, ID); + return RequestEditQueue(NZBMessageRequest::eActionMoveOffset, -1, ID); case eaMoveDown: - return RequestEditQueue(NZBMessageRequest::eActionMoveOffset, +1, ID, ID); + return RequestEditQueue(NZBMessageRequest::eActionMoveOffset, +1, ID); case eaMoveTop: - return RequestEditQueue(NZBMessageRequest::eActionMoveTop, 0, ID, ID); + return RequestEditQueue(NZBMessageRequest::eActionMoveTop, 0, ID); case eaMoveBottom: - return RequestEditQueue(NZBMessageRequest::eActionMoveBottom, 0, ID, ID); + return RequestEditQueue(NZBMessageRequest::eActionMoveBottom, 0, ID); } } else @@ -441,9 +441,9 @@ bool Frontend::RequestDumpDebug() return client.RequestServerDumpDebug(); } -bool Frontend::RequestEditQueue(int iAction, int iOffset, int iIDFrom, int iIDTo) +bool Frontend::RequestEditQueue(int iAction, int iOffset, int iID) { RemoteClient client; client.SetVerbose(false); - return client.RequestServerEditQueue(iAction, iOffset, iIDFrom, iIDTo); + return client.RequestServerEditQueue(iAction, iOffset, &iID, 1); } diff --git a/Frontend.h b/Frontend.h index 40b9dae8..e89c7d3f 100644 --- a/Frontend.h +++ b/Frontend.h @@ -80,7 +80,7 @@ protected: void ServerDumpDebug(); bool RequestDumpDebug(); bool ServerEditQueue(EEditAction eAction, int iEntry); - bool RequestEditQueue(int iAction, int iOffset, int iIDFrom, int iIDTo); + bool RequestEditQueue(int iAction, int iOffset, int iID); public: Frontend(); diff --git a/MessageBase.h b/MessageBase.h index 8af27ded..85e76f1a 100644 --- a/MessageBase.h +++ b/MessageBase.h @@ -181,8 +181,11 @@ struct SNZBEditQueueRequest SNZBMessageBase m_MessageBase; // Must be the first in the struct uint32_t m_iAction; // Action to be executed, see enum in NZBMessageRequest-namespace int32_t m_iOffset; // Offset to move (for m_iAction = 0) - uint32_t m_iIDFrom; // ID of the first file in the range - uint32_t m_iIDTo; // ID of the last file in the range + int32_t m_bSmartOrder; // For Move-Actions: 0 - execute action for each ID in order they are placed in array (faster); + // 1 - smart execute to ensure that the relative order of all affected IDs are not changed (better, not yet implemented). + uint32_t m_iNrTrailingEntries; // Number of ID-entries, following to this structure + uint32_t m_iTrailingDataLength; // Length of all ID-entries, following to this structure + //uint32_t m_iIDs[0]; // variable sized }; // Request dumping of debug info diff --git a/Options.cpp b/Options.cpp index 27642d1f..61c39b27 100644 --- a/Options.cpp +++ b/Options.cpp @@ -74,7 +74,6 @@ static struct option long_options[] = {"log", required_argument, 0, 'G'}, {"top", no_argument, 0, 'T'}, {"edit", required_argument, 0, 'E'}, - {"fileid", required_argument, 0, 'I'}, {"connect", no_argument, 0, 'C'}, {"quit", no_argument, 0, 'Q'}, #ifdef DEBUG @@ -84,7 +83,7 @@ static struct option long_options[] = }; #endif -static char short_options[] = "c:hno:psvABDCG:LPUR:TE:I:Q"; +static char short_options[] = "c:hno:psvABDCG:LPUR:TE:Q"; // Program options static const char* OPTION_DESTDIR = "destdir"; @@ -155,8 +154,8 @@ Options::Options(int argc, char* argv[]) m_bResetLog = false; m_fDownloadRate = 0; m_iEditQueueAction = 0; - m_iEditQueueIDFrom = 0; - m_iEditQueueIDTo = 0; + m_pEditQueueIDList = NULL; + m_iEditQueueIDCount = 0; m_iEditQueueOffset = 0; m_szArgFilename = NULL; m_iConnectionTimeout = 0; @@ -193,7 +192,11 @@ Options::Options(int argc, char* argv[]) m_bNoConfig = false; char szFilename[MAX_PATH + 1]; +#ifdef WIN32 + GetModuleFileName(NULL, szFilename, MAX_PATH + 1); +#else strncpy(szFilename, argv[0], MAX_PATH + 1); +#endif szFilename[MAX_PATH] = '\0'; NormalizePathSeparators(szFilename); char* end = strrchr(szFilename, PATH_SEPARATOR); @@ -295,6 +298,10 @@ Options::~Options() { free(m_szPostProcess); } + if (m_pEditQueueIDList) + { + free(m_pEditQueueIDList); + } for (unsigned int i = 0; i < optEntries.size(); i++) { @@ -401,7 +408,7 @@ void Options::InitOptFile(int argc, char* argv[]) // search for config file in default locations #ifdef WIN32 char szFilename[MAX_PATH + 1]; - strncpy(szFilename, argv[0], MAX_PATH + 1); + GetModuleFileName(NULL, szFilename, MAX_PATH + 1); szFilename[MAX_PATH] = '\0'; NormalizePathSeparators(szFilename); char* end = strrchr(szFilename, PATH_SEPARATOR); @@ -663,34 +670,6 @@ void Options::InitCommandLine(int argc, char* argv[]) } break; } - case 'I': - { - const char* p = strchr(optarg, '-'); - if (p) - { - char buf[101]; - int maxlen = p - optarg < 100 ? p - optarg : 100; - strncpy(buf, optarg, maxlen); - buf[maxlen] = '\0'; - m_iEditQueueIDFrom = atoi(buf); - m_iEditQueueIDTo = atoi(p + 1); - if (m_iEditQueueIDFrom <= 0 || m_iEditQueueIDTo <= 0 || - m_iEditQueueIDFrom > m_iEditQueueIDTo) - { - abort("FATAL ERROR: wrong value for option 'I'\n"); - } - } - else - { - m_iEditQueueIDFrom = atoi(optarg); - if (m_iEditQueueIDFrom <= 0) - { - abort("FATAL ERROR: wrong value for option 'I'\n"); - } - m_iEditQueueIDTo = m_iEditQueueIDFrom; - } - break; - } case 'Q': m_eClientOperation = opClientRequestShutdown; break; @@ -729,8 +708,7 @@ void Options::PrintUsage(char* com) " -T, --top Add file to the top (begining) of queue\n" " (should be used with switch --append)\n" " -G, --log Request last lines from server's screen-log\n" - " -E, --edit Edit queue on the server\n" - " (must be used with switch --fileid):\n" + " -E, --edit Edit queue on the server\n" " where is one of:\n" " <+offset|-offset> Move file(s) in queue relative to current position\n" " offset is an integer number\n" @@ -739,8 +717,8 @@ void Options::PrintUsage(char* com) " P Pause file(s)\n" " U Resume (unpause) file(s)\n" " D Delete file(s)\n" - " -I, --fileid File-id(s) for switch '-E',\n" - " as printed by switch --list\n" + " where is a comma-separated list of file-ids or ranges of file-ids,\n" + " for example: 1-5,3,10-22" "", com); } @@ -758,6 +736,10 @@ void Options::InitFileArg(int argc, char* argv[]) exit(-1); } } + else if (m_eClientOperation == opClientRequestEditQueue) + { + ParseFileIDList(argc, argv, optind); + } else { // Check if the file-name is a relative path or an absolute path @@ -1076,3 +1058,118 @@ void Options::CheckOptions() } #endif } + +void Options::ParseFileIDList(int argc, char* argv[], int optind) +{ + std::vector IDs; + IDs.clear(); + + while (optind < argc) + { + char* szWritableFileIDList = strdup(argv[optind++]); + + char* optarg = strtok(szWritableFileIDList, ", "); + while (optarg) + { + int iEditQueueIDFrom = 0; + int iEditQueueIDTo = 0; + const char* p = strchr(optarg, '-'); + if (p) + { + char buf[101]; + int maxlen = p - optarg < 100 ? p - optarg : 100; + strncpy(buf, optarg, maxlen); + buf[maxlen] = '\0'; + iEditQueueIDFrom = atoi(buf); + iEditQueueIDTo = atoi(p + 1); + if (iEditQueueIDFrom <= 0 || iEditQueueIDTo <= 0 || + iEditQueueIDFrom > iEditQueueIDTo) + { + abort("FATAL ERROR: invalid list of file IDs\n"); + } + } + else + { + iEditQueueIDFrom = atoi(optarg); + if (iEditQueueIDFrom <= 0) + { + abort("FATAL ERROR: invalid list of file IDs\n"); + } + iEditQueueIDTo = iEditQueueIDFrom; + } + + int iEditQueueIDCount = 0; + if (iEditQueueIDTo != 0) + { + iEditQueueIDCount = iEditQueueIDTo - iEditQueueIDFrom + 1; + } + else + { + iEditQueueIDCount = 1; + } + + for (int i = 0; i < iEditQueueIDCount; i++) + { + IDs.push_back(iEditQueueIDFrom + i); + } + + optarg = strtok(NULL, ", "); + } + + free(szWritableFileIDList); + } + + m_iEditQueueIDCount = IDs.size(); + m_pEditQueueIDList = (int*)malloc(sizeof(int) * m_iEditQueueIDCount); + for (int i = 0; i < m_iEditQueueIDCount; i++) + { + m_pEditQueueIDList[i] = IDs[i]; + } +} + +/* +void Options::ParseFileIDList(const char* optarg) +{ + int iEditQueueIDFrom = 0; + int iEditQueueIDTo = 0; + + const char* p = strchr(optarg, '-'); + if (p) + { + char buf[101]; + int maxlen = p - optarg < 100 ? p - optarg : 100; + strncpy(buf, optarg, maxlen); + buf[maxlen] = '\0'; + iEditQueueIDFrom = atoi(buf); + iEditQueueIDTo = atoi(p + 1); + if (iEditQueueIDFrom <= 0 || iEditQueueIDTo <= 0 || + iEditQueueIDFrom > iEditQueueIDTo) + { + abort("FATAL ERROR: invalid list of file IDs\n"); + } + } + else + { + iEditQueueIDFrom = atoi(optarg); + if (iEditQueueIDFrom <= 0) + { + abort("FATAL ERROR: invalid list of file IDs\n"); + } + iEditQueueIDTo = iEditQueueIDFrom; + } + + if (iEditQueueIDTo != 0) + { + m_iEditQueueIDCount = iEditQueueIDTo - iEditQueueIDFrom + 1; + } + else + { + m_iEditQueueIDCount = 1; + } + + m_pEditQueueIDList = (int*)malloc(sizeof(int) * m_iEditQueueIDCount); + for (int i = 0; i < m_iEditQueueIDCount; i++) + { + m_pEditQueueIDList[i] = iEditQueueIDFrom + i; + } +}*/ \ No newline at end of file diff --git a/Options.h b/Options.h index c0fdd198..fcaea6b5 100644 --- a/Options.h +++ b/Options.h @@ -125,8 +125,8 @@ private: bool m_bRemoteClientMode; int m_iEditQueueAction; int m_iEditQueueOffset; - int m_iEditQueueIDFrom; - int m_iEditQueueIDTo; + int* m_pEditQueueIDList; + int m_iEditQueueIDCount; char* m_szArgFilename; bool m_bPrintOptions; bool m_bAddTop; @@ -156,6 +156,7 @@ private: bool ValidateOptionName(const char* optname); void LoadConfig(const char* configfile); void CheckDir(char** dir, const char* szOptionName); + void ParseFileIDList(int argc, char* argv[], int optind); public: Options(int argc, char* argv[]); @@ -205,8 +206,8 @@ public: EClientOperation GetClientOperation() { return m_eClientOperation; } int GetEditQueueAction() { return m_iEditQueueAction; } int GetEditQueueOffset() { return m_iEditQueueOffset; } - int GetEditQueueIDFrom() { return m_iEditQueueIDFrom; } - int GetEditQueueIDTo() { return m_iEditQueueIDTo; } + int* GetEditQueueIDList() { return m_pEditQueueIDList; } + int GetEditQueueIDCount() { return m_iEditQueueIDCount; } const char* GetArgFilename() { return m_szArgFilename; } bool GetAddTop() { return m_bAddTop; } float GetSetRate() { return m_fSetRate; } diff --git a/RemoteClient.cpp b/RemoteClient.cpp index 1f20a53e..835ff62e 100644 --- a/RemoteClient.cpp +++ b/RemoteClient.cpp @@ -243,7 +243,7 @@ bool RemoteClient::RequestServerList() szCompleted[0] = '\0'; if (lRemainingSize < lFileSize) { - sprintf(szCompleted, ", %i%s", (int)(100 - lRemainingSize * 100.0 / lFileSize), "\%"); + sprintf(szCompleted, ", %i%s", (int)(100 - lRemainingSize * 100.0 / lFileSize), "%"); } char szStatus[100]; if (ntohl(pListAnswer->m_bPaused)) @@ -437,31 +437,37 @@ bool RemoteClient::RequestServerDumpDebug() return true; } -bool RemoteClient::RequestServerEditQueue(int iAction, int iOffset, int iIDFrom, int iIDTo) +bool RemoteClient::RequestServerEditQueue(int iAction, int iOffset, int* pIDList, int iIDCount) { - if (iIDTo <= 0) + if (iIDCount <= 0 || pIDList == NULL) { - printf("File(s) not specified (use option -I)\n"); + printf("File(s) not specified\n"); return false; } if (!InitConnection()) return false; + int iLength = sizeof(uint32_t) * iIDCount; + SNZBEditQueueRequest EditQueueRequest; InitMessageBase(&EditQueueRequest.m_MessageBase, NZBMessageRequest::eRequestEditQueue, sizeof(EditQueueRequest)); EditQueueRequest.m_iAction = htonl(iAction); EditQueueRequest.m_iOffset = htonl((int)iOffset); - EditQueueRequest.m_iIDFrom = htonl(iIDFrom); - EditQueueRequest.m_iIDTo = htonl(iIDTo); + EditQueueRequest.m_bSmartOrder = htonl(true); + EditQueueRequest.m_iNrTrailingEntries = htonl(iIDCount); + EditQueueRequest.m_iTrailingDataLength = htonl(iLength); - bool OK = m_pConnection->Send((char*)(&EditQueueRequest), sizeof(EditQueueRequest)) >= 0; - if (OK) + bool OK = false; + if (m_pConnection->Send((char*)(&EditQueueRequest), sizeof(EditQueueRequest)) < 0) { - ReceiveCommandResult(); + perror("m_pConnection->Send"); } else { - perror("m_pConnection->Send"); + m_pConnection->Send((char*)pIDList, iLength); + ReceiveCommandResult(); + m_pConnection->Disconnect(); + OK = true; } m_pConnection->Disconnect(); diff --git a/RemoteClient.h b/RemoteClient.h index b99b2ed4..c0f1e7f8 100644 --- a/RemoteClient.h +++ b/RemoteClient.h @@ -53,7 +53,7 @@ public: bool RequestServerPauseUnpause(bool bPause); bool RequestServerSetDownloadRate(float fRate); bool RequestServerDumpDebug(); - bool RequestServerEditQueue(int iAction, int iOffset, int iIDFrom, int iIDTo); + bool RequestServerEditQueue(int iAction, int iOffset, int* pIDList, int iIDCount); bool RequestServerLog(int iLines); bool RequestServerShutdown(); }; diff --git a/RemoteServer.cpp b/RemoteServer.cpp index 86ff84f3..fda4b337 100644 --- a/RemoteServer.cpp +++ b/RemoteServer.cpp @@ -438,31 +438,59 @@ void MessageCommand::RequestEditQueue() { SNZBEditQueueRequest* pEditQueueRequest = (SNZBEditQueueRequest*) & m_RequestBuffer; - int From = ntohl(pEditQueueRequest->m_iIDFrom); - int To = ntohl(pEditQueueRequest->m_iIDTo); - int Step = 1; - if ((ntohl(pEditQueueRequest->m_iAction) == NZBMessageRequest::eActionMoveTop) || - ((ntohl(pEditQueueRequest->m_iAction) == NZBMessageRequest::eActionMoveOffset) && - ((int)ntohl(pEditQueueRequest->m_iOffset) < 0))) + uint32_t* m_iIDs = NULL; + int iNrEntries = ntohl(pEditQueueRequest->m_iNrTrailingEntries); + + if (ntohl(pEditQueueRequest->m_iTrailingDataLength) > 0) { - Step = -1; - int tmp = From; From = To; To = tmp; + + const char* pExtraData = (m_iExtraDataLength > 0) ? ((char*)pEditQueueRequest + ntohl(pEditQueueRequest->m_MessageBase.m_iStructSize)) : NULL; + int NeedBytes = ntohl(pEditQueueRequest->m_iTrailingDataLength) - m_iExtraDataLength; + + char* pRecvBuffer = (char*)malloc(ntohl(pEditQueueRequest->m_iTrailingDataLength)); + memcpy(pRecvBuffer, pExtraData, m_iExtraDataLength); + char* pBufPtr = pRecvBuffer + m_iExtraDataLength; + + // Read from the socket until nothing remains + int iResult = 0; + while (NeedBytes > 0) + { + iResult = recv(m_iSocket, pBufPtr, NeedBytes, 0); + // Did the recv succeed? + if (iResult <= 0) + { + error("invalid request"); + break; + } + pBufPtr += iResult; + NeedBytes -= iResult; + } + + m_iIDs = (uint32_t*)pRecvBuffer; } - for (int ID = From; ID != To + Step; ID += Step) + if (ntohl(pEditQueueRequest->m_bSmartOrder)) { - switch (ntohl(pEditQueueRequest->m_iAction)) + //TODO: reorder items for smart order + } + + int iAction = ntohl(pEditQueueRequest->m_iAction); + int iOffset = ntohl(pEditQueueRequest->m_iOffset); + for (int i = 0; i < iNrEntries; i++) + { + int ID = m_iIDs[i]; + switch (iAction) { case NZBMessageRequest::eActionPause: case NZBMessageRequest::eActionResume: { - g_pQueueCoordinator->EditQueuePauseUnpauseEntry(ID, ntohl(pEditQueueRequest->m_iAction) == NZBMessageRequest::eActionPause); + g_pQueueCoordinator->EditQueuePauseUnpauseEntry(ID, iAction == NZBMessageRequest::eActionPause); break; } case NZBMessageRequest::eActionMoveOffset: { - g_pQueueCoordinator->EditQueueMoveEntry(ID, ntohl(pEditQueueRequest->m_iOffset), true); + g_pQueueCoordinator->EditQueueMoveEntry(ID, iOffset, true); break; } @@ -485,6 +513,12 @@ void MessageCommand::RequestEditQueue() } } } + + if (m_iIDs) + { + free(m_iIDs); + } + SendResponse("Edit-Command completed successfully"); } diff --git a/Util.cpp b/Util.cpp index 27bf2a57..ff2d6222 100644 --- a/Util.cpp +++ b/Util.cpp @@ -107,7 +107,10 @@ int getopt(int argc, char *argv[], char *optstring) char *cp = strchr(optstring, c); if (cp == NULL || c == ':') + { + fprintf(stderr, "Invalid option %c", c); return '?'; + } cp++; if (*cp == ':') @@ -124,6 +127,7 @@ int getopt(int argc, char *argv[], char *optstring) } else { + fprintf(stderr, "Option %c needs an argument", c); return '?'; } } diff --git a/nzbget.cpp b/nzbget.cpp index d48364f9..07dcf02c 100644 --- a/nzbget.cpp +++ b/nzbget.cpp @@ -305,7 +305,7 @@ void ProcessClientRequest() else if (g_pOptions->GetClientOperation() == Options::opClientRequestEditQueue) { Client->RequestServerEditQueue(g_pOptions->GetEditQueueAction(), g_pOptions->GetEditQueueOffset(), - g_pOptions->GetEditQueueIDFrom(), g_pOptions->GetEditQueueIDTo()); + g_pOptions->GetEditQueueIDList(), g_pOptions->GetEditQueueIDCount()); } else if (g_pOptions->GetClientOperation() == Options::opClientRequestLog) { diff --git a/win32.h b/win32.h index a37e95f6..0351effd 100644 --- a/win32.h +++ b/win32.h @@ -56,7 +56,7 @@ /* Define to 1 if variadic macros are supported */ #define HAVE_VARIADIC_MACROS -#define VERSION "0.3.0" +#define VERSION "0.3.1-testing" /* Suppress warnings */ #define _CRT_SECURE_NO_DEPRECATE