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>