CVE-2023-46728: NULL Pointer Dereference (CWE-476)
value: Squid is a caching proxy for the Web supporting HTTP, HTTPS, FTP, and more. Due to a NULL pointer dereference bug Squid is vulnerable to a Denial of Service attack against Squid's Gopher gateway. The gopher protocol is always available and enabled in Squid prior to Squid 6.0.1. Responses triggering this bug are possible to be received from any gopher server, even those without malicious intent. Gopher support has been removed in Squid version 6.0.1. Users are advised to upgrade. Users unable to upgrade should reject all gopher URL requests.
CWE description: The product dereferences a pointer that it expects to be valid but is NULL.
CVSS v3 base score: 7.5
Published: 2023-11-06T18:15Z
Repository: https://github.com/squid-cache/squid
Language: Other
Files changed: doc/Programming-Guide/Groups.dox, doc/debug-sections.txt, doc/manuals/de.po, doc/manuals/en.po, doc/manuals/en_AU.po, doc/manuals/es.po, doc/manuals/fr.po, doc/manuals/it.po, errors/af.po, errors/az.po, errors/bg.po, errors/ca.po, errors/cs.po, errors/da.po, errors/de.po, errors/el.po, errors/en.po, errors/errorpage.css, errors/es-mx.po, errors/es.po, errors/et.po, errors/fi.po, errors/fr.po, errors/he.po, errors/hu.po, errors/hy.po, errors/it.po, errors/ja.po, errors/ko.po, errors/lt.po, errors/lv.po, errors/nl.po, errors/pl.po, errors/pt-br.po, errors/pt.po, errors/ro.po, errors/ru.po, errors/sk.po, errors/sl.po, errors/sr-latn.po, errors/sv.po, errors/templates/ERR_UNSUP_REQ, errors/tr.po, errors/uk.po, errors/vi.po, errors/zh-hans.po, errors/zh-hant.po, src/FwdState.cc, src/HttpRequest.cc, src/IoStats.h, src/Makefile.am, src/adaptation/ecap/Host.cc, src/adaptation/ecap/MessageRep.cc, src/anyp/ProtocolType.h, src/anyp/Uri.cc, src/anyp/UriScheme.cc, src/cf.data.pre, src/client_side_request.cc, src/error/forward.h, src/http/Message.h, src/mgr/IoAction.cc, src/mgr/IoAction.h, src/squid.8.in, src/stat.cc, src/tests/Stub.am, test-suite/squidconf/regressions-3.4.0.1
Commit date: 2022-07-27 01:47:01 +0000
Commit message: Remove support for Gopher protocol (#1092)

Gopher code quality remains too low for production use in most
environments. The code is a persistent source of vulnerabilities and
fixing it requires significant effort. We should not be spending scarce
Project resources on improving that code, especially given the lack of
strong demand for Gopher support.

With this change, Gopher requests will be handled like any other request
with an unknown (to Squid) protocol. For example, HTTP requests with
Gopher URI scheme result in ERR_UNSUP_REQ.

Default Squid configuration still considers TCP port 70 "safe". The
corresponding Safe_ports ACL rule has not been removed for consistency
sake: We consider WAIS port safe even though Squid refuses to forward
WAIS requests:

    acl Safe_ports port 70          # gopher
    acl Safe_ports port 210         # wais
Diff: diff --git a/doc/Programming-Guide/Groups.dox b/doc/Programming-Guide/Groups.dox
index e115be0b0f..b56feb4bcb 100644
--- a/doc/Programming-Guide/Groups.dox
+++ b/doc/Programming-Guide/Groups.dox
@@ -29,15 +29,10 @@
  *   to other servers, depending on the protocol.  Cache misses
  *   may be forwarded to either origin servers, or other proxy
  *   caches.
  *   All requests to other proxies are sent as HTTP requests.
  *   All requests to origin-server are sent in that servers protocol.
- *
- \par
- *   Wais and Gopher don't receive much
- *   attention because they comprise a relatively insignificant
- *   portion of Internet traffic.
  */
 
 /**
  \defgroup libsquid         Squid Library
  *
diff --git a/doc/debug-sections.txt b/doc/debug-sections.txt
index 6569e3a05f..686e90dc85 100644
--- a/doc/debug-sections.txt
+++ b/doc/debug-sections.txt
@@ -25,11 +25,10 @@ section 05    Socket Functions
 section 05    TCP Socket Functions
 section 06    Disk I/O Routines
 section 07    Multicast
 section 08    Swap File Bitmap
 section 09    File Transfer Protocol (FTP)
-section 10    Gopher
 section 11    Hypertext Transfer Protocol (HTTP)
 section 12    Internet Cache Protocol (ICP)
 section 13    High Level Memory Pool Management
 section 14    IP Cache
 section 14    IP Storage and Handling
diff --git a/doc/manuals/de.po b/doc/manuals/de.po
index dc3d5c2d8e..9eb319be32 100644
--- a/doc/manuals/de.po
+++ b/doc/manuals/de.po
@@ -3866,11 +3866,11 @@ msgid ""
 "supporting FTP, gopher, ICAP, ICP, HTCP and HTTP data objects.  Unlike "
 "traditional caching software, Squid handles all requests in a single, non-"
 "blocking process."
 msgstr ""
 "B<squid> ist ein leistungsfähiger Proxy caching Server für Web Clients, "
-"welcher FTP, gopher, ICAP, ICP, HTCP and HTTP Data Objects unterstützt. Im "
+"welcher FTP, ICAP, ICP, HTCP and HTTP Data Objects unterstützt. Im "
 "Unterschied zu traditioneller caching Software verarbeitet B<squid> alle "
 "Anfragen in einem einzigen non-blocking Prozess."
 
 #. type: Plain text
 #: src/squid.8.in:34
diff --git a/doc/manuals/en.po b/doc/manuals/en.po
index ff93ca0808..68bcb7b4ee 100644
--- a/doc/manuals/en.po
+++ b/doc/manuals/en.po
@@ -4260,11 +4260,11 @@ msgid ""
 "supporting FTP, gopher, ICAP, ICP, HTCP and HTTP data objects.  Unlike "
 "traditional caching software, Squid handles all requests in a single, non-"
 "blocking process."
 msgstr ""
 "B<squid> is a high-performance proxy caching server for web clients, "
-"supporting FTP, gopher, ICAP, ICP, HTCP and HTTP data objects.  Unlike "
+"supporting FTP, ICAP, ICP, HTCP and HTTP data objects.  Unlike "
 "traditional caching software, Squid handles all requests in a single, non-"
 "blocking process."
 
 #. type: Plain text
 #: src/squid.8.in:34
diff --git a/doc/manuals/en_AU.po b/doc/manuals/en_AU.po
index ca719bb911..e8ec8e02b9 100644
--- a/doc/manuals/en_AU.po
+++ b/doc/manuals/en_AU.po
@@ -4058,11 +4058,11 @@ msgid ""
 "supporting FTP, gopher, ICAP, ICP, HTCP and HTTP data objects.  Unlike "
 "traditional caching software, Squid handles all requests in a single, non-"
 "blocking process."
 msgstr ""
 "B<squid> is a high-performance proxy caching server for web clients, "
-"supporting FTP, gopher, ICAP, ICP, HTCP and HTTP data objects.  Unlike "
+"supporting FTP, ICAP, ICP, HTCP and HTTP data objects.  Unlike "
 "traditional caching software, Squid handles all requests in a single, non-"
 "blocking process."
 
 #. type: Plain text
 #: src/squid.8.in:34
diff --git a/doc/manuals/es.po b/doc/manuals/es.po
index 563d07afb4..bf5f9881ef 100644
--- a/doc/manuals/es.po
+++ b/doc/manuals/es.po
@@ -2560,11 +2560,11 @@ msgid ""
 "supporting FTP, gopher, ICAP, ICP, HTCP and HTTP data objects.  Unlike "
 "traditional caching software, Squid handles all requests in a single, non-"
 "blocking process."
 msgstr ""
 "B<squid> es servidor proxy-caché de alto rendimiento para clientes web que "
-"soporta FTP, gopher, ICAP, ICP, HTCP y objetos de datos HTTP. A diferencia "
+"soporta FTP, ICAP, ICP, HTCP y objetos de datos HTTP. A diferencia "
 "del software de caché tradicional, Squid maneja todas las solicitudes en un "
 "único proceso asíncrono"
 
 #. type: Plain text
 #: helpers/basic_auth/LDAP/basic_ldap_auth.8:142
diff --git a/doc/manuals/fr.po b/doc/manuals/fr.po
index a8e001f614..df7455d508 100644
--- a/doc/manuals/fr.po
+++ b/doc/manuals/fr.po
@@ -1493,11 +1493,11 @@ msgid ""
 "supporting FTP, gopher, ICAP, ICP, HTCP and HTTP data objects.  Unlike "
 "traditional caching software, Squid handles all requests in a single, "
 "non-blocking process."
 msgstr ""
 "B<squid> est un serveur proxy de haute performance cachant les clients sur "
-"le web, il supporte les protocoles FTP, Gopher, ICAP, ICP, HTCP et HTTP pour "
+"le web, il supporte les protocoles FTP, ICAP, ICP, HTCP et HTTP pour "
 "le transfert des données. Contrairement aux logiciels de mise en cache "
 "traditionnelle, Squid gère toutes les requêtes en un seul processus non-"
 "bloquant."
 
 #. type: Plain text
diff --git a/doc/manuals/it.po b/doc/manuals/it.po
index 43238ae053..e46fa8f883 100644
--- a/doc/manuals/it.po
+++ b/doc/manuals/it.po
@@ -1483,11 +1483,11 @@ msgid ""
 "supporting FTP, gopher, ICAP, ICP, HTCP and HTTP data objects.  Unlike "
 "traditional caching software, Squid handles all requests in a single, "
 "non-blocking process."
 msgstr ""
 "B<squid> è un server proxy con cache ad alte prestazioni per client WWW, con "
-"supporto supporta i protocolli FTP, gopher, ICAP, ICP, HTCP e HTTP. A "
+"supporto supporta i protocolli FTP, ICAP, ICP, HTCP e HTTP. A "
 "differenza di altri software, Squid gestisce tutte operazioni all'interno di "
 "un singolo processo con I/O non bloccante."
 
 #. type: Plain text
 #: src/squid.8.in:36
diff --git a/errors/af.po b/errors/af.po
index 6247a0f83d..91c538d446 100644
--- a/errors/af.po
+++ b/errors/af.po
@@ -807,21 +807,19 @@ msgstr "Squid het geen data vir hierdie navraag ontvang nie."
 #: templates/ERR_UNSUP_REQ+html.body.div.p:27-1
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
-"Squid ondersteun nie alle navraagmetodes vir alle toegangsprotokolle nie. "
-"Mens kan by voorbeeld nie 'n Gopher-navraag POST nie."
+"Squid ondersteun nie alle navraagmetodes vir alle toegangsprotokolle nie."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
-"Squid ondersteun nie alle navraagmetodes vir alle toegangsprotokolle nie. "
-"Mens kan by voorbeeld nie 'n Gopher-navraag POST nie."
+"Squid ondersteun nie alle navraagmetodes vir alle toegangsprotokolle nie."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/az.po b/errors/az.po
index c02f78690e..ad85365422 100644
--- a/errors/az.po
+++ b/errors/az.po
@@ -834,21 +834,19 @@ msgstr "Squid bu sorğu üçün heç bir məlumat almadı."
 #: templates/ERR_UNSUP_REQ+html.body.div.p:27-1
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
-"Squid bütün sorğu metodları və bütün protokollardəstəkləmir. Məsələn, Gopher "
-"protokolu üzrə siz POST sorğu metodunu yerinə yetirə bilməzsiniz."
+"Squid bütün sorğu metodları və bütün protokollardəstəkləmir."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
-"Squid bütün sorğu metodları və bütün protokollardəstəkləmir. Məsələn, Gopher "
-"protokolu üzrə siz POST sorğu metodunu yerinə yetirə bilməzsiniz."
+"Squid bütün sorğu metodları və bütün protokollardəstəkləmir."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
diff --git a/errors/bg.po b/errors/bg.po
index 0e9f720ee3..14dd150196 100644
--- a/errors/bg.po
+++ b/errors/bg.po
@@ -838,21 +838,19 @@ msgstr "Кеш сървърът не получи никакви данни за
 #: templates/ERR_UNSUP_REQ+html.body.div.p:27-1
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
-"Кеш сървърът не поддържа всички методи на заявка за всички протоколи. "
-"Например, не можете да заявите метод POST за протокол Gopher."
+"Кеш сървърът не поддържа всички методи на заявка за всички протоколи."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
-"Кеш сървърът не поддържа всички методи на заявка за всички протоколи. "
-"Например, не можете да заявите метод POST за протокол Gopher."
+"Кеш сървърът не поддържа всички методи на заявка за всички протоколи."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/ca.po b/errors/ca.po
index 9c747a7b4d..80a193504b 100644
--- a/errors/ca.po
+++ b/errors/ca.po
@@ -832,22 +832,20 @@ msgstr "La <i>cache</i> ha rebut una resposta sense dades, un document buit."
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
 "La <i>cache</i> no admet tots els tipus de petici&oacute; per a tots els "
-"protocols. Per exemple, no es pot fer una petici&oacute; tipus POST en el "
-"protocol Gopher."
+"protocols."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
 "La <i>cache</i> no admet tots els tipus de petici&oacute; per a tots els "
-"protocols. Per exemple, no es pot fer una petici&oacute; tipus POST en el "
-"protocol Gopher."
+"protocols."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
diff --git a/errors/cs.po b/errors/cs.po
index be8600674d..066893051c 100644
--- a/errors/cs.po
+++ b/errors/cs.po
@@ -829,21 +829,19 @@ msgstr "Squid neobdržel v odpovědi na tento dotaz žádná data."
 #: templates/ERR_UNSUP_REQ+html.body.div.p:27-1
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
-"Squid nepodporuje všechny typy metod u všech protokolů. Např. není možno "
-"použit metodu POST u služby GOPHER."
+"Squid nepodporuje všechny typy metod u všech protokolů."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
-"Squid nepodporuje všechny typy metod u všech protokolů. Např. není možno "
-"použit metodu POST u služby GOPHER."
+"Squid nepodporuje všechny typy metod u všech protokolů."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/da.po b/errors/da.po
index 5724aa395a..1ecad0e013 100644
--- a/errors/da.po
+++ b/errors/da.po
@@ -827,22 +827,20 @@ msgstr "Proxy'en Squid modtog ingen data for denne foresp&oslash;rgsel."
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
 "Proxy'en Squid underst&oslash;tter ikke alle foresp&oslash;rgselsmetoder for "
-"alle adgangs protokoller. For eksempel kan du ikke POST en Gopher "
-"foresp&oslash;rgsel."
+"alle adgangs protokoller."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
 "Proxy'en Squid underst&oslash;tter ikke alle foresp&oslash;rgselsmetoder for "
-"alle adgangs protokoller. For eksempel kan du ikke POST en Gopher "
-"foresp&oslash;rgsel."
+"alle adgangs protokoller."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/de.po b/errors/de.po
index bb6c51d1ab..794991b032 100644
--- a/errors/de.po
+++ b/errors/de.po
@@ -830,21 +830,19 @@ msgstr "Squid hat keine Daten für diese Anfrage empfangen."
 #: templates/ERR_UNSUP_REQ+html.body.div.p:27-1
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
-"Squid unterstützt nicht alle Anfragemethoden für alle Protokolle. Sie können "
-"zum Beispiel keine POST Anfrage über das Gopher Protokoll senden."
+"Squid unterstützt nicht alle Anfragemethoden für alle Protokolle."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
-"Squid unterstützt nicht alle Anfragemethoden für alle Protokolle. Sie können "
-"zum Beispiel keine POST Anfrage über das Gopher Protokoll senden."
+"Squid unterstützt nicht alle Anfragemethoden für alle Protokolle."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/el.po b/errors/el.po
index 5182951cb2..64f5a02a53 100644
--- a/errors/el.po
+++ b/errors/el.po
@@ -841,20 +841,20 @@ msgstr "Το Squid δεν έλαβε δεδομένα για αυτό το αί
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
 "Το Squid δεν υποστηρίζει όλες τις μεθόδους αιτήσεων για όλα τα πρωτόκολλα "
-"πρόσβασης. Για παράδειγμα, το POST για Gopher δεν υποστηρίζεται."
+"πρόσβασης."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
 "Το Squid δεν υποστηρίζει όλες τις μεθόδους αιτήσεων για όλα τα πρωτόκολλα "
-"πρόσβασης. Για παράδειγμα, το POST για Gopher δεν υποστηρίζεται."
+"πρόσβασης."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/en.po b/errors/en.po
index 8b186cd9f0..48f3356730 100644
--- a/errors/en.po
+++ b/errors/en.po
@@ -830,21 +830,19 @@ msgstr "Squid did not receive any data for this request."
 #: templates/ERR_UNSUP_REQ+html.body.div.p:27-1
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
-"Squid does not support all request methods for all access protocols. For "
-"example, you can not POST a Gopher request."
+"Squid does not support all request methods for all access protocols."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
-"Squid does not support all request methods for all access protocols. For "
-"example, you can not POST a Gopher request."
+"Squid does not support all request methods for all access protocols."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/errorpage.css b/errors/errorpage.css
index 5e4fd4750c..5f12619a62 100644
--- a/errors/errorpage.css
+++ b/errors/errorpage.css
@@ -71,11 +71,11 @@ p {
 }
 
 pre {
 }
 
-/* special event: FTP / Gopher directory listing */
+/* special event: FTP directory listing */
 #dirmsg {
     font-family: courier, monospace;
     color: black;
     font-size: 10pt;
 }
diff --git a/errors/es-mx.po b/errors/es-mx.po
index b3152bb808..3089867032 100644
--- a/errors/es-mx.po
+++ b/errors/es-mx.po
@@ -841,12 +841,11 @@ msgstr "Squid no recibió información a esta solicitud."
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
 "Squid no soporta todos los métodos de solicitud para algunos protocolos de "
