UNIXworkcode

1 /* 2 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 3 * 4 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 5 * 6 * THE BSD LICENSE 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions are met: 10 * 11 * Redistributions of source code must retain the above copyright notice, this 12 * list of conditions and the following disclaimer. 13 * Redistributions in binary form must reproduce the above copyright notice, 14 * this list of conditions and the following disclaimer in the documentation 15 * and/or other materials provided with the distribution. 16 * 17 * Neither the name of the nor the names of its contributors may be 18 * used to endorse or promote products derived from this software without 19 * specific prior written permission. 20 * 21 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 22 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 23 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 24 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER 25 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 26 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 27 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 28 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 29 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 30 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 31 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 32 */ 33 34 #include <stdio.h> 35 #include <stdlib.h> 36 37 #include "http.h" 38 #include "protocol.h" 39 40 #include "../util/pblock.h" 41 #include "../util/pool.h" 42 #include "../util/util.h" 43 44 45 static int http_etag = 1; 46 47 /* --------------------------- http_format_etag --------------------------- */ 48 49 NSAPI_PUBLIC void http_format_etag(Session *sn, Request *rq, char *etagp, int etaglen, off_t size, time_t mtime) 50 { 51 /* 52 * Heuristic for weak/strong validator: if the request was made within 53 * a second of the last-modified date, then we send a weak Etag. 54 */ 55 if (rq->req_start - mtime <= 1) { 56 /* Send a weak Etag */ 57 *etagp++ = 'W'; 58 *etagp++ = '/'; 59 etaglen -= 2; 60 } 61 62 /* 63 * Create an Etag out of the metadata (size, mtime) to obtain Etag 64 * uniqueness. 65 */ 66 util_snprintf(etagp, etaglen, "\"%x-%x\"", (int)size, (int)mtime); 67 } 68 69 70 /* --------------------------- http_weaken_etag --------------------------- */ 71 72 NSAPI_PUBLIC void http_weaken_etag(Session *sn, Request *rq) 73 { 74 /* Replace any existing strong Etag with a weak Etag */ 75 pb_param *pp = pblock_findkey(pb_key_etag, rq->srvhdrs); 76 if (pp) { 77 if (pp->value[0] != 'W' || pp->value[1] != '/') { 78 char *weak = (char *) pool_malloc(sn->pool, 2 + strlen(pp->value) + 1); 79 80 weak[0] = 'W'; 81 weak[1] = '/'; 82 strcpy(weak + 2, pp->value); 83 84 pool_free(sn->pool, pp->value); 85 pp->value = weak; 86 } 87 } 88 } 89 90 91 /* ------------------------------ match_etag ------------------------------ */ 92 93 NSAPI_PUBLIC int http_match_etag(const char *header, const char *etag, int strong) 94 { 95 if (!etag) 96 return 0; /* mismatch */ 97 98 if (header[0] == '*') 99 return 1; /* match */ 100 101 if (etag[0] == 'W' && etag[1] == '/') { 102 /* Weak Etags never match when using the strong validator */ 103 if (strong) 104 return 0; /* mismatch */ 105 106 etag += 2; 107 } 108 109 /* Look for Etag in header */ 110 const char *found = strstr(header, etag); 111 if (!found) 112 return 0; /* mismatch */ 113 114 /* Weak Etags never match when using the strong validator */ 115 if (strong && found >= header + 2 && found[-2] == 'W' && found[-1] == '/') 116 return 0; /* mismatch */ 117 118 return 1; /* match */ 119 } 120 121 122 /* ----------------------- http_check_preconditions ----------------------- */ 123 124 NSAPI_PUBLIC int http_check_preconditions(Session *sn, Request *rq, struct tm *mtm, const char *etag) 125 { 126 char *header; 127 128 /* If-modified-since */ 129 header = pblock_findkeyval(pb_key_if_modified_since, rq->headers); 130 if (header) { 131 if (mtm && util_later_than(mtm, header)) { 132 protocol_status(sn, rq, PROTOCOL_NOT_MODIFIED, NULL); 133 return REQ_ABORTED; 134 } 135 } 136 137 /* If-unmodified-since */ 138 header = pblock_findkeyval(pb_key_if_unmodified_since, rq->headers); 139 if (header) { 140 if (mtm && !util_later_than(mtm, header)) { 141 if(util_isdate(header)) { 142 protocol_status(sn, rq, PROTOCOL_PRECONDITION_FAIL, NULL); 143 return REQ_ABORTED; 144 } 145 } 146 } 147 148 /* If-none-match */ 149 header = pblock_findkeyval(pb_key_if_none_match, rq->headers); 150 if (header) { 151 /* If the If-none-match header matches the current Etag... */ 152 if (ISMGET(rq) || ISMHEAD(rq)) { 153 if (http_match_etag(header, etag, PR_FALSE)) { 154 protocol_status(sn, rq, PROTOCOL_NOT_MODIFIED, NULL); 155 return REQ_ABORTED; 156 } 157 } else { 158 if (http_match_etag(header, etag, PR_TRUE)) { 159 protocol_status(sn, rq, PROTOCOL_PRECONDITION_FAIL, NULL); 160 return REQ_ABORTED; 161 } 162 } 163 } 164 165 /* If-match */ 166 header = pblock_findkeyval(pb_key_if_match, rq->headers); 167 if (header) { 168 /* If the If-match header matches the current Etag... */ 169 if (!http_match_etag(header, etag, PR_TRUE)) { 170 protocol_status(sn, rq, PROTOCOL_PRECONDITION_FAIL, NULL); 171 return REQ_ABORTED; 172 } 173 } 174 175 /* If-range */ 176 header = pblock_findkeyval(pb_key_if_range, rq->headers); 177 char *range = pblock_findkeyval(pb_key_range, rq->headers); 178 if (range && header) { 179 int rmir = PR_FALSE; 180 if (util_isdate(header)) { 181 if (mtm && !util_later_than(mtm, header)) { 182 rmir = PR_TRUE; 183 } 184 } else { 185 if (!http_match_etag(header, etag, PR_TRUE)) { 186 rmir = PR_TRUE; 187 } 188 } 189 190 if(rmir) { 191 pblock_removekey(pb_key_range, rq->headers); 192 } 193 } 194 195 return REQ_PROCEED; 196 } 197 198 199 /* ---------------------------- http_set_finfo ---------------------------- */ 200 201 static inline int set_finfo(Session *sn, Request *rq, off_t size, time_t mtime, const char *etag) 202 { 203 struct tm mtms; 204 struct tm *mtm = system_gmtime(&mtime, &mtms); 205 pb_param *pp; 206 207 /* Insert Last-modified */ 208 if (mtm) { 209 pp = pblock_key_param_create(rq->srvhdrs, pb_key_last_modified, NULL, 210 HTTP_DATE_LEN); 211 if (!pp || !pp->value) 212 return REQ_ABORTED; 213 strftime(pp->value, HTTP_DATE_LEN, HTTP_DATE_FMT, mtm); 214 pblock_kpinsert(pb_key_last_modified, pp, rq->srvhdrs); 215 } 216 217 /* Insert Content-length */ 218 const int content_length_size = 21; 219 pp = pblock_key_param_create(rq->srvhdrs, pb_key_content_length, NULL, 220 content_length_size); 221 if (!pp || !pp->value) 222 return REQ_ABORTED; 223 snprintf(pp->value, content_length_size, "%lld", (long long)size); 224 pblock_kpinsert(pb_key_content_length, pp, rq->srvhdrs); 225 226 if (http_etag) { 227 /* Insert Etag */ 228 if(etag) { 229 pblock_kvinsert(pb_key_etag, etag, strlen(etag), rq->srvhdrs); 230 } else { 231 pp = pblock_key_param_create(rq->srvhdrs, pb_key_etag, NULL, MAX_ETAG); 232 if (!pp || !pp->value) 233 return REQ_ABORTED; 234 http_format_etag(sn, rq, pp->value, MAX_ETAG, size, mtime); 235 pblock_kpinsert(pb_key_etag, pp, rq->srvhdrs); 236 etag = pp->value; 237 } 238 239 } else { 240 etag = NULL; 241 } 242 243 /* Check If-modified-since, etc. */ 244 return http_check_preconditions(sn, rq, mtm, etag); 245 } 246 247 NSAPI_PUBLIC int http_set_finfo(Session *sn, Request *rq, struct stat *finfo) 248 { 249 return set_finfo(sn, rq, finfo->st_size, finfo->st_mtime, NULL); 250 } 251 252 NSAPI_PUBLIC int http_set_finfo_etag(Session *sn, Request *rq, struct stat *finfo, const char *etag) { 253 return set_finfo(sn, rq, finfo->st_size, finfo->st_mtime, etag); 254 } 255 256 257 /* ---------------------------- http_hdrs2env ----------------------------- */ 258 259 NSAPI_PUBLIC char **http_hdrs2env(pblock *pb) 260 { 261 char *t, *n, **env; 262 struct pb_entry *p; 263 pb_param *pp; 264 register int x, y, z; 265 int pos, ts, ln; 266 267 /* Find out how many there are. */ 268 for(x = 0, y = 0; x < pb->hsize; x++) { 269 p = pb->ht[x]; 270 while(p) { 271 ++y; 272 p = p->next; 273 } 274 } 275 276 env = util_env_create(NULL, y, &pos); 277 278 ts = 1024; 279 t = (char *) MALLOC(ts); 280 281 for(x = 0; x < pb->hsize; x++) { 282 p = pb->ht[x]; 283 284 while(p) { 285 pp = p->param; 286 287 ln = strlen(pp->name) + 6; 288 289 if(ln >= ts) { 290 ts = ln; 291 t = (char *) REALLOC(t, ts); 292 } 293 n = pp->name; 294 295 /* Skip authorization for CGI */ 296 if(strcasecmp(n, "authorization")) { 297 if(strcasecmp(n, "content-type") && 298 strcasecmp(n, "content-length")) 299 { 300 strncpy(t, "HTTP_", 5); 301 z = 5; 302 } 303 else 304 z = 0; 305 306 for(y = 0; n[y]; ++y, ++z) 307 t[z] = (n[y] == '-' ? '_' : toupper(n[y])); 308 t[z] = '\0'; 309 310 env[pos++] = util_env_str(t, pp->value); 311 } 312 p = p->next; 313 } 314 } 315 env[pos] = NULL; 316 FREE(t); 317 return env; 318 } 319