summaryrefslogtreecommitdiff
path: root/usr/src/lib/print/mod_ipp/mod_ipp.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/print/mod_ipp/mod_ipp.c')
-rw-r--r--usr/src/lib/print/mod_ipp/mod_ipp.c552
1 files changed, 552 insertions, 0 deletions
diff --git a/usr/src/lib/print/mod_ipp/mod_ipp.c b/usr/src/lib/print/mod_ipp/mod_ipp.c
new file mode 100644
index 0000000000..2d9ece2287
--- /dev/null
+++ b/usr/src/lib/print/mod_ipp/mod_ipp.c
@@ -0,0 +1,552 @@
+/*
+ * 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: mod_ipp.c 149 2006-04-25 16:55:01Z njacobs $ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * Internet Printing Protocol (IPP) module for Apache.
+ */
+
+#include "ap_config.h"
+
+#include <stdio.h>
+#include <time.h>
+#include <sys/time.h>
+#include <values.h>
+#include <libintl.h>
+#include <alloca.h>
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "papi.h"
+#ifndef APACHE_RELEASE /* appears to only exist in Apache 1.X */
+#define APACHE2
+#include "apr_compat.h"
+#endif
+
+#include <papi.h>
+#include <ipp-listener.h>
+
+#ifndef APACHE2
+module MODULE_VAR_EXPORT ipp_module;
+#else
+module AP_MODULE_DECLARE_DATA ipp_module;
+#endif
+
+#ifndef AP_INIT_TAKE1 /* Apache 2.X has this, but 1.3.X does not */
+#define AP_INIT_NO_ARGS(directive, action, arg, where, mesg) \
+ { directive, action, arg, where, NO_ARGS, mesg }
+#define AP_INIT_TAKE1(directive, action, arg, where, mesg) \
+ { directive, action, arg, where, TAKE1, mesg }
+#define AP_INIT_TAKE2(directive, action, arg, where, mesg) \
+ { directive, action, arg, where, TAKE2, mesg }
+#endif
+
+typedef struct {
+ int conformance;
+ char *default_user;
+ char *default_svc;
+ papi_attribute_t **operations;
+} IPPListenerConfig;
+
+#ifdef DEBUG
+void
+dump_buffer(FILE *fp, char *tag, char *buffer, int bytes)
+{
+ int i, j, ch;
+
+ fprintf(fp, "%s %d(0x%x) bytes\n", (tag ? tag : ""), bytes, bytes);
+ for (i = 0; i < bytes; i += 16) {
+ fprintf(fp, "%s ", (tag ? tag : ""));
+
+ for (j = 0; j < 16 && (i + j) < bytes; j ++)
+ fprintf(fp, " %02X", buffer[i + j] & 255);
+
+ while (j < 16) {
+ fprintf(fp, " ");
+ j++;
+ }
+
+ fprintf(fp, " ");
+ for (j = 0; j < 16 && (i + j) < bytes; j ++) {
+ ch = buffer[i + j] & 255;
+ if (ch < ' ' || ch == 127)
+ ch = '.';
+ putc(ch, fp);
+ }
+ putc('\n', fp);
+ }
+ fflush(fp);
+}
+#endif
+
+static ssize_t
+read_data(void *fd, void *buf, size_t siz)
+{
+ ssize_t len_read;
+ request_rec *ap_r = (request_rec *)fd;
+
+ len_read = ap_get_client_block(ap_r, buf, siz);
+#ifndef APACHE2
+ ap_reset_timeout(ap_r);
+#endif
+
+#ifdef DEBUG
+ fprintf(stderr, "read_data(0x%8.8x, 0x%8.8x, %d): %d",
+ fd, buf, siz, len_read);
+ if (len_read < 0)
+ fprintf(stderr, ": %s", strerror(errno));
+ putc('\n', stderr);
+ dump_buffer(stderr, "read_data:", buf, len_read);
+#endif
+
+ return (len_read);
+}
+
+static ssize_t
+write_data(void *fd, void *buf, size_t siz)
+{
+ ssize_t len_written;
+ request_rec *ap_r = (request_rec *)fd;
+
+#ifndef APACHE2
+ ap_reset_timeout(ap_r);
+#endif
+#ifdef DEBUG
+ dump_buffer(stderr, "write_data:", buf, siz);
+#endif
+ len_written = ap_rwrite(buf, siz, ap_r);
+
+ return (len_written);
+}
+
+static void
+discard_data(request_rec *r)
+{
+#ifdef APACHE2
+ (void) ap_discard_request_body(r);
+#else
+ /*
+ * This is taken from ap_discard_request_body(). The reason we can't
+ * just use it in Apache 1.3 is that it does various timeout things we
+ * don't want it to do. Apache 2.0 doesn't do that, so we can safely
+ * use the normal function.
+ */
+ if (r->read_chunked || r->remaining > 0) {
+ char dumpbuf[HUGE_STRING_LEN];
+ int i;
+
+ do {
+ i = ap_get_client_block(r, dumpbuf, HUGE_STRING_LEN);
+#ifdef DEBUG
+ dump_buffer(stderr, "discarded", dumpbuf, i);
+#endif
+ } while (i > 0);
+ }
+#endif
+}
+
+void _log_rerror(const char *file, int line, int level, request_rec *r,
+ const char *fmt, ...)
+{
+ va_list args;
+ size_t size;
+ char *message = alloca(BUFSIZ);
+
+ va_start(args, fmt);
+ /*
+ * fill in the message. If the buffer is too small, allocate
+ * one that is large enough and fill it in.
+ */
+ if ((size = vsnprintf(message, BUFSIZ, fmt, args)) >= BUFSIZ)
+ if ((message = alloca(size)) != NULL)
+ vsnprintf(message, size, fmt, args);
+ va_end(args);
+
+#ifdef APACHE2
+ ap_log_rerror(file, line, level, NULL, r, message);
+#else
+ ap_log_rerror(file, line, level, r, message);
+#endif
+}
+
+static int
+ipp_handler(request_rec *r)
+{
+ papi_attribute_t **request = NULL, **response = NULL;
+ IPPListenerConfig *config;
+ papi_status_t status;
+ int ret;
+
+ /* Really, IPP is all POST requests */
+ if (r->method_number != M_POST)
+ return (DECLINED);
+
+#ifndef APACHE2
+ /*
+ * An IPP request must have a MIME type of "application/ipp"
+ * (RFC-2910, Section 4, page 19). If it doesn't match this
+ * MIME type, we should decline the request and let someone else
+ * try and handle it.
+ */
+ if (r->headers_in != NULL) {
+ char *mime_type = (char *)ap_table_get(r->headers_in,
+ "Content-Type");
+
+ if ((mime_type == NULL) ||
+ (strcasecmp(mime_type, "application/ipp") != 0))
+ return (DECLINED);
+ }
+#endif
+ /* CHUNKED_DECHUNK might not work right for IPP? */
+ if ((ret = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK)) != OK)
+ return (ret);
+
+ if (!ap_should_client_block(r))
+ return (HTTP_INTERNAL_SERVER_ERROR);
+
+#ifndef APACHE2
+ ap_soft_timeout("ipp_module: read/reply request ", r);
+#endif
+ /* read the IPP request off the network */
+ status = ipp_read_message(read_data, r, &request, IPP_TYPE_REQUEST);
+
+ if (status != PAPI_OK)
+ _log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "read failed: %s\n", papiStatusString(status));
+#ifdef DEBUG
+ papiAttributeListPrint(stderr, request, "request (%d) ", getpid());
+#endif
+
+ (void) papiAttributeListAddString(&request, PAPI_ATTR_EXCL,
+ "originating-host", (char *)
+#ifdef APACHE2
+ ap_get_remote_host
+ (r->connection, r->per_dir_config, REMOTE_NAME, NULL));
+#else
+ ap_get_remote_host
+ (r->connection, r->per_dir_config, REMOTE_NAME));
+#endif
+
+ (void) papiAttributeListAddInteger(&request, PAPI_ATTR_EXCL,
+ "uri-port", ap_get_server_port(r));
+ if (r->headers_in != NULL) {
+ char *host = (char *)ap_table_get(r->headers_in, "Host");
+
+ if ((host == NULL) || (host[0] == '\0'))
+ host = (char *)ap_get_server_name(r);
+
+ (void) papiAttributeListAddString(&request, PAPI_ATTR_EXCL,
+ "uri-host", host);
+ }
+ (void) papiAttributeListAddString(&request, PAPI_ATTR_EXCL,
+ "uri-path", r->uri);
+
+ config = ap_get_module_config(r->per_dir_config, &ipp_module);
+ if (config != NULL) {
+ (void) papiAttributeListAddInteger(&request, PAPI_ATTR_EXCL,
+ "conformance", config->conformance);
+ (void) papiAttributeListAddCollection(&request, PAPI_ATTR_EXCL,
+ "operations", config->operations);
+ if (config->default_user != NULL)
+ (void) papiAttributeListAddString(&request,
+ PAPI_ATTR_EXCL, "default-user",
+ config->default_user);
+ if (config->default_svc != NULL)
+ (void) papiAttributeListAddString(&request,
+ PAPI_ATTR_EXCL, "default-service",
+ config->default_svc);
+ }
+
+ /*
+ * For Trusted Solaris, pass the fd number of the socket connection
+ * to the backend so the it can be forwarded to the backend print
+ * service to retrieve the sensativity label off of a multi-level
+ * port.
+ */
+ (void) papiAttributeListAddInteger(&request, PAPI_ATTR_EXCL,
+ "peer-socket", ap_bfileno(r->connection->client, B_RD));
+
+ /* process the request */
+ status = ipp_process_request(request, &response, read_data, r);
+ if (status != PAPI_OK) {
+ errno = 0;
+ _log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "request failed: %s\n", papiStatusString(status));
+ discard_data(r);
+ }
+#ifdef DEBUG
+ fprintf(stderr, "processing result: %s\n", papiStatusString(status));
+ papiAttributeListPrint(stderr, response, "response (%d) ", getpid());
+#endif
+
+ /*
+ * If the client is using chunking and we have not yet received the
+ * final "0" sized chunk, we need to discard any data that may
+ * remain in the post request.
+ */
+ if ((r->read_chunked != 0) &&
+ (ap_table_get(r->headers_in, "Content-Length") == NULL))
+ discard_data(r);
+
+ /* write an IPP response back to the network */
+ r->content_type = "application/ipp";
+
+#ifndef APACHE2
+ ap_send_http_header(r);
+#endif
+
+ status = ipp_write_message(write_data, r, response);
+ if (status != PAPI_OK)
+ _log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "write failed: %s\n", papiStatusString(status));
+#ifdef DEBUG
+ fprintf(stderr, "write result: %s\n", papiStatusString(status));
+ fflush(stderr);
+#endif
+
+ papiAttributeListFree(request);
+ papiAttributeListFree(response);
+
+#ifndef APACHE2
+ ap_kill_timeout(r);
+ if (ap_rflush(r) < 0)
+ _log_rerror(APLOG_MARK, APLOG_ERR, r,
+ "flush failed, response may not have been sent");
+#endif
+
+ return (OK);
+}
+
+
+/*ARGSUSED1*/
+static void *
+create_ipp_dir_config(
+#ifndef APACHE2
+ pool *p,
+#else
+ apr_pool_t *p,
+#endif
+ char *dirspec)
+{
+ IPPListenerConfig *config =
+#ifndef APACHE2
+ ap_pcalloc(p, sizeof (*config));
+#else
+ apr_pcalloc(p, sizeof (*config));
+#endif
+
+ if (config != NULL) {
+ (void) memset(config, 0, sizeof (*config));
+ config->conformance = IPP_PARSE_CONFORMANCE_RASH;
+ config->default_user = NULL;
+ config->default_svc = NULL;
+ (void) ipp_configure_operation(&config->operations, "required",
+ "enable");
+ }
+
+ return (config);
+}
+
+/*ARGSUSED0*/
+static const char *
+ipp_conformance(cmd_parms *cmd, void *cfg, const char *arg)
+{
+ IPPListenerConfig *config = (IPPListenerConfig *)cfg;
+
+ if (strncasecmp(arg, "automatic", 4) == 0) {
+ config->conformance = IPP_PARSE_CONFORMANCE_RASH;
+ } else if (strcasecmp(arg, "1.0") == 0) {
+ config->conformance = IPP_PARSE_CONFORMANCE_LOOSE;
+ } else if (strcasecmp(arg, "1.1") == 0) {
+ config->conformance = IPP_PARSE_CONFORMANCE_STRICT;
+ } else {
+ return ("unknown conformance, try (automatic/1.0/1.1)");
+ }
+
+ return (NULL);
+}
+
+/*ARGSUSED0*/
+static const char *
+ipp_operation(cmd_parms *cmd, void *cfg, char *op, char *toggle)
+{
+ IPPListenerConfig *config = (IPPListenerConfig *)cfg;
+ papi_status_t status;
+
+ status = ipp_configure_operation(&config->operations, op, toggle);
+ switch (status) {
+ case PAPI_OK:
+ return (NULL);
+ case PAPI_BAD_ARGUMENT:
+ return (gettext("internal error (invalid argument)"));
+ default:
+ return (papiStatusString(status));
+ }
+
+ /* NOTREACHED */
+ /* return (gettext("contact your software vendor")); */
+}
+
+static const char *
+ipp_default_user(cmd_parms *cmd, void *cfg, const char *arg)
+{
+ IPPListenerConfig *config = (IPPListenerConfig *)cfg;
+
+ config->default_user = (char *)arg;
+
+ return (NULL);
+}
+
+static const char *
+ipp_default_svc(cmd_parms *cmd, void *cfg, const char *arg)
+{
+ IPPListenerConfig *config = (IPPListenerConfig *)cfg;
+
+ config->default_svc = (char *)arg;
+
+ return (NULL);
+}
+
+#ifdef DEBUG
+/*ARGSUSED0*/
+static const char *
+ipp_module_hang(cmd_parms *cmd, void *cfg)
+{
+ static int i = 1;
+
+ /* wait so we can attach a debugger, assign i = 0, and step through */
+ while (i);
+
+ return (NULL);
+}
+#endif /* DEBUG */
+
+static const command_rec ipp_cmds[] =
+{
+ AP_INIT_TAKE1("ipp-conformance", ipp_conformance, NULL, ACCESS_CONF,
+ "IPP protocol conformance (loose/strict)"),
+ AP_INIT_TAKE2("ipp-operation", ipp_operation, NULL, ACCESS_CONF,
+ "IPP protocol operations to enable/disable)"),
+ AP_INIT_TAKE1("ipp-default-user", ipp_default_user, NULL, ACCESS_CONF,
+ "default user for various operations"),
+ AP_INIT_TAKE1("ipp-default-service", ipp_default_svc, NULL, ACCESS_CONF,
+ "default service for various operations"),
+#ifdef DEBUG
+ AP_INIT_NO_ARGS("ipp-module-hang", ipp_module_hang, NULL, ACCESS_CONF,
+ "hang the module until we can attach a debugger (no args)"),
+#endif
+ { NULL }
+};
+
+#ifdef APACHE2
+/*ARGSUSED0*/
+static const char *
+ipp_method(const request_rec *r)
+{
+ return ("ipp");
+}
+
+/*ARGSUSED0*/
+static unsigned short
+ipp_port(const request_rec *r)
+{
+ return (631);
+}
+
+/* Dispatch list for API hooks */
+/*ARGSUSED0*/
+static void
+ipp_register_hooks(apr_pool_t *p)
+{
+ static const char * const modules[] = { "mod_dir.c", NULL };
+
+ /* Need to make sure we don't get directory listings by accident */
+ ap_hook_handler(ipp_handler, NULL, modules, APR_HOOK_MIDDLE);
+ ap_hook_default_port(ipp_port, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_http_method(ipp_method, NULL, NULL, APR_HOOK_MIDDLE);
+}
+
+module AP_MODULE_DECLARE_DATA ipp_module = {
+ STANDARD20_MODULE_STUFF,
+ create_ipp_dir_config, /* create per-dir config */
+ NULL, /* merge per-dir config */
+ NULL, /* create per-server config */
+ NULL, /* merge per-server config */
+ ipp_cmds, /* table of config commands */
+ ipp_register_hooks /* register hooks */
+};
+
+#else /* Apache 1.X */
+
+/* Dispatch list of content handlers */
+static const handler_rec ipp_handlers[] = {
+ /*
+ * This handler association causes all IPP request with the
+ * correct MIME type to call the protocol handler.
+ */
+ { "application/ipp", ipp_handler },
+ /*
+ * This hander association is causes everything to go through the IPP
+ * protocol request handler. This is necessary because client POST
+ * request may be for something outside of the normal printer-uri
+ * space.
+ */
+ { "*/*", ipp_handler },
+
+ { NULL, NULL }
+};
+
+
+module MODULE_VAR_EXPORT ipp_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* module initializer */
+ create_ipp_dir_config, /* create per-dir config structures */
+ NULL, /* merge per-dir config structures */
+ NULL, /* create per-server config structures */
+ NULL, /* merge per-server config structures */
+ ipp_cmds, /* table of config file commands */
+ ipp_handlers, /* [#8] MIME-typed-dispatched handlers */
+ NULL, /* [#1] URI to filename translation */
+ NULL, /* [#4] validate user id from request */
+ NULL, /* [#5] check if the user is ok _here_ */
+ NULL, /* [#3] check access by host address */
+ NULL, /* [#6] determine MIME type */
+ NULL, /* [#7] pre-run fixups */
+ NULL, /* [#9] log a transaction */
+ NULL, /* [#2] header parser */
+ NULL, /* child_init */
+ NULL, /* child_exit */
+ NULL /* [#0] post read-request */
+};
+#endif