#!/bin/bash

# Function to remove duplicate lines from a file
remove_duplicates() {
    awk '!seen[$0]++' "$1" > "$1.tmp" && mv "$1.tmp" "$1"
}

# Function to ensure correct values in Postfix config
ensure_postfix_value() {
    local key="$1"
    local value="$2"
    local file="$3"

    # Remove any existing lines with the same key
    sed -i "/^$key\s*=/d" "$file"
    # Add the correct key-value pair
    echo "$key = $value" >> "$file"
    # Remove duplicates
    remove_duplicates "$file"
}

# Detect OS
if grep -q 'AlmaLinux' /etc/os-release; then
    OS='alma'
    PACKAGE_MANAGER='dnf'
    MARIADB_REPO='https://downloads.mariadb.com/MariaDB/mariadb_repo_setup'
    ENABLE_REPO='--enablerepo=crb'
elif grep -q 'Ubuntu' /etc/os-release; then
    OS='ubuntu'
    PACKAGE_MANAGER='apt-get'
    MARIADB_REPO='https://downloads.mariadb.com/MariaDB/mariadb_repo_setup'
    ENABLE_REPO=''
else
    echo "Unsupported OS. This script supports AlmaLinux 9 and Ubuntu 22.04 only."
    exit 1
fi

# Get hostname
myhostname=$(cat /etc/hostname)

# Prompt for variables
read -p "Enter database name for SQL: " dbname
read -p "Enter SQL user: " sql_user
read -sp "Enter SQL password: " sql_password
echo
read -sp "Enter root password for SQL: " root_password
echo
read -p "Enter domain for Postfix: " mydomain
read -sp "Enter mail user password: " mail_password
echo

# Update server
if [ "$OS" == "alma" ]; then
    dnf -y update
elif [ "$OS" == "ubuntu" ]; then
    apt-get update
    apt-get -y upgrade
fi

# Download and use MariaDB repo script
curl -sS $MARIADB_REPO | bash

# Install MariaDB
if [ "$OS" == "alma" ]; then
    dnf -y install mariadb-server mariadb
elif [ "$OS" == "ubuntu" ]; then
    apt-get -y install mariadb-server mariadb-client
fi

systemctl start mariadb
systemctl enable mariadb

# Secure MariaDB
mysql_secure_installation <<EOF

n
n
y
$root_password
$root_password
y
y
y
y
EOF

# Create database
mysql -u root -p"$root_password" <<EOF
CREATE DATABASE $dbname;
GRANT SELECT ON $dbname.* TO '$sql_user'@'127.0.0.1' IDENTIFIED BY '$sql_password';
FLUSH PRIVILEGES;
USE $dbname;
CREATE TABLE virtual_domains (
 DomainId INT NOT NULL AUTO_INCREMENT PRIMARY KEY, 
 DomainName VARCHAR(50) NOT NULL
);
CREATE TABLE virtual_mailboxes (
 MailId INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
 DomainId INT NOT NULL,
 password VARCHAR(255) NOT NULL,
 Email VARCHAR(100) UNIQUE KEY NOT NULL,
 FOREIGN KEY (DomainId) REFERENCES virtual_domains(DomainId) ON DELETE CASCADE
);
CREATE TABLE virtual_aliases (
 AliasId INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
 DomainId INT NOT NULL,
 Source VARCHAR(100) NOT NULL,
 Destination VARCHAR(100) NOT NULL,
 FOREIGN KEY (DomainId) REFERENCES virtual_domains(DomainId) ON DELETE CASCADE
);
EOF

# Install Postfix
if [ "$OS" == "alma" ]; then
    dnf -y install postfix postfix-mysql policycoreutils-python-utils
elif [ "$OS" == "ubuntu" ]; then
    apt-get -y install postfix postfix-mysql
fi

