Okay, here we go. This is gopher for cURL round 2:

-- Curl_write() now used for the critical portion in lib/gopher.c
-- ftpserver.pl modified to serve Gopher over IPv4 and IPv6
-- four base tests for gopher, including index, selector, query and IPv6

-- 
------------------------------------ personal: http://www.cameronkaiser.com/ --
  Cameron Kaiser * Floodgap Systems * www.floodgap.com * [email protected]
-- Proudly running on the Apple Network Server 500/200 ------------------------
>From 6c721a51bbb30c653fa9563f808c9c3278499300 Mon Sep 17 00:00:00 2001
From: Cameron Kaiser <[email protected]>
Date: Thu, 12 Aug 2010 07:55:48 -0700
Subject: [PATCH 1/4] Gopher protocol support (initial release)

---
 configure.ac        |   19 ++++++
 include/curl/curl.h |    1 +
 lib/Makefile.inc    |    3 +-
 lib/gopher.c        |  162 +++++++++++++++++++++++++++++++++++++++++++++++++++
 lib/gopher.h        |   29 +++++++++
 lib/url.c           |    8 +++
 lib/urldata.h       |    9 ++-
 lib/version.c       |    3 +
 src/main.c          |    1 +
 9 files changed, 231 insertions(+), 4 deletions(-)
 create mode 100644 lib/gopher.c
 create mode 100644 lib/gopher.h

diff --git a/configure.ac b/configure.ac
index db04447..1e64246 100644
--- a/configure.ac
+++ b/configure.ac
@@ -570,6 +570,22 @@ AC_HELP_STRING([--disable-smtp],[Disable SMTP support]),
        AC_MSG_RESULT(yes)
 )
 
+AC_MSG_CHECKING([whether to support gopher])
+AC_ARG_ENABLE(gopher,
+AC_HELP_STRING([--enable-gopher],[Enable Gopher support])
+AC_HELP_STRING([--disable-gopher],[Disable Gopher support]),
+[ case "$enableval" in
+  no)
+       AC_MSG_RESULT(no)
+       AC_DEFINE(CURL_DISABLE_GOPHER, 1, [to disable Gopher])
+       AC_SUBST(CURL_DISABLE_GOPHER, [1])
+       ;;
+  *)   AC_MSG_RESULT(yes)
+       ;;
+  esac ],
+       AC_MSG_RESULT(yes)
+)
+
 
 dnl **********************************************************************
 dnl Check for built-in manual
@@ -2738,6 +2754,9 @@ fi
 if test "x$CURL_DISABLE_TFTP" != "x1"; then
   SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS TFTP"
 fi
+if test "x$CURL_DISABLE_GOPHER" != "x1"; then
+  SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS GOPHER"
+fi
 if test "x$CURL_DISABLE_POP3" != "x1"; then
   SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS POP3"
   if test "x$SSL_ENABLED" = "x1"; then
