home *** CD-ROM | disk | FTP | other *** search
/ CD Actual 8 / CDACTUAL8.iso / share / os2 / varios / apache / mod_prox.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-07-03  |  86.2 KB  |  3,263 lines

  1. /* ====================================================================
  2.  * Copyright (c) 1996 The Apache Group.  All rights reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions
  6.  * are met:
  7.  *
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer. 
  10.  *
  11.  * 2. Redistributions in binary form must reproduce the above copyright
  12.  *    notice, this list of conditions and the following disclaimer in
  13.  *    the documentation and/or other materials provided with the
  14.  *    distribution.
  15.  *
  16.  * 3. All advertising materials mentioning features or use of this
  17.  *    software must display the following acknowledgment:
  18.  *    "This product includes software developed by the Apache Group
  19.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  20.  *
  21.  * 4. The names "Apache Server" and "Apache Group" must not be used to
  22.  *    endorse or promote products derived from this software without
  23.  *    prior written permission.
  24.  *
  25.  * 5. Redistributions of any form whatsoever must retain the following
  26.  *    acknowledgment:
  27.  *    "This product includes software developed by the Apache Group
  28.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  29.  *
  30.  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
  31.  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  32.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  33.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
  34.  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  35.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  36.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  37.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  38.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  39.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  40.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  41.  * OF THE POSSIBILITY OF SUCH DAMAGE.
  42.  * ====================================================================
  43.  *
  44.  * This software consists of voluntary contributions made by many
  45.  * individuals on behalf of the Apache Group and was originally based
  46.  * on public domain software written at the National Center for
  47.  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
  48.  * For more information on the Apache Group and the Apache HTTP server
  49.  * project, please see <http://www.apache.org/>.
  50.  *
  51.  */
  52.  
  53. /*
  54. Note that the Explain() stuff is not yet complete.
  55. Also note numerous FIXMEs and CHECKMEs which should be eliminated.
  56.  
  57. If TESTING is set, then garbage collection doesn't delete ... probably a good
  58. idea when hacking.
  59.  
  60. This code is still experimental!
  61.  
  62. Things to do:
  63.  
  64. 1. Make it garbage collect in the background, not while someone is waiting for
  65. a response!
  66.  
  67. 2. Check the logic thoroughly.
  68.  
  69. 3. Empty directories are only removed the next time round (but this does avoid
  70. two passes). Consider doing them the first time round.
  71.  
  72. Ben Laurie <ben@algroup.co.uk> 30 Mar 96
  73.  
  74. More changes:
  75.  
  76. 0) tested w/SOCKS proxy for http
  77. 1) fixed IP address formation in host2addr()
  78. 2) fixed SIGALRM on big cache cleanup
  79. 3) fixed temp files #tmp not removed
  80. 4) changed PF_INET to AF_INET in socket() calls
  81. 5) installed CONNECT code from Troy Morrison <spiffnet@zoom.com> for testing
  82. 6) added NoCache config directive to disallow caching for selected hosts
  83.  
  84. Chuck Murcko <chuck@telebase.com> 2 Jun 96
  85.  
  86. */
  87.  
  88. #define TESTING    0
  89.  
  90. #include "httpd.h"
  91. #include "http_config.h"
  92. #include "http_log.h"
  93. #include "http_main.h"
  94. #include "http_protocol.h"
  95.  
  96. #include "md5.h"
  97.  
  98. #include <utime.h>
  99.  
  100. #include "explain.h"
  101.  
  102. DEF_Explain
  103.  
  104. #define    SEC_ONE_DAY        86400    /* one day, in seconds */
  105. #define    SEC_ONE_HR        3600    /* one hour, in seconds */
  106.  
  107. #define    DEFAULT_FTP_DATA_PORT    20
  108. #define    DEFAULT_FTP_PORT    21
  109. #define    DEFAULT_GOPHER_PORT    70
  110. #define    DEFAULT_NNTP_PORT    119
  111. #define    DEFAULT_WAIS_PORT    210
  112. #define    DEFAULT_HTTPS_PORT    443
  113. #define    DEFAULT_SNEWS_PORT    563
  114. #define    DEFAULT_PROSPERO_PORT    1525    /* WARNING: conflict w/Oracle */
  115.  
  116. /* Some WWW schemes and their default ports; this is basically /etc/services */
  117. static struct
  118. {
  119.     const char *scheme;
  120.     int port;
  121. } defports[]={
  122.     { "ftp",      DEFAULT_FTP_PORT},
  123.     { "gopher",   DEFAULT_GOPHER_PORT},
  124.     { "http",     DEFAULT_PORT},
  125.     { "nntp",     DEFAULT_NNTP_PORT},
  126.     { "wais",     DEFAULT_WAIS_PORT},
  127.     { "https",    DEFAULT_HTTPS_PORT},
  128.     { "snews",    DEFAULT_SNEWS_PORT},
  129.     { "prospero", DEFAULT_PROSPERO_PORT},
  130.     { NULL, -1}  /* unknown port */
  131. };
  132.  
  133.  
  134. /* static information about a remote proxy */
  135. struct proxy_remote
  136. {
  137.     const char *scheme;    /* the schemes handled by this proxy, or '*' */
  138.     const char *protocol;  /* the scheme used to talk to this proxy */
  139.     const char *hostname;  /* the hostname of this proxy */
  140.     int port;              /* the port for this proxy */
  141. };
  142.  
  143. struct proxy_alias {
  144.     char *real;
  145.     char *fake;
  146. };
  147.  
  148. struct nocache_entry {
  149.     char *name;
  150. };
  151.  
  152. #define DEFAULT_CACHE_SPACE 5
  153. #define DEFAULT_CACHE_MAXEXPIRE SEC_ONE_DAY
  154. #define DEFAULT_CACHE_EXPIRE    SEC_ONE_HR
  155. #define DEFAULT_CACHE_LMFACTOR (0.1)
  156.  
  157. /* static information about the local cache */
  158. struct cache_conf
  159. {
  160.     const char *root;   /* the location of the cache directory */
  161.     int space;          /* Maximum cache size (in 1024 bytes) */
  162.     int maxexpire;      /* Maximum time to keep cached files in secs */
  163.     int defaultexpire;  /* default time to keep cached file in secs */
  164.     double lmfactor;    /* factor for estimating expires date */
  165.     int gcinterval;     /* garbage collection interval, in seconds */
  166.     int dirlevels;    /* Number of levels of subdirectories */
  167.     int dirlength;    /* Length of subdirectory names */
  168. };
  169.  
  170. typedef struct
  171. {
  172.  
  173.     struct cache_conf cache;  /* cache configuration */
  174.     array_header *proxies;
  175.     array_header *aliases;
  176.     array_header *nocaches;
  177.     int req;                 /* true if proxy requests are enabled */
  178. } proxy_server_conf;
  179.  
  180. /*
  181.  * A Web proxy module. Stages:
  182.  *
  183.  *  translate_name: set filename to proxy:<URL>
  184.  *  type_checker:   set type to PROXY_MAGIC_TYPE if filename begins proxy:
  185.  *  fix_ups:        convert the URL stored in the filename to the
  186.  *                  canonical form.
  187.  *  handler:        handle proxy requests
  188.  */
  189.  
  190. struct hdr_entry
  191. {
  192.     char *field;
  193.     char *value;
  194. };
  195.  
  196. /* caching information about a request */
  197. struct cache_req
  198. {
  199.     request_rec *req;  /* the request */
  200.     char *url;         /* the URL requested */
  201.     char *filename;    /* name of the cache file, or NULL if no cache */
  202.     char *tempfile;    /* name of the temporary file, of NULL if not caching */
  203.     time_t ims;        /* if-modified-since date of request; -1 if no header */
  204.     BUFF *fp;          /* the cache file descriptor if the file is cached
  205.                           and may be returned, or NULL if the file is
  206.                           not cached (or must be reloaded) */
  207.     time_t expire;      /* calculated expire date of cached entity */
  208.     time_t lmod;        /* last-modified date of cached entity */
  209.     time_t date;        /* the date the cached file was last touched */
  210.     int version;        /* update count of the file */
  211.     unsigned int len;   /* content length */
  212.     char *protocol;     /* Protocol, and major/minor number, e.g. HTTP/1.1 */
  213.     int status;         /* the status of the cached file */
  214.     char *resp_line;    /* the whole status like (protocol, code + message) */
  215.     array_header *hdrs; /* the HTTP headers of the file */
  216. };
  217.       
  218.  
  219. extern module proxy_module;
  220.  
  221.  
  222. static int http_canon(request_rec *r, char *url, const char *scheme,
  223.               int def_port);
  224. static int ftp_canon(request_rec *r, char *url);
  225.  
  226. static int http_handler(request_rec *r, struct cache_req *c, char *url,
  227.             const char *proxyhost, int proxyport);
  228. static int ftp_handler(request_rec *r, struct cache_req *c, char *url);
  229.  
  230. static int connect_handler(request_rec *r, struct cache_req *c, char *url);
  231.  
  232. static BUFF *cache_error(struct cache_req *r);
  233.  
  234. /* -------------------------------------------------------------- */
  235. /* Translate the URL into a 'filename' */
  236.  
  237. static int
  238. alias_match(char *uri, char *alias_fakename)
  239. {
  240.     char *end_fakename = alias_fakename + strlen (alias_fakename);
  241.     char *aliasp = alias_fakename, *urip = uri;
  242.  
  243.     while (aliasp < end_fakename)
  244.     {
  245.     if (*aliasp == '/')
  246.     {
  247.         /* any number of '/' in the alias matches any number in
  248.          * the supplied URI, but there must be at least one...
  249.          */
  250.         if (*urip != '/') return 0;
  251.         
  252.         while (*aliasp == '/') ++ aliasp;
  253.         while (*urip == '/') ++ urip;
  254.     }
  255.     else {
  256.         /* Other characters are compared literally */
  257.         if (*urip++ != *aliasp++) return 0;
  258.     }
  259.     }
  260.  
  261.     /* Check last alias path component matched all the way */
  262.  
  263.     if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/')
  264.     return 0;
  265.  
  266.     /* Return number of characters from URI which matched (may be
  267.      * greater than length of alias, since we may have matched
  268.      * doubled slashes)
  269.      */
  270.  
  271.     return urip - uri;
  272. }
  273.  
  274. static int
  275. proxy_trans(request_rec *r)
  276. {
  277.     void *sconf = r->server->module_config;
  278.     proxy_server_conf *conf =
  279.         (proxy_server_conf *)get_module_config(sconf, &proxy_module);
  280.  
  281.     if (r->proxyreq)
  282.     {
  283.     if (!conf->req) return DECLINED;
  284.     
  285.     r->filename = pstrcat(r->pool, "proxy:", r->uri, NULL);
  286.     r->handler = "proxy-server";
  287.     return OK;
  288.     } else
  289.     {
  290.     int i, len;
  291.     struct proxy_alias *ent=(struct proxy_alias *)conf->aliases->elts;
  292.  
  293.     for (i=0; i < conf->aliases->nelts; i++)
  294.     {
  295.         len = alias_match(r->uri, ent[i].fake);
  296.  
  297.         if (len > 0)
  298.         {
  299.         r->filename = pstrcat(r->pool, "proxy:", ent[i].real,
  300.                       r->uri + len, NULL);
  301.         r->handler = "proxy-server";
  302.         return OK;
  303.         }
  304.     }
  305.     return DECLINED;
  306.     }
  307. }
  308.  
  309. /* -------------------------------------------------------------- */
  310. /* Fixup the filename */
  311.  
  312. /*
  313.  * Canonicalise the URL
  314.  */
  315. static int
  316. proxy_fixup(request_rec *r)
  317. {
  318.     char *url, *p;
  319.     int i;
  320.  
  321.     if (strncmp(r->filename, "proxy:", 6) != 0) return DECLINED;
  322.  
  323.     url = &r->filename[6];
  324. /* lowercase the scheme */
  325.     p = strchr(url, ':');
  326.     if (p == NULL || p == url) return BAD_REQUEST;
  327.     for (i=0; i != p - url; i++) url[i] = tolower(url[i]);
  328.  
  329. /* canonicalise each specific scheme */
  330.     if (strncmp(url, "http:", 5) == 0)
  331.     return http_canon(r, url+5, "http", DEFAULT_PORT);
  332.     else if (strncmp(url, "ftp:", 4) == 0) return ftp_canon(r, url+4);
  333.     else return OK; /* otherwise; we've done the best we can */
  334. }
  335.  
  336. /* already called in the knowledge that the characters are hex digits */
  337. static int
  338. hex2c(const char *x)
  339. {
  340.     int i, ch;
  341.  
  342.     ch = x[0];
  343.     if (isdigit(ch)) i = ch - '0';
  344.     else if (isupper(ch)) i = ch - ('A' - 10);
  345.     else i = ch - ('a' - 10);
  346.     i <<= 4;
  347.  
  348.     ch = x[1];
  349.     if (isdigit(ch)) i += ch - '0';
  350.     else if (isupper(ch)) i += ch - ('A' - 10);
  351.     else i += ch - ('a' - 10);
  352.     return i;
  353. }
  354.  
  355.  
  356. static void
  357. c2hex(int ch, char *x)
  358. {
  359.     int i;
  360.  
  361.     x[0] = '%';
  362.     i = (ch & 0xF0) >> 4;
  363.     if (i >= 10) x[1] = ('A' - 10) + i;
  364.     else x[1] = '0' + i;
  365.  
  366.     i = ch & 0x0F;
  367.     if (i >= 10) x[2] = ('A' - 10) + i;
  368.     else x[2] = '0' + i;
  369. }
  370.  
  371. /*
  372.  * canonicalise a URL-encoded string
  373.  */
  374.  
  375. enum enctype { enc_path, enc_search, enc_user, enc_fpath, enc_parm };
  376.  
  377. /*
  378.  * Decodes a '%' escaped string, and returns the number of characters
  379.  */
  380. static int
  381. decodeenc(char *x)
  382. {
  383.     int i, j, ch;
  384.  
  385.     if (x[0] == '\0') return 0; /* special case for no characters */
  386.     for (i=0, j=0; x[i] != '\0'; i++, j++)
  387.     {
  388. /* decode it if not already done */
  389.     ch = x[i];
  390.     if ( ch == '%' && isxdigit(x[i+1]) && isxdigit(x[i+2]))
  391.     {
  392.         ch = hex2c(&x[i+1]);
  393.         i += 2;
  394.     }
  395.     x[j] = ch;
  396.     }
  397.     x[j] = '\0';
  398.     return j;
  399. }
  400.  
  401.  
  402. /*
  403.  * Convert a URL-encoded string to canonical form.
  404.  * It decodes characters which need not be encoded,
  405.  * and encodes those which must be encoded, and does not touch
  406.  * those which must not be touched.
  407.  */
  408. static char *
  409. canonenc(pool *p, const char *x, int len, enum enctype t, int isenc)
  410. {
  411.     int i, j, ispath, ch;
  412.     char *y;
  413.     const char *allowed;  /* characters which should not be encoded */
  414.     const char *reserved;  /* characters which much not be en/de-coded */
  415.  
  416. /* N.B. in addition to :@&=, this allows ';' in an http path
  417.  * and '?' in an ftp path -- this may be revised
  418.  * 
  419.  * Also, it makes a '+' character in a search string reserved, as
  420.  * it may be form-encoded. (Although RFC 1738 doesn't allow this -
  421.  * it only permits ; / ? : @ = & as reserved chars.)
  422.  */
  423.     if (t == enc_path) allowed = "$-_.+!*'(),;:@&=";
  424.     else if (t == enc_search) allowed = "$-_.!*'(),;:@&=";
  425.     else if (t == enc_user) allowed = "$-_.+!*'(),;@&=";
  426.     else if (t == enc_fpath) allowed = "$-_.+!*'(),?:@&=";
  427.     else /* if (t == enc_parm) */ allowed = "$-_.+!*'(),?/:@&=";
  428.  
  429.     if (t == enc_path) reserved = "/";
  430.     else if (t == enc_search) reserved = "+";
  431.     else reserved = "";
  432.  
  433.     y = palloc(p, 3*len+1);
  434.     ispath = (t == enc_path);
  435.  
  436.     for (i=0, j=0; i < len; i++, j++)
  437.     {
  438. /* always handle '/' first */
  439.     ch = x[i];
  440.     if (ind(reserved, ch) != -1)
  441.     {
  442.         y[j] = ch;
  443.         continue;
  444.     }
  445. /* decode it if not already done */
  446.     if (isenc && ch == '%')
  447.     {
  448.         if (!isxdigit(x[i+1]) || !isxdigit(x[i+2]))
  449.         return NULL;
  450.         ch = hex2c(&x[i+1]);
  451.         i += 2;
  452.         if (ch != 0 && ind(reserved, ch) != -1)
  453.         {  /* keep it encoded */
  454.         c2hex(ch, &y[j]);
  455.         j += 2;
  456.         continue;
  457.         }
  458.     }
  459. /* recode it, if necessary */
  460.     if (!isalnum(ch) && ind(allowed, ch) == -1)
  461.     {
  462.         c2hex(ch, &y[j]);
  463.         j += 2;
  464.     } else y[j] = ch;
  465.     }
  466.     y[j] = '\0';
  467.     return y;
  468. }
  469.  
  470. /*
  471.  * Parses network-location.
  472.  *    urlp           on input the URL; on output the path, after the leading /
  473.  *    user           NULL if no user/password permitted
  474.  *    password       holder for password
  475.  *    host           holder for host
  476.  *    port           port number; only set if one is supplied.
  477.  *
  478.  * Returns an error string.
  479.  */
  480. static char *
  481. canon_netloc(pool *pool, char **const urlp, char **userp, char **passwordp,
  482.         char **hostp, int *port)
  483. {
  484.     int i;
  485.     char *p, *host, *url=*urlp;
  486.  
  487.     if (url[0] != '/' || url[1] != '/') return "Malformed URL";
  488.     host = url + 2;
  489.     url = strchr(host, '/');
  490.     if (url == NULL)
  491.     url = "";
  492.     else
  493.     *(url++) = '\0';  /* skip seperating '/' */
  494.  
  495.     if (userp != NULL)
  496.     {
  497.     char *user=NULL, *password = NULL;
  498.     p = strchr(host, '@');
  499.  
  500.     if (p != NULL)
  501.     {
  502.         *p = '\0';
  503.         user = host;
  504.         host = p + 1;
  505.  
  506. /* find password */
  507.         p = strchr(user, ':');
  508.         if (p != NULL)
  509.         {
  510.         *p = '\0';
  511.         password = canonenc(pool, p+1, strlen(p+1), enc_user, 1);
  512.         if (password == NULL)
  513.             return "Bad %-escape in URL (password)";
  514.         }
  515.  
  516.         user = canonenc(pool, user, strlen(user), enc_user, 1);
  517.         if (user == NULL) return "Bad %-escape in URL (username)";
  518.     }
  519.     *userp = user;
  520.     *passwordp = password;
  521.     }
  522.  
  523.     p = strchr(host, ':');
  524.     if (p != NULL)
  525.     {
  526.     *(p++) = '\0';
  527.     
  528.     for (i=0; p[i] != '\0'; i++)
  529.         if (!isdigit(p[i])) break;
  530.  
  531.     if (i == 0 || p[i] != '\0')
  532.         return "Bad port number in URL";
  533.     *port = atoi(p);
  534.     if (*port > 65535) return "Port number in URL > 65535";
  535.     }
  536.     str_tolower(host); /* DNS names are case-insensitive */
  537.     if (*host == '\0') return "Missing host in URL";
  538. /* check hostname syntax */
  539.     for (i=0; host[i] != '\0'; i++)
  540.     if (!isdigit(host[i]) && host[i] != '.')
  541.         break;
  542.  /* must be an IP address */
  543.     if (host[i] == '\0' && (inet_addr(host) == -1 || inet_network(host) == -1))
  544.         return "Bad IP address in URL";
  545.  
  546.     *urlp = url;
  547.     *hostp = host;
  548.  
  549.     return NULL;
  550. }
  551.  
  552. /*
  553.  * checks an encoded ftp string for bad characters, namely, CR, LF or
  554.  * non-ascii character
  555.  */
  556. static int
  557. ftp_check_string(const char *x)
  558. {
  559.     int i, ch;
  560.  
  561.     for (i=0; x[i] != '\0'; i++)
  562.     {
  563.     ch = x[i];
  564.     if ( ch == '%' && isxdigit(x[i+1]) && isxdigit(x[i+2]))
  565.     {
  566.         ch = hex2c(&x[i+1]);
  567.         i += 2;
  568.     }
  569.     if (ch == '\015' || ch == '\012' || (ch & 0x80)) return 0;
  570.     }
  571.     return 1;
  572. }
  573.  
  574. /*
  575.  * Canonicalise ftp URLs.
  576.  */
  577. static int
  578. ftp_canon(request_rec *r, char *url)
  579. {
  580.     char *user, *password, *host, *path, *parms, *p, sport[7];
  581.     const char *err;
  582.     int port;
  583.  
  584.     port = DEFAULT_FTP_PORT;
  585.     err = canon_netloc(r->pool, &url, &user, &password, &host, &port);
  586.     if (err) return BAD_REQUEST;
  587.     if (user != NULL && !ftp_check_string(user)) return BAD_REQUEST;
  588.     if (password != NULL && !ftp_check_string(password)) return BAD_REQUEST;
  589.  
  590. /* now parse path/parameters args, according to rfc1738 */
  591. /* N.B. if this isn't a true proxy request, then the URL path
  592.  * (but not query args) has already been decoded.
  593.  * This gives rise to the problem of a ; being decoded into the
  594.  * path.
  595.  */
  596.     p = strchr(url, ';');
  597.     if (p != NULL)
  598.     {
  599.     *(p++) = '\0';
  600.     parms = canonenc(r->pool, p, strlen(p), enc_parm, r->proxyreq);
  601.     if (parms == NULL) return BAD_REQUEST;
  602.     } else
  603.     parms = "";
  604.  
  605.     path = canonenc(r->pool, url, strlen(url), enc_path, r->proxyreq);
  606.     if (path == NULL) return BAD_REQUEST;
  607.     if (!ftp_check_string(path)) return BAD_REQUEST;
  608.  
  609.     if (!r->proxyreq && r->args != NULL)
  610.     {
  611.     if (p != NULL)
  612.     {
  613.         p = canonenc(r->pool, r->args, strlen(r->args), enc_parm, 1);
  614.         if (p == NULL) return BAD_REQUEST;
  615.         parms = pstrcat(r->pool, parms, "?", p, NULL);
  616.     }
  617.     else
  618.     {
  619.         p = canonenc(r->pool, r->args, strlen(r->args), enc_path, 1);
  620.         if (p == NULL) return BAD_REQUEST;
  621.         path = pstrcat(r->pool, path, "?", p, NULL);
  622.     }
  623.     r->args = NULL;
  624.     }
  625.  
  626. /* now, rebuild URL */
  627.  
  628.     if (port != DEFAULT_FTP_PORT) sprintf(sport, ":%d", port);
  629.     else sport[0] = '\0';
  630.  
  631.     r->filename = pstrcat(r->pool, "proxy:ftp://", (user != NULL) ? user : "",
  632.               (password != NULL) ? ":" : "",
  633.               (password != NULL) ? password : "",
  634.               (user != NULL) ? "@" : "", host, sport, "/", path,
  635.               (parms[0] != '\0') ? ";" : "", parms, NULL);
  636.  
  637.     return OK;
  638. }
  639.  
  640.  
  641. /*
  642.  * Canonicalise http-like URLs.
  643.  *  scheme is the scheme for the URL
  644.  *  url    is the URL starting with the first '/'
  645.  *  def_port is the default port for this scheme.
  646.  */
  647. static int
  648. http_canon(request_rec *r, char *url, const char *scheme, int def_port)
  649. {
  650.     char *host, *path, *search, *p, sport[7];
  651.     const char *err;
  652.     int port;
  653.  
  654. /* do syntatic check.
  655.  * We break the URL into host, port, path, search
  656.  */
  657.     port = def_port;
  658.     err = canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
  659.     if (err) return BAD_REQUEST;
  660.  
  661. /* now parse path/search args, according to rfc1738 */
  662. /* N.B. if this isn't a true proxy request, then the URL _path_
  663.  * has already been decoded
  664.  */
  665.     if (r->proxyreq)
  666.     {
  667.     p = strchr(url, '?');
  668.     if (p != NULL) *(p++) = '\0';
  669.     } else
  670.     p = r->args;
  671.  
  672. /* process path */
  673.     path = canonenc(r->pool, url, strlen(url), enc_path, r->proxyreq);
  674.     if (path == NULL) return BAD_REQUEST;
  675.  
  676. /* process search */
  677.     if (p != NULL)
  678.     {
  679.     search = p;
  680.     if (search == NULL) return BAD_REQUEST;
  681.     } else
  682.     search = "";
  683.  
  684.     if (port != def_port) sprintf(sport, ":%d", port);
  685.     else sport[0] = '\0';
  686.  
  687.     r->filename = pstrcat(r->pool, "proxy:", scheme, "://", host, sport, "/",
  688.               path, (search[0] != '\0') ? "?" : "", search, NULL);
  689.     return OK;
  690. }
  691.  
  692. static const char *lwday[7]=
  693. {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
  694. static const char *wday[7]=
  695. {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
  696. static const char *months[12]=
  697. {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov",
  698.  "Dec"};
  699.  
  700. /*
  701.  * If the date is a valid RFC 850 date or asctime() date, then it
  702.  * is converted to the RFC 1123 format, otherwise it is not modified.
  703.  * This routine is not very fast at doing conversions, as it uses
  704.  * sscanf and sprintf. However, if the date is already correctly
  705.  * formatted, then it exits very quickly.
  706.  */
  707. static char *
  708. date_canon(pool *p, char *x)
  709. {
  710.     int wk, mday, year, hour, min, sec, mon;
  711.     char *q, month[4], zone[4], week[4];
  712.     
  713.     q = strchr(x, ',');
  714.     /* check for RFC 850 date */
  715.     if (q != NULL && q - x > 3 && q[1] == ' ')
  716.     {
  717.     *q = '\0';
  718.     for (wk=0; wk < 7; wk++)
  719.         if (strcmp(x, lwday[wk]) == 0) break;
  720.     *q = ',';
  721.     if (wk == 7) return x;  /* not a valid date */
  722.     if (q[4] != '-' || q[8] != '-' || q[11] != ' ' || q[14] != ':' ||
  723.         q[17] != ':' || strcmp(&q[20], " GMT") != 0) return x;
  724.     if (sscanf(q+2, "%u-%3s-%u %u:%u:%u %3s", &mday, month, &year,
  725.            &hour, &min, &sec, zone) != 7) return x;
  726.     if (year < 70) year += 2000;
  727.     else year += 1900;
  728.     } else
  729.     {
  730. /* check for acstime() date */
  731.     if (x[3] != ' ' || x[7] != ' ' || x[10] != ' ' || x[13] != ':' ||
  732.         x[16] != ':' || x[19] != ' ' || x[24] != '\0') return x;
  733.     if (sscanf(x, "%3s %3s %u %u:%u:%u %u", week, month, &mday, &hour,
  734.            &min, &sec, &year) != 7) return x;
  735.     for (wk=0; wk < 7; wk++)
  736.         if (strcmp(week, wday[wk]) == 0) break;
  737.     if (wk == 7) return x;
  738.     }
  739.  
  740. /* check date */
  741.     for (mon=0; mon < 12; mon++) if (strcmp(month, months[mon]) == 0) break;
  742.     if (mon == 12) return x;
  743. /*
  744.  *  it doesn't do any harm to convert an invalid date from one format to
  745.  * another
  746.  */
  747. #if 0
  748.     if (hour > 23 || min > 60 || sec > 62 || mday == 0 || mday > 31) return x;
  749.     if (mday == 31 && (mon == 1 || mon == 3 || mon == 5 || mon == 8 || mon == 10))
  750.     return x;
  751.     if (mday > 29 && mon == 1) return x;
  752.     if (mday == 29 && mon == 1)
  753.     if (year%4 != 0 || (year%100 == 0 && year%400 != 0)) return x;
  754. #endif
  755.  
  756.     if (strlen(x) < 31) x = palloc(p, 31);
  757.     sprintf(x, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", wday[wk], mday,
  758.         months[mon], year, hour, min, sec);
  759.     return x;
  760. }
  761.  
  762.  
  763. /* -------------------------------------------------------------- */
  764. /* Invoke handler */
  765.  
  766. /* Utility routines */
  767.  
  768. /*
  769.  * Reads headers from a connection and returns an array of headers.
  770.  * Returns NULL on file error
  771.  */
  772. static array_header *
  773. read_headers(pool *pool, char *buffer, int size, BUFF *f)
  774. {
  775.     int gotcr, len, i, j;
  776.     array_header *resp_hdrs;
  777.     struct hdr_entry *hdr;
  778.     char *p;
  779.  
  780.     resp_hdrs = make_array(pool, 10, sizeof(struct hdr_entry));
  781.     hdr = NULL;
  782.  
  783.     gotcr = 1;
  784.     for (;;)
  785.     {
  786.     len = bgets(buffer, size, f);
  787.     if (len == -1) return NULL;
  788.     if (len == 0) break;
  789.     if (buffer[len-1] == '\n')
  790.     {
  791.         buffer[--len] = '\0';
  792.         i = 1;
  793.     } else
  794.         i = 0;
  795.  
  796.     if (!gotcr || buffer[0] == ' ' || buffer[0] == '\t')
  797.     {
  798.         /* a continuation header */
  799.         if (hdr == NULL)
  800.         {
  801.         /* error!! */
  802.         if (!i)
  803.         {
  804.             i = bskiplf(f);
  805.             if (i == -1) return NULL;
  806.         }
  807.         gotcr = 1;
  808.         continue;
  809.         }
  810.         hdr->value = pstrcat(pool, hdr->value, buffer, NULL);
  811.     }
  812.     else if (gotcr && len == 0) break;
  813.     else
  814.     {
  815.         p = strchr(buffer, ':');
  816.         if (p == NULL)
  817.         {
  818.         /* error!! */
  819.         if (!gotcr)
  820.         {
  821.             i = bskiplf(f);
  822.             if (i == -1) return NULL;
  823.         }
  824.         gotcr = 1;
  825.         hdr = NULL;
  826.         continue;
  827.         }
  828.         hdr = push_array(resp_hdrs);
  829.         *(p++) = '\0';
  830.         hdr->field = pstrdup(pool, buffer);
  831.         while (*p == ' ' || *p == '\t') p++;
  832.         hdr->value = pstrdup(pool, p);
  833.         gotcr = i;
  834.     }
  835.     }
  836.  
  837.     hdr = (struct hdr_entry *)resp_hdrs->elts;
  838.     for (i=0; i < resp_hdrs->nelts; i++)
  839.     {
  840.     p = hdr[i].value;
  841.     j = strlen(p);
  842.     while (j > 0 && (p[j-1] == ' ' || p[j-1] == '\t')) j--;
  843.     p[j] = '\0';
  844.     }
  845.  
  846.     return resp_hdrs;
  847. }
  848.  
  849. static long int
  850. send_fb(BUFF *f, request_rec *r, BUFF *f2, struct cache_req *c)
  851. {
  852.     char buf[IOBUFSIZE];
  853.     long total_bytes_sent;
  854.     register int n,o,w;
  855.     conn_rec *con = r->connection;
  856.     
  857.     total_bytes_sent = 0;
  858.     while (!con->aborted) {
  859.     n = bread(f, buf, IOBUFSIZE);
  860.     if (n == -1) /* input error */
  861.     {
  862.         if (f2 != NULL) f2 = cache_error(c);
  863.         break;
  864.     }
  865.     if (n == 0) break; /* EOF */
  866.         o=0;
  867.     total_bytes_sent += n;
  868.  
  869.     if (f2 != NULL)
  870.         if (bwrite(f2, buf, n) != n) f2 = cache_error(c);
  871.     
  872.         while(n && !r->connection->aborted) {
  873.             w = bwrite(con->client, &buf[o], n);
  874.         if (w <= 0)
  875.         break;
  876.         reset_timeout(r); /* reset timeout after successfule write */
  877.             n-=w;
  878.             o+=w;
  879.         }
  880.     }
  881.     bflush(con->client);
  882.     
  883.     return total_bytes_sent;
  884. }
  885.  
  886. /*
  887.  * Read a header from the array, returning the first entry
  888.  */
  889. static struct hdr_entry *
  890. get_header(array_header *hdrs_arr, const char *name)
  891. {
  892.     struct hdr_entry *hdrs;
  893.     int i;
  894.  
  895.     hdrs = (struct hdr_entry *)hdrs_arr->elts;
  896.     for (i = 0; i < hdrs_arr->nelts; i++)
  897.         if (hdrs[i].field != NULL && strcasecmp(name, hdrs[i].field) == 0)
  898.         return &hdrs[i];
  899.  
  900.     return NULL;
  901. }
  902.  
  903. #define HDR_APP (0)
  904. #define HDR_REP (1)
  905.  
  906. /*
  907.  * Add to the header reply, either concatenating, or replacing existin
  908.  * headers. It stores the pointers provided, so make sure the data
  909.  * is not subsequently overwritten
  910.  */
  911. static struct hdr_entry *
  912. add_header(array_header *hdrs_arr, char *field, char *value,
  913.        int rep)
  914. {
  915.     int i;
  916.     struct hdr_entry *hdrs;
  917.  
  918.     hdrs = (struct hdr_entry *)hdrs_arr->elts;
  919.     if (rep)
  920.     for (i = 0; i < hdrs_arr->nelts; i++)
  921.         if (hdrs[i].field != NULL && strcasecmp(field, hdrs[i].field) == 0)
  922.         {
  923.         hdrs[i].value = value;
  924.         return hdrs;
  925.         }
  926.     
  927.     hdrs = push_array(hdrs_arr);
  928.     hdrs->field = field;
  929.     hdrs->value = value;
  930.  
  931.     return hdrs;
  932. }
  933.  
  934. #ifdef NEEDED
  935. static void
  936. del_header(array_header *hdrs_arr, const char *field)
  937. {
  938.     int i;
  939.     struct hdr_entry *hdrs;
  940.  
  941.     hdrs = (struct hdr_entry *)hdrs_arr->elts;
  942.  
  943.     for (i = 0; i < hdrs_arr->nelts; i++)
  944.     if (hdrs[i].field != NULL && strcasecmp(field, hdrs[i].field) == 0)
  945.         hdrs[i].value = NULL;
  946. }
  947. #endif
  948.  
  949. /*
  950.  * Sends response line and headers
  951.  */
  952. static void
  953. send_headers(BUFF *fp, const char *respline, array_header *hdrs_arr)
  954. {
  955.     struct hdr_entry *hdrs;
  956.     int i;
  957.  
  958.     hdrs = (struct hdr_entry *)hdrs_arr->elts;
  959.  
  960.     bputs(respline, fp);
  961.     bputs("\015\012", fp);
  962.     for (i = 0; i < hdrs_arr->nelts; i++)
  963.     {
  964.         if (hdrs[i].field == NULL) continue;
  965.     bvputs(fp, hdrs[i].field, ": ", hdrs[i].value, "\015\012", NULL);
  966.     }
  967.  
  968.     bputs("\015\012", fp);
  969. }
  970.  
  971.  
  972. /*
  973.  * list is a comma-separated list of case-insensitive tokens, with
  974.  * optional whitespace around the tokens.
  975.  * The return returns 1 if the token val is found in the list, or 0
  976.  * otherwise.
  977.  */
  978. static int
  979. liststr(const char *list, const char *val)
  980. {
  981.     int len, i;
  982.     const char *p;
  983.  
  984.     len = strlen(val);
  985.  
  986.     while (list != NULL)
  987.     {
  988.     p = strchr(list, ',');
  989.     if (p != NULL)
  990.     {
  991.         i = p - list;
  992.         do p++; while (isspace(*p));
  993.     } 
  994.     else
  995.         i = strlen(list);
  996.  
  997.     while (i > 0 && isspace(list[i-1])) i--;
  998.     if (i == len && strncasecmp(list, val, len) == 0) return 1;
  999.     list = p;
  1000.     }
  1001.     return 0;
  1002. }
  1003.  
  1004. /* number of characters in the hash */
  1005. #define HASH_LEN (22*2)
  1006.  
  1007. static void
  1008. hash(const char *it, char *val,int ndepth,int nlength)
  1009. {
  1010.     MD5_CTX context;
  1011.     unsigned char digest[16];
  1012.     char tmp[22];
  1013.     int i, k, d;
  1014.     unsigned int x;
  1015.     static const char table[64]=
  1016. "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_@";
  1017.  
  1018.     MD5Init(&context);
  1019.     MD5Update(&context, (const unsigned char *)it, strlen(it));
  1020.     MD5Final(digest, &context);
  1021.  
  1022. /* encode 128 bits as 22 characters, using a modified uuencoding */
  1023. /* the encoding is 3 bytes -> 4 characters
  1024.  * i.e. 128 bits is 5 x 3 bytes + 1 byte -> 5 * 4 characters + 2 characters
  1025.  */
  1026.     for (i=0, k=0; i < 15; i += 3)
  1027.     {
  1028.     x = (digest[i] << 16) | (digest[i+1] << 8) | digest[i+2];
  1029.     tmp[k++] = table[x >> 18];
  1030.     tmp[k++] = table[(x >> 12) & 0x3f];
  1031.     tmp[k++] = table[(x >> 6) & 0x3f];
  1032.     tmp[k++] = table[x & 0x3f];
  1033.     }
  1034. /* one byte left */
  1035.     x = digest[15];
  1036.     tmp[k++] = table[x >> 2];  /* use up 6 bits */
  1037.     tmp[k++] = table[(x << 4) & 0x3f];
  1038.     /* now split into directory levels */
  1039.  
  1040.     for(i=k=d=0 ; d < ndepth ; ++d)
  1041.     {
  1042.     strncpy(&val[i],&tmp[k],nlength);
  1043.     k+=nlength;
  1044.     val[i+nlength]='/';
  1045.     i+=nlength+1;
  1046.     }
  1047.     memcpy(&val[i],&tmp[k],22-k);
  1048.     val[i+22-k]='\0';
  1049. }
  1050.  
  1051. /*
  1052.  * Compare a string to a mask
  1053.  * Mask characters:
  1054.  *   @ - uppercase letter
  1055.  *   # - lowercase letter
  1056.  *   & - hex digit
  1057.  *   # - digit
  1058.  *   * - swallow remaining characters 
  1059.  *  <x> - exact match for any other character
  1060.  */
  1061. static int
  1062. checkmask(const char *data, const char *mask)
  1063. {
  1064.     int i, ch, d;
  1065.  
  1066.     for (i=0; mask[i] != '\0' && mask[i] != '*'; i++)
  1067.     {
  1068.     ch = mask[i];
  1069.     d = data[i];
  1070.     if (ch == '@')
  1071.     {
  1072.         if (!isupper(d)) return 0;
  1073.     } else if (ch == '$')
  1074.     {
  1075.         if (!islower(d)) return 0;
  1076.     } else if (ch == '#')
  1077.     {
  1078.         if (!isdigit(d)) return 0;
  1079.     } else if (ch == '&')
  1080.     {
  1081.         if (!isxdigit(d)) return 0;
  1082.     } else if (ch != d) return 0;
  1083.     }
  1084.  
  1085.     if (mask[i] == '*') return 1;
  1086.     else return (data[i] == '\0');
  1087. }
  1088.  
  1089. /*
  1090.  * This routine converts a tm structure into the number of seconds
  1091.  * since 1st January 1970 UT
  1092.  * 
  1093.  * The return value is a non-negative integer on success or -1 if the
  1094.  * input date is out of the domain Thu, 01 Jan 1970 00:00:00 to
  1095.  * Tue, 19 Jan 2038 03:14:07 inclusive
  1096.  *
  1097.  * Notes
  1098.  *   This routine has been tested on 1000000 valid dates generated
  1099.  *   at random by gmtime().
  1100.  * 
  1101.  *   This routine is very fast, much faster than mktime().
  1102.  */
  1103. static int
  1104. tm2sec(const struct tm *t)
  1105. {
  1106.     int days, year;
  1107.     static const int dayoffs[12]=
  1108.     {306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275};
  1109.  
  1110.     year = t->tm_year;
  1111. /* shift new year to 1st March; which is where it should be */
  1112.     if (t->tm_mon < 2) year--;  /* now years and months since 1st March 1900 */
  1113.     days = t->tm_mday - 1 + dayoffs[t->tm_mon];
  1114.  
  1115. /* find the number of days since 1st March 1900 (in the Gregorian calendar) */
  1116.     days += year * 365 + year/4 - year/100 + (year/100 + 3)/4;
  1117.     days -= 25508; /* 1 jan 1970 is 25508 days since 1 mar 1900 */
  1118.  
  1119.     days = ((days * 24 + t->tm_hour) * 60 + t->tm_min) * 60 + t->tm_sec;
  1120.     if (year < 69 || year > 138 || days < 0) /* must have overflowed */
  1121.     return -1;
  1122.     else
  1123.     return days;
  1124. }
  1125.  
  1126. /*
  1127.  * Parses a standard HTTP date.
  1128.  * 
  1129.  * The restricted HTTP syntax is
  1130.  *   rfc1123-date = day "," SP 2DIGIT SP date SP time SP "GMT"
  1131.  *   date = 2DIGIT SP month SP 4DIGIT
  1132.  *   time = 2DIGIT ":" 2DIGIT ":" 2DIGIT
  1133.  *
  1134.  *   day = "Mon" | "Tue" | "Wed" | "Thu" | "Fri" | "Sat" | "Sun"
  1135.  *
  1136.  *   month = "Jan" | "Feb" | "Mar" | "Apr" | "May" | "Jun" |
  1137.  *           "Jul" | "Aug" | "Sep" | "Oct" | "Nov" | "Dec"
  1138.  *
  1139.  * The spec is not clear as to whether the day and months are
  1140.  * case-sensitive or not. This code assumes they are.
  1141.  *
  1142.  * It fills in the year, month, mday, hour, min, sec and is_dst fields of
  1143.  * date. It does not set the wday or yday fields.
  1144.  * On failure is sets the year to 0.
  1145.  * 
  1146.  * It also returns the number of seconds since 1 Jan 1970 UT, or
  1147.  * -1 if this would be out of range or if the date is invalid.
  1148.  *
  1149.  * Notes
  1150.  *   This routine has been tested on 100000 valid dates generated
  1151.  *   at random by strftime().
  1152.  * 
  1153.  *   This routine is very fast; it would be 10x slower if it
  1154.  *   used sscanf.
  1155.  */
  1156. static int
  1157. parsedate(const char *date, struct tm *d)
  1158. {
  1159.     int mint, mon, year;
  1160.     struct tm x;
  1161.     const int months[12]={
  1162.     ('J' << 16) | ( 'a' << 8) | 'n', ('F' << 16) | ( 'e' << 8) | 'b',
  1163.     ('M' << 16) | ( 'a' << 8) | 'r', ('A' << 16) | ( 'p' << 8) | 'r',
  1164.     ('M' << 16) | ( 'a' << 8) | 'y', ('J' << 16) | ( 'u' << 8) | 'n',
  1165.     ('J' << 16) | ( 'u' << 8) | 'l', ('A' << 16) | ( 'u' << 8) | 'g',
  1166.     ('S' << 16) | ( 'e' << 8) | 'p', ('O' << 16) | ( 'c' << 8) | 't',
  1167.     ('N' << 16) | ( 'o' << 8) | 'v', ('D' << 16) | ( 'e' << 8) | 'c'};
  1168.     if (d == NULL) d = &x;
  1169.  
  1170.     d->tm_year = 0;  /* bad date */
  1171.     if (!checkmask(date, "@$$, ## @$$ #### ##:##:## GMT")) return -1;
  1172.  
  1173. /* we don't test the weekday */
  1174.     d->tm_mday = (date[5] - '0') * 10 + (date[6] - '0');
  1175.     if (d->tm_mday == 0 || d->tm_mday > 31) return -1;
  1176.  
  1177.     mint = (date[8] << 16) | (date[9] << 8) | date[10];
  1178.     for (mon=0; mon < 12; mon++) if (mint == months[mon]) break;
  1179.     if (mon == 12) return -1;
  1180.     
  1181.     d->tm_mon = mon;
  1182.     year = date[12] * 1000 + date[13] * 100 + date[14] * 10 + date[15] -
  1183.              ('0' * 1111);
  1184.     d->tm_hour = date[17] * 10 + date[18] - '0' * 11;
  1185.     d->tm_min  = date[20] * 10 + date[21] - '0' * 11;
  1186.     d->tm_sec = date[23] * 10 + date[24] - '0' * 11;
  1187.  
  1188.     if (d->tm_hour > 23 || d->tm_min > 59 || d->tm_sec > 61) return -1;
  1189.  
  1190.     if (d->tm_mday == 31 && (mon == 1 || mon == 3 || mon == 5 || mon == 8 ||
  1191.                  mon == 10)) return -1;
  1192.     if (d->tm_mday > 29 && mon == 1) return -1;
  1193.     if (d->tm_mday == 29 && mon == 1)
  1194.     if (year%4 != 0 || (year%100 == 0 && year%400 != 0)) return -1;
  1195.  
  1196.     d->tm_year = year - 1900;
  1197.     d->tm_isdst = 0;
  1198.     return tm2sec(d);
  1199. }
  1200.  
  1201. /*
  1202.  * Converts 8 hex digits to a time integer
  1203.  */
  1204. static int
  1205. hex2sec(const char *x)
  1206. {
  1207.     int i, ch;
  1208.     unsigned int j;
  1209.  
  1210.     for (i=0, j=0; i < 8; i++)
  1211.     {
  1212.     ch = x[i];
  1213.     j <<= 4;
  1214.     if (isdigit(ch)) j |= ch - '0';
  1215.     else if (isupper(ch)) j |= ch - ('A' - 10);
  1216.     else j |= ch - ('a' - 10);
  1217.     }
  1218.     if (j == 0xffffffff) return -1;  /* so that it works with 8-byte ints */
  1219.     else return j;
  1220. }
  1221.  
  1222. /*
  1223.  * Converts a time integer to 8 hex digits
  1224.  */
  1225. static void
  1226. sec2hex(int t, char *y)
  1227. {
  1228.     int i, ch;
  1229.     unsigned int j=t;
  1230.  
  1231.     for (i=7; i >= 0; i--)
  1232.     {
  1233.     ch = j & 0xF;
  1234.     j >>= 4;
  1235.     if (ch >= 10) y[i] = ch + ('A' - 10);
  1236.     else y[i] = ch + '0';
  1237.     }
  1238.     y[8] = '\0';
  1239. }
  1240.  
  1241.  
  1242. static void
  1243. log_uerror(const char *routine, const char *file, const char *err,
  1244.        server_rec *s)
  1245. {
  1246.     char *p, *q;
  1247.  
  1248.     q = get_time();
  1249.     p = strerror(errno);
  1250.  
  1251.     if (err != NULL)
  1252.     {
  1253.     fprintf(s->error_log, "[%s] %s\n", q, err);
  1254.     if (file != NULL)
  1255.         fprintf(s->error_log, "- %s: %s: %s\n", routine, file, p);
  1256.     else
  1257.         fprintf(s->error_log, "- %s: %s\n", routine, p);
  1258.     } else
  1259.     {
  1260.     if (file != NULL)
  1261.         fprintf(s->error_log, "[%s] %s: %s: %s\n", q, routine, file, p);
  1262.     else
  1263.         fprintf(s->error_log, "[%s] %s: %s\n", q, routine, p);
  1264.     }
  1265.  
  1266.     fflush(s->error_log);
  1267. }
  1268.  
  1269. struct gc_ent
  1270. {
  1271.     unsigned long int len;
  1272.     time_t expire;
  1273.     char file[HASH_LEN+1];
  1274.  
  1275. };
  1276.  
  1277. static int
  1278. gcdiff(const void *ap, const void *bp)
  1279. {
  1280.     const struct gc_ent *a=*(struct gc_ent **)ap, *b=*(struct gc_ent **)bp;
  1281.  
  1282.     if (a->expire > b->expire) return 1;
  1283.     else if (a->expire < b->expire) return -1;
  1284.     else return 0;
  1285. }
  1286.  
  1287. static int curbytes, cachesize, every;
  1288. static unsigned long int curblocks;
  1289. static time_t now, expire;
  1290. static char *filename;
  1291.  
  1292. static int sub_garbage_coll(request_rec *r,array_header *files,
  1293.                 const char *cachedir,const char *cachesubdir);
  1294.  
  1295. static void garbage_coll(request_rec *r)
  1296.     {
  1297.     const char *cachedir;
  1298.     void *sconf = r->server->module_config;
  1299.     proxy_server_conf *pconf =
  1300.         (proxy_server_conf *)get_module_config(sconf, &proxy_module);
  1301.     const struct cache_conf *conf=&pconf->cache;
  1302.     array_header *files;
  1303.     struct stat buf;
  1304.     struct gc_ent *fent,**elts;    
  1305.     int i;
  1306.     static time_t lastcheck=-1;  /* static data!!! */
  1307.  
  1308.     cachedir = conf->root;
  1309.     cachesize = conf->space;
  1310.     every = conf->gcinterval;
  1311.  
  1312.     if (cachedir == NULL || every == -1) return;
  1313.     now = time(NULL);
  1314.     if (now != -1 && lastcheck != -1 && now < lastcheck + every) return;
  1315.  
  1316.     block_alarms();    /* avoid SIGALRM on big cache cleanup */
  1317.  
  1318.     filename = palloc(r->pool, strlen(cachedir) + HASH_LEN + 2);
  1319.     strcpy(filename, cachedir);
  1320.     strcat(filename, "/.time");
  1321.     if (stat(filename, &buf) == -1) /* does not exist */
  1322.     {
  1323.     if (errno != ENOENT)
  1324.     {
  1325.         log_uerror("stat", filename, NULL, r->server);
  1326.         return;
  1327.     }
  1328.     if (creat(filename, 0666) == -1)
  1329.     {
  1330.         if (errno != EEXIST)
  1331.         log_uerror("creat", filename, NULL, r->server);
  1332.         else
  1333.         lastcheck = now;  /* someone else got in there */
  1334.         return;
  1335.     }
  1336.     } else
  1337.     {
  1338.     lastcheck = buf.st_mtime;  /* save the time */
  1339.     if (now < lastcheck + every) return;
  1340.     if (utime(filename, NULL) == -1)
  1341.         log_uerror("utimes", filename, NULL, r->server);
  1342.     }
  1343.     files = make_array(r->pool, 100, sizeof(struct gc_ent *));
  1344.     curblocks = 0;
  1345.     curbytes = 0;
  1346.  
  1347.     sub_garbage_coll(r,files,cachedir,"/");
  1348.  
  1349.     if (curblocks < cachesize || curblocks + curbytes <= cachesize)
  1350.     return;
  1351.  
  1352.     qsort(files->elts, files->nelts, sizeof(struct gc_ent *), gcdiff);
  1353.  
  1354.     elts = (struct gc_ent **)files->elts;
  1355.     for (i=0; i < files->nelts; i++)
  1356.     {
  1357.     fent = elts[i];
  1358.     sprintf(filename, "%s%s", cachedir, fent->file);
  1359.     Explain3("GC Unlinking %s (expiry %ld, now %ld)",filename,fent->expire,now);
  1360. #if TESTING
  1361.     fprintf(stderr,"Would unlink %s\n",filename);
  1362. #else
  1363.     if (unlink(filename) == -1)
  1364.     {
  1365.         if (errno != ENOENT)
  1366.         log_uerror("unlink", filename, NULL, r->server);
  1367.     }
  1368.     else
  1369. #endif
  1370.     {
  1371.         curblocks -= fent->len >> 10;
  1372.         curbytes -= fent->len & 0x3FF;
  1373.         if (curbytes < 0)
  1374.         {
  1375.         curbytes += 1024;
  1376.         curblocks--;
  1377.         }
  1378.         if (curblocks < cachesize || curblocks + curbytes <= cachesize)
  1379.         break;
  1380.     }
  1381.     }
  1382.     unblock_alarms();
  1383. }
  1384.  
  1385. static int sub_garbage_coll(request_rec *r,array_header *files,
  1386.                  const char *cachebasedir,const char *cachesubdir)
  1387. {
  1388.     char line[27];
  1389.     char cachedir[HUGE_STRING_LEN];
  1390.     struct stat buf;
  1391.     int fd,i;
  1392.     DIR *dir;
  1393. #if defined(NEXT)
  1394.     struct DIR_TYPE *ent;
  1395. #else
  1396.     struct dirent *ent;
  1397. #endif
  1398.     struct gc_ent *fent;
  1399.     int nfiles=0;
  1400.  
  1401.     sprintf(cachedir,"%s%s",cachebasedir,cachesubdir);
  1402.     Explain1("GC Examining directory %s",cachedir);
  1403.     dir = opendir(cachedir);
  1404.     if (dir == NULL)
  1405.     {
  1406.     log_uerror("opendir", cachedir, NULL, r->server);
  1407.     return 0;
  1408.     }
  1409.  
  1410.     while ((ent = readdir(dir)) != NULL)
  1411.     {
  1412.     if (ent->d_name[0] == '.') continue;
  1413.     sprintf(filename, "%s%s", cachedir, ent->d_name);
  1414.     Explain1("GC Examining file %s",filename);
  1415. /* is it a temporary file? */
  1416.     if (strncmp(ent->d_name, "#tmp", 4) == 0)
  1417.     {
  1418. /* then stat it to see how old it is; delete temporary files > 1 day old */
  1419.         if (stat(filename, &buf) == -1)
  1420.         {
  1421.         if (errno != ENOENT)
  1422.             log_uerror("stat", filename, NULL, r->server);
  1423.         } else if (now != -1 && buf.st_atime < now - SEC_ONE_DAY &&
  1424.                buf.st_mtime < now - SEC_ONE_DAY)
  1425.         {
  1426.         Explain1("GC unlink %s",filename);
  1427. #if TESTING
  1428.         fprintf(stderr,"Would unlink %s\n",filename);
  1429. #else
  1430.         unlink(filename);
  1431. #endif
  1432.         }
  1433.         continue;
  1434.     }
  1435.     ++nfiles;
  1436. /* is it another file? */
  1437.     /* FIXME: Shouldn't any unexpected files be deleted? */
  1438.     /*    if (strlen(ent->d_name) != HASH_LEN) continue; */
  1439.  
  1440. /* read the file */
  1441.     fd = open(filename, O_RDONLY);
  1442.     if (fd == -1)
  1443.     {
  1444.         if (errno  != ENOENT) log_uerror("open", filename,NULL, r->server);
  1445.         continue;
  1446.     }
  1447.     if (fstat(fd, &buf) == -1)
  1448.     {
  1449.         log_uerror("fstat", filename, NULL, r->server);
  1450.         close(fd);
  1451.         continue;
  1452.     }
  1453.     if(S_ISDIR(buf.st_mode))
  1454.         {
  1455.         char newcachedir[HUGE_STRING_LEN];
  1456.         close(fd);
  1457.         sprintf(newcachedir,"%s%s/",cachesubdir,ent->d_name);
  1458.         if(!sub_garbage_coll(r,files,cachebasedir,newcachedir))
  1459.         {
  1460.         sprintf(newcachedir,"%s%s",cachedir,ent->d_name);
  1461. #if TESTING
  1462.         fprintf(stderr,"Would remove directory %s\n",newcachedir);
  1463. #else
  1464.         rmdir(newcachedir);
  1465. #endif
  1466.         --nfiles;
  1467.         }
  1468.         continue;
  1469.         }
  1470.         
  1471.     i = read(fd, line, 26);
  1472.     if (i == -1)
  1473.     {
  1474.         log_uerror("read", filename, NULL, r->server);
  1475.         close(fd);
  1476.         continue;
  1477.     }
  1478.     close(fd);
  1479.     line[i] = '\0';
  1480.     expire = hex2sec(line+18);
  1481.     if (!checkmask(line, "&&&&&&&& &&&&&&&& &&&&&&&&") || expire == -1)
  1482.     {
  1483.         /* bad file */
  1484.         if (now != -1 && buf.st_atime > now + SEC_ONE_DAY &&
  1485.         buf.st_mtime > now + SEC_ONE_DAY)
  1486.         {
  1487.         log_error("proxy: deleting bad cache file", r->server);
  1488. #if TESTING
  1489.         fprintf(stderr,"Would unlink bad file %s\n",filename);
  1490. #else
  1491.         unlink(filename);
  1492. #endif
  1493.         }
  1494.         continue;
  1495.     }
  1496.  
  1497. /*
  1498.  * we need to calculate an 'old' factor, and remove the 'oldest' files
  1499.  * so that the space requirement is met; sort by the expires date of the
  1500.  * file.
  1501.  *
  1502.  */
  1503.     /* FIXME: We should make the array an array of gc_ents, not gc_ent *s
  1504.      */
  1505.     fent = palloc(r->pool, sizeof(struct gc_ent));
  1506.     fent->len = buf.st_size;
  1507.     fent->expire = expire;
  1508.     strcpy(fent->file,cachesubdir);
  1509.     strcat(fent->file, ent->d_name);
  1510.     *(struct gc_ent **)push_array(files) = fent;
  1511.  
  1512. /* accumulate in blocks, to cope with directories > 4Gb */
  1513.     curblocks += buf.st_size >> 10; /* Kbytes */
  1514.     curbytes += buf.st_size & 0x3FF;
  1515.     if (curbytes >= 1024)
  1516.     {
  1517.         curbytes -= 1024;
  1518.         curblocks++;
  1519.     }
  1520.     }
  1521.  
  1522.     closedir(dir);
  1523.  
  1524.     return nfiles;
  1525.  
  1526. }
  1527.  
  1528. /*
  1529.  * read a cache file;
  1530.  * returns 1 on success,
  1531.  *         0 on failure (bad file or wrong URL)
  1532.  *        -1 on UNIX error
  1533.  */
  1534. static int
  1535. rdcache(pool *pool, BUFF *cachefp, struct cache_req *c)
  1536. {
  1537.     char urlbuff[1034], *p;
  1538.     int len;
  1539. /* read the data from the cache file */
  1540. /* format
  1541.  * date SP lastmod SP expire SP count SP content-length CRLF
  1542.  * dates are stored as hex seconds since 1970
  1543.  */
  1544.     len = bgets(urlbuff, 1034, cachefp);
  1545.     if (len == -1) return -1;
  1546.     if (len == 0 || urlbuff[len-1] != '\n') return 0;
  1547.     urlbuff[len-1] = '\0';
  1548.  
  1549.     if (!checkmask(urlbuff, "&&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&&"))
  1550.     return 0;
  1551.  
  1552.     c->date = hex2sec(urlbuff);
  1553.     c->lmod = hex2sec(urlbuff+9);
  1554.     c->expire = hex2sec(urlbuff+18);
  1555.     c->version = hex2sec(urlbuff+27);
  1556.     c->len = hex2sec(urlbuff+36);
  1557.  
  1558. /* check that we have the same URL */
  1559.     len = bgets(urlbuff, 1034, cachefp);
  1560.     if (len == -1) return -1;
  1561.     if (len == 0 || strncmp(urlbuff, "X-URL: ", 7) != 0 ||
  1562.     urlbuff[len-1] != '\n')
  1563.     return 0;
  1564.     urlbuff[len-1] = '\0';
  1565.     if (strcmp(urlbuff+7, c->url) != 0) return 0;
  1566.  
  1567. /* What follows is the message */
  1568.     len = bgets(urlbuff, 1034, cachefp);
  1569.     if (len == -1) return -1;
  1570.     if (len == 0 || urlbuff[len-1] != '\n') return 0;
  1571.     urlbuff[--len] = '\0';
  1572.  
  1573.     c->resp_line = pstrdup(pool, urlbuff);
  1574.     p = strchr(urlbuff, ' ');
  1575.     if (p == NULL) return 0;
  1576.  
  1577.     c->status = atoi(p);
  1578.     c->hdrs = read_headers(pool, urlbuff, 1034, cachefp);
  1579.     if (c->hdrs == NULL) return -1;
  1580.     if (c->len != -1) /* add a content-length header */
  1581.     {
  1582.     struct hdr_entry *q;
  1583.     q = get_header(c->hdrs, "Content-Length");
  1584.     if (q == NULL)
  1585.     {
  1586.         p = palloc(pool, 15);
  1587.         sprintf(p, "%u", c->len);
  1588.         add_header(c->hdrs, "Content-Length", p, HDR_REP);
  1589.     }
  1590.     }
  1591.     return 1;
  1592. }
  1593.  
  1594.  
  1595. /*
  1596.  * Call this to test for a resource in the cache
  1597.  * Returns DECLINED if we need to check the remote host
  1598.  * or an HTTP status code if successful
  1599.  *
  1600.  * Functions:
  1601.  *   if URL is cached then
  1602.  *      if cached file is not expired then
  1603.  *         if last modified after if-modified-since then send body
  1604.  *         else send 304 Not modified
  1605.  *      else
  1606.  *         if last modified after if-modified-since then add
  1607.  *            last modified date to request
  1608.  */
  1609. static int
  1610. cache_check(request_rec *r, char *url, struct cache_conf *conf,
  1611.          struct cache_req **cr)
  1612. {
  1613.     char hashfile[33], *imstr, *pragma, *p, *auth;
  1614.     struct cache_req *c;
  1615.     time_t now;
  1616.     BUFF *cachefp;
  1617.     int cfd, i;
  1618.     const long int zero=0L;
  1619.     void *sconf = r->server->module_config;
  1620.     proxy_server_conf *pconf =
  1621.         (proxy_server_conf *)get_module_config(sconf, &proxy_module);
  1622.  
  1623.     c = pcalloc(r->pool, sizeof(struct cache_req));
  1624.     *cr = c;
  1625.     c->req = r;
  1626.     c->url = pstrdup(r->pool, url);
  1627.  
  1628. /* get the If-Modified-Since date of the request */
  1629.     c->ims = -1;
  1630.     imstr = table_get(r->headers_in, "If-Modified-Since");
  1631.     if (imstr != NULL)
  1632.     {
  1633. /* this may modify the value in the original table */
  1634.     imstr = date_canon(r->pool, imstr);
  1635.     c->ims = parsedate(imstr, NULL);
  1636.     if (c->ims == -1)  /* bad or out of range date; remove it */
  1637.         table_set(r->headers_in, "If-Modified-Since", NULL);
  1638.     }
  1639.  
  1640. /* find the filename for this cache entry */
  1641.     hash(url, hashfile,pconf->cache.dirlevels,pconf->cache.dirlength);
  1642.     if (conf->root != NULL)
  1643.     c->filename = pstrcat(r->pool, conf->root, "/", hashfile, NULL);
  1644.     else
  1645.     c->filename = NULL;
  1646.  
  1647.     cachefp = NULL;
  1648. /* find out about whether the request can access the cache */
  1649.     pragma = table_get(r->headers_in, "Pragma");
  1650.     auth = table_get(r->headers_in, "Authorization");
  1651.     Explain4("Request for %s, pragma=%s, auth=%s, ims=%ld",url,pragma,auth,c->ims);
  1652.     if (c->filename != NULL && r->method_number == M_GET &&
  1653.     strlen(url) < 1024 && !liststr(pragma, "no-cache") && auth == NULL)
  1654.     {
  1655.         Explain1("Check file %s",c->filename);
  1656.     cfd = open(c->filename, O_RDWR);
  1657.     if (cfd != -1)
  1658.     {
  1659.         note_cleanups_for_fd(r->pool, cfd);
  1660.         cachefp = bcreate(r->pool, B_RD | B_WR);
  1661.         bpushfd(cachefp, cfd, cfd);
  1662.     } else if (errno != ENOENT)
  1663.         log_uerror("open", c->filename, "proxy: error opening cache file",
  1664.                r->server);
  1665.     else
  1666.         Explain1("File %s not found",c->filename);
  1667.     }
  1668.     
  1669.     if (cachefp != NULL)
  1670.     {
  1671.     i = rdcache(r->pool, cachefp, c);
  1672.     if (i == -1)
  1673.         log_uerror("read", c->filename, "proxy: error reading cache file",
  1674.                r->server);
  1675.     else if (i == 0)
  1676.         log_error("proxy: bad cache file", r->server);
  1677.     if (i != 1)
  1678.     {
  1679.         pclosef(r->pool, cachefp->fd);
  1680.         cachefp = NULL;
  1681.     }
  1682.     }
  1683.     if (cachefp == NULL)
  1684.     c->hdrs = make_array(r->pool, 2, sizeof(struct hdr_entry));
  1685.     /* FIXME: Shouldn't we check the URL somewhere? */
  1686.     now = time(NULL);
  1687. /* Ok, have we got some un-expired data? */
  1688.     if (cachefp != NULL && c->expire != -1 && now < c->expire)
  1689.     {
  1690.         Explain0("Unexpired data available");
  1691. /* check IMS */
  1692.     if (c->lmod != -1 && c->ims != -1 && c->ims >= c->lmod)
  1693.     {
  1694. /* has the cached file changed since this request? */
  1695.         if (c->date == -1 || c->date > c->ims)
  1696.         {
  1697. /* No, but these header values may have changed, so we send them with the
  1698.  * 304 response
  1699.  */
  1700.         /* CHECKME: surely this was wrong? (Ben)
  1701.         p = table_get(r->headers_in, "Expires");
  1702.         */
  1703.         p = table_get(c->hdrs, "Expires");
  1704.         if (p != NULL)     table_set(r->headers_out, "Expires", p);
  1705.         }
  1706.         pclosef(r->pool, cachefp->fd);
  1707.         Explain0("Use local copy, cached file hasn't changed");
  1708.         return USE_LOCAL_COPY;
  1709.     }
  1710.  
  1711. /* Ok, has been modified */
  1712.     Explain0("Local copy modified, send it");
  1713.     r->status_line = strchr(c->resp_line, ' ') + 1;
  1714.     r->status = c->status;
  1715.     soft_timeout ("send", r);
  1716.     if (!r->assbackwards)
  1717.         send_headers(r->connection->client, c->resp_line,  c->hdrs);
  1718.     bsetopt(r->connection->client, BO_BYTECT, &zero);
  1719.     r->sent_bodyct = 1;
  1720.     if (!r->header_only) send_fb (cachefp, r, NULL, NULL);
  1721.     pclosef(r->pool, cachefp->fd);
  1722.     return OK;
  1723.     }
  1724.  
  1725. /* if we already have data and a last-modified date, and it is not a head
  1726.  * request, then add an If-Modified-Since
  1727.  */
  1728.  
  1729.     if (cachefp != NULL && c->lmod != -1 && !r->header_only)
  1730.     {
  1731. /*
  1732.  * use the later of the one from the request and the last-modified date
  1733.  * from the cache
  1734.  */
  1735.     if (c->ims == -1 || c->ims < c->lmod)
  1736.     {
  1737.         struct hdr_entry *q;
  1738.  
  1739.         q = get_header(c->hdrs, "Last-Modified");
  1740.  
  1741.         if (q != NULL && q->value != NULL)
  1742.         table_set(r->headers_in, "If-Modified-Since",
  1743.               (char *)q->value);
  1744.     }
  1745.     }
  1746.     c->fp = cachefp;
  1747.  
  1748.     Explain0("Local copy not present or expired. Declining.");
  1749.  
  1750.     return DECLINED;
  1751. }
  1752.  
  1753. /*
  1754.  * Having read the response from the client, decide what to do
  1755.  * If the response is not cachable, then delete any previously cached
  1756.  * response, and copy data from remote server to client.
  1757.  * Functions:
  1758.  *  parse dates
  1759.  *  check for an uncachable response
  1760.  *  calculate an expiry date, if one is not provided
  1761.  *  if the remote file has not been modified, then return the document
  1762.  *  from the cache, maybe updating the header line
  1763.  *  otherwise, delete the old cached file and open a new temporary file
  1764.  */
  1765. static int
  1766. cache_update(struct cache_req *c, array_header *resp_hdrs,
  1767.          const char *protocol, int nocache)
  1768. {
  1769.     request_rec *r=c->req;
  1770.     char *p;
  1771.     int i;
  1772.     struct hdr_entry *expire, *dates, *lmods, *clen;
  1773.     time_t expc, date, lmod, now;
  1774.     char buff[46];
  1775.     void *sconf = r->server->module_config;
  1776.     proxy_server_conf *conf =
  1777.         (proxy_server_conf *)get_module_config(sconf, &proxy_module);
  1778.     const long int zero=0L;
  1779.  
  1780.     c->tempfile = NULL;
  1781.  
  1782. /* we've received the response */
  1783. /* read expiry date; if a bad date, then leave it so the client can
  1784.  * read it
  1785.  */
  1786.     expire = get_header(resp_hdrs, "Expire");
  1787.     if (expire != NULL) expc = parsedate(expire->value, NULL);
  1788.     else expc = -1;
  1789.  
  1790. /*
  1791.  * read the last-modified date; if the date is bad, then delete it
  1792.  */
  1793.     lmods = get_header(resp_hdrs, "Last-Modified");
  1794.     if (lmods != NULL)
  1795.     {
  1796.     lmod = parsedate(lmods->value, NULL);
  1797.     if (lmod == -1)
  1798.     {
  1799. /* kill last modified date */
  1800.         lmods->value = NULL;
  1801.         lmods = NULL;
  1802.     }
  1803.     } else
  1804.     lmod = -1;
  1805.  
  1806. /*
  1807.  * what responses should we not cache?
  1808.  * Unknown status responses and those known to be uncacheable
  1809.  * 304 response when we have no valid cache file, or
  1810.  * 200 response from HTTP/1.0 and up without a Last-Modified header, or
  1811.  * HEAD requests, or
  1812.  * requests with an Authorization header, or
  1813.  * protocol requests nocache (e.g. ftp with user/password)
  1814.  */
  1815.     if ((r->status != 200 && r->status != 301 && r->status != 304) ||
  1816.     (expire != NULL && expc == -1) ||
  1817.     (r->status == 304 && c->fp == NULL) ||
  1818.     (r->status == 200 && lmods == NULL &&
  1819.                          strncmp(protocol, "HTTP/1.", 7) == 0) ||
  1820.     r->header_only ||
  1821.     table_get(r->headers_in, "Authorization") != NULL ||
  1822.     nocache)
  1823.     {
  1824.     Explain1("Response is not cacheable, unlinking %s",c->filename);
  1825. /* close the file */
  1826.     if (c->fp != NULL)
  1827.     {
  1828.         pclosef(r->pool, c->fp->fd);
  1829.         c->fp = NULL;
  1830.     }
  1831. /* delete the previously cached file */
  1832.     unlink(c->filename);
  1833.     return DECLINED; /* send data to client but not cache */
  1834.     }
  1835.  
  1836. /* otherwise, we are going to cache the response */
  1837. /*
  1838.  * Read the date. Generate one if one is not supplied
  1839.  */
  1840.     dates = get_header(resp_hdrs, "Date");
  1841.     if (dates != NULL) date = parsedate(dates->value, NULL);
  1842.     else date = -1;
  1843.     
  1844.     now = time(NULL);
  1845.  
  1846.     if (date == -1) /* No, or bad date */
  1847.     {
  1848. /* no date header! */
  1849. /* add one; N.B. use the time _now_ rather than when we were checking the cache
  1850.  */
  1851.     date = now;
  1852.     p = gm_timestr_822(r->pool, now);
  1853.     dates = add_header(resp_hdrs, "Date", p, HDR_REP);
  1854.     Explain0("Added date header");
  1855.     }
  1856.  
  1857. /* check last-modified date */
  1858.     if (lmod != -1 && lmod > date)
  1859. /* if its in the future, then replace by date */
  1860.     {
  1861.     lmod = date;
  1862.     lmods->value = dates->value;
  1863.     Explain0("Last modified is in the future, replacing with now");
  1864.     }
  1865. /* if the response did not contain the header, then use the cached version */
  1866.     if (lmod == -1 && c->fp != NULL)
  1867.     {
  1868.     lmod = c->lmod;
  1869.     Explain0("Reusing cached last modified");
  1870.     }
  1871.  
  1872. /* we now need to calculate the expire data for the object. */
  1873.     if (expire == NULL && c->fp != NULL)  /* no expiry data sent in response */
  1874.     {
  1875.     expire = get_header(c->hdrs, "Expires");
  1876.     if (expire != NULL) expc = parsedate(expire->value, NULL);
  1877.     }
  1878. /* so we now have the expiry date */
  1879. /* if no expiry date then
  1880.  *   if lastmod
  1881.  *      expiry date = now + min((date - lastmod) * factor, maxexpire)
  1882.  *   else
  1883.  *      expire date = now + defaultexpire
  1884.  */
  1885.     Explain1("Expiry date is %ld",expc);
  1886.     if (expc == -1)
  1887.     {
  1888.     if (lmod != -1)
  1889.     {
  1890.         double x = (double)(date - lmod)*conf->cache.lmfactor;
  1891.         double maxex=conf->cache.maxexpire;
  1892.         if (x > maxex) x = maxex;
  1893.         expc = now + (int)x;
  1894.     } else
  1895.         expc = now + conf->cache.defaultexpire;
  1896.     Explain1("Expiry date calculated %ld",expc);
  1897.     }
  1898.  
  1899. /* get the content-length header */
  1900.     clen = get_header(c->hdrs, "Content-Length");
  1901.     if (clen == NULL) c->len = -1;
  1902.     else c->len = atoi(clen->value);
  1903.  
  1904.     sec2hex(date, buff);
  1905.     buff[8] = ' ';
  1906.     sec2hex(lmod, buff+9);
  1907.     buff[17] = ' ';
  1908.     sec2hex(expc, buff+18);
  1909.     buff[26] = ' ';
  1910.     sec2hex(c->version++, buff+27);
  1911.     buff[35] = ' ';
  1912.     sec2hex(c->len, buff+36);
  1913.     buff[44] = '\n';
  1914.     buff[45] = '\0';
  1915.  
  1916. /* if file not modified */
  1917.     if (r->status == 304)
  1918.     {
  1919.     if (c->ims != -1 && lmod != -1 && lmod <= c->ims)
  1920.     {
  1921. /* set any changed headers somehow */
  1922. /* update dates and version, but not content-length */
  1923.         if (lmod != c->lmod || expc != c->expire || date != c->date)
  1924.         {
  1925.         off_t curpos=lseek(c->fp->fd, 0, SEEK_SET);
  1926.         if (curpos == -1)
  1927.             log_uerror("lseek", c->filename,
  1928.                    "proxy: error seeking on cache file",r->server);
  1929.         else if (write(c->fp->fd, buff, 35) == -1)
  1930.             log_uerror("write", c->filename,
  1931.                    "proxy: error updating cache file", r->server);
  1932.         }
  1933.         pclosef(r->pool, c->fp->fd);
  1934.         Explain0("Remote document not modified, use local copy");
  1935.         /* CHECKME: Is this right? Shouldn't we check IMS again here? */
  1936.         return USE_LOCAL_COPY;
  1937.     } else
  1938.     {
  1939. /* return the whole document */
  1940.         Explain0("Remote document updated, sending");
  1941.         r->status_line = strchr(c->resp_line, ' ') + 1;
  1942.         r->status = c->status;
  1943.         soft_timeout ("send", r);
  1944.         if (!r->assbackwards)
  1945.         send_headers(r->connection->client, c->resp_line,  c->hdrs);
  1946.         bsetopt(r->connection->client, BO_BYTECT, &zero);
  1947.         r->sent_bodyct = 1;
  1948.         if (!r->header_only) send_fb (c->fp, r, NULL, NULL);
  1949. /* set any changed headers somehow */
  1950. /* update dates and version, but not content-length */
  1951.         if (lmod != c->lmod || expc != c->expire || date != c->date)
  1952.         {
  1953.         off_t curpos=lseek(c->fp->fd, 0, SEEK_SET);
  1954.  
  1955.         if (curpos == -1)
  1956.             log_uerror("lseek", c->filename,
  1957.                    "proxy: error seeking on cache file",r->server);
  1958.         else if (write(c->fp->fd, buff, 35) == -1)
  1959.             log_uerror("write", c->filename,
  1960.                    "proxy: error updating cache file", r->server);
  1961.         }
  1962.         pclosef(r->pool, c->fp->fd);
  1963.         return OK;
  1964.     }
  1965.     }
  1966. /* new or modified file */        
  1967.     if (c->fp != NULL)
  1968.     {
  1969.     pclosef(r->pool, c->fp->fd);
  1970.     c->fp->fd = -1;
  1971.     }
  1972.     c->version = 0;
  1973.     sec2hex(0, buff+27);
  1974.     buff[35] = ' ';
  1975.  
  1976. /* open temporary file */
  1977. #define TMPFILESTR    "/#tmpXXXXXX"
  1978.     c->tempfile=palloc(r->pool,strlen(conf->cache.root)+sizeof TMPFILESTR-1);
  1979.     strcpy(c->tempfile,conf->cache.root);
  1980.     /*
  1981.     p = strrchr(c->tempfile, '/');
  1982.     if (p == NULL) return DECLINED;
  1983.     strcpy(p, TMPFILESTR);
  1984.     */
  1985.     strcat(c->tempfile,TMPFILESTR);
  1986. #undef TMPFILESTR
  1987.     p = mktemp(c->tempfile);
  1988.     if (p == NULL) return DECLINED;
  1989.  
  1990.     Explain1("Create temporary file %s",c->tempfile);
  1991.  
  1992.     i = open(c->tempfile, O_WRONLY | O_CREAT | O_EXCL, 0622);
  1993.     if (i == -1)
  1994.     {
  1995.     log_uerror("open", c->tempfile, "proxy: error creating cache file",
  1996.            r->server);
  1997.     return DECLINED;
  1998.     }
  1999.     note_cleanups_for_fd(r->pool, i);
  2000.     c->fp = bcreate(r->pool, B_WR);
  2001.     bpushfd(c->fp, -1, i);
  2002.  
  2003.     if (bvputs(c->fp, buff, "X-URL: ", c->url, "\n", NULL) == -1)
  2004.     {
  2005.     log_uerror("write", c->tempfile, "proxy: error writing cache file",
  2006.            r->server);
  2007.     pclosef(r->pool, c->fp->fd);
  2008.     unlink(c->tempfile);
  2009.     c->fp = NULL;
  2010.     }
  2011.     return DECLINED;
  2012. }
  2013.  
  2014. static void
  2015. cache_tidy(struct cache_req *c)
  2016. {
  2017.     server_rec *s=c->req->server;
  2018.     long int bc;
  2019.  
  2020.     if (c->fp == NULL) return;
  2021.  
  2022.     bgetopt(c->req->connection->client, BO_BYTECT, &bc);
  2023.  
  2024.     if (c->len != -1)
  2025.     {
  2026. /* file lengths don't match; don't cache it */
  2027.     if (bc != c->len)
  2028.     {
  2029.         pclosef(c->req->pool, c->fp->fd);  /* no need to flush */
  2030.         unlink(c->tempfile);
  2031.         return;
  2032.     }
  2033.     } else
  2034.     {
  2035. /* update content-length of file */
  2036.     char buff[9];
  2037.     off_t curpos;
  2038.  
  2039.     c->len = bc;
  2040.     bflush(c->fp);
  2041.     sec2hex(c->len, buff);
  2042.     curpos = lseek(c->fp->fd, 36, SEEK_SET);
  2043.     if (curpos == -1)
  2044.         log_uerror("lseek", c->tempfile,
  2045.                "proxy: error seeking on cache file", s);
  2046.     else if (write(c->fp->fd, buff, 8) == -1)
  2047.         log_uerror("write", c->tempfile,
  2048.                "proxy: error updating cache file", s);
  2049.     }
  2050.  
  2051.     if (bflush(c->fp) == -1)
  2052.     {
  2053.     log_uerror("write", c->tempfile, "proxy: error writing to cache file",
  2054.            s);
  2055.     pclosef(c->req->pool, c->fp->fd);
  2056.     unlink(c->tempfile);
  2057.     return;
  2058.     }
  2059.  
  2060.     if (pclosef(c->req->pool, c->fp->fd) == -1)
  2061.     {
  2062.     log_uerror("close", c->tempfile, "proxy: error closing cache file", s);
  2063.     unlink(c->tempfile);
  2064.     return;
  2065.     }
  2066.  
  2067.     if (unlink(c->filename) == -1 && errno != ENOENT)
  2068.     {
  2069.     log_uerror("unlink", c->filename,
  2070.            "proxy: error deleting old cache file", s);
  2071.     } else
  2072.     {
  2073.     char *p;
  2074.     proxy_server_conf *conf=
  2075.       (proxy_server_conf *)get_module_config(s->module_config,&proxy_module);
  2076.  
  2077.     for(p=c->filename+strlen(conf->cache.root)+1 ; ; )
  2078.         {
  2079.         p=strchr(p,'/');
  2080.         if(!p)
  2081.         break;
  2082.         *p='\0';
  2083.         if(mkdir(c->filename,S_IREAD|S_IWRITE|S_IEXEC) < 0 && errno != EEXIST)
  2084.         log_uerror("mkdir",c->filename,"proxy: error creating cache directory",s);
  2085.         *p='/';
  2086.         ++p;
  2087.         }
  2088. #ifdef __EMX__
  2089.         /* Under OS/2 use rename. */            
  2090.         if (rename(c->tempfile, c->filename) == -1)
  2091.             log_uerror("rename", c->filename, "proxy: error renaming cache file", s);
  2092. }
  2093. #else            
  2094.  
  2095.     if (link(c->tempfile, c->filename) == -1)
  2096.         log_uerror("link", c->filename, "proxy: error linking cache file", s);
  2097.     }
  2098.  
  2099.     if (unlink(c->tempfile) == -1)
  2100.     log_uerror("unlink", c->tempfile, "proxy: error deleting temp file",s);
  2101. #endif
  2102.  
  2103.     garbage_coll(c->req);
  2104. }
  2105.  
  2106. static BUFF *
  2107. cache_error(struct cache_req *c)
  2108. {
  2109.     log_uerror("write", c->tempfile, "proxy: error writing to cache file",
  2110.            c->req->server);
  2111.     pclosef(c->req->pool, c->fp->fd);
  2112.     c->fp = NULL;
  2113.     unlink(c->tempfile);
  2114.     return NULL;
  2115. }
  2116.  
  2117. static int
  2118. proxy_handler(request_rec *r)
  2119. {
  2120.     char *url, *scheme, *lenp, *p;
  2121.     void *sconf = r->server->module_config;
  2122.     proxy_server_conf *conf =
  2123.         (proxy_server_conf *)get_module_config(sconf, &proxy_module);
  2124.     array_header *proxies=conf->proxies;
  2125.     struct proxy_remote *ents=(struct proxy_remote *)proxies->elts;
  2126.     int i, rc;
  2127.     struct cache_req *cr;
  2128.  
  2129.     if (strncmp(r->filename, "proxy:", 6) != 0) return DECLINED;
  2130.  
  2131.     lenp = table_get (r->headers_in, "Content-length");
  2132.     if ((r->method_number == M_POST || r->method_number == M_PUT)
  2133.     && lenp == NULL)
  2134.     return BAD_REQUEST;
  2135.  
  2136.     url = r->filename + 6;
  2137.     p = strchr(url, ':');
  2138.     if (p == NULL) return BAD_REQUEST;
  2139.  
  2140.     rc = cache_check(r, url, &conf->cache, &cr);
  2141.     if (rc != DECLINED) return rc;
  2142.  
  2143.     *p = '\0';
  2144.     scheme = pstrdup(r->pool, url);
  2145.     *p = ':';
  2146.  
  2147. /* firstly, try a proxy */
  2148.  
  2149.     for (i=0; i < proxies->nelts; i++)
  2150.     {
  2151.     p = strchr(ents[i].scheme, ':');  /* is it a partial URL? */
  2152.     if (strcmp(ents[i].scheme, "*") == 0 || 
  2153.         (p == NULL && strcmp(scheme, ents[i].scheme) == 0) ||
  2154.         (p != NULL &&
  2155.            strncmp(url, ents[i].scheme, strlen(ents[i].scheme)) == 0))
  2156.     {
  2157. /* we only know how to handle communication to a proxy via http */
  2158.         if (strcmp(ents[i].protocol, "http") == 0)
  2159.         rc = http_handler(r, cr, url, ents[i].hostname, ents[i].port);
  2160.         else rc = DECLINED;
  2161.  
  2162.  /* an error or success */
  2163.         if (rc != DECLINED && rc != BAD_GATEWAY) return rc;
  2164.  /* we failed to talk to the upstream proxy */
  2165.     }
  2166.     }
  2167.  
  2168. /* otherwise, try it direct */
  2169. /* N.B. what if we're behind a firewall, where we must use a proxy or
  2170.  * give up??
  2171.  */
  2172.     /* handle the scheme */
  2173.     if (r->method_number == M_CONNECT) return connect_handler(r, cr, url);
  2174.     if (strcmp(scheme, "http") == 0) return http_handler(r, cr, url, NULL, 0);
  2175.     if (strcmp(scheme, "ftp") == 0) return ftp_handler(r, cr, url);
  2176.     else return NOT_IMPLEMENTED;
  2177. }
  2178.  
  2179.  
  2180. static int
  2181. proxyerror(request_rec *r, const char *message)
  2182. {
  2183.     r->status = SERVER_ERROR;
  2184.     r->status_line = "500 Proxy Error";
  2185.     r->content_type = "text/html";
  2186.  
  2187.     send_http_header(r);
  2188.     rvputs(r, "<!DOCTYPE HTML PUBLIC \"-//IETF//DTD HTML 2.0//EN\">\015\012\
  2189. <html><head><title>Proxy Error</title><head>\015\012<body><h1>Proxy Error\
  2190. </h1>\015\012The proxy server could not handle this request.\
  2191. \015\012<p>\015\012Reason: <b>", message, "</b>\015\012</body><html>\015\012",
  2192.        NULL);
  2193.     return OK;
  2194. }
  2195.  
  2196. /*
  2197.  * This routine returns its own error message
  2198.  */
  2199. static const char *
  2200. host2addr(const char *host, struct in_addr *addr)
  2201. {
  2202.     int i;
  2203.     unsigned long ipaddr;
  2204.  
  2205.     for (i=0; host[i] != '\0'; i++)
  2206.     if (!isdigit(host[i]) && host[i] != '.')
  2207.         break;
  2208.  
  2209.     if (host[i] != '\0')
  2210.     {
  2211.     struct hostent *hp;
  2212.  
  2213.     hp = gethostbyname(host);
  2214.     if (hp == NULL) return "Host not found";
  2215.     memcpy(addr, hp->h_addr, sizeof(struct in_addr));
  2216.     } else
  2217.     {
  2218.     if ((ipaddr = inet_addr(host)) == -1)
  2219.         return "Bad IP address";
  2220.     memcpy(addr, &ipaddr, sizeof(unsigned long));
  2221.     }
  2222.     return NULL;
  2223. }
  2224.  
  2225. /*
  2226.  * Returns the ftp status code;
  2227.  *  or -1 on I/O error, 0 on data error
  2228.  */
  2229. int
  2230. ftp_getrc(BUFF *f)
  2231. {
  2232.     int i, len, status;
  2233.     char linebuff[100], buff[5];
  2234.  
  2235.     len = bgets(linebuff, 100, f);
  2236.     if (len == -1) return -1;
  2237. /* check format */
  2238.     if (len < 5 || !isdigit(linebuff[0]) || !isdigit(linebuff[1]) ||
  2239.     !isdigit(linebuff[2]) || (linebuff[3] != ' ' && linebuff[3] != '-'))
  2240.     return 0;
  2241.     status = 100 * linebuff[0] + 10 * linebuff[1] + linebuff[2] - 111 * '0';
  2242.     
  2243.     if (linebuff[len-1] != '\n')
  2244.     {
  2245.     i = bskiplf(f);
  2246.     if (i != 1) return i;
  2247.     }
  2248.  
  2249. /* skip continuation lines */    
  2250.     if (linebuff[3] == '-')
  2251.     {
  2252.     memcpy(buff, linebuff, 3);
  2253.     buff[3] = ' ';
  2254.     do
  2255.     {
  2256.         len = bgets(linebuff, 100, f);
  2257.         if (len == -1) return -1;
  2258.         if (len < 5) return 0;
  2259.         if (linebuff[len-1] != '\n')
  2260.         {
  2261.         i = bskiplf(f);
  2262.         if (i != 1) return i;
  2263.         }
  2264.     } while (memcmp(linebuff, buff, 4) != 0);
  2265.     }
  2266.  
  2267.     return status;
  2268. }
  2269.  
  2270. static int
  2271. doconnect(int sock, struct sockaddr_in *addr, request_rec *r)
  2272. {
  2273.     int i;
  2274.  
  2275.     hard_timeout ("proxy connect", r);
  2276.     do    i = connect(sock, (struct sockaddr *)addr, sizeof(struct sockaddr_in));
  2277.     while (i == -1 && errno == EINTR);
  2278.     if (i == -1) log_uerror("connect", NULL, NULL, r->server);
  2279.     kill_timeout(r);
  2280.  
  2281.     return i;
  2282. }
  2283.  
  2284. /*
  2285.  * Handles direct access of ftp:// URLs
  2286.  */
  2287. static int
  2288. ftp_handler(request_rec *r, struct cache_req *c, char *url)
  2289. {
  2290.     char *host, *path, *p, *user, *password, *parms;
  2291.     const char *err;
  2292.     int port, userlen, passlen, i, len, sock, dsock, csd, rc, nocache;
  2293.     struct sockaddr_in server;
  2294.     struct hdr_entry *hdr;
  2295.     array_header *resp_hdrs;
  2296.     BUFF *f, *cache, *data;
  2297.     pool *pool=r->pool;
  2298.     const int one=1;
  2299.     const long int zero=0L;
  2300.  
  2301. /* we only support GET and HEAD */
  2302.     if (r->method_number != M_GET) return NOT_IMPLEMENTED;
  2303.  
  2304.     host = pstrdup(r->pool, url+6);
  2305. /* We break the URL into host, port, path-search */
  2306.     port = DEFAULT_FTP_PORT;
  2307.     path = strchr(host, '/');
  2308.     if (path == NULL) path = "";
  2309.     else *(path++) = '\0';
  2310.  
  2311.     user = password = NULL;
  2312.     nocache = 0;
  2313.     passlen=0;    /* not actually needed, but it shuts the compiler up */
  2314.     p = strchr(host, '@');
  2315.     if (p != NULL)
  2316.     {
  2317.     (*p++) = '\0';
  2318.     user = host;
  2319.     host = p;
  2320. /* find password */
  2321.     p = strchr(user, ':');
  2322.     if (p != NULL)
  2323.     {
  2324.         *(p++) = '\0';
  2325.         password = p;
  2326.         passlen = decodeenc(password);
  2327.     }
  2328.     userlen = decodeenc(user);
  2329.     nocache = 1; /* don't cache when a username is supplied */
  2330.     } else
  2331.     {
  2332.     user = "anonymous";
  2333.     userlen = 9;
  2334.  
  2335.     password = "proxy_user@host";
  2336.     passlen = strlen(password);
  2337.     }
  2338.  
  2339.     p = strchr(host, ':');
  2340.     if (p != NULL)
  2341.     {
  2342.     *(p++) = '\0';
  2343.     port = atoi(p);
  2344.     }
  2345.  
  2346.     parms = strchr(path, ';');
  2347.     if (parms != NULL) *(parms++) = '\0';
  2348.  
  2349.     memset(&server,'\0',sizeof server);
  2350.     server.sin_family=AF_INET;
  2351.     server.sin_port = htons(port);
  2352.     err = host2addr(host, &server.sin_addr);
  2353.     if (err != NULL) return proxyerror(r, err); /* give up */
  2354.  
  2355.     sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  2356.     if (sock == -1)
  2357.     {
  2358.     log_uerror("socket", NULL, "proxy: error creating socket", r->server);
  2359.     return SERVER_ERROR;
  2360.     }
  2361.     note_cleanups_for_fd(pool, sock);
  2362.  
  2363.     if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&one,
  2364.            sizeof(int)) == -1)
  2365.     {
  2366.     log_uerror("setsockopt", NULL, "proxy: error setting reuseaddr option",
  2367.            r->server);
  2368.     pclosef(pool, sock);
  2369.     return SERVER_ERROR;
  2370.     }
  2371.  
  2372.     i = doconnect(sock, &server, r);
  2373.     if (i == -1) return proxyerror(r, "Could not connect to remote machine");
  2374.  
  2375.     f = bcreate(pool, B_RDWR);
  2376.     bpushfd(f, sock, sock);
  2377. /* shouldn't we implement telnet control options here? */
  2378.  
  2379. /* possible results: 120, 220, 421 */
  2380.     hard_timeout ("proxy ftp", r);
  2381.     i = ftp_getrc(f);
  2382.     if (i == -1) return proxyerror(r, "Error reading from remote server");
  2383.     if (i != 220) return BAD_GATEWAY;
  2384.  
  2385.     bputs("USER ", f);
  2386.     bwrite(f, user, userlen);
  2387.     bputs("\015\012", f);
  2388.     bflush(f); /* capture any errors */
  2389.     
  2390. /* possible results; 230, 331, 332, 421, 500, 501, 530 */
  2391. /* states: 1 - error, 2 - success; 3 - send password, 4,5 fail */
  2392.     i = ftp_getrc(f);
  2393.     if (i == -1) return proxyerror(r, "Error sending to remote server");
  2394.     if (i == 530) return FORBIDDEN;
  2395.     else if (i != 230 && i != 331) return BAD_GATEWAY;
  2396.     
  2397.     if (i == 331) /* send password */
  2398.     {
  2399.     if (password == NULL) return FORBIDDEN;
  2400.     bputs("PASS ", f);
  2401.     bwrite(f, password, passlen);
  2402.     bputs("\015\012", f);
  2403.     bflush(f);
  2404. /* possible results 202, 230, 332, 421, 500, 501, 503, 530 */
  2405.     i = ftp_getrc(f);
  2406.     if (i == -1) return proxyerror(r, "Error sending to remote server");
  2407.     if (i == 332 || i == 530) return FORBIDDEN;
  2408.     else if (i != 230 && i != 202) return BAD_GATEWAY;
  2409.     }  
  2410.  
  2411. /* set the directory */
  2412. /* this is what we must do if we don't know the OS type of the remote
  2413.  * machine
  2414.  */
  2415.     for (;;)
  2416.     {
  2417.     p = strchr(path, '/');
  2418.     if (p == NULL) break;
  2419.     *p = '\0';
  2420.  
  2421.     len = decodeenc(path);
  2422.     bputs("CWD ", f);
  2423.     bwrite(f, path, len);
  2424.     bputs("\015\012", f);
  2425.         bflush(f);
  2426. /* responses: 250, 421, 500, 501, 502, 530, 550 */
  2427. /* 1,3 error, 2 success, 4,5 failure */
  2428.     i = ftp_getrc(f);
  2429.     if (i == -1) return proxyerror(r, "Error sending to remote server");
  2430.     else if (i == 550) return NOT_FOUND;
  2431.     else if (i != 250) return BAD_GATEWAY;
  2432.  
  2433.     path = p + 1;
  2434.     }
  2435.  
  2436.     if (parms != NULL && strncmp(parms, "type=", 5) == 0)
  2437.     {
  2438.     parms += 5;
  2439.     if ((parms[0] != 'd' && parms[0] != 'a' && parms[0] != 'i') ||
  2440.         parms[1] != '\0') parms = "";
  2441.     }
  2442.     else parms = "";
  2443.  
  2444.     if (parms[0] == 'i')
  2445.     {
  2446.     /* set type to image */
  2447.     bputs("TYPE I", f);
  2448.     bflush(f);
  2449. /* responses: 200, 421, 500, 501, 504, 530 */
  2450.     i = ftp_getrc(f);
  2451.     if (i == -1) return proxyerror(r, "Error sending to remote server");
  2452.     else if (i != 200 && i != 504) return BAD_GATEWAY;
  2453. /* Allow not implemented */
  2454.     else if (i == 504) parms[0] = '\0';
  2455.     }
  2456.  
  2457. /* set up data connection */
  2458.     len = sizeof(struct sockaddr_in);
  2459.     if (getsockname(sock, (struct sockaddr *)&server, &len) < 0)
  2460.     {
  2461.     log_uerror("getsockname", NULL,"proxy: error getting socket address",
  2462.            r->server);
  2463.     pclosef(pool, sock);
  2464.     return SERVER_ERROR;
  2465.     }
  2466.  
  2467.     dsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  2468.     if (dsock == -1)
  2469.     {
  2470.     log_uerror("socket", NULL, "proxy: error creating socket", r->server);
  2471.     pclosef(pool, sock);
  2472.     return SERVER_ERROR;
  2473.     }
  2474.     note_cleanups_for_fd(pool, dsock);
  2475.  
  2476.     if (setsockopt(dsock, SOL_SOCKET, SO_REUSEADDR, (const char *)&one,
  2477.            sizeof(int)) == -1)
  2478.     {
  2479.     log_uerror("setsockopt", NULL, "proxy: error setting reuseaddr option",
  2480.            r->server);
  2481.     pclosef(pool, dsock);
  2482.     pclosef(pool, sock);
  2483.     return SERVER_ERROR;
  2484.     }
  2485.  
  2486.     if (bind(dsock, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) ==
  2487.     -1)
  2488.     {
  2489.     char buff[22];
  2490.  
  2491.     sprintf(buff, "%s:%d", inet_ntoa(server.sin_addr), server.sin_port);
  2492.     log_uerror("bind", buff, "proxy: error binding to ftp data socket",
  2493.            r->server);
  2494.     pclosef(pool, sock);
  2495.     pclosef(pool, dsock);
  2496.     }
  2497.     listen(dsock, 2); /* only need a short queue */
  2498.  
  2499. /* set request */
  2500.     len = decodeenc(path);
  2501.     if (parms[0] == 'd')
  2502.     {
  2503.     if (len != 0) bputs("NLST ", f);
  2504.     else bputs("NLST", f);
  2505.     }
  2506.     else bputs("RETR ", f);
  2507.     bwrite(f, path, len);
  2508.     bputs("\015\012", f);
  2509.     bflush(f);
  2510. /* RETR: 110, 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 530, 550
  2511.    NLST: 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 502, 530 */
  2512.     rc = ftp_getrc(f);
  2513.     if (rc == -1) return proxyerror(r, "Error sending to remote server");
  2514.     if (rc == 550) return NOT_FOUND;
  2515.     if (rc != 125 && rc != 150 && rc != 226 && rc != 250) return BAD_GATEWAY;
  2516.     kill_timeout(r);
  2517.  
  2518.     r->status = 200;
  2519.     r->status_line = "200 OK";
  2520.  
  2521.     resp_hdrs = make_array(pool, 2, sizeof(struct hdr_entry));
  2522.     if (parms[0] == 'd')
  2523.     add_header(resp_hdrs, "Content-Type", "text/plain", HDR_REP);
  2524.     i = cache_update(c, resp_hdrs, "FTP", nocache);
  2525.     if (i != DECLINED)
  2526.     {
  2527.     pclosef(pool, dsock);
  2528.     pclosef(pool, sock);
  2529.     return i;
  2530.     }
  2531.     cache = c->fp;
  2532.  
  2533. /* wait for connection */
  2534.     hard_timeout ("proxy ftp data connect", r);
  2535.     len = sizeof(struct sockaddr_in);
  2536.     do csd = accept(dsock, (struct sockaddr *)&server, &len);
  2537.     while (csd == -1 && errno == EINTR);    /* SHUDDER on SOCKS - cdm */
  2538.     if (csd == -1)
  2539.     {
  2540.     log_uerror("accept", NULL, "proxy: failed to accept data connection",
  2541.            r->server);
  2542.     pclosef(pool, dsock);
  2543.     pclosef(pool, sock);
  2544.     cache_error(c);
  2545.     return BAD_GATEWAY;
  2546.     }
  2547.     note_cleanups_for_fd(pool, csd);
  2548.     data = bcreate(pool, B_RD);
  2549.     bpushfd(data, csd, -1);
  2550.     kill_timeout(r);
  2551.  
  2552.     hard_timeout ("proxy receive", r);
  2553. /* send response */
  2554. /* write status line */
  2555.     if (!r->assbackwards)
  2556.     rvputs(r, SERVER_PROTOCOL, " ", r->status_line, "\015\012", NULL);
  2557.     if (cache != NULL)
  2558.     if (bvputs(cache, SERVER_PROTOCOL, " ", r->status_line, "\015\012",
  2559.            NULL) == -1)
  2560.         cache = cache_error(c);
  2561.  
  2562. /* send headers */
  2563.     len = resp_hdrs->nelts;
  2564.     hdr = (struct hdr_entry *)resp_hdrs->elts;
  2565.     for (i=0; i < len; i++)
  2566.     {
  2567.     if (hdr[i].field == NULL || hdr[i].value == NULL ||
  2568.         hdr[i].value[0] == '\0') continue;
  2569.     if (!r->assbackwards)
  2570.         rvputs(r, hdr[i].field, ": ", hdr[i].value, "\015\012", NULL);
  2571.     if (cache != NULL)
  2572.         if (bvputs(cache, hdr[i].field, ": ", hdr[i].value, "\015\012",
  2573.                NULL) == -1)
  2574.         cache = cache_error(c);
  2575.     }
  2576.  
  2577.     if (!r->assbackwards) rputs("\015\012", r);
  2578.     if (cache != NULL)
  2579.     if (bputs("\015\012", cache) == -1) cache = cache_error(c);
  2580.  
  2581.     bsetopt(r->connection->client, BO_BYTECT, &zero);
  2582.     r->sent_bodyct = 1;
  2583. /* send body */
  2584.     if (!r->header_only)
  2585.     {
  2586.     send_fb(data, r, cache, c);
  2587.     if (rc == 125 || rc == 150) rc = ftp_getrc(f);
  2588.     if (rc != 226 && rc != 250) cache_error(c);
  2589.     }
  2590.     else
  2591.     {
  2592. /* abort the transfer */
  2593.     bputs("ABOR\015\012", f);
  2594.     bflush(f);
  2595. /* responses: 225, 226, 421, 500, 501, 502 */
  2596.     i = ftp_getrc(f);
  2597.     }
  2598.  
  2599.     cache_tidy(c);
  2600.  
  2601. /* finish */
  2602.     bputs("QUIT\015\012", f);
  2603.     bflush(f);
  2604. /* responses: 221, 500 */    
  2605.  
  2606.     pclosef(pool, csd);
  2607.     pclosef(pool, dsock);
  2608.     pclosef(pool, sock);
  2609.  
  2610.     return OK;
  2611. }
  2612.  
  2613. /*  
  2614.  * This handles Netscape CONNECT method secure proxy requests.
  2615.  * A connection is opened to the specified host and data is
  2616.  * passed through between the WWW site and the browser.
  2617.  *
  2618.  * This code is based on the INTERNET-DRAFT document
  2619.  * "Tunneling SSL Through a WWW Proxy" currently at
  2620.  * http://www.mcom.com/newsref/std/tunneling_ssl.html.
  2621.  *
  2622.  * FIXME: this is bad, because it does its own socket I/O
  2623.  *        instead of using the I/O in buff.c.  However,
  2624.  *        the I/O in buff.c blocks on reads, and because
  2625.  *        this function doesn't know how much data will
  2626.  *        be sent either way (or when) it can't use blocking
  2627.  *        I/O.  This may be very implementation-specific
  2628.  *        (to Linux).  Any suggestions?
  2629.  * FIXME: this doesn't log the number of bytes sent, but
  2630.  *        that may be okay, since the data is supposed to
  2631.  *        be transparent. In fact, this doesn't log at all
  2632.  *      yet. 8^)
  2633.  * FIXME: doesn't check any headers initally sent from the
  2634.  *        client.
  2635.  * FIXME: should allow authentication, but hopefully the
  2636.  *        generic proxy authentication is good enough.
  2637.  * FIXME: no check for r->assbackwards, whatever that is.
  2638.  */ 
  2639.  
  2640. static int
  2641. connect_handler(request_rec *r, struct cache_req *c, char *url)
  2642. {
  2643.     struct sockaddr_in server;
  2644.     const char *host, *err;
  2645.     char *p;
  2646.     int   port, sock;
  2647.     char buffer[HUGE_STRING_LEN];
  2648.     int  nbytes, i;
  2649.     fd_set fds;
  2650.  
  2651.     memset(&server, '\0', sizeof(server));
  2652.     server.sin_family=AF_INET;
  2653.  
  2654.     /* Break the URL into host:port pairs */
  2655.  
  2656.     host = url;
  2657.     p = strchr(url, ':');
  2658.     if (p==NULL) port = DEFAULT_HTTPS_PORT;
  2659.     else
  2660.     {
  2661.       port = atoi(p+1);
  2662.       *p='\0';
  2663.     }
  2664.  
  2665.     switch (port)
  2666.     {
  2667.     case DEFAULT_HTTPS_PORT:
  2668.     case DEFAULT_SNEWS_PORT:
  2669.         break;
  2670.     default:
  2671.         return SERVICE_UNAVAILABLE;
  2672.     }
  2673.  
  2674.     Explain2("CONNECT to %s on port %d", host, port);
  2675.  
  2676.     server.sin_port = htons(port);
  2677.     err = host2addr(host, &server.sin_addr);
  2678.     if (err != NULL) return proxyerror(r, err); /* give up */
  2679.  
  2680.     sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);  
  2681.     if (sock == -1)
  2682.     {     
  2683.         log_error("proxy: error creating socket", r->server);
  2684.         return SERVER_ERROR;
  2685.     }     
  2686.     note_cleanups_for_fd(r->pool, sock);
  2687.  
  2688.     i = doconnect(sock, &server, r);
  2689.     if (i == -1 )
  2690.         return proxyerror(r, "Could not connect to remote machine");
  2691.  
  2692.     Explain0("Returning 200 OK Status");
  2693.  
  2694.     rvputs(r, "HTTP/1.0 200 Connection established\015\012", NULL);
  2695.     rvputs(r, "Proxy-agent: ", SERVER_VERSION, "\015\012\015\012", NULL);
  2696.     bflush(r->connection->client);
  2697.  
  2698.     while (1) /* Infinite loop until error (one side closes the connection) */
  2699.     {
  2700.       FD_ZERO(&fds);
  2701.       FD_SET(sock, &fds);
  2702.       FD_SET(r->connection->client->fd, &fds);
  2703.     
  2704.       Explain0("Going to sleep (select)");
  2705.       i = select((r->connection->client->fd > sock ?
  2706.     r->connection->client->fd+1 :
  2707. #ifdef HPUX
  2708.     sock+1), (int*)&fds, NULL, NULL, NULL);
  2709. #else
  2710.     sock+1), &fds, NULL, NULL, NULL);
  2711. #endif
  2712.       Explain1("Woke from select(), i=%d",i);
  2713.     
  2714.       if (i)
  2715.       {
  2716.         if (FD_ISSET(sock, &fds))
  2717.         {
  2718.            Explain0("sock was set");
  2719.            if((nbytes=read(sock,buffer,HUGE_STRING_LEN))!=0)
  2720.            {
  2721.               if(nbytes==-1) break;
  2722.               if(write(r->connection->client->fd, buffer, nbytes)==EOF)break;
  2723.               Explain1("Wrote %d bytes to client", nbytes);
  2724.            }
  2725.            else break;
  2726.         }
  2727.         else if (FD_ISSET(r->connection->client->fd, &fds))
  2728.         { 
  2729.            Explain0("client->fd was set");
  2730.            if((nbytes=read(r->connection->client->fd,buffer,
  2731.         HUGE_STRING_LEN))!=0)   
  2732.            {
  2733.               if(nbytes==-1) break;
  2734.               if(write(sock,buffer,nbytes)==EOF) break;
  2735.               Explain1("Wrote %d bytes to server", nbytes);
  2736.            }
  2737.            else break;
  2738.         }
  2739.         else break; /* Must be done waiting */
  2740.       }
  2741.       else break;
  2742.     }
  2743.  
  2744.     pclosef(r->pool,sock);
  2745.     
  2746.     return OK;
  2747. }     
  2748.  
  2749. /*
  2750.  * This handles http:// URLs, and other URLs using a remote proxy over http
  2751.  * If proxyhost is NULL, then contact the server directly, otherwise
  2752.  * go via the proxy.
  2753.  * Note that if a proxy is used, then URLs other than http: can be accessed,
  2754.  * also, if we have trouble which is clearly specific to the proxy, then
  2755.  * we return DECLINED so that we can try another proxy. (Or the direct
  2756.  * route.)
  2757.  */
  2758. static int
  2759. http_handler(request_rec *r, struct cache_req *c, char *url,
  2760.          const char *proxyhost, int proxyport)
  2761. {
  2762.     char *p;
  2763.     const char *err, *host;
  2764.     int port, i, sock, len;
  2765.     array_header *reqhdrs_arr, *resp_hdrs;
  2766.     table_entry *reqhdrs;
  2767.     struct sockaddr_in server;
  2768.     BUFF *f, *cache;
  2769.     struct hdr_entry *hdr;
  2770.     char buffer[HUGE_STRING_LEN], inprotocol[9], outprotocol[9];
  2771.     pool *pool=r->pool;
  2772.     const long int zero=0L;
  2773.  
  2774.     void *sconf = r->server->module_config;
  2775.     proxy_server_conf *conf =
  2776.         (proxy_server_conf *)get_module_config(sconf, &proxy_module);
  2777.     struct nocache_entry *ent=(struct nocache_entry *)conf->nocaches->elts;
  2778.     int nocache = 0;
  2779.  
  2780.     memset(&server, '\0', sizeof(server));
  2781.     server.sin_family = AF_INET;
  2782.  
  2783.     if (proxyhost != NULL)
  2784.     {
  2785.     server.sin_port = htons(proxyport);
  2786.     err = host2addr(proxyhost, &server.sin_addr);
  2787.     if (err != NULL) return DECLINED;  /* try another */
  2788.     host = proxyhost;
  2789.     } else
  2790.     {
  2791.     url += 7;  /* skip http:// */
  2792. /* We break the URL into host, port, path-search */
  2793.     port = DEFAULT_PORT;
  2794.     p = strchr(url, '/');
  2795.     if (p == NULL)
  2796.     {
  2797.         host = pstrdup(pool, url);
  2798.         url = "/";
  2799.     } else
  2800.     {
  2801.         char *q = palloc(pool, p-url+1);
  2802.         memcpy(q, url, p-url);
  2803.         q[p-url] = '\0';
  2804.         url = p;
  2805.         host = q;
  2806.     }
  2807.  
  2808.     p = strchr(host, ':');
  2809.     if (p != NULL)
  2810.     {
  2811.         *(p++) = '\0';
  2812.         port = atoi(p);
  2813.     }
  2814.     server.sin_port = htons(port);
  2815.     err = host2addr(host, &server.sin_addr);
  2816.     if (err != NULL) return proxyerror(r, err); /* give up */
  2817.     }
  2818.  
  2819.     sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
  2820.     if (sock == -1)
  2821.     {
  2822.     log_error("proxy: error creating socket", r->server);
  2823.     return SERVER_ERROR;
  2824.     }
  2825.     note_cleanups_for_fd(pool, sock);
  2826.  
  2827.     i = doconnect(sock, &server, r);
  2828.     if (i == -1)
  2829.     {
  2830.     if (proxyhost != NULL) return DECLINED; /* try again another way */
  2831.     else return proxyerror(r, "Could not connect to remote machine");
  2832.     }
  2833.  
  2834.     f = bcreate(pool, B_RDWR);
  2835.     bpushfd(f, sock, sock);
  2836.  
  2837.     hard_timeout ("proxy send", r);
  2838.     bvputs(f, r->method, " ", url, " HTTP/1.0\015\012", NULL);
  2839.  
  2840.     reqhdrs_arr = table_elts (r->headers_in);
  2841.     reqhdrs = (table_entry *)reqhdrs_arr->elts;
  2842.     for (i=0; i < reqhdrs_arr->nelts; i++)
  2843.     {
  2844.     if (reqhdrs[i].key == NULL || reqhdrs[i].val == NULL) continue;
  2845.     if (!strcasecmp(reqhdrs[i].key, "Connection")) continue;
  2846.     bvputs(f, reqhdrs[i].key, ": ", reqhdrs[i].val, "\015\012", NULL);
  2847.     }
  2848.  
  2849.     bputs("\015\012", f);
  2850. /* send the request data, if any. N.B. should we trap SIGPIPE ? */
  2851.  
  2852.     if (r->method_number == M_POST || r->method_number == M_PUT)
  2853.     {
  2854.     len = atoi(table_get (r->headers_in, "Content-length"));
  2855.  
  2856.     while (len > 0)
  2857.     {
  2858.         i = len;
  2859.         if (i > HUGE_STRING_LEN) i = HUGE_STRING_LEN;
  2860.         
  2861.         i = read_client_block(r, buffer, i);
  2862.         bwrite(f, buffer, i);
  2863.  
  2864.         len -= i;
  2865.     }
  2866.     }
  2867.     bflush(f);
  2868.     kill_timeout(r);
  2869.  
  2870.     hard_timeout ("proxy receive", r);
  2871.     
  2872.     len = bgets(buffer, HUGE_STRING_LEN-1, f);
  2873.     if (len == -1 || len == 0)
  2874.     {
  2875.     pclosef(pool, sock);
  2876.     return proxyerror(r, "Error reading from remote server");
  2877.     }
  2878.  
  2879. /* Is it an HTTP/1 response? */
  2880.     if (checkmask(buffer,  "HTTP/#.# ### *"))
  2881.     {
  2882. /* If not an HTTP/1 messsage or if the status line was > 8192 bytes */
  2883.     if (buffer[5] != '1' || buffer[len-1] != '\n')
  2884.     {
  2885.         pclosef(pool, sock);
  2886.         return BAD_GATEWAY;
  2887.     }
  2888.     buffer[--len] = '\0';
  2889.     memcpy(inprotocol, buffer, 8);
  2890.     inprotocol[8] = '\0';
  2891.  
  2892. /* we use the same protocol on output as on input */
  2893.     strcpy(outprotocol, inprotocol);
  2894.     buffer[12] = '\0';
  2895.     r->status = atoi(&buffer[9]);
  2896.     buffer[12] = ' ';
  2897.     r->status_line = pstrdup(pool, &buffer[9]);
  2898.  
  2899. /* read the headers. */
  2900. /* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers */
  2901. /* Also, take care with headers with multiple occurences. */
  2902.  
  2903.     resp_hdrs = read_headers(pool, buffer, HUGE_STRING_LEN, f);
  2904.     } else
  2905.     {
  2906. /* an http/0.9 response */
  2907.     strcpy(inprotocol, "HTTP/0.9");
  2908.     strcpy(outprotocol, "HTTP/1.0");
  2909.     r->status = 200;
  2910.     r->status_line = "200 OK";
  2911.  
  2912. /* no headers */
  2913.     resp_hdrs = make_array(pool, 2, sizeof(struct hdr_entry));
  2914.     }
  2915.  
  2916.     kill_timeout(r);
  2917.  
  2918. /*
  2919.  * HTTP/1.0 requires us to accept 3 types of dates, but only generate
  2920.  * one type
  2921.  */
  2922.     
  2923.     len = resp_hdrs->nelts;
  2924.     hdr = (struct hdr_entry *)resp_hdrs->elts;
  2925.     for (i=0; i < len; i++)
  2926.     {
  2927.     if (hdr[i].value[0] == '\0') continue;
  2928.     p = hdr[i].field;
  2929.     if (strcasecmp(p, "Date") == 0 ||
  2930.         strcasecmp(p, "Last-Modified") == 0 ||
  2931.         strcasecmp(p, "Expires") == 0)
  2932.         hdr[i].value = date_canon(pool, hdr[i].value);
  2933.     }
  2934.  
  2935. /* check if NoCache directive on this host */
  2936.     for (i=0; i < conf->nocaches->nelts; i++)
  2937.     {
  2938.         if (ent[i].name != NULL && strstr(host, ent[i].name) != NULL)
  2939.         nocache = 1; 
  2940.     }
  2941.  
  2942.     i = cache_update(c, resp_hdrs, inprotocol, nocache);
  2943.     if (i != DECLINED)
  2944.     {
  2945.     pclosef(pool, sock);
  2946.     return i;
  2947.     }
  2948.  
  2949.     cache = c->fp;
  2950.  
  2951.     hard_timeout ("proxy receive", r);
  2952.  
  2953. /* write status line */
  2954.     if (!r->assbackwards)
  2955.         rvputs(r, "HTTP/1.0 ", r->status_line, "\015\012", NULL);
  2956.     if (cache != NULL)
  2957.     if (bvputs(cache, outprotocol, " ", r->status_line, "\015\012", NULL)
  2958.         == -1)
  2959.         cache = cache_error(c);
  2960.  
  2961. /* send headers */
  2962.     len = resp_hdrs->nelts;
  2963.     for (i=0; i < len; i++)
  2964.     {
  2965.     if (hdr[i].field == NULL || hdr[i].value == NULL ||
  2966.         hdr[i].value[0] == '\0') continue;
  2967.     if (!r->assbackwards)
  2968.         rvputs(r, hdr[i].field, ": ", hdr[i].value, "\015\012", NULL);
  2969.     if (cache != NULL)
  2970.         if (bvputs(cache, hdr[i].field, ": ", hdr[i].value, "\015\012",
  2971.                NULL) == -1)
  2972.         cache = cache_error(c);
  2973.     }
  2974.  
  2975.     if (!r->assbackwards) rputs("\015\012", r);
  2976.     if (cache != NULL)
  2977.     if (bputs("\015\012", cache) == -1) cache = cache_error(c);
  2978.  
  2979.     bsetopt(r->connection->client, BO_BYTECT, &zero);
  2980.     r->sent_bodyct = 1;
  2981. /* Is it an HTTP/0.9 respose? If so, send the extra data */
  2982.     if (strcmp(inprotocol, "HTTP/0.9") == 0)
  2983.     {
  2984.     bwrite(r->connection->client, buffer, len);
  2985.     if (cache != NULL)
  2986.         if (bwrite(f, buffer, len) != len) cache = cache_error(c);
  2987.     }
  2988.  
  2989. /* send body */
  2990. /* if header only, then cache will be NULL */
  2991. /* HTTP/1.0 tells us to read to EOF, rather than content-length bytes */
  2992.     if (!r->header_only) send_fb(f, r, cache, c);
  2993.  
  2994.     cache_tidy(c);
  2995.  
  2996.     pclosef(pool, sock);
  2997.  
  2998.     return OK;
  2999. }
  3000.  
  3001. static handler_rec proxy_handlers[] = {
  3002. { "proxy-server", proxy_handler },
  3003. { NULL }
  3004. };
  3005.  
  3006.  
  3007. static void *
  3008. create_proxy_config(pool *p, server_rec *s)
  3009. {
  3010.   proxy_server_conf *ps = pcalloc(p, sizeof(proxy_server_conf));
  3011.  
  3012.   ps->proxies = make_array(p, 10, sizeof(struct proxy_remote));
  3013.   ps->aliases = make_array(p, 10, sizeof(struct proxy_alias));
  3014.   ps->nocaches = make_array(p, 10, sizeof(struct nocache_entry));
  3015.   ps->req = 0;
  3016.  
  3017.   ps->cache.root = NULL;
  3018.   ps->cache.space = DEFAULT_CACHE_SPACE;
  3019.   ps->cache.maxexpire = DEFAULT_CACHE_MAXEXPIRE;
  3020.   ps->cache.defaultexpire = DEFAULT_CACHE_EXPIRE;
  3021.   ps->cache.lmfactor = DEFAULT_CACHE_LMFACTOR;
  3022.   ps->cache.gcinterval = -1;
  3023.   /* at these levels, the cache can have 2^18 directories (256,000)  */
  3024.   ps->cache.dirlevels=3;
  3025.   ps->cache.dirlength=1;
  3026.  
  3027.   return ps;
  3028. }
  3029.  
  3030. static char *
  3031. add_proxy(cmd_parms *cmd, void *dummy, char *f, char *r)
  3032. {
  3033.     server_rec *s = cmd->server;
  3034.     proxy_server_conf *conf =
  3035.         (proxy_server_conf *)get_module_config(s->module_config,&proxy_module);
  3036.     struct proxy_remote *new;
  3037.     char *p, *q;
  3038.     int port;
  3039.  
  3040.     p = strchr(r, ':');
  3041.     if (p == NULL || p[1] != '/' || p[2] != '/' || p[3] == '\0')
  3042.     return "Bad syntax for a remote proxy server";
  3043.     q = strchr(p + 3, ':');
  3044.     if (q != NULL)
  3045.     {
  3046.     if (sscanf(q+1, "%u", &port) != 1 || port > 65535)
  3047.         return "Bad syntax for a remote proxy server (bad port number)";
  3048.     *q = '\0';
  3049.     } else port = -1;
  3050.     *p = '\0';
  3051.     if (strchr(f, ':') == NULL) str_tolower(f);     /* lowercase scheme */
  3052.     str_tolower(p + 3); /* lowercase hostname */
  3053.  
  3054.     if (port == -1)
  3055.     {
  3056.     int i;
  3057.     for (i=0; defports[i].scheme != NULL; i++)
  3058.         if (strcmp(defports[i].scheme, r) == 0) break;
  3059.     port = defports[i].port;
  3060.     }
  3061.  
  3062.     new = push_array (conf->proxies);
  3063.     new->scheme = f;
  3064.     new->protocol = r;
  3065.     new->hostname = p + 3;
  3066.     new->port = port;
  3067.     return NULL;
  3068. }
  3069.  
  3070. static char *
  3071. add_pass(cmd_parms *cmd, void *dummy, char *f, char *r)
  3072. {
  3073.     server_rec *s = cmd->server;
  3074.     proxy_server_conf *conf =
  3075.         (proxy_server_conf *)get_module_config(s->module_config,&proxy_module);
  3076.     struct proxy_alias *new;
  3077.  
  3078.     new = push_array (conf->aliases);
  3079.     new->fake = f;
  3080.     new->real = r;
  3081.     return NULL;
  3082. }
  3083.  
  3084. static char *
  3085. set_proxy_req(cmd_parms *parms, void *dummy, int flag)
  3086. {
  3087.     proxy_server_conf *psf =
  3088.     get_module_config (parms->server->module_config, &proxy_module);
  3089.  
  3090.     psf->req = flag;
  3091.     return NULL;
  3092. }
  3093.  
  3094.  
  3095. static char *
  3096. set_cache_size(cmd_parms *parms, char *struct_ptr, char *arg)
  3097. {
  3098.     proxy_server_conf *psf =
  3099.     get_module_config (parms->server->module_config, &proxy_module);
  3100.     int val;
  3101.  
  3102.     if (sscanf(arg, "%d", &val) != 1) return "Value must be an integer";
  3103.     psf->cache.space = val;
  3104.     return NULL;
  3105. }
  3106.  
  3107. static char *
  3108. set_cache_root(cmd_parms *parms, void *dummy, char *arg)
  3109. {
  3110.     proxy_server_conf *psf =
  3111.     get_module_config (parms->server->module_config, &proxy_module);
  3112.  
  3113.     psf->cache.root = arg;
  3114.  
  3115.     return NULL;
  3116. }
  3117.  
  3118. static char *
  3119. set_cache_factor(cmd_parms *parms, void *dummy, char *arg)
  3120. {
  3121.     proxy_server_conf *psf =
  3122.     get_module_config (parms->server->module_config, &proxy_module);
  3123.     double val;
  3124.  
  3125.     if (sscanf(arg, "%lg", &val) != 1) return "Value must be a float";
  3126.     psf->cache.lmfactor = val;
  3127.  
  3128.     return NULL;
  3129. }
  3130.  
  3131. static char *
  3132. set_cache_maxex(cmd_parms *parms, void *dummy, char *arg)
  3133. {
  3134.     proxy_server_conf *psf =
  3135.     get_module_config (parms->server->module_config, &proxy_module);
  3136.     double val;
  3137.  
  3138.     if (sscanf(arg, "%lg", &val) != 1) return "Value must be a float";
  3139.     psf->cache.maxexpire = (int)(val * (double)SEC_ONE_HR);
  3140.     return NULL;
  3141. }
  3142.  
  3143. static char *
  3144. set_cache_defex(cmd_parms *parms, void *dummy, char *arg)
  3145. {
  3146.     proxy_server_conf *psf =
  3147.     get_module_config (parms->server->module_config, &proxy_module);
  3148.     double val;
  3149.  
  3150.     if (sscanf(arg, "%lg", &val) != 1) return "Value must be a float";
  3151.     psf->cache.defaultexpire = (int)(val * (double)SEC_ONE_HR);
  3152.     return NULL;
  3153. }
  3154.  
  3155. static char *
  3156. set_cache_gcint(cmd_parms *parms, void *dummy, char *arg)
  3157. {
  3158.     proxy_server_conf *psf =
  3159.     get_module_config (parms->server->module_config, &proxy_module);
  3160.     double val;
  3161.  
  3162.     if (sscanf(arg, "%lg", &val) != 1) return "Value must be a float";
  3163.     psf->cache.gcinterval = (int)(val * (double)SEC_ONE_HR);
  3164.     return NULL;
  3165. }
  3166.  
  3167. static char *
  3168. set_cache_dirlevels(cmd_parms *parms, char *struct_ptr, char *arg)
  3169. {
  3170.     proxy_server_conf *psf =
  3171.     get_module_config (parms->server->module_config, &proxy_module);
  3172.     int val;
  3173.  
  3174.     if (sscanf(arg, "%d", &val) != 1) return "Value must be an integer";
  3175.     psf->cache.dirlevels = val;
  3176.     return NULL;
  3177. }
  3178.  
  3179. static char *
  3180. set_cache_dirlength(cmd_parms *parms, char *struct_ptr, char *arg)
  3181. {
  3182.     proxy_server_conf *psf =
  3183.     get_module_config (parms->server->module_config, &proxy_module);
  3184.     int val;
  3185.  
  3186.     if (sscanf(arg, "%d", &val) != 1) return "Value must be an integer";
  3187.     psf->cache.dirlength = val;
  3188.     return NULL;
  3189. }
  3190.  
  3191. static char *
  3192. set_cache_exclude(cmd_parms *parms, void *dummy, char *arg)
  3193. {
  3194.     server_rec *s = parms->server;
  3195.     proxy_server_conf *conf =
  3196.     get_module_config (s->module_config, &proxy_module);
  3197.     struct nocache_entry *new;
  3198.     struct nocache_entry *list=(struct nocache_entry*)conf->nocaches->elts;
  3199.     int found = 0;
  3200.     int i;
  3201.  
  3202.     /* Don't duplicate entries */
  3203.     for (i=0; i < conf->nocaches->nelts; i++)
  3204.     {
  3205.     if (strcmp(arg, list[i].name) == 0)
  3206.         found = 1;
  3207.     }
  3208.  
  3209.     if (!found)
  3210.     {
  3211.     new = push_array (conf->nocaches);
  3212.     new->name = arg;
  3213.     }
  3214.     return NULL;
  3215. }
  3216.  
  3217. static command_rec proxy_cmds[] = {
  3218. { "ProxyRequests", set_proxy_req, NULL, RSRC_CONF, FLAG,
  3219.   "on if the true proxy requests should be accepted"},
  3220. { "ProxyRemote", add_proxy, NULL, RSRC_CONF, TAKE2, 
  3221.     "a scheme, partial URL or '*' and a proxy server"},
  3222. { "ProxyPass", add_pass, NULL, RSRC_CONF, TAKE2, 
  3223.     "a virtual path and a URL"},
  3224. { "CacheRoot", set_cache_root, NULL, RSRC_CONF, TAKE1,
  3225.       "The directory to store cache files"},
  3226. { "CacheSize", set_cache_size, NULL, RSRC_CONF, TAKE1,
  3227.       "The maximum disk space used by the cache in Kb"},
  3228. { "CacheMaxExpire", set_cache_maxex, NULL, RSRC_CONF, TAKE1,
  3229.       "The maximum time in hours to cache a document"},
  3230. { "CacheDefaultExpire", set_cache_defex, NULL, RSRC_CONF, TAKE1,
  3231.       "The default time in hours to cache a document"},
  3232. { "CacheLastModifiedFactor", set_cache_factor, NULL, RSRC_CONF, TAKE1,
  3233.       "The factor used to estimate Expires date from LastModified date"},
  3234. { "CacheGcInterval", set_cache_gcint, NULL, RSRC_CONF, TAKE1,
  3235.       "The interval between garbage collections, in hours"},
  3236. { "CacheDirLevels", set_cache_dirlevels, NULL, RSRC_CONF, TAKE1,
  3237.     "The number of levels of subdirectories in the cache" },
  3238. { "CacheDirLength", set_cache_dirlength, NULL, RSRC_CONF, TAKE1,
  3239.     "The number of characters in subdirectory names" },
  3240. { "NoCache", set_cache_exclude, NULL, RSRC_CONF, ITERATE,
  3241.     "A list of hosts or domains for which caching is *not* provided" },
  3242. { NULL }
  3243. };
  3244.  
  3245.  
  3246. module proxy_module = {
  3247.    STANDARD_MODULE_STUFF,
  3248.    NULL,            /* initializer */
  3249.    NULL,            /* create per-directory config structure */
  3250.    NULL,            /* merge per-directory config structures */
  3251.    create_proxy_config,        /* create per-server config structure */
  3252.    NULL,                     /* merge per-server config structures */
  3253.    proxy_cmds,            /* command table */
  3254.    proxy_handlers,            /* handlers */
  3255.    proxy_trans,            /* translate_handler */
  3256.    NULL,            /* check_user_id */
  3257.    NULL,            /* check auth */
  3258.    NULL,            /* check access */
  3259.    NULL,            /* type_checker */
  3260.    proxy_fixup,            /* pre-run fixups */
  3261.    NULL                /* logger */
  3262. };
  3263.