-"acceso. Por ejemplo, no es posible hacer una solcitud POST a un servidor "
-"Gopher."
+"acceso."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
diff --git a/errors/es.po b/errors/es.po
index 0f9ea20131..3ed1d3101a 100644
--- a/errors/es.po
+++ b/errors/es.po
@@ -848,20 +848,20 @@ msgstr "Squid no ha recibido ninguna información en respuesta a esta petición.
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
 "Squid no admite todos los m&eacute;todos para todos los protocolos de "
-"acceso. Por ejemplo, no se puede hacer un POST a un servidor Gopher."
+"acceso."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
 "Squid no admite todos los m&eacute;todos para todos los protocolos de "
-"acceso. Por ejemplo, no se puede hacer un POST a un servidor Gopher."
+"acceso."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/et.po b/errors/et.po
index c1347bb595..1ee068d0f3 100644
--- a/errors/et.po
+++ b/errors/et.po
@@ -836,22 +836,20 @@ msgstr "Squid ei saanud selle p&auml;ringu vastuseks midagi."
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
 "Squid ei toeta k&otilde;iki p&auml;ringu meetodeid k&otilde;ikide "
-"protokollidega. N&auml;iteks, te ei saa teha POST operatsiooni Gopher p&auml;"
-"ringus."
+"protokollidega."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
 "Squid ei toeta k&otilde;iki p&auml;ringu meetodeid k&otilde;ikide "
-"protokollidega. N&auml;iteks, te ei saa teha POST operatsiooni Gopher p&auml;"
-"ringus."
+"protokollidega."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/fi.po b/errors/fi.po
index bd7d74ecf9..1e9ad8ad0a 100644
--- a/errors/fi.po
+++ b/errors/fi.po
@@ -821,21 +821,20 @@ msgstr "Haku ei tuonut Squidille mitään tietoa."
 #: templates/ERR_UNSUP_REQ+html.body.div.p:27-1
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
-"Squid ei tue kaikkia hakupyynnon tyyppejä kaikilla protokollilla. Et voi "
-"esimerkiksi käyttää POST-pyyntöä gopherilla."
+"Squid ei tue kaikkia hakupyynnon tyyppejä kaikilla protokollilla."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
-"Squid ei tue kaikkia hakupyynnon tyyppejä kaikilla protokollilla. Et voi "
-"esimerkiksi käyttää POST-pyyntöä gopherilla."
+"Squid ei tue kaikkia pääsyprotokollia. Esimerkiksi SSH-protokolla "
+"ei ole tällä hetkellä tuettu."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/fr.po b/errors/fr.po
index cdc84d8e46..0093fa98b0 100644
--- a/errors/fr.po
+++ b/errors/fr.po
@@ -850,22 +850,20 @@ msgstr "Squid n'a pas reçu toutes les données pour cette requête."
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
 "Squid ne prend pas en charge tous les types de requêtes par rapport à tous "
-"les protocoles d'accès. Vous ne pouvez pas par exemple utiliser une requête "
-"POST avec le protocole Gopher."
+"les protocoles d'accès."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
 "Squid ne prend pas en charge tous les types de requêtes par rapport à tous "
-"les protocoles d'accès. Vous ne pouvez pas par exemple utiliser une requête "
-"POST avec le protocole Gopher."
+"les protocoles d'accès."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/he.po b/errors/he.po
index ccb27a3928..8ab0ee9128 100644
--- a/errors/he.po
+++ b/errors/he.po
@@ -825,21 +825,19 @@ msgstr "השרת לא קיבל כל תשובה לבקשה זו."
 #: templates/ERR_UNSUP_REQ+html.body.div.p:27-1
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
-"שרת ה Squid אינו תומך בכל שיטות הבקשה לכל הפרוטוקולים. לדוגמא אינך יכול "
-"לשלוח בקשת POST ב-Gopher."
+"שרת ה Squid אינו תומך בכל שיטות הבקשה לכל הפרוטוקולים."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
-"שרת ה Squid אינו תומך בכל שיטות הבקשה לכל הפרוטוקולים. לדוגמא אינך יכול "
-"לשלוח בקשת POST ב-Gopher."
+"שרת ה Squid אינו תומך בכל שיטות הבקשה לכל הפרוטוקולים."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/hu.po b/errors/hu.po
index 6e634b2558..f6e8ecbbf7 100644
--- a/errors/hu.po
+++ b/errors/hu.po
@@ -834,22 +834,20 @@ msgstr "A proxyszerver üres választ kapott a kérésre."
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
 "A proxyszerver nem támogat minden létező kéréstípus és protokoll "
-"kombinációt, így pl. nem lehet POST kéréstípust használni egy Gopher "
-"kérésben."
+"kombinációt."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
 "A proxyszerver nem támogat minden létező kéréstípus és protokoll "
-"kombinációt, így pl. nem lehet POST kéréstípust használni egy Gopher "
-"kérésben."
+"kombinációt."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/hy.po b/errors/hy.po
index 446707cb6f..7365c13ac6 100644
--- a/errors/hy.po
+++ b/errors/hy.po
@@ -822,21 +822,19 @@ msgstr "Քեշը ի պատասխան այս հարցումի ոչ մի տվյա
 #: templates/ERR_UNSUP_REQ+html.body.div.p:27-1
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
-"Squid-ը բոլոր արձանագրությունների համար բոլոր հարցման մեթոդները չի աջակցում. "
-"Օրինակ, Gopher արձանագրության համար չեք կարող POST հարցում կատարել."
+"Squid-ը բոլոր արձանագրությունների համար բոլոր հարցման մեթոդները չի աջակցում."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
-"Squid-ը բոլոր արձանագրությունների համար բոլոր հարցման մեթոդները չի աջակցում. "
-"Օրինակ, Gopher արձանագրության համար չեք կարող POST հարցում կատարել."
+"Squid-ը բոլոր արձանագրությունների համար բոլոր հարցման մեթոդները չի աջակցում."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/it.po b/errors/it.po
index b6ca112800..7b44d360ec 100644
--- a/errors/it.po
+++ b/errors/it.po
@@ -851,20 +851,20 @@ msgstr "Squid non ha ricevuto dati per questa richiesta."
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
 "Squid non consente di utilizzare qualsiasi tipo di richiesta per qualsiasi "
-"protocollo (a esempio non consente una richiesta POST su protocollo Gopher)."
+"protocollo."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
 "Squid non consente di utilizzare qualsiasi tipo di richiesta per qualsiasi "
-"protocollo (a esempio non consente una richiesta POST su protocollo Gopher)."
+"protocollo."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/ja.po b/errors/ja.po
index 6664e40d85..beb06def92 100644
--- a/errors/ja.po
+++ b/errors/ja.po
@@ -835,22 +835,20 @@ msgstr "この要求に対して、Squidは何もデータを受け取りませ
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
 "Squidはすべてのアクセス・プロトコルに対して、すべてのリクエストメソッドをサ"
-"ポートしているわけではありません。例えば、POSTをGopherのリクエストで行うこと"
-"はできません。"
+"ポートしているわけではありません。"
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
 "Squidはすべてのアクセス・プロトコルに対して、すべてのリクエストメソッドをサ"
-"ポートしているわけではありません。例えば、POSTをGopherのリクエストで行うこと"
-"はできません。"
+"ポートしているわけではありません。"
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/ko.po b/errors/ko.po
index 049c8ae1f0..829aebd8e6 100644
--- a/errors/ko.po
+++ b/errors/ko.po
@@ -825,21 +825,19 @@ msgstr "Squid는 이 요청으로 부터 아무런 데이터도 받지 못했습
 #: templates/ERR_UNSUP_REQ+html.body.div.p:27-1
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
-"Squid는 모든 접속 프로토콜에 대한 request method를 지원하지 않습니다. 한가지 "
-"예로, Gopher에서 POST request를 사용할 수 없습니다."
+"Squid는 모든 접속 프로토콜에 대한 request method를 지원하지 않습니다."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
-"Squid는 모든 접속 프로토콜에 대한 request method를 지원하지 않습니다. 한가지 "
-"예로, Gopher에서 POST request를 사용할 수 없습니다."
+"Squid는 모든 접속 프로토콜에 대한 request method를 지원하지 않습니다."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/lt.po b/errors/lt.po
index 85fdaa9d77..8c0993f823 100644
--- a/errors/lt.po
+++ b/errors/lt.po
@@ -834,21 +834,19 @@ msgstr "Squid negavo jokių duomenų šiai užklausai."
 #: templates/ERR_UNSUP_REQ+html.body.div.p:27-1
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
-"Squid palaiko ne visus užklausos metodus daliai protokolų. Pavyzdžiui, jūs "
-"negalite vykdyti POST Gopher tipo užklausoje."
+"Squid palaiko ne visus užklausos metodus daliai protokolų."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
-"Squid palaiko ne visus užklausos metodus daliai protokolų. Pavyzdžiui, jūs "
-"negalite vykdyti POST Gopher tipo užklausoje."
+"Squid palaiko ne visus užklausos metodus daliai protokolų."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/lv.po b/errors/lv.po
index d9d6dfe8db..eab4677983 100644
--- a/errors/lv.po
+++ b/errors/lv.po
@@ -838,21 +838,19 @@ msgstr "Squid nav saņēmis nekādus datus šim pieprasījumam."
 #: templates/ERR_UNSUP_REQ+html.body.div.p:27-1
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
-"Squid neatbalsta visas pieprasījuma metodes visiem protokoliem. Piemēram, "
-"Jūs nevarat veikt POST pieprasījumu izmantojot Gopher protokolu."
+"Squid neatbalsta visas pieprasījuma metodes visiem protokoliem."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
-"Squid neatbalsta visas pieprasījuma metodes visiem protokoliem. Piemēram, "
-"Jūs nevarat veikt POST pieprasījumu izmantojot Gopher protokolu."
+"Squid neatbalsta visas pieprasījuma metodes visiem protokoliem."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/nl.po b/errors/nl.po
index fcc71949d1..93197e5ae8 100644
--- a/errors/nl.po
+++ b/errors/nl.po
@@ -840,21 +840,19 @@ msgstr "Squid ontving in het geheel geen data op dit verzoek."
 #: templates/ERR_UNSUP_REQ+html.body.div.p:27-1
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
-"Squid ondersteunt niet alle verzoekmethoden voor alle toegangsprotocollen. U "
-"kunt bijvoorbeeld geen Gopher verzoek POSTen."
+"Squid ondersteunt niet alle verzoekmethoden voor alle toegangsprotocollen."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
-"Squid ondersteunt niet alle verzoekmethoden voor alle toegangsprotocollen. U "
-"kunt bijvoorbeeld geen Gopher verzoek POSTen."
+"Squid ondersteunt niet alle verzoekmethoden voor alle toegangsprotocollen."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/pl.po b/errors/pl.po
index 3400ff443d..e4104d17ad 100644
--- a/errors/pl.po
+++ b/errors/pl.po
@@ -829,21 +829,19 @@ msgstr "Squid nie otrzymał żadnych danych w odpowiedzi na to żądanie."
 #: templates/ERR_UNSUP_REQ+html.body.div.p:27-1
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
-"Squid nie wspiera wszystkich metod we wszystkich protokołach. Na przykład "
-"nie możesz użyć metody POST w żądaniu skierowanym do usługi Gopher."
+"Squid nie wspiera wszystkich metod we wszystkich protokołach."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
-"Squid nie wspiera wszystkich metod we wszystkich protokołach. Na przykład "
-"nie możesz użyć metody POST w żądaniu skierowanym do usługi Gopher."
+"Squid nie wspiera wszystkich metod we wszystkich protokołach."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/pt-br.po b/errors/pt-br.po
index b13c2ab736..77eb19025d 100644
--- a/errors/pt-br.po
+++ b/errors/pt-br.po
@@ -840,22 +840,20 @@ msgstr "Squid não recebeu nenhum dado para essa requisição."
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
 "Squid não suporta todos os métodos de requisição para todos os protocolos de "
-"acesso. Por exemplo, você não pode emitir uma requisição POST ao protocolo "
-"Gopher."
+"acesso."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
 "Squid não suporta todos os métodos de requisição para todos os protocolos de "
-"acesso. Por exemplo, você não pode emitir uma requisição POST ao protocolo "
-"Gopher."
+"acesso."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/pt.po b/errors/pt.po
index 3d6e5cb08f..3e576bf11f 100644
--- a/errors/pt.po
+++ b/errors/pt.po
@@ -838,21 +838,19 @@ msgstr "Squid não recebeu nenhum dado para esta requisição."
 #, fuzzy
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
-"Squid não suporta todos os métodos para todos os protocolos. Por exemplo: "
-"você não pode fazer cache de Gopher."
+"Squid não suporta todos os métodos para todos os protocolos."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
-"Squid não suporta todos os métodos para todos os protocolos. Por exemplo: "
-"você não pode fazer cache de Gopher."
+"Squid não suporta todos os métodos para todos os protocolos."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/ro.po b/errors/ro.po
index fff40e4605..9aa7301460 100644
--- a/errors/ro.po
+++ b/errors/ro.po
@@ -846,20 +846,20 @@ msgstr "Squid nu a recepţionat nici un fel de date pentru această cerere."
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
 "Squid nu suportă toate metodele de cerere pentru toate protocoalele de "
-"acces. De exemplu, nu puteţi face o cerere de tip POST pentru Gopher."
+"acces."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
 "Squid nu suportă toate metodele de cerere pentru toate protocoalele de "
-"acces. De exemplu, nu puteţi face o cerere de tip POST pentru Gopher."
+"acces."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/ru.po b/errors/ru.po
index d9c21c08b3..f000ecb8c2 100644
--- a/errors/ru.po
+++ b/errors/ru.po
@@ -823,21 +823,19 @@ msgstr "Squid не получил никаких данных в ответ на
 #: templates/ERR_UNSUP_REQ+html.body.div.p:27-1
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
-"Squid не поддерживает все методы запросов для всех протоколов. К примеру, "
-"для протокола Gopher Вы не можете выполнить запрос POST."
+"Squid не поддерживает все методы запросов для всех протоколов."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
-"Squid не поддерживает все методы запросов для всех протоколов. К примеру, "
-"для протокола Gopher Вы не можете выполнить запрос POST."
+"Squid не поддерживает все методы запросов для всех протоколов."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/sk.po b/errors/sk.po
index 44a2a1c5ac..df3bffe8b3 100644
--- a/errors/sk.po
+++ b/errors/sk.po
@@ -835,21 +835,19 @@ msgstr "Squid nedostal v odpovedi na túto požiadavku žiadne údaje."
 #: templates/ERR_UNSUP_REQ+html.body.div.p:27-1
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
-"Squid nepodporuje všetky typy metód pri všetkých protokoloch. Napríklad: nie "
-"je možné použiť metódu POST pri službe Gopher."
+"Squid nepodporuje všetky typy metód pri všetkých protokoloch."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
-"Squid nepodporuje všetky typy metód pri všetkých protokoloch. Napríklad: nie "
-"je možné použiť metódu POST pri službe Gopher."
+"Squid nepodporuje všetky typy metód pri všetkých protokoloch."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/sl.po b/errors/sl.po
index 31860264d4..0f7ff9f218 100644
--- a/errors/sl.po
+++ b/errors/sl.po
@@ -825,21 +825,19 @@ msgstr "Squid za to zahtevo ni prejel nobenih podatkov."
 #: templates/ERR_UNSUP_REQ+html.body.div.p:27-1
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
-"Squid ne podpira vseh metod zahtev za vse protokole dostopa. Tako npr. "
-"metode POST ne morete uporabiti za zahtevo Gopher."
+"Squid ne podpira vseh metod zahtev za vse protokole dostopa."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
-"Squid ne podpira vseh metod zahtev za vse protokole dostopa. Tako npr. "
-"metode POST ne morete uporabiti za zahtevo Gopher."
+"Squid ne podpira vseh metod zahtev za vse protokole dostopa."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/sr-latn.po b/errors/sr-latn.po
index c85fae5e91..2ab43f5668 100644
--- a/errors/sr-latn.po
+++ b/errors/sr-latn.po
@@ -833,20 +833,20 @@ msgstr "Squid proksi nije dobio nikakve podatke na vaš zahtev."
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
 "Squid Proksi server ne podržava sve metode zahteva za sve moguæe pristupne "
-"protokole. Na primer ne možete da uradite POST na Gopher zahtev."
+"protokole."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
 "Squid Proksi server ne podržava sve metode zahteva za sve moguæe pristupne "
-"protokole. Na primer ne možete da uradite POST na Gopher zahtev."
+"protokole."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/sv.po b/errors/sv.po
index fe32b666e0..0f4d2ac4a5 100644
--- a/errors/sv.po
+++ b/errors/sv.po
@@ -843,21 +843,19 @@ msgstr ""
 #: templates/ERR_UNSUP_REQ+html.body.div.p:27-1
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
-"Squid st&ouml;djer inte alla fr&aring;gemetoder f&ouml;r alla protokoll. "
-"Till exempel, Ni kan inte POST'a en Gopher f&ouml;rfr&aring;gan."
+"Squid st&ouml;djer inte alla fr&aring;gemetoder f&ouml;r alla protokoll."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
-"Squid st&ouml;djer inte alla fr&aring;gemetoder f&ouml;r alla protokoll. "
-"Till exempel, Ni kan inte POST'a en Gopher f&ouml;rfr&aring;gan."
+"Squid st&ouml;djer inte alla fr&aring;gemetoder f&ouml;r alla protokoll."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/templates/ERR_UNSUP_REQ b/errors/templates/ERR_UNSUP_REQ
index b2310ae203..7cc4e20b48 100644
--- a/errors/templates/ERR_UNSUP_REQ
+++ b/errors/templates/ERR_UNSUP_REQ
@@ -22,11 +22,11 @@ body
 
 <blockquote id="error">
 <p><b>Unsupported Request Method and Protocol</b></p>
 </blockquote>
 
