1use imap_codec::{
2 AuthenticateDataCodec, CommandCodec, IdleDoneCodec,
3 fragmentizer::{FragmentInfo, Fragmentizer, LiteralAnnouncement},
4};
5
6#[path = "common/common.rs"]
7mod common;
8
9use common::{COLOR_SERVER, RESET, read_more};
10use imap_types::{
11 IntoStatic,
12 command::{Command, CommandBody},
13 core::{LiteralMode, Tag},
14};
15
16use crate::common::Role;
17
18enum State {
19 Command,
20 Authenticate(Tag<'static>),
21 Idle,
22}
23
24const WELCOME: &str = r#"# Parsing of IMAP commands
25
26"C:" denotes the client,
27"S:" denotes the server, and
28".." denotes the continuation of an (incomplete) command, e.g., due to the use of an IMAP literal.
29
30Note: "\n" will be automatically replaced by "\r\n".
31
32--------------------------------------------------------------------------------------------------
33
34Enter IMAP commands (or "exit").
35"#;
36
37fn main() {
38 println!("{WELCOME}");
39
40 let mut fragmentizer = Fragmentizer::new(10 * 1024);
41 let mut state = State::Command;
42
43 println!("S: {COLOR_SERVER}* OK ...{RESET}");
45
46 loop {
47 let Some(fragment_info) = fragmentizer.progress() else {
49 let bytes = read_more(Role::Client, fragmentizer.message_bytes().is_empty());
51
52 fragmentizer.enqueue_bytes(&bytes);
54
55 continue;
57 };
58
59 if let FragmentInfo::Line {
61 announcement:
62 Some(LiteralAnnouncement {
63 mode: LiteralMode::Sync,
64 length,
65 }),
66 ..
67 } = fragment_info
68 {
69 if length <= 1024 {
71 println!("S: {COLOR_SERVER}+ {RESET}");
73
74 continue;
76 } else if let Some(tag) = fragmentizer.decode_tag() {
77 println!("S: {COLOR_SERVER}{} BAD ...{RESET}", tag.as_ref());
79
80 fragmentizer.skip_message();
82
83 continue;
85 } else {
86 fragmentizer.poison_message();
89
90 continue;
92 }
93 }
94
95 if !fragmentizer.is_message_complete() {
97 continue;
99 }
100
101 match state {
103 State::Command => {
104 match fragmentizer.decode_message(&CommandCodec::default()) {
105 Ok(Command {
106 tag,
107 body: CommandBody::Authenticate { .. },
108 }) => {
109 println!("S: {COLOR_SERVER}+ {RESET}");
111
112 state = State::Authenticate(tag.into_static());
114 }
115 Ok(Command {
116 body: CommandBody::Idle,
117 ..
118 }) => {
119 println!("S: {COLOR_SERVER}+ ...{RESET}");
121
122 state = State::Idle;
124 }
125 Ok(command) => {
126 println!("{command:#?}");
128 }
129 Err(err) => {
130 println!("Error parsing command: {err:?}");
131 }
132 };
133 }
134 State::Authenticate(ref tag) => {
135 match fragmentizer.decode_message(&AuthenticateDataCodec::default()) {
136 Ok(_authenticate_data) => {
137 println!("S: {COLOR_SERVER}{} OK ...{RESET}", tag.as_ref());
139
140 state = State::Command;
142 }
143 Err(err) => {
144 println!("Error parsing authenticate data: {err:?}");
145 }
146 };
147 }
148 State::Idle => {
149 match fragmentizer.decode_message(&IdleDoneCodec::default()) {
150 Ok(_idle_done) => {
151 state = State::Command;
153 }
154 Err(err) => {
155 println!("Error parsing idle done: {err:?}");
156 }
157 };
158 }
159 }
160 }
161}