#include <stdio.h>
#include <stdlib.h>
#include "http.h"
#include "protocol.h"
#include "../util/pblock.h"
#include "../util/pool.h"
#include "../util/util.h"
static int http_etag =
1;
NSAPI_PUBLIC void http_format_etag(Session *sn, Request *rq,
char *etagp,
int etaglen,
off_t size,
time_t mtime)
{
if (rq->req_start - mtime <=
1) {
*etagp++ =
'W';
*etagp++ =
'/';
etaglen -=
2;
}
util_snprintf(etagp, etaglen,
"\"%x-%x\"", (
int)size, (
int)mtime);
}
NSAPI_PUBLIC void http_weaken_etag(Session *sn, Request *rq)
{
pb_param *pp = pblock_findkey(pb_key_etag, rq->srvhdrs);
if (pp) {
if (pp->value[
0] !=
'W' || pp->value[
1] !=
'/') {
char *weak = (
char *) pool_malloc(sn->pool,
2 + strlen(pp->value) +
1);
weak[
0] =
'W';
weak[
1] =
'/';
strcpy(weak +
2, pp->value);
pool_free(sn->pool, pp->value);
pp->value = weak;
}
}
}
NSAPI_PUBLIC int http_match_etag(
const char *header,
const char *etag,
int strong)
{
if (!etag)
return 0;
if (header[
0] ==
'*')
return 1;
if (etag[
0] ==
'W' && etag[
1] ==
'/') {
if (strong)
return 0;
etag +=
2;
}
const char *found = strstr(header, etag);
if (!found)
return 0;
if (strong && found >= header +
2 && found[-
2] ==
'W' && found[-
1] ==
'/')
return 0;
return 1;
}
NSAPI_PUBLIC int http_check_preconditions(Session *sn, Request *rq,
struct tm *mtm,
const char *etag)
{
char *header;
header = pblock_findkeyval(pb_key_if_modified_since, rq->headers);
if (header) {
if (mtm && util_later_than(mtm, header)) {
protocol_status(sn, rq,
PROTOCOL_NOT_MODIFIED,
NULL);
return REQ_ABORTED;
}
}
header = pblock_findkeyval(pb_key_if_unmodified_since, rq->headers);
if (header) {
if (mtm && !util_later_than(mtm, header)) {
if(util_isdate(header)) {
protocol_status(sn, rq,
PROTOCOL_PRECONDITION_FAIL,
NULL);
return REQ_ABORTED;
}
}
}
header = pblock_findkeyval(pb_key_if_none_match, rq->headers);
if (header) {
if (
ISMGET(rq) ||
ISMHEAD(rq)) {
if (http_match_etag(header, etag,
PR_FALSE)) {
protocol_status(sn, rq,
PROTOCOL_NOT_MODIFIED,
NULL);
return REQ_ABORTED;
}
}
else {
if (http_match_etag(header, etag,
PR_TRUE)) {
protocol_status(sn, rq,
PROTOCOL_PRECONDITION_FAIL,
NULL);
return REQ_ABORTED;
}
}
}
header = pblock_findkeyval(pb_key_if_match, rq->headers);
if (header) {
if (!http_match_etag(header, etag,
PR_TRUE)) {
protocol_status(sn, rq,
PROTOCOL_PRECONDITION_FAIL,
NULL);
return REQ_ABORTED;
}
}
header = pblock_findkeyval(pb_key_if_range, rq->headers);
char *range = pblock_findkeyval(pb_key_range, rq->headers);
if (range && header) {
int rmir =
PR_FALSE;
if (util_isdate(header)) {
if (mtm && !util_later_than(mtm, header)) {
rmir =
PR_TRUE;
}
}
else {
if (!http_match_etag(header, etag,
PR_TRUE)) {
rmir =
PR_TRUE;
}
}
if(rmir) {
pblock_removekey(pb_key_range, rq->headers);
}
}
return REQ_PROCEED;
}
static inline
int set_finfo(Session *sn, Request *rq,
off_t size,
time_t mtime,
const char *etag)
{
struct tm mtms;
struct tm *mtm = system_gmtime(&mtime, &mtms);
pb_param *pp;
if (mtm) {
pp = pblock_key_param_create(rq->srvhdrs, pb_key_last_modified,
NULL,
HTTP_DATE_LEN);
if (!pp || !pp->value)
return REQ_ABORTED;
strftime(pp->value,
HTTP_DATE_LEN,
HTTP_DATE_FMT, mtm);
pblock_kpinsert(pb_key_last_modified, pp, rq->srvhdrs);
}
const int content_length_size =
21;
pp = pblock_key_param_create(rq->srvhdrs, pb_key_content_length,
NULL,
content_length_size);
if (!pp || !pp->value)
return REQ_ABORTED;
snprintf(pp->value, content_length_size,
"%lld", (
long long)size);
pblock_kpinsert(pb_key_content_length, pp, rq->srvhdrs);
if (http_etag) {
if(etag) {
pblock_kvinsert(pb_key_etag, etag, strlen(etag), rq->srvhdrs);
}
else {
pp = pblock_key_param_create(rq->srvhdrs, pb_key_etag,
NULL,
MAX_ETAG);
if (!pp || !pp->value)
return REQ_ABORTED;
http_format_etag(sn, rq, pp->value,
MAX_ETAG, size, mtime);
pblock_kpinsert(pb_key_etag, pp, rq->srvhdrs);
etag = pp->value;
}
}
else {
etag =
NULL;
}
return http_check_preconditions(sn, rq, mtm, etag);
}
NSAPI_PUBLIC int http_set_finfo(Session *sn, Request *rq,
struct stat *finfo)
{
return set_finfo(sn, rq, finfo->st_size, finfo->st_mtime,
NULL);
}
NSAPI_PUBLIC int http_set_finfo_etag(Session *sn, Request *rq,
struct stat *finfo,
const char *etag) {
return set_finfo(sn, rq, finfo->st_size, finfo->st_mtime, etag);
}
NSAPI_PUBLIC char **http_hdrs2env(pblock *pb)
{
char *t, *n, **env;
struct pb_entry *p;
pb_param *pp;
register int x, y, z;
int pos, ts, ln;
for(x =
0, y =
0; x < pb->hsize; x++) {
p = pb->ht[x];
while(p) {
++y;
p = p->next;
}
}
env = util_env_create(
NULL, y, &pos);
ts =
1024;
t = (
char *)
MALLOC(ts);
for(x =
0; x < pb->hsize; x++) {
p = pb->ht[x];
while(p) {
pp = p->param;
ln = strlen(pp->name) +
6;
if(ln >= ts) {
ts = ln;
t = (
char *)
REALLOC(t, ts);
}
n = pp->name;
if(strcasecmp(n,
"authorization")) {
if(strcasecmp(n,
"content-type") &&
strcasecmp(n,
"content-length"))
{
strncpy(t,
"HTTP_",
5);
z =
5;
}
else
z =
0;
for(y =
0; n[y]; ++y, ++z)
t[z] = (n[y] ==
'-' ?
'_' : toupper(n[y]));
t[z] =
'\0';
env[pos++] = util_env_str(t, pp->value);
}
p = p->next;
}
}
env[pos] =
NULL;
FREE(t);
return env;
}