apps/web/lighttpd-1.4.13/src/mod_staticfile.c

Go to the documentation of this file.
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 *! *!  $Log: mod_staticfile.c,v $
00023 *! *!  Revision 1.1.1.1  2008/11/27 20:04:01  elphel
00024 *! *!
00025 *! *!
00026 *! *!  Revision 1.1.1.1  2007/09/21 06:15:02  elphel
00027 *! *!  This is a fresh tree based on elphel353-2.10
00028 *! *!
00029 *! *!  Revision 1.1  2007/09/21 06:15:02  elphel
00030 *! *!  modified lighttpd to be able to serve /dev/curcbuf
00031 *! *!
00032 
00033 */
00034 
00035 
00036 /* plugin config for all request/connections */
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 /* init the plugin data */
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 /* detroy the plugin data */
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 /* handle plugin config and check values */
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 },       /* 0 */
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         /* skip the first, the global context */
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                 /* condition didn't match */
00136                 if (!config_check_cond(srv, con, dc)) continue;
00137 
00138                 /* merge config */
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                         /* -<stop> */
00182 
00183                         le = strtoll(s, &err, 10);
00184 
00185                         if (le == 0) {
00186                                 /* RFC 2616 - 14.35.1 */
00187 
00188                                 con->http_status = 416;
00189                                 error = 1;
00190                         } else if (*err == '\0') {
00191                                 /* end */
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                         /* <start>- */
00208 
00209                         la = strtoll(s, &err, 10);
00210 
00211                         if (err == minus) {
00212                                 /* ok */
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                                 /* error */
00231                                 error = 1;
00232                         }
00233                 } else {
00234                         /* <start>-<stop> */
00235 
00236                         la = strtoll(s, &err, 10);
00237 
00238                         if (err == minus) {
00239                                 le = strtoll(minus+1, &err, 10);
00240 
00241                                 /* RFC 2616 - 14.35.1 */
00242                                 if (la > le) {
00243                                         error = 1;
00244                                 }
00245 
00246                                 if (*err == '\0') {
00247                                         /* ok, end*/
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                                         /* error */
00260 
00261                                         error = 1;
00262                                 }
00263                         } else {
00264                                 /* error */
00265 
00266                                 error = 1;
00267                         }
00268                 }
00269 
00270                 if (!error) {
00271                         if (start < 0) start = 0;
00272 
00273                         /* RFC 2616 - 14.35.1 */
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                                 /* write boundary-header */
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                                 /* write Content-Range */
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                                 /* write END-OF-HEADER */
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         /* something went wrong */
00317         if (error) return -1;
00318 
00319         if (multipart) {
00320                 /* add boundary end */
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                 /* set header-fields */
00332 
00333                 buffer_copy_string(p->range_buf, "multipart/byteranges; boundary=");
00334                 buffer_append_string(p->range_buf, boundary);
00335 
00336                 /* overwrite content-type */
00337                 response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->range_buf));
00338         } else {
00339                 /* add Content-Range-header */
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         /* ok, the file is set-up */
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         /* someone else has done a decision for us */
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         /* someone else has handled this request */
00369         if (con->mode != DIRECT) return HANDLER_GO_ON;
00370 
00371         /* we only handle GET, POST and HEAD */
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         /* ignore certain extensions */
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         /* we only handline regular files */
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 // disabling check it is a regular file - see if it will work with circbuf
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 //              log_error_write(srv, __FILE__, __LINE__,  "sb", "con->physical.path=", con->physical.path);
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 // open file and see it size;
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         /* mod_compress might set several data directly, don't overwrite them */
00463 
00464         /* set response content-type, if not set already */
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                 /* generate e-tag */
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         /* prepare header */
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                 /* check if we have a conditional GET */
00495 
00496                 if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "If-Range"))) {
00497                         /* if the value is the same as our ETag, we do a Range-request,
00498                          * otherwise a full 200 */
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                         /* content prepared, I'm done */
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         /* if we are still here, prepare body */
00517 
00518         /* we add it here for all requests
00519          * the HEAD request will drop it afterwards again
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 /* this function is called at dlopen() time and inits the callbacks */
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 }

Generated on Fri Nov 28 00:06:23 2008 for elphel by  doxygen 1.5.1