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 2006 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 *
26 */
27
28 /* $Id: mod_ipp.c 149 2006-04-25 16:55:01Z njacobs $ */
29
30 #pragma ident "%Z%%M% %I% %E% SMI"
31
32 /*
33 * Internet Printing Protocol (IPP) module for Apache.
34 */
35
36 #include "ap_config.h"
37
38 #include <stdio.h>
39 #include <time.h>
40 #include <sys/time.h>
41 #include <values.h>
42 #include <libintl.h>
43 #include <alloca.h>
44
45 #include "httpd.h"
46 #include "http_config.h"
47 #include "http_core.h"
48 #include "http_protocol.h"
49 #include "http_log.h"
50 #include "http_main.h"
51 #include "papi.h"
52 #ifndef APACHE_RELEASE /* appears to only exist in Apache 1.X */
53 #define APACHE2
54 #include "apr_compat.h"
55 #endif
56
57 #include <papi.h>
58 #include <ipp-listener.h>
59
60 #ifndef APACHE2
61 module MODULE_VAR_EXPORT ipp_module;
62 #else
63 module AP_MODULE_DECLARE_DATA ipp_module;
64 #endif
65
66 #ifndef AP_INIT_TAKE1 /* Apache 2.X has this, but 1.3.X does not */
67 #define AP_INIT_NO_ARGS(directive, action, arg, where, mesg) \
68 { directive, action, arg, where, NO_ARGS, mesg }
69 #define AP_INIT_TAKE1(directive, action, arg, where, mesg) \
70 { directive, action, arg, where, TAKE1, mesg }
71 #define AP_INIT_TAKE2(directive, action, arg, where, mesg) \
72 { directive, action, arg, where, TAKE2, mesg }
73 #endif
74
75 typedef struct {
76 int conformance;
77 char *default_user;
78 char *default_svc;
79 papi_attribute_t **operations;
80 } IPPListenerConfig;
100 fprintf(fp, " ");
101 for (j = 0; j < 16 && (i + j) < bytes; j ++) {
102 ch = buffer[i + j] & 255;
103 if (ch < ' ' || ch == 127)
104 ch = '.';
105 putc(ch, fp);
106 }
107 putc('\n', fp);
108 }
109 fflush(fp);
110 }
111 #endif
112
113 static ssize_t
114 read_data(void *fd, void *buf, size_t siz)
115 {
116 ssize_t len_read;
117 request_rec *ap_r = (request_rec *)fd;
118
119 len_read = ap_get_client_block(ap_r, buf, siz);
120 #ifndef APACHE2
121 ap_reset_timeout(ap_r);
122 #endif
123
124 #ifdef DEBUG
125 fprintf(stderr, "read_data(0x%8.8x, 0x%8.8x, %d): %d",
126 fd, buf, siz, len_read);
127 if (len_read < 0)
128 fprintf(stderr, ": %s", strerror(errno));
129 putc('\n', stderr);
130 dump_buffer(stderr, "read_data:", buf, len_read);
131 #endif
132
133 return (len_read);
134 }
135
136 static ssize_t
137 write_data(void *fd, void *buf, size_t siz)
138 {
139 ssize_t len_written;
140 request_rec *ap_r = (request_rec *)fd;
141
142 #ifndef APACHE2
143 ap_reset_timeout(ap_r);
144 #endif
145 #ifdef DEBUG
146 dump_buffer(stderr, "write_data:", buf, siz);
147 #endif
148 len_written = ap_rwrite(buf, siz, ap_r);
149
150 return (len_written);
151 }
152
153 static void
154 discard_data(request_rec *r)
155 {
156 #ifdef APACHE2
157 (void) ap_discard_request_body(r);
158 #else
159 /*
160 * This is taken from ap_discard_request_body(). The reason we can't
161 * just use it in Apache 1.3 is that it does various timeout things we
162 * don't want it to do. Apache 2.0 doesn't do that, so we can safely
163 * use the normal function.
164 */
165 if (r->read_chunked || r->remaining > 0) {
166 char dumpbuf[HUGE_STRING_LEN];
167 int i;
168
169 do {
170 i = ap_get_client_block(r, dumpbuf, HUGE_STRING_LEN);
171 #ifdef DEBUG
172 dump_buffer(stderr, "discarded", dumpbuf, i);
173 #endif
174 } while (i > 0);
175 }
176 #endif
177 }
178
179 void _log_rerror(const char *file, int line, int level, request_rec *r,
180 const char *fmt, ...)
181 {
182 va_list args;
183 size_t size;
184 char *message = alloca(BUFSIZ);
185
186 va_start(args, fmt);
187 /*
188 * fill in the message. If the buffer is too small, allocate
189 * one that is large enough and fill it in.
190 */
191 if ((size = vsnprintf(message, BUFSIZ, fmt, args)) >= BUFSIZ)
192 if ((message = alloca(size)) != NULL)
193 vsnprintf(message, size, fmt, args);
194 va_end(args);
195
196 #ifdef APACHE2
197 ap_log_rerror(file, line, level, NULL, r, message);
198 #else
199 ap_log_rerror(file, line, level, r, message);
200 #endif
201 }
202
203 static int
204 ipp_handler(request_rec *r)
205 {
206 papi_attribute_t **request = NULL, **response = NULL;
207 IPPListenerConfig *config;
208 papi_status_t status;
209 int ret;
210
211 /* Really, IPP is all POST requests */
212 if (r->method_number != M_POST)
213 return (DECLINED);
214
215 #ifndef APACHE2
216 /*
217 * An IPP request must have a MIME type of "application/ipp"
218 * (RFC-2910, Section 4, page 19). If it doesn't match this
219 * MIME type, we should decline the request and let someone else
220 * try and handle it.
221 */
222 if (r->headers_in != NULL) {
223 char *mime_type = (char *)ap_table_get(r->headers_in,
224 "Content-Type");
225
226 if ((mime_type == NULL) ||
227 (strcasecmp(mime_type, "application/ipp") != 0))
228 return (DECLINED);
229 }
230 #endif
231 /* CHUNKED_DECHUNK might not work right for IPP? */
232 if ((ret = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK)) != OK)
233 return (ret);
234
235 if (!ap_should_client_block(r))
236 return (HTTP_INTERNAL_SERVER_ERROR);
237
238 #ifndef APACHE2
239 ap_soft_timeout("ipp_module: read/reply request ", r);
240 #endif
241 /* read the IPP request off the network */
242 status = ipp_read_message(read_data, r, &request, IPP_TYPE_REQUEST);
243
244 if (status != PAPI_OK)
245 _log_rerror(APLOG_MARK, APLOG_ERR, r,
246 "read failed: %s\n", papiStatusString(status));
247 #ifdef DEBUG
248 papiAttributeListPrint(stderr, request, "request (%d) ", getpid());
249 #endif
250
251 (void) papiAttributeListAddString(&request, PAPI_ATTR_EXCL,
252 "originating-host", (char *)
253 #ifdef APACHE2
254 ap_get_remote_host
255 (r->connection, r->per_dir_config, REMOTE_NAME, NULL));
256 #else
257 ap_get_remote_host
258 (r->connection, r->per_dir_config, REMOTE_NAME));
259 #endif
260
261 (void) papiAttributeListAddInteger(&request, PAPI_ATTR_EXCL,
262 "uri-port", ap_get_server_port(r));
263 if (r->headers_in != NULL) {
264 char *host = (char *)ap_table_get(r->headers_in, "Host");
265
266 if ((host == NULL) || (host[0] == '\0'))
267 host = (char *)ap_get_server_name(r);
268
269 (void) papiAttributeListAddString(&request, PAPI_ATTR_EXCL,
270 "uri-host", host);
271 }
272 (void) papiAttributeListAddString(&request, PAPI_ATTR_EXCL,
273 "uri-path", r->uri);
274
275 config = ap_get_module_config(r->per_dir_config, &ipp_module);
276 if (config != NULL) {
277 (void) papiAttributeListAddInteger(&request, PAPI_ATTR_EXCL,
278 "conformance", config->conformance);
279 (void) papiAttributeListAddCollection(&request, PAPI_ATTR_EXCL,
280 "operations", config->operations);
281 if (config->default_user != NULL)
282 (void) papiAttributeListAddString(&request,
283 PAPI_ATTR_EXCL, "default-user",
284 config->default_user);
285 if (config->default_svc != NULL)
286 (void) papiAttributeListAddString(&request,
287 PAPI_ATTR_EXCL, "default-service",
288 config->default_svc);
289 }
290
291 /*
292 * For Trusted Solaris, pass the fd number of the socket connection
293 * to the backend so the it can be forwarded to the backend print
294 * service to retrieve the sensativity label off of a multi-level
295 * port.
296 */
297 (void) papiAttributeListAddInteger(&request, PAPI_ATTR_EXCL,
298 "peer-socket", ap_bfileno(r->connection->client, B_RD));
299
300 /* process the request */
301 status = ipp_process_request(request, &response, read_data, r);
302 if (status != PAPI_OK) {
303 errno = 0;
304 _log_rerror(APLOG_MARK, APLOG_ERR, r,
305 "request failed: %s\n", papiStatusString(status));
306 discard_data(r);
307 }
308 #ifdef DEBUG
309 fprintf(stderr, "processing result: %s\n", papiStatusString(status));
310 papiAttributeListPrint(stderr, response, "response (%d) ", getpid());
311 #endif
312
313 /*
314 * If the client is using chunking and we have not yet received the
315 * final "0" sized chunk, we need to discard any data that may
316 * remain in the post request.
317 */
318 if ((r->read_chunked != 0) &&
319 (ap_table_get(r->headers_in, "Content-Length") == NULL))
320 discard_data(r);
321
322 /* write an IPP response back to the network */
323 r->content_type = "application/ipp";
324
325 #ifndef APACHE2
326 ap_send_http_header(r);
327 #endif
328
329 status = ipp_write_message(write_data, r, response);
330 if (status != PAPI_OK)
331 _log_rerror(APLOG_MARK, APLOG_ERR, r,
332 "write failed: %s\n", papiStatusString(status));
333 #ifdef DEBUG
334 fprintf(stderr, "write result: %s\n", papiStatusString(status));
335 fflush(stderr);
336 #endif
337
338 papiAttributeListFree(request);
339 papiAttributeListFree(response);
340
341 #ifndef APACHE2
342 ap_kill_timeout(r);
343 if (ap_rflush(r) < 0)
344 _log_rerror(APLOG_MARK, APLOG_ERR, r,
345 "flush failed, response may not have been sent");
346 #endif
347
348 return (OK);
349 }
350
351
352 /*ARGSUSED1*/
353 static void *
354 create_ipp_dir_config(
355 #ifndef APACHE2
356 pool *p,
357 #else
358 apr_pool_t *p,
359 #endif
360 char *dirspec)
361 {
362 IPPListenerConfig *config =
363 #ifndef APACHE2
364 ap_pcalloc(p, sizeof (*config));
365 #else
366 apr_pcalloc(p, sizeof (*config));
367 #endif
368
369 if (config != NULL) {
370 (void) memset(config, 0, sizeof (*config));
371 config->conformance = IPP_PARSE_CONFORMANCE_RASH;
372 config->default_user = NULL;
373 config->default_svc = NULL;
374 (void) ipp_configure_operation(&config->operations, "required",
375 "enable");
376 }
377
378 return (config);
379 }
380
381 /*ARGSUSED0*/
382 static const char *
383 ipp_conformance(cmd_parms *cmd, void *cfg, const char *arg)
384 {
385 IPPListenerConfig *config = (IPPListenerConfig *)cfg;
386
387 if (strncasecmp(arg, "automatic", 4) == 0) {
388 config->conformance = IPP_PARSE_CONFORMANCE_RASH;
389 } else if (strcasecmp(arg, "1.0") == 0) {
390 config->conformance = IPP_PARSE_CONFORMANCE_LOOSE;
391 } else if (strcasecmp(arg, "1.1") == 0) {
392 config->conformance = IPP_PARSE_CONFORMANCE_STRICT;
393 } else {
394 return ("unknown conformance, try (automatic/1.0/1.1)");
395 }
396
397 return (NULL);
398 }
399
400 /*ARGSUSED0*/
401 static const char *
402 ipp_operation(cmd_parms *cmd, void *cfg, char *op, char *toggle)
403 {
404 IPPListenerConfig *config = (IPPListenerConfig *)cfg;
405 papi_status_t status;
406
407 status = ipp_configure_operation(&config->operations, op, toggle);
408 switch (status) {
409 case PAPI_OK:
410 return (NULL);
411 case PAPI_BAD_ARGUMENT:
412 return (gettext("internal error (invalid argument)"));
413 default:
414 return (papiStatusString(status));
415 }
416
417 /* NOTREACHED */
418 /* return (gettext("contact your software vendor")); */
419 }
420
421 static const char *
422 ipp_default_user(cmd_parms *cmd, void *cfg, const char *arg)
452 }
453 #endif /* DEBUG */
454
455 static const command_rec ipp_cmds[] =
456 {
457 AP_INIT_TAKE1("ipp-conformance", ipp_conformance, NULL, ACCESS_CONF,
458 "IPP protocol conformance (loose/strict)"),
459 AP_INIT_TAKE2("ipp-operation", ipp_operation, NULL, ACCESS_CONF,
460 "IPP protocol operations to enable/disable)"),
461 AP_INIT_TAKE1("ipp-default-user", ipp_default_user, NULL, ACCESS_CONF,
462 "default user for various operations"),
463 AP_INIT_TAKE1("ipp-default-service", ipp_default_svc, NULL, ACCESS_CONF,
464 "default service for various operations"),
465 #ifdef DEBUG
466 AP_INIT_NO_ARGS("ipp-module-hang", ipp_module_hang, NULL, ACCESS_CONF,
467 "hang the module until we can attach a debugger (no args)"),
468 #endif
469 { NULL }
470 };
471
472 #ifdef APACHE2
473 /*ARGSUSED0*/
474 static const char *
475 ipp_method(const request_rec *r)
476 {
477 return ("ipp");
478 }
479
480 /*ARGSUSED0*/
481 static unsigned short
482 ipp_port(const request_rec *r)
483 {
484 return (631);
485 }
486
487 /* Dispatch list for API hooks */
488 /*ARGSUSED0*/
489 static void
490 ipp_register_hooks(apr_pool_t *p)
491 {
492 static const char * const modules[] = { "mod_dir.c", NULL };
493
494 /* Need to make sure we don't get directory listings by accident */
495 ap_hook_handler(ipp_handler, NULL, modules, APR_HOOK_MIDDLE);
496 ap_hook_default_port(ipp_port, NULL, NULL, APR_HOOK_MIDDLE);
497 ap_hook_http_method(ipp_method, NULL, NULL, APR_HOOK_MIDDLE);
498 }
499
500 module AP_MODULE_DECLARE_DATA ipp_module = {
501 STANDARD20_MODULE_STUFF,
502 create_ipp_dir_config, /* create per-dir config */
503 NULL, /* merge per-dir config */
504 NULL, /* create per-server config */
505 NULL, /* merge per-server config */
506 ipp_cmds, /* table of config commands */
507 ipp_register_hooks /* register hooks */
508 };
509
510 #else /* Apache 1.X */
511
512 /* Dispatch list of content handlers */
513 static const handler_rec ipp_handlers[] = {
514 /*
515 * This handler association causes all IPP request with the
516 * correct MIME type to call the protocol handler.
517 */
|
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 2006 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
25 *
26 */
27 /*
28 * Copyright (c) 2013 Andrew Stormont. All rights reserved.
29 */
30
31 /* $Id: mod_ipp.c 149 2006-04-25 16:55:01Z njacobs $ */
32
33 /*
34 * Internet Printing Protocol (IPP) module for Apache.
35 */
36
37 #include "ap_config.h"
38
39 #include <stdio.h>
40 #include <time.h>
41 #include <sys/time.h>
42 #include <values.h>
43 #include <libintl.h>
44 #include <alloca.h>
45
46 #include "httpd.h"
47 #include "http_config.h"
48 #include "http_core.h"
49 #include "http_protocol.h"
50 #include "http_log.h"
51 #include "http_main.h"
52 #include "papi.h"
53 /* Try and guess the version of apache */
54 #ifdef APACHE_RELEASE
55 # define AP_SERVER_MAJORVERSION_NUMBER 1
56 # define AP_SERVER_MINORVERSION_NUMBER 0
57 #else
58 # ifndef AP_SERVER_MAJORVERSION_NUMBER
59 # define AP_SERVER_MAJORVERSION_NUMBER 2
60 # endif
61 # ifndef AP_SERVER_MINORVERSION_NUMBER
62 # define AP_SERVER_MINORVERSION_NUMBER 0
63 # endif
64 #endif
65
66 #include <papi.h>
67 #include <ipp-listener.h>
68
69 #if AP_SERVER_MAJORVERSION_NUMBER < 2
70 module MODULE_VAR_EXPORT ipp_module;
71 #else
72 module AP_MODULE_DECLARE_DATA ipp_module;
73 #endif
74
75 #ifndef AP_INIT_TAKE1 /* Apache 2.X has this, but 1.3.X does not */
76 #define AP_INIT_NO_ARGS(directive, action, arg, where, mesg) \
77 { directive, action, arg, where, NO_ARGS, mesg }
78 #define AP_INIT_TAKE1(directive, action, arg, where, mesg) \
79 { directive, action, arg, where, TAKE1, mesg }
80 #define AP_INIT_TAKE2(directive, action, arg, where, mesg) \
81 { directive, action, arg, where, TAKE2, mesg }
82 #endif
83
84 typedef struct {
85 int conformance;
86 char *default_user;
87 char *default_svc;
88 papi_attribute_t **operations;
89 } IPPListenerConfig;
109 fprintf(fp, " ");
110 for (j = 0; j < 16 && (i + j) < bytes; j ++) {
111 ch = buffer[i + j] & 255;
112 if (ch < ' ' || ch == 127)
113 ch = '.';
114 putc(ch, fp);
115 }
116 putc('\n', fp);
117 }
118 fflush(fp);
119 }
120 #endif
121
122 static ssize_t
123 read_data(void *fd, void *buf, size_t siz)
124 {
125 ssize_t len_read;
126 request_rec *ap_r = (request_rec *)fd;
127
128 len_read = ap_get_client_block(ap_r, buf, siz);
129 #if AP_SERVER_MAJORVERSION_NUMBER < 2
130 ap_reset_timeout(ap_r);
131 #endif
132
133 #ifdef DEBUG
134 fprintf(stderr, "read_data(0x%8.8x, 0x%8.8x, %d): %d",
135 fd, buf, siz, len_read);
136 if (len_read < 0)
137 fprintf(stderr, ": %s", strerror(errno));
138 putc('\n', stderr);
139 dump_buffer(stderr, "read_data:", buf, len_read);
140 #endif
141
142 return (len_read);
143 }
144
145 static ssize_t
146 write_data(void *fd, void *buf, size_t siz)
147 {
148 ssize_t len_written;
149 request_rec *ap_r = (request_rec *)fd;
150
151 #if AP_SERVER_MAJORVERSION_NUMBER < 2
152 ap_reset_timeout(ap_r);
153 #endif
154 #ifdef DEBUG
155 dump_buffer(stderr, "write_data:", buf, siz);
156 #endif
157 len_written = ap_rwrite(buf, siz, ap_r);
158
159 return (len_written);
160 }
161
162 static void
163 discard_data(request_rec *r)
164 {
165 #if AP_SERVER_MAJORVERSION_NUMBER < 2
166 (void) ap_discard_request_body(r);
167 #else
168 /*
169 * This is taken from ap_discard_request_body(). The reason we can't
170 * just use it in Apache 1.3 is that it does various timeout things we
171 * don't want it to do. Apache 2.0 doesn't do that, so we can safely
172 * use the normal function.
173 */
174 if (r->read_chunked || r->remaining > 0) {
175 char dumpbuf[HUGE_STRING_LEN];
176 int i;
177
178 do {
179 i = ap_get_client_block(r, dumpbuf, HUGE_STRING_LEN);
180 #ifdef DEBUG
181 dump_buffer(stderr, "discarded", dumpbuf, i);
182 #endif
183 } while (i > 0);
184 }
185 #endif
186 }
187
188 #if AP_SERVER_MAJORVERSION_NUMBER > 2 || AP_SERVER_MINORVERSION_NUMBER > 3
189 void _log_rerror(const char *file, int line, int module_index,
190 int level, request_rec *r, const char *fmt, ...)
191 #else
192 void _log_rerror(const char *file, int line, int level, request_rec *r,
193 const char *fmt, ...)
194 #endif
195 {
196 va_list args;
197 size_t size;
198 char *message = alloca(BUFSIZ);
199
200 va_start(args, fmt);
201 /*
202 * fill in the message. If the buffer is too small, allocate
203 * one that is large enough and fill it in.
204 */
205 if ((size = vsnprintf(message, BUFSIZ, fmt, args)) >= BUFSIZ)
206 if ((message = alloca(size)) != NULL)
207 vsnprintf(message, size, fmt, args);
208 va_end(args);
209
210 #if AP_SERVER_MAJORVERSION_NUMBER > 2 || AP_SERVER_MINORVERSION_NUMBER > 3
211 ap_log_rerror(file, line, module_index, level, APR_SUCCESS, r, message);
212 #elif AP_SERVER_MAJORVERSION_NUMBER > 1
213 ap_log_rerror(file, line, level, NULL, r, message);
214 #else
215 ap_log_rerror(file, line, level, r, message);
216 #endif
217 }
218
219 static int
220 ipp_handler(request_rec *r)
221 {
222 papi_attribute_t **request = NULL, **response = NULL;
223 IPPListenerConfig *config;
224 papi_status_t status;
225 int ret;
226 #if AP_SERVER_MAJORVERSION_NUMBER > 1
227 apr_os_sock_t *os_sock = NULL;
228 apr_status_t st;
229 #endif
230
231 /* Really, IPP is all POST requests */
232 if (r->method_number != M_POST)
233 return (DECLINED);
234
235 #if AP_SERVER_MAJORVERSION_NUMBER < 2
236 /*
237 * An IPP request must have a MIME type of "application/ipp"
238 * (RFC-2910, Section 4, page 19). If it doesn't match this
239 * MIME type, we should decline the request and let someone else
240 * try and handle it.
241 */
242 if (r->headers_in != NULL) {
243 char *mime_type = (char *)ap_table_get(r->headers_in,
244 "Content-Type");
245
246 if ((mime_type == NULL) ||
247 (strcasecmp(mime_type, "application/ipp") != 0))
248 return (DECLINED);
249 }
250 #endif
251 /* CHUNKED_DECHUNK might not work right for IPP? */
252 if ((ret = ap_setup_client_block(r, REQUEST_CHUNKED_DECHUNK)) != OK)
253 return (ret);
254
255 if (!ap_should_client_block(r))
256 return (HTTP_INTERNAL_SERVER_ERROR);
257
258 #if AP_SERVER_MAJORVERSION_NUMBER < 2
259 ap_soft_timeout("ipp_module: read/reply request ", r);
260 #endif
261 /* read the IPP request off the network */
262 status = ipp_read_message(read_data, r, &request, IPP_TYPE_REQUEST);
263
264 if (status != PAPI_OK)
265 _log_rerror(APLOG_MARK, APLOG_ERR, r,
266 "read failed: %s\n", papiStatusString(status));
267 #ifdef DEBUG
268 papiAttributeListPrint(stderr, request, "request (%d) ", getpid());
269 #endif
270
271 (void) papiAttributeListAddString(&request, PAPI_ATTR_EXCL,
272 "originating-host", (char *)
273 #if AP_SERVER_MAJORVERSION_NUMBER >= 2
274 ap_get_remote_host
275 (r->connection, r->per_dir_config, REMOTE_NAME, NULL));
276 #else
277 ap_get_remote_host
278 (r->connection, r->per_dir_config, REMOTE_NAME));
279 #endif
280
281 (void) papiAttributeListAddInteger(&request, PAPI_ATTR_EXCL,
282 "uri-port", ap_get_server_port(r));
283 if (r->headers_in != NULL) {
284 #if AP_SERVER_MAJORVERSION_NUMBER >= 2 || AP_SERVER_MINORVERSION_NUMBER > 2
285 char *host = (char *)apr_table_get(r->headers_in, "Host");
286 #else
287 char *host = (char *)ap_table_get(r->headers_in, "Host");
288 #endif
289
290 if ((host == NULL) || (host[0] == '\0'))
291 host = (char *)ap_get_server_name(r);
292
293 (void) papiAttributeListAddString(&request, PAPI_ATTR_EXCL,
294 "uri-host", host);
295 }
296 (void) papiAttributeListAddString(&request, PAPI_ATTR_EXCL,
297 "uri-path", r->uri);
298
299 config = ap_get_module_config(r->per_dir_config, &ipp_module);
300 if (config != NULL) {
301 (void) papiAttributeListAddInteger(&request, PAPI_ATTR_EXCL,
302 "conformance", config->conformance);
303 (void) papiAttributeListAddCollection(&request, PAPI_ATTR_EXCL,
304 "operations", config->operations);
305 if (config->default_user != NULL)
306 (void) papiAttributeListAddString(&request,
307 PAPI_ATTR_EXCL, "default-user",
308 config->default_user);
309 if (config->default_svc != NULL)
310 (void) papiAttributeListAddString(&request,
311 PAPI_ATTR_EXCL, "default-service",
312 config->default_svc);
313 }
314
315 /*
316 * For Trusted Solaris, pass the fd number of the socket connection
317 * to the backend so the it can be forwarded to the backend print
318 * service to retrieve the sensitivity label off of a multi-level
319 * port.
320 */
321 #if AP_SERVER_MAJORVERSION_NUMBER > 1
322 st = apr_os_sock_get(os_sock, r->connection->cs->pfd.desc.s);
323 if (st == APR_SUCCESS) {
324 (void) papiAttributeListAddInteger(&request, PAPI_ATTR_EXCL,
325 "peer-socket", (int)os_sock);
326 }
327 #else
328 (void) papiAttributeListAddInteger(&request, PAPI_ATTR_EXCL,
329 "peer-socket", ap_bfileno(r->connection->client, B_RD));
330 #endif
331
332 /* process the request */
333 status = ipp_process_request(request, &response, read_data, r);
334 if (status != PAPI_OK) {
335 errno = 0;
336 _log_rerror(APLOG_MARK, APLOG_ERR, r,
337 "request failed: %s\n", papiStatusString(status));
338 discard_data(r);
339 }
340 #ifdef DEBUG
341 fprintf(stderr, "processing result: %s\n", papiStatusString(status));
342 papiAttributeListPrint(stderr, response, "response (%d) ", getpid());
343 #endif
344
345 /*
346 * If the client is using chunking and we have not yet received the
347 * final "0" sized chunk, we need to discard any data that may
348 * remain in the post request.
349 */
350 if ((r->read_chunked != 0) &&
351 #if AP_SERVER_MAJORVERSION_NUMBER >= 2 || AP_SERVER_MINORVERSION_NUMBER > 2
352 (apr_table_get(r->headers_in, "Content-Length") == NULL))
353 #else
354 (ap_table_get(r->headers_in, "Content-Length") == NULL))
355 #endif
356 discard_data(r);
357
358 /* write an IPP response back to the network */
359 r->content_type = "application/ipp";
360
361 #if AP_SERVER_MAJORVERSION_NUMBER < 2
362 ap_send_http_header(r);
363 #endif
364
365 status = ipp_write_message(write_data, r, response);
366 if (status != PAPI_OK)
367 _log_rerror(APLOG_MARK, APLOG_ERR, r,
368 "write failed: %s\n", papiStatusString(status));
369 #ifdef DEBUG
370 fprintf(stderr, "write result: %s\n", papiStatusString(status));
371 fflush(stderr);
372 #endif
373
374 papiAttributeListFree(request);
375 papiAttributeListFree(response);
376
377 #if AP_SERVER_MAJORVERSION_NUMBER < 2
378 ap_kill_timeout(r);
379 if (ap_rflush(r) < 0)
380 _log_rerror(APLOG_MARK, APLOG_ERR, r,
381 "flush failed, response may not have been sent");
382 #endif
383
384 return (OK);
385 }
386
387
388 /*ARGSUSED1*/
389 static void *
390 create_ipp_dir_config(
391 #if AP_SERVER_MAJORVERSION_NUMBER < 2
392 pool *p,
393 #else
394 apr_pool_t *p,
395 #endif
396 char *dirspec)
397 {
398 IPPListenerConfig *config =
399 #if AP_SERVER_MAJORVERSION_NUMBER < 2
400 ap_pcalloc(p, sizeof (*config));
401 #else
402 apr_pcalloc(p, sizeof (*config));
403 #endif
404
405 if (config != NULL) {
406 (void) memset(config, 0, sizeof (*config));
407 config->conformance = IPP_PARSE_CONFORMANCE_RASH;
408 config->default_user = NULL;
409 config->default_svc = NULL;
410 (void) ipp_configure_operation(&config->operations, "required",
411 "enable");
412 }
413
414 return (config);
415 }
416
417 /*ARGSUSED0*/
418 static const char *
419 ipp_conformance(cmd_parms *cmd, void *cfg, const char *arg)
420 {
421 IPPListenerConfig *config = (IPPListenerConfig *)cfg;
422
423 if (strncasecmp(arg, "automatic", 4) == 0) {
424 config->conformance = IPP_PARSE_CONFORMANCE_RASH;
425 } else if (strcasecmp(arg, "1.0") == 0) {
426 config->conformance = IPP_PARSE_CONFORMANCE_LOOSE;
427 } else if (strcasecmp(arg, "1.1") == 0) {
428 config->conformance = IPP_PARSE_CONFORMANCE_STRICT;
429 } else {
430 return ("unknown conformance, try (automatic/1.0/1.1)");
431 }
432
433 return (NULL);
434 }
435
436 /*ARGSUSED0*/
437 static const char *
438 ipp_operation(cmd_parms *cmd, void *cfg, const char *op, const char *toggle)
439 {
440 IPPListenerConfig *config = (IPPListenerConfig *)cfg;
441 papi_status_t status;
442
443 status = ipp_configure_operation(&config->operations, op, toggle);
444 switch (status) {
445 case PAPI_OK:
446 return (NULL);
447 case PAPI_BAD_ARGUMENT:
448 return (gettext("internal error (invalid argument)"));
449 default:
450 return (papiStatusString(status));
451 }
452
453 /* NOTREACHED */
454 /* return (gettext("contact your software vendor")); */
455 }
456
457 static const char *
458 ipp_default_user(cmd_parms *cmd, void *cfg, const char *arg)
488 }
489 #endif /* DEBUG */
490
491 static const command_rec ipp_cmds[] =
492 {
493 AP_INIT_TAKE1("ipp-conformance", ipp_conformance, NULL, ACCESS_CONF,
494 "IPP protocol conformance (loose/strict)"),
495 AP_INIT_TAKE2("ipp-operation", ipp_operation, NULL, ACCESS_CONF,
496 "IPP protocol operations to enable/disable)"),
497 AP_INIT_TAKE1("ipp-default-user", ipp_default_user, NULL, ACCESS_CONF,
498 "default user for various operations"),
499 AP_INIT_TAKE1("ipp-default-service", ipp_default_svc, NULL, ACCESS_CONF,
500 "default service for various operations"),
501 #ifdef DEBUG
502 AP_INIT_NO_ARGS("ipp-module-hang", ipp_module_hang, NULL, ACCESS_CONF,
503 "hang the module until we can attach a debugger (no args)"),
504 #endif
505 { NULL }
506 };
507
508 #if AP_SERVER_MAJORVERSION_NUMBER >= 2
509 /*ARGSUSED0*/
510 static const char *
511 ipp_method(const request_rec *r)
512 {
513 return ("ipp");
514 }
515
516 /*ARGSUSED0*/
517 static unsigned short
518 ipp_port(const request_rec *r)
519 {
520 return (631);
521 }
522
523 /* Dispatch list for API hooks */
524 /*ARGSUSED0*/
525 static void
526 ipp_register_hooks(apr_pool_t *p)
527 {
528 static const char * const modules[] = { "mod_dir.c", NULL };
529
530 /* Need to make sure we don't get directory listings by accident */
531 ap_hook_handler(ipp_handler, NULL, modules, APR_HOOK_MIDDLE);
532 ap_hook_default_port(ipp_port, NULL, NULL, APR_HOOK_MIDDLE);
533 #if AP_SERVER_MAJORVERSION_NUMBER >= 2
534 ap_hook_http_scheme(ipp_method, NULL, NULL, APR_HOOK_MIDDLE);
535 #else
536 ap_hook_http_method(ipp_method, NULL, NULL, APR_HOOK_MIDDLE);
537 #endif
538 }
539
540 module AP_MODULE_DECLARE_DATA ipp_module = {
541 STANDARD20_MODULE_STUFF,
542 create_ipp_dir_config, /* create per-dir config */
543 NULL, /* merge per-dir config */
544 NULL, /* create per-server config */
545 NULL, /* merge per-server config */
546 ipp_cmds, /* table of config commands */
547 ipp_register_hooks /* register hooks */
548 };
549
550 #else /* Apache 1.X */
551
552 /* Dispatch list of content handlers */
553 static const handler_rec ipp_handlers[] = {
554 /*
555 * This handler association causes all IPP request with the
556 * correct MIME type to call the protocol handler.
557 */
|