cookies.c

来自「mms client」· C语言 代码 · 共 575 行

C
575
字号
/* * Module: cookies.c * * Description: Implements a minimal cookie handler for session persistence. * * References: RFC 2109 * * Author: Paul Keogh, ANAM Wireless Internet Solutions * * Date: May 2000 */#include <string.h>#include <ctype.h>#include "gwlib/gwlib.h"#include "wsp.h"#include "cookies.h"/* Statics */static Octstr *get_header_value(Octstr*);static Cookie *parse_cookie(Octstr*);static void add_cookie_to_cache(const WSPMachine*, Cookie*);static void expire_cookies(List*);static void cookie_destroy(void*);static int have_cookie(List*, Cookie*);static int parse_http_date(const char*);static Cookie emptyCookie;Cookie *cookie_create(void){	Cookie *p;	p = gw_malloc(sizeof(Cookie));	/* Never returns NULL */	*p = emptyCookie;	p -> max_age = -1;	time (&p -> birth);	return p;}void cookies_destroy(List *cookies) {	gwlib_assert_init();	if (cookies == NULL)		return;	list_destroy(cookies, cookie_destroy);}int get_cookies (List *headers, const WSPMachine *sm){	Octstr *header = NULL;	Octstr *value = NULL;	Cookie *cookie = NULL;	long pos = 0;	/*      * This can happen if the user aborts while the HTTP request is pending from the server.	 * In that case, the session machine is destroyed and is not available to this function	 * for cookie caching.	 */	if (sm == NULL) {		info (0, "No session machine for cookie retrieval");		return 0;	}	for (pos = 0; pos < list_len (headers); pos++) {		header = list_get(headers, pos);		debug ("wap.wsp.http", 0, "get_cookies: Examining header (%s)", octstr_get_cstr (header));		if (strncasecmp ("set-cookie", octstr_get_cstr (header),10) == 0) {					debug ("wap.wsp.http", 0, "Caching cookie (%s)", octstr_get_cstr (header));			if ((value = get_header_value (header)) == NULL) {				error (0, "get_cookies: No value in (%s)", octstr_get_cstr(header));				continue;			}			/* Parse the received cookie */			if ((cookie = parse_cookie(value)) != NULL) {				/* Check to see if this cookie is already present */				if (have_cookie(sm->cookies, cookie) == 1) {					debug("wap.wsp.http", 0, "parse_cookie: Cookie present");					      cookie_destroy(cookie);					continue;				} else {					add_cookie_to_cache(sm, cookie);					debug("wap.wsp.http", 0, "get_cookies: Added (%s)", 						  octstr_get_cstr(cookie -> name));				}			}		}	}	debug("wap.wsp.http", 0, "get_cookies: End");	return 0;}int set_cookies(List *headers, WSPMachine *sm){	Cookie *value = NULL;	Octstr *cookie = NULL;	long pos = 0;	if (sm->cookies == NULL)		sm->cookies = list_create();	if (headers == NULL || sm == NULL) {		error (0, "set_cookies: Null argument(s) - no headers, WSPMachine or both");		return -1;	}	/* Expire cookies that have timed out */	expire_cookies(sm->cookies);	/* Walk through the cookie cache, adding the cookie to the request headers */	if (list_len (sm->cookies) > 0) {		debug("wap.wsp.http", 0, "set_cookies: Cookies in cache");		for (pos = 0; pos < list_len(sm->cookies); pos++) {			value = list_get(sm->cookies, pos);			cookie = octstr_create("Cookie: ");			octstr_append(cookie, value->version);			octstr_append(cookie, value->name);			octstr_append_char(cookie, '=');			octstr_append(cookie, value->value);			if (value->path) {				octstr_append_char(cookie, ';');				octstr_append(cookie, value->path);			}			if (value->domain) {				octstr_append_char(cookie, ';');				octstr_append(cookie, value->domain);			}			list_append(headers, cookie);			debug("wap.wsp.http", 0, "set_cookies: Added (%s)", octstr_get_cstr (cookie));		}	} else		debug("wap.wsp.http", 0, "set_cookies: No cookies in cache");	return 0;}/* * Private interface functions *//* * Function: get_header_value * * Description: Gets the header value as an Octstr. */static Octstr *get_header_value(Octstr *header) {	Octstr *h = NULL;	long colon = -1;		if (header == NULL) {		error(0, "get_header_value: NULL argument");		return NULL;	}	octstr_strip_blanks(header);	colon = octstr_search_char(header, ':', 0);	if (colon == -1) {		error(0, "get_header_value: Malformed header (%s)", octstr_get_cstr (header));		return NULL;	} else {		h = octstr_copy(header, colon + 1, octstr_len(header));		octstr_strip_blanks(h);	}	debug("wap.wsp.http", 0, "get_header_value: Value (%s)", octstr_get_cstr (h));	return h;}/* * Function: parse_cookie * * Description: Parses the received cookie and rewrites it for sending. */static Cookie *parse_cookie(Octstr *cookiestr){	char *v = NULL;	char *p = NULL;	int delta = 0;	Cookie *c = NULL;	Octstr **f = NULL;	if (cookiestr == NULL) {		error(0, "parse_cookie: NULL argument");		return NULL;	}	v = gw_strdup(octstr_get_cstr (cookiestr));	p = strtok(v, ";");		c = cookie_create();	/* Never returns NULL */	while (p != NULL) {		while (isspace((int)*p)) p++;		/* Skip leading whitespace */		if (strncasecmp("version", p, 7) == 0)			f = &c -> version;		else if (strncasecmp("path", p, 4) == 0)			f = &c -> path;		else if (strncasecmp("domain", p, 6) == 0)			f = &c -> domain;		else if (strncasecmp("max-age", p, 7) == 0) {			c -> max_age = atol(strrchr (p, '=') + 1);			p = strtok(NULL, ";");			continue;		}         else if (strncasecmp("expires", p, 7) == 0) {			delta = parse_http_date(p);			if (delta != -1)                 c->max_age = delta;			p = strtok(NULL, ";");			continue;		}		else if (strncasecmp("comment", p, 7) == 0 || strncasecmp("secure", p, 6) == 0) {			p = strtok(NULL, ";");			continue;		}		else {		/* Name value pair - this should be first */			char *equals = NULL;			if ((equals = strrchr(p, '=')) != NULL) {				*equals = '\0';				c->name = octstr_create(p);				c->value = octstr_create(equals + 1);			} else {				error(0, "parse_cookie: Bad name=value cookie component (%s)", p);				cookie_destroy(c);				return NULL;			}			p = strtok(NULL, ";");			continue;		}		if (*f != NULL) {	/* Undefined behaviour - 4.2.2 */			error(0, "parse_cookie: Duplicate cookie field (%s), discarding", p);			p = strtok(NULL, ";");			continue;		}		*f = octstr_create("$");		octstr_append_cstr(*f, p);		p = strtok(NULL, ";");	}	/* Process version - 4.3.4 */	if (c->version == NULL) {		c->version = octstr_create("");		octstr_append_cstr(c->version, "$Version=\"0\";");	}	gw_free (v);	return c;}/* * Function: add_cookie_to_cache * * Description: Adds the cookie to the WSPMachine cookie cache. */static void add_cookie_to_cache(const WSPMachine *sm, Cookie *value){	gw_assert(sm != NULL);	gw_assert(sm -> cookies != NULL);	gw_assert(value != NULL);	list_append(sm -> cookies, value);	return;}/* * Function: have_cookie * * Description: Checks to see if the cookie is present in the list. */static int have_cookie(List *cookies, Cookie *cookie){	Cookie *value = NULL;	long pos = 0;	if (cookies == NULL || cookie == NULL) {		error(0, "have_cookie: Null argument(s) - no Cookie list, Cookie or both");		return 0;	}	/* Walk through the cookie cache, comparing cookie */	while (pos < list_len(cookies)) {		value = list_get(cookies, pos);		/* octstr_compare() now only returns 0 on an exact match or if both args are 0 */		debug ("wap.wsp.http", 0, "have_cookie: Comparing name (%s:%s), path (%s:%s), domain (%s:%s)",			   cookie->name == NULL ? "NULL" : octstr_get_cstr(cookie -> name), 			   value->name == NULL ? "NULL" : octstr_get_cstr(value -> name),			   cookie->path == NULL ? "NULL" : octstr_get_cstr(cookie -> path), 			   value->path == NULL ? "NULL" : octstr_get_cstr(value -> path),			   cookie->domain == NULL ? "NULL" : octstr_get_cstr(cookie -> domain), 			   value->domain == NULL ? "NULL" : octstr_get_cstr(value -> domain));		/* Match on no value or value and value equality for name, path and domain */		if ((value->name == NULL || 			((value->name != NULL) && octstr_compare(value->name, cookie->name) == 0)) &&			(value->path == NULL ||			((value->path != NULL) && octstr_compare(value->path, cookie->path) == 0)) &&			((value->domain == NULL) ||			((value->domain != NULL) && octstr_compare(value->domain, cookie->domain) == 0))) {						/* We have a match according to 4.3.3 - discard the old one */			cookie_destroy(value);			list_delete(cookies, pos, 1);			/* Discard the new cookie also if max-age is 0 - set if expiry date is up */			if (cookie->max_age == 0) {				debug("wap.wsp.http", 0, "have_cookie: Discarding expired cookie (%s)",				      octstr_get_cstr(cookie->name));				return 1;			}			debug("wap.wsp.http", 0, "have_cookie: Updating cached cookie (%s)",                   octstr_get_cstr (cookie->name));			break;		} else			pos++;	}	return 0;}/* * Function: expire_cookies * * Description: Walks thru the cookie list and checking for expired cookies. */static void expire_cookies(List *cookies){	Cookie *value = NULL;	time_t now = 0;	long pos = 0;	if (cookies == NULL) {		error(0, "expire_cookies: Null argument(s) - no Cookie list");		return;	}	/* Walk through the cookie cache */	time(&now);	if (list_len (cookies) > 0) {		debug("wap.wsp.http", 0, "expire_cookies: Cookies in cache");		for (pos = 0; pos < list_len(cookies); pos++) {			value = list_get(cookies, pos);			gw_assert(value != NULL);			if (value->max_age != -1) {		/* Interesting value */				if (value->max_age + value->birth < now) {					debug("wap.wsp.http", 0, "expire_cookies: Expired cookie (%s)",						  octstr_get_cstr(value->name));					cookie_destroy(value);					list_delete(cookies, pos, 1);				}			}		}	} else		debug("wap.wsp.http", 0, "expire_cookies: No cookies in cache");	return;}static void cookie_destroy (void *p){	Cookie *cookie;		if (p == NULL)         return;	cookie = p;	octstr_destroy(cookie->name);	octstr_destroy(cookie->value);	octstr_destroy(cookie->version);	octstr_destroy(cookie->domain);	octstr_destroy(cookie->path);	gw_free(cookie);	debug("wap.wsp.http", 0, "cookie_destroy: Destroyed cookie");	return;}/* * Function: parse_http_date * * Description: Parses an HTTP date format as used by the Expires: header. See RFC 2616 * section 3.3.1 for more information. * HTTP applications have historically allowed three different formats * for the representation of date/time stamps: * *    Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123 *    Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036 *    Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format * * The first format is preferred as an Internet standard and represents * a fixed-length subset of that defined by RFC 1123 [8] (an update to * RFC 822 [9]). The second format is in common use, but is based on the * obsolete RFC 850 [12] date format and lacks a four-digit year. * HTTP/1.1 clients and servers that parse the date value MUST accept * all three formats (for compatibility with HTTP/1.0), though they MUST * only generate the RFC 1123 format for representing HTTP-date values * in header fields. * * Returns: -1 on success, max-age sematic value on success. */static const char* months[] = {	"Jan", "Feb", "Mar", "Apr", "May", "Jun",    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL};static int month_index(const char *s){	const char **p = &months[0];	int i = 0;	while (*p != NULL) {		if (strcmp(s, *p) == 0)			return i;		p++, i++;	}	return -1;}static int parse_http_date(const char *expires){	struct tm ti;	char *p = NULL;	char *date = NULL;	char month[MAX_HTTP_DATE_LENGTH];	time_t rv;	time_t now;	memset(&ti, 0, sizeof(struct tm));	/* Break up the Expires: header */	if (!(date = strchr(expires, '='))) {		error(0, "parse_http_date: Bogus expires type=value header (%s)", expires);		return -1;	} else {		date++;		while (isspace((int)*date))			++date;	}	/* Onto the date value */	if (!(p = strchr (date,' '))) {		error(0, "parse_http_date: Bogus date string (%s)", date);		return -1;	} else		while (isspace((int)*p))			++p;	if (MAX_HTTP_DATE_LENGTH < strlen(p)) {		error(0, "parse_http_date: %s blows length limit (%d)", date, MAX_HTTP_DATE_LENGTH);		return -1;	}	if (isalpha((int)*p)) {		/* ctime */		sscanf(p, (strstr(p, "DST") ? "%s %d %d:%d:%d %*s %d" : "%s %d %d:%d:%d %d"),			   month, &ti.tm_mday, &ti.tm_hour, &ti.tm_min,			   &ti.tm_sec, &ti.tm_year);		ti.tm_year -= 1900;	} else if (p[2] == '-') {		/* RFC 850 (normal HTTP) */		char  buf[MAX_HTTP_DATE_LENGTH];		sscanf(p, "%s %d:%d:%d", buf, &ti.tm_hour, &ti.tm_min, &ti.tm_sec);		buf[2] = '\0';		ti.tm_mday = atoi(buf);		buf[6] = '\0';		strcpy(month, &buf[3]);		ti.tm_year = atoi(&buf[7]);		/* Prevent wraparound from ambiguity */		if (ti.tm_year < 70) {			ti.tm_year += 100;		} else if (ti.tm_year > 1900) {			ti.tm_year -= 1900;		}	} else {		/* RFC 822 */		sscanf(p,"%d %s %d %d:%d:%d",&ti.tm_mday, month, &ti.tm_year,			   &ti.tm_hour, &ti.tm_min, &ti.tm_sec);		/*          * since tm_year is years since 1900 and the year we parsed		 * is absolute, we need to subtract 1900 years from it		 */		ti.tm_year -= 1900;	}	ti.tm_mon = month_index(month);	if (ti.tm_mon == -1) {		error(0, "parse_http_date () failed on bad month value (%s)", month);		return -1;	}	ti.tm_isdst = -1;	rv = mktime(&ti);	if (ti.tm_isdst)		rv -= 3600;	if (rv == -1) {		error(0, "parse_http_date(): mktime() was unable to resolve date/time: %s", 			  asctime (&ti));		return -1;	}	debug("parse_http_date", 0, "Parsed date (%s) as: %s", date, asctime(&ti));	/*      * If rv is valid, it should be some time in the (near) future. Normalise this to	 * a max-age semantic so we can use the same expiry mechanism 	 */	now = time(NULL);	if (rv - now < 0) {		/* This is bad - set the delta to 0 so we expire next time around */		error(0, "parse_http_date () Expiry time (%s) (delta=%ld) is in the past !", 			  asctime (&ti), rv-now);		return 0;	}	return rv - now;} 

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?