diff --git a/include/curl/curl.h b/include/curl/curl.h
index b19828f..cb9d0fb 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -722,6 +722,7 @@ typedef enum {
 #define CURLPROTO_RTMPTE (1<<22)
 #define CURLPROTO_RTMPS  (1<<23)
 #define CURLPROTO_RTMPTS (1<<24)
+#define CURLPROTO_GOPHER (1<<25)
 #define CURLPROTO_ALL    (~0) /* enable everything */
 
 /* long may be 32 or 64 bits, but we should never depend on anything else
diff --git a/lib/Makefile.inc b/lib/Makefile.inc
index bfd3abe..33b5765 100644
--- a/lib/Makefile.inc
+++ b/lib/Makefile.inc
@@ -20,7 +20,8 @@ CSOURCES = file.c timeval.c base64.c hostip.c progress.c 
formdata.c   \
   strdup.c socks.c ssh.c nss.c qssl.c rawstr.c curl_addrinfo.c         \
   socks_gssapi.c socks_sspi.c curl_sspi.c slist.c nonblock.c           \
   curl_memrchr.c imap.c pop3.c smtp.c pingpong.c rtsp.c curl_threads.c \
-  warnless.c hmac.c polarssl.c curl_rtmp.c openldap.c curl_gethostname.c
+  warnless.c hmac.c polarssl.c curl_rtmp.c openldap.c curl_gethostname.c\
+  gopher.c
 
 HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h      \
   progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h     \
diff --git a/lib/gopher.c b/lib/gopher.c
new file mode 100644
index 0000000..d1ca440
--- /dev/null
+++ b/lib/gopher.c
@@ -0,0 +1,162 @@
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2010, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#include "setup.h"
+
+#ifndef CURL_DISABLE_GOPHER
+
+/* -- WIN32 approved -- */
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#ifdef WIN32
+#include <time.h>
+#include <io.h>
+#else
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#include <netinet/in.h>
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <netdb.h>
+#ifdef HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#ifdef HAVE_NET_IF_H
+#include <net/if.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+
+#endif
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "transfer.h"
+#include "sendf.h"
+
+#include "progress.h"
+#include "strequal.h"
+#include "gopher.h"
+#include "rawstr.h"
+
+#define _MPRINTF_REPLACE /* use our functions only */
+#include <curl/mprintf.h>
+
+/* The last #include file should be: */
+#include "memdebug.h"
+
+
+/*
+ * Forward declarations.
+ */
+
+static CURLcode gopher_do(struct connectdata *conn, bool *done);
+
+/*
+ * Gopher protocol handler.
+ * This is also a nice simple template to build off for simple
+ * connect-command-download protocols.
+ */
+
+const struct Curl_handler Curl_handler_gopher = {
+  "GOPHER",                             /* scheme */
+  ZERO_NULL,                            /* setup_connection */
+  gopher_do,                            /* do_it */
+  ZERO_NULL,                            /* done */
+  ZERO_NULL,                            /* do_more */
+  ZERO_NULL,                            /* connect_it */
+  ZERO_NULL,                            /* connecting */
+  ZERO_NULL,                            /* doing */
+  ZERO_NULL,                            /* proto_getsock */
+  ZERO_NULL,                            /* doing_getsock */
+  ZERO_NULL,                            /* perform_getsock */
+  ZERO_NULL,                            /* disconnect */
+  PORT_GOPHER,                          /* defport */
+  PROT_GOPHER                           /* protocol */
+};
+
+static CURLcode gopher_do(struct connectdata *conn, bool *done)
+{
+  CURLcode result=CURLE_OK;
+  struct SessionHandle *data=conn->data;
+  curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
+
+  curl_off_t *bytecount = &data->req.bytecount;
+  char *path = data->state.path;
+
+  char *sel;
+
+  *done = TRUE; /* unconditionally */
+
+  /* Create selector. Degenerate cases: / and /1 => convert to "" */
+  if (strlen(path) <= 2)
+    sel = (char *)"";
+  else {
+    char *newp;
+    int i, j, len;
+
+    /* Otherwise, drop / and the first character (i.e., item type) ... */
+    newp = path;
+    newp+=2;
+
+    /* ... then turn ? into TAB for search servers, Veronica, etc. ... */
+    j = strlen(newp);
+    if (j)
+      for(i=0; i<j; i++)
+        newp[i] = ((newp[i] == '?') ? '\x09' : newp[i]);
+
+    /* ... and finally unescape */
+    sel = curl_easy_unescape(data, newp, 0, &len);
+    if (!sel)
+      return CURLE_OUT_OF_MEMORY;
+  }
+
+  result = Curl_sendf(sockfd, conn, "%s\r\n", sel);
+
+  if(result) {
+    failf(data, "Failed sending Gopher request");
+    return result;
+  }
+  Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount,
+                      -1, NULL); /* no upload */
+  return CURLE_OK;
+}
+#endif /*CURL_DISABLE_GOPHER*/
diff --git a/lib/gopher.h b/lib/gopher.h
new file mode 100644
index 0000000..38bbc4b
--- /dev/null
+++ b/lib/gopher.h
@@ -0,0 +1,29 @@
+#ifndef HEADER_CURL_GOPHER_H
+#define HEADER_CURL_GOPHER_H
+/***************************************************************************
+ *                                  _   _ ____  _
+ *  Project                     ___| | | |  _ \| |
+ *                             / __| | | | |_) | |
+ *                            | (__| |_| |  _ <| |___
+ *                             \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2009, Daniel Stenberg, <[email protected]>, et al.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution. The terms
+ * are also available at http://curl.haxx.se/docs/copyright.html.
+ *
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
+ * copies of the Software, and permit persons to whom the Software is
+ * furnished to do so, under the terms of the COPYING file.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ***************************************************************************/
+
+#ifndef CURL_DISABLE_GOPHER
+extern const struct Curl_handler Curl_handler_gopher;
+#endif
+
+#endif /* HEADER_CURL_GOPHER_H */
diff --git a/lib/url.c b/lib/url.c
index fd6443a..abc349a 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -138,6 +138,7 @@ void idn_free (void *ptr); /* prototype from idn-free.h, 
not provided by
 #include "socks.h"
 #include "rtsp.h"
 #include "curl_rtmp.h"
+#include "gopher.h"
 
 #define _MPRINTF_REPLACE /* use our functions only */
 #include <curl/mprintf.h>
