#!/usr/bin/env bash

# Author:   Zhang Huangbin (zhb _at_ iredmail.org)

#---------------------------------------------------------------------
# This file is part of iRedMail, which is an open source mail server
# solution for Red Hat(R) Enterprise Linux, CentOS, Debian and Ubuntu.
#
# iRedMail 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
# (at your option) any later version.
#
# iRedMail 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 iRedMail.  If not, see <http://www.gnu.org/licenses/>.
#---------------------------------------------------------------------

# Please refer another file: functions/backend.sh

# -------------------------------------------------------
# -------------------- MySQL ----------------------------
# -------------------------------------------------------
mysql_initialize_db()
{
    ECHO_DEBUG "Initialize MySQL server."

    backup_file ${MYSQL_MY_CNF}

    ECHO_DEBUG "Stop MySQL service before initializing database or updating my.cnf."
    service_control stop ${MYSQL_RC_SCRIPT_NAME}
    sleep 3

    # Initial MySQL database first
    if [ X"${DISTRO}" == X'OPENBSD' ]; then
        ECHO_DEBUG "Run mysql_install_db."
        /usr/local/bin/mysql_install_db >> ${INSTALL_LOG} 2>&1
    elif [ X"${DISTRO}" == X'FREEBSD' ]; then
        # 'mysql_enable=YES' is required to start service immediately.
        ECHO_DEBUG "Enable mysql service when system start up."
        service_control enable 'mysql_enable' 'YES'

        # rc script `mysql-server` doesn't create required directory.
        mkdir -p /var/run/mysql &>/dev/null
        chown mysql:mysql /var/run/mysql
        chmod 0755 /var/run/mysql
    fi

    if [ ! -e ${MYSQL_MY_CNF} ]; then
        ECHO_DEBUG "Copy sample MySQL config file: ${MYSQL_MY_CNF_SAMPLE} -> ${MYSQL_MY_CNF}."
        mkdir -p $(dirname ${MYSQL_MY_CNF}) &>/dev/null >> ${INSTALL_LOG} 2>&1

        if [ -f ${MYSQL_MY_CNF_SAMPLE} ]; then
            cp ${MYSQL_MY_CNF_SAMPLE} ${MYSQL_MY_CNF} >> ${INSTALL_LOG} 2>&1
        else
            cp ${SAMPLE_DIR}/mysql/my.cnf ${MYSQL_MY_CNF} >> ${INSTALL_LOG} 2>&1
        fi
    fi

    ECHO_DEBUG "Disable 'skip-networking' in my.cnf."
    perl -pi -e 's/^(skip-networking.*)/#${1}/' ${MYSQL_MY_CNF} >> ${INSTALL_LOG} 2>&1

    ECHO_DEBUG "Set max_connections to ${MYSQL_MAX_CONNECTIONS}."
    perl -pi -e 's#^(max_connections .*=)(.*)#${1} $ENV{MYSQL_MAX_CONNECTIONS}#' ${MYSQL_MY_CNF} >> ${INSTALL_LOG} 2>&1

    # Enable innodb_file_per_table by default.
    grep '^innodb_file_per_table' ${MYSQL_MY_CNF} &>/dev/null
    if [ X"$?" != X'0' ]; then
        ECHO_DEBUG "Enable 'innodb_file_per_table' in my.cnf."
        if grep '^\[mysqld\]' ${MYSQL_MY_CNF} &>/dev/null; then
            perl -pi -e 's#^(\[mysqld\])#${1}\ninnodb_file_per_table#' ${MYSQL_MY_CNF} >> ${INSTALL_LOG} 2>&1
        else
            echo "[mysqld]" >> ${MYSQL_MY_CNF}
            echo "innodb_file_per_table = ON" >> ${MYSQL_MY_CNF}
        fi
    fi

    grep '^log_warnings' ${MYSQL_MY_CNF} &>/dev/null
    if [ X"$?" != X'0' ]; then
        ECHO_DEBUG "Set 'log_warnings=1' in my.cnf."
        if grep '^\[mysqld\]' ${MYSQL_MY_CNF} &>/dev/null; then
            perl -pi -e 's#^(\[mysqld\])#${1}\nlog_warnings=1#' ${MYSQL_MY_CNF} >> ${INSTALL_LOG} 2>&1
        else
            echo "[mysqld]" >> ${MYSQL_MY_CNF}
            echo "log_warnings = 1" >> ${MYSQL_MY_CNF}
        fi
    fi

    if [ X"${DISTRO}" == X'FREEBSD' ]; then
        if [ X"${BACKEND}" == X'MYSQL' -o X"${BACKEND}" == X'OPENLDAP' ]; then
            if [ X"${LOCAL_ADDRESS}" != X'127.0.0.1' ]; then
                ECHO_DEBUG "Enable 'skip_grant_tables' option, so that we can reset password."
                perl -pi -e 's#^(\[mysqld\])#${1}\nskip_grant_tables#' ${MYSQL_MY_CNF} >> ${INSTALL_LOG} 2>&1

                # inside Jail: listen on Jail IP address
                ECHO_DEBUG "Enable 'bind-address = ${LOCAL_ADDRESS}' in my.cnf."
                perl -pi -e 's#^(bind-address).*#${1} = $ENV{LOCAL_ADDRESS}#' ${MYSQL_MY_CNF} >> ${INSTALL_LOG} 2>&1
            fi
        fi
    elif [ X"${DISTRO}" == X'OPENBSD' ]; then
        # Bind to 127.0.0.1 on OpenBSD
        grep '^bind-address' ${MYSQL_MY_CNF} &>/dev/null
        if [ X"$?" != X'0' ]; then
            ECHO_DEBUG "Enable 'bind-address = 127.0.0.1' in my.cnf."
            perl -pi -e 's#^(\[mysqld\])#${1}\nbind-address = 127.0.0.1#' ${MYSQL_MY_CNF} >> ${INSTALL_LOG} 2>&1
        fi
    fi

    if [ X"${USE_SYSTEMD}" == X'YES' ]; then
        mkdir -p /etc/systemd/system/mariadb.service.d &>/dev/null
        cp -f ${SAMPLE_DIR}/systemd/mariadb.service.d/override.conf /etc/systemd/system/mariadb.service.d/
        systemctl daemon-reload
    fi

    ECHO_DEBUG "Restart service: ${MYSQL_RC_SCRIPT_NAME}."
    service_control restart ${MYSQL_RC_SCRIPT_NAME}

    ECHO_DEBUG "Sleep 10 seconds for MySQL daemon initialization ..."
    sleep 10

    if [ X"${USE_EXISTING_MYSQL}" != X'YES' ]; then
        if [ X"${DISTRO}" == X'FREEBSD' -a X"${LOCAL_ADDRESS}" != X'127.0.0.1' ]; then
            # Jail
            mysql -h ${MYSQL_SERVER_ADDRESS} -u${MYSQL_ROOT_USER} --connect-expired-password mysql -e "UPDATE user SET host='${LOCAL_ADDRESS}',Password=PASSWORD('${MYSQL_ROOT_PASSWD}'),password_expired='N' WHERE User='root' AND Host='localhost'; FLUSH PRIVILEGES;" >> ${INSTALL_LOG} 2>&1

            ECHO_DEBUG "Remove 'skip_grant_tables'."
            perl -pi -e 's#^(skip_grant_tables.*)##g' ${MYSQL_MY_CNF} >> ${INSTALL_LOG} 2>&1

            ECHO_DEBUG "Restart service: ${MYSQL_RC_SCRIPT_NAME}."
            service_control restart ${MYSQL_RC_SCRIPT_NAME}

            ECHO_DEBUG "Sleep 10 seconds for MySQL daemon initialization ..."
            sleep 10
        else
            # Try to access without password, set a password if it's empty.
            if [ X"${MYSQL_SERVER_ADDRESS}" == X'127.0.0.1' ]; then
                # Use local socket
                _mysql_cmd="mysql -u${MYSQL_ROOT_USER}"
                _mysqladmin_cmd="mysqladmin -u${MYSQL_ROOT_USER}"
            else
                _mysql_cmd="mysql -h ${MYSQL_SERVER_ADDRESS} -u${MYSQL_ROOT_USER}"
                _mysqladmin_cmd="mysqladmin -h ${MYSQL_SERVER_ADDRESS} -u${MYSQL_ROOT_USER}"
            fi

            ${_mysql_cmd} -e "show databases" >> ${INSTALL_LOG} 2>&1
            if [ X"$?" == X'0' ]; then
                ECHO_DEBUG "Setting password for MySQL root user: ${MYSQL_ROOT_USER}."
                ${_mysqladmin_cmd} -u${MYSQL_ROOT_USER} password ${MYSQL_ROOT_PASSWD} >> ${INSTALL_LOG} 2>&1
                ${_mysql_cmd} -e "FLUSH PRIVILEGES" &>/dev/null

                #if ${_mysql_cmd} -e "DESC mysql.user" | grep '\<authentication_string\>' >> ${INSTALL_LOG} 2>&1; then
                #    ${_mysql_cmd} -e "UPDATE mysql.user SET authentication_string = PASSWORD('${MYSQL_ROOT_PASSWD}') WHERE User='root' AND Host='localhost'; FLUSH PRIVILEGES;" >> ${INSTALL_LOG} 2>&1
                #fi

                #if ${_mysql_cmd} -e "DESC mysql.user" | grep '\<Password\>' >> ${INSTALL_LOG} 2>&1; then
                #    ${_mysqladmin_cmd} password ${MYSQL_ROOT_PASSWD} >> ${INSTALL_LOG} 2>&1
                #fi
            else
                ECHO_DEBUG "MySQL root password is not empty, not reset."
            fi
        fi
    fi

    cat >> ${TIP_FILE} <<EOF
MySQL:
    * Root user: ${MYSQL_ROOT_USER}, Password: "${MYSQL_ROOT_PASSWD}" (without quotes)
    * Bind account (read-only):
        - Username: ${VMAIL_DB_BIND_USER}, Password: ${VMAIL_DB_BIND_PASSWD}
    * Vmail admin account (read-write):
        - Username: ${VMAIL_DB_ADMIN_USER}, Password: ${VMAIL_DB_ADMIN_PASSWD}
    * Config file: ${MYSQL_MY_CNF}
    * RC script: ${MYSQLD_RC_SCRIPT}

EOF

    echo 'export status_mysql_initialize_db="DONE"' >> ${STATUS_FILE}
}

mysql_generate_defaults_file_root()
{
    ECHO_DEBUG "Generate defauts file for MySQL client option --defaults-file: ${MYSQL_DEFAULTS_FILE_ROOT}."
    backup_file ${MYSQL_DEFAULTS_FILE_ROOT}

    cat > ${MYSQL_DEFAULTS_FILE_ROOT} <<EOF
[client]
user=${MYSQL_ROOT_USER}
password="${MYSQL_ROOT_PASSWD}"
EOF

    if [ X"${DISTRO}" == X'FREEBSD' \
        -o X"${LOCAL_ADDRESS}" != X'127.0.0.1' \
        -o X"${MYSQL_SERVER_ADDRESS}" != X'127.0.0.1' ]; then
        cat >> ${MYSQL_DEFAULTS_FILE_ROOT} <<EOF
host=${MYSQL_SERVER_ADDRESS}
port=${MYSQL_SERVER_PORT}
EOF
    fi

    cat > /root/.my.cnf-${VMAIL_DB_BIND_USER} <<EOF
[client]
host=${MYSQL_SERVER_ADDRESS}
port=${MYSQL_SERVER_PORT}
user=${VMAIL_DB_BIND_USER}
password="${VMAIL_DB_BIND_PASSWD}"
EOF

    cat > /root/.my.cnf-${VMAIL_DB_ADMIN_USER} <<EOF
[client]
host=${MYSQL_SERVER_ADDRESS}
port=${MYSQL_SERVER_PORT}
user=${VMAIL_DB_ADMIN_USER}
password="${VMAIL_DB_ADMIN_PASSWD}"
EOF

    chmod 0400 ${MYSQL_DEFAULTS_FILE_ROOT} /root/.my.cnf-{${VMAIL_DB_BIND_USER},${VMAIL_DB_ADMIN_USER}}

    # Debian/Ubuntu uses /etc/mysql/debian.cnf as the client config file
    # during installing or upgrading mariadb package.
    if [ X"${DISTRO}" == X'DEBIAN' -o X"${DISTRO}" == X'UBUNTU' ]; then
        if [ -f /etc/mysql/debian.cnf ]; then
            backup_file /etc/mysql/debian.cnf
            rm -f /etc/mysql/debian.cnf
            ln -sf /root/.my.cnf /etc/mysql/debian.cnf
        fi
    fi

    echo 'export status_mysql_generate_defaults_file_root="DONE"' >> ${STATUS_FILE}
}

