Annotation of embedaddon/lighttpd/src/mod_expire.c, revision 1.1.1.2
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:
1.1.1.2 ! misho 224: p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
1.1 misho 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:
1.1.1.2 ! misho 309: /* if stat fails => sce == NULL, ignore return value */
! 310: (void) stat_cache_get_entry(srv, con, con->physical.path, &sce);
1.1 misho 311:
312: switch(mod_expire_get_offset(srv, p, ds->value, &ts)) {
313: case 0:
314: /* access */
315: expires = (ts + srv->cur_ts);
316: break;
317: case 1:
318: /* modification */
319:
1.1.1.2 ! misho 320: /* can't set modification based expire header if
! 321: * mtime is not available
! 322: */
! 323: if (NULL == sce) return HANDLER_GO_ON;
! 324:
1.1 misho 325: expires = (ts + sce->st.st_mtime);
326: break;
327: default:
328: /* -1 is handled at parse-time */
1.1.1.2 ! misho 329: return HANDLER_ERROR;
1.1 misho 330: }
331:
332: /* expires should be at least srv->cur_ts */
333: if (expires < srv->cur_ts) expires = srv->cur_ts;
334:
335: if (0 == (len = strftime(p->expire_tstmp->ptr, p->expire_tstmp->size - 1,
336: "%a, %d %b %Y %H:%M:%S GMT", gmtime(&(expires))))) {
337: /* could not set expire header, out of mem */
338:
339: return HANDLER_GO_ON;
340: }
341:
342: p->expire_tstmp->used = len + 1;
343:
344: /* HTTP/1.0 */
345: response_header_overwrite(srv, con, CONST_STR_LEN("Expires"), CONST_BUF_LEN(p->expire_tstmp));
346:
347: /* HTTP/1.1 */
348: buffer_copy_string_len(p->expire_tstmp, CONST_STR_LEN("max-age="));
349: buffer_append_long(p->expire_tstmp, expires - srv->cur_ts); /* as expires >= srv->cur_ts the difference is >= 0 */
350:
351: response_header_append(srv, con, CONST_STR_LEN("Cache-Control"), CONST_BUF_LEN(p->expire_tstmp));
352:
353: return HANDLER_GO_ON;
354: }
355: }
356:
357: /* not found */
358: return HANDLER_GO_ON;
359: }
360:
361: /* this function is called at dlopen() time and inits the callbacks */
362:
363: int mod_expire_plugin_init(plugin *p);
364: int mod_expire_plugin_init(plugin *p) {
365: p->version = LIGHTTPD_VERSION_ID;
366: p->name = buffer_init_string("expire");
367:
368: p->init = mod_expire_init;
369: p->handle_subrequest_start = mod_expire_path_handler;
370: p->set_defaults = mod_expire_set_defaults;
371: p->cleanup = mod_expire_free;
372:
373: p->data = NULL;
374:
375: return 0;
376: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>