@@ -227,6 +228,10 @@ static const struct Curl_handler * const protocols[] = {
   &Curl_handler_rtsp,
 #endif
 
+#ifndef CURL_DISABLE_GOPHER
+  &Curl_handler_gopher,
+#endif
+
 #ifdef USE_LIBRTMP
   &Curl_handler_rtmp,
   &Curl_handler_rtmpt,
@@ -3474,8 +3479,11 @@ static CURLcode findprotocol(struct SessionHandle *data,
     if(Curl_raw_equal(p->scheme, protostr)) {
       /* Protocol found in table. Check if allowed */
       if(!(data->set.allowed_protocols & p->protocol))
+{
         /* nope, get out */
+       fprintf(stderr, "well, shit\n");
         break;
+}
 
       /* it is allowed for "normal" request, now do an extra check if this is
          the result of a redirect */
diff --git a/lib/urldata.h b/lib/urldata.h
index 7919921..1eaca68 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -46,6 +46,7 @@
 #define PORT_RTMP 1935
 #define PORT_RTMPT PORT_HTTP
 #define PORT_RTMPS PORT_HTTPS
+#define PORT_GOPHER 70
 
 #define DICT_MATCH "/MATCH:"
 #define DICT_MATCH2 "/M:"
@@ -715,11 +716,13 @@ struct connectdata {
 #define PROT_RTMPTE  CURLPROTO_RTMPTE
 #define PROT_RTMPS   CURLPROTO_RTMPS
 #define PROT_RTMPTS  CURLPROTO_RTMPTS
+#define PROT_GOPHER  CURLPROTO_GOPHER
 
-/* (1<<24) is currently the highest used bit in the public bitmask. We make
-   sure we use "private bits" above the public ones to make things easier. */
+/* (1<<25) is currently the highest used bit in the public bitmask. We make
+   sure we use "private bits" above the public ones to make things easier;
+   Gopher will not conflict with the current bit 25. */
 
-#define PROT_EXTMASK 0xffffff
+#define PROT_EXTMASK 0x03ffffff
 
 #define PROT_SSL     (1<<29) /* protocol requires SSL */
 
diff --git a/lib/version.c b/lib/version.c
index 9a336a3..9ba2e33 100644
--- a/lib/version.c
+++ b/lib/version.c
@@ -158,6 +158,9 @@ static const char * const protocols[] = {
 #if defined(USE_SSL) && !defined(CURL_DISABLE_FTP)
   "ftps",
 #endif
+#ifndef CURL_DISABLE_GOPHER
+  "gopher",
+#endif
 #ifndef CURL_DISABLE_HTTP
   "http",
 #endif
diff --git a/src/main.c b/src/main.c
index 5585c17..2971342 100644
--- a/src/main.c
+++ b/src/main.c
@@ -1539,6 +1539,7 @@ static long proto2num(struct Configurable *config, long 
*val, const char *str)
     { "smtp", CURLPROTO_SMTP },
     { "smtps", CURLPROTO_SMTPS },
     { "rtsp", CURLPROTO_RTSP },
+    { "gopher", CURLPROTO_GOPHER },
     { NULL, 0 }
   };
 
-- 
1.7.1

>From 3993c54c809cdf4d0f33040860cbae43239390ec Mon Sep 17 00:00:00 2001
From: Cameron Kaiser <[email protected]>
Date: Thu, 12 Aug 2010 08:32:00 -0700
Subject: [PATCH 2/4] Forgot gopher.h in Makefile.inc

---
 lib/Makefile.inc |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)

diff --git a/lib/Makefile.inc b/lib/Makefile.inc
index 33b5765..41ab827 100644
--- a/lib/Makefile.inc
+++ b/lib/Makefile.inc
@@ -35,5 +35,6 @@ HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h 
hostip.h     \
   tftp.h sockaddr.h splay.h strdup.h setup_once.h socks.h ssh.h nssg.h \
   curl_base64.h rawstr.h curl_addrinfo.h curl_sspi.h slist.h nonblock.h        
\
   curl_memrchr.h imap.h pop3.h smtp.h pingpong.h rtsp.h curl_threads.h \
-  warnless.h curl_hmac.h polarssl.h curl_rtmp.h curl_gethostname.h
+  warnless.h curl_hmac.h polarssl.h curl_rtmp.h curl_gethostname.h      \
+  gopher.h
 
-- 
1.7.1

>From 4730844b7bad8081e517423539260b3218079083 Mon Sep 17 00:00:00 2001
From: Cameron Kaiser <[email protected]>
Date: Thu, 12 Aug 2010 09:25:43 -0700
Subject: [PATCH 3/4] Remove url.c test

---
 lib/url.c |    3 ---
 1 files changed, 0 insertions(+), 3 deletions(-)

diff --git a/lib/url.c b/lib/url.c
index abc349a..ed01cc7 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -3479,11 +3479,8 @@ static CURLcode findprotocol(struct SessionHandle *data,
     if(Curl_raw_equal(p->scheme, protostr)) {
       /* Protocol found in table. Check if allowed */
       if(!(data->set.allowed_protocols & p->protocol))
-{
         /* nope, get out */
-       fprintf(stderr, "well, shit\n");
         break;
-}
 
       /* it is allowed for "normal" request, now do an extra check if this is
          the result of a redirect */
-- 
1.7.1

>From 892fb7aac331f23393cbc4a7a5f7ac83b5d91302 Mon Sep 17 00:00:00 2001
From: Cameron Kaiser <[email protected]>
Date: Mon, 23 Aug 2010 14:30:59 -0700
Subject: [PATCH 4/4] Gopher using Curl_write; test suite (4 tests)

---
 lib/gopher.c           |   25 +++++++++++++++++++++----
 tests/data/Makefile.am |    2 +-
 tests/data/test1200    |   40 ++++++++++++++++++++++++++++++++++++++++
 tests/data/test1201    |   40 ++++++++++++++++++++++++++++++++++++++++
 tests/data/test1202    |   41 +++++++++++++++++++++++++++++++++++++++++
 tests/data/test1203    |   41 +++++++++++++++++++++++++++++++++++++++++
 tests/ftp.pm           |    2 +-
 tests/ftpserver.pl     |   44 ++++++++++++++++++++++++++++++++++++++++----
 tests/runtests.pl      |   36 +++++++++++++++++++++++++++++++++---
 tests/serverhelp.pm    |    9 +++++----
 10 files changed, 263 insertions(+), 17 deletions(-)
 create mode 100644 tests/data/test1200
 create mode 100644 tests/data/test1201
 create mode 100644 tests/data/test1202
 create mode 100644 tests/data/test1203

diff --git a/lib/gopher.c b/lib/gopher.c
index d1ca440..2fab6da 100644
--- a/lib/gopher.c
+++ b/lib/gopher.c
@@ -121,8 +121,8 @@ static CURLcode gopher_do(struct connectdata *conn, bool 
*done)
 
   curl_off_t *bytecount = &data->req.bytecount;
   char *path = data->state.path;
-
   char *sel;
+  ssize_t amount, k;
 
   *done = TRUE; /* unconditionally */
 
@@ -149,12 +149,29 @@ static CURLcode gopher_do(struct connectdata *conn, bool 
*done)
       return CURLE_OUT_OF_MEMORY;
   }
 
-  result = Curl_sendf(sockfd, conn, "%s\r\n", sel);
-
-  if(result) {
+  /* We use Curl_write instead of Curl_sendf to make sure the entire buffer
+     is sent, which could be sizeable with long selectors. */
+  k = strlen(sel);
+  for(;;) {
+    result = Curl_write(conn, sockfd, sel, k, &amount);
+    if (CURLE_OK == result) { /* Which may not have written it all! */
+      k -= amount;
+      sel += amount;
+      if (k < 1)
+        break; /* but it did write it all */
+    } else {
+      failf(data, "Failed sending Gopher request");
+      return result;
+    }
+  }
+  /* We can use Curl_sendf to send the terminal \r\n relatively safely and
+     save allocing another string/doing another _write loop. */
+  result = Curl_sendf(sockfd, conn, "\r\n");
+  if (result != CURLE_OK) {
     failf(data, "Failed sending Gopher request");
     return result;
   }
+
   Curl_setup_transfer(conn, FIRSTSOCKET, -1, FALSE, bytecount,
                       -1, NULL); /* no upload */
   return CURLE_OK;
diff --git a/tests/data/Makefile.am b/tests/data/Makefile.am
index 61b1b48..7922000 100644
--- a/tests/data/Makefile.am
+++ b/tests/data/Makefile.am
@@ -67,7 +67,7 @@ EXTRA_DIST = test1 test108 test117 test127 test20 test27 
test34 test46           \
  test312 test1105 test565 test800 test1106 test801 test566 test802 test803 \
  test1107 test1108 test1109 test1110 test1111 test1112 test129 test567     \
  test568 test569 test570 test571 test572 test804 test805 test806 test807 \
- test573 test313 test1115
+ test573 test313 test1115 test1200 test1201 test1202 test1203
 
 filecheck:
        @mkdir test-place; \
diff --git a/tests/data/test1200 b/tests/data/test1200
new file mode 100644
index 0000000..9432720
--- /dev/null
+++ b/tests/data/test1200
@@ -0,0 +1,40 @@
+# Gopher directory fetch
+<testcase>
+<info>
+<keywords>
+GOPHER
+INDEX
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+<datacheck>
+iMenu results          error.host      1
+0Selector      /bar    bar.foo.invalid 70
+.
+</datacheck>
+</reply>
+
+# Client-side
+<client>
+<server>
+gopher
+</server>
+ <name>
+Gopher index
+ </name>
+ <command>
+gopher://%HOSTIP:%GOPHERPORT
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<protocol>
+
+</protocol>
+</verify>
+</testcase>
diff --git a/tests/data/test1201 b/tests/data/test1201
new file mode 100644
index 0000000..bb3ccc5
--- /dev/null
+++ b/tests/data/test1201
@@ -0,0 +1,40 @@
+# Gopher selector fetch
+<testcase>
+<info>
+<keywords>
+GOPHER
+SELECTOR
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+<datacheck>
+iMenu results          error.host      1
+0Selector /selector/SELECTOR   /bar    bar.foo.invalid 70
+.
+</datacheck>
+</reply>
+
+# Client-side
+<client>
+<server>
+gopher
+</server>
+ <name>
+Gopher selector
+ </name>
+ <command>
+gopher://%HOSTIP:%GOPHERPORT/1/selector/SELECTOR
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<protocol>
+/selector/SELECTOR
+</protocol>
+</verify>
+</testcase>
diff --git a/tests/data/test1202 b/tests/data/test1202
new file mode 100644
index 0000000..8b8502d
--- /dev/null
+++ b/tests/data/test1202
@@ -0,0 +1,41 @@
+# Gopher query fetch
+<testcase>
+<info>
+<keywords>
+GOPHER
+QUERY
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+<datacheck>
+iSearch results                error.host      1
+0Query query succeeded /foo    foo.bar.invalid 70
+0Selector /the/search/engine   /bar    bar.foo.invalid 70
+.
+</datacheck>
+</reply>
+
+# Client-side
+<client>
+<server>
+gopher
+</server>
+ <name>
+Gopher query
+ </name>
+ <command>
+gopher://%HOSTIP:%GOPHERPORT/7/the/search/engine?query%20succeeded
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<protocol>
+/the/search/engine     query succeeded
+</protocol>
+</verify>
+</testcase>
diff --git a/tests/data/test1203 b/tests/data/test1203
new file mode 100644
index 0000000..c639512
--- /dev/null
+++ b/tests/data/test1203
@@ -0,0 +1,41 @@
+# Gopher IPv6 connectivity test
+<testcase>
+<info>
+<keywords>
+GOPHER-ipv6
+IPv6
+INDEX
+</keywords>
+</info>
+
+#
+# Server-side
+<reply>
+<datacheck>
+iMenu results          error.host      1
+0Selector      /bar    bar.foo.invalid 70
+.
+</datacheck>
+</reply>
+
+# Client-side
+<client>
+<server>
+gopher-ipv6
+</server>
+ <name>
+Gopher IPv6 index
+ </name>
+ <command>
+-g "gopher://%HOSTIP:%GOPHER6PORT";
+</command>
+</client>
+
+#
+# Verify data after the test has been "shot"
+<verify>
+<protocol>
+
+</protocol>
+</verify>
+</testcase>
diff --git a/tests/ftp.pm b/tests/ftp.pm
index 6a46e76..535075a 100644
--- a/tests/ftp.pm
+++ b/tests/ftp.pm
@@ -181,7 +181,7 @@ sub killsockfilters {
     my $pidfile;
     my $pid;
 
-    return if($proto !~ /^(ftp|imap|pop3|smtp)$/);
+    return if($proto !~ /^(ftp|imap|pop3|smtp|gopher)$/);
 
     die "unsupported sockfilter: $which"
         if($which && ($which !~ /^(main|data)$/));
diff --git a/tests/ftpserver.pl b/tests/ftpserver.pl
index ed2a832..03d9334 100755
--- a/tests/ftpserver.pl
+++ b/tests/ftpserver.pl
@@ -29,6 +29,9 @@
 # protocol per invoke. You need to start mulitple servers to support multiple
 # protocols simultaneously.
 #
+# The gopher protocol test server also lives here, borrowing some of the
+# routines.
+#
 # It is meant to exercise curl, it is not meant to be a fully working
 # or even very standard compliant server.
 #
@@ -341,6 +344,7 @@ sub senddata {
 # for the given protocol. References to protocol command callbacks are
 # stored in 'commandfunc' hash, and text which will be returned to the
 # client before the command callback runs is stored in 'displaytext'.
+# Gopher is not handled here, however (it has a separate routine).
 #
 sub protocolsetup {
     my $proto = $_[0];
@@ -1294,7 +1298,7 @@ while(@ARGV) {
         }
     }
     elsif($ARGV[0] eq '--proto') {
-        if($ARGV[1] && ($ARGV[1] =~ /^(ftp|imap|pop3|smtp)$/)) {
+        if($ARGV[1] && ($ARGV[1] =~ /^(ftp|imap|pop3|smtp|gopher)$/)) {
             $proto = $1;
             shift @ARGV;
         }
@@ -1421,7 +1425,7 @@ while(1) {
 
     &customize(); # read test control instructions
 
-    sendcontrol @welcome;
+    sendcontrol @welcome unless ($proto eq 'gopher');
 
     #remove global variables from last connection
     if($ftplistparserstate) {
@@ -1474,7 +1478,39 @@ while(1) {
         my $FTPCMD;
         my $FTPARG;
         my $full=$_;
-        if($proto eq "imap") {
+
+        if($proto eq 'gopher') {
+          # Gopher protocol support lives right here and we handle it
+          # right here and now, since there will only ever be one selector
+          # and no other commands.
+
+          my $sel = $_;
+          my $query = '';
+          my @response = ();
+          ($sel, $query) = split(/\x09/, $_) if (/\x09/);
+
+          if($sel eq 'erifiedserver') {
+            # NOT verifiedserver, the first character is the item type in
+            # a Gopher URL!
+            push(@response, "iWE ROOLZ: $$\x09\x09error.host\x091\r\n");
+          }
+          if (length($query)) { # fake Veronica, gin up search results
+            push(@response, "iSearch results\x09\x09error.host\x091\r\n");
+            push(@response,
+                 "0Query $query\x09/foo\x09foo.bar.invalid\x0970\r\n");
+          } else { # fake selector, gin up a menu
+            push(@response, "iMenu results\x09\x09error.host\x091\r\n");
+          }
+          push(@response,
+               "0Selector $sel\x09/bar\x09bar.foo.invalid\x0970\r\n");
+          push(@response, ".\r\n");
+          sendcontrol @response;
+
+          # disconnect the client now, no command.
+          $FTPCMD = $FTPARG = '';
+          print SFWRITE "DISC\n";
+        }
+        elsif($proto eq "imap") {
             # IMAP is different with its identifier first on the command line
             unless (m/^([^ ]+) ([^ ]+) (.*)/ ||
                     m/^([^ ]+) ([^ ]+)/) {
@@ -1550,7 +1586,7 @@ while(1) {
             }
         }
 
-        if($check) {
+        if($check && $proto ne 'gopher') {
             logmsg "$FTPCMD wasn't handled!\n";
             sendcontrol "500 $FTPCMD is not dealt with!\r\n";
         }
diff --git a/tests/runtests.pl b/tests/runtests.pl
index 9ddccc0..556ba2a 100755
--- a/tests/runtests.pl
+++ b/tests/runtests.pl
@@ -133,6 +133,8 @@ my $SMTPPORT; # SMTP
 my $SMTP6PORT; # SMTP IPv6 server port
 my $RTSPPORT; # RTSP
 my $RTSP6PORT; # RTSP IPv6 server port
+my $GOPHERPORT; # Gopher
+my $GOPHER6PORT; # Gopher IPv6 server port
 
 my $srcdir = $ENV{'srcdir'} || '.';
 my $CURL="../src/curl".exe_ext(); # what curl executable to run on the tests
@@ -193,6 +195,7 @@ my $has_idn;     # set if libcurl is built with IDN support
 my $http_ipv6;   # set if HTTP server has IPv6 support
 my $ftp_ipv6;    # set if FTP server has IPv6 support
 my $tftp_ipv6;   # set if TFTP server has IPv6 support
+my $gopher_ipv6; # set if Gopher server has IPv6 support
 my $has_ipv6;    # set if libcurl is built with IPv6 support
 my $has_libz;    # set if libcurl is built with libz support
 my $has_getrlimit;  # set if system has getrlimit()
@@ -329,7 +332,7 @@ sub init_serverpidfile_hash {
       }
     }
   }
-  for my $proto (('tftp', 'sftp', 'socks', 'ssh', 'rtsp')) {
+  for my $proto (('tftp', 'sftp', 'socks', 'ssh', 'rtsp', 'gopher')) {
     for my $ipvnum ((4, 6)) {
       for my $idnum ((1, 2)) {
         my $serv = servername_id($proto, $ipvnum, $idnum);
@@ -988,7 +991,8 @@ my %protofunc = ('http' => \&verifyhttp,
                  'ftps' => \&verifyftp,
                  'tftp' => \&verifyftp,
                  'ssh' => \&verifyssh,
-                 'socks' => \&verifysocks);
+                 'socks' => \&verifysocks,
+                 'gopher' => \&verifyftp);
 
 sub verifyserver {
     my ($proto, $ipvnum, $idnum, $ip, $port) = @_;
@@ -1180,7 +1184,7 @@ sub runhttpsserver {
 }
 
 #######################################################################
-# start the pingpong server (FTP, POP3, IMAP, SMTP)
+# start the pingpong server (FTP, POP3, IMAP, SMTP, GOPHER)
 #
 sub runpingpongserver {
     my ($proto, $id, $verbose, $ipv6) = @_;
@@ -1211,6 +1215,9 @@ sub runpingpongserver {
     elsif($proto eq "smtp") {
         $port = ($ipvnum==6) ? $SMTP6PORT : $SMTPPORT;
     }
+    elsif($proto eq "gopher") {
+        $port = ($ipvnum==6) ? $GOPHER6PORT : $GOPHERPORT;
+    }
     else {
         print STDERR "Unsupported protocol $proto!!\n";
         return 0;
@@ -1263,6 +1270,7 @@ sub runpingpongserver {
         $doesntrun{$pidfile} = 1;
         return (0,0);
     }
+
     $pid2 = $pid3;
 
     if($verbose) {
@@ -2039,7 +2047,9 @@ sub checksystem {
         @sws = `server/sockfilt --version`;
         if($sws[0] =~ /IPv6/) {
             # FTP server has ipv6 support!
+            # and since the Gopher server descends from it, we have it too!
             $ftp_ipv6 = 1;
+            $gopher_ipv6 = 1;
         }
     }
 
@@ -2098,6 +2108,10 @@ sub checksystem {
     if($tftp_ipv6) {
         logmsg sprintf("TFTP-IPv6/%d ", $TFTP6PORT);
     }
+    logmsg sprintf("\n*   GOPHER/%d ", $GOPHERPORT);
+    if($gopher_ipv6) {
+        logmsg sprintf("GOPHER-IPv6/%d", $GOPHERPORT);
+    }
     logmsg sprintf("\n*   SSH/%d ", $SSHPORT);
     logmsg sprintf("SOCKS/%d ", $SOCKSPORT);
     logmsg sprintf("POP3/%d ", $POP3PORT);
@@ -2147,6 +2161,8 @@ sub subVariables {
   $$thing =~ s/%CLIENT6IP/$CLIENT6IP/g;
   $$thing =~ s/%RTSPPORT/$RTSPPORT/g;
   $$thing =~ s/%RTSP6PORT/$RTSP6PORT/g;
+  $$thing =~ s/%GOPHERPORT/$GOPHERPORT/g;
+  $$thing =~ s/%GOPHER6PORT/$GOPHER6PORT/g;
 
   # The purpose of FTPTIME2 and FTPTIME3 is to provide times that can be
   # used for time-out tests and that whould work on most hosts as these
@@ -3211,6 +3227,7 @@ sub startservers {
         if(($what eq "pop3") ||
            ($what eq "ftp") ||
            ($what eq "imap") ||
+           ($what eq "gopher") ||
            ($what eq "smtp")) {
             if(!$run{$what}) {
                 ($pid, $pid2) = runpingpongserver($what, "", $verbose);
@@ -3242,6 +3259,17 @@ sub startservers {
                 $run{'ftp-ipv6'}="$pid $pid2";
             }
         }
+        elsif($what eq "gopher-ipv6") {
+            if(!$run{'gopher-ipv6'}) {
+                ($pid, $pid2) = runpingpongserver("gopher","",$verbose,"ipv6");
+                if($pid <= 0) {
+                    return "failed starting GOPHER-IPv6 server";
+                }
+                logmsg sprintf("* pid gopher-ipv6 => %d %d\n", $pid,
+                       $pid2) if($verbose);
+                $run{'gopher-ipv6'}="$pid $pid2";
+            }
+        }
         elsif($what eq "http") {
             if(!$run{'http'}) {
                 ($pid, $pid2) = runhttpserver($verbose);
@@ -3821,6 +3849,8 @@ $SMTPPORT =  $base++;
 $SMTP6PORT = $base++;
 $RTSPPORT =  $base++;
 $RTSP6PORT = $base++;
+$GOPHERPORT =$base++;
+$GOPHER6PORT=$base++;
 
 #######################################################################
 # clear and create logging directory:
diff --git a/tests/serverhelp.pm b/tests/serverhelp.pm
index 4b3b505..8429b40 100644
--- a/tests/serverhelp.pm
+++ b/tests/serverhelp.pm
@@ -96,7 +96,7 @@ sub servername_str {
 
     $proto = uc($proto) if($proto);
     die "unsupported protocol: $proto" unless($proto &&
-        ($proto =~ 
/^(((FTP|HTTP|IMAP|POP3|SMTP)S?)|(TFTP|SFTP|SOCKS|SSH|RTSP))$/));
+        ($proto =~ 
/^(((FTP|HTTP|IMAP|POP3|SMTP)S?)|(TFTP|SFTP|SOCKS|SSH|RTSP|GOPHER))$/));
 
     $ipver = (not $ipver) ? 'ipv4' : lc($ipver);
     die "unsupported IP version: $ipver" unless($ipver &&
@@ -148,7 +148,8 @@ sub server_pidfilename {
 sub server_logfilename {
     my ($logdir, $proto, $ipver, $idnum) = @_;
     my $trailer = '_server.log';
-    $trailer = '_stunnel.log' if(lc($proto) =~ /^(ftp|http|imap|pop3|smtp)s$/);
+    $trailer = '_stunnel.log' if(lc($proto) =~ /^(ftp|http|imap|pop3|smtp)s$/||
+                                 lc($proto) eq 'gopher');
     return "${logdir}/". servername_canon($proto, $ipver, $idnum) ."$trailer";
 }
 
@@ -189,7 +190,7 @@ sub server_outputfilename {
 sub mainsockf_pidfilename {
     my ($proto, $ipver, $idnum) = @_;
     die "unsupported protocol: $proto" unless($proto &&
-        (lc($proto) =~ /^(ftp|imap|pop3|smtp)s?$/));
+        ((lc($proto) =~ /^(ftp|imap|pop3|smtp)s?$/) || lc($proto) eq 
'gopher'));
     my $trailer = (lc($proto) =~ /^ftps?$/) ? '_sockctrl.pid':'_sockfilt.pid';
     return '.'. servername_canon($proto, $ipver, $idnum) ."$trailer";
 }
@@ -201,7 +202,7 @@ sub mainsockf_pidfilename {
 sub mainsockf_logfilename {
     my ($logdir, $proto, $ipver, $idnum) = @_;
     die "unsupported protocol: $proto" unless($proto &&
-        (lc($proto) =~ /^(ftp|imap|pop3|smtp)s?$/));
+        ((lc($proto) =~ /^(ftp|imap|pop3|smtp)s?$/) || lc($proto) eq 
'gopher'));
     my $trailer = (lc($proto) =~ /^ftps?$/) ? '_sockctrl.log':'_sockfilt.log';
     return "${logdir}/". servername_canon($proto, $ipver, $idnum) ."$trailer";
 }
-- 
1.7.1

-------------------------------------------------------------------
List admin: http://cool.haxx.se/list/listinfo/curl-library
Etiquette:  http://curl.haxx.se/mail/etiquette.html

Reply via email to