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