📄 ftp.c.orig
字号:
/* $Id: ftp.c,v 1.37 2004/05/20 11:10:52 mhe Exp $ * * ftp.c -- low(er) level FTP stuff * * Yet Another FTP Client * Copyright (C) 1998-2001, Martin Hedenfalk <mhe@stacken.kth.se> * * This program 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 * (at your option) any later version. See COPYING for more details. */#include "syshdr.h"#include "ftp.h"#include "xmalloc.h"#include "strq.h"#include "gvars.h"#include "ssh_cmd.h"#include "sftp-common.h"#include "args.h"/* in cmd.c */void exit_yafc(void);/* in get.c */char *make_unique_filename(const char *path);/* in tag.c */void save_taglist(const char *alt_filename);/* in bookmark.c */void auto_create_bookmark(void);Ftp *ftp = 0;Ftp *ftp_create(void){ Ftp *ftp; ftp = (Ftp *)xmalloc(sizeof(Ftp)); ftp->verbosity = vbCommand; ftp->tmp_verbosity = vbUnset; ftp->last_mkpath = 0; ftp->cache = list_new((listfunc)rdir_destroy); ftp->dirs_to_flush = list_new((listfunc)free); ftp->reply_timeout = 30; ftp->open_timeout = 30; ftp->taglist = list_new((listfunc)rfile_destroy);#ifdef SECFTP ftp->app_data = 0;/* ftp->in_buffer = 0;*//* ftp->out_buffer = 0;*/#endif ftp->LIST_type = ltUnknown; ftp->ssh_id = 1; ftp->ssh_args = args_create(); return ftp;}int ftp_set_trace(const char *filename){ if(gvLogfp) /* already opened */ return 0; gvLogfp = fopen(filename, "w"); if(gvLogfp) { time_t now = time(0); setbuf(gvLogfp, 0); /* change buffering */ fprintf(gvLogfp, "yafc " VERSION " trace file started %s\n", ctime(&now)); return 0; } return -1;}void ftp_vtrace(const char *fmt, va_list ap){ if(gvLogfp) vfprintf(gvLogfp, fmt, ap);}void ftp_trace(const char *fmt, ...){ va_list ap; va_start(ap, fmt); ftp_vtrace(fmt, ap); va_end(ap);}void ftp_err(const char *fmt, ...){ va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); va_start(ap, fmt); ftp_vtrace(fmt, ap); va_end(ap);}void ftp_use(Ftp *useftp){ ftp = useftp;}void ftp_destroy(Ftp *ftp){ if(!ftp) return; list_free(ftp->dirs_to_flush); list_free(ftp->cache); ftp->cache = ftp->dirs_to_flush = 0; host_destroy(ftp->host); sock_destroy(ftp->data); sock_destroy(ftp->ctrl); ftp->host = 0; ftp->data = ftp->ctrl = 0; url_destroy(ftp->url); ftp->url = 0; free(ftp->homedir); free(ftp->curdir); free(ftp->prevdir); list_free(ftp->taglist); args_destroy(ftp->ssh_args); ftp->ssh_args = 0;#ifdef SECFTP sec_end();#endif free(ftp);}static int proxy_type(url_t *url){ listitem *li; char *url_domain; if(gvProxyType == 0 || url->noproxy == true || gvProxyUrl == 0) return 0; url_domain = strrchr(url->hostname, '.'); for(li=gvProxyExclude->first; li; li=li->next) { char *xhost = (char *)li->data; if(!xhost) /* should not happen */ continue; if(xhost[0] == '.' && url_domain) { if(strcasecmp(url_domain, xhost) == 0) /* exclude domain names */ return 0; } if(strcasecmp(xhost, "localnet") == 0 && url_domain == 0) /* exclude unqualified hosts */ return 0; if(strcasecmp(xhost, url->hostname) == 0) /* exclude hostnames */ return 0; } return gvProxyType;}void ftp_reset_vars(void){ sock_destroy(ftp->data); ftp->data = 0; sock_destroy(ftp->ctrl); ftp->ctrl = 0; host_destroy(ftp->host); ftp->host = 0; if(ftp->ssh_pid) { ftp->ssh_pid = 0; close(ftp->ssh_in); close(ftp->ssh_out); ftp->ssh_id = 0; } args_clear(ftp->ssh_args); url_destroy(ftp->url); ftp->url = 0; ftp->connected = false; ftp->loggedin = false; ftp->has_mdtm_command = true; ftp->has_size_command = true; ftp->has_pasv_command = true; ftp->has_stou_command = true; ftp->has_site_chmod_command = true; ftp->has_site_idle_command = true; ftp->has_mlsd_command = true; list_free(ftp->dirs_to_flush); ftp->dirs_to_flush = list_new((listfunc)free); list_free(ftp->cache); ftp->cache = list_new((listfunc)rdir_destroy); /* don't assume server is in ascii mode initially even if RFC says so */ ftp->prev_type = '?'; ftp->code = ctNone; ftp->fullcode = 0; ftp->reply_timeout = gvCommandTimeout; free(ftp->last_mkpath); ftp->last_mkpath = 0;#ifdef SECFTP sec_end(); ftp->request_data_prot = 0; ftp->buffer_size = 0;#endif ftp->LIST_type = ltUnknown; list_free(ftp->taglist); ftp->taglist = list_new((listfunc)rfile_destroy);}void ftp_close(void){ ftp_trace("Closing down connection...\n"); auto_create_bookmark(); if(gvLoadTaglist != 0) { save_taglist(0); } ftp_reset_vars();}void ftp_quit_all(void){ listitem *li; /* nicely close all open connections */ for(li=gvFtpList->first; li; li=li->next) { ftp_use((Ftp *)li->data); ftp_quit(); }}#ifdef HAVE_POSIX_SIGSETJMPstatic sigjmp_buf open_timeout_jmp;#elsestatic jmp_buf open_timeout_jmp;#endifstatic RETSIGTYPE ftp_open_handler(int signum){ ftp_longjmp(open_timeout_jmp, 1);}int ftp_open_url(url_t *urlp, bool reset_vars){ bool use_proxy; int i; if(reset_vars) ftp_reset_vars(); /* don't assume server is in ascii mode initially even if RFC says so */ ftp->prev_type = '?';#ifdef HAVE_POSIX_SIGSETJMP if(sigsetjmp(open_timeout_jmp, 1))#else if(setjmp(open_timeout_jmp))#endif { ftp_close(); ftp_err(_("Connection timeout after %u seconds\n"), ftp->open_timeout); return 1; } ftp_set_signal(SIGALRM, ftp_open_handler); alarm(ftp->open_timeout); use_proxy = (proxy_type(urlp) != 0); ftp_err(_("Looking up %s... "), use_proxy ? gvProxyUrl->hostname : urlp->hostname); /* Set the default port (22) for SSH if no port is specified. We * need to do this here, 'cause host_lookup() sets it to 21 * (default port for vanilla FTP) */ if(urlp->protocol) { if(strcmp(urlp->protocol, "sftp") == 0) url_setprotocol(urlp, "ssh"); if(strcmp(urlp->protocol, "ssh") == 0 && urlp->port == -1) urlp->port = 22; /* default SSH port */ } ftp->host = host_create(use_proxy ? gvProxyUrl : urlp); if(host_lookup(ftp->host) != 0) { herror(ftp->host->hostname); alarm(0); ftp_set_signal(SIGALRM, SIG_IGN); return -1; } urlp->port = ntohs(ftp->host->port); fprintf(stderr, "\r "); i = strlen(use_proxy ? gvProxyUrl->hostname : urlp->hostname); while(i--) fprintf(stderr, " "); fprintf(stderr, "\r"); ftp_trace("\n"); if(urlp->protocol && strcmp(urlp->protocol, "ssh") == 0) { int ret = ssh_open_url(urlp); alarm(0); return ret; } if(urlp->protocol && strcmp(urlp->protocol, "ftp") != 0) { ftp_err(_("Sorry, don't know how to handle your '%s' protocol\n" "trying 'ftp' instead...\n"), urlp->protocol); url_setprotocol(urlp, 0); } if(use_proxy) { ftp_err(_("Connecting to proxy %s (%s) at port %d...\n"), ftp->host->ohostname, ftp->host->ipnum, urlp->port); } else { ftp_err(_("Connecting to %s (%s) at port %d...\n"), ftp->host->ohostname, ftp->host->ipnum, urlp->port); } ftp->ctrl = sock_create(); if(sock_connect_host(ftp->ctrl, ftp->host) == -1) { alarm(0); ftp_set_signal(SIGALRM, SIG_IGN); return -1; } sock_lowdelay(ftp->ctrl); /* read startup message from server */ ftp_set_tmp_verbosity(vbCommand); ftp_read_reply(); if(ftp->fullcode == 120) { ftp_set_tmp_verbosity(vbCommand); ftp_read_reply(); } alarm(0); ftp_set_signal(SIGALRM, SIG_IGN); if(!sock_connected(ftp->ctrl)) { ftp_close(); return 1; } ftp->connected = (ftp->fullcode == 220); if(ftp->connected) { void (*tracefunq)(const char *fmt, ...); unsigned char *a; url_destroy(ftp->url); ftp->url = url_clone(urlp); tracefunq = (ftp->verbosity == vbDebug ? ftp_err : ftp_trace); a = (unsigned char *)&ftp->ctrl->remote_addr.sin_addr; tracefunq("remote address: %d.%d.%d.%d\n", a[0], a[1], a[2], a[3]); a = (unsigned char *)&ftp->ctrl->local_addr.sin_addr; tracefunq("local address: %d.%d.%d.%d\n", a[0], a[1], a[2], a[3]); return 0; } else { ftp_close(); return 1; }}int ftp_open(const char *host){ url_t *url; url = url_init(host); return ftp_open_url(ftp->url, true);}int ftp_reopen(void){ if(ftp && ftp->url) { url_t *u = url_clone(ftp->url); int r; url_setdirectory(u, ftp->curdir); r = ftp_open_url(u, false); if(r == 0) { r = ftp_login(u->username, gvAnonPasswd); } else ftp_close(); url_destroy(u); return r; } return -1;}/* reads one line from server into ftp->reply * returns 0 on success or -1 on failure */static int ftp_gets(void){ int c, i=0; ftp->reply[0] = 0; if(!sock_connected(ftp->ctrl)) { ftp_err(_("No control connection\n")); return -1; } while(true) { c = sock_get(ftp->ctrl); if(c == EOF) { ftp_err(_("Server has closed control connection\n")); ftp_close(); return -1; } else if(c == 255/*IAC*/) { /* handle telnet commands */ switch(c = sock_get(ftp->ctrl)) { case 251/*WILL*/: case 252/*WONT*/: c = sock_get(ftp->ctrl); sock_printf(ftp->ctrl, "%c%c%c", 255/*IAC*/, 254/*DONT*/, c); sock_flush(ftp->ctrl); break; case 253/*DO*/: case 254/*DONT*/: c = sock_get(ftp->ctrl); sock_printf(ftp->ctrl, "%c%c%c", 255/*IAC*/, 252/*WONT*/, c); sock_flush(ftp->ctrl); break; default: break; } continue; } else if(c == '\r') { c = sock_get(ftp->ctrl); if(c == 0) c = '\r'; else if(c == '\n') { ftp->reply[i++] = (char)c; break; } else if(c == EOF) /* do nothing */ ; else { /* telnet protocol violation, hmpf... */ sock_unget(ftp->ctrl, c); continue; } } else if(c == '\n') break; if(i < MAXREPLY) ftp->reply[i++] = (char)c; } if(i >= MAXREPLY) { ftp_err(_("Reply too long (truncated)\n")); i = MAXREPLY; } ftp->reply[i] = '\0'; ftp->fullcode = atoi(ftp->reply);#ifdef SECFTP { int r = 0; switch(ftp->fullcode) { /* handle protected responses 6xx */ case 631: r = sec_read_msg(ftp->reply, prot_safe); break; case 632: r = sec_read_msg(ftp->reply, prot_private); break; case 633: r = sec_read_msg(ftp->reply, prot_confidential); break; } if(r == -1) { ftp->fullcode = 0; ftp->code = vbNone; return 0; } else ftp->fullcode = atoi(ftp->reply); }#endif strip_trailing_chars(ftp->reply, "\n\r"); ftp->code = ftp->fullcode / 100; return ftp->fullcode;}const char *ftp_getreply(bool withcode){ char *r = ftp->reply; if(ftp->ssh_pid) return fx2txt(ftp->ssh_last_status); if(withcode) return r; if(isdigit((int)r[0])) r += 3; if(r[0] == '-' || r[0] == ' ') r++; return r;}static void ftp_print_reply(void){ verbose_t v = ftp_get_verbosity(); ftp_trace("<-- [%s] %s\n", ftp->url ? ftp->url->hostname : ftp->host->hostname, ftp_getreply(true)); if(v >= vbCommand || (ftp->code >= ctTransient && v == vbError)) { if(v == vbDebug) fprintf(stderr, "<-- [%s] %s\n", ftp->url ? ftp->url->hostname : ftp->host->hostname, ftp_getreply(true)); else fprintf(stderr, "%s\n", ftp_getreply(false)); }}static RETSIGTYPE reply_ALRM_handler(int signum){ ftp_err(_("Tired of waiting for reply, timeout after %u seconds\n"), ftp->reply_timeout); ftp_close(); if(gvJmpBufSet) { ftp_trace("jumping to gvRestartJmp\n"); ftp_longjmp(gvRestartJmp, 1); } else exit_yafc();}/* reads reply * returns 0 on success or -1 on error */int ftp_read_reply(void){ char tmp[5]="xxx "; int r; ftp_set_signal(SIGALRM, reply_ALRM_handler); if(ftp->reply_timeout) alarm(ftp->reply_timeout); clearerr(ftp->ctrl->sin); r = ftp_gets(); if(!sock_connected(ftp->ctrl)) { alarm(0); ftp_set_signal(SIGALRM, SIG_DFL); return -1; } if(r == -1) { alarm(0); ftp_set_signal(SIGALRM, SIG_DFL); ftp_trace("ftp_gets returned -1\n"); return -1; } ftp_print_reply(); if(ftp->reply[3] == '-') { /* multiline response */ strncpy(tmp, ftp->reply, 3); do { if(ftp_gets() == -1) break; ftp_print_reply(); } while(strncmp(tmp, ftp->reply, 4) != 0); } ftp->tmp_verbosity = vbUnset; alarm(0); ftp_set_signal(SIGALRM, SIG_DFL); return r;}static void ftp_print_cmd(const char *cmd, va_list ap){ if(ftp_get_verbosity() == vbDebug) { ftp_err("--> [%s] ", ftp->url->hostname); if(strncmp(cmd, "PASS", 4) == 0) ftp_err("PASS ********"); else { vfprintf(stderr, cmd, ap); ftp_vtrace(cmd, ap);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -