File: server.rs

package info (click to toggle)
rust-imap-codec 2.0.0~alpha5%2B20250307-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,716 kB
  • sloc: makefile: 2; sh: 1
file content (161 lines) | stat: -rw-r--r-- 5,402 bytes parent folder | download
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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
use imap_codec::{
    fragmentizer::{FragmentInfo, Fragmentizer, LiteralAnnouncement},
    AuthenticateDataCodec, CommandCodec, IdleDoneCodec,
};

#[path = "common/common.rs"]
mod common;

use common::{read_more, COLOR_SERVER, RESET};
use imap_types::{
    command::{Command, CommandBody},
    core::{LiteralMode, Tag},
    IntoStatic,
};

use crate::common::Role;

enum State {
    Command,
    Authenticate(Tag<'static>),
    Idle,
}

const WELCOME: &str = r#"# Parsing of IMAP commands

"C:" denotes the client,
"S:" denotes the server, and
".." denotes the continuation of an (incomplete) command, e.g., due to the use of an IMAP literal.

Note: "\n" will be automatically replaced by "\r\n".

--------------------------------------------------------------------------------------------------

Enter IMAP commands (or "exit").
"#;

fn main() {
    println!("{}", WELCOME);

    let mut fragmentizer = Fragmentizer::new(10 * 1024);
    let mut state = State::Command;

    // Send a greeting.
    println!("S: {COLOR_SERVER}* OK ...{RESET}");

    loop {
        // Progress next fragment.
        let Some(fragment_info) = fragmentizer.progress() else {
            // Read more bytes ...
            let bytes = read_more(Role::Client, fragmentizer.message_bytes().is_empty());

            // ... and pass the bytes to the Fragmentizer ...
            fragmentizer.enqueue_bytes(&bytes);

            // ... and try again.
            continue;
        };

        // The Fragmentizer detected a line that announces a sync literal.
        if let FragmentInfo::Line {
            announcement:
                Some(LiteralAnnouncement {
                    mode: LiteralMode::Sync,
                    length,
                }),
            ..
        } = fragment_info
        {
            // Check the length of the literal.
            if length <= 1024 {
                // Accept the literal ...
                println!("S: {COLOR_SERVER}+ {RESET}");

                // ... and continue with the remaining message.
                continue;
            } else if let Some(tag) = fragmentizer.decode_tag() {
                // Reject the literal ...
                println!("S: {COLOR_SERVER}{} BAD ...{RESET}", tag.as_ref());

                // ... and skip the current message ...
                fragmentizer.skip_message();

                // ... and continue with the next message.
                continue;
            } else {
                // The partially received message is malformed. It's unclear what will follow.
                // To be on the safe side, prevent the message from being decoded ...
                fragmentizer.poison_message();

                // ... but continue parsing the message.
                continue;
            }
        }

        // Check whether the Fragmentizer detected a complete message.
        if !fragmentizer.is_message_complete() {
            // Read next fragment.
            continue;
        }

        // The Fragmentizer detected a complete message.
        match state {
            State::Command => {
                match fragmentizer.decode_message(&CommandCodec::default()) {
                    Ok(Command {
                        tag,
                        body: CommandBody::Authenticate { .. },
                    }) => {
                        // Request another SASL round ...
                        println!("S: {COLOR_SERVER}+ {RESET}");

                        // ... and proceed with authenticate data.
                        state = State::Authenticate(tag.into_static());
                    }
                    Ok(Command {
                        body: CommandBody::Idle,
                        ..
                    }) => {
                        // Accept the idle ...
                        println!("S: {COLOR_SERVER}+ ...{RESET}");

                        // ... and proceed with idle done.
                        state = State::Idle;
                    }
                    Ok(command) => {
                        // Do something with the command.
                        println!("{:#?}", command);
                    }
                    Err(err) => {
                        println!("Error parsing command: {err:?}");
                    }
                };
            }
            State::Authenticate(ref tag) => {
                match fragmentizer.decode_message(&AuthenticateDataCodec::default()) {
                    Ok(_authenticate_data) => {
                        // Accept the authentication after one SASL round.
                        println!("S: {COLOR_SERVER}{} OK ...{RESET}", tag.as_ref());

                        // ... and proceed with commands.
                        state = State::Command;
                    }
                    Err(err) => {
                        println!("Error parsing authenticate data: {err:?}");
                    }
                };
            }
            State::Idle => {
                match fragmentizer.decode_message(&IdleDoneCodec::default()) {
                    Ok(_idle_done) => {
                        // End idle and proceed with commands.
                        state = State::Command;
                    }
                    Err(err) => {
                        println!("Error parsing idle done: {err:?}");
                    }
                };
            }
        }
    }
}