12264Sjacobs /*
22264Sjacobs  * CDDL HEADER START
32264Sjacobs  *
42264Sjacobs  * The contents of this file are subject to the terms of the
52264Sjacobs  * Common Development and Distribution License (the "License").
62264Sjacobs  * You may not use this file except in compliance with the License.
72264Sjacobs  *
82264Sjacobs  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
92264Sjacobs  * or http://www.opensolaris.org/os/licensing.
102264Sjacobs  * See the License for the specific language governing permissions
112264Sjacobs  * and limitations under the License.
122264Sjacobs  *
132264Sjacobs  * When distributing Covered Code, include this CDDL HEADER in each
142264Sjacobs  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
152264Sjacobs  * If applicable, add the following below this CDDL HEADER, with the
162264Sjacobs  * fields enclosed by brackets "[]" replaced with your own identifying
172264Sjacobs  * information: Portions Copyright [yyyy] [name of copyright owner]
182264Sjacobs  *
192264Sjacobs  * CDDL HEADER END
202264Sjacobs  */
212264Sjacobs 
222264Sjacobs /*
232264Sjacobs  * Copyright 2006 Sun Microsystems, Inc.  All rights reserved.
242264Sjacobs  * Use is subject to license terms.
252264Sjacobs  *
262264Sjacobs  */
272264Sjacobs 
282264Sjacobs /* $Id: in.lpd.c 170 2006-05-20 05:58:49Z njacobs $ */
292264Sjacobs 
302264Sjacobs #pragma ident	"%Z%%M%	%I%	%E% SMI"
312264Sjacobs 
322264Sjacobs #include <stdio.h>
332264Sjacobs #include <stdlib.h>
34*3125Sjacobs #include <unistd.h>
35*3125Sjacobs #include <fcntl.h>
362264Sjacobs #include <stdarg.h>
372264Sjacobs #include <string.h>
382264Sjacobs #include <errno.h>
392264Sjacobs #include <syslog.h>
402264Sjacobs #include <libintl.h>
41*3125Sjacobs #include <pwd.h>
42*3125Sjacobs #include <grp.h>
43*3125Sjacobs #include <sys/types.h>
44*3125Sjacobs #include <sys/stat.h>
45*3125Sjacobs #include <sys/socket.h>
46*3125Sjacobs #include <netinet/in.h>
47*3125Sjacobs #include <arpa/inet.h>
48*3125Sjacobs #include <netdb.h>
49*3125Sjacobs #include <sys/systeminfo.h>
502264Sjacobs 
512264Sjacobs #include <papi.h>
52*3125Sjacobs #include <uri.h>
532264Sjacobs #include "common.h"
542264Sjacobs 
552264Sjacobs #define	ACK(fp)		{ (void) fputc('\0', fp); (void) fflush(fp); }
562264Sjacobs #define	NACK(fp)	{ (void) fputc('\1', fp); (void) fflush(fp); }
572264Sjacobs 
582264Sjacobs /*
592264Sjacobs  * This file contains the front-end of the BSD Print Protocol adaptor.  This
602264Sjacobs  * code assumes a BSD Socket interface to the networking side.
612264Sjacobs  */
622264Sjacobs 
63*3125Sjacobs static char *
64*3125Sjacobs remote_host_name(FILE *fp)
65*3125Sjacobs {
66*3125Sjacobs 	struct hostent *hp;
67*3125Sjacobs 	struct sockaddr_in6 peer;
68*3125Sjacobs 	socklen_t peer_len = sizeof (peer);
69*3125Sjacobs 	int fd = fileno(fp);
70*3125Sjacobs 	int error_num;
71*3125Sjacobs 	char myname[MAXHOSTNAMELEN], tmp_buf[INET6_ADDRSTRLEN];
72*3125Sjacobs 	char *hostname;
73*3125Sjacobs 
74*3125Sjacobs 	/* who is our peer ? */
75*3125Sjacobs 	if (getpeername(fd, (struct sockaddr *)&peer, &peer_len) < 0) {
76*3125Sjacobs 		if ((errno != ENOTSOCK) && (errno != EINVAL))
77*3125Sjacobs 			return (NULL);
78*3125Sjacobs 		else
79*3125Sjacobs 			return (strdup("localhost"));
80*3125Sjacobs 	}
81*3125Sjacobs 
82*3125Sjacobs 	/* get their name or return a string containing their address */
83*3125Sjacobs 	if ((hp = getipnodebyaddr((const char *)&peer.sin6_addr,
84*3125Sjacobs 				sizeof (struct in6_addr), AF_INET6,
85*3125Sjacobs 				&error_num)) == NULL) {
86*3125Sjacobs 		return (strdup(inet_ntop(peer.sin6_family,
87*3125Sjacobs 			&peer.sin6_addr, tmp_buf, sizeof (tmp_buf))));
88*3125Sjacobs 	}
89*3125Sjacobs 
90*3125Sjacobs 	/* is it "localhost" ? */
91*3125Sjacobs 	if (strcasecmp(hp->h_name, "localhost") == 0)
92*3125Sjacobs 		return (strdup("localhost"));
93*3125Sjacobs 
94*3125Sjacobs 	/* duplicate the name because gethostbyXXXX() is not reentrant */
95*3125Sjacobs 	hostname = strdup(hp->h_name);
96*3125Sjacobs 	(void) sysinfo(SI_HOSTNAME, myname, sizeof (myname));
97*3125Sjacobs 
98*3125Sjacobs 	/* is it from one of my addresses ? */
99*3125Sjacobs 	if ((hp = getipnodebyname(myname, AF_INET6, AI_ALL|AI_V4MAPPED,
100*3125Sjacobs 	    &error_num)) != NULL) {
101*3125Sjacobs 		struct in6_addr **tmp = (struct in6_addr **)hp->h_addr_list;
102*3125Sjacobs 		int i = 0;
103*3125Sjacobs 
104*3125Sjacobs 		while (tmp[i] != NULL) {
105*3125Sjacobs 			if (memcmp(tmp[i++], &peer.sin6_addr, hp->h_length)
106*3125Sjacobs 			    == 0) {
107*3125Sjacobs 				free(hostname);
108*3125Sjacobs 				return (strdup("localhost"));
109*3125Sjacobs 			}
110*3125Sjacobs 		}
111*3125Sjacobs 	}
112*3125Sjacobs 
113*3125Sjacobs 	/* It must be someone else */
114*3125Sjacobs 	return (hostname);
115*3125Sjacobs }
116*3125Sjacobs 
117*3125Sjacobs static void
1182264Sjacobs fatal(FILE *fp, char *fmt, ...)
1192264Sjacobs {
1202264Sjacobs 	va_list ap;
1212264Sjacobs 
1222264Sjacobs 	va_start(ap, fmt);
1232264Sjacobs 	vsyslog(LOG_DEBUG, fmt, ap);
1242264Sjacobs 	vfprintf(fp, fmt, ap);
1252264Sjacobs 	va_end(ap);
126*3125Sjacobs 	exit(1);
1272264Sjacobs }
1282264Sjacobs 
1292264Sjacobs static void
130*3125Sjacobs cleanup(char ***files, char **cf)
131*3125Sjacobs {
132*3125Sjacobs 	if (*files != NULL) {
133*3125Sjacobs 		int i;
134*3125Sjacobs 
135*3125Sjacobs 		for (i = 0; (*files)[i] != NULL; i++) {
136*3125Sjacobs 			(void) unlink((*files)[i]);
137*3125Sjacobs 			free((*files)[i]);
138*3125Sjacobs 		}
139*3125Sjacobs 		free(*files);
140*3125Sjacobs 		*files = NULL;
141*3125Sjacobs 	}
142*3125Sjacobs 
143*3125Sjacobs 	if (*cf != NULL) {
144*3125Sjacobs 		free(*cf);
145*3125Sjacobs 		*cf = NULL;
146*3125Sjacobs 	}
147*3125Sjacobs }
148*3125Sjacobs 
149*3125Sjacobs static papi_attribute_t **
150*3125Sjacobs parse_cf(papi_service_t svc, char *cf, char **files)
1512264Sjacobs {
152*3125Sjacobs 	papi_attribute_t **list = NULL;
153*3125Sjacobs 	char	previous = NULL,
154*3125Sjacobs 		*entry,
155*3125Sjacobs 		*s,
156*3125Sjacobs 		text[BUFSIZ];
157*3125Sjacobs 	int	count = 0,
158*3125Sjacobs 		copies_set = 0,
159*3125Sjacobs 		copies = 0;
160*3125Sjacobs 
161*3125Sjacobs 	for (entry = strtok(cf, "\n"); entry != NULL;
162*3125Sjacobs 	    entry = strtok(NULL, "\n")) {
163*3125Sjacobs 		char *format = NULL;
164*3125Sjacobs 
165*3125Sjacobs 		/* count the copies */
166*3125Sjacobs 		if ((entry[0] >= 'a') && (entry[0] <= 'z') &&
167*3125Sjacobs 		    (copies_set == 0) && (previous == entry[0]))
168*3125Sjacobs 			copies++;
169*3125Sjacobs 		else if ((previous >= 'a') && (previous <= 'z'))
170*3125Sjacobs 			copies_set = 1;
171*3125Sjacobs 		previous = entry[0];
1722264Sjacobs 
173*3125Sjacobs 		/* process the control message */
174*3125Sjacobs 		switch (entry[0]) {
175*3125Sjacobs 		/* RFC-1179 options */
176*3125Sjacobs 		case 'J':	/* RFC-1179 Banner Job Name */
177*3125Sjacobs 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
178*3125Sjacobs 					"job-name", ++entry);
179*3125Sjacobs 			break;
180*3125Sjacobs 		case 'C':	/* RFC-1179 Banner Class Name */
181*3125Sjacobs 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
182*3125Sjacobs 					"rfc-1179-class", ++entry);
183*3125Sjacobs 			break;
184*3125Sjacobs 		case 'L':	/* RFC-1179 Banner toggle  */
185*3125Sjacobs 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
186*3125Sjacobs 					"job-sheets", "standard");
187*3125Sjacobs 			break;
188*3125Sjacobs 		case 'T':	/* RFC-1179 Title (pr)  */
189*3125Sjacobs 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
190*3125Sjacobs 					"pr-title", ++entry);
191*3125Sjacobs 			break;
192*3125Sjacobs 		case 'H':	/* RFC-1179 Host */
193*3125Sjacobs 			/*
194*3125Sjacobs 			 * use the host as known by us, not by them
195*3125Sjacobs 			 *
196*3125Sjacobs 			 * papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
197*3125Sjacobs 			 *		"job-originating-host-name", ++entry);
198*3125Sjacobs 			 */
199*3125Sjacobs 			break;
200*3125Sjacobs 		case 'P':	/* RFC-1179 User */
201*3125Sjacobs 			++entry;
202*3125Sjacobs 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
203*3125Sjacobs 					"requesting-user-name", entry);
204*3125Sjacobs 			papiServiceSetUserName(svc, entry);
205*3125Sjacobs 			break;
206*3125Sjacobs 		case 'M':	/* RFC-1179 Mail to User */
207*3125Sjacobs 			papiAttributeListAddBoolean(&list, PAPI_ATTR_EXCL,
208*3125Sjacobs 				"rfc-1179-mail", 1);
209*3125Sjacobs 			break;
210*3125Sjacobs 		case 'W':	/* RFC-1179 Width (pr) */
211*3125Sjacobs 			papiAttributeListAddInteger(&list, PAPI_ATTR_EXCL,
212*3125Sjacobs 					"pr-width", atoi(++entry));
213*3125Sjacobs 			break;
214*3125Sjacobs 		case 'I':	/* RFC-1179 Indent (pr) */
215*3125Sjacobs 			papiAttributeListAddInteger(&list, PAPI_ATTR_EXCL,
216*3125Sjacobs 					"pr-indent", atoi(++entry));
217*3125Sjacobs 			break;
218*3125Sjacobs 		case 'N':	/* RFC-1179 Filename */
219*3125Sjacobs 			/* could have HPUX extension embedded */
220*3125Sjacobs 			if (entry[1] != ' ') {	/* real pathname */
221*3125Sjacobs #ifdef DEBUG
222*3125Sjacobs 				papiAttributeListAddString(&list,
223*3125Sjacobs 						PAPI_ATTR_EXCL,
224*3125Sjacobs 						"flist", ++entry);
225*3125Sjacobs #endif
226*3125Sjacobs 			} else if (entry[2] == 'O') /* HPUX lp -o options */
227*3125Sjacobs 				papiAttributeListFromString(&list,
228*3125Sjacobs 						PAPI_ATTR_APPEND, ++entry);
229*3125Sjacobs 			break;
230*3125Sjacobs 		case 'U':	/* RFC-1179 Unlink */
231*3125Sjacobs 			break;	/* ignored */
232*3125Sjacobs 		case '1':	/* RFC-1179 TROFF Font R */
233*3125Sjacobs 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
234*3125Sjacobs 				"rfc-1179-font-r", ++entry);
235*3125Sjacobs 			break;
236*3125Sjacobs 		case '2':	/* RFC-1179 TROFF Font I */
237*3125Sjacobs 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
238*3125Sjacobs 				"rfc-1179-font-i", ++entry);
239*3125Sjacobs 			break;
240*3125Sjacobs 		case '3':	/* RFC-1179 TROFF Font B */
241*3125Sjacobs 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
242*3125Sjacobs 				"rfc-1179-font-b", ++entry);
243*3125Sjacobs 			break;
244*3125Sjacobs 		case '4':	/* RFC-1179 TROFF Font S */
245*3125Sjacobs 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
246*3125Sjacobs 				"rfc-1179-font-s", ++entry);
247*3125Sjacobs 			break;
248*3125Sjacobs 		case 'f':	/* RFC-1179 ASCII file (print) */
249*3125Sjacobs 			format = "text/plain";
250*3125Sjacobs 			if (is_postscript(files[0]) == 1)
251*3125Sjacobs 				format = "application/postscript";
252*3125Sjacobs 			break;
253*3125Sjacobs 		case 'l':	/* RFC-1179 CATV file (print) */
254*3125Sjacobs 			format = "application/octet-stream";
255*3125Sjacobs 			if (is_postscript(files[0]) == 1)
256*3125Sjacobs 				format = "application/postscript";
257*3125Sjacobs 			break;
258*3125Sjacobs 		case 'o':	/* RFC-1179 Postscript file (print) */
259*3125Sjacobs 			format = "application/postscript";
260*3125Sjacobs 			break;
261*3125Sjacobs 		case 'p':	/* RFC-1179 PR file (print) */
262*3125Sjacobs 			format = "application/x-pr";
263*3125Sjacobs 			papiAttributeListAddBoolean(&list, PAPI_ATTR_EXCL,
264*3125Sjacobs 					"pr-filter", 1);
265*3125Sjacobs 			break;
266*3125Sjacobs 		case 't':	/* RFC-1179 TROFF file (print) */
267*3125Sjacobs 			format = "application/x-troff";
268*3125Sjacobs 			break;
269*3125Sjacobs 		case 'n':	/* RFC-1179 DITROFF file (print) */
270*3125Sjacobs 			format = "application/x-ditroff";
271*3125Sjacobs 			break;
272*3125Sjacobs 		case 'd':	/* RFC-1179 DVI file (print) */
273*3125Sjacobs 			format = "application/x-dvi";
274*3125Sjacobs 			break;
275*3125Sjacobs 		case 'g':	/* RFC-1179 GRAPH file (print) */
276*3125Sjacobs 			format = "application/x-plot";
277*3125Sjacobs 			break;
278*3125Sjacobs 		case 'c':	/* RFC-1179 CIF file (print) */
279*3125Sjacobs 			format = "application/x-cif";
280*3125Sjacobs 			break;
281*3125Sjacobs 		case 'v':	/* RFC-1179 RASTER file (print) */
282*3125Sjacobs 			format = "application/x-raster";
283*3125Sjacobs 			break;
284*3125Sjacobs 		case 'r':	/* RFC-1179 FORTRAN file (print) */
285*3125Sjacobs 			format = "application/x-fortran";
286*3125Sjacobs 			break;
287*3125Sjacobs 		/* Sun Solaris Extensions */
288*3125Sjacobs 		case 'O':
289*3125Sjacobs 			++entry;
290*3125Sjacobs 			do {
291*3125Sjacobs 				if (*entry != '"')
292*3125Sjacobs 					text[count++] = *entry;
293*3125Sjacobs 			} while (*entry++);
294*3125Sjacobs 			papiAttributeListFromString(&list, PAPI_ATTR_APPEND,
295*3125Sjacobs 				text);
296*3125Sjacobs 			break;
297*3125Sjacobs 		case '5':
298*3125Sjacobs 			++entry;
299*3125Sjacobs 			switch (entry[0]) {
300*3125Sjacobs 			case 'f':	/* Solaris form */
301*3125Sjacobs 				papiAttributeListAddString(&list,
302*3125Sjacobs 						PAPI_ATTR_EXCL,
303*3125Sjacobs 						"form", ++entry);
304*3125Sjacobs 				break;
305*3125Sjacobs 			case 'H':	/* Solaris handling */
306*3125Sjacobs 				++entry;
307*3125Sjacobs 				if (strcasecmp(entry, "hold") == 0)
308*3125Sjacobs 					papiAttributeListAddString(&list,
309*3125Sjacobs 						PAPI_ATTR_EXCL,
310*3125Sjacobs 						"job-hold-until", "indefinite");
311*3125Sjacobs 				else if (strcasecmp(entry, "release") == 0)
312*3125Sjacobs 					papiAttributeListAddString(&list,
313*3125Sjacobs 						PAPI_ATTR_EXCL,
314*3125Sjacobs 						"job-hold-until", "no-hold");
315*3125Sjacobs 				else if (strcasecmp(entry, "immediate") == 0)
316*3125Sjacobs 					papiAttributeListAddInteger(&list,
317*3125Sjacobs 						PAPI_ATTR_EXCL,
318*3125Sjacobs 						"job-priority", 100);
319*3125Sjacobs 				else
320*3125Sjacobs 					papiAttributeListAddString(&list,
321*3125Sjacobs 						PAPI_ATTR_EXCL,
322*3125Sjacobs 						"job-hold-until", entry);
323*3125Sjacobs 				break;
324*3125Sjacobs 			case 'p':	/* Solaris notification */
325*3125Sjacobs 				papiAttributeListAddBoolean(&list,
326*3125Sjacobs 					PAPI_ATTR_EXCL, "rfc-1179-mail", 1);
327*3125Sjacobs 				break;
328*3125Sjacobs 			case 'P':	/* Solaris page list */
329*3125Sjacobs 				papiAttributeListAddString(&list,
330*3125Sjacobs 						PAPI_ATTR_EXCL,
331*3125Sjacobs 						"page-ranges", ++entry);
332*3125Sjacobs 				break;
333*3125Sjacobs 			case 'q': {	/* Solaris priority */
334*3125Sjacobs 				int i = atoi(optarg);
335*3125Sjacobs 
336*3125Sjacobs 				i = 99 * (39 - i) / 39 + 1;
337*3125Sjacobs 				if ((i < 1) || (i > 100))
338*3125Sjacobs 					i = 50;
339*3125Sjacobs 				papiAttributeListAddInteger(&list,
340*3125Sjacobs 					PAPI_ATTR_EXCL, "priority", i);
341*3125Sjacobs 				}
342*3125Sjacobs 				break;
343*3125Sjacobs 			case 'S':	/* Solaris character set */
344*3125Sjacobs 				papiAttributeListAddString(&list,
345*3125Sjacobs 					PAPI_ATTR_EXCL, "lp-charset",
346*3125Sjacobs 					++entry);
347*3125Sjacobs 				break;
348*3125Sjacobs 			case 'T':	/* Solaris type */
349*3125Sjacobs 				format = lp_type_to_mime_type(++entry);
350*3125Sjacobs 				break;
351*3125Sjacobs 			case 'y':	/* Solaris mode */
352*3125Sjacobs 				papiAttributeListAddString(&list,
353*3125Sjacobs 					PAPI_ATTR_APPEND, "lp-modes", ++entry);
354*3125Sjacobs 				break;
355*3125Sjacobs 			default:
356*3125Sjacobs 				syslog(LOG_INFO|LOG_DEBUG,
357*3125Sjacobs 					"Warning: cf message (%s) ignored",
358*3125Sjacobs 					entry);
359*3125Sjacobs 				break;
360*3125Sjacobs 			}
361*3125Sjacobs 			break;
362*3125Sjacobs 		/* Undefined Extensions: SCO, Ultrix, AIX, ... */
363*3125Sjacobs 
364*3125Sjacobs 		default:
365*3125Sjacobs 			syslog(LOG_INFO|LOG_DEBUG,
366*3125Sjacobs 				"Warning: cf message (%s) ignored", entry);
367*3125Sjacobs 			break;
368*3125Sjacobs 		}
369*3125Sjacobs 
370*3125Sjacobs 		if (format != NULL)
371*3125Sjacobs 			papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
372*3125Sjacobs 				"document-format", format);
373*3125Sjacobs 	}
374*3125Sjacobs 
375*3125Sjacobs 	papiAttributeListAddInteger(&list, PAPI_ATTR_EXCL,
376*3125Sjacobs 					"copies", ++copies);
377*3125Sjacobs 	papiAttributeListAddString(&list, PAPI_ATTR_EXCL,
378*3125Sjacobs 					"job-sheets", "none");
379*3125Sjacobs 
380*3125Sjacobs 	return (list);
381*3125Sjacobs }
382*3125Sjacobs 
383*3125Sjacobs static papi_status_t
384*3125Sjacobs submit_job(papi_service_t svc, FILE *ifp, char *printer, char *cf, char **files)
385*3125Sjacobs {
386*3125Sjacobs 	papi_attribute_t **list = NULL;
387*3125Sjacobs 	papi_status_t status;
388*3125Sjacobs 	papi_job_t job = NULL;
389*3125Sjacobs 	char *format = "";
390*3125Sjacobs 
391*3125Sjacobs 	if ((list = parse_cf(svc, cf, files)) != NULL) {
392*3125Sjacobs 		/* use the host as known by us, not by them */
393*3125Sjacobs 		char *host = remote_host_name(ifp);
394*3125Sjacobs 
395*3125Sjacobs 		if (host != NULL) {
396*3125Sjacobs 			papiAttributeListAddString(&list, PAPI_ATTR_REPLACE,
397*3125Sjacobs 					"job-originating-host-name", host);
398*3125Sjacobs 			free(host);
399*3125Sjacobs 		}
400*3125Sjacobs 	}
401*3125Sjacobs 
402*3125Sjacobs 	status = papiJobSubmit(svc, printer, list, NULL, files, &job);
403*3125Sjacobs 	syslog(LOG_DEBUG, "submit: %s", papiStatusString(status));
404*3125Sjacobs 	if (status != PAPI_OK) {
405*3125Sjacobs 		char *tmp = papiServiceGetStatusMessage(svc);
406*3125Sjacobs 
407*3125Sjacobs 		syslog(LOG_DEBUG, "submit-detail: %s", tmp ? tmp : "none");
408*3125Sjacobs 	}
409*3125Sjacobs 	papiJobFree(job);
410*3125Sjacobs 
411*3125Sjacobs 	return (status);
412*3125Sjacobs }
413*3125Sjacobs 
414*3125Sjacobs static char *
415*3125Sjacobs receive_control_file(papi_service_t svc, FILE *ifp, FILE *ofp, int size)
416*3125Sjacobs {
417*3125Sjacobs 	char *ptr, *cf_data;
418*3125Sjacobs 
419*3125Sjacobs 	if ((ptr = cf_data = calloc(1, size + 1)) == NULL) {
420*3125Sjacobs 		NACK(ofp);
421*3125Sjacobs 		return (NULL);
422*3125Sjacobs 	} else
423*3125Sjacobs 		ACK(ofp);
424*3125Sjacobs 
425*3125Sjacobs 	while (size > 0) {
426*3125Sjacobs 		int rc;
427*3125Sjacobs 
428*3125Sjacobs 		if (((rc = fread(ptr, 1, size, ifp)) == 0) &&
429*3125Sjacobs 		    (feof(ifp) != 0)) {
430*3125Sjacobs 			free(cf_data);
431*3125Sjacobs 			return (NULL);
432*3125Sjacobs 		} else {
433*3125Sjacobs 			ptr += rc;
434*3125Sjacobs 			size -= rc;
435*3125Sjacobs 		}
436*3125Sjacobs 	}
437*3125Sjacobs 	syslog(LOG_DEBUG, "cf_data(%s)", cf_data);
438*3125Sjacobs 
439*3125Sjacobs 	if (fgetc(ifp) != 0) {
440*3125Sjacobs 		free(cf_data);
441*3125Sjacobs 		return (NULL);
442*3125Sjacobs 	}
4432264Sjacobs 	ACK(ofp);
4442264Sjacobs 
445*3125Sjacobs 	return (cf_data);
446*3125Sjacobs }
447*3125Sjacobs 
448*3125Sjacobs static char *
449*3125Sjacobs receive_data_file(FILE *ifp, FILE *ofp, int size)
450*3125Sjacobs {
451*3125Sjacobs 	char file[] = "lpdXXXXXX";
452*3125Sjacobs 	char buf[BUFSIZ];
453*3125Sjacobs 	int fd;
454*3125Sjacobs 
455*3125Sjacobs 	if ((fd = mkstemp(file)) < 0) {
456*3125Sjacobs 		NACK(ofp);
457*3125Sjacobs 		return (NULL);
458*3125Sjacobs 	} else
459*3125Sjacobs 		ACK(ofp);
460*3125Sjacobs 
461*3125Sjacobs 	while (size > 0) {
462*3125Sjacobs 		int rc = ((size > BUFSIZ) ? BUFSIZ : size);
463*3125Sjacobs 
464*3125Sjacobs 		if (((rc = fread(buf, 1, rc, ifp)) == 0) &&
465*3125Sjacobs 		    (feof(ifp) != 0)) {
466*3125Sjacobs 			close(fd);
467*3125Sjacobs 			unlink(file);
468*3125Sjacobs 			return (NULL);
469*3125Sjacobs 		} else {
470*3125Sjacobs 			char *ptr = buf;
471*3125Sjacobs 
472*3125Sjacobs 			while (rc > 0) {
473*3125Sjacobs 				int wrc = write(fd, ptr, rc);
474*3125Sjacobs 
475*3125Sjacobs 				if (wrc < 0) {
476*3125Sjacobs 					close(fd);
477*3125Sjacobs 					unlink(file);
478*3125Sjacobs 					return(NULL);
479*3125Sjacobs 				}
480*3125Sjacobs 
481*3125Sjacobs 				ptr += wrc;
482*3125Sjacobs 				size -= wrc;
483*3125Sjacobs 				rc -= wrc;
484*3125Sjacobs 			}
485*3125Sjacobs 		}
486*3125Sjacobs 	}
487*3125Sjacobs 	close(fd);
488*3125Sjacobs 	if (fgetc(ifp) != 0) {
489*3125Sjacobs 		unlink(file);
490*3125Sjacobs 		return (NULL);
491*3125Sjacobs 	}
492*3125Sjacobs 	ACK(ofp);
493*3125Sjacobs 
494*3125Sjacobs 	return (strdup(file));
495*3125Sjacobs }
496*3125Sjacobs 
497*3125Sjacobs static papi_status_t
498*3125Sjacobs berkeley_receive_files(papi_service_t svc, FILE *ifp, FILE *ofp, char *printer)
499*3125Sjacobs {
500*3125Sjacobs 	papi_status_t status = PAPI_OK;
501*3125Sjacobs 	char *file, **files = NULL;	/* the job data files */
502*3125Sjacobs 	char *cf = NULL;
503*3125Sjacobs 	char buf[BUFSIZ];
504*3125Sjacobs 
505*3125Sjacobs 	while (fgets(buf, sizeof (buf), ifp) != NULL) {
506*3125Sjacobs 		int size;
507*3125Sjacobs 
508*3125Sjacobs 		syslog(LOG_DEBUG, "XFER CMD: (%d)%s\n", buf[0], &buf[1]);
509*3125Sjacobs #ifdef DEBUG	/* translate [1-3]... messages to \[1-3] to run by hand */
510*3125Sjacobs 		if ((buf[0] > '0') && (buf[0] < '4'))
511*3125Sjacobs 			buf[0] -= '0';
512*3125Sjacobs #endif
513*3125Sjacobs 		switch (buf[0]) {
5142264Sjacobs 		case 0x01:	/* Abort */
515*3125Sjacobs 			cleanup(&files, &cf);
5162264Sjacobs 			break;
517*3125Sjacobs 		case 0x02: {	/* Receive control file */
518*3125Sjacobs 			cf = receive_control_file(svc, ifp, ofp, atoi(&buf[1]));
519*3125Sjacobs 			if (cf == NULL) {
520*3125Sjacobs 				cleanup(&files, &cf);
521*3125Sjacobs 				return (PAPI_BAD_REQUEST);
522*3125Sjacobs 			} else if (files != NULL) {
523*3125Sjacobs 				status = submit_job(svc, ifp, printer, cf,
524*3125Sjacobs 							files);
525*3125Sjacobs 				cleanup(&files, &cf);
526*3125Sjacobs 			}
527*3125Sjacobs 			}
5282264Sjacobs 			break;
5292264Sjacobs 		case 0x03: {	/* Receive data file */
530*3125Sjacobs 			file = receive_data_file(ifp, ofp, atoi(&buf[1]));
531*3125Sjacobs 			if (file == NULL) {
532*3125Sjacobs 				cleanup(&files, &cf);
533*3125Sjacobs 				return (PAPI_TEMPORARY_ERROR);
534*3125Sjacobs 			}
535*3125Sjacobs 			list_append(&files, file);
5362264Sjacobs 			}
5372264Sjacobs 			break;
5382264Sjacobs 		default:
539*3125Sjacobs 			cleanup(&files, &cf);
5402264Sjacobs 			fatal(ofp, "protocol screwup");
5412264Sjacobs 			break;
5422264Sjacobs 		}
5432264Sjacobs 	}
5442264Sjacobs 
545*3125Sjacobs 	if ((cf != NULL) && (files != NULL))
546*3125Sjacobs 		status = submit_job(svc, ifp, printer, cf, files);
547*3125Sjacobs 
548*3125Sjacobs 	cleanup(&files, &cf);
549*3125Sjacobs 
550*3125Sjacobs 	return (status);
5512264Sjacobs }
5522264Sjacobs 
553*3125Sjacobs static papi_status_t
5542264Sjacobs berkeley_transfer_files(papi_service_t svc, FILE *ifp, FILE *ofp,
5552264Sjacobs 		char *printer)
5562264Sjacobs {
5572264Sjacobs 	papi_status_t status;
5582264Sjacobs 	papi_printer_t p = NULL;
559*3125Sjacobs 	char *keys[] = { "printer-is-accepting-jobs", NULL };
5602264Sjacobs 
5612264Sjacobs 	status = papiPrinterQuery(svc, printer, keys, NULL, &p);
5622264Sjacobs 	if ((status == PAPI_OK) && (p != NULL)) {
5632264Sjacobs 		papi_attribute_t **attrs = papiPrinterGetAttributeList(p);
5642264Sjacobs 		char accepting = PAPI_FALSE;
5652264Sjacobs 
5662264Sjacobs 		papiAttributeListGetBoolean(attrs, NULL,
567*3125Sjacobs 				"printer-is-accepting-jobs", &accepting);
5682264Sjacobs 
569*3125Sjacobs 		if (accepting == PAPI_TRUE) {
570*3125Sjacobs 			ACK(ofp);
571*3125Sjacobs 			status = berkeley_receive_files(svc, ifp, ofp, printer);
572*3125Sjacobs 		} else
5732264Sjacobs 			NACK(ofp);
5742264Sjacobs 
5752264Sjacobs 		papiPrinterFree(p);
5762264Sjacobs 	} else
5772264Sjacobs 		NACK(ofp);
578*3125Sjacobs 
579*3125Sjacobs 	return (status);
5802264Sjacobs }
5812264Sjacobs 
582*3125Sjacobs static int
583*3125Sjacobs cyclical_service_check(char *svc_name)
584*3125Sjacobs {
585*3125Sjacobs 	papi_attribute_t **list;
586*3125Sjacobs 	char buf[BUFSIZ];
587*3125Sjacobs 	uri_t *uri = NULL;
588*3125Sjacobs 	char *s = NULL;
589*3125Sjacobs 
590*3125Sjacobs 	/* was there a printer? */
591*3125Sjacobs 	if (svc_name == NULL)
592*3125Sjacobs 		return (0);
593*3125Sjacobs 
594*3125Sjacobs 	if ((list = getprinterbyname(svc_name, NULL)) == NULL)
595*3125Sjacobs 		return (0);     /* if it doesnt' resolve, we will fail later */
596*3125Sjacobs 
597*3125Sjacobs 	papiAttributeListGetString(list, NULL, "printer-uri-supported", &s);
598*3125Sjacobs 	if ((s == NULL) || (strcasecmp(svc_name, s) != 0))
599*3125Sjacobs 		return (0);     /* they don't match */
600*3125Sjacobs 
601*3125Sjacobs 	/* is it in uri form? */
602*3125Sjacobs 	if (uri_from_string(s, &uri) < 0)
603*3125Sjacobs 		return (0);
604*3125Sjacobs 
605*3125Sjacobs 	if ((uri == NULL) || (uri->scheme == NULL) || (uri->host == NULL)) {
606*3125Sjacobs 		uri_free(uri);
607*3125Sjacobs 		return (0);
608*3125Sjacobs 	}
609*3125Sjacobs 
610*3125Sjacobs 	/* is it in lpd form? */
611*3125Sjacobs 	if (strcasecmp(uri->scheme, "lpd") != 0) {
612*3125Sjacobs 		uri_free(uri);
613*3125Sjacobs 		return (0);
614*3125Sjacobs 	}
615*3125Sjacobs 
616*3125Sjacobs 	/* is it the local host? */
617*3125Sjacobs 	sysinfo(SI_HOSTNAME, buf, sizeof (buf));
618*3125Sjacobs 	if ((strcasecmp(uri->host, "localhost") != 0) &&
619*3125Sjacobs 	     (strcasecmp(uri->host, buf) != 0)) {
620*3125Sjacobs 		uri_free(uri);
621*3125Sjacobs 		return (0);
622*3125Sjacobs 	}
623*3125Sjacobs 
624*3125Sjacobs 	uri_free(uri);
625*3125Sjacobs 	return (1);
626*3125Sjacobs }
627*3125Sjacobs 
628*3125Sjacobs 
6292264Sjacobs /*
6302264Sjacobs  * This is the entry point for this program.  The program takes the
6312264Sjacobs  * following options:
6322264Sjacobs  * 	(none)
6332264Sjacobs  */
6342264Sjacobs int
6352264Sjacobs main(int ac, char *av[])
6362264Sjacobs {
6372264Sjacobs 	papi_status_t status;
6382264Sjacobs 	papi_service_t svc = NULL;
6392264Sjacobs 	papi_encryption_t encryption = PAPI_ENCRYPT_NEVER;
6402264Sjacobs 	FILE	*ifp = stdin,
6412264Sjacobs 		*ofp = stdout;
6422264Sjacobs 	int	c;
6432264Sjacobs 	char	buf[BUFSIZ],
6442264Sjacobs 		**args,
645*3125Sjacobs 		*printer,
646*3125Sjacobs 		*run_dir = "/var/run/in.lpd",
647*3125Sjacobs 		*run_user = NULL;
648*3125Sjacobs 	struct passwd *pw = NULL;
6492264Sjacobs 
650*3125Sjacobs 	(void) chdir("/tmp");		/* run in /tmp by default */
6512264Sjacobs 	openlog("bsd-gw", LOG_PID, LOG_LPR);
6522264Sjacobs 
653*3125Sjacobs 	while ((c = getopt(ac, av, "Ed:u:")) != EOF)
6542264Sjacobs 		switch (c) {
6552264Sjacobs 		case 'E':
6562264Sjacobs 			encryption = PAPI_ENCRYPT_ALWAYS;
6572264Sjacobs 			break;
658*3125Sjacobs 		case 'd':	/* run where they tell you */
659*3125Sjacobs 			run_dir = optarg;
660*3125Sjacobs 			break;
661*3125Sjacobs 		case 'u':	/* run as */
662*3125Sjacobs 			run_user = optarg;
663*3125Sjacobs 			break;
6642264Sjacobs 		default:
6652264Sjacobs 			;
6662264Sjacobs 		}
6672264Sjacobs 
668*3125Sjacobs 	if (run_user != NULL)	/* get the requested user info */
669*3125Sjacobs 		pw = getpwnam(run_user);
670*3125Sjacobs 
671*3125Sjacobs 	if (run_dir != NULL) {	/* setup the run_dir */
672*3125Sjacobs 		(void) mkdir(run_dir, 0700);
673*3125Sjacobs 		if (pw != NULL)
674*3125Sjacobs 			(void) chown(run_dir, pw->pw_uid, pw->pw_gid);
675*3125Sjacobs 	}
676*3125Sjacobs 
677*3125Sjacobs 	if (pw != NULL) {	/* run as the requested user */
678*3125Sjacobs 		syslog(LOG_DEBUG, "name: %s, uid: %d, gid: %d",
679*3125Sjacobs 				pw->pw_name, pw->pw_uid, pw->pw_gid);
680*3125Sjacobs 		initgroups(pw->pw_name, pw->pw_gid);
681*3125Sjacobs 		setgid(pw->pw_gid);
682*3125Sjacobs 		setuid(pw->pw_uid);
683*3125Sjacobs 	}
684*3125Sjacobs 
685*3125Sjacobs 	if (run_dir != NULL)	/* move to the run_dir */
686*3125Sjacobs 		if (chdir(run_dir) < 0) {
687*3125Sjacobs 			syslog(LOG_DEBUG, "failed to chdir(%s)", run_dir);
688*3125Sjacobs 			exit(1);
689*3125Sjacobs 		}
690*3125Sjacobs 
691*3125Sjacobs 	syslog(LOG_DEBUG, "$CWD = %s", getwd(NULL));
692*3125Sjacobs 
6932264Sjacobs 	if (fgets(buf, sizeof (buf), ifp) == NULL) {
6942264Sjacobs 		if (feof(ifp) == 0)
6952264Sjacobs 			syslog(LOG_ERR, "Error reading from connection: %s",
6962264Sjacobs 				strerror(errno));
6972264Sjacobs 		exit(1);
6982264Sjacobs 	}
6992264Sjacobs 
700*3125Sjacobs 	syslog(LOG_DEBUG, "CMD: (%d)%s\n", buf[0], &buf[1]);
701*3125Sjacobs 
702*3125Sjacobs #ifdef DEBUG	/* translate [1-5]... messages to \[1-5] to run by hand */
703*3125Sjacobs 	if ((buf[0] > '0') && (buf[0] < '6'))
704*3125Sjacobs 		buf[0] -= '0';
705*3125Sjacobs #endif
706*3125Sjacobs 
7072264Sjacobs 	if ((buf[0] < 1) || (buf[0] > 5)) {
7082264Sjacobs 		fatal(ofp, "Invalid protocol request (%d): %c%s\n",
7092264Sjacobs 			buf[0], buf[0], buf);
7102264Sjacobs 		exit(1);
7112264Sjacobs 	}
7122264Sjacobs 
7132264Sjacobs 	args = strsplit(&buf[1], "\t\n ");
7142264Sjacobs 	printer = *args++;
7152264Sjacobs 
7162264Sjacobs 	if (printer == NULL) {
7172264Sjacobs 		fatal(ofp, "Can't determine requested printer");
7182264Sjacobs 		exit(1);
7192264Sjacobs 	}
7202264Sjacobs 
721*3125Sjacobs 	if (cyclical_service_check(printer) != 0) {
722*3125Sjacobs 		fatal(ofp, "%s is cyclical\n", printer);
723*3125Sjacobs 		exit(1);
724*3125Sjacobs 	}
725*3125Sjacobs 
7262264Sjacobs 	status = papiServiceCreate(&svc, printer, NULL, NULL, NULL,
7272264Sjacobs 					encryption, NULL);
7282264Sjacobs 	if (status != PAPI_OK) {
7292264Sjacobs 		fatal(ofp, "Failed to contact service for %s: %s\n", printer,
7302264Sjacobs 			verbose_papi_message(svc, status));
7312264Sjacobs 		exit(1);
7322264Sjacobs 	}
7332264Sjacobs 
734*3125Sjacobs 	/*
735*3125Sjacobs 	 * Trusted Solaris can't be trusting of intermediaries.  Pass
736*3125Sjacobs 	 * the socket connection to the print service to retrieve the
737*3125Sjacobs 	 * sensativity label off of a multi-level port.
738*3125Sjacobs 	 */
739*3125Sjacobs 	(void) papiServiceSetPeer(svc, fileno(ifp));
7402264Sjacobs 
7412264Sjacobs 	switch (buf[0]) {
7422264Sjacobs 	case '\1':	/* restart printer */
7432264Sjacobs 		ACK(ofp);	/* there is no equivalent */
7442264Sjacobs 		break;
7452264Sjacobs 	case '\2':	/* transfer job(s) */
746*3125Sjacobs 		status = berkeley_transfer_files(svc, ifp, ofp, printer);
7472264Sjacobs 		break;
7482264Sjacobs 	case '\3':	/* show queue (short) */
7492264Sjacobs 	case '\4': {	/* show queue (long) */
7502264Sjacobs 		int count;
7512264Sjacobs 
7522264Sjacobs 		for (count = 0; args[count] != 0; count++);
7532264Sjacobs 
7542264Sjacobs 		berkeley_queue_report(svc, ofp, printer, buf[0], count, args);
7552264Sjacobs 		}
7562264Sjacobs 		break;
7572264Sjacobs 	case '\5': {	/* cancel job(s) */
758*3125Sjacobs 		char *user = *args++;
759*3125Sjacobs 		char *host = remote_host_name(ifp);
7602264Sjacobs 		int count;
7612264Sjacobs 
762*3125Sjacobs 		if (host != NULL) {
763*3125Sjacobs 			char buf[BUFSIZ];
764*3125Sjacobs 
765*3125Sjacobs 			snprintf(buf, sizeof (buf), "%s@%s", user, host);
766*3125Sjacobs 			status = papiServiceSetUserName(svc, buf);
767*3125Sjacobs 		} else
768*3125Sjacobs 			status = papiServiceSetUserName(svc, user);
769*3125Sjacobs 
7702264Sjacobs 		for (count = 0; args[count] != 0; count++);
7712264Sjacobs 
7722264Sjacobs 		berkeley_cancel_request(svc, ofp, printer, count, args);
7732264Sjacobs 		}
7742264Sjacobs 		break;
7752264Sjacobs 	default:
7762264Sjacobs 		fatal(ofp, "unsupported protocol request (%c), %s",
7772264Sjacobs 			buf[0], &buf[1]);
7782264Sjacobs 	}
7792264Sjacobs 
7802264Sjacobs 	(void) fflush(ofp);
7812264Sjacobs 
7822264Sjacobs 	syslog(LOG_DEBUG, "protocol request(%d) for %s completed: %s",
7832264Sjacobs 		buf[0], printer, papiStatusString(status));
784*3125Sjacobs 	if (status != PAPI_OK)
785*3125Sjacobs 		syslog(LOG_DEBUG, "detail: %s",
786*3125Sjacobs 				verbose_papi_message(svc, status));
7872264Sjacobs 
7882264Sjacobs 	papiServiceDestroy(svc);
7892264Sjacobs 
7902264Sjacobs 	return (0);
7912264Sjacobs }
792