Ejemplo n.º 1
0
/*	Push data from a gzip file pointer down a stream
**	-------------------------------------
**
**   This routine is responsible for creating and PRESENTING any
**   graphic (or other) objects described by the file.
**
**
**  State of file and target stream on entry:
**		      gzFile (gzfp) assumed open (should have gzipped content),
**		      target (sink) assumed valid.
**
**  Return values:
**	HT_INTERRUPTED  Interruption after some data read.
**	HT_PARTIAL_CONTENT	Error after some data read.
**	-1		Error before any data read.
**	HT_LOADED	Normal end of file indication on reading.
**
**  State of file and target stream on return:
**	always		gzfp still open, target stream still valid.
*/
PRIVATE int HTGzFileCopy ARGS2(
	gzFile,			gzfp,
	HTStream*,		sink)
{
    HTStreamClass targetClass;
    int status, bytes;
    int gzerrnum;
    int rv = HT_OK;

    /*	Push the data down the stream
    */
    targetClass = *(sink->isa); /* Copy pointers to procedures */

    /*	read and inflate gzip'd file, and push binary down sink
    */
    HTReadProgress(bytes = 0, 0);
    for (;;) {
	status = gzread(gzfp, input_buffer, INPUT_BUFFER_SIZE);
	if (status <= 0) { /* EOF or error */
	    if (status == 0) {
		rv = HT_LOADED;
		break;
	    }
	    CTRACE((tfp, "HTGzFileCopy: Read error, gzread returns %d\n",
			status));
	    CTRACE((tfp, "gzerror   : %s\n",
			gzerror(gzfp, &gzerrnum)));
	    if (TRACE) {
		if (gzerrnum == Z_ERRNO)
		    perror("gzerror   ");
	    }
	    if (bytes) {
		rv = HT_PARTIAL_CONTENT;
	    } else {
		rv = -1;
	    }
	    break;
	}

	(*targetClass.put_block)(sink, input_buffer, status);
	bytes += status;
	HTReadProgress(bytes, -1);
	HTDisplayPartial();

	if (HTCheckForInterrupt()) {
	    _HTProgress (TRANSFER_INTERRUPTED);
	    if (bytes) {
		rv = HT_INTERRUPTED;
	    } else {
		rv = -1;
	    }
	    break;
	}
    } /* next bufferload */

    HTFinishDisplayPartial();
    return rv;
}
Ejemplo n.º 2
0
/*	Push data from a file pointer down a stream
**	-------------------------------------
**
**   This routine is responsible for creating and PRESENTING any
**   graphic (or other) objects described by the file.
**
**
**  State of file and target stream on entry:
**			FILE* (fp) assumed open,
**			target (sink) assumed valid.
**
**  Return values:
**	HT_INTERRUPTED  Interruption after some data read.
**	HT_PARTIAL_CONTENT	Error after some data read.
**	-1		Error before any data read.
**	HT_LOADED	Normal end of file indication on reading.
**
**  State of file and target stream on return:
**	always		fp still open, target stream still valid.
*/
PUBLIC int HTFileCopy ARGS2(
	FILE *,			fp,
	HTStream*,		sink)
{
    HTStreamClass targetClass;
    int status, bytes;
    int rv = HT_OK;

    /*	Push the data down the stream
    */
    targetClass = *(sink->isa); /* Copy pointers to procedures */

    /*	Push binary from socket down sink
    */
    HTReadProgress(bytes = 0, 0);
    for (;;) {
	status = fread(input_buffer, 1, INPUT_BUFFER_SIZE, fp);
	if (status == 0) { /* EOF or error */
	    if (ferror(fp) == 0) {
		rv = HT_LOADED;
		break;
	    }
	    CTRACE((tfp, "HTFormat: Read error, read returns %d\n",
			ferror(fp)));
	    if (bytes) {
		rv = HT_PARTIAL_CONTENT;
	    } else {
		rv = -1;
	    }
	    break;
	}

	(*targetClass.put_block)(sink, input_buffer, status);
	bytes += status;
	HTReadProgress(bytes, 0);
	/*  Suppress last screen update in partial mode - a regular update
	 *  under control of mainloop() should follow anyway. - kw
	 */
#ifdef DISP_PARTIAL
	if (display_partial && bytes != HTMainAnchor->content_length)
	    HTDisplayPartial();
#endif

	if (HTCheckForInterrupt()) {
	    _HTProgress (TRANSFER_INTERRUPTED);
	    if (bytes) {
		rv = HT_INTERRUPTED;
	    } else {
		rv = -1;
	    }
	    break;
	}
    } /* next bufferload */

    HTFinishDisplayPartial();
    return rv;
}
Ejemplo n.º 3
0
/*	Free an HTML object
**	-------------------
**
**	Note that the SGML parsing context is freed, but the created
**	object is not,
**	as it takes on an existence of its own unless explicitly freed.
*/
PRIVATE void HTFWriter_free ARGS1(HTStream *, me)
{
    fclose(me->fp);
    if (me->end_command) {		/* Temp file */
	_HTProgress(me->end_command);	/* Tell user what's happening */
	system(me->end_command);
	FREE(me->end_command);
	if (me->remove_command) {
	    system(me->remove_command);
	    FREE(me->remove_command);
	}
    }

    FREE(me);
}
Ejemplo n.º 4
0
/*	Push data from an HTChunk down a stream
**	---------------------------------------
**
**   This routine is responsible for creating and PRESENTING any
**   graphic (or other) objects described by the file.
**
**  State of memory and target stream on entry:
**			HTChunk* (chunk) and target (sink) assumed valid.
**
**  Return values:
**	HT_LOADED	All data sent.
**	HT_INTERRUPTED  Interruption after some data read.
**
**  State of memory and target stream on return:
**	always		chunk unchanged, target stream still valid.
*/
PUBLIC int HTMemCopy ARGS2(
	HTChunk *,		chunk,
	HTStream *,		sink)
{
    HTStreamClass targetClass;
    int bytes = 0;
    CONST char *data = chunk->data;
    int rv = HT_OK;

    targetClass = *(sink->isa);
    HTReadProgress(0, 0);
    for (;;) {
	/* Push the data down the stream a piece at a time, in case we're
	** running a large document on a slow machine.
	*/
	int n = INPUT_BUFFER_SIZE;
	if (n > chunk->size - bytes)
	    n = chunk->size - bytes;
	if (n == 0)
	    break;
	(*targetClass.put_block)(sink, data, n);
	bytes += n;
	data += n;
	HTReadProgress(bytes, 0);
	HTDisplayPartial();

	if (HTCheckForInterrupt()) {
	    _HTProgress (TRANSFER_INTERRUPTED);
	    if (bytes) {
		rv = HT_INTERRUPTED;
	    } else {
		rv = -1;
	    }
	    break;
	}
    }

    HTFinishDisplayPartial();
    return rv;
}
Ejemplo n.º 5
0
static int response(char *command,
		    char *sitename,
		    HTParentAnchor *anAnchor,
		    HTFormat format_out,
		    HTStream *sink)
{
    int status;
    int length = (int) strlen(command);
    int ch, i;
    char line[BIG], *l, *cmd = NULL;
    char *p = line, *href = NULL;

    if (length == 0)
	return (-1);

    /* Set up buffering.
     */
    HTInitInput(finger_fd);

    /* Send the command.
     */
    CTRACE((tfp, "HTFinger command to be sent: %s", command));
    status = (int) NETWRITE(finger_fd, (char *) command, (unsigned) length);
    if (status < 0) {
	CTRACE((tfp, "HTFinger: Unable to send command. Disconnecting.\n"));
	NETCLOSE(finger_fd);
	finger_fd = -1;
	return status;
    }
    /* if bad status */
    /* Make a hypertext object with an anchor list.
     */
    target = HTML_new(anAnchor, format_out, sink);
    targetClass = *target->isa;	/* Copy routine entry points */

    /* Create the results report.
     */
    CTRACE((tfp, "HTFinger: Reading finger information\n"));
    START(HTML_HTML);
    PUTC('\n');
    START(HTML_HEAD);
    PUTC('\n');
    START(HTML_TITLE);
    PUTS("Finger server on ");
    PUTS(sitename);
    END(HTML_TITLE);
    PUTC('\n');
    END(HTML_HEAD);
    PUTC('\n');
    START(HTML_BODY);
    PUTC('\n');
    START(HTML_H1);
    PUTS("Finger server on ");
    START(HTML_EM);
    PUTS(sitename);
    END(HTML_EM);
    PUTS(": ");
    StrAllocCopy(cmd, command);
    for (i = ((int) strlen(cmd) - 1); i >= 0; i--) {
	if (cmd[i] == LF || cmd[i] == CR) {
	    cmd[i] = '\0';
	} else {
	    break;
	}
    }
    PUTS(cmd);
    FREE(cmd);
    END(HTML_H1);
    PUTC('\n');
    START(HTML_PRE);

    while ((ch = NEXT_CHAR) != EOF) {

	if (interrupted_in_htgetcharacter) {
	    CTRACE((tfp,
		    "HTFinger: Interrupted in HTGetCharacter, apparently.\n"));
	    _HTProgress(CONNECTION_INTERRUPTED);
	    goto end_html;
	}

	if (ch != LF) {
	    *p = (char) ch;	/* Put character in line */
	    if (p < &line[BIG - 1]) {
		p++;
	    }
	} else {
	    *p = '\0';		/* Terminate line */
	    /*
	     * OK we now have a line.
	     * Load it as 'l' and parse it.
	     */
	    p = l = line;
	    while (*l) {
		if (StrNCmp(l, STR_NEWS_URL, LEN_NEWS_URL) &&
		    StrNCmp(l, "snews://", 8) &&
		    StrNCmp(l, "nntp://", 7) &&
		    StrNCmp(l, "snewspost:", 10) &&
		    StrNCmp(l, "snewsreply:", 11) &&
		    StrNCmp(l, "newspost:", 9) &&
		    StrNCmp(l, "newsreply:", 10) &&
		    StrNCmp(l, "ftp://", 6) &&
		    StrNCmp(l, "file:/", 6) &&
		    StrNCmp(l, "finger://", 9) &&
		    StrNCmp(l, "http://", 7) &&
		    StrNCmp(l, "https://", 8) &&
		    StrNCmp(l, "wais://", 7) &&
		    StrNCmp(l, STR_MAILTO_URL, LEN_MAILTO_URL) &&
		    StrNCmp(l, "cso://", 6) &&
		    StrNCmp(l, "gopher://", 9))
		    PUTC(*l++);
		else {
		    StrAllocCopy(href, l);
		    start_anchor(strtok(href, " \r\n\t,>)\""));
		    while (*l && !StrChr(" \r\n\t,>)\"", *l))
			PUTC(*l++);
		    END(HTML_A);
		    FREE(href);
		}
	    }
	    PUTC('\n');
	}
    }
    NETCLOSE(finger_fd);
    finger_fd = -1;

  end_html:
    END(HTML_PRE);
    PUTC('\n');
    END(HTML_BODY);
    PUTC('\n');
    END(HTML_HTML);
    PUTC('\n');
    FREE_TARGET;
    return (0);
}
Ejemplo n.º 6
0
/*							HTVMSBrowseDir()
**
**	This function generates a directory listing as an HTML-object
**	for local file URL's.  It assumes the first two elements of
**	of the path are a device followed by a directory:
**
**		file://localhost/device/directory[/[foo]]
**
**	Will not accept 000000 as a directory name.
**	Will offer links to parent through the top directory, unless
**	a terminal slash was included in the calling URL.
**
**	Returns HT_LOADED on success, HTLoadError() messages on error.
**
**	Developed for Lynx by Foteos Macrides ([email protected]).
*/
PUBLIC int HTVMSBrowseDir ARGS4(
	CONST char *,		address,
	HTParentAnchor *,	anchor,
	HTFormat,		format_out,
	HTStream *,		sink
)
{
    HTStructured* target;
    HTStructuredClass targetClass;
    char *pathname = HTParse(address, "", PARSE_PATH + PARSE_PUNCTUATION);
    char *tail = NULL;
    char *title = NULL;
    char *header = NULL;
    char *parent = NULL;
    char *relative = NULL;
    char *cp, *cp1;
    int  pathend, len;
    DIR *dp;
    struct stat file_info;
    time_t NowTime;
    static char ThisYear[8];
    VMSEntryInfo *entry_info = 0;
    char string_buffer[64];

    HTUnEscape(pathname);
    CTRACE((tfp,"HTVMSBrowseDir: Browsing `%s\'\n", pathname));

    /*
     *  Require at least two elements (presumably a device and directory)
     *  and disallow the device root (000000 directory).  Symbolic paths
     *  (e.g., sys$help) should have been translated and expanded (e.g.,
     *  to /sys$sysroot/syshlp) before calling this routine.
     */
    if (((*pathname != '/') ||
	 (cp = strchr(pathname+1, '/')) == NULL ||
	 *(cp + 1) == '\0' ||
	 0 == strncmp((cp + 1), "000000", 6)) ||
	(dp = HTVMSopendir(pathname)) == NULL) {
	FREE(pathname);
	return HTLoadError(sink, 403, COULD_NOT_ACCESS_DIR);
    }

    /*
     *  Set up the output stream.
     */
    _HTProgress (BUILDING_DIR_LIST);
    if (UCLYhndl_HTFile_for_unspec >= 0) {
	HTAnchor_setUCInfoStage(anchor,
				UCLYhndl_HTFile_for_unspec,
				UCT_STAGE_PARSER,
				UCT_SETBY_DEFAULT);
    }
    target = HTML_new(anchor, format_out, sink);
    targetClass = *(target->isa);

    /*
     *  Set up the offset string of the anchor reference,
     *  and strings for the title and header.
     */
    cp = strrchr(pathname, '/');  /* find lastslash */
    StrAllocCopy(tail, (cp+1)); /* take slash off the beginning */
    if (*tail != '\0') {
	StrAllocCopy(title, tail);
	*cp = '\0';
	if ((cp1=strrchr(pathname, '/')) != NULL &&
	    cp1 != pathname &&
	    strncmp((cp1+1), "000000", 6))
	    StrAllocCopy(parent, (cp1+1));
	*cp = '/';
    } else {
	pathname[strlen(pathname)-1] = '\0';
	cp = strrchr(pathname, '/');
	StrAllocCopy(title, (cp+1));
	pathname[strlen(pathname)] = '/';
    }
    StrAllocCopy(header, pathname);

    /*
     *  Initialize path name for HTStat().
     */
    pathend = strlen(pathname);
    if (*(pathname+pathend-1) != '/') {
	StrAllocCat(pathname, "/");
	pathend++;
    }

    /*
     *  Output the title and header.
     */
    START(HTML_HTML);
    PUTC('\n');
    START(HTML_HEAD);
    PUTC('\n');
    HTUnEscape(title);
    START(HTML_TITLE);
    PUTS(title);
    PUTS(" directory");
    END(HTML_TITLE);
    PUTC('\n');
    FREE(title);
    END(HTML_HEAD);
    PUTC('\n');
    START(HTML_BODY);
    PUTC('\n');
    HTUnEscape(header);
    START(HTML_H1);
    PUTS(header);
    END(HTML_H1);
    PUTC('\n');
    if (HTDirReadme == HT_DIR_README_TOP) {
	FILE * fp;
	if (header[strlen(header)-1] != '/')
	    StrAllocCat(header, "/");
	StrAllocCat(header, HT_DIR_README_FILE);
	if ((fp = fopen(header,	 "r")) != NULL) {
	    START(HTML_PRE);
	    for(;;) {
		char c = fgetc(fp);
		if (c == (char)EOF)
		    break;
#ifdef NOTDEFINED
		switch (c) {
		    case '&':
		    case '<':
		    case '>':
			PUTC('&');
			PUTC('#');
			PUTC((char)(c / 10));
			PUTC((char) (c % 10));
			PUTC(';');
			break;
		    default:
			PUTC(c);
		}
#else
		PUTC(c);
#endif /* NOTDEFINED */
	    }
	    END(HTML_PRE);
	    fclose(fp);
	}
    }
    FREE(header);
    if (parent) {
	HTSprintf0(&relative, "%s/..", tail);
	HTStartAnchor(target, "", relative);
	PUTS("Up to ");
	HTUnEscape(parent);
	PUTS(parent);
	END(HTML_A);
	START(HTML_P);
	PUTC('\n');
	FREE(relative);
	FREE(parent);
    }

    /*
     *  Set up the date comparison.
     */
    NowTime = time(NULL);
    strcpy(ThisYear, (char *)ctime(&NowTime)+20);
    ThisYear[4] = '\0';

    /*
     * Now, generate the Btree and put it out to the output stream.
     */
    {
	char dottest = 2;	/* To avoid two strcmp() each time */
	STRUCT_DIRENT *dirbuf;
	HTBTree *bt;

	/* Set up sort key and initialize BTree */
	bt = HTBTree_new((HTComparer) compare_VMSEntryInfo_structs);

	/* Build tree */
	while ((dirbuf = HTVMSreaddir(dp))) {
	    HTAtom *encoding = NULL;
	    HTFormat format;

	    /* Skip if not used */
	    if (!dirbuf->d_ino)	{
		continue;
	    }

	    /* Current and parent directories are never shown in list */
	    if (dottest && (!strcmp(dirbuf->d_name, ".") ||
			    !strcmp(dirbuf->d_name, ".."))) {
		dottest--;
		continue;
	    }

	    /* Don't show the selective enabling file
	     * unless version numbers are included */
	    if (!strcasecomp(dirbuf->d_name, HT_DIR_ENABLE_FILE)) {
		continue;
	    }

	    /* Skip files beginning with a dot? */
	    if ((no_dotfiles || !show_dotfiles) && *dirbuf->d_name == '.') {
		continue;
	    }

	    /* OK, make an lstat() and get a key ready. */
	    *(pathname+pathend) = '\0';
	    StrAllocCat(pathname, dirbuf->d_name);
	    if (HTStat(pathname, &file_info)) {
		/* for VMS the failure here means the file is not readable...
		   we however continue to browse through the directory... */
		continue;
	    }
	    entry_info = (VMSEntryInfo *)malloc(sizeof(VMSEntryInfo));
	    if (entry_info == NULL)
		outofmem(__FILE__, "HTVMSBrowseDir");
	    entry_info->type = 0;
	    entry_info->size = 0;
	    entry_info->date = 0;
	    entry_info->filename = 0;
	    entry_info->display = TRUE;

	    /* Get the type */
	    format = HTFileFormat(dirbuf->d_name, &encoding,
				  (CONST char **)&cp);
	    if (!cp) {
		if(!strncmp(HTAtom_name(format), "application",11))
		{
		    cp = HTAtom_name(format) + 12;
		    if(!strncmp(cp,"x-", 2))
			cp += 2;
		}
		else
		    cp = HTAtom_name(format);
	    }
	    StrAllocCopy(entry_info->type, cp);

	    StrAllocCopy(entry_info->filename, dirbuf->d_name);
	    if (S_ISDIR(file_info.st_mode)) {
		/* strip .DIR part... */
		char *dot;
		dot = strstr(entry_info->filename, ".DIR");
		if (dot)
		   *dot = '\0';
		LYLowerCase(entry_info->filename);
		StrAllocCopy(entry_info->type, "Directory");
	    } else {
		if ((cp = strstr(entry_info->filename, "READ")) == NULL) {
		    cp = entry_info->filename;
		} else {
		    cp += 4;
		    if (!strncmp(cp, "ME", 2)) {
			cp += 2;
			while (cp && *cp && *cp != '.') {
			    cp++;
			}
		    } else if (!strncmp(cp, ".ME", 3)) {
			cp = (entry_info->filename +
			      strlen(entry_info->filename));
		    } else {
			cp = entry_info->filename;
		    }
		}
		LYLowerCase(cp);
		if (((len = strlen(entry_info->filename)) > 2) &&
		    entry_info->filename[len-1] == 'z') {
		    if (entry_info->filename[len-2] == '.' ||
			entry_info->filename[len-2] == '_')
			entry_info->filename[len-1] = 'Z';
		}
	    }

	    /* Get the date */
	    {
		char *t = (char *)ctime((CONST time_t *)&file_info.st_ctime);
		*(t+24) = '\0';

		StrAllocCopy(entry_info->date, (t+4));
		*((entry_info->date)+7) = '\0';
		if ((atoi((t+19))) < atoi(ThisYear))
		    StrAllocCat(entry_info->date,  (t+19));
		else {
		    StrAllocCat(entry_info->date, (t+11));
		    *((entry_info->date)+12) = '\0';
		}
	    }

	    /* Get the size */
	    if (!S_ISDIR(file_info.st_mode))
		entry_info->size = (unsigned int)file_info.st_size;
	    else
		entry_info->size = 0;

	    /* Now, update the BTree etc. */
	    if(entry_info->display)
	      {
		 CTRACE((tfp,"Adding file to BTree: %s\n",
						      entry_info->filename));
		 HTBTree_add(bt, entry_info);
	      }

	} /* End while HTVMSreaddir() */

	FREE(pathname);
	HTVMSclosedir(dp);

	START(HTML_PRE);
	/*
	 * Run through the BTree printing out in order
	 */
	{
	    HTBTElement * ele;
	    int i;
	    for (ele = HTBTree_next(bt, NULL);
		 ele != NULL;
		 ele = HTBTree_next(bt, ele))
	    {
		entry_info = (VMSEntryInfo *)HTBTree_object(ele);

		/* Output the date */
		if(entry_info->date)
		       {
			     PUTS(entry_info->date);
			     PUTS("  ");
		       }
		else
			PUTS("     * ");

		/* Output the type */
		if(entry_info->type)
		  {
		    for(i = 0; entry_info->type[i] != '\0' && i < 15; i++)
			PUTC(entry_info->type[i]);
		    for(; i < 17; i++)
			PUTC(' ');

		  }

		/* Output the link for the name */
		HTDirEntry(target, tail, entry_info->filename);
		PUTS(entry_info->filename);
		END(HTML_A);

		/* Output the size */
		if(entry_info->size)
		  {
			  if(entry_info->size < 1024)
			      sprintf(string_buffer,"  %d bytes",
							entry_info->size);
			  else
			      sprintf(string_buffer,"  %dKb",
							entry_info->size/1024);
			  PUTS(string_buffer);
		  }

		PUTC('\n'); /* end of this entry */

		free_VMSEntryInfo_contents(entry_info);
	    }
	}

	HTBTreeAndObject_free(bt);

    } /* End of both BTree loops */

    /*
     *  Complete the output stream.
     */
    END(HTML_PRE);
    PUTC('\n');
    END(HTML_BODY);
    PUTC('\n');
    END(HTML_HTML);
    PUTC('\n');
    FREE(tail);
    FREE_TARGET;

    return HT_LOADED;

} /* End of directory reading section */
Ejemplo n.º 7
0
/*	Push data from a socket down a stream
**	-------------------------------------
**
**   This routine is responsible for creating and PRESENTING any
**   graphic (or other) objects described by the file.
**
**   The file number given is assumed to be a TELNET stream, i.e., containing
**   CRLF at the end of lines which need to be stripped to LF for unix
**   when the format is textual.
**
**  State of socket and target stream on entry:
**			socket (file_number) assumed open,
**			target (sink) assumed valid.
**
**  Return values:
**	HT_INTERRUPTED  Interruption or error after some data received.
**	-2		Unexpected disconnect before any data received.
**	-1		Interruption or error before any data received, or
**			(UNIX) other read error before any data received, or
**			download cancelled.
**	HT_LOADED	Normal close of socket (end of file indication
**			received), or
**			unexpected disconnect after some data received, or
**			other read error after some data received, or
**			(not UNIX) other read error before any data received.
**
**  State of socket and target stream on return depends on return value:
**	HT_INTERRUPTED	socket still open, target aborted.
**	-2		socket still open, target stream still valid.
**	-1		socket still open, target aborted.
**	otherwise	socket closed,	target stream still valid.
*/
PUBLIC int HTCopy ARGS4(
	HTParentAnchor *,	anchor,
	int,			file_number,
	void*,			handle GCC_UNUSED,
	HTStream*,		sink)
{
    HTStreamClass targetClass;
    BOOL suppress_readprogress = NO;
    int bytes;
    int rv = 0;

    /*	Push the data down the stream
    */
    targetClass = *(sink->isa); /* Copy pointers to procedures */

    /*	Push binary from socket down sink
    **
    **	This operation could be put into a main event loop
    */
    HTReadProgress(bytes = 0, 0);
    for (;;) {
	int status;

	if (LYCancelDownload) {
	    LYCancelDownload = FALSE;
	    (*targetClass._abort)(sink, NULL);
	    rv = -1;
	    goto finished;
	}

	if (HTCheckForInterrupt()) {
	    _HTProgress (TRANSFER_INTERRUPTED);
	    (*targetClass._abort)(sink, NULL);
	    if (bytes)
		rv = HT_INTERRUPTED;
	    else
		rv = -1;
	    goto finished;
	}

#ifdef USE_SSL
	if (handle)
	    status = SSL_read((SSL *)handle, input_buffer, INPUT_BUFFER_SIZE);
	else
	    status = NETREAD(file_number, input_buffer, INPUT_BUFFER_SIZE);
#else
	status = NETREAD(file_number, input_buffer, INPUT_BUFFER_SIZE);
#endif /* USE_SSL */

	if (status <= 0) {
	    if (status == 0) {
		break;
	    } else if (status == HT_INTERRUPTED) {
		_HTProgress (TRANSFER_INTERRUPTED);
		(*targetClass._abort)(sink, NULL);
		if (bytes)
		    rv = HT_INTERRUPTED;
		else
		    rv = -1;
		goto finished;
	    } else if (SOCKET_ERRNO == ENOTCONN ||
#ifdef _WINDOWS	/* 1997/11/10 (Mon) 16:57:18 */
		       SOCKET_ERRNO == ETIMEDOUT ||
#endif
		       SOCKET_ERRNO == ECONNRESET ||
		       SOCKET_ERRNO == EPIPE) {
		/*
		 *  Arrrrgh, HTTP 0/1 compatibility problem, maybe.
		 */
		if (bytes <= 0) {
		    /*
		     *	Don't have any data, so let the calling
		     *	function decide what to do about it. - FM
		     */
		    rv = -2;
		    goto finished;
		} else {
#ifdef UNIX
		   /*
		    *  Treat what we've received already as the complete
		    *  transmission, but not without giving the user
		    *  an alert.  I don't know about all the different
		    *  TCP stacks for VMS etc., so this is currently
		    *  only for UNIX. - kw
		    */
		   HTInetStatus("NETREAD");
		   HTAlert("Unexpected server disconnect.");
		   CTRACE((tfp,
	    "HTCopy: Unexpected server disconnect. Treating as completed.\n"));
		   status = 0;
		   break;
#else  /* !UNIX */
		   /*
		    *  Treat what we've gotten already
		    *  as the complete transmission. - FM
		    */
		   CTRACE((tfp,
	    "HTCopy: Unexpected server disconnect.  Treating as completed.\n"));
		   status = 0;
		   break;
#endif /* UNIX */
		}
#ifdef UNIX
	    } else {		/* status < 0 and other errno */
		/*
		 *  Treat what we've received already as the complete
		 *  transmission, but not without giving the user
		 *  an alert.  I don't know about all the different
		 *  TCP stacks for VMS etc., so this is currently
		 *  only for UNIX. - kw
		 */
		HTInetStatus("NETREAD");
		HTAlert("Unexpected read error.");
		if (bytes) {
		    (void)NETCLOSE(file_number);
		    rv = HT_LOADED;
		} else {
		    (*targetClass._abort)(sink, NULL);
		    rv = -1;
		}
		goto finished;
#endif
	    }
	    break;
	}

	/*
	 *  Suppress ReadProgress messages when collecting a redirection
	 *  message, at least initially (unless/until anchor->content_type
	 *  gets changed, probably by the MIME message parser).  That way
	 *  messages put up by the HTTP module or elsewhere can linger in
	 *  the statusline for a while. - kw
	 */
	suppress_readprogress = (anchor && anchor->content_type &&
				 !strcmp(anchor->content_type,
					 "message/x-http-redirection"));
#ifdef NOT_ASCII
	{
	    char * p;
	    for (p = input_buffer; p < input_buffer+status; p++) {
		*p = FROMASCII(*p);
	    }
	}
#endif /* NOT_ASCII */

	(*targetClass.put_block)(sink, input_buffer, status);
	bytes += status;
	if (!suppress_readprogress)
	    HTReadProgress(bytes, anchor ? anchor->content_length : 0);
	HTDisplayPartial();

    } /* next bufferload */

    _HTProgress(TRANSFER_COMPLETE);
    (void)NETCLOSE(file_number);
    rv = HT_LOADED;

finished:
    HTFinishDisplayPartial();
    return(rv);
}