#!/bin/sh # # Usage: pap job user title copies options [filename] # pap # # Important: This backend supports device discovery -- just call it without # parameters to see whether it works correctly on your system. # # Main use: CUPS backend to drive AppleTalk printers. Tested with CUPS # 1.1.14/1.1.15/1.1.19 # # Notes: Device URIs have to have the following format (special chars # in URL encoding): pap://zone/printername/printertype # Normally you shouldn't care about that since the backend lists # all available devices correctly formatted when asked by the # CUPS scheduler. # Don't forget to adjust $devicetypes and $formfeed to your needs! # When used with Darwin 7.0 (MacOS X 10.3.x) you might want to # change the shell to /bin/bash in the first line due to problems # with the encoding routines for the device uri (perl -e call) # ############################################################################# # # author: Thomas Kaiser # url: http://users.phg-online.de/tk/CUPS/backend/pap # date: 08 Jan 2004 (v0.1.3) # ############################################################################# # # Changes # # 0.1.3: - query AppleTalk devices by default for their "Product name" to # let CUPS assign the correct PPD automatically (this is achieved # by setting FullDeviceDiscovery=TRUE) # - set the default netatalk installation path back to /usr/bin # - set a timeout value of 15 seconds when querying printers for # their "Product name" # - massive performance improvement in larger networks with many # zones when doing device discovery while $FullDeviceDiscovery # set to TRUE (due to parallelizing the queries) # # 0.1.2: - fix the inability to deal with printers with slashes in their # names # - report wrong netatalk installation dir --> ${NetatalkBinDir} # - minor documentation improvements # # 0.1.1: - fix some documentation errors and default values # # 0.1.0: - first official release (22 Mar 2003) # ############################################################################# # # To Do: # # - optionally send pagecount queries to get exakt accounting info # ############################################################################# # # Non-Warranty: # This script comes with absolutely no warranty. # # Use at your own risk! # ############################################################################# # # Customizable settings: # # The devicetype variable specifies for which devices to look for (eg. # LaserWriter, DeskWriter, ImageWriter). If you want to lookup more than # one device at a time, write them separated with colons, ie. # # devicetypes="LaserWriter:DeskWriter" # # For all devicetypes use "=" instead. But be warned: this will also find # macs, servers, serial numbers and the like devicetypes="LaserWriter" # specify whether or not to send an additional form feed (DeskWriters # seem to need this. TRUE means send the ff formfeed=FALSE # When you want to use this with Netatalk, then specify the path to # the $BINDIR (do a search for nbplkup, eg.). You might also want to set # $FullDeviceDiscovery to TRUE to let CUPS guess the correct PPD for the # devices by asking them for their "Product" name... # Additionally you can let the backend collect exact pagecounts from # PostScript printers that correctly implement a back channel (answering # the backend's questions). If you use dumb AppleTalk print servers do # NOT set this option # # Note: This last option is still not implemented NetatalkBinDir=/usr/bin FullDeviceDiscovery=TRUE ExaktPageCount=FALSE # currently without effect ############################################################################# main() { # Processing the CUPS request. There exist 2 possibilities: Either CUPS # is asking us about device capabilities we can provide or it want us to # handle a print request and send the print job to a specific device CollectAccountingData "$@" PrepareRequest # No arguments means show available devices and exit... if test $# = 0; then echo "network ${protocol} \"Unknown\" \"AppleTalk Devices via ${protocol}\"" LookupDevices | sort | uniq exit 0 fi # Handle the print request. First check whether the device is available if PrinterAvailable "${PrinterName}" ; then type=`GetAppleTalkType "${PrinterName}"` if test $# = 5; then # Get print file from stdin; copies have already been handled... NotifyCUPS "INFO: Sending to ${PrinterName}" cat | PrintAndAccount "${PrinterName}" NotifyCUPS "PAGE: 1 1" # TO DO: send pagecount queries else # Print file is on command-line and we have to handle copies ourselve... SpoolFile=$6 while [ ${NeededCopies} -gt 0 ]; do ActualCopy=`expr $4 + 1 - ${NeededCopies}` NotifyCUPS "INFO: Sending copy ${ActualCopy} to ${PrinterName}" PrintAndAccount "${PrinterName}" < "${SpoolFile}" NotifyCUPS "PAGE: ${CountOfCopies} $4" # TO DO: send pagecount queries NeededCopies=`expr ${NeededCopies} - 1` done fi # Eventually send an additional form feed (some DeskWriters seem to # need that) if [ "X${type}" = "XDeskWriter" ]; then test "X${formfeed}" = "XTRUE" && printf "\f" | PrintAndAccount "${PrinterName}" fi else NotifyCUPS "ERROR: Unable to get printer status for \"${PrinterName}\"" exit 1 fi } PrepareRequest() { # How are we called? protocol=`basename $0` # Let's have a look whether we are using Netatalk's or Darwin's # AppleTalk implementation case `uname -s` in Darwin) ZoneLookup="/usr/bin/atlookup -z" PrinterAvailable() { /usr/bin/atstatus "$1" >/dev/null } PrintAndAccount() { /usr/bin/atprint "$1" } NBPLookup() { /usr/bin/atlookup -a "$1" | grep -v "^# Found" } ATPErrMsg="ATPsndreq\:\ Operation\ timed\ out" CheckProductName() { echo "Unknown" } TotalPages() { echo ${CUPS_PrintSettingsPMTotalBeginPages} } ;; *) # test the path of ${NetatalkBinDir} if [ ! -x "${NetatalkBinDir}/papstatus" ]; then echo "ERROR: Netatalk tools not found in \"${NetatalkBinDir}\"" >&2 exit 1 fi ZoneLookup="${NetatalkBinDir}/getzones" PrinterAvailable() { ${NetatalkBinDir}/papstatus -p "$1" >/dev/null } PrintAndAccount() { ${NetatalkBinDir}/pap -p "$1" } NBPLookup() { ${NetatalkBinDir}/nbplkup "$1" | \ perl -ne 'chomp; next if /^$/; m|^\s+(.+)\s+\d+\.\d+:|; my $entry = $1; $entry =~ s/\s+$//; print "$entry\n"' } ATPErrMsg="atp_rresp\:\ Connection\ timed\ out" CheckProductName() { # We'll try to ask the printer for its so called # "Product name" to let CUPS automatically assign # the right PPD file for the printer GuessedDeviceType="Unknown" if [ "$2" = "LaserWriter" -a "${FullDeviceDiscovery}" = "TRUE" ]; then if ${NetatalkBinDir}/papstatus -p "$1:$2@$3" >/dev/null 2>&1 then DeviceLookup=`echo -e "%%?BeginFeatureQuery: *Product\nstatusdict begin\nproduct print\nend\n%%?EndFeatureQuery: Unknown\n%%EOF" | ${NetatalkBinDir}/timeout -s HUP 15 -- ${NetatalkBinDir}/pap -p "$1:$2@$3" | sed 's|^\"(||;s|)\"$||;'` if [ -n "${DeviceLookup}" ]; then GuessedDeviceType="${DeviceLookup}" fi fi fi echo "${GuessedDeviceType}" } TotalPages=1 ;; esac # redefine the internal field separator to parse the zone list # correctly IFS=" " } GetAppleTalkType() { echo "$1" | cut -d: -f2 | cut -d@ -f1 } LookupDevices() { # Fetch the zone list and do a lookup in every single zone for occurences # of type $devicetype zonelist="`GetZoneList`" # get a list of AppleTalk Devicetypes to look for OIFS=$IFS IFS=":" set ${devicetypes} IFS=$OIFS for devicetype in $@; do echo "${zonelist}" | while read zone; do NBPLookup "=:${devicetype}@${zone}" | while read nbpentry ; do GetPrinterProperties "${nbpentry}" "${zone}" "${protocol}" & done & done done } GetZoneList() { # check whether we have to deal with zones eval ${ZoneLookup} >/dev/null if [ $? -eq 0 ]; then # okay, zones are available eval ${ZoneLookup} 2>&1 | sed "s|${ATPErrMsg}|*|;" else echo '*' fi } GetPrinterProperties() { name=`echo $1 | awk -F: '{print $1}'` type=`echo $1 | awk -F: '{print $2}'` model=`CheckProductName "${name}" "${type}" "$2"` echo -e "network $3://`EncodeDeviceURI "$2"`/`EncodeDeviceURI "${name}"`/`EncodeDeviceURI "${type}"` \"${model}\" \"${name}@${zone} ($3)\"" } EncodeDeviceURI() { # We'll have to quote the names with an URL encoding (RFC 2396) echo "$@" | perl -ple 's|([^\w=\-:@])|sprintf( "%%%02x", ord( $1))|ge' } DecodeDeviceURI() { # Decode the Device-URI echo "$@" | awk -F/ '{print $4 ":" $5 "@" $3}' | perl -ple 's/%([0-9A-Fa-f]{2})/chr(hex($1))/eg' } NotifyCUPS () { # sending message on stderr to keep CUPS informed echo -e "$*" >&2 } CollectAccountingData () { PrinterName=`DecodeDeviceURI ${DEVICE_URI}` Owner="$2" Title="$3" NeededCopies=$4 # parse the options passed from CUPS eval ParseCUPSOptions $5 } ParseCUPSOptions() { # We parse the CUPS options, delete occurences of "com.apple.print" in # their names, prepend the variable names with "CUPS_", define # them as variables and export them i="com\.apple\.print\." while [ $# -ge 1 ]; do VarName=`echo $1 | cut -d"=" -f1 | sed "s|^$i||g;s|\.[nb]\.$||g;s|\.||g"` VarValue=`echo $1 | cut -d"=" -f2 | sed "s|\ |\\\\\ |g"` eval CUPS_${VarName}=${VarValue} eval export CUPS_${VarName} shift done } ############################################################################# # # Let's start to process the CUPS requests main "$@" exit 0