17dd7cddfSDavid du Colombier #include <u.h> 27dd7cddfSDavid du Colombier #include <libc.h> 37dd7cddfSDavid du Colombier #include <auth.h> 47dd7cddfSDavid du Colombier #include "httpd.h" 580ee5cbfSDavid du Colombier #include "httpsrv.h" 67dd7cddfSDavid du Colombier 780ee5cbfSDavid du Colombier static void printtype(Hio *hout, HContent *type, HContent *enc); 87dd7cddfSDavid du Colombier 97dd7cddfSDavid du Colombier /* 107dd7cddfSDavid du Colombier * these should be done better; see the reponse codes in /lib/rfc/rfc2616 for 117dd7cddfSDavid du Colombier * more info on what should be included. 127dd7cddfSDavid du Colombier */ 137dd7cddfSDavid du Colombier #define UNAUTHED "You are not authorized to see this area.\n" 147dd7cddfSDavid du Colombier #define NOCONTENT "No acceptable type of data is available.\n" 157dd7cddfSDavid du Colombier #define NOENCODE "No acceptable encoding of the contents is available.\n" 167dd7cddfSDavid du Colombier #define UNMATCHED "The entity requested does not match the existing entity.\n" 177dd7cddfSDavid du Colombier #define BADRANGE "No bytes are avaible for the range you requested.\n" 187dd7cddfSDavid du Colombier 197dd7cddfSDavid du Colombier /* 207dd7cddfSDavid du Colombier * fd references a file which has been authorized & checked for relocations. 217dd7cddfSDavid du Colombier * send back the headers & it's contents. 227dd7cddfSDavid du Colombier * includes checks for conditional requests & ranges. 237dd7cddfSDavid du Colombier */ 247dd7cddfSDavid du Colombier int 2580ee5cbfSDavid du Colombier sendfd(HConnect *c, int fd, Dir *dir, HContent *type, HContent *enc) 267dd7cddfSDavid du Colombier { 27*9a747e4fSDavid du Colombier Qid qid; 2880ee5cbfSDavid du Colombier HRange *r; 2980ee5cbfSDavid du Colombier HContents conts; 307dd7cddfSDavid du Colombier Hio *hout; 317dd7cddfSDavid du Colombier char *boundary, etag[32]; 32*9a747e4fSDavid du Colombier long mtime; 337dd7cddfSDavid du Colombier ulong tr; 347dd7cddfSDavid du Colombier int n, nw, multir, ok; 35*9a747e4fSDavid du Colombier vlong wrote, length; 367dd7cddfSDavid du Colombier 377dd7cddfSDavid du Colombier hout = &c->hout; 38*9a747e4fSDavid du Colombier length = dir->length; 39*9a747e4fSDavid du Colombier mtime = dir->mtime; 40*9a747e4fSDavid du Colombier qid = dir->qid; 41*9a747e4fSDavid du Colombier free(dir); 427dd7cddfSDavid du Colombier 437dd7cddfSDavid du Colombier /* 447dd7cddfSDavid du Colombier * figure out the type of file and send headers 457dd7cddfSDavid du Colombier */ 467dd7cddfSDavid du Colombier n = -1; 477dd7cddfSDavid du Colombier r = nil; 487dd7cddfSDavid du Colombier multir = 0; 497dd7cddfSDavid du Colombier boundary = nil; 507dd7cddfSDavid du Colombier if(c->req.vermaj){ 5159cc4ca5SDavid du Colombier if(type == nil && enc == nil){ 5280ee5cbfSDavid du Colombier conts = uriclass(c, c->req.uri); 537dd7cddfSDavid du Colombier type = conts.type; 547dd7cddfSDavid du Colombier enc = conts.encoding; 557dd7cddfSDavid du Colombier if(type == nil && enc == nil){ 5680ee5cbfSDavid du Colombier n = read(fd, c->xferbuf, HBufSize-1); 577dd7cddfSDavid du Colombier if(n > 0){ 587dd7cddfSDavid du Colombier c->xferbuf[n] = '\0'; 5980ee5cbfSDavid du Colombier conts = dataclass(c, c->xferbuf, n); 607dd7cddfSDavid du Colombier type = conts.type; 617dd7cddfSDavid du Colombier enc = conts.encoding; 627dd7cddfSDavid du Colombier } 637dd7cddfSDavid du Colombier } 6459cc4ca5SDavid du Colombier } 657dd7cddfSDavid du Colombier if(type == nil) 6680ee5cbfSDavid du Colombier type = hmkcontent(c, "application", "octet-stream", nil); 677dd7cddfSDavid du Colombier 68*9a747e4fSDavid du Colombier snprint(etag, sizeof(etag), "\"%lluxv%lux\"", qid.path, qid.vers); 69*9a747e4fSDavid du Colombier ok = checkreq(c, type, enc, mtime, etag); 707dd7cddfSDavid du Colombier if(ok <= 0){ 717dd7cddfSDavid du Colombier close(fd); 727dd7cddfSDavid du Colombier return ok; 737dd7cddfSDavid du Colombier } 747dd7cddfSDavid du Colombier 757dd7cddfSDavid du Colombier /* 767dd7cddfSDavid du Colombier * check for if-range requests 777dd7cddfSDavid du Colombier */ 787dd7cddfSDavid du Colombier if(c->head.range == nil 797dd7cddfSDavid du Colombier || c->head.ifrangeetag != nil && !etagmatch(1, c->head.ifrangeetag, etag) 80*9a747e4fSDavid du Colombier || c->head.ifrangedate != 0 && c->head.ifrangedate != mtime){ 817dd7cddfSDavid du Colombier c->head.range = nil; 827dd7cddfSDavid du Colombier c->head.ifrangeetag = nil; 837dd7cddfSDavid du Colombier c->head.ifrangedate = 0; 847dd7cddfSDavid du Colombier } 857dd7cddfSDavid du Colombier 867dd7cddfSDavid du Colombier if(c->head.range != nil){ 87*9a747e4fSDavid du Colombier c->head.range = fixrange(c->head.range, length); 887dd7cddfSDavid du Colombier if(c->head.range == nil){ 897dd7cddfSDavid du Colombier if(c->head.ifrangeetag == nil && c->head.ifrangedate == 0){ 9080ee5cbfSDavid du Colombier hprint(hout, "%s 416 Request range not satisfiable\r\n", hversion); 917dd7cddfSDavid du Colombier hprint(hout, "Date: %D\r\n", time(nil)); 927dd7cddfSDavid du Colombier hprint(hout, "Server: Plan9\r\n"); 93*9a747e4fSDavid du Colombier hprint(hout, "Content-Range: bytes */%lld\r\n", length); 947dd7cddfSDavid du Colombier hprint(hout, "Content-Length: %d\r\n", STRLEN(BADRANGE)); 957dd7cddfSDavid du Colombier hprint(hout, "Content-Type: text/html\r\n"); 967dd7cddfSDavid du Colombier if(c->head.closeit) 977dd7cddfSDavid du Colombier hprint(hout, "Connection: close\r\n"); 987dd7cddfSDavid du Colombier else if(!http11(c)) 997dd7cddfSDavid du Colombier hprint(hout, "Connection: Keep-Alive\r\n"); 1007dd7cddfSDavid du Colombier hprint(hout, "\r\n"); 1017dd7cddfSDavid du Colombier if(strcmp(c->req.meth, "HEAD") != 0) 1027dd7cddfSDavid du Colombier hprint(hout, "%s", BADRANGE); 103*9a747e4fSDavid du Colombier hflush(hout); 1047dd7cddfSDavid du Colombier writelog(c, "Reply: 416 Request range not satisfiable\n"); 105*9a747e4fSDavid du Colombier close(fd); 1067dd7cddfSDavid du Colombier return 1; 1077dd7cddfSDavid du Colombier } 1087dd7cddfSDavid du Colombier c->head.ifrangeetag = nil; 1097dd7cddfSDavid du Colombier c->head.ifrangedate = 0; 1107dd7cddfSDavid du Colombier } 1117dd7cddfSDavid du Colombier } 1127dd7cddfSDavid du Colombier if(c->head.range == nil) 11380ee5cbfSDavid du Colombier hprint(hout, "%s 200 OK\r\n", hversion); 1147dd7cddfSDavid du Colombier else 11580ee5cbfSDavid du Colombier hprint(hout, "%s 206 Partial Content\r\n", hversion); 1167dd7cddfSDavid du Colombier 1177dd7cddfSDavid du Colombier hprint(hout, "Server: Plan9\r\n"); 1187dd7cddfSDavid du Colombier hprint(hout, "Date: %D\r\n", time(nil)); 1197dd7cddfSDavid du Colombier hprint(hout, "ETag: %s\r\n", etag); 1207dd7cddfSDavid du Colombier 1217dd7cddfSDavid du Colombier /* 1227dd7cddfSDavid du Colombier * can't send some entity headers if partially responding 1237dd7cddfSDavid du Colombier * to an if-range: etag request 1247dd7cddfSDavid du Colombier */ 1257dd7cddfSDavid du Colombier r = c->head.range; 1267dd7cddfSDavid du Colombier if(r == nil) 127*9a747e4fSDavid du Colombier hprint(hout, "Content-Length: %lld\r\n", length); 1287dd7cddfSDavid du Colombier else if(r->next == nil) 129*9a747e4fSDavid du Colombier hprint(hout, "Content-Range: bytes %ld-%ld/%lld\r\n", r->start, r->stop, length); 1307dd7cddfSDavid du Colombier else{ 1317dd7cddfSDavid du Colombier multir = 1; 13280ee5cbfSDavid du Colombier boundary = hmkmimeboundary(c); 1337dd7cddfSDavid du Colombier hprint(hout, "Content-Type: multipart/byteranges; boundary=%s\r\n", boundary); 1347dd7cddfSDavid du Colombier } 1357dd7cddfSDavid du Colombier if(c->head.ifrangeetag == nil){ 136*9a747e4fSDavid du Colombier hprint(hout, "Last-Modified: %D\r\n", mtime); 1377dd7cddfSDavid du Colombier if(!multir) 1387dd7cddfSDavid du Colombier printtype(hout, type, enc); 1397dd7cddfSDavid du Colombier if(c->head.fresh_thresh) 14080ee5cbfSDavid du Colombier hintprint(c, hout, c->req.uri, c->head.fresh_thresh, c->head.fresh_have); 1417dd7cddfSDavid du Colombier } 1427dd7cddfSDavid du Colombier 1437dd7cddfSDavid du Colombier if(c->head.closeit) 1447dd7cddfSDavid du Colombier hprint(hout, "Connection: close\r\n"); 1457dd7cddfSDavid du Colombier else if(!http11(c)) 1467dd7cddfSDavid du Colombier hprint(hout, "Connection: Keep-Alive\r\n"); 1477dd7cddfSDavid du Colombier hprint(hout, "\r\n"); 1487dd7cddfSDavid du Colombier } 1497dd7cddfSDavid du Colombier if(strcmp(c->req.meth, "HEAD") == 0){ 1507dd7cddfSDavid du Colombier if(c->head.range == nil) 1517dd7cddfSDavid du Colombier writelog(c, "Reply: 200 file 0\n"); 1527dd7cddfSDavid du Colombier else 1537dd7cddfSDavid du Colombier writelog(c, "Reply: 206 file 0\n"); 154*9a747e4fSDavid du Colombier hflush(hout); 1557dd7cddfSDavid du Colombier close(fd); 1567dd7cddfSDavid du Colombier return 1; 1577dd7cddfSDavid du Colombier } 1587dd7cddfSDavid du Colombier 1597dd7cddfSDavid du Colombier /* 1607dd7cddfSDavid du Colombier * send the file if it's a normal file 1617dd7cddfSDavid du Colombier */ 1627dd7cddfSDavid du Colombier if(r == nil){ 1637dd7cddfSDavid du Colombier hflush(hout); 1647dd7cddfSDavid du Colombier 1657dd7cddfSDavid du Colombier wrote = 0; 1667dd7cddfSDavid du Colombier if(n > 0) 167*9a747e4fSDavid du Colombier wrote = write(hout->fd, c->xferbuf, n); 1687dd7cddfSDavid du Colombier if(n <= 0 || wrote == n){ 16980ee5cbfSDavid du Colombier while((n = read(fd, c->xferbuf, HBufSize)) > 0){ 170*9a747e4fSDavid du Colombier nw = write(hout->fd, c->xferbuf, n); 1717dd7cddfSDavid du Colombier if(nw != n){ 1727dd7cddfSDavid du Colombier if(nw > 0) 1737dd7cddfSDavid du Colombier wrote += nw; 1747dd7cddfSDavid du Colombier break; 1757dd7cddfSDavid du Colombier } 1767dd7cddfSDavid du Colombier wrote += nw; 1777dd7cddfSDavid du Colombier } 1787dd7cddfSDavid du Colombier } 179*9a747e4fSDavid du Colombier writelog(c, "Reply: 200 file %lld %lld\n", length, wrote); 1807dd7cddfSDavid du Colombier close(fd); 181*9a747e4fSDavid du Colombier if(length == wrote) 1827dd7cddfSDavid du Colombier return 1; 1837dd7cddfSDavid du Colombier return -1; 1847dd7cddfSDavid du Colombier } 1857dd7cddfSDavid du Colombier 1867dd7cddfSDavid du Colombier /* 1877dd7cddfSDavid du Colombier * for multipart/byterange messages, 1887dd7cddfSDavid du Colombier * it is not ok for the boundary string to appear within a message part. 1897dd7cddfSDavid du Colombier * however, it probably doesn't matter, since there are lengths for every part. 1907dd7cddfSDavid du Colombier */ 1917dd7cddfSDavid du Colombier wrote = 0; 1927dd7cddfSDavid du Colombier ok = 1; 1937dd7cddfSDavid du Colombier for(; r != nil; r = r->next){ 1947dd7cddfSDavid du Colombier if(multir){ 1957dd7cddfSDavid du Colombier hprint(hout, "\r\n--%s\r\n", boundary); 1967dd7cddfSDavid du Colombier printtype(hout, type, enc); 197*9a747e4fSDavid du Colombier hprint(hout, "Content-Range: bytes %ld-%ld/%lld\r\n", r->start, r->stop, length); 1987dd7cddfSDavid du Colombier hprint(hout, "\r\n"); 1997dd7cddfSDavid du Colombier } 2007dd7cddfSDavid du Colombier hflush(hout); 2017dd7cddfSDavid du Colombier 2027dd7cddfSDavid du Colombier if(seek(fd, r->start, 0) != r->start){ 2037dd7cddfSDavid du Colombier ok = -1; 2047dd7cddfSDavid du Colombier break; 2057dd7cddfSDavid du Colombier } 2067dd7cddfSDavid du Colombier for(tr = r->stop - r->start + 1; tr; tr -= n){ 2077dd7cddfSDavid du Colombier n = tr; 20880ee5cbfSDavid du Colombier if(n > HBufSize) 20980ee5cbfSDavid du Colombier n = HBufSize; 2107dd7cddfSDavid du Colombier if(read(fd, c->xferbuf, n) != n){ 2117dd7cddfSDavid du Colombier ok = -1; 2127dd7cddfSDavid du Colombier goto breakout; 2137dd7cddfSDavid du Colombier } 214*9a747e4fSDavid du Colombier nw = write(hout->fd, c->xferbuf, n); 2157dd7cddfSDavid du Colombier if(nw != n){ 2167dd7cddfSDavid du Colombier if(nw > 0) 2177dd7cddfSDavid du Colombier wrote += nw; 2187dd7cddfSDavid du Colombier ok = -1; 2197dd7cddfSDavid du Colombier goto breakout; 2207dd7cddfSDavid du Colombier } 2217dd7cddfSDavid du Colombier wrote += nw; 2227dd7cddfSDavid du Colombier } 2237dd7cddfSDavid du Colombier } 2247dd7cddfSDavid du Colombier breakout:; 2257dd7cddfSDavid du Colombier if(r == nil){ 2267dd7cddfSDavid du Colombier if(multir){ 2277dd7cddfSDavid du Colombier hprint(hout, "--%s--\r\n", boundary); 2287dd7cddfSDavid du Colombier hflush(hout); 2297dd7cddfSDavid du Colombier } 230*9a747e4fSDavid du Colombier writelog(c, "Reply: 206 partial content %lld %lld\n", length, wrote); 2317dd7cddfSDavid du Colombier }else 232*9a747e4fSDavid du Colombier writelog(c, "Reply: 206 partial content, early termination %lld %lld\n", length, wrote); 2337dd7cddfSDavid du Colombier close(fd); 2347dd7cddfSDavid du Colombier return ok; 2357dd7cddfSDavid du Colombier } 2367dd7cddfSDavid du Colombier 2377dd7cddfSDavid du Colombier static void 23880ee5cbfSDavid du Colombier printtype(Hio *hout, HContent *type, HContent *enc) 2397dd7cddfSDavid du Colombier { 2407dd7cddfSDavid du Colombier hprint(hout, "Content-Type: %s/%s", type->generic, type->specific); 2417dd7cddfSDavid du Colombier /* 2427dd7cddfSDavid du Colombier if(cistrcmp(type->generic, "text") == 0) 2437dd7cddfSDavid du Colombier hprint(hout, ";charset=utf-8"); 2447dd7cddfSDavid du Colombier */ 2457dd7cddfSDavid du Colombier hprint(hout, "\r\n"); 2467dd7cddfSDavid du Colombier if(enc != nil) 2477dd7cddfSDavid du Colombier hprint(hout, "Content-Encoding: %s\r\n", enc->generic); 2487dd7cddfSDavid du Colombier } 2497dd7cddfSDavid du Colombier 2507dd7cddfSDavid du Colombier int 25180ee5cbfSDavid du Colombier etagmatch(int strong, HETag *tags, char *e) 2527dd7cddfSDavid du Colombier { 2537dd7cddfSDavid du Colombier char *s, *t; 2547dd7cddfSDavid du Colombier 2557dd7cddfSDavid du Colombier for(; tags != nil; tags = tags->next){ 2567dd7cddfSDavid du Colombier if(strong && tags->weak) 2577dd7cddfSDavid du Colombier continue; 2587dd7cddfSDavid du Colombier s = tags->etag; 2597dd7cddfSDavid du Colombier if(s[0] == '*' && s[1] == '\0') 2607dd7cddfSDavid du Colombier return 1; 2617dd7cddfSDavid du Colombier 2627dd7cddfSDavid du Colombier t = e + 1; 2637dd7cddfSDavid du Colombier while(*t != '"'){ 2647dd7cddfSDavid du Colombier if(*s != *t) 2657dd7cddfSDavid du Colombier break; 2667dd7cddfSDavid du Colombier s++; 2677dd7cddfSDavid du Colombier t++; 2687dd7cddfSDavid du Colombier } 2697dd7cddfSDavid du Colombier 2707dd7cddfSDavid du Colombier if(*s == '\0' && *t == '"') 2717dd7cddfSDavid du Colombier return 1; 2727dd7cddfSDavid du Colombier } 2737dd7cddfSDavid du Colombier return 0; 2747dd7cddfSDavid du Colombier } 2757dd7cddfSDavid du Colombier 2767dd7cddfSDavid du Colombier static char * 27780ee5cbfSDavid du Colombier acceptcont(char *s, char *e, HContent *ok, char *which) 2787dd7cddfSDavid du Colombier { 2797dd7cddfSDavid du Colombier char *sep; 2807dd7cddfSDavid du Colombier 2817dd7cddfSDavid du Colombier if(ok == nil) 2827dd7cddfSDavid du Colombier return seprint(s, e, "Your browser accepts any %s.<br>\n", which); 2837dd7cddfSDavid du Colombier s = seprint(s, e, "Your browser accepts %s: ", which); 2847dd7cddfSDavid du Colombier sep = ""; 2857dd7cddfSDavid du Colombier for(; ok != nil; ok = ok->next){ 2867dd7cddfSDavid du Colombier if(ok->specific) 2877dd7cddfSDavid du Colombier s = seprint(s, e, "%s%s/%s", sep, ok->generic, ok->specific); 2887dd7cddfSDavid du Colombier else 2897dd7cddfSDavid du Colombier s = seprint(s, e, "%s%s", sep, ok->generic); 2907dd7cddfSDavid du Colombier sep = ", "; 2917dd7cddfSDavid du Colombier } 2927dd7cddfSDavid du Colombier return seprint(s, e, ".<br>\n"); 2937dd7cddfSDavid du Colombier } 2947dd7cddfSDavid du Colombier 2957dd7cddfSDavid du Colombier /* 2967dd7cddfSDavid du Colombier * send back a nice error message if the content is unacceptable 2977dd7cddfSDavid du Colombier * to get this message in ie, go to tools, internet options, advanced, 2987dd7cddfSDavid du Colombier * and turn off Show Friendly HTTP Error Messages under the Browsing category 2997dd7cddfSDavid du Colombier */ 3007dd7cddfSDavid du Colombier static int 30180ee5cbfSDavid du Colombier notaccept(HConnect *c, HContent *type, HContent *enc, char *which) 3027dd7cddfSDavid du Colombier { 3037dd7cddfSDavid du Colombier Hio *hout; 3047dd7cddfSDavid du Colombier char *s, *e; 3057dd7cddfSDavid du Colombier 3067dd7cddfSDavid du Colombier hout = &c->hout; 30780ee5cbfSDavid du Colombier e = &c->xferbuf[HBufSize]; 3087dd7cddfSDavid du Colombier s = c->xferbuf; 3097dd7cddfSDavid du Colombier s = seprint(s, e, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\">\n"); 3107dd7cddfSDavid du Colombier s = seprint(s, e, "<html>\n<title>Unacceptable %s</title>\n<body>\n", which); 3117dd7cddfSDavid du Colombier s = seprint(s, e, "Your browser will not accept this data, %H, because of it's %s.<br>\n", c->req.uri, which); 3127dd7cddfSDavid du Colombier s = seprint(s, e, "It's Content-Type is %s/%s", type->generic, type->specific); 3137dd7cddfSDavid du Colombier if(enc != nil) 3147dd7cddfSDavid du Colombier s = seprint(s, e, ", and Content-Encoding is %s", enc->generic); 3157dd7cddfSDavid du Colombier s = seprint(s, e, ".<br>\n\n"); 3167dd7cddfSDavid du Colombier 3177dd7cddfSDavid du Colombier s = acceptcont(s, e, c->head.oktype, "Content-Type"); 3187dd7cddfSDavid du Colombier s = acceptcont(s, e, c->head.okencode, "Content-Encoding"); 3197dd7cddfSDavid du Colombier s = seprint(s, e, "</body>\n</html>\n"); 3207dd7cddfSDavid du Colombier 32180ee5cbfSDavid du Colombier hprint(hout, "%s 406 Not Acceptable\r\n", hversion); 3227dd7cddfSDavid du Colombier hprint(hout, "Server: Plan9\r\n"); 3237dd7cddfSDavid du Colombier hprint(hout, "Date: %D\r\n", time(nil)); 3247dd7cddfSDavid du Colombier hprint(hout, "Content-Type: text/html\r\n"); 3257dd7cddfSDavid du Colombier hprint(hout, "Content-Length: %lud\r\n", s - c->xferbuf); 3267dd7cddfSDavid du Colombier if(c->head.closeit) 3277dd7cddfSDavid du Colombier hprint(hout, "Connection: close\r\n"); 3287dd7cddfSDavid du Colombier else if(!http11(c)) 3297dd7cddfSDavid du Colombier hprint(hout, "Connection: Keep-Alive\r\n"); 3307dd7cddfSDavid du Colombier hprint(hout, "\r\n"); 3317dd7cddfSDavid du Colombier if(strcmp(c->req.meth, "HEAD") != 0) 3327dd7cddfSDavid du Colombier hwrite(hout, c->xferbuf, s - c->xferbuf); 3337dd7cddfSDavid du Colombier writelog(c, "Reply: 406 Not Acceptable\nReason: %s\n", which); 3347dd7cddfSDavid du Colombier return hflush(hout); 3357dd7cddfSDavid du Colombier } 3367dd7cddfSDavid du Colombier 3377dd7cddfSDavid du Colombier /* 3387dd7cddfSDavid du Colombier * check time and entity tag conditions. 3397dd7cddfSDavid du Colombier */ 3407dd7cddfSDavid du Colombier int 34180ee5cbfSDavid du Colombier checkreq(HConnect *c, HContent *type, HContent *enc, long mtime, char *etag) 3427dd7cddfSDavid du Colombier { 3437dd7cddfSDavid du Colombier Hio *hout; 3447dd7cddfSDavid du Colombier int m; 3457dd7cddfSDavid du Colombier 3467dd7cddfSDavid du Colombier hout = &c->hout; 34780ee5cbfSDavid du Colombier if(c->req.vermaj >= 1 && c->req.vermin >= 1 && !hcheckcontent(type, c->head.oktype, "Content-Type", 0)) 3487dd7cddfSDavid du Colombier return notaccept(c, type, enc, "Content-Type"); 34980ee5cbfSDavid du Colombier if(c->req.vermaj >= 1 && c->req.vermin >= 1 && !hcheckcontent(enc, c->head.okencode, "Content-Encoding", 0)) 3507dd7cddfSDavid du Colombier return notaccept(c, type, enc, "Content-Encoding"); 3517dd7cddfSDavid du Colombier 3527dd7cddfSDavid du Colombier /* 3537dd7cddfSDavid du Colombier * can use weak match only with get or head; 3547dd7cddfSDavid du Colombier * this always uses strong matches 3557dd7cddfSDavid du Colombier */ 3567dd7cddfSDavid du Colombier m = etagmatch(1, c->head.ifnomatch, etag); 3577dd7cddfSDavid du Colombier 3587dd7cddfSDavid du Colombier if(m && strcmp(c->req.meth, "GET") != 0 && strcmp(c->req.meth, "HEAD") != 0 3597dd7cddfSDavid du Colombier || c->head.ifunmodsince && c->head.ifunmodsince < mtime 3607dd7cddfSDavid du Colombier || c->head.ifmatch != nil && !etagmatch(1, c->head.ifmatch, etag)){ 36180ee5cbfSDavid du Colombier hprint(hout, "%s 412 Precondition Failed\r\n", hversion); 3627dd7cddfSDavid du Colombier hprint(hout, "Server: Plan9\r\n"); 3637dd7cddfSDavid du Colombier hprint(hout, "Date: %D\r\n", time(nil)); 3647dd7cddfSDavid du Colombier hprint(hout, "Content-Type: text/html\r\n"); 3657dd7cddfSDavid du Colombier hprint(hout, "Content-Length: %d\r\n", STRLEN(UNMATCHED)); 3667dd7cddfSDavid du Colombier if(c->head.closeit) 3677dd7cddfSDavid du Colombier hprint(hout, "Connection: close\r\n"); 3687dd7cddfSDavid du Colombier else if(!http11(c)) 3697dd7cddfSDavid du Colombier hprint(hout, "Connection: Keep-Alive\r\n"); 3707dd7cddfSDavid du Colombier hprint(hout, "\r\n"); 3717dd7cddfSDavid du Colombier if(strcmp(c->req.meth, "HEAD") != 0) 3727dd7cddfSDavid du Colombier hprint(hout, "%s", UNMATCHED); 3737dd7cddfSDavid du Colombier writelog(c, "Reply: 412 Precondition Failed\n"); 3747dd7cddfSDavid du Colombier return hflush(hout); 3757dd7cddfSDavid du Colombier } 3767dd7cddfSDavid du Colombier 3777dd7cddfSDavid du Colombier if(c->head.ifmodsince >= mtime 3787dd7cddfSDavid du Colombier && (m || c->head.ifnomatch == nil)){ 3797dd7cddfSDavid du Colombier /* 3807dd7cddfSDavid du Colombier * can only send back Date, ETag, Content-Location, 3817dd7cddfSDavid du Colombier * Expires, Cache-Control, and Vary entity-headers 3827dd7cddfSDavid du Colombier */ 38380ee5cbfSDavid du Colombier hprint(hout, "%s 304 Not Modified\r\n", hversion); 3847dd7cddfSDavid du Colombier hprint(hout, "Server: Plan9\r\n"); 3857dd7cddfSDavid du Colombier hprint(hout, "Date: %D\r\n", time(nil)); 3867dd7cddfSDavid du Colombier hprint(hout, "ETag: %s\r\n", etag); 3877dd7cddfSDavid du Colombier if(c->head.closeit) 3887dd7cddfSDavid du Colombier hprint(hout, "Connection: close\r\n"); 3897dd7cddfSDavid du Colombier else if(!http11(c)) 3907dd7cddfSDavid du Colombier hprint(hout, "Connection: Keep-Alive\r\n"); 3917dd7cddfSDavid du Colombier hprint(hout, "\r\n"); 3927dd7cddfSDavid du Colombier writelog(c, "Reply: 304 Not Modified\n"); 3937dd7cddfSDavid du Colombier return hflush(hout); 3947dd7cddfSDavid du Colombier } 3957dd7cddfSDavid du Colombier return 1; 3967dd7cddfSDavid du Colombier } 3977dd7cddfSDavid du Colombier 3987dd7cddfSDavid du Colombier /* 3997dd7cddfSDavid du Colombier * length is the actual length of the entity requested. 4007dd7cddfSDavid du Colombier * discard any range requests which are invalid, 4017dd7cddfSDavid du Colombier * ie start after the end, or have stop before start. 4027dd7cddfSDavid du Colombier * rewrite suffix requests 4037dd7cddfSDavid du Colombier */ 40480ee5cbfSDavid du Colombier HRange* 40580ee5cbfSDavid du Colombier fixrange(HRange *h, long length) 4067dd7cddfSDavid du Colombier { 40780ee5cbfSDavid du Colombier HRange *r, *rr; 4087dd7cddfSDavid du Colombier 4097dd7cddfSDavid du Colombier if(length == 0) 4107dd7cddfSDavid du Colombier return nil; 4117dd7cddfSDavid du Colombier 4127dd7cddfSDavid du Colombier /* 4137dd7cddfSDavid du Colombier * rewrite each range to reflect the actual length of the file 4147dd7cddfSDavid du Colombier * toss out any invalid ranges 4157dd7cddfSDavid du Colombier */ 4167dd7cddfSDavid du Colombier rr = nil; 4177dd7cddfSDavid du Colombier for(r = h; r != nil; r = r->next){ 4187dd7cddfSDavid du Colombier if(r->suffix){ 4197dd7cddfSDavid du Colombier r->start = length - r->stop; 4207dd7cddfSDavid du Colombier if(r->start >= length) 4217dd7cddfSDavid du Colombier r->start = 0; 4227dd7cddfSDavid du Colombier r->stop = length - 1; 4237dd7cddfSDavid du Colombier r->suffix = 0; 4247dd7cddfSDavid du Colombier } 4257dd7cddfSDavid du Colombier if(r->stop >= length) 4267dd7cddfSDavid du Colombier r->stop = length - 1; 4277dd7cddfSDavid du Colombier if(r->start > r->stop){ 4287dd7cddfSDavid du Colombier if(rr == nil) 4297dd7cddfSDavid du Colombier h = r->next; 4307dd7cddfSDavid du Colombier else 4317dd7cddfSDavid du Colombier rr->next = r->next; 4327dd7cddfSDavid du Colombier }else 4337dd7cddfSDavid du Colombier rr = r; 4347dd7cddfSDavid du Colombier } 4357dd7cddfSDavid du Colombier 4367dd7cddfSDavid du Colombier /* 4377dd7cddfSDavid du Colombier * merge consecutive overlapping or abutting ranges 4387dd7cddfSDavid du Colombier * 4397dd7cddfSDavid du Colombier * not clear from rfc2616 how much merging needs to be done. 4407dd7cddfSDavid du Colombier * this code merges only if a range is adjacent to a later starting, 4417dd7cddfSDavid du Colombier * over overlapping or abutting range. this allows a client 4427dd7cddfSDavid du Colombier * to request wanted data first, followed by other data. 4437dd7cddfSDavid du Colombier * this may be useful then fetching part of a page, then the adjacent regions. 4447dd7cddfSDavid du Colombier */ 4457dd7cddfSDavid du Colombier if(h == nil) 4467dd7cddfSDavid du Colombier return h; 4477dd7cddfSDavid du Colombier r = h; 4487dd7cddfSDavid du Colombier for(;;){ 4497dd7cddfSDavid du Colombier rr = r->next; 4507dd7cddfSDavid du Colombier if(rr == nil) 4517dd7cddfSDavid du Colombier break; 4527dd7cddfSDavid du Colombier if(r->start <= rr->start && r->stop + 1 >= rr->start){ 4537dd7cddfSDavid du Colombier if(r->stop < rr->stop) 4547dd7cddfSDavid du Colombier r->stop = rr->stop; 4557dd7cddfSDavid du Colombier r->next = rr->next; 4567dd7cddfSDavid du Colombier }else 4577dd7cddfSDavid du Colombier r = rr; 4587dd7cddfSDavid du Colombier } 4597dd7cddfSDavid du Colombier return h; 4607dd7cddfSDavid du Colombier } 461