X-Git-Url: http://sjero.net/git/?p=wget;a=blobdiff_plain;f=src%2Fftp.c;h=2cc341bd2a09f70f561f6d5b26f0364ffabdc9dc;hp=9d4344a8b9e57420d55f9a33932f7a91c9738716;hb=2f6aa1d7417df1dfc58597777686fbd77179b9fd;hpb=5ef1d7b949abfce3a363a1170746e6cd1f950486 diff --git a/src/ftp.c b/src/ftp.c index 9d4344a8..2cc341bd 100644 --- a/src/ftp.c +++ b/src/ftp.c @@ -1,6 +1,7 @@ /* File Transfer Protocol support. - Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, - 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, Inc. + Copyright (C) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, + 2005, 2006, 2007, 2008, 2009, 2010, 2011 Free Software Foundation, + Inc. This file is part of GNU Wget. @@ -34,9 +35,7 @@ as that of the covered work. */ #include #include #include -#ifdef HAVE_UNISTD_H -# include -#endif +#include #include #include #include @@ -69,7 +68,7 @@ typedef struct int cmd; /* command code */ int csock; /* control connection socket */ double dltime; /* time of the download in msecs */ - enum stype rs; /* remote system reported by ftp server */ + enum stype rs; /* remote system reported by ftp server */ char *id; /* initial directory */ char *target; /* target file name */ struct url *proxy; /* FTWK-style proxy */ @@ -110,7 +109,7 @@ ftp_expected_bytes (const char *s) } #ifdef ENABLE_IPV6 -/* +/* * This function sets up a passive data connection with the FTP server. * It is merely a wrapper around ftp_epsv, ftp_lpsv and ftp_pasv. */ @@ -125,8 +124,8 @@ ftp_do_pasv (int csock, ip_address *addr, int *port) if (!socket_ip_address (csock, addr, ENDPOINT_PEER)) abort (); - /* If our control connection is over IPv6, then we first try EPSV and then - * LPSV if the former is not supported. If the control connection is over + /* If our control connection is over IPv6, then we first try EPSV and then + * LPSV if the former is not supported. If the control connection is over * IPv4, we simply issue the good old PASV request. */ switch (addr->family) { @@ -155,7 +154,7 @@ ftp_do_pasv (int csock, ip_address *addr, int *port) return err; } -/* +/* * This function sets up an active data connection with the FTP server. * It is merely a wrapper around ftp_eprt, ftp_lprt and ftp_port. */ @@ -168,8 +167,8 @@ ftp_do_port (int csock, int *local_sock) if (!socket_ip_address (csock, &cip, ENDPOINT_PEER)) abort (); - /* If our control connection is over IPv6, then we first try EPRT and then - * LPRT if the former is not supported. If the control connection is over + /* If our control connection is over IPv6, then we first try EPRT and then + * LPRT if the former is not supported. If the control connection is over * IPv4, we simply issue the good old PORT request. */ switch (cip.family) { @@ -241,7 +240,7 @@ static uerr_t ftp_get_listing (struct url *, ccon *, struct fileinfo **); and closes the control connection in case of error. */ static uerr_t getftp (struct url *u, wgint passed_expected_bytes, wgint *qtyread, - wgint restval, ccon *con) + wgint restval, ccon *con, int count) { int csock, dtsock, local_sock, res; uerr_t err = RETROK; /* appease the compiler */ @@ -252,6 +251,7 @@ getftp (struct url *u, wgint passed_expected_bytes, wgint *qtyread, int cmd = con->cmd; bool pasv_mode_open = false; wgint expected_bytes = 0; + bool got_expected_bytes = false; bool rest_failed = false; int flags; wgint rd_size; @@ -312,7 +312,7 @@ getftp (struct url *u, wgint passed_expected_bytes, wgint *qtyread, con->csock = -1; /* Second: Login with proper USER/PASS sequence. */ - logprintf (LOG_VERBOSE, _("Logging in as %s ... "), + logprintf (LOG_VERBOSE, _("Logging in as %s ... "), quotearg_style (escape_quoting_style, user)); if (opt.server_response) logputs (LOG_ALWAYS, "\n"); @@ -429,7 +429,7 @@ Error in server response, closing control connection.\n")); */ /* VMS will report something like "PUB$DEVICE:[INITIAL.FOLDER]". - Convert it to "/INITIAL/FOLDER" */ + Convert it to "/INITIAL/FOLDER" */ if (con->rs == ST_VMS) { char *path = strchr (con->id, '['); @@ -502,10 +502,10 @@ Error in server response, closing control connection.\n")); logputs (LOG_VERBOSE, _("==> CWD not needed.\n")); else { - char *targ; - int cwd_count; - int cwd_end; - int cwd_start; + char *targ = NULL; + int cwd_count; + int cwd_end; + int cwd_start; char *target = u->dir; @@ -529,7 +529,7 @@ Error in server response, closing control connection.\n")); Why is this wise even on UNIX? It certainly fouls VMS. See below for a more reliable, more universal method. */ - + /* 2008-04-22 MJC. I'm not crazy about it either. I'm informed it's useful for misconfigured servers that have some dirs in the path @@ -603,7 +603,7 @@ Error in server response, closing control connection.\n")); #endif /* 0 */ /* 2004-09-20 SMS. - A relative directory is relative to the initial directory. + A relative directory is relative to the initial directory. Thus, what _is_ useful on VMS (and probably elsewhere) is to CWD to the initial directory (ideally, whatever the server reports, _exactly_, NOT badly UNIX-ixed), and then @@ -690,7 +690,7 @@ Error in server response, closing control connection.\n")); if (!opt.server_response) logprintf (LOG_VERBOSE, "==> CWD (%d) %s ... ", cwd_count, quotearg_style (escape_quoting_style, target)); - err = ftp_cwd (csock, target); + err = ftp_cwd (csock, targ); /* FTPRERR, WRITEFAILED, FTPNSFOD */ switch (err) { @@ -738,7 +738,7 @@ Error in server response, closing control connection.\n")); if (opt.verbose) { if (!opt.server_response) - logprintf (LOG_VERBOSE, "==> SIZE %s ... ", + logprintf (LOG_VERBOSE, "==> SIZE %s ... ", quotearg_style (escape_quoting_style, u->file)); } @@ -755,6 +755,7 @@ Error in server response, closing control connection.\n")); con->csock = -1; return err; case FTPOK: + got_expected_bytes = true; /* Everything is OK. */ break; default: @@ -765,6 +766,16 @@ Error in server response, closing control connection.\n")); number_to_static_string (expected_bytes)); } + if (cmd & DO_RETR && restval > 0 && restval == expected_bytes) + { + /* Server confirms that file has length restval. We should stop now. + Some servers (f.e. NcFTPd) return error when receive REST 0 */ + logputs (LOG_VERBOSE, _("File has already been retrieved.\n")); + fd_close (csock); + con->csock = -1; + return RETRFINISHED; + } + /* If anything is to be retrieved, PORT (or PASV) must be sent. */ if (cmd & (DO_LIST | DO_RETR)) { @@ -805,7 +816,7 @@ Error in server response, closing control connection.\n")); } /* switch (err) */ if (err==FTPOK) { - DEBUGP (("trying to connect to %s port %d\n", + DEBUGP (("trying to connect to %s port %d\n", print_address (&passive_addr), passive_port)); dtsock = connect_to_ip (&passive_addr, passive_port, NULL); if (dtsock < 0) @@ -935,14 +946,14 @@ Error in server response, closing control connection.\n")); uerr_t res; struct fileinfo *f; res = ftp_get_listing (u, con, &f); - /* Set the DO_RETR command flag again, because it gets unset when - calling ftp_get_listing() and would otherwise cause an assertion - failure earlier on when this function gets repeatedly called + /* Set the DO_RETR command flag again, because it gets unset when + calling ftp_get_listing() and would otherwise cause an assertion + failure earlier on when this function gets repeatedly called (e.g., when recursing). */ con->cmd |= DO_RETR; if (res == RETROK) { - while (f) + while (f) { if (!strcmp (f->name, u->file)) { @@ -977,7 +988,7 @@ Error in server response, closing control connection.\n")); { if (restval) logputs (LOG_VERBOSE, "\n"); - logprintf (LOG_VERBOSE, "==> RETR %s ... ", + logprintf (LOG_VERBOSE, "==> RETR %s ... ", quotearg_style (escape_quoting_style, u->file)); } } @@ -1019,7 +1030,9 @@ Error in server response, closing control connection.\n")); if (!opt.server_response) logputs (LOG_VERBOSE, _("done.\n")); - expected_bytes = ftp_expected_bytes (ftp_last_respline); + + if (! got_expected_bytes) + expected_bytes = ftp_expected_bytes (ftp_last_respline); } /* do retrieve */ if (cmd & DO_LIST) @@ -1065,7 +1078,9 @@ Error in server response, closing control connection.\n")); } if (!opt.server_response) logputs (LOG_VERBOSE, _("done.\n")); - expected_bytes = ftp_expected_bytes (ftp_last_respline); + + if (! got_expected_bytes) + expected_bytes = ftp_expected_bytes (ftp_last_respline); } /* cmd & DO_LIST */ if (!(cmd & (DO_LIST | DO_RETR)) || (opt.spider && !(cmd & DO_LIST))) @@ -1095,7 +1110,7 @@ Error in server response, closing control connection.\n")); } /* Open the file -- if output_stream is set, use it instead. */ - + /* 2005-04-17 SMS. Note that having the output_stream ("-O") file opened in main() (main.c) rather limits the ability in VMS to open the file @@ -1116,7 +1131,7 @@ Error in server response, closing control connection.\n")); con->target = targ; } #endif /* def __VMS */ - + mkalldirs (con->target); if (opt.backups) rotate_backups (con->target); @@ -1135,7 +1150,7 @@ Error in server response, closing control connection.\n")); #else /* def __VMS */ # define BIN_TYPE_FILE 1 #endif /* def __VMS [else] */ - + if (restval && !(con->cmd & DO_LIST)) { #ifdef __VMS @@ -1156,8 +1171,23 @@ Error in server response, closing control connection.\n")); #endif /* def __VMS [else] */ } else if (opt.noclobber || opt.always_rest || opt.timestamping || opt.dirstruct - || opt.output_document) - { + || opt.output_document || count > 0) + { + if (opt.unlink && file_exists_p (con->target)) + { + int res = unlink (con->target); + if (res < 0) + { + logprintf (LOG_NOTQUIET, "%s: %s\n", con->target, + strerror (errno)); + fd_close (csock); + con->csock = -1; + fd_close (dtsock); + fd_close (local_sock); + return UNLINKERR; + } + } + #ifdef __VMS int open_id; @@ -1331,7 +1361,7 @@ Error in server response, closing control connection.\n")); char *p = strchr (line, '\0'); while (p > line && (p[-1] == '\n' || p[-1] == '\r')) *--p = '\0'; - logprintf (LOG_ALWAYS, "%s\n", + logprintf (LOG_ALWAYS, "%s\n", quotearg_style (escape_quoting_style, line)); xfree (line); } @@ -1348,7 +1378,7 @@ Error in server response, closing control connection.\n")); This loop either gets commands from con, or (if ON_YOUR_OWN is set), makes them up to retrieve the file given by the URL. */ static uerr_t -ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con) +ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con, char **local_file) { int count, orig_lp; wgint restval, len = 0, qtyread = 0; @@ -1366,7 +1396,7 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con) else { /* URL-derived file. Consider "-O file" name. */ - con->target = url_file_name (u); + con->target = url_file_name (u, NULL); if (!opt.output_document) locf = con->target; else @@ -1453,11 +1483,11 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con) xfree (hurl); } /* Send getftp the proper length, if fileinfo was provided. */ - if (f) + if (f && f->type != FT_SYMLINK) len = f->size; else len = 0; - err = getftp (u, len, &qtyread, restval, con); + err = getftp (u, len, &qtyread, restval, con, count); if (con->csock == -1) con->st &= ~DONE_CWD; @@ -1468,6 +1498,7 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con) { case HOSTERR: case CONIMPOSSIBLE: case FWRITEERR: case FOPENERR: case FTPNSFOD: case FTPLOGINC: case FTPNOPASV: case CONTNOTSUPPORTED: + case UNLINKERR: /* Fatal errors, give up. */ return err; case CONSOCKERR: case CONERROR: case FTPSRVERR: case FTPRERR: @@ -1480,7 +1511,7 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con) { /* Re-determine the file name. */ xfree_null (con->target); - con->target = url_file_name (u); + con->target = url_file_name (u, NULL); locf = con->target; } continue; @@ -1561,7 +1592,7 @@ ftp_loop_internal (struct url *u, struct fileinfo *f, ccon *con) total_downloaded_bytes += qtyread; numurls++; - if (opt.delete_after) + if (opt.delete_after && !input_file_url (opt.input_filename)) { DEBUGP (("\ Removing file due to --delete-after in ftp_loop_internal():\n")); @@ -1576,6 +1607,10 @@ Removing file due to --delete-after in ftp_loop_internal():\n")); con->cmd |= LEAVE_PENDING; else con->cmd &= ~LEAVE_PENDING; + + if (local_file) + *local_file = xstrdup (locf); + return RETROK; } while (!opt.ntry || (count < opt.ntry)); @@ -1604,14 +1639,14 @@ ftp_get_listing (struct url *u, ccon *con, struct fileinfo **f) /* Find the listing file name. We do it by taking the file name of the URL and replacing the last component with the listing file name. */ - uf = url_file_name (u); + uf = url_file_name (u, NULL); lf = file_merge (uf, LIST_FILENAME); xfree (uf); DEBUGP ((_("Using %s as listing tmp file.\n"), quote (lf))); con->target = xstrdup (lf); xfree (lf); - err = ftp_loop_internal (u, NULL, con); + err = ftp_loop_internal (u, NULL, con, NULL); lf = xstrdup (con->target); xfree (con->target); con->target = old_target; @@ -1698,7 +1733,7 @@ ftp_retrieve_list (struct url *u, struct fileinfo *f, ccon *con) ofile = xstrdup (u->file); url_set_file (u, f->name); - con->target = url_file_name (u); + con->target = url_file_name (u, NULL); err = RETROK; dlthis = true; @@ -1806,7 +1841,7 @@ Already have correct symlink %s -> %s\n\n"), else /* opt.retr_symlinks */ { if (dlthis) - err = ftp_loop_internal (u, f, con); + err = ftp_loop_internal (u, f, con, NULL); } /* opt.retr_symlinks */ break; case FT_DIRECTORY: @@ -1817,7 +1852,7 @@ Already have correct symlink %s -> %s\n\n"), case FT_PLAINFILE: /* Call the retrieve loop. */ if (dlthis) - err = ftp_loop_internal (u, f, con); + err = ftp_loop_internal (u, f, con, NULL); break; case FT_UNKNOWN: logprintf (LOG_NOTQUIET, _("%s: unknown/unsupported file type.\n"), @@ -1849,7 +1884,8 @@ Already have correct symlink %s -> %s\n\n"), original. :( */ if (actual_target != NULL) { - if (!(f->type == FT_SYMLINK && !opt.retr_symlinks) + if (opt.useservertimestamps + && !(f->type == FT_SYMLINK && !opt.retr_symlinks) && f->tstamp != -1 && dlthis && file_exists_p (con->target)) @@ -2033,7 +2069,7 @@ ftp_retrieve_glob (struct url *u, ccon *con, int action) if (matchres == -1) { logprintf (LOG_NOTQUIET, _("Error matching %s against %s: %s\n"), - u->file, quotearg_style (escape_quoting_style, f->name), + u->file, quotearg_style (escape_quoting_style, f->name), strerror (errno)); break; } @@ -2095,7 +2131,7 @@ ftp_retrieve_glob (struct url *u, ccon *con, int action) { /* Let's try retrieving it anyway. */ con->st |= ON_YOUR_OWN; - res = ftp_loop_internal (u, NULL, con); + res = ftp_loop_internal (u, NULL, con, NULL); return res; } @@ -2116,7 +2152,8 @@ ftp_retrieve_glob (struct url *u, ccon *con, int action) of URL. Inherently, its capabilities are limited on what can be encoded into a URL. */ uerr_t -ftp_loop (struct url *u, int *dt, struct url *proxy, bool recursive, bool glob) +ftp_loop (struct url *u, char **local_file, int *dt, struct url *proxy, + bool recursive, bool glob) { ccon con; /* FTP connection */ uerr_t res; @@ -2146,7 +2183,7 @@ ftp_loop (struct url *u, int *dt, struct url *proxy, bool recursive, bool glob) char *filename = (opt.output_document ? xstrdup (opt.output_document) : (con.target ? xstrdup (con.target) - : url_file_name (u))); + : url_file_name (u, NULL))); res = ftp_index (filename, u, f); if (res == FTPOK && opt.verbose) { @@ -2195,7 +2232,7 @@ ftp_loop (struct url *u, int *dt, struct url *proxy, bool recursive, bool glob) ispattern ? GLOB_GLOBALL : GLOB_GETONE); } else - res = ftp_loop_internal (u, NULL, &con); + res = ftp_loop_internal (u, NULL, &con, local_file); } if (res == FTPOK) res = RETROK;