mirror of
https://github.com/chatmail/core.git
synced 2026-05-19 06:46:32 +03:00
refactor: use Rust 1.77.0 support for recursion in async functions
This commit is contained in:
12
.github/workflows/ci.yml
vendored
12
.github/workflows/ci.yml
vendored
@@ -24,7 +24,7 @@ jobs:
|
|||||||
name: Lint Rust
|
name: Lint Rust
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
env:
|
env:
|
||||||
RUSTUP_TOOLCHAIN: 1.77.0
|
RUSTUP_TOOLCHAIN: 1.77.1
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
with:
|
with:
|
||||||
@@ -83,15 +83,15 @@ jobs:
|
|||||||
matrix:
|
matrix:
|
||||||
include:
|
include:
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
rust: 1.77.0
|
rust: 1.77.1
|
||||||
- os: windows-latest
|
- os: windows-latest
|
||||||
rust: 1.77.0
|
rust: 1.77.1
|
||||||
- os: macos-latest
|
- os: macos-latest
|
||||||
rust: 1.77.0
|
rust: 1.77.1
|
||||||
|
|
||||||
# Minimum Supported Rust Version = 1.70.0
|
# Minimum Supported Rust Version = 1.77.0
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
rust: 1.70.0
|
rust: 1.77.0
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ name = "deltachat"
|
|||||||
version = "1.137.1"
|
version = "1.137.1"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MPL-2.0"
|
license = "MPL-2.0"
|
||||||
rust-version = "1.70"
|
rust-version = "1.77"
|
||||||
repository = "https://github.com/deltachat/deltachat-core-rust"
|
repository = "https://github.com/deltachat/deltachat-core-rust"
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ set -euo pipefail
|
|||||||
#
|
#
|
||||||
# Avoid using rustup here as it depends on reading /proc/self/exe and
|
# Avoid using rustup here as it depends on reading /proc/self/exe and
|
||||||
# has problems running under QEMU.
|
# has problems running under QEMU.
|
||||||
RUST_VERSION=1.77.0
|
RUST_VERSION=1.77.1
|
||||||
|
|
||||||
ARCH="$(uname -m)"
|
ARCH="$(uname -m)"
|
||||||
test -f "/lib/libc.musl-$ARCH.so.1" && LIBC=musl || LIBC=gnu
|
test -f "/lib/libc.musl-$ARCH.so.1" && LIBC=musl || LIBC=gnu
|
||||||
|
|||||||
166
src/html.rs
166
src/html.rs
@@ -7,12 +7,8 @@
|
|||||||
//! `MsgId.get_html()` will return HTML -
|
//! `MsgId.get_html()` will return HTML -
|
||||||
//! this allows nice quoting, handling linebreaks properly etc.
|
//! this allows nice quoting, handling linebreaks properly etc.
|
||||||
|
|
||||||
use std::future::Future;
|
|
||||||
use std::pin::Pin;
|
|
||||||
|
|
||||||
use anyhow::{Context as _, Result};
|
use anyhow::{Context as _, Result};
|
||||||
use base64::Engine as _;
|
use base64::Engine as _;
|
||||||
use futures::future::FutureExt;
|
|
||||||
use lettre_email::mime::Mime;
|
use lettre_email::mime::Mime;
|
||||||
use lettre_email::PartBuilder;
|
use lettre_email::PartBuilder;
|
||||||
use mailparse::ParsedContentType;
|
use mailparse::ParsedContentType;
|
||||||
@@ -116,119 +112,109 @@ impl HtmlMsgParser {
|
|||||||
/// Usually, there is at most one plain-text and one HTML-text part,
|
/// Usually, there is at most one plain-text and one HTML-text part,
|
||||||
/// multiple plain-text parts might be used for mailinglist-footers,
|
/// multiple plain-text parts might be used for mailinglist-footers,
|
||||||
/// therefore we use the first one.
|
/// therefore we use the first one.
|
||||||
fn collect_texts_recursive<'a>(
|
async fn collect_texts_recursive<'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
mail: &'a mailparse::ParsedMail<'a>,
|
mail: &'a mailparse::ParsedMail<'a>,
|
||||||
) -> Pin<Box<dyn Future<Output = Result<()>> + 'a + Send>> {
|
) -> Result<()> {
|
||||||
// Boxed future to deal with recursion
|
match get_mime_multipart_type(&mail.ctype) {
|
||||||
async move {
|
MimeMultipartType::Multiple => {
|
||||||
match get_mime_multipart_type(&mail.ctype) {
|
for cur_data in &mail.subparts {
|
||||||
MimeMultipartType::Multiple => {
|
Box::pin(self.collect_texts_recursive(cur_data)).await?
|
||||||
for cur_data in &mail.subparts {
|
|
||||||
self.collect_texts_recursive(cur_data).await?
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
MimeMultipartType::Message => {
|
Ok(())
|
||||||
let raw = mail.get_body_raw()?;
|
}
|
||||||
if raw.is_empty() {
|
MimeMultipartType::Message => {
|
||||||
return Ok(());
|
let raw = mail.get_body_raw()?;
|
||||||
}
|
if raw.is_empty() {
|
||||||
let mail = mailparse::parse_mail(&raw).context("failed to parse mail")?;
|
return Ok(());
|
||||||
self.collect_texts_recursive(&mail).await
|
|
||||||
}
|
}
|
||||||
MimeMultipartType::Single => {
|
let mail = mailparse::parse_mail(&raw).context("failed to parse mail")?;
|
||||||
let mimetype = mail.ctype.mimetype.parse::<Mime>()?;
|
Box::pin(self.collect_texts_recursive(&mail)).await
|
||||||
if mimetype == mime::TEXT_HTML {
|
}
|
||||||
if self.html.is_empty() {
|
MimeMultipartType::Single => {
|
||||||
if let Ok(decoded_data) = mail.get_body() {
|
let mimetype = mail.ctype.mimetype.parse::<Mime>()?;
|
||||||
self.html = decoded_data;
|
if mimetype == mime::TEXT_HTML {
|
||||||
}
|
if self.html.is_empty() {
|
||||||
}
|
|
||||||
} else if mimetype == mime::TEXT_PLAIN && self.plain.is_none() {
|
|
||||||
if let Ok(decoded_data) = mail.get_body() {
|
if let Ok(decoded_data) = mail.get_body() {
|
||||||
self.plain = Some(PlainText {
|
self.html = decoded_data;
|
||||||
text: decoded_data,
|
|
||||||
flowed: if let Some(format) = mail.ctype.params.get("format") {
|
|
||||||
format.as_str().to_ascii_lowercase() == "flowed"
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
},
|
|
||||||
delsp: if let Some(delsp) = mail.ctype.params.get("delsp") {
|
|
||||||
delsp.as_str().to_ascii_lowercase() == "yes"
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
} else if mimetype == mime::TEXT_PLAIN && self.plain.is_none() {
|
||||||
|
if let Ok(decoded_data) = mail.get_body() {
|
||||||
|
self.plain = Some(PlainText {
|
||||||
|
text: decoded_data,
|
||||||
|
flowed: if let Some(format) = mail.ctype.params.get("format") {
|
||||||
|
format.as_str().to_ascii_lowercase() == "flowed"
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
},
|
||||||
|
delsp: if let Some(delsp) = mail.ctype.params.get("delsp") {
|
||||||
|
delsp.as_str().to_ascii_lowercase() == "yes"
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.boxed()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Replace cid:-protocol by the data:-protocol where appropriate.
|
/// Replace cid:-protocol by the data:-protocol where appropriate.
|
||||||
/// This allows the final html-file to be self-contained.
|
/// This allows the final html-file to be self-contained.
|
||||||
fn cid_to_data_recursive<'a>(
|
async fn cid_to_data_recursive<'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
context: &'a Context,
|
context: &'a Context,
|
||||||
mail: &'a mailparse::ParsedMail<'a>,
|
mail: &'a mailparse::ParsedMail<'a>,
|
||||||
) -> Pin<Box<dyn Future<Output = Result<()>> + 'a + Send>> {
|
) -> Result<()> {
|
||||||
// Boxed future to deal with recursion
|
match get_mime_multipart_type(&mail.ctype) {
|
||||||
async move {
|
MimeMultipartType::Multiple => {
|
||||||
match get_mime_multipart_type(&mail.ctype) {
|
for cur_data in &mail.subparts {
|
||||||
MimeMultipartType::Multiple => {
|
Box::pin(self.cid_to_data_recursive(context, cur_data)).await?;
|
||||||
for cur_data in &mail.subparts {
|
|
||||||
self.cid_to_data_recursive(context, cur_data).await?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
MimeMultipartType::Message => {
|
Ok(())
|
||||||
let raw = mail.get_body_raw()?;
|
}
|
||||||
if raw.is_empty() {
|
MimeMultipartType::Message => {
|
||||||
return Ok(());
|
let raw = mail.get_body_raw()?;
|
||||||
}
|
if raw.is_empty() {
|
||||||
let mail = mailparse::parse_mail(&raw).context("failed to parse mail")?;
|
return Ok(());
|
||||||
self.cid_to_data_recursive(context, &mail).await
|
|
||||||
}
|
}
|
||||||
MimeMultipartType::Single => {
|
let mail = mailparse::parse_mail(&raw).context("failed to parse mail")?;
|
||||||
let mimetype = mail.ctype.mimetype.parse::<Mime>()?;
|
Box::pin(self.cid_to_data_recursive(context, &mail)).await
|
||||||
if mimetype.type_() == mime::IMAGE {
|
}
|
||||||
if let Some(cid) = mail.headers.get_header_value(HeaderDef::ContentId) {
|
MimeMultipartType::Single => {
|
||||||
if let Ok(cid) = parse_message_id(&cid) {
|
let mimetype = mail.ctype.mimetype.parse::<Mime>()?;
|
||||||
if let Ok(replacement) = mimepart_to_data_url(mail) {
|
if mimetype.type_() == mime::IMAGE {
|
||||||
let re_string = format!(
|
if let Some(cid) = mail.headers.get_header_value(HeaderDef::ContentId) {
|
||||||
"(<img[^>]*src[^>]*=[^>]*)(cid:{})([^>]*>)",
|
if let Ok(cid) = parse_message_id(&cid) {
|
||||||
regex::escape(&cid)
|
if let Ok(replacement) = mimepart_to_data_url(mail) {
|
||||||
);
|
let re_string = format!(
|
||||||
match regex::Regex::new(&re_string) {
|
"(<img[^>]*src[^>]*=[^>]*)(cid:{})([^>]*>)",
|
||||||
Ok(re) => {
|
regex::escape(&cid)
|
||||||
self.html = re
|
);
|
||||||
.replace_all(
|
match regex::Regex::new(&re_string) {
|
||||||
&self.html,
|
Ok(re) => {
|
||||||
format!("${{1}}{replacement}${{3}}").as_str(),
|
self.html = re
|
||||||
)
|
.replace_all(
|
||||||
.as_ref()
|
&self.html,
|
||||||
.to_string()
|
format!("${{1}}{replacement}${{3}}").as_str(),
|
||||||
}
|
)
|
||||||
Err(e) => warn!(
|
.as_ref()
|
||||||
context,
|
.to_string()
|
||||||
"Cannot create regex for cid: {} throws {}",
|
|
||||||
re_string,
|
|
||||||
e
|
|
||||||
),
|
|
||||||
}
|
}
|
||||||
|
Err(e) => warn!(
|
||||||
|
context,
|
||||||
|
"Cannot create regex for cid: {} throws {}", re_string, e
|
||||||
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.boxed()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,9 +2,7 @@
|
|||||||
|
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use std::collections::{HashMap, HashSet};
|
use std::collections::{HashMap, HashSet};
|
||||||
use std::future::Future;
|
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
use std::pin::Pin;
|
|
||||||
use std::str;
|
use std::str;
|
||||||
|
|
||||||
use anyhow::{bail, Context as _, Result};
|
use anyhow::{bail, Context as _, Result};
|
||||||
@@ -818,59 +816,53 @@ impl MimeMessage {
|
|||||||
self.headers.get(headerdef.get_headername())
|
self.headers.get(headerdef.get_headername())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_mime_recursive<'a>(
|
async fn parse_mime_recursive<'a>(
|
||||||
&'a mut self,
|
&'a mut self,
|
||||||
context: &'a Context,
|
context: &'a Context,
|
||||||
mail: &'a mailparse::ParsedMail<'a>,
|
mail: &'a mailparse::ParsedMail<'a>,
|
||||||
is_related: bool,
|
is_related: bool,
|
||||||
) -> Pin<Box<dyn Future<Output = Result<bool>> + 'a + Send>> {
|
) -> Result<bool> {
|
||||||
use futures::future::FutureExt;
|
enum MimeS {
|
||||||
|
Multiple,
|
||||||
|
Single,
|
||||||
|
Message,
|
||||||
|
}
|
||||||
|
|
||||||
// Boxed future to deal with recursion
|
let mimetype = mail.ctype.mimetype.to_lowercase();
|
||||||
async move {
|
|
||||||
enum MimeS {
|
|
||||||
Multiple,
|
|
||||||
Single,
|
|
||||||
Message,
|
|
||||||
}
|
|
||||||
|
|
||||||
let mimetype = mail.ctype.mimetype.to_lowercase();
|
let m = if mimetype.starts_with("multipart") {
|
||||||
|
if mail.ctype.params.contains_key("boundary") {
|
||||||
let m = if mimetype.starts_with("multipart") {
|
MimeS::Multiple
|
||||||
if mail.ctype.params.contains_key("boundary") {
|
|
||||||
MimeS::Multiple
|
|
||||||
} else {
|
|
||||||
MimeS::Single
|
|
||||||
}
|
|
||||||
} else if mimetype.starts_with("message") {
|
|
||||||
if mimetype == "message/rfc822" && !is_attachment_disposition(mail) {
|
|
||||||
MimeS::Message
|
|
||||||
} else {
|
|
||||||
MimeS::Single
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
MimeS::Single
|
MimeS::Single
|
||||||
};
|
}
|
||||||
|
} else if mimetype.starts_with("message") {
|
||||||
|
if mimetype == "message/rfc822" && !is_attachment_disposition(mail) {
|
||||||
|
MimeS::Message
|
||||||
|
} else {
|
||||||
|
MimeS::Single
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
MimeS::Single
|
||||||
|
};
|
||||||
|
|
||||||
let is_related = is_related || mimetype == "multipart/related";
|
let is_related = is_related || mimetype == "multipart/related";
|
||||||
match m {
|
match m {
|
||||||
MimeS::Multiple => self.handle_multiple(context, mail, is_related).await,
|
MimeS::Multiple => Box::pin(self.handle_multiple(context, mail, is_related)).await,
|
||||||
MimeS::Message => {
|
MimeS::Message => {
|
||||||
let raw = mail.get_body_raw()?;
|
let raw = mail.get_body_raw()?;
|
||||||
if raw.is_empty() {
|
if raw.is_empty() {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
}
|
}
|
||||||
let mail = mailparse::parse_mail(&raw).context("failed to parse mail")?;
|
let mail = mailparse::parse_mail(&raw).context("failed to parse mail")?;
|
||||||
|
|
||||||
self.parse_mime_recursive(context, &mail, is_related).await
|
Box::pin(self.parse_mime_recursive(context, &mail, is_related)).await
|
||||||
}
|
}
|
||||||
MimeS::Single => {
|
MimeS::Single => {
|
||||||
self.add_single_part_if_known(context, mail, is_related)
|
self.add_single_part_if_known(context, mail, is_related)
|
||||||
.await
|
.await
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.boxed()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_multiple(
|
async fn handle_multiple(
|
||||||
|
|||||||
Reference in New Issue
Block a user