#! /bin/bash

#----------------------------------------------------------------------
# Description: the official Linux x86-64 Firefox distribution updater
#              Allows you to update Firefox from command line/CLI/terminal
#
# Author: Artem S. Tashkinov
# Created at: Wed Dec 20 13:50 2017
#             2019-05-29 15:08:05 Firefox 68 fix?
#             2022-12-17 01:47:32 Firefox 108 fix
# Computer: localhost.localdomain
# System: Linux 4.14.3-300.fc27.x86_64 on x86_64
#
# Copyright (c) 2017-2022 Artem S. Tashkinov  All rights reserved.
#----------------------------------------------------------------------

# Comment out to disable or set the number to add an iptables rule for the OUTPUT chain
# firewall=6

diewithmsg()
{
    echo "ERROR: $1"
    exit 1
}

sanitychecker()
{
    test -z "$1" && diewithmsg "Empty version string. Perhaps this script needs to be updated or wget failed."
    result=`echo "$1" | sed 's/\.//g;s/b//;s/[0-9]//g;s/esr//'`
    test -n "$result" && diewithmsg "The version string [$1] is wrong."
}

normalize()
{
    # Add .0 for XX.0 releases, i.e. 60.0 becomes 60.0.0; remove esr; remove beta; awk: 60.1.2 -> 60 000 000 + 1000 + 2
    # will fail to upgrade beta to release
    echo "$1" | sed 's/^[0-9]*\.0$/\0.0/;s/esr//;s/b/\./' | awk -F "." '{print $1*1000000+$2*1000+$3}'
}

extractversion()
{
    # Firefox 108+ fix
    datasrc=modules/AppConstants.jsm
    unzip -p "$1/omni.ja" "$datasrc" &> /dev/null || datasrc=modules/AppConstants.sys.mjs
    unzip -p "$1/omni.ja" "$datasrc" 2>/dev/null | egrep "MOZ_APP_VERSION_DISPLAY|MOZ_UPDATE_CHANNEL|INSTALL_LOCALE" | sed 's/: /=/;s/,//'
    # Firefox 68 fix
    flocale=`unzip -p "$1/omni.ja" "$datasrc" 2>/dev/null | grep INSTALL_LOCALE`
    test -z "$flocale" && echo "INSTALL_LOCALE=`unzip -p "$1/omni.ja" update.locale 2>/dev/null`"
}

fetch()
{
    wget --progress=dot:giga -O update.mar "$1"
}

unzip -v &> /dev/null || diewithmsg "Please install unzip first!"
wget  -V &> /dev/null || diewithmsg "Please install wget first!"
bc    -v &> /dev/null || diewithmsg "Please install bc first!"

test -z "$2" && diewithmsg "Usage is: `basename "$0"` user_for_update firefox_installation_directory forced_new_version[optional]"
grep -qw "$1" /etc/passwd || diewithmsg "Unable to find the user '$1'"
test -f "$2/firefox" || diewithmsg "'$2' is not a valid Firefox installation directory"

id=`id -u` || diewithmsg "id failed!"

if [ "$id" == "0" ]; then
    if [ -n "$firewall" ]; then
        if [ -z "`iptables-save | grep uid-owner\ $1`" ]; then
            echo -n "Adding iptables rule number $firewall for the user $1 ... "
            iptables -I OUTPUT "$firewall" -p tcp -m owner --uid-owner "$1" -j ACCEPT && echo OK || diewithmsg "Failed"
        else
            echo "An iptables rule is already active"
        fi
    fi

    # Change files/directories ownership unconditionally
    echo -n "Applying permissions $1:$1 for '$2' ... "
    chown -Rc "$1:$1" "$2" && echo OK || diewithmsg "This mustn't happen."

    echo -n "Dropping root privileges ... "
    sudo -u "$1" "$0" "$1" "$2" "$3"
    exit $?
fi

echo "OK"
echo "Running as user '`id -un`'."

source <(extractversion "$2")
versionhave="$MOZ_APP_VERSION_DISPLAY"

echo "Locale: $INSTALL_LOCALE"

if [ "$MOZ_UPDATE_CHANNEL" == "beta" ]; then
    echo "Selected the BETA update channel"
    channel="-beta"
elif [ "$MOZ_UPDATE_CHANNEL" == "esr" ]; then
    echo "Selected the ESR update channel"
    channel="-esr"
    versionhave="${versionhave}esr"
else
    echo "Selected the STABLE update channel"
    channel=""
fi

echo -n "Installed version: "
sanitychecker "$versionhave"
echo "$versionhave"

echo -n "Available version: "
versionnext="$3"
if [ -z "$3" ]; then
    versionnext=`wget --spider --timeout=5 "https://download.mozilla.org/?product=firefox$channel-latest&os=linux64&lang=$INSTALL_LOCALE" 2>&1 | grep Location | sed 's/.*releases\/\(.*\)\/linux.*/\1/'`
fi
sanitychecker "$versionnext"
echo "$versionnext"

test "$versionhave" == "$versionnext" && echo "You're up to date. Exiting." && exit 0

norm_vershave=`normalize "$versionhave"`
norm_versnext=`normalize "$versionnext"`
# bc: the result of all boolean operations are 0 and 1 (for false and true) as in relational expressions.
test "`echo "$norm_vershave > $norm_versnext" | bc -l`" -eq 1 && diewithmsg "The installed version is newer than the available update! Perhaps you've updated manually."

workdir=`mktemp -d`
test -d "$workdir" || diewithmsg "Unable to create a working directory"
cd "$workdir" || diewithmsg "Unable to enter a working directory"

url_base="https://ftp.mozilla.org/pub/firefox/releases/$versionnext/update/linux-x86_64/$INSTALL_LOCALE"
upd_incr="firefox-$versionhave-$versionnext.partial.mar"
upd_full="firefox-$versionnext.complete.mar"
url_incr="$url_base/$upd_incr"
url_full="$url_base/$upd_full"
echo -e "\nTrying to download delta from $versionhave to $versionnext ... "
if ! fetch "$url_incr"; then
    echo "Trying to download full update to $versionnext ... "
    fetch "$url_full" || diewithmsg "Cannot download neither update"
fi

echo "Applying update ..."
LD_LIBRARY_PATH="$2" "$2/updater" "$workdir" "$2" "$2"

echo -n "Cleaning up ... "
rm -rf "$workdir" && echo OK

echo "All done"