# Configure Postfix
postfix_main_cf="/etc/postfix/main.cf"
ensure_postfix_value "myhostname" "$myhostname" "$postfix_main_cf"
ensure_postfix_value "mydomain" "$mydomain" "$postfix_main_cf"
ensure_postfix_value "myorigin" "\$mydomain" "$postfix_main_cf"
ensure_postfix_value "inet_interfaces" "all" "$postfix_main_cf"
ensure_postfix_value "inet_protocols" "all" "$postfix_main_cf"
ensure_postfix_value "mydestination" "\$myhostname, localhost.\$mydomain, localhost" "$postfix_main_cf"
ensure_postfix_value "smtpd_recipient_restrictions" "permit_mynetworks" "$postfix_main_cf"
ensure_postfix_value "home_mailbox" "Maildir/" "$postfix_main_cf"
ensure_postfix_value "append_dot_mydomain" "no" "$postfix_main_cf"
ensure_postfix_value "biff" "no" "$postfix_main_cf"
ensure_postfix_value "config_directory" "/etc/postfix" "$postfix_main_cf"
ensure_postfix_value "dovecot_destination_recipient_limit" "1" "$postfix_main_cf"
ensure_postfix_value "smtp_tls_security_level" "may" "$postfix_main_cf"
ensure_postfix_value "smtp_tls_session_cache_database" "btree:\${data_directory}/smtp_scache" "$postfix_main_cf"
ensure_postfix_value "smtpd_tls_cert_file" "/etc/letsencrypt/live/$myhostname/fullchain.pem" "$postfix_main_cf"
ensure_postfix_value "smtpd_tls_key_file" "/etc/letsencrypt/live/$myhostname/privkey.pem" "$postfix_main_cf"
ensure_postfix_value "smtpd_sasl_auth_enable" "yes" "$postfix_main_cf"
ensure_postfix_value "smtpd_sasl_security_options" "noanonymous, noplaintext" "$postfix_main_cf"
ensure_postfix_value "smtpd_sasl_tls_security_options" "noanonymous" "$postfix_main_cf"
ensure_postfix_value "smtpd_tls_auth_only" "yes" "$postfix_main_cf"
ensure_postfix_value "smtpd_tls_security_level" "may" "$postfix_main_cf"
ensure_postfix_value "smtpd_tls_session_cache_database" "btree:\${data_directory}/smtpd_scache" "$postfix_main_cf"
ensure_postfix_value "smtpd_use_tls" "yes" "$postfix_main_cf"

cat <<EOF > /etc/postfix/virtual-mailbox-domains.conf
user = $sql_user
password = $sql_password
hosts = 127.0.0.1
dbname = $dbname
query = SELECT 1 FROM virtual_domains WHERE DomainName ='%s'
EOF

cat <<EOF > /etc/postfix/virtual-mailbox-users.conf
user = $sql_user
password = $sql_password
hosts = 127.0.0.1
dbname = $dbname
query = SELECT 1 FROM virtual_mailboxes WHERE Email='%s'
EOF

cat <<EOF > /etc/postfix/virtual-alias-maps.conf
user = $sql_user
password = $sql_password
hosts = 127.0.0.1
dbname = $dbname
query = SELECT Destination FROM virtual_aliases WHERE Source='%s'
EOF

chmod 640 /etc/postfix/virtual-mailbox-domains.conf
chmod 640 /etc/postfix/virtual-mailbox-users.conf
chmod 640 /etc/postfix/virtual-alias-maps.conf

# Modify /etc/postfix/master.cf
postfix_master_cf="/etc/postfix/master.cf"
cat <<EOF >> $postfix_master_cf
submission inet n       -       n       -       -       smtpd
  -o syslog_name=postfix/submission
  -o smtpd_tls_security_level=encrypt
  -o smtpd_sasl_auth_enable=yes
  -o smtpd_tls_auth_only=yes
  -o smtpd_reject_unlisted_recipient=no
  -o smtpd_recipient_restrictions=permit_sasl_authenticated,reject
  -o milter_macro_daemon_name=ORIGINATING

