1#
2# CDDL HEADER START
3#
4# The contents of this file are subject to the terms of the
5# Common Development and Distribution License (the "License").
6# You may not use this file except in compliance with the License.
7#
8# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9# or http://www.opensolaris.org/os/licensing.
10# See the License for the specific language governing permissions
11# and limitations under the License.
12#
13# When distributing Covered Code, include this CDDL HEADER in each
14# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15# If applicable, add the following below this CDDL HEADER, with the
16# fields enclosed by brackets "[]" replaced with your own identifying
17# information: Portions Copyright [yyyy] [name of copyright owner]
18#
19# CDDL HEADER END
20#
21#
22#ident	"%Z%%M%	%I%	%E% SMI"
23#
24# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
25# Use is subject to license terms.
26#
27#
28###########
29##
30## Network Standard printer interface program for foomatic.
31##
32###########
33
34#####
35# We can't do much except exit if spooler/scheduler
36# cancels us.
37#####
38trap 'eval exit_clean 15' 15
39
40####
41#
42# Send standard error messages to /dev/null rather than to
43# the spooler. Avoids "Terminated" messages that shell puts out
44# when gets SIGTERM. Save standard error so it can be used
45# when we need it
46####
47exec 5>&2 2>/dev/null 3>&1
48
49####
50# set some global variables
51####
52
53: ${LPTMPDIR:=/tmp}
54: ${SPOOLDIR:=/usr/spool/lp}
55: ${LOCALPATH:=${SPOOLDIR}/bin}
56PATH="/bin:/usr/bin:${LOCALPATH}"
57exit_code=0
58
59
60# ${LPTELL} is the name of a program that will send its
61# standard input to the Spooler. It is used to forward
62# the description of a printer fault to the Spooler,
63# which uses it in an alert to the administrator.
64#####
65if [ ! -x "${LPTELL:=${LOCALPATH}/lp.tell}" ]
66then
67        fake_lptell () {
68                header="no"
69                while read line
70                do
71                        if [ "no" = "${header}" ]
72                        then
73                                errmsg ERROR ${E_IP_UNKNOWN} \
74                "unknown printer/interface failure" \
75                "consult your system administrator;
76                reasons for failure (if any) follow:"
77                                header=yes
78                        fi
79                        echo "${line}" >&2
80                done
81                return 1
82        }
83        LPTELL=fake_lptell
84fi
85
86#####
87# ${LPTSOLSEPARATOR} is the name of a program to put banner and trailer
88# pages around the job.
89#####
90if [ -x ${LOCALPATH}/lp.tsol_separator ]
91then
92	LPTSOLSEPARATOR=${LOCALPATH}/lp.tsol_separator
93else
94	echo "${LOCALPATH}/lp.tsol_separator not found." >&2
95	exit 1
96fi
97
98#####
99# Error message formatter:
100#
101# Invoke as
102#
103#       errmsg severity message-number problem help
104#
105# where severity is "ERROR" or "WARNING", message-number is
106# a unique identifier, problem is a short description of the
107# problem, and help is a short suggestion for fixing the problem.
108#####
109
110LP_ERR_LABEL="UX:lp"
111E_IP_ARGS=1
112E_IP_OPTS=2
113#E_IP_FILTER=3
114E_IP_UNKNOWN=5
115E_IP_BADFILE=6
116E_IP_ERRORS=12 	# (in slow.filter)
117
118errmsg () {
119
120        case $1 in
121        ERROR )
122                sev="  ERROR";
123                ;;
124        WARNING )
125                sev="WARNING";
126                ;;
127        esac
128
129        echo "${LP_ERR_LABEL}:$2 ${sev}: $3
130        TO FIX: $4" >&5
131}
132
133###########
134##
135## Check arguments
136###########
137
138parse () {
139        echo "`expr \"$1\" : \"^[^=]*=\(.*\)\"`"
140}
141
142#####
143##
144## Error Cleanup and Exit
145##
146#####
147
148exit_clean()
149{
150
151	if [ -f "${LPTMPDIR}/pr_eexit_code.$$" ]
152	then
153		/bin/rm ${LPTMPDIR}/pr_eexit_code.$$
154	fi
155
156	if [ -f "${LPTMPDIR}/small_banner.$$" ]
157	then
158		/bin/rm ${LPTMPDIR}/small_banner.$$
159	fi
160
161	if [ -f "${LPTMPDIR}/banner.exit_code.$$" ]
162	then
163		/bin/rm ${LPTMPDIR}/banner.exit_code.$$
164	fi
165
166	if [ -f "${LPTMPDIR}/banner.errmsg.$$" ]
167	then
168		/bin/rm ${LPTMPDIR}/banner.errmsg.$$
169	fi
170
171	if [ -f "${tmpfile}" ]
172	then
173		/bin/rm "${tmpfile}"
174	fi
175
176	exit $1
177}
178
179#####
180#
181# This program is invoked as
182#
183# ${SPOOLDIR}/.../printer request-id user title copies options files...
184#
185# The first three arguments are simply reprinted on the banner page,
186# the fourth (copies) is used to control the number of copies to print,
187# the fifth (options) is a blank separated list (in a single argument)
188# of user or Spooler supplied options (without the -o prefix),
189# and the last arguments are the files to print.
190#####
191
192if [ $# -lt 5 ]
193then
194
195        errmsg ERROR ${E_IP_ARGS} \
196                "wrong number of arguments to interface program" \
197                "consult your system administrator"
198        exit 1
199fi
200
201printer=`basename $0`
202request_id=$1
203user_name=$2
204title=$3
205copies=$4
206option_list=$5
207
208shift 5
209files="$*"
210
211
212#
213# debug sent to file if defined in /etc/syslog.conf
214# syslog.conf entry:
215#	lpr.debug	/path/filename
216#
217logger -p lpr.debug -t "tsol_netstandard_foomatic: ${request_id}" " "
218logger -p lpr.debug -t "tsol_netstandard_foomatic: ${request_id}" "INPUT"
219logger -p lpr.debug -t "tsol_netstandard_foomatic: ${request_id}" \
220    "    printer : ${printer}"
221logger -p lpr.debug -t "tsol_netstandard_foomatic: ${request_id}" \
222    "    request_id : ${request_id}"
223logger -p lpr.debug -t "tsol_netstandard_foomatic: ${request_id}" \
224    "    user_name : ${user_name}"
225logger -p lpr.debug -t "tsol_netstandard_foomatic: ${request_id}" \
226    "    title : ${title}"
227logger -p lpr.debug -t "tsol_netstandard_foomatic: ${request_id}" \
228    "    copies : ${copies}"
229logger -p lpr.debug -t "tsol_netstandard_foomatic: ${request_id}" \
230    "    option_list : ${option_list}"
231logger -p lpr.debug -t "tsol_netstandard_foomatic: ${request_id}" \
232    "    files : ${files}"
233logger -p lpr.debug -t "tsol_netstandard_foomatic: ${request_id}" \
234    "	 spooler_key ${SPOOLER_KEY}"
235
236####
237# default: do print a banner
238####
239nobanner=no
240nolabels="no"
241nofilebreak="no"
242inlist=
243data_file_flag=
244
245for i in ${option_list}
246do
247        case "${inlist}${i}" in
248
249        nobanner )
250                nobanner="yes"
251                ;;
252
253        nofilebreak )
254                nofilebreak="yes"
255                ;;
256
257	nolabels )
258		nolabels="yes"
259		;;
260
261        #####
262        #
263        # If you want to add simple options (e.g. -o simple)
264        # identify them here.
265        #####
266#       simple )
267#               simple="yes"
268# 		;;
269
270        cpi=pica )
271                cpi=10
272                ;;
273        cpi=elite )
274                cpi=12
275                ;;
276        cpi=* )
277                cpi=`parse ${i}`
278                ;;
279
280        lpi=* )
281                lpi=`parse ${i}`
282                ;;
283
284        length=* )
285                length=`parse ${i}`
286                ;;
287
288        width=* )
289                width=`parse ${i}`
290                ;;
291        dest=* )
292                dest="-d `parse ${i}`"
293                ;;
294
295        protocol=* )
296                protocol="-P `parse ${i}`"
297                ;;
298        bsdctrl=* )
299		controlfile="-c `parse ${i}`"
300                ;;
301        timeout=* )
302                timeout="-t `parse ${i}`"
303                ;;
304
305        data-file-type=* )
306                data_file_flag="-f `parse ${i}`"
307                ;;
308
309	#
310	# The IPP/PAPI attributes are handled by the foomatic-rip filter so
311	# all we need to do here is ignore them so that they don't invoke the
312	# "unrecognized option" message.
313	#
314
315	finishing=* | page-ranges=* | sides=* )
316		;;
317	number-up=* | orientation-requested=* | media=* )
318		;;
319	printer-resolution=* | print-quality=* )
320		;;
321
322        #####
323        #
324        # If you want to add simple-value options (e.g. -o value=a)
325        # identify them here.
326        #####
327#       value=* )
328#		value=`parse ${i}`
329#		;;
330
331        #####
332        #
333        # If you want to add options that,
334        # take a list (e.g. -o lopt='a b c'), identif
335        # them here and below (look for LOPT).
336        #####
337
338#	flist=* | lpd=* | options=* )
339        flist=* | lpd=* )
340#LOPT   stty=* | flist=* | lpd=* | lopt=* )
341
342                inlist=`expr "${inlist}${i}" : "^\([^=]*=\)"`
343                case "${i}" in
344                ${inlist}\'*\' )
345                        item=`expr "${i}" : "^[^=]*='*\(.*\)'\$"`
346                        ;;
347                ${inlist}\' )
348                        continue
349                        ;;
350                ${inlist}\'* )
351                        item=`expr "${i}" : "^[^=]*='*\(.*\)\$"`
352                        ;;
353                ${inlist}* )
354                        item=`expr "${i}" : "^[^=]*=\(.*\)\$"`
355                        ;;
356                *\' )
357                        item=`expr "${i}" : "^\(.*\)'\$"`
358                        ;;
359                * )
360                        item="${i}"
361                        ;;
362                esac
363
364                #####
365                #
366                # We don't dare use "eval" because a clever user could
367                # put something in an option value that we'd end up
368                # exec'ing.
369                #####
370                case "${inlist}" in
371                flist= )
372                        flist="${flist} ${item}"
373                        ;;
374                lpd= )
375                        lpd="${lpd} ${item}"
376                        ;;
377#LOPT		lopt= )
378#LOPT                   lopt="${lopt} ${item}"
379#LOPT			;;
380#		options= )
381#			options="${options} ${item}"
382#			;;
383                esac
384
385                case "${i}" in
386                ${inlist}\'*\' )
387                        inlist=
388                        ;;
389                ${inlist}\'* )
390                        ;;
391                *\' | ${inlist}* )
392                        inlist=
393                        ;;
394                esac
395                ;;
396
397        * )
398                errmsg WARNING ${E_IP_OPTS} \
399                        "unrecognized \"-o ${i}\" option" \
400                        "check the option, resubmit if necessary
401                printing continues"
402                ;;
403        esac
404done
405
406logger -p lpr.debug -t "tsol_netstandard_foomatic: ${request_id}"  \
407    "term : ${TERM}"
408
409if [ -z "${FILTER}" ]
410then
411        #####
412        #
413        # If no filter is being used, we use netpr to push the
414	# file to the printer.
415        # (QUOTES ARE IMPORTANT!)
416        #####
417
418        case "$TERM" in
419                PS )
420                        # make the "postscript" printers use cat
421			# (TSOL banners are added during filtering, so we have
422			# to use some filter.)
423                        FILTER=/bin/cat
424                ;;
425                PSR )
426                        # make the "reverse postscript" printers reverse the
427                        # output and the use postio to talk to the printer
428                        #FILTER="/usr/lib/lp/postscript/postreverse "
429                        #FILTER=
430                        FILTER="/usr/lib/lp/postscript/postreverse "
431                ;;
432                * )
433                        # We don't know the type, so just assume that the
434                        # input and output are the same. Use netpr.
435                        #FILTER=/bin/cat
436			FILTER=
437                ;;
438        esac
439fi
440
441####
442# sets default value for ordering of data and control files with
443# bsd protocol. Default: data files first. Administrator
444# may set to control file first with lpadmin -o bsdctrl=first
445####
446
447banner_flag=""
448case "${nobanner}" in
449	yes )
450		banner_flag="-b"
451	;;
452esac
453
454NETPR="/usr/lib/lp/bin/netpr ${banner_flag} ${data_file_flag} \
455	-I ${request_id} -U ${user_name} \
456	-p ${printer} ${dest} -T \"${title}\"  \
457	${timeout}  ${protocol} ${controlfile} "
458LPTELL_OPTS="-l"	# netpr sends LaserWriter style messages back
459PPDFILTER=/usr/lib/lp/bin/foomatic-rip
460PPDFILTERA="${PPDFILTER} ${request_id} ${user_name} \"${title}\" ${copies} \"${option_list}\""
461
462logger -p lpr.debug -t "tsol_netstandard_foomatic: ${request_id}" \
463    "NETPR= ${NETPR}"
464logger -p lpr.debug -t "tsol_netstandard_foomatic: ${request_id}" \
465    "filter : ${FILTER}"
466logger -p lpr.debug -t "tsol_netstandard_foomatic: ${request_id}" \
467    "ppdfilter : ${PPDFILTERA}"
468
469node=`uname -n`
470pid=$$
471tmpfile=${LPTMPDIR}/${node}.${pid}
472tmpfilefoo=${LPTMPDIR}/${node}.${pid}.1
473
474logger -p lpr.debug -t "tsol_netstandard_foomatic: ${request_id}" \
475    "tmpfile : ${tmpfile}"
476
477#####
478#
479# Set up filter for banner page
480#
481#####
482banner_filter=
483case "${TERM}" in
484PS | PSR )
485	banner_filter=" | /usr/lib/lp/postscript/postprint "
486	LPTELL_OPTS="-l"
487	;;
488esac
489
490#####
491#
492# Build temporary file that is the banner page
493#
494#####
495PAD="#####${NL}"
496CR="\r"
497NL="${CR}\n"
498FF=
499
500small_banner() {
501        echo "${CR}\c"
502        echo "${PAD}\c"
503        echo "#####  User: ${user_name}${NL}\c"
504        if [ -n "${title}" ]
505        then
506                echo "##### Title: ${title}${NL}\c"
507        fi
508        echo "#####  Date: `LANG=C date '+%a %H:%M %h %d, %Y'`${NL}\c"
509        echo "#####   Job: ${request_id}${NL}\c"
510        echo "${PAD}\c"
511        if [ -n "${FF}" ]
512        then
513                echo "${CR}${FF}\c"
514        fi
515}
516
517#####
518#
519# Doing small banner as we don't know what printer is out there
520#
521#####
522banner=small_banner
523
524## Skip this for PS/PSR printers, since lp.tsol_separator handles the banners
525if [ "no" = "${nobanner}" -a "${TERM}" != "PSR" -a "${TERM}" != "PS" ]
526then
527	eval "${banner} ${banner_filter}" 2>&1 1>${LPTMPDIR}/small_banner.$$
528fi
529
530###########
531##
532## Surround the job by PostScript code to produce banner
533## and trailerpages and page headers and footers.
534##
535###########
536
537BANNER_EXIT_CODE=${LPTMPDIR}/banner.exit_code.$$
538echo 0 > ${BANNER_EXIT_CODE}
539TSOLSEPARATOR_LOG=${LPTMPDIR}/banner.errmsg.$$
540
541tsol_bannerize () {
542	TSOLSEPARATOR_OPTS="-e ${TSOLSEPARATOR_LOG}"
543
544	if [ "yes" = "${nolabels}" ]
545	then
546		TSOLSEPARATOR_OPTS="${TSOLSEPARATOR_OPTS} -l"
547	fi
548
549	if [ "yes" = "${nobanner}" ]
550	then
551		TSOLSEPARATOR_OPTS="${TSOLSEPARATOR_OPTS} -t /dev/null -b /dev/null"
552	fi
553
554	if [ "${TERM}" = "PSR" ]
555	then
556		TSOLSEPARATOR_OPTS="${TSOLSEPARATOR_OPTS} -r"
557	fi
558
559	# Get rid of the #, TAB and NL characters in the title
560	tsol_title=`echo $title`
561	tsol_title=`echo $tsol_title | sed 's/#//g'`
562
563	logger -p lpr.debug -t "tsol_netstandard: ${request_id}" \
564	    "banner command: ${LPTSOLSEPARATOR} ${TSOLSEPARATOR_OPTS} \
565	    ${printer} ${request_id} ${user_name} \"${tsol_title}\" ${file}"
566	${LPTSOLSEPARATOR} ${TSOLSEPARATOR_OPTS} ${printer} \
567	    ${request_id} ${user_name} "${tsol_title}" ${file}
568
569	echo $? > ${BANNER_EXIT_CODE}
570	true
571}
572
573bannerize=tsol_bannerize
574
575if [ "yes" = "${nobanner}" -a  "yes" = "${nolabels}" ]
576then
577	bannerize=cat
578fi
579
580if [ "${TERM}" != "PSR" -a "${TERM}" != "PS" ]
581then
582	bannerize=cat
583fi
584
585#####
586#
587# Print banner page before job unless PSR or PS
588#
589#####
590
591
592## Skip this for PS/PSR printers, since lp.tsol_separator handles the banners
593if [ "no" = "${nobanner}" -a "${TERM}" != "PSR" -a "${TERM}" != "PS" ]
594then
595	(
596		eval ${NETPR} ${LPTMPDIR}/small_banner.$$ 2>&1
597		echo $? > ${LPTMPDIR}/pr_eexit_code.$$
598	) | ${LPTELL} ${LPTELL_OPTS} ${printer}
599
600	exit_code=`cat ${LPTMPDIR}/pr_eexit_code.$$`
601	logger -p lpr.debug -t \
602	    "tsol_netstandard_foomatic: ${request_id}" \
603	    "banner page exit code : ${exit_code}"
604
605fi
606
607i=1
608while [ $i -le $copies ]
609do
610        for file in ${files}
611        do
612                if [ -r "${file}" ]
613                then
614
615			if [ ! -z "${FILTER}" ]
616			then
617 				(
618					#####
619					# There is a filter, use it
620					#
621					# Put 0<${file} before the "eval" to keep
622					# clever users from giving a file name that
623					# evaluates as something to execute.
624					# Redirect stderr to stdout so LPTELL will
625					# get error messages from pipe.
626					#####
627
628					0<${file} $bannerize | eval ${FILTER} 2>&1 1>${tmpfile}
629					echo $? > ${LPTMPDIR}/pr_eexit_code.$$
630				) | ${LPTELL} ${LPTELL_OPTS} ${printer}
631
632				# if lp.tsol_separator had an error,
633				# send its logged error message to LPTELL.
634				banner_exit_code=`cat ${BANNER_EXIT_CODE}`
635				if [ -n "${banner_exit_code}" -a \
636					0 -ne "${banner_exit_code}" -a \
637					 -n "${LPTELL}" -a \
638					-r "${TSOLSEPARATOR_LOG}" ]
639				then
640					cat ${TSOLSEPARATOR_LOG} | ${LPTELL} ${printer}
641					echo 77 > ${LPTMPDIR}/pr_eexit_code
642				fi
643
644				exit_code=`cat ${LPTMPDIR}/pr_eexit_code.$$`
645				logger -p lpr.debug -t \
646				    "tsol_netstandard_foomatic: ${request_id}" \
647				    "filter exit_code : ${exit_code}"
648
649 			 	if [ -n "${exit_code}" ]
650 				then
651					if [ "${exit_code}" -eq 0 ]
652					then
653						printfile=${tmpfile}
654					else
655						####
656						# The filter did not succeed, so don't try to print
657						####
658							printfile=
659					fi
660				fi
661
662			else
663				printfile=${file}
664			fi
665
666			logger -p lpr.debug -t \
667			    "tsol_netstandard_foomatic: ${request_id}" \
668			    "printfile : ${printfile}"
669
670			#####
671			# Print the file
672			#####
673
674			if [ -r "${printfile}" ]
675			then
676				(
677logger -p lpr.debug -t \
678    "@1 tsol_netstandard_foomatic: printfile = ${printfile}" ""
679logger -p lpr.debug -t \
680    "tsol_netstandard_foomatic: ${NETPR} ${printfile}" ""
681					#eval ${NETPR} ${printfile} 2>&1
682					cat ${printfile} | ${PPDFILTER} \
683	      ${request_id} ${user_name} "${title}" ${copies} "${option_list}" \
684						> ${tmpfilefoo} 2> /dev/null
685					eval ${NETPR} ${tmpfilefoo} 2>&1
686					echo $? > ${LPTMPDIR}/pr_eexit_code.$$
687					/bin/rm -f ${tmpfilefoo}
688				) | ${LPTELL} ${LPTELL_OPTS} ${printer}
689
690				exit_code=`cat ${LPTMPDIR}/pr_eexit_code.$$`
691				logger -p lpr.debug -t \
692				    "@2 netstandard_foomatic: ${request_id}" \
693				    "netpr exit_code : ${exit_code}"
694
695#				if [ -f "${tmpfile}" ]
696#				then
697#					/bin/rm "${tmpfile}"
698#				fi
699
700				if [ -n "${exit_code}" ]
701				then
702					if [ "${exit_code}" -eq 0 ]
703					then
704						printone=yes
705					else
706						if [ "${exit_code}" -lt 128 ]
707						then
708							noprint=yes
709						else
710							retry=yes
711						fi
712					fi
713				fi
714
715
716			else
717
718				errmsg WARNING ${E_IP_BADFILE} \
719				"cannot read temporary file \"${printfile}\""\
720					"see if file still exists,
721			or consult your system administrator;
722			printing continues"
723
724			fi
725		else
726
727                        #####
728                        #
729                        # Don't complain about not being able to read
730                        # a file on second and subsequent copies, unless
731                        # we've not complained yet. This removes repeated
732                        # messages about the same file yet reduces the
733                        # chance that the user can remove a file and not
734                        # know that we had trouble finding it.
735                        #####
736
737                        if [ "${i}" -le 1 -o -z "${badfileyet}" ]
738                        then
739                                errmsg WARNING ${E_IP_BADFILE} \
740                                        "cannot read file \"${file}\"" \
741                                        "see if the file still exists and is readable,
742                or consult your system administrator;
743                printing continues"
744                                badfileyet=yes
745                        fi
746
747		fi
748
749# for file in ${files}
750	done
751	i=`expr $i + 1`
752done
753
754#####
755#
756# If printing in reverse order, print the banner page now
757# Skip this for TSOL, since lp.tsol_separator handles the banners
758#
759#####
760
761#if [ "no" = "${nobanner}" -a "${TERM}" = "PSR" ]
762#then
763#(
764#	eval ${NETPR} ${LPTMPDIR}/small_banner.$$ 2>&1
765#	echo $? > ${LPTMPDIR}/pr_eexit_code.$$
766#) | ${LPTELL} ${LPTELL_OPTS} ${printer}
767#fi
768
769exit_code=`cat ${LPTMPDIR}/pr_eexit_code.$$`
770logger -p lpr.debug -t "tsol_netstandard_foomatic: ${request_id}" \
771    "banner page exit code : ${exit_code}"
772
773if [ -n "${printone}" -a -z "${retry}" -a -z "${noprint}" ]
774then
775       	exit_code=`expr 0`
776else
777        if [ -n "${retry}" -a -z "${printone}" -a -z "${noprint}" ]
778        then
779                exit_code=`expr 129`
780        else
781		exit_code=`expr 1`
782	fi
783fi
784
785logger -p lpr.debug -t "tsol_netstandard_foomatic: ${request_id}" \
786    "FINAL exit_code : ${exit_code}"
787
788exit_clean ${exit_code}
789