mirror of
https://github.com/chatmail/core.git
synced 2026-05-16 21:36:30 +03:00
use rfc2047 crate from @valodim and remove dc_strencode.rs completely
This commit is contained in:
7
Cargo.lock
generated
7
Cargo.lock
generated
@@ -644,6 +644,7 @@ dependencies = [
|
|||||||
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rand 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"regex 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"reqwest 0.9.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
"reqwest 0.9.22 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"rfc2047 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rusqlite 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustls 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustls 0.16.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"rustyline 4.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"rustyline 4.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@@ -2286,6 +2287,11 @@ dependencies = [
|
|||||||
"winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"winreg 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rfc2047"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ring"
|
name = "ring"
|
||||||
version = "0.16.9"
|
version = "0.16.9"
|
||||||
@@ -3591,6 +3597,7 @@ dependencies = [
|
|||||||
"checksum rental 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8545debe98b2b139fb04cad8618b530e9b07c152d99a5de83c860b877d67847f"
|
"checksum rental 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "8545debe98b2b139fb04cad8618b530e9b07c152d99a5de83c860b877d67847f"
|
||||||
"checksum rental-impl 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "475e68978dc5b743f2f40d8e0a8fdc83f1c5e78cbf4b8fa5e74e73beebc340de"
|
"checksum rental-impl 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "475e68978dc5b743f2f40d8e0a8fdc83f1c5e78cbf4b8fa5e74e73beebc340de"
|
||||||
"checksum reqwest 0.9.22 (registry+https://github.com/rust-lang/crates.io-index)" = "2c2064233e442ce85c77231ebd67d9eca395207dec2127fe0bbedde4bd29a650"
|
"checksum reqwest 0.9.22 (registry+https://github.com/rust-lang/crates.io-index)" = "2c2064233e442ce85c77231ebd67d9eca395207dec2127fe0bbedde4bd29a650"
|
||||||
|
"checksum rfc2047 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6986b5de4fa30c92b50020fdedf45bf9296d9842df4355b62f92dcc18bcdee15"
|
||||||
"checksum ring 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6747f8da1f2b1fabbee1aaa4eb8a11abf9adef0bf58a41cee45db5d59cecdfac"
|
"checksum ring 0.16.9 (registry+https://github.com/rust-lang/crates.io-index)" = "6747f8da1f2b1fabbee1aaa4eb8a11abf9adef0bf58a41cee45db5d59cecdfac"
|
||||||
"checksum ripemd160 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad5112e0dbbb87577bfbc56c42450235e3012ce336e29c5befd7807bd626da4a"
|
"checksum ripemd160 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ad5112e0dbbb87577bfbc56c42450235e3012ce336e29c5befd7807bd626da4a"
|
||||||
"checksum rsa 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5108a8bbfb84fe77d829d77d5a89255dcd189dfe5c4de5a33d0a47f12808bb15"
|
"checksum rsa 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5108a8bbfb84fe77d829d77d5a89255dcd189dfe5c4de5a33d0a47f12808bb15"
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ num-derive = "0.2.5"
|
|||||||
num-traits = "0.2.6"
|
num-traits = "0.2.6"
|
||||||
lettre = { git = "https://github.com/deltachat/lettre", branch = "feat/mail" }
|
lettre = { git = "https://github.com/deltachat/lettre", branch = "feat/mail" }
|
||||||
lettre_email = { git = "https://github.com/deltachat/lettre", branch = "feat/mail" }
|
lettre_email = { git = "https://github.com/deltachat/lettre", branch = "feat/mail" }
|
||||||
|
rfc2047 = "0.1.1"
|
||||||
async-imap = { git = "https://github.com/async-email/async-imap", branch="master" }
|
async-imap = { git = "https://github.com/async-email/async-imap", branch="master" }
|
||||||
async-tls = "0.6"
|
async-tls = "0.6"
|
||||||
async-std = { version = "1.0", features = ["unstable"] }
|
async-std = { version = "1.0", features = ["unstable"] }
|
||||||
|
|||||||
@@ -430,29 +430,31 @@ class TestOnlineAccount:
|
|||||||
assert self_addr not in ev[2]
|
assert self_addr not in ev[2]
|
||||||
ev = ac1._evlogger.get_matching("DC_EVENT_DELETED_BLOB_FILE")
|
ev = ac1._evlogger.get_matching("DC_EVENT_DELETED_BLOB_FILE")
|
||||||
|
|
||||||
def test_prepare_file(self, acfactory, lp):
|
def test_prepare_file_with_unicode(self, acfactory, lp):
|
||||||
ac1, ac2 = acfactory.get_two_online_accounts()
|
ac1, ac2 = acfactory.get_two_online_accounts()
|
||||||
chat = self.get_chat(ac1, ac2)
|
chat = self.get_chat(ac1, ac2)
|
||||||
|
|
||||||
lp.sec("ac1: prepare and send attachment + text to ac2")
|
lp.sec("ac1: prepare and send attachment + text to ac2")
|
||||||
blobdir = ac1.get_blobdir()
|
blobdir = ac1.get_blobdir()
|
||||||
p = os.path.join(blobdir, "somedata.txt")
|
basename = "somedata.txt" # XXX try unicode
|
||||||
|
p = os.path.join(blobdir, basename)
|
||||||
with open(p, "w") as f:
|
with open(p, "w") as f:
|
||||||
f.write("some data")
|
f.write("some data")
|
||||||
msg = Message.new_empty(ac1, "file")
|
msg = Message.new_empty(ac1, "file")
|
||||||
msg.set_text("hello world")
|
msg.set_text("hello ä world")
|
||||||
msg.set_file(p)
|
msg.set_file(p)
|
||||||
message = chat.prepare_message(msg)
|
message = chat.prepare_message(msg)
|
||||||
assert message.is_out_preparing()
|
assert message.is_out_preparing()
|
||||||
assert message.text == "hello world"
|
assert message.text == "hello ä world"
|
||||||
chat.send_prepared(message)
|
chat.send_prepared(message)
|
||||||
|
|
||||||
lp.sec("ac2: receive message")
|
lp.sec("ac2: receive message")
|
||||||
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
|
ev = ac2._evlogger.get_matching("DC_EVENT_INCOMING_MSG|DC_EVENT_MSGS_CHANGED")
|
||||||
assert ev[2] > const.DC_CHAT_ID_LAST_SPECIAL
|
assert ev[2] > const.DC_CHAT_ID_LAST_SPECIAL
|
||||||
msg = ac2.get_message_by_id(ev[1])
|
msg = ac2.get_message_by_id(ev[1])
|
||||||
assert msg.text == "hello world"
|
assert msg.text == "hello ä world"
|
||||||
assert open(msg.filename).read() == "some data"
|
assert open(msg.filename).read() == "some data"
|
||||||
|
assert msg.filename.endswith(basename)
|
||||||
|
|
||||||
def test_mvbox_sentbox_threads(self, acfactory, lp):
|
def test_mvbox_sentbox_threads(self, acfactory, lp):
|
||||||
lp.sec("ac1: start with mvbox thread")
|
lp.sec("ac1: start with mvbox thread")
|
||||||
|
|||||||
@@ -1,69 +0,0 @@
|
|||||||
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 {
|
|
||||||
// XXX do we need to put ":" in here?
|
|
||||||
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 != '%'
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
#![deny(clippy::correctness, missing_debug_implementations, clippy::all)]
|
#![deny(clippy::correctness, missing_debug_implementations, clippy::all)]
|
||||||
#![warn(
|
// for now we hide warnings to not clutter/hide errors during "cargo check"
|
||||||
|
#![allow(
|
||||||
clippy::type_complexity,
|
clippy::type_complexity,
|
||||||
clippy::cognitive_complexity,
|
clippy::cognitive_complexity,
|
||||||
clippy::too_many_arguments,
|
clippy::too_many_arguments,
|
||||||
@@ -75,7 +76,6 @@ mod dehtml;
|
|||||||
pub mod dc_array;
|
pub mod dc_array;
|
||||||
pub mod dc_receive_imf;
|
pub mod dc_receive_imf;
|
||||||
mod dc_simplify;
|
mod dc_simplify;
|
||||||
mod dc_strencode;
|
|
||||||
pub mod dc_tools;
|
pub mod dc_tools;
|
||||||
|
|
||||||
/// if set imap/incoming and smtp/outgoing MIME messages will be printed
|
/// if set imap/incoming and smtp/outgoing MIME messages will be printed
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
use chrono::TimeZone;
|
use chrono::TimeZone;
|
||||||
use lettre_email::{mime, Address, Header, MimeMultipartType, PartBuilder};
|
use lettre_email::{mime, Address, Header, MimeMultipartType, PartBuilder};
|
||||||
|
use rfc2047::rfc2047_encode;
|
||||||
|
|
||||||
use crate::chat::{self, Chat};
|
use crate::chat::{self, Chat};
|
||||||
use crate::config::Config;
|
use crate::config::Config;
|
||||||
use crate::constants::*;
|
use crate::constants::*;
|
||||||
use crate::contact::*;
|
use crate::contact::*;
|
||||||
use crate::context::{get_version_str, Context};
|
use crate::context::{get_version_str, Context};
|
||||||
use crate::dc_strencode::*;
|
|
||||||
use crate::dc_tools::*;
|
use crate::dc_tools::*;
|
||||||
use crate::e2ee::*;
|
use crate::e2ee::*;
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
@@ -383,7 +383,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
|||||||
let mut unprotected_headers: Vec<Header> = Vec::new();
|
let mut unprotected_headers: Vec<Header> = Vec::new();
|
||||||
|
|
||||||
let from = Address::new_mailbox_with_name(
|
let from = Address::new_mailbox_with_name(
|
||||||
dc_encode_header_words(&self.from_displayname),
|
rfc2047_encode(&self.from_displayname).into_owned(),
|
||||||
self.from_addr.clone(),
|
self.from_addr.clone(),
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -395,7 +395,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
|||||||
to.push(Address::new_mailbox(addr.clone()));
|
to.push(Address::new_mailbox(addr.clone()));
|
||||||
} else {
|
} else {
|
||||||
to.push(Address::new_mailbox_with_name(
|
to.push(Address::new_mailbox_with_name(
|
||||||
dc_encode_header_words(name),
|
rfc2047_encode(name).into_owned(),
|
||||||
addr.clone(),
|
addr.clone(),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
@@ -451,7 +451,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
|||||||
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)?;
|
||||||
|
|
||||||
let subject = dc_encode_header_words(subject_str);
|
let subject = rfc2047_encode(&subject_str).into_owned();
|
||||||
|
|
||||||
let mut message = match self.loaded {
|
let mut message = match self.loaded {
|
||||||
Loaded::Message => {
|
Loaded::Message => {
|
||||||
@@ -611,7 +611,7 @@ impl<'a, 'b> MimeFactory<'a, 'b> {
|
|||||||
if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup {
|
if chat.typ == Chattype::Group || chat.typ == Chattype::VerifiedGroup {
|
||||||
protected_headers.push(Header::new("Chat-Group-ID".into(), chat.grpid.clone()));
|
protected_headers.push(Header::new("Chat-Group-ID".into(), chat.grpid.clone()));
|
||||||
|
|
||||||
let encoded = dc_encode_header_words(&chat.name);
|
let encoded = rfc2047_encode(&chat.name).into_owned();
|
||||||
protected_headers.push(Header::new("Chat-Group-Name".into(), encoded));
|
protected_headers.push(Header::new("Chat-Group-Name".into(), encoded));
|
||||||
|
|
||||||
match command {
|
match command {
|
||||||
@@ -991,9 +991,10 @@ fn build_body_file(
|
|||||||
let cd_value = if needs_ext {
|
let cd_value = if needs_ext {
|
||||||
format!("attachment; filename=\"{}\"", &filename_to_send)
|
format!("attachment; filename=\"{}\"", &filename_to_send)
|
||||||
} else {
|
} else {
|
||||||
|
// XXX do we need to encode filenames?
|
||||||
format!(
|
format!(
|
||||||
"attachment; filename*=\"{}\"",
|
"attachment; filename*=\"{}\"",
|
||||||
dc_encode_header_words(&filename_to_send)
|
rfc2047_encode(&filename_to_send)
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1028,3 +1029,19 @@ fn is_file_size_okay(context: &Context, msg: &Message) -> bool {
|
|||||||
None => false,
|
None => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ******************************************************************************
|
||||||
|
* 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 != '%'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user