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

📄 cookies.c

📁 Wget很好的处理了http和ftp的下载,很值得学习的经典代码
💻 C
📖 第 1 页 / 共 3 页
字号:
/* Support for cookies.   Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007,   2008 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 3 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, see <http://www.gnu.org/licenses/>.Additional permission under GNU GPL version 3 section 7If you modify this program, or any covered work, by linking orcombining it with the OpenSSL project's OpenSSL library (or amodified version of that library), containing parts covered by theterms of the OpenSSL or SSLeay licenses, the Free Software Foundationgrants you additional permission to convey the resulting work.Corresponding Source for a non-source form of such a combinationshall include the source code for the parts of OpenSSL used as wellas that of the covered work.  *//* Written by Hrvoje Niksic.  Parts are loosely inspired by the   cookie patch submitted by Tomasz Wegrzanowski.   This implements the client-side cookie support, as specified   (loosely) by Netscape's "preliminary specification", currently   available at:       http://wp.netscape.com/newsref/std/cookie_spec.html   rfc2109 is not supported because of its incompatibilities with the   above widely-used specification.  rfc2965 is entirely ignored,   since popular client software doesn't implement it, and even the   sites that do send Set-Cookie2 also emit Set-Cookie for   compatibility.  */#include <config.h>#include <stdio.h>#include <string.h>#include <stdlib.h>#include <assert.h>#include <errno.h>#include <time.h>#include "wget.h"#include "utils.h"#include "hash.h"#include "cookies.h"#include "http.h"               /* for http_atotm *//* 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.  */static time_t cookies_now;struct cookie_jar *cookie_jar_new (void){  struct cookie_jar *jar = xnew (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 */  unsigned discard_requested :1; /* whether cookie was created to                                   request discarding another                                   cookie. */  unsigned secure :1;           /* whether cookie should be                                   transmitted over non-https                                   connections. */  unsigned domain_exact :1;     /* whether DOMAIN must match as a                                   whole. */  unsigned permanent :1;        /* whether the cookie should outlive                                   the session. */  time_t expiry_time;           /* time when the cookie expires, 0                                   means undetermined. */  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)/* Allocate and return a new, empty cookie structure. */static struct cookie *cookie_new (void){  struct cookie *cookie = xnew0 (struct cookie);  /* Both cookie->permanent and cookie->expiry_time are now 0.  This     means that the cookie doesn't expire, but is only valid for this     session (i.e. not written out to disk).  */  cookie->port = PORT_ANY;  return cookie;}/* Non-zero if the cookie has expired.  Assumes cookies_now has been   set by one of the entry point functions.  */static boolcookie_expired_p (const struct cookie *c){  return c->expiry_time != 0 && c->expiry_time < cookies_now;}/* Deallocate COOKIE and its components. */static voiddelete_cookie (struct cookie *cookie){  xfree_null (cookie->domain);  xfree_null (cookie->path);  xfree_null (cookie->attr);  xfree_null (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;  IF_DEBUG    {      time_t exptime = cookie->expiry_time;      DEBUGP (("\nStored cookie %s %d%s %s <%s> <%s> [expiry %s] %s %s\n",               cookie->domain, cookie->port,               cookie->port == PORT_ANY ? " (ANY)" : "",               cookie->path,               cookie->permanent ? "permanent" : "session",               cookie->secure ? "secure" : "insecure",               cookie->expiry_time ? datetime_str (exptime) : "none",               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 TOKEN_IS(token, string_literal)                         \  BOUNDED_EQUAL_NO_CASE (token.b, token.e, string_literal)#define TOKEN_NON_EMPTY(token) (token.b != NULL && token.b != token.e)/* 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 first name-value pair will be used to set the cookie's   attribute name and value.  Subsequent parameters will be checked   against field names such as `domain', `path', etc.  Recognized   fields will be parsed and the corresponding members of COOKIE   filled.  */static struct cookie *parse_set_cookie (const char *set_cookie, bool silent){  const char *ptr = set_cookie;  struct cookie *cookie = cookie_new ();  param_token name, value;  if (!extract_param (&ptr, &name, &value, ';'))    goto error;  if (!value.b)    goto error;  cookie->attr = strdupdelim (name.b, name.e);  cookie->value = strdupdelim (value.b, value.e);  while (extract_param (&ptr, &name, &value, ';'))    {      if (TOKEN_IS (name, "domain"))        {          if (!TOKEN_NON_EMPTY (value))            goto error;          xfree_null (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 in browsers.  */          if (*value.b == '.')            ++value.b;          cookie->domain = strdupdelim (value.b, value.e);        }      else if (TOKEN_IS (name, "path"))        {          if (!TOKEN_NON_EMPTY (value))            goto error;          xfree_null (cookie->path);          cookie->path = strdupdelim (value.b, value.e);        }      else if (TOKEN_IS (name, "expires"))        {          char *value_copy;          time_t expires;          if (!TOKEN_NON_EMPTY (value))            goto error;          BOUNDED_TO_ALLOCA (value.b, value.e, value_copy);          expires = http_atotm (value_copy);          if (expires != (time_t) -1)            {              cookie->permanent = 1;              cookie->expiry_time = expires;              /* 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;            }          else            /* Error in expiration spec.  Assume default (cookie doesn't               expire, but valid only for this session.)  */            ;        }      else if (TOKEN_IS (name, "max-age"))        {          double maxage = -1;          char *value_copy;          if (!TOKEN_NON_EMPTY (value))            goto error;          BOUNDED_TO_ALLOCA (value.b, value.e, value_copy);          sscanf (value_copy, "%lf", &maxage);          if (maxage == -1)            /* something went wrong. */            goto error;          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;        }      else if (TOKEN_IS (name, "secure"))        {          /* ignore value completely */          cookie->secure = 1;        }      else        /* Ignore unrecognized attribute. */        ;    }  if (*ptr)    /* extract_param has encountered a syntax error */    goto error;  /* The cookie has been successfully constructed; return it. */  return cookie; error:  if (!silent)    logprintf (LOG_NOTQUIET,               _("Syntax error in Set-Cookie: %s at position %d.\n"),               escnonprint (set_cookie), (int) (ptr - set_cookie));  delete_cookie (cookie);  return NULL;}#undef TOKEN_IS#undef TOKEN_NON_EMPTY/* Sanity checks.  These are important, otherwise it is possible for   mailcious attackers to destroy important cookie information and/or   violate your privacy.  */#define REQUIRE_DIGITS(p) do {                  \  if (!ISDIGIT (*p))                            \    return false;                               \  for (++p; ISDIGIT (*p); p++)                  \    ;                                           \} while (0)#define REQUIRE_DOT(p) do {                     \  if (*p++ != '.')                              \    return false;                               \} while (0)

⌨️ 快捷键说明

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