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