#!/bin/bash
# This script installs the FollowMe print queue at NTNU on Linux and possibly Mac systems.
# The targeted and tested distros are: Debian, Ubuntu (and derivates), Fedora, CentOS, OpenSUSE and Mint
# Copyright © 2017-2019 einar.haraldseid@ntnu.no

# LibreOffice has some problems on Linux, see https://bugs.documentfoundation.org/show_bug.cgi?id=126604

# Documentation
function usage {
  echo "Usage: ./$(basename "${0}") [OPTIONS]"
  echo "Options:"
  echo " -m, --model {ricoh|generic}     Printer model to install (default: generic)"
  echo " -d, --driver {pcl|postscript}   Printer driver to use (default: postscript)"
  echo " -f, --force                     Force running script as if on Linux systems"
  echo " -p, --plaintext                 Store credentials as plaintext in /etc/cups/printers.conf (Linux only)"
  echo " -h, --help                      Display this help text"
}

# Print errors to STDERR
function printerror() {
  echo "${*}" 1>&2
}

# Set default options that may be overridden by passed options below
Model="generic"
Driver="postscript"

# Set other default options
PrintServer="followprint.win.ntnu.no"
PrintFile="ntnuprint-ricoh"
Workgroup="WIN-NTNU-NO"
QueueName="FollowMe"

while [[ $# -gt 0 ]]; do
  Key="${1}"

  case ${Key} in
    -m|--model)
      Model=$(echo "${2}" | tr "[:upper:]" "[:lower:]")
      if [ "${Model}" != "ricoh" ] && [ "${Model}" != "generic" ]; then
        printerror "Unknown model ${Model}, please choose one of ricoh or generic"
        exit 1
      fi
      shift # Jump to next argument
      ;;
    -f|--force)
      Force="YES"
      shift # Jump to next argument
      ;;
    -d|--driver)
      Driver=$(echo "${2}" | tr "[:upper:]" "[:lower:]")
      if [ "${Driver}" != "postscript" ] && [ "${Driver}" != "pcl" ]; then
        printerror "Unknown driver ${Driver}, plase choose one of postscript or pcl"
        exit 1
      fi
      shift # Jump to next argument
      ;;
    -p|--plaintext)
      Plaintext="YES"
      shift # Jump to next argument
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      # Unknown argument
      echo "Unknown argument: ${Key}"
      exit 1
      ;;
  esac

  shift # past argument or value
done

# Make sure we have sudo powers when we need it
if ! sudo -k; then
  printerror "This script requires sudo to function"
  exit 1
fi

# But don't run as super user, otherwise we can't access the keyring
if [ ${UID} -eq 0 ]; then
  if [ "${Plaintext}" != "YES" ]; then
    printerror "Please run this script as your normal user, we will prompt you for your sudo password when needed. Or you can use the --plaintext option"
    exit 1
  fi
else
  echo "Please provide sudo password, as some parts of this script requires it"
  if ! sudo true; then
    printerror "Sorry, this script requires working sudo privileges to function"
    exit 1
  fi
fi

# Test for supported OS
Uname=$(uname | tr "[:upper:]" "[:lower:]")
if [ "${Uname}" != "darwin" ] && [ "${Uname}" != "linux" ] && [ "${Force}" != "YES" ]; then
  printerror "I don't think I can run on ${Uname}, but if you insist, please use -f to force Linux detection. This may not work, though."
  exit 1
fi

# Set Uname to linux if we are forcing running the script
if [ "${Force}" = "YES" ]; then
  Uname="linux"
fi

# Set printer driver path based on driver (we will override these if we're specific distros
if [ "${Driver}" = "postscript" ]; then
  DriverPath="drv:///sample.drv/generic.ppd"
else
  DriverPath="drv:///sample.drv/generpcl.ppd"
fi

