]> git.ipfire.org Git - thirdparty/squid.git/blob - src/errorpage.cc
fix headers to allow inclusion into C++ source
[thirdparty/squid.git] / src / errorpage.cc
1
2 /*
3 * $Id: errorpage.cc,v 1.177 2002/09/15 06:40:57 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
45
46 /* local types */
47
48 typedef struct {
49 int id;
50 char *page_name;
51 } ErrorDynamicPageInfo;
52
53 /* local constant and vars */
54
55 /*
56 * note: hard coded error messages are not appended with %S automagically
57 * to give you more control on the format
58 */
59 static const struct {
60 int type; /* and page_id */
61 const char *text;
62 } error_hard_text[] = {
63
64 {
65 ERR_SQUID_SIGNATURE,
66 "\n<BR clear=\"all\">\n"
67 "<HR noshade size=\"1px\">\n"
68 "<ADDRESS>\n"
69 "Generated %T by %h (%s)\n"
70 "</ADDRESS>\n"
71 "</BODY></HTML>\n"
72 },
73 {
74 TCP_RESET,
75 "reset"
76 }
77 };
78
79 static Stack ErrorDynamicPages;
80
81 /* local prototypes */
82
83 static const int error_hard_text_count = sizeof(error_hard_text) / sizeof(*error_hard_text);
84 static char **error_text = NULL;
85 static int error_page_count = 0;
86
87 static char *errorTryLoadText(const char *page_name, const char *dir);
88 static char *errorLoadText(const char *page_name);
89 static const char *errorFindHardText(err_type type);
90 static ErrorDynamicPageInfo *errorDynamicPageInfoCreate(int id, const char *page_name);
91 static void errorDynamicPageInfoDestroy(ErrorDynamicPageInfo * info);
92 static MemBuf errorBuildContent(ErrorState * err);
93 static int errorDump(ErrorState * err, MemBuf * mb);
94 static const char *errorConvert(char token, ErrorState * err);
95 static CWCB errorSendComplete;
96
97 /*
98 * Function: errorInitialize
99 *
100 * Abstract: This function finds the error messages formats, and stores
101 * them in error_text[];
102 *
103 * Global effects:
104 * error_text[] - is modified
105 */
106 void
107 errorInitialize(void)
108 {
109 err_type i;
110 const char *text;
111 error_page_count = ERR_MAX + ErrorDynamicPages.count;
112 error_text = xcalloc(error_page_count, sizeof(char *));
113 for (i = ERR_NONE, i++; i < error_page_count; i++) {
114 safe_free(error_text[i]);
115 /* hard-coded ? */
116 if ((text = errorFindHardText(i)))
117 error_text[i] = xstrdup(text);
118 else if (i < ERR_MAX) {
119 /* precompiled ? */
120 error_text[i] = errorLoadText(err_type_str[i]);
121 } else {
122 /* dynamic */
123 ErrorDynamicPageInfo *info = ErrorDynamicPages.items[i - ERR_MAX];
124 assert(info && info->id == i && info->page_name);
125 if (strchr(info->page_name, ':') == NULL) {
126 /* Not on redirected errors... */
127 error_text[i] = errorLoadText(info->page_name);
128 }
129 }
130 }
131 }
132
133 void
134 errorClean(void)
135 {
136 if (error_text) {
137 int i;
138 for (i = ERR_NONE + 1; i < error_page_count; i++)
139 safe_free(error_text[i]);
140 safe_free(error_text);
141 }
142 while (ErrorDynamicPages.count)
143 errorDynamicPageInfoDestroy(stackPop(&ErrorDynamicPages));
144 error_page_count = 0;
145 }
146
147 static const char *
148 errorFindHardText(err_type type)
149 {
150 int i;
151 for (i = 0; i < error_hard_text_count; i++)
152 if (error_hard_text[i].type == type)
153 return error_hard_text[i].text;
154 return NULL;
155 }
156
157
158 static char *
159 errorLoadText(const char *page_name)
160 {
161 /* test configured location */
162 char *text = errorTryLoadText(page_name, Config.errorDirectory);
163 /* test default location if failed */
164 if (!text && strcmp(Config.errorDirectory, DEFAULT_SQUID_ERROR_DIR))
165 text = errorTryLoadText(page_name, DEFAULT_SQUID_ERROR_DIR);
166 /* giving up if failed */
167 if (!text)
168 fatal("failed to find or read error text file.");
169 return text;
170 }
171
172 static char *
173 errorTryLoadText(const char *page_name, const char *dir)
174 {
175 int fd;
176 char path[MAXPATHLEN];
177 struct stat sb;
178 char *text;
179
180 snprintf(path, sizeof(path), "%s/%s", dir, page_name);
181 fd = file_open(path, O_RDONLY | O_TEXT);
182 if (fd < 0 || fstat(fd, &sb) < 0) {
183 debug(4, 0) ("errorTryLoadText: '%s': %s\n", path, xstrerror());
184 if (fd >= 0)
185 file_close(fd);
186 return NULL;
187 }
188 text = xcalloc(sb.st_size + 2 + 1, 1); /* 2 == space for %S */
189 if (FD_READ_METHOD(fd, text, sb.st_size) != sb.st_size) {
190 debug(4, 0) ("errorTryLoadText: failed to fully read: '%s': %s\n",
191 path, xstrerror());
192 xfree(text);
193 text = NULL;
194 }
195 file_close(fd);
196 if (strstr(text, "%s") == NULL)
197 strcat(text, "%S"); /* add signature */
198 return text;
199 }
200
201 static ErrorDynamicPageInfo *
202 errorDynamicPageInfoCreate(int id, const char *page_name)
203 {
204 ErrorDynamicPageInfo *info = xcalloc(1, sizeof(ErrorDynamicPageInfo));
205 info->id = id;
206 info->page_name = xstrdup(page_name);
207 return info;
208 }
209
210 static void
211 errorDynamicPageInfoDestroy(ErrorDynamicPageInfo * info)
212 {
213 assert(info);
214 xfree(info->page_name);
215 xfree(info);
216 }
217
218 static int
219 errorPageId(const char *page_name)
220 {
221 int i;
222 for (i = 0; i < ERR_MAX; i++) {
223 if (strcmp(err_type_str[i], page_name) == 0)
224 return i;
225 }
226 for (i = 0; i < ErrorDynamicPages.count; i++) {
227 if (strcmp(((ErrorDynamicPageInfo *) ErrorDynamicPages.items[i])->page_name, page_name) == 0)
228 return i + ERR_MAX;
229 }
230 return ERR_NONE;
231 }
232
233 int
234 errorReservePageId(const char *page_name)
235 {
236 ErrorDynamicPageInfo *info;
237 int id = errorPageId(page_name);
238 if (id == ERR_NONE) {
239 info = errorDynamicPageInfoCreate(ERR_MAX + ErrorDynamicPages.count, page_name);
240 stackPush(&ErrorDynamicPages, info);
241 id = info->id;
242 }
243 return id;
244 }
245
246 static const char *
247 errorPageName(int pageId)
248 {
249 if (pageId >= ERR_NONE && pageId < ERR_MAX) /* common case */
250 return err_type_str[pageId];
251 if (pageId >= ERR_MAX && pageId - ERR_MAX < ErrorDynamicPages.count)
252 return ((ErrorDynamicPageInfo *) ErrorDynamicPages.
253 items[pageId - ERR_MAX])->page_name;
254 return "ERR_UNKNOWN"; /* should not happen */
255 }
256
257 /*
258 * Function: errorCon
259 *
260 * Abstract: This function creates a ErrorState object.
261 */
262 ErrorState *
263 errorCon(err_type type, http_status status)
264 {
265 ErrorState *err;
266 err = cbdataAlloc(ErrorState);
267 err->page_id = type; /* has to be reset manually if needed */
268 err->type = type;
269 err->httpStatus = status;
270 return err;
271 }
272
273 /*
274 * Function: errorAppendEntry
275 *
276 * Arguments: err - This object is destroyed after use in this function.
277 *
278 * Abstract: This function generates a error page from the info contained
279 * by 'err' and then stores the text in the specified store
280 * entry. This function should only be called by ``server
281 * side routines'' which need to communicate errors to the
282 * client side. It should also be called from client_side.c
283 * because we now support persistent connections, and
284 * cannot assume that we can immediately write to the socket
285 * for an error.
286 */
287 void
288 errorAppendEntry(StoreEntry * entry, ErrorState * err)
289 {
290 HttpReply *rep;
291 MemObject *mem = entry->mem_obj;
292 assert(mem != NULL);
293 assert(mem->inmem_hi == 0);
294 if (entry->store_status != STORE_PENDING) {
295 /*
296 * If the entry is not STORE_PENDING, then no clients
297 * care about it, and we don't need to generate an
298 * error message
299 */
300 assert(EBIT_TEST(entry->flags, ENTRY_ABORTED));
301 assert(mem->nclients == 0);
302 errorStateFree(err);
303 return;
304 }
305 if (err->page_id == TCP_RESET) {
306 if (err->request) {
307 debug(4, 2) ("RSTing this reply\n");
308 err->request->flags.reset_tcp = 1;
309 }
310 }
311 storeLockObject(entry);
312 storeBuffer(entry);
313 rep = errorBuildReply(err);
314 /* Add authentication header */
315 /* TODO: alter errorstate to be accel on|off aware. The 0 on the next line
316 * depends on authenticate behaviour: all schemes to date send no extra data
317 * on 407/401 responses, and do not check the accel state on 401/407 responses
318 */
319 authenticateFixHeader(rep, err->auth_user_request, err->request, 0, 1);
320 httpReplySwapOut(rep, entry);
321 httpReplyAbsorb(mem->reply, rep);
322 EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
323 storeBufferFlush(entry);
324 storeComplete(entry);
325 storeNegativeCache(entry);
326 storeReleaseRequest(entry);
327 storeUnlockObject(entry);
328 errorStateFree(err);
329 }
330
331 /*
332 * Function: errorSend
333 *
334 * Arguments: err - This object is destroyed after use in this function.
335 *
336 * Abstract: This function generates a error page from the info contained
337 * by 'err' and then sends it to the client.
338 * The callback function errorSendComplete() is called after
339 * the page has been written to the client socket (fd).
340 * errorSendComplete() deallocates 'err'. We need to add
341 * 'err' to the cbdata because comm_write() requires it
342 * for all callback data pointers.
343 *
344 * Note, normally errorSend() should only be called from
345 * routines in ssl.c and pass.c, where we don't have any
346 * StoreEntry's. In client_side.c we must allocate a StoreEntry
347 * for errors and use errorAppendEntry() to account for
348 * persistent/pipeline connections.
349 */
350 void
351 errorSend(int fd, ErrorState * err)
352 {
353 HttpReply *rep;
354 debug(4, 3) ("errorSend: FD %d, err=%p\n", fd, err);
355 assert(fd >= 0);
356 /*
357 * ugh, this is how we make sure error codes get back to
358 * the client side for logging and error tracking.
359 */
360 if (err->request)
361 err->request->errType = err->type;
362 /* moved in front of errorBuildBuf @?@ */
363 err->flags.flag_cbdata = 1;
364 rep = errorBuildReply(err);
365 comm_write_mbuf(fd, httpReplyPack(rep), errorSendComplete, err);
366 httpReplyDestroy(rep);
367 }
368
369 /*
370 * Function: errorSendComplete
371 *
372 * Abstract: Called by commHandleWrite() after data has been written
373 * to the client socket.
374 *
375 * Note: If there is a callback, the callback is responsible for
376 * closeing the FD, otherwise we do it ourseves.
377 */
378 static void
379 errorSendComplete(int fd, char *bufnotused, size_t size, comm_err_t errflag, void *data)
380 {
381 ErrorState *err = data;
382 debug(4, 3) ("errorSendComplete: FD %d, size=%ld\n", fd, (long int) size);
383 if (errflag != COMM_ERR_CLOSING) {
384 if (err->callback) {
385 debug(4, 3) ("errorSendComplete: callback\n");
386 err->callback(fd, err->callback_data, size);
387 } else {
388 comm_close(fd);
389 debug(4, 3) ("errorSendComplete: comm_close\n");
390 }
391 }
392 errorStateFree(err);
393 }
394
395 void
396 errorStateFree(ErrorState * err)
397 {
398 requestUnlink(err->request);
399 safe_free(err->redirect_url);
400 safe_free(err->url);
401 safe_free(err->host);
402 safe_free(err->dnsserver_msg);
403 safe_free(err->request_hdrs);
404 wordlistDestroy(&err->ftp.server_msg);
405 safe_free(err->ftp.request);
406 safe_free(err->ftp.reply);
407 if (err->auth_user_request)
408 authenticateAuthUserRequestUnlock(err->auth_user_request);
409 err->auth_user_request = NULL;
410 cbdataFree(err);
411 }
412
413 static int
414 errorDump(ErrorState * err, MemBuf * mb)
415 {
416 request_t *r = err->request;
417 MemBuf str = MemBufNULL;
418 const char *p = NULL; /* takes priority over mb if set */
419 memBufReset(&str);
420 /* email subject line */
421 memBufPrintf(&str, "CacheErrorInfo - %s", errorPageName(err->type));
422 memBufPrintf(mb, "?subject=%s", rfc1738_escape_part(str.buf));
423 memBufReset(&str);
424 /* email body */
425 memBufPrintf(&str, "CacheHost: %s\r\n", getMyHostname());
426 /* - Err Msgs */
427 memBufPrintf(&str, "ErrPage: %s\r\n", errorPageName(err->type));
428 if (err->xerrno) {
429 memBufPrintf(&str, "Err: (%d) %s\r\n", err->xerrno, strerror(err->xerrno));
430 } else {
431 memBufPrintf(&str, "Err: [none]\r\n");
432 }
433 if (authenticateAuthUserRequestMessage(err->auth_user_request)) {
434 memBufPrintf(&str, "extAuth ErrMsg: %s\r\n", authenticateAuthUserRequestMessage(err->auth_user_request));
435 }
436 if (err->dnsserver_msg) {
437 memBufPrintf(&str, "DNS Server ErrMsg: %s\r\n", err->dnsserver_msg);
438 }
439 /* - TimeStamp */
440 memBufPrintf(&str, "TimeStamp: %s\r\n\r\n", mkrfc1123(squid_curtime));
441 /* - IP stuff */
442 memBufPrintf(&str, "ClientIP: %s\r\n", inet_ntoa(err->src_addr));
443 if (err->host) {
444 memBufPrintf(&str, "ServerIP: %s\r\n", err->host);
445 }
446 memBufPrintf(&str, "\r\n");
447 /* - HTTP stuff */
448 memBufPrintf(&str, "HTTP Request:\r\n");
449 if (NULL != r) {
450 Packer p;
451 memBufPrintf(&str, "%s %s HTTP/%d.%d\n",
452 RequestMethodStr[r->method],
453 strLen(r->urlpath) ? strBuf(r->urlpath) : "/",
454 r->http_ver.major, r->http_ver.minor);
455 packerToMemInit(&p, &str);
456 httpHeaderPackInto(&r->header, &p);
457 packerClean(&p);
458 } else if (err->request_hdrs) {
459 p = err->request_hdrs;
460 } else {
461 p = "[none]";
462 }
463 memBufPrintf(&str, "\r\n");
464 /* - FTP stuff */
465 if (err->ftp.request) {
466 memBufPrintf(&str, "FTP Request: %s\r\n", err->ftp.request);
467 memBufPrintf(&str, "FTP Reply: %s\r\n", err->ftp.reply);
468 memBufPrintf(&str, "FTP Msg: ");
469 wordlistCat(err->ftp.server_msg, &str);
470 memBufPrintf(&str, "\r\n");
471 }
472 memBufPrintf(&str, "\r\n");
473 memBufPrintf(mb, "&body=%s", rfc1738_escape_part(str.buf));
474 memBufClean(&str);
475 return 0;
476 }
477
478 #define CVT_BUF_SZ 512
479
480 /*
481 * B - URL with FTP %2f hack x
482 * c - Squid error code x
483 * d - seconds elapsed since request received x
484 * e - errno x
485 * E - strerror() x
486 * f - FTP request line x
487 * F - FTP reply line x
488 * g - FTP server message x
489 * h - cache hostname x
490 * H - server host name x
491 * i - client IP address x
492 * I - server IP address x
493 * L - HREF link for more info/contact x
494 * M - Request Method x
495 * m - Error message returned by external Auth. x
496 * p - URL port # x
497 * P - Protocol x
498 * R - Full HTTP Request x
499 * S - squid signature from ERR_SIGNATURE x
500 * s - caching proxy software with version x
501 * t - local time x
502 * T - UTC x
503 * U - URL without password x
504 * u - URL with password x
505 * w - cachemgr email address x
506 * W - error data (to be included in the mailto links)
507 * z - dns server error message x
508 */
509
510 static const char *
511 errorConvert(char token, ErrorState * err)
512 {
513 request_t *r = err->request;
514 static MemBuf mb = MemBufNULL;
515 const char *p = NULL; /* takes priority over mb if set */
516 int do_quote = 1;
517
518 memBufReset(&mb);
519 switch (token) {
520 case 'B':
521 p = r ? ftpUrlWith2f(r) : "[no URL]";
522 break;
523 case 'c':
524 p = errorPageName(err->type);
525 break;
526 case 'e':
527 memBufPrintf(&mb, "%d", err->xerrno);
528 break;
529 case 'E':
530 if (err->xerrno)
531 memBufPrintf(&mb, "(%d) %s", err->xerrno, strerror(err->xerrno));
532 else
533 memBufPrintf(&mb, "[No Error]");
534 break;
535 case 'f':
536 /* FTP REQUEST LINE */
537 if (err->ftp.request)
538 p = err->ftp.request;
539 else
540 p = "nothing";
541 break;
542 case 'F':
543 /* FTP REPLY LINE */
544 if (err->ftp.request)
545 p = err->ftp.reply;
546 else
547 p = "nothing";
548 break;
549 case 'g':
550 /* FTP SERVER MESSAGE */
551 wordlistCat(err->ftp.server_msg, &mb);
552 break;
553 case 'h':
554 memBufPrintf(&mb, "%s", getMyHostname());
555 break;
556 case 'H':
557 p = r ? r->host : "[unknown host]";
558 break;
559 case 'i':
560 memBufPrintf(&mb, "%s", inet_ntoa(err->src_addr));
561 break;
562 case 'I':
563 if (err->host) {
564 memBufPrintf(&mb, "%s", err->host);
565 } else
566 p = "[unknown]";
567 break;
568 case 'L':
569 if (Config.errHtmlText) {
570 memBufPrintf(&mb, "%s", Config.errHtmlText);
571 do_quote = 0;
572 } else
573 p = "[not available]";
574 break;
575 case 'm':
576 p = authenticateAuthUserRequestMessage(err->auth_user_request) ? authenticateAuthUserRequestMessage(err->auth_user_request) : "[not available]";
577 break;
578 case 'M':
579 p = r ? RequestMethodStr[r->method] : "[unkown method]";
580 break;
581 case 'p':
582 if (r) {
583 memBufPrintf(&mb, "%d", (int) r->port);
584 } else {
585 p = "[unknown port]";
586 }
587 break;
588 case 'P':
589 p = r ? ProtocolStr[r->protocol] : "[unkown protocol]";
590 break;
591 case 'R':
592 if (NULL != r) {
593 Packer p;
594 memBufPrintf(&mb, "%s %s HTTP/%d.%d\n",
595 RequestMethodStr[r->method],
596 strLen(r->urlpath) ? strBuf(r->urlpath) : "/",
597 r->http_ver.major, r->http_ver.minor);
598 packerToMemInit(&p, &mb);
599 httpHeaderPackInto(&r->header, &p);
600 packerClean(&p);
601 } else if (err->request_hdrs) {
602 p = err->request_hdrs;
603 } else {
604 p = "[no request]";
605 }
606 break;
607 case 's':
608 p = full_appname_string;
609 break;
610 case 'S':
611 /* signature may contain %-escapes, recursion */
612 if (err->page_id != ERR_SQUID_SIGNATURE) {
613 const int saved_id = err->page_id;
614 MemBuf sign_mb;
615 err->page_id = ERR_SQUID_SIGNATURE;
616 sign_mb = errorBuildContent(err);
617 memBufPrintf(&mb, "%s", sign_mb.buf);
618 memBufClean(&sign_mb);
619 err->page_id = saved_id;
620 do_quote = 0;
621 } else {
622 /* wow, somebody put %S into ERR_SIGNATURE, stop recursion */
623 p = "[%S]";
624 }
625 break;
626 case 't':
627 memBufPrintf(&mb, "%s", mkhttpdlogtime(&squid_curtime));
628 break;
629 case 'T':
630 memBufPrintf(&mb, "%s", mkrfc1123(squid_curtime));
631 break;
632 case 'U':
633 p = r ? urlCanonicalClean(r) : err->url ? err->url : "[no URL]";
634 break;
635 case 'u':
636 p = r ? urlCanonical(r) : err->url ? err->url : "[no URL]";
637 break;
638 case 'w':
639 if (Config.adminEmail)
640 memBufPrintf(&mb, "%s", Config.adminEmail);
641 else
642 p = "[unknown]";
643 break;
644 case 'W':
645 if (Config.adminEmail && Config.onoff.emailErrData)
646 errorDump(err, &mb);
647 break;
648 case 'z':
649 if (err->dnsserver_msg)
650 p = err->dnsserver_msg;
651 else
652 p = "[unknown]";
653 break;
654 case '%':
655 p = "%";
656 break;
657 default:
658 memBufPrintf(&mb, "%%%c", token);
659 break;
660 }
661 if (!p)
662 p = mb.buf; /* do not use mb after this assignment! */
663 assert(p);
664 debug(4, 3) ("errorConvert: %%%c --> '%s'\n", token, p);
665 if (do_quote)
666 p = html_quote(p);
667 return p;
668 }
669
670 /* allocates and initializes an error response */
671 HttpReply *
672 errorBuildReply(ErrorState * err)
673 {
674 HttpReply *rep = httpReplyCreate();
675 const char *name = errorPageName(err->page_id);
676 http_version_t version;
677 /* no LMT for error pages; error pages expire immediately */
678 httpBuildVersion(&version, 1, 0);
679 if (strchr(name, ':')) {
680 /* Redirection */
681 char *quoted_url = rfc1738_escape_part(errorConvert('u', err));
682 httpReplySetHeaders(rep, version, HTTP_MOVED_TEMPORARILY, NULL, "text/html", 0, 0, squid_curtime);
683 httpHeaderPutStrf(&rep->header, HDR_LOCATION, name, quoted_url);
684 httpHeaderPutStrf(&rep->header, HDR_X_SQUID_ERROR, "%d %s\n", err->httpStatus, "Access Denied");
685 } else {
686 MemBuf content = errorBuildContent(err);
687 httpReplySetHeaders(rep, version, err->httpStatus, NULL, "text/html", content.size, 0, squid_curtime);
688 /*
689 * include some information for downstream caches. Implicit
690 * replaceable content. This isn't quite sufficient. xerrno is not
691 * necessarily meaningful to another system, so we really should
692 * expand it. Additionally, we should identify ourselves. Someone
693 * might want to know. Someone _will_ want to know OTOH, the first
694 * X-CACHE-MISS entry should tell us who.
695 */
696 httpHeaderPutStrf(&rep->header, HDR_X_SQUID_ERROR, "%s %d",
697 name, err->xerrno);
698 httpBodySet(&rep->body, &content);
699 /* do not memBufClean() the content, it was absorbed by httpBody */
700 }
701 return rep;
702 }
703
704 static MemBuf
705 errorBuildContent(ErrorState * err)
706 {
707 MemBuf content;
708 const char *m;
709 const char *p;
710 const char *t;
711 assert(err != NULL);
712 assert(err->page_id > ERR_NONE && err->page_id < error_page_count);
713 memBufDefInit(&content);
714 m = error_text[err->page_id];
715 assert(m);
716 while ((p = strchr(m, '%'))) {
717 memBufAppend(&content, m, p - m); /* copy */
718 t = errorConvert(*++p, err); /* convert */
719 memBufPrintf(&content, "%s", t); /* copy */
720 m = p + 1; /* advance */
721 }
722 if (*m)
723 memBufPrintf(&content, "%s", m); /* copy tail */
724 assert(content.size == strlen(content.buf));
725 return content;
726 }