blob: 9a25ff41903405605b2e7d04ba69c63b20f7858c [file] [log] [blame]
/** @file
Main file for the generator plugin for lighttpd
@section license License
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include "base.h"
#include "log.h"
#include "buffer.h"
#include "plugin.h"
#include "response.h"
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <stdio.h>
/**
* this is a generator for a lighttpd plugin
*
* just replaces every occurance of 'generator' by your plugin name
*
* e.g. in vim:
*
* :%s/generator/myhandler/
*
*/
static char static_data[8192];
/* plugin config for all request/connections */
typedef struct {
array *match;
} plugin_config;
typedef struct {
PLUGIN_DATA;
buffer *match_buf;
plugin_config **config_storage;
plugin_config conf;
} plugin_data;
typedef struct {
size_t foo;
} handler_ctx;
// static handler_ctx * handler_ctx_init() {
// handler_ctx * hctx;
// hctx = calloc(1, sizeof(*hctx));
// return hctx;
// }
// static void handler_ctx_free(handler_ctx *hctx) {
// free(hctx);
// }
/* init the plugin data */
INIT_FUNC(mod_generator_init)
{
plugin_data *p;
p = calloc(1, sizeof(*p));
p->match_buf = buffer_init();
return p;
}
/* detroy the plugin data */
FREE_FUNC(mod_generator_free)
{
plugin_data *p = p_d;
UNUSED(srv);
if (!p)
return HANDLER_GO_ON;
if (p->config_storage) {
size_t i;
for (i = 0; i < srv->config_context->used; i++) {
plugin_config *s = p->config_storage[i];
if (!s)
continue;
array_free(s->match);
free(s);
}
free(p->config_storage);
}
buffer_free(p->match_buf);
free(p);
return HANDLER_GO_ON;
}
/* handle plugin config and check values */
SETDEFAULTS_FUNC(mod_generator_set_defaults)
{
plugin_data *p = p_d;
size_t i = 0;
config_values_t cv[] = {{"generator.array", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION}, /* 0 */
{NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET}};
if (!p)
return HANDLER_ERROR;
p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
for (i = 0; i < srv->config_context->used; i++) {
plugin_config *s;
s = calloc(1, sizeof(plugin_config));
s->match = array_init();
cv[0].destination = s->match;
p->config_storage[i] = s;
if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
return HANDLER_ERROR;
}
}
return HANDLER_GO_ON;
}
#define PATCH(x) p->conf.x = s->x;
static int
mod_generator_patch_connection(server *srv, connection *con, plugin_data *p)
{
size_t i, j;
plugin_config *s = p->config_storage[0];
PATCH(match);
/* skip the first, the global context */
for (i = 1; i < srv->config_context->used; i++) {
data_config *dc = (data_config *)srv->config_context->data[i];
s = p->config_storage[i];
/* condition didn't match */
if (!config_check_cond(srv, con, dc))
continue;
/* merge config */
for (j = 0; j < dc->value->used; j++) {
data_unset *du = dc->value->data[j];
if (buffer_is_equal_string(du->key, CONST_STR_LEN("generator.array"))) {
PATCH(match);
}
}
}
return 0;
}
#undef PATCH
URIHANDLER_FUNC(mod_generator_uri_handler)
{
plugin_data *p = p_d;
int s_len;
size_t k;
UNUSED(srv);
if (con->mode != DIRECT)
return HANDLER_GO_ON;
if (con->uri.path->used == 0)
return HANDLER_GO_ON;
mod_generator_patch_connection(srv, con, p);
s_len = con->uri.path->used - 1;
for (k = 0; k < p->conf.match->used; k++) {
data_string *ds = (data_string *)p->conf.match->data[k];
int ct_len = ds->value->used - 1;
if (ct_len > s_len)
continue;
if (ds->value->used == 0)
continue;
if (0 == strncmp(con->uri.path->ptr + s_len - ct_len, ds->value->ptr, ct_len)) {
con->http_status = 403;
return HANDLER_FINISHED;
}
}
/* not found */
return HANDLER_GO_ON;
}
URIHANDLER_FUNC(mod_generator_subrequest_handler)
{
(void)p_d;
// plugin_data *p = p_d;
buffer *b;
b = chunkqueue_get_append_buffer(con->write_queue);
// get the url information
// int length = strlen(con->uri.path->ptr);
char *start = con->uri.path->ptr;
char *end;
// char *end = start + length;
if (*start != '/') {
log_error_write(srv, __FILE__, __LINE__, "s", "url doesn't start with a slash");
return HANDLER_GO_ON;
}
++start;
// get the size in bytes form the url
int64_t bytes = strtoll(start, &end, 10);
switch (*end) {
case 'k':
case 'K':
bytes *= 1024;
++end;
break;
case 'm':
case 'M':
bytes *= 1024 * 1024;
++end;
break;
case 'g':
case 'G':
bytes *= 1024 * 1024 * 1024;
++end;
break;
default:
break;
}
if (start == end && bytes <= 0 && *start != '-') {
log_error_write(srv, __FILE__, __LINE__, "s", "can't find size in bytes");
return HANDLER_GO_ON;
}
start = end + 1;
// get the id from the url.
end = strchr(start, '-');
if (end == NULL) {
log_error_write(srv, __FILE__, __LINE__, "s", "problems finding the id");
return HANDLER_GO_ON;
}
start = end + 1;
// get the time to sleep from the url
int64_t sleepval = strtoll(start, &end, 10);
if (start == end && sleepval < 0 && *start != '-') {
log_error_write(srv, __FILE__, __LINE__, "s", "problems finding the sleepval");
return HANDLER_GO_ON;
}
start = end + 1;
if (sleepval > 0) {
usleep(1000 * sleepval);
}
// check to see if we are going to set cacheable headers
int cache = -1;
if (strcmp(start, "cache") == 0) {
cache = 1;
} else if (strcmp(start, "no_cache") == 0) {
cache = 0;
} else {
log_error_write(srv, __FILE__, __LINE__, "s", "didn't see cache or no_cache in the url");
return HANDLER_GO_ON;
}
// print the body of the message
uint64_t to_write = 0;
--bytes; // leave a char left over for \n
while (bytes > 0) {
if ((uint64_t)bytes > sizeof(static_data)) {
// biger then the static buffer, so write the entire buffer
to_write = sizeof(static_data);
} else {
to_write = bytes;
}
buffer_append_string_len(b, static_data, to_write);
bytes -= to_write;
}
if (bytes == 0) {
buffer_append_string_len(b, "\n", 1); // add a \n to the end of the body
}
// write the headers if it is cacheable or not
if (cache == 0) {
response_header_insert(srv, con, CONST_STR_LEN("Cache-Control"), CONST_STR_LEN("private"));
} else {
response_header_insert(srv, con, CONST_STR_LEN("Last-Modified"), CONST_STR_LEN("Thu, 12 Feb 2009 23:00:00 GMT"));
response_header_insert(srv, con, CONST_STR_LEN("Cache-Control"), CONST_STR_LEN("max-age=86400, public"));
}
con->http_status = 200;
con->file_finished = 1;
return HANDLER_FINISHED;
}
/* this function is called at dlopen() time and inits the callbacks */
int
mod_generator_plugin_init(plugin *p)
{
p->version = LIGHTTPD_VERSION_ID;
p->name = buffer_init_string("generator");
p->init = mod_generator_init;
p->handle_uri_clean = mod_generator_uri_handler;
p->handle_physical = mod_generator_subrequest_handler;
p->set_defaults = mod_generator_set_defaults;
p->cleanup = mod_generator_free;
p->data = NULL;
// set the static data used in the response;
memset(static_data, 'x', sizeof(static_data));
return 0;
}