Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.15.12 ]​[ nginx-1.16.0 ]​

0001 
0002 /*
0003  * Copyright (C) Igor Sysoev
0004  * Copyright (C) Nginx, Inc.
0005  */
0006 
0007 
0008 #include <ngx_config.h>
0009 #include <ngx_core.h>
0010 #include <ngx_http.h>
0011 
0012 
0013 #if 0
0014 
0015 typedef struct {
0016     ngx_buf_t     *buf;
0017     size_t         size;
0018     ngx_pool_t    *pool;
0019     size_t         alloc_size;
0020     ngx_chain_t  **last_out;
0021 } ngx_http_autoindex_ctx_t;
0022 
0023 #endif
0024 
0025 
0026 typedef struct {
0027     ngx_str_t      name;
0028     size_t         utf_len;
0029     size_t         escape;
0030     size_t         escape_html;
0031 
0032     unsigned       dir:1;
0033     unsigned       file:1;
0034 
0035     time_t         mtime;
0036     off_t          size;
0037 } ngx_http_autoindex_entry_t;
0038 
0039 
0040 typedef struct {
0041     ngx_flag_t     enable;
0042     ngx_uint_t     format;
0043     ngx_flag_t     localtime;
0044     ngx_flag_t     exact_size;
0045 } ngx_http_autoindex_loc_conf_t;
0046 
0047 
0048 #define NGX_HTTP_AUTOINDEX_HTML         0
0049 #define NGX_HTTP_AUTOINDEX_JSON         1
0050 #define NGX_HTTP_AUTOINDEX_JSONP        2
0051 #define NGX_HTTP_AUTOINDEX_XML          3
0052 
0053 #define NGX_HTTP_AUTOINDEX_PREALLOCATE  50
0054 
0055 #define NGX_HTTP_AUTOINDEX_NAME_LEN     50
0056 
0057 
0058 static ngx_buf_t *ngx_http_autoindex_html(ngx_http_request_t *r,
0059     ngx_array_t *entries);
0060 static ngx_buf_t *ngx_http_autoindex_json(ngx_http_request_t *r,
0061     ngx_array_t *entries, ngx_str_t *callback);
0062 static ngx_int_t ngx_http_autoindex_jsonp_callback(ngx_http_request_t *r,
0063     ngx_str_t *callback);
0064 static ngx_buf_t *ngx_http_autoindex_xml(ngx_http_request_t *r,
0065     ngx_array_t *entries);
0066 
0067 static int ngx_libc_cdecl ngx_http_autoindex_cmp_entries(const void *one,
0068     const void *two);
0069 static ngx_int_t ngx_http_autoindex_error(ngx_http_request_t *r,
0070     ngx_dir_t *dir, ngx_str_t *name);
0071 
0072 static ngx_int_t ngx_http_autoindex_init(ngx_conf_t *cf);
0073 static void *ngx_http_autoindex_create_loc_conf(ngx_conf_t *cf);
0074 static char *ngx_http_autoindex_merge_loc_conf(ngx_conf_t *cf,
0075     void *parent, void *child);
0076 
0077 
0078 static ngx_conf_enum_t  ngx_http_autoindex_format[] = {
0079     { ngx_string("html"), NGX_HTTP_AUTOINDEX_HTML },
0080     { ngx_string("json"), NGX_HTTP_AUTOINDEX_JSON },
0081     { ngx_string("jsonp"), NGX_HTTP_AUTOINDEX_JSONP },
0082     { ngx_string("xml"), NGX_HTTP_AUTOINDEX_XML },
0083     { ngx_null_string, 0 }
0084 };
0085 
0086 
0087 static ngx_command_t  ngx_http_autoindex_commands[] = {
0088 
0089     { ngx_string("autoindex"),
0090       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
0091       ngx_conf_set_flag_slot,
0092       NGX_HTTP_LOC_CONF_OFFSET,
0093       offsetof(ngx_http_autoindex_loc_conf_t, enable),
0094       NULL },
0095 
0096     { ngx_string("autoindex_format"),
0097       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
0098       ngx_conf_set_enum_slot,
0099       NGX_HTTP_LOC_CONF_OFFSET,
0100       offsetof(ngx_http_autoindex_loc_conf_t, format),
0101       &ngx_http_autoindex_format },
0102 
0103     { ngx_string("autoindex_localtime"),
0104       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
0105       ngx_conf_set_flag_slot,
0106       NGX_HTTP_LOC_CONF_OFFSET,
0107       offsetof(ngx_http_autoindex_loc_conf_t, localtime),
0108       NULL },
0109 
0110     { ngx_string("autoindex_exact_size"),
0111       NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
0112       ngx_conf_set_flag_slot,
0113       NGX_HTTP_LOC_CONF_OFFSET,
0114       offsetof(ngx_http_autoindex_loc_conf_t, exact_size),
0115       NULL },
0116 
0117       ngx_null_command
0118 };
0119 
0120 
0121 static ngx_http_module_t  ngx_http_autoindex_module_ctx = {
0122     NULL,                                  /* preconfiguration */
0123     ngx_http_autoindex_init,               /* postconfiguration */
0124 
0125     NULL,                                  /* create main configuration */
0126     NULL,                                  /* init main configuration */
0127 
0128     NULL,                                  /* create server configuration */
0129     NULL,                                  /* merge server configuration */
0130 
0131     ngx_http_autoindex_create_loc_conf,    /* create location configuration */
0132     ngx_http_autoindex_merge_loc_conf      /* merge location configuration */
0133 };
0134 
0135 
0136 ngx_module_t  ngx_http_autoindex_module = {
0137     NGX_MODULE_V1,
0138     &ngx_http_autoindex_module_ctx,        /* module context */
0139     ngx_http_autoindex_commands,           /* module directives */
0140     NGX_HTTP_MODULE,                       /* module type */
0141     NULL,                                  /* init master */
0142     NULL,                                  /* init module */
0143     NULL,                                  /* init process */
0144     NULL,                                  /* init thread */
0145     NULL,                                  /* exit thread */
0146     NULL,                                  /* exit process */
0147     NULL,                                  /* exit master */
0148     NGX_MODULE_V1_PADDING
0149 };
0150 
0151 
0152 static ngx_int_t
0153 ngx_http_autoindex_handler(ngx_http_request_t *r)
0154 {
0155     u_char                         *last, *filename;
0156     size_t                          len, allocated, root;
0157     ngx_err_t                       err;
0158     ngx_buf_t                      *b;
0159     ngx_int_t                       rc;
0160     ngx_str_t                       path, callback;
0161     ngx_dir_t                       dir;
0162     ngx_uint_t                      level, format;
0163     ngx_pool_t                     *pool;
0164     ngx_chain_t                     out;
0165     ngx_array_t                     entries;
0166     ngx_http_autoindex_entry_t     *entry;
0167     ngx_http_autoindex_loc_conf_t  *alcf;
0168 
0169     if (r->uri.data[r->uri.len - 1] != '/') {
0170         return NGX_DECLINED;
0171     }
0172 
0173     if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
0174         return NGX_DECLINED;
0175     }
0176 
0177     alcf = ngx_http_get_module_loc_conf(r, ngx_http_autoindex_module);
0178 
0179     if (!alcf->enable) {
0180         return NGX_DECLINED;
0181     }
0182 
0183     rc = ngx_http_discard_request_body(r);
0184 
0185     if (rc != NGX_OK) {
0186         return rc;
0187     }
0188 
0189     last = ngx_http_map_uri_to_path(r, &path, &root,
0190                                     NGX_HTTP_AUTOINDEX_PREALLOCATE);
0191     if (last == NULL) {
0192         return NGX_HTTP_INTERNAL_SERVER_ERROR;
0193     }
0194 
0195     allocated = path.len;
0196     path.len = last - path.data;
0197     if (path.len > 1) {
0198         path.len--;
0199     }
0200     path.data[path.len] = '\0';
0201 
0202     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0203                    "http autoindex: \"%s\"", path.data);
0204 
0205     format = alcf->format;
0206 
0207     if (format == NGX_HTTP_AUTOINDEX_JSONP) {
0208         if (ngx_http_autoindex_jsonp_callback(r, &callback) != NGX_OK) {
0209             return NGX_HTTP_BAD_REQUEST;
0210         }
0211 
0212         if (callback.len == 0) {
0213             format = NGX_HTTP_AUTOINDEX_JSON;
0214         }
0215     }
0216 
0217     if (ngx_open_dir(&path, &dir) == NGX_ERROR) {
0218         err = ngx_errno;
0219 
0220         if (err == NGX_ENOENT
0221             || err == NGX_ENOTDIR
0222             || err == NGX_ENAMETOOLONG)
0223         {
0224             level = NGX_LOG_ERR;
0225             rc = NGX_HTTP_NOT_FOUND;
0226 
0227         } else if (err == NGX_EACCES) {
0228             level = NGX_LOG_ERR;
0229             rc = NGX_HTTP_FORBIDDEN;
0230 
0231         } else {
0232             level = NGX_LOG_CRIT;
0233             rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
0234         }
0235 
0236         ngx_log_error(level, r->connection->log, err,
0237                       ngx_open_dir_n " \"%s\" failed", path.data);
0238 
0239         return rc;
0240     }
0241 
0242 #if (NGX_SUPPRESS_WARN)
0243 
0244     /* MSVC thinks 'entries' may be used without having been initialized */
0245     ngx_memzero(&entries, sizeof(ngx_array_t));
0246 
0247 #endif
0248 
0249     /* TODO: pool should be temporary pool */
0250     pool = r->pool;
0251 
0252     if (ngx_array_init(&entries, pool, 40, sizeof(ngx_http_autoindex_entry_t))
0253         != NGX_OK)
0254     {
0255         return ngx_http_autoindex_error(r, &dir, &path);
0256     }
0257 
0258     r->headers_out.status = NGX_HTTP_OK;
0259 
0260     switch (format) {
0261 
0262     case NGX_HTTP_AUTOINDEX_JSON:
0263         ngx_str_set(&r->headers_out.content_type, "application/json");
0264         break;
0265 
0266     case NGX_HTTP_AUTOINDEX_JSONP:
0267         ngx_str_set(&r->headers_out.content_type, "application/javascript");
0268         break;
0269 
0270     case NGX_HTTP_AUTOINDEX_XML:
0271         ngx_str_set(&r->headers_out.content_type, "text/xml");
0272         ngx_str_set(&r->headers_out.charset, "utf-8");
0273         break;
0274 
0275     default: /* NGX_HTTP_AUTOINDEX_HTML */
0276         ngx_str_set(&r->headers_out.content_type, "text/html");
0277         break;
0278     }
0279 
0280     r->headers_out.content_type_len = r->headers_out.content_type.len;
0281     r->headers_out.content_type_lowcase = NULL;
0282 
0283     rc = ngx_http_send_header(r);
0284 
0285     if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
0286         if (ngx_close_dir(&dir) == NGX_ERROR) {
0287             ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
0288                           ngx_close_dir_n " \"%V\" failed", &path);
0289         }
0290 
0291         return rc;
0292     }
0293 
0294     filename = path.data;
0295     filename[path.len] = '/';
0296 
0297     for ( ;; ) {
0298         ngx_set_errno(0);
0299 
0300         if (ngx_read_dir(&dir) == NGX_ERROR) {
0301             err = ngx_errno;
0302 
0303             if (err != NGX_ENOMOREFILES) {
0304                 ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
0305                               ngx_read_dir_n " \"%V\" failed", &path);
0306                 return ngx_http_autoindex_error(r, &dir, &path);
0307             }
0308 
0309             break;
0310         }
0311 
0312         ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
0313                        "http autoindex file: \"%s\"", ngx_de_name(&dir));
0314 
0315         len = ngx_de_namelen(&dir);
0316 
0317         if (ngx_de_name(&dir)[0] == '.') {
0318             continue;
0319         }
0320 
0321         if (!dir.valid_info) {
0322 
0323             /* 1 byte for '/' and 1 byte for terminating '\0' */
0324 
0325             if (path.len + 1 + len + 1 > allocated) {
0326                 allocated = path.len + 1 + len + 1
0327                                      + NGX_HTTP_AUTOINDEX_PREALLOCATE;
0328 
0329                 filename = ngx_pnalloc(pool, allocated);
0330                 if (filename == NULL) {
0331                     return ngx_http_autoindex_error(r, &dir, &path);
0332                 }
0333 
0334                 last = ngx_cpystrn(filename, path.data, path.len + 1);
0335                 *last++ = '/';
0336             }
0337 
0338             ngx_cpystrn(last, ngx_de_name(&dir), len + 1);
0339 
0340             if (ngx_de_info(filename, &dir) == NGX_FILE_ERROR) {
0341                 err = ngx_errno;
0342 
0343                 if (err != NGX_ENOENT && err != NGX_ELOOP) {
0344                     ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
0345                                   ngx_de_info_n " \"%s\" failed", filename);
0346 
0347                     if (err == NGX_EACCES) {
0348                         continue;
0349                     }
0350 
0351                     return ngx_http_autoindex_error(r, &dir, &path);
0352                 }
0353 
0354                 if (ngx_de_link_info(filename, &dir) == NGX_FILE_ERROR) {
0355                     ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
0356                                   ngx_de_link_info_n " \"%s\" failed",
0357                                   filename);
0358                     return ngx_http_autoindex_error(r, &dir, &path);
0359                 }
0360             }
0361         }
0362 
0363         entry = ngx_array_push(&entries);
0364         if (entry == NULL) {
0365             return ngx_http_autoindex_error(r, &dir, &path);
0366         }
0367 
0368         entry->name.len = len;
0369 
0370         entry->name.data = ngx_pnalloc(pool, len + 1);
0371         if (entry->name.data == NULL) {
0372             return ngx_http_autoindex_error(r, &dir, &path);
0373         }
0374 
0375         ngx_cpystrn(entry->name.data, ngx_de_name(&dir), len + 1);
0376 
0377         entry->dir = ngx_de_is_dir(&dir);
0378         entry->file = ngx_de_is_file(&dir);
0379         entry->mtime = ngx_de_mtime(&dir);
0380         entry->size = ngx_de_size(&dir);
0381     }
0382 
0383     if (ngx_close_dir(&dir) == NGX_ERROR) {
0384         ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
0385                       ngx_close_dir_n " \"%V\" failed", &path);
0386     }
0387 
0388     if (entries.nelts > 1) {
0389         ngx_qsort(entries.elts, (size_t) entries.nelts,
0390                   sizeof(ngx_http_autoindex_entry_t),
0391                   ngx_http_autoindex_cmp_entries);
0392     }
0393 
0394     switch (format) {
0395 
0396     case NGX_HTTP_AUTOINDEX_JSON:
0397         b = ngx_http_autoindex_json(r, &entries, NULL);
0398         break;
0399 
0400     case NGX_HTTP_AUTOINDEX_JSONP:
0401         b = ngx_http_autoindex_json(r, &entries, &callback);
0402         break;
0403 
0404     case NGX_HTTP_AUTOINDEX_XML:
0405         b = ngx_http_autoindex_xml(r, &entries);
0406         break;
0407 
0408     default: /* NGX_HTTP_AUTOINDEX_HTML */
0409         b = ngx_http_autoindex_html(r, &entries);
0410         break;
0411     }
0412 
0413     if (b == NULL) {
0414         return NGX_ERROR;
0415     }
0416 
0417     /* TODO: free temporary pool */
0418 
0419     if (r == r->main) {
0420         b->last_buf = 1;
0421     }
0422 
0423     b->last_in_chain = 1;
0424 
0425     out.buf = b;
0426     out.next = NULL;
0427 
0428     return ngx_http_output_filter(r, &out);
0429 }
0430 
0431 
0432 static ngx_buf_t *
0433 ngx_http_autoindex_html(ngx_http_request_t *r, ngx_array_t *entries)
0434 {
0435     u_char                         *last, scale;
0436     off_t                           length;
0437     size_t                          len, entry_len, char_len, escape_html;
0438     ngx_tm_t                        tm;
0439     ngx_buf_t                      *b;
0440     ngx_int_t                       size;
0441     ngx_uint_t                      i, utf8;
0442     ngx_time_t                     *tp;
0443     ngx_http_autoindex_entry_t     *entry;
0444     ngx_http_autoindex_loc_conf_t  *alcf;
0445 
0446     static u_char  title[] =
0447         "<html>" CRLF
0448         "<head><title>Index of "
0449     ;
0450 
0451     static u_char  header[] =
0452         "</title></head>" CRLF
0453         "<body>" CRLF
0454         "<h1>Index of "
0455     ;
0456 
0457     static u_char  tail[] =
0458         "</body>" CRLF
0459         "</html>" CRLF
0460     ;
0461 
0462     static char  *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
0463                                "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
0464 
0465     if (r->headers_out.charset.len == 5
0466         && ngx_strncasecmp(r->headers_out.charset.data, (u_char *) "utf-8", 5)
0467            == 0)
0468     {
0469         utf8 = 1;
0470 
0471     } else {
0472         utf8 = 0;
0473     }
0474 
0475     escape_html = ngx_escape_html(NULL, r->uri.data, r->uri.len);
0476 
0477     len = sizeof(title) - 1
0478           + r->uri.len + escape_html
0479           + sizeof(header) - 1
0480           + r->uri.len + escape_html
0481           + sizeof("</h1>") - 1
0482           + sizeof("<hr><pre><a href=\"../\">../</a>" CRLF) - 1
0483           + sizeof("</pre><hr>") - 1
0484           + sizeof(tail) - 1;
0485 
0486     entry = entries->elts;
0487     for (i = 0; i < entries->nelts; i++) {
0488         entry[i].escape = 2 * ngx_escape_uri(NULL, entry[i].name.data,
0489                                              entry[i].name.len,
0490                                              NGX_ESCAPE_URI_COMPONENT);
0491 
0492         entry[i].escape_html = ngx_escape_html(NULL, entry[i].name.data,
0493                                                entry[i].name.len);
0494 
0495         if (utf8) {
0496             entry[i].utf_len = ngx_utf8_length(entry[i].name.data,
0497                                                entry[i].name.len);
0498         } else {
0499             entry[i].utf_len = entry[i].name.len;
0500         }
0501 
0502         entry_len = sizeof("<a href=\"") - 1
0503                   + entry[i].name.len + entry[i].escape
0504                   + 1                                    /* 1 is for "/" */
0505                   + sizeof("\">") - 1
0506                   + entry[i].name.len - entry[i].utf_len
0507                   + entry[i].escape_html
0508                   + NGX_HTTP_AUTOINDEX_NAME_LEN + sizeof("&gt;") - 2
0509                   + sizeof("</a>") - 1
0510                   + sizeof(" 28-Sep-1970 12:00 ") - 1
0511                   + 20                                   /* the file size */
0512                   + 2;
0513 
0514         if (len > NGX_MAX_SIZE_T_VALUE - entry_len) {
0515             return NULL;
0516         }
0517 
0518         len += entry_len;
0519     }
0520 
0521     b = ngx_create_temp_buf(r->pool, len);
0522     if (b == NULL) {
0523         return NULL;
0524     }
0525 
0526     b->last = ngx_cpymem(b->last, title, sizeof(title) - 1);
0527 
0528     if (escape_html) {
0529         b->last = (u_char *) ngx_escape_html(b->last, r->uri.data, r->uri.len);
0530         b->last = ngx_cpymem(b->last, header, sizeof(header) - 1);
0531         b->last = (u_char *) ngx_escape_html(b->last, r->uri.data, r->uri.len);
0532 
0533     } else {
0534         b->last = ngx_cpymem(b->last, r->uri.data, r->uri.len);
0535         b->last = ngx_cpymem(b->last, header, sizeof(header) - 1);
0536         b->last = ngx_cpymem(b->last, r->uri.data, r->uri.len);
0537     }
0538 
0539     b->last = ngx_cpymem(b->last, "</h1>", sizeof("</h1>") - 1);
0540 
0541     b->last = ngx_cpymem(b->last, "<hr><pre><a href=\"../\">../</a>" CRLF,
0542                          sizeof("<hr><pre><a href=\"../\">../</a>" CRLF) - 1);
0543 
0544     alcf = ngx_http_get_module_loc_conf(r, ngx_http_autoindex_module);
0545     tp = ngx_timeofday();
0546 
0547     for (i = 0; i < entries->nelts; i++) {
0548         b->last = ngx_cpymem(b->last, "<a href=\"", sizeof("<a href=\"") - 1);
0549 
0550         if (entry[i].escape) {
0551             ngx_escape_uri(b->last, entry[i].name.data, entry[i].name.len,
0552                            NGX_ESCAPE_URI_COMPONENT);
0553 
0554             b->last += entry[i].name.len + entry[i].escape;
0555 
0556         } else {
0557             b->last = ngx_cpymem(b->last, entry[i].name.data,
0558                                  entry[i].name.len);
0559         }
0560 
0561         if (entry[i].dir) {
0562             *b->last++ = '/';
0563         }
0564 
0565         *b->last++ = '"';
0566         *b->last++ = '>';
0567 
0568         len = entry[i].utf_len;
0569 
0570         if (entry[i].name.len != len) {
0571             if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) {
0572                 char_len = NGX_HTTP_AUTOINDEX_NAME_LEN - 3 + 1;
0573 
0574             } else {
0575                 char_len = NGX_HTTP_AUTOINDEX_NAME_LEN + 1;
0576             }
0577 
0578             last = b->last;
0579             b->last = ngx_utf8_cpystrn(b->last, entry[i].name.data,
0580                                        char_len, entry[i].name.len + 1);
0581 
0582             if (entry[i].escape_html) {
0583                 b->last = (u_char *) ngx_escape_html(last, entry[i].name.data,
0584                                                      b->last - last);
0585             }
0586 
0587             last = b->last;
0588 
0589         } else {
0590             if (entry[i].escape_html) {
0591                 if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) {
0592                     char_len = NGX_HTTP_AUTOINDEX_NAME_LEN - 3;
0593 
0594                 } else {
0595                     char_len = len;
0596                 }
0597 
0598                 b->last = (u_char *) ngx_escape_html(b->last,
0599                                                   entry[i].name.data, char_len);
0600                 last = b->last;
0601 
0602             } else {
0603                 b->last = ngx_cpystrn(b->last, entry[i].name.data,
0604                                       NGX_HTTP_AUTOINDEX_NAME_LEN + 1);
0605                 last = b->last - 3;
0606             }
0607         }
0608 
0609         if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) {
0610             b->last = ngx_cpymem(last, "..&gt;</a>", sizeof("..&gt;</a>") - 1);
0611 
0612         } else {
0613             if (entry[i].dir && NGX_HTTP_AUTOINDEX_NAME_LEN - len > 0) {
0614                 *b->last++ = '/';
0615                 len++;
0616             }
0617 
0618             b->last = ngx_cpymem(b->last, "</a>", sizeof("</a>") - 1);
0619 
0620             if (NGX_HTTP_AUTOINDEX_NAME_LEN - len > 0) {
0621                 ngx_memset(b->last, ' ', NGX_HTTP_AUTOINDEX_NAME_LEN - len);
0622                 b->last += NGX_HTTP_AUTOINDEX_NAME_LEN - len;
0623             }
0624         }
0625 
0626         *b->last++ = ' ';
0627 
0628         ngx_gmtime(entry[i].mtime + tp->gmtoff * 60 * alcf->localtime, &tm);
0629 
0630         b->last = ngx_sprintf(b->last, "%02d-%s-%d %02d:%02d ",
0631                               tm.ngx_tm_mday,
0632                               months[tm.ngx_tm_mon - 1],
0633                               tm.ngx_tm_year,
0634                               tm.ngx_tm_hour,
0635                               tm.ngx_tm_min);
0636 
0637         if (alcf->exact_size) {
0638             if (entry[i].dir) {
0639                 b->last = ngx_cpymem(b->last,  "                  -",
0640                                      sizeof("                  -") - 1);
0641             } else {
0642                 b->last = ngx_sprintf(b->last, "%19O", entry[i].size);
0643             }
0644 
0645         } else {
0646             if (entry[i].dir) {
0647                 b->last = ngx_cpymem(b->last,  "      -",
0648                                      sizeof("      -") - 1);
0649 
0650             } else {
0651                 length = entry[i].size;
0652 
0653                 if (length > 1024 * 1024 * 1024 - 1) {
0654                     size = (ngx_int_t) (length / (1024 * 1024 * 1024));
0655                     if ((length % (1024 * 1024 * 1024))
0656                                                 > (1024 * 1024 * 1024 / 2 - 1))
0657                     {
0658                         size++;
0659                     }
0660                     scale = 'G';
0661 
0662                 } else if (length > 1024 * 1024 - 1) {
0663                     size = (ngx_int_t) (length / (1024 * 1024));
0664                     if ((length % (1024 * 1024)) > (1024 * 1024 / 2 - 1)) {
0665                         size++;
0666                     }
0667                     scale = 'M';
0668 
0669                 } else if (length > 9999) {
0670                     size = (ngx_int_t) (length / 1024);
0671                     if (length % 1024 > 511) {
0672                         size++;
0673                     }
0674                     scale = 'K';
0675 
0676                 } else {
0677                     size = (ngx_int_t) length;
0678                     scale = '\0';
0679                 }
0680 
0681                 if (scale) {
0682                     b->last = ngx_sprintf(b->last, "%6i%c", size, scale);
0683 
0684                 } else {
0685                     b->last = ngx_sprintf(b->last, " %6i", size);
0686                 }
0687             }
0688         }
0689 
0690         *b->last++ = CR;
0691         *b->last++ = LF;
0692     }
0693 
0694     b->last = ngx_cpymem(b->last, "</pre><hr>", sizeof("</pre><hr>") - 1);
0695 
0696     b->last = ngx_cpymem(b->last, tail, sizeof(tail) - 1);
0697 
0698     return b;
0699 }
0700 
0701 
0702 static ngx_buf_t *
0703 ngx_http_autoindex_json(ngx_http_request_t *r, ngx_array_t *entries,
0704     ngx_str_t *callback)
0705 {
0706     size_t                       len, entry_len;
0707     ngx_buf_t                   *b;
0708     ngx_uint_t                   i;
0709     ngx_http_autoindex_entry_t  *entry;
0710 
0711     len = sizeof("[" CRLF CRLF "]") - 1;
0712 
0713     if (callback) {
0714         len += sizeof("/* callback */" CRLF "();") - 1 + callback->len;
0715     }
0716 
0717     entry = entries->elts;
0718 
0719     for (i = 0; i < entries->nelts; i++) {
0720         entry[i].escape = ngx_escape_json(NULL, entry[i].name.data,
0721                                           entry[i].name.len);
0722 
0723         entry_len = sizeof("{  }," CRLF) - 1
0724                   + sizeof("\"name\":\"\"") - 1
0725                   + entry[i].name.len + entry[i].escape
0726                   + sizeof(", \"type\":\"directory\"") - 1
0727                   + sizeof(", \"mtime\":\"Wed, 31 Dec 1986 10:00:00 GMT\"") - 1;
0728 
0729         if (entry[i].file) {
0730             entry_len += sizeof(", \"size\":") - 1 + NGX_OFF_T_LEN;
0731         }
0732 
0733         if (len > NGX_MAX_SIZE_T_VALUE - entry_len) {
0734             return NULL;
0735         }
0736 
0737         len += entry_len;
0738     }
0739 
0740     b = ngx_create_temp_buf(r->pool, len);
0741     if (b == NULL) {
0742         return NULL;
0743     }
0744 
0745     if (callback) {
0746         b->last = ngx_cpymem(b->last, "/* callback */" CRLF,
0747                              sizeof("/* callback */" CRLF) - 1);
0748 
0749         b->last = ngx_cpymem(b->last, callback->data, callback->len);
0750 
0751         *b->last++ = '(';
0752     }
0753 
0754     *b->last++ = '[';
0755 
0756     for (i = 0; i < entries->nelts; i++) {
0757         b->last = ngx_cpymem(b->last, CRLF "{ \"name\":\"",
0758                              sizeof(CRLF "{ \"name\":\"") - 1);
0759 
0760         if (entry[i].escape) {
0761             b->last = (u_char *) ngx_escape_json(b->last, entry[i].name.data,
0762                                                  entry[i].name.len);
0763         } else {
0764             b->last = ngx_cpymem(b->last, entry[i].name.data,
0765                                  entry[i].name.len);
0766         }
0767 
0768         b->last = ngx_cpymem(b->last, "\", \"type\":\"",
0769                              sizeof("\", \"type\":\"") - 1);
0770 
0771         if (entry[i].dir) {
0772             b->last = ngx_cpymem(b->last, "directory", sizeof("directory") - 1);
0773 
0774         } else if (entry[i].file) {
0775             b->last = ngx_cpymem(b->last, "file", sizeof("file") - 1);
0776 
0777         } else {
0778             b->last = ngx_cpymem(b->last, "other", sizeof("other") - 1);
0779         }
0780 
0781         b->last = ngx_cpymem(b->last, "\", \"mtime\":\"",
0782                              sizeof("\", \"mtime\":\"") - 1);
0783 
0784         b->last = ngx_http_time(b->last, entry[i].mtime);
0785 
0786         if (entry[i].file) {
0787             b->last = ngx_cpymem(b->last, "\", \"size\":",
0788                                  sizeof("\", \"size\":") - 1);
0789             b->last = ngx_sprintf(b->last, "%O", entry[i].size);
0790 
0791         } else {
0792             *b->last++ = '"';
0793         }
0794 
0795         b->last = ngx_cpymem(b->last, " },", sizeof(" },") - 1);
0796     }
0797 
0798     if (i > 0) {
0799         b->last--;  /* strip last comma */
0800     }
0801 
0802     b->last = ngx_cpymem(b->last, CRLF "]", sizeof(CRLF "]") - 1);
0803 
0804     if (callback) {
0805         *b->last++ = ')'; *b->last++ = ';';
0806     }
0807 
0808     return b;
0809 }
0810 
0811 
0812 static ngx_int_t
0813 ngx_http_autoindex_jsonp_callback(ngx_http_request_t *r, ngx_str_t *callback)
0814 {
0815     u_char      *p, c, ch;
0816     ngx_uint_t   i;
0817 
0818     if (ngx_http_arg(r, (u_char *) "callback", 8, callback) != NGX_OK) {
0819         callback->len = 0;
0820         return NGX_OK;
0821     }
0822 
0823     if (callback->len > 128) {
0824         ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
0825                       "client sent too long callback name: \"%V\"", callback);
0826         return NGX_DECLINED;
0827     }
0828 
0829     p = callback->data;
0830 
0831     for (i = 0; i < callback->len; i++) {
0832         ch = p[i];
0833 
0834         c = (u_char) (ch | 0x20);
0835         if (c >= 'a' && c <= 'z') {
0836             continue;
0837         }
0838 
0839         if ((ch >= '0' && ch <= '9') || ch == '_' || ch == '.') {
0840             continue;
0841         }
0842 
0843         ngx_log_error(NGX_LOG_INFO, r->connection->log, 0,
0844                       "client sent invalid callback name: \"%V\"", callback);
0845 
0846         return NGX_DECLINED;
0847     }
0848 
0849     return NGX_OK;
0850 }
0851 
0852 
0853 static ngx_buf_t *
0854 ngx_http_autoindex_xml(ngx_http_request_t *r, ngx_array_t *entries)
0855 {
0856     size_t                          len, entry_len;
0857     ngx_tm_t                        tm;
0858     ngx_buf_t                      *b;
0859     ngx_str_t                       type;
0860     ngx_uint_t                      i;
0861     ngx_http_autoindex_entry_t     *entry;
0862 
0863     static u_char  head[] = "<?xml version=\"1.0\"?>" CRLF "<list>" CRLF;
0864     static u_char  tail[] = "</list>" CRLF;
0865 
0866     len = sizeof(head) - 1 + sizeof(tail) - 1;
0867 
0868     entry = entries->elts;
0869 
0870     for (i = 0; i < entries->nelts; i++) {
0871         entry[i].escape = ngx_escape_html(NULL, entry[i].name.data,
0872                                           entry[i].name.len);
0873 
0874         entry_len = sizeof("<directory></directory>" CRLF) - 1
0875                   + entry[i].name.len + entry[i].escape
0876                   + sizeof(" mtime=\"1986-12-31T10:00:00Z\"") - 1;
0877 
0878         if (entry[i].file) {
0879             entry_len += sizeof(" size=\"\"") - 1 + NGX_OFF_T_LEN;
0880         }
0881 
0882         if (len > NGX_MAX_SIZE_T_VALUE - entry_len) {
0883             return NULL;
0884         }
0885 
0886         len += entry_len;
0887     }
0888 
0889     b = ngx_create_temp_buf(r->pool, len);
0890     if (b == NULL) {
0891         return NULL;
0892     }
0893 
0894     b->last = ngx_cpymem(b->last, head, sizeof(head) - 1);
0895 
0896     for (i = 0; i < entries->nelts; i++) {
0897         *b->last++ = '<';
0898 
0899         if (entry[i].dir) {
0900             ngx_str_set(&type, "directory");
0901 
0902         } else if (entry[i].file) {
0903             ngx_str_set(&type, "file");
0904 
0905         } else {
0906             ngx_str_set(&type, "other");
0907         }
0908 
0909         b->last = ngx_cpymem(b->last, type.data, type.len);
0910 
0911         b->last = ngx_cpymem(b->last, " mtime=\"", sizeof(" mtime=\"") - 1);
0912 
0913         ngx_gmtime(entry[i].mtime, &tm);
0914 
0915         b->last = ngx_sprintf(b->last, "%4d-%02d-%02dT%02d:%02d:%02dZ",
0916                               tm.ngx_tm_year, tm.ngx_tm_mon,
0917                               tm.ngx_tm_mday, tm.ngx_tm_hour,
0918                               tm.ngx_tm_min, tm.ngx_tm_sec);
0919 
0920         if (entry[i].file) {
0921             b->last = ngx_cpymem(b->last, "\" size=\"",
0922                                  sizeof("\" size=\"") - 1);
0923             b->last = ngx_sprintf(b->last, "%O", entry[i].size);
0924         }
0925 
0926         *b->last++ = '"'; *b->last++ = '>';
0927 
0928         if (entry[i].escape) {
0929             b->last = (u_char *) ngx_escape_html(b->last, entry[i].name.data,
0930                                                  entry[i].name.len);
0931         } else {
0932             b->last = ngx_cpymem(b->last, entry[i].name.data,
0933                                  entry[i].name.len);
0934         }
0935 
0936         *b->last++ = '<'; *b->last++ = '/';
0937 
0938         b->last = ngx_cpymem(b->last, type.data, type.len);
0939 
0940         *b->last++ = '>';
0941 
0942         *b->last++ = CR; *b->last++ = LF;
0943     }
0944 
0945     b->last = ngx_cpymem(b->last, tail, sizeof(tail) - 1);
0946 
0947     return b;
0948 }
0949 
0950 
0951 static int ngx_libc_cdecl
0952 ngx_http_autoindex_cmp_entries(const void *one, const void *two)
0953 {
0954     ngx_http_autoindex_entry_t *first = (ngx_http_autoindex_entry_t *) one;
0955     ngx_http_autoindex_entry_t *second = (ngx_http_autoindex_entry_t *) two;
0956 
0957     if (first->dir && !second->dir) {
0958         /* move the directories to the start */
0959         return -1;
0960     }
0961 
0962     if (!first->dir && second->dir) {
0963         /* move the directories to the start */
0964         return 1;
0965     }
0966 
0967     return (int) ngx_strcmp(first->name.data, second->name.data);
0968 }
0969 
0970 
0971 #if 0
0972 
0973 static ngx_buf_t *
0974 ngx_http_autoindex_alloc(ngx_http_autoindex_ctx_t *ctx, size_t size)
0975 {
0976     ngx_chain_t  *cl;
0977 
0978     if (ctx->buf) {
0979 
0980         if ((size_t) (ctx->buf->end - ctx->buf->last) >= size) {
0981             return ctx->buf;
0982         }
0983 
0984         ctx->size += ctx->buf->last - ctx->buf->pos;
0985     }
0986 
0987     ctx->buf = ngx_create_temp_buf(ctx->pool, ctx->alloc_size);
0988     if (ctx->buf == NULL) {
0989         return NULL;
0990     }
0991 
0992     cl = ngx_alloc_chain_link(ctx->pool);
0993     if (cl == NULL) {
0994         return NULL;
0995     }
0996 
0997     cl->buf = ctx->buf;
0998     cl->next = NULL;
0999 
1000     *ctx->last_out = cl;
1001     ctx->last_out = &cl->next;
1002 
1003     return ctx->buf;
1004 }
1005 
1006 #endif
1007 
1008 
1009 static ngx_int_t
1010 ngx_http_autoindex_error(ngx_http_request_t *r, ngx_dir_t *dir, ngx_str_t *name)
1011 {
1012     if (ngx_close_dir(dir) == NGX_ERROR) {
1013         ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
1014                       ngx_close_dir_n " \"%V\" failed", name);
1015     }
1016 
1017     return r->header_sent ? NGX_ERROR : NGX_HTTP_INTERNAL_SERVER_ERROR;
1018 }
1019 
1020 
1021 static void *
1022 ngx_http_autoindex_create_loc_conf(ngx_conf_t *cf)
1023 {
1024     ngx_http_autoindex_loc_conf_t  *conf;
1025 
1026     conf = ngx_palloc(cf->pool, sizeof(ngx_http_autoindex_loc_conf_t));
1027     if (conf == NULL) {
1028         return NULL;
1029     }
1030 
1031     conf->enable = NGX_CONF_UNSET;
1032     conf->format = NGX_CONF_UNSET_UINT;
1033     conf->localtime = NGX_CONF_UNSET;
1034     conf->exact_size = NGX_CONF_UNSET;
1035 
1036     return conf;
1037 }
1038 
1039 
1040 static char *
1041 ngx_http_autoindex_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
1042 {
1043     ngx_http_autoindex_loc_conf_t *prev = parent;
1044     ngx_http_autoindex_loc_conf_t *conf = child;
1045 
1046     ngx_conf_merge_value(conf->enable, prev->enable, 0);
1047     ngx_conf_merge_uint_value(conf->format, prev->format,
1048                               NGX_HTTP_AUTOINDEX_HTML);
1049     ngx_conf_merge_value(conf->localtime, prev->localtime, 0);
1050     ngx_conf_merge_value(conf->exact_size, prev->exact_size, 1);
1051 
1052     return NGX_CONF_OK;
1053 }
1054 
1055 
1056 static ngx_int_t
1057 ngx_http_autoindex_init(ngx_conf_t *cf)
1058 {
1059     ngx_http_handler_pt        *h;
1060     ngx_http_core_main_conf_t  *cmcf;
1061 
1062     cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
1063 
1064     h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
1065     if (h == NULL) {
1066         return NGX_ERROR;
1067     }
1068 
1069     *h = ngx_http_autoindex_handler;
1070 
1071     return NGX_OK;
1072 }