xref: /lighttpd1.4/src/mod_staticfile.c (revision 49e9f0ac)
1 #include "first.h"
2 
3 #include "base.h"
4 #include "log.h"
5 #include "buffer.h"
6 
7 #include "plugin.h"
8 
9 #include "etag.h"
10 #include "http_chunk.h"
11 #include "response.h"
12 
13 #include <stdlib.h>
14 #include <string.h>
15 
16 /**
17  * this is a staticfile for a lighttpd plugin
18  *
19  */
20 
21 
22 
23 /* plugin config for all request/connections */
24 
25 typedef struct {
26 	array *exclude_ext;
27 	unsigned short etags_used;
28 	unsigned short disable_pathinfo;
29 } plugin_config;
30 
31 typedef struct {
32 	PLUGIN_DATA;
33 
34 	plugin_config **config_storage;
35 
36 	plugin_config conf;
37 } plugin_data;
38 
39 /* init the plugin data */
40 INIT_FUNC(mod_staticfile_init) {
41 	plugin_data *p;
42 
43 	p = calloc(1, sizeof(*p));
44 
45 	return p;
46 }
47 
48 /* detroy the plugin data */
49 FREE_FUNC(mod_staticfile_free) {
50 	plugin_data *p = p_d;
51 
52 	UNUSED(srv);
53 
54 	if (!p) return HANDLER_GO_ON;
55 
56 	if (p->config_storage) {
57 		size_t i;
58 		for (i = 0; i < srv->config_context->used; i++) {
59 			plugin_config *s = p->config_storage[i];
60 
61 			if (NULL == s) continue;
62 
63 			array_free(s->exclude_ext);
64 
65 			free(s);
66 		}
67 		free(p->config_storage);
68 	}
69 
70 	free(p);
71 
72 	return HANDLER_GO_ON;
73 }
74 
75 /* handle plugin config and check values */
76 
77 SETDEFAULTS_FUNC(mod_staticfile_set_defaults) {
78 	plugin_data *p = p_d;
79 	size_t i = 0;
80 
81 	config_values_t cv[] = {
82 		{ "static-file.exclude-extensions", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
83 		{ "static-file.etags",    NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
84 		{ "static-file.disable-pathinfo", NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
85 		{ NULL,                         NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
86 	};
87 
88 	if (!p) return HANDLER_ERROR;
89 
90 	p->config_storage = calloc(srv->config_context->used, sizeof(plugin_config *));
91 
92 	for (i = 0; i < srv->config_context->used; i++) {
93 		data_config const* config = (data_config const*)srv->config_context->data[i];
94 		plugin_config *s;
95 
96 		s = calloc(1, sizeof(plugin_config));
97 		s->exclude_ext    = array_init();
98 		s->etags_used     = 1;
99 		s->disable_pathinfo = 0;
100 
101 		cv[0].destination = s->exclude_ext;
102 		cv[1].destination = &(s->etags_used);
103 		cv[2].destination = &(s->disable_pathinfo);
104 
105 		p->config_storage[i] = s;
106 
107 		if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) {
108 			return HANDLER_ERROR;
109 		}
110 
111 		if (!array_is_vlist(s->exclude_ext)) {
112 			log_error_write(srv, __FILE__, __LINE__, "s",
113 					"unexpected value for static-file.exclude-extensions; expected list of \"ext\"");
114 			return HANDLER_ERROR;
115 		}
116 	}
117 
118 	return HANDLER_GO_ON;
119 }
120 
121 #define PATCH(x) \
122 	p->conf.x = s->x;
123 static int mod_staticfile_patch_connection(server *srv, connection *con, plugin_data *p) {
124 	size_t i, j;
125 	plugin_config *s = p->config_storage[0];
126 
127 	PATCH(exclude_ext);
128 	PATCH(etags_used);
129 	PATCH(disable_pathinfo);
130 
131 	/* skip the first, the global context */
132 	for (i = 1; i < srv->config_context->used; i++) {
133 		data_config *dc = (data_config *)srv->config_context->data[i];
134 		s = p->config_storage[i];
135 
136 		/* condition didn't match */
137 		if (!config_check_cond(srv, con, dc)) continue;
138 
139 		/* merge config */
140 		for (j = 0; j < dc->value->used; j++) {
141 			data_unset *du = dc->value->data[j];
142 
143 			if (buffer_is_equal_string(du->key, CONST_STR_LEN("static-file.exclude-extensions"))) {
144 				PATCH(exclude_ext);
145 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("static-file.etags"))) {
146 				PATCH(etags_used);
147 			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN("static-file.disable-pathinfo"))) {
148 				PATCH(disable_pathinfo);
149 			}
150 		}
151 	}
152 
153 	return 0;
154 }
155 #undef PATCH
156 
157 URIHANDLER_FUNC(mod_staticfile_subrequest) {
158 	plugin_data *p = p_d;
159 
160 	/* someone else has done a decision for us */
161 	if (con->http_status != 0) return HANDLER_GO_ON;
162 	if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON;
163 	if (buffer_is_empty(con->physical.path)) return HANDLER_GO_ON;
164 
165 	/* someone else has handled this request */
166 	if (con->mode != DIRECT) return HANDLER_GO_ON;
167 
168 	/* we only handle GET, POST and HEAD */
169 	switch(con->request.http_method) {
170 	case HTTP_METHOD_GET:
171 	case HTTP_METHOD_POST:
172 	case HTTP_METHOD_HEAD:
173 		break;
174 	default:
175 		return HANDLER_GO_ON;
176 	}
177 
178 	mod_staticfile_patch_connection(srv, con, p);
179 
180 	if (p->conf.disable_pathinfo && !buffer_string_is_empty(con->request.pathinfo)) {
181 		if (con->conf.log_request_handling) {
182 			log_error_write(srv, __FILE__, __LINE__,  "s",  "-- NOT handling file as static file, pathinfo forbidden");
183 		}
184 		return HANDLER_GO_ON;
185 	}
186 
187 	/* ignore certain extensions */
188 	if (0 != p->conf.exclude_ext->used && array_match_value_suffix(p->conf.exclude_ext, con->physical.path)) {
189 			if (con->conf.log_request_handling) {
190 				log_error_write(srv, __FILE__, __LINE__,  "s",  "-- NOT handling file as static file, extension forbidden");
191 			}
192 			return HANDLER_GO_ON;
193 	}
194 
195 
196 	if (con->conf.log_request_handling) {
197 		log_error_write(srv, __FILE__, __LINE__,  "s",  "-- handling file as static file");
198 	}
199 
200 	if (!p->conf.etags_used) con->etag_flags = 0;
201 	http_response_send_file(srv, con, con->physical.path);
202 
203 	return HANDLER_FINISHED;
204 }
205 
206 /* this function is called at dlopen() time and inits the callbacks */
207 
208 int mod_staticfile_plugin_init(plugin *p);
209 int mod_staticfile_plugin_init(plugin *p) {
210 	p->version     = LIGHTTPD_VERSION_ID;
211 	p->name        = buffer_init_string("staticfile");
212 
213 	p->init        = mod_staticfile_init;
214 	p->handle_subrequest_start = mod_staticfile_subrequest;
215 	p->set_defaults  = mod_staticfile_set_defaults;
216 	p->cleanup     = mod_staticfile_free;
217 
218 	p->data        = NULL;
219 
220 	return 0;
221 }
222