gopher.cc
Go to the documentation of this file.
1 /*
2  * Copyright (C) 1996-2019 The Squid Software Foundation and contributors
3  *
4  * Squid software is distributed under GPLv2+ license and includes
5  * contributions from numerous individuals and organizations.
6  * Please see the COPYING and CONTRIBUTORS files for details.
7  */
8 
9 /* DEBUG: section 10 Gopher */
10 
11 #include "squid.h"
12 #include "comm.h"
13 #include "comm/Read.h"
14 #include "comm/Write.h"
15 #include "errorpage.h"
16 #include "fd.h"
17 #include "FwdState.h"
18 #include "globals.h"
19 #include "html_quote.h"
20 #include "HttpReply.h"
21 #include "HttpRequest.h"
22 #include "MemBuf.h"
23 #include "mime.h"
24 #include "parser/Tokenizer.h"
25 #include "rfc1738.h"
26 #include "SquidConfig.h"
27 #include "SquidTime.h"
28 #include "StatCounters.h"
29 #include "Store.h"
30 #include "tools.h"
31 
32 #if USE_DELAY_POOLS
33 #include "DelayPools.h"
34 #include "MemObject.h"
35 #endif
36 
37 /* gopher type code from rfc. Anawat. */
38 #define GOPHER_FILE '0'
39 #define GOPHER_DIRECTORY '1'
40 #define GOPHER_CSO '2'
41 #define GOPHER_ERROR '3'
42 #define GOPHER_MACBINHEX '4'
43 #define GOPHER_DOSBIN '5'
44 #define GOPHER_UUENCODED '6'
45 #define GOPHER_INDEX '7'
46 #define GOPHER_TELNET '8'
47 #define GOPHER_BIN '9'
48 #define GOPHER_REDUNT '+'
49 #define GOPHER_3270 'T'
50 #define GOPHER_GIF 'g'
51 #define GOPHER_IMAGE 'I'
52 
53 #define GOPHER_HTML 'h'
54 #define GOPHER_INFO 'i'
55 
57 #define GOPHER_WWW 'w'
58 #define GOPHER_SOUND 's'
59 
60 #define GOPHER_PLUS_IMAGE ':'
61 #define GOPHER_PLUS_MOVIE ';'
62 #define GOPHER_PLUS_SOUND '<'
63 
64 #define GOPHER_PORT 70
65 
66 #define TAB '\t'
67 
68 // TODO CODE: should this be a protocol-specific thing?
69 #define TEMP_BUF_SIZE 4096
70 
71 #define MAX_CSO_RESULT 1024
72 
80 {
82 
83 public:
85  entry(aFwd->entry),
88  HTML_pre(0),
89  type_id(GOPHER_FILE /* '0' */),
90  cso_recno(0),
91  len(0),
92  buf(NULL),
93  fwd(aFwd)
94  {
95  *request = 0;
96  buf = (char *)memAllocate(MEM_4K_BUF);
97  entry->lock("gopherState");
98  *replybuf = 0;
99  }
101 
102  /* AsyncJob API emulated */
103  void deleteThis(const char *aReason);
104  void swanSong();
105 
106 public:
108  enum {
115  } conversion;
117  int HTML_pre;
118  char type_id;
121  int len;
122  char *buf; /* pts to a 4k page */
127 };
128 
130 
132 static void gopherMimeCreate(GopherStateData *);
133 static void gopher_request_parse(const HttpRequest * req,
134  char *type_id,
135  char *request);
136 static void gopherEndHTML(GopherStateData *);
137 static void gopherToHTML(GopherStateData *, char *inbuf, int len);
142 
143 static char def_gopher_bin[] = "www/unknown";
144 
145 static char def_gopher_text[] = "text/plain";
146 
147 static void
149 {
150  GopherStateData *gopherState = (GopherStateData *)params.data;
151 
152  if (gopherState == NULL)
153  return;
154 
155  gopherState->deleteThis("gopherStateFree");
156 }
157 
158 void
160 {
161  swanSong();
162  delete this;
163 }
164 
165 void
167 {
168  if (entry)
169  entry->unlock("gopherState");
170 
171  if (buf) {
173  buf = nullptr;
174  }
175 }
176 
180 static void
182 {
183  StoreEntry *entry = gopherState->entry;
184  const char *mime_type = NULL;
185  const char *mime_enc = NULL;
186 
187  switch (gopherState->type_id) {
188 
189  case GOPHER_DIRECTORY:
190 
191  case GOPHER_INDEX:
192 
193  case GOPHER_HTML:
194 
195  case GOPHER_WWW:
196 
197  case GOPHER_CSO:
198  mime_type = "text/html";
199  break;
200 
201  case GOPHER_GIF:
202 
203  case GOPHER_IMAGE:
204 
205  case GOPHER_PLUS_IMAGE:
206  mime_type = "image/gif";
207  break;
208 
209  case GOPHER_SOUND:
210 
211  case GOPHER_PLUS_SOUND:
212  mime_type = "audio/basic";
213  break;
214 
215  case GOPHER_PLUS_MOVIE:
216  mime_type = "video/mpeg";
217  break;
218 
219  case GOPHER_MACBINHEX:
220 
221  case GOPHER_DOSBIN:
222 
223  case GOPHER_UUENCODED:
224 
225  case GOPHER_BIN:
226  /* Rightnow We have no idea what it is. */
227  mime_enc = mimeGetContentEncoding(gopherState->request);
228  mime_type = mimeGetContentType(gopherState->request);
229  if (!mime_type)
230  mime_type = def_gopher_bin;
231  break;
232 
233  case GOPHER_FILE:
234 
235  default:
236  mime_enc = mimeGetContentEncoding(gopherState->request);
237  mime_type = mimeGetContentType(gopherState->request);
238  if (!mime_type)
239  mime_type = def_gopher_text;
240  break;
241  }
242 
243  assert(entry->isEmpty());
244 
245  HttpReply *reply = new HttpReply;
246  entry->buffer();
247  reply->setHeaders(Http::scOkay, "Gatewaying", mime_type, -1, -1, -2);
248  if (mime_enc)
249  reply->header.putStr(Http::HdrType::CONTENT_ENCODING, mime_enc);
250 
251  entry->replaceHttpReply(reply);
252  gopherState->reply_ = reply;
253 }
254 
258 static void
260 {
262 
263  if (request)
264  *request = 0;
265 
266  tok.skip('/'); // ignore failures? path could be ab-empty
267 
268  if (tok.atEnd()) {
269  *type_id = GOPHER_DIRECTORY;
270  return;
271  }
272 
273  static const CharacterSet anyByte("UTF-8",0x00, 0xFF);
274 
275  SBuf typeId;
276  (void)tok.prefix(typeId, anyByte, 1); // never fails since !atEnd()
277  *type_id = typeId[0];
278 
279  if (request) {
280  SBufToCstring(request, tok.remaining().substr(0, MAX_URL-1));
281  /* convert %xx to char */
282  rfc1738_unescape(request);
283  }
284 }
285 
293 int
295 {
296  int cachable = 1;
297  char type_id;
298  /* parse to see type */
300  &type_id,
301  NULL);
302 
303  switch (type_id) {
304 
305  case GOPHER_INDEX:
306 
307  case GOPHER_CSO:
308 
309  case GOPHER_TELNET:
310 
311  case GOPHER_3270:
312  cachable = 0;
313  break;
314 
315  default:
316  cachable = 1;
317  }
318 
319  return cachable;
320 }
321 
322 static void
323 gopherHTMLHeader(StoreEntry * e, const char *title, const char *substring)
324 {
325  storeAppendPrintf(e, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
326  storeAppendPrintf(e, "<HTML><HEAD><TITLE>");
327  storeAppendPrintf(e, title, substring);
328  storeAppendPrintf(e, "</TITLE>");
329  storeAppendPrintf(e, "<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE>\n");
330  storeAppendPrintf(e, "</HEAD>\n<BODY><H1>");
331  storeAppendPrintf(e, title, substring);
332  storeAppendPrintf(e, "</H1>\n");
333 }
334 
335 static void
337 {
338  storeAppendPrintf(e, "<HR noshade size=\"1px\">\n");
339  storeAppendPrintf(e, "<ADDRESS>\n");
340  storeAppendPrintf(e, "Generated %s by %s (%s)\n",
342  getMyHostname(),
344  storeAppendPrintf(e, "</ADDRESS></BODY></HTML>\n");
345 }
346 
347 static void
349 {
350  StoreEntry *e = gopherState->entry;
351 
352  if (!gopherState->HTML_header_added) {
353  gopherHTMLHeader(e, "Server Return Nothing", NULL);
354  storeAppendPrintf(e, "<P>The Gopher query resulted in a blank response</P>");
355  } else if (gopherState->HTML_pre) {
356  storeAppendPrintf(e, "</PRE>\n");
357  }
358 
359  gopherHTMLFooter(e);
360 }
361 
367 static void
368 gopherToHTML(GopherStateData * gopherState, char *inbuf, int len)
369 {
370  char *pos = inbuf;
371  char *lpos = NULL;
372  char *tline = NULL;
373  LOCAL_ARRAY(char, line, TEMP_BUF_SIZE);
374  LOCAL_ARRAY(char, tmpbuf, TEMP_BUF_SIZE);
375  char *name = NULL;
376  char *selector = NULL;
377  char *host = NULL;
378  char *port = NULL;
379  char *escaped_selector = NULL;
380  const char *icon_url = NULL;
381  char gtype;
382  StoreEntry *entry = NULL;
383 
384  memset(tmpbuf, '\0', TEMP_BUF_SIZE);
385  memset(line, '\0', TEMP_BUF_SIZE);
386 
387  entry = gopherState->entry;
388 
389  if (gopherState->conversion == GopherStateData::HTML_INDEX_PAGE) {
390  char *html_url = html_quote(entry->url());
391  gopherHTMLHeader(entry, "Gopher Index %s", html_url);
392  storeAppendPrintf(entry,
393  "<p>This is a searchable Gopher index. Use the search\n"
394  "function of your browser to enter search terms.\n"
395  "<ISINDEX>\n");
396  gopherHTMLFooter(entry);
397  /* now let start sending stuff to client */
398  entry->flush();
399  gopherState->HTML_header_added = 1;
400 
401  return;
402  }
403 
404  if (gopherState->conversion == GopherStateData::HTML_CSO_PAGE) {
405  char *html_url = html_quote(entry->url());
406  gopherHTMLHeader(entry, "CSO Search of %s", html_url);
407  storeAppendPrintf(entry,
408  "<P>A CSO database usually contains a phonebook or\n"
409  "directory. Use the search function of your browser to enter\n"
410  "search terms.</P><ISINDEX>\n");
411  gopherHTMLFooter(entry);
412  /* now let start sending stuff to client */
413  entry->flush();
414  gopherState->HTML_header_added = 1;
415 
416  return;
417  }
418 
419  String outbuf;
420 
421  if (!gopherState->HTML_header_added) {
422  if (gopherState->conversion == GopherStateData::HTML_CSO_RESULT)
423  gopherHTMLHeader(entry, "CSO Search Result", NULL);
424  else
425  gopherHTMLHeader(entry, "Gopher Menu", NULL);
426 
427  outbuf.append ("<PRE>");
428 
429  gopherState->HTML_header_added = 1;
430 
431  gopherState->HTML_pre = 1;
432  }
433 
434  while (pos < inbuf + len) {
435  int llen;
436  int left = len - (pos - inbuf);
437  lpos = (char *)memchr(pos, '\n', left);
438  if (lpos) {
439  ++lpos; /* Next line is after \n */
440  llen = lpos - pos;
441  } else {
442  llen = left;
443  }
444  if (gopherState->len + llen >= TEMP_BUF_SIZE) {
445  debugs(10, DBG_IMPORTANT, "GopherHTML: Buffer overflow. Lost some data on URL: " << entry->url() );
446  llen = TEMP_BUF_SIZE - gopherState->len - 1;
447  }
448  if (!lpos) {
449  /* there is no complete line in inbuf */
450  /* copy it to temp buffer */
451  /* note: llen is adjusted above */
452  memcpy(gopherState->buf + gopherState->len, pos, llen);
453  gopherState->len += llen;
454  break;
455  }
456  if (gopherState->len != 0) {
457  /* there is something left from last tx. */
458  memcpy(line, gopherState->buf, gopherState->len);
459  memcpy(line + gopherState->len, pos, llen);
460  llen += gopherState->len;
461  gopherState->len = 0;
462  } else {
463  memcpy(line, pos, llen);
464  }
465  line[llen + 1] = '\0';
466  /* move input to next line */
467  pos = lpos;
468 
469  /* at this point. We should have one line in buffer to process */
470 
471  if (*line == '.') {
472  /* skip it */
473  memset(line, '\0', TEMP_BUF_SIZE);
474  continue;
475  }
476 
477  switch (gopherState->conversion) {
478 
480 
482  tline = line;
483  gtype = *tline;
484  ++tline;
485  name = tline;
486  selector = strchr(tline, TAB);
487 
488  if (selector) {
489  *selector = '\0';
490  ++selector;
491  host = strchr(selector, TAB);
492 
493  if (host) {
494  *host = '\0';
495  ++host;
496  port = strchr(host, TAB);
497 
498  if (port) {
499  char *junk;
500  port[0] = ':';
501  junk = strchr(host, TAB);
502 
503  if (junk)
504  *junk++ = 0; /* Chop port */
505  else {
506  junk = strchr(host, '\r');
507 
508  if (junk)
509  *junk++ = 0; /* Chop port */
510  else {
511  junk = strchr(host, '\n');
512 
513  if (junk)
514  *junk++ = 0; /* Chop port */
515  }
516  }
517 
518  if ((port[1] == '0') && (!port[2]))
519  port[0] = 0; /* 0 means none */
520  }
521 
522  /* escape a selector here */
523  escaped_selector = xstrdup(rfc1738_escape_part(selector));
524 
525  switch (gtype) {
526 
527  case GOPHER_DIRECTORY:
528  icon_url = mimeGetIconURL("internal-menu");
529  break;
530 
531  case GOPHER_HTML:
532 
533  case GOPHER_FILE:
534  icon_url = mimeGetIconURL("internal-text");
535  break;
536 
537  case GOPHER_INDEX:
538 
539  case GOPHER_CSO:
540  icon_url = mimeGetIconURL("internal-index");
541  break;
542 
543  case GOPHER_IMAGE:
544 
545  case GOPHER_GIF:
546 
547  case GOPHER_PLUS_IMAGE:
548  icon_url = mimeGetIconURL("internal-image");
549  break;
550 
551  case GOPHER_SOUND:
552 
553  case GOPHER_PLUS_SOUND:
554  icon_url = mimeGetIconURL("internal-sound");
555  break;
556 
557  case GOPHER_PLUS_MOVIE:
558  icon_url = mimeGetIconURL("internal-movie");
559  break;
560 
561  case GOPHER_TELNET:
562 
563  case GOPHER_3270:
564  icon_url = mimeGetIconURL("internal-telnet");
565  break;
566 
567  case GOPHER_BIN:
568 
569  case GOPHER_MACBINHEX:
570 
571  case GOPHER_DOSBIN:
572 
573  case GOPHER_UUENCODED:
574  icon_url = mimeGetIconURL("internal-binary");
575  break;
576 
577  case GOPHER_INFO:
578  icon_url = NULL;
579  break;
580 
581  default:
582  icon_url = mimeGetIconURL("internal-unknown");
583  break;
584  }
585 
586  memset(tmpbuf, '\0', TEMP_BUF_SIZE);
587 
588  if ((gtype == GOPHER_TELNET) || (gtype == GOPHER_3270)) {
589  if (strlen(escaped_selector) != 0)
590  snprintf(tmpbuf, TEMP_BUF_SIZE, "<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"telnet://%s@%s%s%s/\">%s</A>\n",
591  icon_url, escaped_selector, rfc1738_escape_part(host),
592  *port ? ":" : "", port, html_quote(name));
593  else
594  snprintf(tmpbuf, TEMP_BUF_SIZE, "<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"telnet://%s%s%s/\">%s</A>\n",
595  icon_url, rfc1738_escape_part(host), *port ? ":" : "",
596  port, html_quote(name));
597 
598  } else if (gtype == GOPHER_INFO) {
599  snprintf(tmpbuf, TEMP_BUF_SIZE, "\t%s\n", html_quote(name));
600  } else {
601  if (strncmp(selector, "GET /", 5) == 0) {
602  /* WWW link */
603  snprintf(tmpbuf, TEMP_BUF_SIZE, "<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"http://%s/%s\">%s</A>\n",
604  icon_url, host, rfc1738_escape_unescaped(selector + 5), html_quote(name));
605  } else {
606  /* Standard link */
607  snprintf(tmpbuf, TEMP_BUF_SIZE, "<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"gopher://%s/%c%s\">%s</A>\n",
608  icon_url, host, gtype, escaped_selector, html_quote(name));
609  }
610  }
611 
612  safe_free(escaped_selector);
613  outbuf.append(tmpbuf);
614  } else {
615  memset(line, '\0', TEMP_BUF_SIZE);
616  continue;
617  }
618  } else {
619  memset(line, '\0', TEMP_BUF_SIZE);
620  continue;
621  }
622 
623  break;
624  } /* HTML_DIR, HTML_INDEX_RESULT */
625 
627  if (line[0] == '-') {
628  int code, recno;
629  char *s_code, *s_recno, *result;
630 
631  s_code = strtok(line + 1, ":\n");
632  s_recno = strtok(NULL, ":\n");
633  result = strtok(NULL, "\n");
634 
635  if (!result)
636  break;
637 
638  code = atoi(s_code);
639 
640  recno = atoi(s_recno);
641 
642  if (code != 200)
643  break;
644 
645  if (gopherState->cso_recno != recno) {
646  snprintf(tmpbuf, TEMP_BUF_SIZE, "</PRE><HR noshade size=\"1px\"><H2>Record# %d<br><i>%s</i></H2>\n<PRE>", recno, html_quote(result));
647  gopherState->cso_recno = recno;
648  } else {
649  snprintf(tmpbuf, TEMP_BUF_SIZE, "%s\n", html_quote(result));
650  }
651 
652  outbuf.append(tmpbuf);
653  break;
654  } else {
655  int code;
656  char *s_code, *result;
657 
658  s_code = strtok(line, ":");
659  result = strtok(NULL, "\n");
660 
661  if (!result)
662  break;
663 
664  code = atoi(s_code);
665 
666  switch (code) {
667 
668  case 200: {
669  /* OK */
670  /* Do nothing here */
671  break;
672  }
673 
674  case 102: /* Number of matches */
675 
676  case 501: /* No Match */
677 
678  case 502: { /* Too Many Matches */
679  /* Print the message the server returns */
680  snprintf(tmpbuf, TEMP_BUF_SIZE, "</PRE><HR noshade size=\"1px\"><H2>%s</H2>\n<PRE>", html_quote(result));
681  outbuf.append(tmpbuf);
682  break;
683  }
684 
685  }
686  }
687 
688  } /* HTML_CSO_RESULT */
689 
690  default:
691  break; /* do nothing */
692 
693  } /* switch */
694 
695  } /* while loop */
696 
697  if (outbuf.size() > 0) {
698  entry->append(outbuf.rawBuf(), outbuf.size());
699  /* now let start sending stuff to client */
700  entry->flush();
701  }
702 
703  outbuf.clean();
704  return;
705 }
706 
707 static void
709 {
710  GopherStateData *gopherState = static_cast<GopherStateData *>(io.data);
711  debugs(10, 4, HERE << io.conn << ": '" << gopherState->entry->url() << "'" );
712 
713  gopherState->fwd->fail(new ErrorState(ERR_READ_TIMEOUT, Http::scGatewayTimeout, gopherState->fwd->request, gopherState->fwd->al));
714 
715  if (Comm::IsConnOpen(io.conn))
716  io.conn->close();
717 }
718 
723 static void
724 gopherReadReply(const Comm::ConnectionPointer &conn, char *buf, size_t len, Comm::Flag flag, int xerrno, void *data)
725 {
726  GopherStateData *gopherState = (GopherStateData *)data;
727  StoreEntry *entry = gopherState->entry;
728  int clen;
729  int bin;
730  size_t read_sz = BUFSIZ;
731 #if USE_DELAY_POOLS
732  DelayId delayId = entry->mem_obj->mostBytesAllowed();
733 #endif
734 
735  /* Bail out early on Comm::ERR_CLOSING - close handlers will tidy up for us */
736 
737  if (flag == Comm::ERR_CLOSING) {
738  return;
739  }
740 
741  assert(buf == gopherState->replybuf);
742 
743  if (EBIT_TEST(entry->flags, ENTRY_ABORTED)) {
744  gopherState->serverConn->close();
745  return;
746  }
747 
748 #if USE_DELAY_POOLS
749  read_sz = delayId.bytesWanted(1, read_sz);
750 #endif
751 
752  /* leave one space for \0 in gopherToHTML */
753 
754  if (flag == Comm::OK && len > 0) {
755 #if USE_DELAY_POOLS
756  delayId.bytesIn(len);
757 #endif
758 
759  statCounter.server.all.kbytes_in += len;
760  statCounter.server.other.kbytes_in += len;
761  }
762 
763  debugs(10, 5, HERE << conn << " read len=" << len);
764 
765  if (flag == Comm::OK && len > 0) {
768  ++IOStats.Gopher.reads;
769 
770  for (clen = len - 1, bin = 0; clen; ++bin)
771  clen >>= 1;
772 
773  ++IOStats.Gopher.read_hist[bin];
774 
775  HttpRequest *req = gopherState->fwd->request;
776  if (req->hier.bodyBytesRead < 0) {
777  req->hier.bodyBytesRead = 0;
778  // first bytes read, update Reply flags:
779  gopherState->reply_->sources |= Http::Message::srcGopher;
780  }
781 
782  req->hier.bodyBytesRead += len;
783  }
784 
785  if (flag != Comm::OK) {
786  debugs(50, DBG_IMPORTANT, MYNAME << "error reading: " << xstrerr(xerrno));
787 
788  if (ignoreErrno(xerrno)) {
789  AsyncCall::Pointer call = commCbCall(5,4, "gopherReadReply",
790  CommIoCbPtrFun(gopherReadReply, gopherState));
791  comm_read(conn, buf, read_sz, call);
792  } else {
793  const auto err = new ErrorState(ERR_READ_ERROR, Http::scInternalServerError, gopherState->fwd->request, gopherState->fwd->al);
794  err->xerrno = xerrno;
795  gopherState->fwd->fail(err);
796  gopherState->serverConn->close();
797  }
798  } else if (len == 0 && entry->isEmpty()) {
799  gopherState->fwd->fail(new ErrorState(ERR_ZERO_SIZE_OBJECT, Http::scServiceUnavailable, gopherState->fwd->request, gopherState->fwd->al));
800  gopherState->serverConn->close();
801  } else if (len == 0) {
802  /* Connection closed; retrieval done. */
803  /* flush the rest of data in temp buf if there is one. */
804 
805  if (gopherState->conversion != GopherStateData::NORMAL)
806  gopherEndHTML(gopherState);
807 
808  entry->timestampsSet();
809  entry->flush();
810  gopherState->fwd->complete();
811  gopherState->serverConn->close();
812  } else {
813  if (gopherState->conversion != GopherStateData::NORMAL) {
814  gopherToHTML(gopherState, buf, len);
815  } else {
816  entry->append(buf, len);
817  }
818  AsyncCall::Pointer call = commCbCall(5,4, "gopherReadReply",
819  CommIoCbPtrFun(gopherReadReply, gopherState));
820  comm_read(conn, buf, read_sz, call);
821  }
822 }
823 
827 static void
828 gopherSendComplete(const Comm::ConnectionPointer &conn, char *, size_t size, Comm::Flag errflag, int xerrno, void *data)
829 {
830  GopherStateData *gopherState = (GopherStateData *) data;
831  StoreEntry *entry = gopherState->entry;
832  debugs(10, 5, HERE << conn << " size: " << size << " errflag: " << errflag);
833 
834  if (size > 0) {
835  fd_bytes(conn->fd, size, FD_WRITE);
836  statCounter.server.all.kbytes_out += size;
837  statCounter.server.other.kbytes_out += size;
838  }
839 
840  if (errflag) {
841  const auto err = new ErrorState(ERR_WRITE_ERROR, Http::scServiceUnavailable, gopherState->fwd->request, gopherState->fwd->al);
842  err->xerrno = xerrno;
843  err->port = gopherState->fwd->request->url.port();
844  err->url = xstrdup(entry->url());
845  gopherState->fwd->fail(err);
846  gopherState->serverConn->close();
847  return;
848  }
849 
850  /*
851  * OK. We successfully reach remote site. Start MIME typing
852  * stuff. Do it anyway even though request is not HTML type.
853  */
854  entry->buffer();
855 
856  gopherMimeCreate(gopherState);
857 
858  switch (gopherState->type_id) {
859 
860  case GOPHER_DIRECTORY:
861  /* we got to convert it first */
862  gopherState->conversion = GopherStateData::HTML_DIR;
863  gopherState->HTML_header_added = 0;
864  break;
865 
866  case GOPHER_INDEX:
867  /* we got to convert it first */
869  gopherState->HTML_header_added = 0;
870  break;
871 
872  case GOPHER_CSO:
873  /* we got to convert it first */
875  gopherState->cso_recno = 0;
876  gopherState->HTML_header_added = 0;
877  break;
878 
879  default:
880  gopherState->conversion = GopherStateData::NORMAL;
881  entry->flush();
882  }
883 
884  /* Schedule read reply. */
885  AsyncCall::Pointer call = commCbCall(5,5, "gopherReadReply",
886  CommIoCbPtrFun(gopherReadReply, gopherState));
887  entry->delayAwareRead(conn, gopherState->replybuf, BUFSIZ, call);
888 }
889 
893 static void
895 {
896  GopherStateData *gopherState = (GopherStateData *)data;
897  MemBuf mb;
898  mb.init();
899 
900  if (gopherState->type_id == GOPHER_CSO) {
901  const char *t = strchr(gopherState->request, '?');
902 
903  if (t)
904  ++t; /* skip the ? */
905  else
906  t = "";
907 
908  mb.appendf("query %s\r\nquit", t);
909  } else {
910  if (gopherState->type_id == GOPHER_INDEX) {
911  if (char *t = strchr(gopherState->request, '?'))
912  *t = '\t';
913  }
914  mb.append(gopherState->request, strlen(gopherState->request));
915  }
916  mb.append("\r\n", 2);
917 
918  debugs(10, 5, gopherState->serverConn);
919  AsyncCall::Pointer call = commCbCall(5,5, "gopherSendComplete",
920  CommIoCbPtrFun(gopherSendComplete, gopherState));
921  Comm::Write(gopherState->serverConn, &mb, call);
922 
923  if (!gopherState->entry->makePublic())
924  gopherState->entry->makePrivate(true);
925 }
926 
927 void
929 {
930  GopherStateData *gopherState = new GopherStateData(fwd);
931 
932  debugs(10, 3, gopherState->entry->url());
933 
934  ++ statCounter.server.all.requests;
935 
936  ++ statCounter.server.other.requests;
937 
938  /* Parse url. */
940  &gopherState->type_id, gopherState->request);
941 
943 
944  if (((gopherState->type_id == GOPHER_INDEX) || (gopherState->type_id == GOPHER_CSO))
945  && (strchr(gopherState->request, '?') == NULL)) {
946  /* Index URL without query word */
947  /* We have to generate search page back to client. No need for connection */
948  gopherMimeCreate(gopherState);
949 
950  if (gopherState->type_id == GOPHER_INDEX) {
952  } else {
953  if (gopherState->type_id == GOPHER_CSO) {
955  } else {
957  }
958  }
959 
960  gopherToHTML(gopherState, (char *) NULL, 0);
961  fwd->complete();
962  return;
963  }
964 
965  gopherState->serverConn = fwd->serverConnection();
966  gopherSendRequest(fwd->serverConnection()->fd, gopherState);
967  AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "gopherTimeout",
968  CommTimeoutCbPtrFun(gopherTimeout, gopherState));
969  commSetConnTimeout(fwd->serverConnection(), Config.Timeout.read, timeoutCall);
970 }
971 
HierarchyLogEntry hier
Definition: HttpRequest.h:149
char const * rawBuf() const
Definition: SquidString.h:85
#define GOPHER_PLUS_IMAGE
Definition: gopher.cc:60
struct StatCounters::@129 server
static void gopherHTMLFooter(StoreEntry *e)
Definition: gopher.cc:336
#define rfc1738_escape_unescaped(x)
Definition: rfc1738.h:59
StatCounters statCounter
Definition: StatCounters.cc:12
void makePrivate(const bool shareable)
Definition: store.cc:152
struct StatCounters::@129::@138 other
#define assert(EX)
Definition: assert.h:17
const char * mimeGetIconURL(const char *fn)
Definition: mime.cc:158
#define GOPHER_BIN
Definition: gopher.cc:47
const char * mkrfc1123(time_t)
Definition: rfc1123.c:202
virtual void append(const char *c, int sz)
Definition: MemBuf.cc:216
int unlock(const char *context)
Definition: store.cc:463
void deleteThis(const char *aReason)
Definition: gopher.cc:159
void CTCB(const CommTimeoutCbParams &params)
Definition: CommCalls.h:39
char request[MAX_URL]
Definition: gopher.cc:119
void path(const char *p)
Definition: Uri.h:86
bool timestampsSet()
Definition: store.cc:1460
Definition: SBuf.h:86
virtual void append(char const *, int)
Appends a c-string to existing packed data.
Definition: store.cc:829
DelayId mostBytesAllowed() const
Definition: MemObject.cc:449
struct IoStats::@81 Gopher
#define xstrdup
virtual void buffer()
Definition: store.cc:1641
#define safe_free(x)
Definition: xalloc.h:73
void swanSong()
Definition: gopher.cc:166
char * html_quote(const char *)
Definition: html_quote.c:53
Definition: Flag.h:16
const char * mimeGetContentEncoding(const char *fn)
Definition: mime.cc:191
static void gopherMimeCreate(GopherStateData *)
Definition: gopher.cc:181
virtual void flush()
Definition: store.cc:1652
HttpRequest * request
Definition: FwdState.h:148
Definition: enums.h:24
void delayAwareRead(const Comm::ConnectionPointer &conn, char *buf, int len, AsyncCall::Pointer callback)
Definition: store.cc:203
int conn
the current server connection FD
Definition: Transport.cc:26
void SBufToCstring(char *d, const SBuf &s)
Definition: SBuf.h:741
Comm::ConnectionPointer const & serverConnection() const
Definition: FwdState.h:107
#define GOPHER_GIF
Definition: gopher.cc:50
const char * getMyHostname(void)
Definition: tools.cc:412
#define GOPHER_INFO
Definition: gopher.cc:54
void append(char const *buf, int len)
Definition: String.cc:161
time_t squid_curtime
Definition: stub_time.cc:17
static CTCB gopherTimeout
Definition: gopher.cc:138
void replaceHttpReply(HttpReply *, bool andStartWriting=true)
Definition: store.cc:1788
#define GOPHER_IMAGE
Definition: gopher.cc:51
#define GOPHER_UUENCODED
Definition: gopher.cc:44
int64_t bodyBytesRead
number of body bytes received from the next hop or -1
void init(mb_size_t szInit, mb_size_t szMax)
Definition: MemBuf.cc:96
void fd_bytes(int fd, int len, unsigned int type)
Definition: fd.cc:260
static void gopherHTMLHeader(StoreEntry *e, const char *title, const char *substring)
Definition: gopher.cc:323
int read_hist[histSize]
Definition: IoStats.h:21
int gopherCachable(const HttpRequest *req)
Definition: gopher.cc:294
Comm::ConnectionPointer conn
Definition: CommCalls.h:85
const char * xstrerr(int error)
Definition: xstrerror.cc:83
bool IsConnOpen(const Comm::ConnectionPointer &conn)
Definition: Connection.cc:24
void const char HLPCB void * data
Definition: stub_helper.cc:16
StoreEntry * entry
Definition: gopher.cc:107
void port(unsigned short p)
Definition: Uri.h:83
#define debugs(SECTION, LEVEL, CONTENT)
Definition: Debug.h:124
#define GOPHER_FILE
Definition: gopher.cc:38
const char * url() const
Definition: store.cc:1606
#define DBG_IMPORTANT
Definition: Debug.h:46
int bytesWanted(int min, int max) const
Definition: DelayId.cc:132
char * buf
Definition: gopher.cc:122
#define GOPHER_WWW
W3 address.
Definition: gopher.cc:57
void * memAllocate(mem_type)
Allocate one element from the typed pool.
Definition: old_api.cc:214
void complete()
Definition: FwdState.cc:496
#define GOPHER_3270
Definition: gopher.cc:49
static IOCB gopherSendComplete
Definition: gopher.cc:140
FwdState::Pointer fwd
Definition: gopher.cc:124
void comm_read(const Comm::ConnectionPointer &conn, char *buf, int len, AsyncCall::Pointer &callback)
Definition: Read.h:57
#define CBDATA_CLASS(type)
Definition: cbdata.h:302
#define GOPHER_DOSBIN
Definition: gopher.cc:43
bool isEmpty() const
Definition: Store.h:60
AnyP::Uri url
the request URI
Definition: HttpRequest.h:107
void PF(int, void *)
Definition: forward.h:18
optimized set of C chars, with quick membership test and merge support
Definition: CharacterSet.h:17
uint16_t flags
Definition: Store.h:210
#define GOPHER_MACBINHEX
Definition: gopher.cc:42
static void gopher_request_parse(const HttpRequest *req, char *type_id, char *request)
Definition: gopher.cc:259
Definition: parse.c:160
static int port
Definition: ldap_backend.cc:69
MemObject * mem_obj
Definition: Store.h:199
#define LOCAL_ARRAY(type, name, size)
Definition: leakcheck.h:18
#define GOPHER_TELNET
Definition: gopher.cc:46
static void gopherToHTML(GopherStateData *, char *inbuf, int len)
Definition: gopher.cc:368
void gopherStart(FwdState *fwd)
Definition: gopher.cc:928
#define BUFSIZ
Definition: defines.h:20
void CLCB(const CommCloseCbParams &params)
Definition: CommCalls.h:42
unsigned char code
Definition: html_quote.c:20
std::ostream & HERE(std::ostream &s)
Definition: Debug.h:153
void IOCB(const Comm::ConnectionPointer &conn, char *, size_t size, Comm::Flag flag, int xerrno, void *data)
Definition: CommCalls.h:36
int reads
Definition: IoStats.h:19
Flag
Definition: Flag.h:15
#define GOPHER_PLUS_MOVIE
Definition: gopher.cc:61
bool makePublic(const KeyScope keyScope=ksDefault)
Definition: store.cc:145
int ignoreErrno(int ierrno)
Definition: comm.cc:1477
AccessLogEntryPointer al
info for the future access.log entry
Definition: FwdState.h:149
#define MYNAME
Definition: Debug.h:166
#define TAB
Definition: gopher.cc:66
struct StatCounters::@129::@138 all
#define GOPHER_DIRECTORY
Definition: gopher.cc:39
#define GOPHER_INDEX
Definition: gopher.cc:45
void memFree(void *, int type)
Free a element allocated by memAllocate()
Definition: old_api.cc:222
#define CBDATA_CLASS_INIT(type)
Definition: cbdata.h:318
#define GOPHER_SOUND
Definition: gopher.cc:58
GopherStateData(FwdState *aFwd)
Definition: gopher.cc:84
void appendf(const char *fmt,...) PRINTF_FORMAT_ARG2
Append operation with printf-style arguments.
Definition: Packable.h:61
static IOCB gopherReadReply
Definition: gopher.cc:139
static char def_gopher_text[]
Definition: gopher.cc:145
static CLCB gopherStateFree
Definition: gopher.cc:131
Definition: MemBuf.h:23
#define TEMP_BUF_SIZE
Definition: gopher.cc:69
#define GOPHER_HTML
Definition: gopher.cc:53
void rfc1738_unescape(char *url)
Definition: rfc1738.c:146
struct SquidConfig::@98 Timeout
void fail(ErrorState *err)
Definition: FwdState.cc:442
int HTML_header_added
Definition: gopher.cc:116
CommCbFunPtrCallT< Dialer > * commCbCall(int debugSection, int debugLevel, const char *callName, const Dialer &dialer)
Definition: CommCalls.h:342
AsyncCall::Pointer comm_add_close_handler(int fd, CLCB *handler, void *data)
Definition: comm.cc:961
Gopher server.
Definition: Message.h:43
IoStats IOStats
static PF gopherSendRequest
Definition: gopher.cc:141
Comm::ConnectionPointer serverConn
Definition: gopher.cc:123
void clean()
Definition: String.cc:125
#define MAX_URL
Definition: defines.h:118
#define rfc1738_escape_part(x)
Definition: rfc1738.h:55
char replybuf[BUFSIZ]
Definition: gopher.cc:126
void bytesIn(int qty)
Definition: DelayId.cc:154
const char * mimeGetContentType(const char *fn)
Definition: mime.cc:177
#define GOPHER_PLUS_SOUND
Definition: gopher.cc:62
void storeAppendPrintf(StoreEntry *e, const char *fmt,...)
Definition: store.cc:881
char const * visible_appname_string
static void gopherEndHTML(GopherStateData *)
Definition: gopher.cc:348
size_type size() const
Definition: SquidString.h:72
enum GopherStateData::@74 conversion
int commSetConnTimeout(const Comm::ConnectionPointer &conn, int timeout, AsyncCall::Pointer &callback)
Definition: comm.cc:552
#define EBIT_TEST(flag, bit)
Definition: defines.h:107
class SquidConfig Config
Definition: SquidConfig.cc:12
void Controller::create() STUB void Controller Controller nil
HttpReply::Pointer reply_
Definition: gopher.cc:125
#define NULL
Definition: types.h:166
uint32_t sources
The message sources.
Definition: Message.h:100
int size
Definition: ModDevPoll.cc:77
static char def_gopher_bin[]
Definition: gopher.cc:143
#define GOPHER_CSO
Definition: gopher.cc:40
time_t read
Definition: SquidConfig.h:106
void Write(const Comm::ConnectionPointer &conn, const char *buf, int size, AsyncCall::Pointer &callback, FREE *free_func)
Definition: Write.cc:35
void lock(const char *context)
Definition: store.cc:439

 

Introduction

Documentation

Support

Miscellaneous

Web Site Translations

Mirrors