Annotation of embedaddon/lighttpd/src/mod_expire.c, revision 1.1
1.1 ! misho 1: #include "base.h"
! 2: #include "log.h"
! 3: #include "buffer.h"
! 4: #include "response.h"
! 5:
! 6: #include "plugin.h"
! 7: #include "stat_cache.h"
! 8:
! 9: #include <ctype.h>
! 10: #include <stdlib.h>
! 11: #include <string.h>
! 12: #include <time.h>
! 13:
! 14: /**
! 15: * this is a expire module for a lighttpd
! 16: *
! 17: * set 'Expires:' HTTP Headers on demand
! 18: */
! 19:
! 20:
! 21:
! 22: /* plugin config for all request/connections */
! 23:
! 24: typedef struct {
! 25: array *expire_url;
! 26: } plugin_config;
! 27:
! 28: typedef struct {
! 29: PLUGIN_DATA;
! 30:
! 31: buffer *expire_tstmp;
! 32:
! 33: plugin_config **config_storage;
! 34:
! 35: plugin_config conf;
! 36: } plugin_data;
! 37:
! 38: /* init the plugin data */
! 39: INIT_FUNC(mod_expire_init) {
! 40: plugin_data *p;
! 41:
! 42: p = calloc(1, sizeof(*p));
! 43:
! 44: p->expire_tstmp = buffer_init();
! 45:
! 46: buffer_prepare_copy(p->expire_tstmp, 255);
! 47:
! 48: return p;
! 49: }
! 50:
! 51: /* detroy the plugin data */
! 52: FREE_FUNC(mod_expire_free) {
! 53: plugin_data *p = p_d;
! 54:
! 55: UNUSED(srv);
! 56:
! 57: if (!p) return HANDLER_GO_ON;
! 58:
! 59: buffer_free(p->expire_tstmp);
! 60:
! 61: if (p->config_storage) {
! 62: size_t i;
! 63: for (i = 0; i < srv->config_context->used; i++) {
! 64: plugin_config *s = p->config_storage[i];
! 65: if (!s) continue;
! 66:
! 67: array_free(s->expire_url);
! 68: free(s);
! 69: }
! 70: free(p->config_storage);
! 71: }
! 72:
! 73: free(p);
! 74:
! 75: return HANDLER_GO_ON;
! 76: }
! 77:
! 78: static int mod_expire_get_offset(server *srv, plugin_data *p, buffer *expire, time_t *offset) {
! 79: char *ts;
! 80: int type = -1;
! 81: time_t retts = 0;
! 82:
! 83: UNUSED(p);
! 84:
! 85: /*
! 86: * parse
! 87: *
! 88: * '(access|now|modification) [plus] {<num> <type>}*'
! 89: *
! 90: * e.g. 'access 1 years'
! 91: */
! 92:
! 93: if (expire->used == 0) {
! 94: log_error_write(srv, __FILE__, __LINE__, "s",
! 95: "empty:");
! 96: return -1;
! 97: }
! 98:
! 99: ts = expire->ptr;
! 100:
! 101: if (0 == strncmp(ts, "access ", 7)) {
! 102: type = 0;
! 103: ts += 7;
! 104: } else if (0 == strncmp(ts, "now ", 4)) {
! 105: type = 0;
! 106: ts += 4;
! 107: } else if (0 == strncmp(ts, "modification ", 13)) {
! 108: type = 1;
! 109: ts += 13;
! 110: } else {
! 111: /* invalid type-prefix */
! 112: log_error_write(srv, __FILE__, __LINE__, "ss",
! 113: "invalid <base>:", ts);
! 114: return -1;
! 115: }
! 116:
! 117: if (0 == strncmp(ts, "plus ", 5)) {
! 118: /* skip the optional plus */
! 119: ts += 5;
! 120: }
! 121:
! 122: /* the rest is just <number> (years|months|weeks|days|hours|minutes|seconds) */
! 123: while (1) {
! 124: char *space, *err;
! 125: int num;
! 126:
! 127: if (NULL == (space = strchr(ts, ' '))) {
! 128: log_error_write(srv, __FILE__, __LINE__, "ss",
! 129: "missing space after <num>:", ts);
! 130: return -1;
! 131: }
! 132:
! 133: num = strtol(ts, &err, 10);
! 134: if (*err != ' ') {
! 135: log_error_write(srv, __FILE__, __LINE__, "ss",
! 136: "missing <type> after <num>:", ts);
! 137: return -1;
! 138: }
! 139:
! 140: ts = space + 1;
! 141:
! 142: if (NULL != (space = strchr(ts, ' '))) {
! 143: int slen;
! 144: /* */
! 145:
! 146: slen = space - ts;
! 147:
! 148: if (slen == 5 &&
! 149: 0 == strncmp(ts, "years", slen)) {
! 150: num *= 60 * 60 * 24 * 30 * 12;
! 151: } else if (slen == 6 &&
! 152: 0 == strncmp(ts, "months", slen)) {
! 153: num *= 60 * 60 * 24 * 30;
! 154: } else if (slen == 5 &&
! 155: 0 == strncmp(ts, "weeks", slen)) {
! 156: num *= 60 * 60 * 24 * 7;
! 157: } else if (slen == 4 &&
! 158: 0 == strncmp(ts, "days", slen)) {
! 159: num *= 60 * 60 * 24;
! 160: } else if (slen == 5 &&
! 161: 0 == strncmp(ts, "hours", slen)) {
! 162: num *= 60 * 60;
! 163: } else if (slen == 7 &&
! 164: 0 == strncmp(ts, "minutes", slen)) {
! 165: num *= 60;
! 166: } else if (slen == 7 &&
! 167: 0 == strncmp(ts, "seconds", slen)) {
! 168: num *= 1;
! 169: } else {
! 170: log_error_write(srv, __FILE__, __LINE__, "ss",
! 171: "unknown type:", ts);
! 172: return -1;
! 173: }
! 174:
! 175: retts += num;
! 176:
! 177: ts = space + 1;
! 178: } else {
! 179: if (0 == strcmp(ts, "years")) {
! 180: num *= 60 * 60 * 24 * 30 * 12;
! 181: } else if (0 == strcmp(ts, "months")) {
! 182: num *= 60 * 60 * 24 * 30;
! 183: } else if (0 == strcmp(ts, "weeks")) {
! 184: num *= 60 * 60 * 24 * 7;
! 185: } else if (0 == strcmp(ts, "days")) {
! 186: num *= 60 * 60 * 24;
! 187: } else if (0 == strcmp(ts, "hours")) {
! 188: num *= 60 * 60;
! 189: } else if (0 == strcmp(ts, "minutes")) {
! 190: num *= 60;
! 191: } else if (0 == strcmp(ts, "seconds")) {
! 192: num *= 1;
! 193: } else {
! 194: log_error_write(srv, __FILE__, __LINE__, "ss",
! 195: "unknown type:", ts);
! 196: return -1;
! 197: }
! 198:
! 199: retts += num;
! 200:
! 201: break;
! 202: }
! 203: }
! 204:
! 205: if (offset != NULL) *offset = retts;
! 206:
! 207: return type;
! 208: }
! 209:
! 210:
! 211: /* handle plugin config and check values */
! 212:
! 213: SETDEFAULTS_FUNC(mod_expire_set_defaults) {
! 214: plugin_data *p = p_d;
! 215: size_t i = 0, k;
! 216:
! 217: config_values_t cv[] = {
! 218: { "expire.url", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
! 219: { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
! 220: };
! 221:
! 222: if (!p) return HANDLER_ERROR;
! 223:
! 224: p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
! 225:
! 226: for (i = 0; i < srv->config_context->used; i++) {
! 227: plugin_config *s;
! 228:
! 229: s = calloc(1, sizeof(plugin_config));
! 230: s->expire_url = array_init();
! 231:
! 232: cv[0].destination = s->expire_url;
! 233:
! 234: p->config_storage[i] = s;
! 235:
! 236: if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
! 237: return HANDLER_ERROR;
! 238: }
! 239:
! 240: for (k = 0; k < s->expire_url->used; k++) {
! 241: data_string *ds = (data_string *)s->expire_url->data[k];
! 242:
! 243: /* parse lines */
! 244: if (-1 == mod_expire_get_offset(srv, p, ds->value, NULL)) {
! 245: log_error_write(srv, __FILE__, __LINE__, "sb",
! 246: "parsing expire.url failed:", ds->value);
! 247: return HANDLER_ERROR;
! 248: }
! 249: }
! 250: }
! 251:
! 252:
! 253: return HANDLER_GO_ON;
! 254: }
! 255:
! 256: #define PATCH(x) \
! 257: p->conf.x = s->x;
! 258: static int mod_expire_patch_connection(server *srv, connection *con, plugin_data *p) {
! 259: size_t i, j;
! 260: plugin_config *s = p->config_storage[0];
! 261:
! 262: PATCH(expire_url);
! 263:
! 264: /* skip the first, the global context */
! 265: for (i = 1; i < srv->config_context->used; i++) {
! 266: data_config *dc = (data_config *)srv->config_context->data[i];
! 267: s = p->config_storage[i];
! 268:
! 269: /* condition didn't match */
! 270: if (!config_check_cond(srv, con, dc)) continue;
! 271:
! 272: /* merge config */
! 273: for (j = 0; j < dc->value->used; j++) {
! 274: data_unset *du = dc->value->data[j];
! 275:
! 276: if (buffer_is_equal_string(du->key, CONST_STR_LEN("expire.url"))) {
! 277: PATCH(expire_url);
! 278: }
! 279: }
! 280: }
! 281:
! 282: return 0;
! 283: }
! 284: #undef PATCH
! 285:
! 286: URIHANDLER_FUNC(mod_expire_path_handler) {
! 287: plugin_data *p = p_d;
! 288: int s_len;
! 289: size_t k;
! 290:
! 291: if (con->uri.path->used == 0) return HANDLER_GO_ON;
! 292:
! 293: mod_expire_patch_connection(srv, con, p);
! 294:
! 295: s_len = con->uri.path->used - 1;
! 296:
! 297: for (k = 0; k < p->conf.expire_url->used; k++) {
! 298: data_string *ds = (data_string *)p->conf.expire_url->data[k];
! 299: int ct_len = ds->key->used - 1;
! 300:
! 301: if (ct_len > s_len) continue;
! 302: if (ds->key->used == 0) continue;
! 303:
! 304: if (0 == strncmp(con->uri.path->ptr, ds->key->ptr, ct_len)) {
! 305: time_t ts, expires;
! 306: size_t len;
! 307: stat_cache_entry *sce = NULL;
! 308:
! 309: stat_cache_get_entry(srv, con, con->physical.path, &sce);
! 310:
! 311: switch(mod_expire_get_offset(srv, p, ds->value, &ts)) {
! 312: case 0:
! 313: /* access */
! 314: expires = (ts + srv->cur_ts);
! 315: break;
! 316: case 1:
! 317: /* modification */
! 318:
! 319: expires = (ts + sce->st.st_mtime);
! 320: break;
! 321: default:
! 322: /* -1 is handled at parse-time */
! 323: break;
! 324: }
! 325:
! 326: /* expires should be at least srv->cur_ts */
! 327: if (expires < srv->cur_ts) expires = srv->cur_ts;
! 328:
! 329: if (0 == (len = strftime(p->expire_tstmp->ptr, p->expire_tstmp->size - 1,
! 330: "%a, %d %b %Y %H:%M:%S GMT", gmtime(&(expires))))) {
! 331: /* could not set expire header, out of mem */
! 332:
! 333: return HANDLER_GO_ON;
! 334: }
! 335:
! 336: p->expire_tstmp->used = len + 1;
! 337:
! 338: /* HTTP/1.0 */
! 339: response_header_overwrite(srv, con, CONST_STR_LEN("Expires"), CONST_BUF_LEN(p->expire_tstmp));
! 340:
! 341: /* HTTP/1.1 */
! 342: buffer_copy_string_len(p->expire_tstmp, CONST_STR_LEN("max-age="));
! 343: buffer_append_long(p->expire_tstmp, expires - srv->cur_ts); /* as expires >= srv->cur_ts the difference is >= 0 */
! 344:
! 345: response_header_append(srv, con, CONST_STR_LEN("Cache-Control"), CONST_BUF_LEN(p->expire_tstmp));
! 346:
! 347: return HANDLER_GO_ON;
! 348: }
! 349: }
! 350:
! 351: /* not found */
! 352: return HANDLER_GO_ON;
! 353: }
! 354:
! 355: /* this function is called at dlopen() time and inits the callbacks */
! 356:
! 357: int mod_expire_plugin_init(plugin *p);
! 358: int mod_expire_plugin_init(plugin *p) {
! 359: p->version = LIGHTTPD_VERSION_ID;
! 360: p->name = buffer_init_string("expire");
! 361:
! 362: p->init = mod_expire_init;
! 363: p->handle_subrequest_start = mod_expire_path_handler;
! 364: p->set_defaults = mod_expire_set_defaults;
! 365: p->cleanup = mod_expire_free;
! 366:
! 367: p->data = NULL;
! 368:
! 369: return 0;
! 370: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>