mirror of
https://github.com/chatmail/core.git
synced 2026-05-05 22:36:30 +03:00
refactor: use mail-builder instead of lettre_email
This commit is contained in:
15
src/chat.rs
15
src/chat.rs
@@ -3,6 +3,7 @@
|
||||
use std::cmp;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
use std::fmt;
|
||||
use std::io::Cursor;
|
||||
use std::marker::Sync;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
@@ -11,6 +12,7 @@ use std::time::Duration;
|
||||
use anyhow::{anyhow, bail, ensure, Context as _, Result};
|
||||
use deltachat_contact_tools::{sanitize_bidi_characters, sanitize_single_line, ContactAddress};
|
||||
use deltachat_derive::{FromSql, ToSql};
|
||||
use mail_builder::mime::MimePart;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use strum_macros::EnumIter;
|
||||
use tokio::task;
|
||||
@@ -32,7 +34,6 @@ use crate::debug_logging::maybe_set_logging_xdc;
|
||||
use crate::download::DownloadState;
|
||||
use crate::ephemeral::{start_chat_ephemeral_timers, Timer as EphemeralTimer};
|
||||
use crate::events::EventType;
|
||||
use crate::html::new_html_mimepart;
|
||||
use crate::location;
|
||||
use crate::log::LogExt;
|
||||
use crate::message::{self, Message, MessageState, MsgId, Viewtype};
|
||||
@@ -2157,14 +2158,18 @@ impl Chat {
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let new_mime_headers = new_mime_headers.map(|s| new_html_mimepart(s).build().as_string());
|
||||
let new_mime_headers: Option<String> = new_mime_headers.map(|s| {
|
||||
let html_part = MimePart::new("text/html", s);
|
||||
let mut buffer = Vec::new();
|
||||
let cursor = Cursor::new(&mut buffer);
|
||||
html_part.write_part(cursor).ok();
|
||||
String::from_utf8_lossy(&buffer).to_string()
|
||||
});
|
||||
let new_mime_headers = new_mime_headers.or_else(|| match was_truncated {
|
||||
// We need to add some headers so that they are stripped before formatting HTML by
|
||||
// `MsgId::get_html()`, not a part of the actual text. Let's add "Content-Type", it's
|
||||
// anyway a useful metadata about the stored text.
|
||||
true => Some(
|
||||
"Content-Type: text/plain; charset=utf-8\r\n\r\n".to_string() + &msg.text + "\r\n",
|
||||
),
|
||||
true => Some("Content-Type: text/plain; charset=utf-8\r\n\r\n".to_string() + &msg.text),
|
||||
false => None,
|
||||
});
|
||||
let new_mime_headers = match new_mime_headers {
|
||||
|
||||
@@ -247,7 +247,16 @@ pub async fn make_vcard(context: &Context, contacts: &[ContactId]) -> Result<Str
|
||||
timestamp: Ok(now),
|
||||
});
|
||||
}
|
||||
Ok(contact_tools::make_vcard(&vcard_contacts))
|
||||
|
||||
// XXX: newline at the end of vCard is trimmed
|
||||
// for compatibility with core <=1.155.3
|
||||
// Newer core should be able to deal with
|
||||
// trailing CRLF as the fix
|
||||
// <https://github.com/deltachat/deltachat-core-rust/pull/6522>
|
||||
// is merged.
|
||||
Ok(contact_tools::make_vcard(&vcard_contacts)
|
||||
.trim_end()
|
||||
.to_string())
|
||||
}
|
||||
|
||||
/// Imports contacts from the given vCard.
|
||||
|
||||
23
src/e2ee.rs
23
src/e2ee.rs
@@ -1,6 +1,9 @@
|
||||
//! End-to-end encryption support.
|
||||
|
||||
use std::io::Cursor;
|
||||
|
||||
use anyhow::{format_err, Context as _, Result};
|
||||
use mail_builder::mime::MimePart;
|
||||
use num_traits::FromPrimitive;
|
||||
|
||||
use crate::aheader::{Aheader, EncryptPreference};
|
||||
@@ -95,7 +98,7 @@ impl EncryptHelper {
|
||||
self,
|
||||
context: &Context,
|
||||
verified: bool,
|
||||
mail_to_encrypt: lettre_email::PartBuilder,
|
||||
mail_to_encrypt: MimePart<'static>,
|
||||
peerstates: Vec<(Option<Peerstate>, String)>,
|
||||
compress: bool,
|
||||
) -> Result<String> {
|
||||
@@ -136,7 +139,9 @@ impl EncryptHelper {
|
||||
|
||||
let sign_key = load_self_secret_key(context).await?;
|
||||
|
||||
let raw_message = mail_to_encrypt.build().as_string().into_bytes();
|
||||
let mut raw_message = Vec::new();
|
||||
let cursor = Cursor::new(&mut raw_message);
|
||||
mail_to_encrypt.clone().write_part(cursor).ok();
|
||||
|
||||
let ctext = pgp::pk_encrypt(&raw_message, keyring, Some(sign_key), compress).await?;
|
||||
|
||||
@@ -145,15 +150,13 @@ impl EncryptHelper {
|
||||
|
||||
/// Signs the passed-in `mail` using the private key from `context`.
|
||||
/// Returns the payload and the signature.
|
||||
pub async fn sign(
|
||||
self,
|
||||
context: &Context,
|
||||
mail: lettre_email::PartBuilder,
|
||||
) -> Result<(lettre_email::MimeMessage, String)> {
|
||||
pub async fn sign(self, context: &Context, mail: &MimePart<'static>) -> Result<String> {
|
||||
let sign_key = load_self_secret_key(context).await?;
|
||||
let mime_message = mail.build();
|
||||
let signature = pgp::pk_calc_signature(mime_message.as_string().as_bytes(), &sign_key)?;
|
||||
Ok((mime_message, signature))
|
||||
let mut buffer = Vec::new();
|
||||
let cursor = Cursor::new(&mut buffer);
|
||||
mail.clone().write_part(cursor).ok();
|
||||
let signature = pgp::pk_calc_signature(&buffer, &sign_key)?;
|
||||
Ok(signature)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
13
src/html.rs
13
src/html.rs
@@ -11,9 +11,8 @@ use std::mem;
|
||||
|
||||
use anyhow::{Context as _, Result};
|
||||
use base64::Engine as _;
|
||||
use lettre_email::mime::Mime;
|
||||
use lettre_email::PartBuilder;
|
||||
use mailparse::ParsedContentType;
|
||||
use mime::Mime;
|
||||
|
||||
use crate::context::Context;
|
||||
use crate::headerdef::{HeaderDef, HeaderDefMap};
|
||||
@@ -277,16 +276,6 @@ impl MsgId {
|
||||
}
|
||||
}
|
||||
|
||||
/// Wraps HTML text into a new text/html mimepart structure.
|
||||
///
|
||||
/// Used on forwarding messages to avoid leaking the original mime structure
|
||||
/// and also to avoid sending too much, maybe large data.
|
||||
pub fn new_html_mimepart(html: String) -> PartBuilder {
|
||||
PartBuilder::new()
|
||||
.content_type(&"text/html; charset=utf-8".parse::<mime::Mime>().unwrap())
|
||||
.body(html)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -10,8 +10,8 @@ use anyhow::{bail, Context as _, Result};
|
||||
use deltachat_contact_tools::{addr_cmp, addr_normalize, sanitize_bidi_characters};
|
||||
use deltachat_derive::{FromSql, ToSql};
|
||||
use format_flowed::unformat_flowed;
|
||||
use lettre_email::mime::Mime;
|
||||
use mailparse::{addrparse_header, DispositionType, MailHeader, MailHeaderMap, SingleInfo};
|
||||
use mime::Mime;
|
||||
|
||||
use crate::aheader::{Aheader, EncryptPreference};
|
||||
use crate::authres::handle_authres;
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
|
||||
use anyhow::{anyhow, bail, Context as _, Result};
|
||||
use data_encoding::BASE32_NOPAD;
|
||||
use email::Header;
|
||||
use futures_lite::StreamExt;
|
||||
use iroh::{Endpoint, NodeAddr, NodeId, PublicKey, RelayMap, RelayMode, RelayUrl, SecretKey};
|
||||
use iroh_gossip::net::{Event, Gossip, GossipEvent, JoinOptions, GOSSIP_ALPN};
|
||||
@@ -40,7 +39,6 @@ use url::Url;
|
||||
use crate::chat::send_msg;
|
||||
use crate::config::Config;
|
||||
use crate::context::Context;
|
||||
use crate::headerdef::HeaderDef;
|
||||
use crate::message::{Message, MsgId, Viewtype};
|
||||
use crate::mimeparser::SystemMessage;
|
||||
use crate::EventType;
|
||||
@@ -496,14 +494,11 @@ fn create_random_topic() -> TopicId {
|
||||
|
||||
/// Creates `Iroh-Gossip-Header` with a new random topic
|
||||
/// and stores the topic for the message.
|
||||
pub(crate) async fn create_iroh_header(ctx: &Context, msg_id: MsgId) -> Result<Header> {
|
||||
pub(crate) async fn create_iroh_header(ctx: &Context, msg_id: MsgId) -> Result<String> {
|
||||
let topic = create_random_topic();
|
||||
insert_topic_stub(ctx, msg_id, topic).await?;
|
||||
let topic_string = BASE32_NOPAD.encode(topic.as_bytes()).to_ascii_lowercase();
|
||||
Ok(Header::new(
|
||||
HeaderDef::IrohGossipTopic.get_headername().to_string(),
|
||||
topic_string,
|
||||
))
|
||||
Ok(topic_string)
|
||||
}
|
||||
|
||||
async fn subscribe_loop(
|
||||
|
||||
@@ -1061,8 +1061,8 @@ async fn test_classic_mailing_list() -> Result<()> {
|
||||
let mime = sent.payload();
|
||||
|
||||
println!("Sent mime message is:\n\n{mime}\n\n");
|
||||
assert!(mime.contains("Content-Type: text/plain; charset=utf-8\r\n"));
|
||||
assert!(mime.contains("Subject: =?utf-8?q?Re=3A_=5Bdelta-dev=5D_What=27s_up=3F?=\r\n"));
|
||||
assert!(mime.contains("Content-Type: text/plain; charset=\"utf-8\"\r\n"));
|
||||
assert!(mime.contains("Subject: Re: [delta-dev] What's up?\r\n"));
|
||||
assert!(mime.contains("MIME-Version: 1.0\r\n"));
|
||||
assert!(mime.contains("In-Reply-To: <38942@posteo.org>\r\n"));
|
||||
assert!(mime.contains("Chat-Version: 1.0\r\n"));
|
||||
|
||||
12
src/sync.rs
12
src/sync.rs
@@ -1,7 +1,7 @@
|
||||
//! # Synchronize items between devices.
|
||||
|
||||
use anyhow::Result;
|
||||
use lettre_email::PartBuilder;
|
||||
use mail_builder::mime::MimePart;
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::chat::{self, ChatId};
|
||||
@@ -227,14 +227,8 @@ impl Context {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn build_sync_part(&self, json: String) -> PartBuilder {
|
||||
PartBuilder::new()
|
||||
.content_type(&"application/json".parse::<mime::Mime>().unwrap())
|
||||
.header((
|
||||
"Content-Disposition",
|
||||
"attachment; filename=\"multi-device-sync.json\"",
|
||||
))
|
||||
.body(json)
|
||||
pub(crate) fn build_sync_part(&self, json: String) -> MimePart<'static> {
|
||||
MimePart::new("application/json", json).attachment("multi-device-sync.json")
|
||||
}
|
||||
|
||||
/// Takes a JSON string created by `build_sync_json()`
|
||||
|
||||
@@ -27,7 +27,7 @@ use anyhow::{anyhow, bail, ensure, format_err, Context as _, Result};
|
||||
use async_zip::tokio::read::seek::ZipFileReader as SeekZipFileReader;
|
||||
use deltachat_contact_tools::sanitize_bidi_characters;
|
||||
use deltachat_derive::FromSql;
|
||||
use lettre_email::PartBuilder;
|
||||
use mail_builder::mime::MimePart;
|
||||
use rusqlite::OptionalExtension;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
@@ -41,7 +41,6 @@ use crate::context::Context;
|
||||
use crate::events::EventType;
|
||||
use crate::key::{load_self_public_key, DcKey};
|
||||
use crate::message::{Message, MessageState, MsgId, Viewtype};
|
||||
use crate::mimefactory::wrapped_base64_encode;
|
||||
use crate::mimefactory::RECOMMENDED_FILE_SIZE;
|
||||
use crate::mimeparser::SystemMessage;
|
||||
use crate::param::Param;
|
||||
@@ -651,17 +650,8 @@ impl Context {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn build_status_update_part(&self, json: &str) -> PartBuilder {
|
||||
let encoded_body = wrapped_base64_encode(json.as_bytes());
|
||||
|
||||
PartBuilder::new()
|
||||
.content_type(&"application/json".parse::<mime::Mime>().unwrap())
|
||||
.header((
|
||||
"Content-Disposition",
|
||||
"attachment; filename=\"status-update.json\"",
|
||||
))
|
||||
.header(("Content-Transfer-Encoding", "base64"))
|
||||
.body(encoded_body)
|
||||
pub(crate) fn build_status_update_part(&self, json: &str) -> MimePart<'static> {
|
||||
MimePart::new("application/json", json.as_bytes().to_vec()).attachment("status-update.json")
|
||||
}
|
||||
|
||||
/// Receives status updates from receive_imf to the database
|
||||
|
||||
Reference in New Issue
Block a user