diff options
author | Timothy Pearson <kb9vqf@pearsoncomputing.net> | 2013-07-24 11:49:27 -0500 |
---|---|---|
committer | Timothy Pearson <kb9vqf@pearsoncomputing.net> | 2013-07-24 11:49:27 -0500 |
commit | c3f8ee64e905cdb103b5bfa07525fb4e85c31120 (patch) | |
tree | 46eabe44a1b3af79971dcafb743a104af8e18e69 /src/ftplib.cpp | |
download | kasablanca-c3f8ee64e905cdb103b5bfa07525fb4e85c31120.tar.gz kasablanca-c3f8ee64e905cdb103b5bfa07525fb4e85c31120.zip |
Initial import of kasablanca 0.4.0.2
Diffstat (limited to 'src/ftplib.cpp')
-rwxr-xr-x | src/ftplib.cpp | 1562 |
1 files changed, 1562 insertions, 0 deletions
diff --git a/src/ftplib.cpp b/src/ftplib.cpp new file mode 100755 index 0000000..def17f9 --- /dev/null +++ b/src/ftplib.cpp @@ -0,0 +1,1562 @@ +/*************************************************************************** + ftplib.cpp - description + ------------------- + begin : Son Jul 27 2003 + copyright : (C) 2003 by mkulke + email : sikor_sxe@radicalapproach.de + ***************************************************************************/ + +/*************************************************************************** + * * + * This program is free software; you can redistribute it and/or modify * + * it under the terms of the GNU General Public License as published by * + * the Free Software Foundation; either version 2 of the License, or * + * (at your option) any later version. * + * * + ***************************************************************************/ + +/* original unmodified copyright notes from Thomas Pfau */ + +/***************************************************************************/ +/* */ +/* ftplib.c - callable ftp access routines */ +/* Copyright (C) 1996, 1997, 1998 Thomas Pfau, pfau@cnj.digex.net */ +/* 73 Catherine Street, South Bound Brook, NJ, 08880 */ +/* */ +/* This library is free software; you can redistribute it and/or */ +/* modify it under the terms of the GNU Library General Public */ +/* License as published by the Free Software Foundation; either */ +/* version 2 of the License, or (at your option) any later version. */ +/* */ +/* This library is distributed in the hope that it will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU */ +/* Library General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU Library General Public */ +/* License along with this progam; if not, write to the */ +/* Free Software Foundation, Inc., 59 Temple Place - Suite 330, */ +/* Boston, MA 02111-1307, USA. */ +/* */ +/***************************************************************************/ + +// enable > 2gb support (LFS) + +#define _LARGEFILE_SOURCE +#define _LARGEFILE64_SOURCE + +#include "ftplib.h" +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#include <arpa/inet.h> +#include <string> + +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 + +/* + * Constructor + */ + +ftplib::ftplib() +{ + SSL_library_init(); + + mp_netbuf = static_cast<netbuf *>(calloc(1,sizeof(netbuf))); + if (mp_netbuf == NULL) perror("calloc"); + mp_netbuf->buf = static_cast<char *>(malloc(FTPLIB_BUFSIZ)); + if (mp_netbuf->buf == NULL) + { + perror("calloc"); + free(mp_netbuf); + } + mp_netbuf->ctx = SSL_CTX_new(SSLv23_method()); + SSL_CTX_set_verify(mp_netbuf->ctx, SSL_VERIFY_NONE, NULL); + mp_netbuf->ssl = SSL_new(mp_netbuf->ctx); + + ClearNetbuf(); +} + +/* + * Destructor + */ + +ftplib::~ftplib() +{ + SSL_free(mp_netbuf->ssl); + SSL_CTX_free(mp_netbuf->ctx); + free(mp_netbuf->buf); + free(mp_netbuf); +} + +/* + * socket_wait - wait for socket to receive or flush data + * + * return 1 if no user callback, otherwise, return value returned by + * user callback + */ +int ftplib::socket_wait(netbuf *ctl) +{ + fd_set fd,*rfd = NULL,*wfd = NULL; + struct timeval tv; + int rv = 0; + + if (ctl->idlecb == NULL) return 1; + + /*if ((ctl->dir == 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; + + FD_ZERO(&fd); + do + { + FD_SET(ctl->handle,&fd); + tv = ctl->idletime; + rv = select(ctl->handle+1, rfd, wfd, NULL, &tv); + if (rv == -1) + { + rv = 0; + strncpy(ctl->ctrl->response, strerror(errno), sizeof(ctl->ctrl->response)); + break; + } + else if (rv > 0) + { + rv = 1; + break; + } + } while ((rv = ctl->idlecb(ctl->cbarg))); + + return rv; +} + +/* + * read a line of text + * + * return -1 on error or bytecount + */ +int ftplib::readline(char *buf,int max,netbuf *ctl) +{ + int x,retval = 0; + char *end,*bp=buf; + int eof = 0; + + if ((ctl->dir != FTPLIB_CONTROL) && (ctl->dir != FTPLIB_READ)) + return -1; + if (max == 0) + return 0; + do + { + if (ctl->cavail > 0) + { + x = (max >= ctl->cavail) ? ctl->cavail : max-1; + end = static_cast<char*>(memccpy(bp,ctl->cget,'\n',x)); + if (end != NULL) + x = end - bp; + retval += x; + bp += x; + *bp = '\0'; + max -= x; + ctl->cget += x; + ctl->cavail -= x; + if (end != NULL) + { + bp -= 2; + if (strcmp(bp,"\r\n") == 0) + { + *bp++ = '\n'; + *bp++ = '\0'; + --retval; + } + break; + } + } + if (max == 1) + { + *buf = '\0'; + break; + } + if (ctl->cput == ctl->cget) + { + ctl->cput = ctl->cget = ctl->buf; + ctl->cavail = 0; + ctl->cleft = FTPLIB_BUFSIZ; + } + if (eof) + { + if (retval == 0) + retval = -1; + break; + } + + if (!socket_wait(ctl)) return retval; + + if (ctl->tlsdata) x = SSL_read(ctl->ssl, ctl->cput, ctl->cleft); + else + { + if (ctl->tlsctrl) x = SSL_read(ctl->ssl, ctl->cput, ctl->cleft); + else x = read(ctl->handle,ctl->cput,ctl->cleft); + } + if ( x == -1) + { + perror("read"); + retval = -1; + break; + } + + // LOGGING FUNCTIONALITY!!! + + if ((ctl->dir == FTPLIB_CONTROL) && (mp_netbuf->logcb != NULL)) + { + *((ctl->cput)+x) = '\0'; + mp_netbuf->logcb(ctl->cput, mp_netbuf->cbarg, true); + } + + if (x == 0) eof = 1; + ctl->cleft -= x; + ctl->cavail += x; + ctl->cput += x; + } while (1); + return retval; +} + +/* + * write lines of text + * + * return -1 on error or bytecount + */ +int ftplib::writeline(char *buf, int len, netbuf *nData) +{ + int x, nb=0, w; + char *ubp = buf, *nbp; + char lc=0; + + if (nData->dir != FTPLIB_WRITE) + return -1; + nbp = nData->buf; + for (x=0; x < len; x++) + { + if ((*ubp == '\n') && (lc != '\r')) + { + if (nb == FTPLIB_BUFSIZ) + { + if (!socket_wait(nData)) return x; + + if (nData->tlsctrl) w = SSL_write(nData->ssl, nbp, FTPLIB_BUFSIZ); + else w = write(nData->handle, nbp, FTPLIB_BUFSIZ); + + if (w != FTPLIB_BUFSIZ) + { + printf("write(1) returned %d, errno = %d\n", w, errno); + return(-1); + } + nb = 0; + } + nbp[nb++] = '\r'; + } + if (nb == FTPLIB_BUFSIZ) + { + if (!socket_wait(nData)) + return x; + + if (nData->tlsctrl) w = SSL_write(nData->ssl, nbp, FTPLIB_BUFSIZ); + else w = write(nData->handle, nbp, FTPLIB_BUFSIZ); + + if (w != FTPLIB_BUFSIZ) + { + printf("write(2) returned %d, errno = %d\n", w, errno); + return(-1); + } + nb = 0; + } + nbp[nb++] = lc = *ubp++; + } + if (nb) + { + if (!socket_wait(nData)) return x; + if (nData->tlsctrl) w = SSL_write(nData->ssl, nbp, nb); + else w = write(nData->handle, nbp, nb); + if (w != nb) + { + printf("write(3) returned %d, errno = %d\n", w, errno); + return(-1); + } + } + return len; +} + +/* + * read a response from the server + * + * return 0 if first char doesn't match + * return 1 if first char matches + */ +int ftplib::readresp(char c, netbuf *nControl) +{ + char match[5]; + + if (readline(nControl->response,256,nControl) == -1) + { + perror("Control socket read failed"); + return 0; + } + + if (nControl->response[3] == '-') + { + strncpy(match,nControl->response,3); + match[3] = ' '; + match[4] = '\0'; + do + { + if (readline(nControl->response,256,nControl) == -1) + { + perror("Control socket read failed"); + return 0; + } + } while (strncmp(nControl->response,match,4)); + } + if (nControl->response[0] == c) return 1; + return 0; +} + +/* + * FtpLastResponse - return a pointer to the last response received + */ +char* ftplib::LastResponse() +{ + if ((mp_netbuf) && (mp_netbuf->dir == FTPLIB_CONTROL)) return mp_netbuf->response; + return NULL; +} + +/* + * ftplib::Connect - connect to remote server + * + * return 1 if connected, 0 if not + */ +int ftplib::Connect(const char *host) +{ + int sControl; + struct sockaddr_in sin; + struct hostent *phe; + struct servent *pse; + int on=1; + int ret; + char *lhost; + char *pnum; + + mp_netbuf->dir = FTPLIB_CONTROL; + mp_netbuf->ctrl = NULL; + mp_netbuf->xfered = 0; + mp_netbuf->xfered1 = 0; + mp_netbuf->tlsctrl = 0; + mp_netbuf->tlsdata = 0; + mp_netbuf->offset = 0; + mp_netbuf->handle = 0; + + memset(&sin,0,sizeof(sin)); + sin.sin_family = AF_INET; + lhost = strdup(host); + pnum = strchr(lhost,':'); + if (pnum == NULL) + { + if ((pse = getservbyname("ftp","tcp")) == NULL) + { + perror("getservbyname"); + return 0; + } + sin.sin_port = pse->s_port; + } + else + { + *pnum++ = '\0'; + if (isdigit(*pnum)) sin.sin_port = htons(atoi(pnum)); + else + { + pse = getservbyname(pnum,"tcp"); + sin.sin_port = pse->s_port; + } + } + + ret = inet_aton(lhost, &sin.sin_addr); + if (ret == 0) + { + if ((phe = gethostbyname(lhost)) == NULL) + { + perror("gethostbyname"); + return 0; + } + memcpy((char *)&sin.sin_addr, phe->h_addr, phe->h_length); + } + free(lhost); + sControl = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (sControl == -1) + { + perror("socket"); + return 0; + } + if (setsockopt(sControl,SOL_SOCKET,SO_REUSEADDR, SETSOCKOPT_OPTVAL_TYPE &on, sizeof(on)) == -1) + { + perror("setsockopt"); + close(sControl); + return 0; + } + if (connect(sControl, (struct sockaddr *)&sin, sizeof(sin)) == -1) + { + perror("connect"); + close(sControl); + return 0; + } + + mp_netbuf->handle = sControl; + + if (readresp('2', mp_netbuf) == 0) + { + close(sControl); + mp_netbuf->handle = 0; + return 0; + } + + return 1; +} + +/* + * FtpSendCmd - send a command and wait for expected response + * + * return 1 if proper response received, 0 otherwise + */ +int ftplib::FtpSendCmd(const char *cmd, char expresp, netbuf *nControl) +{ + char buf[256]; + int x; + + if (!nControl->handle) return 0; + + if (nControl->dir != FTPLIB_CONTROL) return 0; + sprintf(buf,"%s\r\n",cmd); + + if (nControl->tlsctrl) x = SSL_write(nControl->ssl,buf,strlen(buf)); + else x = write(nControl->handle,buf,strlen(buf)); + + if (x <= 0) + { + perror("write"); + return 0; + } + + if (mp_netbuf->logcb != NULL) mp_netbuf->logcb(buf, mp_netbuf->cbarg, false); + + return readresp(expresp, nControl); +} + +/* + * FtpLogin - log in to remote server + * + * return 1 if logged in, 0 otherwise + */ +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; + sprintf(tempbuf, "USER %s", user); + if (!FtpSendCmd(tempbuf,'3',mp_netbuf)) + { + if (mp_netbuf->ctrl != NULL) return 1; + if (*LastResponse() == '2') return 1; + return 0; + } + sprintf(tempbuf,"PASS %s",pass); + return FtpSendCmd(tempbuf,'2',mp_netbuf); +} + +/* + * FtpAcceptConnection - accept connection from server + * + * return 1 if successful, 0 otherwise + */ +int ftplib::FtpAcceptConnection(netbuf *nData, netbuf *nControl) +{ + int sData; + struct sockaddr addr; + unsigned int l; + int i; + struct timeval tv; + fd_set mask; + int rv = 0; + + FD_ZERO(&mask); + FD_SET(nControl->handle, &mask); + FD_SET(nData->handle, &mask); + 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 == -1) + { + strncpy(nControl->response, strerror(errno), sizeof(nControl->response)); + close(nData->handle); + nData->handle = 0; + rv = 0; + } + else if (i == 0) + { + strcpy(nControl->response, "timed out waiting for connection"); + close(nData->handle); + nData->handle = 0; + rv = 0; + } + else + { + if (FD_ISSET(nData->handle, &mask)) + { + l = sizeof(addr); + sData = accept(nData->handle, &addr, &l); + i = errno; + close(nData->handle); + if (sData > 0) + { + rv = 1; + nData->handle = sData; + nData->ctrl = nControl; + } + else + { + strncpy(nControl->response, strerror(i), sizeof(nControl->response)); + nData->handle = 0; + rv = 0; + } + } + else if (FD_ISSET(nControl->handle, &mask)) + { + close(nData->handle); + nData->handle = 0; + readresp('2', nControl); + rv = 0; + } + } + return rv; +} + +/* + * FtpAccess - return a handle for a data stream + * + * return 1 if successful, 0 otherwise + */ +int ftplib::FtpAccess(const char *path, int typ, int mode, netbuf *nControl, netbuf **nData) +//int ftplib::FtpAccess(const char *path, int typ, int mode) +{ + char buf[256]; + int dir, ret; + + if ((path == NULL) && ((typ == FTPLIB_FILE_WRITE) + || (typ == FTPLIB_FILE_READ) + || (typ == FTPLIB_FILE_READ_APPEND) + || (typ == FTPLIB_FILE_WRITE_APPEND))) + { + sprintf(nControl->response,"Missing path argument for file transfer\n"); + return 0; + } + sprintf(buf, "TYPE %c", mode); + if (!FtpSendCmd(buf, '2', nControl)) return 0; + + switch (typ) + { + case FTPLIB_DIR: + strcpy(buf,"NLST"); + dir = FTPLIB_READ; + break; + case FTPLIB_DIR_VERBOSE: + strcpy(buf,"LIST -aL"); + dir = FTPLIB_READ; + break; + case FTPLIB_FILE_READ_APPEND: + case FTPLIB_FILE_READ: + strcpy(buf,"RETR"); + dir = FTPLIB_READ; + break; + case FTPLIB_FILE_WRITE_APPEND: + case FTPLIB_FILE_WRITE: + strcpy(buf,"STOR"); + dir = FTPLIB_WRITE; + break; + default: + sprintf(nControl->response, "Invalid open type %d\n", typ); + return 0; + } + if (path != NULL) + { + int i = strlen(buf); + buf[i++] = ' '; + 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 (nControl->cmode == ftplib::port) + { + if (FtpOpenPort(nControl, nData, mode, dir, buf) == -1) return 0; + if (!FtpAcceptConnection(*nData,nControl)) + { + FtpClose(*nData); + *nData = NULL; + return 0; + } + } + + if (nControl->tlsdata) + { + (*nData)->ssl = SSL_new(nControl->ctx); + (*nData)->sbio = BIO_new_socket((*nData)->handle, BIO_NOCLOSE); + SSL_set_bio((*nData)->ssl,(*nData)->sbio,(*nData)->sbio); + ret = SSL_connect((*nData)->ssl); + if (ret != 1) return 0; + (*nData)->tlsdata = 1; + } + + return 1; +} + +/* + * FtpOpenPort - Establishes a PORT connection for data transfer + * + * return 1 if successful, -1 otherwise + */ +int ftplib::FtpOpenPort(netbuf *nControl, netbuf **nData, int mode, int dir, char *cmd) +{ + int sData; + union { + struct sockaddr sa; + struct sockaddr_in in; + } sin; + struct linger lng = { 0, 0 }; + unsigned int l; + int on=1; + netbuf *ctrl; + char buf[256]; + + if (nControl->dir != FTPLIB_CONTROL) return -1; + if ((dir != FTPLIB_READ) && (dir != FTPLIB_WRITE)) + { + sprintf(nControl->response, "Invalid direction %d\n", dir); + return -1; + } + if ((mode != ftplib::ascii) && (mode != ftplib::image)) + { + sprintf(nControl->response, "Invalid mode %c\n", mode); + return -1; + } + l = sizeof(sin); + + if (getsockname(nControl->handle, &sin.sa, &l) < 0) + { + perror("getsockname"); + return -1; + } + + sData = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); + if (sData == -1) + { + perror("socket"); + return -1; + } + if (setsockopt(sData,SOL_SOCKET,SO_REUSEADDR, SETSOCKOPT_OPTVAL_TYPE &on,sizeof(on)) == -1) + { + perror("setsockopt"); + close(sData); + return -1; + } + if (setsockopt(sData,SOL_SOCKET,SO_LINGER, SETSOCKOPT_OPTVAL_TYPE &lng,sizeof(lng)) == -1) + { + perror("setsockopt"); + close(sData); + return -1; + } + + sin.in.sin_port = 0; + if (bind(sData, &sin.sa, sizeof(sin)) == -1) + { + perror("bind"); + close(sData); + return -1; + } + if (listen(sData, 1) < 0) + { + perror("listen"); + close(sData); + return -1; + } + if (getsockname(sData, &sin.sa, &l) < 0) 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)) + { + close(sData); + return -1; + } + + if (mp_netbuf->offset != 0) + { + char buf[256]; + sprintf(buf,"REST %lld", mp_netbuf->offset); + if (!FtpSendCmd(buf,'3',nControl)) + { + close(sData); + return 0; + } + } + + ctrl = static_cast<netbuf*>(calloc(1,sizeof(netbuf))); + if (ctrl == NULL) + { + perror("calloc"); + close(sData); + return -1; + } + if ((mode == 'A') && ((ctrl->buf = static_cast<char*>(malloc(FTPLIB_BUFSIZ))) == NULL)) + { + perror("calloc"); + close(sData); + free(ctrl); + return -1; + } + + if (!FtpSendCmd(cmd, '1', nControl)) + { + FtpClose(*nData); + *nData = NULL; + return -1; + } + + ctrl->handle = sData; + ctrl->dir = dir; + ctrl->ctrl = (nControl->cmode == ftplib::pasv) ? nControl : NULL; + ctrl->idletime = nControl->idletime; + ctrl->cbarg = nControl->cbarg; + 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; + *nData = ctrl; + + return 1; +} + +/* + * FtpOpenPasv - Establishes a PASV connection for data transfer + * + * return 1 if successful, -1 otherwise + */ +int ftplib::FtpOpenPasv(netbuf *nControl, netbuf **nData, int mode, int dir, char *cmd) +{ + int sData; + union { + struct sockaddr sa; + struct sockaddr_in in; + } sin; + struct linger lng = { 0, 0 }; + unsigned int l; + int on=1; + netbuf *ctrl; + char *cp; + unsigned char v[6]; + int ret; + + if (nControl->dir != FTPLIB_CONTROL) return -1; + if ((dir != FTPLIB_READ) && (dir != FTPLIB_WRITE)) + { + sprintf(nControl->response, "Invalid direction %d\n", dir); + return -1; + } + if ((mode != ftplib::ascii) && (mode != ftplib::image)) + { + 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)) return -1; + cp = strchr(nControl->response,'('); + if (cp == NULL) return -1; + cp++; + sscanf(cp,"%hhu,%hhu,%hhu,%hhu,%hhu,%hhu",&v[2],&v[3],&v[4],&v[5],&v[0],&v[1]); + 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 (mp_netbuf->offset != 0) + { + char buf[256]; + sprintf(buf,"REST %lld",mp_netbuf->offset); + if (!FtpSendCmd(buf,'3',nControl)) return 0; + } + + sData = socket(PF_INET,SOCK_STREAM,IPPROTO_TCP); + if (sData == -1) + { + perror("socket"); + return -1; + } + if (setsockopt(sData,SOL_SOCKET,SO_REUSEADDR, SETSOCKOPT_OPTVAL_TYPE &on,sizeof(on)) == -1) + { + perror("setsockopt"); + close(sData); + return -1; + } + if (setsockopt(sData,SOL_SOCKET,SO_LINGER, SETSOCKOPT_OPTVAL_TYPE &lng,sizeof(lng)) == -1) + { + perror("setsockopt"); + close(sData); + return -1; + } + + if (nControl->dir != FTPLIB_CONTROL) return -1; + sprintf(cmd,"%s\r\n",cmd); + if (nControl->tlsctrl) ret = SSL_write(nControl->ssl,cmd,strlen(cmd)); + else ret = write(nControl->handle,cmd,strlen(cmd)); + if (ret <= 0) + { + perror("write"); + return -1; + } + + if (connect(sData, &sin.sa, sizeof(sin.sa)) == -1) + { + perror("connect"); + close(sData); + return -1; + } + if (!readresp('1', nControl)) + { + close(sData); + return -1; + } + ctrl = static_cast<netbuf*>(calloc(1,sizeof(netbuf))); + if (ctrl == NULL) + { + perror("calloc"); + close(sData); + return -1; + } + if ((mode == 'A') && ((ctrl->buf = static_cast<char*>(malloc(FTPLIB_BUFSIZ))) == NULL)) + { + perror("calloc"); + close(sData); + free(ctrl); + return -1; + } + ctrl->handle = sData; + ctrl->dir = dir; + ctrl->ctrl = (nControl->cmode == ftplib::pasv) ? nControl : NULL; + ctrl->idletime = nControl->idletime; + ctrl->cbarg = nControl->cbarg; + 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; + *nData = ctrl; + + return 1; +} + +/* + * FtpClose - close a data connection + */ +int ftplib::FtpClose(netbuf *nData) +{ + netbuf *ctrl; + + if (nData->dir == FTPLIB_WRITE) + { + if (nData->buf != NULL) writeline(NULL, 0, nData); + } + else if (nData->dir != FTPLIB_READ) return 0; + if (nData->buf) free(nData->buf); + shutdown(nData->handle,2); + close(nData->handle); + + ctrl = nData->ctrl; + SSL_free(nData->ssl); + free(nData); + if (ctrl) return readresp('2', ctrl); + return 1; +} + +/* + * FtpRead - read from a data connection + */ +int ftplib::FtpRead(void *buf, int max, netbuf *nData) +{ + int i; + + if (nData->dir != FTPLIB_READ) + return 0; + if (nData->buf) i = readline(static_cast<char*>(buf), max, nData); + else + { + i = socket_wait(nData); + if (i != 1) return 0; + if (nData->tlsdata) i = SSL_read(nData->ssl, buf, max); + else i = read(nData->handle,buf,max); + } + 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; + nData->xfered1 = 0; + } + } + return i; +} + +/* + * FtpWrite - write to a data connection + */ +int ftplib::FtpWrite(void *buf, int len, netbuf *nData) +{ + int i; + + if (nData->dir != FTPLIB_WRITE) return 0; + if (nData->buf) i = writeline(static_cast<char*>(buf), len, nData); + else + { + socket_wait(nData); + if (nData->tlsdata) i = SSL_write(nData->ssl, buf, len); + else i = write(nData->handle, buf, len); + } + 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; + nData->xfered1 = 0; + } + } + return i; +} + +/* + * FtpSite - send a SITE command + * + * return 1 if command successful, 0 otherwise + */ +int ftplib::Site(const char *cmd) +{ + char buf[256]; + + if ((strlen(cmd) + 7) > sizeof(buf)) return 0; + sprintf(buf,"SITE %s",cmd); + if (!FtpSendCmd(buf,'2',mp_netbuf)) return 0; + return 1; +} + +/* + * FtpRaw - 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_netbuf)) return 0; + return 1; +} + +/* + * FtpSysType - 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(). + * + * return 1 if command successful, 0 otherwise + */ +int ftplib::SysType(char *buf, int max) +{ + int l = max; + char *b = buf; + char *s; + if (!FtpSendCmd("SYST",'2',mp_netbuf)) return 0; + s = &mp_netbuf->response[4]; + while ((--l) && (*s != ' ')) *b++ = *s++; + *b++ = '\0'; + return 1; +} + +/* + * FtpMkdir - create a directory at server + * + * return 1 if successful, 0 otherwise + */ +int ftplib::Mkdir(const char *path) +{ + char buf[256]; + + if ((strlen(path) + 6) > sizeof(buf)) return 0; + sprintf(buf,"MKD %s",path); + if (!FtpSendCmd(buf,'2', mp_netbuf)) return 0; + return 1; +} + +/* + * FtpChdir - change path at remote + * + * return 1 if successful, 0 otherwise + */ +int ftplib::Chdir(const char *path) +{ + char buf[256]; + + if ((strlen(path) + 6) > sizeof(buf)) return 0; + sprintf(buf,"CWD %s",path); + if (!FtpSendCmd(buf,'2',mp_netbuf)) return 0; + return 1; +} + +/* + * FtpCDUp - move to parent directory at remote + * + * return 1 if successful, 0 otherwise + */ +int ftplib::Cdup() +{ + if (!FtpSendCmd("CDUP",'2',mp_netbuf)) return 0; + return 1; +} + +/* + * FtpRmdir - remove directory at remote + * + * return 1 if successful, 0 otherwise + */ +int ftplib::Rmdir(const char *path) +{ + char buf[256]; + + if ((strlen(path) + 6) > sizeof(buf)) return 0; + sprintf(buf,"RMD %s",path); + if (!FtpSendCmd(buf,'2',mp_netbuf)) return 0; + return 1; +} + +/* + * FtpPwd - get working directory at remote + * + * return 1 if successful, 0 otherwise + */ +int ftplib::Pwd(char *path, int max) +{ + int l = max; + char *b = path; + char *s; + + if (!FtpSendCmd("PWD",'2',mp_netbuf)) return 0; + s = strchr(mp_netbuf->response, '"'); + if (s == NULL) return 0; + s++; + while ((--l) && (*s) && (*s != '"')) *b++ = *s++; + *b++ = '\0'; + return 1; +} + +/* + * FtpXfer - issue a command and transfer data + * + * return 1 if successful, 0 otherwise + */ +int ftplib::FtpXfer(const char *localfile, const char *path, netbuf *nControl, int typ, int mode) +{ + int l,c; + char *dbuf; + FILE *local = NULL; + netbuf *nData; + int rv=1; // 3.1-1 + + if (localfile != NULL) + { + printf("localfile: -%s-", localfile); + + //local = fopen(localfile, (typ == FTPLIB_FILE_WRITE) ? "r" : "w"); + char ac[3] = " "; + if ((typ == FTPLIB_DIR) || (typ == FTPLIB_DIR_VERBOSE)) { ac[0] = 'w'; ac[1] = '\0'; } + if (typ == FTPLIB_FILE_READ) { ac[0] = 'w'; ac[1] = '\0'; } + if (typ == FTPLIB_FILE_WRITE_APPEND) { ac[0] = 'r'; ac[1] = '\0'; } + if (typ == FTPLIB_FILE_READ_APPEND) { ac[0] = 'a'; ac[1] = '\0'; } + if (typ == FTPLIB_FILE_WRITE) { ac[0] = 'r'; ac[1] = '\0'; } + if (mode == ftplib::image) ac[1] = 'b'; + + local = fopen64(localfile, ac); + if (typ == FTPLIB_FILE_WRITE_APPEND) fseeko64(local,mp_netbuf->offset,SEEK_SET); + + if (local == NULL) + { + strncpy(nControl->response, strerror(errno), sizeof(nControl->response)); + return 0; + } + } + if (local == NULL) local = ((typ == FTPLIB_FILE_WRITE) + || (typ == FTPLIB_FILE_WRITE_APPEND)) ? stdin : stdout; + if (!FtpAccess(path, typ, mode, nControl, &nData)) return 0; + + dbuf = static_cast<char*>(malloc(FTPLIB_BUFSIZ)); + if ((typ == FTPLIB_FILE_WRITE) || (typ == FTPLIB_FILE_WRITE_APPEND)) + { + while ((l = fread(dbuf, 1, FTPLIB_BUFSIZ, local)) > 0) + { + if ((c = FtpWrite(dbuf, l, nData)) < l) + { + printf("short write: passed %d, wrote %d\n", l, c); + rv = 0; + break; + } + } + } + else + { + while ((l = FtpRead(dbuf, FTPLIB_BUFSIZ, nData)) > 0) + { + if (fwrite(dbuf, 1, l, local) <= 0) + { + perror("localfile write"); + break; + } + } + } + free(dbuf); + fflush(local); + if (localfile != NULL) + fclose(local); + return FtpClose(nData); + return rv; +} + +/* + * FtpNlst - issue an NLST command and write response to output + * + * return 1 if successful, 0 otherwise + */ +int ftplib::Nlst(const char *outputfile, const char *path) +{ + mp_netbuf->offset = 0; + return FtpXfer(outputfile, path, mp_netbuf, FTPLIB_DIR, ftplib::ascii); +} + +/* + * FtpDir - issue a LIST command and write response to output + * + * return 1 if successful, 0 otherwise + */ +int ftplib::Dir(const char *outputfile, const char *path) +{ + mp_netbuf->offset = 0; + return FtpXfer(outputfile, path, mp_netbuf, FTPLIB_DIR_VERBOSE, ftplib::ascii); +} + +/* + * FtpSize - determine the size of a remote file + * + * return 1 if successful, 0 otherwise + */ +//int ftplib::Size(const char *path, int *size, char mode) +int ftplib::Size(const char *path, int *size, ftplib::ftp mode) +{ + char cmd[256]; + int resp,sz,rv=1; + + if ((strlen(path) + 7) > sizeof(cmd)) return 0; + + sprintf(cmd, "TYPE %c", mode); + if (!FtpSendCmd(cmd, '2', mp_netbuf)) return 0; + + sprintf(cmd,"SIZE %s",path); + if (!FtpSendCmd(cmd,'2',mp_netbuf)) rv = 0; + else + { + if (sscanf(mp_netbuf->response, "%d %d", &resp, &sz) == 2) *size = sz; + else rv = 0; + } + return rv; +} + +/* + * FtpModDate - determine the modification date of a remote file + * + * return 1 if successful, 0 otherwise + */ +int ftplib::ModDate(const char *path, char *dt, int max) +{ + char buf[256]; + int rv = 1; + + if ((strlen(path) + 7) > sizeof(buf)) return 0; + sprintf(buf,"MDTM %s",path); + if (!FtpSendCmd(buf,'2',mp_netbuf)) rv = 0; + else strncpy(dt, &mp_netbuf->response[4], max); + return rv; +} + +/* + * FtpGet - issue a GET command and write received data to output + * + * return 1 if successful, 0 otherwise + */ + +int ftplib::Get(const char *outputfile, const char *path, ftplib::ftp mode, off64_t offset) +{ + mp_netbuf->offset = offset; + if (offset == 0) return FtpXfer(outputfile, path, mp_netbuf, FTPLIB_FILE_READ, mode); + else return FtpXfer(outputfile, path, mp_netbuf, FTPLIB_FILE_READ_APPEND, mode); +} + +/* + * FtpPut - issue a PUT command and send data from input + * + * return 1 if successful, 0 otherwise + */ + +int ftplib::Put(const char *inputfile, const char *path, ftplib::ftp mode, off64_t offset) +{ + mp_netbuf->offset = offset; + if (offset == 0) return FtpXfer(inputfile, path, mp_netbuf, FTPLIB_FILE_WRITE, mode); + else return FtpXfer(inputfile, path, mp_netbuf, FTPLIB_FILE_WRITE_APPEND, mode); +} + +/* + * FtpRename - rename a file at remote + * + * return 1 if successful, 0 otherwise + */ +int ftplib::Rename(const char *src, const char *dst) +{ + char cmd[256]; + + if (((strlen(src) + 7) > sizeof(cmd)) || ((strlen(dst) + 7) > sizeof(cmd))) return 0; + sprintf(cmd,"RNFR %s",src); + if (!FtpSendCmd(cmd,'3',mp_netbuf)) return 0; + sprintf(cmd,"RNTO %s",dst); + if (!FtpSendCmd(cmd,'2',mp_netbuf)) return 0; + + return 1; +} + +/* + * FtpDelete - delete a file at remote + * + * return 1 if successful, 0 otherwise + */ +int ftplib::Delete(const char *fnm) +{ + char cmd[256]; + + if ((strlen(fnm) + 7) > sizeof(cmd)) return 0; + sprintf(cmd,"DELE %s",fnm); + if (!FtpSendCmd(cmd,'2', mp_netbuf)) return 0; + return 1; +} + +/* + * FtpQuit - disconnect from remote + * + * return 1 if successful, 0 otherwise + */ +int ftplib::Quit() +{ + if (mp_netbuf->dir != FTPLIB_CONTROL) return 0; + if (mp_netbuf->handle == 0) + { + strcpy(mp_netbuf->response, "error: no anwser from server\n"); + return 0; + } + if (!FtpSendCmd("QUIT",'2',mp_netbuf)) + { + close(mp_netbuf->handle); + return 0; + } + else + { + close(mp_netbuf->handle); + return 1; + } +} + +int ftplib::Fxp(ftplib* src, ftplib* dst, const char *pathSrc, const char *pathDst, ftplib::ftp mode, ftplib::ftp method) +{ + char *cp; + unsigned char v[6]; + char buf[256]; + int retval = 0; + + sprintf(buf, "TYPE %c", mode); + if (!dst->FtpSendCmd(buf,'2',dst->mp_netbuf)) return -1; + if (!src->FtpSendCmd(buf,'2',src->mp_netbuf)) return -1; + + if (method == ftplib::defaultfxp) + { + // PASV dst + + if (!dst->FtpSendCmd("PASV",'2',dst->mp_netbuf)) return -1; + cp = strchr(dst->mp_netbuf->response,'('); + if (cp == NULL) return -1; + cp++; + sscanf(cp,"%hhu,%hhu,%hhu,%hhu,%hhu,%hhu",&v[2],&v[3],&v[4],&v[5],&v[0],&v[1]); + if (dst->mp_netbuf->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_netbuf)) return -1; + + // RETR src + + strcpy(buf,"RETR"); + if (pathSrc != NULL) + { + int i = strlen(buf); + buf[i++] = ' '; + if ((strlen(pathSrc) + i) >= sizeof(buf)) return 0; + strcpy(&buf[i],pathSrc); + } + if (!src->FtpSendCmd(buf, '1', src->mp_netbuf)) return 0; + + // STOR dst + + strcpy(buf,"STOR"); + if (pathDst != NULL) + { + int i = strlen(buf); + buf[i++] = ' '; + if ((strlen(pathDst) + i) >= sizeof(buf)) return 0; + strcpy(&buf[i],pathDst); + } + if (!dst->FtpSendCmd(buf, '1', dst->mp_netbuf)) + { + /* this closes the data connection, to abort the RETR on + the source ftp. all hail pftp, it took me several + 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_netbuf); + src->readresp('4', src->mp_netbuf); + return 0; + } + + retval = (src->readresp('2', src->mp_netbuf)) & (dst->readresp('2', dst->mp_netbuf)); + + } + else + { + // PASV src + + if (!src->FtpSendCmd("PASV",'2',src->mp_netbuf)) return -1; + cp = strchr(src->mp_netbuf->response,'('); + if (cp == NULL) return -1; + cp++; + sscanf(cp,"%hhu,%hhu,%hhu,%hhu,%hhu,%hhu",&v[2],&v[3],&v[4],&v[5],&v[0],&v[1]); + if (src->mp_netbuf->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_netbuf)) return -1; + + // STOR dest + + strcpy(buf,"STOR"); + if (pathDst != NULL) + { + int i = strlen(buf); + buf[i++] = ' '; + if ((strlen(pathDst) + i) >= sizeof(buf)) return 0; + strcpy(&buf[i],pathDst); + } + if (!dst->FtpSendCmd(buf, '1', dst->mp_netbuf)) return 0; + + // RETR src + + strcpy(buf,"RETR"); + if (pathSrc != NULL) + { + int i = strlen(buf); + buf[i++] = ' '; + if ((strlen(pathSrc) + i) >= sizeof(buf)) return 0; + strcpy(&buf[i],pathSrc); + } + if (!src->FtpSendCmd(buf, '1', src->mp_netbuf)) + { + src->FtpSendCmd("PASV", '2', src->mp_netbuf); + dst->readresp('4', dst->mp_netbuf); + return 0; + } + + // wait til its finished! + + retval = (src->readresp('2', src->mp_netbuf)) & (dst->readresp('2', dst->mp_netbuf)); + + } + + return retval; +} +int ftplib::SetDataEncryption(ftplib::ftp flag) +{ + if (!mp_netbuf->tlsctrl) return 0; + if (!FtpSendCmd("PBSZ 0",'2',mp_netbuf)) return 0; + switch(flag) + { + case 0: + mp_netbuf->tlsdata = 0; + if (!FtpSendCmd("PROT C",'2',mp_netbuf)) return 0; + break; + case 1: + mp_netbuf->tlsdata = 1; + if (!FtpSendCmd("PROT P",'2',mp_netbuf)) return 0; + break; + default: + return 0; + } + return 1; +} + +int ftplib::NegotiateEncryption() +{ + int ret; + + if (!FtpSendCmd("AUTH TLS",'2',mp_netbuf)) return 0; + + mp_netbuf->sbio = BIO_new_socket(mp_netbuf->handle, BIO_NOCLOSE); + SSL_set_bio(mp_netbuf->ssl,mp_netbuf->sbio,mp_netbuf->sbio); + + ret = SSL_connect(mp_netbuf->ssl); + if (ret == 1) mp_netbuf->tlsctrl = 1; + + if (ret < 1) return 0; + + return 1; +} + +void ftplib::SetCallbackIdleFunction(FtpCallbackIdle pointer) +{ + mp_netbuf->idlecb = pointer; +} + +void ftplib::SetCallbackXferFunction(FtpCallbackXfer pointer) +{ + mp_netbuf->xfercb = pointer; +} + +void ftplib::SetCallbackLogFunction(FtpCallbackLog pointer) +{ + mp_netbuf->logcb = pointer; +} + +void ftplib::SetCallbackArg(void *arg) +{ + mp_netbuf->cbarg = arg; +} + +void ftplib::SetCallbackBytes(off64_t bytes) +{ + mp_netbuf->cbbytes = bytes; +} + +void ftplib::SetCallbackIdletime(int time) +{ + mp_netbuf->idletime.tv_sec = time / 1000; + mp_netbuf->idletime.tv_usec = (time % 1000) * 1000; +} + +void ftplib::SetConnmode(ftplib::ftp mode) +{ + if ((mode != ftplib::pasv) && (mode != ftplib::port)) mode = ftplib::defaultconnmode; + mp_netbuf->cmode = mode; +} + +void ftplib::ClearNetbuf() +{ + mp_netbuf->dir = FTPLIB_CONTROL; + mp_netbuf->ctrl = NULL; + mp_netbuf->cmode = ftplib::defaultconnmode; + mp_netbuf->idlecb = NULL; + mp_netbuf->idletime.tv_sec = mp_netbuf->idletime.tv_usec = 0; + mp_netbuf->cbarg = NULL; + mp_netbuf->xfered = 0; + mp_netbuf->xfered1 = 0; + mp_netbuf->cbbytes = 0; + mp_netbuf->tlsctrl = 0; + mp_netbuf->tlsdata = 0; + mp_netbuf->offset = 0; + mp_netbuf->handle = 0; + mp_netbuf->logcb = NULL; + mp_netbuf->xfercb = NULL; + mp_netbuf->correctpasv = false; +} + +int ftplib::CorrectPasvResponse(unsigned char *v) +{ + struct sockaddr ipholder; + unsigned int ipholder_size = sizeof(ipholder); + + if (getpeername(mp_netbuf->handle, &ipholder, &ipholder_size) == -1) + { + perror("getpeername"); + close(mp_netbuf->handle); + return 0; + } + + for (int i = 2; i < 6; i++) v[i] = ipholder.sa_data[i]; + + return 1; +} |