# HG changeset patch # User gthomas # Date 1085427061 0 # Node ID 0e6a7d89dd0e2fe9151ecd7f11de2a840e358c7d # Parent 0a6e548604cc0cff2926af27952d5a32dcdec19e New stream oriented functions - ftp_get_var()/ftp_put_var() diff --git a/packages/net/ftpclient/current/ChangeLog b/packages/net/ftpclient/current/ChangeLog --- a/packages/net/ftpclient/current/ChangeLog +++ b/packages/net/ftpclient/current/ChangeLog @@ -1,3 +1,10 @@ +2004-05-24 Gary Thomas + + * src/ftpclient.c: + * include/ftpclient.h: + * cdl/ftpclient.cdl: Add new functions 'ftp_get_var()' and 'ftp_put_var()' + which allow for streams of data instead of fixed buffers. + 2003-07-02 Daniel Néri * src/ftpclient.c (send_cmd): Fix typo in error message. diff --git a/packages/net/ftpclient/current/cdl/ftpclient.cdl b/packages/net/ftpclient/current/cdl/ftpclient.cdl --- a/packages/net/ftpclient/current/cdl/ftpclient.cdl +++ b/packages/net/ftpclient/current/cdl/ftpclient.cdl @@ -9,6 +9,7 @@ ## ------------------------------------------- ## This file is part of eCos, the Embedded Configurable Operating System. ## Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +## Copyright (C) 2004 Gary Thomas ## ## eCos 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 @@ -53,6 +54,8 @@ cdl_package CYGPKG_NET_FTPCLIENT { display "FTP client" parent CYGPKG_NET requires CYGPKG_IO + requires CYGPKG_NET + requires CYGPKG_MEMALLOC requires { 0 != CYGINT_ISO_STRING_STRFUNCS } requires { 0 != CYGINT_ISO_STRING_MEMFUNCS } requires { 0 != CYGINT_ISO_STDLIB_STRCONV } @@ -61,7 +64,6 @@ cdl_package CYGPKG_NET_FTPCLIENT { requires { 0 != CYGINT_ISO_ERRNO } requires { 0 != CYGINT_ISO_ERRNO_CODES } requires { 0 != CYGINT_ISO_CTYPE } - requires CYGPKG_NET description " FTP client support. Provides ftp_put and ftp_get to put a file onto a remote FTP server and get a file from a remote server. @@ -70,6 +72,16 @@ cdl_package CYGPKG_NET_FTPCLIENT { compile ftpclient.c + cdl_option CYGNUM_NET_FTPCLIENT_BUFSIZE { + display "Size of internal buffers used during FTP transfers" + flavor data + default_value 512 + description " + The FTP data transfer functions buffer the data as it + passes between systems. This option controls the size + of that buffer, which will be allocated using 'malloc'" + } + cdl_component CYGPKG_NET_FTPCLIENT_OPTIONS { display "FTP client build options" flavor none diff --git a/packages/net/ftpclient/current/include/ftpclient.h b/packages/net/ftpclient/current/include/ftpclient.h --- a/packages/net/ftpclient/current/include/ftpclient.h +++ b/packages/net/ftpclient/current/include/ftpclient.h @@ -12,6 +12,7 @@ // ------------------------------------------- // This file is part of eCos, the Embedded Configurable Operating System. // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. +// Copyright (C) 2004 Gary Thomas // // eCos 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 @@ -53,7 +54,12 @@ // //========================================================================== +// User-defined function for printing diagnostic messages typedef void (*ftp_printf_t)(unsigned error, const char *, ...); +// User-defined function used to provide data +typedef int (*ftp_read_t)(char *buf, int bufsize, void *priv); +// User-defined function used to process data +typedef int (*ftp_write_t)(char *buf, int bufsize, void *priv); /* Use the FTP protocol to retrieve a file from a server. Only binary mode is supported. The filename can include a directory name. Only @@ -71,6 +77,19 @@ int ftp_get(char * hostname, unsigned buf_size, ftp_printf_t ftp_printf); +// +// Just like 'ftp_get()' except that the "write()" function is called +// as data arrives instead of using a fixed buffer. Returns the total +// amount of data read, or an error indication (negative codes) +// +int ftp_get_var(char *hostname, + char *username, + char *passwd, + char *filename, + ftp_write_t ftp_write, + void *ftp_write_priv, + ftp_printf_t ftp_printf); + /*Use the FTP protocol to send a file from a server. Only binary mode is supported. The filename can include a directory name. Only use unix style / not M$'s \. The contents of buf is placed into the file @@ -85,6 +104,19 @@ int ftp_put(char * hostname, unsigned buf_size, ftp_printf_t ftp_printf); +// +// Just like 'ftp_put()' except that the "read()" function is called +// to fetch the data to write instead of using a fixed buffer. Returns +// the total amount of data written, or an error indication (negative codes) +// +int ftp_put_var(char *hostname, + char *username, + char *passwd, + char *filename, + ftp_read_t ftp_read, + void *ftp_read_priv, + ftp_printf_t ftp_printf); + /*ftp_get() and ftp_put take the name of a function to call to print out diagnostic and error messages. This is a sample implementation which can be used if you don't want to implement the function @@ -101,6 +133,7 @@ void ftpclient_printf(unsigned error, co #define FTP_BADUSER -4 /* Username/Password failed */ #define FTP_TOOBIG -5 /* Out of buffer space or disk space */ #define FTP_BADFILENAME -6 /* The file does not exist */ +#define FTP_NOMEMORY -7 /* Unable to allocate memory for internal buffers */ -#endif CYGONCE_NET_FTPCLIENT_FTPCLIENT +#endif // CYGONCE_NET_FTPCLIENT_FTPCLIENT diff --git a/packages/net/ftpclient/current/src/ftpclient.c b/packages/net/ftpclient/current/src/ftpclient.c --- a/packages/net/ftpclient/current/src/ftpclient.c +++ b/packages/net/ftpclient/current/src/ftpclient.c @@ -10,6 +10,7 @@ // This file is part of eCos, the Embedded Configurable Operating System. // Copyright (C) 1998, 1999, 2000, 2001, 2002 Red Hat, Inc. // Copyright (C) 2002 Andrew Lunn. +// Copyright (C) 2004 Gary Thomas // // eCos 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 @@ -52,6 +53,7 @@ //========================================================================== #include +#include #include #include @@ -88,8 +90,8 @@ build_cmd(char *buf, 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) { - +get_line(int s, char *buf, unsigned buf_size, ftp_printf_t ftp_printf) +{ int eol = 0; int cnt = 0; int ret; @@ -125,8 +127,8 @@ get_line(int s, char *buf, unsigned buf_ 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) { - +get_reply(int s, ftp_printf_t ftp_printf) +{ char buf[BUFSIZ]; int more = 0; int ret; @@ -161,8 +163,8 @@ get_reply(int s,ftp_printf_t ftp_printf) /* Send a command to the server */ static int -send_cmd(int s,char * msgbuf,ftp_printf_t ftp_printf) { - +send_cmd(int s, char * msgbuf, ftp_printf_t ftp_printf) +{ int len; int slen = strlen(msgbuf); @@ -186,8 +188,8 @@ command(char * cmd, int s, char *msgbuf, int msgbuflen, - ftp_printf_t ftp_printf) { - + ftp_printf_t ftp_printf) +{ int err; if (!build_cmd(msgbuf,msgbuflen,cmd,arg)) { @@ -311,178 +313,196 @@ opendatasock(int ctrl_s, struct sockaddr *ctrl, char *msgbuf, unsigned msgbuflen, - ftp_printf_t ftp_printf) { - - struct sockaddr local; - char name[64]; - char port[10]; - socklen_t len; - int on = 1; - char buf[80]; - int ret; - int s; + ftp_printf_t ftp_printf) +{ + struct sockaddr local; + char name[64]; + char port[10]; + socklen_t len; + int on = 1; + char buf[80]; + int ret; + int s; - s = socket(ctrl->sa_family, SOCK_STREAM, 0); - if (s < 0) { - ftp_printf(1,"socket: %s\n",strerror(errno)); - return FTP_BAD; - } + s = socket(ctrl->sa_family, 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; - } + 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; + } - memcpy(&local,ctrl,sizeof(struct sockaddr)); - switch (ctrl->sa_family) { + memcpy(&local,ctrl,sizeof(struct sockaddr)); + switch (ctrl->sa_family) { case AF_INET: { - struct sockaddr_in * sa4 = (struct sockaddr_in *) &local; - sa4->sin_port = 0; - break; + struct sockaddr_in * sa4 = (struct sockaddr_in *) &local; + sa4->sin_port = 0; + break; } #ifdef CYGPKG_NET_INET6 case AF_INET6: { - struct sockaddr_in6 * sa6 = (struct sockaddr_in6 *) &local; - sa6->sin6_port = 0; - break; + struct sockaddr_in6 * sa6 = (struct sockaddr_in6 *) &local; + sa6->sin6_port = 0; + break; } #endif default: - close (s); - return FTP_BAD; - } + close (s); + return FTP_BAD; + } - if (bind(s,&local,local.sa_len) < 0) { - ftp_printf(1,"bind: %s\n",strerror(errno)); - close(s); - return FTP_BAD; - } + if (bind(s,&local,local.sa_len) < 0) { + ftp_printf(1,"bind: %s\n",strerror(errno)); + close(s); + return FTP_BAD; + } - len = sizeof(local); - if (getsockname(s,&local,&len) < 0) { - ftp_printf(1,"getsockname: %s\n",strerror(errno)); - close(s); - return FTP_BAD; - } + len = sizeof(local); + if (getsockname(s,&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; - } + if (listen(s, 1) < 0) { + ftp_printf(1,"listen: %s\n",strerror(errno)); + close(s); + return FTP_BAD; + } - getnameinfo(&local,sizeof(local),name,sizeof(name), port, sizeof(port), - NI_NUMERICHOST|NI_NUMERICSERV); - switch (local.sa_family) { - case AF_INET: { - snprintf(buf, sizeof(buf), "|1|%s|%s|", name, port); - break; - } + getnameinfo(&local, sizeof(local), name, sizeof(name), port, sizeof(port), + NI_NUMERICHOST|NI_NUMERICSERV); + switch (local.sa_family) { + case AF_INET: { + snprintf(buf, sizeof(buf), "|1|%s|%s|", name, port); + break; + } #ifdef CYGPKG_NET_INET6 - case AF_INET6: { - snprintf(buf, sizeof(buf), "|2|%s|%s|", name, port); - break; - } + case AF_INET6: { + snprintf(buf, sizeof(buf), "|2|%s|%s|", name, port); + break; + } #endif - default: - close (s); - return (FTP_BAD); - } + default: + close (s); + return (FTP_BAD); + } + + ret = command("EPRT",buf,ctrl_s,msgbuf,msgbuflen,ftp_printf); + if (ret < 0) { + close(s); + return (ret); + } - ret = command("EPRT",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); + if (ret != 2) { + int _port = atoi(port); + char *s = name; + while (*s) { + if (*s == '.') *s = ','; + s++; + } + snprintf(buf, sizeof(buf), "%s,%d,%d", name, _port/256, _port%256); + 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) +receive_file(int data_s, ftp_write_t ftp_write, void *ftp_write_priv, 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; - } + char *buf; + int finished = 0; + int total_size=0; + int len, wlen; + int s; + + if ((buf = (char *)malloc(CYGNUM_NET_FTPCLIENT_BUFSIZE)) == (char *)0) { + return FTP_NOMEMORY; + } + s = accept(data_s, NULL, 0); + if (s < 0) { + ftp_printf(1, "listen: %s\n",strerror(errno)); + free(buf); + 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; - } + do { + len = read(s, buf, CYGNUM_NET_FTPCLIENT_BUFSIZE); + if (len < 0) { + ftp_printf(1, "read: %s\n",strerror(errno)); + close(s); + free(buf); + 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); + if (len == 0) { + finished = 1; + } else { + wlen = (*ftp_write)(buf, len, ftp_write_priv); + if (wlen != len) { + ftp_printf(1, "FTP: File too big!\n"); + close(s); + free(buf); + return FTP_TOOBIG; + } + total_size += len; + } + } while (!finished); - close(s); - return total_size; + close(s); + free(buf); + 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) +send_file(int data_s, ftp_read_t ftp_read, void *ftp_read_priv, ftp_printf_t ftp_printf) { - int remaining=buf_size; - int finished = 0; - char * bufp = buf; - int len; - int s; + char *buf; + int len, rlen; + int s; - s = accept(data_s,NULL,0); - if (s<0) { - ftp_printf(1,"listen: %s\n",strerror(errno)); - return FTP_BAD; - } + if ((buf = (char *)malloc(CYGNUM_NET_FTPCLIENT_BUFSIZE)) == (char *)0) { + return FTP_NOMEMORY; + } + s = accept(data_s,NULL,0); + if (s<0) { + ftp_printf(1,"listen: %s\n",strerror(errno)); + free(buf); + 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); + do { + rlen = (*ftp_read)(buf, CYGNUM_NET_FTPCLIENT_BUFSIZE, ftp_read_priv); + if (rlen > 0) { + len = write(s, buf, rlen); + if (len < 0) { + ftp_printf(1,"write: %s\n",strerror(errno)); + close(s); + free(buf); + return FTP_BAD; + } + } + } while (rlen > 0); - close(s); - return 0; + close(s); + free(buf); + return 0; } /* All done, say bye, bye */ @@ -518,6 +538,48 @@ static int quit(int s, bytes received is returned. On error a negative value is returned indicating the type of error. */ +struct _ftp_data{ + char *buf; + int len; + int max_len; +}; + +static int _ftp_read(char *buf, int len, void *priv) +{ + struct _ftp_data *dp = (struct _ftp_data *)priv; + int res = 0; + + // FTP data channel desires to write 'len' bytes. Fetch up + // to that amount into 'buf' + if (dp->len > 0) { + res = dp->len; + if (res > len) res = len; + memcpy(buf, dp->buf, res); + dp->buf += len; + dp->len -= res; + } + return res; +} + +static int _ftp_write(char *buf, int len, void *priv) +{ + struct _ftp_data *dp = (struct _ftp_data *)priv; + int res = 0; + + // FTP data channel has 'len' bytes that have been read. + // Move into 'buf', respecting the max size of 'buf' + if (dp->len < dp->max_len) { + res = dp->max_len - dp->len; + if (res > len) { + res = len; + } + memcpy(dp->buf, buf, res); + dp->buf += len; + dp->len += res; + } + return res; +} + int ftp_get(char * hostname, char * username, char * passwd, @@ -526,6 +588,22 @@ int ftp_get(char * hostname, unsigned buf_size, ftp_printf_t ftp_printf) { + struct _ftp_data ftp_data; + + ftp_data.buf = buf; + ftp_data.len = 0; + ftp_data.max_len = buf_size; + return ftp_get_var(hostname, username, passwd, filename, _ftp_write, &ftp_data, ftp_printf); +} + +int ftp_get_var(char *hostname, + char *username, + char *passwd, + char *filename, + ftp_write_t ftp_write, + void *ftp_write_priv, + ftp_printf_t ftp_printf) +{ struct sockaddr local; char msgbuf[256]; @@ -575,7 +653,7 @@ int ftp_get(char * hostname, return (FTP_BADFILENAME); } - if ((bytes=receive_file(data_s,buf,buf_size,ftp_printf)) < 0) { + if ((bytes=receive_file(data_s,ftp_write,ftp_write_priv,ftp_printf)) < 0) { ftp_printf(0,"FTP: Receiving file failed\n"); close (data_s); close(s); @@ -619,6 +697,21 @@ int ftp_put(char * hostname, unsigned buf_size, ftp_printf_t ftp_printf) { + struct _ftp_data ftp_data; + + ftp_data.buf = buf; + ftp_data.len = buf_size; + return ftp_put_var(hostname, username, passwd, filename, _ftp_read, &ftp_data, ftp_printf); +} + +int ftp_put_var(char *hostname, + char *username, + char *passwd, + char *filename, + ftp_read_t ftp_read, + void *ftp_read_priv, + ftp_printf_t ftp_printf) +{ struct sockaddr local; char msgbuf[256]; @@ -667,7 +760,7 @@ int ftp_put(char * hostname, return (FTP_BADFILENAME); } - if ((ret = send_file(data_s,buf,buf_size,ftp_printf)) < 0) { + if ((ret = send_file(data_s,ftp_read,ftp_read_priv,ftp_printf)) < 0) { ftp_printf(1,"FTP: Sending file failed\n"); close (data_s); close(s);