#!/usr/bin/env bash
#
# GNU/Linux does not really require something like RelativeLink.c
# However, we do want to have the same look and feel with similar features.
#
# Copyright 2017 The Tor Project.  See LICENSE for licensing information.

complain_dialog_title="Tor Browser"

# Make sure this script wasn't started as 'sh start-tor-browser' or similar.
if [ "x$BASH" = "x" ]; then
  echo "$complain_dialog_title should be started as './start-tor-browser'"
  echo "Exiting." >&2
  exit 1;
fi

# Do not (try to) connect to the session manager
unset SESSION_MANAGER

# Complain about an error, by any means necessary.
# Usage: complain message
# message must not begin with a dash.
complain () {
  # Trim leading newlines, to avoid breaking formatting in some dialogs.
  complain_message="`echo "$1" | sed '/./,$!d'`"

  # If we're being run in debug/verbose mode, complain to stderr.
  if [ "$show_output" -eq 1 ]; then
    echo "$complain_message" >&2
    return
  fi

  # Otherwise, we're being run by a GUI program of some sort;
  # try to pop up a message in the GUI in the nicest way
  # possible.
  #
  # In mksh, non-existent commands return 127; I'll assume all
  # other shells set the same exit code if they can't run a
  # command.  (xmessage returns 1 if the user clicks the WM
  # close button, so we do need to look at the exact exit code,
  # not just assume the command failed to display a message if
  # it returns non-zero.)

  # First, try zenity.
  zenity --error \
         --title="$complain_dialog_title" \
         --text="$complain_message"
  if [ "$?" -ne 127 ]; then
    return
  fi

  # Try kdialog.
  kdialog --title "$complain_dialog_title" \
          --error "$complain_message"
  if [ "$?" -ne 127 ]; then
    return
  fi

  # Try xmessage.
  xmessage -title "$complain_dialog_title" \
           -center \
           -buttons OK \
           -default OK \
           -xrm '*message.scrollVertical: Never' \
           "$complain_message"
  if [ "$?" -ne 127 ]; then
    return
  fi

  # Try gxmessage.  This one isn't installed by default on
  # Debian with the default GNOME installation, so it seems to
  # be the least likely program to have available, but it might
  # be used by one of the 'lightweight' Gtk-based desktop
  # environments.
  gxmessage -title "$complain_dialog_title" \
            -center \
            -buttons GTK_STOCK_OK \
            -default OK \
            "$complain_message"
  if [ "$?" -ne 127 ]; then
    return
  fi
}

if [ "`id -u`" -eq 0 ]; then
  complain "The Tor Browser should not be run as root.  Exiting."
  exit 1
fi

if test -r /proc/cpuinfo && ! grep -q '^flags\s*:.* sse2' /proc/cpuinfo; then
  complain "Tor Browser requires a CPU with SSE2 support.  Exiting."
  exit 1
fi

print_usage () {
  printf "\nTor Browser Script Options\n"
  printf "  --verbose         Display Tor and the browser output in the terminal\n"
  printf "  --log [file]      Record Tor and the browser output in file (default: tor-browser.log)\n"
  printf "  --detach          Detach from terminal and run Tor Browser in the background.\n"
  if test -z "$system_install"; then
    printf "  --register-app    Register Tor Browser as a desktop app for this user\n"
    printf "  --unregister-app  Unregister Tor Browser as a desktop app for this user\n"
  fi
}
log_output=0
show_output=0
detach=0
show_usage=0
register_desktop_app=0
logfile=/dev/null
while :
do
  case "$1" in
    --detach)
      detach=1
      shift
      ;;
    -v | --verbose | -d | --debug)
      show_output=1
      verbose_arg="$2"
      shift
      ;;
    -h | "-?" | --help | -help)
      show_usage=1
      show_output=1
      shift
      ;;
    -l | --log)
      if [ -z "$2" -o "${2:0:1}" == "-" ]; then
        printf "Logging Tor Browser debug information to tor-browser.log\n"
        logfile="../tor-browser.log"
      elif [ "${2:0:1}" == "/" -o "${2:0:1}" == "~" ]; then
        printf "Logging Tor Browser debug information to %s\n" "$2"
        logfile="$2"
        shift
      else
        printf "Logging Tor Browser debug information to %s\n" "$2"
        logfile="../$2"
        shift
      fi
      log_output=1
      shift
      ;;
    --register-app)
      register_desktop_app=1
      show_output=1
      shift
      ;;
    --unregister-app)
      register_desktop_app=-1
      show_output=1
      shift
      ;;
    *) # No more options
      break
      ;;
  esac
done

# We can't detach and show output at the same time..
if [ "$show_output" -eq 1 -a "$detach" -eq 1 ]; then
  detach=0
fi

if [ "$show_output" -eq 0 ]; then
  # If the user hasn't requested 'debug mode' or --help, close stdout and stderr,
  # to keep Firefox and the stuff loaded by/for it (including the
  # system's shared-library loader) from printing messages to
  # $HOME/.xsession-errors or other files. (Users wouldn't have seen
  # messages there anyway.)
  exec > "$logfile"
  exec 2> "$logfile"
