1use crate::{
4 common::*,
5 io::tls::MayBeTls,
6 mail::*,
7 smtp::{SessionService, SmtpContext, SmtpSession},
8};
9use std::fmt;
10
11#[derive(Clone, Debug, Default)]
15pub struct SessionLogger;
16
17pub use SessionLogger as DebugService;
18
19impl<T> MailSetup<T> for SessionLogger
20where
21 T: AcceptsSessionService + AcceptsGuard + AcceptsDispatch,
22{
23 fn setup(self, config: &mut T) {
24 config.add_last_session_service(self.clone());
25 config.add_last_guard(self.clone());
26 config.add_last_dispatch(self);
27 }
28}
29impl SessionService for SessionLogger {
30 fn prepare_session<'a, 'i, 's, 'f>(
31 &'a self,
32 _io: &'i mut Box<dyn MayBeTls>,
33 state: &'s mut SmtpContext,
34 ) -> S1Fut<'f, ()>
35 where
36 'a: 'f,
37 'i: 'f,
38 's: 'f,
39 {
40 info!(
41 "{}: Preparing {}",
42 state.session.service_name, state.session.connection
43 );
44 Box::pin(ready(()))
45 }
46}
47
48impl MailGuard for SessionLogger {
49 fn add_recipient<'a, 's, 'f>(
50 &'a self,
51 session: &'s mut SmtpSession,
52 rcpt: Recipient,
53 ) -> S2Fut<'f, AddRecipientResult>
54 where
55 'a: 'f,
56 's: 'f,
57 {
58 info!(
59 "{}: RCPT {} from {:?} (mailid: {:?}).",
60 session.service_name, rcpt.address, session.transaction.mail, session.transaction.id
61 );
62 Box::pin(ready(AddRecipientResult::Inconclusive(rcpt)))
63 }
64 fn start_mail<'a, 's, 'f>(&'a self, session: &'s mut SmtpSession) -> S2Fut<'f, StartMailResult>
65 where
66 'a: 'f,
67 's: 'f,
68 {
69 info!(
70 "{}: MAIL from {:?} (mailid: {:?}). {}",
71 session.service_name, session.transaction.mail, session.transaction.id, session
72 );
73 Box::pin(ready(StartMailResult::Accepted))
74 }
75}
76
77impl MailDispatch for SessionLogger {
78 fn open_mail_body<'a, 's, 'f>(
79 &'a self,
80 session: &'s mut SmtpSession,
81 ) -> S1Fut<'f, DispatchResult>
82 where
83 'a: 'f,
84 's: 'f,
85 {
86 let Transaction {
87 ref mail,
88 ref id,
89 ref rcpts,
90 ..
91 } = session.transaction;
92 info!(
93 "{}: Mail from {:?} for {} (mailid: {:?}). {}",
94 session.service_name,
95 mail.as_ref()
96 .map(|m| m.sender().to_string())
97 .unwrap_or_else(|| "nobody".to_owned()),
98 rcpts.iter().fold(String::new(), |s, r| s + format!(
99 "{:?}, ",
100 r.address.to_string()
101 )
102 .as_ref()),
103 id,
104 session
105 );
106 session.transaction.sink = session.transaction.sink.take().map(|inner| {
107 Box::pin(DebugSink {
108 id: format!("{}: {}", session.service_name, id.clone()),
109 inner,
110 }) as Pin<Box<dyn MailDataSink>>
111 });
112 Box::pin(ready(Ok(())))
113 }
114}
115
116struct DebugSink {
117 id: String,
118 inner: Pin<Box<dyn MailDataSink>>,
119}
120
121impl io::Write for DebugSink {
122 fn poll_flush(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
123 self.inner.as_mut().poll_flush(cx)
124 }
125 fn poll_close(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
126 match self.inner.as_mut().poll_flush(cx) {
127 Poll::Ready(Ok(())) => {
128 info!("{}: Mail complete", self.id);
129 Poll::Ready(Ok(()))
130 }
131 Poll::Ready(Err(e)) => {
132 info!("{}: Mail failed: {:?}", self.id, e);
133 Poll::Ready(Ok(()))
134 }
135 Poll::Pending => Poll::Pending,
136 }
137 }
138 fn poll_write(
139 mut self: Pin<&mut Self>,
140 cx: &mut Context<'_>,
141 buf: &[u8],
142 ) -> Poll<std::io::Result<usize>> {
143 match self.inner.as_mut().poll_write(cx, buf) {
144 Poll::Ready(Ok(len)) => {
145 debug!(
146 "{}: Mail data written: len {} {:?}",
147 self.id,
148 len,
149 String::from_utf8_lossy(&buf[..len])
150 );
151 Poll::Ready(Ok(len))
152 }
153 Poll::Ready(Err(e)) => {
154 info!("{}: Mail data failed: {:?}", self.id, e);
155 Poll::Ready(Err(e))
156 }
157 Poll::Pending => Poll::Pending,
158 }
159 }
160}
161
162impl fmt::Debug for DebugSink {
163 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
164 f.debug_struct("DebugSink")
165 .field("id", &self.id)
166 .field("inner", &"*")
167 .finish()
168 }
169}
170
171#[cfg(test)]
172mod tests {
173 use super::*;
174
175 #[test]
176 fn test_setup() {
177 async_std::task::block_on(async move {
178 let mut sess = SmtpSession::default();
179 let sut = SessionLogger;
180 let tran = sut.start_mail(&mut sess).await;
181 assert_eq!(tran, StartMailResult::Accepted)
182 })
183 }
184}