]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/errorpage.cc
Merge form trunk
[thirdparty/squid.git] / src / errorpage.cc
index 91c26ac0f400fd943de4477f60a4769b94a973be..80d0ae344dfb09806e05dc7b3aca3116edebf1b1 100644 (file)
@@ -1,8 +1,7 @@
-
 /*
  * $Id$
  *
- * DEBUG: section     Error Generation
+ * DEBUG: section 04    Error Generation
  * AUTHOR: Duane Wessels
  *
  * SQUID Web Proxy Cache          http://www.squid-cache.org/
  */
 #include "config.h"
 
-#include "errorpage.h"
 #include "auth/UserRequest.h"
-#include "SquidTime.h"
-#include "Store.h"
+#include "comm/Connection.h"
+#include "err_detail_type.h"
+#include "errorpage.h"
+#include "fde.h"
+#include "html_quote.h"
 #include "HttpReply.h"
 #include "HttpRequest.h"
-#include "MemObject.h"
-#include "fde.h"
 #include "MemBuf.h"
+#include "MemObject.h"
+#include "rfc1738.h"
+#include "SquidTime.h"
+#include "Store.h"
 #include "URLScheme.h"
 #include "wordlist.h"
 
@@ -368,7 +371,7 @@ errorReservePageId(const char *page_name)
 }
 
 /// \ingroup ErrorPageInternal
-static const char *
+const char *
 errorPageName(int pageId)
 {
     if (pageId >= ERR_NONE && pageId < ERR_MAX)                /* common case */
@@ -392,6 +395,7 @@ errorCon(err_type type, http_status status, HttpRequest * request)
     if (request != NULL) {
         err->request = HTTPMSGLOCK(request);
         err->src_addr = request->client_addr;
+        request->detailError(type, ERR_DETAIL_NONE);
     }
 
     return err;
@@ -439,26 +443,26 @@ errorAppendEntry(StoreEntry * entry, ErrorState * err)
 }
 
 void
-errorSend(int fd, ErrorState * err)
+errorSend(const Comm::ConnectionPointer &conn, ErrorState * err)
 {
     HttpReply *rep;
-    debugs(4, 3, "errorSend: FD " << fd << ", err=" << err);
-    assert(fd >= 0);
+    debugs(4, 3, HERE << conn << ", err=" << err);
+    assert(Comm::IsConnOpen(conn));
     /*
      * ugh, this is how we make sure error codes get back to
      * the client side for logging and error tracking.
      */
 
     if (err->request)
-        err->request->errType = err->type;
+        err->request->detailError(err->type, err->xerrno);
 
     /* moved in front of errorBuildBuf @?@ */
     err->flags.flag_cbdata = 1;
 
     rep = err->BuildHttpReply();
-
-    comm_write_mbuf(fd, rep->pack(), errorSendComplete, err);
-
+    MemBuf *mb = rep->pack();
+    comm_write_mbuf(conn, mb, errorSendComplete, err);
+    delete mb;
     delete rep;
 }
 
@@ -472,18 +476,18 @@ errorSend(int fd, ErrorState * err)
  *     closing the FD, otherwise we do it ourselves.
  */
 static void
