Permalink
Switch branches/tags
Nothing to show
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
645 lines (588 sloc) 21.2 KB
/*
** mod_small_light.c -- Apache sample small_light module
** [Autogenerated via ``apxs -n small_light -g'']
**
** To play with this sample module first compile it into a
** DSO file and install it into Apache's modules directory
** by running:
**
** $ apxs -c -i mod_small_light.c
**
** Then activate it in Apache's httpd.conf file for instance
** for the URL /small_light in as follows:
**
** # httpd.conf
** LoadModule small_light_module modules/mod_small_light.so
** <Location /small_light>
** SetHandler small_light
** </Location>
**
** Then after restarting Apache via
**
** $ apachectl restart
**
** you immediately can request the URL /small_light and watch for the
** output of this module. This can be achieved for instance via:
**
** $ lynx -mime_header http://localhost/small_light
**
** The output should be similar to the following one:
**
** HTTP/1.1 200 OK
** Date: Tue, 31 Mar 1998 14:42:22 GMT
** Server: Apache/1.3.4 (Unix)
** Connection: close
** Content-Type: text/html
**
** The sample page from mod_small_light.c
*/
#include "mod_small_light.h"
small_light_filter_prototype(dummy);
small_light_filter_prototype(imlib2);
small_light_filter_prototype(imagemagick);
static const char small_light_filter_name[] = "SMALL_LIGHT";
module AP_MODULE_DECLARE_DATA small_light_module;
static void *create_small_light_server_conf(apr_pool_t *p, server_rec *s)
{
small_light_server_conf_t *conf;
conf = (small_light_server_conf_t *)apr_pcalloc(p, sizeof(small_light_server_conf_t));
conf->p = p;
conf->h = apr_hash_make(conf->p);
return conf;
}
static const char *small_light_define_pattern(cmd_parms *cmd, void *ctx, const char *arg1, const char *arg2)
{
small_light_server_conf_t *sc = ap_get_module_config(
cmd->server->module_config, &small_light_module);
const char *key = arg1;
const char *value = arg2;
if (apr_hash_get(sc->h, key, APR_HASH_KEY_STRING)) {
return (char *)(size_t)apr_psprintf(cmd->pool, "SmallLightPatternDefine %s is already defined", key);
}
small_light_pattern_t *ptn;
ptn = (small_light_pattern_t *)apr_pcalloc(sc->p, sizeof(small_light_pattern_t));
ptn->name = key;
ptn->param_str = (char *)apr_pstrdup(cmd->pool, value);
apr_hash_set(sc->h, key, APR_HASH_KEY_STRING, ptn);
return NULL;
}
static small_light_pattern_t *small_light_find_pattern(request_rec *r, char *name)
{
small_light_server_conf_t *sc = ap_get_module_config(
r->server->module_config, &small_light_module);
small_light_pattern_t *ptn = apr_hash_get(sc->h, name, APR_HASH_KEY_STRING);
return ptn;
}
static int small_light_post_config(
apr_pool_t *pconf,
apr_pool_t *plog,
apr_pool_t *ptemp,
server_rec *s
)
{
#if APR_HAS_THREADS
int mpm_threads;
ap_mpm_query(AP_MPMQ_MAX_THREADS, &mpm_threads);
if (mpm_threads >= 1) {
ap_log_error(APLOG_MARK, APLOG_CRIT, 0, s,
"small_light module only works with mpm prefork mode"
);
return HTTP_INTERNAL_SERVER_ERROR;
}
#endif
return APR_SUCCESS;
}
static apr_status_t small_light_filter(ap_filter_t *f, apr_bucket_brigade *bb)
{
request_rec *r = f->r;
small_light_module_ctx_t *ctx = f->ctx;
// do nothing if bucket brigade is empty.
if (APR_BRIGADE_EMPTY(bb))
return APR_SUCCESS;
// initialize context at first time.
if (!ctx) {
// main request only.
if (r->main) {
ap_remove_output_filter(f);
return ap_pass_brigade(f->next, bb);
}
// modify redirect location if HTTP_MOVED_PERMANENTLY
// or HTTP_MOVED_TEMPORARILY or HTTP_SEE_OTHER.
if (r->status == HTTP_MOVED_PERMANENTLY ||
r->status == HTTP_MOVED_TEMPORARILY ||
r->status == HTTP_SEE_OTHER) {
char *base_uri = (char *)apr_table_get(r->headers_in, "X-SmallLight-Base-URI");
if (base_uri) {
char *loc = (char *)apr_table_get(r->headers_out, "Location");
char *sl_loc = (char *)(size_t)apr_psprintf(r->pool, "%s%s", base_uri, loc);
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Location: %s -> %s", loc, sl_loc);
apr_table_set(r->headers_out, "Location", sl_loc);
}
ap_remove_output_filter(f);
return ap_pass_brigade(f->next, bb);
}
// do nothing if not HTTP_OK.
if (r->status != HTTP_OK) {
ap_remove_output_filter(f);
return ap_pass_brigade(f->next, bb);
}
// No need to process HEAD or 204/304, by mod_deflate.
if (APR_BUCKET_IS_EOS(APR_BRIGADE_FIRST(bb))) {
ap_remove_output_filter(f);
return ap_pass_brigade(f->next, bb);
}
// initialize context.
f->ctx = ctx = (small_light_module_ctx_t *)apr_pcalloc(r->pool, sizeof(small_light_module_ctx_t));
// parse pattern or engine in uri.
ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, "uri=%s", r->uri);
int res;
char param_str[SMALL_LIGHT_PARAM_STR_MAX];
res = small_light_parse_uri_param(r, param_str, r->unparsed_uri);
if (res != OK) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "parse_uri_param failed: %s", r->unparsed_uri);
r->status = HTTP_BAD_REQUEST;
ap_remove_output_filter(f);
return ap_pass_brigade(f->next, bb);
}
ctx->prm = apr_table_make(r->pool, 10);
small_light_init_param(ctx->prm);
res = small_light_parse_param(r, ctx->prm, param_str);
if (res != OK) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "parse_param failed: %s", r->unparsed_uri);
r->status = HTTP_BAD_REQUEST;
ap_remove_output_filter(f);
return ap_pass_brigade(f->next, bb);
}
char *pattern = (char *)apr_table_get(ctx->prm, "p");
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "pattern=%s", pattern);
small_light_pattern_t *ptn = NULL;
if (pattern && pattern[0] != '\0') {
ptn = small_light_find_pattern(r, pattern);
if (!ptn) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "pattern not found: %s", param_str);
r->status = HTTP_BAD_REQUEST;
ap_remove_output_filter(f);
return ap_pass_brigade(f->next, bb);
}
apr_table_clear(ctx->prm);
small_light_init_param(ctx->prm);
small_light_parse_param(r, ctx->prm, ptn->param_str);
res = small_light_parse_param(r, ctx->prm, param_str);
if (res != OK) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "parse_param failed: %s", param_str);
r->status = HTTP_BAD_REQUEST;
ap_remove_output_filter(f);
return ap_pass_brigade(f->next, bb);
}
}
char *engine = (char *)apr_table_get(ctx->prm, "e");
if (!engine || engine[0] == '\0') {
apr_table_set(ctx->prm, "e", SMALL_LIGHT_DEFAULT_ENGINE);
}
engine = (char *)apr_table_get(ctx->prm, "e");
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "engine=%s", engine);
// set function.
if (strcmp(engine, SMALL_LIGHT_ENGINE_IMLIB2) == 0) {
ctx->init_func = (SMALL_LIGHT_FILTER_INIT)small_light_filter_imlib2_init;
ctx->receive_data_func = (SMALL_LIGHT_FILTER_RECEIVE_DATA)small_light_filter_imlib2_receive_data;
ctx->output_data_func = (SMALL_LIGHT_FILTER_OUTPUT_DATA)small_light_filter_imlib2_output_data;
} else if (strcmp(engine, SMALL_LIGHT_ENGINE_IMAGEMAGICK) == 0) {
ctx->init_func = (SMALL_LIGHT_FILTER_INIT)small_light_filter_imagemagick_init;
ctx->receive_data_func = (SMALL_LIGHT_FILTER_RECEIVE_DATA)small_light_filter_imagemagick_receive_data;
ctx->output_data_func = (SMALL_LIGHT_FILTER_OUTPUT_DATA)small_light_filter_imagemagick_output_data;
}
if (ctx->init_func == NULL) {
ctx->init_func = (SMALL_LIGHT_FILTER_INIT)small_light_filter_dummy_init;
ctx->receive_data_func = (SMALL_LIGHT_FILTER_RECEIVE_DATA)small_light_filter_dummy_receive_data;
ctx->output_data_func = (SMALL_LIGHT_FILTER_OUTPUT_DATA)small_light_filter_dummy_output_data;
}
// init filter engine.
ctx->init_func(f, bb, ctx);
// create bucket brigade.
ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc);
gettimeofday(&ctx->t, NULL);
}
// bucket brigade loop.
while (!APR_BRIGADE_EMPTY(bb))
{
apr_bucket *e;
const char *data;
apr_size_t len;
apr_status_t rv;
e = APR_BRIGADE_FIRST(bb);
if (APR_BUCKET_IS_EOS(e)) {
// finally, output data process
rv = ctx->output_data_func(f, bb, ctx, e);
if ( rv != APR_SUCCESS ) {
apr_brigade_cleanup(bb);
f->r->status_line = "500 Internal Server Error";
e = ap_bucket_error_create(HTTP_INTERNAL_SERVER_ERROR,
NULL, r->pool,
f->c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, e);
e = apr_bucket_eos_create(f->c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, e);
ap_pass_brigade(f->next, bb);
return AP_FILTER_ERROR;
}
return rv;
}
if (APR_BUCKET_IS_FLUSH(e)) {
// need to flush
APR_BUCKET_REMOVE(e);
APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
continue;
}
if (APR_BUCKET_IS_METADATA(e)) {
// remove meta data bucket
APR_BUCKET_REMOVE(e);
APR_BRIGADE_INSERT_TAIL(ctx->bb, e);
continue;
}
// read
apr_bucket_read(e, &data, &len, APR_BLOCK_READ);
// receive data process
ctx->receive_data_func(f, bb, ctx, e, data, len);
apr_bucket_delete(e);
}
apr_brigade_cleanup(bb);
return APR_SUCCESS;
}
static void small_light_register_hooks(apr_pool_t *p)
{
ap_hook_post_config(
small_light_post_config,
NULL,
NULL,
APR_HOOK_MIDDLE
);
ap_register_output_filter(
small_light_filter_name,
small_light_filter,
NULL,
AP_FTYPE_RESOURCE
);
}
static const command_rec small_light_cmds[] =
{
AP_INIT_TAKE2(
"SmallLightPatternDefine",
small_light_define_pattern,
NULL,
RSRC_CONF,
"define pattern"),
{NULL}
};
/* Dispatch list for API hooks */
module AP_MODULE_DECLARE_DATA small_light_module =
{
STANDARD20_MODULE_STUFF,
NULL, /* create per-dir config structures */
NULL, /* merge per-dir config structures */
create_small_light_server_conf, /* create per-server config structures */
NULL, /* merge per-server config structures */
small_light_cmds, /* table of config file commands */
small_light_register_hooks /* register hooks */
};
void small_light_init_param(apr_table_t *prm)
{
apr_table_set(prm, "sx", "");
apr_table_set(prm, "sy", "");
apr_table_set(prm, "sw", "");
apr_table_set(prm, "sh", "");
apr_table_set(prm, "dx", "");
apr_table_set(prm, "dy", "");
apr_table_set(prm, "dw", "");
apr_table_set(prm, "dh", "");
apr_table_set(prm, "da", "l");
apr_table_set(prm, "ds", "n");
apr_table_set(prm, "cw", "");
apr_table_set(prm, "ch", "");
apr_table_set(prm, "cc", "000000");
apr_table_set(prm, "bw", "");
apr_table_set(prm, "bh", "");
apr_table_set(prm, "bc", "000000");
apr_table_set(prm, "pt", "n");
apr_table_set(prm, "q", "0");
apr_table_set(prm, "of", "jpeg");
apr_table_set(prm, "info", "0");
apr_table_set(prm, "inhexif", "n");
apr_table_set(prm, "jpeghint", "n");
}
int small_light_parse_uri_param(request_rec *r, char *param_str, const char *uri_str)
{
// parse param string.
// if ($str =~ m#/small_light\(([^\)]*)\)#i) { my $buf = $1; }
int regsuccess;
ap_regmatch_t regmatch[2];
ap_regex_t *regex = ap_pregcomp(r->pool, "small_light\\(([^\\)]*)\\)", REG_EXTENDED | REG_ICASE | REG_NOSUB);
regsuccess = ap_regexec(regex, uri_str, 2, regmatch, 0);
ap_pregfree(r->pool, regex);
if (regsuccess != 0) {
return DECLINED;
}
if (regmatch[1].rm_so < 0) {
return DECLINED;
}
int start = regmatch[1].rm_so;
int len = regmatch[1].rm_eo - regmatch[1].rm_so;
if (len >= SMALL_LIGHT_PARAM_STR_MAX) {
return DECLINED;
}
memcpy(param_str, uri_str + start, len);
param_str[len] = '\0';
return OK;
}
int small_light_parse_param(
request_rec *r,
apr_table_t *prm,
const char *param_str)
{
// tokenize.
const char *ptr1 = param_str;
char *token;
for (;;) {
token = ap_get_token(r->pool, &ptr1, 0);
if (!(token && *token)) {
break;
}
const char *ptr2 = token, *key;
key = ap_getword(r->pool, &ptr2, '=');
apr_table_set(prm, key, ptr2);
if (!*ptr1) {
break;
}
ptr1++;
}
return OK;
}
int small_light_parse_flag(request_rec *r, const char *str)
{
if (str != NULL && str[0] == 'y') {
return 1;
}
return 0;
}
int small_light_parse_int(request_rec *r, const char *str)
{
return atoi(str);
}
double small_light_parse_double(request_rec *r, const char *str)
{
return atof(str);
}
int small_light_parse_coord(request_rec *r, small_light_coord_t *crd, const char *str)
{
if (str[0] == '\0') {
crd->v = 0;
crd->u = SMALL_LIGHT_COORD_UNIT_NONE;
return OK;
}
crd->v = atof(str);
while (((*str >= '0' && *str <= '9') || *str == '.') && *str != '\0') str++;
if (*str == 'p') {
crd->u = SMALL_LIGHT_COORD_UNIT_PERCENT;
return OK;
} else if (*str == '\0') {
crd->u = SMALL_LIGHT_COORD_UNIT_PIXEL;
return OK;
}
return DECLINED;
}
double small_light_calc_coord(small_light_coord_t *crd, double v)
{
if (crd->u == SMALL_LIGHT_COORD_UNIT_PIXEL) {
return crd->v;
} else if (crd->u == SMALL_LIGHT_COORD_UNIT_PERCENT) {
return (v * crd->v * 0.01);
}
return SMALL_LIGHT_COORD_INVALID_VALUE;
}
int small_light_parse_color(request_rec *r, small_light_color_t *color, const char *str)
{
int res;
int len = strlen(str);
if (len == 3) {
res = sscanf(str, "%1hx%1hx%1hx", &color->r, &color->g, &color->b);
if (res != EOF) {
color->a = 255;
return OK;
}
} else if (len == 4) {
res = sscanf(str, "%1hx%1hx%1hx%1hx", &color->r, &color->g, &color->b, &color->a);
if (res != EOF) {
return OK;
}
} else if (len == 6) {
res = sscanf(str, "%02hx%02hx%02hx", &color->r, &color->g, &color->b);
if (res != EOF) {
color->a = 255;
return OK;
}
} else if (len == 8) {
res = sscanf(str, "%02hx%02hx%02hx%02hx", &color->r, &color->g, &color->b, &color->a);
if (res != EOF) {
return OK;
}
}
return DECLINED;
}
void *small_light_alloc(apr_pool_t *pool, apr_size_t size)
{
void *new_ptr = malloc(size);
return new_ptr;
}
void *small_light_realloc(apr_pool_t *pool, void *ptr, apr_size_t size, apr_size_t old_size)
{
void *new_ptr = realloc(ptr, size);
return new_ptr;
}
void small_light_free(apr_pool_t *pool, void *ptr)
{
free(ptr);
}
long small_light_timeval_diff(struct timeval *st, struct timeval *et)
{
return (long)(et->tv_sec - st->tv_sec) * 1000000 + (et->tv_usec - st->tv_usec);
}
apr_status_t small_light_receive_data(
unsigned char **image,
size_t *image_len,
const ap_filter_t *f,
const apr_bucket_brigade *bb,
const char *data,
const apr_size_t data_len)
{
request_rec *r = f->r;
const char *next_buff;
next_buff = (const char *)small_light_realloc(r->pool, *image, *image_len + data_len, *image_len);
if (next_buff == NULL) {
if (*image) {
small_light_free(r->pool, *image);
*image = NULL;
}
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "memory allocation failed");
ap_pass_brigade(f->next, (apr_bucket_brigade *)bb);
return DECLINED;
}
*image = (unsigned char *)next_buff;
memcpy(*image + *image_len, data, data_len);
*image_len += data_len;
return APR_SUCCESS;
}
void small_light_calc_image_size(
small_light_image_size_t *sz,
request_rec *r,
small_light_module_ctx_t *ctx,
double iw, double ih)
{
// calc image size.
small_light_coord_t sx_coord, sy_coord, sw_coord, sh_coord;
small_light_coord_t dx_coord, dy_coord, dw_coord, dh_coord;
small_light_parse_coord(r, &sx_coord, (char *)apr_table_get(ctx->prm, "sx"));
small_light_parse_coord(r, &sy_coord, (char *)apr_table_get(ctx->prm, "sy"));
small_light_parse_coord(r, &sw_coord, (char *)apr_table_get(ctx->prm, "sw"));
small_light_parse_coord(r, &sh_coord, (char *)apr_table_get(ctx->prm, "sh"));
small_light_parse_coord(r, &dx_coord, (char *)apr_table_get(ctx->prm, "dx"));
small_light_parse_coord(r, &dy_coord, (char *)apr_table_get(ctx->prm, "dy"));
small_light_parse_coord(r, &dw_coord, (char *)apr_table_get(ctx->prm, "dw"));
small_light_parse_coord(r, &dh_coord, (char *)apr_table_get(ctx->prm, "dh"));
sz->sx = small_light_calc_coord(&sx_coord, iw);
if (sz->sx == SMALL_LIGHT_COORD_INVALID_VALUE) {
sz->sx = 0;
}
sz->sy = small_light_calc_coord(&sy_coord, ih);
if (sz->sy == SMALL_LIGHT_COORD_INVALID_VALUE) {
sz->sy = 0;
}
sz->sw = small_light_calc_coord(&sw_coord, iw);
if (sz->sw == SMALL_LIGHT_COORD_INVALID_VALUE) {
sz->sw = iw;
}
sz->sh = small_light_calc_coord(&sh_coord, ih);
if (sz->sh == SMALL_LIGHT_COORD_INVALID_VALUE) {
sz->sh = ih;
}
sz->dx = small_light_calc_coord(&dx_coord, iw);
sz->dy = small_light_calc_coord(&dy_coord, ih);
sz->dw = small_light_calc_coord(&dw_coord, iw);
sz->dh = small_light_calc_coord(&dh_coord, ih);
sz->aspect = sz->sw / sz->sh;
char *da_str = (char *)apr_table_get(ctx->prm, "da");
char da = da_str[0] ? da_str[0] : 'l';
if (sz->dw != SMALL_LIGHT_COORD_INVALID_VALUE && sz->dh != SMALL_LIGHT_COORD_INVALID_VALUE) {
if (da == 'l') {
if (sz->sw / sz->dw < sz->sh / sz->dh) {
sz->dw = sz->dh * sz->aspect;
} else {
sz->dh = sz->dw / sz->aspect;
}
} else if (da == 's') {
if (sz->sw / sz->dw < sz->sh / sz->dh) {
sz->dh = sz->dw / sz->aspect;
} else {
sz->dw = sz->dh * sz->aspect;
}
}
} else {
if (sz->dw == SMALL_LIGHT_COORD_INVALID_VALUE && sz->dh == sz->dw) {
double dwo = sz->dw;
sz->dw = sz->dh / sz->aspect;
sz->dh = dwo / sz->aspect;
} else if (sz->dw == SMALL_LIGHT_COORD_INVALID_VALUE) {
sz->dw = sz->dh * sz->aspect;
} else if (sz->dh == SMALL_LIGHT_COORD_INVALID_VALUE) {
sz->dh = sz->dw / sz->aspect;
}
}
sz->cw = small_light_parse_double(r, (char *)apr_table_get(ctx->prm, "cw"));
sz->ch = small_light_parse_double(r, (char *)apr_table_get(ctx->prm, "ch"));
small_light_parse_color(r, &sz->cc, (char *)apr_table_get(ctx->prm, "cc"));
sz->bw = small_light_parse_double(r, (char *)apr_table_get(ctx->prm, "bw"));
sz->bh = small_light_parse_double(r, (char *)apr_table_get(ctx->prm, "bh"));
small_light_parse_color(r, &sz->bc, (char *)apr_table_get(ctx->prm, "bc"));
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
"size info:sx=%f,sy=%f,sw=%f,sh=%f,dw=%f,dh=%f,cw=%f,ch=%f,bw=%f,bh=%f",
sz->sx, sz->sy, sz->sw, sz->sh,
sz->dw, sz->dh, sz->cw, sz->ch, sz->bw, sz->bh);
// get pass through option.
int pt_flg = 0;
char *pt = (char *)apr_table_get(ctx->prm, "pt");
if (pt[0] == '\0' || strcmp(pt, "ptss") == 0) {
if (sz->sw < sz->cw && sz->sh < sz->ch) {
pt_flg = 1;
}
} else if (strcmp(pt, "ptls") == 0) {
if (sz->sw > sz->cw || sz->sh > sz->ch) {
pt_flg = 1;
}
}
sz->pt_flg = pt_flg;
// get scaling option.
char *prm_ds_str = (char *)apr_table_get(ctx->prm, "ds");
char prm_ds = prm_ds_str[0] ? prm_ds_str[0] : 'l';
if (prm_ds == 's' || (sz->dw < sz->sw - sz->sx) || (sz->dh < sz->sh - sz->sy)) {
sz->scale_flg = 1;
} else {
sz->scale_flg = 0;
sz->dw = iw;
sz->dh = ih;
}
if (sz->dx == SMALL_LIGHT_COORD_INVALID_VALUE) {
sz->dx = (sz->cw - sz->dw) * 0.5;
}
if (sz->dy == SMALL_LIGHT_COORD_INVALID_VALUE) {
sz->dy = (sz->ch - sz->dh) * 0.5;
}
// get exif option.
int inhexif_flg = 0;
char *inhexif = (char *)apr_table_get(ctx->prm, "inhexif");
if (inhexif[0] == 'y') {
inhexif_flg = 1;
} else {
inhexif_flg = 0;
}
sz->inhexif_flg = inhexif_flg;
// get jpeghint option.
sz->jpeghint_flg = small_light_parse_flag(r, (char *)apr_table_get(ctx->prm, "jpeghint"));
}