#!/bin/bash
#  _____               _           _
# |   __|___ ___ ___ _| |___ _____| |_ ___ ___ ___
# |   __|  _| -_| -_| . | . |     | . | . |   | -_|
# |__|  |_| |___|___|___|___|_|_|_|___|___|_|_|___|
#
#                              Freedom in the Cloud
#
# Install daemon for the web admin system
# See also freedombone-utils-webadmin
#
# License
# =======
#
# Copyright (C) 2018 Bob Mottram <bob@freedombone.net>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) 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 Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

PROJECT_NAME='freedombone'

export TEXTDOMAIN=${PROJECT_NAME}-installer
export TEXTDOMAINDIR="/usr/share/locale"

INSTALL_STATE_FIRST_BOOT=0
INSTALL_STATE_WEBADMIN_PASSWORD_CTEATED=1
INSTALL_STATE_RUNNING_COMMAND=2
INSTALL_STATE_COMMAND_FAIL=3
INSTALL_STATE_COMMAND_SUCCESS=4

install_state=$INSTALL_STATE_FIRST_BOOT

CONFIGURATION_FILE="/root/${PROJECT_NAME}.cfg"
COMPLETION_FILE="/root/${PROJECT_NAME}-completed.txt"

# file containing a list of removed apps
REMOVED_APPS_FILE=/root/removed

# contains the blocked users and domains
FIREWALL_DOMAINS=/root/${PROJECT_NAME}-firewall-domains.cfg

local_hostname=$(grep 'host-name' /etc/avahi/avahi-daemon.conf | awk -F '=' '{print $2}').local

webadmin_install_dir="/var/www/${local_hostname}/htdocs/admin"
language_file="$webadmin_install_dir/.language.txt"
system_monitor_file="$webadmin_install_dir/.system_monitor.txt"
dynamic_dns_file="$webadmin_install_dir/.dynamicdns.txt"
reset_file="$webadmin_install_dir/.reset.txt"
shutdown_file="$webadmin_install_dir/.shutdown.txt"
setup_file="$webadmin_install_dir/setup.txt"
domain_file="$webadmin_install_dir/.temp_domain.txt"
pending_removes="$webadmin_install_dir/pending_removes.txt"
pending_installs="$webadmin_install_dir/pending_installs.txt"
new_user_file="$webadmin_install_dir/.new_user.txt"
remove_user_file="$webadmin_install_dir/.remove_user.txt"
change_password_file="$webadmin_install_dir/changepassword.dat"
install_state_file="/root/.install_state.txt"
blocklist_file="$webadmin_install_dir/.blocklist.txt"
INSTALL_DIR=/root/build
webadmin_user='admin'
webadmin_temp_password_file=/root/.temp_webadmin_password
installer_script=/root/.fbone_installer.sh

# Used to initiate automatic backups
webadmin_prev_hour=99

# temporary backup and restore scripts
backup_script=/root/.webadmin_backup.sh
backup_keys_script=/root/.webadmin_backup_keys.sh
restore_script=/root/.webadmin_restore.sh
restore_keys_script=/root/.webadmin_restore_keys.sh
format_script=/root/.webadmin_format.sh
new_user_script=/root/.webadmin_new_user.sh
remove_user_script=/root/.webadmin_remove_user.sh

# Files used to initiate backup or restore manually from the web UI
backup_file="$webadmin_install_dir/.start_backup"
backup_keys_file="$webadmin_install_dir/.start_backup_keys"
restore_file="$webadmin_install_dir/.start_restore"
restore_keys_file="$webadmin_install_dir/.start_restore_keys"
format_file="$webadmin_install_dir/.start_format"

# These files contain the percentage progress
backup_progress_file=/root/.backup_progress.txt
restore_progress_file=/root/.backup_progress.txt
format_progress_file=/root/.format_progress.txt

function webadmin_update_version {
    local_hostname=$(grep 'host-name' /etc/avahi/avahi-daemon.conf | awk -F '=' '{print $2}').local
    webadmin_install_dir="/var/www/${local_hostname}/htdocs/admin"
    if [ -f "$webadmin_install_dir/about.html" ]; then
        VERSION=$(grep "VERSION=" "/usr/local/bin/${PROJECT_NAME}-vars" | grep -v DEBIAN | awk -F '"' '{print $2}')
        sed -i "s|\"versiontext\".*|\"versiontext\">$VERSION</p>|g" "$webadmin_install_dir/about.html"
    fi
}

function web_admin_onion_only {
    # In onion only mode domain names or ddns codes
    # don't need to be provided

    if ! grep -q 'ONION_ONLY=' "$CONFIGURATION_FILE"; then
        return
    fi

    ONION_ONLY=$(grep 'ONION_ONLY=' "$CONFIGURATION_FILE" | head -n 1 | awk -F '=' '{print $2}')

    if [ ! "$ONION_ONLY" ]; then
        ONION_ONLY='no'
    fi

    if [[ "$ONION_ONLY" == 'no' ]]; then
        return
    fi

    # shellcheck disable=SC2154
    sed -i '/freedns_code/d' "$webadmin_install_dir/app_add_template.html"
    # shellcheck disable=SC2154
    sed -i '/freedns_code/d' "$webadmin_install_dir/add_app_confirm_template.html"

    sed -i '/install_domain/d' "$webadmin_install_dir/app_add_template.html"
    sed -i '/install_domain/d' "$webadmin_install_dir/add_app_confirm_template.html"

    sed -i 's|onion_only=false;|onion_only=true;|g' "$webadmin_install_dir/installapp.php"
    sed -i 's|onion_only=false;|onion_only=true;|g' "$webadmin_install_dir/installappconfirm.php"

    sed -i 's|setup_domain.html|setup_installing.html|g' "$webadmin_install_dir/setupconfirm.php"
}

