Repository: lettre/lettre (language: rust)

Issue:
Mailbox displays wrongly when containing a comma and a non-ascii char in its name
**Describe the bug**

When the name part of a `Mailbox` contains a comma `,` and a non-ascii char, the `.to_string()` result is not valid.

**To Reproduce**
```rust
// This works
assert_eq!(
    "Şmith <smith@localhost>",
    Mailbox::new(Some("Şmith".into()), "smith@localhost".parse().unwrap()).to_string()
);

// This works
assert_eq!(
    "\"Smith, John\" <smith@localhost>",
    Mailbox::new(Some("Smith, John".into()), "smith@localhost".parse().unwrap()).to_string()
);

// This fails
assert_eq!(
    "\"Şmith, John\" <smith@localhost>",
    Mailbox::new(Some("Şmith, John".into()), "smith@localhost".parse().unwrap()).to_string()
);

// thread 'xxx' panicked at 'assertion failed: `(left == right)`
//  left: `"\"Şmith, John\" <smith@localhost>"`,
//  right: `"\"Å\u{9e}mith, John\" <smith@localhost>"`
```

**Expected behavior**

The last assertion should pass.

**Environment (please complete the following information):**
- Lettre version: `v0.10.1`
- OS: `Linux 5.10.111 #1-NixOS SMP Wed Apr 13 19:01:11 UTC 2022`




Discussion / hints:
*I think* the problem comes from `write_quoted_string_char`: one byte is associated to one character, but in reality one char can be composed of many bytes (unicodes), therefore they should not be printed separately. I will propose a PR soon!

Fix patch:
diff --git a/src/message/mailbox/types.rs b/src/message/mailbox/types.rs
index c6ca090a4..0c0a28ec8 100644
--- a/src/message/mailbox/types.rs
+++ b/src/message/mailbox/types.rs
@@ -397,7 +397,7 @@ fn write_word(f: &mut Formatter<'_>, s: &str) -> FmtResult {
     } else {
         // Quoted string: https://datatracker.ietf.org/doc/html/rfc2822#section-3.2.5
         f.write_char('"')?;
-        for &c in s.as_bytes() {
+        for c in s.chars() {
             write_quoted_string_char(f, c)?;
         }
         f.write_char('"')?;
@@ -441,34 +441,37 @@ fn is_valid_atom_char(c: u8) -> bool {
 }
 
 // https://datatracker.ietf.org/doc/html/rfc2822#section-3.2.5
-fn write_quoted_string_char(f: &mut Formatter<'_>, c: u8) -> FmtResult {
+fn write_quoted_string_char(f: &mut Formatter<'_>, c: char) -> FmtResult {
     match c {
-		// NO-WS-CTL: https://datatracker.ietf.org/doc/html/rfc2822#section-3.2.1
-		1..=8 | 11 | 12 | 14..=31 | 127 |
-
-		// Note, not qcontent but can be put before or after any qcontent.
-		b'\t' |
-		b' ' |
-
-		// The rest of the US-ASCII except \ and "
-		33 |
-		35..=91 |
-		93..=126 |
-
-		// Non-ascii characters will be escaped separately later.
-		128..=255
-
-		=> f.write_char(c.into()),
-
-		// Can not be encoded.
-		b'\n' | b'\r' => Err(std::fmt::Error),
+        // Can not be encoded.
+        '\n' | '\r' => Err(std::fmt::Error),
+
+        // Note, not qcontent but can be put before or after any qcontent.
+        '\t' | ' ' => f.write_char(c),
+
+        c if match c as u32 {
+            // NO-WS-CTL: https://datatracker.ietf.org/doc/html/rfc2822#section-3.2.1
+            1..=8 | 11 | 12 | 14..=31 | 127 |
+
+            // The rest of the US-ASCII except \ and "
+            33 |
+            35..=91 |
+            93..=126 |
+
+            // Non-ascii characters will be escaped separately later.
+            128.. => true,
+            _ => false,
+        } =>
+        {
+            f.write_char(c)
+        }
 
-		c => {
-			// quoted-pair https://datatracker.ietf.org/doc/html/rfc2822#section-3.2.2
-			f.write_char('\\')?;
-			f.write_char(c.into())
-		}
-	}
+        _ => {
+            // quoted-pair https://datatracker.ietf.org/doc/html/rfc2822#section-3.2.2
+            f.write_char('\\')?;
+            f.write_char(c)
+        }
+    }
 }
 
 #[cfg(test)]
@@ -515,6 +518,34 @@ mod test {
         );
     }
 
+    #[test]
+    fn mailbox_format_address_with_comma_and_non_ascii() {
+        assert_eq!(
+            format!(
+                "{}",
+                Mailbox::new(
+                    Some("Laşt, First".into()),
+                    "kayo@example.com".parse().unwrap()
+                )
+            ),
+            r#""Laşt, First" <kayo@example.com>"#
+        );
+    }
+
+    #[test]
+    fn mailbox_format_address_with_comma_and_quoted_non_ascii() {
+        assert_eq!(
+            format!(
+                "{}",
+                Mailbox::new(
+                    Some(r#"Laşt, "First""#.into()),
+                    "kayo@example.com".parse().unwrap()
+                )
+            ),
+            r#""Laşt, \"First\"" <kayo@example.com>"#
+        );
+    }
+
     #[test]
     fn mailbox_format_address_with_color() {
         assert_eq!(


Test patch: