#!/usr/bin/bash
# zmsetup
#
# Description:
# This is a setup wizard for the Mageia installation of ZoneMinder.
# It assumes a default installation using mysql (mariadb) and apache.
# It will check for and if necessary install mysql, start mysql, set up a
# mysql root password and create the ZoneMinder database if 
# required, or update an existing one, setting required db permissions.
# It will check the password strength of any passwords created.
# It will set the date.timezone information in php.ini which is required for
# ZoneMinder to run.
# When all is ready it will start Mysql, Apache, and ZoneMinder and if all is
# well it will give the url of the running ZoneMinder server.
#
# Use:-
# After installation of ZoneMinder run this script as root and answer any questions.
# $ su
# # zmsetup
#===================================
# Changelog
# 13/02/2023 Remove obsolete code. Tidy indentation. Remove tabs. Bug fix.
# 11/08/2021 Add option to replace a forgotten mysql root password.
# 03/08/2021 Revert root password drop, update syntax for mariadb-10.x, 
#            add mysql_upgrade after root password check/create.
#            SET GLOBAL log_bin_trust_function_creators = 1
#            for some special db updates.
# 26/02/2020 Drop use of mysql root password for mariadb-10.4
# 20/12/2018 Check for ScriptAlias in httpd.conf that may clash with ours
# 06/12/2018 Get data from /etc/zm/conf.d/03-mga.conf or /etc/zm/zm.conf
# 22/11/2018 Change /etc/zm.conf to /etc/zm/zm.conf and add /etc/zm/conf.d/03-mga.conf
# 07/11/2018 Use /etc/php.d/05_date.ini for timezone and when needed set it from
#            /etc/clock
# 25/08/2016 Force use of mysql root password
#            Force change of zmuser password in zm.conf if still default
#            Check password(s) validity on entry to new mariadb policy
#            Don't use mysqladmin to set root password
# 18/04/2016 Check timezone is set in php.ini and set it if not
# 19/11/2015 Set mysql db permissions (removes patch from package)
# 22/03/2014 Add file structure update for 1.27.x
# 17/09/2013 Use zmuser not root to modify db (reverted 22/03/2014)
# 22/03/2013 Check for and install mariadb
# 21/01/2012 Moved all service commands to systemctl
# 10/10/2011 1.25.0 no perf update to db required 
# 25/09/2011  Bug fix 
#===================================

confirm()
# User query interface.
{
par=("$@")
rval=3
while (( rval > 2 )); do
    echo -n "${par[0]} ${par[1]} "
    ans= ; rval=
    read -r ans
    if [[ -z $ans ]] || (( ${#ans} > 1 )); then
        rval=3
    else
        case $ans in
            [${par[2]}]*)
                rval=0
                ;;
            [${par[3]}]*)
                rval=1
                ;;
            [${par[4]}]*)
                rval=2
                ;;
            *)
                rval=3
                ;;
        esac
    fi
done
    return $rval
}

#-----------------------
chkpasswd() {
pw="$1"
result=$(echo "$pw"|/usr/sbin/cracklib-check|cut -d: -f2)
if echo "$result"|grep OK; then
	return 0
else
    messg="The password is not strong enough, $result."
    return 1
fi
}

#------------------------
check_mysqld()
{
if [[ $(systemctl status mysqld.service|head -n3|grep Loaded:|tr -s ' '|cut -s -d' ' -f3) != loaded ]]; then
    if confirm "mysql is not installed, would you like to install it now?" "[y/n]" "Yy" "Nn"; then
        urpmi mysql --no-suggests || { echo "Installation failed - please check network and media sources and re-run zmsetup"; exit 0; }
    else
        echo "You will need to provide a mysql database yourself - aborting zmsetup"
    exit 0
    fi
fi

if [[ "$(systemctl status mysqld.service|head -n3|grep -F "Active:"|tr -s ' '|cut -s -d' ' -f3)" != "active" ]]; then
    systemctl start mysqld.service
fi

[[ "$(systemctl status mysqld.service|head -n3|grep -F "Active:"|tr -s ' '|cut -s -d' ' -f3)" == "active" ]] || \
{ echo -e "Aborting zmsetup, see status message:-\n"; systemctl status mysqld.service; exit 0; } 
}

#------------------------
closeall()
{
systemctl stop zoneminder.service > /dev/null 2>&1
systemctl stop httpd.service > /dev/null 2>&1
systemctl stop mysqld.service > /dev/null 2>&1
}

#------------------------
pass() {
passw=
passw2=
while true; do
    read -s -r -p "New password: " passw
    echo
    chkpasswd "$passw" || { echo "$messg"; messg= ; continue; }
    read -s -r -p "Repeat password: " passw2
    echo
    [[ "$passw" == "$passw2" ]] || { echo "Passwords differ, please start again"; continue; }
    break
done
}

