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  2007/09/21 06:15:02  elphel
00024 *! *!  This is a fresh tree based on elphel353-2.10
00025 *! *!
00026 *! *!  Revision 1.1  2007/09/21 06:15:02  elphel
00027 *! *!  modified lighttpd to be able to serve /dev/curcbuf
00028 *! *!
00029 
00030 */
00031 
00032 
00033 /* plugin config for all request/connections */
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 /* init the plugin data */
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 /* detroy the plugin data */
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 /* handle plugin config and check values */
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 },       /* 0 */
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         /* skip the first, the global context */
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                 /* condition didn't match */
00133                 if (!config_check_cond(srv, con, dc)) continue;
00134 
00135                 /* merge config */
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                         /* -<stop> */
00179 
00180                         le = strtoll(s, &err, 10);
00181 
00182                         if (le == 0) {
00183                                 /* RFC 2616 - 14.35.1 */
00184 
00185                                 con->http_status = 416;
00186                                 error = 1;
00187                         } else if (*err == '\0') {
00188                                 /* end */
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                         /* <start>- */
00205 
00206                         la = strtoll(s, &err, 10);
00207 
00208                         if (err == minus) {
00209                                 /* ok */
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                                 /* error */
00228                                 error = 1;
00229                         }
00230                 } else {
00231                         /* <start>-<stop> */
00232 
00233                         la = strtoll(s, &err, 10);
00234 
00235                         if (err == minus) {
00236                                 le = strtoll(minus+1, &err, 10);
00237 
00238                                 /* RFC 2616 - 14.35.1 */
00239                                 if (la > le) {
00240                                         error = 1;
00241                                 }
00242 
00243                                 if (*err == '\0') {
00244                                         /* ok, end*/
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                                         /* error */
00257 
00258                                         error = 1;
00259                                 }
00260                         } else {
00261                                 /* error */
00262 
00263                                 error = 1;
00264                         }
00265                 }
00266 
00267                 if (!error) {
00268                         if (start < 0) start = 0;
00269 
00270                         /* RFC 2616 - 14.35.1 */
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                                 /* write boundary-header */
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                                 /* write Content-Range */
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                                 /* write END-OF-HEADER */
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         /* something went wrong */
00314         if (error) return -1;
00315 
00316         if (multipart) {
00317                 /* add boundary end */
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                 /* set header-fields */
00329 
00330                 buffer_copy_string(p->range_buf, "multipart/byteranges; boundary=");
00331                 buffer_append_string(p->range_buf, boundary);
00332 
00333                 /* overwrite content-type */
00334                 response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->range_buf));
00335         } else {
00336                 /* add Content-Range-header */
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         /* ok, the file is set-up */
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         /* someone else has done a decision for us */
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         /* someone else has handled this request */
00366         if (con->mode != DIRECT) return HANDLER_GO_ON;
00367 
00368         /* we only handle GET, POST and HEAD */
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         /* ignore certain extensions */
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         /* we only handline regular files */
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 // disabling check it is a regular file - see if it will work with circbuf
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 //              log_error_write(srv, __FILE__, __LINE__,  "sb", "con->physical.path=", con->physical.path);
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 // open file and see it size;
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         /* mod_compress might set several data directly, don't overwrite them */
00460 
00461         /* set response content-type, if not set already */
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                 /* generate e-tag */
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         /* prepare header */
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                 /* check if we have a conditional GET */
00492 
00493                 if (NULL != (ds = (data_string *)array_get_element(con->request.headers, "If-Range"))) {
00494                         /* if the value is the same as our ETag, we do a Range-request,
00495                          * otherwise a full 200 */
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                         /* content prepared, I'm done */
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         /* if we are still here, prepare body */
00514 
00515         /* we add it here for all requests
00516          * the HEAD request will drop it afterwards again
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 /* this function is called at dlopen() time and inits the callbacks */
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 }

Generated on Thu Aug 7 16:18:59 2008 for elphel by  doxygen 1.5.1