-errorSendComplete(int fd, char *bufnotused, size_t size, comm_err_t errflag, int xerrno, void *data)
+errorSendComplete(const Comm::ConnectionPointer &conn, char *bufnotused, size_t size, comm_err_t errflag, int xerrno, void *data)
 {
     ErrorState *err = static_cast<ErrorState *>(data);
-    debugs(4, 3, "errorSendComplete: FD " << fd << ", size=" << size);
+    debugs(4, 3, HERE << conn << ", size=" << size);
 
     if (errflag != COMM_ERR_CLOSING) {
         if (err->callback) {
             debugs(4, 3, "errorSendComplete: callback");
-            err->callback(fd, err->callback_data, size);
+            err->callback(conn->fd, err->callback_data, size);
         } else {
-            comm_close(fd);
             debugs(4, 3, "errorSendComplete: comm_close");
+            conn->close();
         }
     }
 
@@ -500,7 +504,7 @@ errorStateFree(ErrorState * err)
     wordlistDestroy(&err->ftp.server_msg);
     safe_free(err->ftp.request);
     safe_free(err->ftp.reply);
-    AUTHUSERREQUESTUNLOCK(err->auth_user_request, "errstate");
+    err->auth_user_request = NULL;
     safe_free(err->err_msg);
 #if USE_ERR_LOCALES
     if (err->err_language != Config.errorDefaultLanguage)
@@ -553,7 +557,7 @@ ErrorState::Dump(MemBuf * mb)
     str.Printf("HTTP Request:\r\n");
 
     if (NULL != request) {
-        Packer p;
+        Packer pck;
         String urlpath_or_slash;
 
         if (request->urlpath.size() != 0)
@@ -565,9 +569,9 @@ ErrorState::Dump(MemBuf * mb)
                    RequestMethodStr(request->method),
                    SQUIDSTRINGPRINT(urlpath_or_slash),
                    request->http_ver.major, request->http_ver.minor);
-        packerToMemInit(&p, &str);
-        request->header.packInto(&p);
-        packerClean(&p);
+        packerToMemInit(&pck, &str);
+        request->header.packInto(&pck);
+        packerClean(&pck);
     } else if (request_hdrs) {
         p = request_hdrs;
     } else {
@@ -595,11 +599,12 @@ ErrorState::Dump(MemBuf * mb)
 #define CVT_BUF_SZ 512
 
 const char *
-ErrorState::Convert(char token)
+ErrorState::Convert(char token, bool building_deny_info_url)
 {
     static MemBuf mb;
     const char *p = NULL;      /* takes priority over mb if set */
     int do_quote = 1;
+    int no_urlescape = 0;       /* if true then item is NOT to be further URL-encoded */
     char ntoabuf[MAX_IPSTRLEN];
 
     mb.reset();
@@ -607,61 +612,60 @@ ErrorState::Convert(char token)
     switch (token) {
 
     case 'a':
-
-        if (request && request->auth_user_request)
+        if (request && request->auth_user_request != NULL)
             p = request->auth_user_request->username();
-
         if (!p)
             p = "-";
-
         break;
 
     case 'B':
+        if (building_deny_info_url) break;
         p = request ? ftpUrlWith2f(request) : "[no URL]";
-
         break;
 
     case 'c':
+        if (building_deny_info_url) break;
         p = errorPageName(type);
-
         break;
 
     case 'e':
         mb.Printf("%d", xerrno);
-
         break;
 
     case 'E':
-
         if (xerrno)
             mb.Printf("(%d) %s", xerrno, strerror(xerrno));
         else
             mb.Printf("[No Error]");
-
         break;
 
     case 'f':
+        if (building_deny_info_url) break;
         /* FTP REQUEST LINE */
         if (ftp.request)
             p = ftp.request;
         else
             p = "nothing";
-
         break;
 
     case 'F':
+        if (building_deny_info_url) break;
         /* FTP REPLY LINE */
         if (ftp.request)
             p = ftp.reply;
         else
             p = "nothing";
-
         break;
 
     case 'g':
+        if (building_deny_info_url) break;
         /* FTP SERVER MESSAGE */
-        wordlistCat(ftp.server_msg, &mb);
-
+        if (ftp.server_msg)
+            wordlistCat(ftp.server_msg, &mb);
+        else if (ftp.listing) {
+            mb.append(ftp.listing->content(), ftp.listing->contentSize());
+            do_quote = 0;
+        }
         break;
 
     case 'h':
@@ -674,70 +678,77 @@ ErrorState::Convert(char token)
                 p = request->hier.host;
             else
                 p = request->GetHost();
-        } else
+        } else if (!building_deny_info_url)
             p = "[unknown host]";
-
         break;
 
     case 'i':
         mb.Printf("%s", src_addr.NtoA(ntoabuf,MAX_IPSTRLEN));
-
         break;
 
     case 'I':
         if (request && request->hier.host[0] != '\0') // if non-empty string
             mb.Printf("%s", request->hier.host);
-        else
+        else if (!building_deny_info_url)
             p = "[unknown]";
-
         break;
 
     case 'l':
+        if (building_deny_info_url) break;
         mb.append(error_stylesheet.content(), error_stylesheet.contentSize());
         do_quote = 0;
         break;
 
     case 'L':
+        if (building_deny_info_url) break;
         if (Config.errHtmlText) {
             mb.Printf("%s", Config.errHtmlText);
             do_quote = 0;
         } else
             p = "[not available]";
-
         break;
 
     case 'm':
+        if (building_deny_info_url) break;
         p = auth_user_request->denyMessage("[not available]");
-
         break;
 
     case 'M':
-        p = request ? RequestMethodStr(request->method) : "[unknown method]";
-
+        if (request)
+            p = RequestMethodStr(request->method);
+        else if (!building_deny_info_url)
+            p= "[unknown method]";
         break;
 
     case 'o':
-        p = external_acl_message ? external_acl_message : "[not available]";
-
+        p = request ? request->extacl_message.termedBuf() : external_acl_message;
+        if (!p && !building_deny_info_url)
+            p = "[not available]";
         break;
 
     case 'p':
         if (request) {
             mb.Printf("%d", (int) request->port);
-        } else {
+        } else if (!building_deny_info_url) {
             p = "[unknown port]";
         }
-
         break;
 
     case 'P':
-        p = request ? ProtocolStr[request->protocol] : "[unknown protocol]";
+        if (request) {
+            p = ProtocolStr[request->protocol];
+        } else if (!building_deny_info_url) {
+            p = "[unknown protocol]";
+        }
         break;
 
     case 'R':
-
+        if (building_deny_info_url) {
+            p = (request->urlpath.size() != 0 ? request->urlpath.termedBuf() : "/");
+            break;
+        }
         if (NULL != request) {
-            Packer p;
+            Packer pck;
             String urlpath_or_slash;
 
             if (request->urlpath.size() != 0)
@@ -749,24 +760,31 @@ ErrorState::Convert(char token)
                       RequestMethodStr(request->method),
                       SQUIDSTRINGPRINT(urlpath_or_slash),
                       request->http_ver.major, request->http_ver.minor);
-            packerToMemInit(&p, &mb);
-            request->header.packInto(&p);
-            packerClean(&p);
+            packerToMemInit(&pck, &mb);
+            request->header.packInto(&pck);
+            packerClean(&pck);
         } else if (request_hdrs) {
             p = request_hdrs;
         } else {
             p = "[no request]";
         }
