00001 #include <ctype.h>
00002 #include <stdlib.h>
00003 #include <stdio.h>
00004 #include <string.h>
00005
00006 #include "base.h"
00007 #include "log.h"
00008 #include "buffer.h"
00009
00010 #include "plugin.h"
00011
00012 #include "stat_cache.h"
00013 #include "etag.h"
00014 #include "http_chunk.h"
00015 #include "response.h"
00016
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038 typedef struct {
00039 array *exclude_ext;
00040 } plugin_config;
00041
00042 typedef struct {
00043 PLUGIN_DATA;
00044
00045 buffer *range_buf;
00046
00047 plugin_config **config_storage;
00048
00049 plugin_config conf;
00050 } plugin_data;
00051
00052
00053 INIT_FUNC(mod_staticfile_init) {
00054 plugin_data *p;
00055
00056 p = calloc(1, sizeof(*p));
00057
00058 p->range_buf = buffer_init();
00059
00060 return p;
00061 }
00062
00063
00064 FREE_FUNC(mod_staticfile_free) {
00065 plugin_data *p = p_d;
00066
00067 UNUSED(srv);
00068
00069 if (!p) return HANDLER_GO_ON;
00070
00071 if (p->config_storage) {
00072 size_t i;
00073 for (i = 0; i < srv->config_context->used; i++) {
00074 plugin_config *s = p->config_storage[i];
00075
00076 array_free(s->exclude_ext);
00077
00078 free(s);
00079 }
00080 free(p->config_storage);
00081 }
00082 buffer_free(p->range_buf);
00083
00084 free(p);
00085
00086 return HANDLER_GO_ON;
00087 }
00088
00089
00090
00091 SETDEFAULTS_FUNC(mod_staticfile_set_defaults) {
00092 plugin_data *p = p_d;
00093 size_t i = 0;
00094
00095 config_values_t cv[] = {
00096 { "static-file.exclude-extensions", NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },
00097 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
00098 };
00099
00100 if (!p) return HANDLER_ERROR;
00101
00102 p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
00103
00104 for (i = 0; i < srv->config_context->used; i++) {
00105 plugin_config *s;
00106
00107 s = calloc(1, sizeof(plugin_config));
00108 s->exclude_ext = array_init();
00109
00110 cv[0].destination = s->exclude_ext;
00111
00112 p->config_storage[i] = s;
00113
00114 if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
00115 return HANDLER_ERROR;
00116 }
00117 }
00118
00119 return HANDLER_GO_ON;
00120 }
00121
00122 #define PATCH(x) \
00123 p->conf.x = s->x;
00124 static int mod_staticfile_patch_connection(server *srv, connection *con, plugin_data *p) {
00125 size_t i, j;
00126 plugin_config *s = p->config_storage[0];
00127
00128 PATCH(exclude_ext);
00129
00130
00131 for (i = 1; i < srv->config_context->used; i++) {
00132 data_config *dc = (data_config *)srv->config_context->data[i];
00133 s = p->config_storage[i];
00134
00135
00136 if (!config_check_cond(srv, con, dc)) continue;
00137
00138
00139 for (j = 0; j < dc->value->used; j++) {
00140 data_unset *du = dc->value->data[j];
00141
00142 if (buffer_is_equal_string(du->key, CONST_STR_LEN("static-file.exclude-extensions"))) {
00143 PATCH(exclude_ext);
00144 }
00145 }
00146 }
00147
00148 return 0;
00149 }
00150 #undef PATCH
00151
00152 static int http_response_parse_range(server *srv, connection *con, plugin_data *p) {
00153 int multipart = 0;
00154 int error;
00155 off_t start, end;
00156 const char *s, *minus;
00157 char *boundary = "fkj49sn38dcn3";
00158 data_string *ds;
00159 stat_cache_entry *sce = NULL;
00160 buffer *content_type = NULL;
00161
00162 if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
00163 SEGFAULT();
00164 }
00165
00166 start = 0;
00167 end = sce->st.st_size - 1;
00168
00169 con->response.content_length = 0;
00170
00171 if (NULL != (ds = (data_string *)array_get_element(con->response.headers, "Content-Type"))) {
00172 content_type = ds->value;
00173 }
00174
00175 for (s = con->request.http_range, error = 0;
00176 !error && *s && NULL != (minus = strchr(s, '-')); ) {
00177 char *err;
00178 off_t la, le;
00179
00180 if (s == minus) {
00181
00182
00183 le = strtoll(s, &err, 10);
00184
00185 if (le == 0) {
00186
00187
00188 con->http_status = 416;
00189 error = 1;
00190 } else if (*err == '\0') {
00191
00192 s = err;
00193
00194 end = sce->st.st_size - 1;
00195 start = sce->st.st_size + le;
00196 } else if (*err == ',') {
00197 multipart = 1;
00198 s = err + 1;
00199
00200 end = sce->st.st_size - 1;
00201 start = sce->st.st_size + le;
00202 } else {
00203 error = 1;
00204 }
00205
00206 } else if (*(minus+1) == '\0' || *(minus+1) == ',') {
00207
00208
00209 la = strtoll(s, &err, 10);
00210
00211 if (err == minus) {
00212
00213
00214 if (*(err + 1) == '\0') {
00215 s = err + 1;
00216
00217 end = sce->st.st_size - 1;
00218 start = la;
00219
00220 } else if (*(err + 1) == ',') {
00221 multipart = 1;
00222 s = err + 2;
00223
00224 end = sce->st.st_size - 1;
00225 start = la;
00226 } else {
00227 error = 1;
00228 }
00229 } else {
00230
00231 error = 1;
00232 }
00233 } else {
00234
00235
00236 la = strtoll(s, &err, 10);
00237
00238 if (err == minus) {
00239 le = strtoll(minus+1, &err, 10);
00240
00241
00242 if (la > le) {
00243 error = 1;
00244 }
00245
00246 if (*err == '\0') {
00247
00248 s = err;
00249
00250 end = le;
00251 start = la;
00252 } else if (*err == ',') {
00253 multipart = 1;
00254 s = err + 1;
00255
00256 end = le;
00257 start = la;
00258 } else {
00259
00260
00261 error = 1;
00262 }
00263 } else {
00264
00265
00266 error = 1;
00267 }
00268 }
00269
00270 if (!error) {
00271 if (start < 0) start = 0;
00272
00273
00274 if (end > sce->st.st_size - 1) end = sce->st.st_size - 1;
00275
00276 if (start > sce->st.st_size - 1) {
00277 error = 1;
00278
00279 con->http_status = 416;
00280 }
00281 }
00282
00283 if (!error) {
00284 if (multipart) {
00285
00286 buffer *b;
00287
00288 b = chunkqueue_get_append_buffer(con->write_queue);
00289
00290 buffer_copy_string(b, "\r\n--");
00291 buffer_append_string(b, boundary);
00292
00293
00294 buffer_append_string(b, "\r\nContent-Range: bytes ");
00295 buffer_append_off_t(b, start);
00296 buffer_append_string(b, "-");
00297 buffer_append_off_t(b, end);
00298 buffer_append_string(b, "/");
00299 buffer_append_off_t(b, sce->st.st_size);
00300
00301 buffer_append_string(b, "\r\nContent-Type: ");
00302 buffer_append_string_buffer(b, content_type);
00303
00304
00305 buffer_append_string(b, "\r\n\r\n");
00306
00307 con->response.content_length += b->used - 1;
00308
00309 }
00310
00311 chunkqueue_append_file(con->write_queue, con->physical.path, start, end - start + 1);
00312 con->response.content_length += end - start + 1;
00313 }
00314 }
00315
00316
00317 if (error) return -1;
00318
00319 if (multipart) {
00320
00321 buffer *b;
00322
00323 b = chunkqueue_get_append_buffer(con->write_queue);
00324
00325 buffer_copy_string_len(b, "\r\n--", 4);
00326 buffer_append_string(b, boundary);
00327 buffer_append_string_len(b, "--\r\n", 4);
00328
00329 con->response.content_length += b->used - 1;
00330
00331
00332
00333 buffer_copy_string(p->range_buf, "multipart/byteranges; boundary=");
00334 buffer_append_string(p->range_buf, boundary);
00335
00336
00337 response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->range_buf));
00338 } else {
00339
00340
00341 buffer_copy_string(p->range_buf, "bytes ");
00342 buffer_append_off_t(p->range_buf, start);
00343 buffer_append_string(p->range_buf, "-");
00344 buffer_append_off_t(p->range_buf, end);
00345 buffer_append_string(p->range_buf, "/");
00346 buffer_append_off_t(p->range_buf, sce->st.st_size);
00347
00348 response_header_insert(srv, con, CONST_STR_LEN("Content-Range"), CONST_BUF_LEN(p->range_buf));
00349 }
00350
00351
00352 return 0;
00353 }
00354
00355 URIHANDLER_FUNC(mod_staticfile_subrequest) {
00356 plugin_data *p = p_d;
00357 size_t k;
00358 int s_len;
00359 stat_cache_entry *sce = NULL;
00360 buffer *mtime;
00361 data_string *ds;
00362
00363
00364 if (con->http_status != 0) return HANDLER_GO_ON;
00365 if (con->uri.path->used == 0) return HANDLER_GO_ON;
00366 if (con->physical.path->used == 0) return HANDLER_GO_ON;
00367
00368
00369 if (con->mode != DIRECT) return HANDLER_GO_ON;
00370
00371
00372 switch(con->request.http_method) {
00373 case HTTP_METHOD_GET:
00374 case HTTP_METHOD_POST:
00375 case HTTP_METHOD_HEAD:
00376 break;
00377 default:
00378 return HANDLER_GO_ON;
00379 }
00380
00381 mod_staticfile_patch_connection(srv, con, p);
00382
00383 s_len = con->uri.path->used - 1;
00384
00385
00386 for (k = 0; k < p->conf.exclude_ext->used; k++) {
00387 ds = (data_string *)p->conf.exclude_ext->data[k];
00388
00389 if (ds->value->used == 0) continue;
00390
00391 if (buffer_is_equal_right_len(con->physical.path, ds->value, ds->value->used - 1)) {
00392 return HANDLER_GO_ON;
00393 }
00394 }
00395
00396
00397 if (con->conf.log_request_handling) {
00398 log_error_write(srv, __FILE__, __LINE__, "s", "-- handling file as static file");
00399 }
00400
00401 if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
00402 con->http_status = 403;
00403
00404 log_error_write(srv, __FILE__, __LINE__, "sbsb",
00405 "not a regular file:", con->uri.path,
00406 "->", con->physical.path);
00407
00408 return HANDLER_FINISHED;
00409 }
00410
00411
00412 #ifdef HAVE_LSTAT
00413 if ((sce->is_symlink == 1) && !con->conf.follow_symlink) {
00414 con->http_status = 403;
00415
00416 if (con->conf.log_request_handling) {
00417 log_error_write(srv, __FILE__, __LINE__, "s", "-- access denied due symlink restriction");
00418 log_error_write(srv, __FILE__, __LINE__, "sb", "Path :", con->physical.path);
00419 }
00420
00421 buffer_reset(con->physical.path);
00422 return HANDLER_FINISHED;
00423 }
00424 #endif
00425
00426 #if 0
00427 if (!S_ISREG(sce->st.st_mode)) {
00428 con->http_status = 404;
00429
00430 if (con->conf.log_file_not_found) {
00431 log_error_write(srv, __FILE__, __LINE__, "sbsb",
00432 "not a regular file:", con->uri.path,
00433 "->", sce->name);
00434 }
00435
00436 return HANDLER_FINISHED;
00437 }
00438 #endif
00439 #if 1
00440 if (con->conf.log_request_handling) {
00441 log_error_write(srv, __FILE__, __LINE__, "sd", "sce->st.st_mode=", (int) (sce->st.st_mode));
00442 log_error_write(srv, __FILE__, __LINE__, "sd", "sce->st.st_size=", (int) (sce->st.st_size));
00443
00444 log_error_write(srv, __FILE__, __LINE__, "ss", "con->physical.path=", con->physical.path->ptr);
00445
00446 }
00447
00448 off_t pseudo_size=0;
00449 FILE * nonregfile;
00450 if (!S_ISREG(sce->st.st_mode)) {
00451
00452 if ((nonregfile= fopen(con->physical.path->ptr, "r"))) {
00453 fseek (nonregfile,0,SEEK_END);
00454 pseudo_size=ftello(nonregfile);
00455 fclose (nonregfile);
00456 }
00457 }
00458 if (con->conf.log_request_handling) {
00459 log_error_write(srv, __FILE__, __LINE__, "sd", "pseudo_size=", (int) pseudo_size);
00460 }
00461 #endif
00462
00463
00464
00465
00466 if (NULL == array_get_element(con->response.headers, "Content-Type")) {
00467 if (buffer_is_empty(sce->content_type)) {
00468 response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("application/octet-stream"));
00469 } else {
00470 response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type));
00471 }
00472 }
00473
00474 if (NULL == array_get_element(con->response.headers, "ETag")) {
00475
00476 etag_mutate(con->physical.etag, sce->etag);
00477
00478 response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag));
00479 }
00480 response_header_overwrite(srv, con, CONST_STR_LEN("Accept-Ranges"), CONST_STR_LEN("bytes"));
00481
00482
00483 if (NULL == (ds = (data_string *)array_get_element(con->response.headers, "Last-Modified"))) {
00484 mtime = strftime_cache_get(srv, sce->st.st_mtime);
00485 response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime));
00486 } else {
00487 mtime = ds->value;
00488 }
00489
00490 if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) {
00491 return HANDLER_FINISHED;
00492 } else if (con->request.http_range && con->conf.range_requests) {
00493 int do_range_request = 1;
00494
00495
00496 if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "If-Range"))) {
00497
00498
00499
00500 if (!buffer_is_equal(ds->value, con->physical.etag)) {
00501 do_range_request = 0;
00502 }
00503 }
00504
00505 if (do_range_request) {
00506
00507 con->file_finished = 1;
00508
00509 if (0 == http_response_parse_range(srv, con, p)) {
00510 con->http_status = 206;
00511 }
00512 return HANDLER_FINISHED;
00513 }
00514 }
00515
00516
00517
00518
00519
00520
00521 #if 0
00522 http_chunk_append_file(srv, con, con->physical.path, 0, sce->st.st_size);
00523 #else
00524 if (pseudo_size) http_chunk_append_file(srv, con, con->physical.path, 0, pseudo_size);
00525 else http_chunk_append_file(srv, con, con->physical.path, 0, sce->st.st_size);
00526 #endif
00527 con->file_finished = 1;
00528
00529 return HANDLER_FINISHED;
00530 }
00531
00532
00533
00534 int mod_staticfile_plugin_init(plugin *p) {
00535 p->version = LIGHTTPD_VERSION_ID;
00536 p->name = buffer_init_string("staticfile");
00537
00538 p->init = mod_staticfile_init;
00539 p->handle_subrequest_start = mod_staticfile_subrequest;
00540 p->set_defaults = mod_staticfile_set_defaults;
00541 p->cleanup = mod_staticfile_free;
00542
00543 p->data = NULL;
00544
00545 return 0;
00546 }