]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/errorpage.cc
SourceFormat: enforcement
[thirdparty/squid.git] / src / errorpage.cc
index 0cd6b22a98bc5e0e5abd9ebe3b56eaa93b29c391..b5d039d33ed8b5f39aaae0b2c146bb0b5d3bea96 100644 (file)
@@ -1,6 +1,6 @@
 
 /*
- * $Id: errorpage.cc,v 1.230 2008/02/26 21:49:34 amosjeffries Exp $
+ * $Id$
  *
  * DEBUG: section 4     Error Generation
  * AUTHOR: Duane Wessels
  *  it under the terms of the GNU General Public License as published by
  *  the Free Software Foundation; either version 2 of the License, or
  *  (at your option) any later version.
- *  
+ *
  *  This program is distributed in the hope that it will be useful,
  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *  GNU General Public License for more details.
- *  
+ *
  *  You should have received a copy of the GNU General Public License
  *  along with this program; if not, write to the Free Software
  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
@@ -35,7 +35,7 @@
 #include "config.h"
 
 #include "errorpage.h"
-#include "AuthUserRequest.h"
+#include "auth/UserRequest.h"
 #include "SquidTime.h"
 #include "Store.h"
 #include "HttpReply.h"
@@ -71,8 +71,7 @@ CBDATA_CLASS_INIT(ErrorState);
 /* local types */
 
 /// \ingroup ErrorPageInternal
