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