1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
use std::num::NonZeroU32;

use abnf_core::streaming::SP;
use nom::{
    branch::alt,
    bytes::streaming::{tag, tag_no_case},
    combinator::{map, opt},
    multi::separated_list1,
    sequence::{delimited, terminated, tuple},
    IResult,
};

use crate::{
    parse::{
        body::body,
        core::{nstring, number, nz_number},
        datetime::date_time,
        envelope::envelope,
        flag::flag_fetch,
        section::section,
    },
    types::response::{Data, MessageAttribute},
};

/// message-data = nz-number SP ("EXPUNGE" / ("FETCH" SP msg-att))
pub(crate) fn message_data(input: &[u8]) -> IResult<&[u8], Data> {
    let (remaining, seq_or_uid) = terminated(nz_number, SP)(input)?;

    alt((
        map(tag_no_case(b"EXPUNGE"), move |_| Data::Expunge(seq_or_uid)),
        map(
            tuple((tag_no_case(b"FETCH"), SP, msg_att)),
            move |(_, _, attributes)| Data::Fetch {
                seq_or_uid,
                attributes,
            },
        ),
    ))(remaining)
}

/// msg-att = "("
///           (msg-att-dynamic / msg-att-static) *(SP (msg-att-dynamic / msg-att-static))
///           ")"
fn msg_att(input: &[u8]) -> IResult<&[u8], Vec<MessageAttribute>> {
    delimited(
        tag(b"("),
        separated_list1(SP, alt((msg_att_dynamic, msg_att_static))),
        tag(b")"),
    )(input)
}

/// msg-att-dynamic = "FLAGS" SP "(" [flag-fetch *(SP flag-fetch)] ")"
///
/// Note: MAY change for a message
fn msg_att_dynamic(input: &[u8]) -> IResult<&[u8], MessageAttribute> {
    let mut parser = tuple((
        tag_no_case(b"FLAGS"),
        SP,
        delimited(tag(b"("), opt(separated_list1(SP, flag_fetch)), tag(b")")),
    ));

    let (remaining, (_, _, flags)) = parser(input)?;

    Ok((
        remaining,
        MessageAttribute::Flags(flags.unwrap_or_default()),
    ))
}

/// msg-att-static = "ENVELOPE" SP envelope /
///                  "INTERNALDATE" SP date-time /
///                  "RFC822" [".HEADER" / ".TEXT"] SP nstring /
///                  "RFC822.SIZE" SP number /
///                  "BODY" ["STRUCTURE"] SP body /
///                  "BODY" section ["<" number ">"] SP nstring /
///                  "UID" SP uniqueid
///
/// Note: MUST NOT change for a message
fn msg_att_static(input: &[u8]) -> IResult<&[u8], MessageAttribute> {
    alt((
        map(
            tuple((tag_no_case(b"ENVELOPE"), SP, envelope)),
            |(_, _, envelope)| MessageAttribute::Envelope(envelope),
        ),
        map(
            tuple((tag_no_case(b"INTERNALDATE"), SP, date_time)),
            |(_, _, date_time)| MessageAttribute::InternalDate(date_time),
        ),
        alt((
            map(
                tuple((tag_no_case(b"RFC822.HEADER"), SP, nstring)),
                |(_, _, nstring)| MessageAttribute::Rfc822Header(nstring.to_owned()),
            ),
            map(
                tuple((tag_no_case(b"RFC822.TEXT"), SP, nstring)),
                |(_, _, nstring)| MessageAttribute::Rfc822Text(nstring.to_owned()),
            ),
            map(
                tuple((tag_no_case(b"RFC822"), SP, nstring)),
                |(_, _, nstring)| MessageAttribute::Rfc822(nstring.to_owned()),
            ),
        )),
        map(
            tuple((tag_no_case(b"RFC822.SIZE"), SP, number)),
            |(_, _, num)| MessageAttribute::Rfc822Size(num),
        ),
        alt((
            map(
                tuple((tag_no_case(b"BODYSTRUCTURE"), SP, body(8))),
                |(_, _, body)| MessageAttribute::BodyStructure(body),
            ),
            map(
                tuple((tag_no_case(b"BODY"), SP, body(8))),
                |(_, _, body)| MessageAttribute::Body(body),
            ),
        )),
        map(
            tuple((
                tag_no_case(b"BODY"),
                section,
                opt(delimited(tag(b"<"), number, tag(b">"))),
                SP,
                nstring,
            )),
            |(_, section, origin, _, data)| MessageAttribute::BodyExt {
                section,
                origin,
                data: data.to_owned(),
            },
        ),
        map(tuple((tag_no_case(b"UID"), SP, uniqueid)), |(_, _, uid)| {
            MessageAttribute::Uid(uid)
        }),
    ))(input)
}

#[inline]
/// uniqueid = nz-number
///
/// Note: Strictly ascending
fn uniqueid(input: &[u8]) -> IResult<&[u8], NonZeroU32> {
    nz_number(input)
}