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