#!/bin/bash
#
# Initialization script for using Firefox in "kiosk" mode.
#
# Description:
#   This script changes the relevant files in the Firefox install dirs to
#   disable any user actions that would provide access to the local filesystem,
#   the printer, the Internet in general, or browser settings.  The idea is to
#   harden the browser to protect against intentional or accidental user
#   tampering.  This script saves the original files as well and provides an
#   option to replace them, restoring the full-function browser.
#
# Change Activity:
#   08/09/2005 Jim Hennessy     Initial version
#   08/25/2006 Jim Hennessy     Enhanced to support multiple versions of Firefox
#   08/30/2006 Jim Hennessy     Make different updates for different versions of Firefox
#
#
#STARTUSAGE
#
# Usage:
#   firefoxKiosk [-r] [-f firefox-install-dir] [-a] HWMCAHOME-directory
#
# Where:
#   -r   Restore the Firefox installation.  That is, instead of modifying the
#        Firefox files to create a kiosk mode, restore them to their original
#        versions, restoring a full-function Firefox browser.
#
#   -f   Provides the location of the Firefox install dir (e.g., /usr/firefox-x.x.x.x).
#        This is useful when running this script for testing, since the
#        automatic detection of Firefox's install dir is geared towards MCP
#        systems.
#
#   -a   Use alternate backup directory.  The location of the backup directory
#        used by this script has changed.  This option will revert back to the
#        old directory, which is useful if the Firefox installation was put
#        into kiosk mode using an old version of this script, and now you want
#        to restore it using a current version.  This option may only be
#        specified in conjunction with the -r option.
#
#   HWMCAHOME-directory
#        When run as an initialization script, this argument is provided, and
#        refers to the root directory of the HMC.  It is used to locate the
#        files that are used to manipulate the Firefox installation.  This
#        argument is required.
#
# This script manipulates the Firefox install to either introduce or remove
# restrictions that provide a "kiosk" mode browser.
#
#ENDUSAGE

