nginx-0.3.22-RELEASE import

    *) Feature: the ngx_http_perl_module supports the $r->args and
       $r->unescape methods.

    *) Feature: the method $r->query_string of ngx_http_perl_module was
       canceled.

    *) Bugfix: segmentation fault was occurred if the "none" or "blocked"
       values was specified in the "valid_referers" directive; the bug had
       appeared in 0.3.18.
diff --git a/src/core/ngx_string.c b/src/core/ngx_string.c
index 2bb335a..c99778d 100644
--- a/src/core/ngx_string.c
+++ b/src/core/ngx_string.c
@@ -931,7 +931,7 @@
 
 
 void
-ngx_unescape_uri(u_char **dst, u_char **src, size_t size)
+ngx_unescape_uri(u_char **dst, u_char **src, size_t size, ngx_uint_t type)
 {
     u_char  *d, *s, ch, c, decoded;
     enum {
@@ -952,7 +952,7 @@
 
         switch (state) {
         case sw_usual:
-            if (ch == '?') {
+            if (ch == '?' && type == NGX_UNESCAPE_URI) {
                 *d++ = ch;
                 goto done;
             }
@@ -995,12 +995,18 @@
             if (ch >= '0' && ch <= '9') {
                 ch = (u_char) ((decoded << 4) + ch - '0');
 
-                if (ch > '%' && ch < 0x7f) {
-                    *d++ = ch;
+                if (type == NGX_UNESCAPE_URI) {
+                    if (ch > '%' && ch < 0x7f) {
+                        *d++ = ch;
+                        break;
+                    }
+
+                    *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1);
+
                     break;
                 }
 
-                *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1);
+                *d++ = ch;
 
                 break;
             }
@@ -1009,17 +1015,22 @@
             if (c >= 'a' && c <= 'f') {
                 ch = (u_char) ((decoded << 4) + c - 'a' + 10);
 
-                if (ch == '?') {
-                    *d++ = ch;
-                    goto done;
-                }
+                if (type == NGX_UNESCAPE_URI) {
+                    if (ch == '?') {
+                        *d++ = ch;
+                        goto done;
+                    }
 
-                if (ch > '%' && ch < 0x7f) {
-                    *d++ = ch;
+                    if (ch > '%' && ch < 0x7f) {
+                        *d++ = ch;
+                        break;
+                    }
+
+                    *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1);
                     break;
                 }
 
-                *d++ = '%'; *d++ = *(s - 2); *d++ = *(s - 1);
+                *d++ = ch;
 
                 break;
             }