diff options
Diffstat (limited to 'toys')
-rw-r--r-- | toys/pending/ftpget.c | 287 |
1 files changed, 287 insertions, 0 deletions
diff --git a/toys/pending/ftpget.c b/toys/pending/ftpget.c new file mode 100644 index 00000000..bd44289e --- /dev/null +++ b/toys/pending/ftpget.c @@ -0,0 +1,287 @@ +/* ftpget.c - Get a remote file from FTP. + * + * Copyright 2013 Ranjan Kumar <ranjankumar.bth@gmail.com> + * Copyright 2013 Kyungwan Han <asura321@gmail.com> + * + * No Standard. + * +USE_FTPGET(NEWTOY(ftpget, "<2cvu:p:P:", TOYFLAG_BIN)) +USE_FTPGET(OLDTOY(ftpput,ftpget, "<2vu:p:P:", TOYFLAG_BIN)) + +config FTPGET + bool "ftpget/ftpput" + default n + help + usage: ftpget [-cv] [-u username -p password -P PortNumber] HOST_NAME [LOCAL_FILENAME] REMOTE_FILENAME + usage: ftpput [-v] [-u username -p password -P PortNumber] HOST_NAME [REMOTE_FILENAME] LOCAL_FILENAME + + ftpget - Get a remote file from FTP. + ftpput - Upload a local file on remote machine through FTP. + + -c Continue previous transfer. + -v Verbose. + -u User name. + -p Password. + -P Port Number. +*/ +#define FOR_ftpget +#include "toys.h" + +GLOBALS( + char *port; + char *password; + char *username; + + FILE *sockfp; + int c; + int isget; + char buf[sizeof(struct sockaddr_storage)]; +) + +#define DATACONNECTION_OPENED 125 +#define FTPFILE_STATUSOKAY 150 +#define FTP_COMMAND_OKAY 200 +#define FTPFILE_STATUS 213 +#define FTPSERVER_READY 220 +#define CLOSE_DATACONECTION 226 +#define PASSIVE_MODE 227 +#define USERLOGGED_SUCCESS 230 +#define PASSWORD_REQUEST 331 +#define REQUESTED_PENDINGACTION 350 + + +static void setport(unsigned port_num) +{ + int af = ((struct sockaddr *)TT.buf)->sa_family; + + if (af == AF_INET) ((struct sockaddr_in*)TT.buf)->sin_port = port_num; + else if (af == AF_INET6) ((struct sockaddr_in6*)TT.buf)->sin6_port = port_num; +} + +static int connect_to_stream() +{ + int sockfd, af = ((struct sockaddr *)TT.buf)->sa_family; + + sockfd = xsocket(af, SOCK_STREAM, 0); + if (connect(sockfd, (struct sockaddr*)TT.buf,((af == AF_INET)? + sizeof(struct sockaddr_in):sizeof(struct sockaddr_in6))) < 0) { + close(sockfd); + perror_exit("can't connect to remote host"); + } + return sockfd; +} + +//close ftp connection and print the message. +static void close_stream(char *msg_str) +{ + char *str = toybuf; //toybuf holds response data. + + //Remove garbage chars (from ' ' space to '\x7f') DEL remote server response. + while ((*str >= 0x20) && (*str < 0x7f)) str++; + *str = '\0'; + if (TT.sockfp) fclose(TT.sockfp); + error_exit("%s server response: %s", (msg_str) ? msg_str:"", toybuf); +} + +//send command to ftp and get return status. +static int get_ftp_response(char *command, char *param) +{ + unsigned cmd_status = 0; + char *fmt = "%s %s\r\n"; + + if (command) { + if (!param) fmt += 3; + fprintf(TT.sockfp, fmt, command, param); + fflush(TT.sockfp); + if (toys.optflags & FLAG_v) + fprintf(stderr, "FTP Request: %s %s\r\n", command, param); + } + + do { + if (!fgets(toybuf, sizeof(toybuf)-1, TT.sockfp)) close_stream(NULL); + } while (!isdigit(toybuf[0]) || toybuf[3] != ' '); + + toybuf[3] = '\0'; + cmd_status = atolx_range(toybuf, 0, INT_MAX); + toybuf[3] = ' '; + return cmd_status; +} + +static void send_requests(void) +{ + int cmd_status = 0; + + //FTP connection request. + if (get_ftp_response(NULL, NULL) != FTPSERVER_READY) close_stream(NULL); + + //230 User authenticated, password please; 331 Password request. + cmd_status = get_ftp_response("USER", TT.username); + if (cmd_status == PASSWORD_REQUEST) { //user logged in. Need Password. + if (get_ftp_response("PASS", TT.password) != USERLOGGED_SUCCESS) + close_stream("PASS"); + } else if (cmd_status == USERLOGGED_SUCCESS); //do nothing + else close_stream("USER"); + //200 Type Binary. Command okay. + if (get_ftp_response("TYPE I", NULL) != FTP_COMMAND_OKAY) + close_stream("TYPE I"); +} + +static void get_sockaddr(char *host) +{ + struct addrinfo hints, *result; + char *ptr; + int status, port; + + errno = 0; + port = strtoul(TT.port, &ptr, 10); + if (errno || port > 65535) error_exit("Invalid port, Range is [0-65535]"); + + memset(&hints, 0 , sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + status = getaddrinfo(host, TT.port, &hints, &result); + if (status) error_exit("bad address '%s' : %s", host, gai_strerror(status)); + + memcpy(TT.buf, result->ai_addr, result->ai_addrlen); + freeaddrinfo(result); +} + +// send commands to ftp fo PASV mode. +static void verify_pasv_mode(char *r_filename) +{ + char *pch; + unsigned portnum; + + //vsftpd reply like:- "227 Entering Passive Mode (125,19,39,117,43,39)". + if (get_ftp_response("PASV", NULL) != PASSIVE_MODE) close_stream("PASV"); + + //Response is "NNN <some text> (N1,N2,N3,N4,P1,P2) garbage. + //Server's IP is N1.N2.N3.N4 + //Server's port for data connection is P1*256+P2. + if ((pch = strrchr(toybuf, ')'))) *pch = '\0'; + if ((pch = strrchr(toybuf, ','))) *pch = '\0'; + portnum = atolx_range(pch + 1, 0, 255); + + if ((pch = strrchr(toybuf, ','))) *pch = '\0'; + portnum = portnum + (atolx_range(pch + 1, 0, 255) * 256); + setport(htons(portnum)); + + if (TT.isget && get_ftp_response("SIZE", r_filename) != FTPFILE_STATUS) + TT.c = 0; +} + +/* + * verify the local file presence. + * if present, get the size of the file. + */ +static void is_localfile_present(char *l_filename) +{ + struct stat sb; + + if (stat(l_filename, &sb) < 0) perror_exit("stat"); + //if local file present, then request for pending file action. + if (sb.st_size > 0) { + sprintf(toybuf, "REST %lu", (unsigned long) sb.st_size); + if (get_ftp_response(toybuf, NULL) != REQUESTED_PENDINGACTION) TT.c = 0; + } else TT.c = 0; +} + +static void transfer_file(int local_fd, int remote_fd) +{ + int len, rfd = (TT.isget)?remote_fd:local_fd, + wfd = (TT.isget)?local_fd:remote_fd; + + if (rfd < 0 || wfd < 0) error_exit("Error in file creation:"); + while ((len = xread(rfd, toybuf, sizeof(toybuf)))) xwrite(wfd, toybuf, len); +} + +static void get_file(char *l_filename, char *r_filename) +{ + int local_fd = -1, remote_fd; + + verify_pasv_mode(r_filename); + remote_fd = connect_to_stream(); //Connect to data socket. + + //if local file name will be '-' then local fd will be stdout. + if ((l_filename[0] == '-') && !l_filename[1]) { + local_fd = 1; //file descriptor will become stdout. + TT.c = 0; + } + + //if continue, check for local file existance. + if (TT.c) is_localfile_present(l_filename); + + //verify the remote file presence. + if (get_ftp_response("RETR", r_filename) > FTPFILE_STATUSOKAY) + close_stream("RETR"); + + //if local fd is not stdout, create a file descriptor. + if (local_fd == -1) { + int flags = O_WRONLY; + + flags |= (TT.c)? O_APPEND : (O_CREAT | O_TRUNC); + local_fd = xcreate((char *)l_filename, flags, 0666); + } + transfer_file(local_fd, remote_fd); + xclose(remote_fd); + xclose(local_fd); + if (get_ftp_response(NULL, NULL) != CLOSE_DATACONECTION) close_stream(NULL); + get_ftp_response("QUIT", NULL); + toys.exitval = EXIT_SUCCESS; +} + +static void put_file(char *r_filename, char *l_filename) +{ + int local_fd = 0, remote_fd; + unsigned cmd_status = 0; + + verify_pasv_mode(r_filename); + remote_fd = connect_to_stream(); //Connect to data socket. + + //open the local file for transfer. + if ((l_filename[0] != '-') || l_filename[1]) + local_fd = xcreate((char *)l_filename, O_RDONLY, 0666); + + //verify for the remote file status, Ok or Open: transfer File. + cmd_status = get_ftp_response("STOR", r_filename); + if ( (cmd_status == DATACONNECTION_OPENED) || + (cmd_status == FTPFILE_STATUSOKAY)) { + transfer_file(local_fd, remote_fd); + if (get_ftp_response(NULL, NULL) != CLOSE_DATACONECTION) close_stream(NULL); + get_ftp_response("QUIT", NULL); + toys.exitval = EXIT_SUCCESS; + } else { + toys.exitval = EXIT_FAILURE; + close_stream("STOR"); + } + xclose(remote_fd); + xclose(local_fd); +} + +void ftpget_main(void) +{ + char **argv = toys.optargs; //host name + file name. + + TT.isget = toys.which->name[3] == 'g'; + TT.c = 1; + //if user name is not specified. + if (!(toys.optflags & FLAG_u) && (toys.optflags & FLAG_p)) + error_exit("Missing username:"); + //if user name and password is not specified in command line. + if (!(toys.optflags & FLAG_u) && !(toys.optflags & FLAG_p)) + TT.username = TT.password ="anonymous"; + + if (!(toys.optflags & FLAG_P)) TT.port = "ftp"; //use default ftp port 21. + //if continue is not in the command line argument. + if (TT.isget && !(toys.optflags & FLAG_c)) TT.c = 0; + + if (toys.optflags & FLAG_v) fprintf(stderr, "Connecting to %s\n", argv[0]); + get_sockaddr(argv[0]); + + TT.sockfp = xfdopen(connect_to_stream(), "r+"); + send_requests(); + + if (TT.isget) get_file(argv[1], argv[2] ? argv[2] : argv[1]); + else put_file(argv[1], argv[2] ? argv[2] : argv[1]); +} |