# HG changeset patch # User Igor Sysoev # Date 1320156555 0 # Node ID 6efec8b1ff521faa6e59021c0e6dd5554cdcb116 # Parent 94049ec3eedae3c5c55b58f59ee4ddb9544d54da Merging r4193, r4194: Autoindex fixes: *) Autoindex: escape '?' in file names. For files with '?' in their names autoindex generated links with '?' not escaped. This resulted in effectively truncated links as '?' indicates query string start. This is an updated version of the patch originally posted at [1]. It introduces generic NGX_ESCAPE_URI_COMPONENT which escapes everything but unreserved characters as per RFC 3986. This approach also renders unneeded special colon processing (as colon is percent-encoded now), it's dropped accordingly. [1] http://nginx.org/pipermail/nginx-devel/2010-February/000112.html *) Autoindex: escape html in file names. diff --git a/src/core/ngx_string.c b/src/core/ngx_string.c --- a/src/core/ngx_string.c +++ b/src/core/ngx_string.c @@ -1380,6 +1380,26 @@ ngx_escape_uri(u_char *dst, u_char *src, 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ }; + /* not ALPHA, DIGIT, "-", ".", "_", "~" */ + + static uint32_t uri_component[] = { + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + + /* ?>=< ;:98 7654 3210 /.-, +*)( '&%$ #"! */ + 0xfc009fff, /* 1111 1100 0000 0000 1001 1111 1111 1111 */ + + /* _^]\ [ZYX WVUT SRQP ONML KJIH GFED CBA@ */ + 0x78000001, /* 0111 1000 0000 0000 0000 0000 0000 0001 */ + + /* ~}| {zyx wvut srqp onml kjih gfed cba` */ + 0xb8000001, /* 1011 1000 0000 0000 0000 0000 0000 0001 */ + + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff, /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + 0xffffffff /* 1111 1111 1111 1111 1111 1111 1111 1111 */ + }; + /* " ", "#", """, "%", "'", %00-%1F, %7F-%FF */ static uint32_t html[] = { @@ -1443,7 +1463,7 @@ ngx_escape_uri(u_char *dst, u_char *src, /* mail_auth is the same as memcached */ static uint32_t *map[] = - { uri, args, html, refresh, memcached, memcached }; + { uri, args, uri_component, html, refresh, memcached, memcached }; escape = map[type]; diff --git a/src/core/ngx_string.h b/src/core/ngx_string.h --- a/src/core/ngx_string.h +++ b/src/core/ngx_string.h @@ -189,12 +189,13 @@ size_t ngx_utf8_length(u_char *p, size_t u_char *ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len); -#define NGX_ESCAPE_URI 0 -#define NGX_ESCAPE_ARGS 1 -#define NGX_ESCAPE_HTML 2 -#define NGX_ESCAPE_REFRESH 3 -#define NGX_ESCAPE_MEMCACHED 4 -#define NGX_ESCAPE_MAIL_AUTH 5 +#define NGX_ESCAPE_URI 0 +#define NGX_ESCAPE_ARGS 1 +#define NGX_ESCAPE_URI_COMPONENT 2 +#define NGX_ESCAPE_HTML 3 +#define NGX_ESCAPE_REFRESH 4 +#define NGX_ESCAPE_MEMCACHED 5 +#define NGX_ESCAPE_MAIL_AUTH 6 #define NGX_UNESCAPE_URI 1 #define NGX_UNESCAPE_REDIRECT 2 diff --git a/src/http/modules/ngx_http_autoindex_module.c b/src/http/modules/ngx_http_autoindex_module.c --- a/src/http/modules/ngx_http_autoindex_module.c +++ b/src/http/modules/ngx_http_autoindex_module.c @@ -26,9 +26,9 @@ typedef struct { ngx_str_t name; size_t utf_len; size_t escape; + size_t escape_html; unsigned dir:1; - unsigned colon:1; time_t mtime; off_t size; @@ -138,7 +138,7 @@ ngx_http_autoindex_handler(ngx_http_requ { u_char *last, *filename, scale; off_t length; - size_t len, utf_len, allocated, root; + size_t len, char_len, escape_html, allocated, root; ngx_tm_t tm; ngx_err_t err; ngx_buf_t *b; @@ -338,7 +338,10 @@ ngx_http_autoindex_handler(ngx_http_requ ngx_cpystrn(entry->name.data, ngx_de_name(&dir), len + 1); entry->escape = 2 * ngx_escape_uri(NULL, ngx_de_name(&dir), len, - NGX_ESCAPE_HTML); + NGX_ESCAPE_URI_COMPONENT); + + entry->escape_html = ngx_escape_html(NULL, entry->name.data, + entry->name.len); if (utf8) { entry->utf_len = ngx_utf8_length(entry->name.data, entry->name.len); @@ -346,8 +349,6 @@ ngx_http_autoindex_handler(ngx_http_requ entry->utf_len = len; } - entry->colon = (ngx_strchr(entry->name.data, ':') != NULL); - entry->dir = ngx_de_is_dir(&dir); entry->mtime = ngx_de_mtime(&dir); entry->size = ngx_de_size(&dir); @@ -358,10 +359,12 @@ ngx_http_autoindex_handler(ngx_http_requ ngx_close_dir_n " \"%s\" failed", &path); } + escape_html = ngx_escape_html(NULL, r->uri.data, r->uri.len); + len = sizeof(title) - 1 - + r->uri.len + + r->uri.len + escape_html + sizeof(header) - 1 - + r->uri.len + + r->uri.len + escape_html + sizeof("") - 1 + sizeof("
../" CRLF) - 1
           + sizeof("

") - 1 @@ -373,7 +376,8 @@ ngx_http_autoindex_handler(ngx_http_requ + entry[i].name.len + entry[i].escape + 1 /* 1 is for "/" */ + sizeof("\">") - 1 - + entry[i].name.len - entry[i].utf_len + entry[i].colon * 2 + + entry[i].name.len - entry[i].utf_len + + entry[i].escape_html + NGX_HTTP_AUTOINDEX_NAME_LEN + sizeof(">") - 2 + sizeof("") - 1 + sizeof(" 28-Sep-1970 12:00 ") - 1 @@ -393,9 +397,18 @@ ngx_http_autoindex_handler(ngx_http_requ } b->last = ngx_cpymem(b->last, title, sizeof(title) - 1); - b->last = ngx_cpymem(b->last, r->uri.data, r->uri.len); - b->last = ngx_cpymem(b->last, header, sizeof(header) - 1); - b->last = ngx_cpymem(b->last, r->uri.data, r->uri.len); + + if (escape_html) { + b->last = (u_char *) ngx_escape_html(b->last, r->uri.data, r->uri.len); + b->last = ngx_cpymem(b->last, header, sizeof(header) - 1); + b->last = (u_char *) ngx_escape_html(b->last, r->uri.data, r->uri.len); + + } else { + b->last = ngx_cpymem(b->last, r->uri.data, r->uri.len); + b->last = ngx_cpymem(b->last, header, sizeof(header) - 1); + b->last = ngx_cpymem(b->last, r->uri.data, r->uri.len); + } + b->last = ngx_cpymem(b->last, "", sizeof("") - 1); b->last = ngx_cpymem(b->last, "
../" CRLF,
@@ -406,14 +419,9 @@ ngx_http_autoindex_handler(ngx_http_requ
     for (i = 0; i < entries.nelts; i++) {
         b->last = ngx_cpymem(b->last, "last++ = '.';
-            *b->last++ = '/';
-        }
-
         if (entry[i].escape) {
             ngx_escape_uri(b->last, entry[i].name.data, entry[i].name.len,
-                           NGX_ESCAPE_HTML);
+                           NGX_ESCAPE_URI_COMPONENT);
 
             b->last += entry[i].name.len + entry[i].escape;
 
@@ -433,20 +441,41 @@ ngx_http_autoindex_handler(ngx_http_requ
 
         if (entry[i].name.len != len) {
             if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) {
-                utf_len = NGX_HTTP_AUTOINDEX_NAME_LEN - 3 + 1;
+                char_len = NGX_HTTP_AUTOINDEX_NAME_LEN - 3 + 1;
 
             } else {
-                utf_len = NGX_HTTP_AUTOINDEX_NAME_LEN + 1;
+                char_len = NGX_HTTP_AUTOINDEX_NAME_LEN + 1;
             }
 
+            last = b->last;
             b->last = ngx_utf8_cpystrn(b->last, entry[i].name.data,
-                                       utf_len, entry[i].name.len + 1);
+                                       char_len, entry[i].name.len + 1);
+
+            if (entry[i].escape_html) {
+                b->last = (u_char *) ngx_escape_html(last, entry[i].name.data,
+                                                     b->last - last);
+            }
+
             last = b->last;
 
         } else {
-            b->last = ngx_cpystrn(b->last, entry[i].name.data,
-                                  NGX_HTTP_AUTOINDEX_NAME_LEN + 1);
-            last = b->last - 3;
+            if (entry[i].escape_html) {
+                if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) {
+                    char_len = NGX_HTTP_AUTOINDEX_NAME_LEN - 3;
+
+                } else {
+                    char_len = len;
+                }
+
+                b->last = (u_char *) ngx_escape_html(b->last,
+                                                  entry[i].name.data, char_len);
+                last = b->last;
+
+            } else {
+                b->last = ngx_cpystrn(b->last, entry[i].name.data,
+                                      NGX_HTTP_AUTOINDEX_NAME_LEN + 1);
+                last = b->last - 3;
+            }
         }
 
         if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) {