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