summaryrefslogtreecommitdiff
path: root/usr/src/lib/print/libpapi-ipp/common/ipp-support.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/print/libpapi-ipp/common/ipp-support.c')
-rw-r--r--usr/src/lib/print/libpapi-ipp/common/ipp-support.c612
1 files changed, 612 insertions, 0 deletions
diff --git a/usr/src/lib/print/libpapi-ipp/common/ipp-support.c b/usr/src/lib/print/libpapi-ipp/common/ipp-support.c
new file mode 100644
index 0000000000..f0f6ced38d
--- /dev/null
+++ b/usr/src/lib/print/libpapi-ipp/common/ipp-support.c
@@ -0,0 +1,612 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ *
+ */
+
+/* $Id: ipp-support.c 148 2006-04-25 16:54:17Z njacobs $ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <papi_impl.h>
+#include <stdlib.h>
+#include <pwd.h>
+#include <locale.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <md5.h>
+
+#include <config-site.h>
+
+papi_status_t
+http_to_papi_status(http_status_t status)
+{
+ switch (status) {
+ case HTTP_OK:
+ return (PAPI_OK);
+ case HTTP_BAD_REQUEST:
+ return (PAPI_BAD_REQUEST);
+ case HTTP_UNAUTHORIZED:
+ case HTTP_FORBIDDEN:
+ return (PAPI_NOT_AUTHORIZED);
+ case HTTP_NOT_FOUND:
+ return (PAPI_NOT_FOUND);
+ case HTTP_GONE:
+ return (PAPI_GONE);
+ case HTTP_SERVICE_UNAVAILABLE:
+ return (PAPI_SERVICE_UNAVAILABLE);
+ default:
+ return ((papi_status_t)status);
+ }
+}
+
+papi_status_t
+ipp_to_papi_status(uint16_t status)
+{
+ switch (status) {
+ case IPP_OK:
+ return (PAPI_OK);
+ case IPP_OK_IGNORED_ATTRIBUTES:
+ return (PAPI_OK);
+ case IPP_OK_CONFLICTING_ATTRIBUTES:
+ return (PAPI_OK);
+ case IPP_OK_IGNORED_SUBSCRIPTIONS:
+ return (PAPI_OK_IGNORED_SUBSCRIPTIONS);
+ case IPP_OK_IGNORED_NOTIFICATIONS:
+ return (PAPI_OK_IGNORED_NOTIFICATIONS);
+ case IPP_CERR_BAD_REQUEST:
+ return (PAPI_BAD_REQUEST);
+ case IPP_CERR_FORBIDDEN:
+ return (PAPI_FORBIDDEN);
+ case IPP_CERR_NOT_AUTHENTICATED:
+ return (PAPI_NOT_AUTHENTICATED);
+ case IPP_CERR_NOT_AUTHORIZED:
+ return (PAPI_NOT_AUTHORIZED);
+ case IPP_CERR_NOT_POSSIBLE:
+ return (PAPI_NOT_POSSIBLE);
+ case IPP_CERR_TIMEOUT:
+ return (PAPI_TIMEOUT);
+ case IPP_CERR_NOT_FOUND:
+ return (PAPI_NOT_FOUND);
+ case IPP_CERR_GONE:
+ return (PAPI_GONE);
+ case IPP_CERR_REQUEST_ENTITY:
+ return (PAPI_REQUEST_ENTITY);
+ case IPP_CERR_REQUEST_VALUE:
+ return (PAPI_REQUEST_VALUE);
+ case IPP_CERR_DOCUMENT_FORMAT:
+ return (PAPI_DOCUMENT_FORMAT);
+ case IPP_CERR_ATTRIBUTES:
+ return (PAPI_ATTRIBUTES);
+ case IPP_CERR_URI_SCHEME:
+ return (PAPI_URI_SCHEME);
+ case IPP_CERR_CHARSET:
+ return (PAPI_CHARSET);
+ case IPP_CERR_CONFLICT:
+ return (PAPI_CONFLICT);
+ case IPP_CERR_COMPRESSION_NOT_SUPPORTED:
+ return (PAPI_COMPRESSION_NOT_SUPPORTED);
+ case IPP_CERR_COMPRESSION_ERROR:
+ return (PAPI_COMPRESSION_ERROR);
+ case IPP_CERR_DOCUMENT_FORMAT_ERROR:
+ return (PAPI_DOCUMENT_FORMAT_ERROR);
+ case IPP_CERR_DOCUMENT_ACCESS_ERROR:
+ return (PAPI_DOCUMENT_ACCESS_ERROR);
+ case IPP_CERR_ATTRIBUTES_NOT_SETTABLE:
+ return (PAPI_ATTRIBUTES_NOT_SETTABLE);
+ case IPP_CERR_IGNORED_ALL_SUBSCRIPTIONS:
+ return (PAPI_IGNORED_ALL_SUBSCRIPTIONS);
+ case IPP_CERR_TOO_MANY_SUBSCRIPTIONS:
+ return (PAPI_TOO_MANY_SUBSCRIPTIONS);
+ case IPP_CERR_IGNORED_ALL_NOTIFICATIONS:
+ return (PAPI_IGNORED_ALL_NOTIFICATIONS);
+ case IPP_CERR_PRINT_SUPPORT_FILE_NOT_FOUND:
+ return (PAPI_PRINT_SUPPORT_FILE_NOT_FOUND);
+ case IPP_SERR_INTERNAL:
+ return (PAPI_INTERNAL_ERROR);
+ case IPP_SERR_OPERATION_NOT_SUPPORTED:
+ return (PAPI_OPERATION_NOT_SUPPORTED);
+ case IPP_SERR_SERVICE_UNAVAILABLE:
+ return (PAPI_SERVICE_UNAVAILABLE);
+ case IPP_SERR_VERSION_NOT_SUPPORTED:
+ return (PAPI_VERSION_NOT_SUPPORTED);
+ case IPP_SERR_DEVICE_ERROR:
+ return (PAPI_DEVICE_ERROR);
+ case IPP_SERR_TEMPORARY_ERROR:
+ return (PAPI_TEMPORARY_ERROR);
+ case IPP_SERR_NOT_ACCEPTING:
+ return (PAPI_NOT_ACCEPTING);
+ case IPP_SERR_BUSY:
+ case IPP_SERR_CANCELLED:
+ default:
+ return (PAPI_TEMPORARY_ERROR);
+ }
+}
+
+void
+ipp_initialize_request(service_t *svc, papi_attribute_t ***request,
+ uint16_t operation)
+{
+ papiAttributeListAddInteger(request, PAPI_ATTR_EXCL,
+ "version-major", 1);
+ papiAttributeListAddInteger(request, PAPI_ATTR_EXCL,
+ "version-minor", 1);
+ papiAttributeListAddInteger(request, PAPI_ATTR_EXCL,
+ "request-id", (short)lrand48());
+ papiAttributeListAddInteger(request, PAPI_ATTR_EXCL,
+ "operation-id", operation);
+}
+
+void
+ipp_initialize_operational_attributes(service_t *svc, papi_attribute_t ***op,
+ papi_attribute_t **attributes)
+{
+ char *charset = "utf-8"; /* default to UTF-8 encoding */
+ char *language = setlocale(LC_ALL, "");
+ char *user = "nobody";
+ struct passwd *pw = NULL;
+
+ /*
+ * All IPP requests must contain the following:
+ * attributes-charset (UTF-8)
+ * attributes-natural-language (our current locale)
+ * requesting-user-name (process user)
+ */
+ papiAttributeListGetString(attributes, NULL,
+ "attributes-charset", &charset);
+ papiAttributeListAddString(op, PAPI_ATTR_EXCL,
+ "attributes-charset", charset);
+
+ papiAttributeListGetString(attributes, NULL,
+ "attributes-natural-language", &language);
+ papiAttributeListAddString(op, PAPI_ATTR_EXCL,
+ "attributes-natural-language", language);
+
+ if ((pw = getpwuid(getuid())) != NULL)
+ user = pw->pw_name;
+ /*
+ * if our euid is 0 "super user", we will allow the system supplied
+ * user name to be overridden, if the requestor wants to.
+ */
+ if (geteuid() == 0) {
+ if (svc->user != NULL)
+ user = svc->user;
+ papiAttributeListGetString(attributes, NULL,
+ "requesting-user-name", &user);
+ }
+ papiAttributeListAddString(op, PAPI_ATTR_REPLACE,
+ "requesting-user-name", user);
+}
+
+#ifndef OPID_CUPS_GET_DEFAULT /* for servers that will enumerate */
+#define OPID_CUPS_GET_DEFAULT 0x4001
+#endif /* OPID_CUPS_GET_DEFAULT */
+
+static papi_status_t
+_default_destination(service_t *svc, char **uri)
+{
+ papi_status_t result = PAPI_INTERNAL_ERROR;
+ printer_t *p = NULL;
+ papi_attribute_t **request = NULL, **op = NULL, **response = NULL;
+ char *tmp = NULL;
+
+ if ((svc == NULL) || (uri == NULL))
+ return (PAPI_BAD_ARGUMENT);
+
+ /* we must be connected to find the default destination */
+ if (svc->connection == NULL)
+ return (PAPI_NOT_POSSIBLE);
+
+ if ((p = calloc(1, sizeof (*p))) == NULL)
+ return (PAPI_TEMPORARY_ERROR);
+
+ ipp_initialize_request(svc, &request, OPID_CUPS_GET_DEFAULT);
+ ipp_initialize_operational_attributes(svc, &op, NULL);
+ papiAttributeListAddString(&op, PAPI_ATTR_APPEND,
+ "requested-attributes", "printer-uri-supported");
+ papiAttributeListAddCollection(&request, PAPI_ATTR_REPLACE,
+ "operational-attributes-group", op);
+ papiAttributeListFree(op);
+ result = ipp_send_request(svc, request, &response);
+ papiAttributeListFree(request);
+
+ op = NULL;
+ papiAttributeListGetCollection(response, NULL,
+ "printer-attributes-group", &op);
+
+ if (uri != NULL) {
+ char *tmp = NULL;
+
+ papiAttributeListGetString(op, NULL, "printer-uri", &tmp);
+ papiAttributeListGetString(op, NULL,
+ "printer-uri-supported", &tmp);
+ if (tmp != NULL)
+ *uri = strdup(tmp);
+ }
+
+ papiAttributeListFree(response);
+
+ return (result);
+}
+
+void
+ipp_add_printer_uri(service_t *svc, char *name, papi_attribute_t ***op)
+{
+ char *uri = name;
+ char buf[BUFSIZ];
+ uri_t *tmp = NULL;
+
+ if (strstr(name, "://") == NULL) { /* not in URI form */
+ if (strcmp(name, DEFAULT_DEST) != 0) {
+ /* not the "default" */
+ snprintf(buf, sizeof (buf), "%s/%s", svc->name, name);
+ uri = buf;
+ } else
+ _default_destination(svc, &uri);
+ }
+
+ papiAttributeListAddString(op, PAPI_ATTR_EXCL, "printer-uri", uri);
+
+ /* save the printer-uri's path to be used by http POST request */
+ if ((uri_from_string(uri, &tmp) == 0) && (tmp != NULL)) {
+ if (svc->post != NULL)
+ free(svc->post);
+ svc->post = strdup(tmp->path);
+ uri_free(tmp);
+ }
+}
+
+
+/*
+ * don't actually write anything, just add to the total size and return the
+ * size of what would be written, so we can figure out how big the request
+ * is going to be.
+ */
+static ssize_t
+size_calculate(void *fd, void *buffer, size_t length)
+{
+ ssize_t *size = (ssize_t *)fd;
+
+ *size += length;
+ return (length);
+}
+
+
+static ssize_t
+build_chunk(void *fd, void *buffer, size_t length)
+{
+ char **s1 = fd;
+
+ memcpy(*s1, buffer, length);
+ *s1 = *s1 + length;
+
+ return (length);
+}
+
+ssize_t
+ipp_request_write(void *fd, void *buffer, size_t length)
+{
+ service_t *svc = (service_t *)fd;
+
+#ifdef DEBUG
+ printf("ipp_request_write(0x%8.8x, 0x%8.8x, %d)\n", fd, buffer, length);
+ httpDumpData(stdout, "ipp_request_write:", buffer, length);
+#endif
+ return (httpWrite(svc->connection, buffer, length));
+}
+
+ssize_t
+ipp_request_read(void *fd, void *buffer, size_t length)
+{
+ service_t *svc = (service_t *)fd;
+ ssize_t rc, i = length;
+ char *p = buffer;
+
+ while ((rc = httpRead(svc->connection, p, i)) != i) {
+ if (rc == 0)
+ return (rc);
+ if (rc < 0)
+ return (rc);
+ i -= rc;
+ p += rc;
+ }
+#ifdef DEBUG
+ printf("ipp_request_read(0x%8.8x, 0x%8.8x, %d) = %d\n",
+ fd, buffer, length, rc);
+ httpDumpData(stdout, "ipp_request_read:", buffer, length);
+#endif
+
+ return (length);
+}
+
+papi_status_t
+ipp_send_initial_request_block(service_t *svc, papi_attribute_t **request,
+ ssize_t file_size)
+{
+ papi_status_t result = PAPI_OK;
+ ssize_t chunk_size = 0;
+ char length[32];
+ void *chunk, *ptr;
+ http_status_t status;
+
+ /* calculate the request size */
+ (void) ipp_write_message(&size_calculate, &chunk_size, request);
+
+ /* Fill in the HTTP Header information */
+ httpClearFields(svc->connection);
+ if (svc->transfer_encoding == TRANSFER_ENCODING_CHUNKED)
+ httpSetField(svc->connection, HTTP_FIELD_TRANSFER_ENCODING,
+ "chunked");
+ else {
+ sprintf(length, "%lu", (unsigned long)(file_size + chunk_size));
+ httpSetField(svc->connection, HTTP_FIELD_CONTENT_LENGTH,
+ length);
+ }
+ httpSetField(svc->connection, HTTP_FIELD_CONTENT_TYPE,
+ "application/ipp");
+ httpSetField(svc->connection, HTTP_FIELD_AUTHORIZATION,
+ svc->connection->authstring);
+
+ /* flush any state information about this connection */
+ httpFlush(svc->connection);
+
+ /* if we have don't have a POST path, use the service uri path */
+ if (svc->post == NULL)
+ svc->post = strdup(svc->uri->path);
+ /* send the HTTP POST message for the IPP request */
+ /* if the POST fails, return the error */
+ status = httpPost(svc->connection, svc->post);
+ if (status != 0)
+ return (http_to_papi_status(status));
+
+ if (httpCheck(svc->connection) != 0) {
+ status = httpUpdate(svc->connection);
+ if (status != HTTP_OK)
+ return (http_to_papi_status(status));
+ }
+
+ /* build the request chunk */
+ chunk = ptr = calloc(1, chunk_size);
+ result = ipp_write_message(&build_chunk, &ptr, request);
+#ifdef DEBUG
+ printf("request: %d (0x%x) bytes\n", chunk_size, chunk_size);
+ httpDumpData(stdout, "request:", chunk, chunk_size);
+#endif
+
+ /* send the actual IPP request */
+ if (ipp_request_write(svc, chunk, chunk_size) != chunk_size)
+ result = PAPI_TEMPORARY_ERROR;
+ free(chunk);
+
+ if (httpCheck(svc->connection) != 0) {
+ status = httpUpdate(svc->connection);
+ if (status != HTTP_OK)
+ return (http_to_papi_status(status));
+ }
+
+ return (result);
+}
+
+static int
+setAuthString(service_t *svc)
+{
+ http_t *http;
+ char *user, *passphrase;
+ char encoded[BUFSIZ];
+
+ if ((svc == NULL) || (svc->connection == NULL) || (svc->name == NULL))
+ return (-1);
+
+ http = svc->connection;
+
+ if (svc->user == NULL) {
+ struct passwd *p;
+
+ if ((p = getpwuid(getuid())) != NULL) {
+ user = p->pw_name;
+ } else if ((user = getenv("LOGNAME")) == NULL)
+ user = getenv("USER");
+ if (user == NULL)
+ user = "nobody";
+ } else
+ user = svc->user;
+
+ /* if the passphrase is not set, use the Authentication Callback */
+ if (((svc->password == NULL) || (svc->password[0] == '\0')) &&
+ (svc->authCB != NULL))
+ (svc->authCB)(svc, svc->app_data);
+ passphrase = svc->password;
+
+ /* if there is still no passphrase, we have to fail */
+ if ((passphrase == NULL) || (passphrase[0] == '\0'))
+ return (-1);
+
+ if (strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE],
+ "Basic", 5) == 0) {
+ char plain[BUFSIZ];
+
+ snprintf(plain, sizeof (plain), "%s:%s", user, passphrase);
+ httpEncode64(encoded, plain);
+ snprintf(http->authstring, sizeof (http->authstring),
+ "Basic %s", encoded);
+ } else if (strncmp(http->fields[HTTP_FIELD_WWW_AUTHENTICATE],
+ "Digest", 6) == 0) {
+ char realm[HTTP_MAX_VALUE];
+ char nonce[HTTP_MAX_VALUE];
+ char line [BUFSIZ];
+ char urp[128];
+ char mr[128];
+ char *uri = svc->post;
+
+ httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE,
+ "realm", realm);
+ httpGetSubField(http, HTTP_FIELD_WWW_AUTHENTICATE,
+ "nonce", nonce);
+
+ snprintf(line, sizeof (line), "%s:%s:%s", user, realm,
+ passphrase);
+ md5_calc(urp, line, strlen(line));
+
+ snprintf(line, sizeof (line), "POST:%s", uri);
+ md5_calc(mr, line, strlen(line));
+
+ snprintf(line, sizeof (line), "%s:%s:%s", urp, mr, nonce);
+ md5_calc(encoded, line, strlen(line));
+
+ snprintf(http->authstring, sizeof (http->authstring),
+ "Digest username=\"%s\", realm=\"%s\", nonce=\"%s\", "
+ "uri=\"%s\", response=\"%s\"", user, realm, nonce, uri,
+ encoded);
+ }
+
+ return (0);
+}
+
+papi_status_t
+ipp_status_info(service_t *svc, papi_attribute_t **response)
+{
+ papi_attribute_t **operational = NULL;
+ int32_t status = 0;
+
+ papiAttributeListGetCollection(response, NULL,
+ "operational-attributes-group", &operational);
+ if (operational != NULL) {
+ char *message = NULL;
+
+ papiAttributeListGetString(response, NULL,
+ "status-message", &message);
+ papiAttributeListAddString(&svc->attributes, PAPI_ATTR_REPLACE,
+ "detailed-status-message", message);
+ }
+ papiAttributeListGetInteger(response, NULL, "status-code", &status);
+
+ return (ipp_to_papi_status(status));
+}
+
+papi_status_t
+ipp_send_request_with_file(service_t *svc, papi_attribute_t **request,
+ papi_attribute_t ***response, char *file)
+{
+ papi_status_t result = PAPI_OK;
+ ssize_t size = 0;
+ int fd;
+
+#ifdef DEBUG
+ fprintf(stderr, "\nIPP-REQUEST: (%s)", (file ? file : ""));
+ papiAttributeListPrint(stderr, request, " ");
+ putc('\n', stderr);
+ fflush(stderr);
+#endif
+
+ /*
+ * if we are sending a file, open it and include it's size in the
+ * message size.
+ */
+ if (file != NULL) {
+ if ((fd = open(file, O_RDONLY)) < 0) {
+ detailed_error(svc, "%s: %s", file, strerror(errno));
+ return (PAPI_DOCUMENT_ACCESS_ERROR);
+ } else if (svc->transfer_encoding !=
+ TRANSFER_ENCODING_CHUNKED) {
+ struct stat st;
+
+ if (fstat(fd, &st) >= 0)
+ size = st.st_size;
+ }
+ }
+
+ *response = NULL;
+ while (*response == NULL) {
+ http_status_t status = HTTP_CONTINUE;
+
+ result = ipp_send_initial_request_block(svc, request, size);
+
+ if (result == PAPI_OK) {
+ if (file != NULL) {
+ /* send the file contents if we have it */
+ int rc;
+ char buf[BUFSIZ];
+
+ lseek(fd, 0L, SEEK_SET);
+ while ((rc = read(fd, buf, sizeof (buf))) > 0) {
+ if (ipp_request_write(svc, buf, rc)
+ < rc) {
+ break;
+ }
+ }
+ }
+
+ (void) ipp_request_write(svc, "", 0);
+ }
+
+ /* update our connection info */
+ while (status == HTTP_CONTINUE)
+ status = httpUpdate(svc->connection);
+
+ if (status == HTTP_UNAUTHORIZED) {
+ httpFlush(svc->connection);
+ if ((svc->connection->authstring[0] == '\0') &&
+ (setAuthString(svc) == 0)) {
+ httpReconnect(svc->connection);
+ continue;
+ }
+ } else if (status == HTTP_UPGRADE_REQUIRED) {
+ /*
+ * If the transport was built with TLS support, we can
+ * try to use it.
+ */
+ httpFlush(svc->connection);
+ httpReconnect(svc->connection);
+ httpEncryption(svc->connection, HTTP_ENCRYPT_REQUIRED);
+ continue;
+ }
+
+ if (status != HTTP_OK)
+ return (http_to_papi_status(status));
+
+ /* read the IPP response */
+ result = ipp_read_message(&ipp_request_read, svc, response,
+ IPP_TYPE_RESPONSE);
+
+ if (result == PAPI_OK)
+ result = ipp_status_info(svc, *response);
+#ifdef DEBUG
+ fprintf(stderr, "\nIPP-RESPONSE: (%s) (%s)", (file ? file : ""),
+ papiStatusString(result));
+ papiAttributeListPrint(stderr, *response, " ");
+ putc('\n', stderr);
+ fflush(stderr);
+#endif
+ }
+
+ return (result);
+}
+
+papi_status_t
+ipp_send_request(service_t *svc, papi_attribute_t **request,
+ papi_attribute_t ***response)
+{
+ return (ipp_send_request_with_file(svc, request, response, NULL));
+}