vsmtp-config 2.0.1-rc.4

Next-gen MTA. Secured, Faster and Greener
Documentation
/*
 * vSMTP mail transfer agent
 * Copyright (C) 2022 viridIT SAS
 *
 * This program is free software: you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program. If not, see https://www.gnu.org/licenses/.
 *
*/
use crate::{config::field::FieldServerSMTP, Config};
use vsmtp_common::{auth::Mechanism, CodeID, Reply, ReplyCode};

fn mech_list_to_code(list: &[Mechanism]) -> String {
    format!(
        "AUTH {}\r\n",
        list.iter()
            .map(ToString::to_string)
            .collect::<Vec<_>>()
            .join(" ")
    )
}

impl Config {
    pub(crate) fn ensure(mut config: Self) -> anyhow::Result<Self> {
        /*
        anyhow::ensure!(
            config.app.logs.filename != config.server.logs.filename,
            "System and Application logs cannot both be written in '{}' !",
            config.app.logs.filename.display()
        );
        */

        anyhow::ensure!(
            config.server.system.thread_pool.processing != 0
                && config.server.system.thread_pool.receiver != 0
                && config.server.system.thread_pool.delivery != 0,
            "Worker threads cannot be set to 0"
        );

        {
            let auth_mechanism_list: Option<(Vec<Mechanism>, Vec<Mechanism>)> = config
                .server
                .smtp
                .auth
                .as_ref()
                .map(|auth| auth.mechanisms.iter().partition(|m| m.must_be_under_tls()));

            config.server.smtp.codes.insert(
                CodeID::EhloPain,
                Reply::new(
                    ReplyCode::Code { code: 250 },
                    [
                        &config.server.name,
                        "\r\n",
                        &auth_mechanism_list
                            .as_ref()
                            .map(|(plain, secured)| {
                                if config
                                    .server
                                    .smtp
                                    .auth
                                    .as_ref()
                                    .map_or(false, |auth| auth.enable_dangerous_mechanism_in_clair)
                                {
                                    mech_list_to_code(&[secured.clone(), plain.clone()].concat())
                                } else {
                                    mech_list_to_code(secured)
                                }
                            })
                            .unwrap_or_default(),
                        "STARTTLS\r\n",
                        "8BITMIME\r\n",
                        "SMTPUTF8\r\n",
                    ]
                    .concat(),
                ),
            );

            config.server.smtp.codes.insert(
                CodeID::EhloSecured,
                Reply::new(
                    ReplyCode::Code { code: 250 },
                    [
                        &config.server.name,
                        "\r\n",
                        &auth_mechanism_list
                            .as_ref()
                            .map(|(must_be_secured, _)| mech_list_to_code(must_be_secured))
                            .unwrap_or_default(),
                        "8BITMIME\r\n",
                        "SMTPUTF8\r\n",
                    ]
                    .concat(),
                ),
            );
        }

        let default_values = FieldServerSMTP::default_smtp_codes();
        let reply_codes = &mut config.server.smtp.codes;

        for key in <CodeID as strum::IntoEnumIterator>::iter() {
            reply_codes
                .entry(key)
                .or_insert_with_key(|key| default_values.get(key).expect("missing code").clone());

            reply_codes.entry(key).and_modify(|reply| {
                reply.set(reply.text().replace("{name}", &config.server.name));
            });
        }

        Ok(config)
    }
}