mod_negotiation.c revision 58fd79b56eb624bf011772994e9761d3c2e228c1
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb/* ====================================================================
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * The Apache Software License, Version 1.1
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb *
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * Copyright (c) 2000-2002 The Apache Software Foundation. All rights
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * reserved.
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb *
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * Redistribution and use in source and binary forms, with or without
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * modification, are permitted provided that the following conditions
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * are met:
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb *
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * 1. Redistributions of source code must retain the above copyright
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * notice, this list of conditions and the following disclaimer.
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb *
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * 2. Redistributions in binary form must reproduce the above copyright
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * notice, this list of conditions and the following disclaimer in
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * the documentation and/or other materials provided with the
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * distribution.
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb *
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * 3. The end-user documentation included with the redistribution,
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * if any, must include the following acknowledgment:
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * "This product includes software developed by the
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * Apache Software Foundation (http://www.apache.org/)."
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * Alternately, this acknowledgment may appear in the software itself,
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * if and wherever such third-party acknowledgments normally appear.
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb *
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * 4. The names "Apache" and "Apache Software Foundation" must
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * not be used to endorse or promote products derived from this
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * software without prior written permission. For written
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * permission, please contact apache@apache.org.
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb *
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * 5. Products derived from this software may not be called "Apache",
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * nor may "Apache" appear in their name, without prior written
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * permission of the Apache Software Foundation.
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb *
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * SUCH DAMAGE.
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * ====================================================================
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb *
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * This software consists of voluntary contributions made by many
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * individuals on behalf of the Apache Software Foundation. For more
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * information on the Apache Software Foundation, please see
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * <http://www.apache.org/>.
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb *
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * Portions of this software are based upon public domain software
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * originally written at the National Center for Supercomputing Applications,
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * University of Illinois, Urbana-Champaign.
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb */
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb/*
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * mod_negotiation.c: keeps track of MIME types the client is willing to
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * accept, and contains code to handle type arbitration.
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb *
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * rst
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb */
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb#include "apr.h"
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb#include "apr_strings.h"
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb#include "apr_file_io.h"
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb#include "apr_lib.h"
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb#define APR_WANT_STRFUNC
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb#include "apr_want.h"
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb#include "ap_config.h"
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb#include "httpd.h"
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb#include "http_config.h"
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb#include "http_request.h"
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb#include "http_protocol.h"
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb#include "http_core.h"
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb#include "http_log.h"
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb#include "util_script.h"
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb#define MAP_FILE_MAGIC_TYPE "application/x-type-map"
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb/* Commands --- configuring document caching on a per (virtual?)
f888346b48f5e5b5e3f0a47dedb8cefd2759a4e2gregames * server basis...
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb */
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbbtypedef struct {
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb int forcelangpriority;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb apr_array_header_t *language_priority;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb} neg_dir_config;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb/* forcelangpriority flags
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz */
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz#define FLP_UNDEF 0 /* Same as FLP_DEFAULT, but base overrides */
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz#define FLP_NONE 1 /* Return 406, HTTP_NOT_ACCEPTABLE */
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz#define FLP_PREFER 2 /* Use language_priority rather than MC */
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz#define FLP_FALLBACK 4 /* Use language_priority rather than NA */
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz#define FLP_DEFAULT FLP_PREFER
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantzmodule AP_MODULE_DECLARE_DATA negotiation_module;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbbstatic void *create_neg_dir_config(apr_pool_t *p, char *dummy)
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb{
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb neg_dir_config *new = (neg_dir_config *) apr_palloc(p, sizeof(neg_dir_config));
a2a0abd88b19e042a3eb2a9fa1702c25ad51303dwrowe
a2a0abd88b19e042a3eb2a9fa1702c25ad51303dwrowe new->forcelangpriority = FLP_UNDEF;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb new->language_priority = NULL;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb return new;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb}
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbbstatic void *merge_neg_dir_configs(apr_pool_t *p, void *basev, void *addv)
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb{
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb neg_dir_config *base = (neg_dir_config *) basev;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb neg_dir_config *add = (neg_dir_config *) addv;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb neg_dir_config *new = (neg_dir_config *) apr_palloc(p, sizeof(neg_dir_config));
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb /* give priority to the config in the subdirectory */
c3e342e5b0b9fea6617ee16d2da02c3ef2108126dougm new->forcelangpriority = (add->forcelangpriority != FLP_UNDEF)
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb ? add->forcelangpriority
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe : base->forcelangpriority;
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe new->language_priority = add->language_priority
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe ? add->language_priority
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe : base->language_priority;
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe return new;
c2cf53a40a9814eb91db2cdf820f97d943f21628coar}
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowestatic const char *set_language_priority(cmd_parms *cmd, void *n_,
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar const char *lang)
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe{
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar neg_dir_config *n = n_;
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe const char **langp;
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe if (!n->language_priority)
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe n->language_priority = apr_array_make(cmd->pool, 4, sizeof(char *));
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe langp = (const char **) apr_array_push(n->language_priority);
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe *langp = lang;
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe return NULL;
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe}
3e392a5afd51526de3cb15d57ee46d8cb160ae65gregames
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowestatic const char *set_force_priority(cmd_parms *cmd, void *n_, const char *w)
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe{
ea512a4af20e6b6e6931de4929d54d93f03a0139ianh neg_dir_config *n = n_;
ea512a4af20e6b6e6931de4929d54d93f03a0139ianh
9e2e5b9bccb45b4a1bb9b747f15c7355e43be916ianh if (!strcasecmp(w, "None")) {
cbd8d35ca8d9780f1081f30ebfe4abda44cab7ebianh if (n->forcelangpriority & ~FLP_NONE) {
ea512a4af20e6b6e6931de4929d54d93f03a0139ianh return "Cannot combine ForceLanguagePriority options with None";
ea512a4af20e6b6e6931de4929d54d93f03a0139ianh }
ea512a4af20e6b6e6931de4929d54d93f03a0139ianh n->forcelangpriority = FLP_NONE;
ea512a4af20e6b6e6931de4929d54d93f03a0139ianh }
ea512a4af20e6b6e6931de4929d54d93f03a0139ianh else if (!strcasecmp(w, "Prefer")) {
ea512a4af20e6b6e6931de4929d54d93f03a0139ianh if (n->forcelangpriority & FLP_NONE) {
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe return "Cannot combine ForceLanguagePriority options None and Prefer";
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe }
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz n->forcelangpriority |= FLP_PREFER;
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz }
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz else if (!strcasecmp(w, "Fallback")) {
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz if (n->forcelangpriority & FLP_NONE) {
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe return "Cannot combine ForceLanguagePriority options None and Fallback";
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe }
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe n->forcelangpriority |= FLP_FALLBACK;
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe }
3e392a5afd51526de3cb15d57ee46d8cb160ae65gregames else {
3e392a5afd51526de3cb15d57ee46d8cb160ae65gregames return apr_pstrcat(cmd->pool, "Invalid ForceLanguagePriority option ", w, NULL);
3e392a5afd51526de3cb15d57ee46d8cb160ae65gregames }
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar return NULL;
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe}
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowestatic const char *cache_negotiated_docs(cmd_parms *cmd, void *dummy,
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe int arg)
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe{
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe ap_set_module_config(cmd->server->module_config, &negotiation_module,
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe (arg ? "Cache" : NULL));
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe return NULL;
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe}
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowestatic int do_cache_negotiated_docs(server_rec *s)
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe{
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe return (ap_get_module_config(s->module_config, &negotiation_module) != NULL);
c2cf53a40a9814eb91db2cdf820f97d943f21628coar}
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe
4775dfc34c90fada8c7c4d6a57ed8a3114d55c2dtrawickstatic const command_rec negotiation_cmds[] =
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe{
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe AP_INIT_FLAG("CacheNegotiatedDocs", cache_negotiated_docs, NULL, RSRC_CONF,
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe "Either 'on' or 'off' (default)"),
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe AP_INIT_ITERATE("LanguagePriority", set_language_priority, NULL, OR_FILEINFO,
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar "space-delimited list of MIME language abbreviations"),
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe AP_INIT_ITERATE("ForceLanguagePriority", set_force_priority, NULL, OR_FILEINFO,
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe "Force LanguagePriority elections, either None, or Fallback and/or Prefer"),
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe {NULL}
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe};
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe/*
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe * Record of available info on a media type specified by the client
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe * (we also use 'em for encodings and languages)
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe */
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowetypedef struct accept_rec {
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe char *name; /* MUST be lowercase */
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe float quality;
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe float level;
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe char *charset; /* for content-type only */
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe} accept_rec;
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe/*
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe * Record of available info on a particular variant
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe *
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz * Note that a few of these fields are updated by the actual negotiation
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe * code. These are:
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe *
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe * level_matched --- initialized to zero. Set to the value of level
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz * if the client actually accepts this media type at that
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe * level (and *not* if it got in on a wildcard). See level_cmp
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe * below.
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar * mime_stars -- initialized to zero. Set to the number of stars
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe * present in the best matching Accept header element.
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe * 1 for star/star, 2 for type/star and 3 for
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe * type/subtype.
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe *
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe * definite -- initialized to 1. Set to 0 if there is a match which
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe * makes the variant non-definite according to the rules
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe * in rfc2296.
c2cf53a40a9814eb91db2cdf820f97d943f21628coar */
c2cf53a40a9814eb91db2cdf820f97d943f21628coar
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowetypedef struct var_rec {
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz request_rec *sub_req; /* May be NULL (is, for map files) */
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz const char *mime_type; /* MUST be lowercase */
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz const char *file_name; /* Set to 'this' (for map file body content) */
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe apr_off_t body; /* Only for map file body content */
c2cf53a40a9814eb91db2cdf820f97d943f21628coar const char *content_encoding;
c2cf53a40a9814eb91db2cdf820f97d943f21628coar apr_array_header_t *content_languages; /* list of languages for this variant */
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe const char *content_charset;
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz const char *description;
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz /* The next five items give the quality values for the dimensions
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe * of negotiation for this variant. They are obtained from the
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe * appropriate header lines, except for source_quality, which
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe * is obtained from the variant itself (the 'qs' parameter value
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe * from the variant's mime-type). Apart from source_quality,
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz * these values are set when we find the quality for each variant
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz * (see best_match()). source_quality is set from the 'qs' parameter
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe * of the variant description or mime type: see set_mime_fields().
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe */
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz float lang_quality; /* quality of this variant's language */
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz float encoding_quality; /* ditto encoding */
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz float charset_quality; /* ditto charset */
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe float mime_type_quality; /* ditto media type */
c2cf53a40a9814eb91db2cdf820f97d943f21628coar float source_quality; /* source quality for this variant */
c2cf53a40a9814eb91db2cdf820f97d943f21628coar
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe /* Now some special values */
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz float level; /* Auxiliary to content-type... */
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz apr_off_t bytes; /* content length, if known */
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz int lang_index; /* Index into LanguagePriority list */
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe int is_pseudo_html; /* text/html, *or* the INCLUDES_MAGIC_TYPEs */
c2cf53a40a9814eb91db2cdf820f97d943f21628coar
c2cf53a40a9814eb91db2cdf820f97d943f21628coar /* Above are all written-once properties of the variant. The
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe * three fields below are changed during negotiation:
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz */
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz float level_matched;
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe int mime_stars;
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe int definite;
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe} var_rec;
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe/* Something to carry around the state of negotiation (and to keep
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe * all of this thread-safe)...
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe */
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowetypedef struct {
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe apr_pool_t *pool;
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz request_rec *r;
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe neg_dir_config *conf;
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe char *dir_name;
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe int accept_q; /* 1 if an Accept item has a q= param */
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe float default_lang_quality; /* fiddle lang q for variants with no lang */
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe /* the array pointers below are NULL if the corresponding accept
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe * headers are not present
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe */
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe apr_array_header_t *accepts; /* accept_recs */
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe apr_array_header_t *accept_encodings; /* accept_recs */
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe apr_array_header_t *accept_charsets; /* accept_recs */
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe apr_array_header_t *accept_langs; /* accept_recs */
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe apr_array_header_t *avail_vars; /* available variants */
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe int count_multiviews_variants; /* number of variants found on disk */
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe int is_transparent; /* 1 if this resource is trans. negotiable */
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe int dont_fiddle_headers; /* 1 if we may not fiddle with accept hdrs */
0540a0b469147b52e858587270dba31c2aaa9e09wrowe int ua_supports_trans; /* 1 if ua supports trans negotiation */
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe int send_alternates; /* 1 if we want to send an Alternates header */
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe int may_choose; /* 1 if we may choose a variant for the client */
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe int use_rvsa; /* 1 if we must use RVSA/1.0 negotiation algo */
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe} negotiation_state;
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe/* A few functions to manipulate var_recs.
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe * Cleaning out the fields...
0f6fdc73136a064819585afe03bc3503826ee592wrowe */
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowestatic void clean_var_rec(var_rec *mime_info)
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe{
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe mime_info->sub_req = NULL;
a2b181763cb35fd899feb4a436aeadaa80bf91eabrianp mime_info->mime_type = "";
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe mime_info->file_name = "";
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe mime_info->body = 0;
a2b181763cb35fd899feb4a436aeadaa80bf91eabrianp mime_info->content_encoding = NULL;
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe mime_info->content_languages = NULL;
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe mime_info->content_charset = "";
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe mime_info->description = "";
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe mime_info->is_pseudo_html = 0;
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe mime_info->level = 0.0f;
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe mime_info->level_matched = 0.0f;
a2b181763cb35fd899feb4a436aeadaa80bf91eabrianp mime_info->bytes = -1;
a2b181763cb35fd899feb4a436aeadaa80bf91eabrianp mime_info->lang_index = -1;
a2b181763cb35fd899feb4a436aeadaa80bf91eabrianp mime_info->mime_stars = 0;
a2b181763cb35fd899feb4a436aeadaa80bf91eabrianp mime_info->definite = 1;
a2b181763cb35fd899feb4a436aeadaa80bf91eabrianp
a2b181763cb35fd899feb4a436aeadaa80bf91eabrianp mime_info->charset_quality = 1.0f;
7a2edaa0193cbb0d79a65a8461a609a9402aea49brianp mime_info->encoding_quality = 1.0f;
a2b181763cb35fd899feb4a436aeadaa80bf91eabrianp mime_info->lang_quality = 1.0f;
a2b181763cb35fd899feb4a436aeadaa80bf91eabrianp mime_info->mime_type_quality = 1.0f;
7a2edaa0193cbb0d79a65a8461a609a9402aea49brianp mime_info->source_quality = 0.0f;
a2b181763cb35fd899feb4a436aeadaa80bf91eabrianp}
a2b181763cb35fd899feb4a436aeadaa80bf91eabrianp
a2b181763cb35fd899feb4a436aeadaa80bf91eabrianp/* Initializing the relevant fields of a variant record from the
7a2edaa0193cbb0d79a65a8461a609a9402aea49brianp * accept_info read out of its content-type, one way or another.
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe */
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowestatic void set_mime_fields(var_rec *var, accept_rec *mime_info)
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe{
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe var->mime_type = mime_info->name;
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe var->source_quality = mime_info->quality;
a2b181763cb35fd899feb4a436aeadaa80bf91eabrianp var->level = mime_info->level;
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe var->content_charset = mime_info->charset;
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe var->is_pseudo_html = (!strcmp(var->mime_type, "text/html")
cc9582e53aead2a044077c4a92f3dfc3605590b3wrowe || !strcmp(var->mime_type, INCLUDES_MAGIC_TYPE)
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb || !strcmp(var->mime_type, INCLUDES_MAGIC_TYPE3));
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb}
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb/* Create a variant list validator in r using info from vlistr. */
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbbstatic void set_vlist_validator(request_rec *r, request_rec *vlistr)
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb{
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb /* Calculating the variant list validator is similar to
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * calculating an etag for the source of the variant list
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * information, so we use ap_make_etag(). Note that this
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * validator can be 'weak' in extreme case.
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb */
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb ap_update_mtime(vlistr, vlistr->finfo.mtime);
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb r->vlist_validator = ap_make_etag(vlistr, 0);
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb /* ap_set_etag will later take r->vlist_validator into account
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * when creating the etag header
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb */
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb}
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb/*****************************************************************
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb *
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * Parsing (lists of) media types and their parameters, as seen in
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * HTTPD header lines and elsewhere.
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb */
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb/*
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * Get a single mime type entry --- one media type and parameters;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * enter the values we recognize into the argument accept_rec
c2cf53a40a9814eb91db2cdf820f97d943f21628coar */
c2cf53a40a9814eb91db2cdf820f97d943f21628coar
b38846b15c8891c6dec44dcc4f96ca40721bf663rbbstatic const char *get_entry(apr_pool_t *p, accept_rec *result,
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb const char *accept_line)
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb{
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe result->quality = 1.0f;
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe result->level = 0.0f;
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe result->charset = "";
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe /*
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe * Note that this handles what I gather is the "old format",
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe *
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe * Accept: text/html text/plain moo/zot
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe *
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe * without any compatibility kludges --- if the token after the
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe * MIME type begins with a semicolon, we know we're looking at parms,
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe * otherwise, we know we aren't. (So why all the pissing and moaning
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe * in the CERN server code? I must be missing something).
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe */
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe result->name = ap_get_token(p, &accept_line, 0);
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe ap_str_tolower(result->name); /* You want case insensitive,
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe * you'll *get* case insensitive.
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe */
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe /* KLUDGE!!! Default HTML to level 2.0 unless the browser
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar * *explicitly* says something else.
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe */
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar if (!strcmp(result->name, "text/html") && (result->level == 0.0)) {
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe result->level = 2.0f;
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar }
11fb2f3611e6ff9a541e10b13e3108934f828141gregames else if (!strcmp(result->name, INCLUDES_MAGIC_TYPE)) {
11fb2f3611e6ff9a541e10b13e3108934f828141gregames result->level = 2.0f;
11fb2f3611e6ff9a541e10b13e3108934f828141gregames }
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe else if (!strcmp(result->name, INCLUDES_MAGIC_TYPE3)) {
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe result->level = 3.0f;
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe }
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe while (*accept_line == ';') {
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe /* Parameters ... */
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar char *parm;
2f1949bb0e3c209db94c8d521cba7380b9d11421trawick char *cp;
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar char *end;
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar ++accept_line;
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar parm = ap_get_token(p, &accept_line, 1);
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar /* Look for 'var = value' --- and make sure the var is in lcase. */
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar for (cp = parm; (*cp && !apr_isspace(*cp) && *cp != '='); ++cp) {
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe *cp = apr_tolower(*cp);
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar }
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar if (!*cp) {
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe continue; /* No '='; just ignore it. */
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe }
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe *cp++ = '\0'; /* Delimit var */
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe while (*cp && (apr_isspace(*cp) || *cp == '=')) {
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb ++cp;
8aefbd756763807188d2e3ce336a8680e4893066wrowe }
8aefbd756763807188d2e3ce336a8680e4893066wrowe
8aefbd756763807188d2e3ce336a8680e4893066wrowe if (*cp == '"') {
8aefbd756763807188d2e3ce336a8680e4893066wrowe ++cp;
8aefbd756763807188d2e3ce336a8680e4893066wrowe for (end = cp;
8aefbd756763807188d2e3ce336a8680e4893066wrowe (*end && *end != '\n' && *end != '\r' && *end != '\"');
8aefbd756763807188d2e3ce336a8680e4893066wrowe end++);
8aefbd756763807188d2e3ce336a8680e4893066wrowe }
8aefbd756763807188d2e3ce336a8680e4893066wrowe else {
8aefbd756763807188d2e3ce336a8680e4893066wrowe for (end = cp; (*end && !apr_isspace(*end)); end++);
8aefbd756763807188d2e3ce336a8680e4893066wrowe }
8aefbd756763807188d2e3ce336a8680e4893066wrowe if (*end) {
8aefbd756763807188d2e3ce336a8680e4893066wrowe *end = '\0'; /* strip ending quote or return */
8aefbd756763807188d2e3ce336a8680e4893066wrowe }
8aefbd756763807188d2e3ce336a8680e4893066wrowe ap_str_tolower(cp);
8aefbd756763807188d2e3ce336a8680e4893066wrowe
a2a0abd88b19e042a3eb2a9fa1702c25ad51303dwrowe if (parm[0] == 'q'
8aefbd756763807188d2e3ce336a8680e4893066wrowe && (parm[1] == '\0' || (parm[1] == 's' && parm[2] == '\0'))) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe result->quality = (float)atof(cp);
8aefbd756763807188d2e3ce336a8680e4893066wrowe }
8aefbd756763807188d2e3ce336a8680e4893066wrowe else if (parm[0] == 'l' && !strcmp(&parm[1], "evel")) {
2fa5b5878e7567e2875807c3e2a2b3b0d3ef74bewrowe result->level = (float)atof(cp);
2fa5b5878e7567e2875807c3e2a2b3b0d3ef74bewrowe }
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe else if (!strcmp(parm, "charset")) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe result->charset = cp;
8aefbd756763807188d2e3ce336a8680e4893066wrowe }
8aefbd756763807188d2e3ce336a8680e4893066wrowe }
dc8692c6c0ca616a09aa12dad005f2ef23baa1a0wrowe
a8d11d78181478da6a672f7fbc58b8d523351f49wrowe if (*accept_line == ',') {
8aa5ca8ff2a4d8e56f62ea3d461e2799136da085trawick ++accept_line;
23c6309e36a63b13b61c35999c978017521993d6wrowe }
23c6309e36a63b13b61c35999c978017521993d6wrowe
23c6309e36a63b13b61c35999c978017521993d6wrowe return accept_line;
23c6309e36a63b13b61c35999c978017521993d6wrowe}
8aefbd756763807188d2e3ce336a8680e4893066wrowe
23c6309e36a63b13b61c35999c978017521993d6wrowe/*****************************************************************
dc8692c6c0ca616a09aa12dad005f2ef23baa1a0wrowe *
23c6309e36a63b13b61c35999c978017521993d6wrowe * Dealing with header lines ...
23c6309e36a63b13b61c35999c978017521993d6wrowe *
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe * Accept, Accept-Charset, Accept-Language and Accept-Encoding
8aefbd756763807188d2e3ce336a8680e4893066wrowe * are handled by do_header_line() - they all have the same
8aefbd756763807188d2e3ce336a8680e4893066wrowe * basic structure of a list of items of the format
cf6ef072483172309861d06e85b1aeff4573c060wrowe * name; q=N; charset=TEXT
cf6ef072483172309861d06e85b1aeff4573c060wrowe *
cf6ef072483172309861d06e85b1aeff4573c060wrowe * where charset is only valid in Accept.
8aefbd756763807188d2e3ce336a8680e4893066wrowe */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
8aefbd756763807188d2e3ce336a8680e4893066wrowestatic apr_array_header_t *do_header_line(apr_pool_t *p, const char *accept_line)
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe{
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe apr_array_header_t *accept_recs;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
a8d11d78181478da6a672f7fbc58b8d523351f49wrowe if (!accept_line) {
a8d11d78181478da6a672f7fbc58b8d523351f49wrowe return NULL;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
8aefbd756763807188d2e3ce336a8680e4893066wrowe
8aefbd756763807188d2e3ce336a8680e4893066wrowe accept_recs = apr_array_make(p, 40, sizeof(accept_rec));
cf6ef072483172309861d06e85b1aeff4573c060wrowe
cf6ef072483172309861d06e85b1aeff4573c060wrowe while (*accept_line) {
cf6ef072483172309861d06e85b1aeff4573c060wrowe accept_rec *new = (accept_rec *) apr_array_push(accept_recs);
8aefbd756763807188d2e3ce336a8680e4893066wrowe accept_line = get_entry(p, new, accept_line);
cf6ef072483172309861d06e85b1aeff4573c060wrowe }
cf6ef072483172309861d06e85b1aeff4573c060wrowe
a2b181763cb35fd899feb4a436aeadaa80bf91eabrianp return accept_recs;
cf6ef072483172309861d06e85b1aeff4573c060wrowe}
cf6ef072483172309861d06e85b1aeff4573c060wrowe
cf6ef072483172309861d06e85b1aeff4573c060wrowe/* Given the text of the Content-Languages: line from the var map file,
cf6ef072483172309861d06e85b1aeff4573c060wrowe * return an array containing the languages of this variant
cf6ef072483172309861d06e85b1aeff4573c060wrowe */
cf6ef072483172309861d06e85b1aeff4573c060wrowe
cf6ef072483172309861d06e85b1aeff4573c060wrowestatic apr_array_header_t *do_languages_line(apr_pool_t *p, const char **lang_line)
cf6ef072483172309861d06e85b1aeff4573c060wrowe{
cf6ef072483172309861d06e85b1aeff4573c060wrowe apr_array_header_t *lang_recs = apr_array_make(p, 2, sizeof(char *));
cf6ef072483172309861d06e85b1aeff4573c060wrowe
cf6ef072483172309861d06e85b1aeff4573c060wrowe if (!lang_line) {
69adb3d949e3dd17c0492a01fc2cf298832c7eefwrowe return lang_recs;
69adb3d949e3dd17c0492a01fc2cf298832c7eefwrowe }
cf6ef072483172309861d06e85b1aeff4573c060wrowe
cf6ef072483172309861d06e85b1aeff4573c060wrowe while (**lang_line) {
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar char **new = (char **) apr_array_push(lang_recs);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe *new = ap_get_token(p, lang_line, 0);
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar ap_str_tolower(*new);
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar if (**lang_line == ',' || **lang_line == ';') {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe ++(*lang_line);
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar }
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe return lang_recs;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe}
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
23c6309e36a63b13b61c35999c978017521993d6wrowe/*****************************************************************
23c6309e36a63b13b61c35999c978017521993d6wrowe *
23c6309e36a63b13b61c35999c978017521993d6wrowe * Handling header lines from clients...
23c6309e36a63b13b61c35999c978017521993d6wrowe */
c2cf53a40a9814eb91db2cdf820f97d943f21628coar
c2cf53a40a9814eb91db2cdf820f97d943f21628coarstatic negotiation_state *parse_accept_headers(request_rec *r)
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe{
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe negotiation_state *new =
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe (negotiation_state *) apr_pcalloc(r->pool, sizeof(negotiation_state));
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe accept_rec *elts;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe apr_table_t *hdrs = r->headers_in;
a9a4544168a37b43bd180b3703ccee995f27a80awrowe int i;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
a9a4544168a37b43bd180b3703ccee995f27a80awrowe new->pool = r->pool;
a9a4544168a37b43bd180b3703ccee995f27a80awrowe new->r = r;
a9a4544168a37b43bd180b3703ccee995f27a80awrowe new->conf = (neg_dir_config *)ap_get_module_config(r->per_dir_config,
a9a4544168a37b43bd180b3703ccee995f27a80awrowe &negotiation_module);
a9a4544168a37b43bd180b3703ccee995f27a80awrowe
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar new->dir_name = ap_make_dirstr_parent(r->pool, r->filename);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar new->accepts = do_header_line(r->pool, apr_table_get(hdrs, "Accept"));
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* calculate new->accept_q value */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if (new->accepts) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe elts = (accept_rec *) new->accepts->elts;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
8aefbd756763807188d2e3ce336a8680e4893066wrowe for (i = 0; i < new->accepts->nelts; ++i) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if (elts[i].quality < 1.0) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe new->accept_q = 1;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe new->accept_encodings =
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe do_header_line(r->pool, apr_table_get(hdrs, "Accept-Encoding"));
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe new->accept_langs =
cf6ef072483172309861d06e85b1aeff4573c060wrowe do_header_line(r->pool, apr_table_get(hdrs, "Accept-Language"));
cf6ef072483172309861d06e85b1aeff4573c060wrowe new->accept_charsets =
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe do_header_line(r->pool, apr_table_get(hdrs, "Accept-Charset"));
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* This is possibly overkill for some servers, heck, we have
8aefbd756763807188d2e3ce336a8680e4893066wrowe * only 33 index.html variants in docs/docroot (today).
0e58e92812f2f679d6bf2ff66cbcfa6c1d1e14bbjerenkrantz * Make this configurable?
0e58e92812f2f679d6bf2ff66cbcfa6c1d1e14bbjerenkrantz */
0e58e92812f2f679d6bf2ff66cbcfa6c1d1e14bbjerenkrantz new->avail_vars = apr_array_make(r->pool, 40, sizeof(var_rec));
cf6ef072483172309861d06e85b1aeff4573c060wrowe
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe return new;
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe}
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowestatic void parse_negotiate_header(request_rec *r, negotiation_state *neg)
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe{
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe const char *negotiate = apr_table_get(r->headers_in, "Negotiate");
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe char *tok;
8aefbd756763807188d2e3ce336a8680e4893066wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* First, default to no TCN, no Alternates, and the original Apache
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe * negotiation algorithm with fiddles for broken browser configs.
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe *
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe * To save network bandwidth, we do not configure to send an
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe * Alternates header to the user agent by default. User
8aefbd756763807188d2e3ce336a8680e4893066wrowe * agents that want an Alternates header for agent-driven
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe * negotiation will have to request it by sending an
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe * appropriate Negotiate header.
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe */
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe neg->ua_supports_trans = 0;
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe neg->send_alternates = 0;
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe neg->may_choose = 1;
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe neg->use_rvsa = 0;
59513b1275fdc2021d4949ee03ae8229469abb86wrowe neg->dont_fiddle_headers = 0;
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe if (!negotiate)
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe return;
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe if (strcmp(negotiate, "trans") == 0) {
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe /* Lynx 2.7 and 2.8 send 'negotiate: trans' even though they
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe * do not support transparent content negotiation, so for Lynx we
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe * ignore the negotiate header when its contents are exactly "trans".
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe * If future versions of Lynx ever need to say 'negotiate: trans',
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe * they can send the equivalent 'negotiate: trans, trans' instead
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe * to avoid triggering the workaround below.
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe */
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe const char *ua = apr_table_get(r->headers_in, "User-Agent");
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe if (ua && (strncmp(ua, "Lynx", 4) == 0))
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe return;
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe }
cf6ef072483172309861d06e85b1aeff4573c060wrowe
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe neg->may_choose = 0; /* An empty Negotiate would require 300 response */
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe while ((tok = ap_get_list_item(neg->pool, &negotiate)) != NULL) {
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe if (strcmp(tok, "trans") == 0 ||
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe strcmp(tok, "vlist") == 0 ||
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe strcmp(tok, "guess-small") == 0 ||
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe apr_isdigit(tok[0]) ||
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe strcmp(tok, "*") == 0) {
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe /* The user agent supports transparent negotiation */
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe neg->ua_supports_trans = 1;
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe /* Send-alternates could be configurable, but note
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe * that it must be 1 if we have 'vlist' in the
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe * negotiate header.
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe */
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe neg->send_alternates = 1;
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe if (strcmp(tok, "1.0") == 0) {
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe /* we may use the RVSA/1.0 algorithm, configure for it */
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe neg->may_choose = 1;
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe neg->use_rvsa = 1;
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe neg->dont_fiddle_headers = 1;
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe }
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe else if (tok[0] == '*') {
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe /* we may use any variant selection algorithm, configure
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe * to use the Apache algorithm
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe */
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe neg->may_choose = 1;
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe /* We disable header fiddles on the assumption that a
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe * client sending Negotiate knows how to send correct
cf6ef072483172309861d06e85b1aeff4573c060wrowe * headers which don't need fiddling.
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe */
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe neg->dont_fiddle_headers = 1;
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe }
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe }
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe }
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe#ifdef NEG_DEBUG
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe "dont_fiddle_headers=%d use_rvsa=%d ua_supports_trans=%d "
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe "send_alternates=%d, may_choose=%d",
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe neg->dont_fiddle_headers, neg->use_rvsa,
38bcc87d9a06e8ba81165421403f275eca4e313btrawick neg->ua_supports_trans, neg->send_alternates, neg->may_choose);
38bcc87d9a06e8ba81165421403f275eca4e313btrawick#endif
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe
0e58e92812f2f679d6bf2ff66cbcfa6c1d1e14bbjerenkrantz}
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe/* Sometimes clients will give us no Accept info at all; this routine sets
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe * up the standard default for that case, and also arranges for us to be
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe * willing to run a CGI script if we find one. (In fact, we set up to
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe * dramatically prefer CGI scripts in cases where that's appropriate,
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe * e.g., POST or when URI includes query args or extra path info).
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe */
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowestatic void maybe_add_default_accepts(negotiation_state *neg,
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe int prefer_scripts)
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe{
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe accept_rec *new_accept;
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe if (!neg->accepts) {
0e58e92812f2f679d6bf2ff66cbcfa6c1d1e14bbjerenkrantz neg->accepts = apr_array_make(neg->pool, 4, sizeof(accept_rec));
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
0e58e92812f2f679d6bf2ff66cbcfa6c1d1e14bbjerenkrantz new_accept = (accept_rec *) apr_array_push(neg->accepts);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
cf6ef072483172309861d06e85b1aeff4573c060wrowe new_accept->name = "*/*";
cf6ef072483172309861d06e85b1aeff4573c060wrowe new_accept->quality = 1.0f;
8aefbd756763807188d2e3ce336a8680e4893066wrowe new_accept->level = 0.0f;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe new_accept = (accept_rec *) apr_array_push(neg->accepts);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe new_accept->name = CGI_MAGIC_TYPE;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if (neg->use_rvsa) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe new_accept->quality = 0;
cf6ef072483172309861d06e85b1aeff4573c060wrowe }
cf6ef072483172309861d06e85b1aeff4573c060wrowe else {
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz new_accept->quality = prefer_scripts ? 2.0f : 0.001f;
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz }
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz new_accept->level = 0.0f;
cf6ef072483172309861d06e85b1aeff4573c060wrowe}
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe/*****************************************************************
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe *
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe * Parsing type-map files, in Roy's meta/http format augmented with
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz * #-comments.
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
4fca95918a9c0ae93593806544b425d0adc2fcc3wrowe/* Reading RFC822-style header lines, ignoring #-comments and
0e58e92812f2f679d6bf2ff66cbcfa6c1d1e14bbjerenkrantz * handling continuations.
0e58e92812f2f679d6bf2ff66cbcfa6c1d1e14bbjerenkrantz */
0e58e92812f2f679d6bf2ff66cbcfa6c1d1e14bbjerenkrantz
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantzenum header_state {
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar header_eof, header_seen, header_sep
8aefbd756763807188d2e3ce336a8680e4893066wrowe};
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowestatic enum header_state get_header_line(char *buffer, int len, apr_file_t *map)
4fca95918a9c0ae93593806544b425d0adc2fcc3wrowe{
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe char *buf_end = buffer + len;
8aefbd756763807188d2e3ce336a8680e4893066wrowe char *cp;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe char c;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* Get a noncommented line */
8aefbd756763807188d2e3ce336a8680e4893066wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe do {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if (apr_file_gets(buffer, MAX_STRING_LEN, map) != APR_SUCCESS) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe return header_eof;
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar }
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe } while (buffer[0] == '#');
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar
8aefbd756763807188d2e3ce336a8680e4893066wrowe /* If blank, just return it --- this ends information on this variant */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe for (cp = buffer; (*cp && apr_isspace(*cp)); ++cp) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe continue;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if (*cp == '\0') {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe return header_sep;
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz }
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* If non-blank, go looking for header lines, but note that we still
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe * have to treat comments specially...
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe */
8aefbd756763807188d2e3ce336a8680e4893066wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe cp += strlen(cp);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* We need to shortcut the rest of this block following the Body:
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe * tag - we will not look for continutation after this line.
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if (!strncasecmp(buffer, "Body:", 5))
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe return header_seen;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe while (apr_file_getc(&c, map) != APR_EOF) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if (c == '#') {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* Comment line */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe while (apr_file_getc(&c, map) != APR_EOF && c != '\n') {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe continue;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
8aefbd756763807188d2e3ce336a8680e4893066wrowe else if (apr_isspace(c)) {
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar /* Leading whitespace. POSSIBLE continuation line
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz * Also, possibly blank --- if so, we ungetc() the final newline
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe * so that we will pick up the blank line the next time 'round.
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe */
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar while (c != '\n' && apr_isspace(c)) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if(apr_file_getc(&c, map) != APR_SUCCESS)
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar break;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe apr_file_ungetc(c, map);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if (c == '\n') {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe return header_seen; /* Blank line */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz /* Continuation */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe while (cp < buf_end - 2 && (apr_file_getc(&c, map)) != APR_EOF && c != '\n') {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe *cp++ = c;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
8aefbd756763807188d2e3ce336a8680e4893066wrowe
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar *cp++ = '\n';
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe *cp = '\0';
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar }
8aefbd756763807188d2e3ce336a8680e4893066wrowe else {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz /* Line beginning with something other than whitespace */
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz apr_file_ungetc(c, map);
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz return header_seen;
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz }
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz return header_seen;
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz}
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowestatic apr_off_t get_body(char *buffer, apr_size_t *len, const char *tag,
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe apr_file_t *map)
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe{
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe char *endbody;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe int bodylen;
8aefbd756763807188d2e3ce336a8680e4893066wrowe int taglen;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe apr_off_t pos;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
8aefbd756763807188d2e3ce336a8680e4893066wrowe taglen = strlen(tag);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe *len -= taglen;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* We are at the first character following a body:tag\n entry
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe * Suck in the body, then backspace to the first char after the
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe * closing tag entry. If we fail to read, find the tag or back
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe * up then we have a hosed file, so give up already
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if (apr_file_read(map, buffer, len) != APR_SUCCESS) {
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar return -1;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe strncpy(buffer + *len, tag, taglen);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe endbody = strstr(buffer, tag);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if (endbody == buffer + *len) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe return -1;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe bodylen = endbody - buffer;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe endbody += strlen(tag);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* Skip all the trailing cruft after the end tag to the next line */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe while (*endbody) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if (*endbody == '\n') {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe ++endbody;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe break;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe ++endbody;
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz }
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe pos = -(apr_off_t)(*len - (endbody - buffer));
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if (apr_file_seek(map, APR_CUR, &pos) != APR_SUCCESS) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe return -1;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar /* Give the caller back the actual body's file offset and length */
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz *len = bodylen;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe return pos - (endbody - buffer);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe}
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe/* Stripping out RFC822 comments */
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowestatic void strip_paren_comments(char *hdr)
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe{
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* Hmmm... is this correct? In Roy's latest draft, (comments) can nest! */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* Nope, it isn't correct. Fails to handle backslash escape as well. */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz while (*hdr) {
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz if (*hdr == '"') {
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz hdr = strchr(hdr, '"');
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz if (hdr == NULL) {
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz return;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe ++hdr;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
c2cf53a40a9814eb91db2cdf820f97d943f21628coar else if (*hdr == '(') {
c2cf53a40a9814eb91db2cdf820f97d943f21628coar while (*hdr && *hdr != ')') {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe *hdr++ = ' ';
c2cf53a40a9814eb91db2cdf820f97d943f21628coar }
2787ef5edc27fa4f6777ba8d51aa48fd9fdf54bbwrowe
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz if (*hdr) {
c2cf53a40a9814eb91db2cdf820f97d943f21628coar *hdr++ = ' ';
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz }
c2cf53a40a9814eb91db2cdf820f97d943f21628coar }
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz else {
2787ef5edc27fa4f6777ba8d51aa48fd9fdf54bbwrowe ++hdr;
2787ef5edc27fa4f6777ba8d51aa48fd9fdf54bbwrowe }
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz }
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz}
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz
2787ef5edc27fa4f6777ba8d51aa48fd9fdf54bbwrowe/* Getting to a header body from the header */
2787ef5edc27fa4f6777ba8d51aa48fd9fdf54bbwrowe
2787ef5edc27fa4f6777ba8d51aa48fd9fdf54bbwrowestatic char *lcase_header_name_return_body(char *header, request_rec *r)
2787ef5edc27fa4f6777ba8d51aa48fd9fdf54bbwrowe{
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe char *cp = header;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe for ( ; *cp && *cp != ':' ; ++cp) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe *cp = apr_tolower(*cp);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
8aefbd756763807188d2e3ce336a8680e4893066wrowe
2520f59894a3e07fefa881ef68aaded763a8d447ben if (!*cp) {
0e58e92812f2f679d6bf2ff66cbcfa6c1d1e14bbjerenkrantz ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar "Syntax error in type map, no ':' in %s for header %s",
8aefbd756763807188d2e3ce336a8680e4893066wrowe r->filename, header);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe return NULL;
8aefbd756763807188d2e3ce336a8680e4893066wrowe }
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe do {
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar ++cp;
8aefbd756763807188d2e3ce336a8680e4893066wrowe } while (*cp && apr_isspace(*cp));
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if (!*cp) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
8aefbd756763807188d2e3ce336a8680e4893066wrowe "Syntax error in type map --- no header body: %s for %s",
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe r->filename, header);
0e58e92812f2f679d6bf2ff66cbcfa6c1d1e14bbjerenkrantz return NULL;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
0e58e92812f2f679d6bf2ff66cbcfa6c1d1e14bbjerenkrantz return cp;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe}
0e58e92812f2f679d6bf2ff66cbcfa6c1d1e14bbjerenkrantz
0e58e92812f2f679d6bf2ff66cbcfa6c1d1e14bbjerenkrantzstatic int read_type_map(apr_file_t **map, negotiation_state *neg, request_rec *rr)
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe{
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe request_rec *r = neg->r;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe apr_file_t *map_ = NULL;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe apr_status_t status;
0e58e92812f2f679d6bf2ff66cbcfa6c1d1e14bbjerenkrantz char buffer[MAX_STRING_LEN];
0e58e92812f2f679d6bf2ff66cbcfa6c1d1e14bbjerenkrantz enum header_state hstate;
0e58e92812f2f679d6bf2ff66cbcfa6c1d1e14bbjerenkrantz struct var_rec mime_info;
0e58e92812f2f679d6bf2ff66cbcfa6c1d1e14bbjerenkrantz int has_content;
8aefbd756763807188d2e3ce336a8680e4893066wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if (!map)
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe map = &map_;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* We are not using multiviews */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe neg->count_multiviews_variants = 0;
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if ((status = apr_file_open(map, rr->filename, APR_READ | APR_BUFFERED,
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar APR_OS_DEFAULT, neg->pool)) != APR_SUCCESS) {
8aefbd756763807188d2e3ce336a8680e4893066wrowe ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r,
cf6ef072483172309861d06e85b1aeff4573c060wrowe "cannot access type map file: %s", rr->filename);
cf6ef072483172309861d06e85b1aeff4573c060wrowe return HTTP_FORBIDDEN;
cf6ef072483172309861d06e85b1aeff4573c060wrowe }
cf6ef072483172309861d06e85b1aeff4573c060wrowe
cf6ef072483172309861d06e85b1aeff4573c060wrowe clean_var_rec(&mime_info);
cf6ef072483172309861d06e85b1aeff4573c060wrowe has_content = 0;
cf6ef072483172309861d06e85b1aeff4573c060wrowe
cf6ef072483172309861d06e85b1aeff4573c060wrowe do {
cf6ef072483172309861d06e85b1aeff4573c060wrowe hstate = get_header_line(buffer, MAX_STRING_LEN, *map);
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe
cf6ef072483172309861d06e85b1aeff4573c060wrowe if (hstate == header_seen) {
69adb3d949e3dd17c0492a01fc2cf298832c7eefwrowe char *body1 = lcase_header_name_return_body(buffer, neg->r);
69adb3d949e3dd17c0492a01fc2cf298832c7eefwrowe const char *body;
69adb3d949e3dd17c0492a01fc2cf298832c7eefwrowe
cf6ef072483172309861d06e85b1aeff4573c060wrowe if (body1 == NULL) {
cf6ef072483172309861d06e85b1aeff4573c060wrowe return HTTP_INTERNAL_SERVER_ERROR;
cf6ef072483172309861d06e85b1aeff4573c060wrowe }
cf6ef072483172309861d06e85b1aeff4573c060wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe strip_paren_comments(body1);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe body = body1;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if (!strncmp(buffer, "uri:", 4)) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe mime_info.file_name = ap_get_token(neg->pool, &body, 0);
cf6ef072483172309861d06e85b1aeff4573c060wrowe }
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe else if (!strncmp(buffer, "content-type:", 13)) {
cf6ef072483172309861d06e85b1aeff4573c060wrowe struct accept_rec accept_info;
c2cf53a40a9814eb91db2cdf820f97d943f21628coar
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe get_entry(neg->pool, &accept_info, body);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe set_mime_fields(&mime_info, &accept_info);
c2cf53a40a9814eb91db2cdf820f97d943f21628coar has_content = 1;
c2cf53a40a9814eb91db2cdf820f97d943f21628coar }
8aefbd756763807188d2e3ce336a8680e4893066wrowe else if (!strncmp(buffer, "content-length:", 15)) {
cf6ef072483172309861d06e85b1aeff4573c060wrowe mime_info.bytes = atol(body);
8aefbd756763807188d2e3ce336a8680e4893066wrowe has_content = 1;
8aefbd756763807188d2e3ce336a8680e4893066wrowe }
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe else if (!strncmp(buffer, "content-language:", 17)) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe mime_info.content_languages = do_languages_line(neg->pool,
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe &body);
8aefbd756763807188d2e3ce336a8680e4893066wrowe has_content = 1;
8aefbd756763807188d2e3ce336a8680e4893066wrowe }
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe else if (!strncmp(buffer, "content-encoding:", 17)) {
cf6ef072483172309861d06e85b1aeff4573c060wrowe mime_info.content_encoding = ap_get_token(neg->pool, &body, 0);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe has_content = 1;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe else if (!strncmp(buffer, "description:", 12)) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe char *desc = apr_pstrdup(neg->pool, body);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe char *cp;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe for (cp = desc; *cp; ++cp) {
69adb3d949e3dd17c0492a01fc2cf298832c7eefwrowe if (*cp=='\n') *cp=' ';
69adb3d949e3dd17c0492a01fc2cf298832c7eefwrowe }
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if (cp>desc) *(cp-1)=0;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe mime_info.description = desc;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
8aefbd756763807188d2e3ce336a8680e4893066wrowe else if (!strncmp(buffer, "body:", 5)) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe char *tag = apr_pstrdup(neg->pool, body);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe char *eol = strchr(tag, '\0');
cf6ef072483172309861d06e85b1aeff4573c060wrowe apr_size_t len = MAX_STRING_LEN;
cf6ef072483172309861d06e85b1aeff4573c060wrowe while (--eol >= tag && apr_isspace(*eol))
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe *eol = '\0';
cadddb2c31d24d48f4017db4df0a29687432326cwrowe if ((mime_info.body = get_body(buffer, &len, tag, *map)) < 0) {
cadddb2c31d24d48f4017db4df0a29687432326cwrowe ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe "Syntax error in type map, no end tag '%s'"
cf6ef072483172309861d06e85b1aeff4573c060wrowe "found in %s for Body: content.",
0e58e92812f2f679d6bf2ff66cbcfa6c1d1e14bbjerenkrantz tag, r->filename);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe break;
8aefbd756763807188d2e3ce336a8680e4893066wrowe }
cf6ef072483172309861d06e85b1aeff4573c060wrowe mime_info.bytes = len;
cf6ef072483172309861d06e85b1aeff4573c060wrowe mime_info.file_name = rr->filename;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
cf6ef072483172309861d06e85b1aeff4573c060wrowe }
cf6ef072483172309861d06e85b1aeff4573c060wrowe else {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if (*mime_info.file_name && has_content) {
c2cf53a40a9814eb91db2cdf820f97d943f21628coar void *new_var = apr_array_push(neg->avail_vars);
c2cf53a40a9814eb91db2cdf820f97d943f21628coar
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe memcpy(new_var, (void *) &mime_info, sizeof(var_rec));
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
8aefbd756763807188d2e3ce336a8680e4893066wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe clean_var_rec(&mime_info);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe has_content = 0;
cf6ef072483172309861d06e85b1aeff4573c060wrowe }
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe } while (hstate != header_eof);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if (map_)
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe apr_file_close(map_);
cf6ef072483172309861d06e85b1aeff4573c060wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe set_vlist_validator(r, rr);
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz return OK;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe}
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
8aefbd756763807188d2e3ce336a8680e4893066wrowe/* Sort function used by read_types_multi. */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowestatic int variantsortf(var_rec *a, var_rec *b) {
cf6ef072483172309861d06e85b1aeff4573c060wrowe
cf6ef072483172309861d06e85b1aeff4573c060wrowe /* First key is the source quality, sort in descending order. */
cf6ef072483172309861d06e85b1aeff4573c060wrowe
cf6ef072483172309861d06e85b1aeff4573c060wrowe /* XXX: note that we currently implement no method of setting the
cf6ef072483172309861d06e85b1aeff4573c060wrowe * source quality for multiviews variants, so we are always comparing
69adb3d949e3dd17c0492a01fc2cf298832c7eefwrowe * 1.0 to 1.0 for now
cf6ef072483172309861d06e85b1aeff4573c060wrowe */
cf6ef072483172309861d06e85b1aeff4573c060wrowe if (a->source_quality < b->source_quality)
cf6ef072483172309861d06e85b1aeff4573c060wrowe return 1;
cf6ef072483172309861d06e85b1aeff4573c060wrowe if (a->source_quality > b->source_quality)
cf6ef072483172309861d06e85b1aeff4573c060wrowe return -1;
cf6ef072483172309861d06e85b1aeff4573c060wrowe
cf6ef072483172309861d06e85b1aeff4573c060wrowe /* Second key is the variant name */
cf6ef072483172309861d06e85b1aeff4573c060wrowe return strcmp(a->file_name, b->file_name);
cf6ef072483172309861d06e85b1aeff4573c060wrowe}
cf6ef072483172309861d06e85b1aeff4573c060wrowe
cf6ef072483172309861d06e85b1aeff4573c060wrowe/*****************************************************************
cf6ef072483172309861d06e85b1aeff4573c060wrowe *
cf6ef072483172309861d06e85b1aeff4573c060wrowe * Same as read_type_map, except we use a filtered directory listing
cf6ef072483172309861d06e85b1aeff4573c060wrowe * as the map...
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowestatic int read_types_multi(negotiation_state *neg)
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe{
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe request_rec *r = neg->r;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe char *filp;
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz int prefix_len;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe apr_dir_t *dirp;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe apr_finfo_t dirent;
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar apr_status_t status;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe struct var_rec mime_info;
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar struct accept_rec accept_info;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe void *new_var;
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar int anymatch = 0;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar clean_var_rec(&mime_info);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if (!(filp = strrchr(r->filename, '/'))) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe return DECLINED; /* Weird... */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* XXX this should be more general, and quit using 'specials' */
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if (strncmp(r->filename, "proxy:", 6) == 0) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe return DECLINED;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe ++filp;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe prefix_len = strlen(filp);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if ((status = apr_dir_open(&dirp, neg->dir_name, neg->pool)) != APR_SUCCESS) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r,
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe "cannot read directory for multi: %s", neg->dir_name);
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe return HTTP_FORBIDDEN;
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar }
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe while (apr_dir_read(&dirent, APR_FINFO_DIRENT, dirp) == APR_SUCCESS) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe apr_array_header_t *exception_list;
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar request_rec *sub_req;
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* Do we have a match? */
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar#ifdef CASE_BLIND_FILESYSTEM
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if (strncasecmp(dirent.name, filp, prefix_len)) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe#else
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if (strncmp(dirent.name, filp, prefix_len)) {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe#endif
8aefbd756763807188d2e3ce336a8680e4893066wrowe continue;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe if (dirent.name[prefix_len] != '.') {
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe continue;
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe }
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar
e4a3f3c2f080cac75a15a6454cca429b8161c050wrowe /* Don't negotiate directories and other unusual files
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar * Really shouldn't see anything but DIR/LNK/REG here,
8aefbd756763807188d2e3ce336a8680e4893066wrowe * and we aught to discover if the LNK was interesting.
8aefbd756763807188d2e3ce336a8680e4893066wrowe *
c2cf53a40a9814eb91db2cdf820f97d943f21628coar * Of course, this only helps platforms that capture the
c2cf53a40a9814eb91db2cdf820f97d943f21628coar * the filetype in apr_dir_read(), which most can once
c2cf53a40a9814eb91db2cdf820f97d943f21628coar * they are optimized with some magic [it's known to the
c2cf53a40a9814eb91db2cdf820f97d943f21628coar * dirent, not associated to the inode, on most FS's.]
c2cf53a40a9814eb91db2cdf820f97d943f21628coar */
8aefbd756763807188d2e3ce336a8680e4893066wrowe if ((dirent.valid & APR_FINFO_TYPE) && (dirent.filetype == APR_DIR))
8aefbd756763807188d2e3ce336a8680e4893066wrowe continue;
8aefbd756763807188d2e3ce336a8680e4893066wrowe
8aefbd756763807188d2e3ce336a8680e4893066wrowe /* Ok, something's here. Maybe nothing useful. Remember that
8aefbd756763807188d2e3ce336a8680e4893066wrowe * we tried, if we completely fail, so we can reject the request!
8aefbd756763807188d2e3ce336a8680e4893066wrowe */
5bb29f57ae0184d2b3c1cdf35132f8ceb011f882wrowe anymatch = 1;
8aefbd756763807188d2e3ce336a8680e4893066wrowe
8aefbd756763807188d2e3ce336a8680e4893066wrowe /* See if it's something which we have access to, and which
8aefbd756763807188d2e3ce336a8680e4893066wrowe * has a known type and encoding (as opposed to something
c2cf53a40a9814eb91db2cdf820f97d943f21628coar * which we'll be slapping default_type on later).
c2cf53a40a9814eb91db2cdf820f97d943f21628coar */
8aefbd756763807188d2e3ce336a8680e4893066wrowe sub_req = ap_sub_req_lookup_dirent(&dirent, r, AP_SUBREQ_MERGE_ARGS, NULL);
c2cf53a40a9814eb91db2cdf820f97d943f21628coar
8aefbd756763807188d2e3ce336a8680e4893066wrowe /* Double check, we still don't multi-resolve non-ordinary files
8aefbd756763807188d2e3ce336a8680e4893066wrowe */
8aefbd756763807188d2e3ce336a8680e4893066wrowe if (sub_req->finfo.filetype != APR_REG)
8aefbd756763807188d2e3ce336a8680e4893066wrowe continue;
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe /* If it has a handler, we'll pretend it's a CGI script,
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe * since that's a good indication of the sort of thing it
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe * might be doing.
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe */
1b315ee865b0f11e582beb64127ca3a99a319d2fwrowe if (sub_req->handler && !sub_req->content_type) {
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar ap_set_content_type(sub_req, CGI_MAGIC_TYPE);
0540a0b469147b52e858587270dba31c2aaa9e09wrowe }
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar /*
0540a0b469147b52e858587270dba31c2aaa9e09wrowe * mod_mime will _always_ provide us the base name in the
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar * ap-mime-exception-list, if it processed anything. If
0540a0b469147b52e858587270dba31c2aaa9e09wrowe * this list is empty, give up immediately, there was
0540a0b469147b52e858587270dba31c2aaa9e09wrowe * nothing interesting. For example, looking at the files
a9a4544168a37b43bd180b3703ccee995f27a80awrowe * readme.txt and readme.foo, we will throw away .foo if
0540a0b469147b52e858587270dba31c2aaa9e09wrowe * it's an insignificant file (e.g. did not identify a
0540a0b469147b52e858587270dba31c2aaa9e09wrowe * language, charset, encoding, content type or handler,)
0540a0b469147b52e858587270dba31c2aaa9e09wrowe */
dc8692c6c0ca616a09aa12dad005f2ef23baa1a0wrowe exception_list =
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar (apr_array_header_t *)apr_table_get(sub_req->notes,
0540a0b469147b52e858587270dba31c2aaa9e09wrowe "ap-mime-exceptions-list");
0540a0b469147b52e858587270dba31c2aaa9e09wrowe
0540a0b469147b52e858587270dba31c2aaa9e09wrowe if (!exception_list) {
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar ap_destroy_sub_req(sub_req);
0540a0b469147b52e858587270dba31c2aaa9e09wrowe continue;
dc8692c6c0ca616a09aa12dad005f2ef23baa1a0wrowe }
0540a0b469147b52e858587270dba31c2aaa9e09wrowe
8aefbd756763807188d2e3ce336a8680e4893066wrowe /* Each unregonized bit better match our base name, in sequence.
8aefbd756763807188d2e3ce336a8680e4893066wrowe * A test of index.html.foo will match index.foo or index.html.foo,
0540a0b469147b52e858587270dba31c2aaa9e09wrowe * but it will never transpose the segments and allow index.foo.html
a2a0abd88b19e042a3eb2a9fa1702c25ad51303dwrowe * because that would introduce too much CPU consumption. Better that
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * we don't attempt a many-to-many match here.
948096a99010fccf648814fecf38f75c689172d7wrowe */
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb {
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb int nexcept = exception_list->nelts;
0540a0b469147b52e858587270dba31c2aaa9e09wrowe char **cur_except = (char**)exception_list->elts;
0540a0b469147b52e858587270dba31c2aaa9e09wrowe char *segstart = filp, *segend, saveend;
948096a99010fccf648814fecf38f75c689172d7wrowe
948096a99010fccf648814fecf38f75c689172d7wrowe while (*segstart && nexcept) {
948096a99010fccf648814fecf38f75c689172d7wrowe if (!(segend = strchr(segstart, '.')))
a2b181763cb35fd899feb4a436aeadaa80bf91eabrianp segend = strchr(segstart, '\0');
3f5b4558f5410fdac5d6feed7aab0c3668f9cd13wrowe saveend = *segend;
948096a99010fccf648814fecf38f75c689172d7wrowe *segend = '\0';
dc8692c6c0ca616a09aa12dad005f2ef23baa1a0wrowe
dc8692c6c0ca616a09aa12dad005f2ef23baa1a0wrowe#ifdef CASE_BLIND_FILESYSTEM
053497224246c4dbef9af594cacf5c00ed271e6cwrowe if (strcasecmp(segstart, *cur_except) == 0) {
0540a0b469147b52e858587270dba31c2aaa9e09wrowe#else
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz if (strcmp(segstart, *cur_except) == 0) {
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb#endif
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb --nexcept;
948096a99010fccf648814fecf38f75c689172d7wrowe ++cur_except;
948096a99010fccf648814fecf38f75c689172d7wrowe }
948096a99010fccf648814fecf38f75c689172d7wrowe
948096a99010fccf648814fecf38f75c689172d7wrowe if (!saveend)
948096a99010fccf648814fecf38f75c689172d7wrowe break;
948096a99010fccf648814fecf38f75c689172d7wrowe
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz *segend = saveend;
948096a99010fccf648814fecf38f75c689172d7wrowe segstart = segend + 1;
948096a99010fccf648814fecf38f75c689172d7wrowe }
948096a99010fccf648814fecf38f75c689172d7wrowe
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz if (nexcept) {
948096a99010fccf648814fecf38f75c689172d7wrowe /* Something you don't know is, something you don't know...
948096a99010fccf648814fecf38f75c689172d7wrowe */
053497224246c4dbef9af594cacf5c00ed271e6cwrowe ap_destroy_sub_req(sub_req);
948096a99010fccf648814fecf38f75c689172d7wrowe continue;
948096a99010fccf648814fecf38f75c689172d7wrowe }
948096a99010fccf648814fecf38f75c689172d7wrowe }
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
c2cf53a40a9814eb91db2cdf820f97d943f21628coar /*
c2cf53a40a9814eb91db2cdf820f97d943f21628coar * ###: be warned, the _default_ content type is already
c2cf53a40a9814eb91db2cdf820f97d943f21628coar * picked up here! If we failed the subrequest, or don't
948096a99010fccf648814fecf38f75c689172d7wrowe * know what we are serving, then continue.
948096a99010fccf648814fecf38f75c689172d7wrowe */
948096a99010fccf648814fecf38f75c689172d7wrowe if (sub_req->status != HTTP_OK || (!sub_req->content_type)) {
948096a99010fccf648814fecf38f75c689172d7wrowe ap_destroy_sub_req(sub_req);
053497224246c4dbef9af594cacf5c00ed271e6cwrowe continue;
a9a4544168a37b43bd180b3703ccee995f27a80awrowe }
948096a99010fccf648814fecf38f75c689172d7wrowe
a9a4544168a37b43bd180b3703ccee995f27a80awrowe /* If it's a map file, we use that instead of the map
a9a4544168a37b43bd180b3703ccee995f27a80awrowe * we're building...
a9a4544168a37b43bd180b3703ccee995f27a80awrowe */
a9a4544168a37b43bd180b3703ccee995f27a80awrowe if (((sub_req->content_type) &&
a9a4544168a37b43bd180b3703ccee995f27a80awrowe !strcmp(sub_req->content_type, MAP_FILE_MAGIC_TYPE)) ||
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar ((sub_req->handler) &&
948096a99010fccf648814fecf38f75c689172d7wrowe !strcmp(sub_req->handler, "type-map"))) {
948096a99010fccf648814fecf38f75c689172d7wrowe
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar apr_dir_close(dirp);
948096a99010fccf648814fecf38f75c689172d7wrowe neg->avail_vars->nelts = 0;
948096a99010fccf648814fecf38f75c689172d7wrowe if (sub_req->status != HTTP_OK) {
948096a99010fccf648814fecf38f75c689172d7wrowe return sub_req->status;
948096a99010fccf648814fecf38f75c689172d7wrowe }
948096a99010fccf648814fecf38f75c689172d7wrowe return read_type_map(NULL, neg, sub_req);
0540a0b469147b52e858587270dba31c2aaa9e09wrowe }
948096a99010fccf648814fecf38f75c689172d7wrowe
aa047239dedf0d26e8efecfade32e7337f35df19wrowe /* Have reasonable variant --- gather notes. */
948096a99010fccf648814fecf38f75c689172d7wrowe
948096a99010fccf648814fecf38f75c689172d7wrowe mime_info.sub_req = sub_req;
948096a99010fccf648814fecf38f75c689172d7wrowe mime_info.file_name = apr_pstrdup(neg->pool, dirent.name);
948096a99010fccf648814fecf38f75c689172d7wrowe if (sub_req->content_encoding) {
948096a99010fccf648814fecf38f75c689172d7wrowe mime_info.content_encoding = sub_req->content_encoding;
053497224246c4dbef9af594cacf5c00ed271e6cwrowe }
0540a0b469147b52e858587270dba31c2aaa9e09wrowe if (sub_req->content_languages) {
053497224246c4dbef9af594cacf5c00ed271e6cwrowe mime_info.content_languages = sub_req->content_languages;
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz }
0540a0b469147b52e858587270dba31c2aaa9e09wrowe
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz get_entry(neg->pool, &accept_info, sub_req->content_type);
aa047239dedf0d26e8efecfade32e7337f35df19wrowe set_mime_fields(&mime_info, &accept_info);
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb new_var = apr_array_push(neg->avail_vars);
053497224246c4dbef9af594cacf5c00ed271e6cwrowe memcpy(new_var, (void *) &mime_info, sizeof(var_rec));
053497224246c4dbef9af594cacf5c00ed271e6cwrowe
053497224246c4dbef9af594cacf5c00ed271e6cwrowe neg->count_multiviews_variants++;
053497224246c4dbef9af594cacf5c00ed271e6cwrowe
053497224246c4dbef9af594cacf5c00ed271e6cwrowe clean_var_rec(&mime_info);
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz }
c2cf53a40a9814eb91db2cdf820f97d943f21628coar
c2cf53a40a9814eb91db2cdf820f97d943f21628coar apr_dir_close(dirp);
c2cf53a40a9814eb91db2cdf820f97d943f21628coar
c2cf53a40a9814eb91db2cdf820f97d943f21628coar /* We found some file names that matched. None could be served.
c2cf53a40a9814eb91db2cdf820f97d943f21628coar * Rather than fall out to autoindex or some other mapper, this
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * request must die.
aa047239dedf0d26e8efecfade32e7337f35df19wrowe */
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz if (anymatch && !neg->avail_vars->nelts) {
053497224246c4dbef9af594cacf5c00ed271e6cwrowe ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb "Negotiation: discovered file(s) matching request: %s"
948096a99010fccf648814fecf38f75c689172d7wrowe " (None could be negotiated).",
948096a99010fccf648814fecf38f75c689172d7wrowe r->filename);
948096a99010fccf648814fecf38f75c689172d7wrowe return HTTP_NOT_FOUND;
0540a0b469147b52e858587270dba31c2aaa9e09wrowe }
948096a99010fccf648814fecf38f75c689172d7wrowe
948096a99010fccf648814fecf38f75c689172d7wrowe set_vlist_validator(r, r);
948096a99010fccf648814fecf38f75c689172d7wrowe
948096a99010fccf648814fecf38f75c689172d7wrowe /* Sort the variants into a canonical order. The negotiation
948096a99010fccf648814fecf38f75c689172d7wrowe * result sometimes depends on the order of the variants. By
948096a99010fccf648814fecf38f75c689172d7wrowe * sorting the variants into a canonical order, rather than using
948096a99010fccf648814fecf38f75c689172d7wrowe * the order in which readdir() happens to return them, we ensure
948096a99010fccf648814fecf38f75c689172d7wrowe * that the negotiation result will be consistent over filesystem
948096a99010fccf648814fecf38f75c689172d7wrowe * backup/restores and over all mirror sites.
948096a99010fccf648814fecf38f75c689172d7wrowe */
948096a99010fccf648814fecf38f75c689172d7wrowe
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb qsort((void *) neg->avail_vars->elts, neg->avail_vars->nelts,
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar sizeof(var_rec), (int (*)(const void *, const void *)) variantsortf);
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz
948096a99010fccf648814fecf38f75c689172d7wrowe return OK;
0540a0b469147b52e858587270dba31c2aaa9e09wrowe}
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar
0540a0b469147b52e858587270dba31c2aaa9e09wrowe/*****************************************************************
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar * And now for the code you've been waiting for... actually
696218c49632c863d18b25fa52ab63617088cb38wrowe * finding a match to the client's requirements.
948096a99010fccf648814fecf38f75c689172d7wrowe */
0540a0b469147b52e858587270dba31c2aaa9e09wrowe
948096a99010fccf648814fecf38f75c689172d7wrowe/* Matching MIME types ... the star/star and foo/star commenting conventions
948096a99010fccf648814fecf38f75c689172d7wrowe * are implemented here. (You know what I mean by star/star, but just
948096a99010fccf648814fecf38f75c689172d7wrowe * try mentioning those three characters in a C comment). Using strcmp()
948096a99010fccf648814fecf38f75c689172d7wrowe * is legit, because everything has already been smashed to lowercase.
053497224246c4dbef9af594cacf5c00ed271e6cwrowe *
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar * Note also that if we get an exact match on the media type, we update
948096a99010fccf648814fecf38f75c689172d7wrowe * level_matched for use in level_cmp below...
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar *
053497224246c4dbef9af594cacf5c00ed271e6cwrowe * We also give a value for mime_stars, which is used later. It should
053497224246c4dbef9af594cacf5c00ed271e6cwrowe * be 1 for star/star, 2 for type/star and 3 for type/subtype.
a9a4544168a37b43bd180b3703ccee995f27a80awrowe */
a9a4544168a37b43bd180b3703ccee995f27a80awrowe
a9a4544168a37b43bd180b3703ccee995f27a80awrowestatic int mime_match(accept_rec *accept_r, var_rec *avail)
948096a99010fccf648814fecf38f75c689172d7wrowe{
948096a99010fccf648814fecf38f75c689172d7wrowe const char *accept_type = accept_r->name;
053497224246c4dbef9af594cacf5c00ed271e6cwrowe const char *avail_type = avail->mime_type;
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar int len = strlen(accept_type);
053497224246c4dbef9af594cacf5c00ed271e6cwrowe
053497224246c4dbef9af594cacf5c00ed271e6cwrowe if (accept_type[0] == '*') { /* Anything matches star/star */
948096a99010fccf648814fecf38f75c689172d7wrowe if (avail->mime_stars < 1) {
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar avail->mime_stars = 1;
948096a99010fccf648814fecf38f75c689172d7wrowe }
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb return 1;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb }
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb else if ((accept_type[len - 1] == '*') &&
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb !strncmp(accept_type, avail_type, len - 2)) {
a2a0abd88b19e042a3eb2a9fa1702c25ad51303dwrowe if (avail->mime_stars < 2) {
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb avail->mime_stars = 2;
aa047239dedf0d26e8efecfade32e7337f35df19wrowe }
aa047239dedf0d26e8efecfade32e7337f35df19wrowe return 1;
aa047239dedf0d26e8efecfade32e7337f35df19wrowe }
0540a0b469147b52e858587270dba31c2aaa9e09wrowe else if (!strcmp(accept_type, avail_type)
0540a0b469147b52e858587270dba31c2aaa9e09wrowe || (!strcmp(accept_type, "text/html")
aa047239dedf0d26e8efecfade32e7337f35df19wrowe && (!strcmp(avail_type, INCLUDES_MAGIC_TYPE)
aa047239dedf0d26e8efecfade32e7337f35df19wrowe || !strcmp(avail_type, INCLUDES_MAGIC_TYPE3)))) {
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb if (accept_r->level >= avail->level) {
dc8692c6c0ca616a09aa12dad005f2ef23baa1a0wrowe avail->level_matched = avail->level;
dc8692c6c0ca616a09aa12dad005f2ef23baa1a0wrowe avail->mime_stars = 3;
dc8692c6c0ca616a09aa12dad005f2ef23baa1a0wrowe return 1;
dc8692c6c0ca616a09aa12dad005f2ef23baa1a0wrowe }
dc8692c6c0ca616a09aa12dad005f2ef23baa1a0wrowe }
dc8692c6c0ca616a09aa12dad005f2ef23baa1a0wrowe
dc8692c6c0ca616a09aa12dad005f2ef23baa1a0wrowe return OK;
dc8692c6c0ca616a09aa12dad005f2ef23baa1a0wrowe}
a2b181763cb35fd899feb4a436aeadaa80bf91eabrianp
aa047239dedf0d26e8efecfade32e7337f35df19wrowe/* This code implements a piece of the tie-breaking algorithm between
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * variants of equal quality. This piece is the treatment of variants
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * of the same base media type, but different levels. What we want to
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * return is the variant at the highest level that the client explicitly
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * claimed to accept.
0540a0b469147b52e858587270dba31c2aaa9e09wrowe *
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz * If all the variants available are at a higher level than that, or if
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * the client didn't say anything specific about this media type at all
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * and these variants just got in on a wildcard, we prefer the lowest
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * level, on grounds that that's the one that the client is least likely
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * to choke on.
aa047239dedf0d26e8efecfade32e7337f35df19wrowe *
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * (This is all motivated by treatment of levels in HTML --- we only
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * want to give level 3 to browsers that explicitly ask for it; browsers
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz * that don't, including HTTP/0.9 browsers that only get the implicit
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * "Accept: * / *" [space added to avoid confusing cpp --- no, that
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * syntax doesn't really work] should get HTML2 if available).
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz *
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * (Note that this code only comes into play when we are choosing among
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * variants of equal quality, where the draft standard gives us a fair
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * bit of leeway about what to do. It ain't specified by the standard;
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * rather, it is a choice made by this server about what to do in cases
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * where the standard does not specify a unique course of action).
aa047239dedf0d26e8efecfade32e7337f35df19wrowe */
c2cf53a40a9814eb91db2cdf820f97d943f21628coar
c2cf53a40a9814eb91db2cdf820f97d943f21628coarstatic int level_cmp(var_rec *var1, var_rec *var2)
c2cf53a40a9814eb91db2cdf820f97d943f21628coar{
aa047239dedf0d26e8efecfade32e7337f35df19wrowe /* Levels are only comparable between matching media types */
aa047239dedf0d26e8efecfade32e7337f35df19wrowe
aa047239dedf0d26e8efecfade32e7337f35df19wrowe if (var1->is_pseudo_html && !var2->is_pseudo_html) {
aa047239dedf0d26e8efecfade32e7337f35df19wrowe return 0;
aa047239dedf0d26e8efecfade32e7337f35df19wrowe }
a9a4544168a37b43bd180b3703ccee995f27a80awrowe
a9a4544168a37b43bd180b3703ccee995f27a80awrowe if (!var1->is_pseudo_html && strcmp(var1->mime_type, var2->mime_type)) {
a9a4544168a37b43bd180b3703ccee995f27a80awrowe return 0;
a9a4544168a37b43bd180b3703ccee995f27a80awrowe }
a9a4544168a37b43bd180b3703ccee995f27a80awrowe /* The result of the above if statements is that, if we get to
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * here, both variants have the same mime_type or both are
a9a4544168a37b43bd180b3703ccee995f27a80awrowe * pseudo-html.
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar */
aa047239dedf0d26e8efecfade32e7337f35df19wrowe
c2cf53a40a9814eb91db2cdf820f97d943f21628coar /* Take highest level that matched, if either did match. */
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar
aa047239dedf0d26e8efecfade32e7337f35df19wrowe if (var1->level_matched > var2->level_matched) {
aa047239dedf0d26e8efecfade32e7337f35df19wrowe return 1;
aa047239dedf0d26e8efecfade32e7337f35df19wrowe }
aa047239dedf0d26e8efecfade32e7337f35df19wrowe if (var1->level_matched < var2->level_matched) {
aa047239dedf0d26e8efecfade32e7337f35df19wrowe return -1;
0540a0b469147b52e858587270dba31c2aaa9e09wrowe }
aa047239dedf0d26e8efecfade32e7337f35df19wrowe
aa047239dedf0d26e8efecfade32e7337f35df19wrowe /* Neither matched. Take lowest level, if there's a difference. */
aa047239dedf0d26e8efecfade32e7337f35df19wrowe
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb if (var1->level < var2->level) {
aa047239dedf0d26e8efecfade32e7337f35df19wrowe return 1;
aa047239dedf0d26e8efecfade32e7337f35df19wrowe }
aa047239dedf0d26e8efecfade32e7337f35df19wrowe if (var1->level > var2->level) {
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb return -1;
0540a0b469147b52e858587270dba31c2aaa9e09wrowe }
aa047239dedf0d26e8efecfade32e7337f35df19wrowe
0540a0b469147b52e858587270dba31c2aaa9e09wrowe /* Tied */
0540a0b469147b52e858587270dba31c2aaa9e09wrowe
aa047239dedf0d26e8efecfade32e7337f35df19wrowe return 0;
aa047239dedf0d26e8efecfade32e7337f35df19wrowe}
c2cf53a40a9814eb91db2cdf820f97d943f21628coar
c2cf53a40a9814eb91db2cdf820f97d943f21628coar/* Finding languages. The main entry point is set_language_quality()
c2cf53a40a9814eb91db2cdf820f97d943f21628coar * which is called for each variant. It sets two elements in the
c2cf53a40a9814eb91db2cdf820f97d943f21628coar * variant record:
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * language_quality - the 'q' value of the 'best' matching language
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * from Accept-Language: header (HTTP/1.1)
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * lang_index - Non-negotiated language priority, using
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * position of language on the Accept-Language:
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * header, if present, else LanguagePriority
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * directive order.
0540a0b469147b52e858587270dba31c2aaa9e09wrowe *
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * When we do the variant checking for best variant, we use language
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * quality first, and if a tie, language_index next (this only applies
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * when _not_ using the RVSA/1.0 algorithm). If using the RVSA/1.0
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * algorithm, lang_index is never used.
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb *
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * set_language_quality() calls find_lang_index() and find_default_index()
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * to set lang_index.
aa047239dedf0d26e8efecfade32e7337f35df19wrowe */
aa047239dedf0d26e8efecfade32e7337f35df19wrowe
aa047239dedf0d26e8efecfade32e7337f35df19wrowestatic int find_lang_index(apr_array_header_t *accept_langs, char *lang)
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb{
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb const char **alang;
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar int i;
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz
aa047239dedf0d26e8efecfade32e7337f35df19wrowe if (!lang || !accept_langs) {
0540a0b469147b52e858587270dba31c2aaa9e09wrowe return -1;
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar }
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar
0540a0b469147b52e858587270dba31c2aaa9e09wrowe alang = (const char **) accept_langs->elts;
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar
aa047239dedf0d26e8efecfade32e7337f35df19wrowe for (i = 0; i < accept_langs->nelts; ++i) {
aa047239dedf0d26e8efecfade32e7337f35df19wrowe if (!strncmp(lang, *alang, strlen(*alang))) {
0540a0b469147b52e858587270dba31c2aaa9e09wrowe return i;
aa047239dedf0d26e8efecfade32e7337f35df19wrowe }
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb alang += (accept_langs->elt_size / sizeof(char*));
aa047239dedf0d26e8efecfade32e7337f35df19wrowe }
aa047239dedf0d26e8efecfade32e7337f35df19wrowe
aa047239dedf0d26e8efecfade32e7337f35df19wrowe return -1;
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar}
aa047239dedf0d26e8efecfade32e7337f35df19wrowe
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar/* set_default_lang_quality() sets the quality we apply to variants
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * which have no language assigned to them. If none of the variants
dc8692c6c0ca616a09aa12dad005f2ef23baa1a0wrowe * have a language, we are not negotiating on language, so all are
a9a4544168a37b43bd180b3703ccee995f27a80awrowe * acceptable, and we set the default q value to 1.0. However if
a9a4544168a37b43bd180b3703ccee995f27a80awrowe * some of the variants have languages, we set this default to 0.0001.
a9a4544168a37b43bd180b3703ccee995f27a80awrowe * The value of this default will be applied to all variants with
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * no explicit language -- which will have the effect of making them
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * acceptable, but only if no variants with an explicit language
dc8692c6c0ca616a09aa12dad005f2ef23baa1a0wrowe * are acceptable. The default q value set here is assigned to variants
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar * with no language type in set_language_quality().
aa047239dedf0d26e8efecfade32e7337f35df19wrowe *
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * Note that if using the RVSA/1.0 algorithm, we don't use this
aa047239dedf0d26e8efecfade32e7337f35df19wrowe * fiddle.
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar */
aa047239dedf0d26e8efecfade32e7337f35df19wrowe
dc8692c6c0ca616a09aa12dad005f2ef23baa1a0wrowestatic void set_default_lang_quality(negotiation_state *neg)
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb{
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb int j;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb if (!neg->dont_fiddle_headers) {
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb for (j = 0; j < neg->avail_vars->nelts; ++j) {
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb var_rec *variant = &avail_recs[j];
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb if (variant->content_languages &&
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb variant->content_languages->nelts) {
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb neg->default_lang_quality = 0.0001f;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb return;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb }
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb }
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb }
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb neg->default_lang_quality = 1.0f;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb}
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb/* Set the language_quality value in the variant record. Also
d2220a04f870f632b8cec1e6713dbb980ed5e386wrowe * assigns lang_index for ForceLanguagePriority.
d2220a04f870f632b8cec1e6713dbb980ed5e386wrowe *
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * To find the language_quality value, we look for the 'q' value
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * of the 'best' matching language on the Accept-Language
d2220a04f870f632b8cec1e6713dbb980ed5e386wrowe * header. The 'best' match is the language on Accept-Language
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * header which matches the language of this variant either fully,
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * or as far as the prefix marker (-). If two or more languages
d2220a04f870f632b8cec1e6713dbb980ed5e386wrowe * match, use the longest string from the Accept-Language header
d2220a04f870f632b8cec1e6713dbb980ed5e386wrowe * (see HTTP/1.1 [14.4])
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb *
e68544ae924174ca227ede8e2e722cefa00ea0d3wrowe * When a variant has multiple languages, we find the 'best'
e68544ae924174ca227ede8e2e722cefa00ea0d3wrowe * match for each variant language tag as above, then select the
e68544ae924174ca227ede8e2e722cefa00ea0d3wrowe * one with the highest q value. Because both the accept-header
e68544ae924174ca227ede8e2e722cefa00ea0d3wrowe * and variant can have multiple languages, we now have a hairy
e68544ae924174ca227ede8e2e722cefa00ea0d3wrowe * loop-within-a-loop here.
e68544ae924174ca227ede8e2e722cefa00ea0d3wrowe *
e68544ae924174ca227ede8e2e722cefa00ea0d3wrowe * If the variant has no language and we have no Accept-Language
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe * items, leave the quality at 1.0 and return.
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe *
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe * If the variant has no language, we use the default as set by
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe * set_default_lang_quality() (1.0 if we are not negotiating on
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe * language, 0.001 if we are).
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe *
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe * Following the setting of the language quality, we drop through to
fa06de8a28a737e8fbaad76d7f3ff67aaa5e4a09wrowe * set the old 'lang_index'. This is set based on either the order
e68544ae924174ca227ede8e2e722cefa00ea0d3wrowe * of the languages on the Accept-Language header, or the
e68544ae924174ca227ede8e2e722cefa00ea0d3wrowe * order on the LanguagePriority directive. This is only used
e68544ae924174ca227ede8e2e722cefa00ea0d3wrowe * in the negotiation if the language qualities tie.
e68544ae924174ca227ede8e2e722cefa00ea0d3wrowe */
e68544ae924174ca227ede8e2e722cefa00ea0d3wrowe
e68544ae924174ca227ede8e2e722cefa00ea0d3wrowestatic void set_language_quality(negotiation_state *neg, var_rec *variant)
e68544ae924174ca227ede8e2e722cefa00ea0d3wrowe{
e68544ae924174ca227ede8e2e722cefa00ea0d3wrowe int forcepriority = neg->conf->forcelangpriority;
e68544ae924174ca227ede8e2e722cefa00ea0d3wrowe if (forcepriority == FLP_UNDEF) {
e68544ae924174ca227ede8e2e722cefa00ea0d3wrowe forcepriority = FLP_DEFAULT;
e68544ae924174ca227ede8e2e722cefa00ea0d3wrowe }
e68544ae924174ca227ede8e2e722cefa00ea0d3wrowe
e68544ae924174ca227ede8e2e722cefa00ea0d3wrowe if (!variant->content_languages || !variant->content_languages->nelts) {
3ed53b42bfd638a63919b6cf2c2009a5a148f066brianp /* This variant has no content-language, so use the default
3ed53b42bfd638a63919b6cf2c2009a5a148f066brianp * quality factor for variants with no content-language
e68544ae924174ca227ede8e2e722cefa00ea0d3wrowe * (previously set by set_default_lang_quality()).
e68544ae924174ca227ede8e2e722cefa00ea0d3wrowe * Leave the factor alone (it remains at 1.0) when we may not fiddle
e68544ae924174ca227ede8e2e722cefa00ea0d3wrowe * with the headers.
e68544ae924174ca227ede8e2e722cefa00ea0d3wrowe */
d2220a04f870f632b8cec1e6713dbb980ed5e386wrowe if (!neg->dont_fiddle_headers) {
d2220a04f870f632b8cec1e6713dbb980ed5e386wrowe variant->lang_quality = neg->default_lang_quality;
d2220a04f870f632b8cec1e6713dbb980ed5e386wrowe }
d2220a04f870f632b8cec1e6713dbb980ed5e386wrowe if (!neg->accept_langs) {
d2220a04f870f632b8cec1e6713dbb980ed5e386wrowe return; /* no accept-language header */
d2220a04f870f632b8cec1e6713dbb980ed5e386wrowe }
d2220a04f870f632b8cec1e6713dbb980ed5e386wrowe return;
e68544ae924174ca227ede8e2e722cefa00ea0d3wrowe }
e68544ae924174ca227ede8e2e722cefa00ea0d3wrowe else {
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb /* Variant has one (or more) languages. Look for the best
c2cf53a40a9814eb91db2cdf820f97d943f21628coar * match. We do this by going through each language on the
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * variant description looking for a match on the
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * Accept-Language header. The best match is the longest
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * matching language on the header. The final result is the
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * best q value from all the languages on the variant
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * description.
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb */
68b29bcadd6c46aecdc9fe14c93555a2238ad2aagregames
68b29bcadd6c46aecdc9fe14c93555a2238ad2aagregames if (!neg->accept_langs) {
68b29bcadd6c46aecdc9fe14c93555a2238ad2aagregames /* no accept-language header makes the variant indefinite */
68b29bcadd6c46aecdc9fe14c93555a2238ad2aagregames variant->definite = 0;
68b29bcadd6c46aecdc9fe14c93555a2238ad2aagregames }
68b29bcadd6c46aecdc9fe14c93555a2238ad2aagregames else { /* There is an accept-language with 0 or more items */
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb accept_rec *accs = (accept_rec *) neg->accept_langs->elts;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb accept_rec *best = NULL, *star = NULL;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb accept_rec *bestthistag;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb char *lang, *p;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb float fiddle_q = 0.0f;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb int any_match_on_star = 0;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb int i, j;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb apr_size_t alen, longest_lang_range_len;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb for (j = 0; j < variant->content_languages->nelts; ++j) {
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb p = NULL;
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar bestthistag = NULL;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb longest_lang_range_len = 0;
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar alen = 0;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb /* lang is the variant's language-tag, which is the one
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * we are allowed to use the prefix of in HTTP/1.1
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar */
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar lang = ((char **) (variant->content_languages->elts))[j];
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar /* now find the best (i.e. longest) matching
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar * Accept-Language header language. We put the best match
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * for this tag in bestthistag. We cannot update the
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * overall best (based on q value) because the best match
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * for this tag is the longest language item on the accept
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * header, not necessarily the highest q.
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe */
b67fb549910fa0faf4cdd8aeaf9aeab51d4b6a92wrowe for (i = 0; i < neg->accept_langs->nelts; ++i) {
b67fb549910fa0faf4cdd8aeaf9aeab51d4b6a92wrowe if (!strcmp(accs[i].name, "*")) {
b67fb549910fa0faf4cdd8aeaf9aeab51d4b6a92wrowe if (!star) {
b67fb549910fa0faf4cdd8aeaf9aeab51d4b6a92wrowe star = &accs[i];
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb }
b67fb549910fa0faf4cdd8aeaf9aeab51d4b6a92wrowe continue;
b67fb549910fa0faf4cdd8aeaf9aeab51d4b6a92wrowe }
b67fb549910fa0faf4cdd8aeaf9aeab51d4b6a92wrowe /* Find language. We match if either the variant
b67fb549910fa0faf4cdd8aeaf9aeab51d4b6a92wrowe * language tag exactly matches the language range
d2220a04f870f632b8cec1e6713dbb980ed5e386wrowe * from the accept header, or a prefix of the variant
b45c1c292ff1fa635004ae81fa691f8cb3cdda85rbb * language tag up to a '-' character matches the
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * whole of the language range in the Accept-Language
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * header. Note that HTTP/1.x allows any number of
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * '-' characters in a tag or range, currently only
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * tags with zero or one '-' characters are defined
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar * for general use (see rfc1766).
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb *
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar * We only use language range in the Accept-Language
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * header the best match for the variant language tag
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * if it is longer than the previous best match.
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb */
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb alen = strlen(accs[i].name);
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe if ((strlen(lang) >= alen) &&
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb !strncmp(lang, accs[i].name, alen) &&
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb ((lang[alen] == 0) || (lang[alen] == '-')) ) {
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb if (alen > longest_lang_range_len) {
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb longest_lang_range_len = alen;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb bestthistag = &accs[i];
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb }
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb }
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb if (!bestthistag && !neg->dont_fiddle_headers) {
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb /* The next bit is a fiddle. Some browsers might
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * be configured to send more specific language
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * ranges than desirable. For example, an
efa1a34b0a7785fc72863eff175b0cfc1ecb0e38wrowe * Accept-Language of en-US should never match
117026201e6d8fe7d82416b8a7324830f5a87292wrowe * variants with languages en or en-GB. But US
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe * English speakers might pick en-US as their
117026201e6d8fe7d82416b8a7324830f5a87292wrowe * language choice. So this fiddle checks if the
117026201e6d8fe7d82416b8a7324830f5a87292wrowe * language range has a prefix, and if so, it
117026201e6d8fe7d82416b8a7324830f5a87292wrowe * matches variants which match that prefix with a
117026201e6d8fe7d82416b8a7324830f5a87292wrowe * priority of 0.001. So a request for en-US would
117026201e6d8fe7d82416b8a7324830f5a87292wrowe * match variants of types en and en-GB, but at
6c24fd6cfe148639988d5b335185ffb215662801wrowe * much lower priority than matches of en-US
117026201e6d8fe7d82416b8a7324830f5a87292wrowe * directly, or of any other language listed on
d2220a04f870f632b8cec1e6713dbb980ed5e386wrowe * the Accept-Language header. Note that this
117026201e6d8fe7d82416b8a7324830f5a87292wrowe * fiddle does not handle multi-level prefixes.
cadddb2c31d24d48f4017db4df0a29687432326cwrowe */
cadddb2c31d24d48f4017db4df0a29687432326cwrowe if ((p = strchr(accs[i].name, '-'))) {
117026201e6d8fe7d82416b8a7324830f5a87292wrowe int plen = p - accs[i].name;
f888346b48f5e5b5e3f0a47dedb8cefd2759a4e2gregames
f888346b48f5e5b5e3f0a47dedb8cefd2759a4e2gregames if (!strncmp(lang, accs[i].name, plen)) {
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe fiddle_q = 0.001f;
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe }
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe }
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe }
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe }
f888346b48f5e5b5e3f0a47dedb8cefd2759a4e2gregames /* Finished looking at Accept-Language headers, the best
f888346b48f5e5b5e3f0a47dedb8cefd2759a4e2gregames * (longest) match is in bestthistag, or NULL if no match
f888346b48f5e5b5e3f0a47dedb8cefd2759a4e2gregames */
f888346b48f5e5b5e3f0a47dedb8cefd2759a4e2gregames if (!best ||
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe (bestthistag && bestthistag->quality > best->quality)) {
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe best = bestthistag;
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe }
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe /* See if the tag matches on a * in the Accept-Language
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe * header. If so, record this fact for later use
f888346b48f5e5b5e3f0a47dedb8cefd2759a4e2gregames */
f888346b48f5e5b5e3f0a47dedb8cefd2759a4e2gregames if (!bestthistag && star) {
f888346b48f5e5b5e3f0a47dedb8cefd2759a4e2gregames any_match_on_star = 1;
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe }
f888346b48f5e5b5e3f0a47dedb8cefd2759a4e2gregames }
cadddb2c31d24d48f4017db4df0a29687432326cwrowe
117026201e6d8fe7d82416b8a7324830f5a87292wrowe /* If one of the language tags of the variant matched on *, we
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar * need to see if its q is better than that of any non-* match
e7505ba54ac56ae30e4e250f912f3dbaf92ca45fwrowe * on any other tag of the variant. If so the * match takes
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar * precedence and the overall match is not definite.
e7505ba54ac56ae30e4e250f912f3dbaf92ca45fwrowe */
cadddb2c31d24d48f4017db4df0a29687432326cwrowe if ( any_match_on_star &&
cadddb2c31d24d48f4017db4df0a29687432326cwrowe ((best && star->quality > best->quality) ||
cadddb2c31d24d48f4017db4df0a29687432326cwrowe (!best)) ) {
cadddb2c31d24d48f4017db4df0a29687432326cwrowe best = star;
5b3abd2fecc712f08ad728114aa77137b9f67716wrowe variant->definite = 0;
85bb5b92490e4f095aae394118fc588a8f4c486fwrowe }
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe
efa1a34b0a7785fc72863eff175b0cfc1ecb0e38wrowe variant->lang_quality = best ? best->quality : fiddle_q;
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe }
beda1fb2f11c52ca4612460a5d5ba47398143efbwrowe }
beda1fb2f11c52ca4612460a5d5ba47398143efbwrowe
beda1fb2f11c52ca4612460a5d5ba47398143efbwrowe /* Handle the ForceDefaultLanguage overrides, based on the best match
beda1fb2f11c52ca4612460a5d5ba47398143efbwrowe * to LanguagePriority order. The best match is the lowest index of
beda1fb2f11c52ca4612460a5d5ba47398143efbwrowe * any LanguagePriority match.
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe */
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe if (((forcepriority & FLP_PREFER)
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe && (variant->lang_index < 0))
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe || ((forcepriority & FLP_FALLBACK)
c2cf53a40a9814eb91db2cdf820f97d943f21628coar && !variant->lang_quality))
c2cf53a40a9814eb91db2cdf820f97d943f21628coar {
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe int bestidx = -1;
c2cf53a40a9814eb91db2cdf820f97d943f21628coar int j;
117026201e6d8fe7d82416b8a7324830f5a87292wrowe
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar for (j = 0; j < variant->content_languages->nelts; ++j)
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe {
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe /* lang is the variant's language-tag, which is the one
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar * we are allowed to use the prefix of in HTTP/1.1
290ecc1ddceca1ed49bc1a5338921264b5c3e07cwrowe */
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar char *lang = ((char **) (variant->content_languages->elts))[j];
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar int idx = -1;
117026201e6d8fe7d82416b8a7324830f5a87292wrowe
117026201e6d8fe7d82416b8a7324830f5a87292wrowe /* If we wish to fallback or
c2cf53a40a9814eb91db2cdf820f97d943f21628coar * we use our own LanguagePriority index.
117026201e6d8fe7d82416b8a7324830f5a87292wrowe */
1067418d9ed9ed9daeb3ca4f74e72db810c49833wrowe idx = find_lang_index(neg->conf->language_priority, lang);
cadddb2c31d24d48f4017db4df0a29687432326cwrowe if ((idx >= 0) && ((bestidx == -1) || (idx < bestidx))) {
cadddb2c31d24d48f4017db4df0a29687432326cwrowe bestidx = idx;
cadddb2c31d24d48f4017db4df0a29687432326cwrowe }
cadddb2c31d24d48f4017db4df0a29687432326cwrowe }
85bb5b92490e4f095aae394118fc588a8f4c486fwrowe
c2cf53a40a9814eb91db2cdf820f97d943f21628coar if (bestidx >= 0) {
c2cf53a40a9814eb91db2cdf820f97d943f21628coar if (variant->lang_quality) {
cadddb2c31d24d48f4017db4df0a29687432326cwrowe if (forcepriority & FLP_PREFER) {
cadddb2c31d24d48f4017db4df0a29687432326cwrowe variant->lang_index = bestidx;
cadddb2c31d24d48f4017db4df0a29687432326cwrowe }
cadddb2c31d24d48f4017db4df0a29687432326cwrowe }
cadddb2c31d24d48f4017db4df0a29687432326cwrowe else {
cadddb2c31d24d48f4017db4df0a29687432326cwrowe if (forcepriority & FLP_FALLBACK) {
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe variant->lang_index = bestidx;
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe variant->lang_quality = .0001f;
cadddb2c31d24d48f4017db4df0a29687432326cwrowe variant->definite = 0;
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe }
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe }
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe }
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar }
cadddb2c31d24d48f4017db4df0a29687432326cwrowe return;
cadddb2c31d24d48f4017db4df0a29687432326cwrowe}
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe/* Determining the content length --- if the map didn't tell us,
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe * we have to do a stat() and remember for next time.
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe */
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowestatic apr_off_t find_content_length(negotiation_state *neg, var_rec *variant)
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe{
3cd826b00280881e5a2f03d8ec1f8d55802b93dewrowe apr_finfo_t statb;
cadddb2c31d24d48f4017db4df0a29687432326cwrowe
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe if (variant->bytes < 0) {
1067418d9ed9ed9daeb3ca4f74e72db810c49833wrowe if (variant->sub_req && (variant->sub_req->finfo.valid & APR_FINFO_SIZE)) {
1067418d9ed9ed9daeb3ca4f74e72db810c49833wrowe variant->bytes = variant->sub_req->finfo.size;
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe }
1067418d9ed9ed9daeb3ca4f74e72db810c49833wrowe else {
117026201e6d8fe7d82416b8a7324830f5a87292wrowe char *fullname = ap_make_full_path(neg->pool, neg->dir_name,
117026201e6d8fe7d82416b8a7324830f5a87292wrowe variant->file_name);
b67fb549910fa0faf4cdd8aeaf9aeab51d4b6a92wrowe
c2cf53a40a9814eb91db2cdf820f97d943f21628coar if (apr_stat(&statb, fullname,
c2cf53a40a9814eb91db2cdf820f97d943f21628coar APR_FINFO_SIZE, neg->pool) == APR_SUCCESS) {
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb variant->bytes = statb.size;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb }
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb }
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb }
5bb29f57ae0184d2b3c1cdf35132f8ceb011f882wrowe
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb return variant->bytes;
d2220a04f870f632b8cec1e6713dbb980ed5e386wrowe}
b45c1c292ff1fa635004ae81fa691f8cb3cdda85rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb/* For a given variant, find the best matching Accept: header
5bb29f57ae0184d2b3c1cdf35132f8ceb011f882wrowe * and assign the Accept: header's quality value to the
5bb29f57ae0184d2b3c1cdf35132f8ceb011f882wrowe * mime_type_quality field of the variant, for later use in
ecc4a080f07af3fbc1b91bbd00997ec1d592c6f9wrowe * determining the best matching variant.
5bb29f57ae0184d2b3c1cdf35132f8ceb011f882wrowe */
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar
2d2dadb81bf34e3bc9321eabcd971a738431b364wrowestatic void set_accept_quality(negotiation_state *neg, var_rec *variant)
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar{
5bb29f57ae0184d2b3c1cdf35132f8ceb011f882wrowe int i;
5bb29f57ae0184d2b3c1cdf35132f8ceb011f882wrowe accept_rec *accept_recs;
5bb29f57ae0184d2b3c1cdf35132f8ceb011f882wrowe float q = 0.0f;
5bb29f57ae0184d2b3c1cdf35132f8ceb011f882wrowe int q_definite = 1;
5bb29f57ae0184d2b3c1cdf35132f8ceb011f882wrowe
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar /* if no Accept: header, leave quality alone (will
ecc4a080f07af3fbc1b91bbd00997ec1d592c6f9wrowe * remain at the default value of 1)
731344ed8f3677d1661c261ca5fcdd2ee3dbc74ccoar *
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * XXX: This if is currently never true because of the effect of
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * maybe_add_default_accepts().
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb */
5bb29f57ae0184d2b3c1cdf35132f8ceb011f882wrowe if (!neg->accepts) {
5b3abd2fecc712f08ad728114aa77137b9f67716wrowe if (variant->mime_type && *variant->mime_type)
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb variant->definite = 0;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb return;
af7e32b660b02a378e91d40987e59b28864db954jwoolley }
c2cf53a40a9814eb91db2cdf820f97d943f21628coar
c2cf53a40a9814eb91db2cdf820f97d943f21628coar accept_recs = (accept_rec *) neg->accepts->elts;
0540a0b469147b52e858587270dba31c2aaa9e09wrowe
0540a0b469147b52e858587270dba31c2aaa9e09wrowe /*
0540a0b469147b52e858587270dba31c2aaa9e09wrowe * Go through each of the ranges on the Accept: header,
c2cf53a40a9814eb91db2cdf820f97d943f21628coar * looking for the 'best' match with this variant's
c2cf53a40a9814eb91db2cdf820f97d943f21628coar * content-type. We use the best match's quality
0540a0b469147b52e858587270dba31c2aaa9e09wrowe * value (from the Accept: header) for this variant's
c2cf53a40a9814eb91db2cdf820f97d943f21628coar * mime_type_quality field.
0540a0b469147b52e858587270dba31c2aaa9e09wrowe *
0540a0b469147b52e858587270dba31c2aaa9e09wrowe * The best match is determined like this:
0540a0b469147b52e858587270dba31c2aaa9e09wrowe * type/type is better than type/ * is better than * / *
0540a0b469147b52e858587270dba31c2aaa9e09wrowe * if match is type/type, use the level mime param if available
c2cf53a40a9814eb91db2cdf820f97d943f21628coar */
0540a0b469147b52e858587270dba31c2aaa9e09wrowe for (i = 0; i < neg->accepts->nelts; ++i) {
c2cf53a40a9814eb91db2cdf820f97d943f21628coar
0540a0b469147b52e858587270dba31c2aaa9e09wrowe accept_rec *type = &accept_recs[i];
7763a4beb8afca9c8f93db0cb6836124901af52awrowe int prev_mime_stars;
0540a0b469147b52e858587270dba31c2aaa9e09wrowe
0540a0b469147b52e858587270dba31c2aaa9e09wrowe prev_mime_stars = variant->mime_stars;
c2cf53a40a9814eb91db2cdf820f97d943f21628coar
c2cf53a40a9814eb91db2cdf820f97d943f21628coar if (!mime_match(type, variant)) {
0540a0b469147b52e858587270dba31c2aaa9e09wrowe continue; /* didn't match the content type at all */
0540a0b469147b52e858587270dba31c2aaa9e09wrowe }
0540a0b469147b52e858587270dba31c2aaa9e09wrowe else {
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz /* did match - see if there were less or more stars than
0540a0b469147b52e858587270dba31c2aaa9e09wrowe * in previous match
0540a0b469147b52e858587270dba31c2aaa9e09wrowe */
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb if (prev_mime_stars == variant->mime_stars) {
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb continue; /* more stars => not as good a match */
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz }
5b3abd2fecc712f08ad728114aa77137b9f67716wrowe }
5b3abd2fecc712f08ad728114aa77137b9f67716wrowe
5b3abd2fecc712f08ad728114aa77137b9f67716wrowe /* If we are allowed to mess with the q-values
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz * and have no explicit q= parameters in the accept header,
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * make wildcards very low, so we have a low chance
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * of ending up with them if there's something better.
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb */
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb if (!neg->dont_fiddle_headers && !neg->accept_q &&
58097d7d8d1a394092374b9f6ddf76b7993724a4rbb variant->mime_stars == 1) {
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb q = 0.01f;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb }
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe else if (!neg->dont_fiddle_headers && !neg->accept_q &&
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb variant->mime_stars == 2) {
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb q = 0.02f;
83a8dc5a596a8a1b9d14f063268287d123b9ed7ewrowe }
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb else {
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb q = type->quality;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb }
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb q_definite = (variant->mime_stars == 3);
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb }
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb variant->mime_type_quality = q;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb variant->definite = variant->definite && q_definite;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb}
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb/* For a given variant, find the 'q' value of the charset given
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * on the Accept-Charset line. If no charsets are listed,
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * assume value of '1'.
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb */
b38846b15c8891c6dec44dcc4f96ca40721bf663rbbstatic void set_charset_quality(negotiation_state *neg, var_rec *variant)
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb{
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb int i;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb accept_rec *accept_recs;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb const char *charset = variant->content_charset;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb accept_rec *star = NULL;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb /* if no Accept-Charset: header, leave quality alone (will
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb * remain at the default value of 1)
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb */
d4abb06ac220bb280ae996b6d21bbd257db51bb1jerenkrantz if (!neg->accept_charsets) {
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb if (charset && *charset)
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb variant->definite = 0;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb return;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb }
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb accept_recs = (accept_rec *) neg->accept_charsets->elts;
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb if (charset == NULL || !*charset) {
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb /* Charset of variant not known */
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb /* if not a text / * type, leave quality alone */
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb if (!(!strncmp(variant->mime_type, "text/", 5)
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb || !strcmp(variant->mime_type, INCLUDES_MAGIC_TYPE)
b38846b15c8891c6dec44dcc4f96ca40721bf663rbb || !strcmp(variant->mime_type, INCLUDES_MAGIC_TYPE3)
))
return;
/* Don't go guessing if we are in strict header mode,
* e.g. when running the rvsa, as any guess won't be reflected
* in the variant list or content-location headers.
*/
if (neg->dont_fiddle_headers)
return;
charset = "iso-8859-1"; /* The default charset for HTTP text types */
}
/*
* Go through each of the items on the Accept-Charset header,
* looking for a match with this variant's charset. If none
* match, charset is unacceptable, so set quality to 0.
*/
for (i = 0; i < neg->accept_charsets->nelts; ++i) {
accept_rec *type = &accept_recs[i];
if (!strcmp(type->name, charset)) {
variant->charset_quality = type->quality;
return;
}
else if (strcmp(type->name, "*") == 0) {
star = type;
}
}
/* No explicit match */
if (star) {
variant->charset_quality = star->quality;
variant->definite = 0;
return;
}
/* If this variant is in charset iso-8859-1, the default is 1.0 */
if (strcmp(charset, "iso-8859-1") == 0) {
variant->charset_quality = 1.0f;
}
else {
variant->charset_quality = 0.0f;
}
}
/* is_identity_encoding is included for back-compat, but does anyone
* use 7bit, 8bin or binary in their var files??
*/
static int is_identity_encoding(const char *enc)
{
return (!enc || !enc[0] || !strcmp(enc, "7bit") || !strcmp(enc, "8bit")
|| !strcmp(enc, "binary"));
}
/*
* set_encoding_quality determines whether the encoding for a particular
* variant is acceptable for the user-agent.
*
* The rules for encoding are that if the user-agent does not supply
* any Accept-Encoding header, then all encodings are allowed but a
* variant with no encoding should be preferred.
* If there is an empty Accept-Encoding header, then no encodings are
* acceptable. If there is a non-empty Accept-Encoding header, then
* any of the listed encodings are acceptable, as well as no encoding
* unless the "identity" encoding is specifically excluded.
*/
static void set_encoding_quality(negotiation_state *neg, var_rec *variant)
{
accept_rec *accept_recs;
const char *enc = variant->content_encoding;
accept_rec *star = NULL;
float value_if_not_found = 0.0f;
int i;
if (!neg->accept_encodings) {
/* We had no Accept-Encoding header, assume that all
* encodings are acceptable with a low quality,
* but we prefer no encoding if available.
*/
if (!enc || is_identity_encoding(enc))
variant->encoding_quality = 1.0f;
else
variant->encoding_quality = 0.5f;
return;
}
if (!enc || is_identity_encoding(enc)) {
enc = "identity";
value_if_not_found = 0.0001f;
}
accept_recs = (accept_rec *) neg->accept_encodings->elts;
/* Go through each of the encodings on the Accept-Encoding: header,
* looking for a match with our encoding. x- prefixes are ignored.
*/
if (enc[0] == 'x' && enc[1] == '-') {
enc += 2;
}
for (i = 0; i < neg->accept_encodings->nelts; ++i) {
char *name = accept_recs[i].name;
if (name[0] == 'x' && name[1] == '-') {
name += 2;
}
if (!strcmp(name, enc)) {
variant->encoding_quality = accept_recs[i].quality;
return;
}
if (strcmp(name, "*") == 0) {
star = &accept_recs[i];
}
}
/* No explicit match */
if (star) {
variant->encoding_quality = star->quality;
return;
}
/* Encoding not found on Accept-Encoding: header, so it is
* _not_ acceptable unless it is the identity (no encoding)
*/
variant->encoding_quality = value_if_not_found;
}
/*************************************************************
* Possible results of the variant selection algorithm
*/
enum algorithm_results {
alg_choice = 1, /* choose variant */
alg_list /* list variants */
};
/* Below is the 'best_match' function. It returns an int, which has
* one of the two values alg_choice or alg_list, which give the result
* of the variant selection algorithm. alg_list means that no best
* variant was found by the algorithm, alg_choice means that a best
* variant was found and should be returned. The list/choice
* terminology comes from TCN (rfc2295), but is used in a more generic
* way here. The best variant is returned in *pbest. best_match has
* two possible algorithms for determining the best variant: the
* RVSA/1.0 algorithm (from RFC2296), and the standard Apache
* algorithm. These are split out into separate functions
* (is_variant_better_rvsa() and is_variant_better()). Selection of
* one is through the neg->use_rvsa flag.
*
* The call to best_match also creates full information, including
* language, charset, etc quality for _every_ variant. This is needed
* for generating a correct Vary header, and can be used for the
* Alternates header, the human-readable list responses and 406 errors.
*/
/* Firstly, the RVSA/1.0 (HTTP Remote Variant Selection Algorithm
* v1.0) from rfc2296. This is the algorithm that goes together with
* transparent content negotiation (TCN).
*/
static int is_variant_better_rvsa(negotiation_state *neg, var_rec *variant,
var_rec *best, float *p_bestq)
{
float bestq = *p_bestq, q;
/* TCN does not cover negotiation on content-encoding. For now,
* we ignore the encoding unless it was explicitly excluded.
*/
if (variant->encoding_quality == 0.0f)
return 0;
q = variant->mime_type_quality *
variant->source_quality *
variant->charset_quality *
variant->lang_quality;
/* RFC 2296 calls for the result to be rounded to 5 decimal places,
* but we don't do that because it serves no useful purpose other
* than to ensure that a remote algorithm operates on the same
* precision as ours. That is silly, since what we obviously want
* is for the algorithm to operate on the best available precision
* regardless of who runs it. Since the above calculation may
* result in significant variance at 1e-12, rounding would be bogus.
*/
#ifdef NEG_DEBUG
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
"Variant: file=%s type=%s lang=%s sourceq=%1.3f "
"mimeq=%1.3f langq=%1.3f charq=%1.3f encq=%1.3f "
"q=%1.5f definite=%d",
(variant->file_name ? variant->file_name : ""),
(variant->mime_type ? variant->mime_type : ""),
(variant->content_languages
? apr_array_pstrcat(neg->pool, variant->content_languages, ',')
: ""),
variant->source_quality,
variant->mime_type_quality,
variant->lang_quality,
variant->charset_quality,
variant->encoding_quality,
q,
variant->definite);
#endif
if (q <= 0.0f) {
return 0;
}
if (q > bestq) {
*p_bestq = q;
return 1;
}
if (q == bestq) {
/* If the best variant's encoding is of lesser quality than
* this variant, then we prefer this variant
*/
if (variant->encoding_quality > best->encoding_quality) {
*p_bestq = q;
return 1;
}
}
return 0;
}
/* Negotiation algorithm as used by previous versions of Apache
* (just about).
*/
static int is_variant_better(negotiation_state *neg, var_rec *variant,
var_rec *best, float *p_bestq)
{
float bestq = *p_bestq, q;
int levcmp;
/* For non-transparent negotiation, server can choose how
* to handle the negotiation. We'll use the following in
* order: content-type, language, content-type level, charset,
* content encoding, content length.
*
* For each check, we have three possible outcomes:
* This variant is worse than current best: return 0
* This variant is better than the current best:
* assign this variant's q to *p_bestq, and return 1
* This variant is just as desirable as the current best:
* drop through to the next test.
*
* This code is written in this long-winded way to allow future
* customisation, either by the addition of additional
* checks, or to allow the order of the checks to be determined
* by configuration options (e.g. we might prefer to check
* language quality _before_ content type).
*/
/* First though, eliminate this variant if it is not
* acceptable by type, charset, encoding or language.
*/
#ifdef NEG_DEBUG
ap_log_error(APLOG_MARK, APLOG_STARTUP, 0, NULL,
"Variant: file=%s type=%s lang=%s sourceq=%1.3f "
"mimeq=%1.3f langq=%1.3f langidx=%d charq=%1.3f encq=%1.3f ",
(variant->file_name ? variant->file_name : ""),
(variant->mime_type ? variant->mime_type : ""),
(variant->content_languages
? apr_array_pstrcat(neg->pool, variant->content_languages, ',')
: ""),
variant->source_quality,
variant->mime_type_quality,
variant->lang_quality,
variant->lang_index,
variant->charset_quality,
variant->encoding_quality);
#endif
if (variant->encoding_quality == 0.0f ||
variant->lang_quality == 0.0f ||
variant->source_quality == 0.0f ||
variant->charset_quality == 0.0f ||
variant->mime_type_quality == 0.0f) {
return 0; /* don't consider unacceptables */
}
q = variant->mime_type_quality * variant->source_quality;
if (q == 0.0 || q < bestq) {
return 0;
}
if (q > bestq || !best) {
*p_bestq = q;
return 1;
}
/* language */
if (variant->lang_quality < best->lang_quality) {
return 0;
}
if (variant->lang_quality > best->lang_quality) {
*p_bestq = q;
return 1;
}
/* if language qualities were equal, try the LanguagePriority stuff */
if (best->lang_index != -1 &&
(variant->lang_index == -1 || variant->lang_index > best->lang_index)) {
return 0;
}
if (variant->lang_index != -1 &&
(best->lang_index == -1 || variant->lang_index < best->lang_index)) {
*p_bestq = q;
return 1;
}
/* content-type level (sometimes used with text/html, though we
* support it on other types too)
*/
levcmp = level_cmp(variant, best);
if (levcmp == -1) {
return 0;
}
if (levcmp == 1) {
*p_bestq = q;
return 1;
}
/* charset */
if (variant->charset_quality < best->charset_quality) {
return 0;
}
/* If the best variant's charset is ISO-8859-1 and this variant has
* the same charset quality, then we prefer this variant
*/
if (variant->charset_quality > best->charset_quality ||
((variant->content_charset != NULL &&
*variant->content_charset != '\0' &&
strcmp(variant->content_charset, "iso-8859-1") != 0) &&
(best->content_charset == NULL ||
*best->content_charset == '\0' ||
strcmp(best->content_charset, "iso-8859-1") == 0))) {
*p_bestq = q;
return 1;
}
/* Prefer the highest value for encoding_quality.
*/
if (variant->encoding_quality < best->encoding_quality) {
return 0;
}
if (variant->encoding_quality > best->encoding_quality) {
*p_bestq = q;
return 1;
}
/* content length if all else equal */
if (find_content_length(neg, variant) >= find_content_length(neg, best)) {
return 0;
}
/* ok, to get here means every thing turned out equal, except
* we have a shorter content length, so use this variant
*/
*p_bestq = q;
return 1;
}
static int best_match(negotiation_state *neg, var_rec **pbest)
{
int j;
var_rec *best = NULL;
float bestq = 0.0f;
enum algorithm_results algorithm_result;
var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
set_default_lang_quality(neg);
/*
* Find the 'best' variant
*/
for (j = 0; j < neg->avail_vars->nelts; ++j) {
var_rec *variant = &avail_recs[j];
/* Find all the relevant 'quality' values from the
* Accept... headers, and store in the variant. This also
* prepares for sending an Alternates header etc so we need to
* do it even if we do not actually plan to find a best
* variant.
*/
set_accept_quality(neg, variant);
set_language_quality(neg, variant);
set_encoding_quality(neg, variant);
set_charset_quality(neg, variant);
/* Only do variant selection if we may actually choose a
* variant for the client
*/
if (neg->may_choose) {
/* Now find out if this variant is better than the current
* best, either using the RVSA/1.0 algorithm, or Apache's
* internal server-driven algorithm. Presumably other
* server-driven algorithms are possible, and could be
* implemented here.
*/
if (neg->use_rvsa) {
if (is_variant_better_rvsa(neg, variant, best, &bestq)) {
best = variant;
}
}
else {
if (is_variant_better(neg, variant, best, &bestq)) {
best = variant;
}
}
}
}
/* We now either have a best variant, or no best variant */
if (neg->use_rvsa) {
/* calculate result for RVSA/1.0 algorithm:
* only a choice response if the best variant has q>0
* and is definite
*/
algorithm_result = (best && best->definite) && (bestq > 0) ?
alg_choice : alg_list;
}
else {
/* calculate result for Apache negotiation algorithm */
algorithm_result = bestq > 0 ? alg_choice : alg_list;
}
/* Returning a choice response with a non-neighboring variant is a
* protocol security error in TCN (see rfc2295). We do *not*
* verify here that the variant and URI are neighbors, even though
* we may return alg_choice. We depend on the environment (the
* caller) to only declare the resource transparently negotiable if
* all variants are neighbors.
*/
*pbest = best;
return algorithm_result;
}
/* Sets response headers for a negotiated response.
* neg->is_transparent determines whether a transparently negotiated
* response or a plain `server driven negotiation' response is
* created. Applicable headers are Alternates, Vary, and TCN.
*
* The Vary header we create is sometimes longer than is required for
* the correct caching of negotiated results by HTTP/1.1 caches. For
* example if we have 3 variants x.html, x.ps.en and x.ps.nl, and if
* the Accept: header assigns a 0 quality to .ps, then the results of
* the two server-side negotiation algorithms we currently implement
* will never depend on Accept-Language so we could return `Vary:
* negotiate, accept' instead of the longer 'Vary: negotiate, accept,
* accept-language' which the code below will return. A routine for
* computing the exact minimal Vary header would be a huge pain to code
* and maintain though, especially because we need to take all possible
* twiddles in the server-side negotiation algorithms into account.
*/
static void set_neg_headers(request_rec *r, negotiation_state *neg,
int alg_result)
{
apr_table_t *hdrs;
var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
const char *sample_type = NULL;
const char *sample_language = NULL;
const char *sample_encoding = NULL;
const char *sample_charset = NULL;
char *lang;
char *qstr;
char *lenstr;
apr_off_t len;
apr_array_header_t *arr;
int max_vlist_array = (neg->avail_vars->nelts * 21);
int first_variant = 1;
int vary_by_type = 0;
int vary_by_language = 0;
int vary_by_charset = 0;
int vary_by_encoding = 0;
int j;
/* In order to avoid O(n^2) memory copies in building Alternates,
* we preallocate a apr_table_t with the maximum substrings possible,
* fill it with the variant list, and then concatenate the entire array.
* Note that if you change the number of substrings pushed, you also
* need to change the calculation of max_vlist_array above.
*/
if (neg->send_alternates && neg->avail_vars->nelts)
arr = apr_array_make(r->pool, max_vlist_array, sizeof(char *));
else
arr = NULL;
/* Put headers into err_headers_out, since send_http_header()
* outputs both headers_out and err_headers_out.
*/
hdrs = r->err_headers_out;
for (j = 0; j < neg->avail_vars->nelts; ++j) {
var_rec *variant = &avail_recs[j];
if (variant->content_languages && variant->content_languages->nelts) {
lang = apr_array_pstrcat(r->pool, variant->content_languages, ',');
}
else {
lang = NULL;
}
/* Calculate Vary by looking for any difference between variants */
if (first_variant) {
sample_type = variant->mime_type;
sample_charset = variant->content_charset;
sample_language = lang;
sample_encoding = variant->content_encoding;
}
else {
if (!vary_by_type &&
strcmp(sample_type ? sample_type : "",
variant->mime_type ? variant->mime_type : "")) {
vary_by_type = 1;
}
if (!vary_by_charset &&
strcmp(sample_charset ? sample_charset : "",
variant->content_charset ?
variant->content_charset : "")) {
vary_by_charset = 1;
}
if (!vary_by_language &&
strcmp(sample_language ? sample_language : "",
lang ? lang : "")) {
vary_by_language = 1;
}
if (!vary_by_encoding &&
strcmp(sample_encoding ? sample_encoding : "",
variant->content_encoding ?
variant->content_encoding : "")) {
vary_by_encoding = 1;
}
}
first_variant = 0;
if (!neg->send_alternates)
continue;
/* Generate the string components for this Alternates entry */
*((const char **) apr_array_push(arr)) = "{\"";
*((const char **) apr_array_push(arr)) = variant->file_name;
*((const char **) apr_array_push(arr)) = "\" ";
qstr = (char *) apr_palloc(r->pool, 6);
apr_snprintf(qstr, 6, "%1.3f", variant->source_quality);
/* Strip trailing zeros (saves those valuable network bytes) */
if (qstr[4] == '0') {
qstr[4] = '\0';
if (qstr[3] == '0') {
qstr[3] = '\0';
if (qstr[2] == '0') {
qstr[1] = '\0';
}
}
}
*((const char **) apr_array_push(arr)) = qstr;
if (variant->mime_type && *variant->mime_type) {
*((const char **) apr_array_push(arr)) = " {type ";
*((const char **) apr_array_push(arr)) = variant->mime_type;
*((const char **) apr_array_push(arr)) = "}";
}
if (variant->content_charset && *variant->content_charset) {
*((const char **) apr_array_push(arr)) = " {charset ";
*((const char **) apr_array_push(arr)) = variant->content_charset;
*((const char **) apr_array_push(arr)) = "}";
}
if (lang) {
*((const char **) apr_array_push(arr)) = " {language ";
*((const char **) apr_array_push(arr)) = lang;
*((const char **) apr_array_push(arr)) = "}";
}
if (variant->content_encoding && *variant->content_encoding) {
/* Strictly speaking, this is non-standard, but so is TCN */
*((const char **) apr_array_push(arr)) = " {encoding ";
*((const char **) apr_array_push(arr)) = variant->content_encoding;
*((const char **) apr_array_push(arr)) = "}";
}
/* Note that the Alternates specification (in rfc2295) does
* not require that we include {length x}, so we could omit it
* if determining the length is too expensive. We currently
* always include it though. 22 bytes is enough for 2^64.
*
* If the variant is a CGI script, find_content_length would
* return the length of the script, not the output it
* produces, so we check for the presence of a handler and if
* there is one we don't add a length.
*
* XXX: TODO: This check does not detect a CGI script if we
* get the variant from a type map. This needs to be fixed
* (without breaking things if the type map specifies a
* content-length, which currently leads to the correct result).
*/
if (!(variant->sub_req && variant->sub_req->handler)
&& (len = find_content_length(neg, variant)) >= 0) {
lenstr = (char *) apr_palloc(r->pool, 22);
apr_snprintf(lenstr, 22, "%" APR_OFF_T_FMT, len);
*((const char **) apr_array_push(arr)) = " {length ";
*((const char **) apr_array_push(arr)) = lenstr;
*((const char **) apr_array_push(arr)) = "}";
}
*((const char **) apr_array_push(arr)) = "}";
*((const char **) apr_array_push(arr)) = ", "; /* trimmed below */
}
if (neg->send_alternates && neg->avail_vars->nelts) {
arr->nelts--; /* remove last comma */
apr_table_mergen(hdrs, "Alternates",
apr_array_pstrcat(r->pool, arr, '\0'));
}
if (neg->is_transparent || vary_by_type || vary_by_language ||
vary_by_language || vary_by_charset || vary_by_encoding) {
apr_table_mergen(hdrs, "Vary", 2 + apr_pstrcat(r->pool,
neg->is_transparent ? ", negotiate" : "",
vary_by_type ? ", accept" : "",
vary_by_language ? ", accept-language" : "",
vary_by_charset ? ", accept-charset" : "",
vary_by_encoding ? ", accept-encoding" : "", NULL));
}
if (neg->is_transparent) { /* Create TCN response header */
apr_table_setn(hdrs, "TCN",
alg_result == alg_list ? "list" : "choice");
}
}
/**********************************************************************
*
* Return an HTML list of variants. This is output as part of the
* choice response or 406 status body.
*/
static char *make_variant_list(request_rec *r, negotiation_state *neg)
{
apr_array_header_t *arr;
int i;
int max_vlist_array = (neg->avail_vars->nelts * 15) + 2;
/* In order to avoid O(n^2) memory copies in building the list,
* we preallocate a apr_table_t with the maximum substrings possible,
* fill it with the variant list, and then concatenate the entire array.
*/
arr = apr_array_make(r->pool, max_vlist_array, sizeof(char *));
*((const char **) apr_array_push(arr)) = "Available variants:\n<ul>\n";
for (i = 0; i < neg->avail_vars->nelts; ++i) {
var_rec *variant = &((var_rec *) neg->avail_vars->elts)[i];
const char *filename = variant->file_name ? variant->file_name : "";
apr_array_header_t *languages = variant->content_languages;
const char *description = variant->description ? variant->description : "";
/* The format isn't very neat, and it would be nice to make
* the tags human readable (eg replace 'language en' with 'English').
* Note that if you change the number of substrings pushed, you also
* need to change the calculation of max_vlist_array above.
*/
*((const char **) apr_array_push(arr)) = "<li><a href=\"";
*((const char **) apr_array_push(arr)) = filename;
*((const char **) apr_array_push(arr)) = "\">";
*((const char **) apr_array_push(arr)) = filename;
*((const char **) apr_array_push(arr)) = "</a> ";
*((const char **) apr_array_push(arr)) = description;
if (variant->mime_type && *variant->mime_type) {
*((const char **) apr_array_push(arr)) = ", type ";
*((const char **) apr_array_push(arr)) = variant->mime_type;
}
if (languages && languages->nelts) {
*((const char **) apr_array_push(arr)) = ", language ";
*((const char **) apr_array_push(arr)) = apr_array_pstrcat(r->pool,
languages, ',');
}
if (variant->content_charset && *variant->content_charset) {
*((const char **) apr_array_push(arr)) = ", charset ";
*((const char **) apr_array_push(arr)) = variant->content_charset;
}
if (variant->content_encoding) {
*((const char **) apr_array_push(arr)) = ", encoding ";
*((const char **) apr_array_push(arr)) = variant->content_encoding;
}
*((const char **) apr_array_push(arr)) = "</li>\n";
}
*((const char **) apr_array_push(arr)) = "</ul>\n";
return apr_array_pstrcat(r->pool, arr, '\0');
}
static void store_variant_list(request_rec *r, negotiation_state *neg)
{
if (r->main == NULL) {
apr_table_setn(r->notes, "variant-list", make_variant_list(r, neg));
}
else {
apr_table_setn(r->main->notes, "variant-list",
make_variant_list(r->main, neg));
}
}
/* Called if we got a "Choice" response from the variant selection algorithm.
* It checks the result of the chosen variant to see if it
* is itself negotiated (if so, return error HTTP_VARIANT_ALSO_VARIES).
* Otherwise, add the appropriate headers to the current response.
*/
static int setup_choice_response(request_rec *r, negotiation_state *neg,
var_rec *variant)
{
request_rec *sub_req;
const char *sub_vary;
if (!variant->sub_req) {
int status;
sub_req = ap_sub_req_lookup_file(variant->file_name, r, NULL);
status = sub_req->status;
if (status != HTTP_OK &&
!apr_table_get(sub_req->err_headers_out, "TCN")) {
ap_destroy_sub_req(sub_req);
return status;
}
variant->sub_req = sub_req;
}
else {
sub_req = variant->sub_req;
}
/* The variant selection algorithm told us to return a "Choice"
* response. This is the normal variant response, with
* some extra headers. First, ensure that the chosen
* variant did or will not itself engage in transparent negotiation.
* If not, set the appropriate headers, and fall through to
* the normal variant handling
*/
/* This catches the error that a transparent type map selects a
* transparent multiviews resource as the best variant.
*
* XXX: We do not signal an error if a transparent type map
* selects a _non_transparent multiviews resource as the best
* variant, because we can generate a legal negotiation response
* in this case. In this case, the vlist_validator of the
* nontransparent subrequest will be lost however. This could
* lead to cases in which a change in the set of variants or the
* negotiation algorithm of the nontransparent resource is never
* propagated up to a HTTP/1.1 cache which interprets Vary. To be
* completely on the safe side we should return HTTP_VARIANT_ALSO_VARIES
* for this type of recursive negotiation too.
*/
if (neg->is_transparent &&
apr_table_get(sub_req->err_headers_out, "TCN")) {
return HTTP_VARIANT_ALSO_VARIES;
}
/* This catches the error that a transparent type map recursively
* selects, as the best variant, another type map which itself
* causes transparent negotiation to be done.
*
* XXX: Actually, we catch this error by catching all cases of
* type map recursion. There are some borderline recursive type
* map arrangements which would not produce transparent
* negotiation protocol errors or lack of cache propagation
* problems, but such arrangements are very hard to detect at this
* point in the control flow, so we do not bother to single them
* out.
*
* Recursive type maps imply a recursive arrangement of negotiated
* resources which is visible to outside clients, and this is not
* supported by the transparent negotiation caching protocols, so
* if we are to have generic support for recursive type maps, we
* have to create some configuration setting which makes all type
* maps non-transparent when recursion is enabled. Also, if we
* want recursive type map support which ensures propagation of
* type map changes into HTTP/1.1 caches that handle Vary, we
* would have to extend the current mechanism for generating
* variant list validators.
*/
if (sub_req->handler && strcmp(sub_req->handler, "type-map") == 0) {
return HTTP_VARIANT_ALSO_VARIES;
}
/* This adds an appropriate Variant-Vary header if the subrequest
* is a multiviews resource.
*
* XXX: TODO: Note that this does _not_ handle any Vary header
* returned by a CGI if sub_req is a CGI script, because we don't
* see that Vary header yet at this point in the control flow.
* This won't cause any cache consistency problems _unless_ the
* CGI script also returns a Cache-Control header marking the
* response as cachable. This needs to be fixed, also there are
* problems if a CGI returns an Etag header which also need to be
* fixed.
*/
if ((sub_vary = apr_table_get(sub_req->err_headers_out, "Vary")) != NULL) {
apr_table_setn(r->err_headers_out, "Variant-Vary", sub_vary);
/* Move the subreq Vary header into the main request to
* prevent having two Vary headers in the response, which
* would be legal but strange.
*/
apr_table_setn(r->err_headers_out, "Vary", sub_vary);
apr_table_unset(sub_req->err_headers_out, "Vary");
}
apr_table_setn(r->err_headers_out, "Content-Location",
apr_pstrdup(r->pool, variant->file_name));
set_neg_headers(r, neg, alg_choice); /* add Alternates and Vary */
/* Still to do by caller: add Expires */
return 0;
}
/****************************************************************
*
* Executive...
*/
static int do_negotiation(request_rec *r, negotiation_state *neg,
var_rec **bestp, int prefer_scripts)
{
var_rec *avail_recs = (var_rec *) neg->avail_vars->elts;
int alg_result; /* result of variant selection algorithm */
int res;
int j;
/* Decide if resource is transparently negotiable */
/* GET or HEAD? (HEAD has same method number as GET) */
if (r->method_number == M_GET) {
/* maybe this should be configurable, see also the comment
* about recursive type maps in setup_choice_response()
*/
neg->is_transparent = 1;
/* We can't be transparent if we are a map file in the middle
* of the request URI.
*/
if (r->path_info && *r->path_info)
neg->is_transparent = 0;
for (j = 0; j < neg->avail_vars->nelts; ++j) {
var_rec *variant = &avail_recs[j];
/* We can't be transparent, because of internal
* assumptions in best_match(), if there is a
* non-neighboring variant. We can have a non-neighboring
* variant when processing a type map.
*/
if (ap_strchr_c(variant->file_name, '/'))
neg->is_transparent = 0;
}
}
if (neg->is_transparent) {
parse_negotiate_header(r, neg);
}
else { /* configure negotiation on non-transparent resource */
neg->may_choose = 1;
}
maybe_add_default_accepts(neg, prefer_scripts);
alg_result = best_match(neg, bestp);
/* alg_result is one of
* alg_choice: a best variant is chosen
* alg_list: no best variant is chosen
*/
if (alg_result == alg_list) {
/* send a list response or HTTP_NOT_ACCEPTABLE error response */
neg->send_alternates = 1; /* always include Alternates header */
set_neg_headers(r, neg, alg_result);
store_variant_list(r, neg);
if (neg->is_transparent && neg->ua_supports_trans) {
/* XXX todo: expires? cachability? */
/* Some HTTP/1.0 clients are known to choke when they get
* a 300 (multiple choices) response without a Location
* header. However the 300 code response we are are about
* to generate will only reach 1.0 clients which support
* transparent negotiation, and they should be OK. The
* response should never reach older 1.0 clients, even if
* we have CacheNegotiatedDocs enabled, because no 1.0
* proxy cache (we know of) will cache and return 300
* responses (they certainly won't if they conform to the
* HTTP/1.0 specification).
*/
return HTTP_MULTIPLE_CHOICES;
}
if (!*bestp) {
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
"no acceptable variant: %s", r->filename);
return HTTP_NOT_ACCEPTABLE;
}
}
/* Variant selection chose a variant */
/* XXX todo: merge the two cases in the if statement below */
if (neg->is_transparent) {
if ((res = setup_choice_response(r, neg, *bestp)) != 0) {
return res; /* return if error */
}
}
else {
set_neg_headers(r, neg, alg_result);
}
/* Make sure caching works - Vary should handle HTTP/1.1, but for
* HTTP/1.0, we can't allow caching at all.
*/
/* XXX: Note that we only set r->no_cache to 1, which causes
* Expires: <now> to be added, when responding to a HTTP/1.0
* client. If we return the response to a 1.1 client, we do not
* add Expires <now>, because doing so would degrade 1.1 cache
* performance by preventing re-use of the response without prior
* revalidation. On the other hand, if the 1.1 client is a proxy
* which was itself contacted by a 1.0 client, or a proxy cache
* which can be contacted later by 1.0 clients, then we currently
* rely on this 1.1 proxy to add the Expires: <now> when it
* forwards the response.
*
* XXX: TODO: Find out if the 1.1 spec requires proxies and
* tunnels to add Expires: <now> when forwarding the response to
* 1.0 clients. I (kh) recall it is rather vague on this point.
* Testing actual 1.1 proxy implementations would also be nice. If
* Expires: <now> is not added by proxies then we need to always
* include Expires: <now> ourselves to ensure correct caching, but
* this would degrade HTTP/1.1 cache efficiency unless we also add
* Cache-Control: max-age=N, which we currently don't.
*
* Roy: No, we are not going to screw over HTTP future just to
* ensure that people who can't be bothered to upgrade their
* clients will always receive perfect server-side negotiation.
* Hell, those clients are sending bogus accept headers anyway.
*
* Manual setting of cache-control/expires always overrides this
* automated kluge, on purpose.
*/
if ((!do_cache_negotiated_docs(r->server)
&& (r->proto_num < HTTP_VERSION(1,1)))
&& neg->count_multiviews_variants != 1) {
r->no_cache = 1;
}
return OK;
}
static int handle_map_file(request_rec *r)
{
negotiation_state *neg;
apr_file_t *map;
var_rec *best;
int res;
char *udir;
if(strcmp(r->handler,MAP_FILE_MAGIC_TYPE) && strcmp(r->handler,"type-map"))
return DECLINED;
neg = parse_accept_headers(r);
if ((res = read_type_map(&map, neg, r))) {
return res;
}
res = do_negotiation(r, neg, &best, 0);
if (res != 0) return res;
if (best->body)
{
conn_rec *c = r->connection;
apr_bucket_brigade *bb;
apr_bucket *e;
ap_allow_standard_methods(r, REPLACE_ALLOW, M_GET, M_OPTIONS, M_POST, -1);
if ((res = ap_discard_request_body(r)) != OK) {
return res;
}
/*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);
if ((res = ap_meets_conditions(r)) != OK) {
return res;
}
bb = apr_brigade_create(r->pool, c->bucket_alloc);
e = apr_bucket_file_create(map, best->body,
(apr_size_t)best->bytes, r->pool,
c->bucket_alloc);
APR_BRIGADE_INSERT_TAIL(bb, e);
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 */
};