# Test for CUPS
if ! sudo bash -c "command -v lpadmin" > /dev/null 2>&1; then
  printerror "You must have CUPS installed to add printers. Please install CUPS."
  exit 1
fi

# Tests for Linux systems
if [ "${Uname}" = "linux" ]; then
  # Test if smbclient is installed
  if ! command -v smbclient >/dev/null 2>&1; then
    printerror "You must have smbclient installed to print to NTNU followprint. Please install smbclient."
    exit 1
  fi

  # Test if secret-tool is installed
  if ! command -v secret-tool >/dev/null 2>&1; then
    printerror "You must have secret-tool installed for this script to work. Please install libsecret-tools (Debian-systems) or secret-tool (Fedora/CentOS)."
    exit 1
  fi

  # Try to determine if the necessary printer drivers are installed, and set correct driver paths

  # This should match OpenSUSE, and probably SUSE Enterprise, and other derivates that use zypper as the primary package manager
  if command -v zypper >/dev/null 2>&1; then
    if [ "${Driver}" = "postscript" ]; then
      if [ "${Model}" = "ricoh" ]; then
        if ! sudo rpm -q OpenPrintingPPDs-postscript >/dev/null 2>&1; then
          printerror "You don't seem to have the correct printer drivers installed, please run:"
          printerror "  sudo zypper install OpenPrintingPPDs-postscript"
          printerror "first, or use the generic model instead."
          exit 1
        else
          DriverPath="OpenPrintingPPDs/postscript/Ricoh-MP_C6003.Postscript-Ricoh.ppd.gz"
        fi
      else
        DriverPath="Postscript.ppd.gz"
      fi
    else
      if ! sudo rpm -q OpenPrintingPPDs-ghostscript >/dev/null 2>&1; then
        printerror "You don't seem to have the correct printer drivers installed, please run:"
        printerror "  sudo zypper install OpenPrintingPPDs-ghostscript"
        printerror "first, or use the generic model instead."
        exit 1
      else
        if [ "${Model}" = "ricoh" ]; then
          DriverPath="OpenPrintingPPDs/ghostscript/Ricoh-MP_C6003.pxlcolor-Ricoh.ppd.gz"
        else
          DriverPath="OpenPrintingPPDs/ghostscript/Generic-PCL_6_PCL_XL_Printer.pxlcolor.ppd.gz"
        fi
      fi
    fi
  else
    # The rest of the systems can use drv:/// paths for the generic drivers, but we need to detect various distros for advice on installing Ricoh drivers
    if [ "${Model}" = "ricoh" ]; then
      # This should match Fedora and other modern rpm based systems that have dnf as the primary package manager
      if command -v dnf >/dev/null 2>&1; then
        if ! sudo rpm -q foomatic-db-ppds > /dev/null 2>&1; then
          printerror "You don't seem to have the correct printer drivers installed, please run:"
          printerror "  sudo dnf install foomatic-db-ppds"
          printerror "first, or use the generic model instead."
          exit 1
        else
          if [ "${Driver}" = "postscript" ]; then
            DriverPath="foomatic-db-ppds/Ricoh/PS/Ricoh-MP_C6003_PS.ppd.gz"
          else
            DriverPath="foomatic-db-ppds/Ricoh/PXL/Ricoh-MP_C6003_PXL.ppd.gz"
          fi
        fi
      # This should match CentOS, RHEL and other RHEL based distros that have yum as the primary package manager
      elif command -v yum >/dev/null 2>&1; then
        if ! sudo rpm -q foomatic-filters > /dev/null 2>&1; then
          printerror "You don't seem to have the foomatic-filters package installed, please run:"
          printerror "  sudo yum install foomatic-filters"
          printerror "first, or use the generic model instead."
          exit 1
        fi
        if ! sudo rpm -q foomatic-db-ppds > /dev/null 2>&1; then
          printerror "You don't seem to have the correct printer drivers installed, please run:"
          printerror "  sudo yum install foomatic-db-ppds"
          printerror "first, or use the generic model instead."
          exit 1
        else
          if [ "${Driver}" = "postscript" ]; then
            DriverPath="foomatic-db-ppds/Ricoh/PS/Ricoh-MP_C6003_PS.ppd.gz"
          else
            DriverPath="foomatic-db-ppds/Ricoh/PXL/Ricoh-MP_C6003_PXL.ppd.gz"
          fi
        fi
      fi
      # This should match Debian, Ubuntu and most if not all derivates that use dpkg as the primary package manager
      if command -v dpkg >/dev/null 2>&1; then
        if ! sudo dpkg -s openprinting-ppds > /dev/null 2>&1; then
          printerror "You must have the correct printer drivers installed, please run:"
          printerror "  sudo apt-get install openprinting-ppds"
          printerror "first, or use the generic model instead."
          exit 1
        else 
          if [ "${Driver}" = "postscript" ]; then
            DriverPath="openprinting-ppds:0/ppd/openprinting/Ricoh/PS/Ricoh-MP_C6003_PS.ppd"
          else
            DriverPath="openprinting-ppds:0/ppd/openprinting/Ricoh/PXL/Ricoh-MP_C6003_PXL.ppd"
          fi
        fi
      fi
    fi
  fi
