coar 99/05/13 11:25:59
Modified: htdocs/manual/mod mod_autoindex.html src CHANGES src/modules/standard mod_autoindex.c mod_include.c Log: Someone finally stood up and made the ReadmeName and HeaderName features use subrequests. Not only that, but they can be parsed for SSIs too! PR: 1574, 3026, 3529, 3569, 4256 Submitted by: Raymond S Brand <[EMAIL PROTECTED]> Reviewed by: Ken Coar Revision Changes Path 1.31 +57 -17 apache-1.3/htdocs/manual/mod/mod_autoindex.html Index: mod_autoindex.html =================================================================== RCS file: /home/cvs/apache-1.3/htdocs/manual/mod/mod_autoindex.html,v retrieving revision 1.30 retrieving revision 1.31 diff -u -r1.30 -r1.31 --- mod_autoindex.html 1999/01/04 14:35:53 1.30 +++ mod_autoindex.html 1999/05/13 18:25:45 1.31 @@ -426,20 +426,53 @@ <A HREF="directive-dict.html#Module" REL="Help" -><STRONG>Module:</STRONG></A> mod_autoindex<P> +><STRONG>Module:</STRONG></A> mod_autoindex + <BR> + <A + HREF="directive-dict.html#Compatibility" + REL="Help" + ><STRONG>Compatibility:</STRONG></A> some features only available after + 1.3.6; see text +<P> The HeaderName directive sets the name of the file that will be inserted at the top of the index listing. <EM>Filename</EM> is the name of the file -to include, and is taken to be relative to the directory being indexed. -The server first attempts to include <EM>filename</EM><CODE>.html</CODE> -as an HTML document, otherwise it will include <EM>filename</EM> as plain -text. Example: +to include. +</P> +<BLOCKQUOTE><STRONG>Apache 1.3.6 and earlier:</STRONG> +The module first attempts to include <EM>filename</EM><CODE>.html</CODE> +as an HTML document, otherwise it will try to include <EM>filename</EM> as +plain text. <EM>Filename</EM> is treated as a filesystem path relative +to the directory being indexed. In no case is SSI processing done. +Example: <BLOCKQUOTE><CODE>HeaderName HEADER</CODE></BLOCKQUOTE> when indexing the directory <CODE>/web</CODE>, the server will first look for the HTML file <CODE>/web/HEADER.html</CODE> and include it if found, otherwise it will include the plain text file <CODE>/web/HEADER</CODE>, if it exists. - -<P>See also <A HREF="#readmename">ReadmeName</A>.<P><HR> +</BLOCKQUOTE> +<BLOCKQUOTE><STRONG>Apache versions after 1.3.6:</STRONG> +<EM>Filename</EM> is treated as a URI path relative to the one used +to access the directory being indexed, and must resolve to a document +with a major content type of "<SAMP>text</SAMP>" (<EM>e.g.</EM>, +<SAMP>text/html</SAMP>, <SAMP>text/plain</SAMP>, <EM>etc.</EM>). +This means that <EM>filename</EM> may refer to a CGI script if the +script's actual file type (as opposed to its output) is marked as +<SAMP>text/html</SAMP> such as with a directive like: +<PRE> + AddType text/html .cgi +</PRE> +<A HREF="../content-negotiation.html">Content negotiation</A> +will be performed if the <SAMP>MultiViews</SAMP> +<A HREF="core.html#options">option</A> is enabled. +If <EM>filename</EM> resolves to a static <SAMP>text/html</SAMP> document +(not a CGI script) and the +<SAMP>Includes</SAMP> <A HREF="core.html#options">option</A> is enabled, +the file will be processed for server-side includes (see the +<A HREF="mod_include.html"><SAMP>mod_include</SAMP></A> documentation). +</BLOCKQUOTE> +<P> +See also <A HREF="#readmename">ReadmeName</A>. +<P><HR> <H2><A NAME="indexignore">IndexIgnore</A></H2> <!--%plaintext <?INDEX {\tt IndexIgnore} directive> --> @@ -753,19 +786,26 @@ <A HREF="directive-dict.html#Module" REL="Help" -><STRONG>Module:</STRONG></A> mod_autoindex<P> +><STRONG>Module:</STRONG></A> mod_autoindex + <BR> + <A + HREF="directive-dict.html#Compatibility" + REL="Help" + ><STRONG>Compatibility:</STRONG></A> some features only available after + 1.3.6; see text +<P> The ReadmeName directive sets the name of the file that will be appended to the end of the index listing. <EM>Filename</EM> is the name of the file -to include, and is taken to be relative to the directory being indexed. -The server first attempts to include <EM>filename</EM><CODE>.html</CODE> -as an HTML document, otherwise it will include <EM>filename</EM> as plain -text. Example: -<BLOCKQUOTE><CODE>ReadmeName README</CODE></BLOCKQUOTE> -when indexing the directory <CODE>/web</CODE>, the server will first look for -the HTML file <CODE>/web/README.html</CODE> and include it if found, otherwise -it will include the plain text file <CODE>/web/README</CODE>, if it exists. - +to include, and is taken to be relative to the location being indexed. +</P> +<BLOCKQUOTE> +<STRONG>The <EM>filename</EM> argument is treated as a stub filename +in Apache 1.3.6 and earlier, and as a relative URI in later versions. +Details of how it is handled may be found under the description of +the <A HREF="#headername">HeaderName</A> directive, which uses the +same mechanism and changed at the same time as ReadmeName.</STRONG> +</BLOCKQUOTE> <P>See also <A HREF="#headername">HeaderName</A>.<P> 1.1354 +8 -0 apache-1.3/src/CHANGES Index: CHANGES =================================================================== RCS file: /home/cvs/apache-1.3/src/CHANGES,v retrieving revision 1.1353 retrieving revision 1.1354 diff -u -r1.1353 -r1.1354 --- CHANGES 1999/05/12 16:50:40 1.1353 +++ CHANGES 1999/05/13 18:25:50 1.1354 @@ -1,5 +1,13 @@ Changes with Apache 1.3.7 + *) Support for server-parsed and multiview-determined ReadmeName and + HeaderName files in mod_autoindex. Removed the restriction on + "/"s in ReadmeName and HeaderName directives since the *sub_req* + routines will deal with the access issues. (It's now possible to + have {site|group|project|customer|...} wide readmes and headers.) + [Raymond S Brand <[EMAIL PROTECTED]>, Ken Coar] PR#1574, 3026, 3529, + 3569, 4256 + *) When stat() fails, don't assume anything about the contents of the struct stat. [Ed Korthof <[EMAIL PROTECTED]>] 1.107 +208 -99 apache-1.3/src/modules/standard/mod_autoindex.c Index: mod_autoindex.c =================================================================== RCS file: /home/cvs/apache-1.3/src/modules/standard/mod_autoindex.c,v retrieving revision 1.106 retrieving revision 1.107 diff -u -r1.106 -r1.107 --- mod_autoindex.c 1999/05/03 20:48:43 1.106 +++ mod_autoindex.c 1999/05/13 18:25:56 1.107 @@ -322,9 +322,6 @@ static const char *add_header(cmd_parms *cmd, void *d, char *name) { - if (strchr(name, '/')) { - return "HeaderName cannot contain a /"; - } push_item(((autoindex_config_rec *) d)->hdr_list, 0, NULL, cmd->path, name); return NULL; @@ -332,9 +329,6 @@ static const char *add_readme(cmd_parms *cmd, void *d, char *name) { - if (strchr(name, '/')) { - return "ReadmeName cannot contain a /"; - } push_item(((autoindex_config_rec *) d)->rdme_list, 0, NULL, cmd->path, name); return NULL; @@ -864,99 +858,223 @@ */ /* - * Look for the specified file, and pump it into the response stream if we - * find it. + * Elements of the emitted document: + * Preamble + * Emitted unless SUPPRESS_PREAMBLE is set AND ap_run_sub_req + * succeeds for the (content_type == text/html) header file. + * Header file + * Emitted if found (and able). + * H1 tag line + * Emitted if a header file is NOT emitted. + * Directory stuff + * Always emitted. + * HR + * Emitted if FANCY_INDEXING is set. + * Readme file + * Emitted if found (and able). + * ServerSig + * Emitted if ServerSignature is not Off AND a readme file + * is NOT emitted. + * Postamble + * Emitted unless SUPPRESS_PREAMBLE is set AND ap_run_sub_req + * succeeds for the (content_type == text/html) readme file. */ -static int insert_readme(char *name, char *readme_fname, char *title, - int hrule, int whichend, request_rec *r) + + +/* + * emit a plain text file + */ +static void do_emit_plain(request_rec *r, FILE *f) { - char *fn; - FILE *f; - struct stat finfo; - int plaintext = 0; - request_rec *rr; - autoindex_config_rec *cfg; - int autoindex_opts; + char buf[IOBUFSIZE + 1]; + int i, n, c, ch; - cfg = (autoindex_config_rec *) ap_get_module_config(r->per_dir_config, - &autoindex_module); - autoindex_opts = cfg->opts; - /* XXX: this is a load of crap, it needs to do a full sub_req_lookup_uri */ - fn = ap_make_full_path(r->pool, name, readme_fname); - fn = ap_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 (hrule) { - ap_rputs("<HR>\n", r); + ap_rputs("<PRE>\n", r); + 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'; + ap_rputs(&buf[c], r); + if (ch == '<') { + ap_rputs("<", r); + } + else if (ch == '>') { + ap_rputs(">", r); + } + else if (ch == '&') { + ap_rputs("&", r); + } + c = i + 1; } - } - else if (hrule) { - ap_rputs("<HR>\n", r); } - /* XXX: when the above is rewritten properly, this necessary security - * check will be redundant. -djg */ - rr = ap_sub_req_lookup_file(fn, r); - if (rr->status != HTTP_OK) { - ap_destroy_sub_req(rr); - return 0; - } - ap_destroy_sub_req(rr); - if (!(f = ap_pfopen(r->pool, fn, "r"))) { - return 0; + ap_rputs("</PRE>\n", r); +} + +/* See mod_include */ +#define SUB_REQ_STRING "Sub request to mod_include" +#define PARENT_STRING "Parent request to mod_include" + +/* + * Handle the preamble through the H1 tag line, inclusive. Locate + * the file with a subrequests. Process text/html documents by actually + * running the subrequest; text/xxx documents get copied verbatim, + * and any other content type is ignored. This means that a non-text + * document (such as HEADER.gif) might get multiviewed as the result + * instead of a text document, meaning nothing will be displayed, but + * oh well. + */ +static void emit_head(request_rec *r, char *header_fname, int suppress_amble, + char *title) +{ + FILE *f; + request_rec *rr = NULL; + int emit_amble = 1; + int emit_H1 = 1; + + /* + * If there's a header file, send a subrequest to look for it. If it's + * found and a text file, handle it -- otherwise fall through and + * pretend there's nothing there. + */ + if ((header_fname != NULL) + && (rr = ap_sub_req_lookup_uri(header_fname, r)) + && (rr->status == HTTP_OK) + && (rr->filename != NULL) + && S_ISREG(rr->finfo.st_mode)) { + /* + * Check for the two specific cases we allow: text/html and + * text/anything-else. The former is allowed to be processed for + * SSIs. + */ + if (rr->content_type != NULL) { + if (!strcasecmp("text/html", rr->content_type)) { + /* Hope everything will work... */ + emit_amble = 0; + emit_H1 = 0; + + if (! suppress_amble) { + emit_preamble(r, title); + } + ap_table_add(r->notes, PARENT_STRING, ""); + ap_table_add(rr->notes, SUB_REQ_STRING, ""); + /* + * If there's a problem running the subrequest, display the + * preamble if we didn't do it before -- the header file + * didn't get displayed. + */ + if (ap_run_sub_req(rr) != OK) { + /* It didn't work */ + emit_amble = suppress_amble; + emit_H1 = 1; + } + } + else if (!strncasecmp("text/", rr->content_type, 5)) { + /* + * If we can open the file, prefix it with the preamble + * regardless; since we'll be sending a <PRE> block around + * the file's contents, any HTML header it had won't end up + * where it belongs. + */ + if ((f = ap_pfopen(r->pool, rr->filename, "r")) != 0) { + emit_preamble(r, title); + emit_amble = 0; + do_emit_plain(r, f); + ap_pfclose(r->pool, f); + emit_H1 = 0; + } + } + } } - if ((whichend == FRONT_MATTER) - && (!(autoindex_opts & SUPPRESS_PREAMBLE))) { + + if (emit_amble) { emit_preamble(r, title); } - if (!plaintext) { - ap_send_fd(f, r); + if (emit_H1) { + ap_rvputs(r, "<H1>Index of ", title, "</H1>\n", NULL); } - else { - char buf[IOBUFSIZE + 1]; - int i, n, c, ch; - ap_rputs("<PRE>\n", r); - 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'; - ap_rputs(&buf[c], r); - if (ch == '<') { - ap_rputs("<", r); - } - else if (ch == '>') { - ap_rputs(">", r); + if (rr != NULL) { + ap_destroy_sub_req(rr); + } +} + + +/* + * Handle the Readme file through the postamble, inclusive. Locate + * the file with a subrequests. Process text/html documents by actually + * running the subrequest; text/xxx documents get copied verbatim, + * and any other content type is ignored. This means that a non-text + * document (such as FOOTER.gif) might get multiviewed as the result + * instead of a text document, meaning nothing will be displayed, but + * oh well. + */ +static void emit_tail(request_rec *r, char *readme_fname, int suppress_amble) +{ + FILE *f; + request_rec *rr = NULL; + int suppress_post = 0; + int suppress_sig = 0; + + /* + * If there's a readme file, send a subrequest to look for it. If it's + * found and a text file, handle it -- otherwise fall through and + * pretend there's nothing there. + */ + if ((readme_fname != NULL) + && (rr = ap_sub_req_lookup_uri(readme_fname, r)) + && (rr->status == HTTP_OK) + && (rr->filename != NULL) + && S_ISREG(rr->finfo.st_mode)) { + /* + * Check for the two specific cases we allow: text/html and + * text/anything-else. The former is allowed to be processed for + * SSIs. + */ + if (rr->content_type != NULL) { + if (!strcasecmp("text/html", rr->content_type)) { + ap_table_add(r->notes, PARENT_STRING, ""); + ap_table_add(rr->notes, SUB_REQ_STRING, ""); + if (ap_run_sub_req(rr) == OK) { + /* worked... */ + suppress_sig = 1; + suppress_post = suppress_amble; } - else if (ch == '&') { - ap_rputs("&", r); + } + else if (!strncasecmp("text/", rr->content_type, 5)) { + /* + * If we can open the file, suppress the signature. + */ + if ((f = ap_pfopen(r->pool, rr->filename, "r")) != 0) { + do_emit_plain(r, f); + ap_pfclose(r->pool, f); + suppress_sig = 1; } - c = i + 1; } } + } + + if (!suppress_sig) { + ap_rputs(ap_psignature("", r), r); } - ap_pfclose(r->pool, f); - if (plaintext) { - ap_rputs("</PRE>\n", r); + if (!suppress_post) { + ap_rputs("</BODY></HTML>\n", r); + } + if (rr != NULL) { + ap_destroy_sub_req(rr); } - return 1; } @@ -1389,7 +1507,6 @@ int num_ent = 0, x; struct ent *head, *p; struct ent **ar = NULL; - char *tmp; const char *qstring; int autoindex_opts = autoindex_conf->opts; char keyid; @@ -1419,12 +1536,8 @@ *title_endp-- = '\0'; } - if ((!(tmp = find_header(autoindex_conf, r))) - || (!(insert_readme(name, tmp, title_name, NO_HRULE, FRONT_MATTER, r))) - ) { - emit_preamble(r, title_name); - ap_rvputs(r, "<H1>Index of ", title_name, "</H1>\n", NULL); - } + emit_head(r, find_header(autoindex_conf, r), + autoindex_opts & SUPPRESS_PREAMBLE, title_name); /* * Figure out what sort of indexing (if any) we're supposed to use. @@ -1489,15 +1602,11 @@ direction); ap_pclosedir(r->pool, d); - if ((tmp = find_readme(autoindex_conf, r))) { - if (!insert_readme(name, tmp, "", - ((autoindex_opts & FANCY_INDEXING) ? HRULE - : NO_HRULE), - END_MATTER, r)) { - ap_rputs(ap_psignature("<HR>\n", r), r); - } + if (autoindex_opts & FANCY_INDEXING) { + ap_rputs("<HR>\n", r); } - ap_rputs("</BODY></HTML>\n", r); + emit_tail(r, find_readme(autoindex_conf, r), + autoindex_opts & SUPPRESS_PREAMBLE); ap_kill_timeout(r); return 0; 1.115 +40 -9 apache-1.3/src/modules/standard/mod_include.c Index: mod_include.c =================================================================== RCS file: /home/cvs/apache-1.3/src/modules/standard/mod_include.c,v retrieving revision 1.114 retrieving revision 1.115 diff -u -r1.114 -r1.115 --- mod_include.c 1999/05/05 17:46:07 1.114 +++ mod_include.c 1999/05/13 18:25:57 1.115 @@ -108,9 +108,6 @@ module MODULE_VAR_EXPORT includes_module; -/* just need some arbitrary non-NULL pointer which can't also be a request_rec */ -#define NESTED_INCLUDE_MAGIC (&includes_module) - /* ------------------------ Environment function -------------------------- */ /* XXX: could use ap_table_overlap here */ @@ -746,9 +743,7 @@ } /* destroy the sub request if it's not a nested include */ - if (rr != NULL - && ap_get_module_config(rr->request_config, &includes_module) - != NESTED_INCLUDE_MAGIC) { + if (rr != NULL) { ap_destroy_sub_req(rr); } } @@ -2376,6 +2371,41 @@ return OK; } +#define SUB_REQ_STRING "Sub request to mod_include" +#define PARENT_STRING "Parent request to mod_include" + + if (ap_table_get(r->notes, SUB_REQ_STRING) != NULL) { + request_rec *p = r->main; + + /* + * The note is a flag to mod_include that this request is actually + * a subrequest from another module and that mod_include needs to + * treat it as if it's a subrequest from mod_include. + * + * HACK ALERT! + * There is no good way to pass the parent request_rec to mod_include. + * Tables only take string values and there is nowhere appropriate in + * in the request_rec that can safely be used. + * + * So we search up the chain of requests and redirects looking for + * the parent request. + */ + + while (p) { + if (ap_table_get(p->notes, PARENT_STRING) != NULL) { + /* Kludge --- See below */ + ap_set_module_config(r->request_config, &includes_module, p); + + ap_add_common_vars(p); + ap_add_cgi_vars(p); + add_include_vars(p, DEFAULT_TIME_FORMAT); + ap_table_unset(r->notes, SUB_REQ_STRING); + break; + } + p = (p->prev) ? p->prev : p->main; + } + } + if ((parent = ap_get_module_config(r->request_config, &includes_module))) { /* Kludge --- for nested includes, we want to keep the subprocess * environment of the base document (for compatibility); that means @@ -2411,9 +2441,10 @@ send_parsed_content(f, r); if (parent) { - /* signify that the sub request should not be killed */ - ap_set_module_config(r->request_config, &includes_module, - NESTED_INCLUDE_MAGIC); + /* Kludge --- Doing this allows the caller to safely destroy the + * sub_req + */ + r->pool = ap_make_sub_pool(r->pool); } ap_kill_timeout(r);