function web_admin_create_add_apps {
    if grep -q 'ONION_ONLY=' "$CONFIGURATION_FILE"; then
        ONION_ONLY=$(grep 'ONION_ONLY=' "$CONFIGURATION_FILE" | head -n 1 | awk -F '=' '{print $2}')
    else
        ONION_ONLY='no'
    fi

    appslist_add_filename="$webadmin_install_dir/apps_add.html"

    apps_add_template_filename="$webadmin_install_dir/apps_add_template.html"
    pending_installs="$webadmin_install_dir/pending_installs.txt"
    icons_dir="$webadmin_install_dir/icons"
    app_add_template_filename="$webadmin_install_dir/app_add_template.html"
    FILES="/usr/share/${PROJECT_NAME}/apps/${PROJECT_NAME}-app-*"

    if [ ! -d "$icons_dir" ]; then
        mkdir -p "$icons_dir"
    fi

    if [ ! -f "$apps_add_template_filename" ]; then
        return
    fi
    cp "$apps_add_template_filename" "$appslist_add_filename"
    sed -i '/<\/body>/d' "$appslist_add_filename"
    sed -i '/<\/html>/d' "$appslist_add_filename"

    available_apps_ctr=0
    for filename in $FILES
    do
        app_name=$(echo "${filename}" | awk -F '-app-' '{print $2}')

        app_is_installed=

        # check if the app is pending installation
        app_pending_install=
        if [ -f "$pending_installs" ]; then
            if grep -q "install_${app_name}" "$pending_installs"; then
                app_is_installed=1
                app_pending_install=1
            fi
        fi

        if [ ! $app_is_installed ]; then
            app_filename="/usr/share/${PROJECT_NAME}/apps/${PROJECT_NAME}-app-${app_name}"
            if [ -f "$app_filename" ]; then
                if grep -q "VARIANTS=''" "$app_filename"; then
                    continue
                fi
                if grep -q 'VARIANTS=""' "$app_filename"; then
                    continue
                fi
                # get the icon for the app
                icon_filename="/usr/share/${PROJECT_NAME}/android-app/${app_name}.png"
                if [ -f "$icon_filename" ]; then
                    cp "$icon_filename" "$webadmin_install_dir/icons/${app_name}.png"
                else
                    continue
                fi

                app_name_upper=$(echo "$app_name" | awk '{print toupper($0)}')
                DESCRIPTION=

                if ! grep -q "${app_name_upper}_SHORT_DESCRIPTION=" "$app_filename"; then
                    continue
                fi
                if grep -q "#${app_name_upper}_SHORT_DESCRIPTION=" "$app_filename"; then
                    continue
                fi

                if grep -q "${app_name_upper}_DESCRIPTION=" "$app_filename"; then
                    DESCRIPTION="$(grep "${app_name_upper}_DESCRIPTION=" "$app_filename" | head -n 1 | sed 's|\$||g' | sed "s|'||g" | sed 's|\"||g' | awk -F '=' '{print $2}')"
                fi

                if [ $available_apps_ctr -eq 0 ]; then
                    echo '    <div class="row">' >> "$appslist_add_filename"
                fi

                filename="$webadmin_install_dir/app_${app_name}.html"
                if [ -f "$filename" ]; then
                    rm "$filename"
                fi

                filename="$webadmin_install_dir/app_add_${app_name}.html"

                if [ ! $app_pending_install ]; then
                    { echo '      <div class="column">';
                      echo '        <div>';
                      echo "          <a href=\"app_add_${app_name}.html\">";
                      echo "            <img src=\"icons/${app_name}.png\" style=\"width:100%\">";
                      echo "            <center>${app_name}</center>";
                      echo '          </a>';
                      echo '        </div>';
                      echo '      </div>'; } >> "$appslist_add_filename"
                else
                    { echo '      <div class="column">';
                      echo '        <div>';
                      echo "        <img src=\"icons/${app_name}.png\" style=\"width:100%\">";
                      echo "        <center class=\"installing\">${app_name}</center>";
                      echo '        </div>';
                      echo '      </div>'; } >> "$appslist_add_filename"
                fi

                cp "$app_add_template_filename" "$filename"

                # Replace app variables
                sed -i "s|HOSTNAME|$(hostname)|g" "$filename"
                sed -i "s|APPNAME|${app_name}|g" "$filename"
                sed -i "s|APPDESCRIPTION|${DESCRIPTION}|g" "$filename"

                # remove domain if onion only
                if [[ "$ONION_ONLY" != 'no' ]]; then
                    if grep -q 'install_domain' "$filename"; then
                        sed -i '/install_domain/d' "$filename"
                    fi
                    sed -i "/installappconfirm.php/a <input type=\"hidden\" name=\"install_domain\" value=\"${app_name}.$(hostname)\">" "$filename"
                fi

                if grep -q 'freedns_code' "$filename"; then
                    sed -i '/freedns_code/d' "$filename"
                fi

                available_apps_ctr=$((available_apps_ctr+1))

                # four columns per row
                if [ $available_apps_ctr -eq 4 ]; then
                    echo '    </div>' >> "$appslist_add_filename"
                    available_apps_ctr=0
                fi
            fi
        fi
    done

    if [ ${available_apps_ctr} -gt 0 ]; then
        # Complete the rest of the four column row
        # shellcheck disable=SC2034
        for i in $(seq ${available_apps_ctr} 3)
        do
            { echo '      <div class="column">';
              echo '        <div>';
              echo '        </div>';
              echo '      </div>'; } >> "$appslist_add_filename"
        done
        echo '    </div>' >> "$appslist_add_filename"
    fi

    { echo '    <br><br>';
      echo '  </body>';
      echo '</html>'; } >> "$appslist_add_filename"

    chown -R www-data:www-data "$webadmin_install_dir"
}

function web_admin_create_installed_apps {
    appslist_filename="$webadmin_install_dir/apps.html"

    appslist_template_filename="$webadmin_install_dir/apps_template.html"
    icons_dir="$webadmin_install_dir/icons"
    pending_removes="$webadmin_install_dir/pending_removes.txt"

    if [ ! -d "$icons_dir" ]; then
        mkdir -p "$icons_dir"
    fi

    if [ ! -f "$appslist_template_filename" ]; then
        return
    fi
    cp "$appslist_template_filename" "$appslist_filename"
    sed -i '/<\/body>/d' "$appslist_filename"
    sed -i '/<\/html>/d' "$appslist_filename"

    installed_apps_ctr=0

    # Complete the rest of the four column row
    # shellcheck disable=SC2034
    for i in $(seq ${installed_apps_ctr} 3)
    do
        { echo '      <div class="column">';
          echo '        <div>';
          echo '        </div>';
          echo '      </div>'; } >> "$appslist_filename"
    done

    echo '  </body>' >> "$appslist_filename"
    echo '</html>' >> "$appslist_filename"

    chown -R www-data:www-data "$webadmin_install_dir"
}

