HTFTP: Support for resuming downloads with "REST" [patch]

Richard Atterer <[email protected]>
Newsgroups gmane.comp.lib.libwww
Message-ID <[email protected]>
On Wed, Feb 12, 2003 at 06:34:29PM +0100, Richard Atterer wrote:
> The bad news first: AFAICT libwww doesn't support FTP resumes (REST
> command) at all. :-( Maybe I'll come up with a patch.

Yes, here it is! Again, to apply it, pipe this mail into
  patch -d somepath/w3c-libwww-5.4.0/Library/src
It will apply with or without the previous patch, although there may be a 
few "Hunk succeeded at offset -10 lines" messages.

Jose/anybody, please apply this, I consider it a quite important feature!

Cheers,

  Richard

-- 
  __   _
  |_) /|  Richard Atterer     |  CS student at the Technische  |  GnuPG key:
  | \/¯|  http://atterer.net  |  Universität München, Germany  |  0x888354F7
  ¯ '` ¯

--- HTFTP.h.orig	2003-01-13 18:12:46.000000000 +0100
+++ HTFTP.h	2003-02-13 16:33:53.000000000 +0100
@@ -24,6 +24,13 @@
 a part of the W3C
 Sample Code Library.
 
+The module supports partial downloads by using the FTP "REST" command. To
+initiate such a partial request, proceed the same way as with an HTTP
+request; use something like HTRequest_addRange(request, "bytes", "12345-"). 
+HTFTP only supports ranges without an end offset, a range like "12345-20000"
+will be ignored. If the "REST" command was successful, an appropriate range
+entry is added to the request's response object.
+
 */
 
 #ifndef HTFTP_H
--- HTFTP.c.orig	2003-02-12 17:39:04.000000000 +0100
+++ HTFTP.c	2003-02-13 00:23:46.000000000 +0100
@@ -55,6 +55,7 @@
 **      Jan 2000 JB     Joe Bester - Fixed the protocol bug that appeared after
 **                      after the CERT advisory warning on wu. Added code
 **                      to do an incremental streaming of FTP data.
+**      Feb 2003        Richard Atterer - added REST support
 **                       
 ** Notes:
 **     			Portions Copyright 1994 Trustees of Dartmouth College
@@ -1093,6 +1094,7 @@
 	NEED_SELECT = 0,
 	NEED_CONNECT,
 	NEED_ACCEPT,
+	NEED_REST,
 	NEED_ACTION,
         NEED_CWD,
 	NEED_SEGMENT,
@@ -1105,7 +1107,7 @@
 	switch ((state) ctrl->substate) {
 	  case NEED_SELECT:
 	    HTTRACE(PROT_TRACE, "FTP Get Data now in state NEED_SELECT\n");
-	    ctrl->substate = data->pasv ? NEED_CONNECT : NEED_ACTION;
+	    ctrl->substate = data->pasv ? NEED_CONNECT : NEED_REST;
 	    break;
 
 	  case NEED_CONNECT:
@@ -1116,7 +1118,7 @@
 	    else if (status == HT_OK) {
 		HTTRACE(PROT_TRACE, "FTP Get Data Active data socket %d\n" _ 
 			    HTNet_socket(dnet));
-		ctrl->substate = NEED_ACTION;
+		ctrl->substate = NEED_REST;
 	    } else {			 	  /* Swap to PORT on the fly */
 		NETCLOSE(HTNet_socket(dnet));
 		HTNet_setSocket(dnet, INVSOC);
@@ -1140,6 +1142,67 @@
 		ctrl->substate = SUB_ERROR;
 	    break;
 
+	  case NEED_REST:
+	    HTTRACE(PROT_TRACE, "FTP Get Data now in state NEED_REST\n");
+	    if (!ctrl->sent) {
+                ctrl->substate = NEED_ACTION;
+                /* If the user added a Range header to the request and that
+                   header is a single "from offset x to the end", transform
+                   that into a REST command. */
+                HTAssocList* ranges = HTRequest_range(request);
+                HTAssoc* r1, * r2;
+                const char* rangeVal, * s;
+                int len = 0;
+                char* chunkData;
+                HTStream* input;
+
+                /* Do nothing unless exactly one range item present. */
+                if (ranges == 0) break; 
+                r1 = HTAssocList_nextObject(ranges);
+                if (r1 == 0) break;
+                r2 = HTAssocList_nextObject(ranges);
+                if (r2 != 0) break;
+                /* Do nothing unless assoc reads: "bytes" => "12345-" */
+                if (strcmp(HTAssoc_name(r1), "bytes") != 0) break;
+                rangeVal = HTAssoc_value(r1);
+                s = rangeVal;
+                if (*s < '0' || *s > '9') break;
+                while (*++s >= '0' && *s <= '9') len += *s - '0';
+                if (*s != '-' || s[1] != '\0' || len == 0) break;
+
+                /* send: "REST 12345" */
+                len = 4 + 1 + (s - rangeVal) + 2;
+                HTChunk_setSize(ctrl->cmd, len + 1);
+                chunkData = HTChunk_data(ctrl->cmd);
+                sprintf(chunkData, "REST %s%c", rangeVal, LF);
+                chunkData[len - 2] = CR; /* Overwrite '-' */
+                input = HTRequest_inputStream(request);
+                HTTRACE(PROT_TRACE, "FTP Get Data Tx...... %s" _ chunkData);
+                status = (*input->isa->put_block)(input, chunkData, len);
+		if (status == HT_WOULD_BLOCK)
+		    return HT_WOULD_BLOCK;
+		else if (status == HT_ERROR)
+		    ctrl->substate = SUB_ERROR;
+		ctrl->sent = YES;
+                ctrl->substate = NEED_REST; /* Wait for reply */
+            } else {
+		status = HTHost_read(HTNet_host(cnet), cnet);
+		if (status == HT_WOULD_BLOCK) {
+		    return HT_WOULD_BLOCK;
+                } else if (status == HT_LOADED && ctrl->repcode == 350) {
+                    /* Add appropriate range header to response object */
+                    HTResponse* resp = HTRequest_response(request);
+                    HTAssocList* ranges = HTRequest_range(request);
+                    HTAssoc* r1 = HTAssocList_nextObject(ranges);
+                    HTResponse_addRange(resp, "bytes", HTAssoc_value(r1));
+                    ctrl->substate = NEED_ACTION;
+                } else {
+		    ctrl->substate = SUB_ERROR;
+                }
+		ctrl->sent = NO;
+            }
+	    break;
+
 	  case NEED_ACTION:
 	    HTTRACE(PROT_TRACE, "FTP Get Data now in state NEED_ACTION\n");
 	    if (!ctrl->sent) {
@@ -1202,7 +1265,7 @@
 			HTCleanTelnetString(segment);
 			ctrl->substate = NEED_CWD;
 		    } else
-			ctrl->substate = NEED_ACTION;
+			ctrl->substate = NEED_REST;
 		}
 	    }
 	    break;
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.