samotop_core/mail/
name.rs

1use super::MailSetup;
2use crate::common::{ready, Identify, S1Fut};
3use crate::io::tls::MayBeTls;
4use crate::mail::{AcceptsSessionService, HasId};
5use crate::smtp::{SessionService, SmtpContext};
6
7/// MailSetup that uses the given service name for a session.
8/// It can also attach the instance ID and session ID for better diagnostics.
9///
10/// Using the default instance or setting name to empty string will reuse the incoming service name already set.
11#[derive(Debug, Default)]
12pub struct Name {
13    name: String,
14    identify_session: bool,
15    identify_instance: bool,
16    instance_identity: String,
17}
18impl Name {
19    /// Construct a name `MailSetup` to use the given service name.
20    /// This name is used in SMTP responses and will be seen in logs.
21    /// It is also used to identify a mail transaction.
22    pub fn new(name: impl ToString) -> Self {
23        Self {
24            name: name.to_string(),
25            identify_session: false,
26            identify_instance: false,
27            instance_identity: String::default(),
28        }
29    }
30    /// Switch if instance ID should be included in the service name
31    pub fn identify_instance(mut self, enable: bool) -> Self {
32        self.identify_instance = enable;
33        self
34    }
35    /// Switch if instance ID should be included in the service name
36    pub fn identify_session(mut self, enable: bool) -> Self {
37        self.identify_session = enable;
38        self
39    }
40}
41impl SessionService for Name {
42    /// Use a given name as a service name in the session.
43    fn prepare_session<'a, 'i, 's, 'f>(
44        &'a self,
45        _io: &'i mut Box<dyn MayBeTls>,
46        state: &'s mut SmtpContext,
47    ) -> S1Fut<'f, ()>
48    where
49        'a: 'f,
50        'i: 'f,
51        's: 'f,
52    {
53        let mut name = if self.name.is_empty() {
54            std::mem::take(&mut state.session.service_name)
55        } else {
56            self.name.clone()
57        };
58
59        if self.identify_instance {
60            let instance_id = if self.instance_identity.is_empty() {
61                Identify::instance().to_string()
62            } else {
63                self.instance_identity.clone()
64            };
65            name = if name.is_empty() {
66                instance_id
67            } else {
68                format!("{}.{}", instance_id, name)
69            }
70        }
71
72        if self.identify_session {
73            let session_id = if state.session.connection.id.is_empty() {
74                Identify::now().to_string()
75            } else {
76                state.session.connection.id.clone()
77            };
78            name = if name.is_empty() {
79                session_id
80            } else {
81                format!("{}.{}", session_id, name)
82            };
83            state.session.connection.id = name.clone();
84        };
85
86        state.session.service_name = name;
87
88        Box::pin(ready(()))
89    }
90}
91impl<T: AcceptsSessionService + HasId> MailSetup<T> for Name {
92    /// Add self as an ESMTP service so it can configure service name for each session
93    fn setup(mut self, config: &mut T) {
94        self.instance_identity = config.id().to_string();
95        config.add_first_session_service(self)
96    }
97}