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