/*==================================================================== * Copyright (c) 1995-2001 The Apache Group. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * 3. All advertising materials mentioning features or use of this * software must display the following acknowledgment: * "This product includes software developed by the Apache Group * for use in the Apache HTTP server project (http://www.apache.org/)." * * 4. The names "Apache Server" and "Apache Group" must not be used to * endorse or promote products derived from this software without * prior written permission. * * 5. Redistributions of any form whatsoever must retain the following * acknowledgment: * "This product includes software developed by the Apache Group * for use in the Apache HTTP server project (http://www.apache.org/)." * * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED * OF THE POSSIBILITY OF SUCH DAMAGE. * ==================================================================== * * This software consists of voluntary contributions made by many * individuals on behalf of the Apache Group and was originally based * on public domain software written at the National Center for * Supercomputing Applications, University of Illinois, Urbana-Champaign. * For more information on the Apache Group and the Apache HTTP server * project, please see . */ static char const *rcsid = "$Id: mod_meta_package.c,v 1.1 2001/10/23 02:03:18 eric Exp $"; /* * mod_meta_package: HTTP Extensions [1] module to package metadata with body * * Eric Prud'hommeaux * * Jul 28 2001: started * * [1] http://www.w3.org/Protocols/HTTP/ietf-http-ext/ */ #include "apr_strings.h" #include "apr_lib.h" #include "apr_hash.h" #include "ap_config.h" #include "httpd.h" #include "http_request.h" #include "http_config.h" #include "http_core.h" #include "http_protocol.h" #include "http_log.h" #include "scoreboard.h" #include "http_ext.h" #include "meta_package.h" #include #include "apr_buckets.h" #include "util_ebcdic.h" #if APR_HAVE_UNISTD_H #include /* Porting issue: included for getpid */ #endif /* internal constants */ #define METAPACK_URI "http://www.w3.org/2001/07/07-MetaPackage/" #define REQ_CONTEXT_KEY METAPACK_URI"#mod_meta_package/request-context" /* ENABLE_DEBUG_LOGGING - compile in a small amount of extra code to handle * the logALot directive and, surprisingly, log a lot when it's set. * * 1 - add the code * 0 - don't add the code */ #define ENABLE_DEBUG_LOGGING 0 #if ENABLE_DEBUG_LOGGING #define LOGRETURN(WHAT, REQ, WHERE, WHY) return _logReturn(WHAT, REQ, WHERE, WHY) #else #define LOGRETURN(WHAT, REQ, WHERE, WHY) return (WHAT) #endif const char * const AclAccessNames[] = { "chacl", /* 0x001 */ "racl", /* 0x002 */ "invalid", /* 0x004 */ "invalid", /* 0x008 */ "head", /* 0x010 */ "get", /* 0x020 */ "put", /* 0x040 */ "post", /* 0x080 */ "delete", /* 0x100 */ "connect", /* 0x200 */ "options", /* 0x400 */ "trace" /* 0x800 */ }; /* The request context goes into r->pool's user data. */ typedef struct metaPack_reqContext_struct { apr_array_header_t *todo; void * httpExt_context; } metaPack_reqContext_t; typedef struct metaPack_directory_struct { /* debugging */ char *dir; } metaPack_directory_rec; typedef struct metaPack_server_struct { #if ENABLE_DEBUG_LOGGING int logALot; #endif } metaPack_server_rec; apr_hash_t * MetaHandlers; static void metaPack_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s); static apr_status_t metaPack_addMeta(ap_filter_t *f, apr_bucket_brigade *b); static void * metaPack_dir_config (apr_pool_t *p, char *d); static void * metaPack_dir_merge (apr_pool_t *p, void *basev, void *overridesv); static void * metaPack_server_config (apr_pool_t *p, server_rec *s); static void * metaPack_server_merge (apr_pool_t *p, void *basev, void *overridesv); static metaPack_ACLquery_t _queryACLData; static const command_rec metaPack_cmds[]; static void metaPack_register_hooks (apr_pool_t *p) { static const char * const listOfOne[]={ "mod_http_ext.c", NULL }; MetaHandlers = apr_hash_make(p); metaPack_register_query(p, "ACLs", (void *)&_queryACLData); ap_hook_post_config(metaPack_post_config, listOfOne, NULL, APR_HOOK_MIDDLE); ap_register_output_filter("metaPack_addMeta", metaPack_addMeta, AP_FTYPE_CONTENT); } module AP_MODULE_DECLARE_DATA meta_package_module = { STANDARD20_MODULE_STUFF, metaPack_dir_config, /* dir config creater */ metaPack_dir_merge, /* dir merger --- default is to override */ metaPack_server_config, /* server config */ metaPack_server_merge, /* merge server config */ NULL, /* command table */ metaPack_register_hooks /* register hooks */ }; #if ENABLE_DEBUG_LOGGING static int _logReturn (int what, request_rec *r, const char * const where, const char * const why) { metaPack_server_rec *srvRec = (metaPack_server_rec *)ap_get_module_config(r->server->module_config, &metaPack_module); if (srvRec->logALot > 0) { ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 999, r->server, "%s returning %d (%s) on %s %s", where, what, why, r->connection->remote_ip, r->the_request); } return what; } #endif /* SERVER AND DIRECTORY CONFIGURATIONS */ static void * metaPack_dir_config (apr_pool_t *p, char *d) { metaPack_directory_rec *dirRec = (metaPack_directory_rec *) apr_pcalloc (p, sizeof(metaPack_directory_rec)); /* debugging */ dirRec->dir = apr_pstrdup(p, d); return dirRec; } static void * metaPack_dir_merge (apr_pool_t *p, void *basev, void *overridesv) { metaPack_directory_rec *merged, *base, *overrides; merged = (metaPack_directory_rec *)apr_pcalloc(p, sizeof(metaPack_directory_rec)); base = (metaPack_directory_rec *)basev; overrides = (metaPack_directory_rec *)overridesv; merged->dir = apr_pstrcat(p, "(", base->dir == NULL ? "" : base->dir, ") -> (", overrides->dir, ")", NULL); return (void *)merged; } static void * metaPack_server_config (apr_pool_t *p, server_rec *s) { metaPack_server_rec *srvRec = (metaPack_server_rec *)apr_pcalloc(p, sizeof(metaPack_server_rec)); #if ENABLE_DEBUG_LOGGING srvRec->logALot = -1; #endif return srvRec; } static void * metaPack_server_merge (apr_pool_t *p, void *basev, void *overridesv) { metaPack_server_rec *merged, *base, *overrides; merged = (metaPack_server_rec *)apr_pcalloc(p, sizeof(metaPack_server_rec)); base = (metaPack_server_rec *)basev; overrides = (metaPack_server_rec *)overridesv; #if ENABLE_DEBUG_LOGGING merged->logALot = overrides->logALot == -1 ? base->logALot : overrides->logALot; #endif return merged; } /* COMMAND HANDLERS */ /* command handler definitions */ /* SUPPORT FUNCTIONS */ /* AUTH MODULE CALLBACKS */ /* API functions for the module. */ static httpExt_notify_t metaPack_notify; static httpExt_error_code metaPack_notify (request_rec *r, const char * const uri, apr_table_t * headersIn, apr_table_t * declExts) { apr_array_header_t *hdrs_arr; apr_table_entry_t *hdrs; void * httpExt_context; metaPack_reqContext_t *ctx; int i; ctx = (metaPack_reqContext_t *)apr_pcalloc(r->pool, sizeof(metaPack_reqContext_t)); if (apr_pool_userdata_set(ctx, REQ_CONTEXT_KEY, apr_pool_cleanup_null, r->pool) != APR_SUCCESS) return HTTP_EXT_FAIL; hdrs_arr = apr_table_elts(headersIn); hdrs = (apr_table_entry_t *) hdrs_arr->elts; for (i = 0; i < hdrs_arr->nelts; ++i) { char * name = apr_pstrdup(r->pool, hdrs[i].key); char * value = apr_pstrdup(r->pool, hdrs[i].val); if (tolower(name[0]) == 'a' && !strcmp(name+1, "ccept")) { if (strcmp(value, "http://www.w3.org/1999/02/22-rdf-syntax-ns#")) return httpExt_parameter_error(r, name, value); } else if (tolower(name[0]) == 'a' && !strcmp(name+1, "ction")) { apr_array_header_t * todo = apr_array_make(r->pool, 2, sizeof(char **)); if (strncmp(value, "GET-META", 8)) /* We don't support PUT-META yet. */ return httpExt_parameter_error(r, name, value); value += 8; while (apr_isspace(*value)) ++value; ctx->todo = todo; if (*value) { int done = 0; if (*value++ != '(') return httpExt_parameter_error(r, name, value); while (!done) { char * start; char ** newEl; while (apr_isspace(*value)) ++value; start = value; while (*value != ' ' && *value != ')') ++value; if (*value == ')') done = 1; *value++ = 0; newEl = apr_array_push(todo); *newEl = start; } } } else if (tolower(name[0]) == 'c' && !strcmp(name+1, "ontent-type")) { } else if (tolower(name[0]) == 'c' && !strcmp(name+1, "ontent-length")) { } else { return httpExt_header_error(r, name, value); } } i = httpExt_register_response(r, METAPACK_URI, HTTP_EXT_EXCLUSIVE, 1, 0, NULL, NULL, &httpExt_context); if (i == HTTP_EXT_OK) { ctx->httpExt_context = httpExt_context; ap_add_output_filter("metaPack_addMeta", NULL, r, r->connection); } return HTTP_EXT_OK; } static void metaPack_post_config (apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp, server_rec *s) { httpExt_register_extension(p, METAPACK_URI, &metaPack_notify, HTTP_EXT_EXCLUSIVE); } static metaPack_generator_t _rdfAclsGenerator; /* * * * * * * */ int _rdfAclsGenerator (apr_bucket_brigade *b, apr_off_t *pLength, request_rec *r, aclType_t type, const char * const name, aclAccess_t access, const char * const uri) { const char * typeStr, * nameStr; aclAccess_t accBit; int i; switch (type) { case aclType_user: typeStr = "user"; nameStr = name; break; case aclType_ip: typeStr = "ip"; nameStr = name; break; case aclType_group: typeStr = "group"; nameStr = name; break; case aclType_all: typeStr = "all"; nameStr = "all"; break; case aclType_none: typeStr = "none"; nameStr = "none"; break; case aclType_known: typeStr = "known"; nameStr = "known"; break; default: return HTTP_INTERNAL_SERVER_ERROR; } /* accessor */ *pLength += apr_brigade_puts(b, NULL, NULL, " \n \n"); /* access permissions */ for (accBit = 1, i = 0; accBit && i < sizeof(AclAccessNames); accBit <<= 1, i++) { if (access & accBit) { *pLength += apr_brigade_puts(b, NULL, NULL, " \n"); } } /* access to */ *pLength += apr_brigade_puts(b, NULL, NULL, " \n \n"); return OK; } static int _queryACLData(apr_bucket_brigade *b, apr_off_t *pLength, request_rec * r, void * context, metaPack_generator_t * generator) { int m = r->method_number; register int x; const char *t, *w; const apr_array_header_t *reqs_arr = ap_requires(r); require_line *reqs; int ret; const aclAccess_t GET_ACCESS = aclAccess_racl|aclAccess_head|aclAccess_get|aclAccess_connect|aclAccess_options|aclAccess_trace; if (!reqs_arr) return (*generator)(b, pLength, r, aclType_all, NULL, GET_ACCESS, r->uri); reqs = (require_line *) reqs_arr->elts; for (x = 0; x < reqs_arr->nelts; x++) { if (!(reqs[x].method_mask & (1 << m))) continue; t = reqs[x].requirement; w = ap_getword_white(r->pool, &t); if (!strcmp(w, "valid-user")) if ((ret = (*generator)(b, pLength, r, aclType_known, NULL, GET_ACCESS, r->uri)) != OK) return ret; if (!strcmp(w, "user")) { while (t[0]) { w = ap_getword_conf(r->pool, &t); if ((ret = (*generator)(b, pLength, r, aclType_user, w, GET_ACCESS, r->uri)) != OK) return ret; } } else if (!strcmp(w, "group")) { while (t[0]) { w = ap_getword_conf(r->pool, &t); if ((ret = (*generator)(b, pLength, r, aclType_group, w, GET_ACCESS, r->uri)) != OK) return ret; } } else { ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, 0, r, "ACLs package for %s failed, reason: unknown require directive:" "\"%s\"", r->uri, reqs[x].requirement); } } return OK; } static apr_status_t _createMetaData(apr_bucket_brigade *b, apr_off_t *pLength, request_rec * r, metaPack_reqContext_t * context) { char ** names; int i, ret; *pLength += apr_brigade_puts(b, NULL, NULL, "\n\n"); names = (char **)context->todo->elts; for (i = 0; i < context->todo->nelts; ++i) { char * name = names[i]; apr_array_header_t * queries = apr_hash_get(MetaHandlers, name, APR_HASH_KEY_STRING); int handled = 0; if (queries != NULL) { metaPack_ACLquery_t ** queryFuncs = (metaPack_ACLquery_t **)queries->elts; int j; for (j = queries->nelts - 1; j >= 0 ; --j) { metaPack_ACLquery_t * f = (metaPack_ACLquery_t *)queryFuncs[j]; if (f != NULL) { ret = (*f)(b, pLength, r, NULL, &_rdfAclsGenerator); switch (ret) { case DECLINED: break; case OK: handled = 1; break; default: handled = 1; ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, ret, r->server, "metaPack: %s hander wants to return %d", name, ret); } } } } if (handled == 0) { *pLength += apr_brigade_puts(b, NULL, NULL, " \n "); *pLength += apr_brigade_puts(b, NULL, NULL, name); *pLength += apr_brigade_puts(b, NULL, NULL, "\n \n"); } } *pLength += apr_brigade_puts(b, NULL, NULL, "\n"); return APR_SUCCESS; } struct metaPack_filter_ctx { apr_bucket_brigade *saved; apr_size_t payloadLength; }; static apr_status_t metaPack_addMeta(ap_filter_t *f, apr_bucket_brigade *bPayload) { request_rec *r = f->r; struct metaPack_filter_ctx * ctxFilter; metaPack_reqContext_t * context; char * ctype; apr_off_t metaDataLength = 0; apr_off_t boundryLength; apr_off_t boundryLengths = 0; apr_bucket *e; apr_bucket *eos; apr_bucket_brigade *bMetaData; int alreadyMultipart; char *tmp; apr_pool_userdata_get((void **)&context, REQ_CONTEXT_KEY, r->pool); /* Early return for a variety of reasons. */ if (context->todo == NULL || !ap_exists_scoreboard_image() || r->method_number != M_GET || r->header_only || r->status != 200 || (bMetaData = apr_brigade_create(bPayload->p)) == NULL || _createMetaData(bMetaData, &metaDataLength, r, context) != APR_SUCCESS) return ap_pass_brigade(f->next, bPayload); ctxFilter = (struct metaPack_filter_ctx *)f->ctx; if (!ctxFilter) { /* first time through */ f->ctx = ctxFilter = apr_pcalloc(r->pool, sizeof(struct metaPack_filter_ctx)); } /* Current message type and length. */ alreadyMultipart = (r->boundary != NULL); ctype = (char *)r->content_type; if (r->clength) { ctxFilter->payloadLength = r->clength; } else { /* We have to calculate the length. */ APR_BRIGADE_FOREACH(e, bPayload) { const char *ignored; apr_size_t length; apr_status_t rv; if (e->length == -1) { /* if length unknown */ rv = apr_bucket_read(e, &ignored, &length, APR_BLOCK_READ); if (rv != APR_SUCCESS) { return rv; } } else { length = e->length; } ctxFilter->payloadLength += length; } } /* See if we are at the end of the stream. */ eos = APR_BRIGADE_LAST(bPayload); if (APR_BUCKET_IS_EOS(eos)) { APR_BUCKET_REMOVE(eos); } else { return ap_save_brigade(f, &ctxFilter->saved, &bPayload); } /* Manufacture new multipart boundry. */ if (!alreadyMultipart) r->boundary = apr_psprintf(r->pool, "%qx%lx", r->request_time, (long) getpid()); /* Pass existing Content-Type and maybe even mime boundry. */ /* apr_table_setn(r->headers_out, "Content-Type", */ r->content_type = apr_pstrcat(r->pool, "multipart/mixed; boundary=", r->boundary, NULL); /* Build a new brigade with a headers, metadata, another mime header, the old content, and a mime trailer, only build it in reverse order by prepending the lead stuff in front of the current brigade. */ /* Prepend the multipart header for the metadata. */ tmp = apr_pstrcat(r->pool, "--", r->boundary, CRLF "Content-type: ", ap_make_content_type(r, "text/xml"), CRLF "Content-length: ", apr_psprintf(r->pool, "%" APR_OFF_T_FMT, metaDataLength), CRLF CRLF, NULL); boundryLength = strlen(tmp); boundryLengths += boundryLength; ap_xlate_proto_to_ascii(tmp, boundryLength); e = apr_bucket_pool_create(tmp, boundryLength, r->pool); APR_BRIGADE_INSERT_HEAD(bMetaData, e); /* Maybe prepend the payload multipart header. */ if (!alreadyMultipart) { tmp = apr_pstrcat(r->pool, CRLF "--", r->boundary, CRLF "Content-type: ", ap_make_content_type(r, ctype), CRLF "Content-length: ", apr_psprintf(r->pool, "%" APR_OFF_T_FMT, (apr_off_t)ctxFilter->payloadLength), CRLF CRLF, NULL); boundryLength = strlen(tmp); boundryLengths += boundryLength; ap_xlate_proto_to_ascii(tmp, boundryLength); e = apr_bucket_pool_create(tmp, boundryLength, r->pool); APR_BRIGADE_INSERT_TAIL(bMetaData, e); } /* Prepend the metadata. */ APR_BRIGADE_CONCAT(bMetaData, bPayload); /* Add the final boundary */ tmp = apr_pstrcat(r->pool, CRLF "--", r->boundary, "--" CRLF, NULL); boundryLength = strlen(tmp); boundryLengths += boundryLength; ap_xlate_proto_to_ascii(tmp, boundryLength); e = apr_bucket_pool_create(tmp, boundryLength, r->pool); APR_BRIGADE_INSERT_TAIL(bMetaData, e); APR_BRIGADE_INSERT_TAIL(bMetaData, eos); /* We're done with the original content */ apr_brigade_destroy(bPayload); /* Set up some HTTP Extension headers. */ httpExt_set_header_out(context->httpExt_context, r, "Package-Language", "http://www.w3.org/1999/02/22-rdf-syntax-ns#"); if (ctype) httpExt_set_header_out(context->httpExt_context, r, "Content-Type", ctype); httpExt_set_header_out(context->httpExt_context, r, "Content-Length", apr_psprintf(r->pool, "%" APR_OFF_T_FMT, (apr_off_t)ctxFilter->payloadLength)); ap_set_content_length(r, metaDataLength + ctxFilter->payloadLength + boundryLengths); /* send our multipart output */ return ap_pass_brigade(f->next, bMetaData); } void metaPack_register_query (apr_pool_t *p, const char * const key, metaPack_ACLquery_t * queryFunc) { metaPack_ACLquery_t ** newEl; apr_array_header_t * queries = apr_hash_get(MetaHandlers, key, APR_HASH_KEY_STRING); if (queries == NULL) { queries = apr_array_make(p, 3, sizeof(metaPack_ACLquery_t *)); apr_hash_set(MetaHandlers, key, APR_HASH_KEY_STRING, (void *)queries); } newEl = apr_array_push(queries); *newEl = queryFunc; }