dovecot   unix  -       n       n       -       -       pipe
  flags=DRhu user=vmail:vmail argv=/usr/libexec/dovecot/deliver -f \${sender} -d \${recipient}
EOF

# Install Dovecot
if [ "$OS" == "alma" ]; then
    dnf -y install dovecot dovecot-mysql
elif [ "$OS" == "ubuntu" ]; then
    apt-get -y install dovecot-core dovecot-imapd dovecot-pop3d dovecot-mysql
fi

# Create vmail user and group
groupadd -g 5000 vmail
useradd -g vmail -u 5000 vmail -d /home/vmail -m

# Configure Dovecot
dovecot_conf="/etc/dovecot/dovecot.conf"
cat <<EOF >> $dovecot_conf
protocols = imap pop3 lmtp
listen = *, ::
!include conf.d/*.conf
!include_try local.conf

service stats {
    unix_listener stats-reader {
        user = vmail
        group = vmail
        mode = 0660
    }

    unix_listener stats-writer {
        user = vmail
        group = vmail
        mode = 0660
    }
}

ssl_dh = </etc/dovecot/dh.pem
EOF

# Configure additional Dovecot settings
echo "ssl_cert = </etc/letsencrypt/live/$myhostname/fullchain.pem" >> /etc/dovecot/conf.d/10-ssl.conf
echo "ssl_key = </etc/letsencrypt/live/$myhostname/privkey.pem" >> /etc/dovecot/conf.d/10-ssl.conf

echo "mail_location = maildir:/home/vmail/%d/%n/Maildir" >> /etc/dovecot/conf.d/10-mail.conf
echo "namespace inbox {" >> /etc/dovecot/conf.d/10-mail.conf
echo "  inbox = yes" >> /etc/dovecot/conf.d/10-mail.conf
echo "}" >> /etc/dovecot/conf.d/10-mail.conf
echo "mail_privileged_group = mail" >> /etc/dovecot/conf.d/10-mail.conf
echo "mbox_write_locks = fcntl" >> /etc/dovecot/conf.d/10-mail.conf

echo "service imap-login {" >> /etc/dovecot/conf.d/10-master.conf
echo "  inet_listener imap {" >> /etc/dovecot/conf.d/10-master.conf
echo "    port = 143" >> /etc/dovecot/conf.d/10-master.conf
echo "  }" >> /etc/dovecot/conf.d/10-master.conf
echo "  inet_listener imaps {" >> /etc/dovecot/conf.d/10-master.conf
echo "    port = 993" >> /etc/dovecot/conf.d/10-master.conf
echo "    ssl = yes" >> /etc/dovecot/conf.d/10-master.conf
echo "  }" >> /etc/dovecot/conf.d/10-master.conf
echo "}" >> /etc/dovecot/conf.d/10-master.conf

echo "service pop3-login {" >> /etc/dovecot/conf.d/10-master.conf
echo "  inet_listener pop3 {" >> /etc/dovecot/conf.d/10-master.conf
echo "    port = 110" >> /etc/dovecot/conf.d/10-master.conf
echo "  }" >> /etc/dovecot/conf.d/10-master.conf
echo "  inet_listener pop3s {" >> /etc/dovecot/conf.d/10-master.conf
echo "    port = 995" >> /etc/dovecot/conf.d/10-master.conf
echo "    ssl = yes" >> /etc/dovecot/conf.d/10-master.conf
echo "  }" >> /etc/dovecot/conf.d/10-master.conf
echo "}" >> /etc/dovecot/conf.d/10-master.conf

echo "service lmtp {" >> /etc/dovecot/conf.d/10-master.conf
echo "  unix_listener /var/spool/postfix/private/dovecot-lmtp {" >> /etc/dovecot/conf.d/10-master.conf
echo "    mode = 0600" >> /etc/dovecot/conf.d/10-master.conf
echo "    user = postfix" >> /etc/dovecot/conf.d/10-master.conf
echo "    group = postfix" >> /etc/dovecot/conf.d/10-master.conf
echo "  }" >> /etc/dovecot/conf.d/10-master.conf
echo "}" >> /etc/dovecot/conf.d/10-master.conf

echo "service auth {" >> /etc/dovecot/conf.d/10-master.conf
echo "  unix_listener auth-userdb {" >> /etc/dovecot/conf.d/10-master.conf
echo "    mode = 0600" >> /etc/dovecot/conf.d/10-master.conf
echo "    user = vmail" >> /etc/dovecot/conf.d/10-master.conf
echo "  }" >> /etc/dovecot/conf.d/10-master.conf
echo "  unix_listener /var/spool/postfix/private/auth {" >> /etc/dovecot/conf.d/10-master.conf
echo "    mode = 0666" >> /etc/dovecot/conf.d/10-master.conf
echo "    user = postfix" >> /etc/dovecot/conf.d/10-master.conf
echo "    group = postfix" >> /etc/dovecot/conf.d/10-master.conf
echo "  }" >> /etc/dovecot/conf.d/10-master.conf
echo "  user = dovecot" >> /etc/dovecot/conf.d/10-master.conf
echo "}" >> /etc/dovecot/conf.d/10-master.conf

echo "service auth-worker {" >> /etc/dovecot/conf.d/10-master.conf
echo "  user = vmail" >> /etc/dovecot/conf.d/10-master.conf
echo "}" >> /etc/dovecot/conf.d/10-master.conf

echo "service dict {" >> /etc/dovecot/conf.d/10-master.conf
echo "  unix_listener dict {" >> /etc/dovecot/conf.d/10-master.conf
echo "  }" >> /etc/dovecot/conf.d/10-master.conf
echo "}" >> /etc/dovecot/conf.d/10-master.conf

echo "disable_plaintext_auth = yes" >> /etc/dovecot/conf.d/10-auth.conf
echo "auth_mechanisms = plain login" >> /etc/dovecot/conf.d/10-auth.conf
echo "!include auth-sql.conf.ext" >> /etc/dovecot/conf.d/10-auth.conf

cat <<EOF > /etc/dovecot/conf.d/auth-sql.conf.ext
passdb {
  driver = sql
  args = /etc/dovecot/dovecot-sql.conf.ext
}

userdb {
  driver = static
  args = uid=vmail gid=vmail home=/home/vmail/%d/%n/Maildir
}
EOF

cat <<EOF > /etc/dovecot/dovecot-sql.conf.ext
driver = mysql
connect = "host=127.0.0.1 dbname=$dbname user=$sql_user password=$sql_password"
default_pass_scheme = SHA512-CRYPT
password_query = SELECT Email as User, password FROM virtual_mailboxes WHERE Email='%u';
EOF

# Generate dh.pem cert for Dovecot
openssl dhparam -out /etc/dovecot/dh.pem 4096

# Set permissions for Dovecot and mail directories
chown -R vmail:vmail /home/vmail
chown -R vmail:dovecot /etc/dovecot
chmod -R o-rwx /etc/dovecot

# Restart Dovecot
systemctl restart dovecot
systemctl enable dovecot

# Install Amavis
if [ "$OS" == "alma" ]; then
    dnf $ENABLE_REPO install -y amavisd-new
elif [ "$OS" == "ubuntu" ]; then
    apt-get -y install amavisd-new
fi

# Configure Amavis
amavisd_conf="/etc/amavis/conf.d/50-user"
sed -i "s/#\$mydomain =.*/\$mydomain = '$mydomain';/" $amavisd_conf
sed -i "s/#\$myhostname =.*/\$myhostname = '$myhostname';/" $amavisd_conf
sed -i "s/#\$notify_method =.*/\$notify_method = 'smtp:[127.0.0.1]:10025';/" $amavisd_conf
sed -i "s/#\$forward_method =.*/\$forward_method = 'smtp:[127.0.0.1]:10025';/" $amavisd_conf

# Add Amavis configuration to Postfix
cat <<EOF >> $postfix_master_cf
smtp-amavis unix -    -    n    -    2 smtp
    -o smtp_data_done_timeout=1200
    -o smtp_send_xforward_command=yes
    -o disable_dns_lookups=yes
127.0.0.1:10025 inet n    -    n    -    - smtpd
    -o content_filter=
    -o local_recipient_maps=
    -o relay_recipient_maps=
    -o smtpd_restriction_classes=
    -o smtpd_client_restrictions=
    -o smtpd_helo_restrictions=
    -o smtpd_sender_restrictions=
    -o smtpd_recipient_restrictions=permit_mynetworks,reject
    -o mynetworks=127.0.0.0/8
    -o strict_rfc821_envelopes=yes
    -o smtpd_error_sleep_time=0
    -o smtpd_soft_error_limit=1001
    -o smtpd_hard_error_limit=1000
EOF

# Restart Amavis
systemctl enable amavisd
systemctl restart amavisd

# Install SpamAssassin
if [ "$OS" == "alma" ]; then
    dnf -y install spamassassin
elif [ "$OS" == "ubuntu" ]; then
    apt-get -y install spamassassin
fi

# Update /etc/postfix/master.cf for SpamAssassin
sed -i 's/^\(smtp .*smtpd\)$/\1 -o content_filter=spamassassin/' $postfix_master_cf
cat <<EOF >> $postfix_master_cf
spamassassin unix - n n - - pipe flags=R user=spamd argv=/usr/bin/spamc -e /usr/sbin/sendmail -oi -f \${sender} \${recipient}
EOF

