diff options
Diffstat (limited to 'apps/netutils/ftpc/ftpc_getfile.c')
-rw-r--r-- | apps/netutils/ftpc/ftpc_getfile.c | 398 |
1 files changed, 398 insertions, 0 deletions
diff --git a/apps/netutils/ftpc/ftpc_getfile.c b/apps/netutils/ftpc/ftpc_getfile.c new file mode 100644 index 000000000..a3ea5008d --- /dev/null +++ b/apps/netutils/ftpc/ftpc_getfile.c @@ -0,0 +1,398 @@ +/**************************************************************************** + * apps/netutils/ftpc/ftpc_getfile.c + * + * Copyright (C) 2011 Gregory Nutt. All rights reserved. + * Author: Gregory Nutt <spudmonkey@racsa.co.cr> + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * 3. Neither the name NuttX nor the names of its contributors may be + * used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS + * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <nuttx/config.h> + +#include <sys/stat.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <debug.h> +#include <errno.h> + +#include <apps/ftpc.h> + +#include "ftpc_internal.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +/**************************************************************************** + * Public Data + ****************************************************************************/ + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ftpc_recvinit + * + * Description: + * Initialize to receive a file + * + ****************************************************************************/ + +static int ftpc_recvinit(struct ftpc_session_s *session, FAR const char *path, + uint8_t xfrmode, off_t offset) +{ + int ret; + + /* Reset transfer related variables */ + + ftpc_xfrreset(session); + + ret = ftpc_xfrinit(session); + if (ret != OK) + { + return ERROR; + } + + /* Configure the transfer: Initial file offset and tranfer mode */ + + session->offset = 0; + session->filesize = -1; + ftpc_xfrmode(session, xfrmode); + + /* Handle the resume offset (caller is responsible for fseeking in the + * file) + */ + + if (offset > 0) + { + /* Send the REST command. This command sets the offset where the + * transfer should start. This must come after PORT or PASV commands. + */ + + ret = ftpc_cmd(session, "REST %ld", offset); + if (ret < 0) + { + ndbg("REST command failed: %d\n", errno); + return ERROR; + } + + session->size = offset; + session->rstrsize = offset; + } + + /* Send the RETR (Retrieve a remote file) command */ + + ret = ftpc_cmd(session, "RETR %s", path); + if (ret < 0) + { + ndbg("RETR command failed: %d\n", errno); + return ERROR; + } + + /* Accept a connection on the data socket */ + + ret = ftpc_sockaccept(&session->data, "r", FTPC_IS_PASSIVE(session)); + if (ret != OK) + { + ndbg("data connection not accepted\n"); + } + + return ret; +} + +/**************************************************************************** + * Name: ftpc_waitinput + * + * Description: + * Wait to receive data. + * + ****************************************************************************/ + +static int ftpc_waitinput(FAR struct ftpc_session_s *session) +{ + int ret; + do { + ret = ftpc_waitdata(session, session->data.instream, true); + if (ret == -1) { + if (errno == EINTR) + { + FTPC_SET_INTERRUPT(session); + } + return ERROR; + } + } while(ret == 0); + + return OK; +} + +/**************************************************************************** + * Name: ftpc_recvbinary + * + * Description: + * Receive a binary file. + * + ****************************************************************************/ + +static int ftpc_recvbinary(FAR struct ftpc_session_s *session, + FAR FILE *rinstream, FAR FILE *loutstream) +{ + FAR char *buf; + ssize_t nread; + ssize_t nwritten; + int err; + int ret = OK; + + buf = (FAR char *)malloc(CONFIG_FTP_BUFSIZE); + if (!buf) + { + err = ENOMEM; + goto errout_with_err; + } + + while (!feof(rinstream)) + { + if (ftpc_waitinput(session) != 0) + { + nvdbg("ftpc_waitinput() failed\n"); + err = EIO; + goto errout_with_buf; + } + + nread = fread(buf, sizeof(char), CONFIG_FTP_BUFSIZE, rinstream); + if (nread <= 0) + { + (void)ftpc_xfrabort(session, rinstream); + ret = ERROR; + break; + } + + nwritten = fwrite(buf, sizeof(char), nread, loutstream); + if (nwritten != nread) + { + (void)ftpc_xfrabort(session, loutstream); + ret = ERROR; + break; + } + + session->size += nread; + } + + free(buf); + return ret; + +errout_with_buf: + free(buf); +errout_with_err: + set_errno(err); + return ERROR; +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: ftpc_getfile + * + * Description: + * Get a file from the remote host. + * + ****************************************************************************/ + +int ftpc_getfile(SESSION handle, FAR const char *rname, FAR const char *lname, + uint8_t how, uint8_t xfrmode) +{ + FAR struct ftpc_session_s *session = (FAR struct ftpc_session_s *)handle; + struct stat statbuf; + FILE *loutstream; + off_t offset; + int ret; + + /* Get information about the local file */ + + ret = stat(lname, &statbuf); + if (ret == 0) + { + /* It already exists. Is it a directory? */ + + if (S_ISDIR(statbuf.st_mode)) + { + ndbg("'%s' is a directory\n", lname); + return ERROR; + } + } + + /* Is it write-able? */ + +#ifdef S_IWRITE + if (!(statbuf.st_mode & S_IWRITE)) + { + ndbg("'%s' permission denied\n", lname); + return ERROR; + } +#endif + + /* Are we resuming the transfers? Is so then the starting offset is the + * size of the existing, partial file. + */ + + if (how == FTPC_GET_RESUME) + { + offset = statbuf.st_size; + } + else + { + offset = 0; + } + + /* Setup to receive the file */ + + ret = ftpc_recvinit(session, rname, xfrmode, offset); + if (ret != OK) + { + ndbg("ftpc_recvinit failed\n"); + return ERROR; + } + + loutstream = fopen(lname, (offset > 0 || (how == FTPC_GET_APPEND)) ? "a" : "w"); + if (!loutstream) + { + ndbg("fopen failed: %d\n", errno); + session->offset = 0; + return ERROR; + } + + if (offset > 0) + { + ret = fseek(loutstream, offset, SEEK_SET); + if (ret != OK) + { + ndbg("fseek failed: %d\n", errno); + fclose(loutstream); + session->offset = 0; + return ERROR; + } + } + + /* Save the new local and remote file names */ + + free(session->rname); + free(session->lname); + session->rname = strdup(rname); + session->lname = strdup(lname); + + /* And receive the new file data */ + + if (xfrmode == FTPC_XFRMODE_ASCII) + { + ret = ftpc_recvbinary(session, session->data.instream, loutstream); + } + else + { + ret = ftpc_recvtext(session, session->data.instream, loutstream); + } + + ftpc_sockclose(&session->data); + + if (ret == 0) + { + fptc_getreply(session); + } + + fclose(loutstream); + return (ret == OK && !FTPC_INTERRUPTED(session)) ? OK : ERROR; +} + +/**************************************************************************** + * Name: ftpc_recvbinary + * + * Description: + * Receive a text file. + * + ****************************************************************************/ + +int ftpc_recvtext(FAR struct ftpc_session_s *session, + FAR FILE *rinstream, FAR FILE *loutstream) +{ + char *buf = (char *)malloc(CONFIG_FTP_BUFSIZE); + int c; + int ret = OK; + + while((c = fgetc(rinstream)) != EOF) { + + if (ftpc_waitinput(session) != 0) + { + break; + } + + if (c == '\r') + { + c = fgetc(rinstream); + if (c == EOF) + { + (void)ftpc_xfrabort(session, rinstream); + ret = ERROR; + break; + } + if (c != '\n') + { + ungetc(c, rinstream); + c = '\r'; + } + } + + if (fputc(c, loutstream) == EOF) + { + (void)ftpc_xfrabort(session, loutstream); + ret = ERROR; + break; + } + + session->size++; + } + + free(buf); + return ret; +} + + |