diff --git a/src/net/tcp/ftp.c b/src/net/tcp/ftp.c index e5bc8e9f..9f93fb66 100644 --- a/src/net/tcp/ftp.c +++ b/src/net/tcp/ftp.c @@ -1,3 +1,22 @@ +/* + * Copyright (C) 2007 Michael Brown . + * + * 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 any later version. + * + * This program 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 + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + */ + #include #include #include @@ -34,6 +53,7 @@ enum ftp_state { FTP_USER, FTP_PASS, FTP_TYPE, + FTP_SIZE, FTP_PASV, FTP_RETR, FTP_WAIT, @@ -68,6 +88,8 @@ struct ftp_request { char status_text[5]; /** Passive-mode parameters, as text */ char passive_text[24]; /* "aaa,bbb,ccc,ddd,eee,fff" */ + /** File size, as text */ + char filesize[20]; }; /** @@ -157,6 +179,7 @@ static struct ftp_control_string ftp_strings[] = { [FTP_USER] = { "USER ", ftp_user }, [FTP_PASS] = { "PASS ", ftp_password }, [FTP_TYPE] = { "TYPE I", NULL }, + [FTP_SIZE] = { "SIZE ", ftp_uri_path }, [FTP_PASV] = { "PASV", NULL }, [FTP_RETR] = { "RETR ", ftp_uri_path }, [FTP_WAIT] = { NULL, NULL }, @@ -234,6 +257,14 @@ static void ftp_reply ( struct ftp_request *ftp ) { if ( status_major == '1' ) return; + /* If the SIZE command is not supported by the server, we go to + * the next step. + */ + if ( ( status_major == '5' ) && ( ftp->state == FTP_SIZE ) ) { + ftp_next_state ( ftp ); + return; + } + /* Anything other than success (2xx) or, in the case of a * repsonse to a "USER" command, a password prompt (3xx), is a * fatal error. @@ -245,6 +276,26 @@ static void ftp_reply ( struct ftp_request *ftp ) { return; } + /* Parse file size */ + if ( ftp->state == FTP_SIZE ) { + size_t filesize; + char *endptr; + + /* Parse size */ + filesize = strtoul ( ftp->filesize, &endptr, 10 ); + if ( *endptr != '\0' ) { + DBGC ( ftp, "FTP %p invalid SIZE \"%s\"\n", + ftp, ftp->filesize ); + ftp_done ( ftp, -EPROTO ); + return; + } + + /* Use seek() to notify recipient of filesize */ + DBGC ( ftp, "FTP %p file size is %zd bytes\n", ftp, filesize ); + xfer_seek ( &ftp->xfer, filesize ); + xfer_seek ( &ftp->xfer, 0 ); + } + /* Open passive connection when we get "PASV" response */ if ( ftp->state == FTP_PASV ) { char *ptr = ftp->passive_text; @@ -295,35 +346,33 @@ static int ftp_control_deliver ( struct ftp_request *ftp, while ( len-- ) { c = *(data++); - switch ( c ) { - case '\r' : - case '\n' : + if ( ( c == '\r' ) || ( c == '\n' ) ) { /* End of line: call ftp_reply() to handle * completed reply. Avoid calling ftp_reply() * twice if we receive both \r and \n. */ - if ( recvsize == 0 ) + if ( recvbuf != ftp->status_text ) ftp_reply ( ftp ); /* Start filling up the status code buffer */ recvbuf = ftp->status_text; recvsize = sizeof ( ftp->status_text ) - 1; - break; - case '(' : + } else if ( ( ftp->state == FTP_PASV ) && ( c == '(' ) ) { /* Start filling up the passive parameter buffer */ recvbuf = ftp->passive_text; recvsize = sizeof ( ftp->passive_text ) - 1; - break; - case ')' : + } else if ( ( ftp->state == FTP_PASV ) && ( c == ')' ) ) { /* Stop filling the passive parameter buffer */ recvsize = 0; - break; - default : + } else if ( ( ftp->state == FTP_SIZE ) && ( c == ' ' ) ) { + /* Start filling up the file size buffer */ + recvbuf = ftp->filesize; + recvsize = sizeof ( ftp->filesize ) - 1; + } else { /* Fill up buffer if applicable */ if ( recvsize > 0 ) { *(recvbuf++) = c; recvsize--; } - break; } }