From 1ad3edea11c7d94c2dfc44b098688a8fed612c1a Mon Sep 17 00:00:00 2001 From: Claus Klein Date: Sun, 28 Jun 2015 21:14:33 +0200 Subject: [PATCH 1/2] prevent all warnings and errors impruve sample and add new make targets make test with sample get about 70% tcov --- .gitignore | 12 +- ChangeLog | 9 +- Makefile | 76 ++- README.md | 89 +-- ToDo | 2 + ftplib.cpp | 1529 ++++++++++++++++++++++++++++++--------------- ftplib.h | 185 +++--- sample/Makefile | 21 + sample/sample.cpp | 223 ++++++- 9 files changed, 1513 insertions(+), 633 deletions(-) mode change 100755 => 100644 ftplib.cpp mode change 100755 => 100644 ftplib.h create mode 100644 sample/Makefile diff --git a/.gitignore b/.gitignore index 27bb42a..a993417 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,13 @@ +.depend +.*.swp +*.back +*~ +*.txt +*.gcov +*.gcda +*.gcno +*.html +sample/sample *.o @@ -6,5 +16,5 @@ *.so *.2 - +*.1 *.0 diff --git a/ChangeLog b/ChangeLog index 75be1db..ae1a5ca 100755 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +v2.1.0 + +- cleanup Makefile +- prevent all g++ compiler warnings (pedantic) +- astyle source code +- disable deprecated encryption functionality + v2.0.4 - Added support for MSVC 2012. @@ -34,4 +41,4 @@ v1.0.1 - ftplib::Fxp was always returning 0, fixed this. - Quit returns if there's no connection anymore to do QUIT. -- Fixed a bug in secure upload. \ No newline at end of file +- Fixed a bug in secure upload. diff --git a/Makefile b/Makefile index 4e97a01..bf22610 100755 --- a/Makefile +++ b/Makefile @@ -1,15 +1,21 @@ # ftplibpp makefile -SONAME = 2 -SOVERSION = $(SONAME).0 +SONAME := 2 +SOVERSION := $(SONAME).1 -TARGETS = libftp++.a libftp++.so -OBJECTS = ftplib.o -SOURCES = ftplib.cpp +TARGETS := libftp++.a ###XXX libftp++.so +OBJECTS := ftplib.o +SOURCES := ftplib.cpp +DEFINES += ###TBD -DNOSSL +DEBUG := -g +CXXWARNFLAGS := -Wold-style-cast -Wsign-conversion -Wconversion -Wsign-compare -Wpointer-arith -CFLAGS = -Wall $(DEBUG) -I. $(INCLUDES) $(DEFINES) -LDFLAGS = -L. -DEPFLAGS = +CXXFLAGS := -std=c++98 -Wall -Wextra $(CXXWARNFLAGS) $(DEBUG) -I. $(INCLUDES) $(DEFINES) +LDFLAGS := -L. + +ifdef TCOV +CXXFLAGS+= --coverage +endif UNAME := $(shell uname) ifeq ($(UNAME), Darwin) @@ -19,46 +25,70 @@ ifeq ($(UNAME), Linux) LIBS = -lssl endif -all : $(TARGETS) +.PHONY: all clean distclean depend install uninstall astyle doc tcov +all : $(TARGETS) doc + $(MAKE) -C sample test 'CXXFLAGS=$(CXXFLAGS)' + +tcov: TCOV=1 +tcov: CXXFLAGS+= --coverage +tcov: all + gcov ftplib + +astyle : + -astyle --style=ansi -t4 *.cpp *.h sample/sample.cpp + +doc : README.html +README.html: README.md + -multimarkdown -o $@ $< + -tidy -qm -asxml $@ clean : - rm -f $(OBJECTS) core *.bak - rm -rf unshared - rm -f $(TARGETS) .depend + rm -f $(OBJECTS) + rm -f $(TARGETS) + $(MAKE) -C sample clean + +distclean : clean + rm -f tags core README.html + rm -f .depend rm -f libftp.so.* + -find . \( -name '*.gcno' -o -name '*.gcda' -o -name '*.gcov' -o -name '*.orig' -o -name '*~' \) -delete +# rm -rf unshared uninstall : rm -f /usr/local/lib/libftp.so.* rm -f /usr/local/include/libftp.h -install : all +install : all libftp++.so install -m 644 libftp.so.$(SOVERSION) /usr/local/lib install -m 644 ftplib.h /usr/local/include (cd /usr/local/lib && \ ln -sf libftp.so.$(SOVERSION) libftp.so.$(SONAME) && \ ln -sf libftp.so.$(SONAME) libftp.so) -depend : - $(CC) $(CFLAGS) -M $(SOURCES) > .depend +depend : .depend +.depend : ftplib.cpp ftplib.h + $(CXX) $(CXXFLAGS) -M $(SOURCES) > .depend # build without -fPIC #unshared/ftplib.o: ftplib.cpp ftplib.h # -mkdir unshared -# $(CC) -c $(CFLAGS) -D_REENTRANT $< -o $@ +# $(CXX) -c $(CXXFLAGS) -D_REENTRANT $< -o $@ ftplib.o: ftplib.cpp ftplib.h - $(CC) -c $(CFLAGS) -fPIC -D_REENTRANT $< -o $@ + $(CXX) -c $(CXXFLAGS) -fPIC -D_REENTRANT $< -o $@ -libftp++.a: ftplib.o - ar -rcs $@ $< +libftp++.a: $(OBJECTS) + $(AR) -rcs $@ $< -libftp.so.$(SOVERSION): ftplib.o - $(CC) -shared -Wl,-install_name,libftp.so.$(SONAME) $(LIBS) -lc -o $@ $< +libftp.so.$(SOVERSION): $(OBJECTS) + $(CXX) -shared -Wl,-install_name,libftp.so.$(SONAME) $(LIBS) -lc -o $@ $< libftp++.so: libftp.so.$(SOVERSION) ln -sf $< libftp.so.$(SONAME) ln -sf $< $@ -ifeq (.depend,$(wildcard .depend)) -include .depend +# include dependency files for any other rule: +ifneq ($(filter-out clean distclean,$(MAKECMDGOALS)),) +-include .depend endif + diff --git a/README.md b/README.md index d621025..84d1b9d 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,27 @@ -# ftplibpp +Title: ftplibpp -Platform independent c++ library providing ftp client functionality. +ftplibpp +=============== -ftplibpp contains a c++ class providing ftp client functionality. It supports all basic -ftp functionality plus some advanced features like resuming, fxp, ssl/tls encryption, +Platform independent c++ library providing FTP client functionality. + +ftplibpp contains a c++ class providing FTP client functionality. It supports all basic +FTP functionality plus some advanced features like resuming, fxp, SSL/TLS encryption, large file support, or logging to fit todays standards. ## Documentation -ftplibpp provides a c++ class providing ftp client functionality. It supports all basic ftp functionality plus some -advanced features like resuming, fxp, ssl/tls encryption, large file support, or logging to fit todays standards. The +ftplibpp provides a c++ class providing ftp client functionality. It supports all basic FTP functionality plus some +advanced features like resuming, fxp, SSL/TLs encryption, large file support, or logging to fit todays standards. The very base of ftplibpp is Thomas Pfau's [ftplib c library](http://nbpfaus.net/%7Epfau/ftplib/). -Every ftp session is represented by an ftplib object, whose methods are called to communicate with the ftp server. The -ftp sessions should begin with a call to *myftp.Connect("myftp.org:21")* (and maybe *myftp.NegotiateEncryption()* ), be +Every FTP session is represented by an ftplib object, whose methods are called to communicate with the FTP server. The +FTP sessions should begin with a call to *myftp.Connect("myftp.org:21")* (and maybe *myftp.NegotiateEncryption()* ), be followed with *myftp.Login("myuser","mypass")* and ended by *myftp.Quit()*. For the magic in between, read the class methods documentation. Most methods have their tasks pretty much explained in their name. ftplibpp uses OpenSSL for -encryption functionality, if you don't need it you can set the "NOSSL" flag (e.g. g++ -c ftplib.cpp -DNOSSL ). If your +encryption functionality, if you don't need it you can set the "NOSSL" flag (e.g. *g++ -c ftplib.cpp -DNOSSL* ). If your system does not feature large file support (or does not need specific LFS functions, because it's built in yet) you can -use the "NOLFS" flag (e.g. g++ -c ftplib.cpp -DNOLFS ). +use the "NOLFS" flag (e.g. g++ -c ftplib.cpp -DNOLFS* ). ### Public types @@ -32,7 +35,7 @@ use the "NOLFS" flag (e.g. g++ -c ftplib.cpp -DNOLFS ). * [enum fxpmethod](#fxpmethod) * [enum dataencryption](#dataencryption) -### Methods +### Public Methods * [ftplib ()](#ftplib) * [char* LastResponse ()](#lastresponse) @@ -48,8 +51,8 @@ use the "NOLFS" flag (e.g. g++ -c ftplib.cpp -DNOLFS ). * [int Pwd (char *path, int max)](#pwd) * [int Nlst (const char *outputfile, const char *path)](#nlst) * [int Dir (const char *outputfile, const char *path)](#dir) -* [int Size (const char *path, int *size, transfermode mode)](#size) -* [int ModDate (const char *path, char *dt, int max)](#moddate) +* [int Size (const char *path, off64_t *size, transfermode mode)](#size) +* [int ModDate (const char *path, char *dt, size_t max)](#moddate) * [int Get (const char *outputfile, const char *path, transfermode mode)](#get) * [int Get (const char *outputfile, const char *path, transfermode mode, off64_t offset)](#get2) * [int Put (const char *inputfile, const char *path, transfermode mode)](#put) @@ -58,10 +61,6 @@ use the "NOLFS" flag (e.g. g++ -c ftplib.cpp -DNOLFS ). * [int Delete (const char *path)](#delete) * [int SetDataEncryption (dataencryption enc)](#setdataencryption) * [int NegotiateEncryption ()](#negotiateencryption) -* [ftphandle* RawOpen (const char *path, accesstype type, transfermode mode)](#rawopen) -* [int RawRead (void *buf, int max, ftphandle *handle)](#rawread) -* [int RawWrite (void *buf, int len, ftphandle *handle)](#rawwrite) -* [int RawClose (ftphandle *handle)](#rawclose) * [void Quit ()](#quit) * [void SetCallbackIdleFunction (FtpCallbackIdle pointer)](#setcallbackidlefunction) * [void SetCallbackLogFunction (FtpCallbackLog pointer)](#setcallbacklogfunction) @@ -77,6 +76,13 @@ use the "NOLFS" flag (e.g. g++ -c ftplib.cpp -DNOLFS ). * [static int Fxp (ftplib* src, ftplib* dst, const char *pathSrc, const char *pathDst, ftplib::ftp mode, ftplib::ftp method)](#fxp) +### Private Methods + +* [ftphandle* RawOpen (const char *path, accesstype type, transfermode mode)](#rawopen) +* [ssize_t RawRead (void *buf, size_t max, ftphandle *handle)](#rawread) +* [ssize_t RawWrite (void *buf, size_t len, ftphandle *handle)](#rawwrite) +* [int RawClose (ftphandle *handle)](#rawclose) + ## Details @@ -151,7 +157,7 @@ This type determines wether data is encrypted or not. [constructor] -Class constructor, an ftplib object is responsible for the ftp session. +Class constructor, an ftplib object is responsible for the FTP session. @@ -169,12 +175,12 @@ LastResponse() returns a pointer to the last server response string. Otherwise, ###int Connect ( const char* host ) Connect() establishes a connection to the FTP server on the specified machine and returns a handle which can be used to -initiate data transfers. The host name should be specified in the form of :. may be either a host name or ip -address. may be either a service name or a port number. +initiate data transfers. The host name should be specified in the form of `:`. `` may be either a host name or ip +address. `` may be either a service name or a port number. ####Parameters: -*host:* The name of the host machine to connect to and optionally an alternate port number to use (ftp.myftp.com:321). +*host:* The name of the host machine to connect to and optionally an alternate port number to use (*ftp.myftp.com:321*). ####Returns: @@ -220,7 +226,7 @@ Raw() sends the specified command unmodified. ####Parameters: -*cmd:* A string containing a custom ftp command. +*cmd:* A string containing a custom FTP command. ####Returns: @@ -344,7 +350,7 @@ Returns 1 if successful or 0 on error. -###int Size ( const char* path, int* size, transfermode mode ) +###int Size ( const char* path, off64_t* size, transfermode mode ) Size() attempts to determine the size of a remote file. @@ -363,10 +369,11 @@ returned. -###int ModDate ( const char* path, char* dt, int max ) +###int ModDate ( const char* path, char* dt, size_t max ) -ModDate() attempts to determine the last access time of a remote file and return it to the user's buffer. The date and -time are returned as a string in the format 'YYYYMMDDHHMMSS'. +ModDate() attempts to determine the File Modification Time (MDTM) of a remote +file and return it to the user's buffer. The date and time are returned as a +string in the format 'YYYYMMDDHHMMSS'. ####Parameters: @@ -403,7 +410,7 @@ Returns 1 if successful or 0 on error. ###int Get (const char* outputfile, const char *path, transfermode mode, off64_t offset ) -Get() copies the contents of a remote file from a given offset and appends it to a local file. Not all ftp servers might +Get() copies the contents of a remote file from a given offset and appends it to a local file. Not all FTP servers might implement this feature. ####Parameters: @@ -438,7 +445,7 @@ Returns 1 if successful or 0 on error. ###int Put ( const char* inputfile, const char *path, transfermode mode, off64_t offset ) -Put() copies the contents of a local file from a given offset and appends it to a remote file. Not all ftp servers might +Put() copies the contents of a local file from a given offset and appends it to a remote file. Not all FTP servers might implement this feature. ####Parameters: @@ -451,7 +458,9 @@ implement this feature. *offset:* Point from where the copy begins. -####Returns: Returns 1 if successful or 0 on error. +####Returns: + +Returns 1 if successful or 0 on error. @@ -465,7 +474,9 @@ FtpRename() sends a rename request to the remote server. *dst:* A string containing the desired new name for the remote file. -####Returns: Returns 1 if successful or 0 on error. +####Returns: + +Returns 1 if successful or 0 on error. @@ -485,7 +496,7 @@ Returns 1 if successful or 0 on error. ###int SetDataEncryption ( dataencryption enc ) -On an already secured ftp session, SetDataEncryption() specifies if the data connection channel will be secured for the +On an already secured FTP session, SetDataEncryption() specifies if the data connection channel will be secured for the next data transfer. ####Parameters: @@ -504,7 +515,7 @@ See [NegotiateEncryption](#negotiateencryption) ###int NegotiateEncryption () -This Method is to be called after Connect and before Login to secure the ftp communication channel. +This Method is to be called after Connect and before Login to secure the FTP communication channel. ####Returns: @@ -524,7 +535,7 @@ Quit() issues a 'QUIT' command and closes the connection to the remote server. ###void SetCallbackXferFunction ( FtpCallbackXfer pointer ) -When SetCallbackBytes is set to a bigger value than 0, a callback function can be called during an ftp data transfer. If +When SetCallbackBytes is set to a bigger value than 0, a callback function can be called during an FTP data transfer. If the callback function returns 0, the data transfer is aborted. The callback function has two parameters: *xfered* & *arg*. *xfered* is the amount of bytes yet transfered during the data connection and *arg* contains either NULL or a custom pointer set by [SetCallbackArg](#setcallbackarg). If *pointer* is specified as NULL the xfer callback is disabled. @@ -544,10 +555,10 @@ method. valid code could look like this: ```cpp ... static int callback(off64_t xfered, void* arg); // static callback function defined in myclass.h -void mymethod(); // common myclass method +void mymethod(off64_t xfered); // common myclass method ... int myclass::callback(off64_t xfered, void* arg) { - ((*myclass)(arg)->mymethod(); // casting the pointers to the correct type and calling class method + ((*myclass)(arg))->mymethod(xfered); // casting the pointers to the correct type and calling class method return 1; } ... @@ -676,7 +687,7 @@ SetConnmode specifies which data connection method is to be used for the next da ###void SetCorrectPasv ( bool b ) Some Ftp-Servers, which run behind a NAT, return their local ip-adresses as PASV replies. When this option is turned on -PASV replies are corrected using the ip address the ftp session is currently connected to. +PASV replies are corrected using the ip address the FTP session is currently connected to. ####Parameters: @@ -688,7 +699,7 @@ PASV replies are corrected using the ip address the ftp session is currently con [static] -Fxp is a static function. Tt uses two ftp session objects and transfer a certain file between them. +Fxp is a static function. Tt uses two FTP session objects and transfer a certain file between them. ####Parameters: @@ -710,7 +721,7 @@ Currently Fxp does not work with encrypted data connections, so be sure to switc performing fxp. -###int RawRead ( void* buf, int max, ftphandle *handle ) +###ssize_t RawRead ( void* buf, size_t max, ftphandle *handle ) RawRead copies up to max bytes of data from the specified data connection and returns it to the user's buffer. If the data connection was opened in ascii mode, no more than one line of data will be returned. @@ -727,7 +738,7 @@ Returns the number of bytes written to the user's buffer or -1 on error or end o -####int RawWrite ( void* buf, int len, ftphandle *handle ) +####ssize_t RawWrite ( void* buf, size_t len, ftphandle *handle ) RawWrite() sends data to a remote file. If the file were accessed in record mode, the necessary conversions are performed. diff --git a/ToDo b/ToDo index 986003a..727e1f4 100644 --- a/ToDo +++ b/ToDo @@ -1 +1,3 @@ - implement up and download speed limitation. +- create unit test suite +- support IPV6 (AF_INET6) diff --git a/ftplib.cpp b/ftplib.cpp old mode 100755 new mode 100644 index 15e49ba..52f7736 --- a/ftplib.cpp +++ b/ftplib.cpp @@ -1,5 +1,5 @@ -// enable > 2gb support (LFS) +// enable > 2gb support (LFS) #ifndef NOLFS #define _LARGEFILE_SOURCE #define _LARGEFILE64_SOURCE @@ -25,14 +25,14 @@ #include #if defined(_WIN32) -#define SETSOCKOPT_OPTVAL_TYPE (const char *) +#define SETSOCKOPT_OPTVAL_TYPE(x) static_cast(x) #else -#define SETSOCKOPT_OPTVAL_TYPE (void *) +#define SETSOCKOPT_OPTVAL_TYPE(x) (x) //TODO not needed! ck static_cast(x) #endif #if defined(_WIN32) -#define net_read(x,y,z) recv(x,(char*)y,z,0) -#define net_write(x,y,z) send(x,(char*)y,z,0) +#define net_read(x,y,z) recv(x,static_cast<(char*>(y),z,0) +#define net_write(x,y,z) send(x,static_cast(y),z,0) #define net_close closesocket #else #define net_read read @@ -49,33 +49,25 @@ typedef int socklen_t; #define strdup _strdup #endif -using namespace std; - /* socket values */ -//#define SETSOCKOPT_OPTVAL_TYPE (void *) -#define FTPLIB_BUFSIZ 1024 -#define ACCEPT_TIMEOUT 30 - -/* io types */ -#define FTPLIB_CONTROL 0 -#define FTPLIB_READ 1 -#define FTPLIB_WRITE 2 +#define FTPLIB_BUFSIZ (16 * 1024) +#define ACCEPT_TIMEOUT 30 /* win32 dll initializer */ #if defined(_WIN32) -BOOL APIENTRY DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved) +BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD reason, LPVOID reserved) { switch (reason) { - case DLL_PROCESS_ATTACH: - break; - case DLL_PROCESS_DETACH: - break; - case DLL_THREAD_ATTACH: - break; - case DLL_THREAD_DETACH: - break; + case DLL_PROCESS_ATTACH: + break; + case DLL_PROCESS_DETACH: + break; + case DLL_THREAD_ATTACH: + break; + case DLL_THREAD_DETACH: + break; } /* Returns TRUE on success, FALSE on failure */ @@ -86,47 +78,55 @@ BOOL APIENTRY DllMain (HINSTANCE hInst, DWORD reason, LPVOID reserved) /* * Constructor */ - -ftplib::ftplib() +ftplib::ftplib() : mp_ftphandle(NULL) { - #if defined(_WIN32) - WSADATA wsa; - if (WSAStartup(MAKEWORD(1, 1), &wsa)) - { - printf("WSAStartup() failed, %lu\n", (unsigned long)GetLastError()); - } - #endif - - #ifndef NOSSL + +#if defined(_WIN32) + WSADATA wsa; + if (WSAStartup(MAKEWORD(1, 1), &wsa)) + { + fprintf(stderr, "WSAStartup() failed, %lu\n", (unsigned long)GetLastError()); + } +#endif + +#ifndef NOSSL SSL_library_init(); - #endif +#endif - mp_ftphandle = static_cast(calloc(1,sizeof(ftphandle))); - if (mp_ftphandle == NULL) perror("calloc"); + mp_ftphandle = static_cast(calloc(1, sizeof(ftphandle))); + if (mp_ftphandle == NULL) + { + perror("calloc()"); + exit(1); //FIXME: can`t continue! ck + } mp_ftphandle->buf = static_cast(malloc(FTPLIB_BUFSIZ)); if (mp_ftphandle->buf == NULL) { - perror("calloc"); + perror("malloc()"); free(mp_ftphandle); + exit(1); //FIXME: can`t continue! ck } - #ifndef NOSSL + +#ifndef NOSSL mp_ftphandle->ctx = SSL_CTX_new(SSLv3_method()); SSL_CTX_set_verify(mp_ftphandle->ctx, SSL_VERIFY_NONE, NULL); mp_ftphandle->ssl = SSL_new(mp_ftphandle->ctx); - #endif +#endif + ClearHandle(); } /* * Destructor */ - ftplib::~ftplib() { - #ifndef NOSSL + +#ifndef NOSSL SSL_free(mp_ftphandle->ssl); SSL_CTX_free(mp_ftphandle->ctx); - #endif +#endif + free(mp_ftphandle->buf); free(mp_ftphandle); } @@ -139,39 +139,49 @@ ftplib::~ftplib() */ int ftplib::socket_wait(ftphandle *ctl) { - fd_set fd,*rfd = NULL,*wfd = NULL; + fd_set fd, *rfd = NULL, *wfd = NULL; struct timeval tv; - int rv = 0; + int rv = 0; // default error - if (ctl->idlecb == NULL) return 1; + if (ctl->idlecb == NULL) + { + return 1; // ok + } - /*if ((ctl->dir == FTPLIB_CONTROL) + /*if ((ctl->dir == ftphandle::FTPLIB_CONTROL) || (ctl->idlecb == NULL) || ((ctl->idletime.tv_sec == 0) && //(ctl->idletime.tv_usec 0)) return 1;*/ - if (ctl->dir == FTPLIB_WRITE) wfd = &fd; - else rfd = &fd; + if (ctl->dir == ftphandle::FTPLIB_WRITE) + { + wfd = &fd; + } + else + { + rfd = &fd; + } FD_ZERO(&fd); do { - FD_SET(ctl->handle,&fd); + FD_SET(ctl->handle, &fd); tv = ctl->idletime; - rv = select(ctl->handle+1, rfd, wfd, NULL, &tv); + rv = select(ctl->handle + 1, rfd, wfd, NULL, &tv); if (rv == -1) { - rv = 0; + rv = 0; // error strncpy(ctl->ctrl->response, strerror(errno), sizeof(ctl->ctrl->response)); break; } else if (rv > 0) { - rv = 1; + rv = 1; // ok break; - } - } while ((rv = ctl->idlecb(ctl->cbarg))); + } //XXX else timeout + } + while ((rv = ctl->idlecb(ctl->cbarg))); return rv; } @@ -181,25 +191,32 @@ int ftplib::socket_wait(ftphandle *ctl) * * return -1 on error or bytecount */ -int ftplib::readline(char *buf,int max,ftphandle *ctl) +int ftplib::readline(char *buf, size_t max, ftphandle *ctl) { - int x,retval = 0; - char *end,*bp=buf; + size_t x; + int retval = 0; + char *end, *bp = buf; int eof = 0; - if ((ctl->dir != FTPLIB_CONTROL) && (ctl->dir != FTPLIB_READ)) - return -1; + if ((ctl->dir != ftphandle::FTPLIB_CONTROL) && (ctl->dir != ftphandle::FTPLIB_READ)) + { + return -1; + } if (max == 0) - return 0; + { + return 0; + } do { if (ctl->cavail > 0) { - x = (max >= ctl->cavail) ? ctl->cavail : max-1; - end = static_cast(memccpy(bp,ctl->cget,'\n',x)); + x = (max >= ctl->cavail) ? ctl->cavail : max - 1; + end = static_cast(memccpy(bp, ctl->cget, '\n', x)); if (end != NULL) - x = end - bp; - retval += x; + { + x = static_cast(end - bp); + } + retval += static_cast(x); bp += x; *bp = '\0'; max -= x; @@ -208,7 +225,7 @@ int ftplib::readline(char *buf,int max,ftphandle *ctl) if (end != NULL) { bp -= 2; - if (strcmp(bp,"\r\n") == 0) + if (strcmp(bp, "\r\n") == 0) //XXX ascii mode { *bp++ = '\n'; *bp++ = '\0'; @@ -231,42 +248,63 @@ int ftplib::readline(char *buf,int max,ftphandle *ctl) if (eof) { if (retval == 0) - retval = -1; + { + retval = -1; + } break; } - if (!socket_wait(ctl)) return retval; + if (!socket_wait(ctl)) + { + return -1; // error + } + + ssize_t len; #ifndef NOSSL - if (ctl->tlsdata) x = SSL_read(ctl->ssl, ctl->cput, ctl->cleft); + if (ctl->tlsdata) + { + len = SSL_read(ctl->ssl, ctl->cput, ctl->cleft); + } else { - if (ctl->tlsctrl) x = SSL_read(ctl->ssl, ctl->cput, ctl->cleft); - else x = net_read(ctl->handle,ctl->cput,ctl->cleft); + if (ctl->tlsctrl) + { + len = SSL_read(ctl->ssl, ctl->cput, ctl->cleft); + } + else + { + len = net_read(ctl->handle, ctl->cput, ctl->cleft); + } } #else - x = net_read(ctl->handle,ctl->cput,ctl->cleft); + len = net_read(ctl->handle, ctl->cput, ctl->cleft); #endif - if ( x == -1) + + if (len == -1) { - perror("read"); + perror("read()"); retval = -1; break; } // LOGGING FUNCTIONALITY!!! - if ((ctl->dir == FTPLIB_CONTROL) && (mp_ftphandle->logcb != NULL)) + if ((ctl->dir == ftphandle::FTPLIB_CONTROL) && (mp_ftphandle->logcb != NULL)) { - *((ctl->cput)+x) = '\0'; + *((ctl->cput) + len) = '\0'; mp_ftphandle->logcb(ctl->cput, mp_ftphandle->cbarg, true); } - if (x == 0) eof = 1; - ctl->cleft -= x; - ctl->cavail += x; - ctl->cput += x; - } while (1); + if (len == 0) + { + eof = 1; + } + ctl->cleft -= static_cast(len); + ctl->cavail += static_cast(len); + ctl->cput += len; + } + while (1); return retval; } @@ -275,32 +313,47 @@ int ftplib::readline(char *buf,int max,ftphandle *ctl) * * return -1 on error or bytecount */ -int ftplib::writeline(char *buf, int len, ftphandle *nData) +int ftplib::writeline(char *buf, size_t len, ftphandle *nData) { - int x, nb=0, w; + size_t nb = 0; + size_t x; + ssize_t w; char *ubp = buf, *nbp; - char lc=0; + char lc = 0; - if (nData->dir != FTPLIB_WRITE) - return -1; - nbp = nData->buf; - for (x=0; x < len; x++) + if (nData->dir != ftphandle::FTPLIB_WRITE) + { + return -1; + } + nbp = nData->buf; //XXX ascii buffer + for (x = 0; x < len; x++) { if ((*ubp == '\n') && (lc != '\r')) { if (nb == FTPLIB_BUFSIZ) { - if (!socket_wait(nData)) return x; + if (!socket_wait(nData)) + { + return -1; // error + } + #ifndef NOSSL - if (nData->tlsctrl) w = SSL_write(nData->ssl, nbp, FTPLIB_BUFSIZ); - else w = net_write(nData->handle, nbp, FTPLIB_BUFSIZ); + if (nData->tlsctrl) + { + w = SSL_write(nData->ssl, nbp, FTPLIB_BUFSIZ); + } + else + { + w = net_write(nData->handle, nbp, FTPLIB_BUFSIZ); + } #else w = net_write(nData->handle, nbp, FTPLIB_BUFSIZ); #endif + if (w != FTPLIB_BUFSIZ) { - printf("write(1) returned %d, errno = %d\n", w, errno); - return(-1); + fprintf(stderr, "write(1) returned %ld, errno = %d\n", w, errno); + return (-1); } nb = 0; } @@ -309,17 +362,27 @@ int ftplib::writeline(char *buf, int len, ftphandle *nData) if (nb == FTPLIB_BUFSIZ) { if (!socket_wait(nData)) - return x; + { + return -1; // error + } + #ifndef NOSSL - if (nData->tlsctrl) w = SSL_write(nData->ssl, nbp, FTPLIB_BUFSIZ); - else w = net_write(nData->handle, nbp, FTPLIB_BUFSIZ); + if (nData->tlsctrl) + { + w = SSL_write(nData->ssl, nbp, FTPLIB_BUFSIZ); + } + else + { + w = net_write(nData->handle, nbp, FTPLIB_BUFSIZ); + } #else w = net_write(nData->handle, nbp, FTPLIB_BUFSIZ); -#endif +#endif + if (w != FTPLIB_BUFSIZ) { - printf("write(2) returned %d, errno = %d\n", w, errno); - return(-1); + fprintf(stderr, "write(2) returned %ld, errno = %d\n", w, errno); + return (-1); } nb = 0; } @@ -327,20 +390,31 @@ int ftplib::writeline(char *buf, int len, ftphandle *nData) } if (nb) { - if (!socket_wait(nData)) return x; + if (!socket_wait(nData)) + { + return -1; // error + } + #ifndef NOSSL - if (nData->tlsctrl) w = SSL_write(nData->ssl, nbp, nb); - else w = net_write(nData->handle, nbp, nb); + if (nData->tlsctrl) + { + w = SSL_write(nData->ssl, nbp, nb); + } + else + { + w = net_write(nData->handle, nbp, nb); + } #else - w = net_write(nData->handle, nbp, nb); + w = net_write(nData->handle, nbp, nb); #endif - if (w != nb) + + if (w != static_cast(nb)) { - printf("write(3) returned %d, errno = %d\n", w, errno); - return(-1); + fprintf(stderr, "write(3) returned %ld, errno = %d\n", w, errno); + return (-1); } } - return len; + return static_cast(len); } /* @@ -352,37 +426,44 @@ int ftplib::writeline(char *buf, int len, ftphandle *nData) int ftplib::readresp(char c, ftphandle *nControl) { char match[5]; - - if (readline(nControl->response,256,nControl) == -1) + + if (readline(nControl->response, PATH_MAX, nControl) == -1) { perror("Control socket read failed"); return 0; } - + if (nControl->response[3] == '-') { - strncpy(match,nControl->response,3); + strncpy(match, nControl->response, 3); match[3] = ' '; match[4] = '\0'; do { - if (readline(nControl->response,256,nControl) == -1) + if (readline(nControl->response, PATH_MAX, nControl) == -1) { perror("Control socket read failed"); return 0; } - } while (strncmp(nControl->response,match,4)); + } + while (strncmp(nControl->response, match, 4)); + } + if (nControl->response[0] == c) + { + return 1; } - if (nControl->response[0] == c) return 1; return 0; } /* - * FtpLastResponse - return a pointer to the last response received + * LastResponse - return a pointer to the last response received */ char* ftplib::LastResponse() { - if ((mp_ftphandle) && (mp_ftphandle->dir == FTPLIB_CONTROL)) return mp_ftphandle->response; + if ((mp_ftphandle) && (mp_ftphandle->dir == ftphandle::FTPLIB_CONTROL)) + { + return mp_ftphandle->response; + } return NULL; } @@ -394,84 +475,99 @@ char* ftplib::LastResponse() int ftplib::Connect(const char *host) { int sControl; - struct sockaddr_in sin; + union + { + struct sockaddr sa; + struct sockaddr_in in; + } sin; struct hostent *phe; struct servent *pse; - int on=1; + int on = 1; int ret; char *lhost; char *pnum; - - mp_ftphandle->dir = FTPLIB_CONTROL; + + mp_ftphandle->dir = ftphandle::FTPLIB_CONTROL; mp_ftphandle->ctrl = NULL; mp_ftphandle->xfered = 0; mp_ftphandle->xfered1 = 0; -#ifndef NOSSL + +#ifndef NOSSL mp_ftphandle->tlsctrl = 0; mp_ftphandle->tlsdata = 0; #endif + mp_ftphandle->offset = 0; mp_ftphandle->handle = 0; - - memset(&sin,0,sizeof(sin)); - sin.sin_family = AF_INET; + + memset(&sin, 0, sizeof(sin)); + sin.in.sin_family = AF_INET; lhost = strdup(host); - pnum = strchr(lhost,':'); + pnum = strchr(lhost, ':'); if (pnum == NULL) { - if ((pse = getservbyname("ftp","tcp")) == NULL) + if ((pse = getservbyname("ftp", "tcp")) == NULL) { - perror("getservbyname"); + perror("getservbyname()"); free(lhost); return 0; } - sin.sin_port = pse->s_port; + sin.in.sin_port = static_cast(pse->s_port); } else { *pnum++ = '\0'; - if (isdigit(*pnum)) sin.sin_port = htons(atoi(pnum)); + if (isdigit(*pnum)) + { + sin.in.sin_port = htons(atoi(pnum)); + } else { - pse = getservbyname(pnum,"tcp"); - sin.sin_port = pse->s_port; + if ((pse = getservbyname(pnum, "tcp")) == NULL); + { + perror("getservbyname()"); + free(lhost); + return 0; + } + sin.in.sin_port = static_cast(pse->s_port); } } - + #if defined(_WIN32) - if ((sin.sin_addr.s_addr = inet_addr(lhost)) == -1) + if ((sin.in.sin_addr.s_addr = inet_addr(lhost)) == -1) #else - ret = inet_aton(lhost, &sin.sin_addr); + ret = inet_aton(lhost, &sin.in.sin_addr); if (ret == 0) #endif + { if ((phe = gethostbyname(lhost)) == NULL) { - perror("gethostbyname"); + perror("gethostbyname()"); free(lhost); return 0; } - memcpy((char *)&sin.sin_addr, phe->h_addr, phe->h_length); + memcpy(&sin.in.sin_addr, phe->h_addr, static_cast(phe->h_length)); } - + free(lhost); sControl = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (sControl == -1) { - perror("socket"); + perror("socket()"); return 0; } - - if (setsockopt(sControl,SOL_SOCKET,SO_REUSEADDR, SETSOCKOPT_OPTVAL_TYPE &on, sizeof(on)) == -1) + + if (setsockopt(sControl, SOL_SOCKET, SO_REUSEADDR, SETSOCKOPT_OPTVAL_TYPE(& on), sizeof(on)) == -1) { - perror("setsockopt"); + perror("setsockopt()"); net_close(sControl); return 0; } - if (connect(sControl, (struct sockaddr *)&sin, sizeof(sin)) == -1) + if (connect(sControl, &sin.sa, sizeof(sin.sa)) == -1) { - perror("connect"); + perror("connect()"); net_close(sControl); return 0; } @@ -489,39 +585,55 @@ int ftplib::Connect(const char *host) } /* - * FtpSendCmd - send a command and wait for expected response + * SendCmd - send a command and wait for expected response * * return 1 if proper response received, 0 otherwise */ int ftplib::FtpSendCmd(const char *cmd, char expresp, ftphandle *nControl) { - char buf[256]; - int x; + char buf[PATH_MAX]; + ssize_t x; - if (!nControl->handle) return 0; + if (!nControl->handle) + { + return 0; + } - if (nControl->dir != FTPLIB_CONTROL) return 0; - sprintf(buf,"%s\r\n",cmd); + if (nControl->dir != ftphandle::FTPLIB_CONTROL) + { + return 0; + } + sprintf(buf, "%s\r\n", cmd); #ifndef NOSSL - if (nControl->tlsctrl) x = SSL_write(nControl->ssl,buf,strlen(buf)); - else x = net_write(nControl->handle,buf,strlen(buf)); + if (nControl->tlsctrl) + { + x = SSL_write(nControl->ssl, buf, strlen(buf)); + } + else + { + x = net_write(nControl->handle, buf, strlen(buf)); + } #else - x = net_write(nControl->handle,buf,strlen(buf)); + x = net_write(nControl->handle, buf, strlen(buf)); #endif + if (x <= 0) { - perror("write"); + perror("write()"); return 0; } - if (mp_ftphandle->logcb != NULL) mp_ftphandle->logcb(buf, mp_ftphandle->cbarg, false); - + if (mp_ftphandle->logcb != NULL) + { + mp_ftphandle->logcb(buf, mp_ftphandle->cbarg, false); + } + return readresp(expresp, nControl); } /* - * FtpLogin - log in to remote server + * Login - log in to remote server * * return 1 if logged in, 0 otherwise */ @@ -529,20 +641,29 @@ int ftplib::Login(const char *user, const char *pass) { char tempbuf[64]; - if (((strlen(user) + 7) > sizeof(tempbuf)) || ((strlen(pass) + 7) > sizeof(tempbuf))) return 0; + if (((strlen(user) + 7) > sizeof(tempbuf)) || ((strlen(pass) + 7) > sizeof(tempbuf))) + { + return 0; + } sprintf(tempbuf, "USER %s", user); - if (!FtpSendCmd(tempbuf,'3',mp_ftphandle)) + if (!FtpSendCmd(tempbuf, '3', mp_ftphandle)) { - if (mp_ftphandle->ctrl != NULL) return 1; - if (*LastResponse() == '2') return 1; + if (mp_ftphandle->ctrl != NULL) + { + return 1; + } + if (*LastResponse() == '2') + { + return 1; + } return 0; } - sprintf(tempbuf,"PASS %s",pass); - return FtpSendCmd(tempbuf,'2',mp_ftphandle); + sprintf(tempbuf, "PASS %s", pass); + return FtpSendCmd(tempbuf, '2', mp_ftphandle); } /* - * FtpAcceptConnection - accept connection from server + * AcceptConnection - accept connection from server * * return 1 if successful, 0 otherwise */ @@ -554,7 +675,7 @@ int ftplib::FtpAcceptConnection(ftphandle *nData, ftphandle *nControl) int i; struct timeval tv; fd_set mask; - int rv = 0; + int rv = 0; // error FD_ZERO(&mask); FD_SET(nControl->handle, &mask); @@ -562,8 +683,11 @@ int ftplib::FtpAcceptConnection(ftphandle *nData, ftphandle *nControl) tv.tv_usec = 0; tv.tv_sec = ACCEPT_TIMEOUT; i = nControl->handle; - if (i < nData->handle) i = nData->handle; - i = select(i+1, &mask, NULL, NULL, &tv); + if (i < nData->handle) + { + i = nData->handle; + } + i = select(i + 1, &mask, NULL, NULL, &tv); if (i == -1) { @@ -585,11 +709,11 @@ int ftplib::FtpAcceptConnection(ftphandle *nData, ftphandle *nControl) { l = sizeof(addr); sData = accept(nData->handle, &addr, &l); - i = errno; + i = errno; // save errno before close net_close(nData->handle); - if (sData > 0) + if (sData >= 0) //FIXME accept return -1 on error! { - rv = 1; + rv = 1; // OK nData->handle = sData; nData->ctrl = nControl; } @@ -612,45 +736,56 @@ int ftplib::FtpAcceptConnection(ftphandle *nData, ftphandle *nControl) } /* - * FtpAccess - return a handle for a data stream + * Access - return a handle for a data stream * * return 1 if successful, 0 otherwise */ int ftplib::FtpAccess(const char *path, accesstype type, transfermode mode, ftphandle *nControl, ftphandle **nData) { - char buf[256]; - int dir, ret; + char buf[PATH_MAX]; + ftphandle::direction dir; + +#ifndef NOSSL + int ret; +#endif if ((path == NULL) && ((type == ftplib::filewrite) - || (type == ftplib::fileread) - || (type == ftplib::filereadappend) - || (type == ftplib::filewriteappend))) + || (type == ftplib::fileread) + || (type == ftplib::filereadappend) + || (type == ftplib::filewriteappend))) { - sprintf(nControl->response,"Missing path argument for file transfer\n"); + sprintf(nControl->response, "Missing path argument for file transfer\n"); return 0; } sprintf(buf, "TYPE %c", mode); - if (!FtpSendCmd(buf, '2', nControl)) return 0; + if (!FtpSendCmd(buf, '2', nControl)) + { + return 0; + } switch (type) { + case ftplib::mlsd: + strcpy(buf, "MLSD"); + dir = ftphandle::FTPLIB_READ; + break; case ftplib::dir: - strcpy(buf,"NLST"); - dir = FTPLIB_READ; + strcpy(buf, "NLST"); + dir = ftphandle::FTPLIB_READ; break; case ftplib::dirverbose: - strcpy(buf,"LIST -aL"); - dir = FTPLIB_READ; + strcpy(buf, "LIST"); + dir = ftphandle::FTPLIB_READ; break; case ftplib::filereadappend: case ftplib::fileread: - strcpy(buf,"RETR"); - dir = FTPLIB_READ; + strcpy(buf, "RETR"); + dir = ftphandle::FTPLIB_READ; break; case ftplib::filewriteappend: case ftplib::filewrite: - strcpy(buf,"STOR"); - dir = FTPLIB_WRITE; + strcpy(buf, "STOR"); + dir = ftphandle::FTPLIB_WRITE; break; default: sprintf(nControl->response, "Invalid open type %d\n", type); @@ -658,21 +793,30 @@ int ftplib::FtpAccess(const char *path, accesstype type, transfermode mode, ftph } if (path != NULL) { - int i = strlen(buf); + size_t i = strlen(buf); buf[i++] = ' '; - if ((strlen(path) + i) >= sizeof(buf)) return 0; - strcpy(&buf[i],path); + if ((strlen(path) + i) >= sizeof(buf)) + { + return 0; + } + strcpy(&buf[i], path); } if (nControl->cmode == ftplib::pasv) { - if (FtpOpenPasv(nControl, nData, mode, dir, buf) == -1) return 0; + if (FtpOpenPasv(nControl, nData, mode, dir, buf) == -1) + { + return 0; + } } if (nControl->cmode == ftplib::port) { - if (FtpOpenPort(nControl, nData, mode, dir, buf) == -1) return 0; - if (!FtpAcceptConnection(*nData,nControl)) + if (FtpOpenPort(nControl, nData, mode, dir, buf) == -1) + { + return 0; + } + if (!FtpAcceptConnection(*nData, nControl)) { FtpClose(*nData); *nData = NULL; @@ -685,12 +829,16 @@ int ftplib::FtpAccess(const char *path, accesstype type, transfermode mode, ftph { (*nData)->ssl = SSL_new(nControl->ctx); (*nData)->sbio = BIO_new_socket((*nData)->handle, BIO_NOCLOSE); - SSL_set_bio((*nData)->ssl,(*nData)->sbio,(*nData)->sbio); + SSL_set_bio((*nData)->ssl, (*nData)->sbio, (*nData)->sbio); ret = SSL_connect((*nData)->ssl); - if (ret != 1) return 0; + if (ret != 1) + { + return 0; + } (*nData)->tlsdata = 1; } #endif + return 1; } @@ -699,21 +847,25 @@ int ftplib::FtpAccess(const char *path, accesstype type, transfermode mode, ftph * * return 1 if successful, -1 otherwise */ -int ftplib::FtpOpenPort(ftphandle *nControl, ftphandle **nData, transfermode mode, int dir, char *cmd) +int ftplib::FtpOpenPort(ftphandle *nControl, ftphandle **nData, transfermode mode, ftphandle::direction dir, char *cmd) { int sData; - union { - struct sockaddr sa; - struct sockaddr_in in; + union + { + struct sockaddr sa; + struct sockaddr_in in; } sin; struct linger lng = { 0, 0 }; socklen_t l; - int on=1; + int on = 1; ftphandle *ctrl; - char buf[256]; + char buf[PATH_MAX]; - if (nControl->dir != FTPLIB_CONTROL) return -1; - if ((dir != FTPLIB_READ) && (dir != FTPLIB_WRITE)) + if (nControl->dir != ftphandle::FTPLIB_CONTROL) + { + return -1; + } + if ((dir != ftphandle::FTPLIB_READ) && (dir != ftphandle::FTPLIB_WRITE)) { sprintf(nControl->response, "Invalid direction %d\n", dir); return -1; @@ -727,25 +879,25 @@ int ftplib::FtpOpenPort(ftphandle *nControl, ftphandle **nData, transfermode mod if (getsockname(nControl->handle, &sin.sa, &l) < 0) { - perror("getsockname"); + perror("getsockname()"); return -1; } - sData = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); + sData = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (sData == -1) { - perror("socket"); + perror("socket()"); return -1; } - if (setsockopt(sData,SOL_SOCKET,SO_REUSEADDR, SETSOCKOPT_OPTVAL_TYPE &on,sizeof(on)) == -1) + if (setsockopt(sData, SOL_SOCKET, SO_REUSEADDR, SETSOCKOPT_OPTVAL_TYPE(& on), sizeof(on)) == -1) { - perror("setsockopt"); + perror("setsockopt()"); net_close(sData); return -1; } - if (setsockopt(sData,SOL_SOCKET,SO_LINGER, SETSOCKOPT_OPTVAL_TYPE &lng,sizeof(lng)) == -1) + if (setsockopt(sData, SOL_SOCKET, SO_LINGER, SETSOCKOPT_OPTVAL_TYPE(& lng), sizeof(lng)) == -1) { - perror("setsockopt"); + perror("setsockopt()"); net_close(sData); return -1; } @@ -753,25 +905,29 @@ int ftplib::FtpOpenPort(ftphandle *nControl, ftphandle **nData, transfermode mod sin.in.sin_port = 0; if (bind(sData, &sin.sa, sizeof(sin)) == -1) { - perror("bind"); + perror("bind()"); net_close(sData); return -1; } if (listen(sData, 1) < 0) { - perror("listen"); + perror("listen()"); net_close(sData); return -1; } - if (getsockname(sData, &sin.sa, &l) < 0) return 0; + if (getsockname(sData, &sin.sa, &l) < 0) + { + net_close(sData); + return 0; + } sprintf(buf, "PORT %hhu,%hhu,%hhu,%hhu,%hhu,%hhu", - (unsigned char) sin.sa.sa_data[2], - (unsigned char) sin.sa.sa_data[3], - (unsigned char) sin.sa.sa_data[4], - (unsigned char) sin.sa.sa_data[5], - (unsigned char) sin.sa.sa_data[0], - (unsigned char) sin.sa.sa_data[1]); - if (!FtpSendCmd(buf,'2',nControl)) + static_cast(sin.sa.sa_data[2]), + static_cast(sin.sa.sa_data[3]), + static_cast(sin.sa.sa_data[4]), + static_cast(sin.sa.sa_data[5]), + static_cast(sin.sa.sa_data[0]), + static_cast(sin.sa.sa_data[1])); + if (!FtpSendCmd(buf, '2', nControl)) { net_close(sData); return -1; @@ -779,25 +935,25 @@ int ftplib::FtpOpenPort(ftphandle *nControl, ftphandle **nData, transfermode mod if (mp_ftphandle->offset != 0) { - char buf[256]; - sprintf(buf,"REST %lld", mp_ftphandle->offset); - if (!FtpSendCmd(buf,'3',nControl)) - { - net_close(sData); - return 0; - } + char buf[PATH_MAX]; + sprintf(buf, "REST %lld", mp_ftphandle->offset); + if (!FtpSendCmd(buf, '3', nControl)) + { + net_close(sData); + return 0; + } } - ctrl = static_cast(calloc(1,sizeof(ftphandle))); + ctrl = static_cast(calloc(1, sizeof(ftphandle))); if (ctrl == NULL) { - perror("calloc"); + perror("calloc()"); net_close(sData); return -1; } if ((mode == 'A') && ((ctrl->buf = static_cast(malloc(FTPLIB_BUFSIZ))) == NULL)) { - perror("calloc"); + perror("malloc()"); net_close(sData); free(ctrl); return -1; @@ -805,11 +961,16 @@ int ftplib::FtpOpenPort(ftphandle *nControl, ftphandle **nData, transfermode mod if (!FtpSendCmd(cmd, '1', nControl)) { - FtpClose(*nData); - *nData = NULL; + if (*nData) + { + FtpClose(*nData); + *nData = NULL; + } + free(ctrl); return -1; } + //TODO use ctrl helper class with constructor ctrl->handle = sData; ctrl->dir = dir; ctrl->ctrl = (nControl->cmode == ftplib::pasv) ? nControl : NULL; @@ -818,10 +979,22 @@ int ftplib::FtpOpenPort(ftphandle *nControl, ftphandle **nData, transfermode mod ctrl->xfered = 0; ctrl->xfered1 = 0; ctrl->cbbytes = nControl->cbbytes; - if (ctrl->idletime.tv_sec || ctrl->idletime.tv_usec) ctrl->idlecb = nControl->idlecb; - else ctrl->idlecb = NULL; - if (ctrl->cbbytes ) ctrl->xfercb = nControl->xfercb; - else ctrl->xfercb = NULL; + if (ctrl->idletime.tv_sec || ctrl->idletime.tv_usec) + { + ctrl->idlecb = nControl->idlecb; + } + else + { + ctrl->idlecb = NULL; + } + if (ctrl->cbbytes) + { + ctrl->xfercb = nControl->xfercb; + } + else + { + ctrl->xfercb = NULL; + } *nData = ctrl; return 1; @@ -832,23 +1005,27 @@ int ftplib::FtpOpenPort(ftphandle *nControl, ftphandle **nData, transfermode mod * * return 1 if successful, -1 otherwise */ -int ftplib::FtpOpenPasv(ftphandle *nControl, ftphandle **nData, transfermode mode, int dir, char *cmd) +int ftplib::FtpOpenPasv(ftphandle *nControl, ftphandle **nData, transfermode mode, ftphandle::direction dir, char *cmd) { int sData; - union { + union + { struct sockaddr sa; struct sockaddr_in in; } sin; struct linger lng = { 0, 0 }; - unsigned int l; - int on=1; + socklen_t l; + int on = 1; ftphandle *ctrl; char *cp; unsigned char v[6]; - int ret; + ssize_t ret; - if (nControl->dir != FTPLIB_CONTROL) return -1; - if ((dir != FTPLIB_READ) && (dir != FTPLIB_WRITE)) + if (nControl->dir != ftphandle::FTPLIB_CONTROL) + { + return -1; + } + if ((dir != ftphandle::FTPLIB_READ) && (dir != ftphandle::FTPLIB_WRITE)) { sprintf(nControl->response, "Invalid direction %d\n", dir); return -1; @@ -862,68 +1039,98 @@ int ftplib::FtpOpenPasv(ftphandle *nControl, ftphandle **nData, transfermode mod memset(&sin, 0, l); sin.in.sin_family = AF_INET; - if (!FtpSendCmd("PASV",'2',nControl)) return -1; - cp = strchr(nControl->response,'('); - if (cp == NULL) return -1; + if (!FtpSendCmd("PASV", '2', nControl)) + { + return -1; + } + cp = strchr(nControl->response, '('); + if (cp == NULL) + { + return -1; + } cp++; + #if defined(_WIN32) unsigned int v_i[6]; - sscanf(cp,"%u,%u,%u,%u,%u,%u",&v_i[2],&v_i[3],&v_i[4],&v_i[5],&v_i[0],&v_i[1]); - for (int i = 0; i < 6; i++) v[i] = (unsigned char) v_i[i]; + sscanf(cp, "%u,%u,%u,%u,%u,%u", &v_i[2], &v_i[3], &v_i[4], &v_i[5], &v_i[0], &v_i[1]); + for (int i = 0; i < 6; i++) + { + v[i] = static_cast(v_i[i]); + } #else - sscanf(cp,"%hhu,%hhu,%hhu,%hhu,%hhu,%hhu",&v[2],&v[3],&v[4],&v[5],&v[0],&v[1]); + sscanf(cp, "%hhu,%hhu,%hhu,%hhu,%hhu,%hhu", &v[2], &v[3], &v[4], &v[5], &v[0], &v[1]); #endif - if (nControl->correctpasv) if (!CorrectPasvResponse(v)) return -1; - sin.sa.sa_data[2] = v[2]; - sin.sa.sa_data[3] = v[3]; - sin.sa.sa_data[4] = v[4]; - sin.sa.sa_data[5] = v[5]; - sin.sa.sa_data[0] = v[0]; - sin.sa.sa_data[1] = v[1]; + + if (nControl->correctpasv) if (!CorrectPasvResponse(v)) + { + return -1; + } + sin.sa.sa_data[2] = static_cast(v[2]); + sin.sa.sa_data[3] = static_cast(v[3]); + sin.sa.sa_data[4] = static_cast(v[4]); + sin.sa.sa_data[5] = static_cast(v[5]); + sin.sa.sa_data[0] = static_cast(v[0]); + sin.sa.sa_data[1] = static_cast(v[1]); if (mp_ftphandle->offset != 0) { - char buf[256]; - sprintf(buf,"REST %lld",mp_ftphandle->offset); - if (!FtpSendCmd(buf,'3',nControl)) return 0; + char buf[PATH_MAX]; + sprintf(buf, "REST %lld", mp_ftphandle->offset); + if (!FtpSendCmd(buf, '3', nControl)) + { + return -1; + } } - sData = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); + sData = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); if (sData == -1) { - perror("socket"); + perror("socket()"); return -1; } - if (setsockopt(sData,SOL_SOCKET,SO_REUSEADDR, SETSOCKOPT_OPTVAL_TYPE &on,sizeof(on)) == -1) + if (setsockopt(sData, SOL_SOCKET, SO_REUSEADDR, SETSOCKOPT_OPTVAL_TYPE(& on), sizeof(on)) == -1) { - perror("setsockopt"); + perror("setsockopt()"); net_close(sData); return -1; } - if (setsockopt(sData,SOL_SOCKET,SO_LINGER, SETSOCKOPT_OPTVAL_TYPE &lng,sizeof(lng)) == -1) + if (setsockopt(sData, SOL_SOCKET, SO_LINGER, SETSOCKOPT_OPTVAL_TYPE(& lng), sizeof(lng)) == -1) { - perror("setsockopt"); + perror("setsockopt()"); net_close(sData); return -1; } - if (nControl->dir != FTPLIB_CONTROL) return -1; - sprintf(cmd,"%s\r\n",cmd); + if (nControl->dir != ftphandle::FTPLIB_CONTROL) + { + net_close(sData); + return -1; + } + sprintf(cmd, "%s\r\n", cmd); + #ifndef NOSSL - if (nControl->tlsctrl) ret = SSL_write(nControl->ssl,cmd,strlen(cmd)); - else ret = net_write(nControl->handle,cmd,strlen(cmd)); + if (nControl->tlsctrl) + { + ret = SSL_write(nControl->ssl, cmd, strlen(cmd)); + } + else + { + ret = net_write(nControl->handle, cmd, strlen(cmd)); + } #else - ret = net_write(nControl->handle,cmd,strlen(cmd)); + ret = net_write(nControl->handle, cmd, strlen(cmd)); #endif + if (ret <= 0) { - perror("write"); + perror("write()"); + net_close(sData); return -1; } if (connect(sData, &sin.sa, sizeof(sin.sa)) == -1) { - perror("connect"); + perror("connect()"); net_close(sData); return -1; } @@ -932,20 +1139,22 @@ int ftplib::FtpOpenPasv(ftphandle *nControl, ftphandle **nData, transfermode mod net_close(sData); return -1; } - ctrl = static_cast(calloc(1,sizeof(ftphandle))); + ctrl = static_cast(calloc(1, sizeof(ftphandle))); if (ctrl == NULL) { - perror("calloc"); + perror("calloc()"); net_close(sData); return -1; } if ((mode == 'A') && ((ctrl->buf = static_cast(malloc(FTPLIB_BUFSIZ))) == NULL)) { - perror("calloc"); + perror("malloc()"); net_close(sData); free(ctrl); return -1; } + + //TODO use ctrl helper class with constructor ctrl->handle = sData; ctrl->dir = dir; ctrl->ctrl = (nControl->cmode == ftplib::pasv) ? nControl : NULL; @@ -954,10 +1163,22 @@ int ftplib::FtpOpenPasv(ftphandle *nControl, ftphandle **nData, transfermode mod ctrl->xfered = 0; ctrl->xfered1 = 0; ctrl->cbbytes = nControl->cbbytes; - if (ctrl->idletime.tv_sec || ctrl->idletime.tv_usec) ctrl->idlecb = nControl->idlecb; - else ctrl->idlecb = NULL; - if (ctrl->cbbytes ) ctrl->xfercb = nControl->xfercb; - else ctrl->xfercb = NULL; + if (ctrl->idletime.tv_sec || ctrl->idletime.tv_usec) + { + ctrl->idlecb = nControl->idlecb; + } + else + { + ctrl->idlecb = NULL; + } + if (ctrl->cbbytes) + { + ctrl->xfercb = nControl->xfercb; + } + else + { + ctrl->xfercb = NULL; + } *nData = ctrl; return 1; @@ -970,53 +1191,89 @@ int ftplib::FtpClose(ftphandle *nData) { ftphandle *ctrl; - if (nData->dir == FTPLIB_WRITE) + if (nData->dir == ftphandle::FTPLIB_WRITE) + { + if (nData->buf != NULL) + { + writeline(NULL, 0, nData); //XXX ascii mode + } + } + else if (nData->dir != ftphandle::FTPLIB_READ) + { + return 0; // error + } + if (nData->buf) { - if (nData->buf != NULL) writeline(NULL, 0, nData); + free(nData->buf); } - else if (nData->dir != FTPLIB_READ) return 0; - if (nData->buf) free(nData->buf); - shutdown(nData->handle,2); + shutdown(nData->handle, SHUT_RDWR); // SHUT_RDWR = 2 net_close(nData->handle); ctrl = nData->ctrl; + #ifndef NOSSL SSL_free(nData->ssl); #endif + free(nData); - if (ctrl) return readresp('2', ctrl); - return 1; + if (ctrl) + { + return readresp('2', ctrl); + } + return 1; // ok } /* * FtpRead - read from a data connection */ -int ftplib::FtpRead(void *buf, int max, ftphandle *nData) +ssize_t ftplib::FtpRead(void *buf, size_t max, ftphandle *nData) { - int i; + ssize_t i = 0; - if (nData->dir != FTPLIB_READ) - return 0; - if (nData->buf) i = readline(static_cast(buf), max, nData); + if (nData->dir != ftphandle::FTPLIB_READ) + { + return 0; + } + if (nData->buf) + { + i = readline(static_cast(buf), max, nData); //XXX ascii mode + } else { - i = socket_wait(nData); - if (i != 1) return 0; + if (!socket_wait(nData)) + { + return -1; // error + } + #ifndef NOSSL - if (nData->tlsdata) i = SSL_read(nData->ssl, buf, max); - else i = net_read(nData->handle,buf,max); + if (nData->tlsdata) + { + i = SSL_read(nData->ssl, buf, max); + } + else + { + i = net_read(nData->handle, buf, max); + } #else - i = net_read(nData->handle,buf,max); + i = net_read(nData->handle, buf, max); #endif + + } + if (i == -1) + { + return -1; // error } - if (i == -1) return 0; + nData->xfered += i; if (nData->xfercb && nData->cbbytes) { nData->xfered1 += i; if (nData->xfered1 > nData->cbbytes) { - if (nData->xfercb(nData->xfered, nData->cbarg) == 0) return 0; + if (nData->xfercb(nData->xfered, nData->cbarg) == 0) + { + return -1; // error + } nData->xfered1 = 0; } } @@ -1026,31 +1283,54 @@ int ftplib::FtpRead(void *buf, int max, ftphandle *nData) /* * FtpWrite - write to a data connection */ -int ftplib::FtpWrite(void *buf, int len, ftphandle *nData) +ssize_t ftplib::FtpWrite(void *buf, size_t len, ftphandle *nData) { - int i; + ssize_t i = 0; - if (nData->dir != FTPLIB_WRITE) return 0; - if (nData->buf) i = writeline(static_cast(buf), len, nData); + if (nData->dir != ftphandle::FTPLIB_WRITE) + { + return 0; + } + if (nData->buf) + { + i = writeline(static_cast(buf), len, nData); //XXX ascii mode + } else { - socket_wait(nData); + if (!socket_wait(nData)) + { + return -1; // error + } + #ifndef NOSSL - if (nData->tlsdata) i = SSL_write(nData->ssl, buf, len); - else i = net_write(nData->handle, buf, len); + if (nData->tlsdata) + { + i = SSL_write(nData->ssl, buf, len); + } + else + { + i = net_write(nData->handle, buf, len); + } #else i = net_write(nData->handle, buf, len); #endif + + } + if (i == -1) + { + return -1; // error } - if (i == -1) return 0; - nData->xfered += i; + nData->xfered += i; if (nData->xfercb && nData->cbbytes) { nData->xfered1 += i; if (nData->xfered1 > nData->cbbytes) { - if (nData->xfercb(nData->xfered, nData->cbarg) == 0) return 0; + if (nData->xfercb(nData->xfered, nData->cbarg) == 0) + { + return -1; // error + } nData->xfered1 = 0; } } @@ -1058,40 +1338,48 @@ int ftplib::FtpWrite(void *buf, int len, ftphandle *nData) } /* - * FtpSite - send a SITE command + * Site - send a SITE command * * return 1 if command successful, 0 otherwise */ int ftplib::Site(const char *cmd) { - char buf[256]; + char buf[PATH_MAX]; - if ((strlen(cmd) + 7) > sizeof(buf)) return 0; - sprintf(buf,"SITE %s",cmd); - if (!FtpSendCmd(buf,'2',mp_ftphandle)) return 0; + if ((strlen(cmd) + 7) > sizeof(buf)) + { + return 0; + } + sprintf(buf, "SITE %s", cmd); + if (!FtpSendCmd(buf, '2', mp_ftphandle)) + { + return 0; + } return 1; } /* - * FtpRaw - send a raw string string + * Raw - send a raw string string * * return 1 if command successful, 0 otherwise */ - int ftplib::Raw(const char *cmd) { - char buf[256]; - strncpy(buf, cmd, 256); - if (!FtpSendCmd(buf,'2',mp_ftphandle)) return 0; + char buf[PATH_MAX]; + strncpy(buf, cmd, PATH_MAX); + if (!FtpSendCmd(buf, '2', mp_ftphandle)) + { + return 0; + } return 1; } /* - * FtpSysType - send a SYST command + * SysType - send a SYST command * * Fills in the user buffer with the remote system type. If more * information from the response is required, the user can parse - * it out of the response buffer returned by FtpLastResponse(). + * it out of the response buffer returned by LastResponse(). * * return 1 if command successful, 0 otherwise */ @@ -1100,71 +1388,98 @@ int ftplib::SysType(char *buf, int max) int l = max; char *b = buf; char *s; - if (!FtpSendCmd("SYST",'2',mp_ftphandle)) return 0; + if (!FtpSendCmd("SYST", '2', mp_ftphandle)) + { + return 0; + } s = &mp_ftphandle->response[4]; - while ((--l) && (*s != ' ')) *b++ = *s++; + while ((--l) && (*s != ' ')) + { + *b++ = *s++; + } *b++ = '\0'; return 1; } /* - * FtpMkdir - create a directory at server + * Mkdir - create a directory at server * * return 1 if successful, 0 otherwise */ int ftplib::Mkdir(const char *path) { - char buf[256]; + char buf[PATH_MAX]; - if ((strlen(path) + 6) > sizeof(buf)) return 0; - sprintf(buf,"MKD %s",path); - if (!FtpSendCmd(buf,'2', mp_ftphandle)) return 0; + if ((strlen(path) + 6) > sizeof(buf)) + { + return 0; + } + sprintf(buf, "MKD %s", path); + if (!FtpSendCmd(buf, '2', mp_ftphandle)) + { + return 0; + } return 1; } /* - * FtpChdir - change path at remote + * Chdir - change path at remote * * return 1 if successful, 0 otherwise */ int ftplib::Chdir(const char *path) { - char buf[256]; + char buf[PATH_MAX]; - if ((strlen(path) + 6) > sizeof(buf)) return 0; - sprintf(buf,"CWD %s",path); - if (!FtpSendCmd(buf,'2',mp_ftphandle)) return 0; + if ((strlen(path) + 6) > sizeof(buf)) + { + return 0; + } + sprintf(buf, "CWD %s", path); + if (!FtpSendCmd(buf, '2', mp_ftphandle)) + { + return 0; + } return 1; } /* - * FtpCDUp - move to parent directory at remote + * Cdup - move to parent directory at remote * * return 1 if successful, 0 otherwise */ int ftplib::Cdup() { - if (!FtpSendCmd("CDUP",'2',mp_ftphandle)) return 0; + if (!FtpSendCmd("CDUP", '2', mp_ftphandle)) + { + return 0; + } return 1; } /* - * FtpRmdir - remove directory at remote + * Rmdir - remove directory at remote * * return 1 if successful, 0 otherwise */ int ftplib::Rmdir(const char *path) { - char buf[256]; + char buf[PATH_MAX]; - if ((strlen(path) + 6) > sizeof(buf)) return 0; - sprintf(buf,"RMD %s",path); - if (!FtpSendCmd(buf,'2',mp_ftphandle)) return 0; + if ((strlen(path) + 6) > sizeof(buf)) + { + return 0; + } + sprintf(buf, "RMD %s", path); + if (!FtpSendCmd(buf, '2', mp_ftphandle)) + { + return 0; + } return 1; } /* - * FtpPwd - get working directory at remote + * Pwd - get working directory at remote * * return 1 if successful, 0 otherwise */ @@ -1174,11 +1489,20 @@ int ftplib::Pwd(char *path, int max) char *b = path; char *s; - if (!FtpSendCmd("PWD",'2',mp_ftphandle)) return 0; + if (!FtpSendCmd("PWD", '2', mp_ftphandle)) + { + return 0; + } s = strchr(mp_ftphandle->response, '"'); - if (s == NULL) return 0; - s++; - while ((--l) && (*s) && (*s != '"')) *b++ = *s++; + if (s == NULL) + { + return 0; + } + s++; + while ((--l) && (*s) && (*s != '"')) + { + *b++ = *s++; + } *b++ = '\0'; return 1; } @@ -1190,32 +1514,60 @@ int ftplib::Pwd(char *path, int max) */ int ftplib::FtpXfer(const char *localfile, const char *path, ftphandle *nControl, accesstype type, transfermode mode) { - int l,c; char *dbuf; FILE *local = NULL; - ftphandle *nData; - int rv=1; // 3.1-1 + ftphandle *nData = NULL; + int rv = 1; // 3.1-1: default ok if (localfile != NULL) { - printf("localfile: -%s-", localfile); - - //local = fopen(localfile, (typ == ftplib::filewrite) ? "r" : "w"); + fprintf(stderr, "localfile: -%s-\n", localfile); + char ac[3] = " "; - if ((type == ftplib::dir) || (type == ftplib::dirverbose)) { ac[0] = 'w'; ac[1] = '\0'; } - if (type == ftplib::fileread) { ac[0] = 'w'; ac[1] = '\0'; } - if (type == ftplib::filewriteappend) { ac[0] = 'r'; ac[1] = '\0'; } - if (type == ftplib::filereadappend) { ac[0] = 'a'; ac[1] = '\0'; } - if (type == ftplib::filewrite) { ac[0] = 'r'; ac[1] = '\0'; } - if (mode == ftplib::image) ac[1] = 'b'; + if ((type == ftplib::dir) || (type == ftplib::dirverbose) || (type == ftplib::mlsd)) + { + ac[0] = 'w'; + ac[1] = '\0'; + } + if (type == ftplib::fileread) + { + ac[0] = 'w'; + ac[1] = '\0'; + } + if (type == ftplib::filewriteappend) + { + ac[0] = 'r'; + ac[1] = '\0'; + } + if (type == ftplib::filereadappend) + { + ac[0] = 'a'; + ac[1] = '\0'; + } + if (type == ftplib::filewrite) + { + ac[0] = 'r'; + ac[1] = '\0'; + } + if (mode == ftplib::image) + { + ac[1] = 'b'; + } #ifndef NOLFS local = fopen64(localfile, ac); - if (type == ftplib::filewriteappend) fseeko64(local,mp_ftphandle->offset,SEEK_SET); + if (type == ftplib::filewriteappend) + { + fseeko64(local, mp_ftphandle->offset, SEEK_SET); + } #else local = fopen(localfile, ac); - if (type == ftplib::filewriteappend) fseek(local,mp_ftphandle->offset,SEEK_SET); + if (type == ftplib::filewriteappend) + { + fseeko(local, mp_ftphandle->offset, SEEK_SET); + } #endif + if (local == NULL) { strncpy(nControl->response, strerror(errno), sizeof(nControl->response)); @@ -1223,43 +1575,79 @@ int ftplib::FtpXfer(const char *localfile, const char *path, ftphandle *nControl } } if (local == NULL) local = ((type == ftplib::filewrite) - || (type == ftplib::filewriteappend)) ? stdin : stdout; - if (!FtpAccess(path, type, mode, nControl, &nData)) return 0; + || (type == ftplib::filewriteappend)) ? stdin : stdout; + if (!FtpAccess(path, type, mode, nControl, &nData)) + { + return 0; // error + } dbuf = static_cast(malloc(FTPLIB_BUFSIZ)); - if ((type == ftplib::filewrite) || (type == ftplib::filewriteappend)) + if (dbuf) { - while ((l = fread(dbuf, 1, FTPLIB_BUFSIZ, local)) > 0) + if ((type == ftplib::filewrite) || (type == ftplib::filewriteappend)) { - if ((c = FtpWrite(dbuf, l, nData)) < l) + size_t len; + ssize_t cnt; + while ((len = fread(dbuf, 1, FTPLIB_BUFSIZ, local)) > 0) { - printf("short write: passed %d, wrote %d\n", l, c); - rv = 0; - break; + if ((cnt = FtpWrite(dbuf, len, nData)) < static_cast(len)) + { + fprintf(stderr, "short write: passed %ld, wrote %ld\n", len, cnt); + rv = 0; // error + break; + } } } - } - else - { - while ((l = FtpRead(dbuf, FTPLIB_BUFSIZ, nData)) > 0) + else { - if (fwrite(dbuf, 1, l, local) <= 0) + ssize_t len; + while ((len = FtpRead(dbuf, FTPLIB_BUFSIZ, nData)) > 0) { - perror("localfile write"); - break; + if (fwrite(dbuf, 1, static_cast(len), local) <= 0) + { + perror("localfile write"); + rv = 0; // error + break; + } +#ifdef __CYGWIN__ + fflush(local); //TODO: needed for test only under cygwin? ck +#endif + } + if (len < 0) + { + perror("FtpRead()"); + rv = 0; // TODO ftp read error } } + free(dbuf); + } + else + { + perror("malloc()"); + rv = 0; // error } - free(dbuf); fflush(local); if (localfile != NULL) - fclose(local); - return FtpClose(nData); + { + fclose(local); + } + rv &= FtpClose(nData); return rv; } /* - * FtpNlst - issue an NLST command and write response to output + * Mlsd - issue an MLSD command and write response to output + * + * return 1 if successful, 0 otherwise + */ +int ftplib::Mlsd(const char *outputfile, const char *path) +{ + mp_ftphandle->offset = 0; + return FtpXfer(outputfile, path, mp_ftphandle, ftplib::mlsd, ftplib::ascii); +} + +/* + * Nlst - issue an NLST command and write response to output * * return 1 if successful, 0 otherwise */ @@ -1270,7 +1658,7 @@ int ftplib::Nlst(const char *outputfile, const char *path) } /* - * FtpDir - issue a LIST command and write response to output + * Dir - issue a LIST command and write response to output * * return 1 if successful, 0 otherwise */ @@ -1281,49 +1669,80 @@ int ftplib::Dir(const char *outputfile, const char *path) } /* - * FtpSize - determine the size of a remote file + * Size - determine the size of a remote file * * return 1 if successful, 0 otherwise */ -int ftplib::Size(const char *path, int *size, transfermode mode) +int ftplib::Size(const char *path, off64_t *size, transfermode mode) { - char cmd[256]; - int resp,sz,rv=1; + char cmd[PATH_MAX]; + off64_t sz = 0; + int resp, rv = 1; - if ((strlen(path) + 7) > sizeof(cmd)) return 0; + if (!path || (strlen(path) + 7) > sizeof(cmd)) + { + return 0; + } sprintf(cmd, "TYPE %c", mode); - if (!FtpSendCmd(cmd, '2', mp_ftphandle)) return 0; - - sprintf(cmd,"SIZE %s",path); - if (!FtpSendCmd(cmd,'2',mp_ftphandle)) rv = 0; - else - { - if (sscanf(mp_ftphandle->response, "%d %d", &resp, &sz) == 2) *size = sz; - else rv = 0; - } - return rv; + if (!FtpSendCmd(cmd, '2', mp_ftphandle)) + { + return 0; + } + + sprintf(cmd, "SIZE %s", path); + if (!FtpSendCmd(cmd, '2', mp_ftphandle)) + { + rv = 0; + } + else + { + if (sscanf(mp_ftphandle->response, "%d %lld", &resp, &sz) == 2) + { + if (size) + { + *size = sz; + } + } + else + { + rv = 0; + } + } + return rv; } /* - * FtpModDate - determine the modification date of a remote file + * ModDate - determine the modification date of a remote file * * return 1 if successful, 0 otherwise */ -int ftplib::ModDate(const char *path, char *dt, int max) +int ftplib::ModDate(const char *path, char *dt, size_t max) { - char buf[256]; + char buf[PATH_MAX]; int rv = 1; - if ((strlen(path) + 7) > sizeof(buf)) return 0; - sprintf(buf,"MDTM %s",path); - if (!FtpSendCmd(buf,'2',mp_ftphandle)) rv = 0; - else strncpy(dt, &mp_ftphandle->response[4], max); + if (!path || (strlen(path) + 7) > sizeof(buf)) + { + return 0; + } + sprintf(buf, "MDTM %s", path); + if (!FtpSendCmd(buf, '2', mp_ftphandle)) + { + rv = 0; + } + else + { + if (dt) + { + strncpy(dt, &mp_ftphandle->response[4], max); + } + } return rv; } /* - * FtpGet - issue a GET command and write received data to output + * Get - issue a GET command and write received data to output * * return 1 if successful, 0 otherwise */ @@ -1331,12 +1750,18 @@ int ftplib::ModDate(const char *path, char *dt, int max) int ftplib::Get(const char *outputfile, const char *path, transfermode mode, off64_t offset) { mp_ftphandle->offset = offset; - if (offset == 0) return FtpXfer(outputfile, path, mp_ftphandle, ftplib::fileread, mode); - else return FtpXfer(outputfile, path, mp_ftphandle, ftplib::filereadappend, mode); + if (offset == 0) + { + return FtpXfer(outputfile, path, mp_ftphandle, ftplib::fileread, mode); + } + else + { + return FtpXfer(outputfile, path, mp_ftphandle, ftplib::filereadappend, mode); + } } /* - * FtpPut - issue a PUT command and send data from input + * Put - issue a PUT command and send data from input * * return 1 if successful, 0 otherwise */ @@ -1344,48 +1769,72 @@ int ftplib::Get(const char *outputfile, const char *path, transfermode mode, off int ftplib::Put(const char *inputfile, const char *path, transfermode mode, off64_t offset) { mp_ftphandle->offset = offset; - if (offset == 0) return FtpXfer(inputfile, path, mp_ftphandle, ftplib::filewrite, mode); - else return FtpXfer(inputfile, path, mp_ftphandle, ftplib::filewriteappend, mode); + if (offset == 0) + { + return FtpXfer(inputfile, path, mp_ftphandle, ftplib::filewrite, mode); + } + else + { + return FtpXfer(inputfile, path, mp_ftphandle, ftplib::filewriteappend, mode); + } } int ftplib::Rename(const char *src, const char *dst) { - char cmd[256]; + char cmd[PATH_MAX]; - if (((strlen(src) + 7) > sizeof(cmd)) || ((strlen(dst) + 7) > sizeof(cmd))) return 0; - sprintf(cmd,"RNFR %s",src); - if (!FtpSendCmd(cmd,'3',mp_ftphandle)) return 0; - sprintf(cmd,"RNTO %s",dst); - if (!FtpSendCmd(cmd,'2',mp_ftphandle)) return 0; + if (((strlen(src) + 7) > sizeof(cmd)) || ((strlen(dst) + 7) > sizeof(cmd))) + { + return 0; + } + sprintf(cmd, "RNFR %s", src); + if (!FtpSendCmd(cmd, '3', mp_ftphandle)) + { + return 0; + } + sprintf(cmd, "RNTO %s", dst); + if (!FtpSendCmd(cmd, '2', mp_ftphandle)) + { + return 0; + } return 1; } int ftplib::Delete(const char *path) { - char cmd[256]; + char cmd[PATH_MAX]; - if ((strlen(path) + 7) > sizeof(cmd)) return 0; - sprintf(cmd,"DELE %s",path); - if (!FtpSendCmd(cmd,'2', mp_ftphandle)) return 0; - return 1; + if ((strlen(path) + 7) > sizeof(cmd)) + { + return 0; + } + sprintf(cmd, "DELE %s", path); + if (!FtpSendCmd(cmd, '2', mp_ftphandle)) + { + return 0; + } + return 1; } /* - * FtpQuit - disconnect from remote + * Quit - disconnect from remote * * return 1 if successful, 0 otherwise */ int ftplib::Quit() { - if (mp_ftphandle->dir != FTPLIB_CONTROL) return 0; + if (mp_ftphandle->dir != ftphandle::FTPLIB_CONTROL) + { + return 0; + } if (mp_ftphandle->handle == 0) { strcpy(mp_ftphandle->response, "error: no anwser from server\n"); return 0; } - if (!FtpSendCmd("QUIT",'2',mp_ftphandle)) + if (!FtpSendCmd("QUIT", '2', mp_ftphandle)) { net_close(mp_ftphandle->handle); return 0; @@ -1396,61 +1845,101 @@ int ftplib::Quit() return 1; } } - + +#ifdef SUPPORT_FXP_FTP +/* + * Fxp is a static function. Tt uses two ftp session objects and transfer a certain file between them. + * + * Returns 1 if successful, + * –1 if initilization failed (“PORT” and “PASV”), + * or 0 if the data transfer somehow failed. + */ int ftplib::Fxp(ftplib* src, ftplib* dst, const char *pathSrc, const char *pathDst, transfermode mode, fxpmethod method) { char *cp; unsigned char v[6]; - char buf[256]; + char buf[PATH_MAX]; int retval = 0; - + sprintf(buf, "TYPE %c", mode); - if (!dst->FtpSendCmd(buf,'2',dst->mp_ftphandle)) return -1; - if (!src->FtpSendCmd(buf,'2',src->mp_ftphandle)) return -1; + if (!dst->FtpSendCmd(buf, '2', dst->mp_ftphandle)) + { + return -1; + } + if (!src->FtpSendCmd(buf, '2', src->mp_ftphandle)) + { + return -1; + } if (method == ftplib::defaultfxp) { // PASV dst - if (!dst->FtpSendCmd("PASV",'2',dst->mp_ftphandle)) return -1; - cp = strchr(dst->mp_ftphandle->response,'('); - if (cp == NULL) return -1; + if (!dst->FtpSendCmd("PASV", '2', dst->mp_ftphandle)) + { + return -1; + } + cp = strchr(dst->mp_ftphandle->response, '('); + if (cp == NULL) + { + return -1; + } cp++; + #if defined(_WIN32) unsigned int v_i[6]; - sscanf(cp,"%u,%u,%u,%u,%u,%u",&v_i[2],&v_i[3],&v_i[4],&v_i[5],&v_i[0],&v_i[1]); - for (int i = 0; i < 6; i++) v[i] = (unsigned char) v_i[i]; + sscanf(cp, "%u,%u,%u,%u,%u,%u", &v_i[2], &v_i[3], &v_i[4], &v_i[5], &v_i[0], &v_i[1]); + for (int i = 0; i < 6; i++) + { + v[i] = (unsigned char) v_i[i]; + } #else - sscanf(cp,"%hhu,%hhu,%hhu,%hhu,%hhu,%hhu",&v[2],&v[3],&v[4],&v[5],&v[0],&v[1]); + sscanf(cp, "%hhu,%hhu,%hhu,%hhu,%hhu,%hhu", &v[2], &v[3], &v[4], &v[5], &v[0], &v[1]); #endif - if (dst->mp_ftphandle->correctpasv) if (!dst->CorrectPasvResponse(v)) return -1; + + if (dst->mp_ftphandle->correctpasv) if (!dst->CorrectPasvResponse(v)) + { + return -1; + } // PORT src - sprintf(buf, "PORT %d,%d,%d,%d,%d,%d", v[2],v[3],v[4],v[5],v[0],v[1]); - if (!src->FtpSendCmd(buf,'2',src->mp_ftphandle)) return -1; + sprintf(buf, "PORT %d,%d,%d,%d,%d,%d", v[2], v[3], v[4], v[5], v[0], v[1]); + if (!src->FtpSendCmd(buf, '2', src->mp_ftphandle)) + { + return -1; + } // RETR src - strcpy(buf,"RETR"); + strcpy(buf, "RETR"); if (pathSrc != NULL) { - int i = strlen(buf); + size_t i = strlen(buf); buf[i++] = ' '; - if ((strlen(pathSrc) + i) >= sizeof(buf)) return 0; - strcpy(&buf[i],pathSrc); + if ((strlen(pathSrc) + i) >= sizeof(buf)) + { + return 0; + } + strcpy(&buf[i], pathSrc); + } + if (!src->FtpSendCmd(buf, '1', src->mp_ftphandle)) + { + return 0; } - if (!src->FtpSendCmd(buf, '1', src->mp_ftphandle)) return 0; // STOR dst - strcpy(buf,"STOR"); + strcpy(buf, "STOR"); if (pathDst != NULL) { - int i = strlen(buf); + size_t i = strlen(buf); buf[i++] = ' '; - if ((strlen(pathDst) + i) >= sizeof(buf)) return 0; - strcpy(&buf[i],pathDst); + if ((strlen(pathDst) + i) >= sizeof(buf)) + { + return 0; + } + strcpy(&buf[i], pathDst); } if (!dst->FtpSendCmd(buf, '1', dst->mp_ftphandle)) { @@ -1459,7 +1948,7 @@ int ftplib::Fxp(ftplib* src, ftplib* dst, const char *pathSrc, const char *pathD hours and i was absolutely clueless, playing around with ABOR and whatever, when i desperately checked the pftp source which gave me this final hint. thanks dude(s). */ - + dst->FtpSendCmd("PASV", '2', dst->mp_ftphandle); src->readresp('4', src->mp_ftphandle); return 0; @@ -1468,49 +1957,75 @@ int ftplib::Fxp(ftplib* src, ftplib* dst, const char *pathSrc, const char *pathD retval = (src->readresp('2', src->mp_ftphandle)) & (dst->readresp('2', dst->mp_ftphandle)); } - else + else { // PASV src - if (!src->FtpSendCmd("PASV",'2',src->mp_ftphandle)) return -1; - cp = strchr(src->mp_ftphandle->response,'('); - if (cp == NULL) return -1; + if (!src->FtpSendCmd("PASV", '2', src->mp_ftphandle)) + { + return -1; + } + cp = strchr(src->mp_ftphandle->response, '('); + if (cp == NULL) + { + return -1; + } cp++; + #if defined(_WIN32) unsigned int v_i[6]; - sscanf(cp,"%u,%u,%u,%u,%u,%u",&v_i[2],&v_i[3],&v_i[4],&v_i[5],&v_i[0],&v_i[1]); - for (int i = 0; i < 6; i++) v[i] = (unsigned char) v_i[i]; + sscanf(cp, "%u,%u,%u,%u,%u,%u", &v_i[2], &v_i[3], &v_i[4], &v_i[5], &v_i[0], &v_i[1]); + for (int i = 0; i < 6; i++) + { + v[i] = (unsigned char) v_i[i]; + } #else - sscanf(cp,"%hhu,%hhu,%hhu,%hhu,%hhu,%hhu",&v[2],&v[3],&v[4],&v[5],&v[0],&v[1]); + sscanf(cp, "%hhu,%hhu,%hhu,%hhu,%hhu,%hhu", &v[2], &v[3], &v[4], &v[5], &v[0], &v[1]); #endif - if (src->mp_ftphandle->correctpasv) if (!src->CorrectPasvResponse(v)) return -1; + + if (src->mp_ftphandle->correctpasv) if (!src->CorrectPasvResponse(v)) + { + return -1; + } // PORT dst - sprintf(buf, "PORT %d,%d,%d,%d,%d,%d", v[2],v[3],v[4],v[5],v[0],v[1]); - if (!dst->FtpSendCmd(buf,'2',dst->mp_ftphandle)) return -1; + sprintf(buf, "PORT %d,%d,%d,%d,%d,%d", v[2], v[3], v[4], v[5], v[0], v[1]); + if (!dst->FtpSendCmd(buf, '2', dst->mp_ftphandle)) + { + return -1; + } // STOR dest - strcpy(buf,"STOR"); + strcpy(buf, "STOR"); if (pathDst != NULL) { - int i = strlen(buf); + size_t i = strlen(buf); buf[i++] = ' '; - if ((strlen(pathDst) + i) >= sizeof(buf)) return 0; - strcpy(&buf[i],pathDst); + if ((strlen(pathDst) + i) >= sizeof(buf)) + { + return 0; + } + strcpy(&buf[i], pathDst); + } + if (!dst->FtpSendCmd(buf, '1', dst->mp_ftphandle)) + { + return 0; } - if (!dst->FtpSendCmd(buf, '1', dst->mp_ftphandle)) return 0; // RETR src - strcpy(buf,"RETR"); + strcpy(buf, "RETR"); if (pathSrc != NULL) { - int i = strlen(buf); + size_t i = strlen(buf); buf[i++] = ' '; - if ((strlen(pathSrc) + i) >= sizeof(buf)) return 0; - strcpy(&buf[i],pathSrc); + if ((strlen(pathSrc) + i) >= sizeof(buf)) + { + return 0; + } + strcpy(&buf[i], pathSrc); } if (!src->FtpSendCmd(buf, '1', src->mp_ftphandle)) { @@ -1527,21 +2042,35 @@ int ftplib::Fxp(ftplib* src, ftplib* dst, const char *pathSrc, const char *pathD return retval; } +#endif + #ifndef NOSSL int ftplib::SetDataEncryption(dataencryption enc) { - if (!mp_ftphandle->tlsctrl) return 0; - if (!FtpSendCmd("PBSZ 0",'2',mp_ftphandle)) return 0; - switch(enc) + if (!mp_ftphandle->tlsctrl) + { + return 0; + } + if (!FtpSendCmd("PBSZ 0", '2', mp_ftphandle)) + { + return 0; + } + switch (enc) { case ftplib::unencrypted: mp_ftphandle->tlsdata = 0; - if (!FtpSendCmd("PROT C",'2',mp_ftphandle)) return 0; + if (!FtpSendCmd("PROT C", '2', mp_ftphandle)) + { + return 0; + } break; case ftplib::secure: mp_ftphandle->tlsdata = 1; - if (!FtpSendCmd("PROT P",'2',mp_ftphandle)) return 0; + if (!FtpSendCmd("PROT P", '2', mp_ftphandle)) + { + return 0; + } break; default: return 0; @@ -1553,21 +2082,33 @@ int ftplib::NegotiateEncryption() { int ret; - if (!FtpSendCmd("AUTH TLS",'2',mp_ftphandle)) return 0; + if (!FtpSendCmd("AUTH TLS", '2', mp_ftphandle)) + { + return 0; + } mp_ftphandle->sbio = BIO_new_socket(mp_ftphandle->handle, BIO_NOCLOSE); - SSL_set_bio(mp_ftphandle->ssl,mp_ftphandle->sbio,mp_ftphandle->sbio); + SSL_set_bio(mp_ftphandle->ssl, mp_ftphandle->sbio, mp_ftphandle->sbio); ret = SSL_connect(mp_ftphandle->ssl); - if (ret == 1) mp_ftphandle->tlsctrl = 1; - + if (ret == 1) + { + mp_ftphandle->tlsctrl = 1; + } + if (mp_ftphandle->certcb != NULL) { X509 *cert = SSL_get_peer_certificate(mp_ftphandle->ssl); - if (!mp_ftphandle->certcb(mp_ftphandle->cbarg, cert)) return 0; + if (!mp_ftphandle->certcb(mp_ftphandle->cbarg, cert)) + { + return 0; + } + } + + if (ret < 1) + { + return 0; } - - if (ret < 1) return 0; return 1; } @@ -1576,9 +2117,9 @@ void ftplib::SetCallbackCertFunction(FtpCallbackCert pointer) { mp_ftphandle->certcb = pointer; } - #endif + void ftplib::SetCallbackIdleFunction(FtpCallbackIdle pointer) { mp_ftphandle->idlecb = pointer; @@ -1617,7 +2158,7 @@ void ftplib::SetConnmode(connmode mode) void ftplib::ClearHandle() { - mp_ftphandle->dir = FTPLIB_CONTROL; + mp_ftphandle->dir = ftphandle::FTPLIB_CONTROL; mp_ftphandle->ctrl = NULL; mp_ftphandle->cmode = ftplib::pasv; mp_ftphandle->idlecb = NULL; @@ -1626,11 +2167,13 @@ void ftplib::ClearHandle() mp_ftphandle->xfered = 0; mp_ftphandle->xfered1 = 0; mp_ftphandle->cbbytes = 0; -#ifndef NOSSL + +#ifndef NOSSL mp_ftphandle->tlsctrl = 0; mp_ftphandle->tlsdata = 0; mp_ftphandle->certcb = NULL; #endif + mp_ftphandle->offset = 0; mp_ftphandle->handle = 0; mp_ftphandle->logcb = NULL; @@ -1645,36 +2188,48 @@ int ftplib::CorrectPasvResponse(unsigned char *v) if (getpeername(mp_ftphandle->handle, &ipholder, &ipholder_size) == -1) { - perror("getpeername"); + perror("getpeername()"); net_close(mp_ftphandle->handle); return 0; } - - for (int i = 2; i < 6; i++) v[i] = ipholder.sa_data[i]; - + + for (int i = 2; i < 6; i++) + { + v[i] = static_cast(ipholder.sa_data[i]); + } + return 1; } + +#ifdef SUPPORT_ROW_FTP ftphandle* ftplib::RawOpen(const char *path, accesstype type, transfermode mode) { int ret; ftphandle* datahandle; - ret = FtpAccess(path, type, mode, mp_ftphandle, &datahandle); - if (ret) return datahandle; - else return NULL; + ret = FtpAccess(path, type, mode, mp_ftphandle, &datahandle); + if (ret) + { + return datahandle; + } + else + { + return NULL; + } } int ftplib::RawClose(ftphandle* handle) { return FtpClose(handle); -} +} -int ftplib::RawWrite(void* buf, int len, ftphandle* handle) +ssize_t ftplib::RawWrite(void* buf, size_t len, ftphandle* handle) { return FtpWrite(buf, len, handle); } -int ftplib::RawRead(void* buf, int max, ftphandle* handle) +ssize_t ftplib::RawRead(void* buf, size_t max, ftphandle* handle) { return FtpRead(buf, max, handle); } +#endif diff --git a/ftplib.h b/ftplib.h old mode 100755 new mode 100644 index 14622d6..85f5b18 --- a/ftplib.h +++ b/ftplib.h @@ -9,19 +9,19 @@ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * - * it under the terms of the GNU Lesser General Public License as * + * it under the terms of the GNU Lesser General Public License as * * published by the Free Software Foundation; either version 2.1 of the * * License, or (at your option) any later version. * * * ***************************************************************************/ - + /*************************************************************************** * Note: ftplib, on which ftplibpp was originally based upon used to be * * licensed as GPL 2.0 software, as of Jan. 26th 2013 its author Thomas * * Pfau allowed the distribution of ftplib via LGPL. Thus the license of * * ftplibpp changed aswell. * ***************************************************************************/ - + #ifndef FTPLIB_H #define FTPLIB_H @@ -33,29 +33,40 @@ # define DLLIMPORT __declspec (dllimport) #endif /* Not BUILDING_DLL */ -#include +#include // struct timeval #endif #ifndef _WIN32 #include +#include // PATH_MAX #include #endif -#ifdef NOLFS -#define off64_t long + +#include // _POSIX_XXX_MAX values +#include // off64_t/off_t +#include // PATH_MAX + +#ifndef PATH_MAX +#error "PATH_MAX undefined!" #endif #if defined(__APPLE__) -#define off64_t __darwin_off_t +#undef NOLFS +#define NOSSL +typedef off_t off64_t; #define fseeko64 fseeko #define fopen64 fopen #endif +#ifdef NOLFS +typedef long off64_t; +#endif + #ifndef NOSSL #include #endif -using namespace std; /** *@author mkulke @@ -69,12 +80,21 @@ typedef void (*FtpCallbackLog)(char *str, void* arg, bool out); typedef bool (*FtpCallbackCert)(void *arg, X509 *cert); #endif -struct ftphandle { - char *cput,*cget; +struct ftphandle +{ + /* FTP io types */ + enum direction + { + FTPLIB_CONTROL = 0, + FTPLIB_READ = 1, + FTPLIB_WRITE = 2 + }; + + char *cput, *cget; int handle; - int cavail,cleft; + size_t cavail, cleft; char *buf; - int dir; + direction dir; ftphandle *ctrl; int cmode; struct timeval idletime; @@ -85,7 +105,8 @@ struct ftphandle { off64_t xfered; off64_t cbbytes; off64_t xfered1; - char response[256]; + char response[PATH_MAX]; + #ifndef NOSSL SSL* ssl; SSL_CTX* ctx; @@ -94,26 +115,30 @@ struct ftphandle { int tlsdata; FtpCallbackCert certcb; #endif + off64_t offset; bool correctpasv; }; -#if defined(_WIN32) -class DLLIMPORT ftplib { +#if defined(_WIN32) +class DLLIMPORT ftplib +{ #else -class ftplib { +class ftplib +{ #endif public: enum accesstype { + mlsd = 0, dir = 1, dirverbose, fileread, filewrite, filereadappend, filewriteappend - }; + }; enum transfermode { @@ -130,77 +155,89 @@ class ftplib { enum fxpmethod { defaultfxp = 0, - alternativefxp + alternativefxp }; - enum dataencryption - { - unencrypted = 0, - secure - }; + enum dataencryption + { + unencrypted = 0, + secure + }; ftplib(); ~ftplib(); - char* LastResponse(); - int Connect(const char *host); - int Login(const char *user, const char *pass); - int Site(const char *cmd); - int Raw(const char *cmd); - int SysType(char *buf, int max); - int Mkdir(const char *path); - int Chdir(const char *path); - int Cdup(); - int Rmdir(const char *path); - int Pwd(char *path, int max); - int Nlst(const char *outputfile, const char *path); - int Dir(const char *outputfile, const char *path); - int Size(const char *path, int *size, transfermode mode); - int ModDate(const char *path, char *dt, int max); - int Get(const char *outputfile, const char *path, transfermode mode, off64_t offset = 0); - int Put(const char *inputfile, const char *path, transfermode mode, off64_t offset = 0); - int Rename(const char *src, const char *dst); - int Delete(const char *path); -#ifndef NOSSL + char* LastResponse(); + int Connect(const char *host); + int Login(const char *user, const char *pass); + int Site(const char *cmd); + int Raw(const char *cmd); + int SysType(char *buf, int max); + int Mkdir(const char *path); + int Chdir(const char *path); + int Cdup(); + int Rmdir(const char *path); + int Pwd(char *path, int max); + int Mlsd(const char *outputfile, const char *path); + int Nlst(const char *outputfile, const char *path); + int Dir(const char *outputfile, const char *path); + int Size(const char *path, off64_t *size, transfermode mode = image); + int ModDate(const char *path, char *dt, size_t max); + int Get(const char *outputfile, const char *path, transfermode mode, off64_t offset = 0); + int Put(const char *inputfile, const char *path, transfermode mode, off64_t offset = 0); + int Rename(const char *src, const char *dst); + int Delete(const char *path); + +#ifndef NOSSL int SetDataEncryption(dataencryption enc); - int NegotiateEncryption(); + int NegotiateEncryption(); void SetCallbackCertFunction(FtpCallbackCert pointer); #endif - int Quit(); - void SetCallbackIdleFunction(FtpCallbackIdle pointer); - void SetCallbackLogFunction(FtpCallbackLog pointer); + + int Quit(); + void SetCallbackIdleFunction(FtpCallbackIdle pointer); + void SetCallbackLogFunction(FtpCallbackLog pointer); void SetCallbackXferFunction(FtpCallbackXfer pointer); void SetCallbackArg(void *arg); - void SetCallbackBytes(off64_t bytes); - void SetCorrectPasv(bool b) { mp_ftphandle->correctpasv = b; }; - void SetCallbackIdletime(int time); - void SetConnmode(connmode mode); - static int Fxp(ftplib* src, ftplib* dst, const char *pathSrc, const char *pathDst, transfermode mode, fxpmethod method); - - ftphandle* RawOpen(const char *path, accesstype type, transfermode mode); - int RawClose(ftphandle* handle); - int RawWrite(void* buf, int len, ftphandle* handle); - int RawRead(void* buf, int max, ftphandle* handle); + void SetCallbackBytes(off64_t bytes); + void SetCorrectPasv(bool b) + { + mp_ftphandle->correctpasv = b; + }; + void SetCallbackIdletime(int time); + void SetConnmode(connmode mode); + +#ifdef SUPPORT_FXP_FTP + static int Fxp(ftplib* src, ftplib* dst, const char *pathSrc, const char *pathDst, transfermode mode, fxpmethod method); +#endif private: - ftphandle* mp_ftphandle; - - int FtpXfer(const char *localfile, const char *path, ftphandle *nControl, accesstype type, transfermode mode); - int FtpOpenPasv(ftphandle *nControl, ftphandle **nData, transfermode mode, int dir, char *cmd); - int FtpSendCmd(const char *cmd, char expresp, ftphandle *nControl); - int FtpAcceptConnection(ftphandle *nData, ftphandle *nControl); - int FtpOpenPort(ftphandle *nControl, ftphandle **nData, transfermode mode, int dir, char *cmd); - int FtpRead(void *buf, int max, ftphandle *nData); - int FtpWrite(void *buf, int len, ftphandle *nData); - int FtpAccess(const char *path, accesstype type, transfermode mode, ftphandle *nControl, ftphandle **nData); - int FtpClose(ftphandle *nData); - + +#ifdef SUPPORT_ROW_FTP + ftphandle* RawOpen(const char *path, accesstype type, transfermode mode); + int RawClose(ftphandle* handle); + ssize_t RawWrite(void* buf, size_t len, ftphandle* handle); + ssize_t RawRead(void* buf, size_t max, ftphandle* handle); +#endif + + ftphandle* mp_ftphandle; + + int FtpXfer(const char *localfile, const char *path, ftphandle *nControl, accesstype type, transfermode mode); + int FtpOpenPasv(ftphandle *nControl, ftphandle **nData, transfermode mode, ftphandle::direction dir, char *cmd); + int FtpSendCmd(const char *cmd, char expresp, ftphandle *nControl); + int FtpAcceptConnection(ftphandle *nData, ftphandle *nControl); + int FtpOpenPort(ftphandle *nControl, ftphandle **nData, transfermode mode, ftphandle::direction dir, char *cmd); + ssize_t FtpRead(void *buf, size_t max, ftphandle *nData); + ssize_t FtpWrite(void *buf, size_t len, ftphandle *nData); + int FtpAccess(const char *path, accesstype type, transfermode mode, ftphandle *nControl, ftphandle **nData); + int FtpClose(ftphandle *nData); + int socket_wait(ftphandle *ctl); - int readline(char *buf,int max,ftphandle *ctl); - int writeline(char *buf, int len, ftphandle *nData); - int readresp(char c, ftphandle *nControl); - + int readline(char *buf, size_t max, ftphandle *ctl); + int writeline(char *buf, size_t len, ftphandle *nData); + int readresp(char c, ftphandle *nControl); + void ClearHandle(); int CorrectPasvResponse(unsigned char *v); }; -#endif \ No newline at end of file +#endif diff --git a/sample/Makefile b/sample/Makefile new file mode 100644 index 0000000..2b243a0 --- /dev/null +++ b/sample/Makefile @@ -0,0 +1,21 @@ +.PHONY: all clean test +all: sample + +sample.o: sample.cpp ../ftplib.h + $(COMPILE.cc) -I.. $< + +sample: sample.o ../libftp++.a + $(LINK.cc) -L.. -o $@ $^ ### -lftp++ + +test: sample + ./sample localhost:2121 + while test ! -f sample.bin; do \ + ./sample localhost:2121; \ + done + diff sample sample.bin + -./sample localhost + -./sample + +clean: + rm -f sample sample.o sample.bin sample.download + diff --git a/sample/sample.cpp b/sample/sample.cpp index 7025191..2d64bfb 100644 --- a/sample/sample.cpp +++ b/sample/sample.cpp @@ -1,11 +1,218 @@ #include "ftplib.h" -int main(void) -{ - ftplib *ftp = new ftplib::ftplib(); - ftp->Connect("ftp.gwdg.de:21"); - ftp->Login("anonymous", ""); - ftp->Dir(NULL, "/ftp/pub"); - ftp->Quit(); - return 0; +#include +#include + +#include +#include + + +// get size of a local file +off_t Size(const std::string filename) +{ + off_t offset(0); + FILE * in(fopen(filename.c_str(), "rb")); + if (in) + { + if (fseek(in, 0, SEEK_END) == 0) + { + offset = ftello(in); + } + fclose(in); + fprintf(stderr, "localfile: -%s- size: %lld Bytes.\n", filename.c_str(), offset); + } + else + { + perror(filename.c_str()); + } + return offset; +} + + +class myclass +{ + off_t transferCount; + +public: + myclass() : transferCount(0) {}; + + static int callbackIdle(void * arg); + static void callbackLog(char *str, void* arg, bool out); + static int callback(off64_t xfered, void* arg); // static callback function + int mymethod(off64_t xfered); // common class method +}; + + +int myclass::mymethod(off64_t xfered) +{ + fprintf(stderr, "callback(%lld)\n", xfered); + transferCount = xfered; + if (transferCount < (32 * 1024)) + { + return true; // continue + } + else + { + return false; // break transfer + } +} + + +int myclass::callback(off64_t xfered, void* arg) +{ + // casting the pointers to the correct type and calling class method + return static_cast(arg)->mymethod(xfered); +} + + +void myclass::callbackLog(char *str, void* , bool in) +{ + if (in) + { + fprintf(stderr, "ftplib IN:%s", str); + } + else + { + fprintf(stderr, "ftplib OUT:%s", str); + } +} + + +int myclass::callbackIdle(void * arg) +{ + fprintf(stderr, "callbackIdle()\n"); + if (static_cast(arg)->transferCount < (32 * 1024)) + { + return true; // continue transfer + } + else + { + return false; // break transfer + } +} + + +int main(int argc, char** argv) +{ + char buf[80] = {0}; + off64_t offset = 0; + int ok = 1; + + ftplib *myftp = new ftplib::ftplib(); + + if (argc > 1) + { + myftp->SetConnmode(ftplib::port); + ok = myftp->Connect(argv[1]); + } + else + { + myftp->SetConnmode(ftplib::pasv); + // ftp://ftp.gwdg.de/pub/incoming/ + ok = myftp->Connect("ftp.gwdg.de"); //TODO /pub/incoming/ + } + + if (ok && myftp->Login("ftp", "OK")) + { + const std::string filename("sample"); // filename for upload and download test + + myftp->Cdup(); + if (myftp->Pwd(buf, sizeof(buf))) + { + puts(buf); + } + if (myftp->SysType(buf, sizeof(buf))) + { + puts(buf); + } + + if (!myftp->Chdir("/pub/incoming/tmp")) + { + if (!myftp->Mkdir("/pub/incoming/tmp")) + { + perror("ftplib::Mkdir()"); + } + } + else + { + myftp->Put("README.md", "/pub/incoming/tmp/README.txt", ftplib::ascii); + myftp->Rmdir("/pub/incoming/tmp"); + } + + myftp->Dir("ftp-dir.txt", "/pub/incoming/"); + myftp->Nlst("ftp-list.txt", "/pub/incoming/"); + myftp->Mlsd("ftp-mlsd.txt", "/pub/incoming/"); + + // check the mod time of the remote file + if (myftp->ModDate("/pub/incoming/sample", buf, sizeof(buf))) + { + struct tm dt; + memset(&dt, 0, sizeof(struct tm)); + + if (strptime(buf, "%Y%m%d%H%M%S", &dt) == NULL) + { + perror("strptime()"); + perror(buf); + } + else + { + //TODO: if older than local file, remove remote files first + time_t seconds = mktime(&dt); + if ((time(NULL) - seconds) > 60) + { + myftp->Delete("/pub/incoming/sample"); + myftp->Delete("/pub/incoming/sample.upload"); + } + } + } + + myclass helper; + myftp->SetCallbackArg(&helper); // supply the myftp object the pointer to the current (myclass) object + myftp->SetCallbackBytes(8 * 1024); // issue a xfer callback every 8 kb + myftp->SetCallbackXferFunction(myclass::callback); + myftp->SetCallbackIdletime(1000); // ms; issue a callback after 1 sec idle time + myftp->SetCallbackIdleFunction(myclass::callbackIdle); + myftp->SetCallbackLogFunction(myclass::callbackLog); + + myftp->SetCorrectPasv(true); + + // ========================= + // upload + // get remote file size first + myftp->Size("/pub/incoming/sample.upload", &offset); + if (!myftp->Put("sample", "/pub/incoming/sample.upload", ftplib::image, offset)) + { + perror("ftplib::Put()"); + //FIXME exit(EXIT_FAILURE); + } + else if (myftp->Chdir("/pub/incoming/")) + { + myftp->Rename("sample.upload", filename.c_str()); + } + + // ========================= + // download + // get local file size first + offset = Size("sample.download"); + if (!myftp->Get("sample.download", filename.c_str(), ftplib::image, offset)) + { + perror("ftplib::Get()"); + //FIXME exit(EXIT_FAILURE); + } + else if (rename("sample.download", "sample.bin")) + { + perror("rename()"); + } + + myftp->Cdup(); + myftp->Quit(); + } + + delete myftp; + + if (ok) + { + exit(EXIT_SUCCESS); + } + exit(EXIT_FAILURE); } From e09f5b2a9df35c6dbb866130ae5035cea81d80da Mon Sep 17 00:00:00 2001 From: Claus Klein Date: Tue, 7 Jul 2015 07:41:20 +0200 Subject: [PATCH 2/2] support IPV6 extented passive ftp too prevent all warnings added sample/sample.cpp and sample/Makefile to test ca 70% of code (tcov) --- .gitignore | 2 + Makefile | 8 +- README.md | 14 +- ToDo | 2 +- ftplib.cpp | 397 +++++++++++++++++++++++++++++----------------- ftplib.h | 3 +- sample/Makefile | 9 +- sample/sample.cpp | 36 +++-- 8 files changed, 302 insertions(+), 169 deletions(-) diff --git a/.gitignore b/.gitignore index a993417..b78b9b5 100644 --- a/.gitignore +++ b/.gitignore @@ -8,6 +8,8 @@ *.gcno *.html sample/sample +sample/sample.bin +sample/sample.download *.o diff --git a/Makefile b/Makefile index bf22610..8f17aa6 100755 --- a/Makefile +++ b/Makefile @@ -7,11 +7,12 @@ TARGETS := libftp++.a ###XXX libftp++.so OBJECTS := ftplib.o SOURCES := ftplib.cpp DEFINES += ###TBD -DNOSSL +INCLUDES := -I/opt/local/include DEBUG := -g CXXWARNFLAGS := -Wold-style-cast -Wsign-conversion -Wconversion -Wsign-compare -Wpointer-arith -CXXFLAGS := -std=c++98 -Wall -Wextra $(CXXWARNFLAGS) $(DEBUG) -I. $(INCLUDES) $(DEFINES) -LDFLAGS := -L. +CXXFLAGS := -std=c++98 -Wall -Wextra $(CXXWARNFLAGS) $(DEBUG) -I$(CURDIR) $(INCLUDES) $(DEFINES) +LDFLAGS := -L$(CURDIR) ifdef TCOV CXXFLAGS+= --coverage @@ -27,12 +28,13 @@ endif .PHONY: all clean distclean depend install uninstall astyle doc tcov all : $(TARGETS) doc - $(MAKE) -C sample test 'CXXFLAGS=$(CXXFLAGS)' + $(MAKE) -C sample test 'CXXFLAGS=$(CXXFLAGS)' 'LDFLAGS=$(LDFLAGS) $(LIBS)' tcov: TCOV=1 tcov: CXXFLAGS+= --coverage tcov: all gcov ftplib + gcov sample/sample astyle : -astyle --style=ansi -t4 *.cpp *.h sample/sample.cpp diff --git a/README.md b/README.md index 84d1b9d..da83cae 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,7 @@ advanced features like resuming, fxp, SSL/TLs encryption, large file support, or very base of ftplibpp is Thomas Pfau's [ftplib c library](http://nbpfaus.net/%7Epfau/ftplib/). Every FTP session is represented by an ftplib object, whose methods are called to communicate with the FTP server. The -FTP sessions should begin with a call to *myftp.Connect("myftp.org:21")* (and maybe *myftp.NegotiateEncryption()* ), be +FTP sessions should begin with a call to *myftp.Connect("myftp.org")* (and maybe *myftp.NegotiateEncryption()* ), be followed with *myftp.Login("myuser","mypass")* and ended by *myftp.Quit()*. For the magic in between, read the class methods documentation. Most methods have their tasks pretty much explained in their name. ftplibpp uses OpenSSL for encryption functionality, if you don't need it you can set the "NOSSL" flag (e.g. *g++ -c ftplib.cpp -DNOSSL* ). If your @@ -39,7 +39,7 @@ use the "NOLFS" flag (e.g. g++ -c ftplib.cpp -DNOLFS* ). * [ftplib ()](#ftplib) * [char* LastResponse ()](#lastresponse) -* [int Connect (const char *host)](#connect) +* [int Connect (const char *host, const char *port)](#connect) * [int Login (const char *user, const char *pass)](#login) * [int Site (const char *cmd)](#site) * [int Raw (const char *cmd)](#raw) @@ -172,11 +172,13 @@ LastResponse() returns a pointer to the last server response string. Otherwise, -###int Connect ( const char* host ) +###int Connect ( const char* host, const char *port) -Connect() establishes a connection to the FTP server on the specified machine and returns a handle which can be used to -initiate data transfers. The host name should be specified in the form of `:`. `` may be either a host name or ip -address. `` may be either a service name or a port number. +Connect() establishes a connection to the FTP server on the specified machine +and returns a handle which can be used to initiate data transfers. +The host name may be either a host name or ip address. +The port may be either a service name or a port. +number. ####Parameters: diff --git a/ToDo b/ToDo index 727e1f4..f8a1d4a 100644 --- a/ToDo +++ b/ToDo @@ -1,3 +1,3 @@ - implement up and download speed limitation. - create unit test suite -- support IPV6 (AF_INET6) +- support IPV6 (AF_INET6) for Fxp() too! diff --git a/ftplib.cpp b/ftplib.cpp index 52f7736..4f63929 100644 --- a/ftplib.cpp +++ b/ftplib.cpp @@ -1,10 +1,13 @@ +static bool verbose = true; + // enable > 2gb support (LFS) #ifndef NOLFS #define _LARGEFILE_SOURCE #define _LARGEFILE64_SOURCE #endif +#include #include "ftplib.h" #if defined(_WIN32) @@ -17,6 +20,7 @@ #include #endif +#include #include #include #include @@ -42,15 +46,24 @@ #if defined(_WIN32) typedef int socklen_t; +typedef unsigned short in_port_t; #endif -#if defined(_WIN32) -#define memccpy _memccpy -#define strdup _strdup -#endif +/* copies bytes until a specifed character is found, or a maximum number of + * characters have been copied. */ +static char * memccpy(char * dest, const char * src, int c, size_t count) +{ + while (count && (*(dest++) = *(src++)) + != static_cast(c)) + { + count--; + } + + return (count ? dest : NULL); +} -/* socket values */ -#define FTPLIB_BUFSIZ (16 * 1024) +/* socket buffer size */ +#define FTPLIB_BUFSIZ (20 * 1024) #define ACCEPT_TIMEOUT 30 /* win32 dll initializer */ @@ -75,6 +88,52 @@ BOOL APIENTRY DllMain(HINSTANCE hInst, DWORD reason, LPVOID reserved) } #endif + +/* + * Translate IPv4 mapped IPv6 address to IPv4 address + * see /usr/include/netinet6/in6.h + */ +static void +unmappedaddr(struct sockaddr_in6 *sin6) +{ + struct sockaddr_in *sin4; + in_addr_t addr; + in_port_t port; + + if (sin6->sin6_family != AF_INET6 || + !IN6_IS_ADDR_V4MAPPED(&sin6->sin6_addr)) + { + +#ifdef BSD + assert(sin6->sin6_len == sizeof(struct sockaddr_in6)); +#endif + + return; + } + + sin4 = reinterpret_cast(sin6); + addr = *reinterpret_cast(&sin6->sin6_addr.s6_addr[12]); + port = sin6->sin6_port; + memset(sin4, 0, sizeof(struct sockaddr_in)); + sin4->sin_addr.s_addr = addr; + sin4->sin_port = port; + sin4->sin_family = AF_INET; + +#ifdef BSD + sin4->sin_len = sizeof(struct sockaddr_in); +#endif + + if (verbose) + { + char str[INET6_ADDRSTRLEN]; + warnx("unmappedaddr %s", inet_ntop(sin4->sin_family, &sin4->sin_addr, str, INET6_ADDRSTRLEN)); + } + + return; +} + + + /* * Constructor */ @@ -97,14 +156,14 @@ ftplib::ftplib() : mp_ftphandle(NULL) if (mp_ftphandle == NULL) { perror("calloc()"); - exit(1); //FIXME: can`t continue! ck + exit(1); //TODO: can`t continue! ck } mp_ftphandle->buf = static_cast(malloc(FTPLIB_BUFSIZ)); if (mp_ftphandle->buf == NULL) { perror("malloc()"); free(mp_ftphandle); - exit(1); //FIXME: can`t continue! ck + exit(1); //TODO: can`t continue! ck } #ifndef NOSSL @@ -211,7 +270,7 @@ int ftplib::readline(char *buf, size_t max, ftphandle *ctl) if (ctl->cavail > 0) { x = (max >= ctl->cavail) ? ctl->cavail : max - 1; - end = static_cast(memccpy(bp, ctl->cget, '\n', x)); + end = memccpy(bp, ctl->cget, '\n', x); if (end != NULL) { x = static_cast(end - bp); @@ -225,7 +284,7 @@ int ftplib::readline(char *buf, size_t max, ftphandle *ctl) if (end != NULL) { bp -= 2; - if (strcmp(bp, "\r\n") == 0) //XXX ascii mode + if (strcmp(bp, "\r\n") == 0) // NOTE: ascii mode { *bp++ = '\n'; *bp++ = '\0'; @@ -264,13 +323,13 @@ int ftplib::readline(char *buf, size_t max, ftphandle *ctl) #ifndef NOSSL if (ctl->tlsdata) { - len = SSL_read(ctl->ssl, ctl->cput, ctl->cleft); + len = SSL_read(ctl->ssl, ctl->cput, static_cast(ctl->cleft)); } else { if (ctl->tlsctrl) { - len = SSL_read(ctl->ssl, ctl->cput, ctl->cleft); + len = SSL_read(ctl->ssl, ctl->cput, static_cast(ctl->cleft)); } else { @@ -325,7 +384,7 @@ int ftplib::writeline(char *buf, size_t len, ftphandle *nData) { return -1; } - nbp = nData->buf; //XXX ascii buffer + nbp = nData->buf; // NOTE: ascii buffer for (x = 0; x < len; x++) { if ((*ubp == '\n') && (lc != '\r')) @@ -398,7 +457,7 @@ int ftplib::writeline(char *buf, size_t len, ftphandle *nData) #ifndef NOSSL if (nData->tlsctrl) { - w = SSL_write(nData->ssl, nbp, nb); + w = SSL_write(nData->ssl, nbp, static_cast(nb)); } else { @@ -472,21 +531,8 @@ char* ftplib::LastResponse() * * return 1 if connected, 0 if not */ -int ftplib::Connect(const char *host) +int ftplib::Connect(const char *host, const char *port) { - int sControl; - union - { - struct sockaddr sa; - struct sockaddr_in in; - } sin; - struct hostent *phe; - struct servent *pse; - int on = 1; - int ret; - char *lhost; - char *pnum; - mp_ftphandle->dir = ftphandle::FTPLIB_CONTROL; mp_ftphandle->ctrl = NULL; mp_ftphandle->xfered = 0; @@ -500,75 +546,54 @@ int ftplib::Connect(const char *host) mp_ftphandle->offset = 0; mp_ftphandle->handle = 0; - memset(&sin, 0, sizeof(sin)); - sin.in.sin_family = AF_INET; - lhost = strdup(host); - pnum = strchr(lhost, ':'); - if (pnum == NULL) - { - if ((pse = getservbyname("ftp", "tcp")) == NULL) - { - perror("getservbyname()"); - free(lhost); - return 0; - } - sin.in.sin_port = static_cast(pse->s_port); - } - else - { - *pnum++ = '\0'; - if (isdigit(*pnum)) - { - sin.in.sin_port = htons(atoi(pnum)); - } - else - { - if ((pse = getservbyname(pnum, "tcp")) == NULL); - { - perror("getservbyname()"); - free(lhost); - return 0; - } - sin.in.sin_port = static_cast(pse->s_port); - } - } + int sControl = -1; + struct addrinfo hints, *res, *res0 = NULL; + int error; + const char *cause = NULL; -#if defined(_WIN32) - if ((sin.in.sin_addr.s_addr = inet_addr(lhost)) == -1) + memset(&hints, 0, sizeof(hints)); + +#ifdef __INTEGRITY +#warning "AI_NUMERICSERV not supported!" + hints.ai_flags = AI_NUMERICHOST; #else - ret = inet_aton(lhost, &sin.in.sin_addr); - if (ret == 0) + //TODO: hints.ai_flags = AI_NUMERICHOST | AI_NUMERICSERV; #endif - { - if ((phe = gethostbyname(lhost)) == NULL) - { - perror("gethostbyname()"); - free(lhost); - return 0; - } - memcpy(&sin.in.sin_addr, phe->h_addr, static_cast(phe->h_length)); - } - - free(lhost); + hints.ai_family = PF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + error = getaddrinfo(host, port, &hints, &res0); - sControl = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); - if (sControl == -1) + if (error) { - perror("socket()"); + warnx("getaddrinfo(%s): %s", host, gai_strerror(error)); + freeaddrinfo(res0); return 0; } - - if (setsockopt(sControl, SOL_SOCKET, SO_REUSEADDR, SETSOCKOPT_OPTVAL_TYPE(& on), sizeof(on)) == -1) + for (res = res0; res; res = res->ai_next) { - perror("setsockopt()"); - net_close(sControl); - return 0; + sControl = socket(res->ai_family, res->ai_socktype, + res->ai_protocol); + if (sControl < 0) + { + cause = "socket"; + continue; + } + + if (connect(sControl, res->ai_addr, res->ai_addrlen) < 0) + { + cause = "connect"; + close(sControl); + sControl = -1; + continue; + } + + break; /* okay we got one */ } - if (connect(sControl, &sin.sa, sizeof(sin.sa)) == -1) + freeaddrinfo(res0); + if (sControl < 0) { - perror("connect()"); - net_close(sControl); + warn("%s", cause); return 0; } @@ -608,7 +633,7 @@ int ftplib::FtpSendCmd(const char *cmd, char expresp, ftphandle *nControl) #ifndef NOSSL if (nControl->tlsctrl) { - x = SSL_write(nControl->ssl, buf, strlen(buf)); + x = SSL_write(nControl->ssl, buf, static_cast(strlen(buf))); } else { @@ -711,7 +736,7 @@ int ftplib::FtpAcceptConnection(ftphandle *nData, ftphandle *nControl) sData = accept(nData->handle, &addr, &l); i = errno; // save errno before close net_close(nData->handle); - if (sData >= 0) //FIXME accept return -1 on error! + if (sData >= 0) // NOTE: accept return -1 on error! { rv = 1; // OK nData->handle = sData; @@ -816,6 +841,7 @@ int ftplib::FtpAccess(const char *path, accesstype type, transfermode mode, ftph { return 0; } + assert(*nData); if (!FtpAcceptConnection(*nData, nControl)) { FtpClose(*nData); @@ -824,6 +850,8 @@ int ftplib::FtpAccess(const char *path, accesstype type, transfermode mode, ftph } } + assert(*nData); + #ifndef NOSSL if (nControl->tlsdata) { @@ -843,18 +871,23 @@ int ftplib::FtpAccess(const char *path, accesstype type, transfermode mode, ftph } /* - * FtpOpenPort - Establishes a PORT connection for data transfer + * FtpOpenPort - Establishes a EPRT connection for data transfer * * return 1 if successful, -1 otherwise */ int ftplib::FtpOpenPort(ftphandle *nControl, ftphandle **nData, transfermode mode, ftphandle::direction dir, char *cmd) { int sData; + + // NOTE: prevent casts union { struct sockaddr sa; - struct sockaddr_in in; - } sin; + struct sockaddr_storage ss; + struct sockaddr_in sin4; + struct sockaddr_in6 sin6; + } sin = {}; + struct linger lng = { 0, 0 }; socklen_t l; int on = 1; @@ -875,15 +908,24 @@ int ftplib::FtpOpenPort(ftphandle *nControl, ftphandle **nData, transfermode mod sprintf(nControl->response, "Invalid mode %c\n", mode); return -1; } - l = sizeof(sin); - if (getsockname(nControl->handle, &sin.sa, &l) < 0) + /* find our own address, bind, and listen */ + //FIXME int getsockname(int, struct sockaddr *, socklen_t *) + l = sizeof(sin.ss); // NOTE: sizeof(socket_storage) used + if (getsockname(nControl->handle, &sin.sa, &l) == -1) { perror("getsockname()"); return -1; } - sData = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sin.ss.ss_family == AF_INET6) + { + char str[INET6_ADDRSTRLEN]; + warnx("sockname: %s", inet_ntop(sin.ss.ss_family, &sin.sin6.sin6_addr, str, INET6_ADDRSTRLEN)); + unmappedaddr(&sin.sin6); + } + + sData = socket(sin.ss.ss_family, SOCK_STREAM, IPPROTO_TCP); if (sData == -1) { perror("socket()"); @@ -902,31 +944,60 @@ int ftplib::FtpOpenPort(ftphandle *nControl, ftphandle **nData, transfermode mod return -1; } - sin.in.sin_port = 0; - if (bind(sData, &sin.sa, sizeof(sin)) == -1) +#if defined(BSD) + if (bind(sData, &sin.sa, sin.sa.sa_len) == -1) +#else + if (bind(sData, &sin.sa, sizeof(sin.ss)) == -1) +#endif { perror("bind()"); net_close(sData); return -1; } - if (listen(sData, 1) < 0) + if (listen(sData, 1) == -1) { perror("listen()"); net_close(sData); return -1; } - if (getsockname(sData, &sin.sa, &l) < 0) + + /* find what port we're on and tell the server */ + //FIXME int getsockname(int, struct sockaddr *, socklen_t *) + l = sizeof(sin.ss); // NOTE: sizeof(socket_storage) used + if (getsockname(sData, &sin.sa, &l) == -1) + { + perror("getsockname()"); + net_close(sData); + return -1; + } + + // we use EPRT (Extended Port command)! ck + // (RFC 2428) In this specification, the FTP commands PORT and PASV are + // replaced with EPRT and EPSV, respectively. + switch (sin.ss.ss_family) + { + case AF_INET: + { + // EPRT |1|132.235.1.2|6275| + char hname[INET6_ADDRSTRLEN]; + inet_ntop(sin.ss.ss_family, &sin.sin4.sin_addr, hname, INET6_ADDRSTRLEN); + sprintf(buf, "EPRT |%d|%s|%d|", 1, hname, htons(sin.sin4.sin_port)); + } + break; + case AF_INET6: { + // EPRT |2|1080::8:800:200C:417A|5282| + char hname[INET6_ADDRSTRLEN]; + assert(sin.sin6.sin6_scope_id == 0); + inet_ntop(sin.ss.ss_family, &sin.sin6.sin6_addr, hname, INET6_ADDRSTRLEN); + sprintf(buf, "EPRT |%d|%s|%d|", 2, hname, htons(sin.sin6.sin6_port)); + } + break; + default: net_close(sData); - return 0; + return -1; } - sprintf(buf, "PORT %hhu,%hhu,%hhu,%hhu,%hhu,%hhu", - static_cast(sin.sa.sa_data[2]), - static_cast(sin.sa.sa_data[3]), - static_cast(sin.sa.sa_data[4]), - static_cast(sin.sa.sa_data[5]), - static_cast(sin.sa.sa_data[0]), - static_cast(sin.sa.sa_data[1])); + if (!FtpSendCmd(buf, '2', nControl)) { net_close(sData); @@ -1001,24 +1072,32 @@ int ftplib::FtpOpenPort(ftphandle *nControl, ftphandle **nData, transfermode mod } /* - * FtpOpenPasv - Establishes a PASV connection for data transfer + * FtpOpenPasv - Establishes a EPSV connection for data transfer * * return 1 if successful, -1 otherwise */ int ftplib::FtpOpenPasv(ftphandle *nControl, ftphandle **nData, transfermode mode, ftphandle::direction dir, char *cmd) { int sData; + + // NOTE: prevent casts union { struct sockaddr sa; - struct sockaddr_in in; - } sin; + struct sockaddr_storage ss; + struct sockaddr_in sin4; + struct sockaddr_in6 sin6; + } sin = {}; + + char str[INET6_ADDRSTRLEN]; + struct linger lng = { 0, 0 }; socklen_t l; int on = 1; ftphandle *ctrl; char *cp; - unsigned char v[6]; + char v[6]; + in_port_t port = 0; ssize_t ret; if (nControl->dir != ftphandle::FTPLIB_CONTROL) @@ -1035,11 +1114,14 @@ int ftplib::FtpOpenPasv(ftphandle *nControl, ftphandle **nData, transfermode mod sprintf(nControl->response, "Invalid mode %c\n", mode); return -1; } - l = sizeof(sin); - memset(&sin, 0, l); - sin.in.sin_family = AF_INET; - if (!FtpSendCmd("PASV", '2', nControl)) + // When the EPSV command is issued with no argument, the server will choose + // the network protocol for the data connection based on the protocol used + // for the control connection. + //XXX if (!FtpSendCmd("EPSV 1", '2', nControl)) + //XXX if (!FtpSendCmd("EPSV 2", '2', nControl)) + //TBD if (!FtpSendCmd("EPSV All", '2', nControl)) + if (!FtpSendCmd("EPSV", '2', nControl)) { return -1; } @@ -1050,27 +1132,48 @@ int ftplib::FtpOpenPasv(ftphandle *nControl, ftphandle **nData, transfermode mod } cp++; -#if defined(_WIN32) - unsigned int v_i[6]; - sscanf(cp, "%u,%u,%u,%u,%u,%u", &v_i[2], &v_i[3], &v_i[4], &v_i[5], &v_i[0], &v_i[1]); - for (int i = 0; i < 6; i++) + // Entering Extended Passive Mode (|||6446|) + if (sscanf(cp, "%c%c%c%hu%c", &v[0], &v[1], &v[2], &port, &v[3]) != 5 || + v[0] != v[1] || v[0] != v[2] || v[0] != v[3]) { - v[i] = static_cast(v_i[i]); + return -1; // protocol error + } + + /* construct sockaddr for data socket */ + //FIXME int getpeername(int, struct sockaddr *, socklen_t *) + l = sizeof(sin.ss); // NOTE: sizeof(socket_storage) used + if (getpeername(nControl->handle, &sin.sa, &l) == -1) + { + perror("getpeername()"); + return -1; + } + + + if (sin.ss.ss_family == AF_INET6) + { + warnx("peername: %s", inet_ntop(sin.ss.ss_family, &sin.sin6.sin6_addr, str, INET6_ADDRSTRLEN)); + unmappedaddr(&sin.sin6); } -#else - sscanf(cp, "%hhu,%hhu,%hhu,%hhu,%hhu,%hhu", &v[2], &v[3], &v[4], &v[5], &v[0], &v[1]); -#endif - if (nControl->correctpasv) if (!CorrectPasvResponse(v)) + switch (sin.ss.ss_family) + { + case AF_INET6: + sin.sin6.sin6_port = htons(port); + if (verbose) { - return -1; + warnx("peer %s:%d", inet_ntop(sin.ss.ss_family, &sin.sin6.sin6_addr, str, INET6_ADDRSTRLEN), port); } - sin.sa.sa_data[2] = static_cast(v[2]); - sin.sa.sa_data[3] = static_cast(v[3]); - sin.sa.sa_data[4] = static_cast(v[4]); - sin.sa.sa_data[5] = static_cast(v[5]); - sin.sa.sa_data[0] = static_cast(v[0]); - sin.sa.sa_data[1] = static_cast(v[1]); + break; + case AF_INET: + sin.sin4.sin_port = htons(port); + if (verbose) + { + warnx("peer %s:%d", inet_ntop(sin.ss.ss_family, &sin.sin4.sin_addr, str, INET6_ADDRSTRLEN), port); + } + break; + default: + return -1; + } if (mp_ftphandle->offset != 0) { @@ -1082,7 +1185,7 @@ int ftplib::FtpOpenPasv(ftphandle *nControl, ftphandle **nData, transfermode mod } } - sData = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + sData = socket(sin.ss.ss_family, SOCK_STREAM, IPPROTO_TCP); if (sData == -1) { perror("socket()"); @@ -1111,7 +1214,7 @@ int ftplib::FtpOpenPasv(ftphandle *nControl, ftphandle **nData, transfermode mod #ifndef NOSSL if (nControl->tlsctrl) { - ret = SSL_write(nControl->ssl, cmd, strlen(cmd)); + ret = SSL_write(nControl->ssl, cmd, static_cast(strlen(cmd))); } else { @@ -1128,9 +1231,13 @@ int ftplib::FtpOpenPasv(ftphandle *nControl, ftphandle **nData, transfermode mod return -1; } - if (connect(sData, &sin.sa, sizeof(sin.sa)) == -1) +#if defined(BSD) + if (connect(sData, &sin.sa, sin.sa.sa_len) == -1) +#else + if (connect(sData, &sin.sa, sizeof(sin.ss)) == -1) +#endif { - perror("connect()"); + perror("connect"); net_close(sData); return -1; } @@ -1195,7 +1302,7 @@ int ftplib::FtpClose(ftphandle *nData) { if (nData->buf != NULL) { - writeline(NULL, 0, nData); //XXX ascii mode + writeline(NULL, 0, nData); // NOTE: ascii mode } } else if (nData->dir != ftphandle::FTPLIB_READ) @@ -1236,19 +1343,19 @@ ssize_t ftplib::FtpRead(void *buf, size_t max, ftphandle *nData) } if (nData->buf) { - i = readline(static_cast(buf), max, nData); //XXX ascii mode + i = readline(static_cast(buf), max, nData); // NOTE: ascii mode } else { if (!socket_wait(nData)) { - return -1; // error + return -1; // error forced by callback } #ifndef NOSSL if (nData->tlsdata) { - i = SSL_read(nData->ssl, buf, max); + i = SSL_read(nData->ssl, buf, static_cast(max)); } else { @@ -1272,7 +1379,7 @@ ssize_t ftplib::FtpRead(void *buf, size_t max, ftphandle *nData) { if (nData->xfercb(nData->xfered, nData->cbarg) == 0) { - return -1; // error + return -1; // error forced by callback } nData->xfered1 = 0; } @@ -1293,7 +1400,7 @@ ssize_t ftplib::FtpWrite(void *buf, size_t len, ftphandle *nData) } if (nData->buf) { - i = writeline(static_cast(buf), len, nData); //XXX ascii mode + i = writeline(static_cast(buf), len, nData); // NOTE: ascii mode } else { @@ -1305,7 +1412,7 @@ ssize_t ftplib::FtpWrite(void *buf, size_t len, ftphandle *nData) #ifndef NOSSL if (nData->tlsdata) { - i = SSL_write(nData->ssl, buf, len); + i = SSL_write(nData->ssl, buf, static_cast(len)); } else { @@ -1600,7 +1707,7 @@ int ftplib::FtpXfer(const char *localfile, const char *path, ftphandle *nControl } else { - ssize_t len; + ssize_t len = 0; while ((len = FtpRead(dbuf, FTPLIB_BUFSIZ, nData)) > 0) { if (fwrite(dbuf, 1, static_cast(len), local) <= 0) @@ -1609,15 +1716,21 @@ int ftplib::FtpXfer(const char *localfile, const char *path, ftphandle *nControl rv = 0; // error break; } + #ifdef __CYGWIN__ fflush(local); //TODO: needed for test only under cygwin? ck #endif + } + +#if 0 //TBD check this! if (len < 0) { perror("FtpRead()"); rv = 0; // TODO ftp read error } +#endif + } free(dbuf); } diff --git a/ftplib.h b/ftplib.h index 85f5b18..8cb9c5f 100644 --- a/ftplib.h +++ b/ftplib.h @@ -53,7 +53,6 @@ #if defined(__APPLE__) #undef NOLFS -#define NOSSL typedef off_t off64_t; #define fseeko64 fseeko #define fopen64 fopen @@ -167,7 +166,7 @@ class ftplib ftplib(); ~ftplib(); char* LastResponse(); - int Connect(const char *host); + int Connect(const char *host, const char * port = "21"); int Login(const char *user, const char *pass); int Site(const char *cmd); int Raw(const char *cmd); diff --git a/sample/Makefile b/sample/Makefile index 2b243a0..1577577 100644 --- a/sample/Makefile +++ b/sample/Makefile @@ -8,12 +8,15 @@ sample: sample.o ../libftp++.a $(LINK.cc) -L.. -o $@ $^ ### -lftp++ test: sample - ./sample localhost:2121 + rm -f sample.bin sample.download + ./sample localhost 2121 while test ! -f sample.bin; do \ - ./sample localhost:2121; \ + ./sample localhost 2121; \ done diff sample sample.bin - -./sample localhost + ./sample 127.0.0.1 2121 --active + ./sample ::ffff:127.0.0.1 + ./sample ::1 2121 --active -./sample clean: diff --git a/sample/sample.cpp b/sample/sample.cpp index 2d64bfb..d319fbb 100644 --- a/sample/sample.cpp +++ b/sample/sample.cpp @@ -5,6 +5,7 @@ #include #include +#include // get size of a local file @@ -102,17 +103,27 @@ int main(int argc, char** argv) if (argc > 1) { - myftp->SetConnmode(ftplib::port); - ok = myftp->Connect(argv[1]); + if (argc > 3) + { + myftp->SetConnmode(ftplib::port); // test active ftp + } + + ok = myftp->Connect(argv[1], ((argc > 2) ? argv[2] : "2121")); } else { - myftp->SetConnmode(ftplib::pasv); + myftp->SetConnmode(ftplib::pasv); // test pasive ftp // ftp://ftp.gwdg.de/pub/incoming/ ok = myftp->Connect("ftp.gwdg.de"); //TODO /pub/incoming/ } - if (ok && myftp->Login("ftp", "OK")) + if (ok) + { + ok = myftp->Login("ftp", "OK"); + //FIXME ok = myftp->Login("anonymous", ""); + } + + if (ok) { const std::string filename("sample"); // filename for upload and download test @@ -136,12 +147,15 @@ int main(int argc, char** argv) else { myftp->Put("README.md", "/pub/incoming/tmp/README.txt", ftplib::ascii); - myftp->Rmdir("/pub/incoming/tmp"); + ok = myftp->Rmdir("/pub/incoming/tmp"); + assert(!ok); } - myftp->Dir("ftp-dir.txt", "/pub/incoming/"); - myftp->Nlst("ftp-list.txt", "/pub/incoming/"); - myftp->Mlsd("ftp-mlsd.txt", "/pub/incoming/"); + ok = myftp->Dir("ftp-dir.txt", "/pub/incoming/"); + assert(ok); + ok &= myftp->Nlst("ftp-list.txt", "/pub/incoming/"); + assert(ok); + (void) myftp->Mlsd("ftp-mlsd.txt", "/pub/incoming/"); // check the mod time of the remote file if (myftp->ModDate("/pub/incoming/sample", buf, sizeof(buf))) @@ -174,8 +188,6 @@ int main(int argc, char** argv) myftp->SetCallbackIdleFunction(myclass::callbackIdle); myftp->SetCallbackLogFunction(myclass::callbackLog); - myftp->SetCorrectPasv(true); - // ========================= // upload // get remote file size first @@ -183,7 +195,7 @@ int main(int argc, char** argv) if (!myftp->Put("sample", "/pub/incoming/sample.upload", ftplib::image, offset)) { perror("ftplib::Put()"); - //FIXME exit(EXIT_FAILURE); + //TBD exit(EXIT_FAILURE); } else if (myftp->Chdir("/pub/incoming/")) { @@ -197,7 +209,7 @@ int main(int argc, char** argv) if (!myftp->Get("sample.download", filename.c_str(), ftplib::image, offset)) { perror("ftplib::Get()"); - //FIXME exit(EXIT_FAILURE); + //TBD exit(EXIT_FAILURE); } else if (rename("sample.download", "sample.bin")) {