mirror of
https://github.com/chatmail/core.git
synced 2026-04-02 13:32:11 +03:00
Compare commits
1 Commits
fix_subjec
...
get_core_v
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fdf8a052e7 |
11
CHANGELOG.md
11
CHANGELOG.md
@@ -1,16 +1,5 @@
|
||||
# Changelog
|
||||
|
||||
## 1.0.0-beta.10 (pending)
|
||||
|
||||
- fix grpid-determination from in-reply-to and references headers. @hpk42
|
||||
|
||||
- only send Autocrypt-gossip headers on encrypted messages. @dignifiedquire
|
||||
|
||||
- fix reply-to-encrypted message to also be encrypted. @hpk42
|
||||
|
||||
- remove last unsafe code from dc_receive_imf :) @hpk42
|
||||
|
||||
|
||||
## 1.0.0-beta.9
|
||||
|
||||
- historic: we now use the mailparse crate and lettre-email to generate mime
|
||||
|
||||
36
Cargo.lock
generated
36
Cargo.lock
generated
@@ -620,7 +620,6 @@ dependencies = [
|
||||
"chrono 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"debug_stub_derive 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"deltachat_derive 0.1.0",
|
||||
"encoded-words 0.1.0 (git+https://github.com/async-email/encoded-words)",
|
||||
"escaper 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"failure_derive 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
@@ -797,20 +796,6 @@ dependencies = [
|
||||
"version_check 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoded-words"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/async-email/encoded-words#019e833f0c9ea7d4b0b693aab44e66d78d18f1d0"
|
||||
dependencies = [
|
||||
"base64 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"charset 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"encoding_rs 0.8.20 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"thiserror 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "encoding"
|
||||
version = "0.2.33"
|
||||
@@ -2775,24 +2760,6 @@ dependencies = [
|
||||
"wincolor 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"thiserror-impl 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
dependencies = [
|
||||
"proc-macro2 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"quote 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"syn 1.0.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thread-local-object"
|
||||
version = "0.1.0"
|
||||
@@ -3461,7 +3428,6 @@ dependencies = [
|
||||
"checksum ed25519-dalek 1.0.0-pre.2 (registry+https://github.com/rust-lang/crates.io-index)" = "845aaacc16f01178f33349e7c992ecd0cee095aa5e577f0f4dee35971bd36455"
|
||||
"checksum either 1.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3"
|
||||
"checksum email 0.0.21 (git+https://github.com/deltachat/rust-email)" = "<none>"
|
||||
"checksum encoded-words 0.1.0 (git+https://github.com/async-email/encoded-words)" = "<none>"
|
||||
"checksum encoding 0.2.33 (registry+https://github.com/rust-lang/crates.io-index)" = "6b0d943856b990d12d3b55b359144ff341533e516d94098b1d3fc1ac666d36ec"
|
||||
"checksum encoding-index-japanese 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "04e8b2ff42e9a05335dbf8b5c6f7567e5591d0d916ccef4e0b1710d32a0d0c91"
|
||||
"checksum encoding-index-korean 1.20141219.5 (registry+https://github.com/rust-lang/crates.io-index)" = "4dc33fb8e6bcba213fe2f14275f0963fd16f0a02c878e3095ecfdf5bee529d81"
|
||||
@@ -3678,8 +3644,6 @@ dependencies = [
|
||||
"checksum tempfile 3.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
|
||||
"checksum termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "adc4587ead41bf016f11af03e55a624c06568b5a19db4e90fde573d805074f83"
|
||||
"checksum termcolor 1.0.5 (registry+https://github.com/rust-lang/crates.io-index)" = "96d6098003bde162e4277c70665bd87c326f5a0c3f3fbfb285787fa482d54e6e"
|
||||
"checksum thiserror 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6f357d1814b33bc2dc221243f8424104bfe72dbe911d5b71b3816a2dff1c977e"
|
||||
"checksum thiserror-impl 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "eb2e25d25307eb8436894f727aba8f65d07adf02e5b35a13cebed48bd282bfef"
|
||||
"checksum thread-local-object 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7da3caa820d0308c84c8654f6cafd81cc3195d45433311cbe22fcf44fc8be071"
|
||||
"checksum thread_local 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c6b53e329000edc2b34dbe8545fd20e55a333362d0a321909685a19bd28c3f1b"
|
||||
"checksum time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "db8dcfca086c1143c9270ac42a2bbd8a7ee477b78ac8e45b19abfb0cbede4b6f"
|
||||
|
||||
@@ -54,7 +54,6 @@ rustls = "0.16.0"
|
||||
webpki-roots = "0.18.0"
|
||||
webpki = "0.21.0"
|
||||
mailparse = "0.10.1"
|
||||
encoded-words = { git = "https://github.com/async-email/encoded-words", branch="master" }
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile = "3.0"
|
||||
|
||||
@@ -87,15 +87,6 @@ $ cargo test --all
|
||||
$ cargo build -p deltachat_ffi --release
|
||||
```
|
||||
|
||||
## Debugging environment variables
|
||||
|
||||
- `DCC_IMAP_DEBUG`: if set IMAP protocol commands and responses will be
|
||||
printed
|
||||
|
||||
- `DCC_MIME_DEBUG`: if set outgoing and incoming message will be printed
|
||||
|
||||
|
||||
|
||||
### Expensive tests
|
||||
|
||||
Some tests are expensive and marked with `#[ignore]`, to run these
|
||||
|
||||
@@ -58,6 +58,8 @@ class Message(object):
|
||||
|
||||
def set_text(self, text):
|
||||
"""set text of this message. """
|
||||
assert self.id > 0, "message not prepared"
|
||||
assert self.is_out_preparing()
|
||||
lib.dc_msg_set_text(self._dc_msg, as_dc_charpointer(text))
|
||||
|
||||
@props.with_doc
|
||||
|
||||
@@ -430,32 +430,6 @@ class TestOnlineAccount:
|
||||
assert self_addr not in ev[2]
|
||||
ev = ac1._evlogger.get_matching("DC_EVENT_DELETED_BLOB_FILE")
|
||||
|
||||
def test_prepare_file_with_unicode(self, acfactory, lp):
|
||||
ac1, ac2 = acfactory.get_two_online_accounts()
|
||||
chat = self.get_chat(ac1, ac2)
|
||||
|
||||
lp.sec("ac1: prepare and send attachment + text to ac2")
|
||||
blobdir = ac1.get_blobdir()
|
||||
basename = "somedäüta.txt"
|
||||
p = os.path.join(blobdir, basename)
|
||||
with open(p, "w") as f:
|
||||
f.write("some data")
|
||||
msg = Message.new_empty(ac1, "file")
|
||||
msg.set_text("hello ä world")
|
||||
msg.set_file(p)
|
||||
message = chat.prepare_message(msg)
|
||||
assert message.is_out_preparing()
|
||||
assert message.text == "hello ä world"
|
||||
chat.send_prepared(message)
|
||||
|
||||
lp.sec("ac2: receive message")
|
||||
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
|
||||
assert ev[2] > const.DC_CHAT_ID_LAST_SPECIAL
|
||||
msg = ac2.get_message_by_id(ev[1])
|
||||
assert msg.text == "hello ä world"
|
||||
assert open(msg.filename).read() == "some data"
|
||||
assert msg.filename.endswith(basename)
|
||||
|
||||
def test_mvbox_sentbox_threads(self, acfactory, lp):
|
||||
lp.sec("ac1: start with mvbox thread")
|
||||
ac1 = acfactory.get_online_configuring_account(mvbox=True, sentbox=True)
|
||||
@@ -501,32 +475,27 @@ class TestOnlineAccount:
|
||||
ac1._evlogger.get_matching("DC_EVENT_IMAP_MESSAGE_MOVED")
|
||||
ac1._evlogger.get_matching("DC_EVENT_IMAP_MESSAGE_MOVED")
|
||||
|
||||
def test_forward_messages(self, acfactory, lp):
|
||||
def test_forward_messages(self, acfactory):
|
||||
ac1, ac2 = acfactory.get_two_online_accounts()
|
||||
chat = self.get_chat(ac1, ac2)
|
||||
|
||||
lp.sec("ac1: send message to ac2")
|
||||
msg_out = chat.send_text("message2")
|
||||
|
||||
lp.sec("ac2: wait for receive")
|
||||
# wait for other account to receive
|
||||
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
|
||||
assert ev[2] == msg_out.id
|
||||
msg_in = ac2.get_message_by_id(msg_out.id)
|
||||
assert msg_in.text == "message2"
|
||||
|
||||
lp.sec("ac2: check that the message arrive in deaddrop")
|
||||
# check the message arrived in contact-requests/deaddrop
|
||||
chat2 = msg_in.chat
|
||||
assert msg_in in chat2.get_messages()
|
||||
assert not msg_in.is_forwarded()
|
||||
assert chat2.is_deaddrop()
|
||||
assert chat2 == ac2.get_deaddrop_chat()
|
||||
|
||||
lp.sec("ac2: create new chat and forward message to it")
|
||||
chat3 = ac2.create_group_chat("newgroup")
|
||||
assert not chat3.is_promoted()
|
||||
ac2.forward_messages([msg_in], chat3)
|
||||
|
||||
lp.sec("ac2: check new chat has a forwarded message")
|
||||
assert chat3.is_promoted()
|
||||
messages = chat3.get_messages()
|
||||
msg = messages[-1]
|
||||
@@ -660,32 +629,6 @@ class TestOnlineAccount:
|
||||
ev = ac1._evlogger.get_matching("DC_EVENT_SMTP_MESSAGE_SENT")
|
||||
assert not msg.is_encrypted()
|
||||
|
||||
def test_send_first_message_as_long_unicode_with_cr(self, acfactory, lp):
|
||||
ac1, ac2 = acfactory.get_two_online_accounts()
|
||||
ac2.set_config("save_mime_headers", "1")
|
||||
|
||||
lp.sec("ac1: create chat with ac2")
|
||||
chat = self.get_chat(ac1, ac2, both_created=True)
|
||||
|
||||
lp.sec("sending multi-line non-unicode message from ac1 to ac2")
|
||||
text1 = "hello\nworld"
|
||||
msg_out = chat.send_text(text1)
|
||||
assert not msg_out.is_encrypted()
|
||||
|
||||
lp.sec("sending multi-line unicode text message from ac1 to ac2")
|
||||
text2 = "äalis\nthis is ßßÄ"
|
||||
msg_out = chat.send_text(text2)
|
||||
assert not msg_out.is_encrypted()
|
||||
|
||||
lp.sec("wait for ac2 to receive multi-line non-unicode message")
|
||||
msg_in = ac2.wait_next_incoming_message()
|
||||
assert msg_in.text == text1
|
||||
|
||||
lp.sec("wait for ac2 to receive multi-line unicode message")
|
||||
msg_in = ac2.wait_next_incoming_message()
|
||||
assert msg_in.text == text2
|
||||
assert ac1.get_config("addr") in msg_in.chat.get_name()
|
||||
|
||||
def test_reply_encrypted(self, acfactory, lp):
|
||||
ac1, ac2 = acfactory.get_two_online_accounts()
|
||||
|
||||
|
||||
@@ -57,13 +57,13 @@ class TestOnlineInCreation:
|
||||
|
||||
lp.sec("wait1 for original or forwarded messages to arrive")
|
||||
ev1 = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
|
||||
assert ev1[1] > const.DC_CHAT_ID_LAST_SPECIAL
|
||||
assert ev1[1] >= const.DC_CHAT_ID_LAST_SPECIAL
|
||||
received_original = ac2.get_message_by_id(ev1[2])
|
||||
assert cmp(received_original.filename, path, False)
|
||||
|
||||
lp.sec("wait2 for original or forwarded messages to arrive")
|
||||
ev2 = ac2._evlogger.get_matching("DC_EVENT_MSGS_CHANGED")
|
||||
assert ev2[1] > const.DC_CHAT_ID_LAST_SPECIAL
|
||||
assert ev2[1] >= const.DC_CHAT_ID_LAST_SPECIAL
|
||||
assert ev2[1] != ev1[1]
|
||||
received_copy = ac2.get_message_by_id(ev2[2])
|
||||
assert cmp(received_copy.filename, path, False)
|
||||
|
||||
@@ -76,6 +76,7 @@ impl Aheader {
|
||||
if let Ok(Some(value)) = headers.get_first_value("Autocrypt") {
|
||||
match Self::from_str(&value) {
|
||||
Ok(header) => {
|
||||
info!(context, "comparing {} - {}", header.addr, wanted_from);
|
||||
if addr_cmp(&header.addr, wanted_from) {
|
||||
return Some(header);
|
||||
}
|
||||
|
||||
@@ -579,10 +579,6 @@ pub fn unblock(context: &Context, chat_id: u32) {
|
||||
}
|
||||
|
||||
pub fn set_blocking(context: &Context, chat_id: u32, new_blocking: Blocked) -> bool {
|
||||
if chat_id == 0 {
|
||||
warn!(context, "ignoring setting of Block-status for chat_id=0");
|
||||
return false;
|
||||
}
|
||||
sql::execute(
|
||||
context,
|
||||
&context.sql,
|
||||
|
||||
@@ -47,10 +47,10 @@ pub fn dc_receive_imf(
|
||||
server_uid,
|
||||
);
|
||||
|
||||
if std::env::var(crate::DCC_MIME_DEBUG).is_ok() {
|
||||
info!(context, "dc_receive_imf: incoming message mime-body:");
|
||||
println!("{}", String::from_utf8_lossy(imf_raw));
|
||||
}
|
||||
// Parse the imf to mailimf_message. normally, this is done by mailimf_message_parse(),
|
||||
// however, as we also need the MIME data,
|
||||
// we use mailmime_parse() through dc_mimeparser (both call mailimf_struct_multiple_parse()
|
||||
// somewhen, I did not found out anything that speaks against this approach yet)
|
||||
|
||||
let mime_parser = MimeParser::from_bytes(context, imf_raw);
|
||||
let mut mime_parser = if let Err(err) = mime_parser {
|
||||
@@ -210,7 +210,7 @@ pub fn dc_receive_imf(
|
||||
&mut created_db_entries,
|
||||
&mut create_event_to_send,
|
||||
) {
|
||||
warn!(context, "add_parts error: {:?}", err);
|
||||
warn!(context, "{}", err);
|
||||
|
||||
cleanup(
|
||||
context,
|
||||
@@ -414,18 +414,18 @@ fn add_parts(
|
||||
Blocked::Deaddrop
|
||||
};
|
||||
|
||||
let (new_chat_id, new_chat_id_blocked) = create_or_lookup_group(
|
||||
create_or_lookup_group(
|
||||
context,
|
||||
&mut mime_parser,
|
||||
allow_creation,
|
||||
create_blocked,
|
||||
*from_id,
|
||||
to_ids,
|
||||
chat_id,
|
||||
&mut chat_id_blocked,
|
||||
)?;
|
||||
*chat_id = new_chat_id;
|
||||
chat_id_blocked = new_chat_id_blocked;
|
||||
if *chat_id != 0 && chat_id_blocked != Blocked::Not && create_blocked == Blocked::Not {
|
||||
chat::unblock(context, new_chat_id);
|
||||
if 0 != *chat_id && Blocked::Not != chat_id_blocked && create_blocked == Blocked::Not {
|
||||
chat::unblock(context, *chat_id);
|
||||
chat_id_blocked = Blocked::Not;
|
||||
}
|
||||
}
|
||||
@@ -507,19 +507,18 @@ fn add_parts(
|
||||
if !to_ids.is_empty() {
|
||||
*to_id = to_ids[0];
|
||||
if *chat_id == 0 {
|
||||
let (new_chat_id, new_chat_id_blocked) = create_or_lookup_group(
|
||||
create_or_lookup_group(
|
||||
context,
|
||||
&mut mime_parser,
|
||||
allow_creation,
|
||||
Blocked::Not,
|
||||
*from_id,
|
||||
to_ids,
|
||||
chat_id,
|
||||
&mut chat_id_blocked,
|
||||
)?;
|
||||
*chat_id = new_chat_id;
|
||||
chat_id_blocked = new_chat_id_blocked;
|
||||
// automatically unblock chat when the user sends a message
|
||||
if *chat_id != 0 && chat_id_blocked != Blocked::Not {
|
||||
chat::unblock(context, new_chat_id);
|
||||
if 0 != *chat_id && Blocked::Not != chat_id_blocked {
|
||||
chat::unblock(context, *chat_id);
|
||||
chat_id_blocked = Blocked::Not;
|
||||
}
|
||||
}
|
||||
@@ -790,7 +789,7 @@ fn calc_timestamps(
|
||||
/// - is there a group with the same recipients? if so, use this (if there are multiple, use the most recent one)
|
||||
/// - create an ad-hoc group based on the recipient list
|
||||
///
|
||||
/// on success the function returns the found/created (chat_id, chat_blocked) tuple .
|
||||
/// So when the function returns, the caller has the group id matching the current state of the group.
|
||||
#[allow(non_snake_case)]
|
||||
fn create_or_lookup_group(
|
||||
context: &Context,
|
||||
@@ -798,9 +797,14 @@ fn create_or_lookup_group(
|
||||
allow_creation: i32,
|
||||
create_blocked: Blocked,
|
||||
from_id: u32,
|
||||
to_ids: &[u32],
|
||||
) -> Result<(u32, Blocked)> {
|
||||
to_ids: &mut Vec<u32>,
|
||||
ret_chat_id: *mut u32,
|
||||
ret_chat_id_blocked: &mut Blocked,
|
||||
) -> Result<()> {
|
||||
let group_explicitly_left: bool;
|
||||
let mut chat_id = 0;
|
||||
let mut chat_id_blocked = Blocked::Not;
|
||||
let mut grpid = "".to_string();
|
||||
let mut grpname = None;
|
||||
let to_ids_cnt = to_ids.len();
|
||||
let mut recreate_member_list = 0;
|
||||
@@ -811,13 +815,26 @@ fn create_or_lookup_group(
|
||||
let mut X_MrGrpImageChanged = "".to_string();
|
||||
let mut better_msg: String = From::from("");
|
||||
|
||||
let cleanup = |ret_chat_id: *mut u32,
|
||||
ret_chat_id_blocked: &mut Blocked,
|
||||
chat_id: u32,
|
||||
chat_id_blocked: Blocked| {
|
||||
if !ret_chat_id.is_null() {
|
||||
unsafe { *ret_chat_id = chat_id };
|
||||
}
|
||||
*ret_chat_id_blocked = if 0 != chat_id {
|
||||
chat_id_blocked
|
||||
} else {
|
||||
Blocked::Not
|
||||
};
|
||||
};
|
||||
|
||||
if mime_parser.is_system_message == SystemMessage::LocationStreamingEnabled {
|
||||
better_msg =
|
||||
context.stock_system_msg(StockMessage::MsgLocationEnabled, "", "", from_id as u32)
|
||||
}
|
||||
set_better_msg(mime_parser, &better_msg);
|
||||
|
||||
let mut grpid = "".to_string();
|
||||
if let Some(optional_field) = mime_parser.lookup_field("Chat-Group-ID") {
|
||||
grpid = optional_field.clone();
|
||||
}
|
||||
@@ -826,26 +843,33 @@ fn create_or_lookup_group(
|
||||
if let Some(value) = mime_parser.lookup_field("Message-ID") {
|
||||
if let Some(extracted_grpid) = dc_extract_grpid_from_rfc724_mid(&value) {
|
||||
grpid = extracted_grpid.to_string();
|
||||
} else {
|
||||
grpid = "".to_string();
|
||||
}
|
||||
}
|
||||
if grpid.is_empty() {
|
||||
if let Some(extracted_grpid) = get_grpid_from_list(mime_parser, "In-Reply-To") {
|
||||
grpid = extracted_grpid;
|
||||
} else if let Some(extracted_grpid) = get_grpid_from_list(mime_parser, "References") {
|
||||
grpid = extracted_grpid;
|
||||
} else {
|
||||
return create_or_lookup_adhoc_group(
|
||||
context,
|
||||
mime_parser,
|
||||
allow_creation,
|
||||
create_blocked,
|
||||
from_id,
|
||||
to_ids,
|
||||
)
|
||||
.map_err(|err| {
|
||||
info!(context, "could not create adhoc-group: {:?}", err);
|
||||
err
|
||||
});
|
||||
if let Some(value) = mime_parser.lookup_field("In-Reply-To") {
|
||||
grpid = value.clone();
|
||||
}
|
||||
if grpid.is_empty() {
|
||||
if let Some(value) = mime_parser.lookup_field("References") {
|
||||
grpid = value.clone();
|
||||
}
|
||||
|
||||
if grpid.is_empty() {
|
||||
create_or_lookup_adhoc_group(
|
||||
context,
|
||||
mime_parser,
|
||||
allow_creation,
|
||||
create_blocked,
|
||||
from_id,
|
||||
to_ids,
|
||||
&mut chat_id,
|
||||
&mut chat_id_blocked,
|
||||
)?;
|
||||
cleanup(ret_chat_id, ret_chat_id_blocked, chat_id, chat_id_blocked);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -924,29 +948,26 @@ fn create_or_lookup_group(
|
||||
|
||||
// check, if we have a chat with this group ID
|
||||
let (mut chat_id, chat_id_verified, _blocked) = chat::get_chat_id_by_grpid(context, &grpid);
|
||||
if chat_id != 0 {
|
||||
if chat_id_verified {
|
||||
if let Err(err) =
|
||||
check_verified_properties(context, mime_parser, from_id as u32, to_ids)
|
||||
{
|
||||
warn!(context, "verification problem: {}", err);
|
||||
let s = format!("{}. See 'Info' for more details", err);
|
||||
mime_parser.repl_msg_by_error(s);
|
||||
}
|
||||
}
|
||||
// check if the sender is a member of the existing group -
|
||||
// if not, we'll recreate the group list
|
||||
if !chat::is_contact_in_chat(context, chat_id, from_id as u32) {
|
||||
recreate_member_list = 1;
|
||||
if chat_id != 0 && chat_id_verified {
|
||||
if let Err(err) = check_verified_properties(context, mime_parser, from_id as u32, to_ids) {
|
||||
warn!(context, "verification problem: {}", err);
|
||||
let s = format!("{}. See 'Info' for more details", err);
|
||||
mime_parser.repl_msg_by_error(s);
|
||||
}
|
||||
}
|
||||
|
||||
// check if the sender is a member of the existing group -
|
||||
// if not, we'll recreate the group list
|
||||
if chat_id != 0 && !chat::is_contact_in_chat(context, chat_id, from_id as u32) {
|
||||
recreate_member_list = 1;
|
||||
}
|
||||
|
||||
// check if the group does not exist but should be created
|
||||
let group_explicitly_left = chat::is_group_explicitly_left(context, &grpid).unwrap_or_default();
|
||||
group_explicitly_left = chat::is_group_explicitly_left(context, &grpid).unwrap_or_default();
|
||||
|
||||
let self_addr = context
|
||||
.get_config(Config::ConfiguredAddr)
|
||||
.unwrap_or_default();
|
||||
|
||||
if chat_id == 0
|
||||
&& !mime_parser.is_mailinglist_message()
|
||||
&& !grpid.is_empty()
|
||||
@@ -957,8 +978,10 @@ fn create_or_lookup_group(
|
||||
&& (!group_explicitly_left
|
||||
|| X_MrAddToGrp.is_some() && addr_cmp(&self_addr, X_MrAddToGrp.as_ref().unwrap()))
|
||||
{
|
||||
// group does not exist but should be created
|
||||
let create_verified = if mime_parser.lookup_field("Chat-Verified").is_some() {
|
||||
let mut create_verified = VerifiedStatus::Unverified;
|
||||
if mime_parser.lookup_field("Chat-Verified").is_some() {
|
||||
create_verified = VerifiedStatus::Verified;
|
||||
|
||||
if let Err(err) =
|
||||
check_verified_properties(context, mime_parser, from_id as u32, to_ids)
|
||||
{
|
||||
@@ -966,16 +989,11 @@ fn create_or_lookup_group(
|
||||
let s = format!("{}. See 'Info' for more details", err);
|
||||
mime_parser.repl_msg_by_error(&s);
|
||||
}
|
||||
VerifiedStatus::Verified
|
||||
} else {
|
||||
VerifiedStatus::Unverified
|
||||
};
|
||||
|
||||
if allow_creation == 0 {
|
||||
info!(context, "creating group forbidden by caller");
|
||||
return Ok((0, Blocked::Not));
|
||||
}
|
||||
|
||||
if 0 == allow_creation {
|
||||
cleanup(ret_chat_id, ret_chat_id_blocked, chat_id, chat_id_blocked);
|
||||
return Ok(());
|
||||
}
|
||||
chat_id = create_group_record(
|
||||
context,
|
||||
&grpid,
|
||||
@@ -989,8 +1007,9 @@ fn create_or_lookup_group(
|
||||
|
||||
// again, check chat_id
|
||||
if chat_id <= DC_CHAT_ID_LAST_SPECIAL {
|
||||
return if group_explicitly_left {
|
||||
Ok((DC_CHAT_ID_TRASH, chat_id_blocked))
|
||||
chat_id = 0;
|
||||
if group_explicitly_left {
|
||||
chat_id = DC_CHAT_ID_TRASH;
|
||||
} else {
|
||||
create_or_lookup_adhoc_group(
|
||||
context,
|
||||
@@ -999,12 +1018,12 @@ fn create_or_lookup_group(
|
||||
create_blocked,
|
||||
from_id,
|
||||
to_ids,
|
||||
)
|
||||
.map_err(|err| {
|
||||
warn!(context, "failed to create ad-hoc group: {:?}", err);
|
||||
err
|
||||
})
|
||||
};
|
||||
&mut chat_id,
|
||||
&mut chat_id_blocked,
|
||||
)?;
|
||||
}
|
||||
cleanup(ret_chat_id, ret_chat_id_blocked, chat_id, chat_id_blocked);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// execute group commands
|
||||
@@ -1109,78 +1128,74 @@ fn create_or_lookup_group(
|
||||
}
|
||||
|
||||
// check the number of receivers -
|
||||
// the only critical situation is if the user hits "Reply" instead
|
||||
// of "Reply all" in a non-messenger-client */
|
||||
if to_ids_cnt == 1
|
||||
&& !mime_parser.is_send_by_messenger
|
||||
&& chat::get_chat_contact_cnt(context, chat_id) > 3
|
||||
{
|
||||
// to_ids_cnt==1 may be "From: A, To: B, SELF" as SELF is not counted in to_ids_cnt.
|
||||
// So everything up to 3 is no error.
|
||||
create_or_lookup_adhoc_group(
|
||||
context,
|
||||
mime_parser,
|
||||
allow_creation,
|
||||
create_blocked,
|
||||
from_id,
|
||||
to_ids,
|
||||
)
|
||||
.map_err(|err| {
|
||||
warn!(context, "could not create ad-hoc group: {:?}", err);
|
||||
err
|
||||
})?;
|
||||
}
|
||||
Ok((chat_id, chat_id_blocked))
|
||||
}
|
||||
|
||||
/// try extract a grpid from a message-id list header value
|
||||
fn get_grpid_from_list(mime_parser: &MimeParser, header_key: &str) -> Option<String> {
|
||||
if let Some(value) = mime_parser.lookup_field(header_key) {
|
||||
for part in value.split(',').map(str::trim) {
|
||||
if !part.is_empty() {
|
||||
if let Some(extracted_grpid) = dc_extract_grpid_from_rfc724_mid(part) {
|
||||
return Some(extracted_grpid.to_string());
|
||||
}
|
||||
}
|
||||
// the only critical situation is if the user hits "Reply" instead of "Reply all" in a non-messenger-client */
|
||||
if to_ids_cnt == 1 && !mime_parser.is_send_by_messenger {
|
||||
let is_contact_cnt = chat::get_chat_contact_cnt(context, chat_id);
|
||||
if is_contact_cnt > 3 {
|
||||
// to_ids_cnt==1 may be "From: A, To: B, SELF" as SELF is not counted in to_ids_cnt.
|
||||
// So everything up to 3 is no error.
|
||||
chat_id = 0;
|
||||
create_or_lookup_adhoc_group(
|
||||
context,
|
||||
mime_parser,
|
||||
allow_creation,
|
||||
create_blocked,
|
||||
from_id,
|
||||
to_ids,
|
||||
&mut chat_id,
|
||||
&mut chat_id_blocked,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
None
|
||||
|
||||
cleanup(ret_chat_id, ret_chat_id_blocked, chat_id, chat_id_blocked);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Handle groups for received messages, return chat_id/Blocked status on success
|
||||
/// Handle groups for received messages
|
||||
fn create_or_lookup_adhoc_group(
|
||||
context: &Context,
|
||||
mime_parser: &MimeParser,
|
||||
allow_creation: i32,
|
||||
create_blocked: Blocked,
|
||||
from_id: u32,
|
||||
to_ids: &[u32],
|
||||
) -> Result<(u32, Blocked)> {
|
||||
// if we're here, no grpid was found, check if there is an existing
|
||||
// ad-hoc group matching the to-list or if we should and can create one
|
||||
// (we do not want to heuristically look at the likely mangled Subject)
|
||||
to_ids: &mut Vec<u32>,
|
||||
ret_chat_id: *mut u32,
|
||||
ret_chat_id_blocked: &mut Blocked,
|
||||
) -> Result<()> {
|
||||
// if we're here, no grpid was found, check there is an existing ad-hoc
|
||||
// group matching the to-list or if we can create one
|
||||
let mut chat_id = 0;
|
||||
let mut chat_id_blocked = Blocked::Not;
|
||||
|
||||
if mime_parser.is_mailinglist_message() {
|
||||
// XXX we could parse List-* headers and actually create and
|
||||
// manage a mailing list group, eventually
|
||||
info!(
|
||||
context,
|
||||
"not creating ad-hoc group for mailing list message"
|
||||
);
|
||||
return Ok((0, Blocked::Not));
|
||||
let cleanup = |ret_chat_id: *mut u32,
|
||||
ret_chat_id_blocked: &mut Blocked,
|
||||
chat_id: u32,
|
||||
chat_id_blocked: Blocked| {
|
||||
if !ret_chat_id.is_null() {
|
||||
unsafe { *ret_chat_id = chat_id };
|
||||
}
|
||||
*ret_chat_id_blocked = chat_id_blocked;
|
||||
};
|
||||
|
||||
// build member list from the given ids
|
||||
if to_ids.is_empty() || mime_parser.is_mailinglist_message() {
|
||||
// too few contacts or a mailinglist
|
||||
cleanup(ret_chat_id, ret_chat_id_blocked, chat_id, chat_id_blocked);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut member_ids = to_ids.to_vec();
|
||||
let mut member_ids = to_ids.clone();
|
||||
if !member_ids.contains(&from_id) {
|
||||
member_ids.push(from_id);
|
||||
}
|
||||
if !member_ids.contains(&DC_CONTACT_ID_SELF) {
|
||||
member_ids.push(DC_CONTACT_ID_SELF);
|
||||
}
|
||||
|
||||
if member_ids.len() < 3 {
|
||||
info!(context, "not creating ad-hoc group: too few contacts");
|
||||
return Ok((0, Blocked::Not));
|
||||
// too few contacts given
|
||||
cleanup(ret_chat_id, ret_chat_id_blocked, chat_id, chat_id_blocked);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let chat_ids = search_chat_ids_by_contact_ids(context, &member_ids)?;
|
||||
@@ -1199,16 +1214,18 @@ fn create_or_lookup_adhoc_group(
|
||||
);
|
||||
|
||||
if let Ok((id, id_blocked)) = res {
|
||||
chat_id = id as u32;
|
||||
chat_id_blocked = id_blocked;
|
||||
/* success, chat found */
|
||||
return Ok((id as u32, id_blocked));
|
||||
cleanup(ret_chat_id, ret_chat_id_blocked, chat_id, chat_id_blocked);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
if allow_creation == 0 {
|
||||
info!(context, "creating ad-hoc group prevented from caller");
|
||||
return Ok((0, Blocked::Not));
|
||||
if 0 == allow_creation {
|
||||
cleanup(ret_chat_id, ret_chat_id_blocked, chat_id, chat_id_blocked);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// we do not check if the message is a reply to another group, this may result in
|
||||
// chats with unclear member list. instead we create a new group in the following lines ...
|
||||
|
||||
@@ -1216,12 +1233,10 @@ fn create_or_lookup_adhoc_group(
|
||||
// - there is no need to check if this group exists; otherwise we would have caught it above
|
||||
let grpid = create_adhoc_grp_id(context, &member_ids);
|
||||
if grpid.is_empty() {
|
||||
warn!(
|
||||
context,
|
||||
"failed to create ad-hoc grpid for {:?}", member_ids
|
||||
);
|
||||
return Ok((0, Blocked::Not));
|
||||
cleanup(ret_chat_id, ret_chat_id_blocked, chat_id, chat_id_blocked);
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// use subject as initial chat name
|
||||
let grpname = if let Some(subject) = mime_parser.subject.as_ref().filter(|s| !s.is_empty()) {
|
||||
subject.to_string()
|
||||
@@ -1230,20 +1245,22 @@ fn create_or_lookup_adhoc_group(
|
||||
};
|
||||
|
||||
// create group record
|
||||
let new_chat_id = create_group_record(
|
||||
chat_id = create_group_record(
|
||||
context,
|
||||
&grpid,
|
||||
grpname,
|
||||
create_blocked,
|
||||
VerifiedStatus::Unverified,
|
||||
);
|
||||
chat_id_blocked = create_blocked;
|
||||
for &member_id in &member_ids {
|
||||
chat::add_to_chat_contacts_table(context, new_chat_id, member_id);
|
||||
chat::add_to_chat_contacts_table(context, chat_id, member_id);
|
||||
}
|
||||
|
||||
context.call_cb(Event::ChatModified(new_chat_id));
|
||||
context.call_cb(Event::ChatModified(chat_id));
|
||||
|
||||
Ok((new_chat_id, create_blocked))
|
||||
cleanup(ret_chat_id, ret_chat_id_blocked, chat_id, chat_id_blocked);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn create_group_record(
|
||||
@@ -1670,7 +1687,6 @@ fn add_or_lookup_contact_by_addr(
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::test_utils::dummy_context;
|
||||
|
||||
#[test]
|
||||
fn test_hex_hash() {
|
||||
@@ -1679,34 +1695,4 @@ mod tests {
|
||||
let res = hex_hash(data);
|
||||
assert_eq!(res, "b94d27b9934d3e08");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_grpid_simple() {
|
||||
let context = dummy_context();
|
||||
let raw = b"From: hello\n\
|
||||
Subject: outer-subject\n\
|
||||
In-Reply-To: <lqkjwelq123@123123>\n\
|
||||
References: <Gr.HcxyMARjyJy.9-uvzWPTLtV@nauta.cu>\n\
|
||||
\n\
|
||||
hello\x00";
|
||||
let mimeparser = MimeParser::from_bytes(&context.ctx, &raw[..]).unwrap();
|
||||
assert_eq!(get_grpid_from_list(&mimeparser, "In-Reply-To"), None);
|
||||
let grpid = Some("HcxyMARjyJy".to_string());
|
||||
assert_eq!(get_grpid_from_list(&mimeparser, "References"), grpid);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_grpid_from_multiple() {
|
||||
let context = dummy_context();
|
||||
let raw = b"From: hello\n\
|
||||
Subject: outer-subject\n\
|
||||
In-Reply-To: <Gr.HcxyMARjyJy.9-qweqwe@asd.net>\n\
|
||||
References: <qweqweqwe>, <Gr.HcxyMARjyJy.9-uvzWPTLtV@nau.ca>\n\
|
||||
\n\
|
||||
hello\x00";
|
||||
let mimeparser = MimeParser::from_bytes(&context.ctx, &raw[..]).unwrap();
|
||||
let grpid = Some("HcxyMARjyJy".to_string());
|
||||
assert_eq!(get_grpid_from_list(&mimeparser, "In-Reply-To"), grpid);
|
||||
assert_eq!(get_grpid_from_list(&mimeparser, "References"), grpid);
|
||||
}
|
||||
}
|
||||
|
||||
68
src/dc_strencode.rs
Normal file
68
src/dc_strencode.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
use itertools::Itertools;
|
||||
|
||||
/// Encode non-ascii-strings as `=?UTF-8?Q?Bj=c3=b6rn_Petersen?=`.
|
||||
/// Belongs to RFC 2047: https://tools.ietf.org/html/rfc2047
|
||||
///
|
||||
/// We do not fold at position 72; this would result in empty words as `=?utf-8?Q??=` which are correct,
|
||||
/// but cannot be displayed by some mail programs (eg. Android Stock Mail).
|
||||
/// however, this is not needed, as long as _one_ word is not longer than 72 characters.
|
||||
/// _if_ it is, the display may get weird. This affects the subject only.
|
||||
/// the best solution wor all this would be if libetpan encodes the line as only libetpan knowns when a header line is full.
|
||||
///
|
||||
/// @param to_encode Null-terminated UTF-8-string to encode.
|
||||
/// @return Returns the encoded string which must be free()'d when no longed needed.
|
||||
/// On errors, NULL is returned.
|
||||
pub fn dc_encode_header_words(input: impl AsRef<str>) -> String {
|
||||
let mut result = String::default();
|
||||
for (_, group) in &input.as_ref().chars().group_by(|c| c.is_whitespace()) {
|
||||
let word: String = group.collect();
|
||||
result.push_str("e_word(&word.as_bytes()));
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
|
||||
fn must_encode(byte: u8) -> bool {
|
||||
static SPECIALS: &[u8] = b",:!\"#$@[\\]^`{|}~=?_";
|
||||
|
||||
SPECIALS.iter().any(|b| *b == byte)
|
||||
}
|
||||
|
||||
fn quote_word(word: &[u8]) -> String {
|
||||
let mut result = String::default();
|
||||
let mut encoded = false;
|
||||
|
||||
for byte in word {
|
||||
let byte = *byte;
|
||||
if byte >= 128 || must_encode(byte) {
|
||||
result.push_str(&format!("={:2X}", byte));
|
||||
encoded = true;
|
||||
} else if byte == b' ' {
|
||||
result.push('_');
|
||||
encoded = true;
|
||||
} else {
|
||||
result.push(byte as _);
|
||||
}
|
||||
}
|
||||
|
||||
if encoded {
|
||||
result = format!("=?utf-8?Q?{}?=", &result);
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/* ******************************************************************************
|
||||
* Encode/decode header words, RFC 2047
|
||||
******************************************************************************/
|
||||
|
||||
pub fn dc_needs_ext_header(to_check: impl AsRef<str>) -> bool {
|
||||
let to_check = to_check.as_ref();
|
||||
|
||||
if to_check.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
to_check.chars().any(|c| {
|
||||
!c.is_ascii_alphanumeric() && c != '-' && c != '_' && c != '.' && c != '~' && c != '%'
|
||||
})
|
||||
}
|
||||
@@ -217,11 +217,8 @@ pub(crate) fn dc_create_outgoing_rfc724_mid(grpid: Option<&str>, from_addr: &str
|
||||
///
|
||||
/// # Arguments
|
||||
///
|
||||
/// * `mid` - A string that holds the message id. Leading/Trailing <>
|
||||
/// characters are automatically stripped.
|
||||
/// * `mid` - A string that holds the message id
|
||||
pub(crate) fn dc_extract_grpid_from_rfc724_mid(mid: &str) -> Option<&str> {
|
||||
let mid = mid.trim_start_matches('<').trim_end_matches('>');
|
||||
|
||||
if mid.len() < 9 || !mid.starts_with("Gr.") {
|
||||
return None;
|
||||
}
|
||||
@@ -691,16 +688,6 @@ mod tests {
|
||||
let mid = "Gr.1234567890123456.morerandom@domain.de";
|
||||
let grpid = dc_extract_grpid_from_rfc724_mid(mid);
|
||||
assert_eq!(grpid, Some("1234567890123456"));
|
||||
|
||||
// Should return extracted grpid for grpid with length of 11
|
||||
let mid = "<Gr.12345678901.morerandom@domain.de>";
|
||||
let grpid = dc_extract_grpid_from_rfc724_mid(mid);
|
||||
assert_eq!(grpid, Some("12345678901"));
|
||||
|
||||
// Should return extracted grpid for grpid with length of 11
|
||||
let mid = "<Gr.1234567890123456.morerandom@domain.de>";
|
||||
let grpid = dc_extract_grpid_from_rfc724_mid(mid);
|
||||
assert_eq!(grpid, Some("1234567890123456"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -11,6 +11,8 @@ use async_tls::client::TlsStream;
|
||||
|
||||
use crate::login_param::{dc_build_tls_config, CertificateChecks};
|
||||
|
||||
const DCC_IMAP_DEBUG: &str = "DCC_IMAP_DEBUG";
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum Client {
|
||||
Secure(ImapClient<TlsStream<TcpStream>>),
|
||||
@@ -40,7 +42,7 @@ impl Client {
|
||||
let tls_connector: async_tls::TlsConnector = Arc::new(tls_config).into();
|
||||
let tls_stream = tls_connector.connect(domain.as_ref(), stream)?.await?;
|
||||
let mut client = ImapClient::new(tls_stream);
|
||||
if std::env::var(crate::DCC_IMAP_DEBUG).is_ok() {
|
||||
if std::env::var(DCC_IMAP_DEBUG).is_ok() {
|
||||
client.debug = true;
|
||||
}
|
||||
|
||||
@@ -56,7 +58,7 @@ impl Client {
|
||||
let stream = TcpStream::connect(addr).await?;
|
||||
|
||||
let mut client = ImapClient::new(stream);
|
||||
if std::env::var(crate::DCC_IMAP_DEBUG).is_ok() {
|
||||
if std::env::var(DCC_IMAP_DEBUG).is_ok() {
|
||||
client.debug = true;
|
||||
}
|
||||
let _greeting = client
|
||||
|
||||
@@ -192,10 +192,6 @@ impl Job {
|
||||
// was sent we need to mark it in the database ASAP as we
|
||||
// otherwise might send it twice.
|
||||
let mut smtp = context.smtp.lock().unwrap();
|
||||
if std::env::var(crate::DCC_MIME_DEBUG).is_ok() {
|
||||
info!(context, "smtp-sending out mime message:");
|
||||
println!("{}", String::from_utf8_lossy(&body));
|
||||
}
|
||||
match smtp.send(context, recipients_list, body, self.job_id) {
|
||||
Err(crate::smtp::send::Error::SendError(err)) => {
|
||||
// Remote error, retry later.
|
||||
|
||||
10
src/lib.rs
10
src/lib.rs
@@ -1,6 +1,5 @@
|
||||
#![deny(clippy::correctness, missing_debug_implementations, clippy::all)]
|
||||
// for now we hide warnings to not clutter/hide errors during "cargo clippy"
|
||||
#![allow(
|
||||
#![warn(
|
||||
clippy::type_complexity,
|
||||
clippy::cognitive_complexity,
|
||||
clippy::too_many_arguments,
|
||||
@@ -76,13 +75,8 @@ mod dehtml;
|
||||
pub mod dc_array;
|
||||
pub mod dc_receive_imf;
|
||||
mod dc_simplify;
|
||||
mod dc_strencode;
|
||||
pub mod dc_tools;
|
||||
|
||||
/// if set imap/incoming and smtp/outgoing MIME messages will be printed
|
||||
pub const DCC_MIME_DEBUG: &str = "DCC_MIME_DEBUG";
|
||||
|
||||
/// if set IMAP protocol commands and responses will be printed
|
||||
pub const DCC_IMAP_DEBUG: &str = "DCC_IMAP_DEBUG";
|
||||
|
||||
#[cfg(test)]
|
||||
mod test_utils;
|
||||
|
||||
@@ -6,6 +6,7 @@ use crate::config::Config;
|
||||
use crate::constants::*;
|
||||
use crate::contact::*;
|
||||
use crate::context::{get_version_str, Context};
|
||||
use crate::dc_strencode::*;
|
||||
use crate::dc_tools::*;
|
||||
use crate::e2ee::*;
|
||||
use crate::error::Error;
|
||||
@@ -332,19 +333,13 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
Loaded::Message => {
|
||||
match self.chat {
|
||||
Some(ref chat) => {
|
||||
let raw = message::get_summarytext_by_raw(
|
||||
let raw_subject = message::get_summarytext_by_raw(
|
||||
self.msg.type_0,
|
||||
self.msg.text.as_ref(),
|
||||
&self.msg.param,
|
||||
32,
|
||||
self.context,
|
||||
);
|
||||
let mut lines = raw.lines();
|
||||
let raw_subject = if let Some(line) = lines.next() {
|
||||
line
|
||||
} else {
|
||||
""
|
||||
};
|
||||
|
||||
let afwd_email = self.msg.param.exists(Param::Forwarded);
|
||||
let fwd = if afwd_email { "Fwd: " } else { "" };
|
||||
@@ -382,7 +377,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
let mut unprotected_headers: Vec<Header> = Vec::new();
|
||||
|
||||
let from = Address::new_mailbox_with_name(
|
||||
encode_words(&self.from_displayname),
|
||||
dc_encode_header_words(&self.from_displayname),
|
||||
self.from_addr.clone(),
|
||||
);
|
||||
|
||||
@@ -394,7 +389,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
to.push(Address::new_mailbox(addr.clone()));
|
||||
} else {
|
||||
to.push(Address::new_mailbox_with_name(
|
||||
encode_words(name),
|
||||
dc_encode_header_words(name),
|
||||
addr.clone(),
|
||||
));
|
||||
}
|
||||
@@ -450,7 +445,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
let e2ee_guranteed = self.is_e2ee_guranteed();
|
||||
let mut encrypt_helper = EncryptHelper::new(self.context)?;
|
||||
|
||||
let subject = encode_words(&subject_str);
|
||||
let subject = dc_encode_header_words(subject_str);
|
||||
|
||||
let mut message = match self.loaded {
|
||||
Loaded::Message => {
|
||||
@@ -610,7 +605,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
||||
if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup {
|
||||
protected_headers.push(Header::new("Chat-Group-ID".into(), chat.grpid.clone()));
|
||||
|
||||
let encoded = encode_words(&chat.name);
|
||||
let encoded = dc_encode_header_words(&chat.name);
|
||||
protected_headers.push(Header::new("Chat-Group-Name".into(), encoded));
|
||||
|
||||
match command {
|
||||
@@ -980,18 +975,20 @@ fn build_body_file(
|
||||
}
|
||||
};
|
||||
|
||||
let needs_ext = dc_needs_ext_header(&filename_to_send);
|
||||
|
||||
// create mime part, for Content-Disposition, see RFC 2183.
|
||||
// `Content-Disposition: attachment` seems not to make a difference to `Content-Disposition: inline`
|
||||
// at least on tested Thunderbird and Gma'l in 2017.
|
||||
// But I've heard about problems with inline and outl'k, so we just use the attachment-type until we
|
||||
// run into other problems ...
|
||||
let cd_value = if needs_encoding(&filename_to_send) {
|
||||
let cd_value = if needs_ext {
|
||||
format!("attachment; filename=\"{}\"", &filename_to_send)
|
||||
} else {
|
||||
format!(
|
||||
"attachment; filename*=\"{}\"",
|
||||
encode_words(&filename_to_send)
|
||||
dc_encode_header_words(&filename_to_send)
|
||||
)
|
||||
} else {
|
||||
format!("attachment; filename=\"{}\"", &filename_to_send)
|
||||
};
|
||||
|
||||
let body = std::fs::read(blob.to_abs_path())?;
|
||||
@@ -1025,23 +1022,3 @@ fn is_file_size_okay(context: &Context, msg: &Message) -> bool {
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
/* ******************************************************************************
|
||||
* Encode/decode header words, RFC 2047
|
||||
******************************************************************************/
|
||||
|
||||
fn encode_words(word: &str) -> String {
|
||||
encoded_words::encode(word, None, encoded_words::EncodingFlag::Shortest, None)
|
||||
}
|
||||
|
||||
pub fn needs_encoding(to_check: impl AsRef<str>) -> bool {
|
||||
let to_check = to_check.as_ref();
|
||||
|
||||
if to_check.is_empty() {
|
||||
return false;
|
||||
}
|
||||
|
||||
to_check.chars().any(|c| {
|
||||
!c.is_ascii_alphanumeric() && c != '-' && c != '_' && c != '.' && c != '~' && c != '%'
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user