smtp_codec/parse/
trace.rs

1/// 4.4.  Trace Information (RFC 5321)
2use abnf_core::streaming::CRLF;
3use nom::{
4    branch::alt,
5    bytes::streaming::{tag, tag_no_case},
6    combinator::{map_res, opt, recognize},
7    multi::many1,
8    sequence::tuple,
9    IResult,
10};
11
12use crate::parse::{
13    address::address_literal,
14    command::{Mailbox, Path, Reverse_path},
15    imf::{
16        datetime::date_time,
17        folding_ws_and_comment::{CFWS, FWS},
18        identification::msg_id,
19    },
20    Atom, Domain, String,
21};
22
23/// Return-path-line = "Return-Path:" FWS Reverse-path <CRLF>
24pub fn Return_path_line(input: &[u8]) -> IResult<&[u8], &[u8]> {
25    let parser = tuple((tag_no_case(b"Return-Path:"), FWS, Reverse_path, CRLF));
26
27    let (remaining, parsed) = recognize(parser)(input)?;
28
29    Ok((remaining, parsed))
30}
31
32/// Time-stamp-line = "Received:" FWS Stamp <CRLF>
33pub fn Time_stamp_line(input: &[u8]) -> IResult<&[u8], &[u8]> {
34    let parser = tuple((tag_no_case(b"Received:"), FWS, Stamp, CRLF));
35
36    let (remaining, parsed) = recognize(parser)(input)?;
37
38    Ok((remaining, parsed))
39}
40
41/// Stamp = From-domain By-domain Opt-info [CFWS] ";" FWS date-time
42///
43/// Caution: Where "date-time" is as defined in RFC 5322 [4]
44///          but the "obs-" forms, especially two-digit
45///          years, are prohibited in SMTP and MUST NOT be used.
46pub fn Stamp(input: &[u8]) -> IResult<&[u8], &[u8]> {
47    let parser = tuple((
48        From_domain,
49        By_domain,
50        Opt_info,
51        opt(CFWS),
52        tag(b";"),
53        FWS,
54        date_time,
55    ));
56
57    let (remaining, parsed) = recognize(parser)(input)?;
58
59    Ok((remaining, parsed))
60}
61
62/// From-domain = "FROM" FWS Extended-Domain
63pub fn From_domain(input: &[u8]) -> IResult<&[u8], &[u8]> {
64    let parser = tuple((tag_no_case(b"FROM"), FWS, Extended_Domain));
65
66    let (remaining, parsed) = recognize(parser)(input)?;
67
68    Ok((remaining, parsed))
69}
70
71/// By-domain = CFWS "BY" FWS Extended-Domain
72pub fn By_domain(input: &[u8]) -> IResult<&[u8], &[u8]> {
73    let parser = tuple((CFWS, tag_no_case(b"BY"), FWS, Extended_Domain));
74
75    let (remaining, parsed) = recognize(parser)(input)?;
76
77    Ok((remaining, parsed))
78}
79
80/// Extended-Domain = Domain /
81///                   ( Domain FWS "(" TCP-info ")" ) /
82///                   ( address-literal FWS "(" TCP-info ")" )
83pub fn Extended_Domain(input: &[u8]) -> IResult<&[u8], &[u8]> {
84    let parser = alt((
85        recognize(Domain),
86        recognize(tuple((Domain, FWS, tag(b"("), TCP_info, tag(b")")))),
87        recognize(tuple((
88            address_literal,
89            FWS,
90            tag(b"("),
91            TCP_info,
92            tag(b")"),
93        ))),
94    ));
95
96    let (remaining, parsed) = recognize(parser)(input)?;
97
98    Ok((remaining, parsed))
99}
100
101/// Information derived by server from TCP connection not client EHLO.
102///
103/// TCP-info = address-literal / ( Domain FWS address-literal )
104pub fn TCP_info(input: &[u8]) -> IResult<&[u8], &[u8]> {
105    let parser = alt((
106        recognize(address_literal),
107        recognize(tuple((Domain, FWS, address_literal))),
108    ));
109
110    let (remaining, parsed) = recognize(parser)(input)?;
111
112    Ok((remaining, parsed))
113}
114
115/// Opt-info = [Via] [With] [ID] [For] [Additional-Registered-Clauses]
116pub fn Opt_info(input: &[u8]) -> IResult<&[u8], &[u8]> {
117    let parser = tuple((
118        opt(Via),
119        opt(With),
120        opt(ID),
121        opt(For),
122        opt(Additional_Registered_Clauses),
123    ));
124
125    let (remaining, parsed) = recognize(parser)(input)?;
126
127    Ok((remaining, parsed))
128}
129
130/// Via = CFWS "VIA" FWS Link
131pub fn Via(input: &[u8]) -> IResult<&[u8], &[u8]> {
132    let parser = tuple((CFWS, tag_no_case(b"VIA"), FWS, Link));
133
134    let (remaining, parsed) = recognize(parser)(input)?;
135
136    Ok((remaining, parsed))
137}
138
139/// With = CFWS "WITH" FWS Protocol
140pub fn With(input: &[u8]) -> IResult<&[u8], &[u8]> {
141    let parser = tuple((CFWS, tag_no_case(b"WITH"), FWS, Protocol));
142
143    let (remaining, parsed) = recognize(parser)(input)?;
144
145    Ok((remaining, parsed))
146}
147
148/// ID = CFWS "ID" FWS ( Atom / msg-id )
149///       ; msg-id is defined in RFC 5322 [4]
150pub fn ID(input: &[u8]) -> IResult<&[u8], &[u8]> {
151    let parser = tuple((
152        CFWS,
153        tag_no_case(b"ID"),
154        FWS,
155        recognize(alt((recognize(Atom), msg_id))),
156    ));
157
158    let (remaining, parsed) = recognize(parser)(input)?;
159
160    Ok((remaining, parsed))
161}
162
163/// For = CFWS "FOR" FWS ( Path / Mailbox )
164pub fn For(input: &[u8]) -> IResult<&[u8], &[u8]> {
165    let parser = tuple((
166        CFWS,
167        tag_no_case(b"FOR"),
168        FWS,
169        alt((recognize(Path), Mailbox)),
170    ));
171
172    let (remaining, parsed) = recognize(parser)(input)?;
173
174    Ok((remaining, parsed))
175}
176
177/// Additional standard clauses may be added in this location by future standards and registration with
178/// IANA.  SMTP servers SHOULD NOT use unregistered names.  See Section 8.
179///
180/// Additional-Registered-Clauses = CFWS Atom FWS String
181pub fn Additional_Registered_Clauses(input: &[u8]) -> IResult<&[u8], &[u8]> {
182    let parser = many1(tuple((CFWS, Atom, FWS, String)));
183
184    let (remaining, parsed) = recognize(parser)(input)?;
185
186    Ok((remaining, parsed))
187}
188
189/// Link = "TCP" / Addtl-Link
190pub fn Link(input: &[u8]) -> IResult<&[u8], &str> {
191    alt((map_res(tag_no_case("TCP"), std::str::from_utf8), Addtl_Link))(input)
192}
193
194/// Additional standard names for links are registered with the Internet Assigned Numbers
195/// Authority (IANA).  "Via" is primarily of value with non-Internet transports.  SMTP servers
196/// SHOULD NOT use unregistered names.
197///
198/// Addtl-Link = Atom
199pub fn Addtl_Link(input: &[u8]) -> IResult<&[u8], &str> {
200    Atom(input)
201}
202
203/// Protocol = "ESMTP" / "SMTP" / Attdl-Protocol
204pub fn Protocol(input: &[u8]) -> IResult<&[u8], &str> {
205    alt((
206        map_res(tag_no_case(b"ESMTP"), std::str::from_utf8),
207        map_res(tag_no_case(b"SMTP"), std::str::from_utf8),
208        Attdl_Protocol,
209    ))(input)
210}
211
212/// Additional standard names for protocols are registered with the Internet Assigned Numbers
213/// Authority (IANA) in the "mail parameters" registry [9].  SMTP servers SHOULD NOT
214/// use unregistered names.
215///
216/// Attdl-Protocol = Atom
217pub fn Attdl_Protocol(input: &[u8]) -> IResult<&[u8], &str> {
218    Atom(input)
219}