Annotation of embedaddon/lighttpd/src/mod_expire.c, revision 1.1.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>