]>
Commit | Line | Data |
---|---|---|
30a4f2a8 | 1 | /* |
30a4f2a8 | 2 | * DEBUG: section 10 Gopher |
3 | * AUTHOR: Harvest Derived | |
4 | * | |
2b6662ba | 5 | * SQUID Web Proxy Cache http://www.squid-cache.org/ |
e25c139f | 6 | * ---------------------------------------------------------- |
30a4f2a8 | 7 | * |
2b6662ba | 8 | * Squid is the result of efforts by numerous individuals from |
9 | * the Internet community; see the CONTRIBUTORS file for full | |
10 | * details. Many organizations have provided support for Squid's | |
11 | * development; see the SPONSORS file for full details. Squid is | |
12 | * Copyrighted (C) 2001 by the Regents of the University of | |
13 | * California; see the COPYRIGHT file for full details. Squid | |
14 | * incorporates software developed and/or copyrighted by other | |
15 | * sources; see the CREDITS file for full details. | |
30a4f2a8 | 16 | * |
17 | * This program is free software; you can redistribute it and/or modify | |
18 | * it under the terms of the GNU General Public License as published by | |
19 | * the Free Software Foundation; either version 2 of the License, or | |
20 | * (at your option) any later version. | |
26ac0430 | 21 | * |
30a4f2a8 | 22 | * This program is distributed in the hope that it will be useful, |
23 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
24 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
25 | * GNU General Public License for more details. | |
26ac0430 | 26 | * |
30a4f2a8 | 27 | * You should have received a copy of the GNU General Public License |
28 | * along with this program; if not, write to the Free Software | |
cbdec147 | 29 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA. |
019dd986 | 30 | */ |
44a47c6e | 31 | |
582c2af2 FC |
32 | #include "squid.h" |
33 | #include "comm.h" | |
ec41b64c | 34 | #include "comm/Write.h" |
aa839030 | 35 | #include "errorpage.h" |
c4ad1349 | 36 | #include "fd.h" |
582c2af2 | 37 | #include "forward.h" |
67679543 | 38 | #include "globals.h" |
25f98340 | 39 | #include "html_quote.h" |
9969d2a8 | 40 | #include "HttpReply.h" |
582c2af2 | 41 | #include "HttpRequest.h" |
8a89c28f | 42 | #include "Mem.h" |
582c2af2 | 43 | #include "MemBuf.h" |
b65ce00c | 44 | #include "mime.h" |
582c2af2 | 45 | #include "rfc1738.h" |
4d5904f7 | 46 | #include "SquidConfig.h" |
582c2af2 FC |
47 | #include "SquidTime.h" |
48 | #include "StatCounters.h" | |
49 | #include "Store.h" | |
4e540555 | 50 | #include "tools.h" |
582c2af2 | 51 | |
9a0a18de | 52 | #if USE_DELAY_POOLS |
b67e2c8c | 53 | #include "DelayPools.h" |
86a2f789 | 54 | #include "MemObject.h" |
b67e2c8c | 55 | #endif |
090089c4 | 56 | |
63be0a78 | 57 | /** |
58 | \defgroup ServerProtocolGopherInternal Server-Side Gopher Internals | |
59 | \ingroup ServerProtocolGopherAPI | |
60 | * Gopher is somewhat complex and gross because it must convert from | |
61 | * the Gopher protocol to HTTP. | |
62 | */ | |
63 | ||
090089c4 | 64 | /* gopher type code from rfc. Anawat. */ |
63be0a78 | 65 | /// \ingroup ServerProtocolGopherInternal |
090089c4 | 66 | #define GOPHER_FILE '0' |
63be0a78 | 67 | /// \ingroup ServerProtocolGopherInternal |
090089c4 | 68 | #define GOPHER_DIRECTORY '1' |
63be0a78 | 69 | /// \ingroup ServerProtocolGopherInternal |
090089c4 | 70 | #define GOPHER_CSO '2' |
63be0a78 | 71 | /// \ingroup ServerProtocolGopherInternal |
090089c4 | 72 | #define GOPHER_ERROR '3' |
63be0a78 | 73 | /// \ingroup ServerProtocolGopherInternal |
090089c4 | 74 | #define GOPHER_MACBINHEX '4' |
63be0a78 | 75 | /// \ingroup ServerProtocolGopherInternal |
090089c4 | 76 | #define GOPHER_DOSBIN '5' |
63be0a78 | 77 | /// \ingroup ServerProtocolGopherInternal |
090089c4 | 78 | #define GOPHER_UUENCODED '6' |
63be0a78 | 79 | /// \ingroup ServerProtocolGopherInternal |
090089c4 | 80 | #define GOPHER_INDEX '7' |
63be0a78 | 81 | /// \ingroup ServerProtocolGopherInternal |
090089c4 | 82 | #define GOPHER_TELNET '8' |
63be0a78 | 83 | /// \ingroup ServerProtocolGopherInternal |
090089c4 | 84 | #define GOPHER_BIN '9' |
63be0a78 | 85 | /// \ingroup ServerProtocolGopherInternal |
090089c4 | 86 | #define GOPHER_REDUNT '+' |
63be0a78 | 87 | /// \ingroup ServerProtocolGopherInternal |
090089c4 | 88 | #define GOPHER_3270 'T' |
63be0a78 | 89 | /// \ingroup ServerProtocolGopherInternal |
090089c4 | 90 | #define GOPHER_GIF 'g' |
63be0a78 | 91 | /// \ingroup ServerProtocolGopherInternal |
090089c4 | 92 | #define GOPHER_IMAGE 'I' |
93 | ||
63be0a78 | 94 | /// \ingroup ServerProtocolGopherInternal |
090089c4 | 95 | #define GOPHER_HTML 'h' /* HTML */ |
63be0a78 | 96 | /// \ingroup ServerProtocolGopherInternal |
090089c4 | 97 | #define GOPHER_INFO 'i' |
63be0a78 | 98 | /** |
99 | \ingroup ServerProtocolGopherInternal | |
100 | W3 address | |
101 | */ | |
102 | #define GOPHER_WWW 'w' | |
103 | /// \ingroup ServerProtocolGopherInternal | |
090089c4 | 104 | #define GOPHER_SOUND 's' |
105 | ||
63be0a78 | 106 | /// \ingroup ServerProtocolGopherInternal |
090089c4 | 107 | #define GOPHER_PLUS_IMAGE ':' |
63be0a78 | 108 | /// \ingroup ServerProtocolGopherInternal |
090089c4 | 109 | #define GOPHER_PLUS_MOVIE ';' |
63be0a78 | 110 | /// \ingroup ServerProtocolGopherInternal |
090089c4 | 111 | #define GOPHER_PLUS_SOUND '<' |
112 | ||
63be0a78 | 113 | /// \ingroup ServerProtocolGopherInternal |
090089c4 | 114 | #define GOPHER_PORT 70 |
090089c4 | 115 | |
63be0a78 | 116 | /// \ingroup ServerProtocolGopherInternal |
090089c4 | 117 | #define TAB '\t' |
63be0a78 | 118 | /// \ingroup ServerProtocolGopherInternal |
119 | /// \todo CODE: should this be a protocol-specific thing? | |
447e176b | 120 | #define TEMP_BUF_SIZE 4096 |
63be0a78 | 121 | /// \ingroup ServerProtocolGopherInternal |
090089c4 | 122 | #define MAX_CSO_RESULT 1024 |
123 | ||
63be0a78 | 124 | /// \ingroup ServerProtocolGopherInternal |
26ac0430 | 125 | typedef struct gopher_ds { |
090089c4 | 126 | StoreEntry *entry; |
090089c4 | 127 | enum { |
62e76326 | 128 | NORMAL, |
129 | HTML_DIR, | |
130 | HTML_INDEX_RESULT, | |
131 | HTML_CSO_RESULT, | |
132 | HTML_INDEX_PAGE, | |
133 | HTML_CSO_PAGE | |
090089c4 | 134 | } conversion; |
135 | int HTML_header_added; | |
d7443679 | 136 | int HTML_pre; |
090089c4 | 137 | char type_id; |
f2052513 | 138 | char request[MAX_URL]; |
090089c4 | 139 | int cso_recno; |
140 | int len; | |
141 | char *buf; /* pts to a 4k page */ | |
e0d28505 | 142 | Comm::ConnectionPointer serverConn; |
190154cf | 143 | HttpRequest *req; |
b6b6f466 | 144 | FwdState::Pointer fwd; |
c4b7a5a9 | 145 | char replybuf[BUFSIZ]; |
2fadd50d | 146 | } GopherStateData; |
56fa4cad | 147 | |
575d05c4 | 148 | static CLCB gopherStateFree; |
f5b8bbc4 | 149 | static void gopherMimeCreate(GopherStateData *); |
190154cf | 150 | static void gopher_request_parse(const HttpRequest * req, |
62e76326 | 151 | char *type_id, |
152 | char *request); | |
f5b8bbc4 | 153 | static void gopherEndHTML(GopherStateData *); |
154 | static void gopherToHTML(GopherStateData *, char *inbuf, int len); | |
8d77a37c | 155 | static CTCB gopherTimeout; |
c4b7a5a9 | 156 | static IOCB gopherReadReply; |
2b663917 | 157 | static IOCB gopherSendComplete; |
582b6456 | 158 | static PF gopherSendRequest; |
56fa4cad | 159 | |
63be0a78 | 160 | /// \ingroup ServerProtocolGopherInternal |
56fa4cad | 161 | static char def_gopher_bin[] = "www/unknown"; |
63be0a78 | 162 | |
163 | /// \ingroup ServerProtocolGopherInternal | |
56fa4cad | 164 | static char def_gopher_text[] = "text/plain"; |
090089c4 | 165 | |
63be0a78 | 166 | /// \ingroup ServerProtocolGopherInternal |
582b6456 | 167 | static void |
575d05c4 | 168 | gopherStateFree(const CommCloseCbParams ¶ms) |
ba718c8f | 169 | { |
575d05c4 | 170 | GopherStateData *gopherState = (GopherStateData *)params.data; |
62e76326 | 171 | |
51fa90db | 172 | if (gopherState == NULL) |
62e76326 | 173 | return; |
174 | ||
bfcaf585 | 175 | if (gopherState->entry) { |
97b5e68f | 176 | gopherState->entry->unlock(); |
bfcaf585 | 177 | } |
62e76326 | 178 | |
6dd9f4bd | 179 | HTTPMSGUNLOCK(gopherState->req); |
62e76326 | 180 | |
b6b6f466 | 181 | gopherState->fwd = NULL; // refcounted |
182 | ||
db1cd23c | 183 | memFree(gopherState->buf, MEM_4K_BUF); |
7dd44885 | 184 | gopherState->buf = NULL; |
185 | cbdataFree(gopherState); | |
ba718c8f | 186 | } |
187 | ||
63be0a78 | 188 | /** |
189 | \ingroup ServerProtocolGopherInternal | |
190 | * Create MIME Header for Gopher Data | |
191 | */ | |
b8d8561b | 192 | static void |
582b6456 | 193 | gopherMimeCreate(GopherStateData * gopherState) |
090089c4 | 194 | { |
9969d2a8 AJ |
195 | StoreEntry *entry = gopherState->entry; |
196 | const char *mime_type = NULL; | |
197 | const char *mime_enc = NULL; | |
090089c4 | 198 | |
582b6456 | 199 | switch (gopherState->type_id) { |
090089c4 | 200 | |
201 | case GOPHER_DIRECTORY: | |
62e76326 | 202 | |
090089c4 | 203 | case GOPHER_INDEX: |
62e76326 | 204 | |
090089c4 | 205 | case GOPHER_HTML: |
62e76326 | 206 | |
090089c4 | 207 | case GOPHER_WWW: |
62e76326 | 208 | |
090089c4 | 209 | case GOPHER_CSO: |
9969d2a8 | 210 | mime_type = "text/html"; |
62e76326 | 211 | break; |
212 | ||
090089c4 | 213 | case GOPHER_GIF: |
62e76326 | 214 | |
090089c4 | 215 | case GOPHER_IMAGE: |
62e76326 | 216 | |
090089c4 | 217 | case GOPHER_PLUS_IMAGE: |
9969d2a8 | 218 | mime_type = "image/gif"; |
62e76326 | 219 | break; |
220 | ||
090089c4 | 221 | case GOPHER_SOUND: |
62e76326 | 222 | |
090089c4 | 223 | case GOPHER_PLUS_SOUND: |
9969d2a8 | 224 | mime_type = "audio/basic"; |
62e76326 | 225 | break; |
226 | ||
090089c4 | 227 | case GOPHER_PLUS_MOVIE: |
9969d2a8 | 228 | mime_type = "video/mpeg"; |
62e76326 | 229 | break; |
230 | ||
090089c4 | 231 | case GOPHER_MACBINHEX: |
62e76326 | 232 | |
090089c4 | 233 | case GOPHER_DOSBIN: |
62e76326 | 234 | |
090089c4 | 235 | case GOPHER_UUENCODED: |
62e76326 | 236 | |
090089c4 | 237 | case GOPHER_BIN: |
62e76326 | 238 | /* Rightnow We have no idea what it is. */ |
9969d2a8 AJ |
239 | mime_enc = mimeGetContentEncoding(gopherState->request); |
240 | mime_type = mimeGetContentType(gopherState->request); | |
241 | if (!mime_type) | |
242 | mime_type = def_gopher_bin; | |
62e76326 | 243 | break; |
244 | ||
090089c4 | 245 | case GOPHER_FILE: |
62e76326 | 246 | |
090089c4 | 247 | default: |
9969d2a8 AJ |
248 | mime_enc = mimeGetContentEncoding(gopherState->request); |
249 | mime_type = mimeGetContentType(gopherState->request); | |
250 | if (!mime_type) | |
251 | mime_type = def_gopher_text; | |
62e76326 | 252 | break; |
090089c4 | 253 | } |
62e76326 | 254 | |
9969d2a8 AJ |
255 | assert(entry->isEmpty()); |
256 | EBIT_CLR(entry->flags, ENTRY_FWD_HDR_WAIT); | |
257 | ||
258 | HttpReply *reply = new HttpReply; | |
259 | entry->buffer(); | |
955394ce | 260 | reply->setHeaders(Http::scOkay, "Gatewaying", mime_type, -1, -1, -2); |
9969d2a8 AJ |
261 | if (mime_enc) |
262 | reply->header.putStr(HDR_CONTENT_ENCODING, mime_enc); | |
263 | ||
264 | entry->replaceHttpReply(reply); | |
090089c4 | 265 | } |
266 | ||
63be0a78 | 267 | /** |
268 | \ingroup ServerProtocolGopherInternal | |
269 | * Parse a gopher request into components. By Anawat. | |
270 | */ | |
09fb5b61 | 271 | static void |
190154cf | 272 | gopher_request_parse(const HttpRequest * req, char *type_id, char *request) |
090089c4 | 273 | { |
d53b3f6d | 274 | const char *path = req->urlpath.termedBuf(); |
09fb5b61 | 275 | |
276 | if (request) | |
62e76326 | 277 | request[0] = '\0'; |
09fb5b61 | 278 | |
279 | if (path && (*path == '/')) | |
95dc7ff4 | 280 | ++path; |
09fb5b61 | 281 | |
282 | if (!path || !*path) { | |
62e76326 | 283 | *type_id = GOPHER_DIRECTORY; |
284 | return; | |
09fb5b61 | 285 | } |
62e76326 | 286 | |
09fb5b61 | 287 | *type_id = path[0]; |
288 | ||
289 | if (request) { | |
62e76326 | 290 | xstrncpy(request, path + 1, MAX_URL); |
291 | /* convert %xx to char */ | |
2c5d57e0 | 292 | rfc1738_unescape(request); |
090089c4 | 293 | } |
090089c4 | 294 | } |
295 | ||
63be0a78 | 296 | /** |
297 | \ingroup ServerProtocolGopherAPI | |
298 | * Parse the request to determine whether it is cachable. | |
299 | * | |
300 | \param req Request data. | |
301 | \retval 0 Not cachable. | |
302 | \retval 1 Cachable. | |
303 | */ | |
b8d8561b | 304 | int |
190154cf | 305 | gopherCachable(const HttpRequest * req) |
090089c4 | 306 | { |
090089c4 | 307 | int cachable = 1; |
09fb5b61 | 308 | char type_id; |
090089c4 | 309 | /* parse to see type */ |
09fb5b61 | 310 | gopher_request_parse(req, |
62e76326 | 311 | &type_id, |
312 | NULL); | |
313 | ||
09fb5b61 | 314 | switch (type_id) { |
62e76326 | 315 | |
090089c4 | 316 | case GOPHER_INDEX: |
62e76326 | 317 | |
090089c4 | 318 | case GOPHER_CSO: |
62e76326 | 319 | |
090089c4 | 320 | case GOPHER_TELNET: |
62e76326 | 321 | |
090089c4 | 322 | case GOPHER_3270: |
62e76326 | 323 | cachable = 0; |
324 | break; | |
325 | ||
090089c4 | 326 | default: |
62e76326 | 327 | cachable = 1; |
090089c4 | 328 | } |
62e76326 | 329 | |
090089c4 | 330 | return cachable; |
331 | } | |
332 | ||
63be0a78 | 333 | /// \ingroup ServerProtocolGopherInternal |
eb7d6bd6 | 334 | static void |
335 | gopherHTMLHeader(StoreEntry * e, const char *title, const char *substring) | |
336 | { | |
df339671 | 337 | storeAppendPrintf(e, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n"); |
eb7d6bd6 | 338 | storeAppendPrintf(e, "<HTML><HEAD><TITLE>"); |
339 | storeAppendPrintf(e, title, substring); | |
df339671 | 340 | storeAppendPrintf(e, "</TITLE>"); |
d8c0128e | 341 | storeAppendPrintf(e, "<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE>\n"); |
df339671 | 342 | storeAppendPrintf(e, "</HEAD>\n<BODY><H1>"); |
eb7d6bd6 | 343 | storeAppendPrintf(e, title, substring); |
344 | storeAppendPrintf(e, "</H1>\n"); | |
345 | } | |
346 | ||
63be0a78 | 347 | /// \ingroup ServerProtocolGopherInternal |
eb7d6bd6 | 348 | static void |
349 | gopherHTMLFooter(StoreEntry * e) | |
350 | { | |
df339671 | 351 | storeAppendPrintf(e, "<HR noshade size=\"1px\">\n"); |
eb7d6bd6 | 352 | storeAppendPrintf(e, "<ADDRESS>\n"); |
353 | storeAppendPrintf(e, "Generated %s by %s (%s)\n", | |
62e76326 | 354 | mkrfc1123(squid_curtime), |
355 | getMyHostname(), | |
d3caee79 | 356 | visible_appname_string); |
eb7d6bd6 | 357 | storeAppendPrintf(e, "</ADDRESS></BODY></HTML>\n"); |
358 | } | |
359 | ||
63be0a78 | 360 | /// \ingroup ServerProtocolGopherInternal |
b8d8561b | 361 | static void |
582b6456 | 362 | gopherEndHTML(GopherStateData * gopherState) |
090089c4 | 363 | { |
eb7d6bd6 | 364 | StoreEntry *e = gopherState->entry; |
62e76326 | 365 | |
d7443679 | 366 | if (!gopherState->HTML_header_added) { |
62e76326 | 367 | gopherHTMLHeader(e, "Server Return Nothing", NULL); |
368 | storeAppendPrintf(e, "<P>The Gopher query resulted in a blank response</P>"); | |
d7443679 | 369 | } else if (gopherState->HTML_pre) { |
62e76326 | 370 | storeAppendPrintf(e, "</PRE>\n"); |
eb7d6bd6 | 371 | } |
62e76326 | 372 | |
eb7d6bd6 | 373 | gopherHTMLFooter(e); |
090089c4 | 374 | } |
375 | ||
63be0a78 | 376 | /** |
377 | \ingroup ServerProtocolGopherInternal | |
378 | * Convert Gopher to HTML. | |
379 | \par | |
380 | * Borrow part of code from libwww2 came with Mosaic distribution. | |
381 | */ | |
b8d8561b | 382 | static void |
582b6456 | 383 | gopherToHTML(GopherStateData * gopherState, char *inbuf, int len) |
090089c4 | 384 | { |
385 | char *pos = inbuf; | |
386 | char *lpos = NULL; | |
387 | char *tline = NULL; | |
95d659f0 | 388 | LOCAL_ARRAY(char, line, TEMP_BUF_SIZE); |
389 | LOCAL_ARRAY(char, tmpbuf, TEMP_BUF_SIZE); | |
090089c4 | 390 | char *name = NULL; |
391 | char *selector = NULL; | |
392 | char *host = NULL; | |
393 | char *port = NULL; | |
394 | char *escaped_selector = NULL; | |
d20b1cd0 | 395 | const char *icon_url = NULL; |
090089c4 | 396 | char gtype; |
397 | StoreEntry *entry = NULL; | |
398 | ||
53d02438 | 399 | memset(tmpbuf, '\0', TEMP_BUF_SIZE); |
400 | memset(line, '\0', TEMP_BUF_SIZE); | |
090089c4 | 401 | |
582b6456 | 402 | entry = gopherState->entry; |
090089c4 | 403 | |
e6ccf245 | 404 | if (gopherState->conversion == gopher_ds::HTML_INDEX_PAGE) { |
3900307b | 405 | char *html_url = html_quote(entry->url()); |
62e76326 | 406 | gopherHTMLHeader(entry, "Gopher Index %s", html_url); |
407 | storeAppendPrintf(entry, | |
408 | "<p>This is a searchable Gopher index. Use the search\n" | |
409 | "function of your browser to enter search terms.\n" | |
410 | "<ISINDEX>\n"); | |
411 | gopherHTMLFooter(entry); | |
412 | /* now let start sending stuff to client */ | |
3900307b | 413 | entry->flush(); |
d7443679 | 414 | gopherState->HTML_header_added = 1; |
62e76326 | 415 | |
416 | return; | |
090089c4 | 417 | } |
62e76326 | 418 | |
e6ccf245 | 419 | if (gopherState->conversion == gopher_ds::HTML_CSO_PAGE) { |
3900307b | 420 | char *html_url = html_quote(entry->url()); |
62e76326 | 421 | gopherHTMLHeader(entry, "CSO Search of %s", html_url); |
422 | storeAppendPrintf(entry, | |
423 | "<P>A CSO database usually contains a phonebook or\n" | |
424 | "directory. Use the search function of your browser to enter\n" | |
425 | "search terms.</P><ISINDEX>\n"); | |
426 | gopherHTMLFooter(entry); | |
427 | /* now let start sending stuff to client */ | |
3900307b | 428 | entry->flush(); |
d7443679 | 429 | gopherState->HTML_header_added = 1; |
62e76326 | 430 | |
431 | return; | |
090089c4 | 432 | } |
62e76326 | 433 | |
30abd221 | 434 | String outbuf; |
090089c4 | 435 | |
582b6456 | 436 | if (!gopherState->HTML_header_added) { |
62e76326 | 437 | if (gopherState->conversion == gopher_ds::HTML_CSO_RESULT) |
438 | gopherHTMLHeader(entry, "CSO Search Result", NULL); | |
439 | else | |
440 | gopherHTMLHeader(entry, "Gopher Menu", NULL); | |
441 | ||
442 | outbuf.append ("<PRE>"); | |
443 | ||
444 | gopherState->HTML_header_added = 1; | |
d7443679 | 445 | |
446 | gopherState->HTML_pre = 1; | |
090089c4 | 447 | } |
62e76326 | 448 | |
7592589a HN |
449 | while (pos < inbuf + len) { |
450 | int llen; | |
451 | int left = len - (pos - inbuf); | |
452 | lpos = (char *)memchr(pos, '\n', left); | |
453 | if (lpos) { | |
95dc7ff4 | 454 | ++lpos; /* Next line is after \n */ |
7592589a HN |
455 | llen = lpos - pos; |
456 | } else { | |
457 | llen = left; | |
458 | } | |
459 | if (gopherState->len + llen >= TEMP_BUF_SIZE) { | |
e0236918 | 460 | debugs(10, DBG_IMPORTANT, "GopherHTML: Buffer overflow. Lost some data on URL: " << entry->url() ); |
7592589a HN |
461 | llen = TEMP_BUF_SIZE - gopherState->len - 1; |
462 | } | |
463 | if (!lpos) { | |
464 | /* there is no complete line in inbuf */ | |
465 | /* copy it to temp buffer */ | |
466 | /* note: llen is adjusted above */ | |
467 | memcpy(gopherState->buf + gopherState->len, pos, llen); | |
468 | gopherState->len += llen; | |
469 | break; | |
470 | } | |
62e76326 | 471 | if (gopherState->len != 0) { |
472 | /* there is something left from last tx. */ | |
7592589a HN |
473 | memcpy(line, gopherState->buf, gopherState->len); |
474 | memcpy(line + gopherState->len, pos, llen); | |
475 | llen += gopherState->len; | |
62e76326 | 476 | gopherState->len = 0; |
62e76326 | 477 | } else { |
7592589a | 478 | memcpy(line, pos, llen); |
62e76326 | 479 | } |
7592589a HN |
480 | line[llen + 1] = '\0'; |
481 | /* move input to next line */ | |
482 | pos = lpos; | |
62e76326 | 483 | |
484 | /* at this point. We should have one line in buffer to process */ | |
485 | ||
486 | if (*line == '.') { | |
487 | /* skip it */ | |
488 | memset(line, '\0', TEMP_BUF_SIZE); | |
489 | continue; | |
490 | } | |
491 | ||
492 | switch (gopherState->conversion) { | |
493 | ||
494 | case gopher_ds::HTML_INDEX_RESULT: | |
495 | ||
496 | case gopher_ds::HTML_DIR: { | |
26ac0430 | 497 | tline = line; |
aec55359 FC |
498 | gtype = *tline; |
499 | ++tline; | |
26ac0430 AJ |
500 | name = tline; |
501 | selector = strchr(tline, TAB); | |
502 | ||
503 | if (selector) { | |
a38ec4b1 FC |
504 | *selector = '\0'; |
505 | ++selector; | |
26ac0430 | 506 | host = strchr(selector, TAB); |
62e76326 | 507 | |
26ac0430 | 508 | if (host) { |
a38ec4b1 FC |
509 | *host = '\0'; |
510 | ++host; | |
26ac0430 | 511 | port = strchr(host, TAB); |
62e76326 | 512 | |
26ac0430 AJ |
513 | if (port) { |
514 | char *junk; | |
515 | port[0] = ':'; | |
516 | junk = strchr(host, TAB); | |
62e76326 | 517 | |
26ac0430 AJ |
518 | if (junk) |
519 | *junk++ = 0; /* Chop port */ | |
520 | else { | |
521 | junk = strchr(host, '\r'); | |
62e76326 | 522 | |
523 | if (junk) | |
524 | *junk++ = 0; /* Chop port */ | |
525 | else { | |
26ac0430 | 526 | junk = strchr(host, '\n'); |
62e76326 | 527 | |
528 | if (junk) | |
529 | *junk++ = 0; /* Chop port */ | |
62e76326 | 530 | } |
62e76326 | 531 | } |
532 | ||
26ac0430 AJ |
533 | if ((port[1] == '0') && (!port[2])) |
534 | port[0] = 0; /* 0 means none */ | |
535 | } | |
62e76326 | 536 | |
26ac0430 AJ |
537 | /* escape a selector here */ |
538 | escaped_selector = xstrdup(rfc1738_escape_part(selector)); | |
62e76326 | 539 | |
26ac0430 | 540 | switch (gtype) { |
62e76326 | 541 | |
26ac0430 AJ |
542 | case GOPHER_DIRECTORY: |
543 | icon_url = mimeGetIconURL("internal-menu"); | |
544 | break; | |
545 | ||
546 | case GOPHER_HTML: | |
62e76326 | 547 | |
26ac0430 AJ |
548 | case GOPHER_FILE: |
549 | icon_url = mimeGetIconURL("internal-text"); | |
550 | break; | |
62e76326 | 551 | |
26ac0430 | 552 | case GOPHER_INDEX: |
62e76326 | 553 | |
26ac0430 AJ |
554 | case GOPHER_CSO: |
555 | icon_url = mimeGetIconURL("internal-index"); | |
556 | break; | |
62e76326 | 557 | |
26ac0430 | 558 | case GOPHER_IMAGE: |
62e76326 | 559 | |
26ac0430 | 560 | case GOPHER_GIF: |
62e76326 | 561 | |
26ac0430 AJ |
562 | case GOPHER_PLUS_IMAGE: |
563 | icon_url = mimeGetIconURL("internal-image"); | |
564 | break; | |
62e76326 | 565 | |
26ac0430 | 566 | case GOPHER_SOUND: |
62e76326 | 567 | |
26ac0430 AJ |
568 | case GOPHER_PLUS_SOUND: |
569 | icon_url = mimeGetIconURL("internal-sound"); | |
570 | break; | |
62e76326 | 571 | |
26ac0430 AJ |
572 | case GOPHER_PLUS_MOVIE: |
573 | icon_url = mimeGetIconURL("internal-movie"); | |
574 | break; | |
62e76326 | 575 | |
26ac0430 | 576 | case GOPHER_TELNET: |
62e76326 | 577 | |
26ac0430 AJ |
578 | case GOPHER_3270: |
579 | icon_url = mimeGetIconURL("internal-telnet"); | |
580 | break; | |
62e76326 | 581 | |
26ac0430 | 582 | case GOPHER_BIN: |
62e76326 | 583 | |
26ac0430 | 584 | case GOPHER_MACBINHEX: |
62e76326 | 585 | |
26ac0430 | 586 | case GOPHER_DOSBIN: |
62e76326 | 587 | |
26ac0430 AJ |
588 | case GOPHER_UUENCODED: |
589 | icon_url = mimeGetIconURL("internal-binary"); | |
590 | break; | |
62e76326 | 591 | |
26ac0430 AJ |
592 | case GOPHER_INFO: |
593 | icon_url = NULL; | |
594 | break; | |
62e76326 | 595 | |
26ac0430 AJ |
596 | default: |
597 | icon_url = mimeGetIconURL("internal-unknown"); | |
598 | break; | |
599 | } | |
62e76326 | 600 | |
26ac0430 | 601 | memset(tmpbuf, '\0', TEMP_BUF_SIZE); |
62e76326 | 602 | |
26ac0430 AJ |
603 | if ((gtype == GOPHER_TELNET) || (gtype == GOPHER_3270)) { |
604 | if (strlen(escaped_selector) != 0) | |
605 | snprintf(tmpbuf, TEMP_BUF_SIZE, "<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"telnet://%s@%s%s%s/\">%s</A>\n", | |
606 | icon_url, escaped_selector, rfc1738_escape_part(host), | |
607 | *port ? ":" : "", port, html_quote(name)); | |
608 | else | |
609 | snprintf(tmpbuf, TEMP_BUF_SIZE, "<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"telnet://%s%s%s/\">%s</A>\n", | |
610 | icon_url, rfc1738_escape_part(host), *port ? ":" : "", | |
611 | port, html_quote(name)); | |
62e76326 | 612 | |
26ac0430 AJ |
613 | } else if (gtype == GOPHER_INFO) { |
614 | snprintf(tmpbuf, TEMP_BUF_SIZE, "\t%s\n", html_quote(name)); | |
615 | } else { | |
616 | if (strncmp(selector, "GET /", 5) == 0) { | |
617 | /* WWW link */ | |
618 | snprintf(tmpbuf, TEMP_BUF_SIZE, "<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"http://%s/%s\">%s</A>\n", | |
619 | icon_url, host, rfc1738_escape_unescaped(selector + 5), html_quote(name)); | |
62e76326 | 620 | } else { |
26ac0430 AJ |
621 | /* Standard link */ |
622 | snprintf(tmpbuf, TEMP_BUF_SIZE, "<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"gopher://%s/%c%s\">%s</A>\n", | |
623 | icon_url, host, gtype, escaped_selector, html_quote(name)); | |
62e76326 | 624 | } |
62e76326 | 625 | } |
26ac0430 AJ |
626 | |
627 | safe_free(escaped_selector); | |
628 | outbuf.append(tmpbuf); | |
62e76326 | 629 | } else { |
630 | memset(line, '\0', TEMP_BUF_SIZE); | |
631 | continue; | |
632 | } | |
26ac0430 AJ |
633 | } else { |
634 | memset(line, '\0', TEMP_BUF_SIZE); | |
635 | continue; | |
636 | } | |
62e76326 | 637 | |
26ac0430 AJ |
638 | break; |
639 | } /* HTML_DIR, HTML_INDEX_RESULT */ | |
62e76326 | 640 | |
62e76326 | 641 | case gopher_ds::HTML_CSO_RESULT: { |
26ac0430 AJ |
642 | if (line[0] == '-') { |
643 | int code, recno; | |
644 | char *s_code, *s_recno, *result; | |
62e76326 | 645 | |
26ac0430 AJ |
646 | s_code = strtok(line + 1, ":\n"); |
647 | s_recno = strtok(NULL, ":\n"); | |
648 | result = strtok(NULL, "\n"); | |
62e76326 | 649 | |
26ac0430 AJ |
650 | if (!result) |
651 | break; | |
62e76326 | 652 | |
26ac0430 | 653 | code = atoi(s_code); |
62e76326 | 654 | |
26ac0430 | 655 | recno = atoi(s_recno); |
62e76326 | 656 | |
26ac0430 | 657 | if (code != 200) |
62e76326 | 658 | break; |
26ac0430 AJ |
659 | |
660 | if (gopherState->cso_recno != recno) { | |
661 | snprintf(tmpbuf, TEMP_BUF_SIZE, "</PRE><HR noshade size=\"1px\"><H2>Record# %d<br><i>%s</i></H2>\n<PRE>", recno, html_quote(result)); | |
662 | gopherState->cso_recno = recno; | |
62e76326 | 663 | } else { |
26ac0430 AJ |
664 | snprintf(tmpbuf, TEMP_BUF_SIZE, "%s\n", html_quote(result)); |
665 | } | |
666 | ||
667 | outbuf.append(tmpbuf); | |
668 | break; | |
669 | } else { | |
670 | int code; | |
671 | char *s_code, *result; | |
62e76326 | 672 | |
26ac0430 AJ |
673 | s_code = strtok(line, ":"); |
674 | result = strtok(NULL, "\n"); | |
62e76326 | 675 | |
26ac0430 AJ |
676 | if (!result) |
677 | break; | |
62e76326 | 678 | |
26ac0430 | 679 | code = atoi(s_code); |
62e76326 | 680 | |
26ac0430 | 681 | switch (code) { |
62e76326 | 682 | |
26ac0430 AJ |
683 | case 200: { |
684 | /* OK */ | |
685 | /* Do nothing here */ | |
686 | break; | |
687 | } | |
62e76326 | 688 | |
26ac0430 | 689 | case 102: /* Number of matches */ |
62e76326 | 690 | |
26ac0430 | 691 | case 501: /* No Match */ |
62e76326 | 692 | |
26ac0430 AJ |
693 | case 502: { /* Too Many Matches */ |
694 | /* Print the message the server returns */ | |
695 | snprintf(tmpbuf, TEMP_BUF_SIZE, "</PRE><HR noshade size=\"1px\"><H2>%s</H2>\n<PRE>", html_quote(result)); | |
696 | outbuf.append(tmpbuf); | |
697 | break; | |
698 | } | |
62e76326 | 699 | |
62e76326 | 700 | } |
26ac0430 | 701 | } |
62e76326 | 702 | |
26ac0430 | 703 | } /* HTML_CSO_RESULT */ |
62e76326 | 704 | |
705 | default: | |
706 | break; /* do nothing */ | |
707 | ||
708 | } /* switch */ | |
090089c4 | 709 | |
710 | } /* while loop */ | |
711 | ||
528b2c61 | 712 | if (outbuf.size() > 0) { |
d53b3f6d | 713 | entry->append(outbuf.rawBuf(), outbuf.size()); |
62e76326 | 714 | /* now let start sending stuff to client */ |
3900307b | 715 | entry->flush(); |
090089c4 | 716 | } |
62e76326 | 717 | |
30abd221 | 718 | outbuf.clean(); |
090089c4 | 719 | return; |
720 | } | |
721 | ||
63be0a78 | 722 | /// \ingroup ServerProtocolGopherInternal |
582b6456 | 723 | static void |
8d77a37c | 724 | gopherTimeout(const CommTimeoutCbParams &io) |
090089c4 | 725 | { |
8d77a37c AJ |
726 | GopherStateData *gopherState = static_cast<GopherStateData *>(io.data); |
727 | debugs(10, 4, HERE << io.conn << ": '" << gopherState->entry->url() << "'" ); | |
62e76326 | 728 | |
955394ce | 729 | gopherState->fwd->fail(new ErrorState(ERR_READ_TIMEOUT, Http::scGateway_Timeout, gopherState->fwd->request)); |
62e76326 | 730 | |
8d77a37c AJ |
731 | if (Comm::IsConnOpen(io.conn)) |
732 | io.conn->close(); | |
090089c4 | 733 | } |
734 | ||
63be0a78 | 735 | /** |
736 | \ingroup ServerProtocolGopherInternal | |
737 | * This will be called when data is ready to be read from fd. | |
738 | * Read until error or connection closed. | |
739 | */ | |
b8d8561b | 740 | static void |
e0d28505 | 741 | gopherReadReply(const Comm::ConnectionPointer &conn, char *buf, size_t len, comm_err_t flag, int xerrno, void *data) |
090089c4 | 742 | { |
e6ccf245 | 743 | GopherStateData *gopherState = (GopherStateData *)data; |
bfcaf585 | 744 | StoreEntry *entry = gopherState->entry; |
090089c4 | 745 | int clen; |
56fa4cad | 746 | int bin; |
c4b7a5a9 | 747 | size_t read_sz = BUFSIZ; |
9a0a18de | 748 | #if USE_DELAY_POOLS |
b67e2c8c | 749 | DelayId delayId = entry->mem_obj->mostBytesAllowed(); |
447e176b | 750 | #endif |
c4b7a5a9 | 751 | |
752 | /* Bail out early on COMM_ERR_CLOSING - close handlers will tidy up for us */ | |
62e76326 | 753 | |
c4b7a5a9 | 754 | if (flag == COMM_ERR_CLOSING) { |
755 | return; | |
756 | } | |
757 | ||
758 | assert(buf == gopherState->replybuf); | |
62e76326 | 759 | |
e92e4e44 | 760 | if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) { |
e0d28505 | 761 | gopherState->serverConn->close(); |
62e76326 | 762 | return; |
e92e4e44 | 763 | } |
c4b7a5a9 | 764 | |
9a0a18de | 765 | #if USE_DELAY_POOLS |
b67e2c8c | 766 | read_sz = delayId.bytesWanted(1, read_sz); |
447e176b | 767 | #endif |
c4b7a5a9 | 768 | |
56fa4cad | 769 | /* leave one space for \0 in gopherToHTML */ |
62e76326 | 770 | |
c4b7a5a9 | 771 | if (flag == COMM_OK && len > 0) { |
9a0a18de | 772 | #if USE_DELAY_POOLS |
62e76326 | 773 | delayId.bytesIn(len); |
447e176b | 774 | #endif |
62e76326 | 775 | |
e4f1fdae FC |
776 | kb_incr(&(statCounter.server.all.kbytes_in), len); |
777 | kb_incr(&(statCounter.server.other.kbytes_in), len); | |
ee1679df | 778 | } |
62e76326 | 779 | |
1b76e6c1 | 780 | debugs(10, 5, HERE << conn << " read len=" << len); |
62e76326 | 781 | |
c4b7a5a9 | 782 | if (flag == COMM_OK && len > 0) { |
8d77a37c AJ |
783 | AsyncCall::Pointer nil; |
784 | commSetConnTimeout(conn, Config.Timeout.read, nil); | |
95dc7ff4 | 785 | ++IOStats.Gopher.reads; |
62e76326 | 786 | |
95dc7ff4 | 787 | for (clen = len - 1, bin = 0; clen; ++bin) |
62e76326 | 788 | clen >>= 1; |
789 | ||
95dc7ff4 | 790 | ++IOStats.Gopher.read_hist[bin]; |
bae917ac CT |
791 | |
792 | HttpRequest *req = gopherState->fwd->request; | |
dcaab393 | 793 | if (req->hier.bodyBytesRead < 0) |
bae917ac | 794 | req->hier.bodyBytesRead = 0; |
dcaab393 | 795 | |
bae917ac | 796 | req->hier.bodyBytesRead += len; |
56fa4cad | 797 | } |
62e76326 | 798 | |
006f376d | 799 | if (flag != COMM_OK) { |
e0236918 | 800 | debugs(50, DBG_IMPORTANT, "gopherReadReply: error reading: " << xstrerror()); |
62e76326 | 801 | |
129fe2a1 | 802 | if (ignoreErrno(xerrno)) { |
1b76e6c1 AJ |
803 | AsyncCall::Pointer call = commCbCall(5,4, "gopherReadReply", |
804 | CommIoCbPtrFun(gopherReadReply, gopherState)); | |
805 | comm_read(conn, buf, read_sz, call); | |
6cae5db1 | 806 | } else { |
955394ce | 807 | ErrorState *err = new ErrorState(ERR_READ_ERROR, Http::scInternalServerError, gopherState->fwd->request); |
129fe2a1 | 808 | err->xerrno = xerrno; |
b6b6f466 | 809 | gopherState->fwd->fail(err); |
e0d28505 | 810 | gopherState->serverConn->close(); |
62e76326 | 811 | } |
528b2c61 | 812 | } else if (len == 0 && entry->isEmpty()) { |
955394ce | 813 | gopherState->fwd->fail(new ErrorState(ERR_ZERO_SIZE_OBJECT, Http::scServiceUnavailable, gopherState->fwd->request)); |
e0d28505 | 814 | gopherState->serverConn->close(); |
090089c4 | 815 | } else if (len == 0) { |
62e76326 | 816 | /* Connection closed; retrieval done. */ |
bb790702 | 817 | /* flush the rest of data in temp buf if there is one. */ |
62e76326 | 818 | |
819 | if (gopherState->conversion != gopher_ds::NORMAL) | |
820 | gopherEndHTML(gopherState); | |
821 | ||
3900307b | 822 | entry->timestampsSet(); |
3900307b | 823 | entry->flush(); |
b6b6f466 | 824 | gopherState->fwd->complete(); |
e0d28505 | 825 | gopherState->serverConn->close(); |
090089c4 | 826 | } else { |
62e76326 | 827 | if (gopherState->conversion != gopher_ds::NORMAL) { |
828 | gopherToHTML(gopherState, buf, len); | |
829 | } else { | |
3900307b | 830 | entry->append(buf, len); |
62e76326 | 831 | } |
abd8f140 AJ |
832 | AsyncCall::Pointer call = commCbCall(5,4, "gopherReadReply", |
833 | CommIoCbPtrFun(gopherReadReply, gopherState)); | |
834 | comm_read(conn, buf, read_sz, call); | |
835 | } | |
090089c4 | 836 | } |
837 | ||
63be0a78 | 838 | /** |
839 | \ingroup ServerProtocolGopherInternal | |
840 | * This will be called when request write is complete. Schedule read of reply. | |
841 | */ | |
b8d8561b | 842 | static void |
e0d28505 | 843 | gopherSendComplete(const Comm::ConnectionPointer &conn, char *buf, size_t size, comm_err_t errflag, int xerrno, void *data) |
090089c4 | 844 | { |
56fa4cad | 845 | GopherStateData *gopherState = (GopherStateData *) data; |
70a9dab4 | 846 | StoreEntry *entry = gopherState->entry; |
e0d28505 | 847 | debugs(10, 5, HERE << conn << " size: " << size << " errflag: " << errflag); |
62e76326 | 848 | |
ee1679df | 849 | if (size > 0) { |
e0d28505 | 850 | fd_bytes(conn->fd, size, FD_WRITE); |
e4f1fdae FC |
851 | kb_incr(&(statCounter.server.all.kbytes_out), size); |
852 | kb_incr(&(statCounter.server.other.kbytes_out), size); | |
ee1679df | 853 | } |
62e76326 | 854 | |
090089c4 | 855 | if (errflag) { |
62e76326 | 856 | ErrorState *err; |
955394ce | 857 | err = new ErrorState(ERR_WRITE_ERROR, Http::scServiceUnavailable, gopherState->fwd->request); |
129fe2a1 | 858 | err->xerrno = xerrno; |
d2b3e30e | 859 | err->port = gopherState->fwd->request->port; |
3900307b | 860 | err->url = xstrdup(entry->url()); |
b6b6f466 | 861 | gopherState->fwd->fail(err); |
e0d28505 | 862 | gopherState->serverConn->close(); |
62e76326 | 863 | |
864 | if (buf) | |
865 | memFree(buf, MEM_4K_BUF); /* Allocated by gopherSendRequest. */ | |
866 | ||
867 | return; | |
090089c4 | 868 | } |
62e76326 | 869 | |
870 | /* | |
090089c4 | 871 | * OK. We successfully reach remote site. Start MIME typing |
872 | * stuff. Do it anyway even though request is not HTML type. | |
873 | */ | |
3900307b | 874 | entry->buffer(); |
b66315e4 | 875 | |
30a4f2a8 | 876 | gopherMimeCreate(gopherState); |
62e76326 | 877 | |
a47b9029 | 878 | switch (gopherState->type_id) { |
62e76326 | 879 | |
a47b9029 | 880 | case GOPHER_DIRECTORY: |
62e76326 | 881 | /* we got to convert it first */ |
62e76326 | 882 | gopherState->conversion = gopher_ds::HTML_DIR; |
883 | gopherState->HTML_header_added = 0; | |
884 | break; | |
885 | ||
a47b9029 | 886 | case GOPHER_INDEX: |
62e76326 | 887 | /* we got to convert it first */ |
62e76326 | 888 | gopherState->conversion = gopher_ds::HTML_INDEX_RESULT; |
889 | gopherState->HTML_header_added = 0; | |
890 | break; | |
891 | ||
a47b9029 | 892 | case GOPHER_CSO: |
62e76326 | 893 | /* we got to convert it first */ |
62e76326 | 894 | gopherState->conversion = gopher_ds::HTML_CSO_RESULT; |
895 | gopherState->cso_recno = 0; | |
896 | gopherState->HTML_header_added = 0; | |
897 | break; | |
898 | ||
a47b9029 | 899 | default: |
62e76326 | 900 | gopherState->conversion = gopher_ds::NORMAL; |
3900307b | 901 | entry->flush(); |
86101e40 | 902 | } |
62e76326 | 903 | |
090089c4 | 904 | /* Schedule read reply. */ |
8d77a37c | 905 | AsyncCall::Pointer call = commCbCall(5,5, "gopherReadReply", |
26ac0430 | 906 | CommIoCbPtrFun(gopherReadReply, gopherState)); |
3e4bebf8 | 907 | entry->delayAwareRead(conn, gopherState->replybuf, BUFSIZ, call); |
62e76326 | 908 | |
090089c4 | 909 | if (buf) |
62e76326 | 910 | memFree(buf, MEM_4K_BUF); /* Allocated by gopherSendRequest. */ |
090089c4 | 911 | } |
912 | ||
63be0a78 | 913 | /** |
914 | \ingroup ServerProtocolGopherInternal | |
915 | * This will be called when connect completes. Write request. | |
916 | */ | |
b8d8561b | 917 | static void |
582b6456 | 918 | gopherSendRequest(int fd, void *data) |
090089c4 | 919 | { |
e6ccf245 | 920 | GopherStateData *gopherState = (GopherStateData *)data; |
921 | char *buf = (char *)memAllocate(MEM_4K_BUF); | |
62e76326 | 922 | |
582b6456 | 923 | if (gopherState->type_id == GOPHER_CSO) { |
62e76326 | 924 | const char *t = strchr(gopherState->request, '?'); |
925 | ||
926 | if (t != NULL) | |
95dc7ff4 | 927 | ++t; /* skip the ? */ |
62e76326 | 928 | else |
929 | t = ""; | |
930 | ||
931 | snprintf(buf, 4096, "query %s\r\nquit\r\n", t); | |
582b6456 | 932 | } else if (gopherState->type_id == GOPHER_INDEX) { |
62e76326 | 933 | char *t = strchr(gopherState->request, '?'); |
934 | ||
935 | if (t != NULL) | |
936 | *t = '\t'; | |
937 | ||
938 | snprintf(buf, 4096, "%s\r\n", gopherState->request); | |
090089c4 | 939 | } else { |
62e76326 | 940 | snprintf(buf, 4096, "%s\r\n", gopherState->request); |
090089c4 | 941 | } |
62e76326 | 942 | |
e0d28505 | 943 | debugs(10, 5, HERE << gopherState->serverConn); |
ec41b64c AJ |
944 | AsyncCall::Pointer call = commCbCall(5,5, "gopherSendComplete", |
945 | CommIoCbPtrFun(gopherSendComplete, gopherState)); | |
b0388924 | 946 | Comm::Write(gopherState->serverConn, buf, strlen(buf), call, NULL); |
62e76326 | 947 | |
d46a87a8 | 948 | if (EBIT_TEST(gopherState->entry->flags, ENTRY_CACHABLE)) |
d88e3c49 | 949 | gopherState->entry->setPublicKey(); /* Make it public */ |
090089c4 | 950 | } |
951 | ||
63be0a78 | 952 | /// \ingroup ServerProtocolGopherInternal |
09fb5b61 | 953 | CBDATA_TYPE(GopherStateData); |
954 | ||
63be0a78 | 955 | /// \ingroup ServerProtocolGopherAPI |
770f051d | 956 | void |
b6b6f466 | 957 | gopherStart(FwdState * fwd) |
090089c4 | 958 | { |
b6b6f466 | 959 | StoreEntry *entry = fwd->entry; |
09fb5b61 | 960 | GopherStateData *gopherState; |
961 | CBDATA_INIT_TYPE(GopherStateData); | |
962 | gopherState = cbdataAlloc(GopherStateData); | |
e6ccf245 | 963 | gopherState->buf = (char *)memAllocate(MEM_4K_BUF); |
34266cde | 964 | |
3d0ac046 | 965 | entry->lock(); |
582b6456 | 966 | gopherState->entry = entry; |
34266cde | 967 | |
b6b6f466 | 968 | gopherState->fwd = fwd; |
34266cde | 969 | |
bf8fe701 | 970 | debugs(10, 3, "gopherStart: " << entry->url() ); |
34266cde | 971 | |
95dc7ff4 | 972 | ++ statCounter.server.all.requests; |
34266cde | 973 | |
95dc7ff4 | 974 | ++ statCounter.server.other.requests; |
34266cde | 975 | |
090089c4 | 976 | /* Parse url. */ |
b6b6f466 | 977 | gopher_request_parse(fwd->request, |
62e76326 | 978 | &gopherState->type_id, gopherState->request); |
34266cde | 979 | |
5229395c | 980 | comm_add_close_handler(fwd->serverConnection()->fd, gopherStateFree, gopherState); |
62e76326 | 981 | |
582b6456 | 982 | if (((gopherState->type_id == GOPHER_INDEX) || (gopherState->type_id == GOPHER_CSO)) |
62e76326 | 983 | && (strchr(gopherState->request, '?') == NULL)) { |
984 | /* Index URL without query word */ | |
985 | /* We have to generate search page back to client. No need for connection */ | |
986 | gopherMimeCreate(gopherState); | |
987 | ||
988 | if (gopherState->type_id == GOPHER_INDEX) { | |
989 | gopherState->conversion = gopher_ds::HTML_INDEX_PAGE; | |
990 | } else { | |
991 | if (gopherState->type_id == GOPHER_CSO) { | |
992 | gopherState->conversion = gopher_ds::HTML_CSO_PAGE; | |
993 | } else { | |
994 | gopherState->conversion = gopher_ds::HTML_INDEX_PAGE; | |
995 | } | |
996 | } | |
997 | ||
998 | gopherToHTML(gopherState, (char *) NULL, 0); | |
b6b6f466 | 999 | fwd->complete(); |
62e76326 | 1000 | return; |
090089c4 | 1001 | } |
62e76326 | 1002 | |
e0d28505 | 1003 | gopherState->serverConn = fwd->serverConnection(); |
5229395c | 1004 | gopherSendRequest(fwd->serverConnection()->fd, gopherState); |
8d77a37c | 1005 | AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "gopherTimeout", |
dc49061a | 1006 | CommTimeoutCbPtrFun(gopherTimeout, gopherState)); |
8d77a37c | 1007 | commSetConnTimeout(fwd->serverConnection(), Config.Timeout.read, timeoutCall); |
e5f6c5c2 | 1008 | } |