#! /bin/bash
#
# Johannes Meixner <jsmeix@suse.de>, 2007, 2008

#set -x

export PATH="/sbin:/usr/sbin:/usr/bin:/bin"
export LC_ALL="POSIX"
export LANG="POSIX"
umask 022

MY_NAME=${0##*/}

# Create temporary file names:
TMP_DATA=$(mktemp -u /tmp/$MY_NAME.XXXXXX)
TMP_DATA_RAW_V=$(mktemp -u /tmp/$MY_NAME.XXXXXX)
TMP_DATA_RAW_L=$(mktemp -u /tmp/$MY_NAME.XXXXXX)
TMP_DATA_RAW_A=$(mktemp -u /tmp/$MY_NAME.XXXXXX)

# Test if lpstat is executable:
LPSTAT="$( type -P lpstat )"
if ! test -x "$LPSTAT"
then echo "Cannot execute lpstat" 1>&2
     exit 1
fi

# Test if cupsd is accessible.
# This is not limited to localhost.
# Even for a system-wide 'client-only' config via /etc/cups/client.conf
# it must work to get the queue information.
# But on the other hand, any other kind of setting for a remote CUPS server
# (e.g. an inherited CUPS_SERVER environment variable setting or a
# ServerName entry in ~/.cups/client.conf of the user who runs this script)
# is ignored to avoid that by accident an unintended CUPS server is asked.
# The 'tr ... [:space:]' makes sure that the first active ServerName entry
# is used if there is more than one which is a broken config.
SERVERNAME="$( grep -i '^ServerName ' /etc/cups/client.conf | tr -s '[:space:]' ' ' | cut -s -d' ' -f2 )"
if test -z "$SERVERNAME"
then SERVERNAME="localhost"
fi
if ! $LPSTAT -h $SERVERNAME -r &>/dev/null
then echo "Cannot access cupsd on '$SERVERNAME'" 1>&2
     exit 2
fi

# Input:

# Get the raw data:
# Continue even after timeout because then an empty YCP map is output
# which indicates that no print queues were autodetected.
# If 'lpstat -v' is aborted but 'lpstat -l' or 'lpstat -a' is successful, there is no DeviceURI
# and then there is also correctly no output because a DeviceURI is mandatory.
MAXIMUM_WAIT="30"
# Get queue names and DeviceURI:
$LPSTAT -h $SERVERNAME -v >$TMP_DATA_RAW_V &
lpstatPID_V=$!
# Get additional info about the queues:
$LPSTAT -h $SERVERNAME -l -p >$TMP_DATA_RAW_L &
lpstatPID_L=$!
# Get info whether or not the queues accept or reject jobs:
$LPSTAT -h $SERVERNAME -a >$TMP_DATA_RAW_A &
lpstatPID_A=$!
# Timeout stuff:
for i in $( seq $MAXIMUM_WAIT )
do ps $lpstatPID_V &>/dev/null || ps $lpstatPID_L &>/dev/null || ps $lpstatPID_A &>/dev/null || break
   sleep 1
done
if ps $lpstatPID_V &>/dev/null
then kill -9 $lpstatPID_V &>/dev/null
     echo "Aborted 'lpstat -v' after $MAXIMUM_WAIT seconds timeout." 1>&2
fi
if ps $lpstatPID_L &>/dev/null
then kill -9 $lpstatPID_L &>/dev/null
     echo "Aborted 'lpstat -l' after $MAXIMUM_WAIT seconds timeout." 1>&2
fi
if ps $lpstatPID_A &>/dev/null
then kill -9 $lpstatPID_A &>/dev/null
     echo "Aborted 'lpstat -a' after $MAXIMUM_WAIT seconds timeout." 1>&2
fi


# Process the data:

# Extract what is needed, quotation marks " are replaced by ' :
tr '"' "'" <$TMP_DATA_RAW_V >$TMP_DATA

# Change "^device for" to "queue"
# and convert only the first colon after the queue name to "\nuri"
# so that the DeviceURI is on a seperated line:
sed -i -e 's/^device for/queue/' \
       -e 's/:/\nuri/' $TMP_DATA

# Condense multiple spaces, convert tabs to blanks, and remove leading and trailing spaces:
sed -i -e 's/[[:space:]][[:space:]]*/ /g' \
       -e 's/^[[:space:]]*//' \
       -e 's/[[:space:]]*$//' $TMP_DATA

# Output:

# Output header:
echo "[" 

# Function to output one entry:
Output()
{ if [ -n "$QUEUE" -a -n "$URI" ]
  then echo -e "  \$[ \"name\":\"$QUEUE\",\n     \"uri\":\"$URI\",\n     \"description\":\"$DESCRIPTION\",\n     \"location\":\"$LOCATION\",\n     \"ppd\":\"$PPD\",\n     \"default\":\"$DEFAULT\",\n     \"disabled\":\"$DISABLED\",\n     \"rejecting\":\"$REJECTING\",\n     \"config\":\"$CONFIG\"\n  ],"
  fi
}

# Make complete and seperated entries.
# The values are collected until a new "queue" line appears, then the values are output.
# The very first "queue" line doesn't result an output because "$URI" is an empty string.
# Since CUPS 1.3 it does no longer work in any case (e.g for a local raw IPP queue)
# to use the "Connection:..." value in the "lpstat -l" output to distinguish between
# a local queue (in /etc/cups/printers.conf) and a remote queue (on another host) and
# therefore /etc/cups/printers.conf is directly inspected to distinguish local and remote queues.
# Reason:
# In the CUPS sources systemv/lpstat.c uses CUPS_PRINTER_REMOTE to distinguish between
# its output "Connection: remote" and "Connection: direct".
# But the new support for "hard-wired" remote printers has muddied the waters:
# CUPS_PRINTER_REMOTE means that the printer is hosted (does filtering) on a remote system.
# CUPS_PRINTER_DISCOVERED means that cupsd added the printer for the user.
# If both bits are set, then the queue is a traditional CUPS browsing remote printer.
# If CUPS_PRINTER_DISCOVERED is not set, it is a local queue but filtering happens remote.
# Summary table:
#   Type                   CUPS_PRINTER_RREMOTE  CUPS_PRINTER_DISCOVERED
#   ---------------------  --------------------  -----------------------
#   Local queue            No                    No
#   CUPS-browsing printer  Yes                   Yes
#   Raw IPP queue          Yes                   No
#   Bonjour queue          Yes                   No

exec <$TMP_DATA
while read KEY VALUE
do case "$KEY" in
        queue) Output
               QUEUE="$VALUE"
               DESCRIPTION=$( sed -n -e "/printer $QUEUE /,/On fault:/{s/^[[:space:]]*Description: //p}" $TMP_DATA_RAW_L )
               LOCATION=$( sed -n -e "/printer $QUEUE /,/On fault:/{s/^[[:space:]]*Location: //p}" $TMP_DATA_RAW_L )
               PPD=$( sed -n -e "/printer $QUEUE /,/On fault:/{s/^[[:space:]]*Interface: //p}" $TMP_DATA_RAW_L )
               [ "${PPD##*.}" != "ppd" ] && PPD=""
               DISABLED=$( grep -q "^printer $QUEUE disabled" $TMP_DATA_RAW_L && echo "yes" || echo "no" )
               REJECTING=$( grep -q "^$QUEUE not accepting" $TMP_DATA_RAW_A && echo "yes" || echo "no" )
               DEFAULT=$( grep -q "^<DefaultPrinter $QUEUE>" /etc/cups/printers.conf && echo "yes" || echo "" )
               CONFIG=$( grep -q "^<.*Printer $QUEUE>" /etc/cups/printers.conf && echo "local" || echo "remote" )
               URI="" ;;
        uri) URI="$VALUE"
             [ -z "${URI%%/*}" ] && URI="file:$VALUE" ;;
        *) echo "Ignoring key $KEY" 1>&2 ;;
   esac
done

# Output the last entry and a footer for YCP
Output
echo -e "  \$[]\n]"

# Remove the temporary files 
rm $TMP_DATA_RAW_V $TMP_DATA_RAW_L $TMP_DATA_RAW_A $TMP_DATA
exit 0

