#define DEBUG 0
#define IOBUF_SIZE 8192
+#define MAX_HTTP_HEADERS_SIZE ((8*1024) - 16)
#if PIPE_BUF >= IOBUF_SIZE
# error "PIPE_BUF >= IOBUF_SIZE"
#endif
char *url_to;
} Htaccess_Proxy;
+typedef enum CGI_type {
+ CGI_NONE = 0,
+ CGI_NORMAL,
+ CGI_INDEX,
+ CGI_INTERPRETER,
+} CGI_type;
+
enum {
HTTP_OK = 200,
HTTP_PARTIAL_CONTENT = 206,
HTTP_REQUEST_TIMEOUT = 408,
HTTP_NOT_IMPLEMENTED = 501, /* used for unrecognized requests */
HTTP_INTERNAL_SERVER_ERROR = 500,
+ HTTP_ENTITY_TOO_LARGE = 413,
HTTP_CONTINUE = 100,
#if 0 /* future use */
HTTP_SWITCHING_PROTOCOLS = 101,
HTTP_BAD_REQUEST,
HTTP_FORBIDDEN,
HTTP_INTERNAL_SERVER_ERROR,
+ HTTP_ENTITY_TOO_LARGE,
#if 0 /* not implemented */
HTTP_CREATED,
HTTP_ACCEPTED,
{ "Bad Request", "Unsupported method" },
{ "Forbidden", "" },
{ "Internal Server Error", "Internal Server Error" },
+ { "Entity Too Large", "Entity Too Large" },
#if 0 /* not implemented */
{ "Created" },
{ "Accepted" },
/* client can handle gzip / we are going to send gzip */
smallint content_gzip;
#endif
- unsigned rmt_ip; /* used for IP-based allow/deny rules */
time_t last_mod;
char *rmt_ip_str; /* for $REMOTE_ADDR and $REMOTE_PORT */
const char *bind_addr_or_port;
- const char *g_query;
+ char *g_query;
const char *opt_c_configFile;
const char *home_httpd;
const char *index_page;
IF_FEATURE_HTTPD_BASIC_AUTH(const char *g_realm;)
IF_FEATURE_HTTPD_BASIC_AUTH(char *remoteuser;)
- IF_FEATURE_HTTPD_CGI(char *referer;)
- IF_FEATURE_HTTPD_CGI(char *user_agent;)
- IF_FEATURE_HTTPD_CGI(char *host;)
- IF_FEATURE_HTTPD_CGI(char *http_accept;)
- IF_FEATURE_HTTPD_CGI(char *http_accept_language;)
off_t file_size; /* -1 - unknown */
#if ENABLE_FEATURE_HTTPD_RANGES
#else
# define content_gzip 0
#endif
-#define rmt_ip (G.rmt_ip )
#define bind_addr_or_port (G.bind_addr_or_port)
#define g_query (G.g_query )
#define opt_c_configFile (G.opt_c_configFile )
file_size = range_end - range_start + 1;
}
#endif
+
+//RFC 2616 4.4 Message Length
+// The transfer-length of a message is the length of the message-body as
+// it appears in the message; that is, after any transfer-codings have
+// been applied. When a message-body is included with a message, the
+// transfer-length of that body is determined by one of the following
+// (in order of precedence):
+// 1.Any response message which "MUST NOT" include a message-body (such
+// as the 1xx, 204, and 304 responses and any response to a HEAD
+// request) is always terminated by the first empty line after the
+// header fields, regardless of the entity-header fields present in
+// the message.
+// 2.If a Transfer-Encoding header field (section 14.41) is present and
+// has any value other than "identity", then the transfer-length is
+// defined by use of the "chunked" transfer-coding (section 3.6),
+// unless the message is terminated by closing the connection.
+// 3.If a Content-Length header field (section 14.13) is present, its
+// decimal value in OCTETs represents both the entity-length and the
+// transfer-length. The Content-Length header field MUST NOT be sent
+// if these two lengths are different (i.e., if a Transfer-Encoding
+// header field is present). If a message is received with both a
+// Transfer-Encoding header field and a Content-Length header field,
+// the latter MUST be ignored.
+// 4.If the message uses the media type "multipart/byteranges" ...
+// 5.By the server closing the connection.
+//
+// (NB: standards do not define "Transfer-Length:" _header_,
+// transfer-length above is just a concept).
+
len += sprintf(iobuf + len,
#if ENABLE_FEATURE_HTTPD_RANGES
"Accept-Ranges: bytes\r\n"
#endif
"Last-Modified: %s\r\n"
- "%s-Length: %"OFF_FMT"u\r\n",
+ /* Because of 4.4 (5), we can forgo sending of "Content-Length"
+ * since we close connection afterwards, but it helps clients
+ * to e.g. estimate download times, show progress bars etc.
+ * Theoretically we should not send it if page is compressed,
+ * but de-facto standard is to send it (see comment below).
+ */
+ "Content-Length: %"OFF_FMT"u\r\n",
date_str,
- content_gzip ? "Transfer" : "Content",
file_size
);
}
+ /* This should be "Transfer-Encoding", not "Content-Encoding":
+ * "data is compressed for transfer", not "data is an archive".
+ * But many clients were not handling "Transfer-Encoding" correctly
+ * (they were not uncompressing gzipped pages, tried to show
+ * raw compressed data), and servers worked around it by using
+ * "Content-Encoding" instead... and this become de-facto standard.
+ * https://bugzilla.mozilla.org/show_bug.cgi?id=68517
+ * https://bugs.chromium.org/p/chromium/issues/detail?id=94730
+ */
if (content_gzip)
len += sprintf(iobuf + len, "Content-Encoding: gzip\r\n");
* ('\r' and '\n' are not counted).
* Data is returned in iobuf.
*/
-static int get_line(void)
+static unsigned get_line(void)
{
- int count;
+ unsigned count;
char c;
alarm(HEADER_READ_TIMEOUT);
* const char *url The requested URL (with leading /).
* const char *orig_uri The original URI before rewriting (if any)
* int post_len Length of the POST body.
- * const char *cookie For set HTTP_COOKIE.
- * const char *content_type For set CONTENT_TYPE.
*/
static void send_cgi_and_exit(
const char *url,
const char *orig_uri,
const char *request,
- int post_len,
- const char *cookie,
- const char *content_type) NORETURN;
+ int post_len) NORETURN;
static void send_cgi_and_exit(
const char *url,
const char *orig_uri,
const char *request,
- int post_len,
- const char *cookie,
- const char *content_type)
+ int post_len)
{
struct fd_pair fromCgi; /* CGI -> httpd pipe */
struct fd_pair toCgi; /* httpd -> CGI pipe */
#endif
}
}
- setenv1("HTTP_USER_AGENT", G.user_agent);
- if (G.http_accept)
- setenv1("HTTP_ACCEPT", G.http_accept);
- if (G.http_accept_language)
- setenv1("HTTP_ACCEPT_LANGUAGE", G.http_accept_language);
if (post_len)
putenv(xasprintf("CONTENT_LENGTH=%u", post_len));
- if (cookie)
- setenv1("HTTP_COOKIE", cookie);
- if (content_type)
- setenv1("CONTENT_TYPE", content_type);
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
if (remoteuser) {
setenv1("REMOTE_USER", remoteuser);
putenv((char*)"AUTH_TYPE=Basic");
}
#endif
- if (G.referer)
- setenv1("HTTP_REFERER", G.referer);
- setenv1("HTTP_HOST", G.host); /* set to "" if NULL */
/* setenv1("SERVER_NAME", safe_gethostname()); - don't do this,
* just run "env SERVER_NAME=xyz httpd ..." instead */
log_and_exit();
}
-static void if_ip_denied_send_HTTP_FORBIDDEN_and_exit(void)
+static void if_ip_denied_send_HTTP_FORBIDDEN_and_exit(unsigned remote_ip)
{
Htaccess_IP *cur;
(unsigned char)(cur->mask)
);
#endif
- if ((rmt_ip & cur->mask) == cur->ip) {
+ if ((remote_ip & cur->mask) == cur->ip) {
if (cur->allow_deny == 'A')
return;
send_headers_and_exit(HTTP_FORBIDDEN);
char *urlcopy;
char *urlp;
char *tptr;
+ unsigned remote_ip;
+#if ENABLE_FEATURE_HTTPD_CGI
+ unsigned total_headers_len;
+#endif
#if ENABLE_FEATURE_HTTPD_CGI
static const char request_HEAD[] ALIGN1 = "HEAD";
const char *prequest;
- char *cookie = NULL;
- char *content_type = NULL;
unsigned long length = 0;
+ enum CGI_type cgi_type = CGI_NONE;
#elif ENABLE_FEATURE_HTTPD_PROXY
#define prequest request_GET
unsigned long length = 0;
smallint authorized = -1;
#endif
char http_major_version;
-#if ENABLE_FEATURE_HTTPD_PROXY
- char http_minor_version;
- char *header_buf = header_buf; /* for gcc */
- char *header_ptr = header_ptr;
- Htaccess_Proxy *proxy_entry;
-#endif
+ char *HTTP_slash;
/* Allocation of iobuf is postponed until now
* (IOW, server process doesn't need to waste 8k) */
iobuf = xmalloc(IOBUF_SIZE);
- rmt_ip = 0;
+ remote_ip = 0;
if (fromAddr->u.sa.sa_family == AF_INET) {
- rmt_ip = ntohl(fromAddr->u.sin.sin_addr.s_addr);
+ remote_ip = ntohl(fromAddr->u.sin.sin_addr.s_addr);
}
#if ENABLE_FEATURE_IPV6
if (fromAddr->u.sa.sa_family == AF_INET6
&& fromAddr->u.sin6.sin6_addr.s6_addr32[0] == 0
&& fromAddr->u.sin6.sin6_addr.s6_addr32[1] == 0
&& ntohl(fromAddr->u.sin6.sin6_addr.s6_addr32[2]) == 0xffff)
- rmt_ip = ntohl(fromAddr->u.sin6.sin6_addr.s6_addr32[3]);
+ remote_ip = ntohl(fromAddr->u.sin6.sin6_addr.s6_addr32[3]);
#endif
if (ENABLE_FEATURE_HTTPD_CGI || DEBUG || verbose) {
/* NB: can be NULL (user runs httpd -i by hand?) */
if (verbose > 2)
bb_error_msg("connected");
}
+ if_ip_denied_send_HTTP_FORBIDDEN_and_exit(remote_ip);
/* Install timeout handler. get_line() needs it. */
signal(SIGALRM, send_REQUEST_TIMEOUT_and_exit);
send_headers_and_exit(HTTP_BAD_REQUEST);
/* Find end of URL and parse HTTP version, if any */
- http_major_version = '0';
- IF_FEATURE_HTTPD_PROXY(http_minor_version = '0';)
- tptr = strchrnul(urlp, ' ');
+//TODO: maybe just reject all queries which have no " HTTP/xyz" suffix?
+//Then 'http_major_version' can be deleted
+ http_major_version = ('0' - 1); /* "less than 0th" version */
+ HTTP_slash = strchrnul(urlp, ' ');
/* Is it " HTTP/"? */
- if (tptr[0] && strncmp(tptr + 1, HTTP_200, 5) == 0) {
- http_major_version = tptr[6];
- IF_FEATURE_HTTPD_PROXY(http_minor_version = tptr[8];)
+ if (HTTP_slash[0] && strncmp(HTTP_slash + 1, HTTP_200, 5) == 0) {
+ http_major_version = HTTP_slash[6];
+ *HTTP_slash++ = '\0';
}
- *tptr = '\0';
/* Copy URL from after "GET "/"POST " to stack-allocated char[] */
- urlcopy = alloca((tptr - urlp) + 2 + strlen(index_page));
+ urlcopy = alloca((HTTP_slash - urlp) + 2 + strlen(index_page));
/*if (urlcopy == NULL)
* send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);*/
strcpy(urlcopy, urlp);
/* NB: urlcopy ptr is never changed after this */
- /* Extract url args if present */
- /* g_query = NULL; - already is */
- tptr = strchr(urlcopy, '?');
- if (tptr) {
- *tptr++ = '\0';
- g_query = tptr;
+#if ENABLE_FEATURE_HTTPD_PROXY
+ {
+ int proxy_fd;
+ len_and_sockaddr *lsa;
+ Htaccess_Proxy *proxy_entry = find_proxy_entry(urlcopy);
+
+ if (proxy_entry) {
+ lsa = host2sockaddr(proxy_entry->host_port, 80);
+ if (!lsa)
+ send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
+ proxy_fd = socket(lsa->u.sa.sa_family, SOCK_STREAM, 0);
+ if (proxy_fd < 0)
+ send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
+ if (connect(proxy_fd, &lsa->u.sa, lsa->len) < 0)
+ send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
+ /* Config directive was of the form:
+ * P:/url:[http://]hostname[:port]/new/path
+ * When /urlSFX is requested, reverse proxy it
+ * to http://hostname[:port]/new/pathSFX
+ */
+ fdprintf(proxy_fd, "%s %s%s %s\r\n",
+ prequest, /* "GET" or "POST" */
+ proxy_entry->url_to, /* "/new/path" */
+ urlcopy + strlen(proxy_entry->url_from), /* "SFX" */
+ HTTP_slash /* HTTP/xyz" or "" */
+ );
+ cgi_io_loop_and_exit(proxy_fd, proxy_fd, /*max POST length:*/ INT_MAX);
+ }
}
+#endif
+
+ /* Extract url args if present */
+ g_query = strchr(urlcopy, '?');
+ if (g_query)
+ *g_query++ = '\0';
/* Decode URL escape sequences */
tptr = percent_decode_in_place(urlcopy, /*strict:*/ 1);
send_headers_and_exit(HTTP_NOT_FOUND);
}
-#if ENABLE_FEATURE_HTTPD_PROXY
- proxy_entry = find_proxy_entry(urlcopy);
- if (proxy_entry)
- header_buf = header_ptr = xmalloc(IOBUF_SIZE);
- else
-#endif
- {
- /* (If not proxying,) decode URL escape sequences */
- tptr = percent_decode_in_place(urlcopy, /*strict:*/ 1);
- if (tptr == NULL)
- send_headers_and_exit(HTTP_BAD_REQUEST);
- if (tptr == urlcopy + 1) {
- /* '/' or NUL is encoded */
- send_headers_and_exit(HTTP_NOT_FOUND);
- }
-//should path canonicalization also be conditional on not proxying?
- }
-
/* Canonicalize path */
/* Algorithm stolen from libbb bb_simplify_path(),
* but don't strdup, retain trailing slash, protect root */
bb_error_msg("url:%s", urlcopy);
tptr = urlcopy;
- if_ip_denied_send_HTTP_FORBIDDEN_and_exit();
while ((tptr = strchr(tptr + 1, '/')) != NULL) {
/* have path1/path2 */
*tptr = '\0';
if (is_directory(urlcopy + 1, /*followlinks:*/ 1)) {
/* may have subdir config */
parse_conf(urlcopy + 1, SUBDIR_PARSE);
- if_ip_denied_send_HTTP_FORBIDDEN_and_exit();
+ if_ip_denied_send_HTTP_FORBIDDEN_and_exit(remote_ip);
}
*tptr = '/';
}
+ tptr = urlcopy + 1; /* skip first '/' */
+
+#if ENABLE_FEATURE_HTTPD_CGI
+ if (is_prefixed_with(tptr, "cgi-bin/")) {
+ if (tptr[8] == '\0') {
+ /* protect listing "cgi-bin/" */
+ send_headers_and_exit(HTTP_FORBIDDEN);
+ }
+ cgi_type = CGI_NORMAL;
+ }
+#endif
+
+ if (urlp[-1] == '/') {
+ /* When index_page string is appended to <dir>/ URL, it overwrites
+ * the query string. If we fall back to call /cgi-bin/index.cgi,
+ * query string would be lost and not available to the CGI.
+ * Work around it by making a deep copy.
+ */
+ if (ENABLE_FEATURE_HTTPD_CGI)
+ g_query = xstrdup(g_query); /* ok for NULL too */
+ strcpy(urlp, index_page);
+ }
+ if (stat(tptr, &sb) == 0) {
+#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
+ char *suffix = strrchr(tptr, '.');
+ if (suffix) {
+ Htaccess *cur;
+ for (cur = script_i; cur; cur = cur->next) {
+ if (strcmp(cur->before_colon + 1, suffix) == 0) {
+ cgi_type = CGI_INTERPRETER;
+ break;
+ }
+ }
+ }
+#endif
+ if (!found_moved_temporarily) {
+ file_size = sb.st_size;
+ last_mod = sb.st_mtime;
+ }
+ }
+#if ENABLE_FEATURE_HTTPD_CGI
+ else if (urlp[-1] == '/') {
+ /* It's a dir URL and there is no index.html
+ * Try cgi-bin/index.cgi */
+ if (access("/cgi-bin/index.cgi"+1, X_OK) == 0) {
+ cgi_type = CGI_INDEX;
+ }
+ }
+#endif
+ urlp[0] = '\0';
+
+#if ENABLE_FEATURE_HTTPD_CGI
+ total_headers_len = 0;
+#endif
if (http_major_version >= '0') {
/* Request was with "... HTTP/nXXX", and n >= 0 */
/* Read until blank line */
while (1) {
- if (!get_line())
+ unsigned iobuf_len = get_line();
+ if (!iobuf_len)
break; /* EOF or error or empty line */
+#if ENABLE_FEATURE_HTTPD_CGI
+ /* Prevent unlimited growth of HTTP_xyz envvars */
+ total_headers_len += iobuf_len;
+ if (total_headers_len >= MAX_HTTP_HEADERS_SIZE)
+ send_headers_and_exit(HTTP_ENTITY_TOO_LARGE);
+#endif
if (DEBUG)
bb_error_msg("header: '%s'", iobuf);
-
-#if ENABLE_FEATURE_HTTPD_PROXY
- if (proxy_entry) {
- /* Why 4, not 2?
- * We need 2 more bytes for yet another "\r\n" -
- * see near fdprintf(proxy_fd...) further below.
- */
- int maxlen = (IOBUF_SIZE-4) - (int)(header_ptr - header_buf);
- if (maxlen > 0) {
- int len = strnlen(iobuf, maxlen);
- memcpy(header_ptr, iobuf, len);
- header_ptr += len;
- header_ptr[0] = '\r';
- header_ptr[1] = '\n';
- header_ptr += 2;
- }
- }
-#endif
-
#if ENABLE_FEATURE_HTTPD_CGI || ENABLE_FEATURE_HTTPD_PROXY
/* Try and do our best to parse more lines */
- if ((STRNCASECMP(iobuf, "Content-Length:") == 0)) {
+ if (STRNCASECMP(iobuf, "Content-Length:") == 0) {
/* extra read only for POST */
if (prequest != request_GET
# if ENABLE_FEATURE_HTTPD_CGI
if (errno || length > INT_MAX)
send_headers_and_exit(HTTP_BAD_REQUEST);
}
- }
-#endif
-#if ENABLE_FEATURE_HTTPD_CGI
- else if (STRNCASECMP(iobuf, "Cookie:") == 0) {
- if (!cookie) /* in case they send millions of these, do not OOM */
- cookie = xstrdup(skip_whitespace(iobuf + sizeof("Cookie:")-1));
- } else if (STRNCASECMP(iobuf, "Content-Type:") == 0) {
- if (!content_type)
- content_type = xstrdup(skip_whitespace(iobuf + sizeof("Content-Type:")-1));
- } else if (STRNCASECMP(iobuf, "Referer:") == 0) {
- if (!G.referer)
- G.referer = xstrdup(skip_whitespace(iobuf + sizeof("Referer:")-1));
- } else if (STRNCASECMP(iobuf, "User-Agent:") == 0) {
- if (!G.user_agent)
- G.user_agent = xstrdup(skip_whitespace(iobuf + sizeof("User-Agent:")-1));
- } else if (STRNCASECMP(iobuf, "Host:") == 0) {
- if (!G.host)
- G.host = xstrdup(skip_whitespace(iobuf + sizeof("Host:")-1));
- } else if (STRNCASECMP(iobuf, "Accept:") == 0) {
- if (!G.http_accept)
- G.http_accept = xstrdup(skip_whitespace(iobuf + sizeof("Accept:")-1));
- } else if (STRNCASECMP(iobuf, "Accept-Language:") == 0) {
- if (!G.http_accept_language)
- G.http_accept_language = xstrdup(skip_whitespace(iobuf + sizeof("Accept-Language:")-1));
+ continue;
}
#endif
#if ENABLE_FEATURE_HTTPD_BASIC_AUTH
* "<user>:<passwd>" is base64 encoded.
*/
tptr = skip_whitespace(iobuf + sizeof("Authorization:")-1);
- if (STRNCASECMP(tptr, "Basic") != 0)
+ if (STRNCASECMP(tptr, "Basic") == 0) {
+ tptr += sizeof("Basic")-1;
+ /* decodeBase64() skips whitespace itself */
+ decodeBase64(tptr);
+ authorized = check_user_passwd(urlcopy, tptr);
continue;
- tptr += sizeof("Basic")-1;
- /* decodeBase64() skips whitespace itself */
- decodeBase64(tptr);
- authorized = check_user_passwd(urlcopy, tptr);
+ }
}
#endif
#if ENABLE_FEATURE_HTTPD_RANGES
range_start = -1;
}
}
+ continue;
}
#endif
#if ENABLE_FEATURE_HTTPD_GZIP
content_gzip = 1;
//}
}
+ continue;
+ }
+#endif
+#if ENABLE_FEATURE_HTTPD_CGI
+ if (cgi_type != CGI_NONE) {
+ bool ct = (STRNCASECMP(iobuf, "Content-Type:") == 0);
+ char *cp;
+ char *colon = strchr(iobuf, ':');
+
+ if (!colon)
+ continue;
+ cp = iobuf;
+ while (cp < colon) {
+ /* a-z => A-Z, not-alnum => _ */
+ char c = (*cp & ~0x20); /* toupper for A-Za-z, undef for others */
+ if ((unsigned)(c - 'A') <= ('Z' - 'A')) {
+ *cp++ = c;
+ continue;
+ }
+ if (!isdigit(*cp))
+ *cp = '_';
+ cp++;
+ }
+ /* "Content-Type:" gets no HTTP_ prefix, all others do */
+ cp = xasprintf(ct ? "HTTP_%.*s=%s" + 5 : "HTTP_%.*s=%s",
+ (int)(colon - iobuf), iobuf,
+ skip_whitespace(colon + 1)
+ );
+ putenv(cp);
}
#endif
} /* while extra header reading */
send_headers_and_exit(HTTP_MOVED_TEMPORARILY);
}
-#if ENABLE_FEATURE_HTTPD_PROXY
- if (proxy_entry) {
- int proxy_fd;
- len_and_sockaddr *lsa;
-
- lsa = host2sockaddr(proxy_entry->host_port, 80);
- if (lsa == NULL)
- send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
- proxy_fd = socket(lsa->u.sa.sa_family, SOCK_STREAM, 0);
- if (proxy_fd < 0)
- send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
- if (connect(proxy_fd, &lsa->u.sa, lsa->len) < 0)
- send_headers_and_exit(HTTP_INTERNAL_SERVER_ERROR);
- fdprintf(proxy_fd, "%s %s%s%s%s HTTP/%c.%c\r\n",
- prequest, /* GET or POST */
- proxy_entry->url_to, /* url part 1 */
- urlcopy + strlen(proxy_entry->url_from), /* url part 2 */
- (g_query ? "?" : ""), /* "?" (maybe) */
- (g_query ? g_query : ""), /* query string (maybe) */
- http_major_version, http_minor_version);
- header_ptr[0] = '\r';
- header_ptr[1] = '\n';
- header_ptr += 2;
- full_write(proxy_fd, header_buf, header_ptr - header_buf);
- free(header_buf); /* on the order of 8k, free it */
- cgi_io_loop_and_exit(proxy_fd, proxy_fd, length);
- }
-#endif
-
tptr = urlcopy + 1; /* skip first '/' */
#if ENABLE_FEATURE_HTTPD_CGI
- if (is_prefixed_with(tptr, "cgi-bin/")) {
- if (tptr[8] == '\0') {
- /* protect listing "cgi-bin/" */
- send_headers_and_exit(HTTP_FORBIDDEN);
- }
- send_cgi_and_exit(urlcopy, urlcopy, prequest, length, cookie, content_type);
+ if (cgi_type != CGI_NONE) {
+ send_cgi_and_exit(
+ (cgi_type == CGI_INDEX) ? "/cgi-bin/index.cgi"
+ /*CGI_NORMAL or CGI_INTERPRETER*/ : urlcopy,
+ urlcopy, prequest, length
+ );
}
#endif
if (urlp[-1] == '/') {
- /* When index_page string is appended to <dir>/ URL, it overwrites
- * the query string. If we fall back to call /cgi-bin/index.cgi,
- * query string would be lost and not available to the CGI.
- * Work around it by making a deep copy.
- */
- if (ENABLE_FEATURE_HTTPD_CGI)
- g_query = xstrdup(g_query); /* ok for NULL too */
strcpy(urlp, index_page);
}
- if (stat(tptr, &sb) == 0) {
-#if ENABLE_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR
- char *suffix = strrchr(tptr, '.');
- if (suffix) {
- Htaccess *cur;
- for (cur = script_i; cur; cur = cur->next) {
- if (strcmp(cur->before_colon + 1, suffix) == 0) {
- send_cgi_and_exit(urlcopy, urlcopy, prequest, length, cookie, content_type);
- }
- }
- }
-#endif
- file_size = sb.st_size;
- last_mod = sb.st_mtime;
- }
-#if ENABLE_FEATURE_HTTPD_CGI
- else if (urlp[-1] == '/') {
- /* It's a dir URL and there is no index.html
- * Try cgi-bin/index.cgi */
- if (access("/cgi-bin/index.cgi"+1, X_OK) == 0) {
- urlp[0] = '\0'; /* remove index_page */
- send_cgi_and_exit("/cgi-bin/index.cgi", urlcopy, prequest, length, cookie, content_type);
- }
- }
- /* else fall through to send_file, it errors out if open fails: */
+#if ENABLE_FEATURE_HTTPD_CGI
if (prequest != request_GET && prequest != request_HEAD) {
/* POST for files does not make sense */
send_headers_and_exit(HTTP_NOT_IMPLEMENTED);