⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 cookies.c

📁 wget (command line browser) source code
💻 C
📖 第 1 页 / 共 3 页
字号:
/* Support for cookies.   Copyright (C) 2001, 2002 Free Software Foundation, Inc.This file is part of GNU Wget.GNU Wget is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; either version 2 of the License, or (atyour option) any later version.GNU Wget is distributed in the hope that it will be useful, butWITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNUGeneral Public License for more details.You should have received a copy of the GNU General Public Licensealong with Wget; if not, write to the Free SoftwareFoundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.In addition, as a special exception, the Free Software Foundationgives permission to link the code of its release of Wget with theOpenSSL project's "OpenSSL" library (or with modified versions of itthat use the same license as the "OpenSSL" library), and distributethe linked executables.  You must obey the GNU General Public Licensein all respects for all of the code used other than "OpenSSL".  If youmodify this file, you may extend this exception to your version of thefile, but you are not obligated to do so.  If you do not wish to doso, delete this exception statement from your version.  *//* Written by Hrvoje Niksic.  Parts are loosely inspired by cookie   code submitted by Tomasz Wegrzanowski.   Ideas for future work:   * Implement limits on cookie-related sizes, such as max. cookie     size, max. number of cookies, etc.   * Add more "cookie jar" methods, such as methods to iterate over     stored cookies, to clear temporary cookies, to perform     intelligent auto-saving, etc.   * Support `Set-Cookie2' and `Cookie2' headers?  Does anyone really     use them?  */#include <config.h>#include <stdio.h>#ifdef HAVE_STRING_H# include <string.h>#else# include <strings.h>#endif#include <stdlib.h>#include <assert.h>#include <errno.h>#include "wget.h"#include "utils.h"#include "hash.h"#include "cookies.h"/* This should *really* be in a .h file!  */time_t http_atotm PARAMS ((const char *));/* Declarations of `struct cookie' and the most basic functions. *//* Cookie jar serves as cookie storage and a means of retrieving   cookies efficiently.  All cookies with the same domain are stored   in a linked list called "chain".  A cookie chain can be reached by   looking up the domain in the cookie jar's chains_by_domain table.   For example, to reach all the cookies under google.com, one must   execute hash_table_get(jar->chains_by_domain, "google.com").  Of   course, when sending a cookie to `www.google.com', one must search   for cookies that belong to either `www.google.com' or `google.com'   -- but the point is that the code doesn't need to go through *all*   the cookies.  */struct cookie_jar {  /* Cookie chains indexed by domain.  */  struct hash_table *chains;  int cookie_count;		/* number of cookies in the jar. */};/* Value set by entry point functions, so that the low-level   routines don't need to call time() all the time.  */time_t cookies_now;struct cookie_jar *cookie_jar_new (void){  struct cookie_jar *jar = xmalloc (sizeof (struct cookie_jar));  jar->chains = make_nocase_string_hash_table (0);  jar->cookie_count = 0;  return jar;}struct cookie {  char *domain;			/* domain of the cookie */  int port;			/* port number */  char *path;			/* path prefix of the cookie */  int secure;			/* whether cookie should be				   transmitted over non-https				   connections. */  int domain_exact;		/* whether DOMAIN must match as a				   whole. */  int permanent;		/* whether the cookie should outlive				   the session */  time_t expiry_time;		/* time when the cookie expires */  int discard_requested;	/* whether cookie was created to				   request discarding another				   cookie */  char *attr;			/* cookie attribute name */  char *value;			/* cookie attribute value */  struct cookie *next;		/* used for chaining of cookies in the				   same domain. */};#define PORT_ANY (-1)#define COOKIE_EXPIRED_P(c) ((c)->expiry_time != 0 && (c)->expiry_time < cookies_now)/* Allocate and return a new, empty cookie structure. */static struct cookie *cookie_new (void){  struct cookie *cookie = xmalloc (sizeof (struct cookie));  memset (cookie, '\0', sizeof (struct cookie));  /* Both cookie->permanent and cookie->expiry_time are now 0.  By     default, we assume that the cookie is non-permanent and valid     until the end of the session.  */  cookie->port = PORT_ANY;  return cookie;}/* Deallocate COOKIE and its components. */static voiddelete_cookie (struct cookie *cookie){  FREE_MAYBE (cookie->domain);  FREE_MAYBE (cookie->path);  FREE_MAYBE (cookie->attr);  FREE_MAYBE (cookie->value);  xfree (cookie);}/* Functions for storing cookies.   All cookies can be reached beginning with jar->chains.  The key in   that table is the domain name, and the value is a linked list of   all cookies from that domain.  Every new cookie is placed on the   head of the list.  *//* Find and return a cookie in JAR whose domain, path, and attribute   name correspond to COOKIE.  If found, PREVPTR will point to the   location of the cookie previous in chain, or NULL if the found   cookie is the head of a chain.   If no matching cookie is found, return NULL. */static struct cookie *find_matching_cookie (struct cookie_jar *jar, struct cookie *cookie,		      struct cookie **prevptr){  struct cookie *chain, *prev;  chain = hash_table_get (jar->chains, cookie->domain);  if (!chain)    goto nomatch;  prev = NULL;  for (; chain; prev = chain, chain = chain->next)    if (0 == strcmp (cookie->path, chain->path)	&& 0 == strcmp (cookie->attr, chain->attr)	&& cookie->port == chain->port)      {	*prevptr = prev;	return chain;      } nomatch:  *prevptr = NULL;  return NULL;}/* Store COOKIE to the jar.   This is done by placing COOKIE at the head of its chain.  However,   if COOKIE matches a cookie already in memory, as determined by   find_matching_cookie, the old cookie is unlinked and destroyed.   The key of each chain's hash table entry is allocated only the   first time; next hash_table_put's reuse the same key.  */static voidstore_cookie (struct cookie_jar *jar, struct cookie *cookie){  struct cookie *chain_head;  char *chain_key;  if (hash_table_get_pair (jar->chains, cookie->domain,			   &chain_key, &chain_head))    {      /* A chain of cookies in this domain already exists.  Check for         duplicates -- if an extant cookie exactly matches our domain,         port, path, and name, replace it.  */      struct cookie *prev;      struct cookie *victim = find_matching_cookie (jar, cookie, &prev);      if (victim)	{	  /* Remove VICTIM from the chain.  COOKIE will be placed at	     the head. */	  if (prev)	    {	      prev->next = victim->next;	      cookie->next = chain_head;	    }	  else	    {	      /* prev is NULL; apparently VICTIM was at the head of		 the chain.  This place will be taken by COOKIE, so		 all we need to do is:  */	      cookie->next = victim->next;	    }	  delete_cookie (victim);	  --jar->cookie_count;	  DEBUGP (("Deleted old cookie (to be replaced.)\n"));	}      else	cookie->next = chain_head;    }  else    {      /* We are now creating the chain.  Use a copy of cookie->domain	 as the key for the life-time of the chain.  Using	 cookie->domain would be unsafe because the life-time of the	 chain may exceed the life-time of the cookie.  (Cookies may	 be deleted from the chain by this very function.)  */      cookie->next = NULL;      chain_key = xstrdup (cookie->domain);    }  hash_table_put (jar->chains, chain_key, cookie);  ++jar->cookie_count;  DEBUGP (("\nStored cookie %s %d%s %s %s %d %s %s %s\n",	   cookie->domain, cookie->port,	   cookie->port == PORT_ANY ? " (ANY)" : "",	   cookie->path,	   cookie->permanent ? "permanent" : "nonpermanent",	   cookie->secure,	   cookie->expiry_time	   ? asctime (localtime (&cookie->expiry_time)) : "<undefined>",	   cookie->attr, cookie->value));}/* Discard a cookie matching COOKIE's domain, port, path, and   attribute name.  This gets called when we encounter a cookie whose   expiry date is in the past, or whose max-age is set to 0.  The   former corresponds to netscape cookie spec, while the latter is   specified by rfc2109.  */static voiddiscard_matching_cookie (struct cookie_jar *jar, struct cookie *cookie){  struct cookie *prev, *victim;  if (!hash_table_count (jar->chains))    /* No elements == nothing to discard. */    return;  victim = find_matching_cookie (jar, cookie, &prev);  if (victim)    {      if (prev)	/* Simply unchain the victim. */	prev->next = victim->next;      else	{	  /* VICTIM was head of its chain.  We need to place a new	     cookie at the head.  */	  char *chain_key = NULL;	  int res;	  res = hash_table_get_pair (jar->chains, victim->domain,				     &chain_key, NULL);	  assert (res != 0);	  if (!victim->next)	    {	      /* VICTIM was the only cookie in the chain.  Destroy the		 chain and deallocate the chain key.  */	      hash_table_remove (jar->chains, victim->domain);	      xfree (chain_key);	    }	  else	    hash_table_put (jar->chains, chain_key, victim->next);	}      delete_cookie (victim);      DEBUGP (("Discarded old cookie.\n"));    }}/* Functions for parsing the `Set-Cookie' header, and creating new   cookies from the wire.  */#define NAME_IS(string_literal)					\  BOUNDED_EQUAL_NO_CASE (name_b, name_e, string_literal)#define VALUE_EXISTS (value_b && value_e)#define VALUE_NON_EMPTY (VALUE_EXISTS && (value_b != value_e))/* Update the appropriate cookie field.  [name_b, name_e) are expected   to delimit the attribute name, while [value_b, value_e) (optional)   should delimit the attribute value.   When called the first time, it will set the cookie's attribute name   and value.  After that, it will check the attribute name for   special fields such as `domain', `path', etc.  Where appropriate,   it will parse the values of the fields it recognizes and fill the   corresponding fields in COOKIE.   Returns 1 on success.  Returns zero in case a syntax error is   found; such a cookie should be discarded.  */static intupdate_cookie_field (struct cookie *cookie,		     const char *name_b, const char *name_e,		     const char *value_b, const char *value_e){  assert (name_b != NULL && name_e != NULL);  if (!cookie->attr)    {      if (!VALUE_EXISTS)	return 0;      cookie->attr = strdupdelim (name_b, name_e);      cookie->value = strdupdelim (value_b, value_e);      return 1;    }  if (NAME_IS ("domain"))    {      if (!VALUE_NON_EMPTY)	return 0;      FREE_MAYBE (cookie->domain);      /* Strictly speaking, we should set cookie->domain_exact if the	 domain doesn't begin with a dot.  But many sites set the	 domain to "foo.com" and expect "subhost.foo.com" to get the	 cookie, and it apparently works.  */      if (*value_b == '.')	++value_b;      cookie->domain = strdupdelim (value_b, value_e);      return 1;    }  else if (NAME_IS ("path"))    {      if (!VALUE_NON_EMPTY)	return 0;      FREE_MAYBE (cookie->path);      cookie->path = strdupdelim (value_b, value_e);      return 1;    }  else if (NAME_IS ("expires"))    {      char *value_copy;      time_t expires;      if (!VALUE_NON_EMPTY)	return 0;      BOUNDED_TO_ALLOCA (value_b, value_e, value_copy);      expires = http_atotm (value_copy);      if (expires != -1)	{	  cookie->permanent = 1;	  cookie->expiry_time = (time_t)expires;	}      else	/* Error in expiration spec.  Assume default (cookie valid for	   this session.)  */	;      /* According to netscape's specification, expiry time in the	 past means that discarding of a matching cookie is	 requested.  */      if (cookie->expiry_time < cookies_now)	cookie->discard_requested = 1;      return 1;    }  else if (NAME_IS ("max-age"))    {      double maxage = -1;      char *value_copy;      if (!VALUE_NON_EMPTY)	return 0;      BOUNDED_TO_ALLOCA (value_b, value_e, value_copy);      sscanf (value_copy, "%lf", &maxage);      if (maxage == -1)	/* something went wrong. */	return 0;      cookie->permanent = 1;      cookie->expiry_time = cookies_now + maxage;      /* According to rfc2109, a cookie with max-age of 0 means that	 discarding of a matching cookie is requested.  */      if (maxage == 0)	cookie->discard_requested = 1;      return 1;    }  else if (NAME_IS ("secure"))    {      /* ignore value completely */      cookie->secure = 1;      return 1;    }  else    /* Unrecognized attribute; ignore it. */    return 1;}#undef NAME_IS/* Returns non-zero for characters that are legal in the name of an   attribute.  This used to allow only alphanumerics, '-', and '_',   but we need to be more lenient because a number of sites wants to   use weirder attribute names.  rfc2965 "informally specifies"   attribute name (token) as "a sequence of non-special, non-white   space characters".  So we allow everything except the stuff we know   could harm us.  */#define ATTR_NAME_CHAR(c) ((c) > 32 && (c) < 127	\			   && (c) != '"' && (c) != '='	\			   && (c) != ';' && (c) != ',')/* Parse the contents of the `Set-Cookie' header.  The header looks   like this:   name1=value1; name2=value2; ...   Trailing semicolon is optional; spaces are allowed between all   tokens.  Additionally, values may be quoted.   A new cookie is returned upon success, NULL otherwise.  The   specified CALLBACK function (normally `update_cookie_field' is used   to update the fields of the newly created cookie structure.  */static struct cookie *parse_set_cookies (const char *sc,		   int (*callback) (struct cookie *,				    const char *, const char *,				    const char *, const char *),		   int silent){  struct cookie *cookie = cookie_new ();  /* #### Hand-written DFAs are no fun to debug.  We'de be better off     to rewrite this as an inline parser.  */  enum { S_START, S_NAME, S_NAME_POST,	 S_VALUE_PRE, S_VALUE, S_QUOTED_VALUE, S_VALUE_TRAILSPACE,	 S_ATTR_ACTION, S_DONE, S_ERROR  } state = S_START;  const char *p = sc;  char c;  const char *name_b  = NULL, *name_e  = NULL;  const char *value_b = NULL, *value_e = NULL;  c = *p;  while (state != S_DONE && state != S_ERROR)    {      switch (state)	{	case S_START:	  if (!c)	    state = S_DONE;	  else if (ISSPACE (c))	    /* Strip all whitespace preceding the name. */	    c = *++p;	  else if (ATTR_NAME_CHAR (c))	    {	      name_b = p;	      state = S_NAME;	    }	  else	    /* empty attr name not allowed */	    state = S_ERROR;	  break;	case S_NAME:	  if (!c || c == ';' || c == '=' || ISSPACE (c))	    {	      name_e = p;	      state = S_NAME_POST;	    }	  else if (ATTR_NAME_CHAR (c))	    c = *++p;	  else	    state = S_ERROR;	  break;	case S_NAME_POST:	  if (!c || c == ';')	    {	      value_b = value_e = NULL;	      if (c == ';')		c = *++p;	      state = S_ATTR_ACTION;	    }	  else if (c == '=')

⌨️ 快捷键说明

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