NT-ware uniFLOW SmartClient Uninstaller for Jamf Pro

cainehorr
Contributor III

Are you running the NT-ware uniFLOW SmartClient for macOS?

As you probably know, NT-ware's older versions (v1.1.0 and lower) were not as robust as their Windows versions.

In order to upgrade/update to newer versions, you need to uninstall the older versions first.

NT-ware places their default uninstall script here...

/etc/smartclient/uninstall-smartclient.bash

Unfortunately, this script is extremely rudimentary and lacks any support for running in an automated environment that uses an MDM/UDM solution such as Jamf Pro.

With that, I give you my highly augmented version of an NT-ware uniFLOW SmartClient uninstaller that works both locally and/or with Jamf Pro.

A brief HOWTO is posted down below... after the script.

THE SCRIPT

#!/bin/sh

##############################################################################
#
# SCRIPT FILENAME:
#   uninstall_nt-ware_uniflow_smartclient.sh
#
# DESCRIPTION:
#   The core functionality of this script was written by NT-ware to uninstall
#   the uniFLOW SmartClient for macOS. 
#
#   This version of the uniFLOW SmartClient "uninstall-smartclient.sh" is a
#   complete overhaul that provides additional error checking, better local
#   user identification, multi-shell compatibility, robust support for unique
#   user environments, better output/logging, and a local (terminal) mode 
#   and an automated UDM/MDM (Jamf Pro) mode. 
#
# DISCLAIMER:
#   Jamf Pro is not required to run this script. This script can be used as
#   a replacement for the default "uninstall-smartclient.sh" that comes 
#   bundled with the uniFLOW SmartClient.
#
# CHANGE LOG:
#   v1.0 - 2020-11-05
#     Written by Caine Hörr
#     https://github.com/cainehorr
#       * Initial Script Creation
#         * Replaced vendor assigned shebang for bash and zsh compatibility
#         * Replaced vendor code that checks for sudo/root permissions to 
#           properly identify the currently logged in user
#         * Replaced vendor code with an array to process sub-folder deletion
#           within home folders
#         * Replaced vendor code with AppleScript to gracefully quit the 
#           uniFLOW SmartClient
#         * Removed vendor code that unnecessarily utilizes ls commands in 
#           favor of echoing path data already held within variables
#         * Added functions for multi-use code blocks
#         * Added comments to properly "document/explain" the script
#         * Added absolute path statements to all called binaries (because 
#           you never know what users have done to their system environment)
#         * Added error checking for existence of files and directories
#         * Added echo statements for running script both locally and for 
#           logging to Jamf Pro policy logs
#         * Added JamfHelper pop-ups to guide the user appropriately when
#           being run from Jamf Pro
#
##############################################################################

##############################################################################
#
# MIT License
#
# Copyright (c) 2020 Caine Hörr
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to 
# deal in the Software without restriction, including without limitation the 
# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
# sell copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
# IN THE SOFTWARE.
#
##############################################################################

# Set the name and location of the uniFLOW SmartClient application
appName="/Applications/uniFLOW SmartClient.app"

# Acquire the currently installed version of uniFLOW SmartClient.app
currentAppVersion="$(defaults read "${appName}/Contents/Info" CFBundleShortVersionString | awk '{ print $1 }')"

##############################################################################
#
# EDIT THESE VALUES AS MUCH AS YOU WANT!
# 
# These values configure the jamfHelper pop-up window look and feel
#
window_Type="utility"
text_Title="mac@Your_Company_Name"
text_Header="uniFLOW SmartClient Uninstall"
align_Header="left"
text_Message_One="IT is uninstalling version ${currentAppVersion} of the following application from your device in preparation for an updated version\n \n${appName}\n \nClicking "OK" will begin the process. \n \nOn the next pop-up window, please click the "Yes" button."
text_Message_Two="The removal of version ${currentAppVersion} of the following application from your device has failed\n \nn${appName}\n \nPlease contact IT for immediate remediation."
text_Message_Three="The removal of version ${currentAppVersion} of the following application from your device has completed\n \n${appName}\n \nIT will now proceed with installing an updated version."
align_Description="left"
icon_path="/path/to/your/corporate/logo.png"
button_Continue="OK"
button_Default=1
#
##############################################################################

