xref: /lighttpd1.4/src/mod_staticfile.c (revision e2de4e58)
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 		if (!config_check_cond(con, i)) continue; /* condition not matched */
134 
135 		data_config *dc = (data_config *)srv->config_context->data[i];
136 		s = p->config_storage[i];
137 
138 		/* merge config */
139 		for (j = 0; j < dc->value->used; j++) {
140 			data_unset *du = dc->value->data[j];
141 
142 			if (buffer_is_equal_string(&du->key, CONST_STR_LEN("static-file.exclude-extensions"))) {
143 				PATCH(exclude_ext);
144 			} else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("static-file.etags"))) {
145 				PATCH(etags_used);
146 			} else if (buffer_is_equal_string(&du->key, CONST_STR_LEN("static-file.disable-pathinfo"))) {
147 				PATCH(disable_pathinfo);
148 			}
149 		}
150 	}
151 
152 	return 0;
153 }
154 #undef PATCH
155 
156 URIHANDLER_FUNC(mod_staticfile_subrequest) {
157 	plugin_data *p = p_d;
158 
159 	/* someone else has done a decision for us */
160 	if (con->http_status != 0) return HANDLER_GO_ON;
161 	if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON;
162 	if (buffer_is_empty(con->physical.path)) return HANDLER_GO_ON;
163 
164 	/* someone else has handled this request */
165 	if (con->mode != DIRECT) return HANDLER_GO_ON;
166 
167 	/* we only handle GET, POST and HEAD */
168 	switch(con->request.http_method) {
169 	case HTTP_METHOD_GET:
170 	case HTTP_METHOD_POST:
171 	case HTTP_METHOD_HEAD:
172 		break;
173 	default:
174 		return HANDLER_GO_ON;
175 	}
176 
177 	mod_staticfile_patch_connection(srv, con, p);
178 
179 	if (p->conf.disable_pathinfo && !buffer_string_is_empty(con->request.pathinfo)) {
180 		if (con->conf.log_request_handling) {
181 			log_error_write(srv, __FILE__, __LINE__,  "s",  "-- NOT handling file as static file, pathinfo forbidden");
182 		}
183 		return HANDLER_GO_ON;
184 	}
185 
186 	/* ignore certain extensions */
187 	if (0 != p->conf.exclude_ext->used && array_match_value_suffix(p->conf.exclude_ext, con->physical.path)) {
188 			if (con->conf.log_request_handling) {
189 				log_error_write(srv, __FILE__, __LINE__,  "s",  "-- NOT handling file as static file, extension forbidden");
190 			}
191 			return HANDLER_GO_ON;
192 	}
193 
194 
195 	if (con->conf.log_request_handling) {
196 		log_error_write(srv, __FILE__, __LINE__,  "s",  "-- handling file as static file");
197 	}
198 
199 	if (!p->conf.etags_used) con->etag_flags = 0;
200 	http_response_send_file(srv, con, con->physical.path);
201 
202 	return HANDLER_FINISHED;
203 }
204 
205 /* this function is called at dlopen() time and inits the callbacks */
206 
207 int mod_staticfile_plugin_init(plugin *p);
208 int mod_staticfile_plugin_init(plugin *p) {
209 	p->version     = LIGHTTPD_VERSION_ID;
210 	p->name        = "staticfile";
211 
212 	p->init        = mod_staticfile_init;
213 	p->handle_subrequest_start = mod_staticfile_subrequest;
214 	p->set_defaults  = mod_staticfile_set_defaults;
215 	p->cleanup     = mod_staticfile_free;
216 
217 	return 0;
218 }
219