fi

# If XAUTHORITY is unset, set it to its default value of $HOME/.Xauthority
# before we change HOME below.  (See xauth(1) and #1945.)  XDM and KDM rely
# on applications using this default value.
if [ -z "$XAUTHORITY" ]; then
  XAUTHORITY=~/.Xauthority
  export XAUTHORITY
fi

# If this script is being run through a symlink, we need to know where
# in the filesystem the script itself is, not where the symlink is.
myname="$0"
if [ -L "$myname" ]; then
  # XXX readlink is not POSIX, but is present in GNU coreutils
  # and on FreeBSD.  Unfortunately, the -f option (which follows
  # a whole chain of symlinks until it reaches a non-symlink
  # path name) is a GNUism, so we have to have a fallback for
  # FreeBSD.  Fortunately, FreeBSD has realpath instead;
  # unfortunately, that's also non-POSIX and is not present in
  # GNU coreutils.
  #
  # If this launcher were a C program, we could just use the
  # realpath function, which *is* POSIX.  Too bad POSIX didn't
  # make that function accessible to shell scripts.

  # If realpath is available, use it; it Does The Right Thing.
  possibly_my_real_name="`realpath "$myname" 2>/dev/null`"
  if [ "$?" -eq 0 ]; then
    myname="$possibly_my_real_name"
  else
    # realpath is not available; hopefully readlink -f works.
    myname="`readlink -f "$myname" 2>/dev/null`"
    if [ "$?" -ne 0 ]; then
      # Ugh.
      complain "start-tor-browser cannot be run using a symlink on this operating system."
    fi
  fi
fi

cd "$(dirname "$myname")"
browser_dir="$(pwd)"
if test -f "$browser_dir/is-packaged-app"; then
  system_install=1
  browser_home="$HOME/.tor-browser-alpha"
  mkdir -p "$browser_home"
  cd "$browser_home"
else
  browser_home="$browser_dir"
fi

# If ${PWD} results in a zero length string, we can try something else...
if [ ! "${PWD}" ]; then
  # "hacking around some braindamage"
  PWD="`pwd`"
  surveysays="This system has a messed up shell.\n"
fi

# This is a fix for an ibus issue on some Linux systems. See #9353 for more
# details. The symlink needs to be created before we change HOME.
if [ ! -d ".config/ibus" ]; then
  mkdir -p .config/ibus
  ln -nsf ~/.config/ibus/bus .config/ibus
fi

if test -z "$system_install"; then
  # Fix up .desktop Icon and Exec Paths, and update the .desktop file from the
  # canonical version if it was changed by the updater.
  cp start-tor-browser.desktop ../
  sed -i -e "s,^Name=.*,Name=Tor Browser Alpha,g" ../start-tor-browser.desktop
  sed -i -e "s,^Icon=.*,Icon=$PWD/browser/chrome/icons/default/default128.png,g" ../start-tor-browser.desktop
  sed -i -e "s,^Icon=.*,Icon=$PWD/browser/chrome/icons/default/default128.png,g" start-tor-browser.desktop
  sed -i -e "s,^Exec=.*,Exec=sh -c '\"$PWD/start-tor-browser\" || ([ !  -x \"$PWD/start-tor-browser\" ] \&\& \"\$(dirname \"\$*\")\"/Browser/start-tor-browser)' dummy %k,g" ../start-tor-browser.desktop

  if [ "$register_desktop_app" -eq 1 ]; then
    mkdir -p "$HOME/.local/share/applications/"
    cp ../start-tor-browser.desktop "$HOME/.local/share/applications/"
    update-desktop-database "$HOME/.local/share/applications/"
    printf "Tor Browser has been registered as a desktop app for this user in ~/.local/share/applications/\n"
    exit 0
  fi

  if [ "$register_desktop_app" -eq -1 ]; then
    if [ -e "$HOME/.local/share/applications/start-tor-browser.desktop" ]; then
      rm -f "$HOME/.local/share/applications/start-tor-browser.desktop"
      update-desktop-database "$HOME/.local/share/applications/"
      printf "Tor Browser has been removed as a user desktop app (from ~/.local/share/applications/)\n"
    else
      printf "Tor Browser does not appear to be a desktop app (not present in ~/.local/share/applications/)\n"
    fi
    exit 0
  fi
fi

export BB_ORIGINAL_HOME="$HOME"
HOME="$browser_home"
export HOME

