]> git.ipfire.org Git - thirdparty/squid.git/blame - src/errorpage.cc
fix headers to allow inclusion into C++ source
[thirdparty/squid.git] / src / errorpage.cc
CommitLineData
4a9a952e 1
30a4f2a8 2/*
29b8d8d6 3 * $Id: errorpage.cc,v 1.177 2002/09/15 06:40:57 robertc Exp $
30a4f2a8 4 *
5 * DEBUG: section 4 Error Generation
6 * AUTHOR: Duane Wessels
7 *
2b6662ba 8 * SQUID Web Proxy Cache http://www.squid-cache.org/
e25c139f 9 * ----------------------------------------------------------
30a4f2a8 10 *
2b6662ba 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.
30a4f2a8 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
cbdec147 32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
e25c139f 33 *
30a4f2a8 34 */
1e74c110 35
fe40a877 36/*
4e3f29eb 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)
fe40a877 41 */
42
44a47c6e 43#include "squid.h"
1e74c110 44
02922e76 45
46/* local types */
47
48typedef struct {
49 int id;
50 char *page_name;
51} ErrorDynamicPageInfo;
52
53/* local constant and vars */
54
1d803566 55/*
56 * note: hard coded error messages are not appended with %S automagically
57 * to give you more control on the format
58 */
2ac76861 59static const struct {
1afe05c5 60 int type; /* and page_id */
2ac76861 61 const char *text;
62} error_hard_text[] = {
1afe05c5 63
2ac76861 64 {
65 ERR_SQUID_SIGNATURE,
df339671 66 "\n<BR clear=\"all\">\n"
67 "<HR noshade size=\"1px\">\n"
b5fb34f1 68 "<ADDRESS>\n"
9bc73deb 69 "Generated %T by %h (%s)\n"
b5fb34f1 70 "</ADDRESS>\n"
e32b11fc 71 "</BODY></HTML>\n"
76cdc28d 72 },
73 {
74 TCP_RESET,
75 "reset"
1d803566 76 }
77};
02922e76 78
79static Stack ErrorDynamicPages;
80
81/* local prototypes */
82
2ac76861 83static const int error_hard_text_count = sizeof(error_hard_text) / sizeof(*error_hard_text);
02922e76 84static char **error_text = NULL;
85static int error_page_count = 0;
9b312a19 86
02922e76 87static char *errorTryLoadText(const char *page_name, const char *dir);
88static char *errorLoadText(const char *page_name);
1d803566 89static const char *errorFindHardText(err_type type);
c68e9c6b 90static ErrorDynamicPageInfo *errorDynamicPageInfoCreate(int id, const char *page_name);
91static void errorDynamicPageInfoDestroy(ErrorDynamicPageInfo * info);
1d803566 92static MemBuf errorBuildContent(ErrorState * err);
b5fb34f1 93static int errorDump(ErrorState * err, MemBuf * mb);
fe40a877 94static const char *errorConvert(char token, ErrorState * err);
9b312a19 95static CWCB errorSendComplete;
1e74c110 96
fe40a877 97/*
98 * Function: errorInitialize
99 *
1d803566 100 * Abstract: This function finds the error messages formats, and stores
fe40a877 101 * them in error_text[];
102 *
103 * Global effects:
104 * error_text[] - is modified
105 */
b8d8561b 106void
0673c0ba 107errorInitialize(void)
fa966b74 108{
728da2ee 109 err_type i;
1d803566 110 const char *text;
02922e76 111 error_page_count = ERR_MAX + ErrorDynamicPages.count;
1afe05c5 112 error_text = xcalloc(error_page_count, sizeof(char *));
728da2ee 113 for (i = ERR_NONE, i++; i < error_page_count; i++) {
1d803566 114 safe_free(error_text[i]);
115 /* hard-coded ? */
116 if ((text = errorFindHardText(i)))
117 error_text[i] = xstrdup(text);
c68e9c6b 118 else if (i < ERR_MAX) {
1afe05c5 119 /* precompiled ? */
02922e76 120 error_text[i] = errorLoadText(err_type_str[i]);
c68e9c6b 121 } else {
122 /* dynamic */
1afe05c5 123 ErrorDynamicPageInfo *info = ErrorDynamicPages.items[i - ERR_MAX];
02922e76 124 assert(info && info->id == i && info->page_name);
76cdc28d 125 if (strchr(info->page_name, ':') == NULL) {
126 /* Not on redirected errors... */
127 error_text[i] = errorLoadText(info->page_name);
128 }
02922e76 129 }
1d803566 130 }
131}
132
c68e9c6b 133void
134errorClean(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
1d803566 147static const char *
148errorFindHardText(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
158static char *
02922e76 159errorLoadText(const char *page_name)
1d803566 160{
161 /* test configured location */
02922e76 162 char *text = errorTryLoadText(page_name, Config.errorDirectory);
1d803566 163 /* test default location if failed */
164 if (!text && strcmp(Config.errorDirectory, DEFAULT_SQUID_ERROR_DIR))
02922e76 165 text = errorTryLoadText(page_name, DEFAULT_SQUID_ERROR_DIR);
1d803566 166 /* giving up if failed */
167 if (!text)
168 fatal("failed to find or read error text file.");
169 return text;
170}
171
172static char *
02922e76 173errorTryLoadText(const char *page_name, const char *dir)
1d803566 174{
9b312a19 175 int fd;
176 char path[MAXPATHLEN];
177 struct stat sb;
1d803566 178 char *text;
179
137ee196 180 snprintf(path, sizeof(path), "%s/%s", dir, page_name);
c4aefe96 181 fd = file_open(path, O_RDONLY | O_TEXT);
1d803566 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 }
d5e36424 188 text = xcalloc(sb.st_size + 2 + 1, 1); /* 2 == space for %S */
1f7c9178 189 if (FD_READ_METHOD(fd, text, sb.st_size) != sb.st_size) {
1d803566 190 debug(4, 0) ("errorTryLoadText: failed to fully read: '%s': %s\n",
191 path, xstrerror());
192 xfree(text);
193 text = NULL;
1e74c110 194 }
1d803566 195 file_close(fd);
c68e9c6b 196 if (strstr(text, "%s") == NULL)
197 strcat(text, "%S"); /* add signature */
1d803566 198 return text;
6eb42cae 199}
200
02922e76 201static ErrorDynamicPageInfo *
202errorDynamicPageInfoCreate(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
210static void
1afe05c5 211errorDynamicPageInfoDestroy(ErrorDynamicPageInfo * info)
02922e76 212{
213 assert(info);
214 xfree(info->page_name);
215 xfree(info);
216}
217
76cdc28d 218static int
219errorPageId(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++) {
15423f15 227 if (strcmp(((ErrorDynamicPageInfo *) ErrorDynamicPages.items[i])->page_name, page_name) == 0)
76cdc28d 228 return i + ERR_MAX;
229 }
230 return ERR_NONE;
231}
232
02922e76 233int
234errorReservePageId(const char *page_name)
235{
76cdc28d 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;
02922e76 244}
245
c68e9c6b 246static const char *
247errorPageName(int pageId)
53ad48e6 248{
c68e9c6b 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 */
53ad48e6 255}
256
fe40a877 257/*
258 * Function: errorCon
259 *
260 * Abstract: This function creates a ErrorState object.
261 */
262ErrorState *
728da2ee 263errorCon(err_type type, http_status status)
fe40a877 264{
28c60158 265 ErrorState *err;
72711e31 266 err = cbdataAlloc(ErrorState);
1afe05c5 267 err->page_id = type; /* has to be reset manually if needed */
fe40a877 268 err->type = type;
29b8d8d6 269 err->httpStatus = status;
fe40a877 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
4e3f29eb 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
79e0dc20 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.
fe40a877 286 */
287void
288errorAppendEntry(StoreEntry * entry, ErrorState * err)
289{
cb69b4c7 290 HttpReply *rep;
cb69b4c7 291 MemObject *mem = entry->mem_obj;
b50179a6 292 assert(mem != NULL);
4e3f29eb 293 assert(mem->inmem_hi == 0);
1ea80e98 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 }
76cdc28d 305 if (err->page_id == TCP_RESET) {
98264874 306 if (err->request) {
76cdc28d 307 debug(4, 2) ("RSTing this reply\n");
98264874 308 err->request->flags.reset_tcp = 1;
309 }
310 }
5507c7d5 311 storeLockObject(entry);
429cf70b 312 storeBuffer(entry);
cb69b4c7 313 rep = errorBuildReply(err);
1cfdbcf0 314 /* Add authentication header */
94439e4e 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 */
721b0310 319 authenticateFixHeader(rep, err->auth_user_request, err->request, 0, 1);
cb69b4c7 320 httpReplySwapOut(rep, entry);
94439e4e 321 httpReplyAbsorb(mem->reply, rep);
ee08bdf5 322 EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT);
429cf70b 323 storeBufferFlush(entry);
00691420 324 storeComplete(entry);
b50179a6 325 storeNegativeCache(entry);
326 storeReleaseRequest(entry);
5507c7d5 327 storeUnlockObject(entry);
fe40a877 328 errorStateFree(err);
fe40a877 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.
4e3f29eb 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.
79e0dc20 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.
fe40a877 349 */
350void
351errorSend(int fd, ErrorState * err)
352{
cb69b4c7 353 HttpReply *rep;
fe40a877 354 debug(4, 3) ("errorSend: FD %d, err=%p\n", fd, err);
355 assert(fd >= 0);
88aad2e5 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)
29b8d8d6 361 err->request->errType = err->type;
cb69b4c7 362 /* moved in front of errorBuildBuf @?@ */
b515fc11 363 err->flags.flag_cbdata = 1;
cb69b4c7 364 rep = errorBuildReply(err);
365 comm_write_mbuf(fd, httpReplyPack(rep), errorSendComplete, err);
366 httpReplyDestroy(rep);
fe40a877 367}
368
369/*
370 * Function: errorSendComplete
371 *
4e3f29eb 372 * Abstract: Called by commHandleWrite() after data has been written
373 * to the client socket.
fe40a877 374 *
375 * Note: If there is a callback, the callback is responsible for
376 * closeing the FD, otherwise we do it ourseves.
377 */
378static void
3d7e9d7c 379errorSendComplete(int fd, char *bufnotused, size_t size, comm_err_t errflag, void *data)
fe40a877 380{
381 ErrorState *err = data;
32754419 382 debug(4, 3) ("errorSendComplete: FD %d, size=%ld\n", fd, (long int) size);
fe40a877 383 if (errflag != COMM_ERR_CLOSING) {
94439e4e 384 if (err->callback) {
385 debug(4, 3) ("errorSendComplete: callback\n");
fe40a877 386 err->callback(fd, err->callback_data, size);
94439e4e 387 } else {
fe40a877 388 comm_close(fd);
94439e4e 389 debug(4, 3) ("errorSendComplete: comm_close\n");
390 }
fe40a877 391 }
392 errorStateFree(err);
fe40a877 393}
394
cb69b4c7 395void
9b312a19 396errorStateFree(ErrorState * err)
6eb42cae 397{
9b312a19 398 requestUnlink(err->request);
399 safe_free(err->redirect_url);
400 safe_free(err->url);
76a5501f 401 safe_free(err->host);
5f3c4e9a 402 safe_free(err->dnsserver_msg);
b5af8569 403 safe_free(err->request_hdrs);
9bc73deb 404 wordlistDestroy(&err->ftp.server_msg);
405 safe_free(err->ftp.request);
406 safe_free(err->ftp.reply);
94439e4e 407 if (err->auth_user_request)
408 authenticateAuthUserRequestUnlock(err->auth_user_request);
5d146f7d 409 err->auth_user_request = NULL;
28c60158 410 cbdataFree(err);
1e74c110 411}
8213067d 412
b5fb34f1 413static int
414errorDump(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
2658f489 478#define CVT_BUF_SZ 512
fe40a877 479
480/*
1d803566 481 * B - URL with FTP %2f hack x
482 * c - Squid error code x
483 * d - seconds elapsed since request received x
fe40a877 484 * e - errno x
485 * E - strerror() x
fe40a877 486 * f - FTP request line x
969c39b9 487 * F - FTP reply line x
7131112f 488 * g - FTP server message x
fe40a877 489 * h - cache hostname x
969c39b9 490 * H - server host name x
fe40a877 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
066ed5c1 495 * m - Error message returned by external Auth. x
fe40a877 496 * p - URL port # x
497 * P - Protocol x
b5af8569 498 * R - Full HTTP Request x
1d803566 499 * S - squid signature from ERR_SIGNATURE x
500 * s - caching proxy software with version x
fe40a877 501 * t - local time x
502 * T - UTC x
969c39b9 503 * U - URL without password x
76cdc28d 504 * u - URL with password x
fe40a877 505 * w - cachemgr email address x
b5fb34f1 506 * W - error data (to be included in the mailto links)
fe40a877 507 * z - dns server error message x
508 */
509
510static const char *
9b312a19 511errorConvert(char token, ErrorState * err)
8213067d 512{
2658f489 513 request_t *r = err->request;
137ee196 514 static MemBuf mb = MemBufNULL;
eeb423fb 515 const char *p = NULL; /* takes priority over mb if set */
10270faa 516 int do_quote = 1;
eeb423fb 517
137ee196 518 memBufReset(&mb);
9b312a19 519 switch (token) {
8f872bb6 520 case 'B':
521 p = r ? ftpUrlWith2f(r) : "[no URL]";
522 break;
42b51993 523 case 'c':
bd704830 524 p = errorPageName(err->type);
42b51993 525 break;
042461c3 526 case 'e':
137ee196 527 memBufPrintf(&mb, "%d", err->xerrno);
042461c3 528 break;
529 case 'E':
23d92c64 530 if (err->xerrno)
137ee196 531 memBufPrintf(&mb, "(%d) %s", err->xerrno, strerror(err->xerrno));
23d92c64 532 else
137ee196 533 memBufPrintf(&mb, "[No Error]");
03d7b07f 534 break;
fe40a877 535 case 'f':
536 /* FTP REQUEST LINE */
537 if (err->ftp.request)
538 p = err->ftp.request;
539 else
a8af3a35 540 p = "nothing";
fe40a877 541 break;
542 case 'F':
543 /* FTP REPLY LINE */
544 if (err->ftp.request)
545 p = err->ftp.reply;
546 else
a8af3a35 547 p = "nothing";
03d7b07f 548 break;
7131112f 549 case 'g':
550 /* FTP SERVER MESSAGE */
9bc73deb 551 wordlistCat(err->ftp.server_msg, &mb);
7131112f 552 break;
03d7b07f 553 case 'h':
137ee196 554 memBufPrintf(&mb, "%s", getMyHostname());
f787fb1e 555 break;
fe40a877 556 case 'H':
557 p = r ? r->host : "[unknown host]";
f787fb1e 558 break;
559 case 'i':
137ee196 560 memBufPrintf(&mb, "%s", inet_ntoa(err->src_addr));
f6aa1a5c 561 break;
f787fb1e 562 case 'I':
563 if (err->host) {
137ee196 564 memBufPrintf(&mb, "%s", err->host);
f787fb1e 565 } else
fe40a877 566 p = "[unknown]";
567 break;
568 case 'L':
569 if (Config.errHtmlText) {
137ee196 570 memBufPrintf(&mb, "%s", Config.errHtmlText);
406ecea9 571 do_quote = 0;
fe40a877 572 } else
573 p = "[not available]";
574 break;
066ed5c1 575 case 'm':
94439e4e 576 p = authenticateAuthUserRequestMessage(err->auth_user_request) ? authenticateAuthUserRequestMessage(err->auth_user_request) : "[not available]";
066ed5c1 577 break;
fe40a877 578 case 'M':
579 p = r ? RequestMethodStr[r->method] : "[unkown method]";
580 break;
581 case 'p':
582 if (r) {
137ee196 583 memBufPrintf(&mb, "%d", (int) r->port);
fe40a877 584 } else {
585 p = "[unknown port]";
586 }
587 break;
588 case 'P':
589 p = r ? ProtocolStr[r->protocol] : "[unkown protocol]";
590 break;
b5af8569 591 case 'R':
865094d7 592 if (NULL != r) {
865094d7 593 Packer p;
ccf44862 594 memBufPrintf(&mb, "%s %s HTTP/%d.%d\n",
865094d7 595 RequestMethodStr[r->method],
596 strLen(r->urlpath) ? strBuf(r->urlpath) : "/",
ccf44862 597 r->http_ver.major, r->http_ver.minor);
865094d7 598 packerToMemInit(&p, &mb);
599 httpHeaderPackInto(&r->header, &p);
600 packerClean(&p);
865094d7 601 } else if (err->request_hdrs) {
602 p = err->request_hdrs;
603 } else {
604 p = "[no request]";
605 }
b5af8569 606 break;
1d803566 607 case 's':
608 p = full_appname_string;
609 break;
610 case 'S':
611 /* signature may contain %-escapes, recursion */
02922e76 612 if (err->page_id != ERR_SQUID_SIGNATURE) {
613 const int saved_id = err->page_id;
137ee196 614 MemBuf sign_mb;
02922e76 615 err->page_id = ERR_SQUID_SIGNATURE;
137ee196 616 sign_mb = errorBuildContent(err);
617 memBufPrintf(&mb, "%s", sign_mb.buf);
618 memBufClean(&sign_mb);
02922e76 619 err->page_id = saved_id;
10270faa 620 do_quote = 0;
1d803566 621 } else {
622 /* wow, somebody put %S into ERR_SIGNATURE, stop recursion */
623 p = "[%S]";
624 }
625 break;
fe40a877 626 case 't':
137ee196 627 memBufPrintf(&mb, "%s", mkhttpdlogtime(&squid_curtime));
f787fb1e 628 break;
f8291f8f 629 case 'T':
137ee196 630 memBufPrintf(&mb, "%s", mkrfc1123(squid_curtime));
f8291f8f 631 break;
fe40a877 632 case 'U':
b5af8569 633 p = r ? urlCanonicalClean(r) : err->url ? err->url : "[no URL]";
b9916917 634 break;
76cdc28d 635 case 'u':
636 p = r ? urlCanonical(r) : err->url ? err->url : "[no URL]";
637 break;
fe40a877 638 case 'w':
137ee196 639 if (Config.adminEmail)
640 memBufPrintf(&mb, "%s", Config.adminEmail);
641 else
fe40a877 642 p = "[unknown]";
643 break;
b5fb34f1 644 case 'W':
645 if (Config.adminEmail && Config.onoff.emailErrData)
646 errorDump(err, &mb);
647 break;
fe40a877 648 case 'z':
649 if (err->dnsserver_msg)
650 p = err->dnsserver_msg;
b9916917 651 else
fe40a877 652 p = "[unknown]";
b9916917 653 break;
e347f8e5 654 case '%':
655 p = "%";
656 break;
9b312a19 657 default:
6b8e7481 658 memBufPrintf(&mb, "%%%c", token);
9b312a19 659 break;
660 }
137ee196 661 if (!p)
eeb423fb 662 p = mb.buf; /* do not use mb after this assignment! */
137ee196 663 assert(p);
e102ebda 664 debug(4, 3) ("errorConvert: %%%c --> '%s'\n", token, p);
10270faa 665 if (do_quote)
666 p = html_quote(p);
9b312a19 667 return p;
8213067d 668}
e381a13d 669
cb69b4c7 670/* allocates and initializes an error response */
671HttpReply *
2ac76861 672errorBuildReply(ErrorState * err)
cb69b4c7 673{
cb69b4c7 674 HttpReply *rep = httpReplyCreate();
76cdc28d 675 const char *name = errorPageName(err->page_id);
ccf44862 676 http_version_t version;
cb69b4c7 677 /* no LMT for error pages; error pages expire immediately */
bffee5af 678 httpBuildVersion(&version, 1, 0);
76cdc28d 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);
29b8d8d6 684 httpHeaderPutStrf(&rep->header, HDR_X_SQUID_ERROR, "%d %s\n", err->httpStatus, "Access Denied");
76cdc28d 685 } else {
686 MemBuf content = errorBuildContent(err);
29b8d8d6 687 httpReplySetHeaders(rep, version, err->httpStatus, NULL, "text/html", content.size, 0, squid_curtime);
76cdc28d 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 }
cb69b4c7 701 return rep;
702}
703
1d803566 704static MemBuf
705errorBuildContent(ErrorState * err)
cb69b4c7 706{
1d803566 707 MemBuf content;
1d803566 708 const char *m;
709 const char *p;
cb69b4c7 710 const char *t;
711 assert(err != NULL);
02922e76 712 assert(err->page_id > ERR_NONE && err->page_id < error_page_count);
1d803566 713 memBufDefInit(&content);
02922e76 714 m = error_text[err->page_id];
1d803566 715 assert(m);
cb69b4c7 716 while ((p = strchr(m, '%'))) {
2ac76861 717 memBufAppend(&content, m, p - m); /* copy */
718 t = errorConvert(*++p, err); /* convert */
719 memBufPrintf(&content, "%s", t); /* copy */
720 m = p + 1; /* advance */
cb69b4c7 721 }
1d803566 722 if (*m)
2ac76861 723 memBufPrintf(&content, "%s", m); /* copy tail */
1d803566 724 assert(content.size == strlen(content.buf));
cb69b4c7 725 return content;
726}