httpd-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ch...@apache.org
Subject cvs commit: httpd-proxy/module-1.0 CHANGES Makefile.OS2 Makefile.libdir Makefile.tmpl mod_proxy.c mod_proxy.h proxy_cache.c proxy_connect.c proxy_ftp.c proxy_http.c proxy_util.c
Date Thu, 07 Jun 2001 19:51:02 GMT
chuck       01/06/07 12:51:00

  Added:       module-1.0 CHANGES Makefile.OS2 Makefile.libdir
                        Makefile.tmpl mod_proxy.c mod_proxy.h proxy_cache.c
                        proxy_connect.c proxy_ftp.c proxy_http.c
                        proxy_util.c
  Log:
  start assembling the current 1.3 proxy patch - 1 of 3
  This is essentially the 1.3.12 patch from Graham only, updated for 1.3.19
  
  Revision  Changes    Path
  1.1                  httpd-proxy/module-1.0/CHANGES
  
  Index: CHANGES
  ===================================================================
  
  mod_proxy changes for httpd 1.3.21-dev
  
    *) Graham Leggett's original 1.3.12 patch, updated for 1.3.19
       Original comments:
  
       HTTP/1.1 support for mod_proxy:
       - support for Cache-Control
       - conditional support If-Match, If-None-Match,
       If-Unmodified-Since, Etag
       - support for content negotiation using Vary
       - storing of request headers (for Vary support) in cache file 
       - storing of updated response headers (with 304 Not Modified) in 
         cache file
       - support for 64 bit dates and content-lengths in cache file
       Fixes:
       - ProxyPassReverse applied to Content-Location
       - entity headers no longer stripped from response after cache
         revalidation 
       - annotation of mod_proxy cache code
       [Graham Leggett <minfrin@sharp.fm>]
  
       changes to preserve binary compatibility with httpd core
       [Chuck Murcko <chuck@topsail.org>]
  
  
  
  1.1                  httpd-proxy/module-1.0/Makefile.OS2
  
  Index: Makefile.OS2
  ===================================================================
  # Extra rules for making DLLs for OS/2
  
  %.def : %.c
  	echo "LIBRARY $* INITINSTANCE" > $@
  	echo "EXPORTS" >> $@
  	grep "^module .*=" $< | sed "s/module .* \(.*\) =/	\1/" >> $@
  
  
  
  1.1                  httpd-proxy/module-1.0/Makefile.libdir
  
  Index: Makefile.libdir
  ===================================================================
  This is a place-holder which indicates to Configure that it shouldn't
  provide the default targets when building the Makefile in this directory.
  Instead it'll just prepend all the important variable definitions, and
  copy the Makefile.tmpl onto the end.
  
  
  
  1.1                  httpd-proxy/module-1.0/Makefile.tmpl
  
  Index: Makefile.tmpl
  ===================================================================
  
  LIB=libproxy.$(LIBEXT)
  
  OBJS=\
       mod_proxy.o \
       proxy_cache.o proxy_connect.o proxy_ftp.o proxy_http.o proxy_util.o
  OBJS_PIC=\
       mod_proxy.lo \
       proxy_cache.lo proxy_connect.lo proxy_ftp.lo proxy_http.lo proxy_util.lo
  
  all: lib
  
  lib: $(LIB)
  
  libproxy.a: $(OBJS)
  	rm -f $@
  	ar cr $@ $(OBJS)
  	$(RANLIB) $@
  
  libproxy.so: $(OBJS_PIC)
  	rm -f $@
  	$(LD_SHLIB) $(LDFLAGS_SHLIB) -o $@ $(OBJS_PIC) $(LIBS_SHLIB)
  
  libproxy.dll: $(OBJS_PIC) mod_proxy.def
  	$(LD_SHLIB) $(LDFLAGS_SHLIB) -o $* $(OBJS_PIC) $(LIBS_SHLIB)
  	emxbind -b -q -s -h0 -dmod_proxy.def $* && \
  	rm $*
  
  .SUFFIXES: .o .lo .dll
  
  .c.o:
  	$(CC) -c $(INCLUDES) $(CFLAGS) $<
  
  .c.lo:
  	$(CC) -c $(INCLUDES) $(CFLAGS) $(CFLAGS_SHLIB) $< && mv $*.o $*.lo
  
  clean:
  	rm -f $(OBJS) $(OBJS_PIC) $(LIB)
  
  distclean: clean
  	-rm -f Makefile
  
  # We really don't expect end users to use this rule.  It works only with
  # gcc, and rebuilds Makefile.tmpl.  You have to re-run Configure after
  # using it.
  depend:
  	cp Makefile.tmpl Makefile.tmpl.bak \
  	    && sed -ne '1,/^# DO NOT REMOVE/p' Makefile.tmpl > Makefile.new \
  	    && gcc -MM $(INCLUDES) $(CFLAGS) *.c >> Makefile.new \
  	    && sed -e '1,$$s: $(INCDIR)/: $$(INCDIR)/:g' \
  	           -e '1,$$s: $(OSDIR)/: $$(OSDIR)/:g' Makefile.new \
  		> Makefile.tmpl \
  	    && rm Makefile.new
  
  #Dependencies
  
  $(OBJS) $(OBJS_PIC): Makefile
  
  # DO NOT REMOVE
  mod_proxy.o: mod_proxy.c mod_proxy.h $(INCDIR)/httpd.h \
   $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \
   $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \
   $(INCDIR)/ap_ctype.h $(INCDIR)/hsregex.h \
   $(INCDIR)/ap_alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \
   $(INCDIR)/util_uri.h $(INCDIR)/http_config.h \
   $(INCDIR)/http_protocol.h $(INCDIR)/explain.h \
   $(INCDIR)/http_log.h $(INCDIR)/http_vhost.h \
   $(INCDIR)/http_request.h
  proxy_cache.o: proxy_cache.c mod_proxy.h $(INCDIR)/httpd.h \
   $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \
   $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \
   $(INCDIR)/ap_ctype.h $(INCDIR)/hsregex.h \
   $(INCDIR)/ap_alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \
   $(INCDIR)/util_uri.h $(INCDIR)/http_config.h \
   $(INCDIR)/http_protocol.h $(INCDIR)/explain.h \
   $(INCDIR)/http_log.h $(INCDIR)/http_main.h \
   $(INCDIR)/util_date.h $(INCDIR)/multithread.h \
   $(INCDIR)/ap_md5.h
  proxy_connect.o: proxy_connect.c mod_proxy.h $(INCDIR)/httpd.h \
   $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \
   $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \
   $(INCDIR)/ap_ctype.h $(INCDIR)/hsregex.h \
   $(INCDIR)/ap_alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \
   $(INCDIR)/util_uri.h $(INCDIR)/http_config.h \
   $(INCDIR)/http_protocol.h $(INCDIR)/explain.h \
   $(INCDIR)/http_log.h $(INCDIR)/http_main.h
  proxy_ftp.o: proxy_ftp.c mod_proxy.h $(INCDIR)/httpd.h \
   $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \
   $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \
   $(INCDIR)/ap_ctype.h $(INCDIR)/hsregex.h \
   $(INCDIR)/ap_alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \
   $(INCDIR)/util_uri.h $(INCDIR)/http_config.h \
   $(INCDIR)/http_protocol.h $(INCDIR)/explain.h \
   $(INCDIR)/http_main.h $(INCDIR)/http_log.h
  proxy_http.o: proxy_http.c mod_proxy.h $(INCDIR)/httpd.h \
   $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \
   $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \
   $(INCDIR)/ap_ctype.h $(INCDIR)/hsregex.h \
   $(INCDIR)/ap_alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \
   $(INCDIR)/util_uri.h $(INCDIR)/http_config.h \
   $(INCDIR)/http_protocol.h $(INCDIR)/explain.h \
   $(INCDIR)/http_log.h $(INCDIR)/http_main.h \
   $(INCDIR)/http_core.h $(INCDIR)/util_date.h
  proxy_util.o: proxy_util.c mod_proxy.h $(INCDIR)/httpd.h \
   $(INCDIR)/ap_config.h $(INCDIR)/ap_mmn.h \
   $(INCDIR)/ap_config_auto.h $(OSDIR)/os.h \
   $(INCDIR)/ap_ctype.h $(INCDIR)/hsregex.h \
   $(INCDIR)/ap_alloc.h $(INCDIR)/buff.h $(INCDIR)/ap.h \
   $(INCDIR)/util_uri.h $(INCDIR)/http_config.h \
   $(INCDIR)/http_protocol.h $(INCDIR)/explain.h \
   $(INCDIR)/http_main.h $(INCDIR)/ap_md5.h \
   $(INCDIR)/multithread.h $(INCDIR)/http_log.h \
   $(INCDIR)/util_date.h
  
  
  
  1.1                  httpd-proxy/module-1.0/mod_proxy.c
  
  Index: mod_proxy.c
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * Portions of this software are based upon public domain software
   * originally written at the National Center for Supercomputing Applications,
   * University of Illinois, Urbana-Champaign.
   */
  
  #include "mod_proxy.h"
  
  #define CORE_PRIVATE
  
  #include "http_log.h"
  #include "http_vhost.h"
  #include "http_request.h"
  
  /* Some WWW schemes and their default ports; this is basically /etc/services */
  /* This will become global when the protocol abstraction comes */
  static struct proxy_services defports[] =
  {
      {"http", DEFAULT_HTTP_PORT},
      {"ftp", DEFAULT_FTP_PORT},
      {"https", DEFAULT_HTTPS_PORT},
      {"gopher", DEFAULT_GOPHER_PORT},
      {"nntp", DEFAULT_NNTP_PORT},
      {"wais", DEFAULT_WAIS_PORT},
      {"snews", DEFAULT_SNEWS_PORT},
      {"prospero", DEFAULT_PROSPERO_PORT},
      {NULL, -1}			/* unknown port */
  };
  
  /*
   * A Web proxy module. Stages:
   *
   *  translate_name: set filename to proxy:<URL>
   *  type_checker:   set type to PROXY_MAGIC_TYPE if filename begins proxy:
   *  fix_ups:        convert the URL stored in the filename to the
   *                  canonical form.
   *  handler:        handle proxy requests
   */
  
  /* -------------------------------------------------------------- */
  /* Translate the URL into a 'filename' */
  
  static int alias_match(const char *uri, const char *alias_fakename)
  {
      const char *end_fakename = alias_fakename + strlen(alias_fakename);
      const char *aliasp = alias_fakename, *urip = uri;
  
      while (aliasp < end_fakename) {
  	if (*aliasp == '/') {
  	    /* any number of '/' in the alias matches any number in
  	     * the supplied URI, but there must be at least one...
  	     */
  	    if (*urip != '/')
  		return 0;
  
  	    while (*aliasp == '/')
  		++aliasp;
  	    while (*urip == '/')
  		++urip;
  	}
  	else {
  	    /* Other characters are compared literally */
  	    if (*urip++ != *aliasp++)
  		return 0;
  	}
      }
  
      /* Check last alias path component matched all the way */
  
      if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/')
  	return 0;
  
      /* Return number of characters from URI which matched (may be
       * greater than length of alias, since we may have matched
       * doubled slashes)
       */
  
      return urip - uri;
  }
  
  /* Detect if an absoluteURI should be proxied or not.  Note that we
   * have to do this during this phase because later phases are
   * "short-circuiting"... i.e. translate_names will end when the first
   * module returns OK.  So for example, if the request is something like:
   *
   * GET http://othervhost/cgi-bin/printenv HTTP/1.0
   *
   * mod_alias will notice the /cgi-bin part and ScriptAlias it and
   * short-circuit the proxy... just because of the ordering in the
   * configuration file.
   */
  static int proxy_detect(request_rec *r)
  {
      void *sconf = r->server->module_config;
      proxy_server_conf *conf;
  
      conf = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
  
      if (conf->req && r->parsed_uri.scheme) {
  	/* but it might be something vhosted */
         if (!(r->parsed_uri.hostname
  	    && !strcasecmp(r->parsed_uri.scheme, ap_http_method(r))
  	    && ap_matches_request_vhost(r, r->parsed_uri.hostname,
                 r->parsed_uri.port_str ? r->parsed_uri.port : ap_default_port(r)))) {
  	    r->proxyreq = STD_PROXY;
  	    r->uri = r->unparsed_uri;
  	    r->filename = ap_pstrcat(r->pool, "proxy:", r->uri, NULL);
  	    r->handler = "proxy-server";
          }
      }
      /* We need special treatment for CONNECT proxying: it has no scheme part */
      else if (conf->req && r->method_number == M_CONNECT
  	     && r->parsed_uri.hostname
  	     && r->parsed_uri.port_str) {
  	    r->proxyreq = STD_PROXY;
  	    r->uri = r->unparsed_uri;
  	    r->filename = ap_pstrcat(r->pool, "proxy:", r->uri, NULL);
  	    r->handler = "proxy-server";
      }
      return DECLINED;
  }
  
  static int proxy_trans(request_rec *r)
  {
      void *sconf = r->server->module_config;
      proxy_server_conf *conf =
      (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
      int i, len;
      struct proxy_alias *ent = (struct proxy_alias *) conf->aliases->elts;
  
      if (r->proxyreq != NOT_PROXY) {
  	/* someone has already set up the proxy, it was possibly ourselves
  	 * in proxy_detect
  	 */
  	return OK;
      }
  
      /* XXX: since r->uri has been manipulated already we're not really
       * compliant with RFC1945 at this point.  But this probably isn't
       * an issue because this is a hybrid proxy/origin server.
       */
  
      for (i = 0; i < conf->aliases->nelts; i++) {
          len = alias_match(r->uri, ent[i].fake);
  	    
         if (len > 0) {
             r->filename = ap_pstrcat(r->pool, "proxy:", ent[i].real,
                                   r->uri + len, NULL);
             r->handler = "proxy-server";
             r->proxyreq = PROXY_PASS;
             return OK;
  	}
      }
      return DECLINED;
  }
  
  /* -------------------------------------------------------------- */
  /* Fixup the filename */
  
  /*
   * Canonicalise the URL
   */
  static int proxy_fixup(request_rec *r)
  {
      char *url, *p;
  
      if (r->proxyreq == NOT_PROXY || strncmp(r->filename, "proxy:", 6) != 0)
  	return DECLINED;
  
      url = &r->filename[6];
  
  /* canonicalise each specific scheme */
      if (strncasecmp(url, "http:", 5) == 0)
  	return ap_proxy_http_canon(r, url + 5, "http", DEFAULT_HTTP_PORT);
      else if (strncasecmp(url, "ftp:", 4) == 0)
  	return ap_proxy_ftp_canon(r, url + 4);
  
      p = strchr(url, ':');
      if (p == NULL || p == url)
  	return HTTP_BAD_REQUEST;
  
      return OK;		/* otherwise; we've done the best we can */
  }
  
  static void proxy_init(server_rec *r, pool *p)
  {
      ap_proxy_garbage_init(r, p);
  }
  
  
  
  /* Send a redirection if the request contains a hostname which is not */
  /* fully qualified, i.e. doesn't have a domain name appended. Some proxy */
  /* servers like Netscape's allow this and access hosts from the local */
  /* domain in this case. I think it is better to redirect to a FQDN, since */
  /* these will later be found in the bookmarks files. */
  /* The "ProxyDomain" directive determines what domain will be appended */
  static int proxy_needsdomain(request_rec *r, const char *url, const char *domain)
  {
      char *nuri;
      const char *ref;
  
      /* We only want to worry about GETs */
      if (r->proxyreq == NOT_PROXY || r->method_number != M_GET || !r->parsed_uri.hostname)
  	return DECLINED;
  
      /* If host does contain a dot already, or it is "localhost", decline */
      if (strchr(r->parsed_uri.hostname, '.') != NULL
       || strcasecmp(r->parsed_uri.hostname, "localhost") == 0)
  	return DECLINED;	/* host name has a dot already */
  
      ref = ap_table_get(r->headers_in, "Referer");
  
      /* Reassemble the request, but insert the domain after the host name */
      /* Note that the domain name always starts with a dot */
      r->parsed_uri.hostname = ap_pstrcat(r->pool, r->parsed_uri.hostname,
  				     domain, NULL);
      nuri = ap_unparse_uri_components(r->pool,
  				  &r->parsed_uri,
  				  UNP_REVEALPASSWORD);
  
      ap_table_set(r->headers_out, "Location", nuri);
      ap_log_rerror(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, r,
  		"Domain missing: %s sent to %s%s%s", r->uri,
  		ap_unparse_uri_components(r->pool, &r->parsed_uri,
  		      UNP_OMITUSERINFO),
  		ref ? " from " : "", ref ? ref : "");
  
      return HTTP_MOVED_PERMANENTLY;
  }
  
  /* -------------------------------------------------------------- */
  /* Invoke handler */
  
  static int proxy_handler(request_rec *r)
  {
      char *url, *scheme, *p;
      void *sconf = r->server->module_config;
      proxy_server_conf *conf =
      (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
      array_header *proxies = conf->proxies;
      struct proxy_remote *ents = (struct proxy_remote *) proxies->elts;
      int i, rc;
      cache_req *cr;
      int direct_connect = 0;
      const char *maxfwd_str;
  
      if (r->proxyreq == NOT_PROXY || strncmp(r->filename, "proxy:", 6) != 0)
  	return DECLINED;
  
      if (r->method_number == M_TRACE &&
  	(maxfwd_str = ap_table_get(r->headers_in, "Max-Forwards")) != NULL) {
  	long maxfwd = strtol(maxfwd_str, NULL, 10);
  	if (maxfwd < 1) {
  	    int access_status;
  	    r->proxyreq = NOT_PROXY;
  	    if ((access_status = ap_send_http_trace(r)))
  		ap_die(access_status, r);
  	    else
  		ap_finalize_request_protocol(r);
  	    return OK;
  	}
  	ap_table_setn(r->headers_in, "Max-Forwards", 
  		      ap_psprintf(r->pool, "%ld", (maxfwd > 0) ? maxfwd-1 : 0));
      }
  
      if ((rc = ap_setup_client_block(r, REQUEST_CHUNKED_ERROR)))
  	return rc;
  
      url = r->filename + 6;
      p = strchr(url, ':');
      if (p == NULL)
  	return HTTP_BAD_REQUEST;
  
      /* Try serve the request from the cache. If we suceed, we leave. */
      rc = ap_proxy_cache_check(r, url, &conf->cache, &cr);
      if (rc != DECLINED)
  	return rc;
  
      /* If the host doesn't have a domain name, add one and redirect. */
      if (conf->domain != NULL) {
  	rc = proxy_needsdomain(r, url, conf->domain);
  	if (ap_is_HTTP_REDIRECT(rc))
  	    return HTTP_MOVED_PERMANENTLY;
      }
  
      *p = '\0';
      scheme = ap_pstrdup(r->pool, url);
      *p = ':';
  
      /* Check URI's destination host against NoProxy hosts */
      /* Bypass ProxyRemote server lookup if configured as NoProxy */
      /* we only know how to handle communication to a proxy via http */
      /*if (strcasecmp(scheme, "http") == 0) */
      {
  	int ii;
  	struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts;
  
  	for (direct_connect = ii = 0; ii < conf->dirconn->nelts && !direct_connect; ii++) {
  	    direct_connect = list[ii].matcher(&list[ii], r);
  	}
  #if DEBUGGING
  	ap_log_rerror(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r,
  		     (direct_connect) ? "NoProxy for %s" : "UseProxy for %s",
  		     r->uri);
  #endif
      }
  
  /* firstly, try a proxy, unless a NoProxy directive is active */
  
      if (!direct_connect)
  	for (i = 0; i < proxies->nelts; i++) {
  	    p = strchr(ents[i].scheme, ':');	/* is it a partial URL? */
  	    if (strcmp(ents[i].scheme, "*") == 0 ||
  		(p == NULL && strcasecmp(scheme, ents[i].scheme) == 0) ||
  		(p != NULL &&
  	       strncasecmp(url, ents[i].scheme, strlen(ents[i].scheme)) == 0)) {
  		/* CONNECT is a special method that bypasses the normal
  		 * proxy code.
  		 */
  		if (r->method_number == M_CONNECT)
  		    rc = ap_proxy_connect_handler(r, cr, url, ents[i].hostname,
  					       ents[i].port);
  /* we only know how to handle communication to a proxy via http */
  		else if (strcasecmp(ents[i].protocol, "http") == 0)
  		    rc = ap_proxy_http_handler(r, cr, url, ents[i].hostname,
  					    ents[i].port);
  		else
  		    rc = DECLINED;
  
  		/* an error or success */
  		if (rc != DECLINED && rc != HTTP_BAD_GATEWAY)
  		    return rc;
  		/* we failed to talk to the upstream proxy */
  	    }
  	}
  
  /* otherwise, try it direct */
  /* N.B. what if we're behind a firewall, where we must use a proxy or
   * give up??
   */
      /* handle the scheme */
      if (r->method_number == M_CONNECT)
  	return ap_proxy_connect_handler(r, cr, url, NULL, 0);
      if (strcasecmp(scheme, "http") == 0)
  	return ap_proxy_http_handler(r, cr, url, NULL, 0);
      if (strcasecmp(scheme, "ftp") == 0)
  	return ap_proxy_ftp_handler(r, cr, url);
      else
  	return HTTP_FORBIDDEN;
  }
  
  /* -------------------------------------------------------------- */
  /* Setup configurable data */
  
  static void *
       create_proxy_config(pool *p, server_rec *s)
  {
      proxy_server_conf *ps = ap_pcalloc(p, sizeof(proxy_server_conf));
  
      ps->proxies = ap_make_array(p, 10, sizeof(struct proxy_remote));
      ps->aliases = ap_make_array(p, 10, sizeof(struct proxy_alias));
      ps->raliases = ap_make_array(p, 10, sizeof(struct proxy_alias));
      ps->noproxies = ap_make_array(p, 10, sizeof(struct noproxy_entry));
      ps->dirconn = ap_make_array(p, 10, sizeof(struct dirconn_entry));
      ps->nocaches = ap_make_array(p, 10, sizeof(struct nocache_entry));
      ps->allowed_connect_ports = ap_make_array(p, 10, sizeof(int));
      ps->domain = NULL;
      ps->viaopt = via_off; /* initially backward compatible with 1.3.1 */
      ps->viaopt_set = 0; /* 0 means default */
      ps->req = 0;
      ps->req_set = 0;
      ps->recv_buffer_size = 0; /* this default was left unset for some reason */
      ps->recv_buffer_size_set = 0;
  
      ps->cache.root = NULL;
      ps->cache.space = DEFAULT_CACHE_SPACE;
      ps->cache.space_set = 0;
      ps->cache.maxexpire = DEFAULT_CACHE_MAXEXPIRE;
      ps->cache.maxexpire_set = 0;
      ps->cache.defaultexpire = DEFAULT_CACHE_EXPIRE;
      ps->cache.defaultexpire_set = 0;
      ps->cache.lmfactor = DEFAULT_CACHE_LMFACTOR;
      ps->cache.lmfactor_set = 0;
      ps->cache.gcinterval = DEFAULT_CACHE_GCINTERVAL;
      ps->cache.gcinterval_set = 1;
      /* at these levels, the cache can have 2^18 directories (256,000)  */
      ps->cache.dirlevels = 3;
      ps->cache.dirlevels_set = 0;
      ps->cache.dirlength = 1;
      ps->cache.dirlength_set = 0;
      ps->cache.cache_completion = DEFAULT_CACHE_COMPLETION;
      ps->cache.cache_completion_set = 0;
  
      return ps;
  }
  
  static void *
       merge_proxy_config(pool *p, void *basev,
                          void *overridesv)
  {
      proxy_server_conf *ps = ap_pcalloc(p, sizeof(proxy_server_conf));
      proxy_server_conf *base = (proxy_server_conf *) basev;
      proxy_server_conf *overrides = (proxy_server_conf *) overridesv;
  
      ps->proxies = ap_append_arrays(p, base->proxies, overrides->proxies);
      ps->aliases = ap_append_arrays(p, base->aliases, overrides->aliases);
      ps->raliases = ap_append_arrays(p, base->raliases, overrides->raliases);
      ps->noproxies = ap_append_arrays(p, base->noproxies, overrides->noproxies);
      ps->dirconn = ap_append_arrays(p, base->dirconn, overrides->dirconn);
      ps->nocaches = ap_append_arrays(p, base->nocaches, overrides->nocaches);
      ps->allowed_connect_ports = ap_append_arrays(p, base->allowed_connect_ports, overrides->allowed_connect_ports);
  
      ps->domain = (overrides->domain == NULL) ? base->domain : overrides->domain;
      ps->viaopt = (overrides->viaopt_set == 0) ? base->viaopt : overrides->viaopt;
      ps->req = (overrides->req_set == 0) ? base->req : overrides->req;
      ps->recv_buffer_size = (overrides->recv_buffer_size_set == 0) ? base->recv_buffer_size : overrides->recv_buffer_size;
  
      ps->cache.root = (overrides->cache.root == NULL) ? base->cache.root : overrides->cache.root;
      ps->cache.space = (overrides->cache.space_set == 0) ? base->cache.space : overrides->cache.space;
      ps->cache.maxexpire = (overrides->cache.maxexpire_set == 0) ? base->cache.maxexpire : overrides->cache.maxexpire;
      ps->cache.defaultexpire = (overrides->cache.defaultexpire_set == 0) ? base->cache.defaultexpire : overrides->cache.defaultexpire;
      ps->cache.lmfactor = (overrides->cache.lmfactor_set == 0) ? base->cache.lmfactor : overrides->cache.lmfactor;
      ps->cache.gcinterval = (overrides->cache.gcinterval_set == 0) ? base->cache.gcinterval : overrides->cache.gcinterval;
      /* at these levels, the cache can have 2^18 directories (256,000)  */
      ps->cache.dirlevels = (overrides->cache.dirlevels_set == 0) ? base->cache.dirlevels : overrides->cache.dirlevels;
      ps->cache.dirlength = (overrides->cache.dirlength_set == 0) ? base->cache.dirlength : overrides->cache.dirlength;
      ps->cache.cache_completion = (overrides->cache.cache_completion_set == 0) ? base->cache.cache_completion : overrides->cache.cache_completion;
  
      return ps;
  }
  
  static const char *
       add_proxy(cmd_parms *cmd, void *dummy, char *f, char *r)
  {
      server_rec *s = cmd->server;
      proxy_server_conf *conf =
      (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
      struct proxy_remote *new;
      char *p, *q;
      int port;
  
      p = strchr(r, ':');
      if (p == NULL || p[1] != '/' || p[2] != '/' || p[3] == '\0')
  	return "ProxyRemote: Bad syntax for a remote proxy server";
      q = strchr(p + 3, ':');
      if (q != NULL) {
  	if (sscanf(q + 1, "%u", &port) != 1 || port > 65535)
  	    return "ProxyRemote: Bad syntax for a remote proxy server (bad port number)";
  	*q = '\0';
      }
      else
  	port = -1;
      *p = '\0';
      if (strchr(f, ':') == NULL)
  	ap_str_tolower(f);		/* lowercase scheme */
      ap_str_tolower(p + 3);		/* lowercase hostname */
  
      if (port == -1) {
  	int i;
  	for (i = 0; defports[i].scheme != NULL; i++)
  	    if (strcasecmp(defports[i].scheme, r) == 0)
  		break;
  	port = defports[i].port;
      }
  
      new = ap_push_array(conf->proxies);
      new->scheme = f;
      new->protocol = r;
      new->hostname = p + 3;
      new->port = port;
      return NULL;
  }
  
  static const char *
       add_pass(cmd_parms *cmd, void *dummy, char *f, char *r)
  {
      server_rec *s = cmd->server;
      proxy_server_conf *conf =
      (proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
      struct proxy_alias *new;
  
      new = ap_push_array(conf->aliases);
      new->fake = f;
      new->real = r;
      return NULL;
  }
  
  static const char *
      add_pass_reverse(cmd_parms *cmd, void *dummy, char *f, char *r)
  {
      server_rec *s = cmd->server;
      proxy_server_conf *conf;
      struct proxy_alias *new;
  
      conf = (proxy_server_conf *)ap_get_module_config(s->module_config, 
                                                    &proxy_module);
      new = ap_push_array(conf->raliases);
      new->fake = f;
      new->real = r;
      return NULL;
  }
  
  static const char *
       set_proxy_exclude(cmd_parms *parms, void *dummy, char *arg)
  {
      server_rec *s = parms->server;
      proxy_server_conf *conf =
      ap_get_module_config(s->module_config, &proxy_module);
      struct noproxy_entry *new;
      struct noproxy_entry *list = (struct noproxy_entry *) conf->noproxies->elts;
      struct hostent hp;
      int found = 0;
      int i;
  
      /* Don't duplicate entries */
      for (i = 0; i < conf->noproxies->nelts; i++) {
  	if (strcasecmp(arg, list[i].name) == 0) /* ignore case for host names */
  	    found = 1;
      }
  
      if (!found) {
  	new = ap_push_array(conf->noproxies);
  	new->name = arg;
  	/* Don't do name lookups on things that aren't dotted */
  	if (strchr(arg, '.') != NULL && ap_proxy_host2addr(new->name, &hp) == NULL)
  	    /*@@@FIXME: This copies only the first of (possibly many) IP addrs */
  	    memcpy(&new->addr, hp.h_addr, sizeof(struct in_addr));
  	else
  	    new->addr.s_addr = 0;
      }
      return NULL;
  }
  
  /*
   * Set the ports CONNECT can use
   */
  static const char *
      set_allowed_ports(cmd_parms *parms, void *dummy, char *arg)
  {
      server_rec *s = parms->server;
      proxy_server_conf *conf =
        ap_get_module_config(s->module_config, &proxy_module);
      int *New;
  
      if (!ap_isdigit(arg[0]))
  	return "AllowCONNECT: port number must be numeric";
  
      New = ap_push_array(conf->allowed_connect_ports);
      *New = atoi(arg);
      return NULL;
  }
  
  /* Similar to set_proxy_exclude(), but defining directly connected hosts,
   * which should never be accessed via the configured ProxyRemote servers
   */
  static const char *
       set_proxy_dirconn(cmd_parms *parms, void *dummy, char *arg)
  {
      server_rec *s = parms->server;
      proxy_server_conf *conf =
      ap_get_module_config(s->module_config, &proxy_module);
      struct dirconn_entry *New;
      struct dirconn_entry *list = (struct dirconn_entry *) conf->dirconn->elts;
      int found = 0;
      int i;
  
      /* Don't duplicate entries */
      for (i = 0; i < conf->dirconn->nelts; i++) {
  	if (strcasecmp(arg, list[i].name) == 0)
  	    found = 1;
      }
  
      if (!found) {
  	New = ap_push_array(conf->dirconn);
  	New->name = arg;
  	New->hostentry = NULL;
  
  	if (ap_proxy_is_ipaddr(New, parms->pool)) {
  #if DEBUGGING
  	    fprintf(stderr, "Parsed addr %s\n", inet_ntoa(New->addr));
  	    fprintf(stderr, "Parsed mask %s\n", inet_ntoa(New->mask));
  #endif
  	}
  	else if (ap_proxy_is_domainname(New, parms->pool)) {
  	    ap_str_tolower(New->name);
  #if DEBUGGING
  	    fprintf(stderr, "Parsed domain %s\n", New->name);
  #endif
  	}
  	else if (ap_proxy_is_hostname(New, parms->pool)) {
  	    ap_str_tolower(New->name);
  #if DEBUGGING
  	    fprintf(stderr, "Parsed host %s\n", New->name);
  #endif
  	}
  	else {
  	    ap_proxy_is_word(New, parms->pool);
  #if DEBUGGING
  	    fprintf(stderr, "Parsed word %s\n", New->name);
  #endif
  	}
      }
      return NULL;
  }
  
  static const char *
       set_proxy_domain(cmd_parms *parms, void *dummy, char *arg)
  {
      proxy_server_conf *psf =
      ap_get_module_config(parms->server->module_config, &proxy_module);
  
      if (arg[0] != '.')
  	return "ProxyDomain: domain name must start with a dot.";
  
      psf->domain = arg;
      return NULL;
  }
  
  static const char *
       set_proxy_req(cmd_parms *parms, void *dummy, int flag)
  {
      proxy_server_conf *psf =
      ap_get_module_config(parms->server->module_config, &proxy_module);
  
      psf->req = flag;
      psf->req_set = 1;
      return NULL;
  }
  
  
  static const char *
       set_cache_size(cmd_parms *parms, char *struct_ptr, char *arg)
  {
      proxy_server_conf *psf =
      ap_get_module_config(parms->server->module_config, &proxy_module);
      int val;
  
      if (sscanf(arg, "%d", &val) != 1)
  	return "CacheSize value must be an integer (kBytes)";
      psf->cache.space = val;
      psf->cache.space_set = 1;
      return NULL;
  }
  
  static const char *
       set_cache_root(cmd_parms *parms, void *dummy, char *arg)
  {
      proxy_server_conf *psf =
      ap_get_module_config(parms->server->module_config, &proxy_module);
  
      psf->cache.root = arg;
  
      return NULL;
  }
  
  static const char *
       set_cache_factor(cmd_parms *parms, void *dummy, char *arg)
  {
      proxy_server_conf *psf =
      ap_get_module_config(parms->server->module_config, &proxy_module);
      double val;
  
      if (sscanf(arg, "%lg", &val) != 1)
  	return "CacheLastModifiedFactor value must be a float";
      psf->cache.lmfactor = val;
      psf->cache.lmfactor_set = 1;
  
      return NULL;
  }
  
  static const char *
       set_cache_maxex(cmd_parms *parms, void *dummy, char *arg)
  {
      proxy_server_conf *psf =
      ap_get_module_config(parms->server->module_config, &proxy_module);
      double val;
  
      if (sscanf(arg, "%lg", &val) != 1)
  	return "CacheMaxExpire value must be a float";
      psf->cache.maxexpire = (int) (val * (double) SEC_ONE_HR);
      psf->cache.maxexpire_set = 1;
      return NULL;
  }
  
  static const char *
       set_cache_defex(cmd_parms *parms, void *dummy, char *arg)
  {
      proxy_server_conf *psf =
      ap_get_module_config(parms->server->module_config, &proxy_module);
      double val;
  
      if (sscanf(arg, "%lg", &val) != 1)
  	return "CacheDefaultExpire value must be a float";
      psf->cache.defaultexpire = (int) (val * (double) SEC_ONE_HR);
      psf->cache.defaultexpire_set = 1;
      return NULL;
  }
  
  static const char *
       set_cache_gcint(cmd_parms *parms, void *dummy, char *arg)
  {
      proxy_server_conf *psf =
      ap_get_module_config(parms->server->module_config, &proxy_module);
      double val;
  
      if (sscanf(arg, "%lg", &val) != 1)
  	return "CacheGcInterval value must be a float";
      psf->cache.gcinterval = (int) (val * (double) SEC_ONE_HR);
      psf->cache.gcinterval_set = 1;
      return NULL;
  }
  
  static const char *
       set_cache_dirlevels(cmd_parms *parms, char *struct_ptr, char *arg)
  {
      proxy_server_conf *psf =
      ap_get_module_config(parms->server->module_config, &proxy_module);
      int val;
  
      val = atoi(arg);
      if (val < 1)
  	return "CacheDirLevels value must be an integer greater than 0";
      if (val * psf->cache.dirlength > CACHEFILE_LEN)
  	return "CacheDirLevels*CacheDirLength value must not be higher than 20";
      psf->cache.dirlevels = val;
      psf->cache.dirlevels_set = 1;
      return NULL;
  }
  
  static const char *
       set_cache_dirlength(cmd_parms *parms, char *struct_ptr, char *arg)
  {
      proxy_server_conf *psf =
      ap_get_module_config(parms->server->module_config, &proxy_module);
      int val;
  
      val = atoi(arg);
      if (val < 1)
  	return "CacheDirLength value must be an integer greater than 0";
      if (val * psf->cache.dirlevels > CACHEFILE_LEN)
  	return "CacheDirLevels*CacheDirLength value must not be higher than 20";
      psf->cache.dirlength = val;
      psf->cache.dirlength_set = 1;
      return NULL;
  }
  
  static const char *
       set_cache_exclude(cmd_parms *parms, void *dummy, char *arg)
  {
      server_rec *s = parms->server;
      proxy_server_conf *conf =
      ap_get_module_config(s->module_config, &proxy_module);
      struct nocache_entry *new;
      struct nocache_entry *list = (struct nocache_entry *) conf->nocaches->elts;
      struct hostent hp;
      int found = 0;
      int i;
  
      /* Don't duplicate entries */
      for (i = 0; i < conf->nocaches->nelts; i++) {
  	if (strcasecmp(arg, list[i].name) == 0) /* ignore case for host names */
  	    found = 1;
      }
  
      if (!found) {
  	new = ap_push_array(conf->nocaches);
  	new->name = arg;
  	/* Don't do name lookups on things that aren't dotted */
  	if (strchr(arg, '.') != NULL && ap_proxy_host2addr(new->name, &hp) == NULL)
  	    /*@@@FIXME: This copies only the first of (possibly many) IP addrs */
  	    memcpy(&new->addr, hp.h_addr, sizeof(struct in_addr));
  	else
  	    new->addr.s_addr = 0;
      }
      return NULL;
  }
  
  static const char *
       set_recv_buffer_size(cmd_parms *parms, void *dummy, char *arg)
  {
      proxy_server_conf *psf =
      ap_get_module_config(parms->server->module_config, &proxy_module);
      int s = atoi(arg);
      if (s < 512 && s != 0) {
  	return "ProxyReceiveBufferSize must be >= 512 bytes, or 0 for system default.";
      }
  
      psf->recv_buffer_size = s;
      psf->recv_buffer_size_set = 1;
      return NULL;
  }
  
  static const char*
      set_cache_completion(cmd_parms *parms, void *dummy, char *arg)
  {
      proxy_server_conf *psf =
      ap_get_module_config(parms->server->module_config, &proxy_module);
      int s = atoi(arg);
      if (s > 100 || s < 0) {
  	return "CacheForceCompletion must be <= 100 percent, "
                 "or 0 for system default.";
      }
  
      if (s > 0)
        psf->cache.cache_completion = ((float)s / 100);
  
      psf->cache.cache_completion = 1;
      return NULL;    
  }
  
  static const char*
      set_via_opt(cmd_parms *parms, void *dummy, char *arg)
  {
      proxy_server_conf *psf =
      ap_get_module_config(parms->server->module_config, &proxy_module);
  
      if (strcasecmp(arg, "Off") == 0)
          psf->viaopt = via_off;
      else if (strcasecmp(arg, "On") == 0)
          psf->viaopt = via_on;
      else if (strcasecmp(arg, "Block") == 0)
          psf->viaopt = via_block;
      else if (strcasecmp(arg, "Full") == 0)
          psf->viaopt = via_full;
      else {
  	return "ProxyVia must be one of: "
                 "off | on | full | block";
      }
  
      psf->viaopt_set = 1;
      return NULL;    
  }
  
  static const handler_rec proxy_handlers[] =
  {
      {"proxy-server", proxy_handler},
      {NULL}
  };
  
  static const command_rec proxy_cmds[] =
  {
      {"ProxyRequests", set_proxy_req, NULL, RSRC_CONF, FLAG,
       "on if the true proxy requests should be accepted"},
      {"ProxyRemote", add_proxy, NULL, RSRC_CONF, TAKE2,
       "a scheme, partial URL or '*' and a proxy server"},
      {"ProxyPass", add_pass, NULL, RSRC_CONF, TAKE2,
       "a virtual path and a URL"},
      {"ProxyPassReverse", add_pass_reverse, NULL, RSRC_CONF, TAKE2,
       "a virtual path and a URL for reverse proxy behaviour"},
      {"ProxyBlock", set_proxy_exclude, NULL, RSRC_CONF, ITERATE,
       "A list of names, hosts or domains to which the proxy will not connect"},
      {"ProxyReceiveBufferSize", set_recv_buffer_size, NULL, RSRC_CONF, TAKE1,
       "Receive buffer size for outgoing HTTP and FTP connections in bytes"},
      {"NoProxy", set_proxy_dirconn, NULL, RSRC_CONF, ITERATE,
       "A list of domains, hosts, or subnets to which the proxy will connect directly"},
      {"ProxyDomain", set_proxy_domain, NULL, RSRC_CONF, TAKE1,
       "The default intranet domain name (in absence of a domain in the URL)"},
      {"AllowCONNECT", set_allowed_ports, NULL, RSRC_CONF, ITERATE,
       "A list of ports which CONNECT may connect to"},
      {"CacheRoot", set_cache_root, NULL, RSRC_CONF, TAKE1,
       "The directory to store cache files"},
      {"CacheSize", set_cache_size, NULL, RSRC_CONF, TAKE1,
       "The maximum disk space used by the cache in Kb"},
      {"CacheMaxExpire", set_cache_maxex, NULL, RSRC_CONF, TAKE1,
       "The maximum time in hours to cache a document"},
      {"CacheDefaultExpire", set_cache_defex, NULL, RSRC_CONF, TAKE1,
       "The default time in hours to cache a document"},
      {"CacheLastModifiedFactor", set_cache_factor, NULL, RSRC_CONF, TAKE1,
       "The factor used to estimate Expires date from LastModified date"},
      {"CacheGcInterval", set_cache_gcint, NULL, RSRC_CONF, TAKE1,
       "The interval between garbage collections, in hours"},
      {"CacheDirLevels", set_cache_dirlevels, NULL, RSRC_CONF, TAKE1,
       "The number of levels of subdirectories in the cache"},
      {"CacheDirLength", set_cache_dirlength, NULL, RSRC_CONF, TAKE1,
       "The number of characters in subdirectory names"},
      {"NoCache", set_cache_exclude, NULL, RSRC_CONF, ITERATE,
       "A list of names, hosts or domains for which caching is *not* provided"},
      {"CacheForceCompletion", set_cache_completion, NULL, RSRC_CONF, TAKE1,
       "Force a http cache completion after this percentage is loaded"},
      {"ProxyVia", set_via_opt, NULL, RSRC_CONF, TAKE1,
       "Configure Via: proxy header header to one of: on | off | block | full"},
      {NULL}
  };
  
  module MODULE_VAR_EXPORT proxy_module =
  {
      STANDARD_MODULE_STUFF,
      proxy_init,			/* initializer */
      NULL,			/* create per-directory config structure */
      NULL,			/* merge per-directory config structures */
      create_proxy_config,	/* create per-server config structure */
      merge_proxy_config,		/* merge per-server config structures */
      proxy_cmds,			/* command table */
      proxy_handlers,		/* handlers */
      proxy_trans,		/* translate_handler */
      NULL,			/* check_user_id */
      NULL,			/* check auth */
      NULL,			/* check access */
      NULL,			/* type_checker */
      proxy_fixup,		/* pre-run fixups */
      NULL,			/* logger */
      NULL,			/* header parser */
      NULL,			/* child_init */
      NULL,			/* child_exit */
      proxy_detect		/* post read-request */
  };
  
  
  
  
  
  1.1                  httpd-proxy/module-1.0/mod_proxy.h
  
  Index: mod_proxy.h
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * Portions of this software are based upon public domain software
   * originally written at the National Center for Supercomputing Applications,
   * University of Illinois, Urbana-Champaign.
   */
  
  #ifndef MOD_PROXY_H
  #define MOD_PROXY_H 
  
  /*
   * Main include file for the Apache proxy
   */
  
  /*
  
     Note that the Explain() stuff is not yet complete.
     Also note numerous FIXMEs and CHECKMEs which should be eliminated.
  
     If TESTING is set, then garbage collection doesn't delete ... probably a good
     idea when hacking.
  
   */
  
  #define TESTING	0
  #undef EXPLAIN
  
  #include "httpd.h"
  #include "http_config.h"
  #include "http_protocol.h"
  
  #include "explain.h"
  
  extern module MODULE_VAR_EXPORT proxy_module;
  
  
  /* for proxy_canonenc() */
  enum enctype {
      enc_path, enc_search, enc_user, enc_fpath, enc_parm
  };
  
  #define HDR_APP (0)		/* append header, for proxy_add_header() */
  #define HDR_REP (1)		/* replace header, for proxy_add_header() */
  
  /* number of characters in the hash */
  #define HASH_LEN (22*2)
  
  /* maximum  'CacheDirLevels*CacheDirLength' value */
  #define CACHEFILE_LEN 20	/* must be less than HASH_LEN/2 */
  
  #define	SEC_ONE_DAY		86400	/* one day, in seconds */
  #define	SEC_ONE_HR		3600	/* one hour, in seconds */
  
  #define	DEFAULT_FTP_DATA_PORT	20
  #define	DEFAULT_FTP_PORT	21
  #define	DEFAULT_GOPHER_PORT	70
  #define	DEFAULT_NNTP_PORT	119
  #define	DEFAULT_WAIS_PORT	210
  #define	DEFAULT_HTTPS_PORT	443
  #define	DEFAULT_SNEWS_PORT	563
  #define	DEFAULT_PROSPERO_PORT	1525	/* WARNING: conflict w/Oracle */
  
  /* Some WWW schemes and their default ports; this is basically /etc/services */
  struct proxy_services {
      const char *scheme;
      int port;
  };
  
  /* static information about a remote proxy */
  struct proxy_remote {
      const char *scheme;		/* the schemes handled by this proxy, or '*' */
      const char *protocol;	/* the scheme used to talk to this proxy */
      const char *hostname;	/* the hostname of this proxy */
      int port;			/* the port for this proxy */
  };
  
  struct proxy_alias {
      char *real;
      char *fake;
  };
  
  struct dirconn_entry {
      char *name;
      struct in_addr addr, mask;
      struct hostent *hostentry;
      int (*matcher) (struct dirconn_entry * This, request_rec *r);
  };
  
  struct noproxy_entry {
      char *name;
      struct in_addr addr;
  };
  
  struct nocache_entry {
      char *name;
      struct in_addr addr;
  };
  
  #define DEFAULT_CACHE_SPACE 5
  #define DEFAULT_CACHE_MAXEXPIRE SEC_ONE_DAY
  #define DEFAULT_CACHE_EXPIRE    SEC_ONE_HR
  #define DEFAULT_CACHE_LMFACTOR (0.1)
  #define DEFAULT_CACHE_COMPLETION (0.9)
  #define DEFAULT_CACHE_GCINTERVAL SEC_ONE_HR
  
  #ifndef MAX
  #define MAX(a,b)                ((a) > (b) ? (a) : (b))
  #endif
  #ifndef MIN
  #define MIN(a,b)                ((a) < (b) ? (a) : (b))
  #endif
  
  /* static information about the local cache */
  struct cache_conf {
      const char *root;		/* the location of the cache directory */
      off_t space;			/* Maximum cache size (in 1024 bytes) */
      char space_set;
      time_t maxexpire;		/* Maximum time to keep cached files in secs */
      char maxexpire_set;
      time_t defaultexpire;	/* default time to keep cached file in secs */
      char defaultexpire_set;
      double lmfactor;		/* factor for estimating expires date */
      char lmfactor_set;
      time_t gcinterval;		/* garbage collection interval, in seconds */
      char gcinterval_set;
      int dirlevels;		/* Number of levels of subdirectories */
      char dirlevels_set;
      int dirlength;		/* Length of subdirectory names */
      char dirlength_set;
      float cache_completion;	/* Force cache completion after this point */
      char cache_completion_set;
  };
  
  typedef struct {
      struct cache_conf cache;	/* cache configuration */
      array_header *proxies;
      array_header *aliases;
      array_header *raliases;
      array_header *noproxies;
      array_header *dirconn;
      array_header *nocaches;
      array_header *allowed_connect_ports;
      char *domain;		/* domain name to use in absence of a domain name in the request */
      int req;			/* true if proxy requests are enabled */
      char req_set;
      enum {
        via_off,
        via_on,
        via_block,
        via_full
      } viaopt;                   /* how to deal with proxy Via: headers */
      char viaopt_set;
      size_t recv_buffer_size;
      char recv_buffer_size_set;
  } proxy_server_conf;
  
  struct hdr_entry {
      const char *field;
      const char *value;
  };
  
  /* caching information about a request */
  typedef struct {
      request_rec *req;		/* the request */
      char *url;			/* the URL requested */
      char *filename;		/* name of the cache file, or NULL if no cache */
      char *tempfile;		/* name of the temporary file, of NULL if not caching */
      time_t ims;			/* if-Modified-Since date of request; -1 if no header */
      time_t ius;			/* if-Unmodified-Since date of request; -1 if no header */
      const char *im;		/* if-Match etag of request; NULL if no header */
      const char *inm;		/* if-None-Match etag of request; NULL if no header */
      BUFF *fp;			/* the cache file descriptor if the file is cached
  				   and may be returned, or NULL if the file is
  				   not cached (or must be reloaded) */
      BUFF *origfp;		/* the old cache file descriptor if the file has
      				   been revalidated and is being rewritten to
      				   disk */
      time_t expire;		/* calculated expire date of cached entity */
      time_t lmod;		/* last-modified date of cached entity */
      time_t date;		/* the date the cached file was last touched */
      time_t req_time;		/* the time the request started */
      time_t resp_time;		/* the time the response was received */
      int version;		/* update count of the file */
      off_t len;			/* content length */
      char *protocol;		/* Protocol, and major/minor number, e.g. HTTP/1.1 */
      int status;			/* the status of the cached file */
      unsigned int written;	/* total *content* bytes written to cache */
      float cache_completion;	/* specific to this request */
      char *resp_line;		/* the whole status line (protocol, code + message) */
      table *req_hdrs;		/* the original request headers when it was made */
      table *hdrs;		/* the original HTTP response headers of the file */
      char *xcache;		/* the X-Cache header value to be sent to client */
  } cache_req;
  
  /* Additional information passed to the function called by ap_table_do() */
  struct tbl_do_args {
      request_rec *req;
      cache_req *cache;
      size_t len;
  };
  
  struct per_thread_data {
      struct hostent hpbuf;
      u_long ipaddr;
      char *charpbuf[2];
  };
  /* Function prototypes */
  
  /* proxy_cache.c */
  
  void ap_proxy_cache_tidy(cache_req *c);
  int ap_proxy_cache_check(request_rec *r, char *url, struct cache_conf *conf,
  		      cache_req **cr);
  int ap_proxy_cache_update(cache_req *c, table *resp_hdrs,
  		       const int is_HTTP1, int nocache);
  void ap_proxy_garbage_coll(request_rec *r);
  
  /* proxy_connect.c */
  
  int ap_proxy_connect_handler(request_rec *r, cache_req *c, char *url,
  			  const char *proxyhost, int proxyport);
  
  /* proxy_ftp.c */
  
  int ap_proxy_ftp_canon(request_rec *r, char *url);
  int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url);
  
  /* proxy_http.c */
  
  int ap_proxy_http_canon(request_rec *r, char *url, const char *scheme,
  		     int def_port);
  int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url,
  		       const char *proxyhost, int proxyport);
  
  /* proxy_util.c */
  
  int ap_proxy_hex2c(const char *x);
  void ap_proxy_c2hex(int ch, char *x);
  char *ap_proxy_canonenc(pool *p, const char *x, int len, enum enctype t,
  			enum proxyreqtype isenc);
  char *ap_proxy_canon_netloc(pool *p, char **const urlp, char **userp,
  			 char **passwordp, char **hostp, int *port);
  const char *ap_proxy_date_canon(pool *p, const char *x);
  table *ap_proxy_read_headers(request_rec *r, char *buffer, int size, BUFF *f);
  long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c, off_t len, int nowrite);
  void ap_proxy_send_headers(request_rec *r, cache_req *c, const char *respline, const char *xcache, table *t);
  int ap_proxy_liststr(const char *list, const char *key, char **val);
  void ap_proxy_hash(const char *it, char *val, int ndepth, int nlength);
  int ap_proxy_hex2sec(const char *x);
  void ap_proxy_sec2hex(int t, char *y);
  cache_req *ap_proxy_cache_error(cache_req *r);
  int ap_proxyerror(request_rec *r, int statuscode, const char *message);
  const char *ap_proxy_host2addr(const char *host, struct hostent *reqhp);
  int ap_proxy_is_ipaddr(struct dirconn_entry *This, pool *p);
  int ap_proxy_is_domainname(struct dirconn_entry *This, pool *p);
  int ap_proxy_is_hostname(struct dirconn_entry *This, pool *p);
  int ap_proxy_is_word(struct dirconn_entry *This, pool *p);
  int ap_proxy_doconnect(int sock, struct sockaddr_in *addr, request_rec *r);
  int ap_proxy_garbage_init(server_rec *, pool *);
  /* This function is called by ap_table_do() for all header lines */
  int ap_proxy_count_hdr_line(void *p, const char *key, const char *value);
  int ap_proxy_send_hdr_line(void *p, const char *key, const char *value);
  unsigned ap_proxy_bputs2(const char *data, BUFF *client, cache_req *cache);
  time_t ap_proxy_current_age(cache_req *c, const time_t age_value);
  BUFF *ap_proxy_open_cachefile(request_rec *r, char *filename);
  BUFF *ap_proxy_create_cachefile(request_rec *r, char *filename);
  void ap_proxy_clear_connection(pool *p, table *headers);
  int ap_proxy_table_replace(table *base, table *overlay);
  
  #endif /*MOD_PROXY_H*/
  
  
  
  1.1                  httpd-proxy/module-1.0/proxy_cache.c
  
  Index: proxy_cache.c
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * Portions of this software are based upon public domain software
   * originally written at the National Center for Supercomputing Applications,
   * University of Illinois, Urbana-Champaign.
   */
  
  /* Cache and garbage collection routines for Apache proxy */
  
  #include "mod_proxy.h"
  #include "http_conf_globals.h"
  #include "http_log.h"
  #include "http_main.h"
  #include "util_date.h"
  #ifdef WIN32
  #include <sys/utime.h>
  #else
  #include <utime.h>
  #endif /* WIN32 */
  #include "multithread.h"
  #include "ap_md5.h"
  #ifdef __TANDEM
  #include <sys/types.h>
  #include <sys/stat.h>
  #endif
  #ifdef TPF
  #include "os.h"
  #endif
  
  DEF_Explain
  
  struct gc_ent {
      unsigned long int len;
      time_t expire;
      char file[HASH_LEN + 1];
  };
  
  /* Poor man's 61 bit arithmetic */
  typedef struct {
      long lower;	/* lower 30 bits of result */
      long upper; /* upper 31 bits of result */
  } long61_t;
  
  /* FIXME: The block size can be different on a `per file system' base.
   * This would make automatic detection highly OS specific.
   * In the GNU fileutils code for du(1), you can see how complicated it can
   * become to detect the block size. And, with BSD-4.x fragments, it
   * it even more difficult to get precise results.
   * As a compromise (and to improve on the incorrect counting of cache
   * size on byte level, omitting directory sizes entirely, which was
   * used up to apache-1.3b7) we're rounding to multiples of 512 here.
   * Your file system may be using larger blocks (I certainly hope so!)
   * but it will hardly use smaller blocks.
   * (So this approximation is still closer to reality than the old behavior).
   * The best solution would be automatic detection, the next best solution
   * IMHO is a sensible default and the possibility to override it.
   */
  
  #define ROUNDUP2BLOCKS(_bytes) (((_bytes)+block_size-1) & ~(block_size-1))
  static long block_size = 512;	/* this must be a power of 2 */
  static long61_t curbytes, cachesize;
  static time_t garbage_now, garbage_expire;
  static mutex *garbage_mutex = NULL;
  
  
  int ap_proxy_garbage_init(server_rec *r, pool *p)
  {
      if (!garbage_mutex)
  	garbage_mutex = ap_create_mutex(NULL);
  
      return (0);
  }
  
  
  static int sub_garbage_coll(request_rec *r, array_header *files,
  			    const char *cachedir, const char *cachesubdir);
  static void help_proxy_garbage_coll(request_rec *r);
  static int should_proxy_garbage_coll(request_rec *r);
  #if !defined(WIN32) && !defined(MPE) && !defined(OS2) && !defined(NETWARE) && !defined(TPF)
  static void detached_proxy_garbage_coll(request_rec *r);
  #endif
  
  
  void ap_proxy_garbage_coll(request_rec *r)
  {
      static int inside = 0;
  
      (void) ap_acquire_mutex(garbage_mutex);
      if (inside == 1) {
  	(void) ap_release_mutex(garbage_mutex);
  	return;
      }
      else
  	inside = 1;
      (void) ap_release_mutex(garbage_mutex);
  
      ap_block_alarms();		/* avoid SIGALRM on big cache cleanup */
      if (should_proxy_garbage_coll(r))
  #if !defined(WIN32) && !defined(MPE) && !defined(OS2) && !defined(NETWARE) && !defined(TPF)
          detached_proxy_garbage_coll(r);
  #else
          help_proxy_garbage_coll(r);
  #endif
      ap_unblock_alarms();
  
      (void) ap_acquire_mutex(garbage_mutex);
      inside = 0;
      (void) ap_release_mutex(garbage_mutex);
  }
  
  
  static void
  add_long61 (long61_t *accu, long val)
  {
      /* Add in lower 30 bits */
      accu->lower += (val & 0x3FFFFFFFL);
      /* add in upper bits, and carry */
      accu->upper += (val >> 30) + ((accu->lower & ~0x3FFFFFFFL) != 0L);
      /* Clear carry */
      accu->lower &= 0x3FFFFFFFL;
  }
  
  static void
  sub_long61 (long61_t *accu, long val)
  {
      int carry = (val & 0x3FFFFFFFL) > accu->lower;
      /* Subtract lower 30 bits */
      accu->lower = accu->lower - (val & 0x3FFFFFFFL) + ((carry) ? 0x40000000 : 0);
      /* add in upper bits, and carry */
      accu->upper -= (val >> 30) + carry;
  }
  
  /* Compare two long61's:
   * return <0 when left < right
   * return  0 when left == right
   * return >0 when left > right
   */
  static long
  cmp_long61 (long61_t *left, long61_t *right)
  {
      return (left->upper == right->upper) ? (left->lower - right->lower)
  					 : (left->upper - right->upper);
  }
  
  /* Compare two gc_ent's, sort them by expiration date */
  static int gcdiff(const void *ap, const void *bp)
  {
      const struct gc_ent *a = (const struct gc_ent *) ap;
      const struct gc_ent *b = (const struct gc_ent *) bp;
  
      if (a->expire > b->expire)
  	return 1;
      else if (a->expire < b->expire)
  	return -1;
      else
  	return 0;
  }
  
  #if !defined(WIN32) && !defined(MPE) && !defined(OS2) && !defined(NETWARE) && !defined(TPF)
  static void detached_proxy_garbage_coll(request_rec *r)
  {
      pid_t pid;
      int status;
      pid_t pgrp;
  
  #if 0
      ap_log_error(APLOG_MARK, APLOG_DEBUG, r->server,
  			 "proxy: Guess what; we fork() again...");
  #endif
      switch (pid = fork()) {
  	case -1:
  	    ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
  			 "proxy: fork() for cache cleanup failed");
  	    return;
  
  	case 0:	/* Child */
  
  	    /* close all sorts of things, including the socket fd */
  	    ap_cleanup_for_exec();
  
  	    /* Fork twice to disassociate from the child */
  	    switch (pid = fork()) {
  		case -1:
  		    ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
  			 "proxy: fork(2nd) for cache cleanup failed");
  		    exit(1);
  
  		case 0:	/* Child */
  		    /* The setpgrp() stuff was snarfed from http_main.c */
  #ifndef NO_SETSID
  		    if ((pgrp = setsid()) == -1) {
  			perror("setsid");
  			fprintf(stderr, "%s: setsid failed\n",
  				ap_server_argv0);
  			exit(1);
  		    }
  #elif defined(NEXT) || defined(NEWSOS)
  		    if (setpgrp(0, getpid()) == -1 || (pgrp = getpgrp(0)) == -1) {
  			perror("setpgrp");
  			fprintf(stderr, "%S: setpgrp or getpgrp failed\n",
  				ap_server_argv0);
  			exit(1);
  		    }
  #else
  		    if ((pgrp = setpgrp(getpid(), 0)) == -1) {
  			perror("setpgrp");
  			fprintf(stderr, "%s: setpgrp failed\n",
  				ap_server_argv0);
  			exit(1);
  		    }
  #endif
  		    help_proxy_garbage_coll(r);
  		    exit(0);
  
  		default:    /* Father */
  		    /* After grandson has been forked off, */
  		    /* there's nothing else to do. */
  		    exit(0);		    
  	    }
  	default:
  	    /* Wait until grandson has been forked off */
  	    /* (without wait we'd leave a zombie) */
  	    waitpid(pid, &status, 0);
  	    return;
      }
  }
  #endif /* ndef WIN32 */
  
  #define DOT_TIME "/.time"	/* marker */
  
  static int should_proxy_garbage_coll(request_rec *r)
  {
      void *sconf = r->server->module_config;
      proxy_server_conf *pconf =
      (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
      const struct cache_conf *conf = &pconf->cache;
  
      const char *cachedir = conf->root;
      char *filename;
      struct stat buf;
      int timefd;
      time_t every = conf->gcinterval;
      static time_t lastcheck = BAD_DATE;         /* static (per-process) data!!! */
  
      if (cachedir == NULL || every == -1)
          return 0;
  
      filename = ap_palloc(r->pool, strlen(cachedir) + strlen( DOT_TIME ) +1);
  
      garbage_now = time(NULL);
      /* Usually, the modification time of <cachedir>/.time can only increase.
       * Thus, even with several child processes having their own copy of
       * lastcheck, if time(NULL) still < lastcheck then it's not time
       * for GC yet.
       */
      if (garbage_now != -1 && lastcheck != BAD_DATE && garbage_now < lastcheck + every)
          return 0;
  
      strcpy(filename,cachedir);
      strcat(filename,DOT_TIME);
  
      /* At this point we have a bit of an engineering compromise. We could either
       * create and/or mark the .time file  (prior to the fork which might
       * fail on a resource issue) or wait until we are safely forked. The
       * advantage of doing it now in this process is that we get some
       * usefull live out of the global last check variable. (XXX which
       * should go scoreboard IMHO.) Note that the actual counting is 
       * at a later moment.
       */
     if (stat(filename, &buf) == -1) {   /* does not exist */
          if (errno != ENOENT) {
              ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
                           "proxy: stat(%s)", filename);
              return 0;
          }
          if ((timefd = creat(filename, 0666)) == -1) {
              if (errno != EEXIST)
                  ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
                               "proxy: creat(%s)", filename);
              else
                  lastcheck = garbage_now;        /* someone else got in there */
              return 0;
          }
          close(timefd);
      }
      else {
  	lastcheck = buf.st_mtime;       /* save the time */
          if (garbage_now < lastcheck + every) {
              return 0;
          }
          if (utime(filename, NULL) == -1)
              ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
                           "proxy: utimes(%s)", filename);
      }
  
      return 1;
  }
  
  static void help_proxy_garbage_coll(request_rec *r)
  {
      const char *cachedir;
      void *sconf = r->server->module_config;
      proxy_server_conf *pconf =
      (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
      const struct cache_conf *conf = &pconf->cache;
      array_header *files;
      struct gc_ent *fent;
      char *filename;
      int i;
  
      cachedir = conf->root;
      filename = ap_palloc(r->pool, strlen(cachedir) + HASH_LEN + 2);
      /* configured size is given in kB. Make it bytes, convert to long61_t: */
      cachesize.lower = cachesize.upper = 0;
      add_long61(&cachesize, conf->space << 10);
  
      ap_block_alarms();		/* avoid SIGALRM on big cache cleanup */
  
      files = ap_make_array(r->pool, 100, sizeof(struct gc_ent));
      curbytes.upper = curbytes.lower = 0L;
  
      sub_garbage_coll(r, files, cachedir, "/");
  
      if (cmp_long61(&curbytes, &cachesize) < 0L) {
  	ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
  			 "proxy GC: Cache is %ld%% full (nothing deleted)",
  			 (long)(((curbytes.upper<<20)|(curbytes.lower>>10))*100/conf->space));
  	ap_unblock_alarms();
  	return;
      }
  
      /* sort the files we found by expiration date */
      qsort(files->elts, files->nelts, sizeof(struct gc_ent), gcdiff);
  
      for (i = 0; i < files->nelts; i++) {
  	fent = &((struct gc_ent *) files->elts)[i];
  	sprintf(filename, "%s%s", cachedir, fent->file);
  	Explain3("GC Unlinking %s (expiry %ld, garbage_now %ld)", filename, (long)fent->expire, (long)garbage_now);
  #if TESTING
  	fprintf(stderr, "Would unlink %s\n", filename);
  #else
  	if (unlink(filename) == -1) {
  	    if (errno != ENOENT)
  		ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
  			     "proxy gc: unlink(%s)", filename);
  	}
  	else
  #endif
  	{
  	    sub_long61(&curbytes, ROUNDUP2BLOCKS(fent->len));
  	    if (cmp_long61(&curbytes, &cachesize) < 0)
  		break;
  	}
      }
  
      ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server,
  			 "proxy GC: Cache is %ld%% full (%d deleted)",
  			 (long)(((curbytes.upper<<20)|(curbytes.lower>>10))*100/conf->space), i);
      ap_unblock_alarms();
  }
  
  static int sub_garbage_coll(request_rec *r, array_header *files,
  			  const char *cachebasedir, const char *cachesubdir)
  {
      char line[17*(3)];
      char cachedir[HUGE_STRING_LEN];
      struct stat buf;
      int fd, i;
      DIR *dir;
  #if defined(NEXT) || defined(WIN32)
      struct DIR_TYPE *ent;
  #else
      struct dirent *ent;
  #endif
      struct gc_ent *fent;
      int nfiles = 0;
      char *filename;
  
      ap_snprintf(cachedir, sizeof(cachedir), "%s%s", cachebasedir, cachesubdir);
      filename = ap_palloc(r->pool, strlen(cachedir) + HASH_LEN + 2);
      Explain1("GC Examining directory %s", cachedir);
      dir = opendir(cachedir);
      if (dir == NULL) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
  		     "proxy gc: opendir(%s)", cachedir);
  	return 0;
      }
  
      while ((ent = readdir(dir)) != NULL) {
  	if (ent->d_name[0] == '.')
  	    continue;
  	sprintf(filename, "%s%s", cachedir, ent->d_name);
  	Explain1("GC Examining file %s", filename);
  /* is it a temporary file? */
  	if (strncmp(ent->d_name, "tmp", 3) == 0) {
  /* then stat it to see how old it is; delete temporary files > 1 day old */
  	    if (stat(filename, &buf) == -1) {
  		if (errno != ENOENT)
  		    ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
  				 "proxy gc: stat(%s)", filename);
  	    }
  	    else if (garbage_now != -1 && buf.st_atime < garbage_now - SEC_ONE_DAY &&
  		     buf.st_mtime < garbage_now - SEC_ONE_DAY) {
  		Explain1("GC unlink %s", filename);
  		ap_log_error(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, r->server,
  			     "proxy gc: deleting orphaned cache file %s", filename);
  #if TESTING
  		fprintf(stderr, "Would unlink %s\n", filename);
  #else
  		unlink(filename);
  #endif
  	    }
  	    continue;
  	}
  	++nfiles;
  /* is it another file? */
  	/* FIXME: Shouldn't any unexpected files be deleted? */
  	/*      if (strlen(ent->d_name) != HASH_LEN) continue; */
  
  /* under OS/2 use dirent's d_attr to identify a diretory */
  /* under TPF use stat to identify a directory */
  #if defined(OS2) || defined(TPF)
  /* is it a directory? */
  #ifdef OS2
  	if (ent->d_attr & A_DIR) {
  #elif defined(TPF)
      if (stat(filename, &buf) == -1) {
          if (errno != ENOENT)
              ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
                   "proxy gc: stat(%s)", filename);
      }
      if (S_ISDIR(buf.st_mode)) {
  #endif
  	    char newcachedir[HUGE_STRING_LEN];
  	    ap_snprintf(newcachedir, sizeof(newcachedir),
  			"%s%s/", cachesubdir, ent->d_name);
  	    if (!sub_garbage_coll(r, files, cachebasedir, newcachedir)) {
  		ap_snprintf(newcachedir, sizeof(newcachedir),
  			    "%s%s", cachedir, ent->d_name);
  #if TESTING
  		fprintf(stderr, "Would remove directory %s\n", newcachedir);
  #else
  		rmdir(newcachedir);
  #endif
  		--nfiles;
  	    }
  	    continue;
  	}
  #endif
  
  /* read the file */
  #if defined(WIN32)
          /* On WIN32 open does not work for directories, 
           * so we us stat instead of fstat to determine 
           * if the file is a directory 
           */
          if (stat(filename, &buf) == -1) {
              ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
          		 "proxy gc: stat(%s)", filename);
              continue;
          }
          fd = -1;
  #else
   	fd = open(filename, O_RDONLY | O_BINARY);
  	if (fd == -1) {
  	    if (errno != ENOENT)
  		ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
  			     "proxy gc: open(%s)", filename);
  	    continue;
  	}
  	if (fstat(fd, &buf) == -1) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
  			 "proxy gc: fstat(%s)", filename);
  	    close(fd);
  	    continue;
  	}
  #endif
  
  /* In OS/2 and TPF this has already been done above */
  #if !defined(OS2) && !defined(TPF)
  	if (S_ISDIR(buf.st_mode)) {
  	    char newcachedir[HUGE_STRING_LEN];
  #if !defined(WIN32)
              /* Win32 used stat, no file to close */
              close(fd);
  #endif
  	    ap_snprintf(newcachedir, sizeof(newcachedir),
  			"%s%s/", cachesubdir, ent->d_name);
  	    if (!sub_garbage_coll(r, files, cachebasedir, newcachedir)) {
  		ap_snprintf(newcachedir, sizeof(newcachedir),
  			    "%s%s", cachedir, ent->d_name);
  #if TESTING
  		fprintf(stderr, "Would remove directory %s\n", newcachedir);
  #else
  		rmdir(newcachedir);
  #endif
  		--nfiles;
  	    } else {
  		/* Directory is not empty. Account for its size: */
  		add_long61(&curbytes, ROUNDUP2BLOCKS(buf.st_size));
  	    }
  	    continue;
  	}
  #endif
  
  #if defined(WIN32)
          /* Since we have determined above that the file is not a directory,
           * it should be safe to open it now 
           */
          fd = open(filename, O_RDONLY | O_BINARY);
          if (fd == -1) {
              if (errno != ENOENT)
  	        ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
  		             "proxy gc: open(%s) = %d", filename, errno);
              continue;
          }
  #endif
   
  	i = read(fd, line, 17*(3)-1);
  	close(fd);
  	if (i == -1) {
  	    ap_log_error(APLOG_MARK, APLOG_ERR, r->server,
  			 "proxy gc: read(%s)", filename);
  	    continue;
  	}
  	line[i] = '\0';
  	garbage_expire = ap_proxy_hex2sec(line + 17*(2));
  	if (!ap_checkmask(line, "&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&") ||
  	    garbage_expire == BAD_DATE) {
  	    /* bad file */
  	    if (garbage_now != -1 && buf.st_atime > garbage_now + SEC_ONE_DAY &&
  		buf.st_mtime > garbage_now + SEC_ONE_DAY) {
  		ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, r->server,
  			     "proxy: deleting bad cache file with future date: %s", filename);
  #if TESTING
  		fprintf(stderr, "Would unlink bad file %s\n", filename);
  #else
  		unlink(filename);
  #endif
  	    }
  	    continue;
  	}
  
  /*
   * we need to calculate an 'old' factor, and remove the 'oldest' files
   * so that the space requirement is met; sort by the expires date of the
   * file.
   *
   */
  	fent = (struct gc_ent *) ap_push_array(files);
  	fent->len = buf.st_size;
  	fent->expire = garbage_expire;
  	strcpy(fent->file, cachesubdir);
  	strcat(fent->file, ent->d_name);
  
  /* accumulate in blocks, to cope with directories > 4Gb */
  	add_long61(&curbytes, ROUNDUP2BLOCKS(buf.st_size));
      }
  
      closedir(dir);
  
      return nfiles;
  
  }
  
  
  /*
   * Read a cache file;
   * returns 1 on success,
   *         0 on failure (bad file or wrong URL)
   *        -1 on UNIX error
   *
   * We read the cache hex header, then the message response line and
   * response headers, and finally we return with the filepointer
   * pointing at the start of the message body itself, ready to be
   * shipped to the client later on, if appropriate.
   */
  static int rdcache(request_rec *r, BUFF *cachefp, cache_req *c)
  {
      char urlbuff[HUGE_STRING_LEN], *strp;
      int len;
  
      /* read the data from the cache file */
  
      /* Format:
       *
       * The cache needs to keep track of the following information:
       * - Date, LastMod, Version, ReqTime, RespTime, ContentLength
       * - The original request headers (for Vary)
       * - The original response headers (for returning with a cached response)
       * - The body of the message
       *
       * date SP lastmod SP expire SP count SP request-time SP response-time SP content-lengthCRLF
       * (dates are stored as hex seconds since 1970)
       * Original URLCRLF
       * Original Request Headers
       * CRLF
       * Original Response Headers
       * CRLF
       * Body
       * 
       */
  
      /* retrieve cachefile information values */
      len = ap_bgets(urlbuff, sizeof urlbuff, cachefp);
      if (len == -1)
  	return -1;
      if (len == 0 || urlbuff[len - 1] != '\n')
  	return 0;
      urlbuff[len - 1] = '\0';
  
      if (!ap_checkmask(urlbuff,
  		   "&&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&& &&&&&&&&&&&&&&&&"))
  	return 0;
  
      c->date = ap_proxy_hex2sec(urlbuff + 17*(0));
      c->lmod = ap_proxy_hex2sec(urlbuff + 17*(1));
      c->expire = ap_proxy_hex2sec(urlbuff + 17*(2));
      c->version = ap_proxy_hex2sec(urlbuff + 17*(3));
      c->req_time = ap_proxy_hex2sec(urlbuff + 17*(4));
      c->resp_time = ap_proxy_hex2sec(urlbuff + 17*(5));
      c->len = ap_proxy_hex2sec(urlbuff + 17*(6));
  
      /* check that we have the same URL */
      len = ap_bgets(urlbuff, sizeof urlbuff, cachefp);
      if (len == -1)
  	return -1;
      if (len == 0 || strncmp(urlbuff, "X-URL: ", 7) != 0 ||
  	urlbuff[len - 1] != '\n')
  	return 0;
      urlbuff[len - 1] = '\0';
      if (strcmp(urlbuff + 7, c->url) != 0)
  	return 0;
  
      /* then the original request headers */
      c->req_hdrs = ap_proxy_read_headers(r, urlbuff, sizeof urlbuff, cachefp);
      if (c->req_hdrs == NULL)
  	return -1;
  
      /* then the original response headers */
      len = ap_bgets(urlbuff, sizeof urlbuff, cachefp);
      if (len == -1)
  	return -1;
      if (len == 0 || urlbuff[len - 1] != '\n')
  	return 0;
      urlbuff[--len] = '\0';
  
      c->resp_line = ap_pstrdup(r->pool, urlbuff);
      strp = strchr(urlbuff, ' ');
      if (strp == NULL)
  	return 0;
  
      c->status = atoi(strp);
      c->hdrs = ap_proxy_read_headers(r, urlbuff, sizeof urlbuff, cachefp);
      if (c->hdrs == NULL)
  	return -1;
      if (c->len != -1)    /* add a content-length header */
  	if (ap_table_get(c->hdrs, "Content-Length") == NULL) {
  	    ap_table_set(c->hdrs, "Content-Length",
  			 ap_psprintf(r->pool, "%lu", (unsigned long)c->len));
  	}
  
      
      return 1;
  }
  
  /*
   * Call this to check the possible conditional status of
   * the client request, and return the response from the cache
   *
   * Conditionals include If-Modified-Since, If-Match, If-Unmodified-Since
   * and If-None-Match.
   *
   * We don't yet understand If-Range, but we will...
   */
  int ap_proxy_cache_conditional(request_rec *r, cache_req *c, BUFF *cachefp)
  {
      const long int zero = 0L;
      const char *etag, *wetag;
  
      /* get etag */
      if (etag = ap_table_get(c->hdrs, "Etag")) {
  	wetag = ap_pstrcat(r->pool, "W/", etag, NULL);
      }
  
      /* check for If-Match, If-Unmodified-Since
       *
       */
      while (1) {
  
  	/* check If-Match and If-Unmodified-Since exist
  	 *
  	 * If neither of these exist, the request is not conditional, and
  	 * we serve it normally
  	 */
  	if (!c->im && BAD_DATE == c->ius) {
  	    break;
  	}
  
  	/* check If-Match
  	 *
  	 * we check if the Etag on the cached file is in the list of Etags
  	 * in the If-Match field. The comparison must be a strong comparison,
  	 * so the Etag cannot be marked as weak. If the comparision fails
  	 * we return 412 Precondition Failed.
  	 *
  	 * if If-Match is specified AND
  	 * If-Match is not a "*" AND
  	 * Etag is missing or weak or not in the list THEN
  	 * return 412 Precondition Failed
  	 */
  
  	if (c->im) {
  	    if (strcmp(c->im, "*") &&
  	    (!etag || (strlen(etag) > 1 && 'W' == etag[0] && '/' == etag[1]) || !ap_proxy_liststr(c->im, etag, NULL))) {
  		Explain0("If-Match specified, and it didn't - return 412");
  	    }
  	    else {
  		Explain0("If-Match specified, and it matched");
  		break;
  	    }
  	}
  
  	/* check If-Unmodified-Since
  	 *
  	 * if If-Unmodified-Since is specified AND
  	 * Last-Modified is specified somewhere AND
  	 * If-Unmodified-Since is in the past compared to Last-Modified THEN
  	 * return 412 Precondition Failed
  	 */
  	if (BAD_DATE != c->ius && BAD_DATE != c->lmod) {
  	    if (c->ius < c->lmod) {
  		Explain0("If-Unmodified-Since specified, but it wasn't - return 412");
  	    }
  	    else {
  		Explain0("If-Unmodified-Since specified, and it was unmodified");
  		break;
  	    }
  	}
  
  	/* if cache file is being updated */
  	if (c->origfp) {
  	    ap_proxy_send_headers(NULL, c, c->resp_line, c->xcache, c->hdrs);
  	    ap_proxy_send_fb(c->origfp, r, c, c->len, 1);
  	    ap_pclosef(r->pool, ap_bfileno(c->origfp, B_WR));
  	    ap_proxy_cache_tidy(c);
  	}
  	else
  	    ap_pclosef(r->pool, ap_bfileno(cachefp, B_WR));
  
  	Explain0("Use your cached copy, conditional precondition failed.");
  	return HTTP_PRECONDITION_FAILED;
      }
  
  
      /* check for If-None-Match, If-Modified-Since
       *
       */
      while (1) {
  
  	/* check for existance of If-None-Match and If-Modified-Since
  	 *
  	 * if neither of these headers have been set, then the request
  	 * is not conditional, and we just send the cached response and
  	 * be done with it.
  	 */
  	if (!c->inm && BAD_DATE == c->ims) {
  	    break;
  	}
  
  	/* check If-None-Match
  	 *
  	 * we check if the Etag on the cached file is in the list of Etags
  	 * in the If-None-Match field. The comparison must be a strong comparison,
  	 * so the Etag cannot be marked as weak. If the comparision fails
  	 * we return 412 Precondition Failed.
  	 *
  	 * if If-None-Match is specified:
  	 * if If-None-Match is a "*" THEN 304
  	 * else if Etag is specified AND we get a match THEN 304
  	 * else if Weak Etag is specified AND we get a match THEN 304
  	 * else sent the original object
  	 */
  	if (c->inm) {
  	    if (!strcmp(c->inm, "*")) {
  		Explain0("If-None-Match: * specified, return 304");
  	    }
  	    else if (etag && ap_proxy_liststr(c->inm, etag, NULL)) {
  		Explain0("If-None-Match: specified and we got a strong match - return 304");
  	    }
  	    else if (wetag && ap_proxy_liststr(c->inm, wetag, NULL)) {
  		Explain0("If-None-Match specified, and we got a weak match - return 304");
  	    }
  	    else
  		break;
  	}
  
  	/* check If-Modified-Since
  	 *
  	 * if If-Modified-Since is specified AND
  	 * Last-Modified is specified somewhere:
  	 * if last modification date is earlier than If-Modified-Since THEN 304
  	 * else send the original object
  	 */
  	if (BAD_DATE != c->ims && BAD_DATE != c->lmod) {
  	    if (c->ims >= c->lmod) {
  		Explain0("If-Modified-Since specified and not modified, try return 304");
  	    }
  	    else
  		break;
  	}
  
  
  	/* are we updating the cache file? */
  	if (c->origfp) {
  	    ap_proxy_send_headers(NULL, c, c->resp_line, c->xcache, c->hdrs);
  	    ap_proxy_send_fb(c->origfp, r, c, c->len, 1);
  	    ap_pclosef(r->pool, ap_bfileno(c->origfp, B_WR));
  	    ap_proxy_cache_tidy(c);
  	}
  	else
  	    ap_pclosef(r->pool, ap_bfileno(cachefp, B_WR));
  
  	Explain0("Use local copy, cached file hasn't changed");
  	return HTTP_NOT_MODIFIED;
      }
  
  
      /* No conditional - just send it cousin! */
      Explain0("Local copy modified, send it");
      r->status_line = strchr(c->resp_line, ' ') + 1;
      r->status = c->status;
      if (!r->assbackwards) {
  	ap_soft_timeout("proxy send headers", r);
  	ap_proxy_send_headers(r, NULL, c->resp_line, c->xcache, c->hdrs);
  	ap_kill_timeout(r);
      }
      ap_bsetopt(r->connection->client, BO_BYTECT, &zero);
      r->sent_bodyct = 1;
  
      /* are we rewriting the cache file? */
      if (c->origfp) {
  	ap_proxy_send_headers(NULL, c, c->resp_line, NULL, c->hdrs);
  	ap_proxy_send_fb(c->origfp, r, c, c->len, r->header_only);
  	ap_pclosef(r->pool, ap_bfileno(c->origfp, B_WR));
  	ap_proxy_cache_tidy(c);
  	return OK;
      }
  
      /* no, we not */
      if (!r->header_only)
  	ap_proxy_send_fb(cachefp, r, NULL, c->len, 0);
  
      ap_pclosef(r->pool, ap_bfileno(cachefp, B_WR));
      return OK;
  }
  
  
  /*
   * Call this to test for a resource in the cache
   * Returns DECLINED if we need to check the remote host
   * or an HTTP status code if successful
   *
   * Functions:
   *   if URL is cached then
   *      if cached file is not expired then
   *         if last modified after if-modified-since then send body
   *         else send 304 Not modified
   *      else if cached file is expired then
   *         if last modified after if-modified-since then add
   *            last modified date to request
   */
  int ap_proxy_cache_check(request_rec *r, char *url, struct cache_conf *conf,
  		      cache_req **cr)
  {
      char hashfile[66];
      const char *datestr, *pragma_req = NULL, *pragma_cresp = NULL;
      const char *etag = NULL, *wetag = NULL, *cc_req = NULL, *cc_cresp = NULL;
      const char *vary = NULL, *ifmatch = NULL, *ifnonematch = NULL;
      const char *imstr, *pragma, *auth;
      cache_req *c;
      time_t now;
      BUFF *cachefp;
      int cfd, i;
      void *sconf = r->server->module_config;
      proxy_server_conf *pconf =
      (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
      const char *agestr = NULL;
      char *val;
      time_t age_c = 0;
      time_t age, maxage_req, maxage_cresp, maxage, smaxage, maxstale, minfresh;
  
      c = ap_pcalloc(r->pool, sizeof(cache_req));
      *cr = c;
      c->req = r;
      c->url = ap_pstrdup(r->pool, url);
      c->filename = NULL;
      c->tempfile = NULL;
      c->fp = NULL;
      c->origfp = NULL;
      c->version = 0;
      c->len = -1;
      c->req_hdrs = NULL;   
      c->hdrs = NULL;
      c->xcache = NULL;
      datestr = ap_table_get(r->headers_in, "If-Modified-Since");
      if (datestr != NULL) {
          /* this may modify the value in the original table */
        datestr = ap_proxy_date_canon(r->pool, datestr);
        c->ims = ap_parseHTTPdate(datestr);
  /* get the If-Modified-Since date of the request */
        c->ims = ap_parseHTTPdate(imstr);
        if (c->ims == BAD_DATE)	/* bad or out of range date; remove it */
           ap_table_unset(r->headers_in, "If-Modified-Since");
      }
  
  /* get the If-Unmodified-Since date of the request, if it exists */
      c->ius = BAD_DATE;
      datestr = ap_table_get(r->headers_in, "If-Unmodified-Since");
      if (datestr != NULL) {
          /* this may modify the value in the original table */
        datestr = ap_proxy_date_canon(r->pool, datestr); 
        c->ius = ap_parseHTTPdate(datestr);
        if (c->ius == BAD_DATE) /* bad or out of range date; remove it */
            ap_table_unset(r->headers_in, "If-Unmodified-Since");
      }
       
  /* get the If-Match of the request, if it exists */
      c->im = ap_table_get(r->headers_in, "If-Match");
       
  /* get the If-None-Match of the request, if it exists */
      c->inm = ap_table_get(r->headers_in, "If-None-Match");
  
  /* find the filename for this cache entry */
      ap_proxy_hash(url, hashfile, pconf->cache.dirlevels, pconf->cache.dirlength);
      if (conf->root != NULL) {
        c->filename = ap_pstrcat(r->pool, conf->root, "/", hashfile, NULL);
      }
      else {
        c->filename = NULL;
        c->fp = NULL;
        Explain0("No CacheRoot, so no caching. Declining.");
        return DECLINED;
      }
  
  /* find certain cache controlling headers */
      pragma_req = ap_table_get(r->headers_in, "Pragma");
      cc_req = ap_table_get(r->headers_in, "Cache-Control");
  
  /* first things first - does the request allow us to return
   * cached information at all? If not, just decline the request.
   *
   * Note that there is a big difference between not being allowed
   * to cache a request (no-store) and not being allowed to return
   * a cached request without revalidation (max-age=0).
   *
   * Caching is forbidden under the following circumstances:
   *
   * - RFC2616 14.9.2 Cache-Control: no-store
   * we are not supposed to store this request at all. Behave as a tunnel.
   *
   */
      if (ap_proxy_liststr(cc_req, "no-store", NULL)) {
  
  /* delete the previously cached file */
        if (c->filename)
            unlink(c->filename);
        c->fp = NULL;
        c->filename = NULL;
        Explain0("no-store forbids caching. Declining.");
        return DECLINED;
      }
  
  /* if the cache file exists, open it */
      cachefp = NULL;
  /* find out about whether the request can access the cache */
      Explain5("Request for %s, pragma_req=%s, auth=%s, ims=%ld, imstr=%s", url,
             pragma_req, auth, c->ims, imstr); 
      if (c->filename != NULL && r->method_number == M_GET &&
          strlen(url) < 1024 ) {
        cachefp = ap_proxy_open_cachefile(r, c->filename); 
      }
  
      if (cachefp != NULL) {
  	i = rdcache(r, cachefp, c);
  	if (i == -1)
  	    ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  			 "proxy: error reading cache file %s", 
  			 c->filename);
  	else if (i == 0)
  	    ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r,
  			 "proxy: bad (short?) cache file: %s", c->filename);
  	if (i != 1) {
  	    ap_pclosef(r->pool, ap_bfileno(cachefp, B_WR));
  	    cachefp = NULL;
  	}
  	if (c->hdrs) {
  	    cc_cresp = ap_table_get(c->hdrs, "Cache-Control");
  	    pragma_cresp = ap_table_get(c->hdrs, "Pragma");
  	    vary = ap_table_get(c->hdrs, "Vary");
  	    if (agestr = ap_table_get(c->hdrs, "Age")) {
  		age_c = atoi(agestr);
  	    }
  	}
      }
  
      /* if a cache file does not exist, create empty header array */
  /* fixed?  in this case, we want to get the headers from the remote server
     it will be handled later if we don't do this (I hope ;-)
  
      if (cachefp == NULL)
  	c->hdrs = ap_make_table(r->pool, 20);
  */
      /* FIXME: Shouldn't we check the URL somewhere? */
  
  
  
      /* Check Content-Negotiation - Vary
       *
       * At this point we need to make sure that the object we found in the cache
       * is the same object that would be delivered to the client, when the
       * effects of content negotiation are taken into effect.
       *
       * In plain english, we want to make sure that a language-negotiated
       * document in one language is not given to a client asking for a
       * language negotiated document in a different language by mistake.
       *
       * RFC2616 13.6 and 14.44 describe the Vary mechanism.
       */
      if (c->hdrs && c->req_hdrs) {
  	char *vary = ap_pstrdup(r->pool, ap_table_get(c->hdrs, "Vary"));
  	char *p;
  	int i;
  
  	while (vary && *vary) {
  	    char *name = vary;
  	    const char *h1, *h2;
  
  	    /* isolate header name */
  	    while (*vary && !ap_isspace(*vary) && (*vary != ','))
  		++vary;
  	    while (*vary && (ap_isspace(*vary) || (*vary == ','))) {
  		*vary = '\0';
  		++vary;
  	    }
  
  	    /* is this header in the request and the header in the cached
  	     * request identical? If not, we give up and do a straight get */
  	    h1 = ap_table_get(r->headers_in, name);
  	    h2 = ap_table_get(c->req_hdrs, name);
  	    if (h1 == h2) {
  		/* both headers NULL, so a match - do nothing */
  	    }
  	    else if (h1 && h2 && !strcmp(h1, h2)) {
  		/* both headers exist and are equal - do nothing */
  	    }
  	    else {
  
  		/* headers do not match, so Vary failed */
  		c->fp = cachefp;
  		Explain0("Vary header mismatch - object must be fetched from scratch. Declining.");
  		return DECLINED;
  
  	    }
  	}
      }
  
  
      /* We now want to check if our cached data is still fresh. This depends
       * on a few things, in this order:
       *
       * - RFC2616 14.9.4 End to end reload, Cache-Control: no-cache
       * no-cache in either the request or the cached response means that
       * we must revalidate the request unconditionally, overriding any
       * expiration mechanism. It's equivalent to max-age=0,must-revalidate.
       *
       * - RFC2616 14.32 Pragma: no-cache
       * This is treated the same as Cache-Control: no-cache.
       *
       * - RFC2616 14.9.3 Cache-Control: max-stale, must-revalidate, proxy-revalidate
       * if the max-stale request header exists, modify the stale calculations
       * below so that an object can be at most <max-stale> seconds stale before
       * we request a revalidation, _UNLESS_ a must-revalidate or
       * proxy-revalidate cached response header exists to stop us doing this.
       *
       * - RFC2616 14.9.3 Cache-Control: s-maxage
       * the origin server specifies the maximum age an object can be before
       * it is considered stale. This directive has the effect of proxy|must
       * revalidate, which in turn means simple ignore any max-stale setting.
       *
       * - RFC2616 14.9.4 Cache-Control: max-age
       * this header can appear in both requests and responses. If both are
       * specified, the smaller of the two takes priority.
       *
       * - RFC2616 14.21 Expires:
       * if this request header exists in the cached entity, and it's value is
       * in the past, it has expired.
       * 
       */
  
      /* calculate age of object */
      age = ap_proxy_current_age(c, age_c);
  
      /* extract s-maxage */
      if (cc_cresp && ap_proxy_liststr(cc_cresp, "s-maxage", &val))
  	smaxage = atoi(val);
      else
  	smaxage = -1;
  
      /* extract max-age from request */
      if (cc_cresp && ap_proxy_liststr(cc_req, "max-age", &val))
  	maxage_req =  atoi(val);
      else
  	maxage_req = -1;
  
      /* extract max-age from response */
      if (cc_cresp && ap_proxy_liststr(cc_cresp, "max-age", &val))
  	maxage_cresp =  atoi(val);
      else
  	maxage_cresp = -1;
  
      /* if both maxage request and response, the smaller one takes priority */
      if (-1 == maxage_req)
  	maxage = maxage_cresp;
      else if (-1 == maxage_cresp)
  	maxage = maxage_req;
      else
  	maxage = MIN(maxage_req, maxage_cresp);
  
      /* extract max-stale */
      if (cc_req && ap_proxy_liststr(cc_req, "max-stale", &val))
  	maxstale =  atoi(val);
      else
  	maxstale = 0;
  
      /* extract min-fresh */
      if (cc_req && ap_proxy_liststr(cc_req, "min-fresh", &val))
  	minfresh =  atoi(val);
      else
  	minfresh = 0;
  
      /* override maxstale if must-revalidate or proxy-revalidate */
      if (maxstale && ( (cc_cresp && ap_proxy_liststr(cc_cresp, "must-revalidate", NULL)) || (cc_cresp && ap_proxy_liststr(cc_cresp, "proxy-revalidate", NULL)) ))
  	maxstale = 0;
  
      now = time(NULL);
      if (cachefp != NULL &&
  
  	/* handle no-cache */
  	!( (cc_req && ap_proxy_liststr(cc_req, "no-cache", NULL)) ||
  	  (pragma_req && ap_proxy_liststr(pragma_req, "no-cache", NULL)) ||
  	  (cc_cresp && ap_proxy_liststr(cc_cresp, "no-cache", NULL)) ||
  	  (pragma_cresp && ap_proxy_liststr(pragma_cresp, "no-cache", NULL)) ) &&
  
  	/* handle expiration */
  	( (-1 < smaxage && age < (smaxage - minfresh)) ||
  	  (-1 < maxage && age < (maxage + maxstale - minfresh)) ||
  	  (c->expire != BAD_DATE && age < (c->expire - c->date + maxstale - minfresh)) )
  
  	) {
  
  	/* it's fresh darlings... */
  
  	Explain0("Unexpired data available");
  
  	/* set age header on response */
  	ap_table_set(c->hdrs, "Age",
  			ap_psprintf(r->pool, "%lu", (unsigned long)age));
  
  	/* add warning if maxstale overrode freshness calculation */
  	if (!( (-1 < smaxage && age < smaxage) ||
  	     (-1 < maxage && age < maxage) ||
  	     (c->expire != BAD_DATE && (c->expire - c->date) > age) )) {
  	    ap_table_set(c->hdrs, "Warning", "110 Response is stale");
  	}
  
  	/* check conditionals (If-Modified-Since, etc) */
  	c->xcache = ap_pstrcat(r->pool, "HIT from ", ap_get_server_name(r), NULL);
  	return ap_proxy_cache_conditional(r, c, cachefp);
  
  
      }
  
      /* at this point we have determined our cached data needs revalidation
       * but first - we check 1 thing:
       *
       * RFC2616 14.9.4 - if "only-if-cached" specified, send a
       * 504 Gateway Timeout - we're not allowed to revalidate the object
       */
      if (ap_proxy_liststr(cc_req, "only-if-cached", NULL)) {
  	if (cachefp)
  	    ap_pclosef(r->pool, ap_bfileno(cachefp, B_WR));
  	return HTTP_GATEWAY_TIME_OUT;
      }
  
  
      /* If we already have cached data and a last-modified date, and it is
       * not a head request, then add an If-Modified-Since.
       *
       * If we also have an Etag, then the object must have come from
       * an HTTP/1.1 server. Add an If-None-Match as well.
       *
       * See RFC2616 13.3.4
       */
  
      if (cachefp != NULL && !r->header_only) {
  
  	const char *etag = ap_table_get(c->hdrs, "Etag");
  
  	/* If-Modified-Since */
  	if (c->lmod != BAD_DATE) {
  	    /* use the later of the one from the request and the last-modified date
  	     * from the cache */
  	    if (c->ims == BAD_DATE || c->ims < c->lmod) {
  		const char *q;
  
  		if ((q = ap_table_get(c->hdrs, "Last-Modified")) != NULL)
  		    ap_table_set(r->headers_in, "If-Modified-Since", (char *) q);
  	    }
  	}
  
  	/* If-None-Match */
  	if (etag) {
  	    ap_table_set(r->headers_in, "If-None-Match", etag);
  	}
  
      }
  
  
      c->fp = cachefp;
  
      Explain0("Local copy not present or expired. Declining.");
  
      return DECLINED;
  }
  
  /*
   * Having read the response from the client, decide what to do
   * If the response is not cachable, then delete any previously cached
   * response, and copy data from remote server to client.
   * Functions:
   *  parse dates
   *  check for an uncachable response
   *  calculate an expiry date, if one is not provided
   *  if the remote file has not been modified, then return the document
   *  from the cache, maybe updating the header line
   *  otherwise, delete the old cached file and open a new temporary file
   */
  int ap_proxy_cache_update(cache_req *c, table *resp_hdrs,
  		       const int is_HTTP1, int nocache)
  {
  #if defined(ULTRIX_BRAIN_DEATH) || defined(SINIX_D_RESOLVER_BUG)
    extern char *mktemp(char *template);
  #endif 
      request_rec *r = c->req;
      char *p;
      int i;
      const char *expire, *lmods, *dates, *clen;
      time_t expc, date, lmod, now;
      char buff[17*7+1];
      void *sconf = r->server->module_config;
      proxy_server_conf *conf =
      (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
      const long int zero = 0L;
      const char *cc_resp;
      off_t orig_clen;
      /* buffer to store response headers */
      char *hdr_buff[HUGE_STRING_LEN];
      table *req_hdrs;
  
      cc_resp = ap_table_get(resp_hdrs, "Cache-Control");
  
      c->tempfile = NULL;
  
      /* we've received the response from the origin server */
      
      /* read expiry date; if a bad date, then leave it so the client can
       * read it */
      expire = ap_table_get(resp_hdrs, "Expires");
      if (expire != NULL)
  	expc = ap_parseHTTPdate(expire);
      else
  	expc = BAD_DATE;
  
      /* read the last-modified date; if the date is bad, then delete it */
      lmods = ap_table_get(resp_hdrs, "Last-Modified");
      if (lmods != NULL) {
  	lmod = ap_parseHTTPdate(lmods);
  	if (lmod == BAD_DATE) {
  	    /* kill last modified date */
  	    lmods = NULL;
  	}
      }
      else
  	lmod = BAD_DATE;
  
  
      /*
       * what responses should we not cache?
       *
       * At this point we decide based on the response headers whether it
       * is appropriate _NOT_ to cache the data from the server. There are
       * a whole lot of conditions that prevent us from caching this data.
       * They are tested here one by one to be clear and unambiguous. */
  
      /* RFC2616 13.4 we are allowed to cache 200, 203, 206, 300, 301 or 410
       * We don't cache 206, because we don't (yet) cache partial responses.
       * We include 304 Not Modified here too as this is the origin server
       * telling us to serve the cached copy. */
      if ((r->status != HTTP_OK && r->status != HTTP_NON_AUTHORITATIVE && r->status != HTTP_MULTIPLE_CHOICES && r->status != HTTP_MOVED_PERMANENTLY && r->status != HTTP_NOT_MODIFIED) ||
  
      /* if a broken Expires header is present, don't cache it */
  	(expire != NULL && expc == BAD_DATE) ||
  
      /* if the server said 304 Not Modified but we have no cache file - pass
       * this untouched to the user agent, it's not for us. */
  	(r->status == HTTP_NOT_MODIFIED && (c == NULL || c->fp == NULL)) ||
  
      /* 200 OK response from HTTP/1.0 and up without a Last-Modified header */
  	(r->status == HTTP_OK && lmods == NULL && is_HTTP1) ||
  
      /* HEAD requests */
  	r->header_only ||
  
      /* RFC2616 14.9.2 Cache-Control: no-store response indicating do not
       * cache, or stop now if you are trying to cache it */
          ap_proxy_liststr(cc_resp, "no-store", NULL) ||
  
      /* RFC2616 14.9.1 Cache-Control: private
       * this object is marked for this user's eyes only. Behave as a tunnel. */
          ap_proxy_liststr(cc_resp, "private", NULL) ||
  
      /* RFC2616 14.8 Authorisation:
       * if authorisation is included in the request, we don't cache, but we
       * can cache if the following exceptions are true:
       * 1) If Cache-Control: s-maxage is included
       * 2) If Cache-Control: must-revalidate is included
       * 3) If Cache-Control: public is included
       */
  	(ap_table_get(r->headers_in, "Authorization") != NULL
  
  	&& !(ap_proxy_liststr(cc_resp, "s-maxage", NULL) || ap_proxy_liststr(cc_resp, "must-revalidate", NULL) || ap_proxy_liststr(cc_resp, "public", NULL))
  	) ||
  
      /* or we've been asked not to cache it above */
  	nocache) {
  
  	Explain1("Response is not cacheable, unlinking %s", c->filename);
  
  	/* close the file */
  	if (c->fp != NULL) {
  	    ap_pclosef(r->pool, ap_bfileno(c->fp, B_WR));
  	    c->fp = NULL;
  	}
  
  	/* delete the previously cached file */
          if (c->filename)
              unlink(c->filename);
  	return DECLINED;	/* send data to client but not cache */
      }
  
  
  
  
  
      /* It's safe to cache the response.
       *
       * We now want to update the cache file header information with
       * the new date, last modified, expire and content length and write
       * it away to our cache file. First, we determine these values from
       * the response, using heuristics if appropriate.
       *
       * In addition, we make HTTP/1.1 age calculations and write them away
       * too.
       */
  
      /* Read the date. Generate one if one is not supplied */
      dates = ap_table_get(resp_hdrs, "Date");
      if (dates != NULL)
  	date = ap_parseHTTPdate(dates);
      else
  	date = BAD_DATE;
  
      now = time(NULL);
  
      if (date == BAD_DATE) {	/* No, or bad date */
  /* no date header! */
  /* add one; N.B. use the time _now_ rather than when we were checking the cache
   */
  	date = now;
  	dates = ap_gm_timestr_822(r->pool, now);
  	ap_table_set(resp_hdrs, "Date", dates);
  	Explain0("Added date header");
      }
  
  /* set response_time for HTTP/1.1 age calculations */
      c->resp_time = now;
  
  /* check last-modified date */
      if (lmod != BAD_DATE && lmod > date)
  /* if its in the future, then replace by date */
      {
  	lmod = date;
  	lmods = dates;
  	Explain0("Last modified is in the future, replacing with now");
      }
  /* if the response did not contain the header, then use the cached version */
      if (lmod == BAD_DATE && c->fp != NULL) {
  	lmod = c->lmod;
  	Explain0("Reusing cached last modified");
      }
  
  /* we now need to calculate the expire data for the object. */
      if (expire == NULL && c->fp != NULL) {	/* no expiry data sent in response */
  	expire = ap_table_get(c->hdrs, "Expires");
  	if (expire != NULL)
  	    expc = ap_parseHTTPdate(expire);
      }
  /* so we now have the expiry date */
  /* if no expiry date then
   *   if lastmod
   *      expiry date = now + min((date - lastmod) * factor, maxexpire)
   *   else
   *      expire date = now + defaultexpire
   */
      Explain1("Expiry date is %ld", (long)expc);
      if (expc == BAD_DATE) {
  	if (lmod != BAD_DATE) {
  	    double x = (double) (date - lmod) * conf->cache.lmfactor;
  	    double maxex = conf->cache.maxexpire;
  	    if (x > maxex)
  		x = maxex;
  	    expc = now + (int) x;
  	}
  	else
  	    expc = now + conf->cache.defaultexpire;
  	Explain1("Expiry date calculated %ld", (long)expc);
      }
  
  /* get the content-length header */
      clen = ap_table_get(resp_hdrs, "Content-Length");
      if (clen == NULL)
  	c->len = -1;
      else
  	c->len = atoi(clen);
  
  /* we have all the header information we need - write it to the cache file
   */      
      c->version++;
      ap_proxy_sec2hex(date, buff + 17*(0));
      buff[17*(1)-1] = ' ';
      ap_proxy_sec2hex(lmod, buff + 17*(1));
      buff[17*(2)-1] = ' '; 
      ap_proxy_sec2hex(expc, buff + 17*(2));
      buff[17*(3)-1] = ' ';
      ap_proxy_sec2hex(c->version, buff + 17*(3));
      buff[17*(4)-1] = ' ';
      ap_proxy_sec2hex(c->req_time, buff + 17*(4));
      buff[17*(5)-1] = ' ';
      ap_proxy_sec2hex(c->resp_time, buff + 17*(5));
      buff[17*(6)-1] = ' '; 
      ap_proxy_sec2hex(c->len, buff + 17*(6));
      buff[17*(7)-1] = '\n';
      buff[17*(7)] = '\0';
  
  /* Was the server response a 304 Not Modified?
   *
   * If it was, it means that we requested a revalidation, and that
   * the result of that revalidation was that the object was fresh.
   *
   */
  
  /* if response from server 304 not modified */
        if (r->status == HTTP_NOT_MODIFIED) {
  
  /* Have the headers changed?
   *
   * if not - we fulfil the request and return now.
   */
  
          if (c->hdrs) {
            if (!ap_proxy_table_replace(c->hdrs, resp_hdrs)) {
                c->xcache = ap_pstrcat(r->pool, "HIT from ", ap_get_server_name(r), " (with revalidation)", NULL);
                return ap_proxy_cache_conditional(r, c, c->fp);
  	    }
          }
          else
            c->hdrs = resp_hdrs;
  /* if we get here - the headers have changed. Go through the motions
   * of creating a new temporary cache file below, we'll then serve
   * the request like we would have in ap_proxy_cache_conditional()
   * above, and at the same time we will also rewrite the contents
   * to the new temporary file.
   */
        }
  
  /* 
   * Ok - lets prepare and open the cached file
   * 
   * If a cached file (in c->fp) is already open, then we want to
   * update that cached file. Copy the c->fp to c->origfp and open
   * up a new one.
   *  
   * If the cached file (in c->fp) is NULL, we must open a new cached
   * file from scratch.
   *
   * The new cache file will be moved to it's final location in the
   * directory tree later, overwriting the old cache file should it exist.
   */       
  
  /* if a cache file was already open */
      if (c->fp != NULL) {
        c->origfp = c->fp;
      }
  
      while (1) {
        struct tbl_do_args tdo;
  
  /* create temporary filename */
  #ifndef TPF
  #define TMPFILESTR    "/tmpXXXXXX"
        if (conf->cache.root == NULL) {
            c = ap_proxy_cache_error(c);
            break;
        }
        c->tempfile = ap_palloc(r->pool, strlen(conf->cache.root) + sizeof(TMPFILESTR));
        strcpy(c->tempfile, conf->cache.root);
        strcat(c->tempfile, TMPFILESTR);
  #undef TMPFILESTR
        p = mktemp(c->tempfile);
  #else
        if (conf->cache.root == NULL) {
            c = ap_proxy_cache_error(c);
            break;
        }
        c->tempfile = ap_palloc(r->pool, strlen(conf->cache.root) +1+ L_tmpnam);
        strcpy(c->tempfile, conf->cache.root);
        strcat(c->tempfile, "/");
        p = tmpnam(NULL);
        strcat(c->tempfile, p);
  #endif
        if (p == NULL) {
            c = ap_proxy_cache_error(c);
            break;
        }
  
        Explain1("Create temporary file %s", c->tempfile);
  
  /* create the new file */
        c->fp = ap_proxy_create_cachefile(r, c->tempfile);
        if (NULL == c->fp) {
            c = ap_proxy_cache_error(c);
            break;
        }
  
  /* write away the cache header and the URL */
        if (ap_bvputs(c->fp, buff, "X-URL: ", c->url, "\n", NULL) == -1) {
            ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
                         "proxy: error writing cache file(%s)", c->tempfile);
            c = ap_proxy_cache_error(c);
            break;
        }
  
  /* get original request headers */
        if (c->req_hdrs)
            req_hdrs = ap_copy_table(r->pool, c->req_hdrs);
        else
            req_hdrs = ap_copy_table(r->pool, r->headers_in);
  
  /* remove hop-by-hop headers */
        ap_proxy_clear_connection(r->pool, req_hdrs);
  
  /* save original request headers */
        tdo.req = NULL;
        tdo.cache = c;
        if (c->req_hdrs)
            ap_table_do(ap_proxy_send_hdr_line, &tdo, c->req_hdrs, NULL);
        else
            ap_table_do(ap_proxy_send_hdr_line, &tdo, r->headers_in, NULL);
        if (ap_bputs(CRLF, c->fp) == -1) {
            ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
                        "proxy: error writing request headers terminating CRLF to %s", c->tempfile);
            c = ap_proxy_cache_error(c);
            break;
        }
        break;
      }
  
  /* Was the server response a 304 Not Modified?
   *
   * If so, we have some work to do that we didn't do when we first
   * checked above. We need to fulfil the request, and we need to
   * copy the body from the old object to the new one.
   */
  
  /* if response from server 304 not modified */
      if (r->status == HTTP_NOT_MODIFIED) {
  
  /* fulfil the request */
        c->xcache = ap_pstrcat(r->pool, "HIT from ", ap_get_server_name(r), " (with revalidation)", NULL);
        return ap_proxy_cache_conditional(r, c, c->fp);
  
      }
      return DECLINED;
  }
  
  void ap_proxy_cache_tidy(cache_req *c)
  {
      server_rec *s;
      long int bc;
  
      if (!c || !c->fp)
  	return;
  
      s = c->req->server;
  
  /* don't care how much was sent, but rather how much was written to cache
      ap_bgetopt(c->req->connection->client, BO_BYTECT, &bc);
   */
      bc = c->written;
  
      if (c->len != -1) {
  /* file lengths don't match; don't cache it */
  	if (bc != c->len) {
  	    ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR));	/* no need to flush */
  	    unlink(c->tempfile);
  	    return;
  	}
      }
  /* don't care if aborted, cache it if fully retrieved from host!
      else if (c->req->connection->aborted) {
  	ap_pclosef(c->req->pool, c->fp->fd);	/ no need to flush /
  	unlink(c->tempfile);
  	return;
      }
  */
      else {
  /* update content-length of file */
  	char buff[17];
  	off_t curpos;
  
  	c->len = bc;
  	ap_bflush(c->fp);
  	ap_proxy_sec2hex(c->len, buff);
  	curpos = lseek(ap_bfileno(c->fp, B_WR), 17*6, SEEK_SET);
  	if (curpos == -1)
  	    ap_log_error(APLOG_MARK, APLOG_ERR, s,
  			 "proxy: error seeking on cache file %s", c->tempfile);
  	else if (write(ap_bfileno(c->fp, B_WR), buff, sizeof(buff) - 1) == -1)
  	    ap_log_error(APLOG_MARK, APLOG_ERR, s,
  			 "proxy: error updating cache file %s", c->tempfile);
      }
  
      if (ap_bflush(c->fp) == -1) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, s,
  		     "proxy: error writing to cache file %s",
  		     c->tempfile);
  	ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR));
  	unlink(c->tempfile);
  	return;
      }
  
      if (ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR)) == -1) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, s,
  		     "proxy: error closing cache file %s", c->tempfile);
  	unlink(c->tempfile);
  	return;
      }
  
      if (unlink(c->filename) == -1 && errno != ENOENT) {
  	ap_log_error(APLOG_MARK, APLOG_ERR, s,
  		     "proxy: error deleting old cache file %s",
  		     c->tempfile);
      }
      else {
  	char *p;
  	proxy_server_conf *conf =
  	(proxy_server_conf *) ap_get_module_config(s->module_config, &proxy_module);
  
  	for (p = c->filename + strlen(conf->cache.root) + 1;;) {
  	    p = strchr(p, '/');
  	    if (!p)
  		break;
  	    *p = '\0';
  #if defined(WIN32) || defined(NETWARE)
  	    if (mkdir(c->filename) < 0 && errno != EEXIST)
  #elif defined(__TANDEM)
  	    if (mkdir(c->filename, S_IRWXU | S_IRWXG | S_IRWXO) < 0 && errno != EEXIST)
  #else
  	    if (mkdir(c->filename, S_IREAD | S_IWRITE | S_IEXEC) < 0 && errno != EEXIST)
  #endif /* WIN32 */
  		ap_log_error(APLOG_MARK, APLOG_ERR, s,
  			     "proxy: error creating cache directory %s",
  			     c->filename);
  	    *p = '/';
  	    ++p;
  	}
  #if defined(OS2) || defined(WIN32) || defined(NETWARE) || defined(MPE)
  	/* Under OS/2 use rename. */
  	if (rename(c->tempfile, c->filename) == -1)
  	    ap_log_error(APLOG_MARK, APLOG_ERR, s,
  			 "proxy: error renaming cache file %s to %s",
  			 c->tempfile, c->filename);
      }
  #else
  
  	if (link(c->tempfile, c->filename) == -1)
  	    ap_log_error(APLOG_MARK, APLOG_ERR, s,
  			 "proxy: error linking cache file %s to %s",
  			 c->tempfile, c->filename);
      }
  
      if (unlink(c->tempfile) == -1)
  	ap_log_error(APLOG_MARK, APLOG_ERR, s,
  		     "proxy: error deleting temp file %s", c->tempfile);
  #endif
  
  }
  
  
  
  1.1                  httpd-proxy/module-1.0/proxy_connect.c
  
  Index: proxy_connect.c
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * Portions of this software are based upon public domain software
   * originally written at the National Center for Supercomputing Applications,
   * University of Illinois, Urbana-Champaign.
   */
  
  /* CONNECT method for Apache proxy */
  
  #include "mod_proxy.h"
  #include "http_log.h"
  #include "http_main.h"
  
  #ifdef HAVE_BSTRING_H
  #include <bstring.h>		/* for IRIX, FD_SET calls bzero() */
  #endif
  
  DEF_Explain
  
  /*  
   * This handles Netscape CONNECT method secure proxy requests.
   * A connection is opened to the specified host and data is
   * passed through between the WWW site and the browser.
   *
   * This code is based on the INTERNET-DRAFT document
   * "Tunneling SSL Through a WWW Proxy" currently at
   * http://www.mcom.com/newsref/std/tunneling_ssl.html.
   *
   * If proxyhost and proxyport are set, we send a CONNECT to 
   * the specified proxy..  
   *
   * FIXME: this is bad, because it does its own socket I/O
   *        instead of using the I/O in buff.c.  However,
   *        the I/O in buff.c blocks on reads, and because
   *        this function doesn't know how much data will
   *        be sent either way (or when) it can't use blocking
   *        I/O.  This may be very implementation-specific
   *        (to Linux).  Any suggestions?
   * FIXME: this doesn't log the number of bytes sent, but
   *        that may be okay, since the data is supposed to
   *        be transparent. In fact, this doesn't log at all
   *        yet. 8^)
   * FIXME: doesn't check any headers initally sent from the
   *        client.
   * FIXME: should allow authentication, but hopefully the
   *        generic proxy authentication is good enough.
   * FIXME: no check for r->assbackwards, whatever that is.
   */
  
  static int
  allowed_port(proxy_server_conf *conf, int port)
  {
      int i;
      int *list = (int *) conf->allowed_connect_ports->elts;
  
      for(i = 0; i < conf->allowed_connect_ports->nelts; i++) {
  	if(port == list[i])
  	    return 1;
      }
      return 0;
  }
  
  
  int ap_proxy_connect_handler(request_rec *r, cache_req *c, char *url,
  			  const char *proxyhost, int proxyport)
  {
      struct sockaddr_in server;
      struct in_addr destaddr;
      struct hostent server_hp;
      const char *host, *err;
      char *p;
      int port, sock;
      char buffer[HUGE_STRING_LEN];
      int nbytes, i, j;
      fd_set fds;
  
      void *sconf = r->server->module_config;
      proxy_server_conf *conf =
      (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
      struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts;
  
      memset(&server, '\0', sizeof(server));
      server.sin_family = AF_INET;
  
      /* Break the URL into host:port pairs */
  
      host = url;
      p = strchr(url, ':');
      if (p == NULL)
  	port = DEFAULT_HTTPS_PORT;
      else {
  	port = atoi(p + 1);
  	*p = '\0';
      }
  
  /* check if ProxyBlock directive on this host */
      destaddr.s_addr = ap_inet_addr(host);
      for (i = 0; i < conf->noproxies->nelts; i++) {
  	if ((npent[i].name != NULL && strstr(host, npent[i].name) != NULL)
  	    || destaddr.s_addr == npent[i].addr.s_addr || npent[i].name[0] == '*')
  	    return ap_proxyerror(r, HTTP_FORBIDDEN,
  				 "Connect to remote machine blocked");
      }
  
      /* Check if it is an allowed port */
      if (conf->allowed_connect_ports->nelts == 0) {
  	/* Default setting if not overridden by AllowCONNECT */
  	switch (port) {
  	    case DEFAULT_HTTPS_PORT:
  	    case DEFAULT_SNEWS_PORT:
  		break;
  	    default:
  		return HTTP_FORBIDDEN;
  	}
      } else if(!allowed_port(conf, port))
  	return HTTP_FORBIDDEN;
  
      if (proxyhost) {
  	Explain2("CONNECT to remote proxy %s on port %d", proxyhost, proxyport);
      }
      else {
  	Explain2("CONNECT to %s on port %d", host, port);
      }
  
      server.sin_port = (proxyport ? htons(proxyport) : htons(port));
      err = ap_proxy_host2addr(proxyhost ? proxyhost : host, &server_hp);
  
      if (err != NULL)
  	return ap_proxyerror(r,
  			     proxyhost ? HTTP_BAD_GATEWAY : HTTP_INTERNAL_SERVER_ERROR,
  			     err);
  
      sock = ap_psocket(r->pool, PF_INET, SOCK_STREAM, IPPROTO_TCP);
      if (sock == -1) {
  	ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  		    "proxy: error creating socket");
  	return HTTP_INTERNAL_SERVER_ERROR;
      }
  
  #ifdef CHECK_FD_SETSIZE
      if (sock >= FD_SETSIZE) {
  	ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_WARNING, NULL,
  	    "proxy_connect_handler: filedescriptor (%u) "
  	    "larger than FD_SETSIZE (%u) "
  	    "found, you probably need to rebuild Apache with a "
  	    "larger FD_SETSIZE", sock, FD_SETSIZE);
  	ap_pclosesocket(r->pool, sock);
  	return HTTP_INTERNAL_SERVER_ERROR;
      }
  #endif
  
      j = 0;
      while (server_hp.h_addr_list[j] != NULL) {
  	memcpy(&server.sin_addr, server_hp.h_addr_list[j],
  	       sizeof(struct in_addr));
  	i = ap_proxy_doconnect(sock, &server, r);
  	if (i == 0)
  	    break;
  	j++;
      }
      if (i == -1) {
  	ap_pclosesocket(r->pool, sock);
  	return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, ap_pstrcat(r->pool,
  					"Could not connect to remote machine:<br>",
  					strerror(errno), NULL));
      }
  
      /* If we are connecting through a remote proxy, we need to pass
       * the CONNECT request on to it.
       */
      if (proxyport) {
  	/* FIXME: We should not be calling write() directly, but we currently
  	 * have no alternative.  Error checking ignored.  Also, we force
  	 * a HTTP/1.0 request to keep things simple.
  	 */
  	Explain0("Sending the CONNECT request to the remote proxy");
  	ap_snprintf(buffer, sizeof(buffer), "CONNECT %s HTTP/1.0" CRLF,
  		    r->uri);
  	send(sock, buffer, strlen(buffer),0);
  	ap_snprintf(buffer, sizeof(buffer),
  		    "Proxy-agent: %s" CRLF CRLF, ap_get_server_version());
  	send(sock, buffer, strlen(buffer),0);
      }
      else {
  	Explain0("Returning 200 OK Status");
  	ap_rvputs(r, "HTTP/1.0 200 Connection established" CRLF, NULL);
  	ap_rvputs(r, "Proxy-agent: ", ap_get_server_version(), CRLF CRLF, NULL);
  	ap_bflush(r->connection->client);
      }
  
      while (1) {			/* Infinite loop until error (one side closes the connection) */
  	FD_ZERO(&fds);
  	FD_SET(sock, &fds);
  	FD_SET(ap_bfileno(r->connection->client, B_WR), &fds);
  
  	Explain0("Going to sleep (select)");
  	i = ap_select((ap_bfileno(r->connection->client, B_WR) > sock ?
  		       ap_bfileno(r->connection->client, B_WR) + 1 :
  		       sock + 1), &fds, NULL, NULL, NULL);
  	Explain1("Woke from select(), i=%d", i);
  
  	if (i) {
  	    if (FD_ISSET(sock, &fds)) {
  		Explain0("sock was set");
  		if ((nbytes = recv(sock, buffer, HUGE_STRING_LEN,0)) != 0) {
  		    if (nbytes == -1)
  			break;
  		    if (send(ap_bfileno(r->connection->client, B_WR), buffer, nbytes,0) == EOF)
  			break;
  		    Explain1("Wrote %d bytes to client", nbytes);
  		}
  		else
  		    break;
  	    }
  	    else if (FD_ISSET(ap_bfileno(r->connection->client, B_WR), &fds)) {
  		Explain0("client->fd was set");
  		if ((nbytes = recv(ap_bfileno(r->connection->client, B_WR), buffer,
  				   HUGE_STRING_LEN, 0)) != 0) {
  		    if (nbytes == -1)
  			break;
  		    if (send(sock, buffer, nbytes, 0) == EOF)
  			break;
  		    Explain1("Wrote %d bytes to server", nbytes);
  		}
  		else
  		    break;
  	    }
  	    else
  		break;		/* Must be done waiting */
  	}
  	else
  	    break;
      }
  
      ap_pclosesocket(r->pool, sock);
  
      return OK;
  }
  
  
  
  1.1                  httpd-proxy/module-1.0/proxy_ftp.c
  
  Index: proxy_ftp.c
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * Portions of this software are based upon public domain software
   * originally written at the National Center for Supercomputing Applications,
   * University of Illinois, Urbana-Champaign.
   */
  
  /* FTP routines for Apache proxy */
  
  #include "mod_proxy.h"
  #include "http_main.h"
  #include "http_log.h"
  #include "http_core.h"
  
  #define AUTODETECT_PWD
  
  DEF_Explain
  
  /*
   * Decodes a '%' escaped string, and returns the number of characters
   */
  static int decodeenc(char *x)
  {
      int i, j, ch;
  
      if (x[0] == '\0')
  	return 0;		/* special case for no characters */
      for (i = 0, j = 0; x[i] != '\0'; i++, j++) {
  /* decode it if not already done */
  	ch = x[i];
  	if (ch == '%' && ap_isxdigit(x[i + 1]) && ap_isxdigit(x[i + 2])) {
  	    ch = ap_proxy_hex2c(&x[i + 1]);
  	    i += 2;
  	}
  	x[j] = ch;
      }
      x[j] = '\0';
      return j;
  }
  
  /*
   * checks an encoded ftp string for bad characters, namely, CR, LF or
   * non-ascii character
   */
  static int ftp_check_string(const char *x)
  {
      int i, ch;
  
      for (i = 0; x[i] != '\0'; i++) {
  	ch = x[i];
  	if (ch == '%' && ap_isxdigit(x[i + 1]) && ap_isxdigit(x[i + 2])) {
  	    ch = ap_proxy_hex2c(&x[i + 1]);
  	    i += 2;
  	}
  	if (ch == CR || ch == LF || (OS_ASC(ch) & 0x80))
  	    return 0;
      }
      return 1;
  }
  
  /*
   * Canonicalise ftp URLs.
   */
  int ap_proxy_ftp_canon(request_rec *r, char *url)
  {
      char *user, *password, *host, *path, *parms, *strp, sport[7];
      pool *p = r->pool;
      const char *err;
      int port;
  
      port = DEFAULT_FTP_PORT;
      err = ap_proxy_canon_netloc(p, &url, &user, &password, &host, &port);
      if (err)
  	return HTTP_BAD_REQUEST;
      if (user != NULL && !ftp_check_string(user))
  	return HTTP_BAD_REQUEST;
      if (password != NULL && !ftp_check_string(password))
  	return HTTP_BAD_REQUEST;
  
  /* now parse path/parameters args, according to rfc1738 */
  /* N.B. if this isn't a true proxy request, then the URL path
   * (but not query args) has already been decoded.
   * This gives rise to the problem of a ; being decoded into the
   * path.
   */
      strp = strchr(url, ';');
      if (strp != NULL) {
  	*(strp++) = '\0';
  	parms = ap_proxy_canonenc(p, strp, strlen(strp), enc_parm,
  				  r->proxyreq);
  	if (parms == NULL)
  	    return HTTP_BAD_REQUEST;
      }
      else
  	parms = "";
  
      path = ap_proxy_canonenc(p, url, strlen(url), enc_path, r->proxyreq);
      if (path == NULL)
  	return HTTP_BAD_REQUEST;
      if (!ftp_check_string(path))
  	return HTTP_BAD_REQUEST;
  
      if (r->proxyreq == NOT_PROXY && r->args != NULL) {
  	if (strp != NULL) {
  	    strp = ap_proxy_canonenc(p, r->args, strlen(r->args), enc_parm, STD_PROXY);
  	    if (strp == NULL)
  		return HTTP_BAD_REQUEST;
  	    parms = ap_pstrcat(p, parms, "?", strp, NULL);
  	}
  	else {
  	    strp = ap_proxy_canonenc(p, r->args, strlen(r->args), enc_fpath, STD_PROXY);
  	    if (strp == NULL)
  		return HTTP_BAD_REQUEST;
  	    path = ap_pstrcat(p, path, "?", strp, NULL);
  	}
  	r->args = NULL;
      }
  
  /* now, rebuild URL */
  
      if (port != DEFAULT_FTP_PORT)
  	ap_snprintf(sport, sizeof(sport), ":%d", port);
      else
  	sport[0] = '\0';
  
      r->filename = ap_pstrcat(p, "proxy:ftp://", (user != NULL) ? user : "",
  			       (password != NULL) ? ":" : "",
  			       (password != NULL) ? password : "",
  		          (user != NULL) ? "@" : "", host, sport, "/", path,
  			       (parms[0] != '\0') ? ";" : "", parms, NULL);
  
      return OK;
  }
  
  /*
   * Returns the ftp status code;
   *  or -1 on I/O error, 0 on data error
   */
  static int ftp_getrc(BUFF *f)
  {
      int len, status;
      char linebuff[100], buff[5];
  
      len = ap_bgets(linebuff, sizeof linebuff, f);
      if (len == -1)
  	return -1;
  /* check format */
      if (len < 5 || !ap_isdigit(linebuff[0]) || !ap_isdigit(linebuff[1]) ||
  	!ap_isdigit(linebuff[2]) || (linebuff[3] != ' ' && linebuff[3] != '-'))
  	status = 0;
      else
  	status = 100 * linebuff[0] + 10 * linebuff[1] + linebuff[2] - 111 * '0';
  
      if (linebuff[len - 1] != '\n') {
  	(void)ap_bskiplf(f);
      }
  
  /* skip continuation lines */
      if (linebuff[3] == '-') {
  	memcpy(buff, linebuff, 3);
  	buff[3] = ' ';
  	do {
  	    len = ap_bgets(linebuff, sizeof linebuff, f);
  	    if (len == -1)
  		return -1;
  	    if (linebuff[len - 1] != '\n') {
  		(void)ap_bskiplf(f);
  	    }
  	} while (memcmp(linebuff, buff, 4) != 0);
      }
  
      return status;
  }
  
  /*
   * Like ftp_getrc but returns both the ftp status code and 
   * remembers the response message in the supplied buffer
   */
  static int ftp_getrc_msg(BUFF *f, char *msgbuf, int msglen)
  {
      int len, status;
      char linebuff[100], buff[5];
      char *mb = msgbuf,
  	 *me = &msgbuf[msglen];
  
      len = ap_bgets(linebuff, sizeof linebuff, f);
      if (len == -1)
  	return -1;
      if (len < 5 || !ap_isdigit(linebuff[0]) || !ap_isdigit(linebuff[1]) ||
  	!ap_isdigit(linebuff[2]) || (linebuff[3] != ' ' && linebuff[3] != '-'))
  	status = 0;
      else
  	status = 100 * linebuff[0] + 10 * linebuff[1] + linebuff[2] - 111 * '0';
  
      mb = ap_cpystrn(mb, linebuff+4, me - mb);
  
      if (linebuff[len - 1] != '\n')
  	(void)ap_bskiplf(f);
  
      if (linebuff[3] == '-') {
  	memcpy(buff, linebuff, 3);
  	buff[3] = ' ';
  	do {
  	    len = ap_bgets(linebuff, sizeof linebuff, f);
  	    if (len == -1)
  		return -1;
  	    if (linebuff[len - 1] != '\n') {
  		(void)ap_bskiplf(f);
  	    }
  	    mb = ap_cpystrn(mb, linebuff+4, me - mb);
  	} while (memcmp(linebuff, buff, 4) != 0);
      }
      return status;
  }
  
  static long int send_dir(BUFF *f, request_rec *r, cache_req *c, char *cwd)
  {
      char buf[IOBUFSIZE];
      char buf2[IOBUFSIZE];
      char *filename;
      int searchidx = 0;
      char *searchptr = NULL;
      int firstfile = 1;
      unsigned long total_bytes_sent = 0;
      register int n, o, w;
      conn_rec *con = r->connection;
      char *dir, *path, *reldir, *site;
  
      /* Save "scheme://site" prefix without password */
      site = ap_unparse_uri_components(r->pool, &r->parsed_uri, UNP_OMITPASSWORD|UNP_OMITPATHINFO);
      /* ... and path without query args */
      path = ap_unparse_uri_components(r->pool, &r->parsed_uri, UNP_OMITSITEPART|UNP_OMITQUERY);
      (void)decodeenc(path);
  
      /* Copy path, strip (all except the last) trailing slashes */
      path = dir = ap_pstrcat(r->pool, path, "/", NULL);
      while ((n = strlen(path)) > 1 && path[n-1] == '/' && path[n-2] == '/')
  	path[n-1] = '\0';
  
      /* print "ftp://host/" */
      n = ap_snprintf(buf, sizeof(buf), DOCTYPE_HTML_3_2
  		"<HTML><HEAD><TITLE>%s%s</TITLE>\n"
  		"<BASE HREF=\"%s%s\"></HEAD>\n"
  		"<BODY><H2>Directory of "
  		"<A HREF=\"/\">%s</A>/",
  		site, path, site, path, site);
      total_bytes_sent += ap_proxy_bputs2(buf, con->client, c);
  
      while ((dir = strchr(dir+1, '/')) != NULL)
      {
  	*dir = '\0';
  	if ((reldir = strrchr(path+1, '/'))==NULL)
  	    reldir = path+1;
  	else
  	    ++reldir;
  	/* print "path/" component */
  	ap_snprintf(buf, sizeof(buf), "<A HREF=\"/%s/\">%s</A>/", path+1, reldir);
  	total_bytes_sent += ap_proxy_bputs2(buf, con->client, c);
  	*dir = '/';
      }
      /* If the caller has determined the current directory, and it differs */
      /* from what the client requested, then show the real name */
      if (cwd == NULL || strncmp (cwd, path, strlen(cwd)) == 0) {
  	ap_snprintf(buf, sizeof(buf), "</H2>\n<HR><PRE>");
      } else {
  	ap_snprintf(buf, sizeof(buf), "</H2>\n(%s)\n<HR><PRE>", cwd);
      }
      total_bytes_sent += ap_proxy_bputs2(buf, con->client, c);
  
      while (!con->aborted) {
  	n = ap_bgets(buf, sizeof buf, f);
  	if (n == -1) {		/* input error */
  	    if (c != NULL) {
  		ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
  		    "proxy: error reading from %s", c->url);
  		c = ap_proxy_cache_error(c);
  	    }
  	    break;
  	}
  	if (n == 0)
  	    break;		/* EOF */
  	if (buf[0] == 'l' && (filename=strstr(buf, " -> ")) != NULL) {
  	    char *link_ptr = filename;
  
  	    do {
  		filename--;
  	    } while (filename[0] != ' ');
  	    *(filename++) = '\0';
  	    *(link_ptr++) = '\0';
  	    if ((n = strlen(link_ptr)) > 1 && link_ptr[n - 1] == '\n')
  	      link_ptr[n - 1] = '\0';
  	    ap_snprintf(buf2, sizeof(buf2), "%s <A HREF=\"%s\">%s %s</A>\n", buf, filename, filename, link_ptr);
  	    ap_cpystrn(buf, buf2, sizeof(buf));
  	    n = strlen(buf);
  	}
  	else if (buf[0] == 'd' || buf[0] == '-' || buf[0] == 'l' || ap_isdigit(buf[0])) {
  	    if (ap_isdigit(buf[0])) {	/* handle DOS dir */
  		searchptr = strchr(buf, '<');
  		if (searchptr != NULL)
  		    *searchptr = '[';
  		searchptr = strchr(buf, '>');
  		if (searchptr != NULL)
  		    *searchptr = ']';
  	    }
  
  	    filename = strrchr(buf, ' ');
  	    *(filename++) = 0;
  	    filename[strlen(filename) - 1] = 0;
  
  	    /* handle filenames with spaces in 'em */
  	    if (!strcmp(filename, ".") || !strcmp(filename, "..") || firstfile) {
  		firstfile = 0;
  		searchidx = filename - buf;
  	    }
  	    else if (searchidx != 0 && buf[searchidx] != 0) {
  		*(--filename) = ' ';
  		buf[searchidx - 1] = 0;
  		filename = &buf[searchidx];
  	    }
  
  	    /* Special handling for '.' and '..' */
  	    if (!strcmp(filename, ".") || !strcmp(filename, "..") || buf[0] == 'd') {
  		ap_snprintf(buf2, sizeof(buf2), "%s <A HREF=\"%s/\">%s</A>\n",
  		    buf, filename, filename);
  	    }
  	    else {
  		ap_snprintf(buf2, sizeof(buf2), "%s <A HREF=\"%s\">%s</A>\n", buf, filename, filename);
  	    }
  	    ap_cpystrn(buf, buf2, sizeof(buf));
  	    n = strlen(buf);
  	}
  
  	o = 0;
  	total_bytes_sent += n;
  
  	if (c != NULL && c->fp && ap_bwrite(c->fp, buf, n) != n) {
  	    ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
  		"proxy: error writing to %s", c->tempfile);
  	    c = ap_proxy_cache_error(c);
  	}
  
  	while (n && !r->connection->aborted) {
  	    w = ap_bwrite(con->client, &buf[o], n);
  	    if (w <= 0)
  		break;
  	    ap_reset_timeout(r);	/* reset timeout after successfule write */
  	    n -= w;
  	    o += w;
  	}
      }
  
      total_bytes_sent += ap_proxy_bputs2("</PRE><HR>\n", con->client, c);
      total_bytes_sent += ap_proxy_bputs2(ap_psignature("", r), con->client, c);
      total_bytes_sent += ap_proxy_bputs2("</BODY></HTML>\n", con->client, c);
  
      ap_bflush(con->client);
  
      return total_bytes_sent;
  }
  
  /* Common routine for failed authorization (i.e., missing or wrong password)
   * to an ftp service. This causes most browsers to retry the request
   * with username and password (which was presumably queried from the user)
   * supplied in the Authorization: header.
   * Note that we "invent" a realm name which consists of the
   * ftp://user@host part of the reqest (sans password -if supplied but invalid-)
   */
  static int ftp_unauthorized (request_rec *r, int log_it)
  {
      r->proxyreq = NOT_PROXY;
      /* Log failed requests if they supplied a password
       * (log username/password guessing attempts)
       */
      if (log_it)
  	ap_log_rerror(APLOG_MARK, APLOG_INFO|APLOG_NOERRNO, r,
  		      "proxy: missing or failed auth to %s",
  		      ap_unparse_uri_components(r->pool,
  		      &r->parsed_uri, UNP_OMITPATHINFO));
  
      ap_table_setn(r->err_headers_out, "WWW-Authenticate",
                    ap_pstrcat(r->pool, "Basic realm=\"",
  		  ap_unparse_uri_components(r->pool, &r->parsed_uri,
  					    UNP_OMITPASSWORD|UNP_OMITPATHINFO),
  		  "\"", NULL));
  
      return HTTP_UNAUTHORIZED;
  }
  
  /*
   * Handles direct access of ftp:// URLs
   * Original (Non-PASV) version from
   * Troy Morrison <spiffnet@zoom.com>
   * PASV added by Chuck
   */
  int ap_proxy_ftp_handler(request_rec *r, cache_req *c, char *url)
  {
      char *host, *path, *strp, *parms;
      char *cwd = NULL;
      char *user = NULL;
  /*    char *account = NULL; how to supply an account in a URL? */
      const char *password = NULL;
      const char *err;
      int port, i, j, len, sock, dsock, rc, nocache = 0;
      int csd = 0;
      struct sockaddr_in server;
      struct hostent server_hp;
      struct in_addr destaddr;
      table *resp_hdrs;
      BUFF *f;
      BUFF *data = NULL;
      pool *p = r->pool;
      int one = 1;
      const long int zero = 0L;
      NET_SIZE_T clen;
      struct tbl_do_args tdo;
  
      void *sconf = r->server->module_config;
      proxy_server_conf *conf =
      (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
      struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts;
      struct nocache_entry *ncent = (struct nocache_entry *) conf->nocaches->elts;
  
  /* stuff for PASV mode */
      unsigned int presult, h0, h1, h2, h3, p0, p1;
      unsigned int paddr;
      unsigned short pport;
      struct sockaddr_in data_addr;
      int pasvmode = 0;
      char pasv[64];
      char *pstr;
  
  /* stuff for responses */
      char resp[MAX_STRING_LEN];
      char *size = NULL;
  
  /* we only support GET and HEAD */
  
      if (r->method_number != M_GET)
  	return HTTP_NOT_IMPLEMENTED;
  
  /* We break the URL into host, port, path-search */
  
      host = r->parsed_uri.hostname;
      port = (r->parsed_uri.port != 0)
  	    ? r->parsed_uri.port
  	    : ap_default_port_for_request(r);
      path = ap_pstrdup(p, r->parsed_uri.path);
      path = (path != NULL && path[0] != '\0') ? &path[1] : "";
  
      /* The "Authorization:" header must be checked first.
       * We allow the user to "override" the URL-coded user [ & password ]
       * in the Browsers' User&Password Dialog.
       * NOTE that this is only marginally more secure than having the
       * password travel in plain as part of the URL, because Basic Auth
       * simply uuencodes the plain text password. 
       * But chances are still smaller that the URL is logged regularly.
       */
      if ((password = ap_table_get(r->headers_in, "Authorization")) != NULL
  	&& strcasecmp(ap_getword(r->pool, &password, ' '), "Basic") == 0
  	&& (password = ap_pbase64decode(r->pool, password))[0] != ':') {
  	/* Note that this allocation has to be made from r->connection->pool
  	 * because it has the lifetime of the connection.  The other allocations
  	 * are temporary and can be tossed away any time.
  	 */
  	user = ap_getword_nulls (r->connection->pool, &password, ':');
  	r->connection->ap_auth_type = "Basic";
  	r->connection->user = r->parsed_uri.user = user;
  	nocache = 1;	/* This resource only accessible with username/password */
      }
      else if ((user = r->parsed_uri.user) != NULL) {
  	user = ap_pstrdup(p, user);
  	decodeenc(user);
  	if ((password = r->parsed_uri.password) != NULL) {
  	    char *tmp = ap_pstrdup(p, password);
  	    decodeenc(tmp);
  	    password = tmp;
  	}
  	nocache = 1;	/* This resource only accessible with username/password */
      }
      else {
  	user = "anonymous";
  	password = "apache_proxy@";
      }
  
      /* check if ProxyBlock directive on this host */
      destaddr.s_addr = ap_inet_addr(host);
      for (i = 0; i < conf->noproxies->nelts; i++) {
          if (destaddr.s_addr == npent[i].addr.s_addr ||
              (npent[i].name != NULL &&
                (npent[i].name[0] == '*' || strstr(host, npent[i].name) != NULL)))
              return ap_proxyerror(r, HTTP_FORBIDDEN,
                                   "Connect to remote machine blocked");
      }
  
      Explain2("FTP: connect to %s:%d", host, port);
  
      parms = strchr(path, ';');
      if (parms != NULL)
  	*(parms++) = '\0';
  
      memset(&server, 0, sizeof(struct sockaddr_in));
      server.sin_family = AF_INET;
      server.sin_port = htons(port);
      err = ap_proxy_host2addr(host, &server_hp);
      if (err != NULL)
  	return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, err);
  
      sock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP);
      if (sock == -1) {
  	ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  		     "proxy: error creating socket");
  	return HTTP_INTERNAL_SERVER_ERROR;
      }
  
  #if !defined(TPF) && !defined(BEOS)
      if (conf->recv_buffer_size > 0
  	&& setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
  		       (const char *) &conf->recv_buffer_size, sizeof(int))
  	    == -1) {
  	    ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  			 "setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default");
      }
  #endif
  
      if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *) &one,
  		   sizeof(one)) == -1) {
  #ifndef _OSD_POSIX /* BS2000 has this option "always on" */
  	ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  		     "proxy: error setting reuseaddr option: setsockopt(SO_REUSEADDR)");
  	ap_pclosesocket(p, sock);
  	return HTTP_INTERNAL_SERVER_ERROR;
  #endif /*_OSD_POSIX*/
      }
  
  #ifdef SINIX_D_RESOLVER_BUG
      {
  	struct in_addr *ip_addr = (struct in_addr *) *server_hp.h_addr_list;
  
  	for (; ip_addr->s_addr != 0; ++ip_addr) {
  	    memcpy(&server.sin_addr, ip_addr, sizeof(struct in_addr));
  	    i = ap_proxy_doconnect(sock, &server, r);
  	    if (i == 0)
  		break;
  	}
      }
  #else
      j = 0;
      while (server_hp.h_addr_list[j] != NULL) {
  	memcpy(&server.sin_addr, server_hp.h_addr_list[j],
  	       sizeof(struct in_addr));
  	i = ap_proxy_doconnect(sock, &server, r);
  	if (i == 0)
  	    break;
  	j++;
      }
  #endif
      if (i == -1) {
  	ap_pclosesocket(p, sock);
  	return ap_proxyerror(r, HTTP_BAD_GATEWAY, ap_pstrcat(r->pool,
  				"Could not connect to remote machine: ",
  				strerror(errno), NULL));
      }
  
      /* record request_time for HTTP/1.1 age calculation */
      c->req_time = time(NULL);
  
      f = ap_bcreate(p, B_RDWR | B_SOCKET);
      ap_bpushfd(f, sock, sock);
  /* shouldn't we implement telnet control options here? */
  
  #ifdef CHARSET_EBCDIC
      ap_bsetflag(f, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 1);
  #endif /*CHARSET_EBCDIC*/
  
      /* possible results: */
      /* 120 Service ready in nnn minutes. */
      /* 220 Service ready for new user. */
      /* 421 Service not available, closing control connection. */
      ap_hard_timeout("proxy ftp", r);
      i = ftp_getrc_msg(f, resp, sizeof resp);
      Explain1("FTP: returned status %d", i);
      if (i == -1) {
  	ap_kill_timeout(r);
  	return ap_proxyerror(r, HTTP_BAD_GATEWAY,
  			     "Error reading from remote server");
      }
  #if 0
      if (i == 120) {
  	ap_kill_timeout(r);
  	/* RFC2068 states:
  	 * 14.38 Retry-After
  	 * 
  	 *  The Retry-After response-header field can be used with a 503 (Service
  	 *  Unavailable) response to indicate how long the service is expected to
  	 *  be unavailable to the requesting client. The value of this field can
  	 *  be either an HTTP-date or an integer number of seconds (in decimal)
  	 *  after the time of the response.
  	 *     Retry-After  = "Retry-After" ":" ( HTTP-date | delta-seconds )
  	 */
  	ap_set_header("Retry-After", ap_psprintf(p, "%u", 60*wait_mins);
  	return ap_proxyerror(r, HTTP_SERVICE_UNAVAILABLE, resp);
      }
  #endif
      if (i != 220) {
  	ap_kill_timeout(r);
  	return ap_proxyerror(r, HTTP_BAD_GATEWAY, resp);
      }
  
      Explain0("FTP: connected.");
  
      ap_bvputs(f, "USER ", user, CRLF, NULL);
      ap_bflush(f);			/* capture any errors */
      Explain1("FTP: USER %s", user);
  
      /* possible results; 230, 331, 332, 421, 500, 501, 530 */
      /* states: 1 - error, 2 - success; 3 - send password, 4,5 fail */
      /* 230 User logged in, proceed. */
      /* 331 User name okay, need password. */
      /* 332 Need account for login. */
      /* 421 Service not available, closing control connection. */
      /* 500 Syntax error, command unrecognized. */
      /*     (This may include errors such as command line too long.) */
      /* 501 Syntax error in parameters or arguments. */
      /* 530 Not logged in. */
      i = ftp_getrc(f);
      Explain1("FTP: returned status %d", i);
      if (i == -1) {
  	ap_kill_timeout(r);
  	return ap_proxyerror(r, HTTP_BAD_GATEWAY,
  			     "Error reading from remote server");
      }
      if (i == 530) {
  	ap_kill_timeout(r);
  	return ftp_unauthorized (r, 1);	/* log it: user name guessing attempt? */
      }
      if (i != 230 && i != 331) {
  	ap_kill_timeout(r);
  	return HTTP_BAD_GATEWAY;
      }
  
      if (i == 331) {		/* send password */
  	if (password == NULL) {
  	    return ftp_unauthorized (r, 0);
  	}
  	ap_bvputs(f, "PASS ", password, CRLF, NULL);
  	ap_bflush(f);
  	Explain1("FTP: PASS %s", password);
      /* possible results 202, 230, 332, 421, 500, 501, 503, 530 */
      /* 230 User logged in, proceed. */
      /* 332 Need account for login. */
      /* 421 Service not available, closing control connection. */
      /* 500 Syntax error, command unrecognized. */
      /* 501 Syntax error in parameters or arguments. */
      /* 503 Bad sequence of commands. */
      /* 530 Not logged in. */
  	i = ftp_getrc(f);
  	Explain1("FTP: returned status %d", i);
  	if (i == -1) {
  	    ap_kill_timeout(r);
  	    return ap_proxyerror(r, HTTP_BAD_GATEWAY,
  				 "Error reading from remote server");
  	}
  	if (i == 332) {
  	    ap_kill_timeout(r);
  	    return ap_proxyerror(r, HTTP_UNAUTHORIZED,
  				 "Need account for login");
  	}
  	/* @@@ questionable -- we might as well return a 403 Forbidden here */
  	if (i == 530) {
  	    ap_kill_timeout(r);
  	    return ftp_unauthorized (r, 1); /* log it: passwd guessing attempt? */
  	}
  	if (i != 230 && i != 202) {
  	    ap_kill_timeout(r);
  	    return HTTP_BAD_GATEWAY;
  	}
      }
  
  /* set the directory (walk directory component by component):
   * this is what we must do if we don't know the OS type of the remote
   * machine
   */
      for (;;) {
  	strp = strchr(path, '/');
  	if (strp == NULL)
  	    break;
  	*strp = '\0';
  
  	len = decodeenc(path);
  	ap_bvputs(f, "CWD ", path, CRLF, NULL);
  	ap_bflush(f);
  	Explain1("FTP: CWD %s", path);
  	*strp = '/';
  /* responses: 250, 421, 500, 501, 502, 530, 550 */
      /* 250 Requested file action okay, completed. */
      /* 421 Service not available, closing control connection. */
      /* 500 Syntax error, command unrecognized. */
      /* 501 Syntax error in parameters or arguments. */
      /* 502 Command not implemented. */
      /* 530 Not logged in. */
      /* 550 Requested action not taken. */
  	i = ftp_getrc(f);
  	Explain1("FTP: returned status %d", i);
  	if (i == -1) {
  	    ap_kill_timeout(r);
  	    return ap_proxyerror(r, HTTP_BAD_GATEWAY,
  				 "Error reading from remote server");
  	}
  	if (i == 550) {
  	    ap_kill_timeout(r);
  	    return HTTP_NOT_FOUND;
  	}
  	if (i != 250) {
  	    ap_kill_timeout(r);
  	    return HTTP_BAD_GATEWAY;
  	}
  
  	path = strp + 1;
      }
  
      if (parms != NULL && strncmp(parms, "type=", 5) == 0) {
  	parms += 5;
  	if ((parms[0] != 'd' && parms[0] != 'a' && parms[0] != 'i') ||
  	    parms[1] != '\0')
  	    parms = "";
      }
      else
  	parms = "";
  
      /* changed to make binary transfers the default */
  
      if (parms[0] != 'a') {
  	/* set type to image */
  	/* TM - Added CRLF to the end of TYPE I, otherwise it hangs the
  	   connection */
  	ap_bputs("TYPE I" CRLF, f);
  	ap_bflush(f);
  	Explain0("FTP: TYPE I");
  /* responses: 200, 421, 500, 501, 504, 530 */
      /* 200 Command okay. */
      /* 421 Service not available, closing control connection. */
      /* 500 Syntax error, command unrecognized. */
      /* 501 Syntax error in parameters or arguments. */
      /* 504 Command not implemented for that parameter. */
      /* 530 Not logged in. */
  	i = ftp_getrc(f);
  	Explain1("FTP: returned status %d", i);
  	if (i == -1) {
  	    ap_kill_timeout(r);
  	    return ap_proxyerror(r, HTTP_BAD_GATEWAY,
  				 "Error reading from remote server");
  	}
  	if (i != 200 && i != 504) {
  	    ap_kill_timeout(r);
  	    return HTTP_BAD_GATEWAY;
  	}
  /* Allow not implemented */
  	if (i == 504)
  	    parms[0] = '\0';
      }
  
  /* try to set up PASV data connection first */
      dsock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP);
      if (dsock == -1) {
  	ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  		     "proxy: error creating PASV socket");
  	ap_bclose(f);
  	ap_kill_timeout(r);
  	return HTTP_INTERNAL_SERVER_ERROR;
      }
  
  #if !defined (TPF) && !defined(BEOS)
      if (conf->recv_buffer_size) {
  	if (setsockopt(dsock, SOL_SOCKET, SO_RCVBUF,
  	       (const char *) &conf->recv_buffer_size, sizeof(int)) == -1) {
  	    ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  			 "setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default");
  	}
      }
  #endif
  
      ap_bputs("PASV" CRLF, f);
      ap_bflush(f);
      Explain0("FTP: PASV command issued");
  /* possible results: 227, 421, 500, 501, 502, 530 */
      /* 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */
      /* 421 Service not available, closing control connection. */
      /* 500 Syntax error, command unrecognized. */
      /* 501 Syntax error in parameters or arguments. */
      /* 502 Command not implemented. */
      /* 530 Not logged in. */
      i = ap_bgets(pasv, sizeof(pasv), f);
      if (i == -1) {
  	ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r,
  		     "PASV: control connection is toast");
  	ap_pclosesocket(p, dsock);
  	ap_bclose(f);
  	ap_kill_timeout(r);
  	return HTTP_INTERNAL_SERVER_ERROR;
      }
      else {
  	pasv[i - 1] = '\0';
  	pstr = strtok(pasv, " ");	/* separate result code */
  	if (pstr != NULL) {
  	    presult = atoi(pstr);
  	    if (*(pstr + strlen(pstr) + 1) == '=')
  	        pstr += strlen(pstr) + 2;
  	    else
  	    {
  	        pstr = strtok(NULL, "(");  /* separate address & port params */
  		if (pstr != NULL)
  		    pstr = strtok(NULL, ")");
  	    }
  	}
  	else
  	    presult = atoi(pasv);
  
  	Explain1("FTP: returned status %d", presult);
  
  	if (presult == 227 && pstr != NULL && (sscanf(pstr,
  		 "%d,%d,%d,%d,%d,%d", &h3, &h2, &h1, &h0, &p1, &p0) == 6)) {
  	    /* pardon the parens, but it makes gcc happy */
  	    paddr = (((((h3 << 8) + h2) << 8) + h1) << 8) + h0;
  	    pport = (p1 << 8) + p0;
  	    Explain5("FTP: contacting host %d.%d.%d.%d:%d",
  		     h3, h2, h1, h0, pport);
  	    data_addr.sin_family = AF_INET;
  	    data_addr.sin_addr.s_addr = htonl(paddr);
  	    data_addr.sin_port = htons(pport);
  	    i = ap_proxy_doconnect(dsock, &data_addr, r);
  
  	    if (i == -1) {
  		ap_kill_timeout(r);
  		return ap_proxyerror(r, HTTP_BAD_GATEWAY,
  				     ap_pstrcat(r->pool,
  						"Could not connect to remote machine: ",
  						strerror(errno), NULL));
  	    }
  	    else {
  		pasvmode = 1;
  	    }
  	}
  	else
  	    ap_pclosesocket(p, dsock);	/* and try the regular way */
      }
  
      if (!pasvmode) {		/* set up data connection */
  	clen = sizeof(struct sockaddr_in);
  	if (getsockname(sock, (struct sockaddr *) &server, &clen) < 0) {
  	    ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  			 "proxy: error getting socket address");
  	    ap_bclose(f);
  	    ap_kill_timeout(r);
  	    return HTTP_INTERNAL_SERVER_ERROR;
  	}
  
  	dsock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP);
  	if (dsock == -1) {
  	    ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  			 "proxy: error creating socket");
  	    ap_bclose(f);
  	    ap_kill_timeout(r);
  	    return HTTP_INTERNAL_SERVER_ERROR;
  	}
  
  	if (setsockopt(dsock, SOL_SOCKET, SO_REUSEADDR, (void *) &one,
  		       sizeof(one)) == -1) {
  #ifndef _OSD_POSIX /* BS2000 has this option "always on" */
  	    ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  			 "proxy: error setting reuseaddr option");
  	    ap_pclosesocket(p, dsock);
  	    ap_bclose(f);
  	    ap_kill_timeout(r);
  	    return HTTP_INTERNAL_SERVER_ERROR;
  #endif /*_OSD_POSIX*/
  	}
  
  	if (bind(dsock, (struct sockaddr *) &server,
  		 sizeof(struct sockaddr_in)) == -1) {
  	    char buff[22];
  
  	    ap_snprintf(buff, sizeof(buff), "%s:%d", inet_ntoa(server.sin_addr), server.sin_port);
  	    ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  			 "proxy: error binding to ftp data socket %s", buff);
  	    ap_bclose(f);
  	    ap_pclosesocket(p, dsock);
  	    return HTTP_INTERNAL_SERVER_ERROR;
  	}
  	listen(dsock, 2);	/* only need a short queue */
      }
  
  /* set request; "path" holds last path component */
      len = decodeenc(path);
  
      /* TM - if len == 0 then it must be a directory (you can't RETR nothing) */
  
      if (len == 0) {
  	parms = "d";
      }
      else {
  	ap_bvputs(f, "SIZE ", path, CRLF, NULL);
  	ap_bflush(f);
  	Explain1("FTP: SIZE %s", path);
  	i = ftp_getrc_msg(f, resp, sizeof resp);
  	Explain2("FTP: returned status %d with response %s", i, resp);
  	if (i != 500) {		/* Size command not recognized */
  	    if (i == 550) {	/* Not a regular file */
  		Explain0("FTP: SIZE shows this is a directory");
  		parms = "d";
  		ap_bvputs(f, "CWD ", path, CRLF, NULL);
  		ap_bflush(f);
  		Explain1("FTP: CWD %s", path);
  		i = ftp_getrc(f);
  		/* possible results: 250, 421, 500, 501, 502, 530, 550 */
  		/* 250 Requested file action okay, completed. */
  		/* 421 Service not available, closing control connection. */
  		/* 500 Syntax error, command unrecognized. */
  		/* 501 Syntax error in parameters or arguments. */
  		/* 502 Command not implemented. */
  		/* 530 Not logged in. */
  		/* 550 Requested action not taken. */
  		Explain1("FTP: returned status %d", i);
  		if (i == -1) {
  		    ap_kill_timeout(r);
  		    return ap_proxyerror(r, HTTP_BAD_GATEWAY,
  					 "Error reading from remote server");
  		}
  		if (i == 550) {
  		    ap_kill_timeout(r);
  		    return HTTP_NOT_FOUND;
  		}
  		if (i != 250) {
  		    ap_kill_timeout(r);
  		    return HTTP_BAD_GATEWAY;
  		}
  		path = "";
  		len = 0;
  	    }
  	    else if (i == 213) { /* Size command ok */
  		for (j = 0; j < sizeof resp && ap_isdigit(resp[j]); j++)
  			;
  		resp[j] = '\0';
  		if (resp[0] != '\0')
  		    size = ap_pstrdup(p, resp);
  	    }
  	}
      }
  
  #ifdef AUTODETECT_PWD
      ap_bvputs(f, "PWD", CRLF, NULL);
      ap_bflush(f);
      Explain0("FTP: PWD");
  /* responses: 257, 500, 501, 502, 421, 550 */
      /* 257 "<directory-name>" <commentary> */
      /* 421 Service not available, closing control connection. */
      /* 500 Syntax error, command unrecognized. */
      /* 501 Syntax error in parameters or arguments. */
      /* 502 Command not implemented. */
      /* 550 Requested action not taken. */
      i = ftp_getrc_msg(f, resp, sizeof resp);
      Explain1("FTP: PWD returned status %d", i);
      if (i == -1 || i == 421) {
  	ap_kill_timeout(r);
  	return ap_proxyerror(r, HTTP_BAD_GATEWAY,
  			     "Error reading from remote server");
      }
      if (i == 550) {
  	ap_kill_timeout(r);
  	return HTTP_NOT_FOUND;
      }
      if (i == 257) {
  	const char *dirp = resp;
  	cwd = ap_getword_conf(r->pool, &dirp);
      }
  #endif /*AUTODETECT_PWD*/
  
      if (parms[0] == 'd') {
  	if (len != 0)
  	    ap_bvputs(f, "LIST ", path, CRLF, NULL);
  	else
  	    ap_bputs("LIST -lag" CRLF, f);
  	Explain1("FTP: LIST %s", (len == 0 ? "" : path));
      }
      else {
  	ap_bvputs(f, "RETR ", path, CRLF, NULL);
  	Explain1("FTP: RETR %s", path);
      }
      ap_bflush(f);
  /* RETR: 110, 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 530, 550
     NLST: 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 502, 530 */
      /* 110 Restart marker reply. */
      /* 125 Data connection already open; transfer starting. */
      /* 150 File status okay; about to open data connection. */
      /* 226 Closing data connection. */
      /* 250 Requested file action okay, completed. */
      /* 421 Service not available, closing control connection. */
      /* 425 Can't open data connection. */
      /* 426 Connection closed; transfer aborted. */
      /* 450 Requested file action not taken. */
      /* 451 Requested action aborted. Local error in processing. */
      /* 500 Syntax error, command unrecognized. */
      /* 501 Syntax error in parameters or arguments. */
      /* 530 Not logged in. */
      /* 550 Requested action not taken. */
      rc = ftp_getrc(f);
      Explain1("FTP: returned status %d", rc);
      if (rc == -1) {
  	ap_kill_timeout(r);
  	return ap_proxyerror(r, HTTP_BAD_GATEWAY,
  			     "Error reading from remote server");
      }
      if (rc == 550) {
  	Explain0("FTP: RETR failed, trying LIST instead");
  	parms = "d";
  	ap_bvputs(f, "CWD ", path, CRLF, NULL);
  	ap_bflush(f);
  	Explain1("FTP: CWD %s", path);
  	/* possible results: 250, 421, 500, 501, 502, 530, 550 */
  	/* 250 Requested file action okay, completed. */
  	/* 421 Service not available, closing control connection. */
  	/* 500 Syntax error, command unrecognized. */
  	/* 501 Syntax error in parameters or arguments. */
  	/* 502 Command not implemented. */
  	/* 530 Not logged in. */
  	/* 550 Requested action not taken. */
  	rc = ftp_getrc(f);
  	Explain1("FTP: returned status %d", rc);
  	if (rc == -1) {
  	    ap_kill_timeout(r);
  	    return ap_proxyerror(r, HTTP_BAD_GATEWAY,
  				 "Error reading from remote server");
  	}
  	if (rc == 550) {
  	    ap_kill_timeout(r);
  	    return HTTP_NOT_FOUND;
  	}
  	if (rc != 250) {
  	    ap_kill_timeout(r);
  	    return HTTP_BAD_GATEWAY;
  	}
  
  #ifdef AUTODETECT_PWD
  	ap_bvputs(f, "PWD", CRLF, NULL);
  	ap_bflush(f);
  	Explain0("FTP: PWD");
  /* responses: 257, 500, 501, 502, 421, 550 */
  	/* 257 "<directory-name>" <commentary> */
  	/* 421 Service not available, closing control connection. */
  	/* 500 Syntax error, command unrecognized. */
  	/* 501 Syntax error in parameters or arguments. */
  	/* 502 Command not implemented. */
  	/* 550 Requested action not taken. */
  	i = ftp_getrc_msg(f, resp, sizeof resp);
  	Explain1("FTP: PWD returned status %d", i);
  	if (i == -1 || i == 421) {
  	    ap_kill_timeout(r);
  	    return ap_proxyerror(r, HTTP_BAD_GATEWAY,
  				 "Error reading from remote server");
  	}
  	if (i == 550) {
  	    ap_kill_timeout(r);
  	    return HTTP_NOT_FOUND;
  	}
  	if (i == 257) {
  	    const char *dirp = resp;
  	    cwd = ap_getword_conf(r->pool, &dirp);
  	}
  #endif /*AUTODETECT_PWD*/
  
  	ap_bputs("LIST -lag" CRLF, f);
  	ap_bflush(f);
  	Explain0("FTP: LIST -lag");
  	rc = ftp_getrc(f);
  	Explain1("FTP: returned status %d", rc);
  	if (rc == -1)
  	    return ap_proxyerror(r, HTTP_BAD_GATEWAY,
  				 "Error reading from remote server");
      }
      ap_kill_timeout(r);
      if (rc != 125 && rc != 150 && rc != 226 && rc != 250)
  	return HTTP_BAD_GATEWAY;
  
      r->status = HTTP_OK;
      r->status_line = "200 OK";
  
      resp_hdrs = ap_make_table(p, 2);
      c->hdrs = resp_hdrs;
  
      ap_table_setn(resp_hdrs, "Date", ap_gm_timestr_822(r->pool, r->request_time));
      ap_table_setn(resp_hdrs, "Server", ap_get_server_version());
  
      if (parms[0] == 'd')
  	ap_table_setn(resp_hdrs, "Content-Type", "text/html");
      else {
  	if (r->content_type != NULL) {
  	    ap_table_setn(resp_hdrs, "Content-Type", r->content_type);
  	    Explain1("FTP: Content-Type set to %s", r->content_type);
  	}
  	else {
  	    ap_table_setn(resp_hdrs, "Content-Type", ap_default_type(r));
  	}
  	if (parms[0] != 'a' && size != NULL) {
  	    /* We "trust" the ftp server to really serve (size) bytes... */
  	    ap_table_set(resp_hdrs, "Content-Length", size);
  	    Explain1("FTP: Content-Length set to %s", size);
  	}
      }
      if (r->content_encoding != NULL && r->content_encoding[0] != '\0') {
  	Explain1("FTP: Content-Encoding set to %s", r->content_encoding);
  	ap_table_setn(resp_hdrs, "Content-Encoding", r->content_encoding);
      }
  
  /* check if NoCache directive on this host */
      if (nocache == 0) {
  	for (i = 0; i < conf->nocaches->nelts; i++) {
  	    if (destaddr.s_addr == ncent[i].addr.s_addr ||
  	        (ncent[i].name != NULL &&
  		  (ncent[i].name[0] == '*' ||
  		   strstr(host, ncent[i].name) != NULL))) {
  	       nocache = 1;
  	       break;
  	    }
  	}
      }
  
      i = ap_proxy_cache_update(c, resp_hdrs, 0, nocache);
  
      if (i != DECLINED) {
  	ap_pclosesocket(p, dsock);
  	ap_bclose(f);
  	return i;
      }
  
      if (!pasvmode) {		/* wait for connection */
  	ap_hard_timeout("proxy ftp data connect", r);
  	clen = sizeof(struct sockaddr_in);
  	do
  	    csd = accept(dsock, (struct sockaddr *) &server, &clen);
  	while (csd == -1 && errno == EINTR);
  	if (csd == -1) {
  	    ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  			 "proxy: failed to accept data connection");
  	    ap_pclosesocket(p, dsock);
  	    ap_bclose(f);
  	    ap_kill_timeout(r);
  	    if (c != NULL)
  		c = ap_proxy_cache_error(c);
  	    return HTTP_BAD_GATEWAY;
  	}
  	ap_note_cleanups_for_socket(p, csd);
  	data = ap_bcreate(p, B_RDWR | B_SOCKET);
  	ap_bpushfd(data, csd, -1);
  	ap_kill_timeout(r);
      }
      else {
  	data = ap_bcreate(p, B_RDWR | B_SOCKET);
  	ap_bpushfd(data, dsock, dsock);
      }
  
      ap_hard_timeout("proxy receive", r);
  
      /* send response */
      /* write status line */
      if (!r->assbackwards)
  	ap_rvputs(r, "HTTP/1.1 ", r->status_line, CRLF, NULL);
      if (c != NULL && c->fp != NULL
  	&& ap_bvputs(c->fp, "HTTP/1.1 ", r->status_line, CRLF, NULL) == -1) {
  	    ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
  		"proxy: error writing CRLF to %s", c->tempfile);
  	    c = ap_proxy_cache_error(c);
      }
  
      /* we don't yet support keepalives */
      ap_rputs("Connection: close" CRLF, r);
  
      /* send headers */
      tdo.req = r;
      tdo.cache = c;
      ap_table_do(ap_proxy_send_hdr_line, &tdo, resp_hdrs, NULL);
  
      if (!r->assbackwards)
  	ap_rputs(CRLF, r);
      if (c != NULL && c->fp != NULL && ap_bputs(CRLF, c->fp) == -1) {
  	ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
  	    "proxy: error writing CRLF to %s", c->tempfile);
  	c = ap_proxy_cache_error(c);
      }
  
      ap_bsetopt(r->connection->client, BO_BYTECT, &zero);
      r->sent_bodyct = 1;
  /* send body */
      if (!r->header_only) {
  	if (parms[0] != 'd') {
  /* we need to set this for ap_proxy_send_fb()... */
  	    if (c != NULL)
  		c->cache_completion = 0;
  	    ap_proxy_send_fb(data, r, c, -1, 0);
  	} else
  	    send_dir(data, r, c, cwd);
  
  	if (rc == 125 || rc == 150)
  	    rc = ftp_getrc(f);
  
  	/* XXX: we checked for 125||150||226||250 above. This is redundant. */
  	if (rc != 226 && rc != 250)
              /* XXX: we no longer log an "error writing to c->tempfile" - should we? */
  	    c = ap_proxy_cache_error(c);
      }
      else {
  /* abort the transfer */
  	ap_bputs("ABOR" CRLF, f);
  	ap_bflush(f);
  	if (!pasvmode)
  	    ap_bclose(data);
  	Explain0("FTP: ABOR");
  /* responses: 225, 226, 421, 500, 501, 502 */
      /* 225 Data connection open; no transfer in progress. */
      /* 226 Closing data connection. */
      /* 421 Service not available, closing control connection. */
      /* 500 Syntax error, command unrecognized. */
      /* 501 Syntax error in parameters or arguments. */
      /* 502 Command not implemented. */
  	i = ftp_getrc(f);
  	Explain1("FTP: returned status %d", i);
      }
  
      ap_kill_timeout(r);
      ap_proxy_cache_tidy(c);
  
  /* finish */
      ap_bputs("QUIT" CRLF, f);
      ap_bflush(f);
      Explain0("FTP: QUIT");
  /* responses: 221, 500 */
      /* 221 Service closing control connection. */
      /* 500 Syntax error, command unrecognized. */
      i = ftp_getrc(f);
      Explain1("FTP: QUIT: status %d", i);
  
      if (pasvmode)
  	ap_bclose(data);
      ap_bclose(f);
  
      ap_rflush(r);	/* flush before garbage collection */
  
      ap_proxy_garbage_coll(r);
  
      return OK;
  }
  
  
  
  1.1                  httpd-proxy/module-1.0/proxy_http.c
  
  Index: proxy_http.c
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * Portions of this software are based upon public domain software
   * originally written at the National Center for Supercomputing Applications,
   * University of Illinois, Urbana-Champaign.
   */
  
  /* HTTP routines for Apache proxy */
  
  #include "mod_proxy.h"
  #include "http_log.h"
  #include "http_main.h"
  #include "http_core.h"
  #include "util_date.h"
  
  /*
   * Canonicalise http-like URLs.
   *  scheme is the scheme for the URL
   *  url    is the URL starting with the first '/'
   *  def_port is the default port for this scheme.
   */
  int ap_proxy_http_canon(request_rec *r, char *url, const char *scheme, int def_port)
  {
      char *host, *path, *search, sport[7];
      const char *err;
      int port;
  
      /* do syntatic check.
       * We break the URL into host, port, path, search
       */
      port = def_port;
      err = ap_proxy_canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
      if (err)
  	return HTTP_BAD_REQUEST;
  
      /* now parse path/search args, according to rfc1738 */
      /* N.B. if this isn't a true proxy request, then the URL _path_
       * has already been decoded.  True proxy requests have r->uri
       * == r->unparsed_uri, and no others have that property.
       */
      if (r->uri == r->unparsed_uri) {
  	search = strchr(url, '?');
  	if (search != NULL)
  	    *(search++) = '\0';
      }
      else
  	search = r->args;
  
      /* process path */
      path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path,
  			     r->proxyreq);
      if (path == NULL)
  	return HTTP_BAD_REQUEST;
  
      if (port != def_port)
  	ap_snprintf(sport, sizeof(sport), ":%d", port);
      else
  	sport[0] = '\0';
  
      r->filename = ap_pstrcat(r->pool, "proxy:", scheme, "://", host, sport, "/",
  		   path, (search) ? "?" : "", (search) ? search : "", NULL);
      return OK;
  }
  
  /* handle the conversion of URLs in the ProxyPassReverse function */
  static const char *proxy_location_reverse_map(request_rec *r, const char *url)
  {
      void *sconf;
      proxy_server_conf *conf;
      struct proxy_alias *ent;
      int i, l1, l2;
      char *u;
  
      sconf = r->server->module_config;
      conf = (proxy_server_conf *)ap_get_module_config(sconf, &proxy_module);
      l1 = strlen(url);
      ent = (struct proxy_alias *)conf->raliases->elts;
      for (i = 0; i < conf->raliases->nelts; i++) {
          l2 = strlen(ent[i].real);
          if (l1 >= l2 && strncmp(ent[i].real, url, l2) == 0) {
              u = ap_pstrcat(r->pool, ent[i].fake, &url[l2], NULL);
              return ap_construct_url(r->pool, u, r);
          }
      }
      return url;
  }
  
  /*
   * This handles http:// URLs, and other URLs using a remote proxy over http
   * If proxyhost is NULL, then contact the server directly, otherwise
   * go via the proxy.
   * Note that if a proxy is used, then URLs other than http: can be accessed,
   * also, if we have trouble which is clearly specific to the proxy, then
   * we return DECLINED so that we can try another proxy. (Or the direct
   * route.)
   */
  int ap_proxy_http_handler(request_rec *r, cache_req *c, char *url,
  		       const char *proxyhost, int proxyport)
  {
      const char *strp;
      char *strp2;
      const char *err, *desthost;
      int i, j, sock, len, backasswards;
      array_header *reqhdrs_arr;
      table *resp_hdrs;
      table_entry *reqhdrs;
      struct sockaddr_in server;
      struct in_addr destaddr;
      struct hostent server_hp;
      BUFF *f;
      char buffer[HUGE_STRING_LEN];
      char portstr[32];
      pool *p = r->pool;
      const long int zero = 0L;
      int destport = 0;
      char *destportstr = NULL;
      const char *urlptr = NULL;
      const char *datestr, *urlstr;
      struct tbl_do_args tdo;
  
      void *sconf = r->server->module_config;
      proxy_server_conf *conf =
      (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
      struct noproxy_entry *npent = (struct noproxy_entry *) conf->noproxies->elts;
      struct nocache_entry *ncent = (struct nocache_entry *) conf->nocaches->elts;
      int nocache = 0;
      char *xcache = NULL;
  
      memset(&server, '\0', sizeof(server));
      server.sin_family = AF_INET;
  
      /* We break the URL into host, port, path-search */
  
      urlptr = strstr(url, "://");
      if (urlptr == NULL)
  	return HTTP_BAD_REQUEST;
      urlptr += 3;
      destport = DEFAULT_HTTP_PORT;
      strp = strchr(urlptr, '/');
      if (strp == NULL) {
  	desthost = ap_pstrdup(p, urlptr);
  	urlptr = "/";
      }
      else {
  	char *q = ap_palloc(p, strp - urlptr + 1);
  	memcpy(q, urlptr, strp - urlptr);
  	q[strp - urlptr] = '\0';
  	urlptr = strp;
  	desthost = q;
      }
  
      strp2 = strchr(desthost, ':');
      if (strp2 != NULL) {
  	*(strp2++) = '\0';
  	if (ap_isdigit(*strp2)) {
  	    destport = atoi(strp2);
  	    destportstr = strp2;
  	}
      }
  
      /* check if ProxyBlock directive on this host */
      destaddr.s_addr = ap_inet_addr(desthost);
      for (i = 0; i < conf->noproxies->nelts; i++) {
          if (destaddr.s_addr == npent[i].addr.s_addr ||
              (npent[i].name != NULL &&
                (npent[i].name[0] == '*' || strstr(desthost, npent[i].name) != NULL)))
              return ap_proxyerror(r, HTTP_FORBIDDEN,
                                   "Connect to remote machine blocked");
      }
  
      if (proxyhost != NULL) {
  	server.sin_port = htons(proxyport);
  	err = ap_proxy_host2addr(proxyhost, &server_hp);
  	if (err != NULL)
  	    return DECLINED;	/* try another */
      }
      else {
  	server.sin_port = htons(destport);
  	err = ap_proxy_host2addr(desthost, &server_hp);
  	if (err != NULL)
  	    return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, err);
      }
  
  
      /* we have worked out who exactly we are going to connect to, now
       * make that connection...
       */
      sock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP);
      if (sock == -1) {
  	ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  		    "proxy: error creating socket");
  	return HTTP_INTERNAL_SERVER_ERROR;
      }
  
  #if !defined(TPF) && !defined(BEOS)
      if (conf->recv_buffer_size) {
  	if (setsockopt(sock, SOL_SOCKET, SO_RCVBUF,
  		       (const char *) &conf->recv_buffer_size, sizeof(int))
  	    == -1) {
  	    ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  			 "setsockopt(SO_RCVBUF): Failed to set ProxyReceiveBufferSize, using default");
  	}
      }
  #endif
  
  #ifdef SINIX_D_RESOLVER_BUG
      {
  	struct in_addr *ip_addr = (struct in_addr *) *server_hp.h_addr_list;
  
  	for (; ip_addr->s_addr != 0; ++ip_addr) {
  	    memcpy(&server.sin_addr, ip_addr, sizeof(struct in_addr));
  	    i = ap_proxy_doconnect(sock, &server, r);
  	    if (i == 0)
  		break;
  	}
      }
  #else
      j = 0;
      while (server_hp.h_addr_list[j] != NULL) {
  	memcpy(&server.sin_addr, server_hp.h_addr_list[j],
  	       sizeof(struct in_addr));
  	i = ap_proxy_doconnect(sock, &server, r);
  	if (i == 0)
  	    break;
  	j++;
      }
  #endif
      if (i == -1) {
  	if (proxyhost != NULL)
  	    return DECLINED;	/* try again another way */
  	else
  	    return ap_proxyerror(r, HTTP_BAD_GATEWAY, ap_pstrcat(r->pool,
  				"Could not connect to remote machine: ",
  				strerror(errno), NULL));
      }
  
      /* record request_time for HTTP/1.1 age calculation */
      c->req_time = time(NULL);
  
      /* strip hop-by-hop headers from the request */
      ap_proxy_clear_connection(r->pool, r->headers_in);
  
  
      /* At this point, we start sending the HTTP/1.1 request to the
       * remote server (proxy or otherwise).
       */
      f = ap_bcreate(p, B_RDWR | B_SOCKET);
      ap_bpushfd(f, sock, sock);
  
      ap_hard_timeout("proxy send", r);
      ap_bvputs(f, r->method, " ", proxyhost ? url : urlptr, " HTTP/1.0" CRLF,
  	   NULL);
      if (destportstr != NULL && destport != DEFAULT_HTTP_PORT)
  	ap_bvputs(f, "Host: ", desthost, ":", destportstr, CRLF, NULL);
      else
  	ap_bvputs(f, "Host: ", desthost, CRLF, NULL);
  
      if (conf->viaopt == via_block) {
  	/* Block all outgoing Via: headers */
  	ap_table_unset(r->headers_in, "Via");
      } else if (conf->viaopt != via_off) {
  	/* Create a "Via:" request header entry and merge it */
  	i = ap_get_server_port(r);
  	if (ap_is_default_port(i,r)) {
  	    strcpy(portstr,"");
  	} else {
  	    ap_snprintf(portstr, sizeof portstr, ":%d", i);
  	}
  	/* Generate outgoing Via: header with/without server comment: */
  	ap_table_mergen(r->headers_in, "Via",
  		    (conf->viaopt == via_full)
  			? ap_psprintf(p, "%d.%d %s%s (%s)",
  				HTTP_VERSION_MAJOR(r->proto_num),
  				HTTP_VERSION_MINOR(r->proto_num),
  				ap_get_server_name(r), portstr,
  				SERVER_BASEVERSION)
  			: ap_psprintf(p, "%d.%d %s%s",
  				HTTP_VERSION_MAJOR(r->proto_num),
  				HTTP_VERSION_MINOR(r->proto_num),
  				ap_get_server_name(r), portstr)
  			);
      }
  
      /* we don't yet support keepalives - but we will soon, I promise! */
      ap_bvputs(f, "Connection: close", CRLF, NULL);
  
      reqhdrs_arr = ap_table_elts(r->headers_in);
      reqhdrs = (table_entry *) reqhdrs_arr->elts;
      for (i = 0; i < reqhdrs_arr->nelts; i++) {
  	if (reqhdrs[i].key == NULL || reqhdrs[i].val == NULL
  
  	/* Clear out hop-by-hop request headers not to send:
  	 * RFC2616 13.5.1 says we should strip these headers:
  	 */
  	    || !strcasecmp(reqhdrs[i].key, "Host")	/* Already sent */
  	    || !strcasecmp(reqhdrs[i].key, "Keep-Alive")
  	    || !strcasecmp(reqhdrs[i].key, "TE")
  	    || !strcasecmp(reqhdrs[i].key, "Trailer")
  	    || !strcasecmp(reqhdrs[i].key, "Transfer-Encoding")
  	    || !strcasecmp(reqhdrs[i].key, "Upgrade")
  
  	    /* XXX: @@@ FIXME: "Proxy-Authorization" should *only* be 
  	     * suppressed if THIS server requested the authentication,
  	     * not when a frontend proxy requested it!
  	     *
  	     * The solution to this problem is probably to strip out
  	     * the Proxy-Authorisation header in the authorisation
  	     * code itself, not here. This saves us having to signal
  	     * somehow whether this request was authenticated or not.
  	     */
  	    || !strcasecmp(reqhdrs[i].key, "Proxy-Authorization"))
  	    continue;
  	ap_bvputs(f, reqhdrs[i].key, ": ", reqhdrs[i].val, CRLF, NULL);
      }
  
      /* the obligatory empty line to mark the end of the headers */
      ap_bputs(CRLF, f);
  
      /* send the request data, if any. */
      if (ap_should_client_block(r)) {
  	while ((i = ap_get_client_block(r, buffer, sizeof buffer)) > 0)
  	    ap_bwrite(f, buffer, i);
      }
      ap_bflush(f);
      ap_kill_timeout(r);
  
  
      /* Right - now it's time to listen for a response.
       *
       */
      ap_hard_timeout("proxy receive", r);
  
      len = ap_bgets(buffer, sizeof buffer - 1, f);
      if (len == -1) {
  	ap_bclose(f);
  	ap_kill_timeout(r);
  	ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  		     "ap_bgets() - proxy receive - Error reading from remote server %s (length %d)",
  		     proxyhost ? proxyhost : desthost, len);
  	return ap_proxyerror(r, HTTP_BAD_GATEWAY,
  			     "Error reading from remote server");
      } else if (len == 0) {
  	ap_bclose(f);
  	ap_kill_timeout(r);
  	return ap_proxyerror(r, HTTP_BAD_GATEWAY,
  			     "Document contains no data");
      }
  
      /* Is it an HTTP/1 response?
       * Do some sanity checks on the response.
       * (This is buggy if we ever see an HTTP/1.10)
       */
      if (ap_checkmask(buffer, "HTTP/#.# ###*")) {
  	int major, minor;
  	if (2 != sscanf(buffer, "HTTP/%u.%u", &major, &minor)) {
              /* if no response, default to HTTP/1.1 - is this correct? */
  	    major = 1;
  	    minor = 1;
  	}
  
          /* If not an HTTP/1 message or if the status line was > 8192 bytes */
  	if (buffer[5] != '1' || buffer[len - 1] != '\n') {
  	    ap_bclose(f);
  	    ap_kill_timeout(r);
  	    return HTTP_BAD_GATEWAY;
  	}
  	backasswards = 0;
  	buffer[--len] = '\0';
  
  	buffer[12] = '\0';
  	r->status = atoi(&buffer[9]);
  	buffer[12] = ' ';
  	r->status_line = ap_pstrdup(p, &buffer[9]);
  
  	/* read the response headers. */
  	/* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers */
  	/* Also, take care with headers with multiple occurences. */
  
  	resp_hdrs = ap_proxy_read_headers(r, buffer, HUGE_STRING_LEN, f);
  	if (resp_hdrs == NULL) {
  	    ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, r->server,
  		 "proxy: Bad HTTP/%d.%d header returned by %s (%s)",
  		 major, minor, r->uri, r->method);
  	    resp_hdrs = ap_make_table(p, 20);
  	    nocache = 1;    /* do not cache this broken file */
  	}
  
  	/* handle Via header in the response */
  	if (conf->viaopt != via_off && conf->viaopt != via_block) {
  	    /* Create a "Via:" response header entry and merge it */
  	    i = ap_get_server_port(r);
  	    if (ap_is_default_port(i,r)) {
  		strcpy(portstr,"");
  	    } else {
  		ap_snprintf(portstr, sizeof portstr, ":%d", i);
  	    }
  	    ap_table_mergen((table *)resp_hdrs, "Via",
  			    (conf->viaopt == via_full)
  			    ? ap_psprintf(p, "%d.%d %s%s (%s)",
  				major, minor,
  				ap_get_server_name(r), portstr,
  				SERVER_BASEVERSION)
  			    : ap_psprintf(p, "%d.%d %s%s",
  				major, minor,
  				ap_get_server_name(r), portstr)
  			    );
  	}
  
  	/* strip hop-by-hop headers defined by Connection */
  	ap_proxy_clear_connection(p, resp_hdrs);
      }
      else {
          /* an http/0.9 response */
  	backasswards = 1;
  	r->status = 200;
  	r->status_line = "200 OK";
  
          /* no headers */
  	resp_hdrs = ap_make_table(p, 20);
      }
  
      ap_kill_timeout(r);
  
      /*
       * HTTP/1.1 requires us to accept 3 types of dates, but only generate
       * one type
       */
      if ((datestr = ap_table_get(resp_hdrs, "Date")) != NULL)
  	ap_table_set(resp_hdrs, "Date", ap_proxy_date_canon(p, datestr));
      if ((datestr = ap_table_get(resp_hdrs, "Last-Modified")) != NULL)
  	ap_table_set(resp_hdrs, "Last-Modified", ap_proxy_date_canon(p, datestr));
      if ((datestr = ap_table_get(resp_hdrs, "Expires")) != NULL)
  	ap_table_set(resp_hdrs, "Expires", ap_proxy_date_canon(p, datestr));
  
      /* handle the ProxyPassReverse mappings */
      if ((urlstr = ap_table_get(resp_hdrs, "Location")) != NULL)
        ap_table_set(resp_hdrs, "Location", proxy_location_reverse_map(r, urlstr));
      if ((urlstr = ap_table_get(resp_hdrs, "URI")) != NULL)
        ap_table_set(resp_hdrs, "URI", proxy_location_reverse_map(r, urlstr));
      if ((urlstr = ap_table_get(resp_hdrs, "Content-Location")) != NULL)
        ap_table_set(resp_hdrs, "Content-Location", proxy_location_reverse_map(r , urlstr));
  
  /* check if NoCache directive on this host */
      if (nocache == 0) {
  	for (i = 0; i < conf->nocaches->nelts; i++) {
  	    if (destaddr.s_addr == ncent[i].addr.s_addr ||
  	        (ncent[i].name != NULL &&
  		  (ncent[i].name[0] == '*' ||
  		   strstr(desthost, ncent[i].name) != NULL))) {
  	       nocache = 1;
  	       break;
  	    }
  	}
      }
  
      /* update the cache file, possibly even fulfilling the request if
       * it turns out a conditional allowed us to serve the object from the
       * cache...
       */
  
      i = ap_proxy_cache_update(c, resp_hdrs, !backasswards, nocache);
      if (i != DECLINED) {
  	ap_bclose(f);
  	return i;
      }
  
      ap_hard_timeout("proxy receive", r);
  
      /* write status line and headers */
      xcache = ap_pstrcat(r->pool, "MISS from ", ap_get_server_name(r), NULL);
      ap_proxy_send_headers(r, c, ap_pstrcat(p, "HTTP/1.1 ", r->status_line, NULL), xcache, resp_hdrs);
  
      ap_bsetopt(r->connection->client, BO_BYTECT, &zero);
      r->sent_bodyct = 1;
      /* Is it an HTTP/0.9 respose? If so, send the extra data */
      if (backasswards) {
  	ap_bwrite(r->connection->client, buffer, len);
  	if (c != NULL && c->fp != NULL && ap_bwrite(c->fp, buffer, len) != len) {
  	    ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
  		"proxy: error writing extra data to %s", c->tempfile);
  	    c = ap_proxy_cache_error(c);
  	}
      }
  
      ap_kill_timeout(r);
  
  #ifdef CHARSET_EBCDIC
      /* What we read/write after the header should not be modified
       * (i.e., the cache copy is ASCII, not EBCDIC, even for text/html)
       */
      ap_bsetflag(f, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 0);
      ap_bsetflag(r->connection->client, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 0);
  #endif
  
  /* send body */
  /* if header only, then cache will be NULL */
  /* HTTP/1.0 tells us to read to EOF, rather than content-length bytes */
  /* XXX CHANGEME: We want to eventually support keepalives, which means
   * we must read content-length bytes... */
      if (!r->header_only) {
  /* we need to set this for ap_proxy_send_fb()... */
  	c->cache_completion = conf->cache.cache_completion;
  
  /* XXX CHECKME: c->len should be the expected content length, or -1 if the
   * content length is not known. We need to make 100% sure c->len is always
   * set correctly before we get here to correctly do keepalive.
   */
  	ap_proxy_send_fb(f, r, c, c->len, 0);
      }
  
      ap_proxy_cache_tidy(c);
  
      ap_bclose(f);
  
      ap_proxy_garbage_coll(r);
      return OK;
  }
  
  
  
  1.1                  httpd-proxy/module-1.0/proxy_util.c
  
  Index: proxy_util.c
  ===================================================================
  /* ====================================================================
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2000 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution,
   *    if any, must include the following acknowledgment:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgment may appear in the software itself,
   *    if and wherever such third-party acknowledgments normally appear.
   *
   * 4. The names "Apache" and "Apache Software Foundation" must
   *    not be used to endorse or promote products derived from this
   *    software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache",
   *    nor may "Apache" appear in their name, without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   * Portions of this software are based upon public domain software
   * originally written at the National Center for Supercomputing Applications,
   * University of Illinois, Urbana-Champaign.
   */
  
  /* Utility routines for Apache proxy */
  #include "mod_proxy.h"
  #include "http_main.h"
  #include "ap_md5.h"
  #include "multithread.h"
  #include "http_log.h"
  #include "util_uri.h"
  #include "util_date.h"	/* get ap_checkmask() decl. */
  
  static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r);
  static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r);
  static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r);
  static int proxy_match_word(struct dirconn_entry *This, request_rec *r);
  static struct per_thread_data *get_per_thread_data(void);
  /* already called in the knowledge that the characters are hex digits */
  int ap_proxy_hex2c(const char *x)
  {
      int i, ch;
  
  #ifndef CHARSET_EBCDIC
      ch = x[0];
      if (ap_isdigit(ch))
  	i = ch - '0';
      else if (ap_isupper(ch))
  	i = ch - ('A' - 10);
      else
  	i = ch - ('a' - 10);
      i <<= 4;
  
      ch = x[1];
      if (ap_isdigit(ch))
  	i += ch - '0';
      else if (ap_isupper(ch))
  	i += ch - ('A' - 10);
      else
  	i += ch - ('a' - 10);
      return i;
  #else /*CHARSET_EBCDIC*/
      return (1 == sscanf(x, "%2x", &i)) ? os_toebcdic[i&0xFF] : 0;
  #endif /*CHARSET_EBCDIC*/
  }
  
  void ap_proxy_c2hex(int ch, char *x)
  {
  #ifndef CHARSET_EBCDIC
      int i;
  
      x[0] = '%';
      i = (ch & 0xF0) >> 4;
      if (i >= 10)
  	x[1] = ('A' - 10) + i;
      else
  	x[1] = '0' + i;
  
      i = ch & 0x0F;
      if (i >= 10)
  	x[2] = ('A' - 10) + i;
      else
  	x[2] = '0' + i;
  #else /*CHARSET_EBCDIC*/
      static const char ntoa[] = { "0123456789ABCDEF" };
      ch = os_toascii[ch & 0xFF];
      x[0] = '%';
      x[1] = ntoa[(ch>>4)&0x0F];
      x[2] = ntoa[ch&0x0F];
      x[3] = '\0';
  #endif /*CHARSET_EBCDIC*/
  }
  
  /*
   * canonicalise a URL-encoded string
   */
  
  /*
   * Convert a URL-encoded string to canonical form.
   * It decodes characters which need not be encoded,
   * and encodes those which must be encoded, and does not touch
   * those which must not be touched.
   */
  char *ap_proxy_canonenc(pool *p, const char *x, int len, enum enctype t,
  			enum proxyreqtype isenc)
  {
      int i, j, ch;
      char *y;
      const char *allowed;	/* characters which should not be encoded */
      const char *reserved;	/* characters which much not be en/de-coded */
  
  /* N.B. in addition to :@&=, this allows ';' in an http path
   * and '?' in an ftp path -- this may be revised
   * 
   * Also, it makes a '+' character in a search string reserved, as
   * it may be form-encoded. (Although RFC 1738 doesn't allow this -
   * it only permits ; / ? : @ = & as reserved chars.)
   */
      if (t == enc_path)
  	allowed = "$-_.+!*'(),;:@&=";
      else if (t == enc_search)
  	allowed = "$-_.!*'(),;:@&=";
      else if (t == enc_user)
  	allowed = "$-_.+!*'(),;@&=";
      else if (t == enc_fpath)
  	allowed = "$-_.+!*'(),?:@&=";
      else			/* if (t == enc_parm) */
  	allowed = "$-_.+!*'(),?/:@&=";
  
      if (t == enc_path)
  	reserved = "/";
      else if (t == enc_search)
  	reserved = "+";
      else
  	reserved = "";
  
      y = ap_palloc(p, 3 * len + 1);
  
      for (i = 0, j = 0; i < len; i++, j++) {
  /* always handle '/' first */
  	ch = x[i];
  	if (strchr(reserved, ch)) {
  	    y[j] = ch;
  	    continue;
  	}
  /* decode it if not already done */
  	if (isenc != NOT_PROXY && ch == '%') {
  	    if (!ap_isxdigit(x[i + 1]) || !ap_isxdigit(x[i + 2]))
  		return NULL;
  	    ch = ap_proxy_hex2c(&x[i + 1]);
  	    i += 2;
  	    if (ch != 0 && strchr(reserved, ch)) {	/* keep it encoded */
  		ap_proxy_c2hex(ch, &y[j]);
  		j += 2;
  		continue;
  	    }
  	}
  /* recode it, if necessary */
  	if (!ap_isalnum(ch) && !strchr(allowed, ch)) {
  	    ap_proxy_c2hex(ch, &y[j]);
  	    j += 2;
  	}
  	else
  	    y[j] = ch;
      }
      y[j] = '\0';
      return y;
  }
  
  /*
   * Parses network-location.
   *    urlp           on input the URL; on output the path, after the leading /
   *    user           NULL if no user/password permitted
   *    password       holder for password
   *    host           holder for host
   *    port           port number; only set if one is supplied.
   *
   * Returns an error string.
   */
  char *
       ap_proxy_canon_netloc(pool *p, char **const urlp, char **userp,
  			char **passwordp, char **hostp, int *port)
  {
      int i;
      char *strp, *host, *url = *urlp;
      char *user = NULL, *password = NULL;
  
      if (url[0] != '/' || url[1] != '/')
  	return "Malformed URL";
      host = url + 2;
      url = strchr(host, '/');
      if (url == NULL)
  	url = "";
      else
  	*(url++) = '\0';	/* skip seperating '/' */
  
      /* find _last_ '@' since it might occur in user/password part */
      strp = strrchr(host, '@');
  
      if (strp != NULL) {
  	*strp = '\0';
  	user = host;
  	host = strp + 1;
  
  /* find password */
  	strp = strchr(user, ':');
  	if (strp != NULL) {
  	    *strp = '\0';
  	    password = ap_proxy_canonenc(p, strp + 1, strlen(strp + 1), enc_user, STD_PROXY);
  	    if (password == NULL)
  		return "Bad %-escape in URL (password)";
  	}
  
  	user = ap_proxy_canonenc(p, user, strlen(user), enc_user, STD_PROXY);
  	if (user == NULL)
  	    return "Bad %-escape in URL (username)";
      }
      if (userp != NULL) {
  	*userp = user;
      }
      if (passwordp != NULL) {
  	*passwordp = password;
      }
  
      strp = strrchr(host, ':');
      if (strp != NULL) {
  	*(strp++) = '\0';
  
  	for (i = 0; strp[i] != '\0'; i++)
  	    if (!ap_isdigit(strp[i]))
  		break;
  
  	/* if (i == 0) the no port was given; keep default */
  	if (strp[i] != '\0') {
  	    return "Bad port number in URL";
  	} else if (i > 0) {
  	    *port = atoi(strp);
  	    if (*port > 65535)
  		return "Port number in URL > 65535";
  	}
      }
      ap_str_tolower(host);		/* DNS names are case-insensitive */
      if (*host == '\0')
  	return "Missing host in URL";
  /* check hostname syntax */
      for (i = 0; host[i] != '\0'; i++)
  	if (!ap_isdigit(host[i]) && host[i] != '.')
  	    break;
      /* must be an IP address */
  #if defined(WIN32) || defined(NETWARE) || defined(TPF) || defined(BEOS)
      if (host[i] == '\0' && (inet_addr(host) == -1))
  #else
      if (host[i] == '\0' && (ap_inet_addr(host) == -1 || inet_network(host) == -1))
  #endif
      {
  	return "Bad IP address in URL";
      }
  
  /*    if (strchr(host,'.') == NULL && domain != NULL)
     host = pstrcat(p, host, domain, NULL);
   */
      *urlp = url;
      *hostp = host;
  
      return NULL;
  }
  
  static const char * const lwday[7] =
  {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
  
  /*
   * If the date is a valid RFC 850 date or asctime() date, then it
   * is converted to the RFC 1123 format, otherwise it is not modified.
   * This routine is not very fast at doing conversions, as it uses
   * sscanf and sprintf. However, if the date is already correctly
   * formatted, then it exits very quickly.
   */
  const char *
       ap_proxy_date_canon(pool *p, const char *x)
  {
      int wk, mday, year, hour, min, sec, mon;
      char *q, month[4], zone[4], week[4];
  
      q = strchr(x, ',');
      /* check for RFC 850 date */
      if (q != NULL && q - x > 3 && q[1] == ' ') {
  	*q = '\0';
  	for (wk = 0; wk < 7; wk++)
  	    if (strcmp(x, lwday[wk]) == 0)
  		break;
  	*q = ',';
  	if (wk == 7)
  	    return x;		/* not a valid date */
  	if (q[4] != '-' || q[8] != '-' || q[11] != ' ' || q[14] != ':' ||
  	    q[17] != ':' || strcmp(&q[20], " GMT") != 0)
  	    return x;
  	if (sscanf(q + 2, "%u-%3s-%u %u:%u:%u %3s", &mday, month, &year,
  		   &hour, &min, &sec, zone) != 7)
  	    return x;
  	if (year < 70)
  	    year += 2000;
  	else
  	    year += 1900;
      }
      else {
  /* check for acstime() date */
  	if (x[3] != ' ' || x[7] != ' ' || x[10] != ' ' || x[13] != ':' ||
  	    x[16] != ':' || x[19] != ' ' || x[24] != '\0')
  	    return x;
  	if (sscanf(x, "%3s %3s %u %u:%u:%u %u", week, month, &mday, &hour,
  		   &min, &sec, &year) != 7)
  	    return x;
  	for (wk = 0; wk < 7; wk++)
  	    if (strcmp(week, ap_day_snames[wk]) == 0)
  		break;
  	if (wk == 7)
  	    return x;
      }
  
  /* check date */
      for (mon = 0; mon < 12; mon++)
  	if (strcmp(month, ap_month_snames[mon]) == 0)
  	    break;
      if (mon == 12)
  	return x;
  
      q = ap_palloc(p, 30);
      ap_snprintf(q, 30, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", ap_day_snames[wk], mday,
  		ap_month_snames[mon], year, hour, min, sec);
      return q;
  }
  
  
  /* NOTE: This routine is taken from http_protocol::getline()
   * because the old code found in the proxy module was too
   * difficult to understand and maintain.
   */
  /* Get a line of protocol input, including any continuation lines
   * caused by MIME folding (or broken clients) if fold != 0, and place it
   * in the buffer s, of size n bytes, without the ending newline.
   *
   * Returns -1 on error, or the length of s.
   *
   * Note: Because bgets uses 1 char for newline and 1 char for NUL,
   *       the most we can get is (n - 2) actual characters if it
   *       was ended by a newline, or (n - 1) characters if the line
   *       length exceeded (n - 1).  So, if the result == (n - 1),
   *       then the actual input line exceeded the buffer length,
   *       and it would be a good idea for the caller to puke 400 or 414.
   */
  static int proxy_getline(char *s, int n, BUFF *in, int fold)
  {
      char *pos, next;
      int retval;
      int total = 0;
  
      pos = s;
  
      do {
          retval = ap_bgets(pos, n, in);     /* retval == -1 if error, 0 if EOF */
  
          if (retval <= 0)
              return ((retval < 0) && (total == 0)) ? -1 : total;
  
          /* retval is the number of characters read, not including NUL      */
  
          n -= retval;            /* Keep track of how much of s is full     */
          pos += (retval - 1);    /* and where s ends                        */
          total += retval;        /* and how long s has become               */
  
          if (*pos == '\n') {     /* Did we get a full line of input?        */
              *pos = '\0';
              --total;
              ++n;
          }
          else
              return total;       /* if not, input line exceeded buffer size */
  
          /* Continue appending if line folding is desired and
           * the last line was not empty and we have room in the buffer and
           * the next line begins with a continuation character.
           */
      } while (fold && (retval != 1) && (n > 1)
                    && (ap_blookc(&next, in) == 1)
                    && ((next == ' ') || (next == '\t')));
  
      return total;
  }
  
  
  /*
   * Reads headers from a buffer and returns an array of headers.
   * Returns NULL on file error
   * This routine tries to deal with too long lines and continuation lines.
   * @@@: XXX: FIXME: currently the headers are passed thru un-merged. 
   * Is that okay, or should they be collapsed where possible?
   */
  table *ap_proxy_read_headers(request_rec *r, char *buffer, int size, BUFF *f)
  {
      table *resp_hdrs;
      int len;
      char *value, *end;
      char field[MAX_STRING_LEN];
  
      resp_hdrs = ap_make_table(r->pool, 20);
  
      /*
       * Read header lines until we get the empty separator line, a read error,
       * the connection closes (EOF), or we timeout.
       */
      while ((len = proxy_getline(buffer, size, f, 1)) > 0) {
  	
  	if (!(value = strchr(buffer, ':'))) {     /* Find the colon separator */
  
  	    /* Buggy MS IIS servers sometimes return invalid headers
  	     * (an extra "HTTP/1.0 200, OK" line sprinkled in between
  	     * the usual MIME headers). Try to deal with it in a sensible
  	     * way, but log the fact.
  	     * XXX: The mask check is buggy if we ever see an HTTP/1.10 */
  
  	    if (!ap_checkmask(buffer, "HTTP/#.# ###*")) {
  		/* Nope, it wasn't even an extra HTTP header. Give up. */
  		return NULL;
  	    }
  
  	    ap_log_error(APLOG_MARK, APLOG_WARNING|APLOG_NOERRNO, r->server,
  			 "proxy: Ignoring duplicate HTTP header "
  			 "returned by %s (%s)", r->uri, r->method);
  	    continue;
  	}
  
          *value = '\0';
          ++value;
  	/* XXX: RFC2068 defines only SP and HT as whitespace, this test is
  	 * wrong... and so are many others probably.
  	 */
          while (ap_isspace(*value))
              ++value;            /* Skip to start of value   */
  
  	/* should strip trailing whitespace as well */
  	for (end = &value[strlen(value)-1]; end > value && ap_isspace(*end); --end)
  	    *end = '\0';
  
          ap_table_add(resp_hdrs, buffer, value);
  
  	/* the header was too long; at the least we should skip extra data */
  	if (len >= size - 1) { 
  	    while ((len = proxy_getline(field, MAX_STRING_LEN, f, 1))
  		    >= MAX_STRING_LEN - 1) {
  		/* soak up the extra data */
  	    }
  	    if (len == 0) /* time to exit the larger loop as well */
  		break;
  	}
      }
      return resp_hdrs;
  }
  
  /* read data from f, write it to:
   * - c->fp, if it is open
   * - r->connection->client, if nowrite == 0
   */
  
  long int ap_proxy_send_fb(BUFF *f, request_rec *r, cache_req *c, off_t len, int nowrite)
  {
      int  ok;
      char buf[IOBUFSIZE];
      long total_bytes_rcvd;
      register int n, o, w;
      conn_rec *con = r->connection;
      int alternate_timeouts = 1;	/* 1 if we alternate between soft & hard timeouts */
  
      total_bytes_rcvd = 0;
      if (c != NULL)
          c->written = 0;
  
  #ifdef CHARSET_EBCDIC
      /* The cache copy is ASCII, not EBCDIC, even for text/html) */
      ap_bsetflag(f, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 0);
      if (c != NULL && c->fp != NULL)
  	ap_bsetflag(c->fp, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 0);
      ap_bsetflag(con->client, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 0);
  #endif
  
      /* Since we are reading from one buffer and writing to another,
       * it is unsafe to do a soft_timeout here, at least until the proxy
       * has its own timeout handler which can set both buffers to EOUT.
       */
  
      ap_kill_timeout(r);
  
  #if defined(WIN32) || defined(TPF) || defined(NETWARE)
      /* works fine under win32, so leave it */
      ap_hard_timeout("proxy send body", r);
      alternate_timeouts = 0;
  #else
      /* CHECKME! Since hard_timeout won't work in unix on sends with partial
       * cache completion, we have to alternate between hard_timeout
       * for reads, and soft_timeout for send.  This is because we need
       * to get a return from ap_bwrite to be able to continue caching.
       * BUT, if we *can't* continue anyway, just use hard_timeout.
       * (Also, if no cache file is written, use hard timeouts)
       */
  
      if (c == NULL || c->len <= 0 || c->cache_completion == 1.0) {
          ap_hard_timeout("proxy send body", r);
          alternate_timeouts = 0;
      }
  #endif
  
      /* Loop and ap_bread() while we can successfully read and write,
       * or (after the client aborted) while we can successfully
       * read and finish the configured cache_completion.
       */
      for (ok = 1; ok; ) {
          if (alternate_timeouts)
              ap_hard_timeout("proxy recv body from upstream server", r);
  
  	/* Read block from server */
  	if (-1 == len) {
  	    n = ap_bread(f, buf, IOBUFSIZE);
  	}
  	else {
  	    n = ap_bread(f, buf, MIN(IOBUFSIZE, len - total_bytes_rcvd));
  	}
  
          if (alternate_timeouts)
              ap_kill_timeout(r);
          else
              ap_reset_timeout(r);
  
  	if (n == -1) {		/* input error */
  	    if (c != NULL) {
  		ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
  		    "proxy: error reading from %s", c->url);
  		c = ap_proxy_cache_error(c);
  	    }
  	    break;
  	}
  	total_bytes_rcvd += n;
  	if (n == 0)
  	    break;		/* EOF */
  	o = 0;
  
  	/* Write to cache first. */
  	/*@@@ XXX FIXME: Assuming that writing the cache file won't time out?!!? */
          if (c != NULL && c->fp != NULL) {
              if (ap_bwrite(c->fp, &buf[0], n) != n) {
                  ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
  		    "proxy: error writing to %s", c->tempfile);
  		c = ap_proxy_cache_error(c);
              } else {
                  c->written += n;
              }
          }
  
  	/* Write the block to the client, detect aborted transfers */
          while (!nowrite && !con->aborted && n > 0) {
              if (alternate_timeouts)
                  ap_soft_timeout("proxy send body", r);
  
              w = ap_bwrite(con->client, &buf[o], n);
  
              if (alternate_timeouts)
                  ap_kill_timeout(r);
              else
                  ap_reset_timeout(r);
  
              if (w <= 0) {
                  if (c != NULL && c->fp != NULL) {
                      /* when a send failure occurs, we need to decide
                       * whether to continue loading and caching the
                       * document, or to abort the whole thing
                       */
                      ok = (c->len > 0) &&
                           (c->cache_completion > 0) &&
                           (c->len * c->cache_completion < total_bytes_rcvd);
  
                      if (! ok) {
                          ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR));
                          c->fp = NULL;
                          unlink(c->tempfile);
  			c = NULL;
                      }
                  }
                  con->aborted = 1;
                  break;
              }
              n -= w;
              o += w;
          } /* while client alive and more data to send */
  
  	/* if we've received everything, leave now */
  	if (total_bytes_rcvd == len)
  	    break;
  
      } /* loop and ap_bread while "ok" */
  
      if (!con->aborted)
  	ap_bflush(con->client);
  
      ap_kill_timeout(r);
      return total_bytes_rcvd;
  }
  
  /*
   * Sends response line and headers.  Uses the client fd and the 
   * headers_out array from the passed request_rec to talk to the client
   * and to properly set the headers it sends for things such as logging.
   * 
   * If respline is NULL, no response line will be sent.
   * If c is NULL, headers won't be written to cache.
   * If r is NULL, headers won't be written to client.
   *
   * A timeout should be set before calling this routine.
   */
  void ap_proxy_send_headers(request_rec *r, cache_req *c, const char *respline, const char *xcache, table *t)
  {
      struct tbl_do_args tdo;
  
      /* write status line */
      if (respline) {
  	if (r && !r->assbackwards)
  	    ap_rvputs(r, respline, CRLF, NULL);
  	if (c && c->fp && ap_bvputs(c->fp, respline, CRLF, NULL) == -1) {
  	    ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
  			  "proxy: error writing status line to %s", c->tempfile);
  	    c = ap_proxy_cache_error(c);
  	}
      }
  
      /* send response headers to client */
      tdo.req = r;
      tdo.cache = c;
      ap_table_do(ap_proxy_send_hdr_line, &tdo, t, NULL);
  
      /* send X-Cache to client */
      if (r && xcache) {
  	ap_rvputs(r, "X-Cache: ", xcache, CRLF, NULL);
      }
  
      /* send terminating CRLF */
      if (r && !r->assbackwards)
  	ap_rputs(CRLF, r);
      if (c != NULL && c->fp != NULL && ap_bputs(CRLF, c->fp) == -1) {
  	ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
  		      "proxy: error writing CRLF to %s", c->tempfile);
  	c = ap_proxy_cache_error(c);
      }
  
  }
  
  
  /*
   * list is a comma-separated list of case-insensitive tokens, with
   * optional whitespace around the tokens.
   * The return returns 1 if the token val is found in the list, or 0
   * otherwise.
   */
  int ap_proxy_liststr(const char *list, const char *key, char **val)
  {
      int len, i;
      const char *p;
      char valbuf[HUGE_STRING_LEN];
      valbuf[sizeof(valbuf)-1] = 0; /* safety terminating zero */
  
      len = strlen(key);
  
      while (list != NULL) {
  	p = strchr(list, ',');
  	if (p != NULL) {
  	    i = p - list;
  	    do
  		p++;
  	    while (ap_isspace(*p));
  	}
  	else
  	    i = strlen(list);
  
  	while (i > 0 && ap_isspace(list[i - 1]))
  	    i--;
  	if (i == len && strncasecmp(list, key, len) == 0) {
  	    if (val) {
  		p = strchr(list, ',');
  		while (ap_isspace(*list)) {
  		    list++;
  		}
  		if ('=' == list[0])
  		    list++;
  		while (ap_isspace(*list)) {
  		    list++;
  		}
  		strncpy(valbuf, list, MIN(p-list, sizeof(valbuf)-1));
  		*val = valbuf;
  	    }
  	    return 1;
  	}
  	list = p;
      }
      return 0;
  }
  
  #ifdef CASE_BLIND_FILESYSTEM
  
  /*
   * On some platforms, the file system is NOT case sensitive. So, a == A
   * need to map to smaller set of characters
   */
  void ap_proxy_hash(const char *it, char *val, int ndepth, int nlength)
  {
      AP_MD5_CTX context;
      unsigned char digest[16];
      char tmp[26];
      int i, k, d;
      unsigned int x;
      static const char enc_table[32] = "abcdefghijklmnopqrstuvwxyz012345";
  
      ap_MD5Init(&context);
      ap_MD5Update(&context, (const unsigned char *) it, strlen(it));
      ap_MD5Final(digest, &context);
  
  /* encode 128 bits as 26 characters, using a modified uuencoding */
  /* the encoding is 5 bytes -> 8 characters
   * i.e. 128 bits is 3 x 5 bytes + 1 byte -> 3 * 8 characters + 2 characters
   */
      for (i = 0, k = 0; i < 15; i += 5) {
  	x = (digest[i] << 24) | (digest[i + 1] << 16) | (digest[i + 2] << 8) | digest[i + 3];
  	tmp[k++] = enc_table[x >> 27];
  	tmp[k++] = enc_table[(x >> 22) & 0x1f];
  	tmp[k++] = enc_table[(x >> 17) & 0x1f];
  	tmp[k++] = enc_table[(x >> 12) & 0x1f];
  	tmp[k++] = enc_table[(x >> 7) & 0x1f];
  	tmp[k++] = enc_table[(x >> 2) & 0x1f];
  	x = ((x & 0x3) << 8) | digest[i + 4];
  	tmp[k++] = enc_table[x >> 5];
  	tmp[k++] = enc_table[x & 0x1f];
      }
  /* one byte left */
      x = digest[15];
      tmp[k++] = enc_table[x >> 3];	/* use up 5 bits */
      tmp[k++] = enc_table[x & 0x7];
      /* now split into directory levels */
  
      for (i = k = d = 0; d < ndepth; ++d) {
  	memcpy(&val[i], &tmp[k], nlength);
  	k += nlength;
  	val[i + nlength] = '/';
  	i += nlength + 1;
      }
      memcpy(&val[i], &tmp[k], 26 - k);
      val[i + 26 - k] = '\0';
  }
  
  #else
  
  void ap_proxy_hash(const char *it, char *val, int ndepth, int nlength)
  {
      AP_MD5_CTX context;
      unsigned char digest[16];
      char tmp[22];
      int i, k, d;
      unsigned int x;
  #if defined(MPE) || (defined(AIX) && defined(__ps2__))
      /* Believe it or not, AIX 1.x does not allow you to name a file '@',
       * so hack around it in the encoding. */
      static const char enc_table[64] =
  	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_%";
  #else
      static const char enc_table[64] =
  	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_@";
  #endif
  
      ap_MD5Init(&context);
      ap_MD5Update(&context, (const unsigned char *) it, strlen(it));
      ap_MD5Final(digest, &context);
  
  /* encode 128 bits as 22 characters, using a modified uuencoding */
  /* the encoding is 3 bytes -> 4 characters
   * i.e. 128 bits is 5 x 3 bytes + 1 byte -> 5 * 4 characters + 2 characters
   */
      for (i = 0, k = 0; i < 15; i += 3) {
  	x = (digest[i] << 16) | (digest[i + 1] << 8) | digest[i + 2];
  	tmp[k++] = enc_table[x >> 18];
  	tmp[k++] = enc_table[(x >> 12) & 0x3f];
  	tmp[k++] = enc_table[(x >> 6) & 0x3f];
  	tmp[k++] = enc_table[x & 0x3f];
      }
  /* one byte left */
      x = digest[15];
      tmp[k++] = enc_table[x >> 2];	/* use up 6 bits */
      tmp[k++] = enc_table[(x << 4) & 0x3f];
      /* now split into directory levels */
  
      for (i = k = d = 0; d < ndepth; ++d) {
  	memcpy(&val[i], &tmp[k], nlength);
  	k += nlength;
  	val[i + nlength] = '/';
  	i += nlength + 1;
      }
      memcpy(&val[i], &tmp[k], 22 - k);
      val[i + 22 - k] = '\0';
  }
  
  #endif /* CASE_BLIND_FILESYSTEM */
  
  /*
   * Converts 16 hex digits to a time integer
   */
  int ap_proxy_hex2sec(const char *x)
  {
      int i, ch;
      unsigned int j;
  
      for (i = 0, j = 0; i < 16; i++) {
  	ch = x[i];
  	j <<= 4;
  	if (ap_isdigit(ch))
  	    j |= ch - '0';
  	else if (ap_isupper(ch))
  	    j |= ch - ('A' - 10);
  	else
  	    j |= ch - ('a' - 10);
      }
  /* no longer necessary, as the source hex is 8-byte int */
  /*    if (j == 0xffffffff)*/
  /*	return -1;*/		/* so that it works with 8-byte ints */
  /*    else */
  	return j;
  }
  
  /*
   * Converts a time integer to 16 hex digits
   */
  void ap_proxy_sec2hex(int t, char *y)
  {
      int i, ch;
      unsigned int j = t;
  
      if (-1 == t) {
  	strcpy(y, "FFFFFFFFFFFFFFFF");
  	return;
      }
  
      for (i = 15; i >= 0; i--) {
  	ch = j & 0xF;
  	j >>= 4;
  	if (ch >= 10)
  	    y[i] = ch + ('A' - 10);
  	else
  	    y[i] = ch + '0';
      }
      y[16] = '\0';
  }
  
  
  cache_req *ap_proxy_cache_error(cache_req *c)
  {
      if (c != NULL) {
  	if (c->fp != NULL) {
  	    ap_pclosef(c->req->pool, ap_bfileno(c->fp, B_WR));
  	    c->fp = NULL;
  	}
  	if (c->origfp != NULL) {
  	    ap_pclosef(c->req->pool, ap_bfileno(c->origfp, B_WR));
  	    c->origfp = NULL;
  	}
  	if (c->tempfile)
  	    unlink(c->tempfile);
      }
      return NULL;
  }
  
  int ap_proxyerror(request_rec *r, int statuscode, const char *message)
  {
      ap_table_setn(r->notes, "error-notes",
  		  ap_pstrcat(r->pool, 
  			     "The proxy server could not handle the request "
  			     "<EM><A HREF=\"", ap_escape_uri(r->pool, r->uri),
  			     "\">", ap_escape_html(r->pool, r->method),
  			     "&nbsp;", 
  			     ap_escape_html(r->pool, r->uri), "</A></EM>.<P>\n"
  			     "Reason: <STRONG>",
  			     ap_escape_html(r->pool, message), 
  			     "</STRONG>", NULL));
  
      /* Allow "error-notes" string to be printed by ap_send_error_response() */
      ap_table_setn(r->notes, "verbose-error-to", ap_pstrdup(r->pool, "*"));
  
      r->status_line = ap_psprintf(r->pool, "%3.3u Proxy Error", statuscode);
      return statuscode;
  }
  
  /*
   * This routine returns its own error message
   */
  const char *
       ap_proxy_host2addr(const char *host, struct hostent *reqhp)
  {
      int i;
      struct hostent *hp;
      struct per_thread_data *ptd = get_per_thread_data();
  
      for (i = 0; host[i] != '\0'; i++)
  	if (!ap_isdigit(host[i]) && host[i] != '.')
  	    break;
  
      if (host[i] != '\0') {
  	hp = gethostbyname(host);
  	if (hp == NULL)
  	    return "Host not found";
      }
      else {
  	ptd->ipaddr = ap_inet_addr(host);
  	hp = gethostbyaddr((char *) &ptd->ipaddr, sizeof(ptd->ipaddr), AF_INET);
  	if (hp == NULL) {
  	    memset(&ptd->hpbuf, 0, sizeof(ptd->hpbuf));
  	    ptd->hpbuf.h_name = 0;
  	    ptd->hpbuf.h_addrtype = AF_INET;
  	    ptd->hpbuf.h_length = sizeof(ptd->ipaddr);
  	    ptd->hpbuf.h_addr_list = ptd->charpbuf;
  	    ptd->hpbuf.h_addr_list[0] = (char *) &ptd->ipaddr;
  	    ptd->hpbuf.h_addr_list[1] = 0;
  	    hp = &ptd->hpbuf;
  	}
      }
      *reqhp = *hp;
      return NULL;
  }
  
  static const char *
       proxy_get_host_of_request(request_rec *r)
  {
      char *url, *user = NULL, *password = NULL, *err, *host;
      int port = -1;
  
      if (r->hostname != NULL)
  	return r->hostname;
  
      /* Set url to the first char after "scheme://" */
      if ((url = strchr(r->uri, ':')) == NULL
  	|| url[1] != '/' || url[2] != '/')
  	return NULL;
  
      url = ap_pstrdup(r->pool, &url[1]);	/* make it point to "//", which is what proxy_canon_netloc expects */
  
      err = ap_proxy_canon_netloc(r->pool, &url, &user, &password, &host, &port);
  
      if (err != NULL)
  	ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r,
  		     "%s", err);
  
      r->hostname = host;
  
      return host;		/* ought to return the port, too */
  }
  
  /* Return TRUE if addr represents an IP address (or an IP network address) */
  int ap_proxy_is_ipaddr(struct dirconn_entry *This, pool *p)
  {
      const char *addr = This->name;
      long ip_addr[4];
      int i, quads;
      long bits;
  
      /* if the address is given with an explicit netmask, use that */
      /* Due to a deficiency in ap_inet_addr(), it is impossible to parse */
      /* "partial" addresses (with less than 4 quads) correctly, i.e.  */
      /* 192.168.123 is parsed as 192.168.0.123, which is not what I want. */
      /* I therefore have to parse the IP address manually: */
      /*if (proxy_readmask(This->name, &This->addr.s_addr, &This->mask.s_addr) == 0) */
      /* addr and mask were set by proxy_readmask() */
      /*return 1; */
  
      /* Parse IP addr manually, optionally allowing */
      /* abbreviated net addresses like 192.168. */
  
      /* Iterate over up to 4 (dotted) quads. */
      for (quads = 0; quads < 4 && *addr != '\0'; ++quads) {
  	char *tmp;
  
  	if (*addr == '/' && quads > 0)	/* netmask starts here. */
  	    break;
  
  	if (!ap_isdigit(*addr))
  	    return 0;		/* no digit at start of quad */
  
  	ip_addr[quads] = strtol(addr, &tmp, 0);
  
  	if (tmp == addr)	/* expected a digit, found something else */
  	    return 0;
  
  	if (ip_addr[quads] < 0 || ip_addr[quads] > 255) {
  	    /* invalid octet */
  	    return 0;
  	}
  
  	addr = tmp;
  
  	if (*addr == '.' && quads != 3)
  	    ++addr;		/* after the 4th quad, a dot would be illegal */
      }
  
      for (This->addr.s_addr = 0, i = 0; i < quads; ++i)
  	This->addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
  
      if (addr[0] == '/' && ap_isdigit(addr[1])) {	/* net mask follows: */
  	char *tmp;
  
  	++addr;
  
  	bits = strtol(addr, &tmp, 0);
  
  	if (tmp == addr)	/* expected a digit, found something else */
  	    return 0;
  
  	addr = tmp;
  
  	if (bits < 0 || bits > 32)	/* netmask must be between 0 and 32 */
  	    return 0;
  
      }
      else {
  	/* Determine (i.e., "guess") netmask by counting the */
  	/* number of trailing .0's; reduce #quads appropriately */
  	/* (so that 192.168.0.0 is equivalent to 192.168.)        */
  	while (quads > 0 && ip_addr[quads - 1] == 0)
  	    --quads;
  
  	/* "IP Address should be given in dotted-quad form, optionally followed by a netmask (e.g., 192.168.111.0/24)"; */
  	if (quads < 1)
  	    return 0;
  
  	/* every zero-byte counts as 8 zero-bits */
  	bits = 8 * quads;
  
  	if (bits != 32)		/* no warning for fully qualified IP address */
  	    fprintf(stderr, "Warning: NetMask not supplied with IP-Addr; guessing: %s/%ld\n",
  		    inet_ntoa(This->addr), bits);
      }
  
      This->mask.s_addr = htonl(INADDR_NONE << (32 - bits));
  
      if (*addr == '\0' && (This->addr.s_addr & ~This->mask.s_addr) != 0) {
  	fprintf(stderr, "Warning: NetMask and IP-Addr disagree in %s/%ld\n",
  		inet_ntoa(This->addr), bits);
  	This->addr.s_addr &= This->mask.s_addr;
  	fprintf(stderr, "         Set to %s/%ld\n",
  		inet_ntoa(This->addr), bits);
      }
  
      if (*addr == '\0') {
  	This->matcher = proxy_match_ipaddr;
  	return 1;
      }
      else
  	return (*addr == '\0');	/* okay iff we've parsed the whole string */
  }
  
  /* Return TRUE if addr represents an IP address (or an IP network address) */
  static int proxy_match_ipaddr(struct dirconn_entry *This, request_rec *r)
  {
      int i;
      int ip_addr[4];
      struct in_addr addr;
      struct in_addr *ip_list;
      char **ip_listptr;
      const char *found;
      const char *host = proxy_get_host_of_request(r);
  
      if (host == NULL)   /* oops! */
         return 0;
  
      memset(&addr, '\0', sizeof addr);
      memset(ip_addr, '\0', sizeof ip_addr);
  
      if (4 == sscanf(host, "%d.%d.%d.%d", &ip_addr[0], &ip_addr[1], &ip_addr[2], &ip_addr[3])) {
  	for (addr.s_addr = 0, i = 0; i < 4; ++i)
  	    addr.s_addr |= htonl(ip_addr[i] << (24 - 8 * i));
  
  	if (This->addr.s_addr == (addr.s_addr & This->mask.s_addr)) {
  #if DEBUGGING
  	    fprintf(stderr, "1)IP-Match: %s[%s] <-> ", host, inet_ntoa(addr));
  	    fprintf(stderr, "%s/", inet_ntoa(This->addr));
  	    fprintf(stderr, "%s\n", inet_ntoa(This->mask));
  #endif
  	    return 1;
  	}
  #if DEBUGGING
  	else {
  	    fprintf(stderr, "1)IP-NoMatch: %s[%s] <-> ", host, inet_ntoa(addr));
  	    fprintf(stderr, "%s/", inet_ntoa(This->addr));
  	    fprintf(stderr, "%s\n", inet_ntoa(This->mask));
  	}
  #endif
      }
      else {
  	struct hostent the_host;
  
  	memset(&the_host, '\0', sizeof the_host);
  	found = ap_proxy_host2addr(host, &the_host);
  
  	if (found != NULL) {
  #if DEBUGGING
  	    fprintf(stderr, "2)IP-NoMatch: hostname=%s msg=%s\n", host, found);
  #endif
  	    return 0;
  	}
  
  	if (the_host.h_name != NULL)
  	    found = the_host.h_name;
  	else
  	    found = host;
  
  	/* Try to deal with multiple IP addr's for a host */
  	for (ip_listptr = the_host.h_addr_list; *ip_listptr; ++ip_listptr) {
  	    ip_list = (struct in_addr *) *ip_listptr;
  	    if (This->addr.s_addr == (ip_list->s_addr & This->mask.s_addr)) {
  #if DEBUGGING
  		fprintf(stderr, "3)IP-Match: %s[%s] <-> ", found, inet_ntoa(*ip_list));
  		fprintf(stderr, "%s/", inet_ntoa(This->addr));
  		fprintf(stderr, "%s\n", inet_ntoa(This->mask));
  #endif
  		return 1;
  	    }
  #if DEBUGGING
  	    else {
  		fprintf(stderr, "3)IP-NoMatch: %s[%s] <-> ", found, inet_ntoa(*ip_list));
  		fprintf(stderr, "%s/", inet_ntoa(This->addr));
  		fprintf(stderr, "%s\n", inet_ntoa(This->mask));
  	    }
  #endif
  	}
      }
  
      return 0;
  }
  
  /* Return TRUE if addr represents a domain name */
  int ap_proxy_is_domainname(struct dirconn_entry *This, pool *p)
  {
      char *addr = This->name;
      int i;
  
      /* Domain name must start with a '.' */
      if (addr[0] != '.')
  	return 0;
  
      /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */
      for (i = 0; ap_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i)
  	continue;
  
  #if 0
      if (addr[i] == ':') {
  	fprintf(stderr, "@@@@ handle optional port in proxy_is_domainname()\n");
  	/* @@@@ handle optional port */
      }
  #endif
  
      if (addr[i] != '\0')
  	return 0;
  
      /* Strip trailing dots */
      for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i)
  	addr[i] = '\0';
  
      This->matcher = proxy_match_domainname;
      return 1;
  }
  
  /* Return TRUE if host "host" is in domain "domain" */
  static int proxy_match_domainname(struct dirconn_entry *This, request_rec *r)
  {
      const char *host = proxy_get_host_of_request(r);
      int d_len = strlen(This->name), h_len;
  
      if (host == NULL)		/* some error was logged already */
  	return 0;
  
      h_len = strlen(host);
  
      /* @@@ do this within the setup? */
      /* Ignore trailing dots in domain comparison: */
      while (d_len > 0 && This->name[d_len - 1] == '.')
  	--d_len;
      while (h_len > 0 && host[h_len - 1] == '.')
  	--h_len;
      return h_len > d_len
  	&& strncasecmp(&host[h_len - d_len], This->name, d_len) == 0;
  }
  
  /* Return TRUE if addr represents a host name */
  int ap_proxy_is_hostname(struct dirconn_entry *This, pool *p)
  {
      struct hostent host;
      char *addr = This->name;
      int i;
  
      /* Host names must not start with a '.' */
      if (addr[0] == '.')
  	return 0;
  
      /* rfc1035 says DNS names must consist of "[-a-zA-Z0-9]" and '.' */
      for (i = 0; ap_isalnum(addr[i]) || addr[i] == '-' || addr[i] == '.'; ++i);
  
  #if 0
      if (addr[i] == ':') {
  	fprintf(stderr, "@@@@ handle optional port in proxy_is_hostname()\n");
  	/* @@@@ handle optional port */
      }
  #endif
  
      if (addr[i] != '\0' || ap_proxy_host2addr(addr, &host) != NULL)
  	return 0;
  
      This->hostentry = ap_pduphostent (p, &host);
  
      /* Strip trailing dots */
      for (i = strlen(addr) - 1; i > 0 && addr[i] == '.'; --i)
  	addr[i] = '\0';
  
      This->matcher = proxy_match_hostname;
      return 1;
  }
  
  /* Return TRUE if host "host" is equal to host2 "host2" */
  static int proxy_match_hostname(struct dirconn_entry *This, request_rec *r)
  {
      char *host = This->name;
      const char *host2 = proxy_get_host_of_request(r);
      int h2_len;
      int h1_len;
  
      if (host == NULL || host2 == NULL)
         return 0; /* oops! */
  
      h2_len = strlen(host2);
      h1_len = strlen(host);
  
  #if 0
      unsigned long *ip_list;
  
      /* Try to deal with multiple IP addr's for a host */
      for (ip_list = *This->hostentry->h_addr_list; *ip_list != 0UL; ++ip_list)
  	if (*ip_list == ? ? ? ? ? ? ? ? ? ? ? ? ?)
  	    return 1;
  #endif
  
      /* Ignore trailing dots in host2 comparison: */
      while (h2_len > 0 && host2[h2_len - 1] == '.')
  	--h2_len;
      while (h1_len > 0 && host[h1_len - 1] == '.')
  	--h1_len;
      return h1_len == h2_len
  	&& strncasecmp(host, host2, h1_len) == 0;
  }
  
  /* Return TRUE if addr is to be matched as a word */
  int ap_proxy_is_word(struct dirconn_entry *This, pool *p)
  {
      This->matcher = proxy_match_word;
      return 1;
  }
  
  /* Return TRUE if string "str2" occurs literally in "str1" */
  static int proxy_match_word(struct dirconn_entry *This, request_rec *r)
  {
      const char *host = proxy_get_host_of_request(r);
      return host != NULL && strstr(host, This->name) != NULL;
  }
  
  int ap_proxy_doconnect(int sock, struct sockaddr_in *addr, request_rec *r)
  {
      int i;
  
      ap_hard_timeout("proxy connect", r);
      do {
  	i = connect(sock, (struct sockaddr *) addr, sizeof(struct sockaddr_in));
  #if defined(WIN32) || defined(NETWARE)
  	if (i == SOCKET_ERROR)
  	    errno = WSAGetLastError();
  #endif /* WIN32 */
      } while (i == -1 && errno == EINTR);
      if (i == -1) {
  	ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  		     "proxy connect to %s port %d failed",
  		     inet_ntoa(addr->sin_addr), ntohs(addr->sin_port));
      }
      ap_kill_timeout(r);
  
      return i;
  }
  
  /* This function is called by ap_table_do() for all header lines
   * (from proxy_http.c and proxy_ftp.c)
   * It is passed a table_do_args struct pointer and a MIME field and value pair
   * This function does not cache certain headers defined by RFC2616 */
  int ap_proxy_send_hdr_line(void *p, const char *key, const char *value)
  {
      struct tbl_do_args *parm = (struct tbl_do_args *)p;
  
      if (key == NULL || value == NULL || value[0] == '\0')
  	return 1;
      if (parm->req && !parm->req->assbackwards) {
  	ap_rvputs(parm->req, key, ": ", value, CRLF, NULL);
  /* CHECKME: Do we need to create the headers_out table first? */
  	ap_table_addn(parm->req->headers_out, key, value);
      }
      if (parm->cache != NULL && parm->cache->fp != NULL &&
  	ap_bvputs(parm->cache->fp, key, ": ", value, CRLF, NULL) == -1) {
  	    ap_log_rerror(APLOG_MARK, APLOG_ERR, parm->cache->req,
  		    "proxy: error writing header to %s", parm->cache->tempfile);
  	    parm->cache = ap_proxy_cache_error(parm->cache);
      }
      return 1; /* tell ap_table_do() to continue calling us for more headers */
  }
  
  /* send a text line to one or two BUFF's; return line length */
  unsigned ap_proxy_bputs2(const char *data, BUFF *client, cache_req *cache)
  {
      unsigned len = ap_bputs(data, client);
      if (cache != NULL && cache->fp != NULL)
  	ap_bputs(data, cache->fp);
      return len;
  }
  
  /* do a HTTP/1.1 age calculation */
  time_t ap_proxy_current_age(cache_req *c, const time_t age_value)
  {
      time_t apparent_age, corrected_received_age, response_delay, corrected_initial_age, resident_time, current_age;
  
      /* Perform an HTTP/1.1 age calculation. (RFC2616 13.2.3) */
  
      apparent_age = MAX(0, c->resp_time - c->date);
      corrected_received_age = MAX(apparent_age, age_value);
      response_delay = c->resp_time - c->req_time;
      corrected_initial_age = corrected_received_age + response_delay;
      resident_time = time(NULL) - c->resp_time;
      current_age = corrected_initial_age + resident_time;
  
      return (current_age);
  }
  
  /* open a cache file and return a pointer to a BUFF */
  BUFF *ap_proxy_open_cachefile(request_rec *r, char *filename)
  {
      BUFF *cachefp = NULL;
      int cfd;
  
      if (filename != NULL) {
  	cfd = open(filename, O_RDWR | O_BINARY);
  	if (cfd != -1) {
  	    ap_note_cleanups_for_fd(r->pool, cfd);
  	    cachefp = ap_bcreate(r->pool, B_RD | B_WR);
  	    ap_bpushfd(cachefp, cfd, cfd);
  	}
  	else if (errno != ENOENT)
  	    ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  	                  "proxy: error opening cache file %s",
  	                  filename);
  #ifdef EXPLAIN
  	else
  	    Explain1("File %s not found", filename);
  #endif
  
      }
      return cachefp;
  }
  
  /* create a cache file and return a pointer to a BUFF */
  BUFF *ap_proxy_create_cachefile(request_rec *r, char *filename)
  {
      BUFF *cachefp = NULL;
      int cfd;
  
      if (filename != NULL) {
  	cfd = open(filename, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0622);
  	if (cfd != -1) {
  	    ap_note_cleanups_for_fd(r->pool, cfd);
  	    cachefp = ap_bcreate(r->pool, B_WR);
  	    ap_bpushfd(cachefp, -1, cfd);
  	}
  	else if (errno != ENOENT)
  	    ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  	                  "proxy: error creating cache file %s",
  	                  filename);
      }
      return cachefp;
  }
  
  /* Clear all connection-based headers from the incoming headers table */
  void ap_proxy_clear_connection(pool *p, table *headers)
  {
      const char *name;
      char *next = ap_pstrdup(p, ap_table_get(headers, "Connection"));
  
      ap_table_unset(headers, "Proxy-Connection");
  	if (!next) 
  	return;
  
      while (*next) { 
    	  name = next;
    	  while (*next && !ap_isspace(*next) && (*next != ','))
              ++next;
          while (*next && (ap_isspace(*next) || (*next == ','))) {
    	      *next = '\0';
    	      ++next;
          }
          ap_table_unset(headers, name);
      }
      ap_table_unset(headers, "Connection");
  }
  
  /* XXX: if you tweak this you should look at is_empty_table() and table_elts()
   * in ap_alloc.h
   *
   * NOTE: this private definition is a duplicate of the one in alloc.c
   * It's here for ap_proxy_table_replace() to avoid breaking binary compat
   */
  struct table {
      /* This has to be first to promote backwards compatibility with
       * older modules which cast a table * to an array_header *...
       * they should use the table_elts() function for most of the
       * cases they do this for.
       */
      array_header a;
  #ifdef MAKE_TABLE_PROFILE
      void *creator;
  #endif
  };
  
  /*
   * overlay one table on another
   * keys in base will be replaced by keys in overlay
   */
  int ap_proxy_table_replace(table *base, table *overlay)
  {
      table_entry *elts = (table_entry *) overlay->a.elts;
      int i, q = 0;
      const char *val;
  
      for (i = 0; i < overlay->a.nelts; ++i) {
          val = ap_table_get(base, elts[i].key);
          if (!val || strcmp(val, elts[i].val))
              q = 1;
          ap_table_set(base, elts[i].key, elts[i].val);
      }
  
      return q;
  }
  
  #if defined WIN32
  
  static DWORD tls_index;
  
  BOOL WINAPI DllMain (HINSTANCE dllhandle, DWORD reason, LPVOID reserved)
  {
      LPVOID memptr;
  
      switch (reason) {
      case DLL_PROCESS_ATTACH:
  	tls_index = TlsAlloc();
      case DLL_THREAD_ATTACH: /* intentional no break */
  	TlsSetValue (tls_index, malloc (sizeof (struct per_thread_data)));
  	break;
      case DLL_THREAD_DETACH:
  	memptr = TlsGetValue (tls_index);
  	if (memptr) {
  	    free (memptr);
  	    TlsSetValue (tls_index, 0);
  	}
  	break;
      }
  
      return TRUE;
  }
  
  #endif
  
  static struct per_thread_data *get_per_thread_data(void)
  {
  #if defined(WIN32)
  
      return (struct per_thread_data *) TlsGetValue (tls_index);
  
  #else
  
      static APACHE_TLS struct per_thread_data sptd;
      return &sptd;
  
  #endif
  }
  
  
  

Mime
View raw message