]>
git.ipfire.org Git - thirdparty/squid.git/blob - src/errorpage.cc
3 * $Id: errorpage.cc,v 1.143 1998/11/12 06:28:05 wessels Exp $
5 * DEBUG: section 4 Error Generation
6 * AUTHOR: Duane Wessels
8 * SQUID Internet Object Cache http://squid.nlanr.net/Squid/
9 * ----------------------------------------------------------
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 the
14 * National Science Foundation. Squid is Copyrighted (C) 1998 by
15 * Duane Wessels and the University of California San Diego. Please
16 * see the COPYRIGHT file for full details. Squid incorporates
17 * software developed and/or copyrighted by other sources. Please see
18 * the CREDITS file for full details.
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.
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.
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.
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)
51 } ErrorDynamicPageInfo
;
53 /* local constant and vars */
56 * note: hard coded error messages are not appended with %S automagically
57 * to give you more control on the format
60 int type
; /* and page_id */
62 } error_hard_text
[] = {
66 "\n<br clear=\"all\">\n"
67 "<hr noshade size=1>\n"
68 "Generated %T by %h (<a href=\"http://squid.nlanr.net/Squid/\">%s</a>)\n"
73 static Stack ErrorDynamicPages
;
75 /* local prototypes */
77 static const int error_hard_text_count
= sizeof(error_hard_text
) / sizeof(*error_hard_text
);
78 static char **error_text
= NULL
;
79 static int error_page_count
= 0;
81 static char *errorTryLoadText(const char *page_name
, const char *dir
);
82 static char *errorLoadText(const char *page_name
);
83 static const char *errorFindHardText(err_type type
);
84 static ErrorDynamicPageInfo
*errorDynamicPageInfoCreate(int id
, const char *page_name
);
85 static void errorDynamicPageInfoDestroy(ErrorDynamicPageInfo
* info
);
86 static MemBuf
errorBuildContent(ErrorState
* err
);
87 static const char *errorConvert(char token
, ErrorState
* err
);
88 static CWCB errorSendComplete
;
91 * Function: errorInitialize
93 * Abstract: This function finds the error messages formats, and stores
94 * them in error_text[];
97 * error_text[] - is modified
100 errorInitialize(void)
104 error_page_count
= ERR_MAX
+ ErrorDynamicPages
.count
;
105 error_text
= xcalloc(error_page_count
, sizeof(char *));
106 for (i
= ERR_NONE
, i
++; i
< error_page_count
; i
++) {
107 safe_free(error_text
[i
]);
109 if ((text
= errorFindHardText(i
)))
110 error_text
[i
] = xstrdup(text
);
111 else if (i
< ERR_MAX
) {
113 error_text
[i
] = errorLoadText(err_type_str
[i
]);
116 ErrorDynamicPageInfo
*info
= ErrorDynamicPages
.items
[i
- ERR_MAX
];
117 assert(info
&& info
->id
== i
&& info
->page_name
);
118 error_text
[i
] = errorLoadText(info
->page_name
);
120 assert(error_text
[i
]);
129 for (i
= ERR_NONE
+ 1; i
< error_page_count
; i
++)
130 safe_free(error_text
[i
]);
131 safe_free(error_text
);
133 while (ErrorDynamicPages
.count
)
134 errorDynamicPageInfoDestroy(stackPop(&ErrorDynamicPages
));
135 error_page_count
= 0;
139 errorFindHardText(err_type type
)
142 for (i
= 0; i
< error_hard_text_count
; i
++)
143 if (error_hard_text
[i
].type
== type
)
144 return error_hard_text
[i
].text
;
150 errorLoadText(const char *page_name
)
152 /* test configured location */
153 char *text
= errorTryLoadText(page_name
, Config
.errorDirectory
);
154 /* test default location if failed */
155 if (!text
&& strcmp(Config
.errorDirectory
, DEFAULT_SQUID_ERROR_DIR
))
156 text
= errorTryLoadText(page_name
, DEFAULT_SQUID_ERROR_DIR
);
157 /* giving up if failed */
159 fatal("failed to find or read error text file.");
164 errorTryLoadText(const char *page_name
, const char *dir
)
167 char path
[MAXPATHLEN
];
171 snprintf(path
, sizeof(path
), "%s/%s", dir
, page_name
);
172 fd
= file_open(path
, O_RDONLY
, NULL
, NULL
, NULL
);
173 if (fd
< 0 || fstat(fd
, &sb
) < 0) {
174 debug(4, 0) ("errorTryLoadText: '%s': %s\n", path
, xstrerror());
179 text
= xcalloc(sb
.st_size
+ 2 + 1, 1); /* 2 == space for %S */
180 if (read(fd
, text
, sb
.st_size
) != sb
.st_size
) {
181 debug(4, 0) ("errorTryLoadText: failed to fully read: '%s': %s\n",
187 if (strstr(text
, "%s") == NULL
)
188 strcat(text
, "%S"); /* add signature */
192 static ErrorDynamicPageInfo
*
193 errorDynamicPageInfoCreate(int id
, const char *page_name
)
195 ErrorDynamicPageInfo
*info
= xcalloc(1, sizeof(ErrorDynamicPageInfo
));
197 info
->page_name
= xstrdup(page_name
);
202 errorDynamicPageInfoDestroy(ErrorDynamicPageInfo
* info
)
205 xfree(info
->page_name
);
210 errorReservePageId(const char *page_name
)
212 ErrorDynamicPageInfo
*info
=
213 errorDynamicPageInfoCreate(ERR_MAX
+ ErrorDynamicPages
.count
, page_name
);
214 stackPush(&ErrorDynamicPages
, info
);
219 errorPageName(int pageId
)
221 if (pageId
>= ERR_NONE
&& pageId
< ERR_MAX
) /* common case */
222 return err_type_str
[pageId
];
223 if (pageId
>= ERR_MAX
&& pageId
- ERR_MAX
< ErrorDynamicPages
.count
)
224 return ((ErrorDynamicPageInfo
*) ErrorDynamicPages
.
225 items
[pageId
- ERR_MAX
])->page_name
;
226 return "ERR_UNKNOWN"; /* should not happen */
232 * Abstract: This function creates a ErrorState object.
235 errorCon(err_type type
, http_status status
)
237 ErrorState
*err
= xcalloc(1, sizeof(ErrorState
));
238 err
->page_id
= type
; /* has to be reset manually if needed */
240 err
->http_status
= status
;
245 * Function: errorAppendEntry
247 * Arguments: err - This object is destroyed after use in this function.
249 * Abstract: This function generates a error page from the info contained
250 * by 'err' and then stores the text in the specified store
251 * entry. This function should only be called by ``server
252 * side routines'' which need to communicate errors to the
253 * client side. It should also be called from client_side.c
254 * because we now support persistent connections, and
255 * cannot assume that we can immediately write to the socket
259 errorAppendEntry(StoreEntry
* entry
, ErrorState
* err
)
262 MemObject
*mem
= entry
->mem_obj
;
264 * Kostas sez PUT "success" replies might not be STORE_PENDING?
266 /* assert(entry->store_status == STORE_PENDING); */
268 assert(mem
->inmem_hi
== 0);
270 rep
= errorBuildReply(err
);
271 httpReplySwapOut(rep
, entry
);
272 httpReplyDestroy(rep
);
273 mem
->reply
->sline
.status
= err
->http_status
;
274 mem
->reply
->content_length
= -1;
275 storeBufferFlush(entry
);
276 storeComplete(entry
);
277 storeNegativeCache(entry
);
278 storeReleaseRequest(entry
);
283 * Function: errorSend
285 * Arguments: err - This object is destroyed after use in this function.
287 * Abstract: This function generates a error page from the info contained
288 * by 'err' and then sends it to the client.
289 * The callback function errorSendComplete() is called after
290 * the page has been written to the client socket (fd).
291 * errorSendComplete() deallocates 'err'. We need to add
292 * 'err' to the cbdata because comm_write() requires it
293 * for all callback data pointers.
295 * Note, normally errorSend() should only be called from
296 * routines in ssl.c and pass.c, where we don't have any
297 * StoreEntry's. In client_side.c we must allocate a StoreEntry
298 * for errors and use errorAppendEntry() to account for
299 * persistent/pipeline connections.
302 errorSend(int fd
, ErrorState
* err
)
305 debug(4, 3) ("errorSend: FD %d, err=%p\n", fd
, err
);
308 * ugh, this is how we make sure error codes get back to
309 * the client side for logging and error tracking.
312 err
->request
->err_type
= err
->type
;
313 /* moved in front of errorBuildBuf @?@ */
314 err
->flags
.flag_cbdata
= 1;
315 cbdataAdd(err
, MEM_NONE
);
316 rep
= errorBuildReply(err
);
317 comm_write_mbuf(fd
, httpReplyPack(rep
), errorSendComplete
, err
);
318 httpReplyDestroy(rep
);
322 * Function: errorSendComplete
324 * Abstract: Called by commHandleWrite() after data has been written
325 * to the client socket.
327 * Note: If there is a callback, the callback is responsible for
328 * closeing the FD, otherwise we do it ourseves.
331 errorSendComplete(int fd
, char *bufnotused
, size_t size
, int errflag
, void *data
)
333 ErrorState
*err
= data
;
334 debug(4, 3) ("errorSendComplete: FD %d, size=%d\n", fd
, size
);
335 if (errflag
!= COMM_ERR_CLOSING
) {
337 err
->callback(fd
, err
->callback_data
, size
);
345 errorStateFree(ErrorState
* err
)
347 requestUnlink(err
->request
);
348 safe_free(err
->redirect_url
);
350 safe_free(err
->host
);
351 safe_free(err
->dnsserver_msg
);
352 safe_free(err
->request_hdrs
);
353 if (err
->flags
.flag_cbdata
)
359 #define CVT_BUF_SZ 512
362 * B - URL with FTP %2f hack x
363 * c - Squid error code x
364 * d - seconds elapsed since request received x
367 * f - FTP request line x
368 * F - FTP reply line x
369 * g - FTP server message x
370 * h - cache hostname x
371 * H - server host name x
372 * i - client IP address x
373 * I - server IP address x
374 * L - HREF link for more info/contact x
375 * M - Request Method x
378 * R - Full HTTP Request x
379 * S - squid signature from ERR_SIGNATURE x
380 * s - caching proxy software with version x
383 * U - URL without password x
384 * u - URL without password, %2f added to path x
385 * w - cachemgr email address x
386 * z - dns server error message x
390 errorConvert(char token
, ErrorState
* err
)
392 request_t
*r
= err
->request
;
393 static MemBuf mb
= MemBufNULL
;
394 const char *p
= NULL
; /* takes priority over mb if set */
399 p
= r
? ftpUrlWith2f(r
) : "[no URL]";
402 memBufPrintf(&mb
, "%d", err
->xerrno
);
406 memBufPrintf(&mb
, "(%d) %s", err
->xerrno
, strerror(err
->xerrno
));
408 memBufPrintf(&mb
, "[No Error]");
411 /* FTP REQUEST LINE */
412 if (err
->ftp
.request
)
413 p
= err
->ftp
.request
;
419 if (err
->ftp
.request
)
425 /* FTP SERVER MESSAGE */
426 wordlistCat(err
->ftp_server_msg
, &mb
);
429 memBufPrintf(&mb
, "%s", getMyHostname());
432 p
= r
? r
->host
: "[unknown host]";
435 memBufPrintf(&mb
, "%s", inet_ntoa(err
->src_addr
));
439 memBufPrintf(&mb
, "%s", err
->host
);
444 if (Config
.errHtmlText
) {
445 memBufPrintf(&mb
, "%s", Config
.errHtmlText
);
447 p
= "[not available]";
450 p
= r
? RequestMethodStr
[r
->method
] : "[unkown method]";
454 memBufPrintf(&mb
, "%d", (int) r
->port
);
456 p
= "[unknown port]";
460 p
= r
? ProtocolStr
[r
->protocol
] : "[unkown protocol]";
465 memBufPrintf(&mb
, "%s %s HTTP/%3.1f\n",
466 RequestMethodStr
[r
->method
],
467 strLen(r
->urlpath
) ? strBuf(r
->urlpath
) : "/",
468 (double) r
->http_ver
);
469 packerToMemInit(&p
, &mb
);
470 httpHeaderPackInto(&r
->header
, &p
);
472 } else if (err
->request_hdrs
) {
473 p
= err
->request_hdrs
;
479 p
= full_appname_string
;
482 /* signature may contain %-escapes, recursion */
483 if (err
->page_id
!= ERR_SQUID_SIGNATURE
) {
484 const int saved_id
= err
->page_id
;
486 err
->page_id
= ERR_SQUID_SIGNATURE
;
487 sign_mb
= errorBuildContent(err
);
488 memBufPrintf(&mb
, "%s", sign_mb
.buf
);
489 memBufClean(&sign_mb
);
490 err
->page_id
= saved_id
;
492 /* wow, somebody put %S into ERR_SIGNATURE, stop recursion */
497 memBufPrintf(&mb
, "%s", mkhttpdlogtime(&squid_curtime
));
500 memBufPrintf(&mb
, "%s", mkrfc1123(squid_curtime
));
503 p
= r
? urlCanonicalClean(r
) : err
->url
? err
->url
: "[no URL]";
506 if (Config
.adminEmail
)
507 memBufPrintf(&mb
, "%s", Config
.adminEmail
);
512 if (err
->dnsserver_msg
)
513 p
= err
->dnsserver_msg
;
525 p
= mb
.buf
; /* do not use mb after this assignment! */
527 debug(4, 3) ("errorConvert: %%%c --> '%s'\n", token
, p
);
531 /* allocates and initializes an error response */
533 errorBuildReply(ErrorState
* err
)
535 HttpReply
*rep
= httpReplyCreate();
536 MemBuf content
= errorBuildContent(err
);
537 /* no LMT for error pages; error pages expire immediately */
538 httpReplySetHeaders(rep
, 1.0, err
->http_status
, NULL
, "text/html", content
.size
, 0, squid_curtime
);
540 * include some information for downstream caches. Implicit
541 * replaceable content. This isn't quite sufficient. xerrno is not
542 * necessarily meaningful to another system, so we really should
543 * expand it. Additionally, we should identify ourselves. Someone
544 * might want to know. Someone _will_ want to know OTOH, the first
545 * X-CACHE-MISS entry should tell us who.
547 httpHeaderPutStrf(&rep
->header
, HDR_X_SQUID_ERROR
, "%s %d",
548 errorPageName(err
->page_id
), err
->xerrno
);
549 httpBodySet(&rep
->body
, &content
);
550 /* do not memBufClean() the content, it was absorbed by httpBody */
555 errorBuildContent(ErrorState
* err
)
562 assert(err
->page_id
> ERR_NONE
&& err
->page_id
< error_page_count
);
563 memBufDefInit(&content
);
564 m
= error_text
[err
->page_id
];
566 while ((p
= strchr(m
, '%'))) {
567 memBufAppend(&content
, m
, p
- m
); /* copy */
568 t
= errorConvert(*++p
, err
); /* convert */
569 memBufPrintf(&content
, "%s", t
); /* copy */
570 m
= p
+ 1; /* advance */
573 memBufPrintf(&content
, "%s", m
); /* copy tail */
574 assert(content
.size
== strlen(content
.buf
));