home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 25 / CDROM25.iso / Share / linux / apache / contrib / modules / probably_obsolete / mod_expires.c < prev    next >
Encoding:
C/C++ Source or Header  |  1998-06-11  |  14.7 KB  |  475 lines

  1. /* ====================================================================
  2.  * Copyright (c) 1995 The Apache Group.  All rights reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions
  6.  * are met:
  7.  *
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer. 
  10.  *
  11.  * 2. Redistributions in binary form must reproduce the above copyright
  12.  *    notice, this list of conditions and the following disclaimer in
  13.  *    the documentation and/or other materials provided with the
  14.  *    distribution.
  15.  *
  16.  * 3. All advertising materials mentioning features or use of this
  17.  *    software must display the following acknowledgment:
  18.  *    "This product includes software developed by the Apache Group
  19.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  20.  *
  21.  * 4. The names "Apache Server" and "Apache Group" must not be used to
  22.  *    endorse or promote products derived from this software without
  23.  *    prior written permission.
  24.  *
  25.  * 5. Redistributions of any form whatsoever must retain the following
  26.  *    acknowledgment:
  27.  *    "This product includes software developed by the Apache Group
  28.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  29.  *
  30.  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
  31.  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  32.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  33.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
  34.  * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  38.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  41.  * OF THE POSSIBILITY OF SUCH DAMAGE.
  42.  * ====================================================================
  43.  *
  44.  * This software consists of voluntary contributions made by many
  45.  * individuals on behalf of the Apache Group and was originally based
  46.  * on public domain software written at the National Center for
  47.  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
  48.  * For more information on the Apache Group and the Apache HTTP server
  49.  * project, please see <http://www.apache.org/>.
  50.  *
  51.  */
  52.  
  53. /*
  54.  * mod_expires.c
  55.  * version 0.0.10
  56.  * status beta
  57.  * 
  58.  * Andrew Wilson <Andrew.Wilson@cm.cf.ac.uk> 26.Jan.96
  59.  *
  60.  * This module allows you to control the form of the Expires: header
  61.  * that Apache issues for each access.  Directives can appear in
  62.  * configuration files or in .htaccess files so expiry semantics can
  63.  * be defined on a per-directory basis.  
  64.  *
  65.  * DIRECTIVE SYNTAX
  66.  *
  67.  * Valid directives are:
  68.  *
  69.  *     ExpiresActive on | off
  70.  *     ExpiresDefault <code><seconds>
  71.  *     ExpiresByType type/encoding <code><seconds>
  72.  *
  73.  * Valid values for <code> are:
  74.  *
  75.  *     'M'    expires header shows file modification date + <seconds>
  76.  *     'A'    expires header shows access time + <seconds>
  77.  *
  78.  *              [I'm not sure which of these is best under different
  79.  *              circumstances, I guess it's for other people to explore.
  80.  *        The effects may be indistinguishable for a number of cases]
  81.  *
  82.  * <seconds> should be an integer value [acceptable to atoi()]
  83.  *
  84.  * There is NO space between the <code> and <seconds>.
  85.  *
  86.  * For example, a directory which contains information which changes
  87.  * frequently might contain:
  88.  *
  89.  *     # reports generated by cron every hour.  don't let caches
  90.  *     # hold onto stale information
  91.  *     ExpiresDefault M3600
  92.  *
  93.  * Another example, our html pages can change all the time, the gifs
  94.  * tend not to change often:
  95.  * 
  96.  *     # pages are hot (1 week), images are cold (1 month)
  97.  *     ExpiresByType text/html A604800
  98.  *     ExpiresByType image/gif A2592000
  99.  *
  100.  * Expires can be turned on for all URLs on the server by placing the
  101.  * following directive in a conf file:
  102.  *
  103.  *     ExpiresActive on
  104.  *
  105.  * ExpiresActive can also appear in .htaccess files, enabling the
  106.  * behaviour to be turned on or off for each chosen directory.
  107.  *
  108.  *     # turn off Expires behaviour in this directory
  109.  *     # and subdirectories
  110.  *     ExpiresActive off
  111.  *
  112.  * Directives defined for a directory are valid in subdirectories
  113.  * unless explicitly overridden by new directives in the subdirectory
  114.  * .htaccess files.
  115.  *
  116.  * ALTERNATIVE DIRECTIVE SYNTAX
  117.  *
  118.  * Directives can also be defined in a more readable syntax of the form:
  119.  *
  120.  *     ExpiresDefault "<base> [plus] {<num> <type>}*"
  121.  *     ExpiresByType type/encoding "<base> [plus] {<num> <type>}*"
  122.  *
  123.  * where <base> is one of:
  124.  *    access    
  125.  *    now        equivalent to 'access'
  126.  *    modification
  127.  *
  128.  * where the 'plus' keyword is optional
  129.  *
  130.  * where <num> should be an integer value [acceptable to atoi()]
  131.  *
  132.  * where <type> is one of:
  133.  *    years
  134.  *    months
  135.  *    weeks
  136.  *    days
  137.  *    hours
  138.  *    minutes
  139.  *    seconds
  140.  *
  141.  * For example, any of the following directives can be used to make
  142.  * documents expire 1 month after being accessed, by default:
  143.  *
  144.  *    ExpiresDefault "access plus 1 month"
  145.  *    ExpiresDefault "access plus 4 weeks"
  146.  *    ExpiresDefault "access plus 30 days"
  147.  *
  148.  * The expiry time can be fine-tuned by adding several '<num> <type>'
  149.  * clauses:
  150.  *
  151.  *    ExpiresByType text/html "access plus 1 month 15 days 2 hours"
  152.  *    ExpiresByType image/gif "modification plus 5 hours 3 minutes"
  153.  *
  154.  * ---
  155.  *
  156.  * Change-log:
  157.  * 29.Jan.96    Hardened the add_* functions.  Server will now bail out
  158.  *        if bad directives are given in the conf files.
  159.  * 02.Feb.96    Returns DECLINED if not 'ExpiresActive on', giving other
  160.  *        expires-aware modules a chance to play with the same
  161.  *        directives. [Michael Rutman]
  162.  * 03.Feb.96    Call tzset() before localtime().  Trying to get the module
  163.  *        to work properly in non GMT timezones.
  164.  * 12.Feb.96    Modified directive syntax to allow more readable commands:
  165.  *          ExpiresDefault "now plus 10 days 20 seconds"
  166.  *          ExpiresDefault "access plus 30 days"
  167.  *          ExpiresDefault "modification plus 1 year 10 months 30 days"
  168.  * 13.Feb.96    Fix call to table_get() with NULL 2nd parameter [Rob Hartill]
  169.  * 19.Feb.96    Call gm_timestr_822() to get time formatted correctly, can't
  170.  *        rely on presence of HTTP_TIME_FORMAT in Apache 1.1+.
  171.  * 21.Feb.96    This version (0.0.9) reverses assumptions made in 0.0.8
  172.  *        about star/star handlers.  Reverting to 0.0.7 behaviour.
  173.  * 08.Jun.96    allows ExpiresDefault to be used with responses that use 
  174.  *              the DefaultType by not DECLINING, but instead skipping 
  175.  *              the table_get check and then looking for an ExpiresDefault.
  176.  *              [Rob Hartill]
  177.  *
  178.  * TODO
  179.  * add support for Cache-Control: max-age=20 from the HTTP/1.1
  180.  * proposal (in this case, a ttl of 20 seconds) [ask roy]
  181.  * add per-file expiry and explicit expiry times - duplicates some
  182.  * of the mod_cern_meta.c functionality.  eg:
  183.  *         ExpiresExplicit index.html "modification plus 30 days"
  184.  *
  185.  * BUGS
  186.  * Hi, welcome to the internet.
  187.  */
  188.  
  189. #include "httpd.h"
  190. #include "http_config.h"
  191. #include "http_log.h"
  192.  
  193. typedef struct {
  194.     int active;
  195.     char *expiresdefault;
  196.     table *expiresbytype;
  197. } expires_dir_config;
  198.  
  199. /* from mod_dir, why is this alias used?
  200.  */
  201. #define DIR_CMD_PERMS OR_INDEXES
  202.  
  203. #define ACTIVE_ON     1
  204. #define ACTIVE_OFF     0
  205. #define ACTIVE_DONTCARE 2
  206.  
  207. module expires_module;
  208.  
  209. void *create_dir_expires_config (pool *p, char *dummy)
  210. {     
  211.     expires_dir_config *new =
  212.         (expires_dir_config *) pcalloc (p, sizeof(expires_dir_config));
  213.     new->active = ACTIVE_DONTCARE;
  214.     new->expiresdefault = "";
  215.     new->expiresbytype = make_table(p, 4);
  216.     return (void *)new;
  217. }   
  218.  
  219. char *set_expiresactive (cmd_parms *cmd, expires_dir_config *dir_config, int arg)
  220. {
  221.     /* if we're here at all it's because someone explicitly
  222.      * set the active flag
  223.      */
  224.     dir_config->active = ACTIVE_ON;
  225.     if ( arg == 0 ) {
  226.         dir_config->active = ACTIVE_OFF;
  227.     };
  228.     return NULL;
  229.  
  230. /* check_code() parse 'code' and return NULL or an error response
  231.  * string.  If we return NULL then real_code contains code converted
  232.  * to the cnnnn format.
  233.  */
  234. char *check_code( pool *pool, char *code, char **real_code )
  235. {
  236.     char *word;
  237.     char base = 'X';
  238.     int modifier = 0;
  239.     int num = 0;
  240.     int factor = 0;
  241.     char foo[MAX_STRING_LEN];
  242.  
  243.     /* 0.0.4 compatibility?
  244.      */
  245.     if ( (code[0] == 'A') || (code[0] == 'M') ) {
  246.     *real_code = pstrdup( pool, code );
  247.     return NULL;
  248.     };
  249.  
  250.     /* <base> [plus] {<num> <type>}*
  251.      */
  252.  
  253.     /* <base>
  254.      */
  255.     word = getword_conf( pool, &code );
  256.     if ( !strncasecmp( word, "now", 1 ) ||
  257.      !strncasecmp( word, "access", 1 ) ) {
  258.     base = 'A';
  259.     } else if ( !strncasecmp( word, "modification", 1 ) ) {
  260.     base = 'M';
  261.     } else {
  262.     return pstrcat( pool, "bad expires code, unrecognised <base> '",
  263.         word, "'", NULL);
  264.     };
  265.  
  266.     /* [plus]
  267.      */
  268.     word = getword_conf( pool, &code );
  269.     if ( !strncasecmp( word, "plus", 1 ) ) {
  270.         word = getword_conf( pool, &code );
  271.     };
  272.  
  273.     /* {<num> <type>}*
  274.      */
  275.     while ( word[0] ) {
  276.     /* <num>
  277.      */
  278.     if ( index("0123456789", word[0]) != NULL ) {
  279.         num = atoi( word );
  280.     } else {
  281.             return pstrcat( pool, "bad expires code, numeric value expected <num> '",
  282.         word, "'", NULL);
  283.     };
  284.  
  285.     /* <type>
  286.      */
  287.     word = getword_conf( pool, &code );
  288.     if ( word[0] ) {
  289.         /* do nothing */
  290.     } else {
  291.             return pstrcat( pool, "bad expires code, missing <type>", NULL);
  292.     };
  293.  
  294.     factor = 0;
  295.     if ( !strncasecmp( word, "years", 1 ) ) {
  296.         factor = 60*60*24*365;
  297.     } else if ( !strncasecmp( word, "months", 2 ) ) {
  298.         factor = 60*60*24*30;
  299.     } else if ( !strncasecmp( word, "weeks", 1 ) ) {
  300.         factor = 60*60*24*7;
  301.     } else if ( !strncasecmp( word, "days", 1 ) ) {
  302.         factor = 60*60*24;
  303.     } else if ( !strncasecmp( word, "hours", 1 ) ) {
  304.         factor = 60*60;
  305.     } else if ( !strncasecmp( word, "minutes", 2 ) ) {
  306.         factor = 60;
  307.     } else if ( !strncasecmp( word, "seconds", 1 ) ) {
  308.         factor = 1;
  309.     } else {
  310.             return pstrcat( pool, "bad expires code, unrecognised <type>", 
  311.         "'", word, "'", NULL);
  312.     };
  313.  
  314.     modifier = modifier + factor * num;
  315.  
  316.     /* next <num>
  317.      */
  318.     word = getword_conf( pool, &code );
  319.     };
  320.  
  321.     sprintf( foo, "%c%d", base, modifier );
  322.     *real_code = pstrdup( pool, foo );
  323.  
  324.     return NULL;
  325. }
  326.  
  327. char *set_expiresbytype(cmd_parms *cmd, expires_dir_config *dir_config, char *mime, char *code)
  328. {
  329.     char *response, *real_code;
  330.  
  331.     if ( (response = check_code( cmd->pool, code, &real_code  )) == NULL ) {
  332.         table_set (dir_config->expiresbytype, mime, real_code);
  333.     return NULL;
  334.     };
  335.     return pstrcat( cmd->pool, 
  336.     "'ExpiresByType ", mime, " ", code, "': ", response, NULL );
  337.  
  338. char *set_expiresdefault (cmd_parms *cmd, expires_dir_config *dir_config, char *code)
  339. {
  340.     char *response, *real_code;
  341.  
  342.     if ( (response = check_code( cmd->pool, code, &real_code )) == NULL ) {
  343.         dir_config->expiresdefault = pstrdup( cmd->pool, real_code );
  344.     return NULL;
  345.     };
  346.     return pstrcat( cmd->pool, 
  347.     "'ExpiresDefault ", code, "': ", response, NULL );
  348.  
  349. command_rec expires_cmds[] = {
  350. { "ExpiresActive", set_expiresactive, NULL, DIR_CMD_PERMS, FLAG, NULL},
  351. { "ExpiresBytype", set_expiresbytype, NULL, DIR_CMD_PERMS, TAKE2,
  352.     "a mime type followed by an expiry date code"},
  353. { "ExpiresDefault", set_expiresdefault, NULL, DIR_CMD_PERMS, TAKE1,
  354.     "an expiry date code"},
  355. { NULL }
  356. };  
  357.  
  358. void *merge_expires_dir_configs (pool *p, void *basev, void *addv)
  359. {
  360.     expires_dir_config *new= (expires_dir_config*)pcalloc (p, sizeof(expires_dir_config));
  361.     expires_dir_config *base = (expires_dir_config *)basev;
  362.     expires_dir_config *add = (expires_dir_config *)addv;
  363.  
  364.     if ( add->active == ACTIVE_DONTCARE ) {
  365.     new->active = base->active;
  366.     } else {
  367.     new->active = add->active;
  368.     };
  369.  
  370.     if ( add->expiresdefault != '\0' ) {
  371.         new->expiresdefault = add->expiresdefault;
  372.     };
  373.  
  374.     new->expiresbytype = overlay_tables (p, add->expiresbytype,
  375.                                           base->expiresbytype);
  376.     return new;
  377. }  
  378.  
  379. int add_expires(request_rec *r)
  380. {
  381.     expires_dir_config *conf =
  382.             (expires_dir_config *)get_module_config(r->per_dir_config, &expires_module);
  383.     char *code;
  384.     time_t base; 
  385.     time_t additional; 
  386.     time_t expires; 
  387.  
  388.     if ( r->finfo.st_mode == 0 )
  389.     return DECLINED;
  390.  
  391.     /* COMMA bites my ass...
  392.      */
  393.     if ( conf == NULL ) {
  394.         log_reason ("internal error in expires_module; add_expires(), conf == NULL", r->filename, r);
  395.     return SERVER_ERROR;
  396.     };
  397.  
  398.     if ( conf->active != ACTIVE_ON )
  399.         return DECLINED;
  400.  
  401.     /* we perhaps could use the default_type(r) in its place but that
  402.      * may be 2nd guesing the desired configuration...  calling table_get
  403.      * with a NULL key will SEGV us
  404.      *
  405.      * I still don't know *why* r->content_type would ever be NULL, this
  406.      * is possibly a result of fixups being called in many different
  407.      * places.  Fixups is probably the wrong place to be doing all this
  408.      * work...  Bah.
  409.      *
  410.      * Changed as of 08.Jun.96 don't DECLINE, look for an ExpiresDefault.
  411.      */
  412.     if ( r->content_type == NULL )
  413.     code = NULL;
  414.     else
  415.     code = (char *) table_get( conf->expiresbytype, r->content_type );
  416.  
  417.     if ( code == NULL ) {
  418.     /* no expires defined for that type, is there a default? */
  419.     code = conf->expiresdefault;
  420.  
  421.         if ( code[0] == '\0' )
  422.         return OK;
  423.     };
  424.  
  425.     /* we have our code */
  426.  
  427.     switch (code[0]) {
  428.     case 'M':
  429.             base = r->finfo.st_mtime;
  430.         additional = atoi( &code[1] );
  431.         break;
  432.     case 'A':
  433.         /* there's been some discussion and it's possible that 
  434.          * 'access time' will be stored in request structure
  435.          */
  436.         base = time( NULL );
  437.         additional = atoi( &code[1] );
  438.         break;
  439.     default:
  440.         /* expecting the add_* routines to be case-hardened this 
  441.          * is just a reminder that module is beta
  442.          */
  443.             log_reason ("internal error in expires_module; bad expires code", r->filename, r);
  444.             return SERVER_ERROR;
  445.     };
  446.  
  447.     expires = base + additional;
  448.     tzset();    /* redundant? called implicitly by localtime, at least 
  449.          * under FreeBSD
  450.          */
  451.     table_set( r->headers_out, "Expires", gm_timestr_822( r->pool, expires ));
  452.     return OK;
  453. }
  454.  
  455. module expires_module = {
  456.    STANDARD_MODULE_STUFF,
  457.    NULL,            /* initializer */
  458.    create_dir_expires_config,    /* dir config creater */
  459.    merge_expires_dir_configs,    /* dir merger --- default is to override */
  460.    NULL,            /* server config */
  461.    NULL,            /* merge server configs */
  462.    expires_cmds,        /* command table */
  463.    NULL,            /* handlers */
  464.    NULL,            /* filename translation */
  465.    NULL,            /* check_user_id */
  466.    NULL,            /* check auth */
  467.    NULL,            /* check access */
  468.    NULL,            /* type_checker */
  469.    add_expires,            /* fixups */
  470.    NULL,            /* logger */
  471. };
  472.