# Adjust SpamAssassin settings
spamassassin_conf="/etc/spamassassin/local.cf"
if [ ! -f "$spamassassin_conf" ]; then
    spamassassin_conf="/etc/mail/spamassassin/local.cf"
fi
sed -i 's/^#required_hits.*/required_hits 8/' $spamassassin_conf

# Create SpamAssassin user
groupadd spamd
useradd -g spamd -s /bin/false -d /var/log/spamassassin spamd
chown spamd:spamd /var/log/spamassassin

# Restart SpamAssassin and update spam rules
systemctl enable spamassassin
systemctl start spamassassin
sa-update
systemctl restart postfix

# Create cron job for SpamAssassin updates
(crontab -l 2>/dev/null; echo "0 0 * * 0 sa-update && systemctl reload spamassassin") | crontab -

# Install OpenDKIM
if [ "$OS" == "alma" ]; then
    dnf install -y libmilter libmemcached
    dnf -y --enablerepo=epel install opendkim
elif [ "$OS" == "ubuntu" ]; then
    apt-get -y install opendkim opendkim-tools
fi

# Configure OpenDKIM
opendkim_conf="/etc/opendkim.conf"
cat <<EOF >> $opendkim_conf
Mode     sv
ReportAddress "postmaster@$mydomain"
Selector key
#KeyFile /etc/opendkim/keys/dkim.private.key
KeyTable            /etc/opendkim/KeyTable
SigningTable        refile:/etc/opendkim/SigningTable
ExternalIgnoreList  refile:/etc/opendkim/TrustedHosts
InternalHosts       refile:/etc/opendkim/TrustedHosts
Socket  inet:8891@localhost
EOF

# Create OpenDKIM key and configuration files
mkdir -p /etc/opendkim/keys
cat <<EOF > /etc/opendkim/KeyTable
key._domainkey.$mydomain $mydomain:key:/etc/opendkim/keys/dkim.private.key
EOF

cat <<EOF > /etc/opendkim/SigningTable
*@$mydomain key._domainkey.$mydomain
EOF

cat <<EOF > /etc/opendkim/TrustedHosts
*.$mydomain
EOF

cd /etc/opendkim/keys
openssl genrsa -out dkim.private.key 4096
openssl rsa -in dkim.private.key -out dkim.public.key -pubout -outform PEM

