Commit
+84 -12 +/-2 browse
1 | diff --git a/maitred/src/server.rs b/maitred/src/server.rs |
2 | index 2f98398..282dc8e 100644 |
3 | --- a/maitred/src/server.rs |
4 | +++ b/maitred/src/server.rs |
5 | @@ -21,8 +21,8 @@ use tokio_util::codec::Framed; |
6 | |
7 | use crate::error::Error; |
8 | use crate::session::Session; |
9 | - use crate::transport::Command; |
10 | - use crate::transport::Transport; |
11 | + use crate::smtp_response; |
12 | + use crate::transport::{Command, Transport}; |
13 | use crate::worker::{Packet, Worker}; |
14 | use crate::{Delivery, Milter, Response, SmtpResponse}; |
15 | |
16 | @@ -190,13 +190,46 @@ where |
17 | } |
18 | } |
19 | Ok(Some(Err(err))) => { |
20 | - tracing::error!("Internal server error: {}", err); |
21 | + tracing::warn!("Client Error: {}", err); |
22 | let response = match err { |
23 | crate::transport::Error::PipelineNotEnabled => { |
24 | crate::smtp_response!(500, 0, 0, 0, "Pipelining is not enabled") |
25 | } |
26 | crate::transport::Error::Smtp(e) => { |
27 | - crate::smtp_response!(500, 0, 0, 0, e.to_string()) |
28 | + match e { |
29 | + smtp_proto::Error::NeedsMoreData { bytes_left: _ } => { |
30 | + // TODO |
31 | + smtp_response!(500, 0, 0, 0, e.to_string()) |
32 | + } |
33 | + smtp_proto::Error::UnknownCommand => { |
34 | + smtp_response!(500, 5, 5, 1, "Invalid Command") |
35 | + } |
36 | + smtp_proto::Error::InvalidSenderAddress => { |
37 | + smtp_response!(501, 5, 1, 8, e.to_string()) |
38 | + } |
39 | + smtp_proto::Error::InvalidRecipientAddress => { |
40 | + smtp_response!(501, 5, 1, 3, e.to_string()) |
41 | + } |
42 | + smtp_proto::Error::SyntaxError { syntax: _ } => { |
43 | + smtp_response!(501, 5, 5, 2, e.to_string()) |
44 | + } |
45 | + smtp_proto::Error::InvalidParameter { param: _ } => { |
46 | + // TODO |
47 | + smtp_response!(500, 0, 0, 0, e.to_string()) |
48 | + } |
49 | + smtp_proto::Error::UnsupportedParameter { param: _ } => { |
50 | + // TODO |
51 | + smtp_response!(500, 0, 0, 0, e.to_string()) |
52 | + } |
53 | + smtp_proto::Error::ResponseTooLong => { |
54 | + // TODO |
55 | + smtp_response!(500, 0, 0, 0, e.to_string()) |
56 | + } |
57 | + smtp_proto::Error::InvalidResponse { code: _ } => { |
58 | + // TODO |
59 | + smtp_response!(500, 0, 0, 0, e.to_string()) |
60 | + } |
61 | + } |
62 | } |
63 | // IO Errors considered fatal for the entire session |
64 | crate::transport::Error::Io(e) => return Err(Error::Io(e)), |
65 | @@ -274,7 +307,12 @@ where |
66 | let (socket, _) = listener.accept().await.unwrap(); |
67 | let addr = socket.local_addr()?; |
68 | tracing::info!("Accepted connection on: {:?}", addr); |
69 | - let framed = Framed::new(socket, Transport::default().pipelining(true)); |
70 | + let pipelining = self |
71 | + .options |
72 | + .as_ref() |
73 | + .is_some_and(|opts| opts.capabilities & smtp_proto::EXT_PIPELINING != 0) |
74 | + || self.options.is_none(); |
75 | + let framed = Framed::new(socket, Transport::default().pipelining(pipelining)); |
76 | |
77 | match self.process(framed, global_queue.clone()).await { |
78 | Ok(_) => { |
79 | @@ -291,7 +329,7 @@ where |
80 | #[cfg(test)] |
81 | mod test { |
82 | |
83 | - use crate::{Delivery, Milter}; |
84 | + use crate::{Delivery, Milter, SessionOptions}; |
85 | |
86 | use super::*; |
87 | |
88 | @@ -349,6 +387,40 @@ mod test { |
89 | todo!() |
90 | } |
91 | } |
92 | + #[tokio::test] |
93 | + async fn test_server() { |
94 | + let stream = FakeStream { |
95 | + buffer: vec![ |
96 | + "HELO example.org\r\n".into(), |
97 | + "MAIL FROM: <fuu@bar.com>\r\n".into(), |
98 | + "RCPT TO: <baz@qux.com>\r\n".into(), |
99 | + "DATA\r\n".into(), |
100 | + "Subject: Hello World\r\n.\r\n".into(), |
101 | + "QUIT\r\n".into(), |
102 | + ], |
103 | + ..Default::default() |
104 | + }; |
105 | + let server = Server::default() |
106 | + // turn off all extended capabilities |
107 | + .with_session_opts(SessionOptions::default().capabilities(0)) |
108 | + .with_milter(Milter::new(|_: Message<'static>| { |
109 | + Box::pin(async move { Ok(Message::default().into_owned()) }) |
110 | + })) |
111 | + .with_delivery(Delivery::new(|_: Message<'static>| { |
112 | + Box::pin(async move { Ok(()) }) |
113 | + })); |
114 | + let framed = Framed::new(stream, Transport::default()); |
115 | + let global_queue = Arc::new(Injector::<Packet>::new()); |
116 | + server.process(framed, global_queue.clone()).await.unwrap(); |
117 | + let packet = global_queue.steal().success().unwrap(); |
118 | + assert!(packet |
119 | + .mail_from |
120 | + .as_ref() |
121 | + .is_some_and(|mail_from| mail_from.email() == "fuu@bar.com")); |
122 | + assert!(packet.rcpt_to.as_ref().is_some_and(|rcpts| rcpts |
123 | + .first() |
124 | + .is_some_and(|rcpt_to| rcpt_to.email() == "baz@qux.com"))); |
125 | + } |
126 | |
127 | #[tokio::test] |
128 | async fn test_server_pipelined() { |
129 | diff --git a/maitred/src/session.rs b/maitred/src/session.rs |
130 | index c78c92c..5c1a081 100644 |
131 | --- a/maitred/src/session.rs |
132 | +++ b/maitred/src/session.rs |
133 | @@ -195,9 +195,9 @@ impl Session { |
134 | if self.initialized.is_none() { |
135 | return Err(smtp_response!( |
136 | 500, |
137 | - 0, |
138 | - 0, |
139 | - 0, |
140 | + 5, |
141 | + 5, |
142 | + 1, |
143 | "It's polite to say EHLO first" |
144 | )); |
145 | } |
146 | @@ -558,9 +558,9 @@ mod test { |
147 | payload: None, |
148 | expected: Err(smtp_response!( |
149 | 500, |
150 | - 0, |
151 | - 0, |
152 | - 0, |
153 | + 5, |
154 | + 5, |
155 | + 1, |
156 | String::from("It's polite to say EHLO first") |
157 | )), |
158 | }]; |