-<p>Squid does not support all request methods for all access protocols. For example, you can not POST a Gopher request.</p>
+<p>Squid does not support all request methods for all access protocols.</p>
 
 <p>Your cache administrator is <a href="mailto:%w%W">%w</a>.</p>
 <br>
 </div>
 
diff --git a/errors/tr.po b/errors/tr.po
index 4abf5bfc0a..ed437f7b2d 100644
--- a/errors/tr.po
+++ b/errors/tr.po
@@ -827,21 +827,19 @@ msgstr "Squid, isteğiniz ile ilgili herhangi bir bilgi alamadı."
 #: templates/ERR_UNSUP_REQ+html.body.div.p:27-1
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
-"Squid, bazı erişim protokollerin, bazı istek yöntemlerini desteklemiyor. "
-"Örneğin Gopher isteğinizde POST yapamazsınız."
+"Squid, bazı erişim protokollerin, bazı istek yöntemlerini desteklemiyor."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
-"Squid, bazı erişim protokollerin, bazı istek yöntemlerini desteklemiyor. "
-"Örneğin Gopher isteğinizde POST yapamazsınız."
+"Squid, bazı erişim protokollerin, bazı istek yöntemlerini desteklemiyor."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/uk.po b/errors/uk.po
index 692d8fc555..b8f0c0dbc9 100644
--- a/errors/uk.po
+++ b/errors/uk.po
@@ -837,21 +837,19 @@ msgstr "Squid не отримав жодних даних для цього за
 #: templates/ERR_UNSUP_REQ+html.body.div.p:27-1
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
-"Squid не підтримує всі методи запитів для всіх наявних протоколів. Як "
-"приклад, Ви не можете виконати запит POST для протоколу Gopher."
+"Squid не підтримує всі методи запитів для всіх наявних протоколів."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
-"Squid не підтримує всі методи запитів для всіх наявних протоколів. Як "
-"приклад, Ви не можете виконати запит POST для протоколу Gopher."
+"Squid не підтримує всі методи запитів для всіх наявних протоколів."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/vi.po b/errors/vi.po
index fad4262a71..ec82cc4340 100644
--- a/errors/vi.po
+++ b/errors/vi.po
@@ -821,20 +821,20 @@ msgstr "Squid chưa nhận dữ liệu cho yêu cầu này."
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
 "Squid không hỗ trợ tất cả các phương pháp yêu cầu cho mỗi giao thức truy "
-"cập. Chẳng hạn, bạn không có khả năng POST một yêu cầu Gopher."
+"cập."
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
 "Squid không hỗ trợ tất cả các phương pháp yêu cầu cho mỗi giao thức truy "
-"cập. Chẳng hạn, bạn không có khả năng POST một yêu cầu Gopher."
+"cập."
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/zh-hans.po b/errors/zh-hans.po
index 5974020c76..268921f26a 100644
--- a/errors/zh-hans.po
+++ b/errors/zh-hans.po
@@ -812,21 +812,19 @@ msgstr "本缓存服务器从被连接的服务器上没有收到任何数据。
 #: templates/ERR_UNSUP_REQ+html.body.div.p:27-1
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
-"Squid （缓存服务器）不能对所有的存取协议支持所有的请求方式。比如说，你不能对 "
-"GOPHER 进行一个 POST 请求。"
+"Squid （缓存服务器）不能对所有的存取协议支持所有的请求方式。"
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
-"Squid （缓存服务器）不能对所有的存取协议支持所有的请求方式。比如说，你不能对 "
-"GOPHER 进行一个 POST 请求。"
+"Squid （缓存服务器）不能对所有的存取协议支持所有的请求方式。"
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/errors/zh-hant.po b/errors/zh-hant.po
index 0aaeceb43d..4b9dadd3ef 100644
--- a/errors/zh-hant.po
+++ b/errors/zh-hant.po
@@ -820,21 +820,20 @@ msgstr ""
 #: templates/ERR_UNSUP_REQ+html.body.div.p:27-1
 msgid ""
 "Squid does not support all request methods for all access protocols. For "
 "example, you can not POST a Gopher request."
 msgstr ""
-"因為 Squid （網路快取程式）並未支援所有的連結要求方式在各式通訊協定上。比如"
-"說，你不能要求一個 GOPHER 的 POST 連結要求。"
+"因為 Squid （網路快取程式）並未支援所有的連結要求方式在各式通訊協定上。"
 
 #: templates/ERR_PROTOCOL_UNKNOWN+html.body.div.p:27-1
 #, fuzzy
 msgid ""
 "Squid does not support some access protocols. For example, the SSH protocol "
 "is currently not supported."
 msgstr ""
-"因為 Squid （網路快取程式）並未支援所有的連結要求方式在各式通訊協定上。比如"
-"說，你不能要求一個 GOPHER 的 POST 連結要求。"
+"Squid 不支持某些訪問協議。例如 SSH 協議 "
+"目前不支持。"
 
 #: templates/ERR_LIFETIME_EXP+html.body.div.p:27-1
 msgid ""
 "Squid has terminated the request because it has exceeded the maximum "
 "connection lifetime."
diff --git a/src/FwdState.cc b/src/FwdState.cc
index 42098a8b88..3f6f9a933e 100644
--- a/src/FwdState.cc
+++ b/src/FwdState.cc
@@ -27,11 +27,10 @@
 #include "event.h"
 #include "fd.h"
 #include "fde.h"
 #include "FwdState.h"
 #include "globals.h"
-#include "gopher.h"
 #include "HappyConnOpener.h"
 #include "hier_code.h"
 #include "http.h"
 #include "http/Stream.h"
 #include "HttpReply.h"
@@ -1282,14 +1281,10 @@ FwdState::dispatch()
 
         case AnyP::PROTO_HTTP:
             httpStart(this);
             break;
 
-        case AnyP::PROTO_GOPHER:
-            gopherStart(this);
-            break;
-
         case AnyP::PROTO_FTP:
             if (request->flags.ftpNative)
                 Ftp::StartRelay(this);
             else
                 Ftp::StartGateway(this);
diff --git a/src/HttpRequest.cc b/src/HttpRequest.cc
index f7543c7d74..0920f08a0c 100644
--- a/src/HttpRequest.cc
+++ b/src/HttpRequest.cc
@@ -17,11 +17,10 @@
 #include "client_side_request.h"
 #include "dns/LookupDetails.h"
 #include "Downloader.h"
 #include "error/Detail.h"
 #include "globals.h"
-#include "gopher.h"
 #include "http.h"
 #include "http/ContentLengthInterpreter.h"
 #include "http/one/RequestParser.h"
 #include "http/Stream.h"
 #include "HttpHdrCc.h"
@@ -559,15 +558,10 @@ HttpRequest::maybeCacheable()
         //     this test is handling request message CC header.
         if (!flags.ignoreCc && cache_control && cache_control->hasNoStore())
             return false;
         break;
 
-    case AnyP::PROTO_GOPHER:
-        if (!gopherCachable(this))
-            return false;
-        break;
-
     case AnyP::PROTO_CACHE_OBJECT:
         return false;
 
     //case AnyP::PROTO_FTP:
     default:
diff --git a/src/IoStats.h b/src/IoStats.h
index 96f64f523c..886607a512 100644
--- a/src/IoStats.h
+++ b/src/IoStats.h
@@ -20,10 +20,10 @@ public:
         int reads_deferred;
         int read_hist[histSize];
         int writes;
         int write_hist[histSize];
     }
-    Http, Ftp, Gopher;
+    Http, Ftp;
 };
 
 #endif /* SQUID_IOSTATS_H_ */
 
diff --git a/src/Makefile.am b/src/Makefile.am
index d337695b0b..ef54d01f74 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -367,12 +367,10 @@ squid_SOURCES = \
 	fqdncache.cc \
 	fqdncache.h \
 	fs_io.cc \
 	fs_io.h \
 	globals.h \
-	gopher.cc \
-	gopher.h \
 	helper.cc \
 	helper.h \
 	hier_code.h \
 	http.cc \
 	http.h \
@@ -1848,12 +1846,10 @@ tests_test_http_range_SOURCES = \
 	filemap.cc \
 	fqdncache.cc \
 	fqdncache.h \
 	fs_io.cc \
 	fs_io.h \
-	tests/stub_gopher.cc \
-	gopher.h \
 	helper.cc \
 	hier_code.h \
 	http.cc \
 	icp_v2.cc \
 	icp_v3.cc \
@@ -2233,12 +2229,10 @@ tests_testHttpRequest_SOURCES = \
 	fde.cc \
 	fqdncache.cc \
 	fqdncache.h \
 	fs_io.cc \
 	fs_io.h \
-	tests/stub_gopher.cc \
-	gopher.h \
 	helper.cc \
 	hier_code.h \
 	http.cc \
 	icp_v2.cc \
 	icp_v3.cc \
@@ -2533,12 +2527,10 @@ tests_testCacheManager_SOURCES = \
 	filemap.cc \
 	fqdncache.cc \
 	fqdncache.h \
 	fs_io.cc \
 	fs_io.h \
-	tests/stub_gopher.cc \
-	gopher.h \
 	helper.cc \
 	hier_code.h \
 	http.cc \
 	icp_v2.cc \
 	icp_v3.cc \
diff --git a/src/adaptation/ecap/Host.cc b/src/adaptation/ecap/Host.cc
index 6202a5f14d..411cac21a6 100644
--- a/src/adaptation/ecap/Host.cc
+++ b/src/adaptation/ecap/Host.cc
@@ -47,11 +47,10 @@ Adaptation::Ecap::Host::Host()
     // TODO: libecap::headerXServerIp.assignHostId(Http::HdrType::X_SERVER_IP);
 
     libecap::protocolHttp.assignHostId(AnyP::PROTO_HTTP);
     libecap::protocolHttps.assignHostId(AnyP::PROTO_HTTPS);
     libecap::protocolFtp.assignHostId(AnyP::PROTO_FTP);
-    libecap::protocolGopher.assignHostId(AnyP::PROTO_GOPHER);
     libecap::protocolWais.assignHostId(AnyP::PROTO_WAIS);
     libecap::protocolUrn.assignHostId(AnyP::PROTO_URN);
     libecap::protocolWhois.assignHostId(AnyP::PROTO_WHOIS);
     protocolCacheObj.assignHostId(AnyP::PROTO_CACHE_OBJECT);
     protocolIcp.assignHostId(AnyP::PROTO_ICP);
diff --git a/src/adaptation/ecap/MessageRep.cc b/src/adaptation/ecap/MessageRep.cc
index 9e9ae699c3..82d35b038b 100644
--- a/src/adaptation/ecap/MessageRep.cc
+++ b/src/adaptation/ecap/MessageRep.cc
@@ -138,12 +138,10 @@ Adaptation::Ecap::FirstLineRep::protocol() const
         return libecap::protocolHttp;
     case AnyP::PROTO_HTTPS:
         return libecap::protocolHttps;
     case AnyP::PROTO_FTP:
         return libecap::protocolFtp;
-    case AnyP::PROTO_GOPHER:
-        return libecap::protocolGopher;
     case AnyP::PROTO_WAIS:
         return libecap::protocolWais;
     case AnyP::PROTO_WHOIS:
         return libecap::protocolWhois;
     case AnyP::PROTO_URN:
diff --git a/src/anyp/ProtocolType.h b/src/anyp/ProtocolType.h
index e736c19a39..a233da5b88 100644
--- a/src/anyp/ProtocolType.h
+++ b/src/anyp/ProtocolType.h
@@ -25,11 +25,10 @@ typedef enum {
     PROTO_HTTP,
     PROTO_FTP,
     PROTO_HTTPS,
     PROTO_COAP,
     PROTO_COAPS,
-    PROTO_GOPHER,
     PROTO_WAIS,
     PROTO_CACHE_OBJECT,
     PROTO_ICP,
 #if USE_HTCP
     PROTO_HTCP,
diff --git a/src/anyp/Uri.cc b/src/anyp/Uri.cc
index a0ad40ce02..d84b84390f 100644
--- a/src/anyp/Uri.cc
+++ b/src/anyp/Uri.cc
@@ -867,11 +867,10 @@ urlCheckRequest(const HttpRequest * r)
                 r->method == Http::METHOD_GET ||
                 r->method == Http::METHOD_HEAD )
             return true;
         return false;
 
-    case AnyP::PROTO_GOPHER:
     case AnyP::PROTO_WAIS:
     case AnyP::PROTO_WHOIS:
         if (r->method == Http::METHOD_GET ||
                 r->method == Http::METHOD_HEAD)
             return true;
diff --git a/src/anyp/UriScheme.cc b/src/anyp/UriScheme.cc
index ef8c71cc28..1a26faeab8 100644
--- a/src/anyp/UriScheme.cc
+++ b/src/anyp/UriScheme.cc
@@ -85,13 +85,10 @@ AnyP::UriScheme::defaultPort() const
     case AnyP::PROTO_COAPS:
         // coaps:// default is TBA as of draft-ietf-core-coap-08.
         // Assuming IANA policy of allocating same port for base and TLS protocol versions will occur.
         return 5683;
 
-    case AnyP::PROTO_GOPHER:
-        return 70;
-
     case AnyP::PROTO_WAIS:
         return 210;
 
     case AnyP::PROTO_CACHE_OBJECT:
         return CACHE_HTTP_PORT;
diff --git a/src/cf.data.pre b/src/cf.data.pre
index 6d22533682..63bfa3d2cb 100644
--- a/src/cf.data.pre
+++ b/src/cf.data.pre
@@ -4860,11 +4860,11 @@ DOC_START
 		[http::]<sS	Upstream object size
 
 		[http::]<bs	Number of HTTP-equivalent message body bytes
 				received from the next hop, excluding chunked
 				transfer encoding and control messages.
-				Generated FTP/Gopher listings are treated as
+				Generated FTP listings are treated as
 				received bodies.
 
 	    TIMING
 
 		[http::]<pt	Peer response time in milliseconds. The timer starts
@@ -6474,11 +6474,10 @@ CONFIG_START
 
 #
 # Add any of your own refresh_pattern entries above these.
 #
 refresh_pattern ^ftp:		1440	20%	10080
-refresh_pattern ^gopher:	1440	0%	1440
 refresh_pattern -i (/cgi-bin/|\?) 0	0%	0
 refresh_pattern .		0	20%	4320
 CONFIG_END
 DOC_END
 
@@ -9213,11 +9212,11 @@ DEFAULT_DOC: No limit.
 DOC_START
 	Set an absolute limit on the number of connections a single
 	client IP can use. Any more than this and Squid will begin to drop
 	new connections from the client until it closes some links.
 
-	Note that this is a global limit. It affects all HTTP, HTCP, Gopher and FTP
+	Note that this is a global limit. It affects all HTTP, HTCP, and FTP
 	connections from the client. For finer control use the ACL access controls.
 
 	Requires client_db to be enabled (the default).
 
 	WARNING: This may noticeably slow down traffic received via external proxies
diff --git a/src/client_side_request.cc b/src/client_side_request.cc
index aa9d6ec7db..3247d793cb 100644
--- a/src/client_side_request.cc
+++ b/src/client_side_request.cc
@@ -32,11 +32,10 @@
 #include "errorpage.h"
 #include "fd.h"
 #include "fde.h"
 #include "format/Token.h"
 #include "FwdState.h"
-#include "gopher.h"
 #include "helper.h"
 #include "helper/Reply.h"
 #include "http.h"
 #include "http/Stream.h"
 #include "HttpHdrCc.h"
@@ -905,13 +904,10 @@ clientHierarchical(ClientHttpRequest * http)
         return 0;
 
     if (request->url.getScheme() == AnyP::PROTO_HTTP)
         return method.respMaybeCacheable();
 
-    if (request->url.getScheme() == AnyP::PROTO_GOPHER)
-        return gopherCachable(request);
-
     if (request->url.getScheme() == AnyP::PROTO_CACHE_OBJECT)
         return 0;
 
     return 1;
 }
