Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.13.12 ]​[ nginx-1.12.2 ]​

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