Skip to main content

imsg_formats/bmessage/
encode.rs

1//! bMessage encoder: produces CRLF-terminated wire format.
2
3use super::types::{BMessage, BVCard, MessageType};
4
5impl BMessage {
6    /// CRLF line endings throughout. LENGTH = byte count of the CRLF `BEGIN:MSG…END:MSG` block;
7    /// iOS rejects LF-only lengths.
8    #[must_use]
9    pub fn encode(&self) -> String {
10        let mut out = String::with_capacity(256);
11        line(&mut out, "BEGIN:BMSG");
12        line(&mut out, "VERSION:1.0");
13        line(&mut out, &format!("STATUS:{}", self.status().as_str()));
14        line(&mut out, &format!("TYPE:{}", MessageType::as_str()));
15        line(&mut out, &format!("FOLDER:{}", self.folder()));
16        if let Some(orig) = self.originator() {
17            encode_vcard(&mut out, orig);
18        }
19        let env = self.envelope();
20        line(&mut out, "BEGIN:BENV");
21        for recip in &env.recipients {
22            encode_vcard(&mut out, recip);
23        }
24        encode_bbody(
25            &mut out,
26            &env.body.encoding,
27            &env.body.charset,
28            &env.body.language,
29            &env.body.text,
30        );
31        line(&mut out, "END:BENV");
32        line(&mut out, "END:BMSG");
33        out
34    }
35}
36
37fn line(out: &mut String, s: &str) {
38    out.push_str(s);
39    out.push_str("\r\n");
40}
41
42fn encode_vcard(out: &mut String, vc: &BVCard) {
43    line(out, "BEGIN:VCARD");
44    line(out, "VERSION:3.0");
45    line(out, &format!("N:{}", vc.name));
46    line(out, &format!("TEL:{}", vc.tel));
47    line(out, "END:VCARD");
48}
49
50fn encode_bbody(out: &mut String, encoding: &str, charset: &str, language: &str, text: &str) {
51    line(out, "BEGIN:BBODY");
52    line(out, &format!("ENCODING:{encoding}"));
53    line(out, &format!("CHARSET:{charset}"));
54    line(out, &format!("LANGUAGE:{language}"));
55    line(out, &format!("LENGTH:{}", msg_block_length(text)));
56    line(out, "BEGIN:MSG");
57    for l in text.lines() {
58        line(out, l);
59    }
60    line(out, "END:MSG");
61    line(out, "END:BBODY");
62}
63
64// iOS computes LENGTH from the CRLF block; LF-only lengths cause send failures.
65fn msg_block_length(text: &str) -> usize {
66    let mut n: usize = 11;
67    for l in text.lines() {
68        n = n.saturating_add(l.len()).saturating_add(2);
69    }
70    n.saturating_add(9)
71}