]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[ftp] Add support for the FTP SIZE command
authorMarin Hannache <git@mareo.fr>
Wed, 15 Aug 2012 16:04:41 +0000 (17:04 +0100)
committerMichael Brown <mcb30@ipxe.org>
Wed, 15 Aug 2012 16:04:41 +0000 (17:04 +0100)
The FTP SIZE command allows us to get the size of a particular file,
as a consequence, we can now show proper transfer progression while
fetching a file using the FTP protocol.

Signed-off-by: Marin Hannache <git@mareo.fr>
Modified-by: Michael Brown <mcb30@ipxe.org>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/net/tcp/ftp.c

index e5bc8e9f39c6f3751911dacc0c364a6552cbefeb..9f93fb66f1769d4ec7a2ad7e481c07a11e706dbc 100644 (file)
@@ -1,3 +1,22 @@
+/*
+ * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * 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 <stdint.h>
 #include <stdlib.h>
 #include <stdio.h>
@@ -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;
                }
        }