📄 ftp.c
字号:
char *target = u->dir; DEBUGP (("changing working directory\n")); /* Change working directory. To change to a non-absolute Unix directory, we need to prepend initial directory (con->id) to it. Absolute directories "just work". A relative directory is one that does not begin with '/' and, on non-Unix OS'es, one that doesn't begin with "[a-z]:". This is not done for OS400, which doesn't use "/"-delimited directories, nor does it support directory hierarchies. "CWD foo" followed by "CWD bar" leaves us in "bar", not in "foo/bar", as would be customary elsewhere. */ if (target[0] != '/' && !(con->rs != ST_UNIX && ISALPHA (target[0]) && target[1] == ':') && con->rs != ST_OS400) { int idlen = strlen (con->id); char *ntarget, *p; /* Strip trailing slash(es) from con->id. */ while (idlen > 0 && con->id[idlen - 1] == '/') --idlen; p = ntarget = (char *)alloca (idlen + 1 + strlen (u->dir) + 1); memcpy (p, con->id, idlen); p += idlen; *p++ = '/'; strcpy (p, target); DEBUGP (("Prepended initial PWD to relative path:\n")); DEBUGP ((" pwd: '%s'\n old: '%s'\n new: '%s'\n", con->id, target, ntarget)); target = ntarget; } /* If the FTP host runs VMS, we will have to convert the absolute directory path in UNIX notation to absolute directory path in VMS notation as VMS FTP servers do not like UNIX notation of absolute paths. "VMS notation" is [dir.subdir.subsubdir]. */ if (con->rs == ST_VMS) { char *tmpp; char *ntarget = (char *)alloca (strlen (target) + 2); /* We use a converted initial dir, so directories in TARGET will be separated with slashes, something like "/INITIAL/FOLDER/DIR/SUBDIR". Convert that to "[INITIAL.FOLDER.DIR.SUBDIR]". */ strcpy (ntarget, target); assert (*ntarget == '/'); *ntarget = '['; for (tmpp = ntarget + 1; *tmpp; tmpp++) if (*tmpp == '/') *tmpp = '.'; *tmpp++ = ']'; *tmpp = '\0'; DEBUGP (("Changed file name to VMS syntax:\n")); DEBUGP ((" Unix: '%s'\n VMS: '%s'\n", target, ntarget)); target = ntarget; } if (!opt.server_response) logprintf (LOG_VERBOSE, "==> CWD %s ... ", escnonprint (target)); err = ftp_cwd (csock, target); /* FTPRERR, WRITEFAILED, FTPNSFOD */ switch (err) { case FTPRERR: logputs (LOG_VERBOSE, "\n"); logputs (LOG_NOTQUIET, _("\Error in server response, closing control connection.\n")); fd_close (csock); con->csock = -1; return err; case WRITEFAILED: logputs (LOG_VERBOSE, "\n"); logputs (LOG_NOTQUIET, _("Write failed, closing control connection.\n")); fd_close (csock); con->csock = -1; return err; case FTPNSFOD: logputs (LOG_VERBOSE, "\n"); logprintf (LOG_NOTQUIET, _("No such directory `%s'.\n\n"), escnonprint (u->dir)); fd_close (csock); con->csock = -1; return err; case FTPOK: break; default: abort (); } if (!opt.server_response) logputs (LOG_VERBOSE, _("done.\n")); } } else /* do not CWD */ logputs (LOG_VERBOSE, _("==> CWD not required.\n")); if ((cmd & DO_RETR) && *len == 0) { if (opt.verbose) { if (!opt.server_response) logprintf (LOG_VERBOSE, "==> SIZE %s ... ", escnonprint (u->file)); } err = ftp_size (csock, u->file, len); /* FTPRERR */ switch (err) { case FTPRERR: case FTPSRVERR: logputs (LOG_VERBOSE, "\n"); logputs (LOG_NOTQUIET, _("\Error in server response, closing control connection.\n")); fd_close (csock); con->csock = -1; return err; case FTPOK: /* Everything is OK. */ break; default: abort (); } if (!opt.server_response) logprintf (LOG_VERBOSE, *len ? "%s\n" : _("done.\n"), number_to_static_string (*len)); } /* If anything is to be retrieved, PORT (or PASV) must be sent. */ if (cmd & (DO_LIST | DO_RETR)) { if (opt.ftp_pasv) { ip_address passive_addr; int passive_port; err = ftp_do_pasv (csock, &passive_addr, &passive_port); /* FTPRERR, WRITEFAILED, FTPNOPASV, FTPINVPASV */ switch (err) { case FTPRERR: logputs (LOG_VERBOSE, "\n"); logputs (LOG_NOTQUIET, _("\Error in server response, closing control connection.\n")); fd_close (csock); con->csock = -1; return err; case WRITEFAILED: logputs (LOG_VERBOSE, "\n"); logputs (LOG_NOTQUIET, _("Write failed, closing control connection.\n")); fd_close (csock); con->csock = -1; return err; case FTPNOPASV: logputs (LOG_VERBOSE, "\n"); logputs (LOG_NOTQUIET, _("Cannot initiate PASV transfer.\n")); break; case FTPINVPASV: logputs (LOG_VERBOSE, "\n"); logputs (LOG_NOTQUIET, _("Cannot parse PASV response.\n")); break; case FTPOK: break; default: abort (); } /* switch (err) */ if (err==FTPOK) { 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) { int save_errno = errno; fd_close (csock); con->csock = -1; logprintf (LOG_VERBOSE, _("couldn't connect to %s port %d: %s\n"), print_address (&passive_addr), passive_port, strerror (save_errno)); return (retryable_socket_connect_error (save_errno) ? CONERROR : CONIMPOSSIBLE); } pasv_mode_open = true; /* Flag to avoid accept port */ if (!opt.server_response) logputs (LOG_VERBOSE, _("done. ")); } /* err==FTP_OK */ } if (!pasv_mode_open) /* Try to use a port command if PASV failed */ { err = ftp_do_port (csock, &local_sock); /* FTPRERR, WRITEFAILED, bindport (FTPSYSERR), HOSTERR, FTPPORTERR */ switch (err) { case FTPRERR: logputs (LOG_VERBOSE, "\n"); logputs (LOG_NOTQUIET, _("\Error in server response, closing control connection.\n")); fd_close (csock); con->csock = -1; fd_close (dtsock); fd_close (local_sock); return err; case WRITEFAILED: logputs (LOG_VERBOSE, "\n"); logputs (LOG_NOTQUIET, _("Write failed, closing control connection.\n")); fd_close (csock); con->csock = -1; fd_close (dtsock); fd_close (local_sock); return err; case CONSOCKERR: logputs (LOG_VERBOSE, "\n"); logprintf (LOG_NOTQUIET, "socket: %s\n", strerror (errno)); fd_close (csock); con->csock = -1; fd_close (dtsock); fd_close (local_sock); return err; case FTPSYSERR: logputs (LOG_VERBOSE, "\n"); logprintf (LOG_NOTQUIET, _("Bind error (%s).\n"), strerror (errno)); fd_close (dtsock); return err; case FTPPORTERR: logputs (LOG_VERBOSE, "\n"); logputs (LOG_NOTQUIET, _("Invalid PORT.\n")); fd_close (csock); con->csock = -1; fd_close (dtsock); fd_close (local_sock); return err; case FTPOK: break; default: abort (); } /* port switch */ if (!opt.server_response) logputs (LOG_VERBOSE, _("done. ")); } /* dtsock == -1 */ } /* cmd & (DO_LIST | DO_RETR) */ /* Restart if needed. */ if (restval && (cmd & DO_RETR)) { if (!opt.server_response) logprintf (LOG_VERBOSE, "==> REST %s ... ", number_to_static_string (restval)); err = ftp_rest (csock, restval); /* FTPRERR, WRITEFAILED, FTPRESTFAIL */ switch (err) { case FTPRERR: logputs (LOG_VERBOSE, "\n"); logputs (LOG_NOTQUIET, _("\Error in server response, closing control connection.\n")); fd_close (csock); con->csock = -1; fd_close (dtsock); fd_close (local_sock); return err; case WRITEFAILED: logputs (LOG_VERBOSE, "\n"); logputs (LOG_NOTQUIET, _("Write failed, closing control connection.\n")); fd_close (csock); con->csock = -1; fd_close (dtsock); fd_close (local_sock); return err; case FTPRESTFAIL: logputs (LOG_VERBOSE, _("\nREST failed, starting from scratch.\n")); rest_failed = true; break; case FTPOK: break; default: abort (); } if (err != FTPRESTFAIL && !opt.server_response) logputs (LOG_VERBOSE, _("done. ")); } /* restval && cmd & DO_RETR */ if (cmd & DO_RETR) { /* If we're in spider mode, don't really retrieve anything. The fact that we got to this point should be proof enough that the file exists, vaguely akin to HTTP's concept of a "HEAD" request. */ if (opt.spider) { fd_close (csock); con->csock = -1; fd_close (dtsock); fd_close (local_sock); return RETRFINISHED; } if (opt.verbose) { if (!opt.server_response) { if (restval) logputs (LOG_VERBOSE, "\n"); logprintf (LOG_VERBOSE, "==> RETR %s ... ", escnonprint (u->file)); } } err = ftp_retr (csock, u->file); /* FTPRERR, WRITEFAILED, FTPNSFOD */ switch (err) { case FTPRERR: logputs (LOG_VERBOSE, "\n"); logputs (LOG_NOTQUIET, _("\Error in server response, closing control connection.\n")); fd_close (csock); con->csock = -1; fd_close (dtsock); fd_close (local_sock); return err; case WRITEFAILED: logputs (LOG_VERBOSE, "\n"); logputs (LOG_NOTQUIET, _("Write failed, closing control connection.\n")); fd_close (csock); con->csock = -1; fd_close (dtsock); fd_close (local_sock); return err; case FTPNSFOD: logputs (LOG_VERBOSE, "\n"); logprintf (LOG_NOTQUIET, _("No such file `%s'.\n\n"), escnonprint (u->file)); fd_close (dtsock); fd_close (local_sock); return err; case FTPOK: break; default: abort (); } if (!opt.server_response) logputs (LOG_VERBOSE, _("done.\n")); expected_bytes = ftp_expected_bytes (ftp_last_respline); } /* do retrieve */ if (cmd & DO_LIST) { if (!opt.server_response) logputs (LOG_VERBOSE, "==> LIST ... "); /* As Maciej W. Rozycki (macro@ds2.pg.gda.pl) says, `LIST' without arguments is better than `LIST .'; confirmed by RFC959. */ err = ftp_list (csock, NULL); /* FTPRERR, WRITEFAILED */ switch (err) { case FTPRERR: logputs (LOG_VERBOSE, "\n"); logputs (LOG_NOTQUIET, _("\Error in server response, closing control connection.\n")); fd_close (csock); con->csock = -1; fd_close (dtsock); fd_close (local_sock); return err; case WRITEFAILED: logputs (LOG_VERBOSE, "\n"); logputs (LOG_NOTQUIET, _("Write failed, closing control connection.\n")); fd_close (csock); con->csock = -1; fd_close (dtsock); fd_close (local_sock); return err; case FTPNSFOD: logputs (LOG_VERBOSE, "\n"); logprintf (LOG_NOTQUIET, _("No such file or directory `%s'.\n\n"), "."); fd_close (dtsock); fd_close (local_sock); return err; case FTPOK: break; default: abort (); } if (!opt.server_response) logputs (LOG_VERBOSE, _("done.\n")); expected_bytes = ftp_expected_bytes (ftp_last_respline); } /* cmd & DO_LIST */ if (!(cmd & (DO_LIST | DO_RETR)) || (opt.spider && !(cmd & DO_LIST))) return RETRFINISHED; /* Some FTP servers return the total length of file after REST command, others just return the remaining size. */ if (*len && restval && expected_bytes && (expected_bytes == *len - restval)) { DEBUGP (("Lying FTP server found, adjusting.\n")); expected_bytes = *len; } /* If no transmission was required, then everything is OK. */ if (!pasv_mode_open) /* we are not using pasive mode so we need to accept */ { /* Wait for the server to connect to the address we're waiting at. */ dtsock = accept_connection (local_sock); if (dtsock < 0) { logprintf (LOG_NOTQUIET, "accept: %s\n", strerror (errno)); return err; } } /* Open the file -- if output_stream is set, use it instead. */ if (!output_stream || con->cmd & DO_LIST) { mkalldirs (con->target); if (opt.backups) rotate_backups (con->target); if (restval) fp = fopen (con->target, "ab"); else if (opt.noclobber || opt.always_rest || opt.timestamping || opt.dirstruct || opt.output_document) fp = fopen (con->target, "wb"); else { fp = fopen_excl (con->target, true); if (!fp && errno == EEXIST) { /* We cannot just invent a new name and use it (which is what functions like unique_create typically do) because we told the user we'd use this name. Instead, return and retry the download. */ logprintf (LOG_NOTQUIET, _("%s has sprung into existence.\n"), con->target); fd_close (csock); con->csock = -1; fd_close (dtsock); fd_close (local_sock); return FOPEN_EXCL_ERR; } } if (!fp) { logprintf (LOG_NOTQUIET, "%s: %s\n", con->target, strerror (errno)); fd_close (csock); con->csock = -1; fd_close (dtsock); fd_close (local_sock); return FOPENERR; } } else fp = output_stream; if (*len)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -