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: ipp-support.c 148 2006-04-25 16:54:17Z njacobs $ */
29
30
31 #include <papi_impl.h>
32 #include <stdlib.h>
33 #include <pwd.h>
34 #include <locale.h>
35 #include <errno.h>
36 #include <fcntl.h>
37 #include <sys/stat.h>
38 #include <md5.h>
39
40 #include <config-site.h>
41
42 #include <ipp.h>
43
44 static void ipp_add_printer_uri(service_t *svc, char *name,
45 papi_attribute_t ***op);
46
47 papi_status_t
http_to_papi_status(http_status_t status)48 http_to_papi_status(http_status_t status)
49 {
50 switch (status) {
51 case HTTP_OK:
52 return (PAPI_OK);
53 case HTTP_BAD_REQUEST:
54 return (PAPI_BAD_REQUEST);
55 case HTTP_UNAUTHORIZED:
56 case HTTP_FORBIDDEN:
57 return (PAPI_NOT_AUTHORIZED);
58 case HTTP_NOT_FOUND:
59 return (PAPI_NOT_FOUND);
60 case HTTP_GONE:
61 return (PAPI_GONE);
62 case HTTP_SERVICE_UNAVAILABLE:
63 return (PAPI_SERVICE_UNAVAILABLE);
64 default:
65 return ((papi_status_t)status);
66 }
67 }
68
69 papi_status_t
ipp_to_papi_status(uint16_t status)70 ipp_to_papi_status(uint16_t status)
71 {
72 switch (status) {
73 case IPP_OK:
74 return (PAPI_OK);
75 case IPP_OK_IGNORED_ATTRIBUTES:
76 return (PAPI_OK);
77 case IPP_OK_CONFLICTING_ATTRIBUTES:
78 return (PAPI_OK);
79 case IPP_OK_IGNORED_SUBSCRIPTIONS:
80 return (PAPI_OK_IGNORED_SUBSCRIPTIONS);
81 case IPP_OK_IGNORED_NOTIFICATIONS:
82 return (PAPI_OK_IGNORED_NOTIFICATIONS);
83 case IPP_CERR_BAD_REQUEST:
84 return (PAPI_BAD_REQUEST);
85 case IPP_CERR_FORBIDDEN:
86 return (PAPI_FORBIDDEN);
87 case IPP_CERR_NOT_AUTHENTICATED:
88 return (PAPI_NOT_AUTHENTICATED);
89 case IPP_CERR_NOT_AUTHORIZED:
90 return (PAPI_NOT_AUTHORIZED);
91 case IPP_CERR_NOT_POSSIBLE:
92 return (PAPI_NOT_POSSIBLE);
93 case IPP_CERR_TIMEOUT:
94 return (PAPI_TIMEOUT);
95 case IPP_CERR_NOT_FOUND:
96 return (PAPI_NOT_FOUND);
97 case IPP_CERR_GONE:
98 return (PAPI_GONE);
99 case IPP_CERR_REQUEST_ENTITY:
100 return (PAPI_REQUEST_ENTITY);
101 case IPP_CERR_REQUEST_VALUE:
102 return (PAPI_REQUEST_VALUE);
103 case IPP_CERR_DOCUMENT_FORMAT:
104 return (PAPI_DOCUMENT_FORMAT);
105 case IPP_CERR_ATTRIBUTES:
106 return (PAPI_ATTRIBUTES);
107 case IPP_CERR_URI_SCHEME:
108 return (PAPI_URI_SCHEME);
109 case IPP_CERR_CHARSET:
110 return (PAPI_CHARSET);
111 case IPP_CERR_CONFLICT:
112 return (PAPI_CONFLICT);
113 case IPP_CERR_COMPRESSION_NOT_SUPPORTED:
114 return (PAPI_COMPRESSION_NOT_SUPPORTED);
115 case IPP_CERR_COMPRESSION_ERROR:
116 return (PAPI_COMPRESSION_ERROR);
117 case IPP_CERR_DOCUMENT_FORMAT_ERROR:
118 return (PAPI_DOCUMENT_FORMAT_ERROR);
119 case IPP_CERR_DOCUMENT_ACCESS_ERROR:
120 return (PAPI_DOCUMENT_ACCESS_ERROR);
121 case IPP_CERR_ATTRIBUTES_NOT_SETTABLE:
122 return (PAPI_ATTRIBUTES_NOT_SETTABLE);
123 case IPP_CERR_IGNORED_ALL_SUBSCRIPTIONS:
124 return (PAPI_IGNORED_ALL_SUBSCRIPTIONS);
125 case IPP_CERR_TOO_MANY_SUBSCRIPTIONS:
126 return (PAPI_TOO_MANY_SUBSCRIPTIONS);
127 case IPP_CERR_IGNORED_ALL_NOTIFICATIONS:
128 return (PAPI_IGNORED_ALL_NOTIFICATIONS);
129 case IPP_CERR_PRINT_SUPPORT_FILE_NOT_FOUND:
130 return (PAPI_PRINT_SUPPORT_FILE_NOT_FOUND);
131 case IPP_SERR_INTERNAL:
132 return (PAPI_INTERNAL_ERROR);
133 case IPP_SERR_OPERATION_NOT_SUPPORTED:
134 return (PAPI_OPERATION_NOT_SUPPORTED);
135 case IPP_SERR_SERVICE_UNAVAILABLE:
136 return (PAPI_SERVICE_UNAVAILABLE);
137 case IPP_SERR_VERSION_NOT_SUPPORTED:
138 return (PAPI_VERSION_NOT_SUPPORTED);
139 case IPP_SERR_DEVICE_ERROR:
140 return (PAPI_DEVICE_ERROR);
141 case IPP_SERR_TEMPORARY_ERROR:
142 return (PAPI_TEMPORARY_ERROR);
143 case IPP_SERR_NOT_ACCEPTING:
144 return (PAPI_NOT_ACCEPTING);
145 case IPP_SERR_BUSY:
146 case IPP_SERR_CANCELLED:
147 default:
148 return (PAPI_TEMPORARY_ERROR);
149 }
150 }
151
152 void
ipp_initialize_request(service_t * svc,papi_attribute_t *** request,uint16_t operation)153 ipp_initialize_request(service_t *svc, papi_attribute_t ***request,
154 uint16_t operation)
155 {
156 papiAttributeListAddInteger(request, PAPI_ATTR_EXCL,
157 "version-major", 1);
158 papiAttributeListAddInteger(request, PAPI_ATTR_EXCL,
159 "version-minor", 1);
160 papiAttributeListAddInteger(request, PAPI_ATTR_EXCL,
161 "request-id", (short)lrand48());
162 papiAttributeListAddInteger(request, PAPI_ATTR_EXCL,
163 "operation-id", operation);
164 }
165
166 void
ipp_initialize_operational_attributes(service_t * svc,papi_attribute_t *** op,char * printer,int job_id)167 ipp_initialize_operational_attributes(service_t *svc, papi_attribute_t ***op,
168 char *printer, int job_id)
169 {
170 char *charset = "utf-8"; /* default to UTF-8 encoding */
171 char *language = setlocale(LC_ALL, "");
172 char *user = "nobody";
173 struct passwd *pw = NULL;
174
175 /*
176 * All IPP requests must contain the following:
177 * attributes-charset (UTF-8)
178 * attributes-natural-language (our current locale)
179 * (object identifier) printer-uri/job-id or job-uri
180 * requesting-user-name (process user or none)
181 */
182 papiAttributeListAddString(op, PAPI_ATTR_EXCL,
183 "attributes-charset", charset);
184
185 papiAttributeListAddString(op, PAPI_ATTR_EXCL,
186 "attributes-natural-language", language);
187
188 if (printer != NULL)
189 ipp_add_printer_uri(svc, printer, op);
190
191 if ((printer != NULL) && (job_id >= 0))
192 papiAttributeListAddInteger(op, PAPI_ATTR_EXCL,
193 "job-id", job_id);
194
195 if ((pw = getpwuid(getuid())) != NULL)
196 user = pw->pw_name;
197 /*
198 * if our euid is 0 "super user", we will allow the system supplied
199 * user name to be overridden, if the requestor wants to.
200 */
201 if (geteuid() == 0) {
202 if (svc->user != NULL)
203 user = svc->user;
204 }
205 papiAttributeListAddString(op, PAPI_ATTR_REPLACE,
206 "requesting-user-name", user);
207 }
208
209 #ifndef OPID_CUPS_GET_DEFAULT /* for servers that will enumerate */
210 #define OPID_CUPS_GET_DEFAULT 0x4001
211 #endif /* OPID_CUPS_GET_DEFAULT */
212
213 static papi_status_t
_default_destination(service_t * svc,char ** uri)214 _default_destination(service_t *svc, char **uri)
215 {
216 papi_status_t result = PAPI_INTERNAL_ERROR;
217 printer_t *p = NULL;
218 papi_attribute_t **request = NULL, **op = NULL, **response = NULL;
219 char *tmp = NULL;
220
221 if ((svc == NULL) || (uri == NULL))
222 return (PAPI_BAD_ARGUMENT);
223
224 /* we must be connected to find the default destination */
225 if (svc->connection == NULL)
226 return (PAPI_NOT_POSSIBLE);
227
228 if ((p = calloc(1, sizeof (*p))) == NULL)
229 return (PAPI_TEMPORARY_ERROR);
230
231 ipp_initialize_request(svc, &request, OPID_CUPS_GET_DEFAULT);
232 ipp_initialize_operational_attributes(svc, &op, NULL, -1);
233 papiAttributeListAddString(&op, PAPI_ATTR_APPEND,
234 "requested-attributes", "printer-uri-supported");
235 papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
236 "operational-attributes-group", op);
237 papiAttributeListFree(op);
238 result = ipp_send_request(svc, request, &response);
239 papiAttributeListFree(request);
240
241 op = NULL;
242 papiAttributeListGetCollection(response, NULL,
243 "printer-attributes-group", &op);
244
245 if (uri != NULL) {
246 char *tmp = NULL;
247
248 papiAttributeListGetString(op, NULL, "printer-uri", &tmp);
249 papiAttributeListGetString(op, NULL,
250 "printer-uri-supported", &tmp);
251 if (tmp != NULL)
252 *uri = strdup(tmp);
253 }
254
255 papiAttributeListFree(response);
256
257 return (result);
258 }
259
260 static void
ipp_add_printer_uri(service_t * svc,char * name,papi_attribute_t *** op)261 ipp_add_printer_uri(service_t *svc, char *name, papi_attribute_t ***op)
262 {
263 char *uri = name;
264 char buf[BUFSIZ];
265 uri_t *tmp = NULL;
266
267 if (strstr(name, "://") == NULL) { /* not in URI form */
268 if (strcmp(name, DEFAULT_DEST) != 0) {
269 /* not the "default" */
270 snprintf(buf, sizeof (buf), "%s/%s", svc->name, name);
271 uri = buf;
272 } else
273 _default_destination(svc, &uri);
274 }
275
276 papiAttributeListAddString(op, PAPI_ATTR_EXCL, "printer-uri", uri);
277
278 /* save the printer-uri's path to be used by http POST request */
279 if ((uri_from_string(uri, &tmp) == 0) && (tmp != NULL)) {
280 if (svc->post != NULL)
281 free(svc->post);
282 svc->post = strdup(tmp->path);
283 uri_free(tmp);
284 }
285 }
286
287
288 /*
289 * don't actually write anything, just add to the total size and return the
290 * size of what would be written, so we can figure out how big the request
291 * is going to be.
292 */
293 static ssize_t
size_calculate(void * fd,void * buffer,size_t length)294 size_calculate(void *fd, void *buffer, size_t length)
295 {
296 ssize_t *size = (ssize_t *)fd;
297
298 *size += length;
299 return (length);
300 }
301
302
303 static ssize_t
build_chunk(void * fd,void * buffer,size_t length)304 build_chunk(void *fd, void *buffer, size_t length)
305 {
306 char **s1 = fd;
307
308 memcpy(*s1, buffer, length);
309 *s1 = *s1 + length;
310
311 return (length);
312 }
313
314 ssize_t
ipp_request_write(void * fd,void * buffer,size_t length)315 ipp_request_write(void *fd, void *buffer, size_t length)
316 {
317 service_t *svc = (service_t *)fd;
318
319 #ifdef DEBUG
320 printf("ipp_request_write(0x%8.8x, 0x%8.8x, %d)\n", fd, buffer, length);
321 httpDumpData(stdout, "ipp_request_write:", buffer, length);
322 #endif
323 return (httpWrite(svc->connection, buffer, length));
324 }
325
326 ssize_t
ipp_request_read(void * fd,void * buffer,size_t length)327 ipp_request_read(void *fd, void *buffer, size_t length)
328 {
329 service_t *svc = (service_t *)fd;
330 ssize_t rc, i = length;
331 char *p = buffer;
332
333 while ((rc = httpRead(svc->connection, p, i)) != i) {
334 if (rc == 0)
335 return (rc);
336 if (rc < 0)
337 return (rc);
338 i -= rc;
339 p += rc;
340 }
341 #ifdef DEBUG
342 printf("ipp_request_read(0x%8.8x, 0x%8.8x, %d) = %d\n",
343 fd, buffer, length, rc);
344 httpDumpData(stdout, "ipp_request_read:", buffer, length);
345 #endif
346
347 return (length);
348 }
349
350 papi_status_t
ipp_send_initial_request_block(service_t * svc,papi_attribute_t ** request,ssize_t file_size)351 ipp_send_initial_request_block(service_t *svc, papi_attribute_t **request,
352 ssize_t file_size)
353 {
354 papi_status_t result = PAPI_OK;
355 ssize_t chunk_size = 0;
356 char length[32];
357 void *chunk, *ptr;
358 http_status_t status;
359
360 /* calculate the request size */
361 (void) ipp_write_message(&size_calculate, &chunk_size, request);
362
363 /* Fill in the HTTP Header information */
364 httpClearFields(svc->connection);
365 if (svc->transfer_encoding == TRANSFER_ENCODING_CHUNKED)
366 httpSetField(svc->connection, HTTP_FIELD_TRANSFER_ENCODING,
367 "chunked");
368 else {
369 sprintf(length, "%lu", (unsigned long)(file_size + chunk_size));
370 httpSetField(svc->connection, HTTP_FIELD_CONTENT_LENGTH,
371 length);
372 }
373 httpSetField(svc->connection, HTTP_FIELD_CONTENT_TYPE,
374 "application/ipp");
375 httpSetField(svc->connection, HTTP_FIELD_AUTHORIZATION,
376 svc->connection->authstring);
377
378 /* flush any state information about this connection */
379 httpFlush(svc->connection);
380
381 /* if we have don't have a POST path, use the service uri path */
382 if ((svc->post == NULL) && (svc->uri->path))
383 svc->post = strdup(svc->uri->path);
384 /* send the HTTP POST message for the IPP request */
385 /* if the POST fails, return the error */
386 status = httpPost(svc->connection, svc->post);
387 if (status != 0)
388 return (http_to_papi_status(status));
389
390 if (httpCheck(svc->connection) != 0) {
391 status = httpUpdate(svc->connection);
392 if (status != HTTP_OK)
393 return (http_to_papi_status(status));
394 }
395
396 /* build the request chunk */
397 chunk = ptr = calloc(1, chunk_size);
398 result = ipp_write_message(&build_chunk, &ptr, request);
399 #ifdef DEBUG
400 printf("request: %d (0x%x) bytes\n", chunk_size, chunk_size);
401 httpDumpData(stdout, "request:", chunk, chunk_size);
402 #endif
403
404 /* send the actual IPP request */
405 if (ipp_request_write(svc, chunk, chunk_size) != chunk_size)
406 result = PAPI_TEMPORARY_ERROR;
407 free(chunk);
408
409 if (httpCheck(svc->connection) != 0) {
410 status = httpUpdate(svc->connection);
411 if (status != HTTP_OK)
412 return (http_to_papi_status(status));
413 }
414
415 return (result);
416 }
417
418 static int
setAuthString(service_t * svc)419 setAuthString(service_t *svc)
420 {
421 http_t *http;
422 char *user, *passphrase;
423 char encoded[BUFSIZ];
424
425 if ((svc == NULL) || (svc->connection == NULL) || (svc->name == NULL))
426 return (-1);
427
428 http = svc->connection;
429
430 if (svc->user == NULL) {
431 struct passwd *p;
432
433 if ((p = getpwuid(getuid())) != NULL) {
434 user = p->pw_name;
435 } else if ((user = getenv("LOGNAME")) == NULL)
436 user = getenv("USER");
437 if (user == NULL)
438 user = "nobody";
439 } else
440 user = svc->user;
441
442 /* if the passphrase is not set, use the Authentication Callback */
443 if (((svc->password == NULL) || (svc->password[0] == '\0')) &&
444 (svc->authCB != NULL))
445 (svc->authCB)(svc, svc->app_data);
446 passphrase = svc->password;
447
448 /* if there is still no passphrase, we have to fail */
449 if ((passphrase == NULL) || (passphrase[0] == '\0'))
450 return (-1);
451
452 if (strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE],
453 "Basic", 5) == 0) {
454 char plain[BUFSIZ];
455
456 snprintf(plain, sizeof (plain), "%s:%s", user, passphrase);
457 httpEncode64(encoded, plain);
458 snprintf(http->authstring, sizeof (http->authstring),
459 "Basic %s", encoded);
460 } else if (strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE],
461 "Digest", 6) == 0) {
462 char realm[HTTP_MAX_VALUE];
463 char nonce[HTTP_MAX_VALUE];
464 char line [BUFSIZ];
465 char urp[128];
466 char mr[128];
467 char *uri = svc->post;
468
469 httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE,
470 "realm", realm);
471 httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE,
472 "nonce", nonce);
473
474 snprintf(line, sizeof (line), "%s:%s:%s", user, realm,
475 passphrase);
476 md5_calc(urp, line, strlen(line));
477
478 snprintf(line, sizeof (line), "POST:%s", uri);
479 md5_calc(mr, line, strlen(line));
480
481 snprintf(line, sizeof (line), "%s:%s:%s", urp, mr, nonce);
482 md5_calc(encoded, line, strlen(line));
483
484 snprintf(http->authstring, sizeof (http->authstring),
485 "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", "
486 "uri=\"%s\", response=\"%s\"", user, realm, nonce, uri,
487 encoded);
488 }
489
490 return (0);
491 }
492
493 papi_status_t
ipp_status_info(service_t * svc,papi_attribute_t ** response)494 ipp_status_info(service_t *svc, papi_attribute_t **response)
495 {
496 papi_attribute_t **operational = NULL;
497 int32_t status = 0;
498
499 papiAttributeListGetCollection(response, NULL,
500 "operational-attributes-group", &operational);
501 if (operational != NULL) {
502 char *message = NULL;
503
504 papiAttributeListGetString(response, NULL,
505 "status-message", &message);
506 papiAttributeListAddString(&svc->attributes, PAPI_ATTR_REPLACE,
507 "detailed-status-message", message);
508 }
509 papiAttributeListGetInteger(response, NULL, "status-code", &status);
510
511 return (ipp_to_papi_status(status));
512 }
513
514 papi_status_t
ipp_send_request_with_file(service_t * svc,papi_attribute_t ** request,papi_attribute_t *** response,char * file)515 ipp_send_request_with_file(service_t *svc, papi_attribute_t **request,
516 papi_attribute_t ***response, char *file)
517 {
518 papi_status_t result = PAPI_OK;
519 ssize_t size = 0;
520 struct stat statbuf;
521 int fd;
522
523 #ifdef DEBUG
524 fprintf(stderr, "\nIPP-REQUEST: (%s)", (file ? file : ""));
525 papiAttributeListPrint(stderr, request, " ");
526 putc('\n', stderr);
527 fflush(stderr);
528 #endif
529
530 /*
531 * if we are sending a file, open it and include it's size in the
532 * message size.
533 */
534 if (file != NULL) {
535 if ((fd = open(file, O_RDONLY)) < 0) {
536 detailed_error(svc, "%s: %s", file, strerror(errno));
537 return (PAPI_DOCUMENT_ACCESS_ERROR);
538 } else if (strcmp("standard input", file) != 0) {
539 if (stat(file, &statbuf) < 0) {
540 detailed_error(svc,
541 gettext("Cannot access file: %s: %s"),
542 file, strerror(errno));
543 return (PAPI_DOCUMENT_ACCESS_ERROR);
544 }
545 if (statbuf.st_size == 0) {
546 detailed_error(svc,
547 "Zero byte (empty) file: %s", file);
548 return (PAPI_BAD_ARGUMENT);
549 }
550 } else if (svc->transfer_encoding !=
551 TRANSFER_ENCODING_CHUNKED) {
552 struct stat st;
553
554 if (fstat(fd, &st) >= 0)
555 size = st.st_size;
556 }
557 }
558
559 *response = NULL;
560 while (*response == NULL) {
561 http_status_t status = HTTP_CONTINUE;
562
563 result = ipp_send_initial_request_block(svc, request, size);
564
565 if (result == PAPI_OK) {
566 if (file != NULL) {
567 /* send the file contents if we have it */
568 int rc;
569 char buf[BUFSIZ];
570
571 lseek(fd, 0L, SEEK_SET);
572 while ((rc = read(fd, buf, sizeof (buf))) > 0) {
573 if (ipp_request_write(svc, buf, rc)
574 < rc) {
575 break;
576 }
577 }
578 }
579
580 (void) ipp_request_write(svc, "", 0);
581 }
582
583 /* update our connection info */
584 while (status == HTTP_CONTINUE)
585 status = httpUpdate(svc->connection);
586
587 if (status == HTTP_UNAUTHORIZED) {
588 httpFlush(svc->connection);
589 if ((svc->connection->authstring[0] == '\0') &&
590 (setAuthString(svc) == 0)) {
591 httpReconnect(svc->connection);
592 continue;
593 }
594 } else if (status == HTTP_UPGRADE_REQUIRED) {
595 /*
596 * If the transport was built with TLS support, we can
597 * try to use it.
598 */
599 httpFlush(svc->connection);
600 httpReconnect(svc->connection);
601 httpEncryption(svc->connection, HTTP_ENCRYPT_REQUIRED);
602 continue;
603 }
604
605 if (status != HTTP_OK)
606 return (http_to_papi_status(status));
607
608 /* read the IPP response */
609 result = ipp_read_message(&ipp_request_read, svc, response,
610 IPP_TYPE_RESPONSE);
611
612 if (result == PAPI_OK)
613 result = ipp_status_info(svc, *response);
614 #ifdef DEBUG
615 fprintf(stderr, "\nIPP-RESPONSE: (%s) (%s)", (file ? file : ""),
616 papiStatusString(result));
617 papiAttributeListPrint(stderr, *response, " ");
618 putc('\n', stderr);
619 fflush(stderr);
620 #endif
621 }
622
623 return (result);
624 }
625
626 papi_status_t
ipp_send_request(service_t * svc,papi_attribute_t ** request,papi_attribute_t *** response)627 ipp_send_request(service_t *svc, papi_attribute_t **request,
628 papi_attribute_t ***response)
629 {
630 return (ipp_send_request_with_file(svc, request, response, NULL));
631 }
632