xref: /illumos-gate/usr/src/lib/print/libpapi-ipp/common/job.c (revision dbed73cbda2229fd1aa6dc5743993cae7f0a7ee9)
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: 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 	/* create job request */
379 	populate_job_request(svc, &request, job_attributes, printer,
380 	    OPID_PRINT_JOB);
381 
382 	*stream = svc->connection;
383 
384 	result = ipp_send_initial_request_block(svc, request, 0);
385 	papiAttributeListFree(request);
386 
387 	return (result);
388 }
389 
390 papi_status_t
391 papiJobStreamWrite(papi_service_t handle,
392 		papi_stream_t stream, void *buffer, size_t buflen)
393 {
394 	papi_status_t result = PAPI_OK;
395 	service_t *svc = handle;
396 	size_t rc;
397 
398 #ifdef DEBUG
399 	printf("papiJobStreamWrite(0x%8.8x, 0x%8.8x, 0x%8.8x, %d)\n",
400 	    handle, stream, buffer, buflen);
401 	httpDumpData(stdout, "papiJobStreamWrite:", buffer, buflen);
402 #endif
403 
404 	if ((svc == NULL) || (stream == NULL) || (buffer == NULL) ||
405 	    (buflen == 0))
406 		return (PAPI_BAD_ARGUMENT);
407 
408 	while ((result == PAPI_OK) && (buflen > 0)) {
409 		rc = ipp_request_write(svc, buffer, buflen);
410 		if (rc < 0)
411 			result = PAPI_TEMPORARY_ERROR;
412 		else {
413 			buffer = (char *)buffer + rc;
414 			buflen -= rc;
415 		}
416 	}
417 
418 #ifdef DEBUG
419 	printf("papiJobStreamWrite(): %s\n", papiStatusString(result));
420 #endif
421 
422 	return (result);
423 }
424 
425 papi_status_t
426 papiJobStreamClose(papi_service_t handle,
427 		papi_stream_t stream, papi_job_t *job)
428 {
429 	papi_status_t result = PAPI_INTERNAL_ERROR;
430 	http_status_t status = HTTP_CONTINUE;
431 	service_t *svc = handle;
432 	papi_attribute_t **response = NULL;
433 	job_t *j = NULL;
434 
435 	if ((svc == NULL) || (stream == NULL) || (job == NULL))
436 		return (PAPI_BAD_ARGUMENT);
437 
438 	if ((*job = j = calloc(1, sizeof (*j))) == NULL)
439 		return (PAPI_TEMPORARY_ERROR);
440 
441 	(void) ipp_request_write(svc, "", 0);
442 
443 	/* update our connection info */
444 	while (status == HTTP_CONTINUE)
445 		status = httpUpdate(svc->connection);
446 
447 	if (status != HTTP_OK)
448 		return (http_to_papi_status(status));
449 	httpWait(svc->connection, 1000);
450 
451 	/* read the IPP response */
452 	result = ipp_read_message(&ipp_request_read, svc, &response,
453 	    IPP_TYPE_RESPONSE);
454 	if (result == PAPI_OK)
455 		result = ipp_status_info(svc, response);
456 
457 	if (result == PAPI_OK) {
458 		papi_attribute_t **op = NULL;
459 
460 		papiAttributeListGetCollection(response, NULL,
461 		    "job-attributes-group", &op);
462 		copy_attributes(&j->attributes, op);
463 	}
464 	papiAttributeListFree(response);
465 
466 	return (result);
467 }
468 
469 papi_status_t
470 papiJobQuery(papi_service_t handle, char *printer, int32_t job_id,
471 		char **requested_attrs,
472 		papi_job_t *job)
473 {
474 	papi_status_t result = PAPI_INTERNAL_ERROR;
475 	service_t *svc = handle;
476 	job_t *j = NULL;
477 	papi_attribute_t **request = NULL, **op = NULL, **response = NULL;
478 
479 	if ((svc == NULL) || (printer == NULL))
480 		return (PAPI_BAD_ARGUMENT);
481 
482 	/* if we are already connected, use that connection. */
483 	if (svc->connection == NULL)
484 		if ((result = service_connect(svc, printer)) != PAPI_OK)
485 			return (result);
486 
487 	if ((*job = j = calloc(1, sizeof (*j))) == NULL)
488 		return (PAPI_TEMPORARY_ERROR);
489 
490 	ipp_initialize_request(svc, &request, OPID_GET_JOB_ATTRIBUTES);
491 
492 	ipp_initialize_operational_attributes(svc, &op, printer, job_id);
493 
494 	if (requested_attrs != NULL) {
495 		int i;
496 
497 		for (i = 0; requested_attrs[i] != NULL; i++)
498 			papiAttributeListAddString(&op, PAPI_ATTR_APPEND,
499 			    "requested-attributes", requested_attrs[i]);
500 	}
501 
502 	papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
503 	    "operational-attributes-group", op);
504 	papiAttributeListFree(op);
505 	result = ipp_send_request(svc, request, &response);
506 	papiAttributeListFree(request);
507 
508 	op = NULL;
509 	papiAttributeListGetCollection(response, NULL,
510 	    "job-attributes-group", &op);
511 	copy_attributes(&j->attributes, op);
512 	papiAttributeListFree(response);
513 
514 	return (result);
515 }
516 
517 /* papiJob{Cancel|Hold|Release|Restart|Promote} are all the same */
518 static papi_status_t
519 _job_cancel_hold_release_restart_promote(papi_service_t handle,
520 		char *printer, int32_t job_id, uint16_t type)
521 {
522 	papi_status_t result = PAPI_INTERNAL_ERROR;
523 	service_t *svc = handle;
524 	papi_attribute_t **request = NULL, **op = NULL, **response = NULL;
525 
526 	if ((svc == NULL) || (printer == NULL) || (job_id < 0))
527 		return (PAPI_BAD_ARGUMENT);
528 
529 	/* if we are already connected, use that connection. */
530 	if (svc->connection == NULL)
531 		if ((result = service_connect(svc, printer)) != PAPI_OK)
532 			return (result);
533 
534 	ipp_initialize_request(svc, &request, type);
535 
536 	ipp_initialize_operational_attributes(svc, &op, printer, job_id);
537 
538 	papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
539 	    "operational-attributes-group", op);
540 	papiAttributeListFree(op);
541 	result = ipp_send_request(svc, request, &response);
542 	papiAttributeListFree(request);
543 	papiAttributeListFree(response);
544 
545 	return (result);
546 }
547 
548 papi_status_t
549 papiJobCancel(papi_service_t handle, char *printer, int32_t job_id)
550 {
551 	return (_job_cancel_hold_release_restart_promote(handle, printer,
552 	    job_id, OPID_CANCEL_JOB));
553 }
554 
555 
556 papi_status_t
557 papiJobHold(papi_service_t handle, char *printer, int32_t job_id)
558 {
559 	return (_job_cancel_hold_release_restart_promote(handle, printer,
560 	    job_id, OPID_HOLD_JOB));
561 }
562 
563 papi_status_t
564 papiJobRelease(papi_service_t handle, char *printer, int32_t job_id)
565 {
566 	return (_job_cancel_hold_release_restart_promote(handle, printer,
567 	    job_id, OPID_RELEASE_JOB));
568 }
569 
570 papi_status_t
571 papiJobRestart(papi_service_t handle, char *printer, int32_t job_id)
572 {
573 	return (_job_cancel_hold_release_restart_promote(handle, printer,
574 	    job_id, OPID_RESTART_JOB));
575 }
576 
577 papi_status_t
578 papiJobPromote(papi_service_t handle, char *printer, int32_t job_id)
579 {
580 	return (_job_cancel_hold_release_restart_promote(handle, printer,
581 	    job_id, OPID_PROMOTE_JOB));
582 }
583 
584 papi_status_t
585 papiJobMove(papi_service_t handle, char *printer, int32_t job_id,
586 		char *destination)
587 {
588 	papi_status_t result = PAPI_INTERNAL_ERROR;
589 	service_t *svc = handle;
590 	papi_attribute_t **request = NULL, **op = NULL, **response = NULL;
591 
592 	if ((svc == NULL) || (printer == NULL) || (job_id < 0) ||
593 	    (destination == NULL))
594 		return (PAPI_BAD_ARGUMENT);
595 
596 	/* if we are already connected, use that connection. */
597 	if (svc->connection == NULL)
598 		if ((result = service_connect(svc, printer)) != PAPI_OK)
599 			return (result);
600 
601 	ipp_initialize_request(svc, &request, OPID_CUPS_MOVE_JOB);
602 
603 	ipp_initialize_operational_attributes(svc, &op, printer, job_id);
604 
605 	papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
606 	    "operational-attributes-group", op);
607 	papiAttributeListFree(op);
608 
609 	op = NULL;
610 	papiAttributeListAddString(&op, PAPI_ATTR_EXCL,
611 	    "job-printer-uri", destination);
612 	papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
613 	    "job-attributes-group", op);
614 	papiAttributeListFree(op);
615 
616 	result = ipp_send_request(svc, request, &response);
617 	papiAttributeListFree(request);
618 	papiAttributeListFree(response);
619 
620 	return (result);
621 }
622 
623 papi_status_t
624 papiJobModify(papi_service_t handle, char *printer, int32_t job_id,
625 		papi_attribute_t **attributes, papi_job_t *job)
626 {
627 	papi_status_t result = PAPI_INTERNAL_ERROR;
628 	service_t *svc = handle;
629 	papi_attribute_t **request = NULL, **op = NULL, **response = NULL;
630 	job_t *j = NULL;
631 
632 	if ((svc == NULL) || (printer == NULL) || (job_id < 0) ||
633 	    (attributes == NULL))
634 		return (PAPI_BAD_ARGUMENT);
635 
636 	if ((*job = j = calloc(1, sizeof (*j))) == NULL)
637 		return (PAPI_TEMPORARY_ERROR);
638 
639 	/* if we are already connected, use that connection. */
640 	if (svc->connection == NULL)
641 		if ((result = service_connect(svc, printer)) != PAPI_OK)
642 			return (result);
643 
644 	ipp_initialize_request(svc, &request, OPID_SET_JOB_ATTRIBUTES);
645 
646 	ipp_initialize_operational_attributes(svc, &op, printer, job_id);
647 
648 	papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
649 	    "operational-attributes-group", op);
650 	papiAttributeListFree(op);
651 	papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
652 	    "job-attributes-group", attributes);
653 	result = ipp_send_request(svc, request, &response);
654 	papiAttributeListFree(request);
655 
656 	op = NULL;
657 	papiAttributeListGetCollection(response, NULL,
658 	    "job-attributes-group", &op);
659 	copy_attributes(&j->attributes, op);
660 	papiAttributeListFree(response);
661 
662 	return (result);
663 }
664