-typedef struct
-{
+typedef struct {
     int id;
     char *page_name;
 } ErrorDynamicPageInfo;
@@ -85,28 +84,27 @@ typedef struct
  \note  hard coded error messages are not appended with %S
  *      automagically to give you more control on the format
  */
-static const struct
-{
+static const struct {
     int type;                  /* and page_id */
     const char *text;
 }
 
 error_hard_text[] = {
 
-                        {
-                            ERR_SQUID_SIGNATURE,
-                            "\n<br>\n"
-                            "<hr>\n"
-                            "<div id=\"footer\">\n"
-                            "Generated %T by %h (%s)\n"
-                            "</div>\n"
-                            "</body></html>\n"
-                        },
-                        {
-                            TCP_RESET,
-                            "reset"
-                        }
-                    };
+    {
+        ERR_SQUID_SIGNATURE,
+        "\n<br>\n"
+        "<hr>\n"
+        "<div id=\"footer\">\n"
+        "Generated %T by %h (%s)\n"
+        "</div>\n"
+        "</body></html>\n"
+    },
+    {
+        TCP_RESET,
+        "reset"
+    }
+};
 
 /// \ingroup ErrorPageInternal
 static Vector<ErrorDynamicPageInfo *> ErrorDynamicPages;
@@ -189,9 +187,9 @@ errorInitialize(void)
     error_stylesheet.reset();
 
     // look for and load stylesheet into global MemBuf for it.
-    if(Config.errorStylesheet) {
-        char *temp = errorLoadText(Config.errorStylesheet);
-        if(temp) {
+    if (Config.errorStylesheet) {
+        char *temp = errorTryLoadText(Config.errorStylesheet,NULL);
+        if (temp) {
             error_stylesheet.Printf("%s",temp);
             safe_free(temp);
         }
@@ -243,16 +241,16 @@ errorLoadText(const char *page_name)
     char *text = NULL;
 
     /** test error_directory configured location */
-    if(Config.errorDirectory)
+    if (Config.errorDirectory)
         text = errorTryLoadText(page_name, Config.errorDirectory);
 
 #if USE_ERR_LOCALES
     /** test error_default_language location */
-    if(!text && Config.errorDefaultLanguage) {
+    if (!text && Config.errorDefaultLanguage) {
         char dir[256];
         snprintf(dir,256,"%s/%s", DEFAULT_SQUID_ERROR_DIR, Config.errorDefaultLanguage);
         text = errorTryLoadText(page_name, dir);
-        if(!text) {
+        if (!text) {
             debugs(1, DBG_CRITICAL, "Unable to load default error language files. Reset to backups.");
         }
     }
@@ -281,19 +279,24 @@ errorTryLoadText(const char *page_name, const char *dir, bool silent)
     ssize_t len;
     MemBuf textbuf;
 
-    snprintf(path, sizeof(path), "%s/%s", dir, page_name);
+    // maybe received compound parts, maybe an absolute page_name and no dir
+    if (dir)
+        snprintf(path, sizeof(path), "%s/%s", dir, page_name);
+    else
+        snprintf(path, sizeof(path), "%s", page_name);
+
     fd = file_open(path, O_RDONLY | O_TEXT);
 
     if (fd < 0) {
         /* with dynamic locale negotiation we may see some failures before a success. */
-        if(!silent)
+        if (!silent)
             debugs(4, DBG_CRITICAL, HERE << "'" << path << "': " << xstrerror());
         return NULL;
     }
 
     textbuf.init();
 
-    while((len = FD_READ_METHOD(fd, buf, sizeof(buf))) > 0) {
+    while ((len = FD_READ_METHOD(fd, buf, sizeof(buf))) > 0) {
         textbuf.append(buf, len);
     }
 
@@ -303,9 +306,6 @@ errorTryLoadText(const char *page_name, const char *dir, bool silent)
 
     file_close(fd);
 
-    if (strstr(textbuf.buf, "%s") == NULL)
-        textbuf.append("%S", 2);       /* add signature */
-
     /* Shrink memory size down to exact size. MemBuf has a tencendy
      * to be rather large..
      */
@@ -469,7 +469,7 @@ errorSend(int fd, ErrorState * err)
  * to the client socket.
  *
  \note If there is a callback, the callback is responsible for
- *     closeing the FD, otherwise we do it ourseves.
+ *     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)
@@ -496,7 +496,6 @@ errorStateFree(ErrorState * err)
     HTTPMSGUNLOCK(err->request);
     safe_free(err->redirect_url);
     safe_free(err->url);
-    safe_free(err->dnsserver_msg);
     safe_free(err->request_hdrs);
     wordlistDestroy(&err->ftp.server_msg);
     safe_free(err->ftp.request);
@@ -504,7 +503,7 @@ errorStateFree(ErrorState * err)
     AUTHUSERREQUESTUNLOCK(err->auth_user_request, "errstate");
     safe_free(err->err_msg);
 #if USE_ERR_LOCALES
-    if(err->err_language != Config.errorDefaultLanguage)
+    if (err->err_language != Config.errorDefaultLanguage)
 #endif
         safe_free(err->err_language);
     cbdataFree(err);
@@ -536,9 +535,8 @@ ErrorState::Dump(MemBuf * mb)
     if (auth_user_request->denyMessage())
         str.Printf("Auth ErrMsg: %s\r\n", auth_user_request->denyMessage());
 
-    if (dnsserver_msg) {
-        str.Printf("DNS Server ErrMsg: %s\r\n", dnsserver_msg);
-    }
+    if (dnsError.size() > 0)
+        str.Printf("DNS ErrMsg: %s\r\n", dnsError.termedBuf());
 
     /* - TimeStamp */
     str.Printf("TimeStamp: %s\r\n\r\n", mkrfc1123(squid_curtime));
@@ -546,7 +544,7 @@ ErrorState::Dump(MemBuf * mb)
     /* - IP stuff */
     str.Printf("ClientIP: %s\r\n", src_addr.NtoA(ntoabuf,MAX_IPSTRLEN));
 
-    if (request && request->hier.host) {
+    if (request && request->hier.host[0] != '\0') {
         str.Printf("ServerIP: %s\r\n", request->hier.host);
     }
 
@@ -556,9 +554,16 @@ ErrorState::Dump(MemBuf * mb)
 
     if (NULL != request) {
         Packer p;
-        str.Printf("%s %s HTTP/%d.%d\n",
+        String urlpath_or_slash;
+
+        if (request->urlpath.size() != 0)
+            urlpath_or_slash = request->urlpath;
+        else
+            urlpath_or_slash = "/";
+
+        str.Printf("%s " SQUIDSTRINGPH " HTTP/%d.%d\n",
                    RequestMethodStr(request->method),
-                   request->urlpath.size() ? request->urlpath.buf() : "/",
+                   SQUIDSTRINGPRINT(urlpath_or_slash),
                    request->http_ver.major, request->http_ver.minor);
         packerToMemInit(&p, &str);
         request->header.packInto(&p);
@@ -661,12 +666,11 @@ ErrorState::Convert(char token)
 
     case 'h':
         mb.Printf("%s", getMyHostname());
-
         break;
 
     case 'H':
         if (request) {
-            if (request->hier.host)
+            if (request->hier.host[0] != '\0') // if non-empty string.
                 p = request->hier.host;
             else
                 p = request->GetHost();
@@ -681,9 +685,9 @@ ErrorState::Convert(char token)
         break;
 
     case 'I':
-        if (request && request->hier.host) {
+        if (request && request->hier.host[0] != '\0') // if non-empty string
             mb.Printf("%s", request->hier.host);
-        else
+        else
             p = "[unknown]";
 
         break;
@@ -734,9 +738,16 @@ ErrorState::Convert(char token)
 
         if (NULL != request) {
             Packer p;
-            mb.Printf("%s %s HTTP/%d.%d\n",
+            String urlpath_or_slash;
+
+            if (request->urlpath.size() != 0)
+                urlpath_or_slash = request->urlpath;
+            else
+                urlpath_or_slash = "/";
+
+            mb.Printf("%s " SQUIDSTRINGPH " HTTP/%d.%d\n",
                       RequestMethodStr(request->method),
-                      request->urlpath.size() ? request->urlpath.buf() : "/",
+                      SQUIDSTRINGPRINT(urlpath_or_slash),
                       request->http_ver.major, request->http_ver.minor);
             packerToMemInit(&p, &mb);
             request->header.packInto(&p);
@@ -781,7 +792,9 @@ ErrorState::Convert(char token)
         break;
 
     case 'U':
-        p = request ? urlCanonicalClean(request) : url ? url : "[no URL]";
+        /* 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]";
         break;
 
     case 'u':
@@ -804,8 +817,8 @@ ErrorState::Convert(char token)
         break;
 
     case 'z':
-        if (dnsserver_msg)
-            p = dnsserver_msg;
+        if (dnsError.size() > 0)
+            p = dnsError.termedBuf();
         else
             p = "[unknown]";
 
@@ -855,7 +868,7 @@ ErrorState::BuildHttpReply()
 
     if (strchr(name, ':')) {
         /* Redirection */
-        rep->setHeaders(version, HTTP_MOVED_TEMPORARILY, NULL, "text/html", 0, 0, squid_curtime);
+        rep->setHeaders(version, HTTP_MOVED_TEMPORARILY, NULL, "text/html", 0, 0, -1);
 
         if (request) {
             char *quoted_url = rfc1738_escape_part(urlCanonical(request));
@@ -865,7 +878,7 @@ ErrorState::BuildHttpReply()
         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, squid_curtime);
+        rep->setHeaders(version, 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
@@ -883,23 +896,22 @@ ErrorState::BuildHttpReply()
          * We have even better reasons though:
          * see http://wiki.squid-cache.org/KnowledgeBase/VaryNotCaching
          */
-        if(!Config.errorDirectory) {
+        if (!Config.errorDirectory) {
             /* We 'negotiated' this ONLY from the Accept-Language. */
-            httpHeaderDelById(&rep->header, HDR_VARY);
-            httpHeaderPutStrf(&rep->header, HDR_VARY, "Accept-Language");
+            rep->header.delById(HDR_VARY);
+            rep->header.putStr(HDR_VARY, "Accept-Language");
         }
 
         /* add the Content-Language header according to RFC section 14.12 */
-        if(err_language) {
-            httpHeaderPutStrf(&rep->header, HDR_CONTENT_LANGUAGE, "%s", err_language);
-        }
-        else
+        if (err_language) {
+            rep->header.putStr(HDR_CONTENT_LANGUAGE, err_language);
+        } else
 #endif /* USE_ERROR_LOCALES */
         {
             /* default templates are in English */
             /* language is known unless error_directory override used */
-            if(!Config.errorDirectory)
-                httpHeaderPutStrf(&rep->header, HDR_CONTENT_LANGUAGE, "en");
+            if (!Config.errorDirectory)
+                rep->header.putStr(HDR_CONTENT_LANGUAGE, "en");
         }
 
         httpBodySet(&rep->body, content);
@@ -927,10 +939,9 @@ ErrorState::BuildContent()
     /** error_directory option in squid.conf overrides translations.
      * Otherwise locate the Accept-Language header
      */
-    if(!Config.errorDirectory && request->header.getList(HDR_ACCEPT_LANGUAGE, &hdr) ) {
+    if (!Config.errorDirectory && request && request->header.getList(HDR_ACCEPT_LANGUAGE, &hdr) ) {
 
-        const char *buf = hdr.buf(); // raw header string for parsing
-        int pos = 0; // current parsing position in header string
+        size_t pos = 0; // current parsing position in header string
         char *reset = NULL; // where to reset the p pointer for each new tag file
         char *dt = NULL;
 
@@ -942,36 +953,67 @@ ErrorState::BuildContent()
 
         debugs(4, 6, HERE << "Testing Header: '" << hdr << "'");
 
-        while( pos < hdr.size() ) {
+        while ( pos < hdr.size() ) {
 
-/*
- * Header value format:
- *  - sequence of whitespace delimited tags
- *  - each tag may suffix with ';'.* which we can ignore.
- *  - IFF a tag contains only two characters we can wildcard ANY translations matching: <it> '-'? .*
- *    with preference given to an exact match.
- */
-            while(pos < hdr.size() && buf[pos] != ';' && buf[pos] != ',' && !xisspace(buf[pos]) && dt < (dir+256) ) {
-                *dt++ = xtolower(buf[pos++]);
+            /* skip any initial whitespace. */
+            while (pos < hdr.size() && xisspace(hdr[pos])) pos++;
+
+            /*
+             * Header value format:
+             *  - sequence of whitespace delimited tags
+             *  - each tag may suffix with ';'.* which we can ignore.
+             *  - IFF a tag contains only two characters we can wildcard ANY translations matching: <it> '-'? .*
+             *    with preference given to an exact match.
+             */
+            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 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]);
+                    // valid codes only contain A-Z, hyphen (-) and *
+                    if (*dt != '-' && *dt != '*' && (*dt < 'a' || *dt > 'z') )
+                        invalid_byte = true;
+                    else
+                        dt++; // move to next destination byte.
+                }
+                pos++;
             }
             *dt++ = '\0'; // nul-terminated the filename content string before system use.
 
-            debugs(4, 9, HERE << "STATE: dt='" << dt << "', reset='" << reset << "', reset[1]='" << reset[1] << "', pos=" << pos << ", buf='" << &buf[pos] << "'");
+            debugs(4, 9, HERE << "STATE: dt='" << dt << "', reset='" << reset << "', pos=" << pos << ", buf='" << ((pos < hdr.size()) ? hdr.substr(pos,hdr.size()) : "") << "'");
 
             /* if we found anything we might use, try it. */
-            if(*reset != '\0') {
+            if (*reset != '\0' && !invalid_byte) {
+
+                /* wildcard uses the configured default language */
+                if (reset[0] == '*' && reset[1] == '\0') {
+                    debugs(4, 6, HERE << "Found language '" << reset << "'. Using configured default.");
+                    m = error_text[page_id];
+                    if (!Config.errorDirectory)
+                        err_language = Config.errorDefaultLanguage;
+                    break;
+                }
 
                 debugs(4, 6, HERE << "Found language '" << reset << "', testing for available template in: '" << dir << "'");
+
                 m = errorTryLoadText( err_type_str[page_id], dir, false);
 
-                if(m) {
+                if (m) {
                     /* store the language we found for the Content-Language reply header */
                     err_language = xstrdup(reset);
                     break;
+                } else if (Config.errorLogMissingLanguages) {
+                    debugs(4, DBG_IMPORTANT, "WARNING: Error Pages Missing Language: " << reset);
                 }
 
 #if HAVE_GLOB
-                if( (dt - reset) == 2) {
+                if ( (dt - reset) == 2) {
                     /* TODO glob the error directory for sub-dirs matching: <tag> '-*'   */
                     /* use first result. */
                     debugs(4,2, HERE << "wildcard fallback errors not coded yet.");
@@ -981,9 +1023,9 @@ ErrorState::BuildContent()
 
             dt = reset; // reset for next tag testing. we replace the failed name instead of cloning.
 
-            // IFF we terminated the tag on ';' we need to skip the 'q=' bit to the next ',' or end.
-            while(pos < hdr.size() && buf[pos] != ',') pos++;
-            if(buf[pos] == ',') pos++;
+            // IFF we terminated the tag on whitespace or ';' we need to skip to the next ',' or end of header.
+            while (pos < hdr.size() && hdr[pos] != ',') pos++;
+            if (hdr[pos] == ',') pos++;
         }
     }
 #endif /* USE_ERR_LOCALES */
@@ -992,10 +1034,10 @@ ErrorState::BuildContent()
      * If client-specific error templates are not enabled or available.
      * fall back to the old style squid.conf settings.
      */
-    if(!m) {
+    if (!m) {
         m = error_text[page_id];
 #if USE_ERR_LOCALES
-        if(!Config.errorDirectory)
+        if (!Config.errorDirectory)
             err_language = Config.errorDefaultLanguage;
 #endif
         debugs(4, 2, HERE << "No existing error page language negotiated for " << errorPageName(page_id) << ". Using default error file.");