joes 2003/01/20 18:36:28
Added: src apreq_cookie.h apreq_cookie.c apreq_env.h
Log:
Import rough cookie API.
Revision Changes Path
1.1 httpd-apreq-2/src/apreq_cookie.h
Index: apreq_cookie.h
===================================================================
#ifndef APREQ_COOKIE_H
#define APREQ_COOKIE_H
#include "apreq_tables.h"
#ifdef __cplusplus
extern "C" {
#endif
typedef struct apreq_table_t apreq_jar_t;
typedef enum { NETSCAPE, RFC } apreq_cookie_version_t;
#define APREQ_COOKIE_DEFAULT_VERSION NETSCAPE
typedef struct apreq_cookie_t {
apreq_cookie_version_t version;
char *path;
char *domain;
char *port;
unsigned secure;
char *comment;
char *commentURL;
union {
long max_age;
const char *expires;
} time;
void *ctx;
apreq_value_t v; /* "raw" value (extended struct) */
} apreq_cookie_t;
#define apreq_value_to_cookie(ptr) apreq_attr_to_type(apreq_cookie_t, v, ptr)
#define apreq_cookie_name(c) ((c)->v.name)
#define apreq_cookie_value(c) ((c)->v.data)
/**
* Returns the number of cookies in the jar.
*
* @param jar The cookie jar.
*/
int apreq_jar_items(apreq_jar_t *jar);
#define apreq_jar_items(j) apreq_table_nelts(j)
/**
* Fetches a cookie from the jar
*
* @param jar The cookie jar.
* @param i The index of the desired cookie.
*/
apreq_cookie_t *apreq_jar_get(apreq_jar_t *jar, char *name);
#define apreq_jar_get(j,k) apreq_value_to_cookie(apreq_char_to_value( \
apreq_table_get(j,k)))
/**
* Adds a cookie by pushing it to the bottom of the jar.
*
* @param jar The cookie jar.
* @param c The cookie to add.
*/
void apreq_jar_add(apreq_jar_t *jar, apreq_cookie_t *c);
#define apreq_jar_add(jar,c) apreq_table_add(jar, &(c)->v)
/**
* Parse the incoming "Cookie:" headers into a cookie jar.
*
* @param r The current request_rec.
* @param data Optional header string to use in place of r->headers_in.
* @remark The current source assumes the client will fold all cookies
* into a single "Cookie:" header. This may assumption may be removed
* in a future release.
*/
apreq_jar_t *apreq_jar_parse(void *ctx, const char *data);
/**
* Returns a new cookie, made from the argument list.
* Valid argument strings, passed in "attr","value" pairs, are:
* name, value, version, expires/max-len, domain, port, path,
* comment, commentURL, secure.
*
* @param r The current request.
*/
apreq_cookie_t *apreq_cookie_make(void *ctx, const apreq_cookie_version_t v,
const char *name, const apr_ssize_t nlen,
const char *value, const apr_ssize_t vlen);
APREQ_DECLARE(apr_status_t) apreq_cookie_attr(apreq_cookie_t *c,
char *attr, char *val);
/**
* Returns a string (allocated from the cookie's pool) that
* represents the cookie as it would appear in a valid "Set-Cookie*" header.
*
* @param c The cookie.
*/
APREQ_DECLARE(const char*) apreq_cookie_as_string(const apreq_cookie_t *c,
apr_pool_t *p);
APREQ_DECLARE(int) apreq_cookie_serialize(const apreq_cookie_t *c,
char *buf, apr_size_t len);
/**
* Get/set the "expires" string. For NETSCAPE cookies, this returns
* the date (properly formatted) when the cookie is to expire.
* For RFC cookies, this function returns NULL.
*
* @param c The cookie.
* @param time_str If NULL, return the current expiry date. Otherwise
* replace with this value instead. The time_str should be in a format
* that apreq_atod() can understand, namely /[+-]?\d+\s*[YMDhms]/.
*/
APREQ_DECLARE(void) apreq_cookie_expires(apreq_cookie_t *c,
const char *time_str);
/**
* Add the cookie to the outgoing "Set-Cookie" headers.
*
* @param c The cookie.
*/
apr_status_t apreq_cookie_bake(apreq_cookie_t *c);
/* XXX: how about baking whole cookie jars, too ??? */
/**
* Add the cookie to the outgoing "Set-Cookie2" headers.
*
* @param c The cookie.
*/
apr_status_t apreq_cookie_bake2(apreq_cookie_t *c);
#ifdef __cplusplus
}
#endif
#endif /*APREQ_COOKIE_H*/
1.1 httpd-apreq-2/src/apreq_cookie.c
Index: apreq_cookie.c
===================================================================
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact [EMAIL PROTECTED]
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
* <http://www.apache.org/>.
*
* Portions of this software are based upon public domain software
* originally written at the National Center for Supercomputing Applications,
* University of Illinois, Urbana-Champaign.
*/
#include "apreq_cookie.h"
#include "apreq_env.h"
APREQ_DECLARE(int) (apreq_jar_items)(apreq_jar_t *jar)
{
return apreq_jar_items(jar);
}
APREQ_DECLARE(apreq_cookie_t *) (apreq_jar_get)(apreq_jar_t *jar,
char *name)
{
return apreq_jar_get(jar,name);
}
APREQ_DECLARE(void) (apreq_jar_add)(apreq_jar_t *jar,
apreq_cookie_t *c)
{
apreq_jar_add(jar,c);
}
APREQ_DECLARE(void) apreq_cookie_expires(apreq_cookie_t *c,
const char *time_str)
{
if ( c->version == NETSCAPE )
c->time.expires = apreq_expires(apreq_env_pool(c->ctx),
time_str,
APREQ_EXPIRES_COOKIE);
else
c->time.max_age = apreq_atol(time_str);
}
APREQ_DECLARE(apr_status_t) apreq_cookie_attr(apreq_cookie_t *c,
char *attr, char *val)
{
if ( attr[0] == '-' || attr[0] == '$' )
++attr;
switch (apr_tolower(*attr)) {
case 'n': /* name */
c->v.name = val;
return APR_SUCCESS;
case 'v': /* version */
c->version = *val - '0';
return APR_SUCCESS;
case 'e': case 'm': /* expires, max-age */
apreq_cookie_expires(c, val);
return APR_SUCCESS;
case 'd':
c->domain = val;
return APR_SUCCESS;
case 'p':
if (strcasecmp("port", attr)==0) {
c->port = val;
return APR_SUCCESS;
}
else if (strcasecmp("path", attr)==0) {
c->path = val;
return APR_SUCCESS;
}
break;
case 'c':
if (strcasecmp("comment", attr)==0) {
c->comment = val;
return APR_SUCCESS;
}
else if (strcasecmp("commentURL", attr)==0) {
c->commentURL = val;
return APR_SUCCESS;
}
break;
case 's':
c->secure = ( strcasecmp(val,"off")!=0 && *val != '0' );
return APR_SUCCESS;
};
apreq_warn(c->ctx, "unknown cookie attribute: `%s' => `%s'",
attr, val);
return APR_EGENERAL;
}
APREQ_DECLARE(apreq_cookie_t *) apreq_cookie_make(void *ctx,
apreq_cookie_version_t version,
const char *name, const apr_ssize_t nlen,
const char *value, const apr_ssize_t vlen)
{
apr_pool_t *p = apreq_env_pool(ctx);
apreq_cookie_t *c = apr_palloc(p, vlen + sizeof *c);
apreq_value_t *v = &c->v;
c->ctx = ctx;
v->size = vlen;
v->name = apr_pstrmemdup(p, name, nlen);
memcpy(v->data, value, vlen);
v->data[vlen] = 0;
c->version = version;
/* session cookie is the default */
if (c->version == NETSCAPE)
c->time.expires = NULL;
else
c->time.max_age = -1;
c->path = NULL;
c->domain = NULL;
c->port = NULL;
c->secure = 0;
c->comment = NULL;
c->commentURL = NULL;
return c;
}
static APR_INLINE apr_status_t get_pair(const char **data,
const char **n, apr_ssize_t *nlen,
const char **v, apr_ssize_t *vlen)
{
const char *d = *data;
unsigned char in_quotes = 0;
*n = d;
while( *d != '=' && !apr_isspace(*d) )
if (*d++ == 0) /*error: no '=' sign */
return APR_EGENERAL;
*nlen = d - *n;
do ++d; while ( *d == '=' || apr_isspace(*d) );
*v = d;
for (;;++d) {
switch (*d) {
case ';': case ',':
if (in_quotes)
break;
/* else fall through */
case 0:
goto pair_result;
case '\\':
if (d[1])
++d;
break;
case '"':
in_quotes = ! in_quotes;
}
}
pair_result:
*data = d;
*vlen = d - *v;
/* shouldn't still be in_quotes */
return in_quotes ? APR_EGENERAL : APR_SUCCESS;
}
APREQ_DECLARE(apreq_jar_t *) apreq_jar_parse(void *ctx,
const char *d)
{
apr_pool_t *p = apreq_env_pool(ctx);
apreq_cookie_version_t version;
apreq_jar_t *j = NULL;
apreq_cookie_t *c;
const char *name, *value;
apr_ssize_t nlen, vlen;
/* initialize jar */
if (d == NULL) {
/* use the environment's cookie data */
/* fetch ctx->jar (cached jar) */
if ( apreq_env_jar(ctx, &j) == APR_SUCCESS && j != NULL)
return j;
d = apreq_env_cookie(ctx);
j = apreq_table_make(apreq_env_pool(ctx), APREQ_DEFAULT_NELTS);
/* XXX: potential race condition here
between env_jar fetch and env_jar set. */
apreq_env_jar(ctx,&j); /* set (cache) ctx->jar */
if (d == NULL)
return j;
}
else {
j = apreq_table_make(p, APREQ_DEFAULT_NELTS);
}
#ifdef DEBUG
apreq_log(APREQ_DEBUG(ctx), "parsing cookie data: %s",d);
#endif
/* parse d */
parse_header:
c = NULL;
version = NETSCAPE;
while (apr_isspace(*d))
++d;
/* XXX cheat: assume "$..." => "$Version" => RFC Cookie header */
if (*d == '$') {
version = RFC;
while (*d && !apr_isspace(*d))
++d;
}
for (;;) {
while (*d == ';' || apr_isspace(*d))
++d;
switch (*d) {
case 0:
return j;
case ',':
++d;
goto parse_header;
case '$':
if ( c == NULL || version == NETSCAPE ||
get_pair(&d, &name, &nlen, &value, &vlen) != APR_SUCCESS )
return j; /* XXX: log error? */
apreq_cookie_attr(c, apr_pstrmemdup(p, name, nlen),
apr_pstrmemdup(p, value, vlen));
break;
default:
if ( get_pair(&d, &name, &nlen, &value, &vlen) != APR_SUCCESS )
return j; /* XXX: log error? */
c = apreq_cookie_make(ctx, version, name, nlen, value, vlen);
apreq_jar_add(j, c);
}
}
/* NOT REACHED */
return j;
}
#define ADD_ATTR(name) do { strcpy(f,c->name ? "; " #name "=%s" : \
"%.0s"); f+= strlen(f); } while (0)
APREQ_DECLARE(int) apreq_cookie_serialize(const apreq_cookie_t *c,
char *buf, apr_size_t len)
{
/* The format string must be large enough to accomodate all
* of the cookie attributes. The current attributes sum to
* ~80 characters (w/ 6 padding chars per attr), so anything
* over that should number be fine.
*/
char format[128] = "%s=%s";
char *f = format + strlen(format);
/* XXX protocol enforcement (for debugging, anyway) ??? */
if (c->v.name == NULL)
return -1;
if (c->version == NETSCAPE) {
ADD_ATTR(path);
ADD_ATTR(domain);
strcpy(f, c->time.expires ? "; expires=%s" : "%.0s");
f += strlen(f);
if (c->secure)
strcpy(f, "; secure");
return apr_snprintf(buf, len, format, c->v.name, c->v.data,
c->path, c->domain, c->time.expires);
}
/* c->version == RFC */
ADD_ATTR(version);
ADD_ATTR(path);
ADD_ATTR(domain);
ADD_ATTR(port);
ADD_ATTR(comment);
ADD_ATTR(commentURL);
/* "%.0s" very hackish, but it should be safe. max_age is
* the last va_arg, and we're not actually printing it in
* the "%.0s" case. Should check the apr_snprintf implementation
* just for certainty though.
*/
strcpy(f, c->time.max_age >= 0 ? "; max_age=%ld" : "%.0s");
f += strlen(f);
if (c->secure)
strcpy(f, "; secure");
return apr_snprintf(buf, len, format, c->v.name, c->v.data,
c->version, c->path, c->domain, c->port,
c->comment, c->commentURL, c->time.max_age);
}
#undef ADD_ATTR
APREQ_DECLARE(const char*) apreq_cookie_as_string(const apreq_cookie_t *c,
apr_pool_t *p)
{
char s[4096];
int n = apreq_cookie_serialize(c, s, 4096);
if ( n > 0 && n < 4096 )
return apr_pstrmemdup(p, s, n);
else
return NULL;
}
APREQ_DECLARE(apr_status_t) apreq_cookie_bake(apreq_cookie_t *c)
{
char s[4096];
int n = apreq_cookie_serialize(c, s, 4096);
if (n > 0 && n < 4096)
return apreq_env_set_cookie(c->ctx, s);
else
return APR_EGENERAL; /* cookie is too large */
}
APREQ_DECLARE(apr_status_t) apreq_cookie_bake2(apreq_cookie_t *c)
{
char s[4096];
int n;
if (c->version != RFC)
return APREQ_ERROR; /* wrong type */
n = apreq_cookie_serialize(c, s, 4096);
if (n > 0 && n < 4096)
return apreq_env_set_cookie2(c->ctx, s);
else
return APR_EGENERAL; /* cookie is too large */
}
#ifdef NEEDS_A_NEW_HOME
void (apreq_cookie_push)(apreq_cookie_t *c, apreq_value_t *v)
{
apreq_cookie_push(c,v);
}
void apreq_cookie_addn(apreq_cookie_t *c, char *val, int len)
{
apreq_value_t *v = (apreq_value_t *)apr_array_push(c->values.data);
v->data = val;
v->size = len;
}
void apreq_cookie_add(apreq_cookie_t *c, char *val, int len)
{
apr_array_header_t *a = (apr_array_header_t *)c->values.data;
apreq_cookie_addn(c, apr_pstrndup(a->pool, val, len), len);
}
APREQ_dDECODE(apreq_cookie_decode)
{
apr_array_header_t *a;
apreq_value_t *v;
apr_off_t len;
char *word;
word = apr_pstrdup(p,key);
len = apreq_unescape(word);
if (len <= 0) /* key size must be > 0 */
return NULL;
a = apr_array_make(p, APREQ_DEFAULT_NELTS, sizeof(apreq_value_t));
v = (apreq_value_t *)apr_array_push(a);
v->data = word;
v->size = len;
while ( *val && (word = apr_getword(p, &val, '&')) ) {
len = apreq_unescape(word);
if (len < 0)
return NULL; /* bad escape data */
v = (apreq_value_t *)apr_array_push(a);
v->data = word;
v->size = len;
}
return a;
}
static const char c2x_table[] = "0123456789abcdef";
APREQ_dENCODE(apreq_cookie_encode)
{
apr_size_t len = 0;
char *res, *data;
int i;
if (s == 0)
return apr_pcalloc(p,1);
for (i = 0; i < s; ++i)
len += a[i].size;
res = data = apr_palloc(p, 3*len + a->nelts);
for (i = 0; i < s; ++i) {
apr_size_t n = a[i].size;
const unsigned char *s = (const unsigned char *)a[i].data;
while (n--) {
unsigned c = *s;
if (apr_isalnum(c))
*data++ = c;
else if (c == ' ')
*data++ = '+';
else {
#if APR_CHARSET_EBCDIC
c = apr_xlate_conv_byte(ap_hdrs_to_ascii, (unsigned char)c);
#endif
*data++ = '%';
*data++ = c2x_table[c >> 4];
*data++ = c2x_table[c & 0xf];
}
++s;
}
*data++ = (i == 0) ? '=' : '&';
}
if (s == 1)
data[0] = 0; /* no value: name= */
else
data[-1] = 0; /* replace final '&' with '\0' */
return res;
}
#endif /* NEEDS_A_NEW_HOME */
1.1 httpd-apreq-2/src/apreq_env.h
Index: apreq_env.h
===================================================================
#ifndef APREQ_ENV_H
#define APREQ_ENV_H
#include "apreq_cookie.h"
/*
#include "apreq_request.h"
*/
#ifdef __cplusplus
extern "C" {
#endif
/* XXX: Per-process initialization */
struct apreq_request_env {
int foo;
/*
apr_status_t (*init)(void *ctx, apreq_parser_cfg_t *cfg);
apreq_request_t *(*make)(void *ctx);
apr_status_t (*parse)(apreq_request_t *req);
*/
};
struct apreq_cookie_env {
const char *(*in)(void *ctx);
const char *(*in2)(void *ctx);
apr_status_t (*jar)(void *ctx, apreq_jar_t **j);
apr_status_t (*out)(void *ctx, const char *c);
apr_status_t (*out2)(void *ctx, const char *c);
};
extern const struct apreq_env {
struct apreq_request_env r;
struct apreq_cookie_env c;
/* uri components (cookies need to know the host/domain and path) */
const char *(*uri)(void *ctx);
const char *(*uri_path)(void *ctx);
apr_pool_t *(*pool)(void *ctx);
void (*log)(const char *file, int line, int level,
apr_status_t status, void *ctx, const char *fmt, ...);
} APREQ_ENV;
APREQ_DECLARE(void) apreq_log(const char *file, int line, int level,
apr_status_t status, void *ctx, const char
*fmt, ...);
APREQ_DECLARE(apr_status_t) apreq_env_jar(void *ctx, apreq_jar_t **j);
APREQ_DECLARE(const char *) apreq_env_cookie(void *ctx);
APREQ_DECLARE(const char *) apreq_env_cookie2(void *ctx);
APREQ_DECLARE(apr_status_t) apreq_env_set_cookie(void *ctx, const char *s);
APREQ_DECLARE(apr_status_t) apreq_env_set_cookie2(void *ctx, const char *s);
APREQ_DECLARE(apr_status_t) apreq_env_cookie_jar(void *ctx,
apreq_jar_t **j);
#define apreq_env_pool(r) APREQ_ENV.pool(r)
#define apreq_env_cookie(r) APREQ_ENV.c.in(r)
#define apreq_env_cookie2(r) APREQ_ENV.c.in2(r)
#define apreq_env_set_cookie(r,s) APREQ_ENV.c.out(r,s)
#define apreq_env_set_cookie2(r,s) APREQ_ENV.c.out2(r,s)
#define AP_LOG_MARK __FILE__,__LINE__
#define AP_LOG_EMERG 0 /* system is unusable */
#define AP_LOG_ALERT 1 /* action must be taken immediately */
#define AP_LOG_CRIT 2 /* critical conditions */
#define AP_LOG_ERR 3 /* error conditions */
#define AP_LOG_WARNING 4 /* warning conditions */
#define AP_LOG_NOTICE 5 /* normal but significant condition */
#define AP_LOG_INFO 6 /* informational */
#define AP_LOG_DEBUG 7 /* debug-level messages */
#define APREQ_LOG_DEBUG(r) AP_LOG_MARK, AP_LOG_DEBUG,0,(r)
#define APREQ_LOG_ERROR(r) AP_LOG_MARK, AP_LOG_ERR,0,(r)
#define APREQ_LOG_WARN(r) AP_LOG_MARK, AP_LOG_WARNING,0,(r)
APREQ_DECLARE(void) apreq_warn(void *ctx, const char *fmt, ...);
#ifdef __cplusplus
}
#endif
#endif