📄 ftp.c
字号:
/* sitecopy, for managing remote web sites. Generic(ish) FTP routines. Copyright (C) 1998-99, Joe Orton <joe@orton.demon.co.uk> (except where otherwise indicated). 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of 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 this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. $Id: ftp.c,v 1.12.2.6 1999/08/19 10:26:07 joe Exp $*//* This contains an FTP client implementation. * It performs transparent connection management - it it dies, * it will be reconnected automagically. */#include <config.h>#include <sys/types.h>#include <sys/socket.h>#include <sys/stat.h>#include <netinet/in.h>#include <ctype.h>#include <errno.h>#include <fcntl.h>#include <stdio.h>#include <time.h>#ifdef HAVE_STRING_H#include <string.h>#endif#ifdef HAVE_STRINGS_H#include <strings.h>#endif #ifdef HAVE_STDLIB_H#include <stdlib.h>#endif /* HAVE_STDLIB_H */#ifdef HAVE_UNISTD_H#include <unistd.h>#endif /* HAVE_UNISTD_H */#ifndef HAVE_SNPRINTF#include "snprintf.h"#endif#include "common.h"#include "frontend.h"#include "ftp.h"#include "protocol.h"#include "socket.h"char ftp_error[BUFSIZ];bool ftp_use_passive = true; /* whether we use PASV mode or not *//* The address of the server for the DTP connection - * this goes here because it's got from ftp_read() rather than in a decent * manner */unsigned short ftp_dtp_port;struct in_addr ftp_dtp_addr;int ftp_dtp_socket;struct in_addr ftp_pi_addr;unsigned short ftp_pi_port;int ftp_pi_socket;bool ftp_connection; /* true when open *//* Reply codes - these are returned by internal functions, * ftp_exec and ftp_open mainly. */#define FTP_OK 0#define FTP_NEEDPASSWORD 1#define FTP_PASSIVE 2#define FTP_READY 3#define FTP_FILEMORE 4#define FTP_MODTIME 5#define FTP_SENT 6#define FTP_CLOSED 101#define FTP_FILEBAD 102#define FTP_CONNECT 992#define FTP_HELLO 993#define FTP_LOGIN 994#define FTP_BROKEN 995#define FTP_DENIED 996#define FTP_UNSUPPORTED 997#define FTP_NOPASSIVE 998#define FTP_ERROR 999/* time from MDTM response */time_t ftp_modtime;/* remember these... we may have to log in more than once. */char *ftp_username, *ftp_password;/* Stores the current transfer type is: * -1: Unknown * 0: Binary * 1: ASCII */int ftp_using_ascii;/* Opens the control connection if necessary. * Returns: * FTP_OK on success * FTP_CONNECT on failed socket connect * FTP_HELLO if the greeting message couldn't be read * FTP_LOGIN on failed login */int ftp_open( void );int ftp_close( void );int ftp_fetch_gettree( const char *startdir, struct proto_file_t **files );int ftp_fetch_walktree( const char *rotodir, struct proto_file_t *files );int ftp_login( void ); /* Performs the login procedure */int ftp_settype( bool ascii );int ftp_active_open(const char * );int ftp_data_open( const char * ); /* Opens the data connection */int ftp_data_close( void ); /* Closes the data connection */int ftp_connect_pasv( void );int ftp_read_pasv( const char * ); /* Used by ftp_response to get the remote IP */int ftp_read_mdtm( const char *response );int ftp_response( const char *, const int ); /* Gets the return code */int get_reply_code( const char * ); /* Used by ftp_response to get the actual code */int ftp_exec( const char * ); /* Executes given command, reads response */int ftp_read( void );/* Initializes the driver + connection. * Returns PROTO_OK on success, or PROTO_LOOKUP if the hostname * could not be resolved. */int ftp_init( const char *remote_root, struct proto_host_t *server, struct proto_host_t *proxy ) { ftp_pi_port = server->port; fe_connection( fe_namelookup ); if( host_lookup( server->hostname, &ftp_pi_addr ) ) return PROTO_LOOKUP; ftp_username = server->username; ftp_password = server->password; ftp_connection = false; ftp_using_ascii = -1; /* unknown */ switch( ftp_open() ) { case FTP_CONNECT: case FTP_HELLO: return PROTO_CONNECT; case FTP_LOGIN: return PROTO_AUTH; default: return PROTO_OK; }}/* Cleans up and closes the control connection. * Returns PROTO_OK if the connection is closed, else PROTO_ERROR; */int ftp_finish( void ) { if( ftp_connection ) if( ftp_close( ) != FTP_CLOSED ) return PROTO_ERROR; return PROTO_OK;}/* Closes the PI connection. */int ftp_close( ) { int ret; ret = ftp_exec( "QUIT" ); close( ftp_pi_socket ); ftp_connection = false; /* although it should have been done already */ return ret;}/* Creates the given directory * FTP state response */int ftp_mkdir( const char *dir ) { char command[BUFSIZ]; snprintf( command, BUFSIZ, "MKD %s", dir ); if( ftp_exec( command ) == FTP_OK ) { return PROTO_OK; } else { return PROTO_ERROR; }} /* Renames or moves a file */int ftp_move( const char *from, const char *to ) { char command[BUFSIZ]; snprintf( command, BUFSIZ, "RNFR %s", from ); if( ftp_exec( command ) == FTP_FILEMORE ) { snprintf( command, BUFSIZ, "RNTO %s", to ); if( ftp_exec( command ) == PROTO_OK ) { return PROTO_OK; } } return PROTO_ERROR;}int ftp_delete( const char *filename ) { char command[BUFSIZ]; snprintf( command, BUFSIZ, "DELE %s", filename ); if( ftp_exec( command ) == FTP_OK ) { return PROTO_OK; } else { return PROTO_ERROR; }}int ftp_rmdir( const char *filename ) { char command[BUFSIZ]; snprintf( command, BUFSIZ, "RMD %s", filename ); if( ftp_exec( command ) == FTP_OK ) { return PROTO_OK; } else { return PROTO_ERROR; }}/* FTP non-PASV mode open */int ftp_active_open(const char *command) { char *a, *p; int ret; ksize_t slen; int listener; char cmd[BUFSIZ]; struct sockaddr_in gotaddr; struct sockaddr_in localaddr; if( ftp_open( ) != FTP_OK ) return FTP_ERROR; slen = sizeof( localaddr ); if( getsockname( ftp_pi_socket, (struct sockaddr *)&localaddr, &slen ) < 0 ) { ftp_seterror_err( "Could not get socket name" ); return FTP_ERROR; } /* But let bind pick a random port for us to use */ localaddr.sin_port = 0; /* Create a local socket to accept the connection on */ listener = socket( AF_INET, SOCK_STREAM, 0 ); if ( listener < 0 ) { ftp_seterror_err( "Could not create socket" ); return FTP_ERROR; } /* Bind it to a port. */ if ( bind(listener, (struct sockaddr *)&localaddr, sizeof(struct sockaddr_in)) < 0 ) { ftp_seterror_err( "Could not bind socket." ); close( listener ); return FTP_ERROR; } slen = sizeof( struct sockaddr_in ); if (getsockname(listener, (struct sockaddr *)&gotaddr, &slen) < 0) { ftp_seterror_err( "Could get get name of socket" ); close( listener ); return FTP_ERROR; } if (listen(listener, 1) < 0) { ftp_seterror_err( "Could not listen for connection" ); close( listener ); return FTP_ERROR; } /* Let the remote end know the address of our port. * Q(joe): Will this actually work on differently-endian machines? */#define UC(b) (((int)b)&0xff) a = (char *)&gotaddr.sin_addr.s_addr; p = (char *)&gotaddr.sin_port; snprintf(cmd, BUFSIZ, "PORT %d,%d,%d,%d,%d,%d", UC(a[0]), UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]) ); /* Execute the PORT command */ ret = ftp_exec(cmd); if( ret != FTP_OK ) { /* Failed to execute the PORT command - close the socket */ DEBUG( DEBUG_FTP, "PORT command failed.\n" ); close( listener ); return ret; } /* Send the command. This will make the remote end * initiate the connection. */ ret = ftp_exec(command); if( ret != FTP_READY ) { /* Do they want it? */ DEBUG( DEBUG_FTP, "Command failed.\n" ); close( listener ); return ret; } /* Now wait for a connection from the remote end. */ ftp_dtp_socket = accept( listener, NULL, NULL ); if (ftp_dtp_socket < 0) { close( listener ); ftp_seterror_err( "Could not accept connection" ); return FTP_ERROR; } close( listener ); return ret;}int ftp_chmod( const char *filename, const mode_t mode ) { char command[BUFSIZ]; snprintf( command, BUFSIZ, "SITE CHMOD %3o %s", mode & 0777, filename ); if( ftp_exec( command ) == FTP_OK ) { return PROTO_OK; } else { return PROTO_ERROR; }}int ftp_data_open( const char *command ) { int ret; if( ftp_use_passive ) { ret = ftp_exec( "PASV" ); if( ret == FTP_PASSIVE ) { if( ftp_connect_pasv( ) ) { return ftp_exec( command ); } else { return FTP_ERROR; } } else { return FTP_NOPASSIVE; } } else { /* we are not using passive mode. */ return ftp_active_open(command); }}/* Closes the data connection */int ftp_data_close( ) { /* Read the response line */ if( close( ftp_dtp_socket ) < 0 ) { ftp_seterror_err( "Error closing data socket" ); return FTP_ERROR; } else { return ftp_read(); }}/* Set the transfer type appropriately. * Only set it if it *needs* setting, though. * Returns FTP_OK on success, or something else otherwise. */int ftp_settype( bool ascii ) { int newascii, ret; newascii = ascii?1:0; if( (ftp_using_ascii == -1) || (newascii != ftp_using_ascii ) ) { ret = ftp_exec( ascii?"TYPE A":"TYPE I" ); if( ret == FTP_OK ) { ftp_using_ascii = newascii; } else { ftp_using_ascii = -1; /* unknown */ } } else { ret = FTP_OK; } return ret;}/* upload the given file */int ftp_put( const char *localfile, const char *remotefile, const bool ascii ) { char command[BUFSIZ]; /* Set the transfer type correctly */ if( ftp_settype( ascii ) != FTP_OK ) return PROTO_ERROR; snprintf( command, BUFSIZ, "STOR %s", remotefile ); if( ftp_data_open( command ) == FTP_READY ) { int ret; /* Send the file. FIXME: We rely on the server's return * code to determine whether the transfer was successful. */ if( ascii ) { ret = send_file_ascii( ftp_dtp_socket, localfile ); } else { ret = send_file_binary( ftp_dtp_socket, localfile ); } if( ret > 0 ) { DEBUG( DEBUG_FTP, "Error sending file... ignoring.\n" ); } if( ftp_data_close( ) == FTP_SENT ) { return PROTO_OK; } } return PROTO_ERROR;}/* Slightly dodgy ftp_get in that you need to know the remote file * size. Works, but not very generic. */int ftp_get( const char *localfile, const char *remotefile, const int remotesize, const bool ascii ) { char command[BUFSIZ]; int ret; if(ascii) strcpy( command, "TYPE A" ); else strcpy( command, "TYPE I"); ret = ftp_exec( command ); snprintf( command, BUFSIZ, "RETR %s", remotefile ); if( ftp_data_open( command ) == FTP_READY ) { /* send the file */ recv_file( ftp_dtp_socket, localfile, remotesize ); if( ftp_data_close( ) == FTP_SENT ) { return PROTO_OK; } } return PROTO_ERROR;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -