about summary refs log tree commit diff stats
path: root/WWW
diff options
context:
space:
mode:
authorThomas E. Dickey <dickey@invisible-island.net>1997-08-17 13:18:55 -0400
committerThomas E. Dickey <dickey@invisible-island.net>1997-08-17 13:18:55 -0400
commitfbcb509d8db6255777742b09da479531108aa8d0 (patch)
treef1b14aa04f7c8eee2464f4575113db0c4c680900 /WWW
parent050803d49a91559644e86a4ed38f11ba0afffbe4 (diff)
downloadlynx-snapshots-fbcb509d8db6255777742b09da479531108aa8d0.tar.gz
snapshot of project "lynx", label v2-7-1ac_0-52
Diffstat (limited to 'WWW')
-rw-r--r--WWW/Library/Implementation/HTAlert.h4
-rw-r--r--WWW/Library/Implementation/HTAnchor.c22
-rw-r--r--WWW/Library/Implementation/HTAnchor.h16
-rw-r--r--WWW/Library/Implementation/HTFile.c237
-rw-r--r--WWW/Library/Implementation/HTFormat.c165
-rw-r--r--WWW/Library/Implementation/HTFormat.h20
-rw-r--r--WWW/Library/Implementation/HTMIME.c23
-rw-r--r--WWW/Library/Implementation/HTML.h4
-rw-r--r--WWW/Library/Implementation/HTNews.c373
-rw-r--r--WWW/Library/Implementation/HTStyle.h2
-rw-r--r--WWW/Library/Implementation/HTTCP.c107
-rw-r--r--WWW/Library/Implementation/HTTP.c139
-rw-r--r--WWW/Library/Implementation/HTUtils.h2
-rw-r--r--WWW/Library/djgpp/makefile2
14 files changed, 982 insertions, 134 deletions
diff --git a/WWW/Library/Implementation/HTAlert.h b/WWW/Library/Implementation/HTAlert.h
index ec1d2397..33d31f11 100644
--- a/WWW/Library/Implementation/HTAlert.h
+++ b/WWW/Library/Implementation/HTAlert.h
@@ -112,6 +112,7 @@ extern BOOL HTConfirmCookie PARAMS((
 **	----------------------------
 **  On entry,
 **      redirecting_url             is the Location.
+**	server_status		    is the server status code.
 **
 **  On exit,
 **      Returns 0 on cancel,
@@ -119,7 +120,8 @@ extern BOOL HTConfirmCookie PARAMS((
 **	303 for redirect as GET without content
 */
 extern int HTConfirmPostRedirect PARAMS((
-	CONST char *	redirecting_url));
+	CONST char *	redirecting_url,
+	int		server_status));
 
 /*
 
diff --git a/WWW/Library/Implementation/HTAnchor.c b/WWW/Library/Implementation/HTAnchor.c
index 49ef2090..397c3684 100644
--- a/WWW/Library/Implementation/HTAnchor.c
+++ b/WWW/Library/Implementation/HTAnchor.c
@@ -949,8 +949,8 @@ PUBLIC void HTAnchor_setRevTitle ARGS2(
 }
 
 /*	Suggested filename handling. - FM
-**	(will be loaded if we had a Content-disposition
-**	 header with file; filename=name.suffix)
+**	(will be loaded if we had a Content-Disposition
+**	 header or META element with filename=name.suffix)
 */
 PUBLIC CONST char * HTAnchor_SugFname ARGS1(
 	HTParentAnchor *,	me)
@@ -958,6 +958,24 @@ PUBLIC CONST char * HTAnchor_SugFname ARGS1(
     return me ? me->SugFname : NULL;
 }
 
+/*	Content-Encoding handling. - FM
+**	(will be loaded if we had a Content-Encoding
+**	 header.)
+*/
+PUBLIC CONST char * HTAnchor_content_encoding ARGS1(
+	HTParentAnchor *,	me)
+{
+    return me ? me->content_encoding : NULL;
+}
+
+/*	Content-Type handling. - FM
+*/
+PUBLIC CONST char * HTAnchor_content_type ARGS1(
+	HTParentAnchor *,	me)
+{
+    return me ? me->content_type : NULL;
+}
+
 /*	Last-Modified header handling. - FM
 */
 PUBLIC CONST char * HTAnchor_last_modified ARGS1(
diff --git a/WWW/Library/Implementation/HTAnchor.h b/WWW/Library/Implementation/HTAnchor.h
index 052290cf..8818b36d 100644
--- a/WWW/Library/Implementation/HTAnchor.h
+++ b/WWW/Library/Implementation/HTAnchor.h
@@ -313,12 +313,24 @@ extern void HTAnchor_setRevTitle PARAMS((
 	CONST char *		title));
 
 /*	Suggested filename handling. - FM
-**	(will be loaded if we had a Content-disposition
-**	 header with file; filename=name.suffix)
+**	(will be loaded if we had a Content-Disposition
+**	 header or META element with filename=name.suffix)
 */
 extern CONST char * HTAnchor_SugFname PARAMS((
 	HTParentAnchor *	me));
 
+/*	Content-Type handling. - FM
+*/
+extern CONST char * HTAnchor_content_type PARAMS((
+	HTParentAnchor *	me));
+
+/*	Content-Encoding handling. - FM
+**	(will be loaded if we had a Content-Encoding
+**	 header.)
+*/
+extern CONST char * HTAnchor_content_encoding PARAMS((
+	HTParentAnchor *	me));
+
 /*	Last-Modified header handling. - FM
 */
 extern CONST char * HTAnchor_last_modified PARAMS((
diff --git a/WWW/Library/Implementation/HTFile.c b/WWW/Library/Implementation/HTFile.c
index 99f0cc41..53f082ab 100644
--- a/WWW/Library/Implementation/HTFile.c
+++ b/WWW/Library/Implementation/HTFile.c
@@ -705,7 +705,8 @@ PUBLIC HTFormat HTFileFormat ARGS2(
     if (!HTSuffixes)
         HTFileInit();
 #endif /* !NO_INIT */
-    *pencoding = NULL;
+    if (pencoding)
+	*pencoding = NULL;
     lf  = strlen(filename);
     n = HTList_count(HTSuffixes);
     for (i = 0; i < n; i++) {
@@ -714,7 +715,8 @@ PUBLIC HTFormat HTFileFormat ARGS2(
 	ls = strlen(suff->suffix);
 	if ((ls <= lf) && 0 == strcasecomp(suff->suffix, filename + lf - ls)) {
 	    int j;
-	    *pencoding = suff->encoding;
+	    if (pencoding)
+		*pencoding = suff->encoding;
 	    if (suff->rep) {
 #ifdef VMS
 		if (semicolon != NULL)
@@ -750,7 +752,7 @@ PUBLIC HTFormat HTFileFormat ARGS2(
     /*
     **  Set default encoding unless found with suffix already.
     */
-    if (!*pencoding)
+    if (pencoding && !*pencoding)
         *pencoding = suff->encoding ? suff->encoding
 				    : HTAtom_for("binary");
 #ifdef VMS
@@ -772,7 +774,6 @@ PUBLIC HTFormat HTCharsetFormat ARGS3(
 	HTFormat,		format,
 	HTParentAnchor *,	anchor,
 	int,			default_LYhndl)
-
 {
     char *cp = NULL, *cp1, *cp2, *cp3 = NULL, *cp4;
     BOOL chartrans_ok = FALSE;
@@ -794,9 +795,9 @@ PUBLIC HTFormat HTCharsetFormat ARGS3(
 	    cp2++;
 #ifdef EXP_CHARTRANS
 	StrAllocCopy(cp3, cp2); /* copy to mutilate more */
-	for (cp4=cp3; (*cp4 != '\0' && *cp4 != '"' &&
-		       *cp4 != ';'  && *cp4 != ':' &&
-		       !WHITE(*cp4)); cp4++) {
+	for (cp4 = cp3; (*cp4 != '\0' && *cp4 != '"' &&
+			 *cp4 != ';'  && *cp4 != ':' &&
+			 !WHITE(*cp4)); cp4++) {
 	    ; /* do nothing */
 	}
 	*cp4 = '\0';
@@ -813,29 +814,29 @@ PUBLIC HTFormat HTCharsetFormat ARGS3(
 	    }
 	}
 	if (UCCanTranslateFromTo(chndl, current_char_set)) {
-				chartrans_ok = YES;
-				*cp1 = '\0';
-				format = HTAtom_for(cp);
-				StrAllocCopy(anchor->charset, cp4);
-				HTAnchor_setUCInfoStage(anchor, chndl,
-				   UCT_STAGE_MIME, UCT_SETBY_MIME);
+	    chartrans_ok = YES;
+	    *cp1 = '\0';
+	    format = HTAtom_for(cp);
+	    StrAllocCopy(anchor->charset, cp4);
+	    HTAnchor_setUCInfoStage(anchor, chndl,
+				    UCT_STAGE_MIME, UCT_SETBY_MIME);
 	} else if (chndl < 0) {
 	    /*
 	    **  Got something but we don't recognize it.
 	    */
-				chndl = UCLYhndl_for_unrec;
+	    chndl = UCLYhndl_for_unrec;
 	    if (UCCanTranslateFromTo(chndl, current_char_set)) {
-				    chartrans_ok = YES;
-				    HTAnchor_setUCInfoStage(anchor, chndl,
-				       UCT_STAGE_MIME, UCT_SETBY_DEFAULT);
-				}
-			    }
-			    FREE(cp3);
-			    if (chartrans_ok) {
+		chartrans_ok = YES;
+		HTAnchor_setUCInfoStage(anchor, chndl,
+					UCT_STAGE_MIME, UCT_SETBY_DEFAULT);
+	    }
+	}
+	FREE(cp3);
+	if (chartrans_ok) {
 	    LYUCcharset *p_in = HTAnchor_getUCInfoStage(anchor,
-							     UCT_STAGE_MIME);
+							UCT_STAGE_MIME);
 	    LYUCcharset *p_out = HTAnchor_setUCInfoStage(anchor,
-							    current_char_set,
+							 current_char_set,
 							 UCT_STAGE_HTEXT,
 							 UCT_SETBY_DEFAULT);
 	    if (!p_out) {
@@ -861,7 +862,7 @@ PUBLIC HTFormat HTCharsetFormat ARGS3(
 					UCT_SETBY_DEFAULT);
 	    }
 	    if (!(p_in->enc & UCT_ENC_CJK) &&
-		(p_in->codepoints & UCT_CP_SUBSETOF_LAT1)){
+		(p_in->codepoints & UCT_CP_SUBSETOF_LAT1)) {
 		HTCJK = NOCJK;
 	    } else if (chndl == current_char_set) {
 		HTPassEightBitRaw = TRUE;
@@ -1421,6 +1422,10 @@ PUBLIC int HTLoadFile ARGS4(
 #else
     extern char *list_format;
 #endif /* VMS */
+#ifdef USE_ZLIB
+    gzFile gzfp;
+    BOOL use_gzread = NO;
+#endif /* USE_ZLIB */
 
     /*
     **  Reduce the filename to a basic form (hopefully unique!).
@@ -1436,11 +1441,11 @@ PUBLIC int HTLoadFile ARGS4(
     if (strcmp("ftp", access) == 0 ||
        (strcmp("localhost", nodename) != 0 &&
 #ifdef VMS
-        strcasecomp(nodename, HTHostName()) != 0))
+        strcasecomp(nodename, HTHostName()) != 0
 #else
-        strcmp(nodename, HTHostName()) != 0))
+        strcmp(nodename, HTHostName()) != 0
 #endif /* VMS */
-    {
+    )) {
         FREE(newname);
 	FREE(filename);
 	FREE(nodename);
@@ -1540,7 +1545,6 @@ PUBLIC int HTLoadFile ARGS4(
 	    int len;
 	    char *cp = NULL;
 	    char *semicolon = NULL;
-            int status;
 
 	    if (HTEditable(vmsname)) {
 		HTAtom * put = HTAtom_for("PUT");
@@ -1588,7 +1592,20 @@ PUBLIC int HTLoadFile ARGS4(
 						 UCLYhndl_HTFile_for_unspec);
 			StrAllocCopy(anchor->content_type, format->name);
 			StrAllocCopy(anchor->content_encoding, "x-gzip");
+#ifdef USE_ZLIB
+			if (strcmp(format_out->name, "www/download") != 0) {
+			    fclose(fp);
+			    gzfp = gzopen(localname, "rb");
+
+			    if (TRACE)
+				fprintf(stderr,
+				       "HTLoadFile: gzopen of `%s' gives %p\n",
+					localname, (void*)gzfp);
+			    use_gzread = YES;
+			}
+#else  /* USE_ZLIB */
 			format = HTAtom_for("www/compressed");
+#endif  /* USE_ZLIB */
 		    }
 		}
 	    }
@@ -1596,8 +1613,43 @@ PUBLIC int HTLoadFile ARGS4(
 	        *semicolon = ';';
 	    FREE(filename);
 	    FREE(nodename);
-	    status = HTParseFile(format, format_out, anchor, fp, sink);
-	    fclose(fp);
+#ifdef USE_ZLIB
+	    if (use_gzread) {
+		if (gzfp) {
+		    char * sugfname = NULL;
+		    if (anchor->SugFname) {
+			StrAllocCopy(sugfname, anchor->SugFname);
+		    } else {
+			char * anchor_path = HTParse(anchor->address, "",
+						     PARSE_PATH + PARSE_PUNCTUATION);
+			char * lastslash;
+			HTUnEscape(anchor_path);
+			lastslash = strrchr(anchor_path, '/');
+			if (lastslash)
+			    StrAllocCopy(sugfname, lastslash + 1);
+			FREE(anchor_path);
+		    }
+		    FREE(anchor->content_encoding);
+		    if (sugfname && *sugfname)
+			HTCheckFnameForCompression(&sugfname, anchor,
+						   TRUE);
+		    if (sugfname && *sugfname)
+			StrAllocCopy(anchor->SugFname, sugfname);
+		    FREE(sugfname);
+		    status = HTParseGzFile(format, format_out,
+					   anchor,
+					   gzfp, sink);
+		} else {
+		    status = HTLoadError(NULL,
+					 -(HT_ERROR),
+					 "Could not open file for decompression!");
+		}
+	    } else
+#endif /* USE_GZREAD */
+	    {
+		status = HTParseFile(format, format_out, anchor, fp, sink);
+		fclose(fp);
+	    }
 	    return status;
         }  /* If successfull open */
 	FREE(filename);
@@ -1635,7 +1687,7 @@ PUBLIC int HTLoadFile ARGS4(
 	    STRUCT_DIRENT * dirbuf;
 	    float best = NO_VALUE_FOUND;	/* So far best is bad */
 	    HTFormat best_rep = NULL;	/* Set when rep found */
-	    STRUCT_DIRENT best_dirbuf;	/* Best dir entry so far */
+	    char * best_name = NULL;	/* Best dir entry so far */
 
 	    char *base = strrchr(localname, '/');
 	    int baselen;
@@ -1665,10 +1717,55 @@ forget_multi:
 #endif
 		if ((int)strlen(dirbuf->d_name) > baselen &&     /* Match? */
 		    !strncmp(dirbuf->d_name, base, baselen)) {	
-		    HTFormat rep = HTFileFormat(dirbuf->d_name, &encoding);
+		    HTFormat rep = HTFileFormat(dirbuf->d_name, NULL);
+		    float filevalue = HTFileValue(dirbuf->d_name);
 		    float value = HTStackValue(rep, format_out,
-		    				HTFileValue(dirbuf->d_name),
-						0.0  /* @@@@@@ */);
+		    				filevalue,
+						0L  /* @@@@@@ */);
+		    if (value <= 0.0) {
+			char * cp = NULL;
+			int len = strlen(dirbuf->d_name);
+    			if (len > 2 &&
+			    dirbuf->d_name[len - 1] == 'Z' &&
+			    dirbuf->d_name[len - 2] == '.') {
+			    StrAllocCopy(cp, dirbuf->d_name);
+			    cp[len - 2] = '\0';
+			    format = HTFileFormat(cp, NULL);
+			    FREE(cp);
+			    value = HTStackValue(format, format_out,
+						 filevalue, 0);
+			    if (value <= 0.0) {
+				format = HTAtom_for("application/x-compressed");
+				value = HTStackValue(format, format_out,
+						     filevalue, 0);
+			    }
+			    if (value <= 0.0) {
+				format = HTAtom_for("www/compressed");
+				value = HTStackValue(format, format_out,
+						     filevalue, 0);
+			    }
+			} else if ((len > 3) &&
+				   !strcasecomp((char *)&dirbuf->d_name[len - 2],
+						"gz") &&
+				   dirbuf->d_name[len - 3] == '.') {
+			    StrAllocCopy(cp, dirbuf->d_name);
+			    cp[len - 3] = '\0';
+			    format = HTFileFormat(cp, NULL);
+			    FREE(cp);
+			    value = HTStackValue(format, format_out,
+						 filevalue, 0);
+			    if (value <= 0.0) {
+				format = HTAtom_for("application/x-gzip");
+				value = HTStackValue(format, format_out,
+						     filevalue, 0);
+			    }
+			    if (value <= 0.0) {
+				format = HTAtom_for("www/compressed");
+				value = HTStackValue(format, format_out,
+						     filevalue, 0);
+			    }
+			}
+		    }
 		    if (value != NO_VALUE_FOUND) {
 		        if (TRACE)
 			    fprintf(stderr,
@@ -1677,7 +1774,7 @@ forget_multi:
 			if  (value > best) {
 			    best_rep = rep;
 			    best = value;
-			    best_dirbuf = *dirbuf;
+			    StrAllocCopy(best_name, dirbuf->d_name);
 		       }
 		    }	/* if best so far */ 		    
 		 } /* if match */  
@@ -1689,8 +1786,9 @@ forget_multi:
 		format = best_rep;
 		base[-1] = '/';		/* Restore directory name */
 		base[0] = '\0';
-		StrAllocCat(localname, best_dirbuf.d_name);
-		goto open_file;
+		StrAllocCat(localname, best_name);
+                FREE(best_name);
+                /* goto open_file; */  /* Nope - might be a directory - kw */
 		
 	    } else { 			/* If not found suitable file */
 		FREE(localname);
@@ -1711,10 +1809,11 @@ forget_multi:
 	**  to point to the current directory being read.
 	*/
 #ifdef _WINDOWS
-	if (!exists(localname)) {
+	if (!exists(localname))
 #else
-	if (stat(localname,&dir_info) == -1) {     /* get file information */
+	if (stat(localname,&dir_info) == -1)	   /* get file information */
 #endif
+	{
 	                               /* if can't read file information */
 	    if (TRACE)
 	        fprintf(stderr, "HTLoadFile: can't stat %s\n", localname);
@@ -1966,6 +2065,11 @@ forget_multi:
 				          "Subirectories:" : "Files:");
 				       END(HTML_EM);
 				    }
+				    END(HTML_H2);
+#ifndef LONG_LIST
+				    START(HTML_DIR);
+#endif /* !LONG_LIST */
+				}
 #else
 				if (state != *(char *)(HTBTree_object(
 							 next_element))) {
@@ -1981,12 +2085,12 @@ forget_multi:
 				    PUTS(state == 'D' ?
 				    "Subdirectories:" : "Files:");
 				    END(HTML_EM);
-#endif /* DIRED_SUPPORT */
 				    END(HTML_H2);
 #ifndef LONG_LIST
 				    START(HTML_DIR);
 #endif /* !LONG_LIST */
 				}
+#endif /* DIRED_SUPPORT */
 #ifndef LONG_LIST
 			        START(HTML_LI);
 #endif /* !LONG_LIST */
@@ -2053,7 +2157,6 @@ open_file:
 	    if (fp) {		/* Good! */
 	        int len;
 		char *cp = NULL;
-		int status;
 
 		if (HTEditable(localname)) {
 		    HTAtom * put = HTAtom_for("PUT");
@@ -2089,13 +2192,61 @@ open_file:
 						 UCLYhndl_HTFile_for_unspec);
 			StrAllocCopy(anchor->content_type, format->name);
 			StrAllocCopy(anchor->content_encoding, "x-gzip");
+#ifdef USE_ZLIB
+			if (strcmp(format_out->name, "www/download") != 0) {
+			    fclose(fp);
+			    gzfp = gzopen(localname, "rb");
+
+			    if (TRACE)
+				fprintf(stderr,
+				       "HTLoadFile: gzopen of `%s' gives %p\n",
+					localname, (void*)gzfp);
+			    use_gzread = YES;
+			}
+#else  /* USE_ZLIB */
 			format = HTAtom_for("www/compressed");
+#endif  /* USE_ZLIB */
 		    }
 		}
 		FREE(localname);
 		FREE(nodename);
-		status = HTParseFile(format, format_out, anchor, fp, sink);
-		fclose(fp);
+#ifdef USE_ZLIB
+		if (use_gzread) {
+		    if (gzfp) {
+			char * sugfname = NULL;
+			if (anchor->SugFname) {
+			    StrAllocCopy(sugfname, anchor->SugFname);
+			} else {
+			    char * anchor_path = HTParse(anchor->address, "",
+							 PARSE_PATH + PARSE_PUNCTUATION);
+			    char * lastslash;
+			    HTUnEscape(anchor_path);
+			    lastslash = strrchr(anchor_path, '/');
+			    if (lastslash)
+				StrAllocCopy(sugfname, lastslash + 1);
+			    FREE(anchor_path);
+			}
+			FREE(anchor->content_encoding);
+			if (sugfname && *sugfname)
+			    HTCheckFnameForCompression(&sugfname, anchor,
+						       TRUE);
+			if (sugfname && *sugfname)
+			    StrAllocCopy(anchor->SugFname, sugfname);
+			FREE(sugfname);
+			status = HTParseGzFile(format, format_out,
+					       anchor,
+					       gzfp, sink);
+		    } else {
+			status = HTLoadError(NULL,
+					     -(HT_ERROR),
+				     "Could not open file for decompression!");
+		    }
+		} else
+#endif /* USE_GZREAD */
+		{
+		    status = HTParseFile(format, format_out, anchor, fp, sink);
+		    fclose(fp);
+		}
 		return status;
 	    }  /* If succesfull open */
 	}    /* scope of fp */
diff --git a/WWW/Library/Implementation/HTFormat.c b/WWW/Library/Implementation/HTFormat.c
index c8bda5de..b5f8584d 100644
--- a/WWW/Library/Implementation/HTFormat.c
+++ b/WWW/Library/Implementation/HTFormat.c
@@ -661,6 +661,99 @@ PUBLIC int HTFileCopy ARGS2(
     return rv;
 }
 
+#ifdef USE_ZLIB
+/*	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.
+**
+**
+*/
+PRIVATE int HTGzFileCopy ARGS2(
+	gzFile,			gzfp,
+	HTStream*,		sink)
+{
+    HTStreamClass targetClass;    
+    char line[256];
+    int status, bytes = 0, nreads = 0, nprogr = 0;
+    int gzerrnum;
+    int rv = HT_OK;
+    
+    /*  Push the data down the stream
+    */
+    targetClass = *(sink->isa);	/* Copy pointers to procedures */
+
+    /*	read and inflate gzipped file, and push binary down sink
+    */
+    for (;;) {
+	status = gzread(gzfp, input_buffer, INPUT_BUFFER_SIZE);
+	nreads++;
+	if (status <= 0) { /* EOF or error */
+	    if (status == 0) {
+		rv = HT_LOADED;
+	        break;
+	    }
+	    if (TRACE) {
+	        fprintf(stderr,
+			"HTGzFileCopy: Read error, gzread returns %d\n",
+			status);
+	        fprintf(stderr,
+			"gzerror   : %s\n",
+			gzerror(gzfp, &gzerrnum));
+		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;
+	if (nreads >= 100) {
+	    /*
+	    **  Show progress messages for local files, and check for
+	    **  user interruption.  Start doing so only after a certain
+	    **  number of reads have been done, and don't update it on
+	    **  every read (normally reading in a local file should be
+	    **  speedy). - KW
+	    */
+	    if (nprogr == 0) {
+		if (bytes < 1024000) {
+		    sprintf(line,
+			    "Read %d uncompressed bytes of data.", bytes);
+		} else {
+		    sprintf(line, "Read %d uncompressed KB of data. %s",
+				  bytes/1024,
+		    "(Press 'z' to abort.)");
+		}
+		HTProgress(line);
+		if (HTCheckForInterrupt()) {
+		    _HTProgress ("Data transfer interrupted.");
+		    if (bytes) {
+			rv = HT_INTERRUPTED;
+		    } else {
+			rv = -1;
+		    }
+		    break;
+		}
+		nprogr++;
+	    } else if (nprogr == 25) {
+		nprogr = 0;
+	    } else {
+		nprogr++;
+	    }
+	}
+    } /* next bufferload */
+
+    return rv;
+}
+#endif /* USE_ZLIB */
+
 /*	Push data from a socket down a stream STRIPPING CR
 **	--------------------------------------------------
 **
@@ -807,6 +900,78 @@ PUBLIC int HTParseFile ARGS5(
 	return HT_LOADED;
 }
 
+#ifdef USE_ZLIB
+PRIVATE int HTCloseGzFile ARGS1(
+	gzFile,			gzfp)
+{
+    int gzres;
+    if (gzfp == NULL)
+	return 0;
+    gzres = gzclose(gzfp);
+    if (TRACE) {
+	if (gzres == Z_ERRNO) {
+	    perror("gzclose   ");
+	} else if (gzres != Z_OK) {
+	    fprintf(stderr, "gzclose   : error number %d\n", gzres);
+	}
+    }
+    return(gzres);
+}
+    
+PUBLIC int HTParseGzFile ARGS5(
+	HTFormat,		rep_in,
+	HTFormat,		format_out,
+	HTParentAnchor *,	anchor,
+	gzFile,			gzfp,
+	HTStream*,		sink)
+{
+    HTStream * stream;
+    HTStreamClass targetClass;    
+    int rv;
+
+    stream = HTStreamStack(rep_in,
+			format_out,
+	 		sink , anchor);
+    
+    if (!stream) {
+        char buffer[1024];	/* @@@@@@@@ */
+	extern char LYCancelDownload;
+	HTCloseGzFile(gzfp);
+        if (LYCancelDownload) {
+	    LYCancelDownload = FALSE;
+	    return -1;
+	}
+	sprintf(buffer, "Sorry, can't convert from %s to %s.",
+		HTAtom_name(rep_in), HTAtom_name(format_out));
+	if (TRACE)
+	    fprintf(stderr, "HTFormat(in HTParseGzFile): %s\n", buffer);
+        return HTLoadError(sink, 501, buffer);
+    }
+    
+    /*  Push the data down the stream
+    **
+    **  @@  Bug:  This decision ought to be made based on "encoding"
+    **  rather than on content-type.  @@@  When we handle encoding.
+    **  The current method smells anyway.
+    */
+    targetClass = *(stream->isa);	/* Copy pointers to procedures */
+    rv = HTGzFileCopy(gzfp, stream);
+    if (rv == -1 || rv == HT_INTERRUPTED) {
+        (*targetClass._abort)(stream, NULL);
+    } else {
+	(*targetClass._free)(stream);
+    }
+
+    HTCloseGzFile(gzfp);
+    if (rv == -1)
+	return HT_NO_DATA;
+    else if (rv == HT_INTERRUPTED || (rv > 0 && rv != HT_LOADED))
+	return HT_PARTIAL_CONTENT;
+    else
+	return HT_LOADED;
+}
+#endif /* USE_ZLIB */
+
 /*	Converter stream: Network Telnet to internal character text
 **	-----------------------------------------------------------
 **
diff --git a/WWW/Library/Implementation/HTFormat.h b/WWW/Library/Implementation/HTFormat.h
index 24972f06..812e2fc3 100644
--- a/WWW/Library/Implementation/HTFormat.h
+++ b/WWW/Library/Implementation/HTFormat.h
@@ -359,6 +359,26 @@ extern int HTParseFile PARAMS((
         FILE            *fp,
         HTStream*       sink));
 
+#ifdef USE_ZLIB
+
+#ifdef USE_ZLIB
+#include <zlib.h>
+#endif /* USE_ZLIB */
+/*
+HTParseGzFile: Parse a gzipped File through a file pointer
+
+   This routine is called by protocols modules to load an object. uses HTStreamStack and
+   HTGzFileCopy .  Returns HT_LOADED if succesful, <0 if not.
+ */
+extern int HTParseGzFile PARAMS((
+        HTFormat        format_in,
+        HTFormat        format_out,
+        HTParentAnchor  *anchor,
+        gzFile          gzfp,
+        HTStream*       sink));
+
+#endif /* USE_ZLIB */
+
 /*
 
 HTNetToText: Convert Net ASCII to local representation
diff --git a/WWW/Library/Implementation/HTMIME.c b/WWW/Library/Implementation/HTMIME.c
index 2b9e31e4..60645dee 100644
--- a/WWW/Library/Implementation/HTMIME.c
+++ b/WWW/Library/Implementation/HTMIME.c
@@ -972,7 +972,7 @@ PRIVATE void HTMIME_put_character ARGS2(
 	} /* switch on character */
 	break;
 
-    case miS:				/* Check for 'a; or 'e' */
+    case miS:				/* Check for 'a' or 'e' */
         switch (c) {
 	case 'a':
 	case 'A':
@@ -1565,25 +1565,24 @@ PRIVATE void HTMIME_put_character ARGS2(
 		*/
 		StrAllocCopy(me->anchor->content_disposition, me->value);
 		/*
-		**  If it includes file; filename=name.suffix
-		**  load the me->SugFname element. - FM
+		**  It's not clear yet from existing RFCs and IDs
+		**  whether we should be looking for file;, attachment;,
+		**  and/or inline; before the filename=value, so we'll
+		**  just search for "filename" followed by '=' and just
+		**  hope we get the intended value.  It is purely a
+		**  suggested name, anyway. - FM
 		*/
 		cp = me->anchor->content_disposition;
-		while (*cp != '\0' && strncasecomp(cp, "file;", 5))
-		    cp++;
-		if (*cp == '\0')
-		    break;
-		cp += 5;
-		while (*cp != '\0' && WHITE(*cp))
+		while (*cp != '\0' && strncasecomp(cp, "filename", 8))
 		    cp++;
 		if (*cp == '\0')
 		    break;
-		while (*cp != '\0' && strncasecomp(cp, "filename", 8))
+		cp += 8;
+		while ((*cp != '\0') && (WHITE(*cp) || *cp == '='))
 		    cp++;
 		if (*cp == '\0')
 		    break;
-		cp += 8;
-		while (*cp == ' ' || *cp == '=')
+		while (*cp != '\0' && WHITE(*cp)) 
 		    cp++;
 		if (*cp == '\0')
 		    break;
diff --git a/WWW/Library/Implementation/HTML.h b/WWW/Library/Implementation/HTML.h
index 2aaa8c0f..7480b678 100644
--- a/WWW/Library/Implementation/HTML.h
+++ b/WWW/Library/Implementation/HTML.h
@@ -88,8 +88,8 @@ struct _HTStructured {
      *  Used for nested lists. - FM
      */
     int		List_Nesting_Level;	/* counter for list nesting level */
-    int 	OL_Counter[7];		/* counter for ordered lists */
-    char 	OL_Type[7];		/* types for ordered lists */
+    int 	OL_Counter[12];		/* counter for ordered lists */
+    char 	OL_Type[12];		/* types for ordered lists */
     int 	Last_OL_Count;		/* last count in ordered lists */
     char 	Last_OL_Type;		/* last type in ordered lists */
 
diff --git a/WWW/Library/Implementation/HTNews.c b/WWW/Library/Implementation/HTNews.c
index 21da80d8..bcf2993e 100644
--- a/WWW/Library/Implementation/HTNews.c
+++ b/WWW/Library/Implementation/HTNews.c
@@ -78,6 +78,7 @@ PRIVATE HTStructured * target;			/* The output sink */
 PRIVATE HTStructuredClass targetClass;		/* Copy of fn addresses */
 PRIVATE HTParentAnchor *node_anchor;		/* Its anchor */
 PRIVATE int	diagnostic;			/* level: 0=none 2=source */
+PRIVATE HTList *NNTP_AuthInfo = NULL;		/* AUTHINFO database */
 
 #define PUTC(c) (*targetClass.put_character)(target, c)
 #define PUTS(s) (*targetClass.put_string)(target, s)
@@ -86,13 +87,42 @@ PRIVATE int	diagnostic;			/* level: 0=none 2=source */
 #define MAYBE_END(e) if (HTML_dtd.tags[e].contents != SGML_EMPTY) \
                         (*targetClass.end_element)(target, e, 0)
 
+typedef struct _NNTPAuth {
+   char * host;
+   char * user;
+   char * pass;
+} NNTPAuth;
+
 PRIVATE void free_news_globals NOARGS
 {
+    if (s >= 0) {
+	NEWS_NETCLOSE(s);
+	s = -1;
+    }
     FREE(HTNewsHost);
     FREE(NewsHost);
     FREE(NewsHREF);
 }
 
+PRIVATE void free_NNTP_AuthInfo NOARGS
+{
+    HTList *cur = NNTP_AuthInfo;
+    NNTPAuth *auth = NULL;
+
+    if (!cur)
+        return;
+
+    while (NULL != (auth = (NNTPAuth *)HTList_nextObject(cur))) {
+        FREE(auth->host);
+	FREE(auth->user);
+	FREE(auth->pass);
+	FREE(auth);
+    }
+    HTList_delete(NNTP_AuthInfo);
+    NNTP_AuthInfo = NULL;
+    return;
+}
+
 PUBLIC CONST char * HTGetNewsHost NOARGS
 {
 	return HTNewsHost;
@@ -260,6 +290,239 @@ PRIVATE BOOL match ARGS2 (CONST char *,unknown, CONST char *,template)
     return (BOOL)(*t == 0);		/* OK if end of template */
 }
 
+typedef enum {
+    NNTPAUTH_ERROR =	  0,	/* general failure */
+    NNTPAUTH_OK =	281,	/* authenticated successfully */
+    NNTPAUTH_CLOSE =	502	/* server probably closed connection */
+} NNTPAuthResult;
+/*
+**  This function handles nntp authentication. - FM
+*/
+PRIVATE NNTPAuthResult HTHandleAuthInfo ARGS1(
+	char *,		host)
+{
+    HTList *cur = NULL;
+    NNTPAuth *auth = NULL;
+    char *UserName = NULL;
+    char *PassWord = NULL;
+    char *msg = NULL;
+    char buffer[512];
+    int status, tries;
+    extern BOOL dump_output_immediately;
+
+    /*
+    **  Make sure we have an interactive user and a host. - FM
+    */
+    if (dump_output_immediately || !(host && *host))
+        return NNTPAUTH_ERROR;
+
+    /*
+    **  Check for an existing authorization entry. - FM
+    */
+    if (NNTP_AuthInfo != NULL) {
+        cur = NNTP_AuthInfo;
+	while (NULL != (auth = (NNTPAuth *)HTList_nextObject(cur))) {
+	    if (!strcmp(auth->host, host)) {
+		UserName = auth->user;
+		PassWord = auth->pass;
+		break;
+	    }
+	}
+    } else {
+	NNTP_AuthInfo = HTList_new();
+	atexit(free_NNTP_AuthInfo);
+    }
+
+    /*
+    **  Handle the username. - FM
+    */
+    buffer[511] = '\0';
+    tries = 3;
+
+    while (tries) {
+	if (UserName == NULL) {
+	    if ((msg = (char *)calloc(1, (strlen(host) + 30))) == NULL) {
+		outofmem(__FILE__, "HTHandleAuthInfo");
+	    }
+	    sprintf(msg, "Username for news host '%s':", host);
+	    UserName = HTPrompt(msg, NULL);
+	    FREE(msg);
+	    if (!(UserName && *UserName)) {
+		FREE(UserName);
+		return NNTPAUTH_ERROR;
+	    }
+	}
+	sprintf(buffer, "AUTHINFO USER %.*s%c%c", 495, UserName, CR, LF);
+	if ((status = response(buffer)) < 0) {
+	    if (status == HT_INTERRUPTED)
+		_HTProgress("Connection interrupted.");
+	    else
+		HTAlert("Connection closed ???");
+	    if (auth) {
+		if (auth->user != UserName) {
+		    FREE(auth->user);
+		    auth->user = UserName;
+		}
+	    } else {
+		FREE(UserName);
+	    }
+	    return NNTPAUTH_CLOSE;
+	}
+	if (status == 281) {
+	    /*
+	    **  Username is OK and no Password is required. - FM
+	    */
+	    if (auth) {
+		if (auth->user != UserName) {
+		    FREE(auth->user);
+		    auth->user = UserName;
+		}
+	    } else {
+		if ((auth =
+		    (NNTPAuth *)calloc(1, sizeof(NNTPAuth))) != NULL) {
+		    StrAllocCopy(auth->host, host);
+		    auth->user = UserName;
+		    HTList_appendObject(NNTP_AuthInfo, auth);
+		}
+	    }
+	    return NNTPAUTH_OK;
+	}
+	if (status != 381) {
+	    /*
+	    **  Not a request for the password, so it must be an error. - FM
+	    */
+	    HTAlert(response_text);
+	    tries--;
+	    if ((tries > 0) && HTConfirm("Change username?")) {
+		if (!auth || auth->user != UserName) {
+		    FREE(UserName);
+		}
+		if ((UserName = HTPrompt("Username:", UserName)) != NULL &&
+		    *UserName) {
+		    continue;
+		}
+	    }
+	    if (auth) {
+		if (auth->user != UserName) {
+		    FREE(auth->user);
+		}
+		FREE(auth->pass);
+	    }
+	    FREE(UserName);
+	    return NNTPAUTH_ERROR;
+	}
+	break;
+    }
+
+    if (status == 381) {
+	/*
+	**  Username is OK, and a password is required. - FM
+	*/
+	tries = 3;
+	while (tries) {
+	    if (PassWord == NULL) {
+		if ((msg = (char *)calloc(1, (strlen(host) + 30))) == NULL) {
+		    outofmem(__FILE__, "HTHandleAuthInfo");
+		}
+		sprintf(msg, "Password for news host '%s':", host);
+		PassWord = HTPromptPassword(msg);
+		FREE(msg);
+		if (!(PassWord && *PassWord)) {
+		    FREE(PassWord);
+		    return NNTPAUTH_ERROR;
+		}
+	    }
+	    sprintf(buffer, "AUTHINFO PASS %.*s%c%c", 495, PassWord, CR, LF);
+	    if ((status = response(buffer)) < 0) {
+		if (status == HT_INTERRUPTED) {
+		    _HTProgress("Connection interrupted.");
+		} else {
+		    HTAlert("Connection closed ???");
+		}
+		if (auth) {
+		    if (auth->user != UserName) {
+			FREE(auth->user);
+			auth->user = UserName;
+		    }
+		    if (auth->pass != PassWord) {
+			FREE(auth->pass);
+			auth->pass = PassWord;
+		    }
+		} else {
+		    FREE(UserName);
+		    FREE(PassWord);
+		}
+		return NNTPAUTH_CLOSE;
+	    }
+	    if (status == 502) {
+		/*
+		 *  That's what INN's nnrpd returns.
+		 *  It closes the connection after this. - kw
+		 */
+		HTAlert(response_text);
+		if (auth) {
+		    if (auth->user == UserName)
+			UserName = NULL;
+		    FREE(auth->user);
+		    if (auth->pass == PassWord)
+			PassWord = NULL;
+		    FREE(auth->pass);
+		}
+		FREE(UserName);
+		FREE(PassWord);
+		return NNTPAUTH_CLOSE;
+	    }
+	    if (status == 281) {
+		/*
+		**  Password is OK. - FM
+		*/
+		if (auth) {
+		    if (auth->user != UserName) {
+			FREE(auth->user);
+			auth->user = UserName;
+		    }
+		    if (auth->pass != PassWord) {
+			FREE(auth->pass);
+			auth->pass = PassWord;
+		    }
+		} else {
+		    if ((auth =
+			(NNTPAuth *)calloc(1, sizeof(NNTPAuth))) != NULL) {
+			StrAllocCopy(auth->host, host);
+			auth->user = UserName;
+			auth->pass = PassWord;
+			HTList_appendObject(NNTP_AuthInfo, auth);
+		    }
+		}
+		return NNTPAUTH_OK;
+	    }
+	    /*
+	    **  Show the error message and see if we should try again. - FM
+	    */
+	    HTAlert(response_text);
+	    if (!auth || auth->pass != PassWord) {
+		FREE(PassWord);
+	    } else {
+		PassWord = NULL;
+	    }
+	    tries--;
+	    if ((tries > 0) && HTConfirm("Change password?")) {
+		continue;
+	    }
+	    if (auth) {
+		if (auth->user == UserName)
+		    UserName = NULL;
+		FREE(auth->user);
+		FREE(auth->pass);
+	    }
+	    FREE(UserName);
+	    break;
+	}
+    }
+
+    return NNTPAUTH_ERROR;
+}
+
 /*	Find Author's name in mail address
 **	----------------------------------
 **
@@ -1919,7 +2182,7 @@ PUBLIC int HTLoadNews ARGS4(
 	if (post_wanted || reply_wanted || spost_wanted || sreply_wanted) {
 	    strcpy(command, "POST");
 	} else if (list_wanted) {
-	    strcpy(command, "LIST NEWSGROUPS");
+	    sprintf(command, "XGTITLE %.*s", 249, p1);
 	} else if (group_wanted) {
 	    char * slash = strchr(p1, '/');
 	    strcpy(command, "GROUP ");
@@ -2170,10 +2433,10 @@ PUBLIC int HTLoadNews ARGS4(
         } else {
 	    /*
 	    **  Ensure reader mode, but don't bother checking the
-	    **  status for anything but HT_INERRUPTED, because if
-	    **  the reader mode command is not needed, the server
-	    **  probably return a 500, which is irrelevant at
-	    **  this point. - FM
+	    **  status for anything but HT_INTERRUPTED or a 480
+	    **  Authorization request, because if the reader mode
+	    **  command is not needed, the server probably returned
+	    **  a 500, which is irrelevant at this point. - FM
 	    */
 	    char buffer[20];
 
@@ -2182,6 +2445,22 @@ PUBLIC int HTLoadNews ARGS4(
 		_HTProgress("Connection interrupted.");
 		break;
 	    }
+	    if (status == 480) {
+		NNTPAuthResult auth_result = HTHandleAuthInfo(NewsHost);
+		if (auth_result == NNTPAUTH_CLOSE) {
+		    if (s != -1 && !(ProxyHost || ProxyHREF)) {
+			NEWS_NETCLOSE(s);
+			s = -1;
+		    }
+		}
+	        if (auth_result != NNTPAUTH_OK) {
+		    break;
+	        }
+		if ((status = response(buffer)) == HT_INTERRUPTED) {
+		    _HTProgress("Connection interrupted.");
+		    break;
+		}
+	    }
 	}
 
 Send_NNTP_command:
@@ -2196,12 +2475,41 @@ Send_NNTP_command:
 	        break;
 	    }
 	}
+	/*
+	 *  For some well known error responses which are expected
+	 *  to occur in normal use, break from the loop without retrying
+	 *  and without closing the connection.  It is unlikely that
+	 *  these are leftovers from a timed-out connection (but we do
+	 *  some checks to see whether the response rorresponds to the
+	 *  last command), or that they will give anything else when
+	 *  automatically retried.  - kw
+	 */
+	if (status == 411 && group_wanted &&
+	    !strncmp(command, "GROUP ", 6) &&
+	    !strncasecomp(response_text + 3, " No such group ", 15) &&
+	    !strcmp(response_text + 18, groupName)) {
+
+	    HTAlert(response_text);
+	    break;
+	} else if (status == 430 && !group_wanted && !list_wanted &&
+	    !strncmp(command, "ARTICLE <", 9) &&
+	    !strcasecomp(response_text + 3, " No such article")) {
+
+	    HTAlert(response_text);
+	    break;
+	}
 	if ((status/100) != 2 &&
-	    status != 340) {
-	    if (retries)
+	    status != 340 &&
+	    status != 480) {
+	    if (retries) {
+		if (list_wanted && !strncmp(command, "XGTITLE", 7)) {
+		    sprintf(command, "LIST NEWSGROUPS%c%c", CR, LF);
+		    goto Send_NNTP_command;
+		}
 	        HTAlert(response_text);
-	    else
+	    } else {
 	        _HTProgress(response_text);
+	    }
 	    NEWS_NETCLOSE(s);
 	    s = -1;
 	    /*
@@ -2215,7 +2523,36 @@ Send_NNTP_command:
 	/*
 	**  Post or load a group, article, etc
 	*/
-	if (post_wanted || reply_wanted || spost_wanted || sreply_wanted) {
+	if (status == 480) {
+	    NNTPAuthResult auth_result;
+	    /*
+	     *  Some servers return 480 for a failed XGTITLE. - FM
+	     */
+	    if (list_wanted && !strncmp(command, "XGTITLE", 7) &&
+	        strstr(response_text, "uthenticat") == NULL &&
+	        strstr(response_text, "uthor") == NULL) {
+		sprintf(command, "LIST NEWSGROUPS%c%c", CR, LF);
+		goto Send_NNTP_command;
+	    }
+	    /*
+	    **  Handle Authorization. - FM
+	    */
+	    if ((auth_result = HTHandleAuthInfo(NewsHost)) == NNTPAUTH_OK) {
+		goto Send_NNTP_command;
+	    } else if (auth_result == NNTPAUTH_CLOSE) {
+		if (s != -1 && !(ProxyHost || ProxyHREF)) {
+		    NEWS_NETCLOSE(s);
+		    s = -1;
+		}
+		if (retries < 1)
+		    continue;
+	    }
+	    status = HT_NOT_LOADED;
+	} else if (post_wanted || reply_wanted ||
+		   spost_wanted || sreply_wanted) {
+	    /*
+	    **  Handle posting of an article. - FM
+	    */
 	    if (status != 340) {
 		HTAlert("Cannot POST to this host.");
 		if (postfile) {
@@ -2232,9 +2569,15 @@ Send_NNTP_command:
 	    FREE(postfile);
 	    status = HT_NOT_LOADED;
 	} else if (list_wanted) {
+	    /*
+	    **  List available newsgroups. - FM
+	    */
 	    _HTProgress("Reading list of available newsgroups.");
 	    status = read_list(ListArg);
 	} else if (group_wanted) {
+	    /*
+	    **  List articles in a news group. - FM
+	    */
 	    if (last < 0) {
 	        /*
 		**  We got one article number rather than a range
@@ -2252,6 +2595,9 @@ Send_NNTP_command:
 	    _HTProgress("Reading list of articles in newsgroup.");
 	    status = read_group(groupName, first, last);
         } else {
+	    /*
+	    **  Get an article from a news group. - FM
+	    */
 	    _HTProgress("Reading news article.");
 	    status = read_article();
 	}
@@ -2260,8 +2606,13 @@ Send_NNTP_command:
 	    status = HT_LOADED;
 	}
 	if (!(post_wanted || reply_wanted ||
-	      spost_wanted || sreply_wanted))
-	    (*targetClass._free)(target);
+	      spost_wanted || sreply_wanted)) {
+	    if (status == HT_NOT_LOADED) {
+		(*targetClass._abort)(target, NULL);
+	    } else {
+		(*targetClass._free)(target);
+	    }
+	}
 	FREE(NewsHREF);
 	if (ProxyHREF) {
 	    StrAllocCopy(NewsHost, ProxyHost);
diff --git a/WWW/Library/Implementation/HTStyle.h b/WWW/Library/Implementation/HTStyle.h
index 837251aa..99f57e54 100644
--- a/WWW/Library/Implementation/HTStyle.h
+++ b/WWW/Library/Implementation/HTStyle.h
@@ -104,7 +104,7 @@ typedef struct _HTStyle {
 */
     HTFont              font;           /* Font id */
     HTCoord             fontSize;       /* The size of font, not independent */
-    HTColor             color;  /* text gray of current run */
+    HTColor             color;		/* text gray of current run */
     int                 superscript;    /* superscript (-sub) in points */
 
     HTAnchor            *anchor;        /* Anchor id if any, else zero */
diff --git a/WWW/Library/Implementation/HTTCP.c b/WWW/Library/Implementation/HTTCP.c
index 98d21800..06c9ff03 100644
--- a/WWW/Library/Implementation/HTTCP.c
+++ b/WWW/Library/Implementation/HTTCP.c
@@ -325,9 +325,15 @@ PUBLIC int HTParseInet ARGS2(
     struct hostent  *phost;	/* Pointer to host - See netdb.h */
 
     if (!str) {
-    	if (TRACE)
-	    fprintf(stderr, 
-		    "HTParseInet: Can't parse `NULL'.\n");
+    	if (TRACE) {
+	    fprintf(stderr, "HTParseInet: Can't parse `NULL'.\n");
+	}
+	return -1;
+    }
+    if (HTCheckForInterrupt()) {
+	if (TRACE) {
+	    fprintf (stderr, "HTParseInet: INTERRUPTED for '%s'.\n", str);
+	}
 	return -1;
     }
     StrAllocCopy(host, str);	/* Make a copy we can mutilate */
@@ -350,8 +356,11 @@ PUBLIC int HTParseInet ARGS2(
 	} else {
 #ifdef SUPPRESS		/* 1. crashes!?!.  2. Not recommended */
 	    struct servent * serv = getservbyname(port, (char*)0);
-	    if (serv) sin->sin_port = serv->s_port;
-	    else if (TRACE) fprintf(stderr, "TCP: Unknown service %s\n", port);
+	    if (serv) {
+		sin->sin_port = serv->s_port;
+	    } else if (TRACE) {
+		fprintf(stderr, "TCP: Unknown service %s\n", port);
+	    }
 #endif /* SUPPRESS */
 	}
     }
@@ -363,11 +372,11 @@ PUBLIC int HTParseInet ARGS2(
     */
     sin->sdn_nam.n_len = min(DN_MAXNAML, strlen(host));  /* <=6 in phase 4 */
     strncpy (sin->sdn_nam.n_name, host, sin->sdn_nam.n_len + 1);
-    if (TRACE)
+    if (TRACE) {
         fprintf(stderr,  
 		"DECnet: Parsed address as object number %d on host %.6s...\n",
 		sin->sdn_objnum, host);
-
+    }
 #else  /* parse Internet host */
 
     if (*host >= '0' && *host <= '9') {   /* Test for numeric node address: */
@@ -404,8 +413,10 @@ PUBLIC int HTParseInet ARGS2(
 	FREE(host);
     } else {		    /* Alphanumeric node name: */
 #ifdef MVS	/* Oustanding problem with crash in MVS gethostbyname */
-	if (TRACE)
-	    fprintf(stderr, "HTTCP: Calling gethostbyname(%s)\n", host);
+	if (TRACE) {
+	    fprintf(stderr,
+		    "HTParseInet: Calling gethostbyname(%s)\n", host);
+	}
 #endif /* MVS */
 
 #ifdef NSL_FORK
@@ -462,8 +473,10 @@ PUBLIC int HTParseInet ARGS2(
 		**  Abort if interrupt key pressed.
 		*/
 		if (HTCheckForInterrupt()) {
-		    if (TRACE)
-			fprintf (stderr, "*** INTERRUPTED gethostbyname.\n");
+		    if (TRACE) {
+			fprintf(stderr,
+				"HTParseInet: INTERRUPTED gethostbyname.\n");
+		    }
 		    kill(fpid , SIGKILL);
 		    FREE(host);
 		    close(pfd[0]);
@@ -475,26 +488,28 @@ PUBLIC int HTParseInet ARGS2(
 		*/
 		sleep(1);
 	    }
-	    if (waitret <= 0)
+	    if (waitret <= 0) {
 		waitret = waitpid(fpid, &cst1, WNOHANG);
+	    }
 	    if (TRACE) {
 		if (WIFEXITED(cst1)) {
 		    fprintf(stderr,
-			    "NSL_FORK: Child %d exited, status 0x%x.\n",
+		      "HTParseInet: NSL_FORK child %d exited, status 0x%x.\n",
 			    (int)waitret, cst1);
 		} else if (WIFSIGNALED(cst1)) {
 		    fprintf(stderr,
-			    "NSL_FORK: Child %d got signal, status 0x%x!\n",
+		  "HTParseInet: NSL_FORK child %d got signal, status 0x%x!\n",
 			    (int)waitret, cst1);
 #ifdef WCOREDUMP
 		    if (WCOREDUMP(cst1)) {
-		    fprintf(stderr,
-			    "NSL_FORK: Child %d dumped core!\n",(int)waitret);
+			fprintf(stderr,
+			      "HTParseInet: NSL_FORK child %d dumped core!\n",
+				(int)waitret);
 		    }
-#endif
+#endif /* WCOREDUMP */
 		} else if (WIFSTOPPED(cst1)) {
 		    fprintf(stderr,
-			    "NSL_FORK: Child %d is stopped, status 0x%x!\n",
+		  "HTParseInet: NSL_FORK child %d is stopped, status 0x%x!\n",
 			    (int)waitret, cst1);
 		}
 	    }
@@ -503,22 +518,28 @@ PUBLIC int HTParseInet ARGS2(
 	    */
 	    IOCTL(pfd[0], FIONREAD, &cstat);
 	    if (cstat < 4) {
-		if (TRACE)
+		if (TRACE) {
 		    fprintf(stderr, 
-			    "NSL_FORK: Child returns only %d bytes, \
-trying again without forking...\n", cstat);
+		       "HTParseInet: NSL_FORK child returns only %d bytes.\n",
+			    cstat);
+		    fprintf(stderr,
+		       "             Trying again without forking.\n");
+		}
 		phost = gethostbyname(host);	/* See netdb.h */
 		if (!phost) {
-		    if (TRACE)
+		    if (TRACE) {
 			fprintf(stderr, 
-				"HTParseInet: Can't find internet node name `%s'.\n",host);
+			 "HTParseInet: Can't find internet node name `%s'.\n",
+				host);
+		    }
 		    memset((void *)&sin->sin_addr, 0, sizeof(sin->sin_addr));
 		} else {
-		    memcpy((void *)&sin->sin_addr, phost->h_addr, phost->h_length);
+		    memcpy((void *)&sin->sin_addr,
+		    	   phost->h_addr, phost->h_length);
 		}
-#if 0		
+#ifdef NOTDEFINED
 	        cstat = read(pfd[0], (void *)&sin->sin_addr , 4);
-#endif
+#endif /* NOTDEFINED */
 	    } else {
 	        cstat = read(pfd[0], (void *)&sin->sin_addr , cstat);
 	    }
@@ -526,16 +547,20 @@ trying again without forking...\n", cstat);
 	    close(pfd[1]);
 	}
 	if (sin->sin_addr.s_addr == 0) {
-	    if (TRACE)
+	    if (TRACE) {
 	        fprintf(stderr, 
-		    "HTTPAccess: Can't find internet node name `%s'.\n",host);
-	      FREE(host);
-	      return -1;
+			"HTParseInet: Can't find internet node name `%s'.\n",
+			host);
+	    }
+	    FREE(host);
+	    return -1;
 	}
 	FREE(host);
 #ifdef MVS
-	if (TRACE)
-	    fprintf(stderr, "HTTCP: gethostbyname() returned %d\n", phost);
+	if (TRACE) {
+	    fprintf(stderr,
+		    "HTParseInet: gethostbyname() returned %d\n", phost);
+	}
 #endif /* MVS */
 
 #else /* Not NSL_FORK: */
@@ -551,13 +576,17 @@ trying again without forking...\n", cstat);
 #else
 	phost = gethostbyname(host);	/* See netdb.h */
 #ifdef MVS
-	if (TRACE)
-	    fprintf(stderr, "HTTCP: gethostbyname() returned %d\n", phost);
+	if (TRACE) {
+	    fprintf(stderr,
+		    "HTParseInet: gethostbyname() returned %d\n", phost);
+	}
 #endif /* MVS */
 	if (!phost) {
-	    if (TRACE)
+	    if (TRACE) {
 	        fprintf(stderr, 
-		    "HTTPAccess: Can't find internet node name `%s'.\n",host);
+			"HTParseInet: Can't find internet node name `%s'.\n",
+			host);
+	    }
 	    FREE(host);
 	    return -1;  /* Fail? */
 	}
@@ -579,15 +608,15 @@ trying again without forking...\n", cstat);
 #endif /* NSL_FORK */
     }
 
-    if (TRACE)
+    if (TRACE) {
         fprintf(stderr,  
-		"TCP: Parsed address as port %d, IP address %d.%d.%d.%d\n",
+	   "HTParseInet: Parsed address as port %d, IP address %d.%d.%d.%d\n",
 		(int)ntohs(sin->sin_port),
 		(int)*((unsigned char *)(&sin->sin_addr)+0),
 		(int)*((unsigned char *)(&sin->sin_addr)+1),
 		(int)*((unsigned char *)(&sin->sin_addr)+2),
 		(int)*((unsigned char *)(&sin->sin_addr)+3));
-
+    }
 #endif  /* Internet vs. Decnet */
 
     return 0;	/* OK */
diff --git a/WWW/Library/Implementation/HTTP.c b/WWW/Library/Implementation/HTTP.c
index 0bcc2c62..f56f2daa 100644
--- a/WWW/Library/Implementation/HTTP.c
+++ b/WWW/Library/Implementation/HTTP.c
@@ -261,7 +261,8 @@ try_again:
           if (pres->rep_out == WWW_PRESENT) {
 	      if (pres->rep != WWW_SOURCE &&
 		  strcasecomp(HTAtom_name(pres->rep), "www/mime") &&
-		  strcasecomp(HTAtom_name(pres->rep), "www/compressed")) {
+		  strcasecomp(HTAtom_name(pres->rep), "www/compressed") &&
+		  pres->quality <= 1.0 && pres->quality >= 0.0) {
 		  if (pres->quality < 1.0) {
 		      if (pres->maxbytes > 0) {
 		          sprintf(temp, ";q=%4.3f;mxb=%ld",
@@ -459,7 +460,7 @@ try_again:
 	    } else {
 		if (TRACE)
 		    fprintf(stderr,
-		    	    "HTTP: Not sending authorization (yet)\n");
+			    "HTTP: Not sending authorization (yet).\n");
 	    }
 	    /*
 	    **  Add 'Cookie:' header, if it's HTTP or HTTPS
@@ -802,14 +803,16 @@ try_again:
 	error_file = fopen(http_error_file, "w");
 	if (error_file) {		/* Managed to open the file */
 	    fprintf(error_file, "error=%d\n", server_status);
+	    fclose(error_file);
+	}
 #else
 	error_file = fopen(http_error_file, "a");
 	if (error_file) {		/* Managed to open the file */
 	    fprintf(error_file, "   URL=%s (%s)\n", url, METHOD);
 	    fprintf(error_file, "STATUS=%s\n", line_buffer);
-#endif /* SERVER_STATUS_ONLY */
 	    fclose(error_file);
 	}
+#endif /* SERVER_STATUS_ONLY */
     }
 
     /*
@@ -944,11 +947,13 @@ try_again:
 	    **  Various forms of Redirection. - FM
 	    **  300 Multiple Choices.
 	    **  301 Moved Permanently.
-	    **  302 Moved Temporarily.
-	    **  303 See Other.
+	    **  302 General (temporary) Redirection (we can, and do, use GET).
+	    **  303 See Other (always use GET).
 	    **  304 Not Modified.
 	    **  305 Use Proxy.
-	    **  > 305 is unknown.
+	    **  306 Set Proxy.
+	    **  307 Temporary Redirect with method retained.
+	    **  > 308 is unknown.
 	    */
 	    if (no_url_redirection || do_head || keep_mime_headers) {
 	        /*
@@ -973,6 +978,20 @@ try_again:
 		 *  choices (i.e., we use the latter, for now). - FM
 		 */
 		HTAlert(line_buffer);
+		if (traversal) {
+		    HTTP_NETCLOSE(s, handle);
+		    status = -1;
+		    goto clean_up;
+		}
+		if (!dump_output_immediately &&
+		    format_out == HTAtom_for("www/download")) {
+		    /*
+		     *  Convert a download request to
+		     *  a presentation request for
+		     *  interactive users. - FM
+		     */
+		    format_out = WWW_PRESENT;
+		}
 		break;
 	    }
 
@@ -992,18 +1011,36 @@ try_again:
 		/*
 		 *  We don't want to compound proxying, so if we
 		 *  got this from a proxy, just show any message
-		 *  to the user. - FM
+		 *  to the user.  Otherwise, we look for a Location:
+		 *  header and use that if present.  We should also
+		 *  look for a Set-Proxy: header, but that's not yet
+		 *  implemented. - FM
 		 */
 		if (using_proxy) {
 		    HTAlert("Got redirection to a proxy from the proxy!");
 		    break;
 		}
-	    } else if (server_status > 305) {
+	    } else if (server_status == 306 || server_status > 307) {
 	        /*
-		 *  Show user the content, if any, for redirection
-		 *  statuses we don't know. - FM
+		 *  Show user the content, if any, for 306 until we
+		 *  implement Set-Proxy: header handling, and for
+		 *  redirection statuses we don't know. - FM
 		 */
 		HTAlert(line_buffer);
+		if (traversal) {
+		    HTTP_NETCLOSE(s, handle);
+		    status = -1;
+		    goto clean_up;
+		}
+		if (!dump_output_immediately &&
+		    format_out == HTAtom_for("www/download")) {
+		    /*
+		     *  Convert a download request to
+		     *  a presentation request for
+		     *  interactive users. - FM
+		     */
+		    format_out = WWW_PRESENT;
+		}
 		break;
 	    }
 
@@ -1015,14 +1052,15 @@ try_again:
 	     *  If that's another redirecting_url, we'll repeat the
 	     *  checks, and fetch initiations if acceptible, until
 	     *  we reach the actual URL, or the redirection limit
-	     *  set in HTAccess.c is exceeded.  If the status was 301,
+	     *  set in HTAccess.c is exceeded.  If the status was 301
 	     *  indicating that the relocation is permanent, we set
 	     *  the permanent_redirection flag to make it permanent
 	     *  for the current anchor tree (i.e., will persist until
 	     *  the tree is freed or the client exits).  If the
 	     *  redirection would include POST content, we seek
-	     *  confirmation from an interactive user, and otherwise
-	     *  refuse the redirection.  We also don't allow permanent
+	     *  confirmation from an interactive user, with option to
+	     *  use 303 for 301 (but not for 307), and otherwise refuse
+	     *  the redirection.  We also don't allow permanent
 	     *  redirection if we keep POST content.  If we don't find
 	     *  the Location header or it's value is zero-length, we
 	     *  display whatever the server returned, and the user
@@ -1034,7 +1072,10 @@ try_again:
 	      char *cp;
 
 	      if ((dump_output_immediately || traversal) &&
-	      	  do_post && server_status != 303) {
+	      	  do_post &&
+		  server_status != 303 &&
+		  server_status != 302 &&
+		  server_status != 301) {
 		  /*
 		   *  Don't redirect POST content without approval
 		   *  from an interactive user. - FM
@@ -1079,7 +1120,7 @@ try_again:
 		       */
 		      if (TRACE)
 		          fprintf(stderr,
-   "HTTP: Have POST content. Treating 301 (Permanent) as 302 (Temporary).\n");
+	 "HTTP: Have POST content. Treating 301 (Permanent) as Temporary.\n");
 		      HTAlert(
 	 "Have POST content. Treating Permanent Redirection as Temporary.\n");
 		  } else {
@@ -1347,12 +1388,14 @@ Cookie2_continuation:
 					"HTTP: Proxy URL is '%s'\n",
 					redirecting_url);
 			}
-			if (!do_post || server_status == 303) {
+			if (!do_post ||
+			    server_status == 303 ||
+			    server_status == 302) {
 			    /*
 			     *  We don't have POST content (nor support PUT
-			     *  or DELETE), or the status is "See Other" and
-			     *  we can convert to GET, so go back and check
-			     *  out the new URL. - FM
+			     *  or DELETE), or the status is "See Other"  or
+			     *  "General Redirection" and we can convert to
+			     *  GET, so go back and check out the new URL. - FM
 			     */
 		            status = HT_REDIRECTING;
 		            goto clean_up;
@@ -1361,7 +1404,8 @@ Cookie2_continuation:
 			 *  Make sure the user wants to redirect
 			 *  the POST content, or treat as GET - FM & DK
 			 */
-			switch (HTConfirmPostRedirect(redirecting_url)) {
+			switch (HTConfirmPostRedirect(redirecting_url,
+						      server_status)) {
 			    /*
 			     *  User failed to confirm.
 			     *  Abort the fetch.
@@ -1413,6 +1457,20 @@ Cookie2_continuation:
 	      length = strlen(start_of_data);
 	      HTAlert("Got redirection with no Location header.");
 	      HTProgress(line_buffer);
+	      if (traversal) {
+		  HTTP_NETCLOSE(s, handle);
+		  status = -1;
+		  goto clean_up;
+	      }
+	      if (!dump_output_immediately &&
+		  format_out == HTAtom_for("www/download")) {
+		  /*
+		   *  Convert a download request to
+		   *  a presentation request for
+		   *  interactive users. - FM
+		   */
+		  format_out = WWW_PRESENT;
+	      }
 	      break;
 	   }
 
@@ -1513,6 +1571,15 @@ Cookie2_continuation:
                     break;
 		} else if (!(traversal || dump_output_immediately) &&
 		           HTConfirm("Show the 407 message body?")) {
+		    if (!dump_output_immediately &&
+			format_out == HTAtom_for("www/download")) {
+			/*
+			 *  Convert a download request to
+			 *  a presentation request for
+			 *  interactive users. - FM
+			 */
+			format_out = WWW_PRESENT;
+		    }
 		    break;
                 } else {
 		    if (traversal || dump_output_immediately)
@@ -1562,6 +1629,15 @@ Cookie2_continuation:
 		    status = -1;
 		    goto clean_up;
 		}
+		if (!dump_output_immediately &&
+		    format_out == HTAtom_for("www/download")) {
+		    /*
+		     *  Convert a download request to
+		     *  a presentation request for
+		     *  interactive users. - FM
+		     */
+		    format_out = WWW_PRESENT;
+		}
                 break;
             } /* case 4 switch */
             break;
@@ -1580,6 +1656,20 @@ Cookie2_continuation:
 	    **  we always should display. - FM
 	    */
 	    HTAlert(line_buffer);
+	    if (traversal) {
+		HTTP_NETCLOSE(s, handle);
+		status = -1;
+		goto clean_up;
+	    }
+	    if (!dump_output_immediately &&
+		format_out == HTAtom_for("www/download")) {
+		/*
+		 *  Convert a download request to
+		 *  a presentation request for
+		 *  interactive users. - FM
+		 */
+		format_out = WWW_PRESENT;
+	    }
             break;
 
           default:
@@ -1595,6 +1685,15 @@ Cookie2_continuation:
 		status = -1;
 		goto clean_up;
 	    }
+	    if (!dump_output_immediately &&
+		format_out == HTAtom_for("www/download")) {
+		/*
+		 *  Convert a download request to
+		 *  a presentation request for
+		 *  interactive users. - FM
+		 */
+		format_out = WWW_PRESENT;
+	    }
             break;
         } /* Switch on server_status/100 */
 
diff --git a/WWW/Library/Implementation/HTUtils.h b/WWW/Library/Implementation/HTUtils.h
index ec013fc5..a0c93655 100644
--- a/WWW/Library/Implementation/HTUtils.h
+++ b/WWW/Library/Implementation/HTUtils.h
@@ -364,6 +364,8 @@ Sucess (>=0) and failure (<0) codes
 #define HT_NOT_LOADED -29999
 #define HT_OK           0               /* Generic success*/
 
+#define HT_ERROR                -1      /* Generic failure */
+
 #define HT_NO_ACCESS    -10             /* Access not available */
 #define HT_FORBIDDEN    -11             /* Access forbidden */
 #define HT_INTERNAL     -12             /* Weird -- should never happen. */
diff --git a/WWW/Library/djgpp/makefile b/WWW/Library/djgpp/makefile
index d3cc446e..cf37f9f1 100644
--- a/WWW/Library/djgpp/makefile
+++ b/WWW/Library/djgpp/makefile
@@ -7,7 +7,7 @@ WWW_MACH = djgpp
 # The ASIS repository's name for the machine we are on
 #ASIS_MACH = hardware/os
 
-CFLAGS = -O -DCOLOR_CURSES -DDOSPATH -DNOUSERS -DDEBUG -I../../../djgpp/tcplib/include -I../../../djgpp/tcplib/include/tcp
+CFLAGS = -O3 -DEXP_CHARTRANS -DCOLOR_CURSES -DDOSPATH -DNOUSERS -DDEBUG -I../../../djgpp/tcplib/include -I../../../djgpp/tcplib/include/tcp
 LFLAGS =
 CC = gcc