fi

echo "This script will add a new printer called ${QueueName}, connecting to the
print server ${PrintServer} using your user name and password from NTNU."

# Get username and password
printf "NTNU User name: "
read -r Username
printf "NTNU Password: "
Settings=$(stty -g)
stty -echo
read -r Password
stty "${Settings}"

echo ""

# Some further tests for Linux systems
if [ "${Uname}" = "linux" ]; then
  # Test for valid username and password
  # Bonus: find out if the print share is actually available
  PrintServerIP=$(getent ahostsv4 ${PrintServer} | head -n 1 | cut -d " " -f 1)
  if ! smbclient -U "${Workgroup}/${Username}%${Password}" -L "//${PrintServer}" -I "${PrintServerIP}" > /dev/null 2>&1; then
    printerror "User name or password incorrect, or no contact with the print server ${PrintServer}."
    exit 1
  fi
  ShareFound=$(smbclient -U "${Workgroup}/${Username}%${Password}" -L "//${PrintServer}" -I "${PrintServerIP}" -g 2>/dev/null | grep ${PrintFile} | cut -d "|" -f 1)
  if [ "${ShareFound}" != "Printer" ]; then
    printerror "Could not find printer share called ${PrintFile} on the server" 
    printerror "This script must be broken or outdated. Please contact orakel@ntnu.no for further assistance."
    exit 1
  fi
fi

# Similar tests for OSX
if [ "${Uname}" = "darwin" ]; then
  if ! smbutil view -A "//${Workgroup};${Username}:${Password}@${PrintServer}" > /dev/null 2>&1; then
    printerror "User name or password incorrect, or no contact with the print server ${PrintServer}."
    exit 1
  fi
  ShareFound=$(smbutil view "//${Workgroup};${Username}:${Password}@${PrintServer}" 2>/dev/null | grep ${PrintFile} | cut -d " " -f 1)
  if [ "${ShareFound}" != "${PrintFile}" ]; then
    printerror "Could not find printer share called ${PrintFile} on the server"
    printerror "This script must be broken or outdated. Please contact orakel@ntnu.no for further assistance."
    exit 1
  fi
fi

# Finally we can add the printer, let's remove any existing printer share with the same name first
sudo lpadmin -x ${QueueName} > /dev/null 2>&1

