mirror of
https://github.com/chatmail/core.git
synced 2026-05-08 01:16:31 +03:00
restructure mimefactory
This commit is contained in:
@@ -1,8 +1,4 @@
|
|||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use std::borrow::Cow;
|
|
||||||
|
|
||||||
use charset::Charset;
|
|
||||||
use percent_encoding::{percent_decode, utf8_percent_encode, AsciiSet, CONTROLS};
|
|
||||||
|
|
||||||
/// Encode non-ascii-strings as `=?UTF-8?Q?Bj=c3=b6rn_Petersen?=`.
|
/// Encode non-ascii-strings as `=?UTF-8?Q?Bj=c3=b6rn_Petersen?=`.
|
||||||
/// Belongs to RFC 2047: https://tools.ietf.org/html/rfc2047
|
/// Belongs to RFC 2047: https://tools.ietf.org/html/rfc2047
|
||||||
|
|||||||
124
src/job.rs
124
src/job.rs
@@ -18,7 +18,7 @@ use crate::location;
|
|||||||
use crate::login_param::LoginParam;
|
use crate::login_param::LoginParam;
|
||||||
use crate::message::MsgId;
|
use crate::message::MsgId;
|
||||||
use crate::message::{self, Message, MessageState};
|
use crate::message::{self, Message, MessageState};
|
||||||
use crate::mimefactory::{vec_contains_lowercase, Loaded, MimeFactory};
|
use crate::mimefactory::{vec_contains_lowercase, MimeFactory, RenderedEmail};
|
||||||
use crate::param::*;
|
use crate::param::*;
|
||||||
use crate::sql;
|
use crate::sql;
|
||||||
|
|
||||||
@@ -604,42 +604,38 @@ fn set_delivered(context: &Context, msg_id: MsgId) {
|
|||||||
/* special case for DC_JOB_SEND_MSG_TO_SMTP */
|
/* special case for DC_JOB_SEND_MSG_TO_SMTP */
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub fn job_send_msg(context: &Context, msg_id: MsgId) -> Result<(), Error> {
|
pub fn job_send_msg(context: &Context, msg_id: MsgId) -> Result<(), Error> {
|
||||||
let mut mimefactory = MimeFactory::load_msg(context, msg_id)?;
|
let mut msg = Message::load_from_db(context, msg_id)?;
|
||||||
|
|
||||||
if chat::msgtype_has_file(mimefactory.msg.type_0) {
|
if chat::msgtype_has_file(msg.type_0) {
|
||||||
let file_param = mimefactory.msg.param.get_path(Param::File, context)?;
|
let file_param = msg.param.get_path(Param::File, context)?;
|
||||||
if let Some(pathNfilename) = file_param {
|
if let Some(pathNfilename) = file_param {
|
||||||
if (mimefactory.msg.type_0 == Viewtype::Image
|
if (msg.type_0 == Viewtype::Image || msg.type_0 == Viewtype::Gif)
|
||||||
|| mimefactory.msg.type_0 == Viewtype::Gif)
|
&& !msg.param.exists(Param::Width)
|
||||||
&& !mimefactory.msg.param.exists(Param::Width)
|
|
||||||
{
|
{
|
||||||
mimefactory.msg.param.set_int(Param::Width, 0);
|
msg.param.set_int(Param::Width, 0);
|
||||||
mimefactory.msg.param.set_int(Param::Height, 0);
|
msg.param.set_int(Param::Height, 0);
|
||||||
|
|
||||||
if let Ok(buf) = dc_read_file(context, pathNfilename) {
|
if let Ok(buf) = dc_read_file(context, pathNfilename) {
|
||||||
if let Ok((width, height)) = dc_get_filemeta(&buf) {
|
if let Ok((width, height)) = dc_get_filemeta(&buf) {
|
||||||
mimefactory.msg.param.set_int(Param::Width, width as i32);
|
msg.param.set_int(Param::Width, width as i32);
|
||||||
mimefactory.msg.param.set_int(Param::Height, height as i32);
|
msg.param.set_int(Param::Height, height as i32);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mimefactory.msg.save_param_to_disk(context);
|
msg.save_param_to_disk(context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* create message */
|
/* create message */
|
||||||
if let Err(msg) = mimefactory.render() {
|
let needs_encryption = msg.param.get_int(Param::GuaranteeE2ee).unwrap_or_default();
|
||||||
let e = msg.to_string();
|
|
||||||
message::set_msg_failed(context, msg_id, Some(e));
|
let mimefactory = MimeFactory::from_msg(context, &msg)?;
|
||||||
return Err(msg);
|
let mut rendered_msg = mimefactory.render().map_err(|err| {
|
||||||
}
|
message::set_msg_failed(context, msg_id, Some(err.to_string()));
|
||||||
if 0 != mimefactory
|
err
|
||||||
.msg
|
})?;
|
||||||
.param
|
|
||||||
.get_int(Param::GuaranteeE2ee)
|
if 0 != needs_encryption && !rendered_msg.is_encrypted {
|
||||||
.unwrap_or_default()
|
|
||||||
&& !mimefactory.out_encrypted
|
|
||||||
{
|
|
||||||
/* unrecoverable */
|
/* unrecoverable */
|
||||||
message::set_msg_failed(
|
message::set_msg_failed(
|
||||||
context,
|
context,
|
||||||
@@ -649,19 +645,17 @@ pub fn job_send_msg(context: &Context, msg_id: MsgId) -> Result<(), Error> {
|
|||||||
bail!(
|
bail!(
|
||||||
"e2e encryption unavailable {} - {:?}",
|
"e2e encryption unavailable {} - {:?}",
|
||||||
msg_id,
|
msg_id,
|
||||||
mimefactory.msg.param.get_int(Param::GuaranteeE2ee),
|
needs_encryption
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if context.get_config_bool(Config::BccSelf)
|
if context.get_config_bool(Config::BccSelf)
|
||||||
&& !vec_contains_lowercase(&mimefactory.recipients_addr, &mimefactory.from_addr)
|
&& !vec_contains_lowercase(&rendered_msg.recipients, &rendered_msg.from)
|
||||||
{
|
{
|
||||||
mimefactory.recipients_names.push("".to_string());
|
rendered_msg.recipients.push(rendered_msg.from.clone());
|
||||||
mimefactory
|
|
||||||
.recipients_addr
|
|
||||||
.push(mimefactory.from_addr.to_string());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if mimefactory.recipients_addr.is_empty() {
|
if rendered_msg.recipients.is_empty() {
|
||||||
// may happen eg. for groups with only SELF and bcc_self disabled
|
// may happen eg. for groups with only SELF and bcc_self disabled
|
||||||
info!(
|
info!(
|
||||||
context,
|
context,
|
||||||
@@ -671,36 +665,27 @@ pub fn job_send_msg(context: &Context, msg_id: MsgId) -> Result<(), Error> {
|
|||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
|
|
||||||
if mimefactory.out_gossiped {
|
if rendered_msg.is_gossiped {
|
||||||
chat::set_gossiped_timestamp(context, mimefactory.msg.chat_id, time());
|
chat::set_gossiped_timestamp(context, msg.chat_id, time());
|
||||||
}
|
}
|
||||||
if 0 != mimefactory.out_last_added_location_id {
|
if 0 != rendered_msg.last_added_location_id {
|
||||||
if let Err(err) = location::set_kml_sent_timestamp(context, mimefactory.msg.chat_id, time())
|
if let Err(err) = location::set_kml_sent_timestamp(context, msg.chat_id, time()) {
|
||||||
{
|
|
||||||
error!(context, "Failed to set kml sent_timestamp: {:?}", err);
|
error!(context, "Failed to set kml sent_timestamp: {:?}", err);
|
||||||
}
|
}
|
||||||
if !mimefactory.msg.hidden {
|
if !msg.hidden {
|
||||||
if let Err(err) = location::set_msg_location_id(
|
if let Err(err) =
|
||||||
context,
|
location::set_msg_location_id(context, msg.id, rendered_msg.last_added_location_id)
|
||||||
mimefactory.msg.id,
|
{
|
||||||
mimefactory.out_last_added_location_id,
|
|
||||||
) {
|
|
||||||
error!(context, "Failed to set msg_location_id: {:?}", err);
|
error!(context, "Failed to set msg_location_id: {:?}", err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if mimefactory.out_encrypted
|
if rendered_msg.is_encrypted && needs_encryption == 0 {
|
||||||
&& mimefactory
|
msg.param.set_int(Param::GuaranteeE2ee, 1);
|
||||||
.msg
|
msg.save_param_to_disk(context);
|
||||||
.param
|
|
||||||
.get_int(Param::GuaranteeE2ee)
|
|
||||||
.unwrap_or_default()
|
|
||||||
== 0
|
|
||||||
{
|
|
||||||
mimefactory.msg.param.set_int(Param::GuaranteeE2ee, 1);
|
|
||||||
mimefactory.msg.save_param_to_disk(context);
|
|
||||||
}
|
}
|
||||||
add_smtp_job(context, Action::SendMsgToSmtp, &mut mimefactory)?;
|
|
||||||
|
add_smtp_job(context, Action::SendMsgToSmtp, &rendered_msg)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -886,33 +871,40 @@ fn suspend_smtp_thread(context: &Context, suspend: bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn send_mdn(context: &Context, msg_id: MsgId) -> Result<(), Error> {
|
fn send_mdn(context: &Context, msg_id: MsgId) -> Result<(), Error> {
|
||||||
let mut mimefactory = MimeFactory::load_mdn(context, msg_id)?;
|
let msg = Message::load_from_db(context, msg_id)?;
|
||||||
mimefactory.render()?;
|
let mimefactory = MimeFactory::from_mdn(context, &msg)?;
|
||||||
add_smtp_job(context, Action::SendMdn, &mut mimefactory)?;
|
let rendered_msg = mimefactory.render()?;
|
||||||
|
|
||||||
|
add_smtp_job(context, Action::SendMdn, &rendered_msg)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
fn add_smtp_job(context: &Context, action: Action, mimefactory: &MimeFactory) -> Result<(), Error> {
|
fn add_smtp_job(
|
||||||
|
context: &Context,
|
||||||
|
action: Action,
|
||||||
|
rendered_msg: &RenderedEmail,
|
||||||
|
) -> Result<(), Error> {
|
||||||
ensure!(
|
ensure!(
|
||||||
!mimefactory.recipients_addr.is_empty(),
|
!rendered_msg.recipients.is_empty(),
|
||||||
"no recipients for smtp job set"
|
"no recipients for smtp job set"
|
||||||
);
|
);
|
||||||
let mut param = Params::new();
|
let mut param = Params::new();
|
||||||
let bytes = &mimefactory.out;
|
let bytes = &rendered_msg.message;
|
||||||
let blob = BlobObject::create(context, &mimefactory.rfc724_mid, bytes)?;
|
let blob = BlobObject::create(context, &rendered_msg.rfc724_mid, bytes)?;
|
||||||
let recipients = mimefactory.recipients_addr.join("\x1e");
|
|
||||||
|
let recipients = rendered_msg.recipients.join("\x1e");
|
||||||
param.set(Param::File, blob.as_name());
|
param.set(Param::File, blob.as_name());
|
||||||
param.set(Param::Recipients, &recipients);
|
param.set(Param::Recipients, &recipients);
|
||||||
|
|
||||||
job_add(
|
job_add(
|
||||||
context,
|
context,
|
||||||
action,
|
action,
|
||||||
(if mimefactory.loaded == Loaded::Message {
|
rendered_msg
|
||||||
mimefactory.msg.id.to_u32() as i32
|
.foreign_id
|
||||||
} else {
|
.map(|v| v.to_u32() as i32)
|
||||||
0
|
.unwrap_or_default(),
|
||||||
}) as libc::c_int,
|
|
||||||
param,
|
param,
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use chrono::TimeZone;
|
use chrono::TimeZone;
|
||||||
use lettre::Envelope;
|
|
||||||
use lettre_email::{Address, Header, MimeMessage, MimeMultipartType, PartBuilder};
|
use lettre_email::{Address, Header, MimeMessage, MimeMultipartType, PartBuilder};
|
||||||
|
|
||||||
use crate::chat::{self, Chat};
|
use crate::chat::{self, Chat};
|
||||||
@@ -21,38 +20,53 @@ use crate::stock::StockMessage;
|
|||||||
|
|
||||||
#[derive(Clone, Copy, Eq, PartialEq)]
|
#[derive(Clone, Copy, Eq, PartialEq)]
|
||||||
pub enum Loaded {
|
pub enum Loaded {
|
||||||
Nothing,
|
|
||||||
Message,
|
Message,
|
||||||
MDN, // TODO: invent more descriptive name
|
MDN,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Helper to construct mime messages.
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct MimeFactory<'a> {
|
pub struct MimeFactory<'a, 'b> {
|
||||||
pub from_addr: String,
|
pub from_addr: String,
|
||||||
pub from_displayname: String,
|
pub from_displayname: String,
|
||||||
pub selfstatus: String,
|
pub selfstatus: String,
|
||||||
pub recipients_names: Vec<String>,
|
pub recipients_names: Vec<String>,
|
||||||
pub recipients_addr: Vec<String>,
|
pub recipients_addr: Vec<String>,
|
||||||
pub timestamp: i64,
|
pub timestamp: i64,
|
||||||
pub rfc724_mid: String,
|
|
||||||
pub loaded: Loaded,
|
pub loaded: Loaded,
|
||||||
pub msg: Message,
|
pub msg: &'b Message,
|
||||||
pub chat: Option<Chat>,
|
pub chat: Option<Chat>,
|
||||||
pub increation: bool,
|
pub increation: bool,
|
||||||
pub in_reply_to: String,
|
pub in_reply_to: String,
|
||||||
pub references: String,
|
pub references: String,
|
||||||
pub req_mdn: bool,
|
pub req_mdn: bool,
|
||||||
pub out: Vec<u8>,
|
|
||||||
pub envelope: Option<Envelope>,
|
|
||||||
pub out_encrypted: bool,
|
|
||||||
pub out_gossiped: bool,
|
|
||||||
pub out_last_added_location_id: u32,
|
|
||||||
pub context: &'a Context,
|
pub context: &'a Context,
|
||||||
|
last_added_location_id: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> MimeFactory<'a> {
|
/// Result of rendering a message, ready to be submitted to a send job.
|
||||||
fn from_message(context: &'a Context, msg: Message) -> Self {
|
#[derive(Debug, Clone)]
|
||||||
MimeFactory {
|
pub struct RenderedEmail {
|
||||||
|
pub message: Vec<u8>,
|
||||||
|
// pub envelope: Envelope,
|
||||||
|
pub is_encrypted: bool,
|
||||||
|
pub is_gossiped: bool,
|
||||||
|
pub last_added_location_id: u32,
|
||||||
|
/// None for MDN, the message id otherwise
|
||||||
|
pub foreign_id: Option<MsgId>,
|
||||||
|
|
||||||
|
pub from: String,
|
||||||
|
pub recipients: Vec<String>,
|
||||||
|
|
||||||
|
/// Message ID (Message in the sense of Email)
|
||||||
|
pub rfc724_mid: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||||
|
pub fn from_msg(context: &'a Context, msg: &'b Message) -> Result<MimeFactory<'a, 'b>, Error> {
|
||||||
|
let chat = Chat::load_from_db(context, msg.chat_id)?;
|
||||||
|
|
||||||
|
let mut factory = MimeFactory {
|
||||||
from_addr: context
|
from_addr: context
|
||||||
.get_config(Config::ConfiguredAddr)
|
.get_config(Config::ConfiguredAddr)
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
@@ -62,31 +76,103 @@ impl<'a> MimeFactory<'a> {
|
|||||||
.unwrap_or_else(|| context.stock_str(StockMessage::StatusLine).to_string()),
|
.unwrap_or_else(|| context.stock_str(StockMessage::StatusLine).to_string()),
|
||||||
recipients_names: Vec::with_capacity(5),
|
recipients_names: Vec::with_capacity(5),
|
||||||
recipients_addr: Vec::with_capacity(5),
|
recipients_addr: Vec::with_capacity(5),
|
||||||
timestamp: 0,
|
timestamp: msg.timestamp_sort,
|
||||||
rfc724_mid: String::default(),
|
loaded: Loaded::Message,
|
||||||
loaded: Loaded::Nothing,
|
|
||||||
msg,
|
msg,
|
||||||
chat: None,
|
chat: Some(chat),
|
||||||
increation: false,
|
increation: msg.is_increation(),
|
||||||
in_reply_to: String::default(),
|
in_reply_to: String::default(),
|
||||||
references: String::default(),
|
references: String::default(),
|
||||||
req_mdn: false,
|
req_mdn: false,
|
||||||
out: Vec::new(),
|
last_added_location_id: 0,
|
||||||
envelope: None,
|
|
||||||
out_encrypted: false,
|
|
||||||
out_gossiped: false,
|
|
||||||
out_last_added_location_id: 0,
|
|
||||||
context,
|
context,
|
||||||
}
|
};
|
||||||
}
|
|
||||||
|
|
||||||
pub fn finalize_mime_message(&mut self, encrypted: bool, gossiped: bool) -> Result<(), Error> {
|
// just set the chat above
|
||||||
self.out_encrypted = encrypted;
|
let chat = factory.chat.as_ref().unwrap();
|
||||||
self.out_gossiped = encrypted && gossiped;
|
|
||||||
|
if chat.is_self_talk() {
|
||||||
|
factory
|
||||||
|
.recipients_names
|
||||||
|
.push(factory.from_displayname.to_string());
|
||||||
|
factory.recipients_addr.push(factory.from_addr.to_string());
|
||||||
|
} else {
|
||||||
|
context.sql.query_map(
|
||||||
|
"SELECT c.authname, c.addr \
|
||||||
|
FROM chats_contacts cc \
|
||||||
|
LEFT JOIN contacts c ON cc.contact_id=c.id \
|
||||||
|
WHERE cc.chat_id=? AND cc.contact_id>9;",
|
||||||
|
params![factory.msg.chat_id as i32],
|
||||||
|
|row| {
|
||||||
|
let authname: String = row.get(0)?;
|
||||||
|
let addr: String = row.get(1)?;
|
||||||
|
Ok((authname, addr))
|
||||||
|
},
|
||||||
|
|rows| {
|
||||||
|
for row in rows {
|
||||||
|
let (authname, addr) = row?;
|
||||||
|
if !vec_contains_lowercase(&factory.recipients_addr, &addr) {
|
||||||
|
factory.recipients_addr.push(addr);
|
||||||
|
factory.recipients_names.push(authname);
|
||||||
|
}
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let command = factory.msg.param.get_cmd();
|
||||||
|
let msg = &factory.msg;
|
||||||
|
|
||||||
|
/* for added members, the list is just fine */
|
||||||
|
if command == SystemMessage::MemberRemovedFromGroup {
|
||||||
|
let email_to_remove = msg.param.get(Param::Arg).unwrap_or_default();
|
||||||
|
|
||||||
|
let self_addr = context
|
||||||
|
.get_config(Config::ConfiguredAddr)
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
if !email_to_remove.is_empty() && !addr_cmp(email_to_remove, self_addr) {
|
||||||
|
if !vec_contains_lowercase(&factory.recipients_addr, &email_to_remove) {
|
||||||
|
factory.recipients_names.push("".to_string());
|
||||||
|
factory.recipients_addr.push(email_to_remove.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if command != SystemMessage::AutocryptSetupMessage
|
||||||
|
&& command != SystemMessage::SecurejoinMessage
|
||||||
|
&& context.get_config_bool(Config::MdnsEnabled)
|
||||||
|
{
|
||||||
|
factory.req_mdn = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let row = context.sql.query_row(
|
||||||
|
"SELECT mime_in_reply_to, mime_references FROM msgs WHERE id=?",
|
||||||
|
params![msg.id],
|
||||||
|
|row| {
|
||||||
|
let in_reply_to: String = row.get(0)?;
|
||||||
|
let references: String = row.get(1)?;
|
||||||
|
|
||||||
|
Ok((in_reply_to, references))
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
match row {
|
||||||
|
Ok((in_reply_to, references)) => {
|
||||||
|
factory.in_reply_to = in_reply_to;
|
||||||
|
factory.references = references;
|
||||||
|
}
|
||||||
|
Err(err) => {
|
||||||
|
error!(
|
||||||
|
context,
|
||||||
|
"mimefactory: failed to load mime_in_reply_to: {:?}", err
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_mdn(context: &'a Context, msg_id: MsgId) -> Result<Self, Error> {
|
Ok(factory)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_mdn(context: &'a Context, msg: &'b Message) -> Result<Self, Error> {
|
||||||
// MDNs not enabled - check this is late, in the job. the
|
// MDNs not enabled - check this is late, in the job. the
|
||||||
// user may have changed its choice while offline ...
|
// user may have changed its choice while offline ...
|
||||||
ensure!(
|
ensure!(
|
||||||
@@ -94,33 +180,36 @@ impl<'a> MimeFactory<'a> {
|
|||||||
"MDNs meanwhile disabled"
|
"MDNs meanwhile disabled"
|
||||||
);
|
);
|
||||||
|
|
||||||
let msg = Message::load_from_db(context, msg_id)?;
|
let contact = Contact::load_from_db(context, msg.from_id)?;
|
||||||
let mut factory = MimeFactory::from_message(context, msg);
|
|
||||||
let contact = Contact::load_from_db(factory.context, factory.msg.from_id)?;
|
|
||||||
|
|
||||||
// Do not send MDNs trash etc.; chats.blocked is already checked by the caller
|
// Do not send MDNs trash etc.; chats.blocked is already checked by the caller
|
||||||
// in dc_markseen_msgs()
|
// in dc_markseen_msgs()
|
||||||
ensure!(!contact.is_blocked(), "Contact blocked");
|
ensure!(!contact.is_blocked(), "Contact blocked");
|
||||||
ensure!(
|
ensure!(msg.chat_id > DC_CHAT_ID_LAST_SPECIAL, "Invalid chat id");
|
||||||
factory.msg.chat_id > DC_CHAT_ID_LAST_SPECIAL,
|
|
||||||
"Invalid chat id"
|
|
||||||
);
|
|
||||||
|
|
||||||
factory
|
Ok(MimeFactory {
|
||||||
.recipients_names
|
context,
|
||||||
.push(contact.get_authname().to_string());
|
from_addr: context
|
||||||
factory.recipients_addr.push(contact.get_addr().to_string());
|
.get_config(Config::ConfiguredAddr)
|
||||||
factory.timestamp = dc_create_smeared_timestamp(factory.context);
|
.unwrap_or_default(),
|
||||||
factory.rfc724_mid = dc_create_outgoing_rfc724_mid(None, &factory.from_addr);
|
from_displayname: context.get_config(Config::Displayname).unwrap_or_default(),
|
||||||
factory.loaded = Loaded::MDN;
|
selfstatus: context
|
||||||
|
.get_config(Config::Selfstatus)
|
||||||
Ok(factory)
|
.unwrap_or_else(|| context.stock_str(StockMessage::StatusLine).to_string()),
|
||||||
|
recipients_names: vec![contact.get_authname().to_string()],
|
||||||
|
recipients_addr: vec![contact.get_addr().to_string()],
|
||||||
|
timestamp: dc_create_smeared_timestamp(context),
|
||||||
|
loaded: Loaded::MDN,
|
||||||
|
msg,
|
||||||
|
chat: None,
|
||||||
|
increation: false,
|
||||||
|
in_reply_to: String::default(),
|
||||||
|
references: String::default(),
|
||||||
|
req_mdn: false,
|
||||||
|
last_added_location_id: 0,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/*******************************************************************************
|
|
||||||
* Render a basic email
|
|
||||||
******************************************************************************/
|
|
||||||
|
|
||||||
fn peerstates_for_recipients(&self) -> Result<Vec<(Option<Peerstate>, &str)>, Error> {
|
fn peerstates_for_recipients(&self) -> Result<Vec<(Option<Peerstate>, &str)>, Error> {
|
||||||
let self_addr = self
|
let self_addr = self
|
||||||
.context
|
.context
|
||||||
@@ -165,7 +254,6 @@ impl<'a> MimeFactory<'a> {
|
|||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
Loaded::MDN => Ok(false),
|
Loaded::MDN => Ok(false),
|
||||||
Loaded::Nothing => bail!("No message loaded"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -180,7 +268,6 @@ impl<'a> MimeFactory<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loaded::MDN => Ok(PeerstateVerifiedStatus::Unverified),
|
Loaded::MDN => Ok(PeerstateVerifiedStatus::Unverified),
|
||||||
Loaded::Nothing => bail!("No message loaded"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -199,7 +286,6 @@ impl<'a> MimeFactory<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Loaded::MDN => Ok(DC_FP_NO_AUTOCRYPT_HEADER),
|
Loaded::MDN => Ok(DC_FP_NO_AUTOCRYPT_HEADER),
|
||||||
Loaded::Nothing => bail!("No message loaded"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -225,7 +311,6 @@ impl<'a> MimeFactory<'a> {
|
|||||||
Ok(false)
|
Ok(false)
|
||||||
}
|
}
|
||||||
Loaded::MDN => Ok(false),
|
Loaded::MDN => Ok(false),
|
||||||
Loaded::Nothing => bail!("No message loaded"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,7 +333,6 @@ impl<'a> MimeFactory<'a> {
|
|||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
Loaded::MDN => Ok(None),
|
Loaded::MDN => Ok(None),
|
||||||
Loaded::Nothing => bail!("No message loaded"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -288,18 +372,10 @@ impl<'a> MimeFactory<'a> {
|
|||||||
let e = self.context.stock_str(StockMessage::ReadRcpt);
|
let e = self.context.stock_str(StockMessage::ReadRcpt);
|
||||||
Ok(format!("Chat: {}", e).to_string())
|
Ok(format!("Chat: {}", e).to_string())
|
||||||
}
|
}
|
||||||
Loaded::Nothing => bail!("No message loaded"),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render(&mut self) -> Result<(), Error> {
|
pub fn render(mut self) -> Result<RenderedEmail, Error> {
|
||||||
// TODO: take self
|
|
||||||
|
|
||||||
ensure!(
|
|
||||||
self.loaded != Loaded::Nothing && self.out.is_empty(),
|
|
||||||
"Invalid use of mimefactory-object."
|
|
||||||
);
|
|
||||||
|
|
||||||
let e2ee_guranteed = self.is_e2ee_guranteed()?;
|
let e2ee_guranteed = self.is_e2ee_guranteed()?;
|
||||||
|
|
||||||
let mut encrypt_helper = EncryptHelper::new(self.context)?;
|
let mut encrypt_helper = EncryptHelper::new(self.context)?;
|
||||||
@@ -389,7 +465,6 @@ impl<'a> MimeFactory<'a> {
|
|||||||
self.render_message(&mut protected_headers, &mut unprotected_headers, &grpimage)?
|
self.render_message(&mut protected_headers, &mut unprotected_headers, &grpimage)?
|
||||||
}
|
}
|
||||||
Loaded::MDN => self.render_mdn(&mut protected_headers, &mut unprotected_headers)?,
|
Loaded::MDN => self.render_mdn(&mut protected_headers, &mut unprotected_headers)?,
|
||||||
Loaded::Nothing => bail!("No message loaded"),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if force_plaintext != DC_FP_NO_AUTOCRYPT_HEADER {
|
if force_plaintext != DC_FP_NO_AUTOCRYPT_HEADER {
|
||||||
@@ -403,8 +478,9 @@ impl<'a> MimeFactory<'a> {
|
|||||||
let peerstates = self.peerstates_for_recipients()?;
|
let peerstates = self.peerstates_for_recipients()?;
|
||||||
let should_encrypt =
|
let should_encrypt =
|
||||||
encrypt_helper.should_encrypt(self.context, e2ee_guranteed, &peerstates)?;
|
encrypt_helper.should_encrypt(self.context, e2ee_guranteed, &peerstates)?;
|
||||||
|
let is_encrypted = should_encrypt && force_plaintext == 0;
|
||||||
|
|
||||||
let mut outer_message = if should_encrypt && force_plaintext == 0 {
|
let mut outer_message = if is_encrypted {
|
||||||
for header in protected_headers.into_iter() {
|
for header in protected_headers.into_iter() {
|
||||||
message = message.header(header);
|
message = message.header(header);
|
||||||
}
|
}
|
||||||
@@ -452,9 +528,6 @@ impl<'a> MimeFactory<'a> {
|
|||||||
)
|
)
|
||||||
.header(("Subject".to_string(), "...".to_string()));
|
.header(("Subject".to_string(), "...".to_string()));
|
||||||
|
|
||||||
let gossiped = do_gossip && !peerstates.is_empty();
|
|
||||||
self.finalize_mime_message(true, gossiped)?;
|
|
||||||
|
|
||||||
outer_message
|
outer_message
|
||||||
} else {
|
} else {
|
||||||
// In the unencrypted case, we add all headers to the outer message.
|
// In the unencrypted case, we add all headers to the outer message.
|
||||||
@@ -464,8 +537,6 @@ impl<'a> MimeFactory<'a> {
|
|||||||
for header in unprotected_headers.into_iter() {
|
for header in unprotected_headers.into_iter() {
|
||||||
message = message.header(header);
|
message = message.header(header);
|
||||||
}
|
}
|
||||||
self.finalize_mime_message(false, false)?;
|
|
||||||
|
|
||||||
message
|
message
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -473,11 +544,36 @@ impl<'a> MimeFactory<'a> {
|
|||||||
.header(Header::new_with_value("To".into(), to).unwrap())
|
.header(Header::new_with_value("To".into(), to).unwrap())
|
||||||
.header(Header::new_with_value("From".into(), vec![from]).unwrap());
|
.header(Header::new_with_value("From".into(), vec![from]).unwrap());
|
||||||
|
|
||||||
// TODO
|
let is_gossiped = is_encrypted && do_gossip && !peerstates.is_empty();
|
||||||
// self.envelope = Some(Envelope::new(Some(from), to).expect("setting from"));
|
|
||||||
self.out = outer_message.build().as_string().into_bytes();
|
|
||||||
|
|
||||||
Ok(())
|
let MimeFactory {
|
||||||
|
recipients_addr,
|
||||||
|
from_addr,
|
||||||
|
last_added_location_id,
|
||||||
|
msg,
|
||||||
|
loaded,
|
||||||
|
..
|
||||||
|
} = self;
|
||||||
|
|
||||||
|
let rfc724_mid = match loaded {
|
||||||
|
Loaded::Message => msg.rfc724_mid.clone(),
|
||||||
|
Loaded::MDN => dc_create_outgoing_rfc724_mid(None, &from_addr),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(RenderedEmail {
|
||||||
|
message: outer_message.build().as_string().into_bytes(),
|
||||||
|
// envelope: Envelope::new,
|
||||||
|
is_encrypted,
|
||||||
|
is_gossiped,
|
||||||
|
last_added_location_id,
|
||||||
|
foreign_id: match loaded {
|
||||||
|
Loaded::Message => Some(msg.id),
|
||||||
|
Loaded::MDN => None,
|
||||||
|
},
|
||||||
|
recipients: recipients_addr,
|
||||||
|
from: from_addr,
|
||||||
|
rfc724_mid,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_message(
|
fn render_message(
|
||||||
@@ -738,7 +834,7 @@ impl<'a> MimeFactory<'a> {
|
|||||||
is_multipart = true;
|
is_multipart = true;
|
||||||
if !self.msg.param.exists(Param::SetLatitude) {
|
if !self.msg.param.exists(Param::SetLatitude) {
|
||||||
// otherwise, the independent location is already filed
|
// otherwise, the independent location is already filed
|
||||||
self.out_last_added_location_id = last_added_location_id;
|
self.last_added_location_id = last_added_location_id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
@@ -761,100 +857,6 @@ impl<'a> MimeFactory<'a> {
|
|||||||
) -> Result<PartBuilder, Error> {
|
) -> Result<PartBuilder, Error> {
|
||||||
unimplemented!()
|
unimplemented!()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_msg(context: &Context, msg_id: MsgId) -> Result<MimeFactory<'_>, Error> {
|
|
||||||
let msg = Message::load_from_db(context, msg_id)?;
|
|
||||||
let chat = Chat::load_from_db(context, msg.chat_id)?;
|
|
||||||
let mut factory = MimeFactory::from_message(context, msg);
|
|
||||||
factory.chat = Some(chat);
|
|
||||||
|
|
||||||
// just set the chat above
|
|
||||||
let chat = factory.chat.as_ref().unwrap();
|
|
||||||
|
|
||||||
if chat.is_self_talk() {
|
|
||||||
factory
|
|
||||||
.recipients_names
|
|
||||||
.push(factory.from_displayname.to_string());
|
|
||||||
factory.recipients_addr.push(factory.from_addr.to_string());
|
|
||||||
} else {
|
|
||||||
context.sql.query_map(
|
|
||||||
"SELECT c.authname, c.addr \
|
|
||||||
FROM chats_contacts cc \
|
|
||||||
LEFT JOIN contacts c ON cc.contact_id=c.id \
|
|
||||||
WHERE cc.chat_id=? AND cc.contact_id>9;",
|
|
||||||
params![factory.msg.chat_id as i32],
|
|
||||||
|row| {
|
|
||||||
let authname: String = row.get(0)?;
|
|
||||||
let addr: String = row.get(1)?;
|
|
||||||
Ok((authname, addr))
|
|
||||||
},
|
|
||||||
|rows| {
|
|
||||||
for row in rows {
|
|
||||||
let (authname, addr) = row?;
|
|
||||||
if !vec_contains_lowercase(&factory.recipients_addr, &addr) {
|
|
||||||
factory.recipients_addr.push(addr);
|
|
||||||
factory.recipients_names.push(authname);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
},
|
|
||||||
)?;
|
|
||||||
|
|
||||||
let command = factory.msg.param.get_cmd();
|
|
||||||
let msg = &factory.msg;
|
|
||||||
|
|
||||||
/* for added members, the list is just fine */
|
|
||||||
if command == SystemMessage::MemberRemovedFromGroup {
|
|
||||||
let email_to_remove = msg.param.get(Param::Arg).unwrap_or_default();
|
|
||||||
|
|
||||||
let self_addr = context
|
|
||||||
.get_config(Config::ConfiguredAddr)
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
if !email_to_remove.is_empty() && !addr_cmp(email_to_remove, self_addr) {
|
|
||||||
if !vec_contains_lowercase(&factory.recipients_addr, &email_to_remove) {
|
|
||||||
factory.recipients_names.push("".to_string());
|
|
||||||
factory.recipients_addr.push(email_to_remove.to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if command != SystemMessage::AutocryptSetupMessage
|
|
||||||
&& command != SystemMessage::SecurejoinMessage
|
|
||||||
&& context.get_config_bool(Config::MdnsEnabled)
|
|
||||||
{
|
|
||||||
factory.req_mdn = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let row = context.sql.query_row(
|
|
||||||
"SELECT mime_in_reply_to, mime_references FROM msgs WHERE id=?",
|
|
||||||
params![factory.msg.id],
|
|
||||||
|row| {
|
|
||||||
let in_reply_to: String = row.get(0)?;
|
|
||||||
let references: String = row.get(1)?;
|
|
||||||
|
|
||||||
Ok((in_reply_to, references))
|
|
||||||
},
|
|
||||||
);
|
|
||||||
match row {
|
|
||||||
Ok((in_reply_to, references)) => {
|
|
||||||
factory.in_reply_to = in_reply_to;
|
|
||||||
factory.references = references;
|
|
||||||
}
|
|
||||||
Err(err) => {
|
|
||||||
error!(
|
|
||||||
context,
|
|
||||||
"mimefactory: failed to load mime_in_reply_to: {:?}", err
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
factory.loaded = Loaded::Message;
|
|
||||||
factory.timestamp = factory.msg.timestamp_sort;
|
|
||||||
factory.rfc724_mid = factory.msg.rfc724_mid.clone();
|
|
||||||
factory.increation = factory.msg.is_increation();
|
|
||||||
|
|
||||||
Ok(factory)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn build_body_file(
|
fn build_body_file(
|
||||||
|
|||||||
Reference in New Issue
Block a user