diff --git a/src/error/forward.h b/src/error/forward.h
index 6caa4cf5dc..798e853457 100644
--- a/src/error/forward.h
+++ b/src/error/forward.h
@@ -65,11 +65,11 @@ typedef enum {
 
     /* Squid problem */
     ERR_GATEWAY_FAILURE,
 
     /* Special Cases */
-    ERR_DIR_LISTING,            /* Display of remote directory (FTP, Gopher) */
+    ERR_DIR_LISTING,            /* Display of remote directory (FTP) */
     ERR_SQUID_SIGNATURE,        /* not really an error */
     ERR_SHUTTING_DOWN,
     ERR_PROTOCOL_UNKNOWN,
 
     // NOTE: error types defined below TCP_RESET are optional and do not generate
diff --git a/src/gopher.cc b/src/gopher.cc
deleted file mode 100644
index 0d882a27d0..0000000000
--- a/src/gopher.cc
+++ /dev/null
@@ -1,993 +0,0 @@
-/*
- * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-/* DEBUG: section 10    Gopher */
-
-#include "squid.h"
-#include "base/AsyncCbdataCalls.h"
-#include "comm.h"
-#include "comm/Read.h"
-#include "comm/Write.h"
-#include "errorpage.h"
-#include "fd.h"
-#include "FwdState.h"
-#include "globals.h"
-#include "gopher.h"
-#include "html_quote.h"
-#include "HttpReply.h"
-#include "HttpRequest.h"
-#include "MemBuf.h"
-#include "mime.h"
-#include "parser/Tokenizer.h"
-#include "rfc1738.h"
-#include "SquidConfig.h"
-#include "StatCounters.h"
-#include "Store.h"
-#include "tools.h"
-
-#if USE_DELAY_POOLS
-#include "DelayPools.h"
-#include "MemObject.h"
-#endif
-
-// RFC 1436 section 3.8 gopher item-type codes
-#define GOPHER_FILE         '0'
-#define GOPHER_DIRECTORY    '1'
-#define GOPHER_CSO          '2'
-#define GOPHER_ERROR        '3'
-#define GOPHER_MACBINHEX    '4'
-#define GOPHER_DOSBIN       '5'
-#define GOPHER_UUENCODED    '6'
-#define GOPHER_INDEX        '7'
-#define GOPHER_TELNET       '8'
-#define GOPHER_BIN          '9'
-#define GOPHER_REDUNT       '+'
-#define GOPHER_3270         'T'
-#define GOPHER_GIF          'g'
-#define GOPHER_IMAGE        'I'
-
-// Gopher+ section 2.9 extension types
-// https://github.com/jgoerzen/pygopherd/blob/master/doc/standards/Gopher%2B.txt
-#define GOPHER_PLUS_IMAGE   ':'
-#define GOPHER_PLUS_MOVIE   ';'
-#define GOPHER_PLUS_SOUND   '<'
-
-// non-standard item-type codes
-#define GOPHER_HTML         'h'
-#define GOPHER_INFO         'i'
-#define GOPHER_WWW          'w'
-#define GOPHER_SOUND        's'
-
-#define GOPHER_PORT         70
-
-#define TAB                 '\t'
-
-// TODO CODE: should this be a protocol-specific thing?
-#define TEMP_BUF_SIZE       4096
-
-#define MAX_CSO_RESULT      1024
-
-/**
- * Gopher Gateway Internals
- *
- * Gopher is somewhat complex and gross because it must convert from
- * the Gopher protocol to HTTP.
- */
-class GopherStateData
-{
-    CBDATA_CLASS(GopherStateData);
-
-public:
-    GopherStateData(FwdState *aFwd) :
-        entry(aFwd->entry),
-        conversion(NORMAL),
-        HTML_header_added(0),
-        HTML_pre(0),
-        type_id(GOPHER_FILE /* '0' */),
-        overflowed(false),
-        cso_recno(0),
-        len(0),
-        buf(nullptr),
-        fwd(aFwd)
-    {
-        *request = 0;
-        buf = (char *)memAllocate(MEM_4K_BUF);
-        entry->lock("gopherState");
-        *replybuf = 0;
-    }
-
-    ~GopherStateData();
-
-    /// queues or defers a read call
-    static void DelayAwareRead(GopherStateData *);
-
-    /// URL for icon to display (or nil), given the Gopher item-type code.
-    /// The returned c-string is invalidated by the next call to this function.
-    const char *iconUrl(char);
-
-public:
-    StoreEntry *entry;
-    enum {
-        NORMAL,
-        HTML_DIR,
-        HTML_INDEX_RESULT,
-        HTML_CSO_RESULT,
-        HTML_INDEX_PAGE,
-        HTML_CSO_PAGE
-    } conversion;
-    int HTML_header_added;
-    int HTML_pre;
-    char type_id;
-    char request[MAX_URL];
-
-    /// some received bytes ignored due to internal buffer capacity limits
-    bool overflowed;
-
-    int cso_recno;
-
-    /// the number of not-yet-parsed Gopher line bytes in this->buf
-    int len;
-
-    char *buf;          /* pts to a 4k page */
-    Comm::ConnectionPointer serverConn;
-    FwdState::Pointer fwd;
-    HttpReply::Pointer reply_;
-    char replybuf[BUFSIZ];
-};
-
-CBDATA_CLASS_INIT(GopherStateData);
-
-static CLCB gopherStateFree;
-static void gopherMimeCreate(GopherStateData *);
-static void gopher_request_parse(const HttpRequest * req,
-                                 char *type_id,
-                                 char *request);
-static void gopherEndHTML(GopherStateData *);
-static void gopherToHTML(GopherStateData *, char *inbuf, int len);
-static CTCB gopherTimeout;
-static IOCB gopherReadReply;
-static IOCB gopherSendComplete;
-static PF gopherSendRequest;
-
-static char def_gopher_bin[] = "www/unknown";
-
-static char def_gopher_text[] = "text/plain";
-
-static void
-gopherStateFree(const CommCloseCbParams &params)
-{
-    GopherStateData *gopherState = (GopherStateData *)params.data;
-    // Assume that FwdState is monitoring and calls noteClosure(). See XXX about
-    // Connection sharing with FwdState in gopherStart().
-    delete gopherState;
-}
-
-GopherStateData::~GopherStateData()
-{
-    if (entry)
-        entry->unlock("gopherState");
-
-    if (buf)
-        memFree(buf, MEM_4K_BUF);
-}
-
-const char *
-GopherStateData::iconUrl(const char gtype)
-{
-    switch (gtype) {
-
-    case GOPHER_DIRECTORY:
-        return mimeGetIconURL("internal-menu");
-
-    case GOPHER_HTML:
-    case GOPHER_FILE:
-        return mimeGetIconURL("internal-text");
-
-    case GOPHER_INDEX:
-    case GOPHER_CSO:
-        return mimeGetIconURL("internal-index");
-
-    case GOPHER_IMAGE:
-    case GOPHER_GIF:
-    case GOPHER_PLUS_IMAGE:
-        return mimeGetIconURL("internal-image");
-
-    case GOPHER_SOUND:
-    case GOPHER_PLUS_SOUND:
-        return mimeGetIconURL("internal-sound");
-
-    case GOPHER_PLUS_MOVIE:
-        return mimeGetIconURL("internal-movie");
-
-    case GOPHER_TELNET:
-    case GOPHER_3270:
-        return mimeGetIconURL("internal-telnet");
-
-    case GOPHER_BIN:
-
-    case GOPHER_MACBINHEX:
-    case GOPHER_DOSBIN:
-    case GOPHER_UUENCODED:
-        return mimeGetIconURL("internal-binary");
-
-    case GOPHER_INFO:
-        return nullptr;
-
-    case GOPHER_WWW:
-        return mimeGetIconURL("internal-link");
-
-    default:
-        return mimeGetIconURL("internal-unknown");
-    }
-}
-
-/**
- * Create MIME Header for Gopher Data
- */
-static void
-gopherMimeCreate(GopherStateData * gopherState)
-{
-    StoreEntry *entry = gopherState->entry;
-    const char *mime_type = nullptr;
-    const char *mime_enc = nullptr;
-
-    switch (gopherState->type_id) {
-
-    case GOPHER_DIRECTORY:
-
-    case GOPHER_INDEX:
-
-    case GOPHER_HTML:
-
-    case GOPHER_WWW:
-
-    case GOPHER_CSO:
-        mime_type = "text/html";
-        break;
-
-    case GOPHER_GIF:
-
-    case GOPHER_IMAGE:
-
-    case GOPHER_PLUS_IMAGE:
-        mime_type = "image/gif";
-        break;
-
-    case GOPHER_SOUND:
-
-    case GOPHER_PLUS_SOUND:
-        mime_type = "audio/basic";
-        break;
-
-    case GOPHER_PLUS_MOVIE:
-        mime_type = "video/mpeg";
-        break;
-
-    case GOPHER_MACBINHEX:
-
-    case GOPHER_DOSBIN:
-
-    case GOPHER_UUENCODED:
-
-    case GOPHER_BIN:
-        /* Rightnow We have no idea what it is. */
-        mime_enc = mimeGetContentEncoding(gopherState->request);
-        mime_type = mimeGetContentType(gopherState->request);
-        if (!mime_type)
-            mime_type = def_gopher_bin;
-        break;
-
-    case GOPHER_FILE:
-
-    default:
-        mime_enc = mimeGetContentEncoding(gopherState->request);
-        mime_type = mimeGetContentType(gopherState->request);
-        if (!mime_type)
-            mime_type = def_gopher_text;
-        break;
-    }
-
-    assert(entry->isEmpty());
-
-    HttpReply *reply = new HttpReply;
-    entry->buffer();
-    reply->setHeaders(Http::scOkay, "Gatewaying", mime_type, -1, -1, -2);
-    if (mime_enc)
-        reply->header.putStr(Http::HdrType::CONTENT_ENCODING, mime_enc);
-
-    entry->replaceHttpReply(reply);
-    gopherState->reply_ = reply;
-}
-
-/**
- * Parse a gopher request into components.  By Anawat.
- */
-static void
-gopher_request_parse(const HttpRequest * req, char *type_id, char *request)
-{
-    ::Parser::Tokenizer tok(req->url.path());
-
-    if (request)
-        *request = 0;
-
-    tok.skip('/'); // ignore failures? path could be ab-empty
-
-    if (tok.atEnd()) {
-        *type_id = GOPHER_DIRECTORY;
-        return;
-    }
-
-    static const CharacterSet anyByte("UTF-8",0x00, 0xFF);
-
-    SBuf typeId;
-    (void)tok.prefix(typeId, anyByte, 1); // never fails since !atEnd()
-    *type_id = typeId[0];
-
-    if (request) {
-        SBufToCstring(request, tok.remaining().substr(0, MAX_URL-1));
-        /* convert %xx to char */
-        rfc1738_unescape(request);
-    }
-}
-
-/**
- * Parse the request to determine whether it is cachable.
- *
- * \param req   Request data.
- * \retval 0    Not cachable.
- * \retval 1    Cachable.
- */
-int
-gopherCachable(const HttpRequest * req)
-{
-    int cachable = 1;
-    char type_id;
-    /* parse to see type */
-    gopher_request_parse(req,
-                         &type_id,
-                         nullptr);
-
-    switch (type_id) {
-
-    case GOPHER_INDEX:
-
-    case GOPHER_CSO:
-
-    case GOPHER_TELNET:
-
-    case GOPHER_3270:
-        cachable = 0;
-        break;
-
-    default:
-        cachable = 1;
-    }
-
-    return cachable;
-}
-
-static void
-gopherHTMLHeader(StoreEntry * e, const char *title, const char *substring)
-{
-    storeAppendPrintf(e, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
-    storeAppendPrintf(e, "<HTML><HEAD><TITLE>");
-    storeAppendPrintf(e, title, substring);
-    storeAppendPrintf(e, "</TITLE>");
-    storeAppendPrintf(e, "<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE>\n");
-    storeAppendPrintf(e, "</HEAD>\n<BODY><H1>");
-    storeAppendPrintf(e, title, substring);
-    storeAppendPrintf(e, "</H1>\n");
-}
-
-static void
-gopherHTMLFooter(StoreEntry * e)
-{
-    storeAppendPrintf(e, "<HR noshade size=\"1px\">\n");
-    storeAppendPrintf(e, "<ADDRESS>\n");
-    storeAppendPrintf(e, "Generated %s by %s (%s)\n",
-                      Time::FormatRfc1123(squid_curtime),
-                      getMyHostname(),
-                      visible_appname_string);
-    storeAppendPrintf(e, "</ADDRESS></BODY></HTML>\n");
-}
-
-static void
-gopherEndHTML(GopherStateData * gopherState)
-{
-    StoreEntry *e = gopherState->entry;
-
-    if (!gopherState->HTML_header_added) {
-        gopherHTMLHeader(e, "Server Return Nothing", nullptr);
-        storeAppendPrintf(e, "<P>The Gopher query resulted in a blank response</P>");
-    } else if (gopherState->HTML_pre) {
-        storeAppendPrintf(e, "</PRE>\n");
-    }
-
-    gopherHTMLFooter(e);
-}
-
-/**
- * Convert Gopher to HTML.
- *
- * Borrow part of code from libwww2 came with Mosaic distribution.
- */
-static void
-gopherToHTML(GopherStateData * gopherState, char *inbuf, int len)
-{
-    char *pos = inbuf;
-    char *lpos = nullptr;
-    char *tline = nullptr;
-    LOCAL_ARRAY(char, line, TEMP_BUF_SIZE);
-    char *name = nullptr;
-    char *selector = nullptr;
-    char *host = nullptr;
-    char *port = nullptr;
-    char *escaped_selector = nullptr;
-    char gtype;
-    StoreEntry *entry = nullptr;
-
-    memset(line, '\0', TEMP_BUF_SIZE);
-
-    entry = gopherState->entry;
-
-    if (gopherState->conversion == GopherStateData::HTML_INDEX_PAGE) {
-        char *html_url = html_quote(entry->url());
-        gopherHTMLHeader(entry, "Gopher Index %s", html_url);
-        storeAppendPrintf(entry,
-                          "<p>This is a searchable Gopher index. Use the search\n"
-                          "function of your browser to enter search terms.\n"
-                          "<ISINDEX>\n");
-        gopherHTMLFooter(entry);
-        /* now let start sending stuff to client */
-        entry->flush();
-        gopherState->HTML_header_added = 1;
-
-        return;
-    }
-
-    if (gopherState->conversion == GopherStateData::HTML_CSO_PAGE) {
-        char *html_url = html_quote(entry->url());
-        gopherHTMLHeader(entry, "CSO Search of %s", html_url);
-        storeAppendPrintf(entry,
-                          "<P>A CSO database usually contains a phonebook or\n"
-                          "directory.  Use the search function of your browser to enter\n"
-                          "search terms.</P><ISINDEX>\n");
-        gopherHTMLFooter(entry);
-        /* now let start sending stuff to client */
-        entry->flush();
-        gopherState->HTML_header_added = 1;
-
-        return;
-    }
-
-    SBuf outbuf;
-
-    if (!gopherState->HTML_header_added) {
-        if (gopherState->conversion == GopherStateData::HTML_CSO_RESULT)
-            gopherHTMLHeader(entry, "CSO Search Result", nullptr);
-        else
-            gopherHTMLHeader(entry, "Gopher Menu", nullptr);
-
-        outbuf.append ("<PRE>");
-
-        gopherState->HTML_header_added = 1;
-
-        gopherState->HTML_pre = 1;
-    }
-
-    while (pos < inbuf + len) {
-        int llen;
-        int left = len - (pos - inbuf);
-        lpos = (char *)memchr(pos, '\n', left);
-        if (lpos) {
-            ++lpos;             /* Next line is after \n */
-            llen = lpos - pos;
-        } else {
-            llen = left;
-        }
-        if (gopherState->len + llen >= TEMP_BUF_SIZE) {
-            debugs(10, DBG_IMPORTANT, "GopherHTML: Buffer overflow. Lost some data on URL: " << entry->url()  );
-            llen = TEMP_BUF_SIZE - gopherState->len - 1;
-            gopherState->overflowed = true; // may already be true
-        }
-        if (!lpos) {
-            /* there is no complete line in inbuf */
-            /* copy it to temp buffer */
-            /* note: llen is adjusted above */
-            memcpy(gopherState->buf + gopherState->len, pos, llen);
-            gopherState->len += llen;
-            break;
-        }
-        if (gopherState->len != 0) {
-            /* there is something left from last tx. */
-            memcpy(line, gopherState->buf, gopherState->len);
-            memcpy(line + gopherState->len, pos, llen);
-            llen += gopherState->len;
-            gopherState->len = 0;
-        } else {
-            memcpy(line, pos, llen);
-        }
-        line[llen + 1] = '\0';
-        /* move input to next line */
-        pos = lpos;
-
-        /* at this point. We should have one line in buffer to process */
-
-        if (*line == '.') {
-            /* skip it */
-            memset(line, '\0', TEMP_BUF_SIZE);
-            continue;
-        }
-
-        switch (gopherState->conversion) {
-
-        case GopherStateData::HTML_INDEX_RESULT:
-
-        case GopherStateData::HTML_DIR: {
-            tline = line;
-            gtype = *tline;
-            ++tline;
-            name = tline;
-            selector = strchr(tline, TAB);
-
-            if (selector) {
-                *selector = '\0';
-                ++selector;
-                host = strchr(selector, TAB);
-
-                if (host) {
-                    *host = '\0';
-                    ++host;
-                    port = strchr(host, TAB);
-
-                    if (port) {
-                        char *junk;
-                        port[0] = ':';
-                        junk = strchr(host, TAB);
-
-                        if (junk)
-                            *junk++ = 0;    /* Chop port */
-                        else {
-                            junk = strchr(host, '\r');
-
-                            if (junk)
-                                *junk++ = 0;    /* Chop port */
-                            else {
-                                junk = strchr(host, '\n');
-
-                                if (junk)
-                                    *junk++ = 0;    /* Chop port */
-                            }
-                        }
-
-                        if ((port[1] == '0') && (!port[2]))
-                            port[0] = 0;    /* 0 means none */
-                    }
-
-                    /* escape a selector here */
-                    escaped_selector = xstrdup(rfc1738_escape_part(selector));
-
-                    const auto icon_url = gopherState->iconUrl(gtype);
-
-                    if ((gtype == GOPHER_TELNET) || (gtype == GOPHER_3270)) {
-                        if (strlen(escaped_selector) != 0)
-                            outbuf.appendf("<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"telnet://%s@%s%s%s/\">%s</A>\n",
-                                           icon_url, escaped_selector, rfc1738_escape_part(host),
-                                           *port ? ":" : "", port, html_quote(name));
-                        else
-                            outbuf.appendf("<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"telnet://%s%s%s/\">%s</A>\n",
-                                           icon_url, rfc1738_escape_part(host), *port ? ":" : "",
-                                           port, html_quote(name));
-
-                    } else if (gtype == GOPHER_INFO) {
-                        outbuf.appendf("\t%s\n", html_quote(name));
-                    } else {
-                        if (strncmp(selector, "GET /", 5) == 0) {
-                            /* WWW link */
-                            outbuf.appendf("<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"http://%s/%s\">%s</A>\n",
-                                           icon_url, host, rfc1738_escape_unescaped(selector + 5), html_quote(name));
-                        } else if (gtype == GOPHER_WWW) {
-                            outbuf.appendf("<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"gopher://%s/%c%s\">%s</A>\n",
-                                           icon_url, rfc1738_escape_unescaped(selector), html_quote(name));
-                        } else {
-                            /* Standard link */
-                            outbuf.appendf("<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"gopher://%s/%c%s\">%s</A>\n",
-                                           icon_url, host, gtype, escaped_selector, html_quote(name));
-                        }
-                    }
-
-                    safe_free(escaped_selector);
-                } else {
-                    memset(line, '\0', TEMP_BUF_SIZE);
-                    continue;
-                }
-            } else {
-                memset(line, '\0', TEMP_BUF_SIZE);
-                continue;
-            }
-
-            break;
-            }           /* HTML_DIR, HTML_INDEX_RESULT */
-
-        case GopherStateData::HTML_CSO_RESULT: {
-            if (line[0] == '-') {
-                int code, recno;
-                char *s_code, *s_recno, *result;
-
-                s_code = strtok(line + 1, ":\n");
-                s_recno = strtok(nullptr, ":\n");
-                result = strtok(nullptr, "\n");
-
-                if (!result)
-                    break;
-
-                code = atoi(s_code);
-
-                recno = atoi(s_recno);
-
-                if (code != 200)
-                    break;
-
-                if (gopherState->cso_recno != recno) {
-                    outbuf.appendf("</PRE><HR noshade size=\"1px\"><H2>Record# %d<br><i>%s</i></H2>\n<PRE>", recno, html_quote(result));
-                    gopherState->cso_recno = recno;
-                } else {
-                    outbuf.appendf("%s\n", html_quote(result));
-                }
-
-                break;
-            } else {
-                int code;
-                char *s_code, *result;
-
-                s_code = strtok(line, ":");
-                result = strtok(nullptr, "\n");
-
-                if (!result)
-                    break;
-
-                code = atoi(s_code);
-
-                switch (code) {
-
-                case 200: {
-                    /* OK */
-                    /* Do nothing here */
-                    break;
-                }
-
-                case 102:   /* Number of matches */
-
-                case 501:   /* No Match */
-
-                case 502: { /* Too Many Matches */
-                    /* Print the message the server returns */
-                    outbuf.appendf("</PRE><HR noshade size=\"1px\"><H2>%s</H2>\n<PRE>", html_quote(result));
-                    break;
-                }
-
-                }
-            }
-
-            break;
-            }           /* HTML_CSO_RESULT */
-        default:
-            break;      /* do nothing */
-
-        }           /* switch */
-
-    }               /* while loop */
-
-    if (outbuf.length() > 0) {
-        entry->append(outbuf.rawContent(), outbuf.length());
-        /* now let start sending stuff to client */
-        entry->flush();
-    }
-
-    return;
-}
-
-static void
-gopherTimeout(const CommTimeoutCbParams &io)
-{
-    GopherStateData *gopherState = static_cast<GopherStateData *>(io.data);
-    debugs(10, 4, io.conn << ": '" << gopherState->entry->url() << "'" );
-
-    gopherState->fwd->fail(new ErrorState(ERR_READ_TIMEOUT, Http::scGatewayTimeout, gopherState->fwd->request, gopherState->fwd->al));
-
-    if (Comm::IsConnOpen(io.conn))
-        io.conn->close();
-}
-
-/**
- * This will be called when data is ready to be read from fd.
- * Read until error or connection closed.
- */
-static void
-gopherReadReply(const Comm::ConnectionPointer &conn, char *buf, size_t len, Comm::Flag flag, int xerrno, void *data)
-{
-    GopherStateData *gopherState = (GopherStateData *)data;
-    StoreEntry *entry = gopherState->entry;
-    int clen;
-    int bin;
-    size_t read_sz = BUFSIZ;
-#if USE_DELAY_POOLS
-    DelayId delayId = entry->mem_obj->mostBytesAllowed();
-#endif
-
-    /* Bail out early on Comm::ERR_CLOSING - close handlers will tidy up for us */
-
-    if (flag == Comm::ERR_CLOSING) {
-        return;
-    }
-
-    assert(buf == gopherState->replybuf);
-
-    // XXX: Should update delayId, statCounter, etc. before bailing
-    if (!entry->isAccepting()) {
-        debugs(10, 3, "terminating due to bad " << *entry);
-        // TODO: Do not abuse connection for triggering cleanup.
-        gopherState->serverConn->close();
-        return;
-    }
-
-#if USE_DELAY_POOLS
-    read_sz = delayId.bytesWanted(1, read_sz);
-#endif
-
-    /* leave one space for \0 in gopherToHTML */
-
-    if (flag == Comm::OK && len > 0) {
-#if USE_DELAY_POOLS
-        delayId.bytesIn(len);
-#endif
-
-        statCounter.server.all.kbytes_in += len;
-        statCounter.server.other.kbytes_in += len;
-    }
-
-    debugs(10, 5, conn << " read len=" << len);
-
-    if (flag == Comm::OK && len > 0) {
-        AsyncCall::Pointer nil;
-        commSetConnTimeout(conn, Config.Timeout.read, nil);
-        ++IOStats.Gopher.reads;
-
-        for (clen = len - 1, bin = 0; clen; ++bin)
-            clen >>= 1;
-
-        ++IOStats.Gopher.read_hist[bin];
-
-        HttpRequest *req = gopherState->fwd->request;
-        if (req->hier.bodyBytesRead < 0) {
-            req->hier.bodyBytesRead = 0;
-            // first bytes read, update Reply flags:
-            gopherState->reply_->sources |= Http::Message::srcGopher;
-        }
-
-        req->hier.bodyBytesRead += len;
-    }
-
-    if (flag != Comm::OK) {
-        debugs(50, DBG_IMPORTANT, "ERROR: " << MYNAME << "reading: " << xstrerr(xerrno));
-
-        if (ignoreErrno(xerrno)) {
-            AsyncCall::Pointer call = commCbCall(5,4, "gopherReadReply",
-                                                 CommIoCbPtrFun(gopherReadReply, gopherState));
-            comm_read(conn, buf, read_sz, call);
-        } else {
-            const auto err = new ErrorState(ERR_READ_ERROR, Http::scInternalServerError, gopherState->fwd->request, gopherState->fwd->al);
-            err->xerrno = xerrno;
-            gopherState->fwd->fail(err);
-            gopherState->serverConn->close();
-        }
-    } else if (len == 0 && entry->isEmpty()) {
-        gopherState->fwd->fail(new ErrorState(ERR_ZERO_SIZE_OBJECT, Http::scServiceUnavailable, gopherState->fwd->request, gopherState->fwd->al));
-        gopherState->serverConn->close();
-    } else if (len == 0) {
-        /* Connection closed; retrieval done. */
-        /* flush the rest of data in temp buf if there is one. */
-
-        if (gopherState->conversion != GopherStateData::NORMAL)
-            gopherEndHTML(gopherState);
-
-        entry->timestampsSet();
-        entry->flush();
-
-        if (!gopherState->len && !gopherState->overflowed)
-            gopherState->fwd->markStoredReplyAsWhole("gopher EOF after receiving/storing some bytes");
-
-        gopherState->fwd->complete();
-        gopherState->serverConn->close();
-    } else {
-        if (gopherState->conversion != GopherStateData::NORMAL) {
-            gopherToHTML(gopherState, buf, len);
-        } else {
-            entry->append(buf, len);
-        }
-        GopherStateData::DelayAwareRead(gopherState);
-    }
-}
-
-void
-GopherStateData::DelayAwareRead(GopherStateData *gopherState)
-{
-    const auto &conn = gopherState->serverConn;
-
-    if (!Comm::IsConnOpen(conn) || fd_table[conn->fd].closing()) {
-        debugs(10, 3, "will not read from " << conn);
-        return;
-    }
-
-    const auto amountToRead = gopherState->entry->bytesWanted(Range<size_t>(0, BUFSIZ));
-
-    if (amountToRead <= 0) {
-        AsyncCall::Pointer delayCall = asyncCall(10, 3, "GopherStateData::DelayAwareRead",
-                                       cbdataDialer(&GopherStateData::DelayAwareRead, gopherState));
-        gopherState->entry->mem().delayRead(delayCall);
-        return;
-    }
-
-    AsyncCall::Pointer readCall = commCbCall(5, 5, "gopherReadReply", CommIoCbPtrFun(gopherReadReply, gopherState));
-    comm_read(conn, gopherState->replybuf, amountToRead, readCall);
-}
-
-/**
- * This will be called when request write is complete. Schedule read of reply.
- */
-static void
-gopherSendComplete(const Comm::ConnectionPointer &conn, char *, size_t size, Comm::Flag errflag, int xerrno, void *data)
-{
-    GopherStateData *gopherState = (GopherStateData *) data;
-    StoreEntry *entry = gopherState->entry;
-    debugs(10, 5, conn << " size: " << size << " errflag: " << errflag);
-
-    if (size > 0) {
-        fd_bytes(conn->fd, size, FD_WRITE);
-        statCounter.server.all.kbytes_out += size;
-        statCounter.server.other.kbytes_out += size;
-    }
-
-    if (!entry->isAccepting()) {
-        debugs(10, 3, "terminating due to bad " << *entry);
-        // TODO: Do not abuse connection for triggering cleanup.
-        gopherState->serverConn->close();
-        return;
-    }
-
-    if (errflag) {
-        const auto err = new ErrorState(ERR_WRITE_ERROR, Http::scServiceUnavailable, gopherState->fwd->request, gopherState->fwd->al);
-        err->xerrno = xerrno;
-        err->port = gopherState->fwd->request->url.port();
-        err->url = xstrdup(entry->url());
-        gopherState->fwd->fail(err);
-        gopherState->serverConn->close();
-        return;
-    }
-
-    /*
-     * OK. We successfully reach remote site.  Start MIME typing
-     * stuff.  Do it anyway even though request is not HTML type.
-     */
-    entry->buffer();
-
-    gopherMimeCreate(gopherState);
-
-    switch (gopherState->type_id) {
-
-    case GOPHER_DIRECTORY:
-        /* we got to convert it first */
-        gopherState->conversion = GopherStateData::HTML_DIR;
-        gopherState->HTML_header_added = 0;
-        break;
-
-    case GOPHER_INDEX:
-        /* we got to convert it first */
-        gopherState->conversion = GopherStateData::HTML_INDEX_RESULT;
-        gopherState->HTML_header_added = 0;
-        break;
-
-    case GOPHER_CSO:
-        /* we got to convert it first */
-        gopherState->conversion = GopherStateData::HTML_CSO_RESULT;
-        gopherState->cso_recno = 0;
-        gopherState->HTML_header_added = 0;
-        break;
-
-    default:
-        gopherState->conversion = GopherStateData::NORMAL;
-        entry->flush();
-    }
-
-    GopherStateData::DelayAwareRead(gopherState);
-}
-
-/**
- * This will be called when connect completes. Write request.
- */
-static void
-gopherSendRequest(int, void *data)
-{
-    GopherStateData *gopherState = (GopherStateData *)data;
-    MemBuf mb;
-    mb.init();
-
-    if (gopherState->type_id == GOPHER_CSO) {
-        const char *t = strchr(gopherState->request, '?');
-
-        if (t)
-            ++t;        /* skip the ? */
-        else
-            t = "";
-
-        mb.appendf("query %s\r\nquit", t);
-    } else {
-        if (gopherState->type_id == GOPHER_INDEX) {
-            if (char *t = strchr(gopherState->request, '?'))
-                *t = '\t';
-        }
-        mb.append(gopherState->request, strlen(gopherState->request));
-    }
-    mb.append("\r\n", 2);
-
-    debugs(10, 5, gopherState->serverConn);
-    AsyncCall::Pointer call = commCbCall(5,5, "gopherSendComplete",
-                                         CommIoCbPtrFun(gopherSendComplete, gopherState));
-    Comm::Write(gopherState->serverConn, &mb, call);
-
-    if (!gopherState->entry->makePublic())
-        gopherState->entry->makePrivate(true);
-}
-
-void
-gopherStart(FwdState * fwd)
-{
-    GopherStateData *gopherState = new GopherStateData(fwd);
-
-    debugs(10, 3, gopherState->entry->url());
-
-    ++ statCounter.server.all.requests;
-
-    ++ statCounter.server.other.requests;
-
-    /* Parse url. */
-    gopher_request_parse(fwd->request,
-                         &gopherState->type_id, gopherState->request);
-
-    comm_add_close_handler(fwd->serverConnection()->fd, gopherStateFree, gopherState);
-
-    if (((gopherState->type_id == GOPHER_INDEX) || (gopherState->type_id == GOPHER_CSO))
-            && (strchr(gopherState->request, '?') == nullptr)) {
-        /* Index URL without query word */
-        /* We have to generate search page back to client. No need for connection */
-        gopherMimeCreate(gopherState);
-
-        if (gopherState->type_id == GOPHER_INDEX) {
-            gopherState->conversion = GopherStateData::HTML_INDEX_PAGE;
-        } else {
-            if (gopherState->type_id == GOPHER_CSO) {
-                gopherState->conversion = GopherStateData::HTML_CSO_PAGE;
-            } else {
-                gopherState->conversion = GopherStateData::HTML_INDEX_PAGE;
-            }
-        }
-
-        gopherToHTML(gopherState, (char *) nullptr, 0);
-        fwd->markStoredReplyAsWhole("gopher instant internal request satisfaction");
-        fwd->complete();
-        return;
-    }
-
-    // XXX: Sharing open Connection with FwdState that has its own handlers/etc.
-    gopherState->serverConn = fwd->serverConnection();
-    gopherSendRequest(fwd->serverConnection()->fd, gopherState);
-    AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "gopherTimeout",
-                                     CommTimeoutCbPtrFun(gopherTimeout, gopherState));
-    commSetConnTimeout(fwd->serverConnection(), Config.Timeout.read, timeoutCall);
-}
-
diff --git a/src/gopher.h b/src/gopher.h
deleted file mode 100644
index f63504bc6f..0000000000
--- a/src/gopher.h
+++ /dev/null
@@ -1,29 +0,0 @@
-/*
- * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-/* DEBUG: section 10    Gopher */
-
-#ifndef SQUID_GOPHER_H_
-#define SQUID_GOPHER_H_
-
-class FwdState;
-class HttpRequest;
-
-/**
- \defgroup ServerProtocolGopherAPI Server-Side Gopher API
- \ingroup ServerProtocol
- */
-
-/// \ingroup ServerProtocolGopherAPI
-void gopherStart(FwdState *);
-
-/// \ingroup ServerProtocolGopherAPI
-int gopherCachable(const HttpRequest *);
-
-#endif /* SQUID_GOPHER_H_ */
-
diff --git a/src/http/Message.h b/src/http/Message.h
index 11a9312140..a06c34e6d9 100644
--- a/src/http/Message.h
+++ b/src/http/Message.h
@@ -38,11 +38,10 @@ public:
         /* these flags "taint" the message: it may have been observed or mangled outside Squid */
         srcHttp = 1 << (16 + 0), ///< http_port or HTTP server
         srcFtp = 1 << (16 + 1), ///< ftp_port or FTP server
         srcIcap = 1 << (16 + 2), ///< traditional ICAP service without encryption
         srcEcap = 1 << (16 + 3), ///< eCAP service that uses insecure libraries/daemons
-        srcGopher = 1 << (16 + 14), ///< Gopher server
         srcWhois = 1 << (16 + 15), ///< Whois server
         srcUnsafe = 0xFFFF0000,  ///< Unsafe sources mask
         srcSafe = 0x0000FFFF ///< Safe sources mask
     };
 
