cookies.c
上传用户:gzpyjq
上传日期:2013-01-31
资源大小:1852k
文件大小:14k
源码类别:

手机WAP编程

开发平台:

WINDOWS

  1. /*
  2.  * Module: cookies.c
  3.  *
  4.  * Description: Implements a minimal cookie handler for session persistence.
  5.  *
  6.  * References: RFC 2109
  7.  *
  8.  * Author: Paul Keogh, ANAM Wireless Internet Solutions
  9.  *
  10.  * Date: May 2000
  11.  */
  12. #include <string.h>
  13. #include <ctype.h>
  14. #include "gwlib/gwlib.h"
  15. #include "wsp.h"
  16. #include "cookies.h"
  17. /* Statics */
  18. static Octstr *get_header_value(Octstr*);
  19. static Cookie *parse_cookie(Octstr*);
  20. static void add_cookie_to_cache(const WSPMachine*, Cookie*);
  21. static void expire_cookies(List*);
  22. static void cookie_destroy(void*);
  23. static int have_cookie(List*, Cookie*);
  24. static int parse_http_date(const char*);
  25. static Cookie emptyCookie;
  26. Cookie *cookie_create(void)
  27. {
  28. Cookie *p;
  29. p = gw_malloc(sizeof(Cookie)); /* Never returns NULL */
  30. *p = emptyCookie;
  31. p -> max_age = -1;
  32. time (&p -> birth);
  33. return p;
  34. }
  35. void cookies_destroy(List *cookies) {
  36. gwlib_assert_init();
  37. if (cookies == NULL)
  38. return;
  39. list_destroy(cookies, cookie_destroy);
  40. }
  41. int get_cookies (List *headers, const WSPMachine *sm)
  42. {
  43. Octstr *header = NULL;
  44. Octstr *value = NULL;
  45. Cookie *cookie = NULL;
  46. long pos = 0;
  47. /* 
  48.      * This can happen if the user aborts while the HTTP request is pending from the server.
  49.  * In that case, the session machine is destroyed and is not available to this function
  50.  * for cookie caching.
  51.  */
  52. if (sm == NULL) {
  53. info (0, "No session machine for cookie retrieval");
  54. return 0;
  55. }
  56. for (pos = 0; pos < list_len (headers); pos++) {
  57. header = list_get(headers, pos);
  58. debug ("wap.wsp.http", 0, "get_cookies: Examining header (%s)", octstr_get_cstr (header));
  59. if (strncasecmp ("set-cookie", octstr_get_cstr (header),10) == 0) {
  60. debug ("wap.wsp.http", 0, "Caching cookie (%s)", octstr_get_cstr (header));
  61. if ((value = get_header_value (header)) == NULL) {
  62. error (0, "get_cookies: No value in (%s)", octstr_get_cstr(header));
  63. continue;
  64. }
  65. /* Parse the received cookie */
  66. if ((cookie = parse_cookie(value)) != NULL) {
  67. /* Check to see if this cookie is already present */
  68. if (have_cookie(sm->cookies, cookie) == 1) {
  69. debug("wap.wsp.http", 0, "parse_cookie: Cookie present");
  70.       cookie_destroy(cookie);
  71. continue;
  72. } else {
  73. add_cookie_to_cache(sm, cookie);
  74. debug("wap.wsp.http", 0, "get_cookies: Added (%s)", 
  75.   octstr_get_cstr(cookie -> name));
  76. }
  77. }
  78. }
  79. }
  80. debug("wap.wsp.http", 0, "get_cookies: End");
  81. return 0;
  82. }
  83. int set_cookies(List *headers, WSPMachine *sm)
  84. {
  85. Cookie *value = NULL;
  86. Octstr *cookie = NULL;
  87. long pos = 0;
  88. if (sm->cookies == NULL)
  89. sm->cookies = list_create();
  90. if (headers == NULL || sm == NULL) {
  91. error (0, "set_cookies: Null argument(s) - no headers, WSPMachine or both");
  92. return -1;
  93. }
  94. /* Expire cookies that have timed out */
  95. expire_cookies(sm->cookies);
  96. /* Walk through the cookie cache, adding the cookie to the request headers */
  97. if (list_len (sm->cookies) > 0) {
  98. debug("wap.wsp.http", 0, "set_cookies: Cookies in cache");
  99. for (pos = 0; pos < list_len(sm->cookies); pos++) {
  100. value = list_get(sm->cookies, pos);
  101. cookie = octstr_create("Cookie: ");
  102. octstr_append(cookie, value->version);
  103. octstr_append(cookie, value->name);
  104. octstr_append_char(cookie, '=');
  105. octstr_append(cookie, value->value);
  106. if (value->path) {
  107. octstr_append_char(cookie, ';');
  108. octstr_append(cookie, value->path);
  109. }
  110. if (value->domain) {
  111. octstr_append_char(cookie, ';');
  112. octstr_append(cookie, value->domain);
  113. }
  114. list_append(headers, cookie);
  115. debug("wap.wsp.http", 0, "set_cookies: Added (%s)", octstr_get_cstr (cookie));
  116. }
  117. } else
  118. debug("wap.wsp.http", 0, "set_cookies: No cookies in cache");
  119. return 0;
  120. }
  121. /*
  122.  * Private interface functions
  123.  */
  124. /*
  125.  * Function: get_header_value
  126.  *
  127.  * Description: Gets the header value as an Octstr.
  128.  */
  129. static Octstr *get_header_value(Octstr *header) 
  130. {
  131. Octstr *h = NULL;
  132. long colon = -1;
  133. if (header == NULL) {
  134. error(0, "get_header_value: NULL argument");
  135. return NULL;
  136. }
  137. octstr_strip_blanks(header);
  138. colon = octstr_search_char(header, ':', 0);
  139. if (colon == -1) {
  140. error(0, "get_header_value: Malformed header (%s)", octstr_get_cstr (header));
  141. return NULL;
  142. } else {
  143. h = octstr_copy(header, colon + 1, octstr_len(header));
  144. octstr_strip_blanks(h);
  145. }
  146. debug("wap.wsp.http", 0, "get_header_value: Value (%s)", octstr_get_cstr (h));
  147. return h;
  148. }
  149. /*
  150.  * Function: parse_cookie
  151.  *
  152.  * Description: Parses the received cookie and rewrites it for sending.
  153.  */
  154. static Cookie *parse_cookie(Octstr *cookiestr)
  155. {
  156. char *v = NULL;
  157. char *p = NULL;
  158. int delta = 0;
  159. Cookie *c = NULL;
  160. Octstr **f = NULL;
  161. if (cookiestr == NULL) {
  162. error(0, "parse_cookie: NULL argument");
  163. return NULL;
  164. }
  165. v = gw_strdup(octstr_get_cstr (cookiestr));
  166. p = strtok(v, ";");
  167. c = cookie_create(); /* Never returns NULL */
  168. while (p != NULL) {
  169. while (isspace((int)*p)) p++; /* Skip leading whitespace */
  170. if (strncasecmp("version", p, 7) == 0)
  171. f = &c -> version;
  172. else if (strncasecmp("path", p, 4) == 0)
  173. f = &c -> path;
  174. else if (strncasecmp("domain", p, 6) == 0)
  175. f = &c -> domain;
  176. else if (strncasecmp("max-age", p, 7) == 0) {
  177. c -> max_age = atol(strrchr (p, '=') + 1);
  178. p = strtok(NULL, ";");
  179. continue;
  180.         else if (strncasecmp("expires", p, 7) == 0) {
  181. delta = parse_http_date(p);
  182. if (delta != -1) 
  183.                 c->max_age = delta;
  184. p = strtok(NULL, ";");
  185. continue;
  186. }
  187. else if (strncasecmp("comment", p, 7) == 0 || strncasecmp("secure", p, 6) == 0) {
  188. p = strtok(NULL, ";");
  189. continue;
  190. }
  191. else { /* Name value pair - this should be first */
  192. char *equals = NULL;
  193. if ((equals = strrchr(p, '=')) != NULL) {
  194. *equals = '';
  195. c->name = octstr_create(p);
  196. c->value = octstr_create(equals + 1);
  197. } else {
  198. error(0, "parse_cookie: Bad name=value cookie component (%s)", p);
  199. cookie_destroy(c);
  200. return NULL;
  201. }
  202. p = strtok(NULL, ";");
  203. continue;
  204. }
  205. if (*f != NULL) { /* Undefined behaviour - 4.2.2 */
  206. error(0, "parse_cookie: Duplicate cookie field (%s), discarding", p);
  207. p = strtok(NULL, ";");
  208. continue;
  209. }
  210. *f = octstr_create("$");
  211. octstr_append_cstr(*f, p);
  212. p = strtok(NULL, ";");
  213. }
  214. /* Process version - 4.3.4 */
  215. if (c->version == NULL) {
  216. c->version = octstr_create("");
  217. octstr_append_cstr(c->version, "$Version="0";");
  218. }
  219. gw_free (v);
  220. return c;
  221. }
  222. /*
  223.  * Function: add_cookie_to_cache
  224.  *
  225.  * Description: Adds the cookie to the WSPMachine cookie cache.
  226.  */
  227. static void add_cookie_to_cache(const WSPMachine *sm, Cookie *value)
  228. {
  229. gw_assert(sm != NULL);
  230. gw_assert(sm -> cookies != NULL);
  231. gw_assert(value != NULL);
  232. list_append(sm -> cookies, value);
  233. return;
  234. }
  235. /*
  236.  * Function: have_cookie
  237.  *
  238.  * Description: Checks to see if the cookie is present in the list.
  239.  */
  240. static int have_cookie(List *cookies, Cookie *cookie)
  241. {
  242. Cookie *value = NULL;
  243. long pos = 0;
  244. if (cookies == NULL || cookie == NULL) {
  245. error(0, "have_cookie: Null argument(s) - no Cookie list, Cookie or both");
  246. return 0;
  247. }
  248. /* Walk through the cookie cache, comparing cookie */
  249. while (pos < list_len(cookies)) {
  250. value = list_get(cookies, pos);
  251. /* octstr_compare() now only returns 0 on an exact match or if both args are 0 */
  252. debug ("wap.wsp.http", 0, "have_cookie: Comparing name (%s:%s), path (%s:%s), domain (%s:%s)",
  253.    cookie->name == NULL ? "NULL" : octstr_get_cstr(cookie -> name), 
  254.    value->name == NULL ? "NULL" : octstr_get_cstr(value -> name),
  255.    cookie->path == NULL ? "NULL" : octstr_get_cstr(cookie -> path), 
  256.    value->path == NULL ? "NULL" : octstr_get_cstr(value -> path),
  257.    cookie->domain == NULL ? "NULL" : octstr_get_cstr(cookie -> domain), 
  258.    value->domain == NULL ? "NULL" : octstr_get_cstr(value -> domain));
  259. /* Match on no value or value and value equality for name, path and domain */
  260. if ((value->name == NULL || 
  261. ((value->name != NULL) && octstr_compare(value->name, cookie->name) == 0)) &&
  262. (value->path == NULL ||
  263. ((value->path != NULL) && octstr_compare(value->path, cookie->path) == 0)) &&
  264. ((value->domain == NULL) ||
  265. ((value->domain != NULL) && octstr_compare(value->domain, cookie->domain) == 0))) {
  266. /* We have a match according to 4.3.3 - discard the old one */
  267. cookie_destroy(value);
  268. list_delete(cookies, pos, 1);
  269. /* Discard the new cookie also if max-age is 0 - set if expiry date is up */
  270. if (cookie->max_age == 0) {
  271. debug("wap.wsp.http", 0, "have_cookie: Discarding expired cookie (%s)",
  272.       octstr_get_cstr(cookie->name));
  273. return 1;
  274. }
  275. debug("wap.wsp.http", 0, "have_cookie: Updating cached cookie (%s)", 
  276.                   octstr_get_cstr (cookie->name));
  277. break;
  278. } else
  279. pos++;
  280. }
  281. return 0;
  282. }
  283. /*
  284.  * Function: expire_cookies
  285.  *
  286.  * Description: Walks thru the cookie list and checking for expired cookies.
  287.  */
  288. static void expire_cookies(List *cookies)
  289. {
  290. Cookie *value = NULL;
  291. time_t now = 0;
  292. long pos = 0;
  293. if (cookies == NULL) {
  294. error(0, "expire_cookies: Null argument(s) - no Cookie list");
  295. return;
  296. }
  297. /* Walk through the cookie cache */
  298. time(&now);
  299. if (list_len (cookies) > 0) {
  300. debug("wap.wsp.http", 0, "expire_cookies: Cookies in cache");
  301. for (pos = 0; pos < list_len(cookies); pos++) {
  302. value = list_get(cookies, pos);
  303. gw_assert(value != NULL);
  304. if (value->max_age != -1) { /* Interesting value */
  305. if (value->max_age + value->birth < now) {
  306. debug("wap.wsp.http", 0, "expire_cookies: Expired cookie (%s)",
  307.   octstr_get_cstr(value->name));
  308. cookie_destroy(value);
  309. list_delete(cookies, pos, 1);
  310. }
  311. }
  312. }
  313. } else
  314. debug("wap.wsp.http", 0, "expire_cookies: No cookies in cache");
  315. return;
  316. }
  317. static void cookie_destroy (void *p)
  318. {
  319. Cookie *cookie;
  320. if (p == NULL) 
  321.         return;
  322. cookie = p;
  323. octstr_destroy(cookie->name);
  324. octstr_destroy(cookie->value);
  325. octstr_destroy(cookie->version);
  326. octstr_destroy(cookie->domain);
  327. octstr_destroy(cookie->path);
  328. gw_free(cookie);
  329. debug("wap.wsp.http", 0, "cookie_destroy: Destroyed cookie");
  330. return;
  331. }
  332. /*
  333.  * Function: parse_http_date
  334.  *
  335.  * Description: Parses an HTTP date format as used by the Expires: header. See RFC 2616
  336.  * section 3.3.1 for more information.
  337.  * HTTP applications have historically allowed three different formats
  338.  * for the representation of date/time stamps:
  339.  *
  340.  *    Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
  341.  *    Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
  342.  *    Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format
  343.  *
  344.  * The first format is preferred as an Internet standard and represents
  345.  * a fixed-length subset of that defined by RFC 1123 [8] (an update to
  346.  * RFC 822 [9]). The second format is in common use, but is based on the
  347.  * obsolete RFC 850 [12] date format and lacks a four-digit year.
  348.  * HTTP/1.1 clients and servers that parse the date value MUST accept
  349.  * all three formats (for compatibility with HTTP/1.0), though they MUST
  350.  * only generate the RFC 1123 format for representing HTTP-date values
  351.  * in header fields.
  352.  *
  353.  * Returns: -1 on success, max-age sematic value on success.
  354.  */
  355. static const char* months[] = {
  356. "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  357.     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL
  358. };
  359. static int month_index(const char *s)
  360. {
  361. const char **p = &months[0];
  362. int i = 0;
  363. while (*p != NULL) {
  364. if (strcmp(s, *p) == 0)
  365. return i;
  366. p++, i++;
  367. }
  368. return -1;
  369. }
  370. static int parse_http_date(const char *expires)
  371. {
  372. struct tm ti;
  373. char *p = NULL;
  374. char *date = NULL;
  375. char month[MAX_HTTP_DATE_LENGTH];
  376. time_t rv;
  377. time_t now;
  378. memset(&ti, 0, sizeof(struct tm));
  379. /* Break up the Expires: header */
  380. if (!(date = strchr(expires, '='))) {
  381. error(0, "parse_http_date: Bogus expires type=value header (%s)", expires);
  382. return -1;
  383. } else {
  384. date++;
  385. while (isspace((int)*date))
  386. ++date;
  387. }
  388. /* Onto the date value */
  389. if (!(p = strchr (date,' '))) {
  390. error(0, "parse_http_date: Bogus date string (%s)", date);
  391. return -1;
  392. } else
  393. while (isspace((int)*p))
  394. ++p;
  395. if (MAX_HTTP_DATE_LENGTH < strlen(p)) {
  396. error(0, "parse_http_date: %s blows length limit (%d)", date, MAX_HTTP_DATE_LENGTH);
  397. return -1;
  398. }
  399. if (isalpha((int)*p)) {
  400. /* ctime */
  401. sscanf(p, (strstr(p, "DST") ? "%s %d %d:%d:%d %*s %d" : "%s %d %d:%d:%d %d"),
  402.    month, &ti.tm_mday, &ti.tm_hour, &ti.tm_min,
  403.    &ti.tm_sec, &ti.tm_year);
  404. ti.tm_year -= 1900;
  405. } else if (p[2] == '-') {
  406. /* RFC 850 (normal HTTP) */
  407. char  buf[MAX_HTTP_DATE_LENGTH];
  408. sscanf(p, "%s %d:%d:%d", buf, &ti.tm_hour, &ti.tm_min, &ti.tm_sec);
  409. buf[2] = '';
  410. ti.tm_mday = atoi(buf);
  411. buf[6] = '';
  412. strcpy(month, &buf[3]);
  413. ti.tm_year = atoi(&buf[7]);
  414. /* Prevent wraparound from ambiguity */
  415. if (ti.tm_year < 70) {
  416. ti.tm_year += 100;
  417. } else if (ti.tm_year > 1900) {
  418. ti.tm_year -= 1900;
  419. }
  420. } else {
  421. /* RFC 822 */
  422. sscanf(p,"%d %s %d %d:%d:%d",&ti.tm_mday, month, &ti.tm_year,
  423.    &ti.tm_hour, &ti.tm_min, &ti.tm_sec);
  424. /* 
  425.          * since tm_year is years since 1900 and the year we parsed
  426.  * is absolute, we need to subtract 1900 years from it
  427.  */
  428. ti.tm_year -= 1900;
  429. }
  430. ti.tm_mon = month_index(month);
  431. if (ti.tm_mon == -1) {
  432. error(0, "parse_http_date () failed on bad month value (%s)", month);
  433. return -1;
  434. }
  435. ti.tm_isdst = -1;
  436. rv = mktime(&ti);
  437. if (ti.tm_isdst)
  438. rv -= 3600;
  439. if (rv == -1) {
  440. error(0, "parse_http_date(): mktime() was unable to resolve date/time: %s", 
  441.   asctime (&ti));
  442. return -1;
  443. }
  444. debug("parse_http_date", 0, "Parsed date (%s) as: %s", date, asctime(&ti));
  445. /* 
  446.      * If rv is valid, it should be some time in the (near) future. Normalise this to
  447.  * a max-age semantic so we can use the same expiry mechanism 
  448.  */
  449. now = time(NULL);
  450. if (rv - now < 0) {
  451. /* This is bad - set the delta to 0 so we expire next time around */
  452. error(0, "parse_http_date () Expiry time (%s) (delta=%ld) is in the past !", 
  453.   asctime (&ti), rv-now);
  454. return 0;
  455. }
  456. return rv - now;
  457. }
  458.