Round 2;
Just the error page auto-negotiation process. This is based on optional
pre-translated error pages supplied by other patch.
This patch:
* Converts error_directory squid.conf option into an optional override.
Providing backward compatibility with older configurations and local
customizations.
* Adds error_default_language to administratively set the backup
language presented by Squid.
* Fixes design of previously broken hard-coded failover language.
WAS: default to FreeBSD error install location. Hidden by
error_directory being required.
NOW: failover to build-time configured errors/ directory 'English'
templates.
* Adds --enable-auto-locale configure option to enable the following.
* Adds logic to locate visitors most-preferred of available languages
and present a tailored error page as their reply.
Amos
=== modified file 'configure.in'
--- configure.in 2008-07-11 19:32:10 +0000
+++ configure.in 2008-07-23 11:23:05 +0000
@@ -30,6 +30,9 @@
AC_PROG_CXX
AC_CANONICAL_HOST
+dnl Make location configure settings available to the code
+AC_DEFINE_UNQUOTED([DEFAULT_SQUID_CONFIG_DIR], "${sysconfdir}" , [Location of Configuration files] )
+AC_DEFINE_UNQUOTED([DEFAULT_SQUID_DATA_DIR], "${datadir}" , [Location of other data files] )
use_loadable_modules=1
AC_MSG_CHECKING(whether to use loadable modules)
@@ -3694,6 +3697,22 @@
fi
fi
+dnl Squid now has limited locale handling ...
+dnl on error pages
+AC_ARG_ENABLE(auto-locale,
+[ --enable-auto-locale This enables squid to lookup translated error pages
+ based on the clients request headers. Without it squid
+ is limited to a single language set in squid.conf],
+[ if test "$enableval" = "yes" ; then
+ echo "Enabling Multi-Language Support"
+ AC_DEFINE(USE_ERR_LOCALES,1,[Use multi-language support on error pages])
+ else
+ echo "Disabling Multi-Language Support"
+ AC_DEFINE(USE_ERR_LOCALES,0,[Use multi-language support on error pages])
+ fi
+])
+
+
dnl Need the debugging version of malloc if available
XTRA_OBJS=''
if test "$ac_cv_lib_malloc_main" = "yes" ; then
=== modified file 'src/Makefile.am'
--- src/Makefile.am 2008-07-14 17:08:55 +0000
+++ src/Makefile.am 2008-07-23 11:21:37 +0000
@@ -1053,7 +1053,8 @@
DEFAULT_UNLINKD = $(libexecdir)/`echo unlinkd | sed '$(transform);s/$$/$(EXEEXT)/'`
DEFAULT_DISKD = $(libexecdir)/`echo diskd | sed '$(transform);s/$$/$(EXEEXT)/'`
DEFAULT_ICON_DIR = $(datadir)/icons
-DEFAULT_ERROR_DIR = $(datadir)/errors/@ERR_DEFAULT_LANGUAGE@
+DEFAULT_ERROR_DIR = $(datadir)/errors
+DEFAULT_LANGUAGE = @ERR_DEFAULT_LANGUAGE@
DEFAULT_MIB_PATH = $(datadir)/mib.txt
DEFAULT_HOSTS = @OPT_DEFAULT_HOSTS@
@@ -1090,7 +1091,7 @@
## FIXME: generate a sed command file from configure. Then this doesn't
-## depend on the Makefile.
+## depend on the Makefile.
cf.data: cf.data.pre Makefile
sed "\
[EMAIL PROTECTED]@%$(DEFAULT_HTTP_PORT)%g;\
@@ -1110,6 +1111,7 @@
[EMAIL PROTECTED]@%$(DEFAULT_ICON_DIR)%g;\
[EMAIL PROTECTED]@%$(DEFAULT_MIB_PATH)%g;\
[EMAIL PROTECTED]@%$(DEFAULT_ERROR_DIR)%g;\
+ [EMAIL PROTECTED]@%$(DEFAULT_LANGUAGE)%g;\
[EMAIL PROTECTED]@%$(DEFAULT_PREFIX)%g;\
[EMAIL PROTECTED]@%$(DEFAULT_HOSTS)%g;\
[EMAIL PROTECTED]@%$(VERSION)%g;"\
=== modified file 'src/cache_cf.cc'
--- src/cache_cf.cc 2008-07-17 12:38:06 +0000
+++ src/cache_cf.cc 2008-07-23 11:21:37 +0000
@@ -522,7 +522,8 @@
requirePathnameExists("Icon Directory", Config.icons.directory);
- requirePathnameExists("Error Directory", Config.errorDirectory);
+ if(Config.errorDirectory)
+ requirePathnameExists("Error Directory", Config.errorDirectory);
#if HTTP_VIOLATIONS
=== modified file 'src/cf.data.pre'
--- src/cf.data.pre 2008-07-17 15:17:06 +0000
+++ src/cf.data.pre 2008-07-23 11:21:37 +0000
@@ -4712,17 +4712,40 @@
NAME: error_directory
TYPE: string
LOC: Config.errorDirectory
-DEFAULT: @DEFAULT_ERROR_DIR@
+DEFAULT: none
DOC_START
If you wish to create your own versions of the default
- (English) error files, either to customize them to suit your
- language or company copy the template English files to another
- directory and point this tag at them.
+ error files to customize them to suit your company copy
+ the error/template files to another directory and point
+ this tag at them.
+
+ WARNING: This option will disable multi-language support
+ on error pages if used.
The squid developers are interested in making squid available in
a wide variety of languages. If you are making translations for a
- langauge that Squid does not currently provide please consider
+ language that Squid does not currently provide please consider
contributing your translation back to the project.
+ http://wiki.squid-cache.org/Translations
+
+ The squid developers working on translations are happy to supply drop-in
+ translated error files in exchange for any new language contributions.
+DOC_END
+
+NAME: error_default_language
+IFDEF: USE_ERR_LOCALES
+TYPE: string
+LOC: Config.errorDefaultLanguage
+DEFAULT: @DEFAULT_LANGUAGE@
+DOC_START
+ Set the default language which squid will send error pages in
+ if no existing translation matches the clients language
+ preferences.
+
+ The squid developers are interested in making squid available in
+ a wide variety of languages. If you are interested in making
+ translations for any language see the squid wiki for details.
+ http://wiki.squid-cache.org/Translations
DOC_END
NAME: err_html_text
@@ -4759,7 +4782,7 @@
DOC_START
Usage: deny_info err_page_name acl
or deny_info http://... acl
- Example: deny_info ERR_CUSTOM_ACCESS_DENIED bad_guys
+ or deny_info TCP_RESET acl
This can be used to return a ERR_ page for requests which
do not pass the 'http_access' rules. Squid remembers the last
@@ -4773,8 +4796,9 @@
- When none of the http_access lines matches. It's then the last
acl processed on the last http_access line.
- You may use ERR_ pages that come with Squid or create your own pages
- and put them into the configured errors/ directory.
+ NP: If providing your own custom error pages with error_directory
+ you may also specify them by your custom file name:
+ Example: deny_info ERR_CUSTOM_ACCESS_DENIED bad_guys
Alternatively you can specify an error URL. The browsers will
get redirected (302) to the specified URL. %s in the redirection
=== modified file 'src/defines.h'
--- src/defines.h 2008-04-07 10:30:11 +0000
+++ src/defines.h 2008-07-23 11:21:37 +0000
@@ -217,11 +217,6 @@
*/
#define N_COUNT_HOUR_HIST (86400 * 3) / (60 * COUNT_INTERVAL)
-/* were to look for errors if config path fails */
-#ifndef DEFAULT_SQUID_ERROR_DIR
-#define DEFAULT_SQUID_ERROR_DIR "/usr/local/squid/etc/errors"
-#endif
-
/* handy to determine the #elements in a static array */
#define countof(arr) (sizeof(arr)/sizeof(*arr))
=== modified file 'src/errorpage.cc'
--- src/errorpage.cc 2008-07-22 12:33:41 +0000
+++ src/errorpage.cc 2008-07-23 11:21:37 +0000
@@ -58,6 +58,13 @@
*/
+#ifndef DEFAULT_SQUID_ERROR_DIR
+/** Where to look for errors if config path fails.
+ \note Please use ./configure --datadir=/path instead of patching
+ */
+#define DEFAULT_SQUID_ERROR_DIR DEFAULT_SQUID_DATA_DIR"/errors"
+#endif
+
/// \ingroup ErrorPageInternal
CBDATA_CLASS_INIT(ErrorState);
@@ -115,7 +122,7 @@
/// \ingroup ErrorPageInternal
static int error_page_count = 0;
-static char *errorTryLoadText(const char *page_name, const char *dir);
+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);
@@ -147,20 +154,30 @@
for (i = ERR_NONE, ++i; i < error_page_count; ++i) {
safe_free(error_text[i]);
- /* hard-coded ? */
- if ((text = errorFindHardText(i)))
+ if ((text = errorFindHardText(i))) {
+ /**\par
+ * Index any hard-coded error text into defaults.
+ */
error_text[i] = xstrdup(text);
- else if (i < ERR_MAX) {
- /* precompiled ? */
+
+ } else if (i < ERR_MAX) {
+ /**\par
+ * Index precompiled fixed template files from one of two sources:
+ * (a) default language translation directory (error_default_language)
+ * (b) admin specified custom directory (error_directory)
+ */
error_text[i] = errorLoadText(err_type_str[i]);
+
} else {
- /* dynamic */
+ /** \par
+ * Index any unknown file names used by deny_info.
+ */
ErrorDynamicPageInfo *info = ErrorDynamicPages.items[i - ERR_MAX];
assert(info && info->id == i && info->page_name);
if (strchr(info->page_name, ':') == NULL) {
- /* Not on redirected errors... */
+ /** But only if they are not redirection URL. */
error_text[i] = errorLoadText(info->page_name);
}
}
@@ -198,17 +215,38 @@
return NULL;
}
-
-/// \ingroup ErrorPageInternal
+/**
+ * \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)
{
- /* test configured location */
- char *text = errorTryLoadText(page_name, Config.errorDirectory);
- /* test default location if failed */
-
- if (!text && strcmp(Config.errorDirectory, DEFAULT_SQUID_ERROR_DIR))
- text = errorTryLoadText(page_name, DEFAULT_SQUID_ERROR_DIR);
+ char *text = NULL;
+
+ /** test error_directory configured location */
+ if(Config.errorDirectory)
+ text = errorTryLoadText(page_name, Config.errorDirectory);
+
+#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 language. Reset to English");
+ }
+ }
+#endif
+
+ /* test default location if failed (templates == English translation base templates) */
+ if (!text)
+ text = errorTryLoadText(page_name, DEFAULT_SQUID_ERROR_DIR"/templates");
/* giving up if failed */
if (!text)
@@ -219,7 +257,7 @@
/// \ingroup ErrorPageInternal
static char *
-errorTryLoadText(const char *page_name, const char *dir)
+errorTryLoadText(const char *page_name, const char *dir, bool silent)
{
int fd;
char path[MAXPATHLEN];
@@ -232,7 +270,9 @@
fd = file_open(path, O_RDONLY | O_TEXT);
if (fd < 0) {
- debugs(4, 0, "errorTryLoadText: '" << path << "': " << xstrerror());
+ /* with dynamic locale negotiation we may see some failures before a success. */
+ if(!silent)
+ debugs(4, DBG_CRITICAL, HERE << "'" << path << "': " << xstrerror());
return NULL;
}
@@ -821,13 +861,88 @@
ErrorState::BuildContent()
{
MemBuf *content = new MemBuf;
- const char *m;
+ const char *m = NULL;
const char *p;
const char *t;
+
assert(page_id > ERR_NONE && page_id < error_page_count);
+
+#if USE_ERR_LOCALES
+ String hdr;
+ char dir[256];
+ int l = 0;
+
+ /** error_directory option in squid.conf overrides translations.
+ * Otherwise locate the Accept-Language header
+ */
+ if(!Config.errorDirectory && 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
+ 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() ) {
+
+/*
+ * 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++ = xtolower(buf[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] << "'");
+
+ /* if we found anything we might use, try it. */
+ if(*reset != '\0') {
+
+ debugs(4, 6, HERE << "Found language '" << reset << "', testing for available template in: '" << dir << "'");
+ m = errorTryLoadText( err_type_str[page_id], dir, false);
+
+ if(m) break; // FOUND IT!!
+
+#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 ';' we need to skip the 'q=' bit to the next ',' or end.
+ while(pos < hdr.size() && buf[pos] != ',') pos++;
+ if(buf[pos] == ',') pos++;
+ }
+ }
+#endif /* USE_ERR_LOCALES */
+
+ /** \par
+ * If client-specific error templates are not enabled or available.
+ * fall back to the old style squid.conf settings.
+ */
+ if(!m) {
+ m = error_text[page_id];
+ debugs(4, 1, HERE << "No existing languages found. Fall back on default language.");
+ }
+
+ assert(m);
content->init();
- m = error_text[page_id];
- assert(m);
while ((p = strchr(m, '%'))) {
content->append(m, p - m); /* copy */
=== modified file 'src/structs.h'
--- src/structs.h 2008-07-17 12:38:06 +0000
+++ src/structs.h 2008-07-23 11:21:37 +0000
@@ -549,6 +549,9 @@
int use_short_names;
} icons;
char *errorDirectory;
+#if USE_ERR_LOCALES
+ char *errorDefaultLanguage;
+#endif
struct
{