Compare commits

...
472 Commits
Author SHA1 Message Date
Safihre 10609644c8 Set version to 5.1.2 2026-08-25 16:23:34 +02:00
Safihre b53a60f92d Merge branch '5.1.x' 2026-08-25 16:23:17 +02:00
Safihre 919bcbf5a3 Update text files for 5.1.2 2026-08-25 16:21:59 +02:00
Safihre 6525703a94 Restrict mode-based API key logic to actual API routes
The `mode` parameter in API calls enables specific behaviors and key bypasses (e.g., 'version', 'auth', NZB-key). Previously, this logic was applied universally by `check_apikey`. This change ensures such `mode`-based logic is only honored when accessing the dedicated `/api` endpoint, preventing potential API key bypasses on other web UI routes.
2026-08-25 13:24:45 +02:00
Safihre a0e2408933 Sanitize PP-values 2026-08-25 13:24:37 +02:00
mnightingale 79e37e6b73 Fix applying changes to RSS filters (#3582) 2026-08-24 14:35:35 +02:00
Safihre c57af32131 Harden paths that could overwrite __ADMIN__ files
Prevent directory creation in `renamer` to overwrite `__ADMIN__`
Check the whole new path for `__ADMIN__` in `renamer`
Drop `__ADMIN__` from names that are allowed to have sub-directories
Resolve the path before checking it against `__ADMIN__`
2026-08-24 10:41:42 +02:00
Safihre 6ddabb5a45 Prevent directory traversal in Deobfuscate 2026-08-24 09:04:36 +02:00
Safihre ab90c4c36a Set version 5.1.1 2026-08-18 17:29:12 +02:00
Safihre 57abc3215c Merge branch '5.1.x' 2026-08-18 17:28:58 +02:00
Safihre 8848e5b2fb Update text files for 5.1.1 2026-08-18 17:28:04 +02:00
Safihre 9a12300877 Logout should not send actual cookies
And increase secret enthropy
2026-08-18 17:28:03 +02:00
mnightingale 31e052796d Fix rss "Clear Downloaded" did not appear to do anything (#3569) 2026-08-18 12:31:00 +02:00
mnightingale 9ae6b71d2b Fix rss clearing at startup and downloaded items still in the feed (#3576)
* Only purge unconfigured rss feeds at startup

* Only perform remove obsolete actions if the feed was read

* Update seen_at so downloaded items are not removed while still in the feed

* Increase hardcoded retention to 7 days
2026-08-18 11:47:13 +02:00
Safihre 944816e765 Add static cache busting for Config Auto colorscheme 2026-08-18 08:49:55 +02:00
Safihre 3e75a76602 Add static cache busting for Auto colorscheme
Yes ugly, but it works for major updates
2026-08-18 08:49:13 +02:00
Safihre ab16e1359c Set version to 5.1.0 2026-08-10 11:58:24 +01:00
Safihre 442522c2ff Merge branch '5.1.x' 2026-08-10 11:57:24 +01:00
Safihre 54e868dd72 Update text files for 5.1.0 2026-08-10 09:36:48 +02:00
Safihre 128e0d03d7 Update version for 5.0.4 2026-06-11 10:29:35 +02:00
Safihre d6ccb620be Merge branch '5.0.x' 2026-06-11 10:29:12 +02:00
Safihre a498a98336 Update text files for 5.0.4 2026-06-11 10:28:54 +02:00
Safihre e39d1e323a Update text files for 5.0.4RC1 2026-06-07 12:46:52 +02:00
Safihre 091b9cfafd Update macOS build Python to 3.14.5 2026-06-07 12:43:37 +02:00
mnightingale 08aaca09df Add guards to blocking connections to prevent them processing the queue (#3444) 2026-06-07 12:36:50 +02:00
Ryan HollisterandClaude Sonnet 4.6 f99fb0202f Fix RuntimeError: dictionary changed size during iteration in stop_idle_jobs (#3432)
* Fix RuntimeError from dict mutation in stop_idle_jobs (#3431)

Collecting exhausted articles into a snapshot list while holding
nzf.lock, then calling register_article outside the iteration.
This prevents RuntimeError when register_article -> nzf.remove_article
pops from nzf.articles (a dict) while stop_idle_jobs is iterating it.

The original code in fd3ece31c used `nzf.articles[:]` which was safe
when articles was a list. When 44d94226e changed articles to a dict
the protective copy was dropped, leaving bare dict iteration that
mutates mid-loop.

The collect-then-act pattern matches nzf_remove_list in nzb/object.py
and the empty-nzo list already used in stop_idle_jobs itself. It also
correctly calls register_article outside nzf.lock, consistent with its
own "not locked for performance" contract.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Apply Black formatting to nzbqueue.py

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Soften comment per maintainer feedback

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 12:36:29 +02:00
mnightingale 5636283f31 Match previous RSS parsing behaviour (#3425)
* Match to previous RSS behaviour

* Cleanup logic due to normalisation

* Tests comparing 4.5.5 outcome

* Remove comment - fixed since 4.5.5

* Consistent types for prio, rule, season, and episode

* Rename matching_rule_index

* Fix type warnings
2026-06-07 12:36:02 +02:00
Safihre 0ccb23b9d3 Refactor RSS processing for clarity and option precedence
Refactor RSS feed processing logic to enhance clarity and ensure correct precedence for resolved options. Renames functions and variables for better understanding, and inlines feed configuration logic. Adds new tests to verify how category, post-processing, script, and priority settings are applied hierarchically. Also provides a default display for empty RSS log categories.
2026-06-07 12:35:52 +02:00
Safihre ee9a6c0b6c Front-end would show job with invalid date as URL-fetching
Closes #3418
2026-06-07 12:33:32 +02:00
Safihre e3308e61b2 Replace unreliable directory access check
The `os.access()` function can produce false negatives for directory writability, particularly in environments like NFS with UID mapping or root-squashing.

This change replaces the `os.access()` call with a direct I/O test (creating and deleting a temporary file) to ensure accurate and reliable verification of directory permissions.
2026-06-07 12:33:16 +02:00
Safihre 53e6ae479d Use reduced progress bar width in compact layout
Apply the table-header-progress-smaller class (25% width) to both queue and
history when compact layout is enabled, matching the behavior when extra columns
are shown. This provides more horizontal space for longer NZB titles.

Fixes #3414
2026-06-07 12:33:03 +02:00
Safihre 7549f23956 Update version for 5.0.3 2026-05-15 16:22:13 +02:00
Safihre 2f92c2b3e9 Merge branch '5.0.x' 2026-05-15 16:21:36 +02:00
Safihre a2b3ed3de1 Update text files for 5.0.3 2026-05-15 16:19:48 +02:00
Safihre fb8b181205 URLGrabber would crash on new installs
Closes #3417
2026-05-15 14:40:28 +02:00
Safihre bc73f7de05 Set version for 5.0.2 2026-05-14 20:17:24 +02:00
Safihre 1355f40a60 Merge branch '5.0.x' 2026-05-14 20:16:56 +02:00
Safihre 45873d4cbb Update text files for 5.0.2 2026-05-14 20:16:11 +02:00
Safihre f32847229a Apply fixes found by ruff 2026-05-14 20:11:26 +02:00
Safihre 72630c30ae Update text files for 5.0.2RC1 2026-05-08 11:08:06 +02:00
Safihre 990dce2232 Merge branch 'develop' into 5.0.x 2026-05-08 10:02:38 +02:00
Safihre c56f974ee4 Set version for 5.0.1 2026-05-01 22:38:41 +02:00
Safihre 6d8fd5853a Merge branch '5.0.x' 2026-05-01 22:38:19 +02:00
Safihre 99447d7a62 Update text files for 5.0.1 2026-05-01 22:37:30 +02:00
Safihre 6455a44ed4 Add compatibility for pre-5.0 queue files
Closes #3388
2026-05-01 22:31:01 +02:00
Safihre cb05869e0c Adjust README formatting so highlights display as list 2026-05-01 10:41:42 +02:00
Safihre 56ced93868 Set version for 5.0.0 2026-05-01 09:58:38 +02:00
Safihre 50dad36028 Merge branch '5.0.x' 2026-05-01 09:58:07 +02:00
Safihre 936b2db7df Update text files for 5.0.0 2026-05-01 09:56:02 +02:00
Safihre 11ba9ae12a Set version to 4.5.5 2025-10-24 12:58:51 +02:00
Safihre a61a5539a7 Merge branch '4.5.x' 2025-10-24 12:58:35 +02:00
Safihre 77f7490aea Update text files for 4.5.5 2025-10-24 12:56:26 +02:00
Safihre a7198b6a81 Merge branch 'develop' into 4.5.x 2025-10-24 12:47:36 +02:00
Safihre 977dbc805f Set version to 4.5.4 2025-10-22 23:09:30 +02:00
Safihre abcca19820 Merge branch '4.5.x' 2025-10-22 23:09:14 +02:00
Safihre 52a7b5dcff Update text files for 4.5.4 2025-10-22 23:05:40 +02:00
Safihre 9518714885 Merge branch 'develop' into 4.5.x 2025-10-22 22:35:07 +02:00
Safihre 4a89fcf8ea Update text files for 4.5.4RC1 2025-10-13 16:25:48 +02:00
Safihre d11e757c6e Merge branch 'develop' into 4.5.x 2025-10-13 16:24:19 +02:00
Safihre 4f9ed7803f Update text files for 4.5.4Beta2 2025-10-05 23:08:14 +02:00
Safihre 95bc069af9 Merge branch 'develop' into 4.5.x 2025-10-05 22:58:51 +02:00
Safihre d1d9bab65a Update text files for 4.5.4 Beta 1 2025-09-22 14:11:13 +02:00
Safihre e2560bf214 Merge branch 'develop' into 4.5.x 2025-09-22 14:10:50 +02:00
Safihre 825322baa4 Set version to 4.5.3 2025-08-25 15:25:56 +02:00
Safihre 7a5ca5b226 Merge branch '4.5.x' 2025-08-25 15:25:41 +02:00
Safihre cb4f022d17 Update text files for 4.5.3 2025-08-25 15:25:21 +02:00
Safihre 913e4ea02e Update text files for 4.5.3RC1 2025-08-19 16:34:20 +02:00
Safihre aa0d44a60b Merge branch 'develop' into 4.5.x 2025-08-19 16:33:20 +02:00
Safihre 8b5f29df8f Update text files for 4.5.3Beta2 2025-08-11 12:10:29 +02:00
Safihre 82954f5930 Merge branch 'develop' into 4.5.x 2025-08-11 12:02:33 +02:00
Safihre 67f1858315 Update text files for 4.5.3 Beta 1 2025-08-05 11:33:53 +02:00
Safihre 55bb81ceef Merge branch 'develop' into 4.5.x 2025-08-05 11:33:00 +02:00
Safihre 6864810ace Set version to 4.5.2 2025-07-09 20:22:44 +02:00
Safihre bae55636a8 Merge branch '4.5.x' 2025-07-09 20:22:27 +02:00
Safihre f4778abd1f Update text files for 4.5.2 2025-07-09 20:22:04 +02:00
Safihre 2cb716ce26 Update text files for 4.5.2RC2 2025-06-27 10:53:56 +02:00
Safihre 3246e9c6d4 Merge branch 'develop' into 4.5.x 2025-06-27 10:52:13 +02:00
Safihre ae6d5f54bd Update text files for 4.5.2RC1 2025-06-17 13:51:24 +02:00
Safihre bd95c29866 Merge branch 'develop' into 4.5.x 2025-06-17 13:34:39 +02:00
thezoggy 074eed16e1 fixup prowl/pushover pri setting due to value returned as int not string now (#3070) 2025-04-17 21:52:32 +03:00
Safihre 3b72a005fd Set version to 4.5.1 2025-04-11 11:48:24 +02:00
Safihre afb9a4758f Merge branch '4.5.x' 2025-04-11 11:47:59 +02:00
Safihre 35c180216b Update text files for 4.5.1 2025-04-11 11:47:13 +02:00
jcfp f0c6fe5786 fix root dir check in make_mo (#3061) 2025-04-11 11:44:37 +02:00
Safihre bd8c245b83 Update text files for 4.5.1RC1
Get Unrar 7.11 to users due to the CVE
2025-04-04 14:19:58 +02:00
Safihre effc7265d4 Merge branch 'develop' into 4.5.x 2025-04-04 14:08:13 +02:00
Safihre bd9a8e5c33 Set version to 4.5.0 2025-03-30 18:09:51 +02:00
Safihre c55d662e1f Merge branch '4.5.x' 2025-03-30 18:07:42 +02:00
Safihre dbfabc1d80 Update text files for 4.5.0 2025-03-30 18:06:29 +02:00
Safihre d897936da5 Set version to 4.4.1 2024-12-20 14:10:49 +01:00
Safihre f81a8c97c4 Merge branch '4.4.x' 2024-12-20 11:16:30 +01:00
Safihre e93e01dd59 Update text files for 4.4.1 2024-12-20 11:16:01 +01:00
Safihre 79b504ff93 Update text files for 4.4.1RC1 2024-12-18 13:17:47 +01:00
Safihre 52dafd4ab8 Add new Certificate verification setting Medium
Closes #2993
2024-12-18 12:53:59 +01:00
Safihre 0cc538ac5a Don't block virusscanner certificates on Windows
See #2993
2024-12-14 21:52:47 +01:00
Safihre 4b99d04454 Add missing hidden import for Windows notifications 2024-12-13 15:00:19 +01:00
Safihre 708fad33f3 Set version to 4.4.0 2024-12-09 21:56:46 +01:00
Safihre c6dc25c9c2 Merge branch '4.4.x' 2024-12-09 21:56:29 +01:00
Safihre 07be38cd01 Update text files for 4.4.0 2024-12-09 21:55:33 +01:00
Safihre 0121e0ae16 Update release date of 4.4.0 in appdata 2024-12-09 21:50:07 +01:00
Safihre f24b3ced28 Set version to 4.3.3 2024-08-21 13:26:46 +02:00
Safihre 157dfc928d Merge branch '4.3.x' 2024-08-21 13:26:14 +02:00
Safihre d10639542d Update text files for 4.3.3 2024-08-21 13:25:48 +02:00
Safihre c0f0b7eb31 Add Docker to CPU label 2024-08-19 20:22:29 +02:00
Safihre d6d70325db Small style fixes for the Config 2024-08-19 20:22:23 +02:00
Safihre 46954165d2 Update text files for 4.3.3RC2 2024-08-17 15:08:57 +02:00
Safihre 58e7d520bf increase_bad_articles_counter should be called before register_article 2024-08-17 15:08:03 +02:00
Safihre a4f8040324 Prevent excessive newswrapper data buffer size
Closes #2895
2024-08-17 15:07:57 +02:00
Safihre 8d5cc9a3e6 Do not use article_queue when resetting newswrapper
Relates to #2866
2024-08-17 15:07:52 +02:00
jcfp 4592ce4d55 Remove pyfakefs workarounds and put its new apply_umask option to good use (#2922) 2024-08-17 15:07:47 +02:00
Safihre b62b38b5af Update text files for 4.3.3RC1 2024-08-13 10:29:25 +02:00
renovate[bot] 14b1d4630c Update all dependencies 2024-08-13 10:16:28 +02:00
Safihre 8a42abd1e7 Set version to 4.3.2 2024-05-29 16:38:23 +02:00
Safihre 41e5dfdf18 Merge branch '4.3.x' 2024-05-29 16:28:43 +02:00
Safihre 41de13388c Merge branch '4.3.x' 2024-05-04 09:48:35 +02:00
Safihre 1f16f13169 Set version to 4.3.1 2024-05-03 15:30:36 +02:00
Safihre ef23d40972 Merge branch '4.3.x' 2024-05-03 15:30:17 +02:00
Safihre b07b43496c Set version to 4.3.0 2024-05-01 21:45:22 +02:00
Safihre 2ba04f1a6a Merge branch '4.3.x' 2024-05-01 21:44:38 +02:00
Safihre e7e06dea41 Update text files for 4.2.3 2024-03-11 08:42:25 +01:00
Safihre ce32504a81 Set version to 4.2.3 2024-03-11 08:25:26 +01:00
Safihre 7cd6c94482 Merge branch '4.2.x' 2024-03-11 08:24:46 +01:00
Safihre fcb3d01194 Correct input placeholder styling in Night themes 2024-03-04 09:57:51 +01:00
Safihre af0b53990c Update text files for 4.2.3RC3 2024-03-04 08:46:42 +01:00
Safihre e3861954ba Support NNTP code 220 after ARTICLE request
Closes #2817
2024-03-02 21:40:56 +01:00
Safihre 006dd8dc77 Do not log NNTP data in unknown status code warning
See #2817
2024-03-02 21:23:25 +01:00
Safihre dbff203c62 Update text files for 4.2.3RC2 2024-02-26 22:30:38 +01:00
Safihre f45eb891cd Correct translatable texts about connection threads 2024-02-26 22:23:51 +01:00
Safihre 77b58240cf Reduce Server test timeout to 10s
Unless otherwise specified
Related to #2802
2024-02-26 22:23:51 +01:00
Safihre 97ae1ff10e Refactor part of database.py
Use autocommit and skip unnecessary checks on every connect
2024-02-26 22:23:51 +01:00
Safihre 8734a4f24b Update text files for 4.2.3RC1 2024-02-19 22:19:43 +01:00
Shane Mc Cormack 480fce55a8 Handle NNTP error code 451 (#2808)
* Handle error code 451.

This is used by some servers to show that an article was intentionally removed.
Fix #2807

* Add a warning when an unknown status code is given for an article.

* Make warning message translatable.
2024-02-19 22:06:29 +01:00
thezoggyandSafihre d4136fadd2 Add missing tooltips (#2800)
Co-authored-by: Safihre <safihre@sabnzbd.org>
2024-02-18 13:42:48 +01:00
Safihre 308bc375bd Update log message about version check 2024-02-18 13:42:31 +01:00
Safihre 3bbcf6a41e Update standby command on macOS 2024-02-18 13:41:33 +01:00
Safihre 3d5d10a4c1 Remove parsing of Group command code
Since we never request it.
2024-02-18 13:41:05 +01:00
Safihre 0e979c14f0 Remove Send Group option
Closes #2715
2024-02-18 13:38:31 +01:00
Safihre 70f49114ac Wrong archive password is used for Retry
Closes #2790
2024-02-18 13:38:13 +01:00
Safihre f730607414 Set version to 4.2.2 2024-01-30 21:48:15 +01:00
Safihre 0172ee25c9 Merge branch '4.2.x' 2024-01-30 21:47:59 +01:00
Safihre 699d75bb9f Update text files for 4.2.2 2024-01-30 21:46:56 +01:00
Safihre 95822704c8 Update black formatting 2024-01-30 21:36:34 +01:00
Safihre 76e5f69e67 Only attempt Windows Toasts on Windows 10 and above 2024-01-29 16:34:03 +01:00
Safihre abd31d0249 Update text files for 4.2.2RC2 2024-01-24 13:28:19 +01:00
Safihre 9ae7ee6e2d Add logging which notification will be sent 2024-01-22 15:15:44 +01:00
Safihre 18f4cc25f3 Add 4.2.1 to appdata 2024-01-05 10:11:07 +01:00
Safihre b755192600 Update version to 4.2.1 2024-01-05 09:54:19 +01:00
Safihre 045140cfbc Merge branch '4.2.x' 2024-01-05 09:54:04 +01:00
Safihre 4e7e44e25f Set version to 4.2.0 2024-01-03 14:17:07 +01:00
Safihre 5c4dfa4cc6 Merge branch '4.2.x' 2024-01-03 14:14:25 +01:00
Safihre b7e3401e8e Set version to 4.1.0 2023-09-26 15:12:39 +02:00
Safihre 90cee7fb31 Merge branch '4.1.x' 2023-09-26 15:11:58 +02:00
Safihre 8e0e3cf35e Update appdata.xml for 4.1.0 2023-09-26 15:07:08 +02:00
Safihre 7f72584537 Update text files for 4.1.0 2023-09-26 14:28:05 +02:00
renovate[bot] 8f0d606892 Update dependency cryptography to v41.0.4 [SECURITY] 2023-09-26 14:15:15 +02:00
Safihre 9fafe64cff Update version to 4.0.3 2023-07-10 14:55:46 +02:00
Safihre 94b42e0597 Merge branch '4.0.x' 2023-07-10 14:55:16 +02:00
Safihre b2c1960d93 Release notes were not present in releases 2023-07-10 14:54:34 +02:00
Safihre 9d24b4cc35 Correct finding of release in appdata 2023-07-10 14:19:56 +02:00
Safihre 3d675b033c Update text files for 4.0.3 2023-07-10 13:43:06 +02:00
Sanderandsander 0d2d9be8b3 better docker detections: works for older and newer docker versions (#2606)
* better docker detections: works for Ubuntu 18.04 and 22.04

* DOCKER = False, needed for non-POSIX

---------

Co-authored-by: sander <san.d.erjonkers+github@gmail.com>
2023-07-03 16:11:25 +02:00
Safihre 6e9b6dab97 add a grace period for expected filenames to show up (#2609) 2023-07-03 16:11:04 +02:00
Michael Nightingale 44a1717f6d Fix uu decoding when collapsing of lines starting with a doubled period is required (#2605) 2023-06-28 10:01:15 +02:00
Safihre 4f51c74297 Build binary using Python 3.11.4 2023-06-28 10:01:01 +02:00
thezoggy 87c64a8c5d add random import back to fixup (#2604) 2023-06-27 09:06:25 +02:00
Safihre b6c6635f22 Add newline after link to Downloads page in Reddit post 2023-06-23 21:45:36 +02:00
Safihre 5a7abcb07c Update text files for 4.0.3RC1 2023-06-23 14:03:26 +02:00
jcfp 65232d134b Fix sorting for #2551 (#2598)
* fix #2551

* add test data dirs

* move sorting test data into subdir

* undo change to sabnews.create_nzb
2023-06-23 13:52:26 +02:00
Safihre d7b4bdefe5 Check if version is present appdata before releasing 2023-06-23 13:52:12 +02:00
Safihre 6d9174bea1 Additional logging to debug Direct Unpack 2023-06-23 13:52:07 +02:00
François M 921edfd4c5 Add versions to appdata (#2595) 2023-06-23 13:52:00 +02:00
Safihre 786d5b0667 Lock add/remove_socket in Downloader
See if we can resolve #2591
2023-06-23 13:51:49 +02:00
Safihre e846c71f20 Link to Downloads page was not included in Reddit post 2023-06-16 11:48:52 +02:00
Safihre 0108e2ef5a Update text files for 4.0.3Beta1 2023-06-16 10:53:58 +02:00
Safihre 9a81277ff6 No longer * import AppKit and Foundation 2023-06-16 09:07:29 +02:00
Safihre 06cc2ff316 Update release script to post directly to r/usenet and include link 2023-06-13 14:06:38 +02:00
renovate[bot] 7cdf4cb48c Update all dependencies 2023-06-13 14:06:31 +02:00
thezoggy c34c547f1f Unable to modify Sorters (#2587) 2023-06-13 14:06:22 +02:00
Safihre 9507294db7 Move DirScanner Lock creation 2023-06-13 14:06:12 +02:00
Safihre ae7dd62d9f Only initialize DirScanner Lock after starting event loop 2023-06-13 14:06:05 +02:00
renovate[bot] 52e309cb09 Update all dependencies 2023-06-13 14:06:00 +02:00
jcfp b580373982 Fix sorting lowercasing (#2584)
* run lowercasing on season pack setname

* also subject %fn to lowercasing

* add tests

* woops
2023-06-13 14:05:37 +02:00
renovate[bot] ec7bde5bb2 Update dependency cryptography to v41 [SECURITY] (#2583)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2023-06-13 14:05:30 +02:00
Safihre 3516eeec5b Force full refresh after changing items-per-page
Closes #2416
2023-06-13 14:05:24 +02:00
Safihre 52351192e6 Use more reliable marker if job is still active 2023-06-13 14:05:18 +02:00
Safihre 3a6f04496d Set version to 4.0.2 2023-06-07 17:31:29 +02:00
Safihre 47f2df2112 Merge branch '4.0.x' 2023-06-07 17:25:45 +02:00
Safihre 363a26b8a1 Update text files for 4.0.2 2023-06-07 17:24:54 +02:00
Safihre 7e50a00f55 Correct parameter in release script to merge PR of update 2023-05-31 22:06:26 +02:00
Safihre a7d6a80e82 Update text files for 4.0.2RC2 2023-05-31 21:49:02 +02:00
Safihre e7da95b2ac Merge branch 'develop' into 4.0.x 2023-05-31 21:38:48 +02:00
Safihre 74fca23d59 Update text files for 4.0.2RC1 2023-05-23 21:31:43 +02:00
Safihre 0a12fa1253 Merge branch 'develop' into 4.0.x 2023-05-23 21:31:11 +02:00
Safihre 1263068140 Set version to 4.0.1 2023-05-01 21:46:30 +02:00
Safihre 916c191b18 Merge branch '4.0.x' 2023-05-01 21:46:02 +02:00
Safihre d8c0220353 Update text files for 4.0.1 2023-05-01 21:45:27 +02:00
Safihre 4ab425d15c Update appdata for 4.0.1 release 2023-05-01 21:41:48 +02:00
François M 74e5633d1c Add releases tag (#2539)
* Add 3.7.2 release tag

* Add 4.0.0 placeholder
2023-05-01 21:41:37 +02:00
Safihre 89d36bbc61 Update sabctools to 7.0.2 2023-05-01 21:36:32 +02:00
Safihre 1877ac18a5 Show a better crash on Python <3.8 2023-05-01 21:36:23 +02:00
Safihre 5e42e25617 Set version to 4.0.0 2023-04-28 14:56:51 +02:00
Safihre c27c9564cf Merge branch '4.0.x' 2023-04-28 14:53:02 +02:00
Safihre c4b0da335d Update text files for 4.0.0 2023-04-28 14:47:36 +02:00
Safihre fab36ec008 Set version to 3.7.2 2023-02-05 22:17:20 +01:00
Safihre 8a2b875779 Merge branch '3.7.x' 2023-02-05 22:15:54 +01:00
Safihre efaffb8298 Update text files for 3.7.2 2023-02-05 22:15:40 +01:00
jcfp e004eb3f00 restore startup history purge (#2449) 2023-02-05 22:08:12 +01:00
Safihre 43e8f6dc81 Update formatting with black 23 rules 2023-02-05 22:08:07 +01:00
Safihre f5bff8fe7c Update copyright year to 2023 2023-02-05 22:07:42 +01:00
Safihre fad8484b93 Update text files for 3.7.2RC1 2023-01-23 21:49:08 +01:00
Safihre 7664b54f89 Fix orjson to 3.8.3 due to issue in 3.8.4 2023-01-15 13:52:25 +01:00
Safihre 21cbc353dd Update text files for 3.7.2Beta1 2023-01-15 13:46:21 +01:00
Safihre 8d66306ec4 Merge branch 'develop' into 3.7.x 2023-01-15 13:35:21 +01:00
Safihre 479daf0e76 Set version to 3.7.1 2022-12-16 22:04:36 +01:00
Safihre bf0fbb7b10 Merge branch '3.7.x' 2022-12-16 22:03:56 +01:00
Safihre d3c91f1585 Update text files for 3.7.1 2022-12-16 22:02:40 +01:00
Safihre ca165b328a Update text files for 3.7.1RC2 2022-12-06 17:10:08 +01:00
Safihre fa2ffeea92 Merge branch 'develop' into 3.7.x 2022-12-06 17:07:31 +01:00
Safihre 0d00965ac3 Update text files for 3.7.1RC1 2022-12-01 16:33:59 +01:00
Safihre 7d7bec1f80 Merge branch 'develop' into 3.7.x 2022-12-01 16:09:09 +01:00
Safihre b6fd915365 Set version to 3.7.0 2022-11-04 10:36:24 +01:00
Safihre fecae72267 Merge branch '3.7.x' 2022-11-04 10:23:22 +01:00
Safihre 7bffd91e3f Update text files for 3.7.0 2022-11-04 10:18:37 +01:00
Safihre f859521a7e Replace apple-actions/import-codesign-certs 2022-11-04 10:17:28 +01:00
renovate[bot] a869386fac Update dependency cryptography to v38.0.3 [SECURITY] 2022-11-04 09:30:25 +01:00
Safihre 8bc7885b7a Update translatable texts
[skip ci]
2022-11-04 09:29:14 +01:00
Safihre 78be46738d Correct typo of Python 3.11 version 2022-10-27 10:07:01 +02:00
Safihre 6fce73855c Update text files for 3.7.0RC1 2022-10-27 09:08:20 +02:00
Safihre fa844a6223 Update Release Notes with right version 2022-08-18 10:46:08 +02:00
Safihre 906379dd09 Set all versions to 3.6.1 2022-08-18 09:52:31 +02:00
Safihre 37cded612f Set version to 3.6.1 2022-08-18 08:48:09 +02:00
Safihre 73e8fade61 Merge branch '3.6.x' 2022-08-18 08:47:17 +02:00
Safihre 758cc7afab Update text files for 3.6.1RC2 2022-08-12 20:18:11 +02:00
Safihre d74b7b06d2 Update Unrar to 6.11/6.12
Closes #2265
2022-08-12 20:16:34 +02:00
Safihre 39009f2f71 Update text files for 3.6.1RC1 2022-08-03 21:44:29 +02:00
jcfp 9fdc1c6813 use OSType in pyfakefs instead of setting separate properties (#2243) 2022-08-03 21:44:29 +02:00
Safihre c5568fe830 Remove deprecation notices 2022-08-02 22:31:49 +02:00
Safihre bad81f84b9 Update dependencies and Python version 2022-08-02 22:29:58 +02:00
Safihre 2ac08dd0e6 Remove new QuickCheck implementation
See https://github.com/sabnzbd/sabnzbd/discussions/2160
And https://github.com/sabnzbd/sabnzbd/issues/2251
2022-08-02 22:21:12 +02:00
Safihre 408ffc4539 Downloads in Checking-status were not displayed correctly
Closes #2249
2022-07-25 11:02:50 +02:00
Safihre eb958327c5 Prevent scheduler crash if event is canceled that was no longer queued
https://www.reddit.com/r/SABnzbd/comments/vfa1fr/what_is_causing_this_error_its_not_harming/
2022-06-19 20:37:14 +02:00
Safihre e157d77a1e Prevent crash in new Quick-check if file was already moved 2022-06-16 13:54:19 +02:00
Joulinar e961c9ea8f Update sabyenc3 to 5.4.3 (#2209)
* Update requirements.txt

Update requirements sabyenc3 to new version

* Update sabyenc3 to 5.4.3

Update sabyenc3 to 5.4.2
2022-06-15 13:25:27 +02:00
Safihre 258c4f769d Prevent showing crash on Status window during Shutdown 2022-06-14 14:59:06 +02:00
Safihre b31fedd857 Set version to 3.6.0 2022-06-07 12:23:13 +02:00
Safihre eafe69500b Merge branch '3.6.x' 2022-06-07 12:22:36 +02:00
Safihre ae09990c43 Update text files for 3.6.0 2022-06-07 12:20:28 +02:00
Safihre cf54b65c32 Remove enable_https_verification from possible deprecation list 2022-06-07 09:29:09 +02:00
Safihre 7974421fa1 Update text files for 3.6.0RC3 2022-05-29 14:34:48 +02:00
Safihre 847a098d4e Update sabyenc3 to 5.4.2 2022-05-29 14:28:55 +02:00
Safihre eb4de0ae0f Remove helpful warning about new quick-check for release 2022-05-28 20:19:59 +02:00
Safihre bca9f3b753 Set version to 3.5.3 2022-03-17 14:29:50 +01:00
Safihre cad8a9a5d3 Merge branch '3.5.x' 2022-03-17 14:12:55 +01:00
Safihre f5f36d21e8 Update text files 3.5.3 2022-03-17 14:12:40 +01:00
Safihre c51435c114 Revert "Notify users of Prowl/Pushover/Pushbullet to switch to nzb-notify"
This reverts commit 56fe140ebf.
2022-03-15 21:52:16 +01:00
Safihre 2a7f1780b4 Update text files for 3.5.3RC1 2022-03-13 13:26:18 +01:00
Safihre 98a44e40fb Jobs waiting to fetch get stuck indefinitely upon restart
Closes #2114
2022-03-11 16:29:28 +01:00
Safihre 65cf6fa9a1 Prevent Direct Unpack proceeding faster than it should, locking files
Relates to #2113
2022-03-11 16:29:19 +01:00
Safihre b2e32d1720 Log also the OSError.winerror just to be sure 2022-03-11 16:29:11 +01:00
Safihre f0bfedbe8e Revert "Revert "Disable buffering when writing files in assembler""
This reverts commit 03b380f90b.
2022-03-11 16:29:04 +01:00
Safihre fd4e059c13 Set version to 3.5.2 2022-03-09 14:33:48 +01:00
Safihre a53575e154 Merge branch '3.5.x' 2022-03-09 14:24:26 +01:00
Safihre 4a73484603 Update text files for 3.5.2 2022-03-09 14:24:13 +01:00
Safihre 03b380f90b Revert "Disable buffering when writing files in assembler"
This reverts commit 3c3aeac93c.

It turns out this causes problems!
2022-03-03 15:44:15 +01:00
Safihre a2bd3b2dfe RSS filters At most/least were broken 2022-03-01 08:42:31 +01:00
Safihre 56fe140ebf Notify users of Prowl/Pushover/Pushbullet to switch to nzb-notify
Relates to #2093
2022-02-24 16:47:44 +01:00
Safihre 4fafcce740 Set version to 3.5.1 2022-02-20 19:47:55 +01:00
Safihre 02352c4ae6 Merge branch '3.5.x' 2022-02-20 19:37:50 +01:00
Safihre 4b74aab335 Update text files for 3.5.1 2022-02-20 19:36:51 +01:00
Safihre 2d67ac189d Add Debug logging when applying permissions failed 2022-02-16 13:08:12 +01:00
Safihre 8ece62e23d Add small delay to test_download check of result file
To prevent zip-test-failures on Windows.
2022-02-16 12:48:53 +01:00
Safihre 56c2bdd77d Allow chmod failures if no custom permissions are set
This is the same as before 3.5.0!
2022-02-16 11:32:15 +01:00
Safihre 1f555f1930 Update text files for 3.5.1RC2 2022-02-14 09:22:45 +01:00
Wolfgang SchererandSafihre 8496432c14 Do not fail if attribute subject is missing in NZB file-section (#2075)
* Use attribute poster of file element, if attribute subject ist missing

* Don't fail, if subject is missing.

* Textual change

Co-authored-by: Safihre <safihre@sabnzbd.org>
2022-02-14 09:16:29 +01:00
jcfp 1672ffa670 set log level of "completed not on fat" to debug 2022-02-14 09:16:23 +01:00
Safihre 6aab199f12 Wait before removing the temporary directory in binary test 2022-02-09 14:58:17 +01:00
Safihre 46d0c379a4 Fix version of more dependencies 2022-02-09 14:58:12 +01:00
Safihre 99240f145a Restore correct display of Direct Unpack progress 2022-02-07 15:27:54 +01:00
Safihre 3c9079d73c Correctly handle the transition from download to active post-processing 2022-02-07 15:22:58 +01:00
Safihre 0eb98b9a6c Use nzo.deleted instead of nzo.is_gone to prevent assembly during pp
Relates to #2059, #2054 and #1509.
2022-02-07 12:14:00 +01:00
Safihre 76bfd98b77 Prevent Direct Unpack crash on obfuscated posts
Closes #2060
2022-02-07 11:57:55 +01:00
Safihre 3348640c88 Correct undefined function remove_data 2022-02-05 09:51:36 +01:00
Safihre d81c64fd2b Use notarytool on macOS to do notarization 2022-02-04 22:28:46 +01:00
Safihre 8b4c919617 Test starting of binary during release building 2022-02-04 21:53:04 +01:00
Safihre 76c58953df Pin even more requirements
Closes #2056
2022-02-04 10:09:53 +01:00
thezoggy 4ddc5caa49 pin builder setuptools to deal with breakage on pyinstaller 4.8 2022-02-04 09:08:51 +01:00
Safihre 694663bd95 Update text files for 3.5.1RC1 2022-02-03 20:09:38 +01:00
Safihre 62aba5844e Add small delay between volumes in Direct Unpack to prevent UnRar error 2022-01-31 12:23:38 +01:00
Safihre d0d60cef05 Disable buffering when writing files in assembler 2022-01-31 12:23:31 +01:00
Safihre 3d293fdcb0 RSS feeds with HTML-chars in the feed name would result in crash 2022-01-31 12:23:23 +01:00
Safihre 96e9528046 Fix dependencies in requirements.txt and configure dependabot 2022-01-30 10:52:52 +01:00
Safihre 4ea24b3203 Black formatting update 2022-01-30 09:43:31 +01:00
Safihre a756eea25a Set version to 3.5.0 2022-01-28 11:47:15 +01:00
Safihre 210020e489 Merge branch '3.5.x' 2022-01-28 11:42:22 +01:00
Safihre e586ead024 Update text files for 3.5.0 2022-01-28 11:39:21 +01:00
Safihre 14c80bf1dc Reduce par2cmdline output log in Debug mode 2022-01-28 11:35:59 +01:00
Safihre bdd56e794a Prevent extra error when no 7zip is available
Closes #2036, #2035
2022-01-23 13:41:26 +01:00
Safihre a544548934 Set Python for macOS release to 3.10.2 2022-01-21 16:53:29 +01:00
Safihre e06c1d61fb Update text files for 3.5.0RC4 2022-01-18 09:41:26 +01:00
Safihre 600c5209c6 HTML-sanitizer would sanitize the source data
Closes #2026
2022-01-17 14:08:08 +01:00
Safihre bee90366ee Update text files for 3.5.0RC3 2022-01-16 19:02:50 +01:00
Safihre e9bc4e9417 Sort sevenset so x.7z.001 is always the first file 2022-01-15 17:09:21 +01:00
Safihre f01ff15761 Failed 7zip unpack was not reported in the history 2022-01-15 17:05:03 +01:00
Safihre 356ada159d Update text files for 3.5.0RC2 2022-01-13 14:48:30 +01:00
Safihre cc831e16d8 Set version to 3.4.2 2021-10-15 08:28:14 +02:00
Safihre b8dc46ad01 Merge branch '3.4.x' 2021-10-15 08:24:21 +02:00
Safihre d8ab19087d Update text files for 3.4.2 2021-10-15 08:19:00 +02:00
Safihre ec8a79eedd Revert to using regex based sample detection
Closes #1964
2021-10-13 18:24:48 +02:00
Safihre f1e2a8e9d8 Prevent double guessit parsing 2021-10-12 09:02:03 +02:00
Safihre 4042a5fe5d Update text files for 3.4.2RC3 2021-10-08 10:41:27 +02:00
Safihre a4752751ed Fix tavern for Python 3.6 and run tests on Python 3.10 (Linux-only) 2021-10-08 10:34:14 +02:00
Safihre e23ecf46d1 Correct behavior of Sorter when no filename and/or extension is supplied
Closes #1962, #1957
2021-10-08 10:24:44 +02:00
Safihre 70a8c597a6 Only fail jobs if the sorter should have renamed 2021-10-08 09:58:17 +02:00
Safihre fa639bdb53 Use general detection of RAR-files in file-extension correction
Correct file_extension test
2021-10-08 09:58:17 +02:00
Safihre 233bdd5b1d Update text files for 3.4.2RC2 2021-10-06 15:00:44 +02:00
Safihre a0ab6d35c7 Require at least 1 category to be set for Sorting and warn if not set
Before 3.4.0, only for TV sorting we allowed to set 0 categories. But for Movies and Date Sorting we did require at least 1 category to be set. This was harmonized in 3.4.0, breaking existing setups. Added warning for those users.
The Sorting behavior is different from Notifications: in Notifications selecting Default only(!) means to apply it to all categories.
However, that has never been the case for Sorting. So for now added a bit more help texts to the Affected categories box on both pages.
2021-10-06 14:50:00 +02:00
Sander bd29680ce7 make .cbz a well-known extension, so that no extension is added (#1960) 2021-10-06 14:49:54 +02:00
Sander 7139e92554 make .cbr a well-known extension, so that no extension (".rar") is added (#1959) 2021-10-05 12:19:55 +02:00
Safihre 897df53466 Check for puremagic and guessit first and add comments about cherrypy 2021-10-04 08:57:58 +02:00
Safihre 58281711f6 Always show number of MB missing
https://forums.sabnzbd.org/viewtopic.php?f=2&t=25573
2021-10-04 08:57:51 +02:00
Safihre b524383aa3 Job failure due to Sorting-problems was not shown in the interface 2021-10-01 15:35:09 +02:00
Safihre 75a16e3588 Update text files for 3.4.2RC1 2021-09-30 09:04:12 +02:00
Safihre 1453032ad6 rXX files are popular extensions and don't need renames
Closes #1955
2021-09-29 13:53:47 +02:00
Safihre 824ab4afad Do not search whole file when checking if txt or nzb file 2021-09-29 13:53:37 +02:00
Safihre 73dd41c67f Only run process_unpacked_par2 when cleanup happened
Relates to https://forums.sabnzbd.org/viewtopic.php?f=1&t=25552
2021-09-29 13:53:32 +02:00
Safihre 59ee77355d Make add_parfile return if it could actually add the file
Maybe it was long finished, which could result in crashes.
Closes #1953
2021-09-29 13:53:20 +02:00
Safihre 5c758773ad Do not rename in decode_par2 if the filename didn't change
Closes #1952
2021-09-29 13:53:14 +02:00
Safihre 46de49df06 Set version to 3.4.1 2021-09-23 09:21:11 +02:00
Safihre d1c54a9a74 Merge branch 'develop' 2021-09-23 08:50:17 +02:00
Safihre e7527c45cd Set version to 3.4.0 2021-09-17 22:01:19 +02:00
Safihre 7d5207aa67 Merge branch 'develop' 2021-09-17 20:46:31 +02:00
Safihre 654302e691 Set version to 3.3.1 2021-06-18 13:52:58 +02:00
Safihre ee673b57fd Merge branch '3.3.x' 2021-06-18 13:51:48 +02:00
Safihre 2be374b841 Update text files for 3.3.1 2021-06-18 13:51:33 +02:00
puzzledsab 906e1eda89 Keep password order 2021-06-13 16:21:41 +02:00
Safihre ece02cc4fa Automatically publish release when all files are present 2021-06-11 17:39:30 +02:00
Safihre 876ad60ddf Update text files for 3.3.1RC1 2021-06-11 14:59:25 +02:00
Safihre 862da354ac Add direct opening of tabs by URL to Glitter tab-layout 2021-06-11 14:28:31 +02:00
Safihre 8fd477b979 Include wiki URL in Internal internet access denied message 2021-06-05 15:56:20 +02:00
Safihre 2d7005655c Clean timeline_total of BPSMeter
Received multiple reports that somehow it could get corrupt values in there
2021-06-05 15:56:16 +02:00
Safihre 7322f8348a Filtering active post-proc queue by category was broken 2021-06-05 15:56:09 +02:00
Safihre e3e3a12e73 Correct example in test_name_extractor 2021-06-05 15:56:04 +02:00
Safihre 77cdd057a4 Filenames should end after the extension 2021-06-01 11:19:49 +02:00
Safihre e8206fbdd9 Set version to 3.3.0 2021-06-01 07:35:13 +02:00
Jiri van Bergen 589f15a77b Merge branch '3.3.x' 2021-06-01 07:34:59 +02:00
Safihre 7bb443678a Build release when creating the tag 2021-06-01 07:18:41 +02:00
Safihre 6390415101 Update text files for 3.3.0 2021-06-01 07:16:42 +02:00
Sander 4abf192e11 deobfuscate: bugfix for collections if extension in CAPITALS (#1904) 2021-06-01 07:06:21 +02:00
Safihre 1fed37f9da Notify users that Plush will be removed in 3.4.0
Relates to #1902
2021-05-25 09:28:10 +02:00
Safihre a9d86a7447 Set version to 3.2.1 2021-03-31 10:24:42 +02:00
Safihre 2abe4c3cef Merge branch '3.2.x' 2021-03-31 09:25:49 +02:00
Safihre 0542c25003 Update text files for 3.2.1
draft release
2021-03-31 09:24:31 +02:00
puzzledsab 1b8ee4e290 Show server expiration date in server summary (#1841) 2021-03-31 08:57:38 +02:00
Safihre 51128cba55 Do not notify warning/errors from same source twice
Closes #1842
2021-03-30 17:30:07 +02:00
Safihre 3612432581 Do not discard data for CrcError's
https://forums.sabnzbd.org/viewtopic.php?f=11&t=25278
2021-03-30 16:05:04 +02:00
Safihre deca000a1b Revert some improvements to the encrypted RAR-detection
Closes #1840
2021-03-29 14:05:52 +02:00
Safihre 39cccb5653 Update text files for 3.2.1RC2
draft release
2021-03-24 10:13:43 +01:00
Safihre f6838dc985 Improvements to the encrypted RAR-detection 2021-03-20 18:32:11 +01:00
Safihre 8cd4d92395 Make get_all_passwords return only unique passwords
If the filename and the NZB specified the same one it could occur 2 or 3 times.
2021-03-20 18:32:05 +01:00
Safihre 3bf9906f45 Update text files for 3.2.1RC1
draft release
2021-03-18 10:30:05 +01:00
Safihre 9f7daf96ef Update URL for Python 3 information 2021-03-18 09:10:39 +01:00
Sander 67de4df155 deobfuscate: no globber, but use given filelist (#1830) 2021-03-18 09:10:31 +01:00
Safihre bc51a4bd1c Remove old compatibility code from BPSMeter that causes crash on startup
Closes #1827
2021-03-18 09:10:23 +01:00
Sander bb54616018 deobfuscate: rename accompanying (smaller) files with same basename, and no renaming of collections with same extension (#1826)
* deobfuscate: rename accompanying (smaller) files with same basename

* deobfuscate: do not rename collections of same extension

* deobfuscate: collection ... much easier with one loop, thanks safihre.

* deobfuscate: globber_full, and cleanup

* deobfuscate: unittest test_deobfuscate_big_file_small_accompanying_files

* deobfuscate: unittest test_deobfuscate_collection_with_same_extension

* deobfuscate: unittest test_deobfuscate_collection_with_same_extension
2021-03-18 09:10:18 +01:00
Safihre 6bcff5e014 More space for the RSS table
Closes #1824
2021-03-18 09:10:09 +01:00
puzzledsab 8970a03a9a Use binary mode to make write test more accurate on Windows (#1815) 2021-03-10 22:23:10 +01:00
Safihre 3ad717ca35 Single indexer categories would be saved with "," between each letter 2021-03-10 22:23:10 +01:00
jcfp b14f72c67a fix config auto_sort setting, broken by #1666 (#1813)
* fix config auto_sort setting, broken by #1666

* oops I did it again
2021-03-10 22:23:10 +01:00
Safihre 45d036804f Show name of item to be deleted from queue/history in confirm dialog 2021-03-10 22:23:10 +01:00
Safihre 8f606db233 Add traceback when failing to read the password file
Closes #1810
2021-03-10 22:23:10 +01:00
Safihre 3766ba5402 pre-create subdir if needed (POSIX, par2) (#1802)
* pre-create subdir it needed

* pre-create subdir it needed: check if already exists

* use os.makedirs() to handle subdir1/subdir2/blabla

* protect against malicous "..", and better naming

* check for Windows \ and POSIX /

* check again within path, typo and formatting

* regex: square brackets

* cleanup: only "/" can occur in par2

* cleanup: better logging

* unit test: testing of filesystem.renamer()

* if subdir specified in par2: let filesystem.renamer() do all the work

* if create_local_directories=True, then renamer() must stay within specified directory. Plus unittest for that.

* if create_local_directories=True, then renamer() must stay within specified directory. Plus unittest for that.

* more comments in code

* use filesystem.create_all_dirs(), less logging, clearer "..", and other feedback from Safihre

* make remote black happy too

* Small changes in wording of comments and error

Co-authored-by: Safihre <safihre@sabnzbd.org>
2021-03-10 22:23:10 +01:00
jxyzn e851813cef Sanitize names possibly derived from X-DNZB-EpisodeName (#1806) 2021-03-10 22:15:23 +01:00
thezoggy 4d49ad9141 3.2.x cleanup (#1808)
* Update uni_config bootstrap css to same version of js (3.3.7).
* small accessibility change, removed thin dot border on focus

* Ignore VS Code settings folder

* cherry picked 'Fix disabled select for Glitter Night'

* glitter night - fix search border color
2021-02-27 14:47:44 +01:00
Safihre 16618b3af2 Set version to 3.2.0 2021-02-26 10:30:00 +01:00
Safihre 0e5c0f664f Merge branch '3.2.x' 2021-02-26 10:29:39 +01:00
Safihre 7be9281431 Update text files for 3.2.0
draft release
2021-02-26 09:56:47 +01:00
Safihre ee0327fac1 Update macOS build Python to 3.9.2 2021-02-26 09:44:51 +01:00
Safihre 9930de3e7f Log all nzo_info when adding NZB's
Relates to #1806
2021-02-26 09:18:14 +01:00
Sander e8503e89c6 handle gracefully if no malloc_trim() available (#1800) 2021-02-26 09:18:00 +01:00
puzzledsab 1d9ed419eb Remove some redundant ifs (#1791) 2021-02-26 09:17:29 +01:00
Safihre 0207652e3e Update text files for 3.2.0RC2
draft release
2021-02-08 21:02:38 +01:00
Safihre 0f1e99c5cb Update translatable texts 2021-02-08 13:29:16 +01:00
puzzledsabandSafihre f134bc7efb Right-to-Left support for Glitter and Config (#1776)
* Add rtl on main page

* Adjustments to rtl

* Forgot to add black check for this checkout

* Remove unnecessary style

* Remove more redundant attributes

* Some more reordering and alignment

* Align sorting and nzb drop downs

* Update NZB details and shutdown page

* Fix format

* Fix SABnzbd Config title tag

* Change file list header direction

* Set rtl variables in build_header instead and test dir="rtl" in config pages

* Revert some changes and handle styling using CSS

* Move more items to CSS

* Config RTL

* Move even more to CSS

* Small tweak

Co-authored-by: Safihre <safihre@sabnzbd.org>
2021-02-08 13:23:03 +01:00
puzzledsab dcd7c7180e Do full server check when there are busy_threads (#1786)
* Do full server check when there are busy_threads

* Reduce next_article_search delay to 0.5s
2021-02-08 13:19:38 +01:00
jcfp fbbfcd075b fix bonjour with localhost, retire LOCALHOSTS constant (#1782)
* fix bonjour with localhost, retire LOCALHOSTS constant

* rename probablyipv[46] functions to is_ipv[46]_addr

* refuse to send ssdp description_xml to outsiders
2021-02-08 13:19:30 +01:00
Safihre f42d2e4140 Rename Glitter Default to Light and make Auto the new Default 2021-02-05 15:01:28 +01:00
Sam Edwards 88882cebbc Support for auto night mode switching in Glitter (#1783) 2021-02-05 15:01:13 +01:00
Safihre 17a979675c Do not re-release from GA when the release tag is pushed 2021-02-05 15:01:04 +01:00
Safihre 4642850c79 Set macOS Python installer target to "/" 2021-02-05 15:01:00 +01:00
Safihre e8d6eebb04 Set version to 3.1.1 2020-11-11 22:04:44 +01:00
Safihre 864c5160c0 Merge branch '3.1.x' 2020-11-11 22:01:20 +01:00
Safihre 99b5a00c12 Update text files for 3.1.1 2020-11-11 21:56:15 +01:00
Safihre 85ee1f07d7 Do not crash if we cannot format the error message 2020-11-08 15:06:50 +01:00
exizak42 e58b4394e0 Separate email message lines are with CRLF (#1671)
SMTP protocol dictates that all lines are supposed to be separated
with CRLF and not LF (even on LF-based systems). This change ensures
that even if the original byte string message is using `\n` for line
separators, the SMTP protocol will still work properly.

This resolves sabnzbd#1669

Fix code formatting
2020-11-08 14:44:44 +01:00
Safihre 1e91a57bf1 It was not possible to set directory-settings to empty values 2020-11-06 16:14:53 +01:00
Safihre 39cee52a7e Update text files for 3.1.1RC1 2020-11-02 20:03:43 +01:00
Safihre 72068f939d Improve handling of binary restarts (macOS / Windows) 2020-11-02 19:57:57 +01:00
Safihre 096d0d3cad Deobfuscate-during-download did not work
https://forums.sabnzbd.org/viewtopic.php?f=3&t=25037
2020-11-01 15:35:09 +01:00
Safihre 2472ab0121 Python 3.5 does not know ssl.PROTOCOL_TLS_SERVER
Closes #1658
2020-10-27 15:52:28 +01:00
Safihre 00421717b8 Queue Repair would fail if Rating is enabled
Closes #1649
2020-10-24 11:10:03 +02:00
Safihre ae96d93f94 Set version to 3.1.0 2020-10-16 17:02:28 +02:00
Safihre 8522c40c8f Merge branch '3.1.x' 2020-10-16 16:58:58 +02:00
Safihre 23f86e95f1 Update text files for 3.1.0 2020-10-16 16:42:35 +02:00
Safihre eed2045189 After pre-check the job was not restored to the original spot 2020-10-16 16:27:51 +02:00
Safihre 217785bf0f Applying Filters to a feed would result in crash
Closes #1634
2020-10-15 18:07:06 +02:00
Safihre 6aef50dc5d Update text files for 3.1.0RC3 2020-10-02 11:34:21 +02:00
Safihre 16b6e3caa7 Notify users of Deobfuscate.py that it is now part of SABnzbd 2020-09-29 14:08:51 +02:00
Safihre 3de4c99a8a Only set the "Waiting" status when the job hits post-processing
https://forums.sabnzbd.org/viewtopic.php?f=11&t=24969
2020-09-29 13:51:15 +02:00
Safihre 980aa19a75 Only run Windows Service code when executed from the executables
Could be made to work with the from-sources code.. But seems like very small usecase.
Closes #1623
2020-09-29 10:42:23 +02:00
Safihre fb4b57e056 Update text files for 3.1.0RC2 2020-09-27 17:19:34 +02:00
Safihre 03638365ea Set execute bit on Deobfuscate.py 2020-09-27 17:17:30 +02:00
Safihre 157cb1c83d Handle failing RSS-feeds for feedparser 6.0.0+
Closes #1621
Now throws warnings (that can be disabled, helpfull_warnings) if readout failed.
2020-09-27 13:32:38 +02:00
Safihre e51f11c2b1 Do not crash if attributes file is not present 2020-09-25 10:50:19 +02:00
Safihre 1ad0961dd8 Existing files were not parsed when re-adding a job 2020-09-25 10:49:50 +02:00
Safihre 46ff7dd4e2 Do not crash if we can't save attributes, the job might be gone 2020-09-25 10:03:05 +02:00
Safihre 8b067df914 Correctly parse failed_only for Plush 2020-09-23 16:56:57 +02:00
Safihre ef43b13272 Assume RarFile parses the correct filepaths for the RAR-volumes
Parsing UTF8 from command-line still fails.
https://forums.sabnzbd.org/viewtopic.php?p=122267#p122267
2020-09-21 22:12:43 +02:00
Safihre e8e9974224 work_name would not be sanatized when adding NZB's
Closes #1615
Now with tests, yeah.
2020-09-21 22:12:34 +02:00
Safihre feebbb9f04 Merge branch '3.0.x' 2020-09-13 16:40:43 +02:00
Safihre bc4f06dd1d Limit feedparser<6.0.0 for 3.0.x 2020-09-13 16:40:14 +02:00
Safihre 971e4fc909 Merge branch '3.0.x' 2020-08-30 20:58:31 +02:00
Safihre 51cc765949 Update text files for 3.0.2 2020-08-30 20:50:45 +02:00
Safihre 19c6a4fffa Propagation delay label was shown even if no delay was activated 2020-08-29 16:46:16 +02:00
Safihre 105ac32d2f Reading RSS feed with no categories set could result in crash
Closes #1589
2020-08-28 10:16:49 +02:00
Safihre 57550675d2 Removed logging in macOS sabApp that resulted in double logging 2020-08-28 10:16:41 +02:00
Safihre e674abc5c0 Update text files for 3.0.2RC2 2020-08-26 08:56:29 +02:00
Safihre f965c96f51 Change the macOS power assertion to NoIdleSleep 2020-08-26 08:50:54 +02:00
Safihre c76b8ed9e0 End-of-queue-script did not run on Windows due to long-path
https://forums.sabnzbd.org/viewtopic.php?f=3&t=24918

Will refactor this so they all call 1 function.
2020-08-24 11:28:14 +02:00
Safihre 4fbd0d8a7b Check if name is a string before switching to nzbfile in addfile
Closes #1584
2020-08-24 09:05:25 +02:00
Safihre 2186c0fff6 Update text files for 3.0.2 RC 1 2020-08-21 15:42:35 +02:00
Safihre 1adca9a9c1 Do not crash if certifi certificates are not available
This could happen on Windows, due to overactive virus scanners
2020-08-21 15:26:06 +02:00
Safihre 9408353f2b Priority was not parsed correctly if supplied as string 2020-08-21 15:12:09 +02:00
Safihre 84f4d453d2 Permissions would be set even if user didn't set any
Windows developers like me shouldn't do permissions stuff..
2020-08-21 15:12:01 +02:00
Safihre d10209f2a1 Extend tests of create_all_dirs to cover apply_umask=False 2020-08-21 15:11:53 +02:00
Safihre 3ae149c72f Split the make_mo.py command for NSIS 2020-08-19 22:21:02 +02:00
Safihre 47385acc3b Make sure we force the final_name to string on legacy get_attrib_file 2020-08-19 16:21:13 +02:00
Safihre 814eeaa900 Redesigned the saving of attributes
Now uses pickle, so that the type of the property is preserved.
Made flexible, so that more properties can be easily added later.
Closes #1575
2020-08-19 16:21:07 +02:00
Safihre 5f2ea13aad NzbFile comparison could crash when comparing finished_files
https://forums.sabnzbd.org/viewtopic.php?f=3&t=24902&p=121748
2020-08-19 08:50:06 +02:00
Safihre 41ca217931 Merge branch '3.0.x' 2020-08-18 11:05:50 +02:00
Safihre b57d36e8dd Set version information to 3.0.1 2020-08-18 11:05:36 +02:00
Safihre 9a4be70734 List Cheetah minimal version in requirements.txt 2020-08-18 08:21:20 +02:00
Safihre a8443595a6 Generalize use of certifi module 2020-08-18 08:20:47 +02:00
Safihre fd0a70ac58 Update text files for 3.0.1 2020-08-17 16:52:23 +02:00
Safihre 8a8685c968 Permissions should only be applied if requested
Corrects 050b925f7b
2020-08-16 18:28:39 +02:00
Safihre 9e6cb8da8e Temporarily set cheroot version due to it breaking our tests
cherrypy/cheroot/issues/312
2020-08-16 18:28:13 +02:00
Safihre 054ec54d51 Basic authentication option was broken
Closes #1571
2020-08-10 15:34:01 +02:00
Safihre 272ce773cb Update text files for 3.0.1RC1 2020-08-07 15:28:11 +02:00
Safihre 050b925f7b Permissions were not set correctly when creating directories (#1568)
Restores changes made in d2e0ebe
2020-08-07 15:22:53 +02:00
Safihre 0087940898 Merge branch '3.0.x' into master 2020-08-02 09:46:41 +02:00
Safihre e323c014f9 Set version information to 3.0.0 2020-08-01 16:17:08 +02:00
Safihre cc465c7554 Update text files for 3.0.0
🎉🎉
2020-08-01 15:59:30 +02:00
Safihre 14cb37564f Update translate-link in SABnzbd 2020-07-19 13:01:39 +02:00
Safihre 094db56c3b Default-text for Automatically sort queue 2020-07-16 22:29:02 +02:00
Safihre aabb709b8b Update text files for 3.0.0 RC 2 2020-07-15 14:10:35 +02:00
Safihre 0833dd2db9 Update translatable texts in 3.0.x branch 2020-07-15 14:07:21 +02:00
Safihre cd3f912be4 RAR-renamer should be run on badly named RAR-files
https://forums.sabnzbd.org/viewtopic.php?f=2&t=24514&p=121433
2020-07-15 14:01:48 +02:00
Safihre 665c516db6 Only really run pre-script when it is set 2020-07-12 14:20:18 +02:00
Safihre b670da9fa0 Always use Default-priority when creating NZB-objects
Closes #1552
2020-07-12 14:03:07 +02:00
Safihre 80bee9bffe Search-icon would be shown on top of drop-downs
Closes #1545
2020-06-30 12:57:28 +02:00
Safihre d85a70e8ad Always report API paused status as a boolean
Closes #1542
2020-06-30 10:26:34 +02:00
Safihre 8f21533e76 Set version to 2.3.9 2019-05-24 11:39:14 +02:00
Safihre 89996482a1 Merge branch '2.3.x' 2019-05-24 09:33:12 +02:00
Safihre 03c10dce91 Update text files for 2.3.9 2019-05-24 09:32:34 +02:00
Safihre bd5331be05 Merge branch 'develop' into 2.3.x 2019-05-24 09:12:02 +02:00
Safihre 46e1645289 Correct typo in release notes 2019-05-18 10:56:39 +02:00
Safihre 4ce3965747 Update text files for 2.3.9RC2 2019-05-18 09:56:05 +02:00
Safihre 9d4af19db3 Merge branch 'develop' into 2.3.x 2019-05-18 09:45:20 +02:00
Safihre 48e034f4be Update text files for 2.3.9RC1 2019-05-07 13:50:20 +02:00
Safihre f8959baa2f Revert "Notify develop-users that we will switch to Python 3"
This reverts commit fb238af7de.
2019-05-07 13:35:13 +02:00
Safihre 8ed5997eae Merge branch 'develop' into 2.3.x 2019-05-07 13:10:10 +02:00
Safihre daf9f50ac8 Set version to 2.3.8 2019-03-18 11:10:56 +01:00
Safihre 6b11013c1a Merge branch '2.3.x' 2019-03-18 11:09:35 +01:00
18 changed files with 875 additions and 309 deletions

No files matched your search

+77 -2
View File
@@ -1,7 +1,80 @@
Release Notes - SABnzbd 5.1.0 Release Candidate 2
Release Notes - SABnzbd 5.1.2
=========================================================
This is the second release candidate of version 5.1.
SABnzbd 5.1.1 and 5.1.2 resolved 3 serious vulnerabilities.
## Critical remote code execution vulnerability resolved in 5.1.2 (GHSA-rgqj-28c2-gxwp)
You are only affected if an untrusted party can reach the web interface.
By default, SABnzbd is only accessible from your own device and `External internet access`
is set to `No access`. If either of those is still at its default, or if you use a proxy
service for authentication, you are not affected.
In version 5.1.1 and earlier, an attacker who can reach the web interface could bypass
authentication on configuration endpoints, even when SABnzbd is protected with a username
and password. By changing SABnzbd's settings this way, an attacker could ultimately execute
arbitrary commands on the system running SABnzbd, with the same permissions as SABnzbd itself.
This also means that all information in SABnzbd would be exposed.
See GHSA-xrfq-jhgh-wqch below for actions and mitigations.
More information: https://sabnzbd.org/5-1-vulnerabilities
## High-severity path traversal vulnerability resolved in 5.1.2 (GHSA-75g3-96fr-7p2r)
You are affected if SABnzbd processes NZBs or downloads from a source you do not fully
trust, such as a public indexer.
In version 5.1.1 and earlier, a maliciously crafted PAR2 or SFV file inside a download
could cause SABnzbd to move downloaded content outside of the job's own folder during
post-processing. By using this to plant a file in another job's internal administration,
an attacker could ultimately execute arbitrary commands on the system running SABnzbd,
with the same permissions as SABnzbd itself.
The only mitigation is to update, there is no configuration change that prevents this.
More information: https://sabnzbd.org/5-1-vulnerabilities
## Critical authentication vulnerability resolved in 5.1.1 (GHSA-xrfq-jhgh-wqch)
You are only affected if an untrusted party can reach the web interface.
By default, SABnzbd is only accessible from your own device and `External internet access`
is set to `No access`. If either of those is still at its default, or if you use a proxy
service for authentication, you are not affected.
If the SABnzbd login page is reachable from outside your network, an attacker could
obtain a valid session in version 5.1.0 and earlier, even when SABnzbd is protected with
a username and password. This means that all information in SABnzbd would be exposed.
If you rely on the SABnzbd username and password to keep out other users on your
network or the internet, it is recommended that you change the following
sensitive information after applying the update:
* SABnzbd username and password.
* SABnzbd API-key.
* Usenet server passwords.
* API-keys from indexers used within RSS-feeds.
* Authentication information for notification services.
If you cannot update right away, the only mitigations are to ensure the web interface is not
reachable by untrusted parties, or to lower `External internet access` to `Full API` or below.
More information: https://sabnzbd.org/5-1-vulnerabilities
## Other bug fixes in 5.1.2
* RSS filters were not applied directly.
## Other bug fixes in 5.1.1
* Styling cache issues could occur after updating.
* RSS items were removed too eagerly from RSS-history after download.
* The RSS-feed `Clear Downloaded` button did not do anything.
## Changelog 5.1.0
This release brings a fundamental improvement to "Retry": instead of
re-downloading any files with missing data, only the articles that were actually
missing are fetched again. RSS got an overhaul under the hood, the interface is
refreshed, and we added quite a long list of long-requested features and bugfixes.
## New features in 5.1.0
@@ -39,10 +112,12 @@ This is the second release candidate of version 5.1.
* Diskspace checks would be too strict when unpacking to different disk.
* Prevent deadlock when a second signal arrives during shutdown procedure.
* Verification using SFV-checks could fail, even though files were correct.
* Don't log Warning during clean shutdowns.
* Windows: Uninstall would not remove Settings or Windows Service.
* Windows: Warn for OS-limit if more than 1024 connections are configured.
* macOS: AppleDouble files could result in failures in `Moving` stage.
* macOS: Restarting the application resulted in Terminal window.
* macOS: Included par2cmdline-turbo will now use all available CPU-features.
## Upgrade notices
@@ -1 +1 @@
@import url('Night.css') screen and (prefers-color-scheme: dark);
@import url('Night.css?lastmajorupdate=510') screen and (prefers-color-scheme: dark);
@@ -1 +1 @@
@import url('Night.css') screen and (prefers-color-scheme: dark);
@import url('Night.css?lastmajorupdate=510') screen and (prefers-color-scheme: dark);
+239 -233
View File
@@ -1,237 +1,243 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- Copyright 2022-2026 by The SABnzbd-Team (sabnzbd.org) -->
<component type="desktop-application">
<id>org.sabnzbd.sabnzbd</id>
<metadata_license>MIT</metadata_license>
<name>SABnzbd</name>
<summary>Free and easy binary newsreader</summary>
<summary xml:lang="fr">Lecteur de news binaire libre et facile à utiliser</summary>
<summary xml:lang="tr">Özgür ve kullanımı kolay Usenet ikili okuyucu</summary>
<branding>
<color type="primary" scheme_preference="light">#e7e7e7</color>
<color type="primary" scheme_preference="dark">#444444</color>
</branding>
<description>
<p>
SABnzbd is a free and Open Source web-based binary newsreader,
with support for the popular nzb file format. It greatly simplifies
the process of downloading from Usenet, thanks to a friendly
web-based user interface and advanced built-in post-processing
options including the ability to automatically verify, repair,
extract and clean up downloaded posts. It runs anywhere, comes in
over a dozen languages, and integrates with a host of tools, apps
and services that help automate the download process.
</p>
<p xml:lang="fr">
SABnzbd est un lecteur binaire de newsgroups libre et gratuit prenant
en charge le format nzb très répandu. Il simplifie grandement le processus
de téléchargement depuis Usenet grâce à une interface web conviviale
et à des options avancées de post-traitement intégrées qui comprennent
la capacité à vérifier, réparer, extraire et nettoyer automatiquement
les messages téléchargés. Il fonctionne partout, est fourni avec une
douzaine de traductions et s'intègre avec de nombreux outils, applications
et services qui aident à automatiser le processus de téléchargement.
</p>
<p xml:lang="tr">
SABnzbd, popüler nzb biçimini destekleyen özgür ve ücretsiz bir ikili
haber grupları okuyucusudur. Kullanıcı dostu Web tabanlı arayüzü ve
indirilen mesajları otomatik olarak doğrulama, tamir etme, çıkarma ve
temizleme kabiliyetine sahip gelişmiş yerleşik son işlem seçenekleri
sayesinde Usenet ağından indirmeyi büyük ölçüde kolaylaştırır. Her yerde
çalışabilir, bir düzineden fazla dil desteği sunar ve indirme sürecini
otomatik hâle getirmeye yardımcı olan birçok araç, uygulama ve hizmet ile
birlikte çalışabilir.
</p>
</description>
<keywords>
<keyword>usenet</keyword>
<keyword>nzb</keyword>
<keyword>download</keyword>
<keyword>newsreader</keyword>
<keyword>binary</keyword>
<keyword xml:lang="fr">usenet</keyword>
<keyword xml:lang="fr">nzb</keyword>
<keyword xml:lang="fr">téléchargement</keyword>
<keyword xml:lang="fr">binaires</keyword>
<keyword xml:lang="tr">usenet</keyword>
<keyword xml:lang="tr">nzb</keyword>
<keyword xml:lang="tr">indirme</keyword>
</keywords>
<categories>
<category>Network</category>
<category>FileTransfer</category>
</categories>
<url type="homepage">https://sabnzbd.org</url>
<url type="bugtracker">https://github.com/sabnzbd/sabnzbd/issues</url>
<url type="vcs-browser">https://github.com/sabnzbd/sabnzbd</url>
<url type="contribute">https://github.com/sabnzbd/sabnzbd</url>
<url type="translate">https://sabnzbd.org/wiki/translate</url>
<url type="donation">https://sabnzbd.org/donate</url>
<url type="help">https://sabnzbd.org/wiki/</url>
<url type="faq">https://sabnzbd.org/wiki/faq</url>
<url type="contact">https://sabnzbd.org/live-chat.html</url>
<releases>
<release version="5.1.0" date="2026-08-01" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/5.1.0</url>
</release>
<release version="5.0.4" date="2026-06-11" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/5.0.4</url>
</release>
<release version="5.0.3" date="2026-05-15" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/5.0.3</url>
</release>
<release version="5.0.2" date="2026-05-14" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/5.0.2</url>
</release>
<release version="5.0.1" date="2026-05-01" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/5.0.1</url>
</release>
<release version="5.0.0" date="2026-05-01" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/5.0.0</url>
</release>
<release version="4.5.5" date="2025-10-24" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/4.5.5</url>
</release>
<release version="4.5.4" date="2025-10-22" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/4.5.4</url>
</release>
<release version="4.5.3" date="2025-08-25" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/4.5.3</url>
</release>
<release version="4.5.2" date="2025-07-09" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/4.5.2</url>
</release>
<release version="4.5.1" date="2025-04-11" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/4.5.1</url>
</release>
<release version="4.5.0" date="2025-04-01" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/4.5.0</url>
</release>
<release version="4.4.1" date="2024-12-23" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/4.4.1</url>
</release>
<release version="4.4.0" date="2024-12-09" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/4.4.0</url>
</release>
<release version="4.3.3" date="2024-08-01" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/4.3.3</url>
</release>
<release version="4.3.2" date="2024-05-30" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/4.3.2</url>
</release>
<release version="4.3.1" date="2024-05-03" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/4.3.1</url>
</release>
</releases>
<launchable type="desktop-id">sabnzbd.desktop</launchable>
<provides>
<mediatype>application/x-nzb</mediatype>
<mediatype>application/x-compressed-nzb</mediatype>
</provides>
<supports>
<control>pointing</control>
<control>keyboard</control>
<control>touch</control>
</supports>
<recommends>
<display_length compare="ge">640</display_length>
<internet>always</internet>
</recommends>
<project_license>GPL-2.0-or-later</project_license>
<developer id="org.sabnzbd">
<name>The SABnzbd-Team</name>
</developer>
<screenshots>
<screenshot type="default">
<image>https://sabnzbd.org/images/landing/screenshots/interface.png</image>
<caption>Intuitive interface</caption>
<caption xml:lang="fr">Interface intuitive</caption>
<caption xml:lang="tr">Sezgisel arayüz</caption>
</screenshot>
<screenshot>
<image>https://sabnzbd.org/images/landing/screenshots/night-mode.png</image>
<caption>Also comes in Night-mode</caption>
<caption xml:lang="fr">Fourni également avec le mode nuit</caption>
<caption xml:lang="tr">Gece kipi ile de sunulur</caption>
</screenshot>
<screenshot>
<image>https://sabnzbd.org/images/landing/screenshots/add-nzb.png</image>
<caption>Add NZB's or use drag-and-drop!</caption>
<caption xml:lang="fr">Ajoutez des fichiers NZB ou utilisez le glisser - déposer !</caption>
<caption xml:lang="tr">NZB dosyaları ekleyin veya kaydır - bırak kullanın!</caption>
</screenshot>
<screenshot>
<image>https://sabnzbd.org/images/landing/screenshots/phone-interface.png</image>
<caption>Scales to any screen size</caption>
<caption xml:lang="fr">S'adapte à toutes les tailles d'écran</caption>
<caption xml:lang="tr">Her ekran boyutuna uyum sağlar</caption>
</screenshot>
<screenshot>
<image>https://sabnzbd.org/images/landing/screenshots/history-details.png</image>
<caption>Easy overview of all history details</caption>
<caption xml:lang="fr">Vue d'ensemble aisée de tous les tails de l'historique</caption>
<caption xml:lang="tr">Tarihçenin tüm ayrıntıları için kolay genel bakış</caption>
</screenshot>
<screenshot>
<image>https://sabnzbd.org/images/landing/screenshots/phone-extra.png</image>
<caption>Every option, on every screen size</caption>
<caption xml:lang="fr">Toutes les options, quelle que soit la taille d'écran</caption>
<caption xml:lang="tr">Ekran boyutu ne olursa olsun, tüm seçenekler elinizin altında</caption>
</screenshot>
<screenshot>
<image>https://sabnzbd.org/images/landing/screenshots/file-lists.png</image>
<caption>Manage a job's individual files</caption>
<caption xml:lang="fr">Gérez les fichiers individuels d'une tâche</caption>
<caption xml:lang="tr">Herhangi bir görevin dosyalarını tek tek yönetin</caption>
</screenshot>
<screenshot>
<image>https://sabnzbd.org/images/landing/screenshots/set-speedlimit.png</image>
<caption>Easy speed limiting</caption>
<caption xml:lang="fr">Limitation facile du débit</caption>
<caption xml:lang="tr">Kolay hız sınırlaması</caption>
</screenshot>
<screenshot>
<image>https://sabnzbd.org/images/landing/screenshots/set-options.png</image>
<caption>Quickly change settings</caption>
<caption xml:lang="fr">Changement rapide des réglages</caption>
<caption xml:lang="tr">Ayarları hızlıca değiştirin</caption>
</screenshot>
<screenshot>
<image>https://sabnzbd.org/images/landing/screenshots/dashboard.png</image>
<caption>Easy system check</caption>
<caption xml:lang="fr">Vérification facile du système</caption>
<caption xml:lang="tr">Sistemi kolayca kontrol edin</caption>
</screenshot>
<screenshot>
<image>https://sabnzbd.org/images/landing/screenshots/connections-overview.png</image>
<caption>See active connections</caption>
<caption xml:lang="fr">Voyez les connexions actives</caption>
<caption xml:lang="tr">Etkin bağlantıları görüntüleyin</caption>
</screenshot>
<screenshot>
<image>https://sabnzbd.org/images/landing/screenshots/skin-settings.png</image>
<caption>Customize the interface</caption>
<caption xml:lang="fr">Personnalisez l'interface</caption>
<caption xml:lang="tr">Arayüzü kişiselleştirin</caption>
</screenshot>
<screenshot>
<image>https://sabnzbd.org/images/landing/screenshots/tabbed.png</image>
<caption>Tabbed-mode</caption>
<caption xml:lang="fr">Mode avec onglets</caption>
<caption xml:lang="tr">Sekmeli kip</caption>
</screenshot>
<screenshot>
<image>https://sabnzbd.org/images/landing/screenshots/set-custom-pause.png</image>
<caption>Specify any pause duration</caption>
<caption xml:lang="fr">Spécifiez une durée de pause quelconque</caption>
<caption xml:lang="tr">Herhangi bir duraklama süresi belirtin</caption>
</screenshot>
<screenshot>
<image>https://sabnzbd.org/images/landing/screenshots/config.png</image>
<caption>Easy configuration</caption>
<caption xml:lang="fr">Configuration facile</caption>
<caption xml:lang="tr">Kolay yapılandırma</caption>
</screenshot>
</screenshots>
<content_rating type="oars-1.1"/>
<id>org.sabnzbd.sabnzbd</id>
<metadata_license>MIT</metadata_license>
<name>SABnzbd</name>
<summary>Free and easy binary newsreader</summary>
<summary xml:lang="fr">Lecteur de news binaire libre et facile à utiliser</summary>
<summary xml:lang="tr">Özgür ve kullanımı kolay Usenet ikili okuyucu</summary>
<branding>
<color type="primary" scheme_preference="light">#e7e7e7</color>
<color type="primary" scheme_preference="dark">#444444</color>
</branding>
<description>
<p>
SABnzbd is a free and Open Source web-based binary newsreader,
with support for the popular nzb file format. It greatly simplifies
the process of downloading from Usenet, thanks to a friendly
web-based user interface and advanced built-in post-processing
options including the ability to automatically verify, repair,
extract and clean up downloaded posts. It runs anywhere, comes in
over a dozen languages, and integrates with a host of tools, apps
and services that help automate the download process.
</p>
<p xml:lang="fr">
SABnzbd est un lecteur binaire de newsgroups libre et gratuit prenant
en charge le format nzb très répandu. Il simplifie grandement le processus
de téléchargement depuis Usenet grâce à une interface web conviviale
et à des options avancées de post-traitement intégrées qui comprennent
la capacité à vérifier, réparer, extraire et nettoyer automatiquement
les messages téléchargés. Il fonctionne partout, est fourni avec une
douzaine de traductions et s'intègre avec de nombreux outils, applications
et services qui aident à automatiser le processus de téléchargement.
</p>
<p xml:lang="tr">
SABnzbd, popüler nzb biçimini destekleyen özgür ve ücretsiz bir ikili
haber grupları okuyucusudur. Kullanıcı dostu Web tabanlı arayüzü ve
indirilen mesajları otomatik olarak doğrulama, tamir etme, çıkarma ve
temizleme kabiliyetine sahip gelişmiş yerleşik son işlem seçenekleri
sayesinde Usenet ağından indirmeyi büyük ölçüde kolaylaştırır. Her yerde
çalışabilir, bir düzineden fazla dil desteği sunar ve indirme sürecini
otomatik hâle getirmeye yardımcı olan birçok araç, uygulama ve hizmet ile
birlikte çalışabilir.
</p>
</description>
<keywords>
<keyword>usenet</keyword>
<keyword>nzb</keyword>
<keyword>download</keyword>
<keyword>newsreader</keyword>
<keyword>binary</keyword>
<keyword xml:lang="fr">usenet</keyword>
<keyword xml:lang="fr">nzb</keyword>
<keyword xml:lang="fr">téléchargement</keyword>
<keyword xml:lang="fr">binaires</keyword>
<keyword xml:lang="tr">usenet</keyword>
<keyword xml:lang="tr">nzb</keyword>
<keyword xml:lang="tr">indirme</keyword>
</keywords>
<categories>
<category>Network</category>
<category>FileTransfer</category>
</categories>
<url type="homepage">https://sabnzbd.org</url>
<url type="bugtracker">https://github.com/sabnzbd/sabnzbd/issues</url>
<url type="vcs-browser">https://github.com/sabnzbd/sabnzbd</url>
<url type="contribute">https://github.com/sabnzbd/sabnzbd</url>
<url type="translate">https://sabnzbd.org/wiki/translate</url>
<url type="donation">https://sabnzbd.org/donate</url>
<url type="help">https://sabnzbd.org/wiki/</url>
<url type="faq">https://sabnzbd.org/wiki/faq</url>
<url type="contact">https://sabnzbd.org/live-chat.html</url>
<releases>
<release version="5.1.2" date="2026-08-25" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/5.1.2</url>
</release>
<release version="5.1.1" date="2026-08-18" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/5.1.1</url>
</release>
<release version="5.1.0" date="2026-08-10" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/5.1.0</url>
</release>
<release version="5.0.4" date="2026-06-11" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/5.0.4</url>
</release>
<release version="5.0.3" date="2026-05-15" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/5.0.3</url>
</release>
<release version="5.0.2" date="2026-05-14" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/5.0.2</url>
</release>
<release version="5.0.1" date="2026-05-01" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/5.0.1</url>
</release>
<release version="5.0.0" date="2026-05-01" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/5.0.0</url>
</release>
<release version="4.5.5" date="2025-10-24" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/4.5.5</url>
</release>
<release version="4.5.4" date="2025-10-22" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/4.5.4</url>
</release>
<release version="4.5.3" date="2025-08-25" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/4.5.3</url>
</release>
<release version="4.5.2" date="2025-07-09" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/4.5.2</url>
</release>
<release version="4.5.1" date="2025-04-11" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/4.5.1</url>
</release>
<release version="4.5.0" date="2025-04-01" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/4.5.0</url>
</release>
<release version="4.4.1" date="2024-12-23" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/4.4.1</url>
</release>
<release version="4.4.0" date="2024-12-09" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/4.4.0</url>
</release>
<release version="4.3.3" date="2024-08-01" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/4.3.3</url>
</release>
<release version="4.3.2" date="2024-05-30" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/4.3.2</url>
</release>
<release version="4.3.1" date="2024-05-03" type="stable">
<url type="details">https://github.com/sabnzbd/sabnzbd/releases/tag/4.3.1</url>
</release>
</releases>
<launchable type="desktop-id">sabnzbd.desktop</launchable>
<provides>
<mediatype>application/x-nzb</mediatype>
<mediatype>application/x-compressed-nzb</mediatype>
</provides>
<supports>
<control>pointing</control>
<control>keyboard</control>
<control>touch</control>
</supports>
<recommends>
<display_length compare="ge">640</display_length>
<internet>always</internet>
</recommends>
<project_license>GPL-2.0-or-later</project_license>
<developer id="org.sabnzbd">
<name>The SABnzbd-Team</name>
</developer>
<screenshots>
<screenshot type="default">
<image>https://sabnzbd.org/images/landing/screenshots/interface.png</image>
<caption>Intuitive interface</caption>
<caption xml:lang="fr">Interface intuitive</caption>
<caption xml:lang="tr">Sezgisel arayüz</caption>
</screenshot>
<screenshot>
<image>https://sabnzbd.org/images/landing/screenshots/night-mode.png</image>
<caption>Also comes in Night-mode</caption>
<caption xml:lang="fr">Fourni également avec le mode nuit</caption>
<caption xml:lang="tr">Gece kipi ile de sunulur</caption>
</screenshot>
<screenshot>
<image>https://sabnzbd.org/images/landing/screenshots/add-nzb.png</image>
<caption>Add NZB's or use drag-and-drop!</caption>
<caption xml:lang="fr">Ajoutez des fichiers NZB ou utilisez le glisser - déposer !</caption>
<caption xml:lang="tr">NZB dosyaları ekleyin veya kaydır - bırak kullanın!</caption>
</screenshot>
<screenshot>
<image>https://sabnzbd.org/images/landing/screenshots/phone-interface.png</image>
<caption>Scales to any screen size</caption>
<caption xml:lang="fr">S'adapte à toutes les tailles d'écran</caption>
<caption xml:lang="tr">Her ekran boyutuna uyum sağlar</caption>
</screenshot>
<screenshot>
<image>https://sabnzbd.org/images/landing/screenshots/history-details.png</image>
<caption>Easy overview of all history details</caption>
<caption xml:lang="fr">Vue d'ensemble aisée de tous les détails de l'historique</caption>
<caption xml:lang="tr">Tarihçenin tüm ayrıntıları için kolay genel bakış</caption>
</screenshot>
<screenshot>
<image>https://sabnzbd.org/images/landing/screenshots/phone-extra.png</image>
<caption>Every option, on every screen size</caption>
<caption xml:lang="fr">Toutes les options, quelle que soit la taille d'écran</caption>
<caption xml:lang="tr">Ekran boyutu ne olursa olsun, tüm seçenekler elinizin altında</caption>
</screenshot>
<screenshot>
<image>https://sabnzbd.org/images/landing/screenshots/file-lists.png</image>
<caption>Manage a job's individual files</caption>
<caption xml:lang="fr">Gérez les fichiers individuels d'une tâche</caption>
<caption xml:lang="tr">Herhangi bir görevin dosyalarını tek tek yönetin</caption>
</screenshot>
<screenshot>
<image>https://sabnzbd.org/images/landing/screenshots/set-speedlimit.png</image>
<caption>Easy speed limiting</caption>
<caption xml:lang="fr">Limitation facile du débit</caption>
<caption xml:lang="tr">Kolay hız sınırlaması</caption>
</screenshot>
<screenshot>
<image>https://sabnzbd.org/images/landing/screenshots/set-options.png</image>
<caption>Quickly change settings</caption>
<caption xml:lang="fr">Changement rapide des réglages</caption>
<caption xml:lang="tr">Ayarları hızlıca değiştirin</caption>
</screenshot>
<screenshot>
<image>https://sabnzbd.org/images/landing/screenshots/dashboard.png</image>
<caption>Easy system check</caption>
<caption xml:lang="fr">Vérification facile du système</caption>
<caption xml:lang="tr">Sistemi kolayca kontrol edin</caption>
</screenshot>
<screenshot>
<image>https://sabnzbd.org/images/landing/screenshots/connections-overview.png</image>
<caption>See active connections</caption>
<caption xml:lang="fr">Voyez les connexions actives</caption>
<caption xml:lang="tr">Etkin bağlantıları görüntüleyin</caption>
</screenshot>
<screenshot>
<image>https://sabnzbd.org/images/landing/screenshots/skin-settings.png</image>
<caption>Customize the interface</caption>
<caption xml:lang="fr">Personnalisez l'interface</caption>
<caption xml:lang="tr">Arayüzü kişiselleştirin</caption>
</screenshot>
<screenshot>
<image>https://sabnzbd.org/images/landing/screenshots/tabbed.png</image>
<caption>Tabbed-mode</caption>
<caption xml:lang="fr">Mode avec onglets</caption>
<caption xml:lang="tr">Sekmeli kip</caption>
</screenshot>
<screenshot>
<image>https://sabnzbd.org/images/landing/screenshots/set-custom-pause.png</image>
<caption>Specify any pause duration</caption>
<caption xml:lang="fr">Spécifiez une durée de pause quelconque</caption>
<caption xml:lang="tr">Herhangi bir duraklama süresi belirtin</caption>
</screenshot>
<screenshot>
<image>https://sabnzbd.org/images/landing/screenshots/config.png</image>
<caption>Easy configuration</caption>
<caption xml:lang="fr">Configuration facile</caption>
<caption xml:lang="tr">Kolay yapılandırma</caption>
</screenshot>
</screenshots>
<content_rating type="oars-1.1"/>
</component>
+2 -2
View File
@@ -533,8 +533,8 @@ def delayed_startup_actions():
# aren't on 24/7 and typically don't benefit from the daily scheduled call at midnight
sabnzbd.database.scheduled_history_purge()
# Purge links older than 3 days
sabnzbd.rss.expired_purge()
# Drop leftover records of feeds that are no longer configured
sabnzbd.rss.purge_removed_feeds()
# Start SSDP and Bonjour if SABnzbd isn't listening on localhost only
if sabnzbd.cfg.enable_broadcast() and not misc.is_localhost(cfg.web_host()):
Regular → Executable
+6 -1
View File
@@ -72,7 +72,12 @@ def decode_par2(parfile: str) -> list[str]:
new_path = os.path.join(dirname, md5of16k[file_md5of16k])
# Make sure it's a unique name
unique_filename = get_unique_filename(new_path)
renamer(filepath, unique_filename)
# Untrusted par2 name: contain it, skip on traversal
try:
renamer(filepath, unique_filename, create_local_directories=True)
except OSError:
logging.info("Skipping par2 rename of %s to %s", filepath, unique_filename)
continue
new_files.append(unique_filename)
return new_files
+55 -5
View File
@@ -210,13 +210,37 @@ for i in range(1, 32):
CH_ILLEGAL_WIN += chr(i)
def sanitize_filename(filename: str) -> str:
def sanitize_filename(filename: str, allow_subdirs: bool = False) -> str:
"""Return filename with illegal chars converted to legal ones
and with the par2 extension always in lowercase
and with the par2 extension always in lowercase.
With allow_subdirs the forward slashes that par2 uses to separate sub-directories are kept
and every part is sanitized on its own. The result is always local to the current folder:
empty parts, "." and ".." are dropped, so a leading slash or any amount of traversal can
never produce a name that points outside of it. The admin folder is dropped as well, so
it can never point into it either.
"""
if not filename:
return filename
if allow_subdirs:
# Par2 always uses a forward slash, no matter which platform created the set
parts = []
for part in filename.split("/"):
if part in ("", os.curdir):
continue
if part == os.pardir:
logging.info("Dropping directory traversal from name %s", filename)
continue
if part.lower() == JOB_ADMIN.lower():
# Never let a name point into the admin folder, its files are pickle-loaded
logging.info("Dropping admin folder from name %s", filename)
continue
parts.append(sanitize_filename(part))
# Nothing usable left, or no sub-directories after all
if not parts:
return "unknown"
return os.path.join(*parts)
filename = unicode_nfc_normalize(filename)
illegal = CH_ILLEGAL
@@ -455,6 +479,23 @@ def same_directory(a: str, b: str) -> int:
return is_subfolder
def points_into_admin_dir(path: str, base: str) -> bool:
"""Return True if path is, or is inside, an admin folder somewhere below base.
Both sides are resolved first, because the name alone cannot be trusted: on Windows
an NTFS 8.3 alias ("__ADMI~1") and on any platform a link point at a directory that
is named differently than the path says. Resolving also settles any case difference.
"""
# realpath() only keeps the \\?\ prefix if it was there to begin with, so clip both
# sides: comparing a prefixed path to a plain one would put the admin folder out of
# sight and let it pass
try:
relative = os.path.relpath(clip_path(os.path.realpath(path)), clip_path(os.path.realpath(base)))
except ValueError:
# Windows only: resolving ended up on another drive, so it left base altogether
return True
return JOB_ADMIN.lower() in relative.lower().split(os.sep)
def is_network_path(path: str) -> bool:
"""Check weither a path is a network path.
On Windows, use win32 functions to detect users that try to avoid this detection by using a mapped drive letter.
@@ -923,12 +964,21 @@ def renamer(old: str, new: str, create_local_directories: bool = False) -> str:
oldpath, _ = os.path.split(old)
# Check not outside directory
# In case of "same_file() == 1": same directory, so nothing to do
if same_directory(oldpath, path) == 0:
location = same_directory(oldpath, path)
if location == 0:
# Outside current directory, this is most likely malicious
logging.error(T("Blocked attempt to create directory %s"), path)
raise OSError("Refusing to go outside directory")
elif same_directory(oldpath, path) == 2:
# Sub-directory, so create if does not yet exist:
# Refuse the admin folder, it is pickle-loaded. Check the whole new path: if the
# last element is the admin folder itself, the move below would put the file
# inside it, since shutil.move accepts a directory as its target.
if points_into_admin_dir(new, oldpath):
logging.error(T("Blocked attempt to create directory %s"), path)
raise OSError("Refusing to go into admin directory")
if location == 2:
# Sub-directory, create if does not yet exist:
create_all_dirs(path)
logging.debug('Renaming "%s" to "%s"', old, new)
+47 -37
View File
@@ -30,6 +30,7 @@ import socket
import ssl
import functools
import copy
import secrets
from random import randint
from xml.sax.saxutils import escape
from Cheetah.Template import Template
@@ -110,6 +111,7 @@ def secured_expose(
check_configlock: bool = False,
check_for_login: bool = True,
check_api_key: bool = False,
api_route: bool = False,
access_type: int = 4,
) -> Callable | str:
"""Wrapper for both cherrypy.expose and login/access check"""
@@ -119,6 +121,7 @@ def secured_expose(
check_configlock=check_configlock,
check_for_login=check_for_login,
check_api_key=check_api_key,
api_route=api_route,
access_type=access_type,
)
@@ -182,7 +185,7 @@ def secured_expose(
# Some pages need correct API key
if check_api_key:
if msg := check_apikey(kwargs):
if msg := check_apikey(kwargs, api_route=api_route):
cherrypy.response.status = 403
if cfg.api_warnings():
return msg
@@ -262,8 +265,10 @@ def check_hostname():
return False
# Create a more unique ID for each instance
COOKIE_SECRET = str(randint(1000, 100000) * os.getpid())
# Per-process secret used to sign login cookies. Regenerated on every start,
# so sessions do not survive a restart (as before), but with enough entropy
# that a captured cookie cannot be brute-forced back to the secret.
COOKIE_SECRET = secrets.token_hex(32)
def remote_ip_from_xff(xff_ips: list[str]) -> str:
@@ -295,23 +300,25 @@ def set_login_cookie(remove=False, remember_me=False):
else:
remote_ip = cherrypy.request.remote.ip
cookie_str = utob(str(salt) + remote_ip + COOKIE_SECRET)
cherrypy.response.cookie["login_cookie"] = hashlib.sha1(cookie_str).hexdigest()
cherrypy.response.cookie["login_cookie"]["path"] = "/"
cherrypy.response.cookie["login_cookie"]["httponly"] = 1
cherrypy.response.cookie["login_salt"] = salt
cherrypy.response.cookie["login_salt"]["path"] = "/"
cherrypy.response.cookie["login_salt"]["httponly"] = 1
# If we want to be remembered
if remember_me:
cherrypy.response.cookie["login_cookie"]["max-age"] = 3600 * 24 * 14
cherrypy.response.cookie["login_salt"]["max-age"] = 3600 * 24 * 14
# To remove
if remove:
# Never emit a valid cookie/salt pair on logout
cherrypy.response.cookie["login_cookie"] = ""
cherrypy.response.cookie["login_salt"] = ""
cherrypy.response.cookie["login_cookie"]["expires"] = 0
cherrypy.response.cookie["login_salt"]["expires"] = 0
else:
cookie_str = utob(str(salt) + remote_ip + COOKIE_SECRET)
cherrypy.response.cookie["login_cookie"] = hashlib.sha1(cookie_str).hexdigest()
cherrypy.response.cookie["login_salt"] = salt
# If we want to be remembered
if remember_me:
cherrypy.response.cookie["login_cookie"]["max-age"] = 3600 * 24 * 14
cherrypy.response.cookie["login_salt"]["max-age"] = 3600 * 24 * 14
cherrypy.response.cookie["login_cookie"]["path"] = "/"
cherrypy.response.cookie["login_cookie"]["httponly"] = 1
cherrypy.response.cookie["login_salt"]["path"] = "/"
cherrypy.response.cookie["login_salt"]["httponly"] = 1
def check_login_cookie():
@@ -369,31 +376,34 @@ def set_auth(conf):
conf.update({"tools.auth_basic.on": False})
def check_apikey(kwargs):
"""Check API-key or NZB-key
Return None when OK, otherwise an error message
"""
mode = kwargs.get("mode", "")
name = kwargs.get("name", "")
# Lookup required access level for the specific api-call
req_access = sabnzbd.api.api_level(mode, name)
if not check_access(req_access, warn_user=True):
return _MSG_ACCESS_DENIED
# Skip for auth and version calls
if mode in ("version", "auth"):
return None
# First check API-key, if OK that's sufficient
def check_apikey(kwargs, api_route: bool = False):
"""Check API-key or NZB-key, return None when OK, else an error message.
Only the real /api route trusts "mode"; elsewhere it's attacker-controlled,
so a valid API-key is always required."""
key = kwargs.get("apikey")
if api_route:
mode = kwargs.get("mode", "")
name = kwargs.get("name", "")
req_access = sabnzbd.api.api_level(mode, name)
if not check_access(req_access, warn_user=True):
return _MSG_ACCESS_DENIED
# Skip for auth and version calls
if mode in ("version", "auth"):
return None
# NZB-key suffices for nzb-level calls
if req_access == 1 and key and key == cfg.nzb_key():
return None
# A valid API-key is required for everything else
if not key:
log_warning_and_ip(
T("API Key missing, please enter the api key from Config->General into your 3rd party program:")
)
return _MSG_APIKEY_REQUIRED
elif req_access == 1 and key == cfg.nzb_key():
return None
elif key == cfg.api_key():
return None
else:
@@ -492,7 +502,7 @@ class MainPage:
sabnzbd.shutdown_program()
return T("SABnzbd shutdown finished")
@secured_expose(check_api_key=True, access_type=1)
@secured_expose(check_api_key=True, api_route=True, access_type=1)
def api(self, **kwargs):
"""Redirect to API-handler, we check the access_type in the API-handler"""
return api_handler(kwargs)
+7 -1
View File
@@ -61,6 +61,7 @@ from sabnzbd.constants import (
DEF_ARTICLE_CACHE_MAX,
REPAIR_REQUEST,
GUESSIT_SORT_TYPES,
PP_LOOKUP,
)
import sabnzbd.config as config
import sabnzbd.cfg as cfg
@@ -244,7 +245,7 @@ def cat_pp_script_sanitizer(
cat: Optional[str] = None,
pp: Optional[int | str] = None,
script: Optional[str] = None,
) -> tuple[Optional[int | str], Optional[str], Optional[str]]:
) -> tuple[Optional[str], Optional[int], Optional[str]]:
"""Basic sanitizer from outside input to a bit more predictable values"""
# * and Default are valid values
if safe_lower(cat) in ("", "none"):
@@ -253,6 +254,11 @@ def cat_pp_script_sanitizer(
# Cannot use "not pp" because pp can also be 0
if safe_lower(pp) in ("", "-1", "none"):
pp = None
else:
# Only accept a valid pp value (key of PP_LOOKUP)
pp = int_conv(pp)
if pp not in PP_LOOKUP:
pp = None
# Check for valid script is performed in NzbObject init
if not script or safe_lower(script) == "default":
+7 -1
View File
@@ -1867,7 +1867,13 @@ def sfv_check(sfvs: list[str], nzo: NzbObject) -> bool:
if calculated_crc32.get(nzf.filename, "") == sfv_parse_results[file]:
try:
logging.debug("SFV-check will rename %s to %s", nzf.filename, file)
renamer(os.path.join(nzo.download_path, nzf.filename), os.path.join(nzo.download_path, file))
# Untrusted sfv name: normalize separators and contain
file = os.path.normpath(file)
renamer(
os.path.join(nzo.download_path, nzf.filename),
os.path.join(nzo.download_path, file),
create_local_directories=True,
)
renames[file] = nzf.filename
nzf.filename = file
result &= True
+11
View File
@@ -89,6 +89,8 @@ from sabnzbd.filesystem import (
remove_data,
get_ext,
create_work_name,
same_directory,
points_into_admin_dir,
RAR_RE,
)
from sabnzbd.par2file import FilePar2Info, has_par2_in_filename, analyse_par2, parse_par2_file, is_par2_file
@@ -1454,6 +1456,15 @@ class NzbObject(TryList):
candidate = f"{base_name}.{num}{ext}"
path = os.path.join(directory, candidate)
num += 1
if subdir := os.path.dirname(candidate):
# sanitize_filename() keeps the name local, so this should never trigger
if same_directory(directory, os.path.dirname(path)) == 0:
raise ValueError("Refusing to write %s outside of %s" % (candidate, directory))
if points_into_admin_dir(path, directory):
raise ValueError("Refusing to write %s into the admin folder" % candidate)
create_all_dirs(os.path.join(directory, subdir))
self.filenames.add(candidate)
return path
+38 -14
View File
@@ -621,10 +621,10 @@ class RSSRepository:
"""
Expire G/B links that are not in new_jobs (mark them 'X')
Expired links older than 3 days are removed
Expired links older than 7 days are removed
"""
now = datetime.datetime.now(datetime.timezone.utc)
limit = int((now - datetime.timedelta(days=3)).timestamp())
limit = int((now - datetime.timedelta(days=7)).timestamp())
if new_urls:
# Create temporary table for all new URLs
@@ -635,6 +635,22 @@ class RSSRepository:
placeholders = ",".join(["(?)"] * len(batch))
self.db.execute(f"INSERT INTO temp_urls(url) VALUES {placeholders}", batch)
# Refresh seen_at for everything still listed in the feed. Entries in a terminal
# state are skipped during evaluation, so this is the only place they are touched
# and without it they would be purged while still present in the feed.
self.db.execute(
"""
UPDATE rss
SET seen_at = ?
WHERE feed = ?
AND url IN (SELECT url FROM temp_urls)
""",
(
int(datetime.datetime.now(datetime.timezone.utc).timestamp()),
feed,
),
)
# Update rss to mark G/B not in temp_urls as X
self.db.execute(
"""
@@ -686,6 +702,7 @@ class RSSRepository:
def get_feed_jobs(
self,
feed: Optional[str] = None,
archive: Optional[bool] = None,
search: Optional[str] = None,
states: Optional[list[RSSState]] = None,
) -> Generator[ResolvedEntry, Any, None]:
@@ -693,6 +710,11 @@ class RSSRepository:
command_args = []
where_clauses = []
if archive:
where_clauses.append("archived_at IS NOT NULL")
else:
where_clauses.append("archived_at IS NULL")
if search is not None:
where_clauses.append("title LIKE ?")
command_args.append(convert_search(search))
@@ -847,10 +869,13 @@ class RSSRepository:
)
return bool(self.db.cursor.fetchone()["found"])
def expired_purge(self):
"""Removed expired links from all feeds"""
def purge_removed_feeds(self):
"""Remove all records of feeds that are no longer configured"""
configured = set(config.get_rss())
for feed in self.get_feeds():
self.remove_obsolete(feed)
if feed not in configured:
logging.debug("Purging records of removed feed %s", feed)
self.clear_feed(feed)
def import_rss_records(self):
"""Migrate old RSS database"""
@@ -981,7 +1006,6 @@ class RSSReader:
new_links.add(entry.link)
downloaded = self._process_entry(
repo,
feed_entry=entry,
filters=filters,
first=first,
@@ -998,7 +1022,8 @@ class RSSReader:
if new_downloads and cfg.email_rss() and not force:
emailer.rss_mail(feed, new_downloads)
repo.remove_obsolete(feed, new_links, purge_downloaded=True)
if readout:
repo.remove_obsolete(feed, new_links, purge_downloaded=True)
return ""
@@ -1119,8 +1144,6 @@ class RSSReader:
def _process_entry(
self,
repo: RSSRepository,
*,
feed_entry: ResolvedEntry,
filters: FeedConfig,
first: bool,
@@ -1178,8 +1201,9 @@ class RSSReader:
initial_scan=initial_scan,
)
repo.upsert(resolved_entry)
self.enqueue_download(repo, resolved_entry)
with sabnzbd.rss.rss_repository() as repo:
repo.upsert(resolved_entry)
self.enqueue_download(repo, resolved_entry)
return bool(evaluation.matched and should_download)
@@ -1266,10 +1290,10 @@ def special_rss_site(url: str) -> bool:
return bool(cfg.rss_filenames() or match_str(url, cfg.rss_odd_titles()))
def expired_purge():
"""Purge links older than 3 days"""
def purge_removed_feeds():
"""Purge records of feeds that are no longer configured"""
with rss_repository() as repo:
repo.expired_purge()
repo.purge_removed_feeds()
@contextmanager
+2 -2
View File
@@ -6,5 +6,5 @@
# You MUST use double quotes (so " and not ')
# Do not forget to update the appdata file for every major release!
__version__ = "5.1.0RC2"
__baseline__ = "unknown"
__version__ = "5.1.2"
__baseline__ = "b53a60f92d17ea49b2cdef310b2d359bba1858d8"
+160 -1
View File
@@ -38,7 +38,7 @@ import sabnzbd
import sabnzbd.cfg
from sabnzbd import cfg
import sabnzbd.filesystem as filesystem
from sabnzbd.constants import DEF_FOLDER_MAX, DEF_FILE_MAX
from sabnzbd.constants import DEF_FOLDER_MAX, DEF_FILE_MAX, JOB_ADMIN
# Set the global uid for fake filesystems to a non-root user;
# by default this depends on the user running pytest.
@@ -123,6 +123,77 @@ class TestFileFolderNameSanitizer:
assert filesystem.sanitize_filename("../") == ".._"
assert filesystem.sanitize_filename("../test") == ".._test"
@pytest.mark.parametrize("platform", ["win32", "macos", "linux"])
@pytest.mark.platform()
def test_file_allow_subdirs(self, platform):
"""Par2 uses "/" to separate sub-directories, no matter which platform created the set"""
assert filesystem.sanitize_filename("sub/test.rar", allow_subdirs=True) == os.path.join("sub", "test.rar")
assert filesystem.sanitize_filename("sub/deeper/test.rar", allow_subdirs=True) == os.path.join(
"sub", "deeper", "test.rar"
)
# No sub-directory at all, or nothing but separators
assert filesystem.sanitize_filename("test.rar", allow_subdirs=True) == "test.rar"
assert filesystem.sanitize_filename("a//b.rar", allow_subdirs=True) == os.path.join("a", "b.rar")
assert filesystem.sanitize_filename("sub/./test.rar", allow_subdirs=True) == os.path.join("sub", "test.rar")
# Every part is sanitized on its own, chr(0) is illegal on all platforms
assert filesystem.sanitize_filename("sub" + chr(0) + "1/test" + chr(0) + "2.rar", allow_subdirs=True) == (
os.path.join("sub_1", "test_2.rar")
)
@pytest.mark.parametrize(
"hostile_name",
[
"/test.rar",
"//test.rar",
"../test.rar",
"../../../../../../etc/shadow",
"sub/../../test.rar",
"sub/../../../sub/test.rar",
"./../test.rar",
"../..",
"../",
"/",
"//",
"/../",
"...",
"....",
],
)
@pytest.mark.parametrize("platform", ["win32", "macos", "linux"])
@pytest.mark.platform()
def test_file_allow_subdirs_cannot_escape(self, platform, hostile_name):
"""Whatever the par2 claims, the result has to stay inside the folder it is used in.
Joining it onto any base directory must never point above that base."""
result = filesystem.sanitize_filename(hostile_name, allow_subdirs=True)
assert result, "an empty result would resolve to the base directory itself"
assert not os.path.isabs(result)
assert os.pardir not in result.split(os.sep)
# The real test: it cannot climb out of whatever it gets joined to
base = os.path.join(os.sep + "downloads", "incomplete", "job")
resolved = os.path.normpath(os.path.join(base, result))
assert resolved.startswith(base + os.sep), "%s escaped to %s" % (hostile_name, resolved)
@pytest.mark.parametrize(
"hostile_name",
[
JOB_ADMIN,
JOB_ADMIN + "/__verified__",
JOB_ADMIN.lower() + "/__verified__",
"sub/" + JOB_ADMIN + "/__verified__",
JOB_ADMIN + "/deeper/__verified__",
],
)
@pytest.mark.parametrize("platform", ["win32", "macos", "linux"])
@pytest.mark.platform()
def test_file_allow_subdirs_cannot_enter_admin(self, platform, hostile_name):
"""The admin folder is pickle-loaded, so a par2 name must never point into it"""
result = filesystem.sanitize_filename(hostile_name, allow_subdirs=True)
assert result, "an empty result would resolve to the base directory itself"
assert JOB_ADMIN.lower() not in result.lower().split(os.sep)
@pytest.mark.platform("linux")
def test_folder_illegal_chars_linux(self):
assert filesystem.sanitize_foldername('test"aftertest') == "test_aftertest"
@@ -443,6 +514,59 @@ class TestSameDirectory:
assert 0 == filesystem.same_directory("/test/../Home", "/home")
class TestPointsIntoAdminDir:
def test_by_name(self, tmp_path):
base = str(tmp_path)
assert filesystem.points_into_admin_dir(os.path.join(base, JOB_ADMIN), base)
assert filesystem.points_into_admin_dir(os.path.join(base, JOB_ADMIN, "__verified__"), base)
assert filesystem.points_into_admin_dir(os.path.join(base, "sub", JOB_ADMIN, "__verified__"), base)
def test_regular_names_are_left_alone(self, tmp_path):
base = str(tmp_path)
assert not filesystem.points_into_admin_dir(os.path.join(base, "testfile.rar"), base)
assert not filesystem.points_into_admin_dir(os.path.join(base, "sub", "testfile.rar"), base)
# Only a full part counts, not a name that merely starts with it
assert not filesystem.points_into_admin_dir(os.path.join(base, JOB_ADMIN + "-data", "testfile.rar"), base)
def test_link_cannot_hide_it(self, tmp_path):
"""On Windows an NTFS 8.3 alias ("__ADMI~1") points at the admin folder under a
different name, exactly like a link does here, so the name cannot be trusted"""
base = str(tmp_path)
admin_dir = os.path.join(base, JOB_ADMIN)
os.mkdir(admin_dir)
linkname = os.path.join(base, "notadmin")
os.symlink(admin_dir, linkname)
assert filesystem.points_into_admin_dir(linkname, base)
assert filesystem.points_into_admin_dir(os.path.join(linkname, "__verified__"), base)
@pytest.mark.skipif(not sys.platform.startswith("win"), reason="NTFS 8.3 aliases only exist on Windows")
def test_ntfs_8dot3_alias_cannot_hide_it(self, tmp_path):
"""The real thing the link above stands in for: NTFS keeps an 8.3 alias for every
long name, so "__ADMI~1" reaches the admin folder without ever spelling it out"""
import win32api
base = str(tmp_path)
admin_dir = os.path.join(base, JOB_ADMIN)
os.mkdir(admin_dir)
# Ask the filesystem for the alias instead of assuming what it generated
alias = os.path.basename(win32api.GetShortPathName(admin_dir))
if alias.lower() == JOB_ADMIN.lower():
pytest.skip("8.3 name creation is disabled on this volume")
assert filesystem.points_into_admin_dir(os.path.join(base, alias), base)
assert filesystem.points_into_admin_dir(os.path.join(base, alias, "__verified__"), base)
# And the rename that the alias was meant to sneak through has to fail
filename = os.path.join(base, "myfile.txt")
Path(filename).touch()
with pytest.raises(OSError):
filesystem.renamer(filename, os.path.join(base, alias, "__verified__"), create_local_directories=True)
assert os.path.isfile(filename)
assert not os.listdir(admin_dir)
class TestFirstExistingPath:
def test_existing_path(self, tmp_path):
assert filesystem.first_existing_path(str(tmp_path)) == str(tmp_path)
@@ -1203,6 +1327,41 @@ class TestRenamer:
assert os.path.isfile(filename)
assert not os.path.isfile(newfilename)
# ... renaming into the admin folder is not allowed either
admin_dir = os.path.join(dirname, JOB_ADMIN)
os.mkdir(admin_dir)
Path(filename).touch()
newfilename = os.path.join(admin_dir, "__verified__")
try:
filesystem.renamer(filename, newfilename, create_local_directories=True)
except Exception:
pass
assert os.path.isfile(filename)
assert not os.path.isfile(newfilename)
# ... nor is naming the admin folder itself: a move into an existing directory
# keeps the old basename, so this would end up inside the admin folder as well
Path(filename).touch()
try:
filesystem.renamer(filename, admin_dir, create_local_directories=True)
except Exception:
pass
assert os.path.isfile(filename)
assert not os.listdir(admin_dir)
# ... and not under another name that resolves to it, such as a link. On Windows
# an NTFS 8.3 alias ("__ADMI~1") reaches the admin folder the very same way.
linkname = os.path.join(dirname, "notadmin")
os.symlink(admin_dir, linkname)
Path(filename).touch()
try:
filesystem.renamer(filename, os.path.join(linkname, "__verified__"), create_local_directories=True)
except Exception:
pass
assert os.path.isfile(filename)
assert not os.listdir(admin_dir)
os.remove(linkname)
# Cleanup working directory
shutil.rmtree(dirname)
+54
View File
@@ -228,3 +228,57 @@ class TestInterfaceFunctions:
assert interface.remote_ip_from_xff(xff_ips) is expected_result
_func()
@pytest.mark.config({"api_key": "the_real_api_key", "nzb_key": "the_real_nzb_key"})
@pytest.mark.parametrize(
"api_route, kwargs, expected",
[
# /api route: version/auth public, NZB-key valid for nzb-level calls
(True, {"mode": "version"}, None),
(True, {"mode": "auth"}, None),
(True, {"mode": "addfile", "apikey": "the_real_nzb_key"}, None),
(True, {"mode": "queue", "apikey": "the_real_api_key"}, None),
(True, {"mode": "queue"}, interface._MSG_APIKEY_REQUIRED),
(True, {"mode": "queue", "apikey": "wrong"}, interface._MSG_APIKEY_INCORRECT),
# Web-ui routes must ignore 'mode': no version/auth or NZB-key bypass
(False, {"mode": "version"}, interface._MSG_APIKEY_REQUIRED),
(False, {"mode": "auth"}, interface._MSG_APIKEY_REQUIRED),
(False, {"mode": "addfile", "apikey": "the_real_nzb_key"}, interface._MSG_APIKEY_INCORRECT),
(False, {"apikey": "the_real_api_key"}, None),
(False, {"mode": "version", "apikey": "the_real_api_key"}, None),
(False, {"apikey": "wrong"}, interface._MSG_APIKEY_INCORRECT),
(False, {}, interface._MSG_APIKEY_REQUIRED),
],
)
def test_check_apikey_ignores_mode_off_api_route(self, api_route, kwargs, expected):
"""'mode' is only trusted on the real /api route, not on web-ui handlers."""
cherrypy.request.remote.ip = "127.0.0.1"
cherrypy.request.headers.update({"X-Forwarded-For": None})
cherrypy.request.remote_label = "127.0.0.1 [test]"
assert interface.check_apikey(kwargs, api_route=api_route) == expected
@pytest.mark.config({"verify_xff_header": False})
def test_logout_does_not_leak_valid_cookie(self):
"""A logout must never emit a cookie/salt pair that passes check_login_cookie.
The Set-Cookie header is readable regardless of its expiry, so leaking valid
values there is an authentication bypass (harvest via logout, then replay)."""
cherrypy.request.remote.ip = "10.11.12.13"
cherrypy.request.headers.update({"X-Forwarded-For": None})
# Sanity check: a real login produces a cookie that validates
cherrypy.response.cookie.clear()
interface.set_login_cookie()
cherrypy.request.cookie["login_cookie"] = cherrypy.response.cookie["login_cookie"].value
cherrypy.request.cookie["login_salt"] = cherrypy.response.cookie["login_salt"].value
assert interface.check_login_cookie() is True
# Logout must blank out the values, not emit a working hash/salt
cherrypy.response.cookie.clear()
interface.set_login_cookie(remove=True)
assert cherrypy.response.cookie["login_cookie"].value == ""
assert cherrypy.response.cookie["login_salt"].value == ""
# Replaying whatever the logout response carried must fail authentication
cherrypy.request.cookie["login_cookie"] = cherrypy.response.cookie["login_cookie"].value
cherrypy.request.cookie["login_salt"] = cherrypy.response.cookie["login_salt"].value
assert interface.check_login_cookie() is False
+13 -3
View File
@@ -109,16 +109,26 @@ class TestMisc:
(None, None, None, (None, None, None)),
("", "", "", (None, None, None)),
("none", "-1", "default", (None, None, None)),
("SomeCategory", "5", "SomeScript", ("SomeCategory", "5", "SomeScript")),
("SomeCategory", "1", "SomeScript", ("SomeCategory", 1, "SomeScript")),
("none", 0, "default", (None, 0, None)),
("Movies", "", "default", ("Movies", None, None)),
("", "10", "default", (None, "10", None)),
("none", "15", "", (None, "15", None)),
("none", 0, "Default", (None, 0, None)),
("other", "-1", "Default", ("other", None, None)),
("none", "None", "default", (None, None, None)),
("some", "none", "script", ("some", None, "script")),
("none", "NONE", "Default", (None, None, None)),
# pp must be a PP_LOOKUP key or None
("none", "2", "default", (None, 2, None)),
("none", 3, "default", (None, 3, None)),
# Out-of-range ints are invalid
("", "10", "default", (None, None, None)),
("none", "15", "", (None, None, None)),
("none", 4, "default", (None, None, None)),
# Non-numeric never passes as a string
("none", "-c", "default", (None, 0, None)),
("none", "echo pwned", "default", (None, 0, None)),
("none", "2; rm -rf /", "default", (None, 0, None)),
("none", "1.5", "default", (None, 0, None)),
],
)
def test_cat_pp_script_sanitizer(self, cat, pp, script, expected):
+26 -2
View File
@@ -51,8 +51,31 @@ class TestNewsUnpackFunctions:
assert not newsunpack.is_sfv_file("tests/data/only_comments.sfv")
assert not newsunpack.is_sfv_file("tests/data/random.bin")
def test_is_sevenfile(self):
# False, because the command is not set
def test_sfv_check_blocks_path_traversal(self, tmp_path):
"""A traversing SFV filename must not move a file out of the job directory"""
download_path = str(tmp_path)
obfuscated_name = "6f1ed002ab5595859014ebf0951522d9"
obfuscated_path = os.path.join(download_path, obfuscated_name)
with open(obfuscated_path, "wb") as test_file:
test_file.write(b"payload")
# SFV entry with matching crc32 but a traversing target name
sfv_path = os.path.join(download_path, "check.sfv")
with open(sfv_path, "w") as sfv_file:
sfv_file.write("../escaped.bin deadbeef\n")
nzf = mock.Mock(filename=obfuscated_name, filepath=obfuscated_path, crc32=0xDEADBEEF)
nzo = mock.Mock(download_path=download_path, finished_files=[nzf])
assert newsunpack.sfv_check([sfv_path], nzo) is False
assert not os.path.exists(os.path.join(download_path, os.pardir, "escaped.bin"))
assert os.path.exists(obfuscated_path)
def test_is_sevenfile(self, monkeypatch):
# False, because the command is not set. Force it explicitly: SEVENZIP_COMMAND
# is a module global that another test in this class may have populated via
# find_programs(), and under pytest-xdist tests share no ordering guarantee.
monkeypatch.setattr(newsunpack, "SEVENZIP_COMMAND", None)
assert not newsunpack.SEVENZIP_COMMAND
assert not newsunpack.is_sevenfile("tests/data/test_7zip/testfile.7z")
@@ -65,6 +88,7 @@ class TestNewsUnpackFunctions:
assert newsunpack.is_sevenfile("tests/data/test_7zip/testfile.7z")
def test_sevenzip(self):
newsunpack.find_programs(".")
testzip = newsunpack.SevenZip("tests/data/test_7zip/testfile.7z")
assert testzip.namelist() == ["My_Test_Download.bin"]
# Basic check that we can get data from the 7zip
+129 -3
View File
@@ -734,9 +734,15 @@ class TestRSS:
assert job_after_clear.archived_at is not None
assert job_after_clear.is_downloaded
# get_jobs should return all jobs for a feed
# get_jobs should return all non-archived jobs for a feed
jobs_from_get_jobs = list(repo.get_feed_jobs(feed=feed))
assert {j.link for j in jobs_from_get_jobs} == set(links_by_feed[feed])
assert {j.link for j in jobs_from_get_jobs} == {
job_link for job_link in links_by_feed[feed] if job_link is not link
}
# get_jobs archive should return only archived jobs for a feed
jobs_from_get_jobs = list(repo.get_feed_jobs(feed=feed, archive=True))
assert {j.link for j in jobs_from_get_jobs} == {link}
# is_duplicate should detect similar jobs in other feeds
duplicate_candidate = ResolvedEntry(
@@ -775,7 +781,7 @@ class TestRSS:
now = datetime.datetime.now(datetime.timezone.utc)
age = now - datetime.timedelta(weeks=52)
old_seen_at = now - datetime.timedelta(days=4)
old_seen_at = now - datetime.timedelta(days=8)
new_seen_at = now - datetime.timedelta(days=1)
# Old good item that should be kept because it is part of the new_urls set
@@ -1039,3 +1045,123 @@ class TestRSS:
# Shared link must only appear once
assert links == {shared_link, a_only_link, b_only_link}
def test_purge_removed_feeds_only_drops_unconfigured_feeds(self, tmp_rss):
"""Records should only be dropped for feeds that are no longer configured."""
repo, _reader = tmp_rss
configured_feed = "ConfiguredFeed"
removed_feed = "RemovedFeed"
self.setup_rss(configured_feed, "http://example.test/rss.xml")
age = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(weeks=52)
old_seen_at = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(days=30)
for feed in (configured_feed, removed_feed):
repo.upsert(
ResolvedEntry(
feed=feed,
link=f"http://example.test/{feed}/job",
title=f"{feed} job",
infourl=None,
size=10,
age=age,
seen_at=old_seen_at,
season=1,
episode=1,
category=None,
state=RSSState.GOOD,
)
)
repo.purge_removed_feeds()
assert set(repo.get_feeds()) == {configured_feed}
def test_process_feed_without_readout_keeps_stored_jobs(self, httpserver: HTTPServer, tmp_rss):
"""Replaying stored jobs (readout=False) must not expire or purge anything."""
repo, reader = tmp_rss
feed_name = "NoReadoutFeed"
feed_xml = """<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>NoReadout</title>
<item>
<title>New.Show.S01E01.720p</title>
<link>http://example.test/no-readout/current</link>
<guid>http://example.test/info/no-readout-current</guid>
<category>tv</category>
<pubDate>Wed, 01 Jan 2025 00:00:00 GMT</pubDate>
</item>
</channel>
</rss>
"""
httpserver.expect_request("/rss_no_readout.xml").respond_with_data(feed_xml, content_type="application/rss+xml")
self.setup_rss(feed_name, httpserver.url_for("/rss_no_readout.xml"))
now = datetime.datetime.now(datetime.timezone.utc)
old_url = "http://example.test/no-readout/expired"
repo.upsert(
ResolvedEntry(
feed=feed_name,
link=old_url,
title="Old.Show.S01E01.720p",
infourl=None,
size=10,
age=now - datetime.timedelta(weeks=52),
seen_at=now - datetime.timedelta(days=8),
season=1,
episode=1,
category=None,
state=RSSState.EXPIRED,
)
)
assert reader.process_feed(feed_name, readout=False) == ""
assert repo.find_job_by_url(feed_name, old_url) is not None
# A real readout does not find the link anymore, so it gets purged
assert reader.process_feed(feed_name, readout=True) == ""
assert repo.find_job_by_url(feed_name, old_url) is None
def test_downloaded_item_still_in_feed_is_not_redownloaded(self, httpserver: HTTPServer, tmp_rss, mocker):
"""An item that stays in the feed must survive the retention period and not be grabbed twice."""
repo, reader = tmp_rss
feed_name = "LongLivedFeed"
link = "http://example.test/long-lived"
feed_xml = f"""<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>LongLived</title>
<item>
<title>Show.S01E01.720p</title>
<link>{link}</link>
<guid>http://example.test/info/long-lived</guid>
<category>tv</category>
<pubDate>Wed, 01 Jan 2025 00:00:00 GMT</pubDate>
</item>
</channel>
</rss>
"""
httpserver.expect_request("/rss_long_lived.xml").respond_with_data(feed_xml, content_type="application/rss+xml")
self.setup_rss(feed_name, httpserver.url_for("/rss_long_lived.xml"))
add_url = mocker.patch("sabnzbd.urlgrabber.add_url")
assert reader.process_feed(feed_name, download=True, force=True) == ""
assert add_url.call_count == 1
assert repo.find_job_by_url(feed_name, link).state is RSSState.DOWNLOADED
# Pretend the item was downloaded longer ago than the retention period, while
# it is still being served by the feed
stale = int((datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(days=30)).timestamp())
repo.db.execute("UPDATE rss SET seen_at = ? WHERE feed = ?", (stale, feed_name))
# Still being listed should refresh seen_at instead of purging the job
assert reader.process_feed(feed_name, download=True) == ""
job = repo.find_job_by_url(feed_name, link)
assert job is not None
assert job.state is RSSState.DOWNLOADED
assert job.seen_at.timestamp() > stale
# And it must not be picked up as a new job on the next scans
assert reader.process_feed(feed_name, download=True) == ""
assert add_url.call_count == 1