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 	return (status);
286 }
287 
288 static char *
289 unused_attributes(papi_attribute_t **list, papi_attribute_t **used)
290 {
291 	char *result = NULL;
292 	char **names = NULL;
293 	int i;
294 
295 	if ((list == NULL) || (used == NULL))
296 		return (NULL);
297 
298 	for (i = 0; used[i] != NULL; i++)
299 		list_append(&names, used[i]->name);
300 
301 	if (names != NULL) {
302 		papi_attribute_t **unused = NULL;
303 
304 		/* add these to the list of things to ignore */
305 		list_append(&names, "document-format");
306 		list_append(&names, "copies");
307 
308 		split_and_copy_attributes(names, list, NULL, &unused);
309 		if (unused != NULL) {
310 			size_t size = 0;
311 
312 			do {
313 				size += 1024;
314 				if (result != NULL)
315 					free(result);
316 				result = calloc(1, size);
317 			} while (papiAttributeListToString(unused, " ",
318 			    result, size) != PAPI_OK);
319 			papiAttributeListFree(unused);
320 		}
321 		free(names);
322 	}
323 
324 	return (result);
325 }
326 
327 /*
328  * lpd_add_svr4_attributes
329  *	Solaris 2.x LP - BSD protocol extensions
330  */
331 static papi_status_t
332 lpd_add_svr4_attributes(service_t *svc, papi_attribute_t **attributes,
333 		char **metadata, papi_attribute_t ***used)
334 {
335 	papi_attribute_t *tmp[2];
336 	char *s;
337 	int integer;
338 
339 	if (svc == NULL)
340 		return (PAPI_BAD_REQUEST);
341 
342 	/* media */
343 	s = NULL;
344 	papiAttributeListGetString(attributes, NULL, "media", &s);
345 	if (s != NULL) {
346 		add_svr4_control_line(metadata, 'f', s);
347 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
348 		    "media", s);
349 	}
350 
351 	/* Handling */
352 	s = NULL;
353 	papiAttributeListGetString(attributes, NULL, "job-hold-until", &s);
354 	if ((s != NULL) && (strcmp(s, "indefinite"))) {
355 		add_svr4_control_line(metadata, 'H', "hold");
356 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
357 		    "job-hold-until", "indefinite");
358 	} else if ((s != NULL) && (strcmp(s, "no-hold"))) {
359 		add_svr4_control_line(metadata, 'H', "immediate");
360 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
361 		    "job-hold-until", "no-hold");
362 	} else if (s != NULL) {
363 		add_svr4_control_line(metadata, 'H', s);
364 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
365 		    "job-hold-until", s);
366 	}
367 
368 	/* Pages */
369 	s = NULL;
370 	memset(tmp, NULL, sizeof (tmp));
371 	tmp[0] = papiAttributeListFind(attributes, "page-ranges");
372 	if (tmp[0] != NULL) {
373 		char buf[BUFSIZ];
374 
375 		papiAttributeListToString(tmp, " ", buf, sizeof (buf));
376 		if ((s = strchr(buf, '=')) != NULL) {
377 			add_svr4_control_line(metadata, 'P', ++s);
378 			papiAttributeListAddString(used, PAPI_ATTR_EXCL,
379 			    "page-ranges", s);
380 		}
381 	}
382 
383 	/* Priority : lp -q */
384 	integer = -1;
385 	papiAttributeListGetInteger(attributes, NULL, "job-priority", &integer);
386 	if (integer != -1) {
387 		integer = 40 - (integer / 2.5);
388 		add_int_control_line(metadata, 'q', integer, LPD_SVR4);
389 		papiAttributeListAddInteger(used, PAPI_ATTR_EXCL,
390 		    "job-priority", integer);
391 	}
392 
393 	/* Charset : lp -S */
394 	s = NULL;
395 	papiAttributeListGetString(attributes, NULL, "lp-charset", &s);
396 	if (s != NULL) {
397 		add_svr4_control_line(metadata, 'S', s);
398 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
399 		    "lp-charset", s);
400 	}
401 
402 	/* Type : done when adding file  */
403 
404 	/* Mode : lp -y */
405 	s = NULL;
406 	papiAttributeListGetString(attributes, NULL, "lp-modes", &s);
407 	if (s != NULL) {
408 		add_svr4_control_line(metadata, 'y', s);
409 		papiAttributeListAddString(used, PAPI_ATTR_EXCL,
410 		    "lp-modes", s);
411 	}
412 
413 	/* Options lp -o are handled elsewhere */
414 	if ((s = unused_attributes(attributes, *used)) != NULL) {
415 		add_lpd_control_line(metadata, 'O', s);
416 		free(s);
417 	}
418 
419 	return (PAPI_OK);
420 }
421 
422 papi_status_t
423 lpd_add_hpux_attributes(service_t *svc, papi_attribute_t **attributes,
424 		char **metadata, papi_attribute_t ***used)
425 {
426 	char *s = NULL;
427 
428 	/* Options lp -o */
429 	if ((s = unused_attributes(attributes, *used)) != NULL) {
430 		add_hpux_control_line(metadata, s);
431 		free(s);
432 	}
433 
434 	return (PAPI_OK);
435 }
436 
437 papi_status_t
438 lpd_job_add_attributes(service_t *svc, papi_attribute_t **attributes,
439 		char **metadata, papi_attribute_t ***used)
440 {
441 	if ((svc == NULL) || (metadata == NULL))
442 		return (PAPI_BAD_REQUEST);
443 
444 	lpd_add_rfc1179_attributes(svc, attributes, metadata, used);
445 
446 	/* add protocol extensions if applicable */
447 	if (svc->uri->fragment != NULL) {
448 		if ((strcasecmp(svc->uri->fragment, "solaris") == 0) ||
449 		    (strcasecmp(svc->uri->fragment, "svr4") == 0))
450 			lpd_add_svr4_attributes(svc, attributes, metadata,
451 			    used);
452 		else if (strcasecmp(svc->uri->fragment, "hpux") == 0)
453 			lpd_add_hpux_attributes(svc, attributes, metadata,
454 			    used);
455 		/*
456 		 * others could be added here:
457 		 *	lprng, sco, aix, digital unix, xerox, ...
458 		 */
459 	}
460 
461 	return (PAPI_OK);
462 }
463 
464 papi_status_t
465 lpd_job_add_files(service_t *svc, papi_attribute_t **attributes,
466 		char **files, char **metadata, papi_attribute_t ***used)
467 {
468 	char *format = "text/plain";
469 	char rfc_fmt = 'l';
470 	int copies = 1;
471 	char host[BUFSIZ];
472 	int i;
473 
474 	if ((svc == NULL) || (attributes == NULL) || (files == NULL) ||
475 	    (metadata == NULL))
476 		return (PAPI_BAD_ARGUMENT);
477 
478 	papiAttributeListGetString(attributes, NULL, "document-format",
479 	    &format);
480 	papiAttributeListAddString(used, PAPI_ATTR_EXCL,
481 	    "document-format", format);
482 	if ((rfc_fmt = mime_type_to_rfc1179_type(format)) == '\0') {
483 		if ((svc->uri->fragment != NULL) &&
484 		    ((strcasecmp(svc->uri->fragment, "solaris") == 0) ||
485 		    (strcasecmp(svc->uri->fragment, "svr4") == 0)))
486 			add_svr4_control_line(metadata, 'T', format);
487 		rfc_fmt = 'l';
488 	}
489 
490 	papiAttributeListGetInteger(attributes, NULL, "copies", &copies);
491 	if (copies < 1)
492 		copies = 1;
493 	papiAttributeListAddInteger(used, PAPI_ATTR_EXCL, "copies", copies);
494 
495 	gethostname(host, sizeof (host));
496 
497 	for (i = 0; files[i] != NULL; i++) {
498 		char name[BUFSIZ];
499 		struct stat statbuf;
500 		char key;
501 		int j;
502 
503 		if ((strcmp("standard input", files[i]) != 0) &&
504 		    (access(files[i], R_OK) < 0)) {
505 			detailed_error(svc, gettext("aborting request, %s: %s"),
506 			    files[i], strerror(errno));
507 			return (PAPI_NOT_AUTHORIZED);
508 		}
509 		if (strcmp("standard input", files[i]) != 0) {
510 			stat(files[i], &statbuf);
511 			if (statbuf.st_size == 0) {
512 				detailed_error(svc,
513 				    gettext("Zero byte (empty) file: %s"),
514 				    files[i]);
515 				return (PAPI_BAD_ARGUMENT);
516 			}
517 		}
518 
519 		if (i < 26)
520 			key = 'A' + i;
521 		else if (i < 52)
522 			key = 'a' + (i - 26);
523 		else if (i < 62)
524 			key = '0' + (i - 52);
525 		else {
526 			detailed_error(svc,
527 			    gettext("too many files, truncated at 62"));
528 			return (PAPI_OK_SUBST);
529 		}
530 
531 		snprintf(name, sizeof (name), "df%cXXX%s", key, host);
532 
533 		for (j = 0; j < copies; j++)
534 			add_lpd_control_line(metadata, rfc_fmt, name);
535 		add_lpd_control_line(metadata, 'U', name);
536 		add_lpd_control_line(metadata, 'N', (char *)files[i]);
537 	}
538 
539 	return (PAPI_OK);
540 }
541 
542 papi_status_t
543 lpd_submit_job(service_t *svc, char *metadata, papi_attribute_t ***attributes,
544 		int *ofd)
545 {
546 	papi_status_t status = PAPI_INTERNAL_ERROR;
547 	int fd;
548 	char path[32];
549 	char *list[2];
550 
551 	if ((svc == NULL) || (metadata == NULL))
552 		return (PAPI_BAD_ARGUMENT);
553 
554 	strcpy(path, "/tmp/lpd-job-XXXXXX");
555 	fd = mkstemp(path);
556 	write(fd, metadata, strlen(metadata));
557 	close(fd);
558 
559 	list[0] = path;
560 	list[1] = NULL;
561 
562 	if (((fd = lpd_open(svc, 's', list, 15)) < 0) && (errno != EBADMSG)) {
563 		switch (errno) {
564 		case ENOSPC:
565 			status = PAPI_TEMPORARY_ERROR;
566 			break;
567 		case EIO:
568 			status = PAPI_TEMPORARY_ERROR;
569 			break;
570 		case ECONNREFUSED:
571 			status = PAPI_SERVICE_UNAVAILABLE;
572 			break;
573 		case ENOENT:
574 			status = PAPI_NOT_ACCEPTING;
575 			break;
576 		case EBADMSG:
577 		case EBADF:
578 			status = PAPI_OK;
579 			break;
580 		default:
581 			status = PAPI_TIMEOUT;
582 			break;
583 		}
584 	} else
585 		status = PAPI_OK;
586 
587 	if (ofd != NULL)
588 		*ofd = fd;
589 	else
590 		close(fd);
591 
592 	/* read the ID and add it to to the job */
593 	if ((fd = open(path, O_RDONLY)) >= 0) {
594 		int job_id = 0;
595 		read(fd, &job_id, sizeof (job_id));
596 		papiAttributeListAddInteger(attributes, PAPI_ATTR_REPLACE,
597 		    "job-id", job_id);
598 		close(fd);
599 	}
600 
601 	unlink(path);
602 
603 	return (status);
604 }
605