# Set OpenDKIM permissions
chown opendkim:opendkim /etc/opendkim/keys/ -R
usermod -a -G opendkim postfix

# Add DKIM configuration to Postfix
cat <<EOF >> $postfix_main_cf
# Milter configuration for DKIM
milter_default_action = accept
milter_protocol = 6
smtpd_milters = inet:127.0.0.1:8891
non_smtpd_milters = \$smtpd_milters
EOF

# Configure smtpd_recipient_restrictions in Postfix
ensure_postfix_value "smtpd_recipient_restrictions" "permit_mynetworks,check_sender_access hash:/etc/postfix/sender_access,reject_rhsbl_helo dbl.spamhaus.org,reject_rhsbl_sender dbl.spamhaus.org,reject_rbl_client zen.spamhaus.org,reject_rbl_client bl.spamcop.net,reject_unauth_pipelining,reject_non_fqdn_recipient" "$postfix_main_cf"

# Create Postfix sender access file
cat <<EOF > /etc/postfix/sender_access
support@transip.nl      OK
junk@spam.com           REJECT
marketing@              REJECT
theboss@                REJECT
deals.marketing.com     REJECT
$mydomain               OK
EOF

# Restart services
systemctl start opendkim
systemctl enable opendkim
systemctl restart postfix

# Configure firewall
if [ "$OS" == "alma" ]; then
    firewall-cmd --zone=public --permanent --add-port=80/tcp
    firewall-cmd --zone=public --permanent --add-port=443/tcp
    firewall-cmd --zone=public --permanent --add-port=25/tcp
    firewall-cmd --zone=public --permanent --add-port=465/tcp
    firewall-cmd --zone=public --permanent --add-port=587/tcp
    firewall-cmd --zone=public --permanent --add-port=993/tcp
    firewall-cmd --zone=public --permanent --add-port=995/tcp
    firewall-cmd --reload
elif [ "$OS" == "ubuntu" ]; then
    ufw allow 80
    ufw allow 443
    ufw allow 25
    ufw allow 465
    ufw allow 587
    ufw allow 993
    ufw allow 995
    ufw reload
fi

# Install Certbot
if [ "$OS" == "alma" ]; then
    dnf -y install certbot
elif [ "$OS" == "ubuntu" ]; then
    apt-get -y install certbot
fi

# Generate SSL certificate
certbot certonly --standalone -d mail.$mydomain

# Set permissions for SSL certificate
chmod 755 /etc/letsencrypt/archive
chmod 755 /etc/letsencrypt/archive/mail.$mydomain
chmod 644 /etc/letsencrypt/archive/mail.$mydomain/privkey1.pem
chmod 755 /etc/letsencrypt/live
chmod 755 /etc/letsencrypt/live/mail.$mydomain

# Set up cron job for SSL certificate renewal
(crontab -l 2>/dev/null; echo "SHELL=/bin/bash") | crontab -
(crontab -l 2>/dev/null; echo "HOME=/") | crontab -
(crontab -l 2>/dev/null; echo "@monthly certbot -q renew >> /var/log/le.log") | crontab -

# Create mail directory
mkdir -p /home/vmail/$mydomain
mkdir /home/vmail/$mydomain/user
chown -R vmail:vmail /home/vmail/$mydomain

# Add domain and user to mailserver database
mysql -u root -p"$root_password" <<EOF
USE $dbname;
INSERT INTO virtual_domains (DomainName) VALUES ('$mydomain');
INSERT INTO virtual_mailboxes (DomainId, Email, password) VALUES (1, 'transip@$mydomain', ENCRYPT('$mail_password', CONCAT('\$6\$', SUBSTRING(SHA(RAND()), -16))));
EOF

# Enable and restart Postfix and Dovecot
systemctl enable postfix
systemctl enable dovecot
systemctl restart postfix
systemctl restart dovecot

echo "Installation and configuration complete."
