xref: /illumos-gate/usr/src/cmd/print/bsd-sysv-commands/in.lpd.c (revision 4764d912222e53f8386bae7bf491f5780fd102ec)
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 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  */
27 
28 /* $Id: in.lpd.c 170 2006-05-20 05:58:49Z njacobs $ */
29 
30 #pragma ident	"%Z%%M%	%I%	%E% SMI"
31 
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <fcntl.h>
36 #include <stdarg.h>
37 #include <string.h>
38 #include <ctype.h>
39 #include <errno.h>
40 #include <syslog.h>
41 #include <libintl.h>
42 #include <pwd.h>
43 #include <grp.h>
44 #include <sys/types.h>
45 #include <sys/stat.h>
46 #include <sys/socket.h>
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
49 #include <netdb.h>
50 #include <sys/systeminfo.h>
51 
52 #include <papi.h>
53 #include <uri.h>
54 #include "common.h"
55 
56 #define	ACK(fp)		{ (void) fputc('\0', fp); (void) fflush(fp); }
57 #define	NACK(fp)	{ (void) fputc('\1', fp); (void) fflush(fp); }
58 
59 /*
60  * This file contains the front-end of the BSD Print Protocol adaptor.  This
61  * code assumes a BSD Socket interface to the networking side.
62  */
63 
64 static char *
65 remote_host_name(FILE *fp)
66 {
67 	struct hostent *hp;
68 	struct sockaddr_in6 peer;
69 	socklen_t peer_len = sizeof (peer);
70 	int fd = fileno(fp);
71 	int error_num;
72 	char tmp_buf[INET6_ADDRSTRLEN];
73 	char *hostname;
74 
75 	/* who is our peer ? */
76 	if (getpeername(fd, (struct sockaddr *)&peer, &peer_len) < 0) {
77 		if ((errno != ENOTSOCK) && (errno != EINVAL))
78 			return (NULL);
79 		else
80 			return (strdup("localhost"));
81 	}
82 
83 	/* get their name or return a string containing their address */
84 	if ((hp = getipnodebyaddr((const char *)&peer.sin6_addr,
85 				sizeof (struct in6_addr), AF_INET6,
86 				&error_num)) == NULL) {
87 		return (strdup(inet_ntop(peer.sin6_family,
88 			&peer.sin6_addr, tmp_buf, sizeof (tmp_buf))));
89 	}
90 
91 	hostname = strdup(hp->h_name);
92 	if (is_localhost(hp->h_name) != 0)
93 		return (strdup("localhost"));
94 
95 	/* It must be someone else */
96 	return (hostname);
97 }
98 
99 static void
100 fatal(FILE *fp, char *fmt, ...)
101 {
102 	va_list ap;
103 
104 	va_start(ap, fmt);
105 	vsyslog(LOG_DEBUG, fmt, ap);
106 	vfprintf(fp, fmt, ap);
107 	va_end(ap);
108 	exit(1);
109 }
110 
111 static void
112 cleanup(char ***files, char **cf)
113 {
114 	if (*files != NULL) {
115 		int i;
116 
117 		for (i = 0; (*files)[i] != NULL; i++) {
118 			(void) unlink((*files)[i]);
119 			free((*files)[i]);
120 		}
121 		free(*files);
122 		*files = NULL;
123 	}
124 
125 	if (*cf != NULL) {
126 		free(*cf);
127 		*cf = NULL;
128 	}
129 }
130 
131 static papi_attribute_t **
132 parse_cf(papi_service_t svc, char *cf, char **files)
133 {
134 	papi_attribute_t **list = NULL;
135 	char	previous = NULL,
136 		*entry;
137 	int	copies_set = 0,
138 		copies = 0;
139 
140 	for (entry = strtok(cf, "\n"); entry != NULL;
141 	    entry = strtok(NULL, "\n")) {
142 		char *format = NULL;
143 
144 		/* count the copies */
145 		if ((entry[0] >= 'a') && (entry[0] <= 'z') &&
146 		    (copies_set == 0) && (previous == entry[0]))
147 			copies++;
148 		else if ((previous >= 'a') && (previous <= 'z'))
149 			copies_set = 1;
150 		previous = entry[0];
151 
152 		/* process the control message */
153 		switch (entry[0]) {
154 		/* RFC-1179 options */
155 		case 'J':	/* RFC-1179 Banner Job Name */
156 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
157 					"job-name", ++entry);
158 			break;
159 		case 'C':	/* RFC-1179 Banner Class Name */
160 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
161 					"rfc-1179-class", ++entry);
162 			break;
163 		case 'L':	/* RFC-1179 Banner toggle  */
164 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
165 					"job-sheets", "standard");
166 			break;
167 		case 'T':	/* RFC-1179 Title (pr)  */
168 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
169 					"pr-title", ++entry);
170 			break;
171 		case 'H':	/* RFC-1179 Host */
172 			/*
173 			 * use the host as known by us, not by them
174 			 *
175 			 * papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
176 			 *		"job-originating-host-name", ++entry);
177 			 */
178 			break;
179 		case 'P':	/* RFC-1179 User */
180 			++entry;
181 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
182 					"requesting-user-name", entry);
183 			papiServiceSetUserName(svc, entry);
184 			break;
185 		case 'M':	/* RFC-1179 Mail to User */
186 			papiAttributeListAddBoolean(&list, PAPI_ATTR_EXCL,
187 				"rfc-1179-mail", 1);
188 			break;
189 		case 'W':	/* RFC-1179 Width (pr) */
190 			papiAttributeListAddInteger(&list, PAPI_ATTR_EXCL,
191 					"pr-width", atoi(++entry));
192 			break;
193 		case 'I':	/* RFC-1179 Indent (pr) */
194 			papiAttributeListAddInteger(&list, PAPI_ATTR_EXCL,
195 					"pr-indent", atoi(++entry));
196 			break;
197 		case 'N':	/* RFC-1179 Filename */
198 			/* could have HPUX extension embedded */
199 			if (entry[1] != ' ') {	/* real pathname */
200 #ifdef DEBUG
201 				papiAttributeListAddString(&list,
202 						PAPI_ATTR_EXCL,
203 						"flist", ++entry);
204 #endif
205 			} else if (entry[2] == 'O') /* HPUX lp -o options */
206 				papiAttributeListFromString(&list,
207 						PAPI_ATTR_APPEND, ++entry);
208 			break;
209 		case 'U':	/* RFC-1179 Unlink */
210 			break;	/* ignored */
211 		case '1':	/* RFC-1179 TROFF Font R */
212 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
213 				"rfc-1179-font-r", ++entry);
214 			break;
215 		case '2':	/* RFC-1179 TROFF Font I */
216 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
217 				"rfc-1179-font-i", ++entry);
218 			break;
219 		case '3':	/* RFC-1179 TROFF Font B */
220 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
221 				"rfc-1179-font-b", ++entry);
222 			break;
223 		case '4':	/* RFC-1179 TROFF Font S */
224 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
225 				"rfc-1179-font-s", ++entry);
226 			break;
227 		case 'f':	/* RFC-1179 ASCII file (print) */
228 			format = "text/plain";
229 			if (is_postscript(files[0]) == 1)
230 				format = "application/postscript";
231 			break;
232 		case 'l':	/* RFC-1179 CATV file (print) */
233 			format = "application/octet-stream";
234 			if (is_postscript(files[0]) == 1)
235 				format = "application/postscript";
236 			break;
237 		case 'o':	/* RFC-1179 Postscript file (print) */
238 			format = "application/postscript";
239 			break;
240 		case 'p':	/* RFC-1179 PR file (print) */
241 			format = "application/x-pr";
242 			papiAttributeListAddBoolean(&list, PAPI_ATTR_EXCL,
243 					"pr-filter", 1);
244 			break;
245 		case 't':	/* RFC-1179 TROFF file (print) */
246 			format = "application/x-troff";
247 			break;
248 		case 'n':	/* RFC-1179 DITROFF file (print) */
249 			format = "application/x-ditroff";
250 			break;
251 		case 'd':	/* RFC-1179 DVI file (print) */
252 			format = "application/x-dvi";
253 			break;
254 		case 'g':	/* RFC-1179 GRAPH file (print) */
255 			format = "application/x-plot";
256 			break;
257 		case 'c':	/* RFC-1179 CIF file (print) */
258 			format = "application/x-cif";
259 			break;
260 		case 'v':	/* RFC-1179 RASTER file (print) */
261 			format = "application/x-raster";
262 			break;
263 		case 'r':	/* RFC-1179 FORTRAN file (print) */
264 			format = "application/x-fortran";
265 			break;
266 		/* Sun Solaris Extensions */
267 		case 'O':
268 			++entry;
269 			{
270 				int rd, wr;
271 
272 				for (rd = wr = 0; entry[rd] != '\0'; rd++) {
273 					if (entry[rd] == '"')
274 						continue;
275 					if (rd != wr)
276 						entry[wr] = entry[rd];
277 					wr++;
278 				}
279 				entry[wr] = '\0';
280 
281 				papiAttributeListFromString(&list,
282 				    PAPI_ATTR_APPEND, entry);
283 			}
284 			break;
285 		case '5':
286 			++entry;
287 			switch (entry[0]) {
288 			case 'f':	/* Solaris form */
289 				papiAttributeListAddString(&list,
290 						PAPI_ATTR_EXCL,
291 						"form", ++entry);
292 				break;
293 			case 'H':	/* Solaris handling */
294 				++entry;
295 				if (strcasecmp(entry, "hold") == 0)
296 					papiAttributeListAddString(&list,
297 						PAPI_ATTR_EXCL,
298 						"job-hold-until", "indefinite");
299 				else if (strcasecmp(entry, "immediate") == 0)
300 					papiAttributeListAddString(&list,
301 						PAPI_ATTR_EXCL,
302 						"job-hold-until", "no-hold");
303 				else
304 					papiAttributeListAddString(&list,
305 						PAPI_ATTR_EXCL,
306 						"job-hold-until", entry);
307 				break;
308 			case 'p':	/* Solaris notification */
309 				papiAttributeListAddBoolean(&list,
310 					PAPI_ATTR_EXCL, "rfc-1179-mail", 1);
311 				break;
312 			case 'P': {	/* Solaris page list */
313 				char buf[BUFSIZ];
314 
315 				snprintf(buf, sizeof (buf), "page-ranges=%s",
316 						++entry);
317 				papiAttributeListFromString(&list,
318 						PAPI_ATTR_EXCL, buf);
319 				}
320 				break;
321 			case 'q': {	/* Solaris priority */
322 				int i = atoi(optarg);
323 
324 				i = 100 - (i * 2.5);
325 				if ((i < 1) || (i > 100))
326 					i = 50;
327 				papiAttributeListAddInteger(&list,
328 					PAPI_ATTR_EXCL, "job-priority", i);
329 				}
330 				break;
331 			case 'S':	/* Solaris character set */
332 				papiAttributeListAddString(&list,
333 					PAPI_ATTR_EXCL, "lp-charset",
334 					++entry);
335 				break;
336 			case 'T':	/* Solaris type */
337 				format = lp_type_to_mime_type(++entry);
338 				break;
339 			case 'y':	/* Solaris mode */
340 				papiAttributeListAddString(&list,
341 					PAPI_ATTR_APPEND, "lp-modes", ++entry);
342 				break;
343 			default:
344 				syslog(LOG_INFO|LOG_DEBUG,
345 					"Warning: cf message (%s) ignored",
346 					entry);
347 				break;
348 			}
349 			break;
350 		/* Undefined Extensions: SCO, Ultrix, AIX, ... */
351 
352 		default:
353 			syslog(LOG_INFO|LOG_DEBUG,
354 				"Warning: cf message (%s) ignored", entry);
355 			break;
356 		}
357 
358 		if (format != NULL)
359 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
360 				"document-format", format);
361 	}
362 
363 	papiAttributeListAddInteger(&list, PAPI_ATTR_EXCL,
364 					"copies", ++copies);
365 	papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
366 					"job-sheets", "none");
367 
368 	return (list);
369 }
370 
371 static papi_status_t
372 submit_job(papi_service_t svc, FILE *ifp, char *printer, int rid, char *cf,
373 		char **files)
374 {
375 	papi_attribute_t **list = NULL;
376 	papi_status_t status;
377 	papi_job_t job = NULL;
378 	char *format = "";
379 
380 	if ((list = parse_cf(svc, cf, files)) != NULL) {
381 		/* use the host as known by us, not by them */
382 		char *host = remote_host_name(ifp);
383 
384 		if (host != NULL) {
385 			papiAttributeListAddString(&list, PAPI_ATTR_REPLACE,
386 					"job-originating-host-name", host);
387 			free(host);
388 		}
389 		if (rid > 0) {
390 			papiAttributeListAddInteger(&list, PAPI_ATTR_EXCL,
391 					"job-id-requested", rid);
392 		}
393 	}
394 
395 	status = papiJobSubmit(svc, printer, list, NULL, files, &job);
396 	syslog(LOG_DEBUG, "submit: %s", papiStatusString(status));
397 	if (status != PAPI_OK) {
398 		char *tmp = papiServiceGetStatusMessage(svc);
399 
400 		syslog(LOG_DEBUG, "submit-detail: %s", tmp ? tmp : "none");
401 	}
402 	papiJobFree(job);
403 
404 	return (status);
405 }
406 
407 static char *
408 receive_control_file(papi_service_t svc, FILE *ifp, FILE *ofp, int size)
409 {
410 	char *ptr, *cf_data;
411 
412 	if ((ptr = cf_data = calloc(1, size + 1)) == NULL) {
413 		NACK(ofp);
414 		return (NULL);
415 	} else
416 		ACK(ofp);
417 
418 	while (size > 0) {
419 		int rc;
420 
421 		if (((rc = fread(ptr, 1, size, ifp)) == 0) &&
422 		    (feof(ifp) != 0)) {
423 			free(cf_data);
424 			return (NULL);
425 		} else {
426 			ptr += rc;
427 			size -= rc;
428 		}
429 	}
430 	syslog(LOG_DEBUG, "cf_data(%s)", cf_data);
431 
432 	if (fgetc(ifp) != 0) {
433 		free(cf_data);
434 		return (NULL);
435 	}
436 	ACK(ofp);
437 
438 	return (cf_data);
439 }
440 
441 static char *
442 receive_data_file(FILE *ifp, FILE *ofp, int size)
443 {
444 	char file[] = "lpdXXXXXX";
445 	char buf[BUFSIZ];
446 	int fd;
447 
448 	if ((fd = mkstemp(file)) < 0) {
449 		NACK(ofp);
450 		return (NULL);
451 	} else
452 		ACK(ofp);
453 
454 	while (size > 0) {
455 		int rc = ((size > BUFSIZ) ? BUFSIZ : size);
456 
457 		if (((rc = fread(buf, 1, rc, ifp)) == 0) &&
458 		    (feof(ifp) != 0)) {
459 			close(fd);
460 			unlink(file);
461 			return (NULL);
462 		} else {
463 			char *ptr = buf;
464 
465 			while (rc > 0) {
466 				int wrc = write(fd, ptr, rc);
467 
468 				if (wrc < 0) {
469 					close(fd);
470 					unlink(file);
471 					return (NULL);
472 				}
473 
474 				ptr += wrc;
475 				size -= wrc;
476 				rc -= wrc;
477 			}
478 		}
479 	}
480 	close(fd);
481 	if (fgetc(ifp) != 0) {
482 		unlink(file);
483 		return (NULL);
484 	}
485 	ACK(ofp);
486 
487 	return (strdup(file));
488 }
489 
490 static papi_status_t
491 berkeley_receive_files(papi_service_t svc, FILE *ifp, FILE *ofp, char *printer)
492 {
493 	papi_status_t status = PAPI_OK;
494 	char *file, **files = NULL;	/* the job data files */
495 	char *cf = NULL;
496 	int rid = 0;
497 	char buf[BUFSIZ];
498 
499 	while (fgets(buf, sizeof (buf), ifp) != NULL) {
500 		int size;
501 
502 		syslog(LOG_DEBUG, "XFER CMD: (%d)%s\n", buf[0], &buf[1]);
503 #ifdef DEBUG	/* translate [1-3]... messages to \[1-3] to run by hand */
504 		if ((buf[0] > '0') && (buf[0] < '4'))
505 			buf[0] -= '0';
506 #endif
507 		switch (buf[0]) {
508 		case 0x01:	/* Abort */
509 			cleanup(&files, &cf);
510 			break;
511 		case 0x02: {	/* Receive control file */
512 			if (((cf = strchr(buf, ' ')) != NULL) &&
513 			    (strlen(cf) > 4)) {
514 				while ((*cf != NULL) && (isdigit(*cf) == 0))
515 					cf++;
516 				rid = atoi(cf);
517 			}
518 			cf = receive_control_file(svc, ifp, ofp, atoi(&buf[1]));
519 			if (cf == NULL) {
520 				cleanup(&files, &cf);
521 				return (PAPI_BAD_REQUEST);
522 			} else if (files != NULL) {
523 				status = submit_job(svc, ifp, printer, rid, cf,
524 							files);
525 				cleanup(&files, &cf);
526 			}
527 			}
528 			break;
529 		case 0x03: {	/* Receive data file */
530 			file = receive_data_file(ifp, ofp, atoi(&buf[1]));
531 			if (file == NULL) {
532 				cleanup(&files, &cf);
533 				return (PAPI_TEMPORARY_ERROR);
534 			}
535 			list_append(&files, file);
536 			}
537 			break;
538 		default:
539 			cleanup(&files, &cf);
540 			fatal(ofp, "protocol screwup");
541 			break;
542 		}
543 	}
544 
545 	if ((cf != NULL) && (files != NULL))
546 		status = submit_job(svc, ifp, printer, rid, cf, files);
547 
548 	cleanup(&files, &cf);
549 
550 	return (status);
551 }
552 
553 static papi_status_t
554 berkeley_transfer_files(papi_service_t svc, FILE *ifp, FILE *ofp,
555 		char *printer)
556 {
557 	papi_status_t status;
558 	papi_printer_t p = NULL;
559 	char *keys[] = { "printer-is-accepting-jobs", NULL };
560 
561 	status = papiPrinterQuery(svc, printer, keys, NULL, &p);
562 	if ((status == PAPI_OK) && (p != NULL)) {
563 		papi_attribute_t **attrs = papiPrinterGetAttributeList(p);
564 		char accepting = PAPI_FALSE;
565 
566 		papiAttributeListGetBoolean(attrs, NULL,
567 				"printer-is-accepting-jobs", &accepting);
568 
569 		if (accepting == PAPI_TRUE) {
570 			ACK(ofp);
571 			status = berkeley_receive_files(svc, ifp, ofp, printer);
572 		} else
573 			NACK(ofp);
574 
575 		papiPrinterFree(p);
576 	} else
577 		NACK(ofp);
578 
579 	return (status);
580 }
581 
582 static int
583 cyclical_service_check(char *svc_name)
584 {
585 	papi_attribute_t **list;
586 	uri_t *uri = NULL;
587 	char *s = NULL;
588 
589 	/* was there a printer? */
590 	if (svc_name == NULL)
591 		return (0);
592 
593 	if ((list = getprinterbyname(svc_name, NULL)) == NULL)
594 		return (0);	/* if it doesnt' resolve, we will fail later */
595 
596 	papiAttributeListGetString(list, NULL, "printer-uri-supported", &s);
597 	if ((s == NULL) || (strcasecmp(svc_name, s) != 0))
598 		return (0);	/* they don't match */
599 
600 	/* is it in uri form? */
601 	if (uri_from_string(s, &uri) < 0)
602 		return (0);
603 
604 	if ((uri == NULL) || (uri->scheme == NULL) || (uri->host == NULL)) {
605 		uri_free(uri);
606 		return (0);
607 	}
608 
609 	/* is it in lpd form? */
610 	if (strcasecmp(uri->scheme, "lpd") != 0) {
611 		uri_free(uri);
612 		return (0);
613 	}
614 
615 	/* is it the local host? */
616 	if (is_localhost(uri->host) != 0) {
617 		uri_free(uri);
618 		return (0);
619 	}
620 
621 	uri_free(uri);
622 	return (1);
623 }
624 
625 
626 /*
627  * This is the entry point for this program.  The program takes the
628  * following options:
629  * 	(none)
630  */
631 int
632 main(int ac, char *av[])
633 {
634 	papi_status_t status;
635 	papi_service_t svc = NULL;
636 	papi_encryption_t encryption = PAPI_ENCRYPT_NEVER;
637 	FILE	*ifp = stdin,
638 		*ofp = stdout;
639 	int	c;
640 	char	buf[BUFSIZ],
641 		**args,
642 		*printer,
643 		*run_dir = "/var/run/in.lpd",
644 		*run_user = NULL;
645 	struct passwd *pw = NULL;
646 
647 	(void) chdir("/tmp");		/* run in /tmp by default */
648 	openlog("bsd-gw", LOG_PID, LOG_LPR);
649 
650 	while ((c = getopt(ac, av, "Ed:u:")) != EOF)
651 		switch (c) {
652 		case 'E':
653 			encryption = PAPI_ENCRYPT_ALWAYS;
654 			break;
655 		case 'd':	/* run where they tell you */
656 			run_dir = optarg;
657 			break;
658 		case 'u':	/* run as */
659 			run_user = optarg;
660 			break;
661 		default:
662 			;
663 		}
664 
665 	if (run_user != NULL)	/* get the requested user info */
666 		pw = getpwnam(run_user);
667 
668 	if (run_dir != NULL) {	/* setup the run_dir */
669 		(void) mkdir(run_dir, 0700);
670 		if (pw != NULL)
671 			(void) chown(run_dir, pw->pw_uid, pw->pw_gid);
672 	}
673 
674 	if (pw != NULL) {	/* run as the requested user */
675 		syslog(LOG_DEBUG, "name: %s, uid: %d, gid: %d",
676 				pw->pw_name, pw->pw_uid, pw->pw_gid);
677 		initgroups(pw->pw_name, pw->pw_gid);
678 		setgid(pw->pw_gid);
679 		setuid(pw->pw_uid);
680 	}
681 
682 	if (run_dir != NULL)	/* move to the run_dir */
683 		if (chdir(run_dir) < 0) {
684 			syslog(LOG_DEBUG, "failed to chdir(%s)", run_dir);
685 			exit(1);
686 		}
687 
688 	syslog(LOG_DEBUG, "$CWD = %s", getwd(NULL));
689 
690 	if (fgets(buf, sizeof (buf), ifp) == NULL) {
691 		if (feof(ifp) == 0)
692 			syslog(LOG_ERR, "Error reading from connection: %s",
693 				strerror(errno));
694 		exit(1);
695 	}
696 
697 	syslog(LOG_DEBUG, "CMD: (%d)%s\n", buf[0], &buf[1]);
698 
699 #ifdef DEBUG	/* translate [1-5]... messages to \[1-5] to run by hand */
700 	if ((buf[0] > '0') && (buf[0] < '6'))
701 		buf[0] -= '0';
702 #endif
703 
704 	if ((buf[0] < 1) || (buf[0] > 5)) {
705 		fatal(ofp, "Invalid protocol request (%d): %c%s\n",
706 			buf[0], buf[0], buf);
707 		exit(1);
708 	}
709 
710 	args = strsplit(&buf[1], "\t\n ");
711 	printer = *args++;
712 
713 	if (printer == NULL) {
714 		fatal(ofp, "Can't determine requested printer");
715 		exit(1);
716 	}
717 
718 	if (cyclical_service_check(printer) != 0) {
719 		fatal(ofp, "%s is cyclical\n", printer);
720 		exit(1);
721 	}
722 
723 	status = papiServiceCreate(&svc, printer, NULL, NULL, NULL,
724 					encryption, NULL);
725 	if (status != PAPI_OK) {
726 		fatal(ofp, "Failed to contact service for %s: %s\n", printer,
727 			verbose_papi_message(svc, status));
728 		exit(1);
729 	}
730 
731 	/*
732 	 * Trusted Solaris can't be trusting of intermediaries.  Pass
733 	 * the socket connection to the print service to retrieve the
734 	 * sensativity label off of a multi-level port.
735 	 */
736 	(void) papiServiceSetPeer(svc, fileno(ifp));
737 
738 	switch (buf[0]) {
739 	case '\1':	/* restart printer */
740 		ACK(ofp);	/* there is no equivalent */
741 		break;
742 	case '\2':	/* transfer job(s) */
743 		status = berkeley_transfer_files(svc, ifp, ofp, printer);
744 		break;
745 	case '\3':	/* show queue (short) */
746 	case '\4': {	/* show queue (long) */
747 		int count;
748 
749 		for (count = 0; args[count] != 0; count++);
750 
751 		berkeley_queue_report(svc, ofp, printer, buf[0], count, args);
752 		}
753 		break;
754 	case '\5': {	/* cancel job(s) */
755 		char *user = *args++;
756 		char *host = remote_host_name(ifp);
757 		int count;
758 
759 		if (host != NULL) {
760 			char buf[BUFSIZ];
761 
762 			snprintf(buf, sizeof (buf), "%s@%s", user, host);
763 			status = papiServiceSetUserName(svc, buf);
764 		} else
765 			status = papiServiceSetUserName(svc, user);
766 
767 		for (count = 0; args[count] != 0; count++);
768 
769 		berkeley_cancel_request(svc, ofp, printer, count, args);
770 		}
771 		break;
772 	default:
773 		fatal(ofp, "unsupported protocol request (%c), %s",
774 			buf[0], &buf[1]);
775 	}
776 
777 	(void) fflush(ofp);
778 
779 	syslog(LOG_DEBUG, "protocol request(%d) for %s completed: %s",
780 		buf[0], printer, papiStatusString(status));
781 	if (status != PAPI_OK)
782 		syslog(LOG_DEBUG, "detail: %s",
783 				verbose_papi_message(svc, status));
784 
785 	papiServiceDestroy(svc);
786 
787 	return (0);
788 }
789