xref: /illumos-gate/usr/src/lib/print/libpapi-ipp/common/job.c (revision 69a119caa6570c7077699161b7c28b6ee9f8b0f4)
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 2010 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  *
26  */
27 
28 /* $Id: job.c 148 2006-04-25 16:54:17Z njacobs $ */
29 
30 
31 /*LINTLIBRARY*/
32 
33 #include <stdlib.h>
34 #include <errno.h>
35 #include <string.h>
36 #include <papi_impl.h>
37 #include <fcntl.h>
38 #include <sys/types.h>
39 #include <sys/stat.h>
40 #include <libintl.h>
41 
42 #ifndef OPID_CUPS_MOVE_JOB
43 #define	OPID_CUPS_MOVE_JOB 0x400D
44 #endif
45 
46 void
47 papiJobFree(papi_job_t job)
48 {
49 	job_t *tmp = (job_t *)job;
50 
51 	if (tmp != NULL) {
52 		if (tmp->attributes != NULL)
53 			papiAttributeListFree(tmp->attributes);
54 		free(tmp);
55 	}
56 }
57 
58 void
59 papiJobListFree(papi_job_t *jobs)
60 {
61 	if (jobs != NULL) {
62 		int i;
63 
64 		for (i = 0; jobs[i] != NULL; i++)
65 			papiJobFree(jobs[i]);
66 		free(jobs);
67 	}
68 }
69 
70 papi_attribute_t **
71 papiJobGetAttributeList(papi_job_t job)
72 {
73 	papi_attribute_t **result = NULL;
74 	job_t *j = job;
75 
76 	if (j != NULL)
77 		result = j->attributes;
78 
79 	return (result);
80 }
81 
82 char *
83 papiJobGetPrinterName(papi_job_t job)
84 {
85 	char *result = NULL;
86 	job_t *j = job;
87 
88 	if (j != NULL)
89 		(void) papiAttributeListGetString(j->attributes, NULL,
90 		    "printer-name", &result);
91 
92 	return (result);
93 }
94 
95 int32_t
96 papiJobGetId(papi_job_t job)
97 {
98 	int32_t result = -1;
99 	job_t *j = job;
100 
101 	if (j != NULL)
102 		(void) papiAttributeListGetInteger(j->attributes, NULL,
103 		    "job-id", &result);
104 
105 	return (result);
106 }
107 
108 papi_job_ticket_t *
109 papiJobGetJobTicket(papi_job_t job)
110 {
111 	papi_job_ticket_t *result = NULL;
112 
113 	return (result);
114 }
115 
116 static void
117 populate_job_request(service_t *svc, papi_attribute_t ***request,
118 		papi_attribute_t **attributes, char *printer, uint16_t type)
119 {
120 	papi_attribute_t **operational = NULL, **job = NULL;
121 	static char *operational_names[] = {
122 		"job-name", "ipp-attribute-fidelity", "document-name",
123 		"compression", "document-format", "document-natural-language",
124 		"job-k-octets", "job-impressions", "job-media-sheets", NULL
125 	};
126 
127 	/* create the base IPP request */
128 	ipp_initialize_request(svc, request, type);
129 
130 	/* create an operational attributes group */
131 	ipp_initialize_operational_attributes(svc, &operational, printer, -1);
132 
133 	/* split up the attributes into operational and job attributes */
134 	split_and_copy_attributes(operational_names, attributes,
135 	    &operational, &job);
136 
137 	/* add the operational attributes group to the request */
138 	papiAttributeListAddCollection(request, PAPI_ATTR_REPLACE,
139 	    "operational-attributes-group", operational);
140 	papiAttributeListFree(operational);
141 
142 	/* add the job attributes group to the request */
143 	if (job != NULL) {
144 		/*
145 		 * Add job-originating-host-name to attributes
146 		 * if not already set.
147 		 */
148 		char *hostname = NULL;
149 
150 		papiAttributeListGetString(job, NULL,
151 		    "job-originating-host-name", &hostname);
152 
153 		if (hostname == NULL) {
154 			char host[BUFSIZ];
155 
156 			if (gethostname(host, sizeof (host)) == 0)
157 				papiAttributeListAddString(&job, PAPI_ATTR_EXCL,
158 				    "job-originating-host-name", host);
159 		}
160 
161 		papiAttributeListAddCollection(request, PAPI_ATTR_REPLACE,
162 		    "job-attributes-group", job);
163 		papiAttributeListFree(job);
164 	}
165 }
166 
167 static papi_status_t
168 send_document_uri(service_t *svc, char *file, papi_attribute_t **attributes,
169 		char *printer, int32_t id, char last, uint16_t type)
170 {
171 	papi_status_t result = PAPI_INTERNAL_ERROR;
172 	papi_attribute_t **request = NULL, **op = NULL, **response = NULL;
173 
174 	/* create the base IPP request */
175 	ipp_initialize_request(svc, &request, type);
176 
177 	/* create an operational attributes group */
178 	ipp_initialize_operational_attributes(svc, &op, printer, id);
179 
180 	papiAttributeListAddString(&op, PAPI_ATTR_REPLACE, "document-name",
181 	    file);
182 	papiAttributeListAddBoolean(&op, PAPI_ATTR_REPLACE, "last-document",
183 	    (last ? PAPI_TRUE : PAPI_FALSE));
184 	papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
185 	    "operational-attributes-group", op);
186 	papiAttributeListFree(op);
187 
188 	/* send the IPP request to the server */
189 	result = ipp_send_request_with_file(svc, request, &response, file);
190 	papiAttributeListFree(request);
191 	papiAttributeListFree(response);
192 
193 	return (result);
194 }
195 
196 typedef enum {_WITH_DATA, _BY_REFERENCE, _VALIDATE} call_type_t;
197 
198 papi_status_t
199 internal_job_submit(papi_service_t handle, char *printer,
200 		papi_attribute_t **job_attributes,
201 		papi_job_ticket_t *job_ticket,
202 		char **files, papi_job_t *job,
203 		call_type_t call_type)
204 {
205 	papi_status_t result = PAPI_INTERNAL_ERROR;
206 	service_t *svc = handle;
207 	struct stat statbuf;
208 	job_t *j = NULL;
209 	int i;
210 	uint16_t req_type = OPID_PRINT_JOB;
211 	uint16_t data_type = OPID_SEND_DOCUMENT;
212 	papi_attribute_t **request = NULL, **response = NULL;
213 
214 	if ((svc == NULL) || (printer == NULL) || (job == NULL))
215 		return (PAPI_BAD_ARGUMENT);
216 
217 	switch (call_type) {
218 	case _BY_REFERENCE:
219 #ifdef SOME_DAY_WE_WILL_BE_ABLE_TO_USE_URIS_FOR_JOB_DATA
220 		/*
221 		 * For the time being, this is disabled.  There are a number
222 		 * of issues to be dealt with before we can send a URI
223 		 * across the network to the server.  For example, the file
224 		 * name(s) passed in are most likely relative to the current
225 		 * hosts filesystem.  They also most likely will require some
226 		 * form of authentication information to be passed with the
227 		 * URI.
228 		 */
229 		req_type = OPID_PRINT_URI;
230 		req_type = OPID_SEND_URI;
231 #endif
232 		/* fall-through */
233 	case _WITH_DATA:
234 		if ((files == NULL) || (files[0] == NULL))
235 			return (PAPI_BAD_ARGUMENT);
236 
237 		if (files[1] != NULL)	/* more than 1 file */
238 			req_type = OPID_CREATE_JOB;
239 
240 		break;
241 	case _VALIDATE:
242 		req_type = OPID_VALIDATE_JOB;
243 		/* if we have files, validate access to them */
244 		if (files != NULL) {
245 			for (i = 0; files[i] != NULL; i++) {
246 				if (access(files[i], R_OK) < 0) {
247 					detailed_error(svc, "%s: %s", files[i],
248 					    strerror(errno));
249 					return (PAPI_DOCUMENT_ACCESS_ERROR);
250 				}
251 
252 				if (strcmp("standard input", files[i]) != 0) {
253 					if (stat(files[i], &statbuf) < 0) {
254 						detailed_error(svc, gettext(
255 						    "Cannot access file: %s:"
256 						    " %s"), files[i],
257 						    strerror(errno));
258 						return (
259 						    PAPI_DOCUMENT_ACCESS_ERROR);
260 					}
261 					if (statbuf.st_size == 0) {
262 						detailed_error(svc,
263 						    "Zero byte (empty) file: "
264 						    "%s",
265 						    files[i]);
266 						return (PAPI_BAD_ARGUMENT);
267 					}
268 				}
269 			}
270 			files = NULL;
271 		}
272 		break;
273 	}
274 
275 	/* if we are already connected, use that connection. */
276 	if (svc->connection == NULL)
277 		if ((result = service_connect(svc, printer)) != PAPI_OK)
278 			return (result);
279 
280 	if ((*job = j = calloc(1, sizeof (*j))) == NULL)
281 		return (PAPI_TEMPORARY_ERROR);
282 
283 	/*
284 	 * before creating IPP request
285 	 * add the job-name
286 	 */
287 	if ((files != NULL) && (files[0] != NULL))
288 		papiAttributeListAddString(&job_attributes, PAPI_ATTR_EXCL,
289 		    "job-name", files[0]);
290 
291 	/* create IPP request */
292 	populate_job_request(svc, &request, job_attributes, printer, req_type);
293 
294 	switch (req_type) {
295 	case OPID_PRINT_JOB:
296 		result = ipp_send_request_with_file(svc, request, &response,
297 		    files[0]);
298 		break;
299 	case OPID_CREATE_JOB:
300 	case OPID_VALIDATE_JOB:
301 	case OPID_PRINT_URI:
302 		result = ipp_send_request(svc, request, &response);
303 		break;
304 	}
305 	papiAttributeListFree(request);
306 
307 	if (result == PAPI_OK) {
308 		papi_attribute_t **op = NULL;
309 
310 		/* retrieve the job attributes */
311 		papiAttributeListGetCollection(response, NULL,
312 		    "job-attributes-group", &op);
313 		copy_attributes(&j->attributes, op);
314 
315 		if (req_type == OPID_CREATE_JOB) {
316 			int32_t id = 0;
317 
318 			papiAttributeListGetInteger(j->attributes, NULL,
319 			    "job-id", &id);
320 			/* send each document */
321 			for (i = 0; ((result == PAPI_OK) && (files[i] != NULL));
322 			    i++)
323 				result = send_document_uri(svc, files[i],
324 				    job_attributes,
325 				    printer, id, (files[i+1]?0:1),
326 				    data_type);
327 		}
328 	}
329 	papiAttributeListFree(response);
330 
331 	return (result);
332 }
333 
334 papi_status_t
335 papiJobSubmit(papi_service_t handle, char *printer,
336 		papi_attribute_t **job_attributes,
337 		papi_job_ticket_t *job_ticket, char **files, papi_job_t *job)
338 {
339 	return (internal_job_submit(handle, printer, job_attributes,
340 	    job_ticket, files, job, _WITH_DATA));
341 }
342 
343 papi_status_t
344 papiJobSubmitByReference(papi_service_t handle, char *printer,
345 		papi_attribute_t **job_attributes,
346 		papi_job_ticket_t *job_ticket, char **files, papi_job_t *job)
347 {
348 	return (internal_job_submit(handle, printer, job_attributes,
349 	    job_ticket, files, job, _BY_REFERENCE));
350 }
351 
352 papi_status_t
353 papiJobValidate(papi_service_t handle, char *printer,
354 		papi_attribute_t **job_attributes,
355 		papi_job_ticket_t *job_ticket, char **files, papi_job_t *job)
356 {
357 	return (internal_job_submit(handle, printer, job_attributes,
358 	    job_ticket, files, job, _VALIDATE));
359 }
360 
361 papi_status_t
362 papiJobStreamOpen(papi_service_t handle, char *printer,
363 		papi_attribute_t **job_attributes,
364 		papi_job_ticket_t *job_ticket, papi_stream_t *stream)
365 {
366 	papi_status_t result = PAPI_INTERNAL_ERROR;
367 	papi_attribute_t **request = NULL;
368 	service_t *svc = handle;
369 
370 	if ((svc == NULL) || (printer == NULL) || (stream == NULL))
371 		return (PAPI_BAD_ARGUMENT);
372 
373 	/* if we are already connected, use that connection. */
374 	if (svc->connection == NULL)
375 		if ((result = service_connect(svc, printer)) != PAPI_OK)
376 			return (result);
377 
378 	papiAttributeListAddString(&job_attributes, PAPI_ATTR_EXCL,
379 	    "job-name", "standard input");
380 
381 	/* create job request */
382 	populate_job_request(svc, &request, job_attributes, printer,
383 	    OPID_PRINT_JOB);
384 
385 	*stream = svc->connection;
386 
387 	result = ipp_send_initial_request_block(svc, request, 0);
388 	papiAttributeListFree(request);
389 
390 	return (result);
391 }
392 
393 papi_status_t
394 papiJobStreamWrite(papi_service_t handle,
395 		papi_stream_t stream, void *buffer, size_t buflen)
396 {
397 	papi_status_t result = PAPI_OK;
398 	service_t *svc = handle;
399 	size_t rc;
400 
401 #ifdef DEBUG
402 	printf("papiJobStreamWrite(0x%8.8x, 0x%8.8x, 0x%8.8x, %d)\n",
403 	    handle, stream, buffer, buflen);
404 	httpDumpData(stdout, "papiJobStreamWrite:", buffer, buflen);
405 #endif
406 
407 	if ((svc == NULL) || (stream == NULL) || (buffer == NULL) ||
408 	    (buflen == 0))
409 		return (PAPI_BAD_ARGUMENT);
410 
411 	while ((result == PAPI_OK) && (buflen > 0)) {
412 		rc = ipp_request_write(svc, buffer, buflen);
413 		if (rc < 0)
414 			result = PAPI_TEMPORARY_ERROR;
415 		else {
416 			buffer = (char *)buffer + rc;
417 			buflen -= rc;
418 		}
419 	}
420 
421 #ifdef DEBUG
422 	printf("papiJobStreamWrite(): %s\n", papiStatusString(result));
423 #endif
424 
425 	return (result);
426 }
427 
428 papi_status_t
429 papiJobStreamClose(papi_service_t handle,
430 		papi_stream_t stream, papi_job_t *job)
431 {
432 	papi_status_t result = PAPI_INTERNAL_ERROR;
433 	http_status_t status = HTTP_CONTINUE;
434 	service_t *svc = handle;
435 	papi_attribute_t **response = NULL;
436 	job_t *j = NULL;
437 
438 	if ((svc == NULL) || (stream == NULL) || (job == NULL))
439 		return (PAPI_BAD_ARGUMENT);
440 
441 	if ((*job = j = calloc(1, sizeof (*j))) == NULL)
442 		return (PAPI_TEMPORARY_ERROR);
443 
444 	(void) ipp_request_write(svc, "", 0);
445 
446 	/* update our connection info */
447 	while (status == HTTP_CONTINUE)
448 		status = httpUpdate(svc->connection);
449 
450 	if (status != HTTP_OK)
451 		return (http_to_papi_status(status));
452 	httpWait(svc->connection, 1000);
453 
454 	/* read the IPP response */
455 	result = ipp_read_message(&ipp_request_read, svc, &response,
456 	    IPP_TYPE_RESPONSE);
457 	if (result == PAPI_OK)
458 		result = ipp_status_info(svc, response);
459 
460 	if (result == PAPI_OK) {
461 		papi_attribute_t **op = NULL;
462 
463 		papiAttributeListGetCollection(response, NULL,
464 		    "job-attributes-group", &op);
465 		copy_attributes(&j->attributes, op);
466 	}
467 	papiAttributeListFree(response);
468 
469 	return (result);
470 }
471 
472 papi_status_t
473 papiJobQuery(papi_service_t handle, char *printer, int32_t job_id,
474 		char **requested_attrs,
475 		papi_job_t *job)
476 {
477 	papi_status_t result = PAPI_INTERNAL_ERROR;
478 	service_t *svc = handle;
479 	job_t *j = NULL;
480 	papi_attribute_t **request = NULL, **op = NULL, **response = NULL;
481 
482 	if ((svc == NULL) || (printer == NULL))
483 		return (PAPI_BAD_ARGUMENT);
484 
485 	/* if we are already connected, use that connection. */
486 	if (svc->connection == NULL)
487 		if ((result = service_connect(svc, printer)) != PAPI_OK)
488 			return (result);
489 
490 	if ((*job = j = calloc(1, sizeof (*j))) == NULL)
491 		return (PAPI_TEMPORARY_ERROR);
492 
493 	ipp_initialize_request(svc, &request, OPID_GET_JOB_ATTRIBUTES);
494 
495 	ipp_initialize_operational_attributes(svc, &op, printer, job_id);
496 
497 	if (requested_attrs != NULL) {
498 		int i;
499 
500 		for (i = 0; requested_attrs[i] != NULL; i++)
501 			papiAttributeListAddString(&op, PAPI_ATTR_APPEND,
502 			    "requested-attributes", requested_attrs[i]);
503 	}
504 
505 	papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
506 	    "operational-attributes-group", op);
507 	papiAttributeListFree(op);
508 	result = ipp_send_request(svc, request, &response);
509 	papiAttributeListFree(request);
510 
511 	op = NULL;
512 	papiAttributeListGetCollection(response, NULL,
513 	    "job-attributes-group", &op);
514 	copy_attributes(&j->attributes, op);
515 	papiAttributeListFree(response);
516 
517 	return (result);
518 }
519 
520 /* papiJob{Cancel|Hold|Release|Restart|Promote} are all the same */
521 static papi_status_t
522 _job_cancel_hold_release_restart_promote(papi_service_t handle,
523 		char *printer, int32_t job_id, uint16_t type)
524 {
525 	papi_status_t result = PAPI_INTERNAL_ERROR;
526 	service_t *svc = handle;
527 	papi_attribute_t **request = NULL, **op = NULL, **response = NULL;
528 
529 	if ((svc == NULL) || (printer == NULL) || (job_id < 0))
530 		return (PAPI_BAD_ARGUMENT);
531 
532 	/* if we are already connected, use that connection. */
533 	if (svc->connection == NULL)
534 		if ((result = service_connect(svc, printer)) != PAPI_OK)
535 			return (result);
536 
537 	ipp_initialize_request(svc, &request, type);
538 
539 	ipp_initialize_operational_attributes(svc, &op, printer, job_id);
540 
541 	papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
542 	    "operational-attributes-group", op);
543 	papiAttributeListFree(op);
544 	result = ipp_send_request(svc, request, &response);
545 	papiAttributeListFree(request);
546 	papiAttributeListFree(response);
547 
548 	return (result);
549 }
550 
551 papi_status_t
552 papiJobCancel(papi_service_t handle, char *printer, int32_t job_id)
553 {
554 	return (_job_cancel_hold_release_restart_promote(handle, printer,
555 	    job_id, OPID_CANCEL_JOB));
556 }
557 
558 
559 papi_status_t
560 papiJobHold(papi_service_t handle, char *printer, int32_t job_id)
561 {
562 	return (_job_cancel_hold_release_restart_promote(handle, printer,
563 	    job_id, OPID_HOLD_JOB));
564 }
565 
566 papi_status_t
567 papiJobRelease(papi_service_t handle, char *printer, int32_t job_id)
568 {
569 	return (_job_cancel_hold_release_restart_promote(handle, printer,
570 	    job_id, OPID_RELEASE_JOB));
571 }
572 
573 papi_status_t
574 papiJobRestart(papi_service_t handle, char *printer, int32_t job_id)
575 {
576 	return (_job_cancel_hold_release_restart_promote(handle, printer,
577 	    job_id, OPID_RESTART_JOB));
578 }
579 
580 papi_status_t
581 papiJobPromote(papi_service_t handle, char *printer, int32_t job_id)
582 {
583 	return (_job_cancel_hold_release_restart_promote(handle, printer,
584 	    job_id, OPID_PROMOTE_JOB));
585 }
586 
587 papi_status_t
588 papiJobMove(papi_service_t handle, char *printer, int32_t job_id,
589 		char *destination)
590 {
591 	papi_status_t result = PAPI_INTERNAL_ERROR;
592 	service_t *svc = handle;
593 	papi_attribute_t **request = NULL, **op = NULL, **response = NULL;
594 
595 	if ((svc == NULL) || (printer == NULL) || (job_id < 0) ||
596 	    (destination == NULL))
597 		return (PAPI_BAD_ARGUMENT);
598 
599 	/* if we are already connected, use that connection. */
600 	if (svc->connection == NULL)
601 		if ((result = service_connect(svc, printer)) != PAPI_OK)
602 			return (result);
603 
604 	ipp_initialize_request(svc, &request, OPID_CUPS_MOVE_JOB);
605 
606 	ipp_initialize_operational_attributes(svc, &op, printer, job_id);
607 
608 	papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
609 	    "operational-attributes-group", op);
610 	papiAttributeListFree(op);
611 
612 	op = NULL;
613 	papiAttributeListAddString(&op, PAPI_ATTR_EXCL,
614 	    "job-printer-uri", destination);
615 	papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
616 	    "job-attributes-group", op);
617 	papiAttributeListFree(op);
618 
619 	result = ipp_send_request(svc, request, &response);
620 	papiAttributeListFree(request);
621 	papiAttributeListFree(response);
622 
623 	return (result);
624 }
625 
626 papi_status_t
627 papiJobModify(papi_service_t handle, char *printer, int32_t job_id,
628 		papi_attribute_t **attributes, papi_job_t *job)
629 {
630 	papi_status_t result = PAPI_INTERNAL_ERROR;
631 	service_t *svc = handle;
632 	papi_attribute_t **request = NULL, **op = NULL, **response = NULL;
633 	job_t *j = NULL;
634 
635 	if ((svc == NULL) || (printer == NULL) || (job_id < 0) ||
636 	    (attributes == NULL))
637 		return (PAPI_BAD_ARGUMENT);
638 
639 	if ((*job = j = calloc(1, sizeof (*j))) == NULL)
640 		return (PAPI_TEMPORARY_ERROR);
641 
642 	/* if we are already connected, use that connection. */
643 	if (svc->connection == NULL)
644 		if ((result = service_connect(svc, printer)) != PAPI_OK)
645 			return (result);
646 
647 	ipp_initialize_request(svc, &request, OPID_SET_JOB_ATTRIBUTES);
648 
649 	ipp_initialize_operational_attributes(svc, &op, printer, job_id);
650 
651 	papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
652 	    "operational-attributes-group", op);
653 	papiAttributeListFree(op);
654 	papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
655 	    "job-attributes-group", attributes);
656 	result = ipp_send_request(svc, request, &response);
657 	papiAttributeListFree(request);
658 
659 	op = NULL;
660 	papiAttributeListGetCollection(response, NULL,
661 	    "job-attributes-group", &op);
662 	copy_attributes(&j->attributes, op);
663 	papiAttributeListFree(response);
664 
665 	return (result);
666 }
667