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