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