xref: /illumos-gate/usr/src/lib/print/libpapi-lpd/common/lpd-job.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 	char *h1;
165 
166 	if (svc == NULL)
167 		return (PAPI_BAD_REQUEST);
168 
169 	/* There is nothing to do */
170 	if (attributes == NULL)
171 		return (PAPI_OK);
172 
173 	gethostname(host, sizeof (host));
174 	if (papiAttributeListGetString(attributes, NULL,
175 	    "job-originating-host-name", &h1) == PAPI_OK) {
176 		papiAttributeListAddString(&attributes, PAPI_ATTR_APPEND,
177 		    "job-host", h1);
178 	}
179 	add_lpd_control_line(metadata, 'H', host);
180 	papiAttributeListAddString(used, PAPI_ATTR_EXCL,
181 	    "job-originating-host-name", host);
182 
183 	if ((pw = getpwuid(uid)) != NULL)
184 		user = pw->pw_name;
185 	if (uid == 0)
186 		papiAttributeListGetString(svc->attributes, NULL, "username",
187 		    &user);
188 	add_lpd_control_line(metadata, 'P', user);
189 	papiAttributeListAddString(used, PAPI_ATTR_EXCL,
190 	    "job-originating-user-name", user);
191 
192 	/* Class for Banner Page */
193 	s = NULL;
194 	papiAttributeListGetString(attributes, NULL, "rfc-1179-class", &s);
195 	if (s != NULL) {
196 		add_lpd_control_line(metadata, 'C', s);
197 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
198 		    "rfc-1179-class", s);
199 	}
200 
201 	/* Print Banner Page */
202 	s = NULL;
203 	papiAttributeListGetString(attributes, NULL, "job-sheets", &s);
204 	if ((s != NULL) && (strcmp(s, "standard") == 0)) {
205 		add_lpd_control_line(metadata, 'L', user);
206 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
207 		    "job-sheets", s);
208 	}
209 
210 	/* Jobname */
211 	s = NULL;
212 	papiAttributeListGetString(attributes, NULL, "job-name", &s);
213 	if (s != NULL) {
214 		add_lpd_control_line(metadata, 'J', s);
215 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
216 		    "job-name", s);
217 	}
218 
219 	/* User to mail when job is done - lpr -m */
220 	bool = PAPI_FALSE;
221 	papiAttributeListGetBoolean(attributes, NULL, "rfc-1179-mail", &bool);
222 	if (bool == PAPI_TRUE) {
223 		add_lpd_control_line(metadata, 'M', user);
224 		papiAttributeListAddBoolean(used, PAPI_ATTR_EXCL,
225 		    "rfc-1179-mail", bool);
226 	}
227 
228 	/* Title for pr */
229 	s = NULL;
230 	papiAttributeListGetString(attributes, NULL, "pr-title", &s);
231 	if (s != NULL) {
232 		add_lpd_control_line(metadata, 'T', s);
233 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
234 		    "pr-title", s);
235 	}
236 
237 	/* Indent - used with pr filter */
238 	integer = 0;
239 	papiAttributeListGetInteger(attributes, NULL, "pr-indent", &integer);
240 	if (integer >= 1) {
241 		add_int_control_line(metadata, 'I', integer, LPD_RFC);
242 		papiAttributeListAddInteger(used, PAPI_ATTR_EXCL,
243 		    "pr-indent", integer);
244 	}
245 
246 	/* Width - used with pr filter */
247 	integer = 0;
248 	papiAttributeListGetInteger(attributes, NULL, "pr-width", &integer);
249 	if (integer >= 1) {
250 		add_int_control_line(metadata, 'W', integer, LPD_RFC);
251 		papiAttributeListAddInteger(used, PAPI_ATTR_EXCL,
252 		    "pr-width", integer);
253 	}
254 
255 	/* file with Times Roman font lpr -1	*/
256 	s = NULL;
257 	papiAttributeListGetString(attributes, NULL, "rfc-1179-font-r", &s);
258 	if (s != NULL) {
259 		add_lpd_control_line(metadata, '1', s);
260 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
261 		    "rfc-1179-font-r", s);
262 	}
263 
264 	/* file with Times Roman font lpr -2	*/
265 	s = NULL;
266 	papiAttributeListGetString(attributes, NULL, "rfc-1179-font-i", &s);
267 	if (s != NULL) {
268 		add_lpd_control_line(metadata, '2', s);
269 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
270 		    "rfc-1179-font-i", s);
271 	}
272 
273 	/* file with Times Roman font lpr -3	*/
274 	s = NULL;
275 	papiAttributeListGetString(attributes, NULL, "rfc-1179-font-b", &s);
276 	if (s != NULL) {
277 		add_lpd_control_line(metadata, '3', s);
278 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
279 		    "rfc-1179-font-b", s);
280 	}
281 
282 	/* file with Times Roman font lpr -4	*/
283 	s = NULL;
284 	papiAttributeListGetString(attributes, NULL, "rfc-1179-font-s", &s);
285 	if (s != NULL) {
286 		add_lpd_control_line(metadata, '4', s);
287 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
288 		    "rfc-1179-font-s", s);
289 	}
290 
291 	/*
292 	 * The document format needs to be added, but the control line
293 	 * should be added when the filenames are figured out.
294 	 */
295 	s = NULL;
296 	papiAttributeListGetString(attributes, NULL, "document-format", &s);
297 	if (s != NULL) {
298 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
299 		    "document-format", s);
300 	}
301 
302 	return (status);
303 }
304 
305 static char *
306 unused_attributes(papi_attribute_t **list, papi_attribute_t **used)
307 {
308 	char *result = NULL;
309 	char **names = NULL;
310 	int i;
311 
312 	if ((list == NULL) || (used == NULL))
313 		return (NULL);
314 
315 	for (i = 0; used[i] != NULL; i++)
316 		list_append(&names, used[i]->name);
317 
318 	if (names != NULL) {
319 		papi_attribute_t **unused = NULL;
320 
321 		/* add these to the list of things to ignore */
322 		list_append(&names, "document-format");
323 		list_append(&names, "copies");
324 
325 		split_and_copy_attributes(names, list, NULL, &unused);
326 		if (unused != NULL) {
327 			size_t size = 0;
328 
329 			do {
330 				size += 1024;
331 				if (result != NULL)
332 					free(result);
333 				result = calloc(1, size);
334 			} while (papiAttributeListToString(unused, " ",
335 			    result, size) != PAPI_OK);
336 			papiAttributeListFree(unused);
337 		}
338 		free(names);
339 	}
340 
341 	return (result);
342 }
343 
344 /*
345  * lpd_add_svr4_attributes
346  *	Solaris 2.x LP - BSD protocol extensions
347  */
348 static papi_status_t
349 lpd_add_svr4_attributes(service_t *svc, papi_attribute_t **attributes,
350 		char **metadata, papi_attribute_t ***used)
351 {
352 	papi_attribute_t *tmp[2];
353 	char *s;
354 	int integer;
355 
356 	if (svc == NULL)
357 		return (PAPI_BAD_REQUEST);
358 
359 	/* media */
360 	s = NULL;
361 	papiAttributeListGetString(attributes, NULL, "media", &s);
362 	if (s != NULL) {
363 		add_svr4_control_line(metadata, 'f', s);
364 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
365 		    "media", s);
366 	}
367 
368 	/* Handling */
369 	s = NULL;
370 	papiAttributeListGetString(attributes, NULL, "job-hold-until", &s);
371 	if ((s != NULL) && (strcmp(s, "indefinite") == 0)) {
372 		add_svr4_control_line(metadata, 'H', "hold");
373 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
374 		    "job-hold-until", "indefinite");
375 	} else if ((s != NULL) && (strcmp(s, "no-hold") == 0)) {
376 		add_svr4_control_line(metadata, 'H', "immediate");
377 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
378 		    "job-hold-until", "no-hold");
379 	} else if (s != NULL) {
380 		add_svr4_control_line(metadata, 'H', s);
381 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
382 		    "job-hold-until", s);
383 	}
384 
385 	/* Pages */
386 	s = NULL;
387 	memset(tmp, NULL, sizeof (tmp));
388 	tmp[0] = papiAttributeListFind(attributes, "page-ranges");
389 	if (tmp[0] != NULL) {
390 		char buf[BUFSIZ];
391 
392 		papiAttributeListToString(tmp, " ", buf, sizeof (buf));
393 		if ((s = strchr(buf, '=')) != NULL) {
394 			add_svr4_control_line(metadata, 'P', ++s);
395 			papiAttributeListAddString(used, PAPI_ATTR_EXCL,
396 			    "page-ranges", s);
397 		}
398 	}
399 
400 	/* Priority : lp -q */
401 	integer = -1;
402 	papiAttributeListGetInteger(attributes, NULL, "job-priority", &integer);
403 	if (integer != -1) {
404 		integer = 40 - (integer / 2.5);
405 		add_int_control_line(metadata, 'q', integer, LPD_SVR4);
406 		papiAttributeListAddInteger(used, PAPI_ATTR_EXCL,
407 		    "job-priority", integer);
408 	}
409 
410 	/* Charset : lp -S */
411 	s = NULL;
412 	papiAttributeListGetString(attributes, NULL, "lp-charset", &s);
413 	if (s != NULL) {
414 		add_svr4_control_line(metadata, 'S', s);
415 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
416 		    "lp-charset", s);
417 	}
418 
419 	/* Type : done when adding file  */
420 
421 	/* Mode : lp -y */
422 	s = NULL;
423 	papiAttributeListGetString(attributes, NULL, "lp-modes", &s);
424 	if (s != NULL) {
425 		add_svr4_control_line(metadata, 'y', s);
426 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
427 		    "lp-modes", s);
428 	}
429 
430 	/* Options lp -o are handled elsewhere */
431 	if ((s = unused_attributes(attributes, *used)) != NULL) {
432 		add_lpd_control_line(metadata, 'O', s);
433 		free(s);
434 	}
435 
436 	return (PAPI_OK);
437 }
438 
439 papi_status_t
440 lpd_add_hpux_attributes(service_t *svc, papi_attribute_t **attributes,
441 		char **metadata, papi_attribute_t ***used)
442 {
443 	char *s = NULL;
444 
445 	/* Options lp -o */
446 	if ((s = unused_attributes(attributes, *used)) != NULL) {
447 		add_hpux_control_line(metadata, s);
448 		free(s);
449 	}
450 
451 	return (PAPI_OK);
452 }
453 
454 papi_status_t
455 lpd_job_add_attributes(service_t *svc, papi_attribute_t **attributes,
456 		char **metadata, papi_attribute_t ***used)
457 {
458 	if ((svc == NULL) || (metadata == NULL))
459 		return (PAPI_BAD_REQUEST);
460 
461 	lpd_add_rfc1179_attributes(svc, attributes, metadata, used);
462 
463 	/* add protocol extensions if applicable */
464 	if (svc->uri->fragment != NULL) {
465 		if ((strcasecmp(svc->uri->fragment, "solaris") == 0) ||
466 		    (strcasecmp(svc->uri->fragment, "svr4") == 0))
467 			lpd_add_svr4_attributes(svc, attributes, metadata,
468 			    used);
469 		else if (strcasecmp(svc->uri->fragment, "hpux") == 0)
470 			lpd_add_hpux_attributes(svc, attributes, metadata,
471 			    used);
472 		/*
473 		 * others could be added here:
474 		 *	lprng, sco, aix, digital unix, xerox, ...
475 		 */
476 	}
477 
478 	return (PAPI_OK);
479 }
480 
481 papi_status_t
482 lpd_job_add_files(service_t *svc, papi_attribute_t **attributes,
483 		char **files, char **metadata, papi_attribute_t ***used)
484 {
485 	char *format = "text/plain";
486 	char rfc_fmt = 'l';
487 	int copies = 1;
488 	char host[BUFSIZ];
489 	int i;
490 
491 	if ((svc == NULL) || (attributes == NULL) || (files == NULL) ||
492 	    (metadata == NULL))
493 		return (PAPI_BAD_ARGUMENT);
494 
495 	papiAttributeListGetString(attributes, NULL, "document-format",
496 	    &format);
497 	papiAttributeListAddString(used, PAPI_ATTR_EXCL,
498 	    "document-format", format);
499 	if ((rfc_fmt = mime_type_to_rfc1179_type(format)) == '\0') {
500 		if ((svc->uri->fragment != NULL) &&
501 		    ((strcasecmp(svc->uri->fragment, "solaris") == 0) ||
502 		    (strcasecmp(svc->uri->fragment, "svr4") == 0)))
503 			add_svr4_control_line(metadata, 'T', format);
504 		rfc_fmt = 'l';
505 	}
506 
507 	papiAttributeListGetInteger(attributes, NULL, "copies", &copies);
508 	if (copies < 1)
509 		copies = 1;
510 	papiAttributeListAddInteger(used, PAPI_ATTR_EXCL, "copies", copies);
511 
512 	gethostname(host, sizeof (host));
513 
514 	for (i = 0; files[i] != NULL; i++) {
515 		char name[BUFSIZ];
516 		struct stat statbuf;
517 		char key;
518 		int j;
519 
520 		if ((strcmp("standard input", files[i]) != 0) &&
521 		    (access(files[i], R_OK) < 0)) {
522 			detailed_error(svc, gettext("aborting request, %s: %s"),
523 			    files[i], strerror(errno));
524 			return (PAPI_NOT_AUTHORIZED);
525 		}
526 		if (strcmp("standard input", files[i]) != 0) {
527 			if (stat(files[i], &statbuf) < 0) {
528 				detailed_error(svc,
529 				    gettext("Cannot access file: %s: %s"),
530 				    files[i], strerror(errno));
531 				return (PAPI_DOCUMENT_ACCESS_ERROR);
532 			}
533 			if (statbuf.st_size == 0) {
534 				detailed_error(svc,
535 				    gettext("Zero byte (empty) file: %s"),
536 				    files[i]);
537 				return (PAPI_BAD_ARGUMENT);
538 			}
539 		}
540 
541 		if (i < 26)
542 			key = 'A' + i;
543 		else if (i < 52)
544 			key = 'a' + (i - 26);
545 		else if (i < 62)
546 			key = '0' + (i - 52);
547 		else {
548 			detailed_error(svc,
549 			    gettext("too many files, truncated at 62"));
550 			return (PAPI_OK_SUBST);
551 		}
552 
553 		snprintf(name, sizeof (name), "df%cXXX%s", key, host);
554 
555 		for (j = 0; j < copies; j++)
556 			add_lpd_control_line(metadata, rfc_fmt, name);
557 		add_lpd_control_line(metadata, 'U', name);
558 		add_lpd_control_line(metadata, 'N', (char *)files[i]);
559 	}
560 
561 	return (PAPI_OK);
562 }
563 
564 papi_status_t
565 lpd_submit_job(service_t *svc, char *metadata, papi_attribute_t ***attributes,
566 		int *ofd)
567 {
568 	papi_status_t status = PAPI_INTERNAL_ERROR;
569 	int fd;
570 	char path[32];
571 	char *list[2];
572 
573 	if ((svc == NULL) || (metadata == NULL))
574 		return (PAPI_BAD_ARGUMENT);
575 
576 	strcpy(path, "/tmp/lpd-job-XXXXXX");
577 	fd = mkstemp(path);
578 	write(fd, metadata, strlen(metadata));
579 	close(fd);
580 
581 	list[0] = path;
582 	list[1] = NULL;
583 
584 	if (((fd = lpd_open(svc, 's', list, 15)) < 0) && (errno != EBADMSG)) {
585 		switch (errno) {
586 		case ENOSPC:
587 			status = PAPI_TEMPORARY_ERROR;
588 			break;
589 		case EIO:
590 			status = PAPI_TEMPORARY_ERROR;
591 			break;
592 		case ECONNREFUSED:
593 			status = PAPI_SERVICE_UNAVAILABLE;
594 			break;
595 		case ENOENT:
596 			status = PAPI_NOT_ACCEPTING;
597 			break;
598 		case EBADMSG:
599 		case EBADF:
600 			status = PAPI_OK;
601 			break;
602 		default:
603 			status = PAPI_TIMEOUT;
604 			break;
605 		}
606 	} else
607 		status = PAPI_OK;
608 
609 	if (ofd != NULL)
610 		*ofd = fd;
611 	else
612 		close(fd);
613 
614 	/* read the ID and add it to to the job */
615 	if ((fd = open(path, O_RDONLY)) >= 0) {
616 		int job_id = 0;
617 		read(fd, &job_id, sizeof (job_id));
618 		papiAttributeListAddInteger(attributes, PAPI_ATTR_REPLACE,
619 		    "job-id", job_id);
620 		close(fd);
621 	}
622 
623 	unlink(path);
624 
625 	return (status);
626 }
627