]>
Commit | Line | Data |
---|---|---|
30a4f2a8 | 1 | /* |
56fa4cad | 2 | * $Id: gopher.cc,v 1.42 1996/09/12 16:39:53 wessels Exp $ |
30a4f2a8 | 3 | * |
4 | * DEBUG: section 10 Gopher | |
5 | * AUTHOR: Harvest Derived | |
6 | * | |
7 | * SQUID Internet Object Cache http://www.nlanr.net/Squid/ | |
8 | * -------------------------------------------------------- | |
9 | * | |
10 | * Squid is the result of efforts by numerous individuals from the | |
11 | * Internet community. Development is led by Duane Wessels of the | |
12 | * National Laboratory for Applied Network Research and funded by | |
13 | * the National Science Foundation. | |
14 | * | |
15 | * This program is free software; you can redistribute it and/or modify | |
16 | * it under the terms of the GNU General Public License as published by | |
17 | * the Free Software Foundation; either version 2 of the License, or | |
18 | * (at your option) any later version. | |
19 | * | |
20 | * This program is distributed in the hope that it will be useful, | |
21 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
22 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
23 | * GNU General Public License for more details. | |
24 | * | |
25 | * You should have received a copy of the GNU General Public License | |
26 | * along with this program; if not, write to the Free Software | |
27 | * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | |
28 | * | |
29 | */ | |
019dd986 | 30 | |
31 | /* | |
30a4f2a8 | 32 | * Copyright (c) 1994, 1995. All rights reserved. |
33 | * | |
34 | * The Harvest software was developed by the Internet Research Task | |
35 | * Force Research Group on Resource Discovery (IRTF-RD): | |
36 | * | |
37 | * Mic Bowman of Transarc Corporation. | |
38 | * Peter Danzig of the University of Southern California. | |
39 | * Darren R. Hardy of the University of Colorado at Boulder. | |
40 | * Udi Manber of the University of Arizona. | |
41 | * Michael F. Schwartz of the University of Colorado at Boulder. | |
42 | * Duane Wessels of the University of Colorado at Boulder. | |
43 | * | |
44 | * This copyright notice applies to software in the Harvest | |
45 | * ``src/'' directory only. Users should consult the individual | |
46 | * copyright notices in the ``components/'' subdirectories for | |
47 | * copyright information about other software bundled with the | |
48 | * Harvest source code distribution. | |
49 | * | |
50 | * TERMS OF USE | |
51 | * | |
52 | * The Harvest software may be used and re-distributed without | |
53 | * charge, provided that the software origin and research team are | |
54 | * cited in any use of the system. Most commonly this is | |
55 | * accomplished by including a link to the Harvest Home Page | |
56 | * (http://harvest.cs.colorado.edu/) from the query page of any | |
57 | * Broker you deploy, as well as in the query result pages. These | |
58 | * links are generated automatically by the standard Broker | |
59 | * software distribution. | |
60 | * | |
61 | * The Harvest software is provided ``as is'', without express or | |
62 | * implied warranty, and with no support nor obligation to assist | |
63 | * in its use, correction, modification or enhancement. We assume | |
64 | * no liability with respect to the infringement of copyrights, | |
65 | * trade secrets, or any patents, and are not responsible for | |
66 | * consequential damages. Proper use of the Harvest software is | |
67 | * entirely the responsibility of the user. | |
68 | * | |
69 | * DERIVATIVE WORKS | |
70 | * | |
71 | * Users may make derivative works from the Harvest software, subject | |
72 | * to the following constraints: | |
73 | * | |
74 | * - You must include the above copyright notice and these | |
75 | * accompanying paragraphs in all forms of derivative works, | |
76 | * and any documentation and other materials related to such | |
77 | * distribution and use acknowledge that the software was | |
78 | * developed at the above institutions. | |
79 | * | |
80 | * - You must notify IRTF-RD regarding your distribution of | |
81 | * the derivative work. | |
82 | * | |
83 | * - You must clearly notify users that your are distributing | |
84 | * a modified version and not the original Harvest software. | |
85 | * | |
86 | * - Any derivative product is also subject to these copyright | |
87 | * and use restrictions. | |
88 | * | |
89 | * Note that the Harvest software is NOT in the public domain. We | |
90 | * retain copyright, as specified above. | |
91 | * | |
92 | * HISTORY OF FREE SOFTWARE STATUS | |
93 | * | |
94 | * Originally we required sites to license the software in cases | |
95 | * where they were going to build commercial products/services | |
96 | * around Harvest. In June 1995 we changed this policy. We now | |
97 | * allow people to use the core Harvest software (the code found in | |
98 | * the Harvest ``src/'' directory) for free. We made this change | |
99 | * in the interest of encouraging the widest possible deployment of | |
100 | * the technology. The Harvest software is really a reference | |
101 | * implementation of a set of protocols and formats, some of which | |
102 | * we intend to standardize. We encourage commercial | |
103 | * re-implementations of code complying to this set of standards. | |
019dd986 | 104 | */ |
44a47c6e | 105 | |
106 | #include "squid.h" | |
090089c4 | 107 | |
090089c4 | 108 | /* gopher type code from rfc. Anawat. */ |
109 | #define GOPHER_FILE '0' | |
110 | #define GOPHER_DIRECTORY '1' | |
111 | #define GOPHER_CSO '2' | |
112 | #define GOPHER_ERROR '3' | |
113 | #define GOPHER_MACBINHEX '4' | |
114 | #define GOPHER_DOSBIN '5' | |
115 | #define GOPHER_UUENCODED '6' | |
116 | #define GOPHER_INDEX '7' | |
117 | #define GOPHER_TELNET '8' | |
118 | #define GOPHER_BIN '9' | |
119 | #define GOPHER_REDUNT '+' | |
120 | #define GOPHER_3270 'T' | |
121 | #define GOPHER_GIF 'g' | |
122 | #define GOPHER_IMAGE 'I' | |
123 | ||
124 | #define GOPHER_HTML 'h' /* HTML */ | |
125 | #define GOPHER_INFO 'i' | |
126 | #define GOPHER_WWW 'w' /* W3 address */ | |
127 | #define GOPHER_SOUND 's' | |
128 | ||
129 | #define GOPHER_PLUS_IMAGE ':' | |
130 | #define GOPHER_PLUS_MOVIE ';' | |
131 | #define GOPHER_PLUS_SOUND '<' | |
132 | ||
133 | #define GOPHER_PORT 70 | |
134 | #define GOPHER_DELETE_GAP (64*1024) | |
135 | ||
136 | #define TAB '\t' | |
137 | #define TEMP_BUF_SIZE SM_PAGE_SIZE | |
138 | #define MAX_CSO_RESULT 1024 | |
139 | ||
140 | typedef struct gopher_ds { | |
141 | StoreEntry *entry; | |
ed43818f | 142 | char host[SQUIDHOSTNAMELEN + 1]; |
090089c4 | 143 | enum { |
144 | NORMAL, | |
145 | HTML_DIR, | |
146 | HTML_INDEX_RESULT, | |
147 | HTML_CSO_RESULT, | |
148 | HTML_INDEX_PAGE, | |
149 | HTML_CSO_PAGE | |
150 | } conversion; | |
151 | int HTML_header_added; | |
152 | int port; | |
090089c4 | 153 | char type_id; |
fd45fa07 | 154 | char request[MAX_URL + 1]; |
090089c4 | 155 | int data_in; |
156 | int cso_recno; | |
157 | int len; | |
158 | char *buf; /* pts to a 4k page */ | |
56fa4cad | 159 | } GopherStateData; |
160 | ||
161 | static int gopherStateFree _PARAMS((int fd, GopherStateData *)); | |
162 | static void gopher_mime_content _PARAMS((char *buf, char *name, char *def)); | |
163 | static void gopherMimeCreate _PARAMS((GopherStateData *)); | |
164 | static int gopher_url_parser _PARAMS((char *url, | |
165 | char *host, | |
166 | int *port, | |
167 | char *type_id, | |
168 | char *request)); | |
169 | static void gopherEndHTML _PARAMS((GopherStateData *)); | |
170 | static void gopherToHTML _PARAMS((GopherStateData *, char *inbuf, int len)); | |
171 | static int gopherReadReplyTimeout _PARAMS((int fd, GopherStateData *)); | |
172 | static void gopherLifetimeExpire _PARAMS((int fd, GopherStateData *)); | |
173 | static void gopherReadReply _PARAMS((int fd, GopherStateData *)); | |
174 | static void gopherSendComplete _PARAMS((int fd, | |
175 | char *buf, | |
176 | int size, | |
177 | int errflag, | |
178 | void *data)); | |
179 | static void gopherSendRequest _PARAMS((int fd, GopherStateData *)); | |
180 | static GopherStateData *CreateGopherStateData _PARAMS((void)); | |
181 | ||
182 | static char def_gopher_bin[] = "www/unknown"; | |
183 | static char def_gopher_text[] = "text/plain"; | |
090089c4 | 184 | |
51fa90db | 185 | static int gopherStateFree(fd, gopherState) |
ba718c8f | 186 | int fd; |
56fa4cad | 187 | GopherStateData *gopherState; |
ba718c8f | 188 | { |
51fa90db | 189 | if (gopherState == NULL) |
190 | return 1; | |
30a4f2a8 | 191 | if (gopherState->entry) |
192 | storeUnlockObject(gopherState->entry); | |
51fa90db | 193 | put_free_4k_page(gopherState->buf); |
194 | xfree(gopherState); | |
195 | return 0; | |
ba718c8f | 196 | } |
197 | ||
198 | ||
090089c4 | 199 | /* figure out content type from file extension */ |
200 | static void gopher_mime_content(buf, name, def) | |
201 | char *buf; | |
202 | char *name; | |
203 | char *def; | |
204 | { | |
95d659f0 | 205 | LOCAL_ARRAY(char, temp, MAX_URL + 1); |
090089c4 | 206 | char *ext1 = NULL; |
207 | char *ext2 = NULL; | |
208 | char *str = NULL; | |
209 | ext_table_entry *e = NULL; | |
210 | ||
211 | ext2 = NULL; | |
212 | strcpy(temp, name); | |
213 | for (ext1 = temp; *ext1; ext1++) | |
214 | if (isupper(*ext1)) | |
215 | *ext1 = tolower(*ext1); | |
216 | if ((ext1 = strrchr(temp, '.')) == NULL) { | |
217 | /* use default */ | |
218 | sprintf(buf + strlen(buf), "Content-Type: %s\r\n", def); | |
219 | return; | |
220 | } | |
221 | /* try extension table */ | |
222 | *ext1++ = 0; | |
223 | if (strcmp("gz", ext1) == 0 || strcmp("z", ext1) == 0) { | |
224 | ext2 = ext1; | |
225 | if ((ext1 = strrchr(temp, '.')) == NULL) { | |
226 | ext1 = ext2; | |
227 | ext2 = NULL; | |
228 | } else | |
229 | ext1++; | |
230 | } | |
231 | if ((e = mime_ext_to_type(ext1)) == NULL) { | |
232 | /* mime_ext_to_type() can return a NULL */ | |
233 | if (ext2 && (e = mime_ext_to_type(ext2))) { | |
234 | str = e->mime_type; | |
235 | ext2 = NULL; | |
236 | } else { | |
237 | str = def; | |
238 | } | |
239 | } else { | |
240 | str = e->mime_type; | |
241 | } | |
242 | sprintf(buf + strlen(buf), "Content-Type: %s\r\n", str); | |
243 | if (ext2 && (e = mime_ext_to_type(ext2))) { | |
244 | sprintf(buf + strlen(buf), "Content-Encoding: %s\r\n", | |
245 | e->mime_encoding); | |
246 | } | |
247 | } | |
248 | ||
249 | ||
250 | ||
251 | /* create MIME Header for Gopher Data */ | |
56fa4cad | 252 | static void gopherMimeCreate(data) |
253 | GopherStateData *data; | |
090089c4 | 254 | { |
95d659f0 | 255 | LOCAL_ARRAY(char, tempMIME, MAX_MIME); |
090089c4 | 256 | |
257 | sprintf(tempMIME, "\ | |
258 | HTTP/1.0 200 OK Gatewaying\r\n\ | |
c5c666ab | 259 | Server: Squid/%s\r\n\ |
8213067d | 260 | MIME-version: 1.0\r\n", version_string); |
090089c4 | 261 | |
262 | switch (data->type_id) { | |
263 | ||
264 | case GOPHER_DIRECTORY: | |
265 | case GOPHER_INDEX: | |
266 | case GOPHER_HTML: | |
267 | case GOPHER_WWW: | |
268 | case GOPHER_CSO: | |
269 | strcat(tempMIME, "Content-Type: text/html\r\n"); | |
270 | break; | |
271 | case GOPHER_GIF: | |
272 | case GOPHER_IMAGE: | |
273 | case GOPHER_PLUS_IMAGE: | |
274 | strcat(tempMIME, "Content-Type: image/gif\r\n"); | |
275 | break; | |
276 | case GOPHER_SOUND: | |
277 | case GOPHER_PLUS_SOUND: | |
278 | strcat(tempMIME, "Content-Type: audio/basic\r\n"); | |
279 | break; | |
280 | case GOPHER_PLUS_MOVIE: | |
281 | strcat(tempMIME, "Content-Type: video/mpeg\r\n"); | |
282 | break; | |
283 | case GOPHER_MACBINHEX: | |
284 | case GOPHER_DOSBIN: | |
285 | case GOPHER_UUENCODED: | |
286 | case GOPHER_BIN: | |
287 | /* Rightnow We have no idea what it is. */ | |
288 | gopher_mime_content(tempMIME, data->request, def_gopher_bin); | |
289 | break; | |
290 | ||
291 | case GOPHER_FILE: | |
292 | default: | |
293 | gopher_mime_content(tempMIME, data->request, def_gopher_text); | |
294 | break; | |
295 | ||
296 | } | |
297 | ||
298 | strcat(tempMIME, "\r\n"); | |
299 | storeAppend(data->entry, tempMIME, strlen(tempMIME)); | |
300 | } | |
301 | ||
302 | /* Parse a gopher url into components. By Anawat. */ | |
56fa4cad | 303 | static int gopher_url_parser(url, host, port, type_id, request) |
090089c4 | 304 | char *url; |
305 | char *host; | |
306 | int *port; | |
307 | char *type_id; | |
308 | char *request; | |
309 | { | |
95d659f0 | 310 | LOCAL_ARRAY(char, proto, MAX_URL); |
311 | LOCAL_ARRAY(char, hostbuf, MAX_URL); | |
090089c4 | 312 | int t; |
313 | ||
de06a228 | 314 | proto[0] = hostbuf[0] = '\0'; |
090089c4 | 315 | host[0] = request[0] = '\0'; |
316 | (*port) = 0; | |
317 | (*type_id) = 0; | |
318 | ||
de06a228 | 319 | t = sscanf(url, "%[a-zA-Z]://%[^/]/%c%s", proto, hostbuf, |
090089c4 | 320 | type_id, request); |
de06a228 | 321 | if ((t < 2) || strcasecmp(proto, "gopher")) { |
090089c4 | 322 | return -1; |
323 | } else if (t == 2) { | |
324 | (*type_id) = GOPHER_DIRECTORY; | |
325 | request[0] = '\0'; | |
326 | } else if (t == 3) { | |
327 | request[0] = '\0'; | |
328 | } else { | |
329 | /* convert %xx to char */ | |
de06a228 | 330 | (void) url_convert_hex(request, 0); |
090089c4 | 331 | } |
332 | ||
333 | host[0] = '\0'; | |
334 | if (sscanf(hostbuf, "%[^:]:%d", host, port) < 2) | |
335 | (*port) = GOPHER_PORT; | |
336 | ||
337 | return 0; | |
338 | } | |
339 | ||
6eb42cae | 340 | int gopherCachable(url) |
090089c4 | 341 | char *url; |
090089c4 | 342 | { |
56fa4cad | 343 | GopherStateData *data = NULL; |
090089c4 | 344 | int cachable = 1; |
090089c4 | 345 | /* use as temp data structure to parse gopher URL */ |
56fa4cad | 346 | data = CreateGopherStateData(); |
090089c4 | 347 | /* parse to see type */ |
56fa4cad | 348 | gopher_url_parser(url, |
349 | data->host, | |
350 | &data->port, | |
351 | &data->type_id, | |
352 | data->request); | |
090089c4 | 353 | switch (data->type_id) { |
354 | case GOPHER_INDEX: | |
355 | case GOPHER_CSO: | |
356 | case GOPHER_TELNET: | |
357 | case GOPHER_3270: | |
358 | cachable = 0; | |
359 | break; | |
360 | default: | |
361 | cachable = 1; | |
362 | } | |
51fa90db | 363 | gopherStateFree(-1, data); |
090089c4 | 364 | return cachable; |
365 | } | |
366 | ||
56fa4cad | 367 | static void gopherEndHTML(data) |
368 | GopherStateData *data; | |
090089c4 | 369 | { |
95d659f0 | 370 | LOCAL_ARRAY(char, tmpbuf, TEMP_BUF_SIZE); |
090089c4 | 371 | |
372 | if (!data->data_in) { | |
a8f7d3ee | 373 | sprintf(tmpbuf, "<HTML><HEAD><TITLE>Server Return Nothing.</TITLE>\n\ |
374 | </HEAD><BODY><HR><H1>Server Return Nothing.</H1></BODY></HTML>\n"); | |
090089c4 | 375 | storeAppend(data->entry, tmpbuf, strlen(tmpbuf)); |
376 | return; | |
377 | } | |
378 | } | |
379 | ||
380 | ||
381 | /* Convert Gopher to HTML */ | |
382 | /* Borrow part of code from libwww2 came with Mosaic distribution */ | |
56fa4cad | 383 | static void gopherToHTML(data, inbuf, len) |
384 | GopherStateData *data; | |
090089c4 | 385 | char *inbuf; |
386 | int len; | |
387 | { | |
388 | char *pos = inbuf; | |
389 | char *lpos = NULL; | |
390 | char *tline = NULL; | |
95d659f0 | 391 | LOCAL_ARRAY(char, line, TEMP_BUF_SIZE); |
392 | LOCAL_ARRAY(char, tmpbuf, TEMP_BUF_SIZE); | |
393 | LOCAL_ARRAY(char, outbuf, TEMP_BUF_SIZE << 4); | |
090089c4 | 394 | char *name = NULL; |
395 | char *selector = NULL; | |
396 | char *host = NULL; | |
397 | char *port = NULL; | |
398 | char *escaped_selector = NULL; | |
399 | char *icon_type = NULL; | |
400 | char gtype; | |
401 | StoreEntry *entry = NULL; | |
402 | ||
53d02438 | 403 | memset(outbuf, '\0', TEMP_BUF_SIZE << 4); |
404 | memset(tmpbuf, '\0', TEMP_BUF_SIZE); | |
405 | memset(line, '\0', TEMP_BUF_SIZE); | |
090089c4 | 406 | |
407 | entry = data->entry; | |
408 | ||
409 | if (data->conversion == HTML_INDEX_PAGE) { | |
a8f7d3ee | 410 | sprintf(outbuf, "<HTML><HEAD><TITLE>Gopher Index %s</TITLE></HEAD>\n\ |
411 | <BODY><H1>%s<BR>Gopher Search</H1>\n\ | |
412 | <p>This is a searchable Gopher index. Use the search\n\ | |
413 | function of your browser to enter search terms.\n\ | |
414 | <ISINDEX></BODY></HTML>\n", entry->url, entry->url); | |
090089c4 | 415 | storeAppend(entry, outbuf, strlen(outbuf)); |
416 | /* now let start sending stuff to client */ | |
417 | BIT_RESET(entry->flag, DELAY_SENDING); | |
418 | data->data_in = 1; | |
419 | ||
420 | return; | |
421 | } | |
422 | if (data->conversion == HTML_CSO_PAGE) { | |
a8f7d3ee | 423 | sprintf(outbuf, "<HTML><HEAD><TITLE>CSO Search of %s</TITLE></HEAD>\n\ |
424 | <BODY><H1>%s<BR>CSO Search</H1>\n\ | |
425 | <P>A CSO database usually contains a phonebook or\n\ | |
426 | directory. Use the search function of your browser to enter\n\ | |
427 | search terms.</P><ISINDEX></BODY></HTML>\n", | |
090089c4 | 428 | entry->url, entry->url); |
429 | ||
430 | storeAppend(entry, outbuf, strlen(outbuf)); | |
431 | /* now let start sending stuff to client */ | |
432 | BIT_RESET(entry->flag, DELAY_SENDING); | |
433 | data->data_in = 1; | |
434 | ||
435 | return; | |
436 | } | |
437 | inbuf[len] = '\0'; | |
438 | ||
439 | if (!data->HTML_header_added) { | |
440 | if (data->conversion == HTML_CSO_RESULT) | |
a8f7d3ee | 441 | strcat(outbuf, "<HTML><HEAD><TITLE>CSO Searchs Result</TITLE></HEAD>\n\ |
442 | <BODY><H1>CSO Searchs Result</H1>\n<PRE>\n"); | |
090089c4 | 443 | else |
a8f7d3ee | 444 | strcat(outbuf, "<HTML><HEAD><TITLE>Gopher Menu</TITLE></HEAD>\n\ |
445 | <BODY><H1>Gopher Menu</H1>\n<PRE>\n"); | |
090089c4 | 446 | data->HTML_header_added = 1; |
447 | } | |
448 | while ((pos != NULL) && (pos < inbuf + len)) { | |
449 | ||
450 | if (data->len != 0) { | |
451 | /* there is something left from last tx. */ | |
452 | strncpy(line, data->buf, data->len); | |
453 | lpos = (char *) memccpy(line + data->len, inbuf, '\n', len); | |
454 | if (lpos) | |
455 | *lpos = '\0'; | |
456 | else { | |
457 | /* there is no complete line in inbuf */ | |
458 | /* copy it to temp buffer */ | |
459 | if (data->len + len > TEMP_BUF_SIZE) { | |
019dd986 | 460 | debug(10, 1, "GopherHTML: Buffer overflow. Lost some data on URL: %s\n", |
090089c4 | 461 | entry->url); |
462 | len = TEMP_BUF_SIZE - data->len; | |
463 | } | |
30a4f2a8 | 464 | xmemcpy(data->buf + data->len, inbuf, len); |
090089c4 | 465 | data->len += len; |
466 | return; | |
467 | } | |
468 | ||
469 | /* skip one line */ | |
470 | pos = (char *) memchr(pos, '\n', 256); | |
471 | if (pos) | |
472 | pos++; | |
473 | ||
474 | /* we're done with the remain from last tx. */ | |
475 | data->len = 0; | |
476 | *(data->buf) = '\0'; | |
477 | } else { | |
478 | ||
479 | lpos = (char *) memccpy(line, pos, '\n', len - (pos - inbuf)); | |
480 | if (lpos) | |
481 | *lpos = '\0'; | |
482 | else { | |
483 | /* there is no complete line in inbuf */ | |
484 | /* copy it to temp buffer */ | |
485 | if ((len - (pos - inbuf)) > TEMP_BUF_SIZE) { | |
019dd986 | 486 | debug(10, 1, "GopherHTML: Buffer overflow. Lost some data on URL: %s\n", |
090089c4 | 487 | entry->url); |
488 | len = TEMP_BUF_SIZE; | |
489 | } | |
490 | if (len > (pos - inbuf)) { | |
30a4f2a8 | 491 | xmemcpy(data->buf, pos, len - (pos - inbuf)); |
090089c4 | 492 | data->len = len - (pos - inbuf); |
493 | } | |
494 | break; | |
495 | } | |
496 | ||
497 | /* skip one line */ | |
498 | pos = (char *) memchr(pos, '\n', 256); | |
499 | if (pos) | |
500 | pos++; | |
501 | ||
502 | } | |
503 | ||
504 | /* at this point. We should have one line in buffer to process */ | |
505 | ||
506 | if (*line == '.') { | |
507 | /* skip it */ | |
508 | memset(line, '\0', TEMP_BUF_SIZE); | |
509 | continue; | |
510 | } | |
511 | switch (data->conversion) { | |
512 | ||
513 | case HTML_INDEX_RESULT: | |
514 | case HTML_DIR:{ | |
515 | tline = line; | |
516 | gtype = *tline++; | |
517 | name = tline; | |
518 | selector = strchr(tline, TAB); | |
519 | if (selector) { | |
520 | *selector++ = '\0'; | |
521 | host = strchr(selector, TAB); | |
522 | if (host) { | |
523 | *host++ = '\0'; | |
524 | port = strchr(host, TAB); | |
525 | if (port) { | |
526 | char *junk; | |
527 | port[0] = ':'; | |
528 | junk = strchr(host, TAB); | |
529 | if (junk) | |
530 | *junk++ = 0; /* Chop port */ | |
531 | else { | |
532 | junk = strchr(host, '\r'); | |
533 | if (junk) | |
534 | *junk++ = 0; /* Chop port */ | |
535 | else { | |
536 | junk = strchr(host, '\n'); | |
537 | if (junk) | |
538 | *junk++ = 0; /* Chop port */ | |
539 | } | |
540 | } | |
541 | if ((port[1] == '0') && (!port[2])) | |
542 | port[0] = 0; /* 0 means none */ | |
543 | } | |
544 | /* escape a selector here */ | |
545 | escaped_selector = url_escape(selector); | |
546 | ||
547 | switch (gtype) { | |
548 | case GOPHER_DIRECTORY: | |
549 | icon_type = "internal-gopher-menu"; | |
550 | break; | |
551 | case GOPHER_FILE: | |
552 | icon_type = "internal-gopher-text"; | |
553 | break; | |
554 | case GOPHER_INDEX: | |
555 | case GOPHER_CSO: | |
556 | icon_type = "internal-gopher-index"; | |
557 | break; | |
558 | case GOPHER_IMAGE: | |
559 | case GOPHER_GIF: | |
560 | case GOPHER_PLUS_IMAGE: | |
561 | icon_type = "internal-gopher-image"; | |
562 | break; | |
563 | case GOPHER_SOUND: | |
564 | case GOPHER_PLUS_SOUND: | |
565 | icon_type = "internal-gopher-sound"; | |
566 | break; | |
567 | case GOPHER_PLUS_MOVIE: | |
568 | icon_type = "internal-gopher-movie"; | |
569 | break; | |
570 | case GOPHER_TELNET: | |
571 | case GOPHER_3270: | |
572 | icon_type = "internal-gopher-telnet"; | |
573 | break; | |
574 | case GOPHER_BIN: | |
575 | case GOPHER_MACBINHEX: | |
576 | case GOPHER_DOSBIN: | |
577 | case GOPHER_UUENCODED: | |
578 | icon_type = "internal-gopher-binary"; | |
579 | break; | |
580 | default: | |
581 | icon_type = "internal-gopher-unknown"; | |
582 | break; | |
583 | } | |
584 | ||
585 | ||
586 | memset(tmpbuf, '\0', TEMP_BUF_SIZE); | |
587 | if ((gtype == GOPHER_TELNET) || (gtype == GOPHER_3270)) { | |
588 | if (strlen(escaped_selector) != 0) | |
589 | sprintf(tmpbuf, "<IMG BORDER=0 SRC=\"%s\"> <A HREF=\"telnet://%s@%s/\">%s</A>\n", | |
590 | icon_type, escaped_selector, host, name); | |
591 | else | |
592 | sprintf(tmpbuf, "<IMG BORDER=0 SRC=\"%s\"> <A HREF=\"telnet://%s/\">%s</A>\n", | |
593 | icon_type, host, name); | |
594 | ||
595 | } else { | |
596 | sprintf(tmpbuf, "<IMG BORDER=0 SRC=\"%s\"> <A HREF=\"gopher://%s/%c%s\">%s</A>\n", | |
597 | icon_type, host, gtype, escaped_selector, name); | |
598 | } | |
599 | safe_free(escaped_selector); | |
600 | strcat(outbuf, tmpbuf); | |
601 | data->data_in = 1; | |
602 | } else { | |
603 | memset(line, '\0', TEMP_BUF_SIZE); | |
604 | continue; | |
605 | } | |
606 | } else { | |
607 | memset(line, '\0', TEMP_BUF_SIZE); | |
608 | continue; | |
609 | } | |
610 | break; | |
611 | } /* HTML_DIR, HTML_INDEX_RESULT */ | |
612 | ||
613 | ||
614 | case HTML_CSO_RESULT:{ | |
615 | int t; | |
616 | int code; | |
617 | int recno; | |
95d659f0 | 618 | LOCAL_ARRAY(char, result, MAX_CSO_RESULT); |
090089c4 | 619 | |
620 | tline = line; | |
621 | ||
622 | if (tline[0] == '-') { | |
623 | t = sscanf(tline, "-%d:%d:%[^\n]", &code, &recno, result); | |
624 | if (t < 3) | |
625 | break; | |
626 | ||
627 | if (code != 200) | |
628 | break; | |
629 | ||
630 | if (data->cso_recno != recno) { | |
631 | sprintf(tmpbuf, "</PRE><HR><H2>Record# %d<br><i>%s</i></H2>\n<PRE>", recno, result); | |
632 | data->cso_recno = recno; | |
633 | } else { | |
634 | sprintf(tmpbuf, "%s\n", result); | |
635 | } | |
636 | strcat(outbuf, tmpbuf); | |
637 | data->data_in = 1; | |
638 | break; | |
639 | } else { | |
640 | /* handle some error codes */ | |
641 | t = sscanf(tline, "%d:%[^\n]", &code, result); | |
642 | ||
643 | if (t < 2) | |
644 | break; | |
645 | ||
646 | switch (code) { | |
647 | ||
648 | case 200:{ | |
649 | /* OK */ | |
650 | /* Do nothing here */ | |
651 | break; | |
652 | } | |
653 | ||
654 | case 102: /* Number of matches */ | |
655 | case 501: /* No Match */ | |
656 | case 502: /* Too Many Matches */ | |
657 | { | |
658 | /* Print the message the server returns */ | |
a8f7d3ee | 659 | sprintf(tmpbuf, "</PRE><HR><H2>%s</H2>\n<PRE>", result); |
090089c4 | 660 | strcat(outbuf, tmpbuf); |
661 | data->data_in = 1; | |
662 | break; | |
663 | } | |
664 | ||
665 | ||
666 | } | |
667 | } | |
668 | ||
669 | } /* HTML_CSO_RESULT */ | |
670 | default: | |
671 | break; /* do nothing */ | |
672 | ||
673 | } /* switch */ | |
674 | ||
675 | } /* while loop */ | |
676 | ||
677 | if ((int) strlen(outbuf) > 0) { | |
678 | storeAppend(entry, outbuf, strlen(outbuf)); | |
679 | /* now let start sending stuff to client */ | |
680 | BIT_RESET(entry->flag, DELAY_SENDING); | |
681 | } | |
682 | return; | |
683 | } | |
684 | ||
56fa4cad | 685 | static int gopherReadReplyTimeout(fd, data) |
090089c4 | 686 | int fd; |
56fa4cad | 687 | GopherStateData *data; |
090089c4 | 688 | { |
689 | StoreEntry *entry = NULL; | |
690 | entry = data->entry; | |
019dd986 | 691 | debug(10, 4, "GopherReadReplyTimeout: Timeout on %d\n url: %s\n", fd, entry->url); |
b8de7ebe | 692 | squid_error_entry(entry, ERR_READ_TIMEOUT, NULL); |
51fa90db | 693 | comm_close(fd); |
090089c4 | 694 | return 0; |
695 | } | |
696 | ||
697 | /* This will be called when socket lifetime is expired. */ | |
56fa4cad | 698 | static void gopherLifetimeExpire(fd, data) |
090089c4 | 699 | int fd; |
56fa4cad | 700 | GopherStateData *data; |
090089c4 | 701 | { |
702 | StoreEntry *entry = NULL; | |
703 | entry = data->entry; | |
019dd986 | 704 | debug(10, 4, "gopherLifeTimeExpire: FD %d: <URL:%s>\n", fd, entry->url); |
b8de7ebe | 705 | squid_error_entry(entry, ERR_LIFETIME_EXP, NULL); |
d1cfbef7 | 706 | comm_set_select_handler(fd, |
707 | COMM_SELECT_READ | COMM_SELECT_WRITE, | |
708 | 0, | |
709 | 0); | |
51fa90db | 710 | comm_close(fd); |
090089c4 | 711 | } |
712 | ||
713 | ||
714 | ||
715 | ||
716 | /* This will be called when data is ready to be read from fd. Read until | |
717 | * error or connection closed. */ | |
56fa4cad | 718 | static void gopherReadReply(fd, data) |
090089c4 | 719 | int fd; |
56fa4cad | 720 | GopherStateData *data; |
090089c4 | 721 | { |
722 | char *buf = NULL; | |
723 | int len; | |
724 | int clen; | |
725 | int off; | |
726 | StoreEntry *entry = NULL; | |
56fa4cad | 727 | int bin; |
090089c4 | 728 | |
729 | entry = data->entry; | |
56fa4cad | 730 | if (entry->flag & DELETE_BEHIND && !storeClientWaiting(entry)) { |
731 | /* we can terminate connection right now */ | |
732 | squid_error_entry(entry, ERR_NO_CLIENTS_BIG_OBJ, NULL); | |
733 | comm_close(fd); | |
734 | return; | |
735 | } | |
736 | /* check if we want to defer reading */ | |
737 | clen = entry->mem_obj->e_current_len; | |
738 | off = storeGetLowestReaderOffset(entry); | |
739 | if ((clen - off) > GOPHER_DELETE_GAP) { | |
740 | if (entry->flag & CLIENT_ABORT_REQUEST) { | |
741 | squid_error_entry(entry, ERR_CLIENT_ABORT, NULL); | |
51fa90db | 742 | comm_close(fd); |
56fa4cad | 743 | return; |
744 | } | |
745 | IOStats.Gopher.reads_deferred++; | |
746 | debug(10, 3, "gopherReadReply: Read deferred for Object: %s\n", | |
747 | entry->url); | |
748 | debug(10, 3, " Current Gap: %d bytes\n", clen - off); | |
749 | /* reschedule, so it will automatically reactivated when | |
750 | * Gap is big enough. */ | |
751 | comm_set_select_handler(fd, | |
752 | COMM_SELECT_READ, | |
753 | (PF) gopherReadReply, | |
754 | (void *) data); | |
755 | /* don't install read timeout until we are below the GAP */ | |
756 | comm_set_select_handler_plus_timeout(fd, | |
757 | COMM_SELECT_TIMEOUT, | |
758 | (PF) NULL, | |
759 | (void *) NULL, | |
760 | (time_t) 0); | |
761 | if (!BIT_TEST(entry->flag, READ_DEFERRED)) { | |
762 | comm_set_fd_lifetime(fd, 3600); /* limit during deferring */ | |
763 | BIT_SET(entry->flag, READ_DEFERRED); | |
090089c4 | 764 | } |
56fa4cad | 765 | /* dont try reading again for a while */ |
766 | comm_set_stall(fd, Config.stallDelay); | |
767 | return; | |
768 | } else { | |
769 | BIT_RESET(entry->flag, READ_DEFERRED); | |
090089c4 | 770 | } |
2daae136 | 771 | buf = get_free_4k_page(); |
6fe6313d | 772 | errno = 0; |
56fa4cad | 773 | /* leave one space for \0 in gopherToHTML */ |
774 | len = read(fd, buf, TEMP_BUF_SIZE - 1); | |
019dd986 | 775 | debug(10, 5, "gopherReadReply: FD %d read len=%d\n", fd, len); |
56fa4cad | 776 | if (len > 0) { |
777 | IOStats.Gopher.reads++; | |
778 | for (clen = len - 1, bin = 0; clen; bin++) | |
779 | clen >>= 1; | |
780 | IOStats.Gopher.read_hist[bin]++; | |
781 | } | |
ba718c8f | 782 | if (len < 0) { |
783 | debug(10, 1, "gopherReadReply: error reading: %s\n", xstrerror()); | |
784 | if (errno == EAGAIN || errno == EWOULDBLOCK) { | |
6fe6313d | 785 | /* reinstall handlers */ |
786 | /* XXX This may loop forever */ | |
56fa4cad | 787 | comm_set_select_handler(fd, |
788 | COMM_SELECT_READ, | |
789 | (PF) gopherReadReply, | |
790 | (void *) data); | |
791 | comm_set_select_handler_plus_timeout(fd, | |
792 | COMM_SELECT_TIMEOUT, | |
793 | (PF) gopherReadReplyTimeout, | |
794 | (void *) data, | |
795 | Config.readTimeout); | |
6fe6313d | 796 | } else { |
1c481e00 | 797 | BIT_RESET(entry->flag, ENTRY_CACHABLE); |
2daae136 | 798 | storeReleaseRequest(entry); |
b8de7ebe | 799 | squid_error_entry(entry, ERR_READ_ERROR, xstrerror()); |
51fa90db | 800 | comm_close(fd); |
6fe6313d | 801 | } |
ba718c8f | 802 | } else if (len == 0 && entry->mem_obj->e_current_len == 0) { |
b8de7ebe | 803 | squid_error_entry(entry, |
ba718c8f | 804 | ERR_ZERO_SIZE_OBJECT, |
805 | errno ? xstrerror() : NULL); | |
51fa90db | 806 | comm_close(fd); |
090089c4 | 807 | } else if (len == 0) { |
808 | /* Connection closed; retrieval done. */ | |
809 | /* flush the rest of data in temp buf if there is one. */ | |
810 | if (data->conversion != NORMAL) | |
811 | gopherEndHTML(data); | |
812 | if (!(entry->flag & DELETE_BEHIND)) | |
dd44ede3 | 813 | ttlSet(entry); |
090089c4 | 814 | BIT_RESET(entry->flag, DELAY_SENDING); |
815 | storeComplete(entry); | |
51fa90db | 816 | comm_close(fd); |
b6f794d6 | 817 | } else if (((entry->mem_obj->e_current_len + len) > Config.Gopher.maxObjSize) && |
090089c4 | 818 | !(entry->flag & DELETE_BEHIND)) { |
819 | /* accept data, but start to delete behind it */ | |
820 | storeStartDeleteBehind(entry); | |
090089c4 | 821 | if (data->conversion != NORMAL) { |
822 | gopherToHTML(data, buf, len); | |
823 | } else { | |
824 | storeAppend(entry, buf, len); | |
825 | } | |
ba718c8f | 826 | comm_set_select_handler(fd, |
827 | COMM_SELECT_READ, | |
828 | (PF) gopherReadReply, | |
51496678 | 829 | (void *) data); |
ba718c8f | 830 | comm_set_select_handler_plus_timeout(fd, |
831 | COMM_SELECT_TIMEOUT, | |
832 | (PF) gopherReadReplyTimeout, | |
51496678 | 833 | (void *) data, |
b6f794d6 | 834 | Config.readTimeout); |
090089c4 | 835 | } else if (entry->flag & CLIENT_ABORT_REQUEST) { |
836 | /* append the last bit of info we got */ | |
837 | if (data->conversion != NORMAL) { | |
838 | gopherToHTML(data, buf, len); | |
839 | } else { | |
840 | storeAppend(entry, buf, len); | |
841 | } | |
b8de7ebe | 842 | squid_error_entry(entry, ERR_CLIENT_ABORT, NULL); |
090089c4 | 843 | if (data->conversion != NORMAL) |
844 | gopherEndHTML(data); | |
845 | BIT_RESET(entry->flag, DELAY_SENDING); | |
51fa90db | 846 | comm_close(fd); |
090089c4 | 847 | } else { |
848 | if (data->conversion != NORMAL) { | |
849 | gopherToHTML(data, buf, len); | |
850 | } else { | |
851 | storeAppend(entry, buf, len); | |
852 | } | |
ba718c8f | 853 | comm_set_select_handler(fd, |
854 | COMM_SELECT_READ, | |
855 | (PF) gopherReadReply, | |
51496678 | 856 | (void *) data); |
ba718c8f | 857 | comm_set_select_handler_plus_timeout(fd, |
858 | COMM_SELECT_TIMEOUT, | |
859 | (PF) gopherReadReplyTimeout, | |
51496678 | 860 | (void *) data, |
b6f794d6 | 861 | Config.readTimeout); |
090089c4 | 862 | } |
2daae136 | 863 | put_free_4k_page(buf); |
56fa4cad | 864 | return; |
090089c4 | 865 | } |
866 | ||
867 | /* This will be called when request write is complete. Schedule read of | |
868 | * reply. */ | |
56fa4cad | 869 | static void gopherSendComplete(fd, buf, size, errflag, data) |
090089c4 | 870 | int fd; |
871 | char *buf; | |
872 | int size; | |
873 | int errflag; | |
30a4f2a8 | 874 | void *data; |
090089c4 | 875 | { |
56fa4cad | 876 | GopherStateData *gopherState = (GopherStateData *) data; |
090089c4 | 877 | StoreEntry *entry = NULL; |
30a4f2a8 | 878 | entry = gopherState->entry; |
019dd986 | 879 | debug(10, 5, "gopherSendComplete: FD %d size: %d errflag: %d\n", |
090089c4 | 880 | fd, size, errflag); |
881 | if (errflag) { | |
b8de7ebe | 882 | squid_error_entry(entry, ERR_CONNECT_FAIL, xstrerror()); |
51fa90db | 883 | comm_close(fd); |
090089c4 | 884 | if (buf) |
2daae136 | 885 | put_free_4k_page(buf); /* Allocated by gopherSendRequest. */ |
44a47c6e | 886 | return; |
090089c4 | 887 | } |
888 | /* | |
889 | * OK. We successfully reach remote site. Start MIME typing | |
890 | * stuff. Do it anyway even though request is not HTML type. | |
891 | */ | |
30a4f2a8 | 892 | gopherMimeCreate(gopherState); |
090089c4 | 893 | |
6eb42cae | 894 | if (!BIT_TEST(entry->flag, ENTRY_HTML)) |
30a4f2a8 | 895 | gopherState->conversion = NORMAL; |
090089c4 | 896 | else |
30a4f2a8 | 897 | switch (gopherState->type_id) { |
090089c4 | 898 | |
899 | case GOPHER_DIRECTORY: | |
900 | /* we got to convert it first */ | |
901 | BIT_SET(entry->flag, DELAY_SENDING); | |
30a4f2a8 | 902 | gopherState->conversion = HTML_DIR; |
903 | gopherState->HTML_header_added = 0; | |
090089c4 | 904 | break; |
905 | ||
906 | case GOPHER_INDEX: | |
907 | /* we got to convert it first */ | |
908 | BIT_SET(entry->flag, DELAY_SENDING); | |
30a4f2a8 | 909 | gopherState->conversion = HTML_INDEX_RESULT; |
910 | gopherState->HTML_header_added = 0; | |
090089c4 | 911 | break; |
912 | ||
913 | case GOPHER_CSO: | |
914 | /* we got to convert it first */ | |
915 | BIT_SET(entry->flag, DELAY_SENDING); | |
30a4f2a8 | 916 | gopherState->conversion = HTML_CSO_RESULT; |
917 | gopherState->cso_recno = 0; | |
918 | gopherState->HTML_header_added = 0; | |
090089c4 | 919 | break; |
920 | ||
921 | default: | |
30a4f2a8 | 922 | gopherState->conversion = NORMAL; |
090089c4 | 923 | |
924 | } | |
925 | /* Schedule read reply. */ | |
926 | comm_set_select_handler(fd, | |
927 | COMM_SELECT_READ, | |
928 | (PF) gopherReadReply, | |
30a4f2a8 | 929 | (void *) gopherState); |
090089c4 | 930 | comm_set_select_handler_plus_timeout(fd, |
931 | COMM_SELECT_TIMEOUT, | |
932 | (PF) gopherReadReplyTimeout, | |
30a4f2a8 | 933 | (void *) gopherState, |
b6f794d6 | 934 | Config.readTimeout); |
30a4f2a8 | 935 | comm_set_fd_lifetime(fd, 86400); /* extend lifetime */ |
090089c4 | 936 | |
937 | if (buf) | |
2daae136 | 938 | put_free_4k_page(buf); /* Allocated by gopherSendRequest. */ |
090089c4 | 939 | } |
940 | ||
941 | /* This will be called when connect completes. Write request. */ | |
56fa4cad | 942 | static void gopherSendRequest(fd, data) |
090089c4 | 943 | int fd; |
56fa4cad | 944 | GopherStateData *data; |
090089c4 | 945 | { |
090089c4 | 946 | int len; |
95d659f0 | 947 | LOCAL_ARRAY(char, query, MAX_URL); |
2daae136 | 948 | char *buf = get_free_4k_page(); |
090089c4 | 949 | |
090089c4 | 950 | if (data->type_id == GOPHER_CSO) { |
951 | sscanf(data->request, "?%s", query); | |
952 | len = strlen(query) + 15; | |
2285407f | 953 | sprintf(buf, "query %s\r\nquit\r\n", query); |
090089c4 | 954 | } else if (data->type_id == GOPHER_INDEX) { |
955 | char *c_ptr = strchr(data->request, '?'); | |
956 | if (c_ptr) { | |
957 | *c_ptr = '\t'; | |
958 | } | |
959 | len = strlen(data->request) + 3; | |
2285407f | 960 | sprintf(buf, "%s\r\n", data->request); |
090089c4 | 961 | } else { |
962 | len = strlen(data->request) + 3; | |
2285407f | 963 | sprintf(buf, "%s\r\n", data->request); |
090089c4 | 964 | } |
965 | ||
019dd986 | 966 | debug(10, 5, "gopherSendRequest: FD %d\n", fd); |
30a4f2a8 | 967 | comm_write(fd, |
44a47c6e | 968 | buf, |
969 | len, | |
970 | 30, | |
971 | gopherSendComplete, | |
9864ee44 | 972 | (void *) data, |
973 | put_free_4k_page); | |
1c481e00 | 974 | if (BIT_TEST(data->entry->flag, ENTRY_CACHABLE)) |
147d3115 | 975 | storeSetPublicKey(data->entry); /* Make it public */ |
090089c4 | 976 | } |
977 | ||
978 | int gopherStart(unusedfd, url, entry) | |
979 | int unusedfd; | |
980 | char *url; | |
981 | StoreEntry *entry; | |
982 | { | |
983 | /* Create state structure. */ | |
984 | int sock, status; | |
56fa4cad | 985 | GopherStateData *data = CreateGopherStateData(); |
090089c4 | 986 | |
30a4f2a8 | 987 | storeLockObject(data->entry = entry, NULL, NULL); |
090089c4 | 988 | |
019dd986 | 989 | debug(10, 3, "gopherStart: url: %s\n", url); |
090089c4 | 990 | |
991 | /* Parse url. */ | |
992 | if (gopher_url_parser(url, data->host, &data->port, | |
993 | &data->type_id, data->request)) { | |
b8de7ebe | 994 | squid_error_entry(entry, ERR_INVALID_URL, NULL); |
51fa90db | 995 | gopherStateFree(-1, data); |
090089c4 | 996 | return COMM_ERROR; |
997 | } | |
998 | /* Create socket. */ | |
b6f794d6 | 999 | sock = comm_open(COMM_NONBLOCKING, Config.Addrs.tcp_outgoing, 0, url); |
090089c4 | 1000 | if (sock == COMM_ERROR) { |
019dd986 | 1001 | debug(10, 4, "gopherStart: Failed because we're out of sockets.\n"); |
b8de7ebe | 1002 | squid_error_entry(entry, ERR_NO_FDS, xstrerror()); |
51fa90db | 1003 | gopherStateFree(-1, data); |
090089c4 | 1004 | return COMM_ERROR; |
1005 | } | |
30a4f2a8 | 1006 | comm_add_close_handler(sock, |
983061ed | 1007 | (PF) gopherStateFree, |
51fa90db | 1008 | (void *) data); |
1009 | ||
090089c4 | 1010 | /* check if IP is already in cache. It must be. |
1011 | * It should be done before this route is called. | |
1012 | * Otherwise, we cannot check return code for connect. */ | |
30a4f2a8 | 1013 | if (!ipcache_gethostbyname(data->host, 0)) { |
019dd986 | 1014 | debug(10, 4, "gopherStart: Called without IP entry in ipcache. OR lookup failed.\n"); |
b8de7ebe | 1015 | squid_error_entry(entry, ERR_DNS_FAIL, dns_error_message); |
51fa90db | 1016 | comm_close(sock); |
090089c4 | 1017 | return COMM_ERROR; |
1018 | } | |
1019 | if (((data->type_id == GOPHER_INDEX) || (data->type_id == GOPHER_CSO)) | |
1020 | && (strchr(data->request, '?') == NULL) | |
6eb42cae | 1021 | && (BIT_TEST(entry->flag, ENTRY_HTML))) { |
090089c4 | 1022 | /* Index URL without query word */ |
1023 | /* We have to generate search page back to client. No need for connection */ | |
1024 | gopherMimeCreate(data); | |
1025 | ||
1026 | if (data->type_id == GOPHER_INDEX) { | |
1027 | data->conversion = HTML_INDEX_PAGE; | |
1028 | } else { | |
1029 | if (data->type_id == GOPHER_CSO) { | |
1030 | data->conversion = HTML_CSO_PAGE; | |
1031 | } else { | |
1032 | data->conversion = HTML_INDEX_PAGE; | |
1033 | } | |
1034 | } | |
1035 | gopherToHTML(data, (char *) NULL, 0); | |
1036 | storeComplete(entry); | |
51fa90db | 1037 | comm_close(sock); |
090089c4 | 1038 | return COMM_OK; |
1039 | } | |
1040 | /* Open connection. */ | |
1041 | if ((status = comm_connect(sock, data->host, data->port)) != 0) { | |
1042 | if (status != EINPROGRESS) { | |
b8de7ebe | 1043 | squid_error_entry(entry, ERR_CONNECT_FAIL, xstrerror()); |
51fa90db | 1044 | comm_close(sock); |
090089c4 | 1045 | return COMM_ERROR; |
1046 | } else { | |
019dd986 | 1047 | debug(10, 5, "startGopher: conn %d EINPROGRESS\n", sock); |
090089c4 | 1048 | } |
1049 | } | |
1050 | /* Install connection complete handler. */ | |
f900607e | 1051 | if (opt_no_ipcache) |
1052 | ipcacheInvalidate(data->host); | |
d1cfbef7 | 1053 | comm_set_select_handler(sock, |
1054 | COMM_SELECT_LIFETIME, | |
1055 | (PF) gopherLifetimeExpire, | |
51496678 | 1056 | (void *) data); |
d1cfbef7 | 1057 | comm_set_select_handler(sock, |
1058 | COMM_SELECT_WRITE, | |
1059 | (PF) gopherSendRequest, | |
51496678 | 1060 | (void *) data); |
090089c4 | 1061 | return COMM_OK; |
1062 | } | |
1063 | ||
1064 | ||
56fa4cad | 1065 | static GopherStateData *CreateGopherStateData() |
090089c4 | 1066 | { |
56fa4cad | 1067 | GopherStateData *gd = xcalloc(1, sizeof(GopherStateData)); |
2daae136 | 1068 | gd->buf = get_free_4k_page(); |
090089c4 | 1069 | return (gd); |
1070 | } |