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