diff options
Diffstat (limited to 'usr/src/lib/print/mod_ipp/mod_ipp.c')
-rw-r--r-- | usr/src/lib/print/mod_ipp/mod_ipp.c | 552 |
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 |