Repository: deltachat/deltachat-core-rust (language: python)

Issue:
Do not use format=flowed in outgoing messages
format=flowed was introduced in core 1.46.0 to solve the problem of too long lines in plain text triggering spam filters (https://github.com/deltachat/deltachat-core-rust/issues/1750, https://github.com/deltachat/deltachat-core-rust/issues/2700) and long lines in user-sent messages being hard wrapped to 1000 characters.

Core 1.133.1 has since enabled quoted-printable for the text part: https://github.com/deltachat/deltachat-core-rust/pull/4344
It wraps the message to 76 lines already, so text parts don't exceed any line length limits even without format=flowed.

The only use of format=flowed is making the messages display nicely on large screens, but when displayed in a client that has a small screen (typical phone) and does not support format=flowed (Outlook and most web clients), it makes display worse.

We should keep format=flowed support for received messages and make an option to enable sending it (at least for tests), but disable it by default and not expose in the UI.

Related forum thread:
https://support.delta.chat/t/disable-format-flowed-by-default/3322


Discussion / hints:
might also be possible, there is a bug:

from https://elekk.xyz/@aelius/113480416110559548 :

> [@delta](https://chaos.social/@delta) reading further into quoted printable, it seems possible that delta chat's encoding logic is buggy.
>
> With quoted printable:
> 
> Unbroken lines are supposed=
to be encoded by an ascii=
'equals' character.=
>
> However:
>
> Deltachat is encoding=20
unbroken lines like this=20
>
> Presumably this is meant to indicate format=flowed, however f=f doesn't work when paired with qp.
Does f=f accomplish anything that qp = doesn't? It might be that ditching f=f is the solution here...


This is not a bug, `format=flowed` breaks lines by inserting a space at the end and actually breaking a line, then quoted-printable encodes it as `=20`. As there are no long lines after `format=flowed`, quoted-printable does not have to do its own `=` line breaks.

Delta Chat correctly uses encodes and decodes both format=flowed and quoted-printable, the only problem is that other clients do not support receiving format=flowed and display its line breaks.

Fix patch:
diff --git a/src/mimefactory.rs b/src/mimefactory.rs
index fb294b107d..5c3df4b7be 100644
--- a/src/mimefactory.rs
+++ b/src/mimefactory.rs
@@ -6,7 +6,6 @@ use anyhow::{bail, Context as _, Result};
 use base64::Engine as _;
 use chrono::TimeZone;
 use email::Mailbox;
-use format_flowed::{format_flowed, format_flowed_quote};
 use lettre_email::{Address, Header, MimeMultipartType, PartBuilder};
 use tokio::fs;
 
@@ -1300,9 +1299,18 @@ impl MimeFactory {
 
         let final_text = placeholdertext.as_deref().unwrap_or(&msg.text);
 
-        let mut quoted_text = msg
-            .quoted_text()
-            .map(|quote| format_flowed_quote(&quote) + "\r\n\r\n");
+        let mut quoted_text = None;
+        if let Some(msg_quoted_text) = msg.quoted_text() {
+            let mut some_quoted_text = String::new();
+            for quoted_line in msg_quoted_text.split('\n') {
+                some_quoted_text += "> ";
+                some_quoted_text += quoted_line;
+                some_quoted_text += "\r\n";
+            }
+            some_quoted_text += "\r\n";
+            quoted_text = Some(some_quoted_text)
+        }
+
         if !is_encrypted && msg.param.get_bool(Param::ProtectQuote).unwrap_or_default() {
             // Message is not encrypted but quotes encrypted message.
             quoted_text = Some("> ...\r\n\r\n".to_string());
@@ -1312,7 +1320,6 @@ impl MimeFactory {
             // Delta Chat.
             quoted_text = Some("\r\n".to_string());
         }
-        let flowed_text = format_flowed(final_text);
 
         let is_reaction = msg.param.get_int(Param::Reaction).unwrap_or_default() != 0;
 
@@ -1322,7 +1329,7 @@ impl MimeFactory {
             "{}{}{}{}{}{}",
             fwdhint.unwrap_or_default(),
             quoted_text.unwrap_or_default(),
-            escape_message_footer_marks(&flowed_text),
+            escape_message_footer_marks(final_text),
             if !final_text.is_empty() && !footer.is_empty() {
                 "\r\n\r\n"
             } else {
@@ -1332,10 +1339,8 @@ impl MimeFactory {
             footer
         );
 
-        let mut main_part = PartBuilder::new().header((
-            "Content-Type",
-            "text/plain; charset=utf-8; format=flowed; delsp=no",
-        ));
+        let mut main_part =
+            PartBuilder::new().header(("Content-Type", "text/plain; charset=utf-8"));
         main_part = self.add_message_text(main_part, message_text);
 
         if is_reaction {


Test patch:
diff --git a/src/receive_imf/tests.rs b/src/receive_imf/tests.rs
index ee3cd317b0..22788eed3f 100644
--- a/src/receive_imf/tests.rs
+++ b/src/receive_imf/tests.rs
@@ -1068,7 +1068,7 @@ async fn test_classic_mailing_list() -> Result<()> {
     let mime = sent.payload();
 
     println!("Sent mime message is:\n\n{mime}\n\n");
-    assert!(mime.contains("Content-Type: text/plain; charset=utf-8; format=flowed; delsp=no\r\n"));
+    assert!(mime.contains("Content-Type: text/plain; charset=utf-8\r\n"));
     assert!(mime.contains("Subject: =?utf-8?q?Re=3A_=5Bdelta-dev=5D_What=27s_up=3F?=\r\n"));
     assert!(mime.contains("MIME-Version: 1.0\r\n"));
     assert!(mime.contains("In-Reply-To: <38942@posteo.org>\r\n"));