Search treats % and _ as themselves (#296)

The changelog lines for #292, #293 and #295 ride along in this commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-19 18:12:58 +00:00
parent a21472730e
commit 44afc08b9d
2 changed files with 13 additions and 1 deletions

View File

@@ -40,10 +40,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Artwork a publisher sends without an image type, or as `application/octet-stream`, is shown when it is a JPEG, PNG, GIF or WebP, instead of initials (#279). - Artwork a publisher sends without an image type, or as `application/octet-stream`, is shown when it is a JPEG, PNG, GIF or WebP, instead of initials (#279).
- A 429 from TypeSafe holds every call back as it asks, instead of the pass going on asking (#277). - A 429 from TypeSafe holds every call back as it asks, instead of the pass going on asking (#277).
- Looking for items to read for Signals read every item on the server on each scan; it now reads only recent ones (#274). - Looking for items to read for Signals read every item on the server on each scan; it now reads only recent ones (#274).
- With the downloads folder out of reach during a clean-up, as when the array stops, every downloaded episode was taken as deleted by hand and forgotten; the clean-up now leaves them alone until the folder is back (#293).
- A feed address that serves something larger than 50 MB, such as an episode's file given as a feed, is refused as too large, instead of read whole into memory at every check (#295).
- Searching items for `%` or `_` finds those characters, instead of treating them as wildcards that matched nearly everything (#296).
### Security ### Security
- ammonia, which cleans publishers' HTML, updated to 4.2.4 for three advisories, among them XSS through SVG animation tags; rustls to 0.23.46 for five and h2 to 0.4.20 for one, in every connection ipx makes (#281). - ammonia, which cleans publishers' HTML, updated to 4.2.4 for three advisories, among them XSS through SVG animation tags; rustls to 0.23.46 for five and h2 to 0.4.20 for one, in every connection ipx makes (#281).
- The address of a file being downloaded, which for Patreon carries its signature, is sent to open pages and written to the log without its query, as other addresses already were (#292).
## [1.3.0] - 2026-10-16 ## [1.3.0] - 2026-10-16

View File

@@ -1222,8 +1222,13 @@ fn entries_from(
} }
let search = match search.map(|q| q.trim().to_lowercase()).filter(|q| !q.is_empty()) { let search = match search.map(|q| q.trim().to_lowercase()).filter(|q| !q.is_empty()) {
Some(q) => { Some(q) => {
// % and _ as typed, not as wildcards: "100%" matched anything starting 100 (#296).
let q = q.replace('\\', "\\\\").replace('%', "\\%").replace('_', "\\_");
let like = a.p(format!("%{q}%")); let like = a.p(format!("%{q}%"));
format!("(lower(coalesce(e.title, '')) LIKE {like} OR lower(coalesce(e.description, '')) LIKE {like})") format!(
"(lower(coalesce(e.title, '')) LIKE {like} ESCAPE '\\' \
OR lower(coalesce(e.description, '')) LIKE {like} ESCAPE '\\')"
)
} }
None => "true".into(), None => "true".into(),
}; };
@@ -3614,6 +3619,9 @@ mod tests {
assert_eq!(db.count_in(7, Some("f"), Filter::Downloaded, None).await.unwrap(), 1); assert_eq!(db.count_in(7, Some("f"), Filter::Downloaded, None).await.unwrap(), 1);
assert_eq!(db.count_in(7, Some("f"), Filter::Flagged, None).await.unwrap(), 1); assert_eq!(db.count_in(7, Some("f"), Filter::Flagged, None).await.unwrap(), 1);
assert_eq!(db.count_in(7, Some("f"), Filter::All, Some("dive")).await.unwrap(), 2); assert_eq!(db.count_in(7, Some("f"), Filter::All, Some("dive")).await.unwrap(), 2);
for wild in ["%", "_", "\\"] {
assert_eq!(db.count_in(7, Some("f"), Filter::All, Some(wild)).await.unwrap(), 0, "{wild} is only itself");
}
assert_eq!( assert_eq!(
db.count_in(7, Some("f"), Filter::All, Some("NOTES two")).await.unwrap(), db.count_in(7, Some("f"), Filter::All, Some("NOTES two")).await.unwrap(),
1, 1,