changeset 1168:fbe6804a3f33 draft

Attached is an implementation for ftpget/put commands.
author Ashwini Sharma <ak.ashwini1981@gmail.com>
date Fri, 27 Dec 2013 08:30:32 -0600
parents 17387db4d979
children ab7f336bfe52
files toys/pending/ftpget.c
diffstat 1 files changed, 287 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/toys/pending/ftpget.c	Fri Dec 27 08:30:32 2013 -0600
@@ -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]);
+}