* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
*
*/
-#include "config.h"
+#include "squid.h"
+#include "comm/Connection.h"
#include "comm/Write.h"
+#include "err_detail_type.h"
#include "errorpage.h"
#if USE_AUTH
#include "auth/UserRequest.h"
#endif
#include "SquidTime.h"
+#if USE_SSL
+#include "ssl/ErrorDetailManager.h"
+#endif
#include "Store.h"
#include "html_quote.h"
#include "HttpReply.h"
#include "MemObject.h"
#include "fde.h"
#include "MemBuf.h"
+#include "protos.h"
#include "rfc1738.h"
#include "URLScheme.h"
#include "wordlist.h"
-#include "err_detail_type.h"
/**
\defgroup ErrorPageInternal Error Page Internals
*/
-#ifndef DEFAULT_SQUID_ERROR_DIR
+#if !defined(DEFAULT_SQUID_ERROR_DIR)
/** Where to look for errors if config path fails.
\note Please use ./configure --datadir=/path instead of patching
*/
/// \ingroup ErrorPageInternal
static MemBuf error_stylesheet;
-static char *errorTryLoadText(const char *page_name, const char *dir, bool silent = false);
-static char *errorLoadText(const char *page_name);
static const char *errorFindHardText(err_type type);
static ErrorDynamicPageInfo *errorDynamicPageInfoCreate(int id, const char *page_name);
static void errorDynamicPageInfoDestroy(ErrorDynamicPageInfo * info);
static IOCB errorSendComplete;
+/// \ingroup ErrorPageInternal
+/// manages an error page template
+class ErrorPageFile: public TemplateFile
+{
+public:
+ ErrorPageFile(const char *name, const err_type code): TemplateFile(name,code) { textBuf.init();}
+
+ /// The template text data read from disk
+ const char *text() { return textBuf.content(); }
+
+private:
+ /// stores the data read from disk to a local buffer
+ virtual bool parse(const char *buf, int len, bool eof) {
+ if (len)
+ textBuf.append(buf, len);
+ return true;
+ }
+
+ MemBuf textBuf; ///< A buffer to store the error page
+};
/// \ingroup ErrorPageInternal
err_type &operator++ (err_type &anErr)
* (a) default language translation directory (error_default_language)
* (b) admin specified custom directory (error_directory)
*/
- error_text[i] = errorLoadText(err_type_str[i]);
-
+ ErrorPageFile errTmpl(err_type_str[i], i);
+ error_text[i] = errTmpl.loadDefault() ? xstrdup(errTmpl.text()) : NULL;
} else {
/** \par
* Index any unknown file names used by deny_info.
if (strchr(pg, ':') == NULL) {
/** But only if they are not redirection URL. */
- error_text[i] = errorLoadText(pg);
+ ErrorPageFile errTmpl(pg, ERR_MAX);
+ error_text[i] = errTmpl.loadDefault() ? xstrdup(errTmpl.text()) : NULL;
}
}
}
// look for and load stylesheet into global MemBuf for it.
if (Config.errorStylesheet) {
- char *temp = errorTryLoadText(Config.errorStylesheet,NULL);
- if (temp) {
- error_stylesheet.Printf("%s",temp);
- safe_free(temp);
- }
+ ErrorPageFile tmpl("StylesSheet", ERR_MAX);
+ tmpl.loadFromFile(Config.errorStylesheet);
+ error_stylesheet.Printf("%s",tmpl.text());
}
+
+#if USE_SSL
+ Ssl::errorDetailInitialize();
+#endif
}
void
if (error_text) {
int i;
- for (i = ERR_NONE + 1; i < error_page_count; i++)
+ for (i = ERR_NONE + 1; i < error_page_count; ++i)
safe_free(error_text[i]);
safe_free(error_text);
errorDynamicPageInfoDestroy(ErrorDynamicPages.pop_back());
error_page_count = 0;
+
+#if USE_SSL
+ Ssl::errorDetailClean();
+#endif
}
/// \ingroup ErrorPageInternal
{
int i;
- for (i = 0; i < error_hard_text_count; i++)
+ for (i = 0; i < error_hard_text_count; ++i)
if (error_hard_text[i].type == type)
return error_hard_text[i].text;
return NULL;
}
-/**
- * \ingroup ErrorPageInternal
- *
- * Load into the in-memory error text Index a file probably available at:
- * (a) admin specified custom directory (error_directory)
- * (b) default language translation directory (error_default_language)
- * (c) English sub-directory where errors should ALWAYS exist
- */
-static char *
-errorLoadText(const char *page_name)
+TemplateFile::TemplateFile(const char *name, const err_type code): silent(false), wasLoaded(false), templateName(name), templateCode(code)
{
- char *text = NULL;
+ assert(name);
+}
+
+bool
+TemplateFile::loadDefault()
+{
+ if (loaded()) // already loaded?
+ return true;
/** test error_directory configured location */
- if (Config.errorDirectory)
- text = errorTryLoadText(page_name, Config.errorDirectory);
+ if (Config.errorDirectory) {
+ char path[MAXPATHLEN];
+ snprintf(path, sizeof(path), "%s/%s", Config.errorDirectory, templateName.termedBuf());
+ loadFromFile(path);
+ }
#if USE_ERR_LOCALES
/** test error_default_language location */
- 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) {
- debugs(1, DBG_CRITICAL, "Unable to load default error language files. Reset to backups.");
+ if (!loaded() && Config.errorDefaultLanguage) {
+ if (!tryLoadTemplate(Config.errorDefaultLanguage)) {
+ debugs(1, (templateCode < TCP_RESET ? DBG_CRITICAL : 3), "Unable to load default error language files. Reset to backups.");
}
}
#endif
/* test default location if failed (templates == English translation base templates) */
- if (!text) {
- text = errorTryLoadText(page_name, DEFAULT_SQUID_ERROR_DIR"/templates");
+ if (!loaded()) {
+ tryLoadTemplate("templates");
}
/* giving up if failed */
- if (!text)
- fatal("failed to find or read error text file.");
+ if (!loaded()) {
+ debugs(1, (templateCode < TCP_RESET ? DBG_CRITICAL : 3), "WARNING: failed to find or read error text file " << templateName);
+ parse("Internal Error: Missing Template ", 33, '\0');
+ parse(templateName.termedBuf(), templateName.size(), '\0');
+ }
- return text;
+ return true;
}
-/// \ingroup ErrorPageInternal
-static char *
-errorTryLoadText(const char *page_name, const char *dir, bool silent)
+bool
+TemplateFile::tryLoadTemplate(const char *lang)
{
- int fd;
+ assert(lang);
+
char path[MAXPATHLEN];
+ /* TODO: prep the directory path string to prevent snprintf ... */
+ snprintf(path, sizeof(path), "%s/%s/%s",
+ DEFAULT_SQUID_ERROR_DIR, lang, templateName.termedBuf());
+ path[MAXPATHLEN-1] = '\0';
+
+ if (loadFromFile(path))
+ return true;
+
+#if HAVE_GLOB
+ if ( strlen(lang) == 2) {
+ /* TODO glob the error directory for sub-dirs matching: <tag> '-*' */
+ /* use first result. */
+ debugs(4,2, HERE << "wildcard fallback errors not coded yet.");
+ }
+#endif
+
+ return false;
+}
+
+bool
+TemplateFile::loadFromFile(const char *path)
+{
+ int fd;
char buf[4096];
- char *text;
ssize_t len;
- MemBuf textbuf;
- // 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);
+ if (loaded()) // already loaded?
+ return true;
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 && templateCode < TCP_RESET)
debugs(4, DBG_CRITICAL, HERE << "'" << path << "': " << xstrerror());
- return NULL;
+ wasLoaded = false;
+ return wasLoaded;
}
- textbuf.init();
-
while ((len = FD_READ_METHOD(fd, buf, sizeof(buf))) > 0) {
- textbuf.append(buf, len);
+ if (!parse(buf, len, false)) {
+ debugs(4, DBG_CRITICAL, HERE << " parse error while reading template file: " << path);
+ wasLoaded = false;
+ return wasLoaded;
+ }
}
+ parse(buf, 0, true);
if (len < 0) {
debugs(4, DBG_CRITICAL, HERE << "failed to fully read: '" << path << "': " << xstrerror());
file_close(fd);
- /* Shrink memory size down to exact size. MemBuf has a tencendy
- * to be rather large..
- */
- text = xstrdup(textbuf.buf);
+ wasLoaded = true;
+ return wasLoaded;
+}
+
+bool strHdrAcptLangGetItem(const String &hdr, char *lang, int langLen, size_t &pos)
+{
+ while (pos < hdr.size()) {
+ char *dt = lang;
+
+ if (!pos) {
+ /* skip any initial whitespace. */
+ while (pos < hdr.size() && xisspace(hdr[pos]))
+ ++pos;
+ } else {
+ // 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;
+ }
+
+ /*
+ * 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 < (lang + (langLen -1)) ) {
+ if (!invalid_byte) {
+#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]);
+ // 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.
+ ++dt;
+
+ debugs(4, 9, HERE << "STATE: dt='" << dt << "', lang='" << lang << "', pos=" << pos << ", buf='" << ((pos < hdr.size()) ? hdr.substr(pos,hdr.size()) : "") << "'");
+
+ /* if we found anything we might use, try it. */
+ if (*lang != '\0' && !invalid_byte)
+ return true;
+ }
+ return false;
+}
+
+bool
+TemplateFile::loadFor(HttpRequest *request)
+{
+ String hdr;
+
+#if USE_ERR_LOCALES
+ if (loaded()) // already loaded?
+ return true;
+
+ if (!request || !request->header.getList(HDR_ACCEPT_LANGUAGE, &hdr) )
+ return false;
+
+ char lang[256];
+ size_t pos = 0; // current parsing position in header string
+
+ debugs(4, 6, HERE << "Testing Header: '" << hdr << "'");
+
+ while ( strHdrAcptLangGetItem(hdr, lang, 256, pos) ) {
- textbuf.clean();
+ /* wildcard uses the configured default language */
+ if (lang[0] == '*' && lang[1] == '\0') {
+ debugs(4, 6, HERE << "Found language '" << lang << "'. Using configured default.");
+ return false;
+ }
+
+ debugs(4, 6, HERE << "Found language '" << lang << "', testing for available template");
+
+ if (tryLoadTemplate(lang)) {
+ /* store the language we found for the Content-Language reply header */
+ errLanguage = lang;
+ break;
+ } else if (Config.errorLogMissingLanguages) {
+ debugs(4, DBG_IMPORTANT, "WARNING: Error Pages Missing Language: " << lang);
+ }
+ }
+#endif
- return text;
+ return loaded();
}
/// \ingroup ErrorPageInternal
self_destruct();
} else if ( /* >= 200 && */ info->page_redirect < 300 && strchr(&(page_name[4]), ':')) {
// 2xx require a local template file
- debugs(0, DBG_CRITICAL, "FATAL: status " << info->page_redirect << " is not valid on '" << page_name << "'");
+ debugs(0, DBG_CRITICAL, "FATAL: status " << info->page_redirect << " requires a template on '" << page_name << "'");
self_destruct();
- } else if (/* >= 300 && */ info->page_redirect <= 399 && !strchr(&(page_name[4]), ':')) {
+ } else if (info->page_redirect >= 300 && info->page_redirect <= 399 && !strchr(&(page_name[4]), ':')) {
// 3xx require an absolute URL
- debugs(0, DBG_CRITICAL, "FATAL: status " << info->page_redirect << " is not valid on '" << page_name << "'");
+ debugs(0, DBG_CRITICAL, "FATAL: status " << info->page_redirect << " requires a URL on '" << page_name << "'");
self_destruct();
} else if (info->page_redirect >= 400 /* && <= 599 */ && strchr(&(page_name[4]), ':')) {
// 4xx/5xx require a local template file
- debugs(0, DBG_CRITICAL, "FATAL: status " << info->page_redirect << " is not valid on '" << page_name << "'");
+ debugs(0, DBG_CRITICAL, "FATAL: status " << info->page_redirect << " requires a template on '" << page_name << "'");
self_destruct();
}
// else okay.
static int
errorPageId(const char *page_name)
{
- for (int i = 0; i < ERR_MAX; i++) {
+ for (int i = 0; i < ERR_MAX; ++i) {
if (strcmp(err_type_str[i], page_name) == 0)
return i;
}
- for (size_t j = 0; j < ErrorDynamicPages.size(); j++) {
+ for (size_t j = 0; j < ErrorDynamicPages.size(); ++j) {
if (strcmp(ErrorDynamicPages.items[j]->page_name, page_name) == 0)
return j + ERR_MAX;
}
return "ERR_UNKNOWN"; /* should not happen */
}
-ErrorState *
-errorCon(err_type type, http_status status, HttpRequest * request)
+ErrorState::ErrorState(err_type t, http_status status, HttpRequest * req) :
+ type(t),
+ page_id(t),
+ err_language(NULL),
+ httpStatus(status),
+#if USE_AUTH
+ auth_user_request (NULL),
+#endif
+ request(NULL),
+ url(NULL),
+ xerrno(0),
+ port(0),
+ dnsError(),
+ ttl(0),
+ src_addr(),
+ redirect_url(NULL),
+ callback(NULL),
+ callback_data(NULL),
+ request_hdrs(NULL),
+ err_msg(NULL),
+#if USE_SSL
+ detail(NULL),
+#endif
+ detailCode(ERR_DETAIL_NONE)
{
- ErrorState *err = new ErrorState;
- err->page_id = type; /* has to be reset manually if needed */
- err->err_language = NULL;
- err->type = type;
- err->httpStatus = status;
- if (err->page_id >= ERR_MAX && ErrorDynamicPages.items[err->page_id - ERR_MAX]->page_redirect != HTTP_STATUS_NONE)
- err->httpStatus = ErrorDynamicPages.items[err->page_id - ERR_MAX]->page_redirect;
-
- if (request != NULL) {
- err->request = HTTPMSGLOCK(request);
- err->src_addr = request->client_addr;
- request->detailError(type, ERR_DETAIL_NONE);
- }
+ memset(&flags, 0, sizeof(flags));
+ memset(&ftp, 0, sizeof(ftp));
+
+ if (page_id >= ERR_MAX && ErrorDynamicPages.items[page_id - ERR_MAX]->page_redirect != HTTP_STATUS_NONE)
+ httpStatus = ErrorDynamicPages.items[page_id - ERR_MAX]->page_redirect;
- return err;
+ if (req != NULL) {
+ request = HTTPMSGLOCK(req);
+ src_addr = req->client_addr;
+ }
}
void
*/
assert(EBIT_TEST(entry->flags, ENTRY_ABORTED));
assert(entry->mem_obj->nclients == 0);
- errorStateFree(err);
+ delete err;
return;
}
entry->negativeCache();
entry->releaseRequest();
entry->unlock();
- errorStateFree(err);
+ delete 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);
- /*
- * 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->detailError(err->type, err->xerrno);
+ debugs(4, 3, HERE << conn << ", err=" << err);
+ assert(Comm::IsConnOpen(conn));
/* moved in front of errorBuildBuf @?@ */
err->flags.flag_cbdata = 1;
MemBuf *mb = rep->pack();
AsyncCall::Pointer call = commCbCall(78, 5, "errorSendComplete",
CommIoCbPtrFun(&errorSendComplete, err));
- Comm::Write(fd, mb, call);
+ Comm::Write(conn, mb, call);
delete mb;
delete rep;
* 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();
}
}
- errorStateFree(err);
+ delete err;
}
-void
-errorStateFree(ErrorState * err)
+ErrorState::~ErrorState()
{
- HTTPMSGUNLOCK(err->request);
- safe_free(err->redirect_url);
- safe_free(err->url);
- safe_free(err->request_hdrs);
- wordlistDestroy(&err->ftp.server_msg);
- safe_free(err->ftp.request);
- safe_free(err->ftp.reply);
+ HTTPMSGUNLOCK(request);
+ safe_free(redirect_url);
+ safe_free(url);
+ safe_free(request_hdrs);
+ wordlistDestroy(&ftp.server_msg);
+ safe_free(ftp.request);
+ safe_free(ftp.reply);
#if USE_AUTH
- err->auth_user_request = NULL;
+ auth_user_request = NULL;
#endif
- safe_free(err->err_msg);
+ safe_free(err_msg);
#if USE_ERR_LOCALES
- if (err->err_language != Config.errorDefaultLanguage)
+ if (err_language != Config.errorDefaultLanguage)
#endif
- safe_free(err->err_language);
+ safe_free(err_language);
#if USE_SSL
- delete err->detail;
+ delete detail;
#endif
- cbdataFree(err);
}
int
else
urlpath_or_slash = "/";
- str.Printf("%s " SQUIDSTRINGPH " HTTP/%d.%d\n",
+ str.Printf("%s " SQUIDSTRINGPH " %s/%d.%d\n",
RequestMethodStr(request->method),
SQUIDSTRINGPRINT(urlpath_or_slash),
+ AnyP::ProtocolType_str[request->http_ver.protocol],
request->http_ver.major, request->http_ver.minor);
packerToMemInit(&pck, &str);
request->header.packInto(&pck);
if (ftp.request) {
str.Printf("FTP Request: %s\r\n", ftp.request);
- str.Printf("FTP Reply: %s\r\n", ftp.reply);
+ str.Printf("FTP Reply: %s\r\n", (ftp.reply? ftp.reply:"[none]"));
str.Printf("FTP Msg: ");
wordlistCat(ftp.server_msg, &str);
str.Printf("\r\n");
#if USE_SSL
// currently only SSL error details implemented
else if (detail) {
+ detail->useRequest(request);
const String &errDetail = detail->toString();
if (errDetail.defined()) {
MemBuf *detail_mb = ConvertText(errDetail.termedBuf(), false);
case 'F':
if (building_deny_info_url) break;
/* FTP REPLY LINE */
- if (ftp.request)
+ if (ftp.reply)
p = ftp.reply;
else
p = "nothing";
break;
case 'I':
- if (request && request->hier.host[0] != '\0') // if non-empty string
- mb.Printf("%s", request->hier.host);
+ if (request && request->hier.tcpServer != NULL)
+ p = request->hier.tcpServer->remote.NtoA(ntoabuf,MAX_IPSTRLEN);
else if (!building_deny_info_url)
p = "[unknown]";
break;
case 'R':
if (building_deny_info_url) {
p = (request->urlpath.size() != 0 ? request->urlpath.termedBuf() : "/");
+ no_urlescape = 1;
break;
}
if (NULL != request) {
else
urlpath_or_slash = "/";
- mb.Printf("%s " SQUIDSTRINGPH " HTTP/%d.%d\n",
+ mb.Printf("%s " SQUIDSTRINGPH " %s/%d.%d\n",
RequestMethodStr(request->method),
SQUIDSTRINGPRINT(urlpath_or_slash),
+ AnyP::ProtocolType_str[request->http_ver.protocol],
request->http_ver.major, request->http_ver.minor);
packerToMemInit(&pck, &mb);
- request->header.packInto(&pck);
+ request->header.packInto(&pck, true); //hide authorization data
packerClean(&pck);
} else if (request_hdrs) {
p = request_hdrs;
/* 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");
+ debugs(0, DBG_CRITICAL, "WARNING: deny_info now accepts coded tags. Use %u to get the full URL instead of %s");
} else
p = visible_appname_string;
break;
const char *name = errorPageName(page_id);
/* no LMT for error pages; error pages expire immediately */
- if (name[0] == '3' || (name[0] != '4' && name[0] != '5' && strchr(name, ':'))) {
+ if (name[0] == '3' || (name[0] != '2' && name[0] != '4' && name[0] != '5' && strchr(name, ':'))) {
/* Redirection */
http_status status = HTTP_MOVED_TEMPORARILY;
// Use configured 3xx reply status if set.
rep->header.putStr(HDR_CONTENT_LANGUAGE, "en");
}
- httpBodySet(&rep->body, content);
+ rep->body.setMb(content);
/* do not memBufClean() or delete the content, it was absorbed by httpBody */
}
+ // Make sure error codes get back to the client side for logging and
+ // error tracking.
+ if (request) {
+ int edc = ERR_DETAIL_NONE; // error detail code
+#if USE_SSL
+ if (detail)
+ edc = detail->errorNo();
+ else
+#endif
+ if (detailCode)
+ edc = detailCode;
+ else
+ edc = xerrno;
+ request->detailError(type, edc);
+ }
+
return rep;
}
assert(page_id > ERR_NONE && page_id < error_page_count);
#if USE_ERR_LOCALES
- String hdr;
- char dir[256];
- int l = 0;
- const char *freePage = NULL;
+ ErrorPageFile *localeTmpl = NULL;
/** 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 && 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
- char *dt = NULL;
-
- /* prep the directory path string to prevent snprintf ... */
- l = strlen(DEFAULT_SQUID_ERROR_DIR);
- memcpy(dir, DEFAULT_SQUID_ERROR_DIR, l);
- dir[ l++ ] = '/';
- reset = dt = dir + l;
-
- debugs(4, 6, HERE << "Testing Header: '" << hdr << "'");
-
- while ( pos < hdr.size() ) {
-
- /* 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 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]);
- // 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 << "', pos=" << pos << ", buf='" << ((pos < hdr.size()) ? hdr.substr(pos,hdr.size()) : "") << "'");
-
- /* if we found anything we might use, try it. */
- 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) {
- /* store the language we found for the Content-Language reply header */
- err_language = xstrdup(reset);
- freePage = m;
- break;
- } else if (Config.errorLogMissingLanguages) {
- debugs(4, DBG_IMPORTANT, "WARNING: Error Pages Missing Language: " << reset);
- }
-
-#if HAVE_GLOB
- 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.");
- }
-#endif
- }
-
- dt = reset; // reset for next tag testing. we replace the failed name instead of cloning.
-
- // 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++;
+ if (!Config.errorDirectory && page_id < ERR_MAX) {
+ if (err_language && err_language != Config.errorDefaultLanguage)
+ safe_free(err_language);
+
+ localeTmpl = new ErrorPageFile(err_type_str[page_id], static_cast<err_type>(page_id));
+ if (localeTmpl->loadFor(request)) {
+ m = localeTmpl->text();
+ assert(localeTmpl->language());
+ err_language = xstrdup(localeTmpl->language());
}
}
#endif /* USE_ERR_LOCALES */
MemBuf *result = ConvertText(m, true);
#if USE_ERR_LOCALES
- safe_free(freePage);
+ if (localeTmpl)
+ delete localeTmpl;
#endif
-
return result;
}