#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#if !defined(WIN32) && !defined(MPE) && !defined(TPF)
#include <sys/time.h>
#endif
module MODULE_VAR_EXPORT usertrack_module;
typedef
struct
{
int
always;
time_t
expires;
} cookie_log_state;
typedef
enum
{
CT_UNSET,
CT_NETSCAPE,
CT_COOKIE,
CT_COOKIE2
} cookie_type_e;
typedef
struct
{
int
enabled;
cookie_type_e style;
char
*cookie_name;
char
*cookie_domain;
} cookie_dir_rec;
#define MILLENIAL_COOKIES
#define COOKIE_NAME "Apache"
static
void
make_cookie(request_rec *r)
{
cookie_log_state *cls = ap_get_module_config(r->server->module_config,
&usertrack_module);
#if defined(NO_GETTIMEOFDAY) && !defined(NO_TIMES)
clock_t
mpe_times;
struct
tms mpe_tms;
#elif !defined(WIN32)
struct
timeval tv;
#ifdef NETWARE
time_t
tz = 0;
#else
struct
timezone tz = {0, 0};
#endif /* defined(NETWARE) */
#endif
char
cookiebuf[1024];
char
*new_cookie;
const
char
*rname = ap_get_remote_host(r->connection, r->per_dir_config,
REMOTE_NAME);
cookie_dir_rec *dcfg;
dcfg = ap_get_module_config(r->per_dir_config, &usertrack_module);
#if defined(NO_GETTIMEOFDAY) && !defined(NO_TIMES)
mpe_times = times(&mpe_tms);
ap_snprintf(cookiebuf,
sizeof
(cookiebuf),
"%s.%d%ld%ld"
, rname,
(
int
) getpid(),
(
long
) r->request_time, (
long
) mpe_tms.tms_utime);
#elif defined(NETWARE)
ap_snprintf(cookiebuf,
sizeof
(cookiebuf),
"%s.%d%ld%ld"
, rname,
(
int
) getpid(), (
long
) r->request_time, (
long
)
clock
());
#elif defined(WIN32)
ap_snprintf(cookiebuf,
sizeof
(cookiebuf),
"%s.%d%ld%ld"
, rname,
(
int
) getpid(),
(
long
) r->request_time, (
long
) GetTickCount());
#else
gettimeofday(&tv, &tz);
ap_snprintf(cookiebuf,
sizeof
(cookiebuf),
"%s.%d%ld%d"
, rname,
(
int
) getpid(),
(
long
) tv.tv_sec, (
int
) tv.tv_usec / 1000);
#endif
if
(cls->expires) {
struct
tm
*tms;
time_t
when;
when = cls->expires;
if
((dcfg->style == CT_UNSET) || (dcfg->style == CT_NETSCAPE)) {
when += r->request_time;
#ifndef MILLENIAL_COOKIES
if
(when > 946684799)
when = 946684799;
#endif
}
tms =
gmtime
(&when);
new_cookie = ap_psprintf(r->pool,
"%s=%s; path=/"
,
dcfg->cookie_name, cookiebuf);
if
((dcfg->style == CT_UNSET) || (dcfg->style == CT_NETSCAPE)) {
new_cookie = ap_psprintf(r->pool,
"%s; "
"expires=%s, %.2d-%s-%.2d "
"%.2d:%.2d:%.2d GMT"
,
new_cookie,
ap_day_snames[tms->tm_wday],
tms->tm_mday,
ap_month_snames[tms->tm_mon],
tms->tm_year % 100,
tms->tm_hour, tms->tm_min, tms->tm_sec);
}
else
{
new_cookie = ap_psprintf(r->pool,
"%s; max-age=%d"
,
new_cookie, (
int
) when);
}
}
else
{
new_cookie = ap_psprintf(r->pool,
"%s=%s; path=/"
,
dcfg->cookie_name, cookiebuf);
}
if
(dcfg->cookie_domain != NULL) {
new_cookie = ap_psprintf(r->pool,
"%s; domain=%s"
,
new_cookie, dcfg->cookie_domain);
}
if
(dcfg->style == CT_COOKIE2) {
new_cookie = ap_pstrcat(r->pool, new_cookie,
"; version=1"
, NULL);
}
ap_table_setn(r->headers_out,
(dcfg->style == CT_COOKIE2 ?
"Set-Cookie2"
:
"Set-Cookie"
),
new_cookie);
ap_table_setn(r->notes,
"cookie"
, ap_pstrdup(r->pool, cookiebuf));
return
;
}
static
int
spot_cookie(request_rec *r)
{
cookie_dir_rec *dcfg = ap_get_module_config(r->per_dir_config,
&usertrack_module);
const
char
*cookie;
char
*value;
if
(!dcfg->enabled) {
return
DECLINED;
}
if
((cookie = ap_table_get(r->headers_in,
(dcfg->style == CT_COOKIE2
?
"Cookie2"
:
"Cookie"
))))
if
((value =
strstr
(cookie, dcfg->cookie_name))) {
char
*cookiebuf, *cookieend;
value +=
strlen
(dcfg->cookie_name) + 1;
cookiebuf = ap_pstrdup(r->pool, value);
cookieend =
strchr
(cookiebuf,
';'
);
if
(cookieend)
*cookieend =
'\0'
;
ap_table_setn(r->notes,
"cookie"
, cookiebuf);
return
DECLINED;
}
make_cookie(r);
return
OK;
}
static
void
*make_cookie_log_state(pool *p, server_rec *s)
{
cookie_log_state *cls =
(cookie_log_state *) ap_palloc(p,
sizeof
(cookie_log_state));
cls->expires = 0;
return
(
void
*) cls;
}
static
void
*make_cookie_dir(pool *p,
char
*d)
{
cookie_dir_rec *dcfg;
dcfg = (cookie_dir_rec *) ap_pcalloc(p,
sizeof
(cookie_dir_rec));
dcfg->cookie_name = COOKIE_NAME;
dcfg->cookie_domain = NULL;
dcfg->style = CT_UNSET;
dcfg->enabled = 0;
return
dcfg;
}
static
const
char
*set_cookie_enable(cmd_parms *cmd,
void
*mconfig,
int
arg)
{
cookie_dir_rec *dcfg = mconfig;
dcfg->enabled = arg;
return
NULL;
}
static
const
char
*set_cookie_exp(cmd_parms *parms,
void
*dummy,
const
char
*arg)
{
cookie_log_state *cls;
time_t
factor, modifier = 0;
time_t
num = 0;
char
*word;
cls = ap_get_module_config(parms->server->module_config,
&usertrack_module);
if
(ap_isdigit(arg[0]) && ap_isdigit(arg[
strlen
(arg) - 1])) {
cls->expires =
atol
(arg);
return
NULL;
}
word = ap_getword_conf(parms->pool, &arg);
if
(!strncasecmp(word,
"plus"
, 1)) {
word = ap_getword_conf(parms->pool, &arg);
};
while
(word[0]) {
if
(ap_isdigit(word[0]))
num =
atoi
(word);
else
return
"bad expires code, numeric value expected."
;
word = ap_getword_conf(parms->pool, &arg);
if
(!word[0])
return
"bad expires code, missing <type>"
;
factor = 0;
if
(!strncasecmp(word,
"years"
, 1))
factor = 60 * 60 * 24 * 365;
else
if
(!strncasecmp(word,
"months"
, 2))
factor = 60 * 60 * 24 * 30;
else
if
(!strncasecmp(word,
"weeks"
, 1))
factor = 60 * 60 * 24 * 7;
else
if
(!strncasecmp(word,
"days"
, 1))
factor = 60 * 60 * 24;
else
if
(!strncasecmp(word,
"hours"
, 1))
factor = 60 * 60;
else
if
(!strncasecmp(word,
"minutes"
, 2))
factor = 60;
else
if
(!strncasecmp(word,
"seconds"
, 1))
factor = 1;
else
return
"bad expires code, unrecognized type"
;
modifier = modifier + factor * num;
word = ap_getword_conf(parms->pool, &arg);
}
cls->expires = modifier;
return
NULL;
}
static
const
char
*set_cookie_name(cmd_parms *cmd,
void
*mconfig,
char
*name)
{
cookie_dir_rec *dcfg = (cookie_dir_rec *) mconfig;
dcfg->cookie_name = ap_pstrdup(cmd->pool, name);
return
NULL;
}
static
const
char
*set_cookie_domain(cmd_parms *cmd,
void
*mconfig,
char
*name)
{
cookie_dir_rec *dcfg;
dcfg = (cookie_dir_rec *) mconfig;
if
(
strlen
(name) == 0) {
return
"CookieDomain values may not be null"
;
}
if
(name[0] !=
'.'
) {
return
"CookieDomain values must begin with a dot"
;
}
if
(
strchr
(&name[1],
'.'
) == NULL) {
return
"CookieDomain values must contain at least one embedded dot"
;
}
dcfg->cookie_domain = ap_pstrdup(cmd->pool, name);
return
NULL;
}
static
const
char
*set_cookie_style(cmd_parms *cmd,
void
*mconfig,
char
*name)
{
cookie_dir_rec *dcfg;
dcfg = (cookie_dir_rec *) mconfig;
if
(strcasecmp(name,
"Netscape"
) == 0) {
dcfg->style = CT_NETSCAPE;
}
else
if
((strcasecmp(name,
"Cookie"
) == 0)
|| (strcasecmp(name,
"RFC2109"
) == 0)) {
dcfg->style = CT_COOKIE;
}
else
if
((strcasecmp(name,
"Cookie2"
) == 0)
|| (strcasecmp(name,
"RFC2965"
) == 0)) {
dcfg->style = CT_COOKIE2;
}
else
{
return
ap_psprintf(cmd->pool,
"Invalid %s keyword: '%s'"
,
cmd->cmd->name, name);
}
return
NULL;
}
static
const
command_rec cookie_log_cmds[] = {
{
"CookieExpires"
, set_cookie_exp, NULL, OR_FILEINFO, TAKE1,
"an expiry date code"
},
{
"CookieTracking"
, set_cookie_enable, NULL, OR_FILEINFO, FLAG,
"whether or not to enable cookies"
},
{
"CookieName"
, set_cookie_name, NULL, OR_FILEINFO, TAKE1,
"name of the tracking cookie"
},
{
"CookieDomain"
, set_cookie_domain, NULL, OR_FILEINFO, TAKE1,
"domain to which this cookie applies"
},
{
"CookieStyle"
, set_cookie_style, NULL, OR_FILEINFO, TAKE1,
"'Netscape', 'Cookie' (RFC2109), or 'Cookie2' (RFC2965)"
},
{NULL}
};
module MODULE_VAR_EXPORT usertrack_module = {
STANDARD_MODULE_STUFF,
NULL,
make_cookie_dir,
NULL,
make_cookie_log_state,
NULL,
cookie_log_cmds,
NULL,
NULL,
NULL,
NULL,
NULL,
NULL,
spot_cookie,
NULL,
NULL,
NULL,
NULL,
NULL
};