# The getFirefoxLibDir function is also copied (mostly) in the "browserjail" script
function getFirefoxLibDir() {
   if [[ -n "$firefoxInstallDir" ]]; then
      if [[ -r "$firefoxInstallDir/chrome/browser.jar" ]]; then
         echo "$firefoxInstallDir"
      else
         echo "The specific directory $firefoxInstallDir does not seem to be a firefox library dir" >&2
      fi
      return
   fi

   # Find Firefox launching shell script
   local scriptName=$(which firefox)
   if [[ -z "$scriptName" ]]; then
      # Not in the PATH, apparently.  Check likely locations for driver machine
      set -- $(ls -d /usr/lib/firefox*)
      if [[ $# == 0 ]]; then
         set -- $(ls -d /opt/MozillaFirefox-*)
         if [[ $# == 0 ]]; then
            echo "Unable to deduce Firefox install dir.  Can't find a" >&2
            echo "firefox install under /usr/lib or /opt" >&2
            return

         elif [[ $# > 1 ]]; then
            echo "Unable to deduce Firefox install dir.  More than one" >&2
            echo "version seems to be installed under /opt" >&2
            return
         fi

      elif [[ $# > 1 ]]; then
         echo "Unable to deduce Firefox install dir.  More than one" >&2
         echo "version seems to be installed under /usr/lib" >&2
         return
      fi

      local scriptNamePossibilies="$1/bin/firefox.sh $1/firefox.sh $1/bin/firefox $1/firefox"
      local scriptNameTest
      for scriptNameTest in $scriptNamePossibilies; do
         if [[ -r "$scriptNameTest" ]]; then
            scriptName="$scriptNameTest"
            break
         fi
      done

      if [[ ! -r "$scriptName" ]]; then
         echo "Can't find Firefox launcher file: $scriptName" >&2
         return
      fi
   fi

   # We know the script name.  Find the install dir.
   local libDir=$(cat "$scriptName"|grep 'MOZ_DIST_LIB='|sed -r -e 's/MOZ_DIST_LIB="(.*)"\s*/\1/')
   if [[ -z "$libDir" ]]; then
      libDir=$(cat "$scriptName"|grep 'MOZ_DIST_BIN='|sed -r -e 's/MOZ_DIST_BIN="(.*)"\s*/\1/')
   fi

   [[ -n "$libDir" && -e "$libDir/chrome/browser.jar" ]] && echo "$libDir"
}

function getFirefoxVersion() {
   if [[ ! -r "$firefoxLib/firefox-bin" ]]; then
      echo "Cannot deduce Firefox version; $firefoxLib/firefox-bin not found." >&2
      return
   fi

   # The filenames in the md5sum files are unqualified, so we must cd to the right dir
   local currentDir=$(pwd)
   cd "$firefoxLib"

   # Check out all the (visible) directories immediately under $firefoxData
   local testDir
   for testDir in $(find "$firefoxData" -mindepth 1 -maxdepth 1 -type d '!' -name '.*'); do
      # See if the firefox-bin executable is the version this subdir expects
      md5sum --check --status "$testDir/md5sum"
      if [[ "$?" == "0" ]]; then
         # The saved hash matches the actual file, so we have a match
         cd "$currentDir"
         echo $(basename "$testDir")
         return
      fi
   done

   cd "$currentDir"
}

function readKioskInstructions() {
   if [[ ! -r "$firefoxData/kiosk.instructions" ]]; then
      echo "The file $firefoxData/kiosk.instructions is missing." >&2
      exit 7
   fi

   cat "$firefoxData/kiosk.instructions"|grep -vE '^#|^ *$'
}

function kioskOn() {
   # Create a temporary directory to manipulate the contents of the jar files.
   workdir="/tmp/firefoxKiosk.$$"
   mkdir "$workdir" || exit 4

   # Create backup directory to hold the originals
   mkdir -p $BACKUP_DIR || exit 4

   # Read and process the kiosk instructions
   readKioskInstructions|while IFS=':' read destination sourceFile; do
      if [[ -z "${destination##*!*}" ]]; then
         # The destination is a jar file.
         local jarPath=$(echo "$destination"|cut -d'!' -f1)
         local pathInJar=$(echo "$destination"|cut -d'!' -f2)
         local jarName=$(basename "$jarPath")

         # If this is the first instruction referring to this jar file, unpack
         # the jar file now.
         if [[ ! -e "$workdir/$jarName" ]]; then
            # Only save backup if we haven't already.  We don't want to overwrite
            # the original.
            if [[ ! -e "$BACKUP_DIR/$jarPath" ]]; then
               echo "Saving backup of $firefoxLib/$jarPath"
               mkdir -p $(dirname "$BACKUP_DIR/$jarPath") || exit 4
               cp -p "$firefoxLib/$jarPath" "$BACKUP_DIR/$jarPath" || exit 4
            fi

            mkdir -p "$workdir/$jarName" || exit 4
            cd "$workdir/$jarName"
            unzip -q "$firefoxLib/$jarPath" || exit 4
         else
            cd "$workdir/$jarName"
         fi

         if diff -q "$firefoxData/$sourceFile" "$pathInJar/" &>/dev/null; then
            echo "The file $sourceFile in $(dirname $jarPath) is already the kiosk version."
         else
            echo "Updating file $sourceFile in $jarPath"
            cp "$firefoxData/$sourceFile" "$pathInJar/" || exit 5

            # Indicate that this jar file has been updated and should be replaced
            [[ ! -e "hmcReplaceJarFlag" ]] &&
               echo "$jarPath" > "hmcReplaceJarFlag"
         fi

         # Go back to starting directory
         cd - >/dev/null

         # The updated jar file will be created at the end of instruction processing
      else
         # The destination is a regular file.

         # Only save backup if we haven't already.  We don't want to overwrite
         # the original.
         if [[ ! -e "$BACKUP_DIR/$destination" ]]; then
            echo "Saving backup of $firefoxLib/$destination"
            mkdir -p $(dirname "$BACKUP_DIR/$destination") || exit 4
            cp -p "$firefoxLib/$destination" "$BACKUP_DIR/$destination" || exit 4
         fi

         if diff -q "$firefoxLib/$destination" "$firefoxData/$sourceFile" &>/dev/null; then
            echo "The file $destination is already the kiosk version."
         else
            echo "Updating file $destination"
            cp "$firefoxData/$sourceFile" "$destination" || exit 5
         fi
      fi
   done

   # Now replace all the updated jar files
   local jarDir
   for jarDir in $(find "$workdir" -mindepth 1 -maxdepth 1 -type d '!' -name '.*'); do
      # If no changes were made in the jar contents, don't update the jar file
      [[ ! -r "$jarDir/hmcReplaceJarFlag" ]] && continue

      cd "$jarDir"
      local jarPath=$(cat "hmcReplaceJarFlag")
      rm "hmcReplaceJarFlag"

      echo "Updating $firefoxLib/$jarPath..."
      zip -rq0 "$firefoxLib/$jarPath" * || exit 6
      cd - >/dev/null
   done

   # Cleanup the work dir
   rm -rf "$workdir" 2>/dev/null
}

function kioskOff() {
   # Create a temporary directory to keep track of our restore
   workdir="/tmp/firefoxKiosk.$$"
   mkdir "$workdir" || exit 4

   # Read and process the kiosk instructions
   readKioskInstructions|while IFS=':' read destination sourceFile; do
      if [[ -z "${destination##*!*}" ]]; then
         # The destination is a jar file.
         local jarPath=$(echo "$destination"|cut -d'!' -f1)
         local jarName=$(basename "$jarPath")

         # If we've already restored this jar file, skip it this time
         [[ -e "$workdir/$jarName" ]] && continue

         # Don't process this jar file again
         touch "$workdir/$jarName"

         if [[ ! -e "$BACKUP_DIR/$jarPath" ]]; then
            echo "Unable to restore Firefox installation.  Original version of $jarPath don't exist in directory $(dirname $BACKUP_DIR/$jarPath)" >&2
            continue
         fi

         echo "Restoring $firefoxLib/$jarPath..."
         cp -p "$BACKUP_DIR/$jarPath" "$firefoxLib/$jarPath" || exit 4

      else
         # The destination is a regular file.

         # Only save backup if we haven't already.  We don't want to overwrite
         # the original.
         if [[ ! -e "$BACKUP_DIR/$destination" ]]; then
            echo "Unable to restore Firefox installation.  Original version of $destination don't exist in directory $(dirname $BACKUP_DIR/$destination)" >&2
            continue
         fi

         echo "Restoring $firefoxLib/$destination..."
         cp -p "$BACKUP_DIR/$destination" "$firefoxLib/$destination" || exit 4
      fi
   done

   # Cleanup the work dir
   rm -rf "$workdir" 2>/dev/null
}

# # # # Start of main script # # # #

restore=0
firefoxInstallDir=
useAlternateBackup=0
giveUsage=0

# Parse the options
while getopts 'rf:a?' optname; do
   case "$optname" in
      r) restore=1;;
      f) firefoxInstallDir="$OPTARG";;
      a) useAlternateBackup=1;;
      \?) giveUsage=1; break;;
   esac
done

if [ "$giveUsage" -eq 1 ]; then
   # Print out the prologue comments as usage info
   sed -e '/STARTUSAGE/,/ENDUSAGE/ s/^#//' -e '1,/STARTUSAGE/ d' -e '/ENDUSAGE/,$ d' "$0"
   exit 0
fi

if [[ "$useAlternateBackup" == 1 && "$restore" == 0 ]]; then
   echo "The -a option may only be specified when the -r option is also specified." >&2
   exit 1
fi

# First positional parameter is at index OPTIND.  Shift so that these parms
# are easily accessible to following code.
if [ $OPTIND -ne 1 ]; then
   shift $(($OPTIND-1))
fi

if [[ -z "$1" ]]; then
   echo "The first argument must be the HWMCAHOME dir" >&2
   exit 2
fi
HWMCAHOME="$1"

if [[ $(id -u) != "0" ]]; then
   echo "You must have root authority to run this script" >&2
   exit 1
fi

# Figure out where Firefox is installed.
firefoxLib=$(getFirefoxLibDir)
if [[ -z "$firefoxLib" ]]; then
   echo "Unable to deduce location of Firefox package." >&2
   exit
fi

# Include the common hmc functions
if [[ -r "$HWMCAHOME/hmcfunctions" ]]; then
   . "$HWMCAHOME/hmcfunctions"
elif [[ -r "$HWMCAHOME/native/scripts/hmcfunctions" ]]; then
   . "$HWMCAHOME/native/scripts/hmcfunctions"
else
   echo "The first argument does not appear to name a proper directory.  It must be the HWMCAHOME dir" >&2
   echo "Can't find hmcfunctions script." >&2
   exit 2
fi

# queryFileLocation requires that CONSOLE_PATH be set (with a trailing slash)
if [[ -z "$CONSOLE_PATH" ]]; then
   CONSOLE_PATH="${HWMCAHOME%/}/"
fi

# Get the location where the entire collection of Firefox replacement files live
firefoxData=$(queryFileLocation "firefoxKiosk")
if [[ -z "$firefoxData" ]]; then
   echo "Unable to resolve location of Firefox kiosk replacement files." >&2
   exit 3
fi

# Make sure firefoxData looks like the right directory
if [[ ! -r "$firefoxData/kiosk.instructions.README" ]]; then
   echo "The directory $firefoxData does not appear to contain the kiosk replacement files." >&2
   exit 3
fi

# Figure out what version of Firefox we must deal with
version=$(getFirefoxVersion)
if [[ -z "$version" ]]; then
   echo "There are no kiosk replacement files for the version of Firefox in $firefoxLib." >&2
   exit 4
fi

echo "Processing Firefox version $version in $firefoxLib"

# Here's where the data files for this particular version of Firefox are
firefoxData="$firefoxData/$version"
BACKUP_DIR="$firefoxLib/hmcOriginalNonKioskFiles/$version"

[[ "$useAlternateBackup" == 1 ]] && BACKUP_DIR=/home/hmcmanager/.firefoxKiosk

if [[ "$restore" == "0" ]]; then
  kioskOn "$@";
else
  kioskOff "$@";
fi