diff --git a/src/mgr/IoAction.cc b/src/mgr/IoAction.cc
index 5c3cedaf90..ead2ef4fe5 100644
--- a/src/mgr/IoAction.cc
+++ b/src/mgr/IoAction.cc
@@ -33,13 +33,10 @@ Mgr::IoActionData::operator += (const IoActionData& stats)
     for (int i = 0; i < IoStats::histSize; ++i)
         http_read_hist[i] += stats.http_read_hist[i];
     ftp_reads += stats.ftp_reads;
     for (int i = 0; i < IoStats::histSize; ++i)
         ftp_read_hist[i] += stats.ftp_read_hist[i];
-    gopher_reads += stats.gopher_reads;
-    for (int i = 0; i < IoStats::histSize; ++i)
-        gopher_read_hist[i] += stats.gopher_read_hist[i];
 
     return *this;
 }
 
 Mgr::IoAction::Pointer
diff --git a/src/mgr/IoAction.h b/src/mgr/IoAction.h
index 7e9ad1a7a6..dc4a3a5ebc 100644
--- a/src/mgr/IoAction.h
+++ b/src/mgr/IoAction.h
@@ -25,14 +25,12 @@ public:
     IoActionData& operator += (const IoActionData& stats);
 
 public:
     double http_reads;
     double ftp_reads;
-    double gopher_reads;
     double http_read_hist[IoStats::histSize];
     double ftp_read_hist[IoStats::histSize];
-    double gopher_read_hist[IoStats::histSize];
 };
 
 /// implement aggregated 'io' action
 class IoAction: public Action
 {
diff --git a/src/squid.8.in b/src/squid.8.in
index 7b5bd8ad0f..9999e2e55e 100644
--- a/src/squid.8.in
+++ b/src/squid.8.in
@@ -23,11 +23,11 @@ command\-line
 .
 .SH DESCRIPTION
 .PP
 .B squid
 is a high\-performance proxy caching server for web clients,
-supporting FTP, gopher, ICAP, ICP, HTCP and HTTP data objects.
+supporting FTP, ICAP, ICP, HTCP and HTTP data objects.
 Unlike traditional caching software, 
 Squid handles all requests in a single, non-blocking process.
 .PP
 Squid keeps meta data and especially hot objects cached in RAM, 
 caches DNS lookups, supports non\-blocking DNS lookups, and implements
diff --git a/src/stat.cc b/src/stat.cc
index 9e78a86204..c17a50133f 100644
--- a/src/stat.cc
+++ b/src/stat.cc
@@ -198,16 +198,10 @@ GetIoStats(Mgr::IoActionData& stats)
     stats.ftp_reads = IOStats.Ftp.reads;
 
     for (i = 0; i < IoStats::histSize; ++i) {
         stats.ftp_read_hist[i] = IOStats.Ftp.read_hist[i];
     }
-
-    stats.gopher_reads = IOStats.Gopher.reads;
-
-    for (i = 0; i < IoStats::histSize; ++i) {
-        stats.gopher_read_hist[i] = IOStats.Gopher.read_hist[i];
-    }
 }
 
 void
 DumpIoStats(Mgr::IoActionData& stats, StoreEntry* sentry)
 {
@@ -236,23 +230,10 @@ DumpIoStats(Mgr::IoActionData& stats, StoreEntry* sentry)
                           1 << i,
                           stats.ftp_read_hist[i],
                           Math::doublePercent(stats.ftp_read_hist[i], stats.ftp_reads));
     }
 
