]> git.ipfire.org Git - thirdparty/squid.git/blob - src/errorpage.cc
Summary: Final MSVC fixups.
[thirdparty/squid.git] / src / errorpage.cc
1
2 /*
3 * $Id: errorpage.cc,v 1.190 2003/08/10 11:00:42 robertc Exp $
4 *
5 * DEBUG: section 4 Error Generation
6 * AUTHOR: Duane Wessels
7 *
8 * SQUID Web Proxy Cache http://www.squid-cache.org/
9 * ----------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from
12 * the Internet community; see the CONTRIBUTORS file for full
13 * details. Many organizations have provided support for Squid's
14 * development; see the SPONSORS file for full details. Squid is
15 * Copyrighted (C) 2001 by the Regents of the University of
16 * California; see the COPYRIGHT file for full details. Squid
17 * incorporates software developed and/or copyrighted by other
18 * sources; see the CREDITS file for full details.
19 *
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
24 *
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
29 *
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
33 *
34 */
35
36 /*
37 * Abstract: These routines are used to generate error messages to be
38 * sent to clients. The error type is used to select between
39 * the various message formats. (formats are stored in the
40 * Config.errorDirectory)
41 */
42
43 #include "squid.h"
44 #include "authenticate.h"
45 #include "Store.h"
46 #include "HttpReply.h"
47 #include "HttpRequest.h"
48 #include "MemObject.h"
49 #include "fde.h"
50
51 /* local types */
52
53 typedef struct
54 {
55 int id;
56 char *page_name;
57 }
58
59 ErrorDynamicPageInfo;
60
61 /* local constant and vars */
62
63 /*
64 * note: hard coded error messages are not appended with %S automagically
65 * to give you more control on the format
66 */
67
68 static const struct
69 {
70 int type; /* and page_id */
71 const char *text;
72 }
73
74 error_hard_text[] = {
75
76 {
77 ERR_SQUID_SIGNATURE,
78 "\n<BR clear=\"all\">\n"
79 "<HR noshade size=\"1px\">\n"
80 "<ADDRESS>\n"
81 "Generated %T by %h (%s)\n"
82 "</ADDRESS>\n"
83 "</BODY></HTML>\n"
84 },
85 {
86 TCP_RESET,
87 "reset"
88 }
89 };
90
91 static Vector<ErrorDynamicPageInfo *> ErrorDynamicPages;
92
93 /* local prototypes */
94
95 static const int error_hard_text_count = sizeof(error_hard_text) / sizeof(*error_hard_text);
96 static char **error_text = NULL;
97 static int error_page_count = 0;
98
99 static char *errorTryLoadText(const char *page_name, const char *dir);
100 static char *errorLoadText(const char *page_name);
101 static const char *errorFindHardText(err_type type);
102 static ErrorDynamicPageInfo *errorDynamicPageInfoCreate(int id, const char *page_name);
103 static void errorDynamicPageInfoDestroy(ErrorDynamicPageInfo * info);
104 static MemBuf errorBuildContent(ErrorState * err);
105 static int errorDump(ErrorState * err, MemBuf * mb);
106 static const char *errorConvert(char token, ErrorState * err);
107 static CWCB errorSendComplete;
108
109
110 err_type &operator++ (err_type &anErr)
111 {
112 int tmp = (int)anErr;
113 anErr = (err_type)(++tmp);
114 return anErr;
115 }
116
117 int operator - (err_type const &anErr, err_type const &anErr2)
118 {
119 return (int)anErr - (int)anErr2;
120 }
121
122 /*
123 * Function: errorInitialize
124 *
125 * Abstract: This function finds the error messages formats, and stores
126 * them in error_text[];
127 *
128 * Global effects:
129 * error_text[] - is modified
130 */
131 void
132 errorInitialize(void)
133 {
134 err_type i;
135 const char *text;
136 error_page_count = ERR_MAX + ErrorDynamicPages.size();
137 error_text = static_cast<char **>(xcalloc(error_page_count, sizeof(char *)));
138
139 for (i = ERR_NONE, ++i; i < error_page_count; ++i) {
140 safe_free(error_text[i]);
141 /* hard-coded ? */
142
143 if ((text = errorFindHardText(i)))
144 error_text[i] = xstrdup(text);
145 else if (i < ERR_MAX) {
146 /* precompiled ? */
147 error_text[i] = errorLoadText(err_type_str[i]);
148 } else {
149 /* dynamic */
150 ErrorDynamicPageInfo *info = ErrorDynamicPages.items[i - ERR_MAX];
151 assert(info && info->id == i && info->page_name);
152
153 if (strchr(info->page_name, ':') == NULL) {
154 /* Not on redirected errors... */
155 error_text[i] = errorLoadText(info->page_name);
156 }
157 }
158 }
159 }
160
161 void
162 errorClean(void)
163 {
164 if (error_text) {
165 int i;
166
167 for (i = ERR_NONE + 1; i < error_page_count; i++)
168 safe_free(error_text[i]);
169
170 safe_free(error_text);
171 }
172
173 while (ErrorDynamicPages.size())
174 errorDynamicPageInfoDestroy(ErrorDynamicPages.pop_back());
175
176 error_page_count = 0;
177 }
178
179 static const char *
180 errorFindHardText(err_type type)
181 {
182 int i;
183
184 for (i = 0; i < error_hard_text_count; i++)
185 if (error_hard_text[i].type == type)
186 return error_hard_text[i].text;
187
188 return NULL;
189 }
190
191
192 static char *
193 errorLoadText(const char *page_name)
194 {
195 /* test configured location */
196 char *text = errorTryLoadText(page_name, Config.errorDirectory);
197 /* test default location if failed */
198
199 if (!text && strcmp(Config.errorDirectory, DEFAULT_SQUID_ERROR_DIR))
200 text = errorTryLoadText(page_name, DEFAULT_SQUID_ERROR_DIR);
201
202 /* giving up if failed */
203 if (!text)
204 fatal("failed to find or read error text file.");
205
206 return text;
207 }
208
209 static char *
210 errorTryLoadText(const char *page_name, const char *dir)
211 {
212 int fd;
213 char path[MAXPATHLEN];
214 char buf[4096];
215 char *text;
216 ssize_t len;
217 MemBuf textbuf;
218
219 snprintf(path, sizeof(path), "%s/%s", dir, page_name);
220 fd = file_open(path, O_RDONLY | O_TEXT);
221
222 if (fd < 0) {
223 debug(4, 0) ("errorTryLoadText: '%s': %s\n", path, xstrerror());
224 return NULL;
225 }
226
227 memBufDefInit(&textbuf);
228
229 while((len = FD_READ_METHOD(fd, buf, sizeof(buf))) > 0) {
230 memBufAppend(&textbuf, buf, len);
231 }
232
233 if (len < 0) {
234 debug(4, 0) ("errorTryLoadText: failed to fully read: '%s': %s\n",
235 path, xstrerror());
236 }
237
238 file_close(fd);
239
240 if (strstr(textbuf.buf, "%s") == NULL)
241 memBufAppend(&textbuf, "%S", 2); /* add signature */
242
243 /* Shrink memory size down to exact size. MemBuf has a tencendy
244 * to be rather large..
245 */
246 text = xstrdup(textbuf.buf);
247
248 memBufClean(&textbuf);
249
250 return text;
251 }
252
253 static ErrorDynamicPageInfo *
254 errorDynamicPageInfoCreate(int id, const char *page_name)
255 {
256 ErrorDynamicPageInfo *info = new ErrorDynamicPageInfo;
257 info->id = id;
258 info->page_name = xstrdup(page_name);
259 return info;
260 }
261
262 static void
263 errorDynamicPageInfoDestroy(ErrorDynamicPageInfo * info)
264 {
265 assert(info);
266 xfree(info->page_name);
267 delete info;
268 }
269
270 static int
271 errorPageId(const char *page_name)
272 {
273 for (int i = 0; i < ERR_MAX; i++) {
274 if (strcmp(err_type_str[i], page_name) == 0)
275 return i;
276 }
277
278 for (size_t j = 0; j < ErrorDynamicPages.size(); j++) {
279 if (strcmp(ErrorDynamicPages.items[j]->page_name, page_name) == 0)
280 return j + ERR_MAX;
281 }
282
283 return ERR_NONE;
284 }
285
286 err_type
287 errorReservePageId(const char *page_name)
288 {
289 ErrorDynamicPageInfo *info;
290 int id = errorPageId(page_name);
291
292 if (id == ERR_NONE) {
293 info = errorDynamicPageInfoCreate(ERR_MAX + ErrorDynamicPages.size(), page_name);
294 ErrorDynamicPages.push_back(info);
295 id = info->id;
296 }
297
298 return (err_type)id;
299 }
300
301 static const char *
302 errorPageName(int pageId)
303 {
304 if (pageId >= ERR_NONE && pageId < ERR_MAX) /* common case */
305 return err_type_str[pageId];
306
307 if (pageId >= ERR_MAX && pageId - ERR_MAX < (ssize_t)ErrorDynamicPages.size())
308 return ErrorDynamicPages.items[pageId - ERR_MAX]->page_name;
309
310 return "ERR_UNKNOWN"; /* should not happen */
311 }
312
313 /*
314 * Function: errorCon
315 *
316 * Abstract: This function creates a ErrorState object.
317 */
318 ErrorState *
319 errorCon(err_type type, http_status status)
320 {
321 ErrorState *err;
322 err = cbdataAlloc(ErrorState);
323 err->page_id = type; /* has to be reset manually if needed */
324 err->type = type;
325 err->httpStatus = status;
326 return err;
327 }
328
329 /*
330 * Function: errorAppendEntry
331 *
332 * Arguments: err - This object is destroyed after use in this function.
333 *
334 * Abstract: This function generates a error page from the info contained
335 * by 'err' and then stores the text in the specified store
336 * entry. This function should only be called by ``server
337 * side routines'' which need to communicate errors to the
338 * client side. It should also be called from client_side.c
339 * because we now support persistent connections, and
340 * cannot assume that we can immediately write to the socket
341 * for an error.
342 */
343 void
344 errorAppendEntry(StoreEntry * entry, ErrorState * err)
345 {
346 HttpReply *rep;
347 MemObject *mem = entry->mem_obj;
348 assert(mem != NULL);
349 assert (entry->isEmpty());
350
351 if (entry->store_status != STORE_PENDING) {
352 /*
353 * If the entry is not STORE_PENDING, then no clients
354 * care about it, and we don't need to generate an
355 * error message
356 */
357 assert(EBIT_TEST(entry->flags, ENTRY_ABORTED));
358 assert(mem->nclients == 0);
359 errorStateFree(err);
360 return;
361 }
362
363 if (err->page_id == TCP_RESET) {
364 if (err->request) {
365 debug(4, 2) ("RSTing this reply\n");
366 err->request->flags.setResetTCP();
367 }
368 }
369
370 storeLockObject(entry);
371 storeBuffer(entry);
372 rep = errorBuildReply(err);
373 /* Add authentication header */
374 /* TODO: alter errorstate to be accel on|off aware. The 0 on the next line
375 * depends on authenticate behaviour: all schemes to date send no extra data
376 * on 407/401 responses, and do not check the accel state on 401/407 responses
377 */
378 authenticateFixHeader(rep, err->auth_user_request, err->request, 0, 1);
379 httpReplySwapOut(rep, entry);
380 EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
381 storeBufferFlush(entry);
382 entry->complete();
383 storeNegativeCache(entry);
384 storeReleaseRequest(entry);
385 storeUnlockObject(entry);
386 errorStateFree(err);
387 }
388
389 /*
390 * Function: errorSend
391 *
392 * Arguments: err - This object is destroyed after use in this function.
393 *
394 * Abstract: This function generates a error page from the info contained
395 * by 'err' and then sends it to the client.
396 * The callback function errorSendComplete() is called after
397 * the page has been written to the client socket (fd).
398 * errorSendComplete() deallocates 'err'. We need to add
399 * 'err' to the cbdata because comm_write() requires it
400 * for all callback data pointers.
401 *
402 * Note, normally errorSend() should only be called from
403 * routines in ssl.c and pass.c, where we don't have any
404 * StoreEntry's. In client_side.c we must allocate a StoreEntry
405 * for errors and use errorAppendEntry() to account for
406 * persistent/pipeline connections.
407 */
408 void
409 errorSend(int fd, ErrorState * err)
410 {
411 HttpReply *rep;
412 debug(4, 3) ("errorSend: FD %d, err=%p\n", fd, err);
413 assert(fd >= 0);
414 /*
415 * ugh, this is how we make sure error codes get back to
416 * the client side for logging and error tracking.
417 */
418
419 if (err->request)
420 err->request->errType = err->type;
421
422 /* moved in front of errorBuildBuf @?@ */
423 err->flags.flag_cbdata = 1;
424
425 rep = errorBuildReply(err);
426
427 comm_old_write_mbuf(fd, httpReplyPack(rep), errorSendComplete, err);
428
429 httpReplyDestroy(rep);
430 }
431
432 /*
433 * Function: errorSendComplete
434 *
435 * Abstract: Called by commHandleWrite() after data has been written
436 * to the client socket.
437 *
438 * Note: If there is a callback, the callback is responsible for
439 * closeing the FD, otherwise we do it ourseves.
440 */
441 static void
442 errorSendComplete(int fd, char *bufnotused, size_t size, comm_err_t errflag, void *data)
443 {
444 ErrorState *err = static_cast<ErrorState *>(data);
445 debug(4, 3) ("errorSendComplete: FD %d, size=%ld\n", fd, (long int) size);
446
447 if (errflag != COMM_ERR_CLOSING) {
448 if (err->callback) {
449 debug(4, 3) ("errorSendComplete: callback\n");
450 err->callback(fd, err->callback_data, size);
451 } else {
452 comm_close(fd);
453 debug(4, 3) ("errorSendComplete: comm_close\n");
454 }
455 }
456
457 errorStateFree(err);
458 }
459
460 void
461 errorStateFree(ErrorState * err)
462 {
463 requestUnlink(err->request);
464 safe_free(err->redirect_url);
465 safe_free(err->url);
466 safe_free(err->host);
467 safe_free(err->dnsserver_msg);
468 safe_free(err->request_hdrs);
469 wordlistDestroy(&err->ftp.server_msg);
470 safe_free(err->ftp.request);
471 safe_free(err->ftp.reply);
472
473 if (err->auth_user_request)
474 authenticateAuthUserRequestUnlock(err->auth_user_request);
475
476 err->auth_user_request = NULL;
477
478 safe_free(err->err_msg);
479
480 cbdataFree(err);
481 }
482
483 static int
484 errorDump(ErrorState * err, MemBuf * mb)
485 {
486 HttpRequest *r = err->request;
487 MemBuf str = MemBufNULL;
488 const char *p = NULL; /* takes priority over mb if set */
489 memBufReset(&str);
490 /* email subject line */
491 memBufPrintf(&str, "CacheErrorInfo - %s", errorPageName(err->type));
492 memBufPrintf(mb, "?subject=%s", rfc1738_escape_part(str.buf));
493 memBufReset(&str);
494 /* email body */
495 memBufPrintf(&str, "CacheHost: %s\r\n", getMyHostname());
496 /* - Err Msgs */
497 memBufPrintf(&str, "ErrPage: %s\r\n", errorPageName(err->type));
498
499 if (err->xerrno) {
500 memBufPrintf(&str, "Err: (%d) %s\r\n", err->xerrno, strerror(err->xerrno));
501 } else {
502 memBufPrintf(&str, "Err: [none]\r\n");
503 }
504
505 if (authenticateAuthUserRequestMessage(err->auth_user_request)) {
506 memBufPrintf(&str, "extAuth ErrMsg: %s\r\n", authenticateAuthUserRequestMessage(err->auth_user_request));
507 }
508
509 if (err->dnsserver_msg) {
510 memBufPrintf(&str, "DNS Server ErrMsg: %s\r\n", err->dnsserver_msg);
511 }
512
513 /* - TimeStamp */
514 memBufPrintf(&str, "TimeStamp: %s\r\n\r\n", mkrfc1123(squid_curtime));
515
516 /* - IP stuff */
517 memBufPrintf(&str, "ClientIP: %s\r\n", inet_ntoa(err->src_addr));
518
519 if (err->host) {
520 memBufPrintf(&str, "ServerIP: %s\r\n", err->host);
521 }
522
523 memBufPrintf(&str, "\r\n");
524 /* - HTTP stuff */
525 memBufPrintf(&str, "HTTP Request:\r\n");
526
527 if (NULL != r) {
528 Packer p;
529 memBufPrintf(&str, "%s %s HTTP/%d.%d\n",
530 RequestMethodStr[r->method],
531 r->urlpath.size() ? r->urlpath.buf() : "/",
532 r->http_ver.major, r->http_ver.minor);
533 packerToMemInit(&p, &str);
534 httpHeaderPackInto(&r->header, &p);
535 packerClean(&p);
536 } else if (err->request_hdrs) {
537 p = err->request_hdrs;
538 } else {
539 p = "[none]";
540 }
541
542 memBufPrintf(&str, "\r\n");
543 /* - FTP stuff */
544
545 if (err->ftp.request) {
546 memBufPrintf(&str, "FTP Request: %s\r\n", err->ftp.request);
547 memBufPrintf(&str, "FTP Reply: %s\r\n", err->ftp.reply);
548 memBufPrintf(&str, "FTP Msg: ");
549 wordlistCat(err->ftp.server_msg, &str);
550 memBufPrintf(&str, "\r\n");
551 }
552
553 memBufPrintf(&str, "\r\n");
554 memBufPrintf(mb, "&body=%s", rfc1738_escape_part(str.buf));
555 memBufClean(&str);
556 return 0;
557 }
558
559 #define CVT_BUF_SZ 512
560
561 /*
562 * B - URL with FTP %2f hack x
563 * c - Squid error code x
564 * d - seconds elapsed since request received x
565 * e - errno x
566 * E - strerror() x
567 * f - FTP request line x
568 * F - FTP reply line x
569 * g - FTP server message x
570 * h - cache hostname x
571 * H - server host name x
572 * i - client IP address x
573 * I - server IP address x
574 * L - HREF link for more info/contact x
575 * M - Request Method x
576 * m - Error message returned by external Auth. x
577 * p - URL port # x
578 * P - Protocol x
579 * R - Full HTTP Request x
580 * S - squid signature from ERR_SIGNATURE x
581 * s - caching proxy software with version x
582 * t - local time x
583 * T - UTC x
584 * U - URL without password x
585 * u - URL with password x
586 * w - cachemgr email address x
587 * W - error data (to be included in the mailto links)
588 * z - dns server error message x
589 * Z - Preformatted error message x
590 */
591
592 static const char *
593 errorConvert(char token, ErrorState * err)
594 {
595 HttpRequest *r = err->request;
596 static MemBuf mb = MemBufNULL;
597 const char *p = NULL; /* takes priority over mb if set */
598 int do_quote = 1;
599
600 memBufReset(&mb);
601
602 switch (token) {
603
604 case 'B':
605 p = r ? ftpUrlWith2f(r) : "[no URL]";
606 break;
607
608 case 'c':
609 p = errorPageName(err->type);
610 break;
611
612 case 'e':
613 memBufPrintf(&mb, "%d", err->xerrno);
614 break;
615
616 case 'E':
617
618 if (err->xerrno)
619 memBufPrintf(&mb, "(%d) %s", err->xerrno, strerror(err->xerrno));
620 else
621 memBufPrintf(&mb, "[No Error]");
622
623 break;
624
625 case 'f':
626 /* FTP REQUEST LINE */
627 if (err->ftp.request)
628 p = err->ftp.request;
629 else
630 p = "nothing";
631
632 break;
633
634 case 'F':
635 /* FTP REPLY LINE */
636 if (err->ftp.request)
637 p = err->ftp.reply;
638 else
639 p = "nothing";
640
641 break;
642
643 case 'g':
644 /* FTP SERVER MESSAGE */
645 wordlistCat(err->ftp.server_msg, &mb);
646
647 break;
648
649 case 'h':
650 memBufPrintf(&mb, "%s", getMyHostname());
651
652 break;
653
654 case 'H':
655 p = r ? r->host : "[unknown host]";
656
657 break;
658
659 case 'i':
660 memBufPrintf(&mb, "%s", inet_ntoa(err->src_addr));
661
662 break;
663
664 case 'I':
665 if (err->host) {
666 memBufPrintf(&mb, "%s", err->host);
667 } else
668 p = "[unknown]";
669
670 break;
671
672 case 'L':
673 if (Config.errHtmlText) {
674 memBufPrintf(&mb, "%s", Config.errHtmlText);
675 do_quote = 0;
676 } else
677 p = "[not available]";
678
679 break;
680
681 case 'm':
682 p = authenticateAuthUserRequestMessage(err->auth_user_request) ? authenticateAuthUserRequestMessage(err->auth_user_request) : "[not available]";
683
684 break;
685
686 case 'M':
687 p = r ? RequestMethodStr[r->method] : "[unkown method]";
688
689 break;
690
691 case 'o':
692 p = external_acl_message ? external_acl_message : "[not available]";
693
694 break;
695
696 case 'p':
697 if (r) {
698 memBufPrintf(&mb, "%d", (int) r->port);
699 } else {
700 p = "[unknown port]";
701 }
702
703 break;
704
705 case 'P':
706 p = r ? ProtocolStr[r->protocol] : "[unkown protocol]";
707 break;
708
709 case 'R':
710
711 if (NULL != r) {
712 Packer p;
713 memBufPrintf(&mb, "%s %s HTTP/%d.%d\n",
714 RequestMethodStr[r->method],
715 r->urlpath.size() ? r->urlpath.buf() : "/",
716 r->http_ver.major, r->http_ver.minor);
717 packerToMemInit(&p, &mb);
718 httpHeaderPackInto(&r->header, &p);
719 packerClean(&p);
720 } else if (err->request_hdrs) {
721 p = err->request_hdrs;
722 } else {
723 p = "[no request]";
724 }
725
726 break;
727
728 case 's':
729 p = full_appname_string;
730 break;
731
732 case 'S':
733 /* signature may contain %-escapes, recursion */
734
735 if (err->page_id != ERR_SQUID_SIGNATURE) {
736 const int saved_id = err->page_id;
737 MemBuf sign_mb;
738 err->page_id = ERR_SQUID_SIGNATURE;
739 sign_mb = errorBuildContent(err);
740 memBufPrintf(&mb, "%s", sign_mb.buf);
741 memBufClean(&sign_mb);
742 err->page_id = saved_id;
743 do_quote = 0;
744 } else {
745 /* wow, somebody put %S into ERR_SIGNATURE, stop recursion */
746 p = "[%S]";
747 }
748
749 break;
750
751 case 't':
752 memBufPrintf(&mb, "%s", mkhttpdlogtime(&squid_curtime));
753 break;
754
755 case 'T':
756 memBufPrintf(&mb, "%s", mkrfc1123(squid_curtime));
757 break;
758
759 case 'U':
760 p = r ? urlCanonicalClean(r) : err->url ? err->url : "[no URL]";
761 break;
762
763 case 'u':
764 p = r ? urlCanonical(r) : err->url ? err->url : "[no URL]";
765 break;
766
767 case 'w':
768
769 if (Config.adminEmail)
770 memBufPrintf(&mb, "%s", Config.adminEmail);
771 else
772 p = "[unknown]";
773
774 break;
775
776 case 'W':
777 if (Config.adminEmail && Config.onoff.emailErrData)
778 errorDump(err, &mb);
779
780 break;
781
782 case 'z':
783 if (err->dnsserver_msg)
784 p = err->dnsserver_msg;
785 else
786 p = "[unknown]";
787
788 break;
789
790 case 'Z':
791 if (err->err_msg)
792 p = err->err_msg;
793 else
794 p = "[unknown]";
795
796 break;
797
798 case '%':
799 p = "%";
800
801 break;
802
803 default:
804 memBufPrintf(&mb, "%%%c", token);
805
806 break;
807 }
808
809 if (!p)
810 p = mb.buf; /* do not use mb after this assignment! */
811
812 assert(p);
813
814 debug(4, 3) ("errorConvert: %%%c --> '%s'\n", token, p);
815
816 if (do_quote)
817 p = html_quote(p);
818
819 return p;
820 }
821
822 /* allocates and initializes an error response */
823 HttpReply *
824 errorBuildReply(ErrorState * err)
825 {
826 HttpReply *rep = httpReplyCreate();
827 const char *name = errorPageName(err->page_id);
828 http_version_t version;
829 /* no LMT for error pages; error pages expire immediately */
830 httpBuildVersion(&version, 1, 0);
831
832 if (strchr(name, ':')) {
833 /* Redirection */
834 char *quoted_url = rfc1738_escape_part(errorConvert('u', err));
835 httpReplySetHeaders(rep, version, HTTP_MOVED_TEMPORARILY, NULL, "text/html", 0, 0, squid_curtime);
836 httpHeaderPutStrf(&rep->header, HDR_LOCATION, name, quoted_url);
837 httpHeaderPutStrf(&rep->header, HDR_X_SQUID_ERROR, "%d %s\n", err->httpStatus, "Access Denied");
838 } else {
839 MemBuf content = errorBuildContent(err);
840 httpReplySetHeaders(rep, version, err->httpStatus, NULL, "text/html", content.size, 0, squid_curtime);
841 /*
842 * include some information for downstream caches. Implicit
843 * replaceable content. This isn't quite sufficient. xerrno is not
844 * necessarily meaningful to another system, so we really should
845 * expand it. Additionally, we should identify ourselves. Someone
846 * might want to know. Someone _will_ want to know OTOH, the first
847 * X-CACHE-MISS entry should tell us who.
848 */
849 httpHeaderPutStrf(&rep->header, HDR_X_SQUID_ERROR, "%s %d",
850 name, err->xerrno);
851 httpBodySet(&rep->body, &content);
852 /* do not memBufClean() the content, it was absorbed by httpBody */
853 }
854
855 return rep;
856 }
857
858 static MemBuf
859 errorBuildContent(ErrorState * err)
860 {
861 MemBuf content;
862 const char *m;
863 const char *p;
864 const char *t;
865 assert(err != NULL);
866 assert(err->page_id > ERR_NONE && err->page_id < error_page_count);
867 memBufDefInit(&content);
868 m = error_text[err->page_id];
869 assert(m);
870
871 while ((p = strchr(m, '%'))) {
872 memBufAppend(&content, m, p - m); /* copy */
873 t = errorConvert(*++p, err); /* convert */
874 memBufPrintf(&content, "%s", t); /* copy */
875 m = p + 1; /* advance */
876 }
877
878 if (*m)
879 memBufPrintf(&content, "%s", m); /* copy tail */
880
881 assert((size_t)content.size == strlen(content.buf));
882
883 return content;
884 }