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) 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 char *etag; 227 if (http_etag) { 228 /* Insert Etag */ 229 pp = pblock_key_param_create(rq->srvhdrs, pb_key_etag, NULL, MAX_ETAG); 230 if (!pp || !pp->value) 231 return REQ_ABORTED; 232 http_format_etag(sn, rq, pp->value, MAX_ETAG, size, mtime); 233 pblock_kpinsert(pb_key_etag, pp, rq->srvhdrs); 234 etag = pp->value; 235 } else { 236 etag = NULL; 237 } 238 239 /* Check If-modified-since, etc. */ 240 return http_check_preconditions(sn, rq, mtm, etag); 241 } 242 243 NSAPI_PUBLIC int http_set_finfo(Session *sn, Request *rq, struct stat *finfo) 244 { 245 return set_finfo(sn, rq, finfo->st_size, finfo->st_mtime); 246 } 247 248 249 /* ---------------------------- http_hdrs2env ----------------------------- */ 250 251 NSAPI_PUBLIC char **http_hdrs2env(pblock *pb) 252 { 253 char *t, *n, **env; 254 struct pb_entry *p; 255 pb_param *pp; 256 register int x, y, z; 257 int pos, ts, ln; 258 259 /* Find out how many there are. */ 260 for(x = 0, y = 0; x < pb->hsize; x++) { 261 p = pb->ht[x]; 262 while(p) { 263 ++y; 264 p = p->next; 265 } 266 } 267 268 env = util_env_create(NULL, y, &pos); 269 270 ts = 1024; 271 t = (char *) MALLOC(ts); 272 273 for(x = 0; x < pb->hsize; x++) { 274 p = pb->ht[x]; 275 276 while(p) { 277 pp = p->param; 278 279 ln = strlen(pp->name) + 6; 280 281 if(ln >= ts) { 282 ts = ln; 283 t = (char *) REALLOC(t, ts); 284 } 285 n = pp->name; 286 287 /* Skip authorization for CGI */ 288 if(strcasecmp(n, "authorization")) { 289 if(strcasecmp(n, "content-type") && 290 strcasecmp(n, "content-length")) 291 { 292 strncpy(t, "HTTP_", 5); 293 z = 5; 294 } 295 else 296 z = 0; 297 298 for(y = 0; n[y]; ++y, ++z) 299 t[z] = (n[y] == '-' ? '_' : toupper(n[y])); 300 t[z] = '\0'; 301 302 env[pos++] = util_env_str(t, pp->value); 303 } 304 p = p->next; 305 } 306 } 307 env[pos] = NULL; 308 FREE(t); 309 return env; 310 } 311