=== 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 "\ s%@DEFAULT_HTTP_PORT@%$(DEFAULT_HTTP_PORT)%g;\ @@ -1110,6 +1111,7 @@ s%@DEFAULT_ICON_DIR@%$(DEFAULT_ICON_DIR)%g;\ s%@DEFAULT_MIB_PATH@%$(DEFAULT_MIB_PATH)%g;\ s%@DEFAULT_ERROR_DIR@%$(DEFAULT_ERROR_DIR)%g;\ + s%@DEFAULT_LANGUAGE@%$(DEFAULT_LANGUAGE)%g;\ s%@DEFAULT_PREFIX@%$(DEFAULT_PREFIX)%g;\ s%@DEFAULT_HOSTS@%$(DEFAULT_HOSTS)%g;\ s%@[V]ERSION@%$(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/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 { === modified file 'src/errorpage.cc' --- src/errorpage.cc 2008-07-22 12:33:41 +0000 +++ src/errorpage.cc 2008-07-28 01:53:20 +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: '-'? .* + * 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++]); + } + *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: '-*' */ + /* 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 */