/*====================================================================
* 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;
}