summaryrefslogtreecommitdiffstats
path: root/openpgp/src/message/lexer.rs
blob: 442399d4f6e4b0cbe2b0d14de6b665b2a0db8b7e (plain)
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
use Error;
use Result;

use Packet;
use PacketPile;

// The type of the parser's input.
//
// The parser iterators over tuples consisting of the token's starting
// position, the token itself, and the token's ending position.
pub(crate) type LexerItem<Tok, Loc, Error>
    = ::std::result::Result<(Loc, Tok, Loc), Error>;

#[derive(Debug, Clone)]
pub enum Token {
    Literal,
    CompressedData,

    SKESK,
    PKESK,
    SEIP,

    OPS,
    SIG,

    Pop,

    // This represents the content of a container that is not parsed.
    OpaqueContent,
}

#[derive(Debug)]
pub enum LexicalError {
    // There are no lexing errors.
}

pub(crate) enum Lexer<'input> {
    Refed(Box<Iterator<Item=(usize, &'input Token)> + 'input>),
    Owned(Box<Iterator<Item=(usize, Token)> + 'input>),
}

impl<'input> Iterator for Lexer<'input> {
    type Item = LexerItem<Token, usize, LexicalError>;

    fn next(&mut self) -> Option<Self::Item> {
        let n = match self {
            Lexer::Refed(ref mut i) =>
                i.next().map(|(pos, tok)| (pos, tok.clone())),
            Lexer::Owned(ref mut i) => i.next(),
        };

        if let Some((pos, tok)) = n {
            Some(Ok((pos, tok, pos)))
        } else {
            None
        }
    }
}

impl<'input> Lexer<'input> {
    /// Uses a raw sequence of tokens as input to the parser.
    // This is only used in the test code.  It would be better to use
    // cfg(test), but then we have to do the same for the Lexer enum
    // above and then we also have to specialize Lexer::next().  This
    // is significantly less ugly.
    #[allow(unused)]
    pub(crate) fn from_tokens(raw: &'input [Token]) -> Self {
        let iter = raw.iter().enumerate();
        Lexer::Refed(Box::new(iter))
    }

    /// Uses a `PacketPile` as input to the parser.
    pub(crate) fn from_packet_pile(pp: &'input PacketPile) -> Result<Self> {
        let mut t = vec![];
        let mut last_path = vec![0];

        for (path, p) in pp.descendants().paths() {
            if last_path.len() > path.len() {
                // We popped one or more containers.
                for _ in 1..last_path.len() - path.len() + 1 {
                    t.push(Token::Pop);
                }
            }
            last_path = path;

            match p {
                Packet::Literal(_) => t.push(Token::Literal),
                Packet::CompressedData(_) => t.push(Token::CompressedData),
                Packet::SKESK(_) => t.push(Token::SKESK),
                Packet::PKESK(_) => t.push(Token::PKESK),
                Packet::SEIP(_) => t.push(Token::SEIP),
                Packet::OnePassSig(_) => t.push(Token::OPS),
                Packet::Signature(_) => t.push(Token::SIG),

                p =>
                    return Err(Error::MalformedMessage(
                        format!("Invalid OpenPGP message: \
                                 unexpected packet: {:?}",
                                p.tag()).into()).into()),
            }

            match p {
                Packet::CompressedData(_) | Packet::SEIP(_) => {
                    // If a container's content is not unpacked, then
                    // we treat the content as an opaque message.

                    if p.children.is_none() && p.body.is_some() {
                        t.push(Token::OpaqueContent);
                        t.push(Token::Pop);
                    }
                }
                _ => {}
            }
        }

        if last_path.len() > 1 {
            // We popped one or more containers.
            for _ in 1..last_path.len() {
                t.push(Token::Pop);
            }
        }

        Ok(Lexer::Owned(Box::new(t.into_iter().enumerate())))
    }
}