about summary refs log tree commit diff stats
path: root/WWW/Library/Implementation
diff options
context:
space:
mode:
Diffstat (limited to 'WWW/Library/Implementation')
-rw-r--r--WWW/Library/Implementation/HTAccess.c101
-rw-r--r--WWW/Library/Implementation/HTAccess.h3
-rw-r--r--WWW/Library/Implementation/HTMIME.c7
-rw-r--r--WWW/Library/Implementation/HTNews.c15
-rw-r--r--WWW/Library/Implementation/HTRules.c260
-rw-r--r--WWW/Library/Implementation/HTRules.h27
-rw-r--r--WWW/Library/Implementation/HTTCP.c146
-rw-r--r--WWW/Library/Implementation/HTTP.c1
-rw-r--r--WWW/Library/Implementation/HTUtils.h11
9 files changed, 517 insertions, 54 deletions
diff --git a/WWW/Library/Implementation/HTAccess.c b/WWW/Library/Implementation/HTAccess.c
index 008d42b7..11dfc5ae 100644
--- a/WWW/Library/Implementation/HTAccess.c
+++ b/WWW/Library/Implementation/HTAccess.c
@@ -72,6 +72,7 @@ extern HTCJKlang HTCJK;
 PUBLIC char * HTClientHost = NULL; /* Name of remote login host if any */
 PUBLIC FILE * HTlogfile = NULL;    /* File to which to output one-liners */
 PUBLIC BOOL HTSecure = NO;	   /* Disable access for telnet users? */
+PUBLIC BOOL HTPermitRedir = NO;	   /* Always allow redirection in getfile()? */
 
 PUBLIC BOOL using_proxy = NO; /* are we using a proxy gateway? */
 
@@ -315,6 +316,9 @@ PUBLIC BOOL override_proxy ARGS1(
 #ifndef DISABLE_FINGER
 	    else if (!strcmp(acc_method, "finger"))	port = 79;
 #endif
+	    else if (!strcmp(acc_method, "telnet"))	port = 23;
+	    else if (!strcmp(acc_method, "tn3270"))	port = 23;
+	    else if (!strcmp(acc_method, "rlogin"))	port = 513;
 	    FREE(acc_method);
 	}
     }
