xref: /illumos-gate/usr/src/lib/print/libpapi-lpd/common/lpd-job.c (revision 7a088f03b431bdffa96c3b2175964d4d38420caa)
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 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  */
27 
28 /* $Id: lpd-job.c 157 2006-04-26 15:07:55Z ktou $ */
29 
30 
31 #define	__EXTENSIONS__	/* for strtok_r() */
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <unistd.h>
35 #include <errno.h>
36 #include <limits.h>
37 #include <sys/types.h>
38 #include <sys/stat.h>
39 #include <fcntl.h>
40 #include <string.h>
41 #include <pwd.h>
42 #include <libintl.h>
43 #include <papi_impl.h>
44 
45 enum { LPD_RFC, LPD_SVR4 };
46 
47 static char
48 mime_type_to_rfc1179_type(char *mime)
49 {
50 	static struct { char *mime; char rfc; } cvt[] = {
51 		{ "text/plain", 'f' },
52 		{ "application/octet-stream", 'l' },
53 		{ "application/postscript", 'f' }, /* rfc incorrectly has 'o' */
54 		{ "application/x-pr", 'p' },
55 		{ "application/x-cif", 'c' },
56 		{ "application/x-dvi", 'd' },
57 		{ "application/x-fortran", 'r' },
58 		{ "application/x-plot", 'g' },
59 		{ "application/x-ditroff", 'n' },
60 		{ "application/x-troff", 't' },
61 		{ "application/x-raster", 'v' },
62 		{ NULL, 0}
63 	};
64 	char result = '\0';
65 
66 	if (mime != NULL) {
67 		int i;
68 
69 		for (i = 0; cvt[i].mime != NULL; i++)
70 			if (strcasecmp(cvt[i].mime, mime) == 0) {
71 				result = cvt[i].rfc;
72 				break;
73 			}
74 	}
75 
76 	return (result);
77 }
78 
79 static papi_status_t
80 add_lpd_control_line(char **metadata, char code, char *value)
81 {
82 	size_t size = 0;
83 	char line[BUFSIZ];
84 
85 	if ((metadata == NULL) || (value == NULL))
86 		return (PAPI_BAD_REQUEST);
87 
88 	if (*metadata != NULL)
89 		size = strlen(*metadata);
90 	size += strlen(value) + 3;
91 
92 	if (*metadata == NULL) {
93 		*metadata = (char *)calloc(1, size);
94 	} else {
95 		void *tmp;
96 		tmp = calloc(1, size);
97 		if (tmp) {
98 			strlcpy(tmp, *metadata, size);
99 			free(*metadata);
100 			*metadata = (char *)tmp;
101 		} else
102 			return (PAPI_TEMPORARY_ERROR);
103 	}
104 
105 	snprintf(line, sizeof (line), "%c%s\n", code, value);
106 	strlcat(*metadata, line, size);
107 
108 	return (PAPI_OK);
109 }
110 
111 static papi_status_t
112 add_svr4_control_line(char **metadata, char code, char *value)
113 {
114 
115 	char line[BUFSIZ];
116 
117 	if ((metadata == NULL) || (value == NULL))
118 		return (PAPI_BAD_REQUEST);
119 
120 	snprintf(line, sizeof (line), "%c%s", code, value);
121 
122 	return (add_lpd_control_line(metadata, '5', line));
123 }
124 
125 static papi_status_t
126 add_hpux_control_line(char **metadata, char *value)
127 {
128 
129 	char line[BUFSIZ];
130 
131 	if ((metadata == NULL) || (value == NULL))
132 		return (PAPI_BAD_REQUEST);
133 
134 	snprintf(line, sizeof (line), " O%s", value);
135 
136 	return (add_lpd_control_line(metadata, 'N', line));
137 }
138 
139 static papi_status_t
140 add_int_control_line(char **metadata, char code, int value, int flag)
141 {
142 	char buf[16];
143 
144 	snprintf(buf, sizeof (buf), "%d", value);
145 
146 	if (flag == LPD_SVR4)
147 		return (add_svr4_control_line(metadata, code, buf));
148 	else
149 		return (add_lpd_control_line(metadata, code, buf));
150 }
151 
152 static papi_status_t
153 lpd_add_rfc1179_attributes(service_t *svc, papi_attribute_t **attributes,
154 		char **metadata, papi_attribute_t ***used)
155 {
156 	papi_status_t status = PAPI_OK;
157 	char *s;
158 	int integer;
159 	char bool;
160 	char host[BUFSIZ];
161 	char *user = "nobody";
162 	uid_t uid = getuid();
163 	struct passwd *pw;
164 
165 	if (svc == NULL)
166 		return (PAPI_BAD_REQUEST);
167 
168 	/* There is nothing to do */
169 	if (attributes == NULL)
170 		return (PAPI_OK);
171 
172 	gethostname(host, sizeof (host));
173 	add_lpd_control_line(metadata, 'H', host);
174 	papiAttributeListAddString(used, PAPI_ATTR_EXCL,
175 	    "job-originating-host-name", host);
176 
177 	if ((pw = getpwuid(uid)) != NULL)
178 		user = pw->pw_name;
179 	if (uid == 0)
180 		papiAttributeListGetString(svc->attributes, NULL, "username",
181 		    &user);
182 	add_lpd_control_line(metadata, 'P', user);
183 	papiAttributeListAddString(used, PAPI_ATTR_EXCL,
184 	    "job-originating-user-name", user);
185 
186 	/* Class for Banner Page */
187 	s = NULL;
188 	papiAttributeListGetString(attributes, NULL, "rfc-1179-class", &s);
189 	if (s != NULL) {
190 		add_lpd_control_line(metadata, 'C', s);
191 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
192 		    "rfc-1179-class", s);
193 	}
194 
195 	/* Print Banner Page */
196 	s = NULL;
197 	papiAttributeListGetString(attributes, NULL, "job-sheets", &s);
198 	if ((s != NULL) && (strcmp(s, "standard") == 0)) {
199 		add_lpd_control_line(metadata, 'L', user);
200 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
201 		    "job-sheets", s);
202 	}
203 
204 	/* Jobname */
205 	s = NULL;
206 	papiAttributeListGetString(attributes, NULL, "job-name", &s);
207 	if (s != NULL) {
208 		add_lpd_control_line(metadata, 'J', s);
209 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
210 		    "job-name", s);
211 	}
212 
213 	/* User to mail when job is done - lpr -m */
214 	bool = PAPI_FALSE;
215 	papiAttributeListGetBoolean(attributes, NULL, "rfc-1179-mail", &bool);
216 	if (bool == PAPI_TRUE) {
217 		add_lpd_control_line(metadata, 'M', user);
218 		papiAttributeListAddBoolean(used, PAPI_ATTR_EXCL,
219 		    "rfc-1179-mail", bool);
220 	}
221 
222 	/* Title for pr */
223 	s = NULL;
224 	papiAttributeListGetString(attributes, NULL, "pr-title", &s);
225 	if (s != NULL) {
226 		add_lpd_control_line(metadata, 'T', s);
227 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
228 		    "pr-title", s);
229 	}
230 
231 	/* Indent - used with pr filter */
232 	integer = 0;
233 	papiAttributeListGetInteger(attributes, NULL, "pr-indent", &integer);
234 	if (integer >= 1) {
235 		add_int_control_line(metadata, 'I', integer, LPD_RFC);
236 		papiAttributeListAddInteger(used, PAPI_ATTR_EXCL,
237 		    "pr-indent", integer);
238 	}
239 
240 	/* Width - used with pr filter */
241 	integer = 0;
242 	papiAttributeListGetInteger(attributes, NULL, "pr-width", &integer);
243 	if (integer >= 1) {
244 		add_int_control_line(metadata, 'W', integer, LPD_RFC);
245 		papiAttributeListAddInteger(used, PAPI_ATTR_EXCL,
246 		    "pr-width", integer);
247 	}
248 
249 	/* file with Times Roman font lpr -1	*/
250 	s = NULL;
251 	papiAttributeListGetString(attributes, NULL, "rfc-1179-font-r", &s);
252 	if (s != NULL) {
253 		add_lpd_control_line(metadata, '1', s);
254 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
255 		    "rfc-1179-font-r", s);
256 	}
257 
258 	/* file with Times Roman font lpr -2	*/
259 	s = NULL;
260 	papiAttributeListGetString(attributes, NULL, "rfc-1179-font-i", &s);
261 	if (s != NULL) {
262 		add_lpd_control_line(metadata, '2', s);
263 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
264 		    "rfc-1179-font-i", s);
265 	}
266 
267 	/* file with Times Roman font lpr -3	*/
268 	s = NULL;
269 	papiAttributeListGetString(attributes, NULL, "rfc-1179-font-b", &s);
270 	if (s != NULL) {
271 		add_lpd_control_line(metadata, '3', s);
272 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
273 		    "rfc-1179-font-b", s);
274 	}
275 
276 	/* file with Times Roman font lpr -4	*/
277 	s = NULL;
278 	papiAttributeListGetString(attributes, NULL, "rfc-1179-font-s", &s);
279 	if (s != NULL) {
280 		add_lpd_control_line(metadata, '4', s);
281 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
282 		    "rfc-1179-font-s", s);
283 	}
284 
285 	/*
286 	 * The document format needs to be added, but the control line
287 	 * should be added when the filenames are figured out.
288 	 */
289 	s = NULL;
290 	papiAttributeListGetString(attributes, NULL, "document-format", &s);
291 	if (s != NULL) {
292 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
293 		    "document-format", s);
294 	}
295 
296 	return (status);
297 }
298 
299 static char *
300 unused_attributes(papi_attribute_t **list, papi_attribute_t **used)
301 {
302 	char *result = NULL;
303 	char **names = NULL;
304 	int i;
305 
306 	if ((list == NULL) || (used == NULL))
307 		return (NULL);
308 
309 	for (i = 0; used[i] != NULL; i++)
310 		list_append(&names, used[i]->name);
311 
312 	if (names != NULL) {
313 		papi_attribute_t **unused = NULL;
314 
315 		/* add these to the list of things to ignore */
316 		list_append(&names, "document-format");
317 		list_append(&names, "copies");
318 
319 		split_and_copy_attributes(names, list, NULL, &unused);
320 		if (unused != NULL) {
321 			size_t size = 0;
322 
323 			do {
324 				size += 1024;
325 				if (result != NULL)
326 					free(result);
327 				result = calloc(1, size);
328 			} while (papiAttributeListToString(unused, " ",
329 			    result, size) != PAPI_OK);
330 			papiAttributeListFree(unused);
331 		}
332 		free(names);
333 	}
334 
335 	return (result);
336 }
337 
338 /*
339  * lpd_add_svr4_attributes
340  *	Solaris 2.x LP - BSD protocol extensions
341  */
342 static papi_status_t
343 lpd_add_svr4_attributes(service_t *svc, papi_attribute_t **attributes,
344 		char **metadata, papi_attribute_t ***used)
345 {
346 	papi_attribute_t *tmp[2];
347 	char *s;
348 	int integer;
349 
350 	if (svc == NULL)
351 		return (PAPI_BAD_REQUEST);
352 
353 	/* media */
354 	s = NULL;
355 	papiAttributeListGetString(attributes, NULL, "media", &s);
356 	if (s != NULL) {
357 		add_svr4_control_line(metadata, 'f', s);
358 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
359 		    "media", s);
360 	}
361 
362 	/* Handling */
363 	s = NULL;
364 	papiAttributeListGetString(attributes, NULL, "job-hold-until", &s);
365 	if ((s != NULL) && (strcmp(s, "indefinite") == 0)) {
366 		add_svr4_control_line(metadata, 'H', "hold");
367 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
368 		    "job-hold-until", "indefinite");
369 	} else if ((s != NULL) && (strcmp(s, "no-hold") == 0)) {
370 		add_svr4_control_line(metadata, 'H', "immediate");
371 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
372 		    "job-hold-until", "no-hold");
373 	} else if (s != NULL) {
374 		add_svr4_control_line(metadata, 'H', s);
375 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
376 		    "job-hold-until", s);
377 	}
378 
379 	/* Pages */
380 	s = NULL;
381 	memset(tmp, NULL, sizeof (tmp));
382 	tmp[0] = papiAttributeListFind(attributes, "page-ranges");
383 	if (tmp[0] != NULL) {
384 		char buf[BUFSIZ];
385 
386 		papiAttributeListToString(tmp, " ", buf, sizeof (buf));
387 		if ((s = strchr(buf, '=')) != NULL) {
388 			add_svr4_control_line(metadata, 'P', ++s);
389 			papiAttributeListAddString(used, PAPI_ATTR_EXCL,
390 			    "page-ranges", s);
391 		}
392 	}
393 
394 	/* Priority : lp -q */
395 	integer = -1;
396 	papiAttributeListGetInteger(attributes, NULL, "job-priority", &integer);
397 	if (integer != -1) {
398 		integer = 40 - (integer / 2.5);
399 		add_int_control_line(metadata, 'q', integer, LPD_SVR4);
400 		papiAttributeListAddInteger(used, PAPI_ATTR_EXCL,
401 		    "job-priority", integer);
402 	}
403 
404 	/* Charset : lp -S */
405 	s = NULL;
406 	papiAttributeListGetString(attributes, NULL, "lp-charset", &s);
407 	if (s != NULL) {
408 		add_svr4_control_line(metadata, 'S', s);
409 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
410 		    "lp-charset", s);
411 	}
412 
413 	/* Type : done when adding file  */
414 
415 	/* Mode : lp -y */
416 	s = NULL;
417 	papiAttributeListGetString(attributes, NULL, "lp-modes", &s);
418 	if (s != NULL) {
419 		add_svr4_control_line(metadata, 'y', s);
420 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
421 		    "lp-modes", s);
422 	}
423 
424 	/* Options lp -o are handled elsewhere */
425 	if ((s = unused_attributes(attributes, *used)) != NULL) {
426 		add_lpd_control_line(metadata, 'O', s);
427 		free(s);
428 	}
429 
430 	return (PAPI_OK);
431 }
432 
433 papi_status_t
434 lpd_add_hpux_attributes(service_t *svc, papi_attribute_t **attributes,
435 		char **metadata, papi_attribute_t ***used)
436 {
437 	char *s = NULL;
438 
439 	/* Options lp -o */
440 	if ((s = unused_attributes(attributes, *used)) != NULL) {
441 		add_hpux_control_line(metadata, s);
442 		free(s);
443 	}
444 
445 	return (PAPI_OK);
446 }
447 
448 papi_status_t
449 lpd_job_add_attributes(service_t *svc, papi_attribute_t **attributes,
450 		char **metadata, papi_attribute_t ***used)
451 {
452 	if ((svc == NULL) || (metadata == NULL))
453 		return (PAPI_BAD_REQUEST);
454 
455 	lpd_add_rfc1179_attributes(svc, attributes, metadata, used);
456 
457 	/* add protocol extensions if applicable */
458 	if (svc->uri->fragment != NULL) {
459 		if ((strcasecmp(svc->uri->fragment, "solaris") == 0) ||
460 		    (strcasecmp(svc->uri->fragment, "svr4") == 0))
461 			lpd_add_svr4_attributes(svc, attributes, metadata,
462 			    used);
463 		else if (strcasecmp(svc->uri->fragment, "hpux") == 0)
464 			lpd_add_hpux_attributes(svc, attributes, metadata,
465 			    used);
466 		/*
467 		 * others could be added here:
468 		 *	lprng, sco, aix, digital unix, xerox, ...
469 		 */
470 	}
471 
472 	return (PAPI_OK);
473 }
474 
475 papi_status_t
476 lpd_job_add_files(service_t *svc, papi_attribute_t **attributes,
477 		char **files, char **metadata, papi_attribute_t ***used)
478 {
479 	char *format = "text/plain";
480 	char rfc_fmt = 'l';
481 	int copies = 1;
482 	char host[BUFSIZ];
483 	int i;
484 
485 	if ((svc == NULL) || (attributes == NULL) || (files == NULL) ||
486 	    (metadata == NULL))
487 		return (PAPI_BAD_ARGUMENT);
488 
489 	papiAttributeListGetString(attributes, NULL, "document-format",
490 	    &format);
491 	papiAttributeListAddString(used, PAPI_ATTR_EXCL,
492 	    "document-format", format);
493 	if ((rfc_fmt = mime_type_to_rfc1179_type(format)) == '\0') {
494 		if ((svc->uri->fragment != NULL) &&
495 		    ((strcasecmp(svc->uri->fragment, "solaris") == 0) ||
496 		    (strcasecmp(svc->uri->fragment, "svr4") == 0)))
497 			add_svr4_control_line(metadata, 'T', format);
498 		rfc_fmt = 'l';
499 	}
500 
501 	papiAttributeListGetInteger(attributes, NULL, "copies", &copies);
502 	if (copies < 1)
503 		copies = 1;
504 	papiAttributeListAddInteger(used, PAPI_ATTR_EXCL, "copies", copies);
505 
506 	gethostname(host, sizeof (host));
507 
508 	for (i = 0; files[i] != NULL; i++) {
509 		char name[BUFSIZ];
510 		struct stat statbuf;
511 		char key;
512 		int j;
513 
514 		if ((strcmp("standard input", files[i]) != 0) &&
515 		    (access(files[i], R_OK) < 0)) {
516 			detailed_error(svc, gettext("aborting request, %s: %s"),
517 			    files[i], strerror(errno));
518 			return (PAPI_NOT_AUTHORIZED);
519 		}
520 		if (strcmp("standard input", files[i]) != 0) {
521 			stat(files[i], &statbuf);
522 			if (statbuf.st_size == 0) {
523 				detailed_error(svc,
524 				    gettext("Zero byte (empty) file: %s"),
525 				    files[i]);
526 				return (PAPI_BAD_ARGUMENT);
527 			}
528 		}
529 
530 		if (i < 26)
531 			key = 'A' + i;
532 		else if (i < 52)
533 			key = 'a' + (i - 26);
534 		else if (i < 62)
535 			key = '0' + (i - 52);
536 		else {
537 			detailed_error(svc,
538 			    gettext("too many files, truncated at 62"));
539 			return (PAPI_OK_SUBST);
540 		}
541 
542 		snprintf(name, sizeof (name), "df%cXXX%s", key, host);
543 
544 		for (j = 0; j < copies; j++)
545 			add_lpd_control_line(metadata, rfc_fmt, name);
546 		add_lpd_control_line(metadata, 'U', name);
547 		add_lpd_control_line(metadata, 'N', (char *)files[i]);
548 	}
549 
550 	return (PAPI_OK);
551 }
552 
553 papi_status_t
554 lpd_submit_job(service_t *svc, char *metadata, papi_attribute_t ***attributes,
555 		int *ofd)
556 {
557 	papi_status_t status = PAPI_INTERNAL_ERROR;
558 	int fd;
559 	char path[32];
560 	char *list[2];
561 
562 	if ((svc == NULL) || (metadata == NULL))
563 		return (PAPI_BAD_ARGUMENT);
564 
565 	strcpy(path, "/tmp/lpd-job-XXXXXX");
566 	fd = mkstemp(path);
567 	write(fd, metadata, strlen(metadata));
568 	close(fd);
569 
570 	list[0] = path;
571 	list[1] = NULL;
572 
573 	if (((fd = lpd_open(svc, 's', list, 15)) < 0) && (errno != EBADMSG)) {
574 		switch (errno) {
575 		case ENOSPC:
576 			status = PAPI_TEMPORARY_ERROR;
577 			break;
578 		case EIO:
579 			status = PAPI_TEMPORARY_ERROR;
580 			break;
581 		case ECONNREFUSED:
582 			status = PAPI_SERVICE_UNAVAILABLE;
583 			break;
584 		case ENOENT:
585 			status = PAPI_NOT_ACCEPTING;
586 			break;
587 		case EBADMSG:
588 		case EBADF:
589 			status = PAPI_OK;
590 			break;
591 		default:
592 			status = PAPI_TIMEOUT;
593 			break;
594 		}
595 	} else
596 		status = PAPI_OK;
597 
598 	if (ofd != NULL)
599 		*ofd = fd;
600 	else
601 		close(fd);
602 
603 	/* read the ID and add it to to the job */
604 	if ((fd = open(path, O_RDONLY)) >= 0) {
605 		int job_id = 0;
606 		read(fd, &job_id, sizeof (job_id));
607 		papiAttributeListAddInteger(attributes, PAPI_ATTR_REPLACE,
608 		    "job-id", job_id);
609 		close(fd);
610 	}
611 
612 	unlink(path);
613 
614 	return (status);
615 }
616