📄 cookies.c
字号:
struct weighed_cookie *wc2 = (struct weighed_cookie *)p2; /* Subtractions take `wc2' as the first argument becauase we want a sort in *decreasing* order of goodness. */ int dgdiff = wc2->domain_goodness - wc1->domain_goodness; int pgdiff = wc2->path_goodness - wc1->path_goodness; /* Sort by domain goodness; if these are the same, sort by path goodness. (The sorting order isn't really specified; maybe it should be the other way around.) */ return dgdiff ? dgdiff : pgdiff;}/* Generate a `Cookie' header for a request that goes to HOST:PORT and requests PATH from the server. The resulting string is allocated with `malloc', and the caller is responsible for freeing it. If no cookies pertain to this request, i.e. no cookie header should be generated, NULL is returned. */char *cookie_header (struct cookie_jar *jar, const char *host, int port, const char *path, bool secflag){ struct cookie **chains; int chain_count; struct cookie *cookie; struct weighed_cookie *outgoing; int count, i, ocnt; char *result; int result_size, pos; PREPEND_SLASH (path); /* see cookie_handle_set_cookie */ /* First, find the cookie chains whose domains match HOST. */ /* Allocate room for find_chains_of_host to write to. The number of chains can at most equal the number of subdomains, hence 1+<number of dots>. */ chains = alloca_array (struct cookie *, 1 + count_char (host, '.')); chain_count = find_chains_of_host (jar, host, chains); /* No cookies for this host. */ if (!chain_count) return NULL; cookies_now = time (NULL); /* Now extract from the chains those cookies that match our host (for domain_exact cookies), port (for cookies with port other than PORT_ANY), etc. See matching_cookie for details. */ /* Count the number of matching cookies. */ count = 0; for (i = 0; i < chain_count; i++) for (cookie = chains[i]; cookie; cookie = cookie->next) if (cookie_matches_url (cookie, host, port, path, secflag, NULL)) ++count; if (!count) return NULL; /* no cookies matched */ /* Allocate the array. */ outgoing = alloca_array (struct weighed_cookie, count); /* Fill the array with all the matching cookies from the chains that match HOST. */ ocnt = 0; for (i = 0; i < chain_count; i++) for (cookie = chains[i]; cookie; cookie = cookie->next) { int pg; if (!cookie_matches_url (cookie, host, port, path, secflag, &pg)) continue; outgoing[ocnt].cookie = cookie; outgoing[ocnt].domain_goodness = strlen (cookie->domain); outgoing[ocnt].path_goodness = pg; ++ocnt; } assert (ocnt == count); /* Eliminate duplicate cookies; that is, those whose name and value are the same. */ count = eliminate_dups (outgoing, count); /* Sort the array so that best-matching domains come first, and that, within one domain, best-matching paths come first. */ qsort (outgoing, count, sizeof (struct weighed_cookie), goodness_comparator); /* Count the space the name=value pairs will take. */ result_size = 0; for (i = 0; i < count; i++) { struct cookie *c = outgoing[i].cookie; /* name=value */ result_size += strlen (c->attr) + 1 + strlen (c->value); } /* Allocate output buffer: name=value pairs -- result_size "; " separators -- (count - 1) * 2 \0 terminator -- 1 */ result_size = result_size + (count - 1) * 2 + 1; result = xmalloc (result_size); pos = 0; for (i = 0; i < count; i++) { struct cookie *c = outgoing[i].cookie; int namlen = strlen (c->attr); int vallen = strlen (c->value); memcpy (result + pos, c->attr, namlen); pos += namlen; result[pos++] = '='; memcpy (result + pos, c->value, vallen); pos += vallen; if (i < count - 1) { result[pos++] = ';'; result[pos++] = ' '; } } result[pos++] = '\0'; assert (pos == result_size); return result;}/* Support for loading and saving cookies. The format used for loading and saving should be the format of the `cookies.txt' file used by Netscape and Mozilla, at least the Unix versions. (Apparently IE can export cookies in that format as well.) The format goes like this: DOMAIN DOMAIN-FLAG PATH SECURE-FLAG TIMESTAMP ATTR-NAME ATTR-VALUE DOMAIN -- cookie domain, optionally followed by :PORT DOMAIN-FLAG -- whether all hosts in the domain match PATH -- cookie path SECURE-FLAG -- whether cookie requires secure connection TIMESTAMP -- expiry timestamp, number of seconds since epoch ATTR-NAME -- name of the cookie attribute ATTR-VALUE -- value of the cookie attribute (empty if absent) The fields are separated by TABs. All fields are mandatory, except for ATTR-VALUE. The `-FLAG' fields are boolean, their legal values being "TRUE" and "FALSE'. Empty lines, lines consisting of whitespace only, and comment lines (beginning with # optionally preceded by whitespace) are ignored. Example line from cookies.txt (split in two lines for readability): .google.com TRUE / FALSE 2147368447 \ PREF ID=34bb47565bbcd47b:LD=en:NR=20:TM=985172580:LM=985739012*//* If the region [B, E) ends with :<digits>, parse the number, return it, and store new boundary (location of the `:') to DOMAIN_E_PTR. If port is not specified, return 0. */static intdomain_port (const char *domain_b, const char *domain_e, const char **domain_e_ptr){ int port = 0; const char *p; const char *colon = memchr (domain_b, ':', domain_e - domain_b); if (!colon) return 0; for (p = colon + 1; p < domain_e && ISDIGIT (*p); p++) port = 10 * port + (*p - '0'); if (p < domain_e) /* Garbage following port number. */ return 0; *domain_e_ptr = colon; return port;}#define GET_WORD(p, b, e) do { \ b = p; \ while (*p && *p != '\t') \ ++p; \ e = p; \ if (b == e || !*p) \ goto next; \ ++p; \} while (0)/* Load cookies from FILE. */voidcookie_jar_load (struct cookie_jar *jar, const char *file){ char *line; FILE *fp = fopen (file, "r"); if (!fp) { logprintf (LOG_NOTQUIET, _("Cannot open cookies file `%s': %s\n"), file, strerror (errno)); return; } cookies_now = time (NULL); for (; ((line = read_whole_line (fp)) != NULL); xfree (line)) { struct cookie *cookie; char *p = line; double expiry; int port; char *domain_b = NULL, *domain_e = NULL; char *domflag_b = NULL, *domflag_e = NULL; char *path_b = NULL, *path_e = NULL; char *secure_b = NULL, *secure_e = NULL; char *expires_b = NULL, *expires_e = NULL; char *name_b = NULL, *name_e = NULL; char *value_b = NULL, *value_e = NULL; /* Skip leading white-space. */ while (*p && ISSPACE (*p)) ++p; /* Ignore empty lines. */ if (!*p || *p == '#') continue; GET_WORD (p, domain_b, domain_e); GET_WORD (p, domflag_b, domflag_e); GET_WORD (p, path_b, path_e); GET_WORD (p, secure_b, secure_e); GET_WORD (p, expires_b, expires_e); GET_WORD (p, name_b, name_e); /* Don't use GET_WORD for value because it ends with newline, not TAB. */ value_b = p; value_e = p + strlen (p); if (value_e > value_b && value_e[-1] == '\n') --value_e; if (value_e > value_b && value_e[-1] == '\r') --value_e; /* Empty values are legal (I think), so don't bother checking. */ cookie = cookie_new (); cookie->attr = strdupdelim (name_b, name_e); cookie->value = strdupdelim (value_b, value_e); cookie->path = strdupdelim (path_b, path_e); cookie->secure = BOUNDED_EQUAL (secure_b, secure_e, "TRUE"); /* Curl source says, quoting Andre Garcia: "flag: A TRUE/FALSE value indicating if all machines within a given domain can access the variable. This value is set automatically by the browser, depending on the value set for the domain." */ cookie->domain_exact = !BOUNDED_EQUAL (domflag_b, domflag_e, "TRUE"); /* DOMAIN needs special treatment because we might need to extract the port. */ port = domain_port (domain_b, domain_e, (const char **)&domain_e); if (port) cookie->port = port; if (*domain_b == '.') ++domain_b; /* remove leading dot internally */ cookie->domain = strdupdelim (domain_b, domain_e); /* safe default in case EXPIRES field is garbled. */ expiry = (double)cookies_now - 1; /* I don't like changing the line, but it's safe here. (line is malloced.) */ *expires_e = '\0'; sscanf (expires_b, "%lf", &expiry); if (expiry == 0) { /* EXPIRY can be 0 for session cookies saved because the user specified `--keep-session-cookies' in the past. They remain session cookies, and will be saved only if the user has specified `keep-session-cookies' again. */ } else { if (expiry < cookies_now) goto abort_cookie; /* ignore stale cookie. */ cookie->expiry_time = expiry; cookie->permanent = 1; } store_cookie (jar, cookie); next: continue; abort_cookie: delete_cookie (cookie); } fclose (fp);}/* Save cookies, in format described above, to FILE. */voidcookie_jar_save (struct cookie_jar *jar, const char *file){ FILE *fp; hash_table_iterator iter; DEBUGP (("Saving cookies to %s.\n", file)); cookies_now = time (NULL); fp = fopen (file, "w"); if (!fp) { logprintf (LOG_NOTQUIET, _("Cannot open cookies file `%s': %s\n"), file, strerror (errno)); return; } fputs ("# HTTP cookie file.\n", fp); fprintf (fp, "# Generated by Wget on %s.\n", datetime_str (cookies_now)); fputs ("# Edit at your own risk.\n\n", fp); for (hash_table_iterate (jar->chains, &iter); hash_table_iter_next (&iter); ) { const char *domain = iter.key; struct cookie *cookie = iter.value; for (; cookie; cookie = cookie->next) { if (!cookie->permanent && !opt.keep_session_cookies) continue; if (cookie_expired_p (cookie)) continue; if (!cookie->domain_exact) fputc ('.', fp); fputs (domain, fp); if (cookie->port != PORT_ANY) fprintf (fp, ":%d", cookie->port); fprintf (fp, "\t%s\t%s\t%s\t%.0f\t%s\t%s\n", cookie->domain_exact ? "FALSE" : "TRUE", cookie->path, cookie->secure ? "TRUE" : "FALSE", (double)cookie->expiry_time, cookie->attr, cookie->value); if (ferror (fp)) goto out; } } out: if (ferror (fp)) logprintf (LOG_NOTQUIET, _("Error writing to `%s': %s\n"), file, strerror (errno)); if (fclose (fp) < 0) logprintf (LOG_NOTQUIET, _("Error closing `%s': %s\n"), file, strerror (errno)); DEBUGP (("Done saving cookies.\n"));}/* Clean up cookie-related data. */voidcookie_jar_delete (struct cookie_jar *jar){ /* Iterate over chains (indexed by domain) and free them. */ hash_table_iterator iter; for (hash_table_iterate (jar->chains, &iter); hash_table_iter_next (&iter); ) { struct cookie *chain = iter.value; xfree (iter.key); /* Then all cookies in this chain. */ while (chain) { struct cookie *next = chain->next; delete_cookie (chain); chain = next; } } hash_table_destroy (jar->chains); xfree (jar);}/* Test cases. Currently this is only tests parse_set_cookies. To use, recompile Wget with -DTEST_COOKIES and call test_cookies() from main. */#ifdef TEST_COOKIESvoidtest_cookies (void){ /* Tests expected to succeed: */ static struct { const char *data; const char *results[10]; } tests_succ[] = { { "arg=value", {"arg", "value", NULL} }, { "arg1=value1;arg2=value2", {"arg1", "value1", "arg2", "value2", NULL} }, { "arg1=value1; arg2=value2", {"arg1", "value1", "arg2", "value2", NULL} }, { "arg1=value1; arg2=value2;", {"arg1", "value1", "arg2", "value2", NULL} }, { "arg1=value1; arg2=value2; ", {"arg1", "value1", "arg2", "value2", NULL} }, { "arg1=\"value1\"; arg2=\"\"", {"arg1", "value1", "arg2", "", NULL} }, { "arg=", {"arg", "", NULL} }, { "arg1=; arg2=", {"arg1", "", "arg2", "", NULL} }, { "arg1 = ; arg2= ", {"arg1", "", "arg2", "", NULL} }, }; /* Tests expected to fail: */ static char *tests_fail[] = { ";", "arg=\"unterminated", "=empty-name", "arg1=;=another-empty-name", }; int i; for (i = 0; i < countof (tests_succ); i++) { int ind; const char *data = tests_succ[i].data; const char **expected = tests_succ[i].results; struct cookie *c; c = parse_set_cookie (data, true); if (!c) { printf ("NULL cookie returned for valid data: %s\n", data); continue; } /* Test whether extract_param handles these cases correctly. */ { param_token name, value; const char *ptr = data; int j = 0; while (extract_param (&ptr, &name, &value, ';')) { char *n = strdupdelim (name.b, name.e); char *v = strdupdelim (value.b, value.e); if (!expected[j]) { printf ("Too many parameters for '%s'\n", data); break; } if (0 != strcmp (expected[j], n)) printf ("Invalid name %d for '%s' (expected '%s', got '%s')\n", j / 2 + 1, data, expected[j], n); if (0 != strcmp (expected[j + 1], v)) printf ("Invalid value %d for '%s' (expected '%s', got '%s')\n", j / 2 + 1, data, expected[j + 1], v); j += 2; free (n); free (v); } if (expected[j]) printf ("Too few parameters for '%s'\n", data); } } for (i = 0; i < countof (tests_fail); i++) { struct cookie *c; char *data = tests_fail[i]; c = parse_set_cookie (data, true); if (c) printf ("Failed to report error on invalid data: %s\n", data); }}#endif /* TEST_COOKIES */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -