Compare commits

..

1 Commits

Author SHA1 Message Date
link2xt
ee0e7f4c43 feat: remove Content-Description and Content-Disposition from multipart/encrypted parts
This is not required by <https://datatracker.ietf.org/doc/html/rfc3156>.

Looks like Content-Description and Content-Disposition are
the same as what Thunderbird produces,
e.g. in `test-data/message/thunderbird_encrypted_signed.eml`,
and the same values also made it into Autocrypt spec.

Content-Description is likely not used by anyone.

For Content-Disposition specification see <https://datatracker.ietf.org/doc/html/rfc2183>.
While it is explicitly allowed to set the filename for inline attachment,
it does not look useful to specify generic "encrypted.asc" explicitly.
Content-Disposition for the second part results in some webmail clients
showing the second part as an attachment with "encrypted.asc" filename.
They will likely show it differently after this change,
e.g. with a different filename or actually inline.
2026-05-09 19:36:31 +02:00
19 changed files with 54 additions and 53 deletions

View File

@@ -275,7 +275,7 @@ pub unsafe extern "C" fn dc_get_config(
.strdup()
} else {
match config::Config::from_str(&key)
.with_context(|| format!("Invalid key {key:?}"))
.with_context(|| format!("Invalid key {:?}", &key))
.log_err(ctx)
{
Ok(key) => ctx

View File

@@ -122,7 +122,7 @@ async fn poke_spec(context: &Context, spec: Option<&str>) -> bool {
let name_f = entry.file_name();
let name = name_f.to_string_lossy();
if name.ends_with(".eml") {
let path_plus_name = format!("{real_spec}/{name}");
let path_plus_name = format!("{}/{}", &real_spec, name);
println!("Import: {path_plus_name}");
if poke_eml_file(context, Path::new(&path_plus_name))
.await
@@ -133,11 +133,11 @@ async fn poke_spec(context: &Context, spec: Option<&str>) -> bool {
}
}
} else {
eprintln!("Import: Cannot open directory {real_spec:?}.");
eprintln!("Import: Cannot open directory \"{}\".", &real_spec);
return false;
}
}
println!("Import: {read_cnt} items read from {real_spec:?}.");
println!("Import: {} items read from \"{}\".", read_cnt, &real_spec);
if read_cnt > 0 {
context.emit_msgs_changed_without_ids();
}
@@ -179,7 +179,7 @@ async fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
msg.get_id(),
if msg.get_showpadlock() { "🔒" } else { "" },
if msg.has_location() { "📍" } else { "" },
contact_name,
&contact_name,
contact_id,
msgtext,
if msg.has_html() { "[HAS-HTML]" } else { "" },
@@ -221,7 +221,7 @@ async fn log_msg(context: &Context, prefix: impl AsRef<str>, msg: &Message) {
},
statestr,
downloadstate,
temp2,
&temp2,
);
}
@@ -561,7 +561,7 @@ pub async fn cmdline(context: Context, line: &str, chat_id: &mut ChatId) -> Resu
.map_or_else(String::new, |prefix| format!("{prefix}: ")),
summary.text,
statestr,
timestr,
&timestr,
if chat.is_sending_locations() {
"📍"
} else {

View File

@@ -432,7 +432,7 @@ async fn handle_cmd(
{
println!("Open the following url, set mail_pw to the generated token and server_flags to 2:\n{oauth2_url}");
} else {
println!("OAuth2 not available for {addr}.");
println!("OAuth2 not available for {}.", &addr);
}
} else {
println!("oauth2: set addr first.");

View File

@@ -794,7 +794,7 @@ impl Config {
.with_push_subscriber(push_subscriber.clone())
.build()
.await
.with_context(|| format!("failed to create context from file {dbfile:?}"))?;
.with_context(|| format!("failed to create context from file {:?}", &dbfile))?;
// Try to open without a passphrase,
// but do not return an error if account is passphare-protected.
ctx.open("".to_string()).await?;

View File

@@ -2529,7 +2529,7 @@ async fn prepare_msg_blob(context: &Context, msg: &mut Message) -> Result<()> {
// running numbers, etc.
let filename: String = match viewtype_orig {
Viewtype::Voice => format!(
"voice-messsage_{}.{suffix}",
"voice-messsage_{}.{}",
chrono::Utc
.timestamp_opt(msg.timestamp_sort, 0)
.single()
@@ -2537,9 +2537,10 @@ async fn prepare_msg_blob(context: &Context, msg: &mut Message) -> Result<()> {
|| "YY-mm-dd_hh:mm:ss".to_string(),
|ts| ts.format("%Y-%m-%d_%H-%M-%S").to_string()
),
&suffix
),
Viewtype::Image | Viewtype::Gif => format!(
"image_{}.{suffix}",
"image_{}.{}",
chrono::Utc
.timestamp_opt(msg.timestamp_sort, 0)
.single()
@@ -2547,9 +2548,10 @@ async fn prepare_msg_blob(context: &Context, msg: &mut Message) -> Result<()> {
|| "YY-mm-dd_hh:mm:ss".to_string(),
|ts| ts.format("%Y-%m-%d_%H-%M-%S").to_string(),
),
&suffix,
),
Viewtype::Video => format!(
"video_{}.{suffix}",
"video_{}.{}",
chrono::Utc
.timestamp_opt(msg.timestamp_sort, 0)
.single()
@@ -2557,6 +2559,7 @@ async fn prepare_msg_blob(context: &Context, msg: &mut Message) -> Result<()> {
|| "YY-mm-dd_hh:mm:ss".to_string(),
|ts| ts.format("%Y-%m-%d_%H-%M-%S").to_string()
),
&suffix
),
_ => filename,
};

View File

@@ -707,7 +707,8 @@ async fn get_autoconfig(
ctx,
// the doc does not mention `emailaddress=`, however, Thunderbird adds it, see <https://releases.mozilla.org/pub/thunderbird/>, which makes some sense
&format!(
"https://{param_domain}/.well-known/autoconfig/mail/config-v1.1.xml?emailaddress={param_addr_urlencoded}"
"https://{}/.well-known/autoconfig/mail/config-v1.1.xml?emailaddress={}",
&param_domain, &param_addr_urlencoded
),
&param.addr,
)
@@ -720,7 +721,7 @@ async fn get_autoconfig(
// Outlook uses always SSL but different domains (this comment describes the next two steps)
if let Ok(res) = outlk_autodiscover(
ctx,
format!("https://{param_domain}/autodiscover/autodiscover.xml"),
format!("https://{}/autodiscover/autodiscover.xml", &param_domain),
)
.await
{
@@ -730,7 +731,10 @@ async fn get_autoconfig(
if let Ok(res) = outlk_autodiscover(
ctx,
format!("https://autodiscover.{param_domain}/autodiscover/autodiscover.xml",),
format!(
"https://autodiscover.{}/autodiscover/autodiscover.xml",
&param_domain
),
)
.await
{
@@ -741,7 +745,7 @@ async fn get_autoconfig(
// always SSL for Thunderbird's database
if let Ok(res) = moz_autoconfigure(
ctx,
&format!("https://autoconfig.thunderbird.net/v1.1/{param_domain}"),
&format!("https://autoconfig.thunderbird.net/v1.1/{}", &param_domain),
&param.addr,
)
.await

View File

@@ -1045,12 +1045,15 @@ impl Session {
if target.is_empty() {
self.delete_message_batch(context, &uid_set, rowid_set)
.await
.with_context(|| format!("cannot delete batch of messages {uid_set:?}"))?;
.with_context(|| format!("cannot delete batch of messages {:?}", &uid_set))?;
} else {
self.move_message_batch(context, &uid_set, rowid_set, &target)
.await
.with_context(|| {
format!("cannot move batch of messages {uid_set:?} to folder {target:?}",)
format!(
"cannot move batch of messages {:?} to folder {:?}",
&uid_set, target
)
})?;
}
}
@@ -1284,10 +1287,9 @@ impl Session {
for (request_uids, set) in build_sequence_sets(&request_uids)? {
info!(context, "Starting UID FETCH of message set \"{}\".", set);
let mut fetch_responses = self
.uid_fetch(&set, BODY_FULL)
.await
.with_context(|| format!("fetching messages {set} from folder {folder:?}"))?;
let mut fetch_responses = self.uid_fetch(&set, BODY_FULL).await.with_context(|| {
format!("fetching messages {} from folder \"{}\"", &set, folder)
})?;
// Map from UIDs to unprocessed FETCH results. We put unprocessed FETCH results here
// when we want to process other messages first.

View File

@@ -222,7 +222,7 @@ SELECT ?1, rfc724_mid, pre_rfc724_mid, timestamp, ?, ? FROM msgs WHERE id=?1
} else {
msg.timestamp_sort
});
ret += &format!("Received: {s}");
ret += &format!("Received: {}", &s);
ret += "\n";
}
@@ -301,7 +301,7 @@ SELECT ?1, rfc724_mid, pre_rfc724_mid, timestamp, ?, ? FROM msgs WHERE id=?1
ret += "Type: ";
ret += &format!("{}", msg.viewtype);
ret += "\n";
ret += &format!("Mimetype: {}\n", msg.get_filemime().unwrap_or_default());
ret += &format!("Mimetype: {}\n", &msg.get_filemime().unwrap_or_default());
}
let w = msg.param.get_int(Param::Width).unwrap_or_default();
let h = msg.param.get_int(Param::Height).unwrap_or_default();

View File

@@ -1948,23 +1948,9 @@ pub(crate) fn wrap_encrypted_part(encrypted: String) -> MimePart<'static> {
"multipart/encrypted; protocol=\"application/pgp-encrypted\"",
vec![
// Autocrypt part 1
MimePart::new("application/pgp-encrypted", "Version: 1\r\n").header(
"Content-Description",
mail_builder::headers::raw::Raw::new("PGP/MIME version identification"),
),
MimePart::new("application/pgp-encrypted", "Version: 1\r\n"),
// Autocrypt part 2
MimePart::new(
"application/octet-stream; name=\"encrypted.asc\"",
encrypted,
)
.header(
"Content-Description",
mail_builder::headers::raw::Raw::new("OpenPGP encrypted message"),
)
.header(
"Content-Disposition",
mail_builder::headers::raw::Raw::new("inline; filename=\"encrypted.asc\";"),
),
MimePart::new("application/octet-stream", encrypted),
],
)
}

View File

@@ -332,7 +332,7 @@ fn inner_generate_secure_join_qr_code(
d.attr("cx", logo_position_x + HALF_LOGO_SIZE)?;
d.attr("cy", logo_position_y + HALF_LOGO_SIZE)?;
d.attr("r", HALF_LOGO_SIZE)?;
d.attr("style", format!("fill:{color}"))
d.attr("style", format!("fill:{}", &color))
})?;
let avatar_font_size = LOGO_SIZE * 0.65;

View File

@@ -3560,7 +3560,12 @@ async fn create_or_lookup_mailinglist_or_broadcast(
mime_parser.timestamp_sent,
)
.await
.with_context(|| format!("failed to create mailinglist '{name}' for grpid={listid}",))?;
.with_context(|| {
format!(
"failed to create mailinglist '{}' for grpid={}",
&name, &listid
)
})?;
if chattype == Chattype::InBroadcast {
chat::add_to_chat_contacts_table(

View File

@@ -218,7 +218,7 @@ pub(crate) fn maybe_network_lost(context: &Context, stores: Vec<ConnectivityStor
impl fmt::Debug for ConnectivityStore {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if let Some(guard) = self.0.try_lock() {
write!(f, "ConnectivityStore {:?}", *guard)
write!(f, "ConnectivityStore {:?}", &*guard)
} else {
write!(f, "ConnectivityStore [LOCKED]")
}

View File

@@ -73,7 +73,8 @@ fn shorten_name(name: &str, length: usize) -> String {
// We use _ rather than ... to avoid dots at the end of the URL, which would confuse linkifiers
format!(
"{}_",
name.chars()
&name
.chars()
.take(length.saturating_sub(1))
.collect::<String>()
)

View File

@@ -992,7 +992,7 @@ async fn test_wrong_auth_token() -> Result<()> {
tcm.send_recv(alice, bob, "hi").await;
let alice_qr = get_securejoin_qr(alice, None).await?;
println!("{alice_qr}");
println!("{}", &alice_qr);
let invalid_alice_qr = alice_qr.replace("&s=", "&s=INVALIDAUTHTOKEN&someotherkey=");
join_securejoin(bob, &invalid_alice_qr).await?;

View File

@@ -1685,7 +1685,7 @@ async fn write_msg(context: &Context, prefix: &str, msg: &Message, buf: &mut Str
msg.get_id(),
if msg.get_showpadlock() { "🔒" } else { "" },
if msg.has_location() { "📍" } else { "" },
contact_name,
&contact_name,
contact_id,
msgtext,
if msg.get_from_id() == ContactId::SELF {

View File

@@ -161,7 +161,7 @@ async fn check_that_transition_worked(
2,
"Group {} has members {:?}, but should have members {:?} and {:?}",
group,
members,
&members,
alice_contact_id,
ContactId::SELF
);

View File

@@ -62,13 +62,13 @@ pub(crate) fn truncate(buf: &str, approx_chars: usize) -> Cow<'_, str> {
if let Some(index) = buf.get(..end_pos).and_then(|s| s.rfind([' ', '\n'])) {
Cow::Owned(format!(
"{}{}",
buf.get(..=index).unwrap_or_default(),
&buf.get(..=index).unwrap_or_default(),
DC_ELLIPSIS
))
} else {
Cow::Owned(format!(
"{}{}",
buf.get(..end_pos).unwrap_or_default(),
&buf.get(..end_pos).unwrap_or_default(),
DC_ELLIPSIS
))
}

View File

@@ -247,12 +247,12 @@ proptest! {
assert!(
l <= approx_chars + el_len,
"buf: '{}' - res: '{}' - len {}, approx {}",
buf, res, res.len(), approx_chars
&buf, &res, res.len(), approx_chars
);
if buf.chars().count() > approx_chars + el_len {
let l = res.len();
assert_eq!(&res[l-5..l], "[...]", "missing ellipsis in {res}");
assert_eq!(&res[l-5..l], "[...]", "missing ellipsis in {}", &res);
}
}
}

View File

@@ -116,7 +116,7 @@ pub(crate) struct ConnectionCandidate {
impl fmt::Display for ConnectionCandidate {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}:{}", self.host, self.port, self.security)?;
write!(f, "{}:{}:{}", &self.host, self.port, self.security)?;
Ok(())
}
}
@@ -131,7 +131,7 @@ pub(crate) struct ConfiguredServerLoginParam {
impl fmt::Display for ConfiguredServerLoginParam {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}", self.connection, self.user)?;
write!(f, "{}:{}", self.connection, &self.user)?;
Ok(())
}
}