]> git.ipfire.org Git - thirdparty/squid.git/blame - src/errorpage.cc
ifdef http->lookup_type
[thirdparty/squid.git] / src / errorpage.cc
CommitLineData
4a9a952e 1
30a4f2a8 2/*
5da06f20 3 * $Id: errorpage.cc,v 1.127 1998/04/23 19:27:22 wessels Exp $
30a4f2a8 4 *
5 * DEBUG: section 4 Error Generation
6 * AUTHOR: Duane Wessels
7 *
42c04c16 8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
30a4f2a8 9 * --------------------------------------------------------
10 *
11 * Squid is the result of efforts by numerous individuals from the
12 * Internet community. Development is led by Duane Wessels of the
13 * National Laboratory for Applied Network Research and funded by
14 * the National Science Foundation.
15 *
16 * This program is free software; you can redistribute it and/or modify
17 * it under the terms of the GNU General Public License as published by
18 * the Free Software Foundation; either version 2 of the License, or
19 * (at your option) any later version.
20 *
21 * This program is distributed in the hope that it will be useful,
22 * but WITHOUT ANY WARRANTY; without even the implied warranty of
23 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24 * GNU General Public License for more details.
25 *
26 * You should have received a copy of the GNU General Public License
27 * along with this program; if not, write to the Free Software
28 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 *
30 */
1e74c110 31
fe40a877 32/*
4e3f29eb 33 * Abstract: These routines are used to generate error messages to be
34 * sent to clients. The error type is used to select between
35 * the various message formats. (formats are stored in the
36 * Config.errorDirectory)
fe40a877 37 */
38
44a47c6e 39#include "squid.h"
1e74c110 40
02922e76 41
42/* local types */
43
44typedef struct {
45 int id;
46 char *page_name;
47} ErrorDynamicPageInfo;
48
49/* local constant and vars */
50
1d803566 51/*
52 * note: hard coded error messages are not appended with %S automagically
53 * to give you more control on the format
54 */
2ac76861 55static const struct {
1afe05c5 56 int type; /* and page_id */
2ac76861 57 const char *text;
58} error_hard_text[] = {
1afe05c5 59
2ac76861 60 {
61 ERR_SQUID_SIGNATURE,
62 "\n<br clear=\"all\">\n"
63 "<hr noshade size=1>\n"
5d85cb62 64 "Generated %T by %h (<a href=\"http://squid.nlanr.net/Squid/\">%s</a>)\n"
e32b11fc 65 "</BODY></HTML>\n"
1d803566 66 }
67};
02922e76 68
69static Stack ErrorDynamicPages;
70
71/* local prototypes */
72
2ac76861 73static const int error_hard_text_count = sizeof(error_hard_text) / sizeof(*error_hard_text);
02922e76 74static char **error_text = NULL;
75static int error_page_count = 0;
9b312a19 76
02922e76 77static char *errorTryLoadText(const char *page_name, const char *dir);
78static char *errorLoadText(const char *page_name);
1d803566 79static const char *errorFindHardText(err_type type);
80static MemBuf errorBuildContent(ErrorState * err);
fe40a877 81static const char *errorConvert(char token, ErrorState * err);
9b312a19 82static CWCB errorSendComplete;
1e74c110 83
fe40a877 84/*
85 * Function: errorInitialize
86 *
1d803566 87 * Abstract: This function finds the error messages formats, and stores
fe40a877 88 * them in error_text[];
89 *
90 * Global effects:
91 * error_text[] - is modified
92 */
b8d8561b 93void
0673c0ba 94errorInitialize(void)
fa966b74 95{
02922e76 96 int i;
1d803566 97 const char *text;
02922e76 98 error_page_count = ERR_MAX + ErrorDynamicPages.count;
1afe05c5 99 error_text = xcalloc(error_page_count, sizeof(char *));
02922e76 100 for (i = ERR_NONE + 1; i < error_page_count; i++) {
1d803566 101 safe_free(error_text[i]);
102 /* hard-coded ? */
103 if ((text = errorFindHardText(i)))
104 error_text[i] = xstrdup(text);
105 else
1afe05c5 106 /* precompiled ? */
02922e76 107 if (i < ERR_MAX)
108 error_text[i] = errorLoadText(err_type_str[i]);
109 /* dynamic */
110 else {
1afe05c5 111 ErrorDynamicPageInfo *info = ErrorDynamicPages.items[i - ERR_MAX];
02922e76 112 assert(info && info->id == i && info->page_name);
113 error_text[i] = errorLoadText(info->page_name);
114 }
1d803566 115 assert(error_text[i]);
116 }
117}
118
119static const char *
120errorFindHardText(err_type type)
121{
122 int i;
123 for (i = 0; i < error_hard_text_count; i++)
124 if (error_hard_text[i].type == type)
125 return error_hard_text[i].text;
126 return NULL;
127}
128
129
130static char *
02922e76 131errorLoadText(const char *page_name)
1d803566 132{
133 /* test configured location */
02922e76 134 char *text = errorTryLoadText(page_name, Config.errorDirectory);
1d803566 135 /* test default location if failed */
136 if (!text && strcmp(Config.errorDirectory, DEFAULT_SQUID_ERROR_DIR))
02922e76 137 text = errorTryLoadText(page_name, DEFAULT_SQUID_ERROR_DIR);
1d803566 138 /* giving up if failed */
139 if (!text)
140 fatal("failed to find or read error text file.");
141 return text;
142}
143
144static char *
02922e76 145errorTryLoadText(const char *page_name, const char *dir)
1d803566 146{
9b312a19 147 int fd;
148 char path[MAXPATHLEN];
149 struct stat sb;
1d803566 150 char *text;
151
152 snprintf(path, MAXPATHLEN, "%s/%s",
02922e76 153 dir, page_name);
1d803566 154 fd = file_open(path, O_RDONLY, NULL, NULL, NULL);
155 if (fd < 0 || fstat(fd, &sb) < 0) {
156 debug(4, 0) ("errorTryLoadText: '%s': %s\n", path, xstrerror());
157 if (fd >= 0)
158 file_close(fd);
159 return NULL;
160 }
161 text = xcalloc(sb.st_size + 2 + 1, 1);
162 if (read(fd, text, sb.st_size) != sb.st_size) {
163 debug(4, 0) ("errorTryLoadText: failed to fully read: '%s': %s\n",
164 path, xstrerror());
165 xfree(text);
166 text = NULL;
1e74c110 167 }
1d803566 168 file_close(fd);
2ac76861 169 strcat(text, "%S"); /* add signature */
1d803566 170 return text;
6eb42cae 171}
172
02922e76 173static ErrorDynamicPageInfo *
174errorDynamicPageInfoCreate(int id, const char *page_name)
175{
176 ErrorDynamicPageInfo *info = xcalloc(1, sizeof(ErrorDynamicPageInfo));
177 info->id = id;
178 info->page_name = xstrdup(page_name);
179 return info;
180}
181
182static void
1afe05c5 183errorDynamicPageInfoDestroy(ErrorDynamicPageInfo * info)
02922e76 184{
185 assert(info);
186 xfree(info->page_name);
187 xfree(info);
188}
189
190int
191errorReservePageId(const char *page_name)
192{
1afe05c5 193 ErrorDynamicPageInfo *info =
194 errorDynamicPageInfoCreate(ERR_MAX + ErrorDynamicPages.count, page_name);
02922e76 195 stackPush(&ErrorDynamicPages, info);
196 return info->id;
197}
198
53ad48e6 199void
200errorFree(void)
201{
202 int i;
02922e76 203 for (i = ERR_NONE + 1; i < error_page_count; i++)
53ad48e6 204 safe_free(error_text[i]);
02922e76 205 while (ErrorDynamicPages.count)
206 errorDynamicPageInfoDestroy(stackPop(&ErrorDynamicPages));
207 safe_free(error_text);
208 error_page_count = 0;
53ad48e6 209}
210
fe40a877 211/*
212 * Function: errorCon
213 *
214 * Abstract: This function creates a ErrorState object.
215 */
216ErrorState *
02922e76 217errorCon(int type, http_status status)
fe40a877 218{
219 ErrorState *err = xcalloc(1, sizeof(ErrorState));
1afe05c5 220 err->page_id = type; /* has to be reset manually if needed */
fe40a877 221 err->type = type;
222 err->http_status = status;
223 return err;
224}
225
226/*
227 * Function: errorAppendEntry
228 *
229 * Arguments: err - This object is destroyed after use in this function.
230 *
231 * Abstract: This function generates a error page from the info contained
4e3f29eb 232 * by 'err' and then stores the text in the specified store
233 * entry. This function should only be called by ``server
234 * side routines'' which need to communicate errors to the
79e0dc20 235 * client side. It should also be called from client_side.c
236 * because we now support persistent connections, and
237 * cannot assume that we can immediately write to the socket
238 * for an error.
fe40a877 239 */
240void
241errorAppendEntry(StoreEntry * entry, ErrorState * err)
242{
cb69b4c7 243 HttpReply *rep;
cb69b4c7 244 MemObject *mem = entry->mem_obj;
901e234d 245#if 0
246 /*
247 * Kostas sez PUT "success" replies might not be STORE_PENDING?
248 */
fe40a877 249 assert(entry->store_status == STORE_PENDING);
54220df8 250#endif
b50179a6 251 assert(mem != NULL);
4e3f29eb 252 assert(mem->inmem_hi == 0);
cb69b4c7 253 rep = errorBuildReply(err);
254 httpReplySwapOut(rep, entry);
255 httpReplyDestroy(rep);
cb69b4c7 256 mem->reply->sline.status = err->http_status;
00691420 257 storeComplete(entry);
b50179a6 258 storeNegativeCache(entry);
259 storeReleaseRequest(entry);
fe40a877 260 errorStateFree(err);
fe40a877 261}
262
263/*
264 * Function: errorSend
265 *
266 * Arguments: err - This object is destroyed after use in this function.
267 *
268 * Abstract: This function generates a error page from the info contained
269 * by 'err' and then sends it to the client.
4e3f29eb 270 * The callback function errorSendComplete() is called after
271 * the page has been written to the client socket (fd).
272 * errorSendComplete() deallocates 'err'. We need to add
273 * 'err' to the cbdata because comm_write() requires it
274 * for all callback data pointers.
79e0dc20 275 *
276 * Note, normally errorSend() should only be called from
277 * routines in ssl.c and pass.c, where we don't have any
278 * StoreEntry's. In client_side.c we must allocate a StoreEntry
279 * for errors and use errorAppendEntry() to account for
280 * persistent/pipeline connections.
fe40a877 281 */
282void
283errorSend(int fd, ErrorState * err)
284{
cb69b4c7 285 HttpReply *rep;
fe40a877 286 debug(4, 3) ("errorSend: FD %d, err=%p\n", fd, err);
287 assert(fd >= 0);
88aad2e5 288 /*
289 * ugh, this is how we make sure error codes get back to
290 * the client side for logging and error tracking.
291 */
292 if (err->request)
293 err->request->err_type = err->type;
cb69b4c7 294 /* moved in front of errorBuildBuf @?@ */
79a15e0a 295 EBIT_SET(err->flags, ERR_FLAG_CBDATA);
3f6c0fb2 296 cbdataAdd(err, MEM_NONE);
cb69b4c7 297 rep = errorBuildReply(err);
298 comm_write_mbuf(fd, httpReplyPack(rep), errorSendComplete, err);
299 httpReplyDestroy(rep);
fe40a877 300}
301
302/*
303 * Function: errorSendComplete
304 *
4e3f29eb 305 * Abstract: Called by commHandleWrite() after data has been written
306 * to the client socket.
fe40a877 307 *
308 * Note: If there is a callback, the callback is responsible for
309 * closeing the FD, otherwise we do it ourseves.
310 */
311static void
79a15e0a 312errorSendComplete(int fd, char *bufnotused, size_t size, int errflag, void *data)
fe40a877 313{
314 ErrorState *err = data;
315 debug(4, 3) ("errorSendComplete: FD %d, size=%d\n", fd, size);
316 if (errflag != COMM_ERR_CLOSING) {
317 if (err->callback)
318 err->callback(fd, err->callback_data, size);
319 else
320 comm_close(fd);
321 }
322 errorStateFree(err);
fe40a877 323}
324
cb69b4c7 325void
9b312a19 326errorStateFree(ErrorState * err)
6eb42cae 327{
9b312a19 328 requestUnlink(err->request);
329 safe_free(err->redirect_url);
330 safe_free(err->url);
76a5501f 331 safe_free(err->host);
5f3c4e9a 332 safe_free(err->dnsserver_msg);
b5af8569 333 safe_free(err->request_hdrs);
79a15e0a 334 if (EBIT_TEST(err->flags, ERR_FLAG_CBDATA))
38d04788 335 cbdataFree(err);
bb0929d8 336 else
337 safe_free(err);
1e74c110 338}
8213067d 339
2658f489 340#define CVT_BUF_SZ 512
fe40a877 341
342/*
1d803566 343 * B - URL with FTP %2f hack x
344 * c - Squid error code x
345 * d - seconds elapsed since request received x
fe40a877 346 * e - errno x
347 * E - strerror() x
fe40a877 348 * f - FTP request line x
969c39b9 349 * F - FTP reply line x
7131112f 350 * g - FTP server message x
fe40a877 351 * h - cache hostname x
969c39b9 352 * H - server host name x
fe40a877 353 * i - client IP address x
354 * I - server IP address x
355 * L - HREF link for more info/contact x
356 * M - Request Method x
357 * p - URL port # x
358 * P - Protocol x
b5af8569 359 * R - Full HTTP Request x
1d803566 360 * S - squid signature from ERR_SIGNATURE x
361 * s - caching proxy software with version x
fe40a877 362 * t - local time x
363 * T - UTC x
969c39b9 364 * U - URL without password x
365 * u - URL without password, %2f added to path x
fe40a877 366 * w - cachemgr email address x
367 * z - dns server error message x
368 */
369
370static const char *
9b312a19 371errorConvert(char token, ErrorState * err)
8213067d 372{
2658f489 373 request_t *r = err->request;
374 static char buf[CVT_BUF_SZ];
fe40a877 375 const char *p = buf;
9b312a19 376 switch (token) {
8f872bb6 377 case 'B':
378 p = r ? ftpUrlWith2f(r) : "[no URL]";
379 break;
042461c3 380 case 'e':
c45ed9ad 381 snprintf(buf, CVT_BUF_SZ, "%d", err->xerrno);
042461c3 382 break;
383 case 'E':
23d92c64 384 if (err->xerrno)
53ad48e6 385 snprintf(buf, CVT_BUF_SZ, "(%d) %s", err->xerrno, strerror(err->xerrno));
23d92c64 386 else
0d87e358 387 snprintf(buf, CVT_BUF_SZ, "[No Error]");
03d7b07f 388 break;
fe40a877 389 case 'f':
390 /* FTP REQUEST LINE */
391 if (err->ftp.request)
392 p = err->ftp.request;
393 else
394 p = "<none>";
395 break;
396 case 'F':
397 /* FTP REPLY LINE */
398 if (err->ftp.request)
399 p = err->ftp.reply;
400 else
401 p = "<none>";
03d7b07f 402 break;
7131112f 403 case 'g':
404 /* FTP SERVER MESSAGE */
5da06f20 405 p = wordlistCat(err->ftp_server_msg);
7131112f 406 break;
03d7b07f 407 case 'h':
408 snprintf(buf, CVT_BUF_SZ, "%s", getMyHostname());
f787fb1e 409 break;
fe40a877 410 case 'H':
411 p = r ? r->host : "[unknown host]";
f787fb1e 412 break;
413 case 'i':
414 snprintf(buf, CVT_BUF_SZ, "%s", inet_ntoa(err->src_addr));
f6aa1a5c 415 break;
f787fb1e 416 case 'I':
417 if (err->host) {
418 snprintf(buf, CVT_BUF_SZ, "%s", err->host);
f787fb1e 419 } else
fe40a877 420 p = "[unknown]";
421 break;
422 case 'L':
423 if (Config.errHtmlText) {
424 snprintf(buf, CVT_BUF_SZ, "%s", Config.errHtmlText);
425 } else
426 p = "[not available]";
427 break;
428 case 'M':
429 p = r ? RequestMethodStr[r->method] : "[unkown method]";
430 break;
431 case 'p':
432 if (r) {
433 snprintf(buf, CVT_BUF_SZ, "%d", (int) r->port);
434 } else {
435 p = "[unknown port]";
436 }
437 break;
438 case 'P':
439 p = r ? ProtocolStr[r->protocol] : "[unkown protocol]";
440 break;
b5af8569 441 case 'R':
442 p = err->request_hdrs ? err->request_hdrs : "[no request]";
443 break;
1d803566 444 case 's':
445 p = full_appname_string;
446 break;
447 case 'S':
448 /* signature may contain %-escapes, recursion */
02922e76 449 if (err->page_id != ERR_SQUID_SIGNATURE) {
450 const int saved_id = err->page_id;
1d803566 451 MemBuf mb;
02922e76 452 err->page_id = ERR_SQUID_SIGNATURE;
1d803566 453 mb = errorBuildContent(err);
454 snprintf(buf, CVT_BUF_SZ, "%s", mb.buf);
455 memBufClean(&mb);
02922e76 456 err->page_id = saved_id;
1d803566 457 } else {
458 /* wow, somebody put %S into ERR_SIGNATURE, stop recursion */
459 p = "[%S]";
460 }
461 break;
fe40a877 462 case 't':
463 xstrncpy(buf, mkhttpdlogtime(&squid_curtime), 128);
f787fb1e 464 break;
f8291f8f 465 case 'T':
466 snprintf(buf, CVT_BUF_SZ, "%s", mkrfc1123(squid_curtime));
f8291f8f 467 break;
fe40a877 468 case 'U':
b5af8569 469 p = r ? urlCanonicalClean(r) : err->url ? err->url : "[no URL]";
b9916917 470 break;
fe40a877 471 case 'w':
472 if (Config.adminEmail) {
473 snprintf(buf, CVT_BUF_SZ, "%s", Config.adminEmail);
474 } else
475 p = "[unknown]";
476 break;
477 case 'z':
478 if (err->dnsserver_msg)
479 p = err->dnsserver_msg;
b9916917 480 else
fe40a877 481 p = "[unknown]";
b9916917 482 break;
e347f8e5 483 case '%':
484 p = "%";
485 break;
9b312a19 486 default:
487 p = "%UNKNOWN%";
488 break;
489 }
9e6aef51 490 assert(p != NULL);
e102ebda 491 debug(4, 3) ("errorConvert: %%%c --> '%s'\n", token, p);
9b312a19 492 return p;
8213067d 493}
e381a13d 494
cb69b4c7 495/* allocates and initializes an error response */
496HttpReply *
2ac76861 497errorBuildReply(ErrorState * err)
cb69b4c7 498{
cb69b4c7 499 HttpReply *rep = httpReplyCreate();
1d803566 500 MemBuf content = errorBuildContent(err);
cb69b4c7 501 /* no LMT for error pages; error pages expire immediately */
1d803566 502 httpReplySetHeaders(rep, 1.0, err->http_status, NULL, "text/html", content.size, 0, squid_curtime);
2ac76861 503 httpBodySet(&rep->body, content.buf, content.size + 1, NULL);
1d803566 504 memBufClean(&content);
cb69b4c7 505 return rep;
506}
507
1d803566 508static MemBuf
509errorBuildContent(ErrorState * err)
cb69b4c7 510{
1d803566 511 MemBuf content;
1d803566 512 const char *m;
513 const char *p;
cb69b4c7 514 const char *t;
515 assert(err != NULL);
02922e76 516 assert(err->page_id > ERR_NONE && err->page_id < error_page_count);
1d803566 517 memBufDefInit(&content);
02922e76 518 m = error_text[err->page_id];
1d803566 519 assert(m);
cb69b4c7 520 while ((p = strchr(m, '%'))) {
2ac76861 521 memBufAppend(&content, m, p - m); /* copy */
522 t = errorConvert(*++p, err); /* convert */
523 memBufPrintf(&content, "%s", t); /* copy */
524 m = p + 1; /* advance */
cb69b4c7 525 }
1d803566 526 if (*m)
2ac76861 527 memBufPrintf(&content, "%s", m); /* copy tail */
1d803566 528 assert(content.size == strlen(content.buf));
cb69b4c7 529 return content;
530}