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
|
use crate::imap::capability::{ClientCapability, ServerCapability};
use crate::imap::command::{anonymous, authenticated, examined, selected};
use crate::imap::flow;
use crate::imap::response::Response;
use crate::login::ArcLoginProvider;
use imap_codec::imap_types::command::Command;
//-----
pub struct Instance {
pub login_provider: ArcLoginProvider,
pub server_capabilities: ServerCapability,
pub client_capabilities: ClientCapability,
pub state: flow::State,
}
impl Instance {
pub fn new(login_provider: ArcLoginProvider, cap: ServerCapability) -> Self {
let client_cap = ClientCapability::new(&cap);
Self {
login_provider,
state: flow::State::NotAuthenticated,
server_capabilities: cap,
client_capabilities: client_cap,
}
}
pub async fn command(&mut self, cmd: Command<'static>) -> Response<'static> {
// Command behavior is modulated by the state.
// To prevent state error, we handle the same command in separate code paths.
let (resp, tr) = match &mut self.state {
flow::State::NotAuthenticated => {
let ctx = anonymous::AnonymousContext {
req: &cmd,
login_provider: &self.login_provider,
server_capabilities: &self.server_capabilities,
};
anonymous::dispatch(ctx).await
}
flow::State::Authenticated(ref user) => {
let ctx = authenticated::AuthenticatedContext {
req: &cmd,
server_capabilities: &self.server_capabilities,
client_capabilities: &mut self.client_capabilities,
user,
};
authenticated::dispatch(ctx).await
}
flow::State::Selected(ref user, ref mut mailbox) => {
let ctx = selected::SelectedContext {
req: &cmd,
server_capabilities: &self.server_capabilities,
client_capabilities: &mut self.client_capabilities,
user,
mailbox,
};
selected::dispatch(ctx).await
}
flow::State::Examined(ref user, ref mut mailbox) => {
let ctx = examined::ExaminedContext {
req: &cmd,
server_capabilities: &self.server_capabilities,
client_capabilities: &mut self.client_capabilities,
user,
mailbox,
};
examined::dispatch(ctx).await
}
flow::State::Logout => Response::build()
.tag(cmd.tag.clone())
.message("No commands are allowed in the LOGOUT state.")
.bad()
.map(|r| (r, flow::Transition::None)),
}
.unwrap_or_else(|err| {
tracing::error!("Command error {:?} occured while processing {:?}", err, cmd);
(
Response::build()
.to_req(&cmd)
.message("Internal error while processing command")
.bad()
.unwrap(),
flow::Transition::None,
)
});
if let Err(e) = self.state.apply(tr) {
tracing::error!(
"Transition error {:?} occured while processing on command {:?}",
e,
cmd
);
return Response::build()
.to_req(&cmd)
.message(
"Internal error, processing command triggered an illegal IMAP state transition",
)
.bad()
.unwrap();
}
resp
}
}
|