Michael Spiegle wrote:
Hi All,
I'm writing a module called mod_gfx which will perform on-the-fly image
resizing via libgd. I wanted to make the module as configurable as
possible so others can take advantage of it as well. I'm currently
running into a rough spot with custom containers, but first I'd like to
make mention of how the module works.
I don't think it is correct, but I have found a solution for now. Since
I can only seem to execute directives inside a container via
ap_walk_config(), and it requires a section_vector to be working, I'm
(ab)using ap_set_config_vectors() to send a tag to the per-dir config
allocator so it knows what type of config to allocate. I have no idea
how bad this is, but it appears to be working. Here's the relevant code:
static const char*
gfx_config_profile(cmd_parms* cmd, void* p, const char* arg) {
const char* endp = ap_strrchr_c(arg, '>');
const char* args;
char* profile_name;
ap_conf_vector_t* gfx_conf;
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Entering
gfx_config_profile callback");
//create a new profile for each <GfxProfile> encountered
gfx_conf = ap_create_per_dir_config(cmd->pool);
const char* err = ap_check_cmd_context(cmd, NOT_IN_LIMIT |
NOT_IN_DIRECTORY);
if (err != NULL)
return err;
//get the profile name (argument to <GfxProfile>)
//do some checks for good measure
args = apr_pstrndup(cmd->pool, arg, endp - arg);
if (!args[0])
return apr_psprintf(cmd->pool, "%s %s", cmd->cmd->name, ">
directive requires profile name");
if (endp == NULL)
return apr_psprintf(cmd->pool, "%s %s", cmd->cmd->name, ">
directive missing closing '>'");
profile_name = ap_getword_conf(cmd->pool, &args);
if (!profile_name)
return apr_psprintf(cmd->pool, "%s %s", cmd->cmd->name, ">
error reading profile name");
//i abusing this by sending <GfxProfile> so the container creator
knows what type to give me back
gfx_profile_config_t* profile = ap_set_config_vectors(cmd->server,
gfx_conf, "<GfxProfile>", &gfx_module, cmd->pool);
//setup profile
profile->name = apr_pstrdup(cmd->pool, profile_name);
//not sure yet - i hope it iterates over the gfx parms in the
<GfxProfile>
err = ap_walk_config(cmd->directive->first_child, cmd, gfx_conf);
if (err != NULL)
return err;
//add profile to server profiles hashmap
gfx_server_config_t* sconf =
ap_get_module_config(cmd->server->module_config, &gfx_module);
apr_hash_set(sconf->profiles, profile->name, APR_HASH_KEY_STRING,
profile);
return NULL;
}
static const char*
gfx_config_origin(cmd_parms* cmd, void* p, const char* arg) {
const char* endp = ap_strrchr_c(arg, '>');
const char* args;
char* origin_name;
ap_conf_vector_t* gfx_conf;
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Entering
gfx_config_origin callback");
//create a new origin for each <GfxOrigin> encountered
gfx_conf = ap_create_per_dir_config(cmd->pool);
const char* err = ap_check_cmd_context(cmd, NOT_IN_LIMIT |
NOT_IN_DIRECTORY);
if (err != NULL)
return err;
//get the origin name (argument to <GfxOrigin>)
//do some checks for good measure
args = apr_pstrndup(cmd->pool, arg, endp - arg);
if (!args[0])
return apr_psprintf(cmd->pool, "%s %s", cmd->cmd->name, ">
directive requires origin name");
if (endp == NULL)
return apr_psprintf(cmd->pool, "%s %s", cmd->cmd->name, ">
directive missing closing '>'");
origin_name = ap_getword_conf(cmd->pool, &args);
if (!origin_name)
return apr_psprintf(cmd->pool, "%s %s", cmd->cmd->name, ">
error reading origin name");
//i think this associates the generic config type with my origin config
gfx_origin_config_t* origin = ap_set_config_vectors(cmd->server,
gfx_conf, "<GfxOrigin>", &gfx_module, cmd->pool);
//setup origin
origin->name = apr_pstrdup(cmd->pool, origin_name);
//not sure yet - i hope it iterates over the gfx parms in the
<GfxOrigin>
err = ap_walk_config(cmd->directive->first_child, cmd, gfx_conf);
if (err != NULL)
return err;
//add origin to server origins
gfx_server_config_t* sconf =
ap_get_module_config(cmd->server->module_config, &gfx_module);
apr_hash_set(sconf->origins, origin->name, APR_HASH_KEY_STRING,
origin);
return NULL;
}
static void*
gfx_create_server_config(apr_pool_t* pool, server_rec* server) {
gfx_server_config_t* sconf;
sconf = (gfx_server_config_t*)apr_pcalloc(pool,
sizeof(gfx_server_config_t));
sconf->profiles = apr_hash_make(pool);
sconf->origins = apr_hash_make(pool);
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Created server config");
return (void*)sconf;
}
static void*
gfx_create_container_config(apr_pool_t* pool, char* arg) {
//I'm not really sure why apache calls this function at startup
//I don't think I need it, so I'm just going to ignore it...
if (arg == NULL)
return (void*)NULL;
//Create a GfxProfile struct on-command and pass it back to the caller
if (strcmp(arg, "<GfxProfile>") == 0) {
gfx_profile_config_t* profile;
profile = (gfx_profile_config_t*)apr_pcalloc(pool,
sizeof(gfx_profile_config_t));
profile->name = NULL;
profile->output_format = IMAGE_TYPE_SRC;
profile->actions = apr_array_make(pool, 8, sizeof(const char*));
profile->rsx = 0;
profile->rsy = 0;
profile->qual = 0;
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Created
<GfxProfile> container");
return profile;
}
//Create a GfxOrigin struct on-command and pass it back to the caller
if (strcmp(arg, "<GfxOrigin>") == 0) {
gfx_origin_config_t* origin;
origin = (gfx_origin_config_t*)apr_pcalloc(pool,
sizeof(gfx_origin_config_t));
origin->name = NULL;
origin->hosts = apr_array_make(pool, 16, sizeof(const char*));
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Created
<GfxOrigin> container");
return origin;
}
//default case. shouldn't ever happen
ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL, "Error creating
container (%s)", arg);
return (void*)NULL;
}
static const
command_rec gfx_cmds[] = {
AP_INIT_RAW_ARGS("<GfxProfile", gfx_config_profile, NULL, RSRC_CONF,
"Container to create profiles"),
AP_INIT_NO_ARGS("</GfxProfile>", NULL, NULL, RSRC_CONF, "Place
Holder"),
AP_INIT_RAW_ARGS("<GfxOrigin", gfx_config_origin, NULL, RSRC_CONF,
"Container to create origin hosts"),
AP_INIT_NO_ARGS("</GfxOrigin>", NULL, NULL, RSRC_CONF, "Place Holder"),
AP_INIT_ITERATE("GfxActions", gfx_profile_actions, NULL, RSRC_CONF,
"Defines a list of actions to perform on the image"),
AP_INIT_TAKE1("GfxRsX", gfx_profile_rsx, NULL, RSRC_CONF,
"Specifies a maximum width to resize to"),
AP_INIT_TAKE1("GfxRsY", gfx_profile_rsy, NULL, RSRC_CONF,
"Specifies a maximum height to resize to"),
AP_INIT_TAKE1("GfxQual", gfx_profile_qual, NULL, RSRC_CONF,
"Specifies the quality (if supported by output format)"),
AP_INIT_TAKE1("GfxOutputFormat", gfx_profile_outputformat, NULL,
RSRC_CONF,
"Specifies the ouput format"),
AP_INIT_TAKE2("GfxOriginMatch", gfx_config_originmatch, NULL,
RSRC_CONF,
"A regex to declare origins"),
AP_INIT_TAKE1("GfxOriginHost", gfx_config_originhost, NULL, RSRC_CONF,
"Define a host to origin content from"),
{ NULL }
};
module AP_MODULE_DECLARE_DATA gfx_module = {
STANDARD20_MODULE_STUFF,
gfx_create_container_config, //do I really need this?
NULL, //i'm too lazy to implement merging
gfx_create_server_config, //create server config
NULL, //i'm too lazy to implement merging
gfx_cmds, //command table
gfx_register_hooks //hooks
};