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; }