function web_admin_create_users {
    if [ ! -d "/home/$webadmin_user" ]; then
        return
    fi

    users_file="$webadmin_install_dir/users.html"

    cp "$webadmin_install_dir"/users_template.html "$users_file"
    sed -i '/users list/,/end of users/d' "$users_file"
    sed -i '/<\/body>/d' "$users_file"
    sed -i '/<\/html>/d' "$users_file"

    echo "    <div class=\"row\">" >> "$users_file"
    echo "      <div class=\"column\">" >> "$users_file"

    rm "$webadmin_install_dir/userprofile_"*

    USERNAME="$webadmin_user"
    userfile="$webadmin_install_dir/userprofile_${USERNAME}.html"
    useremail=${USERNAME}@${HOSTNAME}
    GPG_ID=$(su -c "gpg --list-keys '$useremail'" - "$USERNAME" | sed -n '2p' | sed 's/^[ \t]*//')
    if [ -f "/home/$USERNAME/.gnupg/gpg.conf" ]; then
        if grep -q "default-key" "/home/$USERNAME/.gnupg/gpg.conf"; then
            default_gpg_key=$(grep "default-key" "/home/$USERNAME/.gnupg/gpg.conf")
            if [[ "$default_gpg_key" != *'#'* ]]; then
                default_gpg_key=$(grep "default-key" "/home/$USERNAME/.gnupg/gpg.conf" | awk -F ' ' '{print $2}')
                if [ ${#default_gpg_key} -gt 3 ]; then
                    GPG_ID=$(su -c "gpg --list-keys '$default_gpg_key'" - "$USERNAME" | sed -n '2p' | sed 's/^[ \t]*//')
                fi
            fi
        fi
    fi
    pubkey_qrcode="$webadmin_install_dir/images/userprofile_${USERNAME}.png"
    su -c "gpg --armor --export \"$GPG_ID\"" - "$USERNAME" | qrencode -t PNG -o "$pubkey_qrcode"

    { echo '        <div class="chip">';
      echo "          <a href=\"userprofile_${USERNAME}.html\">";
      echo '            <img src="images/admin_users.png" alt="Person" width="96" height="96">';
      echo "            $USERNAME";
      echo '          </a>';
      echo '        </div>'; } >> "$users_file"

    cp "$webadmin_install_dir"/userprofile.html "$userfile"
    sed -i "s|USERNAME|${USERNAME}|g" "$userfile"
    if [[ "$USERNAME" == "$webadmin_user" ]]; then
        sed -i "s|USERTYPE|Admin|g" "$userfile"
        sed -i '/remove user button/,/end of remove/d' "$userfile"
    else
        sed -i "s|USERTYPE|User|g" "$userfile"
    fi
    sed -i "s|USEREMAIL|${useremail}|g" "$userfile"
    sed -i "s|USERGPG|${GPG_ID}|g" "$userfile"

    chown www-data:www-data "$userfile"
    chown www-data:www-data "$pubkey_qrcode"

    { echo '        <div class="chip">';
      echo "          <a href=\"new_user.html\">";
      echo '            <img src="images/admin_users.png" alt="Person" width="96" height="96">';
      echo '            +';
      echo '          </a>';
      echo '        </div>';
      echo '      </div>';
      echo '    </div>';
      echo '  </body>';
      echo '</html>'; } >> "$users_file"

    chown www-data:www-data "$users_file"
}

function wait_for_enough_entropy {
    # Wait indefinitely until enough entropy is available to
    # generate the webadmin login password
    while true
    do
        ENTROPY=$(cat /proc/sys/kernel/random/entropy_avail)
        # shellcheck disable=SC2086
        if [ $ENTROPY -gt 500 ]; then
            break
        fi
        sleep 2
    done
}

function web_admin_create_user {
    username="$1"
    password="$2"

    if [ ! "$username" ]; then
        return
    fi

    if [ ! -d "/home/$username" ]; then
        chmod 600 /etc/shadow
        chmod 600 /etc/gshadow
        useradd -m -p "$password" -s /bin/bash "$username"
        groupadd "$username"
        chmod 0000 /etc/shadow
        chmod 0000 /etc/gshadow
    else
        echo "${username}:${password}"|chpasswd
    fi
}

function web_admin_setup_login {
    if [ $install_state -ne $INSTALL_STATE_FIRST_BOOT ]; then
        return
    fi

    # if an nginx password file has not been created for web admin
    if [ -f /etc/nginx/.webadminpasswd ]; then
        return
    fi

    # this file indicates that the setup screen is active
    # and gets removed by freedombone-installer
    if [ ! -f "$webadmin_install_dir/.setupscreenactive" ]; then
        touch "$webadmin_install_dir/.setupscreenactive"
    fi

    if [ ! -f $webadmin_temp_password_file ]; then
        wait_for_enough_entropy
        webadmin_password="$(openssl rand -base64 32 | tr -dc A-Za-z0-9 | head -c 10 ; echo -n '')"
        echo -n "$webadmin_password" > $webadmin_temp_password_file
    else
        webadmin_password=$(cat $webadmin_temp_password_file)
    fi

    web_admin_create_user "$webadmin_user" "$webadmin_password"

    # create a password for users
    if [ ! -f /etc/nginx/.webadminpasswd ]; then
        touch /etc/nginx/.webadminpasswd
    fi

    # create a password file used by nginx
    echo -n "$webadmin_password" | htpasswd -i -s -c /etc/nginx/.webadminpasswd "$webadmin_user"
    if ! grep -q "${webadmin_user}:" /etc/nginx/.webadminpasswd; then
        echo $"/etc/nginx/.webadminpasswd password not created for $webadmin_user"
        if [ -f /etc/nginx/.webadminpasswd ]; then
            rm /etc/nginx/.webadminpasswd
        fi
        return
    fi

    # create a setup page with the initial password inserted
    # and copy it to the index
    cp "$webadmin_install_dir"/setup.html "$webadmin_install_dir"/setup.prev
    cp "$webadmin_install_dir"/setup_confirm_template.html "$webadmin_install_dir"/setup_confirm.html
    sed -i "s|WEBADMINPASSWORD|${webadmin_password}|g" "$webadmin_install_dir/setup.prev"
    sed -i "s|WEBADMINPASSWORD|${webadmin_password}|g" "$webadmin_install_dir/setup_confirm.html"
    cp "$webadmin_install_dir"/setup.prev "$webadmin_install_dir"/index.html

    # if initial setup has not yet happened then create
    # a password file
    if ! grep -q 'install_final' "$COMPLETION_FILE"; then
        echo -n "$webadmin_password" > /root/login.txt
    fi

    echo -n "$INSTALL_STATE_WEBADMIN_PASSWORD_CTEATED" > "$install_state_file"
    install_state=$INSTALL_STATE_WEBADMIN_PASSWORD_CTEATED
}

function enable_webadmin_login {
    # switch on nginx authentication for freedombone.local
    if grep -q "#auth_basic" "/etc/nginx/sites-available/${local_hostname}"; then
        sed -i 's|#auth_basic|auth_basic|g' "/etc/nginx/sites-available/${local_hostname}"
    fi
}

function remove_initial_setup_page {
    if [ -f "$webadmin_install_dir/index.prev" ]; then
        curr_language=$(web_admin_get_language_subdir)

        # Replace the installing screen with the main index page when done
        # It should reload by itself
        if [ -f "$webadmin_install_dir/setup_installing.html" ]; then
            cp "$webadmin_install_dir/${curr_language}/index.html" "$webadmin_install_dir/setup_installing.html"
        fi

        # Replace the setup confirm screen with the main index
        if [ -f "$webadmin_install_dir/setup_confirm.html" ]; then
            cp "$webadmin_install_dir/${curr_language}/index.html" "$webadmin_install_dir/setup_confirm.html"
        fi

        # Replace the setup domain screen with the main index
        if [ -f "$webadmin_install_dir/setup_domain.html" ]; then
            cp "$webadmin_install_dir/${curr_language}/index.html" "$webadmin_install_dir/setup_domain.html"
        fi

        # Show the web admin index page (not the initial setup page)
        cp "$webadmin_install_dir/${curr_language}/index.html" "$webadmin_install_dir/index.html"
        rm "$webadmin_install_dir/index.prev"
    fi

    # Remove the initial setup page
    if [ -f "$webadmin_install_dir/setup.prev" ]; then
        rm "$webadmin_install_dir/setup.prev"
    fi
}

function remove_temporary_setup_files {
    # remove the file which indicates that the setup screen is active
    if [ -f "$webadmin_install_dir/.setupscreenactive" ]; then
        rm "$webadmin_install_dir/.setupscreenactive"
    fi

    # remove the setup file created by setup.php
    if [ -f "$setup_file" ]; then
        rm "$setup_file"
    fi

    # remove the file containing the domain name
    if [ -f "$domain_file" ]; then
        rm "$domain_file"
    fi
}

function restore_webadmin_files {
    # restore files which were removed for the initial setup
    if [ -d "$INSTALL_DIR/tempwebadmin" ]; then
        mv "$INSTALL_DIR/tempwebadmin"/* "$webadmin_install_dir"
        rm -rf "$INSTALL_DIR/tempwebadmin"
    fi
}

function set_webadmin_permissions {
    # set permissions for web admin site at freedombone.local
    chown www-data:www-data "$webadmin_install_dir/*.html"
}

function check_for_existing_processes {
    script_name="$1"

    { echo '#!/bin/bash';
      echo '';
      echo 'function exit_if_process_is_running {';
      echo "    process_name=\"\$1\"";
      echo "    process_exists=\$(ps a | grep \"\$process_name\" | grep -v 'grep')";
      echo "    if [ \"\$process_exists\" ]; then";
      echo "        if [[ \"\$process_exists\" != *'grep'* ]]; then";
      echo "            process_id=\$(echo \"\$process_exists\" | awk -F ' ' '{print \$1}')";
      echo "            if [ \"\$process_id\" ]; then";
      echo "                exit 0";
      echo '            fi';
      echo '        fi';
      echo '    fi';
      echo '}';
      echo '';
      echo 'exit_if_process_is_running fbone_installer.sh';
      echo 'exit_if_process_is_running webadmin_format.sh';
      echo 'exit_if_process_is_running webadmin_backup.sh';
      echo 'exit_if_process_is_running webadmin_backup_keys.sh';
      echo 'exit_if_process_is_running webadmin_restore.sh';
      echo 'exit_if_process_is_running webadmin_restore_keys.sh';
      echo 'exit_if_process_is_running webadmin_new_user.sh';
      echo 'exit_if_process_is_running webadmin_remove_user.sh';
      echo ''; } > "$script_name"
}

function run_setup_command {
    if [ $install_state -ne $INSTALL_STATE_WEBADMIN_PASSWORD_CTEATED ]; then
        return
    fi

    check_for_existing_processes $installer_script

    { echo "echo \"Beginning install \$(date)\" > $webadmin_install_dir/log.txt";
      echo "chown www-data:www-data $webadmin_install_dir/log.txt";
      echo '';
      echo "/usr/local/bin/freedombone -c \"$CONFIGURATION_FILE\" >> $webadmin_install_dir/log.txt;";

      echo "if ! grep -q 'install_final' \"$COMPLETION_FILE\"; then";
      echo "    chown www-data:www-data $webadmin_install_dir/log.txt";
      echo "    echo '$INSTALL_STATE_COMMAND_FAIL' > $install_state_file";
      echo '    exit 364873';
      echo 'fi';
      echo '';
      echo "chown www-data:www-data $webadmin_install_dir/log.txt";
      echo '';
      echo "exit 0"; } >> $installer_script
    chmod +x $installer_script

    echo -n "$INSTALL_STATE_RUNNING_COMMAND" > "$install_state_file"
    install_state=$INSTALL_STATE_RUNNING_COMMAND

    # run in a separate process
    ./$installer_script &
}

function check_install_command_state {
    if [ $install_state -ne $INSTALL_STATE_RUNNING_COMMAND ]; then
        return
    fi
    reset_install_state=
    if [ ! -f "$install_state_file" ]; then
        reset_install_state=1
    else
        curr_state=$(cat "$install_state_file")

        # shellcheck disable=SC2086
        if [ $curr_state -eq $INSTALL_STATE_COMMAND_FAIL ]; then
            reset_install_state=1
        fi

        install_has_finished=

        # shellcheck disable=SC2086
        if [ $curr_state -eq $INSTALL_STATE_COMMAND_SUCCESS ]; then
            install_has_finished=1
        else
            # Also check the install log for a message
            # indicating completion has happened
            if [ -f "$webadmin_install_dir/log.txt" ]; then
                end_of_log=$(tail -n 10 "$webadmin_install_dir/log.txt" | grep ${PROJECT_NAME})
                end_of_log_message=$'installation is complete'
                if [[ "$end_of_log" == *"$end_of_log_message"* ]]; then
                    install_has_finished=1
                fi
            fi
        fi

        if [ $install_has_finished ]; then
            echo -n "$INSTALL_STATE_COMMAND_SUCCESS" > "$install_state_file"
            install_state=$INSTALL_STATE_COMMAND_SUCCESS
            restore_webadmin_files
        fi
    fi

    if [ $reset_install_state ]; then
        chown www-data:www-data "$webadmin_install_dir/log.txt"
        # remove the setup file containing login details
        if [ -f "$setup_file" ]; then
            rm "$setup_file"
        fi
        # remove the webadmin password file
        if [ -f /etc/nginx/.webadminpasswd ]; then
            rm /etc/nginx/.webadminpasswd
        fi
        echo -n "$INSTALL_STATE_FIRST_BOOT" > "$install_state_file"
        install_state=$INSTALL_STATE_FIRST_BOOT
    fi
}

function web_admin_get_language_subdir {
    if grep -q 'DEFAULT_LANGUAGE=' "$CONFIGURATION_FILE"; then
        DEFAULT_LANGUAGE=$(grep 'DEFAULT_LANGUAGE=' "$CONFIGURATION_FILE" | head -n 1 | awk -F '=' '{print $2}')
    fi
    if [ ! "$DEFAULT_LANGUAGE" ]; then
        echo 'EN'
        return
    fi
    lang_lower="${DEFAULT_LANGUAGE:0:2}"
    echo "$lang_lower" | awk '{print toupper($0)}'
}

function before_setup_runs {
    if [ $install_state -ne $INSTALL_STATE_WEBADMIN_PASSWORD_CTEATED ]; then
        return
    fi

    if [ ! -f /etc/nginx/.webadminpasswd ]; then
        return
    fi

    if grep -q 'install_final' "$COMPLETION_FILE"; then
        return
    fi

    curr_language=$(web_admin_get_language_subdir)

    if [ ! -f "/usr/share/${PROJECT_NAME}/webadmin/${curr_language}/setup_installing.html" ]; then
        return
    fi

    cp "/usr/share/${PROJECT_NAME}/webadmin/${curr_language}/setup_installing.html" "$webadmin_install_dir/index.html"

    # get the username and domain from the .temp_setup.txt file
    # created by setup.php
    MY_USERNAME=$(cat "$setup_file")
    if [ ! "$MY_USERNAME" ]; then
        MY_USERNAME="$webadmin_user"
    else
        if [[ "$MY_USERNAME" != "$webadmin_user" ]]; then
            temp_webadmin_password=$(cat $webadmin_temp_password_file)
            web_admin_create_user "$webadmin_user" "$temp_webadmin_password"
            webadmin_user="$MY_USERNAME"
        fi
    fi
    DEFAULT_DOMAIN_NAME=${local_hostname}

    if [ -f "$domain_file" ]; then
        DEFAULT_DOMAIN_NAME=$(cat "$domain_file")
    fi

    # change the username in the config file
    if grep -q 'MY_USERNAME=' "$CONFIGURATION_FILE"; then
        if ! grep -Fxq "MY_USERNAME=$MY_USERNAME" "$CONFIGURATION_FILE"; then
            sed -i "s|MY_USERNAME=.*|MY_USERNAME=$MY_USERNAME|g" "$CONFIGURATION_FILE"
        fi
    else
        echo "MY_USERNAME=$MY_USERNAME" >> "$CONFIGURATION_FILE"
    fi

    # change the full name in the config file
    if grep -q 'MY_NAME=' "$CONFIGURATION_FILE"; then
        if ! grep -Fxq "MY_NAME=$MY_USERNAME" "$CONFIGURATION_FILE"; then
            sed -i "s|MY_NAME=.*|MY_USERNAME=$MY_USERNAME|g" "$CONFIGURATION_FILE"
        fi
    else
        echo "MY_NAME=$MY_USERNAME" >> "$CONFIGURATION_FILE"
    fi

    # change the default domain in the config file
    if grep -q 'DEFAULT_DOMAIN_NAME=' "$CONFIGURATION_FILE"; then
        if ! grep -Fxq "DEFAULT_DOMAIN_NAME=$DEFAULT_DOMAIN_NAME" "$CONFIGURATION_FILE"; then
            sed -i "s|DEFAULT_DOMAIN_NAME=.*|DEFAULT_DOMAIN_NAME=$DEFAULT_DOMAIN_NAME|g" "$CONFIGURATION_FILE"
        fi
    else
        echo "DEFAULT_DOMAIN_NAME=$DEFAULT_DOMAIN_NAME" >> "$CONFIGURATION_FILE"
    fi

    # ensure that minimal install is set
    if grep -q 'MINIMAL_INSTALL=' "$CONFIGURATION_FILE"; then
        if ! grep -Fxq "MINIMAL_INSTALL=yes" "$CONFIGURATION_FILE"; then
            sed -i 's|MINIMAL_INSTALL=.*|MINIMAL_INSTALL=yes|g' "$CONFIGURATION_FILE"
        fi
    else
        echo 'MINIMAL_INSTALL=yes' >> "$CONFIGURATION_FILE"
    fi

    # ensure that no DDNS provider is specified
    if grep -q 'DDNS_PROVIDER=' "$CONFIGURATION_FILE"; then
        if ! grep -Fxq "DDNS_PROVIDER=none" "$CONFIGURATION_FILE"; then
            sed -i 's|DDNS_PROVIDER=.*|DDNS_PROVIDER=none|g' "$CONFIGURATION_FILE"
        fi
    else
        echo 'DDNS_PROVIDER=none' >> "$CONFIGURATION_FILE"
    fi

    run_setup_command
}

function after_setup_has_finished {
    if [ $install_state -ne $INSTALL_STATE_COMMAND_SUCCESS ]; then
        return
    fi
    if [ -f /root/.initial_setup ]; then
        rm /root/.initial_setup
    fi
    enable_webadmin_login
    remove_initial_setup_page
    set_webadmin_permissions
    remove_temporary_setup_files

    if [ -f $webadmin_temp_password_file ]; then
        rm $webadmin_temp_password_file
    fi

    web_admin_onion_only
    web_admin_create_users
    web_admin_create_installed_apps
    web_admin_create_add_apps
    webadmin_update_version

    # remove default user
    if [ -d /home/fbone ]; then
        userdel -r fbone
        if [ -d /home/fbone ]; then
            rm -rf /home/fbone
        fi
    fi

    systemctl restart nginx
    if [ -f $installer_script ]; then
        rm $installer_script
    fi

    regenerate_blocklist

    systemctl reboot -i
}

function install_apps_from_webadmin {
    if [ -f "$pending_installs" ]; then
        linestr=$(head -n 1 "$pending_installs")
        if [[ "$linestr" == "install_"* ]]; then
            app_name=$(echo "$linestr" | awk -F '_' '{print $2}' | awk -F ',' '{print $1}')
            if [ -f "/usr/share/${PROJECT_NAME}/apps/${PROJECT_NAME}-app-${app_name}" ]; then
                app_domain=$(echo "$linestr" | awk -F ',' '{print $2}')
                if [ ! "$app_domain" ]; then
                    app_domain=${app_name}.local
                fi
                app_name_upper=$(echo "$app_name" | awk '{print toupper($0)}')
                freedns_code=$(echo -n "$linestr" | awk -F ',' '{print $3}')

                # indicate that we are installing
                if [[ "$linestr" != *'_running'* ]]; then
                    sed -i "s|${app_name}|${app_name}_running|g" "$pending_installs"
                fi

                # Add domain name to the config
                if ! grep -q "${app_name_upper}_DOMAIN_NAME=" $CONFIGURATION_FILE; then
                    echo "${app_name_upper}_DOMAIN_NAME=${app_domain}" >> $CONFIGURATION_FILE
                else
                    sed -i "s|${app_name_upper}_DOMAIN_NAME=.*|${app_name_upper}_DOMAIN_NAME=${app_domain}|g" $CONFIGURATION_FILE
                fi

                if grep -q 'ONION_ONLY=no' $CONFIGURATION_FILE; then
                    # Add freedns code to the config
                    if [ "$freedns_code" ]; then
                        # shellcheck disable=SC2086
                        ${app_name_upper}_CODE="${freedns_code}"

                        if ! grep -q "${app_name_upper}_CODE=" $CONFIGURATION_FILE; then
                            echo "${app_name_upper}_CODE=${freedns_code}" >> $CONFIGURATION_FILE
                        else
                            sed -i "s|${app_name_upper}_CODE=.*|${app_name_upper}_CODE=${freedns_code}|g" $CONFIGURATION_FILE
                        fi
                    fi
                fi

                # remove app from the removed file
                if ! grep -Fxq "_${app_name}_" "$REMOVED_APPS_FILE"; then
                    sed -i "/_${app_name}_/d" "$REMOVED_APPS_FILE"
                fi

                if ! /usr/local/bin/${PROJECT_NAME}-addremove add "${app_name}" > "$webadmin_install_dir/applog.txt"; then
                    sed -i "/$linestr/d" "$pending_installs"
                    web_admin_create_add_apps
                fi

                chown www-data:www-data "$webadmin_install_dir/applog.txt"
            fi
            # remove the line
            sed -i "/$linestr/d" "$pending_installs"
        else
            # if any unusual line is found then remove the file
            rm "$pending_installs"
        fi
    fi
}

function remove_apps_from_webadmin {
    if [ -f "$pending_removes" ]; then
        linestr=$(head -n 1 "$pending_removes")
        if [[ "$linestr" == "remove_"* ]]; then
            app_name=$(echo "$linestr" | awk -F '_' '{print $2}')
            if [ -f "/usr/share/${PROJECT_NAME}/apps/${PROJECT_NAME}-app-${app_name}" ]; then
                # indicate that we are removing
                if [[ "$linestr" != *'_running'* ]]; then
                    sed -i "s|${app_name}|${app_name}_running|g" "$pending_removes"
                fi

                /usr/local/bin/${PROJECT_NAME}-addremove remove "${app_name}" > "$webadmin_install_dir/applog.txt"
                web_admin_create_add_apps
                chown www-data:www-data "$webadmin_install_dir/applog.txt"
            fi
            # remove the line
            sed -i "/$linestr/d" "$pending_removes"
        else
            # if any unusual line is found then remove the file
            rm "$pending_removes"
        fi
    fi
}

function update_progress_bar {
    # shellcheck disable=SC2086
    if [ $install_state -ne $INSTALL_STATE_RUNNING_COMMAND ]; then
        return
    fi
    if [ ! -f /root/.install_counter ]; then
        echo -n '1' > /root/.install_counter
    fi
    max_counter=273
    installing_page="$webadmin_install_dir/setup_installing.html"
    progress_counter=$(cat /root/.install_counter)
    progress_percent=$((progress_counter * 100 / max_counter))
    if [ $progress_percent -gt 100 ]; then
        progress_percent=100
    fi
    sed -i "s|<div class=\"w3-container.*|<div class=\"w3-container w3-blue w3-round-xlarge\" style=\"width:${progress_percent}%\">${progress_percent}%</div>|g" "$installing_page"
    if ! grep -q 'w3-container w3-blue' "$webadmin_install_dir/index.html"; then
        cp "$webadmin_install_dir/setup_installing.html" "$webadmin_install_dir/index.html"
    fi
    sed -i "s|<div class=\"w3-container.*|<div class=\"w3-container w3-blue w3-round-xlarge\" style=\"width:${progress_percent}%\">${progress_percent}%</div>|g" "$webadmin_install_dir/index.html"
}

function initiate_automatic_backup {
    backup_hour=3
    if grep -q "BACKUP_HOUR=" "$CONFIGURATION_FILE"; then
        backup_hour=$(grep "BACKUP_HOUR=" "$CONFIGURATION_FILE" | awk -F '=' '{print $2}')
    else
        echo "BACKUP_HOUR=$backup_hour" >> "$CONFIGURATION_FILE"
    fi

    curr_hour=$(date +%H)
    if [[ "$curr_hour" == "$backup_hour" ]]; then
        if [[ "$webadmin_prev_hour" != "$backup_hour" ]]; then
            touch "$backup_file"
        fi
    fi
    webadmin_prev_hour=$curr_hour
}

function update_backup_progress_bar {
    if [ ! -f "$backup_progress_file" ]; then
        return
    fi

    backup_process_exists=
    backup_process=$(pgrep 'webadmin_backup.sh' | grep -v 'grep')
    if [ "$backup_process" ]; then
        if [[ "$backup_process" != *'grep'* ]]; then
            backup_pid=$(echo "$backup_process" | awk -F ' ' '{print $1}')
            if [ "$backup_pid" ]; then
                backup_process_exists=1
            fi
        fi
    fi

    if [ ! $backup_process_exists ]; then
        return
    fi

    max_counter=203
    progress_page="$webadmin_install_dir/backup_progress.html"
    progress_counter=$(cat $backup_progress_file)
    progress_percent=$((progress_counter * 100 / max_counter))
    if [ $progress_percent -gt 100 ]; then
        progress_percent=$((progress_percent - 100))
    fi
    sed -i "s|<div class=\"w3-container.*|<div class=\"w3-container w3-blue w3-round-xlarge\" style=\"width:${progress_percent}%\">${progress_percent}%</div>|g" "$progress_page"
}

function update_restore_progress_bar {
    if [ ! -f "$restore_progress_file" ]; then
        return
    fi
    restore_process_exists=
    restore_process=$(pgrep 'webadmin_restore.sh' | grep -v 'grep')
    if [ "$restore_process" ]; then
        if [[ "$restore_process" != *'grep'* ]]; then
            restore_pid=$(echo "$restore_process" | awk -F ' ' '{print $1}')
            if [ "$restore_pid" ]; then
                restore_process_exists=1
            fi
        fi
    fi

    if [ ! $restore_process_exists ]; then
        return
    fi

    max_counter=203
    progress_page="$webadmin_install_dir/restore_progress.html"
    progress_counter=$(cat $restore_progress_file)
    progress_percent=$((progress_counter * 100 / max_counter))
    if [ $progress_percent -gt 100 ]; then
        progress_percent=$((progress_percent -  100))
    fi
    sed -i "s|<div class=\"w3-container.*|<div class=\"w3-container w3-blue w3-round-xlarge\" style=\"width:${progress_percent}%\">${progress_percent}%</div>|g" "$progress_page"
}

function update_format_progress_bar {
    if [ ! -f "$format_progress_file" ]; then
        return
    fi
    format_process_exists=
    format_process=$(pgrep 'webadmin_format.sh' | grep -v 'grep')
    if [ "$format_process" ]; then
        if [[ "$format_process" != *'grep'* ]]; then
            format_pid=$(echo "$format_process" | awk -F ' ' '{print $1}')
            if [ "$format_pid" ]; then
                format_process_exists=1
            fi
        fi
    fi

    if [ ! $format_process_exists ]; then
        return
    fi

    max_counter=5
    progress_page="$webadmin_install_dir/format_progress.html"
    progress_counter=$(cat $format_progress_file)
    progress_percent=$((progress_counter * 100 / max_counter))
    if [ $progress_percent -gt 100 ]; then
        progress_percent=100
    fi
    sed -i "s|<div class=\"w3-container.*|<div class=\"w3-container w3-blue w3-round-xlarge\" style=\"width:${progress_percent}%\">${progress_percent}%</div>|g" "$progress_page"
}

function backup_and_restore {
    update_backup_progress_bar
    update_restore_progress_bar
    update_format_progress_bar

    initiate_automatic_backup

    if [ -f "$restore_file" ]; then
        rm "$restore_file"

        restore_title=$'Restore status'
        restore_failed_message=$"Restore failed. See http://${HOSTNAME}/admin/restore.txt for details"
        restore_success_message=$"Restore succeeded"

        check_for_existing_processes $restore_script
        { echo "echo \"Beginning webadmin restore \$(date)\" > $webadmin_install_dir/restore.txt";
          echo "chown www-data:www-data $webadmin_install_dir/restore.txt";
          echo '';
          echo "cp $webadmin_install_dir/restore_progress_template.html $webadmin_install_dir/restore_progress.html";
          echo "chown www-data:www-data $webadmin_install_dir/restore_progress.html";
          echo '';
          echo "if /usr/local/bin/${PROJECT_NAME}-restore-local simple >> $webadmin_install_dir/restore.txt; then";
          echo "    echo \"Completed \$(date)\" >> $webadmin_install_dir/restore.txt";
          echo "    cp $webadmin_install_dir/restore_complete.html $webadmin_install_dir/restore_progress.html";
          echo "    /usr/local/bin/${PROJECT_NAME}-notification -m \"$restore_success_message\" -s \"$restore_title\"";
          echo 'else';
          echo "    cp $webadmin_install_dir/restore_failed.html $webadmin_install_dir/restore_progress.html";
          echo "    /usr/local/bin/${PROJECT_NAME}-notification -m \"$restore_failed_message\" -s \"$restore_title\"";
          echo 'fi';
          echo '';
          echo "chown www-data:www-data $webadmin_install_dir/restore.txt";
          echo "chown www-data:www-data $webadmin_install_dir/restore_progress.html";
          echo '';
          echo "rm $restore_progress_file";
          echo '';
          echo "exit 0"; } >> $restore_script
        chmod +x $restore_script

        # run in a separate process
        ./$restore_script &
    else
        if [ -f "$backup_file" ]; then
            rm "$backup_file"

            backup_title=$'Backup status'
            backup_failed_message=$"Backup failed. See http://${HOSTNAME}/admin/backup.txt for details"
            backup_success_message=$"Backup succeeded"

            check_for_existing_processes $backup_script
            { echo "echo \"Beginning webadmin backup \$(date)\" > $webadmin_install_dir/backup.txt";
              echo "chown www-data:www-data $webadmin_install_dir/backup.txt";
              echo '';
              echo "cp $webadmin_install_dir/backup_progress_template.html $webadmin_install_dir/backup_progress.html";
              echo "chown www-data:www-data $webadmin_install_dir/backup_progress.html";
              echo '';
              echo "if /usr/local/bin/${PROJECT_NAME}-backup-local simple >> $webadmin_install_dir/backup.txt; then";
              echo "    echo \"Completed \$(date)\" >> $webadmin_install_dir/backup.txt";
              echo "    cp $webadmin_install_dir/backup_complete.html $webadmin_install_dir/backup_progress.html";
              echo "    /usr/local/bin/${PROJECT_NAME}-notification -m \"$backup_success_message\" -s \"$backup_title\"";
              echo 'else';
              echo "    cp $webadmin_install_dir/backup_failed.html $webadmin_install_dir/backup_progress.html";
              echo "    /usr/local/bin/${PROJECT_NAME}-notification -m \"$backup_failed_message\" -s \"$backup_title\"";
              echo 'fi';
              echo '';
              echo "chown www-data:www-data $webadmin_install_dir/backup.txt";
              echo "chown www-data:www-data $webadmin_install_dir/backup_progress.html";
              echo '';
              echo "rm $backup_progress_file";
              echo '';
              echo "exit 0"; } >> $backup_script
            chmod +x $backup_script

            # run in a separate process
            ./$backup_script &
        else
            if [ -f "$format_file" ]; then
                rm "$format_file"

                format_title=$'USB format status'
                format_failed_message=$"USB format failed. See http://${HOSTNAME}/admin/format.txt for details"
                format_success_message=$"USB format succeeded"

                check_for_existing_processes $format_script
                { echo "if /usr/local/bin/${PROJECT_NAME}-format simple > $webadmin_install_dir/format.txt; then";
                  echo "    cp $webadmin_install_dir/format_complete.html $webadmin_install_dir/format_progress.html";
                  echo "    /usr/local/bin/${PROJECT_NAME}-notification -m \"$format_success_message\" -s \"$format_title\"";
                  echo 'else';
                  echo "    cp $webadmin_install_dir/format_failed.html $webadmin_install_dir/format_progress.html";
                  echo "    /usr/local/bin/${PROJECT_NAME}-notification -m \"$format_failed_message\" -s \"$format_title\"";
                  echo 'fi';
                  echo "chown www-data:www-data $webadmin_install_dir/format_progress.html";
                  echo '';
                  echo "rm $format_progress_file";
                  echo '';
                  echo "exit 0"; } >> $format_script
                chmod +x $format_script

                # run in a separate process
                ./$format_script &
            fi
        fi
    fi
}

function backup_and_restore_keys {
    if [ -f "$restore_keys_file" ]; then
        rm "$restore_keys_file"
        admin_username=$(grep 'MY_USERNAME=' "$CONFIGURATION_FILE" | head -n 1 | awk -F '=' '{print $2}')
        # check_for_existing_processes $restore_keys_script
        { echo "echo \"Beginning webadmin restore keys \$(date)\" > $webadmin_install_dir/restore.txt";
          echo "chown www-data:www-data $webadmin_install_dir/restore.txt";
          echo '';
          echo "cp $webadmin_install_dir/restore_keys_progress_template.html $webadmin_install_dir/restore_keys_progress.html";
          echo "chown www-data:www-data $webadmin_install_dir/restore_keys_progress.html";
          echo '';
          echo "if /usr/local/bin/${PROJECT_NAME}-recoverkey -u \"$admin_username\" -d \"simple\" >> $webadmin_install_dir/restore.txt; then";
          echo "    echo \"Completed \$(date)\" >> $webadmin_install_dir/restore.txt";
          echo "    cp $webadmin_install_dir/restore_keys_complete.html $webadmin_install_dir/restore_keys_progress.html";
          echo 'else';
          echo "    cp $webadmin_install_dir/restore_keys_failed.html $webadmin_install_dir/restore_keys_progress.html";
          echo 'fi';
          echo '';
          echo "chown www-data:www-data $webadmin_install_dir/restore.txt";
          echo "chown www-data:www-data $webadmin_install_dir/restore_keys_progress.html";
          echo '';
          echo "exit 0"; } >> $restore_keys_script
        chmod +x $restore_keys_script

        # run in a separate process
        ./$restore_keys_script &
    else
        if [ -f "$backup_keys_file" ]; then
            rm "$backup_keys_file"
            admin_username=$(grep 'MY_USERNAME=' "$CONFIGURATION_FILE" | head -n 1 | awk -F '=' '{print $2}')
            check_for_existing_processes $backup_keys_script
            { echo "echo \"Beginning webadmin backup keys \$(date)\" > $webadmin_install_dir/backup.txt";
              echo "chown www-data:www-data $webadmin_install_dir/backup.txt";
              echo '';
              echo "cp $webadmin_install_dir/backup_keys_progress_template.html $webadmin_install_dir/backup_keys_progress.html";
              echo "chown www-data:www-data $webadmin_install_dir/backup_keys_progress.html";
              echo '';
              echo "if /usr/local/bin/${PROJECT_NAME}-keydrive -u \"$admin_username\" --master 'yes' -d \"simple\" >> $webadmin_install_dir/backup.txt; then";
              echo "    echo \"Completed \$(date)\" >> $webadmin_install_dir/backup.txt";
              echo "    cp $webadmin_install_dir/backup_keys_complete.html $webadmin_install_dir/backup_keys_progress.html";
              echo 'else';
              echo "    cp $webadmin_install_dir/backup_keys_failed.html $webadmin_install_dir/backup_keys_progress.html";
              echo 'fi';
              echo '';
              echo "chown www-data:www-data $webadmin_install_dir/backup.txt";
              echo "chown www-data:www-data $webadmin_install_dir/backup_keys_progress.html";
              echo '';
              echo "exit 0"; } >> $backup_keys_script
            chmod +x $backup_keys_script

            # run in a separate process
            ./$backup_keys_script &
        fi
    fi
}

function is_valid_user {
    USRNAME="$1"
    if [[ "$USRNAME" != "turtl" && "$USRNAME" != "znc" && "$USRNAME" != "pihole" && "$USRNAME" != "fbone" && "$USRNAME" != "go" && "$USRNAME" != "gogs" && "$USRNAME" != "git" && "$USRNAME" != "sync" && "$USRNAME" != "tahoelafs" ]]; then
        echo "1"
    else
        echo "0"
    fi
}

function valid_username_characters {
  local re='^[[:lower:]_][[:lower:][:digit:]_-]{2,15}$'
  (( ${#1} > 16 )) && return 1
  [[ $1 =~ $re ]] # return value of this comparison is used for the function
}

function add_remove_users {
    if [ -f "$new_user_file" ]; then
        new_username=$(awk -F ',' '{print $1}' < "$new_user_file")

        if [ ${#new_username} -ge 3 ]; then
            if [ ${#new_username} -le 32 ]; then
                if valid_username_characters "$new_username"; then
                    if [ ! -d "/home/$new_username" ]; then
                        if [[ $(is_valid_user "$new_username") == "1" ]]; then
                            new_password=$(awk -F ',' '{print $2}' < "$new_user_file")

                            check_for_existing_processes $new_user_script
                            { echo "/usr/local/bin/${PROJECT_NAME}-adduser $new_username password=$new_password > $webadmin_install_dir/log.txt";
                              echo "exit 0"; } >> $new_user_script
                            chmod +x $new_user_script

                            # run in a separate process
                            ./$new_user_script &

                            new_password=
                        fi
                    fi
                fi
            fi
        fi

        rm "$new_user_file"
    fi

    if [ -f "$remove_user_file" ]; then
        remove_username=$(cat "$remove_user_file")

        if [ ${#remove_username} -ge 3 ]; then
            if [ ${#remove_username} -le 32 ]; then
                if valid_username_characters "$remove_username"; then
                    if [ -d "/home/$remove_username" ]; then
                        if [[ $(is_valid_user "$remove_username") == "1" ]]; then
                            admin_username=$(grep 'MY_USERNAME=' "$CONFIGURATION_FILE" | head -n 1 | awk -F '=' '{print $2}')
                            if [[ "$remove_username" != "$admin_username" ]]; then
                                check_for_existing_processes $remove_user_script
                                { echo "/usr/local/bin/${PROJECT_NAME}-rmuser $remove_username -f > $webadmin_install_dir/log.txt";
                                  echo "exit 0"; } >> $remove_user_script
                                chmod +x $remove_user_script

                                # run in a separate process
                                ./$remove_user_script &
                            fi
                        fi
                    fi
                fi
            fi
        fi

        rm "$remove_user_file"
    fi
}

function webadmin_change_password {
    if [ ! -f "$change_password_file" ]; then
        return
    fi

    temp_password_file=$(cat "$change_password_file")
    if [ -f "$temp_password_file" ]; then
        curr_username=$(awk -F ',' '{print $1}' < "$temp_password_file")
        if [ ${#curr_username} -ge 3 ]; then
            if [[ $(is_valid_user "$curr_username") == "1" ]]; then
                if [ -d "/home/$curr_username" ]; then
                    new_password=$(awk -F ',' '{print $2}' < "$temp_password_file")

                    admin_username=$(grep 'MY_USERNAME=' "$CONFIGURATION_FILE" | head -n 1 | awk -F '=' '{print $2}')

                    # change the webadmin login
                    if [[ "$curr_username" == "$admin_username" ]]; then
                        if [ -f /etc/nginx/.webadminpasswd ]; then
                            sed -i "/${curr_username}:/d" /etc/nginx/.webadminpasswd
                        fi
                        echo -n "$new_password" | htpasswd -i -s -c /etc/nginx/.webadminpasswd "$curr_username"
                    fi

                    # change the login for the user
                    echo "${curr_username}:${new_password}"|chpasswd

                    # change passwords for each installed app
                    /usr/local/bin/${PROJECT_NAME}-chpasswd "$curr_username" "$new_password"

                    new_password=
                fi
            fi
        fi
        rm "$temp_password_file"
    fi
    rm "$change_password_file"
}

function reset_shutdown {
    if [ -f "$shutdown_file" ]; then
        rm "$shutdown_file"
        if [ -f "$reset_file" ]; then
            rm "$reset_file"
        fi
        sleep 2
        systemctl poweroff
    fi

    if [ -f "$reset_file" ]; then
        rm "$reset_file"
        if [ -f "$shutdown_file" ]; then
            rm "$shutdown_file"
        fi
        sleep 2
        systemctl reboot -i
    fi
}

function regenerate_blocklist {
    if [ ! -f "$FIREWALL_DOMAINS" ]; then
        touch "$FIREWALL_DOMAINS"
    fi
    local_hostname=$(grep 'host-name' /etc/avahi/avahi-daemon.conf | awk -F '=' '{print $2}').local
    webadmin_install_dir="/var/www/${local_hostname}/htdocs/admin"
    if [ -f "$webadmin_install_dir/blocking_template.html" ]; then
        cp "$webadmin_install_dir/blocking_template.html" "$webadmin_install_dir/blocking.html"
        blockedlist=$(sed 's@[/\&]@\\&@g;s/$/\\/' "$FIREWALL_DOMAINS"; echo .)
        blockedlist=${blockedlist%.}
        sed -i "s|BLOCKEDLIST|$blockedlist|g" "$webadmin_install_dir/blocking.html"
        chown www-data:www-data "$webadmin_install_dir/blocking.html"
    fi
}

function regenerate_dynamic_dns {
    ddns="$1"
    ddns_username="$2"
    ddns_password="$3"

    local_hostname=$(grep 'host-name' /etc/avahi/avahi-daemon.conf | awk -F '=' '{print $2}').local
    webadmin_install_dir="/var/www/${local_hostname}/htdocs/admin"
    if [ -f "$webadmin_install_dir/dynamicdns_template.html" ]; then
        cp "$webadmin_install_dir/dynamicdns_template.html" "$webadmin_install_dir/dynamicdns.html"
        if [[ "$ddns" == 'default@freedns.afraid.org' ]]; then
            ddns='freedns'
        fi
        if [[ "$ddns" == 'dyn.com' ]]; then
            ddns='dyn'
        fi
        sed -i "s|\"$ddns\"|\"$ddns\" selected|g" "$webadmin_install_dir/dynamicdns.html"
        sed -i "s|DDNS_USERNAME|$ddns_username|g" "$webadmin_install_dir/dynamicdns.html"
        # Note: For security don't populate the password
        #sed -i "s|DDNS_PASSWORD|$ddns_password|g" "$webadmin_install_dir/dynamicdns.html"
        chown www-data:www-data "$webadmin_install_dir/dynamicdns.html"
    fi
}

function update_blocklist {
    if [ -f "$blocklist_file" ]; then

        if [ -f "${FIREWALL_DOMAINS}.new" ]; then
            rm "${FIREWALL_DOMAINS}.new"
        fi
        touch "${FIREWALL_DOMAINS}.new"

        blocked_lines_ctr=0
        while read -r line; do
            if [ $blocked_lines_ctr -gt 1024 ]; then
                # This prevents someone copy-pasting a giant amount of text
                # into the blocklist. If you're legitimately blocking more
                # than this number then you probably have other problems to
                # think about and should consider whitelisting instead
                break
            fi
            blocked_lines_ctr=$((blocked_lines_ctr + 1))

            if [ "$line" ]; then
                # remove leading and trailing spaces
                newline="$(echo -e "${line}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')"
                line="$newline"
                if [[ "$line" == *'@'* ]]; then
                    # blocked user
                    # remove leading @
                    if [[ "$line" == '@'* ]]; then
                        newline=${line:1}
                        line="$newline"
                    fi
                    blocked_username=$(echo "$line" | awk -F '@' '{print $1}')
                    if valid_username_characters "$blocked_username"; then
                        blocked_domain=$(echo "$line" | awk -F '@' '{print $2}')
                        if [[ "$blocked_domain" == *'.'* ]]; then
                            if [ ${#blocked_domain} -gt 3 ]; then
                                test_domain=$(echo "$blocked_domain" | tr -dc a-z0-9.-)
                                if [[ "$blocked_domain" == "$test_domain" ]]; then
                                    blocked_address="${blocked_username}@${blocked_domain}"
                                    if ! grep -q "$blocked_address" "${FIREWALL_DOMAINS}.new"; then
                                        echo "${blocked_username}@${blocked_domain}" >> "${FIREWALL_DOMAINS}.new"
                                    fi
                                fi
                            fi
                        fi
                    fi
                else
                    # blocked domain
                    if [[ "$line" == *'.'* ]]; then
                        blocked_domain="$line"
                        test_domain=$(echo "$blocked_domain" | tr -dc a-z0-9.-)
                        if [[ "$blocked_domain" == "$test_domain" ]]; then
                            if [ ${#blocked_domain} -gt 3 ]; then
                                if ! grep -q "$blocked_domain" "${FIREWALL_DOMAINS}.new"; then
                                    echo "$blocked_domain" >> "${FIREWALL_DOMAINS}.new"
                                fi
                            fi
                        fi
                    fi
                fi
            fi
        done < "$blocklist_file"

        mv "${FIREWALL_DOMAINS}" "${FIREWALL_DOMAINS}.backup"
        mv "${FIREWALL_DOMAINS}.new" "${FIREWALL_DOMAINS}"
        rm "$blocklist_file"
        regenerate_blocklist
    fi
}

function update_dynamic_dns {
    if [ -f "$dynamic_dns_file" ]; then
        ddns=$(awk -F ',' '{print $1}' < "$dynamic_dns_file")
        ddns_username=$(awk -F ',' '{print $2}' < "$dynamic_dns_file")
        ddns_password=$(awk -F ',' '{print $3}' < "$dynamic_dns_file")

        ddns_is_valid=
        if [ "$ddns_username" ]; then
            if [ "$ddns_password" ]; then
                if [ ${#ddns_username} -gt 2 ]; then
                    if [ ${#ddns_username} -lt 512 ]; then
                        if [ ${#ddns_password} -gt 2 ]; then
                            if [ ${#ddns_password} -lt 512 ]; then
                                ddns_is_valid=1
                            fi
                        fi
                    fi
                fi
            fi
        fi

        if [ $ddns_is_valid ]; then
            /usr/local/bin/${PROJECT_NAME}-ddns "$ddns" "$ddns_username" "$ddns_password"
            regenerate_dynamic_dns "$ddns" "$ddns_username" "$ddns_password"
        fi

        rm "$dynamic_dns_file"
    fi
}

function change_language {
    if [ -f "$language_file" ]; then
        language=$(cat "$language_file")
        local_hostname=$(grep 'host-name' /etc/avahi/avahi-daemon.conf | awk -F '=' '{print $2}').local
        webadmin_install_dir="/var/www/${local_hostname}/htdocs/admin"
        if [ -f "$webadmin_install_dir/language_template.html" ]; then
            if grep -q "\"$language\"" "$webadmin_install_dir/language_template.html"; then
                cp "$webadmin_install_dir/language_template.html" "$webadmin_install_dir/language.html"
                sed -i "s|\"$language\"|\"$language\" selected|g" "$webadmin_install_dir/language.html"
                chown www-data:www-data "$webadmin_install_dir/language.html"

                config_language=$(grep "DEFAULT_LANGUAGE=" "$CONFIGURATION_FILE" | awk -F '=' '{print $2}')
                if [[ "$config_language" != "$language" ]]; then
                    curr_language=$(web_admin_get_language_subdir)
                    if [ -d "/usr/share/${PROJECT_NAME}/webadmin/${curr_language}" ]; then
                        # copy files for the new language
                        cp -r "/usr/share/${PROJECT_NAME}/webadmin/${curr_language}/*" "/usr/share/${PROJECT_NAME}/webadmin/"

                        # recreate some screens from templates
                        regenerate_blocklist
                        touch "$system_monitor_file"
                        update_system_monitor
                        webadmin_update_version
                    fi

                    chown -R www-data:www-data "$webadmin_install_dir/*"

                    sed -i "s|DEFAULT_LANGUAGE=.*|DEFAULT_LANGUAGE=$language|g" "$CONFIGURATION_FILE"
                    locale-gen "${language}"
                    update-locale LANG="${language}"
                    update-locale LANGUAGE="${language}"
                    update-locale LC_MESSAGES="${language}"
                    update-locale LC_ALL="${language}"
                    update-locale LC_CTYPE="${language}"
                fi
            fi
        fi

        rm "$language_file"
    fi
}

function update_system_monitor {
    if [ -f "$system_monitor_file" ]; then
        rm "$system_monitor_file"

        disk_use=$(df -h | grep /dev/sda1)
        disk_use_percent=0
        if [[ "$disk_use" == *'/dev/sda1'* ]]; then
            disk_use_percent=$(df -h | grep /dev/sda1 | awk -F ' ' '{print $5}')
        else
            disk_use_percent=$(df -h | grep /dev/mmcblk0p1 | awk -F ' ' '{print $5}')
        fi

        used_memory=$(free -m | grep "Mem:" | awk -F ' ' '{print $3}')
        total_memory=$(free -m | grep "Mem:" | awk -F ' ' '{print $2}')
        used_memory_percent=$((used_memory * 100 / total_memory))

        cpu_temperature=0
        cpu_temp=$(cat /sys/class/thermal/thermal_zone*/temp | head -n 1)
        if [ "$cpu_temp" ]; then
            cpu_temperature=$((cpu_temp / 1000))
        fi

        test_drive=/dev/sda1
        if ! ls $test_drive; then
            if ls /dev/mmcblk0p2; then
                test_drive=/dev/mmcblk0p2
            else
                test_drive=
            fi
        fi
        if [ "$test_drive" ]; then
            disk_io_speed_percent=0
            disk_io_speed=$(hdparm -t $test_drive | awk -F '= ' '{print $2}' | awk -F ' ' '{print $1}')
            if [ "$disk_io_speed" ]; then
                disk_io_speed_percent=$((disk_io_speed * 100 / 500))
                if [ $disk_io_speed_percent -gt 100 ]; then
                    disk_io_speed_percent=100
                fi
            fi
        fi

        local_hostname=$(grep 'host-name' /etc/avahi/avahi-daemon.conf | awk -F '=' '{print $2}').local
        webadmin_install_dir="/var/www/${local_hostname}/htdocs/admin"
        if [ -f "$webadmin_install_dir/system_monitor_template.html" ]; then
            cp "$webadmin_install_dir/system_monitor_template.html" "$webadmin_install_dir/system_monitor.html"

            if [[ "$disk_use_percent" == *'%'* ]]; then
                sed -i "s|<div name=\"diskspace\" class=\"w3-container.*|<div name=\"diskspace\" class=\"w3-container w3-blue w3-round-xlarge\" style=\"width:$disk_use_percent\">$disk_use_percent</div>|g" "$webadmin_install_dir/system_monitor.html"
            fi

            sed -i "s|<div name=\"memory\" class=\"w3-container.*|<div name=\"memory\" class=\"w3-container w3-blue w3-round-xlarge\" style=\"width:${used_memory_percent}%\">${used_memory_percent}%</div>|g" "$webadmin_install_dir/system_monitor.html"

            sed -i "s|<div name=\"cputemp\" class=\"w3-container.*|<div name=\"cputemp\" class=\"w3-container w3-blue w3-round-xlarge\" style=\"width:${cpu_temperature}%\">${cpu_temperature}C</div>|g" "$webadmin_install_dir/system_monitor.html"

            sed -i "s|<div name=\"diskio\" class=\"w3-container.*|<div name=\"diskio\" class=\"w3-container w3-blue w3-round-xlarge\" style=\"width:${disk_io_speed_percent}%\">${disk_io_speed} MB/s</div>|g" "$webadmin_install_dir/system_monitor.html"

            chown www-data:www-data "$webadmin_install_dir/system_monitor.html"
        fi
    fi
}

# If the freedombone command is already running then kill its process
#shellcheck disable=SC2009
install_process=$(ps a | grep '/usr/local/bin/freedombone -c' | grep -v 'grep')
if [ "$install_process" ]; then
    if [[ "$install_process" != *'grep'* ]]; then
        install_pid=$(echo "$install_process" | awk -F ' ' '{print $1}')
        if [ "$install_pid" ]; then
            pkill -9 "$install_pid"
        fi
    fi
fi

# get the current install state
if [ -f "$install_state_file" ]; then
    install_state=$(cat "$install_state_file")
    # shellcheck disable=SC2086
    if [ $install_state -ne $INSTALL_STATE_COMMAND_SUCCESS ]; then
        if grep -q 'install_final' "$COMPLETION_FILE"; then
            install_state=$INSTALL_STATE_COMMAND_SUCCESS
        else
            chown www-data:www-data "$webadmin_install_dir/log.txt"
            # remove the setup file containing login details
            if [ -f "$setup_file" ]; then
                rm "$setup_file"
            fi
            if [ -f /etc/nginx/.webadminpasswd ]; then
                rm /etc/nginx/.webadminpasswd
            fi
            install_state=$INSTALL_STATE_FIRST_BOOT
        fi
        echo -n "$install_state" > "$install_state_file"
    fi
else
    if grep -q 'install_final' "$COMPLETION_FILE"; then
        install_state=$INSTALL_STATE_COMMAND_SUCCESS
    else
        install_state=$INSTALL_STATE_FIRST_BOOT
    fi
    echo -n "$install_state" > "$install_state_file"
fi

while true
do
    if [ -f /tmp/.upgrading ]; then
        sleep 2
    else
        web_admin_setup_login
        if [ -d "$webadmin_install_dir" ]; then
            if [ -f "$setup_file" ]; then
                update_progress_bar
                before_setup_runs
                check_install_command_state
                after_setup_has_finished
                sleep 1
            else
                if [ $install_state -eq $INSTALL_STATE_COMMAND_SUCCESS ]; then
                    reset_shutdown
                    webadmin_change_password
                    update_blocklist
                    update_dynamic_dns
                    update_system_monitor
                    change_language
                    backup_and_restore
                    backup_and_restore_keys
                    add_remove_users
                    install_apps_from_webadmin
                    sleep 1
                    remove_apps_from_webadmin
                fi
            fi
        fi
        sleep 1
    fi
done

exit 0