Add -fno-strict-aliasing to prevent compile warnings on some systems.
[polipo.git] / http.c
blobca30095af9537cc0a166a111548db138c4986222
1 /*
2 Copyright (c) 2003-2006 by Juliusz Chroboczek
4 Permission is hereby granted, free of charge, to any person obtaining a copy
5 of this software and associated documentation files (the "Software"), to deal
6 in the Software without restriction, including without limitation the rights
7 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 copies of the Software, and to permit persons to whom the Software is
9 furnished to do so, subject to the following conditions:
11 The above copyright notice and this permission notice shall be included in
12 all copies or substantial portions of the Software.
14 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 THE SOFTWARE.
23 #include "polipo.h"
25 int disableProxy = 0;
26 AtomPtr proxyName = NULL;
27 int proxyPort = 8123;
29 int clientTimeout = 120;
30 int serverTimeout = 90;
31 int serverIdleTimeout = 45;
33 int bigBufferSize = (32 * 1024);
35 AtomPtr authRealm = NULL;
36 AtomPtr authCredentials = NULL;
38 AtomPtr parentAuthCredentials = NULL;
40 AtomListPtr allowedClients = NULL;
41 NetAddressPtr allowedNets = NULL;
43 IntListPtr allowedPorts = NULL;
44 IntListPtr tunnelAllowedPorts = NULL;
45 int expectContinue = 1;
46 int dontTrustVaryETag = 1;
48 AtomPtr atom100Continue;
50 int disableVia = 1;
52 /* 0 means that all failures lead to errors. 1 means that failures to
53 connect are reported in a Warning header when stale objects are
54 served. 2 means that only missing data is fetched from the net,
55 stale data is served without revalidation (browser-side
56 Cache-Control directives are still honoured). 3 means that no
57 connections are ever attempted. */
59 int proxyOffline = 0;
60 int relaxTransparency = 0;
61 AtomPtr proxyAddress = NULL;
63 int dontIdentifyToClients = 0;
65 static int timeoutSetter(ConfigVariablePtr var, void *value);
67 void
68 preinitHttp()
70 proxyAddress = internAtom("127.0.0.1");
71 CONFIG_VARIABLE_SETTABLE(disableProxy, CONFIG_BOOLEAN, configIntSetter,
72 "Whether to be a web server only.");
73 CONFIG_VARIABLE_SETTABLE(proxyOffline, CONFIG_BOOLEAN, configIntSetter,
74 "Avoid contacting remote servers.");
75 CONFIG_VARIABLE_SETTABLE(relaxTransparency, CONFIG_TRISTATE,
76 configIntSetter,
77 "Avoid contacting remote servers.");
78 CONFIG_VARIABLE(proxyPort, CONFIG_INT,
79 "The TCP port on which the proxy listens.");
80 CONFIG_VARIABLE(proxyAddress, CONFIG_ATOM_LOWER,
81 "The IP address on which the proxy listens.");
82 CONFIG_VARIABLE_SETTABLE(proxyName, CONFIG_ATOM_LOWER, configAtomSetter,
83 "The name by which the proxy is known.");
84 CONFIG_VARIABLE_SETTABLE(clientTimeout, CONFIG_TIME,
85 timeoutSetter, "Client-side timeout.");
86 CONFIG_VARIABLE_SETTABLE(serverTimeout, CONFIG_TIME,
87 timeoutSetter, "Server-side timeout.");
88 CONFIG_VARIABLE_SETTABLE(serverIdleTimeout, CONFIG_TIME,
89 timeoutSetter, "Server-side idle timeout.");
90 CONFIG_VARIABLE(authRealm, CONFIG_ATOM,
91 "Authentication realm.");
92 CONFIG_VARIABLE(authCredentials, CONFIG_PASSWORD,
93 "username:password.");
94 CONFIG_VARIABLE(parentAuthCredentials, CONFIG_PASSWORD,
95 "username:password.");
96 CONFIG_VARIABLE(allowedClients, CONFIG_ATOM_LIST_LOWER,
97 "Networks from which clients are allowed to connect.");
98 CONFIG_VARIABLE(tunnelAllowedPorts, CONFIG_INT_LIST,
99 "Ports to which tunnelled connections are allowed.");
100 CONFIG_VARIABLE(allowedPorts, CONFIG_INT_LIST,
101 "Ports to which connections are allowed.");
102 CONFIG_VARIABLE(expectContinue, CONFIG_TRISTATE,
103 "Send Expect-Continue to servers.");
104 CONFIG_VARIABLE(bigBufferSize, CONFIG_INT,
105 "Size of big buffers (max size of headers).");
106 CONFIG_VARIABLE_SETTABLE(disableVia, CONFIG_BOOLEAN, configIntSetter,
107 "Don't use Via headers.");
108 CONFIG_VARIABLE(dontTrustVaryETag, CONFIG_TRISTATE,
109 "Whether to trust the ETag when there's Vary.");
110 CONFIG_VARIABLE(dontIdentifyToClients, CONFIG_BOOLEAN,
111 "Avoid sending machine-identifiable information "
112 "to clients.");
113 preinitHttpParser();
116 static int
117 timeoutSetter(ConfigVariablePtr var, void *value)
119 configIntSetter(var, value);
120 if(clientTimeout <= serverTimeout)
121 clientTimeout = serverTimeout + 1;
122 return 1;
125 void
126 initHttp()
128 char *buf = NULL;
129 int namelen;
130 int n;
131 struct hostent *host;
133 initHttpParser();
135 atom100Continue = internAtom("100-continue");
137 if(clientTimeout <= serverTimeout) {
138 clientTimeout = serverTimeout + 1;
139 do_log(L_WARN, "Value of clientTimeout too small -- setting to %d.\n",
140 clientTimeout);
143 if(authCredentials != NULL && authRealm == NULL)
144 authRealm = internAtom("Polipo");
146 if(allowedClients) {
147 allowedNets = parseNetAddress(allowedClients);
148 if(allowedNets == NULL)
149 exit(1);
152 if(allowedPorts == NULL) {
153 allowedPorts = makeIntList(0);
154 if(allowedPorts == NULL) {
155 do_log(L_ERROR, "Couldn't allocate allowedPorts.\n");
156 exit(1);
158 intListCons(80, 100, allowedPorts);
159 intListCons(1024, 0xFFFF, allowedPorts);
162 if(tunnelAllowedPorts == NULL) {
163 tunnelAllowedPorts = makeIntList(0);
164 if(tunnelAllowedPorts == NULL) {
165 do_log(L_ERROR, "Couldn't allocate tunnelAllowedPorts.\n");
166 exit(1);
168 intListCons(22, 22, tunnelAllowedPorts); /* ssh */
169 intListCons(80, 80, tunnelAllowedPorts); /* HTTP */
170 intListCons(109, 110, tunnelAllowedPorts); /* POP 2 and 3*/
171 intListCons(143, 143, tunnelAllowedPorts); /* IMAP 2/4 */
172 intListCons(443, 443, tunnelAllowedPorts); /* HTTP/SSL */
173 intListCons(873, 873, tunnelAllowedPorts); /* rsync */
174 intListCons(993, 993, tunnelAllowedPorts); /* IMAP/SSL */
175 intListCons(995, 995, tunnelAllowedPorts); /* POP/SSL */
176 intListCons(2401, 2401, tunnelAllowedPorts); /* CVS */
177 intListCons(5222, 5223, tunnelAllowedPorts); /* Jabber */
178 intListCons(9418, 9418, tunnelAllowedPorts); /* Git */
181 if(proxyName)
182 return;
184 buf = get_chunk();
185 if(buf == NULL) {
186 do_log(L_ERROR, "Couldn't allocate chunk for host name.\n");
187 goto fail;
190 n = gethostname(buf, CHUNK_SIZE);
191 if(n != 0) {
192 do_log_error(L_WARN, errno, "Gethostname");
193 strcpy(buf, "polipo");
194 goto success;
196 /* gethostname doesn't necessarily NUL-terminate on overflow */
197 buf[CHUNK_SIZE - 1] = '\0';
199 if(strcmp(buf, "(none)") == 0 ||
200 strcmp(buf, "localhost") == 0 ||
201 strcmp(buf, "localhost.localdomain") == 0) {
202 do_log(L_WARN, "Couldn't determine host name -- using ``polipo''.\n");
203 strcpy(buf, "polipo");
204 goto success;
207 if(strchr(buf, '.') != NULL)
208 goto success;
210 host = gethostbyname(buf);
211 if(host == NULL) {
212 goto success;
215 if(host->h_addrtype != AF_INET)
216 goto success;
218 host = gethostbyaddr(host->h_addr_list[0], host->h_length, AF_INET);
220 if(!host || !host->h_name || strcmp(host->h_name, "localhost") == 0 ||
221 strcmp(host->h_name, "localhost.localdomain") == 0)
222 goto success;
224 namelen = strlen(host->h_name);
225 if(namelen >= CHUNK_SIZE) {
226 do_log(L_ERROR, "Host name too long.\n");
227 goto success;
230 memcpy(buf, host->h_name, namelen + 1);
232 success:
233 proxyName = internAtom(buf);
234 if(proxyName == NULL) {
235 do_log(L_ERROR, "Couldn't allocate proxy name.\n");
236 goto fail;
238 dispose_chunk(buf);
239 return;
241 fail:
242 if(buf)
243 dispose_chunk(buf);
244 exit(1);
245 return;
249 httpSetTimeout(HTTPConnectionPtr connection, int secs)
251 TimeEventHandlerPtr new;
253 if(connection->timeout)
254 cancelTimeEvent(connection->timeout);
255 connection->timeout = NULL;
257 if(secs > 0) {
258 new = scheduleTimeEvent(secs, httpTimeoutHandler,
259 sizeof(connection), &connection);
260 if(!new) {
261 do_log(L_ERROR, "Couldn't schedule timeout for connection 0x%lx\n",
262 (unsigned long)connection);
263 return -1;
265 } else {
266 new = NULL;
269 connection->timeout = new;
270 return 1;
273 int
274 httpTimeoutHandler(TimeEventHandlerPtr event)
276 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
278 if(connection->fd >= 0) {
279 int rc;
280 rc = shutdown(connection->fd, 2);
281 if(rc < 0 && errno != ENOTCONN)
282 do_log_error(L_ERROR, errno, "Timeout: shutdown failed");
283 pokeFdEvent(connection->fd, -EDOTIMEOUT, POLLIN | POLLOUT);
285 connection->timeout = NULL;
286 return 1;
290 httpWriteObjectHeaders(char *buf, int offset, int len,
291 ObjectPtr object, int from, int to)
293 int n = offset;
295 if(from <= 0 && to < 0) {
296 if(object->length >= 0) {
297 n = snnprintf(buf, n, len,
298 "\r\nContent-Length: %d", object->length);
300 } else {
301 if(to >= 0) {
302 n = snnprintf(buf, n, len,
303 "\r\nContent-Length: %d", to - from);
307 if(from > 0 || to > 0) {
308 if(object->length >= 0) {
309 if(from >= to) {
310 n = snnprintf(buf, n, len,
311 "\r\nContent-Range: bytes */%d",
312 object->length);
313 } else {
314 n = snnprintf(buf, n, len,
315 "\r\nContent-Range: bytes %d-%d/%d",
316 from, to - 1,
317 object->length);
319 } else {
320 if(to >= 0) {
321 n = snnprintf(buf, n, len,
322 "\r\nContent-Range: bytes %d-/*",
323 from);
324 } else {
325 n = snnprintf(buf, n, len,
326 "\r\nContent-Range: bytes %d-%d/*",
327 from, to);
332 if(object->etag) {
333 n = snnprintf(buf, n, len, "\r\nETag: \"%s\"", object->etag);
335 if((object->flags & OBJECT_LOCAL) || object->date >= 0) {
336 n = snnprintf(buf, n, len, "\r\nDate: ");
337 n = format_time(buf, n, len,
338 (object->flags & OBJECT_LOCAL) ?
339 current_time.tv_sec : object->date);
340 if(n < 0)
341 goto fail;
344 if(object->last_modified >= 0) {
345 n = snnprintf(buf, n, len, "\r\nLast-Modified: ");
346 n = format_time(buf, n, len, object->last_modified);
347 if(n < 0)
348 goto fail;
351 if(object->expires >= 0) {
352 n = snnprintf(buf, n, len, "\r\nExpires: ");
353 n = format_time(buf, n, len, object->expires);
354 if(n < 0)
355 goto fail;
358 n = httpPrintCacheControl(buf, n, len,
359 object->cache_control, NULL);
360 if(n < 0)
361 goto fail;
363 if(!disableVia && object->via)
364 n = snnprintf(buf, n, len, "\r\nVia: %s", object->via->string);
366 if(object->headers)
367 n = snnprint_n(buf, n, len, object->headers->string,
368 object->headers->length);
370 if(n < len)
371 return n;
372 else
373 return -1;
375 fail:
376 return -1;
379 static int
380 cachePrintSeparator(char *buf, int offset, int len,
381 int subsequent)
383 int n;
384 if(subsequent)
385 n = snnprintf(buf, offset, len, ", ");
386 else
387 n = snnprintf(buf, offset, len, "\r\nCache-Control: ");
388 return n;
392 httpPrintCacheControl(char *buf, int offset, int len,
393 int flags, CacheControlPtr cache_control)
395 int n = offset;
396 int sub = 0;
398 #define PRINT_SEP() \
399 do {\
400 n = cachePrintSeparator(buf, n, len, sub); \
401 sub = 1; \
402 } while(0)
404 if(cache_control)
405 flags |= cache_control->flags;
407 if(flags & CACHE_NO) {
408 PRINT_SEP();
409 n = snnprintf(buf, n, len, "no-cache");
411 if(flags & CACHE_PUBLIC) {
412 PRINT_SEP();
413 n = snnprintf(buf, n, len, "public");
415 if(flags & CACHE_PRIVATE) {
416 PRINT_SEP();
417 n = snnprintf(buf, n, len, "private");
419 if(flags & CACHE_NO_STORE) {
420 PRINT_SEP();
421 n = snnprintf(buf, n, len, "no-store");
423 if(flags & CACHE_NO_TRANSFORM) {
424 PRINT_SEP();
425 n = snnprintf(buf, n, len, "no-transform");
427 if(flags & CACHE_MUST_REVALIDATE) {
428 PRINT_SEP();
429 n = snnprintf(buf, n, len, "must-revalidate");
431 if(flags & CACHE_PROXY_REVALIDATE) {
432 PRINT_SEP();
433 n = snnprintf(buf, n, len, "proxy-revalidate");
435 if(flags & CACHE_ONLY_IF_CACHED) {
436 PRINT_SEP();
437 n = snnprintf(buf, n, len, "only-if-cached");
439 if(cache_control) {
440 if(cache_control->max_age >= 0) {
441 PRINT_SEP();
442 n = snnprintf(buf, n, len, "max-age=%d",
443 cache_control->max_age);
445 if(cache_control->s_maxage >= 0) {
446 PRINT_SEP();
447 n = snnprintf(buf, n, len, "s-maxage=%d",
448 cache_control->s_maxage);
450 if(cache_control->min_fresh > 0) {
451 PRINT_SEP();
452 n = snnprintf(buf, n, len, "min-fresh=%d",
453 cache_control->min_fresh);
455 if(cache_control->max_stale > 0) {
456 PRINT_SEP();
457 n = snnprintf(buf, n, len, "max-stale=%d",
458 cache_control->min_fresh);
461 return n;
462 #undef PRINT_SEP
465 char *
466 httpMessage(int code)
468 switch(code) {
469 case 200:
470 return "Okay";
471 case 206:
472 return "Partial content";
473 case 300:
474 return "Multiple choices";
475 case 301:
476 return "Moved permanently";
477 case 302:
478 return "Found";
479 case 303:
480 return "See other";
481 case 304:
482 return "Not changed";
483 case 307:
484 return "Temporary redirect";
485 case 401:
486 return "Authentication Required";
487 case 403:
488 return "Forbidden";
489 case 404:
490 return "Not found";
491 case 405:
492 return "Method not allowed";
493 case 407:
494 return "Proxy authentication required";
495 default:
496 return "Unknown error code";
501 htmlString(char *buf, int n, int len, char *s, int slen)
503 int i = 0;
504 while(i < slen && n + 5 < len) {
505 switch(s[i]) {
506 case '&':
507 buf[n++] = '&'; buf[n++] = 'a'; buf[n++] = 'm'; buf[n++] = 'p';
508 buf[n++] = ';';
509 break;
510 case '<':
511 buf[n++] = '&'; buf[n++] = 'l'; buf[n++] = 't'; buf[n++] = ';';
512 break;
513 case '>':
514 buf[n++] = '&'; buf[n++] = 'g'; buf[n++] = 't'; buf[n++] = ';';
515 break;
516 case '"':
517 buf[n++] = '&'; buf[n++] = 'q'; buf[n++] = 'u'; buf[n++] = 'o';
518 buf[n++] = 't'; buf[n++] = ';';
519 break;
520 case '\0':
521 break;
522 default:
523 buf[n++] = s[i];
525 i++;
527 return n;
530 void
531 htmlPrint(FILE *out, char *s, int slen)
533 int i;
534 for(i = 0; i < slen; i++) {
535 switch(s[i]) {
536 case '&':
537 fputs("&amp;", out);
538 break;
539 case '<':
540 fputs("&lt;", out);
541 break;
542 case '>':
543 fputs("&gt;", out);
544 break;
545 default:
546 fputc(s[i], out);
551 HTTPConnectionPtr
552 httpMakeConnection()
554 HTTPConnectionPtr connection;
555 connection = malloc(sizeof(HTTPConnectionRec));
556 if(connection == NULL)
557 return NULL;
558 connection->flags = 0;
559 connection->fd = -1;
560 connection->buf = NULL;
561 connection->len = 0;
562 connection->offset = 0;
563 connection->request = NULL;
564 connection->request_last = NULL;
565 connection->serviced = 0;
566 connection->version = HTTP_UNKNOWN;
567 connection->time = current_time.tv_sec;
568 connection->timeout = NULL;
569 connection->te = TE_IDENTITY;
570 connection->reqbuf = NULL;
571 connection->reqlen = 0;
572 connection->reqbegin = 0;
573 connection->reqoffset = 0;
574 connection->bodylen = -1;
575 connection->reqte = TE_IDENTITY;
576 connection->chunk_remaining = 0;
577 connection->server = NULL;
578 connection->pipelined = 0;
579 connection->connecting = 0;
580 connection->server = NULL;
581 return connection;
584 void
585 httpDestroyConnection(HTTPConnectionPtr connection)
587 assert(connection->flags == 0);
588 httpConnectionDestroyBuf(connection);
589 assert(!connection->request);
590 assert(!connection->request_last);
591 httpConnectionDestroyReqbuf(connection);
592 assert(!connection->timeout);
593 assert(!connection->server);
594 free(connection);
597 void
598 httpConnectionDestroyBuf(HTTPConnectionPtr connection)
600 if(connection->buf) {
601 if(connection->flags & CONN_BIGBUF)
602 free(connection->buf);
603 else
604 dispose_chunk(connection->buf);
606 connection->flags &= ~CONN_BIGBUF;
607 connection->buf = NULL;
610 void
611 httpConnectionDestroyReqbuf(HTTPConnectionPtr connection)
613 if(connection->reqbuf) {
614 if(connection->flags & CONN_BIGREQBUF)
615 free(connection->reqbuf);
616 else
617 dispose_chunk(connection->reqbuf);
619 connection->flags &= ~CONN_BIGREQBUF;
620 connection->reqbuf = NULL;
623 HTTPRequestPtr
624 httpMakeRequest()
626 HTTPRequestPtr request;
627 request = malloc(sizeof(HTTPRequestRec));
628 if(request == NULL)
629 return NULL;
630 request->flags = 0;
631 request->connection = NULL;
632 request->object = NULL;
633 request->method = METHOD_UNKNOWN;
634 request->from = 0;
635 request->to = -1;
636 request->cache_control = no_cache_control;
637 request->condition = NULL;
638 request->via = NULL;
639 request->chandler = NULL;
640 request->can_mutate = NULL;
641 request->error_code = 0;
642 request->error_message = NULL;
643 request->error_headers = NULL;
644 request->headers = NULL;
645 request->time0 = null_time;
646 request->time1 = null_time;
647 request->request = NULL;
648 request->next = NULL;
649 return request;
652 void
653 httpDestroyRequest(HTTPRequestPtr request)
655 if(request->object)
656 releaseObject(request->object);
657 if(request->condition)
658 httpDestroyCondition(request->condition);
659 releaseAtom(request->via);
660 assert(request->chandler == NULL);
661 releaseAtom(request->error_message);
662 releaseAtom(request->headers);
663 releaseAtom(request->error_headers);
664 assert(request->request == NULL);
665 assert(request->next == NULL);
666 free(request);
669 void
670 httpQueueRequest(HTTPConnectionPtr connection, HTTPRequestPtr request)
672 assert(request->next == NULL && request->connection == NULL);
673 request->connection = connection;
674 if(connection->request_last) {
675 assert(connection->request);
676 connection->request_last->next = request;
677 connection->request_last = request;
678 } else {
679 assert(!connection->request_last);
680 connection->request = request;
681 connection->request_last = request;
685 HTTPRequestPtr
686 httpDequeueRequest(HTTPConnectionPtr connection)
688 HTTPRequestPtr request = connection->request;
689 if(request) {
690 assert(connection->request_last);
691 connection->request = request->next;
692 if(!connection->request) connection->request_last = NULL;
693 request->next = NULL;
695 return request;
699 httpConnectionBigify(HTTPConnectionPtr connection)
701 char *bigbuf;
702 assert(!(connection->flags & CONN_BIGBUF));
704 if(bigBufferSize <= CHUNK_SIZE)
705 return 0;
707 bigbuf = malloc(bigBufferSize);
708 if(bigbuf == NULL)
709 return -1;
710 if(connection->len > 0)
711 memcpy(bigbuf, connection->buf, connection->len);
712 if(connection->buf)
713 dispose_chunk(connection->buf);
714 connection->buf = bigbuf;
715 connection->flags |= CONN_BIGBUF;
716 return 1;
720 httpConnectionBigifyReqbuf(HTTPConnectionPtr connection)
722 char *bigbuf;
723 assert(!(connection->flags & CONN_BIGREQBUF));
725 if(bigBufferSize <= CHUNK_SIZE)
726 return 0;
728 bigbuf = malloc(bigBufferSize);
729 if(bigbuf == NULL)
730 return -1;
731 if(connection->reqlen > 0)
732 memcpy(bigbuf, connection->reqbuf, connection->reqlen);
733 if(connection->reqbuf)
734 dispose_chunk(connection->reqbuf);
735 connection->reqbuf = bigbuf;
736 connection->flags |= CONN_BIGREQBUF;
737 return 1;
741 httpConnectionUnbigify(HTTPConnectionPtr connection)
743 char *buf;
744 assert(connection->flags & CONN_BIGBUF);
745 assert(connection->len < CHUNK_SIZE);
747 buf = get_chunk();
748 if(buf == NULL)
749 return -1;
750 if(connection->len > 0)
751 memcpy(buf, connection->buf, connection->len);
752 free(connection->buf);
753 connection->buf = buf;
754 connection->flags &= ~CONN_BIGBUF;
755 return 1;
759 httpConnectionUnbigifyReqbuf(HTTPConnectionPtr connection)
761 char *buf;
762 assert(connection->flags & CONN_BIGREQBUF);
763 assert(connection->reqlen < CHUNK_SIZE);
765 buf = get_chunk();
766 if(buf == NULL)
767 return -1;
768 if(connection->reqlen > 0)
769 memcpy(buf, connection->reqbuf, connection->reqlen);
770 free(connection->reqbuf);
771 connection->reqbuf = buf;
772 connection->flags &= ~CONN_BIGREQBUF;
773 return 1;
776 HTTPConditionPtr
777 httpMakeCondition()
779 HTTPConditionPtr condition;
780 condition = malloc(sizeof(HTTPConditionRec));
781 if(condition == NULL)
782 return NULL;
783 condition->ims = -1;
784 condition->inms = -1;
785 condition->im = NULL;
786 condition->inm = NULL;
787 condition->ifrange = NULL;
788 return condition;
791 void
792 httpDestroyCondition(HTTPConditionPtr condition)
794 if(condition->inm)
795 free(condition->inm);
796 if(condition->im)
797 free(condition->im);
798 if(condition->ifrange)
799 free(condition->ifrange);
800 free(condition);
804 httpCondition(ObjectPtr object, HTTPConditionPtr condition)
806 int rc = CONDITION_MATCH;
808 assert(!(object->flags & OBJECT_INITIAL));
810 if(!condition) return CONDITION_MATCH;
812 if(condition->ims >= 0) {
813 if(object->last_modified < 0 ||
814 condition->ims < object->last_modified)
815 return rc;
816 else
817 rc = CONDITION_NOT_MODIFIED;
820 if(condition->inms >= 0) {
821 if(object->last_modified < 0 ||
822 condition->inms >= object->last_modified)
823 return rc;
824 else
825 rc = CONDITION_FAILED;
828 if(condition->inm) {
829 if(!object->etag || strcmp(object->etag, condition->inm) != 0)
830 return rc;
831 else
832 rc = CONDITION_NOT_MODIFIED;
835 if(condition->im) {
836 if(!object->etag || strcmp(object->etag, condition->im) != 0)
837 rc = CONDITION_FAILED;
838 else
839 return rc;
842 return rc;
846 httpWriteErrorHeaders(char *buf, int size, int offset, int do_body,
847 int code, AtomPtr message, int close, AtomPtr headers,
848 char *url, int url_len, char *etag)
850 int n, m, i;
851 char *body;
852 char htmlMessage[100];
854 assert(code != 0);
856 i = htmlString(htmlMessage, 0, 100, message->string, message->length);
857 if(i < 0)
858 strcpy(htmlMessage, "(Couldn't format message)");
859 else
860 htmlMessage[MIN(i, 99)] = '\0';
862 if(code != 304) {
863 body = get_chunk();
864 if(!body) {
865 do_log(L_ERROR, "Couldn't allocate body buffer.\n");
866 return -1;
868 m = snnprintf(body, 0, CHUNK_SIZE,
869 "<!DOCTYPE HTML PUBLIC "
870 "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
871 "\"http://www.w3.org/TR/html4/loose.dtd\">"
872 "\n<html><head>"
873 "\n<title>Proxy %s: %3d %s.</title>"
874 "\n</head><body>"
875 "\n<h1>%3d %s</h1>"
876 "\n<p>The following %s",
877 code >= 400 ? "error" : "result",
878 code, htmlMessage,
879 code, htmlMessage,
880 code >= 400 ?
881 "error occurred" :
882 "status was returned");
883 if(url_len > 0) {
884 m = snnprintf(body, m, CHUNK_SIZE,
885 " while trying to access <strong>");
886 m = htmlString(body, m, CHUNK_SIZE, url, url_len);
887 m = snnprintf(body, m, CHUNK_SIZE, "</strong>");
890 m = snnprintf(body, m, CHUNK_SIZE,
891 ":<br><br>"
892 "\n<strong>%3d %s</strong></p>",
893 code, htmlMessage);
894 if (!dontIdentifyToClients) {
895 char timeStr[100];
896 /* On BSD systems, tv_sec is a long. */
897 const time_t ct = current_time.tv_sec;
898 /*Mon, 24 Sep 2004 17:46:35 GMT*/
899 strftime(timeStr, sizeof(timeStr), "%a, %d %b %Y %H:%M:%S %Z",
900 localtime(&ct));
902 m = snnprintf(body, m, CHUNK_SIZE,
903 "\n<hr>Generated %s by Polipo on <em>%s:%d</em>.",
904 timeStr, proxyName->string, proxyPort);
907 m = snnprintf(body, m, CHUNK_SIZE, "\n</body></html>\r\n");
909 if(m <= 0 || m >= CHUNK_SIZE) {
910 do_log(L_ERROR, "Couldn't write error body.\n");
911 dispose_chunk(body);
912 return -1;
914 } else {
915 body = NULL;
916 m = 0;
919 n = snnprintf(buf, 0, size,
920 "HTTP/1.1 %3d %s"
921 "\r\nConnection: %s"
922 "\r\nDate: ",
923 code, atomString(message),
924 close ? "close" : "keep-alive");
925 n = format_time(buf, n, size, current_time.tv_sec);
926 if(code != 304) {
927 n = snnprintf(buf, n, size,
928 "\r\nContent-Type: text/html"
929 "\r\nContent-Length: %d", m);
930 } else {
931 if(etag)
932 n = snnprintf(buf, n, size, "\r\nETag: \"%s\"", etag);
935 if(code != 304 && code != 412) {
936 n = snnprintf(buf, n, size,
937 "\r\nExpires: 0"
938 "\r\nCache-Control: no-cache"
939 "\r\nPragma: no-cache");
942 if(headers)
943 n = snnprint_n(buf, n, size,
944 headers->string, headers->length);
946 n = snnprintf(buf, n, size, "\r\n\r\n");
948 if(n < 0 || n >= size) {
949 do_log(L_ERROR, "Couldn't write error.\n");
950 dispose_chunk(body);
951 return -1;
954 if(code != 304 && do_body) {
955 if(m > 0) memcpy(buf + n, body, m);
956 n += m;
959 if(body)
960 dispose_chunk(body);
962 return n;
965 AtomListPtr
966 urlDecode(char *buf, int n)
968 char mybuf[500];
969 int i, j = 0;
970 AtomListPtr list;
971 AtomPtr atom;
973 list = makeAtomList(NULL, 0);
974 if(list == NULL)
975 return NULL;
977 i = 0;
978 while(i < n) {
979 if(buf[i] == '%') {
980 int a, b;
981 if(i + 3 > n)
982 goto fail;
983 a = h2i(buf[i + 1]);
984 b = h2i(buf[i + 2]);
985 if(a < 0 || b < 0)
986 goto fail;
987 mybuf[j++] = (char)((a << 4) | b);
988 i += 3;
989 if(j >= 500) goto fail;
990 } else if(buf[i] == '&') {
991 atom = internAtomN(mybuf, j);
992 if(atom == NULL)
993 goto fail;
994 atomListCons(atom, list);
995 j = 0;
996 i++;
997 } else {
998 mybuf[j++] = buf[i++];
999 if(j >= 500) goto fail;
1003 atom = internAtomN(mybuf, j);
1004 if(atom == NULL)
1005 goto fail;
1006 atomListCons(atom, list);
1007 return list;
1009 fail:
1010 destroyAtomList(list);
1011 return NULL;
1014 void
1015 httpTweakCachability(ObjectPtr object)
1017 int code = object->code;
1019 if((object->cache_control & CACHE_AUTHORIZATION) &&
1020 !(object->cache_control & CACHE_PUBLIC))
1021 object->cache_control |= (CACHE_NO_HIDDEN | OBJECT_LINEAR);
1023 /* This is not required by RFC 2616 -- but see RFC 3143 2.1.1. We
1024 manically avoid caching replies that we don't know how to
1025 handle, even if Expires or Cache-Control says otherwise. As to
1026 known uncacheable replies, we obey Cache-Control and default to
1027 allowing sharing but not caching. */
1028 if(code != 200 && code != 206 &&
1029 code != 300 && code != 301 && code != 302 && code != 303 &&
1030 code != 304 && code != 307 &&
1031 code != 403 && code != 404 && code != 405 && code != 416) {
1032 object->cache_control |=
1033 (CACHE_NO_HIDDEN | CACHE_MISMATCH | OBJECT_LINEAR);
1034 } else if(code != 200 && code != 206 &&
1035 code != 300 && code != 301 && code != 304 &&
1036 code != 410) {
1037 if(object->expires < 0 && !(object->cache_control & CACHE_PUBLIC)) {
1038 object->cache_control |= CACHE_NO_HIDDEN;
1040 } else if(dontCacheRedirects && (code == 301 || code == 302)) {
1041 object->cache_control |= CACHE_NO_HIDDEN;
1044 if(urlIsUncachable(object->key, object->key_size)) {
1045 object->cache_control |= CACHE_NO_HIDDEN;
1048 if((object->cache_control & CACHE_NO_STORE) != 0) {
1049 object->cache_control |= CACHE_NO_HIDDEN;
1052 if(object->cache_control & CACHE_VARY) {
1053 if(!object->etag || dontTrustVaryETag >= 2) {
1054 object->cache_control |= CACHE_MISMATCH;
1060 httpHeaderMatch(AtomPtr header, AtomPtr headers1, AtomPtr headers2)
1062 int rc1, b1, e1, rc2, b2, e2;
1064 /* Short cut if both sets of headers are identical */
1065 if(headers1 == headers2)
1066 return 1;
1068 rc1 = httpFindHeader(header, headers1->string, headers1->length,
1069 &b1, &e1);
1070 rc2 = httpFindHeader(header, headers2->string, headers2->length,
1071 &b2, &e2);
1073 if(rc1 == 0 && rc2 == 0)
1074 return 1;
1076 if(rc1 == 0 || rc2 == 0)
1077 return 0;
1079 if(e1 - b1 != e2 - b2)
1080 return 0;
1082 if(memcmp(headers1->string + b1, headers2->string + b2, e1 - b1) != 0)
1083 return 0;
1085 return 1;
1088 const char *
1089 getScrubbedProxyName(void)
1091 if(dontIdentifyToClients)
1092 return "polipo";
1094 return proxyName->string;