diff options
| author | Fumitoshi UKAI <ukai@debian.or.jp> | 2002-01-29 19:08:49 +0000 | 
|---|---|---|
| committer | Fumitoshi UKAI <ukai@debian.or.jp> | 2002-01-29 19:08:49 +0000 | 
| commit | d1ede3ab4f4f70beabdc0228b88854bd0438b388 (patch) | |
| tree | fad244ef2c4cb9f058d485e1f28ff6297b092635 | |
| parent | [w3m-dev 02927] mailto: POST method with w3mmail.cgi (diff) | |
| download | w3m-d1ede3ab4f4f70beabdc0228b88854bd0438b388.tar.gz w3m-d1ede3ab4f4f70beabdc0228b88854bd0438b388.zip | |
[w3m-dev 02928] RFC2617: HTTP Digest authentication
* NEWS: RFC2617: HTTP Digest authentication
* config.h.dist (USE_DIGEST_AUTH): added
* configure (use_digest_auth): added, enabled when openssl found
				(because it used openssl/md5.h)
* file.c (auth_param): added
* file.c (http_auth): added
* file.c (extract_auth_val): added
* file.c (qstr_unquote): added
* file.c (extract_auth_param): added
* file.c (get_auth_param): added
* file.c (AuthBasicCred): added
* file.c (digest_hex): added
* file.c (AuthDigestCred): added
* file.c (basic_auth_param): added
* file.c (digest_auth_param): added
* file.c (www_auth): added
* file.c (findAuthentication): added
* file.c (getAuthCookie): rewrite, use http_auth
* file.c (loadGeneralFile): HRequest hr
* file.c (loadGeneralFile): use findAuthentication, new getAuthCookie
* proto.h (HTTPrequestMethod): added
* proto.h (HTTPrequestURI): added
* proto.h (openURL): add `hr' arg
* url.c (HTTPrequestMethod): added
* url.c (HTTPrequestURI): added
* url.c (HTTPrequest): use HTTPrequestMethod and HTTPrequestURI
* url.c (openURL): add `hr' arg
From: Fumitoshi UKAI  <ukai@debian.or.jp>
Diffstat (limited to '')
| -rw-r--r-- | ChangeLog | 33 | ||||
| -rw-r--r-- | NEWS | 1 | ||||
| -rw-r--r-- | config.h.dist | 1 | ||||
| -rwxr-xr-x | configure | 6 | ||||
| -rw-r--r-- | file.c | 383 | ||||
| -rw-r--r-- | proto.h | 6 | ||||
| -rw-r--r-- | url.c | 82 | 
7 files changed, 437 insertions, 75 deletions
| @@ -1,3 +1,34 @@ +2002-01-30  Fumitoshi UKAI  <ukai@debian.or.jp> + +	* [w3m-dev 02928] RFC2617: HTTP Digest authentication +	* NEWS: RFC2617: HTTP Digest authentication +	* config.h.dist (USE_DIGEST_AUTH): added +	* configure (use_digest_auth): added, enabled when openssl found +				(because it used openssl/md5.h) +	* file.c (auth_param): added +	* file.c (http_auth): added +	* file.c (extract_auth_val): added +	* file.c (qstr_unquote): added +	* file.c (extract_auth_param): added +	* file.c (get_auth_param): added +	* file.c (AuthBasicCred): added +	* file.c (digest_hex): added +	* file.c (AuthDigestCred): added +	* file.c (basic_auth_param): added +	* file.c (digest_auth_param): added +	* file.c (www_auth): added +	* file.c (findAuthentication): added +	* file.c (getAuthCookie): rewrite, use http_auth +	* file.c (loadGeneralFile): HRequest hr +	* file.c (loadGeneralFile): use findAuthentication, new getAuthCookie +	* proto.h (HTTPrequestMethod): added +	* proto.h (HTTPrequestURI): added +	* proto.h (openURL): add `hr' arg +	* url.c (HTTPrequestMethod): added +	* url.c (HTTPrequestURI): added +	* url.c (HTTPrequest): use HTTPrequestMethod and HTTPrequestURI +	* url.c (openURL): add `hr' arg +  2002-01-30  SASAKI Takeshi <sasaki@ct.sakura.ne.jp>  	* [w3m-dev 02927] mailto: POST method with w3mmail.cgi @@ -2362,4 +2393,4 @@  	* release-0-2-1  	* import w3m-0.2.1 -$Id: ChangeLog,v 1.272 2002/01/29 17:16:35 ukai Exp $ +$Id: ChangeLog,v 1.273 2002/01/29 19:08:49 ukai Exp $ @@ -1,5 +1,6 @@  w3m 0.3? +* RFC2617: HTTP Digest authentication  * rc: default_url=0(empty) 1(current URL) 2(link URL)  * GOTO_RELATIVE (M-u)  * highlight for incremental search diff --git a/config.h.dist b/config.h.dist index 2104f07..8c88b6d 100644 --- a/config.h.dist +++ b/config.h.dist @@ -113,6 +113,7 @@ MODEL=Linux.i686-monster-ja  #undef USE_SYSMOUSE  #define USE_MENU  #define USE_COOKIE +#define USE_DIGEST_AUTH  #define USE_SSL  #undef USE_SSL_VERIFY  #undef FTPPASS_HOSTNAMEGEN @@ -1,5 +1,5 @@  #!/bin/sh -# $Id: configure,v 1.57 2002/01/29 16:23:37 ukai Exp $ +# $Id: configure,v 1.58 2002/01/29 19:08:49 ukai Exp $  #	Configuration.  # @@ -685,9 +685,12 @@ if [ "$use_ssl" = y ]; then  fi  if [ "$use_ssl" = y ]; then    ask_param "SSL verification support (SSL library >= version 0.8)" use_ssl_verify n +  ask_param "Digest Auth support" use_digest_auth y  else    use_ssl_verify=n    def_param "use_ssl_verify" n +  use_digest_auth=n +  def_param "use_digest_auth" n  fi  if [ "$use_color" = y ]; then @@ -2045,6 +2048,7 @@ $def_use_gpm  $def_use_sysmouse  $def_use_menu  $def_use_cookie +$def_use_digest_auth  $def_use_ssl  $def_use_ssl_verify  $def_ftppass_hostnamegen @@ -1,4 +1,4 @@ -/* $Id: file.c,v 1.48 2002/01/29 17:16:35 ukai Exp $ */ +/* $Id: file.c,v 1.49 2002/01/29 19:08:50 ukai Exp $ */  #include "fm.h"  #include <sys/types.h>  #include "myctype.h" @@ -841,36 +841,337 @@ checkContentType(Buffer *buf)      return r->ptr;  } +struct auth_param { +    char *name; +    Str val; +}; + +struct http_auth { +    int pri; +    char *scheme; +    struct auth_param *param; +    Str (*cred) (struct http_auth * ha, Str uname, Str pw, ParsedURL *pu, +		 HRequest *hr); +}; + +#define TOKEN_PAT	"[^][()<>@,;:\\\"/?={} \t\001-\037\177]*" +  static Str -extractRealm(char *q) +extract_auth_val(char **q)  { -    Str p = Strnew(); -    char c; +    unsigned char *qq = *(unsigned char **)q; +    int quoted = 0; +    Str val = Strnew(); + +    SKIP_BLANKS(qq); +    if (*qq == '"') { +	quoted = TRUE; +	Strcat_char(val, *qq++); +    } +    while (*qq != '\0') { +	if (quoted && *qq == '"') { +	    Strcat_char(val, *qq++); +	    break; +	} +	if (!quoted) { +	    switch (*qq) { +	    case '(': +	    case ')': +	    case '<': +	    case '>': +	    case '@': +	    case ',': +	    case ';': +	    case ':': +	    case '\\': +	    case '"': +	    case '/': +	    case '?': +	    case '=': +	    case ' ': +	    case '\t': +		qq++; +		goto end_token; +	    default: +		if (*qq <= 037 || *qq == 177) { +		    qq++; +		    goto end_token; +		} +	    } +	} +	else if (quoted && *qq == '\\') +	    Strcat_char(val, *qq++); +	Strcat_char(val, *qq++); +    } +  end_token: +    if (*qq != '\0') { +	SKIP_BLANKS(qq); +	if (*qq == ',') +	    qq++; +    } +    *q = qq; +    return val; +} -    SKIP_BLANKS(q); -    if (strncasecmp(q, "Basic ", 6) != 0) { -	/* complicated authentication... not implemented */ -	return NULL; +static Str +qstr_unquote(Str s) +{ +    char *p = s->ptr; +    if (*p == '"') { +	Str tmp = Strnew(); +	for (p++; *p != '\0'; p++) { +	    if (*p == '\\') +		p++; +	    Strcat_char(tmp, *p); +	} +	if (Strlastchar(tmp) == '"') +	    Strshrink(tmp, 1); +	return tmp;      } -    q += 6; -    SKIP_BLANKS(q); -    if (strncasecmp(q, "realm=", 6) != 0) { -	/* no realm attribute... get confused */ -	return NULL; +    else +	return s; +} + +static char * +extract_auth_param(char *q, struct auth_param *auth) +{ +    struct auth_param *ap; +    char *q0; +    Regex re_token; + +    newRegex(TOKEN_PAT, FALSE, &re_token, NULL); + +    for (ap = auth; ap->name != NULL; ap++) { +	ap->val = NULL; +    } + +    while (*q != '\0') { +	SKIP_BLANKS(q); +	for (ap = auth; ap->name != NULL; ap++) { +	    if (strncasecmp(q, ap->name, strlen(ap->name)) == 0) { +		q += strlen(ap->name); +		SKIP_BLANKS(q); +		if (*q != '=') +		    return q; +		q++; +		ap->val = extract_auth_val(&q); +		break; +	    } +	} +	if (ap->name == NULL) { +	    /* skip unknown param */ +	    if (RegexMatch(&re_token, q, -1, TRUE) == 0) +		return q; +	    MatchedPosition(&re_token, &q0, &q); +	    SKIP_BLANKS(q); +	    if (*q != '=') +		return q; +	    q++; +	    extract_auth_val(&q); +	} +    } +    return q; +} + +static Str +get_auth_param(struct auth_param *auth, char *name) +{ +    struct auth_param *ap; +    for (ap = auth; ap->name != NULL; ap++) { +	if (strcasecmp(name, ap->name) == 0) +	    return ap->val; +    } +    return NULL; +} + +static Str +AuthBasicCred(struct http_auth *ha, Str uname, Str pw, ParsedURL *pu, +	      HRequest *hr) +{ +    Str s = Strdup(uname); +    Strcat_char(s, ':'); +    Strcat(s, pw); +    return encodeB(s->ptr); +} + +#ifdef USE_DIGEST_AUTH +#include <openssl/md5.h> + +/* RFC2617: 3.2.2 The Authorization Request Header + *  + * credentials      = "Digest" digest-response + * digest-response  = 1#( username | realm | nonce | digest-uri + *                    | response | [ algorithm ] | [cnonce] | + *                     [opaque] | [message-qop] | + *                         [nonce-count]  | [auth-param] ) + * + * username         = "username" "=" username-value + * username-value   = quoted-string + * digest-uri       = "uri" "=" digest-uri-value + * digest-uri-value = request-uri   ; As specified by HTTP/1.1 + * message-qop      = "qop" "=" qop-value + * cnonce           = "cnonce" "=" cnonce-value + * cnonce-value     = nonce-value + * nonce-count      = "nc" "=" nc-value + * nc-value         = 8LHEX + * response         = "response" "=" request-digest + * request-digest = <"> 32LHEX <"> + * LHEX             =  "0" | "1" | "2" | "3" | + *                     "4" | "5" | "6" | "7" | + *                     "8" | "9" | "a" | "b" | + *                     "c" | "d" | "e" | "f" + */ + +static Str +digest_hex(char *p) +{ +    char *h = "0123456789abcdef"; +    Str tmp = Strnew_size(MD5_DIGEST_LENGTH * 2 + 1); +    int i; +    for (i = 0; i < MD5_DIGEST_LENGTH; i++, p++) { +	Strcat_char(tmp, h[(*p >> 4) & 0x0f]); +	Strcat_char(tmp, h[*p & 0x0f]); +    } +    return tmp; +} + +static Str +AuthDigestCred(struct http_auth *ha, Str uname, Str pw, ParsedURL *pu, +	       HRequest *hr) +{ +    Str tmp, a1buf, a2buf, rd, s; +    char md5[MD5_DIGEST_LENGTH + 1]; +    Str uri = HTTPrequestURI(pu, hr); +    /* +     * A1BUF = H(user ":" realm ":" password) +     * A2BUF = H(method ":" path) +     * RESPONSE_DIGEST = H(A1BUF ":" nonce ":" A2BUF) +     */ + +    tmp = Strnew_m_charp(uname->ptr, ":", +			 qstr_unquote(get_auth_param(ha->param, "realm"))->ptr, +			 ":", pw->ptr, NULL); +    MD5(tmp->ptr, strlen(tmp->ptr), md5); +    a1buf = digest_hex(md5); + +    tmp = Strnew_m_charp(HTTPrequestMethod(hr)->ptr, ":", uri->ptr, NULL); +    MD5(tmp->ptr, strlen(tmp->ptr), md5); +    a2buf = digest_hex(md5); + +    tmp = Strnew_m_charp(a1buf->ptr, ":", +			 qstr_unquote(get_auth_param(ha->param, "nonce"))->ptr, +			 ":", a2buf->ptr, NULL); +    MD5(tmp->ptr, strlen(tmp->ptr), md5); +    rd = digest_hex(md5); + +    /* +     * username=<uname>, +     * realm=<realm>, +     * nonce=<nonce>, +     * uri=<uri>, +     * response=<RESPONSE_DIGEST>, +     * [opaque=<opaque>] +     */ + +    tmp = Strnew_m_charp("username=\"", uname->ptr, "\"", NULL); +    Strcat_m_charp(tmp, ", realm=", +		   get_auth_param(ha->param, "realm")->ptr, NULL); +    Strcat_m_charp(tmp, ", nonce=", +		   get_auth_param(ha->param, "nonce")->ptr, NULL); +    Strcat_m_charp(tmp, ", uri=\"", uri->ptr, "\"", NULL); +    Strcat_m_charp(tmp, ", response=\"", rd->ptr, "\"", NULL); + +    if ((s = get_auth_param(ha->param, "opaque")) != NULL) +	Strcat_m_charp(tmp, ", opaque=", s->ptr, NULL); + +    return tmp; +} +#endif + +/* *INDENT-OFF* */ +struct auth_param basic_auth_param[] = { +    {"realm", NULL}, +    {NULL, NULL} +}; + +#ifdef USE_DIGEST_AUTH +/* RFC2617: 3.2.1 The WWW-Authenticate Response Header + * challenge        =  "Digest" digest-challenge + *  + * digest-challenge  = 1#( realm | [ domain ] | nonce | + *                       [ opaque ] |[ stale ] | [ algorithm ] | + *                        [ qop-options ] | [auth-param] ) + * + * domain            = "domain" "=" <"> URI ( 1*SP URI ) <"> + * URI               = absoluteURI | abs_path + * nonce             = "nonce" "=" nonce-value + * nonce-value       = quoted-string + * opaque            = "opaque" "=" quoted-string + * stale             = "stale" "=" ( "true" | "false" ) + * algorithm         = "algorithm" "=" ( "MD5" | "MD5-sess" | + *                        token ) + * qop-options       = "qop" "=" <"> 1#qop-value <"> + * qop-value         = "auth" | "auth-int" | token + */ +struct auth_param digest_auth_param[] = { +    {"realm", NULL}, +    {"domain", NULL}, +    {"nonce", NULL}, +    {"opaque", NULL}, +    {"stale", NULL}, +    {"algorithm", NULL}, +    {"qop", NULL}, +    {NULL, NULL} +}; +#endif +/* for RFC2617: HTTP Authentication */ +struct http_auth www_auth[] = { +    { 1, "Basic ", basic_auth_param, AuthBasicCred }, +#ifdef USE_DIGEST_AUTH +    { 10, "Digest ", digest_auth_param, AuthDigestCred }, +#endif +    { 0, NULL, NULL, NULL,} +}; +/* *INDENT-ON* */ + +static struct http_auth * +findAuthentication(struct http_auth *hauth, Buffer *buf, char *auth_field) +{ +    struct http_auth *ha; +    int len = strlen(auth_field); +    TextListItem *i; +    char *p; +    Regex re_token; + +    newRegex(TOKEN_PAT, FALSE, &re_token, NULL); + +    bzero(hauth, sizeof(struct http_auth)); +    for (i = buf->document_header->first; i != NULL; i = i->next) { +	if (strncasecmp(i->ptr, auth_field, len) == 0) { +	    for (p = i->ptr + len; p != NULL && *p != '\0';) { +		SKIP_BLANKS(p); +		for (ha = &www_auth[0]; ha->scheme != NULL; ha++) { +		    if (strncmp(p, ha->scheme, strlen(ha->scheme)) == 0) { +			if (hauth->pri < ha->pri) { +			    *hauth = *ha; +			    p += strlen(ha->scheme); +			    SKIP_BLANKS(p); +			    p = extract_auth_param(p, hauth->param); +			    break; +			} +			else +			    p += strlen(ha->scheme); +		    } +		} +	    } +	}      } -    q += 6; -    SKIP_BLANKS(q); -    c = '\0'; -    if (*q == '"') -	c = *q++; -    while (*q != '\0' && *q != c) -	Strcat_char(p, *q++); -    return p; +    return hauth->scheme ? hauth : NULL;  }  static Str -getAuthCookie(char *realm, char *auth_header, TextList *extra_header, -	      ParsedURL *pu) +getAuthCookie(struct http_auth *hauth, char *auth_header, +	      TextList *extra_header, ParsedURL *pu, HRequest *hr)  {      Str ss;      Str uname, pwd; @@ -878,6 +1179,7 @@ getAuthCookie(char *realm, char *auth_header, TextList *extra_header,      TextListItem *i, **i0;      int a_found;      int auth_header_len = strlen(auth_header); +    char *realm = qstr_unquote(get_auth_param(hauth->param, "realm"))->ptr;      a_found = FALSE;      for (i0 = &(extra_header->first), i = *i0; i != NULL; @@ -888,8 +1190,9 @@ getAuthCookie(char *realm, char *auth_header, TextList *extra_header,  	}      }      if (a_found) { -	/* This means that *-Authenticate: header is received after * -	 * Authorization: header is sent to the server. */ +	/* This means that *-Authenticate: header is received after +	 * Authorization: header is sent to the server.  +	 */  	if (fmInitialized) {  	    message("Wrong username or password", 0, 0);  	    refresh(); @@ -951,18 +1254,17 @@ getAuthCookie(char *realm, char *auth_header, TextList *extra_header,  				       "Password: "));  #endif  	} -	Strcat_char(uname, ':'); -	Strcat(uname, pwd); -	ss = encodeB(uname->ptr); +	ss = hauth->cred(hauth, uname, pwd, pu, hr);      }      tmp = Strnew_charp(auth_header); -    Strcat_charp(tmp, " Basic "); +    Strcat_m_charp(tmp, " ", hauth->scheme, NULL);      Strcat(tmp, ss);      Strcat_charp(tmp, "\r\n");      pushText(extra_header, tmp->ptr);      return ss;  } +  static int  same_url_p(ParsedURL *pu1, ParsedURL *pu2)  { @@ -997,6 +1299,7 @@ loadGeneralFile(char *path, ParsedURL *volatile current, char *referer,      unsigned char status = HTST_NORMAL;      URLOption url_option;      Str tmp; +    HRequest hr;      tpath = path;      prevtrap = NULL; @@ -1010,7 +1313,7 @@ loadGeneralFile(char *path, ParsedURL *volatile current, char *referer,      url_option.referer = referer;      url_option.flag = flag;      f = openURL(tpath, &pu, current, &url_option, request, extra_header, of, -		&status); +		&hr, &status);      of = NULL;  #ifdef JP_CHARSET      content_charset = '\0'; @@ -1193,10 +1496,11 @@ loadGeneralFile(char *path, ParsedURL *volatile current, char *referer,  	if ((p = checkHeader(t_buf, "WWW-Authenticate:")) != NULL &&  	    http_response_code == 401) {  	    /* Authentication needed */ -	    realm = extractRealm(p); -	    if (realm != NULL) { -		ss = getAuthCookie(realm->ptr, "Authorization:", extra_header, -				   &pu); +	    struct http_auth hauth; +	    if (findAuthentication(&hauth, t_buf, "WWW-Authenticate:") != NULL +		&& (realm = get_auth_param(hauth.param, "realm")) != NULL) { +		ss = getAuthCookie(&hauth, "Authorization:", extra_header, +				   &pu, &hr);  		if (ss == NULL) {  		    /* abort */  		    UFclose(&f); @@ -1212,11 +1516,12 @@ loadGeneralFile(char *path, ParsedURL *volatile current, char *referer,  	if ((p = checkHeader(t_buf, "Proxy-Authenticate:")) != NULL &&  	    http_response_code == 407) {  	    /* Authentication needed */ -	    realm = extractRealm(p); -	    if (realm != NULL) { -		ss = getAuthCookie(realm->ptr, -				   "Proxy-Authorization:", -				   extra_header, &HTTP_proxy_parsed); +	    struct http_auth hauth; +	    if (findAuthentication(&hauth, t_buf, "Proxy-Authenticate:") +		!= NULL +		&& (realm = get_auth_param(hauth.param, "realm")) != NULL) { +		ss = getAuthCookie(&hauth, "Proxy-Authorization:", +				   extra_header, &HTTP_proxy_parsed, &hr);  		proxy_auth_cookie = ss;  		if (ss == NULL) {  		    /* abort */ @@ -1,4 +1,4 @@ -/* $Id: proto.h,v 1.29 2002/01/22 16:59:11 ukai Exp $ */ +/* $Id: proto.h,v 1.30 2002/01/29 19:08:50 ukai Exp $ */  /*    *   This file was automatically generated by version 1.7 of cextract.   *   Manual editing not recommended. @@ -416,10 +416,12 @@ extern void parseURL2(char *url, ParsedURL *pu, ParsedURL *current);  extern Str parsedURL2Str(ParsedURL *pu);  extern int getURLScheme(char **url);  extern void init_stream(URLFile *uf, int scheme, InputStream stream); +Str HTTPrequestMethod(HRequest *hr); +Str HTTPrequestURI(ParsedURL *pu, HRequest *hr);  extern URLFile openURL(char *url, ParsedURL *pu, ParsedURL *current,  		       URLOption *option, FormList *request,  		       TextList *extra_header, URLFile *ouf, -		       unsigned char *status); +		       HRequest *hr, unsigned char *status);  extern int mailcapMatch(struct mailcap *mcap, char *type);  extern struct mailcap *searchMailcap(struct mailcap *table, char *type);  extern void initMailcap(); @@ -1,4 +1,4 @@ -/* $Id: url.c,v 1.38 2002/01/21 18:34:00 ukai Exp $ */ +/* $Id: url.c,v 1.39 2002/01/29 19:08:50 ukai Exp $ */  #include "fm.h"  #include <sys/types.h>  #include <sys/socket.h> @@ -1236,28 +1236,29 @@ otherinfo(ParsedURL *target, ParsedURL *current, char *referer)      return s->ptr;  } -static Str -HTTPrequest(ParsedURL *pu, ParsedURL *current, HRequest *hr, TextList *extra) +Str +HTTPrequestMethod(HRequest *hr)  { -    Str tmp; -    TextListItem *i; -#ifdef USE_COOKIE -    Str cookie; -#endif				/* USE_COOKIE */      switch (hr->command) {      case HR_COMMAND_CONNECT: -	tmp = Strnew_charp("CONNECT "); -	break; +	return Strnew_charp("CONNECT");      case HR_COMMAND_POST: -	tmp = Strnew_charp("POST "); +	return Strnew_charp("POST");  	break;      case HR_COMMAND_HEAD: -	tmp = Strnew_charp("HEAD "); +	return Strnew_charp("HEAD");  	break;      case HR_COMMAND_GET:      default: -	tmp = Strnew_charp("GET "); +	return Strnew_charp("GET");      } +    return NULL; +} + +Str +HTTPrequestURI(ParsedURL *pu, HRequest *hr) +{ +    Str tmp = Strnew();      if (hr->command == HR_COMMAND_CONNECT) {  	Strcat_charp(tmp, pu->host);  	Strcat(tmp, Sprintf(":%d", pu->port)); @@ -1272,6 +1273,20 @@ HTTPrequest(ParsedURL *pu, ParsedURL *current, HRequest *hr, TextList *extra)      else {  	Strcat(tmp, _parsedURL2Str(pu, TRUE));      } +    return tmp; +} + +static Str +HTTPrequest(ParsedURL *pu, ParsedURL *current, HRequest *hr, TextList *extra) +{ +    Str tmp; +    TextListItem *i; +#ifdef USE_COOKIE +    Str cookie; +#endif				/* USE_COOKIE */ +    tmp = HTTPrequestMethod(hr); +    Strcat_charp(tmp, " "); +    Strcat_charp(tmp, HTTPrequestURI(pu, hr)->ptr);      Strcat_charp(tmp, " HTTP/1.0\r\n");      if (hr->referer == NO_REFERER)  	Strcat_charp(tmp, otherinfo(pu, NULL, NULL)); @@ -1346,13 +1361,13 @@ openFTPStream(ParsedURL *pu)  URLFile  openURL(char *url, ParsedURL *pu, ParsedURL *current,  	URLOption *option, FormList *request, TextList *extra_header, -	URLFile *ouf, unsigned char *status) +	URLFile *ouf, HRequest *hr, unsigned char *status)  {      Str tmp;      int i, sock, scheme;      char *p, *q, *u;      URLFile uf; -    HRequest hr; +    HRequest hr0;  #ifdef USE_SSL      SSL *sslh = NULL;  #endif				/* USE_SSL */ @@ -1363,6 +1378,9 @@ openURL(char *url, ParsedURL *pu, ParsedURL *current,  #endif				/* USE_NNTP */      int extlen = strlen(CGI_EXTENSION); +    if (hr == NULL) +	hr = &hr0; +      if (ouf) {  	uf = *ouf;      } @@ -1400,10 +1418,10 @@ openURL(char *url, ParsedURL *pu, ParsedURL *current,      pu->is_nocache = (option->flag & RG_NOCACHE);      uf.ext = filename_extension(pu->file, 1); -    hr.command = HR_COMMAND_GET; -    hr.flag = 0; -    hr.referer = option->referer; -    hr.request = request; +    hr->command = HR_COMMAND_GET; +    hr->flag = 0; +    hr->referer = option->referer; +    hr->request = request;      switch (pu->scheme) {      case SCM_LOCAL: @@ -1501,7 +1519,7 @@ openURL(char *url, ParsedURL *pu, ParsedURL *current,  	    if (sock < 0)  		return uf;  	    uf.scheme = SCM_HTTP; -	    tmp = HTTPrequest(pu, current, &hr, extra_header); +	    tmp = HTTPrequest(pu, current, hr, extra_header);  	    write(sock, tmp->ptr, tmp->length);  	}  	else { @@ -1517,9 +1535,9 @@ openURL(char *url, ParsedURL *pu, ParsedURL *current,  	if (pu->file == NULL)  	    pu->file = allocStr("/", -1);  	if (request && request->method == FORM_METHOD_POST && request->body) -	    hr.command = HR_COMMAND_POST; +	    hr->command = HR_COMMAND_POST;  	if (request && request->method == FORM_METHOD_HEAD) -	    hr.command = HR_COMMAND_HEAD; +	    hr->command = HR_COMMAND_HEAD;  	if (non_null(HTTP_proxy) &&  	    !Do_not_use_proxy &&  	    pu->host != NULL && !check_no_proxy(pu->host)) { @@ -1554,20 +1572,20 @@ openURL(char *url, ParsedURL *pu, ParsedURL *current,  #ifdef USE_SSL  	    if (pu->scheme == SCM_HTTPS) {  		if (*status == HTST_NORMAL) { -		    hr.command = HR_COMMAND_CONNECT; -		    tmp = HTTPrequest(pu, current, &hr, NULL); +		    hr->command = HR_COMMAND_CONNECT; +		    tmp = HTTPrequest(pu, current, hr, NULL);  		    *status = HTST_CONNECT;  		}  		else { -		    hr.flag |= HR_FLAG_LOCAL; -		    tmp = HTTPrequest(pu, current, &hr, extra_header); +		    hr->flag |= HR_FLAG_LOCAL; +		    tmp = HTTPrequest(pu, current, hr, extra_header);  		    *status = HTST_NORMAL;  		}  	    }  	    else  #endif				/* USE_SSL */  	    { -		tmp = HTTPrequest(pu, current, &hr, extra_header); +		tmp = HTTPrequest(pu, current, hr, extra_header);  		*status = HTST_NORMAL;  		pu->label = save_label;  	    } @@ -1587,8 +1605,8 @@ openURL(char *url, ParsedURL *pu, ParsedURL *current,  		}  	    }  #endif				/* USE_SSL */ -	    hr.flag |= HR_FLAG_LOCAL; -	    tmp = HTTPrequest(pu, current, &hr, extra_header); +	    hr->flag |= HR_FLAG_LOCAL; +	    tmp = HTTPrequest(pu, current, hr, extra_header);  	    *status = HTST_NORMAL;  	}  #ifdef USE_SSL @@ -1598,7 +1616,7 @@ openURL(char *url, ParsedURL *pu, ParsedURL *current,  		SSL_write(sslh, tmp->ptr, tmp->length);  	    else  		write(sock, tmp->ptr, tmp->length); -	    if (hr.command == HR_COMMAND_POST && +	    if (hr->command == HR_COMMAND_POST &&  		request->enctype == FORM_ENCTYPE_MULTIPART) {  		if (sslh)  		    SSL_write_from_file(sslh, request->body); @@ -1618,7 +1636,7 @@ openURL(char *url, ParsedURL *pu, ParsedURL *current,  		fclose(ff);  	    }  #endif				/* HTTP_DEBUG */ -	    if (hr.command == HR_COMMAND_POST && +	    if (hr->command == HR_COMMAND_POST &&  		request->enctype == FORM_ENCTYPE_MULTIPART)  		write_from_file(sock, request->body);  	} @@ -1634,7 +1652,7 @@ openURL(char *url, ParsedURL *pu, ParsedURL *current,  	    if (sock < 0)  		return uf;  	    uf.scheme = SCM_HTTP; -	    tmp = HTTPrequest(pu, current, &hr, extra_header); +	    tmp = HTTPrequest(pu, current, hr, extra_header);  	}  	else {  	    sock = openSocket(pu->host, | 
