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