##############################################################################
#
# THERE ARE NO USER SERVICABLE PARTS DOWN HERE...
# DO NOT EDIT BELOW THIS LINE!
#
##############################################################################

# Check for sudo/root permissions
# This is only need when running the script locally
# When running in an automated UDM/MDM environment, sudo/root is implied
if [[ "$(/usr/bin/id -u)" != "0" ]]; then
  echo "[ERROR] $0 requires sudo/root privileges"
  exit 1
fi

function_jamfHelperPopUpWindow(){
  # Call jamfHelper and display the pop-up window
  jamfHelper -windowType "${window_Type}" -lockHUD -title "${text_Title}" -heading "${text_Header}" -alignHeading "${align_Header}" -description "${text_Description}" -alignDescription "${align_Description}" -icon "${icon_path}" -button1 "${button_Continue}" -defaultButton "${button_Default}"

}

function_quit_uniFLOW_SmartClient(){
  if [[ ${jamfHelperInstalled} = "True" ]]; then
    # Set the jamfHelper Message
    text_Description=$( echo ${text_Message_One} )

    function_jamfHelperPopUpWindow
  fi

  # Attempt to quit the uniFLOW SmartClient gracefully
  /usr/bin/osascript <<'END'
tell application "SmartClient"
  quit
end tell
END
}

echo "Preparing to uninstall ${appName} v${currentAppVersion}"

# Set path to the jamfHelper.app
jamfHelperPath=/Library/Application Support/JAMF/bin/jamfHelper.app

if [[ -d "${jamfHelperPath}" ]]; then
  jamfHelperInstalled="True"
  echo "jamfHelper is installed - Running in Jamf Pro mode"
  # Set path to the jamfHelper.app/Contents/MacOS
  PATH=$PATH:/Library/Application Support/JAMF/bin/jamfHelper.app/Contents/MacOS
else
  jamfHelperInstalled="False"
  echo "jamfHelper is not installed - Running in terminal mode"
fi

# Identify if the uniFLOW SmartClient is running - First Pass
runningInstance=$(/bin/ps -a -x -o comm | /usr/bin/grep 'SmartClient$' )
# Validate that $runningInstance contains information
if [[ -n "$runningInstance" ]]; then
  echo "[WARNING] uniFLOW SmartClient is running on this device - Attempting to quit"
  function_quit_uniFLOW_SmartClient

  until function_quit_uniFLOW_SmartClient; do
    if [[ $? -eq 0 ]]; then
        echo "User agreed to the uniFLOW SmartClient uninstallation"
    fi
  done
else
  echo "[WARNING] uniFLOW SmartClient is not running on this device"
fi

# Identify if the uniFLOW SmartClient is running - Second Pass
runningInstance=$(/bin/ps -a -x -o comm | /usr/bin/grep 'SmartClient$' )
# Validate that $runningInstance does not contains information
if [[ ! -z "$runningInstance" ]]; then
  echo "[ERROR] uniFLOW SmartClient is running on this device - Attempting to quit has failed"

  if [[ ${jamfHelperInstalled} = "True" ]]; then
    # Set the jamfHelper Message
    text_Description=$( echo ${text_Message_Two} )

    function_jamfHelperPopUpWindow
  fi

  exit 1
fi

# Remove UniFlow SmartCient Domains and Services 
plistPath=/Library/LaunchAgents/com.ntware.SmartClient.plist
if [[ -f ${plistPath} ]]; then
  echo "Removing UniFlow SmartCient Domains and Services"
  /bin/launchctl bootout system /Library/LaunchAgents/com.ntware.SmartClient.plist
fi

# Delete existing uniFLOW SmartClient printer queues
if [[ -f /Users/shared/.smartclient/queue_created_at_installation ]]; then
   QUEUE_NAME_NO_SPACES=$(/bin/cat /Users/shared/.smartclient/queue_created_at_installation)
   # Validate that $QUEUE_NAME_NO_SPACES is not null
   if [[ -n "$QUEUE_NAME_NO_SPACES" ]]; then
      echo "Deleting printer queue $QUEUE_NAME_NO_SPACES"
      # Delete the printer or class destination
      /usr/sbin/lpadmin -x "$QUEUE_NAME_NO_SPACES"
   fi
fi

