diff packages/net/ftpclient/current/src/ftpclient.c @ 196:9b55e9c69693

Merge from eCos master repository on 2001-11-23-15:32:03-GMT
author jlarmour
date Fri, 23 Nov 2001 17:12:52 +0000
parents
children ea3f0bd33a73
line wrap: on
line diff
new file mode 100644
--- /dev/null
+++ b/packages/net/ftpclient/current/src/ftpclient.c
@@ -0,0 +1,679 @@
+//==========================================================================
+//
+//      ftpclient.c
+//
+//      A simple FTP client
+//
+//==========================================================================
+//####COPYRIGHTBEGIN####
+//                                                                          
+// -------------------------------------------                              
+// The contents of this file are subject to the Red Hat eCos Public License 
+// Version 1.1 (the "License"); you may not use this file except in         
+// compliance with the License.  You may obtain a copy of the License at    
+// http://www.redhat.com/                                                   
+//                                                                          
+// Software distributed under the License is distributed on an "AS IS"      
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the 
+// License for the specific language governing rights and limitations under 
+// the License.                                                             
+//                                                                          
+// The Original Code is eCos - Embedded Configurable Operating System,      
+// released September 30, 1998.                                             
+//                                                                          
+// The Initial Developer of the Original Code is Red Hat.                   
+// Portions created by Red Hat are                                          
+// Copyright (C) 1998, 1999, 2000, 2001 Red Hat, Inc.
+// All Rights Reserved.                                                     
+// -------------------------------------------                              
+//                                                                          
+//####COPYRIGHTEND####
+//==========================================================================
+//#####DESCRIPTIONBEGIN####
+//
+// Author(s):    andrew.lunn@ascom.ch
+// Contributors: andrew.lunn@ascom.ch
+// Date:         2001-11-4
+// Purpose:      
+// Description:  
+//              
+//####DESCRIPTIONEND####
+//
+//==========================================================================
+
+#include <network.h>
+#include <stdio.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <ctype.h>
+#include "ftpclient.h"
+
+/* Build one command to send to the FTP server */
+static int 
+build_cmd(char *buf,
+          unsigned bufsize,
+          char *cmd, 
+          char *arg1)
+{
+  int cnt;
+  
+  if (arg1) {
+    cnt = snprintf(buf,bufsize,"%s %s\r\n",cmd,arg1);
+  } else {
+    cnt = snprintf(buf,bufsize,"%s\r\n",cmd);
+  }
+  
+  if (cnt < bufsize) 
+    return 1;
+  
+  return 0;
+}
+
+
+/* Read one line from the server, being careful not to overrun the
+   buffer. If we do reach the end of the buffer, discard the rest of
+   the line. */
+static int 
+get_line(int s, char *buf, unsigned buf_size,ftp_printf_t ftp_printf) {
+  
+  int eol = 0;
+  int cnt = 0;
+  int ret;
+  char c;
+  
+  while(!eol) {
+    ret = read(s,&c,1);
+    
+    if (ret != 1) {
+      ftp_printf(1,"read %s\n",strerror(errno));
+      return FTP_BAD;
+    }
+    
+    if (c == '\n') {
+      eol = 1;
+      continue;
+    }
+
+    if (cnt < buf_size) {
+      buf[cnt++] = c;
+    }   
+  }
+  if (cnt < buf_size) {
+    buf[cnt++] = '\0';
+  } else {
+    buf[buf_size -1] = '\0';
+  }
+  return 0;
+}  
+
+/* Read the reply from the server and return the MSB from the return
+   code. This gives us a basic idea if the command failed/worked. The
+   reply can be spread over multiple lines. When this happens the line
+   will start with a - to indicate there is more*/
+static int 
+get_reply(int s,ftp_printf_t ftp_printf) {
+
+  char buf[BUFSIZ];
+  int more = 0;
+  int ret;
+
+  do {
+    
+    if ((ret=get_line(s,buf,sizeof(buf),ftp_printf)) < 0) {
+      return(ret);
+    }
+    
+    ftp_printf(0,"FTP: %s\n",buf);
+    
+    more = (buf[3] == '-'); 
+  } while (more);
+
+  return (buf[0] - '0');
+}
+
+/* Send a command to the server */
+static int 
+send_cmd(int s,char * msgbuf,ftp_printf_t ftp_printf) {
+  
+  int len;
+  int slen = strlen(msgbuf);
+  
+  if ((len = write(s,msgbuf,slen)) != slen) {
+    if (slen < 0) {
+      ftp_printf(1,"write %s\n",strerror(errno));
+      return FTP_BAD;
+    } else {
+      ftp_printf(1,"write truncasted!\n");
+      return FTP_BAD;
+    }
+  }
+  return 1;
+}
+
+/* Send a complete command to the server and receive the reply. Return the 
+   MSB of the reply code. */
+static int 
+command(char * cmd, 
+        char * arg, 
+        int s, 
+        char *msgbuf, 
+        int msgbuflen,
+        ftp_printf_t ftp_printf) {
+
+  int err;
+  
+  if (!build_cmd(msgbuf,msgbuflen,cmd,arg)) {
+    ftp_printf(1,"FTP: %s command to long\n",cmd);
+    return FTP_BAD;
+  }
+
+  ftp_printf(0,"FTP: Sending %s command\n",cmd);
+  
+  if ((err=send_cmd(s,msgbuf,ftp_printf)) < 0) {
+    return(err);
+  }
+
+  return (get_reply(s,ftp_printf));
+}
+
+/* Open a socket and connect it to the server. Also print out the
+   address of the server for debug purposes.*/
+
+static int
+connect_to_server(char *hostname, 
+                  struct sockaddr_in * local,
+                  ftp_printf_t ftp_printf) 
+{ 
+  struct sockaddr_in host; 
+  struct servent *sent; 
+  struct hostent *hp; 
+  int s, len;
+
+  s = socket(AF_INET, SOCK_STREAM, 0);
+  if (s < 0) {
+    ftp_printf(1,"socket: %s\n",strerror(errno));
+    return FTP_BAD;
+  }
+  
+  sent = getservbyname("ftp", "tcp");
+  if (sent == (struct servent *)0) {
+    ftp_printf(1,"FTP: unknown serivice\n");
+    close(s);
+    return FTP_BAD;
+  }
+  
+  hp = gethostbyname(hostname);
+  
+  if (hp) {           /* try name first */
+    host.sin_family = hp->h_addrtype;
+    bcopy(hp->h_addr, &host.sin_addr, hp->h_length);
+  } else {			/* maybe it's a numeric address ?*/
+    host.sin_family = AF_INET;
+    
+    if (inet_aton(hostname,&host.sin_addr) == 0)  { 
+      ftp_printf(1,"host not found: %s\n", hostname);
+      close (s);
+      return FTP_NOSUCHHOST;
+    }
+  }
+  
+  host.sin_port = sent->s_port;
+  
+  if (connect(s, (struct sockaddr *)&host, sizeof(host)) < 0) {
+    ftp_printf(1,"FTP Connect failed: %s\n",strerror(errno));
+    close (s);
+    return FTP_NOSUCHHOST;
+  }
+  
+  len = sizeof(struct sockaddr_in);
+  if (getsockname(s, (struct sockaddr *)local, &len) < 0) {
+    ftp_printf(1,"getsockname failed %s\n",strerror(errno));
+    close(s);
+    return FTP_BAD;
+  }
+  ftp_printf(0,"FTP: Connected to %s.%d\n",
+             inet_ntoa(host.sin_addr), ntohs(host.sin_port));
+  
+  return (s);
+}
+
+/* Perform a login to the server. Pass the username and password and
+   put the connection into binary mode. This assumes a passwd is
+   always needed. Is this true? */
+
+static int 
+login(char * username, 
+      char *passwd, 
+      int s, 
+      char *msgbuf, 
+      unsigned msgbuflen,
+      ftp_printf_t ftp_printf) {
+  
+  int ret;
+
+  ret = command("USER",username,s,msgbuf,msgbuflen,ftp_printf);
+  if (ret != 3) {
+    ftp_printf(1,"FTP: User %s not accepted\n",username);
+    return (FTP_BADUSER);
+  }
+  
+  ret = command("PASS",passwd,s,msgbuf,msgbuflen,ftp_printf);
+  if (ret < 0) {
+    return (ret);
+  }
+  if (ret != 2) {
+    ftp_printf(1,"FTP: Login failed for User %s\n",username);
+    return (FTP_BADUSER);
+  }
+  
+  ftp_printf(0,"FTP: Login sucessfull\n");
+  
+  ret = command("TYPE","I",s,msgbuf,msgbuflen,ftp_printf);
+  if (ret < 0) {
+    return (ret);
+  }
+  if (ret != 2) {
+    ftp_printf(1,"FTP: TYPE failed!\n");
+    return (FTP_BAD);
+  }
+  return (ret);
+}
+
+
+/* Open a data socket. This is a client socket, i.e. its listening
+waiting for the FTP server to connect to it. Once the socket has been
+opened send the port command to the server so the server knows which
+port we are listening on.*/
+static int 
+opendatasock(int ctrl_s,
+             struct sockaddr_in ctrl, 
+             char *msgbuf, 
+             unsigned msgbuflen,
+             ftp_printf_t ftp_printf) {
+
+  struct sockaddr_in local;
+  socklen_t len;
+  int on = 1;
+  char buf[4*6+1];
+  char *a, *p;
+  int ret;
+  int s;
+
+  s = socket(AF_INET, SOCK_STREAM, 0);
+  if (s < 0) {
+    ftp_printf(1,"socket: %s\n",strerror(errno));
+    return FTP_BAD;
+  }
+  
+  if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof (on)) < 0) {
+    ftp_printf(1,"setsockopt: %s\n",strerror(errno));
+    close(s);
+    return FTP_BAD;
+  }
+
+  local = ctrl;
+  local.sin_family = AF_INET;
+  local.sin_port = 0;
+  if (bind(s,(struct sockaddr *)&local,sizeof(local)) < 0) {
+    ftp_printf(1,"bind: %s\n",strerror(errno));
+    close(s);
+    return FTP_BAD;   
+  }
+  
+  len = sizeof(local);
+  if (getsockname(s,(struct sockaddr *)&local,&len) < 0) {
+    ftp_printf(1,"getsockname: %s\n",strerror(errno));
+    close(s);
+    return FTP_BAD;   
+  }
+  
+  if (listen(s, 1) < 0) {
+    ftp_printf(1,"listen: %s\n",strerror(errno));
+    close(s);
+    return FTP_BAD;   
+  }
+
+#define	BtoI(b)	(((int)b)&0xff)
+  a = (char *)&local.sin_addr;
+  p = (char *)&local.sin_port;
+  sprintf(buf,"%d,%d,%d,%d,%d,%d",
+          BtoI(a[0]),BtoI(a[1]),BtoI(a[2]),BtoI(a[3]),
+          BtoI(p[0]),BtoI(p[1]));
+
+  ret = command("PORT",buf,ctrl_s,msgbuf,msgbuflen,ftp_printf);
+  if (ret < 0) {
+    close(s);
+    return (ret);
+  }
+
+  if (ret != 2) {
+    ftp_printf(1,"FTP: PORT failed!\n");
+    close(s);
+    return (FTP_BAD);
+  }
+  return (s);
+}
+
+/* Receive the file into the buffer and close the data socket
+   afterwards */
+static int 
+receive_file(int data_s, char *buf, int buf_size,ftp_printf_t ftp_printf)
+{
+  int remaining = buf_size;
+  int finished = 0;
+  int total_size=0;
+  char *bufp = buf;
+  int len;
+  int s;
+  
+  s = accept(data_s,NULL,0);
+  if (s<0) {
+    ftp_printf(1,"listen: %s\n",strerror(errno));
+    return FTP_BAD;   
+  }
+  
+  do {
+    len = read(s,bufp,remaining);
+    if (len < 0) {
+      ftp_printf(1,"read: %s\n",strerror(errno));
+      close(s);
+      return FTP_BAD;   
+    }
+
+    if (len == 0) {
+      finished = 1;
+    } else {
+      total_size += len;
+      remaining -= len;
+      bufp += len;
+    
+      if (total_size == buf_size) {
+        ftp_printf(1,"FTP: File too big!\n");
+        close(s);
+        return FTP_TOOBIG;
+      }
+    }
+  } while (!finished);
+  
+  close(s);
+  return total_size;
+}
+
+/* Receive the file into the buffer and close the socket afterwards*/
+static int 
+send_file(int data_s, char *buf, int buf_size,ftp_printf_t ftp_printf)
+{
+  int remaining=buf_size;
+  int finished = 0;
+  char * bufp = buf;
+  int len;
+  int s;
+  
+  s = accept(data_s,NULL,0);
+  if (s<0) {
+    ftp_printf(1,"listen: %s\n",strerror(errno));
+    return FTP_BAD;   
+  }
+  
+  do { 
+    len = write(s,bufp,remaining);
+    if (len < 0) {
+      ftp_printf(1,"write: %s\n",strerror(errno));
+      close(s);
+      return FTP_BAD;   
+    }
+    
+    if (len == remaining) {
+      finished = 1;
+    } else {
+      remaining -= len;
+      bufp += len;
+    }
+  } while (!finished);
+  
+  close(s);
+  return 0;
+}
+
+/* All done, say bye, bye */
+static int quit(int s, 
+                char *msgbuf, 
+                unsigned msgbuflen,
+                ftp_printf_t ftp_printf) {
+  
+  int ret;
+  
+  ret = command("QUIT","",s,msgbuf,msgbuflen,ftp_printf);
+  if (ret < 0) {
+    return (ret);
+  }
+  if (ret != 2) {
+    ftp_printf(1,"FTP: Quit failed!\n");
+    return (FTP_BAD);
+  }
+  
+  ftp_printf(0,"FTP: Connection closed\n");
+  return (0);
+}
+
+/* Get a file from an FTP server. Hostname is the name/IP address of
+   the server. username is the username used to connect to the server
+   with. Passwd is the password used to authentificate the
+   username. filename is the name of the file to receive. It should be
+   the full pathname of the file. buf is a pointer to a buffer the
+   contents of the file should be placed in and buf_size is the size
+   of the buffer. If the file is bigger than the buffer, buf_size
+   bytes will be retrieved and an error code returned. ftp_printf is a
+   function to be called to perform printing. On success the number of
+   bytes received is returned. On error a negative value is returned
+   indicating the type of error. */
+
+int ftp_get(char * hostname, 
+            char * username, 
+            char * passwd, 
+            char * filename, 
+            char * buf, 
+            unsigned buf_size,
+            ftp_printf_t ftp_printf)
+{
+
+  struct sockaddr_in local;
+  char msgbuf[256];
+  int s,data_s;
+  int bytes;
+  int ret;
+  
+  s = connect_to_server(hostname,&local,ftp_printf);
+  if (s < 0) {
+    return (s);
+  }
+  
+  /* Read the welcome message from the server */
+  if (get_reply(s,ftp_printf) != 2) {
+    ftp_printf(0,"FTP: Server refused connection\n");
+    close(s);
+    return FTP_BAD;
+  }
+
+  ret = login(username,passwd,s,msgbuf,sizeof(msgbuf),ftp_printf);
+  if (ret < 0) {
+    close(s);
+    return (ret);
+  }
+
+  /* We are now logged in and ready to transfer the file. Open the
+     data socket ready to receive the file. It also build the PORT
+     command ready to send */
+  data_s = opendatasock(s,local,msgbuf,sizeof(msgbuf),ftp_printf);
+  if (data_s < 0) {
+    close (s);
+    return (data_s);
+  }
+
+  /* Ask for the file */
+  ret = command("RETR",filename,s,msgbuf,sizeof(msgbuf),ftp_printf);
+  if (ret < 0) {
+    close(s);
+    close(data_s);
+    return (ret);
+  }
+  
+  if (ret != 1) {
+    ftp_printf(0,"FTP: RETR failed!\n");
+    close (data_s);
+    close(s);
+    return (FTP_BADFILENAME);
+  }
+  
+  if ((bytes=receive_file(data_s,buf,buf_size,ftp_printf)) < 0) {
+    ftp_printf(0,"FTP: Receiving file failed\n");
+    close (data_s);
+    close(s);
+    return (bytes);
+  }
+  
+  if (get_reply(s,ftp_printf) != 2) {
+    ftp_printf(0,"FTP: Transfer failed!\n");
+    close (data_s);
+    close(s);
+    return (FTP_BAD);
+  }
+
+  ret = quit(s,msgbuf,sizeof(msgbuf),ftp_printf);
+  if (ret < 0) {
+    close(s);
+    close(data_s);
+    return (ret);
+  }
+            
+  close (data_s);
+  close(s);
+  return bytes;
+}
+  
+/* Put a file on an FTP server. Hostname is the name/IP address of the
+   server. username is the username used to connect to the server
+   with. Passwd is the password used to authentificate the
+   username. filename is the name of the file to receive. It should be
+   the full pathname of the file. buf is a pointer to a buffer the
+   contents of the file should be placed in and buf_size is the size
+   of the buffer. ftp_printf is a function to be called to perform
+   printing. On success 0 is returned. On error a negative value is
+   returned indicating the type of error. */
+
+int ftp_put(char * hostname, 
+            char * username, 
+            char * passwd, 
+            char * filename, 
+            char * buf, 
+            unsigned buf_size,
+            ftp_printf_t ftp_printf)
+{
+
+  struct sockaddr_in local;
+  char msgbuf[256];
+  int s,data_s;
+  int ret;
+  
+  s = connect_to_server(hostname,&local,ftp_printf);
+  if (s < 0) {
+    return (s);
+  }
+  
+  /* Read the welcome message from the server */
+  if (get_reply(s,ftp_printf) != 2) {
+    ftp_printf(1,"FTP: Server refused connection\n");
+    close(s);
+    return FTP_BAD;
+  }
+
+  ret = login(username,passwd,s,msgbuf,sizeof(msgbuf),ftp_printf);
+  if (ret < 0) {
+    close(s);
+    return (ret);
+  }
+
+  /* We are now logged in and ready to transfer the file. Open the
+     data socket ready to receive the file. It also build the PORT
+     command ready to send */
+  data_s = opendatasock(s,local,msgbuf,sizeof(msgbuf),ftp_printf);
+  if (data_s < 0) {
+    close (s);
+    return (data_s);
+  }
+
+  /* Ask for the file */
+  ret = command("STOR",filename,s,msgbuf,sizeof(msgbuf),ftp_printf);
+  if (ret < 0) {
+    close(s);
+    close(data_s);
+    return (ret);
+  }
+  
+  if (ret != 1) {
+    ftp_printf(1,"FTP: STOR failed!\n");
+    close (data_s);
+    close(s);
+    return (FTP_BADFILENAME);
+  }
+  
+  if ((ret = send_file(data_s,buf,buf_size,ftp_printf)) < 0) {
+    ftp_printf(1,"FTP: Sending file failed\n");
+    close (data_s);
+    close(s);
+    return (ret);
+  }
+  
+  if (get_reply(s,ftp_printf) != 2) {
+    ftp_printf(1,"FTP: Transfer failed!\n");
+    close (data_s);
+    close(s);
+    return (FTP_BAD);
+  }
+
+  ret = quit(s,msgbuf,sizeof(msgbuf),ftp_printf);
+  if (ret < 0) {
+    close(s);
+    close(data_s);
+    return (ret);
+  }
+            
+  close (data_s);
+  close(s);
+  return 0;
+}
+
+/* An example ftp_printf function. This uses the standard eCos diag
+output device for outputting error and diagnostic messages. The
+function take one addition parameter to the normal printf function. The
+first parameter indicates when the message is an error message when
+true. This can be used to filter errors from diagnostic output. In
+this example the error parameter is ignored and everything printed.
+
+The diag_vprintf() function does not use varargs, so we have to do the
+hard work ourselves of unpacking the stack and passing an array of
+pointers*/
+
+externC void diag_vprintf( const char *fmt, CYG_ADDRWORD *args);
+
+void ftpclient_printf(unsigned error, const char *fmt, ...) 
+{
+  va_list ap;
+  CYG_ADDRWORD args[8];
+  
+  va_start(ap, fmt);
+
+  args[0] = va_arg(ap,CYG_ADDRWORD);
+  args[1] = va_arg(ap,CYG_ADDRWORD);
+  args[2] = va_arg(ap,CYG_ADDRWORD);
+  args[3] = va_arg(ap,CYG_ADDRWORD);
+  args[4] = va_arg(ap,CYG_ADDRWORD);
+  args[5] = va_arg(ap,CYG_ADDRWORD);
+  args[6] = va_arg(ap,CYG_ADDRWORD);
+  args[7] = va_arg(ap,CYG_ADDRWORD);
+  
+  diag_vprintf( fmt, args);
+
+  va_end(ap);
+}