Implements the ForceLanguagePriority directive from 2.0.32 in 1.3.23
ftp://ftp.nettonettech.com/download/mod_negotiation.patch
---------------------------------------------------------------------
--- mod_negotiation-old.c Thu Mar 21 12:30:16 2002
+++ mod_negotiation.c Thu Mar 21 12:32:39 2002
@@ -76,16 +76,28 @@
*/
typedef struct {
+ int forcelangpriority;
array_header *language_priority;
} neg_dir_config;
+/* forcelangpriority flags
+ */
+#define FLP_UNDEF 0 /* Same as FLP_DEFAULT, but base overrides */
+#define FLP_NONE 1 /* Return 406, HTTP_NOT_ACCEPTABLE */
+#define FLP_PREFER 2 /* Use language_priority rather than MC */
+#define FLP_FALLBACK 4 /* Use language_priority rather than NA */
+
+#define FLP_DEFAULT FLP_PREFER
+
module MODULE_VAR_EXPORT negotiation_module;
static void *create_neg_dir_config(pool *p, char *dummy)
{
neg_dir_config *new = (neg_dir_config *) ap_palloc(p,
sizeof(neg_dir_config));
-
- new->language_priority = ap_make_array(p, 4, sizeof(char *));
+
+ new->forcelangpriority = FLP_UNDEF;
+ new->language_priority = NULL;
+
return new;
}
@@ -96,17 +108,54 @@
neg_dir_config *new = (neg_dir_config *) ap_palloc(p,
sizeof(neg_dir_config));
/* give priority to the config in the subdirectory */
- new->language_priority = ap_append_arrays(p, add->language_priority,
- base->language_priority);
+ new->forcelangpriority = (add->forcelangpriority != FLP_UNDEF)
+ ? add->forcelangpriority
+ : base->forcelangpriority;
+ new->language_priority = add->language_priority
+ ? add->language_priority
+ : base->language_priority;
return new;
}
-static const char *set_language_priority(cmd_parms *cmd, void *n, char
*lang)
+static const char *set_language_priority(cmd_parms *cmd, void *n_, const
char *lang)
{
- array_header *arr = ((neg_dir_config *) n)->language_priority;
- char **langp = (char **) ap_push_array(arr);
+ neg_dir_config *n = n_;
+ const char **langp;
+
+ if (!n->language_priority)
+ n->language_priority = ap_make_array(cmd->pool, 4, sizeof(char *));
+
+ langp = (const char **) ap_push_array(n->language_priority);
+ *langp = lang;
+ return NULL;
+}
+
+static const char *set_force_priority(cmd_parms *cmd, void *n_, const char
*w)
+{
+ neg_dir_config *n = n_;
+
+ if (!strcasecmp(w, "None")) {
+ if (n->forcelangpriority & ~FLP_NONE) {
+ return "Cannot combine ForceLanguagePriority options with None";
+ }
+ n->forcelangpriority = FLP_NONE;
+ }
+ else if (!strcasecmp(w, "Prefer")) {
+ if (n->forcelangpriority & FLP_NONE) {
+ return "Cannot combine ForceLanguagePriority options None and
Prefer";
+ }
+ n->forcelangpriority |= FLP_PREFER;
+ }
+ else if (!strcasecmp(w, "Fallback")) {
+ if (n->forcelangpriority & FLP_NONE) {
+ return "Cannot combine ForceLanguagePriority options None and
Fallback";
+ }
+ n->forcelangpriority |= FLP_FALLBACK;
+ }
+ else {
+ return ap_pstrcat(cmd->pool, "Invalid ForceLanguagePriority option
", w, NULL);
+ }
- *langp = lang;
return NULL;
}
@@ -130,6 +179,8 @@
"no arguments (either present or absent)"},
{"LanguagePriority", set_language_priority, NULL, OR_FILEINFO, ITERATE,
"space-delimited list of MIME language abbreviations"},
+ {"ForceLanguagePriority", set_force_priority, NULL, OR_FILEINFO,
ITERATE,
+ "Force LanguagePriority elections, either None, or Fallback and/or
Prefer"},
{NULL}
};
@@ -211,6 +262,7 @@
typedef struct {
pool *pool;
request_rec *r;
+ neg_dir_config *conf;
char *dir_name;
int accept_q; /* 1 if an Accept item has a q= param */
float default_lang_quality; /* fiddle lang q for variants with no lang
*/
@@ -475,6 +527,10 @@
new->pool = r->pool;
new->r = r;
+
+ new->conf = (neg_dir_config *)ap_get_module_config(r->per_dir_config,
+
&negotiation_module);
+
new->dir_name = ap_make_dirstr_parent(r->pool, r->filename);
new->accepts = do_header_line(r->pool, ap_table_get(hdrs, "Accept"));
@@ -1219,48 +1275,21 @@
static int find_lang_index(array_header *accept_langs, char *lang)
{
- accept_rec *accs;
+ const char **alang;
int i;
if (!lang || !accept_langs) {
return -1;
}
- accs = (accept_rec *) accept_langs->elts;
+ alang = (const char **) accept_langs->elts;
for (i = 0; i < accept_langs->nelts; ++i) {
- if (!strncmp(lang, accs[i].name, strlen(accs[i].name))) {
- return i;
- }
- }
-
- return -1;
-}
-
-/* This function returns the priority of a given language
- * according to LanguagePriority. It is used in case of a tie
- * between several languages.
- */
-static int find_default_index(neg_dir_config *conf, char *lang)
-{
- array_header *arr;
- int nelts;
- char **elts;
- int i;
-
- if (!lang) {
- return -1;
- }
-
- arr = conf->language_priority;
- nelts = arr->nelts;
- elts = (char **) arr->elts;
-
- for (i = 0; i < nelts; ++i) {
- if (!strcasecmp(elts[i], lang)) {
+ if (!strncmp(lang, *alang, strlen(*alang))) {
return i;
}
+ alang += (accept_langs->elt_size / sizeof(char*));
}
return -1;
@@ -1301,7 +1330,7 @@
}
/* Set the language_quality value in the variant record. Also
- * assigns lang_index for back-compat.
+ * assigns lang_index for ForceLanguagePriority.
*
* To find the language_quality value, we look for the 'q' value
* of the 'best' matching language on the Accept-Language
@@ -1333,8 +1362,11 @@
static void set_language_quality(negotiation_state *neg, var_rec *variant)
{
- char *firstlang;
- int idx;
+
+ int forcepriority = neg->conf->forcelangpriority;
+ if (forcepriority == FLP_UNDEF) {
+ forcepriority = FLP_DEFAULT;
+ }
if (!variant->content_languages || !variant->content_languages->nelts)
{
/* This variant has no content-language, so use the default
@@ -1349,9 +1381,9 @@
if (!neg->accept_langs) {
return; /* no accept-language header */
}
-
+ return;
}
- else {
+ else {
/* Variant has one (or more) languages. Look for the best
* match. We do this by going through each language on the
* variant description looking for a match on the
@@ -1363,7 +1395,7 @@
if (!neg->accept_langs) {
/* no accept-language header makes the variant indefinite */
- variant->definite = 0;
+ variant->definite = 0;
}
else { /* There is an accept-language with 0 or more items */
accept_rec *accs = (accept_rec *) neg->accept_langs->elts;
@@ -1484,27 +1516,53 @@
}
}
- /* Now set the old lang_index field. Since this is old
- * stuff anyway, don't bother with handling multiple languages
- * per variant, just use the first one assigned to it
+ /* Handle the ForceDefaultLanguage overrides, based on the best match
+ * to LanguagePriority order. The best match is the lowest index of
+ * any LanguagePriority match.
*/
- idx = 0;
- if (variant->content_languages && variant->content_languages->nelts) {
- firstlang = ((char **) variant->content_languages->elts)[0];
- }
- else {
- firstlang = "";
- }
- if (!neg->accept_langs) { /* Client doesn't care */
- idx = find_default_index((neg_dir_config *) ap_get_module_config(
- neg->r->per_dir_config,
&negotiation_module),
- firstlang);
- }
- else { /* Client has Accept-Language */
- idx = find_lang_index(neg->accept_langs, firstlang);
- }
- variant->lang_index = idx;
+ if (((forcepriority & FLP_PREFER)
+ && (variant->lang_index < 0))
+ || ((forcepriority & FLP_FALLBACK)
+ && !variant->lang_quality))
+ {
+ int bestidx = -1;
+ int j;
+
+ for (j = 0; j < variant->content_languages->nelts; ++j)
+ {
+ /* lang is the variant's language-tag, which is the one
+ * we are allowed to use the prefix of in HTTP/1.1
+ */
+ char *lang = ((char **) (variant->content_languages->elts))[j];
+ int idx = -1;
+
+ /* If we wish to fallback or
+ * we use our own LanguagePriority index.
+ */
+
+ idx = find_lang_index(neg->conf->language_priority, lang);
+
+ if ((idx >= 0) && ((bestidx == -1) || (idx < bestidx))) {
+ bestidx = idx;
+ }
+ }
+ if (bestidx >= 0) {
+ if (variant->lang_quality) {
+ if (forcepriority & FLP_PREFER) {
+ variant->lang_index = bestidx;
+ }
+ }
+ else {
+ if (forcepriority & FLP_FALLBACK) {
+ variant->lang_index = bestidx;
+ variant->lang_quality = .0001f;
+ variant->definite = 0;
+ }
+ }
+ }
+ }
+
return;
}