Merge tag 'upstream/17.1+dfsg'
Upstream version 17.1+dfsg
Andreas Moog
7 years ago
0 | nzbget-17.1: | |
1 | - adjustments and fixes for "Retry failed articles" function, better handling | |
2 | of certain corner cases; | |
3 | - partial compatibility with gcc 4.8; | |
4 | - removed unnecessary debug logging to javascript console; | |
5 | - improved error reporting on certain file operations; | |
6 | - corrected option description; | |
7 | - corrected text in history delete confirmation dialog; | |
8 | - fixed performance issue on certain Windows systems; | |
9 | - fixed: root drive paths on Windows could not be used (for example | |
10 | "NzbDir=N:\"); | |
11 | - fixed hanging after marking as BAD from queue script; | |
12 | - fixed: old nzbget.exe was deleted even when installing into a new directory | |
13 | (Windows only); | |
14 | - fixed: compilation error if configured with unit tests but without par-module; | |
15 | - fixed crash on malformed articles; | |
16 | - fixed javascript error on Chrome for Linux; | |
17 | - fixed compilation error if configured without TLS. | |
18 | ||
0 | 19 | nzbget-17.0: |
1 | 20 | - reworked the full source code base to utilize modern C++ features: |
2 | 21 | - with the main motivation to make the code nicer and more fun to work |
216 | 216 | tests/main/CommandLineParserTest.cpp \ |
217 | 217 | tests/main/OptionsTest.cpp \ |
218 | 218 | tests/feed/FeedFilterTest.cpp \ |
219 | tests/postprocess/ParCheckerTest.cpp \ | |
220 | tests/postprocess/ParRenamerTest.cpp \ | |
221 | 219 | tests/postprocess/DupeMatcherTest.cpp \ |
222 | 220 | tests/queue/NzbFileTest.cpp \ |
223 | 221 | tests/nntp/ServerPoolTest.cpp \ |
224 | 222 | tests/util/FileSystemTest.cpp \ |
225 | 223 | tests/util/NStringTest.cpp \ |
226 | 224 | tests/util/UtilTest.cpp |
225 | ||
226 | if WITH_PAR2 | |
227 | nzbget_SOURCES += \ | |
228 | tests/postprocess/ParCheckerTest.cpp \ | |
229 | tests/postprocess/ParRenamerTest.cpp | |
230 | endif | |
227 | 231 | |
228 | 232 | AM_CPPFLAGS += \ |
229 | 233 | -I$(srcdir)/lib/catch \ |
0 | 0 | #! /bin/sh |
1 | 1 | # Guess values for system-dependent variables and create Makefiles. |
2 | # Generated by GNU Autoconf 2.61 for nzbget 17.0. | |
2 | # Generated by GNU Autoconf 2.61 for nzbget 17.1. | |
3 | 3 | # |
4 | 4 | # Report bugs to <hugbug@users.sourceforge.net>. |
5 | 5 | # |
573 | 573 | # Identity of this package. |
574 | 574 | PACKAGE_NAME='nzbget' |
575 | 575 | PACKAGE_TARNAME='nzbget' |
576 | PACKAGE_VERSION='17.0' | |
577 | PACKAGE_STRING='nzbget 17.0' | |
576 | PACKAGE_VERSION='17.1' | |
577 | PACKAGE_STRING='nzbget 17.1' | |
578 | 578 | PACKAGE_BUGREPORT='hugbug@users.sourceforge.net' |
579 | 579 | |
580 | 580 | ac_unique_file="daemon/main/nzbget.cpp" |
1250 | 1250 | # Omit some internal or obsolete options to make the list less imposing. |
1251 | 1251 | # This message is too long to be a string in the A/UX 3.1 sh. |
1252 | 1252 | cat <<_ACEOF |
1253 | \`configure' configures nzbget 17.0 to adapt to many kinds of systems. | |
1253 | \`configure' configures nzbget 17.1 to adapt to many kinds of systems. | |
1254 | 1254 | |
1255 | 1255 | Usage: $0 [OPTION]... [VAR=VALUE]... |
1256 | 1256 | |
1321 | 1321 | |
1322 | 1322 | if test -n "$ac_init_help"; then |
1323 | 1323 | case $ac_init_help in |
1324 | short | recursive ) echo "Configuration of nzbget 17.0:";; | |
1324 | short | recursive ) echo "Configuration of nzbget 17.1:";; | |
1325 | 1325 | esac |
1326 | 1326 | cat <<\_ACEOF |
1327 | 1327 | |
1465 | 1465 | test -n "$ac_init_help" && exit $ac_status |
1466 | 1466 | if $ac_init_version; then |
1467 | 1467 | cat <<\_ACEOF |
1468 | nzbget configure 17.0 | |
1468 | nzbget configure 17.1 | |
1469 | 1469 | generated by GNU Autoconf 2.61 |
1470 | 1470 | |
1471 | 1471 | Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001, |
1479 | 1479 | This file contains any messages produced by compilers while |
1480 | 1480 | running configure, to aid debugging if configure makes a mistake. |
1481 | 1481 | |
1482 | It was created by nzbget $as_me 17.0, which was | |
1482 | It was created by nzbget $as_me 17.1, which was | |
1483 | 1483 | generated by GNU Autoconf 2.61. Invocation command line was |
1484 | 1484 | |
1485 | 1485 | $ $0 $@ |
2275 | 2275 | |
2276 | 2276 | # Define the identity of the package. |
2277 | 2277 | PACKAGE='nzbget' |
2278 | VERSION='17.0' | |
2278 | VERSION='17.1' | |
2279 | 2279 | |
2280 | 2280 | |
2281 | 2281 | cat >>confdefs.h <<_ACEOF |
11824 | 11824 | # report actual input values of CONFIG_FILES etc. instead of their |
11825 | 11825 | # values after options handling. |
11826 | 11826 | ac_log=" |
11827 | This file was extended by nzbget $as_me 17.0, which was | |
11827 | This file was extended by nzbget $as_me 17.1, which was | |
11828 | 11828 | generated by GNU Autoconf 2.61. Invocation command line was |
11829 | 11829 | |
11830 | 11830 | CONFIG_FILES = $CONFIG_FILES |
11877 | 11877 | _ACEOF |
11878 | 11878 | cat >>$CONFIG_STATUS <<_ACEOF |
11879 | 11879 | ac_cs_version="\\ |
11880 | nzbget config.status 17.0 | |
11880 | nzbget config.status 17.1 | |
11881 | 11881 | configured by $0, generated by GNU Autoconf 2.61, |
11882 | 11882 | with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\" |
11883 | 11883 |
20 | 20 | # Process this file with autoconf to produce a configure script. |
21 | 21 | |
22 | 22 | AC_PREREQ(2.59) |
23 | AC_INIT(nzbget, 17.0, hugbug@users.sourceforge.net) | |
23 | AC_INIT(nzbget, 17.1, hugbug@users.sourceforge.net) | |
24 | 24 | AC_CONFIG_AUX_DIR(posix) |
25 | 25 | AC_CANONICAL_TARGET |
26 | 26 | AM_INIT_AUTOMAKE([foreign]) |
109 | 109 | NzbInfo* nzbInfo = downloadQueue->GetQueue()->Find(m_id); |
110 | 110 | if (nzbInfo) |
111 | 111 | { |
112 | PrintMessage(Message::mkWarning, "Cancelling download and deleting %s", *m_nzbName); | |
112 | nzbInfo->PrintMessage(Message::mkWarning, "Cancelling download and deleting %s", *m_nzbName); | |
113 | 113 | nzbInfo->SetDeleteStatus(NzbInfo::dsBad); |
114 | 114 | downloadQueue->EditEntry(m_id, DownloadQueue::eaGroupDelete, 0, nullptr); |
115 | 115 | } |
214 | 214 | NzbInfo* nzbInfo = QueueScriptCoordinator::FindNzbInfo(downloadQueue, m_id); |
215 | 215 | if (nzbInfo) |
216 | 216 | { |
217 | SetLogPrefix(nullptr); | |
218 | PrintMessage(Message::mkWarning, "Marking %s as bad", *m_nzbName); | |
219 | SetLogPrefix(m_script->GetDisplayName()); | |
217 | nzbInfo->PrintMessage(Message::mkWarning, "Marking %s as bad", *m_nzbName); | |
220 | 218 | nzbInfo->SetMarkStatus(NzbInfo::ksBad); |
221 | 219 | } |
222 | 220 | } |
956 | 956 | #ifdef DISABLE_TLS |
957 | 957 | if (tls) |
958 | 958 | { |
959 | ConfigError("Invalid value for option \"%s\": program was compiled without TLS/SSL-support", optname); | |
959 | ConfigError("Invalid value for option \"%s\": program was compiled without TLS/SSL-support", | |
960 | *BString<100>("Server%i.Encryption", n)); | |
960 | 961 | tls = false; |
961 | 962 | } |
962 | 963 | #endif |
39 | 39 | Guard guard(m_taskListMutex); |
40 | 40 | |
41 | 41 | std::sort(m_taskList.begin(), m_taskList.end(), |
42 | [](std::unique_ptr<Task>& task1, std::unique_ptr<Task>& task2) | |
42 | [](const std::unique_ptr<Task>& task1, const std::unique_ptr<Task>& task2) | |
43 | 43 | { |
44 | 44 | return (task1->m_hours < task2->m_hours) || |
45 | 45 | ((task1->m_hours == task2->m_hours) && (task1->m_minutes < task2->m_minutes)); |
339 | 339 | #define SCANF_SYNTAX(strindex) |
340 | 340 | #endif |
341 | 341 | |
342 | // providing "std::make_unique" for GCC 4.8.x (only 4.8.x) | |
343 | #if __GNUC__ && __cplusplus < 201402L && __cpp_generic_lambdas < 201304 | |
344 | namespace std { | |
345 | template<class T> struct _Unique_if { typedef unique_ptr<T> _Single_object; }; | |
346 | template<class T> struct _Unique_if<T[]> { typedef unique_ptr<T[]> _Unknown_bound; }; | |
347 | template<class T, class... Args> typename _Unique_if<T>::_Single_object make_unique(Args&&... args) { | |
348 | return unique_ptr<T>(new T(std::forward<Args>(args)...)); | |
349 | } | |
350 | template<class T> typename _Unique_if<T>::_Unknown_bound make_unique(size_t n) { | |
351 | typedef typename remove_extent<T>::type U; | |
352 | return unique_ptr<T>(new U[n]()); | |
353 | } | |
354 | } | |
355 | #endif | |
356 | ||
342 | 357 | #endif /* NZBGET_H */ |
128 | 128 | // park remaining files |
129 | 129 | for (FileInfo* fileInfo : nzbInfo->GetFileList()) |
130 | 130 | { |
131 | fileInfo->GetNzbInfo()->UpdateCompletedStats(fileInfo); | |
132 | fileInfo->GetNzbInfo()->GetCompletedFiles()->emplace_back(fileInfo->GetId(), | |
131 | nzbInfo->UpdateCompletedStats(fileInfo); | |
132 | nzbInfo->GetCompletedFiles()->emplace_back(fileInfo->GetId(), | |
133 | 133 | fileInfo->GetFilename(), CompletedFile::cfNone, 0); |
134 | 134 | } |
135 | 135 | |
136 | 136 | // Cleaning up parked files if par-check was successful or unpack was successful or |
137 | // health is 100% (if unpack and par-check were not performed) | |
137 | // health is 100% (if unpack and par-check were not performed) or if deleted | |
138 | 138 | bool cleanupParkedFiles = |
139 | 139 | ((nzbInfo->GetParStatus() == NzbInfo::psSuccess || |
140 | 140 | nzbInfo->GetParStatus() == NzbInfo::psRepairPossible) && |
146 | 146 | (nzbInfo->GetUnpackStatus() <= NzbInfo::usSkipped && |
147 | 147 | nzbInfo->GetParStatus() != NzbInfo::psFailure && |
148 | 148 | nzbInfo->GetFailedSize() - nzbInfo->GetParFailedSize() == 0) || |
149 | nzbInfo->GetUnpackCleanedUpDisk(); | |
149 | (nzbInfo->GetDeleteStatus() != NzbInfo::dsNone); | |
150 | ||
151 | // Do not cleanup when parking | |
152 | cleanupParkedFiles &= !nzbInfo->GetParking(); | |
153 | ||
154 | // Parking not possible if files were already deleted | |
155 | cleanupParkedFiles |= nzbInfo->GetUnpackCleanedUpDisk(); | |
150 | 156 | |
151 | 157 | if (cleanupParkedFiles) |
152 | 158 | { |
157 | 163 | nzbInfo->SetParkedFileCount(0); |
158 | 164 | for (CompletedFile& completedFile : nzbInfo->GetCompletedFiles()) |
159 | 165 | { |
160 | if (completedFile.GetStatus() == CompletedFile::cfNone) | |
166 | if (completedFile.GetStatus() == CompletedFile::cfNone || | |
167 | // consider last completed file with partial status not completely tried | |
168 | (completedFile.GetStatus() == CompletedFile::cfPartial && | |
169 | &completedFile == &*nzbInfo->GetCompletedFiles()->rbegin())) | |
161 | 170 | { |
162 | 171 | nzbInfo->PrintMessage(Message::mkDetail, "Parking file %s", completedFile.GetFileName()); |
163 | 172 | nzbInfo->SetParkedFileCount(nzbInfo->GetParkedFileCount() + 1); |
575 | 584 | (resetFailed || fileInfo->GetRemainingSize() > 0)))) |
576 | 585 | { |
577 | 586 | fileInfo->SetFilename(completedFile.GetFileName()); |
578 | fileInfo->SetPaused(fileInfo->GetParFile()); | |
579 | 587 | fileInfo->SetNzbInfo(nzbInfo); |
580 | 588 | |
581 | 589 | BString<1024> outputFilename("%s%c%s", nzbInfo->GetDestDir(), PATH_SEPARATOR, fileInfo->GetFilename()); |
594 | 602 | else if (!reprocess) |
595 | 603 | { |
596 | 604 | nzbInfo->PrintMessage(Message::mkWarning, "File %s could not be found on disk, downloading again", fileInfo->GetFilename()); |
605 | fileInfo->SetPartialState(FileInfo::psNone); | |
597 | 606 | } |
598 | 607 | } |
599 | 608 | |
618 | 627 | nzbInfo->UpdateCurrentStats(); |
619 | 628 | |
620 | 629 | MoveToQueue(downloadQueue, itHistory, historyInfo, reprocess); |
630 | ||
631 | if (g_Options->GetParCheck() != Options::pcForce) | |
632 | { | |
633 | downloadQueue->EditEntry(nzbInfo->GetId(), DownloadQueue::eaGroupPauseExtraPars, 0, nullptr); | |
634 | } | |
621 | 635 | } |
622 | 636 | |
623 | 637 | void HistoryCoordinator::ResetArticles(FileInfo* fileInfo, bool allFailed, bool resetFailed) |
708 | 708 | { |
709 | 709 | fileInfo->GetNzbInfo()->GetCompletedFiles()->emplace_back( |
710 | 710 | fileInfo->GetId(), |
711 | completed ? FileSystem::BaseFileName(fileInfo->GetOutputFilename()) : fileInfo->GetFilename(), | |
711 | completed && fileInfo->GetOutputFilename() ? | |
712 | FileSystem::BaseFileName(fileInfo->GetOutputFilename()) : fileInfo->GetFilename(), | |
712 | 713 | fileStatus, |
713 | 714 | fileStatus == CompletedFile::cfSuccess ? fileInfo->GetCrc() : 0); |
714 | 715 | } |
37 | 37 | GroupSorter(NzbList* nzbList, QueueEditor::ItemList* sortItemList) : |
38 | 38 | m_nzbList(nzbList), m_sortItemList(sortItemList) {} |
39 | 39 | bool Execute(const char* sort); |
40 | bool operator()(std::unique_ptr<NzbInfo>& refNzbInfo1, std::unique_ptr<NzbInfo>& refNzbInfo2) const; | |
40 | bool operator()(const std::unique_ptr<NzbInfo>& refNzbInfo1, const std::unique_ptr<NzbInfo>& refNzbInfo2) const; | |
41 | 41 | |
42 | 42 | private: |
43 | 43 | enum ESortCriteria |
128 | 128 | std::sort(m_nzbList->begin(), m_nzbList->end(), *this); |
129 | 129 | |
130 | 130 | if (origSortOrder == soAuto && |
131 | std::equal(tempList.begin(), tempList.end(), m_nzbList->begin(), m_nzbList->end(), | |
131 | std::equal(tempList.begin(), tempList.end(), m_nzbList->begin(), | |
132 | 132 | [](NzbInfo* nzbInfo1, std::unique_ptr<NzbInfo>& nzbInfo2) |
133 | 133 | { |
134 | 134 | return nzbInfo1 == nzbInfo2.get(); |
141 | 141 | return true; |
142 | 142 | } |
143 | 143 | |
144 | bool GroupSorter::operator()(std::unique_ptr<NzbInfo>& refNzbInfo1, std::unique_ptr<NzbInfo>& refNzbInfo2) const | |
144 | bool GroupSorter::operator()(const std::unique_ptr<NzbInfo>& refNzbInfo1, const std::unique_ptr<NzbInfo>& refNzbInfo2) const | |
145 | 145 | { |
146 | 146 | NzbInfo* nzbInfo1 = refNzbInfo1.get(); |
147 | 147 | NzbInfo* nzbInfo2 = refNzbInfo2.get(); |
777 | 777 | nzbInfo->SetParking(action == DownloadQueue::eaGroupParkDelete && |
778 | 778 | g_Options->GetKeepHistory() > 0 && |
779 | 779 | !nzbInfo->GetUnpackCleanedUpDisk() && |
780 | (nzbInfo->GetSuccessArticles() > 0 || nzbInfo->GetFailedArticles() > 0)); | |
780 | nzbInfo->GetCurrentSuccessArticles() > 0); | |
781 | 781 | nzbInfo->SetAvoidHistory(action == DownloadQueue::eaGroupFinalDelete); |
782 | 782 | nzbInfo->SetDeletePaused(allPaused); |
783 | 783 | if (action == DownloadQueue::eaGroupDupeDelete) |
2425 | 2425 | "<member><name>ID</name><value><i4>%i</i4></value></member>\n" // Deprecated, use "NZBID" instead |
2426 | 2426 | "<member><name>Name</name><value><string>%s</string></value></member>\n" |
2427 | 2427 | "<member><name>RemainingFileCount</name><value><i4>%i</i4></value></member>\n" |
2428 | "<member><name>RetryData</name><value><boolean>%s</boolean></value></member>\n" | |
2428 | 2429 | "<member><name>HistoryTime</name><value><i4>%i</i4></value></member>\n" |
2429 | 2430 | "<member><name>Status</name><value><string>%s</string></value></member>\n" |
2430 | 2431 | "<member><name>Log</name><value><array><data></data></array></value></member>\n"; // Deprected, always empty |
2434 | 2435 | "\"ID\" : %i,\n" // Deprecated, use "NZBID" instead |
2435 | 2436 | "\"Name\" : \"%s\",\n" |
2436 | 2437 | "\"RemainingFileCount\" : %i,\n" |
2438 | "\"RetryData\" : %s,\n" | |
2437 | 2439 | "\"HistoryTime\" : %i,\n" |
2438 | 2440 | "\"Status\" : \"%s\",\n" |
2439 | 2441 | "\"Log\" : [],\n"; // Deprected, always empty |
2505 | 2507 | |
2506 | 2508 | AppendFmtResponse(IsJson() ? JSON_HISTORY_ITEM_START : XML_HISTORY_ITEM_START, |
2507 | 2509 | historyInfo->GetId(), *EncodeStr(historyInfo->GetName()), nzbInfo->GetParkedFileCount(), |
2508 | historyInfo->GetTime(), status); | |
2510 | BoolToStr(nzbInfo->GetCompletedFiles()->size()), historyInfo->GetTime(), status); | |
2509 | 2511 | } |
2510 | 2512 | else if (historyInfo->GetKind() == HistoryInfo::hkDup) |
2511 | 2513 | { |
29 | 29 | { |
30 | 30 | BString<1024> msg; |
31 | 31 | strerror_r(errno, msg, msg.Capacity()); |
32 | ||
33 | #ifdef WIN32 | |
34 | if (!errno) | |
35 | { | |
36 | FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, | |
37 | nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | |
38 | msg, 1024, nullptr); | |
39 | } | |
40 | #endif | |
41 | ||
32 | 42 | return *msg; |
33 | 43 | } |
34 | 44 | |
104 | 114 | } |
105 | 115 | } |
106 | 116 | |
117 | errmsg.Format("path %s does not exist and could not be created", *normPath); | |
107 | 118 | return false; |
108 | 119 | } |
109 | 120 | #else |
248 | 259 | HANDLE hFile = CreateFileW(UtfPathToWidePath(filename), GENERIC_WRITE, FILE_SHARE_READ, 0, CREATE_NEW, 0, nullptr); |
249 | 260 | if (hFile == INVALID_HANDLE_VALUE) |
250 | 261 | { |
262 | errno = 0; // wanting error message from WinAPI instead of C-lib | |
251 | 263 | errmsg = GetLastErrorMessage(); |
252 | 264 | return false; |
253 | 265 | } |
525 | 537 | { |
526 | 538 | #ifdef WIN32 |
527 | 539 | WIN32_FIND_DATAW findData; |
528 | // extra "\*" needed for network shares | |
529 | 540 | HANDLE handle = FindFirstFileW(UtfPathToWidePath( |
530 | 541 | BString<1024>(dirFilename && dirFilename[strlen(dirFilename) - 1] == PATH_SEPARATOR ? "%s*" : "%s\\*", dirFilename)), |
531 | 542 | &findData); |
532 | 543 | if (handle != INVALID_HANDLE_VALUE) |
533 | 544 | { |
534 | 545 | bool exists = ((findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) || |
535 | (strlen(dirFilename) == 3 && dirFilename[1] == ':'); | |
546 | (dirFilename[0] != '\0' && dirFilename[1] == ':' && (dirFilename[2] == '\0' || dirFilename[3] == '\0')); | |
536 | 547 | FindClose(handle); |
537 | 548 | return exists; |
549 | } | |
550 | if (GetLastError() == ERROR_FILE_NOT_FOUND) | |
551 | { | |
552 | // path exists but doesn't have any file/directory entries - possible only for root paths (e. g. "C:\") | |
553 | return true; | |
538 | 554 | } |
539 | 555 | return false; |
540 | 556 | #else |
840 | 856 | BOOL ok = ::FlushFileBuffers((HANDLE)_get_osfhandle(fileDescriptor)); |
841 | 857 | if (!ok) |
842 | 858 | { |
843 | errmsg.Reserve(1024 - 1); | |
844 | FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, | |
845 | nullptr, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), | |
846 | errmsg, 1024, nullptr); | |
859 | errno = 0; // wanting error message from WinAPI instead of C-lib | |
860 | errmsg = GetLastErrorMessage(); | |
847 | 861 | } |
848 | 862 | return ok; |
849 | 863 | #else |
505 | 505 | |
506 | 506 | time_t Util::CurrentTime() |
507 | 507 | { |
508 | #ifdef WIN32 | |
509 | // C-library function "time()" works on Windows too but is very CPU intensive | |
510 | // since it uses high performance timer which we don't need anyway. | |
511 | // A combination of GetSystemTime() + Timegm() works much faster. | |
512 | SYSTEMTIME systm; | |
513 | GetSystemTime(&systm); | |
514 | struct tm tm; | |
515 | tm.tm_year = systm.wYear - 1900; | |
516 | tm.tm_mon = systm.wMonth - 1; | |
517 | tm.tm_mday = systm.wDay; | |
518 | tm.tm_hour = systm.wHour; | |
519 | tm.tm_min = systm.wMinute; | |
520 | tm.tm_sec = systm.wSecond; | |
521 | return Timegm(&tm); | |
522 | #else | |
508 | 523 | return ::time(nullptr); |
524 | #endif | |
509 | 525 | } |
510 | 526 | |
511 | 527 | /* From boost */ |
13 | 13 | # |
14 | 14 | # If you want to distinguish between partially downloaded files and |
15 | 15 | # completed downloads, use also option <InterDir>. |
16 | # | |
17 | # It is allowed to enter multiple directories here by separating them with comma | |
18 | # or semicolon. NZBGet checks how much free disk space is available in each | |
19 | # directory (assuming all directories are located on different drives) and | |
20 | # chooses the directory with the most free space. | |
21 | 16 | DestDir=${MainDir}/dst |
22 | 17 | |
23 | 18 | # Directory to store intermediate files. |
51 | 51 | <ClCompile> |
52 | 52 | <Optimization>Disabled</Optimization> |
53 | 53 | <AdditionalIncludeDirectories>.\daemon\connect;.\daemon\extension;.\daemon\feed;.\daemon\frontend;.\daemon\main;.\daemon\nntp;.\daemon\postprocess;.\daemon\queue;.\daemon\remote;.\daemon\util;.\daemon\windows;.\lib\par2;.\windows\resources;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> |
54 | <PreprocessorDefinitions>WIN32;PACKAGE="nzbget";VERSION="17.0";_DEBUG;_CONSOLE;DEBUG;_WIN32_WINNT=0x0403;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |
54 | <PreprocessorDefinitions>WIN32;PACKAGE="nzbget";VERSION="17.1";_DEBUG;_CONSOLE;DEBUG;_WIN32_WINNT=0x0403;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |
55 | 55 | <MinimalRebuild>false</MinimalRebuild> |
56 | 56 | <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks> |
57 | 57 | <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary> |
71 | 71 | <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> |
72 | 72 | <ClCompile> |
73 | 73 | <AdditionalIncludeDirectories>.\daemon\connect;.\daemon\extension;.\daemon\feed;.\daemon\frontend;.\daemon\main;.\daemon\nntp;.\daemon\postprocess;.\daemon\queue;.\daemon\remote;.\daemon\util;.\daemon\windows;.\lib\par2;.\windows\resources;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories> |
74 | <PreprocessorDefinitions>WIN32;PACKAGE="nzbget";VERSION="17.0";NDEBUG;_CONSOLE;_WIN32_WINNT=0x0403;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |
74 | <PreprocessorDefinitions>WIN32;PACKAGE="nzbget";VERSION="17.1";NDEBUG;_CONSOLE;_WIN32_WINNT=0x0403;%(PreprocessorDefinitions)</PreprocessorDefinitions> | |
75 | 75 | <ExceptionHandling>Sync</ExceptionHandling> |
76 | 76 | <RuntimeLibrary>MultiThreaded</RuntimeLibrary> |
77 | 77 | <PrecompiledHeader>Use</PrecompiledHeader> |
1536 | 1536 | $('#Notif_Config_TestConnectionProgress').fadeOut(function() { |
1537 | 1537 | if (errtext == '') |
1538 | 1538 | { |
1539 | Notification.show('#Notif_Config_TestConnectionOK'); | |
1539 | PopupNotification.show('#Notif_Config_TestConnectionOK'); | |
1540 | 1540 | } |
1541 | 1541 | else |
1542 | 1542 | { |
1661 | 1661 | |
1662 | 1662 | if (serverSaveRequest.length === 0 && webSaveRequest.length === 0) |
1663 | 1663 | { |
1664 | Notification.show('#Notif_Config_Unchanged'); | |
1664 | PopupNotification.show('#Notif_Config_Unchanged'); | |
1665 | 1665 | return; |
1666 | 1666 | } |
1667 | 1667 | |
1705 | 1705 | } |
1706 | 1706 | else |
1707 | 1707 | { |
1708 | Notification.show('#Notif_Config_Failed'); | |
1708 | PopupNotification.show('#Notif_Config_Failed'); | |
1709 | 1709 | } |
1710 | 1710 | configSaved = true; |
1711 | 1711 | } |
2555 | 2555 | var checkedCount = $SectionTable.fasttable('checkedCount'); |
2556 | 2556 | if (checkedCount === 0) |
2557 | 2557 | { |
2558 | Notification.show('#Notif_Config_RestoreSections'); | |
2558 | PopupNotification.show('#Notif_Config_RestoreSections'); | |
2559 | 2559 | return; |
2560 | 2560 | } |
2561 | 2561 | |
2813 | 2813 | { |
2814 | 2814 | if (!started) |
2815 | 2815 | { |
2816 | Notification.show('#Notif_StartUpdate_Failed'); | |
2816 | PopupNotification.show('#Notif_StartUpdate_Failed'); | |
2817 | 2817 | return; |
2818 | 2818 | } |
2819 | 2819 |
354 | 354 | Refresher.update(); |
355 | 355 | if (notification) |
356 | 356 | { |
357 | Notification.show(notification); | |
357 | PopupNotification.show(notification); | |
358 | 358 | notification = null; |
359 | 359 | } |
360 | 360 | } |
374 | 374 | { |
375 | 375 | if (group.postprocess && !allowPostProcess) |
376 | 376 | { |
377 | Notification.show('#Notif_Downloads_CheckPostProcess'); | |
377 | PopupNotification.show('#Notif_Downloads_CheckPostProcess'); | |
378 | 378 | return null; |
379 | 379 | } |
380 | 380 | if (group.Kind === 'URL' && !allowUrl) |
381 | 381 | { |
382 | Notification.show('#Notif_Downloads_CheckURL'); | |
382 | PopupNotification.show('#Notif_Downloads_CheckURL'); | |
383 | 383 | return null; |
384 | 384 | } |
385 | 385 | |
389 | 389 | |
390 | 390 | if (checkedEditIDs.length === 0 && !allowEmpty) |
391 | 391 | { |
392 | Notification.show('#Notif_Downloads_Select'); | |
392 | PopupNotification.show('#Notif_Downloads_Select'); | |
393 | 393 | return null; |
394 | 394 | } |
395 | 395 | |
426 | 426 | |
427 | 427 | if (checkedEditIDs.length < 2) |
428 | 428 | { |
429 | Notification.show('#Notif_Downloads_SelectMulti'); | |
429 | PopupNotification.show('#Notif_Downloads_SelectMulti'); | |
430 | 430 | return; |
431 | 431 | } |
432 | 432 | |
489 | 489 | |
490 | 490 | if (downloadIDs.length === 0 && postprocessIDs.length === 0) |
491 | 491 | { |
492 | Notification.show('#Notif_Downloads_Select'); | |
492 | PopupNotification.show('#Notif_Downloads_Select'); | |
493 | 493 | return; |
494 | 494 | } |
495 | 495 |
298 | 298 | Refresher.update(); |
299 | 299 | if (notification) |
300 | 300 | { |
301 | Notification.show(notification); | |
301 | PopupNotification.show(notification); | |
302 | 302 | notification = null; |
303 | 303 | } |
304 | 304 | } |
654 | 654 | var checkedCount = $DownloadsFileTable.fasttable('checkedCount'); |
655 | 655 | if (checkedCount === 0) |
656 | 656 | { |
657 | Notification.show('#Notif_Edit_Select'); | |
657 | PopupNotification.show('#Notif_Edit_Select'); | |
658 | 658 | return; |
659 | 659 | } |
660 | 660 | |
747 | 747 | { |
748 | 748 | if (splitError) |
749 | 749 | { |
750 | Notification.show('#Notif_Downloads_SplitNotPossible'); | |
750 | PopupNotification.show('#Notif_Downloads_SplitNotPossible'); | |
751 | 751 | } |
752 | 752 | else |
753 | 753 | { |
1415 | 1415 | Refresher.update(); |
1416 | 1416 | if (notification) |
1417 | 1417 | { |
1418 | Notification.show(notification); | |
1418 | PopupNotification.show(notification); | |
1419 | 1419 | } |
1420 | 1420 | } |
1421 | 1421 | }(jQuery)); |
1483 | 1483 | { |
1484 | 1484 | $DownloadsMergeDialog.modal('hide'); |
1485 | 1485 | Refresher.update(); |
1486 | Notification.show('#Notif_Downloads_Merged'); | |
1486 | PopupNotification.show('#Notif_Downloads_Merged'); | |
1487 | 1487 | } |
1488 | 1488 | }(jQuery)); |
1489 | 1489 | |
1540 | 1540 | $('#DownloadsEditDialog').modal('hide'); |
1541 | 1541 | $DownloadsSplitDialog.modal('hide'); |
1542 | 1542 | Refresher.update(); |
1543 | Notification.show(result ? '#Notif_Downloads_Splitted' : '#Notif_Downloads_SplitError'); | |
1543 | PopupNotification.show(result ? '#Notif_Downloads_Splitted' : '#Notif_Downloads_SplitError'); | |
1544 | 1544 | } |
1545 | 1545 | }(jQuery)); |
1546 | 1546 | |
1739 | 1739 | Util.show('#HistoryEdit_Return', hist.RemainingFileCount > 0); |
1740 | 1740 | Util.show('#HistoryEdit_ReturnURL', hist.Kind === 'URL'); |
1741 | 1741 | Util.show('#HistoryEdit_Redownload', hist.Kind === 'NZB'); |
1742 | Util.show('#HistoryEdit_RetryFailed', hist.Kind === 'NZB' && hist.FailedArticles > 0 && hist.ParStatus !== 'SUCCESS' && | |
1743 | (hist.DeleteStatus === 'NONE' || hist.RemainingFileCount > 0)); | |
1742 | Util.show('#HistoryEdit_RetryFailed', hist.Kind === 'NZB' && hist.FailedArticles > 0 && hist.RetryData); | |
1744 | 1743 | Util.show('#HistoryEdit_PathGroup, #HistoryEdit_StatisticsGroup, #HistoryEdit_Reprocess', hist.Kind === 'NZB'); |
1745 | 1744 | Util.show('#HistoryEdit_CategoryGroup', hist.Kind !== 'DUP'); |
1746 | 1745 | Util.show('#HistoryEdit_DupGroup', hist.Kind === 'DUP'); |
1901 | 1900 | { |
1902 | 1901 | e.preventDefault(); |
1903 | 1902 | HistoryUI.deleteConfirm(doItemDelete, curHist.Kind === 'NZB', curHist.Kind === 'DUP', |
1904 | curHist.ParStatus === 'FAILURE' || curHist.UnpackStatus === 'FAILURE', false); | |
1903 | curHist.ParStatus === 'FAILURE' || curHist.UnpackStatus === 'FAILURE' || | |
1904 | curHist.DeleteStatus != 'NONE', false); | |
1905 | 1905 | } |
1906 | 1906 | |
1907 | 1907 | function doItemDelete(command) |
1974 | 1974 | Refresher.update(); |
1975 | 1975 | if (notification) |
1976 | 1976 | { |
1977 | Notification.show(notification); | |
1977 | PopupNotification.show(notification); | |
1978 | 1978 | notification = null; |
1979 | 1979 | } |
1980 | 1980 | } |
79 | 79 | var id = parseInt($(this).attr('data-id')); |
80 | 80 | RPC.call('fetchfeed', [id], function() |
81 | 81 | { |
82 | Notification.show('#Notif_Feeds_Fetch'); | |
82 | PopupNotification.show('#Notif_Feeds_Fetch'); | |
83 | 83 | }); |
84 | 84 | } |
85 | 85 | |
87 | 87 | { |
88 | 88 | RPC.call('fetchfeed', [0], function() |
89 | 89 | { |
90 | Notification.show('#Notif_Feeds_Fetch'); | |
90 | PopupNotification.show('#Notif_Feeds_Fetch'); | |
91 | 91 | }); |
92 | 92 | } |
93 | 93 | }(jQuery)); |
332 | 332 | var checkedCount = $ItemTable.fasttable('checkedCount'); |
333 | 333 | if (checkedCount === 0) |
334 | 334 | { |
335 | Notification.show('#Notif_FeedDialog_Select'); | |
335 | PopupNotification.show('#Notif_FeedDialog_Select'); | |
336 | 336 | return; |
337 | 337 | } |
338 | 338 | |
371 | 371 | else |
372 | 372 | { |
373 | 373 | $FeedDialog.modal('hide'); |
374 | Notification.show('#Notif_FeedDialog_Fetched'); | |
374 | PopupNotification.show('#Notif_FeedDialog_Fetched'); | |
375 | 375 | } |
376 | 376 | } |
377 | 377 |
259 | 259 | var checkedCount = $HistoryTable.fasttable('checkedCount'); |
260 | 260 | if (checkedCount === 0) |
261 | 261 | { |
262 | Notification.show('#Notif_History_Select'); | |
262 | PopupNotification.show('#Notif_History_Select'); | |
263 | 263 | return; |
264 | 264 | } |
265 | 265 | |
275 | 275 | hasNzb |= hist.Kind === 'NZB'; |
276 | 276 | hasUrl |= hist.Kind === 'URL'; |
277 | 277 | hasDup |= hist.Kind === 'DUP'; |
278 | hasFailed |= hist.ParStatus === 'FAILURE' || hist.UnpackStatus === 'FAILURE'; | |
278 | hasFailed |= hist.ParStatus === 'FAILURE' || hist.UnpackStatus === 'FAILURE' || | |
279 | hist.DeleteStatus != 'NONE'; | |
279 | 280 | } |
280 | 281 | } |
281 | 282 | |
289 | 290 | case 'REPROCESS': |
290 | 291 | if (hasUrl || hasDup) |
291 | 292 | { |
292 | Notification.show('#Notif_History_CantReprocess'); | |
293 | PopupNotification.show('#Notif_History_CantReprocess'); | |
293 | 294 | return; |
294 | 295 | } |
295 | 296 | notification = '#Notif_History_Reprocess'; |
299 | 300 | case 'REDOWNLOAD': |
300 | 301 | if (hasDup) |
301 | 302 | { |
302 | Notification.show('#Notif_History_CantRedownload'); | |
303 | PopupNotification.show('#Notif_History_CantRedownload'); | |
303 | 304 | return; |
304 | 305 | } |
305 | 306 | notification = '#Notif_History_Returned'; |
314 | 315 | case 'MARKBAD': |
315 | 316 | if (hasUrl) |
316 | 317 | { |
317 | Notification.show('#Notif_History_CantMark'); | |
318 | PopupNotification.show('#Notif_History_CantMark'); | |
318 | 319 | return; |
319 | 320 | } |
320 | 321 | notification = '#Notif_History_Marked'; |
358 | 359 | Refresher.update(); |
359 | 360 | if (notification) |
360 | 361 | { |
361 | Notification.show(notification); | |
362 | PopupNotification.show(notification); | |
362 | 363 | notification = null; |
363 | 364 | } |
364 | 365 | } |
496 | 497 | this.deleteConfirm = function(actionCallback, hasNzb, hasDup, hasFailed, multi, selCount) |
497 | 498 | { |
498 | 499 | var dupeCheck = Options.option('DupeCheck') === 'yes'; |
499 | var cleanupDisk = Options.option('DeleteCleanupDisk') === 'yes'; | |
500 | 500 | var dialog = null; |
501 | 501 | |
502 | 502 | function init(_dialog) |
506 | 506 | $('#HistoryDeleteConfirmDialog_Hide', dialog).prop('checked', true); |
507 | 507 | Util.show($('#HistoryDeleteConfirmDialog_Options', dialog), hasNzb && dupeCheck); |
508 | 508 | Util.show($('#HistoryDeleteConfirmDialog_Simple', dialog), !(hasNzb && dupeCheck)); |
509 | Util.show($('#HistoryDeleteConfirmDialog_DeleteWillCleanup', dialog), hasNzb && hasFailed && cleanupDisk); | |
510 | Util.show($('#HistoryDeleteConfirmDialog_DeleteCanCleanup', dialog), hasNzb && hasFailed && !cleanupDisk); | |
509 | Util.show($('#HistoryDeleteConfirmDialog_DeleteWillCleanup', dialog), hasNzb && hasFailed); | |
511 | 510 | Util.show($('#HistoryDeleteConfirmDialog_DeleteNoCleanup', dialog), !(hasNzb && hasFailed)); |
512 | 511 | Util.show($('#HistoryDeleteConfirmDialog_DupAlert', dialog), !hasNzb && dupeCheck && hasDup); |
513 | 512 | Util.show('#ConfirmDialog_Help', hasNzb && dupeCheck); |
2042 | 2042 | Selected records will be deleted from history. All files remain on disk. |
2043 | 2043 | </p> |
2044 | 2044 | |
2045 | <p id="HistoryDeleteConfirmDialog_DeleteCanCleanup" class="confirm-help-block"> | |
2046 | Selected records will be deleted from history. All files remain on disk (for failed downloads this | |
2047 | behavior can be changed via option <strong><em>DeleteCleanupDisk</em></strong>). | |
2048 | </p> | |
2049 | ||
2050 | 2045 | <p id="HistoryDeleteConfirmDialog_DeleteWillCleanup" class="confirm-help-block"> |
2051 | Selected records will be deleted from history. For failed downloads (par-failure or unpack-failure) | |
2052 | all downloaded files will be deleted (this behavior can be changed via option <strong><em>DeleteCleanupDisk</em></strong>). | |
2046 | Selected records will be deleted from history. For failed downloads | |
2047 | all downloaded files will be deleted. | |
2053 | 2048 | </p> |
2054 | 2049 | |
2055 | 2050 | <p id="HistoryDeleteConfirmDialog_DupAlert" class="alert alert-warning" style="margin-top: 10px;"> |
811 | 811 | function TODO(text) |
812 | 812 | { |
813 | 813 | $('#Notif_NotImplemented_Param').html(text === undefined ? '' : ': ' + text); |
814 | Notification.show('#Notif_NotImplemented'); | |
814 | PopupNotification.show('#Notif_NotImplemented'); | |
815 | 815 | } |
816 | 816 | |
817 | 817 | |
911 | 911 | |
912 | 912 | /*** NOTIFICATIONS *********************************************************/ |
913 | 913 | |
914 | var Notification = (new function($) | |
914 | var PopupNotification = (new function($) | |
915 | 915 | { |
916 | 916 | 'use strict'; |
917 | 917 |
303 | 303 | Refresher.update(); |
304 | 304 | if (notification) |
305 | 305 | { |
306 | Notification.show(notification); | |
306 | PopupNotification.show(notification); | |
307 | 307 | notification = null; |
308 | 308 | } |
309 | 309 | } |
207 | 207 | showBtn.fadeIn(500); |
208 | 208 | if (!Play && !status.ServerStandBy) |
209 | 209 | { |
210 | Notification.show('#Notif_Downloads_Pausing'); | |
210 | PopupNotification.show('#Notif_Downloads_Pausing'); | |
211 | 211 | } |
212 | 212 | } |
213 | 213 | else |
257 | 257 | |
258 | 258 | this.playClick = function() |
259 | 259 | { |
260 | //Notification.show('#Notif_Play'); | |
260 | //PopupNotification.show('#Notif_Play'); | |
261 | 261 | |
262 | 262 | if (lastPlayState) |
263 | 263 | { |
414 | 414 | if (title.indexOf(name) > -1) |
415 | 415 | { |
416 | 416 | var value = titleGen[name](); |
417 | console.log(name + '=' + value); | |
418 | 417 | title = title.replace(name, value); |
419 | 418 | } |
420 | 419 | } |
1272 | 1271 | var year = parseInt(period); |
1273 | 1272 | if (year < 2013 || year > 2050) |
1274 | 1273 | { |
1275 | Notification.show('#Notif_StatRangeError'); | |
1274 | PopupNotification.show('#Notif_StatRangeError'); | |
1276 | 1275 | return; |
1277 | 1276 | } |
1278 | 1277 | period = '' + year; |
1283 | 1282 | var year = parseInt(period.substring(0, 4)); |
1284 | 1283 | if (year < 2013 || year > 2050 || month < 1 || month > 12) |
1285 | 1284 | { |
1286 | Notification.show('#Notif_StatRangeError'); | |
1285 | PopupNotification.show('#Notif_StatRangeError'); | |
1287 | 1286 | return; |
1288 | 1287 | } |
1289 | 1288 | period = year + '-' + (month-1); |
1304 | 1303 | { |
1305 | 1304 | RPC.call('resetservervolume', [curServer === 0 ? -1 : curServer, 'CUSTOM'], function() |
1306 | 1305 | { |
1307 | Notification.show('#Notif_StatReset'); | |
1306 | PopupNotification.show('#Notif_StatReset'); | |
1308 | 1307 | Refresher.update(); |
1309 | 1308 | }); |
1310 | 1309 | } |
1475 | 1474 | $LimitDialog.modal('hide'); |
1476 | 1475 | if (changed) |
1477 | 1476 | { |
1478 | Notification.show('#Notif_SetSpeedLimit'); | |
1477 | PopupNotification.show('#Notif_SetSpeedLimit'); | |
1479 | 1478 | } |
1480 | 1479 | Refresher.update(); |
1481 | 1480 | } |
1598 | 1597 | { |
1599 | 1598 | if ($Table_filter.val() === '') |
1600 | 1599 | { |
1601 | Notification.show('#Notif_SaveFilterEmpty'); | |
1600 | PopupNotification.show('#Notif_SaveFilterEmpty'); | |
1602 | 1601 | return; |
1603 | 1602 | } |
1604 | 1603 |
1297 | 1297 | filter: alpha(opacity=0); |
1298 | 1298 | } |
1299 | 1299 | |
1300 | /* BEGIN: Notification alerts */ | |
1300 | /* BEGIN: PopupNotification alerts */ | |
1301 | 1301 | .alert-inverse { |
1302 | 1302 | color: #ffffff; |
1303 | 1303 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); |
1338 | 1338 | .alert-info.alert-center { |
1339 | 1339 | border-color: #3a87ad; |
1340 | 1340 | } |
1341 | /* END: Notification alerts */ | |
1341 | /* END: PopupNotification alerts */ | |
1342 | 1342 | |
1343 | 1343 | .confirm-help-block { |
1344 | 1344 | color: #555555; |
447 | 447 | $AddDialog.modal('hide'); |
448 | 448 | if (index > 0) |
449 | 449 | { |
450 | Notification.show('#Notif_AddFiles'); | |
450 | PopupNotification.show('#Notif_AddFiles'); | |
451 | 451 | } |
452 | 452 | } |
453 | 453 | } |
464 | 464 | { |
465 | 465 | needRefresh = true; |
466 | 466 | $AddDialog.modal('hide'); |
467 | Notification.show('#Notif_Scan'); | |
467 | PopupNotification.show('#Notif_Scan'); | |
468 | 468 | }); |
469 | 469 | } |
470 | 470 | }(jQuery)); |
102 | 102 | SetOutPath "$INSTDIR" |
103 | 103 | |
104 | 104 | ; Stop NZBGet (if running) |
105 | ReadRegStr $R1 HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\NZBGet" "InstallLocation" | |
106 | ${If} $R1 != "" | |
107 | ${AndIf} ${FileExists} "$R1\nzbget.exe" | |
108 | Delete "$R1\nzbget.exe" | |
109 | ExecWait '"$R1\nzbget.exe" -Q' $R2 | |
105 | ${If} ${FileExists} "$INSTDIR\nzbget.exe" | |
106 | Delete "$INSTDIR\nzbget.exe" | |
107 | ExecWait '"$INSTDIR\nzbget.exe" -Q' $R2 | |
110 | 108 | DetailPrint "Stopping NZBGet..." |
111 | 109 | |
112 | 110 | try_delete: |
113 | 111 | ; Wait up to 10 seconds until stopped |
114 | 112 | StrCpy $R2 20 |
115 | ${While} ${FileExists} "$R1\nzbget.exe" | |
113 | ${While} ${FileExists} "$INSTDIR\nzbget.exe" | |
116 | 114 | ${If} $R2 = 0 |
117 | 115 | ${Break} |
118 | 116 | ${EndIf} |
119 | 117 | Sleep 500 |
120 | 118 | IntOp $R2 $R2 - 1 |
121 | Delete "$R1\nzbget.exe" | |
119 | Delete "$INSTDIR\nzbget.exe" | |
122 | 120 | ${EndWhile} |
123 | 121 | |
124 | ${If} ${FileExists} "$R1\nzbget.exe" | |
122 | ${If} ${FileExists} "$INSTDIR\nzbget.exe" | |
125 | 123 | MessageBox MB_RETRYCANCEL "NZBGet seems to be running right now. Please stop NZBGet and try again." \ |
126 | 124 | IDRETRY try_delete IDCANCEL cancel |
127 | 125 | cancel: |