]>
Commit | Line | Data |
---|---|---|
4a9a952e | 1 | |
30a4f2a8 | 2 | /* |
1cfdbcf0 | 3 | * $Id: errorpage.cc,v 1.145 1999/01/11 22:54:19 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/ |
e25c139f | 9 | * ---------------------------------------------------------- |
30a4f2a8 | 10 | * |
11 | * Squid is the result of efforts by numerous individuals from the | |
12 | * Internet community. Development is led by Duane Wessels of the | |
e25c139f | 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. | |
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 | ||
48 | typedef struct { | |
49 | int id; | |
50 | char *page_name; | |
51 | } ErrorDynamicPageInfo; | |
52 | ||
53 | /* local constant and vars */ | |
54 | ||
1cfdbcf0 | 55 | static const char *const proxy_auth_challenge_fmt = "Basic realm=\"%s\""; |
56 | ||
1d803566 | 57 | /* |
58 | * note: hard coded error messages are not appended with %S automagically | |
59 | * to give you more control on the format | |
60 | */ | |
2ac76861 | 61 | static const struct { |
1afe05c5 | 62 | int type; /* and page_id */ |
2ac76861 | 63 | const char *text; |
64 | } error_hard_text[] = { | |
1afe05c5 | 65 | |
2ac76861 | 66 | { |
67 | ERR_SQUID_SIGNATURE, | |
68 | "\n<br clear=\"all\">\n" | |
69 | "<hr noshade size=1>\n" | |
5d85cb62 | 70 | "Generated %T by %h (<a href=\"http://squid.nlanr.net/Squid/\">%s</a>)\n" |
e32b11fc | 71 | "</BODY></HTML>\n" |
1d803566 | 72 | } |
73 | }; | |
02922e76 | 74 | |
75 | static Stack ErrorDynamicPages; | |
76 | ||
77 | /* local prototypes */ | |
78 | ||
2ac76861 | 79 | static const int error_hard_text_count = sizeof(error_hard_text) / sizeof(*error_hard_text); |
02922e76 | 80 | static char **error_text = NULL; |
81 | static int error_page_count = 0; | |
9b312a19 | 82 | |
02922e76 | 83 | static char *errorTryLoadText(const char *page_name, const char *dir); |
84 | static char *errorLoadText(const char *page_name); | |
1d803566 | 85 | static const char *errorFindHardText(err_type type); |
c68e9c6b | 86 | static ErrorDynamicPageInfo *errorDynamicPageInfoCreate(int id, const char *page_name); |
87 | static void errorDynamicPageInfoDestroy(ErrorDynamicPageInfo * info); | |
1d803566 | 88 | static MemBuf errorBuildContent(ErrorState * err); |
fe40a877 | 89 | static const char *errorConvert(char token, ErrorState * err); |
9b312a19 | 90 | static CWCB errorSendComplete; |
1e74c110 | 91 | |
fe40a877 | 92 | /* |
93 | * Function: errorInitialize | |
94 | * | |
1d803566 | 95 | * Abstract: This function finds the error messages formats, and stores |
fe40a877 | 96 | * them in error_text[]; |
97 | * | |
98 | * Global effects: | |
99 | * error_text[] - is modified | |
100 | */ | |
b8d8561b | 101 | void |
0673c0ba | 102 | errorInitialize(void) |
fa966b74 | 103 | { |
728da2ee | 104 | err_type i; |
1d803566 | 105 | const char *text; |
02922e76 | 106 | error_page_count = ERR_MAX + ErrorDynamicPages.count; |
1afe05c5 | 107 | error_text = xcalloc(error_page_count, sizeof(char *)); |
728da2ee | 108 | for (i = ERR_NONE, i++; i < error_page_count; i++) { |
1d803566 | 109 | safe_free(error_text[i]); |
110 | /* hard-coded ? */ | |
111 | if ((text = errorFindHardText(i))) | |
112 | error_text[i] = xstrdup(text); | |
c68e9c6b | 113 | else if (i < ERR_MAX) { |
1afe05c5 | 114 | /* precompiled ? */ |
02922e76 | 115 | error_text[i] = errorLoadText(err_type_str[i]); |
c68e9c6b | 116 | } else { |
117 | /* dynamic */ | |
1afe05c5 | 118 | ErrorDynamicPageInfo *info = ErrorDynamicPages.items[i - ERR_MAX]; |
02922e76 | 119 | assert(info && info->id == i && info->page_name); |
120 | error_text[i] = errorLoadText(info->page_name); | |
121 | } | |
1d803566 | 122 | assert(error_text[i]); |
123 | } | |
124 | } | |
125 | ||
c68e9c6b | 126 | void |
127 | errorClean(void) | |
128 | { | |
129 | if (error_text) { | |
130 | int i; | |
131 | for (i = ERR_NONE + 1; i < error_page_count; i++) | |
132 | safe_free(error_text[i]); | |
133 | safe_free(error_text); | |
134 | } | |
135 | while (ErrorDynamicPages.count) | |
136 | errorDynamicPageInfoDestroy(stackPop(&ErrorDynamicPages)); | |
137 | error_page_count = 0; | |
138 | } | |
139 | ||
1d803566 | 140 | static const char * |
141 | errorFindHardText(err_type type) | |
142 | { | |
143 | int i; | |
144 | for (i = 0; i < error_hard_text_count; i++) | |
145 | if (error_hard_text[i].type == type) | |
146 | return error_hard_text[i].text; | |
147 | return NULL; | |
148 | } | |
149 | ||
150 | ||
151 | static char * | |
02922e76 | 152 | errorLoadText(const char *page_name) |
1d803566 | 153 | { |
154 | /* test configured location */ | |
02922e76 | 155 | char *text = errorTryLoadText(page_name, Config.errorDirectory); |
1d803566 | 156 | /* test default location if failed */ |
157 | if (!text && strcmp(Config.errorDirectory, DEFAULT_SQUID_ERROR_DIR)) | |
02922e76 | 158 | text = errorTryLoadText(page_name, DEFAULT_SQUID_ERROR_DIR); |
1d803566 | 159 | /* giving up if failed */ |
160 | if (!text) | |
161 | fatal("failed to find or read error text file."); | |
162 | return text; | |
163 | } | |
164 | ||
165 | static char * | |
02922e76 | 166 | errorTryLoadText(const char *page_name, const char *dir) |
1d803566 | 167 | { |
9b312a19 | 168 | int fd; |
169 | char path[MAXPATHLEN]; | |
170 | struct stat sb; | |
1d803566 | 171 | char *text; |
172 | ||
137ee196 | 173 | snprintf(path, sizeof(path), "%s/%s", dir, page_name); |
1d803566 | 174 | fd = file_open(path, O_RDONLY, NULL, NULL, NULL); |
175 | if (fd < 0 || fstat(fd, &sb) < 0) { | |
176 | debug(4, 0) ("errorTryLoadText: '%s': %s\n", path, xstrerror()); | |
177 | if (fd >= 0) | |
178 | file_close(fd); | |
179 | return NULL; | |
180 | } | |
d5e36424 | 181 | text = xcalloc(sb.st_size + 2 + 1, 1); /* 2 == space for %S */ |
1d803566 | 182 | if (read(fd, text, sb.st_size) != sb.st_size) { |
183 | debug(4, 0) ("errorTryLoadText: failed to fully read: '%s': %s\n", | |
184 | path, xstrerror()); | |
185 | xfree(text); | |
186 | text = NULL; | |
1e74c110 | 187 | } |
1d803566 | 188 | file_close(fd); |
c68e9c6b | 189 | if (strstr(text, "%s") == NULL) |
190 | strcat(text, "%S"); /* add signature */ | |
1d803566 | 191 | return text; |
6eb42cae | 192 | } |
193 | ||
02922e76 | 194 | static ErrorDynamicPageInfo * |
195 | errorDynamicPageInfoCreate(int id, const char *page_name) | |
196 | { | |
197 | ErrorDynamicPageInfo *info = xcalloc(1, sizeof(ErrorDynamicPageInfo)); | |
198 | info->id = id; | |
199 | info->page_name = xstrdup(page_name); | |
200 | return info; | |
201 | } | |
202 | ||
203 | static void | |
1afe05c5 | 204 | errorDynamicPageInfoDestroy(ErrorDynamicPageInfo * info) |
02922e76 | 205 | { |
206 | assert(info); | |
207 | xfree(info->page_name); | |
208 | xfree(info); | |
209 | } | |
210 | ||
211 | int | |
212 | errorReservePageId(const char *page_name) | |
213 | { | |
1afe05c5 | 214 | ErrorDynamicPageInfo *info = |
215 | errorDynamicPageInfoCreate(ERR_MAX + ErrorDynamicPages.count, page_name); | |
02922e76 | 216 | stackPush(&ErrorDynamicPages, info); |
217 | return info->id; | |
218 | } | |
219 | ||
c68e9c6b | 220 | static const char * |
221 | errorPageName(int pageId) | |
53ad48e6 | 222 | { |
c68e9c6b | 223 | if (pageId >= ERR_NONE && pageId < ERR_MAX) /* common case */ |
224 | return err_type_str[pageId]; | |
225 | if (pageId >= ERR_MAX && pageId - ERR_MAX < ErrorDynamicPages.count) | |
226 | return ((ErrorDynamicPageInfo *) ErrorDynamicPages. | |
227 | items[pageId - ERR_MAX])->page_name; | |
228 | return "ERR_UNKNOWN"; /* should not happen */ | |
53ad48e6 | 229 | } |
230 | ||
fe40a877 | 231 | /* |
232 | * Function: errorCon | |
233 | * | |
234 | * Abstract: This function creates a ErrorState object. | |
235 | */ | |
236 | ErrorState * | |
728da2ee | 237 | errorCon(err_type type, http_status status) |
fe40a877 | 238 | { |
239 | ErrorState *err = xcalloc(1, sizeof(ErrorState)); | |
1afe05c5 | 240 | err->page_id = type; /* has to be reset manually if needed */ |
fe40a877 | 241 | err->type = type; |
242 | err->http_status = status; | |
243 | return err; | |
244 | } | |
245 | ||
246 | /* | |
247 | * Function: errorAppendEntry | |
248 | * | |
249 | * Arguments: err - This object is destroyed after use in this function. | |
250 | * | |
251 | * Abstract: This function generates a error page from the info contained | |
4e3f29eb | 252 | * by 'err' and then stores the text in the specified store |
253 | * entry. This function should only be called by ``server | |
254 | * side routines'' which need to communicate errors to the | |
79e0dc20 | 255 | * client side. It should also be called from client_side.c |
256 | * because we now support persistent connections, and | |
257 | * cannot assume that we can immediately write to the socket | |
258 | * for an error. | |
fe40a877 | 259 | */ |
260 | void | |
261 | errorAppendEntry(StoreEntry * entry, ErrorState * err) | |
262 | { | |
cb69b4c7 | 263 | HttpReply *rep; |
cb69b4c7 | 264 | MemObject *mem = entry->mem_obj; |
901e234d | 265 | /* |
266 | * Kostas sez PUT "success" replies might not be STORE_PENDING? | |
267 | */ | |
ef849d44 | 268 | /* assert(entry->store_status == STORE_PENDING); */ |
b50179a6 | 269 | assert(mem != NULL); |
4e3f29eb | 270 | assert(mem->inmem_hi == 0); |
429cf70b | 271 | storeBuffer(entry); |
cb69b4c7 | 272 | rep = errorBuildReply(err); |
1cfdbcf0 | 273 | /* Add authentication header */ |
274 | switch (err->http_status) { | |
275 | case HTTP_PROXY_AUTHENTICATION_REQUIRED: | |
276 | /* Proxy authorisation needed */ | |
277 | httpHeaderPutStrf(&rep->header, HDR_PROXY_AUTHENTICATE, | |
278 | proxy_auth_challenge_fmt, Config.proxyAuthRealm); | |
279 | break; | |
280 | case HTTP_UNAUTHORIZED: | |
281 | /* WWW Authorisation needed */ | |
282 | httpHeaderPutStrf(&rep->header, HDR_WWW_AUTHENTICATE, | |
283 | proxy_auth_challenge_fmt, Config.proxyAuthRealm); | |
284 | break; | |
285 | default: | |
286 | /* Keep GCC happy */ | |
287 | break; | |
288 | } | |
cb69b4c7 | 289 | httpReplySwapOut(rep, entry); |
290 | httpReplyDestroy(rep); | |
cb69b4c7 | 291 | mem->reply->sline.status = err->http_status; |
2fcac026 | 292 | mem->reply->content_length = -1; |
429cf70b | 293 | storeBufferFlush(entry); |
00691420 | 294 | storeComplete(entry); |
b50179a6 | 295 | storeNegativeCache(entry); |
296 | storeReleaseRequest(entry); | |
fe40a877 | 297 | errorStateFree(err); |
fe40a877 | 298 | } |
299 | ||
300 | /* | |
301 | * Function: errorSend | |
302 | * | |
303 | * Arguments: err - This object is destroyed after use in this function. | |
304 | * | |
305 | * Abstract: This function generates a error page from the info contained | |
306 | * by 'err' and then sends it to the client. | |
4e3f29eb | 307 | * The callback function errorSendComplete() is called after |
308 | * the page has been written to the client socket (fd). | |
309 | * errorSendComplete() deallocates 'err'. We need to add | |
310 | * 'err' to the cbdata because comm_write() requires it | |
311 | * for all callback data pointers. | |
79e0dc20 | 312 | * |
313 | * Note, normally errorSend() should only be called from | |
314 | * routines in ssl.c and pass.c, where we don't have any | |
315 | * StoreEntry's. In client_side.c we must allocate a StoreEntry | |
316 | * for errors and use errorAppendEntry() to account for | |
317 | * persistent/pipeline connections. | |
fe40a877 | 318 | */ |
319 | void | |
320 | errorSend(int fd, ErrorState * err) | |
321 | { | |
cb69b4c7 | 322 | HttpReply *rep; |
fe40a877 | 323 | debug(4, 3) ("errorSend: FD %d, err=%p\n", fd, err); |
324 | assert(fd >= 0); | |
88aad2e5 | 325 | /* |
326 | * ugh, this is how we make sure error codes get back to | |
327 | * the client side for logging and error tracking. | |
328 | */ | |
329 | if (err->request) | |
330 | err->request->err_type = err->type; | |
cb69b4c7 | 331 | /* moved in front of errorBuildBuf @?@ */ |
b515fc11 | 332 | err->flags.flag_cbdata = 1; |
db1cd23c | 333 | cbdataAdd(err, cbdataXfree, 0); |
cb69b4c7 | 334 | rep = errorBuildReply(err); |
335 | comm_write_mbuf(fd, httpReplyPack(rep), errorSendComplete, err); | |
336 | httpReplyDestroy(rep); | |
fe40a877 | 337 | } |
338 | ||
339 | /* | |
340 | * Function: errorSendComplete | |
341 | * | |
4e3f29eb | 342 | * Abstract: Called by commHandleWrite() after data has been written |
343 | * to the client socket. | |
fe40a877 | 344 | * |
345 | * Note: If there is a callback, the callback is responsible for | |
346 | * closeing the FD, otherwise we do it ourseves. | |
347 | */ | |
348 | static void | |
79a15e0a | 349 | errorSendComplete(int fd, char *bufnotused, size_t size, int errflag, void *data) |
fe40a877 | 350 | { |
351 | ErrorState *err = data; | |
352 | debug(4, 3) ("errorSendComplete: FD %d, size=%d\n", fd, size); | |
353 | if (errflag != COMM_ERR_CLOSING) { | |
354 | if (err->callback) | |
355 | err->callback(fd, err->callback_data, size); | |
356 | else | |
357 | comm_close(fd); | |
358 | } | |
359 | errorStateFree(err); | |
fe40a877 | 360 | } |
361 | ||
cb69b4c7 | 362 | void |
9b312a19 | 363 | errorStateFree(ErrorState * err) |
6eb42cae | 364 | { |
9b312a19 | 365 | requestUnlink(err->request); |
366 | safe_free(err->redirect_url); | |
367 | safe_free(err->url); | |
76a5501f | 368 | safe_free(err->host); |
5f3c4e9a | 369 | safe_free(err->dnsserver_msg); |
b5af8569 | 370 | safe_free(err->request_hdrs); |
b515fc11 | 371 | if (err->flags.flag_cbdata) |
38d04788 | 372 | cbdataFree(err); |
bb0929d8 | 373 | else |
374 | safe_free(err); | |
1e74c110 | 375 | } |
8213067d | 376 | |
2658f489 | 377 | #define CVT_BUF_SZ 512 |
fe40a877 | 378 | |
379 | /* | |
1d803566 | 380 | * B - URL with FTP %2f hack x |
381 | * c - Squid error code x | |
382 | * d - seconds elapsed since request received x | |
fe40a877 | 383 | * e - errno x |
384 | * E - strerror() x | |
fe40a877 | 385 | * f - FTP request line x |
969c39b9 | 386 | * F - FTP reply line x |
7131112f | 387 | * g - FTP server message x |
fe40a877 | 388 | * h - cache hostname x |
969c39b9 | 389 | * H - server host name x |
fe40a877 | 390 | * i - client IP address x |
391 | * I - server IP address x | |
392 | * L - HREF link for more info/contact x | |
393 | * M - Request Method x | |
394 | * p - URL port # x | |
395 | * P - Protocol x | |
b5af8569 | 396 | * R - Full HTTP Request x |
1d803566 | 397 | * S - squid signature from ERR_SIGNATURE x |
398 | * s - caching proxy software with version x | |
fe40a877 | 399 | * t - local time x |
400 | * T - UTC x | |
969c39b9 | 401 | * U - URL without password x |
402 | * u - URL without password, %2f added to path x | |
fe40a877 | 403 | * w - cachemgr email address x |
404 | * z - dns server error message x | |
405 | */ | |
406 | ||
407 | static const char * | |
9b312a19 | 408 | errorConvert(char token, ErrorState * err) |
8213067d | 409 | { |
2658f489 | 410 | request_t *r = err->request; |
137ee196 | 411 | static MemBuf mb = MemBufNULL; |
eeb423fb | 412 | const char *p = NULL; /* takes priority over mb if set */ |
413 | ||
137ee196 | 414 | memBufReset(&mb); |
9b312a19 | 415 | switch (token) { |
8f872bb6 | 416 | case 'B': |
417 | p = r ? ftpUrlWith2f(r) : "[no URL]"; | |
418 | break; | |
042461c3 | 419 | case 'e': |
137ee196 | 420 | memBufPrintf(&mb, "%d", err->xerrno); |
042461c3 | 421 | break; |
422 | case 'E': | |
23d92c64 | 423 | if (err->xerrno) |
137ee196 | 424 | memBufPrintf(&mb, "(%d) %s", err->xerrno, strerror(err->xerrno)); |
23d92c64 | 425 | else |
137ee196 | 426 | memBufPrintf(&mb, "[No Error]"); |
03d7b07f | 427 | break; |
fe40a877 | 428 | case 'f': |
429 | /* FTP REQUEST LINE */ | |
430 | if (err->ftp.request) | |
431 | p = err->ftp.request; | |
432 | else | |
a8af3a35 | 433 | p = "nothing"; |
fe40a877 | 434 | break; |
435 | case 'F': | |
436 | /* FTP REPLY LINE */ | |
437 | if (err->ftp.request) | |
438 | p = err->ftp.reply; | |
439 | else | |
a8af3a35 | 440 | p = "nothing"; |
03d7b07f | 441 | break; |
7131112f | 442 | case 'g': |
443 | /* FTP SERVER MESSAGE */ | |
137ee196 | 444 | wordlistCat(err->ftp_server_msg, &mb); |
7131112f | 445 | break; |
03d7b07f | 446 | case 'h': |
137ee196 | 447 | memBufPrintf(&mb, "%s", getMyHostname()); |
f787fb1e | 448 | break; |
fe40a877 | 449 | case 'H': |
450 | p = r ? r->host : "[unknown host]"; | |
f787fb1e | 451 | break; |
452 | case 'i': | |
137ee196 | 453 | memBufPrintf(&mb, "%s", inet_ntoa(err->src_addr)); |
f6aa1a5c | 454 | break; |
f787fb1e | 455 | case 'I': |
456 | if (err->host) { | |
137ee196 | 457 | memBufPrintf(&mb, "%s", err->host); |
f787fb1e | 458 | } else |
fe40a877 | 459 | p = "[unknown]"; |
460 | break; | |
461 | case 'L': | |
462 | if (Config.errHtmlText) { | |
137ee196 | 463 | memBufPrintf(&mb, "%s", Config.errHtmlText); |
fe40a877 | 464 | } else |
465 | p = "[not available]"; | |
466 | break; | |
467 | case 'M': | |
468 | p = r ? RequestMethodStr[r->method] : "[unkown method]"; | |
469 | break; | |
470 | case 'p': | |
471 | if (r) { | |
137ee196 | 472 | memBufPrintf(&mb, "%d", (int) r->port); |
fe40a877 | 473 | } else { |
474 | p = "[unknown port]"; | |
475 | } | |
476 | break; | |
477 | case 'P': | |
478 | p = r ? ProtocolStr[r->protocol] : "[unkown protocol]"; | |
479 | break; | |
b5af8569 | 480 | case 'R': |
865094d7 | 481 | if (NULL != r) { |
865094d7 | 482 | Packer p; |
865094d7 | 483 | memBufPrintf(&mb, "%s %s HTTP/%3.1f\n", |
484 | RequestMethodStr[r->method], | |
485 | strLen(r->urlpath) ? strBuf(r->urlpath) : "/", | |
486 | (double) r->http_ver); | |
487 | packerToMemInit(&p, &mb); | |
488 | httpHeaderPackInto(&r->header, &p); | |
489 | packerClean(&p); | |
865094d7 | 490 | } else if (err->request_hdrs) { |
491 | p = err->request_hdrs; | |
492 | } else { | |
493 | p = "[no request]"; | |
494 | } | |
b5af8569 | 495 | break; |
1d803566 | 496 | case 's': |
497 | p = full_appname_string; | |
498 | break; | |
499 | case 'S': | |
500 | /* signature may contain %-escapes, recursion */ | |
02922e76 | 501 | if (err->page_id != ERR_SQUID_SIGNATURE) { |
502 | const int saved_id = err->page_id; | |
137ee196 | 503 | MemBuf sign_mb; |
02922e76 | 504 | err->page_id = ERR_SQUID_SIGNATURE; |
137ee196 | 505 | sign_mb = errorBuildContent(err); |
506 | memBufPrintf(&mb, "%s", sign_mb.buf); | |
507 | memBufClean(&sign_mb); | |
02922e76 | 508 | err->page_id = saved_id; |
1d803566 | 509 | } else { |
510 | /* wow, somebody put %S into ERR_SIGNATURE, stop recursion */ | |
511 | p = "[%S]"; | |
512 | } | |
513 | break; | |
fe40a877 | 514 | case 't': |
137ee196 | 515 | memBufPrintf(&mb, "%s", mkhttpdlogtime(&squid_curtime)); |
f787fb1e | 516 | break; |
f8291f8f | 517 | case 'T': |
137ee196 | 518 | memBufPrintf(&mb, "%s", mkrfc1123(squid_curtime)); |
f8291f8f | 519 | break; |
fe40a877 | 520 | case 'U': |
b5af8569 | 521 | p = r ? urlCanonicalClean(r) : err->url ? err->url : "[no URL]"; |
b9916917 | 522 | break; |
fe40a877 | 523 | case 'w': |
137ee196 | 524 | if (Config.adminEmail) |
525 | memBufPrintf(&mb, "%s", Config.adminEmail); | |
526 | else | |
fe40a877 | 527 | p = "[unknown]"; |
528 | break; | |
529 | case 'z': | |
530 | if (err->dnsserver_msg) | |
531 | p = err->dnsserver_msg; | |
b9916917 | 532 | else |
fe40a877 | 533 | p = "[unknown]"; |
b9916917 | 534 | break; |
e347f8e5 | 535 | case '%': |
536 | p = "%"; | |
537 | break; | |
9b312a19 | 538 | default: |
539 | p = "%UNKNOWN%"; | |
540 | break; | |
541 | } | |
137ee196 | 542 | if (!p) |
eeb423fb | 543 | p = mb.buf; /* do not use mb after this assignment! */ |
137ee196 | 544 | assert(p); |
e102ebda | 545 | debug(4, 3) ("errorConvert: %%%c --> '%s'\n", token, p); |
9b312a19 | 546 | return p; |
8213067d | 547 | } |
e381a13d | 548 | |
cb69b4c7 | 549 | /* allocates and initializes an error response */ |
550 | HttpReply * | |
2ac76861 | 551 | errorBuildReply(ErrorState * err) |
cb69b4c7 | 552 | { |
cb69b4c7 | 553 | HttpReply *rep = httpReplyCreate(); |
1d803566 | 554 | MemBuf content = errorBuildContent(err); |
cb69b4c7 | 555 | /* no LMT for error pages; error pages expire immediately */ |
1d803566 | 556 | httpReplySetHeaders(rep, 1.0, err->http_status, NULL, "text/html", content.size, 0, squid_curtime); |
fcc62502 | 557 | /* |
558 | * include some information for downstream caches. Implicit | |
2246b732 | 559 | * replaceable content. This isn't quite sufficient. xerrno is not |
fcc62502 | 560 | * necessarily meaningful to another system, so we really should |
561 | * expand it. Additionally, we should identify ourselves. Someone | |
562 | * might want to know. Someone _will_ want to know OTOH, the first | |
563 | * X-CACHE-MISS entry should tell us who. | |
564 | */ | |
2246b732 | 565 | httpHeaderPutStrf(&rep->header, HDR_X_SQUID_ERROR, "%s %d", |
c68e9c6b | 566 | errorPageName(err->page_id), err->xerrno); |
1d21d91d | 567 | httpBodySet(&rep->body, &content); |
568 | /* do not memBufClean() the content, it was absorbed by httpBody */ | |
cb69b4c7 | 569 | return rep; |
570 | } | |
571 | ||
1d803566 | 572 | static MemBuf |
573 | errorBuildContent(ErrorState * err) | |
cb69b4c7 | 574 | { |
1d803566 | 575 | MemBuf content; |
1d803566 | 576 | const char *m; |
577 | const char *p; | |
cb69b4c7 | 578 | const char *t; |
579 | assert(err != NULL); | |
02922e76 | 580 | assert(err->page_id > ERR_NONE && err->page_id < error_page_count); |
1d803566 | 581 | memBufDefInit(&content); |
02922e76 | 582 | m = error_text[err->page_id]; |
1d803566 | 583 | assert(m); |
cb69b4c7 | 584 | while ((p = strchr(m, '%'))) { |
2ac76861 | 585 | memBufAppend(&content, m, p - m); /* copy */ |
586 | t = errorConvert(*++p, err); /* convert */ | |
587 | memBufPrintf(&content, "%s", t); /* copy */ | |
588 | m = p + 1; /* advance */ | |
cb69b4c7 | 589 | } |
1d803566 | 590 | if (*m) |
2ac76861 | 591 | memBufPrintf(&content, "%s", m); /* copy tail */ |
1d803566 | 592 | assert(content.size == strlen(content.buf)); |
cb69b4c7 | 593 | return content; |
594 | } |