mysql_grant_permission_on_remote_server()
{
    # If we're using a remote mysql server: grant access privilege first.
    if [ X"${USE_EXISTING_MYSQL}" == X'YES' -a X"${MYSQL_SERVER_ADDRESS}" != X'127.0.0.1' ]; then
        ECHO_DEBUG "Grant access privilege to ${MYSQL_ROOT_USER}@${MYSQL_GRANT_HOST} ..."

        cp -f ${SAMPLE_DIR}/mysql/sql/remote_grant_permission.sql ${RUNTIME_DIR}/
        perl -pi -e 's#PH_MYSQL_ROOT_USER#$ENV{MYSQL_ROOT_USER}#g' ${RUNTIME_DIR}/remote_grant_permission.sql
        perl -pi -e 's#PH_MYSQL_GRANT_HOST#$ENV{MYSQL_GRANT_HOST}#g' ${RUNTIME_DIR}/remote_grant_permission.sql
        perl -pi -e 's#PH_HOSTNAME#$ENV{HOSTNAME}#g' ${RUNTIME_DIR}/remote_grant_permission.sql

        ${MYSQL_CLIENT_ROOT} -e "SOURCE ${RUNTIME_DIR}/remote_grant_permission.sql;"
    fi

    echo 'export status_mysql_grant_permission_on_remote_server="DONE"' >> ${STATUS_FILE}
}

