1
0
mirror of https://github.com/moparisthebest/wget synced 2024-07-03 16:38:41 -04:00

[svn] Add the POST method.

Published in <sxssn5yj4eb.fsf@florida.arsdigita.de>.
This commit is contained in:
hniksic 2002-04-13 21:22:47 -07:00
parent f0c20666da
commit 5cb8a6f44d
10 changed files with 240 additions and 15 deletions

4
NEWS
View File

@ -7,6 +7,10 @@ Please send GNU Wget bug reports to <bug-wget@gnu.org>.
* Changes in Wget 1.9. * Changes in Wget 1.9.
** It is now possible to specify that POST method be used for HTTP
requests. For example, `wget --post-data="id=foo&data=bar" URL' will
send a POST request with the specified contents.
** IPv6 is experimentally supported. ** IPv6 is experimentally supported.
** The `--timeout' option now affects the connect timeout as well. ** The `--timeout' option now affects the connect timeout as well.

View File

@ -1,3 +1,19 @@
2002-04-14 Hrvoje Niksic <hniksic@arsdigita.com>
* retr.c (retrieve_url): Make sure that POST is not honored for
redirections.
* http.c (gethttp): Send the POST data when requested.
(post_file): New function.
(gethttp): Use it.
* main.c (main): Ditto.
* init.c: Add new options.
* options.h (struct options): New options post_data and
post_file_name.
2002-04-14 Hrvoje Niksic <hniksic@arsdigita.com> 2002-04-14 Hrvoje Niksic <hniksic@arsdigita.com>
* connect.c (connect_with_timeout): Firing SIGALRM can result in * connect.c (connect_with_timeout): Firing SIGALRM can result in

View File

@ -473,7 +473,6 @@ iwrite (int fd, char *buf, int len)
{ {
/* Set errno to ETIMEDOUT on timeout. */ /* Set errno to ETIMEDOUT on timeout. */
if (res == 0) if (res == 0)
/* #### Potentially evil! */
errno = ETIMEDOUT; errno = ETIMEDOUT;
return -1; return -1;
} }

View File