-    storeAppendPrintf(sentry, "\n");
-    storeAppendPrintf(sentry, "Gopher I/O\n");
-    storeAppendPrintf(sentry, "number of reads: %.0f\n", stats.gopher_reads);
-    storeAppendPrintf(sentry, "Read Histogram:\n");
-
-    for (i = 0; i < IoStats::histSize; ++i) {
-        storeAppendPrintf(sentry, "%5d-%5d: %9.0f %2.0f%%\n",
-                          i ? (1 << (i - 1)) + 1 : 1,
-                          1 << i,
-                          stats.gopher_read_hist[i],
-                          Math::doublePercent(stats.gopher_read_hist[i], stats.gopher_reads));
-    }
-
     storeAppendPrintf(sentry, "\n");
 }
 
 static const char *
 describeStatuses(const StoreEntry * entry)
diff --git a/src/tests/Stub.am b/src/tests/Stub.am
index 9df760ebb5..d7e5c10a4a 100644
--- a/src/tests/Stub.am
+++ b/src/tests/Stub.am
@@ -45,11 +45,10 @@ STUB_SOURCE = \
 	tests/stub_event.cc \
 	tests/stub_external_acl.cc \
 	tests/stub_fatal.cc \
 	tests/stub_fd.cc \
 	tests/stub_fqdncache.cc \
-	tests/stub_gopher.cc \
 	tests/stub_helper.cc \
 	tests/stub_http.cc \
 	tests/stub_icp.cc \
 	tests/stub_internal.cc \
 	tests/stub_ipc.cc \
diff --git a/src/tests/stub_gopher.cc b/src/tests/stub_gopher.cc
deleted file mode 100644
index 2dc56b9b38..0000000000
--- a/src/tests/stub_gopher.cc
+++ /dev/null
@@ -1,17 +0,0 @@
-/*
- * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
- *
- * Squid software is distributed under GPLv2+ license and includes
- * contributions from numerous individuals and organizations.
- * Please see the COPYING and CONTRIBUTORS files for details.
- */
-
-#include "squid.h"
-
-#define STUB_API "gopher.cc"
-#include "tests/STUB.h"
-
-#include "gopher.h"
-void gopherStart(FwdState *) STUB
-int gopherCachable(const HttpRequest *) STUB
-
diff --git a/test-suite/squidconf/regressions-3.4.0.1 b/test-suite/squidconf/regressions-3.4.0.1
index 853a8488c9..59238c6863 100644
--- a/test-suite/squidconf/regressions-3.4.0.1
+++ b/test-suite/squidconf/regressions-3.4.0.1
@@ -42,8 +42,7 @@ refresh_pattern http://ftp\.software\.invalid/ 0 80% 20160
 refresh_pattern -i \.mp4 1440 90% 43200
 refresh_pattern -i \.(gif|png|jpg|jpeg|ico)$ 40320 75% 86400
 refresh_pattern -i \.(iso|avi|wav|mp3|mpeg|swf|flv|x-flv)$ 1440 40% 40320
 
 refresh_pattern ^ftp:           1440    20%     10080
-refresh_pattern ^gopher:        1440    0%      1440
 refresh_pattern -i (/cgi-bin/|\?)       0       0%      0
 refresh_pattern .       0       20%     4320

Vulnerable code:  *
 \par
 *   Wais and Gopher don't receive much
 *   attention because they comprise a relatively insignificant
 *   portion of Internet traffic.
section 10    Gopher
"welcher FTP, gopher, ICAP, ICP, HTCP and HTTP Data Objects unterstützt. Im "
"supporting FTP, gopher, ICAP, ICP, HTCP and HTTP data objects.  Unlike "
"supporting FTP, gopher, ICAP, ICP, HTCP and HTTP data objects.  Unlike "
"soporta FTP, gopher, ICAP, ICP, HTCP y objetos de datos HTTP. A diferencia "
"le web, il supporte les protocoles FTP, Gopher, ICAP, ICP, HTCP et HTTP pour "
"supporto supporta i protocolli FTP, gopher, ICAP, ICP, HTCP e HTTP. A "
"Squid ondersteun nie alle navraagmetodes vir alle toegangsprotokolle nie. "
"Mens kan by voorbeeld nie 'n Gopher-navraag POST nie."
"Squid ondersteun nie alle navraagmetodes vir alle toegangsprotokolle nie. "
"Mens kan by voorbeeld nie 'n Gopher-navraag POST nie."
"Squid bütün sorğu metodları və bütün protokollardəstəkləmir. Məsələn, Gopher "
"protokolu üzrə siz POST sorğu metodunu yerinə yetirə bilməzsiniz."
"Squid bütün sorğu metodları və bütün protokollardəstəkləmir. Məsələn, Gopher "
"protokolu üzrə siz POST sorğu metodunu yerinə yetirə bilməzsiniz."
"Кеш сървърът не поддържа всички методи на заявка за всички протоколи. "
"Например, не можете да заявите метод POST за протокол Gopher."
"Кеш сървърът не поддържа всички методи на заявка за всички протоколи. "
"Например, не можете да заявите метод POST за протокол Gopher."
"protocols. Per exemple, no es pot fer una petici&oacute; tipus POST en el "
"protocol Gopher."
"protocols. Per exemple, no es pot fer una petici&oacute; tipus POST en el "
"protocol Gopher."
"Squid nepodporuje všechny typy metod u všech protokolů. Např. není možno "
"použit metodu POST u služby GOPHER."
"Squid nepodporuje všechny typy metod u všech protokolů. Např. není možno "
"použit metodu POST u služby GOPHER."
"alle adgangs protokoller. For eksempel kan du ikke POST en Gopher "
"foresp&oslash;rgsel."
"alle adgangs protokoller. For eksempel kan du ikke POST en Gopher "
"foresp&oslash;rgsel."
"Squid unterstützt nicht alle Anfragemethoden für alle Protokolle. Sie können "
"zum Beispiel keine POST Anfrage über das Gopher Protokoll senden."
"Squid unterstützt nicht alle Anfragemethoden für alle Protokolle. Sie können "
"zum Beispiel keine POST Anfrage über das Gopher Protokoll senden."
"πρόσβασης. Για παράδειγμα, το POST για Gopher δεν υποστηρίζεται."
"πρόσβασης. Για παράδειγμα, το POST για Gopher δεν υποστηρίζεται."
"Squid does not support all request methods for all access protocols. For "
"example, you can not POST a Gopher request."
"Squid does not support all request methods for all access protocols. For "
"example, you can not POST a Gopher request."
/* special event: FTP / Gopher directory listing */
"acceso. Por ejemplo, no es posible hacer una solcitud POST a un servidor "
"Gopher."
"acceso. Por ejemplo, no se puede hacer un POST a un servidor Gopher."
"acceso. Por ejemplo, no se puede hacer un POST a un servidor Gopher."
"protokollidega. N&auml;iteks, te ei saa teha POST operatsiooni Gopher p&auml;"
"ringus."
"protokollidega. N&auml;iteks, te ei saa teha POST operatsiooni Gopher p&auml;"
"ringus."
"Squid ei tue kaikkia hakupyynnon tyyppejä kaikilla protokollilla. Et voi "
"esimerkiksi käyttää POST-pyyntöä gopherilla."
"Squid ei tue kaikkia hakupyynnon tyyppejä kaikilla protokollilla. Et voi "
"esimerkiksi käyttää POST-pyyntöä gopherilla."
"les protocoles d'accès. Vous ne pouvez pas par exemple utiliser une requête "
"POST avec le protocole Gopher."
"les protocoles d'accès. Vous ne pouvez pas par exemple utiliser une requête "
"POST avec le protocole Gopher."
"שרת ה Squid אינו תומך בכל שיטות הבקשה לכל הפרוטוקולים. לדוגמא אינך יכול "
"לשלוח בקשת POST ב-Gopher."
"שרת ה Squid אינו תומך בכל שיטות הבקשה לכל הפרוטוקולים. לדוגמא אינך יכול "
"לשלוח בקשת POST ב-Gopher."
"kombinációt, így pl. nem lehet POST kéréstípust használni egy Gopher "
"kérésben."
"kombinációt, így pl. nem lehet POST kéréstípust használni egy Gopher "
"kérésben."
"Squid-ը բոլոր արձանագրությունների համար բոլոր հարցման մեթոդները չի աջակցում. "
"Օրինակ, Gopher արձանագրության համար չեք կարող POST հարցում կատարել."
"Squid-ը բոլոր արձանագրությունների համար բոլոր հարցման մեթոդները չի աջակցում. "
"Օրինակ, Gopher արձանագրության համար չեք կարող POST հարցում կատարել."
"protocollo (a esempio non consente una richiesta POST su protocollo Gopher)."
"protocollo (a esempio non consente una richiesta POST su protocollo Gopher)."
"ポートしているわけではありません。例えば、POSTをGopherのリクエストで行うこと"
"はできません。"
"ポートしているわけではありません。例えば、POSTをGopherのリクエストで行うこと"
"はできません。"
"Squid는 모든 접속 프로토콜에 대한 request method를 지원하지 않습니다. 한가지 "
"예로, Gopher에서 POST request를 사용할 수 없습니다."
"Squid는 모든 접속 프로토콜에 대한 request method를 지원하지 않습니다. 한가지 "
"예로, Gopher에서 POST request를 사용할 수 없습니다."
"Squid palaiko ne visus užklausos metodus daliai protokolų. Pavyzdžiui, jūs "
"negalite vykdyti POST Gopher tipo užklausoje."
"Squid palaiko ne visus užklausos metodus daliai protokolų. Pavyzdžiui, jūs "
"negalite vykdyti POST Gopher tipo užklausoje."
"Squid neatbalsta visas pieprasījuma metodes visiem protokoliem. Piemēram, "
"Jūs nevarat veikt POST pieprasījumu izmantojot Gopher protokolu."
"Squid neatbalsta visas pieprasījuma metodes visiem protokoliem. Piemēram, "
"Jūs nevarat veikt POST pieprasījumu izmantojot Gopher protokolu."
"Squid ondersteunt niet alle verzoekmethoden voor alle toegangsprotocollen. U "
"kunt bijvoorbeeld geen Gopher verzoek POSTen."
"Squid ondersteunt niet alle verzoekmethoden voor alle toegangsprotocollen. U "
"kunt bijvoorbeeld geen Gopher verzoek POSTen."
"Squid nie wspiera wszystkich metod we wszystkich protokołach. Na przykład "
"nie możesz użyć metody POST w żądaniu skierowanym do usługi Gopher."
"Squid nie wspiera wszystkich metod we wszystkich protokołach. Na przykład "
"nie możesz użyć metody POST w żądaniu skierowanym do usługi Gopher."
"acesso. Por exemplo, você não pode emitir uma requisição POST ao protocolo "
"Gopher."
"acesso. Por exemplo, você não pode emitir uma requisição POST ao protocolo "
"Gopher."
"Squid não suporta todos os métodos para todos os protocolos. Por exemplo: "
"você não pode fazer cache de Gopher."
"Squid não suporta todos os métodos para todos os protocolos. Por exemplo: "
"você não pode fazer cache de Gopher."
"acces. De exemplu, nu puteţi face o cerere de tip POST pentru Gopher."
"acces. De exemplu, nu puteţi face o cerere de tip POST pentru Gopher."
"Squid не поддерживает все методы запросов для всех протоколов. К примеру, "
"для протокола Gopher Вы не можете выполнить запрос POST."
"Squid не поддерживает все методы запросов для всех протоколов. К примеру, "
"для протокола Gopher Вы не можете выполнить запрос POST."
"Squid nepodporuje všetky typy metód pri všetkých protokoloch. Napríklad: nie "
"je možné použiť metódu POST pri službe Gopher."
"Squid nepodporuje všetky typy metód pri všetkých protokoloch. Napríklad: nie "
"je možné použiť metódu POST pri službe Gopher."
"Squid ne podpira vseh metod zahtev za vse protokole dostopa. Tako npr. "
"metode POST ne morete uporabiti za zahtevo Gopher."
"Squid ne podpira vseh metod zahtev za vse protokole dostopa. Tako npr. "
"metode POST ne morete uporabiti za zahtevo Gopher."
"protokole. Na primer ne možete da uradite POST na Gopher zahtev."
"protokole. Na primer ne možete da uradite POST na Gopher zahtev."
"Squid st&ouml;djer inte alla fr&aring;gemetoder f&ouml;r alla protokoll. "
"Till exempel, Ni kan inte POST'a en Gopher f&ouml;rfr&aring;gan."
"Squid st&ouml;djer inte alla fr&aring;gemetoder f&ouml;r alla protokoll. "
"Till exempel, Ni kan inte POST'a en Gopher f&ouml;rfr&aring;gan."
<p>Squid does not support all request methods for all access protocols. For example, you can not POST a Gopher request.</p>
"Squid, bazı erişim protokollerin, bazı istek yöntemlerini desteklemiyor. "
"Örneğin Gopher isteğinizde POST yapamazsınız."
"Squid, bazı erişim protokollerin, bazı istek yöntemlerini desteklemiyor. "
"Örneğin Gopher isteğinizde POST yapamazsınız."
"Squid не підтримує всі методи запитів для всіх наявних протоколів. Як "
"приклад, Ви не можете виконати запит POST для протоколу Gopher."
"Squid не підтримує всі методи запитів для всіх наявних протоколів. Як "
"приклад, Ви не можете виконати запит POST для протоколу Gopher."
"cập. Chẳng hạn, bạn không có khả năng POST một yêu cầu Gopher."
"cập. Chẳng hạn, bạn không có khả năng POST một yêu cầu Gopher."
"Squid （缓存服务器）不能对所有的存取协议支持所有的请求方式。比如说，你不能对 "
"GOPHER 进行一个 POST 请求。"
"Squid （缓存服务器）不能对所有的存取协议支持所有的请求方式。比如说，你不能对 "
"GOPHER 进行一个 POST 请求。"
"因為 Squid （網路快取程式）並未支援所有的連結要求方式在各式通訊協定上。比如"
"說，你不能要求一個 GOPHER 的 POST 連結要求。"
"因為 Squid （網路快取程式）並未支援所有的連結要求方式在各式通訊協定上。比如"
"說，你不能要求一個 GOPHER 的 POST 連結要求。"
#include "gopher.h"
        case AnyP::PROTO_GOPHER:
            gopherStart(this);
            break;

#include "gopher.h"
    case AnyP::PROTO_GOPHER:
        if (!gopherCachable(this))
            return false;
        break;

    Http, Ftp, Gopher;
	gopher.cc \
	gopher.h \
	tests/stub_gopher.cc \
	gopher.h \
	tests/stub_gopher.cc \
	gopher.h \
	tests/stub_gopher.cc \
	gopher.h \
    libecap::protocolGopher.assignHostId(AnyP::PROTO_GOPHER);
    case AnyP::PROTO_GOPHER:
        return libecap::protocolGopher;
    PROTO_GOPHER,
    case AnyP::PROTO_GOPHER:
    case AnyP::PROTO_GOPHER:
        return 70;

				Generated FTP/Gopher listings are treated as
refresh_pattern ^gopher:	1440	0%	1440
	Note that this is a global limit. It affects all HTTP, HTCP, Gopher and FTP
#include "gopher.h"
    if (request->url.getScheme() == AnyP::PROTO_GOPHER)
        return gopherCachable(request);

    ERR_DIR_LISTING,            /* Display of remote directory (FTP, Gopher) */
/*
 * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
 *
 * Squid software is distributed under GPLv2+ license and includes
 * contributions from numerous individuals and organizations.
 * Please see the COPYING and CONTRIBUTORS files for details.
 */

/* DEBUG: section 10    Gopher */

#include "squid.h"
#include "base/AsyncCbdataCalls.h"
#include "comm.h"
#include "comm/Read.h"
#include "comm/Write.h"
#include "errorpage.h"
#include "fd.h"
#include "FwdState.h"
#include "globals.h"
#include "gopher.h"
#include "html_quote.h"
#include "HttpReply.h"
#include "HttpRequest.h"
#include "MemBuf.h"
#include "mime.h"
#include "parser/Tokenizer.h"
#include "rfc1738.h"
#include "SquidConfig.h"
#include "StatCounters.h"
#include "Store.h"
#include "tools.h"

#if USE_DELAY_POOLS
#include "DelayPools.h"
#include "MemObject.h"
#endif

// RFC 1436 section 3.8 gopher item-type codes
#define GOPHER_FILE         '0'
#define GOPHER_DIRECTORY    '1'
#define GOPHER_CSO          '2'
#define GOPHER_ERROR        '3'
#define GOPHER_MACBINHEX    '4'
#define GOPHER_DOSBIN       '5'
#define GOPHER_UUENCODED    '6'
#define GOPHER_INDEX        '7'
#define GOPHER_TELNET       '8'
#define GOPHER_BIN          '9'
#define GOPHER_REDUNT       '+'
#define GOPHER_3270         'T'
#define GOPHER_GIF          'g'
#define GOPHER_IMAGE        'I'

// Gopher+ section 2.9 extension types
// https://github.com/jgoerzen/pygopherd/blob/master/doc/standards/Gopher%2B.txt
#define GOPHER_PLUS_IMAGE   ':'
#define GOPHER_PLUS_MOVIE   ';'
#define GOPHER_PLUS_SOUND   '<'

// non-standard item-type codes
#define GOPHER_HTML         'h'
#define GOPHER_INFO         'i'
#define GOPHER_WWW          'w'
#define GOPHER_SOUND        's'

#define GOPHER_PORT         70

#define TAB                 '\t'

// TODO CODE: should this be a protocol-specific thing?
#define TEMP_BUF_SIZE       4096

#define MAX_CSO_RESULT      1024

/**
 * Gopher Gateway Internals
 *
 * Gopher is somewhat complex and gross because it must convert from
 * the Gopher protocol to HTTP.
 */
class GopherStateData
{
    CBDATA_CLASS(GopherStateData);

public:
    GopherStateData(FwdState *aFwd) :
        entry(aFwd->entry),
        conversion(NORMAL),
        HTML_header_added(0),
        HTML_pre(0),
        type_id(GOPHER_FILE /* '0' */),
        overflowed(false),
        cso_recno(0),
        len(0),
        buf(nullptr),
        fwd(aFwd)
    {
        *request = 0;
        buf = (char *)memAllocate(MEM_4K_BUF);
        entry->lock("gopherState");
        *replybuf = 0;
    }

    ~GopherStateData();

    /// queues or defers a read call
    static void DelayAwareRead(GopherStateData *);

    /// URL for icon to display (or nil), given the Gopher item-type code.
    /// The returned c-string is invalidated by the next call to this function.
    const char *iconUrl(char);

public:
    StoreEntry *entry;
    enum {
        NORMAL,
        HTML_DIR,
        HTML_INDEX_RESULT,
        HTML_CSO_RESULT,
        HTML_INDEX_PAGE,
        HTML_CSO_PAGE
    } conversion;
    int HTML_header_added;
    int HTML_pre;
    char type_id;
    char request[MAX_URL];

    /// some received bytes ignored due to internal buffer capacity limits
    bool overflowed;

    int cso_recno;

    /// the number of not-yet-parsed Gopher line bytes in this->buf
    int len;

    char *buf;          /* pts to a 4k page */
    Comm::ConnectionPointer serverConn;
    FwdState::Pointer fwd;
    HttpReply::Pointer reply_;
    char replybuf[BUFSIZ];
};

CBDATA_CLASS_INIT(GopherStateData);

static CLCB gopherStateFree;
static void gopherMimeCreate(GopherStateData *);
static void gopher_request_parse(const HttpRequest * req,
                                 char *type_id,
                                 char *request);
static void gopherEndHTML(GopherStateData *);
static void gopherToHTML(GopherStateData *, char *inbuf, int len);
static CTCB gopherTimeout;
static IOCB gopherReadReply;
static IOCB gopherSendComplete;
static PF gopherSendRequest;

static char def_gopher_bin[] = "www/unknown";

static char def_gopher_text[] = "text/plain";

static void
gopherStateFree(const CommCloseCbParams &params)
{
    GopherStateData *gopherState = (GopherStateData *)params.data;
    // Assume that FwdState is monitoring and calls noteClosure(). See XXX about
    // Connection sharing with FwdState in gopherStart().
    delete gopherState;
}

GopherStateData::~GopherStateData()
{
    if (entry)
        entry->unlock("gopherState");

    if (buf)
        memFree(buf, MEM_4K_BUF);
}

const char *
GopherStateData::iconUrl(const char gtype)
{
    switch (gtype) {

    case GOPHER_DIRECTORY:
        return mimeGetIconURL("internal-menu");

    case GOPHER_HTML:
    case GOPHER_FILE:
        return mimeGetIconURL("internal-text");

    case GOPHER_INDEX:
    case GOPHER_CSO:
        return mimeGetIconURL("internal-index");

    case GOPHER_IMAGE:
    case GOPHER_GIF:
    case GOPHER_PLUS_IMAGE:
        return mimeGetIconURL("internal-image");

    case GOPHER_SOUND:
    case GOPHER_PLUS_SOUND:
        return mimeGetIconURL("internal-sound");

    case GOPHER_PLUS_MOVIE:
        return mimeGetIconURL("internal-movie");

    case GOPHER_TELNET:
    case GOPHER_3270:
        return mimeGetIconURL("internal-telnet");

    case GOPHER_BIN:

    case GOPHER_MACBINHEX:
    case GOPHER_DOSBIN:
    case GOPHER_UUENCODED:
        return mimeGetIconURL("internal-binary");

    case GOPHER_INFO:
        return nullptr;

    case GOPHER_WWW:
        return mimeGetIconURL("internal-link");

    default:
        return mimeGetIconURL("internal-unknown");
    }
}

/**
 * Create MIME Header for Gopher Data
 */
static void
gopherMimeCreate(GopherStateData * gopherState)
{
    StoreEntry *entry = gopherState->entry;
    const char *mime_type = nullptr;
    const char *mime_enc = nullptr;

    switch (gopherState->type_id) {

    case GOPHER_DIRECTORY:

    case GOPHER_INDEX:

    case GOPHER_HTML:

    case GOPHER_WWW:

    case GOPHER_CSO:
        mime_type = "text/html";
        break;

    case GOPHER_GIF:

    case GOPHER_IMAGE:

    case GOPHER_PLUS_IMAGE:
        mime_type = "image/gif";
        break;

    case GOPHER_SOUND:

    case GOPHER_PLUS_SOUND:
        mime_type = "audio/basic";
        break;

    case GOPHER_PLUS_MOVIE:
        mime_type = "video/mpeg";
        break;

    case GOPHER_MACBINHEX:

    case GOPHER_DOSBIN:

    case GOPHER_UUENCODED:

    case GOPHER_BIN:
        /* Rightnow We have no idea what it is. */
        mime_enc = mimeGetContentEncoding(gopherState->request);
        mime_type = mimeGetContentType(gopherState->request);
        if (!mime_type)
            mime_type = def_gopher_bin;
        break;

    case GOPHER_FILE:

    default:
        mime_enc = mimeGetContentEncoding(gopherState->request);
        mime_type = mimeGetContentType(gopherState->request);
        if (!mime_type)
            mime_type = def_gopher_text;
        break;
    }

    assert(entry->isEmpty());

    HttpReply *reply = new HttpReply;
    entry->buffer();
    reply->setHeaders(Http::scOkay, "Gatewaying", mime_type, -1, -1, -2);
    if (mime_enc)
        reply->header.putStr(Http::HdrType::CONTENT_ENCODING, mime_enc);

    entry->replaceHttpReply(reply);
    gopherState->reply_ = reply;
}

/**
 * Parse a gopher request into components.  By Anawat.
 */
static void
gopher_request_parse(const HttpRequest * req, char *type_id, char *request)
{
    ::Parser::Tokenizer tok(req->url.path());

    if (request)
        *request = 0;

    tok.skip('/'); // ignore failures? path could be ab-empty

    if (tok.atEnd()) {
        *type_id = GOPHER_DIRECTORY;
        return;
    }

    static const CharacterSet anyByte("UTF-8",0x00, 0xFF);

    SBuf typeId;
    (void)tok.prefix(typeId, anyByte, 1); // never fails since !atEnd()
    *type_id = typeId[0];

    if (request) {
        SBufToCstring(request, tok.remaining().substr(0, MAX_URL-1));
        /* convert %xx to char */
        rfc1738_unescape(request);
    }
}

/**
 * Parse the request to determine whether it is cachable.
 *
 * \param req   Request data.
 * \retval 0    Not cachable.
 * \retval 1    Cachable.
 */
int
gopherCachable(const HttpRequest * req)
{
    int cachable = 1;
    char type_id;
    /* parse to see type */
    gopher_request_parse(req,
                         &type_id,
                         nullptr);

    switch (type_id) {

    case GOPHER_INDEX:

    case GOPHER_CSO:

    case GOPHER_TELNET:

    case GOPHER_3270:
        cachable = 0;
        break;

    default:
        cachable = 1;
    }

    return cachable;
}