# Prevent disk leaks in $HOME/.local/share (tor-browser#17560)
function erase_leaky() {
  local leaky="$1"
  [ -e "$leaky" ] &&
    ( srm -r "$leaky" ||
      wipe -r "$leaky" ||
      find "$leaky" -type f -exec shred -u {} \; ;
      rm -rf "$leaky"
    ) > /dev/null 2>&1
}
local_dir="$HOME/.local/"
share_dir="$local_dir/share"
# We don't want to mess with symlinks, possibly pointing outside the
# Browser directory (tor-browser-build#41050).
# We're not using realpath/readlink for consistency with the (possibly
# outdated) availability assumptions made elsewhere in this script.
if ! [ -L "$local_dir" -o -L "$share_dir" ]; then
  if [ -d "$share_dir" ]; then
    for leaky_path in "gvfs-metadata" "recently-used.xbel"; do
      erase_leaky "$share_dir/$leaky_path"
    done
  else
    mkdir -p "$local_dir"
  fi
  ln -fs /dev/null "$share_dir"
fi
[ -L "$HOME/.cache" ] || erase_leaky "$HOME/.cache/nvidia"

SYSARCHITECTURE=$(getconf LONG_BIT)
TORARCHITECTURE=$(expr "$(file TorBrowser/Tor/tor)" : '.*ELF \([[:digit:]]*\)')

if [ $SYSARCHITECTURE -ne $TORARCHITECTURE ]; then
  complain "Wrong architecture? 32-bit vs. 64-bit."
  exit 1
fi


function setControlPortPasswd() {
  local ctrlPasswd=$1

  if test -z "$ctrlPasswd" -o "$ctrlPasswd" = $'\"secret\"' ; then
    unset TOR_CONTROL_PASSWD
    return
  fi

  if test "${ctrlPasswd:0:1}" = $'\"'; then  # First 2 chars were '"
    printf "Using system Tor process.\n"
    export TOR_CONTROL_PASSWD
  else
    complain "There seems to have been a quoting problem with your \
TOR_CONTROL_PASSWD environment variable."
    echo "The Tor ControlPort password should be given inside double"
    echo "quotes, inside single quotes. That is, if the ControlPort"
    echo 'password is “secret” (without curly quotes) then we must'
    echo "start this script after setting the environment variable"
    echo "exactly like this:"
    echo
    echo "  \$ TOR_CONTROL_PASSWD='\"secret\"' $myname"
  fi
}

# Using a system-installed Tor process with Tor Browser:
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# The Tor ControlPort password should be given inside double quotes, inside
# single quotes, i.e. if the ControlPort password is “secret” (without
# curly quotes) then we must set the environment variable *exactly* like
# this:
#
# TOR_CONTROL_PASSWD='"secret"'
#
# Yes, the variable MUST be double-quoted, then single-quoted, exactly as
# shown. This is used by TorButton and Tor Launcher to authenticate to Tor's
# ControlPort, and is necessary for using TB with a system-installed Tor.
#
# Additionally, if using a system-installed Tor, the following about:config
# options should be set (values in <> mean they are the value taken from your
# torrc):
#
# SETTING NAME                            VALUE
# network.proxy.socks                     127.0.0.1
# network.proxy.socks_port                <SocksPort>
# extensions.torbutton.inserted_button    true
# extensions.torbutton.launch_warning     false
# extensions.torbutton.loglevel           2
# extensions.torbutton.logmethod          0
# extensions.torlauncher.control_port      <ControlPort>
# extensions.torlauncher.loglevel          2
# extensions.torlauncher.logmethod         0
# extensions.torlauncher.prompt_at_startup false
# extensions.torlauncher.start_tor         false
#
# where the '[...]' in the banned_ports option means "leave anything that was
# already in the preference alone, just append the things specified after it".

# Either set `TOR_CONTROL_PASSWD` before running ./start-tor-browser, or put
# your password in the following line where the word “secret” is:
setControlPortPasswd ${TOR_CONTROL_PASSWD:='"secret"'}

# Avoid overwriting user's dconf values. Fixes #27903.
export GSETTINGS_BACKEND=memory

# tor-browser-build#41017: Nvidia drivers create a shader cache by default in
# $HOME/.cache/nvidia. We we can easily disable it.
export __GL_SHADER_DISK_CACHE=0

cd "$browser_dir"

if [ "$show_usage" -eq 1 ]; then
  # Display Firefox help, then our help
  TOR_CONTROL_PASSWD=${TOR_CONTROL_PASSWD} ./firefox --help 2>/dev/null
  print_usage
elif [ "$detach" -eq 1 ] ; then
  TOR_CONTROL_PASSWD=${TOR_CONTROL_PASSWD} ./firefox "${@}" > "$logfile" 2>&1 </dev/null &
  disown "$!"
elif [ "$log_output" -eq 1 -a "$show_output" -eq 1 ]; then
  TOR_CONTROL_PASSWD=${TOR_CONTROL_PASSWD} ./firefox "${@}" 2>&1 </dev/null | \
    tee "$logfile"
elif [ "$show_output" -eq 1 ]; then
  TOR_CONTROL_PASSWD=${TOR_CONTROL_PASSWD} ./firefox "${@}" < /dev/null
else
  TOR_CONTROL_PASSWD=${TOR_CONTROL_PASSWD} ./firefox "${@}" > "$logfile" 2>&1 </dev/null
fi

exit $?
