gopher.c
上传用户:liugui
上传日期:2007-01-04
资源大小:822k
文件大小:22k
源码类别:

代理服务器

开发平台:

Unix_Linux

  1. /*
  2.  * $Id: gopher.c,v 1.150 1999/01/31 15:58:54 wessels Exp $
  3.  *
  4.  * DEBUG: section 10    Gopher
  5.  * AUTHOR: Harvest Derived
  6.  *
  7.  * SQUID Internet Object Cache  http://squid.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 the
  13.  *  National Science Foundation.  Squid is Copyrighted (C) 1998 by
  14.  *  Duane Wessels and the University of California San Diego.  Please
  15.  *  see the COPYRIGHT file for full details.  Squid incorporates
  16.  *  software developed and/or copyrighted by other sources.  Please see
  17.  *  the CREDITS file for full details.
  18.  *
  19.  *  This program is free software; you can redistribute it and/or modify
  20.  *  it under the terms of the GNU General Public License as published by
  21.  *  the Free Software Foundation; either version 2 of the License, or
  22.  *  (at your option) any later version.
  23.  *  
  24.  *  This program is distributed in the hope that it will be useful,
  25.  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  26.  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  27.  *  GNU General Public License for more details.
  28.  *  
  29.  *  You should have received a copy of the GNU General Public License
  30.  *  along with this program; if not, write to the Free Software
  31.  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
  32.  *
  33.  */
  34. #include "squid.h"
  35. /* gopher type code from rfc. Anawat. */
  36. #define GOPHER_FILE         '0'
  37. #define GOPHER_DIRECTORY    '1'
  38. #define GOPHER_CSO          '2'
  39. #define GOPHER_ERROR        '3'
  40. #define GOPHER_MACBINHEX    '4'
  41. #define GOPHER_DOSBIN       '5'
  42. #define GOPHER_UUENCODED    '6'
  43. #define GOPHER_INDEX        '7'
  44. #define GOPHER_TELNET       '8'
  45. #define GOPHER_BIN          '9'
  46. #define GOPHER_REDUNT       '+'
  47. #define GOPHER_3270         'T'
  48. #define GOPHER_GIF          'g'
  49. #define GOPHER_IMAGE        'I'
  50. #define GOPHER_HTML         'h' /* HTML */
  51. #define GOPHER_INFO         'i'
  52. #define GOPHER_WWW          'w' /* W3 address */
  53. #define GOPHER_SOUND        's'
  54. #define GOPHER_PLUS_IMAGE   ':'
  55. #define GOPHER_PLUS_MOVIE   ';'
  56. #define GOPHER_PLUS_SOUND   '<'
  57. #define GOPHER_PORT         70
  58. #define TAB                 't'
  59. #define TEMP_BUF_SIZE       4096
  60. #define MAX_CSO_RESULT      1024
  61. typedef struct gopher_ds {
  62.     StoreEntry *entry;
  63.     char host[SQUIDHOSTNAMELEN + 1];
  64.     enum {
  65. NORMAL,
  66. HTML_DIR,
  67. HTML_INDEX_RESULT,
  68. HTML_CSO_RESULT,
  69. HTML_INDEX_PAGE,
  70. HTML_CSO_PAGE
  71.     } conversion;
  72.     int HTML_header_added;
  73.     int port;
  74.     char type_id;
  75.     char request[MAX_URL];
  76.     int data_in;
  77.     int cso_recno;
  78.     int len;
  79.     char *buf; /* pts to a 4k page */
  80.     int fd;
  81.     FwdState *fwdState;
  82. } GopherStateData;
  83. static PF gopherStateFree;
  84. static void gopher_mime_content(MemBuf * mb, const char *name, const char *def);
  85. static void gopherMimeCreate(GopherStateData *);
  86. static int gopher_url_parser(const char *url,
  87.     char *host,
  88.     int *port,
  89.     char *type_id,
  90.     char *request);
  91. static void gopherEndHTML(GopherStateData *);
  92. static void gopherToHTML(GopherStateData *, char *inbuf, int len);
  93. static PF gopherTimeout;
  94. static PF gopherReadReply;
  95. static CWCB gopherSendComplete;
  96. static PF gopherSendRequest;
  97. static GopherStateData *CreateGopherStateData(void);
  98. static char def_gopher_bin[] = "www/unknown";
  99. static char def_gopher_text[] = "text/plain";
  100. static void
  101. gopherStateFree(int fdnotused, void *data)
  102. {
  103.     GopherStateData *gopherState = data;
  104.     if (gopherState == NULL)
  105. return;
  106.     if (gopherState->entry) {
  107. storeUnlockObject(gopherState->entry);
  108.     }
  109.     memFree(gopherState->buf, MEM_4K_BUF);
  110.     gopherState->buf = NULL;
  111.     cbdataFree(gopherState);
  112. }
  113. /* figure out content type from file extension */
  114. static void
  115. gopher_mime_content(MemBuf * mb, const char *name, const char *def_ctype)
  116. {
  117.     char *ctype = mimeGetContentType(name);
  118.     char *cenc = mimeGetContentEncoding(name);
  119.     if (cenc)
  120. memBufPrintf(mb, "Content-Encoding: %srn", cenc);
  121.     memBufPrintf(mb, "Content-Type: %srn",
  122. ctype ? ctype : def_ctype);
  123. }
  124. /* create MIME Header for Gopher Data */
  125. static void
  126. gopherMimeCreate(GopherStateData * gopherState)
  127. {
  128.     MemBuf mb;
  129.     memBufDefInit(&mb);
  130.     memBufPrintf(&mb,
  131. "HTTP/1.0 200 OK Gatewayingrn"
  132. "Server: Squid/%srn"
  133. "Date: %srn"
  134. "MIME-version: 1.0rn",
  135. version_string, mkrfc1123(squid_curtime));
  136.     switch (gopherState->type_id) {
  137.     case GOPHER_DIRECTORY:
  138.     case GOPHER_INDEX:
  139.     case GOPHER_HTML:
  140.     case GOPHER_WWW:
  141.     case GOPHER_CSO:
  142. memBufPrintf(&mb, "Content-Type: text/htmlrn");
  143. break;
  144.     case GOPHER_GIF:
  145.     case GOPHER_IMAGE:
  146.     case GOPHER_PLUS_IMAGE:
  147. memBufPrintf(&mb, "Content-Type: image/gifrn");
  148. break;
  149.     case GOPHER_SOUND:
  150.     case GOPHER_PLUS_SOUND:
  151. memBufPrintf(&mb, "Content-Type: audio/basicrn");
  152. break;
  153.     case GOPHER_PLUS_MOVIE:
  154. memBufPrintf(&mb, "Content-Type: video/mpegrn");
  155. break;
  156.     case GOPHER_MACBINHEX:
  157.     case GOPHER_DOSBIN:
  158.     case GOPHER_UUENCODED:
  159.     case GOPHER_BIN:
  160. /* Rightnow We have no idea what it is. */
  161. gopher_mime_content(&mb, gopherState->request, def_gopher_bin);
  162. break;
  163.     case GOPHER_FILE:
  164.     default:
  165. gopher_mime_content(&mb, gopherState->request, def_gopher_text);
  166. break;
  167.     }
  168.     memBufPrintf(&mb, "rn");
  169.     EBIT_CLR(gopherState->entry->flags, ENTRY_FWD_HDR_WAIT);
  170.     storeAppend(gopherState->entry, mb.buf, mb.size);
  171.     memBufClean(&mb);
  172. }
  173. /* Parse a gopher url into components.  By Anawat. */
  174. static int
  175. gopher_url_parser(const char *url, char *host, int *port, char *type_id, char *request)
  176. {
  177.     LOCAL_ARRAY(char, proto, MAX_URL);
  178.     LOCAL_ARRAY(char, hostbuf, MAX_URL);
  179.     int t;
  180.     proto[0] = hostbuf[0] = '';
  181.     host[0] = request[0] = '';
  182.     (*port) = 0;
  183.     (*type_id) = 0;
  184.     t = sscanf(url,
  185. #if defined(__QNX__)
  186. "%[abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ]://%[^/]/%c%s",
  187. #else
  188. "%[a-zA-Z]://%[^/]/%c%s",
  189. #endif
  190. proto, hostbuf, type_id, request);
  191.     if ((t < 2) || strcasecmp(proto, "gopher")) {
  192. return -1;
  193.     } else if (t == 2) {
  194. (*type_id) = GOPHER_DIRECTORY;
  195. request[0] = '';
  196.     } else if (t == 3) {
  197. request[0] = '';
  198.     } else {
  199. /* convert %xx to char */
  200. url_convert_hex(request, 0);
  201.     }
  202.     host[0] = '';
  203.     if (sscanf(hostbuf, "%[^:]:%d", host, port) < 2)
  204. (*port) = GOPHER_PORT;
  205.     return 0;
  206. }
  207. int
  208. gopherCachable(const char *url)
  209. {
  210.     GopherStateData *gopherState = NULL;
  211.     int cachable = 1;
  212.     /* use as temp data structure to parse gopher URL */
  213.     gopherState = CreateGopherStateData();
  214.     /* parse to see type */
  215.     gopher_url_parser(url,
  216. gopherState->host,
  217. &gopherState->port,
  218. &gopherState->type_id,
  219. gopherState->request);
  220.     switch (gopherState->type_id) {
  221.     case GOPHER_INDEX:
  222.     case GOPHER_CSO:
  223.     case GOPHER_TELNET:
  224.     case GOPHER_3270:
  225. cachable = 0;
  226. break;
  227.     default:
  228. cachable = 1;
  229.     }
  230.     gopherStateFree(-1, gopherState);
  231.     return cachable;
  232. }
  233. static void
  234. gopherEndHTML(GopherStateData * gopherState)
  235. {
  236.     if (!gopherState->data_in)
  237. storeAppendPrintf(gopherState->entry,
  238.     "<HTML><HEAD><TITLE>Server Return Nothing.</TITLE>n"
  239.     "</HEAD><BODY><HR><H1>Server Return Nothing.</H1></BODY></HTML>n");
  240. }
  241. /* Convert Gopher to HTML */
  242. /* Borrow part of code from libwww2 came with Mosaic distribution */
  243. static void
  244. gopherToHTML(GopherStateData * gopherState, char *inbuf, int len)
  245. {
  246.     char *pos = inbuf;
  247.     char *lpos = NULL;
  248.     char *tline = NULL;
  249.     LOCAL_ARRAY(char, line, TEMP_BUF_SIZE);
  250.     LOCAL_ARRAY(char, tmpbuf, TEMP_BUF_SIZE);
  251.     LOCAL_ARRAY(char, outbuf, TEMP_BUF_SIZE << 4);
  252.     char *name = NULL;
  253.     char *selector = NULL;
  254.     char *host = NULL;
  255.     char *port = NULL;
  256.     char *escaped_selector = NULL;
  257.     char *icon_url = NULL;
  258.     char gtype;
  259.     StoreEntry *entry = NULL;
  260.     memset(outbuf, '', TEMP_BUF_SIZE << 4);
  261.     memset(tmpbuf, '', TEMP_BUF_SIZE);
  262.     memset(line, '', TEMP_BUF_SIZE);
  263.     entry = gopherState->entry;
  264.     if (gopherState->conversion == HTML_INDEX_PAGE) {
  265. storeAppendPrintf(entry,
  266.     "<HTML><HEAD><TITLE>Gopher Index %s</TITLE></HEAD>n"
  267.     "<BODY><H1>%s<BR>Gopher Search</H1>n"
  268.     "<p>This is a searchable Gopher index. Use the searchn"
  269.     "function of your browser to enter search terms.n"
  270.     "<ISINDEX></BODY></HTML>n",
  271.     storeUrl(entry), storeUrl(entry));
  272. /* now let start sending stuff to client */
  273. storeBufferFlush(entry);
  274. gopherState->data_in = 1;
  275. return;
  276.     }
  277.     if (gopherState->conversion == HTML_CSO_PAGE) {
  278. storeAppendPrintf(entry,
  279.     "<HTML><HEAD><TITLE>CSO Search of %s</TITLE></HEAD>n"
  280.     "<BODY><H1>%s<BR>CSO Search</H1>n"
  281.     "<P>A CSO database usually contains a phonebook orn"
  282.     "directory.  Use the search function of your browser to entern"
  283.     "search terms.</P><ISINDEX></BODY></HTML>n",
  284.     storeUrl(entry), storeUrl(entry));
  285. /* now let start sending stuff to client */
  286. storeBufferFlush(entry);
  287. gopherState->data_in = 1;
  288. return;
  289.     }
  290.     inbuf[len] = '';
  291.     if (!gopherState->HTML_header_added) {
  292. if (gopherState->conversion == HTML_CSO_RESULT)
  293.     strcat(outbuf, "<HTML><HEAD><TITLE>CSO Searchs Result</TITLE></HEAD>n"
  294. "<BODY><H1>CSO Searchs Result</H1>n<PRE>n");
  295. else
  296.     strcat(outbuf, "<HTML><HEAD><TITLE>Gopher Menu</TITLE></HEAD>n"
  297. "<BODY><H1>Gopher Menu</H1>n<PRE>n");
  298. gopherState->HTML_header_added = 1;
  299.     }
  300.     while ((pos != NULL) && (pos < inbuf + len)) {
  301. if (gopherState->len != 0) {
  302.     /* there is something left from last tx. */
  303.     xstrncpy(line, gopherState->buf, gopherState->len);
  304.     lpos = (char *) memccpy(line + gopherState->len, inbuf, 'n', len);
  305.     if (lpos)
  306. *lpos = '';
  307.     else {
  308. /* there is no complete line in inbuf */
  309. /* copy it to temp buffer */
  310. if (gopherState->len + len > TEMP_BUF_SIZE) {
  311.     debug(10, 1) ("GopherHTML: Buffer overflow. Lost some data on URL: %sn",
  312. storeUrl(entry));
  313.     len = TEMP_BUF_SIZE - gopherState->len;
  314. }
  315. xmemcpy(gopherState->buf + gopherState->len, inbuf, len);
  316. gopherState->len += len;
  317. return;
  318.     }
  319.     /* skip one line */
  320.     pos = (char *) memchr(pos, 'n', len);
  321.     if (pos)
  322. pos++;
  323.     /* we're done with the remain from last tx. */
  324.     gopherState->len = 0;
  325.     *(gopherState->buf) = '';
  326. } else {
  327.     lpos = (char *) memccpy(line, pos, 'n', len - (pos - inbuf));
  328.     if (lpos)
  329. *lpos = '';
  330.     else {
  331. /* there is no complete line in inbuf */
  332. /* copy it to temp buffer */
  333. if ((len - (pos - inbuf)) > TEMP_BUF_SIZE) {
  334.     debug(10, 1) ("GopherHTML: Buffer overflow. Lost some data on URL: %sn",
  335. storeUrl(entry));
  336.     len = TEMP_BUF_SIZE;
  337. }
  338. if (len > (pos - inbuf)) {
  339.     xmemcpy(gopherState->buf, pos, len - (pos - inbuf));
  340.     gopherState->len = len - (pos - inbuf);
  341. }
  342. break;
  343.     }
  344.     /* skip one line */
  345.     pos = (char *) memchr(pos, 'n', len);
  346.     if (pos)
  347. pos++;
  348. }
  349. /* at this point. We should have one line in buffer to process */
  350. if (*line == '.') {
  351.     /* skip it */
  352.     memset(line, '', TEMP_BUF_SIZE);
  353.     continue;
  354. }
  355. switch (gopherState->conversion) {
  356. case HTML_INDEX_RESULT:
  357. case HTML_DIR:{
  358. tline = line;
  359. gtype = *tline++;
  360. name = tline;
  361. selector = strchr(tline, TAB);
  362. if (selector) {
  363.     *selector++ = '';
  364.     host = strchr(selector, TAB);
  365.     if (host) {
  366. *host++ = '';
  367. port = strchr(host, TAB);
  368. if (port) {
  369.     char *junk;
  370.     port[0] = ':';
  371.     junk = strchr(host, TAB);
  372.     if (junk)
  373. *junk++ = 0; /* Chop port */
  374.     else {
  375. junk = strchr(host, 'r');
  376. if (junk)
  377.     *junk++ = 0; /* Chop port */
  378. else {
  379.     junk = strchr(host, 'n');
  380.     if (junk)
  381. *junk++ = 0; /* Chop port */
  382. }
  383.     }
  384.     if ((port[1] == '0') && (!port[2]))
  385. port[0] = 0; /* 0 means none */
  386. }
  387. /* escape a selector here */
  388. escaped_selector = xstrdup(rfc1738_escape(selector));
  389. switch (gtype) {
  390. case GOPHER_DIRECTORY:
  391.     icon_url = mimeGetIconURL("internal-menu");
  392.     break;
  393. case GOPHER_FILE:
  394.     icon_url = mimeGetIconURL("internal-text");
  395.     break;
  396. case GOPHER_INDEX:
  397. case GOPHER_CSO:
  398.     icon_url = mimeGetIconURL("internal-index");
  399.     break;
  400. case GOPHER_IMAGE:
  401. case GOPHER_GIF:
  402. case GOPHER_PLUS_IMAGE:
  403.     icon_url = mimeGetIconURL("internal-image");
  404.     break;
  405. case GOPHER_SOUND:
  406. case GOPHER_PLUS_SOUND:
  407.     icon_url = mimeGetIconURL("internal-sound");
  408.     break;
  409. case GOPHER_PLUS_MOVIE:
  410.     icon_url = mimeGetIconURL("internal-movie");
  411.     break;
  412. case GOPHER_TELNET:
  413. case GOPHER_3270:
  414.     icon_url = mimeGetIconURL("internal-telnet");
  415.     break;
  416. case GOPHER_BIN:
  417. case GOPHER_MACBINHEX:
  418. case GOPHER_DOSBIN:
  419. case GOPHER_UUENCODED:
  420.     icon_url = mimeGetIconURL("internal-binary");
  421.     break;
  422. default:
  423.     icon_url = mimeGetIconURL("internal-unknown");
  424.     break;
  425. }
  426. memset(tmpbuf, '', TEMP_BUF_SIZE);
  427. if ((gtype == GOPHER_TELNET) || (gtype == GOPHER_3270)) {
  428.     if (strlen(escaped_selector) != 0)
  429. snprintf(tmpbuf, TEMP_BUF_SIZE, "<IMG BORDER=0 SRC="%s"> <A HREF="telnet://%s@%s/">%s</A>n",
  430.     icon_url, escaped_selector, host, name);
  431.     else
  432. snprintf(tmpbuf, TEMP_BUF_SIZE, "<IMG BORDER=0 SRC="%s"> <A HREF="telnet://%s/">%s</A>n",
  433.     icon_url, host, name);
  434. } else {
  435.     snprintf(tmpbuf, TEMP_BUF_SIZE, "<IMG BORDER=0 SRC="%s"> <A HREF="gopher://%s/%c%s">%s</A>n",
  436. icon_url, host, gtype, escaped_selector, name);
  437. }
  438. safe_free(escaped_selector);
  439. strcat(outbuf, tmpbuf);
  440. gopherState->data_in = 1;
  441.     } else {
  442. memset(line, '', TEMP_BUF_SIZE);
  443. continue;
  444.     }
  445. } else {
  446.     memset(line, '', TEMP_BUF_SIZE);
  447.     continue;
  448. }
  449. break;
  450.     } /* HTML_DIR, HTML_INDEX_RESULT */
  451. case HTML_CSO_RESULT:{
  452. int t;
  453. int code;
  454. int recno;
  455. LOCAL_ARRAY(char, result, MAX_CSO_RESULT);
  456. tline = line;
  457. if (tline[0] == '-') {
  458.     t = sscanf(tline, "-%d:%d:%[^n]", &code, &recno, result);
  459.     if (t < 3)
  460. break;
  461.     if (code != 200)
  462. break;
  463.     if (gopherState->cso_recno != recno) {
  464. snprintf(tmpbuf, TEMP_BUF_SIZE, "</PRE><HR><H2>Record# %d<br><i>%s</i></H2>n<PRE>", recno, result);
  465. gopherState->cso_recno = recno;
  466.     } else {
  467. snprintf(tmpbuf, TEMP_BUF_SIZE, "%sn", result);
  468.     }
  469.     strcat(outbuf, tmpbuf);
  470.     gopherState->data_in = 1;
  471.     break;
  472. } else {
  473.     /* handle some error codes */
  474.     t = sscanf(tline, "%d:%[^n]", &code, result);
  475.     if (t < 2)
  476. break;
  477.     switch (code) {
  478.     case 200:{
  479.     /* OK */
  480.     /* Do nothing here */
  481.     break;
  482. }
  483.     case 102: /* Number of matches */
  484.     case 501: /* No Match */
  485.     case 502: /* Too Many Matches */
  486. {
  487.     /* Print the message the server returns */
  488.     snprintf(tmpbuf, TEMP_BUF_SIZE, "</PRE><HR><H2>%s</H2>n<PRE>", result);
  489.     strcat(outbuf, tmpbuf);
  490.     gopherState->data_in = 1;
  491.     break;
  492. }
  493.     }
  494. }
  495.     } /* HTML_CSO_RESULT */
  496. default:
  497.     break; /* do nothing */
  498. } /* switch */
  499.     } /* while loop */
  500.     if ((int) strlen(outbuf) > 0) {
  501. storeAppend(entry, outbuf, strlen(outbuf));
  502. /* now let start sending stuff to client */
  503. storeBufferFlush(entry);
  504.     }
  505.     return;
  506. }
  507. static void
  508. gopherTimeout(int fd, void *data)
  509. {
  510.     GopherStateData *gopherState = data;
  511.     StoreEntry *entry = gopherState->entry;
  512.     debug(10, 4) ("gopherTimeout: FD %d: '%s'n", fd, storeUrl(entry));
  513.     if (entry->store_status == STORE_PENDING) {
  514. if (entry->mem_obj->inmem_hi == 0) {
  515.     fwdFail(gopherState->fwdState,
  516. errorCon(ERR_READ_TIMEOUT, HTTP_GATEWAY_TIMEOUT));
  517. }
  518.     }
  519.     comm_close(fd);
  520. }
  521. /* This will be called when data is ready to be read from fd.  Read until
  522.  * error or connection closed. */
  523. static void
  524. gopherReadReply(int fd, void *data)
  525. {
  526.     GopherStateData *gopherState = data;
  527.     StoreEntry *entry = gopherState->entry;
  528.     char *buf = NULL;
  529.     int len;
  530.     int clen;
  531.     int bin;
  532.     size_t read_sz;
  533. #if DELAY_POOLS
  534.     delay_id delay_id = delayMostBytesAllowed(entry->mem_obj);
  535. #endif
  536.     if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
  537. comm_close(fd);
  538. return;
  539.     }
  540.     errno = 0;
  541.     buf = memAllocate(MEM_4K_BUF);
  542.     read_sz = 4096 - 1; /* leave room for termination */
  543. #if DELAY_POOLS
  544.     read_sz = delayBytesWanted(delay_id, 1, read_sz);
  545. #endif
  546.     /* leave one space for  in gopherToHTML */
  547.     Counter.syscalls.sock.reads++;
  548.     len = read(fd, buf, read_sz);
  549.     if (len > 0) {
  550. fd_bytes(fd, len, FD_READ);
  551. #if DELAY_POOLS
  552. delayBytesIn(delay_id, len);
  553. #endif
  554. kb_incr(&Counter.server.all.kbytes_in, len);
  555. kb_incr(&Counter.server.other.kbytes_in, len);
  556.     }
  557.     debug(10, 5) ("gopherReadReply: FD %d read len=%dn", fd, len);
  558.     if (len > 0) {
  559. commSetTimeout(fd, Config.Timeout.read, NULL, NULL);
  560. IOStats.Gopher.reads++;
  561. for (clen = len - 1, bin = 0; clen; bin++)
  562.     clen >>= 1;
  563. IOStats.Gopher.read_hist[bin]++;
  564.     }
  565.     if (len < 0) {
  566. debug(50, 1) ("gopherReadReply: error reading: %sn", xstrerror());
  567. if (ignoreErrno(errno)) {
  568.     commSetSelect(fd, COMM_SELECT_READ, gopherReadReply, data, 0);
  569. } else if (entry->mem_obj->inmem_hi == 0) {
  570.     ErrorState *err;
  571.     err = errorCon(ERR_READ_ERROR, HTTP_INTERNAL_SERVER_ERROR);
  572.     err->xerrno = errno;
  573.     err->url = xstrdup(storeUrl(entry));
  574.     errorAppendEntry(entry, err);
  575.     comm_close(fd);
  576. } else {
  577.     comm_close(fd);
  578. }
  579.     } else if (len == 0 && entry->mem_obj->inmem_hi == 0) {
  580. ErrorState *err;
  581. err = errorCon(ERR_ZERO_SIZE_OBJECT, HTTP_SERVICE_UNAVAILABLE);
  582. err->xerrno = errno;
  583. err->url = xstrdup(gopherState->request);
  584. errorAppendEntry(entry, err);
  585. comm_close(fd);
  586.     } else if (len == 0) {
  587. /* Connection closed; retrieval done. */
  588. /* flush the rest of data in temp buf if there is one. */
  589. if (gopherState->conversion != NORMAL)
  590.     gopherEndHTML(data);
  591. storeTimestampsSet(entry);
  592. storeBufferFlush(entry);
  593. fwdComplete(gopherState->fwdState);
  594. comm_close(fd);
  595.     } else {
  596. if (gopherState->conversion != NORMAL) {
  597.     gopherToHTML(data, buf, len);
  598. } else {
  599.     storeAppend(entry, buf, len);
  600. }
  601. commSetSelect(fd,
  602.     COMM_SELECT_READ,
  603.     gopherReadReply,
  604.     data, 0);
  605.     }
  606.     memFree(buf, MEM_4K_BUF);
  607.     return;
  608. }
  609. /* This will be called when request write is complete. Schedule read of
  610.  * reply. */
  611. static void
  612. gopherSendComplete(int fd, char *buf, size_t size, int errflag, void *data)
  613. {
  614.     GopherStateData *gopherState = (GopherStateData *) data;
  615.     StoreEntry *entry = gopherState->entry;
  616.     debug(10, 5) ("gopherSendComplete: FD %d size: %d errflag: %dn",
  617. fd, size, errflag);
  618.     if (size > 0) {
  619. fd_bytes(fd, size, FD_WRITE);
  620. kb_incr(&Counter.server.all.kbytes_out, size);
  621. kb_incr(&Counter.server.other.kbytes_out, size);
  622.     }
  623.     if (errflag) {
  624. ErrorState *err;
  625. err = errorCon(ERR_CONNECT_FAIL, HTTP_SERVICE_UNAVAILABLE);
  626. err->xerrno = errno;
  627. err->host = xstrdup(gopherState->host);
  628. err->port = gopherState->port;
  629. err->url = xstrdup(storeUrl(entry));
  630. errorAppendEntry(entry, err);
  631. comm_close(fd);
  632. if (buf)
  633.     memFree(buf, MEM_4K_BUF); /* Allocated by gopherSendRequest. */
  634. return;
  635.     }
  636.     /* 
  637.      * OK. We successfully reach remote site.  Start MIME typing
  638.      * stuff.  Do it anyway even though request is not HTML type.
  639.      */
  640.     gopherMimeCreate(gopherState);
  641.     switch (gopherState->type_id) {
  642.     case GOPHER_DIRECTORY:
  643. /* we got to convert it first */
  644. storeBuffer(entry);
  645. gopherState->conversion = HTML_DIR;
  646. gopherState->HTML_header_added = 0;
  647. break;
  648.     case GOPHER_INDEX:
  649. /* we got to convert it first */
  650. storeBuffer(entry);
  651. gopherState->conversion = HTML_INDEX_RESULT;
  652. gopherState->HTML_header_added = 0;
  653. break;
  654.     case GOPHER_CSO:
  655. /* we got to convert it first */
  656. storeBuffer(entry);
  657. gopherState->conversion = HTML_CSO_RESULT;
  658. gopherState->cso_recno = 0;
  659. gopherState->HTML_header_added = 0;
  660. break;
  661.     default:
  662. gopherState->conversion = NORMAL;
  663.     }
  664.     /* Schedule read reply. */
  665.     commSetSelect(fd, COMM_SELECT_READ, gopherReadReply, gopherState, 0);
  666.     commSetDefer(fd, fwdCheckDeferRead, entry);
  667.     if (buf)
  668. memFree(buf, MEM_4K_BUF); /* Allocated by gopherSendRequest. */
  669. }
  670. /* This will be called when connect completes. Write request. */
  671. static void
  672. gopherSendRequest(int fd, void *data)
  673. {
  674.     GopherStateData *gopherState = data;
  675.     LOCAL_ARRAY(char, query, MAX_URL);
  676.     char *buf = memAllocate(MEM_4K_BUF);
  677.     char *t;
  678.     if (gopherState->type_id == GOPHER_CSO) {
  679. sscanf(gopherState->request, "?%s", query);
  680. snprintf(buf, 4096, "query %srnquitrn", query);
  681.     } else if (gopherState->type_id == GOPHER_INDEX) {
  682. if ((t = strchr(gopherState->request, '?')))
  683.     *t = 't';
  684. snprintf(buf, 4096, "%srn", gopherState->request);
  685.     } else {
  686. snprintf(buf, 4096, "%srn", gopherState->request);
  687.     }
  688.     debug(10, 5) ("gopherSendRequest: FD %dn", fd);
  689.     comm_write(fd,
  690. buf,
  691. strlen(buf),
  692. gopherSendComplete,
  693. data,
  694. memFree4K);
  695.     if (EBIT_TEST(gopherState->entry->flags, ENTRY_CACHABLE))
  696. storeSetPublicKey(gopherState->entry); /* Make it public */
  697. }
  698. void
  699. gopherStart(FwdState * fwdState)
  700. {
  701.     int fd = fwdState->server_fd;
  702.     StoreEntry *entry = fwdState->entry;
  703.     GopherStateData *gopherState = CreateGopherStateData();
  704.     storeLockObject(entry);
  705.     gopherState->entry = entry;
  706.     debug(10, 3) ("gopherStart: %sn", storeUrl(entry));
  707.     Counter.server.all.requests++;
  708.     Counter.server.other.requests++;
  709.     /* Parse url. */
  710.     if (gopher_url_parser(storeUrl(entry), gopherState->host, &gopherState->port,
  711.     &gopherState->type_id, gopherState->request)) {
  712. ErrorState *err;
  713. err = errorCon(ERR_INVALID_URL, HTTP_BAD_REQUEST);
  714. err->url = xstrdup(storeUrl(entry));
  715. errorAppendEntry(entry, err);
  716. gopherStateFree(-1, gopherState);
  717. return;
  718.     }
  719.     comm_add_close_handler(fd, gopherStateFree, gopherState);
  720.     if (((gopherState->type_id == GOPHER_INDEX) || (gopherState->type_id == GOPHER_CSO))
  721. && (strchr(gopherState->request, '?') == NULL)) {
  722. /* Index URL without query word */
  723. /* We have to generate search page back to client. No need for connection */
  724. gopherMimeCreate(gopherState);
  725. if (gopherState->type_id == GOPHER_INDEX) {
  726.     gopherState->conversion = HTML_INDEX_PAGE;
  727. } else {
  728.     if (gopherState->type_id == GOPHER_CSO) {
  729. gopherState->conversion = HTML_CSO_PAGE;
  730.     } else {
  731. gopherState->conversion = HTML_INDEX_PAGE;
  732.     }
  733. }
  734. gopherToHTML(gopherState, (char *) NULL, 0);
  735. fwdComplete(fwdState);
  736. comm_close(fd);
  737. return;
  738.     }
  739.     gopherState->fd = fd;
  740.     gopherState->fwdState = fwdState;
  741.     commSetSelect(fd, COMM_SELECT_WRITE, gopherSendRequest, gopherState, 0);
  742.     commSetTimeout(fd, Config.Timeout.read, gopherTimeout, gopherState);
  743. }
  744. static GopherStateData *
  745. CreateGopherStateData(void)
  746. {
  747.     GopherStateData *gd = xcalloc(1, sizeof(GopherStateData));
  748.     cbdataAdd(gd, cbdataXfree, 0);
  749.     gd->buf = memAllocate(MEM_4K_BUF);
  750.     return (gd);
  751. }