]> git.ipfire.org Git - thirdparty/squid.git/blame - src/errorpage.cc
LINT
[thirdparty/squid.git] / src / errorpage.cc
CommitLineData
4a9a952e 1
30a4f2a8 2/*
79d39a72 3 * $Id: errorpage.cc,v 1.100 1997/11/05 05:29: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
fe40a877 41static const char *err_string[] =
1e74c110 42{
9b312a19 43 "ERR_NONE",
44 "ERR_READ_TIMEOUT",
45 "ERR_LIFETIME_EXP",
9b312a19 46 "ERR_READ_ERROR",
47 "ERR_WRITE_ERROR",
48 "ERR_CLIENT_ABORT",
49 "ERR_CONNECT_FAIL",
50 "ERR_INVALID_REQ",
51 "ERR_UNSUP_REQ",
52 "ERR_INVALID_URL",
53 "ERR_SOCKET_FAILURE",
54 "ERR_DNS_FAIL",
9b312a19 55 "ERR_CANNOT_FORWARD",
56 "ERR_NO_RELAY",
9b312a19 57 "ERR_ZERO_SIZE_OBJECT",
58 "ERR_FTP_DISABLED",
b9916917 59 "ERR_FTP_FAILURE",
9b312a19 60 "ERR_ACCESS_DENIED",
61 "ERR_MAX"
1e74c110 62};
63
9b312a19 64static char *error_text[ERR_MAX];
65
f5b8bbc4 66static void errorStateFree(ErrorState * err);
fe40a877 67static const char *errorConvert(char token, ErrorState * err);
68static const char *errorBuildBuf(ErrorState * err, int *len);
9b312a19 69static CWCB errorSendComplete;
1e74c110 70
fe40a877 71/*
72 * Function: errorInitialize
73 *
74 * Abstract: This function reads in the error messages formats, and stores
75 * them in error_text[];
76 *
77 * Global effects:
78 * error_text[] - is modified
79 */
b8d8561b 80void
0673c0ba 81errorInitialize(void)
fa966b74 82{
9b312a19 83 err_type i;
84 int fd;
85 char path[MAXPATHLEN];
86 struct stat sb;
3d36dfc8 87 assert(sizeof(err_string) == (ERR_MAX + 1) * sizeof(char *));
9b312a19 88 for (i = ERR_NONE + 1; i < ERR_MAX; i++) {
89 snprintf(path, MAXPATHLEN, "%s/%s",
90 Config.errorDirectory, err_string[i]);
91 fd = file_open(path, O_RDONLY, NULL, NULL);
92 if (fd < 0) {
93 debug(4, 0) ("errorInitialize: %s: %s\n", path, xstrerror());
94 fatal("Failed to open error text file");
95 }
96 if (fstat(fd, &sb) < 0)
fe40a877 97 fatal("fstat() failed on error text file");
9b312a19 98 safe_free(error_text[i]);
f787fb1e 99 error_text[i] = xcalloc(sb.st_size + 1, 1);
9b312a19 100 if (read(fd, error_text[i], sb.st_size) != sb.st_size)
fe40a877 101 fatal("failed to fully read error text file");
9b312a19 102 file_close(fd);
1e74c110 103 }
6eb42cae 104}
105
fe40a877 106/*
107 * Function: errorCon
108 *
109 * Abstract: This function creates a ErrorState object.
110 */
111ErrorState *
112errorCon(err_type type, http_status status)
113{
114 ErrorState *err = xcalloc(1, sizeof(ErrorState));
115 err->type = type;
116 err->http_status = status;
117 return err;
118}
119
120/*
121 * Function: errorAppendEntry
122 *
123 * Arguments: err - This object is destroyed after use in this function.
124 *
125 * Abstract: This function generates a error page from the info contained
4e3f29eb 126 * by 'err' and then stores the text in the specified store
127 * entry. This function should only be called by ``server
128 * side routines'' which need to communicate errors to the
129 * client side. The client side should call errorSend().
fe40a877 130 */
131void
132errorAppendEntry(StoreEntry * entry, ErrorState * err)
133{
134 const char *buf;
135 MemObject *mem = entry->mem_obj;
136 int len;
137 assert(entry->store_status == STORE_PENDING);
4e3f29eb 138#if WE_SHOULD_PROBABLY_REQUIRE_THIS
139 assert(mem->inmem_hi == 0);
140#endif
fe40a877 141 buf = errorBuildBuf(err, &len);
142 storeAppend(entry, buf, len);
143 if (mem)
144 mem->reply->code = err->http_status;
145 errorStateFree(err);
fe40a877 146}
147
148/*
149 * Function: errorSend
150 *
151 * Arguments: err - This object is destroyed after use in this function.
152 *
153 * Abstract: This function generates a error page from the info contained
154 * by 'err' and then sends it to the client.
4e3f29eb 155 * The callback function errorSendComplete() is called after
156 * the page has been written to the client socket (fd).
157 * errorSendComplete() deallocates 'err'. We need to add
158 * 'err' to the cbdata because comm_write() requires it
159 * for all callback data pointers.
fe40a877 160 */
161void
162errorSend(int fd, ErrorState * err)
163{
164 const char *buf;
165 int len;
166 debug(4, 3) ("errorSend: FD %d, err=%p\n", fd, err);
167 assert(fd >= 0);
168 buf = errorBuildBuf(err, &len);
536ae767 169 BIT_SET(err->flags, ERR_FLAG_CBDATA);
fe40a877 170 cbdataAdd(err);
171 comm_write(fd, xstrdup(buf), len, errorSendComplete, err, xfree);
172}
173
174/*
175 * Function: errorSendComplete
176 *
4e3f29eb 177 * Abstract: Called by commHandleWrite() after data has been written
178 * to the client socket.
fe40a877 179 *
180 * Note: If there is a callback, the callback is responsible for
181 * closeing the FD, otherwise we do it ourseves.
182 */
183static void
79d39a72 184errorSendComplete(int fd, char *bufnotused, int size, int errflag, void *data)
fe40a877 185{
186 ErrorState *err = data;
187 debug(4, 3) ("errorSendComplete: FD %d, size=%d\n", fd, size);
188 if (errflag != COMM_ERR_CLOSING) {
189 if (err->callback)
190 err->callback(fd, err->callback_data, size);
191 else
192 comm_close(fd);
193 }
194 errorStateFree(err);
fe40a877 195}
196
9b312a19 197static void
198errorStateFree(ErrorState * err)
6eb42cae 199{
9b312a19 200 requestUnlink(err->request);
201 safe_free(err->redirect_url);
202 safe_free(err->url);
76a5501f 203 safe_free(err->host);
5f3c4e9a 204 safe_free(err->dnsserver_msg);
bb0929d8 205 if (BIT_TEST(err->flags, ERR_FLAG_CBDATA))
38d04788 206 cbdataFree(err);
bb0929d8 207 else
208 safe_free(err);
1e74c110 209}
8213067d 210
2658f489 211#define CVT_BUF_SZ 512
fe40a877 212
213/*
214 * c - Squid error code
215 * d - seconds elapsed since request received
216 * e - errno x
217 * E - strerror() x
218 * F - FTP reply line x
219 * f - FTP request line x
220 * h - cache hostname x
221 * i - client IP address x
222 * I - server IP address x
223 * L - HREF link for more info/contact x
224 * M - Request Method x
225 * p - URL port # x
226 * P - Protocol x
227 * t - local time x
228 * T - UTC x
229 * w - cachemgr email address x
230 * z - dns server error message x
231 */
232
233static const char *
9b312a19 234errorConvert(char token, ErrorState * err)
8213067d 235{
2658f489 236 request_t *r = err->request;
237 static char buf[CVT_BUF_SZ];
fe40a877 238 const char *p = buf;
9b312a19 239 switch (token) {
042461c3 240 case 'e':
c45ed9ad 241 snprintf(buf, CVT_BUF_SZ, "%d", err->xerrno);
042461c3 242 break;
243 case 'E':
c45ed9ad 244 snprintf(buf, CVT_BUF_SZ, "(%d) %s", err->xerrno, strerror(err->xerrno));
03d7b07f 245 break;
fe40a877 246 case 'f':
247 /* FTP REQUEST LINE */
248 if (err->ftp.request)
249 p = err->ftp.request;
250 else
251 p = "<none>";
252 break;
253 case 'F':
254 /* FTP REPLY LINE */
255 if (err->ftp.request)
256 p = err->ftp.reply;
257 else
258 p = "<none>";
03d7b07f 259 break;
260 case 'h':
261 snprintf(buf, CVT_BUF_SZ, "%s", getMyHostname());
f787fb1e 262 break;
fe40a877 263 case 'H':
264 p = r ? r->host : "[unknown host]";
f787fb1e 265 break;
266 case 'i':
267 snprintf(buf, CVT_BUF_SZ, "%s", inet_ntoa(err->src_addr));
f6aa1a5c 268 break;
f787fb1e 269 case 'I':
270 if (err->host) {
271 snprintf(buf, CVT_BUF_SZ, "%s", err->host);
f787fb1e 272 } else
fe40a877 273 p = "[unknown]";
274 break;
275 case 'L':
276 if (Config.errHtmlText) {
277 snprintf(buf, CVT_BUF_SZ, "%s", Config.errHtmlText);
278 } else
279 p = "[not available]";
280 break;
281 case 'M':
282 p = r ? RequestMethodStr[r->method] : "[unkown method]";
283 break;
284 case 'p':
285 if (r) {
286 snprintf(buf, CVT_BUF_SZ, "%d", (int) r->port);
287 } else {
288 p = "[unknown port]";
289 }
290 break;
291 case 'P':
292 p = r ? ProtocolStr[r->protocol] : "[unkown protocol]";
293 break;
294 case 't':
295 xstrncpy(buf, mkhttpdlogtime(&squid_curtime), 128);
f787fb1e 296 break;
f8291f8f 297 case 'T':
298 snprintf(buf, CVT_BUF_SZ, "%s", mkrfc1123(squid_curtime));
f8291f8f 299 break;
fe40a877 300 case 'U':
301 p = r ? urlCanonicalClean(r) : err->url;
b9916917 302 break;
fe40a877 303 case 'w':
304 if (Config.adminEmail) {
305 snprintf(buf, CVT_BUF_SZ, "%s", Config.adminEmail);
306 } else
307 p = "[unknown]";
308 break;
309 case 'z':
310 if (err->dnsserver_msg)
311 p = err->dnsserver_msg;
b9916917 312 else
fe40a877 313 p = "[unknown]";
b9916917 314 break;
e347f8e5 315 case '%':
316 p = "%";
317 break;
9b312a19 318 default:
319 p = "%UNKNOWN%";
320 break;
321 }
322 if (p == NULL)
323 p = "<NULL>";
e102ebda 324 debug(4, 3) ("errorConvert: %%%c --> '%s'\n", token, p);
9b312a19 325 return p;
8213067d 326}
e381a13d 327
fe40a877 328static const char *
9b312a19 329errorBuildBuf(ErrorState * err, int *len)
e381a13d 330{
9b312a19 331 LOCAL_ARRAY(char, buf, ERROR_BUF_SZ);
332 LOCAL_ARRAY(char, content, ERROR_BUF_SZ);
333 char *hdr;
334 int clen;
335 int tlen;
336 char *m;
b8b780a4 337 char *mx;
9b312a19 338 char *p;
fe40a877 339 const char *t;
9b312a19 340 assert(err != NULL);
341 assert(err->type > ERR_NONE && err->type < ERR_MAX);
b8b780a4 342 mx = m = xstrdup(error_text[err->type]);
9b312a19 343 clen = 0;
344 while ((p = strchr(m, '%'))) {
345 *p = '\0'; /* terminate */
346 xstrncpy(content + clen, m, ERROR_BUF_SZ - clen); /* copy */
347 clen += (p - m); /* advance */
348 if (clen >= ERROR_BUF_SZ)
349 break;
350 p++;
351 m = p + 1;
352 t = errorConvert(*p, err); /* convert */
353 xstrncpy(content + clen, t, ERROR_BUF_SZ - clen); /* copy */
354 clen += strlen(t); /* advance */
355 if (clen >= ERROR_BUF_SZ)
356 break;
357 }
358 if (clen < ERROR_BUF_SZ && m != NULL) {
359 xstrncpy(content + clen, m, ERROR_BUF_SZ - clen);
360 clen += strlen(m);
361 }
362 if (clen >= ERROR_BUF_SZ) {
363 clen = ERROR_BUF_SZ - 1;
364 *(content + clen) = '\0';
365 }
366 assert(clen == strlen(content));
367 hdr = httpReplyHeader((double) 1.0,
368 err->http_status,
b8d8561b 369 "text/html",
9b312a19 370 clen,
437e2060 371 0, /* no LMT for error pages */
9b312a19 372 squid_curtime);
373 tlen = snprintf(buf, ERROR_BUF_SZ, "%s\r\n%s", hdr, content);
374 if (len)
375 *len = tlen;
b8b780a4 376 xfree(mx);
9b312a19 377 return buf;
e381a13d 378}