static void
gopherHTMLHeader(StoreEntry * e, const char *title, const char *substring)
{
    storeAppendPrintf(e, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
    storeAppendPrintf(e, "<HTML><HEAD><TITLE>");
    storeAppendPrintf(e, title, substring);
    storeAppendPrintf(e, "</TITLE>");
    storeAppendPrintf(e, "<STYLE type=\"text/css\"><!--BODY{background-color:#ffffff;font-family:verdana,sans-serif}--></STYLE>\n");
    storeAppendPrintf(e, "</HEAD>\n<BODY><H1>");
    storeAppendPrintf(e, title, substring);
    storeAppendPrintf(e, "</H1>\n");
}

static void
gopherHTMLFooter(StoreEntry * e)
{
    storeAppendPrintf(e, "<HR noshade size=\"1px\">\n");
    storeAppendPrintf(e, "<ADDRESS>\n");
    storeAppendPrintf(e, "Generated %s by %s (%s)\n",
                      Time::FormatRfc1123(squid_curtime),
                      getMyHostname(),
                      visible_appname_string);
    storeAppendPrintf(e, "</ADDRESS></BODY></HTML>\n");
}

static void
gopherEndHTML(GopherStateData * gopherState)
{
    StoreEntry *e = gopherState->entry;

    if (!gopherState->HTML_header_added) {
        gopherHTMLHeader(e, "Server Return Nothing", nullptr);
        storeAppendPrintf(e, "<P>The Gopher query resulted in a blank response</P>");
    } else if (gopherState->HTML_pre) {
        storeAppendPrintf(e, "</PRE>\n");
    }

    gopherHTMLFooter(e);
}

/**
 * Convert Gopher to HTML.
 *
 * Borrow part of code from libwww2 came with Mosaic distribution.
 */
static void
gopherToHTML(GopherStateData * gopherState, char *inbuf, int len)
{
    char *pos = inbuf;
    char *lpos = nullptr;
    char *tline = nullptr;
    LOCAL_ARRAY(char, line, TEMP_BUF_SIZE);
    char *name = nullptr;
    char *selector = nullptr;
    char *host = nullptr;
    char *port = nullptr;
    char *escaped_selector = nullptr;
    char gtype;
    StoreEntry *entry = nullptr;

    memset(line, '\0', TEMP_BUF_SIZE);

    entry = gopherState->entry;

    if (gopherState->conversion == GopherStateData::HTML_INDEX_PAGE) {
        char *html_url = html_quote(entry->url());
        gopherHTMLHeader(entry, "Gopher Index %s", html_url);
        storeAppendPrintf(entry,
                          "<p>This is a searchable Gopher index. Use the search\n"
                          "function of your browser to enter search terms.\n"
                          "<ISINDEX>\n");
        gopherHTMLFooter(entry);
        /* now let start sending stuff to client */
        entry->flush();
        gopherState->HTML_header_added = 1;

        return;
    }

    if (gopherState->conversion == GopherStateData::HTML_CSO_PAGE) {
        char *html_url = html_quote(entry->url());
        gopherHTMLHeader(entry, "CSO Search of %s", html_url);
        storeAppendPrintf(entry,
                          "<P>A CSO database usually contains a phonebook or\n"
                          "directory.  Use the search function of your browser to enter\n"
                          "search terms.</P><ISINDEX>\n");
        gopherHTMLFooter(entry);
        /* now let start sending stuff to client */
        entry->flush();
        gopherState->HTML_header_added = 1;

        return;
    }

    SBuf outbuf;

    if (!gopherState->HTML_header_added) {
        if (gopherState->conversion == GopherStateData::HTML_CSO_RESULT)
            gopherHTMLHeader(entry, "CSO Search Result", nullptr);
        else
            gopherHTMLHeader(entry, "Gopher Menu", nullptr);

        outbuf.append ("<PRE>");

        gopherState->HTML_header_added = 1;

        gopherState->HTML_pre = 1;
    }

    while (pos < inbuf + len) {
        int llen;
        int left = len - (pos - inbuf);
        lpos = (char *)memchr(pos, '\n', left);
        if (lpos) {
            ++lpos;             /* Next line is after \n */
            llen = lpos - pos;
        } else {
            llen = left;
        }
        if (gopherState->len + llen >= TEMP_BUF_SIZE) {
            debugs(10, DBG_IMPORTANT, "GopherHTML: Buffer overflow. Lost some data on URL: " << entry->url()  );
            llen = TEMP_BUF_SIZE - gopherState->len - 1;
            gopherState->overflowed = true; // may already be true
        }
        if (!lpos) {
            /* there is no complete line in inbuf */
            /* copy it to temp buffer */
            /* note: llen is adjusted above */
            memcpy(gopherState->buf + gopherState->len, pos, llen);
            gopherState->len += llen;
            break;
        }
        if (gopherState->len != 0) {
            /* there is something left from last tx. */
            memcpy(line, gopherState->buf, gopherState->len);
            memcpy(line + gopherState->len, pos, llen);
            llen += gopherState->len;
            gopherState->len = 0;
        } else {
            memcpy(line, pos, llen);
        }
        line[llen + 1] = '\0';
        /* move input to next line */
        pos = lpos;

        /* at this point. We should have one line in buffer to process */

        if (*line == '.') {
            /* skip it */
            memset(line, '\0', TEMP_BUF_SIZE);
            continue;
        }

        switch (gopherState->conversion) {

        case GopherStateData::HTML_INDEX_RESULT:

        case GopherStateData::HTML_DIR: {
            tline = line;
            gtype = *tline;
            ++tline;
            name = tline;
            selector = strchr(tline, TAB);

            if (selector) {
                *selector = '\0';
                ++selector;
                host = strchr(selector, TAB);

                if (host) {
                    *host = '\0';
                    ++host;
                    port = strchr(host, TAB);

                    if (port) {
                        char *junk;
                        port[0] = ':';
                        junk = strchr(host, TAB);

                        if (junk)
                            *junk++ = 0;    /* Chop port */
                        else {
                            junk = strchr(host, '\r');

                            if (junk)
                                *junk++ = 0;    /* Chop port */
                            else {
                                junk = strchr(host, '\n');

                                if (junk)
                                    *junk++ = 0;    /* Chop port */
                            }
                        }

                        if ((port[1] == '0') && (!port[2]))
                            port[0] = 0;    /* 0 means none */
                    }

                    /* escape a selector here */
                    escaped_selector = xstrdup(rfc1738_escape_part(selector));

                    const auto icon_url = gopherState->iconUrl(gtype);

                    if ((gtype == GOPHER_TELNET) || (gtype == GOPHER_3270)) {
                        if (strlen(escaped_selector) != 0)
                            outbuf.appendf("<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"telnet://%s@%s%s%s/\">%s</A>\n",
                                           icon_url, escaped_selector, rfc1738_escape_part(host),
                                           *port ? ":" : "", port, html_quote(name));
                        else
                            outbuf.appendf("<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"telnet://%s%s%s/\">%s</A>\n",
                                           icon_url, rfc1738_escape_part(host), *port ? ":" : "",
                                           port, html_quote(name));

                    } else if (gtype == GOPHER_INFO) {
                        outbuf.appendf("\t%s\n", html_quote(name));
                    } else {
                        if (strncmp(selector, "GET /", 5) == 0) {
                            /* WWW link */
                            outbuf.appendf("<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"http://%s/%s\">%s</A>\n",
                                           icon_url, host, rfc1738_escape_unescaped(selector + 5), html_quote(name));
                        } else if (gtype == GOPHER_WWW) {
                            outbuf.appendf("<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"gopher://%s/%c%s\">%s</A>\n",
                                           icon_url, rfc1738_escape_unescaped(selector), html_quote(name));
                        } else {
                            /* Standard link */
                            outbuf.appendf("<IMG border=\"0\" SRC=\"%s\"> <A HREF=\"gopher://%s/%c%s\">%s</A>\n",
                                           icon_url, host, gtype, escaped_selector, html_quote(name));
                        }
                    }

                    safe_free(escaped_selector);
                } else {
                    memset(line, '\0', TEMP_BUF_SIZE);
                    continue;
                }
            } else {
                memset(line, '\0', TEMP_BUF_SIZE);
                continue;
            }

            break;
            }           /* HTML_DIR, HTML_INDEX_RESULT */

        case GopherStateData::HTML_CSO_RESULT: {
            if (line[0] == '-') {
                int code, recno;
                char *s_code, *s_recno, *result;

                s_code = strtok(line + 1, ":\n");
                s_recno = strtok(nullptr, ":\n");
                result = strtok(nullptr, "\n");

                if (!result)
                    break;

                code = atoi(s_code);

                recno = atoi(s_recno);

                if (code != 200)
                    break;

                if (gopherState->cso_recno != recno) {
                    outbuf.appendf("</PRE><HR noshade size=\"1px\"><H2>Record# %d<br><i>%s</i></H2>\n<PRE>", recno, html_quote(result));
                    gopherState->cso_recno = recno;
                } else {
                    outbuf.appendf("%s\n", html_quote(result));
                }

                break;
            } else {
                int code;
                char *s_code, *result;

                s_code = strtok(line, ":");
                result = strtok(nullptr, "\n");

                if (!result)
                    break;

                code = atoi(s_code);

                switch (code) {

                case 200: {
                    /* OK */
                    /* Do nothing here */
                    break;
                }

                case 102:   /* Number of matches */

                case 501:   /* No Match */

                case 502: { /* Too Many Matches */
                    /* Print the message the server returns */
                    outbuf.appendf("</PRE><HR noshade size=\"1px\"><H2>%s</H2>\n<PRE>", html_quote(result));
                    break;
                }

                }
            }

            break;
            }           /* HTML_CSO_RESULT */
        default:
            break;      /* do nothing */

        }           /* switch */

    }               /* while loop */

    if (outbuf.length() > 0) {
        entry->append(outbuf.rawContent(), outbuf.length());
        /* now let start sending stuff to client */
        entry->flush();
    }

    return;
}

static void
gopherTimeout(const CommTimeoutCbParams &io)
{
    GopherStateData *gopherState = static_cast<GopherStateData *>(io.data);
    debugs(10, 4, io.conn << ": '" << gopherState->entry->url() << "'" );

    gopherState->fwd->fail(new ErrorState(ERR_READ_TIMEOUT, Http::scGatewayTimeout, gopherState->fwd->request, gopherState->fwd->al));

    if (Comm::IsConnOpen(io.conn))
        io.conn->close();
}

/**
 * This will be called when data is ready to be read from fd.
 * Read until error or connection closed.
 */
static void
gopherReadReply(const Comm::ConnectionPointer &conn, char *buf, size_t len, Comm::Flag flag, int xerrno, void *data)
{
    GopherStateData *gopherState = (GopherStateData *)data;
    StoreEntry *entry = gopherState->entry;
    int clen;
    int bin;
    size_t read_sz = BUFSIZ;
#if USE_DELAY_POOLS
    DelayId delayId = entry->mem_obj->mostBytesAllowed();
#endif

    /* Bail out early on Comm::ERR_CLOSING - close handlers will tidy up for us */

    if (flag == Comm::ERR_CLOSING) {
        return;
    }

    assert(buf == gopherState->replybuf);

    // XXX: Should update delayId, statCounter, etc. before bailing
    if (!entry->isAccepting()) {
        debugs(10, 3, "terminating due to bad " << *entry);
        // TODO: Do not abuse connection for triggering cleanup.
        gopherState->serverConn->close();
        return;
    }

#if USE_DELAY_POOLS
    read_sz = delayId.bytesWanted(1, read_sz);
#endif

    /* leave one space for \0 in gopherToHTML */

    if (flag == Comm::OK && len > 0) {
#if USE_DELAY_POOLS
        delayId.bytesIn(len);
#endif

        statCounter.server.all.kbytes_in += len;
        statCounter.server.other.kbytes_in += len;
    }

    debugs(10, 5, conn << " read len=" << len);

    if (flag == Comm::OK && len > 0) {
        AsyncCall::Pointer nil;
        commSetConnTimeout(conn, Config.Timeout.read, nil);
        ++IOStats.Gopher.reads;

        for (clen = len - 1, bin = 0; clen; ++bin)
            clen >>= 1;

        ++IOStats.Gopher.read_hist[bin];

        HttpRequest *req = gopherState->fwd->request;
        if (req->hier.bodyBytesRead < 0) {
            req->hier.bodyBytesRead = 0;
            // first bytes read, update Reply flags:
            gopherState->reply_->sources |= Http::Message::srcGopher;
        }

        req->hier.bodyBytesRead += len;
    }

    if (flag != Comm::OK) {
        debugs(50, DBG_IMPORTANT, "ERROR: " << MYNAME << "reading: " << xstrerr(xerrno));

        if (ignoreErrno(xerrno)) {
            AsyncCall::Pointer call = commCbCall(5,4, "gopherReadReply",
                                                 CommIoCbPtrFun(gopherReadReply, gopherState));
            comm_read(conn, buf, read_sz, call);
        } else {
            const auto err = new ErrorState(ERR_READ_ERROR, Http::scInternalServerError, gopherState->fwd->request, gopherState->fwd->al);
            err->xerrno = xerrno;
            gopherState->fwd->fail(err);
            gopherState->serverConn->close();
        }
    } else if (len == 0 && entry->isEmpty()) {
        gopherState->fwd->fail(new ErrorState(ERR_ZERO_SIZE_OBJECT, Http::scServiceUnavailable, gopherState->fwd->request, gopherState->fwd->al));
        gopherState->serverConn->close();
    } else if (len == 0) {
        /* Connection closed; retrieval done. */
        /* flush the rest of data in temp buf if there is one. */

        if (gopherState->conversion != GopherStateData::NORMAL)
            gopherEndHTML(gopherState);

        entry->timestampsSet();
        entry->flush();

        if (!gopherState->len && !gopherState->overflowed)
            gopherState->fwd->markStoredReplyAsWhole("gopher EOF after receiving/storing some bytes");

        gopherState->fwd->complete();
        gopherState->serverConn->close();
    } else {
        if (gopherState->conversion != GopherStateData::NORMAL) {
            gopherToHTML(gopherState, buf, len);
        } else {
            entry->append(buf, len);
        }
        GopherStateData::DelayAwareRead(gopherState);
    }
}

void
GopherStateData::DelayAwareRead(GopherStateData *gopherState)
{
    const auto &conn = gopherState->serverConn;

    if (!Comm::IsConnOpen(conn) || fd_table[conn->fd].closing()) {
        debugs(10, 3, "will not read from " << conn);
        return;
    }

    const auto amountToRead = gopherState->entry->bytesWanted(Range<size_t>(0, BUFSIZ));

    if (amountToRead <= 0) {
        AsyncCall::Pointer delayCall = asyncCall(10, 3, "GopherStateData::DelayAwareRead",
                                       cbdataDialer(&GopherStateData::DelayAwareRead, gopherState));
        gopherState->entry->mem().delayRead(delayCall);
        return;
    }

    AsyncCall::Pointer readCall = commCbCall(5, 5, "gopherReadReply", CommIoCbPtrFun(gopherReadReply, gopherState));
    comm_read(conn, gopherState->replybuf, amountToRead, readCall);
}

/**
 * This will be called when request write is complete. Schedule read of reply.
 */
static void
gopherSendComplete(const Comm::ConnectionPointer &conn, char *, size_t size, Comm::Flag errflag, int xerrno, void *data)
{
    GopherStateData *gopherState = (GopherStateData *) data;
    StoreEntry *entry = gopherState->entry;
    debugs(10, 5, conn << " size: " << size << " errflag: " << errflag);

    if (size > 0) {
        fd_bytes(conn->fd, size, FD_WRITE);
        statCounter.server.all.kbytes_out += size;
        statCounter.server.other.kbytes_out += size;
    }

    if (!entry->isAccepting()) {
        debugs(10, 3, "terminating due to bad " << *entry);
        // TODO: Do not abuse connection for triggering cleanup.
        gopherState->serverConn->close();
        return;
    }

    if (errflag) {
        const auto err = new ErrorState(ERR_WRITE_ERROR, Http::scServiceUnavailable, gopherState->fwd->request, gopherState->fwd->al);
        err->xerrno = xerrno;
        err->port = gopherState->fwd->request->url.port();
        err->url = xstrdup(entry->url());
        gopherState->fwd->fail(err);
        gopherState->serverConn->close();
        return;
    }

    /*
     * OK. We successfully reach remote site.  Start MIME typing
     * stuff.  Do it anyway even though request is not HTML type.
     */
    entry->buffer();

    gopherMimeCreate(gopherState);

    switch (gopherState->type_id) {

    case GOPHER_DIRECTORY:
        /* we got to convert it first */
        gopherState->conversion = GopherStateData::HTML_DIR;
        gopherState->HTML_header_added = 0;
        break;

    case GOPHER_INDEX:
        /* we got to convert it first */
        gopherState->conversion = GopherStateData::HTML_INDEX_RESULT;
        gopherState->HTML_header_added = 0;
        break;

    case GOPHER_CSO:
        /* we got to convert it first */
        gopherState->conversion = GopherStateData::HTML_CSO_RESULT;
        gopherState->cso_recno = 0;
        gopherState->HTML_header_added = 0;
        break;

    default:
        gopherState->conversion = GopherStateData::NORMAL;
        entry->flush();
    }

    GopherStateData::DelayAwareRead(gopherState);
}

/**
 * This will be called when connect completes. Write request.
 */
static void
gopherSendRequest(int, void *data)
{
    GopherStateData *gopherState = (GopherStateData *)data;
    MemBuf mb;
    mb.init();

    if (gopherState->type_id == GOPHER_CSO) {
        const char *t = strchr(gopherState->request, '?');

        if (t)
            ++t;        /* skip the ? */
        else
            t = "";

        mb.appendf("query %s\r\nquit", t);
    } else {
        if (gopherState->type_id == GOPHER_INDEX) {
            if (char *t = strchr(gopherState->request, '?'))
                *t = '\t';
        }
        mb.append(gopherState->request, strlen(gopherState->request));
    }
    mb.append("\r\n", 2);

    debugs(10, 5, gopherState->serverConn);
    AsyncCall::Pointer call = commCbCall(5,5, "gopherSendComplete",
                                         CommIoCbPtrFun(gopherSendComplete, gopherState));
    Comm::Write(gopherState->serverConn, &mb, call);

    if (!gopherState->entry->makePublic())
        gopherState->entry->makePrivate(true);
}

void
gopherStart(FwdState * fwd)
{
    GopherStateData *gopherState = new GopherStateData(fwd);

    debugs(10, 3, gopherState->entry->url());

    ++ statCounter.server.all.requests;

    ++ statCounter.server.other.requests;

    /* Parse url. */
    gopher_request_parse(fwd->request,
                         &gopherState->type_id, gopherState->request);

    comm_add_close_handler(fwd->serverConnection()->fd, gopherStateFree, gopherState);

    if (((gopherState->type_id == GOPHER_INDEX) || (gopherState->type_id == GOPHER_CSO))
            && (strchr(gopherState->request, '?') == nullptr)) {
        /* Index URL without query word */
        /* We have to generate search page back to client. No need for connection */
        gopherMimeCreate(gopherState);

        if (gopherState->type_id == GOPHER_INDEX) {
            gopherState->conversion = GopherStateData::HTML_INDEX_PAGE;
        } else {
            if (gopherState->type_id == GOPHER_CSO) {
                gopherState->conversion = GopherStateData::HTML_CSO_PAGE;
            } else {
                gopherState->conversion = GopherStateData::HTML_INDEX_PAGE;
            }
        }

        gopherToHTML(gopherState, (char *) nullptr, 0);
        fwd->markStoredReplyAsWhole("gopher instant internal request satisfaction");
        fwd->complete();
        return;
    }

    // XXX: Sharing open Connection with FwdState that has its own handlers/etc.
    gopherState->serverConn = fwd->serverConnection();
    gopherSendRequest(fwd->serverConnection()->fd, gopherState);
    AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "gopherTimeout",
                                     CommTimeoutCbPtrFun(gopherTimeout, gopherState));
    commSetConnTimeout(fwd->serverConnection(), Config.Timeout.read, timeoutCall);
}

/*
 * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
 *
 * Squid software is distributed under GPLv2+ license and includes
 * contributions from numerous individuals and organizations.
 * Please see the COPYING and CONTRIBUTORS files for details.
 */

/* DEBUG: section 10    Gopher */

#ifndef SQUID_GOPHER_H_
#define SQUID_GOPHER_H_

class FwdState;
class HttpRequest;

/**
 \defgroup ServerProtocolGopherAPI Server-Side Gopher API
 \ingroup ServerProtocol
 */

/// \ingroup ServerProtocolGopherAPI
void gopherStart(FwdState *);

/// \ingroup ServerProtocolGopherAPI
int gopherCachable(const HttpRequest *);

#endif /* SQUID_GOPHER_H_ */

        srcGopher = 1 << (16 + 14), ///< Gopher server
    gopher_reads += stats.gopher_reads;
    for (int i = 0; i < IoStats::histSize; ++i)
        gopher_read_hist[i] += stats.gopher_read_hist[i];
    double gopher_reads;
    double gopher_read_hist[IoStats::histSize];
supporting FTP, gopher, ICAP, ICP, HTCP and HTTP data objects.

    stats.gopher_reads = IOStats.Gopher.reads;

    for (i = 0; i < IoStats::histSize; ++i) {
        stats.gopher_read_hist[i] = IOStats.Gopher.read_hist[i];
    }
    storeAppendPrintf(sentry, "\n");
    storeAppendPrintf(sentry, "Gopher I/O\n");
    storeAppendPrintf(sentry, "number of reads: %.0f\n", stats.gopher_reads);
    storeAppendPrintf(sentry, "Read Histogram:\n");

    for (i = 0; i < IoStats::histSize; ++i) {
        storeAppendPrintf(sentry, "%5d-%5d: %9.0f %2.0f%%\n",
                          i ? (1 << (i - 1)) + 1 : 1,
                          1 << i,
                          stats.gopher_read_hist[i],
                          Math::doublePercent(stats.gopher_read_hist[i], stats.gopher_reads));
    }

	tests/stub_gopher.cc \
/*
 * Copyright (C) 1996-2022 The Squid Software Foundation and contributors
 *
 * Squid software is distributed under GPLv2+ license and includes
 * contributions from numerous individuals and organizations.
 * Please see the COPYING and CONTRIBUTORS files for details.
 */

#include "squid.h"

#define STUB_API "gopher.cc"
#include "tests/STUB.h"

#include "gopher.h"
void gopherStart(FwdState *) STUB
int gopherCachable(const HttpRequest *) STUB

refresh_pattern ^gopher:        1440    0%      1440
Fixed code: "welcher FTP, ICAP, ICP, HTCP and HTTP Data Objects unterstützt. Im "
"supporting FTP, ICAP, ICP, HTCP and HTTP data objects.  Unlike "
"supporting FTP, ICAP, ICP, HTCP and HTTP data objects.  Unlike "
"soporta FTP, ICAP, ICP, HTCP y objetos de datos HTTP. A diferencia "
"le web, il supporte les protocoles FTP, ICAP, ICP, HTCP et HTTP pour "
"supporto supporta i protocolli FTP, ICAP, ICP, HTCP e HTTP. A "
"Squid ondersteun nie alle navraagmetodes vir alle toegangsprotokolle nie."
"Squid ondersteun nie alle navraagmetodes vir alle toegangsprotokolle nie."
"Squid bütün sorğu metodları və bütün protokollardəstəkləmir."
"Squid bütün sorğu metodları və bütün protokollardəstəkləmir."
"Кеш сървърът не поддържа всички методи на заявка за всички протоколи."
"Кеш сървърът не поддържа всички методи на заявка за всички протоколи."
"protocols."
"protocols."
"Squid nepodporuje všechny typy metod u všech protokolů."
"Squid nepodporuje všechny typy metod u všech protokolů."
"alle adgangs protokoller."
"alle adgangs protokoller."
"Squid unterstützt nicht alle Anfragemethoden für alle Protokolle."
"Squid unterstützt nicht alle Anfragemethoden für alle Protokolle."
"πρόσβασης."
"πρόσβασης."
"Squid does not support all request methods for all access protocols."
"Squid does not support all request methods for all access protocols."
/* special event: FTP directory listing */
"acceso."
"acceso."
"acceso."
"protokollidega."
"protokollidega."
"Squid ei tue kaikkia hakupyynnon tyyppejä kaikilla protokollilla."
"Squid ei tue kaikkia pääsyprotokollia. Esimerkiksi SSH-protokolla "
"ei ole tällä hetkellä tuettu."
"les protocoles d'accès."
"les protocoles d'accès."
"שרת ה Squid אינו תומך בכל שיטות הבקשה לכל הפרוטוקולים."
"שרת ה Squid אינו תומך בכל שיטות הבקשה לכל הפרוטוקולים."
"kombinációt."
"kombinációt."
"Squid-ը բոլոր արձանագրությունների համար բոլոր հարցման մեթոդները չի աջակցում."
"Squid-ը բոլոր արձանագրությունների համար բոլոր հարցման մեթոդները չի աջակցում."
"protocollo."
"protocollo."
"ポートしているわけではありません。"
"ポートしているわけではありません。"
"Squid는 모든 접속 프로토콜에 대한 request method를 지원하지 않습니다."
"Squid는 모든 접속 프로토콜에 대한 request method를 지원하지 않습니다."
"Squid palaiko ne visus užklausos metodus daliai protokolų."
"Squid palaiko ne visus užklausos metodus daliai protokolų."
"Squid neatbalsta visas pieprasījuma metodes visiem protokoliem."
"Squid neatbalsta visas pieprasījuma metodes visiem protokoliem."
"Squid ondersteunt niet alle verzoekmethoden voor alle toegangsprotocollen."
"Squid ondersteunt niet alle verzoekmethoden voor alle toegangsprotocollen."
"Squid nie wspiera wszystkich metod we wszystkich protokołach."
"Squid nie wspiera wszystkich metod we wszystkich protokołach."
"acesso."
"acesso."
"Squid não suporta todos os métodos para todos os protocolos."
"Squid não suporta todos os métodos para todos os protocolos."
"acces."
"acces."
"Squid не поддерживает все методы запросов для всех протоколов."
"Squid не поддерживает все методы запросов для всех протоколов."
"Squid nepodporuje všetky typy metód pri všetkých protokoloch."
"Squid nepodporuje všetky typy metód pri všetkých protokoloch."
"Squid ne podpira vseh metod zahtev za vse protokole dostopa."
"Squid ne podpira vseh metod zahtev za vse protokole dostopa."
"protokole."
"protokole."
"Squid st&ouml;djer inte alla fr&aring;gemetoder f&ouml;r alla protokoll."
"Squid st&ouml;djer inte alla fr&aring;gemetoder f&ouml;r alla protokoll."
<p>Squid does not support all request methods for all access protocols.</p>
"Squid, bazı erişim protokollerin, bazı istek yöntemlerini desteklemiyor."
"Squid, bazı erişim protokollerin, bazı istek yöntemlerini desteklemiyor."
"Squid не підтримує всі методи запитів для всіх наявних протоколів."
"Squid не підтримує всі методи запитів для всіх наявних протоколів."
"cập."
"cập."
"Squid （缓存服务器）不能对所有的存取协议支持所有的请求方式。"
"Squid （缓存服务器）不能对所有的存取协议支持所有的请求方式。"
"因為 Squid （網路快取程式）並未支援所有的連結要求方式在各式通訊協定上。"
"Squid 不支持某些訪問協議。例如 SSH 協議 "
"目前不支持。"
    Http, Ftp;
				Generated FTP listings are treated as
	Note that this is a global limit. It affects all HTTP, HTCP, and FTP
    ERR_DIR_LISTING,            /* Display of remote directory (FTP) */
supporting FTP, ICAP, ICP, HTCP and HTTP data objects.