# Identify the uniFLOW SmartClient printers and what devices they are attached to
list=$(/usr/bin/lpstat -v | /usr/bin/grep 'mom:S*$')
# Determine the number of uniFLOW SmartClient printers present
count=$(echo "$list" | /usr/bin/wc -l)

for queueNumber in $(seq $count); do
   name=$(echo "$list" | /usr/bin/sed -n "$queueNumber p" | /usr/bin/awk '{print $(NF-1)}' 2>/dev/null | /usr/bin/sed 's?:$??')
   newPath=$(echo "$list" | /usr/bin/sed -n "$queueNumber p" | /usr/bin/awk '{print $(NF)}' 2>/dev/null | /usr/bin/sed 's?mom:??g')
   # If $name OR $newPath value is null then continue to the next queueNumber
   # in the loop without running any further commands below this if/then statement
   if [ -z "$name" -o -z "$newPath" ]; then
      continue
   fi
  # The following statements will not execute if $name OR $newPath are null
   echo "Changing path for $name to $newPath"
   /usr/sbin/lpadmin -p "$name" -E -v "$newPath"
done

# If the script can't uninstall all uniFLOW SmartClient printers, throw this error
remaining=$(/usr/bin/lpstat -v | /usr/bin/grep 'mom:S*$')
if [[ -n "$remaining" ]]; then
   echo "[ERROR] Unable to remove 'mom:' backend from all printers. \n \nPlease do so manually before attempting to uninstall again."
   exit 1
fi

# Set input field separator to a comma ","
IFS=','

files='/Library/LaunchAgents/com.ntware.SmartClient.plist,/private/etc/paths.d/momadmin-path,/private/etc/sudoers.d/momadmin-sudoers,/usr/libexec/cups/backend/mom,/usr/local/sbin/momadmin.sh'
directories='/Applications/uniFLOW SmartClient.app,/private/etc/smartclient,/Users/Shared/.smartclient'

# Check for the existence of files as declared in $files
for file in ${files}; do
  # If file is not found, break out of loop
  if [[ ! -f ${file} ]]; then
    # If content of $file is not found then continue to the next itteration of $file
    # in the loop without running any further commands below this if/then statement
    echo "[WARNING] File not found: ${file}"
    continue
  fi
  # The following statements will not execute if $file is not found
  # Delete ${file}
  /bin/rm -f ${file}
  echo "Deleting the following file: ${file}"
done

# Check for the existence of directories as declared in $directories
for directory in ${directories}; do
  if [[ ! -d ${directory} ]]; then
    # If content of $directories is not found then continue to the next itteration of $directories
    # in the loop without running any further commands below this if/then statement
    echo "[WARNING] Directory not found: ${directory}"
    continue
  fi
  # The following statements will not execute if $directories is not found
  # Delete ${directory} 
  /bin/rm -r ${directory}
  echo "Deleting the following directory: ${directory}"
done

# Delete all .smartclient sub-directories from all User home folders
array=(/Users/*)
for i in "${!array[@]}"; do
  userDirectory=${array[i]}
  smartClientUserConfig="${userDirectory}/.smartclient"
  if [[ -d "${smartClientUserConfig}" ]]; then
    echo "Deleting ${smartClientUserConfig}"
    /bin/rm -rf "${smartClientUserConfig}"
  else
    echo "[WARNING] Directory not found: ${smartClientUserConfig}"
  fi
done

if [[ ${jamfHelperInstalled} = "True" ]]; then
  # Set the jamfHelper Message
  text_Description=$( echo ${text_Message_Three} )

  function_jamfHelperPopUpWindow
fi

exit

HOWTO USE uninstall_nt-ware_uniflow_smartclient.sh

It's pretty straight forward really.

  1. Upload the script to Jamf Pro > Settings > Computer Management > Script
  2. Set the script priority to "Before"
  3. Create a Computer Policy and assign the script
  4. Set any additional policy parameters you require

ENJOY!

Kind regards,

Caine Hörr

A reboot a day keeps the admin away!

2 REPLIES 2

sdg4754
New Contributor

We are having issues installing SmartClient. Do you have any ideas for how we might do that?

themacallan
New Contributor III

We just switched from PaperCut to uniFLOW and we discovered exactly what you described in terms of requiring an uninstall. Thanks for doing the work and making life easier. Cheers!