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 		papiAttributeListAddCollection(request, PAPI_ATTR_REPLACE,
144 		    "job-attributes-group", job);
145 		papiAttributeListFree(job);
146 	}
147 }
148 
149 static papi_status_t
150 send_document_uri(service_t *svc, char *file, papi_attribute_t **attributes,
151 		char *printer, int32_t id, char last, uint16_t type)
152 {
153 	papi_status_t result = PAPI_INTERNAL_ERROR;
154 	papi_attribute_t **request = NULL, **op = NULL, **response = NULL;
155 
156 	/* create the base IPP request */
157 	ipp_initialize_request(svc, &request, type);
158 
159 	/* create an operational attributes group */
160 	ipp_initialize_operational_attributes(svc, &op, printer, id);
161 
162 	papiAttributeListAddString(&op, PAPI_ATTR_REPLACE, "document-name",
163 	    file);
164 	papiAttributeListAddBoolean(&op, PAPI_ATTR_REPLACE, "last-document",
165 	    (last ? PAPI_TRUE : PAPI_FALSE));
166 	papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
167 	    "operational-attributes-group", op);
168 	papiAttributeListFree(op);
169 
170 	/* send the IPP request to the server */
171 	result = ipp_send_request_with_file(svc, request, &response, file);
172 	papiAttributeListFree(request);
173 	papiAttributeListFree(response);
174 
175 	return (result);
176 }
177 
178 typedef enum {_WITH_DATA, _BY_REFERENCE, _VALIDATE} call_type_t;
179 
180 papi_status_t
181 internal_job_submit(papi_service_t handle, char *printer,
182 		papi_attribute_t **job_attributes,
183 		papi_job_ticket_t *job_ticket,
184 		char **files, papi_job_t *job,
185 		call_type_t call_type)
186 {
187 	papi_status_t result = PAPI_INTERNAL_ERROR;
188 	service_t *svc = handle;
189 	struct stat statbuf;
190 	job_t *j = NULL;
191 	int i;
192 	uint16_t req_type = OPID_PRINT_JOB;
193 	uint16_t data_type = OPID_SEND_DOCUMENT;
194 	papi_attribute_t **request = NULL, **response = NULL;
195 
196 	if ((svc == NULL) || (printer == NULL) || (job == NULL))
197 		return (PAPI_BAD_ARGUMENT);
198 
199 	switch (call_type) {
200 	case _BY_REFERENCE:
201 #ifdef SOME_DAY_WE_WILL_BE_ABLE_TO_USE_URIS_FOR_JOB_DATA
202 		/*
203 		 * For the time being, this is disabled.  There are a number
204 		 * of issues to be dealt with before we can send a URI
205 		 * across the network to the server.  For example, the file
206 		 * name(s) passed in are most likely relative to the current
207 		 * hosts filesystem.  They also most likely will require some
208 		 * form of authentication information to be passed with the
209 		 * URI.
210 		 */
211 		req_type = OPID_PRINT_URI;
212 		req_type = OPID_SEND_URI;
213 #endif
214 		/* fall-through */
215 	case _WITH_DATA:
216 		if ((files == NULL) || (files[0] == NULL))
217 			return (PAPI_BAD_ARGUMENT);
218 
219 		if (files[1] != NULL)	/* more than 1 file */
220 			req_type = OPID_CREATE_JOB;
221 
222 		break;
223 	case _VALIDATE:
224 		req_type = OPID_VALIDATE_JOB;
225 		/* if we have files, validate access to them */
226 		if (files != NULL) {
227 			for (i = 0; files[i] != NULL; i++) {
228 				if (access(files[i], R_OK) < 0) {
229 					detailed_error(svc, "%s: %s", files[i],
230 					    strerror(errno));
231 					return (PAPI_DOCUMENT_ACCESS_ERROR);
232 				}
233 
234 				if (strcmp("standard input", files[i]) != 0) {
235 					stat(files[i], &statbuf);
236 					if (statbuf.st_size == 0) {
237 						detailed_error(svc,
238 						    "Zero byte (empty) file: "
239 						    "%s",
240 						    files[i]);
241 						return (PAPI_BAD_ARGUMENT);
242 					}
243 				}
244 			}
245 			files = NULL;
246 		}
247 		break;
248 	}
249 
250 	/* if we are already connected, use that connection. */
251 	if (svc->connection == NULL)
252 		if ((result = service_connect(svc, printer)) != PAPI_OK)
253 			return (result);
254 
255 	if ((*job = j = calloc(1, sizeof (*j))) == NULL)
256 		return (PAPI_TEMPORARY_ERROR);
257 
258 	/* create IPP request */
259 	populate_job_request(svc, &request, job_attributes, printer, req_type);
260 
261 	switch (req_type) {
262 	case OPID_PRINT_JOB:
263 		result = ipp_send_request_with_file(svc, request, &response,
264 		    files[0]);
265 		break;
266 	case OPID_CREATE_JOB:
267 	case OPID_VALIDATE_JOB:
268 	case OPID_PRINT_URI:
269 		result = ipp_send_request(svc, request, &response);
270 		break;
271 	}
272 	papiAttributeListFree(request);
273 
274 	if (result == PAPI_OK) {
275 		papi_attribute_t **op = NULL;
276 
277 		/* retrieve the job attributes */
278 		papiAttributeListGetCollection(response, NULL,
279 		    "job-attributes-group", &op);
280 		copy_attributes(&j->attributes, op);
281 
282 		if (req_type == OPID_CREATE_JOB) {
283 			int32_t id = 0;
284 
285 			papiAttributeListGetInteger(j->attributes, NULL,
286 			    "job-id", &id);
287 			/* send each document */
288 			for (i = 0; ((result == PAPI_OK) && (files[i] != NULL));
289 			    i++)
290 				result = send_document_uri(svc, files[i],
291 				    job_attributes,
292 				    printer, id, (files[i+1]?0:1),
293 				    data_type);
294 		}
295 	}
296 	papiAttributeListFree(response);
297 
298 	return (result);
299 }
300 
301 papi_status_t
302 papiJobSubmit(papi_service_t handle, char *printer,
303 		papi_attribute_t **job_attributes,
304 		papi_job_ticket_t *job_ticket, char **files, papi_job_t *job)
305 {
306 	return (internal_job_submit(handle, printer, job_attributes,
307 	    job_ticket, files, job, _WITH_DATA));
308 }
309 
310 papi_status_t
311 papiJobSubmitByReference(papi_service_t handle, char *printer,
312 		papi_attribute_t **job_attributes,
313 		papi_job_ticket_t *job_ticket, char **files, papi_job_t *job)
314 {
315 	return (internal_job_submit(handle, printer, job_attributes,
316 	    job_ticket, files, job, _BY_REFERENCE));
317 }
318 
319 papi_status_t
320 papiJobValidate(papi_service_t handle, char *printer,
321 		papi_attribute_t **job_attributes,
322 		papi_job_ticket_t *job_ticket, char **files, papi_job_t *job)
323 {
324 	return (internal_job_submit(handle, printer, job_attributes,
325 	    job_ticket, files, job, _VALIDATE));
326 }
327 
328 papi_status_t
329 papiJobStreamOpen(papi_service_t handle, char *printer,
330 		papi_attribute_t **job_attributes,
331 		papi_job_ticket_t *job_ticket, papi_stream_t *stream)
332 {
333 	papi_status_t result = PAPI_INTERNAL_ERROR;
334 	papi_attribute_t **request = NULL;
335 	service_t *svc = handle;
336 
337 	if ((svc == NULL) || (printer == NULL) || (stream == NULL))
338 		return (PAPI_BAD_ARGUMENT);
339 
340 	/* if we are already connected, use that connection. */
341 	if (svc->connection == NULL)
342 		if ((result = service_connect(svc, printer)) != PAPI_OK)
343 			return (result);
344 
345 	/* create job request */
346 	populate_job_request(svc, &request, job_attributes, printer,
347 	    OPID_PRINT_JOB);
348 
349 	*stream = svc->connection;
350 
351 	result = ipp_send_initial_request_block(svc, request, 0);
352 	papiAttributeListFree(request);
353 
354 	return (result);
355 }
356 
357 papi_status_t
358 papiJobStreamWrite(papi_service_t handle,
359 		papi_stream_t stream, void *buffer, size_t buflen)
360 {
361 	papi_status_t result = PAPI_OK;
362 	service_t *svc = handle;
363 	size_t rc;
364 
365 #ifdef DEBUG
366 	printf("papiJobStreamWrite(0x%8.8x, 0x%8.8x, 0x%8.8x, %d)\n",
367 	    handle, stream, buffer, buflen);
368 	httpDumpData(stdout, "papiJobStreamWrite:", buffer, buflen);
369 #endif
370 
371 	if ((svc == NULL) || (stream == NULL) || (buffer == NULL) ||
372 	    (buflen == 0))
373 		return (PAPI_BAD_ARGUMENT);
374 
375 	while ((result == PAPI_OK) && (buflen > 0)) {
376 		rc = ipp_request_write(svc, buffer, buflen);
377 		if (rc < 0)
378 			result = PAPI_TEMPORARY_ERROR;
379 		else {
380 			buffer = (char *)buffer + rc;
381 			buflen -= rc;
382 		}
383 	}
384 
385 #ifdef DEBUG
386 	printf("papiJobStreamWrite(): %s\n", papiStatusString(result));
387 #endif
388 
389 	return (result);
390 }
391 
392 papi_status_t
393 papiJobStreamClose(papi_service_t handle,
394 		papi_stream_t stream, papi_job_t *job)
395 {
396 	papi_status_t result = PAPI_INTERNAL_ERROR;
397 	http_status_t status = HTTP_CONTINUE;
398 	service_t *svc = handle;
399 	papi_attribute_t **response = NULL;
400 	job_t *j = NULL;
401 
402 	if ((svc == NULL) || (stream == NULL) || (job == NULL))
403 		return (PAPI_BAD_ARGUMENT);
404 
405 	if ((*job = j = calloc(1, sizeof (*j))) == NULL)
406 		return (PAPI_TEMPORARY_ERROR);
407 
408 	(void) ipp_request_write(svc, "", 0);
409 
410 	/* update our connection info */
411 	while (status == HTTP_CONTINUE)
412 		status = httpUpdate(svc->connection);
413 
414 	if (status != HTTP_OK)
415 		return (http_to_papi_status(status));
416 	httpWait(svc->connection, 1000);
417 
418 	/* read the IPP response */
419 	result = ipp_read_message(&ipp_request_read, svc, &response,
420 	    IPP_TYPE_RESPONSE);
421 	if (result == PAPI_OK)
422 		result = ipp_status_info(svc, response);
423 
424 	if (result == PAPI_OK) {
425 		papi_attribute_t **op = NULL;
426 
427 		papiAttributeListGetCollection(response, NULL,
428 		    "job-attributes-group", &op);
429 		copy_attributes(&j->attributes, op);
430 	}
431 	papiAttributeListFree(response);
432 
433 	return (result);
434 }
435 
436 papi_status_t
437 papiJobQuery(papi_service_t handle, char *printer, int32_t job_id,
438 		char **requested_attrs,
439 		papi_job_t *job)
440 {
441 	papi_status_t result = PAPI_INTERNAL_ERROR;
442 	service_t *svc = handle;
443 	job_t *j = NULL;
444 	papi_attribute_t **request = NULL, **op = NULL, **response = NULL;
445 
446 	if ((svc == NULL) || (printer == NULL))
447 		return (PAPI_BAD_ARGUMENT);
448 
449 	/* if we are already connected, use that connection. */
450 	if (svc->connection == NULL)
451 		if ((result = service_connect(svc, printer)) != PAPI_OK)
452 			return (result);
453 
454 	if ((*job = j = calloc(1, sizeof (*j))) == NULL)
455 		return (PAPI_TEMPORARY_ERROR);
456 
457 	ipp_initialize_request(svc, &request, OPID_GET_JOB_ATTRIBUTES);
458 
459 	ipp_initialize_operational_attributes(svc, &op, printer, job_id);
460 
461 	if (requested_attrs != NULL) {
462 		int i;
463 
464 		for (i = 0; requested_attrs[i] != NULL; i++)
465 			papiAttributeListAddString(&op, PAPI_ATTR_APPEND,
466 			    "requested-attributes", requested_attrs[i]);
467 	}
468 
469 	papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
470 	    "operational-attributes-group", op);
471 	papiAttributeListFree(op);
472 	result = ipp_send_request(svc, request, &response);
473 	papiAttributeListFree(request);
474 
475 	op = NULL;
476 	papiAttributeListGetCollection(response, NULL,
477 	    "job-attributes-group", &op);
478 	copy_attributes(&j->attributes, op);
479 	papiAttributeListFree(response);
480 
481 	return (result);
482 }
483 
484 /* papiJob{Cancel|Hold|Release|Restart|Promote} are all the same */
485 static papi_status_t
486 _job_cancel_hold_release_restart_promote(papi_service_t handle,
487 		char *printer, int32_t job_id, uint16_t type)
488 {
489 	papi_status_t result = PAPI_INTERNAL_ERROR;
490 	service_t *svc = handle;
491 	papi_attribute_t **request = NULL, **op = NULL, **response = NULL;
492 
493 	if ((svc == NULL) || (printer == NULL) || (job_id < 0))
494 		return (PAPI_BAD_ARGUMENT);
495 
496 	/* if we are already connected, use that connection. */
497 	if (svc->connection == NULL)
498 		if ((result = service_connect(svc, printer)) != PAPI_OK)
499 			return (result);
500 
501 	ipp_initialize_request(svc, &request, type);
502 
503 	ipp_initialize_operational_attributes(svc, &op, printer, job_id);
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 	papiAttributeListFree(response);
511 
512 	return (result);
513 }
514 
515 papi_status_t
516 papiJobCancel(papi_service_t handle, char *printer, int32_t job_id)
517 {
518 	return (_job_cancel_hold_release_restart_promote(handle, printer,
519 	    job_id, OPID_CANCEL_JOB));
520 }
521 
522 
523 papi_status_t
524 papiJobHold(papi_service_t handle, char *printer, int32_t job_id)
525 {
526 	return (_job_cancel_hold_release_restart_promote(handle, printer,
527 	    job_id, OPID_HOLD_JOB));
528 }
529 
530 papi_status_t
531 papiJobRelease(papi_service_t handle, char *printer, int32_t job_id)
532 {
533 	return (_job_cancel_hold_release_restart_promote(handle, printer,
534 	    job_id, OPID_RELEASE_JOB));
535 }
536 
537 papi_status_t
538 papiJobRestart(papi_service_t handle, char *printer, int32_t job_id)
539 {
540 	return (_job_cancel_hold_release_restart_promote(handle, printer,
541 	    job_id, OPID_RESTART_JOB));
542 }
543 
544 papi_status_t
545 papiJobPromote(papi_service_t handle, char *printer, int32_t job_id)
546 {
547 	return (_job_cancel_hold_release_restart_promote(handle, printer,
548 	    job_id, OPID_PROMOTE_JOB));
549 }
550 
551 papi_status_t
552 papiJobMove(papi_service_t handle, char *printer, int32_t job_id,
553 		char *destination)
554 {
555 	papi_status_t result = PAPI_INTERNAL_ERROR;
556 	service_t *svc = handle;
557 	papi_attribute_t **request = NULL, **op = NULL, **response = NULL;
558 
559 	if ((svc == NULL) || (printer == NULL) || (job_id < 0) ||
560 	    (destination == NULL))
561 		return (PAPI_BAD_ARGUMENT);
562 
563 	/* if we are already connected, use that connection. */
564 	if (svc->connection == NULL)
565 		if ((result = service_connect(svc, printer)) != PAPI_OK)
566 			return (result);
567 
568 	ipp_initialize_request(svc, &request, OPID_CUPS_MOVE_JOB);
569 
570 	ipp_initialize_operational_attributes(svc, &op, printer, job_id);
571 
572 	papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
573 	    "operational-attributes-group", op);
574 	papiAttributeListFree(op);
575 
576 	op = NULL;
577 	papiAttributeListAddString(&op, PAPI_ATTR_EXCL,
578 	    "job-printer-uri", destination);
579 	papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
580 	    "job-attributes-group", op);
581 	papiAttributeListFree(op);
582 
583 	result = ipp_send_request(svc, request, &response);
584 	papiAttributeListFree(request);
585 	papiAttributeListFree(response);
586 
587 	return (result);
588 }
589 
590 papi_status_t
591 papiJobModify(papi_service_t handle, char *printer, int32_t job_id,
592 		papi_attribute_t **attributes, papi_job_t *job)
593 {
594 	papi_status_t result = PAPI_INTERNAL_ERROR;
595 	service_t *svc = handle;
596 	papi_attribute_t **request = NULL, **op = NULL, **response = NULL;
597 	job_t *j = NULL;
598 
599 	if ((svc == NULL) || (printer == NULL) || (job_id < 0) ||
600 	    (attributes == NULL))
601 		return (PAPI_BAD_ARGUMENT);
602 
603 	if ((*job = j = calloc(1, sizeof (*j))) == NULL)
604 		return (PAPI_TEMPORARY_ERROR);
605 
606 	/* if we are already connected, use that connection. */
607 	if (svc->connection == NULL)
608 		if ((result = service_connect(svc, printer)) != PAPI_OK)
609 			return (result);
610 
611 	ipp_initialize_request(svc, &request, OPID_SET_JOB_ATTRIBUTES);
612 
613 	ipp_initialize_operational_attributes(svc, &op, printer, job_id);
614 
615 	papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
616 	    "operational-attributes-group", op);
617 	papiAttributeListFree(op);
618 	papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
619 	    "job-attributes-group", attributes);
620 	result = ipp_send_request(svc, request, &response);
621 	papiAttributeListFree(request);
622 
623 	op = NULL;
624 	papiAttributeListGetCollection(response, NULL,
625 	    "job-attributes-group", &op);
626 	copy_attributes(&j->attributes, op);
627 	papiAttributeListFree(response);
628 
629 	return (result);
630 }
631