@ -1,5 +1,5 @@
/* HTTP support. /* HTTP support.
Copyright (C) 1995, 1996, 1997, 1998, 2000, 2001 Copyright (C) 1995, 1996, 1997, 1998, 2000, 2001, 2002
Free Software Foundation, Inc. Free Software Foundation, Inc.
This file is part of GNU Wget. This file is part of GNU Wget.
@ -170,6 +170,79 @@ parse_http_status_line (const char *line, const char **reason_phrase_ptr)
return statcode; return statcode;
} }
#define WMIN(x, y) ((x) > (y) ? (y) : (x))
/* Send the contents of FILE_NAME to SOCK/SSL. Make sure that exactly
PROMISED_SIZE bytes are sent over the wire -- if the file is
longer, read only that much; if the file is shorter, pad it with
zeros. */
static int
post_file (int sock, void *ssl, const char *file_name, long promised_size)
{
static char chunk[8192];
int written = 0;
int write_error;
FILE *fp;
/* Only one of SOCK and SSL may be active at the same time. */
assert (sock > -1 || ssl != NULL);
assert (sock == -1 || ssl == NULL);
DEBUGP (("[writing POST file %s ... ", file_name));
fp = fopen (file_name, "rb");
if (!fp)
goto pad;
while (written < promised_size)
{
long towrite;
int length = fread (chunk, 1, sizeof (chunk), fp);
if (length == 0)
break;
towrite = WMIN (promised_size - written, length);
#ifdef HAVE_SSL
if (ssl)
write_error = ssl_iwrite (ssl, chunk, towrite);
else
#endif
write_error = iwrite (sock, chunk, towrite);
if (write_error < 0)
{
fclose (fp);
return -1;
}
written += towrite;
}
fclose (fp);
pad:
if (written < promised_size)
{
DEBUGP (("padding ... "));
/* This highly unlikely case can happen only if the file has
shrunk while we weren't looking. To uphold the promise, pad
the remaining data with zeros. #### Should we abort
instead? */
memset (chunk, '\0', sizeof (chunk));
while (written < promised_size)
{
long towrite = WMIN (promised_size - written, sizeof (chunk));
#ifdef HAVE_SSL
if (ssl)
write_error = ssl_iwrite (ssl, chunk, towrite);
else
#endif
write_error = iwrite (sock, chunk, towrite);
if (write_error < 0)
return -1;
written += towrite;
}
}
assert (written == promised_size);
DEBUGP (("done]\n"));
return 0;
}
/* Functions to be used as arguments to header_process(): */ /* Functions to be used as arguments to header_process(): */
struct http_process_range_closure { struct http_process_range_closure {
@ -540,7 +613,8 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
char *all_headers; char *all_headers;
char *port_maybe; char *port_maybe;
char *request_keep_alive; char *request_keep_alive;
int sock, hcount, num_written, all_length, statcode; int sock, hcount, all_length, statcode;
int write_error;
long contlen, contrange; long contlen, contrange;
struct url *conn; struct url *conn;
FILE *fp; FILE *fp;
@ -549,7 +623,7 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
#ifdef HAVE_SSL #ifdef HAVE_SSL
static SSL_CTX *ssl_ctx = NULL; static SSL_CTX *ssl_ctx = NULL;
SSL *ssl = NULL; SSL *ssl = NULL;
#endif /* HAVE_SSL */ #endif
char *cookies = NULL; char *cookies = NULL;
/* Whether this connection will be kept alive after the HTTP request /* Whether this connection will be kept alive after the HTTP request
@ -568,6 +642,10 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
"Host: symbolic-name:1234". */ "Host: symbolic-name:1234". */
int squares_around_host = 0; int squares_around_host = 0;
/* Headers sent when using POST. */
char *post_content_type, *post_content_length;
long post_data_size;
#ifdef HAVE_SSL #ifdef HAVE_SSL
/* initialize ssl_ctx on first run */ /* initialize ssl_ctx on first run */
if (!ssl_ctx) if (!ssl_ctx)
@ -624,6 +702,9 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
keep_alive = 0; keep_alive = 0;
http_keep_alive_1 = http_keep_alive_2 = 0; http_keep_alive_1 = http_keep_alive_2 = 0;
post_content_type = NULL;
post_content_length = NULL;
/* Initialize certain elements of struct http_stat. */ /* Initialize certain elements of struct http_stat. */
hs->len = 0L; hs->len = 0L;
hs->contlen = -1; hs->contlen = -1;
@ -683,7 +764,12 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
DEBUGP (("Reusing fd %d.\n", sock)); DEBUGP (("Reusing fd %d.\n", sock));
} }
command = (*dt & HEAD_ONLY) ? "HEAD" : "GET"; if (*dt & HEAD_ONLY)
command = "HEAD";
else if (opt.post_file_name || opt.post_data)
command = "POST";
else
command = "GET";
referer = NULL; referer = NULL;
if (hs->referer) if (hs->referer)
@ -812,6 +898,26 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
#endif #endif
); );
if (opt.post_data || opt.post_file_name)
{
post_content_type = "Content-Type: application/x-www-form-urlencoded\r\n";
if (opt.post_data)
post_data_size = strlen (opt.post_data);
else
{
post_data_size = file_size (opt.post_file_name);
if (post_data_size == -1)
{
logprintf (LOG_NOTQUIET, "POST data file missing: %s\n",
opt.post_file_name);
post_data_size = 0;
}
}
post_content_length = xmalloc (16 + numdigit (post_data_size) + 2 + 1);
sprintf (post_content_length,
"Content-Length: %ld\r\n", post_data_size);
}
if (proxy) if (proxy)
full_path = xstrdup (u->url); full_path = xstrdup (u->url);
else else
@ -838,6 +944,10 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
+ (proxyauth ? strlen (proxyauth) : 0) + (proxyauth ? strlen (proxyauth) : 0)
+ (range ? strlen (range) : 0) + (range ? strlen (range) : 0)
+ strlen (pragma_h) + strlen (pragma_h)
+ (post_content_type
? strlen (post_content_type) : 0)
+ (post_content_length
? strlen (post_content_length) : 0)
+ (opt.user_header ? strlen (opt.user_header) : 0) + (opt.user_header ? strlen (opt.user_header) : 0)
+ 64); + 64);
/* Construct the request. */ /* Construct the request. */
@ -846,7 +956,7 @@ gethttp (struct url *u, struct http_stat *hs, int *dt, struct url *proxy)
User-Agent: %s\r\n\ User-Agent: %s\r\n\
Host: %s%s%s%s\r\n\ Host: %s%s%s%s\r\n\
Accept: %s\r\n\ Accept: %s\r\n\
%s%s%s%s%s%s%s%s\r\n", %s%s%s%s%s%s%s%s%s%s\r\n",
command, full_path, command, full_path,
useragent, useragent,
squares_around_host ? "[" : "", u->host, squares_around_host ? "]" : "", squares_around_host ? "[" : "", u->host, squares_around_host ? "]" : "",
@ -858,9 +968,11 @@ Accept: %s\r\n\
wwwauth ? wwwauth : "", wwwauth ? wwwauth : "",
proxyauth ? proxyauth : "", proxyauth ? proxyauth : "",
range ? range : "", range ? range : "",
pragma_h, pragma_h,
post_content_type ? post_content_type : "",
post_content_length ? post_content_length : "",
opt.user_header ? opt.user_header : ""); opt.user_header ? opt.user_header : "");
DEBUGP (("---request begin---\n%s---request end---\n", request)); DEBUGP (("---request begin---\n%s", request));
/* Free the temporary memory. */ /* Free the temporary memory. */
FREE_MAYBE (wwwauth); FREE_MAYBE (wwwauth);
@ -871,12 +983,38 @@ Accept: %s\r\n\
/* Send the request to server. */ /* Send the request to server. */
#ifdef HAVE_SSL #ifdef HAVE_SSL
if (conn->scheme == SCHEME_HTTPS) if (conn->scheme == SCHEME_HTTPS)
num_written = ssl_iwrite (ssl, request, strlen (request)); write_error = ssl_iwrite (ssl, request, strlen (request));
else else
#endif /* HAVE_SSL */ #endif
num_written = iwrite (sock, request, strlen (request)); write_error = iwrite (sock, request, strlen (request));
if (num_written < 0) if (write_error >= 0)
{
if (opt.post_data)
{
DEBUGP (("[POST data: %s]\n", opt.post_data));
#ifdef HAVE_SSL
if (conn->scheme == SCHEME_HTTPS)
write_error = ssl_iwrite (ssl, opt.post_data, post_data_size);
else
#endif
write_error = iwrite (sock, opt.post_data, post_data_size);
}
else if (opt.post_file_name)
{
#ifdef HAVE_SSL
if (conn->scheme == SCHEME_HTTPS)
write_error = post_file (-1, ssl, opt.post_file_name,
post_data_size);
else
#endif
write_error = post_file (sock, NULL, opt.post_file_name,
post_data_size);
}
}
DEBUGP (("---request end---\n"));
if (write_error < 0)
{ {
logprintf (LOG_VERBOSE, _("Failed writing HTTP request: %s.\n"), logprintf (LOG_VERBOSE, _("Failed writing HTTP request: %s.\n"),
strerror (errno)); strerror (errno));

View File

@ -158,6 +158,8 @@ static struct {
{ "pagerequisites", &opt.page_requisites, cmd_boolean }, { "pagerequisites", &opt.page_requisites, cmd_boolean },
{ "passiveftp", &opt.ftp_pasv, cmd_lockable_boolean }, { "passiveftp", &opt.ftp_pasv, cmd_lockable_boolean },
{ "passwd", &opt.ftp_pass, cmd_string }, { "passwd", &opt.ftp_pass, cmd_string },
{ "postdata", &opt.post_data, cmd_string },
{ "postfile", &opt.post_file_name, cmd_file },
{ "progress", &opt.progress_type, cmd_spec_progress }, { "progress", &opt.progress_type, cmd_spec_progress },
{ "proxypasswd", &opt.proxy_passwd, cmd_string }, { "proxypasswd", &opt.proxy_passwd, cmd_string },
{ "proxyuser", &opt.proxy_user, cmd_string }, { "proxyuser", &opt.proxy_user, cmd_string },

View File

@ -307,6 +307,8 @@ main (int argc, char *const *argv)
{ "no", required_argument, NULL, 'n' }, { "no", required_argument, NULL, 'n' },
{ "output-document", required_argument, NULL, 'O' }, { "output-document", required_argument, NULL, 'O' },
{ "output-file", required_argument, NULL, 'o' }, { "output-file", required_argument, NULL, 'o' },
{ "post-data", required_argument, NULL, 167 },
{ "post-file", required_argument, NULL, 168 },
{ "progress", required_argument, NULL, 163 }, { "progress", required_argument, NULL, 163 },
{ "proxy", required_argument, NULL, 'Y' }, { "proxy", required_argument, NULL, 'Y' },
{ "proxy-passwd", required_argument, NULL, 144 }, { "proxy-passwd", required_argument, NULL, 144 },
@ -547,6 +549,12 @@ GNU General Public License for more details.\n"));
setval ("egdfile", optarg); setval ("egdfile", optarg);
break; break;
#endif /* HAVE_SSL */ #endif /* HAVE_SSL */
case 167:
setval ("postdata", optarg);
break;
case 168:
setval ("postfile", optarg);
break;
case 'A': case 'A':
setval ("accept", optarg); setval ("accept", optarg);
break; break;

View File

@ -164,6 +164,9 @@ struct options
int cookies; int cookies;
char *cookies_input; char *cookies_input;
char *cookies_output; char *cookies_output;
char *post_data; /* POST query string */
char *post_file_name; /* File to post */
}; };
extern struct options opt; extern struct options opt;

View File

@ -154,7 +154,7 @@ get_contents (int fd, FILE *fp, long *len, long restval, long expected,
res = -2; res = -2;
goto out; goto out;
} }
if (opt.verbose) if (progress)
progress_update (progress, sz, 0); progress_update (progress, sz, 0);
} }
@ -202,7 +202,7 @@ get_contents (int fd, FILE *fp, long *len, long restval, long expected,
last_dltime = dltime; last_dltime = dltime;
} }
if (opt.verbose) if (progress)
progress_update (progress, res, dltime); progress_update (progress, res, dltime);
*len += res; *len += res;
} }
@ -213,7 +213,7 @@ get_contents (int fd, FILE *fp, long *len, long restval, long expected,
res = -1; res = -1;
out: out:
if (opt.verbose) if (progress)
progress_finish (progress, dltime); progress_finish (progress, dltime);
if (elapsed) if (elapsed)
*elapsed = dltime; *elapsed = dltime;
@ -281,9 +281,29 @@ calc_rate (long bytes, long msecs, int *units)
#define MAX_REDIRECTIONS 20 #define MAX_REDIRECTIONS 20
#define SUSPEND_POST_DATA do { \
post_data_suspended = 1; \
saved_post_data = opt.post_data; \
saved_post_file_name = opt.post_file_name; \
opt.post_data = NULL; \
opt.post_file_name = NULL; \
} while (0)
#define RESTORE_POST_DATA do { \
if (post_data_suspended) \
{ \
opt.post_data = saved_post_data; \
opt.post_file_name = saved_post_file_name; \
post_data_suspended = 0; \
} \
} while (0)
/* Retrieve the given URL. Decides which loop to call -- HTTP, FTP, /* Retrieve the given URL. Decides which loop to call -- HTTP, FTP,
FTP, proxy, etc. */ FTP, proxy, etc. */
/* #### This function should be rewritten so it doesn't return from
multiple points. */
uerr_t uerr_t
retrieve_url (const char *origurl, char **file, char **newloc, retrieve_url (const char *origurl, char **file, char **newloc,
const char *refurl, int *dt) const char *refurl, int *dt)
@ -297,6 +317,10 @@ retrieve_url (const char *origurl, char **file, char **newloc,
char *local_file; char *local_file;
int redirection_count = 0; int redirection_count = 0;
int post_data_suspended = 0;
char *saved_post_data;
char *saved_post_file_name;
/* If dt is NULL, just ignore it. */ /* If dt is NULL, just ignore it. */
if (!dt) if (!dt)
dt = &dummy; dt = &dummy;
@ -334,6 +358,7 @@ retrieve_url (const char *origurl, char **file, char **newloc,
logprintf (LOG_NOTQUIET, _("Error parsing proxy URL %s: %s.\n"), logprintf (LOG_NOTQUIET, _("Error parsing proxy URL %s: %s.\n"),
proxy, url_error (up_error_code)); proxy, url_error (up_error_code));
xfree (url); xfree (url);
RESTORE_POST_DATA;
return PROXERR; return PROXERR;
} }
if (proxy_url->scheme != SCHEME_HTTP && proxy_url->scheme != u->scheme) if (proxy_url->scheme != SCHEME_HTTP && proxy_url->scheme != u->scheme)
@ -341,6 +366,7 @@ retrieve_url (const char *origurl, char **file, char **newloc,
logprintf (LOG_NOTQUIET, _("Error in proxy URL %s: Must be HTTP.\n"), proxy); logprintf (LOG_NOTQUIET, _("Error in proxy URL %s: Must be HTTP.\n"), proxy);
url_free (proxy_url); url_free (proxy_url);
xfree (url); xfree (url);
RESTORE_POST_DATA;
return PROXERR; return PROXERR;
} }
} }
@ -409,6 +435,7 @@ retrieve_url (const char *origurl, char **file, char **newloc,
url_free (u); url_free (u);
xfree (url); xfree (url);
xfree (mynewloc); xfree (mynewloc);
RESTORE_POST_DATA;
return result; return result;
} }
@ -427,6 +454,7 @@ retrieve_url (const char *origurl, char **file, char **newloc,
url_free (u); url_free (u);
xfree (url); xfree (url);
xfree (mynewloc); xfree (mynewloc);
RESTORE_POST_DATA;
return WRONGCODE; return WRONGCODE;
} }
@ -434,6 +462,15 @@ retrieve_url (const char *origurl, char **file, char **newloc,
url = mynewloc; url = mynewloc;
url_free (u); url_free (u);
u = newloc_parsed; u = newloc_parsed;
/* If we're being redirected from POST, we don't want to POST
again. Many requests answer POST with a redirection to an
index page; that redirection is clearly a GET. We "suspend"
POST data for the duration of the redirections, and restore
it when we're done. */
if (!post_data_suspended)
SUSPEND_POST_DATA;
goto redirected; goto redirected;
} }
@ -471,6 +508,7 @@ retrieve_url (const char *origurl, char **file, char **newloc,
} }
++global_download_count; ++global_download_count;
RESTORE_POST_DATA;
return result; return result;
} }

