Annotation of embedaddon/lighttpd/src/mod_expire.c, revision 1.1.1.3

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

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>