-
         break;
 
     case 's':
-        p = visible_appname_string;
+        /* for backward compat we make %s show the full URL. Drop this in some future release. */
+        if (building_deny_info_url) {
+            p = request ? urlCanonical(request) : url;
+            debugs(0,0, "WARNING: deny_info now accepts coded tags. Use %u to get the full URL instead of %s");
+        } else
+            p = visible_appname_string;
         break;
 
     case 'S':
+        if (building_deny_info_url) {
+            p = visible_appname_string;
+            break;
+        }
         /* signature may contain %-escapes, recursion */
-
         if (page_id != ERR_SQUID_SIGNATURE) {
             const int saved_id = page_id;
             page_id = ERR_SQUID_SIGNATURE;
@@ -780,7 +798,6 @@ ErrorState::Convert(char token)
             /* wow, somebody put %S into ERR_SIGNATURE, stop recursion */
             p = "[%S]";
         }
-
         break;
 
     case 't':
@@ -794,54 +811,62 @@ ErrorState::Convert(char token)
     case 'U':
         /* Using the fake-https version of canonical so error pages see https:// */
         /* even when the url-path cannot be shown as more than '*' */
-        p = request ? urlCanonicalFakeHttps(request) : url ? url : "[no URL]";
+        if (request)
+            p = urlCanonicalFakeHttps(request);
+        else if (url)
+            p = url;
+        else if (!building_deny_info_url)
+            p = "[no URL]";
         break;
 
     case 'u':
-        p = request ? urlCanonical(request) : url ? url : "[no URL]";
+        if (request)
+            p = urlCanonical(request);
+        else if (url)
+            p = url;
+        else if (!building_deny_info_url)
+            p = "[no URL]";
         break;
 
     case 'w':
-
         if (Config.adminEmail)
             mb.Printf("%s", Config.adminEmail);
-        else
+        else if (!building_deny_info_url)
             p = "[unknown]";
-
         break;
 
     case 'W':
+        if (building_deny_info_url) break;
         if (Config.adminEmail && Config.onoff.emailErrData)
             Dump(&mb);
-
+        no_urlescape = 1;
         break;
 
     case 'z':
+        if (building_deny_info_url) break;
         if (dnsError.size() > 0)
             p = dnsError.termedBuf();
+        else if (ftp.cwd_msg)
+            p = ftp.cwd_msg;
         else
             p = "[unknown]";
-
         break;
 
     case 'Z':
+        if (building_deny_info_url) break;
         if (err_msg)
             p = err_msg;
         else
             p = "[unknown]";
-
         break;
 
     case '%':
         p = "%";
-
         break;
 
     default:
         mb.Printf("%%%c", token);