View File

@ -554,6 +554,22 @@ file_non_directory_p (const char *path)
return S_ISDIR (buf.st_mode) ? 0 : 1; return S_ISDIR (buf.st_mode) ? 0 : 1;
} }
/* Return the size of file named by FILENAME, or -1 if it cannot be
opened or seeked into. */
long
file_size (const char *filename)
{
long size;
/* We use fseek rather than stat to determine the file size because
that way we can also verify whether the file is readable.
Inspired by the POST patch by Arnaud Wylie. */
FILE *fp = fopen (filename, "rb");
fseek (fp, 0, SEEK_END);
size = ftell (fp);
fclose (fp);
return size;
}
/* Return a unique filename, given a prefix and count */ /* Return a unique filename, given a prefix and count */
static char * static char *
unique_name_1 (const char *fileprefix, int count) unique_name_1 (const char *fileprefix, int count)

View File

@ -61,6 +61,7 @@ void touch PARAMS ((const char *, time_t));
int remove_link PARAMS ((const char *)); int remove_link PARAMS ((const char *));
int file_exists_p PARAMS ((const char *)); int file_exists_p PARAMS ((const char *));
int file_non_directory_p PARAMS ((const char *)); int file_non_directory_p PARAMS ((const char *));
long file_size PARAMS ((const char *));
int make_directory PARAMS ((const char *)); int make_directory PARAMS ((const char *));
char *unique_name PARAMS ((const char *)); char *unique_name PARAMS ((const char *));
char *file_merge PARAMS ((const char *, const char *)); char *file_merge PARAMS ((const char *, const char *));