Handle localhost.localdomain in initHttp.
[polipo.git] / http.c
blobccab84775e2ef6ff1cb01c1e70d54d0bf3e9ba00
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;
32 int bigBufferSize = (32 * 1024);
34 AtomPtr authRealm = NULL;
35 AtomPtr authCredentials = NULL;
37 AtomPtr parentAuthCredentials = NULL;
39 AtomListPtr allowedClients = NULL;
40 NetAddressPtr allowedNets = NULL;
42 IntListPtr allowedPorts = NULL;
43 IntListPtr tunnelAllowedPorts = NULL;
44 int expectContinue = 1;
46 AtomPtr atom100Continue;
48 int disableVia = 1;
50 /* 0 means that all failures lead to errors. 1 means that failures to
51 connect are reported in a Warning header when stale objects are
52 served. 2 means that only missing data is fetched from the net,
53 stale data is served without revalidation (browser-side
54 Cache-Control directives are still honoured). 3 means that no
55 connections are ever attempted. */
57 int proxyOffline = 0;
58 int relaxTransparency = 0;
59 AtomPtr proxyAddress = NULL;
61 static int timeoutSetter(ConfigVariablePtr var, void *value);
63 void
64 preinitHttp()
66 proxyAddress = internAtom("127.0.0.1");
67 CONFIG_VARIABLE_SETTABLE(disableProxy, CONFIG_BOOLEAN, configIntSetter,
68 "Whether to be a web server only.");
69 CONFIG_VARIABLE_SETTABLE(proxyOffline, CONFIG_BOOLEAN, configIntSetter,
70 "Avoid contacting remote servers.");
71 CONFIG_VARIABLE_SETTABLE(relaxTransparency, CONFIG_TRISTATE,
72 configIntSetter,
73 "Avoid contacting remote servers.");
74 CONFIG_VARIABLE(proxyPort, CONFIG_INT,
75 "The TCP port on which the proxy listens.");
76 CONFIG_VARIABLE(proxyAddress, CONFIG_ATOM_LOWER,
77 "The IP address on which the proxy listens.");
78 CONFIG_VARIABLE_SETTABLE(proxyName, CONFIG_ATOM_LOWER, configAtomSetter,
79 "The name by which the proxy is known.");
80 CONFIG_VARIABLE_SETTABLE(clientTimeout, CONFIG_TIME,
81 timeoutSetter, "Client-side timeout.");
82 CONFIG_VARIABLE_SETTABLE(serverTimeout, CONFIG_TIME,
83 timeoutSetter, "Server-side timeout.");
84 CONFIG_VARIABLE(authRealm, CONFIG_ATOM,
85 "Authentication realm.");
86 CONFIG_VARIABLE(authCredentials, CONFIG_PASSWORD,
87 "username:password.");
88 CONFIG_VARIABLE(parentAuthCredentials, CONFIG_PASSWORD,
89 "username:password.");
90 CONFIG_VARIABLE(allowedClients, CONFIG_ATOM_LIST_LOWER,
91 "Networks from which clients are allowed to connect.");
92 CONFIG_VARIABLE(tunnelAllowedPorts, CONFIG_INT_LIST,
93 "Ports to which tunnelled connections are allowed.");
94 CONFIG_VARIABLE(allowedPorts, CONFIG_INT_LIST,
95 "Ports to which connections are allowed.");
96 CONFIG_VARIABLE(expectContinue, CONFIG_TRISTATE,
97 "Send Expect-Continue to servers.");
98 CONFIG_VARIABLE(bigBufferSize, CONFIG_INT,
99 "Size of big buffers (max size of headers).");
100 CONFIG_VARIABLE_SETTABLE(disableVia, CONFIG_BOOLEAN, configIntSetter,
101 "Don't use Via headers.");
102 preinitHttpParser();
105 static int
106 timeoutSetter(ConfigVariablePtr var, void *value)
108 configIntSetter(var, value);
109 if(clientTimeout <= serverTimeout)
110 clientTimeout = serverTimeout + 1;
111 return 1;
114 void
115 initHttp()
117 char *buf = get_chunk();
118 int namelen;
119 int n;
120 struct hostent *host;
122 initHttpParser();
124 atom100Continue = internAtom("100-continue");
126 if(clientTimeout <= serverTimeout) {
127 clientTimeout = serverTimeout + 1;
128 do_log(L_WARN, "Value of clientTimeout too small -- setting to %d.\n",
129 clientTimeout);
132 if(authCredentials != NULL && authRealm == NULL)
133 authRealm = internAtom("Polipo");
135 if(allowedClients) {
136 allowedNets = parseNetAddress(allowedClients);
137 if(allowedNets == NULL)
138 exit(1);
141 if(allowedPorts == NULL) {
142 allowedPorts = makeIntList(0);
143 if(allowedPorts == NULL) {
144 do_log(L_ERROR, "Couldn't allocate allowedPorts.\n");
145 exit(1);
147 intListCons(80, 100, allowedPorts);
148 intListCons(1024, 0xFFFF, allowedPorts);
151 if(tunnelAllowedPorts == NULL) {
152 tunnelAllowedPorts = makeIntList(0);
153 if(tunnelAllowedPorts == NULL) {
154 do_log(L_ERROR, "Couldn't allocate tunnelAllowedPorts.\n");
155 exit(1);
157 intListCons(22, 22, tunnelAllowedPorts);
158 intListCons(80, 80, tunnelAllowedPorts);
159 intListCons(443, 443, tunnelAllowedPorts);
160 intListCons(873, 873, tunnelAllowedPorts);
163 if(proxyName)
164 return;
166 if(buf == NULL) {
167 do_log(L_ERROR, "Couldn't allocate chunk for host name.\n");
168 goto fail;
171 n = gethostname(buf, CHUNK_SIZE);
172 if(n != 0) {
173 do_log_error(L_WARN, errno, "Gethostname");
174 strcpy(buf, "polipo");
175 goto success;
177 /* gethostname doesn't necessarily NUL-terminate on overflow */
178 buf[CHUNK_SIZE - 1] = '\0';
180 if(strcmp(buf, "(none)") == 0 ||
181 strcmp(buf, "localhost") == 0 ||
182 strcmp(buf, "localhost.localdomain") == 0) {
183 do_log(L_WARN, "Couldn't determine host name -- using ``polipo''.\n");
184 strcpy(buf, "polipo");
185 goto success;
188 if(strchr(buf, '.') != NULL)
189 goto success;
191 host = gethostbyname(buf);
192 if(host == NULL) {
193 goto success;
196 if(host->h_addrtype != AF_INET)
197 goto success;
199 host = gethostbyaddr(host->h_addr_list[0], host->h_length, AF_INET);
201 if(!host || !host->h_name || strcmp(host->h_name, "localhost") == 0 ||
202 strcmp(host->h_name, "localhost.localdomain") == 0)
203 goto success;
205 namelen = strlen(host->h_name);
206 if(namelen >= CHUNK_SIZE) {
207 do_log(L_ERROR, "Host name too long.\n");
208 goto success;
211 memcpy(buf, host->h_name, namelen + 1);
213 success:
214 proxyName = internAtom(buf);
215 if(proxyName == NULL) {
216 do_log(L_ERROR, "Couldn't allocate proxy name.\n");
217 goto fail;
219 dispose_chunk(buf);
220 return;
222 fail:
223 if(buf)
224 dispose_chunk(buf);
225 exit(1);
226 return;
230 httpSetTimeout(HTTPConnectionPtr connection, int secs)
232 TimeEventHandlerPtr new;
234 if(connection->timeout)
235 cancelTimeEvent(connection->timeout);
236 connection->timeout = NULL;
238 if(secs > 0) {
239 new = scheduleTimeEvent(secs, httpTimeoutHandler,
240 sizeof(connection), &connection);
241 if(!new) {
242 do_log(L_ERROR, "Couldn't schedule timeout for connection 0x%x\n",
243 (unsigned)connection);
244 return -1;
246 } else {
247 new = NULL;
250 connection->timeout = new;
251 return 1;
254 int
255 httpTimeoutHandler(TimeEventHandlerPtr event)
257 HTTPConnectionPtr connection = *(HTTPConnectionPtr*)event->data;
259 if(connection->fd >= 0) {
260 int rc;
261 rc = shutdown(connection->fd, 2);
262 if(rc < 0 && errno != ENOTCONN)
263 do_log_error(L_ERROR, errno, "Timeout: shutdown failed");
264 pokeFdEvent(connection->fd, -EDOTIMEOUT, POLLIN | POLLOUT);
266 connection->timeout = NULL;
267 return 1;
271 httpWriteObjectHeaders(char *buf, int offset, int len,
272 ObjectPtr object, int from, int to)
274 int n = offset;
276 if(from <= 0 && to < 0) {
277 if(object->length >= 0) {
278 n = snnprintf(buf, n, len,
279 "\r\nContent-Length: %d", object->length);
281 } else {
282 if(to >= 0) {
283 n = snnprintf(buf, n, len,
284 "\r\nContent-Length: %d", to - from);
288 if(from > 0 || to > 0) {
289 if(object->length >= 0) {
290 if(from >= to) {
291 n = snnprintf(buf, n, len,
292 "\r\nContent-Range: bytes */%d",
293 object->length);
294 } else {
295 n = snnprintf(buf, n, len,
296 "\r\nContent-Range: bytes %d-%d/%d",
297 from, to - 1,
298 object->length);
300 } else {
301 if(to >= 0) {
302 n = snnprintf(buf, n, len,
303 "\r\nContent-Range: bytes %d-/*",
304 from);
305 } else {
306 n = snnprintf(buf, n, len,
307 "\r\nContent-Range: bytes %d-%d/*",
308 from, to);
313 if(object->etag) {
314 n = snnprintf(buf, n, len, "\r\nETag: \"%s\"", object->etag);
316 if((object->flags & OBJECT_LOCAL) || object->date >= 0) {
317 n = snnprintf(buf, n, len, "\r\nDate: ");
318 n = format_time(buf, n, len,
319 (object->flags & OBJECT_LOCAL) ?
320 current_time.tv_sec : object->date);
321 if(n < 0)
322 goto fail;
325 if(object->last_modified >= 0) {
326 n = snnprintf(buf, n, len, "\r\nLast-Modified: ");
327 n = format_time(buf, n, len, object->last_modified);
328 if(n < 0)
329 goto fail;
332 if(object->expires >= 0) {
333 n = snnprintf(buf, n, len, "\r\nExpires: ");
334 n = format_time(buf, n, len, object->expires);
335 if(n < 0)
336 goto fail;
339 n = httpPrintCacheControl(buf, n, len,
340 object->cache_control, NULL);
341 if(n < 0)
342 goto fail;
344 if(!disableVia && object->via)
345 n = snnprintf(buf, n, len, "\r\nVia: %s", object->via->string);
347 if(object->headers)
348 n = snnprint_n(buf, n, len, object->headers->string,
349 object->headers->length);
351 if(n < len)
352 return n;
353 else
354 return -1;
356 fail:
357 return -1;
360 static int
361 cachePrintSeparator(char *buf, int offset, int len,
362 int subsequent)
364 int n = offset;
365 if(subsequent)
366 n = snnprintf(buf, offset, len, ", ");
367 else
368 n = snnprintf(buf, offset, len, "\r\nCache-Control: ");
369 return n;
373 httpPrintCacheControl(char *buf, int offset, int len,
374 int flags, CacheControlPtr cache_control)
376 int n = offset;
377 int sub = 0;
379 #define PRINT_SEP() \
380 do {\
381 n = cachePrintSeparator(buf, n, len, sub); \
382 sub = 1; \
383 } while(0)
385 if(cache_control)
386 flags |= cache_control->flags;
388 if(flags & CACHE_NO) {
389 PRINT_SEP();
390 n = snnprintf(buf, n, len, "no-cache");
392 if(flags & CACHE_PUBLIC) {
393 PRINT_SEP();
394 n = snnprintf(buf, n, len, "public");
396 if(flags & CACHE_PRIVATE) {
397 PRINT_SEP();
398 n = snnprintf(buf, n, len, "private");
400 if(flags & CACHE_NO_STORE) {
401 PRINT_SEP();
402 n = snnprintf(buf, n, len, "no-store");
404 if(flags & CACHE_NO_TRANSFORM) {
405 PRINT_SEP();
406 n = snnprintf(buf, n, len, "no-transform");
408 if(flags & CACHE_MUST_REVALIDATE) {
409 PRINT_SEP();
410 n = snnprintf(buf, n, len, "must-revalidate");
412 if(flags & CACHE_PROXY_REVALIDATE) {
413 PRINT_SEP();
414 n = snnprintf(buf, n, len, "proxy-revalidate");
416 if(flags & CACHE_ONLY_IF_CACHED) {
417 PRINT_SEP();
418 n = snnprintf(buf, n, len, "only-if-cached");
420 if(cache_control) {
421 if(cache_control->max_age >= 0) {
422 PRINT_SEP();
423 n = snnprintf(buf, n, len, "max-age=%d",
424 cache_control->max_age);
426 if(cache_control->s_maxage >= 0) {
427 PRINT_SEP();
428 n = snnprintf(buf, n, len, "s-maxage=%d",
429 cache_control->s_maxage);
431 if(cache_control->min_fresh > 0) {
432 PRINT_SEP();
433 n = snnprintf(buf, n, len, "min-fresh=%d",
434 cache_control->min_fresh);
436 if(cache_control->max_stale > 0) {
437 PRINT_SEP();
438 n = snnprintf(buf, n, len, "max-stale=%d",
439 cache_control->min_fresh);
442 return n;
443 #undef PRINT_SEP
446 char *
447 httpMessage(int code)
449 switch(code) {
450 case 200:
451 return "Okay";
452 case 206:
453 return "Partial content";
454 case 300:
455 return "Multiple choices";
456 case 301:
457 return "Moved permanently";
458 case 302:
459 return "Found";
460 case 303:
461 return "See other";
462 case 304:
463 return "Not changed";
464 case 307:
465 return "Temporary redirect";
466 case 401:
467 return "Authentication Required";
468 case 403:
469 return "Forbidden";
470 case 404:
471 return "Not found";
472 case 405:
473 return "Method not allowed";
474 case 407:
475 return "Proxy authentication required";
476 default:
477 return "Unknown error code";
482 htmlString(char *buf, int n, int len, char *s, int slen)
484 int i = 0;
485 while(i < slen && n + 5 < len) {
486 switch(s[i]) {
487 case '&':
488 buf[n++] = '&'; buf[n++] = 'a'; buf[n++] = 'm'; buf[n++] = 'p';
489 buf[n++] = ';';
490 break;
491 case '<':
492 buf[n++] = '&'; buf[n++] = 'l'; buf[n++] = 't'; buf[n++] = ';';
493 break;
494 case '>':
495 buf[n++] = '&'; buf[n++] = 'g'; buf[n++] = 't'; buf[n++] = ';';
496 break;
497 case '"':
498 buf[n++] = '&'; buf[n++] = 'q'; buf[n++] = 'u'; buf[n++] = 'o';
499 buf[n++] = 't'; buf[n++] = ';';
500 break;
501 case '\0':
502 break;
503 default:
504 buf[n++] = s[i];
506 i++;
508 return n;
511 void
512 htmlPrint(FILE *out, char *s, int slen)
514 int i;
515 for(i = 0; i < slen; i++) {
516 switch(s[i]) {
517 case '&':
518 fputs("&amp;", out);
519 break;
520 case '<':
521 fputs("&lt;", out);
522 break;
523 case '>':
524 fputs("&gt;", out);
525 break;
526 default:
527 fputc(s[i], out);
532 HTTPConnectionPtr
533 httpMakeConnection()
535 HTTPConnectionPtr connection;
536 connection = malloc(sizeof(HTTPConnectionRec));
537 if(connection == NULL)
538 return NULL;
539 connection->flags = 0;
540 connection->fd = -1;
541 connection->buf = NULL;
542 connection->len = 0;
543 connection->offset = 0;
544 connection->request = NULL;
545 connection->request_last = NULL;
546 connection->serviced = 0;
547 connection->version = HTTP_UNKNOWN;
548 connection->timeout = NULL;
549 connection->te = TE_IDENTITY;
550 connection->reqbuf = NULL;
551 connection->reqlen = 0;
552 connection->reqbegin = 0;
553 connection->reqoffset = 0;
554 connection->bodylen = -1;
555 connection->reqte = TE_IDENTITY;
556 connection->chunk_remaining = 0;
557 connection->server = NULL;
558 connection->pipelined = 0;
559 connection->connecting = 0;
560 connection->server = NULL;
561 return connection;
564 void
565 httpDestroyConnection(HTTPConnectionPtr connection)
567 assert(connection->flags == 0);
568 httpConnectionDestroyBuf(connection);
569 assert(!connection->request);
570 assert(!connection->request_last);
571 httpConnectionDestroyReqbuf(connection);
572 assert(!connection->timeout);
573 assert(!connection->server);
574 free(connection);
577 void
578 httpConnectionDestroyBuf(HTTPConnectionPtr connection)
580 if(connection->buf) {
581 if(connection->flags & CONN_BIGBUF)
582 free(connection->buf);
583 else
584 dispose_chunk(connection->buf);
586 connection->flags &= ~CONN_BIGBUF;
587 connection->buf = NULL;
590 void
591 httpConnectionDestroyReqbuf(HTTPConnectionPtr connection)
593 if(connection->reqbuf) {
594 if(connection->flags & CONN_BIGREQBUF)
595 free(connection->reqbuf);
596 else
597 dispose_chunk(connection->reqbuf);
599 connection->flags &= ~CONN_BIGREQBUF;
600 connection->reqbuf = NULL;
603 HTTPRequestPtr
604 httpMakeRequest()
606 HTTPRequestPtr request;
607 request = malloc(sizeof(HTTPRequestRec));
608 if(request == NULL)
609 return NULL;
610 request->flags = 0;
611 request->connection = NULL;
612 request->object = NULL;
613 request->method = METHOD_UNKNOWN;
614 request->from = 0;
615 request->to = -1;
616 request->cache_control = no_cache_control;
617 request->condition = NULL;
618 request->via = NULL;
619 request->chandler = NULL;
620 request->can_mutate = NULL;
621 request->error_code = 0;
622 request->error_message = NULL;
623 request->error_headers = NULL;
624 request->headers = NULL;
625 request->time0 = null_time;
626 request->time1 = null_time;
627 request->request = NULL;
628 request->next = NULL;
629 return request;
632 void
633 httpDestroyRequest(HTTPRequestPtr request)
635 if(request->object)
636 releaseObject(request->object);
637 if(request->condition)
638 httpDestroyCondition(request->condition);
639 releaseAtom(request->via);
640 assert(request->chandler == NULL);
641 releaseAtom(request->error_message);
642 releaseAtom(request->headers);
643 releaseAtom(request->error_headers);
644 assert(request->request == NULL);
645 assert(request->next == NULL);
646 free(request);
649 void
650 httpQueueRequest(HTTPConnectionPtr connection, HTTPRequestPtr request)
652 assert(request->next == NULL && request->connection == NULL);
653 request->connection = connection;
654 if(connection->request_last) {
655 assert(connection->request);
656 connection->request_last->next = request;
657 connection->request_last = request;
658 } else {
659 assert(!connection->request_last);
660 connection->request = request;
661 connection->request_last = request;
665 HTTPRequestPtr
666 httpDequeueRequest(HTTPConnectionPtr connection)
668 HTTPRequestPtr request = connection->request;
669 if(request) {
670 assert(connection->request_last);
671 connection->request = request->next;
672 if(!connection->request) connection->request_last = NULL;
673 request->next = NULL;
675 return request;
679 httpConnectionBigify(HTTPConnectionPtr connection)
681 char *bigbuf;
682 assert(!(connection->flags & CONN_BIGBUF));
684 if(bigBufferSize <= CHUNK_SIZE)
685 return 0;
687 bigbuf = malloc(bigBufferSize);
688 if(bigbuf == NULL)
689 return -1;
690 if(connection->len > 0)
691 memcpy(bigbuf, connection->buf, connection->len);
692 if(connection->buf)
693 dispose_chunk(connection->buf);
694 connection->buf = bigbuf;
695 connection->flags |= CONN_BIGBUF;
696 return 1;
700 httpConnectionBigifyReqbuf(HTTPConnectionPtr connection)
702 char *bigbuf;
703 assert(!(connection->flags & CONN_BIGREQBUF));
705 if(bigBufferSize <= CHUNK_SIZE)
706 return 0;
708 bigbuf = malloc(bigBufferSize);
709 if(bigbuf == NULL)
710 return -1;
711 if(connection->reqlen > 0)
712 memcpy(bigbuf, connection->reqbuf, connection->reqlen);
713 if(connection->reqbuf)
714 dispose_chunk(connection->reqbuf);
715 connection->reqbuf = bigbuf;
716 connection->flags |= CONN_BIGREQBUF;
717 return 1;
721 httpConnectionUnbigify(HTTPConnectionPtr connection)
723 char *buf;
724 assert(connection->flags & CONN_BIGBUF);
725 assert(connection->len < CHUNK_SIZE);
727 buf = get_chunk();
728 if(buf == NULL)
729 return -1;
730 if(connection->len > 0)
731 memcpy(buf, connection->buf, connection->len);
732 free(connection->buf);
733 connection->buf = buf;
734 connection->flags &= ~CONN_BIGBUF;
735 return 1;
739 httpConnectionUnbigifyReqbuf(HTTPConnectionPtr connection)
741 char *buf;
742 assert(connection->flags & CONN_BIGREQBUF);
743 assert(connection->reqlen < CHUNK_SIZE);
745 buf = get_chunk();
746 if(buf == NULL)
747 return -1;
748 if(connection->reqlen > 0)
749 memcpy(buf, connection->reqbuf, connection->reqlen);
750 free(connection->reqbuf);
751 connection->reqbuf = buf;
752 connection->flags &= ~CONN_BIGREQBUF;
753 return 1;
756 HTTPConditionPtr
757 httpMakeCondition()
759 HTTPConditionPtr condition;
760 condition = malloc(sizeof(HTTPConditionRec));
761 if(condition == NULL)
762 return NULL;
763 condition->ims = -1;
764 condition->inms = -1;
765 condition->im = NULL;
766 condition->inm = NULL;
767 condition->ifrange = NULL;
768 return condition;
771 void
772 httpDestroyCondition(HTTPConditionPtr condition)
774 if(condition->inm)
775 free(condition->inm);
776 if(condition->im)
777 free(condition->im);
778 if(condition->ifrange)
779 free(condition->ifrange);
780 free(condition);
784 httpCondition(ObjectPtr object, HTTPConditionPtr condition)
786 int rc = CONDITION_MATCH;
788 assert(!(object->flags & OBJECT_INITIAL));
790 if(!condition) return CONDITION_MATCH;
792 if(condition->ims >= 0) {
793 if(object->last_modified < 0 ||
794 condition->ims < object->last_modified)
795 return rc;
796 else
797 rc = CONDITION_NOT_MODIFIED;
800 if(condition->inms >= 0) {
801 if(object->last_modified < 0 ||
802 condition->inms >= object->last_modified)
803 return rc;
804 else
805 rc = CONDITION_FAILED;
808 if(condition->inm) {
809 if(!object->etag || strcmp(object->etag, condition->inm) != 0)
810 return rc;
811 else
812 rc = CONDITION_NOT_MODIFIED;
815 if(condition->im) {
816 if(!object->etag || strcmp(object->etag, condition->im) != 0)
817 rc = CONDITION_FAILED;
818 else
819 return rc;
822 return rc;
826 httpWriteErrorHeaders(char *buf, int size, int offset, int do_body,
827 int code, AtomPtr message, int close, AtomPtr headers,
828 char *url, int url_len, char *etag)
830 int n, m, i;
831 char *body;
832 char htmlMessage[100];
833 char timeStr[100];
835 assert(code != 0);
837 i = htmlString(htmlMessage, 0, 100, message->string, message->length);
838 if(i < 0)
839 strcpy(htmlMessage, "(Couldn't format message)");
840 else
841 htmlMessage[MIN(i, 99)] = '\0';
843 if(code != 304) {
844 body = get_chunk();
845 if(!body) {
846 do_log(L_ERROR, "Couldn't allocate body buffer.\n");
847 return -1;
849 m = snnprintf(body, 0, CHUNK_SIZE,
850 "<!DOCTYPE HTML PUBLIC "
851 "\"-//W3C//DTD HTML 4.01 Transitional//EN\" "
852 "\"http://www.w3.org/TR/html4/loose.dtd\">"
853 "\n<html><head>"
854 "\n<title>Proxy %s: %3d %s.</title>"
855 "\n</head><body>"
856 "\n<h1>%3d %s</h1>"
857 "\n<p>The following %s",
858 code >= 400 ? "error" : "result",
859 code, htmlMessage,
860 code, htmlMessage,
861 code >= 400 ?
862 "error occurred" :
863 "status was returned");
864 if(url_len > 0) {
865 m = snnprintf(body, m, CHUNK_SIZE,
866 " while trying to access <strong>");
867 m = htmlString(body, m, CHUNK_SIZE, url, url_len);
868 m = snnprintf(body, m, CHUNK_SIZE, "</strong>");
871 /*Mon, 24 Sep 2004 17:46:35 GMT*/
872 strftime(timeStr, sizeof(timeStr), "%a, %d %b %Y %H:%M:%S %Z",
873 localtime(&(current_time.tv_sec)));
875 m = snnprintf(body, m, CHUNK_SIZE,
876 ":<br><br>"
877 "\n<strong>%3d %s</strong></p>"
878 "\n<hr>Generated %s by Polipo on <em>%s:%d</em>."
879 "\n</body></html>\r\n",
880 code, htmlMessage,
881 timeStr, proxyName->string, proxyPort);
882 if(m <= 0 || m >= CHUNK_SIZE) {
883 do_log(L_ERROR, "Couldn't write error body.\n");
884 dispose_chunk(body);
885 return -1;
887 } else {
888 body = NULL;
889 m = 0;
892 n = snnprintf(buf, 0, size,
893 "HTTP/1.1 %3d %s"
894 "\r\nConnection: %s"
895 "\r\nDate: ",
896 code, atomString(message),
897 close ? "close" : "keep-alive");
898 n = format_time(buf, n, size, current_time.tv_sec);
899 if(code != 304) {
900 n = snnprintf(buf, n, size,
901 "\r\nContent-Type: text/html"
902 "\r\nContent-Length: %d", m);
903 } else {
904 if(etag)
905 n = snnprintf(buf, n, size, "\r\nETag: \"%s\"", etag);
908 if(code != 304 && code != 412) {
909 n = snnprintf(buf, n, size,
910 "\r\nExpires: 0"
911 "\r\nCache-Control: no-cache"
912 "\r\nPragma: no-cache");
915 if(headers)
916 n = snnprint_n(buf, n, size,
917 headers->string, headers->length);
919 n = snnprintf(buf, n, size, "\r\n\r\n");
921 if(n < 0 || n >= size) {
922 do_log(L_ERROR, "Couldn't write error.\n");
923 dispose_chunk(body);
924 return -1;
927 if(code != 304 && do_body) {
928 if(m > 0) memcpy(buf + n, body, m);
929 n += m;
932 if(body)
933 dispose_chunk(body);
935 return n;
938 AtomListPtr
939 urlDecode(char *buf, int n)
941 char mybuf[500];
942 int i, j = 0;
943 AtomListPtr list;
944 AtomPtr atom;
946 list = makeAtomList(NULL, 0);
947 if(list == NULL)
948 return NULL;
950 i = 0;
951 while(i < n) {
952 if(buf[i] == '%') {
953 int a, b;
954 if(i + 3 > n)
955 goto fail;
956 a = h2i(buf[i + 1]);
957 b = h2i(buf[i + 2]);
958 if(a < 0 || b < 0)
959 goto fail;
960 mybuf[j++] = (char)((a << 4) | b);
961 i += 3;
962 if(j > 500) goto fail;
963 } else if(buf[i] == '&') {
964 atom = internAtomN(mybuf, j);
965 if(atom == NULL)
966 goto fail;
967 atomListCons(atom, list);
968 j = 0;
969 i++;
970 } else {
971 mybuf[j++] = buf[i++];
972 if(j > 500) goto fail;
976 atom = internAtomN(mybuf, j);
977 if(atom == NULL)
978 goto fail;
979 atomListCons(atom, list);
980 return list;
982 fail:
983 destroyAtomList(list);
984 return NULL;