diff --git a/Cargo.lock b/Cargo.lock index 0526d1706..bd4497790 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -635,7 +635,6 @@ dependencies = [ "hex 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "image-meta 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", - "jetscii 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "lettre 0.9.2 (git+https://github.com/deltachat/lettre?branch=feat/rustls)", "lettre_email 0.9.2 (git+https://github.com/deltachat/lettre?branch=feat/rustls)", @@ -1367,11 +1366,6 @@ name = "itoa" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -[[package]] -name = "jetscii" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "js-sys" version = "0.3.32" @@ -3509,7 +3503,6 @@ dependencies = [ "checksum iovec 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "b2b3ea6ff95e175473f8ffe6a7eb7c00d054240321b84c57051175fe3c1e075e" "checksum itertools 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)" = "f56a2d0bc861f9165be4eb3442afd3c236d8a98afd426f65d92324ae1091a484" "checksum itoa 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "501266b7edd0174f8530248f87f99c88fbe60ca4ef3dd486835b8d8d53136f7f" -"checksum jetscii 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "5f25cca2463cb19dbb1061eb3bd38a8b5e4ce1cc5a5a9fc0e02de486d92b9b05" "checksum js-sys 0.3.32 (registry+https://github.com/rust-lang/crates.io-index)" = "1c840fdb2167497b0bd0db43d6dfe61e91637fa72f9d061f8bd17ddc44ba6414" "checksum keccak 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "67c21572b4949434e4fc1e1978b99c5f77064153c59d998bf13ecd96fb5ecba7" "checksum kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" diff --git a/Cargo.toml b/Cargo.toml index 7fa9f7d1b..5d364b7f9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -47,7 +47,6 @@ image-meta = "0.1.0" quick-xml = "0.15.0" escaper = "0.1.0" bitflags = "1.1.0" -jetscii = "0.4.4" debug_stub_derive = "0.3.0" sanitize-filename = "0.2.1" stop-token = { version = "0.1.1", features = ["unstable"] } diff --git a/src/dc_strencode.rs b/src/dc_strencode.rs index 7f6eb1413..dcf589a84 100644 --- a/src/dc_strencode.rs +++ b/src/dc_strencode.rs @@ -1,28 +1,21 @@ use itertools::Itertools; use std::borrow::Cow; -use std::ffi::CString; -use std::ptr; use charset::Charset; -use libc::free; use percent_encoding::{percent_decode, utf8_percent_encode, AsciiSet, CONTROLS}; -use crate::dc_tools::*; - -/** - * 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. - */ +/// 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) -> String { let mut result = String::default(); for (_, group) in &input.as_ref().chars().group_by(|c| c.is_whitespace()) { @@ -83,107 +76,3 @@ pub fn dc_needs_ext_header(to_check: impl AsRef) -> bool { || c == '%') }) } - -const EXT_ASCII_ST: &AsciiSet = &CONTROLS - .add(b' ') - .add(b'-') - .add(b'_') - .add(b'.') - .add(b'~') - .add(b'%'); - -/// Encode an UTF-8 string to the extended header format. -pub fn dc_encode_ext_header(to_encode: impl AsRef) -> String { - let encoded = utf8_percent_encode(to_encode.as_ref(), &EXT_ASCII_ST); - format!("utf-8''{}", encoded) -} - -/// Decode an extended-header-format strings to UTF-8. -pub fn dc_decode_ext_header(to_decode: &[u8]) -> Cow { - if let Some(index) = bytes!(b'\'').find(to_decode) { - let (charset, rest) = to_decode.split_at(index); - if !charset.is_empty() { - // skip language - if let Some(index2) = bytes!(b'\'').find(&rest[1..]) { - let decoded = percent_decode(&rest[index2 + 2..]); - - if charset != b"utf-8" && charset != b"UTF-8" { - if let Some(encoding) = Charset::for_label(charset) { - let bytes = decoded.collect::>(); - let (res, _, _) = encoding.decode(&bytes); - return Cow::Owned(res.into_owned()); - } else { - return decoded.decode_utf8_lossy(); - } - } else { - return decoded.decode_utf8_lossy(); - } - } - } - } - - String::from_utf8_lossy(to_decode) -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_dc_encode_ext_header() { - let buf1 = dc_encode_ext_header("Björn Petersen"); - assert_eq!(&buf1, "utf-8\'\'Bj%C3%B6rn%20Petersen"); - let buf2 = dc_decode_ext_header(buf1.as_bytes()); - assert_eq!(&buf2, "Björn Petersen",); - - let buf1 = dc_decode_ext_header(b"iso-8859-1\'en\'%A3%20rates"); - assert_eq!(buf1, "£ rates",); - - let buf1 = dc_decode_ext_header(b"wrong\'format"); - assert_eq!(buf1, "wrong\'format",); - - let buf1 = dc_decode_ext_header(b"\'\'"); - assert_eq!(buf1, "\'\'"); - - let buf1 = dc_decode_ext_header(b"x\'\'"); - assert_eq!(buf1, ""); - - let buf1 = dc_decode_ext_header(b"\'"); - assert_eq!(buf1, "\'"); - - let buf1 = dc_decode_ext_header(b""); - assert_eq!(buf1, ""); - - // regressions - assert_eq!( - dc_decode_ext_header(dc_encode_ext_header("%0A").as_bytes()), - "%0A" - ); - } - - #[test] - fn test_dc_needs_ext_header() { - assert_eq!(dc_needs_ext_header("Björn"), true); - assert_eq!(dc_needs_ext_header("Bjoern"), false); - assert_eq!(dc_needs_ext_header(""), false); - assert_eq!(dc_needs_ext_header(" "), true); - assert_eq!(dc_needs_ext_header("a b"), true); - } - - use proptest::prelude::*; - - proptest! { - #[test] - fn test_ext_header_roundtrip(buf: String) { - let encoded = dc_encode_ext_header(&buf); - let decoded = dc_decode_ext_header(encoded.as_bytes()); - assert_eq!(buf, decoded); - } - - #[test] - fn test_ext_header_decode_anything(buf: Vec) { - // make sure this never panics - let _decoded = dc_decode_ext_header(&buf); - } - } -} diff --git a/src/dc_tools.rs b/src/dc_tools.rs index 87db1b882..b0409319e 100644 --- a/src/dc_tools.rs +++ b/src/dc_tools.rs @@ -7,7 +7,7 @@ use std::ffi::{CStr, CString}; use std::path::{Path, PathBuf}; use std::str::FromStr; use std::time::SystemTime; -use std::{fmt, fs, ptr}; +use std::{fmt, fs}; use chrono::{Local, TimeZone}; use libc::{memcpy, strlen}; @@ -857,20 +857,6 @@ pub(crate) unsafe fn strdup(s: *const libc::c_char) -> *mut libc::c_char { result as *mut _ } -pub(crate) unsafe fn strcasecmp(s1: *const libc::c_char, s2: *const libc::c_char) -> libc::c_int { - let s1 = std::ffi::CStr::from_ptr(s1) - .to_string_lossy() - .to_lowercase(); - let s2 = std::ffi::CStr::from_ptr(s2) - .to_string_lossy() - .to_lowercase(); - if s1 == s2 { - 0 - } else { - 1 - } -} - #[cfg(test)] mod tests { use super::*; diff --git a/src/e2ee.rs b/src/e2ee.rs index f48dc8452..4f6b6ce06 100644 --- a/src/e2ee.rs +++ b/src/e2ee.rs @@ -15,10 +15,6 @@ use crate::pgp; use crate::securejoin::handle_degrade_event; use crate::wrapmime; -// standard mime-version header aka b"Version: 1\r\n\x00" -static mut VERSION_CONTENT: [libc::c_char; 13] = - [86, 101, 114, 115, 105, 111, 110, 58, 32, 49, 13, 10, 0]; - #[derive(Debug)] pub struct EncryptHelper { pub prefer_encrypt: EncryptPreference, @@ -91,7 +87,6 @@ impl EncryptHelper { pub fn encrypt( &mut self, context: &Context, - e2ee_guaranteed: bool, min_verified: PeerstateVerifiedStatus, do_gossip: bool, mut mail_to_encrypt: lettre_email::PartBuilder, @@ -215,43 +210,6 @@ pub fn try_decrypt( Ok((out_mail, signatures, gossipped_addr)) } -// fn new_data_part( -// data: *mut libc::c_void, -// data_bytes: libc::size_t, -// content_type: &str, -// default_encoding: u32, -// ) -> Result<*mut Mailmime> { -// let content = new_content_type(&content_type)?; -// let mut encoding = ptr::null_mut(); -// if wrapmime::content_type_needs_encoding(content) { -// encoding = { mailmime_mechanism_new(default_encoding as i32, ptr::null_mut()) }; -// ensure!(!encoding.is_null(), "failed to create encoding"); -// } -// let mime_fields = { -// { -// mailmime_fields_new_with_data( -// encoding, -// ptr::null_mut(), -// ptr::null_mut(), -// ptr::null_mut(), -// ptr::null_mut(), -// ) -// } -// }; -// ensure!(!mime_fields.is_null(), "internal mime error"); -// -// let mime = { mailmime_new_empty(content, mime_fields) }; -// ensure!(!mime.is_null(), "internal mime error"); -// -// if { (*mime).mm_type } == MAILMIME_SINGLE as libc::c_int { -// if !data.is_null() && data_bytes > 0 { -// { mailmime_set_body_text(mime, data as *mut libc::c_char, data_bytes) }; -// } -// } -// -// Ok(mime) -// } - /// Load public key from database or generate a new one. /// /// This will load a public key from the database, generating and diff --git a/src/lib.rs b/src/lib.rs index 9f75b5303..698edee37 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -17,8 +17,6 @@ extern crate strum; #[macro_use] extern crate strum_macros; #[macro_use] -extern crate jetscii; -#[macro_use] extern crate debug_stub_derive; #[macro_use] diff --git a/src/mimefactory.rs b/src/mimefactory.rs index 6462a3bcb..fb88292ee 100644 --- a/src/mimefactory.rs +++ b/src/mimefactory.rs @@ -345,6 +345,8 @@ impl<'a> MimeFactory<'a> { .unwrap() .to_rfc2822(); + unprotected_headers.push(Header::new("Date".into(), date)); + let os_name = &self.context.os_name; let os_part = os_name .as_ref() @@ -420,7 +422,6 @@ impl<'a> MimeFactory<'a> { let encrypted = encrypt_helper.encrypt( self.context, - e2ee_guranteed, min_verified, do_gossip, message, @@ -433,7 +434,7 @@ impl<'a> MimeFactory<'a> { PartBuilder::new() .content_type(&"application/pgp-encrypted".parse::().unwrap()) .header(("Content-Description", "PGP/MIME version identification")) - .body("Version: 1") + .body("Version: 1\r\n") .build(), ) .child( @@ -451,7 +452,8 @@ impl<'a> MimeFactory<'a> { ) .header(("Subject".to_string(), "...".to_string())); - self.finalize_mime_message(true, do_gossip && !peerstates.is_empty())?; + let gossiped = do_gossip && !peerstates.is_empty(); + self.finalize_mime_message(true, gossiped)?; outer_message } else { diff --git a/src/mimeparser.rs b/src/mimeparser.rs index 2b1d8359e..ca668810a 100644 --- a/src/mimeparser.rs +++ b/src/mimeparser.rs @@ -478,7 +478,7 @@ impl<'a> MimeParser<'a> { /* eg. `report-type=delivery-status`; maybe we should show them as a little error icon */ if let Some(first) = mail.subparts.iter().next() { - any_part_added = self.parse_mime_recursive(mail)?; + any_part_added = self.parse_mime_recursive(first)?; } } }