+
+#define SET_BYTES_SENT(r) \
+ do { if (r->sent_bodyct) \
+ bgetopt (r->connection->client, BO_BYTECT, &r->bytes_sent); \
+ } while (0)
+
+/* Handling of conditional gets (if-modified-since); Roy owes Rob beer.
+ * This would be considerably easier if strptime or timegm were portable...
+ */
+
+const char month_snames[12][4] = {
+ "Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"
+};
+
+int find_month(char *mon) {
+ register int x;
+
+ for(x=0;x<12;x++)
+ if(!strcmp(month_snames[x],mon))
+ return x;
+ return -1;
+}
+
+int later_than(struct tm *lms, char *ims) {
+ char *ip;
+ char mname[MAX_STRING_LEN];
+ int year = 0, month = 0, day = 0, hour = 0, min = 0, sec = 0, x;
+
+ /* Whatever format we're looking at, it will start with weekday. */
+ /* Skip to first space. */
+ if(!(ip = strchr(ims,' ')))
+ return 0;
+ else
+ while(isspace(*ip))
+ ++ip;
+
+ if(isalpha(*ip)) {
+ /* ctime */
+ sscanf(ip,"%s %d %d:%d:%d %d",mname,&day,&hour,&min,&sec,&year);
+ }
+ else if(ip[2] == '-') {
+ /* RFC 850 (normal HTTP) */
+ char t[MAX_STRING_LEN];
+ sscanf(ip,"%s %d:%d:%d",t,&hour,&min,&sec);
+ t[2] = '\0';
+ day = atoi(t);
+ t[6] = '\0';
+ strcpy(mname,&t[3]);
+ x = atoi(&t[7]);
+ /* Prevent wraparound from ambiguity */
+ if(x < 70)
+ x += 100;
+ year = 1900 + x;
+ }
+ else {
+ /* RFC 822 */
+ sscanf(ip,"%d %s %d %d:%d:%d",&day,mname,&year,&hour,&min,&sec);
+ }
+ month = find_month(mname);
+
+ if((x = (1900+lms->tm_year) - year))
+ return x < 0;
+ if((x = lms->tm_mon - month))
+ return x < 0;
+ if((x = lms->tm_mday - day))
+ return x < 0;
+ if((x = lms->tm_hour - hour))
+ return x < 0;
+ if((x = lms->tm_min - min))
+ return x < 0;
+ if((x = lms->tm_sec - sec))
+ return x < 0;
+
+ return 1;
+}
+
+
+int set_content_length (request_rec *r, long clength)
+{
+ char ts[MAX_STRING_LEN];
+
+ sprintf (ts, "%ld", (long)r->finfo.st_size);
+ table_set (r->headers_out, "Content-length", pstrdup (r->pool, ts));
+ return 0;
+}
+
+int set_keepalive(request_rec *r)
+{
+ char *conn = table_get (r->headers_in, "Connection");
+ char *length = table_get (r->headers_out, "Content-length");
+
+ if (conn && length && !strncasecmp(conn, "Keep-Alive", 10) &&
+ r->server->keep_alive_timeout &&
+ (r->server->keep_alive > r->connection->keepalives)) {
+ char header[26];
+ int left = r->server->keep_alive - r->connection->keepalives;
+
+ r->connection->keepalive = 1;
+ r->connection->keepalives++;
+ sprintf(header, "timeout=%d, max=%d", r->server->keep_alive_timeout,
+ left);
+ table_set (r->headers_out, "Connection", "Keep-Alive");
+ table_set (r->headers_out, "Keep-Alive", pstrdup(r->pool, header));
+
+ return 1;
+ }
+
+ return 0;
+}
+
+int set_last_modified(request_rec *r, time_t mtime)
+{
+ char *ts;
+ char *if_modified_since = table_get (r->headers_in, "If-modified-since");
+
+ /* Cacheing proxies use the absence of a Last-modified header
+ * to indicate that a document is dynamic and shouldn't be cached.
+ * For the moment, we enforce that here, though it would probably
+ * work just as well to generate an Expires: header in send_http_header.
+ *
+ * However, even in that case, if no_cache is set, we would *not*
+ * want to send USE_LOCAL_COPY, since the client isn't *supposed*
+ * to have it cached.
+ */
+
+ if (r->no_cache) return OK;
+
+ ts = gm_timestr_822(r->pool, mtime);
+ table_set (r->headers_out, "Last-modified", ts);
+
+ /* Check for conditional GETs --- note that we only want this check
+ * to succeed if the GET was successful; ErrorDocuments *always* get sent.
+ */
+
+ if (r->status == 200 &&
+ if_modified_since && later_than(gmtime(&mtime), if_modified_since))
+
+ return USE_LOCAL_COPY;
+ else
+ return OK;
+}
+
+/*
+ * Finally, real protocol stuff.
+ */
+
+static char *
+getline(char *s, int n, BUFF *in)
+{
+ int retval = bgets (s, n, in);
+
+ if (retval == -1) return NULL;
+
+ if (retval > 0 && s[retval-1] == '\n') s[retval-1] = '\0';
+
+ return s;
+}
+
+void parse_uri (request_rec *r, char *uri)
+{
+ const char *s;
+ /* If we ever want to do byte-ranges a la Netscape & Franks,
+ * this is the place to parse them; with proper support in
+ * rprintf and rputc, and the sub-request setup and finalizers
+ * here, it'll all just work, even for vile cases like
+ * inclusion of byte-ranges of the output of CGI scripts, with
+ * the client requesting only a byte-range of *that*!
+ *
+ * But for now...
+ */
+
+#ifdef __EMX__
+ /* Variable for OS/2 fix below. */
+ int loop;
+#endif
+
+/* A proxy request contains a ':' early on, but not as first character */
+ for (s=uri; s != '\0'; s++)
+ if (!isalnum(*s) && *s != '+' && *s != '-' && *s != '.') break;
+
+ if (*s == ':' && s != uri)
+ {
+ r->proxyreq = 1;
+ r->uri = uri;
+ r->args = NULL;
+ } else
+ {
+ r->proxyreq = 0;
+ r->uri = getword (r->pool, &uri, '?');
+
+#ifdef __EMX__
+ /* Handle path translations for OS/2 and plug security hole. */
+ /* This will prevent "http://www.wherever.com/..\..\/" from
+ returning a directory for the root drive. */
+ for (loop = 0; loop <= strlen(r->uri); ++loop) {
+ if (r->uri[loop] == '\\')
+ r->uri[loop] = '/';
+};
+#endif
+
+ if (*uri) r->args= uri;
+ else r->args = NULL;
+ }
+}
+
+char *check_fulluri (request_rec *r, char *uri) {
+ char *name, *host;
+ int i, port;
+
+ /* This routine parses full URLs, if they match the server */
+ if (strncmp(uri, "http://", 7)) return uri;
+ name = pstrdup(r->pool, uri + 7);
+
+ /* Find the hostname, assuming a valid request */
+ i = ind(name, '/');
+ name[i] = '\0';
+
+ /* Find the port */
+ host = getword(r->pool, &name, ':');
+ if (*name) port = atoi(name);
+ else port = 80;
+
+ /* Make sure ports patch */
+ if (port != r->server->port) return uri;
+
+ /* Save it for later use */
+ r->hostname = pstrdup(r->pool, host);
+ r->hostlen = 7 + i;
+
+ /* The easy cases first */
+ if (!strcasecmp(host, r->server->server_hostname)) {
+ return (uri + r->hostlen);
+ }
+ else if (!strcmp(host, inet_ntoa(r->connection->local_addr.sin_addr))) {
+ return (uri + r->hostlen);
+ }
+
+ /* Now things get a bit trickier - check the IP address(es) of the host */
+ /* they gave, see if it matches ours. */
+ else {
+ struct hostent *hp;
+ int n;
+
+ if ((hp = gethostbyname(host))) {
+ for (n = 0; hp->h_addr_list[n] != NULL; n++) {
+ if (r->connection->local_addr.sin_addr.s_addr ==
+ (((struct in_addr *)(hp->h_addr_list[n]))->s_addr)) {
+ return (uri + r->hostlen);
+ }
+ }
+ }
+ }
+
+ return uri;
+}
+
+int read_request_line (request_rec *r)
+{
+ char l[HUGE_STRING_LEN];
+ char *ll = l, *uri;
+ conn_rec *conn = r->connection;
+ int major = 1, minor = 0; /* Assume HTTP/1.0 if non-"HTTP" protocol*/
+
+ l[0] = '\0';
+ if(!getline(l, HUGE_STRING_LEN, conn->client))
+ return 0;
+ if(!l[0])
+ return 0;
+
+ r->the_request = pstrdup (r->pool, l);
+ r->method = getword(r->pool, &ll,' ');
+ uri = getword(r->pool, &ll,' ');
+ uri = check_fulluri(r, uri);
+ parse_uri (r, uri);
+
+ r->assbackwards = (ll[0] == '\0');
+ r->protocol = pstrdup (r->pool, ll[0] ? ll : "HTTP/0.9");
+ sscanf(r->protocol, "HTTP/%d.%d", &major, &minor);
+ r->proto_num = 1000*major + minor;
+
+ return 1;
+}
+
+void get_mime_headers(request_rec *r)
+{
+ char w[MAX_STRING_LEN];
+ char *t;
+ conn_rec *c = r->connection;
+
+ while(getline(w, MAX_STRING_LEN-1, c->client)) {
+ if(!w[0])
+ return;
+ if(!(t = strchr(w,':')))
+ continue;
+ *t++ = '\0';
+ while(isspace(*t)) ++t;
+
+ table_merge (r->headers_in, w, t);
+ }
+}
+
+void check_hostalias (request_rec *r) {
+ char *host = getword(r->pool, &r->hostname, ':'); /* Get rid of port */
+ int port = (*r->hostname) ? atoi(r->hostname) : 0;
+ server_rec *s;
+
+ if (port && (port != r->server->port))
+ return;
+
+ if ((host[strlen(host)-1]) == '.') {
+ host[strlen(host)-1] = '\0';
+ }
+
+ r->hostname = host;
+
+ for (s = r->server->next; s; s = s->next) {
+ char *names = s->names;
+
+ if ((!strcasecmp(host, s->server_hostname)) &&
+ (!port || (port == s->port))) {
+ r->server = r->connection->server = s;
+ if (r->hostlen && !strncmp(r->uri, "http://", 7)) {
+ r->uri += r->hostlen;
+ parse_uri(r, r->uri);
+ }
+ }
+
+ if (!names) continue;
+
+ while (*names) {
+ char *name = getword_conf (r->pool, &names);
+
+ if ((is_matchexp(name) && !strcasecmp_match(host, name)) ||
+ (!strcasecmp(host, name))) {
+ r->server = r->connection->server = s;
+ if (r->hostlen && !strncmp(r->uri, "http://", 7)) {
+ r->uri += r->hostlen;
+ r->proxyreq = 0;
+ }
+ }
+ }
+ }
+}
+
+void check_serverpath (request_rec *r) {
+ server_rec *s;
+
+ /* This is in conjunction with the ServerPath code in
+ * http_core, so we get the right host attached to a non-
+ * Host-sending request.
+ */
+
+ for (s = r->server->next; s; s = s->next) {
+ if (s->path && !strncmp(r->uri, s->path, s->pathlen))
+ r->server = r->connection->server = s;
+ }
+}
+
+request_rec *read_request (conn_rec *conn)
+{
+ request_rec *r = (request_rec *)pcalloc (conn->pool, sizeof(request_rec));
+
+ r->connection = conn;
+ r->server = conn->server;
+ r->pool = make_sub_pool(conn->pool);
+
+ conn->keptalive = conn->keepalive;
+ conn->keepalive = 0;
+
+ conn->user = NULL;
+ conn->auth_type = NULL;
+
+ r->headers_in = make_table (r->pool, 50);
+ r->subprocess_env = make_table (r->pool, 50);
+ r->headers_out = make_table (r->pool, 5);
+ r->err_headers_out = make_table (r->pool, 5);
+ r->notes = make_table (r->pool, 5);
+
+ r->request_config = create_request_config (r->pool);
+ r->per_dir_config = r->server->lookup_defaults; /* For now. */
+
+ r->sent_bodyct = 0; /* bytect isn't for body */
+
+ r->status = 200; /* Until further notice.
+ * Only changed by die(), or (bletch!)
+ * scan_script_header...
+ */
+
+ /* Get the request... */
+
+ hard_timeout ("read", r);
+ if (!read_request_line (r)) return NULL;
+ if (!r->assbackwards) get_mime_headers(r);
+
+/* handle Host header here, to get virtual server */
+
+ if (r->hostname || (r->hostname = table_get(r->headers_in, "Host")))
+ check_hostalias(r);
+ else
+ check_serverpath(r);
+
+ kill_timeout (r);
+ conn->keptalive = 0; /* We now have a request - so no more short timeouts */
+
+ if(!strcmp(r->method, "HEAD")) {
+ r->header_only=1;
+ r->method_number = M_GET;
+ }
+ else if(!strcmp(r->method, "GET"))
+ r->method_number = M_GET;
+ else if(!strcmp(r->method,"POST"))
+ r->method_number = M_POST;
+ else if(!strcmp(r->method,"PUT"))
+ r->method_number = M_PUT;
+ else if(!strcmp(r->method,"DELETE"))
+ r->method_number = M_DELETE;
+ else if(!strcmp(r->method,"CONNECT"))
+ r->method_number = M_CONNECT;
+ else
+ r->method_number = M_INVALID; /* Will eventually croak. */
+
+ return r;
+}
+
+/*
+ * A couple of other functions which initialize some of the fields of
+ * a request structure, as appropriate for adjuncts of one kind or another
+ * to a request in progress. Best here, rather than elsewhere, since
+ * *someone* has to set the protocol-specific fields...
+ */
+
+void set_sub_req_protocol (request_rec *rnew, request_rec *r)
+{
+ rnew->assbackwards = 1; /* Don't send headers from this. */
+ rnew->no_cache = 1; /* Don't try to send USE_LOCAL_COPY for a
+ * fragment.
+ */
+ rnew->method = "GET"; rnew->method_number = M_GET;
+ rnew->protocol = "INCLUDED";
+
+ rnew->status = 200;
+
+ rnew->headers_in = r->headers_in;
+ rnew->subprocess_env = copy_table (rnew->pool, r->subprocess_env);
+ rnew->headers_out = make_table (rnew->pool, 5);
+ rnew->err_headers_out = make_table (rnew->pool, 5);
+ rnew->notes = make_table (rnew->pool, 5);
+
+ rnew->main = r;
+}
+
+void finalize_sub_req_protocol (request_rec *sub)
+{
+ SET_BYTES_SENT (sub->main);
+}
+
+/* Support for the Basic authentication protocol, and a bit for Digest.
+ */
+
+void note_auth_failure(request_rec *r)
+{
+ if (!strcasecmp(auth_type(r), "Basic"))
+ note_basic_auth_failure(r);
+ else if(!strcasecmp(auth_type(r), "Digest"))
+ note_digest_auth_failure(r);
+}
+
+void note_basic_auth_failure(request_rec *r)
+{
+ if (strcasecmp(auth_type(r), "Basic"))
+ note_auth_failure(r);
+ else
+ table_set (r->err_headers_out, "WWW-Authenticate",
+ pstrcat(r->pool, "Basic realm=\"", auth_name(r), "\"", NULL));
+}
+
+void note_digest_auth_failure(request_rec *r)
+{
+ char nonce[10];
+
+ sprintf(nonce, "%lu", time(NULL));
+ table_set (r->err_headers_out, "WWW-Authenticate",
+ pstrcat(r->pool, "Digest realm=\"", auth_name(r),
+ "\", nonce=\"", nonce, "\"", NULL));
+}
+
+int get_basic_auth_pw (request_rec *r, char **pw)
+{
+ char *auth_line = table_get (r->headers_in, "Authorization");
+ char *t;
+
+ if(!(t = auth_type(r)) || strcasecmp(t, "Basic"))
+ return DECLINED;
+
+ if (!auth_name (r)) {
+ log_reason ("need AuthName", r->uri, r);
+ return SERVER_ERROR;
+ }
+
+ if(!auth_line) {
+ note_basic_auth_failure (r);
+ return AUTH_REQUIRED;
+ }
+
+ if (strcmp(getword (r->pool, &auth_line, ' '), "Basic")) {
+ /* Client tried to authenticate using wrong auth scheme */
+ log_reason ("client used wrong authentication scheme", r->uri, r);
+ note_basic_auth_failure (r);
+ return AUTH_REQUIRED;
+ }
+
+ t = uudecode (r->pool, auth_line);
+ r->connection->user = getword_nulls (r->pool, &t, ':');
+ r->connection->auth_type = "Basic";
+
+ *pw = t;
+
+ return OK;
+}
+
+#define RESPONSE_CODE_LIST " 200 302 304 400 401 403 404 500 503 501 502 "
+
+/* New Apache routine to map error responses into array indicies
+ * e.g. 400 -> 0, 500 -> 1, 502 -> 2 ...
+ * the indicies have no significance
+ */
+
+char *status_lines[] = {
+ "200 OK",
+ "302 Found",
+ "304 Not Modified",
+ "400 Bad Request",
+ "401 Unauthorized",
+ "403 Forbidden",
+ "404 Not found",
+ "500 Server error",
+ "503 Out of resources",
+ "501 Not Implemented",
+ "502 Bad Gateway"
+};
+
+char *response_titles[] = {
+ "200 OK", /* Never actually sent, barring die(200,...) */
+ "Document moved", /* 302 Redirect */
+ "304 Not Modified", /* Never sent... 304 MUST be header only */
+ "Bad Request",
+ "Authorization Required",
+ "Forbidden",
+ "File Not found",
+ "Server Error",
+ "Out of resources",
+ "Method not implemented",
+ "Bad Gateway"
+};
+
+int index_of_response(int err_no) {
+ char *cptr, err_string[10];
+ static char *response_codes = RESPONSE_CODE_LIST;
+ int index_number;
+
+ sprintf(err_string,"%3d",err_no);
+
+ cptr = response_codes;
+ cptr++;
+ index_number = 0;
+ while (*cptr && strncmp(cptr, err_string, 3)) {
+ cptr += 4;
+ index_number++;
+ }
+ if (!*cptr) return -1;
+ return index_number;
+}
+
+
+void basic_http_header (request_rec *r)
+{
+ BUFF *fd = r->connection->client;
+
+ if (r->assbackwards) return;
+
+ if (!r->status_line)
+ r->status_line = status_lines[index_of_response(r->status)];
+
+ bvputs(fd, SERVER_PROTOCOL, " ", r->status_line, "\015\012", NULL);
+ bvputs(fd,"Date: ",gm_timestr_822 (r->pool, time(NULL)), "\015\012", NULL);
+ bvputs(fd,"Server: ", SERVER_VERSION, "\015\012", NULL);
+}
+
+char *nuke_mime_parms (pool *p, char *content_type)
+{
+ /* How marvelous. Arena doesn't *accept* "text/html; level=3"
+ * as a MIME type, so we have to strip off the parms.
+ */
+
+#ifndef ARENA_BUG_WORKAROUND
+ return content_type;
+#else
+
+ char *cp = strchr(content_type, ';');
+
+ if (cp) {
+ content_type = pstrdup (p, content_type);
+ cp = strchr (content_type, ';');
+
+ while (cp > content_type && isspace (cp[-1]))
+ --cp;
+ *cp = '\0';
+ }
+
+ return content_type;
+#endif
+}
+
+void send_http_header(request_rec *r)
+{
+ conn_rec *c = r->connection;
+ BUFF *fd = c->client;
+ const long int zero=0L;
+ array_header *hdrs_arr;
+ table_entry *hdrs;
+ int i;
+
+ core_dir_config *dir_conf =
+ (core_dir_config *)get_module_config(r->per_dir_config, &core_module);
+ char *default_type = dir_conf->default_type;
+
+ if (r->assbackwards) {
+ bsetopt(fd, BO_BYTECT, &zero);
+ r->sent_bodyct = 1;
+ return;
+ }
+
+ basic_http_header (r);
+
+ set_keepalive (r);
+
+ if (r->content_type)
+ bvputs(fd, "Content-type: ",
+ nuke_mime_parms (r->pool, r->content_type), "\015\012", NULL);
+ else if(default_type)
+ bvputs(fd, "Content-type: ", default_type, "\015\012", NULL);
+
+ if (r->content_encoding)
+ bvputs(fd,"Content-encoding: ", r->content_encoding, "\015\012", NULL);
+
+ if (r->content_language)
+ bvputs(fd,"Content-language: ", r->content_language, "\015\012", NULL);
+
+ hdrs_arr = table_elts(r->headers_out);
+ hdrs = (table_entry *)hdrs_arr->elts;
+ for (i = 0; i < hdrs_arr->nelts; ++i) {
+ if (!hdrs[i].key) continue;
+ bvputs(fd, hdrs[i].key, ": ", hdrs[i].val, "\015\012", NULL);
+ }
+
+ hdrs_arr = table_elts(r->err_headers_out);
+ hdrs = (table_entry *)hdrs_arr->elts;
+ for (i = 0; i < hdrs_arr->nelts; ++i) {
+ if (!hdrs[i].key) continue;
+ bvputs(fd, hdrs[i].key, ": ", hdrs[i].val, "\015\012", NULL);
+ }
+
+ bputs("\015\012",fd);
+
+ if (c->keepalive)
+ bflush(r->connection->client); /* For bugs in Netscape, perhaps */
+
+ bsetopt(fd, BO_BYTECT, &zero);
+ r->sent_bodyct = 1; /* Whatever follows is real body stuff... */
+}
+
+long read_client_block (request_rec *r, char *buffer, int bufsiz)
+{
+ return bread(r->connection->client, buffer, bufsiz);
+}
+
+long send_fd(FILE *f, request_rec *r)
+{
+ char buf[IOBUFSIZE];
+ long total_bytes_sent;
+ register int n,o,w;
+ conn_rec *c = r->connection;
+
+ total_bytes_sent = 0;
+ while (!r->connection->aborted) {
+ while ((n= fread(buf, sizeof(char), IOBUFSIZE, f)) < 1
+ && ferror(f) && errno == EINTR)
+ continue;
+
+ if (n < 1) {
+ break;
+ }
+ o=0;
+ total_bytes_sent += n;
+
+ while(n && !r->connection->aborted) {
+ w=bwrite(c->client, &buf[o], n);
+ if(w <= 0)
+ break;
+ reset_timeout(r); /* reset timeout after successfule write */
+ n-=w;
+ o+=w;
+ }
+ }
+ bflush(c->client);
+
+ SET_BYTES_SENT(r);
+ return total_bytes_sent;
+}
+
+int rputc (int c, request_rec *r)
+{
+ if (r->connection->aborted) return EOF;
+ bputc(c, r->connection->client);
+ SET_BYTES_SENT(r);
+ return c;
+}
+
+int
+rputs(const char *str, request_rec *r)
+{
+ if (r->connection->aborted) return EOF;
+ SET_BYTES_SENT(r);
+ return bputs(str, r->connection->client);
+}
+
+int rprintf(request_rec *r,const char *fmt,...)
+ {
+ va_list vlist;
+ int n;
+
+ if(r->connection->aborted) return EOF;
+ va_start(vlist,fmt);
+ n=vbprintf(r->connection->client,fmt,vlist);
+ va_end(vlist);
+ SET_BYTES_SENT(r);
+ return n;
+ }
+
+int
+rvputs(request_rec *r, ...)
+{
+ va_list args;
+ int i, j, k;
+ const char *x;
+ BUFF *fb=r->connection->client;
+
+ if (r->connection->aborted) return EOF;
+
+ va_start (args, r);
+ for (k=0;;)
+ {
+ x = va_arg(args, const char *);
+ if (x == NULL) break;
+ j = strlen(x);
+ i = bwrite(fb, x, j);
+ if (i != j)
+ {
+ va_end(args);
+ return -1;
+ }
+ k += i;
+ }
+ va_end(args);
+
+ SET_BYTES_SENT(r);
+ return k;
+}
+
+void send_error_response (request_rec *r, int recursive_error)
+{
+ conn_rec *c = r->connection;
+ char *custom_response;
+ int status = r->status;
+ int idx = index_of_response (status);
+ char *location = table_get (r->headers_out, "Location");
+
+ if (!r->assbackwards) {
+ int i;
+ table *err_hdrs_arr = r->err_headers_out;
+ table_entry *err_hdrs = (table_entry *)err_hdrs_arr->elts;
+
+ basic_http_header (r);
+
+ /* For conditional get's which didn't send anything, *don't*
+ * send a bogus content-type, or any body --- but must still
+ * terminate header.
+ */
+
+ if (status == USE_LOCAL_COPY) {
+ if (set_keepalive(r))
+ bputs("Connection: Keep-Alive\015\012", c->client);
+ bputs("\015\012", c->client);
+ return;
+ }
+
+ if (status == REDIRECT)
+ bvputs(c->client, "Location: ", location, "\015\012", NULL);
+
+ for (i = 0; i < err_hdrs_arr->nelts; ++i) {
+ if (!err_hdrs[i].key) continue;
+ bvputs(c->client, err_hdrs[i].key, ": ", err_hdrs[i].val,
+ "\015\012", NULL);
+ }
+
+ bputs("Content-type: text/html\015\012\015\012", c->client);
+ }
+
+ if (r->header_only) return;
+
+ if ((custom_response = response_code_string (r, idx)))
+ bputs(custom_response, c->client);
+ else {
+ char *title = response_titles[idx];
+ BUFF *fd = c->client;
+
+ bvputs(fd,"", title, "\n", title,
+ "
\n", NULL);
+
+ switch (r->status) {
+ case REDIRECT:
+ bvputs(fd, "The document has moved pool, location), "\">here.\n", NULL);
+ break;
+ case AUTH_REQUIRED:
+ bputs("This server could not verify that you\n", fd);
+ bputs("are authorized to access the document you\n", fd);
+ bputs("requested. Either you supplied the wrong\n", fd);
+ bputs("credentials (e.g., bad password), or your\n", fd);
+ bputs("browser doesn't understand how to supply\n", fd);
+ bputs("the credentials required.
\n", fd);
+ break;
+ case BAD_REQUEST:
+ bputs("Your browser sent a query that\n", fd);
+ bputs("this server could not understand.
\n", fd);
+ break;
+ case FORBIDDEN:
+ bvputs(fd, "You don't have permission to access ",
+ escape_html(r->pool, r->uri), "\non this server.
\n",
+ NULL);
+ break;
+ case NOT_FOUND:
+ bvputs(fd, "The requested URL ", escape_html(r->pool, r->uri),
+ " was not found on this server.
\n", NULL);
+ break;
+ case SERVER_ERROR:
+ bputs("The server encountered an internal error or\n", fd);
+ bputs("misconfiguration and was unable to complete\n", fd);
+ bputs("your request.
\n", fd);
+ bputs("Please contact the server administrator,\n ", fd);
+ bputs(escape_html(r->pool, r->server->server_admin), fd);
+ bputs(" and inform them of the time the error occurred,\n", fd);
+ bputs("and anything you might have done that may have\n", fd);
+ bputs("caused the error.
\n", fd);
+ break;
+ case NOT_IMPLEMENTED:
+ bvputs(fd, escape_html(r->pool, r->method), " to ",
+ escape_html(r->pool, r->uri), " not supported.
\n", NULL);
+ break;
+ case BAD_GATEWAY:
+ bputs("The proxy server received an invalid\015\012", fd);
+ bputs("response from an upstream server.
\015\012", fd);
+ break;
+ }
+
+ if (recursive_error) {
+ char x[80];
+ sprintf (x, "Additionally, an error of type %d was encountered\n",
+ recursive_error);
+ bputs(x, fd);
+ bputs("while trying to use an ErrorDocument to\n", fd);
+ bputs("handle the request.\n", fd);
+ }
+ bputs("\n", fd);
+ }
+
+}
+
+/* Finally, this... it's here to support nph- scripts
+ * Now what ever are we going to do about them when HTTP-NG packetization
+ * comes along?
+ */
+
+void client_to_stdout (conn_rec *c)
+{
+ bflush(c->client);
+ dup2(c->client->fd, STDOUT_FILENO);
+}
diff --git a/RELEASE_1_1_X/src/main/http_request.c b/RELEASE_1_1_X/src/main/http_request.c
new file mode 100644
index 00000000000..db5b13c93f5
--- /dev/null
+++ b/RELEASE_1_1_X/src/main/http_request.c
@@ -0,0 +1,870 @@
+
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+
+/*
+ * http_request.c: functions to get and process requests
+ *
+ * Rob McCool 3/21/93
+ *
+ * Thoroughly revamped by rst for Shambhala. NB this file reads
+ * best from the bottom up.
+ *
+ */
+
+#define CORE_PRIVATE
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "scoreboard.h"
+
+/*****************************************************************
+ *
+ * Getting and checking directory configuration. Also checks the
+ * FollowSymlinks and FollowSymOwner stuff, since this is really the
+ * only place that can happen (barring a new mid_dir_walk callout).
+ *
+ * We can't do it as an access_checker module function which gets
+ * called with the final per_dir_config, since we could have a directory
+ * with FollowSymLinks disabled, which contains a symlink to another
+ * with a .htaccess file which turns FollowSymLinks back on --- and
+ * access in such a case must be denied. So, whatever it is that
+ * checks FollowSymLinks needs to know the state of the options as
+ * they change, all the way down.
+ */
+
+int check_symlinks (char *d, int opts)
+{
+ struct stat lfi, fi;
+ char *lastp;
+ int res;
+
+#ifdef __EMX__
+ /* OS/2 dosen't have symlinks */
+ return OK;
+#else
+
+ if (opts & OPT_SYM_LINKS) return OK;
+
+ /* Strip trailing '/', if any, off what we're checking; trailing
+ * slashes make some systems follow symlinks to directories even in
+ * lstat(). After we've done the lstat, put it back. Also, don't
+ * bother checking '/' at all...
+ *
+ * Note that we don't have to worry about multiple slashes here
+ * because of no2slash() below...
+ */
+
+ lastp = d + strlen(d) - 1;
+ if (lastp == d) return OK; /* Root directory, '/' */
+
+ if (*lastp == '/') *lastp = '\0';
+ else lastp = NULL;
+
+ res = lstat (d, &lfi);
+
+ if (lastp) *lastp = '/';
+
+ /* Note that we don't reject accesses to nonexistent files (multiviews
+ * or the like may cons up a way to run the transaction anyway)...
+ */
+
+ if (!(res >= 0) || !S_ISLNK(lfi.st_mode)) return OK;
+
+ /* OK, it's a symlink. May still be OK with OPT_SYM_OWNER */
+
+ if (!(opts & OPT_SYM_OWNER)) return FORBIDDEN;
+
+ if (stat (d, &fi) < 0) return FORBIDDEN;
+
+ return (fi.st_uid == lfi.st_uid) ? OK : FORBIDDEN;
+
+#endif
+}
+
+/* Dealing with the file system to get PATH_INFO
+ */
+
+void get_path_info(request_rec *r)
+{
+ char *cp;
+ char *path = r->filename;
+ char *end = &path[strlen(path)];
+ char *last_cp = NULL;
+ int rv;
+
+ /* Advance over trailing slashes ... NOT part of filename */
+
+ for (cp = end; cp > path && cp[-1] == '/'; --cp)
+ continue;
+
+ while (cp > path) {
+
+ /* See if the pathname ending here exists... */
+
+ *cp = '\0';
+ rv = stat(path, &r->finfo);
+ if (cp != end) *cp = '/';
+
+ if (!rv) {
+
+ /* Aha! Found something. If it was a directory, we will
+ * search contents of that directory for a multi_match, so
+ * the PATH_INFO argument starts with the component after that.
+ */
+
+ if (S_ISDIR(r->finfo.st_mode) && last_cp) {
+ r->finfo.st_mode = 0; /* No such file... */
+ cp = last_cp;
+ }
+
+ r->path_info = pstrdup (r->pool, cp);
+ *cp = '\0';
+ return;
+ }
+ else {
+ last_cp = cp;
+
+ while (--cp > path && *cp != '/')
+ continue;
+
+ while (cp > path && cp[-1] == '/')
+ --cp;
+ }
+ }
+}
+
+int directory_walk (request_rec *r)
+{
+ core_server_config *sconf = get_module_config (r->server->module_config,
+ &core_module);
+ array_header *sec_array = copy_array (r->pool, sconf->sec);
+ void *per_dir_defaults = r->server->lookup_defaults;
+
+ core_dir_config **sec = (core_dir_config **)sec_array->elts;
+ int num_sec = sec_array->nelts;
+ char *test_filename = pstrdup (r->pool, r->filename);
+
+ int num_dirs, res;
+ int i;
+
+ /* Are we dealing with a file? If not, we can (hopefuly) safely assume
+ * we have a handler that doesn't require one, but for safety's sake,
+ * and so we have something find_types() can get something out of,
+ * fake one. But don't run through the directory entries.
+ */
+
+ if (test_filename == NULL) {
+ r->filename = pstrdup(r->pool, r->uri);
+ r->finfo.st_mode = 0; /* Not really a file... */
+ r->per_dir_config = per_dir_defaults;
+
+ return OK;
+ }
+
+ /* Go down the directory hierarchy. Where we have to check for symlinks,
+ * do so. Where a .htaccess file has permission to override anything,
+ * try to find one. If either of these things fails, we could poke
+ * around, see why, and adjust the lookup_rec accordingly --- this might
+ * save us a call to get_path_info (with the attendant stat()s); however,
+ * for the moment, that's not worth the trouble.
+ */
+
+ if (test_filename[0] != '/')
+ {
+/* fake filenames only match Directory sections */
+ void *this_conf, *entry_config;
+ core_dir_config *entry_core;
+ char *entry_dir;
+ int j;
+
+ for (j = 0; j < num_sec; ++j) {
+
+ entry_config = sec[j];
+ if (!entry_config) continue;
+
+ entry_core =(core_dir_config *)
+ get_module_config(entry_config, &core_module);
+ entry_dir = entry_core->d;
+
+ this_conf = NULL;
+ if (is_matchexp(entry_dir)) {
+ if (!strcmp_match(test_filename, entry_dir))
+ this_conf = entry_config;
+ }
+ else if (!strncmp (test_filename, entry_dir, strlen(entry_dir)))
+ this_conf = entry_config;
+
+ if (this_conf)
+ per_dir_defaults = merge_per_dir_configs (r->pool,
+ per_dir_defaults, this_conf);
+ }
+
+ r->per_dir_config = per_dir_defaults;
+
+ return OK;
+ }
+
+ no2slash (test_filename);
+ num_dirs = count_dirs(test_filename);
+ get_path_info (r);
+
+ if (S_ISDIR (r->finfo.st_mode)) ++num_dirs;
+
+ for (i = 1; i <= num_dirs; ++i) {
+ core_dir_config *core_dir =
+ (core_dir_config *)get_module_config(per_dir_defaults, &core_module);
+ int allowed_here = core_dir->opts;
+ int overrides_here = core_dir->override;
+ void *this_conf = NULL, *htaccess_conf = NULL;
+ char *this_dir = make_dirstr (r->pool, test_filename, i);
+ char *config_name = make_full_path(r->pool, this_dir,
+ sconf->access_name);
+ int j;
+
+ /* Do symlink checks first, because they are done with the
+ * permissions appropriate to the *parent* directory...
+ */
+
+ if ((res = check_symlinks (this_dir, allowed_here)))
+ {
+ log_reason("Symbolic link not allowed", this_dir, r);
+ return res;
+ }
+
+ /* Begin *this* level by looking for matching sections from
+ * access.conf.
+ */
+
+ for (j = 0; j < num_sec; ++j) {
+ void *entry_config = sec[j];
+ core_dir_config *entry_core;
+ char *entry_dir;
+
+ if (!entry_config) continue;
+
+ entry_core =
+ (core_dir_config *)get_module_config(entry_config, &core_module);
+ entry_dir = entry_core->d;
+
+ if (is_matchexp(entry_dir) && !strcmp_match(this_dir, entry_dir)) {
+ /* Don't try this wildcard again --- if it ends in '*'
+ * it'll match again, and subdirectories won't be able to
+ * override it...
+ */
+ sec[j] = NULL;
+ this_conf = entry_config;
+ }
+ else if (!strcmp (this_dir, entry_dir))
+ this_conf = entry_config;
+ }
+
+ if (this_conf)
+ {
+ per_dir_defaults =
+ merge_per_dir_configs (r->pool, per_dir_defaults, this_conf);
+ core_dir =(core_dir_config *)get_module_config(per_dir_defaults,
+ &core_module);
+ }
+ overrides_here = core_dir->override;
+
+ /* If .htaccess files are enabled, check for one.
+ */
+
+ if (overrides_here) {
+ res = parse_htaccess (&htaccess_conf, r, overrides_here,
+ this_dir, config_name);
+ if (res) return res;
+ }
+
+ if (htaccess_conf)
+ per_dir_defaults =
+ merge_per_dir_configs (r->pool, per_dir_defaults,
+ htaccess_conf);
+
+ }
+
+ r->per_dir_config = per_dir_defaults;
+
+ if ((res = check_symlinks (r->filename, allow_options(r))))
+ {
+ log_reason("Symbolic link not allowed", r->filename, r);
+ return res;
+ }
+
+ return OK; /* Can only "fail" if access denied
+ * by the symlink goop.
+ */
+}
+
+int location_walk (request_rec *r)
+{
+ core_server_config *sconf = get_module_config (r->server->module_config,
+ &core_module);
+ array_header *url_array = copy_array (r->pool, sconf->sec_url);
+ void *per_dir_defaults = r->per_dir_config;
+
+ core_dir_config **url = (core_dir_config **)url_array->elts;
+ int num_url = url_array->nelts;
+ char *test_location = pstrdup (r->pool, r->uri);
+
+ /* Go through the location entries, and check for matches. */
+
+ if (num_url) {
+ void *this_conf, *entry_config;
+ core_dir_config *entry_core;
+ char *entry_url;
+ int j;
+
+/*
+ * we apply the directive sections in some order; should really try them
+ * with the most general first.
+ */
+ for (j = 0; j < num_url; ++j) {
+
+ entry_config = url[j];
+ if (!entry_config) continue;
+
+ entry_core =(core_dir_config *)
+ get_module_config(entry_config, &core_module);
+ entry_url = entry_core->d;
+
+ this_conf = NULL;
+ if (is_matchexp(entry_url)) {
+ if (!strcmp_match(test_location, entry_url))
+ this_conf = entry_config;
+ }
+ else if (!strncmp (test_location, entry_url, strlen(entry_url)))
+ this_conf = entry_config;
+
+ if (this_conf)
+ per_dir_defaults = merge_per_dir_configs (r->pool,
+ per_dir_defaults, this_conf);
+ }
+
+ r->per_dir_config = per_dir_defaults;
+ }
+
+ return OK;
+}
+
+/*****************************************************************
+ *
+ * The sub_request mechanism.
+ *
+ * Fns to look up a relative URI from, e.g., a map file or SSI document.
+ * These do all access checks, etc., but don't actually run the transaction
+ * ... use run_sub_req below for that. Also, be sure to use destroy_sub_req
+ * as appropriate if you're likely to be creating more than a few of these.
+ * (An early Shambhala version didn't destroy the sub_reqs used in directory
+ * indexing. The result, when indexing a directory with 800-odd files in
+ * it, was massively excessive storage allocation).
+ *
+ * Note more manipulation of protocol-specific vars in the request
+ * structure...
+ */
+
+request_rec *make_sub_request (request_rec *r)
+{
+ pool *rrp = make_sub_pool (r->pool);
+ request_rec *rr = pcalloc (rrp, sizeof (request_rec));
+
+ rr->pool = rrp;
+ return rr;
+}
+
+request_rec *sub_req_lookup_simple (char *new_file, request_rec *r)
+{
+ /* This handles the simple case, common to ..._lookup_uri and _file,
+ * of looking up another file in the same directory.
+ */
+ request_rec *rnew = make_sub_request (r);
+ pool *rnewp = rnew->pool;
+ int res;
+
+ char *udir = make_dirstr(rnewp, r->uri, count_dirs(r->uri));
+ char *fdir = make_dirstr(rnewp, r->filename, count_dirs(r->filename));
+
+ *rnew = *r; /* Copy per_dir config, etc. */
+ rnew->pool = rnewp;
+ rnew->uri = make_full_path (rnewp, udir, new_file);
+ rnew->filename = make_full_path (rnewp, fdir, new_file);
+ set_sub_req_protocol (rnew, r);
+
+ rnew->finfo.st_mode = 0;
+
+ if ((res = check_symlinks (rnew->filename, allow_options (rnew))))
+ {
+ rnew->status = res;
+ }
+
+ if (rnew->finfo.st_mode == 0 && stat (rnew->filename, &rnew->finfo) < 0)
+ rnew->finfo.st_mode = 0;
+
+ if ((rnew->status == 200) && (res = find_types (rnew)))
+ rnew->status = res;
+
+ if ((rnew->status == 200) && (res = run_fixups (rnew)))
+ rnew->status = res;
+
+ return rnew;
+}
+
+
+static int some_auth_required (request_rec *r);
+
+request_rec *sub_req_lookup_uri (char *new_file, request_rec *r)
+{
+ request_rec *rnew;
+ int res;
+ char *udir;
+
+ rnew = make_sub_request (r);
+ rnew->connection = r->connection;
+ rnew->server = r->server;
+ rnew->request_config = create_request_config (rnew->pool);
+ rnew->htaccess = r->htaccess; /* copy htaccess cache */
+ set_sub_req_protocol (rnew, r);
+
+ if (new_file[0] == '/')
+ parse_uri(rnew, new_file);
+ else
+ {
+ udir = make_dirstr (rnew->pool, r->uri, count_dirs (r->uri));
+ udir = escape_uri(rnew->pool, udir); /* re-escape it */
+ parse_uri (rnew, make_full_path (rnew->pool, udir, new_file));
+ }
+
+ res = unescape_url (rnew->uri);
+ if (res)
+ {
+ rnew->status = res;
+ return rnew;
+ }
+
+ getparents (rnew->uri);
+
+ res = translate_name(rnew);
+ if (res)
+ {
+ rnew->status = res;
+ return rnew;
+ }
+
+ /* We could be clever at this point, and avoid calling directory_walk, etc.
+ * However, we'd need to test that the old and new filenames contain the
+ * same directory components, so it would require duplicating the start
+ * of translate_name.
+ * Instead we rely on the cache of .htaccess results.
+ */
+
+ if ((res = directory_walk (rnew))
+ || (!some_auth_required (rnew) ? 0 :
+ ((res = check_user_id (rnew)) || (res = check_auth (rnew))))
+ || (res = check_access (rnew))
+ || (res = find_types (rnew))
+ || (res = run_fixups (rnew))
+ )
+ {
+ rnew->status = res;
+ }
+
+ return rnew;
+}
+
+request_rec *sub_req_lookup_file (char *new_file, request_rec *r)
+{
+ request_rec *rnew;
+ int res;
+ char *fdir;
+
+ /* Check for a special case... if there are no '/' characters in new_file
+ * at all, then we are looking at a relative lookup in the same directory.
+ * That means we don't have to redo any access checks.
+ */
+
+ if (strchr (new_file, '/') == NULL)
+ return sub_req_lookup_simple (new_file, r);
+
+ rnew = make_sub_request (r);
+ fdir = make_dirstr (rnew->pool, r->filename, count_dirs (r->filename));
+
+ rnew->connection = r->connection; /* For now... */
+ rnew->server = r->server;
+ rnew->request_config = create_request_config (rnew->pool);
+ rnew->htaccess = r->htaccess; /* copy htaccess cache */
+ set_sub_req_protocol (rnew, r);
+
+ rnew->uri = "INTERNALLY GENERATED file-relative req";
+ rnew->filename = ((new_file[0] == '/') ?
+ new_file :
+ make_full_path (rnew->pool, fdir, new_file));
+
+ if ((res = directory_walk (rnew))
+ || (res = check_access (rnew))
+ || (!some_auth_required (rnew) ? 0 :
+ ((res = check_user_id (rnew)) && (res = check_auth (rnew))))
+ || (res = find_types (rnew))
+ || (res = run_fixups (rnew))
+ )
+ {
+ rnew->status = res;
+ }
+
+ return rnew;
+}
+
+int run_sub_req (request_rec *r)
+{
+ int retval = invoke_handler (r);
+ finalize_sub_req_protocol (r);
+ return retval;
+}
+
+void destroy_sub_req (request_rec *r)
+{
+ /* Reclaim the space */
+ destroy_pool (r->pool);
+}
+
+/*****************************************************************
+ *
+ * Mainline request processing...
+ */
+
+void die(int type, request_rec *r)
+{
+ int error_index = index_of_response (type);
+ char *custom_response = response_code_string(r, error_index);
+ int recursive_error = 0;
+
+ /* The following takes care of Apache redirects to custom response URLs
+ * Note that if we are already dealing with the response to some other
+ * error condition, we just report on the original error, and give up on
+ * any attempt to handle the other thing "intelligently"...
+ */
+
+ if (r->status != 200) {
+ recursive_error = type;
+
+ while (r->prev && r->prev->status != 200)
+ r = r->prev; /* Get back to original error */
+
+ type = r->status;
+ custom_response = NULL; /* Do NOT retry the custom thing! */
+ }
+
+ r->status = type;
+
+ /* Two types of custom redirects --- plain text, and URLs.
+ * Plain text has a leading '"', so the URL code, here, is triggered
+ * on its absence
+ */
+
+ if (custom_response && custom_response[0] != '"') {
+
+ if (is_url(custom_response)) {
+ /* The URL isn't local, so lets drop through the rest of
+ * this apache code, and continue with the usual REDIRECT
+ * handler. But note that the client will ultimately see
+ * the wrong status...
+ */
+ r->status = REDIRECT;
+ table_set (r->headers_out, "Location", custom_response);
+ } else if ( custom_response[0] == '/') {
+ r->no_cache = 1; /* Do NOT send USE_LOCAL_COPY for
+ * error documents!
+ */
+ /* This redirect needs to be a GET no matter what the original
+ * method was.
+ */
+ r->method = pstrdup(r->pool, "GET");
+ r->method_number = M_GET;
+ internal_redirect (custom_response, r);
+ return;
+ } else {
+ /* Dumb user has given us a bad url to redirect to
+ * --- fake up dying with a recursive server error...
+ */
+ recursive_error = SERVER_ERROR;
+ log_reason("Invalid error redirection directive", custom_response,
+ r);
+ }
+ }
+
+ send_error_response (r, recursive_error);
+}
+
+static void decl_die (int status, char *phase, request_rec *r)
+{
+ if (status == DECLINED) {
+ log_reason (pstrcat (r->pool,
+ "configuration error: couldn't ",
+ phase, NULL),
+ r->uri,
+ r);
+ die (SERVER_ERROR, r);
+ }
+ else die (status, r);
+}
+
+static int some_auth_required (request_rec *r)
+{
+ /* Is there a require line configured for the type of *this* req? */
+
+ array_header *reqs_arr = requires (r);
+ require_line *reqs;
+ int i;
+
+ if (!reqs_arr) return 0;
+
+ reqs = (require_line *)reqs_arr->elts;
+
+ for (i = 0; i < reqs_arr->nelts; ++i)
+ if (reqs[i].method_mask & (1 << r->method_number))
+ return 1;
+
+ return 0;
+}
+
+void process_request_internal (request_rec *r)
+{
+ int access_status;
+
+ /* Kludge to be reading the assbackwards field outside of protocol.c,
+ * but we've got to check for this sort of nonsense somewhere...
+ */
+
+ if (r->assbackwards && r->header_only) {
+ /* Client asked for headers only with HTTP/0.9, which doesn't
+ * send headers! Have to dink things even to make sure the
+ * error message comes through...
+ */
+ log_reason ("client sent illegal HTTP/0.9 request", r->uri, r);
+ r->header_only = 0;
+ die (BAD_REQUEST, r);
+ return;
+ }
+
+ if (!r->hostname && (r->proto_num >= 1001)) {
+ /* Client sent us a HTTP/1.1 or later request without telling
+ * us the hostname, either with a full URL or a Host: header.
+ * We therefore need to (as per the 1.1 spec) send an error
+ */
+ log_reason ("client sent HTTP/1.1 request without hostname",
+ r->uri, r);
+ die (BAD_REQUEST, r);
+ return;
+ }
+
+ if (!r->proxyreq)
+ {
+ access_status = unescape_url(r->uri);
+ if (access_status)
+ {
+ die(access_status, r);
+ return;
+ }
+
+ getparents(r->uri); /* OK --- shrinking transformations... */
+ }
+
+ if ((access_status = translate_name (r))) {
+ decl_die (access_status, "translate", r);
+ return;
+ }
+
+ if ((access_status = directory_walk (r))) {
+ die (access_status, r);
+ return;
+ }
+
+ if ((access_status = location_walk (r))) {
+ die (access_status, r);
+ return;
+ }
+
+ if ((access_status = check_access (r)) != 0) {
+ decl_die (access_status, "check access", r);
+ return;
+ }
+
+ if (some_auth_required (r)) {
+ if ((access_status = check_user_id (r)) != 0) {
+ decl_die (access_status, "check user. No user file?", r);
+ return;
+ }
+
+ if ((access_status = check_auth (r)) != 0) {
+ decl_die (access_status, "check access. No groups file?", r);
+ return;
+ }
+ }
+
+ if ((access_status = find_types (r)) != 0) {
+ decl_die (access_status, "find types", r);
+ return;
+ }
+
+ if ((access_status = run_fixups (r)) != 0) {
+ die (access_status, r);
+ return;
+ }
+
+ if ((access_status = invoke_handler (r)) != 0)
+ die (access_status, r);
+}
+
+void process_request (request_rec *r)
+{
+#ifdef STATUS
+ int old_stat;
+#endif /* STATUS */
+ process_request_internal (r);
+#ifdef STATUS
+ old_stat = update_child_status (r->connection->child_num, SERVER_BUSY_LOG,
+ r);
+#endif /* STATUS */
+ log_transaction (r);
+#ifdef STATUS
+ (void)update_child_status (r->connection->child_num, old_stat, r);
+#endif /* STATUS */
+}
+
+table *rename_original_env (pool *p, table *t)
+{
+ array_header *env_arr = table_elts (t);
+ table_entry *elts = (table_entry *)env_arr->elts;
+ table *new = make_table (p, env_arr->nelts);
+ int i;
+
+ for (i = 0; i < env_arr->nelts; ++i) {
+ if (!elts[i].key) continue;
+ table_set (new, pstrcat (p, "REDIRECT_", elts[i].key, NULL),
+ elts[i].val);
+ }
+
+ return new;
+}
+
+request_rec *internal_internal_redirect (char *new_uri, request_rec *r)
+{
+ request_rec *new = (request_rec *)pcalloc(r->pool, sizeof(request_rec));
+ char t[10]; /* Long enough... */
+
+ new->connection = r->connection;
+ new->server = r->server;
+ new->pool = r->pool;
+
+ /* A whole lot of this really ought to be shared with protocol.c...
+ * another missing cleanup. It's particularly inappropriate to be
+ * setting header_only, etc., here.
+ */
+
+ parse_uri (new, new_uri);
+ new->request_config = create_request_config (r->pool);
+ new->per_dir_config = r->server->lookup_defaults;
+
+ new->prev = r;
+ r->next = new;
+
+ /* Inherit the rest of the protocol info... */
+
+ new->method = r->method;
+ new->method_number = r->method_number;
+
+ new->status = r->status;
+ new->assbackwards = r->assbackwards;
+ new->header_only = r->header_only;
+ new->protocol = r->protocol;
+ new->main = r->main;
+
+ new->headers_in = r->headers_in;
+ new->headers_out = make_table (r->pool, 5);
+ new->err_headers_out = r->err_headers_out;
+ new->subprocess_env = rename_original_env (r->pool, r->subprocess_env);
+ new->notes = make_table (r->pool, 5);
+ new->htaccess = r->htaccess; /* copy .htaccess cache */
+
+ new->no_cache = r->no_cache; /* If we've already made up our minds
+ * about this, don't change 'em back!
+ */
+
+ sprintf (t, "%d", r->status);
+ table_set (new->subprocess_env, "REDIRECT_STATUS", pstrdup (r->pool, t));
+
+ return new;
+}
+
+void internal_redirect (char *new_uri, request_rec *r)
+{
+ request_rec *new = internal_internal_redirect(new_uri, r);
+ process_request_internal (new);
+}
+
+/* This function is designed for things like actions or CGI scripts, when
+ * using AddHandler, and you want to preserve the content type across
+ * an internal redirect.
+ */
+
+void internal_redirect_handler (char *new_uri, request_rec *r)
+{
+ request_rec *new = internal_internal_redirect(new_uri, r);
+ if (r->handler)
+ new->content_type = r->content_type;
+ process_request_internal (new);
+}
diff --git a/RELEASE_1_1_X/src/main/md5c.c b/RELEASE_1_1_X/src/main/md5c.c
new file mode 100644
index 00000000000..4ec60cb1a38
--- /dev/null
+++ b/RELEASE_1_1_X/src/main/md5c.c
@@ -0,0 +1,354 @@
+/*
+ * This is work is derived from material Copyright RSA Data Security, Inc.
+ *
+ * The RSA copyright statement and Licence for that original material is
+ * included below. This is followed by the Apache copyright statement and
+ * licence for the modifications made to that material.
+ */
+
+/* MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
+ */
+
+/* Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
+rights reserved.
+
+License to copy and use this software is granted provided that it
+is identified as the "RSA Data Security, Inc. MD5 Message-Digest
+Algorithm" in all material mentioning or referencing this software
+or this function.
+
+License is also granted to make and use derivative works provided
+that such works are identified as "derived from the RSA Data
+Security, Inc. MD5 Message-Digest Algorithm" in all material
+mentioning or referencing the derived work.
+
+RSA Data Security, Inc. makes no representations concerning either
+the merchantability of this software or the suitability of this
+software for any particular purpose. It is provided "as is"
+without express or implied warranty of any kind.
+
+These notices must be retained in any copies of any part of this
+documentation and/or software.
+ */
+
+/* ====================================================================
+ * Copyright (c) 1996 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+#include
+
+#include "md5.h"
+
+/* Constants for MD5Transform routine.
+ */
+
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+
+static void MD5Transform(UINT4 state[4], const unsigned char block[64]);
+static void Encode(unsigned char *output, const UINT4 *input,
+ unsigned int len);
+static void Decode(UINT4 *output, const unsigned char *input,
+ unsigned int len);
+
+static unsigned char PADDING[64] =
+{
+ 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* F, G, H and I are basic MD5 functions.
+ */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits.
+ */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
+Rotation is separate from addition to prevent recomputation.
+ */
+#define FF(a, b, c, d, x, s, ac) { \
+ (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define GG(a, b, c, d, x, s, ac) { \
+ (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define HH(a, b, c, d, x, s, ac) { \
+ (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define II(a, b, c, d, x, s, ac) { \
+ (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+
+/* MD5 initialization. Begins an MD5 operation, writing a new context.
+ */
+void
+MD5Init(MD5_CTX *context)
+{
+ context->count[0] = context->count[1] = 0;
+ /* Load magic initialization constants. */
+ context->state[0] = 0x67452301;
+ context->state[1] = 0xefcdab89;
+ context->state[2] = 0x98badcfe;
+ context->state[3] = 0x10325476;
+}
+
+/* MD5 block update operation. Continues an MD5 message-digest
+ operation, processing another message block, and updating the
+ context.
+ */
+void
+MD5Update(MD5_CTX *context, const unsigned char *input, unsigned int inputLen)
+{
+ unsigned int i, index, partLen;
+
+ /* Compute number of bytes mod 64 */
+ index = (unsigned int)((context->count[0] >> 3) & 0x3F);
+
+ /* Update number of bits */
+ if ((context->count[0] += ((UINT4)inputLen << 3)) < ((UINT4)inputLen << 3))
+ context->count[1]++;
+ context->count[1] += (UINT4)inputLen >> 29;
+
+ partLen = 64 - index;
+
+ /* Transform as many times as possible. */
+ if (inputLen >= partLen)
+ {
+ memcpy(&context->buffer[index], input, partLen);
+ MD5Transform(context->state, context->buffer);
+
+ for (i = partLen; i + 63 < inputLen; i += 64)
+ MD5Transform(context->state, &input[i]);
+
+ index = 0;
+ }
+ else
+ i = 0;
+
+ /* Buffer remaining input */
+ memcpy(&context->buffer[index], &input[i], inputLen-i);
+}
+
+/* MD5 finalization. Ends an MD5 message-digest operation, writing the
+ the message digest and zeroizing the context.
+ */
+void
+MD5Final(unsigned char digest[16], MD5_CTX *context)
+{
+ unsigned char bits[8];
+ unsigned int index, padLen;
+
+ /* Save number of bits */
+ Encode (bits, context->count, 8);
+
+ /* Pad out to 56 mod 64. */
+ index = (unsigned int)((context->count[0] >> 3) & 0x3f);
+ padLen = (index < 56) ? (56 - index) : (120 - index);
+ MD5Update(context, PADDING, padLen);
+
+ /* Append length (before padding) */
+ MD5Update(context, bits, 8);
+
+ /* Store state in digest */
+ Encode(digest, context->state, 16);
+
+ /* Zeroize sensitive information. */
+ memset(context, 0, sizeof (*context));
+}
+
+/* MD5 basic transformation. Transforms state based on block. */
+static void
+MD5Transform(UINT4 state[4], const unsigned char block[64])
+{
+ UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16];
+
+ Decode (x, block, 64);
+
+ /* Round 1 */
+ FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */
+ FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */
+ FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */
+ FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */
+ FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */
+ FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */
+ FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */
+ FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */
+ FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */
+ FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */
+ FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */
+ FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */
+ FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */
+ FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */
+ FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */
+ FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */
+
+ /* Round 2 */
+ GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */
+ GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */
+ GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */
+ GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */
+ GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */
+ GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */
+ GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */
+ GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */
+ GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */
+ GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */
+ GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */
+ GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */
+ GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */
+ GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */
+ GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */
+ GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */
+
+ /* Round 3 */
+ HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */
+ HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */
+ HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */
+ HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */
+ HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */
+ HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */
+ HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */
+ HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */
+ HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */
+ HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */
+ HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */
+ HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */
+ HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */
+ HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */
+ HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */
+ HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */
+
+ /* Round 4 */
+ II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */
+ II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */
+ II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */
+ II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */
+ II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */
+ II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */
+ II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */
+ II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */
+ II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */
+ II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */
+ II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */
+ II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */
+ II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */
+ II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */
+ II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */
+ II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */
+
+ state[0] += a;
+ state[1] += b;
+ state[2] += c;
+ state[3] += d;
+
+ /* Zeroize sensitive information. */
+ memset(x, 0, sizeof (x));
+}
+
+/* Encodes input (UINT4) into output (unsigned char). Assumes len is
+ a multiple of 4.
+ */
+static void
+Encode(unsigned char *output, const UINT4 *input, unsigned int len)
+{
+ unsigned int i, j;
+ UINT4 k;
+
+ for (i = 0, j = 0; j < len; i++, j += 4)
+ {
+ k = input[i];
+ output[j] = (unsigned char)(k & 0xff);
+ output[j+1] = (unsigned char)((k >> 8) & 0xff);
+ output[j+2] = (unsigned char)((k >> 16) & 0xff);
+ output[j+3] = (unsigned char)((k >> 24) & 0xff);
+ }
+}
+
+/* Decodes input (unsigned char) into output (UINT4). Assumes len is
+ a multiple of 4.
+ */
+static void
+Decode(UINT4 *output, const unsigned char *input, unsigned int len)
+{
+ unsigned int i, j;
+
+ for (i = 0, j = 0; j < len; i++, j += 4)
+ output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) |
+ (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24);
+}
diff --git a/RELEASE_1_1_X/src/main/rfc1413.c b/RELEASE_1_1_X/src/main/rfc1413.c
new file mode 100644
index 00000000000..556cb8aae56
--- /dev/null
+++ b/RELEASE_1_1_X/src/main/rfc1413.c
@@ -0,0 +1,233 @@
+/* ====================================================================
+ * Copyright (c) 1995,1996 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+
+/*
+ * rfc1413() speaks a common subset of the RFC 1413, AUTH, TAP and IDENT
+ * protocols. The code queries an RFC 1413 etc. compatible daemon on a remote
+ * host to look up the owner of a connection. The information should not be
+ * used for authentication purposes. This routine intercepts alarm signals.
+ *
+ * Diagnostics are reported through syslog(3).
+ *
+ * Author: Wietse Venema, Eindhoven University of Technology,
+ * The Netherlands.
+ */
+
+/* Some small additions for Shambhala --- ditch the "sccsid" var if
+ * compiling with gcc (it *has* changed), include conf.h for the
+ * prototypes it defines on at least one system (SunlOSs) which has
+ * them missing from the standard header files, and one minor change
+ * below (extra parens around assign "if (foo = bar) ..." to shut up
+ * gcc -Wall).
+ */
+
+/* Rewritten by David Robinson */
+
+#include "httpd.h" /* for server_rec, conn_rec */
+#include "http_log.h" /* for log_unixerr */
+#include "rfc1413.h"
+
+#ifndef _HPUX_SOURCE
+#define _HPUX_SOURCE
+#endif
+
+/* System libraries. */
+
+#include
+
+#ifndef SCO
+extern char *strchr();
+extern char *inet_ntoa();
+#endif
+
+/* Local stuff. */
+/* Semi-well-known port */
+#define RFC1413_PORT 113
+/* maximum allowed length of userid */
+#define RFC1413_USERLEN 512
+/* rough limit on the amount of data we accept. */
+#define RFC1413_MAXDATA 1000
+
+#define RFC1413_TIMEOUT 60
+#define ANY_PORT 0 /* Any old port will do */
+#define FROM_UNKNOWN "unknown"
+
+int rfc1413_timeout = RFC1413_TIMEOUT;/* Global so it can be changed */
+
+static jmp_buf timebuf;
+
+/* bind_connect - bind both ends of a socket */
+
+static int
+get_rfc1413(int sock, const struct sockaddr_in *our_sin,
+ const struct sockaddr_in *rmt_sin, char user[256], server_rec *srv)
+{
+ struct sockaddr_in rmt_query_sin, our_query_sin;
+ unsigned int rmt_port, our_port;
+ int i;
+ char *cp;
+ char buffer[RFC1413_MAXDATA+1];
+
+ /*
+ * Bind the local and remote ends of the query socket to the same
+ * IP addresses as the connection under investigation. We go
+ * through all this trouble because the local or remote system
+ * might have more than one network address. The RFC1413 etc.
+ * client sends only port numbers; the server takes the IP
+ * addresses from the query socket.
+ */
+
+ our_query_sin = *our_sin;
+ our_query_sin.sin_port = htons(ANY_PORT);
+ rmt_query_sin = *rmt_sin;
+ rmt_query_sin.sin_port = htons(RFC1413_PORT);
+
+ if (bind(sock, (struct sockaddr *)&our_query_sin,
+ sizeof(struct sockaddr_in)) < 0)
+ {
+ log_unixerr("bind", NULL, "rfc1413: Error binding to local port", srv);
+ return -1;
+ }
+
+/*
+ * errors from connect usually imply the remote machine doesn't support
+ * the service
+ */
+ if (connect(sock, (struct sockaddr *)&rmt_query_sin,
+ sizeof(struct sockaddr_in)) < 0)
+ return -1;
+
+/* send the data */
+ sprintf(buffer, "%u,%u\r\n", ntohs(rmt_sin->sin_port),
+ ntohs(our_sin->sin_port));
+ do i = write(sock, buffer, strlen(buffer));
+ while (i == -1 && errno == EINTR);
+ if (i == -1)
+ {
+ log_unixerr("write", NULL, "rfc1413: error sending request", srv);
+ return -1;
+ }
+
+ /*
+ * Read response from server. We assume that all the data
+ * comes in a single packet.
+ */
+
+ do i = read(sock, buffer, RFC1413_MAXDATA);
+ while (i == -1 && errno == EINTR);
+ if (i == -1)
+ {
+ log_unixerr("read", NULL, "rfc1413: error reading response", srv);
+ return -1;
+ }
+
+ buffer[i] = '\0';
+/* RFC1413_USERLEN = 512 */
+ if (sscanf(buffer, "%u , %u : USERID :%*[^:]:%512s", &rmt_port, &our_port,
+ user) != 3 || ntohs(rmt_sin->sin_port) != rmt_port
+ || ntohs(our_sin->sin_port) != our_port) return -1;
+
+ /*
+ * Strip trailing carriage return. It is part of the
+ * protocol, not part of the data.
+ */
+
+ if ((cp = strchr(user, '\r'))) *cp = '\0';
+
+ return 0;
+}
+
+/* timeout - handle timeouts */
+static void
+timeout(int sig)
+{
+ longjmp(timebuf, sig);
+}
+
+/* rfc1413 - return remote user name, given socket structures */
+char *
+rfc1413(conn_rec *conn, server_rec *srv)
+{
+ static char user[RFC1413_USERLEN+1]; /* XXX */
+ static char *result;
+ static int sock;
+
+ result = FROM_UNKNOWN;
+
+ sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (sock < 0)
+ {
+ log_unixerr("socket", NULL, "rfc1413: error creating socket", srv);
+ conn->remote_logname = result;
+ }
+
+ /*
+ * Set up a timer so we won't get stuck while waiting for the server.
+ */
+ if (setjmp(timebuf) == 0)
+ {
+ signal(SIGALRM, timeout);
+ alarm(rfc1413_timeout);
+
+ if (get_rfc1413(sock, &conn->local_addr, &conn->remote_addr, user,
+ srv)
+ >= 0)
+ result = user;
+
+ alarm(0);
+ }
+ close(sock);
+ conn->remote_logname = result;
+
+ return conn->remote_logname;
+}
diff --git a/RELEASE_1_1_X/src/main/util.c b/RELEASE_1_1_X/src/main/util.c
new file mode 100644
index 00000000000..84a782405cf
--- /dev/null
+++ b/RELEASE_1_1_X/src/main/util.c
@@ -0,0 +1,1196 @@
+
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+
+/*
+ * str.c: string utility things
+ *
+ * 3/21/93 Rob McCool
+ *
+ */
+
+
+#include "httpd.h"
+#include "http_conf_globals.h" /* for user_id & group_id */
+#ifdef QNX
+#include
+#endif
+
+#ifdef NOTDEF
+extern char** environ;
+
+/* taken from bdflush-1.5 for Linux source code */
+void inststr(char *dst[], int argc, char *src)
+{
+ if (strlen(src) <= strlen(dst[0]))
+ {
+ char *ptr;
+
+ for (ptr = dst[0]; *ptr; *(ptr++) = '\0');
+
+ strcpy(dst[0], src);
+ } else
+ {
+ /* stolen from the source to perl 4.036 (assigning to $0) */
+ char *ptr, *ptr2;
+ int count;
+ ptr = dst[0] + strlen(dst[0]);
+ for (count = 1; count < argc; count++) {
+ if (dst[count] == ptr + 1)
+ ptr += strlen(++ptr);
+ }
+ if (environ[0] == ptr + 1) {
+ for (count = 0; environ[count]; count++)
+ if (environ[count] == ptr + 1)
+ ptr += strlen(++ptr);
+ }
+ count = 0;
+ for (ptr2 = dst[0]; ptr2 <= ptr; ptr2++) {
+ *ptr2 = '\0';
+ count++;
+ }
+ strncpy(dst[0], src, count);
+ }
+}
+#endif
+
+char *get_time() {
+ time_t t;
+ char *time_string;
+
+ t=time(NULL);
+ time_string = ctime(&t);
+ time_string[strlen(time_string) - 1] = '\0';
+ return (time_string);
+}
+
+char *ht_time(pool *p, time_t t, char *fmt, int gmt) {
+ char ts[MAX_STRING_LEN];
+ struct tm *tms;
+
+ tms = (gmt ? gmtime(&t) : localtime(&t));
+
+ /* check return code? */
+ strftime(ts,MAX_STRING_LEN,fmt,tms);
+ return pstrdup (p, ts);
+}
+
+char *gm_timestr_822(pool *p, time_t sec) {
+ static const char *const days[7]=
+ {"Sun","Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+ char ts[50];
+ struct tm *tms;
+
+ tms = gmtime(&sec);
+
+/* RFC date format; as strftime '%a, %d %b %Y %T GMT' */
+ sprintf(ts, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", days[tms->tm_wday],
+ tms->tm_mday, month_snames[tms->tm_mon], tms->tm_year + 1900,
+ tms->tm_hour, tms->tm_min, tms->tm_sec);
+
+ return pstrdup (p, ts);
+}
+
+/* What a pain in the ass. */
+struct tm *get_gmtoff(long *tz) {
+ time_t tt;
+ struct tm *t;
+
+ tt = time(NULL);
+ t = localtime(&tt);
+#if defined(HAS_GMTOFF)
+ *tz = t->tm_gmtoff;
+#elif !defined(NO_TIMEZONE)
+ *tz = - timezone;
+ if(t->tm_isdst)
+ *tz += 3600;
+#else
+ {
+ static struct tm loc_t;
+
+ loc_t = *t; /* save it */
+ t = gmtime(&tt);
+ *tz = mktime(&loc_t) - mktime(t);
+ t = &loc_t; /* return pointer to saved time */
+ }
+#endif
+ return t;
+}
+
+
+/* Match = 0, NoMatch = 1, Abort = -1 */
+/* Based loosely on sections of wildmat.c by Rich Salz
+ * Hmmm... shouldn't this really go component by component?
+ */
+int strcmp_match(char *str, char *exp) {
+ int x,y;
+
+ for(x=0,y=0;exp[y];++y,++x) {
+ if((!str[x]) && (exp[y] != '*'))
+ return -1;
+ if(exp[y] == '*') {
+ while(exp[++y] == '*');
+ if(!exp[y])
+ return 0;
+ while(str[x]) {
+ int ret;
+ if((ret = strcmp_match(&str[x++],&exp[y])) != 1)
+ return ret;
+ }
+ return -1;
+ } else
+ if((exp[y] != '?') && (str[x] != exp[y]))
+ return 1;
+ }
+ return (str[x] != '\0');
+}
+
+int strcasecmp_match(char *str, char *exp) {
+ int x,y;
+
+ for(x=0,y=0;exp[y];++y,++x) {
+ if((!str[x]) && (exp[y] != '*'))
+ return -1;
+ if(exp[y] == '*') {
+ while(exp[++y] == '*');
+ if(!exp[y])
+ return 0;
+ while(str[x]) {
+ int ret;
+ if((ret = strcasecmp_match(&str[x++],&exp[y])) != 1)
+ return ret;
+ }
+ return -1;
+ } else
+ if((exp[y] != '?') && (tolower(str[x]) != tolower(exp[y])))
+ return 1;
+ }
+ return (str[x] != '\0');
+}
+
+int is_matchexp(char *str) {
+ register int x;
+
+ for(x=0;str[x];x++)
+ if((str[x] == '*') || (str[x] == '?'))
+ return 1;
+ return 0;
+}
+
+/*
+ * Parse .. so we don't compromise security
+ */
+void getparents(char *name)
+{
+ int l, w;
+
+ /* Four paseses, as per RFC 1808 */
+ /* a) remove ./ path segments */
+
+ for (l=0, w=0; name[l] != '\0';)
+ {
+ if (name[l] == '.' && name[l+1] == '/' && (l == 0 || name[l-1] == '/'))
+ l += 2;
+ else
+ name[w++] = name[l++];
+ }
+
+ /* b) remove trailing . path, segment */
+ if (w == 1 && name[0] == '.') w--;
+ else if (w > 1 && name[w-1] == '.' && name[w-2] == '/') w--;
+ name[w] = '\0';
+
+ /* c) remove all xx/../ segments. (including leading ../ and /../) */
+ l = 0;
+
+ while(name[l]!='\0') {
+ if(name[l] == '.' && name[l+1] == '.' && name[l+2] == '/' &&
+ (l == 0 || name[l-1] == '/')) {
+ register int m=l+3,n;
+
+ l=l-2;
+ if(l>=0) {
+ while(l >= 0 && name[l] != '/') l--;
+ l++;
+ }
+ else l=0;
+ n=l;
+ while((name[n]=name[m])) (++n,++m);
+ }
+ else ++l;
+ }
+
+ /* d) remove trailing xx/.. segment. */
+ if (l == 2 && name[0] == '.' && name[1] == '.') name[0] = '\0';
+ else if (l > 2 && name[l-1] == '.' && name[l-2] == '.' && name[l-3] == '/')
+ {
+ l = l - 4;
+ if (l >= 0)
+ {
+ while (l >= 0 && name[l] != '/') l--;
+ l++;
+ }
+ else l = 0;
+ name[l] = '\0';
+ }
+}
+
+void no2slash(char *name) {
+ register int x,y;
+
+ for(x=0; name[x];)
+ if(x && (name[x-1] == '/') && (name[x] == '/'))
+ for(y=x+1;name[y-1];y++)
+ name[y-1] = name[y];
+ else x++;
+}
+
+char *make_dirstr(pool *p, char *s, int n) {
+ register int x,f;
+ char *res;
+
+ for(x=0,f=0;s[x];x++) {
+ if(s[x] == '/')
+ if((++f) == n) {
+ res = palloc(p, x + 2);
+ strncpy (res, s, x);
+ res[x] = '/';
+ res[x+1] = '\0';
+ return res;
+ }
+ }
+
+ if (s[strlen(s) - 1] == '/')
+ return pstrdup (p, s);
+ else
+ return pstrcat (p, s, "/", NULL);
+}
+
+int count_dirs(char *path) {
+ register int x,n;
+
+ for(x=0,n=0;path[x];x++)
+ if(path[x] == '/') n++;
+ return n;
+}
+
+
+void chdir_file(char *file) {
+ int i;
+
+ if((i = rind(file,'/')) == -1)
+ return;
+ file[i] = '\0';
+ chdir(file);
+ file[i] = '/';
+}
+
+char *getword(pool* atrans, char **line, char stop) {
+ int pos = ind(*line, stop);
+ char *res;
+
+ if (pos == -1) {
+ res = pstrdup (atrans, *line);
+ *line += strlen (*line);
+ return res;
+ }
+
+ res = palloc(atrans, pos + 1);
+ strncpy (res, *line, pos);
+ res[pos] = '\0';
+
+ while ((*line)[pos] == stop) ++pos;
+
+ *line += pos;
+
+ return res;
+}
+char *getword_nulls(pool* atrans, char **line, char stop) {
+ int pos = ind(*line, stop);
+ char *res;
+
+ if (pos == -1) {
+ res = pstrdup (atrans, *line);
+ *line += strlen (*line);
+ return res;
+ }
+
+ res = palloc(atrans, pos + 1);
+ strncpy (res, *line, pos);
+ res[pos] = '\0';
+
+ ++pos;
+
+ *line += pos;
+
+ return res;
+}
+
+/* Get a word, (new) config-file style --- quoted strings and backslashes
+ * all honored
+ */
+
+char *substring_conf (pool *p, char *start, int len)
+{
+ char *result = palloc (p, len + 2);
+ char *resp = result;
+ int i;
+
+ for (i = 0; i < len; ++i) {
+ if (start[i] == '\\')
+ *resp++ = start[++i];
+ else
+ *resp++ = start[i];
+ }
+
+ *resp++ = '\0';
+ return result;
+}
+
+char *getword_conf(pool* p, char **line) {
+ char *str = *line, *strend, *res;
+ char quote;
+
+ while (*str && isspace (*str))
+ ++str;
+
+ if (!*str) {
+ *line = str;
+ return "";
+ }
+
+ if ((quote = *str) == '"' || quote == '\'') {
+ strend = str + 1;
+ while (*strend && *strend != quote) {
+ if (*strend == '\\' && strend[1]) strend += 2;
+ else ++strend;
+ }
+ res = substring_conf (p, str + 1, strend - str - 1);
+
+ if (*strend == quote) ++strend;
+ } else {
+ strend = str;
+ while (*strend && !isspace (*strend))
+ if (*strend == '\\' && strend[1]) strend += 2;
+ else ++strend;
+
+ res = substring_conf (p, str, strend - str);
+ }
+
+ while (*strend && isspace(*strend)) ++ strend;
+ *line = strend;
+ return res;
+}
+
+void cfg_getword(char *word, char *line) {
+ int x=0,y;
+
+ for(x=0;line[x] && isspace(line[x]);x++);
+ y=0;
+ while(1) {
+ if(!(word[y] = line[x]))
+ break;
+ if(isspace(line[x]))
+ if((!x) || (line[x-1] != '\\'))
+ break;
+ if(line[x] != '\\') ++y;
+ ++x;
+ }
+ word[y] = '\0';
+ while(line[x] && isspace(line[x])) ++x;
+ for(y=0;(line[y] = line[x]);++x,++y);
+}
+
+int
+cfg_getline(char *s, int n, FILE *f) {
+ register int i=0, c;
+
+ s[0] = '\0';
+ /* skip leading whitespace */
+ do {
+ c = getc(f);
+ } while (c == '\t' || c == ' ');
+
+ while(1) {
+ if((c == '\t') || (c == ' ')) {
+ s[i++] = ' ';
+ while((c == '\t') || (c == ' '))
+ c = getc(f);
+ }
+ if(c == CR) {
+ c = getc(f);
+ }
+ if(c == EOF || c == 0x4 || c == LF || i == (n-1)) {
+ /* blast trailing whitespace */
+ while(i && (s[i-1] == ' ')) --i;
+ s[i] = '\0';
+ return (feof(f) ? 1 : 0);
+ }
+ s[i] = c;
+ ++i;
+ c = getc(f);
+ }
+}
+
+/* Retrieve a token, spacing over it and returning a pointer to
+ * the first non-white byte afterwards. Note that these tokens
+ * are delimited by semis and commas; and can also be delimited
+ * by whitespace at the caller's option.
+ */
+
+char *get_token (pool *p, char **accept_line, int accept_white)
+{
+ char *ptr = *accept_line;
+ char *tok_start;
+ char *token;
+ int tok_len;
+
+ /* Find first non-white byte */
+
+ while (*ptr && isspace(*ptr))
+ ++ptr;
+
+ tok_start = ptr;
+
+ /* find token end, skipping over quoted strings.
+ * (comments are already gone).
+ */
+
+ while (*ptr && (accept_white || !isspace(*ptr))
+ && *ptr != ';' && *ptr != ',')
+ {
+ if (*ptr++ == '"')
+ while (*ptr)
+ if (*ptr++ == '"') break;
+ }
+
+ tok_len = ptr - tok_start;
+ token = palloc (p, tok_len + 1);
+ strncpy (token, tok_start, tok_len);
+ token[tok_len] = '\0';
+
+ /* Advance accept_line pointer to the next non-white byte */
+
+ while (*ptr && isspace(*ptr))
+ ++ptr;
+
+ *accept_line = ptr;
+ return token;
+}
+
+char *escape_shell_cmd(pool *p, char *s) {
+ register int x,y,l;
+ char *cmd;
+
+ l=strlen(s);
+ cmd = palloc (p, 2 * l + 1); /* Be safe */
+ strcpy (cmd, s);
+
+ for(x=0;cmd[x];x++) {
+
+#ifdef __EMX__
+ /* Don't allow '&' in parameters under OS/2. */
+ /* This can be used to send commands to the shell. */
+ if (cmd[x] == '&') {
+ cmd[x] = ' ';
+ }
+#endif
+
+ if(ind("&;`'\"|*?~<>^()[]{}$\\\n",cmd[x]) != -1){
+ for(y=l+1;y>x;y--)
+ cmd[y] = cmd[y-1];
+ l++; /* length has been increased */
+ cmd[x] = '\\';
+ x++; /* skip the character */
+ }
+ }
+
+ return cmd;
+}
+
+void plustospace(char *str) {
+ register int x;
+
+ for(x=0;str[x];x++) if(str[x] == '+') str[x] = ' ';
+}
+
+void spacetoplus(char *str) {
+ register int x;
+
+ for(x=0;str[x];x++) if(str[x] == ' ') str[x] = '+';
+}
+
+char x2c(char *what) {
+ register char digit;
+
+ digit = ((what[0] >= 'A') ? ((what[0] & 0xdf) - 'A')+10 : (what[0] - '0'));
+ digit *= 16;
+ digit += (what[1] >= 'A' ? ((what[1] & 0xdf) - 'A')+10 : (what[1] - '0'));
+ return(digit);
+}
+
+/*
+ * Unescapes a URL.
+ * Returns 0 on success, non-zero on error
+ * Failure is due to
+ * bad % escape returns BAD_REQUEST
+ *
+ * decoding %00 -> \0
+ * decoding %2f -> / (a special character)
+ * returns NOT_FOUND
+ */
+int
+unescape_url(char *url) {
+ register int x,y, badesc, badpath;
+
+ badesc = 0;
+ badpath = 0;
+ for(x=0,y=0;url[y];++x,++y) {
+ if (url[y] != '%') url[x] = url[y];
+ else
+ {
+ if (!isxdigit(url[y+1]) || !isxdigit(url[y+2]))
+ {
+ badesc = 1;
+ url[x] = '%';
+ } else
+ {
+ url[x] = x2c(&url[y+1]);
+ y += 2;
+ if (url[x] == '/' || url[x] == '\0') badpath = 1;
+ }
+ }
+ }
+ url[x] = '\0';
+ if (badesc) return BAD_REQUEST;
+ else if (badpath) return NOT_FOUND;
+ else return OK;
+}
+
+char *construct_url(pool *p, char *uri, server_rec *s) {
+ char portnum[10]; /* Long enough. Really! */
+
+ if (s->port == 80) {
+ return pstrcat (p, "http://", s->server_hostname, uri, NULL);
+ } else {
+ sprintf (portnum, "%d", s->port);
+ return pstrcat (p, "http://", s->server_hostname, ":", portnum, uri,
+ NULL);
+ }
+}
+
+#define c2x(what,where) sprintf(where,"%%%02x",what)
+
+/*
+escape_path_segment() escapes a path segment, as defined in RFC 1808. This
+routine is (should be) OS independent.
+
+os_escape_path() converts an OS path to a URL, in an OS dependent way. In all
+cases if a ':' occurs before the first '/' in the URL, the URL should be
+prefixed with "./" (or the ':' escaped). In the case of Unix, this means
+leaving '/' alone, but otherwise doing what escape_path_segment() does. For
+efficiency reasons, we don't use escape_path_segment(), which is provided for
+reference. Again, RFC 1808 is where this stuff is defined.
+
+If partial is set, os_escape_path() assumes that the path will be appended to
+something with a '/' in it (and thus does not prefix "./").
+*/
+
+char *escape_path_segment(pool *p, const char *segment) {
+ register int x,y;
+ char *copy = palloc (p, 3 * strlen (segment) + 1);
+
+ for(x=0,y=0; segment[x]; x++,y++) {
+ char c=segment[x];
+ if((c < 'A' || c > 'Z') && (c < 'a' || c > 'z') && (c < '0' || c >'9')
+ && ind("$-_.+!*'(),:@&=~",c) == -1)
+ {
+ c2x(c,©[y]);
+ y+=2;
+ }
+ else
+ copy[y]=c;
+ }
+ copy[y] = '\0';
+ return copy;
+}
+
+char *os_escape_path(pool *p,const char *path,int partial) {
+ char *copy=palloc(p,3*strlen(path)+3);
+ char *s=copy;
+
+ if(!partial)
+ {
+ int colon=ind(path,':');
+ int slash=ind(path,'/');
+
+ if(colon >= 0 && (colon < slash || slash < 0))
+ {
+ *s++='.';
+ *s++='/';
+ }
+ }
+ for( ; *path ; ++path)
+ {
+ char c=*path;
+ if((c < 'A' || c > 'Z') && (c < 'a' || c > 'z') && (c < '0' || c >'9')
+ && ind("$-_.+!*'(),:@&=/~",c) == -1)
+ {
+ c2x(c,s);
+ s+=3;
+ }
+ else
+ *s++=c;
+ }
+ *s='\0';
+ return copy;
+}
+
+char *escape_uri(pool *p, char *uri) {
+ register int x,y;
+ char *copy = palloc (p, 3 * strlen (uri) + 1);
+
+ for(x=0,y=0; uri[x]; x++,y++) {
+ if (ind (":% ?+&",(copy[y] = uri[x])) != -1) {
+ c2x(uri[x],©[y]);
+ y+=2;
+ }
+ }
+ copy[y] = '\0';
+ return copy;
+}
+
+char *
+escape_html(pool *p, const char *s)
+{
+ int i, j;
+ char *x;
+
+/* first, count the number of extra characters */
+ for (i=0, j=0; s[i] != '\0'; i++)
+ if (s[i] == '<' || s[i] == '>') j += 3;
+ else if (s[i] == '&') j += 4;
+
+ if (j == 0) return pstrdup(p, s);
+ x = palloc(p, i + j + 1);
+ for (i=0, j=0; s[i] != '\0'; i++, j++)
+ if (s[i] == '<')
+ {
+ memcpy(&x[j], "<", 4);
+ j += 3;
+ } else if (s[i] == '>')
+ {
+ memcpy(&x[j], ">", 4);
+ j += 3;
+ } else if (s[i] == '&')
+ {
+ memcpy(&x[j], "&", 5);
+ j += 4;
+ } else
+ x[j] = s[i];
+
+ x[j] = '\0';
+ return x;
+}
+
+#ifdef NOTDEF
+
+void escape_url(char *url) {
+ register int x,y;
+ register char digit;
+ char *copy;
+
+ copy = strdup(url);
+
+ for(x=0,y=0;copy[x];x++,y++) {
+ if(ind("% ?+&",url[y] = copy[x]) != -1) {
+ c2x(copy[x],&url[y]);
+ y+=2;
+ }
+ }
+ url[y] = '\0';
+ free(copy);
+}
+
+#endif
+
+int is_directory(char *path) {
+ struct stat finfo;
+
+ if(stat(path,&finfo) == -1)
+ return 0; /* in error condition, just return no */
+
+ return(S_ISDIR(finfo.st_mode));
+}
+
+char *make_full_path(pool *a, char *src1,char *src2) {
+ register int x;
+
+ x = strlen(src1);
+ if (x == 0) return pstrcat (a, "/", src2, NULL);
+
+ if (src1[x - 1] != '/') return pstrcat (a, src1, "/", src2, NULL);
+ else return pstrcat (a, src1, src2, NULL);
+}
+
+int is_url(char *u) {
+ register int x;
+
+ for(x=0;u[x] != ':';x++)
+ if((!u[x]) || (!isalpha(u[x])))
+ return 0;
+
+ if((u[x+1] == '/') && (u[x+2] == '/'))
+ return 1;
+ else return 0;
+}
+
+int can_exec(struct stat *finfo) {
+#ifdef __EMX__
+ /* OS/2 dosen't have Users and Groups */
+ return (finfo->st_mode & S_IEXEC);
+#else
+ if(user_id == finfo->st_uid)
+ if(finfo->st_mode & S_IXUSR)
+ return 1;
+ if(group_id == finfo->st_gid)
+ if(finfo->st_mode & S_IXGRP)
+ return 1;
+ return (finfo->st_mode & S_IXOTH);
+#endif
+}
+
+#ifdef NEED_STRDUP
+char *strdup (char *str)
+{
+ char *dup;
+
+ if(!(dup = (char *)malloc (strlen (str) + 1)))
+ return NULL;
+ dup = strcpy (dup, str);
+
+ return dup;
+}
+#endif
+
+/* The following two routines were donated for SVR4 by Andreas Vogel */
+#ifdef NEED_STRCASECMP
+int strcasecmp (const char *a, const char *b)
+{
+ const char *p = a;
+ const char *q = b;
+ for (p = a, q = b; *p && *q; p++, q++)
+ {
+ int diff = tolower(*p) - tolower(*q);
+ if (diff) return diff;
+ }
+ if (*p) return 1; /* p was longer than q */
+ if (*q) return -1; /* p was shorter than q */
+ return 0; /* Exact match */
+}
+
+#endif
+
+#ifdef NEED_STRNCASECMP
+int strncasecmp (const char *a, const char *b, int n)
+{
+ const char *p = a;
+ const char *q = b;
+
+ for (p = a, q = b; /*NOTHING*/; p++, q++)
+ {
+ int diff;
+ if (p == a + n) return 0; /* Match up to n characters */
+ if (!(*p && *q)) return *p - *q;
+ diff = tolower(*p) - tolower(*q);
+ if (diff) return diff;
+ }
+ /*NOTREACHED*/
+}
+#endif
+
+
+
+#ifdef NEED_INITGROUPS
+int initgroups(const char *name, gid_t basegid)
+{
+#ifdef QNX
+/* QNX does not appear to support supplementary groups.
+Ben */
+ return 0;
+#else /* ndef QNX */
+ gid_t groups[NGROUPS_MAX];
+ struct group *g;
+ int index = 0;
+
+ setgrent();
+
+ groups[index++] = basegid;
+
+ while (index < NGROUPS_MAX && ((g = getgrent()) != NULL))
+ if (g->gr_gid != basegid)
+ {
+ char **names;
+
+ for (names = g->gr_mem; *names != NULL; ++names)
+ if (!strcmp(*names, name))
+ groups[index++] = g->gr_gid;
+ }
+
+ endgrent();
+
+ return setgroups(index, groups);
+#endif /* def QNX */
+}
+#endif /* def NEED_INITGROUPS */
+
+#ifdef NEED_WAITPID
+/* From ikluft@amdahl.com */
+/* this is not ideal but it works for SVR3 variants */
+/* httpd does not use the options so this doesn't implement them */
+int waitpid(pid_t pid, int *statusp, int options)
+{
+ int tmp_pid;
+ if ( kill ( pid,0 ) == -1) {
+ errno=ECHILD;
+ return -1;
+ }
+ while ((( tmp_pid = wait(statusp)) != pid) && ( tmp_pid != -1 ));
+ return tmp_pid;
+}
+#endif
+
+int ind(const char *s, char c) {
+ register int x;
+
+ for(x=0;s[x];x++)
+ if(s[x] == c) return x;
+
+ return -1;
+}
+
+int rind(const char *s, char c) {
+ register int x;
+
+ for(x=strlen(s)-1;x != -1;x--)
+ if(s[x] == c) return x;
+
+ return -1;
+}
+
+void str_tolower(char *str) {
+ while(*str) {
+ *str = tolower(*str);
+ ++str;
+ }
+}
+
+uid_t uname2id(char *name) {
+ struct passwd *ent;
+
+ if(name[0] == '#')
+ return(atoi(&name[1]));
+
+ if(!(ent = getpwnam(name))) {
+ fprintf(stderr,"httpd: bad user name %s\n",name);
+ exit(1);
+ }
+ else return(ent->pw_uid);
+}
+
+gid_t gname2id(char *name) {
+ struct group *ent;
+
+ if(name[0] == '#')
+ return(atoi(&name[1]));
+
+ if(!(ent = getgrnam(name))) {
+ fprintf(stderr,"httpd: bad group name %s\n",name);
+ exit(1);
+ }
+ else return(ent->gr_gid);
+}
+
+#if 0
+int get_portnum(int sd) {
+ struct sockaddr addr;
+ int len;
+
+ len = sizeof(struct sockaddr);
+ if(getsockname(sd,&addr,&len) < 0)
+ return -1;
+ return ntohs(((struct sockaddr_in *)&addr)->sin_port);
+}
+
+struct in_addr get_local_addr(int sd) {
+ struct sockaddr addr;
+ int len;
+
+ len = sizeof(struct sockaddr);
+ if(getsockname(sd,&addr,&len) < 0) {
+ fprintf (stderr, "Can't get local host address!\n");
+ perror ("getsockname");
+ exit(1);
+ }
+
+ return ((struct sockaddr_in *)&addr)->sin_addr;
+}
+#endif
+
+/*
+ * Parses a host of the form [:port]
+ * :port is permitted if 'port' is not NULL
+ */
+unsigned long get_virthost_addr (char *w, short int *ports) {
+ struct hostent *hep;
+ unsigned long my_addr;
+ char *p;
+
+ p = strchr(w, ':');
+ if (ports != NULL)
+ {
+ *ports = 0;
+ if (p != NULL && strcmp(p+1, "*") != 0) *ports = atoi(p+1);
+ }
+
+ if (p != NULL) *p = '\0';
+ if (strcmp(w, "*") == 0)
+ {
+ if (p != NULL) *p = ':';
+ return htonl(INADDR_ANY);
+ }
+
+#ifdef DGUX
+ my_addr = inet_network(w);
+#else
+ my_addr = inet_addr(w);
+#endif
+ if (my_addr != ((unsigned long) 0xffffffff))
+ {
+ if (p != NULL) *p = ':';
+ return my_addr;
+ }
+
+ hep = gethostbyname(w);
+
+ if ((!hep) || (hep->h_addrtype != AF_INET || !hep->h_addr_list[0])) {
+ fprintf (stderr, "Cannot resolve host name %s --- exiting!\n", w);
+ exit(1);
+ }
+
+ if (hep->h_addr_list[1]) {
+ fprintf(stderr, "Host %s has multiple addresses ---\n", w);
+ fprintf(stderr, "you must choose one explicitly for use as\n");
+ fprintf(stderr, "a virtual host. Exiting!!!\n");
+ exit(1);
+ }
+
+ if (p != NULL) *p = ':';
+
+ return ((struct in_addr *)(hep->h_addr))->s_addr;
+}
+
+
+#ifdef NOTDEF
+
+char *get_remote_logname(FILE *fd) {
+ int len;
+ char *result;
+#if defined(NEXT) || defined(BSD4_4) || defined(SOLARIS2) || defined(LINUX) || defined(__EMX__)
+ struct sockaddr sa_server, sa_client;
+#else
+ struct sockaddr_in sa_server,sa_client;
+#endif
+
+ len = sizeof(sa_client);
+ if(getpeername(fileno(stdout),&sa_client,&len) != -1) {
+ len = sizeof(sa_server);
+ if(getsockname(fileno(stdout),&sa_server,&len) == -1)
+ result = "unknown";
+ else
+ result = rfc931((struct sockaddr_in *) & sa_client,
+ (struct sockaddr_in *) & sa_server);
+ }
+ else result = "unknown";
+
+ return result; /* robm=pinhead */
+}
+#endif
+
+static char *find_fqdn(pool *a, struct hostent *p) {
+ int x;
+
+ if(ind(p->h_name,'.') == -1) {
+ for(x=0;p->h_aliases[x];++x) {
+ if((ind(p->h_aliases[x],'.') != -1) &&
+ (!strncmp(p->h_aliases[x],p->h_name,strlen(p->h_name))))
+ return pstrdup(a, p->h_aliases[x]);
+ }
+ return NULL;
+ } else return pstrdup(a, (void *)p->h_name);
+}
+
+char *get_local_host(pool *a)
+{
+ char str[128];
+ int len = 128;
+ char *server_hostname;
+
+ struct hostent *p;
+ gethostname(str, len);
+ if((!(p=gethostbyname(str))) || (!(server_hostname = find_fqdn(a, p)))) {
+ fprintf(stderr,"httpd: cannot determine local host name.\n");
+ fprintf(stderr,"Use ServerName to set it manually.\n");
+ exit(1);
+ }
+
+ return server_hostname;
+}
+
+/* aaaack but it's fast and const should make it shared text page. */
+const int pr2six[256]={
+ 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
+ 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,62,64,64,64,63,
+ 52,53,54,55,56,57,58,59,60,61,64,64,64,64,64,64,64,0,1,2,3,4,5,6,7,8,9,
+ 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,64,64,64,64,64,64,26,27,
+ 28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,
+ 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
+ 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
+ 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
+ 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
+ 64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,64,
+ 64,64,64,64,64,64,64,64,64,64,64,64,64
+};
+
+char *uudecode(pool *p, char *bufcoded) {
+ int nbytesdecoded;
+ register unsigned char *bufin;
+ register char *bufplain;
+ register unsigned char *bufout;
+ register int nprbytes;
+
+ /* Strip leading whitespace. */
+
+ while(*bufcoded==' ' || *bufcoded == '\t') bufcoded++;
+
+ /* Figure out how many characters are in the input buffer.
+ * Allocate this many from the per-transaction pool for the result.
+ */
+ bufin = (unsigned char *)bufcoded;
+ while(pr2six[*(bufin++)] <= 63);
+ nprbytes = (char *)bufin - bufcoded - 1;
+ nbytesdecoded = ((nprbytes+3)/4) * 3;
+
+ bufplain = palloc(p, nbytesdecoded + 1);
+ bufout = (unsigned char *)bufplain;
+
+ bufin = (unsigned char *)bufcoded;
+
+ while (nprbytes > 0) {
+ *(bufout++) =
+ (unsigned char) (pr2six[*bufin] << 2 | pr2six[bufin[1]] >> 4);
+ *(bufout++) =
+ (unsigned char) (pr2six[bufin[1]] << 4 | pr2six[bufin[2]] >> 2);
+ *(bufout++) =
+ (unsigned char) (pr2six[bufin[2]] << 6 | pr2six[bufin[3]]);
+ bufin += 4;
+ nprbytes -= 4;
+ }
+
+ if(nprbytes & 03) {
+ if(pr2six[bufin[-2]] > 63)
+ nbytesdecoded -= 2;
+ else
+ nbytesdecoded -= 1;
+ }
+ bufplain[nbytesdecoded] = '\0';
+ return bufplain;
+}
+
+#ifdef __EMX__
+void os2pathname(char *path) {
+ char newpath[MAX_STRING_LEN];
+ int loop;
+ int offset;
+
+ offset = 0;
+ for (loop=0; loop < (strlen(path) + 1); loop++) {
+ if (path[loop] == '/') {
+ newpath[offset] = '\\';
+ /*
+ offset = offset + 1;
+ newpath[offset] = '\\';
+ */
+ } else
+ newpath[offset] = path[loop];
+ offset = offset + 1;
+ };
+ /* Debugging code */
+ /* fprintf(stderr, "%s \n", newpath); */
+
+ strcpy(path, newpath);
+};
+#endif
+
+
+#ifdef NEED_STRERROR
+char *
+strerror (int err) {
+
+ char *p;
+ extern char *const sys_errlist[];
+
+ p = sys_errlist[err];
+ return (p);
+}
+#endif
diff --git a/RELEASE_1_1_X/src/main/util_md5.c b/RELEASE_1_1_X/src/main/util_md5.c
new file mode 100644
index 00000000000..4d8fd1cf26f
--- /dev/null
+++ b/RELEASE_1_1_X/src/main/util_md5.c
@@ -0,0 +1,193 @@
+
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+/************************************************************************
+ * NCSA HTTPd Server
+ * Software Development Group
+ * National Center for Supercomputing Applications
+ * University of Illinois at Urbana-Champaign
+ * 605 E. Springfield, Champaign, IL 61820
+ * httpd@ncsa.uiuc.edu
+ *
+ * Copyright (C) 1995, Board of Trustees of the University of Illinois
+ *
+ ************************************************************************
+ *
+ * md5.c: NCSA HTTPd code which uses the md5c.c RSA Code
+ *
+ * Original Code Copyright (C) 1994, Jeff Hostetler, Spyglass, Inc.
+ * Portions of Content-MD5 code Copyright (C) 1993, 1994 by Carnegie Mellon
+ * University (see Copyright below).
+ * Portions of Content-MD5 code Copyright (C) 1991 Bell Communications
+ * Research, Inc. (Bellcore) (see Copyright below).
+ * Portions extracted from mpack, John G. Myers - jgm+@cmu.edu
+ * Content-MD5 Code contributed by Martin Hamilton (martin@net.lut.ac.uk)
+ *
+ */
+
+
+
+/* md5.c --Module Interface to MD5. */
+/* Jeff Hostetler, Spyglass, Inc., 1994. */
+
+#include "httpd.h"
+#include "util_md5.h"
+
+char *md5 (pool *p, unsigned char *string)
+{
+ MD5_CTX my_md5;
+ unsigned char hash[16];
+ char *r, result[33];
+ int i;
+
+ /*
+ * Take the MD5 hash of the string argument.
+ */
+
+ MD5Init(&my_md5);
+ MD5Update(&my_md5, string, strlen((const char *)string));
+ MD5Final(hash, &my_md5);
+
+ for (i=0, r=result; i<16; i++, r+=2)
+ sprintf(r, "%02x", hash[i]);
+ *r = '\0';
+
+ return pstrdup(p, result);
+}
+
+/* these portions extracted from mpack, John G. Myers - jgm+@cmu.edu */
+
+/* (C) Copyright 1993,1994 by Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software
+ * and its documentation for any purpose is hereby granted without
+ * fee, provided that the above copyright notice appear in all copies
+ * and that both that copyright notice and this permission notice
+ * appear in supporting documentation, and that the name of Carnegie
+ * Mellon University not be used in advertising or publicity
+ * pertaining to distribution of the software without specific,
+ * written prior permission. Carnegie Mellon University makes no
+ * representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
+ * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
+ * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
+ * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
+ * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+/*
+ * Copyright (c) 1991 Bell Communications Research, Inc. (Bellcore)
+ *
+ * Permission to use, copy, modify, and distribute this material
+ * for any purpose and without fee is hereby granted, provided
+ * that the above copyright notice and this permission notice
+ * appear in all copies, and that the name of Bellcore not be
+ * used in advertising or publicity pertaining to this
+ * material without the specific, prior written permission
+ * of an authorized representative of Bellcore. BELLCORE
+ * MAKES NO REPRESENTATIONS ABOUT THE ACCURACY OR SUITABILITY
+ * OF THIS MATERIAL FOR ANY PURPOSE. IT IS PROVIDED "AS IS",
+ * WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES.
+ */
+
+static char basis_64[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+char *md5contextTo64(pool *a, MD5_CTX *context)
+{
+ unsigned char digest[18];
+ char *encodedDigest;
+ int i;
+ char *p;
+
+ encodedDigest = (char *)pcalloc(a, 25 * sizeof(char));
+
+ MD5Final(digest, context);
+ digest[sizeof(digest)-1] = digest[sizeof(digest)-2] = 0;
+
+ p = encodedDigest;
+ for (i=0; i < sizeof(digest); i+=3) {
+ *p++ = basis_64[digest[i]>>2];
+ *p++ = basis_64[((digest[i] & 0x3)<<4) | ((int)(digest[i+1] & 0xF0)>>4)];
+ *p++ = basis_64[((digest[i+1] & 0xF)<<2) | ((int)(digest[i+2] & 0xC0)>>6)];
+ *p++ = basis_64[digest[i+2] & 0x3F];
+ }
+ *p-- = '\0';
+ *p-- = '=';
+ *p-- = '=';
+ return encodedDigest;
+}
+
+char *md5digest(pool *p, FILE *infile)
+{
+ MD5_CTX context;
+ unsigned char buf[1000];
+ long length = 0;
+ int nbytes;
+
+ MD5Init(&context);
+ while ((nbytes = fread(buf, 1, sizeof(buf), infile))) {
+ length += nbytes;
+ MD5Update(&context, buf, nbytes);
+ }
+ rewind(infile);
+ return md5contextTo64(p, &context);
+}
+
diff --git a/RELEASE_1_1_X/src/main/util_script.c b/RELEASE_1_1_X/src/main/util_script.c
new file mode 100644
index 00000000000..05b33105562
--- /dev/null
+++ b/RELEASE_1_1_X/src/main/util_script.c
@@ -0,0 +1,364 @@
+
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+
+
+#include "httpd.h"
+#include "http_main.h"
+#include "http_log.h"
+#include "http_protocol.h"
+#include "http_core.h" /* For document_root. Sigh... */
+#include "http_request.h" /* for sub_req_lookup_uri() */
+#include "util_script.h"
+
+/*
+ * Various utility functions which are common to a whole lot of
+ * script-type extensions mechanisms, and might as well be gathered
+ * in one place (if only to avoid creating inter-module dependancies
+ * where there don't have to be).
+ */
+
+#define MALFORMED_MESSAGE "malformed header from script. Bad header="
+#define MALFORMED_HEADER_LENGTH_TO_SHOW 30
+
+char **create_argv(pool *p, char *av0, char *args) {
+ register int x,n;
+ char **av;
+ char *w;
+
+ for(x=0,n=2;args[x];x++)
+ if(args[x] == '+') ++n;
+
+ av = (char **)palloc(p, (n+1)*sizeof(char *));
+ av[0] = av0;
+
+ for(x=1;xelts;
+ char **env = (char **)palloc (p, (env_arr->nelts + 2) *sizeof (char *));
+ int i, j;
+ char *tz;
+
+ j = 0;
+ tz = getenv("TZ");
+ if (tz!= NULL) env[j++] = pstrcat(p, "TZ=", tz, NULL);
+ for (i = 0; i < env_arr->nelts; ++i) {
+ if (!elts[i].key) continue;
+ env[j++] = pstrcat (p, elts[i].key, "=", elts[i].val, NULL);
+ }
+
+ env[j] = NULL;
+ return env;
+}
+
+void add_common_vars(request_rec *r)
+{
+ table *e = r->subprocess_env;
+ server_rec *s = r->server;
+ conn_rec *c = r->connection;
+ const char *rem_logname;
+
+ char port[40],*env_path;
+
+ array_header *hdrs_arr = table_elts (r->headers_in);
+ table_entry *hdrs = (table_entry *)hdrs_arr->elts;
+ int i;
+
+ /* First, add environment vars from headers... this is as per
+ * CGI specs, though other sorts of scripting interfaces see
+ * the same vars...
+ */
+
+ for (i = 0; i < hdrs_arr->nelts; ++i) {
+ if (!hdrs[i].key) continue;
+
+ /* A few headers are special cased --- Authorization to prevent
+ * rogue scripts from capturing passwords; content-type and -length
+ * for no particular reason.
+ */
+
+ if (!strcasecmp (hdrs[i].key, "Content-type"))
+ table_set (e, "CONTENT_TYPE", hdrs[i].val);
+ else if (!strcasecmp (hdrs[i].key, "Content-length"))
+ table_set (e, "CONTENT_LENGTH", hdrs[i].val);
+ else if (!strcasecmp (hdrs[i].key, "Authorization"))
+ continue;
+ else
+ table_set (e, http2env (r->pool, hdrs[i].key), hdrs[i].val);
+ }
+
+ sprintf(port, "%d", s->port);
+
+ if(!(env_path = getenv("PATH")))
+ env_path=DEFAULT_PATH;
+
+ table_set (e, "PATH", env_path);
+ table_set (e, "SERVER_SOFTWARE", SERVER_VERSION);
+ table_set (e, "SERVER_NAME", s->server_hostname);
+ table_set (e, "SERVER_PORT", port);
+ table_set (e, "REMOTE_HOST",
+ get_remote_host(c, r->per_dir_config, REMOTE_NAME));
+ table_set (e, "REMOTE_ADDR", c->remote_ip);
+ table_set (e, "DOCUMENT_ROOT", document_root(r)); /* Apache */
+ table_set (e, "SERVER_ADMIN", s->server_admin); /* Apache */
+ table_set (e, "SCRIPT_FILENAME", r->filename); /* Shambhala */
+
+ if (c->user) table_set(e, "REMOTE_USER", c->user);
+ if (c->auth_type) table_set(e, "AUTH_TYPE", c->auth_type);
+ rem_logname = get_remote_logname(r);
+ if (rem_logname) table_set(e, "REMOTE_IDENT", rem_logname);
+
+ /* Apache custom error responses. If we have redirected set two new vars */
+
+ if (r->prev) {
+ if (r->prev->args) table_set(e,"REDIRECT_QUERY_STRING", r->prev->args);
+ if (r->prev->uri) table_set (e, "REDIRECT_URL", r->prev->uri);
+ }
+}
+
+
+void add_cgi_vars(request_rec *r)
+{
+ table *e = r->subprocess_env;
+
+ table_set (e, "GATEWAY_INTERFACE","CGI/1.1");
+ table_set (e, "SERVER_PROTOCOL", r->protocol);
+ table_set (e, "REQUEST_METHOD", r->method);
+ table_set (e, "QUERY_STRING", r->args ? r->args : "");
+
+ /* Note that the code below special-cases scripts run from includes,
+ * because it "knows" that the sub_request has been hacked to have the
+ * args and path_info of the original request, and not any that may have
+ * come with the script URI in the include command. Ugh.
+ */
+
+ if (!r->path_info || !*r->path_info || !strcmp (r->protocol, "INCLUDED")) {
+ table_set (e, "SCRIPT_NAME", r->uri);
+ } else {
+ int path_info_start = strlen (r->uri) - strlen (r->path_info);
+
+ r->uri[path_info_start] = '\0';
+ table_set (e, "SCRIPT_NAME", r->uri);
+ r->uri[path_info_start] = '/';
+ }
+
+ if (r->path_info && r->path_info[0]) {
+ /*
+ * To get PATH_TRANSLATED, treat PATH_INFO as a URI path.
+ * Need to re-escape it for this, since the entire URI was
+ * un-escaped before we determined where the PATH_INFO began.
+ */
+ request_rec *pa_req = sub_req_lookup_uri(
+ escape_uri(r->pool, r->path_info), r);
+
+ table_set (e, "PATH_INFO", r->path_info);
+
+ /* Don't bother destroying pa_req --- it's only created in
+ * child processes which are about to jettison their address
+ * space anyway. BTW, we concatenate filename and path_info
+ * from the sub_request to be compatible in case the PATH_INFO
+ * is pointing to an object which doesn't exist.
+ */
+
+ if (pa_req->filename)
+ table_set (e, "PATH_TRANSLATED",
+ pstrcat (r->pool, pa_req->filename, pa_req->path_info,
+ NULL));
+ }
+}
+
+int scan_script_header(request_rec *r, FILE *f)
+{
+ char w[MAX_STRING_LEN];
+ char *l;
+ int p;
+
+ hard_timeout ("read script header", r);
+
+ while(1) {
+
+ if (fgets(w, MAX_STRING_LEN-1, f) == NULL) {
+ log_reason ("Premature end of script headers", r->filename, r);
+ return SERVER_ERROR;
+ }
+
+ /* Delete terminal (CR?)LF */
+
+ p = strlen(w);
+ if (p > 0 && w[p-1] == '\n')
+ {
+ if (p > 1 && w[p-2] == '\015') w[p-2] = '\0';
+ else w[p-1] = '\0';
+ }
+
+ if(w[0] == '\0') {
+ kill_timeout (r);
+ return OK;
+ }
+
+ /* if we see a bogus header don't ignore it. Shout and scream */
+
+ if(!(l = strchr(w,':'))) {
+ char malformed[(sizeof MALFORMED_MESSAGE)+1+MALFORMED_HEADER_LENGTH_TO_SHOW];
+ strcpy(malformed, MALFORMED_MESSAGE);
+ strncat(malformed, w, MALFORMED_HEADER_LENGTH_TO_SHOW);
+ /* Soak up all the script output --- may save an outright kill */
+ while (fgets(w, MAX_STRING_LEN-1, f) != NULL)
+ continue;
+
+ kill_timeout (r);
+ log_reason (malformed, r->filename, r);
+ return SERVER_ERROR;
+ }
+
+ *l++ = '\0';
+ while (*l && isspace (*l)) ++l;
+
+ if(!strcasecmp(w,"Content-type")) {
+
+ /* Nuke trailing whitespace */
+
+ char *endp = l + strlen(l) - 1;
+ while (endp > l && isspace(*endp)) *endp-- = '\0';
+
+ r->content_type = pstrdup (r->pool, l);
+ }
+ else if(!strcasecmp(w,"Status")) {
+ sscanf(l, "%d", &r->status);
+ r->status_line = pstrdup(r->pool, l);
+ }
+ else if(!strcasecmp(w,"Location")) {
+ table_set (r->headers_out, w, l);
+ }
+
+/* The HTTP specification says that it is legal to merge duplicate
+ * headers into one. Some browsers that support Cookies don't like
+ * merged headers and prefer that each Set-Cookie header is sent
+ * separately. Lets humour those browsers.
+ */
+ else if(!strcasecmp(w, "Set-Cookie")) {
+ table_add(r->err_headers_out, w, l);
+ }
+ else {
+ table_merge (r->err_headers_out, w, l);
+ }
+ }
+}
+
+void send_size(size_t size, request_rec *r) {
+ char ss[20];
+
+ if(size == -1)
+ strcpy(ss, " -");
+ else if(!size)
+ strcpy(ss, " 0k");
+ else if(size < 1024)
+ strcpy(ss, " 1k");
+ else if(size < 1048576)
+ sprintf(ss, "%4dk", size / 1024);
+ else
+ sprintf(ss, "%4dM", size / 1048576);
+ rputs(ss, r);
+}
+
+#ifdef __EMX__
+char **create_argv_cmd(pool *p, char *av0, char *args, char *path) {
+ register int x,n;
+ char **av;
+ char *w;
+
+ for(x=0,n=2;args[x];x++)
+ if(args[x] == '+') ++n;
+
+ /* Add extra strings to array. */
+ n = n + 2;
+
+ av = (char **)palloc(p, (n+1)*sizeof(char *));
+ av[0] = av0;
+
+ /* Now insert the extra strings we made room for above. */
+ av[1] = strdup("/C");
+ av[2] = strdup(path);
+
+ for(x=(1+2);x.
+ *
+ */
+
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+
+/* Why are you even thinking about using this? */
+
+int ai_backcompat_kludge (request_rec *r)
+{
+ if (r->method_number != M_GET ||
+ (r->args && r->args[0]) || (r->path_info && r->path_info[0]))
+ {
+ char *doit_filename = pstrcat (r->pool, r->filename, ".doit", NULL);
+ struct stat finfo;
+
+ if (stat (doit_filename, &finfo) >= 0) {
+ r->filename = doit_filename;
+ r->finfo = finfo;
+ }
+ }
+
+ return DECLINED;
+}
+
+module ai_backcompat_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ NULL, /* dir config creater */
+ NULL, /* dir merger --- default is to override */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ NULL, /* command table */
+ NULL, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ ai_backcompat_kludge, /* type_checker */
+ NULL, /* fixups */
+ NULL /* logger */
+};
diff --git a/RELEASE_1_1_X/src/mod_cookies.c b/RELEASE_1_1_X/src/mod_cookies.c
new file mode 100644
index 00000000000..241c10b800b
--- /dev/null
+++ b/RELEASE_1_1_X/src/mod_cookies.c
@@ -0,0 +1,299 @@
+
+/* ====================================================================
+ * Copyright (c) 1995, 1996 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+
+/* User Tracking Module
+ *
+ * This Apache module is designed to track users paths through a site.
+ * It uses the client-side state ("Cookie") protocol developed by Netscape.
+ * It is known to work on Netscape browsers, Microsoft Internet
+ * Explorer and others currently being developed.
+ *
+ * Each time a page is requested we look to see if the browser is sending
+ * us a Cookie: header that we previously generated.
+ *
+ * If we don't find one then the user hasn't been to this site since
+ * starting their browser or their browser doesn't support cookies. So
+ * we generate a unique Cookie for the transaction and send it back to
+ * the browser (via a "Set-Cookie" header)
+ * Future requests from the same browser should keep the same Cookie line.
+ *
+ * The cookie and request are logged to a file. Use the directive
+ * "CookieLog somefilename" in one of the config files to enable the Cookie
+ * module. By matching up all the requests with the same cookie you can
+ * work out exactly what path a user took through your site.
+ *
+ * Notes:
+ * 1. This code doesn't log the initial transaction (the one that created
+ * the cookie to start with). If it did then the cookie log file would
+ * be bigger than a normal access log.
+ * 2. This module has been designed to not interfere with other Cookies
+ * your site may be using; just avoid sending out cookies with
+ * the name "Apache=" or things will get confused.
+ * 3. If you want you can modify the Set-Cookie line so that the Cookie
+ * never expires. You would then get the same Cookie each time the
+ * user revisits your site.
+ *
+ * Mark Cox, mark@ukweb.com, http://www.ukweb.com/~mark/, 6 July 95
+ *
+ * 6.12.95 MJC Now be more friendly. Allow our cookies to overlap with
+ * others the site may be using. Use a more descriptive
+ * cookie name.
+ *
+ * 18.3.96 MJC Generate cookies for EVERY request no matter what the
+ * browser. We never know when a new browser writer will
+ * add cookie support.
+ *
+ * 96/03/31 -JimC Allow the log to be sent to a pipe. Copies the relevant
+ * code from mod_log_agent.c.
+ *
+ * 24.5.96 MJC Improved documentation after receiving comments from users
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include
+
+module cookies_module;
+
+typedef struct {
+ char *fname;
+ int log_fd;
+ int always;
+} cookie_log_state;
+
+/* Make Cookie: Now we have to generate something that is going to be
+ * pretty unique. We can base it on the pid, time, hostip */
+
+#define COOKIE_NAME "Apache="
+
+void make_cookie(request_rec *r)
+{
+ struct timeval tv;
+ char new_cookie[100]; /* blurgh */
+ char *dot;
+ const char *rname = pstrdup(r->pool,
+ get_remote_host(r->connection, r->per_dir_config,
+ REMOTE_NAME));
+
+ struct timezone tz = { 0 , 0 };
+
+ if ((dot = strchr(rname,'.'))) *dot='\0'; /* First bit of hostname */
+ gettimeofday(&tv, &tz);
+ sprintf(new_cookie,"%s%s%d%ld%d; path=/",
+ COOKIE_NAME, rname,
+ (int)getpid(),
+ (long)tv.tv_sec, (int)tv.tv_usec/1000 );
+
+ table_set(r->headers_out,"Set-Cookie",new_cookie);
+ return;
+}
+
+int spot_cookie(request_rec *r)
+{
+ char *cookie;
+
+ if ((cookie = table_get (r->headers_in, "Cookie")))
+ if (strstr(cookie,COOKIE_NAME))
+ return DECLINED; /* Theres already a cookie, no new one */
+ make_cookie(r);
+ return OK; /* We set our cookie */
+}
+
+static int cookie_flags = ( O_WRONLY | O_APPEND | O_CREAT );
+
+#ifdef __EMX__
+/* OS/2 lacks support for users and groups */
+static mode_t cookie_mode = ( S_IREAD | S_IWRITE );
+#else
+static mode_t cookie_mode = ( S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH );
+#endif
+
+void *make_cookie_log_state (pool *p, server_rec *s)
+{
+ cookie_log_state *cls =
+ (cookie_log_state *)palloc (p, sizeof (cookie_log_state));
+
+ cls->fname = "";
+ cls->log_fd = -1;
+
+ return (void *)cls;
+}
+
+char *set_cookie_log (cmd_parms *parms, void *dummy, char *arg)
+{
+ cookie_log_state *cls = get_module_config (parms->server->module_config,
+ &cookies_module);
+ cls->fname = arg;
+ return NULL;
+}
+
+command_rec cookie_log_cmds[] = {
+{ "CookieLog", set_cookie_log, NULL, RSRC_CONF, TAKE1,
+ "the filename of the cookie log" },
+{ NULL }
+};
+
+void cookie_log_child (void *cmd)
+{
+ /* Child process code for 'CookieLog "|..."';
+ * may want a common framework for this, since I expect it will
+ * be common for other foo-loggers to want this sort of thing...
+ */
+
+ cleanup_for_exec();
+ signal (SIGHUP, SIG_IGN);
+ execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL);
+ exit (1);
+}
+
+void open_cookie_log (server_rec *s, pool *p)
+{
+ cookie_log_state *cls = get_module_config (s->module_config,
+ &cookies_module);
+ char *fname = server_root_relative (p, cls->fname);
+
+ if (cls->log_fd > 0) return;
+
+ if (*cls->fname == '|') {
+ FILE *dummy;
+
+ spawn_child(p, cookie_log_child, (void *)(cls->fname+1),
+ kill_after_timeout, &dummy, NULL);
+
+ if (dummy == NULL) {
+ fprintf (stderr, "Couldn't fork child for CookieLog process\n");
+ exit (1);
+ }
+
+ cls->log_fd = fileno (dummy);
+ }
+ else if(*cls->fname != '\0') {
+ if((cls->log_fd = popenf(p, fname, cookie_flags, cookie_mode)) < 0) {
+ fprintf(stderr, "httpd: could not open cookie log file %s.\n", fname);
+ perror("open");
+ exit(1);
+ }
+ }
+}
+
+void init_cookie_log (server_rec *s, pool *p)
+{
+ for (; s; s = s->next) open_cookie_log (s, p);
+}
+
+int cookie_log_transaction(request_rec *orig)
+{
+ cookie_log_state *cls = get_module_config (orig->server->module_config,
+ &cookies_module);
+ char *str;
+ long timz;
+ struct tm *t;
+ char tstr[MAX_STRING_LEN],sign;
+ request_rec *r;
+ char *cookie,*cookiebuf,*cookieend;
+ char *value;
+
+ for (r = orig; r->next; r = r->next)
+ continue;
+ if (*cls->fname == '\0') /* Don't log cookies */
+ return DECLINED;
+
+ if (!(cookie = table_get (r->headers_in, "Cookie")))
+ return DECLINED; /* Theres no cookie, don't bother logging */
+ value=strstr(cookie,COOKIE_NAME);
+ if (!value) /* Only log cookies we generated! */
+ return DECLINED;
+ value+=strlen(COOKIE_NAME);
+ cookiebuf=pstrdup( r->pool, value );
+ cookieend=strchr(cookiebuf,';');
+ if (cookieend) *cookieend='\0'; /* Ignore anything after a ; */
+
+ t = get_gmtoff(&timz);
+ sign = (timz < 0 ? '-' : '+');
+ if(timz < 0)
+ timz = -timz;
+
+ strftime(tstr,MAX_STRING_LEN,"\" [%d/%b/%Y:%H:%M:%S ",t);
+ if (r->status != -1)
+ sprintf(&tstr[strlen(tstr)], "%c%02ld%02ld] %d\n", sign, timz/3600,
+ timz%3600, r->status);
+ sprintf(&tstr[strlen(tstr)], "%c%02ld%02ld] -\n", sign, timz/3600,
+ timz%3600);
+
+ str = pstrcat(orig->pool, cookiebuf, " \"", orig->the_request, tstr, NULL);
+
+ write(cls->log_fd, str, strlen(str));
+
+ return OK;
+}
+
+
+module cookies_module = {
+ STANDARD_MODULE_STUFF,
+ init_cookie_log, /* initializer */
+ NULL, /* dir config creater */
+ NULL, /* dir merger --- default is to override */
+ make_cookie_log_state, /* server config */
+ NULL, /* merge server configs */
+ cookie_log_cmds, /* command table */
+ NULL, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ spot_cookie, /* fixups */
+ cookie_log_transaction, /* logger */
+};
diff --git a/RELEASE_1_1_X/src/mod_log_common.c b/RELEASE_1_1_X/src/mod_log_common.c
new file mode 100644
index 00000000000..a18d97674a0
--- /dev/null
+++ b/RELEASE_1_1_X/src/mod_log_common.c
@@ -0,0 +1,220 @@
+
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+
+
+#include "httpd.h"
+#include "http_core.h"
+#include "http_config.h"
+
+module common_log_module;
+
+static int xfer_flags = ( O_WRONLY | O_APPEND | O_CREAT );
+
+#ifdef __EMX__
+/* OS/2 lacks support for users and groups */
+static mode_t xfer_mode = ( S_IREAD | S_IWRITE );
+#else
+static mode_t xfer_mode = ( S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH );
+#endif
+
+typedef struct {
+ char *fname;
+ int log_fd;
+} common_log_state;
+
+void *make_common_log_state (pool *p, server_rec *s)
+{
+ common_log_state *cls =
+ (common_log_state *)palloc (p, sizeof (common_log_state));
+
+ cls->fname = DEFAULT_XFERLOG;
+ cls->log_fd = -1;
+
+ return (void *)cls;
+}
+
+char *set_common_log (cmd_parms *parms, void *dummy, char *arg)
+{
+ common_log_state *cls = get_module_config (parms->server->module_config,
+ &common_log_module);
+
+ cls->fname = arg;
+ return NULL;
+}
+
+command_rec common_log_cmds[] = {
+{ "TransferLog", set_common_log, NULL, RSRC_CONF, TAKE1,
+ "the filename of the access log" },
+{ NULL }
+};
+
+void common_log_child (void *cmd)
+{
+ /* Child process code for 'TransferLog "|..."';
+ * may want a common framework for this, since I expect it will
+ * be common for other foo-loggers to want this sort of thing...
+ */
+
+ cleanup_for_exec();
+ signal (SIGHUP, SIG_IGN);
+ execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL);
+ fprintf (stderr, "Exec of shell for logging failed!!!\n");
+ exit (1);
+}
+
+void open_common_log (server_rec *s, pool *p)
+{
+ common_log_state *cls = get_module_config (s->module_config,
+ &common_log_module);
+
+ char *fname = server_root_relative (p, cls->fname);
+
+ if (cls->log_fd > 0) return; /* virtual log shared w/main server */
+
+ if (*cls->fname == '|') {
+ FILE *dummy;
+
+ spawn_child(p, common_log_child, (void *)(cls->fname+1),
+ kill_after_timeout, &dummy, NULL);
+
+ if (dummy == NULL) {
+ fprintf (stderr, "Couldn't fork child for TransferLog process\n");
+ exit (1);
+ }
+
+ cls->log_fd = fileno (dummy);
+ }
+ else if((cls->log_fd = popenf(p, fname, xfer_flags, xfer_mode)) < 0) {
+ fprintf(stderr,"httpd: could not open transfer log file %s.\n", fname);
+ perror("open");
+ exit(1);
+ }
+}
+
+void init_common_log (server_rec *s, pool *p)
+{
+ for (; s; s = s->next) open_common_log (s, p);
+}
+
+int common_log_transaction(request_rec *orig)
+{
+ common_log_state *cls = get_module_config (orig->server->module_config,
+ &common_log_module);
+
+ char *str;
+ long timz;
+ struct tm *t;
+ const char *rem_logname;
+ char tstr[MAX_STRING_LEN], status[MAX_STRING_LEN], sign;
+ conn_rec *c = orig->connection;
+ request_rec *r;
+
+ /* Common log format records an unholy melange of the original request
+ * and whatever it was that we actually served. Just stay compatible
+ * here; the whole point of the module scheme is to allow people to
+ * create better alternatives, but screwing up is an option we wish
+ * to preserve...
+ */
+
+ for (r = orig; r->next; r = r->next)
+ continue;
+
+ t = get_gmtoff(&timz);
+ sign = (timz < 0 ? '-' : '+');
+ if(timz < 0)
+ timz = -timz;
+
+ sprintf(tstr, " [%.2d/%s/%d:%.2d:%.2d:%.2d %c%02ld%02ld] \"", t->tm_mday,
+ month_snames[t->tm_mon], t->tm_year + 1900, t->tm_hour, t->tm_min,
+ t->tm_sec, sign, timz/3600, timz%3600);
+
+ if (r->status != -1) sprintf(status,"%d ", r->status);
+ else strcpy(status, "- ");
+
+ if (r->bytes_sent > 0)
+ sprintf(&status[strlen(status)], "%ld\n", r->bytes_sent);
+ else
+ strcat(status, "-\n");
+
+ rem_logname = get_remote_logname(r);
+ if (rem_logname == NULL) rem_logname = "-";
+
+ str = pstrcat(orig->pool,
+ get_remote_host(c, r->per_dir_config, REMOTE_NAME), " ",
+ rem_logname, " ", (c->user != NULL ? c->user : "-"), tstr,
+ (orig->the_request != NULL ? orig->the_request : "NULL"),
+ "\" ", status, NULL);
+
+ write(cls->log_fd, str, strlen(str));
+
+ return OK;
+}
+
+module common_log_module = {
+ STANDARD_MODULE_STUFF,
+ init_common_log, /* initializer */
+ NULL, /* create per-dir config */
+ NULL, /* merge per-dir config */
+ make_common_log_state, /* server config */
+ NULL, /* merge server config */
+ common_log_cmds, /* command table */
+ NULL, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* fixups */
+ common_log_transaction /* logger */
+};
diff --git a/RELEASE_1_1_X/src/mod_proxy.c b/RELEASE_1_1_X/src/mod_proxy.c
new file mode 100644
index 00000000000..9eb695c305f
--- /dev/null
+++ b/RELEASE_1_1_X/src/mod_proxy.c
@@ -0,0 +1,3262 @@
+/* ====================================================================
+ * Copyright (c) 1996 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+/*
+Note that the Explain() stuff is not yet complete.
+Also note numerous FIXMEs and CHECKMEs which should be eliminated.
+
+If TESTING is set, then garbage collection doesn't delete ... probably a good
+idea when hacking.
+
+This code is still experimental!
+
+Things to do:
+
+1. Make it garbage collect in the background, not while someone is waiting for
+a response!
+
+2. Check the logic thoroughly.
+
+3. Empty directories are only removed the next time round (but this does avoid
+two passes). Consider doing them the first time round.
+
+Ben Laurie 30 Mar 96
+
+More changes:
+
+0) tested w/SOCKS proxy for http
+1) fixed IP address formation in host2addr()
+2) fixed SIGALRM on big cache cleanup
+3) fixed temp files #tmp not removed
+4) changed PF_INET to AF_INET in socket() calls
+5) installed CONNECT code from Troy Morrison for testing
+6) added NoCache config directive to disallow caching for selected hosts
+
+Chuck Murcko 2 Jun 96
+
+*/
+
+#define TESTING 0
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "http_protocol.h"
+
+#include "md5.h"
+
+#include
+
+#include "explain.h"
+
+DEF_Explain
+
+#define SEC_ONE_DAY 86400 /* one day, in seconds */
+#define SEC_ONE_HR 3600 /* one hour, in seconds */
+
+#define DEFAULT_FTP_DATA_PORT 20
+#define DEFAULT_FTP_PORT 21
+#define DEFAULT_GOPHER_PORT 70
+#define DEFAULT_NNTP_PORT 119
+#define DEFAULT_WAIS_PORT 210
+#define DEFAULT_HTTPS_PORT 443
+#define DEFAULT_SNEWS_PORT 563
+#define DEFAULT_PROSPERO_PORT 1525 /* WARNING: conflict w/Oracle */
+
+/* Some WWW schemes and their default ports; this is basically /etc/services */
+static struct
+{
+ const char *scheme;
+ int port;
+} defports[]={
+ { "ftp", DEFAULT_FTP_PORT},
+ { "gopher", DEFAULT_GOPHER_PORT},
+ { "http", DEFAULT_PORT},
+ { "nntp", DEFAULT_NNTP_PORT},
+ { "wais", DEFAULT_WAIS_PORT},
+ { "https", DEFAULT_HTTPS_PORT},
+ { "snews", DEFAULT_SNEWS_PORT},
+ { "prospero", DEFAULT_PROSPERO_PORT},
+ { NULL, -1} /* unknown port */
+};
+
+
+/* static information about a remote proxy */
+struct proxy_remote
+{
+ const char *scheme; /* the schemes handled by this proxy, or '*' */
+ const char *protocol; /* the scheme used to talk to this proxy */
+ const char *hostname; /* the hostname of this proxy */
+ int port; /* the port for this proxy */
+};
+
+struct proxy_alias {
+ char *real;
+ char *fake;
+};
+
+struct nocache_entry {
+ char *name;
+};
+
+#define DEFAULT_CACHE_SPACE 5
+#define DEFAULT_CACHE_MAXEXPIRE SEC_ONE_DAY
+#define DEFAULT_CACHE_EXPIRE SEC_ONE_HR
+#define DEFAULT_CACHE_LMFACTOR (0.1)
+
+/* static information about the local cache */
+struct cache_conf
+{
+ const char *root; /* the location of the cache directory */
+ int space; /* Maximum cache size (in 1024 bytes) */
+ int maxexpire; /* Maximum time to keep cached files in secs */
+ int defaultexpire; /* default time to keep cached file in secs */
+ double lmfactor; /* factor for estimating expires date */
+ int gcinterval; /* garbage collection interval, in seconds */
+ int dirlevels; /* Number of levels of subdirectories */
+ int dirlength; /* Length of subdirectory names */
+};
+
+typedef struct
+{
+
+ struct cache_conf cache; /* cache configuration */
+ array_header *proxies;
+ array_header *aliases;
+ array_header *nocaches;
+ int req; /* true if proxy requests are enabled */
+} proxy_server_conf;
+
+/*
+ * A Web proxy module. Stages:
+ *
+ * translate_name: set filename to proxy:
+ * type_checker: set type to PROXY_MAGIC_TYPE if filename begins proxy:
+ * fix_ups: convert the URL stored in the filename to the
+ * canonical form.
+ * handler: handle proxy requests
+ */
+
+struct hdr_entry
+{
+ char *field;
+ char *value;
+};
+
+/* caching information about a request */
+struct cache_req
+{
+ request_rec *req; /* the request */
+ char *url; /* the URL requested */
+ char *filename; /* name of the cache file, or NULL if no cache */
+ char *tempfile; /* name of the temporary file, of NULL if not caching */
+ time_t ims; /* if-modified-since date of request; -1 if no header */
+ BUFF *fp; /* the cache file descriptor if the file is cached
+ and may be returned, or NULL if the file is
+ not cached (or must be reloaded) */
+ time_t expire; /* calculated expire date of cached entity */
+ time_t lmod; /* last-modified date of cached entity */
+ time_t date; /* the date the cached file was last touched */
+ int version; /* update count of the file */
+ unsigned int len; /* content length */
+ char *protocol; /* Protocol, and major/minor number, e.g. HTTP/1.1 */
+ int status; /* the status of the cached file */
+ char *resp_line; /* the whole status like (protocol, code + message) */
+ array_header *hdrs; /* the HTTP headers of the file */
+};
+
+
+extern module proxy_module;
+
+
+static int http_canon(request_rec *r, char *url, const char *scheme,
+ int def_port);
+static int ftp_canon(request_rec *r, char *url);
+
+static int http_handler(request_rec *r, struct cache_req *c, char *url,
+ const char *proxyhost, int proxyport);
+static int ftp_handler(request_rec *r, struct cache_req *c, char *url);
+
+static int connect_handler(request_rec *r, struct cache_req *c, char *url);
+
+static BUFF *cache_error(struct cache_req *r);
+
+/* -------------------------------------------------------------- */
+/* Translate the URL into a 'filename' */
+
+static int
+alias_match(char *uri, char *alias_fakename)
+{
+ char *end_fakename = alias_fakename + strlen (alias_fakename);
+ char *aliasp = alias_fakename, *urip = uri;
+
+ while (aliasp < end_fakename)
+ {
+ if (*aliasp == '/')
+ {
+ /* any number of '/' in the alias matches any number in
+ * the supplied URI, but there must be at least one...
+ */
+ if (*urip != '/') return 0;
+
+ while (*aliasp == '/') ++ aliasp;
+ while (*urip == '/') ++ urip;
+ }
+ else {
+ /* Other characters are compared literally */
+ if (*urip++ != *aliasp++) return 0;
+ }
+ }
+
+ /* Check last alias path component matched all the way */
+
+ if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/')
+ return 0;
+
+ /* Return number of characters from URI which matched (may be
+ * greater than length of alias, since we may have matched
+ * doubled slashes)
+ */
+
+ return urip - uri;
+}
+
+static int
+proxy_trans(request_rec *r)
+{
+ void *sconf = r->server->module_config;
+ proxy_server_conf *conf =
+ (proxy_server_conf *)get_module_config(sconf, &proxy_module);
+
+ if (r->proxyreq)
+ {
+ if (!conf->req) return DECLINED;
+
+ r->filename = pstrcat(r->pool, "proxy:", r->uri, NULL);
+ r->handler = "proxy-server";
+ return OK;
+ } else
+ {
+ int i, len;
+ struct proxy_alias *ent=(struct proxy_alias *)conf->aliases->elts;
+
+ for (i=0; i < conf->aliases->nelts; i++)
+ {
+ len = alias_match(r->uri, ent[i].fake);
+
+ if (len > 0)
+ {
+ r->filename = pstrcat(r->pool, "proxy:", ent[i].real,
+ r->uri + len, NULL);
+ r->handler = "proxy-server";
+ return OK;
+ }
+ }
+ return DECLINED;
+ }
+}
+
+/* -------------------------------------------------------------- */
+/* Fixup the filename */
+
+/*
+ * Canonicalise the URL
+ */
+static int
+proxy_fixup(request_rec *r)
+{
+ char *url, *p;
+ int i;
+
+ if (strncmp(r->filename, "proxy:", 6) != 0) return DECLINED;
+
+ url = &r->filename[6];
+/* lowercase the scheme */
+ p = strchr(url, ':');
+ if (p == NULL || p == url) return BAD_REQUEST;
+ for (i=0; i != p - url; i++) url[i] = tolower(url[i]);
+
+/* canonicalise each specific scheme */
+ if (strncmp(url, "http:", 5) == 0)
+ return http_canon(r, url+5, "http", DEFAULT_PORT);
+ else if (strncmp(url, "ftp:", 4) == 0) return ftp_canon(r, url+4);
+ else return OK; /* otherwise; we've done the best we can */
+}
+
+/* already called in the knowledge that the characters are hex digits */
+static int
+hex2c(const char *x)
+{
+ int i, ch;
+
+ ch = x[0];
+ if (isdigit(ch)) i = ch - '0';
+ else if (isupper(ch)) i = ch - ('A' - 10);
+ else i = ch - ('a' - 10);
+ i <<= 4;
+
+ ch = x[1];
+ if (isdigit(ch)) i += ch - '0';
+ else if (isupper(ch)) i += ch - ('A' - 10);
+ else i += ch - ('a' - 10);
+ return i;
+}
+
+
+static void
+c2hex(int ch, char *x)
+{
+ int i;
+
+ x[0] = '%';
+ i = (ch & 0xF0) >> 4;
+ if (i >= 10) x[1] = ('A' - 10) + i;
+ else x[1] = '0' + i;
+
+ i = ch & 0x0F;
+ if (i >= 10) x[2] = ('A' - 10) + i;
+ else x[2] = '0' + i;
+}
+
+/*
+ * canonicalise a URL-encoded string
+ */
+
+enum enctype { enc_path, enc_search, enc_user, enc_fpath, enc_parm };
+
+/*
+ * Decodes a '%' escaped string, and returns the number of characters
+ */
+static int
+decodeenc(char *x)
+{
+ int i, j, ch;
+
+ if (x[0] == '\0') return 0; /* special case for no characters */
+ for (i=0, j=0; x[i] != '\0'; i++, j++)
+ {
+/* decode it if not already done */
+ ch = x[i];
+ if ( ch == '%' && isxdigit(x[i+1]) && isxdigit(x[i+2]))
+ {
+ ch = hex2c(&x[i+1]);
+ i += 2;
+ }
+ x[j] = ch;
+ }
+ x[j] = '\0';
+ return j;
+}
+
+
+/*
+ * Convert a URL-encoded string to canonical form.
+ * It decodes characters which need not be encoded,
+ * and encodes those which must be encoded, and does not touch
+ * those which must not be touched.
+ */
+static char *
+canonenc(pool *p, const char *x, int len, enum enctype t, int isenc)
+{
+ int i, j, ispath, ch;
+ char *y;
+ const char *allowed; /* characters which should not be encoded */
+ const char *reserved; /* characters which much not be en/de-coded */
+
+/* N.B. in addition to :@&=, this allows ';' in an http path
+ * and '?' in an ftp path -- this may be revised
+ *
+ * Also, it makes a '+' character in a search string reserved, as
+ * it may be form-encoded. (Although RFC 1738 doesn't allow this -
+ * it only permits ; / ? : @ = & as reserved chars.)
+ */
+ if (t == enc_path) allowed = "$-_.+!*'(),;:@&=";
+ else if (t == enc_search) allowed = "$-_.!*'(),;:@&=";
+ else if (t == enc_user) allowed = "$-_.+!*'(),;@&=";
+ else if (t == enc_fpath) allowed = "$-_.+!*'(),?:@&=";
+ else /* if (t == enc_parm) */ allowed = "$-_.+!*'(),?/:@&=";
+
+ if (t == enc_path) reserved = "/";
+ else if (t == enc_search) reserved = "+";
+ else reserved = "";
+
+ y = palloc(p, 3*len+1);
+ ispath = (t == enc_path);
+
+ for (i=0, j=0; i < len; i++, j++)
+ {
+/* always handle '/' first */
+ ch = x[i];
+ if (ind(reserved, ch) != -1)
+ {
+ y[j] = ch;
+ continue;
+ }
+/* decode it if not already done */
+ if (isenc && ch == '%')
+ {
+ if (!isxdigit(x[i+1]) || !isxdigit(x[i+2]))
+ return NULL;
+ ch = hex2c(&x[i+1]);
+ i += 2;
+ if (ch != 0 && ind(reserved, ch) != -1)
+ { /* keep it encoded */
+ c2hex(ch, &y[j]);
+ j += 2;
+ continue;
+ }
+ }
+/* recode it, if necessary */
+ if (!isalnum(ch) && ind(allowed, ch) == -1)
+ {
+ c2hex(ch, &y[j]);
+ j += 2;
+ } else y[j] = ch;
+ }
+ y[j] = '\0';
+ return y;
+}
+
+/*
+ * Parses network-location.
+ * urlp on input the URL; on output the path, after the leading /
+ * user NULL if no user/password permitted
+ * password holder for password
+ * host holder for host
+ * port port number; only set if one is supplied.
+ *
+ * Returns an error string.
+ */
+static char *
+canon_netloc(pool *pool, char **const urlp, char **userp, char **passwordp,
+ char **hostp, int *port)
+{
+ int i;
+ char *p, *host, *url=*urlp;
+
+ if (url[0] != '/' || url[1] != '/') return "Malformed URL";
+ host = url + 2;
+ url = strchr(host, '/');
+ if (url == NULL)
+ url = "";
+ else
+ *(url++) = '\0'; /* skip seperating '/' */
+
+ if (userp != NULL)
+ {
+ char *user=NULL, *password = NULL;
+ p = strchr(host, '@');
+
+ if (p != NULL)
+ {
+ *p = '\0';
+ user = host;
+ host = p + 1;
+
+/* find password */
+ p = strchr(user, ':');
+ if (p != NULL)
+ {
+ *p = '\0';
+ password = canonenc(pool, p+1, strlen(p+1), enc_user, 1);
+ if (password == NULL)
+ return "Bad %-escape in URL (password)";
+ }
+
+ user = canonenc(pool, user, strlen(user), enc_user, 1);
+ if (user == NULL) return "Bad %-escape in URL (username)";
+ }
+ *userp = user;
+ *passwordp = password;
+ }
+
+ p = strchr(host, ':');
+ if (p != NULL)
+ {
+ *(p++) = '\0';
+
+ for (i=0; p[i] != '\0'; i++)
+ if (!isdigit(p[i])) break;
+
+ if (i == 0 || p[i] != '\0')
+ return "Bad port number in URL";
+ *port = atoi(p);
+ if (*port > 65535) return "Port number in URL > 65535";
+ }
+ str_tolower(host); /* DNS names are case-insensitive */
+ if (*host == '\0') return "Missing host in URL";
+/* check hostname syntax */
+ for (i=0; host[i] != '\0'; i++)
+ if (!isdigit(host[i]) && host[i] != '.')
+ break;
+ /* must be an IP address */
+ if (host[i] == '\0' && (inet_addr(host) == -1 || inet_network(host) == -1))
+ return "Bad IP address in URL";
+
+ *urlp = url;
+ *hostp = host;
+
+ return NULL;
+}
+
+/*
+ * checks an encoded ftp string for bad characters, namely, CR, LF or
+ * non-ascii character
+ */
+static int
+ftp_check_string(const char *x)
+{
+ int i, ch;
+
+ for (i=0; x[i] != '\0'; i++)
+ {
+ ch = x[i];
+ if ( ch == '%' && isxdigit(x[i+1]) && isxdigit(x[i+2]))
+ {
+ ch = hex2c(&x[i+1]);
+ i += 2;
+ }
+ if (ch == '\015' || ch == '\012' || (ch & 0x80)) return 0;
+ }
+ return 1;
+}
+
+/*
+ * Canonicalise ftp URLs.
+ */
+static int
+ftp_canon(request_rec *r, char *url)
+{
+ char *user, *password, *host, *path, *parms, *p, sport[7];
+ const char *err;
+ int port;
+
+ port = DEFAULT_FTP_PORT;
+ err = canon_netloc(r->pool, &url, &user, &password, &host, &port);
+ if (err) return BAD_REQUEST;
+ if (user != NULL && !ftp_check_string(user)) return BAD_REQUEST;
+ if (password != NULL && !ftp_check_string(password)) return BAD_REQUEST;
+
+/* now parse path/parameters args, according to rfc1738 */
+/* N.B. if this isn't a true proxy request, then the URL path
+ * (but not query args) has already been decoded.
+ * This gives rise to the problem of a ; being decoded into the
+ * path.
+ */
+ p = strchr(url, ';');
+ if (p != NULL)
+ {
+ *(p++) = '\0';
+ parms = canonenc(r->pool, p, strlen(p), enc_parm, r->proxyreq);
+ if (parms == NULL) return BAD_REQUEST;
+ } else
+ parms = "";
+
+ path = canonenc(r->pool, url, strlen(url), enc_path, r->proxyreq);
+ if (path == NULL) return BAD_REQUEST;
+ if (!ftp_check_string(path)) return BAD_REQUEST;
+
+ if (!r->proxyreq && r->args != NULL)
+ {
+ if (p != NULL)
+ {
+ p = canonenc(r->pool, r->args, strlen(r->args), enc_parm, 1);
+ if (p == NULL) return BAD_REQUEST;
+ parms = pstrcat(r->pool, parms, "?", p, NULL);
+ }
+ else
+ {
+ p = canonenc(r->pool, r->args, strlen(r->args), enc_path, 1);
+ if (p == NULL) return BAD_REQUEST;
+ path = pstrcat(r->pool, path, "?", p, NULL);
+ }
+ r->args = NULL;
+ }
+
+/* now, rebuild URL */
+
+ if (port != DEFAULT_FTP_PORT) sprintf(sport, ":%d", port);
+ else sport[0] = '\0';
+
+ r->filename = pstrcat(r->pool, "proxy:ftp://", (user != NULL) ? user : "",
+ (password != NULL) ? ":" : "",
+ (password != NULL) ? password : "",
+ (user != NULL) ? "@" : "", host, sport, "/", path,
+ (parms[0] != '\0') ? ";" : "", parms, NULL);
+
+ return OK;
+}
+
+
+/*
+ * Canonicalise http-like URLs.
+ * scheme is the scheme for the URL
+ * url is the URL starting with the first '/'
+ * def_port is the default port for this scheme.
+ */
+static int
+http_canon(request_rec *r, char *url, const char *scheme, int def_port)
+{
+ char *host, *path, *search, *p, sport[7];
+ const char *err;
+ int port;
+
+/* do syntatic check.
+ * We break the URL into host, port, path, search
+ */
+ port = def_port;
+ err = canon_netloc(r->pool, &url, NULL, NULL, &host, &port);
+ if (err) return BAD_REQUEST;
+
+/* now parse path/search args, according to rfc1738 */
+/* N.B. if this isn't a true proxy request, then the URL _path_
+ * has already been decoded
+ */
+ if (r->proxyreq)
+ {
+ p = strchr(url, '?');
+ if (p != NULL) *(p++) = '\0';
+ } else
+ p = r->args;
+
+/* process path */
+ path = canonenc(r->pool, url, strlen(url), enc_path, r->proxyreq);
+ if (path == NULL) return BAD_REQUEST;
+
+/* process search */
+ if (p != NULL)
+ {
+ search = p;
+ if (search == NULL) return BAD_REQUEST;
+ } else
+ search = "";
+
+ if (port != def_port) sprintf(sport, ":%d", port);
+ else sport[0] = '\0';
+
+ r->filename = pstrcat(r->pool, "proxy:", scheme, "://", host, sport, "/",
+ path, (search[0] != '\0') ? "?" : "", search, NULL);
+ return OK;
+}
+
+static const char *lwday[7]=
+{"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
+static const char *wday[7]=
+{"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+static const char *months[12]=
+{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov",
+ "Dec"};
+
+/*
+ * If the date is a valid RFC 850 date or asctime() date, then it
+ * is converted to the RFC 1123 format, otherwise it is not modified.
+ * This routine is not very fast at doing conversions, as it uses
+ * sscanf and sprintf. However, if the date is already correctly
+ * formatted, then it exits very quickly.
+ */
+static char *
+date_canon(pool *p, char *x)
+{
+ int wk, mday, year, hour, min, sec, mon;
+ char *q, month[4], zone[4], week[4];
+
+ q = strchr(x, ',');
+ /* check for RFC 850 date */
+ if (q != NULL && q - x > 3 && q[1] == ' ')
+ {
+ *q = '\0';
+ for (wk=0; wk < 7; wk++)
+ if (strcmp(x, lwday[wk]) == 0) break;
+ *q = ',';
+ if (wk == 7) return x; /* not a valid date */
+ if (q[4] != '-' || q[8] != '-' || q[11] != ' ' || q[14] != ':' ||
+ q[17] != ':' || strcmp(&q[20], " GMT") != 0) return x;
+ if (sscanf(q+2, "%u-%3s-%u %u:%u:%u %3s", &mday, month, &year,
+ &hour, &min, &sec, zone) != 7) return x;
+ if (year < 70) year += 2000;
+ else year += 1900;
+ } else
+ {
+/* check for acstime() date */
+ if (x[3] != ' ' || x[7] != ' ' || x[10] != ' ' || x[13] != ':' ||
+ x[16] != ':' || x[19] != ' ' || x[24] != '\0') return x;
+ if (sscanf(x, "%3s %3s %u %u:%u:%u %u", week, month, &mday, &hour,
+ &min, &sec, &year) != 7) return x;
+ for (wk=0; wk < 7; wk++)
+ if (strcmp(week, wday[wk]) == 0) break;
+ if (wk == 7) return x;
+ }
+
+/* check date */
+ for (mon=0; mon < 12; mon++) if (strcmp(month, months[mon]) == 0) break;
+ if (mon == 12) return x;
+/*
+ * it doesn't do any harm to convert an invalid date from one format to
+ * another
+ */
+#if 0
+ if (hour > 23 || min > 60 || sec > 62 || mday == 0 || mday > 31) return x;
+ if (mday == 31 && (mon == 1 || mon == 3 || mon == 5 || mon == 8 || mon == 10))
+ return x;
+ if (mday > 29 && mon == 1) return x;
+ if (mday == 29 && mon == 1)
+ if (year%4 != 0 || (year%100 == 0 && year%400 != 0)) return x;
+#endif
+
+ if (strlen(x) < 31) x = palloc(p, 31);
+ sprintf(x, "%s, %.2d %s %d %.2d:%.2d:%.2d GMT", wday[wk], mday,
+ months[mon], year, hour, min, sec);
+ return x;
+}
+
+
+/* -------------------------------------------------------------- */
+/* Invoke handler */
+
+/* Utility routines */
+
+/*
+ * Reads headers from a connection and returns an array of headers.
+ * Returns NULL on file error
+ */
+static array_header *
+read_headers(pool *pool, char *buffer, int size, BUFF *f)
+{
+ int gotcr, len, i, j;
+ array_header *resp_hdrs;
+ struct hdr_entry *hdr;
+ char *p;
+
+ resp_hdrs = make_array(pool, 10, sizeof(struct hdr_entry));
+ hdr = NULL;
+
+ gotcr = 1;
+ for (;;)
+ {
+ len = bgets(buffer, size, f);
+ if (len == -1) return NULL;
+ if (len == 0) break;
+ if (buffer[len-1] == '\n')
+ {
+ buffer[--len] = '\0';
+ i = 1;
+ } else
+ i = 0;
+
+ if (!gotcr || buffer[0] == ' ' || buffer[0] == '\t')
+ {
+ /* a continuation header */
+ if (hdr == NULL)
+ {
+ /* error!! */
+ if (!i)
+ {
+ i = bskiplf(f);
+ if (i == -1) return NULL;
+ }
+ gotcr = 1;
+ continue;
+ }
+ hdr->value = pstrcat(pool, hdr->value, buffer, NULL);
+ }
+ else if (gotcr && len == 0) break;
+ else
+ {
+ p = strchr(buffer, ':');
+ if (p == NULL)
+ {
+ /* error!! */
+ if (!gotcr)
+ {
+ i = bskiplf(f);
+ if (i == -1) return NULL;
+ }
+ gotcr = 1;
+ hdr = NULL;
+ continue;
+ }
+ hdr = push_array(resp_hdrs);
+ *(p++) = '\0';
+ hdr->field = pstrdup(pool, buffer);
+ while (*p == ' ' || *p == '\t') p++;
+ hdr->value = pstrdup(pool, p);
+ gotcr = i;
+ }
+ }
+
+ hdr = (struct hdr_entry *)resp_hdrs->elts;
+ for (i=0; i < resp_hdrs->nelts; i++)
+ {
+ p = hdr[i].value;
+ j = strlen(p);
+ while (j > 0 && (p[j-1] == ' ' || p[j-1] == '\t')) j--;
+ p[j] = '\0';
+ }
+
+ return resp_hdrs;
+}
+
+static long int
+send_fb(BUFF *f, request_rec *r, BUFF *f2, struct cache_req *c)
+{
+ char buf[IOBUFSIZE];
+ long total_bytes_sent;
+ register int n,o,w;
+ conn_rec *con = r->connection;
+
+ total_bytes_sent = 0;
+ while (!con->aborted) {
+ n = bread(f, buf, IOBUFSIZE);
+ if (n == -1) /* input error */
+ {
+ if (f2 != NULL) f2 = cache_error(c);
+ break;
+ }
+ if (n == 0) break; /* EOF */
+ o=0;
+ total_bytes_sent += n;
+
+ if (f2 != NULL)
+ if (bwrite(f2, buf, n) != n) f2 = cache_error(c);
+
+ while(n && !r->connection->aborted) {
+ w = bwrite(con->client, &buf[o], n);
+ if (w <= 0)
+ break;
+ reset_timeout(r); /* reset timeout after successfule write */
+ n-=w;
+ o+=w;
+ }
+ }
+ bflush(con->client);
+
+ return total_bytes_sent;
+}
+
+/*
+ * Read a header from the array, returning the first entry
+ */
+static struct hdr_entry *
+get_header(array_header *hdrs_arr, const char *name)
+{
+ struct hdr_entry *hdrs;
+ int i;
+
+ hdrs = (struct hdr_entry *)hdrs_arr->elts;
+ for (i = 0; i < hdrs_arr->nelts; i++)
+ if (hdrs[i].field != NULL && strcasecmp(name, hdrs[i].field) == 0)
+ return &hdrs[i];
+
+ return NULL;
+}
+
+#define HDR_APP (0)
+#define HDR_REP (1)
+
+/*
+ * Add to the header reply, either concatenating, or replacing existin
+ * headers. It stores the pointers provided, so make sure the data
+ * is not subsequently overwritten
+ */
+static struct hdr_entry *
+add_header(array_header *hdrs_arr, char *field, char *value,
+ int rep)
+{
+ int i;
+ struct hdr_entry *hdrs;
+
+ hdrs = (struct hdr_entry *)hdrs_arr->elts;
+ if (rep)
+ for (i = 0; i < hdrs_arr->nelts; i++)
+ if (hdrs[i].field != NULL && strcasecmp(field, hdrs[i].field) == 0)
+ {
+ hdrs[i].value = value;
+ return hdrs;
+ }
+
+ hdrs = push_array(hdrs_arr);
+ hdrs->field = field;
+ hdrs->value = value;
+
+ return hdrs;
+}
+
+#ifdef NEEDED
+static void
+del_header(array_header *hdrs_arr, const char *field)
+{
+ int i;
+ struct hdr_entry *hdrs;
+
+ hdrs = (struct hdr_entry *)hdrs_arr->elts;
+
+ for (i = 0; i < hdrs_arr->nelts; i++)
+ if (hdrs[i].field != NULL && strcasecmp(field, hdrs[i].field) == 0)
+ hdrs[i].value = NULL;
+}
+#endif
+
+/*
+ * Sends response line and headers
+ */
+static void
+send_headers(BUFF *fp, const char *respline, array_header *hdrs_arr)
+{
+ struct hdr_entry *hdrs;
+ int i;
+
+ hdrs = (struct hdr_entry *)hdrs_arr->elts;
+
+ bputs(respline, fp);
+ bputs("\015\012", fp);
+ for (i = 0; i < hdrs_arr->nelts; i++)
+ {
+ if (hdrs[i].field == NULL) continue;
+ bvputs(fp, hdrs[i].field, ": ", hdrs[i].value, "\015\012", NULL);
+ }
+
+ bputs("\015\012", fp);
+}
+
+
+/*
+ * list is a comma-separated list of case-insensitive tokens, with
+ * optional whitespace around the tokens.
+ * The return returns 1 if the token val is found in the list, or 0
+ * otherwise.
+ */
+static int
+liststr(const char *list, const char *val)
+{
+ int len, i;
+ const char *p;
+
+ len = strlen(val);
+
+ while (list != NULL)
+ {
+ p = strchr(list, ',');
+ if (p != NULL)
+ {
+ i = p - list;
+ do p++; while (isspace(*p));
+ }
+ else
+ i = strlen(list);
+
+ while (i > 0 && isspace(list[i-1])) i--;
+ if (i == len && strncasecmp(list, val, len) == 0) return 1;
+ list = p;
+ }
+ return 0;
+}
+
+/* number of characters in the hash */
+#define HASH_LEN (22*2)
+
+static void
+hash(const char *it, char *val,int ndepth,int nlength)
+{
+ MD5_CTX context;
+ unsigned char digest[16];
+ char tmp[22];
+ int i, k, d;
+ unsigned int x;
+ static const char table[64]=
+"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_@";
+
+ MD5Init(&context);
+ MD5Update(&context, (const unsigned char *)it, strlen(it));
+ MD5Final(digest, &context);
+
+/* encode 128 bits as 22 characters, using a modified uuencoding */
+/* the encoding is 3 bytes -> 4 characters
+ * i.e. 128 bits is 5 x 3 bytes + 1 byte -> 5 * 4 characters + 2 characters
+ */
+ for (i=0, k=0; i < 15; i += 3)
+ {
+ x = (digest[i] << 16) | (digest[i+1] << 8) | digest[i+2];
+ tmp[k++] = table[x >> 18];
+ tmp[k++] = table[(x >> 12) & 0x3f];
+ tmp[k++] = table[(x >> 6) & 0x3f];
+ tmp[k++] = table[x & 0x3f];
+ }
+/* one byte left */
+ x = digest[15];
+ tmp[k++] = table[x >> 2]; /* use up 6 bits */
+ tmp[k++] = table[(x << 4) & 0x3f];
+ /* now split into directory levels */
+
+ for(i=k=d=0 ; d < ndepth ; ++d)
+ {
+ strncpy(&val[i],&tmp[k],nlength);
+ k+=nlength;
+ val[i+nlength]='/';
+ i+=nlength+1;
+ }
+ memcpy(&val[i],&tmp[k],22-k);
+ val[i+22-k]='\0';
+}
+
+/*
+ * Compare a string to a mask
+ * Mask characters:
+ * @ - uppercase letter
+ * # - lowercase letter
+ * & - hex digit
+ * # - digit
+ * * - swallow remaining characters
+ * - exact match for any other character
+ */
+static int
+checkmask(const char *data, const char *mask)
+{
+ int i, ch, d;
+
+ for (i=0; mask[i] != '\0' && mask[i] != '*'; i++)
+ {
+ ch = mask[i];
+ d = data[i];
+ if (ch == '@')
+ {
+ if (!isupper(d)) return 0;
+ } else if (ch == '$')
+ {
+ if (!islower(d)) return 0;
+ } else if (ch == '#')
+ {
+ if (!isdigit(d)) return 0;
+ } else if (ch == '&')
+ {
+ if (!isxdigit(d)) return 0;
+ } else if (ch != d) return 0;
+ }
+
+ if (mask[i] == '*') return 1;
+ else return (data[i] == '\0');
+}
+
+/*
+ * This routine converts a tm structure into the number of seconds
+ * since 1st January 1970 UT
+ *
+ * The return value is a non-negative integer on success or -1 if the
+ * input date is out of the domain Thu, 01 Jan 1970 00:00:00 to
+ * Tue, 19 Jan 2038 03:14:07 inclusive
+ *
+ * Notes
+ * This routine has been tested on 1000000 valid dates generated
+ * at random by gmtime().
+ *
+ * This routine is very fast, much faster than mktime().
+ */
+static int
+tm2sec(const struct tm *t)
+{
+ int days, year;
+ static const int dayoffs[12]=
+ {306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275};
+
+ year = t->tm_year;
+/* shift new year to 1st March; which is where it should be */
+ if (t->tm_mon < 2) year--; /* now years and months since 1st March 1900 */
+ days = t->tm_mday - 1 + dayoffs[t->tm_mon];
+
+/* find the number of days since 1st March 1900 (in the Gregorian calendar) */
+ days += year * 365 + year/4 - year/100 + (year/100 + 3)/4;
+ days -= 25508; /* 1 jan 1970 is 25508 days since 1 mar 1900 */
+
+ days = ((days * 24 + t->tm_hour) * 60 + t->tm_min) * 60 + t->tm_sec;
+ if (year < 69 || year > 138 || days < 0) /* must have overflowed */
+ return -1;
+ else
+ return days;
+}
+
+/*
+ * Parses a standard HTTP date.
+ *
+ * The restricted HTTP syntax is
+ * rfc1123-date = day "," SP 2DIGIT SP date SP time SP "GMT"
+ * date = 2DIGIT SP month SP 4DIGIT
+ * time = 2DIGIT ":" 2DIGIT ":" 2DIGIT
+ *
+ * day = "Mon" | "Tue" | "Wed" | "Thu" | "Fri" | "Sat" | "Sun"
+ *
+ * month = "Jan" | "Feb" | "Mar" | "Apr" | "May" | "Jun" |
+ * "Jul" | "Aug" | "Sep" | "Oct" | "Nov" | "Dec"
+ *
+ * The spec is not clear as to whether the day and months are
+ * case-sensitive or not. This code assumes they are.
+ *
+ * It fills in the year, month, mday, hour, min, sec and is_dst fields of
+ * date. It does not set the wday or yday fields.
+ * On failure is sets the year to 0.
+ *
+ * It also returns the number of seconds since 1 Jan 1970 UT, or
+ * -1 if this would be out of range or if the date is invalid.
+ *
+ * Notes
+ * This routine has been tested on 100000 valid dates generated
+ * at random by strftime().
+ *
+ * This routine is very fast; it would be 10x slower if it
+ * used sscanf.
+ */
+static int
+parsedate(const char *date, struct tm *d)
+{
+ int mint, mon, year;
+ struct tm x;
+ const int months[12]={
+ ('J' << 16) | ( 'a' << 8) | 'n', ('F' << 16) | ( 'e' << 8) | 'b',
+ ('M' << 16) | ( 'a' << 8) | 'r', ('A' << 16) | ( 'p' << 8) | 'r',
+ ('M' << 16) | ( 'a' << 8) | 'y', ('J' << 16) | ( 'u' << 8) | 'n',
+ ('J' << 16) | ( 'u' << 8) | 'l', ('A' << 16) | ( 'u' << 8) | 'g',
+ ('S' << 16) | ( 'e' << 8) | 'p', ('O' << 16) | ( 'c' << 8) | 't',
+ ('N' << 16) | ( 'o' << 8) | 'v', ('D' << 16) | ( 'e' << 8) | 'c'};
+ if (d == NULL) d = &x;
+
+ d->tm_year = 0; /* bad date */
+ if (!checkmask(date, "@$$, ## @$$ #### ##:##:## GMT")) return -1;
+
+/* we don't test the weekday */
+ d->tm_mday = (date[5] - '0') * 10 + (date[6] - '0');
+ if (d->tm_mday == 0 || d->tm_mday > 31) return -1;
+
+ mint = (date[8] << 16) | (date[9] << 8) | date[10];
+ for (mon=0; mon < 12; mon++) if (mint == months[mon]) break;
+ if (mon == 12) return -1;
+
+ d->tm_mon = mon;
+ year = date[12] * 1000 + date[13] * 100 + date[14] * 10 + date[15] -
+ ('0' * 1111);
+ d->tm_hour = date[17] * 10 + date[18] - '0' * 11;
+ d->tm_min = date[20] * 10 + date[21] - '0' * 11;
+ d->tm_sec = date[23] * 10 + date[24] - '0' * 11;
+
+ if (d->tm_hour > 23 || d->tm_min > 59 || d->tm_sec > 61) return -1;
+
+ if (d->tm_mday == 31 && (mon == 1 || mon == 3 || mon == 5 || mon == 8 ||
+ mon == 10)) return -1;
+ if (d->tm_mday > 29 && mon == 1) return -1;
+ if (d->tm_mday == 29 && mon == 1)
+ if (year%4 != 0 || (year%100 == 0 && year%400 != 0)) return -1;
+
+ d->tm_year = year - 1900;
+ d->tm_isdst = 0;
+ return tm2sec(d);
+}
+
+/*
+ * Converts 8 hex digits to a time integer
+ */
+static int
+hex2sec(const char *x)
+{
+ int i, ch;
+ unsigned int j;
+
+ for (i=0, j=0; i < 8; i++)
+ {
+ ch = x[i];
+ j <<= 4;
+ if (isdigit(ch)) j |= ch - '0';
+ else if (isupper(ch)) j |= ch - ('A' - 10);
+ else j |= ch - ('a' - 10);
+ }
+ if (j == 0xffffffff) return -1; /* so that it works with 8-byte ints */
+ else return j;
+}
+
+/*
+ * Converts a time integer to 8 hex digits
+ */
+static void
+sec2hex(int t, char *y)
+{
+ int i, ch;
+ unsigned int j=t;
+
+ for (i=7; i >= 0; i--)
+ {
+ ch = j & 0xF;
+ j >>= 4;
+ if (ch >= 10) y[i] = ch + ('A' - 10);
+ else y[i] = ch + '0';
+ }
+ y[8] = '\0';
+}
+
+
+static void
+log_uerror(const char *routine, const char *file, const char *err,
+ server_rec *s)
+{
+ char *p, *q;
+
+ q = get_time();
+ p = strerror(errno);
+
+ if (err != NULL)
+ {
+ fprintf(s->error_log, "[%s] %s\n", q, err);
+ if (file != NULL)
+ fprintf(s->error_log, "- %s: %s: %s\n", routine, file, p);
+ else
+ fprintf(s->error_log, "- %s: %s\n", routine, p);
+ } else
+ {
+ if (file != NULL)
+ fprintf(s->error_log, "[%s] %s: %s: %s\n", q, routine, file, p);
+ else
+ fprintf(s->error_log, "[%s] %s: %s\n", q, routine, p);
+ }
+
+ fflush(s->error_log);
+}
+
+struct gc_ent
+{
+ unsigned long int len;
+ time_t expire;
+ char file[HASH_LEN+1];
+
+};
+
+static int
+gcdiff(const void *ap, const void *bp)
+{
+ const struct gc_ent *a=*(struct gc_ent **)ap, *b=*(struct gc_ent **)bp;
+
+ if (a->expire > b->expire) return 1;
+ else if (a->expire < b->expire) return -1;
+ else return 0;
+}
+
+static int curbytes, cachesize, every;
+static unsigned long int curblocks;
+static time_t now, expire;
+static char *filename;
+
+static int sub_garbage_coll(request_rec *r,array_header *files,
+ const char *cachedir,const char *cachesubdir);
+
+static void garbage_coll(request_rec *r)
+ {
+ const char *cachedir;
+ void *sconf = r->server->module_config;
+ proxy_server_conf *pconf =
+ (proxy_server_conf *)get_module_config(sconf, &proxy_module);
+ const struct cache_conf *conf=&pconf->cache;
+ array_header *files;
+ struct stat buf;
+ struct gc_ent *fent,**elts;
+ int i;
+ static time_t lastcheck=-1; /* static data!!! */
+
+ cachedir = conf->root;
+ cachesize = conf->space;
+ every = conf->gcinterval;
+
+ if (cachedir == NULL || every == -1) return;
+ now = time(NULL);
+ if (now != -1 && lastcheck != -1 && now < lastcheck + every) return;
+
+ block_alarms(); /* avoid SIGALRM on big cache cleanup */
+
+ filename = palloc(r->pool, strlen(cachedir) + HASH_LEN + 2);
+ strcpy(filename, cachedir);
+ strcat(filename, "/.time");
+ if (stat(filename, &buf) == -1) /* does not exist */
+ {
+ if (errno != ENOENT)
+ {
+ log_uerror("stat", filename, NULL, r->server);
+ return;
+ }
+ if (creat(filename, 0666) == -1)
+ {
+ if (errno != EEXIST)
+ log_uerror("creat", filename, NULL, r->server);
+ else
+ lastcheck = now; /* someone else got in there */
+ return;
+ }
+ } else
+ {
+ lastcheck = buf.st_mtime; /* save the time */
+ if (now < lastcheck + every) return;
+ if (utime(filename, NULL) == -1)
+ log_uerror("utimes", filename, NULL, r->server);
+ }
+ files = make_array(r->pool, 100, sizeof(struct gc_ent *));
+ curblocks = 0;
+ curbytes = 0;
+
+ sub_garbage_coll(r,files,cachedir,"/");
+
+ if (curblocks < cachesize || curblocks + curbytes <= cachesize)
+ return;
+
+ qsort(files->elts, files->nelts, sizeof(struct gc_ent *), gcdiff);
+
+ elts = (struct gc_ent **)files->elts;
+ for (i=0; i < files->nelts; i++)
+ {
+ fent = elts[i];
+ sprintf(filename, "%s%s", cachedir, fent->file);
+ Explain3("GC Unlinking %s (expiry %ld, now %ld)",filename,fent->expire,now);
+#if TESTING
+ fprintf(stderr,"Would unlink %s\n",filename);
+#else
+ if (unlink(filename) == -1)
+ {
+ if (errno != ENOENT)
+ log_uerror("unlink", filename, NULL, r->server);
+ }
+ else
+#endif
+ {
+ curblocks -= fent->len >> 10;
+ curbytes -= fent->len & 0x3FF;
+ if (curbytes < 0)
+ {
+ curbytes += 1024;
+ curblocks--;
+ }
+ if (curblocks < cachesize || curblocks + curbytes <= cachesize)
+ break;
+ }
+ }
+ unblock_alarms();
+}
+
+static int sub_garbage_coll(request_rec *r,array_header *files,
+ const char *cachebasedir,const char *cachesubdir)
+{
+ char line[27];
+ char cachedir[HUGE_STRING_LEN];
+ struct stat buf;
+ int fd,i;
+ DIR *dir;
+#if defined(NEXT)
+ struct DIR_TYPE *ent;
+#else
+ struct dirent *ent;
+#endif
+ struct gc_ent *fent;
+ int nfiles=0;
+
+ sprintf(cachedir,"%s%s",cachebasedir,cachesubdir);
+ Explain1("GC Examining directory %s",cachedir);
+ dir = opendir(cachedir);
+ if (dir == NULL)
+ {
+ log_uerror("opendir", cachedir, NULL, r->server);
+ return 0;
+ }
+
+ while ((ent = readdir(dir)) != NULL)
+ {
+ if (ent->d_name[0] == '.') continue;
+ sprintf(filename, "%s%s", cachedir, ent->d_name);
+ Explain1("GC Examining file %s",filename);
+/* is it a temporary file? */
+ if (strncmp(ent->d_name, "#tmp", 4) == 0)
+ {
+/* then stat it to see how old it is; delete temporary files > 1 day old */
+ if (stat(filename, &buf) == -1)
+ {
+ if (errno != ENOENT)
+ log_uerror("stat", filename, NULL, r->server);
+ } else if (now != -1 && buf.st_atime < now - SEC_ONE_DAY &&
+ buf.st_mtime < now - SEC_ONE_DAY)
+ {
+ Explain1("GC unlink %s",filename);
+#if TESTING
+ fprintf(stderr,"Would unlink %s\n",filename);
+#else
+ unlink(filename);
+#endif
+ }
+ continue;
+ }
+ ++nfiles;
+/* is it another file? */
+ /* FIXME: Shouldn't any unexpected files be deleted? */
+ /* if (strlen(ent->d_name) != HASH_LEN) continue; */
+
+/* read the file */
+ fd = open(filename, O_RDONLY);
+ if (fd == -1)
+ {
+ if (errno != ENOENT) log_uerror("open", filename,NULL, r->server);
+ continue;
+ }
+ if (fstat(fd, &buf) == -1)
+ {
+ log_uerror("fstat", filename, NULL, r->server);
+ close(fd);
+ continue;
+ }
+ if(S_ISDIR(buf.st_mode))
+ {
+ char newcachedir[HUGE_STRING_LEN];
+ close(fd);
+ sprintf(newcachedir,"%s%s/",cachesubdir,ent->d_name);
+ if(!sub_garbage_coll(r,files,cachebasedir,newcachedir))
+ {
+ sprintf(newcachedir,"%s%s",cachedir,ent->d_name);
+#if TESTING
+ fprintf(stderr,"Would remove directory %s\n",newcachedir);
+#else
+ rmdir(newcachedir);
+#endif
+ --nfiles;
+ }
+ continue;
+ }
+
+ i = read(fd, line, 26);
+ if (i == -1)
+ {
+ log_uerror("read", filename, NULL, r->server);
+ close(fd);
+ continue;
+ }
+ close(fd);
+ line[i] = '\0';
+ expire = hex2sec(line+18);
+ if (!checkmask(line, "&&&&&&&& &&&&&&&& &&&&&&&&") || expire == -1)
+ {
+ /* bad file */
+ if (now != -1 && buf.st_atime > now + SEC_ONE_DAY &&
+ buf.st_mtime > now + SEC_ONE_DAY)
+ {
+ log_error("proxy: deleting bad cache file", r->server);
+#if TESTING
+ fprintf(stderr,"Would unlink bad file %s\n",filename);
+#else
+ unlink(filename);
+#endif
+ }
+ continue;
+ }
+
+/*
+ * we need to calculate an 'old' factor, and remove the 'oldest' files
+ * so that the space requirement is met; sort by the expires date of the
+ * file.
+ *
+ */
+ /* FIXME: We should make the array an array of gc_ents, not gc_ent *s
+ */
+ fent = palloc(r->pool, sizeof(struct gc_ent));
+ fent->len = buf.st_size;
+ fent->expire = expire;
+ strcpy(fent->file,cachesubdir);
+ strcat(fent->file, ent->d_name);
+ *(struct gc_ent **)push_array(files) = fent;
+
+/* accumulate in blocks, to cope with directories > 4Gb */
+ curblocks += buf.st_size >> 10; /* Kbytes */
+ curbytes += buf.st_size & 0x3FF;
+ if (curbytes >= 1024)
+ {
+ curbytes -= 1024;
+ curblocks++;
+ }
+ }
+
+ closedir(dir);
+
+ return nfiles;
+
+}
+
+/*
+ * read a cache file;
+ * returns 1 on success,
+ * 0 on failure (bad file or wrong URL)
+ * -1 on UNIX error
+ */
+static int
+rdcache(pool *pool, BUFF *cachefp, struct cache_req *c)
+{
+ char urlbuff[1034], *p;
+ int len;
+/* read the data from the cache file */
+/* format
+ * date SP lastmod SP expire SP count SP content-length CRLF
+ * dates are stored as hex seconds since 1970
+ */
+ len = bgets(urlbuff, 1034, cachefp);
+ if (len == -1) return -1;
+ if (len == 0 || urlbuff[len-1] != '\n') return 0;
+ urlbuff[len-1] = '\0';
+
+ if (!checkmask(urlbuff, "&&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&&"))
+ return 0;
+
+ c->date = hex2sec(urlbuff);
+ c->lmod = hex2sec(urlbuff+9);
+ c->expire = hex2sec(urlbuff+18);
+ c->version = hex2sec(urlbuff+27);
+ c->len = hex2sec(urlbuff+36);
+
+/* check that we have the same URL */
+ len = bgets(urlbuff, 1034, cachefp);
+ if (len == -1) return -1;
+ if (len == 0 || strncmp(urlbuff, "X-URL: ", 7) != 0 ||
+ urlbuff[len-1] != '\n')
+ return 0;
+ urlbuff[len-1] = '\0';
+ if (strcmp(urlbuff+7, c->url) != 0) return 0;
+
+/* What follows is the message */
+ len = bgets(urlbuff, 1034, cachefp);
+ if (len == -1) return -1;
+ if (len == 0 || urlbuff[len-1] != '\n') return 0;
+ urlbuff[--len] = '\0';
+
+ c->resp_line = pstrdup(pool, urlbuff);
+ p = strchr(urlbuff, ' ');
+ if (p == NULL) return 0;
+
+ c->status = atoi(p);
+ c->hdrs = read_headers(pool, urlbuff, 1034, cachefp);
+ if (c->hdrs == NULL) return -1;
+ if (c->len != -1) /* add a content-length header */
+ {
+ struct hdr_entry *q;
+ q = get_header(c->hdrs, "Content-Length");
+ if (q == NULL)
+ {
+ p = palloc(pool, 15);
+ sprintf(p, "%u", c->len);
+ add_header(c->hdrs, "Content-Length", p, HDR_REP);
+ }
+ }
+ return 1;
+}
+
+
+/*
+ * Call this to test for a resource in the cache
+ * Returns DECLINED if we need to check the remote host
+ * or an HTTP status code if successful
+ *
+ * Functions:
+ * if URL is cached then
+ * if cached file is not expired then
+ * if last modified after if-modified-since then send body
+ * else send 304 Not modified
+ * else
+ * if last modified after if-modified-since then add
+ * last modified date to request
+ */
+static int
+cache_check(request_rec *r, char *url, struct cache_conf *conf,
+ struct cache_req **cr)
+{
+ char hashfile[33], *imstr, *pragma, *p, *auth;
+ struct cache_req *c;
+ time_t now;
+ BUFF *cachefp;
+ int cfd, i;
+ const long int zero=0L;
+ void *sconf = r->server->module_config;
+ proxy_server_conf *pconf =
+ (proxy_server_conf *)get_module_config(sconf, &proxy_module);
+
+ c = pcalloc(r->pool, sizeof(struct cache_req));
+ *cr = c;
+ c->req = r;
+ c->url = pstrdup(r->pool, url);
+
+/* get the If-Modified-Since date of the request */
+ c->ims = -1;
+ imstr = table_get(r->headers_in, "If-Modified-Since");
+ if (imstr != NULL)
+ {
+/* this may modify the value in the original table */
+ imstr = date_canon(r->pool, imstr);
+ c->ims = parsedate(imstr, NULL);
+ if (c->ims == -1) /* bad or out of range date; remove it */
+ table_set(r->headers_in, "If-Modified-Since", NULL);
+ }
+
+/* find the filename for this cache entry */
+ hash(url, hashfile,pconf->cache.dirlevels,pconf->cache.dirlength);
+ if (conf->root != NULL)
+ c->filename = pstrcat(r->pool, conf->root, "/", hashfile, NULL);
+ else
+ c->filename = NULL;
+
+ cachefp = NULL;
+/* find out about whether the request can access the cache */
+ pragma = table_get(r->headers_in, "Pragma");
+ auth = table_get(r->headers_in, "Authorization");
+ Explain4("Request for %s, pragma=%s, auth=%s, ims=%ld",url,pragma,auth,c->ims);
+ if (c->filename != NULL && r->method_number == M_GET &&
+ strlen(url) < 1024 && !liststr(pragma, "no-cache") && auth == NULL)
+ {
+ Explain1("Check file %s",c->filename);
+ cfd = open(c->filename, O_RDWR);
+ if (cfd != -1)
+ {
+ note_cleanups_for_fd(r->pool, cfd);
+ cachefp = bcreate(r->pool, B_RD | B_WR);
+ bpushfd(cachefp, cfd, cfd);
+ } else if (errno != ENOENT)
+ log_uerror("open", c->filename, "proxy: error opening cache file",
+ r->server);
+ else
+ Explain1("File %s not found",c->filename);
+ }
+
+ if (cachefp != NULL)
+ {
+ i = rdcache(r->pool, cachefp, c);
+ if (i == -1)
+ log_uerror("read", c->filename, "proxy: error reading cache file",
+ r->server);
+ else if (i == 0)
+ log_error("proxy: bad cache file", r->server);
+ if (i != 1)
+ {
+ pclosef(r->pool, cachefp->fd);
+ cachefp = NULL;
+ }
+ }
+ if (cachefp == NULL)
+ c->hdrs = make_array(r->pool, 2, sizeof(struct hdr_entry));
+ /* FIXME: Shouldn't we check the URL somewhere? */
+ now = time(NULL);
+/* Ok, have we got some un-expired data? */
+ if (cachefp != NULL && c->expire != -1 && now < c->expire)
+ {
+ Explain0("Unexpired data available");
+/* check IMS */
+ if (c->lmod != -1 && c->ims != -1 && c->ims >= c->lmod)
+ {
+/* has the cached file changed since this request? */
+ if (c->date == -1 || c->date > c->ims)
+ {
+/* No, but these header values may have changed, so we send them with the
+ * 304 response
+ */
+ /* CHECKME: surely this was wrong? (Ben)
+ p = table_get(r->headers_in, "Expires");
+ */
+ p = table_get(c->hdrs, "Expires");
+ if (p != NULL) table_set(r->headers_out, "Expires", p);
+ }
+ pclosef(r->pool, cachefp->fd);
+ Explain0("Use local copy, cached file hasn't changed");
+ return USE_LOCAL_COPY;
+ }
+
+/* Ok, has been modified */
+ Explain0("Local copy modified, send it");
+ r->status_line = strchr(c->resp_line, ' ') + 1;
+ r->status = c->status;
+ soft_timeout ("send", r);
+ if (!r->assbackwards)
+ send_headers(r->connection->client, c->resp_line, c->hdrs);
+ bsetopt(r->connection->client, BO_BYTECT, &zero);
+ r->sent_bodyct = 1;
+ if (!r->header_only) send_fb (cachefp, r, NULL, NULL);
+ pclosef(r->pool, cachefp->fd);
+ return OK;
+ }
+
+/* if we already have data and a last-modified date, and it is not a head
+ * request, then add an If-Modified-Since
+ */
+
+ if (cachefp != NULL && c->lmod != -1 && !r->header_only)
+ {
+/*
+ * use the later of the one from the request and the last-modified date
+ * from the cache
+ */
+ if (c->ims == -1 || c->ims < c->lmod)
+ {
+ struct hdr_entry *q;
+
+ q = get_header(c->hdrs, "Last-Modified");
+
+ if (q != NULL && q->value != NULL)
+ table_set(r->headers_in, "If-Modified-Since",
+ (char *)q->value);
+ }
+ }
+ c->fp = cachefp;
+
+ Explain0("Local copy not present or expired. Declining.");
+
+ return DECLINED;
+}
+
+/*
+ * Having read the response from the client, decide what to do
+ * If the response is not cachable, then delete any previously cached
+ * response, and copy data from remote server to client.
+ * Functions:
+ * parse dates
+ * check for an uncachable response
+ * calculate an expiry date, if one is not provided
+ * if the remote file has not been modified, then return the document
+ * from the cache, maybe updating the header line
+ * otherwise, delete the old cached file and open a new temporary file
+ */
+static int
+cache_update(struct cache_req *c, array_header *resp_hdrs,
+ const char *protocol, int nocache)
+{
+ request_rec *r=c->req;
+ char *p;
+ int i;
+ struct hdr_entry *expire, *dates, *lmods, *clen;
+ time_t expc, date, lmod, now;
+ char buff[46];
+ void *sconf = r->server->module_config;
+ proxy_server_conf *conf =
+ (proxy_server_conf *)get_module_config(sconf, &proxy_module);
+ const long int zero=0L;
+
+ c->tempfile = NULL;
+
+/* we've received the response */
+/* read expiry date; if a bad date, then leave it so the client can
+ * read it
+ */
+ expire = get_header(resp_hdrs, "Expire");
+ if (expire != NULL) expc = parsedate(expire->value, NULL);
+ else expc = -1;
+
+/*
+ * read the last-modified date; if the date is bad, then delete it
+ */
+ lmods = get_header(resp_hdrs, "Last-Modified");
+ if (lmods != NULL)
+ {
+ lmod = parsedate(lmods->value, NULL);
+ if (lmod == -1)
+ {
+/* kill last modified date */
+ lmods->value = NULL;
+ lmods = NULL;
+ }
+ } else
+ lmod = -1;
+
+/*
+ * what responses should we not cache?
+ * Unknown status responses and those known to be uncacheable
+ * 304 response when we have no valid cache file, or
+ * 200 response from HTTP/1.0 and up without a Last-Modified header, or
+ * HEAD requests, or
+ * requests with an Authorization header, or
+ * protocol requests nocache (e.g. ftp with user/password)
+ */
+ if ((r->status != 200 && r->status != 301 && r->status != 304) ||
+ (expire != NULL && expc == -1) ||
+ (r->status == 304 && c->fp == NULL) ||
+ (r->status == 200 && lmods == NULL &&
+ strncmp(protocol, "HTTP/1.", 7) == 0) ||
+ r->header_only ||
+ table_get(r->headers_in, "Authorization") != NULL ||
+ nocache)
+ {
+ Explain1("Response is not cacheable, unlinking %s",c->filename);
+/* close the file */
+ if (c->fp != NULL)
+ {
+ pclosef(r->pool, c->fp->fd);
+ c->fp = NULL;
+ }
+/* delete the previously cached file */
+ unlink(c->filename);
+ return DECLINED; /* send data to client but not cache */
+ }
+
+/* otherwise, we are going to cache the response */
+/*
+ * Read the date. Generate one if one is not supplied
+ */
+ dates = get_header(resp_hdrs, "Date");
+ if (dates != NULL) date = parsedate(dates->value, NULL);
+ else date = -1;
+
+ now = time(NULL);
+
+ if (date == -1) /* No, or bad date */
+ {
+/* no date header! */
+/* add one; N.B. use the time _now_ rather than when we were checking the cache
+ */
+ date = now;
+ p = gm_timestr_822(r->pool, now);
+ dates = add_header(resp_hdrs, "Date", p, HDR_REP);
+ Explain0("Added date header");
+ }
+
+/* check last-modified date */
+ if (lmod != -1 && lmod > date)
+/* if its in the future, then replace by date */
+ {
+ lmod = date;
+ lmods->value = dates->value;
+ Explain0("Last modified is in the future, replacing with now");
+ }
+/* if the response did not contain the header, then use the cached version */
+ if (lmod == -1 && c->fp != NULL)
+ {
+ lmod = c->lmod;
+ Explain0("Reusing cached last modified");
+ }
+
+/* we now need to calculate the expire data for the object. */
+ if (expire == NULL && c->fp != NULL) /* no expiry data sent in response */
+ {
+ expire = get_header(c->hdrs, "Expires");
+ if (expire != NULL) expc = parsedate(expire->value, NULL);
+ }
+/* so we now have the expiry date */
+/* if no expiry date then
+ * if lastmod
+ * expiry date = now + min((date - lastmod) * factor, maxexpire)
+ * else
+ * expire date = now + defaultexpire
+ */
+ Explain1("Expiry date is %ld",expc);
+ if (expc == -1)
+ {
+ if (lmod != -1)
+ {
+ double x = (double)(date - lmod)*conf->cache.lmfactor;
+ double maxex=conf->cache.maxexpire;
+ if (x > maxex) x = maxex;
+ expc = now + (int)x;
+ } else
+ expc = now + conf->cache.defaultexpire;
+ Explain1("Expiry date calculated %ld",expc);
+ }
+
+/* get the content-length header */
+ clen = get_header(c->hdrs, "Content-Length");
+ if (clen == NULL) c->len = -1;
+ else c->len = atoi(clen->value);
+
+ sec2hex(date, buff);
+ buff[8] = ' ';
+ sec2hex(lmod, buff+9);
+ buff[17] = ' ';
+ sec2hex(expc, buff+18);
+ buff[26] = ' ';
+ sec2hex(c->version++, buff+27);
+ buff[35] = ' ';
+ sec2hex(c->len, buff+36);
+ buff[44] = '\n';
+ buff[45] = '\0';
+
+/* if file not modified */
+ if (r->status == 304)
+ {
+ if (c->ims != -1 && lmod != -1 && lmod <= c->ims)
+ {
+/* set any changed headers somehow */
+/* update dates and version, but not content-length */
+ if (lmod != c->lmod || expc != c->expire || date != c->date)
+ {
+ off_t curpos=lseek(c->fp->fd, 0, SEEK_SET);
+ if (curpos == -1)
+ log_uerror("lseek", c->filename,
+ "proxy: error seeking on cache file",r->server);
+ else if (write(c->fp->fd, buff, 35) == -1)
+ log_uerror("write", c->filename,
+ "proxy: error updating cache file", r->server);
+ }
+ pclosef(r->pool, c->fp->fd);
+ Explain0("Remote document not modified, use local copy");
+ /* CHECKME: Is this right? Shouldn't we check IMS again here? */
+ return USE_LOCAL_COPY;
+ } else
+ {
+/* return the whole document */
+ Explain0("Remote document updated, sending");
+ r->status_line = strchr(c->resp_line, ' ') + 1;
+ r->status = c->status;
+ soft_timeout ("send", r);
+ if (!r->assbackwards)
+ send_headers(r->connection->client, c->resp_line, c->hdrs);
+ bsetopt(r->connection->client, BO_BYTECT, &zero);
+ r->sent_bodyct = 1;
+ if (!r->header_only) send_fb (c->fp, r, NULL, NULL);
+/* set any changed headers somehow */
+/* update dates and version, but not content-length */
+ if (lmod != c->lmod || expc != c->expire || date != c->date)
+ {
+ off_t curpos=lseek(c->fp->fd, 0, SEEK_SET);
+
+ if (curpos == -1)
+ log_uerror("lseek", c->filename,
+ "proxy: error seeking on cache file",r->server);
+ else if (write(c->fp->fd, buff, 35) == -1)
+ log_uerror("write", c->filename,
+ "proxy: error updating cache file", r->server);
+ }
+ pclosef(r->pool, c->fp->fd);
+ return OK;
+ }
+ }
+/* new or modified file */
+ if (c->fp != NULL)
+ {
+ pclosef(r->pool, c->fp->fd);
+ c->fp->fd = -1;
+ }
+ c->version = 0;
+ sec2hex(0, buff+27);
+ buff[35] = ' ';
+
+/* open temporary file */
+#define TMPFILESTR "/#tmpXXXXXX"
+ c->tempfile=palloc(r->pool,strlen(conf->cache.root)+sizeof TMPFILESTR-1);
+ strcpy(c->tempfile,conf->cache.root);
+ /*
+ p = strrchr(c->tempfile, '/');
+ if (p == NULL) return DECLINED;
+ strcpy(p, TMPFILESTR);
+ */
+ strcat(c->tempfile,TMPFILESTR);
+#undef TMPFILESTR
+ p = mktemp(c->tempfile);
+ if (p == NULL) return DECLINED;
+
+ Explain1("Create temporary file %s",c->tempfile);
+
+ i = open(c->tempfile, O_WRONLY | O_CREAT | O_EXCL, 0622);
+ if (i == -1)
+ {
+ log_uerror("open", c->tempfile, "proxy: error creating cache file",
+ r->server);
+ return DECLINED;
+ }
+ note_cleanups_for_fd(r->pool, i);
+ c->fp = bcreate(r->pool, B_WR);
+ bpushfd(c->fp, -1, i);
+
+ if (bvputs(c->fp, buff, "X-URL: ", c->url, "\n", NULL) == -1)
+ {
+ log_uerror("write", c->tempfile, "proxy: error writing cache file",
+ r->server);
+ pclosef(r->pool, c->fp->fd);
+ unlink(c->tempfile);
+ c->fp = NULL;
+ }
+ return DECLINED;
+}
+
+static void
+cache_tidy(struct cache_req *c)
+{
+ server_rec *s=c->req->server;
+ long int bc;
+
+ if (c->fp == NULL) return;
+
+ bgetopt(c->req->connection->client, BO_BYTECT, &bc);
+
+ if (c->len != -1)
+ {
+/* file lengths don't match; don't cache it */
+ if (bc != c->len)
+ {
+ pclosef(c->req->pool, c->fp->fd); /* no need to flush */
+ unlink(c->tempfile);
+ return;
+ }
+ } else
+ {
+/* update content-length of file */
+ char buff[9];
+ off_t curpos;
+
+ c->len = bc;
+ bflush(c->fp);
+ sec2hex(c->len, buff);
+ curpos = lseek(c->fp->fd, 36, SEEK_SET);
+ if (curpos == -1)
+ log_uerror("lseek", c->tempfile,
+ "proxy: error seeking on cache file", s);
+ else if (write(c->fp->fd, buff, 8) == -1)
+ log_uerror("write", c->tempfile,
+ "proxy: error updating cache file", s);
+ }
+
+ if (bflush(c->fp) == -1)
+ {
+ log_uerror("write", c->tempfile, "proxy: error writing to cache file",
+ s);
+ pclosef(c->req->pool, c->fp->fd);
+ unlink(c->tempfile);
+ return;
+ }
+
+ if (pclosef(c->req->pool, c->fp->fd) == -1)
+ {
+ log_uerror("close", c->tempfile, "proxy: error closing cache file", s);
+ unlink(c->tempfile);
+ return;
+ }
+
+ if (unlink(c->filename) == -1 && errno != ENOENT)
+ {
+ log_uerror("unlink", c->filename,
+ "proxy: error deleting old cache file", s);
+ } else
+ {
+ char *p;
+ proxy_server_conf *conf=
+ (proxy_server_conf *)get_module_config(s->module_config,&proxy_module);
+
+ for(p=c->filename+strlen(conf->cache.root)+1 ; ; )
+ {
+ p=strchr(p,'/');
+ if(!p)
+ break;
+ *p='\0';
+ if(mkdir(c->filename,S_IREAD|S_IWRITE|S_IEXEC) < 0 && errno != EEXIST)
+ log_uerror("mkdir",c->filename,"proxy: error creating cache directory",s);
+ *p='/';
+ ++p;
+ }
+#ifdef __EMX__
+ /* Under OS/2 use rename. */
+ if (rename(c->tempfile, c->filename) == -1)
+ log_uerror("rename", c->filename, "proxy: error renaming cache file", s);
+}
+#else
+
+ if (link(c->tempfile, c->filename) == -1)
+ log_uerror("link", c->filename, "proxy: error linking cache file", s);
+ }
+
+ if (unlink(c->tempfile) == -1)
+ log_uerror("unlink", c->tempfile, "proxy: error deleting temp file",s);
+#endif
+
+ garbage_coll(c->req);
+}
+
+static BUFF *
+cache_error(struct cache_req *c)
+{
+ log_uerror("write", c->tempfile, "proxy: error writing to cache file",
+ c->req->server);
+ pclosef(c->req->pool, c->fp->fd);
+ c->fp = NULL;
+ unlink(c->tempfile);
+ return NULL;
+}
+
+static int
+proxy_handler(request_rec *r)
+{
+ char *url, *scheme, *lenp, *p;
+ void *sconf = r->server->module_config;
+ proxy_server_conf *conf =
+ (proxy_server_conf *)get_module_config(sconf, &proxy_module);
+ array_header *proxies=conf->proxies;
+ struct proxy_remote *ents=(struct proxy_remote *)proxies->elts;
+ int i, rc;
+ struct cache_req *cr;
+
+ if (strncmp(r->filename, "proxy:", 6) != 0) return DECLINED;
+
+ lenp = table_get (r->headers_in, "Content-length");
+ if ((r->method_number == M_POST || r->method_number == M_PUT)
+ && lenp == NULL)
+ return BAD_REQUEST;
+
+ url = r->filename + 6;
+ p = strchr(url, ':');
+ if (p == NULL) return BAD_REQUEST;
+
+ rc = cache_check(r, url, &conf->cache, &cr);
+ if (rc != DECLINED) return rc;
+
+ *p = '\0';
+ scheme = pstrdup(r->pool, url);
+ *p = ':';
+
+/* firstly, try a proxy */
+
+ for (i=0; i < proxies->nelts; i++)
+ {
+ p = strchr(ents[i].scheme, ':'); /* is it a partial URL? */
+ if (strcmp(ents[i].scheme, "*") == 0 ||
+ (p == NULL && strcmp(scheme, ents[i].scheme) == 0) ||
+ (p != NULL &&
+ strncmp(url, ents[i].scheme, strlen(ents[i].scheme)) == 0))
+ {
+/* we only know how to handle communication to a proxy via http */
+ if (strcmp(ents[i].protocol, "http") == 0)
+ rc = http_handler(r, cr, url, ents[i].hostname, ents[i].port);
+ else rc = DECLINED;
+
+ /* an error or success */
+ if (rc != DECLINED && rc != BAD_GATEWAY) return rc;
+ /* we failed to talk to the upstream proxy */
+ }
+ }
+
+/* otherwise, try it direct */
+/* N.B. what if we're behind a firewall, where we must use a proxy or
+ * give up??
+ */
+ /* handle the scheme */
+ if (r->method_number == M_CONNECT) return connect_handler(r, cr, url);
+ if (strcmp(scheme, "http") == 0) return http_handler(r, cr, url, NULL, 0);
+ if (strcmp(scheme, "ftp") == 0) return ftp_handler(r, cr, url);
+ else return NOT_IMPLEMENTED;
+}
+
+
+static int
+proxyerror(request_rec *r, const char *message)
+{
+ r->status = SERVER_ERROR;
+ r->status_line = "500 Proxy Error";
+ r->content_type = "text/html";
+
+ send_http_header(r);
+ rvputs(r, "\015\012\
+Proxy Error\015\012Proxy Error\
+
\015\012The proxy server could not handle this request.\
+\015\012\015\012Reason: ", message, "\015\012\015\012",
+ NULL);
+ return OK;
+}
+
+/*
+ * This routine returns its own error message
+ */
+static const char *
+host2addr(const char *host, struct in_addr *addr)
+{
+ int i;
+ unsigned long ipaddr;
+
+ for (i=0; host[i] != '\0'; i++)
+ if (!isdigit(host[i]) && host[i] != '.')
+ break;
+
+ if (host[i] != '\0')
+ {
+ struct hostent *hp;
+
+ hp = gethostbyname(host);
+ if (hp == NULL) return "Host not found";
+ memcpy(addr, hp->h_addr, sizeof(struct in_addr));
+ } else
+ {
+ if ((ipaddr = inet_addr(host)) == -1)
+ return "Bad IP address";
+ memcpy(addr, &ipaddr, sizeof(unsigned long));
+ }
+ return NULL;
+}
+
+/*
+ * Returns the ftp status code;
+ * or -1 on I/O error, 0 on data error
+ */
+int
+ftp_getrc(BUFF *f)
+{
+ int i, len, status;
+ char linebuff[100], buff[5];
+
+ len = bgets(linebuff, 100, f);
+ if (len == -1) return -1;
+/* check format */
+ if (len < 5 || !isdigit(linebuff[0]) || !isdigit(linebuff[1]) ||
+ !isdigit(linebuff[2]) || (linebuff[3] != ' ' && linebuff[3] != '-'))
+ return 0;
+ status = 100 * linebuff[0] + 10 * linebuff[1] + linebuff[2] - 111 * '0';
+
+ if (linebuff[len-1] != '\n')
+ {
+ i = bskiplf(f);
+ if (i != 1) return i;
+ }
+
+/* skip continuation lines */
+ if (linebuff[3] == '-')
+ {
+ memcpy(buff, linebuff, 3);
+ buff[3] = ' ';
+ do
+ {
+ len = bgets(linebuff, 100, f);
+ if (len == -1) return -1;
+ if (len < 5) return 0;
+ if (linebuff[len-1] != '\n')
+ {
+ i = bskiplf(f);
+ if (i != 1) return i;
+ }
+ } while (memcmp(linebuff, buff, 4) != 0);
+ }
+
+ return status;
+}
+
+static int
+doconnect(int sock, struct sockaddr_in *addr, request_rec *r)
+{
+ int i;
+
+ hard_timeout ("proxy connect", r);
+ do i = connect(sock, (struct sockaddr *)addr, sizeof(struct sockaddr_in));
+ while (i == -1 && errno == EINTR);
+ if (i == -1) log_uerror("connect", NULL, NULL, r->server);
+ kill_timeout(r);
+
+ return i;
+}
+
+/*
+ * Handles direct access of ftp:// URLs
+ */
+static int
+ftp_handler(request_rec *r, struct cache_req *c, char *url)
+{
+ char *host, *path, *p, *user, *password, *parms;
+ const char *err;
+ int port, userlen, passlen, i, len, sock, dsock, csd, rc, nocache;
+ struct sockaddr_in server;
+ struct hdr_entry *hdr;
+ array_header *resp_hdrs;
+ BUFF *f, *cache, *data;
+ pool *pool=r->pool;
+ const int one=1;
+ const long int zero=0L;
+
+/* we only support GET and HEAD */
+ if (r->method_number != M_GET) return NOT_IMPLEMENTED;
+
+ host = pstrdup(r->pool, url+6);
+/* We break the URL into host, port, path-search */
+ port = DEFAULT_FTP_PORT;
+ path = strchr(host, '/');
+ if (path == NULL) path = "";
+ else *(path++) = '\0';
+
+ user = password = NULL;
+ nocache = 0;
+ passlen=0; /* not actually needed, but it shuts the compiler up */
+ p = strchr(host, '@');
+ if (p != NULL)
+ {
+ (*p++) = '\0';
+ user = host;
+ host = p;
+/* find password */
+ p = strchr(user, ':');
+ if (p != NULL)
+ {
+ *(p++) = '\0';
+ password = p;
+ passlen = decodeenc(password);
+ }
+ userlen = decodeenc(user);
+ nocache = 1; /* don't cache when a username is supplied */
+ } else
+ {
+ user = "anonymous";
+ userlen = 9;
+
+ password = "proxy_user@host";
+ passlen = strlen(password);
+ }
+
+ p = strchr(host, ':');
+ if (p != NULL)
+ {
+ *(p++) = '\0';
+ port = atoi(p);
+ }
+
+ parms = strchr(path, ';');
+ if (parms != NULL) *(parms++) = '\0';
+
+ memset(&server,'\0',sizeof server);
+ server.sin_family=AF_INET;
+ server.sin_port = htons(port);
+ err = host2addr(host, &server.sin_addr);
+ if (err != NULL) return proxyerror(r, err); /* give up */
+
+ sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (sock == -1)
+ {
+ log_uerror("socket", NULL, "proxy: error creating socket", r->server);
+ return SERVER_ERROR;
+ }
+ note_cleanups_for_fd(pool, sock);
+
+ if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (const char *)&one,
+ sizeof(int)) == -1)
+ {
+ log_uerror("setsockopt", NULL, "proxy: error setting reuseaddr option",
+ r->server);
+ pclosef(pool, sock);
+ return SERVER_ERROR;
+ }
+
+ i = doconnect(sock, &server, r);
+ if (i == -1) return proxyerror(r, "Could not connect to remote machine");
+
+ f = bcreate(pool, B_RDWR);
+ bpushfd(f, sock, sock);
+/* shouldn't we implement telnet control options here? */
+
+/* possible results: 120, 220, 421 */
+ hard_timeout ("proxy ftp", r);
+ i = ftp_getrc(f);
+ if (i == -1) return proxyerror(r, "Error reading from remote server");
+ if (i != 220) return BAD_GATEWAY;
+
+ bputs("USER ", f);
+ bwrite(f, user, userlen);
+ bputs("\015\012", f);
+ bflush(f); /* capture any errors */
+
+/* possible results; 230, 331, 332, 421, 500, 501, 530 */
+/* states: 1 - error, 2 - success; 3 - send password, 4,5 fail */
+ i = ftp_getrc(f);
+ if (i == -1) return proxyerror(r, "Error sending to remote server");
+ if (i == 530) return FORBIDDEN;
+ else if (i != 230 && i != 331) return BAD_GATEWAY;
+
+ if (i == 331) /* send password */
+ {
+ if (password == NULL) return FORBIDDEN;
+ bputs("PASS ", f);
+ bwrite(f, password, passlen);
+ bputs("\015\012", f);
+ bflush(f);
+/* possible results 202, 230, 332, 421, 500, 501, 503, 530 */
+ i = ftp_getrc(f);
+ if (i == -1) return proxyerror(r, "Error sending to remote server");
+ if (i == 332 || i == 530) return FORBIDDEN;
+ else if (i != 230 && i != 202) return BAD_GATEWAY;
+ }
+
+/* set the directory */
+/* this is what we must do if we don't know the OS type of the remote
+ * machine
+ */
+ for (;;)
+ {
+ p = strchr(path, '/');
+ if (p == NULL) break;
+ *p = '\0';
+
+ len = decodeenc(path);
+ bputs("CWD ", f);
+ bwrite(f, path, len);
+ bputs("\015\012", f);
+ bflush(f);
+/* responses: 250, 421, 500, 501, 502, 530, 550 */
+/* 1,3 error, 2 success, 4,5 failure */
+ i = ftp_getrc(f);
+ if (i == -1) return proxyerror(r, "Error sending to remote server");
+ else if (i == 550) return NOT_FOUND;
+ else if (i != 250) return BAD_GATEWAY;
+
+ path = p + 1;
+ }
+
+ if (parms != NULL && strncmp(parms, "type=", 5) == 0)
+ {
+ parms += 5;
+ if ((parms[0] != 'd' && parms[0] != 'a' && parms[0] != 'i') ||
+ parms[1] != '\0') parms = "";
+ }
+ else parms = "";
+
+ if (parms[0] == 'i')
+ {
+ /* set type to image */
+ bputs("TYPE I", f);
+ bflush(f);
+/* responses: 200, 421, 500, 501, 504, 530 */
+ i = ftp_getrc(f);
+ if (i == -1) return proxyerror(r, "Error sending to remote server");
+ else if (i != 200 && i != 504) return BAD_GATEWAY;
+/* Allow not implemented */
+ else if (i == 504) parms[0] = '\0';
+ }
+
+/* set up data connection */
+ len = sizeof(struct sockaddr_in);
+ if (getsockname(sock, (struct sockaddr *)&server, &len) < 0)
+ {
+ log_uerror("getsockname", NULL,"proxy: error getting socket address",
+ r->server);
+ pclosef(pool, sock);
+ return SERVER_ERROR;
+ }
+
+ dsock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (dsock == -1)
+ {
+ log_uerror("socket", NULL, "proxy: error creating socket", r->server);
+ pclosef(pool, sock);
+ return SERVER_ERROR;
+ }
+ note_cleanups_for_fd(pool, dsock);
+
+ if (setsockopt(dsock, SOL_SOCKET, SO_REUSEADDR, (const char *)&one,
+ sizeof(int)) == -1)
+ {
+ log_uerror("setsockopt", NULL, "proxy: error setting reuseaddr option",
+ r->server);
+ pclosef(pool, dsock);
+ pclosef(pool, sock);
+ return SERVER_ERROR;
+ }
+
+ if (bind(dsock, (struct sockaddr *)&server, sizeof(struct sockaddr_in)) ==
+ -1)
+ {
+ char buff[22];
+
+ sprintf(buff, "%s:%d", inet_ntoa(server.sin_addr), server.sin_port);
+ log_uerror("bind", buff, "proxy: error binding to ftp data socket",
+ r->server);
+ pclosef(pool, sock);
+ pclosef(pool, dsock);
+ }
+ listen(dsock, 2); /* only need a short queue */
+
+/* set request */
+ len = decodeenc(path);
+ if (parms[0] == 'd')
+ {
+ if (len != 0) bputs("NLST ", f);
+ else bputs("NLST", f);
+ }
+ else bputs("RETR ", f);
+ bwrite(f, path, len);
+ bputs("\015\012", f);
+ bflush(f);
+/* RETR: 110, 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 530, 550
+ NLST: 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 502, 530 */
+ rc = ftp_getrc(f);
+ if (rc == -1) return proxyerror(r, "Error sending to remote server");
+ if (rc == 550) return NOT_FOUND;
+ if (rc != 125 && rc != 150 && rc != 226 && rc != 250) return BAD_GATEWAY;
+ kill_timeout(r);
+
+ r->status = 200;
+ r->status_line = "200 OK";
+
+ resp_hdrs = make_array(pool, 2, sizeof(struct hdr_entry));
+ if (parms[0] == 'd')
+ add_header(resp_hdrs, "Content-Type", "text/plain", HDR_REP);
+ i = cache_update(c, resp_hdrs, "FTP", nocache);
+ if (i != DECLINED)
+ {
+ pclosef(pool, dsock);
+ pclosef(pool, sock);
+ return i;
+ }
+ cache = c->fp;
+
+/* wait for connection */
+ hard_timeout ("proxy ftp data connect", r);
+ len = sizeof(struct sockaddr_in);
+ do csd = accept(dsock, (struct sockaddr *)&server, &len);
+ while (csd == -1 && errno == EINTR); /* SHUDDER on SOCKS - cdm */
+ if (csd == -1)
+ {
+ log_uerror("accept", NULL, "proxy: failed to accept data connection",
+ r->server);
+ pclosef(pool, dsock);
+ pclosef(pool, sock);
+ cache_error(c);
+ return BAD_GATEWAY;
+ }
+ note_cleanups_for_fd(pool, csd);
+ data = bcreate(pool, B_RD);
+ bpushfd(data, csd, -1);
+ kill_timeout(r);
+
+ hard_timeout ("proxy receive", r);
+/* send response */
+/* write status line */
+ if (!r->assbackwards)
+ rvputs(r, SERVER_PROTOCOL, " ", r->status_line, "\015\012", NULL);
+ if (cache != NULL)
+ if (bvputs(cache, SERVER_PROTOCOL, " ", r->status_line, "\015\012",
+ NULL) == -1)
+ cache = cache_error(c);
+
+/* send headers */
+ len = resp_hdrs->nelts;
+ hdr = (struct hdr_entry *)resp_hdrs->elts;
+ for (i=0; i < len; i++)
+ {
+ if (hdr[i].field == NULL || hdr[i].value == NULL ||
+ hdr[i].value[0] == '\0') continue;
+ if (!r->assbackwards)
+ rvputs(r, hdr[i].field, ": ", hdr[i].value, "\015\012", NULL);
+ if (cache != NULL)
+ if (bvputs(cache, hdr[i].field, ": ", hdr[i].value, "\015\012",
+ NULL) == -1)
+ cache = cache_error(c);
+ }
+
+ if (!r->assbackwards) rputs("\015\012", r);
+ if (cache != NULL)
+ if (bputs("\015\012", cache) == -1) cache = cache_error(c);
+
+ bsetopt(r->connection->client, BO_BYTECT, &zero);
+ r->sent_bodyct = 1;
+/* send body */
+ if (!r->header_only)
+ {
+ send_fb(data, r, cache, c);
+ if (rc == 125 || rc == 150) rc = ftp_getrc(f);
+ if (rc != 226 && rc != 250) cache_error(c);
+ }
+ else
+ {
+/* abort the transfer */
+ bputs("ABOR\015\012", f);
+ bflush(f);
+/* responses: 225, 226, 421, 500, 501, 502 */
+ i = ftp_getrc(f);
+ }
+
+ cache_tidy(c);
+
+/* finish */
+ bputs("QUIT\015\012", f);
+ bflush(f);
+/* responses: 221, 500 */
+
+ pclosef(pool, csd);
+ pclosef(pool, dsock);
+ pclosef(pool, sock);
+
+ return OK;
+}
+
+/*
+ * This handles Netscape CONNECT method secure proxy requests.
+ * A connection is opened to the specified host and data is
+ * passed through between the WWW site and the browser.
+ *
+ * This code is based on the INTERNET-DRAFT document
+ * "Tunneling SSL Through a WWW Proxy" currently at
+ * http://www.mcom.com/newsref/std/tunneling_ssl.html.
+ *
+ * FIXME: this is bad, because it does its own socket I/O
+ * instead of using the I/O in buff.c. However,
+ * the I/O in buff.c blocks on reads, and because
+ * this function doesn't know how much data will
+ * be sent either way (or when) it can't use blocking
+ * I/O. This may be very implementation-specific
+ * (to Linux). Any suggestions?
+ * FIXME: this doesn't log the number of bytes sent, but
+ * that may be okay, since the data is supposed to
+ * be transparent. In fact, this doesn't log at all
+ * yet. 8^)
+ * FIXME: doesn't check any headers initally sent from the
+ * client.
+ * FIXME: should allow authentication, but hopefully the
+ * generic proxy authentication is good enough.
+ * FIXME: no check for r->assbackwards, whatever that is.
+ */
+
+static int
+connect_handler(request_rec *r, struct cache_req *c, char *url)
+{
+ struct sockaddr_in server;
+ const char *host, *err;
+ char *p;
+ int port, sock;
+ char buffer[HUGE_STRING_LEN];
+ int nbytes, i;
+ fd_set fds;
+
+ memset(&server, '\0', sizeof(server));
+ server.sin_family=AF_INET;
+
+ /* Break the URL into host:port pairs */
+
+ host = url;
+ p = strchr(url, ':');
+ if (p==NULL) port = DEFAULT_HTTPS_PORT;
+ else
+ {
+ port = atoi(p+1);
+ *p='\0';
+ }
+
+ switch (port)
+ {
+ case DEFAULT_HTTPS_PORT:
+ case DEFAULT_SNEWS_PORT:
+ break;
+ default:
+ return SERVICE_UNAVAILABLE;
+ }
+
+ Explain2("CONNECT to %s on port %d", host, port);
+
+ server.sin_port = htons(port);
+ err = host2addr(host, &server.sin_addr);
+ if (err != NULL) return proxyerror(r, err); /* give up */
+
+ sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (sock == -1)
+ {
+ log_error("proxy: error creating socket", r->server);
+ return SERVER_ERROR;
+ }
+ note_cleanups_for_fd(r->pool, sock);
+
+ i = doconnect(sock, &server, r);
+ if (i == -1 )
+ return proxyerror(r, "Could not connect to remote machine");
+
+ Explain0("Returning 200 OK Status");
+
+ rvputs(r, "HTTP/1.0 200 Connection established\015\012", NULL);
+ rvputs(r, "Proxy-agent: ", SERVER_VERSION, "\015\012\015\012", NULL);
+ bflush(r->connection->client);
+
+ while (1) /* Infinite loop until error (one side closes the connection) */
+ {
+ FD_ZERO(&fds);
+ FD_SET(sock, &fds);
+ FD_SET(r->connection->client->fd, &fds);
+
+ Explain0("Going to sleep (select)");
+ i = select((r->connection->client->fd > sock ?
+ r->connection->client->fd+1 :
+#ifdef HPUX
+ sock+1), (int*)&fds, NULL, NULL, NULL);
+#else
+ sock+1), &fds, NULL, NULL, NULL);
+#endif
+ Explain1("Woke from select(), i=%d",i);
+
+ if (i)
+ {
+ if (FD_ISSET(sock, &fds))
+ {
+ Explain0("sock was set");
+ if((nbytes=read(sock,buffer,HUGE_STRING_LEN))!=0)
+ {
+ if(nbytes==-1) break;
+ if(write(r->connection->client->fd, buffer, nbytes)==EOF)break;
+ Explain1("Wrote %d bytes to client", nbytes);
+ }
+ else break;
+ }
+ else if (FD_ISSET(r->connection->client->fd, &fds))
+ {
+ Explain0("client->fd was set");
+ if((nbytes=read(r->connection->client->fd,buffer,
+ HUGE_STRING_LEN))!=0)
+ {
+ if(nbytes==-1) break;
+ if(write(sock,buffer,nbytes)==EOF) break;
+ Explain1("Wrote %d bytes to server", nbytes);
+ }
+ else break;
+ }
+ else break; /* Must be done waiting */
+ }
+ else break;
+ }
+
+ pclosef(r->pool,sock);
+
+ return OK;
+}
+
+/*
+ * This handles http:// URLs, and other URLs using a remote proxy over http
+ * If proxyhost is NULL, then contact the server directly, otherwise
+ * go via the proxy.
+ * Note that if a proxy is used, then URLs other than http: can be accessed,
+ * also, if we have trouble which is clearly specific to the proxy, then
+ * we return DECLINED so that we can try another proxy. (Or the direct
+ * route.)
+ */
+static int
+http_handler(request_rec *r, struct cache_req *c, char *url,
+ const char *proxyhost, int proxyport)
+{
+ char *p;
+ const char *err, *host;
+ int port, i, sock, len;
+ array_header *reqhdrs_arr, *resp_hdrs;
+ table_entry *reqhdrs;
+ struct sockaddr_in server;
+ BUFF *f, *cache;
+ struct hdr_entry *hdr;
+ char buffer[HUGE_STRING_LEN], inprotocol[9], outprotocol[9];
+ pool *pool=r->pool;
+ const long int zero=0L;
+
+ void *sconf = r->server->module_config;
+ proxy_server_conf *conf =
+ (proxy_server_conf *)get_module_config(sconf, &proxy_module);
+ struct nocache_entry *ent=(struct nocache_entry *)conf->nocaches->elts;
+ int nocache = 0;
+
+ memset(&server, '\0', sizeof(server));
+ server.sin_family = AF_INET;
+
+ if (proxyhost != NULL)
+ {
+ server.sin_port = htons(proxyport);
+ err = host2addr(proxyhost, &server.sin_addr);
+ if (err != NULL) return DECLINED; /* try another */
+ host = proxyhost;
+ } else
+ {
+ url += 7; /* skip http:// */
+/* We break the URL into host, port, path-search */
+ port = DEFAULT_PORT;
+ p = strchr(url, '/');
+ if (p == NULL)
+ {
+ host = pstrdup(pool, url);
+ url = "/";
+ } else
+ {
+ char *q = palloc(pool, p-url+1);
+ memcpy(q, url, p-url);
+ q[p-url] = '\0';
+ url = p;
+ host = q;
+ }
+
+ p = strchr(host, ':');
+ if (p != NULL)
+ {
+ *(p++) = '\0';
+ port = atoi(p);
+ }
+ server.sin_port = htons(port);
+ err = host2addr(host, &server.sin_addr);
+ if (err != NULL) return proxyerror(r, err); /* give up */
+ }
+
+ sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (sock == -1)
+ {
+ log_error("proxy: error creating socket", r->server);
+ return SERVER_ERROR;
+ }
+ note_cleanups_for_fd(pool, sock);
+
+ i = doconnect(sock, &server, r);
+ if (i == -1)
+ {
+ if (proxyhost != NULL) return DECLINED; /* try again another way */
+ else return proxyerror(r, "Could not connect to remote machine");
+ }
+
+ f = bcreate(pool, B_RDWR);
+ bpushfd(f, sock, sock);
+
+ hard_timeout ("proxy send", r);
+ bvputs(f, r->method, " ", url, " HTTP/1.0\015\012", NULL);
+
+ reqhdrs_arr = table_elts (r->headers_in);
+ reqhdrs = (table_entry *)reqhdrs_arr->elts;
+ for (i=0; i < reqhdrs_arr->nelts; i++)
+ {
+ if (reqhdrs[i].key == NULL || reqhdrs[i].val == NULL) continue;
+ if (!strcasecmp(reqhdrs[i].key, "Connection")) continue;
+ bvputs(f, reqhdrs[i].key, ": ", reqhdrs[i].val, "\015\012", NULL);
+ }
+
+ bputs("\015\012", f);
+/* send the request data, if any. N.B. should we trap SIGPIPE ? */
+
+ if (r->method_number == M_POST || r->method_number == M_PUT)
+ {
+ len = atoi(table_get (r->headers_in, "Content-length"));
+
+ while (len > 0)
+ {
+ i = len;
+ if (i > HUGE_STRING_LEN) i = HUGE_STRING_LEN;
+
+ i = read_client_block(r, buffer, i);
+ bwrite(f, buffer, i);
+
+ len -= i;
+ }
+ }
+ bflush(f);
+ kill_timeout(r);
+
+ hard_timeout ("proxy receive", r);
+
+ len = bgets(buffer, HUGE_STRING_LEN-1, f);
+ if (len == -1 || len == 0)
+ {
+ pclosef(pool, sock);
+ return proxyerror(r, "Error reading from remote server");
+ }
+
+/* Is it an HTTP/1 response? */
+ if (checkmask(buffer, "HTTP/#.# ### *"))
+ {
+/* If not an HTTP/1 messsage or if the status line was > 8192 bytes */
+ if (buffer[5] != '1' || buffer[len-1] != '\n')
+ {
+ pclosef(pool, sock);
+ return BAD_GATEWAY;
+ }
+ buffer[--len] = '\0';
+ memcpy(inprotocol, buffer, 8);
+ inprotocol[8] = '\0';
+
+/* we use the same protocol on output as on input */
+ strcpy(outprotocol, inprotocol);
+ buffer[12] = '\0';
+ r->status = atoi(&buffer[9]);
+ buffer[12] = ' ';
+ r->status_line = pstrdup(pool, &buffer[9]);
+
+/* read the headers. */
+/* N.B. for HTTP/1.0 clients, we have to fold line-wrapped headers */
+/* Also, take care with headers with multiple occurences. */
+
+ resp_hdrs = read_headers(pool, buffer, HUGE_STRING_LEN, f);
+ } else
+ {
+/* an http/0.9 response */
+ strcpy(inprotocol, "HTTP/0.9");
+ strcpy(outprotocol, "HTTP/1.0");
+ r->status = 200;
+ r->status_line = "200 OK";
+
+/* no headers */
+ resp_hdrs = make_array(pool, 2, sizeof(struct hdr_entry));
+ }
+
+ kill_timeout(r);
+
+/*
+ * HTTP/1.0 requires us to accept 3 types of dates, but only generate
+ * one type
+ */
+
+ len = resp_hdrs->nelts;
+ hdr = (struct hdr_entry *)resp_hdrs->elts;
+ for (i=0; i < len; i++)
+ {
+ if (hdr[i].value[0] == '\0') continue;
+ p = hdr[i].field;
+ if (strcasecmp(p, "Date") == 0 ||
+ strcasecmp(p, "Last-Modified") == 0 ||
+ strcasecmp(p, "Expires") == 0)
+ hdr[i].value = date_canon(pool, hdr[i].value);
+ }
+
+/* check if NoCache directive on this host */
+ for (i=0; i < conf->nocaches->nelts; i++)
+ {
+ if (ent[i].name != NULL && strstr(host, ent[i].name) != NULL)
+ nocache = 1;
+ }
+
+ i = cache_update(c, resp_hdrs, inprotocol, nocache);
+ if (i != DECLINED)
+ {
+ pclosef(pool, sock);
+ return i;
+ }
+
+ cache = c->fp;
+
+ hard_timeout ("proxy receive", r);
+
+/* write status line */
+ if (!r->assbackwards)
+ rvputs(r, "HTTP/1.0 ", r->status_line, "\015\012", NULL);
+ if (cache != NULL)
+ if (bvputs(cache, outprotocol, " ", r->status_line, "\015\012", NULL)
+ == -1)
+ cache = cache_error(c);
+
+/* send headers */
+ len = resp_hdrs->nelts;
+ for (i=0; i < len; i++)
+ {
+ if (hdr[i].field == NULL || hdr[i].value == NULL ||
+ hdr[i].value[0] == '\0') continue;
+ if (!r->assbackwards)
+ rvputs(r, hdr[i].field, ": ", hdr[i].value, "\015\012", NULL);
+ if (cache != NULL)
+ if (bvputs(cache, hdr[i].field, ": ", hdr[i].value, "\015\012",
+ NULL) == -1)
+ cache = cache_error(c);
+ }
+
+ if (!r->assbackwards) rputs("\015\012", r);
+ if (cache != NULL)
+ if (bputs("\015\012", cache) == -1) cache = cache_error(c);
+
+ bsetopt(r->connection->client, BO_BYTECT, &zero);
+ r->sent_bodyct = 1;
+/* Is it an HTTP/0.9 respose? If so, send the extra data */
+ if (strcmp(inprotocol, "HTTP/0.9") == 0)
+ {
+ bwrite(r->connection->client, buffer, len);
+ if (cache != NULL)
+ if (bwrite(f, buffer, len) != len) cache = cache_error(c);
+ }
+
+/* send body */
+/* if header only, then cache will be NULL */
+/* HTTP/1.0 tells us to read to EOF, rather than content-length bytes */
+ if (!r->header_only) send_fb(f, r, cache, c);
+
+ cache_tidy(c);
+
+ pclosef(pool, sock);
+
+ return OK;
+}
+
+static handler_rec proxy_handlers[] = {
+{ "proxy-server", proxy_handler },
+{ NULL }
+};
+
+
+static void *
+create_proxy_config(pool *p, server_rec *s)
+{
+ proxy_server_conf *ps = pcalloc(p, sizeof(proxy_server_conf));
+
+ ps->proxies = make_array(p, 10, sizeof(struct proxy_remote));
+ ps->aliases = make_array(p, 10, sizeof(struct proxy_alias));
+ ps->nocaches = make_array(p, 10, sizeof(struct nocache_entry));
+ ps->req = 0;
+
+ ps->cache.root = NULL;
+ ps->cache.space = DEFAULT_CACHE_SPACE;
+ ps->cache.maxexpire = DEFAULT_CACHE_MAXEXPIRE;
+ ps->cache.defaultexpire = DEFAULT_CACHE_EXPIRE;
+ ps->cache.lmfactor = DEFAULT_CACHE_LMFACTOR;
+ ps->cache.gcinterval = -1;
+ /* at these levels, the cache can have 2^18 directories (256,000) */
+ ps->cache.dirlevels=3;
+ ps->cache.dirlength=1;
+
+ return ps;
+}
+
+static char *
+add_proxy(cmd_parms *cmd, void *dummy, char *f, char *r)
+{
+ server_rec *s = cmd->server;
+ proxy_server_conf *conf =
+ (proxy_server_conf *)get_module_config(s->module_config,&proxy_module);
+ struct proxy_remote *new;
+ char *p, *q;
+ int port;
+
+ p = strchr(r, ':');
+ if (p == NULL || p[1] != '/' || p[2] != '/' || p[3] == '\0')
+ return "Bad syntax for a remote proxy server";
+ q = strchr(p + 3, ':');
+ if (q != NULL)
+ {
+ if (sscanf(q+1, "%u", &port) != 1 || port > 65535)
+ return "Bad syntax for a remote proxy server (bad port number)";
+ *q = '\0';
+ } else port = -1;
+ *p = '\0';
+ if (strchr(f, ':') == NULL) str_tolower(f); /* lowercase scheme */
+ str_tolower(p + 3); /* lowercase hostname */
+
+ if (port == -1)
+ {
+ int i;
+ for (i=0; defports[i].scheme != NULL; i++)
+ if (strcmp(defports[i].scheme, r) == 0) break;
+ port = defports[i].port;
+ }
+
+ new = push_array (conf->proxies);
+ new->scheme = f;
+ new->protocol = r;
+ new->hostname = p + 3;
+ new->port = port;
+ return NULL;
+}
+
+static char *
+add_pass(cmd_parms *cmd, void *dummy, char *f, char *r)
+{
+ server_rec *s = cmd->server;
+ proxy_server_conf *conf =
+ (proxy_server_conf *)get_module_config(s->module_config,&proxy_module);
+ struct proxy_alias *new;
+
+ new = push_array (conf->aliases);
+ new->fake = f;
+ new->real = r;
+ return NULL;
+}
+
+static char *
+set_proxy_req(cmd_parms *parms, void *dummy, int flag)
+{
+ proxy_server_conf *psf =
+ get_module_config (parms->server->module_config, &proxy_module);
+
+ psf->req = flag;
+ return NULL;
+}
+
+
+static char *
+set_cache_size(cmd_parms *parms, char *struct_ptr, char *arg)
+{
+ proxy_server_conf *psf =
+ get_module_config (parms->server->module_config, &proxy_module);
+ int val;
+
+ if (sscanf(arg, "%d", &val) != 1) return "Value must be an integer";
+ psf->cache.space = val;
+ return NULL;
+}
+
+static char *
+set_cache_root(cmd_parms *parms, void *dummy, char *arg)
+{
+ proxy_server_conf *psf =
+ get_module_config (parms->server->module_config, &proxy_module);
+
+ psf->cache.root = arg;
+
+ return NULL;
+}
+
+static char *
+set_cache_factor(cmd_parms *parms, void *dummy, char *arg)
+{
+ proxy_server_conf *psf =
+ get_module_config (parms->server->module_config, &proxy_module);
+ double val;
+
+ if (sscanf(arg, "%lg", &val) != 1) return "Value must be a float";
+ psf->cache.lmfactor = val;
+
+ return NULL;
+}
+
+static char *
+set_cache_maxex(cmd_parms *parms, void *dummy, char *arg)
+{
+ proxy_server_conf *psf =
+ get_module_config (parms->server->module_config, &proxy_module);
+ double val;
+
+ if (sscanf(arg, "%lg", &val) != 1) return "Value must be a float";
+ psf->cache.maxexpire = (int)(val * (double)SEC_ONE_HR);
+ return NULL;
+}
+
+static char *
+set_cache_defex(cmd_parms *parms, void *dummy, char *arg)
+{
+ proxy_server_conf *psf =
+ get_module_config (parms->server->module_config, &proxy_module);
+ double val;
+
+ if (sscanf(arg, "%lg", &val) != 1) return "Value must be a float";
+ psf->cache.defaultexpire = (int)(val * (double)SEC_ONE_HR);
+ return NULL;
+}
+
+static char *
+set_cache_gcint(cmd_parms *parms, void *dummy, char *arg)
+{
+ proxy_server_conf *psf =
+ get_module_config (parms->server->module_config, &proxy_module);
+ double val;
+
+ if (sscanf(arg, "%lg", &val) != 1) return "Value must be a float";
+ psf->cache.gcinterval = (int)(val * (double)SEC_ONE_HR);
+ return NULL;
+}
+
+static char *
+set_cache_dirlevels(cmd_parms *parms, char *struct_ptr, char *arg)
+{
+ proxy_server_conf *psf =
+ get_module_config (parms->server->module_config, &proxy_module);
+ int val;
+
+ if (sscanf(arg, "%d", &val) != 1) return "Value must be an integer";
+ psf->cache.dirlevels = val;
+ return NULL;
+}
+
+static char *
+set_cache_dirlength(cmd_parms *parms, char *struct_ptr, char *arg)
+{
+ proxy_server_conf *psf =
+ get_module_config (parms->server->module_config, &proxy_module);
+ int val;
+
+ if (sscanf(arg, "%d", &val) != 1) return "Value must be an integer";
+ psf->cache.dirlength = val;
+ return NULL;
+}
+
+static char *
+set_cache_exclude(cmd_parms *parms, void *dummy, char *arg)
+{
+ server_rec *s = parms->server;
+ proxy_server_conf *conf =
+ get_module_config (s->module_config, &proxy_module);
+ struct nocache_entry *new;
+ struct nocache_entry *list=(struct nocache_entry*)conf->nocaches->elts;
+ int found = 0;
+ int i;
+
+ /* Don't duplicate entries */
+ for (i=0; i < conf->nocaches->nelts; i++)
+ {
+ if (strcmp(arg, list[i].name) == 0)
+ found = 1;
+ }
+
+ if (!found)
+ {
+ new = push_array (conf->nocaches);
+ new->name = arg;
+ }
+ return NULL;
+}
+
+static command_rec proxy_cmds[] = {
+{ "ProxyRequests", set_proxy_req, NULL, RSRC_CONF, FLAG,
+ "on if the true proxy requests should be accepted"},
+{ "ProxyRemote", add_proxy, NULL, RSRC_CONF, TAKE2,
+ "a scheme, partial URL or '*' and a proxy server"},
+{ "ProxyPass", add_pass, NULL, RSRC_CONF, TAKE2,
+ "a virtual path and a URL"},
+{ "CacheRoot", set_cache_root, NULL, RSRC_CONF, TAKE1,
+ "The directory to store cache files"},
+{ "CacheSize", set_cache_size, NULL, RSRC_CONF, TAKE1,
+ "The maximum disk space used by the cache in Kb"},
+{ "CacheMaxExpire", set_cache_maxex, NULL, RSRC_CONF, TAKE1,
+ "The maximum time in hours to cache a document"},
+{ "CacheDefaultExpire", set_cache_defex, NULL, RSRC_CONF, TAKE1,
+ "The default time in hours to cache a document"},
+{ "CacheLastModifiedFactor", set_cache_factor, NULL, RSRC_CONF, TAKE1,
+ "The factor used to estimate Expires date from LastModified date"},
+{ "CacheGcInterval", set_cache_gcint, NULL, RSRC_CONF, TAKE1,
+ "The interval between garbage collections, in hours"},
+{ "CacheDirLevels", set_cache_dirlevels, NULL, RSRC_CONF, TAKE1,
+ "The number of levels of subdirectories in the cache" },
+{ "CacheDirLength", set_cache_dirlength, NULL, RSRC_CONF, TAKE1,
+ "The number of characters in subdirectory names" },
+{ "NoCache", set_cache_exclude, NULL, RSRC_CONF, ITERATE,
+ "A list of hosts or domains for which caching is *not* provided" },
+{ NULL }
+};
+
+
+module proxy_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ NULL, /* create per-directory config structure */
+ NULL, /* merge per-directory config structures */
+ create_proxy_config, /* create per-server config structure */
+ NULL, /* merge per-server config structures */
+ proxy_cmds, /* command table */
+ proxy_handlers, /* handlers */
+ proxy_trans, /* translate_handler */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ proxy_fixup, /* pre-run fixups */
+ NULL /* logger */
+};
diff --git a/RELEASE_1_1_X/src/modules/standard/mod_access.c b/RELEASE_1_1_X/src/modules/standard/mod_access.c
new file mode 100644
index 00000000000..2d91de2ff66
--- /dev/null
+++ b/RELEASE_1_1_X/src/modules/standard/mod_access.c
@@ -0,0 +1,251 @@
+
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+
+/*
+ * Security options etc.
+ *
+ * Module derived from code originally written by Rob McCool
+ *
+ */
+
+#include "httpd.h"
+#include "http_core.h"
+#include "http_config.h"
+#include "http_log.h"
+
+typedef struct {
+ char *from;
+ int limited;
+} allowdeny;
+
+/* things in the 'order' array */
+#define DENY_THEN_ALLOW 0
+#define ALLOW_THEN_DENY 1
+#define MUTUAL_FAILURE 2
+
+typedef struct {
+ int order[METHODS];
+ array_header *allows;
+ array_header *denys;
+} access_dir_conf;
+
+module access_module;
+
+void *create_access_dir_config (pool *p, char *dummy)
+{
+ access_dir_conf *conf =
+ (access_dir_conf *)pcalloc(p, sizeof(access_dir_conf));
+ int i;
+
+ for (i = 0; i < METHODS; ++i) conf->order[i] = DENY_THEN_ALLOW;
+ conf->allows = make_array (p, 1, sizeof (allowdeny));
+ conf->denys = make_array (p, 1, sizeof (allowdeny));
+
+ return (void *)conf;
+}
+
+char *order (cmd_parms *cmd, void *dv, char *arg)
+{
+ access_dir_conf *d = (access_dir_conf *)dv;
+ int i, order;
+
+ if (!strcasecmp (arg, "allow,deny")) order = ALLOW_THEN_DENY;
+ else if (!strcasecmp (arg, "deny,allow")) order = DENY_THEN_ALLOW;
+ else if (!strcasecmp (arg, "mutual-failure")) order = MUTUAL_FAILURE;
+ else return "unknown order";
+
+ for (i = 0; i < METHODS; ++i)
+ if (cmd->limited & (1 << i))
+ d->order[i] = order;
+
+ return NULL;
+}
+
+char *allow_cmd (cmd_parms *cmd, void *dv, char *from, char *where)
+{
+ access_dir_conf *d = (access_dir_conf *)dv;
+ allowdeny *a;
+
+ if (strcasecmp (from, "from"))
+ return "allow and deny must be followed by 'from'";
+
+ a = (allowdeny *)push_array (cmd->info ? d->allows : d->denys);
+ a->from = pstrdup (cmd->pool, where);
+ a->limited = cmd->limited;
+ return NULL;
+}
+
+static char its_an_allow;
+
+command_rec access_cmds[] = {
+{ "order", order, NULL, OR_LIMIT, TAKE1,
+ "'allow,deny', 'deny,allow', or 'mutual-failure'" },
+{ "allow", allow_cmd, &its_an_allow, OR_LIMIT, ITERATE2,
+ "'from' followed by hostnames or IP-address wildcards" },
+{ "deny", allow_cmd, NULL, OR_LIMIT, ITERATE2,
+ "'from' followed by hostnames or IP-address wildcards" },
+{NULL}
+};
+
+int in_domain(const char *domain, const char *what) {
+ int dl=strlen(domain);
+ int wl=strlen(what);
+
+ if((wl-dl) >= 0) {
+ if (strcmp(domain,&what[wl-dl]) != 0) return 0;
+
+ /* Make sure we matched an *entire* subdomain --- if the user
+ * said 'allow from good.com', we don't want people from nogood.com
+ * to be able to get in.
+ */
+
+ if (wl == dl) return 1; /* matched whole thing */
+ else return (domain[0] == '.' || what[wl - dl - 1] == '.');
+ } else
+ return 0;
+}
+
+int in_ip(char *domain, char *what) {
+
+ /* Check a similar screw case to the one checked above ---
+ * "allow from 204.26.2" shouldn't let in people from 204.26.23
+ */
+
+ int l = strlen(domain);
+ if (strncmp(domain,what,l) != 0) return 0;
+ if (domain[l - 1] == '.') return 1;
+ return (what[l] == '\0' || what[l] == '.');
+}
+
+int find_allowdeny (request_rec *r, array_header *a, int method)
+{
+ allowdeny *ap = (allowdeny *)a->elts;
+ int mmask = (1 << method);
+ int i, gothost=0;
+ const char *remotehost=NULL;
+
+ for (i = 0; i < a->nelts; ++i) {
+ if (!(mmask & ap[i].limited))
+ continue;
+ if (!strcmp (ap[i].from, "all"))
+ return 1;
+ if (!gothost)
+ {
+ remotehost = get_remote_host(r->connection, r->per_dir_config,
+ REMOTE_HOST);
+ gothost = 1;
+ }
+ if (remotehost != NULL && isalpha(remotehost[0]))
+ if (in_domain(ap[i].from, remotehost))
+ return 1;
+ if (in_ip (ap[i].from, r->connection->remote_ip))
+ return 1;
+ }
+
+ return 0;
+}
+
+int check_dir_access (request_rec *r)
+{
+ int method = r->method_number;
+ access_dir_conf *a =
+ (access_dir_conf *)
+ get_module_config (r->per_dir_config, &access_module);
+ int ret = OK;
+
+ if (a->order[method] == ALLOW_THEN_DENY) {
+ ret = FORBIDDEN;
+ if (find_allowdeny (r, a->allows, method))
+ ret = OK;
+ if (find_allowdeny (r, a->denys, method))
+ ret = FORBIDDEN;
+ } else if (a->order[method] == DENY_THEN_ALLOW) {
+ if (find_allowdeny (r, a->denys, method))
+ ret = FORBIDDEN;
+ if (find_allowdeny (r, a->allows, method))
+ ret = OK;
+ }
+ else {
+ if (find_allowdeny(r, a->allows, method)
+ && !find_allowdeny(r, a->denys, method))
+ ret = OK;
+ else
+ ret = FORBIDDEN;
+ }
+
+ if (ret == FORBIDDEN)
+ log_reason ("Client denied by server configuration", r->filename, r);
+
+ return ret;
+}
+
+
+
+module access_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ create_access_dir_config, /* dir config creater */
+ NULL, /* dir merger --- default is to override */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ access_cmds,
+ NULL, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ check_dir_access, /* check access */
+ NULL, /* type_checker */
+ NULL, /* fixups */
+ NULL /* logger */
+};
diff --git a/RELEASE_1_1_X/src/modules/standard/mod_actions.c b/RELEASE_1_1_X/src/modules/standard/mod_actions.c
new file mode 100644
index 00000000000..c2868608a6a
--- /dev/null
+++ b/RELEASE_1_1_X/src/modules/standard/mod_actions.c
@@ -0,0 +1,212 @@
+
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+/*
+ * mod_actions.c: executes scripts based on MIME type
+ *
+ * by Alexei Kosut; based on mod_cgi.c, mod_mime.c and mod_includes.c,
+ * adapted by rst from original NCSA code by Rob McCool
+ *
+ * Usage instructions:
+ *
+ * Action mime/type /cgi-bin/script
+ *
+ * will activate /cgi-bin/script when a file of content type mime/type. It
+ * sends the URL and file path of the requested document using the standard
+ * CGI PATH_INFO and PATH_TRANSLATED environment variables.
+ *
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_main.h"
+#include "http_log.h"
+#include "util_script.h"
+
+typedef struct {
+ table *action_types; /* Added with Action... */
+ char *get; /* Added with Script GET */
+ char *post; /* Added with Script POST */
+ char *put; /* Added with Script PUT */
+ char *delete; /* Added with Script DELETE */
+} action_dir_config;
+
+module action_module;
+
+void *create_action_dir_config (pool *p, char *dummy)
+{
+ action_dir_config *new =
+ (action_dir_config *) palloc (p, sizeof(action_dir_config));
+
+ new->action_types = make_table (p, 4);
+ new->get = NULL;
+ new->post = NULL;
+ new->put = NULL;
+ new->delete = NULL;
+
+ return new;
+}
+
+void *merge_action_dir_configs (pool *p, void *basev, void *addv)
+{
+ action_dir_config *base = (action_dir_config *)basev;
+ action_dir_config *add = (action_dir_config *)addv;
+ action_dir_config *new =
+ (action_dir_config *)palloc (p, sizeof(action_dir_config));
+
+ new->action_types = overlay_tables (p, add->action_types,
+ base->action_types);
+
+ new->get = add->get ? add->get : base->get;
+ new->post = add->post ? add->post : base->post;
+ new->put = add->put ? add->put : base->put;
+ new->delete = add->delete ? add->delete : base->delete;
+
+ return new;
+}
+
+char *add_action(cmd_parms *cmd, action_dir_config *m, char *type, char *script)
+{
+ table_set (m->action_types, type, script);
+ return NULL;
+}
+
+char *set_script (cmd_parms *cmd, action_dir_config *m, char *method,
+ char *script)
+{
+ if (!strcmp(method, "GET"))
+ m->get = pstrdup(cmd->pool, script);
+ else if (!strcmp(method, "POST"))
+ m->post = pstrdup(cmd->pool, script);
+ else if (!strcmp(method, "PUT"))
+ m->put = pstrdup(cmd->pool, script);
+ else if (!strcmp(method, "DELETE"))
+ m->delete = pstrdup(cmd->pool, script);
+ else
+ return "Unknown method type for Script";
+
+ return NULL;
+}
+
+command_rec action_cmds[] = {
+{ "Action", add_action, NULL, OR_FILEINFO, TAKE2,
+ "a media type followed by a script name" },
+{ "Script", set_script, NULL, ACCESS_CONF|RSRC_CONF, TAKE2,
+ "a method followed by a script name" },
+{ NULL }
+};
+
+int action_handler (request_rec *r)
+{
+ action_dir_config *conf =
+ (action_dir_config *)get_module_config(r->per_dir_config,&action_module);
+ char *t, *action = r->handler ? r->handler : r->content_type;
+ char *script = NULL;
+
+ /* First, check for the method-handling scripts */
+ if ((r->method_number == M_GET) && r->args && conf->get)
+ script = conf->get;
+ else if ((r->method_number == M_POST) && conf->post)
+ script = conf->post;
+ else if ((r->method_number == M_PUT) && conf->put)
+ script = conf->put;
+ else if ((r->method_number == M_DELETE) && conf->delete)
+ script = conf->delete;
+
+ /* Check for looping, which can happen if the CGI script isn't */
+ if (script && r->prev && r->prev->prev)
+ return DECLINED;
+
+ /* Second, check for actions (which override the method scripts) */
+ if ((action || default_type(r)) && (t = table_get(conf->action_types,
+ action ? action : default_type(r)))) {
+ script = t;
+ if (r->finfo.st_mode == 0) {
+ log_reason("File does not exist", r->filename, r);
+ return NOT_FOUND;
+ }
+ }
+
+ if (script == NULL)
+ return DECLINED;
+
+ internal_redirect_handler(pstrcat(r->pool, script, escape_uri(r->pool,
+ r->uri), r->args ? "?" : NULL, r->args, NULL), r);
+ return OK;
+}
+
+handler_rec action_handlers[] = {
+{ "*/*", action_handler },
+{ NULL }
+};
+
+module action_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ create_action_dir_config, /* dir config creater */
+ merge_action_dir_configs, /* dir merger --- default is to override */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ action_cmds, /* command table */
+ action_handlers, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* fixups */
+ NULL /* logger */
+};
diff --git a/RELEASE_1_1_X/src/modules/standard/mod_alias.c b/RELEASE_1_1_X/src/modules/standard/mod_alias.c
new file mode 100644
index 00000000000..2e7b89f6c78
--- /dev/null
+++ b/RELEASE_1_1_X/src/modules/standard/mod_alias.c
@@ -0,0 +1,259 @@
+
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+
+/*
+ * http_alias.c: Stuff for dealing with directory aliases
+ *
+ * Original by Rob McCool, rewritten in succession by David Robinson
+ * and rst.
+ *
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+
+typedef struct {
+ char *real;
+ char *fake;
+ char *handler;
+} alias_entry;
+
+typedef struct {
+ array_header *aliases;
+ array_header *redirects;
+} alias_server_conf;
+
+module alias_module;
+
+void *create_alias_config (pool *p, server_rec *s)
+{
+ alias_server_conf *a =
+ (alias_server_conf *)pcalloc (p, sizeof(alias_server_conf));
+
+ a->aliases = make_array (p, 20, sizeof(alias_entry));
+ a->redirects = make_array (p, 20, sizeof(alias_entry));
+ return a;
+}
+
+void *merge_alias_config (pool *p, void *basev, void *overridesv)
+{
+ alias_server_conf *a =
+ (alias_server_conf *)pcalloc (p, sizeof(alias_server_conf));
+ alias_server_conf *base = (alias_server_conf *)basev,
+ *overrides = (alias_server_conf *)overridesv;
+
+ a->aliases = append_arrays (p, overrides->aliases, base->aliases);
+ a->redirects = append_arrays (p, overrides->redirects, base->redirects);
+ return a;
+}
+
+char *add_alias(cmd_parms *cmd, void *dummy, char *f, char *r)
+{
+ server_rec *s = cmd->server;
+ alias_server_conf *conf =
+ (alias_server_conf *)get_module_config(s->module_config,&alias_module);
+ alias_entry *new = push_array (conf->aliases);
+
+ /* XX r can NOT be relative to DocumentRoot here... compat bug. */
+
+ new->fake = f; new->real = r; new->handler = cmd->info;
+ return NULL;
+}
+
+char *add_redirect(cmd_parms *cmd, void *dummy, char *f, char *url)
+{
+ server_rec *s = cmd->server;
+ alias_server_conf *conf =
+ (alias_server_conf *)get_module_config(s->module_config,&alias_module);
+ alias_entry *new = push_array (conf->redirects);
+
+ if (!is_url (url)) return "Redirect to non-URL";
+
+ new->fake = f; new->real = url;
+ return NULL;
+}
+
+command_rec alias_cmds[] = {
+{ "Alias", add_alias, NULL, RSRC_CONF, TAKE2,
+ "a fakename and a realname"},
+{ "ScriptAlias", add_alias, "cgi-script", RSRC_CONF, TAKE2,
+ "a fakename and a realname"},
+{ "Redirect", add_redirect, NULL, OR_FILEINFO, TAKE2,
+ "a document to be redirected, then the destination URL" },
+{ NULL }
+};
+
+int alias_matches (char *uri, char *alias_fakename)
+{
+ char *end_fakename = alias_fakename + strlen (alias_fakename);
+ char *aliasp = alias_fakename, *urip = uri;
+
+ while (aliasp < end_fakename) {
+ if (*aliasp == '/') {
+ /* any number of '/' in the alias matches any number in
+ * the supplied URI, but there must be at least one...
+ */
+ if (*urip != '/') return 0;
+
+ while (*aliasp == '/') ++ aliasp;
+ while (*urip == '/') ++ urip;
+ }
+ else {
+ /* Other characters are compared literally */
+ if (*urip++ != *aliasp++) return 0;
+ }
+ }
+
+ /* Check last alias path component matched all the way */
+
+ if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/')
+ return 0;
+
+ /* Return number of characters from URI which matched (may be
+ * greater than length of alias, since we may have matched
+ * doubled slashes)
+ */
+
+ return urip - uri;
+}
+
+char *try_alias_list (request_rec *r, array_header *aliases, int doesc)
+{
+ alias_entry *entries = (alias_entry *)aliases->elts;
+ int i;
+
+ for (i = 0; i < aliases->nelts; ++i) {
+ alias_entry *p = &entries[i];
+ int l = alias_matches (r->uri, p->fake);
+
+ if (l > 0) {
+ if (p->handler) { /* Set handler, and leave a note for mod_cgi */
+ r->handler = pstrdup(r->pool, p->handler);
+ table_set (r->notes, "alias-forced-type", p->handler);
+ }
+
+ if (doesc) {
+ char *escurl;
+ escurl = os_escape_path(r->pool, r->uri + l, 1);
+ return pstrcat(r->pool, p->real, escurl, NULL);
+ } else
+ return pstrcat(r->pool, p->real, r->uri + l, NULL);
+ }
+ }
+
+ return NULL;
+}
+
+int translate_alias_redir(request_rec *r)
+{
+ void *sconf = r->server->module_config;
+ alias_server_conf *conf =
+ (alias_server_conf *)get_module_config(sconf, &alias_module);
+ char *ret;
+
+#ifdef __EMX__
+ /* Add support for OS/2 drive names */
+ if ((r->uri[0] != '/' && r->uri[0] != '\0') && r->uri[1] != ':')
+#else
+ if (r->uri[0] != '/' && r->uri[0] != '\0')
+#endif
+ return BAD_REQUEST;
+
+ if ((ret = try_alias_list (r, conf->redirects, 1)) != NULL) {
+ table_set (r->headers_out, "Location", ret);
+ return REDIRECT;
+ }
+
+ if ((ret = try_alias_list (r, conf->aliases, 0)) != NULL) {
+ r->filename = ret;
+ return OK;
+ }
+
+ return DECLINED;
+}
+
+int fixup_redir(request_rec *r)
+{
+ void *sconf = r->server->module_config;
+ alias_server_conf *conf =
+ (alias_server_conf *)get_module_config(sconf, &alias_module);
+ char *ret;
+
+ /* It may have changed since last time, so try again */
+
+ if ((ret = try_alias_list (r, conf->redirects, 1)) != NULL) {
+ table_set (r->headers_out, "Location", ret);
+ return REDIRECT;
+ }
+
+ return DECLINED;
+}
+
+module alias_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ NULL, /* dir config creater */
+ NULL, /* dir merger --- default is to override */
+ create_alias_config, /* server config */
+ merge_alias_config, /* merge server configs */
+ alias_cmds, /* command table */
+ NULL, /* handlers */
+ translate_alias_redir, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ fixup_redir, /* fixups */
+ NULL /* logger */
+};
diff --git a/RELEASE_1_1_X/src/modules/standard/mod_asis.c b/RELEASE_1_1_X/src/modules/standard/mod_asis.c
new file mode 100644
index 00000000000..a429ccab0a1
--- /dev/null
+++ b/RELEASE_1_1_X/src/modules/standard/mod_asis.c
@@ -0,0 +1,128 @@
+
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_protocol.h"
+#include "http_log.h"
+#include "util_script.h"
+#include "http_main.h"
+#include "http_request.h"
+
+int asis_handler (request_rec *r)
+{
+ FILE *f;
+ char *location;
+
+ if (r->method_number != M_GET) return DECLINED;
+ if (r->finfo.st_mode == 0) {
+ log_reason("File does not exist", r->filename, r);
+ return NOT_FOUND;
+ }
+
+ f = fopen (r->filename, "r");
+
+ if (f == NULL) {
+ log_reason("file permissions deny server access", r->filename, r);
+ return FORBIDDEN;
+ }
+
+ scan_script_header (r, f);
+ location = table_get (r->headers_out, "Location");
+
+ if (location && location[0] == '/' &&
+ (r->status == 200 || r->status == 301 || r->status == 302)) {
+
+ r->status = 200; /* Assume 200 status on whatever we're pointing to */
+
+ /* This redirect needs to be a GET no matter what the original
+ * method was.
+ */
+ r->method = pstrdup(r->pool, "GET");
+ r->method_number = M_GET;
+
+ internal_redirect_handler (location, r);
+ return OK;
+ }
+
+ soft_timeout ("send", r);
+ send_http_header (r);
+ if (!r->header_only) send_fd (f, r);
+ fclose (f);
+ return OK;
+}
+
+handler_rec asis_handlers[] = {
+{ ASIS_MAGIC_TYPE, asis_handler },
+{ "send-as-is", asis_handler },
+{ NULL }
+};
+
+module asis_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ NULL, /* create per-directory config structure */
+ NULL, /* merge per-directory config structures */
+ NULL, /* create per-server config structure */
+ NULL, /* merge per-server config structures */
+ NULL, /* command table */
+ asis_handlers, /* handlers */
+ NULL, /* translate_handler */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* pre-run fixups */
+ NULL /* logger */
+};
diff --git a/RELEASE_1_1_X/src/modules/standard/mod_auth.c b/RELEASE_1_1_X/src/modules/standard/mod_auth.c
new file mode 100644
index 00000000000..7c628c8093b
--- /dev/null
+++ b/RELEASE_1_1_X/src/modules/standard/mod_auth.c
@@ -0,0 +1,267 @@
+
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+
+/*
+ * http_auth: authentication
+ *
+ * Rob McCool
+ *
+ * Adapted to Shambhala by rst.
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "http_protocol.h"
+
+typedef struct auth_config_struct {
+ char *auth_pwfile;
+ char *auth_grpfile;
+} auth_config_rec;
+
+void *create_auth_dir_config (pool *p, char *d)
+{
+ return pcalloc (p, sizeof(auth_config_rec));
+}
+
+command_rec auth_cmds[] = {
+{ "AuthUserFile", set_string_slot,
+ (void*)XtOffsetOf(auth_config_rec,auth_pwfile), OR_AUTHCFG, TAKE1, NULL },
+{ "AuthGroupFile", set_string_slot,
+ (void*)XtOffsetOf(auth_config_rec,auth_grpfile), OR_AUTHCFG, TAKE1, NULL },
+{ NULL }
+};
+
+module auth_module;
+
+char *get_pw(request_rec *r, char *user, char *auth_pwfile)
+{
+ FILE *f;
+ char l[MAX_STRING_LEN];
+ char *rpw, *w;
+
+ if(!(f=pfopen(r->pool, auth_pwfile, "r"))) {
+ log_reason ("Could not open password file", auth_pwfile, r);
+ return NULL;
+ }
+ while(!(cfg_getline(l,MAX_STRING_LEN,f))) {
+ if((l[0] == '#') || (!l[0])) continue;
+ rpw = l;
+ w = getword(r->pool, &rpw, ':');
+
+ if(!strcmp(user,w)) {
+ pfclose(r->pool, f);
+ return pstrdup (r->pool, rpw);
+ }
+ }
+ pfclose(r->pool, f);
+ return NULL;
+}
+
+table *groups_for_user (pool *p, char *user, char *grpfile) {
+ FILE *f;
+ table *grps = make_table (p, 15);
+ pool *sp;
+ char l[MAX_STRING_LEN];
+ char *group_name, *ll, *w;
+
+ if(!(f=pfopen(p, grpfile, "r")))
+ return NULL;
+
+ sp = make_sub_pool (p);
+
+ while(!(cfg_getline(l,MAX_STRING_LEN,f))) {
+ if((l[0] == '#') || (!l[0])) continue;
+ ll = l;
+ clear_pool (sp);
+
+ group_name = getword(sp, &ll, ':');
+
+ while(ll[0]) {
+ w = getword_conf (sp, &ll);
+ if(!strcmp(w,user)) {
+ table_set (grps, group_name, "in");
+ break;
+ }
+ }
+ }
+ pfclose(p, f);
+ destroy_pool (sp);
+ return grps;
+}
+
+/* These functions return 0 if client is OK, and proper error status
+ * if not... either AUTH_REQUIRED, if we made a check, and it failed, or
+ * SERVER_ERROR, if things are so totally confused that we couldn't
+ * figure out how to tell if the client is authorized or not.
+ *
+ * If they return DECLINED, and all other modules also decline, that's
+ * treated by the server core as a configuration error, logged and
+ * reported as such.
+ */
+
+/* Determine user ID, and check if it really is that user, for HTTP
+ * basic authentication...
+ */
+
+int authenticate_basic_user (request_rec *r)
+{
+ auth_config_rec *sec =
+ (auth_config_rec *)get_module_config (r->per_dir_config, &auth_module);
+ conn_rec *c = r->connection;
+ char *sent_pw, *real_pw;
+ char errstr[MAX_STRING_LEN];
+ int res;
+
+ if ((res = get_basic_auth_pw (r, &sent_pw))) return res;
+
+ if(!sec->auth_pwfile)
+ return DECLINED;
+
+ if (!(real_pw = get_pw(r, c->user, sec->auth_pwfile))) {
+ sprintf(errstr,"user %s not found",c->user);
+ log_reason (errstr, r->uri, r);
+ note_basic_auth_failure (r);
+ return AUTH_REQUIRED;
+ }
+ /* anyone know where the prototype for crypt is? */
+ if(strcmp(real_pw,(char *)crypt(sent_pw,real_pw))) {
+ sprintf(errstr,"user %s: password mismatch",c->user);
+ log_reason (errstr, r->uri, r);
+ note_basic_auth_failure (r);
+ return AUTH_REQUIRED;
+ }
+ return OK;
+}
+
+/* Checking ID */
+
+int check_user_access (request_rec *r) {
+ auth_config_rec *sec =
+ (auth_config_rec *)get_module_config (r->per_dir_config, &auth_module);
+ char *user = r->connection->user;
+ int m = r->method_number;
+ int method_restricted = 0;
+ register int x;
+ char *t, *w;
+ table *grpstatus;
+ array_header *reqs_arr = requires (r);
+ require_line *reqs;
+
+ /* BUG FIX: tadc, 11-Nov-1995. If there is no "requires" directive,
+ * then any user will do.
+ */
+ if (!reqs_arr)
+ return (OK);
+ reqs = (require_line *)reqs_arr->elts;
+
+ if(sec->auth_grpfile)
+ grpstatus = groups_for_user (r->pool, user, sec->auth_grpfile);
+ else
+ grpstatus = NULL;
+
+ for(x=0; x < reqs_arr->nelts; x++) {
+
+ if (! (reqs[x].method_mask & (1 << m))) continue;
+
+ method_restricted = 1;
+
+ t = reqs[x].requirement;
+ w = getword(r->pool, &t, ' ');
+ if(!strcmp(w,"valid-user"))
+ return OK;
+ if(!strcmp(w,"user")) {
+ while(t[0]) {
+ w = getword_conf (r->pool, &t);
+ if(!strcmp(user,w))
+ return OK;
+ }
+ }
+ else if(!strcmp(w,"group")) {
+ if(!grpstatus)
+ return DECLINED; /* DBM group? Something else? */
+
+ while(t[0]) {
+ w = getword_conf(r->pool, &t);
+ if(table_get (grpstatus, w))
+ return OK;
+ }
+ }
+ }
+
+ if (!method_restricted)
+ return OK;
+
+ note_basic_auth_failure (r);
+ return AUTH_REQUIRED;
+}
+
+module auth_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ create_auth_dir_config, /* dir config creater */
+ NULL, /* dir merger --- default is to override */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ auth_cmds, /* command table */
+ NULL, /* handlers */
+ NULL, /* filename translation */
+ authenticate_basic_user, /* check_user_id */
+ check_user_access, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* fixups */
+ NULL /* logger */
+};
diff --git a/RELEASE_1_1_X/src/modules/standard/mod_auth_anon.c b/RELEASE_1_1_X/src/modules/standard/mod_auth_anon.c
new file mode 100644
index 00000000000..49b736a8c51
--- /dev/null
+++ b/RELEASE_1_1_X/src/modules/standard/mod_auth_anon.c
@@ -0,0 +1,298 @@
+
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+
+/*
+ * http_auth: authentication
+ *
+ * Rob McCool & Brian Behlendorf.
+ *
+ * Adapted to Shambhala by rst.
+ *
+ * Version 0.5 May 1996
+ *
+ * Brutally raped by Dirk.vanGulik@jrc.it to
+ *
+ * Adapted to allow anonymous logins, just like with Anon-FTP, when
+ * one gives the magic user name 'anonymous' and ones email address
+ * as the password.
+ *
+ * Just add the following tokes to your setup:
+ *
+ * Anonymous magic-user-id [magic-user-id]...
+ *
+ * Anonymous_MustGiveEmail [ on | off ] default = off
+ * Anonymous_LogEmail [ on | off ] default = on
+ * Anonymous_VerifyEmail [ on | off ] default = off
+ * Anonymous_NoUserId [ on | off ] default = off
+ * Anonymous_Authorative [ on | off ] default = off
+ *
+ * The magic user id is something like 'anonymous', it is NOT case sensitive.
+ *
+ * The MustGiveEmail flag can be used to force users to enter something
+ * in the password field (like an email address). Default is off.
+ *
+ * Furthermore the 'NoUserID' flag can be set to allow completely empty
+ * usernames in as well; this can be is convenient as a single return
+ * in broken GUIs like W95 is often given by the user. The Default is off.
+ *
+ * Dirk.vanGulik@jrc.it; http://ewse.ceo.org; http://me-www.jrc.it/~dirkx
+ *
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "http_protocol.h"
+
+typedef struct auth_anon {
+ char *password;
+ struct auth_anon * next;
+ } auth_anon;
+
+typedef struct {
+
+ auth_anon *auth_anon_passwords;
+ int auth_anon_nouserid;
+ int auth_anon_logemail;
+ int auth_anon_verifyemail;
+ int auth_anon_mustemail;
+ int auth_anon_authorative;
+
+} anon_auth_config_rec;
+
+void *create_anon_auth_dir_config (pool *p, char *d)
+{
+ anon_auth_config_rec * sec = (anon_auth_config_rec *)
+ pcalloc (p, sizeof(anon_auth_config_rec));
+
+ if (!sec) return NULL; /* no memory... */
+
+ /* just to illustrate the defaults really. */
+ sec -> auth_anon_passwords =NULL;
+
+ sec -> auth_anon_nouserid =0;
+ sec -> auth_anon_logemail =1;
+ sec -> auth_anon_verifyemail =0;
+ sec -> auth_anon_mustemail =1;
+ sec -> auth_anon_authorative =0;
+ return sec;
+}
+
+char *anon_set_passwd_flag (cmd_parms *cmd,
+ anon_auth_config_rec *sec, int arg) {
+ sec->auth_anon_mustemail=arg;
+ return NULL;
+}
+
+char *anon_set_userid_flag (cmd_parms *cmd,
+ anon_auth_config_rec *sec, int arg) {
+ sec->auth_anon_nouserid=arg;
+ return NULL;
+}
+char *anon_set_logemail_flag (cmd_parms *cmd,
+ anon_auth_config_rec *sec, int arg) {
+ sec->auth_anon_logemail=arg;
+ return NULL;
+}
+char *anon_set_verifyemail_flag (cmd_parms *cmd,
+ anon_auth_config_rec *sec, int arg) {
+ sec->auth_anon_verifyemail=arg;
+ return NULL;
+}
+char *anon_set_authorative_flag (cmd_parms *cmd,
+ anon_auth_config_rec *sec, int arg) {
+ sec->auth_anon_authorative=arg;
+ return NULL;
+}
+
+char *anon_set_string_slots (cmd_parms *cmd,
+ anon_auth_config_rec *sec, char *arg) {
+
+ auth_anon * first;
+
+ if (!(*arg))
+ return "Anonymous string cannot be empty, use Anonymous_NoUserId instead";
+
+ /* squeeze in a record */
+ first = sec->auth_anon_passwords;
+
+ if (
+ (!(sec->auth_anon_passwords=(auth_anon *) palloc(cmd -> pool, sizeof(auth_anon)))) ||
+ (!(sec->auth_anon_passwords->password = pstrdup(cmd -> pool, arg)))
+ ) return "Failed to claim memory for an anonymous password...";
+
+ /* and repair the next */
+ sec->auth_anon_passwords->next = first;
+
+ return NULL;
+}
+
+command_rec anon_auth_cmds[] = {
+{ "Anonymous", anon_set_string_slots,
+ NULL,OR_AUTHCFG, ITERATE, NULL },
+{ "Anonymous_MustGiveEmail", anon_set_passwd_flag, NULL, OR_AUTHCFG, FLAG,
+ "Limited to 'on' or 'off'" },
+{ "Anonymous_NoUserId", anon_set_userid_flag, NULL, OR_AUTHCFG, FLAG,
+ "Limited to 'on' or 'off'" },
+{ "Anonymous_VerifyEmail", anon_set_verifyemail_flag, NULL, OR_AUTHCFG, FLAG,
+ "Limited to 'on' or 'off'" },
+{ "Anonymous_LogEmail", anon_set_logemail_flag, NULL, OR_AUTHCFG, FLAG,
+ "Limited to 'on' or 'off'" },
+{ "Anonymous_Authorative", anon_set_authorative_flag, NULL, OR_AUTHCFG, FLAG,
+ "Limited to 'on' or 'off'" },
+
+{ NULL }
+};
+
+module anon_auth_module;
+
+int anon_authenticate_basic_user (request_rec *r)
+{
+ anon_auth_config_rec *sec =
+ (anon_auth_config_rec *)get_module_config (r->per_dir_config,
+ &anon_auth_module);
+ conn_rec *c = r->connection;
+ char *send_pw;
+ char errstr[MAX_STRING_LEN];
+ int res=DECLINED;
+
+
+ if ((res=get_basic_auth_pw (r,&send_pw)))
+ return res;
+
+ /* Ignore if we are not configured */
+ if (!sec->auth_anon_passwords) return DECLINED;
+
+ /* Do we allow an empty userID and/or is it the magic one
+ */
+
+ if ( (!(c->user[0])) && (sec->auth_anon_nouserid) ) {
+ res=OK;
+ } else {
+ auth_anon *p=sec->auth_anon_passwords;
+ res=DECLINED;
+ while ((res == DECLINED) && (p !=NULL)) {
+ if (!(strcasecmp(c->user,p->password)))
+ res=OK;
+ p=p->next;
+ }
+ }
+ if (
+ /* username is OK */
+ (res == OK) &&
+ /* password been filled out ? */
+ ( (!sec->auth_anon_mustemail) || strlen(send_pw) ) &&
+ /* does the password look like an email address ? */
+ ( (!sec->auth_anon_verifyemail) ||
+ (strpbrk("@",send_pw) != NULL) ||
+ (strpbrk(".",send_pw) != NULL)
+ )
+ ) {
+ if (sec->auth_anon_logemail) {
+ sprintf(errstr,"Anonymous: Passwd <%s> Accepted",
+ send_pw ? send_pw : "\'none\'");
+ log_error (errstr, r->server );
+ }
+ return OK;
+ } else {
+ if (sec->auth_anon_authorative) {
+ sprintf(errstr,"Anonymous: Authorative, Passwd <%s> not accepted",
+ send_pw ? send_pw : "\'none\'");
+ log_error(errstr,r->server);
+ return AUTH_REQUIRED;
+ }
+ /* Drop out the bottom to return DECLINED */
+ }
+
+
+ return DECLINED;
+}
+
+int check_anon_access (request_rec *r) {
+
+#ifdef NOTYET
+ conn_rec *c = r->connection;
+ anon_auth_config_rec *sec =
+ (anon_auth_config_rec *)get_module_config (r->per_dir_config,
+ &anon_auth_module);
+
+ if (!sec->auth_anon) return DECLINED;
+
+ if ( strcasecmp(r->connection->user,sec->auth_anon ))
+ return DECLINED;
+
+ return OK;
+#endif
+ return DECLINED;
+}
+
+
+module anon_auth_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ create_anon_auth_dir_config, /* dir config creater */
+ NULL, /* dir merger ensure strictness */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ anon_auth_cmds, /* command table */
+ NULL, /* handlers */
+ NULL, /* filename translation */
+ anon_authenticate_basic_user,/* check_user_id */
+ check_anon_access, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* fixups */
+ NULL /* logger */
+};
diff --git a/RELEASE_1_1_X/src/modules/standard/mod_auth_db.c b/RELEASE_1_1_X/src/modules/standard/mod_auth_db.c
new file mode 100644
index 00000000000..34dd2fec14b
--- /dev/null
+++ b/RELEASE_1_1_X/src/modules/standard/mod_auth_db.c
@@ -0,0 +1,267 @@
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+
+/*
+ * mod_auth_db: authentication
+ *
+ * Original work by Rob McCool & Brian Behlendorf.
+ *
+ * Adapted to Apache by rst (mod_auth_dbm)
+ *
+ * Adapted for Berkeley DB by Andrew Cohen
+ *
+ * mod_auth_db was based on mod_auth_dbm.
+ *
+ * Warning, this is not a drop in replacement for mod_auth_dbm,
+ * for people wanting to switch from dbm to Berkeley DB.
+ * It requires the use of AuthDBUserFile and AuthDBGroupFile
+ * instead of AuthDBMUserFile AuthDBMGroupFile
+ *
+ * Also, in the configuration file you need to specify
+ * db_auth_module rather than dbm_auth_module
+ *
+ * On some BSD systems (e.g. FreeBSD and NetBSD) dbm is automatically
+ * mapped to Berkeley DB. You can use either mod_auth_dbm or
+ * mod_auth_db. The latter makes it more obvious that it's Berkeley.
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "http_protocol.h"
+#include
+
+typedef struct {
+
+ char *auth_dbpwfile;
+ char *auth_dbgrpfile;
+
+} db_auth_config_rec;
+
+void *create_db_auth_dir_config (pool *p, char *d)
+{
+ return pcalloc (p, sizeof(db_auth_config_rec));
+}
+
+command_rec db_auth_cmds[] = {
+{ "AuthDBUserFile", set_string_slot,
+ (void*)XtOffsetOf(db_auth_config_rec, auth_dbpwfile),
+ OR_AUTHCFG, TAKE1, NULL },
+{ "AuthDBGroupFile", set_string_slot,
+ (void*)XtOffsetOf(db_auth_config_rec, auth_dbgrpfile),
+ OR_AUTHCFG, TAKE1, NULL },
+{ NULL }
+};
+
+module db_auth_module;
+
+char *get_db_pw(request_rec *r, char *user, const char *auth_dbpwfile) {
+ DB *f;
+ DBT d, q;
+ char *pw = NULL;
+
+ q.data = user;
+ q.size = strlen(q.data);
+
+ if(!(f=dbopen(auth_dbpwfile,O_RDONLY,0664,DB_HASH,NULL))) {
+ log_reason ("could not open db auth file", (char *) auth_dbpwfile, r);
+ return NULL;
+ }
+
+ if (!((f->get)(f,&q,&d,0))) {
+ pw = palloc (r->pool, d.size + 1);
+ strncpy(pw,d.data,d.size);
+ pw[d.size] = '\0'; /* Terminate the string */
+ }
+
+ (f->close)(f);
+ return pw;
+}
+
+/* We do something strange with the group file. If the group file
+ * contains any : we assume the format is
+ * key=username value=":"groupname [":"anything here is ignored]
+ * otherwise we now (0.8.14+) assume that the format is
+ * key=username value=groupname
+ * The first allows the password and group files to be the same
+ * physical DB file; key=username value=password":"groupname[":"anything]
+ *
+ * mark@telescope.org, 22Sep95
+ */
+
+char *get_db_grp(request_rec *r, char *user, const char *auth_dbgrpfile) {
+ char *grp_data = get_db_pw (r, user, auth_dbgrpfile);
+ char *grp_colon; char *grp_colon2;
+
+ if (grp_data == NULL) return NULL;
+
+ if ((grp_colon = strchr(grp_data, ':'))!=NULL) {
+ grp_colon2 = strchr(++grp_colon, ':');
+ if (grp_colon2) *grp_colon2='\0';
+ return grp_colon;
+ }
+ return grp_data;
+}
+
+int db_authenticate_basic_user (request_rec *r)
+{
+ db_auth_config_rec *sec =
+ (db_auth_config_rec *)get_module_config (r->per_dir_config,
+ &db_auth_module);
+ conn_rec *c = r->connection;
+ char *sent_pw, *real_pw, *colon_pw;
+ char errstr[MAX_STRING_LEN];
+ int res;
+
+ if ((res = get_basic_auth_pw (r, &sent_pw)))
+ return res;
+
+ if(!sec->auth_dbpwfile)
+ return DECLINED;
+
+ if(!(real_pw = get_db_pw(r, c->user, sec->auth_dbpwfile))) {
+ sprintf(errstr,"DB user %s not found", c->user);
+ log_reason (errstr, r->filename, r);
+ note_basic_auth_failure (r);
+ return AUTH_REQUIRED;
+ }
+ /* Password is up to first : if exists */
+ colon_pw = strchr(real_pw,':');
+ if (colon_pw) *colon_pw='\0';
+ /* anyone know where the prototype for crypt is? */
+ if(strcmp(real_pw,(char *)crypt(sent_pw,real_pw))) {
+ sprintf(errstr,"user %s: password mismatch",c->user);
+ log_reason (errstr, r->uri, r);
+ note_basic_auth_failure (r);
+ return AUTH_REQUIRED;
+ }
+ return OK;
+}
+
+/* Checking ID */
+
+int db_check_auth(request_rec *r) {
+ db_auth_config_rec *sec =
+ (db_auth_config_rec *)get_module_config (r->per_dir_config,
+ &db_auth_module);
+ char *user = r->connection->user;
+ int m = r->method_number;
+ char errstr[MAX_STRING_LEN];
+
+ array_header *reqs_arr = requires (r);
+ require_line *reqs = reqs_arr ? (require_line *)reqs_arr->elts : NULL;
+
+ register int x;
+ char *t, *w;
+
+ if (!sec->auth_dbgrpfile) return DECLINED;
+ if (!reqs_arr) return DECLINED;
+
+ for(x=0; x < reqs_arr->nelts; x++) {
+
+ if (! (reqs[x].method_mask & (1 << m))) continue;
+
+ t = reqs[x].requirement;
+ w = getword(r->pool, &t, ' ');
+
+ if(!strcmp(w,"group") && sec->auth_dbgrpfile) {
+ char *orig_groups,*groups,*v;
+
+ if (!(groups = get_db_grp(r, user, sec->auth_dbgrpfile))) {
+ sprintf(errstr,"user %s not in DB group file %s",
+ user, sec->auth_dbgrpfile);
+ log_reason (errstr, r->filename, r);
+ note_basic_auth_failure (r);
+ return AUTH_REQUIRED;
+ }
+ orig_groups = groups;
+ while(t[0]) {
+ w = getword(r->pool, &t, ' ');
+ groups = orig_groups;
+ while(groups[0]) {
+ v = getword(r->pool, &groups,',');
+ if(!strcmp(v,w))
+ return OK;
+ }
+ }
+ sprintf(errstr,"user %s not in right group",user);
+ log_reason (errstr, r->filename, r);
+ note_basic_auth_failure(r);
+ return AUTH_REQUIRED;
+ }
+ }
+
+ return DECLINED;
+}
+
+
+module db_auth_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ create_db_auth_dir_config, /* dir config creater */
+ NULL, /* dir merger --- default is to override */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ db_auth_cmds, /* command table */
+ NULL, /* handlers */
+ NULL, /* filename translation */
+ db_authenticate_basic_user, /* check_user_id */
+ db_check_auth, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* fixups */
+ NULL /* logger */
+};
+
+
diff --git a/RELEASE_1_1_X/src/modules/standard/mod_auth_dbm.c b/RELEASE_1_1_X/src/modules/standard/mod_auth_dbm.c
new file mode 100644
index 00000000000..4a2a61854ea
--- /dev/null
+++ b/RELEASE_1_1_X/src/modules/standard/mod_auth_dbm.c
@@ -0,0 +1,252 @@
+
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+
+/*
+ * http_auth: authentication
+ *
+ * Rob McCool & Brian Behlendorf.
+ *
+ * Adapted to Shambhala by rst.
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "http_protocol.h"
+#include
+
+typedef struct {
+
+ char *auth_dbmpwfile;
+ char *auth_dbmgrpfile;
+
+} dbm_auth_config_rec;
+
+void *create_dbm_auth_dir_config (pool *p, char *d)
+{
+ return pcalloc (p, sizeof(dbm_auth_config_rec));
+}
+
+command_rec dbm_auth_cmds[] = {
+{ "AuthDBMUserFile", set_string_slot,
+ (void*)XtOffsetOf(dbm_auth_config_rec, auth_dbmpwfile),
+ OR_AUTHCFG, TAKE1, NULL },
+{ "AuthDBMGroupFile", set_string_slot,
+ (void*)XtOffsetOf(dbm_auth_config_rec, auth_dbmgrpfile),
+ OR_AUTHCFG, TAKE1, NULL },
+{ NULL }
+};
+
+module dbm_auth_module;
+
+char *get_dbm_pw(request_rec *r, char *user, char *auth_dbmpwfile) {
+ DBM *f;
+ datum d, q;
+ char *pw = NULL;
+
+ q.dptr = user;
+ q.dsize = strlen(q.dptr);
+
+ if(!(f=dbm_open(auth_dbmpwfile,O_RDONLY,0664))) {
+ log_reason ("could not open dbm auth file", auth_dbmpwfile, r);
+ return NULL;
+ }
+
+ d = dbm_fetch(f, q);
+
+ if (d.dptr) {
+ pw = palloc (r->pool, d.dsize + 1);
+ strncpy(pw,d.dptr,d.dsize);
+ pw[d.dsize] = '\0'; /* Terminate the string */
+ }
+
+ dbm_close(f);
+ return pw;
+}
+
+/* We do something strange with the group file. If the group file
+ * contains any : we assume the format is
+ * key=username value=":"groupname [":"anything here is ignored]
+ * otherwise we now (0.8.14+) assume that the format is
+ * key=username value=groupname
+ * The first allows the password and group files to be the same
+ * physical DBM file; key=username value=password":"groupname[":"anything]
+ *
+ * mark@telescope.org, 22Sep95
+ */
+
+char *get_dbm_grp(request_rec *r, char *user, char *auth_dbmgrpfile) {
+ char *grp_data = get_dbm_pw (r, user, auth_dbmgrpfile);
+ char *grp_colon; char *grp_colon2;
+
+ if (grp_data == NULL) return NULL;
+
+ if ((grp_colon = strchr(grp_data, ':'))!=NULL) {
+ grp_colon2 = strchr(++grp_colon, ':');
+ if (grp_colon2) *grp_colon2='\0';
+ return grp_colon;
+ }
+ return grp_data;
+}
+
+int dbm_authenticate_basic_user (request_rec *r)
+{
+ dbm_auth_config_rec *sec =
+ (dbm_auth_config_rec *)get_module_config (r->per_dir_config,
+ &dbm_auth_module);
+ conn_rec *c = r->connection;
+ char *sent_pw, *real_pw, *colon_pw;
+ char errstr[MAX_STRING_LEN];
+ int res;
+
+ if ((res = get_basic_auth_pw (r, &sent_pw)))
+ return res;
+
+ if(!sec->auth_dbmpwfile)
+ return DECLINED;
+
+ if(!(real_pw = get_dbm_pw(r, c->user, sec->auth_dbmpwfile))) {
+ sprintf(errstr,"DBM user %s not found", c->user);
+ log_reason (errstr, r->filename, r);
+ note_basic_auth_failure (r);
+ return AUTH_REQUIRED;
+ }
+ /* Password is up to first : if exists */
+ colon_pw = strchr(real_pw,':');
+ if (colon_pw) *colon_pw='\0';
+ /* anyone know where the prototype for crypt is? */
+ if(strcmp(real_pw,(char *)crypt(sent_pw,real_pw))) {
+ sprintf(errstr,"user %s: password mismatch",c->user);
+ log_reason (errstr, r->uri, r);
+ note_basic_auth_failure (r);
+ return AUTH_REQUIRED;
+ }
+ return OK;
+}
+
+/* Checking ID */
+
+int dbm_check_auth(request_rec *r) {
+ dbm_auth_config_rec *sec =
+ (dbm_auth_config_rec *)get_module_config (r->per_dir_config,
+ &dbm_auth_module);
+ char *user = r->connection->user;
+ int m = r->method_number;
+ char errstr[MAX_STRING_LEN];
+
+ array_header *reqs_arr = requires (r);
+ require_line *reqs = reqs_arr ? (require_line *)reqs_arr->elts : NULL;
+
+ register int x;
+ char *t, *w;
+
+ if (!sec->auth_dbmgrpfile) return DECLINED;
+ if (!reqs_arr) return DECLINED;
+
+ for(x=0; x < reqs_arr->nelts; x++) {
+
+ if (! (reqs[x].method_mask & (1 << m))) continue;
+
+ t = reqs[x].requirement;
+ w = getword(r->pool, &t, ' ');
+
+ if(!strcmp(w,"group") && sec->auth_dbmgrpfile) {
+ char *orig_groups,*groups,*v;
+
+ if (!(groups = get_dbm_grp(r, user, sec->auth_dbmgrpfile))) {
+ sprintf(errstr,"user %s not in DBM group file %s",
+ user, sec->auth_dbmgrpfile);
+ log_reason (errstr, r->filename, r);
+ note_basic_auth_failure (r);
+ return AUTH_REQUIRED;
+ }
+ orig_groups = groups;
+ while(t[0]) {
+ w = getword(r->pool, &t, ' ');
+ groups = orig_groups;
+ while(groups[0]) {
+ v = getword(r->pool, &groups,',');
+ if(!strcmp(v,w))
+ return OK;
+ }
+ }
+ sprintf(errstr,"user %s not in right group",user);
+ log_reason (errstr, r->filename, r);
+ note_basic_auth_failure(r);
+ return AUTH_REQUIRED;
+ }
+ }
+
+ return DECLINED;
+}
+
+
+module dbm_auth_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ create_dbm_auth_dir_config, /* dir config creater */
+ NULL, /* dir merger --- default is to override */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ dbm_auth_cmds, /* command table */
+ NULL, /* handlers */
+ NULL, /* filename translation */
+ dbm_authenticate_basic_user, /* check_user_id */
+ dbm_check_auth, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* fixups */
+ NULL /* logger */
+};
diff --git a/RELEASE_1_1_X/src/modules/standard/mod_auth_msql.c b/RELEASE_1_1_X/src/modules/standard/mod_auth_msql.c
new file mode 100644
index 00000000000..b8d0e21d2d9
--- /dev/null
+++ b/RELEASE_1_1_X/src/modules/standard/mod_auth_msql.c
@@ -0,0 +1,971 @@
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+
+/*
+ * mod_auth_msql: authentication
+ *
+ * Rob McCool & Brian Behlendorf.
+ *
+ * Adapted to Shambhala by rst.
+ *
+ * Addapted for use with the mSQL database
+ * (see ftp:/ftp.bond.edu.au/pub/Minerva/mSQL)
+ *
+ * Version 1.0 May 1996 - Blame: Dirk.vanGulik@jrc.it.
+ *
+ * A (sometimes more up to date) version of the documentation
+ * can be found at the http://www.apache.org site or at
+ * http://me-www.jrc.it/~dirkx/mod_auth_msql.html.
+ *
+ * Outline:
+ *
+ * This module allows access control using the public domain
+ * mSQL database; a fast but limted SQL engine which can be
+ * contacted over an internal unix domain protocol as well as
+ * over normal inter-machine tcp/ip socket communication.
+ *
+ * An example table could be:
+ *
+ * create table user_records (
+ * User_id char(32) primary key,
+ * Cpasswd char(32),
+ * [ Xgroup char(32) ]
+ * ) \g
+ *
+ * The user_id can be as long as desired; however some of the
+ * popular web browsers truncate, or stop the user from entering
+ * names longer than 32 characters. Furthermore the 'crypt' function
+ * on your platform might impose further limits. Also use of
+ * the 'require users uid [uid..]' directive in the access.conf file,
+ * where the user ids are separated by spaces can possibly prohibit the
+ * use of spaces in your user-names. Also, not the MAX_FIELD_LEN define
+ * somewhere below.
+ *
+ * To use the above, the following example could be in your access.conf
+ * file. Also there is a more elaborate description afther this example.
+ *
+ *
+ *
+ * Auth_MSQLhost localhost
+ * or
+ * Auth_MSQLhost datab.machine.your.org
+ *
+ * If this directive is ommited, or set to
+ * localhost, the machine on which apache
+ * runs is assumed, and the faster /dev/msql
+ * communication channel will be used. Otherwise
+ * it is the machine to contact by tcp/ip.
+ *
+ * Auth_MSQLdatabase www
+ *
+ * The name of the database on the above machine,
+ * which contains *both* the tables for group and
+ * for user/passwords. Currently it is not possible
+ * to have these split over two databases. Make
+ * sure that the msql.acl (access control file) of
+ * mSQL does indeed allow the effective uid of the
+ * web server read access to this database. Check the
+ * httpd.conf file for this uid.
+ *
+ * Auth_MSQLpwd_table user_records
+ *
+ * Here the table which contain the uid/password combination
+ * is specified.
+ *
+ * Auth_MSQLuid_field User_id
+ * Auth_MSQLpwd_field Cpasswd
+ *
+ * These two directive specify the field names in the 'user_record'
+ * table. If this module is compiled with the BACKWARD_VITEK
+ * compatibility switch, the defaults 'user' and 'password' are
+ * assumed if you do not specify them. Currently the user_id field
+ * *MUST* be a primary key or one must ensure that each user only
+ * occurs *once* in the table. If a UID occurs twice access is
+ * denied by default.
+ *
+ * Auth_MSQLgrp_table user_records
+ * Auth_MSQLgrp_field Xgroup
+ *
+ * Optionaly one can also specify a table which contains the
+ * user/group combinations. This can be the same table which
+ * also contains the username/password combinations. However
+ * if a user belongs to two or more groups, one will have to
+ * use a differt table with multiple entries.
+ *
+ * Auth_MSQL_nopasswd off
+ * Auth_MSQL_Authorative on
+ * Auth_MSQL_EncryptedPasswords on
+ *
+ * These three optional fields (all set to the sensible defaults,
+ * so you really do not have to enter them) are described in more
+ * detail below. If you choose to set these to any other values than
+ * the above be very sure you understand the security implications and
+ * do verify that apache does what you exect it to do.
+ *
+ * AuthName example mSQL realm
+ * AuthType basic
+ *
+ * Normal apache/ncsa tokens for access control
+ *
+ *
+ * order deny,allow
+ * allow from all
+ *
+ * require valid-user
+ * 'valid-user'; allow in any user which has a valid uid/passwd
+ * pair in the above pwd_table.
+ * or
+ * require user smith jones
+ * Limit access to users who have a valid uid/passwd pair in the
+ * above pwd_table AND whose uid is 'smith' or 'jones'. Do note that
+ * the uid's are separated by 'spaces' for historic (ncsa) reasons.
+ * So allowing uids with spaces might cause problems.
+ *
+ * require group has_paid
+ * Optionally also ensure that the uid has the value 'has_paid' in the group
+ * field in the group table.
+ *
+ *
+ *
+ * End of the example
+ *
+ * - full description of all tokens: -
+ *
+ * Directives:
+ *
+ * Auth_MSQLhost Hostname of the machine running
+ * the mSQL demon. The effective uid
+ * of the server should be allowed
+ * access. If not given, or if it is
+ * the magic name 'localhost', it is
+ * passed to the mSQL libary as a null
+ * pointer. This effectively forces it
+ * to use /dev/msql rather than the
+ * (slower) socket communication.
+ *
+ * Auth_MSQLdatabase Name of the database in which the following
+ * table(s) are contained.
+ *
+ * Auth_MSQLpwd_table Contains at least the fields with the
+ * username and the (encrypted) password. Each
+ * uid should only occur once in this table and
+ * for performance reasons should be a primary key.
+ * Normally this table is compulsory, but it is
+ * possible to use a fall-through to other methods
+ * and use the mSQL module for group control only;
+ * see the Authorative directive below.
+ *
+ * Auth_MSQLgrp_table Contains at least the fields with the
+ * username and the groupname. A user which
+ * is in multiple groups has therefore
+ * multiple entries; this might be some per-
+ * formance problems associated with this; and one
+ * might consider to have separate tables for each
+ * group (rather than all groups in one table) if
+ * your directory structure allows for it.
+ * One only needs to specify this table when doing
+ * group control.
+ *
+ * Auth_MSQLuid_field Name of the field containing the username
+ * Auth_MSQLpwd_field Fieldname for the passwords
+ * Auth_MSQLgrp_field Fieldname for the groupname
+ *
+ * Only the fields used need to be specified. When this
+ * module is compiled with the BACKWARD_VITEK option the
+ * uid and pwd field names default to 'user' and 'password'.
+ *
+ *
+ * Auth_MSQL_nopasswd
+ * skip password comparison if passwd field is
+ * empty; i.e. allow 'any' password. This is off
+ * by default; thus to ensure that an empty field
+ * in the mSQL table does not allow people in by
+ * default with a random password.
+ *
+ * Auth_MSQL_Authorative
+ * default is 'on'. When set on, there is no
+ * fall through to other authorization methods. So if a
+ * user is not in the mSQL dbase table (and perhaps
+ * not in the right group) or has the password wrong, then
+ * he or she is denied access. When this directive is set to
+ * 'off' control is passed on to any other authorization
+ * modules, such as the basic auth module wih the htpasswd
+ * file and or the unix-(g)dbm modules.
+ * The default is 'ON' to avoid nasty 'fall-through' sur-
+ * prizes. Do be sure you know what you decide to switch
+ * it off.
+ *
+ * Auth_MSQL_EncryptedPasswords
+ * default is on. When set on, the values in the
+ * pwd_field are assumed to be crypted using *your*
+ * machines 'crypt' function; and the incoming password
+ * is 'crypt'ed before comparison. When this function is
+ * off, the comparison is done directly with the plaintext
+ * entered password. (Yes; http-basic-auth does send the
+ * password as plaintext over the wire :-( ). The default
+ * is a sensible 'on', and I personally thing that it is
+ * a *very-bad-idea* to change this. However a multi
+ * vendor or international environment (which sometimes
+ * leads to different crypts functions) might force you to.
+ *
+ * Dirk.vanGulik@jrc.it; http://ewse.ceo.org; http://me-www.jrc.it/~dirkx
+ * 23 Nov 1995, 24 Feb 1996, 16 May 1996.
+ *
+ * Version 0.0 First release
+ * 0.1 Update to apache 1.00
+ * 0.2 added lines which got missing god knows when
+ * and which did the valid-user authentification
+ * no good at all !
+ * 0.3 Added 'Auth_MSQL_nopasswd' option
+ * 0.4 Cleaned out the error messages mess.
+ * 0.6 Inconsistency with gid/grp in comment/token/source
+ * Make sure you really use 'Auth_MSQLgrp_field' as
+ * indicated above.
+ * 0.7 *host to host fixed. Credits go to Rob Stout,
+ * for spotting this one.
+ * 0.8 Authorative directive added. See above.
+ * 0.9 palloc return code check(s), should be backward compatible with
+ * 1.11 version of Vivek Khera msql module,
+ * fixed broken err msg in group control, changed command table
+ * messages to make more sense when displayed in that new module
+ * management tool. Added EncryptedPassword on/off functionality.
+ * msqlClose() statements added upon error. Support for persistent
+ * connections with the mSQL database (riscy). Escaping of ' and \.
+ * Replaced some MAX_STRING_LENGTH claims.
+ * 1.0 removed some error check as they where already done elsehwere
+ * NumFields -> NumRows (Thanks Vitek). More stack memory.
+ */
+
+
+#define ONLY_ONCE 1
+/*
+ * If the mSQL table containing the uid/passwd combination does
+ * not have the uid field as a primary key, it is possible for the
+ * uid to occur more than once in the table with possibly different
+ * passwords. When this module is compiled with the ONLY_ONCE directive
+ * set, access is denied if the uid occures more than once in the
+ * uid/passwd table. If you choose not to set it, the software takes
+ * the first pair returned and ignores any further pairs. The SQL
+ * statement used for this is
+ *
+ * "select password form pwd_table where user='uid'"
+ *
+ * this might lead to unpredictable results. For this reason as well
+ * as for performance reasons you are strongly adviced to make the
+ * uid field a primary key. Use at your own peril :-)
+ */
+
+#undef KEEP_MSQL_CONNECTION_OPEN
+/*
+ * Normally the (tcp/ip) connection with the database is opened and
+ * closed for each SQL query. When the httpd-server and the database
+ * are on the same machine, and /dev/msql is used this does not
+ * cause a serious overhead. However when your platform does not
+ * support this (see the mSQL documentation) or when the web server
+ * and the database are on different machines the overhead can be
+ * considerable. When the above is set defined the server leaves the
+ * connection open; i.e. no call to msqlClose(). If an error occures
+ * an attempt is made to re-open the connection for the next http-rq.
+ *
+ * This has a number of very serious drawbacks
+ * - It costs 2 already rare filedescriptors for each child.
+ * - It costs msql-connections, typically one per child. The (compiled in)
+ * number of connections mSQL can handle is low, typically 6 or 12.
+ * which might prohibit access to the mSQL database for later
+ * processes.
+ * - when a child dies, it might not free that connection properly
+ * or quick enough.
+ * - When errors start to occur, connection/file-descr resources might
+ * become exausted very quickly.
+ *
+ * In short; use this at your own peril and only in a highly controled and
+ * monitored environment
+ */
+
+#define BACKWARD_VITEK
+#define VITEX_uid_name "user"
+#define VITEX_gid_name "passwd"
+/* A second mSQL auth module for apache has also been developed by
+ * Vivek Khera and was subsequently distributed
+ * with some early versions of Apache. It can be optained from
+ * ftp://ftp.kcilink.com/pub/mod_auth_msql.c*. Older 'vitek' versions had
+ * the field/table names compiled in; newer versions, v.1.11 have
+ * more access.conf configuration options; however these where
+ * choosen not to be in line the 'ewse' version of this module. Also,
+ * the 'vitek' module does not give group control or 'empty' password
+ * control.
+ *
+ * To get things slightly more in line this version (0.9) should
+ * be backward compatible with the vitek module by:
+ *
+ * - adding support for the EncryptedPassword on/off functionality
+ *
+ * - adding support for the different spelling fo the 4 configuration
+ * tokens for user-table-name, user/password-field-name and dbase-name.
+ *
+ * - setting some field names to a default which used to be hard
+ * coded in in older vitek modules.
+ *
+ * If this troubles you; remove the 'BACKWARD_VITEX' define.
+ */
+
+/* get some sensible values; rather than that big MAX_STRING_LEN,
+ */
+
+/* Max field value length limit; well above the limit of some browsers :-)
+ */
+#define MAX_FIELD_LEN (64)
+/* the next two values can be pulled from msql_priv.c, which is *NOT* copied to your
+ * /usr/local/include as part of the normal install procedure which comes with
+ * mSQL.
+ */
+#define MSQL_FIELD_NAME_LEN (19)
+#define MSQL_TABLE_NAME_LEN (19)
+/* We only do the following two queries:
+ *
+ * - for the user/passwd combination
+ * select PWDFIELD from PWDTABEL where USERFIELD='UID'
+ *
+ * - optionally for the user/group combination:
+ * select GROUPFIELD from GROUPTABLE where USERFIELD='UID' and GROUPFIELD='GID'
+ *
+ * This leads to the following limits: (we are ignoring escaping a wee bit bit here
+ * assuming not more than 24 escapes.)
+ */
+
+#define MAX_QUERY_LEN (32+24+MAX_FIELD_LEN*2+3*MSQL_FIELD_NAME_LEN+1*MSQL_TABLE_NAME_LEN)
+
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "http_protocol.h"
+#include
+#ifdef HAVE_CRYPT_H
+#include
+#endif
+
+typedef struct {
+
+ char *auth_msql_host;
+ char *auth_msql_database;
+
+ char *auth_msql_pwd_table;
+ char *auth_msql_grp_table;
+
+ char *auth_msql_pwd_field;
+ char *auth_msql_uname_field;
+ char *auth_msql_grp_field;
+
+ int auth_msql_nopasswd;
+ int auth_msql_authorative;
+ int auth_msql_encrypted;
+
+} msql_auth_config_rec;
+
+void *create_msql_auth_dir_config (pool *p, char *d)
+{
+ msql_auth_config_rec * sec= (msql_auth_config_rec *) pcalloc (p, sizeof(msql_auth_config_rec));
+
+ sec->auth_msql_host = NULL; /* just to enforce the default 'localhost' behaviour */
+
+ /* just in case, to be nice... */
+ sec->auth_msql_database = NULL;
+ sec->auth_msql_pwd_table = NULL;
+ sec->auth_msql_grp_table = NULL;
+ sec->auth_msql_pwd_field = NULL;
+ sec->auth_msql_uname_field = NULL;
+ sec->auth_msql_grp_field = NULL;
+
+
+ sec->auth_msql_authorative = 1; /* set some defaults, just in case... */
+ sec->auth_msql_encrypted = 1;
+ sec->auth_msql_nopasswd = 0;
+
+#ifdef BACKWARD_VITEK
+ /* these are for backward compatibility with the Vivek
+ * msql module, as it used to have compile-time defaults.
+ */
+ sec->auth_msql_uname_field = VITEX_uid_name;
+ sec->auth_msql_pwd_field = VITEX_gid_name;
+#endif
+
+ return sec;
+}
+
+char *set_passwd_flag (cmd_parms *cmd, msql_auth_config_rec *sec, int arg) {
+ sec->auth_msql_nopasswd=arg;
+ return NULL;
+}
+
+char *set_authorative_flag (cmd_parms *cmd, msql_auth_config_rec *sec, int arg) {
+ sec->auth_msql_authorative=arg;
+ return NULL;
+}
+
+char *set_crypted_password_flag (cmd_parms *cmd, msql_auth_config_rec *sec , int arg) {
+ sec->auth_msql_encrypted = arg;
+ return NULL;
+}
+
+char *msql_set_string_slot (cmd_parms *cmd, char *struct_ptr, char *arg) {
+ int offset = (int)cmd->info;
+ *(char **)(struct_ptr + offset) = pstrdup (cmd->pool, arg);
+ return NULL;
+}
+
+
+command_rec msql_auth_cmds[] = {
+{ "Auth_MSQLhost", msql_set_string_slot,
+ (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_host),
+ OR_AUTHCFG, TAKE1, "Host on which the mSQL database engine resides (defaults to localhost)" },
+
+{ "Auth_MSQLdatabase", msql_set_string_slot,
+ (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_database),
+ OR_AUTHCFG, TAKE1, "Name of the mSQL database which contains the password (and possibly the group) tables. " },
+
+{ "Auth_MSQLpwd_table", msql_set_string_slot,
+ (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_pwd_table),
+ OR_AUTHCFG, TAKE1, "Name of the mSQL table containing the password/user-name combination" },
+
+{ "Auth_MSQLgrp_table", msql_set_string_slot,
+ (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_grp_table),
+ OR_AUTHCFG, TAKE1, "Name of the mSQL table containing the group-name/user-name combination; can be the same as the password-table." },
+
+{ "Auth_MSQLpwd_field", msql_set_string_slot,
+ (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_pwd_field),
+ OR_AUTHCFG, TAKE1, "The name of the field in the mSQL password table" },
+
+{ "Auth_MSQLuid_field", msql_set_string_slot,
+ (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_uname_field),
+ OR_AUTHCFG, TAKE1, "The name of the user-name field in the mSQL password (and possibly group) table(s)." },
+
+{ "Auth_MSQLgrp_field", msql_set_string_slot,
+ (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_grp_field),
+ OR_AUTHCFG, TAKE1,
+ "The name of the group field in the mSQL group table; must be set if you want to use groups." },
+
+{ "Auth_MSQL_nopasswd", set_passwd_flag, NULL, OR_AUTHCFG, FLAG,
+ "Enable (on) or disable (off) empty password strings; in which case any user password is accepted." },
+
+{ "Auth_MSQL_Authorative", set_authorative_flag, NULL, OR_AUTHCFG, FLAG,
+ "When 'on' the mSQL database is taken to be authorative and access control is not passed along to other db or access modules." },
+
+{ "Auth_MSQL_EncryptedPasswords", set_crypted_password_flag, NULL, OR_AUTHCFG, FLAG,
+ "When 'on' the password in the password table are taken to be crypt()ed using your machines crypt() function." },
+
+#ifdef BACKWARD_VITEK
+/* These 'altenative' tokens should ensure backward compatibility
+ * with viteks mSQL module. The only difference is the spelling.
+ * Note that these tokens do not allow group configuration.
+ */
+{ "AuthMSQLHost", set_string_slot,
+ (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_host),
+ OR_AUTHCFG, TAKE1, "mSQL server hostname" },
+{ "AuthMSQLDB", set_string_slot,
+ (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_database),
+ OR_AUTHCFG, TAKE1, "mSQL database name" },
+{ "AuthMSQLUserTable", set_string_slot,
+ (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_pwd_table),
+ OR_AUTHCFG, TAKE1, "mSQL user table name" },
+{ "AuthMSQLGroupTable", set_string_slot,
+ (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_grp_table),
+ OR_AUTHCFG, TAKE1, "mSQL group table name" },
+{ "AuthMSQLNameField", set_string_slot,
+ (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_uname_field),
+ OR_AUTHCFG, TAKE1, "mSQL User ID field name within table" },
+{ "AuthMSQLGroupField", set_string_slot,
+ (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_grp_field),
+ OR_AUTHCFG, TAKE1, "mSQL Group field name within table" },
+{ "AuthMSQLPasswordField", set_string_slot,
+ (void*)XtOffsetOf(msql_auth_config_rec, auth_msql_pwd_field),
+ OR_AUTHCFG, TAKE1, "mSQL Password field name within table" },
+{ "AuthMSQLCryptedPasswords", set_crypted_password_flag, NULL,
+ OR_AUTHCFG, FLAG, "mSQL passwords are stored encrypted if On" },
+
+#endif
+
+{ NULL }
+};
+
+module msql_auth_module;
+
+/* boring little routine which escapes the ' and \ in the
+ * SQL query. See the mSQL FAQ for more information :-) on
+ * this very popular subject in the msql-mailing list.
+ */
+char *msql_escape(char *out, char *in, char *msql_errstr) {
+
+ register int i=0,j=0;
+
+ do {
+ /* do we need to escape */
+ if ( (in[i] == '\'') || (in[i] == '\\')) {
+
+ /* does this fit ? */
+ if (j >= (MAX_FIELD_LEN-1)) {
+ sprintf(msql_errstr,"Could not escape '%s', longer than %d",in,MAX_FIELD_LEN);
+ return NULL;
+ };
+
+ out[j++] = '\\'; /* insert that escaping slash for good measure */
+ };
+
+ /* Do things still fit ? */
+ if (j >= MAX_FIELD_LEN) return NULL;
+
+ } while ( ( out[j++] = in[i++]) != '\0' );
+
+ return out;
+}
+
+/* get the password for uname=user, and copy it
+ * into r. Assume that user is a string and stored
+ * as such in the mSQL database
+ */
+char *do_msql_query(request_rec *r, char *query, msql_auth_config_rec *sec, int once , char *msql_errstr) {
+
+ static int sock=-1;
+ int hit;
+ m_result *results;
+ m_row currow;
+
+ char *result=NULL;
+ char *host=sec->auth_msql_host;
+
+#ifndef KEEP_MSQL_CONNECTION_OPEN
+ sock=-1;
+#endif
+
+ /* force fast access over /dev/msql */
+
+ if ((host) && (!(strcasecmp(host,"localhost"))))
+ host=NULL;
+
+ /* (re) open if nessecary
+ */
+ if (sock==-1) if ((sock=msqlConnect(host)) == -1) {
+ sprintf (msql_errstr,
+ "mSQL: Could not connect to Msql DB %s (%s)",
+ (sec->auth_msql_host ? sec->auth_msql_host : "\'unset, assuming localhost!\'"),
+ msqlErrMsg);
+ return NULL;
+ }
+
+ /* we always do this, as it avoids book-keeping
+ * and is quite cheap anyway
+ */
+ if (msqlSelectDB(sock,sec->auth_msql_database) == -1 ) {
+ sprintf (msql_errstr,"mSQL: Could not select Msql Table \'%s\' on host \'%s\'(%s)",
+ (sec->auth_msql_database ? sec->auth_msql_database : "\'unset!\'"),
+ (sec->auth_msql_host ? sec->auth_msql_host : "\'unset, assuming localhost!\'"),
+ msqlErrMsg);
+ msqlClose(sock);
+ sock=-1;
+ return NULL;
+ }
+
+ if (msqlQuery(sock,query) == -1 ) {
+ sprintf (msql_errstr,"mSQL: Could not Query database '%s' on host '%s' (%s) with query [%s]",
+ (sec->auth_msql_database ? sec->auth_msql_database : "\'unset!\'"),
+ (sec->auth_msql_host ? sec->auth_msql_host : "\'unset, assuming localhost!\'"),
+ msqlErrMsg,
+ ( query ? query : "\'unset!\'") );
+ msqlClose(sock);
+ sock=-1;
+ return NULL;
+ }
+
+ if (!(results=msqlStoreResult())) {
+ sprintf (msql_errstr,"mSQL: Could not get the results from mSQL database \'%s\' on \'%s\' (%s) with query [%s]",
+ (sec->auth_msql_database ? sec->auth_msql_database : "\'unset!\'"),
+ (sec->auth_msql_host ? sec->auth_msql_host : "\'unset, assuming localhost!\'"),
+ msqlErrMsg,
+ ( query ? query : "\'unset!\'") );
+ msqlClose(sock);
+ sock=-1;
+ return NULL;
+ };
+
+ hit=msqlNumRows(results);
+
+ if (( once ) && ( hit >1 )) {
+ /* complain if there are to many
+ * matches.
+ */
+ sprintf (msql_errstr,"mSQL: More than %d matches (%d) whith query [%s]",
+ once,hit,( query ? query : "\'unset!\'") );
+ } else
+ /* if we have a it, try to get it
+ */
+ if ( hit ) {
+ if ( (currow=msqlFetchRow(results)) != NULL) {
+ /* copy the first matching field value */
+ if (!(result=palloc(r->pool,strlen(currow[0])+1))) {
+ sprintf (msql_errstr,"mSQL: Could not get memory for mSQL %s (%s) with [%s]",
+ (sec->auth_msql_database ? sec->auth_msql_database : "\'unset!\'"),
+ msqlErrMsg,
+ ( query ? query : "\'unset!\'") );
+ /* do not return right away, to ensure Free/Close.
+ */
+ } else {
+ strcpy(result,currow[0]);
+ };
+ }
+ };
+
+ /* ignore errors, functions are voids anyway. */
+ msqlFreeResult(results);
+
+#ifndef KEEP_MSQL_CONNECTION_OPEN
+ /* close the connection, unless explicitly told not to. Do note that
+ * we do not have a decent closing option of child termination due
+ * the lack of hooks in the API (or my understanding thereof)
+ */
+ msqlClose(sock);
+ sock=-1;
+#endif
+
+ return result;
+}
+
+char *get_msql_pw(request_rec *r, char *user, msql_auth_config_rec *sec ,char *msql_errstr) {
+ char query[MAX_QUERY_LEN];
+ char esc_user[MAX_FIELD_LEN];
+
+ /* do we have enough information to build a query */
+ if (
+ (!sec->auth_msql_pwd_table) ||
+ (!sec->auth_msql_pwd_field) ||
+ (!sec->auth_msql_uname_field)
+ ) {
+ sprintf(msql_errstr,
+ "mSQL: Missing parameters for password lookup: %s%s%s",
+ (sec->auth_msql_pwd_table ? "" : "Password table "),
+ (sec->auth_msql_pwd_field ? "" : "Password field name "),
+ (sec->auth_msql_uname_field ? "" : "UserID field name ")
+ );
+ return NULL;
+ };
+
+ if (!(msql_escape(esc_user, user, msql_errstr))) {
+ sprintf(msql_errstr,
+ "mSQL: Could not cope/escape the '%s' user_id value; ",user);
+ return NULL;
+ };
+ sprintf(query,"select %s from %s where %s='%s'",
+ sec->auth_msql_pwd_field,
+ sec->auth_msql_pwd_table,
+ sec->auth_msql_uname_field,
+ esc_user
+ );
+
+ return do_msql_query(r,query,sec,ONLY_ONCE,msql_errstr);
+}
+
+char *get_msql_grp(request_rec *r, char *group,char *user, msql_auth_config_rec *sec, char *msql_errstr) {
+ char query[MAX_QUERY_LEN];
+
+ char esc_user[MAX_FIELD_LEN];
+ char esc_group[MAX_FIELD_LEN];
+
+ /* do we have enough information to build a query */
+ if (
+ (!sec->auth_msql_grp_table) ||
+ (!sec->auth_msql_grp_field) ||
+ (!sec->auth_msql_uname_field)
+ ) {
+ sprintf(msql_errstr,
+ "mSQL: Missing parameters for group lookup: %s%s%s",
+ (sec->auth_msql_grp_table ? "" : "Group table "),
+ (sec->auth_msql_grp_field ? "" : "GroupID field name "),
+ (sec->auth_msql_uname_field ? "" : "UserID field name ")
+ );
+ return NULL;
+ };
+
+ if (!(msql_escape(esc_user, user,msql_errstr))) {
+ sprintf(msql_errstr,
+ "mSQL: Could not cope/escape the '%s' user_id value",user);
+
+ return NULL;
+ };
+ if (!(msql_escape(esc_group, group,msql_errstr))) {
+ sprintf(msql_errstr,
+ "mSQL: Could not cope/escape the '%s' group_id value",group);
+
+ return NULL;
+ };
+
+ sprintf(query,"select %s from %s where %s='%s' and %s='%s'",
+ sec->auth_msql_grp_field,
+ sec->auth_msql_grp_table,
+ sec->auth_msql_uname_field,esc_user,
+ sec->auth_msql_grp_field, esc_group
+ );
+
+ return do_msql_query(r,query,sec,0,msql_errstr);
+}
+
+
+int msql_authenticate_basic_user (request_rec *r)
+{
+ msql_auth_config_rec *sec =
+ (msql_auth_config_rec *)get_module_config (r->per_dir_config,
+ &msql_auth_module);
+ char msql_errstr[MAX_STRING_LEN];
+ conn_rec *c = r->connection;
+ char *sent_pw, *real_pw;
+ int res;
+ msql_errstr[0]='\0';
+
+ if ((res = get_basic_auth_pw (r, &sent_pw)))
+ return res;
+
+ /* if mSQL *password* checking is configured in any way, i.e. then
+ * handle it, if not decline and leave it to the next in line..
+ * We do not check on dbase, group, userid or host name, as it is
+ * perfectly possible to only do group control with mSQL and leave
+ * user control to the next (dbm) guy in line.
+ */
+ if (
+ (!sec->auth_msql_pwd_table) &&
+ (!sec->auth_msql_pwd_field)
+ ) return DECLINED;
+
+ if(!(real_pw = get_msql_pw(r, c->user, sec,msql_errstr ))) {
+ if ( msql_errstr[0] ) {
+ res = SERVER_ERROR;
+ } else {
+ if (sec->auth_msql_authorative) {
+ /* insist that the user is in the database
+ */
+ sprintf(msql_errstr,"mSQL: Password for user %s not found", c->user);
+ note_basic_auth_failure (r);
+ res = AUTH_REQUIRED;
+ } else {
+ /* pass control on to the next authorization module.
+ */
+ return DECLINED;
+ }; /* if authorative */
+ }; /* if no error */
+ log_reason (msql_errstr, r->filename, r);
+ return res;
+ }
+
+ /* allow no password, if the flag is set and the password
+ * is empty. But be sure to log this.
+ */
+
+ if ((sec->auth_msql_nopasswd) && (!strlen(real_pw))) {
+ sprintf(msql_errstr,"mSQL: user %s: Empty/'any' password accepted",c->user);
+ log_reason (msql_errstr, r->uri, r);
+ return OK;
+ };
+
+ /* if the flag is off however, keep that kind of stuff at
+ * an arms length.
+ */
+ if ((!strlen(real_pw)) || (!strlen(sent_pw))) {
+ sprintf(msql_errstr,"mSQL: user %s: Empty Password(s) Rejected",c->user);
+ log_reason (msql_errstr, r->uri, r);
+ note_basic_auth_failure (r);
+ return AUTH_REQUIRED;
+ };
+
+ if(sec->auth_msql_encrypted) {
+ /* anyone know where the prototype for crypt is?
+ *
+ * PLEASE NOTE:
+ * The crypt function (at least under FreeBSD 2.0.5) returns
+ * a ptr to a *static* array (max 120 chars) and does *not*
+ * modify the string pointed at by sent_pw !
+ */
+ sent_pw=(char *)crypt(sent_pw,real_pw);
+ };
+
+ if (strcmp(real_pw,sent_pw)) {
+ sprintf(msql_errstr,"mSQL user %s: password mismatch",c->user);
+ log_reason (msql_errstr, r->uri, r);
+ note_basic_auth_failure (r);
+ return AUTH_REQUIRED;
+ }
+ return OK;
+}
+
+/* Checking ID */
+
+int msql_check_auth (request_rec *r) {
+ int user_result=DECLINED,group_result=DECLINED;
+
+ msql_auth_config_rec *sec =
+ (msql_auth_config_rec *)get_module_config (r->per_dir_config,
+ &msql_auth_module);
+ char msql_errstr[MAX_STRING_LEN];
+ char *user = r->connection->user;
+ int m = r->method_number;
+ array_header *reqs_arr = requires (r);
+ require_line *reqs = reqs_arr ? (require_line *)reqs_arr->elts : NULL;
+
+ register int x;
+ char *t, *w;
+ msql_errstr[0]='\0';
+
+ if (!reqs_arr) {
+ if (sec->auth_msql_authorative) {
+ sprintf(msql_errstr,"user %s denied, no access rules specified (MSQL-Authorative) ",user);
+ log_reason (msql_errstr, r->uri, r);
+ note_basic_auth_failure(r);
+ return AUTH_REQUIRED;
+ };
+ return DECLINED;
+ };
+
+ for(x=0; (x < reqs_arr->nelts) ; x++) {
+
+ if (! (reqs[x].method_mask & (1 << m))) continue;
+
+ t = reqs[x].requirement;
+ w = getword(r->pool, &t, ' ');
+
+ if ((user_result != OK) && (!strcmp(w,"user"))) {
+ user_result=AUTH_REQUIRED;
+ while(t[0]) {
+ w = getword_conf (r->pool, &t);
+ if (!strcmp(user,w)) {
+ user_result= OK;
+ break;
+ };
+ }
+ if ((sec->auth_msql_authorative) && ( user_result != OK)) {
+ sprintf(msql_errstr,"User %s not found (MSQL-Auhtorative)",user);
+ log_reason (msql_errstr, r->uri, r);
+ note_basic_auth_failure(r);
+ return AUTH_REQUIRED;
+ };
+ }
+
+ if ( (group_result != OK) &&
+ (!strcmp(w,"group")) &&
+ (sec->auth_msql_grp_table) &&
+ (sec->auth_msql_grp_field)
+ ) {
+ /* look up the membership for each of the groups in the table
+ */
+ group_result=AUTH_REQUIRED;
+ while ( (t[0]) && (group_result != OK) && (!msql_errstr[0]) ) {
+ if (get_msql_grp(r,getword(r->pool, &t, ' '),user,sec,msql_errstr)) {
+ group_result= OK;
+ break;
+ };
+ };
+
+ if (msql_errstr[0]) {
+ log_reason (msql_errstr, r->filename, r);
+ return SERVER_ERROR;
+ };
+
+ if ( (sec->auth_msql_authorative) && (group_result != OK) ) {
+ sprintf(msql_errstr,"user %s not in right groups (MSQL-Authorative) ",user);
+ log_reason (msql_errstr, r->uri, r);
+ note_basic_auth_failure(r);
+ return AUTH_REQUIRED;
+ };
+ };
+
+ if(!strcmp(w,"valid-user")) {
+ user_result= OK;
+ };
+ }
+
+ /* we do not have to check the valid-ness of the group result as
+ * have not (yet) a 'valid-group' token
+ */
+ if ( (user_result != OK) && (sec->auth_msql_authorative) ) {
+ sprintf(msql_errstr,"User %s denied, no access rules applied (MSQL-Authorative) ",user);
+ log_reason (msql_errstr, r->uri, r);
+ note_basic_auth_failure(r);
+ return AUTH_REQUIRED;
+ };
+
+
+ /* if the user is DECLINED, it is up to the group_result to tip
+ * the balance. But if the group result is AUTH_REQUIRED it should
+ * always override. A SERVER_ERROR should not get here.
+ */
+ if ( (user_result == DECLINED) || (group_result == AUTH_REQUIRED))
+ return group_result;
+
+ return user_result;
+}
+
+
+module msql_auth_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ create_msql_auth_dir_config, /* dir config creater */
+ NULL, /* dir merger --- default is to override */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ msql_auth_cmds, /* command table */
+ NULL, /* handlers */
+ NULL, /* filename translation */
+ msql_authenticate_basic_user,/* check_user_id */
+ msql_check_auth, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* pre-run fixups */
+ NULL /* logger */
+};
+
diff --git a/RELEASE_1_1_X/src/modules/standard/mod_autoindex.c b/RELEASE_1_1_X/src/modules/standard/mod_autoindex.c
new file mode 100644
index 00000000000..c30b57c58d3
--- /dev/null
+++ b/RELEASE_1_1_X/src/modules/standard/mod_autoindex.c
@@ -0,0 +1,846 @@
+
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+
+/*
+ * http_dir.c: Handles the on-the-fly html index generation
+ *
+ * Rob McCool
+ * 3/23/93
+ *
+ * Adapted to Shambhala by rst.
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_request.h"
+#include "http_protocol.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "util_script.h"
+
+module dir_module;
+
+/****************************************************************
+ *
+ * Handling configuration directives...
+ */
+
+#define FANCY_INDEXING 1 /* Indexing options */
+#define ICONS_ARE_LINKS 2
+#define SCAN_HTML_TITLES 4
+#define SUPPRESS_LAST_MOD 8
+#define SUPPRESS_SIZE 16
+#define SUPPRESS_DESC 32
+
+struct item {
+ char *type;
+ char *apply_to;
+ char *apply_path;
+ char *data;
+};
+
+typedef struct dir_config_struct {
+
+ char *default_icon;
+ char *index_names;
+
+ array_header *icon_list, *alt_list, *desc_list, *ign_list;
+ array_header *hdr_list, *rdme_list, *opts_list;
+
+} dir_config_rec;
+
+char c_by_encoding, c_by_type, c_by_path;
+
+#define BY_ENCODING &c_by_encoding
+#define BY_TYPE &c_by_type
+#define BY_PATH &c_by_path
+
+void push_item(array_header *arr, char *type, char *to, char *path, char *data)
+{
+ struct item *p = (struct item *)push_array(arr);
+
+ if (!to) to = "";
+ if (!path) path = "";
+
+ p->type = type;
+ p->data = data ? pstrdup(arr->pool, data): NULL;
+ p->apply_path = pstrcat(arr->pool, path, "*", NULL);
+
+ if((type == BY_PATH) && (!is_matchexp(to)))
+ p->apply_to = pstrcat (arr->pool, "*", to, NULL);
+ else if (to)
+ p->apply_to = pstrdup (arr->pool, to);
+ else
+ p->apply_to = NULL;
+}
+
+char *add_alt(cmd_parms *cmd, void *d, char *alt, char *to)
+{
+ if (cmd->info == BY_PATH)
+ if(!strcmp(to,"**DIRECTORY**"))
+ to = "^^DIRECTORY^^";
+
+ push_item(((dir_config_rec *)d)->alt_list, cmd->info, to, cmd->path, alt);
+ return NULL;
+}
+
+char *add_icon(cmd_parms *cmd, void *d, char *icon, char *to)
+{
+ char *iconbak = pstrdup (cmd->pool, icon);
+
+ if(icon[0] == '(') {
+ char *alt = getword (cmd->pool, &iconbak, ',');
+ iconbak[strlen(iconbak) - 1] = '\0'; /* Lose closing paren */
+ add_alt(cmd, d, &alt[1], to);
+ }
+ if(cmd->info == BY_PATH)
+ if(!strcmp(to,"**DIRECTORY**"))
+ to = "^^DIRECTORY^^";
+
+ push_item(((dir_config_rec *)d)->icon_list, cmd->info, to, cmd->path,
+ iconbak);
+ return NULL;
+}
+
+char *add_desc(cmd_parms *cmd, void *d, char *desc, char *to)
+{
+ push_item(((dir_config_rec *)d)->desc_list, cmd->info, to, cmd->path,desc);
+ return NULL;
+}
+
+char *add_ignore(cmd_parms *cmd, void *d, char *ext) {
+ push_item(((dir_config_rec *)d)->ign_list, 0, ext, cmd->path, NULL);
+ return NULL;
+}
+
+char *add_header(cmd_parms *cmd, void *d, char *name) {
+ push_item(((dir_config_rec *)d)->hdr_list, 0, NULL, cmd->path, name);
+ return NULL;
+}
+
+char *add_readme(cmd_parms *cmd, void *d, char *name) {
+ push_item(((dir_config_rec *)d)->rdme_list, 0, NULL, cmd->path, name);
+ return NULL;
+}
+
+
+char *add_opts_int(cmd_parms *cmd, void *d, int opts) {
+ push_item(((dir_config_rec *)d)->opts_list, (char*)opts, NULL,
+ cmd->path, NULL);
+ return NULL;
+}
+
+char *fancy_indexing (cmd_parms *cmd, void *d, int arg)
+{
+ return add_opts_int (cmd, d, arg? FANCY_INDEXING : 0);
+}
+
+char *add_opts(cmd_parms *cmd, void *d, char *optstr) {
+ char *w;
+ int opts = 0;
+
+ while(optstr[0]) {
+ w = getword_conf(cmd->pool, &optstr);
+ if(!strcasecmp(w,"FancyIndexing"))
+ opts |= FANCY_INDEXING;
+ else if(!strcasecmp(w,"IconsAreLinks"))
+ opts |= ICONS_ARE_LINKS;
+ else if(!strcasecmp(w,"ScanHTMLTitles"))
+ opts |= SCAN_HTML_TITLES;
+ else if(!strcasecmp(w,"SuppressLastModified"))
+ opts |= SUPPRESS_LAST_MOD;
+ else if(!strcasecmp(w,"SuppressSize"))
+ opts |= SUPPRESS_SIZE;
+ else if(!strcasecmp(w,"SuppressDescription"))
+ opts |= SUPPRESS_DESC;
+ else if(!strcasecmp(w,"None"))
+ opts = 0;
+ else
+ return "Invalid directory indexing option";
+ }
+ return add_opts_int(cmd, d, opts);
+}
+
+#define DIR_CMD_PERMS OR_INDEXES
+
+command_rec dir_cmds[] = {
+{ "AddIcon", add_icon, BY_PATH, DIR_CMD_PERMS, ITERATE2,
+ "an icon URL followed by one or more filenames" },
+{ "AddIconByType", add_icon, BY_TYPE, DIR_CMD_PERMS, ITERATE2,
+ "an icon URL followed by one or more MIME types" },
+{ "AddIconByEncoding", add_icon, BY_ENCODING, DIR_CMD_PERMS, ITERATE2,
+ "an icon URL followed by one or more content encodings" },
+{ "AddAlt", add_alt, BY_PATH, DIR_CMD_PERMS, ITERATE2,
+ "alternate descriptive text followed by one or more filenames" },
+{ "AddAltByType", add_alt, BY_TYPE, DIR_CMD_PERMS, ITERATE2,
+ "alternate descriptive text followed by one or more MIME types" },
+{ "AddAltByEncoding", add_alt, BY_ENCODING, DIR_CMD_PERMS, ITERATE2,
+ "alternate descriptive text followed by one or more content encodings" },
+{ "IndexOptions", add_opts, NULL, DIR_CMD_PERMS, RAW_ARGS,
+ "one or more index options" },
+{ "IndexIgnore", add_ignore, NULL, DIR_CMD_PERMS, ITERATE,
+ "one or more file extensions" },
+{ "AddDescription", add_desc, BY_PATH, DIR_CMD_PERMS, ITERATE2,
+ "Descriptive text followed by one or more filenames" },
+{ "HeaderName", add_header, NULL, DIR_CMD_PERMS, TAKE1, "a filename" },
+{ "ReadmeName", add_readme, NULL, DIR_CMD_PERMS, TAKE1, "a filename" },
+{ "FancyIndexing", fancy_indexing, NULL, DIR_CMD_PERMS, FLAG, NULL },
+{ "DefaultIcon", set_string_slot,
+ (void*)XtOffsetOf(dir_config_rec, default_icon),
+ DIR_CMD_PERMS, TAKE1, "an icon URL"},
+{ "DirectoryIndex", set_string_slot,
+ (void*)XtOffsetOf(dir_config_rec, index_names),
+ DIR_CMD_PERMS, RAW_ARGS, NULL },
+{ NULL }
+};
+
+void *create_dir_config (pool *p, char *dummy)
+{
+ dir_config_rec *new =
+ (dir_config_rec *) pcalloc (p, sizeof(dir_config_rec));
+
+ new->index_names = NULL;
+ new->icon_list = make_array (p, 4, sizeof (struct item));
+ new->alt_list = make_array (p, 4, sizeof (struct item));
+ new->desc_list = make_array (p, 4, sizeof (struct item));
+ new->ign_list = make_array (p, 4, sizeof (struct item));
+ new->hdr_list = make_array (p, 4, sizeof (struct item));
+ new->rdme_list = make_array (p, 4, sizeof (struct item));
+ new->opts_list = make_array (p, 4, sizeof (struct item));
+
+ return (void *)new;
+}
+
+void *merge_dir_configs (pool *p, void *basev, void *addv)
+{
+ dir_config_rec *new=(dir_config_rec*)pcalloc (p, sizeof(dir_config_rec));
+ dir_config_rec *base = (dir_config_rec *)basev;
+ dir_config_rec *add = (dir_config_rec *)addv;
+
+ new->default_icon = add->default_icon?add->default_icon:base->default_icon;
+ new->index_names = add->index_names? add->index_names: base->index_names;
+
+ new->alt_list = append_arrays (p, add->alt_list, base->alt_list);
+ new->ign_list = append_arrays (p, add->ign_list, base->ign_list);
+ new->hdr_list = append_arrays (p, add->hdr_list, base->hdr_list);
+ new->desc_list = append_arrays (p, add->desc_list, base->desc_list);
+ new->icon_list = append_arrays (p, add->icon_list, base->icon_list);
+ new->rdme_list = append_arrays (p, add->rdme_list, base->rdme_list);
+ new->opts_list = append_arrays (p, add->opts_list, base->opts_list);
+
+ return new;
+}
+
+/****************************************************************
+ *
+ * Looking things up in config entries...
+ */
+
+/* Structure used to hold entries when we're actually building an index */
+
+struct ent {
+ char *name;
+ char *icon;
+ char *alt;
+ char *desc;
+ size_t size;
+ time_t lm;
+ struct ent *next;
+};
+
+char *find_item(request_rec *r, array_header *list, int path_only) {
+ char *content_type = r->content_type;
+ char *content_encoding = r->content_encoding;
+ char *path = r->filename;
+
+ struct item *items = (struct item *)list->elts;
+ int i;
+
+ for (i = 0; i < list->nelts; ++i) {
+ struct item *p = &items[i];
+
+ /* Special cased for ^^DIRECTORY^^ and ^^BLANKICON^^ */
+ if((path[0] == '^') || (!strcmp_match(path,p->apply_path))) {
+ if(!*(p->apply_to))
+ return p->data;
+ else if(p->type == BY_PATH || path[0] == '^') {
+ if(!strcmp_match(path,p->apply_to))
+ return p->data;
+ } else if(!path_only) {
+ if(!content_encoding) {
+ if(p->type == BY_TYPE) {
+ if(content_type && !strcmp_match(content_type,p->apply_to))
+ return p->data;
+ }
+ } else {
+ if(p->type == BY_ENCODING) {
+ if(!strcmp_match(content_encoding,p->apply_to))
+ return p->data;
+ }
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+#define find_icon(d,p,t) find_item(p,d->icon_list,t)
+#define find_alt(d,p,t) find_item(p,d->alt_list,t)
+#define find_desc(d,p) find_item(p,d->desc_list,0)
+#define find_header(d,p) find_item(p,d->hdr_list,0)
+#define find_readme(d,p) find_item(p,d->rdme_list,0)
+
+char *find_default_icon (dir_config_rec *d, char *bogus_name)
+{
+ request_rec r;
+
+ /* Bleah. I tried to clean up find_item, and it lead to this bit
+ * of ugliness. Note that the fields initialized are precisely
+ * those that find_item looks at...
+ */
+
+ r.filename = bogus_name;
+ r.content_type = r.content_encoding = NULL;
+
+ return find_item (&r, d->icon_list, 1);
+}
+
+int ignore_entry(dir_config_rec *d, char *path) {
+ array_header *list = d->ign_list;
+ struct item *items = (struct item *)list->elts;
+ char *tt;
+ int i;
+
+ if((tt=strrchr(path,'/')) == NULL)
+ tt=path;
+ else {
+ tt++;
+ }
+
+ for (i = 0; i < list->nelts; ++i) {
+ struct item *p = &items[i];
+ char *ap;
+
+ if((ap=strrchr(p->apply_to,'/')) == NULL)
+ ap=p->apply_to;
+ else
+ ap++;
+
+ if(!strcmp_match(path,p->apply_path) && !strcmp_match(tt,ap))
+ return 1;
+ }
+ return 0;
+}
+
+int find_opts(dir_config_rec *d, request_rec *r) {
+ char *path = r->filename;
+ array_header *list = d->opts_list;
+ struct item *items = (struct item *)list->elts;
+ int i;
+
+ for (i = 0; i < list->nelts; ++i) {
+ struct item *p = &items[i];
+
+ if(!strcmp_match(path,p->apply_path))
+ return (int)p->type;
+ }
+ return 0;
+}
+
+/*****************************************************************
+ *
+ * Actually generating output
+ */
+
+
+int insert_readme(char *name, char *readme_fname, int rule, request_rec *r) {
+ char *fn;
+ FILE *f;
+ struct stat finfo;
+ int plaintext=0;
+
+ fn = make_full_path(r->pool, name, readme_fname);
+ fn = pstrcat(r->pool, fn, ".html", NULL);
+ if(stat(fn,&finfo) == -1) {
+ /* A brief fake multiviews search for README.html */
+ fn[strlen(fn)-5] = '\0';
+ if(stat(fn,&finfo) == -1)
+ return 0;
+ plaintext=1;
+ if(rule) rputs("
\n", r);
+ rputs("\n", r);
+ }
+ else if (rule) rputs("
\n", r);
+ if(!(f = pfopen(r->pool,fn,"r")))
+ return 0;
+ if (!plaintext)
+ send_fd(f, r);
+ else
+ {
+ char buf[IOBUFSIZE+1];
+ int i, n, c, ch;
+ while (!feof(f))
+ {
+ do n = fread(buf, sizeof(char), IOBUFSIZE, f);
+ while (n == -1 && ferror(f) && errno == EINTR);
+ if (n == -1 || n == 0) break;
+ buf[n] = '\0';
+ c = 0;
+ while (c < n)
+ {
+ for (i=c; i < n; i++)
+ if (buf[i] == '<' || buf[i] == '>' || buf[i] == '&') break;
+ ch = buf[i];
+ buf[i] = '\0';
+ rputs(&buf[c], r);
+ if (ch == '<') rputs("<", r);
+ else if (ch == '>') rputs(">", r);
+ else if (ch == '&') rputs("&", r);
+ c = i + 1;
+ }
+ }
+ }
+ pfclose(r->pool, f);
+ if(plaintext)
+ rputs("
\n", r);
+ return 1;
+}
+
+
+char *find_title(request_rec *r) {
+ char titlebuf[MAX_STRING_LEN], *find = "";
+ FILE *thefile = NULL;
+ int x,y,n,p;
+
+ if (r->content_type && !strcmp(r->content_type,"text/html") && !r->content_encoding) {
+ if(!(thefile = pfopen(r->pool, r->filename,"r")))
+ return NULL;
+ n = fread(titlebuf,sizeof(char),MAX_STRING_LEN - 1,thefile);
+ titlebuf[n] = '\0';
+ for(x=0,p=0;titlebuf[x];x++) {
+ if(toupper(titlebuf[x]) == find[p]) {
+ if(!find[++p]) {
+ if((p = ind(&titlebuf[++x],'<')) != -1)
+ titlebuf[x+p] = '\0';
+ /* Scan for line breaks for Tanmoy's secretary */
+ for(y=x;titlebuf[y];y++)
+ if((titlebuf[y] == CR) || (titlebuf[y] == LF))
+ titlebuf[y] = ' ';
+ pfclose (r->pool, thefile);
+ return pstrdup(r->pool, &titlebuf[x]);
+ }
+ } else p=0;
+ }
+ pfclose(r->pool, thefile);
+ }
+ return NULL;
+}
+
+struct ent *make_dir_entry(char *name, int dir_opts,
+ dir_config_rec *d, request_rec *r)
+{
+ struct ent *p;
+
+ if((name[0] == '.') && (!name[1]))
+ return(NULL);
+
+ if (ignore_entry(d, make_full_path (r->pool, r->filename, name)))
+ return(NULL);
+
+ p=(struct ent *)pcalloc(r->pool, sizeof(struct ent));
+ p->name = pstrdup (r->pool, name);
+ p->size = -1;
+ p->icon = NULL;
+ p->alt = NULL;
+ p->desc = NULL;
+ p->lm = -1;
+
+ if(dir_opts & FANCY_INDEXING) {
+ request_rec *rr = sub_req_lookup_file (name, r);
+
+ if (rr->finfo.st_mode != 0) {
+ p->lm = rr->finfo.st_mtime;
+ if(S_ISDIR(rr->finfo.st_mode)) {
+ if(!(p->icon = find_icon(d,rr,1)))
+ p->icon = find_default_icon(d,"^^DIRECTORY^^");
+ if(!(p->alt = find_alt(d,rr,1)))
+ p->alt = "DIR";
+ p->size = -1;
+ p->name = pstrcat (r->pool, name, "/", NULL);
+ }
+ else {
+ p->icon = find_icon(d, rr, 0);
+ p->alt = find_alt(d, rr, 0);
+ p->size = rr->finfo.st_size;
+ }
+ }
+
+ p->desc = find_desc(d, rr);
+
+ if((!p->desc) && (dir_opts & SCAN_HTML_TITLES))
+ p->desc = pstrdup (r->pool, find_title(rr));
+
+ destroy_sub_req (rr);
+ }
+ return(p);
+}
+
+char *terminate_description(dir_config_rec *d, char *desc, int dir_opts) {
+ int maxsize = 23;
+ register int x;
+
+ if(dir_opts & SUPPRESS_LAST_MOD) maxsize += 17;
+ if(dir_opts & SUPPRESS_SIZE) maxsize += 7;
+
+ for(x=0;desc[x] && maxsize;x++) {
+ if(desc[x] == '<') {
+ while(desc[x] != '>') {
+ if(!desc[x]) {
+ maxsize = 0;
+ break;
+ }
+ ++x;
+ }
+ }
+ else --maxsize;
+ }
+ if(!maxsize) {
+ desc[x-1] = '>'; /* Grump. */
+ desc[x] = '\0'; /* Double Grump! */
+ }
+ return desc;
+}
+
+void output_directories(struct ent **ar, int n,
+ dir_config_rec *d, request_rec *r, int dir_opts)
+{
+ int x, len;
+ char *name = r->uri;
+ char *tp;
+ pool *scratch = make_sub_pool (r->pool);
+
+ if(name[0] == '\0') name = "/";
+
+ if(dir_opts & FANCY_INDEXING) {
+ rputs("", r);
+ if((tp = find_default_icon(d,"^^BLANKICON^^")))
+ rvputs(r, "
", NULL);
+ rputs("Name ", r);
+ if(!(dir_opts & SUPPRESS_LAST_MOD))
+ rputs("Last modified ", r);
+ if(!(dir_opts & SUPPRESS_SIZE))
+ rputs("Size ", r);
+ if(!(dir_opts & SUPPRESS_DESC))
+ rputs("Description", r);
+ rputs("\n
\n", r);
+ }
+ else {
+ rputs("
", r);
+ }
+
+ for(x=0;xname,"../")) || (!strcmp(ar[x]->name,".."))) {
+ char *t = make_full_path (scratch, name, "../");
+ getparents(t);
+ if(t[0] == '\0') t = "/";
+ anchor = pstrcat (scratch, "", NULL);
+ t2 = "Parent Directory ";
+ }
+ else {
+ t = ar[x]->name;
+ len = strlen(t);
+ if(len > 23) {
+ t2 = pstrdup(scratch, t);
+ t2[21] = '.';
+ t2[22] = '.';
+ t2[23] = '\0';
+ t2 = escape_html(scratch, t2);
+ t2 = pstrcat(scratch, t2, "", NULL);
+ } else
+ {
+ char buff[24]=" ";
+ t2 = escape_html(scratch, t);
+ buff[23-len] = '\0';
+ t2 = pstrcat(scratch, t2, "", buff, NULL);
+ }
+ anchor = pstrcat (scratch, "", NULL);
+ }
+
+ if(dir_opts & FANCY_INDEXING) {
+ if(dir_opts & ICONS_ARE_LINKS)
+ rputs(anchor, r);
+ if((ar[x]->icon) || d->default_icon) {
+ rvputs(r, "
icon ?
+ ar[x]->icon : d->default_icon),
+ "\" ALT=\"[", (ar[x]->alt ? ar[x]->alt : " "),
+ "]\">", NULL);
+ }
+ if(dir_opts & ICONS_ARE_LINKS)
+ rputs("", r);
+
+ rvputs(r," ", anchor, t2, NULL);
+ if(!(dir_opts & SUPPRESS_LAST_MOD)) {
+ if(ar[x]->lm != -1) {
+ char time[MAX_STRING_LEN];
+ struct tm *ts = localtime(&ar[x]->lm);
+ strftime(time,MAX_STRING_LEN,"%d-%b-%y %H:%M ",ts);
+ rputs(time, r);
+ }
+ else {
+ rputs(" ", r);
+ }
+ }
+ if(!(dir_opts & SUPPRESS_SIZE)) {
+ send_size(ar[x]->size,r);
+ rputs(" ", r);
+ }
+ if(!(dir_opts & SUPPRESS_DESC)) {
+ if(ar[x]->desc) {
+ rputs(terminate_description(d, ar[x]->desc, dir_opts), r);
+ }
+ }
+ }
+ else
+ rvputs(r, "- ", anchor," ", t2, NULL);
+ rputc('\n', r);
+ }
+ if(dir_opts & FANCY_INDEXING) {
+ rputs("", r);
+ }
+ else {
+ rputs("
", r);
+ }
+}
+
+
+int dsortf(struct ent **s1,struct ent **s2)
+{
+ return(strcmp((*s1)->name,(*s2)->name));
+}
+
+
+int index_directory(request_rec *r, dir_config_rec *dir_conf)
+{
+ char *title_name = escape_html(r->pool, r->uri);
+ char *title_endp;
+ char *name = r->filename;
+
+ DIR *d;
+ struct DIR_TYPE *dstruct;
+ int num_ent=0,x;
+ struct ent *head,*p;
+ struct ent **ar;
+ char *tmp;
+ int dir_opts = find_opts(dir_conf, r);
+
+ if(!(d=opendir(name))) return FORBIDDEN;
+
+ r->content_type = "text/html";
+
+ soft_timeout ("send directory", r);
+ send_http_header(r);
+
+ if (r->header_only) {
+ closedir (d);
+ return 0;
+ }
+
+ /* Spew HTML preamble */
+
+ title_endp = title_name + strlen(title_name) - 1;
+
+ while (title_endp > title_name && *title_endp == '/')
+ *title_endp-- = '\0';
+
+ rvputs(r, "Index of ", title_name, "\n",
+ NULL);
+
+ if((!(tmp = find_header(dir_conf,r))) || (!(insert_readme(name,tmp,0,r))))
+ rvputs(r, "Index of ", title_name, "
\n", NULL);
+
+ /*
+ * Since we don't know how many dir. entries there are, put them into a
+ * linked list and then arrayificate them so qsort can use them.
+ */
+ head=NULL;
+ while((dstruct=readdir(d))) {
+ if((p = make_dir_entry(dstruct->d_name, dir_opts, dir_conf, r))) {
+ p->next=head;
+ head=p;
+ num_ent++;
+ }
+ }
+ ar=(struct ent **) palloc(r->pool, num_ent*sizeof(struct ent *));
+ p=head;
+ x=0;
+ while(p) {
+ ar[x++]=p;
+ p = p->next;
+ }
+
+ qsort((void *)ar,num_ent,sizeof(struct ent *),
+#ifdef ULTRIX_BRAIN_DEATH
+ (int (*))dsortf);
+#else
+ (int (*)(const void *,const void *))dsortf);
+#endif
+ output_directories(ar, num_ent, dir_conf, r, dir_opts);
+ closedir(d);
+
+ if (dir_opts & FANCY_INDEXING)
+ if((tmp = find_readme(dir_conf, r)))
+ insert_readme(name,tmp,1,r);
+ else {
+ rputs("", r);
+ }
+
+ rputs("", r);
+ return 0;
+}
+
+/* The formal handler... */
+
+int handle_dir (request_rec *r)
+{
+ dir_config_rec *d =
+ (dir_config_rec *)get_module_config (r->per_dir_config, &dir_module);
+ char *names_ptr = d->index_names ? d->index_names : DEFAULT_INDEX;
+ int allow_opts = allow_options (r);
+
+ if (r->uri[0] == '\0' || r->uri[strlen(r->uri)-1] != '/') {
+ char* ifile;
+ if (r->args != NULL)
+ ifile = pstrcat (r->pool, escape_uri(r->pool, r->uri),
+ "/", "?", r->args, NULL);
+ else
+ ifile = pstrcat (r->pool, escape_uri(r->pool, r->uri),
+ "/", NULL);
+
+ table_set (r->headers_out, "Location",
+ construct_url(r->pool, ifile, r->server));
+ return REDIRECT;
+ }
+
+ /* KLUDGE --- make the sub_req lookups happen in the right directory.
+ * Fixing this in the sub_req_lookup functions themselves is difficult,
+ * and would probably break virtual includes...
+ */
+
+ r->filename = pstrcat (r->pool, r->filename, "/", NULL);
+
+ while (*names_ptr) {
+
+ char *name_ptr = getword_conf (r->pool, &names_ptr);
+ request_rec *rr = sub_req_lookup_uri (name_ptr, r);
+
+ if (rr->status == 200 && rr->finfo.st_mode != 0) {
+ char* new_uri = escape_uri(r->pool, rr->uri);
+
+ if (rr->args != NULL)
+ new_uri = pstrcat(r->pool, new_uri, "?", rr->args, NULL);
+ else if (r->args != NULL)
+ new_uri = pstrcat(r->pool, new_uri, "?", r->args, NULL);
+
+ destroy_sub_req (rr);
+ internal_redirect (new_uri, r);
+ return OK;
+ }
+
+ destroy_sub_req (rr);
+ }
+
+ if (r->method_number != M_GET) return NOT_IMPLEMENTED;
+
+ /* OK, nothing easy. Trot out the heavy artillery... */
+
+ if (allow_opts & OPT_INDEXES)
+ return index_directory (r, d);
+ else
+ return FORBIDDEN;
+}
+
+
+handler_rec dir_handlers[] = {
+{ DIR_MAGIC_TYPE, handle_dir },
+{ NULL }
+};
+
+module dir_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ create_dir_config, /* dir config creater */
+ merge_dir_configs, /* dir merger --- default is to override */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ dir_cmds, /* command table */
+ dir_handlers, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* fixups */
+ NULL /* logger */
+};
diff --git a/RELEASE_1_1_X/src/modules/standard/mod_cern_meta.c b/RELEASE_1_1_X/src/modules/standard/mod_cern_meta.c
new file mode 100644
index 00000000000..c16bde4e63d
--- /dev/null
+++ b/RELEASE_1_1_X/src/modules/standard/mod_cern_meta.c
@@ -0,0 +1,330 @@
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+/*
+ * mod_cern_meta.c
+ * version 0.0.5
+ * status beta
+ *
+ * Andrew Wilson 25.Jan.96
+ *
+ * Emulate the CERN HTTPD Meta file semantics. Meta files are HTTP
+ * headers that can be output in addition to the normal range of
+ * headers for each file accessed. They appear rather like the Apache
+ * .asis files, and are able to provide a crude way of influencing
+ * the Expires: header, as well as providing other curiosities.
+ * There are many ways to manage meta information, this one was
+ * chosen because there is already a large number of CERN users
+ * who can exploit this module. It should be noted that there are probably
+ * more sensitive ways of managing the Expires: header specifically.
+ *
+ * The module obeys the following directives, which can only appear
+ * in the server's .conf files and not in any .htaccess file.
+ *
+ * MetaDir
+ *
+ * specifies the name of the directory in which Apache can find
+ * meta information files. The directory is usually a 'hidden'
+ * subdirectory of the directory that contains the file being
+ * accessed. eg:
+ *
+ * # .meta files are in the *same* directory as the
+ * # file being accessed
+ * MetaDir .
+ *
+ * the default is to look in a '.web' subdirectory. This is the
+ * same as for CERN 3.+ webservers and behaviour is the same as
+ * for the directive:
+ *
+ * MetaDir .web
+ *
+ * MetaSuffix
+ *
+ * specifies the file name suffix for the file containing the
+ * meta information. eg:
+ *
+ * # our meta files are suffixed with '.cern_meta'
+ * MetaSuffix .cern_meta
+ *
+ * the default is to look for files with the suffix '.meta'. This
+ * behaviour is the same as for the directive:
+ *
+ * MetaSuffix .meta
+ *
+ * When accessing the file
+ *
+ * DOCUMENT_ROOT/somedir/index.html
+ *
+ * this module will look for the file
+ *
+ * DOCUMENT_ROOT/somedir/.web/index.html.meta
+ *
+ * and will use its contents to generate additional MIME header
+ * information.
+ *
+ * For more information on the CERN Meta file semantics see:
+ *
+ * http://www.w3.org/hypertext/WWW/Daemon/User/Config/General.html#MetaDir
+ *
+ * Change-log:
+ * 29.Jan.96 pfopen/pfclose instead of fopen/fclose
+ * DECLINE when real file not found, we may be checking each
+ * of the index.html/index.shtml/index.htm variants and don't
+ * need to report missing ones as spurious errors.
+ * 31.Jan.96 log_error reports about a malformed .meta file, rather
+ * than a script error.
+ *
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include
+#include
+#include "util_script.h"
+#include "http_log.h"
+
+#define DEFAULT_METADIR ".web"
+#define DEFAULT_METASUFFIX ".meta"
+
+module cern_meta_module;
+
+typedef struct {
+ char *metadir;
+ char *metasuffix;
+} cern_meta_config;
+
+void *create_cern_meta_config (pool *p, server_rec *dummy)
+{
+ cern_meta_config *new =
+ (cern_meta_config *) palloc (p, sizeof(cern_meta_config));
+
+ new->metadir = DEFAULT_METADIR;
+ new->metasuffix = DEFAULT_METASUFFIX;
+
+ return new;
+}
+
+char *set_metadir (cmd_parms *parms, void *dummy, char *arg)
+{
+ cern_meta_config *cmc ;
+
+ cmc = get_module_config (parms->server->module_config,
+ &cern_meta_module);
+ cmc->metadir = arg;
+ return NULL;
+}
+
+char *set_metasuffix (cmd_parms *parms, void *dummy, char *arg)
+{
+ cern_meta_config *cmc ;
+
+ cmc = get_module_config (parms->server->module_config,
+ &cern_meta_module);
+ cmc->metasuffix = arg;
+ return NULL;
+}
+
+command_rec cern_meta_cmds[] = {
+{ "MetaDir", set_metadir, NULL, RSRC_CONF, TAKE1,
+ "the name of the directory containing meta files"},
+{ "MetaSuffix", set_metasuffix, NULL, RSRC_CONF, TAKE1,
+ "the filename suffix for meta files"},
+{ NULL }
+};
+
+int scan_meta_file(request_rec *r, FILE *f)
+{
+ char w[MAX_STRING_LEN];
+ char *l;
+ int p;
+
+ while( fgets(w, MAX_STRING_LEN-1, f) != NULL ) {
+
+ /* Delete terminal (CR?)LF */
+
+ p = strlen(w);
+ if (p > 0 && w[p-1] == '\n')
+ {
+ if (p > 1 && w[p-2] == '\015') w[p-2] = '\0';
+ else w[p-1] = '\0';
+ }
+
+ if(w[0] == '\0') {
+ return OK;
+ }
+
+ /* if we see a bogus header don't ignore it. Shout and scream */
+
+ if(!(l = strchr(w,':'))) {
+ log_reason ("malformed header in meta file", r->filename, r);
+ return SERVER_ERROR;
+ }
+
+ *l++ = '\0';
+ while (*l && isspace (*l)) ++l;
+
+ if(!strcasecmp(w,"Content-type")) {
+
+ /* Nuke trailing whitespace */
+
+ char *endp = l + strlen(l) - 1;
+ while (endp > l && isspace(*endp)) *endp-- = '\0';
+
+ r->content_type = pstrdup (r->pool, l);
+ }
+ else if(!strcasecmp(w,"Status")) {
+ sscanf(l, "%d", &r->status);
+ r->status_line = pstrdup(r->pool, l);
+ }
+ else {
+ table_set (r->headers_out, w, l);
+ }
+ }
+ return OK;
+}
+
+int add_cern_meta_data(request_rec *r)
+{
+ char *metafilename;
+ char *last_slash;
+ char *real_file;
+ char *scrap_book;
+ struct stat meta_stat;
+ FILE *f;
+ cern_meta_config *cmc ;
+ int rv;
+
+ cmc = get_module_config (r->server->module_config,
+ &cern_meta_module);
+
+ /* if ./.web/$1.meta exists then output 'asis' */
+
+ if (r->finfo.st_mode == 0) {
+ return DECLINED;
+ };
+
+ /* does uri end in a trailing slash? */
+ if ( r->uri[strlen(r->uri) - 1] == '/' ) {
+ return DECLINED;
+ };
+
+ /* what directory is this file in? */
+ scrap_book = pstrdup( r->pool, r->filename );
+ /* skip leading slash, recovered in later processing */
+ scrap_book++;
+ last_slash = strrchr( scrap_book, '/' );
+ if ( last_slash != NULL ) {
+ /* skip over last slash */
+ real_file = last_slash;
+ real_file++;
+ *last_slash = '\0';
+ } else {
+ /* no last slash, buh?! */
+ log_reason("internal error in mod_cern_meta", r->filename, r);
+ /* should really barf, but hey, let's be friends... */
+ return DECLINED;
+ };
+
+ metafilename = pstrcat(r->pool, "/", scrap_book, "/", cmc->metadir, "/", real_file, cmc->metasuffix, NULL);
+
+ /*
+ * stat can legitimately fail for a bewildering number of reasons,
+ * only one of which implies the file isn't there. A hardened
+ * version of this module should test for all conditions, but later...
+ */
+ if (stat(metafilename, &meta_stat) == -1) {
+ /* stat failed, possibly file missing */
+ return DECLINED;
+ };
+
+ /*
+ * this check is to be found in other Jan/96 Apache code, I've
+ * not been able to find any corroboration in the man pages but
+ * I've been wrong before so I'll put it in anyway. Never
+ * admit to being clueless...
+ */
+ if ( meta_stat.st_mode == 0 ) {
+ /* stat failed, definately file missing */
+ return DECLINED;
+ };
+
+ f = pfopen (r->pool, metafilename, "r");
+
+ if (f == NULL) {
+ log_reason("meta file permissions deny server access", metafilename, r);
+ return FORBIDDEN;
+ };
+
+ /* read the headers in */
+ rv = scan_meta_file(r, f);
+ pfclose( r->pool, f );
+
+ return rv;
+}
+
+module cern_meta_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ NULL, /* dir config creater */
+ NULL, /* dir merger --- default is to override */
+ create_cern_meta_config, /* server config */
+ NULL, /* merge server configs */
+ cern_meta_cmds, /* command table */
+ NULL, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ add_cern_meta_data, /* fixups */
+ NULL, /* logger */
+};
diff --git a/RELEASE_1_1_X/src/modules/standard/mod_cgi.c b/RELEASE_1_1_X/src/modules/standard/mod_cgi.c
new file mode 100644
index 00000000000..0a869427e0e
--- /dev/null
+++ b/RELEASE_1_1_X/src/modules/standard/mod_cgi.c
@@ -0,0 +1,412 @@
+
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+
+/*
+ * http_script: keeps all script-related ramblings together.
+ *
+ * Compliant to CGI/1.1 spec
+ *
+ * Adapted by rst from original NCSA code by Rob McCool
+ *
+ * Apache adds some new env vars; REDIRECT_URL and REDIRECT_QUERY_STRING for
+ * custom error responses, and DOCUMENT_ROOT because we found it useful.
+ * It also adds SERVER_ADMIN - useful for scripts to know who to mail when
+ * they fail.
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_main.h"
+#include "http_log.h"
+#include "util_script.h"
+
+/* KLUDGE --- for back-combatibility, we don't have to check ExecCGI
+ * in ScriptAliased directories, which means we need to know if this
+ * request came through ScriptAlias or not... so the Alias module
+ * leaves a note for us.
+ */
+
+int is_scriptaliased (request_rec *r)
+{
+ char *t = table_get (r->notes, "alias-forced-type");
+ return t && (!strcmp (t, "cgi-script"));
+}
+
+/****************************************************************
+ *
+ * Actual CGI handling...
+ */
+
+
+struct cgi_child_stuff {
+ request_rec *r;
+ int nph;
+ char *argv0;
+};
+
+void cgi_child (void *child_stuff)
+{
+ struct cgi_child_stuff *cld = (struct cgi_child_stuff *)child_stuff;
+ request_rec *r = cld->r;
+ char *argv0 = cld->argv0;
+ int nph = cld->nph;
+
+#ifdef DEBUG_CGI
+#ifdef __EMX__
+ /* Under OS/2 need to use device con. */
+ FILE *dbg = fopen ("con", "w");
+#else
+ FILE *dbg = fopen ("/dev/tty", "w");
+#endif
+ int i;
+#endif
+
+ char **env;
+ char err_string[HUGE_STRING_LEN];
+
+#ifdef DEBUG_CGI
+ fprintf (dbg, "Attempting to exec %s as %sCGI child (argv0 = %s)\n",
+ r->filename, nph ? "NPH " : "", argv0);
+#endif
+
+ add_cgi_vars (r);
+ env = create_environment (r->pool, r->subprocess_env);
+
+#ifdef DEBUG_CGI
+ fprintf (dbg, "Environment: \n");
+ for (i = 0; env[i]; ++i) fprintf (dbg, "'%s'\n", env[i]);
+#endif
+
+ chdir_file (r->filename);
+ error_log2stderr (r->server);
+
+#ifndef __EMX__
+ if (nph) client_to_stdout (r->connection);
+#endif
+
+ /* Transumute outselves into the script.
+ * NB only ISINDEX scripts get decoded arguments.
+ */
+
+ cleanup_for_exec();
+
+#ifdef __EMX__
+ if((!r->args) || (!r->args[0]) || (ind(r->args,'=') >= 0)) {
+ int emxloop;
+ char *emxtemp;
+
+ /* For OS/2 place the variables in the current
+ enviornment then it will be inherited. This way
+ the program will also get all of OS/2's other SETs. */
+ for (emxloop=0; ((emxtemp = env[emxloop]) != NULL); emxloop++)
+ putenv(emxtemp);
+
+ if (strstr(strupr(r->filename), ".CMD") > 0) {
+ /* Special case to allow use of REXX commands as scripts. */
+ os2pathname(r->filename);
+ execl("CMD.EXE", "CMD.EXE", "/C", r->filename, NULL);
+ } else {
+ execl(r->filename, argv0, NULL);
+ }
+ } else {
+ int emxloop;
+ char *emxtemp;
+
+ /* For OS/2 place the variables in the current
+ enviornment then it will be inherited. This way
+ the program will also get all of OS/2's other SETs. */
+ for (emxloop=0; ((emxtemp = env[emxloop]) != NULL); emxloop++)
+ putenv(emxtemp);
+
+ if (strstr(strupr(r->filename), ".CMD") > 0) {
+ /* Special case to allow use of REXX commands as scripts. */
+ os2pathname(r->filename);
+ execv("CMD.EXE", create_argv_cmd(r->pool, argv0, r->args, r->filename));
+ } else {
+ execv(r->filename, create_argv(r->pool, argv0, r->args));
+ }
+ }
+#else
+ if((!r->args) || (!r->args[0]) || (ind(r->args,'=') >= 0))
+ execle(r->filename, argv0, NULL, env);
+ else
+ execve(r->filename, create_argv(r->pool, argv0, r->args), env);
+#endif
+
+ /* Uh oh. Still here. Where's the kaboom? There was supposed to be an
+ * EARTH-shattering kaboom!
+ *
+ * Oh, well. Muddle through as best we can...
+ *
+ * (NB we can't use log_error, or anything like that, because we
+ * just closed the file descriptor which r->server->error_log
+ * was tied to in cleanup_for_exec(). It's only available on stderr
+ * now, so that's what we use).
+ */
+
+ sprintf(err_string,
+ "exec of %s failed, errno is %d\n", r->filename, errno);
+ write(2, err_string, strlen(err_string));
+ exit(0);
+}
+
+int cgi_handler (request_rec *r)
+{
+ int nph;
+ char *argv0;
+ FILE *script_out, *script_in;
+ char argsbuffer[HUGE_STRING_LEN];
+ int is_included = !strcmp (r->protocol, "INCLUDED");
+ char *lenp = table_get (r->headers_in, "Content-length");
+
+ struct cgi_child_stuff cld;
+
+ if((argv0 = strrchr(r->filename,'/')) != NULL)
+ argv0++;
+ else argv0 = r->filename;
+
+ nph = !(strncmp(argv0,"nph-",4));
+
+ if (!(allow_options (r) & OPT_EXECCGI) && !is_scriptaliased (r)) {
+ log_reason("Options ExecCGI is off in this directory", r->filename, r);
+ return FORBIDDEN;
+ }
+ if (nph && is_included) {
+ log_reason("attempt to include NPH CGI script", r->filename, r);
+ return FORBIDDEN;
+ }
+
+ if (S_ISDIR(r->finfo.st_mode)) {
+ log_reason("attempt to invoke directory as script", r->filename, r);
+ return FORBIDDEN;
+ }
+ if (r->finfo.st_mode == 0) {
+ log_reason("script not found or unable to stat", r->filename, r);
+ return NOT_FOUND;
+ }
+ if(!can_exec(&r->finfo)) {
+ log_reason("file permissions deny server execution", r->filename, r);
+ return FORBIDDEN;
+ }
+ if ((r->method_number == M_POST || r->method_number == M_PUT)
+ && !lenp) {
+ log_reason("POST or PUT without Content-length:", r->filename, r);
+ return BAD_REQUEST;
+ }
+
+ add_common_vars (r);
+ cld.argv0 = argv0; cld.r = r; cld.nph = nph;
+
+#ifdef __EMX__
+ if (r->method_number == M_POST || r->method_number == M_PUT) {
+ int len_to_read = atoi (lenp);
+
+ if (len_to_read > HUGE_STRING_LEN) len_to_read = HUGE_STRING_LEN;
+ read_client_block (r, argsbuffer, len_to_read);
+
+ if (!spawn_child_os2 (r->connection->pool, cgi_child, (void *)&cld,
+ nph ? just_wait : kill_after_timeout,
+ &script_out, &script_in, argsbuffer, atoi(lenp))) {
+ log_reason ("couldn't spawn child process", r->filename, r);
+ return SERVER_ERROR;
+ }
+ } else {
+ if (!spawn_child (r->connection->pool, cgi_child, (void *)&cld,
+ nph ? just_wait : kill_after_timeout,
+ &script_out, &script_in)) {
+ log_reason ("couldn't spawn child process", r->filename, r);
+ return SERVER_ERROR;
+ }
+ }
+
+#else
+ if (!spawn_child (r->connection->pool, cgi_child, (void *)&cld,
+ nph ? just_wait : kill_after_timeout,
+ &script_out, nph ? NULL : &script_in)) {
+ log_reason ("couldn't spawn child process", r->filename, r);
+ return SERVER_ERROR;
+ }
+#endif
+
+ /* Transfer any put/post args, CERN style...
+ * Note that if a buggy script fails to read everything we throw
+ * at it, or a buggy client sends too much, we get a SIGPIPE, so
+ * we have to ignore SIGPIPE while doing this. CERN does the same
+ * (and in fact, they pretty nearly guarantee themselves a SIGPIPE
+ * on every invocation by chasing the real client data with a
+ * spurious newline).
+ */
+
+#ifndef __EMX__
+ if (r->method_number == M_POST || r->method_number == M_PUT) {
+ void (*handler)();
+ int remaining = atoi (lenp);
+
+ hard_timeout ("copy script args", r);
+ handler = signal (SIGPIPE, SIG_IGN);
+
+ while ((remaining > 0))
+ {
+ int len_read, len_to_read = remaining;
+
+ if (len_to_read > HUGE_STRING_LEN) len_to_read = HUGE_STRING_LEN;
+
+ len_read = read_client_block (r, argsbuffer, len_to_read);
+ if (len_read == 0)
+ break;
+ if (fwrite (argsbuffer, 1, len_read, script_out) == 0)
+ break;
+ remaining -= len_read;
+ }
+
+ /* If script stopped reading early, soak up remaining stuff from
+ * client...
+ */
+
+ while (remaining > 0) {
+ int len_read, len_to_read = remaining;
+ if (len_to_read > HUGE_STRING_LEN) len_to_read = HUGE_STRING_LEN;
+
+ len_read = read_client_block (r, argsbuffer, len_to_read);
+ if (len_read == 0) break;
+ }
+
+ fflush (script_out);
+ signal (SIGPIPE, handler);
+
+ kill_timeout (r);
+ }
+#endif
+
+ pfclose (r->connection->pool, script_out);
+
+ /* Handle script return... */
+ if (script_in && !nph) {
+ char *location;
+ int ret;
+
+ if ((ret = scan_script_header(r, script_in)))
+ return ret;
+
+ location = table_get (r->headers_out, "Location");
+
+ if (location && location[0] == '/' && r->status == 200) {
+
+ /* Soak up all the script output */
+ hard_timeout ("read from script", r);
+ while (fgets(argsbuffer, HUGE_STRING_LEN-1, script_in) != NULL)
+ continue;
+ kill_timeout (r);
+
+
+ /* This redirect needs to be a GET no matter what the original
+ * method was.
+ */
+ r->method = pstrdup(r->pool, "GET");
+ r->method_number = M_GET;
+
+ internal_redirect_handler (location, r);
+ return OK;
+ }
+ else if (location && r->status == 200) {
+ /* XX Note that if a script wants to produce its own Redirect
+ * body, it now has to explicitly *say* "Status: 302"
+ */
+ return REDIRECT;
+ }
+
+ hard_timeout ("send script output", r);
+ send_http_header(r);
+ if(!r->header_only) send_fd (script_in, r);
+ kill_timeout (r);
+ pfclose (r->connection->pool, script_in);
+ }
+
+#ifdef __EMX__
+ if (nph) {
+ while (fgets(argsbuffer, HUGE_STRING_LEN-1, script_in) != NULL) {
+ bputs(argsbuffer, r->connection->client);
+ }
+ }
+#endif
+
+ return OK; /* NOT r->status, even if it has changed. */
+}
+
+handler_rec cgi_handlers[] = {
+{ CGI_MAGIC_TYPE, cgi_handler },
+{ "cgi-script", cgi_handler },
+{ NULL }
+};
+
+module cgi_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ NULL, /* dir config creater */
+ NULL, /* dir merger --- default is to override */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ NULL, /* command table */
+ cgi_handlers, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* fixups */
+ NULL /* logger */
+};
diff --git a/RELEASE_1_1_X/src/modules/standard/mod_digest.c b/RELEASE_1_1_X/src/modules/standard/mod_digest.c
new file mode 100644
index 00000000000..3ac81be4aff
--- /dev/null
+++ b/RELEASE_1_1_X/src/modules/standard/mod_digest.c
@@ -0,0 +1,356 @@
+
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+
+/*
+ * mod_digest: MD5 digest authentication
+ *
+ * by Alexei Kosut
+ * based on mod_auth, by Rob McCool and Robert S. Thau
+ *
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "http_protocol.h"
+#include "util_md5.h"
+
+typedef struct digest_config_struct {
+ char *pwfile;
+} digest_config_rec;
+
+typedef struct digest_header_struct {
+ char *username;
+ char *realm;
+ char *nonce;
+ char *requested_uri;
+ char *digest;
+} digest_header_rec;
+
+void *create_digest_dir_config (pool *p, char *d)
+{
+ return pcalloc (p, sizeof(digest_config_rec));
+}
+
+command_rec digest_cmds[] = {
+{ "AuthDigestFile", set_string_slot,
+ (void*)XtOffsetOf(digest_config_rec,pwfile), OR_AUTHCFG, TAKE1, NULL },
+{ NULL }
+};
+
+module digest_module;
+
+char *get_hash(request_rec *r, char *user, char *auth_pwfile)
+{
+ FILE *f;
+ char l[MAX_STRING_LEN];
+ char *rpw, *w, *x;
+
+ if(!(f=pfopen(r->pool, auth_pwfile, "r"))) {
+ log_reason ("Could not open password file", auth_pwfile, r);
+ return NULL;
+ }
+ while(!(cfg_getline(l,MAX_STRING_LEN,f))) {
+ if((l[0] == '#') || (!l[0])) continue;
+ rpw = l;
+ w = getword(r->pool, &rpw, ':');
+ x = getword(r->pool, &rpw, ':');
+
+ if(x && w && !strcmp(user,w) && !strcmp(auth_name(r), x)) {
+ pfclose(r->pool, f);
+ return pstrdup (r->pool, rpw);
+ }
+ }
+ pfclose(r->pool, f);
+ return NULL;
+}
+
+/* Parse the Authorization header, if it exists */
+
+int get_digest_rec(request_rec *r, digest_header_rec *response) {
+ char *auth_line = table_get(r->headers_in, "Authorization");
+ int l;
+ int s = 0, vk = 0, vv = 0;
+ char *t, *key, *value;
+
+ if (!(t = auth_type(r)) || strcasecmp(t, "Digest"))
+ return DECLINED;
+
+ if (!auth_name (r)) {
+ log_reason ("need AuthName", r->uri, r);
+ return SERVER_ERROR;
+ }
+
+ if (!auth_line) {
+ note_digest_auth_failure (r);
+ return AUTH_REQUIRED;
+ }
+
+ if (strcmp(getword (r->pool, &auth_line, ' '), "Digest")) {
+ /* Client tried to authenticate using wrong auth scheme */
+ log_reason ("client used wrong authentication scheme", r->uri, r);
+ note_digest_auth_failure (r);
+ return AUTH_REQUIRED;
+ }
+
+ l = strlen(auth_line);
+
+ key=palloc(r->pool,l);
+ value=palloc(r->pool,l);
+
+ /* There's probably a better way to do this, but for the time being... */
+
+#define D_KEY 0
+#define D_VALUE 1
+#define D_STRING 2
+#define D_EXIT -1
+
+ while (s != D_EXIT) {
+ switch (s) {
+ case D_STRING:
+ if (auth_line[0] == '\"') {
+ s = D_VALUE;
+ }
+ else {
+ value[vv] = auth_line[0];
+ vv++;
+ }
+ auth_line++;
+ break;
+
+ case D_VALUE:
+ if (isalnum(auth_line[0])) {
+ value[vv] = auth_line[0];
+ vv++;
+ }
+ else if (auth_line[0] == '\"') {
+ s = D_STRING;
+ }
+ else {
+ value[vv] = '\0';
+
+ if (!strcasecmp(key, "username"))
+ response->username = pstrdup(r->pool, value);
+ else if (!strcasecmp(key, "realm"))
+ response->realm = pstrdup(r->pool, value);
+ else if (!strcasecmp(key, "nonce"))
+ response->nonce = pstrdup(r->pool, value);
+ else if (!strcasecmp(key, "uri"))
+ response->requested_uri = pstrdup(r->pool, value);
+ else if (!strcasecmp(key, "response"))
+ response->digest = pstrdup(r->pool, value);
+
+ vv = 0;
+ s = D_KEY;
+ }
+ auth_line++;
+ break;
+
+ case D_KEY:
+ if (isalnum(auth_line[0])) {
+ key[vk] = auth_line[0];
+ vk++;
+ }
+ else if (auth_line[0] == '=') {
+ key[vk] = '\0';
+ vk = 0;
+ s = D_VALUE;
+ }
+ auth_line++;
+ break;
+ }
+
+ if (auth_line[-1] == '\0')
+ s = D_EXIT;
+ }
+
+ if (!response->username || !response->realm || !response->nonce ||
+ !response->requested_uri || !response->digest) {
+ note_digest_auth_failure (r);
+ return AUTH_REQUIRED;
+ }
+
+ r->connection->user = response->username;
+ r->connection->auth_type = "Digest";
+
+ return OK;
+}
+
+/* The actual MD5 code... whee */
+
+char *find_digest(request_rec *r, digest_header_rec *h, char *a1) {
+ return md5(r->pool,
+ (unsigned char *)pstrcat(r->pool, a1, ":", h->nonce, ":",
+ md5(r->pool,
+ (unsigned char *)pstrcat(r->pool,r->method,":",
+ h->requested_uri,NULL)),
+ NULL));
+}
+
+/* These functions return 0 if client is OK, and proper error status
+ * if not... either AUTH_REQUIRED, if we made a check, and it failed, or
+ * SERVER_ERROR, if things are so totally confused that we couldn't
+ * figure out how to tell if the client is authorized or not.
+ *
+ * If they return DECLINED, and all other modules also decline, that's
+ * treated by the server core as a configuration error, logged and
+ * reported as such.
+ */
+
+/* Determine user ID, and check if it really is that user, for HTTP
+ * basic authentication...
+ */
+
+int authenticate_digest_user (request_rec *r)
+{
+ digest_config_rec *sec =
+ (digest_config_rec *)get_module_config (r->per_dir_config,
+ &digest_module);
+ digest_header_rec *response = pcalloc (r->pool, sizeof(digest_header_rec));
+ conn_rec *c = r->connection;
+ char *a1;
+ char errstr[MAX_STRING_LEN];
+ int res;
+
+ if ((res = get_digest_rec (r, response))) return res;
+
+ if(!sec->pwfile)
+ return DECLINED;
+
+ if (!(a1 = get_hash(r, c->user, sec->pwfile))) {
+ sprintf(errstr,"user %s not found",c->user);
+ log_reason (errstr, r->uri, r);
+ note_digest_auth_failure (r);
+ return AUTH_REQUIRED;
+ }
+ /* anyone know where the prototype for crypt is? */
+ if(strcmp(response->digest, find_digest(r, response, a1))) {
+ sprintf(errstr,"user %s: password mismatch",c->user);
+ log_reason (errstr, r->uri, r);
+ note_digest_auth_failure (r);
+ return AUTH_REQUIRED;
+ }
+ return OK;
+}
+
+/* Checking ID */
+
+int digest_check_auth (request_rec *r) {
+ char *user = r->connection->user;
+ int m = r->method_number;
+ int method_restricted = 0;
+ register int x;
+ char *t, *w;
+ array_header *reqs_arr;
+ require_line *reqs;
+
+ if (!(t = auth_type(r)) || strcasecmp(t, "Digest"))
+ return DECLINED;
+
+ reqs_arr = requires (r);
+ /* If there is no "requires" directive,
+ * then any user will do.
+ */
+ if (!reqs_arr)
+ return OK;
+ reqs = (require_line *)reqs_arr->elts;
+
+ for(x=0; x < reqs_arr->nelts; x++) {
+
+ if (! (reqs[x].method_mask & (1 << m))) continue;
+
+ method_restricted = 1;
+
+ t = reqs[x].requirement;
+ w = getword(r->pool, &t, ' ');
+ if(!strcmp(w,"valid-user"))
+ return OK;
+ else if(!strcmp(w,"user")) {
+ while(t[0]) {
+ w = getword_conf (r->pool, &t);
+ if(!strcmp(user,w))
+ return OK;
+ }
+ }
+ else
+ return DECLINED;
+ }
+
+ if (!method_restricted)
+ return OK;
+
+ note_digest_auth_failure(r);
+ return AUTH_REQUIRED;
+}
+
+module digest_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ create_digest_dir_config, /* dir config creater */
+ NULL, /* dir merger --- default is to override */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ digest_cmds, /* command table */
+ NULL, /* handlers */
+ NULL, /* filename translation */
+ authenticate_digest_user, /* check_user_id */
+ digest_check_auth, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* fixups */
+ NULL /* logger */
+};
+
+
diff --git a/RELEASE_1_1_X/src/modules/standard/mod_dir.c b/RELEASE_1_1_X/src/modules/standard/mod_dir.c
new file mode 100644
index 00000000000..c30b57c58d3
--- /dev/null
+++ b/RELEASE_1_1_X/src/modules/standard/mod_dir.c
@@ -0,0 +1,846 @@
+
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+
+/*
+ * http_dir.c: Handles the on-the-fly html index generation
+ *
+ * Rob McCool
+ * 3/23/93
+ *
+ * Adapted to Shambhala by rst.
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_request.h"
+#include "http_protocol.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "util_script.h"
+
+module dir_module;
+
+/****************************************************************
+ *
+ * Handling configuration directives...
+ */
+
+#define FANCY_INDEXING 1 /* Indexing options */
+#define ICONS_ARE_LINKS 2
+#define SCAN_HTML_TITLES 4
+#define SUPPRESS_LAST_MOD 8
+#define SUPPRESS_SIZE 16
+#define SUPPRESS_DESC 32
+
+struct item {
+ char *type;
+ char *apply_to;
+ char *apply_path;
+ char *data;
+};
+
+typedef struct dir_config_struct {
+
+ char *default_icon;
+ char *index_names;
+
+ array_header *icon_list, *alt_list, *desc_list, *ign_list;
+ array_header *hdr_list, *rdme_list, *opts_list;
+
+} dir_config_rec;
+
+char c_by_encoding, c_by_type, c_by_path;
+
+#define BY_ENCODING &c_by_encoding
+#define BY_TYPE &c_by_type
+#define BY_PATH &c_by_path
+
+void push_item(array_header *arr, char *type, char *to, char *path, char *data)
+{
+ struct item *p = (struct item *)push_array(arr);
+
+ if (!to) to = "";
+ if (!path) path = "";
+
+ p->type = type;
+ p->data = data ? pstrdup(arr->pool, data): NULL;
+ p->apply_path = pstrcat(arr->pool, path, "*", NULL);
+
+ if((type == BY_PATH) && (!is_matchexp(to)))
+ p->apply_to = pstrcat (arr->pool, "*", to, NULL);
+ else if (to)
+ p->apply_to = pstrdup (arr->pool, to);
+ else
+ p->apply_to = NULL;
+}
+
+char *add_alt(cmd_parms *cmd, void *d, char *alt, char *to)
+{
+ if (cmd->info == BY_PATH)
+ if(!strcmp(to,"**DIRECTORY**"))
+ to = "^^DIRECTORY^^";
+
+ push_item(((dir_config_rec *)d)->alt_list, cmd->info, to, cmd->path, alt);
+ return NULL;
+}
+
+char *add_icon(cmd_parms *cmd, void *d, char *icon, char *to)
+{
+ char *iconbak = pstrdup (cmd->pool, icon);
+
+ if(icon[0] == '(') {
+ char *alt = getword (cmd->pool, &iconbak, ',');
+ iconbak[strlen(iconbak) - 1] = '\0'; /* Lose closing paren */
+ add_alt(cmd, d, &alt[1], to);
+ }
+ if(cmd->info == BY_PATH)
+ if(!strcmp(to,"**DIRECTORY**"))
+ to = "^^DIRECTORY^^";
+
+ push_item(((dir_config_rec *)d)->icon_list, cmd->info, to, cmd->path,
+ iconbak);
+ return NULL;
+}
+
+char *add_desc(cmd_parms *cmd, void *d, char *desc, char *to)
+{
+ push_item(((dir_config_rec *)d)->desc_list, cmd->info, to, cmd->path,desc);
+ return NULL;
+}
+
+char *add_ignore(cmd_parms *cmd, void *d, char *ext) {
+ push_item(((dir_config_rec *)d)->ign_list, 0, ext, cmd->path, NULL);
+ return NULL;
+}
+
+char *add_header(cmd_parms *cmd, void *d, char *name) {
+ push_item(((dir_config_rec *)d)->hdr_list, 0, NULL, cmd->path, name);
+ return NULL;
+}
+
+char *add_readme(cmd_parms *cmd, void *d, char *name) {
+ push_item(((dir_config_rec *)d)->rdme_list, 0, NULL, cmd->path, name);
+ return NULL;
+}
+
+
+char *add_opts_int(cmd_parms *cmd, void *d, int opts) {
+ push_item(((dir_config_rec *)d)->opts_list, (char*)opts, NULL,
+ cmd->path, NULL);
+ return NULL;
+}
+
+char *fancy_indexing (cmd_parms *cmd, void *d, int arg)
+{
+ return add_opts_int (cmd, d, arg? FANCY_INDEXING : 0);
+}
+
+char *add_opts(cmd_parms *cmd, void *d, char *optstr) {
+ char *w;
+ int opts = 0;
+
+ while(optstr[0]) {
+ w = getword_conf(cmd->pool, &optstr);
+ if(!strcasecmp(w,"FancyIndexing"))
+ opts |= FANCY_INDEXING;
+ else if(!strcasecmp(w,"IconsAreLinks"))
+ opts |= ICONS_ARE_LINKS;
+ else if(!strcasecmp(w,"ScanHTMLTitles"))
+ opts |= SCAN_HTML_TITLES;
+ else if(!strcasecmp(w,"SuppressLastModified"))
+ opts |= SUPPRESS_LAST_MOD;
+ else if(!strcasecmp(w,"SuppressSize"))
+ opts |= SUPPRESS_SIZE;
+ else if(!strcasecmp(w,"SuppressDescription"))
+ opts |= SUPPRESS_DESC;
+ else if(!strcasecmp(w,"None"))
+ opts = 0;
+ else
+ return "Invalid directory indexing option";
+ }
+ return add_opts_int(cmd, d, opts);
+}
+
+#define DIR_CMD_PERMS OR_INDEXES
+
+command_rec dir_cmds[] = {
+{ "AddIcon", add_icon, BY_PATH, DIR_CMD_PERMS, ITERATE2,
+ "an icon URL followed by one or more filenames" },
+{ "AddIconByType", add_icon, BY_TYPE, DIR_CMD_PERMS, ITERATE2,
+ "an icon URL followed by one or more MIME types" },
+{ "AddIconByEncoding", add_icon, BY_ENCODING, DIR_CMD_PERMS, ITERATE2,
+ "an icon URL followed by one or more content encodings" },
+{ "AddAlt", add_alt, BY_PATH, DIR_CMD_PERMS, ITERATE2,
+ "alternate descriptive text followed by one or more filenames" },
+{ "AddAltByType", add_alt, BY_TYPE, DIR_CMD_PERMS, ITERATE2,
+ "alternate descriptive text followed by one or more MIME types" },
+{ "AddAltByEncoding", add_alt, BY_ENCODING, DIR_CMD_PERMS, ITERATE2,
+ "alternate descriptive text followed by one or more content encodings" },
+{ "IndexOptions", add_opts, NULL, DIR_CMD_PERMS, RAW_ARGS,
+ "one or more index options" },
+{ "IndexIgnore", add_ignore, NULL, DIR_CMD_PERMS, ITERATE,
+ "one or more file extensions" },
+{ "AddDescription", add_desc, BY_PATH, DIR_CMD_PERMS, ITERATE2,
+ "Descriptive text followed by one or more filenames" },
+{ "HeaderName", add_header, NULL, DIR_CMD_PERMS, TAKE1, "a filename" },
+{ "ReadmeName", add_readme, NULL, DIR_CMD_PERMS, TAKE1, "a filename" },
+{ "FancyIndexing", fancy_indexing, NULL, DIR_CMD_PERMS, FLAG, NULL },
+{ "DefaultIcon", set_string_slot,
+ (void*)XtOffsetOf(dir_config_rec, default_icon),
+ DIR_CMD_PERMS, TAKE1, "an icon URL"},
+{ "DirectoryIndex", set_string_slot,
+ (void*)XtOffsetOf(dir_config_rec, index_names),
+ DIR_CMD_PERMS, RAW_ARGS, NULL },
+{ NULL }
+};
+
+void *create_dir_config (pool *p, char *dummy)
+{
+ dir_config_rec *new =
+ (dir_config_rec *) pcalloc (p, sizeof(dir_config_rec));
+
+ new->index_names = NULL;
+ new->icon_list = make_array (p, 4, sizeof (struct item));
+ new->alt_list = make_array (p, 4, sizeof (struct item));
+ new->desc_list = make_array (p, 4, sizeof (struct item));
+ new->ign_list = make_array (p, 4, sizeof (struct item));
+ new->hdr_list = make_array (p, 4, sizeof (struct item));
+ new->rdme_list = make_array (p, 4, sizeof (struct item));
+ new->opts_list = make_array (p, 4, sizeof (struct item));
+
+ return (void *)new;
+}
+
+void *merge_dir_configs (pool *p, void *basev, void *addv)
+{
+ dir_config_rec *new=(dir_config_rec*)pcalloc (p, sizeof(dir_config_rec));
+ dir_config_rec *base = (dir_config_rec *)basev;
+ dir_config_rec *add = (dir_config_rec *)addv;
+
+ new->default_icon = add->default_icon?add->default_icon:base->default_icon;
+ new->index_names = add->index_names? add->index_names: base->index_names;
+
+ new->alt_list = append_arrays (p, add->alt_list, base->alt_list);
+ new->ign_list = append_arrays (p, add->ign_list, base->ign_list);
+ new->hdr_list = append_arrays (p, add->hdr_list, base->hdr_list);
+ new->desc_list = append_arrays (p, add->desc_list, base->desc_list);
+ new->icon_list = append_arrays (p, add->icon_list, base->icon_list);
+ new->rdme_list = append_arrays (p, add->rdme_list, base->rdme_list);
+ new->opts_list = append_arrays (p, add->opts_list, base->opts_list);
+
+ return new;
+}
+
+/****************************************************************
+ *
+ * Looking things up in config entries...
+ */
+
+/* Structure used to hold entries when we're actually building an index */
+
+struct ent {
+ char *name;
+ char *icon;
+ char *alt;
+ char *desc;
+ size_t size;
+ time_t lm;
+ struct ent *next;
+};
+
+char *find_item(request_rec *r, array_header *list, int path_only) {
+ char *content_type = r->content_type;
+ char *content_encoding = r->content_encoding;
+ char *path = r->filename;
+
+ struct item *items = (struct item *)list->elts;
+ int i;
+
+ for (i = 0; i < list->nelts; ++i) {
+ struct item *p = &items[i];
+
+ /* Special cased for ^^DIRECTORY^^ and ^^BLANKICON^^ */
+ if((path[0] == '^') || (!strcmp_match(path,p->apply_path))) {
+ if(!*(p->apply_to))
+ return p->data;
+ else if(p->type == BY_PATH || path[0] == '^') {
+ if(!strcmp_match(path,p->apply_to))
+ return p->data;
+ } else if(!path_only) {
+ if(!content_encoding) {
+ if(p->type == BY_TYPE) {
+ if(content_type && !strcmp_match(content_type,p->apply_to))
+ return p->data;
+ }
+ } else {
+ if(p->type == BY_ENCODING) {
+ if(!strcmp_match(content_encoding,p->apply_to))
+ return p->data;
+ }
+ }
+ }
+ }
+ }
+ return NULL;
+}
+
+#define find_icon(d,p,t) find_item(p,d->icon_list,t)
+#define find_alt(d,p,t) find_item(p,d->alt_list,t)
+#define find_desc(d,p) find_item(p,d->desc_list,0)
+#define find_header(d,p) find_item(p,d->hdr_list,0)
+#define find_readme(d,p) find_item(p,d->rdme_list,0)
+
+char *find_default_icon (dir_config_rec *d, char *bogus_name)
+{
+ request_rec r;
+
+ /* Bleah. I tried to clean up find_item, and it lead to this bit
+ * of ugliness. Note that the fields initialized are precisely
+ * those that find_item looks at...
+ */
+
+ r.filename = bogus_name;
+ r.content_type = r.content_encoding = NULL;
+
+ return find_item (&r, d->icon_list, 1);
+}
+
+int ignore_entry(dir_config_rec *d, char *path) {
+ array_header *list = d->ign_list;
+ struct item *items = (struct item *)list->elts;
+ char *tt;
+ int i;
+
+ if((tt=strrchr(path,'/')) == NULL)
+ tt=path;
+ else {
+ tt++;
+ }
+
+ for (i = 0; i < list->nelts; ++i) {
+ struct item *p = &items[i];
+ char *ap;
+
+ if((ap=strrchr(p->apply_to,'/')) == NULL)
+ ap=p->apply_to;
+ else
+ ap++;
+
+ if(!strcmp_match(path,p->apply_path) && !strcmp_match(tt,ap))
+ return 1;
+ }
+ return 0;
+}
+
+int find_opts(dir_config_rec *d, request_rec *r) {
+ char *path = r->filename;
+ array_header *list = d->opts_list;
+ struct item *items = (struct item *)list->elts;
+ int i;
+
+ for (i = 0; i < list->nelts; ++i) {
+ struct item *p = &items[i];
+
+ if(!strcmp_match(path,p->apply_path))
+ return (int)p->type;
+ }
+ return 0;
+}
+
+/*****************************************************************
+ *
+ * Actually generating output
+ */
+
+
+int insert_readme(char *name, char *readme_fname, int rule, request_rec *r) {
+ char *fn;
+ FILE *f;
+ struct stat finfo;
+ int plaintext=0;
+
+ fn = make_full_path(r->pool, name, readme_fname);
+ fn = pstrcat(r->pool, fn, ".html", NULL);
+ if(stat(fn,&finfo) == -1) {
+ /* A brief fake multiviews search for README.html */
+ fn[strlen(fn)-5] = '\0';
+ if(stat(fn,&finfo) == -1)
+ return 0;
+ plaintext=1;
+ if(rule) rputs("
\n", r);
+ rputs("\n", r);
+ }
+ else if (rule) rputs("
\n", r);
+ if(!(f = pfopen(r->pool,fn,"r")))
+ return 0;
+ if (!plaintext)
+ send_fd(f, r);
+ else
+ {
+ char buf[IOBUFSIZE+1];
+ int i, n, c, ch;
+ while (!feof(f))
+ {
+ do n = fread(buf, sizeof(char), IOBUFSIZE, f);
+ while (n == -1 && ferror(f) && errno == EINTR);
+ if (n == -1 || n == 0) break;
+ buf[n] = '\0';
+ c = 0;
+ while (c < n)
+ {
+ for (i=c; i < n; i++)
+ if (buf[i] == '<' || buf[i] == '>' || buf[i] == '&') break;
+ ch = buf[i];
+ buf[i] = '\0';
+ rputs(&buf[c], r);
+ if (ch == '<') rputs("<", r);
+ else if (ch == '>') rputs(">", r);
+ else if (ch == '&') rputs("&", r);
+ c = i + 1;
+ }
+ }
+ }
+ pfclose(r->pool, f);
+ if(plaintext)
+ rputs("
\n", r);
+ return 1;
+}
+
+
+char *find_title(request_rec *r) {
+ char titlebuf[MAX_STRING_LEN], *find = "";
+ FILE *thefile = NULL;
+ int x,y,n,p;
+
+ if (r->content_type && !strcmp(r->content_type,"text/html") && !r->content_encoding) {
+ if(!(thefile = pfopen(r->pool, r->filename,"r")))
+ return NULL;
+ n = fread(titlebuf,sizeof(char),MAX_STRING_LEN - 1,thefile);
+ titlebuf[n] = '\0';
+ for(x=0,p=0;titlebuf[x];x++) {
+ if(toupper(titlebuf[x]) == find[p]) {
+ if(!find[++p]) {
+ if((p = ind(&titlebuf[++x],'<')) != -1)
+ titlebuf[x+p] = '\0';
+ /* Scan for line breaks for Tanmoy's secretary */
+ for(y=x;titlebuf[y];y++)
+ if((titlebuf[y] == CR) || (titlebuf[y] == LF))
+ titlebuf[y] = ' ';
+ pfclose (r->pool, thefile);
+ return pstrdup(r->pool, &titlebuf[x]);
+ }
+ } else p=0;
+ }
+ pfclose(r->pool, thefile);
+ }
+ return NULL;
+}
+
+struct ent *make_dir_entry(char *name, int dir_opts,
+ dir_config_rec *d, request_rec *r)
+{
+ struct ent *p;
+
+ if((name[0] == '.') && (!name[1]))
+ return(NULL);
+
+ if (ignore_entry(d, make_full_path (r->pool, r->filename, name)))
+ return(NULL);
+
+ p=(struct ent *)pcalloc(r->pool, sizeof(struct ent));
+ p->name = pstrdup (r->pool, name);
+ p->size = -1;
+ p->icon = NULL;
+ p->alt = NULL;
+ p->desc = NULL;
+ p->lm = -1;
+
+ if(dir_opts & FANCY_INDEXING) {
+ request_rec *rr = sub_req_lookup_file (name, r);
+
+ if (rr->finfo.st_mode != 0) {
+ p->lm = rr->finfo.st_mtime;
+ if(S_ISDIR(rr->finfo.st_mode)) {
+ if(!(p->icon = find_icon(d,rr,1)))
+ p->icon = find_default_icon(d,"^^DIRECTORY^^");
+ if(!(p->alt = find_alt(d,rr,1)))
+ p->alt = "DIR";
+ p->size = -1;
+ p->name = pstrcat (r->pool, name, "/", NULL);
+ }
+ else {
+ p->icon = find_icon(d, rr, 0);
+ p->alt = find_alt(d, rr, 0);
+ p->size = rr->finfo.st_size;
+ }
+ }
+
+ p->desc = find_desc(d, rr);
+
+ if((!p->desc) && (dir_opts & SCAN_HTML_TITLES))
+ p->desc = pstrdup (r->pool, find_title(rr));
+
+ destroy_sub_req (rr);
+ }
+ return(p);
+}
+
+char *terminate_description(dir_config_rec *d, char *desc, int dir_opts) {
+ int maxsize = 23;
+ register int x;
+
+ if(dir_opts & SUPPRESS_LAST_MOD) maxsize += 17;
+ if(dir_opts & SUPPRESS_SIZE) maxsize += 7;
+
+ for(x=0;desc[x] && maxsize;x++) {
+ if(desc[x] == '<') {
+ while(desc[x] != '>') {
+ if(!desc[x]) {
+ maxsize = 0;
+ break;
+ }
+ ++x;
+ }
+ }
+ else --maxsize;
+ }
+ if(!maxsize) {
+ desc[x-1] = '>'; /* Grump. */
+ desc[x] = '\0'; /* Double Grump! */
+ }
+ return desc;
+}
+
+void output_directories(struct ent **ar, int n,
+ dir_config_rec *d, request_rec *r, int dir_opts)
+{
+ int x, len;
+ char *name = r->uri;
+ char *tp;
+ pool *scratch = make_sub_pool (r->pool);
+
+ if(name[0] == '\0') name = "/";
+
+ if(dir_opts & FANCY_INDEXING) {
+ rputs("", r);
+ if((tp = find_default_icon(d,"^^BLANKICON^^")))
+ rvputs(r, "
", NULL);
+ rputs("Name ", r);
+ if(!(dir_opts & SUPPRESS_LAST_MOD))
+ rputs("Last modified ", r);
+ if(!(dir_opts & SUPPRESS_SIZE))
+ rputs("Size ", r);
+ if(!(dir_opts & SUPPRESS_DESC))
+ rputs("Description", r);
+ rputs("\n
\n", r);
+ }
+ else {
+ rputs("
", r);
+ }
+
+ for(x=0;xname,"../")) || (!strcmp(ar[x]->name,".."))) {
+ char *t = make_full_path (scratch, name, "../");
+ getparents(t);
+ if(t[0] == '\0') t = "/";
+ anchor = pstrcat (scratch, "", NULL);
+ t2 = "Parent Directory ";
+ }
+ else {
+ t = ar[x]->name;
+ len = strlen(t);
+ if(len > 23) {
+ t2 = pstrdup(scratch, t);
+ t2[21] = '.';
+ t2[22] = '.';
+ t2[23] = '\0';
+ t2 = escape_html(scratch, t2);
+ t2 = pstrcat(scratch, t2, "", NULL);
+ } else
+ {
+ char buff[24]=" ";
+ t2 = escape_html(scratch, t);
+ buff[23-len] = '\0';
+ t2 = pstrcat(scratch, t2, "", buff, NULL);
+ }
+ anchor = pstrcat (scratch, "", NULL);
+ }
+
+ if(dir_opts & FANCY_INDEXING) {
+ if(dir_opts & ICONS_ARE_LINKS)
+ rputs(anchor, r);
+ if((ar[x]->icon) || d->default_icon) {
+ rvputs(r, "
icon ?
+ ar[x]->icon : d->default_icon),
+ "\" ALT=\"[", (ar[x]->alt ? ar[x]->alt : " "),
+ "]\">", NULL);
+ }
+ if(dir_opts & ICONS_ARE_LINKS)
+ rputs("", r);
+
+ rvputs(r," ", anchor, t2, NULL);
+ if(!(dir_opts & SUPPRESS_LAST_MOD)) {
+ if(ar[x]->lm != -1) {
+ char time[MAX_STRING_LEN];
+ struct tm *ts = localtime(&ar[x]->lm);
+ strftime(time,MAX_STRING_LEN,"%d-%b-%y %H:%M ",ts);
+ rputs(time, r);
+ }
+ else {
+ rputs(" ", r);
+ }
+ }
+ if(!(dir_opts & SUPPRESS_SIZE)) {
+ send_size(ar[x]->size,r);
+ rputs(" ", r);
+ }
+ if(!(dir_opts & SUPPRESS_DESC)) {
+ if(ar[x]->desc) {
+ rputs(terminate_description(d, ar[x]->desc, dir_opts), r);
+ }
+ }
+ }
+ else
+ rvputs(r, "- ", anchor," ", t2, NULL);
+ rputc('\n', r);
+ }
+ if(dir_opts & FANCY_INDEXING) {
+ rputs("", r);
+ }
+ else {
+ rputs("
", r);
+ }
+}
+
+
+int dsortf(struct ent **s1,struct ent **s2)
+{
+ return(strcmp((*s1)->name,(*s2)->name));
+}
+
+
+int index_directory(request_rec *r, dir_config_rec *dir_conf)
+{
+ char *title_name = escape_html(r->pool, r->uri);
+ char *title_endp;
+ char *name = r->filename;
+
+ DIR *d;
+ struct DIR_TYPE *dstruct;
+ int num_ent=0,x;
+ struct ent *head,*p;
+ struct ent **ar;
+ char *tmp;
+ int dir_opts = find_opts(dir_conf, r);
+
+ if(!(d=opendir(name))) return FORBIDDEN;
+
+ r->content_type = "text/html";
+
+ soft_timeout ("send directory", r);
+ send_http_header(r);
+
+ if (r->header_only) {
+ closedir (d);
+ return 0;
+ }
+
+ /* Spew HTML preamble */
+
+ title_endp = title_name + strlen(title_name) - 1;
+
+ while (title_endp > title_name && *title_endp == '/')
+ *title_endp-- = '\0';
+
+ rvputs(r, "Index of ", title_name, "\n",
+ NULL);
+
+ if((!(tmp = find_header(dir_conf,r))) || (!(insert_readme(name,tmp,0,r))))
+ rvputs(r, "Index of ", title_name, "
\n", NULL);
+
+ /*
+ * Since we don't know how many dir. entries there are, put them into a
+ * linked list and then arrayificate them so qsort can use them.
+ */
+ head=NULL;
+ while((dstruct=readdir(d))) {
+ if((p = make_dir_entry(dstruct->d_name, dir_opts, dir_conf, r))) {
+ p->next=head;
+ head=p;
+ num_ent++;
+ }
+ }
+ ar=(struct ent **) palloc(r->pool, num_ent*sizeof(struct ent *));
+ p=head;
+ x=0;
+ while(p) {
+ ar[x++]=p;
+ p = p->next;
+ }
+
+ qsort((void *)ar,num_ent,sizeof(struct ent *),
+#ifdef ULTRIX_BRAIN_DEATH
+ (int (*))dsortf);
+#else
+ (int (*)(const void *,const void *))dsortf);
+#endif
+ output_directories(ar, num_ent, dir_conf, r, dir_opts);
+ closedir(d);
+
+ if (dir_opts & FANCY_INDEXING)
+ if((tmp = find_readme(dir_conf, r)))
+ insert_readme(name,tmp,1,r);
+ else {
+ rputs("", r);
+ }
+
+ rputs("", r);
+ return 0;
+}
+
+/* The formal handler... */
+
+int handle_dir (request_rec *r)
+{
+ dir_config_rec *d =
+ (dir_config_rec *)get_module_config (r->per_dir_config, &dir_module);
+ char *names_ptr = d->index_names ? d->index_names : DEFAULT_INDEX;
+ int allow_opts = allow_options (r);
+
+ if (r->uri[0] == '\0' || r->uri[strlen(r->uri)-1] != '/') {
+ char* ifile;
+ if (r->args != NULL)
+ ifile = pstrcat (r->pool, escape_uri(r->pool, r->uri),
+ "/", "?", r->args, NULL);
+ else
+ ifile = pstrcat (r->pool, escape_uri(r->pool, r->uri),
+ "/", NULL);
+
+ table_set (r->headers_out, "Location",
+ construct_url(r->pool, ifile, r->server));
+ return REDIRECT;
+ }
+
+ /* KLUDGE --- make the sub_req lookups happen in the right directory.
+ * Fixing this in the sub_req_lookup functions themselves is difficult,
+ * and would probably break virtual includes...
+ */
+
+ r->filename = pstrcat (r->pool, r->filename, "/", NULL);
+
+ while (*names_ptr) {
+
+ char *name_ptr = getword_conf (r->pool, &names_ptr);
+ request_rec *rr = sub_req_lookup_uri (name_ptr, r);
+
+ if (rr->status == 200 && rr->finfo.st_mode != 0) {
+ char* new_uri = escape_uri(r->pool, rr->uri);
+
+ if (rr->args != NULL)
+ new_uri = pstrcat(r->pool, new_uri, "?", rr->args, NULL);
+ else if (r->args != NULL)
+ new_uri = pstrcat(r->pool, new_uri, "?", r->args, NULL);
+
+ destroy_sub_req (rr);
+ internal_redirect (new_uri, r);
+ return OK;
+ }
+
+ destroy_sub_req (rr);
+ }
+
+ if (r->method_number != M_GET) return NOT_IMPLEMENTED;
+
+ /* OK, nothing easy. Trot out the heavy artillery... */
+
+ if (allow_opts & OPT_INDEXES)
+ return index_directory (r, d);
+ else
+ return FORBIDDEN;
+}
+
+
+handler_rec dir_handlers[] = {
+{ DIR_MAGIC_TYPE, handle_dir },
+{ NULL }
+};
+
+module dir_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ create_dir_config, /* dir config creater */
+ merge_dir_configs, /* dir merger --- default is to override */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ dir_cmds, /* command table */
+ dir_handlers, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* fixups */
+ NULL /* logger */
+};
diff --git a/RELEASE_1_1_X/src/modules/standard/mod_dld.c b/RELEASE_1_1_X/src/modules/standard/mod_dld.c
new file mode 100644
index 00000000000..a0bc13c8647
--- /dev/null
+++ b/RELEASE_1_1_X/src/modules/standard/mod_dld.c
@@ -0,0 +1,191 @@
+
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+
+/*
+ * A first stab at dynamic loading, using the GNU dld library
+ * (or at least, an embarassingly old version of it...).
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_conf_globals.h" /* server_argv0. Sigh... */
+#include
+
+/*
+ * The hard part of implementing LoadModule is deciding what to do about
+ * rereading the config files. This proof-of-concept implementation takes the
+ * cheap way out: we only actually load the modules the first time through.
+ */
+
+static int been_there_done_that = 0; /* Loaded the modules yet? */
+static int have_symbol_table = 0;
+
+char *insure_dld_sane()
+{
+ int errcode;
+ char *bin_name;
+
+ if (have_symbol_table) return NULL;
+
+ bin_name = dld_find_executable (server_argv0);
+
+ if ((errcode = dld_init (bin_name))) {
+ dld_perror (server_argv0);
+ return "Cannot find server binary (needed for dynamic linking).";
+ }
+
+ have_symbol_table = 1;
+ return NULL;
+}
+
+char *link_file (pool *p, char *filename)
+{
+ int errcode;
+
+ filename = server_root_relative (p, filename);
+ if ((errcode = dld_link (filename))) {
+ dld_perror (server_argv0);
+ return pstrcat (p, "Cannot load ", filename, " into server", NULL);
+ }
+ return NULL;
+}
+
+char *load_module (cmd_parms *cmd, void *dummy, char *modname, char *filename)
+{
+ char *errname;
+ module *modp;
+
+ if (been_there_done_that) return NULL;
+
+ if ((errname = insure_dld_sane())) return errname;
+ if ((errname = link_file (cmd->pool, filename))) return errname;
+ if (!(modp = (module *)dld_get_symbol (modname))) {
+ return pstrcat (cmd->pool, "Can't find module ", modname,
+ " in file ", filename, NULL);
+ }
+
+ add_module (modp);
+
+ /* Alethea Patch (rws,djw2) - need to run configuration functions
+ in new modules */
+
+ if (modp->create_server_config)
+ ((void**)cmd->server->module_config)[modp->module_index]=
+ (*modp->create_server_config)(cmd->pool, cmd->server);
+
+ if (modp->create_dir_config)
+ ((void**)cmd->server->lookup_defaults)[modp->module_index]=
+ (*modp->create_dir_config)(cmd->pool, NULL);
+
+
+ return NULL;
+}
+
+char *load_file (cmd_parms *cmd, void *dummy, char *filename)
+{
+ char *errname;
+
+ if (been_there_done_that) return NULL;
+
+ if ((errname = insure_dld_sane())) return errname;
+ if ((errname = link_file (cmd->pool, filename))) return errname;
+ return NULL;
+}
+
+void check_loaded_modules (server_rec *dummy, pool *p)
+{
+ if (been_there_done_that) return;
+
+ if (dld_undefined_sym_count > 0) {
+ /* Screwup. Do the best we can to inform the user, and exit */
+ char **bad_syms = dld_list_undefined_sym();
+ int i;
+
+ fprintf(stderr, "Dynamic linking error --- symbols left undefined.\n");
+ fprintf(stderr, "(It may help to relink libraries).\n");
+ fprintf(stderr, "Undefined symbols follow:\n");
+
+ for (i = 0; i < dld_undefined_sym_count; ++i)
+ fprintf (stderr, "%s\n", bad_syms[i]);
+
+ exit (1);
+ }
+
+ been_there_done_that = 1;
+}
+
+command_rec dld_cmds[] = {
+{ "LoadModule", load_module, NULL, RSRC_CONF, TAKE2,
+ "a module name, and the name of a file to load it from"},
+{ "LoadFile", load_file, NULL, RSRC_CONF, ITERATE,
+ "files or libraries to link into the server at runtime"},
+{ NULL }
+};
+
+module dld_module = {
+ STANDARD_MODULE_STUFF,
+ check_loaded_modules, /* initializer */
+ NULL, /* create per-dir config */
+ NULL, /* merge per-dir config */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ dld_cmds, /* command table */
+ NULL, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL /* logger */
+};
diff --git a/RELEASE_1_1_X/src/modules/standard/mod_env.c b/RELEASE_1_1_X/src/modules/standard/mod_env.c
new file mode 100644
index 00000000000..d16acccaa68
--- /dev/null
+++ b/RELEASE_1_1_X/src/modules/standard/mod_env.c
@@ -0,0 +1,257 @@
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+/*
+ * mod_env.c
+ * version 0.0.5
+ * status beta
+ * Pass environment variables to CGI/SSI scripts.
+ *
+ * Andrew Wilson 06.Dec.95
+ *
+ * Change log:
+ * 08.Dec.95 Now allows PassEnv directive to appear more than once in
+ * conf files.
+ * 10.Dec.95 optimisation. getenv() only called at startup and used
+ * to build a fast-to-access table. table used to build
+ * per-server environment for each request.
+ * robustness. better able to handle errors in configuration
+ * files:
+ * 1) PassEnv directive present, but no environment variable listed
+ * 2) PassEnv FOO present, but $FOO not present in environment
+ * 3) no PassEnv directive present
+ * 23.Dec.95 Now allows SetEnv directive with same semantics as 'sh' setenv:
+ * SetEnv Var sets Var to the empty string
+ * SetEnv Var Val sets Var to the value Val
+ * Values containing whitespace should be quoted, eg:
+ * SetEnv Var "this is some text"
+ * Environment variables take their value from the last instance
+ * of PassEnv / SetEnv to be reached in the configuration file.
+ * For example, the sequence:
+ * PassEnv PATH
+ * SetEnv PATH /special/path
+ * Causes PATH to take the value '/special/path'.
+ * 23.Feb.96 Added UnsetEnv directive to allow environment variables
+ * to be removed.
+ * Virtual hosts now 'inherit' parent server environment which
+ * they're able to overwrite with their own directives or
+ * selectively ignore with UnsetEnv.
+ * *** IMPORTANT - the way that virtual hosts inherit their ***
+ * *** environment variables from the default server's ***
+ * *** configuration has changed. You should test your ***
+ * *** configuration carefully before accepting this ***
+ * *** version of the module in a live webserver which used ***
+ * *** older versions of the module. ***
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+
+typedef struct {
+ table *vars;
+ char *unsetenv;
+ int vars_present;
+} env_server_config_rec;
+
+module env_module;
+
+void *create_env_server_config (pool *p, server_rec *dummy)
+{
+ env_server_config_rec *new =
+ (env_server_config_rec *) palloc (p, sizeof(env_server_config_rec));
+ new->vars = make_table (p, 50);
+ new->unsetenv = "";
+ new->vars_present = 0;
+ return (void *) new;
+}
+
+void *merge_env_server_configs (pool *p, void *basev, void *addv)
+{
+ env_server_config_rec *base = (env_server_config_rec *)basev;
+ env_server_config_rec *add = (env_server_config_rec *)addv;
+ env_server_config_rec *new =
+ (env_server_config_rec *)palloc (p, sizeof(env_server_config_rec));
+
+ table *new_table;
+ table_entry *elts;
+
+ int i;
+ char *uenv, *copy;
+
+ /*
+ * new_table = copy_table( p, base->vars );
+ * foreach $element ( @add->vars ) {
+ * table_set( new_table, $element.key, $element.val );
+ * };
+ * foreach $unsetenv ( @UNSETENV ) {
+ * table_unset( new_table, $unsetenv );
+ * }
+ */
+
+ new_table = copy_table( p, base->vars );
+
+ elts = (table_entry *) add->vars->elts;
+
+ for ( i = 0; i < add->vars->nelts; ++i ) {
+ table_set( new_table, elts[i].key, elts[i].val );
+ };
+
+ copy = pstrdup( p, add->unsetenv );
+ uenv = getword_conf( p, © );
+ while ( uenv[0] != '\0' ) {
+ table_unset( new_table, uenv );
+ uenv = getword_conf( p, © );
+ };
+
+ new->vars = new_table;
+
+ new->vars_present = base->vars_present || add->vars_present;
+
+ return new;
+}
+
+char *add_env_module_vars_passed (cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+ env_server_config_rec *sconf =
+ get_module_config (cmd->server->module_config, &env_module);
+ table *vars = sconf->vars;
+ char *env_var;
+ char *name_ptr;
+
+ while (*arg) {
+ name_ptr = getword_conf (cmd->pool, &arg);
+ env_var = getenv(name_ptr);
+ if ( env_var != NULL ) {
+ sconf->vars_present = 1;
+ table_set (vars, name_ptr, env_var);
+ };
+ }
+ return NULL;
+}
+
+char *add_env_module_vars_set (cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+ env_server_config_rec *sconf =
+ get_module_config (cmd->server->module_config, &env_module);
+ table *vars = sconf->vars;
+ char *name, *value;
+
+ name = getword_conf( cmd->pool, &arg );
+ value = getword_conf( cmd->pool, &arg );
+
+ /* name is mandatory, value is optional. no value means
+ * set the variable to an empty string
+ */
+
+
+ if ( (*name == '\0') || (*arg != '\0')) {
+ return "SetEnv takes one or two arguments. An environment variable name and an optional value to pass to CGI." ;
+ };
+
+ sconf->vars_present = 1;
+ table_set (vars, name, value);
+
+ return NULL;
+}
+
+char *add_env_module_vars_unset (cmd_parms *cmd, char *struct_ptr, char *arg)
+{
+ env_server_config_rec *sconf =
+ get_module_config (cmd->server->module_config, &env_module);
+ sconf->unsetenv = sconf->unsetenv ?
+ pstrcat( cmd->pool, sconf->unsetenv, " ", arg, NULL ) :
+ pstrdup( cmd->pool, arg );
+ return NULL;
+}
+
+command_rec env_module_cmds[] = {
+{ "PassEnv", add_env_module_vars_passed, NULL,
+ RSRC_CONF, RAW_ARGS, "a list of environment variables to pass to CGI." },
+{ "SetEnv", add_env_module_vars_set, NULL,
+ RSRC_CONF, RAW_ARGS, "an environment variable name and a value to pass to CGI." },
+{ "UnsetEnv", add_env_module_vars_unset, NULL,
+ RSRC_CONF, RAW_ARGS, "a list of variables to remove from the CGI environment." },
+{ NULL },
+};
+
+int fixup_env_module(request_rec *r)
+{
+ table *e = r->subprocess_env;
+ server_rec *s = r->server;
+ env_server_config_rec *sconf = get_module_config (s->module_config,
+ &env_module);
+ table *vars = sconf->vars;
+
+ if ( !sconf->vars_present ) return DECLINED;
+
+ r->subprocess_env = overlay_tables( r->pool, e, vars );
+
+ return OK;
+}
+
+module env_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ NULL, /* dir config creater */
+ NULL, /* dir merger --- default is to override */
+ create_env_server_config, /* server config */
+ merge_env_server_configs, /* merge server configs */
+ env_module_cmds, /* command table */
+ NULL, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ fixup_env_module, /* fixups */
+ NULL, /* logger */
+};
diff --git a/RELEASE_1_1_X/src/modules/standard/mod_imap.c b/RELEASE_1_1_X/src/modules/standard/mod_imap.c
new file mode 100644
index 00000000000..2eddcf14771
--- /dev/null
+++ b/RELEASE_1_1_X/src/modules/standard/mod_imap.c
@@ -0,0 +1,829 @@
+
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+/*
+ * This imagemap module started as a port of the original imagemap.c
+ * written by Rob McCool (11/13/93 robm@ncsa.uiuc.edu).
+ * This version includes the mapping algorithms found in version 1.3
+ * of imagemap.c.
+ *
+ * Contributors to this code include:
+ *
+ * Kevin Hughes, kevinh@pulua.hcc.hawaii.edu
+ *
+ * Eric Haines, erich@eye.com
+ * "macmartinized" polygon code copyright 1992 by Eric Haines, erich@eye.com
+ *
+ * Randy Terbush, randy@zyzzyva.com
+ * port to Apache module format, "base_uri" and support for relative URLs
+ *
+ * James H. Cloos, Jr., cloos@jhcloos.com
+ * Added point datatype, using code in NCSA's version 1.8 imagemap.c
+ * program, as distributed with version 1.4.1 of their server.
+ * The point code is originally added by Craig Milo Rogers, Rogers@ISI.Edu
+ *
+ * Nathan Kurz, nate@tripod.com
+ * Rewrite/reorganization. New handling of default, base and relative URLs.
+ * New Configuration directives:
+ * ImapMenu {none, formatted, semiformatted, unformatted}
+ * ImapDefault {error, nocontent, referer, menu, URL}
+ * ImapBase {map, referer, URL}
+ * Support for creating non-graphical menu added. (backwards compatible):
+ * Old: directive URL [x,y ...]
+ * New: directive URL "Menu text" [x,y ...]
+ * or: directive URL x,y ... "Menu text"
+ * Map format and menu concept courtesy Joshua Bell, jsbell@acs.ucalgary.ca.
+ *
+ * Mark Cox, mark@ukweb.com, Allow relative URLs even when no base specified
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_main.h"
+#include "http_log.h"
+#include "util_script.h"
+
+#define IMAP_MAGIC_TYPE "application/x-httpd-imap"
+#define LARGEBUF 500
+#define SMALLBUF 100
+#define MAXVERTS 100
+#define X 0
+#define Y 1
+
+#define IMAP_MENU_DEFAULT "formatted"
+#define IMAP_DEFAULT_DEFAULT "nocontent"
+#define IMAP_BASE_DEFAULT "map"
+
+#ifdef SUNOS4
+double strtod(); /* SunOS needed this */
+#endif
+
+module imap_module;
+
+typedef struct {
+ char *imap_menu;
+ char *imap_default;
+ char *imap_base;
+} imap_conf_rec;
+
+void *create_imap_dir_config (pool *p, char *dummy) {
+ imap_conf_rec *icr =
+ (imap_conf_rec *)palloc(p, sizeof(imap_conf_rec));
+
+ icr->imap_menu = NULL;
+ icr->imap_default = NULL;
+ icr->imap_base = NULL;
+
+ return icr;
+}
+
+void *merge_imap_dir_configs (pool *p, void *basev, void *addv)
+{
+ imap_conf_rec *new=(imap_conf_rec *)pcalloc (p, sizeof(imap_conf_rec));
+ imap_conf_rec *base = (imap_conf_rec *)basev;
+ imap_conf_rec *add = (imap_conf_rec *)addv;
+
+ new->imap_menu = add->imap_menu ? add->imap_menu : base->imap_menu;
+ new->imap_default=add->imap_default ? add->imap_default : base->imap_default;
+ new->imap_base =add-> imap_base ? add->imap_base : base->imap_base;
+
+ return new;
+}
+
+
+command_rec imap_cmds[] = {
+{ "ImapMenu", set_string_slot,
+ (void*)XtOffsetOf(imap_conf_rec, imap_menu), OR_INDEXES, TAKE1,
+ "the type of menu generated: none, formatted, semiformatted, unformatted"},
+{ "ImapDefault", set_string_slot,
+ (void*)XtOffsetOf(imap_conf_rec, imap_default), OR_INDEXES, TAKE1,
+ "the action taken if no match: error, nocontent, referer, menu, URL" },
+{ "ImapBase", set_string_slot,
+ (void*)XtOffsetOf(imap_conf_rec, imap_base), OR_INDEXES, TAKE1,
+ "the base for all URL's: map, referer, URL (or start of)" },
+{ NULL }
+};
+
+int pointinrect(double point[2], double coords[MAXVERTS][2])
+{
+ double max[2], min[2];
+ if (coords[0][X] > coords[1][X]) {
+ max[0] = coords[0][X];
+ min[0] = coords[1][X];
+ } else {
+ max[0] = coords[1][X];
+ min[0] = coords[0][X];
+ }
+
+ if (coords[0][Y] > coords[1][Y]) {
+ max[1] = coords[0][Y];
+ min[1] = coords[1][Y];
+ } else {
+ max[1] = coords[1][Y];
+ min[1] = coords[0][Y];
+ }
+
+ return ((point[X] >= min[0] && point[X] <= max[0]) &&
+ (point[Y] >= min[1] && point[Y] <= max[1]));
+}
+
+int pointincircle(double point[2], double coords[MAXVERTS][2])
+{
+ int radius1, radius2;
+
+ radius1 = ((coords[0][Y] - coords[1][Y]) * (coords[0][Y] - coords[1][Y]))
+ + ((coords[0][X] - coords[1][X]) * (coords[0][X] - coords[1][X]));
+
+ radius2 = ((coords[0][Y] - point[Y]) * (coords[0][Y] - point[Y]))
+ + ((coords[0][X] - point[X]) * (coords[0][X] - point[X]));
+
+ return (radius2 <= radius1);
+}
+
+int pointinpoly(double point[2], double pgon[MAXVERTS][2])
+{
+ int i, numverts, inside_flag, xflag0;
+ int crossings;
+ double *p, *stop;
+ double tx, ty, y;
+
+ for (i = 0; pgon[i][X] != -1 && i < MAXVERTS; i++);
+
+ numverts = i;
+ crossings = 0;
+
+ tx = point[X];
+ ty = point[Y];
+ y = pgon[numverts - 1][Y];
+
+ p = (double *) pgon + 1;
+ if ((y >= ty) != (*p >= ty)) {
+
+ if ((xflag0 = (pgon[numverts - 1][X] >= tx)) == (*(double *) pgon >= tx)) {
+ if (xflag0)
+ crossings++;
+ }
+ else {
+ crossings += (pgon[numverts - 1][X] - (y - ty) *
+ (*(double *) pgon - pgon[numverts - 1][X]) /
+ (*p - y)) >= tx;
+ }
+ }
+
+ stop = pgon[numverts];
+
+ for (y = *p, p += 2; p < stop; y = *p, p += 2) {
+
+ if (y >= ty) {
+
+ while ((p < stop) && (*p >= ty))
+ p += 2;
+
+ if (p >= stop)
+ break;
+ if ((xflag0 = (*(p - 3) >= tx)) == (*(p - 1) >= tx)) {
+
+ if (xflag0)
+ crossings++;
+ }
+ else {
+ crossings += (*(p - 3) - (*(p - 2) - ty) *
+ (*(p - 1) - *(p - 3)) / (*p - *(p - 2))) >= tx;
+ }
+ }
+ else {
+ while ((p < stop) && (*p < ty))
+ p += 2;
+
+ if (p >= stop)
+ break;
+
+ if ((xflag0 = (*(p - 3) >= tx)) == (*(p - 1) >= tx)) {
+ if (xflag0)
+ crossings++;
+ }
+ else {
+ crossings += (*(p - 3) - (*(p - 2) - ty) *
+ (*(p - 1) - *(p - 3)) / (*p - *(p - 2))) >= tx;
+ }
+ }
+ }
+
+ inside_flag = crossings & 0x01;
+ return (inside_flag);
+}
+
+
+int is_closer(double point[2], double coords[MAXVERTS][2], double *closest)
+{
+ double dist_squared =((point[X] - coords[0][X]) * (point[X] - coords[0][X]))
+ + ((point[Y] - coords[0][Y]) * (point[Y] - coords[0][Y]));
+
+ if (point[X] < 0 || point[Y] < 0 )
+ return(0); /* don't mess around with negative coordinates */
+
+ if ( *closest < 0 || dist_squared < *closest ) {
+ *closest = dist_squared;
+ return(1); /* if this is the first point or is the closest yet
+ set 'closest' equal to this distance^2 */
+ }
+
+ return(0); /* if it's not the first or closest */
+
+}
+
+double get_x_coord(char *args)
+{
+ char *endptr; /* we want it non-null */
+ double x_coord = -1; /* -1 is returned if no coordinate is given */
+
+ if (args == NULL)
+ return(-1); /* in case we aren't passed anything */
+
+ while( *args && !isdigit(*args) && *args != ',')
+ args++; /* jump to the first digit, but not past a comma or end */
+
+ x_coord = strtod(args, &endptr);
+
+ if (endptr > args) /* if a conversion was made */
+ return(x_coord);
+
+ return(-1); /* else if no conversion was made, or if no args was given */
+}
+
+double get_y_coord(char *args)
+{
+ char *endptr; /* we want it non-null */
+ char *start_of_y = NULL;
+ double y_coord = -1; /* -1 is returned on error */
+
+ if (args == NULL)
+ return(-1); /* in case we aren't passed anything */
+
+ start_of_y = strchr(args, ','); /* the comma */
+
+ if (start_of_y) {
+
+ start_of_y++; /* start looking at the character after the comma */
+
+ while( *start_of_y && !isdigit(*start_of_y))
+ start_of_y++; /* jump to the first digit, but not past the end */
+
+ y_coord = strtod(start_of_y, &endptr);
+
+ if (endptr > start_of_y)
+ return(y_coord);
+ }
+
+ return(-1); /* if no conversion was made, or no comma was found in args */
+}
+
+
+int read_quoted(char *string, char *quoted_part)
+{
+ char *starting_pos = string;
+
+ while ( isspace(*string) )
+ string++; /* go along string until non-whitespace */
+
+ if ( *string == '"' ) { /* if that character is a double quote */
+
+ string++; /* step over it */
+
+ while ( *string && *string != '"' ) {
+ *quoted_part++ = *string++; /* copy the quoted portion */
+ }
+
+ *quoted_part = '\0'; /* end the string with a SNUL */
+
+ string++; /* step over the last double quote */
+ }
+
+ return(string - starting_pos); /* return the total characters read */
+}
+
+
+void imap_url(request_rec *r, char *base, char *value, char *url)
+{
+/* translates a value into a URL. */
+ int slen, clen;
+ char *string_pos = NULL;
+ char *directory = NULL;
+ char *referer = NULL;
+ char my_base[SMALLBUF] = {'\0'};
+
+ if ( ! strcasecmp(value, "map" ) || ! strcasecmp(value, "menu") ) {
+ if (r->server->port == 80 ) {
+ sprintf(url, "http://%s%s", r->server->server_hostname, r->uri);
+ }
+ else {
+ sprintf(url, "http://%s:%d%s", r->server->server_hostname,
+ r->server->port, r->uri);
+ }
+ return;
+ }
+
+ if ( ! strcasecmp(value, "nocontent") || ! strcasecmp(value, "error") ) {
+ strcpy(url, value);
+ return; /* these are handled elsewhere, so just copy them */
+ }
+
+ if ( ! strcasecmp(value, "referer" ) ) {
+ referer = table_get(r->headers_in, "Referer");
+ if ( referer && *referer ) {
+ strcpy(url, referer);
+ return;
+ }
+ else {
+ *value = '\0'; /* if 'referer' but no referring page, null the value */
+ }
+ }
+
+ string_pos = value;
+ while ( isalpha(*string_pos) )
+ string_pos++; /* go along the URL from the map until a non-letter */
+ if ( *string_pos == ':' ) {
+ strcpy(url, value); /* if letters and then a colon (like http:) */
+ return; /* it's an absolute URL, so use it! */
+ }
+
+ if ( ! base || ! *base ) {
+ if ( value && *value ) {
+ strcpy(url, value); /* no base: use what is given */
+ }
+ else {
+ if (r->server->port == 80 ) {
+ sprintf(url, "http://%s/", r->server->server_hostname);
+ }
+ if (r->server->port != 80 ) {
+ sprintf(url, "http://%s:%d/", r->server->server_hostname,
+ r->server->port);
+ } /* no base, no value: pick a simple default */
+ }
+ return;
+ }
+
+ strcpy(my_base, base); /* must be a relative URL to be combined with base */
+ string_pos = my_base;
+ while (*string_pos) {
+ if (*string_pos == '/' && *(string_pos+1) == '/') {
+ string_pos += 2; /* if there are two slashes, jump over them */
+ continue;
+ }
+ if (*string_pos == '/') { /* the first single slash */
+ if ( value[0] == '/' ) {
+ *string_pos = '\0';
+ } /* if the URL from the map starts from root, end the
+ base URL string at the first single slash */
+ else {
+ directory = string_pos; /* save the start of the directory portion */
+
+ string_pos = strrchr(string_pos, '/'); /* now reuse string_pos */
+ string_pos++; /* step over that last slash */
+ *string_pos = '\0';
+ } /* but if the map url is relative, leave the
+ slash on the base (if there is one) */
+ break;
+ }
+ string_pos++; /* until we get to the end of my_base without finding
+ a slash by itself */
+ }
+
+ while ( ! strncmp(value, "../", 3) || ! strcmp(value, "..") ) {
+
+ if (directory && (slen = strlen (directory))) {
+
+ /* for each '..', knock a directory off the end
+ by ending the string right at the last slash.
+ But only consider the directory portion: don't eat
+ into the server name. And only try if a directory
+ portion was found */
+
+ clen = slen - 1;
+
+ while ((slen - clen) == 1) {
+
+ if ((string_pos = strrchr(directory, '/')))
+ *string_pos = '\0';
+ clen = strlen (directory);
+ if (clen == 0) break;
+ }
+
+ value += 2; /* jump over the '..' that we found in the value */
+ }
+
+ if (! strncmp(value, "/../", 4) || ! strcmp(value, "/..") )
+
+ value++; /* step over the '/' if there are more '..' to do.
+ this way, we leave the starting '/' on value after
+ the last '..', but get rid of it otherwise */
+
+ } /* by this point, value does not start with '..' */
+
+ if ( value && *value ) {
+ sprintf(url, "%s%s", my_base, value);
+ }
+ else {
+ sprintf(url, "%s", my_base);
+ }
+ return;
+}
+
+int imap_reply(request_rec *r, char *redirect)
+{
+ if ( ! strcasecmp(redirect, "error") ) {
+ return SERVER_ERROR; /* they actually requested an error! */
+ }
+ if ( ! strcasecmp(redirect, "nocontent") ) {
+ r->status_line = pstrdup(r->pool, "204 No Content");
+ soft_timeout ("send no content", r);
+ send_http_header(r);
+ return OK; /* tell the client to keep the page it has */
+ }
+ if (redirect && *redirect ) {
+ table_set(r->headers_out, "Location", redirect);
+ return REDIRECT; /* must be a URL, so redirect to it */
+ }
+ return SERVER_ERROR;
+}
+
+void menu_header(request_rec *r, char *menu)
+{
+ if (! strcasecmp(menu, "formatted")) {
+ r->content_type = "text/html";
+ soft_timeout ("send menu", r);
+ send_http_header(r);
+ rvputs(r, "\nMenu for ", r->uri,
+ "\n\n\n", NULL);
+ rvputs(r, "Menu for ", r->uri, "
\n
\n\n", NULL);
+ }
+ if (! strcasecmp(menu, "semiformatted")) {
+ r->content_type = "text/html";
+ soft_timeout ("send menu", r);
+ send_http_header(r);
+ rvputs(r, "\nMenu for ", r->uri,
+ "\n\n\n", NULL);
+ }
+ if (! strcasecmp(menu, "unformatted")) {
+ r->content_type = "text/html";
+ soft_timeout ("send menu", r);
+ send_http_header(r);
+ rvputs(r, "\nMenu for ", r->uri,
+ "\n\n\n", NULL);
+ }
+ return;
+}
+
+void menu_blank(request_rec *r, char *menu)
+{
+ if (! strcasecmp(menu, "formatted") ) {
+ rputs("\n", r);
+ }
+ if (! strcasecmp(menu, "semiformatted") ) {
+ rputs("
\n", r);
+ }
+ if (! strcasecmp(menu, "unformatted") ) {
+ rputs("\n", r);
+ }
+ return;
+}
+
+void menu_comment(request_rec *r, char *menu, char *comment)
+{
+ if (! strcasecmp(menu, "formatted") ) {
+ rputs("\n", r); /* print just a newline if 'formatted' */
+ }
+ if (! strcasecmp(menu, "semiformatted") && *comment ) {
+ rvputs(r, comment, "\n", NULL);
+ }
+ if (! strcasecmp(menu, "unformatted") && *comment ) {
+ rvputs(r, comment, "\n", NULL);
+ }
+ return; /* comments are ignored in the 'formatted' form */
+}
+
+void menu_default(request_rec *r, char *menu, char *href, char *text)
+{
+ if ( ! strcasecmp(href, "error") || ! strcasecmp(href, "nocontent") ) {
+ return; /* don't print such lines, these aren'te really href's */
+ }
+ if ( ! strcasecmp(menu, "formatted" ) ) {
+ rvputs(r, "(Default) ", text, "
\n",
+ NULL);
+ }
+ if ( ! strcasecmp(menu, "semiformatted" ) ) {
+ rvputs(r, "(Default) ", text, "
\n",
+ NULL);
+ }
+ if ( ! strcasecmp(menu, "unformatted" ) ) {
+ rvputs(r, "", text, "", NULL);
+ }
+ return;
+}
+
+void menu_directive(request_rec *r, char *menu, char *href, char *text)
+{
+ if ( ! strcasecmp(href, "error") || ! strcasecmp(href, "nocontent") ) {
+ return; /* don't print such lines, as this isn't really an href */
+ }
+ if ( ! strcasecmp(menu, "formatted" ) ) {
+ rvputs(r, " ", text, "
\n",
+ NULL);
+ }
+ if ( ! strcasecmp(menu, "semiformatted" ) ) {
+ rvputs(r, " ", text, "
\n",
+ NULL);
+ }
+ if ( ! strcasecmp(menu, "unformatted" ) ) {
+ rvputs(r, "", text, "", NULL);
+ }
+ return;
+}
+
+void menu_footer(request_rec *r)
+{
+ rputs("\n\n\n\n", r); /* finish the menu */
+}
+
+int imap_handler(request_rec *r)
+{
+ char input[LARGEBUF] = {'\0'};
+ char href_text[SMALLBUF] = {'\0'};
+ char base[SMALLBUF] = {'\0'};
+ char redirect[SMALLBUF] = {'\0'};
+ char directive[SMALLBUF] = {'\0'};
+ char value[SMALLBUF] = {'\0'};
+ char mapdflt[SMALLBUF] = {'\0'};
+ char closest[SMALLBUF] = {'\0'};
+ double closest_yet = -1;
+
+ double testpoint[2] = { -1,-1 };
+ double pointarray[MAXVERTS + 1][2] = { {-1,-1} };
+ int vertex = 0;
+
+ char *string_pos = NULL;
+ int chars_read = 0;
+ int showmenu = 0;
+
+ imap_conf_rec *icr = get_module_config(r->per_dir_config, &imap_module);
+
+ char *imap_menu = icr->imap_menu ?
+ icr->imap_menu : IMAP_MENU_DEFAULT;
+ char *imap_default = icr->imap_default ?
+ icr->imap_default : IMAP_DEFAULT_DEFAULT;
+ char *imap_base = icr->imap_base ?
+ icr->imap_base : IMAP_BASE_DEFAULT;
+
+ FILE *imap = pfopen(r->pool, r->filename, "r");
+
+ if ( ! imap )
+ return NOT_FOUND;
+
+ imap_url(r, NULL, imap_base, base); /* set base according to default */
+ imap_url(r, NULL, imap_default, mapdflt); /* and default to global default */
+
+ testpoint[X] = get_x_coord(r->args);
+ testpoint[Y] = get_y_coord(r->args);
+
+ if ((testpoint[X] == -1 || testpoint[Y] == -1) ||
+ (testpoint[X] == 0 && testpoint[Y] == 0) ) {
+ /* if either is -1 or if both are zero (new Lynx) */
+ /* we don't have valid coordinates */
+ testpoint[X] = -1;
+ testpoint[Y] = -1;
+ if ( strncasecmp(imap_menu, "none", 2) )
+ showmenu = 1; /* show the menu _unless_ ImapMenu is 'none' or 'no' */
+ }
+
+ if (showmenu) { /* send start of imagemap menu if we're going to */
+ menu_header(r, imap_menu);
+ }
+
+ while (!cfg_getline(input, LARGEBUF, imap)) {
+ string_pos = input; /* always start at the beginning of line */
+
+ directive[0] = '\0';
+ value[0] = '\0';
+ href_text[0] = '\0';
+ redirect[0] = '\0';
+ chars_read = 0; /* clear these before using */
+
+ if ( ! input[0] ) {
+ if (showmenu) {
+ menu_blank(r, imap_menu);
+ }
+ continue;
+ }
+
+ if ( input[0] == '#' ) {
+ if (showmenu) {
+ menu_comment(r, imap_menu, input + 1);
+ }
+ continue;
+ } /* blank lines and comments are ignored if we aren't printing a menu */
+
+
+ if (sscanf(input, "%s %s", directive, value) != 2) {
+ continue; /* make sure we read two fields */
+ }
+ /* Now skip what we just read... we can't use ANSIism %n */
+ while (!(isspace(*string_pos))) /* past directive */
+ string_pos++;
+ while (isspace(*string_pos)) /* and whitespace */
+ string_pos++;
+ while (!(isspace(*string_pos))) /* and value... have to watch it */
+ string_pos++; /* can have punctuation and stuff */
+
+ if ( ! strncasecmp(directive, "base", 4 ) ) { /* base, base_uri */
+ imap_url(r, NULL, value, base);
+ continue; /* base is never printed to a menu */
+ }
+
+ chars_read = read_quoted(string_pos, href_text);
+ string_pos += chars_read; /* read the quoted href text if present */
+
+ if ( ! strcasecmp(directive, "default" ) ) { /* default */
+ imap_url(r, NULL, value, mapdflt);
+ if (showmenu) { /* print the default if there's a menu */
+ if (! *href_text) { /* if we didn't find a "href text" */
+ strcpy(href_text, mapdflt); /* use the href itself as text */
+ }
+ imap_url(r, base, mapdflt, redirect);
+ menu_default(r, imap_menu, redirect, href_text);
+ }
+ continue;
+ }
+
+ vertex = 0;
+ while ( vertex < MAXVERTS &&
+ sscanf(string_pos, "%lf,%lf",
+ &pointarray[vertex][X], &pointarray[vertex][Y]) == 2)
+ {
+ /* Now skip what we just read... we can't use ANSIism %n */
+ while(isspace(*string_pos)) /* past whitespace */
+ string_pos++;
+ while(isdigit(*string_pos)) /* and the 1st number */
+ string_pos++;
+ string_pos++; /* skip the ',' */
+ while(isdigit(*string_pos)) /* 2nd number */
+ string_pos++;
+ vertex++;
+ } /* so long as there are more vertices to read, and
+ we have room, read them in. We start where we left
+ off of the last sscanf, not at the beginning.*/
+
+ pointarray[vertex][X] = -1; /* signals the end of vertices */
+
+ if (showmenu) {
+ read_quoted(string_pos, href_text); /* href text could be here instead */
+ if (! *href_text) { /* if we didn't find a "href text" */
+ strcpy(href_text, value); /* use the href itself in the menu */
+ }
+ imap_url(r, base, value, redirect);
+ menu_directive(r, imap_menu, redirect, href_text);
+ continue;
+ }
+ /* note that we don't make it past here if we are making a menu */
+
+ if (testpoint[X] == -1 || pointarray[0][X] == -1 )
+ continue; /* don't try the following tests if testpoints
+ are invalid, or if there are no coordinates */
+
+ if ( ! strcasecmp(directive, "poly" ) ) { /* poly */
+
+ if (pointinpoly (testpoint, pointarray) ) {
+ pfclose(r->pool, imap);
+ imap_url(r, base, value, redirect);
+ return (imap_reply(r, redirect));
+ }
+ continue;
+ }
+
+ if ( ! strcasecmp(directive, "circle" ) ) { /* circle */
+
+ if (pointincircle (testpoint, pointarray) ) {
+ pfclose(r->pool, imap);
+ imap_url(r, base, value, redirect);
+ return (imap_reply(r, redirect));
+ }
+ continue;
+ }
+
+ if ( ! strcasecmp(directive, "rect" ) ) { /* rect */
+
+ if (pointinrect (testpoint, pointarray) ) {
+ pfclose(r->pool, imap);
+ imap_url(r, base, value, redirect);
+ return (imap_reply(r, redirect));
+ }
+ continue;
+ }
+
+ if ( ! strcasecmp(directive, "point" ) ) { /* point */
+
+ if (is_closer(testpoint, pointarray, &closest_yet) ) {
+ strcpy(closest, value); /* if the closest point yet save it */
+ }
+
+ continue;
+ } /* move on to next line whether it's closest or not */
+
+ } /* nothing matched, so we get another line! */
+
+ pfclose(r->pool, imap); /* we are done with the map file, so close it */
+
+ if (showmenu) {
+ menu_footer(r); /* finish the menu and we are done */
+ return OK;
+ }
+
+ if (*closest) { /* if a 'point' directive has been seen */
+ imap_url(r, base, closest, redirect);
+ return (imap_reply(r, redirect));
+ }
+
+ if (*mapdflt ) { /* a default should be defined, even if only 'nocontent'*/
+ imap_url(r, base, mapdflt, redirect);
+ return(imap_reply(r, redirect));
+ }
+
+ return SERVER_ERROR; /* If we make it this far, we failed. They lose! */
+}
+
+
+handler_rec imap_handlers[] = {
+{ IMAP_MAGIC_TYPE, imap_handler },
+{ "imap-file", imap_handler },
+{ NULL }
+};
+
+module imap_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ create_imap_dir_config, /* dir config creater */
+ merge_imap_dir_configs, /* dir merger --- default is to override */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ imap_cmds, /* command table */
+ imap_handlers, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* fixups */
+ NULL /* logger */
+};
diff --git a/RELEASE_1_1_X/src/modules/standard/mod_include.c b/RELEASE_1_1_X/src/modules/standard/mod_include.c
new file mode 100644
index 00000000000..dcd5b0fd5ee
--- /dev/null
+++ b/RELEASE_1_1_X/src/modules/standard/mod_include.c
@@ -0,0 +1,908 @@
+
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+
+/*
+ * http_include.c: Handles the server-parsed HTML documents
+ *
+ * Original by Rob McCool; substantial fixups by David Robinson;
+ * incorporated into the Shambhala module framework by rst.
+ *
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "util_script.h"
+
+#define STARTING_SEQUENCE ""
+#define DEFAULT_ERROR_MSG "[an error occurred while processing this directive]"
+#define DEFAULT_TIME_FORMAT "%A, %d-%b-%y %T %Z"
+#define SIZEFMT_BYTES 0
+#define SIZEFMT_KMG 1
+
+static void decodehtml(char *s);
+static char *get_tag(pool *p, FILE *in, char *tag, int tag_len, int dodecode);
+static int get_directive(FILE *in, char *d, pool *p);
+
+/* ------------------------ Environment function -------------------------- */
+
+void add_include_vars(request_rec *r, char *timefmt)
+{
+ struct passwd *pw;
+ table *e = r->subprocess_env;
+ char *t;
+ time_t date = time(NULL);
+
+ table_set(e, "DATE_LOCAL", ht_time(r->pool, date, timefmt, 0));
+ table_set(e, "DATE_GMT", ht_time(r->pool, date, timefmt, 1));
+ table_set(e, "LAST_MODIFIED",ht_time(r->pool,r->finfo.st_mtime,timefmt,0));
+ table_set(e, "DOCUMENT_URI", r->uri);
+ table_set(e, "DOCUMENT_PATH_INFO", r->path_info);
+ pw = getpwuid(r->finfo.st_uid);
+ if (pw) {
+ table_set(e, "USER_NAME", pw->pw_name);
+ } else {
+ char uid[16];
+ sprintf(uid, "user#%ld", (unsigned long)r->finfo.st_uid);
+ table_set(e, "USER_NAME", uid);
+ }
+
+ if((t = strrchr(r->filename, '/')))
+ table_set (e, "DOCUMENT_NAME", ++t);
+ else
+ table_set (e, "DOCUMENT_NAME", r->uri);
+ if (r->args) {
+ unescape_url (r->args);
+ table_set (e, "QUERY_STRING_UNESCAPED",
+ escape_shell_cmd (r->pool, r->args));
+ }
+}
+
+#define GET_CHAR(f,c,r,p) \
+ { \
+ int i = getc(f); \
+ if(feof(f) || ferror(f) || (i == -1)) { \
+ pfclose(p, f); \
+ return r; \
+ } \
+ c = (char)i; \
+ }
+
+/* --------------------------- Parser functions --------------------------- */
+
+/* Grrrr... rputc makes this slow as all-get-out. Elsewhere, it doesn't
+ * matter much, but this is an inner loop...
+ */
+
+int find_string(FILE *in,char *str, request_rec *r) {
+ int x,l=strlen(str),p;
+ char c;
+
+ p=0;
+ while(1) {
+ GET_CHAR(in,c,1,r->pool);
+ if(c == str[p]) {
+ if((++p) == l)
+ return 0;
+ }
+ else {
+ if(r) {
+ if(p) {
+ for(x=0;x= 11 && val <= 31) ||
+ (val >= 127 && val <= 160) || val >= 256)
+ p--; /* no data to output */
+ else
+ *p = val;
+ } else{
+ j = i-1;
+ if (i-1 > MAXENTLEN || entlist[i-1] == NULL) { /* wrong length */
+ *p = '&';
+ continue; /* skip it */
+ }
+ for (ents=entlist[i-1]; *ents != '\0'; ents += i)
+ if (strncmp(s+1, ents, i-1) == 0) break;
+
+ if (*ents == '\0')
+ *p = '&'; /* unknown */
+ else {
+ *p = ((const unsigned char *)ents)[i-1];
+ s += i;
+ }
+ }
+ }
+
+ *p = '\0';
+}
+
+/*
+ * extract the next tag name and value.
+ * if there are no more tags, set the tag name to 'done'
+ * the tag value is html decoded if dodecode is non-zero
+ */
+
+static char *
+get_tag(pool *p, FILE *in, char *tag, int tagbuf_len, int dodecode) {
+ char *t = tag, *tag_val, c, term;
+ int n;
+
+ n = 0;
+
+ do { /* skip whitespace */
+ GET_CHAR(in,c,NULL,p);
+ } while (isspace(c));
+
+ /* tags can't start with - */
+ if(c == '-') {
+ GET_CHAR(in,c,NULL,p);
+ if(c == '-') {
+ do {
+ GET_CHAR(in,c,NULL,p);
+ } while (isspace(c));
+ if(c == '>') {
+ strcpy(tag,"done");
+ return tag;
+ }
+ }
+ return NULL; /* failed */
+ }
+
+ /* find end of tag name */
+ while(1) {
+ if(++n == tagbuf_len) {
+ t[tagbuf_len - 1] = '\0';
+ return NULL;
+ }
+ if(c == '=' || isspace(c)) break;
+ *(t++) = tolower(c);
+ GET_CHAR(in,c,NULL,p);
+ }
+
+ *t++ = '\0';
+ tag_val = t;
+
+ while (isspace(c)) GET_CHAR(in, c, NULL,p); /* space before = */
+ if (c != '=') return NULL;
+
+ do {
+ GET_CHAR(in,c,NULL,p); /* space after = */
+ } while (isspace(c));
+
+ /* we should allow a 'name' as a value */
+
+ if (c != '"' && c != '\'') return NULL;
+ term = c;
+ while(1) {
+ GET_CHAR(in,c,NULL,p);
+ if(++n == tagbuf_len) {
+ t[tagbuf_len - 1] = '\0';
+ return NULL;
+ }
+ if (c == term) break;
+ *(t++) = c;
+ }
+ *t = '\0';
+ if (dodecode) decodehtml(tag_val);
+ return pstrdup (p, tag_val);
+}
+
+/* the pool is required to allow GET_CHAR to call pfclose */
+static int
+get_directive(FILE *in, char *d, pool *p) {
+ char c;
+
+ /* skip initial whitespace */
+ while(1) {
+ GET_CHAR(in,c,1,p);
+ if(!isspace(c))
+ break;
+ }
+ /* now get directive */
+ while(1) {
+ *d++ = tolower(c);
+ GET_CHAR(in,c,1,p);
+ if(isspace(c))
+ break;
+ }
+ *d = '\0';
+ return 0;
+}
+
+/* --------------------------- Action handlers ---------------------------- */
+
+int include_cgi(char *s, request_rec *r)
+{
+ request_rec *rr = sub_req_lookup_uri (s, r);
+
+ if (rr->status != 200) return -1;
+
+ /* No hardwired path info or query allowed */
+
+ if ((rr->path_info && rr->path_info[0]) || rr->args) return -1;
+ if (rr->finfo.st_mode == 0) return -1;
+
+ /* Script gets parameters of the *document*, for back compatibility */
+
+ rr->path_info = r->path_info; /* painful to get right; see mod_cgi.c */
+ rr->args = r->args;
+
+ /* Force sub_req to be treated as a CGI request, even if ordinary
+ * typing rules would have called it something else.
+ */
+
+ rr->content_type = CGI_MAGIC_TYPE;
+
+ /* Run it. */
+
+ if (run_sub_req (rr) == REDIRECT) {
+ char *location = table_get (rr->headers_out, "Location");
+ location = escape_html(rr->pool, location);
+ rvputs(r,"", location, "", NULL);
+ }
+
+ destroy_sub_req (rr);
+
+ return 0;
+}
+
+int handle_include(FILE *in, request_rec *r, char *error, int noexec) {
+ char tag[MAX_STRING_LEN];
+ char *tag_val;
+
+ while(1) {
+ if(!(tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 1)))
+ return 1;
+ if(!strcmp(tag,"file") || !strcmp (tag, "virtual")) {
+ request_rec *rr=NULL;
+ char *error_fmt = NULL;
+
+ if (tag[0] == 'f')
+ { /* be safe; only files in this directory or below allowed */
+ char tmp[MAX_STRING_LEN+2];
+ sprintf(tmp, "/%s/", tag_val);
+ if (tag_val[0] == '/' || strstr(tmp, "/../") != NULL)
+ error_fmt = "unable to include file %s in parsed file %s";
+ else
+ rr = sub_req_lookup_file (tag_val, r);
+ } else
+ rr = sub_req_lookup_uri (tag_val, r);
+
+ if (!error_fmt && rr->status != 200)
+ error_fmt = "unable to include %s in parsed file %s";
+
+ if (!error_fmt && noexec && rr->content_type
+ && (strncmp (rr->content_type, "text/", 5)))
+ error_fmt =
+ "unable to include potential exec %s in parsed file %s";
+
+ if (error_fmt == NULL)
+ {
+ request_rec *p;
+
+ for (p=r; p != NULL; p=p->main)
+ if (strcmp(p->filename, rr->filename) == 0) break;
+ if (p != NULL)
+ error_fmt = "Recursive include of %s in parsed file %s";
+ }
+
+ if (!error_fmt && run_sub_req (rr))
+ error_fmt = "unable to include %s in parsed file %s";
+
+ if (error_fmt) {
+ log_printf(r->server, error_fmt, tag_val, r->filename);
+ rputs(error, r);
+ }
+
+ if (rr != NULL) destroy_sub_req (rr);
+ }
+ else if(!strcmp(tag,"done"))
+ return 0;
+ else {
+ log_printf(r->server, "unknown parameter %s to tag include in %s",
+ tag, r->filename);
+ rputs(error, r);
+ }
+ }
+}
+
+typedef struct {
+ request_rec *r;
+ char *s;
+} include_cmd_arg;
+
+void include_cmd_child (void *arg)
+{
+ request_rec *r = ((include_cmd_arg *)arg)->r;
+ char *s = ((include_cmd_arg *)arg)->s;
+ table *env = r->subprocess_env;
+#ifdef DEBUG_INCLUDE_CMD
+#ifdef __EMX__
+ /* under OS/2 /dev/tty is referenced as con */
+ FILE *dbg = fopen ("con", "w");
+#else
+ FILE *dbg = fopen ("/dev/tty", "w");
+#endif
+#endif
+ char err_string [MAX_STRING_LEN];
+
+#ifdef DEBUG_INCLUDE_CMD
+ fprintf (dbg, "Attempting to include command '%s'\n", s);
+#endif
+
+ if (r->path_info && r->path_info[0] != '\0')
+ {
+ request_rec *pa_req;
+
+ table_set (env, "PATH_INFO", escape_shell_cmd (r->pool, r->path_info));
+
+ pa_req = sub_req_lookup_uri(escape_uri(r->pool, r->path_info), r);
+ if (pa_req->filename)
+ table_set(env, "PATH_TRANSLATED",
+ pstrcat(r->pool, pa_req->filename, pa_req->path_info,
+ NULL));
+ }
+
+ if (r->args) {
+ table_set (env, "QUERY_STRING", r->args);
+ unescape_url (r->args);
+ table_set (env, "QUERY_STRING_UNESCAPED",
+ escape_shell_cmd (r->pool, r->args));
+ }
+
+ error_log2stderr (r->server);
+
+#ifdef DEBUG_INCLUDE_CMD
+ fprintf (dbg, "Attempting to exec '%s'\n", s);
+#endif
+ cleanup_for_exec();
+ execle(SHELL_PATH, SHELL_PATH, "-c", s, NULL,
+ create_environment (r->pool, env));
+
+ /* Oh, drat. We're still here. The log file descriptors are closed,
+ * so we have to whimper a complaint onto stderr...
+ */
+
+#ifdef DEBUG_INCLUDE_CMD
+ fprintf (dbg, "Exec failed\n");
+#endif
+ sprintf(err_string, "httpd: exec of %s failed, errno is %d\n",
+ SHELL_PATH,errno);
+ write (2, err_string, strlen(err_string));
+ exit(0);
+}
+
+int include_cmd(char *s, request_rec *r) {
+ include_cmd_arg arg;
+ FILE *f;
+
+ arg.r = r; arg.s = s;
+
+ if (!spawn_child (r->connection->pool, include_cmd_child, &arg,
+ kill_after_timeout, NULL, &f))
+ return -1;
+
+ send_fd(f,r);
+ pfclose(r->pool, f); /* will wait for zombie when
+ * r->pool is cleared
+ */
+ return 0;
+}
+
+
+int handle_exec(FILE *in, request_rec *r, char *error)
+{
+ char tag[MAX_STRING_LEN];
+ char *tag_val;
+ char *file = r->filename;
+
+ while(1) {
+ if(!(tag_val = get_tag (r->pool, in, tag, MAX_STRING_LEN, 1)))
+ return 1;
+ if(!strcmp(tag,"cmd")) {
+ if(include_cmd(tag_val, r) == -1) {
+ log_printf(r->server, "failed command exec %s in %s",
+ tag_val, file);
+ rputs(error, r);
+ }
+ /* just in case some stooge changed directories */
+ chdir_file(r->filename);
+ }
+ else if(!strcmp(tag,"cgi")) {
+ if(include_cgi(tag_val, r) == -1) {
+ log_printf(r->server, "invalid CGI ref %s in %s",tag_val,file);
+ rputs(error, r);
+ }
+ /* grumble groan */
+ chdir_file(r->filename);
+ }
+ else if(!strcmp(tag,"done"))
+ return 0;
+ else {
+ log_printf(r->server, "unknown parameter %s to tag exec in %s",
+ tag, file);
+ rputs(error, r);
+ }
+ }
+
+}
+
+int handle_echo (FILE *in, request_rec *r, char *error) {
+ char tag[MAX_STRING_LEN];
+ char *tag_val;
+
+ while(1) {
+ if(!(tag_val = get_tag (r->pool, in, tag, MAX_STRING_LEN, 1)))
+ return 1;
+ if(!strcmp(tag,"var")) {
+ char *val = table_get (r->subprocess_env, tag_val);
+
+ if (val) rputs(val, r);
+ else rputs("(none)", r);
+ } else if(!strcmp(tag,"done"))
+ return 0;
+ else {
+ log_printf(r->server, "unknown parameter %s to tag echo in %s",
+ tag, r->filename);
+ rputs(error, r);
+ }
+ }
+}
+
+int handle_config(FILE *in, request_rec *r, char *error, char *tf,
+ int *sizefmt) {
+ char tag[MAX_STRING_LEN];
+ char *tag_val;
+ table *env = r->subprocess_env;
+
+ while(1) {
+ if(!(tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 0)))
+ return 1;
+ if(!strcmp(tag,"errmsg"))
+ strcpy(error,tag_val);
+ else if(!strcmp(tag,"timefmt")) {
+ time_t date = time(NULL);
+ strcpy(tf,tag_val);
+ table_set (env, "DATE_LOCAL", ht_time(r->pool,date,tf,0));
+ table_set (env, "DATE_GMT", ht_time(r->pool,date,tf,1));
+ table_set (env, "LAST_MODIFIED", ht_time(r->pool,r->finfo.st_mtime,tf,0));
+ }
+ else if(!strcmp(tag,"sizefmt")) {
+ decodehtml(tag_val);
+ if(!strcmp(tag_val,"bytes"))
+ *sizefmt = SIZEFMT_BYTES;
+ else if(!strcmp(tag_val,"abbrev"))
+ *sizefmt = SIZEFMT_KMG;
+ }
+ else if(!strcmp(tag,"done"))
+ return 0;
+ else {
+ log_printf(r->server, "unknown parameter %s to tag config in %s",
+ tag, r->filename);
+ rputs(error, r);
+ }
+ }
+}
+
+
+
+int find_file(request_rec *r, char *directive, char *tag,
+ char *tag_val, struct stat *finfo, char *error)
+{
+ char dir[MAX_STRING_LEN];
+ char *to_send;
+
+ if(!strcmp(tag,"file")) {
+ getparents(tag_val); /* get rid of any nasties */
+ getwd(dir);
+ to_send = make_full_path (r->pool, dir, tag_val);
+ if(stat(to_send,finfo) == -1) {
+ log_printf(r->server,
+ "unable to get information about %s in parsed file %s",
+ to_send, r->filename);
+ rputs(error, r);
+ return -1;
+ }
+ return 0;
+ }
+ else if(!strcmp(tag,"virtual")) {
+ request_rec *rr = sub_req_lookup_uri (tag_val, r);
+
+ if (rr->status == 200 && rr->finfo.st_mode != 0) {
+ memcpy ((char*)finfo, (const char *)&rr->finfo, sizeof (struct stat));
+ destroy_sub_req (rr);
+ return 0;
+ } else {
+ log_printf(r->server,
+ "unable to get information about %s in parsed file %s",
+ tag_val, r->filename);
+ rputs(error, r);
+ destroy_sub_req (rr);
+ return -1;
+ }
+ }
+ else {
+ log_printf(r->server, "unknown parameter %s to tag %s in %s",
+ tag, directive, r->filename);
+ rputs(error, r);
+ return -1;
+ }
+}
+
+
+int handle_fsize(FILE *in, request_rec *r, char *error, int sizefmt)
+{
+ char tag[MAX_STRING_LEN];
+ char *tag_val;
+ struct stat finfo;
+
+ while(1) {
+ if(!(tag_val = get_tag(r->pool, in, tag, MAX_STRING_LEN, 1)))
+ return 1;
+ else if(!strcmp(tag,"done"))
+ return 0;
+ else if(!find_file(r,"fsize",tag,tag_val,&finfo,error)) {
+ if(sizefmt == SIZEFMT_KMG) {
+ send_size(finfo.st_size, r);
+ }
+ else {
+ int l,x;
+#if defined(BSD) && BSD > 199305
+ sprintf(tag,"%qd",finfo.st_size);
+#else
+ sprintf(tag,"%ld",finfo.st_size);
+#endif
+ l = strlen(tag); /* grrr */
+ for(x=0;xpool, in, tag, MAX_STRING_LEN, 1)))
+ return 1;
+ else if(!strcmp(tag,"done"))
+ return 0;
+ else if(!find_file(r,"flastmod",tag,tag_val,&finfo,error))
+ rputs(ht_time(r->pool, finfo.st_mtime, tf, 0), r);
+ }
+}
+
+
+
+/* -------------------------- The main function --------------------------- */
+
+/* This is a stub which parses a file descriptor. */
+
+void send_parsed_content(FILE *f, request_rec *r)
+{
+ char directive[MAX_STRING_LEN], error[MAX_STRING_LEN];
+ char timefmt[MAX_STRING_LEN];
+ int noexec = allow_options (r) & OPT_INCNOEXEC;
+ int ret, sizefmt;
+
+ strcpy(error,DEFAULT_ERROR_MSG);
+ strcpy(timefmt,DEFAULT_TIME_FORMAT);
+ sizefmt = SIZEFMT_KMG;
+
+ chdir_file (r->filename);
+
+ while(1) {
+ if(!find_string(f,STARTING_SEQUENCE,r)) {
+ if(get_directive(f,directive,r->pool))
+ return;
+ if(!strcmp(directive,"exec")) {
+ if(noexec) {
+ log_printf(r->server,
+ "httpd: exec used but not allowed in %s",
+ r->filename);
+ rputs(error, r);
+ ret = find_string(f,ENDING_SEQUENCE,NULL);
+ } else
+ ret=handle_exec(f, r, error);
+ }
+ else if(!strcmp(directive,"config"))
+ ret=handle_config(f, r, error, timefmt, &sizefmt);
+ else if(!strcmp(directive,"include"))
+ ret=handle_include(f, r, error, noexec);
+ else if(!strcmp(directive,"echo"))
+ ret=handle_echo(f, r, error);
+ else if(!strcmp(directive,"fsize"))
+ ret=handle_fsize(f, r, error, sizefmt);
+ else if(!strcmp(directive,"flastmod"))
+ ret=handle_flastmod(f, r, error, timefmt);
+ else {
+ log_printf(r->server,
+ "httpd: unknown directive %s in parsed doc %s",
+ directive, r->filename);
+ rputs(error, r);
+ ret=find_string(f,ENDING_SEQUENCE,NULL);
+ }
+ if(ret) {
+ log_printf(r->server, "httpd: premature EOF in parsed file %s",
+ r->filename);
+ return;
+ }
+ } else
+ return;
+ }
+}
+
+/*****************************************************************
+ *
+ * XBITHACK. Sigh... NB it's configurable per-directory; the compile-time
+ * option only changes the default.
+ */
+
+module includes_module;
+enum xbithack { xbithack_off, xbithack_on, xbithack_full };
+
+#ifdef XBITHACK
+#define DEFAULT_XBITHACK xbithack_full
+#else
+#define DEFAULT_XBITHACK xbithack_off
+#endif
+
+void *create_includes_dir_config (pool *p, char *dummy)
+{
+ enum xbithack *result = (enum xbithack*)palloc(p, sizeof (enum xbithack));
+ *result = DEFAULT_XBITHACK;
+ return result;
+}
+
+char *set_xbithack (cmd_parms *cmd, void *xbp, char *arg)
+{
+ enum xbithack *state = (enum xbithack *)xbp;
+
+ if (!strcasecmp (arg, "off")) *state = xbithack_off;
+ else if (!strcasecmp (arg, "on")) *state = xbithack_on;
+ else if (!strcasecmp (arg, "full")) *state = xbithack_full;
+ else return "XBitHack must be set to Off, On, or Full";
+
+ return NULL;
+}
+
+int send_parsed_file(request_rec *r)
+{
+ FILE *f;
+ enum xbithack *state =
+ (enum xbithack *)get_module_config(r->per_dir_config,&includes_module);
+ int errstatus;
+
+ if (!(allow_options (r) & OPT_INCLUDES)) return DECLINED;
+ if (r->method_number != M_GET) return DECLINED;
+ if (r->finfo.st_mode == 0) return NOT_FOUND;
+
+ if (*state == xbithack_full
+#ifndef __EMX__
+ /* OS/2 dosen't support Groups. */
+ && (r->finfo.st_mode & S_IXGRP)
+#endif
+ && (errstatus = set_last_modified (r, r->finfo.st_mtime)))
+ return errstatus;
+
+ if(!(f=pfopen(r->pool, r->filename, "r"))) {
+ log_reason("file permissions deny server access", r->filename, r);
+ return FORBIDDEN;
+ }
+
+ hard_timeout ("send", r);
+ send_http_header(r);
+
+ if (r->header_only) {
+ kill_timeout (r);
+ pfclose (r->pool, f);
+ return OK;
+ }
+
+ if (r->main) {
+ /* Kludge --- for nested includes, we want to keep the
+ * subprocess environment of the base document (for compatibility);
+ * that means torquing our own last_modified date as well so that
+ * the LAST_MODIFIED variable gets reset to the proper value if
+ * the nested document resets
+ */
+ r->subprocess_env = r->main->subprocess_env;
+ r->finfo.st_mtime= r->main->finfo.st_mtime;
+ } else {
+ add_common_vars (r);
+ add_cgi_vars(r);
+ add_include_vars (r, DEFAULT_TIME_FORMAT);
+ }
+
+ send_parsed_content (f, r);
+
+ kill_timeout (r);
+ return OK;
+}
+
+int send_shtml_file (request_rec *r)
+{
+ r->content_type = "text/html";
+ return send_parsed_file(r);
+}
+
+int xbithack_handler (request_rec *r)
+{
+ enum xbithack *state;
+
+#ifdef __EMX__
+ /* OS/2 dosen't currently support the xbithack. This is being worked on. */
+ return DECLINED;
+#else
+
+ if (!(r->finfo.st_mode & S_IXUSR)) return DECLINED;
+
+ state = (enum xbithack *)get_module_config(r->per_dir_config,
+ &includes_module);
+
+ if (*state == xbithack_off) return DECLINED;
+ return send_parsed_file (r);
+#endif
+}
+
+command_rec includes_cmds[] = {
+{ "XBitHack", set_xbithack, NULL, OR_OPTIONS, TAKE1, "Off, On, or Full" },
+{ NULL }
+};
+
+handler_rec includes_handlers[] = {
+{ INCLUDES_MAGIC_TYPE, send_shtml_file },
+{ INCLUDES_MAGIC_TYPE3, send_shtml_file },
+{ "server-parsed", send_parsed_file },
+{ "text/html", xbithack_handler },
+{ NULL }
+};
+
+module includes_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ create_includes_dir_config, /* dir config creater */
+ NULL, /* dir merger --- default is to override */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ includes_cmds, /* command table */
+ includes_handlers, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* fixups */
+ NULL /* logger */
+};
diff --git a/RELEASE_1_1_X/src/modules/standard/mod_info.c b/RELEASE_1_1_X/src/modules/standard/mod_info.c
new file mode 100644
index 00000000000..644d519c141
--- /dev/null
+++ b/RELEASE_1_1_X/src/modules/standard/mod_info.c
@@ -0,0 +1,426 @@
+/* ====================================================================
+ * Copyright (c) 1995, 1996 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+/*
+ * Info Module. Display configuration information for the server and
+ * all included modules.
+ *
+ *
+ * SetHandler server-info
+ *
+ *
+ * GET /info - Returns full configuration page for server and all modules
+ * GET /info?server - Returns server configuration only
+ * GET /info?module_name - Returns configuration for a single module
+ * GET /info?list - Returns quick list of included modules
+ *
+ * Rasmus Lerdorf , May 1996
+ *
+ * 05.01.96 Initial Version
+ *
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_log.h"
+#include "http_main.h"
+#include "http_protocol.h"
+#include "util_script.h"
+
+typedef struct mod_info_config_lines {
+ char *cmd;
+ char *line;
+ struct mod_info_config_lines *next;
+} mod_info_config_lines;
+
+module info_module;
+extern module *top_module;
+
+char *mod_info_html_cmd_string(char *string) {
+ char *s,*t;
+ static char ret[64]; /* What is the max size of a command? */
+
+ ret[0]='\0';
+ s = string;
+ t=ret;
+ while(*s) {
+ if(*s=='<') { strcat(t,"<"); t+=4*sizeof(char); }
+ else if(*s=='>') { strcat(t,">"); t+=4*sizeof(char); }
+ else *t++=*s;
+ s++;
+ *t='\0';
+ }
+ return(ret);
+}
+
+mod_info_config_lines *mod_info_load_config(pool *p, char *filename) {
+ char s[MAX_STRING_LEN];
+ FILE *fp;
+ mod_info_config_lines *new, *ret=NULL, *prev=NULL;
+ char *t,*tt,o;
+
+ fp = pfopen(p,filename,"r");
+ if(!fp) return NULL;
+ while(!cfg_getline(s,MAX_STRING_LEN,fp)) {
+ if(*s=='#') continue; /* skip comments */
+ new = palloc(p,sizeof(struct mod_info_config_lines));
+ new->next = NULL;
+ if(!ret) ret=new;
+ if(prev) prev->next=new;
+ t=strchr(s,' ');
+ tt=strchr(s,'\t');
+ if(t && tt) t = (tcmd = pstrdup(p,s);
+ new->line = pstrdup(p,t+1);
+ *t=o;
+ } else {
+ new->cmd = pstrdup(p,s);
+ new->line = NULL;
+ }
+ prev=new;
+ }
+ pfclose(p,fp);
+ return(ret);
+}
+
+void mod_info_module_cmds(request_rec *r, mod_info_config_lines *cfg, command_rec *cmds,char *label) {
+ command_rec *cmd=cmds;
+ mod_info_config_lines *li=cfg,*li_st=NULL,*li_se=NULL,*block_start=NULL;
+ int lab=0, nest=0;
+
+ while(li) {
+ if(!strncasecmp(li->cmd,"cmd,"cmd,"next;
+ nest++;
+ continue;
+ } else if(nest && (!strncasecmp(li->cmd,"cmd,"cmd,"",r);
+ if(nest==2) rputs(" ",r);
+ rputs(mod_info_html_cmd_string(li->cmd),r);
+ rputs(" ",r);
+ if(li->line) rputs(mod_info_html_cmd_string(li->line),r);
+ rputs("\n",r);
+ nest--;
+ if(!nest) {
+ block_start=NULL;
+ li_st=NULL;
+ } else {
+ block_start=li_st;
+ }
+ li_se=NULL;
+ } else {
+ nest--;
+ if(!nest) {
+ li_st=NULL;
+ }
+ li_se=NULL;
+ }
+ } else {
+ nest--;
+ if(!nest) {
+ li_st=NULL;
+ }
+ li_se=NULL;
+ }
+ li=li->next;
+ continue;
+ }
+ cmd = cmds;
+ while(cmd) {
+ if(cmd->name) {
+ if(!strcasecmp(cmd->name,li->cmd)) {
+ if(!lab) {
+ rputs("",r);
+ rputs(label,r);
+ rputs("\n",r);
+ lab=1;
+ }
+ if(((nest && block_start==NULL) || (nest==2 && block_start==li_st))
+ && (strncasecmp(li->cmd,"cmd,"cmd,"cmd,"cmd,"cmd,"",r);
+ rputs(mod_info_html_cmd_string(li_st->cmd),r);
+ rputs(" ",r);
+ if(li_st->line) rputs(mod_info_html_cmd_string(li_st->line),r);
+ rputs("\n",r);
+ block_start=li_st;
+ if(li_se) {
+ rputs(" ",r);
+ rputs(mod_info_html_cmd_string(li_se->cmd),r);
+ rputs(" ",r);
+ if(li_se->line) rputs(mod_info_html_cmd_string(li_se->line),r);
+ rputs("\n",r);
+ block_start=li_se;
+ }
+ }
+ rputs("",r);
+ if(nest) rputs(" ",r);
+ if(nest==2) rputs(" ",r);
+ rputs(mod_info_html_cmd_string(li->cmd),r);
+ if(li->line) {
+ rputs(" ",r);
+ rputs(mod_info_html_cmd_string(li->line),r);
+ rputs("",r);
+ }
+ }
+ } else break;
+ cmd++;
+ }
+ li = li->next;
+ }
+}
+
+int display_info(request_rec *r) {
+ module *modp = NULL;
+ char buf[256];
+ extern char *module_names[];
+ char **names = module_names;
+ command_rec *cmd=NULL;
+ handler_rec *hand=NULL;
+ server_rec *serv = r->server;
+ int comma=0;
+ mod_info_config_lines *mod_info_cfg_httpd=NULL;
+ mod_info_config_lines *mod_info_cfg_srm=NULL;
+ mod_info_config_lines *mod_info_cfg_access=NULL;
+ extern int standalone;
+ extern uid_t user_id;
+ extern char *user_name;
+ extern gid_t group_id;
+ extern int max_requests_per_child;
+ extern char *pid_fname;
+ extern char *scoreboard_fname;
+ extern int daemons_to_start;
+ extern int daemons_min_free;
+ extern int daemons_max_free;
+ extern int daemons_limit;
+ extern char server_root[MAX_STRING_LEN];
+ extern char server_confname[MAX_STRING_LEN];
+
+ /* Init timeout */
+ soft_timeout ("send server info", r);
+ r->content_type = "text/html";
+ send_http_header(r);
+ if(r->header_only) {
+ return 0;
+ }
+
+ rputs("Server Information\n",r);
+ rputs("Apache Server Information
\n",r);
+ if(!r->args || strcasecmp(r->args,"list")) {
+ sprintf(buf,"%s/%s",server_root,server_confname);
+ mod_info_cfg_httpd = mod_info_load_config(r->pool,buf);
+ sprintf(buf,"%s/%s",server_root,serv->srm_confname);
+ mod_info_cfg_srm = mod_info_load_config(r->pool,buf);
+ sprintf(buf,"%s/%s",server_root,serv->access_confname);
+ mod_info_cfg_access = mod_info_load_config(r->pool,buf);
+ if(!r->args) {
+ rputs("Server Settings, ",r);
+ for(modp = top_module, names=module_names; modp; modp = modp->next, names++) {
+ sprintf(buf,"%s",*names,*names);
+ rputs(buf, r);
+ if(modp->next) rputs(", ",r);
+ }
+ rputs("
",r);
+
+ }
+ if(!r->args || !strcasecmp(r->args,"server")) {
+ sprintf(buf,"Server Version: %s
\n",SERVER_VERSION);
+ rputs(buf,r);
+ sprintf(buf,"API Version: %d
\n",MODULE_MAGIC_NUMBER);
+ rputs(buf,r);
+ sprintf(buf,"Run Mode: %s
\n",standalone?"standalone":"inetd");
+ rputs(buf,r);
+ sprintf(buf,"User/Group: %s(%d)/%d
\n",user_name,(int)user_id,(int)group_id);
+ rputs(buf,r);
+ sprintf(buf,"Hostname/port: %s:%d
\n",serv->server_hostname,serv->port);
+ rputs(buf,r);
+ sprintf(buf,"Daemons: start: %d min idle: %d max idle: %d max: %d
\n",daemons_to_start,daemons_min_free,daemons_max_free,daemons_limit);
+ rputs(buf,r);
+ sprintf(buf,"Max Requests: per child: %d per connection: %d
\n",max_requests_per_child,serv->keep_alive);
+ rputs(buf,r);
+ sprintf(buf,"Timeouts: connection: %d keep-alive: %d
",serv->timeout,serv->keep_alive_timeout);
+ rputs(buf,r);
+ sprintf(buf,"Server Root: %s
\n",server_root);
+ rputs(buf,r);
+ sprintf(buf,"Config File: %s
\n",server_confname);
+ rputs(buf,r);
+ sprintf(buf,"PID File: %s
\n",pid_fname);
+ rputs(buf,r);
+ sprintf(buf,"Scoreboard File: %s
\n",scoreboard_fname);
+ rputs(buf,r);
+ }
+ rputs("
",r);
+ for(modp = top_module, names=module_names; modp; modp = modp->next, names++) {
+ if(!r->args || !strcasecmp(*names,r->args)) {
+ sprintf(buf,"- Module Name: %s\n",*names,*names);
+ rputs(buf,r);
+ rputs("
- Content-types affected:",r);
+ hand = modp->handlers;
+ if(hand) {
+ while(hand) {
+ if(hand->content_type) {
+ sprintf(buf," %s\n",hand->content_type);
+ rputs(buf,r);
+ } else break;
+ hand++;
+ if(hand && hand->content_type) rputs(",",r);
+ }
+ } else {
+ rputs(" none",r);
+ }
+ rputs("
- Module Groups: \n",r);
+ if(modp->translate_handler) {
+ rputs("Translate Handler\n",r);
+ comma=1;
+ }
+ if(modp->check_user_id) {
+ if(comma) rputs(", ",r);
+ rputs("User ID Checking\n",r);
+ comma=1;
+ }
+ if(modp->auth_checker) {
+ if(comma) rputs(", ",r);
+ rputs("Authentication Checking\n",r);
+ comma=1;
+ }
+ if(modp->access_checker) {
+ if(comma) rputs(", ",r);
+ rputs("Access Checking\n",r);
+ comma=1;
+ }
+ if(modp->type_checker) {
+ if(comma) rputs(", ",r);
+ rputs("Type Checking\n",r);
+ comma=1;
+ }
+ if(modp->fixer_upper) {
+ if(comma) rputs(", ",r);
+ rputs("Header Fixer\n",r);
+ comma=1;
+ }
+ if(modp->logger) {
+ if(comma) rputs(", ",r);
+ rputs("Logging\n",r);
+ comma=1;
+ }
+ if(!comma) rputs(" none",r);
+ comma=0;
+ rputs("
- Module Configuration Commands: ",r);
+ cmd = modp->cmds;
+ if(cmd) {
+ while(cmd) {
+ if(cmd->name) {
+ sprintf(buf,"
- %s - ",mod_info_html_cmd_string(cmd->name));
+ rputs(buf,r);
+ if(cmd->errmsg) rputs(cmd->errmsg,r);
+ rputs("\n",r);
+ } else break;
+ cmd++;
+ }
+ rputs("
- Current Configuration:\n",r);
+ mod_info_module_cmds(r,mod_info_cfg_httpd,modp->cmds,"httpd.conf");
+ mod_info_module_cmds(r,mod_info_cfg_srm,modp->cmds,"srm.conf");
+ mod_info_module_cmds(r,mod_info_cfg_access,modp->cmds,"access.conf");
+ } else {
+ rputs(" none\n",r);
+ }
+ rputs("
\n",r);
+ if(r->args) break;
+ }
+ }
+ if(!modp && r->args && strcasecmp(r->args,"server")) rputs("No such module\n",r);
+ } else {
+ for(modp = top_module; modp; modp = modp->next, names++) {
+ rputs(*names,r);
+ if(modp->next) rputs("
",r);
+ }
+ }
+ rputs("
\n",r);
+ /* Done, turn off timeout, close file and return */
+ return 0;
+}
+
+handler_rec info_handlers[] = {
+ { "server-info", display_info },
+ { NULL }
+};
+
+module info_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ NULL, /* dir config creater */
+ NULL, /* dir merger --- default is to override */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ NULL, /* command table */
+ info_handlers, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* fixups */
+ NULL /* logger */
+};
diff --git a/RELEASE_1_1_X/src/modules/standard/mod_log_agent.c b/RELEASE_1_1_X/src/modules/standard/mod_log_agent.c
new file mode 100644
index 00000000000..14c185b9fa5
--- /dev/null
+++ b/RELEASE_1_1_X/src/modules/standard/mod_log_agent.c
@@ -0,0 +1,193 @@
+
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+
+
+#include "httpd.h"
+#include "http_config.h"
+
+module agent_log_module;
+
+static int xfer_flags = ( O_WRONLY | O_APPEND | O_CREAT );
+#ifdef __EMX__
+/* OS/2 dosen't support users and groups */
+static mode_t xfer_mode = ( S_IREAD | S_IWRITE );
+#else
+static mode_t xfer_mode = ( S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH );
+#endif
+
+typedef struct {
+ char *fname;
+ int agent_fd;
+} agent_log_state;
+
+void *make_agent_log_state (pool *p, server_rec *s)
+{
+ agent_log_state *cls =
+ (agent_log_state *)palloc (p, sizeof (agent_log_state));
+
+ cls->fname = "";
+ cls->agent_fd = -1;
+
+
+ return (void *)cls;
+}
+
+char *set_agent_log (cmd_parms *parms, void *dummy, char *arg)
+{
+ agent_log_state *cls = get_module_config (parms->server->module_config,
+ &agent_log_module);
+
+ cls->fname = arg;
+ return NULL;
+}
+
+command_rec agent_log_cmds[] = {
+{ "AgentLog", set_agent_log, NULL, RSRC_CONF, TAKE1,
+ "the filename of the agent log" },
+{ NULL }
+};
+
+void agent_log_child (void *cmd)
+{
+ /* Child process code for 'AgentLog "|..."';
+ * may want a common framework for this, since I expect it will
+ * be common for other foo-loggers to want this sort of thing...
+ */
+
+ cleanup_for_exec();
+ signal (SIGHUP, SIG_IGN);
+ execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL);
+ exit (1);
+}
+
+void open_agent_log (server_rec *s, pool *p)
+{
+ agent_log_state *cls = get_module_config (s->module_config,
+ &agent_log_module);
+
+ char *fname = server_root_relative (p, cls->fname);
+
+ if (cls->agent_fd > 0) return; /* virtual log shared w/main server */
+
+ if (*cls->fname == '|') {
+ FILE *dummy;
+
+ spawn_child(p, agent_log_child, (void *)(cls->fname+1),
+ kill_after_timeout, &dummy, NULL);
+
+ if (dummy == NULL) {
+ fprintf (stderr, "Couldn't fork child for AgentLog process\n");
+ exit (1);
+ }
+
+ cls->agent_fd = fileno (dummy);
+ }
+ else if(*cls->fname != '\0') {
+ if((cls->agent_fd = popenf(p, fname, xfer_flags, xfer_mode)) < 0) {
+ fprintf(stderr,"httpd: could not open agent log file %s.\n", fname);
+ perror("open");
+ exit(1);
+ }
+ }
+}
+
+void init_agent_log (server_rec *s, pool *p)
+{
+ for (; s; s = s->next) open_agent_log (s, p);
+}
+
+int agent_log_transaction(request_rec *orig)
+{
+ agent_log_state *cls = get_module_config (orig->server->module_config,
+ &agent_log_module);
+
+ char str[HUGE_STRING_LEN];
+ char *agent;
+ request_rec *r;
+
+ if(cls->agent_fd <0)
+ return OK;
+
+ for (r = orig; r->next; r = r->next)
+ continue;
+ if (*cls->fname == '\0') /* Don't log agent */
+ return DECLINED;
+
+ agent = table_get(orig->headers_in, "User-Agent");
+ if(agent != NULL)
+ {
+ sprintf(str, "%s\n", agent);
+ write(cls->agent_fd, str, strlen(str));
+ }
+
+ return OK;
+}
+
+module agent_log_module = {
+ STANDARD_MODULE_STUFF,
+ init_agent_log, /* initializer */
+ NULL, /* create per-dir config */
+ NULL, /* merge per-dir config */
+ make_agent_log_state, /* server config */
+ NULL, /* merge server config */
+ agent_log_cmds, /* command table */
+ NULL, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* fixups */
+ agent_log_transaction /* logger */
+};
diff --git a/RELEASE_1_1_X/src/modules/standard/mod_log_config.c b/RELEASE_1_1_X/src/modules/standard/mod_log_config.c
new file mode 100644
index 00000000000..3084cccafae
--- /dev/null
+++ b/RELEASE_1_1_X/src/modules/standard/mod_log_config.c
@@ -0,0 +1,573 @@
+
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+
+/*
+ * This is module implements the TransferLog directive (same as the
+ * common log module), and an additional directive, LogFormat.
+ *
+ * The argument to LogFormat is a string, which can include literal
+ * characters copied into the log files, and '%' directives as follows:
+ *
+ * %...h: remote host
+ * %...l: remote logname (from identd, if supplied)
+ * %...u: remote user (from auth; may be bogus if return status (%s) is 401)
+ * %...t: time, in common log format time format
+ * %...r: first line of request
+ * %...s: status. For requests that got internally redirected, this
+ * is status of the *original* request --- %...>s for the last.
+ * %...b: bytes sent.
+ * %...{Foobar}i: The contents of Foobar: header line(s) in the request
+ * sent to the client.
+ * %...{Foobar}o: The contents of Foobar: header line(s) in the reply.
+ *
+ * The '...' can be nothing at all (e.g. "%h %u %r %s %b"), or it can
+ * indicate conditions for inclusion of the item (which will cause it
+ * to be replaced with '-' if the condition is not met). Note that
+ * there is no escaping performed on the strings from %r, %...i and
+ * %...o; some with long memories may remember that I thought this was
+ * a bad idea, once upon a time, and I'm still not comfortable with
+ * it, but it is difficult to see how to "do the right thing" with all
+ * of '%..i', unless we URL-escape everything and break with CLF.
+ *
+ * The forms of condition are a list of HTTP status codes, which may
+ * or may not be preceded by '!'. Thus, '%400,501{User-agent}i' logs
+ * User-agent: on 400 errors and 501 errors (Bad Request, Not
+ * Implemented) only; '%!200,304,302{Referer}i' logs Referer: on all
+ * requests which did *not* return some sort of normal status.
+ *
+ * The default LogFormat reproduces CLF; see below.
+ *
+ * The way this is supposed to work with virtual hosts is as follows:
+ * a virtual host can have its own LogFormat, or its own TransferLog.
+ * If it doesn't have its own LogFormat, it inherits from the main
+ * server. If it doesn't have its own TransferLog, it writes to the
+ * same descriptor (meaning the same process for "| ...").
+ *
+ * That means that you can do things like:
+ *
+ *
+ * LogFormat "hosta ..."
+ * ...
+ *
+ *
+ *
+ * LogFormat "hostb ..."
+ * ...
+ *
+ *
+ * ... to have different virtual servers write into the same log file,
+ * but have some indication which host they came from, though a %v
+ * directive may well be a better way to handle this.
+ *
+ * --- rst */
+
+#define DEFAULT_LOG_FORMAT "%h %l %u %t \"%r\" %s %b"
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h" /* For REMOTE_NAME */
+
+module config_log_module;
+
+static int xfer_flags = ( O_WRONLY | O_APPEND | O_CREAT );
+#ifdef __EMX__
+/* OS/2 dosen't support users and groups */
+static mode_t xfer_mode = ( S_IREAD | S_IWRITE );
+#else
+static mode_t xfer_mode = ( S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH );
+#endif
+
+typedef struct {
+ char *fname;
+ array_header *format;
+ int log_fd;
+} config_log_state;
+
+/*
+ * Format items...
+ */
+
+typedef char *(*item_key_func)(request_rec *, char *);
+
+typedef struct {
+ item_key_func func;
+ char *arg;
+ int condition_sense;
+ int want_orig;
+ array_header *conditions;
+} log_format_item;
+
+char *format_integer(pool *p, int i)
+{
+ char dummy[40];
+ sprintf (dummy, "%d", i);
+ return pstrdup (p, dummy);
+}
+
+static char *pfmt(pool *p, int i)
+{
+ if (i <= 0) return "-";
+ else return format_integer (p, i);
+}
+
+char *constant_item (request_rec *dummy, char *stuff) { return stuff; }
+
+char *log_remote_host (request_rec *r, char *a)
+{ return (char *)get_remote_host(r->connection, r->per_dir_config, REMOTE_NAME); }
+
+char *log_remote_logname(request_rec *r, char *a)
+{return (char *)get_remote_logname(r);}
+
+char *log_remote_user (request_rec *r, char *a)
+{ return r->connection->user; }
+
+char *log_request_line (request_rec *r, char *a)
+{ return r->the_request; }
+
+char *log_status (request_rec *r, char *a)
+{ return pfmt(r->pool, r->status); }
+
+char *log_bytes_sent (request_rec *r, char *a)
+{
+ if (!r->sent_bodyct) return "-";
+ else
+ {
+ long int bs;
+ char dummy[40];
+ bgetopt(r->connection->client, BO_BYTECT, &bs);
+ sprintf(dummy, "%ld", bs);
+ return pstrdup(r->pool, dummy);
+ }
+}
+
+char *log_header_in (request_rec *r, char *a)
+{ return table_get (r->headers_in, a); }
+
+char *log_header_out (request_rec *r, char *a)
+{
+ char *cp = table_get (r->headers_out, a);
+ if (cp) return cp;
+ return table_get (r->err_headers_out, a);
+}
+
+char *log_env_var (request_rec *r, char *a)
+{ return table_get (r->subprocess_env, a); }
+
+char *log_request_time (request_rec *r, char *a)
+{
+ long timz;
+ struct tm *t;
+ char tstr[MAX_STRING_LEN],sign;
+
+ t = get_gmtoff(&timz);
+ sign = (timz < 0 ? '-' : '+');
+ if(timz < 0)
+ timz = -timz;
+
+ strftime(tstr,MAX_STRING_LEN,"[%d/%b/%Y:%H:%M:%S ",t);
+
+ sprintf (tstr + strlen(tstr), "%c%02ld%02ld]",
+ sign, timz/3600, timz%3600);
+
+ return pstrdup (r->pool, tstr);
+}
+
+/*****************************************************************
+ *
+ * Parsing the log format string
+ */
+
+struct log_item_list {
+ char ch;
+ item_key_func func;
+ int want_orig_default;
+} log_item_keys[] = {
+ { 'h', log_remote_host, 0 },
+ { 'l', log_remote_logname, 0 },
+ { 'u', log_remote_user, 0 },
+ { 't', log_request_time, 0 },
+ { 'r', log_request_line, 1 },
+ { 's', log_status, 1 },
+ { 'b', log_bytes_sent, 0 },
+ { 'i', log_header_in, 0 },
+ { 'o', log_header_out, 0 },
+ { 'e', log_env_var, 0 },
+ { '\0' }
+};
+
+struct log_item_list *find_log_func (char k)
+{
+ int i;
+
+ for (i = 0; log_item_keys[i].ch; ++i)
+ if (k == log_item_keys[i].ch)
+ return &log_item_keys[i];
+
+ return NULL;
+}
+
+char *log_format_substring (pool *p, char *start, char *end)
+{
+ char *res = palloc (p, end - start + 1);
+ strncpy (res, start, end - start);
+ res[end - start] = '\0';
+ return res;
+}
+
+char *parse_log_misc_string (pool *p, log_format_item *it, char **sa)
+{
+ char *s = *sa;
+
+ it->func = constant_item;
+ it->conditions = NULL;
+
+ while (*s && *s != '%') ++s;
+ it->arg = log_format_substring (p, *sa, s);
+ *sa = s;
+
+ return NULL;
+}
+
+char *parse_log_item (pool *p, log_format_item *it, char **sa)
+{
+ char *s = *sa;
+ if (*s != '%') return parse_log_misc_string (p, it, sa);
+
+ ++s;
+ it->condition_sense = 0;
+ it->conditions = NULL;
+ it->want_orig = -1;
+ it->arg = ""; /* For safety's sake... */
+
+ while (*s) {
+ int i;
+ struct log_item_list *l;
+
+ switch (*s) {
+ case '!':
+ ++s;
+ it->condition_sense = !it->condition_sense;
+ break;
+
+ case '<':
+ ++s;
+ it->want_orig = 1;
+ break;
+
+ case '>':
+ ++s;
+ it->want_orig = 0;
+ break;
+
+ case ',':
+ ++s;
+ break;
+
+ case '{':
+ ++s;
+ it->arg = getword (p, &s, '}');
+ break;
+
+ case '0': case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ i = *s - '0';
+ while (isdigit (*++s)) i = i * 10 + (*s) - '0';
+ if (!it->conditions)
+ it->conditions = make_array (p, 4, sizeof(int));
+ *(int *)push_array(it->conditions) = i;
+ break;
+
+ default:
+ l = find_log_func (*s++);
+ if (!l) {
+ char dummy[] = { '\0', '\0'};
+ dummy[0] = s[-1];
+ return pstrcat (p, "Unrecognized LogFormat directive %",
+ dummy, NULL);
+ }
+ it->func = l->func;
+ if (it->want_orig == -1) it->want_orig = l->want_orig_default;
+ *sa = s;
+ return NULL;
+ }
+ }
+
+ return "Ran off end of LogFormat parsing args to some directive";
+}
+
+array_header *parse_log_string (pool *p, char *s, char **err)
+{
+ array_header *a = make_array (p, 30, sizeof (log_format_item));
+ char *res;
+
+ while (*s) {
+ if ((res = parse_log_item (p, (log_format_item *)push_array(a), &s))) {
+ *err = res;
+ return NULL;
+ }
+ }
+
+ s = "\n";
+ parse_log_item (p, (log_format_item *)push_array(a), &s);
+ return a;
+}
+
+/*****************************************************************
+ *
+ * Actually logging.
+ */
+
+char *process_item(request_rec *r, request_rec *orig, log_format_item *item)
+{
+ char *cp;
+
+ /* First, see if we need to process this thing at all... */
+
+ if (item->conditions && item->conditions->nelts != 0) {
+ int i;
+ int *conds = (int *)item->conditions->elts;
+ int in_list = 0;
+
+ for (i = 0; i < item->conditions->nelts; ++i)
+ if (r->status == conds[i]) {
+ in_list = 1;
+ break;
+ }
+
+ if ((item->condition_sense && in_list)
+ || (!item->condition_sense && !in_list))
+ {
+ return "-";
+ }
+ }
+
+ /* We do. Do it... */
+
+ cp = (*item->func)(item->want_orig ? orig : r, item->arg);
+ return cp ? cp : "-";
+}
+
+int config_log_transaction(request_rec *r)
+{
+ config_log_state *cls = get_module_config (r->server->module_config,
+ &config_log_module);
+
+ array_header *strsa= make_array(r->pool, cls->format->nelts,sizeof(char*));
+ log_format_item *items = (log_format_item *)cls->format->elts;
+ char *str, **strs, *s;
+ request_rec *orig;
+ int i;
+ int len = 0;
+
+ orig = r;
+ while (orig->prev) orig = orig->prev;
+ while (r->next) r = r->next;
+
+ for (i = 0; i < cls->format->nelts; ++i)
+ *((char**)push_array (strsa)) = process_item (r, orig, &items[i]);
+
+ strs = (char **)strsa->elts;
+
+ for (i = 0; i < cls->format->nelts; ++i)
+ len += strlen (strs[i]);
+
+ str = palloc (r->pool, len + 1);
+
+ for (i = 0, s = str; i < cls->format->nelts; ++i) {
+ strcpy (s, strs[i]);
+ s += strlen (strs[i]);
+ }
+
+ write(cls->log_fd, str, strlen(str));
+
+ return OK;
+}
+
+/*****************************************************************
+ *
+ * Module glue...
+ */
+
+void *make_config_log_state (pool *p, server_rec *s)
+{
+ config_log_state *cls =
+ (config_log_state *)palloc (p, sizeof (config_log_state));
+
+ cls->fname = NULL;
+ cls->format = NULL;
+ cls->log_fd = -1;
+
+ return (void *)cls;
+}
+
+char *set_config_log (cmd_parms *parms, void *dummy, char *arg)
+{
+ config_log_state *cls = get_module_config (parms->server->module_config,
+ &config_log_module);
+
+ cls->fname = arg;
+ return NULL;
+}
+
+char *log_format (cmd_parms *cmd, void *dummy, char *arg)
+{
+ char *err_string = NULL;
+ config_log_state *cls = get_module_config (cmd->server->module_config,
+ &config_log_module);
+
+ cls->format = parse_log_string (cmd->pool, arg, &err_string);
+ return err_string;
+}
+
+command_rec config_log_cmds[] = {
+{ "TransferLog", set_config_log, NULL, RSRC_CONF, TAKE1,
+ "the filename of the access log" },
+{ "LogFormat", log_format, NULL, RSRC_CONF, TAKE1,
+ "a log format string (see docs)" },
+{ NULL }
+};
+
+void config_log_child (void *cmd)
+{
+ /* Child process code for 'TransferLog "|..."';
+ * may want a common framework for this, since I expect it will
+ * be common for other foo-loggers to want this sort of thing...
+ */
+
+ cleanup_for_exec();
+ signal (SIGHUP, SIG_IGN);
+ execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL);
+ fprintf (stderr, "Exec of shell for logging failed!!!\n");
+ exit (1);
+}
+
+config_log_state *open_config_log (server_rec *s, pool *p,
+ config_log_state *defaults)
+{
+ config_log_state *cls = get_module_config (s->module_config,
+ &config_log_module);
+
+ if (cls->log_fd > 0) return cls; /* virtual config shared w/main server */
+
+ if (cls->format == NULL) {
+ char *dummy;
+
+ if (defaults) cls->format = defaults->format;
+ else cls->format = parse_log_string (p, DEFAULT_LOG_FORMAT, &dummy);
+ }
+
+ if (cls->fname == NULL) {
+ if (defaults) {
+ cls->log_fd = defaults->log_fd;
+ return cls;
+ }
+ else cls->fname = DEFAULT_XFERLOG;
+ }
+
+ if (*cls->fname == '|') {
+ FILE *dummy;
+
+ spawn_child(p, config_log_child, (void *)(cls->fname+1),
+ kill_after_timeout, &dummy, NULL);
+
+ if (dummy == NULL) {
+ fprintf (stderr, "Couldn't fork child for TransferLog process\n");
+ exit (1);
+ }
+
+ cls->log_fd = fileno (dummy);
+ }
+ else {
+ char *fname = server_root_relative (p, cls->fname);
+ if((cls->log_fd = popenf(p, fname, xfer_flags, xfer_mode)) < 0) {
+ fprintf (stderr,
+ "httpd: could not open transfer log file %s.\n", fname);
+ perror("open");
+ exit(1);
+ }
+ }
+
+ return cls;
+}
+
+void init_config_log (server_rec *s, pool *p)
+{
+ /* First, do "physical" server, which gets default log fd and format
+ * for the virtual servers, if they don't override...
+ */
+
+ config_log_state *default_conf = open_config_log (s, p, NULL);
+
+ /* Then, virtual servers */
+
+ for (s = s->next; s; s = s->next) open_config_log (s, p, default_conf);
+}
+
+module config_log_module = {
+ STANDARD_MODULE_STUFF,
+ init_config_log, /* initializer */
+ NULL, /* create per-dir config */
+ NULL, /* merge per-dir config */
+ make_config_log_state, /* server config */
+ NULL, /* merge server config */
+ config_log_cmds, /* command table */
+ NULL, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* fixups */
+ config_log_transaction /* logger */
+};
diff --git a/RELEASE_1_1_X/src/modules/standard/mod_log_referer.c b/RELEASE_1_1_X/src/modules/standard/mod_log_referer.c
new file mode 100644
index 00000000000..26ac28d0a5a
--- /dev/null
+++ b/RELEASE_1_1_X/src/modules/standard/mod_log_referer.c
@@ -0,0 +1,231 @@
+
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+
+
+#include "httpd.h"
+#include "http_config.h"
+
+module referer_log_module;
+
+static int xfer_flags = ( O_WRONLY | O_APPEND | O_CREAT );
+
+#ifdef __EMX__
+/* OS/2 lacks support for users and groups */
+static mode_t xfer_mode = ( S_IREAD | S_IWRITE );
+#else
+static mode_t xfer_mode = ( S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH );
+#endif
+
+typedef struct {
+ char *fname;
+ int referer_fd;
+ array_header *referer_ignore_list;
+} referer_log_state;
+
+void *make_referer_log_state (pool *p, server_rec *s)
+{
+ referer_log_state *cls =
+ (referer_log_state *)palloc (p, sizeof (referer_log_state));
+
+ cls->fname = "";
+ cls->referer_fd = -1;
+ cls->referer_ignore_list = make_array(p, 1, sizeof(char *));
+ return (void *)cls;
+}
+
+char *set_referer_log (cmd_parms *parms, void *dummy, char *arg)
+{
+ referer_log_state *cls = get_module_config (parms->server->module_config,
+ &referer_log_module);
+
+ cls->fname = arg;
+ return NULL;
+}
+
+char *add_referer_ignore (cmd_parms *parms, void *dummy, char *arg)
+{
+ char **addme;
+ referer_log_state *cls = get_module_config (parms->server->module_config,
+ &referer_log_module);
+
+ addme = push_array(cls->referer_ignore_list);
+ *addme = pstrdup(cls->referer_ignore_list->pool, arg);
+ return NULL;
+}
+
+command_rec referer_log_cmds[] = {
+{ "RefererLog", set_referer_log, NULL, RSRC_CONF, TAKE1,
+ "the filename of the referer log" },
+{ "RefererIgnore", add_referer_ignore, NULL, RSRC_CONF, ITERATE,
+ "referer hostnames to ignore" },
+{ NULL }
+};
+
+void referer_log_child (void *cmd)
+{
+ /* Child process code for 'RefererLog "|..."';
+ * may want a common framework for this, since I expect it will
+ * be common for other foo-loggers to want this sort of thing...
+ */
+
+ cleanup_for_exec();
+ signal (SIGHUP, SIG_IGN);
+ execl (SHELL_PATH, SHELL_PATH, "-c", (char *)cmd, NULL);
+ fprintf (stderr, "Exec of shell for logging failed!!!\n");
+ exit (1);
+}
+
+void open_referer_log (server_rec *s, pool *p)
+{
+ referer_log_state *cls = get_module_config (s->module_config,
+ &referer_log_module);
+
+ char *fname = server_root_relative (p, cls->fname);
+
+ if (cls->referer_fd > 0) return; /* virtual log shared w/main server */
+
+ if (*cls->fname == '|') {
+ FILE *dummy;
+
+ spawn_child(p, referer_log_child, (void *)(cls->fname+1),
+ kill_after_timeout, &dummy, NULL);
+
+ if (dummy == NULL) {
+ fprintf (stderr, "Couldn't fork child for RefererLog process\n");
+ exit (1);
+ }
+
+ cls->referer_fd = fileno (dummy);
+ }
+ else if(*cls->fname != '\0') {
+ if((cls->referer_fd = popenf(p, fname, xfer_flags, xfer_mode)) < 0) {
+ fprintf(stderr,"httpd: could not open referer log file %s.\n", fname);
+ perror("open");
+ exit(1);
+ }
+ }
+}
+
+void init_referer_log (server_rec *s, pool *p)
+{
+ for (; s; s = s->next) open_referer_log (s, p);
+}
+
+int referer_log_transaction(request_rec *orig)
+{
+ char **ptrptr, **ptrptr2;
+ referer_log_state *cls = get_module_config (orig->server->module_config,
+ &referer_log_module);
+
+ char *str;
+ char *referer;
+ request_rec *r;
+
+ if(cls->referer_fd <0)
+ return OK;
+
+ for (r = orig; r->next; r = r->next)
+ continue;
+ if (*cls->fname == '\0') /* Don't log referer */
+ return DECLINED;
+
+ referer = table_get(orig->headers_in, "Referer");
+ if(referer != NULL)
+ {
+
+
+ /* The following is an upsetting mess of pointers, I'm sorry
+ Anyone with the motiviation and/or the time should feel free
+ to make this cleaner... */
+
+ ptrptr2 = (char **) (cls->referer_ignore_list->elts +
+ (cls->referer_ignore_list->nelts *
+ cls->referer_ignore_list->elt_size));
+
+ /* Go through each element of the ignore list and compare it to the
+ referer_host. If we get a match, return without logging */
+
+ for(ptrptr = (char **) cls->referer_ignore_list->elts;
+ ptrptr < ptrptr2;
+ ptrptr = (char **)((char *)ptrptr + cls->referer_ignore_list->elt_size))
+ {
+ if(strstr(referer, *ptrptr))
+ return OK;
+ }
+
+
+ str = pstrcat(orig->pool, referer, " -> ", r->uri, "\n", NULL);
+ write(cls->referer_fd, str, strlen(str));
+ }
+
+ return OK;
+}
+
+module referer_log_module = {
+ STANDARD_MODULE_STUFF,
+ init_referer_log, /* initializer */
+ NULL, /* create per-dir config */
+ NULL, /* merge per-dir config */
+ make_referer_log_state, /* server config */
+ NULL, /* merge server config */
+ referer_log_cmds, /* command table */
+ NULL, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* fixups */
+ referer_log_transaction /* logger */
+};
diff --git a/RELEASE_1_1_X/src/modules/standard/mod_mime.c b/RELEASE_1_1_X/src/modules/standard/mod_mime.c
new file mode 100644
index 00000000000..74393f42abb
--- /dev/null
+++ b/RELEASE_1_1_X/src/modules/standard/mod_mime.c
@@ -0,0 +1,315 @@
+
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+
+/*
+ * http_mime.c: Sends/gets MIME headers for requests
+ *
+ * Rob McCool
+ *
+ */
+
+#define MIME_PRIVATE
+
+#include "httpd.h"
+#include "http_config.h"
+
+typedef struct {
+ table *forced_types; /* Additional AddTyped stuff */
+ table *encoding_types; /* Added with AddEncoding... */
+ table *language_types; /* Added with AddLanguage... */
+ table *handlers; /* Added with AddHandler... */
+
+ char *type; /* Type forced with ForceType */
+ char *handler; /* Handler forced with SetHandler */
+} mime_dir_config;
+
+module mime_module;
+
+void *create_mime_dir_config (pool *p, char *dummy)
+{
+ mime_dir_config *new =
+ (mime_dir_config *) palloc (p, sizeof(mime_dir_config));
+
+ new->forced_types = make_table (p, 4);
+ new->encoding_types = make_table (p, 4);
+ new->language_types = make_table (p, 4);
+ new->handlers = make_table (p, 4);
+
+ new->type = NULL;
+ new->handler = NULL;
+
+ return new;
+}
+
+void *merge_mime_dir_configs (pool *p, void *basev, void *addv)
+{
+ mime_dir_config *base = (mime_dir_config *)basev;
+ mime_dir_config *add = (mime_dir_config *)addv;
+ mime_dir_config *new =
+ (mime_dir_config *)palloc (p, sizeof(mime_dir_config));
+
+ new->forced_types = overlay_tables (p, add->forced_types,
+ base->forced_types);
+ new->encoding_types = overlay_tables (p, add->encoding_types,
+ base->encoding_types);
+ new->language_types = overlay_tables (p, add->language_types,
+ base->language_types);
+ new->handlers = overlay_tables (p, add->handlers,
+ base->handlers);
+
+ new->type = add->type ? add->type : base->type;
+ new->handler = add->handler ? add->handler : base->handler;
+
+ return new;
+}
+
+char *add_type(cmd_parms *cmd, mime_dir_config *m, char *ct, char *ext)
+{
+ if (*ext == '.') ++ext;
+ table_set (m->forced_types, ext, ct);
+ return NULL;
+}
+
+char *add_encoding(cmd_parms *cmd, mime_dir_config *m, char *enc, char *ext)
+{
+ if (*ext == '.') ++ext;
+ table_set (m->encoding_types, ext, enc);
+ return NULL;
+}
+
+char *add_language(cmd_parms *cmd, mime_dir_config *m, char *lang, char *ext)
+{
+ if (*ext == '.') ++ext;
+ table_set (m->language_types, ext, lang);
+ return NULL;
+}
+
+char *add_handler(cmd_parms *cmd, mime_dir_config *m, char *hdlr, char *ext)
+{
+ if (*ext == '.') ++ext;
+ table_set (m->handlers, ext, hdlr);
+ return NULL;
+}
+
+/* The sole bit of server configuration that the MIME module has is
+ * the name of its config file, so...
+ */
+
+char *set_types_config (cmd_parms *cmd, void *dummy, char *arg)
+{
+ set_module_config (cmd->server->module_config, &mime_module,
+ pstrdup (cmd->pool, arg));
+ return NULL;
+}
+
+command_rec mime_cmds[] = {
+{ "AddType", add_type, NULL, OR_FILEINFO, ITERATE2,
+ "a mime type followed by one or more file extensions" },
+{ "AddEncoding", add_encoding, NULL, OR_FILEINFO, ITERATE2,
+ "an encoding (e.g., gzip), followed by one or more file extensions" },
+{ "AddLanguage", add_language, NULL, OR_FILEINFO, ITERATE2,
+ "a language (e.g., fr), followed by one or more file extensions" },
+{ "AddHandler", add_handler, NULL, OR_FILEINFO, ITERATE2,
+ "a handler name followed by one or more file extensions" },
+{ "ForceType", set_string_slot, (void*)XtOffsetOf(mime_dir_config, type),
+ OR_FILEINFO, TAKE1, "a media type" },
+{ "SetHandler", set_string_slot, (void*)XtOffsetOf(mime_dir_config, handler),
+ OR_FILEINFO, TAKE1, "a handler name" },
+{ "TypesConfig", set_types_config, NULL, RSRC_CONF, TAKE1,
+ "the MIME types config file" },
+{ NULL }
+};
+
+/* Hash table --- only one of these per daemon; virtual hosts can
+ * get private versions through AddType...
+ */
+
+#define MIME_HASHSIZE 27
+#define hash(i) (isalpha(i) ? (tolower(i)) - 'a' : 26)
+
+static table *hash_buckets[MIME_HASHSIZE];
+
+void init_mime (server_rec *s, pool *p)
+{
+ FILE *f;
+ char l[MAX_STRING_LEN];
+ int x;
+ char *types_confname = get_module_config (s->module_config, &mime_module);
+
+ if (!types_confname) types_confname = TYPES_CONFIG_FILE;
+
+ types_confname = server_root_relative (p, types_confname);
+
+ if(!(f = fopen(types_confname,"r"))) {
+ fprintf(stderr,"httpd: could not open mime types file %s\n",
+ types_confname);
+ perror("fopen");
+ exit(1);
+ }
+
+ for(x=0;x<27;x++)
+ hash_buckets[x] = make_table (p, 10);
+
+ while(!(cfg_getline(l,MAX_STRING_LEN,f))) {
+ char *ll = l, *ct;
+
+ if(l[0] == '#') continue;
+ ct = getword_conf (p, &ll);
+
+ while(ll[0]) {
+ char *ext = getword_conf (p, &ll);
+ str_tolower (ext); /* ??? */
+ table_set (hash_buckets[hash(ext[0])], ext, ct);
+ }
+ }
+ fclose(f);
+}
+
+int find_ct(request_rec *r)
+{
+ char *fn = strrchr(r->filename, '/');
+ mime_dir_config *conf =
+ (mime_dir_config *)get_module_config(r->per_dir_config, &mime_module);
+ char *ext, *type, *orighandler = r->handler;
+
+ if (S_ISDIR(r->finfo.st_mode)) {
+ r->content_type = DIR_MAGIC_TYPE;
+ return OK;
+ }
+
+ /* TM -- FIXME
+ *
+ * if r->filename does not contain a '/', the following passes a null
+ * pointer to getword, causing a SEGV ..
+ */
+
+ if(fn == NULL) fn = r->filename;
+
+ /* Parse filename extensions, which can be in any order */
+ while ((ext = getword(r->pool, &fn, '.')) && *ext) {
+ int found = 0;
+
+ /* Check for Content-Type */
+ if ((type = table_get (conf->forced_types, ext))
+ || (type = table_get (hash_buckets[hash(*ext)], ext))) {
+ r->content_type = type;
+ found = 1;
+ }
+
+ /* Check for Content-Language */
+ if ((type = table_get (conf->language_types, ext))) {
+ r->content_language = type;
+ found = 1;
+ }
+
+ /* Check for Content-Encoding */
+ if ((type = table_get (conf->encoding_types, ext))) {
+ if (!r->content_encoding)
+ r->content_encoding = type;
+ else
+ r->content_encoding = pstrcat(r->pool, r->content_encoding,
+ ", ", type, NULL);
+ found = 1;
+ }
+
+ /* Check for a special handler, but not for proxy request */
+ if ((type = table_get (conf->handlers, ext)) && !r->proxyreq) {
+ r->handler = type;
+ found = 1;
+ }
+
+ /* This is to deal with cases such as foo.gif.bak, which we want
+ * to not have a type. So if we find an unknown extension, we
+ * zap the type/language/encoding and reset the handler
+ */
+
+ if (!found) {
+ r->content_type = NULL;
+ r->content_language = NULL;
+ r->content_encoding = NULL;
+ r->handler = orighandler;
+ }
+
+ }
+
+ /* Check for overrides with ForceType/SetHandler */
+
+ if (conf->type && strcmp(conf->type, "none"))
+ r->content_type = pstrdup(r->pool, conf->type);
+ if (conf->handler && strcmp(conf->handler, "none"))
+ r->handler = pstrdup(r->pool, conf->handler);
+
+ if (!r->content_type) return DECLINED;
+
+ return OK;
+}
+
+
+module mime_module = {
+ STANDARD_MODULE_STUFF,
+ init_mime, /* initializer */
+ create_mime_dir_config,
+ merge_mime_dir_configs,
+ NULL, /* server config */
+ NULL, /* merge server config */
+ mime_cmds,
+ NULL, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ find_ct, /* type_checker */
+ NULL, /* fixups */
+ NULL /* logger */
+};
diff --git a/RELEASE_1_1_X/src/modules/standard/mod_negotiation.c b/RELEASE_1_1_X/src/modules/standard/mod_negotiation.c
new file mode 100644
index 00000000000..77f1cbd4961
--- /dev/null
+++ b/RELEASE_1_1_X/src/modules/standard/mod_negotiation.c
@@ -0,0 +1,1122 @@
+
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+
+/*
+ * mod_negotiation.c: keeps track of MIME types the client is willing to
+ * accept, and contains code to handle type arbitration.
+ *
+ * rst
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_request.h"
+#include "http_core.h"
+#include "http_log.h"
+
+/* Commands --- configuring document caching on a per (virtual?)
+ * server basis...
+ */
+
+typedef struct {
+ array_header *language_priority;
+} neg_dir_config;
+
+module negotiation_module;
+
+void *create_neg_dir_config (pool *p, char *dummy)
+{
+ neg_dir_config *new =
+ (neg_dir_config *) palloc (p, sizeof (neg_dir_config));
+
+ new->language_priority = make_array (p, 4, sizeof (char *));
+ return new;
+}
+
+void *merge_neg_dir_configs (pool *p, void *basev, void *addv)
+{
+ neg_dir_config *base = (neg_dir_config *)basev;
+ neg_dir_config *add = (neg_dir_config *)addv;
+ neg_dir_config *new =
+ (neg_dir_config *) palloc (p, sizeof (neg_dir_config));
+
+ /* give priority to the config in the subdirectory */
+ new->language_priority = append_arrays (p, add->language_priority,
+ base->language_priority);
+ return new;
+}
+
+char *set_language_priority (cmd_parms *cmd, void *n, char *lang)
+{
+ array_header *arr = ((neg_dir_config *) n)->language_priority;
+ char **langp = (char **) push_array (arr);
+
+ *langp = pstrdup (arr->pool, lang);
+ return NULL;
+}
+
+char *cache_negotiated_docs (cmd_parms *cmd, void *dummy, char *dummy2)
+{
+ void *server_conf = cmd->server->module_config;
+
+ set_module_config (server_conf, &negotiation_module, "Cache");
+ return NULL;
+}
+
+int do_cache_negotiated_docs (server_rec *s)
+{
+ return (get_module_config (s->module_config, &negotiation_module) != NULL);
+}
+
+command_rec negotiation_cmds[] = {
+{ "CacheNegotiatedDocs", cache_negotiated_docs, NULL, RSRC_CONF, RAW_ARGS,
+ NULL },
+{ "LanguagePriority", set_language_priority, NULL, OR_FILEINFO, ITERATE,
+ NULL },
+{ NULL }
+};
+
+/*
+ * TO DO --- error code 406. Unfortunately, the specification for
+ * a 406 reply in the current draft standard is unworkable;
+ * we return 404 for these pending a workable spec.
+ */
+
+/* Record of available info on a media type specified by the client
+ * (we also use 'em for encodings and languages)
+ */
+
+typedef struct accept_rec {
+ char *type_name;
+ float quality;
+ float max_bytes;
+ float level;
+} accept_rec;
+
+/* Record of available info on a particular variant
+ *
+ * Note that a few of these fields are updated by the actual negotiation
+ * code. These are:
+ *
+ * quality --- initialized to the value of qs, and subsequently jiggered
+ * to reflect the client's preferences. In particular, it
+ * gets zeroed out if the variant has an unacceptable content
+ * encoding, or if it is in a language which the client
+ * doesn't accept and some other variant *is* in a language
+ * the client accepts.
+ *
+ * level_matched --- initialized to zero. Set to the value of level
+ * if the client actually accepts this media type at that
+ * level (and *not* if it got in on a wildcard). See level_cmp
+ * below.
+ */
+
+typedef struct var_rec {
+ request_rec *sub_req; /* May be NULL (is, for map files) */
+ char *type_name;
+ char *file_name;
+ char *content_encoding;
+ char *content_language;
+ float level; /* Auxiliary to content-type... */
+ float qs;
+ float bytes;
+ int lang_index;
+ int is_pseudo_html; /* text/html, *or* the INCLUDES_MAGIC_TYPEs */
+
+ /* Above are all written-once properties of the variant. The
+ * three fields below are changed during negotiation:
+ */
+
+ float quality;
+ float level_matched;
+ int mime_stars;
+} var_rec;
+
+/* Something to carry around the state of negotiation (and to keep
+ * all of this thread-safe)...
+ */
+
+typedef struct {
+ pool *pool;
+ request_rec *r;
+ char *dir_name;
+
+ array_header *accepts; /* accept_recs */
+ array_header *accept_encodings; /* accept_recs */
+ array_header *accept_langs; /* accept_recs */
+ array_header *avail_vars; /* available variants */
+} negotiation_state;
+
+/* A few functions to manipulate var_recs.
+ * Cleaning out the fields...
+ */
+
+void clean_var_rec (var_rec *mime_info)
+{
+ mime_info->sub_req = NULL;
+ mime_info->type_name = "";
+ mime_info->file_name = "";
+ mime_info->content_encoding = "";
+ mime_info->content_language = "";
+
+ mime_info->is_pseudo_html = 0;
+ mime_info->level = 0.0;
+ mime_info->level_matched = 0.0;
+ mime_info->qs = 0.0;
+ mime_info->quality = 0.0;
+ mime_info->bytes = 0;
+ mime_info->lang_index = -1;
+ mime_info->mime_stars = 0;
+}
+
+/* Initializing the relevant fields of a variant record from the
+ * accept_info read out of its content-type, one way or another.
+ */
+
+void set_mime_fields (var_rec *var, accept_rec *mime_info)
+{
+ var->type_name = mime_info->type_name;
+ var->qs = mime_info->quality;
+ var->quality = mime_info->quality; /* Initial quality is just qs */
+ var->level = mime_info->level;
+
+ var->is_pseudo_html =
+ (!strcmp (var->type_name, "text/html")
+ || !strcmp (var->type_name, INCLUDES_MAGIC_TYPE)
+ || !strcmp (var->type_name, INCLUDES_MAGIC_TYPE3));
+}
+
+/*****************************************************************
+ *
+ * Parsing (lists of) media types and their parameters, as seen in
+ * HTTPD header lines and elsewhere.
+ */
+
+/*
+ * Get a single mime type entry --- one media type and parameters;
+ * enter the values we recognize into the argument accept_rec
+ */
+
+char *get_entry (pool *p, accept_rec *result, char *accept_line)
+{
+ result->quality = 1.0;
+ result->max_bytes = 0.0;
+ result->level = 0.0;
+
+ /* Note that this handles what I gather is the "old format",
+ *
+ * Accept: text/html text/plain moo/zot
+ *
+ * without any compatibility kludges --- if the token after the
+ * MIME type begins with a semicolon, we know we're looking at parms,
+ * otherwise, we know we aren't. (So why all the pissing and moaning
+ * in the CERN server code? I must be missing something).
+ */
+
+ result->type_name = get_token (p, &accept_line, 0);
+ str_tolower (result->type_name); /* You want case-insensitive,
+ * you'll *get* case-insensitive.
+ */
+
+
+ /* KLUDGE!!! Default HTML to level 2.0 unless the browser
+ * *explicitly* says something else.
+ */
+
+ if (!strcmp (result->type_name, "text/html")
+ && result->level == 0.0)
+ result->level = 2.0;
+ else if (!strcmp (result->type_name, INCLUDES_MAGIC_TYPE))
+ result->level = 2.0;
+ else if (!strcmp (result->type_name, INCLUDES_MAGIC_TYPE3))
+ result->level = 3.0;
+
+ while (*accept_line == ';') {
+ /* Parameters ... */
+
+ char *parm;
+ char *cp;
+
+ ++accept_line;
+ parm = get_token (p, &accept_line, 1);
+
+ /* Look for 'var = value' --- and make sure the var is in lcase. */
+
+ for (cp = parm; *cp && !isspace(*cp) && *cp != '='; ++cp)
+ *cp = tolower(*cp);
+
+ if (!*cp) continue; /* No '='; just ignore it. */
+
+ *cp++ = '\0'; /* Delimit var */
+ while (*cp && (isspace(*cp) || *cp == '='))
+ ++cp;
+
+ if (*cp == '"') ++cp;
+
+ if (parm[0] == 'q'
+ && (parm[1] == '\0' || (parm[1] == 's' && parm[2] == '\0')))
+ result->quality = atof(cp);
+ else if (parm[0] == 'm' && parm[1] == 'x' &&
+ parm[2] == 'b' && parm[3] == '\0')
+ result->max_bytes = atof(cp);
+ else if (parm[0] == 'l' && !strcmp (&parm[1], "evel"))
+ result->level = atof(cp);
+ }
+
+ if (*accept_line == ',') ++accept_line;
+
+ return accept_line;
+}
+
+
+/*****************************************************************
+ *
+ * Dealing with header lines ...
+ */
+
+array_header *do_header_line (pool *p, char *accept_line)
+{
+ array_header *accept_recs = make_array (p, 40, sizeof (accept_rec));
+
+ if (!accept_line) return accept_recs;
+
+ while (*accept_line) {
+ accept_rec *new = (accept_rec *)push_array (accept_recs);
+ accept_line = get_entry (p, new, accept_line);
+ }
+
+ return accept_recs;
+}
+
+/*****************************************************************
+ *
+ * Handling header lines from clients...
+ */
+
+negotiation_state *parse_accept_headers (request_rec *r)
+{
+ negotiation_state *new =
+ (negotiation_state *)palloc (r->pool, sizeof (negotiation_state));
+ table *hdrs = r->headers_in;
+
+ new->pool = r->pool;
+ new->r = r;
+ new->dir_name = make_dirstr(r->pool, r->filename, count_dirs(r->filename));
+
+ new->accepts = do_header_line (r->pool, table_get (hdrs, "Accept"));
+ new->accept_encodings =
+ do_header_line (r->pool, table_get (hdrs, "Accept-encoding"));
+ new->accept_langs =
+ do_header_line (r->pool, table_get (hdrs, "Accept-language"));
+ new->avail_vars = make_array (r->pool, 40, sizeof (var_rec));
+
+ return new;
+}
+
+/* Sometimes clients will give us no Accept info at all; this routine sets
+ * up the standard default for that case, and also arranges for us to be
+ * willing to run a CGI script if we find one. (In fact, we set up to
+ * dramatically prefer CGI scripts in cases where that's appropriate,
+ * e.g., POST).
+ */
+
+void maybe_add_default_encodings(negotiation_state *neg, int prefer_scripts)
+{
+ accept_rec *new_accept = (accept_rec *)push_array (neg->accepts);
+
+ new_accept->type_name = CGI_MAGIC_TYPE;
+ new_accept->quality = prefer_scripts ? 1e-20 : 1e20;
+ new_accept->level = 0.0;
+ new_accept->max_bytes = 0.0;
+
+ if (neg->accepts->nelts > 1) return;
+
+ new_accept = (accept_rec *)push_array (neg->accepts);
+
+ new_accept->type_name = "*/*";
+ new_accept->quality = 1.0;
+ new_accept->level = 0.0;
+ new_accept->max_bytes = 0.0;
+}
+
+/*****************************************************************
+ *
+ * Parsing type-map files, in Roy's meta/http format augmented with
+ * #-comments.
+ */
+
+/* Reading RFC822-style header lines, ignoring #-comments and
+ * handling continuations.
+ */
+
+enum header_state { header_eof, header_seen, header_sep };
+
+enum header_state get_header_line (char *buffer, int len, FILE *map)
+{
+ char *buf_end = buffer + len;
+ char *cp;
+ int c;
+
+ /* Get a noncommented line */
+
+ do {
+ if (fgets(buffer, MAX_STRING_LEN, map) == NULL)
+ return header_eof;
+ } while (buffer[0] == '#');
+
+ /* If blank, just return it --- this ends information on this variant */
+
+ for (cp = buffer; *cp && isspace (*cp); ++cp)
+ continue;
+
+ if (*cp == '\0') return header_sep;
+
+ /* If non-blank, go looking for header lines, but note that we still
+ * have to treat comments specially...
+ */
+
+ cp += strlen(cp);
+
+ while ((c = getc(map)) != EOF)
+ {
+ if (c == '#') {
+ /* Comment line */
+ while ((c = getc(map)) != EOF && c != '\n')
+ continue;
+ } else if (isspace(c)) {
+ /* Leading whitespace. POSSIBLE continuation line
+ * Also, possibly blank --- if so, we ungetc() the final newline
+ * so that we will pick up the blank line the next time 'round.
+ */
+
+ while (c != EOF && c != '\n' && isspace(c))
+ c = getc(map);
+
+ ungetc (c, map);
+
+ if (c == '\n') return header_seen; /* Blank line */
+
+ /* Continuation */
+
+ while (cp < buf_end - 2 && (c = getc(map)) != EOF && c != '\n')
+ *cp++ = c;
+
+ *cp++ = '\n';
+ *cp = '\0';
+ } else {
+
+ /* Line beginning with something other than whitespace */
+
+ ungetc (c, map);
+ return header_seen;
+ }
+ }
+
+ return header_seen;
+}
+
+/* Stripping out RFC822 comments */
+
+void strip_paren_comments (char *hdr)
+{
+ /* Hmmm... is this correct? In Roy's latest draft, (comments) can nest! */
+
+ while (*hdr) {
+ if (*hdr == '"') {
+ while (*++hdr && *hdr != '"')
+ continue;
+ ++hdr;
+ }
+ else if (*hdr == '(') {
+ while (*hdr && *hdr != ')') *hdr++ = ' ';
+
+ if (*hdr) *hdr++ = ' ';
+ }
+ else ++hdr;
+ }
+}
+
+/* Getting to a header body from the header */
+
+char *lcase_header_name_return_body (char *header, request_rec *r)
+{
+ char *cp = header;
+
+ while (*cp && *cp != ':')
+ *cp++ = tolower(*cp);
+
+ if (!*cp) {
+ log_reason ("Syntax error in type map --- no ':'", r->filename, r);
+ return NULL;
+ }
+
+ do ++cp; while (*cp && isspace (*cp));
+
+ if (!*cp) {
+ log_reason ("Syntax error in type map --- no header body",
+ r->filename, r);
+ return NULL;
+ }
+
+ return cp;
+}
+
+int read_type_map (negotiation_state *neg, char *map_name)
+{
+ request_rec *r = neg->r;
+ FILE *map = pfopen (neg->pool, map_name, "r");
+
+ char buffer[MAX_STRING_LEN];
+ enum header_state hstate;
+ struct var_rec mime_info;
+
+ if (map == NULL) {
+ log_reason("cannot access type map file", map_name, r);
+ return FORBIDDEN;
+ }
+
+ clean_var_rec (&mime_info);
+
+ do {
+ hstate = get_header_line (buffer, MAX_STRING_LEN, map);
+
+ if (hstate == header_seen) {
+ char *body = lcase_header_name_return_body (buffer, neg->r);
+
+ if (body == NULL) return SERVER_ERROR;
+
+ strip_paren_comments (body);
+
+ if (!strncmp (buffer, "uri:", 4)) {
+ mime_info.file_name = get_token (neg->pool, &body, 0);
+ }
+ else if (!strncmp (buffer, "content-type:", 13)) {
+ struct accept_rec accept_info;
+
+ get_entry (neg->pool, &accept_info, body);
+ set_mime_fields (&mime_info, &accept_info);
+ }
+ else if (!strncmp (buffer, "content-length:", 15)) {
+ mime_info.bytes = atoi(body);
+ }
+ else if (!strncmp (buffer, "content-language:", 17)) {
+ mime_info.content_language = get_token (neg->pool, &body, 0);
+ str_tolower (mime_info.content_language);
+ }
+ else if (!strncmp (buffer, "content-encoding:", 17)) {
+ mime_info.content_encoding = get_token (neg->pool, &body, 0);
+ str_tolower (mime_info.content_encoding);
+ }
+ } else {
+ if (mime_info.quality > 0) {
+ void *new_var = push_array (neg->avail_vars);
+ memcpy (new_var, (void *)&mime_info, sizeof (var_rec));
+ }
+
+ clean_var_rec(&mime_info);
+ }
+ } while (hstate != header_eof);
+
+ pfclose (neg->pool, map);
+ return OK;
+}
+
+/*****************************************************************
+ *
+ * Same, except we use a filtered directory listing as the map...
+ */
+
+int read_types_multi (negotiation_state *neg)
+{
+ request_rec *r = neg->r;
+
+ char *filp;
+ int prefix_len;
+ DIR *dirp;
+ struct DIR_TYPE *dir_entry;
+ struct var_rec mime_info;
+ struct accept_rec accept_info;
+ void *new_var;
+
+ clean_var_rec (&mime_info);
+
+ if (!(filp = strrchr (r->filename, '/'))) return DECLINED; /* Weird... */
+
+ ++filp;
+ prefix_len = strlen (filp);
+
+ dirp = opendir (neg->dir_name); /* Not pool protected; sigh... */
+
+ if (dirp == NULL) {
+ log_reason("cannot read directory for multi", neg->dir_name, r);
+ return FORBIDDEN;
+ }
+
+ while ((dir_entry = readdir (dirp))) {
+
+ request_rec *sub_req;
+
+ /* Do we have a match? */
+
+ if (strncmp (dir_entry->d_name, filp, prefix_len)) continue;
+ if (dir_entry->d_name[prefix_len] != '.') continue;
+
+ /* Yep. See if it's something which we have access to, and
+ * which has a known type and encoding (as opposed to something
+ * which we'll be slapping default_type on later).
+ */
+
+ sub_req = sub_req_lookup_file (dir_entry->d_name, r);
+
+ /* If it has a handler, we'll pretend it's a CGI script,
+ * since that's a good indication of the sort of thing it
+ * might be doing.
+ */
+ if (sub_req->handler && !sub_req->content_type)
+ sub_req->content_type = CGI_MAGIC_TYPE;
+
+ if (sub_req->status != 200 || !sub_req->content_type) continue;
+
+ /* If it's a map file, we use that instead of the map
+ * we're building...
+ */
+
+ if (((sub_req->content_type) &&
+ !strcmp (sub_req->content_type, MAP_FILE_MAGIC_TYPE)) ||
+ ((sub_req->handler) &&
+ !strcmp (sub_req->handler, "type-map"))) {
+ closedir(dirp);
+
+ neg->avail_vars->nelts = 0;
+ return read_type_map (neg, sub_req->filename);
+ }
+
+ /* Have reasonable variant --- gather notes.
+ */
+
+ mime_info.sub_req = sub_req;
+ mime_info.file_name = pstrdup(neg->pool, dir_entry->d_name);
+ mime_info.content_encoding = sub_req->content_encoding;
+ mime_info.content_language = sub_req->content_language;
+
+ get_entry (neg->pool, &accept_info, sub_req->content_type);
+ set_mime_fields (&mime_info, &accept_info);
+
+ new_var = push_array (neg->avail_vars);
+ memcpy (new_var, (void *)&mime_info, sizeof (var_rec));
+
+ clean_var_rec(&mime_info);
+ }
+
+ closedir(dirp);
+ return OK;
+}
+
+
+/*****************************************************************
+ * And now for the code you've been waiting for... actually
+ * finding a match to the client's requirements.
+ */
+
+/* Matching MIME types ... the star/star and foo/star commenting conventions
+ * are implemented here. (You know what I mean by star/star, but just
+ * try mentioning those three characters in a C comment). Using strcmp()
+ * is legit, because everything has already been smashed to lowercase.
+ *
+ * Note also that if we get an exact match on the media type, we update
+ * level_matched for use in level_cmp below...
+ *
+ * We also give a value for mime_stars, which is used later. It should
+ * be 1 for star/star, 2 for type/star and 3 for type/subtype.
+ */
+
+int mime_match (accept_rec *accept, var_rec *avail)
+{
+ char *accept_type = accept->type_name;
+ char *avail_type = avail->type_name;
+ int len = strlen(accept_type);
+
+ if (accept_type[0] == '*') { /* Anything matches star/star */
+ if (avail->mime_stars < 1)
+ avail->mime_stars = 1;
+ return 1;
+ }
+ else if ((accept_type[len - 1] == '*') &&
+ !strncmp (accept_type, avail_type, len - 2)) {
+ if (avail->mime_stars < 2)
+ avail->mime_stars = 2;
+ return 1;
+ }
+ else if (!strcmp (accept_type, avail_type)
+ || (!strcmp (accept_type, "text/html")
+ && (!strcmp(avail_type, INCLUDES_MAGIC_TYPE)
+ || !strcmp(avail_type, INCLUDES_MAGIC_TYPE3)))) {
+ if (accept->level >= avail->level) {
+ avail->level_matched = avail->level;
+ avail->mime_stars = 3;
+ return 1;
+ }
+ }
+
+ return OK;
+}
+
+/* This code implements a piece of the tie-breaking algorithm between
+ * variants of equal quality. This piece is the treatment of variants
+ * of the same base media type, but different levels. What we want to
+ * return is the variant at the highest level that the client explicitly
+ * claimed to accept.
+ *
+ * If all the variants available are at a higher level than that, or if
+ * the client didn't say anything specific about this media type at all
+ * and these variants just got in on a wildcard, we prefer the lowest
+ * level, on grounds that that's the one that the client is least likely
+ * to choke on.
+ *
+ * (This is all motivated by treatment of levels in HTML --- we only
+ * want to give level 3 to browsers that explicitly ask for it; browsers
+ * that don't, including HTTP/0.9 browsers that only get the implicit
+ * "Accept: * / *" [space added to avoid confusing cpp --- no, that
+ * syntax doesn't really work] should get HTML2 if available).
+ *
+ * (Note that this code only comes into play when we are choosing among
+ * variants of equal quality, where the draft standard gives us a fair
+ * bit of leeway about what to do. It ain't specified by the standard;
+ * rather, it is a choice made by this server about what to do in cases
+ * where the standard does not specify a unique course of action).
+ */
+
+int level_cmp (var_rec *var1, var_rec *var2)
+{
+ /* Levels are only comparable between matching media types */
+
+ if (var1->is_pseudo_html && !var2->is_pseudo_html)
+ return 0;
+
+ if (!var1->is_pseudo_html && strcmp (var1->type_name, var2->type_name))
+ return 0;
+
+ /* Take highest level that matched, if either did match. */
+
+ if (var1->level_matched > var2->level_matched) return 1;
+ if (var1->level_matched < var2->level_matched) return -1;
+
+ /* Neither matched. Take lowest level, if there's a difference. */
+
+ if (var1->level < var2->level) return 1;
+ if (var1->level > var2->level) return -1;
+
+ /* Tied */
+
+ return 0;
+}
+
+/* Finding languages. Note that we only match the substring specified
+ * by the Accept: line --- this is to allow "en" to match all subvariants
+ * of English.
+ *
+ * Again, strcmp() is legit because we've ditched case already.
+ */
+
+int find_lang_index (array_header *accept_langs, char *lang)
+{
+ accept_rec *accs;
+ int i;
+
+ if (!lang)
+ return -1;
+
+ accs = (accept_rec *)accept_langs->elts;
+
+ for (i = 0; i < accept_langs->nelts; ++i)
+ if (!strncmp (lang, accs[i].type_name, strlen(accs[i].type_name)))
+ return i;
+
+ return -1;
+}
+
+/* This function returns the priority of a given language
+ * according to LanguagePriority. It is used in case of a tie
+ * between several languages.
+ */
+
+int find_default_index (neg_dir_config *conf, char *lang)
+{
+ array_header *arr;
+ int nelts;
+ char **elts;
+ int i;
+
+ if (!lang)
+ return -1;
+
+ arr = conf->language_priority;
+ nelts = arr->nelts;
+ elts = (char **) arr->elts;
+
+ for (i = 0; i < nelts; ++i)
+ if (!strcasecmp (elts[i], lang))
+ return i;
+
+ return -1;
+}
+
+void find_lang_indexes (negotiation_state *neg)
+{
+ var_rec *var_recs = (var_rec*)neg->avail_vars->elts;
+ int i;
+ int found_any = 0;
+ neg_dir_config *conf = NULL;
+ int naccept = neg->accept_langs->nelts;
+
+ if (naccept == 0)
+ conf = (neg_dir_config *) get_module_config (neg->r->per_dir_config,
+ &negotiation_module);
+
+ for (i = 0; i < neg->avail_vars->nelts; ++i)
+ if (var_recs[i].quality > 0) {
+ int index;
+ if (naccept == 0) /* Client doesn't care */
+ index = find_default_index (conf,
+ var_recs[i].content_language);
+ else /* Client has Accept-Language */
+ index = find_lang_index (neg->accept_langs,
+ var_recs[i].content_language);
+
+ var_recs[i].lang_index = index;
+ if (index >= 0) found_any = 1;
+ }
+
+ /* If we have any variants in a language acceptable to the client,
+ * blow away everything that isn't.
+ */
+
+ if (found_any)
+ for (i = 0; i < neg->avail_vars->nelts; ++i)
+ if (var_recs[i].lang_index < 0)
+ var_recs[i].quality = 0;
+}
+
+/* Finding content encodings. Note that we assume that the client
+ * accepts the trivial encodings. Strcmp() is legit because... aw, hell.
+ */
+
+int is_identity_encoding (char *enc)
+{
+ return (!enc || !enc[0] || !strcmp (enc, "7bit") || !strcmp (enc, "8bit")
+ || !strcmp (enc, "binary"));
+}
+
+int find_encoding (array_header *accept_encodings, char *enc)
+{
+ accept_rec *accs = (accept_rec *)accept_encodings->elts;
+ int i;
+
+ if (is_identity_encoding(enc)) return 1;
+
+ for (i = 0; i < accept_encodings->nelts; ++i)
+ if (!strcmp (enc, accs[i].type_name))
+ return 1;
+
+ return 0;
+}
+
+void do_encodings (negotiation_state *neg)
+{
+ var_rec *var_recs = (var_rec*)neg->avail_vars->elts;
+ int i;
+
+ /* If no Accept-Encoding is present, everything is acceptable */
+
+ if (!neg->accept_encodings->nelts)
+ return;
+
+ /* Lose any variant with an unacceptable content encoding */
+
+ for (i = 0; i < neg->avail_vars->nelts; ++i)
+ if (var_recs[i].quality > 0
+ && !find_encoding (neg->accept_encodings,
+ var_recs[i].content_encoding))
+
+ var_recs[i].quality = 0;
+}
+
+/* Determining the content length --- if the map didn't tell us,
+ * we have to do a stat() and remember for next time.
+ *
+ * Grump. For shambhala, even the first stat here may well be
+ * redundant (for multiviews) with a stat() done by the sub_req
+ * machinery. At some point, that ought to be fixed.
+ */
+
+int find_content_length(negotiation_state *neg, var_rec *variant)
+{
+ struct stat statb;
+
+ if (variant->bytes == 0) {
+ char *fullname = make_full_path (neg->pool, neg->dir_name,
+ variant->file_name);
+
+ if (stat (fullname, &statb) >= 0) variant->bytes = statb.st_size;
+ }
+
+ return variant->bytes;
+}
+
+/* The main event. */
+
+var_rec *best_match(negotiation_state *neg)
+{
+ int i, j;
+ var_rec *best = NULL;
+ float best_quality = 0.0;
+ int levcmp;
+
+ accept_rec *accept_recs = (accept_rec *)neg->accepts->elts;
+ var_rec *avail_recs = (var_rec *)neg->avail_vars->elts;
+
+ /* Nuke variants which are unsuitable due to a content encoding,
+ * or possibly a language, which the client doesn't accept.
+ * (If we haven't *got* a variant in a language the client accepts,
+ * find_lang_indexes keeps 'em all, so we still wind up serving
+ * something...).
+ */
+
+ do_encodings (neg);
+ find_lang_indexes (neg);
+
+ for (i = 0; i < neg->accepts->nelts; ++i) {
+
+ accept_rec *type = &accept_recs[i];
+
+ for (j = 0; j < neg->avail_vars->nelts; ++j) {
+
+ var_rec *variant = &avail_recs[j];
+ float q = type->quality * variant->quality;
+
+ /* If we've already rejected this variant, don't waste time */
+
+ if (q == 0.0) continue;
+
+ /* If media types don't match, forget it.
+ * (This includes the level check).
+ */
+
+ if (!mime_match(type, variant)) continue;
+
+ /* Check maxbytes */
+
+ if (type->max_bytes > 0
+ && (find_content_length(neg, variant)
+ > type->max_bytes))
+ continue;
+
+ /* If it lasted this far, consider it ---
+ * If better quality than our current best, take it.
+ * If equal quality, *maybe* take it.
+ *
+ * Note that the current http draft specifies no particular
+ * behavior for variants which tie in quality; the server
+ * can, at its option, return a 300 response listing all
+ * of them (and perhaps the others), or choose one of the
+ * tied variants by whatever means it likes. This server
+ * breaks ties as follows, in order:
+ *
+ * By perferring non-wildcard entries to those with
+ * wildcards. The spec specifically says we should
+ * do this, and it makes a lot of sense.
+ *
+ * By order of languages in Accept-language, to give the
+ * client a way to specify a language preference. I'd prefer
+ * to give this precedence over media type, but the standard
+ * doesn't allow for that.
+ *
+ * By level preference, as defined by level_cmp above.
+ *
+ * By order of Accept: header matched, so that the order in
+ * which media types are named by the client functions as a
+ * preference order, if the client didn't give us explicit
+ * quality values.
+ *
+ * Finally, by content_length, so that among variants which
+ * have the same quality, language and content_type (including
+ * level) we ship the one that saps the least bandwidth.
+ */
+
+ if (q > best_quality
+ || (q == best_quality
+ && ((variant->mime_stars > best->mime_stars)
+ || (variant->lang_index < best->lang_index
+ || (variant->lang_index == best->lang_index
+ && ((levcmp = level_cmp (variant, best)) == 1
+ || (levcmp == 0
+ && !strcmp (variant->type_name,
+ best->type_name)
+ && (find_content_length(neg, variant)
+ <
+ find_content_length(neg, best)))))))))
+ {
+ best = variant;
+ best_quality = q;
+ }
+ }
+ }
+
+ return best;
+}
+
+/****************************************************************
+ *
+ * Executive...
+ */
+
+int handle_map_file (request_rec *r)
+{
+ negotiation_state *neg = parse_accept_headers (r);
+ var_rec *best;
+ int res;
+
+ char *udir;
+
+ if ((res = read_type_map (neg, r->filename))) return res;
+
+ maybe_add_default_encodings(neg, 0);
+
+ if (!(best = best_match(neg))) {
+ /* Should be a 406 */
+ log_reason ("no acceptable variant", r->filename, r);
+ return NOT_FOUND;
+ }
+
+ if (!do_cache_negotiated_docs(r->server)) r->no_cache = 1;
+ udir = make_dirstr (r->pool, r->uri, count_dirs (r->uri));
+ udir = escape_uri(r->pool, udir);
+ internal_redirect (make_full_path (r->pool, udir, best->file_name), r);
+ return OK;
+}
+
+int handle_multi (request_rec *r)
+{
+ negotiation_state *neg;
+ var_rec *best;
+ request_rec *sub_req;
+ int res;
+
+ if (r->finfo.st_mode != 0 || !(allow_options (r) & OPT_MULTI))
+ return DECLINED;
+
+ neg = parse_accept_headers (r);
+
+ if ((res = read_types_multi (neg))) return res;
+
+ maybe_add_default_encodings(neg,
+ r->method_number != M_GET
+ || r->args || r->path_info);
+
+ if (neg->avail_vars->nelts == 0) return DECLINED;
+
+ if (!(best = best_match(neg))) {
+ /* Should be a 406 */
+ log_reason ("no acceptable variant", r->filename, r);
+ return NOT_FOUND;
+ }
+
+ if (! (sub_req = best->sub_req)) {
+ /* We got this out of a map file, so we don't actually have
+ * a sub_req structure yet. Get one now.
+ */
+
+ sub_req = sub_req_lookup_file (best->file_name, r);
+ if (sub_req->status != 200) return sub_req->status;
+ }
+
+ /* BLETCH --- don't multi-resolve non-ordinary files */
+
+ if (!S_ISREG(sub_req->finfo.st_mode)) return NOT_FOUND;
+
+ /* Otherwise, use it. */
+
+ if (!do_cache_negotiated_docs(r->server)) r->no_cache = 1;
+ r->filename = sub_req->filename;
+ r->content_type = sub_req->content_type;
+ r->content_encoding = sub_req->content_encoding;
+ r->content_language = sub_req->content_language;
+ r->finfo = sub_req->finfo;
+
+ return OK;
+}
+
+handler_rec negotiation_handlers[] = {
+{ MAP_FILE_MAGIC_TYPE, handle_map_file },
+{ "type-map", handle_map_file },
+{ NULL }
+};
+
+module negotiation_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ create_neg_dir_config, /* dir config creater */
+ merge_neg_dir_configs, /* dir merger --- default is to override */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ negotiation_cmds, /* command table */
+ negotiation_handlers, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ handle_multi, /* type_checker */
+ NULL, /* fixups */
+ NULL /* logger */
+};
diff --git a/RELEASE_1_1_X/src/modules/standard/mod_status.c b/RELEASE_1_1_X/src/modules/standard/mod_status.c
new file mode 100644
index 00000000000..5a324fa66d6
--- /dev/null
+++ b/RELEASE_1_1_X/src/modules/standard/mod_status.c
@@ -0,0 +1,591 @@
+/* ====================================================================
+ * Copyright (c) 1995, 1996 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+/* Status Module. Provide a way of getting at the internal Apache
+ * status information without having to worry where the scoreboard is
+ * held.
+ *
+ * AddType application/x-httpd-status .status
+ * You might like to do this in a .htaccess in a protected directory only
+ *
+ * GET /.status - Returns pretty page for system admin user
+ * GET /.status?refresh - Returns page with 1 second refresh
+ * GET /.status?refresh=6 - Returns page with refresh every 6 seconds
+ * GET /.status?auto - Returns page with data for automatic parsing
+ * GET /.status?notable - Returns page with no table niceties
+ *
+ * Mark Cox, mark@ukweb.com, November 1995
+ *
+ * 12.11.95 Initial version for telescope.org
+ * 13.3.96 Updated to remove rprintf's [Mark]
+ * 18.3.96 Added CPU usage, process information, and tidied [Ben Laurie]
+ * 18.3.96 Make extra Scoreboard variables #definable
+ * 25.3.96 Make short report have full precision [Ben Laurie suggested]
+ * 25.3.96 Show uptime better [Mark/Ben Laurie]
+ * 29.3.96 Better HTML and explanation [Mark/Rob Hartill suggested]
+ * 09.4.96 Added message for non-STATUS compiled version
+ * 18.4.96 Added per child and per slot counters [Jim Jagielski]
+ * 01.5.96 Table format, cleanup, even more spiffy data [Chuck Murcko/Jim J.]
+ * 18.5.96 Adapted to use new rprintf() routine, incidentally fixing a missing
+ * piece in short reports [Ben Laurie]
+ * 21.5.96 Additional Status codes (DNS and LOGGING only enabled if
+ extended STATUS is enabled) [George Burgyan/Jim J.]
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+#include "http_core.h"
+#include "http_protocol.h"
+#include "http_main.h"
+#include "util_script.h"
+#include
+#include "scoreboard.h"
+
+#ifdef NEXT
+#include
+#endif
+
+#define STATUS_MAXLINE 50
+
+#define KBYTE 1024
+#define MBYTE 1048576L
+#define GBYTE 1073741824L
+
+module status_module;
+
+/* Format the number of bytes nicely */
+
+void format_byte_out(request_rec *r,unsigned long bytes)
+{
+ if (bytes < (5 * KBYTE))
+ rprintf(r,"%d B",(int)bytes);
+ else if (bytes < (MBYTE / 2))
+ rprintf(r,"%.1f kB",(float)bytes/KBYTE);
+ else if (bytes < (GBYTE / 2))
+ rprintf(r,"%.1f MB",(float)bytes/MBYTE);
+ else
+ rprintf(r,"%.1f GB",(float)bytes/GBYTE);
+}
+
+void show_time(request_rec *r,time_t tsecs)
+{
+ long days,hrs,mins,secs;
+ char buf[100];
+ char *s;
+
+ secs=tsecs%60;
+ tsecs/=60;
+ mins=tsecs%60;
+ tsecs/=60;
+ hrs=tsecs%24;
+ days=tsecs/24;
+ s=buf;
+ *s='\0';
+ if(days)
+ rprintf(r," %ld day%s",days,days==1?"":"s");
+ if(hrs)
+ rprintf(r," %ld hour%s",hrs,hrs==1?"":"s");
+ if(mins)
+ rprintf(r," %ld minute%s",mins,mins==1?"":"s");
+ if(secs)
+ rprintf(r," %ld second%s",secs,secs==1?"":"s");
+}
+
+#if defined(SUNOS4)
+double
+difftime(time1, time0)
+ time_t time1, time0;
+{
+ return(time1 - time0);
+}
+#endif
+
+/* Main handler for x-httpd-status requests */
+
+/* ID values for command table */
+
+#define STAT_OPT_END -1
+#define STAT_OPT_REFRESH 0
+#define STAT_OPT_NOTABLE 1
+#define STAT_OPT_AUTO 2
+
+struct stat_opt
+{
+ int id;
+ char *form_data_str;
+ char *hdr_out_str;
+};
+
+int status_handler (request_rec *r)
+{
+ struct stat_opt options[] = /* see #defines above */
+ {
+ { STAT_OPT_REFRESH, "refresh", "Refresh" },
+ { STAT_OPT_NOTABLE, "notable", NULL },
+ { STAT_OPT_AUTO, "auto", NULL },
+ { STAT_OPT_END, NULL, NULL }
+ };
+ char *loc;
+ time_t nowtime=time(NULL);
+ time_t up_time;
+ int i,res;
+ int ready=0;
+ int busy=0;
+#if defined(STATUS)
+ unsigned long count=0;
+ unsigned long lres,bytes;
+ unsigned long my_lres,my_bytes,conn_bytes;
+ unsigned short conn_lres;
+ unsigned long bcount=0;
+#ifdef NEXT
+ float tick=HZ;
+#else
+ float tick=sysconf(_SC_CLK_TCK);
+#endif
+#endif /* STATUS */
+ int short_report=0;
+ int no_table_report=0;
+ server_rec *server = r->server;
+ short_score score_record;
+ char status[]="??????????";
+ char stat_buffer[HARD_SERVER_LIMIT];
+ clock_t tu,ts,tcu,tcs;
+
+ tu=ts=tcu=tcs=0;
+
+ status[SERVER_DEAD]='.'; /* We don't want to assume these are in */
+ status[SERVER_READY]='_'; /* any particular order in scoreboard.h */
+ status[SERVER_STARTING]='S';
+ status[SERVER_BUSY_READ]='R';
+ status[SERVER_BUSY_WRITE]='W';
+ status[SERVER_BUSY_KEEPALIVE]='K';
+ status[SERVER_BUSY_LOG]='L';
+ status[SERVER_BUSY_DNS]='D';
+
+ if (r->method_number != M_GET) return NOT_IMPLEMENTED;
+ r->content_type = "text/html";
+
+ /*
+ * Simple table-driven form data set parser that lets you alter the header
+ */
+
+ if (r->args)
+ {
+ i = 0;
+ while (options[i].id != STAT_OPT_END)
+ {
+ if ((loc = strstr(r->args,options[i].form_data_str)) != NULL)
+ {
+ switch (options[i].id)
+ {
+ case STAT_OPT_REFRESH:
+ if(*(loc + strlen(options[i].form_data_str)) == '=')
+ table_set(r->headers_out,options[i].hdr_out_str,
+ loc+strlen(options[i].hdr_out_str)+1);
+ else
+ table_set(r->headers_out,options[i].hdr_out_str,"1");
+ break;
+ case STAT_OPT_NOTABLE:
+ no_table_report = 1;
+ break;
+ case STAT_OPT_AUTO:
+ r->content_type = "text/plain";
+ short_report = 1;
+ break;
+ }
+ }
+ i++;
+ }
+ }
+
+ soft_timeout ("send status info", r);
+ send_http_header(r);
+
+ if (r->header_only)
+ return 0;
+
+ sync_scoreboard_image();
+ for (i = 0; iApache Status\n",r);
+ rputs("Apache Server Status for ",r);
+ rvputs(r,server->server_hostname,"
\n\n",NULL);
+ rvputs(r,"Current Time: ",asctime(localtime(&nowtime)),"
\n",NULL);
+ rvputs(r,"Restart Time: ",asctime(localtime(&restart_time)),"
\n",
+ NULL);
+ rputs("Server uptime: ",r);
+ show_time(r,up_time);
+ rputs("
\n",r);
+ }
+
+#if defined(STATUS)
+ if (short_report)
+ {
+ rprintf(r,"Total Accesses: %lu\nTotal Bytes: %lu\n",count,bcount);
+
+ if(ts || tu || tcu || tcs)
+ rprintf(r,"CPULoad: %g\n",(tu+ts+tcu+tcs)/tick/up_time*100.);
+
+ rprintf(r,"Uptime: %ld\n",(long)(up_time));
+ if (up_time>0)
+ rprintf(r,"ReqPerSec: %g\n",(float)count/(float)up_time);
+
+ if (up_time>0)
+ rprintf(r,"BytesPerSec: %g\n",(float)bcount/(float)up_time);
+
+ if (count>0)
+ rprintf(r,"BytesPerReq: %g\n",(float)bcount/(float)count);
+ } else /* !short_report */
+ {
+ rprintf(r,"Total accesses: %lu - Total Traffic: ", count);
+ format_byte_out(r,bcount);
+ rputs("
\n",r);
+ rprintf(r,"CPU Usage: u%g s%g cu%g cs%g",
+ tu/tick,ts/tick,tcu/tick,tcs/tick);
+
+ if(ts || tu || tcu || tcs)
+ rprintf(r," - %.3g%% CPU load",(tu+ts+tcu+tcs)/tick/up_time*100.);
+
+ rputs("
\n",r);
+
+ if (up_time>0)
+ rprintf(r,"%.3g requests/sec - ",
+ (float)count/(float)up_time);
+
+ if (up_time>0)
+ {
+ format_byte_out(r,(float)bcount/(float)up_time);
+ rputs("/second - ",r);
+ }
+
+ if (count>0)
+ {
+ format_byte_out(r,(float)bcount/(float)count);
+ rputs("/request",r);
+ }
+
+ rputs("\n",r);
+ } /* short_report */
+#endif /* STATUS */
+
+ /* send the scoreboard 'table' out */
+
+ rputs("Scoreboard: \n",r);
+
+ if(!short_report)
+ rputs("
",r);
+
+ rputs("\n",r);
+
+ for (i = 0; i\n",r);
+ rputs("Key:
\n",r);
+ rputs("\"_
\" Waiting for Connection, \n",r);
+ rputs("\"S
\" Starting up,
\n",r);
+ rputs("\"R
\" Reading Request, \n",r);
+ rputs("\"W
\" Sending Reply,
\n",r);
+ rputs("\"K
\" Keepalive (read), \n",r);
+ rputs("\"D
\" DNS Lookup, \n",r);
+ rputs("\"L
\" Logging\n",r);
+ rprintf(r,"\n%d requests currently being processed, %d idle servers\n"
+ ,busy,ready);
+ }
+
+#if defined(STATUS)
+ if (!short_report)
+ if(no_table_report)
+ rputs("
Server Details
\n\n",r);
+ else
+ rputs("\n\n
Srv | PID | Acc | M | CPU\n | SS | Conn | Child | Slot | Host | Request |
\n\n",r);
+
+
+ for (i = 0; iServer %d (%d): %d|%lu|%lu [",
+ i,(int)score_record.pid,(int)conn_lres,my_lres,lres);
+
+ switch (score_record.status)
+ {
+ case SERVER_READY:
+ rputs("Ready",r);
+ break;
+ case SERVER_STARTING:
+ rputs("Starting",r);
+ break;
+ case SERVER_BUSY_READ:
+ rputs("Read",r);
+ break;
+ case SERVER_BUSY_WRITE:
+ rputs("Write",r);
+ break;
+ case SERVER_BUSY_KEEPALIVE:
+ rputs("Keepalive",r);
+ break;
+ case SERVER_BUSY_LOG:
+ rputs("Logging",r);
+ break;
+ case SERVER_BUSY_DNS:
+ rputs("DNS lookup",r);
+ break;
+ case SERVER_DEAD:
+ rputs("Dead",r);
+ break;
+ }
+ rprintf(r,"] u%g s%g cu%g cs%g\n %s (",
+ score_record.times.tms_utime/tick,
+ score_record.times.tms_stime/tick,
+ score_record.times.tms_cutime/tick,
+ score_record.times.tms_cstime/tick,
+ asctime(localtime(&score_record.last_used)));
+ format_byte_out(r,conn_bytes);
+ rputs("|",r);
+ format_byte_out(r,my_bytes);
+ rputs("|",r);
+ format_byte_out(r,bytes);
+ rputs(")\n",r);
+ rprintf(r," %s {%s}
\n\n",
+ score_record.client, score_record.request);
+ }
+ else /* !no_table_report */
+ {
+ rprintf(r,"%d | %d | %d/%lu/%lu",
+ i,(int)score_record.pid,(int)conn_lres,my_lres,lres);
+
+ switch (score_record.status)
+ {
+ case SERVER_READY:
+ rputs(" | _",r);
+ break;
+ case SERVER_STARTING:
+ rputs(" | S",r);
+ break;
+ case SERVER_BUSY_READ:
+ rputs(" | R",r);
+ break;
+ case SERVER_BUSY_WRITE:
+ rputs(" | W",r);
+ break;
+ case SERVER_BUSY_KEEPALIVE:
+ rputs(" | K",r);
+ break;
+ case SERVER_BUSY_LOG:
+ rputs(" | L",r);
+ break;
+ case SERVER_BUSY_DNS:
+ rputs(" | D",r);
+ break;
+ case SERVER_DEAD:
+ rputs(" | .",r);
+ break;
+ }
+ rprintf(r,"\n | %.2f | %.0f",
+ (score_record.times.tms_utime +
+ score_record.times.tms_stime +
+ score_record.times.tms_cutime +
+ score_record.times.tms_cstime)/tick,
+ difftime(nowtime, score_record.last_used));
+ rprintf(r," | %-1.1f | %-2.2f | %-2.2f\n",
+ (float)conn_bytes/KBYTE, (float)my_bytes/MBYTE,
+ (float)bytes/MBYTE);
+ rprintf(r," | %s | %s |
\n\n",
+ score_record.client, score_record.request);
+ } /* no_table_report */
+ } /* !short_report */
+ } /* if () */
+ } /* for () */
+
+ if (!(short_report || no_table_report))
+ {
+ rputs("
\n \
+
\
+\n \
+Srv | Server number\n \
+ |
---|
PID | OS process ID\n \
+ |
---|
Acc | Number of accesses this connection / this child / this slot\n \
+ |
---|
M | Mode of operation\n \
+ |
---|
CPU | CPU usage, number of seconds\n \
+ |
---|
SS | Seconds since beginning of most recent request\n \
+ |
---|
Conn | Kilobytes transferred this connection\n \
+ |
---|
Child | Megabytes transferred this child\n \
+ |
---|
Slot | Total megabytes transferred this slot\n \
+ |
---|
\n",r);
+ }
+
+#else /* !defined(STATUS) */
+
+ rputs("
To obtain a full report with current status information \n",r);
+ rputs("you need to recompile Apache adding the -DSTATUS
\n",r);
+ rputs("directive on the CFLAGS
line in the \n",r);
+ rputs("Configuration
file.\n",r);
+ rputs("DNS
and LOGGING
status \n",r);
+ rputs("also requires the -DSTATUS
directive. \n",r);
+
+#endif /* STATUS */
+
+ if (!short_report)
+ rputs("",r);
+ return 0;
+}
+
+handler_rec status_handlers[] =
+{
+{ STATUS_MAGIC_TYPE, status_handler },
+{ "server-status", status_handler },
+{ NULL }
+};
+
+module status_module =
+{
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ NULL, /* dir config creater */
+ NULL, /* dir merger --- default is to override */
+ NULL, /* server config */
+ NULL, /* merge server config */
+ NULL, /* command table */
+ status_handlers, /* handlers */
+ NULL, /* filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* fixups */
+ NULL /* logger */
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/RELEASE_1_1_X/src/modules/standard/mod_userdir.c b/RELEASE_1_1_X/src/modules/standard/mod_userdir.c
new file mode 100644
index 00000000000..320f8699209
--- /dev/null
+++ b/RELEASE_1_1_X/src/modules/standard/mod_userdir.c
@@ -0,0 +1,206 @@
+
+/* ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+ *
+ */
+
+
+/*
+ * mod_userdir... implement the UserDir command. Broken away from the
+ * Alias stuff for a couple of good and not-so-good reasons:
+ *
+ * 1) It shows a real minimal working example of how to do something like
+ * this.
+ * 2) I know people who are actually interested in changing this *particular*
+ * aspect of server functionality without changing the rest of it. That's
+ * what this whole modular arrangement is supposed to be good at...
+ *
+ * Modified by Alexei Kosut to support the following constructs
+ * (server running at www.foo.com, request for /~bar/one/two.html)
+ *
+ * UserDir public_html -> ~bar/public_html/one/two.html
+ * UserDir /usr/web -> /usr/web/bar/one/two.html
+ * UserDir /home/ * /www -> /home/bar/www/one/two.html
+ * NOTE: theses ^ ^ space only added allow it to work in a comment, ignore
+ * UserDir http://x/users -> (302) http://x/users/bar/one/two.html
+ * UserDir http://x/ * /y -> (302) http://x/bar/y/one/two.html
+ * NOTE: here also ^ ^
+ *
+ * In addition, you can use multiple entries, to specify alternate
+ * user directories (a la Directory Index). For example:
+ *
+ * UserDir public_html /usr/web http://www.xyz.com/users
+ *
+ */
+
+#include "httpd.h"
+#include "http_config.h"
+
+module userdir_module;
+
+/*
+ * Sever config for this module is a little unconventional...
+ * It's just one string anyway, so why pretend?
+ */
+
+void *create_userdir_config (pool *dummy, server_rec *s) {
+ return (void*)DEFAULT_USER_DIR;
+}
+
+char *set_user_dir (cmd_parms *cmd, void *dummy, char *arg)
+{
+ void *server_conf = cmd->server->module_config;
+
+ set_module_config (server_conf, &userdir_module, pstrdup (cmd->pool, arg));
+ return NULL;
+}
+
+command_rec userdir_cmds[] = {
+{ "UserDir", set_user_dir, NULL, RSRC_CONF, RAW_ARGS,
+ "the public subdirectory in users' home directories, or 'disabled'" },
+{ NULL }
+};
+
+int translate_userdir (request_rec *r)
+{
+ void *server_conf = r->server->module_config;
+ char *userdirs = (char *)get_module_config(server_conf, &userdir_module);
+ char *name = r->uri;
+ char *w, *dname, *redirect;
+ char *x = NULL;
+
+ if (userdirs == NULL || !strcasecmp(userdirs, "disabled") ||
+ (name[0] != '/') || (name[1] != '~')) {
+ return DECLINED;
+ }
+
+ while (*userdirs) {
+ char *userdir = getword_conf (r->pool, &userdirs);
+ char *filename = NULL;
+
+ dname = name + 2;
+ w = getword(r->pool, &dname, '/');
+
+ if (!strcmp(w, ""))
+ return DECLINED;
+
+ /* The 'dname' funny business involves backing it up to capture
+ * the '/' delimiting the "/~user" part from the rest of the URL,
+ * in case there was one (the case where there wasn't being just
+ * "GET /~user HTTP/1.0", for which we don't want to tack on a
+ * '/' onto the filename).
+ */
+
+ if (dname[-1] == '/') --dname;
+
+ if (strchr(userdir, '*'))
+ x = getword(r->pool, &userdir, '*');
+
+ if (userdir[0] == '/') {
+ if (x) {
+ if (strchr(x, ':')) {
+ redirect = pstrcat(r->pool, x, w, userdir, dname, NULL);
+ table_set (r->headers_out, "Location", redirect);
+ return REDIRECT;
+ }
+ else
+ filename = pstrcat (r->pool, x, w, userdir, NULL);
+ }
+ else
+ filename = pstrcat (r->pool, userdir, "/", w, NULL);
+ }
+ else if (strchr(userdir, ':')) {
+ redirect = pstrcat(r->pool, userdir, "/", w, dname, NULL);
+ table_set (r->headers_out, "Location", redirect);
+ return REDIRECT;
+ }
+ else {
+ struct passwd *pw;
+ if((pw=getpwnam(w)))
+#ifdef __EMX__
+ /* Need to manually add user name for OS/2 */
+ filename = pstrcat (r->pool, pw->pw_dir, w, "/", userdir, NULL);
+#else
+ filename = pstrcat (r->pool, pw->pw_dir, "/", userdir, NULL);
+#endif
+
+ }
+
+ /* Now see if it exists, or we're at the last entry. If we are at the
+ last entry, then use the filename generated (if there is one) anyway,
+ in the hope that some handler might handle it. This can be used, for
+ example, to run a CGI script for the user.
+ */
+ if (filename && (!*userdirs || stat(filename, &r->finfo) != -1)) {
+ r->filename = pstrcat(r->pool, filename, dname, NULL);
+ return OK;
+ }
+ }
+
+ return DECLINED;
+}
+
+module userdir_module = {
+ STANDARD_MODULE_STUFF,
+ NULL, /* initializer */
+ NULL, /* dir config creater */
+ NULL, /* dir merger --- default is to override */
+ create_userdir_config, /* server config */
+ NULL, /* merge server config */
+ userdir_cmds, /* command table */
+ NULL, /* handlers */
+ translate_userdir, /*filename translation */
+ NULL, /* check_user_id */
+ NULL, /* check auth */
+ NULL, /* check access */
+ NULL, /* type_checker */
+ NULL, /* fixups */
+ NULL /* logger */
+};
diff --git a/RELEASE_1_1_X/src/support/.cvsignore b/RELEASE_1_1_X/src/support/.cvsignore
new file mode 100644
index 00000000000..0eada2a266d
--- /dev/null
+++ b/RELEASE_1_1_X/src/support/.cvsignore
@@ -0,0 +1,6 @@
+rotatelogs
+htpasswd
+htdigest
+unescape
+inc2shtml
+httpd_monitor
diff --git a/RELEASE_1_1_X/src/support/Makefile b/RELEASE_1_1_X/src/support/Makefile
new file mode 100755
index 00000000000..5234181807f
--- /dev/null
+++ b/RELEASE_1_1_X/src/support/Makefile
@@ -0,0 +1,76 @@
+# For gcc
+CC= gcc
+# For ANSI compilers
+#CC= cc
+
+#For Optimization
+#CFLAGS= -O2
+#For debugging
+CFLAGS= -g
+# For SCO ODT
+#EXTRA_LIBS= -lcrypt_i
+# For OS/2 port
+#EXTRA_LIBS= -llibufc
+
+
+INCLUDES= -I../src
+
+RM= /bin/rm -f
+#--- You shouldn't have to edit anything else. ---
+
+.c.o:
+ $(CC) -c $(CFLAGS) $(INCLUDES) $<
+
+TARGETS=htpasswd htdigest httpd_monitor rotatelogs logresolve
+
+all: $(TARGETS)
+
+aux:
+ make all CC=gcc CFLAGS=-O2
+
+ibm:
+ make all CC=gcc
+
+sun:
+ make all CC=gcc
+
+hp:
+ make all CC=gcc
+
+sgi:
+ make all CC=cc
+
+decmips:
+ make all CC=cc
+
+decaxp:
+ make all CC=cc
+
+sco5:
+ make all CC=cc
+
+sco3:
+ make all CC=cc EXTRA_LIBS=-lcrypt_i
+
+
+tar: htpasswd
+ $(RM) htpasswd
+
+htpasswd: htpasswd.c
+ $(CC) $(CFLAGS) htpasswd.c -o htpasswd $(EXTRA_LIBS)
+
+htdigest: htdigest.c
+ $(CC) $(CFLAGS) htdigest.c -o htdigest
+
+httpd_monitor: httpd_monitor.c
+ $(CC) $(INCLUDES) $(CFLAGS) httpd_monitor.c -o httpd_monitor
+
+rotatelogs: rotatelogs.c
+ $(CC) $(INCLUDES) $(CFLAGS) rotatelogs.c -o rotatelogs
+
+logresolve: logresolve.c
+ $(CC) $(INCLUDES) $(CFLAGS) logresolve.c -o logresolve
+
+clean:
+ rm -f $(TARGETS)
+
diff --git a/RELEASE_1_1_X/src/support/cls.c b/RELEASE_1_1_X/src/support/cls.c
new file mode 100644
index 00000000000..2c553cec93d
--- /dev/null
+++ b/RELEASE_1_1_X/src/support/cls.c
@@ -0,0 +1,165 @@
+#include
+#include
+#include
+#include
+#include
+
+/*
+ * Compare a string to a mask
+ * Mask characters:
+ * @ - uppercase letter
+ * # - lowercase letter
+ * & - hex digit
+ * # - digit
+ * * - swallow remaining characters
+ * - exact match for any other character
+ */
+static int
+checkmask(const char *data, const char *mask)
+{
+ int i, ch, d;
+
+ for (i=0; mask[i] != '\0' && mask[i] != '*'; i++)
+ {
+ ch = mask[i];
+ d = data[i];
+ if (ch == '@')
+ {
+ if (!isupper(d)) return 0;
+ } else if (ch == '$')
+ {
+ if (!islower(d)) return 0;
+ } else if (ch == '#')
+ {
+ if (!isdigit(d)) return 0;
+ } else if (ch == '&')
+ {
+ if (!isxdigit(d)) return 0;
+ } else if (ch != d) return 0;
+ }
+
+ if (mask[i] == '*') return 1;
+ else return (data[i] == '\0');
+}
+
+/*
+ * Converts 8 hex digits to a time integer
+ */
+static int
+hex2sec(const char *x)
+{
+ int i, ch;
+ unsigned int j;
+
+ for (i=0, j=0; i < 8; i++)
+ {
+ ch = x[i];
+ j <<= 4;
+ if (isdigit(ch)) j |= ch - '0';
+ else if (isupper(ch)) j |= ch - ('A' - 10);
+ else j |= ch - ('a' - 10);
+ }
+ if (j == 0xffffffff) return -1; /* so that it works with 8-byte ints */
+ else return j;
+}
+
+int
+main(int argc, char **argv)
+{
+ int i, ver;
+ DIR *d;
+ struct dirent *e;
+ const char *s;
+ FILE *fp;
+ char path[FILENAME_MAX+1];
+ char line[1035];
+ time_t date, lmod, expire;
+ unsigned int len;
+ struct tm ts;
+ char sdate[30], slmod[30], sexpire[30];
+ const char time_format[]="%e %b %Y %R";
+
+ if (argc != 2)
+ {
+ printf("Usage: cls directory\n");
+ exit(0);
+ }
+
+ d = opendir(argv[1]);
+ if (d == NULL)
+ {
+ perror("opendir");
+ exit(1);
+ }
+
+ for (;;)
+ {
+ e = readdir(d);
+ if (e == NULL) break;
+ s = e->d_name;
+ if (s[0] == '.' || s[0] == '#') continue;
+ sprintf(path, "%s/%s", argv[1], s);
+ fp = fopen(path, "r");
+ if (fp == NULL)
+ {
+ perror("fopen");
+ continue;
+ }
+ if (fgets(line, 1034, fp) == NULL)
+ {
+ perror("fgets");
+ fclose(fp);
+ continue;
+ }
+ if (!checkmask(line, "&&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&&\n"))
+ {
+ fprintf(stderr, "Bad cache file\n");
+ fclose(fp);
+ continue;
+ }
+ date = hex2sec(line);
+ lmod = hex2sec(line+9);
+ expire = hex2sec(line+18);
+ ver = hex2sec(line+27);
+ len = hex2sec(line+35);
+ if (fgets(line, 1034, fp) == NULL)
+ {
+ perror("fgets");
+ fclose(fp);
+ continue;
+ }
+ fclose(fp);
+ i = strlen(line);
+ if (strncmp(line, "X-URL: ", 7) != 0 || line[i-1] != '\n')
+ {
+ fprintf(stderr, "Bad cache file\n");
+ continue;
+ }
+ line[i-1] = '\0';
+ if (date != -1)
+ {
+ ts = *gmtime(&date);
+ strftime(sdate, 30, time_format, &ts);
+ } else
+ strcpy(sdate, "-");
+
+ if (lmod != -1)
+ {
+ ts = *gmtime(&lmod);
+ strftime(slmod, 30, time_format, &ts);
+ } else
+ strcpy(slmod, "-");
+
+ if (expire != -1)
+ {
+ ts = *gmtime(&expire);
+ strftime(sexpire, 30, time_format, &ts);
+ } else
+ strcpy(sexpire, "-");
+
+ printf("%s: %d; %s %s %s\n", line+7, ver, sdate, slmod, sexpire);
+ }
+
+ closedir(d);
+ return 0;
+}
diff --git a/RELEASE_1_1_X/src/support/dbmmanage b/RELEASE_1_1_X/src/support/dbmmanage
new file mode 100644
index 00000000000..c337ddf545b
--- /dev/null
+++ b/RELEASE_1_1_X/src/support/dbmmanage
@@ -0,0 +1,123 @@
+#!/usr/local/bin/perl
+
+# ====================================================================
+# Copyright (c) 1995 The Apache Group. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# 3. All advertising materials mentioning features or use of this
+# software must display the following acknowledgment:
+# "This product includes software developed by the Apache Group
+# for use in the Apache HTTP server project (http://www.apache.org/)."
+#
+# 4. The names "Apache Server" and "Apache Group" must not be used to
+# endorse or promote products derived from this software without
+# prior written permission.
+#
+# 5. Redistributions of any form whatsoever must retain the following
+# acknowledgment:
+# "This product includes software developed by the Apache Group
+# for use in the Apache HTTP server project (http://www.apache.org/)."
+#
+# THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+# ====================================================================
+#
+# This software consists of voluntary contributions made by many
+# individuals on behalf of the Apache Group and was originally based
+# on public domain software written at the National Center for
+# Supercomputing Applications, University of Illinois, Urbana-Champaign.
+# For more information on the Apache Group and the Apache HTTP server
+# project, please see .
+
+
+# usage: dbmmanage
+#
+# commands: add, delete, view, adduser
+#
+# no values needed for delete, no keys or values needed for view.
+# to change a value, simply use "add".
+# adduser encrypts the password:
+# dbmmanage adduser
+#
+# is optional, and may also be supplied to add the user
+# to a specified group:
+# dbmmanage adduser
+
+if (scalar(@ARGV) < 2) {
+ print "Too few arguments.\n";
+ exit;
+}
+
+# ugly - this should be changed to be random.
+$salt="XX";
+$file=$ARGV[0];
+$command=$ARGV[1];
+$key=$ARGV[2];
+$value=$ARGV[3];
+$group=$ARGV[4];
+
+if ($command eq "add") {
+ dbmopen(%DB, $file, 0664) || die "Error: $!\n";
+ $value .= ":$group" if $group ne "";
+ $DB{$key} = $value;
+ dbmclose(%DB);
+ print "Entry $key added with value $value.\n";
+ exit;
+}
+
+if ($command eq "adduser") {
+ $hash = crypt($value, "$salt");
+ dbmopen(%DB, $file, 0664) || die "Error: $!\n";
+ $hash .= ":$group" if $group ne "";
+ $value .= ":$group" if $group ne "";
+ $DB{$key} = $hash;
+ dbmclose(%DB);
+ print "User $key added with password $value, encrypted to $hash\n";
+ exit;
+}
+
+if ($command eq "delete") {
+ dbmopen(%DB, $file, 0664) || die "Error: $!\n";
+ delete($DB{$key});
+ dbmclose(%DB);
+ exit;
+}
+
+if ($command eq "view") {
+ dbmopen(%DB, $file, undef) || die "Error: $!\n";
+ $return_status = 1;
+ unless ($key) {
+ while (($nkey,$val) = each %DB) {
+ print "$nkey = $val\n";
+ }
+ } else {
+ $return_status = 0 if defined $DB{$key};
+ print "$key = $DB{$key}\n";
+ }
+ dbmclose(%DB);
+ exit($return_status);
+}
+
+print "Command unrecognized - must be one of: view, add, adduser, delete.\n";
+
diff --git a/RELEASE_1_1_X/src/support/dbmmanage.new b/RELEASE_1_1_X/src/support/dbmmanage.new
new file mode 100644
index 00000000000..dd07b08ebd6
--- /dev/null
+++ b/RELEASE_1_1_X/src/support/dbmmanage.new
@@ -0,0 +1,133 @@
+#!/usr/local/bin/perl
+
+# ====================================================================
+# Copyright (c) 1995 The Apache Group. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# 3. All advertising materials mentioning features or use of this
+# software must display the following acknowledgment:
+# "This product includes software developed by the Apache Group
+# for use in the Apache HTTP server project (http://www.apache.org/)."
+#
+# 4. The names "Apache Server" and "Apache Group" must not be used to
+# endorse or promote products derived from this software without
+# prior written permission.
+#
+# 5. Redistributions of any form whatsoever must retain the following
+# acknowledgment:
+# "This product includes software developed by the Apache Group
+# for use in the Apache HTTP server project (http://www.apache.org/)."
+#
+# THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+# ====================================================================
+#
+# This software consists of voluntary contributions made by many
+# individuals on behalf of the Apache Group and was originally based
+# on public domain software written at the National Center for
+# Supercomputing Applications, University of Illinois, Urbana-Champaign.
+# For more information on the Apache Group and the Apache HTTP server
+# project, please see .
+
+
+# usage: dbmmanage
+#
+# commands: add, delete, view, adduser
+#
+# no values needed for delete, no keys or values needed for view.
+# to change a value, simply use "add".
+# adduser encrypts the password:
+# dbmmanage adduser
+
+die "Too few arguments.\n" if (@ARGV < 2);
+
+($file,$command,$key,$value) = @ARGV;
+
+$file =~ s/\.db.?$//; # remove ".db" or ".dbX" extension if any
+$file =~ s/\.pag$//; # remove ".pag" and ".dir" as well.
+$file =~ s/\.dir$//; # these are all common DBM extensions.
+
+if ($command eq "add") {
+ dbmopen(%DB, $file, 0664) || die "Error: $!\n";
+ $DB{$key} = $value;
+ dbmclose(%DB);
+ print "Entry $key added with value $value.\n";
+} elsif ($command eq "adduser") {
+ srand; # needs to be done only once.
+ $salt = &compute_salt(0); # change to compute_salt(1) for new crypt()
+ $hash = crypt($value, $salt);
+ dbmopen(%DB, $file, 0664) || die "Error: $!\n";
+ $DB{$key} = $hash;
+ dbmclose(%DB);
+ print "User $key added with password ``$value'', encrypted to $hash\n";
+} elsif ($command eq "delete") {
+ dbmopen(%DB, $file, 0664) || die "Error: $!\n";
+ delete($DB{$key});
+ dbmclose(%DB);
+} elsif ($command eq "view") {
+ dbmopen(%DB, $file, undef) || die "Error: $!\n";
+ unless ($key) {
+ while (($nkey,$val) = each %DB) {
+ print "$nkey = $val\n";
+ }
+ } else {
+ print "$key = $DB{$key}\n";
+ }
+ dbmclose(%DB);
+} else {
+ print "Command unrecognized - must be one of: view, add, adduser, delete.\n";
+}
+
+exit(0);
+
+# if $newstyle is 1, then use new style salt (starts with '_' and contains
+# four bytes of iteration count and four bytes of salt). Otherwise, just use
+# the traditional two-byte salt.
+# see the man page on your system to decide if you have a newer crypt() lib.
+# I believe that 4.4BSD derived systems do (at least BSD/OS 2.0 does).
+# The new style crypt() allows up to 20 characters of the password to be
+# significant rather than only 8.
+sub compute_salt {
+ local($newstyle) = @_;
+ local($salt);
+ if ($newstyle) {
+ $salt = "_" . &randchar(1) . "a.." . &randchar(4);
+ } else {
+ $salt = &randchar(2);
+ }
+ $salt;
+}
+
+# return $count random characters
+sub randchar {
+ local($count) = @_;
+ local($str) = "";
+ local($enc) =
+ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+ while ($count--) {
+ # 64 = length($enc) in call to rand() below
+ $str .= substr($enc,int(rand(64))+1,1);
+ }
+ $str;
+}
diff --git a/RELEASE_1_1_X/src/support/dbmmanage.readme b/RELEASE_1_1_X/src/support/dbmmanage.readme
new file mode 100644
index 00000000000..3862096b1eb
--- /dev/null
+++ b/RELEASE_1_1_X/src/support/dbmmanage.readme
@@ -0,0 +1,6 @@
+Two versions of the dbmmanage script are included with this release.
+One is the old faithful version, which should continue to work if you've
+been using it; the other is a newer cut, which can be easily modified to
+support the newer extended crypt routines which are present on some
+systems (including 4.4BSD derivatives); this newer version is, for the
+nonce, experimental...
diff --git a/RELEASE_1_1_X/src/support/htdigest.c b/RELEASE_1_1_X/src/support/htdigest.c
new file mode 100644
index 00000000000..ab8b712701f
--- /dev/null
+++ b/RELEASE_1_1_X/src/support/htdigest.c
@@ -0,0 +1,182 @@
+/*
+ * htdigest.c: simple program for manipulating digest passwd file for Apache
+ *
+ * by Alexei Kosut, based on htpasswd.c, by Rob McCool
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+/* This is probably the easiest way to do it */
+#include "../src/md5c.c"
+
+#define LF 10
+#define CR 13
+
+#define MAX_STRING_LEN 256
+
+char *tn;
+
+char *strd(char *s) {
+ char *d;
+
+ d=(char *)malloc(strlen(s) + 1);
+ strcpy(d,s);
+ return(d);
+}
+
+void getword(char *word, char *line, char stop) {
+ int x = 0,y;
+
+ for(x=0;((line[x]) && (line[x] != stop));x++)
+ word[x] = line[x];
+
+ word[x] = '\0';
+ if(line[x]) ++x;
+ y=0;
+
+ while((line[y++] = line[x++]));
+}
+
+int getline(char *s, int n, FILE *f) {
+ register int i=0;
+
+ while(1) {
+ s[i] = (char)fgetc(f);
+
+ if(s[i] == CR)
+ s[i] = fgetc(f);
+
+ if((s[i] == 0x4) || (s[i] == LF) || (i == (n-1))) {
+ s[i] = '\0';
+ return (feof(f) ? 1 : 0);
+ }
+ ++i;
+ }
+}
+
+void putline(FILE *f,char *l) {
+ int x;
+
+ for(x=0;l[x];x++) fputc(l[x],f);
+ fputc('\n',f);
+}
+
+
+void add_password(char *user, char *realm, FILE *f) {
+ char *pw, *cpw;
+ MD5_CTX context;
+ unsigned char digest[16];
+ char string[MAX_STRING_LEN];
+ unsigned int i;
+
+ pw = strd((char *) getpass("New password:"));
+ if(strcmp(pw,(char *) getpass("Re-type new password:"))) {
+ fprintf(stderr,"They don't match, sorry.\n");
+ if(tn)
+ unlink(tn);
+ exit(1);
+ }
+ fprintf(f,"%s:%s:",user,realm);
+
+ /* Do MD5 stuff */
+ sprintf(string, "%s:%s:%s", user, realm, pw);
+
+ MD5Init (&context);
+ MD5Update (&context, string, strlen(string));
+ MD5Final (digest, &context);
+
+ for (i = 0; i < 16; i++)
+ fprintf(f, "%02x", digest[i]);
+
+ fprintf(f, "\n");
+}
+
+void usage() {
+ fprintf(stderr,"Usage: htdigest [-c] passwordfile realm username\n");
+ fprintf(stderr,"The -c flag creates a new file.\n");
+ exit(1);
+}
+
+void interrupted() {
+ fprintf(stderr,"Interrupted.\n");
+ if(tn) unlink(tn);
+ exit(1);
+}
+
+void main(int argc, char *argv[]) {
+ FILE *tfp,*f;
+ char user[MAX_STRING_LEN];
+ char realm[MAX_STRING_LEN];
+ char line[MAX_STRING_LEN];
+ char l[MAX_STRING_LEN];
+ char w[MAX_STRING_LEN];
+ char x[MAX_STRING_LEN];
+ char command[MAX_STRING_LEN];
+ int found;
+
+ tn = NULL;
+ signal(SIGINT,(void (*)())interrupted);
+ if(argc == 5) {
+ if(strcmp(argv[1],"-c"))
+ usage();
+ if(!(tfp = fopen(argv[2],"w"))) {
+ fprintf(stderr,"Could not open passwd file %s for writing.\n",
+ argv[2]);
+ perror("fopen");
+ exit(1);
+ }
+ printf("Adding password for %s in realm %s.\n",argv[4], argv[3]);
+ add_password(argv[4],argv[3],tfp);
+ fclose(tfp);
+ exit(0);
+ } else if(argc != 4) usage();
+
+ tn = tmpnam(NULL);
+ if(!(tfp = fopen(tn,"w"))) {
+ fprintf(stderr,"Could not open temp file.\n");
+ exit(1);
+ }
+
+ if(!(f = fopen(argv[1],"r"))) {
+ fprintf(stderr,
+ "Could not open passwd file %s for reading.\n",argv[1]);
+ fprintf(stderr,"Use -c option to create new one.\n");
+ exit(1);
+ }
+ strcpy(user,argv[3]);
+ strcpy(realm,argv[2]);
+
+ found = 0;
+ while(!(getline(line,MAX_STRING_LEN,f))) {
+ if(found || (line[0] == '#') || (!line[0])) {
+ putline(tfp,line);
+ continue;
+ }
+ strcpy(l,line);
+ getword(w,l,':');
+ getword(x,l,':');
+ if(strcmp(user,w) || strcmp(realm,x)) {
+ putline(tfp,line);
+ continue;
+ }
+ else {
+ printf("Changing password for user %s in realm %s\n",user,realm);
+ add_password(user,realm,tfp);
+ found = 1;
+ }
+ }
+ if(!found) {
+ printf("Adding user %s in realm %s\n",user,realm);
+ add_password(user,realm,tfp);
+ }
+ fclose(f);
+ fclose(tfp);
+ sprintf(command,"cp %s %s",tn,argv[1]);
+ system(command);
+ unlink(tn);
+}
diff --git a/RELEASE_1_1_X/src/support/htpasswd.c b/RELEASE_1_1_X/src/support/htpasswd.c
new file mode 100644
index 00000000000..239617056fb
--- /dev/null
+++ b/RELEASE_1_1_X/src/support/htpasswd.c
@@ -0,0 +1,180 @@
+/*
+ * htpasswd.c: simple program for manipulating password file for NCSA httpd
+ *
+ * Rob McCool
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#define LF 10
+#define CR 13
+
+#define MAX_STRING_LEN 256
+
+char *tn;
+
+char *strd(char *s) {
+ char *d;
+
+ d=(char *)malloc(strlen(s) + 1);
+ strcpy(d,s);
+ return(d);
+}
+
+void getword(char *word, char *line, char stop) {
+ int x = 0,y;
+
+ for(x=0;((line[x]) && (line[x] != stop));x++)
+ word[x] = line[x];
+
+ word[x] = '\0';
+ if(line[x]) ++x;
+ y=0;
+
+ while(line[y++] = line[x++]);
+}
+
+int getline(char *s, int n, FILE *f) {
+ register int i=0;
+
+ while(1) {
+ s[i] = (char)fgetc(f);
+
+ if(s[i] == CR)
+ s[i] = fgetc(f);
+
+ if((s[i] == 0x4) || (s[i] == LF) || (i == (n-1))) {
+ s[i] = '\0';
+ return (feof(f) ? 1 : 0);
+ }
+ ++i;
+ }
+}
+
+void putline(FILE *f,char *l) {
+ int x;
+
+ for(x=0;l[x];x++) fputc(l[x],f);
+ fputc('\n',f);
+}
+
+
+/* From local_passwd.c (C) Regents of Univ. of California blah blah */
+static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */
+ "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+
+to64(s, v, n)
+ register char *s;
+ register long v;
+ register int n;
+{
+ while (--n >= 0) {
+ *s++ = itoa64[v&0x3f];
+ v >>= 6;
+ }
+}
+
+char *crypt(char *pw, char *salt); /* why aren't these prototyped in include */
+
+void add_password(char *user, FILE *f) {
+ char *pw, *cpw, salt[3];
+
+ pw = strd((char *) getpass("New password:"));
+ if(strcmp(pw,(char *) getpass("Re-type new password:"))) {
+ fprintf(stderr,"They don't match, sorry.\n");
+ if(tn)
+ unlink(tn);
+ exit(1);
+ }
+ (void)srand((int)time((time_t *)NULL));
+ to64(&salt[0],rand(),2);
+ cpw = crypt(pw,salt);
+ free(pw);
+ fprintf(f,"%s:%s\n",user,cpw);
+}
+
+void usage() {
+ fprintf(stderr,"Usage: htpasswd [-c] passwordfile username\n");
+ fprintf(stderr,"The -c flag creates a new file.\n");
+ exit(1);
+}
+
+void interrupted() {
+ fprintf(stderr,"Interrupted.\n");
+ if(tn) unlink(tn);
+ exit(1);
+}
+
+main(int argc, char *argv[]) {
+ FILE *tfp,*f;
+ char user[MAX_STRING_LEN];
+ char line[MAX_STRING_LEN];
+ char l[MAX_STRING_LEN];
+ char w[MAX_STRING_LEN];
+ char command[MAX_STRING_LEN];
+ int found;
+
+ tn = NULL;
+ signal(SIGINT,(void (*)())interrupted);
+ if(argc == 4) {
+ if(strcmp(argv[1],"-c"))
+ usage();
+ if(!(tfp = fopen(argv[2],"w"))) {
+ fprintf(stderr,"Could not open passwd file %s for writing.\n",
+ argv[2]);
+ perror("fopen");
+ exit(1);
+ }
+ printf("Adding password for %s.\n",argv[3]);
+ add_password(argv[3],tfp);
+ fclose(tfp);
+ exit(0);
+ } else if(argc != 3) usage();
+
+ tn = tmpnam(NULL);
+ if(!(tfp = fopen(tn,"w"))) {
+ fprintf(stderr,"Could not open temp file.\n");
+ exit(1);
+ }
+
+ if(!(f = fopen(argv[1],"r"))) {
+ fprintf(stderr,
+ "Could not open passwd file %s for reading.\n",argv[1]);
+ fprintf(stderr,"Use -c option to create new one.\n");
+ exit(1);
+ }
+ strcpy(user,argv[2]);
+
+ found = 0;
+ while(!(getline(line,MAX_STRING_LEN,f))) {
+ if(found || (line[0] == '#') || (!line[0])) {
+ putline(tfp,line);
+ continue;
+ }
+ strcpy(l,line);
+ getword(w,l,':');
+ if(strcmp(user,w)) {
+ putline(tfp,line);
+ continue;
+ }
+ else {
+ printf("Changing password for user %s\n",user);
+ add_password(user,tfp);
+ found = 1;
+ }
+ }
+ if(!found) {
+ printf("Adding user %s\n",user);
+ add_password(user,tfp);
+ }
+ fclose(f);
+ fclose(tfp);
+ sprintf(command,"cp %s %s",tn,argv[1]);
+ system(command);
+ unlink(tn);
+}
diff --git a/RELEASE_1_1_X/src/support/httpd.1m b/RELEASE_1_1_X/src/support/httpd.1m
new file mode 100644
index 00000000000..e3db0b49874
--- /dev/null
+++ b/RELEASE_1_1_X/src/support/httpd.1m
@@ -0,0 +1,108 @@
+.TH httpd 1m "October 1995"
+.\" Copyright (c) 1995 David Robinson. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\"
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in
+.\" the documentation and/or other materials provided with the
+.\" distribution.
+.\"
+.\" 3. All advertising materials mentioning features or use of this
+.\" software must display the following acknowledgment:
+.\" "This product includes software developed by the Apache Group
+.\" for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" 4. The names "Apache Server" and "Apache Group" must not be used to
+.\" endorse or promote products derived from this software without
+.\" prior written permission.
+.\"
+.\" 5. Redistributions of any form whatsoever must retain the following
+.\" acknowledgment:
+.\" "This product includes software developed by the Apache Group
+.\" for use in the Apache HTTP server project (http://www.apache.org/)."
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+.\" EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+.\" ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+.\" LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+.\" OF THE POSSIBILITY OF SUCH DAMAGE.
+.\" ====================================================================
+.\"
+.\" This software consists of voluntary contributions made by many
+.\" individuals on behalf of the Apache Group and was originally based
+.\" on public domain software written at the National Center for
+.\" Supercomputing Applications, University of Illinois, Urbana-Champaign.
+.\" For more information on the Apache Group and the Apache HTTP server
+.\" project, please see .
+.SH NAME
+httpd \- Apache hypertext transfer protocol server
+.SH SYNOPSIS
+.B httpd
+[
+.B \-vX?
+] [
+.BI \-d " serverroot"
+] [
+.BI \-f " config"
+]
+.SH DESCRIPTION
+.B httpd
+is the Apache HyperText Transfer Protocol (HTTP) server process. The server may
+be invoked by the Internet daemon inetd(1M) each time a connection to the
+HTTP service is made, or alternatively it may run as a daemon.
+.SH OPTIONS
+.TP 12
+.BI \-d " serverroot"
+Set the initial value for the ServerRoot variable to \fIserverroot\fP. This
+can be overridden by the ServerRoot command in the configuration file. The
+default is \fB/usr/local/etc/httpd\fP.
+.TP
+.BI \-f " config"
+Execute the commands in the file \fIconfig\fP on startup. If \fIconfig\fP
+does not begin with a /, then it is taken to be a path relative to
+the ServerRoot. The default is \fBconf/httpd.conf\fP.
+.TP
+.B \-X
+Run in single-process mode, for internal debugging purposes only; the daemon
+does not detach from the terminal or fork any children. Do NOT use this mode
+to provide ordinary web service.
+.TP
+.B \-v
+Print the version of httpd, and then exit.
+.TP
+.B \-?
+Print a list of the httpd options, and then exit.
+.SH FILES
+.PD 0
+.B /usr/local/etc/httpd/conf/httpd.conf
+.br
+.B /usr/local/etc/httpd/conf/srm.conf
+.br
+.B /usr/local/etc/httpd/conf/access.conf
+.br
+.B /usr/local/etc/httpd/conf/mime.types
+.br
+.B /usr/local/etc/httpd/logs/error_log
+.br
+.B /usr/local/etc/httpd/logs/access_log
+.br
+.B /usr/local/etc/httpd/logs/httpd.pid
+.PD
+.SH SEE ALSO
+.BR inetd (1m).
+.PP
+Documentation for the Apache http server is available from
+http://www.apache.org
diff --git a/RELEASE_1_1_X/src/support/httpd_monitor.c b/RELEASE_1_1_X/src/support/httpd_monitor.c
new file mode 100644
index 00000000000..c9a87eef28f
--- /dev/null
+++ b/RELEASE_1_1_X/src/support/httpd_monitor.c
@@ -0,0 +1,302 @@
+/*
+ * ====================================================================
+ * Copyright (c) 1995 The Apache Group. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ * software must display the following acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ * endorse or promote products derived from this software without
+ * prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ * acknowledgment:
+ * "This product includes software developed by the Apache Group
+ * for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+ * IT'S CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see .
+
+
+ * simple script to monitor the child Apache processes
+ * Usage:
+ * httpd_monitor -p pid_file -s sleep_time
+ * Will give you an update ever sleep_time seconds
+ * using pid_file as the location of the PID file.
+ * If you choose 0, it might chew up lots of CPU time.
+ *
+ * Output explanation..
+ *
+ * s = sleeping but "ready to go" child
+ * R = active child
+ * _ = dead child (no longer needed)
+ * t = just starting
+ *
+ *
+ * Jim Jagielski
+ * v1.0 Notes:
+ * This code is much more ugly and complicated than it
+ * needs to be.
+ *
+ * v1.1:
+ * Minor fixes
+ */
+
+#include
+#include
+#include
+#include
+#include "scoreboard.h"
+#include "httpd.h"
+
+#define PIDFILE_OPT "PidFile"
+#define SCORE_OPT "ScoreBoardFile"
+#define DEFAULT_SLEEPTIME 2
+#define ASIZE 1024
+#define MAX_PROC 40
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+ short_score scoreboard_image;
+ FILE *afile;
+ char conf_name[ASIZE];
+ char pid_name[ASIZE];
+ char score_name[ASIZE];
+ char tbuf[ASIZE];
+ char *ptmp;
+ static char kid_stat[] = { '_', 's', 'R', 't' };
+ char achar;
+ long thepid;
+ int score_fd;
+ int sleep_time = DEFAULT_SLEEPTIME;
+ int last_len = 0;
+ int kiddies;
+ int running, dead, total, loop;
+ short got_config = 0;
+ struct stat statbuf;
+ time_t last_time = 0;
+ extern char *optarg;
+ extern int optind, opterr;
+ void lookfor();
+
+ int usage();
+
+ /*
+ * Handle the options. Using getopt() is most probably overkill,
+ * but let's think about the future!
+ */
+ strcpy(conf_name, HTTPD_ROOT);
+ while((achar = getopt(argc,argv,"s:d:f:")) != -1) {
+ switch(achar) {
+ case 'd':
+ strcpy(conf_name, optarg);
+ break;
+ case 'f':
+ strcpy(conf_name, optarg);
+ got_config = 1;
+ break;
+ case 's':
+ sleep_time = atoi(optarg);
+ break;
+ case '?':
+ usage(argv[0]);
+ }
+ }
+
+ /*
+ * Now build the name of the httpd.conf file
+ */
+ if (!got_config) {
+ strcat(conf_name, "/");
+ strcat(conf_name, SERVER_CONFIG_FILE);
+ }
+
+ /*
+ * Make sure we have the right file... Barf if not
+ */
+ if (!(afile = fopen(conf_name, "r"))) {
+ perror("httpd_monitor");
+ fprintf(stderr, "Can't open config file: %s\n", conf_name);
+ exit(1);
+ }
+ /*
+ * now scan thru the ConfigFile to look for the items that
+ * interest us
+ */
+ lookfor(pid_name, score_name, afile);
+ fclose(afile);
+
+ /*
+ * now open the PidFile and then the ScoreBoardFile
+ */
+ if (!(afile = fopen(pid_name, "r"))) {
+ perror("httpd_monitor");
+ fprintf(stderr, "Can't open PIDfile: %s\n", pid_name);
+ exit(1);
+ }
+ fscanf(afile, "%ld", &thepid);
+ fclose(afile);
+
+ /*
+ * Enough taters, time for the MEAT!
+ */
+ for(;;sleep(sleep_time)) {
+ if (stat(score_name, &statbuf)) {
+ perror("httpd_monitor");
+ fprintf(stderr, "Can't stat scoreboard file: %s\n", score_name);
+ exit(1);
+ }
+ if (last_time == statbuf.st_mtime)
+ continue; /* tricky ;) */
+ last_time = statbuf.st_mtime; /* for next time */
+ if ((score_fd = open(score_name, 0)) == -1 ) {
+ perror("httpd_monitor");
+ fprintf(stderr, "Can't open scoreboard file: %s\n", score_name);
+ exit(1);
+ }
+ /*
+ * all that for _this_
+ */
+ running = dead = total = 0;
+ ptmp = tbuf;
+ *ptmp = '\0';
+ for(kiddies=0;kiddies strlen(tbuf)) {
+ for(loop=1;loop<=last_len;loop++)
+ putchar(' ');
+ for(loop=1;loop<=last_len;loop++)
+ putchar('\010');
+ }
+ printf("%s", tbuf);
+ fflush(stdout);
+ last_len = strlen(tbuf);
+ } /* for */
+}
+
+int
+usage(arg)
+char *arg;
+{
+ printf("httpd_monitor: Usage\n");
+ printf(" httpd_monitor [ -d config-dir] [ -s sleep-time ]\n");
+ printf(" Defaults: config-dir = %s\n", HTTPD_ROOT);
+ printf(" sleep-time = %d seconds\n", DEFAULT_SLEEPTIME);
+ exit(0);
+}
+
+/*
+ * This function uses some hard-wired knowledge about the
+ * Apache httpd.conf file setup (basically names of the 3
+ * parameters we are interested in)
+ *
+ * We basically scan thru the file and grab the 3 values we
+ * need. This could be done better...
+ */
+void
+lookfor(pidname, scorename, thefile)
+char *pidname, *scorename;
+FILE *thefile;
+{
+ char line[ASIZE], param[ASIZE], value[ASIZE];
+ char sroot[ASIZE], pidfile[ASIZE], scorefile[ASIZE];
+
+ *sroot = *pidfile = *scorefile = '\0';
+ while (!(feof(thefile))) {
+ fgets(line, ASIZE-1, thefile);
+ *value = '\0'; /* protect braindead sscanf() */
+ sscanf(line, "%s %s", param, value);
+ if (strcmp(param, "PidFile")==0 && *value)
+ strcpy(pidfile, value);
+ if (strcmp(param, "ScoreBoardFile")==0 && *value)
+ strcpy(scorefile, value);
+ if (strcmp(param, "ServerRoot")==0 && *value)
+ strcpy(sroot, value);
+ }
+
+ /*
+ * We've reached EOF... we should have encountered the
+ * ServerRoot line... if not, we bail out
+ */
+ if (!*sroot) {
+ perror("httpd_monitor");
+ fprintf(stderr, "Can't find ServerRoot!\n");
+ exit(1);
+ }
+
+ /*
+ * Not finding PidFile or ScoreBoardFile is OK, since
+ * we have defaults for them
+ */
+ if (!*pidfile)
+ strcpy(pidfile, DEFAULT_PIDLOG);
+ if (!*scorefile)
+ strcpy(scorefile, DEFAULT_SCOREBOARD);
+
+ /*
+ * Relative or absolute? Handle both
+ */
+ if (*pidfile == '/')
+ strcpy(pidname, pidfile);
+ else {
+ strcpy(pidname, sroot);
+ strcat(pidname, "/");
+ strcat(pidname, pidfile);
+ }
+ if (*scorefile == '/')
+ strcpy(scorename, scorefile);
+ else {
+ strcpy(scorename, sroot);
+ strcat(scorename, "/");
+ strcat(scorename, scorefile);
+ }
+}
+
diff --git a/RELEASE_1_1_X/src/support/log_server_status b/RELEASE_1_1_X/src/support/log_server_status
new file mode 100755
index 00000000000..ed75197f085
--- /dev/null
+++ b/RELEASE_1_1_X/src/support/log_server_status
@@ -0,0 +1,110 @@
+#!/usr/local/bin/perl
+
+# ====================================================================
+# Copyright (c) 1995 The Apache Group. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+#
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+#
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in
+# the documentation and/or other materials provided with the
+# distribution.
+#
+# 3. All advertising materials mentioning features or use of this
+# software must display the following acknowledgment:
+# "This product includes software developed by the Apache Group
+# for use in the Apache HTTP server project (http://www.apache.org/)."
+#
+# 4. The names "Apache Server" and "Apache Group" must not be used to
+# endorse or promote products derived from this software without
+# prior written permission.
+#
+# 5. Redistributions of any form whatsoever must retain the following
+# acknowledgment:
+# "This product includes software developed by the Apache Group
+# for use in the Apache HTTP server project (http://www.apache.org/)."
+#
+# THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
+# EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE APACHE GROUP OR
+# ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+# OF THE POSSIBILITY OF SUCH DAMAGE.
+# ====================================================================
+#
+# This software consists of voluntary contributions made by many
+# individuals on behalf of the Apache Group and was originally based
+# on public domain software written at the National Center for
+# Supercomputing Applications, University of Illinois, Urbana-Champaign.
+# For more information on the Apache Group and the Apache HTTP server
+# project, please see .
+
+
+# Log Server Status
+# Mark J Cox, UK Web Ltd 1996, mark@ukweb.com
+#
+# This script is designed to be run at a frequent interval by something
+# like cron. It connects to the server and downloads the status
+# information. It reformats the information to a single line and logs
+# it to a file. Make sure the directory $wherelog is writable by the
+# user who runs this script.
+#
+require 'sys/socket.ph';
+
+$wherelog = "/var/log/graph/"; # Logs will be like "/var/log/graph/960312"
+$server = "localhost"; # Name of server, could be "www.foo.com"
+$port = "80"; # Port on server
+$request = "/status/?auto"; # Request to send
+
+sub tcp_connect
+{
+ local($host,$port) =@_;
+ $sockaddr='S n a4 x8';
+ chop($hostname=`hostname`);
+ $port=(getservbyname($port, 'tcp'))[2] unless $port =~ /^\d+$/;
+ $me=pack($sockaddr,&AF_INET,0,(gethostbyname($hostname))[4]);
+ $them=pack($sockaddr,&AF_INET,$port,(gethostbyname($host))[4]);
+ socket(S,&PF_INET,&SOCK_STREAM,(getprotobyname('tcp'))[2]) ||
+ die "socket: $!";
+ bind(S,$me) || return "bind: $!";
+ connect(S,$them) || return "connect: $!";
+ select(S);
+ $| = 1;
+ select(stdout);
+ return "";
+}
+
+### Main
+
+{
+ $date=`date +%y%m%d:%H%M%S`;
+ chop($date);
+ ($day,$time)=split(/:/,$date);
+ $res=&tcp_connect($server,$port);
+ open(OUT,">>$wherelog$day");
+ if ($res) {
+ print OUT "$time:-1:-1:-1:-1:$res\n";
+ exit 1;
+ }
+ print S "GET $request\n";
+ while () {
+ $requests=$1 if ( m|^BusyServers:\ (\S+)|);
+ $idle=$1 if ( m|^IdleServers:\ (\S+)|);
+ $number=$1 if ( m|sses:\ (\S+)|);
+ $cpu=$1 if (m|^CPULoad:\ (\S+)|);
+ }
+ print OUT "$time:$requests:$idle:$number:$cpu\n";
+}
+
+
diff --git a/RELEASE_1_1_X/src/support/logresolve.c b/RELEASE_1_1_X/src/support/logresolve.c
new file mode 100644
index 00000000000..c66a34467f1
--- /dev/null
+++ b/RELEASE_1_1_X/src/support/logresolve.c
@@ -0,0 +1,352 @@
+/*** ***\
+
+ logresolve 1.1
+
+ Tom Rathborne - tomr@uunet.ca - http://www.uunet.ca/~tomr/
+ UUNET Canada, April 16, 1995
+
+ Rewritten by David Robinson. (drtr@ast.cam.ac.uk)
+
+ Usage: logresolve [-s filename] [-c] < access_log > new_log
+
+ Arguments:
+ -s filename name of a file to record statistics
+ -c check the DNS for a matching A record for the host.
+
+ Notes:
+
+ To generate meaningful statistics from an HTTPD log file, it's good
+ to have the domain name of each machine that accessed your site, but
+ doing this on the fly can slow HTTPD down.
+
+ Compiling NCSA HTTPD with the -DMINIMAL_DNS flag turns IP#->hostname
+ resolution off. Before running your stats program, just run your log
+ file through this program (logresolve) and all of your IP numbers will
+ be resolved into hostnames (where possible).
+
+ logresolve takes an HTTPD access log (in the COMMON log file format,
+ or any other format that has the IP number/domain name as the first
+ field for that matter), and outputs the same file with all of the
+ domain names looked up. Where no domain name can be found, the IP
+ number is left in.
+
+ To minimize impact on your nameserver, logresolve has its very own
+ internal hash-table cache. This means that each IP number will only
+ be looked up the first time it is found in the log file.
+
+ The -c option causes logresolve to apply the same check as httpd
+ compiled with -DMAXIMUM_DNS; after finding the hostname from the IP
+ address, it looks up the IP addresses for the hostname and checks
+ that one of these matches the original address.
+
+\*** ***/
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+#include
+
+#include
+#include
+
+static void cgethost(struct in_addr ipnum, char *string, int check);
+static int getline(char *s, int n);
+static void stats(FILE *output);
+
+
+/* maximum line length */
+#define MAXLINE 1024
+
+/* maximum length of a domain name */
+#ifndef MAXDNAME
+#define MAXDNAME 256
+#endif
+
+/* number of buckets in cache hash table */
+#define BUCKETS 256
+
+/*
+ * struct nsrec - record of nameservice for cache linked list
+ *
+ * ipnum - IP number hostname - hostname noname - nonzero if IP number has no
+ * hostname, i.e. hostname=IP number
+ */
+
+struct nsrec {
+ struct in_addr ipnum;
+ char *hostname;
+ int noname;
+ struct nsrec *next;
+} *nscache[BUCKETS];
+
+/*
+ * statistics - obvious
+ */
+
+/* largeste value for h_errno */
+#define MAX_ERR (NO_ADDRESS)
+#define UNKNOWN_ERR (MAX_ERR+1)
+#define NO_REVERSE (MAX_ERR+2)
+
+static int cachehits = 0;
+static int cachesize = 0;
+static int entries = 0;
+static int resolves = 0;
+static int withname = 0;
+static int errors[MAX_ERR+3];
+
+/*
+ * cgethost - gets hostname by IP address, caching, and adding unresolvable
+ * IP numbers with their IP number as hostname, setting noname flag
+ */
+
+static void
+cgethost(ipnum, string, check)
+struct in_addr ipnum;
+char *string;
+int check;
+{
+ struct nsrec **current, *new;
+ struct hostent *hostdata;
+ char *name;
+ extern int h_errno; /* some machines don't have this in their headers */
+
+ current = &nscache[((ipnum.s_addr + (ipnum.s_addr >> 8) +
+ (ipnum.s_addr >> 16) + (ipnum.s_addr >> 24)) % BUCKETS)];
+
+ while (*current != NULL && ipnum.s_addr != (*current)->ipnum.s_addr)
+ current = & (*current)->next;
+
+ if (*current == NULL)
+ {
+ cachesize++;
+ new = (struct nsrec *) malloc(sizeof(struct nsrec));
+ if (new == NULL)
+ {
+ perror("malloc");
+ fprintf(stderr, "Insufficient memory\n");
+ exit(1);
+ }
+ *current = new;
+ new->next = NULL;
+
+ new->ipnum = ipnum;
+
+ hostdata = gethostbyaddr((const char *) &ipnum, sizeof(struct in_addr),
+ AF_INET);
+ if (hostdata == NULL)
+ {
+ if (h_errno > MAX_ERR) errors[UNKNOWN_ERR]++;
+ else errors[h_errno]++;
+ new->noname = h_errno;
+ name = strdup(inet_ntoa(ipnum));
+ } else
+ {
+ new->noname = 0;
+ name = strdup(hostdata->h_name);
+ if (check)
+ {
+ if (name == NULL)
+ {
+ perror("strdup");
+ fprintf(stderr, "Insufficient memory\n");
+ exit(1);
+ }
+ hostdata = gethostbyname(name);
+ if (hostdata != NULL)
+ {
+ char **hptr;
+
+ for (hptr=hostdata->h_addr_list; *hptr != NULL; hptr++)
+ if(((struct in_addr *)(*hptr))->s_addr == ipnum.s_addr)
+ break;
+ if (*hptr == NULL) hostdata = NULL;
+ }
+ if (hostdata == NULL)
+ {
+ fprintf(stderr, "Bad host: %s != %s\n", name,
+ inet_ntoa(ipnum));
+ new->noname = NO_REVERSE;
+ free(name);
+ name = strdup(inet_ntoa(ipnum));
+ errors[NO_REVERSE]++;
+ }
+ }
+ }
+ new->hostname = name;
+ if (new->hostname == NULL)
+ {
+ perror("strdup");
+ fprintf(stderr, "Insufficient memory\n");
+ exit(1);
+ }
+ } else
+ cachehits++;
+
+ strcpy(string, (*current)->hostname);
+}
+
+/*
+ * prints various statistics to output
+ */
+
+static void
+stats(output)
+FILE *output;
+{
+ int i;
+ char *ipstring;
+ struct nsrec *current;
+ char *errstring[MAX_ERR+3];
+
+ for (i=0; i < MAX_ERR+3; i++) errstring[i] = "Unknown error";
+ errstring[HOST_NOT_FOUND] = "Host not found";
+ errstring[TRY_AGAIN] = "Try again";
+ errstring[NO_RECOVERY] = "Non recoverable error";
+ errstring[NO_DATA] = "No data record";
+ errstring[NO_ADDRESS] = "No address";
+ errstring[NO_REVERSE] = "No reverse entry";
+
+ fprintf(output, "logresolve Statistics:\n");
+
+ fprintf(output, "Entries: %d\n", entries);
+ fprintf(output, " With name : %d\n", withname);
+ fprintf(output, " Resolves : %d\n", resolves);
+ if (errors[HOST_NOT_FOUND])
+ fprintf(output, " - Not found : %d\n", errors[HOST_NOT_FOUND]);
+ if (errors[TRY_AGAIN])
+ fprintf(output, " - Try again : %d\n", errors[TRY_AGAIN]);
+ if (errors[NO_DATA])
+ fprintf(output, " - No data : %d\n", errors[NO_DATA]);
+ if (errors[NO_ADDRESS])
+ fprintf(output, " - No address: %d\n", errors[NO_ADDRESS]);
+ if (errors[NO_REVERSE])
+ fprintf(output, " - No reverse: %d\n", errors[NO_REVERSE]);
+ fprintf(output, "Cache hits : %d\n", cachehits);
+ fprintf(output, "Cache size : %d\n", cachesize);
+ fprintf(output, "Cache buckets : IP number * hostname\n");
+
+ for (i = 0; i < BUCKETS; i++)
+ for (current = nscache[i]; current != NULL; current = current->next)
+ {
+ ipstring = inet_ntoa(current->ipnum);
+ if (current->noname == 0)
+ fprintf(output, " %3d %15s - %s\n", i, ipstring,
+ current->hostname);
+ else
+ {
+ if (current->noname > MAX_ERR+2)
+ fprintf(output, " %3d %15s : Unknown error\n", i,
+ ipstring);
+ else
+ fprintf(output, " %3d %15s : %s\n", i, ipstring,
+ errstring[current->noname]);
+ }
+ }
+}
+
+
+/*
+ * gets a line from stdin
+ */
+
+static int
+getline(s, n)
+char *s;
+int n;
+{
+ char *cp;
+
+ if (!fgets(s, n, stdin))
+ return (0);
+ cp = strchr(s, '\n');
+ if (cp)
+ *cp = '\0';
+ return (1);
+}
+
+int
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ struct in_addr ipnum;
+ char *bar, hoststring[MAXDNAME+1], line[MAXLINE], *statfile;
+ int i, check;
+
+ check = 0;
+ statfile = NULL;
+ for (i=1; i < argc; i++)
+ {
+ if (strcmp(argv[i], "-c") == 0) check = 1;
+ else if (strcmp(argv[i], "-s") == 0)
+ {
+ if (i == argc-1)
+ {
+ fprintf(stderr, "logresolve: missing filename to -s\n");
+ exit(1);
+ }
+ i++;
+ statfile = argv[i];
+ }
+ else
+ {
+ fprintf(stderr, "Usage: logresolve [-s statfile] [-c] < input > output");
+ exit(0);
+ }
+ }
+
+
+ for (i = 0; i < BUCKETS; i++) nscache[i] = NULL;
+ for (i=0; i < MAX_ERR+2; i++) errors[i] = 0;
+
+ while (getline(line, MAXLINE))
+ {
+ if (line[0] == '\0') continue;
+ entries++;
+ if (!isdigit(line[0]))
+ { /* short cut */
+ puts(line);
+ withname++;
+ continue;
+ }
+ bar = strchr(line, ' ');
+ if (bar != NULL) *bar = '\0';
+ ipnum.s_addr = inet_addr(line);
+ if (ipnum.s_addr == 0xffffffffu)
+ {
+ if (bar != NULL) *bar = ' ';
+ puts(line);
+ withname++;
+ continue;
+ }
+
+ resolves++;
+
+ cgethost(ipnum, hoststring, check);
+ if (bar != NULL)
+ printf("%s %s\n", hoststring, bar+1);
+ else
+ puts(hoststring);
+ }
+
+ if (statfile != NULL)
+ {
+ FILE *fp;
+ fp = fopen(statfile, "w");
+ if (fp == NULL)
+ {
+ fprintf(stderr, "logresolve: could not open statistics file '%s'\n"
+ , statfile);
+ exit(1);
+ }
+ stats(fp);
+ fclose(fp);
+ }
+
+ return (0);
+}
diff --git a/RELEASE_1_1_X/src/support/rotatelogs.c b/RELEASE_1_1_X/src/support/rotatelogs.c
new file mode 100644
index 00000000000..e446841fa33
--- /dev/null
+++ b/RELEASE_1_1_X/src/support/rotatelogs.c
@@ -0,0 +1,82 @@
+/*
+
+Simple program to rotate Apache logs without having to kill the server.
+
+Contributed by Ben Laurie
+
+12 Mar 1996
+
+*/
+
+#define BUFSIZE 65536
+#define MAX_PATH 1024
+
+#include
+#include
+#include
+#include
+
+void main(int argc,char **argv)
+ {
+ char buf[BUFSIZE],buf2[MAX_PATH];
+ time_t tLogEnd;
+ time_t tRotation;
+ int nLogFD=-1;
+ int nRead;
+ char *szLogRoot;
+
+ if(argc != 3)
+ {
+ fprintf(stderr,"%s \n\n",argv[0]);
+#ifdef __EMX__
+ fprintf(stderr,"Add this:\n\nTransferLog \"|%s.exe /some/where 86400\"\n\n",argv[0]);
+#else
+ fprintf(stderr,"Add this:\n\nTransferLog \"|%s /some/where 86400\"\n\n",argv[0]);
+#endif
+ fprintf(stderr,"to httpd.conf. The generated name will be /some/where.nnnn where nnnn is the\n");
+ fprintf(stderr,"system time at which the log nominally starts (N.B. this time will always be a\n");
+ fprintf(stderr,"multiple of the rotation time, so you can synchronize cron scripts with it).\n");
+ fprintf(stderr,"At the end of each rotation time a new log is started.\n");
+ exit(1);
+ }
+
+ szLogRoot=argv[1];
+ tRotation=atoi(argv[2]);
+ if(tRotation <= 0)
+ {
+ fprintf(stderr,"Rotation time must be > 0\n");
+ exit(6);
+ }
+
+ for( ; ; )
+ {
+ nRead=read(0,buf,sizeof buf);
+ if(nRead == 0)
+ exit(3);
+ if(nRead < 0)
+ if(errno != EINTR)
+ exit(4);
+ if(nLogFD >= 0 && (time(NULL) >= tLogEnd || nRead < 0))
+ {
+ close(nLogFD);
+ nLogFD=-1;
+ }
+ if(nLogFD < 0)
+ {
+ time_t tLogStart=(time(NULL)/tRotation)*tRotation;
+ sprintf(buf2,"%s.%d",szLogRoot,tLogStart);
+ tLogEnd=tLogStart+tRotation;
+ nLogFD=open(buf2,O_WRONLY|O_CREAT|O_APPEND,0666);
+ if(nLogFD < 0)
+ {
+ perror(buf2);
+ exit(2);
+ }
+ }
+ if(write(nLogFD,buf,nRead) != nRead)
+ {
+ perror(buf2);
+ exit(5);
+ }
+ }
+ }
diff --git a/RELEASE_1_1_X/src/test/cls.c b/RELEASE_1_1_X/src/test/cls.c
new file mode 100644
index 00000000000..2c553cec93d
--- /dev/null
+++ b/RELEASE_1_1_X/src/test/cls.c
@@ -0,0 +1,165 @@
+#include
+#include
+#include
+#include
+#include
+
+/*
+ * Compare a string to a mask
+ * Mask characters:
+ * @ - uppercase letter
+ * # - lowercase letter
+ * & - hex digit
+ * # - digit
+ * * - swallow remaining characters
+ * - exact match for any other character
+ */
+static int
+checkmask(const char *data, const char *mask)
+{
+ int i, ch, d;
+
+ for (i=0; mask[i] != '\0' && mask[i] != '*'; i++)
+ {
+ ch = mask[i];
+ d = data[i];
+ if (ch == '@')
+ {
+ if (!isupper(d)) return 0;
+ } else if (ch == '$')
+ {
+ if (!islower(d)) return 0;
+ } else if (ch == '#')
+ {
+ if (!isdigit(d)) return 0;
+ } else if (ch == '&')
+ {
+ if (!isxdigit(d)) return 0;
+ } else if (ch != d) return 0;
+ }
+
+ if (mask[i] == '*') return 1;
+ else return (data[i] == '\0');
+}
+
+/*
+ * Converts 8 hex digits to a time integer
+ */
+static int
+hex2sec(const char *x)
+{
+ int i, ch;
+ unsigned int j;
+
+ for (i=0, j=0; i < 8; i++)
+ {
+ ch = x[i];
+ j <<= 4;
+ if (isdigit(ch)) j |= ch - '0';
+ else if (isupper(ch)) j |= ch - ('A' - 10);
+ else j |= ch - ('a' - 10);
+ }
+ if (j == 0xffffffff) return -1; /* so that it works with 8-byte ints */
+ else return j;
+}
+
+int
+main(int argc, char **argv)
+{
+ int i, ver;
+ DIR *d;
+ struct dirent *e;
+ const char *s;
+ FILE *fp;
+ char path[FILENAME_MAX+1];
+ char line[1035];
+ time_t date, lmod, expire;
+ unsigned int len;
+ struct tm ts;
+ char sdate[30], slmod[30], sexpire[30];
+ const char time_format[]="%e %b %Y %R";
+
+ if (argc != 2)
+ {
+ printf("Usage: cls directory\n");
+ exit(0);
+ }
+
+ d = opendir(argv[1]);
+ if (d == NULL)
+ {
+ perror("opendir");
+ exit(1);
+ }
+
+ for (;;)
+ {
+ e = readdir(d);
+ if (e == NULL) break;
+ s = e->d_name;
+ if (s[0] == '.' || s[0] == '#') continue;
+ sprintf(path, "%s/%s", argv[1], s);
+ fp = fopen(path, "r");
+ if (fp == NULL)
+ {
+ perror("fopen");
+ continue;
+ }
+ if (fgets(line, 1034, fp) == NULL)
+ {
+ perror("fgets");
+ fclose(fp);
+ continue;
+ }
+ if (!checkmask(line, "&&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&& &&&&&&&&\n"))
+ {
+ fprintf(stderr, "Bad cache file\n");
+ fclose(fp);
+ continue;
+ }
+ date = hex2sec(line);
+ lmod = hex2sec(line+9);
+ expire = hex2sec(line+18);
+ ver = hex2sec(line+27);
+ len = hex2sec(line+35);
+ if (fgets(line, 1034, fp) == NULL)
+ {
+ perror("fgets");
+ fclose(fp);
+ continue;
+ }
+ fclose(fp);
+ i = strlen(line);
+ if (strncmp(line, "X-URL: ", 7) != 0 || line[i-1] != '\n')
+ {
+ fprintf(stderr, "Bad cache file\n");
+ continue;
+ }
+ line[i-1] = '\0';
+ if (date != -1)
+ {
+ ts = *gmtime(&date);
+ strftime(sdate, 30, time_format, &ts);
+ } else
+ strcpy(sdate, "-");
+
+ if (lmod != -1)
+ {
+ ts = *gmtime(&lmod);
+ strftime(slmod, 30, time_format, &ts);
+ } else
+ strcpy(slmod, "-");
+
+ if (expire != -1)
+ {
+ ts = *gmtime(&expire);
+ strftime(sexpire, 30, time_format, &ts);
+ } else
+ strcpy(sexpire, "-");
+
+ printf("%s: %d; %s %s %s\n", line+7, ver, sdate, slmod, sexpire);
+ }
+
+ closedir(d);
+ return 0;
+}