mysql_remove_insecure_data()
{
    ECHO_DEBUG "Delete anonymous database user."
    ${MYSQL_CLIENT_ROOT} -e "SOURCE ${SAMPLE_DIR}/mysql/sql/delete_anonymous_user.sql;"

    # Delete root accounts with empty passwords
    ${MYSQL_CLIENT_ROOT} -e "DESC mysql.user" | grep '\<Password\>' &>/dev/null
    if [ X"$?" == X'0' ]; then
        ECHO_DEBUG "Delete root access with empty passwords."
        ${MYSQL_CLIENT_ROOT} -e "DELETE FROM mysql.user WHERE User='root' AND Password=''"
    fi

    ${MYSQL_CLIENT_ROOT} -e "SHOW DATABASES" | grep '\<test\>' &>/dev/null
    if [ X"$?" == X'0' ]; then
        ECHO_DEBUG "Remove 'test' database."
        ${MYSQL_CLIENT_ROOT} -e "DROP DATABASE test"
    fi

    echo 'export status_mysql_remove_insecure_data="DONE"' >> ${STATUS_FILE}
}

# It's used only when backend is MySQL.
mysql_import_vmail_users()
{
    ECHO_DEBUG "Generate sample SQL templates."
    cp -f ${SAMPLE_DIR}/mysql/sql/init_vmail_db.sql ${RUNTIME_DIR}/
    cp -f ${SAMPLE_DIR}/iredmail/iredmail.mysql ${RUNTIME_DIR}/iredmail.sql
    cp -f ${SAMPLE_DIR}/mysql/sql/add_first_domain_and_user.sql ${RUNTIME_DIR}/

    perl -pi -e 's#PH_VMAIL_DB_NAME#$ENV{VMAIL_DB_NAME}#g' ${RUNTIME_DIR}/*.sql
    perl -pi -e 's#PH_VMAIL_DB_BIND_USER#$ENV{VMAIL_DB_BIND_USER}#g' ${RUNTIME_DIR}/*.sql
    perl -pi -e 's#PH_VMAIL_DB_BIND_PASSWD#$ENV{VMAIL_DB_BIND_PASSWD}#g' ${RUNTIME_DIR}/*.sql
    perl -pi -e 's#PH_VMAIL_DB_ADMIN_USER#$ENV{VMAIL_DB_ADMIN_USER}#g' ${RUNTIME_DIR}/*.sql
    perl -pi -e 's#PH_VMAIL_DB_ADMIN_PASSWD#$ENV{VMAIL_DB_ADMIN_PASSWD}#g' ${RUNTIME_DIR}/*.sql
    perl -pi -e 's#PH_MYSQL_GRANT_HOST#$ENV{MYSQL_GRANT_HOST}#g' ${RUNTIME_DIR}/*.sql
    perl -pi -e 's#PH_HOSTNAME#$ENV{HOSTNAME}#g' ${RUNTIME_DIR}/*.sql

    perl -pi -e 's#PH_DOMAIN_ADMIN_EMAIL#$ENV{DOMAIN_ADMIN_EMAIL}#g' ${RUNTIME_DIR}/*.sql
    perl -pi -e 's#PH_FIRST_DOMAIN#$ENV{FIRST_DOMAIN}#g' ${RUNTIME_DIR}/*.sql
    perl -pi -e 's#PH_TRANSPORT#$ENV{TRANSPORT}#g' ${RUNTIME_DIR}/*.sql
    perl -pi -e 's#PH_DOMAIN_ADMIN_PASSWD_HASH#$ENV{DOMAIN_ADMIN_PASSWD_HASH}#g' ${RUNTIME_DIR}/*.sql
    perl -pi -e 's#PH_DOMAIN_ADMIN_MAILDIR_HASH_PART#$ENV{DOMAIN_ADMIN_MAILDIR_HASH_PART}#g' ${RUNTIME_DIR}/*.sql
    perl -pi -e 's#PH_DOMAIN_ADMIN_NAME#$ENV{DOMAIN_ADMIN_NAME}#g' ${RUNTIME_DIR}/*.sql

    # Modify default SQL template
    perl -pi -e 's#^-- (USE.*)#${1}#g' ${RUNTIME_DIR}/iredmail.sql
    perl -pi -e 's#(.*storagebasedirectory.*DEFAULT).*#${1} "$ENV{STORAGE_BASE_DIR}",#' ${RUNTIME_DIR}/iredmail.sql
    perl -pi -e 's#(.*storagenode.*DEFAULT).*#${1} "$ENV{STORAGE_NODE}",#' ${RUNTIME_DIR}/iredmail.sql
    # Rename SQL table `vmail.used_quota`
    perl -pi -e 's#used_quota`#$ENV{DOVECOT_REALTIME_QUOTA_TABLE}`#g' ${RUNTIME_DIR}/iredmail.sql

    ECHO_DEBUG "Create database: ${VMAIL_DB_NAME}."
    ${MYSQL_CLIENT_ROOT} -e "SOURCE ${RUNTIME_DIR}/init_vmail_db.sql;"

    ECHO_DEBUG "Initialize database: ${VMAIL_DB_NAME}."
    ${MYSQL_CLIENT_ROOT} -e "SOURCE ${RUNTIME_DIR}/iredmail.sql;"

    ECHO_DEBUG "Add first domain and postmaster@ user."
    ${MYSQL_CLIENT_ROOT} -e "SOURCE ${RUNTIME_DIR}/add_first_domain_and_user.sql;"

    cat >> ${TIP_FILE} <<EOF
Virtual Users:
    - ${MYSQL_VMAIL_STRUCTURE_SAMPLE}
    - ${RUNTIME_DIR}/*.sql

EOF

    echo 'export status_mysql_import_vmail_users="DONE"' >> ${STATUS_FILE}
}

mysql_cron_backup()
{
    mysql_backup_script="${BACKUP_DIR}/${BACKUP_SCRIPT_MYSQL_NAME}"

    ECHO_INFO "Setup daily cron job to backup SQL databases with ${mysql_backup_script}"

    [ ! -d ${BACKUP_DIR} ] && mkdir -p ${BACKUP_DIR} >> ${INSTALL_LOG} 2>&1

    backup_file ${mysql_backup_script}
    cp ${TOOLS_DIR}/${BACKUP_SCRIPT_MYSQL_NAME} ${mysql_backup_script}
    chown ${SYS_USER_ROOT}:${SYS_GROUP_ROOT} ${mysql_backup_script}
    chmod 0500 ${mysql_backup_script}

    export MYSQL_ROOT_PASSWD
    perl -pi -e 's#^(export BACKUP_ROOTDIR=).*#${1}"$ENV{BACKUP_DIR}"#' ${mysql_backup_script}
    perl -pi -e 's#^(export MYSQL_ROOT_USER=).*#${1}"$ENV{MYSQL_ROOT_USER}"#' ${mysql_backup_script}

    # Add cron job
    cat >> ${CRON_FILE_ROOT} <<EOF
# ${PROG_NAME}: Backup MySQL databases at 03:30 AM.
30   3   *   *   *   ${SHELL_BASH} ${mysql_backup_script}

EOF

    if [ X"${INITIALIZE_SQL_DATA}" != X'YES' ]; then
        perl -pi -e 's/(.*bash.*backup_mysql.sh.*)/#${1}/g' ${CRON_FILE_ROOT}
    fi

    cat >> ${TIP_FILE} <<EOF
Backup MySQL database:
    * Script: ${mysql_backup_script}
    * See also:
        # crontab -l -u ${SYS_USER_ROOT}

EOF

    echo 'export status_mysql_cron_backup="DONE"' >> ${STATUS_FILE}
}

mysql_setup()
{
    ECHO_INFO "Configure MariaDB database server."

    if [ X"${USE_EXISTING_MYSQL}" != X'YES' ]; then
        check_status_before_run mysql_initialize_db
    fi

    check_status_before_run mysql_generate_defaults_file_root

    if [ X"${INITIALIZE_SQL_DATA}" == X'YES' ]; then
        check_status_before_run mysql_grant_permission_on_remote_server
        check_status_before_run mysql_remove_insecure_data
        check_status_before_run mysql_import_vmail_users
    fi

    check_status_before_run mysql_cron_backup

    write_iredmail_kv "sql_user_${MYSQL_ROOT_USER}" "${MYSQL_ROOT_PASSWD}"

    if [ X"${BACKEND}" == X'MYSQL' ]; then
        write_iredmail_kv "sql_user_${VMAIL_DB_BIND_USER}" "${VMAIL_DB_BIND_PASSWD}"
        write_iredmail_kv "sql_user_${VMAIL_DB_ADMIN_USER}" "${VMAIL_DB_ADMIN_PASSWD}"
    fi

    echo 'export status_mysql_setup="DONE"' >> ${STATUS_FILE}
}
