xref: /illumos-gate/usr/src/lib/print/libpapi-ipp/common/job.c (revision 3d09a4fec6be19a6f09e277d5d5d17942bb4abf4)
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 	/* create IPP request */
276 	populate_job_request(svc, &request, job_attributes, printer, req_type);
277 
278 	switch (req_type) {
279 	case OPID_PRINT_JOB:
280 		result = ipp_send_request_with_file(svc, request, &response,
281 		    files[0]);
282 		break;
283 	case OPID_CREATE_JOB:
284 	case OPID_VALIDATE_JOB:
285 	case OPID_PRINT_URI:
286 		result = ipp_send_request(svc, request, &response);
287 		break;
288 	}
289 	papiAttributeListFree(request);
290 
291 	if (result == PAPI_OK) {
292 		papi_attribute_t **op = NULL;
293 
294 		/* retrieve the job attributes */
295 		papiAttributeListGetCollection(response, NULL,
296 		    "job-attributes-group", &op);
297 		copy_attributes(&j->attributes, op);
298 
299 		if (req_type == OPID_CREATE_JOB) {
300 			int32_t id = 0;
301 
302 			papiAttributeListGetInteger(j->attributes, NULL,
303 			    "job-id", &id);
304 			/* send each document */
305 			for (i = 0; ((result == PAPI_OK) && (files[i] != NULL));
306 			    i++)
307 				result = send_document_uri(svc, files[i],
308 				    job_attributes,
309 				    printer, id, (files[i+1]?0:1),
310 				    data_type);
311 		}
312 	}
313 	papiAttributeListFree(response);
314 
315 	return (result);
316 }
317 
318 papi_status_t
319 papiJobSubmit(papi_service_t handle, char *printer,
320 		papi_attribute_t **job_attributes,
321 		papi_job_ticket_t *job_ticket, char **files, papi_job_t *job)
322 {
323 	return (internal_job_submit(handle, printer, job_attributes,
324 	    job_ticket, files, job, _WITH_DATA));
325 }
326 
327 papi_status_t
328 papiJobSubmitByReference(papi_service_t handle, char *printer,
329 		papi_attribute_t **job_attributes,
330 		papi_job_ticket_t *job_ticket, char **files, papi_job_t *job)
331 {
332 	return (internal_job_submit(handle, printer, job_attributes,
333 	    job_ticket, files, job, _BY_REFERENCE));
334 }
335 
336 papi_status_t
337 papiJobValidate(papi_service_t handle, char *printer,
338 		papi_attribute_t **job_attributes,
339 		papi_job_ticket_t *job_ticket, char **files, papi_job_t *job)
340 {
341 	return (internal_job_submit(handle, printer, job_attributes,
342 	    job_ticket, files, job, _VALIDATE));
343 }
344 
345 papi_status_t
346 papiJobStreamOpen(papi_service_t handle, char *printer,
347 		papi_attribute_t **job_attributes,
348 		papi_job_ticket_t *job_ticket, papi_stream_t *stream)
349 {
350 	papi_status_t result = PAPI_INTERNAL_ERROR;
351 	papi_attribute_t **request = NULL;
352 	service_t *svc = handle;
353 
354 	if ((svc == NULL) || (printer == NULL) || (stream == NULL))
355 		return (PAPI_BAD_ARGUMENT);
356 
357 	/* if we are already connected, use that connection. */
358 	if (svc->connection == NULL)
359 		if ((result = service_connect(svc, printer)) != PAPI_OK)
360 			return (result);
361 
362 	/* create job request */
363 	populate_job_request(svc, &request, job_attributes, printer,
364 	    OPID_PRINT_JOB);
365 
366 	*stream = svc->connection;
367 
368 	result = ipp_send_initial_request_block(svc, request, 0);
369 	papiAttributeListFree(request);
370 
371 	return (result);
372 }
373 
374 papi_status_t
375 papiJobStreamWrite(papi_service_t handle,
376 		papi_stream_t stream, void *buffer, size_t buflen)
377 {
378 	papi_status_t result = PAPI_OK;
379 	service_t *svc = handle;
380 	size_t rc;
381 
382 #ifdef DEBUG
383 	printf("papiJobStreamWrite(0x%8.8x, 0x%8.8x, 0x%8.8x, %d)\n",
384 	    handle, stream, buffer, buflen);
385 	httpDumpData(stdout, "papiJobStreamWrite:", buffer, buflen);
386 #endif
387 
388 	if ((svc == NULL) || (stream == NULL) || (buffer == NULL) ||
389 	    (buflen == 0))
390 		return (PAPI_BAD_ARGUMENT);
391 
392 	while ((result == PAPI_OK) && (buflen > 0)) {
393 		rc = ipp_request_write(svc, buffer, buflen);
394 		if (rc < 0)
395 			result = PAPI_TEMPORARY_ERROR;
396 		else {
397 			buffer = (char *)buffer + rc;
398 			buflen -= rc;
399 		}
400 	}
401 
402 #ifdef DEBUG
403 	printf("papiJobStreamWrite(): %s\n", papiStatusString(result));
404 #endif
405 
406 	return (result);
407 }
408 
409 papi_status_t
410 papiJobStreamClose(papi_service_t handle,
411 		papi_stream_t stream, papi_job_t *job)
412 {
413 	papi_status_t result = PAPI_INTERNAL_ERROR;
414 	http_status_t status = HTTP_CONTINUE;
415 	service_t *svc = handle;
416 	papi_attribute_t **response = NULL;
417 	job_t *j = NULL;
418 
419 	if ((svc == NULL) || (stream == NULL) || (job == NULL))
420 		return (PAPI_BAD_ARGUMENT);
421 
422 	if ((*job = j = calloc(1, sizeof (*j))) == NULL)
423 		return (PAPI_TEMPORARY_ERROR);
424 
425 	(void) ipp_request_write(svc, "", 0);
426 
427 	/* update our connection info */
428 	while (status == HTTP_CONTINUE)
429 		status = httpUpdate(svc->connection);
430 
431 	if (status != HTTP_OK)
432 		return (http_to_papi_status(status));
433 	httpWait(svc->connection, 1000);
434 
435 	/* read the IPP response */
436 	result = ipp_read_message(&ipp_request_read, svc, &response,
437 	    IPP_TYPE_RESPONSE);
438 	if (result == PAPI_OK)
439 		result = ipp_status_info(svc, response);
440 
441 	if (result == PAPI_OK) {
442 		papi_attribute_t **op = NULL;
443 
444 		papiAttributeListGetCollection(response, NULL,
445 		    "job-attributes-group", &op);
446 		copy_attributes(&j->attributes, op);
447 	}
448 	papiAttributeListFree(response);
449 
450 	return (result);
451 }
452 
453 papi_status_t
454 papiJobQuery(papi_service_t handle, char *printer, int32_t job_id,
455 		char **requested_attrs,
456 		papi_job_t *job)
457 {
458 	papi_status_t result = PAPI_INTERNAL_ERROR;
459 	service_t *svc = handle;
460 	job_t *j = NULL;
461 	papi_attribute_t **request = NULL, **op = NULL, **response = NULL;
462 
463 	if ((svc == NULL) || (printer == NULL))
464 		return (PAPI_BAD_ARGUMENT);
465 
466 	/* if we are already connected, use that connection. */
467 	if (svc->connection == NULL)
468 		if ((result = service_connect(svc, printer)) != PAPI_OK)
469 			return (result);
470 
471 	if ((*job = j = calloc(1, sizeof (*j))) == NULL)
472 		return (PAPI_TEMPORARY_ERROR);
473 
474 	ipp_initialize_request(svc, &request, OPID_GET_JOB_ATTRIBUTES);
475 
476 	ipp_initialize_operational_attributes(svc, &op, printer, job_id);
477 
478 	if (requested_attrs != NULL) {
479 		int i;
480 
481 		for (i = 0; requested_attrs[i] != NULL; i++)
482 			papiAttributeListAddString(&op, PAPI_ATTR_APPEND,
483 			    "requested-attributes", requested_attrs[i]);
484 	}
485 
486 	papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
487 	    "operational-attributes-group", op);
488 	papiAttributeListFree(op);
489 	result = ipp_send_request(svc, request, &response);
490 	papiAttributeListFree(request);
491 
492 	op = NULL;
493 	papiAttributeListGetCollection(response, NULL,
494 	    "job-attributes-group", &op);
495 	copy_attributes(&j->attributes, op);
496 	papiAttributeListFree(response);
497 
498 	return (result);
499 }
500 
501 /* papiJob{Cancel|Hold|Release|Restart|Promote} are all the same */
502 static papi_status_t
503 _job_cancel_hold_release_restart_promote(papi_service_t handle,
504 		char *printer, int32_t job_id, uint16_t type)
505 {
506 	papi_status_t result = PAPI_INTERNAL_ERROR;
507 	service_t *svc = handle;
508 	papi_attribute_t **request = NULL, **op = NULL, **response = NULL;
509 
510 	if ((svc == NULL) || (printer == NULL) || (job_id < 0))
511 		return (PAPI_BAD_ARGUMENT);
512 
513 	/* if we are already connected, use that connection. */
514 	if (svc->connection == NULL)
515 		if ((result = service_connect(svc, printer)) != PAPI_OK)
516 			return (result);
517 
518 	ipp_initialize_request(svc, &request, type);
519 
520 	ipp_initialize_operational_attributes(svc, &op, printer, job_id);
521 
522 	papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
523 	    "operational-attributes-group", op);
524 	papiAttributeListFree(op);
525 	result = ipp_send_request(svc, request, &response);
526 	papiAttributeListFree(request);
527 	papiAttributeListFree(response);
528 
529 	return (result);
530 }
531 
532 papi_status_t
533 papiJobCancel(papi_service_t handle, char *printer, int32_t job_id)
534 {
535 	return (_job_cancel_hold_release_restart_promote(handle, printer,
536 	    job_id, OPID_CANCEL_JOB));
537 }
538 
539 
540 papi_status_t
541 papiJobHold(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_HOLD_JOB));
545 }
546 
547 papi_status_t
548 papiJobRelease(papi_service_t handle, char *printer, int32_t job_id)
549 {
550 	return (_job_cancel_hold_release_restart_promote(handle, printer,
551 	    job_id, OPID_RELEASE_JOB));
552 }
553 
554 papi_status_t
555 papiJobRestart(papi_service_t handle, char *printer, int32_t job_id)
556 {
557 	return (_job_cancel_hold_release_restart_promote(handle, printer,
558 	    job_id, OPID_RESTART_JOB));
559 }
560 
561 papi_status_t
562 papiJobPromote(papi_service_t handle, char *printer, int32_t job_id)
563 {
564 	return (_job_cancel_hold_release_restart_promote(handle, printer,
565 	    job_id, OPID_PROMOTE_JOB));
566 }
567 
568 papi_status_t
569 papiJobMove(papi_service_t handle, char *printer, int32_t job_id,
570 		char *destination)
571 {
572 	papi_status_t result = PAPI_INTERNAL_ERROR;
573 	service_t *svc = handle;
574 	papi_attribute_t **request = NULL, **op = NULL, **response = NULL;
575 
576 	if ((svc == NULL) || (printer == NULL) || (job_id < 0) ||
577 	    (destination == NULL))
578 		return (PAPI_BAD_ARGUMENT);
579 
580 	/* if we are already connected, use that connection. */
581 	if (svc->connection == NULL)
582 		if ((result = service_connect(svc, printer)) != PAPI_OK)
583 			return (result);
584 
585 	ipp_initialize_request(svc, &request, OPID_CUPS_MOVE_JOB);
586 
587 	ipp_initialize_operational_attributes(svc, &op, printer, job_id);
588 
589 	papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
590 	    "operational-attributes-group", op);
591 	papiAttributeListFree(op);
592 
593 	op = NULL;
594 	papiAttributeListAddString(&op, PAPI_ATTR_EXCL,
595 	    "job-printer-uri", destination);
596 	papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
597 	    "job-attributes-group", op);
598 	papiAttributeListFree(op);
599 
600 	result = ipp_send_request(svc, request, &response);
601 	papiAttributeListFree(request);
602 	papiAttributeListFree(response);
603 
604 	return (result);
605 }
606 
607 papi_status_t
608 papiJobModify(papi_service_t handle, char *printer, int32_t job_id,
609 		papi_attribute_t **attributes, papi_job_t *job)
610 {
611 	papi_status_t result = PAPI_INTERNAL_ERROR;
612 	service_t *svc = handle;
613 	papi_attribute_t **request = NULL, **op = NULL, **response = NULL;
614 	job_t *j = NULL;
615 
616 	if ((svc == NULL) || (printer == NULL) || (job_id < 0) ||
617 	    (attributes == NULL))
618 		return (PAPI_BAD_ARGUMENT);
619 
620 	if ((*job = j = calloc(1, sizeof (*j))) == NULL)
621 		return (PAPI_TEMPORARY_ERROR);
622 
623 	/* if we are already connected, use that connection. */
624 	if (svc->connection == NULL)
625 		if ((result = service_connect(svc, printer)) != PAPI_OK)
626 			return (result);
627 
628 	ipp_initialize_request(svc, &request, OPID_SET_JOB_ATTRIBUTES);
629 
630 	ipp_initialize_operational_attributes(svc, &op, printer, job_id);
631 
632 	papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
633 	    "operational-attributes-group", op);
634 	papiAttributeListFree(op);
635 	papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
636 	    "job-attributes-group", attributes);
637 	result = ipp_send_request(svc, request, &response);
638 	papiAttributeListFree(request);
639 
640 	op = NULL;
641 	papiAttributeListGetCollection(response, NULL,
642 	    "job-attributes-group", &op);
643 	copy_attributes(&j->attributes, op);
644 	papiAttributeListFree(response);
645 
646 	return (result);
647 }
648