You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
629 lines
14 KiB
C
629 lines
14 KiB
C
10 years ago
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include <unistd.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <errno.h>
|
||
|
#include <syslog.h>
|
||
|
#include <time.h>
|
||
|
#include <ctype.h>
|
||
|
#include <pwd.h>
|
||
|
#include <sys/time.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <sys/socket.h>
|
||
|
#include <netinet/in.h>
|
||
|
|
||
|
#include "httpd.h"
|
||
|
|
||
|
/* ---------------------------------------------------------------------- */
|
||
|
|
||
|
void
|
||
|
read_request(struct REQUEST *req, int pipelined)
|
||
|
{
|
||
|
int rc;
|
||
|
char *h;
|
||
|
|
||
|
restart:
|
||
|
#ifdef USE_SSL
|
||
|
if (with_ssl)
|
||
|
rc = ssl_read(req, req->hreq + req->hdata, MAX_HEADER - req->hdata);
|
||
|
else
|
||
|
#endif
|
||
|
rc = read(req->fd, req->hreq + req->hdata, MAX_HEADER - req->hdata);
|
||
|
switch (rc) {
|
||
|
case -1:
|
||
|
if (errno == EAGAIN) {
|
||
|
if (pipelined)
|
||
|
break; /* check if there is already a full request */
|
||
|
else
|
||
|
return;
|
||
|
}
|
||
|
if (errno == EINTR)
|
||
|
goto restart;
|
||
|
xperror(LOG_INFO,"read",req->peerhost);
|
||
|
/* fall through */
|
||
|
case 0:
|
||
|
req->state = STATE_CLOSE;
|
||
|
return;
|
||
|
default:
|
||
|
req->hdata += rc;
|
||
|
req->hreq[req->hdata] = 0;
|
||
|
}
|
||
|
|
||
|
/* check if this looks like a http request after
|
||
|
the first few bytes... */
|
||
|
if (req->hdata < 5)
|
||
|
return;
|
||
|
if (strncmp(req->hreq,"GET ",4) != 0 &&
|
||
|
strncmp(req->hreq,"PUT ",4) != 0 &&
|
||
|
strncmp(req->hreq,"HEAD ",5) != 0 &&
|
||
|
strncmp(req->hreq,"POST ",5) != 0) {
|
||
|
mkerror(req,400,0);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* header complete ?? */
|
||
|
if (NULL != (h = strstr(req->hreq,"\r\n\r\n")) ||
|
||
|
NULL != (h = strstr(req->hreq,"\n\n"))) {
|
||
|
if (*h == '\r') {
|
||
|
h += 4;
|
||
|
*(h-2) = 0;
|
||
|
} else {
|
||
|
h += 2;
|
||
|
*(h-1) = 0;
|
||
|
}
|
||
|
req->lreq = h - req->hreq;
|
||
|
req->state = STATE_PARSE_HEADER;
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (req->hdata == MAX_HEADER) {
|
||
|
/* oops: buffer full, but found no complete request ... */
|
||
|
mkerror(req,400,0);
|
||
|
return;
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* ---------------------------------------------------------------------- */
|
||
|
|
||
|
#if 0
|
||
|
static time_t
|
||
|
parse_date(char *line)
|
||
|
{
|
||
|
static char *m[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
||
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
|
||
|
char month[4];
|
||
|
struct tm tm;
|
||
|
int i;
|
||
|
|
||
|
line = strchr(line,' '); /* skip weekday */
|
||
|
if (NULL == line)
|
||
|
return -1;
|
||
|
line++;
|
||
|
|
||
|
/* first: RFC 1123 date ... */
|
||
|
if (6 != sscanf(line,"%2d %3s %4d %2d:%2d:%2d GMT",
|
||
|
&tm.tm_mday,month,&tm.tm_year,
|
||
|
&tm.tm_hour,&tm.tm_min,&tm.tm_sec))
|
||
|
/* second: RFC 1036 date ... */
|
||
|
if (6 != sscanf(line,"%2d-%3s-%2d %2d:%2d:%2d GMT",
|
||
|
&tm.tm_mday,month,&tm.tm_year,
|
||
|
&tm.tm_hour,&tm.tm_min,&tm.tm_sec))
|
||
|
/* third: asctime() format */
|
||
|
if (6 != sscanf(line,"%3s %2d %2d:%2d:%2d %4d",
|
||
|
month,&tm.tm_mday,
|
||
|
&tm.tm_hour,&tm.tm_min,&tm.tm_sec,
|
||
|
&tm.tm_year))
|
||
|
/* none worked :-( */
|
||
|
return -1;
|
||
|
for (i = 0; i <= 11; i++)
|
||
|
if (0 == strcmp(month,m[i]))
|
||
|
break;
|
||
|
tm.tm_mon = i;
|
||
|
if (tm.tm_year > 1900)
|
||
|
tm.tm_year -= 1900;
|
||
|
|
||
|
return mktime(&tm);
|
||
|
}
|
||
|
#endif
|
||
|
|
||
|
static off_t
|
||
|
parse_off_t(char *str, int *pos)
|
||
|
{
|
||
|
off_t value = 0;
|
||
|
|
||
|
while (isdigit(str[*pos])) {
|
||
|
value *= 10;
|
||
|
value += str[*pos] - '0';
|
||
|
(*pos)++;
|
||
|
}
|
||
|
return value;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
parse_ranges(struct REQUEST *req)
|
||
|
{
|
||
|
char *h,*line = req->range_hdr;
|
||
|
int i,off;
|
||
|
|
||
|
for (h = line, req->ranges=1; *h != '\n' && *h != '\0'; h++)
|
||
|
if (*h == ',')
|
||
|
req->ranges++;
|
||
|
if (debug)
|
||
|
fprintf(stderr,"%03d: %d ranges:",req->fd,req->ranges);
|
||
|
req->r_start = malloc(req->ranges*sizeof(off_t));
|
||
|
req->r_end = malloc(req->ranges*sizeof(off_t));
|
||
|
req->r_head = malloc((req->ranges+1)*BR_HEADER);
|
||
|
req->r_hlen = malloc((req->ranges+1)*sizeof(int));
|
||
|
if (NULL == req->r_start || NULL == req->r_end ||
|
||
|
NULL == req->r_head || NULL == req->r_hlen) {
|
||
|
if (req->r_start) free(req->r_start);
|
||
|
if (req->r_end) free(req->r_end);
|
||
|
if (req->r_head) free(req->r_head);
|
||
|
if (req->r_hlen) free(req->r_hlen);
|
||
|
if (debug)
|
||
|
fprintf(stderr,"oom\n");
|
||
|
return 500;
|
||
|
}
|
||
|
for (i = 0, off=0; i < req->ranges; i++) {
|
||
|
if (line[off] == '-') {
|
||
|
off++;
|
||
|
if (!isdigit(line[off]))
|
||
|
goto parse_error;
|
||
|
req->r_start[i] = req->bst.st_size - parse_off_t(line,&off);
|
||
|
req->r_end[i] = req->bst.st_size;
|
||
|
} else {
|
||
|
if (!isdigit(line[off]))
|
||
|
goto parse_error;
|
||
|
req->r_start[i] = parse_off_t(line,&off);
|
||
|
if (line[off] != '-')
|
||
|
goto parse_error;
|
||
|
off++;
|
||
|
if (isdigit(line[off]))
|
||
|
req->r_end[i] = parse_off_t(line,&off) +1;
|
||
|
else
|
||
|
req->r_end[i] = req->bst.st_size;
|
||
|
}
|
||
|
off++; /* skip "," */
|
||
|
/* ranges ok? */
|
||
|
if (debug)
|
||
|
fprintf(stderr," %d-%d",
|
||
|
(int)(req->r_start[i]),
|
||
|
(int)(req->r_end[i]));
|
||
|
if (req->r_start[i] > req->r_end[i] ||
|
||
|
req->r_end[i] > req->bst.st_size)
|
||
|
goto parse_error;
|
||
|
}
|
||
|
if (debug)
|
||
|
fprintf(stderr," ok\n");
|
||
|
return 0;
|
||
|
|
||
|
parse_error:
|
||
|
req->ranges = 0;
|
||
|
if (debug)
|
||
|
fprintf(stderr," range error\n");
|
||
|
return 400;
|
||
|
}
|
||
|
|
||
|
static int
|
||
|
unhex(unsigned char c)
|
||
|
{
|
||
|
if (c < '@')
|
||
|
return c - '0';
|
||
|
return (c & 0x0f) + 9;
|
||
|
}
|
||
|
|
||
|
/* handle %hex quoting, also split path / querystring */
|
||
|
static void
|
||
|
unquote(unsigned char *path, unsigned char *qs, unsigned char *src)
|
||
|
{
|
||
|
int q;
|
||
|
unsigned char *dst;
|
||
|
|
||
|
q=0;
|
||
|
dst = path;
|
||
|
while (src[0] != 0) {
|
||
|
if (!q && *src == '?') {
|
||
|
q = 1;
|
||
|
*dst = 0;
|
||
|
dst = qs;
|
||
|
src++;
|
||
|
continue;
|
||
|
}
|
||
|
if (q && *src == '+') {
|
||
|
*dst = ' ';
|
||
|
} else if ((*src == '%') && isxdigit(src[1]) && isxdigit(src[2])) {
|
||
|
*dst = (unhex(src[1]) << 4) | unhex(src[2]);
|
||
|
src += 2;
|
||
|
} else {
|
||
|
*dst = *src;
|
||
|
}
|
||
|
dst++;
|
||
|
src++;
|
||
|
}
|
||
|
*dst = 0;
|
||
|
}
|
||
|
|
||
|
/* delete unneeded path elements */
|
||
|
static void
|
||
|
fixpath(char *path)
|
||
|
{
|
||
|
char *dst = path;
|
||
|
char *src = path;
|
||
|
|
||
|
for (;*src;) {
|
||
|
if (0 == strncmp(src,"//",2)) {
|
||
|
src++;
|
||
|
continue;
|
||
|
}
|
||
|
if (0 == strncmp(src,"/./",3)) {
|
||
|
src+=2;
|
||
|
continue;
|
||
|
}
|
||
|
*(dst++) = *(src++);
|
||
|
}
|
||
|
*dst = 0;
|
||
|
}
|
||
|
|
||
|
static int base64_table[] = {
|
||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
|
||
|
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
|
||
|
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
|
||
|
-1, 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, -1, -1, -1, -1, -1,
|
||
|
-1, 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, -1, -1, -1, -1, -1,
|
||
|
};
|
||
|
|
||
|
static void
|
||
|
decode_base64(unsigned char *dest, unsigned char *src, int maxlen)
|
||
|
{
|
||
|
int a,b,d;
|
||
|
|
||
|
for (a=0, b=0, d=0; *src != 0 && d < maxlen; src++) {
|
||
|
if (*src >= 128 || -1 == base64_table[*src])
|
||
|
break;
|
||
|
a = (a<<6) | base64_table[*src];
|
||
|
b += 6;
|
||
|
if (b >= 8) {
|
||
|
b -= 8;
|
||
|
dest[d++] = (a >> b) & 0xff;
|
||
|
}
|
||
|
}
|
||
|
dest[d] = 0;
|
||
|
}
|
||
|
|
||
|
static int sanity_checks(struct REQUEST *req)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
/* path: must start with a '/' */
|
||
|
if (req->path[0] != '/') {
|
||
|
mkerror(req,400,0);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
/* path: must not contain "/../" */
|
||
|
if (strstr(req->path,"/../")) {
|
||
|
mkerror(req,403,1);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
if (req->hostname[0] == '\0')
|
||
|
/* no hostname specified */
|
||
|
return 0;
|
||
|
|
||
|
/* validate hostname */
|
||
|
for (i = 0; req->hostname[i] != '\0'; i++) {
|
||
|
switch (req->hostname[i]) {
|
||
|
case 'A' ... 'Z':
|
||
|
req->hostname[i] += 32; /* lowercase */
|
||
|
case 'a' ... 'z':
|
||
|
case '0' ... '9':
|
||
|
case '-':
|
||
|
/* these are fine as-is */
|
||
|
break;
|
||
|
case '.':
|
||
|
/* some extra checks */
|
||
|
if (0 == i) {
|
||
|
/* don't allow a dot as first character */
|
||
|
mkerror(req,400,0);
|
||
|
return -1;
|
||
|
}
|
||
|
if ('.' == req->hostname[i-1]) {
|
||
|
/* don't allow two dots in sequence */
|
||
|
mkerror(req,400,0);
|
||
|
return -1;
|
||
|
}
|
||
|
break;
|
||
|
default:
|
||
|
/* invalid character */
|
||
|
mkerror(req,400,0);
|
||
|
return -1;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
void
|
||
|
parse_request(struct REQUEST *req)
|
||
|
{
|
||
|
char filename[MAX_PATH+1], proto[MAX_MISC+1], *h;
|
||
|
int port, rc, len;
|
||
|
struct passwd *pw=NULL;
|
||
|
|
||
|
if (debug > 2)
|
||
|
fprintf(stderr,"%s\n",req->hreq);
|
||
|
|
||
|
/* parse request. Hehe, scanf is powerfull :-) */
|
||
|
if (4 != sscanf(req->hreq,
|
||
|
"%" S(MAX_MISC) "[A-Z] "
|
||
|
"%" S(MAX_PATH) "[^ \t\r\n] HTTP/%d.%d",
|
||
|
req->type, filename, &(req->major),&(req->minor))) {
|
||
|
mkerror(req,400,0);
|
||
|
return;
|
||
|
}
|
||
|
if (filename[0] == '/') {
|
||
|
strncpy(req->uri,filename,sizeof(req->uri)-1);
|
||
|
} else {
|
||
|
port = 0;
|
||
|
*proto = 0;
|
||
|
if (4 != sscanf(filename,
|
||
|
"%" S(MAX_MISC) "[a-zA-Z]://"
|
||
|
"%" S(MAX_HOST) "[a-zA-Z0-9.-]:%d"
|
||
|
"%" S(MAX_PATH) "[^ \t\r\n]",
|
||
|
proto, req->hostname, &port, req->uri) &&
|
||
|
3 != sscanf(filename,
|
||
|
"%" S(MAX_MISC) "[a-zA-Z]://"
|
||
|
"%" S(MAX_HOST) "[a-zA-Z0-9.-]"
|
||
|
"%" S(MAX_PATH) "[^ \t\r\n]",
|
||
|
proto, req->hostname, req->uri)) {
|
||
|
mkerror(req,400,0);
|
||
|
return;
|
||
|
}
|
||
|
if (*proto != 0 && 0 != strcasecmp(proto,"http")) {
|
||
|
mkerror(req,400,0);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
unquote(req->path,req->query,req->uri);
|
||
|
fixpath(req->path);
|
||
|
if (debug)
|
||
|
fprintf(stderr,"%03d: %s \"%s\" HTTP/%d.%d\n",
|
||
|
req->fd, req->type, req->path, req->major, req->minor);
|
||
|
|
||
|
if (0 != strcmp(req->type,"GET") &&
|
||
|
0 != strcmp(req->type,"HEAD")) {
|
||
|
mkerror(req,501,0);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (0 == strcmp(req->type,"HEAD")) {
|
||
|
req->head_only = 1;
|
||
|
}
|
||
|
|
||
|
/* parse header lines */
|
||
|
req->keep_alive = req->minor;
|
||
|
for (h = req->hreq; h - req->hreq < req->lreq;) {
|
||
|
h = strchr(h,'\n');
|
||
|
if (NULL == h)
|
||
|
break;
|
||
|
h++;
|
||
|
|
||
|
h[-2] = 0;
|
||
|
h[-1] = 0;
|
||
|
list_add(&req->header,h,0);
|
||
|
|
||
|
if (0 == strncasecmp(h,"Connection: ",12)) {
|
||
|
req->keep_alive = (0 == strncasecmp(h+12,"Keep-Alive",10));
|
||
|
|
||
|
} else if (0 == strncasecmp(h,"Host: ",6)) {
|
||
|
if (2 != sscanf(h+6,"%" S(MAX_HOST) "[a-zA-Z0-9.-]:%d",
|
||
|
req->hostname,&port))
|
||
|
sscanf(h+6,"%" S(MAX_HOST) "[a-zA-Z0-9.-]",
|
||
|
req->hostname);
|
||
|
|
||
|
} else if (0 == strncasecmp(h,"If-Modified-Since: ",19)) {
|
||
|
req->if_modified = h+19;
|
||
|
|
||
|
} else if (0 == strncasecmp(h,"If-Unmodified-Since: ",21)) {
|
||
|
req->if_unmodified = h+21;
|
||
|
|
||
|
} else if (0 == strncasecmp(h,"If-Range: ",10)) {
|
||
|
req->if_range = h+10;
|
||
|
|
||
|
} else if (0 == strncasecmp(h,"Authorization: Basic ",21)) {
|
||
|
decode_base64(req->auth,h+21,sizeof(req->auth)-1);
|
||
|
if (debug)
|
||
|
fprintf(stderr,"%03d: auth: %s\n",req->fd,req->auth);
|
||
|
|
||
|
} else if (0 == strncasecmp(h,"Range: bytes=",13)) {
|
||
|
/* parsing must be done after fstat, we need the file size
|
||
|
for the boundary checks */
|
||
|
req->range_hdr = h+13;
|
||
|
}
|
||
|
}
|
||
|
if (debug) {
|
||
|
if (req->if_modified)
|
||
|
fprintf(stderr,"%03d: if-modified-since: \"%s\"\n",
|
||
|
req->fd, req->if_modified);
|
||
|
if (req->if_unmodified)
|
||
|
fprintf(stderr,"%03d: if-unmodified-since: \"%s\"\n",
|
||
|
req->fd, req->if_unmodified);
|
||
|
if (req->if_range)
|
||
|
fprintf(stderr,"%03d: if-range: \"%s\"\n",
|
||
|
req->fd, req->if_range);
|
||
|
}
|
||
|
|
||
|
/* take care about the hostname */
|
||
|
if (virtualhosts) {
|
||
|
if (req->hostname[0] == 0) {
|
||
|
if (req->minor > 0) {
|
||
|
/* HTTP/1.1 clients MUST specify a hostname */
|
||
|
mkerror(req,400,0);
|
||
|
return;
|
||
|
}
|
||
|
strncpy(req->hostname,server_host,sizeof(req->hostname)-1);
|
||
|
}
|
||
|
} else {
|
||
|
if (req->hostname[0] == '\0' || canonicalhost)
|
||
|
strncpy(req->hostname,server_host,sizeof(req->hostname)-1);
|
||
|
}
|
||
|
|
||
|
/* checks */
|
||
|
if (0 != sanity_checks(req))
|
||
|
return;
|
||
|
|
||
|
/* check basic auth */
|
||
|
if (NULL != userpass && 0 != strcmp(userpass,req->auth)) {
|
||
|
mkerror(req,401,1);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* is CGI ? */
|
||
|
if (NULL != cgipath &&
|
||
|
0 == strncmp(req->path,cgipath,strlen(cgipath))) {
|
||
|
cgi_request(req);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* build filename */
|
||
|
if (userdir && '~' == req->path[1]) {
|
||
|
/* expand user directories, i.e.
|
||
|
/~user/path/file => $HOME/public_html/path/file */
|
||
|
h = strchr(req->path+2,'/');
|
||
|
if (NULL == h) {
|
||
|
mkerror(req,404,1);
|
||
|
return;
|
||
|
}
|
||
|
*h = 0;
|
||
|
pw = getpwnam(req->path+2);
|
||
|
*h = '/';
|
||
|
if (NULL == pw) {
|
||
|
mkerror(req,404,1);
|
||
|
return;
|
||
|
}
|
||
|
len = snprintf(filename, sizeof(filename)-1,
|
||
|
"%s/%s/%s", pw->pw_dir, userdir, h+1);
|
||
|
} else {
|
||
|
len = snprintf(filename, sizeof(filename)-1,
|
||
|
"%s%s%s%s",
|
||
|
do_chroot ? "" : doc_root,
|
||
|
virtualhosts ? "/" : "",
|
||
|
virtualhosts ? req->hostname : "",
|
||
|
req->path);
|
||
|
}
|
||
|
|
||
|
h = filename +len -1;
|
||
|
if (*h == '/') {
|
||
|
/* looks like the client asks for a directory */
|
||
|
if (indexhtml) {
|
||
|
/* check for index file */
|
||
|
strncpy(h+1, indexhtml, sizeof(filename) -len -1);
|
||
|
if (-1 != (req->bfd = open(filename,O_RDONLY))) {
|
||
|
/* ok, we have one */
|
||
|
close_on_exec(req->bfd);
|
||
|
goto regular_file;
|
||
|
} else {
|
||
|
if (errno == ENOENT) {
|
||
|
/* no such file or directory => listing */
|
||
|
h[1] = '\0';
|
||
|
} else {
|
||
|
mkerror(req,403,1);
|
||
|
return;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (no_listing) {
|
||
|
mkerror(req,403,1);
|
||
|
return;
|
||
|
};
|
||
|
|
||
|
if (-1 == stat(filename,&(req->bst))) {
|
||
|
if (errno == EACCES) {
|
||
|
mkerror(req,403,1);
|
||
|
} else {
|
||
|
mkerror(req,404,1);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
strftime(req->mtime, sizeof(req->mtime), RFC1123, gmtime(&req->bst.st_mtime));
|
||
|
req->mime = "text/html";
|
||
|
req->dir = get_dir(req,filename);
|
||
|
if (NULL == req->body) {
|
||
|
/* We arrive here if opendir failed, probably due to -EPERM
|
||
|
* It does exist (see the stat() call above) */
|
||
|
mkerror(req,403,1);
|
||
|
return;
|
||
|
} else if (NULL != req->if_modified &&
|
||
|
0 == strcmp(req->if_modified, req->mtime)) {
|
||
|
/* 304 not modified */
|
||
|
mkheader(req,304);
|
||
|
req->head_only = 1;
|
||
|
} else {
|
||
|
/* 200 OK */
|
||
|
mkheader(req,200);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* it is /probably/ a regular file */
|
||
|
if (-1 == (req->bfd = open(filename,O_RDONLY))) {
|
||
|
if (errno == EACCES) {
|
||
|
mkerror(req,403,1);
|
||
|
} else {
|
||
|
mkerror(req,404,1);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
regular_file:
|
||
|
fstat(req->bfd,&(req->bst));
|
||
|
if (req->range_hdr)
|
||
|
if (0 != (rc = parse_ranges(req))) {
|
||
|
mkerror(req,rc,1);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (!S_ISREG(req->bst.st_mode)) {
|
||
|
/* /not/ a regular file */
|
||
|
close(req->bfd);
|
||
|
req->bfd = -1;
|
||
|
if (S_ISDIR(req->bst.st_mode)) {
|
||
|
/* oops: a directory without trailing slash */
|
||
|
strcat(req->path,"/");
|
||
|
mkredirect(req);
|
||
|
} else {
|
||
|
/* anything else is'nt allowed here */
|
||
|
mkerror(req,403,1);
|
||
|
}
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
/* it is /really/ a regular file */
|
||
|
req->mime = get_mime(filename);
|
||
|
strftime(req->mtime, sizeof(req->mtime), RFC1123, gmtime(&req->bst.st_mtime));
|
||
|
if (NULL != req->if_range && 0 != strcmp(req->if_range, req->mtime))
|
||
|
/* mtime mismatch -> no ranges */
|
||
|
req->ranges = 0;
|
||
|
if (NULL != req->if_unmodified && 0 != strcmp(req->if_unmodified, req->mtime)) {
|
||
|
/* 412 precondition failed */
|
||
|
mkerror(req,412,1);
|
||
|
} else if (NULL != req->if_modified && 0 == strcmp(req->if_modified, req->mtime)) {
|
||
|
/* 304 not modified */
|
||
|
mkheader(req,304);
|
||
|
req->head_only = 1;
|
||
|
} else if (req->ranges > 0) {
|
||
|
/* send byte range(s) */
|
||
|
mkheader(req,206);
|
||
|
} else {
|
||
|
/* normal */
|
||
|
mkheader(req,200);
|
||
|
}
|
||
|
return;
|
||
|
}
|