# The Linux way
if [ "${Uname}" = "linux" ]; then
  if [ "${Plaintext}" = "YES" ]; then
    PrinterShare="smb://${Workgroup}/${Username}:${Password}@${PrintServer}/${PrintFile}"
    AuthInfo="none"
    echo -e "\nNOTE: Your credentials will be stored in plaintext in /etc/cups/printers.conf.\nThis is usually only necessary on headless systems or on systems that don't run a dbus-daemon and/or a keyring that can provide the org.freedesktop.secrets service.\nNeedless to say, this is not a good idea on multi-user systems.\n"
  else
    PrinterShare="smb://${Workgroup}/${PrintServer}/${PrintFile}"
    AuthInfo="username,password"
    echo -e "\nNOTE: Due to the way credentials are stored and accessed on Linux, some print operations will still halt for credentials, notably the \"Print test page\" function and printing from LibreOffice. In those cases, supply your normal NTNU username and password. If this becomes too tedious, you can try using the --plaintext option.\n"
  fi
  if ! sudo lpadmin  -p ${QueueName} \
   -D "FollowMe print queue at NTNU" \
   -L "Many locations" \
   -v "${PrinterShare}" \
   -m "${DriverPath}" \
   -o auth-info-required="$AuthInfo" \
   -u allow:all -E; then
    printerror "Could not connect to printer share. See above error for details."
    exit 1
  fi
  if [ "${Plaintext}" != "YES" ]; then
    # Add credentials to the keyring
    if ! echo -n "${Password}" | secret-tool store \
     --label "ipp://localhost:631/printers/$QueueName" \
     uri "ipp://localhost:631/printers/${QueueName}" \
     user "${Username}"; then
      printerror "Could not store credentials to the keyring, see above error for details. A workaround can be to use the option --plaintext"
    fi
  fi
fi

# The OSX way
if [ "${Uname}" = "darwin" ]; then
  if ! sudo lpadmin -p ${QueueName} \
   -D "FollowMe print queue at NTNU" \
   -L "Many locations" \
   -v "smb://${PrintServer}/${PrintFile}" \
   -m "${DriverPath}" \
   -o printer-is-shared=false -o printer-op-policy=authenticated \
   -u allow:all -E; then
    printerror "Could not connect to printer share. See above error for details."
    exit 1
  fi

  # Add credentials to the keychain if they are missing
  # Shamelessly stolen^W^WBorrowed from https://github.com/Orakeltjenesten/scripts/blob/33abfb353524f449f0bbdee27adb2f1f0a9756a2/print/ntnuprint-mac.sh
  security -v add-internet-password -U -a "${Workgroup}\\${Username}" -s "${PrintServer}" \
   -w "${Password}" -D "Network Password" -r "smb " -l "${QueueName}" \
   -T /System/Library/CoreServices/NetAuthAgent.app -T 'group://NetAuth' \
   -T /System/Library/CoreServices/NetAuthAgent.app/Contents/MacOS/NetAuthSysAgent >/dev/null 2>&1

  # Make sure the password has the correct ACL ref https://mostlikelee.com/blog-1/2017/9/16/scripting-the-macos-keychain-partition-ids
  OS_Min_Vers=$(sw_vers | grep ProductVersion | awk '{print $2}' | cut -d "." -f2)
  if [ "${OS_Min_Vers}" -ge 12 ]; then
    echo -e "\nNOTE: You need to provide your local Mac password again here in order to give the printing system access to your credentials.\n"
    security set-internet-password-partition-list -S "apple-tool:,apple:" -s "${PrintServer}" > /dev/null
  fi

  sudo cupsenable "${QueueName}"
  sudo cupsaccept "${QueueName}"
  sudo launchctl stop org.cups.cupsd
  sudo launchctl start org.cups.cupsd
fi

# Set correct paper size and enable the duplexer option
if ! sudo lpadmin -p ${QueueName} -o PageSize=A4 -o Option1=True; then
  printerror "Could not set default options on the print queue ${QueueName}. See above error for details."
  exit 1
fi

# Set as default
if ! sudo lpadmin -d ${QueueName}; then
  printerror "Could not set the print queue ${QueueName} as default printer. See above error for details."
  exit 1
fi

echo "Printer successfully installed."
