web: don't use carets (<,>) in URLs

Signed-off-by: Manos Pitsidianakis <manos@pitsidianak.is>
main
Manos Pitsidianakis 2024-01-26 16:13:40 +02:00
parent 4bc606236f
commit acb26c52da
Signed by: Manos Pitsidianakis
GPG Key ID: 7729C7707F7E09D0
4 changed files with 36 additions and 12 deletions

View File

@ -221,6 +221,33 @@ impl StripCarets for &str {
}
}
/// Trait for stripping carets ('<','>') from Message IDs inplace.
pub trait StripCaretsInplace {
/// If `self` is surrounded by carets, strip them.
fn strip_carets_inplace(self) -> Self;
}
impl StripCaretsInplace for &str {
fn strip_carets_inplace(self) -> Self {
let mut self_ref = self.trim();
if self_ref.starts_with('<') && self_ref.ends_with('>') {
self_ref = &self_ref[1..self_ref.len().saturating_sub(1)];
}
self_ref
}
}
impl StripCaretsInplace for String {
fn strip_carets_inplace(mut self) -> Self {
if self.starts_with('<') && self.ends_with('>') {
self.drain(0..1);
let len = self.len();
self.drain(len.saturating_sub(1)..len);
}
self
}
}
use percent_encoding::CONTROLS;
pub use percent_encoding::{utf8_percent_encode, AsciiSet};

View File

@ -658,7 +658,7 @@ impl Connection {
) -> Result<Option<DbVal<Post>>> {
let mut stmt = self.connection.prepare(
"SELECT *, strftime('%Y-%m', CAST(timestamp AS INTEGER), 'unixepoch') AS month_year \
FROM post WHERE list = ? AND message_id = ?;",
FROM post WHERE list = ?1 AND (message_id = ?2 OR concat('<', ?2, '>') = message_id);",
)?;
let ret = stmt
.query_row(rusqlite::params![&list_pk, &message_id], |row| {

View File

@ -19,7 +19,7 @@
use chrono::TimeZone;
use indexmap::IndexMap;
use mailpot::models::Post;
use mailpot::{models::Post, StripCarets, StripCaretsInplace};
use super::*;
@ -228,7 +228,7 @@ pub async fn list_post(
list_obj.set_safety(list_owners.as_slice(), &state.conf.administrators);
let context = minijinja::context! {
canonical_url => ListPostPath(ListPathIdentifier::from(list.id.clone()), msg_id.to_string()).to_crumb(),
canonical_url => ListPostPath(ListPathIdentifier::from(list.id.clone()), msg_id.to_string().strip_carets_inplace()).to_crumb(),
page_title => subject_ref,
description => &list.description,
list => Value::from_object(list_obj),
@ -239,8 +239,8 @@ pub async fn list_post(
to => &envelope.field_to_to_string(),
subject => &envelope.subject(),
trimmed_subject => subject_ref,
in_reply_to => &envelope.in_reply_to_display().map(|r| r.to_string().as_str().strip_carets().to_string()),
references => &envelope.references().into_iter().map(|m| m.to_string().as_str().strip_carets().to_string()).collect::<Vec<String>>(),
in_reply_to => &envelope.in_reply_to_display().map(|r| r.to_string().strip_carets_inplace()),
references => &envelope.references().into_iter().map(|m| m.to_string().strip_carets_inplace()).collect::<Vec<String>>(),
message_id => msg_id,
message => post.message,
timestamp => post.timestamp,

View File

@ -177,13 +177,10 @@ macro_rules! list_post_impl {
msg_id: Value,
) -> std::result::Result<Value, Error> {
urlize(state, {
let Some(msg_id) = msg_id.as_str().map(|s| {
if s.starts_with('<') && s.ends_with('>') {
s.to_string()
} else {
format!("<{s}>")
}
}) else {
let Some(msg_id) = msg_id
.as_str()
.map(|s| s.to_string().strip_carets_inplace())
else {
return Err(Error::new(
minijinja::ErrorKind::UnknownMethod,
"Second argument of list_post_path must be a string.",