-
         do_quote = 0;
-
         break;
     }
 
@@ -855,30 +880,54 @@ ErrorState::Convert(char token)
     if (do_quote)
         p = html_quote(p);
 
+    if (building_deny_info_url && !no_urlescape)
+        p = rfc1738_escape_part(p);
+
     return p;
 }
 
+void
+ErrorState::DenyInfoLocation(const char *name, HttpRequest *aRequest, MemBuf &result)
+{
+    char const *m = name;
+    char const *p = m;
+    char const *t;
+
+    while ((p = strchr(m, '%'))) {
+        result.append(m, p - m);       /* copy */
+        t = Convert(*++p, true);       /* convert */
+        result.Printf("%s", t);        /* copy */
+        m = p + 1;                     /* advance */
+    }
+
+    if (*m)
+        result.Printf("%s", m);        /* copy tail */
+
+    assert((size_t)result.contentSize() == strlen(result.content()));
+}
+
 HttpReply *
 ErrorState::BuildHttpReply()
 {
     HttpReply *rep = new HttpReply;
     const char *name = errorPageName(page_id);
     /* no LMT for error pages; error pages expire immediately */
-    HttpVersion version(1, 0);
 
     if (strchr(name, ':')) {
         /* Redirection */
-        rep->setHeaders(version, HTTP_MOVED_TEMPORARILY, NULL, "text/html", 0, 0, -1);
+        rep->setHeaders(HTTP_MOVED_TEMPORARILY, NULL, "text/html", 0, 0, -1);
 
         if (request) {
-            char *quoted_url = rfc1738_escape_part(urlCanonical(request));
-            httpHeaderPutStrf(&rep->header, HDR_LOCATION, name, quoted_url);
+            MemBuf redirect_location;
+            redirect_location.init();
+            DenyInfoLocation(name, request, redirect_location);
+            httpHeaderPutStrf(&rep->header, HDR_LOCATION, "%s", redirect_location.content() );
         }
 
         httpHeaderPutStrf(&rep->header, HDR_X_SQUID_ERROR, "%d %s", httpStatus, "Access Denied");
     } else {
         MemBuf *content = BuildContent();
-        rep->setHeaders(version, httpStatus, NULL, "text/html", content->contentSize(), 0, -1);
+        rep->setHeaders(httpStatus, NULL, "text/html", content->contentSize(), 0, -1);
         /*
          * include some information for downstream caches. Implicit
          * replaceable content. This isn't quite sufficient. xerrno is not
@@ -937,9 +986,10 @@ ErrorState::BuildContent()
     int l = 0;
 
     /** error_directory option in squid.conf overrides translations.
+     * Custom errors are always found either in error_directory or the templates directory.
      * Otherwise locate the Accept-Language header
      */
-    if (!Config.errorDirectory && request && request->header.getList(HDR_ACCEPT_LANGUAGE, &hdr) ) {
+    if (!Config.errorDirectory && page_id < ERR_MAX && request && request->header.getList(HDR_ACCEPT_LANGUAGE, &hdr) ) {
 
         size_t pos = 0; // current parsing position in header string
         char *reset = NULL; // where to reset the p pointer for each new tag file
@@ -968,14 +1018,14 @@ ErrorState::BuildContent()
             bool invalid_byte = false;
             while (pos < hdr.size() && hdr[pos] != ';' && hdr[pos] != ',' && !xisspace(hdr[pos]) && dt < (dir+256) ) {
                 if (!invalid_byte) {
-#if HTTP_VIOLATIONS
+#if USE_HTTP_VIOLATIONS
                     // if accepting violations we may as well accept some broken browsers
                     //  which may send us the right code, wrong ISO formatting.
                     if (hdr[pos] == '_')
                         *dt = '-';
                     else
 #endif
-                    *dt = xtolower(hdr[pos]);
+                        *dt = xtolower(hdr[pos]);
                     // valid codes only contain A-Z, hyphen (-) and *
                     if (*dt != '-' && *dt != '*' && (*dt < 'a' || *dt > 'z') )
                         invalid_byte = true;
@@ -1048,7 +1098,7 @@ ErrorState::BuildContent()
 
     while ((p = strchr(m, '%'))) {
         content->append(m, p - m);     /* copy */
-        t = Convert(*++p);             /* convert */
+        t = Convert(*++p, false);      /* convert */
         content->Printf("%s", t);      /* copy */
         m = p + 1;                     /* advance */
     }