#-----------------------
change_mysqld_root_pw() {
new_password="$1"

# Stop mysqld
systemctl stop mysqld

# Start mysqld_safe
echo "Starting mysqld_safe - please wait..."
mysqld_safe  --skip-grant-tables --skip-networking &

RET=1
while [[ RET -ne 0 ]]; do
    sleep 5
    mysql -u root -e "status" > /dev/null 2>&1
    RET=$?
done
echo "mysqld_safe is running"

# Change password
echo "Changing password"
mysql -u root mysql -se "FLUSH PRIVILEGES; ALTER USER 'root'@'localhost' IDENTIFIED BY '$new_password';"

# Stop mysqld_safe
echo "Stopping mysqld_safe"
[[ -f /var/run/mysqld/mysqld.pid ]] && kill "$(cat /var/run/mysqld/mysqld.pid)"

# Restart mysqld
systemctl start mysqld.service
}

#-------------------------
getrootpass() {
if mysql -u root -e 'SELECT 1;' > /dev/null 2>&1 ; then
    echo -e "You do not appear to have a mysql root password set.\n"
    echo -e "$messg1"
    set=0
    while [[ $set = 0 ]]; do
        pass
        passwd=$passw
        if confirm "Set new password now - confirm - OK?" "[y/n]" "Yy" "Nn"; then
            echo "Please wait ..."
            mysql -u root mysql -se "ALTER USER 'root'@'localhost' IDENTIFIED BY '$passwd'; flush privileges;"
            systemctl restart mysqld.service
            set=1
        else
            set=0
        fi
    done
else # Root password is already set so ask for it
    badpass=0
    while true ; do
        read -s -r -p "Please enter your mysql root password: " passwd
        echo
        (( ${#passwd} == 0 )) && continue
        if mysql -u root -p"$passwd" -e 'SELECT 1;' > /dev/null 2>&1; then
            break
        fi
        badpass=$((badpass+1))
        if (( badpass == 1 )); then
            echo "After 2 more failed attempts you will be given the option to set a new password"
        fi

        if (( badpass > 2 )) && confirm "Forgotten it? Set a new mysql root password now?" "[y/n]" "Yy" "Nn"; then
            passw=
            pass
            change_mysqld_root_pw "$passw"
            passwd=
            badpass=0
            echo -e "\nPassword is now changed"
        fi
    done
fi
mysqlpass="-p$passwd"
mysqlrpass="$passwd"
}

#----------------------------
updtsql() {
echo -e "Checking mysql database is current, please wait...\n"
# mysql_upgrade was removed in MariaDB 10.4.6+
# Upgrades now happen automatically on service start
if command -v mysql_upgrade &> /dev/null; then
    # Old MariaDB/MySQL - run mysql_upgrade
    mysql_upgrade -u root "$mysqlpass" || { echo "zmupdate halted due to above error"; exit 1; }
else
    # New MariaDB - upgrade is automatic on service start
    echo "MariaDB 10.4.6+ detected - database upgrades are automatic"
    systemctl restart mysqld.service
fi
}

#----------------------------
get_cfg() {
if [[ -f /etc/zm/conf.d/03-mga.conf ]]; then
zm_db_name=$(grep ZM_DB_NAME /etc/zm/conf.d/03-mga.conf|cut -d= -f2)
    if (( ${#zm_db_name} == 0 )); then
        zm_db_name=$(grep ZM_DB_NAME /etc/zm/zm.conf|cut -d= -f2)
    fi	
zm_db_user=$(grep ZM_DB_USER /etc/zm/conf.d/03-mga.conf|cut -d= -f2)
    if (( ${#zm_db_user} == 0 )); then
        zm_db_user=$(grep ZM_DB_USER /etc/zm/zm.conf|cut -d= -f2)
    fi
zm_db_pass=$(grep ZM_DB_PASS /etc/zm/conf.d/03-mga.conf|cut -d= -f2)
    if (( ${#zm_db_pass} == 0 )); then
        zm_db_pass=$(grep ZM_DB_PASS /etc/zm/zm.conf|cut -d= -f2)
    fi
fi
}

#----------------------------
chkzmpass()
{
if ! chkpasswd "$zm_db_pass"; then
echo "Checking the current ZM_USER_PASSWORD..."
echo "$messg"
messg=
echo -e "$messg1"
pass
zmpasswd="$passw"
passw=
sed -i "s/$zm_db_pass/$zmpasswd/" /etc/zm/conf.d/03-mga.conf
zm_db_pass="$zmpasswd"
fi
}

updtdb()
{
setperms
zmupdate.pl -u root -p "$mysqlrpass"
zmupdate.pl -f -u root -p "$mysqlrpass"
}

#----------------------------
chkdb()
{
if mysql -u root "$mysqlpass" "$zm_db_name" -e 'SELECT 1;' > /dev/null 2>&1 ; then
    echo "You already have a ZoneMinder database installed"
    zmdb=1
else
    zmdb=0
fi

if (( zmdb == 1 )) && confirm "Do you want to re-use it?" "[y/n]" "Yy" "Nn"; then
    echo "Updating database structure where necessary ..."
    updt=1
    reuse=1
else
    reuse=0
fi

if (( zmdb == 1 )) && (( reuse == 0 )); then
    if confirm "Delete existing ZoneMinder database? OK?"  "[y/n]" "Yy" "Nn"; then
        mysql -u root "$mysqlpass" -e "DROP DATABASE $zm_db_name;"
        zmdb=0
    else
        echo "You must delete the old database before creating a new one - aborting"
        exit 0
    fi
fi

if (( zmdb == 0 )); then
    echo "Installing a new ZoneMinder database ..."
    mysql -u root "$mysqlpass" < /usr/share/zoneminder/db/zm_create.sql
    setperms
fi

(( updt == 1 )) && updtdb
}

setperms() {
# Create user if not exists (MariaDB 10.4+ compatible syntax)
mysql -u root "$mysqlpass" -e "SET GLOBAL log_bin_trust_function_creators = 1; CREATE USER IF NOT EXISTS '$zm_db_user'@'localhost' IDENTIFIED BY '$zm_db_pass'; GRANT ALL PRIVILEGES ON $zm_db_name.* TO '$zm_db_user'@'localhost'; FLUSH PRIVILEGES;"
}

chkphp() {
if ! grep "^date\.timezone =" /etc/php.d/05_date.ini >/dev/null 2>&1 \
  && ! grep "^date\.timezone =" /etc/php.ini >/dev/null 2>&1; then
    echo "You do not appear to have a timezone set for php."
    echo -e "This is required for the Web-UI to work.\n"
    if [[ -f /etc/sysconfig/clock ]]; then
        tz=$(grep "ZONE" /etc/sysconfig/clock|cut -d= -f2)
        if [[ -f /etc/php.d/05_date.ini ]]; then
            echo "date.timezone = $tz" >> /etc/php.d/05_date.ini
            echo -e "Added timezone $tz to /etc/php.d/05_date.ini \n"
        else
            echo "date.timezone = $tz" >> /etc/php.ini
            echo -e "Added timezone $tz to /etc/php.ini \n"
        fi
    else
        echo -e "Automatic timezone detection failed, so you need to \n\
add a line like 'date.timezone = Europe/London' to /etc/php.d/05_date.ini\n\
See http://php.net/manual/en/timezones.php for the full list.\n\n\
Aborting zmsetup - Re-run it after fixing php timezone."
        exit 1
    fi
fi
}

http_conf() {
if [[ -e /etc/httpd/conf/httpd.conf ]]; then
    cat /etc/httpd/conf/httpd.conf |sed -e 's/^[[:space:]]*//'|grep -v "^#" |grep -wq  ^ScriptAlias > /dev/null 2>&1
    if [[ $? = 0 ]]; then
        echo -e "\nNOTE: You may need to comment out or edit the line beginning 'ScriptAlias' in\n\
in /etc/httpd/httpd.conf if you cannot view streams and see cgi errors in the log.\n\n\
Note that the cgi-bin ScripAlias for ZoneMinder is already set in\n\
/etc/httpd/conf/sites.d/zoneminder.conf\n"
    fi
fi
}

messg1="Passwords should have at least eight characters with no dictionary\n\
words or common sequences.\n\
Please enter a new one now. (hint: write it down first as it will not be displayed)"

# Script main body starts here ================================
clear
# Check we are root
((UID)) && { echo "Sorry, you must run this as root."; exit 1; }
echo -e "*** Welcome to ZoneMinder Setup ***"
# Get some data from zm.conf and/or 03-mga.conf
get_cfg

# Check that a non-default zm user password has been set
chkzmpass

echo "Please wait a moment..."

# Check that mysql is installed and enabled
check_mysqld

# Stop apache mysql and zoneminder services
closeall

# Start mysql
systemctl start mysqld.service

# Check if a mysql root password is set or set one, or replace forgotten one
getrootpass

# Run mariadb_upgrade in case it has been updated
updtsql

# Check for existing zm database and create new or update existing one
chkdb

# Check timezone is set in /etc/php.d/05_date.ini or zm won't run
chkphp

# Warn about ScriptAlias in default httpd.conf
http_conf

# Rename /etc/zm/conf.d/zmcustom.conf if it was created by zmupdate.pl we don't need it
[[ -f /etc/zm/conf.d/zmcustom.conf ]] && mv /etc/zm/conf.d/zmcustom.conf /etc/zm/conf.d/zmcustom.bak

# Start Apache and Zoneminder
systemctl start httpd.service > /dev/null 2>&1 || { echo "Problem starting Apache"; exit 1; }
systemctl start zoneminder.service || { echo "Problem starting ZoneMinder"; exit 1; }
# If we got this far then congratulations are in order!
echo -e "Congratulations - ZoneMinder is now running.\nYou should be able to \
access the ZM Console in your browser using :-\nhttp://$(hostname)/zm" 
