X-Git-Url: http://sjero.net/git/?a=blobdiff_plain;f=src%2Fhttp.c;h=faeb0e7fd345cbe94826445e27f9fe18bff819f4;hb=4d7c5e087b2bc82c9f503dff003916d1047903ce;hp=72a5c12e7f4b1f71afff1b73ab51a519172d2e09;hpb=66ee8957346f5a8c44c9bbc6b55381a7099c5496;p=wget diff --git a/src/http.c b/src/http.c index 72a5c12e..faeb0e7f 100644 --- a/src/http.c +++ b/src/http.c @@ -1,11 +1,11 @@ /* HTTP support. - Copyright (C) 1996-2005 Free Software Foundation, Inc. + Copyright (C) 1996-2006 Free Software Foundation, Inc. This file is part of GNU Wget. GNU Wget is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by -the Free Software Foundation; either version 2 of the License, or +the Free Software Foundation; either version 3 of the License, or (at your option) any later version. GNU Wget is distributed in the hope that it will be useful, @@ -14,8 +14,7 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with Wget; if not, write to the Free Software Foundation, Inc., -51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +along with Wget. If not, see . In addition, as a special exception, the Free Software Foundation gives permission to link the code of its release of Wget with the @@ -59,6 +58,7 @@ so, delete this exception statement from your version. */ # include "gen-md5.h" #endif #include "convert.h" +#include "spider.h" #ifdef TESTING #include "test.h" @@ -737,6 +737,20 @@ resp_free (struct response *resp) xfree (resp); } +/* Print a single line of response, the characters [b, e). We tried + getting away with + logprintf (LOG_VERBOSE, "%s%.*s\n", prefix, (int) (e - b), b); + but that failed to escape the non-printable characters and, in fact, + caused crashes in UTF-8 locales. */ + +static void +print_response_line(const char *prefix, const char *b, const char *e) +{ + char *copy; + BOUNDED_TO_ALLOCA(b, e, copy); + logprintf (LOG_VERBOSE, "%s%s\n", prefix, escnonprint(copy)); +} + /* Print the server response, line by line, omitting the trailing CRLF from individual header lines, and prefixed with PREFIX. */ @@ -755,9 +769,7 @@ print_server_response (const struct response *resp, const char *prefix) --e; if (b < e && e[-1] == '\r') --e; - /* This is safe even on printfs with broken handling of "%.s" - because resp->headers ends with \0. */ - logprintf (LOG_VERBOSE, "%s%.*s\n", prefix, e - b, b); + print_response_line(prefix, b, e); } } @@ -855,16 +867,11 @@ skip_short_body (int fd, wgint contlen) return true; } -typedef struct { - /* A token consists of characters in the [b, e) range. */ - const char *b, *e; -} param_token; - -/* Extract a parameter from the HTTP header at *SOURCE and advance - *SOURCE to the next parameter. Return false when there are no more - parameters to extract. The name of the parameter is returned in - NAME, and the value in VALUE. If the parameter has no value, the - token's value is zeroed out. +/* Extract a parameter from the string (typically an HTTP header) at + **SOURCE and advance SOURCE to the next parameter. Return false + when there are no more parameters to extract. The name of the + parameter is returned in NAME, and the value in VALUE. If the + parameter has no value, the token's value is zeroed out. For example, if *SOURCE points to the string "attachment; filename=\"foo bar\"", the first call to this function will return @@ -872,34 +879,40 @@ typedef struct { return the token named "filename" and value "foo bar". The third call will return false, indicating no more valid tokens. */ -static bool -extract_param (const char **source, param_token *name, param_token *value) +bool +extract_param (const char **source, param_token *name, param_token *value, + char separator) { const char *p = *source; while (ISSPACE (*p)) ++p; if (!*p) - return false; /* nothing more to extract */ + { + *source = p; + return false; /* no error; nothing more to extract */ + } /* Extract name. */ name->b = p; - while (*p && !ISSPACE (*p) && *p != '=' && *p != ';') ++p; + while (*p && !ISSPACE (*p) && *p != '=' && *p != separator) ++p; name->e = p; + if (name->b == name->e) + return false; /* empty name: error */ while (ISSPACE (*p)) ++p; - if (*p == ';' || !*p) /* no value */ + if (*p == separator || !*p) /* no value */ { xzero (*value); - if (*p == ';') ++p; + if (*p == separator) ++p; *source = p; return true; } if (*p != '=') - return false; /* error */ + return false; /* error */ /* *p is '=', extract value */ ++p; while (ISSPACE (*p)) ++p; - if (*p == '"') /* quoted */ + if (*p == '"') /* quoted */ { value->b = ++p; while (*p && *p != '"') ++p; @@ -908,21 +921,21 @@ extract_param (const char **source, param_token *name, param_token *value) value->e = p++; /* Currently at closing quote; find the end of param. */ while (ISSPACE (*p)) ++p; - while (*p && *p != ';') ++p; - if (*p == ';') - ++p; + while (*p && *p != separator) ++p; + if (*p == separator) + ++p; else if (*p) - /* garbage after closed quote, e.g. foo="bar"baz */ - return false; + /* garbage after closed quote, e.g. foo="bar"baz */ + return false; } - else /* unquoted */ + else /* unquoted */ { value->b = p; - while (*p && *p != ';') ++p; + while (*p && *p != separator) ++p; value->e = p; while (value->e != value->b && ISSPACE (value->e[-1])) --value->e; - if (*p == ';') ++p; + if (*p == separator) ++p; } *source = p; return true; @@ -931,23 +944,58 @@ extract_param (const char **source, param_token *name, param_token *value) #undef MAX #define MAX(p, q) ((p) > (q) ? (p) : (q)) +/* Parse the contents of the `Content-Disposition' header, extracting + the information useful to Wget. Content-Disposition is a header + borrowed from MIME; when used in HTTP, it typically serves for + specifying the desired file name of the resource. For example: + + Content-Disposition: attachment; filename="flora.jpg" + + Wget will skip the tokens it doesn't care about, such as + "attachment" in the previous example; it will also skip other + unrecognized params. If the header is syntactically correct and + contains a file name, a copy of the file name is stored in + *filename and true is returned. Otherwise, the function returns + false. + + The file name is stripped of directory components and must not be + empty. */ + static bool parse_content_disposition (const char *hdr, char **filename) { param_token name, value; - while (extract_param (&hdr, &name, &value)) + while (extract_param (&hdr, &name, &value, ';')) if (BOUNDED_EQUAL_NO_CASE (name.b, name.e, "filename") && value.b != NULL) { - /* Make the file name begin at the last slash or backslash. */ + /* Make the file name begin at the last slash or backslash. */ const char *last_slash = memrchr (value.b, '/', value.e - value.b); const char *last_bs = memrchr (value.b, '\\', value.e - value.b); if (last_slash && last_bs) value.b = 1 + MAX (last_slash, last_bs); else if (last_slash || last_bs) value.b = 1 + (last_slash ? last_slash : last_bs); - if (value.b == value.e) - continue; - *filename = strdupdelim (value.b, value.e); + if (value.b == value.e) + continue; + /* Start with the directory prefix, if specified. */ + if (opt.dir_prefix) + { + int prefix_length = strlen (opt.dir_prefix); + bool add_slash = (opt.dir_prefix[prefix_length - 1] != '/'); + int total_length; + + if (add_slash) + ++prefix_length; + total_length = prefix_length + (value.e - value.b); + *filename = xmalloc (total_length + 1); + strcpy (*filename, opt.dir_prefix); + if (add_slash) + (*filename)[prefix_length - 1] = '/'; + memcpy (*filename + prefix_length, value.b, (value.e - value.b)); + (*filename)[total_length] = '\0'; + } + else + *filename = strdupdelim (value.b, value.e); return true; } return false; @@ -1708,33 +1756,46 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy) /* Determine the local filename if needed. Notice that if -O is used * hstat.local_file is set by http_loop to the argument of -O. */ - if (!hs->local_file) + if (!hs->local_file) { /* Honor Content-Disposition whether possible. */ - if (!resp_header_copy (resp, "Content-Disposition", hdrval, sizeof (hdrval)) + if (!opt.content_disposition + || !resp_header_copy (resp, "Content-Disposition", + hdrval, sizeof (hdrval)) || !parse_content_disposition (hdrval, &hs->local_file)) { - /* Choose filename according to URL name. */ + /* The Content-Disposition header is missing or broken. + * Choose unique file name according to given URL. */ hs->local_file = url_file_name (u); } } /* TODO: perform this check only once. */ - if (opt.noclobber && file_exists_p (hs->local_file)) + if (file_exists_p (hs->local_file)) { - /* If opt.noclobber is turned on and file already exists, do not - retrieve the file */ - logprintf (LOG_VERBOSE, _("\ + if (opt.noclobber) + { + /* If opt.noclobber is turned on and file already exists, do not + retrieve the file */ + logprintf (LOG_VERBOSE, _("\ File `%s' already there; not retrieving.\n\n"), hs->local_file); - /* If the file is there, we suppose it's retrieved OK. */ - *dt |= RETROKF; + /* If the file is there, we suppose it's retrieved OK. */ + *dt |= RETROKF; - /* #### Bogusness alert. */ - /* If its suffix is "html" or "htm" or similar, assume text/html. */ - if (has_html_suffix_p (hs->local_file)) - *dt |= TEXTHTML; + /* #### Bogusness alert. */ + /* If its suffix is "html" or "htm" or similar, assume text/html. */ + if (has_html_suffix_p (hs->local_file)) + *dt |= TEXTHTML; - return RETROK; + return RETROK; + } + else if (!ALLOW_CLOBBER) + { + char *unique = unique_name (hs->local_file, true); + if (unique != hs->local_file) + xfree (hs->local_file); + hs->local_file = unique; + } } /* Support timestamping */ @@ -1772,7 +1833,7 @@ File `%s' already there; not retrieving.\n\n"), hs->local_file); /* Try to stat() the .orig file. */ if (stat (filename_plus_orig_suffix, &st) == 0) { - local_dot_orig_file_exists = 1; + local_dot_orig_file_exists = true; local_filename = filename_plus_orig_suffix; } } @@ -1980,7 +2041,7 @@ File `%s' already there; not retrieving.\n\n"), hs->local_file); content-type. */ if (!type || 0 == strncasecmp (type, TEXTHTML_S, strlen (TEXTHTML_S)) || - 0 == strncasecmp (type, TEXTXHTML_S, strlen (TEXTXHTML_S))) + 0 == strncasecmp (type, TEXTXHTML_S, strlen (TEXTXHTML_S))) *dt |= TEXTHTML; else *dt &= ~TEXTHTML; @@ -2103,13 +2164,6 @@ File `%s' already there; not retrieving.\n\n"), hs->local_file); return RETRFINISHED; } - /* Print fetch message, if opt.verbose. */ - if (opt.verbose) - { - logprintf (LOG_NOTQUIET, _("Saving to: `%s'\n"), - HYPHENP (hs->local_file) ? "STDOUT" : hs->local_file); - } - /* Open the local file. */ if (!output_stream) { @@ -2146,6 +2200,13 @@ File `%s' already there; not retrieving.\n\n"), hs->local_file); else fp = output_stream; + /* Print fetch message, if opt.verbose. */ + if (opt.verbose) + { + logprintf (LOG_NOTQUIET, _("Saving to: `%s'\n"), + HYPHENP (hs->local_file) ? "STDOUT" : hs->local_file); + } + /* This confuses the timestamping code that checks for file size. #### The timestamping code should be smarter about file size. */ if (opt.save_headers && hs->restval == 0) @@ -2194,14 +2255,15 @@ http_loop (struct url *u, char **newloc, char **local_file, const char *referer, int *dt, struct url *proxy) { int count; - bool got_head = false; /* used for time-stamping */ + bool got_head = false; /* used for time-stamping and filename detection */ + bool got_name = false; char *tms; const char *tmrate; - uerr_t err; + uerr_t err, ret = TRYLIMEXC; time_t tmr = -1; /* remote time-stamp */ wgint local_size = 0; /* the size of the local file */ struct http_stat hstat; /* HTTP status */ - struct_stat st; + struct_stat st; /* Assert that no value for *LOCAL_FILE was passed. */ assert (local_file == NULL || *local_file == NULL); @@ -2228,7 +2290,10 @@ http_loop (struct url *u, char **newloc, char **local_file, const char *referer, hstat.referer = referer; if (opt.output_document) - hstat.local_file = xstrdup (opt.output_document); + { + hstat.local_file = xstrdup (opt.output_document); + got_name = true; + } /* Reset the counter. */ count = 0; @@ -2244,8 +2309,12 @@ http_loop (struct url *u, char **newloc, char **local_file, const char *referer, sleep_between_retrievals (count); /* Get the current time string. */ - tms = time_str (NULL); + tms = time_str (time (NULL)); + if (opt.spider && !got_head) + logprintf (LOG_VERBOSE, _("\ +Spider mode enabled. Check if remote file exists.\n")); + /* Print fetch message, if opt.verbose. */ if (opt.verbose) { @@ -2273,13 +2342,14 @@ http_loop (struct url *u, char **newloc, char **local_file, const char *referer, /* Default document type is empty. However, if spider mode is on or time-stamping is employed, HEAD_ONLY commands is encoded within *dt. */ - if (opt.spider || (opt.timestamping && !got_head)) + if (((opt.spider || opt.timestamping) && !got_head) || !got_name) *dt |= HEAD_ONLY; else *dt &= ~HEAD_ONLY; /* Decide whether or not to restart. */ if (opt.always_rest + && got_name && stat (hstat.local_file, &st) == 0 && S_ISREG (st.st_mode)) /* When -c is used, continue from on-disk size. (Can't use @@ -2299,8 +2369,7 @@ http_loop (struct url *u, char **newloc, char **local_file, const char *referer, we require a fresh get. b) caching is explicitly inhibited. */ if ((proxy && count > 1) /* a */ - || !opt.allow_cache /* b */ - ) + || !opt.allow_cache) /* b */ *dt |= SEND_NOCACHE; else *dt &= ~SEND_NOCACHE; @@ -2309,7 +2378,7 @@ http_loop (struct url *u, char **newloc, char **local_file, const char *referer, err = gethttp (u, &hstat, dt, proxy); /* Time? */ - tms = time_str (NULL); + tms = time_str (time (NULL)); /* Get the new location (with or without the redirection). */ if (hstat.newloc) @@ -2323,26 +2392,23 @@ http_loop (struct url *u, char **newloc, char **local_file, const char *referer, /* Non-fatal errors continue executing the loop, which will bring them to "while" statement at the end, to judge whether the number of tries was exceeded. */ - /* free_hstat (&hstat); */ printwhat (count, opt.ntry); continue; - case HOSTERR: case CONIMPOSSIBLE: case PROXERR: case AUTHFAILED: - case SSLINITFAILED: case CONTNOTSUPPORTED: - /* Fatal errors just return from the function. */ - free_hstat (&hstat); - return err; case FWRITEERR: case FOPENERR: /* Another fatal error. */ logputs (LOG_VERBOSE, "\n"); logprintf (LOG_NOTQUIET, _("Cannot write to `%s' (%s).\n"), hstat.local_file, strerror (errno)); - free_hstat (&hstat); - return err; + case HOSTERR: case CONIMPOSSIBLE: case PROXERR: case AUTHFAILED: + case SSLINITFAILED: case CONTNOTSUPPORTED: + /* Fatal errors just return from the function. */ + ret = err; + goto exit; case CONSSLERR: /* Another fatal error. */ logprintf (LOG_NOTQUIET, _("Unable to establish SSL connection.\n")); - free_hstat (&hstat); - return err; + ret = err; + goto exit; case NEWLOCATION: /* Return the new location to the caller. */ if (!*newloc) @@ -2350,15 +2416,17 @@ http_loop (struct url *u, char **newloc, char **local_file, const char *referer, logprintf (LOG_NOTQUIET, _("ERROR: Redirection (%d) without location.\n"), hstat.statcode); - free_hstat (&hstat); - return WRONGCODE; + ret = WRONGCODE; } - free_hstat (&hstat); - return NEWLOCATION; + else + { + ret = NEWLOCATION; + } + goto exit; case RETRUNNEEDED: /* The file was already fully retrieved. */ - free_hstat (&hstat); - return RETROK; + ret = RETROK; + goto exit; case RETRFINISHED: /* Deal with you later. */ break; @@ -2366,26 +2434,43 @@ http_loop (struct url *u, char **newloc, char **local_file, const char *referer, /* All possibilities should have been exhausted. */ abort (); } - + if (!(*dt & RETROKF)) { + char *hurl = NULL; if (!opt.verbose) { /* #### Ugly ugly ugly! */ - char *hurl = url_string (u, true); + hurl = url_string (u, true); logprintf (LOG_NONVERBOSE, "%s:\n", hurl); - xfree (hurl); } - logprintf (LOG_NOTQUIET, _("%s ERROR %d: %s.\n"), - tms, hstat.statcode, escnonprint (hstat.error)); + /* Maybe we should always keep track of broken links, not just in + * spider mode. */ + if (opt.spider) + { + /* #### Again: ugly ugly ugly! */ + if (!hurl) + hurl = url_string (u, true); + nonexisting_url (hurl); + logprintf (LOG_NOTQUIET, _("\ +Remote file does not exist -- broken link!!!\n")); + } + else + { + logprintf (LOG_NOTQUIET, _("%s ERROR %d: %s.\n"), + tms, hstat.statcode, escnonprint (hstat.error)); + } logputs (LOG_VERBOSE, "\n"); - free_hstat (&hstat); - return WRONGCODE; + ret = WRONGCODE; + xfree_null (hurl); + goto exit; } /* Did we get the time-stamp? */ if (!got_head) { + bool restart_loop = false; + if (opt.timestamping && !hstat.remote_time) { logputs (LOG_NOTQUIET, _("\ @@ -2399,53 +2484,95 @@ Last-modified header missing -- time-stamps turned off.\n")); logputs (LOG_VERBOSE, _("\ Last-modified header invalid -- time-stamp ignored.\n")); } - } - - /* The time-stamping section. */ - if (opt.timestamping && !got_head) - { - got_head = true; /* no more time-stamping */ - *dt &= ~HEAD_ONLY; - count = 0; /* the retrieve count for HEAD is reset */ - - if (hstat.remote_time && tmr != (time_t) (-1)) + + /* The time-stamping section. */ + if (opt.timestamping) { - /* Now time-stamping can be used validly. Time-stamping - means that if the sizes of the local and remote file - match, and local file is newer than the remote file, - it will not be retrieved. Otherwise, the normal - download procedure is resumed. */ - if (hstat.orig_file_tstamp >= tmr) + if (hstat.orig_file_name) /* Perform the following checks only + if the file we're supposed to + download already exists. */ { - if (hstat.contlen == -1 || hstat.orig_file_size == hstat.contlen) + if (hstat.remote_time && + tmr != (time_t) (-1)) { - logprintf (LOG_VERBOSE, _("\ + /* Now time-stamping can be used validly. Time-stamping + means that if the sizes of the local and remote file + match, and local file is newer than the remote file, + it will not be retrieved. Otherwise, the normal + download procedure is resumed. */ + if (hstat.orig_file_tstamp >= tmr) + { + if (hstat.contlen == -1 + || hstat.orig_file_size == hstat.contlen) + { + logprintf (LOG_VERBOSE, _("\ Server file no newer than local file `%s' -- not retrieving.\n\n"), - hstat.orig_file_name); - free_hstat (&hstat); - return RETROK; + hstat.orig_file_name); + ret = RETROK; + goto exit; + } + else + { + logprintf (LOG_VERBOSE, _("\ +The sizes do not match (local %s) -- retrieving.\n"), + number_to_static_string (local_size)); + } + } + else + logputs (LOG_VERBOSE, + _("Remote file is newer, retrieving.\n")); + + logputs (LOG_VERBOSE, "\n"); } - else + } + + /* free_hstat (&hstat); */ + hstat.timestamp_checked = true; + restart_loop = true; + } + + if (opt.always_rest) + { + got_name = true; + restart_loop = true; + } + + if (opt.spider) + { + if (opt.recursive) + { + if (*dt & TEXTHTML) + { + logputs (LOG_VERBOSE, _("\ +Remote file exists and could contain links to other resources -- retrieving.\n\n")); + restart_loop = true; + } + else { logprintf (LOG_VERBOSE, _("\ -The sizes do not match (local %s) -- retrieving.\n"), - number_to_static_string (local_size)); +Remote file exists but does not contain any link -- not retrieving.\n\n")); + ret = RETRUNNEEDED; + goto exit; } } else - logputs (LOG_VERBOSE, - _("Remote file is newer, retrieving.\n")); - - logputs (LOG_VERBOSE, "\n"); + { + logprintf (LOG_VERBOSE, _("\ +Remote file exists but recursion is disabled -- not retrieving.\n\n")); + ret = RETRUNNEEDED; + goto exit; + } } - - /* free_hstat (&hstat); */ - hstat.timestamp_checked = true; - continue; + + got_head = true; /* no more time-stamping */ + *dt &= ~HEAD_ONLY; + count = 0; /* the retrieve count for HEAD is reset */ + + if (restart_loop) + continue; } - + if ((tmr != (time_t) (-1)) - && !opt.spider && ((hstat.len == hstat.contlen) || ((hstat.res == 0) && (hstat.contlen == -1)))) { @@ -2464,13 +2591,6 @@ The sizes do not match (local %s) -- retrieving.\n"), } /* End of time-stamping section. */ - if (opt.spider) - { - logprintf (LOG_NOTQUIET, "%d %s\n\n", hstat.statcode, - escnonprint (hstat.error)); - return RETROK; - } - tmrate = retr_rate (hstat.rd_size, hstat.dltime); total_download_time += hstat.dltime; @@ -2499,8 +2619,8 @@ The sizes do not match (local %s) -- retrieving.\n"), else downloaded_file(FILE_DOWNLOADED_NORMALLY, hstat.local_file); - free_hstat (&hstat); - return RETROK; + ret = RETROK; + goto exit; } else if (hstat.res == 0) /* No read error */ { @@ -2527,8 +2647,8 @@ The sizes do not match (local %s) -- retrieving.\n"), else downloaded_file(FILE_DOWNLOADED_NORMALLY, hstat.local_file); - free_hstat (&hstat); - return RETROK; + ret = RETROK; + goto exit; } else if (hstat.len < hstat.contlen) /* meaning we lost the connection too soon */ @@ -2537,7 +2657,6 @@ The sizes do not match (local %s) -- retrieving.\n"), _("%s (%s) - Connection closed at byte %s. "), tms, tmrate, number_to_static_string (hstat.len)); printwhat (count, opt.ntry); - /* free_hstat (&hstat); */ continue; } else @@ -2554,7 +2673,6 @@ The sizes do not match (local %s) -- retrieving.\n"), tms, tmrate, number_to_static_string (hstat.len), hstat.rderrmsg); printwhat (count, opt.ntry); - /* free_hstat (&hstat); */ continue; } else /* hstat.res == -1 and contlen is given */ @@ -2566,15 +2684,19 @@ The sizes do not match (local %s) -- retrieving.\n"), number_to_static_string (hstat.contlen), hstat.rderrmsg); printwhat (count, opt.ntry); - /* free_hstat (&hstat); */ continue; } } /* not reached */ } while (!opt.ntry || (count < opt.ntry)); + +exit: + if (ret == RETROK) + *local_file = xstrdup (hstat.local_file); + free_hstat (&hstat); - return TRYLIMEXC; + return ret; } /* Check whether the result of strptime() indicates success. @@ -2715,45 +2837,6 @@ basic_authentication_encode (const char *user, const char *passwd) } while (0) #ifdef ENABLE_DIGEST -/* Parse HTTP `WWW-Authenticate:' header. AU points to the beginning - of a field in such a header. If the field is the one specified by - ATTR_NAME ("realm", "opaque", and "nonce" are used by the current - digest authorization code), extract its value in the (char*) - variable pointed by RET. Returns negative on a malformed header, - or number of bytes that have been parsed by this call. */ -static int -extract_header_attr (const char *au, const char *attr_name, char **ret) -{ - const char *ep; - const char *cp = au; - - if (strncmp (cp, attr_name, strlen (attr_name)) == 0) - { - cp += strlen (attr_name); - if (!*cp) - return -1; - SKIP_WS (cp); - if (*cp != '=') - return -1; - if (!*++cp) - return -1; - SKIP_WS (cp); - if (*cp != '\"') - return -1; - if (!*++cp) - return -1; - for (ep = cp; *ep && *ep != '\"'; ep++) - ; - if (!*ep) - return -1; - xfree_null (*ret); - *ret = strdupdelim (cp, ep); - return ep - au + 1; - } - else - return 0; -} - /* Dump the hexadecimal representation of HASH to BUF. HASH should be an array of 16 bytes containing the hash keys, and BUF should be a buffer of 33 writable characters (32 for hex digits plus one for @@ -2788,53 +2871,21 @@ digest_authentication_encode (const char *au, const char *user, { "nonce", &nonce } }; char *res; + param_token name, value; realm = opaque = nonce = NULL; au += 6; /* skip over `Digest' */ - while (*au) + while (extract_param (&au, &name, &value, ',')) { int i; - - SKIP_WS (au); for (i = 0; i < countof (options); i++) - { - int skip = extract_header_attr (au, options[i].name, - options[i].variable); - if (skip < 0) - { - xfree_null (realm); - xfree_null (opaque); - xfree_null (nonce); - return NULL; - } - else if (skip) - { - au += skip; - break; - } - } - if (i == countof (options)) - { - while (*au && *au != '=') - au++; - if (*au && *++au) - { - SKIP_WS (au); - if (*au == '\"') - { - au++; - while (*au && *au != '\"') - au++; - if (*au) - au++; - } - } - } - while (*au && *au != ',') - au++; - if (*au) - au++; + if (name.e - name.b == strlen (options[i].name) + && 0 == strncmp (name.b, options[i].name, name.e - name.b)) + { + *options[i].variable = strdupdelim (value.b, value.e); + break; + } } if (!realm || !nonce || !user || !passwd || !path || !method) { @@ -2998,32 +3049,38 @@ http_cleanup (void) #ifdef TESTING -char * +const char * test_parse_content_disposition() { int i; struct { char *hdrval; + char *opt_dir_prefix; char *filename; bool result; } test_array[] = { - { "filename=\"file.ext\"", "file.ext", true }, - { "attachment; filename=\"file.ext\"", "file.ext", true }, - { "attachment; filename=\"file.ext\"; dummy", "file.ext", true }, - { "attachment", NULL, false }, + { "filename=\"file.ext\"", NULL, "file.ext", true }, + { "filename=\"file.ext\"", "somedir", "somedir/file.ext", true }, + { "attachment; filename=\"file.ext\"", NULL, "file.ext", true }, + { "attachment; filename=\"file.ext\"", "somedir", "somedir/file.ext", true }, + { "attachment; filename=\"file.ext\"; dummy", NULL, "file.ext", true }, + { "attachment; filename=\"file.ext\"; dummy", "somedir", "somedir/file.ext", true }, + { "attachment", NULL, NULL, false }, + { "attachment", "somedir", NULL, false }, }; for (i = 0; i < sizeof(test_array)/sizeof(test_array[0]); ++i) { char *filename; - bool res = parse_content_disposition (test_array[i].hdrval, &filename); + bool res; + + opt.dir_prefix = test_array[i].opt_dir_prefix; + res = parse_content_disposition (test_array[i].hdrval, &filename); mu_assert ("test_parse_content_disposition: wrong result", res == test_array[i].result && (res == false || 0 == strcmp (test_array[i].filename, filename))); - - /* printf ("test %d: %s\n", i, res == false ? "false" : filename); */ } return NULL;