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