home *** CD-ROM | disk | FTP | other *** search
/ PC Online 1999 April / PCO0499.ISO / filesbbs / os2 / apach134.arj / APACH134.ZIP / src / modules / standard / mod_usertrack.c < prev   
Encoding:
C/C++ Source or Header  |  1999-01-01  |  12.4 KB  |  347 lines

  1. /* ====================================================================
  2.  * Copyright (c) 1995-1999 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. For written permission, please contact
  24.  *    apache@apache.org.
  25.  *
  26.  * 5. Products derived from this software may not be called "Apache"
  27.  *    nor may "Apache" appear in their names without prior written
  28.  *    permission of the Apache Group.
  29.  *
  30.  * 6. Redistributions of any form whatsoever must retain the following
  31.  *    acknowledgment:
  32.  *    "This product includes software developed by the Apache Group
  33.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  34.  *
  35.  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
  36.  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  37.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  38.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
  39.  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  41.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  42.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  43.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  44.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  45.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  46.  * OF THE POSSIBILITY OF SUCH DAMAGE.
  47.  * ====================================================================
  48.  *
  49.  * This software consists of voluntary contributions made by many
  50.  * individuals on behalf of the Apache Group and was originally based
  51.  * on public domain software written at the National Center for
  52.  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
  53.  * For more information on the Apache Group and the Apache HTTP server
  54.  * project, please see <http://www.apache.org/>.
  55.  *
  56.  */
  57.  
  58. /* User Tracking Module (Was mod_cookies.c)
  59.  *
  60.  * This Apache module is designed to track users paths through a site.
  61.  * It uses the client-side state ("Cookie") protocol developed by Netscape.
  62.  * It is known to work on Netscape browsers, Microsoft Internet
  63.  * Explorer and others currently being developed.
  64.  *
  65.  * Each time a page is requested we look to see if the browser is sending
  66.  * us a Cookie: header that we previously generated.
  67.  *
  68.  * If we don't find one then the user hasn't been to this site since
  69.  * starting their browser or their browser doesn't support cookies.  So
  70.  * we generate a unique Cookie for the transaction and send it back to
  71.  * the browser (via a "Set-Cookie" header)
  72.  * Future requests from the same browser should keep the same Cookie line.
  73.  *
  74.  * By matching up all the requests with the same cookie you can
  75.  * work out exactly what path a user took through your site.  To log
  76.  * the cookie use the " %{Cookie}n " directive in a custom access log;
  77.  *
  78.  * Example 1 : If you currently use the standard Log file format (CLF)
  79.  * and use the command "TransferLog somefilename", add the line
  80.  *       LogFormat "%h %l %u %t \"%r\" %s %b %{Cookie}n"
  81.  * to your config file.
  82.  *
  83.  * Example 2 : If you used to use the old "CookieLog" directive, you
  84.  * can emulate it by adding the following command to your config file
  85.  *       CustomLog filename "%{Cookie}n \"%r\" %t"
  86.  *
  87.  * Notes:
  88.  * 1.  This code now logs the initial transaction (the one that created
  89.  *     the cookie to start with).
  90.  * 2.  This module has been designed to not interfere with other Cookies
  91.  *     your site may be using; just avoid sending out cookies with
  92.  *     the name "Apache=" or things will get confused.
  93.  * 3.  If you want you can modify the Set-Cookie line so that the Cookie
  94.  *     never expires.  You would then get the same Cookie each time the
  95.  *     user revisits your site.
  96.  *
  97.  * Mark Cox, mark@ukweb.com, 6 July 95
  98.  *
  99.  * This file replaces mod_cookies.c
  100.  */
  101.  
  102. #include "httpd.h"
  103. #include "http_config.h"
  104. #include "http_core.h"
  105. #if !defined(WIN32) && !defined(MPE)
  106. #include <sys/time.h>
  107. #endif
  108.  
  109. module MODULE_VAR_EXPORT usertrack_module;
  110.  
  111. typedef struct {
  112.     int always;
  113.     time_t expires;
  114. }      cookie_log_state;
  115.  
  116. /* Define this to allow post-2000 cookies. Cookies use two-digit dates,
  117.  * so it might be dicey. (Netscape does it correctly, but others may not)
  118.  */
  119. #define MILLENIAL_COOKIES
  120.  
  121. /* Make Cookie: Now we have to generate something that is going to be
  122.  * pretty unique.  We can base it on the pid, time, hostip */
  123.  
  124. #define COOKIE_NAME "Apache="
  125.  
  126. static void make_cookie(request_rec *r)
  127. {
  128.     cookie_log_state *cls = ap_get_module_config(r->server->module_config,
  129.                                               &usertrack_module);
  130. #if defined(NO_GETTIMEOFDAY) && !defined(NO_TIMES)
  131.     clock_t mpe_times;
  132.     struct tms mpe_tms;
  133. #elif !defined(WIN32)
  134.     struct timeval tv;
  135.     struct timezone tz = {0, 0};
  136. #endif
  137.     /* 1024 == hardcoded constant */
  138.     char cookiebuf[1024];
  139.     char *new_cookie;
  140.     const char *rname = ap_get_remote_host(r->connection, r->per_dir_config,
  141.                     REMOTE_NAME);
  142.  
  143. #if defined(NO_GETTIMEOFDAY) && !defined(NO_TIMES)
  144. /* We lack gettimeofday(), so we must use time() to obtain the epoch
  145.    seconds, and then times() to obtain CPU clock ticks (milliseconds).
  146.    Combine this together to obtain a hopefully unique cookie ID. */
  147.  
  148.     mpe_times = times(&mpe_tms);
  149.  
  150.     ap_snprintf(cookiebuf, sizeof(cookiebuf), "%s.%d%ld%ld", rname, (int) getpid(),
  151.                 (long) r->request_time, (long) mpe_tms.tms_utime);
  152. #elif defined(WIN32)
  153.     /*
  154.      * We lack gettimeofday() and we lack times(). So we'll use a combination
  155.      * of time() and GetTickCount(), which returns milliseconds since Windows
  156.      * was started. It should be relatively unique.
  157.      */
  158.  
  159.     ap_snprintf(cookiebuf, sizeof(cookiebuf), "%s.%d%ld%ld", rname, (int) getpid(),
  160.                 (long) r->request_time, (long) GetTickCount());
  161.  
  162. #else
  163.     gettimeofday(&tv, &tz);
  164.  
  165.     ap_snprintf(cookiebuf, sizeof(cookiebuf), "%s.%d%ld%d", rname, (int) getpid(),
  166.                 (long) tv.tv_sec, (int) tv.tv_usec / 1000);
  167. #endif
  168.  
  169.     if (cls->expires) {
  170.         struct tm *tms;
  171.         time_t when = r->request_time + cls->expires;
  172.  
  173. #ifndef MILLENIAL_COOKIES
  174.         /*
  175.          * Only two-digit date string, so we can't trust "00" or more.
  176.          * Therefore, we knock it all back to just before midnight on
  177.          * 1/1/2000 (which is 946684799)
  178.          */
  179.  
  180.         if (when > 946684799)
  181.             when = 946684799;
  182. #endif
  183.         tms = gmtime(&when);
  184.  
  185.         /* Cookie with date; as strftime '%a, %d-%h-%y %H:%M:%S GMT' */
  186.         new_cookie = ap_psprintf(r->pool,
  187.                 "%s%s; path=/; expires=%s, %.2d-%s-%.2d %.2d:%.2d:%.2d GMT",
  188.                     COOKIE_NAME, cookiebuf, ap_day_snames[tms->tm_wday],
  189.                     tms->tm_mday, ap_month_snames[tms->tm_mon],
  190.             tms->tm_year % 100,
  191.                     tms->tm_hour, tms->tm_min, tms->tm_sec);
  192.     }
  193.     else
  194.     new_cookie = ap_psprintf(r->pool, "%s%s; path=/", COOKIE_NAME, cookiebuf);
  195.  
  196.     ap_table_setn(r->headers_out, "Set-Cookie", new_cookie);
  197.     ap_table_setn(r->notes, "cookie", ap_pstrdup(r->pool, cookiebuf));   /* log first time */
  198.     return;
  199. }
  200.  
  201. static int spot_cookie(request_rec *r)
  202. {
  203.     int *enable = (int *) ap_get_module_config(r->per_dir_config,
  204.                                             &usertrack_module);
  205.     const char *cookie;
  206.     char *value;
  207.  
  208.     if (!*enable)
  209.         return DECLINED;
  210.  
  211.     if ((cookie = ap_table_get(r->headers_in, "Cookie")))
  212.         if ((value = strstr(cookie, COOKIE_NAME))) {
  213.             char *cookiebuf, *cookieend;
  214.  
  215.             value += strlen(COOKIE_NAME);
  216.             cookiebuf = ap_pstrdup(r->pool, value);
  217.             cookieend = strchr(cookiebuf, ';');
  218.             if (cookieend)
  219.                 *cookieend = '\0';      /* Ignore anything after a ; */
  220.  
  221.             /* Set the cookie in a note, for logging */
  222.             ap_table_setn(r->notes, "cookie", cookiebuf);
  223.  
  224.             return DECLINED;    /* Theres already a cookie, no new one */
  225.         }
  226.     make_cookie(r);
  227.     return OK;                  /* We set our cookie */
  228. }
  229.  
  230. static void *make_cookie_log_state(pool *p, server_rec *s)
  231. {
  232.     cookie_log_state *cls =
  233.     (cookie_log_state *) ap_palloc(p, sizeof(cookie_log_state));
  234.  
  235.     cls->expires = 0;
  236.  
  237.     return (void *) cls;
  238. }
  239.  
  240. static void *make_cookie_dir(pool *p, char *d)
  241. {
  242.     return (void *) ap_pcalloc(p, sizeof(int));
  243. }
  244.  
  245. static const char *set_cookie_enable(cmd_parms *cmd, int *c, int arg)
  246. {
  247.     *c = arg;
  248.     return NULL;
  249. }
  250.  
  251. static const char *set_cookie_exp(cmd_parms *parms, void *dummy, const char *arg)
  252. {
  253.     cookie_log_state *cls = ap_get_module_config(parms->server->module_config,
  254.                                               &usertrack_module);
  255.     time_t factor, modifier = 0;
  256.     time_t num = 0;
  257.     char *word;
  258.  
  259.     /* The simple case first - all numbers (we assume) */
  260.     if (ap_isdigit(arg[0]) && ap_isdigit(arg[strlen(arg) - 1])) {
  261.         cls->expires = atol(arg);
  262.         return NULL;
  263.     }
  264.  
  265.     /*
  266.      * The harder case - stolen from mod_expires 
  267.      *
  268.      * CookieExpires "[plus] {<num> <type>}*"
  269.      */
  270.  
  271.     word = ap_getword_conf(parms->pool, &arg);
  272.     if (!strncasecmp(word, "plus", 1)) {
  273.         word = ap_getword_conf(parms->pool, &arg);
  274.     };
  275.  
  276.     /* {<num> <type>}* */
  277.     while (word[0]) {
  278.         /* <num> */
  279.     if (ap_isdigit(word[0]))
  280.             num = atoi(word);
  281.         else
  282.             return "bad expires code, numeric value expected.";
  283.  
  284.         /* <type> */
  285.         word = ap_getword_conf(parms->pool, &arg);
  286.         if (!word[0])
  287.             return "bad expires code, missing <type>";
  288.  
  289.         factor = 0;
  290.         if (!strncasecmp(word, "years", 1))
  291.             factor = 60 * 60 * 24 * 365;
  292.         else if (!strncasecmp(word, "months", 2))
  293.             factor = 60 * 60 * 24 * 30;
  294.         else if (!strncasecmp(word, "weeks", 1))
  295.             factor = 60 * 60 * 24 * 7;
  296.         else if (!strncasecmp(word, "days", 1))
  297.             factor = 60 * 60 * 24;
  298.         else if (!strncasecmp(word, "hours", 1))
  299.             factor = 60 * 60;
  300.         else if (!strncasecmp(word, "minutes", 2))
  301.             factor = 60;
  302.         else if (!strncasecmp(word, "seconds", 1))
  303.             factor = 1;
  304.         else
  305.             return "bad expires code, unrecognized type";
  306.  
  307.         modifier = modifier + factor * num;
  308.  
  309.         /* next <num> */
  310.         word = ap_getword_conf(parms->pool, &arg);
  311.     }
  312.  
  313.     cls->expires = modifier;
  314.  
  315.     return NULL;
  316. }
  317.  
  318. static const command_rec cookie_log_cmds[] = {
  319.     {"CookieExpires", set_cookie_exp, NULL, RSRC_CONF, TAKE1,
  320.     "an expiry date code"},
  321.     {"CookieTracking", set_cookie_enable, NULL, OR_FILEINFO, FLAG,
  322.     "whether or not to enable cookies"},
  323.     {NULL}
  324. };
  325.  
  326. module MODULE_VAR_EXPORT usertrack_module = {
  327.     STANDARD_MODULE_STUFF,
  328.     NULL,                       /* initializer */
  329.     make_cookie_dir,            /* dir config creater */
  330.     NULL,                       /* dir merger --- default is to override */
  331.     make_cookie_log_state,      /* server config */
  332.     NULL,                       /* merge server configs */
  333.     cookie_log_cmds,            /* command table */
  334.     NULL,                       /* handlers */
  335.     NULL,                       /* filename translation */
  336.     NULL,                       /* check_user_id */
  337.     NULL,                       /* check auth */
  338.     NULL,                       /* check access */
  339.     NULL,                       /* type_checker */
  340.     spot_cookie,                /* fixups */
  341.     NULL,                       /* logger */
  342.     NULL,                       /* header parser */
  343.     NULL,                       /* child_init */
  344.     NULL,                       /* child_exit */
  345.     NULL                        /* post read-request */
  346. };
  347.