@@ -379,17 +383,34 @@ PRIVATE int get_physical ARGS2(
     char * acc_method = NULL;	/* Name of access method */
     char * physical = NULL;
     char * Server_addr = NULL;
+    BOOL override_flag = NO;
+
+    /*
+    **	Make sure the using_proxy variable is FALSE.
+    */
+    using_proxy = NO;
 
 #ifndef NO_RULES
     physical = HTTranslate(addr);
     if (!physical) {
+	if (redirecting_url) {
+	    return HT_REDIRECTING;
+	}
 	return HT_FORBIDDEN;
     }
     if (anchor->isISMAPScript == TRUE) {
 	StrAllocCat(physical, "?0,0");
 	CTRACE(tfp, "HTAccess: Appending '?0,0' coordinate pair.\n");
     }
-    HTAnchor_setPhysical(anchor, physical);
+    if (!strncmp(physical, "Proxied=", 8)) {
+	HTAnchor_setPhysical(anchor, physical + 8);
+	using_proxy = YES;
+    } else if (!strncmp(physical, "NoProxy=", 8)) {
+	HTAnchor_setPhysical(anchor, physical + 8);
+	override_flag = YES;
+    } else {
+	HTAnchor_setPhysical(anchor, physical);
+    }
     FREE(physical);			/* free our copy */
 #else
     if (anchor->isISMAPScript == TRUE) {
@@ -410,53 +431,56 @@ PRIVATE int get_physical ARGS2(
     **	Check whether gateway access has been set up for this.
     **
     **	This function can be replaced by the rule system above.
+    **
+    **  If the rule system has already determined that we should
+    **  use a proxy, or that we shouldn't, ignore proxy-related
+    **  settings, don't use no_proxy either.
     */
 #define USE_GATEWAYS
 #ifdef USE_GATEWAYS
-    /*
-    **	Make sure the using_proxy variable is FALSE.
-    */
-    using_proxy = NO;
 
-    if (!strcasecomp(acc_method, "news")) {
-	/*
-	**  News is different, so we need to check the name of the server,
-	**  as well as the default port for selective exclusions.
-	*/
-	char *host = NULL;
-	if ((host = HTParse(addr, "", PARSE_HOST))) {
-	    if (strchr(host, ':') == NULL) {
+    if (!override_flag && !using_proxy) {   /* else ignore no_proxy env var */
+	if (!strcasecomp(acc_method, "news")) {
+	    /*
+	    **  News is different, so we need to check the name of the server,
+	    **  as well as the default port for selective exclusions.
+	    */
+	    char *host = NULL;
+	    if ((host = HTParse(addr, "", PARSE_HOST))) {
+		if (strchr(host, ':') == NULL) {
+		    StrAllocCopy(Server_addr, "news://");
+		    StrAllocCat(Server_addr, host);
+		    StrAllocCat(Server_addr, ":119/");
+		}
+		FREE(host);
+	    } else if (getenv("NNTPSERVER") != NULL) {
 		StrAllocCopy(Server_addr, "news://");
-		StrAllocCat(Server_addr, host);
+		StrAllocCat(Server_addr, (char *)getenv("NNTPSERVER"));
 		StrAllocCat(Server_addr, ":119/");
 	    }
-	    FREE(host);
-	} else if (getenv("NNTPSERVER") != NULL) {
-	    StrAllocCopy(Server_addr, "news://");
-	    StrAllocCat(Server_addr, (char *)getenv("NNTPSERVER"));
-	    StrAllocCat(Server_addr, ":119/");
-	 }
-    } else if (!strcasecomp(acc_method, "wais")) {
-	/*
+	} else if (!strcasecomp(acc_method, "wais")) {
+	    /*
 	**  Wais also needs checking of the default port
 	**  for selective exclusions.
 	*/
-	char *host = NULL;
-	if ((host = HTParse(addr, "", PARSE_HOST))) {
-	    if (!(strchr(host, ':'))) {
-		StrAllocCopy(Server_addr, "wais://");
-		StrAllocCat(Server_addr, host);
-		StrAllocCat(Server_addr, ":210/");
+	    char *host = NULL;
+	    if ((host = HTParse(addr, "", PARSE_HOST))) {
+		if (!(strchr(host, ':'))) {
+		    StrAllocCopy(Server_addr, "wais://");
+		    StrAllocCat(Server_addr, host);
+		    StrAllocCat(Server_addr, ":210/");
+		}
+		FREE(host);
 	    }
-	    FREE(host);
-	}
-	else
+	    else
+		StrAllocCopy(Server_addr, addr);
+	} else {
 	    StrAllocCopy(Server_addr, addr);
-    } else {
-	StrAllocCopy(Server_addr, addr);
+	}
+	override_flag = override_proxy(Server_addr);
     }
 
-    if (!override_proxy(Server_addr)) {
+    if (!override_flag && !using_proxy) {
 	char * gateway_parameter, *gateway, *proxy;
 
 	/*
@@ -641,11 +665,17 @@ PRIVATE int HTLoad ARGS4(
     HTProtocol *p;
     int status = get_physical(addr, anchor);
     if (status == HT_FORBIDDEN) {
+	 /* prevent crash if telnet or similar was forbidden by rule. - kw */
+	LYFixCursesOn("show alert:");
 	return HTLoadError(sink, 500, gettext("Access forbidden by rule"));
+    } else if (status == HT_REDIRECTING) {
+	return status;	/* fake redirection by rule, to redirecting_url */
     }
     if (status < 0)
 	return status;	/* Can't resolve or forbidden */
 
+    /* prevent crash if telnet or similar mapped or proxied by rule. - kw */
+    LYFixCursesOnForAccess(addr, HTAnchor_physical(anchor));
     p = (HTProtocol *)HTAnchor_protocol(anchor);
     anchor->underway = TRUE;		/* Hack to deal with caching */
     status= (*(p->load))(HTAnchor_physical(anchor),
@@ -668,6 +698,8 @@ PUBLIC HTStream *HTSaveStream ARGS1(
     return (*p->saveStream)(anchor);
 }
 
+PUBLIC int redirection_attempts = 0; /* counter in HTLoadDocument */
+
 /*	Load a document - with logging etc		HTLoadDocument()
 **	----------------------------------
 **
@@ -697,7 +729,6 @@ PRIVATE BOOL HTLoadDocument ARGS4(
     CONST char * address_to_load = full_address;
     char *cp;
     BOOL ForcingNoCache = LYforce_no_cache;
-    static int redirection_attempts = 0;
 
     CTRACE (tfp, "HTAccess: loading document %s\n", address_to_load);
 
diff --git a/WWW/Library/Implementation/HTAccess.h b/WWW/Library/Implementation/HTAccess.h
index 2cfd49d5..0c763051 100644
--- a/WWW/Library/Implementation/HTAccess.h
+++ b/WWW/Library/Implementation/HTAccess.h
@@ -13,6 +13,8 @@
 
 extern char * use_this_url_instead;
 
+extern int redirection_attempts;
+
 /*      Definition uses:
 */
 #include <HTAnchor.h>
@@ -76,6 +78,7 @@ extern int HTDiag;                      /* Flag: load source as plain text */
 extern char * HTClientHost;             /* Name or number of telnetting host */
 extern FILE * HTlogfile;                /* File to output one-liners to */
 extern BOOL HTSecure;                   /* Disable security holes? */
+extern BOOL HTPermitRedir;              /* Special flag for getfile() */
 extern HTStream* HTOutputStream;        /* For non-interactive, set this */
 extern HTFormat HTOutputFormat;         /* To convert on load, set this */
 
diff --git a/WWW/Library/Implementation/HTMIME.c b/WWW/Library/Implementation/HTMIME.c
index 65bab4f9..24b42194 100644
--- a/WWW/Library/Implementation/HTMIME.c
+++ b/WWW/Library/Implementation/HTMIME.c
@@ -1558,6 +1558,13 @@ PRIVATE void HTMIME_put_character ARGS2(
 		if (!strcasecomp(me->value, "YES") ||
 		    !strcasecomp(me->value, "TRUE")) {
 		    me->anchor->safe = TRUE;
+		} else if (!strcasecomp(me->value, "NO") ||
+		    !strcasecomp(me->value, "FALSE")) {
+		    /*
+		    **  If server explicitly tells us that it has changed
+		    **  its mind, reset flag in anchor. - kw
+		    */
+		    me->anchor->safe = FALSE;
 		}
 		break;
 	    case miSERVER:
diff --git a/WWW/Library/Implementation/HTNews.c b/WWW/Library/Implementation/HTNews.c
index 5b16ee3c..32f4e3a6 100644
--- a/WWW/Library/Implementation/HTNews.c
+++ b/WWW/Library/Implementation/HTNews.c
@@ -771,8 +771,12 @@ PRIVATE void abort_socket NOARGS
 {
     CTRACE(tfp, "HTNews: EOF on read, closing socket %d\n", s);
     NEWS_NETCLOSE(s);	/* End of file, close socket */
-    PUTS("Network Error: connection lost");
-    PUTC('\n');
+    if (rawtext) {
+	RAW_PUTS("Network Error: connection lost\n");
+    } else {
+	PUTS("Network Error: connection lost");
+	PUTC('\n');
+    }
     s = -1;		/* End of file on response */
 }
 
@@ -1299,7 +1303,7 @@ PRIVATE int read_article ARGS1(
 
     if (rawtext) {
 	/*
-	 *  No tags - kw
+	 *  No tags, and never do a PUTC. - kw
 	 */
 	;
     } else if (diagnostic) {
@@ -1308,14 +1312,15 @@ PRIVATE int read_article ARGS1(
 	**  as XMP formatted text. - FM
 	*/
 	START(HTML_XMP);
+	PUTC('\n');
     } else {
 	/*
 	**  Read in the BODY of the Article
 	**  as PRE formatted text. - FM
 	*/
 	START(HTML_PRE);
+	PUTC('\n');
     }
-    PUTC('\n');
 
     p = line;
     while (!done) {
@@ -2783,7 +2788,7 @@ Send_NNTP_command:
 
 /*
 **  This function clears all authorization information by
-**  invoking the free_HTAAGlobals() function, which normally
+**  invoking the free_NNTP_AuthInfo() function, which normally
 **  is invoked at exit.  It allows a browser command to do
 **  this at any time, for example, if the user is leaving
 **  the terminal for a period of time, but does not want
diff --git a/WWW/Library/Implementation/HTRules.c b/WWW/Library/Implementation/HTRules.c
index 97df34e6..6140765e 100644
--- a/WWW/Library/Implementation/HTRules.c
+++ b/WWW/Library/Implementation/HTRules.c
@@ -15,6 +15,7 @@
 **			Bug Fix: in case of PASS, only one parameter to printf.
 **	19 Sep 93  AL	Added Access Authorization stuff.
 **	 1 Nov 93  AL	Added htbin.
+**	25 May 99  KW	Added redirect for lynx.
 **
 */
 
@@ -35,10 +36,17 @@ typedef struct _rule {
 	HTRuleOp	op;
 	char *		pattern;
 	char *		equiv;
+	char *		condition_op; /* as strings - may be inefficient, */
+	char *		condition;    /* but this is not for a server - kw */
 } rule;
 
 #ifndef NO_RULES
 
+#include <HTTP.h> /* for redirecting_url, indirectly HTPermitRedir - kw */
+#include <LYGlobalDefs.h> /* for LYUserSpecifiedURL - kw */
+#include <LYUtils.h>		/* for LYFixCursesOn - kw */
+#include <HTAlert.h>
+
 /*	Global variables
 **	----------------
 */
@@ -68,15 +76,17 @@ PRIVATE rule * rule_tail = 0;	/* Pointer to last on list */
 **	returns 	0 if success, -1 if error.
 */
 
-PUBLIC int HTAddRule ARGS3(
+PUBLIC int HTAddRule ARGS5(
     HTRuleOp,		op,
     CONST char *,	pattern,
-    CONST char *,	equiv)
+    CONST char *,	equiv,
+    CONST char *,	cond_op,
+    CONST char *,	cond)
 { /* BYTE_ADDRESSING removed and memory check - AS - 1 Sep 93 */
     rule *	temp;
     char *	pPattern;
 
-    temp = (rule *)malloc(sizeof(*temp));
+    temp = (rule *)calloc(1, sizeof(*temp));
     if (temp==NULL)
 	outofmem(__FILE__, "HTAddRule");
     pPattern = (char *)malloc(strlen(pattern)+1);
@@ -91,14 +101,23 @@ PUBLIC int HTAddRule ARGS3(
     } else {
 	temp->equiv = 0;
     }
+    if (cond_op) {
+	StrAllocCopy(temp->condition_op, cond_op);
+	StrAllocCopy(temp->condition, cond);
+    }
     temp->pattern = pPattern;
     temp->op = op;
 
     strcpy(pPattern, pattern);
     if (equiv) {
-	CTRACE(tfp, "Rule: For `%s' op %d `%s'\n", pattern, op, equiv);
+	CTRACE(tfp, "Rule: For `%s' op %d `%s'", pattern, op, equiv);
     } else {
-	CTRACE(tfp, "Rule: For `%s' op %d\n", pattern, op);
+	CTRACE(tfp, "Rule: For `%s' op %d", pattern, op);
+    }
+    if (cond_op) {
+	CTRACE(tfp, "\t%s %s\n", cond_op, cond ? cond : "<null>");
+    } else {
+	CTRACE(tfp, "\n");
     }
 
     if (!rules) {
@@ -137,6 +156,8 @@ void HTClearRules NOARGS
 	rules = temp->next;
 	FREE(temp->pattern);
 	FREE(temp->equiv);
+	FREE(temp->condition_op);
+	FREE(temp->condition);
 	FREE(temp);
     }
 #ifndef PUT_ON_HEAD
@@ -144,7 +165,32 @@ void HTClearRules NOARGS
 #endif
 }
 
-
+PRIVATE BOOL rule_cond_ok ARGS1(
+    rule *,	 r)
+{
+    BOOL result;
+    if (!r->condition_op)
+	return YES;
+    if (strcmp(r->condition_op, "if") && strcmp(r->condition_op, "unless")) {
+	CTRACE(tfp, "....... rule ignored, unrecognized `%s'!\n",
+	       r->condition_op);
+	return NO;
+    }
+    if (!strcmp(r->condition, "redirected"))
+	result = (redirection_attempts > 0);
+    else if (!strcmp(r->condition, "userspec"))
+	result = LYUserSpecifiedURL;
+    else {
+	CTRACE(tfp, "....... rule ignored, unrecognized `%s %s'!\n",
+	       r->condition_op, r->condition ? r->condition : "<null>");
+	return NO;
+    }
+    if (!strcmp(r->condition_op, "if"))
+	return result;
+    else
+	return (!result);
+	
+}
 /*	Translate by rules					HTTranslate()
 **	------------------
 **
@@ -169,6 +215,9 @@ char * HTTranslate ARGS1(
 {
     rule * r;
     char *current = NULL;
+    char *msgtmp = NULL, *pMsg;
+    int proxy_none_flag = 0;
+    int permitredir_flag = 0;
     StrAllocCopy(current, required);
 
     HTAA_clearProtections();	/* Reset from previous call -- AL */
@@ -188,6 +237,9 @@ char * HTTranslate ARGS1(
 	} else				/* Not wildcard */
 	    if (*p != *q) continue;	/* plain mismatch: go to next rule */
 
+	if (!rule_cond_ok(r))	/* check condition, next rule if false - kw */
+	    continue;
+
 	switch (r->op) {		/* Perform operation */
 
 #ifdef ACCESS_AUTH
@@ -225,14 +277,53 @@ char * HTTranslate ARGS1(
 	    break;
 #endif /* ACCESS_AUTH */
 
+	case HT_UserMsg:		/* Produce message immediately */
+	    LYFixCursesOn("show rule message:");
+	    HTUserMsg2((r->equiv ? r->equiv : "Rule: %s"), current);
+	    break;
+	case HT_InfoMsg:		/* Produce messages immediately */
+	case HT_Progress:
+	case HT_Alert:
+	    LYFixCursesOn("show rule message:"); /* and fall through */
+	case HT_AlwaysAlert:
+	    pMsg = r->equiv ? r->equiv :
+		(r->op==HT_AlwaysAlert) ? "%s" : "Rule: %s";
+	    if (strchr(pMsg, '%')) {
+		HTSprintf0(&msgtmp, pMsg, current);
+		pMsg = msgtmp;
+	    }
+	    switch (r->op) {		/* Actually produce message */
+	    case HT_InfoMsg:	HTInfoMsg(pMsg);	break;
+	    case HT_Progress:	HTProgress(pMsg);	break;
+	    case HT_Alert:	HTAlert(pMsg);		break;
+	    case HT_AlwaysAlert: HTAlwaysAlert("Rule alert:", pMsg);	break;
+	    default: break;
+	    }
+	    FREE(msgtmp);
+	    break;
+
+	case HT_PermitRedir:			/* Set special flag */
+		    permitredir_flag = 1;
+		    CTRACE(tfp, "HTRule: Mark for redirection permitted\n");
+		    break;
+
 	case HT_Pass:				/* Authorised */
 		if (!r->equiv) {
+		    if (proxy_none_flag) {
+			char * temp = NULL;
+			StrAllocCopy(temp, "NoProxy=");
+			StrAllocCat(temp, current);
+			FREE(current);
+			current = temp;
+		    }
 		    CTRACE(tfp, "HTRule: Pass `%s'\n", current);
 		    return current;
 		}
 		/* Else fall through ...to map and pass */
 
 	case HT_Map:
+	case HT_Redirect:
+	case HT_RedirectPerm:
 	    if (*p == *q) { /* End of both strings, no wildcard */
 		  CTRACE(tfp, "For `%s' using `%s'\n", current, r->equiv);
 		  StrAllocCopy(current, r->equiv); /* use entire translation */
@@ -264,9 +355,49 @@ char * HTTranslate ARGS1(
 		    } /* If no insertion point exists */
 		}
 		if (r->op == HT_Pass) {
+		    if (proxy_none_flag) {
+			char * temp = NULL;
+			StrAllocCopy(temp, "NoProxy=");
+			StrAllocCat(temp, current);
+			FREE(current);
+			current = temp;
+		    }
 		    CTRACE(tfp, "HTRule: ...and pass `%s'\n",
 				current);
 		    return current;
+		} else if (r->op == HT_Redirect) {
+		    CTRACE(tfp, "HTRule: ...and redirect to `%s'\n",
+				current);
+		    redirecting_url = current;
+		    HTPermitRedir = (permitredir_flag == 1);
+		    return (char *)0;
+		} else if (r->op == HT_RedirectPerm) {
+		    CTRACE(tfp, "HTRule: ...and redirect like 301 to `%s'\n",
+				current);
+		    redirecting_url = current;
+		    permanent_redirection = TRUE;
+		    HTPermitRedir = (permitredir_flag == 1);
+		    return (char *)0;
+		}
+		break;
+
+	case HT_UseProxy:
+		if (r->equiv && 0==strcasecomp(r->equiv, "none")) {
+		    CTRACE(tfp, "For `%s' will not use proxy\n", current);
+		    proxy_none_flag = 1;
+		} else if (proxy_none_flag) {
+		    CTRACE(tfp, "For `%s' proxy server ignored: %s\n",
+			   current,
+			   r->equiv ? r->equiv : "<null>");
+		} else {
+		    char * temp = NULL;
+		    StrAllocCopy(temp, "Proxied=");
+		    StrAllocCat(temp, r->equiv);
+		    StrAllocCat(temp, current);
+		    CTRACE(tfp, "HTRule: proxy server found: %s\n",
+			   r->equiv ? r->equiv : "<null>");
+		    FREE(current);
+		    return temp;
 		}
 		break;
 
@@ -280,6 +411,12 @@ char * HTTranslate ARGS1(
 
     } /* loop over rules */
 
+    if (proxy_none_flag) {
+	char * temp = NULL;
+	StrAllocCopy(temp, "NoProxy=");
+	StrAllocCat(temp, current);
+	return temp;
+    }
 
     return current;
 }
@@ -298,6 +435,7 @@ PUBLIC int  HTSetConfiguration ARGS1(
     char * line = NULL;
     char * pointer = line;
     char *word1, *word2, *word3;
+    char *cond_op=NULL, *cond=NULL;
     float quality, secs, secs_per_byte;
     int maxbytes;
     int status;
@@ -357,13 +495,121 @@ PUBLIC int  HTSetConfiguration ARGS1(
 	op =	0==strcasecomp(word1, "map")  ? HT_Map
 	    :	0==strcasecomp(word1, "pass") ? HT_Pass
 	    :	0==strcasecomp(word1, "fail") ? HT_Fail
+	    :	0==strcasecomp(word1, "redirect") ? HT_Redirect
+	    :	0==strncasecomp(word1, "redirectperm", 12) ? HT_RedirectPerm
+	    :	0==strcasecomp(word1, "redirecttemp") ? HT_Redirect
+	    :	0==strcasecomp(word1, "permitredirection") ? HT_PermitRedir
+	    :	0==strcasecomp(word1, "useproxy") ? HT_UseProxy
+	    :	0==strcasecomp(word1, "alert") ? HT_Alert
+	    :	0==strcasecomp(word1, "alwaysalert") ? HT_AlwaysAlert
+	    :	0==strcasecomp(word1, "progress") ? HT_Progress
+	    :	0==strcasecomp(word1, "usermsg") ? HT_UserMsg
+	    :	0==strcasecomp(word1, "infomsg") ? HT_InfoMsg
 	    :	0==strcasecomp(word1, "defprot") ? HT_DefProt
 	    :	0==strcasecomp(word1, "protect") ? HT_Protect
 	    :						HT_Invalid;
 	if (op==HT_Invalid) {
 	    fprintf(stderr, "HTRule: %s '%s'\n", RULE_INCORRECT, config);
 	} else {
-	    HTAddRule(op, word2, word3);
+	    switch (op) {
+	    case HT_Fail:	/* never a or other 2nd parameter */
+	    case HT_PermitRedir:
+		cond_op = word3;
+		if (cond_op && *cond_op) {
+		    word3 = NULL;
+		    cond = HTNextField(&pointer);
+		}
+		break;
+
+	    case HT_Pass:	/* possibly a URL2 */
+		if (word3 && (!strcasecomp(word3, "if") ||
+			      !strcasecomp(word3, "unless"))) {
+		    cond_op = word3;
+		    word3 = NULL;
+		    cond = HTNextField(&pointer);
+		    break;
+		} /* else fall through */
+
+	    case HT_Map:	/* always a URL2 (or other 2nd parameter) */
+	    case HT_Redirect:
+	    case HT_RedirectPerm:
+	    case HT_UseProxy:
+		cond_op = HTNextField(&pointer);
+		/* check for extra status word in "Redirect" */
+		if (op==HT_Redirect && 0==strcasecomp(word1, "redirect") &&
+		    cond_op &&
+		    strcasecomp(cond_op, "if") &&
+		    strcasecomp(cond_op, "unless")) {
+		    if (0==strcmp(word2, "301") ||
+			0==strcasecomp(word2, "permanent")) {
+			op = HT_RedirectPerm;
+		    } else if (!(0==strcmp(word2, "302") ||
+				 0==strcmp(word2, "303") ||
+				 0==strcasecomp(word2, "temp") ||
+				 0==strcasecomp(word2, "seeother"))) {
+			CTRACE(tfp, "Rule: Ignoring `%s' in Redirect\n", word2);
+		    }
+		    word2 = word3;
+		    word3 = cond_op; /* cond_op isn't condition op after all */
+		    cond_op = HTNextField(&pointer);
+		}
+		if (cond_op && *cond_op)
+		    cond = HTNextField(&pointer);
+		break;
+
+	    case HT_Progress:
+	    case HT_InfoMsg:
+	    case HT_UserMsg:
+	    case HT_Alert:
+	    case HT_AlwaysAlert:
+		cond_op = HTNextField(&pointer);
+		if (cond_op && *cond_op)
+		    cond = HTNextField(&pointer);
+		if (word3) {	/* Fix string with too may %s - kw */
+		    char *cp = word3, *cp1, *cp2;
+		    while ((cp1=strchr(cp, '%'))) {
+			if (cp1[1] == '\0') {
+			    *cp1 = '\0';
+			    break;
+			} else if (cp1[1] == '%') {
+			    cp = cp1 + 2;
+			    continue;
+			} else while ((cp2=strchr(cp1+2, '%'))) {
+			    if (cp2[1] == '\0') {
+				*cp2 = '\0';
+				break;
+			    } else if (cp2[1] == '%') {
+				cp1 = cp2;
+			    } else {
+				*cp2 = '?'; /* replace bad % */
+				cp1 = cp2;
+			    }
+			}
+			break;
+		    }
+		}
+		break;
+
+		default:
+		break;
+	    }
+	    if (cond_op && cond && *cond && !strcasecomp(cond_op, "unless")) {
+		cond_op = "unless";
+	    } else if (cond_op && cond && *cond &&
+		       !strcasecomp(cond_op, "if")) {
+		cond_op = "if";
+	    } else if (cond_op || cond) {
+		fprintf(stderr, "HTRule: %s '%s'\n", RULE_INCORRECT, config);
+		FREE(line);	/* syntax error, condition is a mess - kw */
+		return -2;	/* NB unrecognized cond passes here - kw */
+	    }
+	    if (cond && !strncasecomp(cond, "redirected", strlen(cond))) {
+		cond = "redirected"; /* recognized, canonical case - kw */
+	    } else if (cond && strlen(cond) >= 8 &&
+		!strncasecomp(cond, "userspecified", strlen(cond))) {
+		cond = "userspec"; /* also allow abbreviation - kw */
+	    }
+	    HTAddRule(op, word2, word3, cond_op, cond);
 	}
     }
     FREE(line);
diff --git a/WWW/Library/Implementation/HTRules.h b/WWW/Library/Implementation/HTRules.h
index fce782f8..0974bbae 100644
--- a/WWW/Library/Implementation/HTRules.h
+++ b/WWW/Library/Implementation/HTRules.h
@@ -28,7 +28,16 @@ typedef enum _HTRuleOp {
         HT_Pass,
         HT_Fail,
         HT_DefProt,
-        HT_Protect
+        HT_Protect,
+        HT_Progress,
+        HT_InfoMsg,
+        HT_UserMsg,
+        HT_Alert,
+        HT_AlwaysAlert,
+        HT_Redirect,
+        HT_RedirectPerm,
+        HT_PermitRedir,
+        HT_UseProxy
 } HTRuleOp;
 
 #ifndef NO_RULES
@@ -57,7 +66,14 @@ HTAddRule:  Add rule to the list
   pattern                points to 0-terminated string containing a single "*"
 
   equiv                  points to the equivalent string with * for the place where the
-                         text matched by * goes.
+                         text matched by * goes; or to other 2nd parameter
+                         meaning depends on op).			 
+
+  cond_op,               additional condition for applying rule; cond_op should
+  cond                   be either NULL (no additional condition), or one of
+                         the strings "if" or "unless"; if cond_op is not NULL,
+                         cond should point to a recognized condition keyword
+                         (as a string) such as "userspec", "redirected".
 
   ON EXIT,
 
@@ -68,7 +84,12 @@ HTAddRule:  Add rule to the list
    large.
 
  */
-extern int HTAddRule PARAMS((HTRuleOp op, CONST char * pattern, CONST char * equiv));
+extern int HTAddRule PARAMS((
+    HTRuleOp op,
+    CONST char * pattern,
+    CONST char * equiv,
+    CONST char * cond_op,
+    CONST char * cond));
 
 
 /*
diff --git a/WWW/Library/Implementation/HTTCP.c b/WWW/Library/Implementation/HTTCP.c
index 1a6b2621..fdc6b66a 100644
--- a/WWW/Library/Implementation/HTTCP.c
+++ b/WWW/Library/Implementation/HTTCP.c
@@ -645,7 +645,7 @@ PUBLIC struct hostent * LYGetHostByName ARGS1(
     struct _statuses {
 	size_t rehostentlen;
 	int h_length;
-	int child_errno;  /* maybe not very useful */
+	int child_errno;  /* sometimes useful to pass this on */
 	int child_h_errno;
 	BOOL h_errno_valid;
     } statuses;
@@ -687,7 +687,7 @@ PUBLIC struct hostent * LYGetHostByName ARGS1(
     CTRACE(tfp, "LYGetHostByName: Calling gethostbyname(%s)\n", host);
 #endif /* MVS */
 
-    CTRACE_FLUSH(tfp);  /* so child messages will not mess parent log */
+    CTRACE_FLUSH(tfp);  /* so child messages will not mess up parent log */
 
     lynx_nsl_status = HT_INTERNAL;	/* should be set to something else below */
 
@@ -699,6 +699,10 @@ PUBLIC struct hostent * LYGetHostByName ARGS1(
 	*/
     {
 	int got_rehostent = 0;
+#if HAVE_SIGACTION
+	sigset_t old_sigset;
+	sigset_t new_sigset;
+#endif
 	/*
 	**	Pipe, child pid, status buffers, start time, select()
 	**	control variables.
@@ -727,6 +731,32 @@ PUBLIC struct hostent * LYGetHostByName ARGS1(
 
 	pipe(pfd);
 
+#if HAVE_SIGACTION
+	/*
+	 *  Attempt to prevent a rare situation where the child
+	 *  could execute the Lynx signal handlers because it gets
+	 *  killed before it even has a chance to reset its handlers,
+	 *  resulting in bogus 'Exiting via interrupt' message and
+	 *  screen corruption or worse.
+	 *  Should that continue to be reported, for systems without
+	 *  sigprocmask(), we need to find a different solutions for
+	 *  those. - kw 19990430
+	 */
+	sigemptyset(&new_sigset);
+	sigaddset(&new_sigset, SIGTERM);
+	sigaddset(&new_sigset, SIGINT);
+#ifndef NOSIGHUP
+	sigaddset(&new_sigset, SIGHUP);
+#endif /* NOSIGHUP */
+#ifdef SIGTSTP
+	sigaddset(&new_sigset, SIGTSTP);
+#endif /* SIGTSTP */
+#ifdef SIGWINCH
+	sigaddset(&new_sigset, SIGWINCH);
+#endif /* SIGWINCH */
+	sigprocmask(SIG_BLOCK, &new_sigset, &old_sigset);
+#endif /* HAVE_SIGACTION */
+
 	if ((fpid = fork()) == 0 ) {
 	    struct hostent  *phost;	/* Pointer to host - See netdb.h */
 	    /*
@@ -764,13 +794,26 @@ PUBLIC struct hostent * LYGetHostByName ARGS1(
 	    signal(SIGSEGV, SIG_DFL);
 	    signal(SIGILL, SIG_DFL);
 
+#if HAVE_SIGACTION
+	    /* Restore signal mask to whatever it was before the fork. -kw */
+	    sigprocmask(SIG_SETMASK, &old_sigset, NULL);
+#endif /* HAVE_SIGACTION */
+
 	    /*
 	    **  Child won't use read side.  -BL
 	    */
 	    close(pfd[0]);
+#ifdef HAVE_H_ERRNO
+	    /* to detect cases when it doesn't get set although it should */
+	    h_errno = -2;
+#endif
+	    errno = 0;
 	    phost = gethostbyname(host);
+	    statuses.child_errno = errno;
 	    statuses.child_h_errno = h_errno;
+#ifdef HAVE_H_ERRNO
 	    statuses.h_errno_valid = YES;
+#endif
 #ifdef MVS
 	    CTRACE(tfp, "LYGetHostByName: gethostbyname() returned %d\n", phost);
 #endif /* MVS */
@@ -790,12 +833,17 @@ PUBLIC struct hostent * LYGetHostByName ARGS1(
 		statuses.h_length = 0;
 	    } else {
 		statuses.h_length = ((struct hostent *)rehostent)->h_length;
+#ifdef HAVE_H_ERRNO
+		if (h_errno == -2) /* success, but h_errno unchanged? */
+		    statuses.h_errno_valid = NO;
+#endif
 	    }
 	    /*
 	    **  Send variables indicating status of lookup to parent.
 	    **  That includes rehostentlen, which the parent will use
 	    **  as the size for the second read (if > 0).
 	    */
+	    if (!statuses.child_errno)
 	    statuses.child_errno = errno;
 	    statuses.rehostentlen = rehostentlen;
 	    write(pfd[1], &statuses, sizeof(statuses));
@@ -815,6 +863,14 @@ PUBLIC struct hostent * LYGetHostByName ARGS1(
 	    }
 	}
 
+#if HAVE_SIGACTION
+	/*
+	**  (parent) Restore signal mask to whatever it was
+	**  before the fork. - kw
+	*/
+	sigprocmask(SIG_SETMASK, &old_sigset, NULL);
+#endif /* HAVE_SIGACTION */
+
 	/*
 	**	(parent) Wait until lookup finishes, or interrupt,
 	**	or cycled too many times (just in case) -BL
@@ -871,8 +927,44 @@ PUBLIC struct hostent * LYGetHostByName ARGS1(
 		if (readret == sizeof(statuses)) {
 		    h_errno = statuses.child_h_errno;
 		    errno = statuses.child_errno;
-		    if (statuses.h_errno_valid)
+#ifdef HAVE_H_ERRNO
+		    if (statuses.h_errno_valid) {
 			lynx_nsl_status = HT_H_ERRNO_VALID;
+			/*
+			 *  If something went wrong in the child process
+			 *  other than normal lookup errors, and it appears
+			 *  that we have enough info to know what went wrong,
+			 *  generate diagnostic output.
+			 *  ENOMEM observed on linux in processes constrained
+			 *  with ulimit.  It would be too unkind to abort
+			 *  the session, access to local files or through a
+			 *  proxy may still work. - kw
+			 */
+			if (
+#ifdef NETDB_INTERNAL		/* linux glibc: defined in netdb.h */
+			    (errno && h_errno == NETDB_INTERNAL) ||
+#endif
+			    (errno == ENOMEM &&
+			     statuses.rehostentlen == 0 &&
+			     /* should probably be NETDB_INTERNAL if child
+				memory exhausted, but we may find that
+				h_errno remains unchanged. - kw */
+			     h_errno == -2)) {
+#ifndef MULTINET
+			    HTInetStatus("CHILD gethostbyname");
+#endif
+			    HTAlert(LYStrerror(statuses.child_errno));
+			    if (errno == ENOMEM) {
+				/*
+				 *  Not much point in continuing, right?
+				 *  Fake a 'z', should shorten pointless
+				 *  guessing cycle. - kw
+				 */
+				LYFakeZap(YES);
+			    }
+			}
+		    }
+#endif /* HAVE_H_ERRNO */
 		    if (statuses.rehostentlen > sizeof(struct hostent)) {
 			/*
 			**  Then get the full reorganized hostent.  -BL, kw
@@ -1311,6 +1403,11 @@ PUBLIC CONST char * HTHostName NOARGS
     return hostname;
 }
 
+#ifndef MULTINET		/* SOCKET_ERRNO != errno ? */
+#if !defined(UCX) || !defined(VAXC) /* errno not modifiable ? */
+#define SOCKET_DEBUG_TRACE    /* show errno status after some system calls */
+#endif  /* UCX && VAXC */
+#endif /* MULTINET */
 /*
 **  Interruptable connect as implemented for Mosaic by Marc Andreesen
 **  and hacked in for Lynx years ago by Lou Montulli, and further
@@ -1449,6 +1546,13 @@ PUBLIC int HTDoConnect ARGS4(
 	int ret;
 	int tries=0;
 
+#ifdef SOCKET_DEBUG_TRACE
+	{
+	    int saved_errno = SOCKET_ERRNO;
+	    HTInetStatus("this socket's first connect");
+	    errno = saved_errno;	/* I don't trust HTInetStatus */
+	}
+#endif /* SOCKET_DEBUG_TRACE */
 	ret = 0;
 	while (ret <= 0) {
 	    fd_set writefds;
@@ -1477,6 +1581,13 @@ PUBLIC int HTDoConnect ARGS4(
 #endif /* SOCKS */
 	    ret = select(FD_SETSIZE, NULL, (void *)&writefds, NULL, &timeout);
 
+#ifdef SOCKET_DEBUG_TRACE
+	    if (tries == 1) {
+		int saved_errno = SOCKET_ERRNO;
+		HTInetStatus("this socket's first select");
+		errno = saved_errno;	/* I don't trust HTInetStatus */
+	    }
+#endif /* SOCKET_DEBUG_TRACE */
 	   /*
 	   **  If we suspend, then it is possible that select will be
 	   **  interrupted.  Allow for this possibility. - JED
@@ -1484,6 +1595,13 @@ PUBLIC int HTDoConnect ARGS4(
 	   if ((ret == -1) && (errno == EINTR))
 	     continue;
 
+#ifdef SOCKET_DEBUG_TRACE
+	   if (ret < 0) {
+	       int saved_errno = SOCKET_ERRNO;
+	       HTInetStatus("failed select");
+	       errno = saved_errno;	/* I don't trust HTInetStatus */
+	   }
+#endif /* SOCKET_DEBUG_TRACE */
 	    /*
 	    **	Again according to the Sun and Motorola man pages for connect:
 	    **	   EALREADY	       The socket is non-blocking and a  previ-
@@ -1527,8 +1645,16 @@ PUBLIC int HTDoConnect ARGS4(
 
 		if (status && (SOCKET_ERRNO == EALREADY)) /* new stuff LJM */
 		    ret = 0; /* keep going */
-		else
+		else {
+#ifdef SOCKET_DEBUG_TRACE
+		    if (status < 0) {
+			int saved_errno = SOCKET_ERRNO;
+			HTInetStatus("confirm-ready connect");
+			errno = saved_errno;
+		    }
+#endif /* SOCKET_DEBUG_TRACE */
 		    break;
+		}
 #ifdef SOCKS
 		}
 #endif /* SOCKS */
@@ -1559,6 +1685,11 @@ PUBLIC int HTDoConnect ARGS4(
 		    (SOCKET_ERRNO != 18242) &&
 #endif /* UCX */
 		    (SOCKET_ERRNO != EISCONN)) {
+#ifdef SOCKET_DEBUG_TRACE
+		    int saved_errno = SOCKET_ERRNO;
+		    HTInetStatus("confirm-not-ready connect");
+		    errno = saved_errno;
+#endif /* SOCKET_DEBUG_TRACE */
 		    break;
 		}
 	    }
@@ -1570,6 +1701,13 @@ PUBLIC int HTDoConnect ARGS4(
 	    }
 	}
     }
+#ifdef SOCKET_DEBUG_TRACE
+    else if (status < 0) {
+	    int saved_errno = SOCKET_ERRNO;
+	    HTInetStatus("this socket's first and only connect");
+	    errno = saved_errno;	/* I don't trust HTInetStatus */
+    }
+#endif /* SOCKET_DEBUG_TRACE */
 #endif /* !DJGPP */
     if (status < 0) {
 	/*
diff --git a/WWW/Library/Implementation/HTTP.c b/WWW/Library/Implementation/HTTP.c
index 96067a51..05b3e89c 100644
--- a/WWW/Library/Implementation/HTTP.c
+++ b/WWW/Library/Implementation/HTTP.c
@@ -918,6 +918,7 @@ try_again:
 		 */
 		HTAlert(line_buffer);
 		HTTP_NETCLOSE(s, handle);
+		HTNoDataOK = 1;
 		status = HT_NO_DATA;
 		goto clean_up;
 
diff --git a/WWW/Library/Implementation/HTUtils.h b/WWW/Library/Implementation/HTUtils.h
index dd5be2dd..dd0a51e7 100644
--- a/WWW/Library/Implementation/HTUtils.h
+++ b/WWW/Library/Implementation/HTUtils.h
@@ -265,6 +265,17 @@ Macros for declarations
 
 /*
 
+OFTEN USED INTEGER MACROS
+
+  Min and Max functions
+  
+ */
+#ifndef HTMIN
+#define HTMIN(a,b) ((a) <= (b) ? (a) : (b))
+#define HTMAX(a,b) ((a) >= (b) ? (a) : (b))
+#endif
+/*
+
 Booleans
 
  */