mod_negotiation.c revision 0c9166d0186cf0e1ad397025f730ae6967f44ce6
842ae4bd224140319ae7feec1872b93dfd491143fielding/* Copyright 1999-2006 The Apache Software Foundation or its licensors, as
842ae4bd224140319ae7feec1872b93dfd491143fielding * applicable.
842ae4bd224140319ae7feec1872b93dfd491143fielding *
842ae4bd224140319ae7feec1872b93dfd491143fielding * Licensed under the Apache License, Version 2.0 (the "License");
842ae4bd224140319ae7feec1872b93dfd491143fielding * you may not use this file except in compliance with the License.
842ae4bd224140319ae7feec1872b93dfd491143fielding * You may obtain a copy of the License at
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes *
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * http://www.apache.org/licenses/LICENSE-2.0
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes *
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * Unless required by applicable law or agreed to in writing, software
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * distributed under the License is distributed on an "AS IS" BASIS,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * See the License for the specific language governing permissions and
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * limitations under the License.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes/*
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * mod_negotiation.c: keeps track of MIME types the client is willing to
e8f95a682820a599fe41b22977010636be5c2717jim * accept, and contains code to handle type arbitration.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes *
e8f95a682820a599fe41b22977010636be5c2717jim * rst
1747d30b98aa1bdbc43994c02cd46ab4cb9319e4fielding */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes#include "apr.h"
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes#include "apr_strings.h"
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes#include "apr_file_io.h"
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes#include "apr_lib.h"
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes#define APR_WANT_STRFUNC
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes#include "apr_want.h"
11f2c481e1d57bedb3f758565307501e9a2730ddtrawick
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes#include "ap_config.h"
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes#include "httpd.h"
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes#include "http_config.h"
5c0419d51818eb02045cf923a9fe456127a44c60wrowe#include "http_request.h"
5c0419d51818eb02045cf923a9fe456127a44c60wrowe#include "http_protocol.h"
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes#include "http_core.h"
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes#include "http_log.h"
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes#include "util_script.h"
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes#define MAP_FILE_MAGIC_TYPE "application/x-type-map"
d266c3777146d36a4c23c17aad6f153aebea1bf4jorton
d266c3777146d36a4c23c17aad6f153aebea1bf4jorton/* Commands --- configuring document caching on a per (virtual?)
22f8da8087791fcb95b836c8a81937c5a9bba202bnicholes * server basis...
22f8da8087791fcb95b836c8a81937c5a9bba202bnicholes */
22f8da8087791fcb95b836c8a81937c5a9bba202bnicholes
22f8da8087791fcb95b836c8a81937c5a9bba202bnicholestypedef struct {
22f8da8087791fcb95b836c8a81937c5a9bba202bnicholes int forcelangpriority;
22f8da8087791fcb95b836c8a81937c5a9bba202bnicholes apr_array_header_t *language_priority;
22f8da8087791fcb95b836c8a81937c5a9bba202bnicholes} neg_dir_config;
cd3bbd6d2df78d6c75e5d159a81ef8bdd5f70df9trawick
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes/* forcelangpriority flags
0f60998368b493f90120180a93fc2e1e74490872covener */
0f60998368b493f90120180a93fc2e1e74490872covener#define FLP_UNDEF 0 /* Same as FLP_DEFAULT, but base overrides */
0f60998368b493f90120180a93fc2e1e74490872covener#define FLP_NONE 1 /* Return 406, HTTP_NOT_ACCEPTABLE */
0f60998368b493f90120180a93fc2e1e74490872covener#define FLP_PREFER 2 /* Use language_priority rather than MC */
0f60998368b493f90120180a93fc2e1e74490872covener#define FLP_FALLBACK 4 /* Use language_priority rather than NA */
0f60998368b493f90120180a93fc2e1e74490872covener
0f60998368b493f90120180a93fc2e1e74490872covener#define FLP_DEFAULT FLP_PREFER
0f60998368b493f90120180a93fc2e1e74490872covener
87587593f1a53030e840acc0dec6cc881022ea40covener/* env evaluation
87587593f1a53030e840acc0dec6cc881022ea40covener */
87587593f1a53030e840acc0dec6cc881022ea40covener#define DISCARD_ALL_ENCODINGS 1 /* no-gzip */
87587593f1a53030e840acc0dec6cc881022ea40covener#define DISCARD_ALL_BUT_HTML 2 /* gzip-only-text/html */
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovenermodule AP_MODULE_DECLARE_DATA negotiation_module;
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholesstatic void *create_neg_dir_config(apr_pool_t *p, char *dummy)
43997561b2302d13dee973998e77743a3ddd2374trawick{
fa123db15501821e36e513afa78e839775ad2800covener neg_dir_config *new = (neg_dir_config *) apr_palloc(p,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes sizeof(neg_dir_config));
0568280364eb026393be492ebc732795c4934643jorton
0568280364eb026393be492ebc732795c4934643jorton new->forcelangpriority = FLP_UNDEF;
0568280364eb026393be492ebc732795c4934643jorton new->language_priority = NULL;
0568280364eb026393be492ebc732795c4934643jorton return new;
0568280364eb026393be492ebc732795c4934643jorton}
0568280364eb026393be492ebc732795c4934643jorton
0568280364eb026393be492ebc732795c4934643jortonstatic void *merge_neg_dir_configs(apr_pool_t *p, void *basev, void *addv)
0568280364eb026393be492ebc732795c4934643jorton{
0568280364eb026393be492ebc732795c4934643jorton neg_dir_config *base = (neg_dir_config *) basev;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes neg_dir_config *add = (neg_dir_config *) addv;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes neg_dir_config *new = (neg_dir_config *) apr_palloc(p,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes sizeof(neg_dir_config));
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* give priority to the config in the subdirectory */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes new->forcelangpriority = (add->forcelangpriority != FLP_UNDEF)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes ? add->forcelangpriority
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes : base->forcelangpriority;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes new->language_priority = add->language_priority
796e4a7141265d8ed7036e4628161c6eafb2a789jorton ? add->language_priority
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes : base->language_priority;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return new;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes}
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholesstatic const char *set_language_priority(cmd_parms *cmd, void *n_,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes const char *lang)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes{
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes neg_dir_config *n = n_;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes const char **langp;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (!n->language_priority)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes n->language_priority = apr_array_make(cmd->pool, 4, sizeof(char *));
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes langp = (const char **) apr_array_push(n->language_priority);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes *langp = lang;
8113dac419143273351446c3ad653f3fe5ba5cfdwrowe return NULL;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes}
95b6fe1346805e1731e6e97c15d569c73be22cf7minfrin
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholesstatic const char *set_force_priority(cmd_parms *cmd, void *n_, const char *w)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes{
95b6fe1346805e1731e6e97c15d569c73be22cf7minfrin neg_dir_config *n = n_;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
95b6fe1346805e1731e6e97c15d569c73be22cf7minfrin if (!strcasecmp(w, "None")) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (n->forcelangpriority & ~FLP_NONE) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return "Cannot combine ForceLanguagePriority options with None";
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes n->forcelangpriority = FLP_NONE;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
95b6fe1346805e1731e6e97c15d569c73be22cf7minfrin else if (!strcasecmp(w, "Prefer")) {
95b6fe1346805e1731e6e97c15d569c73be22cf7minfrin if (n->forcelangpriority & FLP_NONE) {
95b6fe1346805e1731e6e97c15d569c73be22cf7minfrin return "Cannot combine ForceLanguagePriority options None and "
a1790fb35c4b352dab721370985c623a9f8f5062rpluem "Prefer";
713a2b68bac4aeb1e9c48785006c0732451039depquerna }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes n->forcelangpriority |= FLP_PREFER;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes else if (!strcasecmp(w, "Fallback")) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (n->forcelangpriority & FLP_NONE) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return "Cannot combine ForceLanguagePriority options None and "
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe "Fallback";
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes n->forcelangpriority |= FLP_FALLBACK;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes else {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return apr_pstrcat(cmd->pool, "Invalid ForceLanguagePriority option ",
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes w, NULL);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return NULL;
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener}
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholesstatic const char *cache_negotiated_docs(cmd_parms *cmd, void *dummy,
f43b67c5a9d29b572eac916f8335cedc80c908bebnicholes int arg)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes{
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes ap_set_module_config(cmd->server->module_config, &negotiation_module,
8113dac419143273351446c3ad653f3fe5ba5cfdwrowe (arg ? "Cache" : NULL));
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return NULL;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes}
fa123db15501821e36e513afa78e839775ad2800covener
fa123db15501821e36e513afa78e839775ad2800covenerstatic int do_cache_negotiated_docs(server_rec *s)
fa123db15501821e36e513afa78e839775ad2800covener{
fa123db15501821e36e513afa78e839775ad2800covener return (ap_get_module_config(s->module_config,
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener &negotiation_module) != NULL);
fa123db15501821e36e513afa78e839775ad2800covener}
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovenerstatic const command_rec negotiation_cmds[] =
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener{
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener AP_INIT_FLAG("CacheNegotiatedDocs", cache_negotiated_docs, NULL, RSRC_CONF,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes "Either 'on' or 'off' (default)"),
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener AP_INIT_ITERATE("LanguagePriority", set_language_priority, NULL,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes OR_FILEINFO,
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener "space-delimited list of MIME language abbreviations"),
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes AP_INIT_ITERATE("ForceLanguagePriority", set_force_priority, NULL,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes OR_FILEINFO,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes "Force LanguagePriority elections, either None, or "
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes "Fallback and/or Prefer"),
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes {NULL}
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes};
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes/*
8113dac419143273351446c3ad653f3fe5ba5cfdwrowe * Record of available info on a media type specified by the client
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * (we also use 'em for encodings and languages)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholestypedef struct accept_rec {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes char *name; /* MUST be lowercase */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes float quality;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes float level;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes char *charset; /* for content-type only */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes} accept_rec;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
fa123db15501821e36e513afa78e839775ad2800covener/*
cceddc0b6c0fdaed0c73abda39975bb1d388243acovener * Record of available info on a particular variant
cceddc0b6c0fdaed0c73abda39975bb1d388243acovener *
fa123db15501821e36e513afa78e839775ad2800covener * Note that a few of these fields are updated by the actual negotiation
cceddc0b6c0fdaed0c73abda39975bb1d388243acovener * code. These are:
f2be127030aa4190033084f0a6add531c9bc41desf *
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * level_matched --- initialized to zero. Set to the value of level
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * if the client actually accepts this media type at that
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * level (and *not* if it got in on a wildcard). See level_cmp
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * below.
60215f303c7e1ce8b6d272acb5bfa5b3d99dfd34covener * mime_stars -- initialized to zero. Set to the number of stars
60215f303c7e1ce8b6d272acb5bfa5b3d99dfd34covener * present in the best matching Accept header element.
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * 1 for star/star, 2 for type/star and 3 for
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * type/subtype.
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener *
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * definite -- initialized to 1. Set to 0 if there is a match which
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * makes the variant non-definite according to the rules
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * in rfc2296.
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener */
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovenertypedef struct var_rec {
6683642c1e0032eeeed5f99e8c14880692ef84c5sf request_rec *sub_req; /* May be NULL (is, for map files) */
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener const char *mime_type; /* MUST be lowercase */
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener const char *file_name; /* Set to 'this' (for map file body content) */
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener apr_off_t body; /* Only for map file body content */
6683642c1e0032eeeed5f99e8c14880692ef84c5sf const char *content_encoding;
6683642c1e0032eeeed5f99e8c14880692ef84c5sf apr_array_header_t *content_languages; /* list of lang. for this variant */
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener const char *content_charset;
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener const char *description;
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener /* The next five items give the quality values for the dimensions
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * of negotiation for this variant. They are obtained from the
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * appropriate header lines, except for source_quality, which
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * is obtained from the variant itself (the 'qs' parameter value
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * from the variant's mime-type). Apart from source_quality,
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * these values are set when we find the quality for each variant
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * (see best_match()). source_quality is set from the 'qs' parameter
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * of the variant description or mime type: see set_mime_fields().
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener */
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener float lang_quality; /* quality of this variant's language */
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener float encoding_quality; /* ditto encoding */
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener float charset_quality; /* ditto charset */
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener float mime_type_quality; /* ditto media type */
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener float source_quality; /* source quality for this variant */
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* Now some special values */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes float level; /* Auxiliary to content-type... */
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener apr_off_t bytes; /* content length, if known */
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener int lang_index; /* Index into LanguagePriority list */
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener int is_pseudo_html; /* text/html, *or* the INCLUDES_MAGIC_TYPEs */
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener /* Above are all written-once properties of the variant. The
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * three fields below are changed during negotiation:
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener */
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener float level_matched;
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener int mime_stars;
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener int definite;
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener} var_rec;
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener/* Something to carry around the state of negotiation (and to keep
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener * all of this thread-safe)...
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholestypedef struct {
60215f303c7e1ce8b6d272acb5bfa5b3d99dfd34covener apr_pool_t *pool;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes request_rec *r;
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes neg_dir_config *conf;
0e05808dc59a321566303084c84b9826a4353cefrederpj char *dir_name;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes int accept_q; /* 1 if an Accept item has a q= param */
b08925593f214f621161742925dcf074a8047e0acovener float default_lang_quality; /* fiddle lang q for variants with no lang */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* the array pointers below are NULL if the corresponding accept
465bb68501690d7a47bfd2a6129580047d76d8f1rederpj * headers are not present
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf */
465bb68501690d7a47bfd2a6129580047d76d8f1rederpj apr_array_header_t *accepts; /* accept_recs */
e8f95a682820a599fe41b22977010636be5c2717jim apr_array_header_t *accept_encodings; /* accept_recs */
3dfeb02cfb853d8717ca0cc259b59fea610173f5bnicholes apr_array_header_t *accept_charsets; /* accept_recs */
3dfeb02cfb853d8717ca0cc259b59fea610173f5bnicholes apr_array_header_t *accept_langs; /* accept_recs */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes apr_array_header_t *avail_vars; /* available variants */
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes int count_multiviews_variants; /* number of variants found on disk */
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes int is_transparent; /* 1 if this resource is trans. negotiable */
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes
ebe5305f8b22507374358f32b74d12fb50c05a25covener int dont_fiddle_headers; /* 1 if we may not fiddle with accept hdrs */
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes int ua_supports_trans; /* 1 if ua supports trans negotiation */
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes int send_alternates; /* 1 if we want to send an Alternates header */
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes int may_choose; /* 1 if we may choose a variant for the client */
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes int use_rvsa; /* 1 if we must use RVSA/1.0 negotiation algo */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes} negotiation_state;
513b324e774c559b579896df131fd7c8471ed529rederpj
513b324e774c559b579896df131fd7c8471ed529rederpj/* A few functions to manipulate var_recs.
513b324e774c559b579896df131fd7c8471ed529rederpj * Cleaning out the fields...
513b324e774c559b579896df131fd7c8471ed529rederpj */
513b324e774c559b579896df131fd7c8471ed529rederpj
513b324e774c559b579896df131fd7c8471ed529rederpjstatic void clean_var_rec(var_rec *mime_info)
513b324e774c559b579896df131fd7c8471ed529rederpj{
513b324e774c559b579896df131fd7c8471ed529rederpj mime_info->sub_req = NULL;
513b324e774c559b579896df131fd7c8471ed529rederpj mime_info->mime_type = "";
513b324e774c559b579896df131fd7c8471ed529rederpj mime_info->file_name = "";
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes mime_info->body = 0;
02fd88c85a9850109753b87612955ad372de1575sf mime_info->content_encoding = NULL;
02fd88c85a9850109753b87612955ad372de1575sf mime_info->content_languages = NULL;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes mime_info->content_charset = "";
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes mime_info->description = "";
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes mime_info->is_pseudo_html = 0;
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes mime_info->level = 0.0f;
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes mime_info->level_matched = 0.0f;
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes mime_info->bytes = -1;
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes mime_info->lang_index = -1;
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes mime_info->mime_stars = 0;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes mime_info->definite = 1;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes mime_info->charset_quality = 1.0f;
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes mime_info->encoding_quality = 1.0f;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes mime_info->lang_quality = 1.0f;
707f6d077f73cc948deead8df5b40ea42c1eaa78covener mime_info->mime_type_quality = 1.0f;
707f6d077f73cc948deead8df5b40ea42c1eaa78covener mime_info->source_quality = 0.0f;
fa123db15501821e36e513afa78e839775ad2800covener}
707f6d077f73cc948deead8df5b40ea42c1eaa78covener
707f6d077f73cc948deead8df5b40ea42c1eaa78covener/* Initializing the relevant fields of a variant record from the
707f6d077f73cc948deead8df5b40ea42c1eaa78covener * accept_info read out of its content-type, one way or another.
707f6d077f73cc948deead8df5b40ea42c1eaa78covener */
707f6d077f73cc948deead8df5b40ea42c1eaa78covener
707f6d077f73cc948deead8df5b40ea42c1eaa78covenerstatic void set_mime_fields(var_rec *var, accept_rec *mime_info)
707f6d077f73cc948deead8df5b40ea42c1eaa78covener{
9ad7b260be233be7d7b5576979825cac72e15498rederpj var->mime_type = mime_info->name;
9ad7b260be233be7d7b5576979825cac72e15498rederpj var->source_quality = mime_info->quality;
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes var->level = mime_info->level;
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes var->content_charset = mime_info->charset;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes var->is_pseudo_html = (!strcmp(var->mime_type, "text/html")
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes || !strcmp(var->mime_type, INCLUDES_MAGIC_TYPE)
ebe5305f8b22507374358f32b74d12fb50c05a25covener || !strcmp(var->mime_type, INCLUDES_MAGIC_TYPE3));
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes}
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes/* Create a variant list validator in r using info from vlistr. */
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholesstatic void set_vlist_validator(request_rec *r, request_rec *vlistr)
54d22ed1c429b903b029bbd62621f11a9e286137minfrin{
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes /* Calculating the variant list validator is similar to
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * calculating an etag for the source of the variant list
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes * information, so we use ap_make_etag(). Note that this
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes * validator can be 'weak' in extreme case.
ebe5305f8b22507374358f32b74d12fb50c05a25covener */
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes ap_update_mtime(vlistr, vlistr->finfo.mtime);
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes r->vlist_validator = ap_make_etag(vlistr, 0);
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes /* ap_set_etag will later take r->vlist_validator into account
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes * when creating the etag header
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes}
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes
b08925593f214f621161742925dcf074a8047e0acovener/*****************************************************************
b08925593f214f621161742925dcf074a8047e0acovener *
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * Parsing (lists of) media types and their parameters, as seen in
707f6d077f73cc948deead8df5b40ea42c1eaa78covener * HTTPD header lines and elsewhere.
707f6d077f73cc948deead8df5b40ea42c1eaa78covener */
9ad7b260be233be7d7b5576979825cac72e15498rederpj
707f6d077f73cc948deead8df5b40ea42c1eaa78covener/*
707f6d077f73cc948deead8df5b40ea42c1eaa78covener * parse quality value. atof(3) is not well-usable here, because it
9ad7b260be233be7d7b5576979825cac72e15498rederpj * depends on the locale (argh).
707f6d077f73cc948deead8df5b40ea42c1eaa78covener *
707f6d077f73cc948deead8df5b40ea42c1eaa78covener * However, RFC 2616 states:
707f6d077f73cc948deead8df5b40ea42c1eaa78covener * 3.9 Quality Values
707f6d077f73cc948deead8df5b40ea42c1eaa78covener *
9ad7b260be233be7d7b5576979825cac72e15498rederpj * [...] HTTP/1.1 applications MUST NOT generate more than three digits
707f6d077f73cc948deead8df5b40ea42c1eaa78covener * after the decimal point. User configuration of these values SHOULD also
707f6d077f73cc948deead8df5b40ea42c1eaa78covener * be limited in this fashion.
707f6d077f73cc948deead8df5b40ea42c1eaa78covener *
707f6d077f73cc948deead8df5b40ea42c1eaa78covener * qvalue = ( "0" [ "." 0*3DIGIT ] )
707f6d077f73cc948deead8df5b40ea42c1eaa78covener * | ( "1" [ "." 0*3("0") ] )
707f6d077f73cc948deead8df5b40ea42c1eaa78covener *
707f6d077f73cc948deead8df5b40ea42c1eaa78covener * This is quite easy. If the supplied string doesn't match the above
707f6d077f73cc948deead8df5b40ea42c1eaa78covener * definition (loosely), we simply return 1 (same as if there's no qvalue)
707f6d077f73cc948deead8df5b40ea42c1eaa78covener */
707f6d077f73cc948deead8df5b40ea42c1eaa78covener
707f6d077f73cc948deead8df5b40ea42c1eaa78covenerstatic float atoq(const char *string)
707f6d077f73cc948deead8df5b40ea42c1eaa78covener{
707f6d077f73cc948deead8df5b40ea42c1eaa78covener if (!string || !*string) {
707f6d077f73cc948deead8df5b40ea42c1eaa78covener return 1.0f;
707f6d077f73cc948deead8df5b40ea42c1eaa78covener }
707f6d077f73cc948deead8df5b40ea42c1eaa78covener
707f6d077f73cc948deead8df5b40ea42c1eaa78covener while (*string && apr_isspace(*string)) {
707f6d077f73cc948deead8df5b40ea42c1eaa78covener ++string;
707f6d077f73cc948deead8df5b40ea42c1eaa78covener }
707f6d077f73cc948deead8df5b40ea42c1eaa78covener
707f6d077f73cc948deead8df5b40ea42c1eaa78covener /* be tolerant and accept qvalues without leading zero
707f6d077f73cc948deead8df5b40ea42c1eaa78covener * (also for backwards compat, where atof() was in use)
707f6d077f73cc948deead8df5b40ea42c1eaa78covener */
707f6d077f73cc948deead8df5b40ea42c1eaa78covener if (*string != '.' && *string++ != '0') {
707f6d077f73cc948deead8df5b40ea42c1eaa78covener return 1.0f;
707f6d077f73cc948deead8df5b40ea42c1eaa78covener }
707f6d077f73cc948deead8df5b40ea42c1eaa78covener
707f6d077f73cc948deead8df5b40ea42c1eaa78covener if (*string == '.') {
707f6d077f73cc948deead8df5b40ea42c1eaa78covener /* better only one division later, than dealing with fscking
9ad7b260be233be7d7b5576979825cac72e15498rederpj * IEEE format 0.1 factors ...
9ad7b260be233be7d7b5576979825cac72e15498rederpj */
9ad7b260be233be7d7b5576979825cac72e15498rederpj int i = 0;
7add8f7fb048534390571801b7794f71cd9e127abnicholes
7add8f7fb048534390571801b7794f71cd9e127abnicholes if (*++string >= '0' && *string <= '9') {
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem i += (*string - '0') * 100;
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem
7add8f7fb048534390571801b7794f71cd9e127abnicholes if (*++string >= '0' && *string <= '9') {
7add8f7fb048534390571801b7794f71cd9e127abnicholes i += (*string - '0') * 10;
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes if (*++string > '0' && *string <= '9') {
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes i += (*string - '0');
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes }
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes }
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes }
7add8f7fb048534390571801b7794f71cd9e127abnicholes
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes return (float)i / 1000.0f;
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes }
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes return 0.0f;
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes}
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes/*
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes * Get a single mime type entry --- one media type and parameters;
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes * enter the values we recognize into the argument accept_rec
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes */
7add8f7fb048534390571801b7794f71cd9e127abnicholes
7add8f7fb048534390571801b7794f71cd9e127abnicholesstatic const char *get_entry(apr_pool_t *p, accept_rec *result,
141e1368614dc7564e1627671361b01b4869b491bnicholes const char *accept_line)
3dfeb02cfb853d8717ca0cc259b59fea610173f5bnicholes{
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes result->quality = 1.0f;
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf result->level = 0.0f;
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes result->charset = "";
e8f95a682820a599fe41b22977010636be5c2717jim
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes /*
ebe5305f8b22507374358f32b74d12fb50c05a25covener * Note that this handles what I gather is the "old format",
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf *
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes * Accept: text/html text/plain moo/zot
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes *
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes * without any compatibility kludges --- if the token after the
3dfeb02cfb853d8717ca0cc259b59fea610173f5bnicholes * MIME type begins with a semicolon, we know we're looking at parms,
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes * otherwise, we know we aren't. (So why all the pissing and moaning
3dfeb02cfb853d8717ca0cc259b59fea610173f5bnicholes * in the CERN server code? I must be missing something).
3dfeb02cfb853d8717ca0cc259b59fea610173f5bnicholes */
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf result->name = ap_get_token(p, &accept_line, 0);
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf ap_str_tolower(result->name); /* You want case insensitive,
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf * you'll *get* case insensitive.
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf */
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf /* KLUDGE!!! Default HTML to level 2.0 unless the browser
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf * *explicitly* says something else.
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf */
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf if (!strcmp(result->name, "text/html") && (result->level == 0.0)) {
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf result->level = 2.0f;
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf }
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf else if (!strcmp(result->name, INCLUDES_MAGIC_TYPE)) {
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf result->level = 2.0f;
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf }
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf else if (!strcmp(result->name, INCLUDES_MAGIC_TYPE3)) {
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf result->level = 3.0f;
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf }
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes while (*accept_line == ';') {
e8f95a682820a599fe41b22977010636be5c2717jim /* Parameters ... */
7dbf29be626018bc389ef94c1846aeac4b72633bsf
7dbf29be626018bc389ef94c1846aeac4b72633bsf char *parm;
7dbf29be626018bc389ef94c1846aeac4b72633bsf char *cp;
7dbf29be626018bc389ef94c1846aeac4b72633bsf char *end;
7dbf29be626018bc389ef94c1846aeac4b72633bsf
7dbf29be626018bc389ef94c1846aeac4b72633bsf ++accept_line;
7dbf29be626018bc389ef94c1846aeac4b72633bsf parm = ap_get_token(p, &accept_line, 1);
7dbf29be626018bc389ef94c1846aeac4b72633bsf
7dbf29be626018bc389ef94c1846aeac4b72633bsf /* Look for 'var = value' --- and make sure the var is in lcase. */
7dbf29be626018bc389ef94c1846aeac4b72633bsf
7dbf29be626018bc389ef94c1846aeac4b72633bsf for (cp = parm; (*cp && !apr_isspace(*cp) && *cp != '='); ++cp) {
7dbf29be626018bc389ef94c1846aeac4b72633bsf *cp = apr_tolower(*cp);
7dbf29be626018bc389ef94c1846aeac4b72633bsf }
7dbf29be626018bc389ef94c1846aeac4b72633bsf
7dbf29be626018bc389ef94c1846aeac4b72633bsf if (!*cp) {
7dbf29be626018bc389ef94c1846aeac4b72633bsf continue; /* No '='; just ignore it. */
7dbf29be626018bc389ef94c1846aeac4b72633bsf }
7dbf29be626018bc389ef94c1846aeac4b72633bsf
7dbf29be626018bc389ef94c1846aeac4b72633bsf *cp++ = '\0'; /* Delimit var */
7dbf29be626018bc389ef94c1846aeac4b72633bsf while (*cp && (apr_isspace(*cp) || *cp == '=')) {
7dbf29be626018bc389ef94c1846aeac4b72633bsf ++cp;
7dbf29be626018bc389ef94c1846aeac4b72633bsf }
7dbf29be626018bc389ef94c1846aeac4b72633bsf
7dbf29be626018bc389ef94c1846aeac4b72633bsf if (*cp == '"') {
7dbf29be626018bc389ef94c1846aeac4b72633bsf ++cp;
7dbf29be626018bc389ef94c1846aeac4b72633bsf for (end = cp;
7dbf29be626018bc389ef94c1846aeac4b72633bsf (*end && *end != '\n' && *end != '\r' && *end != '\"');
7dbf29be626018bc389ef94c1846aeac4b72633bsf end++);
7dbf29be626018bc389ef94c1846aeac4b72633bsf }
7dbf29be626018bc389ef94c1846aeac4b72633bsf else {
7dbf29be626018bc389ef94c1846aeac4b72633bsf for (end = cp; (*end && !apr_isspace(*end)); end++);
7dbf29be626018bc389ef94c1846aeac4b72633bsf }
7dbf29be626018bc389ef94c1846aeac4b72633bsf if (*end) {
7dbf29be626018bc389ef94c1846aeac4b72633bsf *end = '\0'; /* strip ending quote or return */
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes }
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes ap_str_tolower(cp);
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes if (parm[0] == 'q'
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes && (parm[1] == '\0' || (parm[1] == 's' && parm[2] == '\0'))) {
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes result->quality = atoq(cp);
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes }
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes else if (parm[0] == 'l' && !strcmp(&parm[1], "evel")) {
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes result->level = (float)atoi(cp);
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes }
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes else if (!strcmp(parm, "charset")) {
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf result->charset = cp;
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf }
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes }
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes if (*accept_line == ',') {
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes ++accept_line;
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes }
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes return accept_line;
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes}
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes/*****************************************************************
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes *
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes * Dealing with header lines ...
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes *
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes * Accept, Accept-Charset, Accept-Language and Accept-Encoding
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes * are handled by do_header_line() - they all have the same
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes * basic structure of a list of items of the format
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes * name; q=N; charset=TEXT
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes *
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf * where charset is only valid in Accept.
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes */
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholesstatic apr_array_header_t *do_header_line(apr_pool_t *p,
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes const char *accept_line)
d330a801b1e5d63a4b8b4fd431542ad0903fd71bbnicholes{
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes apr_array_header_t *accept_recs;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (!accept_line) {
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf return NULL;
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf }
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes accept_recs = apr_array_make(p, 40, sizeof(accept_rec));
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf while (*accept_line) {
7dbf29be626018bc389ef94c1846aeac4b72633bsf accept_rec *new = (accept_rec *) apr_array_push(accept_recs);
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf accept_line = get_entry(p, new, accept_line);
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
e8f95a682820a599fe41b22977010636be5c2717jim return accept_recs;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes}
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes/* Given the text of the Content-Languages: line from the var map file,
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf * return an array containing the languages of this variant
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
7dbf29be626018bc389ef94c1846aeac4b72633bsfstatic apr_array_header_t *do_languages_line(apr_pool_t *p,
7dbf29be626018bc389ef94c1846aeac4b72633bsf const char **lang_line)
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf{
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf apr_array_header_t *lang_recs = apr_array_make(p, 2, sizeof(char *));
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf
9c63a05713cb83a44a1590b4af33edeebf39f118sf if (!lang_line) {
9c63a05713cb83a44a1590b4af33edeebf39f118sf return lang_recs;
9c63a05713cb83a44a1590b4af33edeebf39f118sf }
9c63a05713cb83a44a1590b4af33edeebf39f118sf
9c63a05713cb83a44a1590b4af33edeebf39f118sf while (**lang_line) {
9c63a05713cb83a44a1590b4af33edeebf39f118sf char **new = (char **) apr_array_push(lang_recs);
9c63a05713cb83a44a1590b4af33edeebf39f118sf *new = ap_get_token(p, lang_line, 0);
9c63a05713cb83a44a1590b4af33edeebf39f118sf ap_str_tolower(*new);
9c63a05713cb83a44a1590b4af33edeebf39f118sf if (**lang_line == ',' || **lang_line == ';') {
9c63a05713cb83a44a1590b4af33edeebf39f118sf ++(*lang_line);
9c63a05713cb83a44a1590b4af33edeebf39f118sf }
9c63a05713cb83a44a1590b4af33edeebf39f118sf }
9c63a05713cb83a44a1590b4af33edeebf39f118sf
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf return lang_recs;
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf}
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf/*****************************************************************
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf *
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf * Handling header lines from clients...
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholesstatic negotiation_state *parse_accept_headers(request_rec *r)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes{
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes negotiation_state *new =
54d22ed1c429b903b029bbd62621f11a9e286137minfrin (negotiation_state *) apr_pcalloc(r->pool, sizeof(negotiation_state));
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes accept_rec *elts;
9c63a05713cb83a44a1590b4af33edeebf39f118sf apr_table_t *hdrs = r->headers_in;
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf int i;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes new->pool = r->pool;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes new->r = r;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes new->conf = (neg_dir_config *)ap_get_module_config(r->per_dir_config,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes &negotiation_module);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
54d22ed1c429b903b029bbd62621f11a9e286137minfrin new->dir_name = ap_make_dirstr_parent(r->pool, r->filename);
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin new->accepts = do_header_line(r->pool, apr_table_get(hdrs, "Accept"));
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* calculate new->accept_q value */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if (new->accepts) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin elts = (accept_rec *) new->accepts->elts;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin for (i = 0; i < new->accepts->nelts; ++i) {
e8f95a682820a599fe41b22977010636be5c2717jim if (elts[i].quality < 1.0) {
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe new->accept_q = 1;
8a03cd420b800a2428f49f4617293de9b2387b20jorton }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin new->accept_encodings =
54d22ed1c429b903b029bbd62621f11a9e286137minfrin do_header_line(r->pool, apr_table_get(hdrs, "Accept-Encoding"));
54d22ed1c429b903b029bbd62621f11a9e286137minfrin new->accept_langs =
54d22ed1c429b903b029bbd62621f11a9e286137minfrin do_header_line(r->pool, apr_table_get(hdrs, "Accept-Language"));
54d22ed1c429b903b029bbd62621f11a9e286137minfrin new->accept_charsets =
54d22ed1c429b903b029bbd62621f11a9e286137minfrin do_header_line(r->pool, apr_table_get(hdrs, "Accept-Charset"));
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* This is possibly overkill for some servers, heck, we have
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * only 33 index.html variants in docs/docroot (today).
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * Make this configurable?
54d22ed1c429b903b029bbd62621f11a9e286137minfrin */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin new->avail_vars = apr_array_make(r->pool, 40, sizeof(var_rec));
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin return new;
6999a76d8eb5ef6b4b295e51df0b2fb6064bd373covener}
6999a76d8eb5ef6b4b295e51df0b2fb6064bd373covener
6999a76d8eb5ef6b4b295e51df0b2fb6064bd373covener
6999a76d8eb5ef6b4b295e51df0b2fb6064bd373covenerstatic void parse_negotiate_header(request_rec *r, negotiation_state *neg)
6999a76d8eb5ef6b4b295e51df0b2fb6064bd373covener{
6999a76d8eb5ef6b4b295e51df0b2fb6064bd373covener const char *negotiate = apr_table_get(r->headers_in, "Negotiate");
54d22ed1c429b903b029bbd62621f11a9e286137minfrin char *tok;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* First, default to no TCN, no Alternates, and the original Apache
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * negotiation algorithm with fiddles for broken browser configs.
54d22ed1c429b903b029bbd62621f11a9e286137minfrin *
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * To save network bandwidth, we do not configure to send an
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * Alternates header to the user agent by default. User
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * agents that want an Alternates header for agent-driven
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * negotiation will have to request it by sending an
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * appropriate Negotiate header.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes neg->ua_supports_trans = 0;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes neg->send_alternates = 0;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes neg->may_choose = 1;
e8f95a682820a599fe41b22977010636be5c2717jim neg->use_rvsa = 0;
e8f95a682820a599fe41b22977010636be5c2717jim neg->dont_fiddle_headers = 0;
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (!negotiate)
e8f95a682820a599fe41b22977010636be5c2717jim return;
8113dac419143273351446c3ad653f3fe5ba5cfdwrowe
8113dac419143273351446c3ad653f3fe5ba5cfdwrowe if (strcmp(negotiate, "trans") == 0) {
8113dac419143273351446c3ad653f3fe5ba5cfdwrowe /* Lynx 2.7 and 2.8 send 'negotiate: trans' even though they
f0f6f1b90ab582896f8a7d56d85bd62a55e57d90covener * do not support transparent content negotiation, so for Lynx we
8113dac419143273351446c3ad653f3fe5ba5cfdwrowe * ignore the negotiate header when its contents are exactly "trans".
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * If future versions of Lynx ever need to say 'negotiate: trans',
560fd0658902ab57754616c172d8953e69fc4722bnicholes * they can send the equivalent 'negotiate: trans, trans' instead
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener * to avoid triggering the workaround below.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
e8f95a682820a599fe41b22977010636be5c2717jim const char *ua = apr_table_get(r->headers_in, "User-Agent");
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (ua && (strncmp(ua, "Lynx", 4) == 0))
9ad7b260be233be7d7b5576979825cac72e15498rederpj return;
9ad7b260be233be7d7b5576979825cac72e15498rederpj }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes neg->may_choose = 0; /* An empty Negotiate would require 300 response */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes while ((tok = ap_get_list_item(neg->pool, &negotiate)) != NULL) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (strcmp(tok, "trans") == 0 ||
560fd0658902ab57754616c172d8953e69fc4722bnicholes strcmp(tok, "vlist") == 0 ||
560fd0658902ab57754616c172d8953e69fc4722bnicholes strcmp(tok, "guess-small") == 0 ||
560fd0658902ab57754616c172d8953e69fc4722bnicholes apr_isdigit(tok[0]) ||
560fd0658902ab57754616c172d8953e69fc4722bnicholes strcmp(tok, "*") == 0) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* The user agent supports transparent negotiation */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes neg->ua_supports_trans = 1;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* Send-alternates could be configurable, but note
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * that it must be 1 if we have 'vlist' in the
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * negotiate header.
e8f95a682820a599fe41b22977010636be5c2717jim */
e8f95a682820a599fe41b22977010636be5c2717jim neg->send_alternates = 1;
e8f95a682820a599fe41b22977010636be5c2717jim
e8f95a682820a599fe41b22977010636be5c2717jim if (strcmp(tok, "1.0") == 0) {
e8f95a682820a599fe41b22977010636be5c2717jim /* we may use the RVSA/1.0 algorithm, configure for it */
e8f95a682820a599fe41b22977010636be5c2717jim neg->may_choose = 1;
ae1981fc94adf2b231e2d0e15d2f895b2138c969covener neg->use_rvsa = 1;
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe neg->dont_fiddle_headers = 1;
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener }
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener else if (tok[0] == '*') {
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener /* we may use any variant selection algorithm, configure
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener * to use the Apache algorithm
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener */
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener neg->may_choose = 1;
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener /* We disable header fiddles on the assumption that a
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener * client sending Negotiate knows how to send correct
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener * headers which don't need fiddling.
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener */
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener neg->dont_fiddle_headers = 1;
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes#ifdef NEG_DEBUG
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes "dont_fiddle_headers=%d use_rvsa=%d ua_supports_trans=%d "
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes "send_alternates=%d, may_choose=%d",
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes neg->dont_fiddle_headers, neg->use_rvsa,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes neg->ua_supports_trans, neg->send_alternates, neg->may_choose);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes#endif
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes}
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes/* Sometimes clients will give us no Accept info at all; this routine sets
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * up the standard default for that case, and also arranges for us to be
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * willing to run a CGI script if we find one. (In fact, we set up to
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * dramatically prefer CGI scripts in cases where that's appropriate,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * e.g., POST or when URI includes query args or extra path info).
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholesstatic void maybe_add_default_accepts(negotiation_state *neg,
e8f95a682820a599fe41b22977010636be5c2717jim int prefer_scripts)
560fd0658902ab57754616c172d8953e69fc4722bnicholes{
ae1981fc94adf2b231e2d0e15d2f895b2138c969covener accept_rec *new_accept;
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (!neg->accepts) {
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener neg->accepts = apr_array_make(neg->pool, 4, sizeof(accept_rec));
fa123db15501821e36e513afa78e839775ad2800covener
fa123db15501821e36e513afa78e839775ad2800covener new_accept = (accept_rec *) apr_array_push(neg->accepts);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes new_accept->name = "*/*";
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes new_accept->quality = 1.0f;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes new_accept->level = 0.0f;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes new_accept = (accept_rec *) apr_array_push(neg->accepts);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes new_accept->name = CGI_MAGIC_TYPE;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (neg->use_rvsa) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes new_accept->quality = 0;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes else {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes new_accept->quality = prefer_scripts ? 2.0f : 0.001f;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes new_accept->level = 0.0f;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes}
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
0e05808dc59a321566303084c84b9826a4353cefrederpj/*****************************************************************
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes *
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * Parsing type-map files, in Roy's meta/http format augmented with
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * #-comments.
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener */
0e05808dc59a321566303084c84b9826a4353cefrederpj
ebe5305f8b22507374358f32b74d12fb50c05a25covener/* Reading RFC822-style header lines, ignoring #-comments and
ebe5305f8b22507374358f32b74d12fb50c05a25covener * handling continuations.
ebe5305f8b22507374358f32b74d12fb50c05a25covener */
ebe5305f8b22507374358f32b74d12fb50c05a25covener
ebe5305f8b22507374358f32b74d12fb50c05a25covenerenum header_state {
ebe5305f8b22507374358f32b74d12fb50c05a25covener header_eof, header_seen, header_sep
ebe5305f8b22507374358f32b74d12fb50c05a25covener};
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovenerstatic enum header_state get_header_line(char *buffer, int len, apr_file_t *map)
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener{
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener char *buf_end = buffer + len;
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener char *cp;
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener char c;
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener /* Get a noncommented line */
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener do {
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener if (apr_file_gets(buffer, MAX_STRING_LEN, map) != APR_SUCCESS) {
0e05808dc59a321566303084c84b9826a4353cefrederpj return header_eof;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener } while (buffer[0] == '#');
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* If blank, just return it --- this ends information on this variant */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener for (cp = buffer; (*cp && apr_isspace(*cp)); ++cp) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes continue;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (*cp == '\0') {
9ad7b260be233be7d7b5576979825cac72e15498rederpj return header_sep;
9ad7b260be233be7d7b5576979825cac72e15498rederpj }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* If non-blank, go looking for header lines, but note that we still
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * have to treat comments specially...
54d22ed1c429b903b029bbd62621f11a9e286137minfrin */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin cp += strlen(cp);
560fd0658902ab57754616c172d8953e69fc4722bnicholes
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* We need to shortcut the rest of this block following the Body:
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * tag - we will not look for continutation after this line.
ae1981fc94adf2b231e2d0e15d2f895b2138c969covener */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (!strncasecmp(buffer, "Body:", 5))
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener return header_seen;
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener
4e9c24785b525d2956e6e381015c0f2bd0a72f4bcovener while (apr_file_getc(&c, map) != APR_EOF) {
fa123db15501821e36e513afa78e839775ad2800covener if (c == '#') {
fa123db15501821e36e513afa78e839775ad2800covener /* Comment line */
fa123db15501821e36e513afa78e839775ad2800covener while (apr_file_getc(&c, map) != APR_EOF && c != '\n') {
fa123db15501821e36e513afa78e839775ad2800covener continue;
fa123db15501821e36e513afa78e839775ad2800covener }
fa123db15501821e36e513afa78e839775ad2800covener }
fa123db15501821e36e513afa78e839775ad2800covener else if (apr_isspace(c)) {
fa123db15501821e36e513afa78e839775ad2800covener /* Leading whitespace. POSSIBLE continuation line
fa123db15501821e36e513afa78e839775ad2800covener * Also, possibly blank --- if so, we ungetc() the final newline
fa123db15501821e36e513afa78e839775ad2800covener * so that we will pick up the blank line the next time 'round.
fa123db15501821e36e513afa78e839775ad2800covener */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes while (c != '\n' && apr_isspace(c)) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if(apr_file_getc(&c, map) != APR_SUCCESS)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes break;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes apr_file_ungetc(c, map);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (c == '\n') {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return header_seen; /* Blank line */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* Continuation */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes while ( cp < buf_end - 2
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes && (apr_file_getc(&c, map)) != APR_EOF
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes && c != '\n') {
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe *cp++ = c;
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe }
e8f95a682820a599fe41b22977010636be5c2717jim
e8f95a682820a599fe41b22977010636be5c2717jim *cp++ = '\n';
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe *cp = '\0';
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes else {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
e8f95a682820a599fe41b22977010636be5c2717jim /* Line beginning with something other than whitespace */
e8f95a682820a599fe41b22977010636be5c2717jim
8113dac419143273351446c3ad653f3fe5ba5cfdwrowe apr_file_ungetc(c, map);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return header_seen;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
e8f95a682820a599fe41b22977010636be5c2717jim }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return header_seen;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes}
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholesstatic apr_off_t get_body(char *buffer, apr_size_t *len, const char *tag,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes apr_file_t *map)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes{
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe char *endbody;
e8f95a682820a599fe41b22977010636be5c2717jim int bodylen;
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe int taglen;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes apr_off_t pos;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes taglen = strlen(tag);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes *len -= taglen;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* We are at the first character following a body:tag\n entry
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * Suck in the body, then backspace to the first char after the
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * closing tag entry. If we fail to read, find the tag or back
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * up then we have a hosed file, so give up already
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (apr_file_read(map, buffer, len) != APR_SUCCESS) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return -1;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* put a copy of the tag *after* the data read from the file
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * so that strstr() will find something with no reliance on
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * terminating '\0'
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes memcpy(buffer + *len, tag, taglen);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes endbody = strstr(buffer, tag);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (endbody == buffer + *len) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return -1;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes bodylen = endbody - buffer;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes endbody += taglen;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* Skip all the trailing cruft after the end tag to the next line */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes while (*endbody) {
e8f95a682820a599fe41b22977010636be5c2717jim if (*endbody == '\n') {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes ++endbody;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes break;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes ++endbody;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes pos = -(apr_off_t)(*len - (endbody - buffer));
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (apr_file_seek(map, APR_CUR, &pos) != APR_SUCCESS) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return -1;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
e8f95a682820a599fe41b22977010636be5c2717jim
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* Give the caller back the actual body's file offset and length */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes *len = bodylen;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return pos - (endbody - buffer);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes}
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
43c3e6a4b559b76b750c245ee95e2782c15b4296jim/* Stripping out RFC822 comments */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholesstatic void strip_paren_comments(char *hdr)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes{
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* Hmmm... is this correct? In Roy's latest draft, (comments) can nest! */
f43b67c5a9d29b572eac916f8335cedc80c908bebnicholes /* Nope, it isn't correct. Fails to handle backslash escape as well. */
43c3e6a4b559b76b750c245ee95e2782c15b4296jim
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes while (*hdr) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (*hdr == '"') {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes hdr = strchr(hdr, '"');
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (hdr == NULL) {
37af4b0cf648275b68ff41c866c665b4ccf4667dcovener return;
37af4b0cf648275b68ff41c866c665b4ccf4667dcovener }
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf ++hdr;
37af4b0cf648275b68ff41c866c665b4ccf4667dcovener }
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe else if (*hdr == '(') {
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe while (*hdr && *hdr != ')') {
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe *hdr++ = ' ';
f43b67c5a9d29b572eac916f8335cedc80c908bebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (*hdr) {
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf *hdr++ = ' ';
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf }
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf }
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf else {
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf ++hdr;
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf }
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf }
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf}
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf/* Getting to a header body from the header */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholesstatic char *lcase_header_name_return_body(char *header, request_rec *r)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes{
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes char *cp = header;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes for ( ; *cp && *cp != ':' ; ++cp) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes *cp = apr_tolower(*cp);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (!*cp) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes "Syntax error in type map, no ':' in %s for header %s",
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes r->filename, header);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return NULL;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes do {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes ++cp;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes } while (*cp && apr_isspace(*cp));
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (!*cp) {
e8f95a682820a599fe41b22977010636be5c2717jim ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes "Syntax error in type map --- no header body: %s for %s",
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe r->filename, header);
e8f95a682820a599fe41b22977010636be5c2717jim return NULL;
e8f95a682820a599fe41b22977010636be5c2717jim }
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return cp;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes}
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholesstatic int read_type_map(apr_file_t **map, negotiation_state *neg,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes request_rec *rr)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes{
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes request_rec *r = neg->r;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes apr_file_t *map_ = NULL;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes apr_status_t status;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes char buffer[MAX_STRING_LEN];
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes enum header_state hstate;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes struct var_rec mime_info;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes int has_content;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
e8f95a682820a599fe41b22977010636be5c2717jim if (!map)
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem map = &map_;
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem /* We are not using multiviews */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes neg->count_multiviews_variants = 0;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
8113dac419143273351446c3ad653f3fe5ba5cfdwrowe if ((status = apr_file_open(map, rr->filename, APR_READ | APR_BUFFERED,
8113dac419143273351446c3ad653f3fe5ba5cfdwrowe APR_OS_DEFAULT, neg->pool)) != APR_SUCCESS) {
8113dac419143273351446c3ad653f3fe5ba5cfdwrowe ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes "cannot access type map file: %s", rr->filename);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (APR_STATUS_IS_ENOTDIR(status) || APR_STATUS_IS_ENOENT(status)) {
e8f95a682820a599fe41b22977010636be5c2717jim return HTTP_NOT_FOUND;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes else {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return HTTP_FORBIDDEN;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe clean_var_rec(&mime_info);
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe has_content = 0;
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes do {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes hstate = get_header_line(buffer, MAX_STRING_LEN, *map);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (hstate == header_seen) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes char *body1 = lcase_header_name_return_body(buffer, neg->r);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes const char *body;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (body1 == NULL) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return HTTP_INTERNAL_SERVER_ERROR;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes strip_paren_comments(body1);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes body = body1;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
e8f95a682820a599fe41b22977010636be5c2717jim if (!strncmp(buffer, "uri:", 4)) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes mime_info.file_name = ap_get_token(neg->pool, &body, 0);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes else if (!strncmp(buffer, "content-type:", 13)) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes struct accept_rec accept_info;
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj get_entry(neg->pool, &accept_info, body);
e8f95a682820a599fe41b22977010636be5c2717jim set_mime_fields(&mime_info, &accept_info);
e8f95a682820a599fe41b22977010636be5c2717jim has_content = 1;
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe }
e8f95a682820a599fe41b22977010636be5c2717jim else if (!strncmp(buffer, "content-length:", 15)) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes char *errp;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes apr_off_t number;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (apr_strtoff(&number, body, &errp, 10)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes || *errp || number < 0) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes "Parse error in type map, Content-Length: "
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes "'%s' in %s is invalid.",
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes body, r->filename);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes break;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes mime_info.bytes = number;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes has_content = 1;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes else if (!strncmp(buffer, "content-language:", 17)) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes mime_info.content_languages = do_languages_line(neg->pool,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes &body);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes has_content = 1;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes else if (!strncmp(buffer, "content-encoding:", 17)) {
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj mime_info.content_encoding = ap_get_token(neg->pool, &body, 0);
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj has_content = 1;
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj }
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj else if (!strncmp(buffer, "description:", 12)) {
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj char *desc = apr_pstrdup(neg->pool, body);
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj char *cp;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes for (cp = desc; *cp; ++cp) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (*cp=='\n') *cp=' ';
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (cp>desc) *(cp-1)=0;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes mime_info.description = desc;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes else if (!strncmp(buffer, "body:", 5)) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes char *tag = apr_pstrdup(neg->pool, body);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes char *eol = strchr(tag, '\0');
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes apr_size_t len = MAX_STRING_LEN;
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj while (--eol >= tag && apr_isspace(*eol))
f43b67c5a9d29b572eac916f8335cedc80c908bebnicholes *eol = '\0';
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if ((mime_info.body = get_body(buffer, &len, tag, *map)) < 0) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes "Syntax error in type map, no end tag '%s'"
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes "found in %s for Body: content.",
37af4b0cf648275b68ff41c866c665b4ccf4667dcovener tag, r->filename);
37af4b0cf648275b68ff41c866c665b4ccf4667dcovener break;
37af4b0cf648275b68ff41c866c665b4ccf4667dcovener }
37af4b0cf648275b68ff41c866c665b4ccf4667dcovener mime_info.bytes = len;
37af4b0cf648275b68ff41c866c665b4ccf4667dcovener mime_info.file_name = apr_filepath_name_get(rr->filename);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
f43b67c5a9d29b572eac916f8335cedc80c908bebnicholes else {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (*mime_info.file_name && has_content) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes void *new_var = apr_array_push(neg->avail_vars);
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf memcpy(new_var, (void *) &mime_info, sizeof(var_rec));
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf }
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf clean_var_rec(&mime_info);
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf has_content = 0;
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf }
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf } while (hstate != header_eof);
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (map_)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes apr_file_close(map_);
e8f95a682820a599fe41b22977010636be5c2717jim
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes set_vlist_validator(r, rr);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return OK;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes}
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes/* Sort function used by read_types_multi. */
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpjstatic int variantsortf(var_rec *a, var_rec *b) {
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* First key is the source quality, sort in descending order. */
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe
e8f95a682820a599fe41b22977010636be5c2717jim /* XXX: note that we currently implement no method of setting the
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * source quality for multiviews variants, so we are always comparing
e8f95a682820a599fe41b22977010636be5c2717jim * 1.0 to 1.0 for now
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe */
e8f95a682820a599fe41b22977010636be5c2717jim if (a->source_quality < b->source_quality)
e8f95a682820a599fe41b22977010636be5c2717jim return 1;
e8f95a682820a599fe41b22977010636be5c2717jim if (a->source_quality > b->source_quality)
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe return -1;
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj /* Second key is the variant name */
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj return strcmp(a->file_name, b->file_name);
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem}
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem
9c63a05713cb83a44a1590b4af33edeebf39f118sf/*****************************************************************
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem *
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf * Same as read_type_map, except we use a filtered directory listing
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj * as the map...
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholesstatic int read_types_multi(negotiation_state *neg)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes{
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes request_rec *r = neg->r;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes char *filp;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes int prefix_len;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes apr_dir_t *dirp;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes apr_finfo_t dirent;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes apr_status_t status;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes struct var_rec mime_info;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes struct accept_rec accept_info;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes void *new_var;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes int anymatch = 0;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes clean_var_rec(&mime_info);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (r->proxyreq || !r->filename
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes || !ap_os_is_path_absolute(neg->pool, r->filename)) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return DECLINED;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
8869662bb1a4078297020e94ae5e928626d877c6rederpj /* Only absolute paths here */
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem if (!(filp = strrchr(r->filename, '/'))) {
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem return DECLINED;
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem }
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem ++filp;
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem prefix_len = strlen(filp);
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem if ((status = apr_dir_open(&dirp, neg->dir_name,
8869662bb1a4078297020e94ae5e928626d877c6rederpj neg->pool)) != APR_SUCCESS) {
8869662bb1a4078297020e94ae5e928626d877c6rederpj ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r,
8869662bb1a4078297020e94ae5e928626d877c6rederpj "cannot read directory for multi: %s", neg->dir_name);
8869662bb1a4078297020e94ae5e928626d877c6rederpj return HTTP_FORBIDDEN;
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem }
8869662bb1a4078297020e94ae5e928626d877c6rederpj
8869662bb1a4078297020e94ae5e928626d877c6rederpj while (apr_dir_read(&dirent, APR_FINFO_DIRENT, dirp) == APR_SUCCESS) {
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem apr_array_header_t *exception_list;
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem request_rec *sub_req;
8869662bb1a4078297020e94ae5e928626d877c6rederpj
8869662bb1a4078297020e94ae5e928626d877c6rederpj /* Do we have a match? */
8869662bb1a4078297020e94ae5e928626d877c6rederpj#ifdef CASE_BLIND_FILESYSTEM
8869662bb1a4078297020e94ae5e928626d877c6rederpj if (strncasecmp(dirent.name, filp, prefix_len)) {
8869662bb1a4078297020e94ae5e928626d877c6rederpj#else
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem if (strncmp(dirent.name, filp, prefix_len)) {
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem#endif
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem continue;
8869662bb1a4078297020e94ae5e928626d877c6rederpj }
8869662bb1a4078297020e94ae5e928626d877c6rederpj if (dirent.name[prefix_len] != '.') {
8869662bb1a4078297020e94ae5e928626d877c6rederpj continue;
8869662bb1a4078297020e94ae5e928626d877c6rederpj }
8869662bb1a4078297020e94ae5e928626d877c6rederpj
8869662bb1a4078297020e94ae5e928626d877c6rederpj /* Don't negotiate directories and other unusual files
8869662bb1a4078297020e94ae5e928626d877c6rederpj * Really shouldn't see anything but DIR/LNK/REG here,
8869662bb1a4078297020e94ae5e928626d877c6rederpj * and we aught to discover if the LNK was interesting.
8869662bb1a4078297020e94ae5e928626d877c6rederpj *
8869662bb1a4078297020e94ae5e928626d877c6rederpj * Of course, this only helps platforms that capture the
8869662bb1a4078297020e94ae5e928626d877c6rederpj * the filetype in apr_dir_read(), which most can once
8869662bb1a4078297020e94ae5e928626d877c6rederpj * they are optimized with some magic [it's known to the
8869662bb1a4078297020e94ae5e928626d877c6rederpj * dirent, not associated to the inode, on most FS's.]
8869662bb1a4078297020e94ae5e928626d877c6rederpj */
6733d943c9e8d0f27dd077a04037e8c49eb090ffcovener if ((dirent.valid & APR_FINFO_TYPE) && (dirent.filetype == APR_DIR))
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem continue;
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem
8869662bb1a4078297020e94ae5e928626d877c6rederpj /* Ok, something's here. Maybe nothing useful. Remember that
8869662bb1a4078297020e94ae5e928626d877c6rederpj * we tried, if we completely fail, so we can reject the request!
8869662bb1a4078297020e94ae5e928626d877c6rederpj */
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf anymatch = 1;
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf /* See if it's something which we have access to, and which
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf * has a known type and encoding (as opposed to something
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf * which we'll be slapping default_type on later).
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf */
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf sub_req = ap_sub_req_lookup_dirent(&dirent, r, AP_SUBREQ_MERGE_ARGS,
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf NULL);
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf
8869662bb1a4078297020e94ae5e928626d877c6rederpj /* Double check, we still don't multi-resolve non-ordinary files
8869662bb1a4078297020e94ae5e928626d877c6rederpj */
8869662bb1a4078297020e94ae5e928626d877c6rederpj if (sub_req->finfo.filetype != APR_REG)
8869662bb1a4078297020e94ae5e928626d877c6rederpj continue;
8869662bb1a4078297020e94ae5e928626d877c6rederpj
8869662bb1a4078297020e94ae5e928626d877c6rederpj /* If it has a handler, we'll pretend it's a CGI script,
8869662bb1a4078297020e94ae5e928626d877c6rederpj * since that's a good indication of the sort of thing it
8869662bb1a4078297020e94ae5e928626d877c6rederpj * might be doing.
8869662bb1a4078297020e94ae5e928626d877c6rederpj */
8869662bb1a4078297020e94ae5e928626d877c6rederpj if (sub_req->handler && !sub_req->content_type) {
8869662bb1a4078297020e94ae5e928626d877c6rederpj ap_set_content_type(sub_req, CGI_MAGIC_TYPE);
8869662bb1a4078297020e94ae5e928626d877c6rederpj }
8869662bb1a4078297020e94ae5e928626d877c6rederpj
8869662bb1a4078297020e94ae5e928626d877c6rederpj /*
8869662bb1a4078297020e94ae5e928626d877c6rederpj * mod_mime will _always_ provide us the base name in the
8869662bb1a4078297020e94ae5e928626d877c6rederpj * ap-mime-exception-list, if it processed anything. If
8869662bb1a4078297020e94ae5e928626d877c6rederpj * this list is empty, give up immediately, there was
8869662bb1a4078297020e94ae5e928626d877c6rederpj * nothing interesting. For example, looking at the files
8869662bb1a4078297020e94ae5e928626d877c6rederpj * readme.txt and readme.foo, we will throw away .foo if
8869662bb1a4078297020e94ae5e928626d877c6rederpj * it's an insignificant file (e.g. did not identify a
8869662bb1a4078297020e94ae5e928626d877c6rederpj * language, charset, encoding, content type or handler,)
8869662bb1a4078297020e94ae5e928626d877c6rederpj */
8869662bb1a4078297020e94ae5e928626d877c6rederpj exception_list =
8869662bb1a4078297020e94ae5e928626d877c6rederpj (apr_array_header_t *)apr_table_get(sub_req->notes,
8869662bb1a4078297020e94ae5e928626d877c6rederpj "ap-mime-exceptions-list");
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem if (!exception_list) {
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem ap_destroy_sub_req(sub_req);
8869662bb1a4078297020e94ae5e928626d877c6rederpj continue;
8869662bb1a4078297020e94ae5e928626d877c6rederpj }
8869662bb1a4078297020e94ae5e928626d877c6rederpj
8869662bb1a4078297020e94ae5e928626d877c6rederpj /* Each unregonized bit better match our base name, in sequence.
8869662bb1a4078297020e94ae5e928626d877c6rederpj * A test of index.html.foo will match index.foo or index.html.foo,
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * but it will never transpose the segments and allow index.foo.html
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * because that would introduce too much CPU consumption. Better that
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * we don't attempt a many-to-many match here.
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem */
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem {
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem int nexcept = exception_list->nelts;
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem char **cur_except = (char**)exception_list->elts;
8869662bb1a4078297020e94ae5e928626d877c6rederpj char *segstart = filp, *segend, saveend;
8869662bb1a4078297020e94ae5e928626d877c6rederpj
8869662bb1a4078297020e94ae5e928626d877c6rederpj while (*segstart && nexcept) {
8869662bb1a4078297020e94ae5e928626d877c6rederpj if (!(segend = strchr(segstart, '.')))
8869662bb1a4078297020e94ae5e928626d877c6rederpj segend = strchr(segstart, '\0');
8869662bb1a4078297020e94ae5e928626d877c6rederpj saveend = *segend;
8869662bb1a4078297020e94ae5e928626d877c6rederpj *segend = '\0';
8869662bb1a4078297020e94ae5e928626d877c6rederpj
8869662bb1a4078297020e94ae5e928626d877c6rederpj#ifdef CASE_BLIND_FILESYSTEM
8869662bb1a4078297020e94ae5e928626d877c6rederpj if (strcasecmp(segstart, *cur_except) == 0) {
8869662bb1a4078297020e94ae5e928626d877c6rederpj#else
8869662bb1a4078297020e94ae5e928626d877c6rederpj if (strcmp(segstart, *cur_except) == 0) {
8869662bb1a4078297020e94ae5e928626d877c6rederpj#endif
8869662bb1a4078297020e94ae5e928626d877c6rederpj --nexcept;
8869662bb1a4078297020e94ae5e928626d877c6rederpj ++cur_except;
8869662bb1a4078297020e94ae5e928626d877c6rederpj }
8869662bb1a4078297020e94ae5e928626d877c6rederpj
8869662bb1a4078297020e94ae5e928626d877c6rederpj if (!saveend)
8869662bb1a4078297020e94ae5e928626d877c6rederpj break;
8869662bb1a4078297020e94ae5e928626d877c6rederpj
8869662bb1a4078297020e94ae5e928626d877c6rederpj *segend = saveend;
8869662bb1a4078297020e94ae5e928626d877c6rederpj segstart = segend + 1;
8869662bb1a4078297020e94ae5e928626d877c6rederpj }
8869662bb1a4078297020e94ae5e928626d877c6rederpj
8869662bb1a4078297020e94ae5e928626d877c6rederpj if (nexcept) {
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem /* Something you don't know is, something you don't know...
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem */
8869662bb1a4078297020e94ae5e928626d877c6rederpj ap_destroy_sub_req(sub_req);
8869662bb1a4078297020e94ae5e928626d877c6rederpj continue;
8869662bb1a4078297020e94ae5e928626d877c6rederpj }
8869662bb1a4078297020e94ae5e928626d877c6rederpj }
8869662bb1a4078297020e94ae5e928626d877c6rederpj
8869662bb1a4078297020e94ae5e928626d877c6rederpj /*
8869662bb1a4078297020e94ae5e928626d877c6rederpj * ###: be warned, the _default_ content type is already
8869662bb1a4078297020e94ae5e928626d877c6rederpj * picked up here! If we failed the subrequest, or don't
8869662bb1a4078297020e94ae5e928626d877c6rederpj * know what we are serving, then continue.
8869662bb1a4078297020e94ae5e928626d877c6rederpj */
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj if (sub_req->status != HTTP_OK || (!sub_req->content_type)) {
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem ap_destroy_sub_req(sub_req);
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem continue;
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem }
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem /* If it's a map file, we use that instead of the map
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * we're building...
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj */
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj if (((sub_req->content_type) &&
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem !strcmp(sub_req->content_type, MAP_FILE_MAGIC_TYPE)) ||
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem ((sub_req->handler) &&
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem !strcmp(sub_req->handler, "type-map"))) {
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem apr_dir_close(dirp);
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem neg->avail_vars->nelts = 0;
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem if (sub_req->status != HTTP_OK) {
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem return sub_req->status;
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem }
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem return read_type_map(NULL, neg, sub_req);
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem }
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem /* Have reasonable variant --- gather notes. */
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem mime_info.sub_req = sub_req;
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem mime_info.file_name = apr_pstrdup(neg->pool, dirent.name);
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem if (sub_req->content_encoding) {
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem mime_info.content_encoding = sub_req->content_encoding;
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem }
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem if (sub_req->content_languages) {
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem mime_info.content_languages = sub_req->content_languages;
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem }
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem get_entry(neg->pool, &accept_info, sub_req->content_type);
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem set_mime_fields(&mime_info, &accept_info);
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem new_var = apr_array_push(neg->avail_vars);
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem memcpy(new_var, (void *) &mime_info, sizeof(var_rec));
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj neg->count_multiviews_variants++;
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem clean_var_rec(&mime_info);
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem }
11ca38a20ab9b2d00258f745620e2724838e7e21rederpj
11ca38a20ab9b2d00258f745620e2724838e7e21rederpj apr_dir_close(dirp);
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem /* We found some file names that matched. None could be served.
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * Rather than fall out to autoindex or some other mapper, this
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * request must die.
11ca38a20ab9b2d00258f745620e2724838e7e21rederpj */
11ca38a20ab9b2d00258f745620e2724838e7e21rederpj if (anymatch && !neg->avail_vars->nelts) {
11ca38a20ab9b2d00258f745620e2724838e7e21rederpj ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
11ca38a20ab9b2d00258f745620e2724838e7e21rederpj "Negotiation: discovered file(s) matching request: %s"
11ca38a20ab9b2d00258f745620e2724838e7e21rederpj " (None could be negotiated).",
11ca38a20ab9b2d00258f745620e2724838e7e21rederpj r->filename);
11ca38a20ab9b2d00258f745620e2724838e7e21rederpj return HTTP_NOT_FOUND;
0e05808dc59a321566303084c84b9826a4353cefrederpj }
a9c4332dc6241dc11dd104826bd179d42ccc0f12fuankg
a9c4332dc6241dc11dd104826bd179d42ccc0f12fuankg set_vlist_validator(r, r);
11ca38a20ab9b2d00258f745620e2724838e7e21rederpj
11ca38a20ab9b2d00258f745620e2724838e7e21rederpj /* Sort the variants into a canonical order. The negotiation
11ca38a20ab9b2d00258f745620e2724838e7e21rederpj * result sometimes depends on the order of the variants. By
11ca38a20ab9b2d00258f745620e2724838e7e21rederpj * sorting the variants into a canonical order, rather than using
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * the order in which readdir() happens to return them, we ensure
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * that the negotiation result will be consistent over filesystem
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * backup/restores and over all mirror sites.
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem */
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem qsort((void *) neg->avail_vars->elts, neg->avail_vars->nelts,
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem sizeof(var_rec), (int (*)(const void *, const void *)) variantsortf);
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem
11ca38a20ab9b2d00258f745620e2724838e7e21rederpj return OK;
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj}
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj/*****************************************************************
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * And now for the code you've been waiting for... actually
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * finding a match to the client's requirements.
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem */
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem/* Matching MIME types ... the star/star and foo/star commenting conventions
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * are implemented here. (You know what I mean by star/star, but just
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * try mentioning those three characters in a C comment). Using strcmp()
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * is legit, because everything has already been smashed to lowercase.
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj *
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj * Note also that if we get an exact match on the media type, we update
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj * level_matched for use in level_cmp below...
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj *
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj * We also give a value for mime_stars, which is used later. It should
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj * be 1 for star/star, 2 for type/star and 3 for type/subtype.
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj */
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpjstatic int mime_match(accept_rec *accept_r, var_rec *avail)
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj{
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem const char *accept_type = accept_r->name;
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem const char *avail_type = avail->mime_type;
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem int len = strlen(accept_type);
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj if (accept_type[0] == '*') { /* Anything matches star/star */
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj if (avail->mime_stars < 1) {
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj avail->mime_stars = 1;
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj }
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj return 1;
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj }
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj else if ((accept_type[len - 1] == '*') &&
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj !strncmp(accept_type, avail_type, len - 2)) {
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj if (avail->mime_stars < 2) {
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj avail->mime_stars = 2;
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj }
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj return 1;
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj }
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj else if (!strcmp(accept_type, avail_type)
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj || (!strcmp(accept_type, "text/html")
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj && (!strcmp(avail_type, INCLUDES_MAGIC_TYPE)
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem || !strcmp(avail_type, INCLUDES_MAGIC_TYPE3)))) {
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem if (accept_r->level >= avail->level) {
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj avail->level_matched = avail->level;
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj avail->mime_stars = 3;
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem return 1;
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem }
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem }
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem
0e05808dc59a321566303084c84b9826a4353cefrederpj return OK;
0e05808dc59a321566303084c84b9826a4353cefrederpj}
0e05808dc59a321566303084c84b9826a4353cefrederpj
0e05808dc59a321566303084c84b9826a4353cefrederpj/* This code implements a piece of the tie-breaking algorithm between
0e05808dc59a321566303084c84b9826a4353cefrederpj * variants of equal quality. This piece is the treatment of variants
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf * of the same base media type, but different levels. What we want to
0e05808dc59a321566303084c84b9826a4353cefrederpj * return is the variant at the highest level that the client explicitly
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf * claimed to accept.
0e05808dc59a321566303084c84b9826a4353cefrederpj *
0e05808dc59a321566303084c84b9826a4353cefrederpj * If all the variants available are at a higher level than that, or if
0e05808dc59a321566303084c84b9826a4353cefrederpj * the client didn't say anything specific about this media type at all
0e05808dc59a321566303084c84b9826a4353cefrederpj * and these variants just got in on a wildcard, we prefer the lowest
0e05808dc59a321566303084c84b9826a4353cefrederpj * level, on grounds that that's the one that the client is least likely
0e05808dc59a321566303084c84b9826a4353cefrederpj * to choke on.
0e05808dc59a321566303084c84b9826a4353cefrederpj *
0e05808dc59a321566303084c84b9826a4353cefrederpj * (This is all motivated by treatment of levels in HTML --- we only
0e05808dc59a321566303084c84b9826a4353cefrederpj * want to give level 3 to browsers that explicitly ask for it; browsers
0e05808dc59a321566303084c84b9826a4353cefrederpj * that don't, including HTTP/0.9 browsers that only get the implicit
0e05808dc59a321566303084c84b9826a4353cefrederpj * "Accept: * / *" [space added to avoid confusing cpp --- no, that
0e05808dc59a321566303084c84b9826a4353cefrederpj * syntax doesn't really work] should get HTML2 if available).
0e05808dc59a321566303084c84b9826a4353cefrederpj *
0e05808dc59a321566303084c84b9826a4353cefrederpj * (Note that this code only comes into play when we are choosing among
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj * variants of equal quality, where the draft standard gives us a fair
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj * bit of leeway about what to do. It ain't specified by the standard;
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj * rather, it is a choice made by this server about what to do in cases
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj * where the standard does not specify a unique course of action).
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj */
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj
0e05808dc59a321566303084c84b9826a4353cefrederpjstatic int level_cmp(var_rec *var1, var_rec *var2)
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj{
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem /* Levels are only comparable between matching media types */
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem if (var1->is_pseudo_html && !var2->is_pseudo_html) {
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem return 0;
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj }
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf if (!var1->is_pseudo_html && strcmp(var1->mime_type, var2->mime_type)) {
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf return 0;
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj }
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj /* The result of the above if statements is that, if we get to
0e05808dc59a321566303084c84b9826a4353cefrederpj * here, both variants have the same mime_type or both are
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * pseudo-html.
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem */
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj /* Take highest level that matched, if either did match. */
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj if (var1->level_matched > var2->level_matched) {
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj return 1;
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj }
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj if (var1->level_matched < var2->level_matched) {
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj return -1;
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj }
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem /* Neither matched. Take lowest level, if there's a difference. */
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj if (var1->level < var2->level) {
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj return 1;
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem }
0e05808dc59a321566303084c84b9826a4353cefrederpj if (var1->level > var2->level) {
0e05808dc59a321566303084c84b9826a4353cefrederpj return -1;
0e05808dc59a321566303084c84b9826a4353cefrederpj }
0e05808dc59a321566303084c84b9826a4353cefrederpj
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem /* Tied */
0e05808dc59a321566303084c84b9826a4353cefrederpj
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf return 0;
0e05808dc59a321566303084c84b9826a4353cefrederpj}
0e05808dc59a321566303084c84b9826a4353cefrederpj
0e05808dc59a321566303084c84b9826a4353cefrederpj/* Finding languages. The main entry point is set_language_quality()
0e05808dc59a321566303084c84b9826a4353cefrederpj * which is called for each variant. It sets two elements in the
0e05808dc59a321566303084c84b9826a4353cefrederpj * variant record:
0e05808dc59a321566303084c84b9826a4353cefrederpj * language_quality - the 'q' value of the 'best' matching language
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf * from Accept-Language: header (HTTP/1.1)
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf * lang_index - Non-negotiated language priority, using
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf * position of language on the Accept-Language:
0e05808dc59a321566303084c84b9826a4353cefrederpj * header, if present, else LanguagePriority
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj * directive order.
0e05808dc59a321566303084c84b9826a4353cefrederpj *
40a1aee60a66f7c8dbd0835fdd4f09334e12fc15rpluem * When we do the variant checking for best variant, we use language
0e05808dc59a321566303084c84b9826a4353cefrederpj * quality first, and if a tie, language_index next (this only applies
0e05808dc59a321566303084c84b9826a4353cefrederpj * when _not_ using the RVSA/1.0 algorithm). If using the RVSA/1.0
40a1aee60a66f7c8dbd0835fdd4f09334e12fc15rpluem * algorithm, lang_index is never used.
0e05808dc59a321566303084c84b9826a4353cefrederpj *
0e05808dc59a321566303084c84b9826a4353cefrederpj * set_language_quality() calls find_lang_index() and find_default_index()
0e05808dc59a321566303084c84b9826a4353cefrederpj * to set lang_index.
0e05808dc59a321566303084c84b9826a4353cefrederpj */
0e05808dc59a321566303084c84b9826a4353cefrederpj
0e05808dc59a321566303084c84b9826a4353cefrederpjstatic int find_lang_index(apr_array_header_t *accept_langs, char *lang)
0e05808dc59a321566303084c84b9826a4353cefrederpj{
0e05808dc59a321566303084c84b9826a4353cefrederpj const char **alang;
0e05808dc59a321566303084c84b9826a4353cefrederpj int i;
0e05808dc59a321566303084c84b9826a4353cefrederpj
0e05808dc59a321566303084c84b9826a4353cefrederpj if (!lang || !accept_langs) {
0e05808dc59a321566303084c84b9826a4353cefrederpj return -1;
0e05808dc59a321566303084c84b9826a4353cefrederpj }
0e05808dc59a321566303084c84b9826a4353cefrederpj
0e05808dc59a321566303084c84b9826a4353cefrederpj alang = (const char **) accept_langs->elts;
0e05808dc59a321566303084c84b9826a4353cefrederpj
0e05808dc59a321566303084c84b9826a4353cefrederpj for (i = 0; i < accept_langs->nelts; ++i) {
0e05808dc59a321566303084c84b9826a4353cefrederpj if (!strncmp(lang, *alang, strlen(*alang))) {
0e05808dc59a321566303084c84b9826a4353cefrederpj return i;
0e05808dc59a321566303084c84b9826a4353cefrederpj }
0e05808dc59a321566303084c84b9826a4353cefrederpj alang += (accept_langs->elt_size / sizeof(char*));
0e05808dc59a321566303084c84b9826a4353cefrederpj }
0e05808dc59a321566303084c84b9826a4353cefrederpj
0e05808dc59a321566303084c84b9826a4353cefrederpj return -1;
0e05808dc59a321566303084c84b9826a4353cefrederpj}
0e05808dc59a321566303084c84b9826a4353cefrederpj
0e05808dc59a321566303084c84b9826a4353cefrederpj/* set_default_lang_quality() sets the quality we apply to variants
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj * which have no language assigned to them. If none of the variants
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj * have a language, we are not negotiating on language, so all are
0e05808dc59a321566303084c84b9826a4353cefrederpj * acceptable, and we set the default q value to 1.0. However if
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj * some of the variants have languages, we set this default to 0.0001.
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj * The value of this default will be applied to all variants with
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * no explicit language -- which will have the effect of making them
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * acceptable, but only if no variants with an explicit language
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * are acceptable. The default q value set here is assigned to variants
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * with no language type in set_language_quality().
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem *
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj * Note that if using the RVSA/1.0 algorithm, we don't use this
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj * fiddle.
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj */
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpjstatic void set_default_lang_quality(negotiation_state *neg)
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj{
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj int j;
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem if (!neg->dont_fiddle_headers) {
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem for (j = 0; j < neg->avail_vars->nelts; ++j) {
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem var_rec *variant = &avail_recs[j];
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj if (variant->content_languages &&
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj variant->content_languages->nelts) {
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem neg->default_lang_quality = 0.0001f;
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem return;
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem }
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem }
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf }
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf neg->default_lang_quality = 1.0f;
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf}
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj/* Set the language_quality value in the variant record. Also
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * assigns lang_index for ForceLanguagePriority.
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem *
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * To find the language_quality value, we look for the 'q' value
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * of the 'best' matching language on the Accept-Language
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf * header. The 'best' match is the language on Accept-Language
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf * header which matches the language of this variant either fully,
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf * or as far as the prefix marker (-). If two or more languages
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf * match, use the longest string from the Accept-Language header
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj * (see HTTP/1.1 [14.4])
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem *
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * When a variant has multiple languages, we find the 'best'
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * match for each variant language tag as above, then select the
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * one with the highest q value. Because both the accept-header
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj * and variant can have multiple languages, we now have a hairy
85da6b76d07b7af570cbbec208a87697ba9c44f5rederpj * loop-within-a-loop here.
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj *
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj * If the variant has no language and we have no Accept-Language
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj * items, leave the quality at 1.0 and return.
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj *
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj * If the variant has no language, we use the default as set by
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj * set_default_lang_quality() (1.0 if we are not negotiating on
8113dac419143273351446c3ad653f3fe5ba5cfdwrowe * language, 0.001 if we are).
e8f95a682820a599fe41b22977010636be5c2717jim *
8113dac419143273351446c3ad653f3fe5ba5cfdwrowe * Following the setting of the language quality, we drop through to
e8f95a682820a599fe41b22977010636be5c2717jim * set the old 'lang_index'. This is set based on either the order
8113dac419143273351446c3ad653f3fe5ba5cfdwrowe * of the languages on the Accept-Language header, or the
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * order on the LanguagePriority directive. This is only used
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * in the negotiation if the language qualities tie.
8bdea88407c848c1c2693655e2f8b23abde12307bnicholes */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholesstatic void set_language_quality(negotiation_state *neg, var_rec *variant)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes{
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes int forcepriority = neg->conf->forcelangpriority;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (forcepriority == FLP_UNDEF) {
f05787953018140838ad51456c86c965d6a86267jim forcepriority = FLP_DEFAULT;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
f05787953018140838ad51456c86c965d6a86267jim
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (!variant->content_languages || !variant->content_languages->nelts) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* This variant has no content-language, so use the default
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * quality factor for variants with no content-language
e8f95a682820a599fe41b22977010636be5c2717jim * (previously set by set_default_lang_quality()).
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * Leave the factor alone (it remains at 1.0) when we may not fiddle
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * with the headers.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (!neg->dont_fiddle_headers) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes variant->lang_quality = neg->default_lang_quality;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
e8f95a682820a599fe41b22977010636be5c2717jim if (!neg->accept_langs) {
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe return; /* no accept-language header */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes else {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* Variant has one (or more) languages. Look for the best
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * match. We do this by going through each language on the
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * variant description looking for a match on the
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * Accept-Language header. The best match is the longest
e8f95a682820a599fe41b22977010636be5c2717jim * matching language on the header. The final result is the
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * best q value from all the languages on the variant
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * description.
e8f95a682820a599fe41b22977010636be5c2717jim */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (!neg->accept_langs) {
e8f95a682820a599fe41b22977010636be5c2717jim /* no accept-language header makes the variant indefinite */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes variant->definite = 0;
e8f95a682820a599fe41b22977010636be5c2717jim }
e8f95a682820a599fe41b22977010636be5c2717jim else { /* There is an accept-language with 0 or more items */
e8f95a682820a599fe41b22977010636be5c2717jim accept_rec *accs = (accept_rec *) neg->accept_langs->elts;
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe accept_rec *best = NULL, *star = NULL;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes accept_rec *bestthistag;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes char *lang, *p;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes float fiddle_q = 0.0f;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes int any_match_on_star = 0;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes int i, j;
e8f95a682820a599fe41b22977010636be5c2717jim apr_size_t alen, longest_lang_range_len;
e8f95a682820a599fe41b22977010636be5c2717jim
e8f95a682820a599fe41b22977010636be5c2717jim for (j = 0; j < variant->content_languages->nelts; ++j) {
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe p = NULL;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes bestthistag = NULL;
c4f16f709c79bb7e2ddffb532bc7708eab9a9691covener longest_lang_range_len = 0;
c4f16f709c79bb7e2ddffb532bc7708eab9a9691covener alen = 0;
d64dd2fd4516c2b1b664c5e59c0628d9aff26984covener
d64dd2fd4516c2b1b664c5e59c0628d9aff26984covener /* lang is the variant's language-tag, which is the one
d64dd2fd4516c2b1b664c5e59c0628d9aff26984covener * we are allowed to use the prefix of in HTTP/1.1
d64dd2fd4516c2b1b664c5e59c0628d9aff26984covener */
c4f16f709c79bb7e2ddffb532bc7708eab9a9691covener lang = ((char **) (variant->content_languages->elts))[j];
c4f16f709c79bb7e2ddffb532bc7708eab9a9691covener
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* now find the best (i.e. longest) matching
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * Accept-Language header language. We put the best match
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * for this tag in bestthistag. We cannot update the
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * overall best (based on q value) because the best match
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * for this tag is the longest language item on the accept
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * header, not necessarily the highest q.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes for (i = 0; i < neg->accept_langs->nelts; ++i) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (!strcmp(accs[i].name, "*")) {
e8f95a682820a599fe41b22977010636be5c2717jim if (!star) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes star = &accs[i];
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes continue;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe /* Find language. We match if either the variant
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * language tag exactly matches the language range
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * from the accept header, or a prefix of the variant
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * language tag up to a '-' character matches the
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * whole of the language range in the Accept-Language
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * header. Note that HTTP/1.x allows any number of
f43b67c5a9d29b572eac916f8335cedc80c908bebnicholes * '-' characters in a tag or range, currently only
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * tags with zero or one '-' characters are defined
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * for general use (see rfc1766).
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes *
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * We only use language range in the Accept-Language
37af4b0cf648275b68ff41c866c665b4ccf4667dcovener * header the best match for the variant language tag
37af4b0cf648275b68ff41c866c665b4ccf4667dcovener * if it is longer than the previous best match.
37af4b0cf648275b68ff41c866c665b4ccf4667dcovener */
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf
37af4b0cf648275b68ff41c866c665b4ccf4667dcovener alen = strlen(accs[i].name);
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if ((strlen(lang) >= alen) &&
f43b67c5a9d29b572eac916f8335cedc80c908bebnicholes !strncmp(lang, accs[i].name, alen) &&
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes ((lang[alen] == 0) || (lang[alen] == '-')) ) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (alen > longest_lang_range_len) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes longest_lang_range_len = alen;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes bestthistag = &accs[i];
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (!bestthistag && !neg->dont_fiddle_headers) {
e8f95a682820a599fe41b22977010636be5c2717jim /* The next bit is a fiddle. Some browsers might
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * be configured to send more specific language
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * ranges than desirable. For example, an
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * Accept-Language of en-US should never match
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * variants with languages en or en-GB. But US
e8f95a682820a599fe41b22977010636be5c2717jim * English speakers might pick en-US as their
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * language choice. So this fiddle checks if the
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * language range has a prefix, and if so, it
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * matches variants which match that prefix with a
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * priority of 0.001. So a request for en-US would
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * match variants of types en and en-GB, but at
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * much lower priority than matches of en-US
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * directly, or of any other language listed on
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * the Accept-Language header. Note that this
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * fiddle does not handle multi-level prefixes.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if ((p = strchr(accs[i].name, '-'))) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes int plen = p - accs[i].name;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (!strncmp(lang, accs[i].name, plen)) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes fiddle_q = 0.001f;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
e8f95a682820a599fe41b22977010636be5c2717jim }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* Finished looking at Accept-Language headers, the best
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * (longest) match is in bestthistag, or NULL if no match
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (!best ||
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes (bestthistag && bestthistag->quality > best->quality)) {
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes best = bestthistag;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* See if the tag matches on a * in the Accept-Language
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * header. If so, record this fact for later use
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
e8f95a682820a599fe41b22977010636be5c2717jim if (!bestthistag && star) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes any_match_on_star = 1;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
7dbf29be626018bc389ef94c1846aeac4b72633bsf /* If one of the language tags of the variant matched on *, we
7dbf29be626018bc389ef94c1846aeac4b72633bsf * need to see if its q is better than that of any non-* match
7dbf29be626018bc389ef94c1846aeac4b72633bsf * on any other tag of the variant. If so the * match takes
7dbf29be626018bc389ef94c1846aeac4b72633bsf * precedence and the overall match is not definite.
7dbf29be626018bc389ef94c1846aeac4b72633bsf */
7dbf29be626018bc389ef94c1846aeac4b72633bsf if ( any_match_on_star &&
7dbf29be626018bc389ef94c1846aeac4b72633bsf ((best && star->quality > best->quality) ||
7dbf29be626018bc389ef94c1846aeac4b72633bsf (!best)) ) {
7dbf29be626018bc389ef94c1846aeac4b72633bsf best = star;
7dbf29be626018bc389ef94c1846aeac4b72633bsf variant->definite = 0;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
f43b67c5a9d29b572eac916f8335cedc80c908bebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes variant->lang_quality = best ? best->quality : fiddle_q;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* Handle the ForceDefaultLanguage overrides, based on the best match
7dbf29be626018bc389ef94c1846aeac4b72633bsf * to LanguagePriority order. The best match is the lowest index of
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * any LanguagePriority match.
f43b67c5a9d29b572eac916f8335cedc80c908bebnicholes */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (((forcepriority & FLP_PREFER)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes && (variant->lang_index < 0))
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes || ((forcepriority & FLP_FALLBACK)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes && !variant->lang_quality))
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes int bestidx = -1;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes int j;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes for (j = 0; j < variant->content_languages->nelts; ++j)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* lang is the variant's language-tag, which is the one
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * we are allowed to use the prefix of in HTTP/1.1
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes char *lang = ((char **) (variant->content_languages->elts))[j];
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes int idx = -1;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* If we wish to fallback or
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * we use our own LanguagePriority index.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes idx = find_lang_index(neg->conf->language_priority, lang);
8bdea88407c848c1c2693655e2f8b23abde12307bnicholes if ((idx >= 0) && ((bestidx == -1) || (idx < bestidx))) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes bestidx = idx;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (bestidx >= 0) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (variant->lang_quality) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (forcepriority & FLP_PREFER) {
e8f95a682820a599fe41b22977010636be5c2717jim variant->lang_index = bestidx;
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes else {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (forcepriority & FLP_FALLBACK) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes variant->lang_index = bestidx;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes variant->lang_quality = .0001f;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes variant->definite = 0;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
e8f95a682820a599fe41b22977010636be5c2717jim }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes}
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes/* Determining the content length --- if the map didn't tell us,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * we have to do a stat() and remember for next time.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholesstatic apr_off_t find_content_length(negotiation_state *neg, var_rec *variant)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes{
8bdea88407c848c1c2693655e2f8b23abde12307bnicholes apr_finfo_t statb;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
e8f95a682820a599fe41b22977010636be5c2717jim if (variant->bytes < 0) {
e8f95a682820a599fe41b22977010636be5c2717jim if ( variant->sub_req
e8f95a682820a599fe41b22977010636be5c2717jim && (variant->sub_req->finfo.valid & APR_FINFO_SIZE)) {
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe variant->bytes = variant->sub_req->finfo.size;
e8f95a682820a599fe41b22977010636be5c2717jim }
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe else {
e8f95a682820a599fe41b22977010636be5c2717jim char *fullname = ap_make_full_path(neg->pool, neg->dir_name,
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes variant->file_name);
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes if (apr_stat(&statb, fullname,
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes APR_FINFO_SIZE, neg->pool) == APR_SUCCESS) {
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes variant->bytes = statb.size;
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes }
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes return variant->bytes;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes}
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes/* For a given variant, find the best matching Accept: header
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * and assign the Accept: header's quality value to the
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * mime_type_quality field of the variant, for later use in
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * determining the best matching variant.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholesstatic void set_accept_quality(negotiation_state *neg, var_rec *variant)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes{
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes int i;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes accept_rec *accept_recs;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes float q = 0.0f;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes int q_definite = 1;
8113dac419143273351446c3ad653f3fe5ba5cfdwrowe
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes /* if no Accept: header, leave quality alone (will
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * remain at the default value of 1)
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes *
f43b67c5a9d29b572eac916f8335cedc80c908bebnicholes * XXX: This if is currently never true because of the effect of
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * maybe_add_default_accepts().
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes */
8113dac419143273351446c3ad653f3fe5ba5cfdwrowe if (!neg->accepts) {
e8f95a682820a599fe41b22977010636be5c2717jim if (variant->mime_type && *variant->mime_type)
e8f95a682820a599fe41b22977010636be5c2717jim variant->definite = 0;
8113dac419143273351446c3ad653f3fe5ba5cfdwrowe return;
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes }
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes
8bdea88407c848c1c2693655e2f8b23abde12307bnicholes accept_recs = (accept_rec *) neg->accepts->elts;
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes /*
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * Go through each of the ranges on the Accept: header,
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * looking for the 'best' match with this variant's
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * content-type. We use the best match's quality
f05787953018140838ad51456c86c965d6a86267jim * value (from the Accept: header) for this variant's
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * mime_type_quality field.
f05787953018140838ad51456c86c965d6a86267jim *
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * The best match is determined like this:
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * type/type is better than type/ * is better than * / *
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * if match is type/type, use the level mime param if available
e8f95a682820a599fe41b22977010636be5c2717jim */
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes for (i = 0; i < neg->accepts->nelts; ++i) {
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes accept_rec *type = &accept_recs[i];
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes int prev_mime_stars;
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes prev_mime_stars = variant->mime_stars;
e8f95a682820a599fe41b22977010636be5c2717jim
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe if (!mime_match(type, variant)) {
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes continue; /* didn't match the content type at all */
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes }
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes else {
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes /* did match - see if there were less or more stars than
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * in previous match
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes */
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes if (prev_mime_stars == variant->mime_stars) {
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes continue; /* more stars => not as good a match */
e8f95a682820a599fe41b22977010636be5c2717jim }
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe }
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes
e8f95a682820a599fe41b22977010636be5c2717jim /* If we are allowed to mess with the q-values
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * and have no explicit q= parameters in the accept header,
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * make wildcards very low, so we have a low chance
e8f95a682820a599fe41b22977010636be5c2717jim * of ending up with them if there's something better.
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes */
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes if (!neg->dont_fiddle_headers && !neg->accept_q &&
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes variant->mime_stars == 1) {
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes q = 0.01f;
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes }
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes else if (!neg->dont_fiddle_headers && !neg->accept_q &&
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes variant->mime_stars == 2) {
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes q = 0.02f;
c4f16f709c79bb7e2ddffb532bc7708eab9a9691covener }
c4f16f709c79bb7e2ddffb532bc7708eab9a9691covener else {
d64dd2fd4516c2b1b664c5e59c0628d9aff26984covener q = type->quality;
d64dd2fd4516c2b1b664c5e59c0628d9aff26984covener }
d64dd2fd4516c2b1b664c5e59c0628d9aff26984covener
d64dd2fd4516c2b1b664c5e59c0628d9aff26984covener q_definite = (variant->mime_stars == 3);
c4f16f709c79bb7e2ddffb532bc7708eab9a9691covener }
c4f16f709c79bb7e2ddffb532bc7708eab9a9691covener variant->mime_type_quality = q;
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes variant->definite = variant->definite && q_definite;
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes}
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes/* For a given variant, find the 'q' value of the charset given
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * on the Accept-Charset line. If no charsets are listed,
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * assume value of '1'.
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes */
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholesstatic void set_charset_quality(negotiation_state *neg, var_rec *variant)
e8f95a682820a599fe41b22977010636be5c2717jim{
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes int i;
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes accept_rec *accept_recs;
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes const char *charset = variant->content_charset;
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes accept_rec *star = NULL;
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes /* if no Accept-Charset: header, leave quality alone (will
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * remain at the default value of 1)
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes */
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes if (!neg->accept_charsets) {
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes if (charset && *charset)
f43b67c5a9d29b572eac916f8335cedc80c908bebnicholes variant->definite = 0;
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes return;
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes }
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes accept_recs = (accept_rec *) neg->accept_charsets->elts;
37af4b0cf648275b68ff41c866c665b4ccf4667dcovener
37af4b0cf648275b68ff41c866c665b4ccf4667dcovener if (charset == NULL || !*charset) {
37af4b0cf648275b68ff41c866c665b4ccf4667dcovener /* Charset of variant not known */
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf
37af4b0cf648275b68ff41c866c665b4ccf4667dcovener /* if not a text / * type, leave quality alone */
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe if (!(!strncmp(variant->mime_type, "text/", 5)
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes || !strcmp(variant->mime_type, INCLUDES_MAGIC_TYPE)
f43b67c5a9d29b572eac916f8335cedc80c908bebnicholes || !strcmp(variant->mime_type, INCLUDES_MAGIC_TYPE3)
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes ))
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes return;
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes /* Don't go guessing if we are in strict header mode,
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * e.g. when running the rvsa, as any guess won't be reflected
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * in the variant list or content-location headers.
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes */
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes if (neg->dont_fiddle_headers)
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes return;
e8f95a682820a599fe41b22977010636be5c2717jim
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes charset = "iso-8859-1"; /* The default charset for HTTP text types */
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes }
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes /*
e8f95a682820a599fe41b22977010636be5c2717jim * Go through each of the items on the Accept-Charset header,
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * looking for a match with this variant's charset. If none
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * match, charset is unacceptable, so set quality to 0.
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes */
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes for (i = 0; i < neg->accept_charsets->nelts; ++i) {
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe accept_rec *type = &accept_recs[i];
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes if (!strcmp(type->name, charset)) {
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes variant->charset_quality = type->quality;
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes return;
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes }
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes else if (strcmp(type->name, "*") == 0) {
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes star = type;
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes }
490046d2a164ad86cc63bd789f32eaed663d239abnicholes }
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes /* No explicit match */
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes if (star) {
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes variant->charset_quality = star->quality;
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes variant->definite = 0;
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes return;
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes }
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes /* If this variant is in charset iso-8859-1, the default is 1.0 */
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes if (strcmp(charset, "iso-8859-1") == 0) {
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes variant->charset_quality = 1.0f;
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes }
8bdea88407c848c1c2693655e2f8b23abde12307bnicholes else {
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes variant->charset_quality = 0.0f;
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes }
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes}
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes/* is_identity_encoding is included for back-compat, but does anyone
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * use 7bit, 8bin or binary in their var files??
e8f95a682820a599fe41b22977010636be5c2717jim */
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholesstatic int is_identity_encoding(const char *enc)
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes{
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes return (!enc || !enc[0] || !strcmp(enc, "7bit") || !strcmp(enc, "8bit")
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes || !strcmp(enc, "binary"));
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes}
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes/*
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * set_encoding_quality determines whether the encoding for a particular
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * variant is acceptable for the user-agent.
e8f95a682820a599fe41b22977010636be5c2717jim *
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * The rules for encoding are that if the user-agent does not supply
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * any Accept-Encoding header, then all encodings are allowed but a
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * variant with no encoding should be preferred.
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * If there is an empty Accept-Encoding header, then no encodings are
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * acceptable. If there is a non-empty Accept-Encoding header, then
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * any of the listed encodings are acceptable, as well as no encoding
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * unless the "identity" encoding is specifically excluded.
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes */
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholesstatic void set_encoding_quality(negotiation_state *neg, var_rec *variant)
8bdea88407c848c1c2693655e2f8b23abde12307bnicholes{
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes accept_rec *accept_recs;
e8f95a682820a599fe41b22977010636be5c2717jim const char *enc = variant->content_encoding;
e8f95a682820a599fe41b22977010636be5c2717jim accept_rec *star = NULL;
e8f95a682820a599fe41b22977010636be5c2717jim float value_if_not_found = 0.0f;
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe int i;
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe if (!neg->accept_encodings) {
e8f95a682820a599fe41b22977010636be5c2717jim /* We had no Accept-Encoding header, assume that all
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * encodings are acceptable with a low quality,
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * but we prefer no encoding if available.
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes */
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes if (!enc || is_identity_encoding(enc))
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes variant->encoding_quality = 1.0f;
0c8aa496e9d7676ff8101783398f17c0da1900f7bnicholes else
0c8aa496e9d7676ff8101783398f17c0da1900f7bnicholes variant->encoding_quality = 0.5f;
0c8aa496e9d7676ff8101783398f17c0da1900f7bnicholes
0c8aa496e9d7676ff8101783398f17c0da1900f7bnicholes return;
0c8aa496e9d7676ff8101783398f17c0da1900f7bnicholes }
0c8aa496e9d7676ff8101783398f17c0da1900f7bnicholes
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes if (!enc || is_identity_encoding(enc)) {
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes enc = "identity";
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes value_if_not_found = 0.0001f;
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes }
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes accept_recs = (accept_rec *) neg->accept_encodings->elts;
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes /* Go through each of the encodings on the Accept-Encoding: header,
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes * looking for a match with our encoding. x- prefixes are ignored.
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes */
03f4448f864e31ade79856ac8c264a5e6dce3b10bnicholes if (enc[0] == 'x' && enc[1] == '-') {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes enc += 2;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
e8f95a682820a599fe41b22977010636be5c2717jim for (i = 0; i < neg->accept_encodings->nelts; ++i) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes char *name = accept_recs[i].name;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
8113dac419143273351446c3ad653f3fe5ba5cfdwrowe if (name[0] == 'x' && name[1] == '-') {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes name += 2;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (!strcmp(name, enc)) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin variant->encoding_quality = accept_recs[i].quality;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (strcmp(name, "*") == 0) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes star = &accept_recs[i];
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
e8f95a682820a599fe41b22977010636be5c2717jim }
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe /* No explicit match */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (star) {
e8f95a682820a599fe41b22977010636be5c2717jim variant->encoding_quality = star->quality;
e8f95a682820a599fe41b22977010636be5c2717jim return;
43c3e6a4b559b76b750c245ee95e2782c15b4296jim }
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes /* Encoding not found on Accept-Encoding: header, so it is
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes * _not_ acceptable unless it is the identity (no encoding)
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes */
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes variant->encoding_quality = value_if_not_found;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes}
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes/*************************************************************
e8f95a682820a599fe41b22977010636be5c2717jim * Possible results of the variant selection algorithm
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf */
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sfenum algorithm_results {
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf alg_choice = 1, /* choose variant */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes alg_list /* list variants */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes};
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes/* Below is the 'best_match' function. It returns an int, which has
e8f95a682820a599fe41b22977010636be5c2717jim * one of the two values alg_choice or alg_list, which give the result
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * of the variant selection algorithm. alg_list means that no best
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * variant was found by the algorithm, alg_choice means that a best
e8f95a682820a599fe41b22977010636be5c2717jim * variant was found and should be returned. The list/choice
e8f95a682820a599fe41b22977010636be5c2717jim * terminology comes from TCN (rfc2295), but is used in a more generic
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * way here. The best variant is returned in *pbest. best_match has
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes * two possible algorithms for determining the best variant: the
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes * RVSA/1.0 algorithm (from RFC2296), and the standard Apache
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes * algorithm. These are split out into separate functions
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes * (is_variant_better_rvsa() and is_variant_better()). Selection of
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes * one is through the neg->use_rvsa flag.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes *
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * The call to best_match also creates full information, including
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * language, charset, etc quality for _every_ variant. This is needed
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * for generating a correct Vary header, and can be used for the
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * Alternates header, the human-readable list responses and 406 errors.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes/* Firstly, the RVSA/1.0 (HTTP Remote Variant Selection Algorithm
e8f95a682820a599fe41b22977010636be5c2717jim * v1.0) from rfc2296. This is the algorithm that goes together with
e8f95a682820a599fe41b22977010636be5c2717jim * transparent content negotiation (TCN).
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholesstatic int is_variant_better_rvsa(negotiation_state *neg, var_rec *variant,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes var_rec *best, float *p_bestq)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes{
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes float bestq = *p_bestq, q;
e8f95a682820a599fe41b22977010636be5c2717jim
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe /* TCN does not cover negotiation on content-encoding. For now,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * we ignore the encoding unless it was explicitly excluded.
e8f95a682820a599fe41b22977010636be5c2717jim */
e8f95a682820a599fe41b22977010636be5c2717jim if (variant->encoding_quality == 0.0f)
43c3e6a4b559b76b750c245ee95e2782c15b4296jim return 0;
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes q = variant->mime_type_quality *
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes variant->source_quality *
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes variant->charset_quality *
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes variant->lang_quality;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* RFC 2296 calls for the result to be rounded to 5 decimal places,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * but we don't do that because it serves no useful purpose other
e8f95a682820a599fe41b22977010636be5c2717jim * than to ensure that a remote algorithm operates on the same
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf * precision as ours. That is silly, since what we obviously want
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf * is for the algorithm to operate on the best available precision
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * regardless of who runs it. Since the above calculation may
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * result in significant variance at 1e-12, rounding would be bogus.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
e8f95a682820a599fe41b22977010636be5c2717jim#ifdef NEG_DEBUG
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes "Variant: file=%s type=%s lang=%s sourceq=%1.3f "
e8f95a682820a599fe41b22977010636be5c2717jim "mimeq=%1.3f langq=%1.3f charq=%1.3f encq=%1.3f "
e8f95a682820a599fe41b22977010636be5c2717jim "q=%1.5f definite=%d",
43c3e6a4b559b76b750c245ee95e2782c15b4296jim (variant->file_name ? variant->file_name : ""),
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes (variant->mime_type ? variant->mime_type : ""),
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes (variant->content_languages
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes ? apr_array_pstrcat(neg->pool, variant->content_languages, ',')
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes : ""),
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes variant->source_quality,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes variant->mime_type_quality,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes variant->lang_quality,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes variant->charset_quality,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes variant->encoding_quality,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes q,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes variant->definite);
e8f95a682820a599fe41b22977010636be5c2717jim#endif
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf if (q <= 0.0f) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return 0;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (q > bestq) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes *p_bestq = q;
e8f95a682820a599fe41b22977010636be5c2717jim return 1;
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (q == bestq) {
e8f95a682820a599fe41b22977010636be5c2717jim /* If the best variant's encoding is of lesser quality than
e8f95a682820a599fe41b22977010636be5c2717jim * this variant, then we prefer this variant
43c3e6a4b559b76b750c245ee95e2782c15b4296jim */
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes if (variant->encoding_quality > best->encoding_quality) {
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes *p_bestq = q;
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes return 1;
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes }
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return 0;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes}
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
e8f95a682820a599fe41b22977010636be5c2717jim/* Negotiation algorithm as used by previous versions of Apache
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf * (just about).
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholesstatic int is_variant_better(negotiation_state *neg, var_rec *variant,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes var_rec *best, float *p_bestq)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes{
e8f95a682820a599fe41b22977010636be5c2717jim float bestq = *p_bestq, q;
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe int levcmp;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
e8f95a682820a599fe41b22977010636be5c2717jim /* For non-transparent negotiation, server can choose how
e8f95a682820a599fe41b22977010636be5c2717jim * to handle the negotiation. We'll use the following in
43c3e6a4b559b76b750c245ee95e2782c15b4296jim * order: content-type, language, content-type level, charset,
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes * content encoding, content length.
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes *
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes * For each check, we have three possible outcomes:
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes * This variant is worse than current best: return 0
3effb85eb3124c6f02cd89e22ffed0fc9afaddb9bnicholes * This variant is better than the current best:
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * assign this variant's q to *p_bestq, and return 1
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * This variant is just as desirable as the current best:
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * drop through to the next test.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes *
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * This code is written in this long-winded way to allow future
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * customisation, either by the addition of additional
e8f95a682820a599fe41b22977010636be5c2717jim * checks, or to allow the order of the checks to be determined
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf * by configuration options (e.g. we might prefer to check
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf * language quality _before_ content type).
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* First though, eliminate this variant if it is not
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * acceptable by type, charset, encoding or language.
54d22ed1c429b903b029bbd62621f11a9e286137minfrin */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin#ifdef NEG_DEBUG
54d22ed1c429b903b029bbd62621f11a9e286137minfrin ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
54d22ed1c429b903b029bbd62621f11a9e286137minfrin "Variant: file=%s type=%s lang=%s sourceq=%1.3f "
54d22ed1c429b903b029bbd62621f11a9e286137minfrin "mimeq=%1.3f langq=%1.3f langidx=%d charq=%1.3f encq=%1.3f ",
54d22ed1c429b903b029bbd62621f11a9e286137minfrin (variant->file_name ? variant->file_name : ""),
54d22ed1c429b903b029bbd62621f11a9e286137minfrin (variant->mime_type ? variant->mime_type : ""),
54d22ed1c429b903b029bbd62621f11a9e286137minfrin (variant->content_languages
54d22ed1c429b903b029bbd62621f11a9e286137minfrin ? apr_array_pstrcat(neg->pool, variant->content_languages, ',')
88adce5ec0da39b41450ce1d5a77659168097e0cjorton : ""),
88adce5ec0da39b41450ce1d5a77659168097e0cjorton variant->source_quality,
54d22ed1c429b903b029bbd62621f11a9e286137minfrin variant->mime_type_quality,
54d22ed1c429b903b029bbd62621f11a9e286137minfrin variant->lang_quality,
54d22ed1c429b903b029bbd62621f11a9e286137minfrin variant->lang_index,
54d22ed1c429b903b029bbd62621f11a9e286137minfrin variant->charset_quality,
54d22ed1c429b903b029bbd62621f11a9e286137minfrin variant->encoding_quality);
54d22ed1c429b903b029bbd62621f11a9e286137minfrin#endif
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if (variant->encoding_quality == 0.0f ||
54d22ed1c429b903b029bbd62621f11a9e286137minfrin variant->lang_quality == 0.0f ||
54d22ed1c429b903b029bbd62621f11a9e286137minfrin variant->source_quality == 0.0f ||
54d22ed1c429b903b029bbd62621f11a9e286137minfrin variant->charset_quality == 0.0f ||
54d22ed1c429b903b029bbd62621f11a9e286137minfrin variant->mime_type_quality == 0.0f) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin return 0; /* don't consider unacceptables */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin q = variant->mime_type_quality * variant->source_quality;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if (q == 0.0 || q < bestq) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin return 0;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if (q > bestq || !best) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin *p_bestq = q;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin return 1;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* language */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if (variant->lang_quality < best->lang_quality) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin return 0;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if (variant->lang_quality > best->lang_quality) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin *p_bestq = q;
09338db7fdcf82ecc189195347da3a3ed5d0287abnicholes return 1;
09338db7fdcf82ecc189195347da3a3ed5d0287abnicholes }
09338db7fdcf82ecc189195347da3a3ed5d0287abnicholes
09338db7fdcf82ecc189195347da3a3ed5d0287abnicholes /* if language qualities were equal, try the LanguagePriority stuff */
09338db7fdcf82ecc189195347da3a3ed5d0287abnicholes if (best->lang_index != -1 &&
54d22ed1c429b903b029bbd62621f11a9e286137minfrin (variant->lang_index == -1 || variant->lang_index > best->lang_index)) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin return 0;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if (variant->lang_index != -1 &&
54d22ed1c429b903b029bbd62621f11a9e286137minfrin (best->lang_index == -1 || variant->lang_index < best->lang_index)) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin *p_bestq = q;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin return 1;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* content-type level (sometimes used with text/html, though we
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * support it on other types too)
54d22ed1c429b903b029bbd62621f11a9e286137minfrin */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin levcmp = level_cmp(variant, best);
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if (levcmp == -1) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin return 0;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if (levcmp == 1) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin *p_bestq = q;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin return 1;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin }
09338db7fdcf82ecc189195347da3a3ed5d0287abnicholes
09338db7fdcf82ecc189195347da3a3ed5d0287abnicholes /* charset */
09338db7fdcf82ecc189195347da3a3ed5d0287abnicholes if (variant->charset_quality < best->charset_quality) {
09338db7fdcf82ecc189195347da3a3ed5d0287abnicholes return 0;
09338db7fdcf82ecc189195347da3a3ed5d0287abnicholes }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* If the best variant's charset is ISO-8859-1 and this variant has
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * the same charset quality, then we prefer this variant
54d22ed1c429b903b029bbd62621f11a9e286137minfrin */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if (variant->charset_quality > best->charset_quality ||
54d22ed1c429b903b029bbd62621f11a9e286137minfrin ((variant->content_charset != NULL &&
54d22ed1c429b903b029bbd62621f11a9e286137minfrin *variant->content_charset != '\0' &&
54d22ed1c429b903b029bbd62621f11a9e286137minfrin strcmp(variant->content_charset, "iso-8859-1") != 0) &&
54d22ed1c429b903b029bbd62621f11a9e286137minfrin (best->content_charset == NULL ||
54d22ed1c429b903b029bbd62621f11a9e286137minfrin *best->content_charset == '\0' ||
54d22ed1c429b903b029bbd62621f11a9e286137minfrin strcmp(best->content_charset, "iso-8859-1") == 0))) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin *p_bestq = q;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin return 1;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* Prefer the highest value for encoding_quality.
54d22ed1c429b903b029bbd62621f11a9e286137minfrin */
e8f95a682820a599fe41b22977010636be5c2717jim if (variant->encoding_quality < best->encoding_quality) {
e8f95a682820a599fe41b22977010636be5c2717jim return 0;
e8f95a682820a599fe41b22977010636be5c2717jim }
e8f95a682820a599fe41b22977010636be5c2717jim if (variant->encoding_quality > best->encoding_quality) {
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe *p_bestq = q;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return 1;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* content length if all else equal */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (find_content_length(neg, variant) >= find_content_length(neg, best)) {
5aa455d45abacfa675c88d4ff53fbe97c44ce545bnicholes return 0;
1223ef8a85a044b5e3a8df29391a66530153aefcbnicholes }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
d5cff0d8e871bf2528aadd8736fb50dc044b1e6dbnicholes /* ok, to get here means every thing turned out equal, except
5aa455d45abacfa675c88d4ff53fbe97c44ce545bnicholes * we have a shorter content length, so use this variant
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes *p_bestq = q;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return 1;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes}
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin/* figure out, whether a variant is in a specific language
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * it returns also false, if the variant has no language.
54d22ed1c429b903b029bbd62621f11a9e286137minfrin */
54d22ed1c429b903b029bbd62621f11a9e286137minfrinstatic int variant_has_language(var_rec *variant, const char *lang)
54d22ed1c429b903b029bbd62621f11a9e286137minfrin{
54d22ed1c429b903b029bbd62621f11a9e286137minfrin int j, max;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* fast exit */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if ( !lang
54d22ed1c429b903b029bbd62621f11a9e286137minfrin || !variant->content_languages
54d22ed1c429b903b029bbd62621f11a9e286137minfrin || !(max = variant->content_languages->nelts)) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin return 0;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin for (j = 0; j < max; ++j) {
796e4a7141265d8ed7036e4628161c6eafb2a789jorton if (!strcmp(lang,
54d22ed1c429b903b029bbd62621f11a9e286137minfrin ((char **) (variant->content_languages->elts))[j])) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin return 1;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin }
d5cff0d8e871bf2528aadd8736fb50dc044b1e6dbnicholes
54d22ed1c429b903b029bbd62621f11a9e286137minfrin return 0;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin}
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin/* check for environment variables 'no-gzip' and
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * 'gzip-only-text/html' to get a behaviour similiar
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * to mod_deflate
54d22ed1c429b903b029bbd62621f11a9e286137minfrin */
54d22ed1c429b903b029bbd62621f11a9e286137minfrinstatic int discard_variant_by_env(var_rec *variant, int discard)
54d22ed1c429b903b029bbd62621f11a9e286137minfrin{
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if ( is_identity_encoding(variant->content_encoding)
e8f95a682820a599fe41b22977010636be5c2717jim || !strcmp(variant->content_encoding, "identity")) {
e8f95a682820a599fe41b22977010636be5c2717jim return 0;
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin return ( (discard == DISCARD_ALL_ENCODINGS)
54d22ed1c429b903b029bbd62621f11a9e286137minfrin || (discard == DISCARD_ALL_BUT_HTML
54d22ed1c429b903b029bbd62621f11a9e286137minfrin && (!variant->mime_type
54d22ed1c429b903b029bbd62621f11a9e286137minfrin || strncmp(variant->mime_type, "text/html", 9))));
54d22ed1c429b903b029bbd62621f11a9e286137minfrin}
5aa455d45abacfa675c88d4ff53fbe97c44ce545bnicholes
5aa455d45abacfa675c88d4ff53fbe97c44ce545bnicholesstatic int best_match(negotiation_state *neg, var_rec **pbest)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes{
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes int j;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes var_rec *best;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes float bestq = 0.0f;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin enum algorithm_results algorithm_result;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin int may_discard = 0;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* fetch request dependent variables
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * prefer-language: prefer a certain language.
54d22ed1c429b903b029bbd62621f11a9e286137minfrin */
e8f95a682820a599fe41b22977010636be5c2717jim const char *preferred_language = apr_table_get(neg->r->subprocess_env,
e8f95a682820a599fe41b22977010636be5c2717jim "prefer-language");
e8f95a682820a599fe41b22977010636be5c2717jim
e8f95a682820a599fe41b22977010636be5c2717jim /* no-gzip: do not send encoded documents */
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe if (apr_table_get(neg->r->subprocess_env, "no-gzip")) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes may_discard = DISCARD_ALL_ENCODINGS;
ae1981fc94adf2b231e2d0e15d2f895b2138c969covener }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* gzip-only-text/html: send encoded documents only
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * if they are text/html. (no-gzip has a higher priority).
d5cff0d8e871bf2528aadd8736fb50dc044b1e6dbnicholes */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin else {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin const char *env_value = apr_table_get(neg->r->subprocess_env,
54d22ed1c429b903b029bbd62621f11a9e286137minfrin "gzip-only-text/html");
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if (env_value && !strcmp(env_value, "1")) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin may_discard = DISCARD_ALL_BUT_HTML;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin }
ae1981fc94adf2b231e2d0e15d2f895b2138c969covener }
ae1981fc94adf2b231e2d0e15d2f895b2138c969covener
ae1981fc94adf2b231e2d0e15d2f895b2138c969covener set_default_lang_quality(neg);
e8f95a682820a599fe41b22977010636be5c2717jim
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe /*
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * Find the 'best' variant
ae1981fc94adf2b231e2d0e15d2f895b2138c969covener * We run the loop possibly twice: if "prefer-language"
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * environment variable is set but we did not find an appropriate
09338db7fdcf82ecc189195347da3a3ed5d0287abnicholes * best variant. In that case forget the preferred language and
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * negotiate over all variants.
54d22ed1c429b903b029bbd62621f11a9e286137minfrin */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin do {
ae1981fc94adf2b231e2d0e15d2f895b2138c969covener best = NULL;
ae1981fc94adf2b231e2d0e15d2f895b2138c969covener
54d22ed1c429b903b029bbd62621f11a9e286137minfrin for (j = 0; j < neg->avail_vars->nelts; ++j) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin var_rec *variant = &avail_recs[j];
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* if this variant is encoded somehow and there are special
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * variables set, we do not negotiate it. see above.
54d22ed1c429b903b029bbd62621f11a9e286137minfrin */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if ( may_discard
54d22ed1c429b903b029bbd62621f11a9e286137minfrin && discard_variant_by_env(variant, may_discard)) {
796e4a7141265d8ed7036e4628161c6eafb2a789jorton continue;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* if a language is preferred, but the current variant
ae1981fc94adf2b231e2d0e15d2f895b2138c969covener * is not in that language, then drop it for now
ae1981fc94adf2b231e2d0e15d2f895b2138c969covener */
e8f95a682820a599fe41b22977010636be5c2717jim if ( preferred_language
54d22ed1c429b903b029bbd62621f11a9e286137minfrin && !variant_has_language(variant, preferred_language)) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin continue;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* Find all the relevant 'quality' values from the
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * Accept... headers, and store in the variant. This also
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * prepares for sending an Alternates header etc so we need to
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * do it even if we do not actually plan to find a best
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * variant.
e8f95a682820a599fe41b22977010636be5c2717jim */
e8f95a682820a599fe41b22977010636be5c2717jim set_accept_quality(neg, variant);
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe /* accept the preferred language, even when it's not listed within
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * the Accept-Language header
54d22ed1c429b903b029bbd62621f11a9e286137minfrin */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if (preferred_language) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin variant->lang_quality = 1.0f;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin variant->definite = 1;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes else {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin set_language_quality(neg, variant);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin set_encoding_quality(neg, variant);
54d22ed1c429b903b029bbd62621f11a9e286137minfrin set_charset_quality(neg, variant);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* Only do variant selection if we may actually choose a
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * variant for the client
54d22ed1c429b903b029bbd62621f11a9e286137minfrin */
e8f95a682820a599fe41b22977010636be5c2717jim if (neg->may_choose) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* Now find out if this variant is better than the current
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * best, either using the RVSA/1.0 algorithm, or Apache's
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * internal server-driven algorithm. Presumably other
e8f95a682820a599fe41b22977010636be5c2717jim * server-driven algorithms are possible, and could be
e8f95a682820a599fe41b22977010636be5c2717jim * implemented here.
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if (neg->use_rvsa) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if (is_variant_better_rvsa(neg, variant, best, &bestq)) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin best = variant;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
796e4a7141265d8ed7036e4628161c6eafb2a789jorton }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin else {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if (is_variant_better(neg, variant, best, &bestq)) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes best = variant;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* We now either have a best variant, or no best variant */
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe if (neg->use_rvsa) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* calculate result for RVSA/1.0 algorithm:
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * only a choice response if the best variant has q>0
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * and is definite
54d22ed1c429b903b029bbd62621f11a9e286137minfrin */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin algorithm_result = (best && best->definite) && (bestq > 0) ?
54d22ed1c429b903b029bbd62621f11a9e286137minfrin alg_choice : alg_list;
54d22ed1c429b903b029bbd62621f11a9e286137minfrin }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin else {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* calculate result for Apache negotiation algorithm */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes algorithm_result = bestq > 0 ? alg_choice : alg_list;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
e8f95a682820a599fe41b22977010636be5c2717jim
e8f95a682820a599fe41b22977010636be5c2717jim /* run the loop again, if the "prefer-language" got no clear result */
141e1368614dc7564e1627671361b01b4869b491bnicholes if (preferred_language && (!best || algorithm_result != alg_choice)) {
141e1368614dc7564e1627671361b01b4869b491bnicholes preferred_language = NULL;
141e1368614dc7564e1627671361b01b4869b491bnicholes continue;
141e1368614dc7564e1627671361b01b4869b491bnicholes }
141e1368614dc7564e1627671361b01b4869b491bnicholes
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes break;
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes } while (1);
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes /* Returning a choice response with a non-neighboring variant is a
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes * protocol security error in TCN (see rfc2295). We do *not*
141e1368614dc7564e1627671361b01b4869b491bnicholes * verify here that the variant and URI are neighbors, even though
796e4a7141265d8ed7036e4628161c6eafb2a789jorton * we may return alg_choice. We depend on the environment (the
e8f95a682820a599fe41b22977010636be5c2717jim * caller) to only declare the resource transparently negotiable if
141e1368614dc7564e1627671361b01b4869b491bnicholes * all variants are neighbors.
141e1368614dc7564e1627671361b01b4869b491bnicholes */
141e1368614dc7564e1627671361b01b4869b491bnicholes *pbest = best;
141e1368614dc7564e1627671361b01b4869b491bnicholes return algorithm_result;
141e1368614dc7564e1627671361b01b4869b491bnicholes}
141e1368614dc7564e1627671361b01b4869b491bnicholes
141e1368614dc7564e1627671361b01b4869b491bnicholes/* Sets response headers for a negotiated response.
141e1368614dc7564e1627671361b01b4869b491bnicholes * neg->is_transparent determines whether a transparently negotiated
e8f95a682820a599fe41b22977010636be5c2717jim * response or a plain `server driven negotiation' response is
e8f95a682820a599fe41b22977010636be5c2717jim * created. Applicable headers are Alternates, Vary, and TCN.
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe *
0894cd17fe3f614bf01dbf84f1414756bd5a34c3bnicholes * The Vary header we create is sometimes longer than is required for
465bb68501690d7a47bfd2a6129580047d76d8f1rederpj * the correct caching of negotiated results by HTTP/1.1 caches. For
e8f95a682820a599fe41b22977010636be5c2717jim * example if we have 3 variants x.html, x.ps.en and x.ps.nl, and if
e8f95a682820a599fe41b22977010636be5c2717jim * the Accept: header assigns a 0 quality to .ps, then the results of
43c3e6a4b559b76b750c245ee95e2782c15b4296jim * the two server-side negotiation algorithms we currently implement
465bb68501690d7a47bfd2a6129580047d76d8f1rederpj * will never depend on Accept-Language so we could return `Vary:
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes * negotiate, accept' instead of the longer 'Vary: negotiate, accept,
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes * accept-language' which the code below will return. A routine for
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes * computing the exact minimal Vary header would be a huge pain to code
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes * and maintain though, especially because we need to take all possible
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes * twiddles in the server-side negotiation algorithms into account.
0894cd17fe3f614bf01dbf84f1414756bd5a34c3bnicholes */
1c9fe70e77b36d36ae34997fe25fe47beacf8709bnicholesstatic void set_neg_headers(request_rec *r, negotiation_state *neg,
0894cd17fe3f614bf01dbf84f1414756bd5a34c3bnicholes int alg_result)
0894cd17fe3f614bf01dbf84f1414756bd5a34c3bnicholes{
e8f95a682820a599fe41b22977010636be5c2717jim apr_table_t *hdrs;
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf const char *sample_type = NULL;
1c9fe70e77b36d36ae34997fe25fe47beacf8709bnicholes const char *sample_language = NULL;
4990e910dd8574b9b40beda0cf1aaa72334d6837bnicholes const char *sample_encoding = NULL;
d0fb19f6ddefe0d1b3c94eee44776a848ab0690drpluem const char *sample_charset = NULL;
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe char *lang;
1c9fe70e77b36d36ae34997fe25fe47beacf8709bnicholes char *qstr;
0894cd17fe3f614bf01dbf84f1414756bd5a34c3bnicholes apr_off_t len;
0894cd17fe3f614bf01dbf84f1414756bd5a34c3bnicholes apr_array_header_t *arr;
0894cd17fe3f614bf01dbf84f1414756bd5a34c3bnicholes int max_vlist_array = (neg->avail_vars->nelts * 21);
0894cd17fe3f614bf01dbf84f1414756bd5a34c3bnicholes int first_variant = 1;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes int vary_by_type = 0;
9ad7b260be233be7d7b5576979825cac72e15498rederpj int vary_by_language = 0;
9ad7b260be233be7d7b5576979825cac72e15498rederpj int vary_by_charset = 0;
9ad7b260be233be7d7b5576979825cac72e15498rederpj int vary_by_encoding = 0;
9ad7b260be233be7d7b5576979825cac72e15498rederpj int j;
9ad7b260be233be7d7b5576979825cac72e15498rederpj
9ad7b260be233be7d7b5576979825cac72e15498rederpj /* In order to avoid O(n^2) memory copies in building Alternates,
9ad7b260be233be7d7b5576979825cac72e15498rederpj * we preallocate a apr_table_t with the maximum substrings possible,
cb304b0c01e893dc2c24ee83ca382d8a6861d0e4trawick * fill it with the variant list, and then concatenate the entire array.
128a5d93141a86e3afa151e921035a07297c9833rederpj * Note that if you change the number of substrings pushed, you also
9ad7b260be233be7d7b5576979825cac72e15498rederpj * need to change the calculation of max_vlist_array above.
9ad7b260be233be7d7b5576979825cac72e15498rederpj */
9ad7b260be233be7d7b5576979825cac72e15498rederpj if (neg->send_alternates && neg->avail_vars->nelts)
9ad7b260be233be7d7b5576979825cac72e15498rederpj arr = apr_array_make(r->pool, max_vlist_array, sizeof(char *));
9ad7b260be233be7d7b5576979825cac72e15498rederpj else
9ad7b260be233be7d7b5576979825cac72e15498rederpj arr = NULL;
0f60998368b493f90120180a93fc2e1e74490872covener
0f60998368b493f90120180a93fc2e1e74490872covener /* Put headers into err_headers_out, since send_http_header()
0f60998368b493f90120180a93fc2e1e74490872covener * outputs both headers_out and err_headers_out.
0f60998368b493f90120180a93fc2e1e74490872covener */
0f60998368b493f90120180a93fc2e1e74490872covener hdrs = r->err_headers_out;
0f60998368b493f90120180a93fc2e1e74490872covener
0f60998368b493f90120180a93fc2e1e74490872covener for (j = 0; j < neg->avail_vars->nelts; ++j) {
0f60998368b493f90120180a93fc2e1e74490872covener var_rec *variant = &avail_recs[j];
0f60998368b493f90120180a93fc2e1e74490872covener
0f60998368b493f90120180a93fc2e1e74490872covener if (variant->content_languages && variant->content_languages->nelts) {
0f60998368b493f90120180a93fc2e1e74490872covener lang = apr_array_pstrcat(r->pool, variant->content_languages, ',');
0f60998368b493f90120180a93fc2e1e74490872covener }
0f60998368b493f90120180a93fc2e1e74490872covener else {
0f60998368b493f90120180a93fc2e1e74490872covener lang = NULL;
0f60998368b493f90120180a93fc2e1e74490872covener }
0f60998368b493f90120180a93fc2e1e74490872covener
0f60998368b493f90120180a93fc2e1e74490872covener /* Calculate Vary by looking for any difference between variants */
0f60998368b493f90120180a93fc2e1e74490872covener
0f60998368b493f90120180a93fc2e1e74490872covener if (first_variant) {
0f60998368b493f90120180a93fc2e1e74490872covener sample_type = variant->mime_type;
9ad7b260be233be7d7b5576979825cac72e15498rederpj sample_charset = variant->content_charset;
9ad7b260be233be7d7b5576979825cac72e15498rederpj sample_language = lang;
9ad7b260be233be7d7b5576979825cac72e15498rederpj sample_encoding = variant->content_encoding;
9ad7b260be233be7d7b5576979825cac72e15498rederpj }
9ad7b260be233be7d7b5576979825cac72e15498rederpj else {
9ad7b260be233be7d7b5576979825cac72e15498rederpj if (!vary_by_type &&
9ad7b260be233be7d7b5576979825cac72e15498rederpj strcmp(sample_type ? sample_type : "",
9ad7b260be233be7d7b5576979825cac72e15498rederpj variant->mime_type ? variant->mime_type : "")) {
67c5e190a7b84528b8895d7ef7be81712d206805covener vary_by_type = 1;
67c5e190a7b84528b8895d7ef7be81712d206805covener }
67c5e190a7b84528b8895d7ef7be81712d206805covener if (!vary_by_charset &&
67c5e190a7b84528b8895d7ef7be81712d206805covener strcmp(sample_charset ? sample_charset : "",
9ad7b260be233be7d7b5576979825cac72e15498rederpj variant->content_charset ?
9ad7b260be233be7d7b5576979825cac72e15498rederpj variant->content_charset : "")) {
9ad7b260be233be7d7b5576979825cac72e15498rederpj vary_by_charset = 1;
9ad7b260be233be7d7b5576979825cac72e15498rederpj }
9ad7b260be233be7d7b5576979825cac72e15498rederpj if (!vary_by_language &&
9ad7b260be233be7d7b5576979825cac72e15498rederpj strcmp(sample_language ? sample_language : "",
9ad7b260be233be7d7b5576979825cac72e15498rederpj lang ? lang : "")) {
9ad7b260be233be7d7b5576979825cac72e15498rederpj vary_by_language = 1;
9ad7b260be233be7d7b5576979825cac72e15498rederpj }
9ad7b260be233be7d7b5576979825cac72e15498rederpj if (!vary_by_encoding &&
9ad7b260be233be7d7b5576979825cac72e15498rederpj strcmp(sample_encoding ? sample_encoding : "",
128a5d93141a86e3afa151e921035a07297c9833rederpj variant->content_encoding ?
ae1981fc94adf2b231e2d0e15d2f895b2138c969covener variant->content_encoding : "")) {
128a5d93141a86e3afa151e921035a07297c9833rederpj vary_by_encoding = 1;
87587593f1a53030e840acc0dec6cc881022ea40covener }
9ad7b260be233be7d7b5576979825cac72e15498rederpj }
9ad7b260be233be7d7b5576979825cac72e15498rederpj first_variant = 0;
9ad7b260be233be7d7b5576979825cac72e15498rederpj
9ad7b260be233be7d7b5576979825cac72e15498rederpj if (!neg->send_alternates)
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf continue;
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf /* Generate the string components for this Alternates entry */
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf *((const char **) apr_array_push(arr)) = "{\"";
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf *((const char **) apr_array_push(arr)) = variant->file_name;
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf *((const char **) apr_array_push(arr)) = "\" ";
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf qstr = (char *) apr_palloc(r->pool, 6);
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf apr_snprintf(qstr, 6, "%1.3f", variant->source_quality);
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf /* Strip trailing zeros (saves those valuable network bytes) */
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf if (qstr[4] == '0') {
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf qstr[4] = '\0';
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf if (qstr[3] == '0') {
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf qstr[3] = '\0';
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf if (qstr[2] == '0') {
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf qstr[1] = '\0';
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf }
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf }
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf }
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf *((const char **) apr_array_push(arr)) = qstr;
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf if (variant->mime_type && *variant->mime_type) {
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf *((const char **) apr_array_push(arr)) = " {type ";
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf *((const char **) apr_array_push(arr)) = variant->mime_type;
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf *((const char **) apr_array_push(arr)) = "}";
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf }
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf if (variant->content_charset && *variant->content_charset) {
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf *((const char **) apr_array_push(arr)) = " {charset ";
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf *((const char **) apr_array_push(arr)) = variant->content_charset;
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf *((const char **) apr_array_push(arr)) = "}";
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf }
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf if (lang) {
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf *((const char **) apr_array_push(arr)) = " {language ";
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf *((const char **) apr_array_push(arr)) = lang;
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf *((const char **) apr_array_push(arr)) = "}";
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf }
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf if (variant->content_encoding && *variant->content_encoding) {
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf /* Strictly speaking, this is non-standard, but so is TCN */
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf *((const char **) apr_array_push(arr)) = " {encoding ";
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf *((const char **) apr_array_push(arr)) = variant->content_encoding;
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf *((const char **) apr_array_push(arr)) = "}";
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf }
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf /* Note that the Alternates specification (in rfc2295) does
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf * not require that we include {length x}, so we could omit it
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener * if determining the length is too expensive. We currently
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener * always include it though.
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener *
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener * If the variant is a CGI script, find_content_length would
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener * return the length of the script, not the output it
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener * produces, so we check for the presence of a handler and if
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener * there is one we don't add a length.
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener *
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener * XXX: TODO: This check does not detect a CGI script if we
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener * get the variant from a type map. This needs to be fixed
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener * (without breaking things if the type map specifies a
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener * content-length, which currently leads to the correct result).
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf */
0972df03482b520989d4adbf12626755aef24907covener if (!(variant->sub_req && variant->sub_req->handler)
0972df03482b520989d4adbf12626755aef24907covener && (len = find_content_length(neg, variant)) >= 0) {
0972df03482b520989d4adbf12626755aef24907covener
0972df03482b520989d4adbf12626755aef24907covener *((const char **) apr_array_push(arr)) = " {length ";
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener *((const char **) apr_array_push(arr)) = apr_off_t_toa(r->pool,
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener len);
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener *((const char **) apr_array_push(arr)) = "}";
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener }
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener
9ad7b260be233be7d7b5576979825cac72e15498rederpj *((const char **) apr_array_push(arr)) = "}";
8113dac419143273351446c3ad653f3fe5ba5cfdwrowe *((const char **) apr_array_push(arr)) = ", "; /* trimmed below */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
e8f95a682820a599fe41b22977010636be5c2717jim
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (neg->send_alternates && neg->avail_vars->nelts) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes arr->nelts--; /* remove last comma */
a0b44ec81b32ffb946537d4e43e1c3a3f7594137gregames apr_table_mergen(hdrs, "Alternates",
1233ffe2092021833c2a642f1017d6c332075469gregames apr_array_pstrcat(r->pool, arr, '\0'));
1233ffe2092021833c2a642f1017d6c332075469gregames }
a0b44ec81b32ffb946537d4e43e1c3a3f7594137gregames
490046d2a164ad86cc63bd789f32eaed663d239abnicholes if (neg->is_transparent || vary_by_type || vary_by_language ||
490046d2a164ad86cc63bd789f32eaed663d239abnicholes vary_by_language || vary_by_charset || vary_by_encoding) {
490046d2a164ad86cc63bd789f32eaed663d239abnicholes
490046d2a164ad86cc63bd789f32eaed663d239abnicholes apr_table_mergen(hdrs, "Vary", 2 + apr_pstrcat(r->pool,
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes neg->is_transparent ? ", negotiate" : "",
ecc6e723b804fb4b8f858910eff3f88242ec56fasf vary_by_type ? ", accept" : "",
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes vary_by_language ? ", accept-language" : "",
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes vary_by_charset ? ", accept-charset" : "",
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes vary_by_encoding ? ", accept-encoding" : "", NULL));
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
54d22ed1c429b903b029bbd62621f11a9e286137minfrin if (neg->is_transparent) { /* Create TCN response header */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin apr_table_setn(hdrs, "TCN",
54d22ed1c429b903b029bbd62621f11a9e286137minfrin alg_result == alg_list ? "list" : "choice");
54d22ed1c429b903b029bbd62621f11a9e286137minfrin }
0894cd17fe3f614bf01dbf84f1414756bd5a34c3bnicholes}
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf/**********************************************************************
141e1368614dc7564e1627671361b01b4869b491bnicholes *
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener * Return an HTML list of variants. This is output as part of the
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * choice response or 406 status body.
54d22ed1c429b903b029bbd62621f11a9e286137minfrin */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrinstatic char *make_variant_list(request_rec *r, negotiation_state *neg)
f2386b627177c7a80d38fed6ec0aed3c086909c1covener{
f2386b627177c7a80d38fed6ec0aed3c086909c1covener apr_array_header_t *arr;
f2386b627177c7a80d38fed6ec0aed3c086909c1covener int i;
e8f95a682820a599fe41b22977010636be5c2717jim int max_vlist_array = (neg->avail_vars->nelts * 15) + 2;
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* In order to avoid O(n^2) memory copies in building the list,
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * we preallocate a apr_table_t with the maximum substrings possible,
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * fill it with the variant list, and then concatenate the entire array.
54d22ed1c429b903b029bbd62621f11a9e286137minfrin */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin arr = apr_array_make(r->pool, max_vlist_array, sizeof(char *));
9c247afacdbb56fa794715f2ac218eb1cae39fe8bnicholes
490046d2a164ad86cc63bd789f32eaed663d239abnicholes *((const char **) apr_array_push(arr)) = "Available variants:\n<ul>\n";
9c247afacdbb56fa794715f2ac218eb1cae39fe8bnicholes
490046d2a164ad86cc63bd789f32eaed663d239abnicholes for (i = 0; i < neg->avail_vars->nelts; ++i) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin var_rec *variant = &((var_rec *) neg->avail_vars->elts)[i];
9c247afacdbb56fa794715f2ac218eb1cae39fe8bnicholes const char *filename = variant->file_name ? variant->file_name : "";
9c247afacdbb56fa794715f2ac218eb1cae39fe8bnicholes apr_array_header_t *languages = variant->content_languages;
9c247afacdbb56fa794715f2ac218eb1cae39fe8bnicholes const char *description = variant->description
54d22ed1c429b903b029bbd62621f11a9e286137minfrin ? variant->description
54d22ed1c429b903b029bbd62621f11a9e286137minfrin : "";
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin /* The format isn't very neat, and it would be nice to make
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * the tags human readable (eg replace 'language en' with 'English').
9ab5933c174cb21de69e8305f80544cbe7ed2a7ccovener * Note that if you change the number of substrings pushed, you also
9c247afacdbb56fa794715f2ac218eb1cae39fe8bnicholes * need to change the calculation of max_vlist_array above.
9c247afacdbb56fa794715f2ac218eb1cae39fe8bnicholes */
9c247afacdbb56fa794715f2ac218eb1cae39fe8bnicholes *((const char **) apr_array_push(arr)) = "<li><a href=\"";
e8f95a682820a599fe41b22977010636be5c2717jim *((const char **) apr_array_push(arr)) = filename;
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe *((const char **) apr_array_push(arr)) = "\">";
e8f95a682820a599fe41b22977010636be5c2717jim *((const char **) apr_array_push(arr)) = filename;
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe *((const char **) apr_array_push(arr)) = "</a> ";
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes *((const char **) apr_array_push(arr)) = description;
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes if (variant->mime_type && *variant->mime_type) {
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes *((const char **) apr_array_push(arr)) = ", type ";
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes *((const char **) apr_array_push(arr)) = variant->mime_type;
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes }
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes if (languages && languages->nelts) {
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes *((const char **) apr_array_push(arr)) = ", language ";
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes *((const char **) apr_array_push(arr)) = apr_array_pstrcat(r->pool,
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes languages, ',');
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes }
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes if (variant->content_charset && *variant->content_charset) {
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf *((const char **) apr_array_push(arr)) = ", charset ";
cbbfa8f4222ecc7b2e0f16db3032d0118d6a0accbnicholes *((const char **) apr_array_push(arr)) = variant->content_charset;
0f60998368b493f90120180a93fc2e1e74490872covener }
9c247afacdbb56fa794715f2ac218eb1cae39fe8bnicholes if (variant->content_encoding) {
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener *((const char **) apr_array_push(arr)) = ", encoding ";
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener *((const char **) apr_array_push(arr)) = variant->content_encoding;
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes *((const char **) apr_array_push(arr)) = "</li>\n";
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes *((const char **) apr_array_push(arr)) = "</ul>\n";
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return apr_array_pstrcat(r->pool, arr, '\0');
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes}
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholesstatic void store_variant_list(request_rec *r, negotiation_state *neg)
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes{
e8f95a682820a599fe41b22977010636be5c2717jim if (r->main == NULL) {
54d22ed1c429b903b029bbd62621f11a9e286137minfrin apr_table_setn(r->notes, "variant-list", make_variant_list(r, neg));
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes else {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes apr_table_setn(r->main->notes, "variant-list",
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes make_variant_list(r->main, neg));
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes}
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
11f2c481e1d57bedb3f758565307501e9a2730ddtrawick/* Called if we got a "Choice" response from the variant selection algorithm.
11f2c481e1d57bedb3f758565307501e9a2730ddtrawick * It checks the result of the chosen variant to see if it
11f2c481e1d57bedb3f758565307501e9a2730ddtrawick * is itself negotiated (if so, return error HTTP_VARIANT_ALSO_VARIES).
11f2c481e1d57bedb3f758565307501e9a2730ddtrawick * Otherwise, add the appropriate headers to the current response.
11f2c481e1d57bedb3f758565307501e9a2730ddtrawick */
43997561b2302d13dee973998e77743a3ddd2374trawick
11f2c481e1d57bedb3f758565307501e9a2730ddtrawickstatic int setup_choice_response(request_rec *r, negotiation_state *neg,
11f2c481e1d57bedb3f758565307501e9a2730ddtrawick var_rec *variant)
11f2c481e1d57bedb3f758565307501e9a2730ddtrawick{
11f2c481e1d57bedb3f758565307501e9a2730ddtrawick request_rec *sub_req;
11f2c481e1d57bedb3f758565307501e9a2730ddtrawick const char *sub_vary;
11f2c481e1d57bedb3f758565307501e9a2730ddtrawick
11f2c481e1d57bedb3f758565307501e9a2730ddtrawick if (!variant->sub_req) {
11f2c481e1d57bedb3f758565307501e9a2730ddtrawick int status;
e8f95a682820a599fe41b22977010636be5c2717jim
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes sub_req = ap_sub_req_lookup_file(variant->file_name, r, NULL);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes status = sub_req->status;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (status != HTTP_OK &&
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes !apr_table_get(sub_req->err_headers_out, "TCN")) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes ap_destroy_sub_req(sub_req);
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe return status;
e8f95a682820a599fe41b22977010636be5c2717jim }
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe variant->sub_req = sub_req;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5cff0d8e871bf2528aadd8736fb50dc044b1e6dbnicholes else {
d5cff0d8e871bf2528aadd8736fb50dc044b1e6dbnicholes sub_req = variant->sub_req;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* The variant selection algorithm told us to return a "Choice"
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * response. This is the normal variant response, with
59d316b83d42d2a07e25c20d8c35a07b369618bdsf * some extra headers. First, ensure that the chosen
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * variant did or will not itself engage in transparent negotiation.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * If not, set the appropriate headers, and fall through to
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * the normal variant handling
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
490046d2a164ad86cc63bd789f32eaed663d239abnicholes /* This catches the error that a transparent type map selects a
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * transparent multiviews resource as the best variant.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes *
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * XXX: We do not signal an error if a transparent type map
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * selects a _non_transparent multiviews resource as the best
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * variant, because we can generate a legal negotiation response
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * in this case. In this case, the vlist_validator of the
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * nontransparent subrequest will be lost however. This could
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * lead to cases in which a change in the set of variants or the
e8f95a682820a599fe41b22977010636be5c2717jim * negotiation algorithm of the nontransparent resource is never
e8f95a682820a599fe41b22977010636be5c2717jim * propagated up to a HTTP/1.1 cache which interprets Vary. To be
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * completely on the safe side we should return HTTP_VARIANT_ALSO_VARIES
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * for this type of recursive negotiation too.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (neg->is_transparent &&
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes apr_table_get(sub_req->err_headers_out, "TCN")) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return HTTP_VARIANT_ALSO_VARIES;
e70cf415769ad0b3704e98b3f6da38e916ff7228jorton }
e70cf415769ad0b3704e98b3f6da38e916ff7228jorton
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* This catches the error that a transparent type map recursively
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * selects, as the best variant, another type map which itself
024e70e05386a6367eb45d0d1cc406764520ff4cwrowe * causes transparent negotiation to be done.
43997561b2302d13dee973998e77743a3ddd2374trawick *
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * XXX: Actually, we catch this error by catching all cases of
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * type map recursion. There are some borderline recursive type
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * map arrangements which would not produce transparent
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * negotiation protocol errors or lack of cache propagation
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * problems, but such arrangements are very hard to detect at this
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * point in the control flow, so we do not bother to single them
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * out.
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe *
e8f95a682820a599fe41b22977010636be5c2717jim * Recursive type maps imply a recursive arrangement of negotiated
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * resources which is visible to outside clients, and this is not
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * supported by the transparent negotiation caching protocols, so
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * if we are to have generic support for recursive type maps, we
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * have to create some configuration setting which makes all type
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * maps non-transparent when recursion is enabled. Also, if we
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * want recursive type map support which ensures propagation of
b26e0fd306aeae8a51efc1a57d1e9f42dc37a3f1covener * type map changes into HTTP/1.1 caches that handle Vary, we
e8f95a682820a599fe41b22977010636be5c2717jim * would have to extend the current mechanism for generating
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * variant list validators.
e8f95a682820a599fe41b22977010636be5c2717jim */
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe if (sub_req->handler && strcmp(sub_req->handler, "type-map") == 0) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return HTTP_VARIANT_ALSO_VARIES;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* This adds an appropriate Variant-Vary header if the subrequest
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * is a multiviews resource.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes *
e8f95a682820a599fe41b22977010636be5c2717jim * XXX: TODO: Note that this does _not_ handle any Vary header
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * returned by a CGI if sub_req is a CGI script, because we don't
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * see that Vary header yet at this point in the control flow.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * This won't cause any cache consistency problems _unless_ the
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * CGI script also returns a Cache-Control header marking the
e8f95a682820a599fe41b22977010636be5c2717jim * response as cachable. This needs to be fixed, also there are
e8f95a682820a599fe41b22977010636be5c2717jim * problems if a CGI returns an Etag header which also need to be
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * fixed.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if ((sub_vary = apr_table_get(sub_req->err_headers_out, "Vary")) != NULL) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes apr_table_setn(r->err_headers_out, "Variant-Vary", sub_vary);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
e70cf415769ad0b3704e98b3f6da38e916ff7228jorton /* Move the subreq Vary header into the main request to
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * prevent having two Vary headers in the response, which
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * would be legal but strange.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes apr_table_setn(r->err_headers_out, "Vary", sub_vary);
e8f95a682820a599fe41b22977010636be5c2717jim apr_table_unset(sub_req->err_headers_out, "Vary");
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin apr_table_setn(r->err_headers_out, "Content-Location",
54d22ed1c429b903b029bbd62621f11a9e286137minfrin apr_pstrdup(r->pool, variant->file_name));
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrin set_neg_headers(r, neg, alg_choice); /* add Alternates and Vary */
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
e8f95a682820a599fe41b22977010636be5c2717jim /* Still to do by caller: add Expires */
e8f95a682820a599fe41b22977010636be5c2717jim
e8f95a682820a599fe41b22977010636be5c2717jim return 0;
d5cff0d8e871bf2528aadd8736fb50dc044b1e6dbnicholes}
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
ebe5305f8b22507374358f32b74d12fb50c05a25covener/****************************************************************
54d22ed1c429b903b029bbd62621f11a9e286137minfrin *
54d22ed1c429b903b029bbd62621f11a9e286137minfrin * Executive...
54d22ed1c429b903b029bbd62621f11a9e286137minfrin */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin
54d22ed1c429b903b029bbd62621f11a9e286137minfrinstatic int do_negotiation(request_rec *r, negotiation_state *neg,
e70cf415769ad0b3704e98b3f6da38e916ff7228jorton var_rec **bestp, int prefer_scripts)
e70cf415769ad0b3704e98b3f6da38e916ff7228jorton{
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes int alg_result; /* result of variant selection algorithm */
54d22ed1c429b903b029bbd62621f11a9e286137minfrin int res;
e8f95a682820a599fe41b22977010636be5c2717jim int j;
e70cf415769ad0b3704e98b3f6da38e916ff7228jorton
e70cf415769ad0b3704e98b3f6da38e916ff7228jorton /* Decide if resource is transparently negotiable */
e70cf415769ad0b3704e98b3f6da38e916ff7228jorton
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* GET or HEAD? (HEAD has same method number as GET) */
0894cd17fe3f614bf01dbf84f1414756bd5a34c3bnicholes if (r->method_number == M_GET) {
f7edd56768a46a01ae8b43c712c9eeef40e37f59bnicholes
f7edd56768a46a01ae8b43c712c9eeef40e37f59bnicholes /* maybe this should be configurable, see also the comment
f7edd56768a46a01ae8b43c712c9eeef40e37f59bnicholes * about recursive type maps in setup_choice_response()
57e892abdf632bb9224c6ae6a715d863242a00f3fuankg */
0f60998368b493f90120180a93fc2e1e74490872covener neg->is_transparent = 1;
0f60998368b493f90120180a93fc2e1e74490872covener
0f60998368b493f90120180a93fc2e1e74490872covener /* We can't be transparent if we are a map file in the middle
0f60998368b493f90120180a93fc2e1e74490872covener * of the request URI.
0f60998368b493f90120180a93fc2e1e74490872covener */
0f60998368b493f90120180a93fc2e1e74490872covener if (r->path_info && *r->path_info)
0f60998368b493f90120180a93fc2e1e74490872covener neg->is_transparent = 0;
0f60998368b493f90120180a93fc2e1e74490872covener
57e892abdf632bb9224c6ae6a715d863242a00f3fuankg for (j = 0; j < neg->avail_vars->nelts; ++j) {
0f60998368b493f90120180a93fc2e1e74490872covener var_rec *variant = &avail_recs[j];
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes /* We can't be transparent, because of internal
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * assumptions in best_match(), if there is a
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * non-neighboring variant. We can have a non-neighboring
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * variant when processing a type map.
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes */
e8f95a682820a599fe41b22977010636be5c2717jim if (ap_strchr_c(variant->file_name, '/'))
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe neg->is_transparent = 0;
d266c3777146d36a4c23c17aad6f153aebea1bf4jorton
d266c3777146d36a4c23c17aad6f153aebea1bf4jorton /* We can't be transparent, because of the behavior
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes * of variant typemap bodies.
e8f95a682820a599fe41b22977010636be5c2717jim */
11f2c481e1d57bedb3f758565307501e9a2730ddtrawick if (variant->body) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes neg->is_transparent = 0;
af6d8b02b2fd4ea9a0f6bfb95940d6075030e9a7minfrin }
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf }
9efd5708f6f0ab1992f6a5233eb622ad5e4eae18sf }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if (neg->is_transparent) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes parse_negotiate_header(r, neg);
e025b9c2dbc3e2873142a775c37336a75ead222bjorton }
e8f95a682820a599fe41b22977010636be5c2717jim else { /* configure negotiation on non-transparent resource */
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe neg->may_choose = 1;
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe }
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe maybe_add_default_accepts(neg, prefer_scripts);
e8f95a682820a599fe41b22977010636be5c2717jim
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe alg_result = best_match(neg, bestp);
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe /* alg_result is one of
e8f95a682820a599fe41b22977010636be5c2717jim * alg_choice: a best variant is chosen
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * alg_list: no best variant is chosen
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe */
023ab66b169a239c82d6c36410ab43fe63561d11covener
023ab66b169a239c82d6c36410ab43fe63561d11covener if (alg_result == alg_list) {
023ab66b169a239c82d6c36410ab43fe63561d11covener /* send a list response or HTTP_NOT_ACCEPTABLE error response */
e8f95a682820a599fe41b22977010636be5c2717jim
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe neg->send_alternates = 1; /* always include Alternates header */
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe set_neg_headers(r, neg, alg_result);
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe store_variant_list(r, neg);
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe if (neg->is_transparent && neg->ua_supports_trans) {
e8f95a682820a599fe41b22977010636be5c2717jim /* XXX todo: expires? cachability? */
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe /* Some HTTP/1.0 clients are known to choke when they get
023ab66b169a239c82d6c36410ab43fe63561d11covener * a 300 (multiple choices) response without a Location
023ab66b169a239c82d6c36410ab43fe63561d11covener * header. However the 300 code response we are are about
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * to generate will only reach 1.0 clients which support
e8f95a682820a599fe41b22977010636be5c2717jim * transparent negotiation, and they should be OK. The
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * response should never reach older 1.0 clients, even if
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * we have CacheNegotiatedDocs enabled, because no 1.0
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * proxy cache (we know of) will cache and return 300
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * responses (they certainly won't if they conform to the
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * HTTP/1.0 specification).
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe */
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe return HTTP_MULTIPLE_CHOICES;
c99be08e59d6c21cdc4aa79f645feec41e157b09rederpj }
c99be08e59d6c21cdc4aa79f645feec41e157b09rederpj
c99be08e59d6c21cdc4aa79f645feec41e157b09rederpj if (!*bestp) {
c99be08e59d6c21cdc4aa79f645feec41e157b09rederpj ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
c99be08e59d6c21cdc4aa79f645feec41e157b09rederpj "no acceptable variant: %s", r->filename);
c99be08e59d6c21cdc4aa79f645feec41e157b09rederpj return HTTP_NOT_ACCEPTABLE;
c99be08e59d6c21cdc4aa79f645feec41e157b09rederpj }
c99be08e59d6c21cdc4aa79f645feec41e157b09rederpj }
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe /* Variant selection chose a variant */
ae1981fc94adf2b231e2d0e15d2f895b2138c969covener
c99be08e59d6c21cdc4aa79f645feec41e157b09rederpj /* XXX todo: merge the two cases in the if statement below */
c99be08e59d6c21cdc4aa79f645feec41e157b09rederpj if (neg->is_transparent) {
c99be08e59d6c21cdc4aa79f645feec41e157b09rederpj
c99be08e59d6c21cdc4aa79f645feec41e157b09rederpj if ((res = setup_choice_response(r, neg, *bestp)) != 0) {
c99be08e59d6c21cdc4aa79f645feec41e157b09rederpj return res; /* return if error */
c99be08e59d6c21cdc4aa79f645feec41e157b09rederpj }
c99be08e59d6c21cdc4aa79f645feec41e157b09rederpj }
c99be08e59d6c21cdc4aa79f645feec41e157b09rederpj else {
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe set_neg_headers(r, neg, alg_result);
e8f95a682820a599fe41b22977010636be5c2717jim }
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe /* Make sure caching works - Vary should handle HTTP/1.1, but for
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * HTTP/1.0, we can't allow caching at all.
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe */
e8f95a682820a599fe41b22977010636be5c2717jim
141e1368614dc7564e1627671361b01b4869b491bnicholes /* XXX: Note that we only set r->no_cache to 1, which causes
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * Expires: <now> to be added, when responding to a HTTP/1.0
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * client. If we return the response to a 1.1 client, we do not
8445dae5cc606ba8ba04efc341cc1e081d95920drpluem * add Expires <now>, because doing so would degrade 1.1 cache
141e1368614dc7564e1627671361b01b4869b491bnicholes * performance by preventing re-use of the response without prior
e8f95a682820a599fe41b22977010636be5c2717jim * revalidation. On the other hand, if the 1.1 client is a proxy
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * which was itself contacted by a 1.0 client, or a proxy cache
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * which can be contacted later by 1.0 clients, then we currently
482f676c6c19b1c5bb5cca04dad11509c1da3a4cwrowe * rely on this 1.1 proxy to add the Expires: <now> when it
0894cd17fe3f614bf01dbf84f1414756bd5a34c3bnicholes * forwards the response.
9ad7b260be233be7d7b5576979825cac72e15498rederpj *
9ad7b260be233be7d7b5576979825cac72e15498rederpj * XXX: TODO: Find out if the 1.1 spec requires proxies and
9ad7b260be233be7d7b5576979825cac72e15498rederpj * tunnels to add Expires: <now> when forwarding the response to
9ad7b260be233be7d7b5576979825cac72e15498rederpj * 1.0 clients. I (kh) recall it is rather vague on this point.
9ad7b260be233be7d7b5576979825cac72e15498rederpj * Testing actual 1.1 proxy implementations would also be nice. If
9ad7b260be233be7d7b5576979825cac72e15498rederpj * Expires: <now> is not added by proxies then we need to always
9ad7b260be233be7d7b5576979825cac72e15498rederpj * include Expires: <now> ourselves to ensure correct caching, but
87587593f1a53030e840acc0dec6cc881022ea40covener * this would degrade HTTP/1.1 cache efficiency unless we also add
9ad7b260be233be7d7b5576979825cac72e15498rederpj * Cache-Control: max-age=N, which we currently don't.
0f60998368b493f90120180a93fc2e1e74490872covener *
0f60998368b493f90120180a93fc2e1e74490872covener * Roy: No, we are not going to screw over HTTP future just to
0f60998368b493f90120180a93fc2e1e74490872covener * ensure that people who can't be bothered to upgrade their
0f60998368b493f90120180a93fc2e1e74490872covener * clients will always receive perfect server-side negotiation.
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf * Hell, those clients are sending bogus accept headers anyway.
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf *
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf * Manual setting of cache-control/expires always overrides this
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf * automated kluge, on purpose.
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener */
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener if ((!do_cache_negotiated_docs(r->server)
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener && (r->proto_num < HTTP_VERSION(1,1)))
a81c0c1ae464b2063a21b45f80c9da8d89bb840ecovener && neg->count_multiviews_variants != 1) {
4be9c459920a7c1cfe62d654327dae5c4bb6b284sf r->no_cache = 1;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return OK;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes}
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
f43b67c5a9d29b572eac916f8335cedc80c908bebnicholesstatic int handle_map_file(request_rec *r)
f43b67c5a9d29b572eac916f8335cedc80c908bebnicholes{
f43b67c5a9d29b572eac916f8335cedc80c908bebnicholes negotiation_state *neg;
f43b67c5a9d29b572eac916f8335cedc80c908bebnicholes apr_file_t *map;
f43b67c5a9d29b572eac916f8335cedc80c908bebnicholes var_rec *best;
f43b67c5a9d29b572eac916f8335cedc80c908bebnicholes int res;
f43b67c5a9d29b572eac916f8335cedc80c908bebnicholes char *udir;
f43b67c5a9d29b572eac916f8335cedc80c908bebnicholes
f43b67c5a9d29b572eac916f8335cedc80c908bebnicholes if(strcmp(r->handler,MAP_FILE_MAGIC_TYPE) && strcmp(r->handler,"type-map"))
3f5585f7f4a7d74f2f94ec729ea8c1879d419e35rederpj return DECLINED;
f43b67c5a9d29b572eac916f8335cedc80c908bebnicholes
11f2c481e1d57bedb3f758565307501e9a2730ddtrawick neg = parse_accept_headers(r);
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes if ((res = read_type_map(&map, neg, r))) {
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes return res;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes }
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes res = do_negotiation(r, neg, &best, 0);
36ef8f77bffe75d1aa327882be1b5bdbe2ff567asf if (res != 0) return res;
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes
9ad7b260be233be7d7b5576979825cac72e15498rederpj if (best->body)
f05787953018140838ad51456c86c965d6a86267jim {
f05787953018140838ad51456c86c965d6a86267jim conn_rec *c = r->connection;
f05787953018140838ad51456c86c965d6a86267jim apr_bucket_brigade *bb;
f05787953018140838ad51456c86c965d6a86267jim apr_bucket *e;
f05787953018140838ad51456c86c965d6a86267jim
d5b12fe8ae917e654a33247fd4e59dc9e75170aebnicholes ap_allow_standard_methods(r, REPLACE_ALLOW, M_GET, M_OPTIONS,
M_POST, -1);
/* XXX: ?
* if (r->method_number == M_OPTIONS) {
* return ap_send_http_options(r);
*}
*/
if (r->method_number != M_GET && r->method_number != M_POST) {
return HTTP_METHOD_NOT_ALLOWED;
}
/* ### These may be implemented by adding some 'extra' info
* of the file offset onto the etag
* ap_update_mtime(r, r->finfo.mtime);
* ap_set_last_modified(r);
* ap_set_etag(r);
*/
apr_table_setn(r->headers_out, "Accept-Ranges", "bytes");
ap_set_content_length(r, best->bytes);
/* set MIME type and charset as negotiated */
if (best->mime_type && *best->mime_type) {
if (best->content_charset && *best->content_charset) {
ap_set_content_type(r, apr_pstrcat(r->pool,
best->mime_type,
"; charset=",
best->content_charset,
NULL));
}
else {
ap_set_content_type(r, apr_pstrdup(r->pool, best->mime_type));
}
}
/* set Content-language(s) as negotiated */
if (best->content_languages && best->content_languages->nelts) {
r->content_languages = apr_array_copy(r->pool,
best->content_languages);
}
/* set Content-Encoding as negotiated */
if (best->content_encoding && *best->content_encoding) {
r->content_encoding = apr_pstrdup(r->pool,
best->content_encoding);
}
if ((res = ap_meets_conditions(r)) != OK) {
return res;
}
if ((res = ap_discard_request_body(r)) != OK) {
return res;
}
bb = apr_brigade_create(r->pool, c->bucket_alloc);
apr_brigade_insert_file(bb, map, best->body, best->bytes, r->pool);
e = apr_bucket_eos_create(c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, e);
return ap_pass_brigade(r->output_filters, bb);
}
if (r->path_info && *r->path_info) {
/* remove any path_info from the end of the uri before trying
* to change the filename. r->path_info from the original
* request is passed along on the redirect.
*/
r->uri[ap_find_path_info(r->uri, r->path_info)] = '\0';
}
udir = ap_make_dirstr_parent(r->pool, r->uri);
udir = ap_escape_uri(r->pool, udir);
ap_internal_redirect(apr_pstrcat(r->pool, udir, best->file_name,
r->path_info, NULL), r);
return OK;
}
static int handle_multi(request_rec *r)
{
negotiation_state *neg;
var_rec *best, *avail_recs;
request_rec *sub_req;
int res;
int j;
if (r->finfo.filetype != APR_NOFILE
|| !(ap_allow_options(r) & OPT_MULTI)) {
return DECLINED;
}
neg = parse_accept_headers(r);
if ((res = read_types_multi(neg))) {
return_from_multi:
/* free all allocated memory from subrequests */
avail_recs = (var_rec *) neg->avail_vars->elts;
for (j = 0; j < neg->avail_vars->nelts; ++j) {
var_rec *variant = &avail_recs[j];
if (variant->sub_req) {
ap_destroy_sub_req(variant->sub_req);
}
}
return res;
}
if (neg->avail_vars->nelts == 0) {
return DECLINED;
}
res = do_negotiation(r, neg, &best,
(r->method_number != M_GET) || r->args ||
(r->path_info && *r->path_info));
if (res != 0)
goto return_from_multi;
if (!(sub_req = best->sub_req)) {
/* We got this out of a map file, so we don't actually have
* a sub_req structure yet. Get one now.
*/
sub_req = ap_sub_req_lookup_file(best->file_name, r, NULL);
if (sub_req->status != HTTP_OK) {
res = sub_req->status;
ap_destroy_sub_req(sub_req);
goto return_from_multi;
}
}
/* now do a "fast redirect" ... promotes the sub_req into the main req */
ap_internal_fast_redirect(sub_req, r);
/* give no advise for time on this subrequest. Perhaps we
* should tally the last mtime amoung all variants, and date
* the most recent, but that could confuse the proxies.
*/
r->mtime = 0;
/* clean up all but our favorite variant, since that sub_req
* is now merged into the main request!
*/
avail_recs = (var_rec *) neg->avail_vars->elts;
for (j = 0; j < neg->avail_vars->nelts; ++j) {
var_rec *variant = &avail_recs[j];
if (variant != best && variant->sub_req) {
ap_destroy_sub_req(variant->sub_req);
}
}
return OK;
}
/**********************************************************************
* There is a problem with content-encoding, as some clients send and
* expect an x- token (e.g. x-gzip) while others expect the plain token
* (i.e. gzip). To try and deal with this as best as possible we do
* the following: if the client sent an Accept-Encoding header and it
* contains a plain token corresponding to the content encoding of the
* response, then set content encoding using the plain token. Else if
* the A-E header contains the x- token use the x- token in the C-E
* header. Else don't do anything.
*
* Note that if no A-E header was sent, or it does not contain a token
* compatible with the final content encoding, then the token in the
* C-E header will be whatever was specified in the AddEncoding
* directive.
*/
static int fix_encoding(request_rec *r)
{
const char *enc = r->content_encoding;
char *x_enc = NULL;
apr_array_header_t *accept_encodings;
accept_rec *accept_recs;
int i;
if (!enc || !*enc) {
return DECLINED;
}
if (enc[0] == 'x' && enc[1] == '-') {
enc += 2;
}
if ((accept_encodings = do_header_line(r->pool,
apr_table_get(r->headers_in, "Accept-Encoding"))) == NULL) {
return DECLINED;
}
accept_recs = (accept_rec *) accept_encodings->elts;
for (i = 0; i < accept_encodings->nelts; ++i) {
char *name = accept_recs[i].name;
if (!strcmp(name, enc)) {
r->content_encoding = name;
return OK;
}
if (name[0] == 'x' && name[1] == '-' && !strcmp(name+2, enc)) {
x_enc = name;
}
}
if (x_enc) {
r->content_encoding = x_enc;
return OK;
}
return DECLINED;
}
static void register_hooks(apr_pool_t *p)
{
ap_hook_fixups(fix_encoding,NULL,NULL,APR_HOOK_MIDDLE);
ap_hook_type_checker(handle_multi,NULL,NULL,APR_HOOK_FIRST);
ap_hook_handler(handle_map_file,NULL,NULL,APR_HOOK_MIDDLE);
}
module AP_MODULE_DECLARE_DATA negotiation_module =
{
STANDARD20_MODULE_STUFF,
create_neg_dir_config, /* dir config creator */
merge_neg_dir_configs, /* dir merger --- default is to override */
NULL, /* server config */
NULL, /* merge server config */
negotiation_cmds, /* command apr_table_t */
register_hooks /* register hooks */
};