I spoke with Daniel offlist and wrote up initial support for the Gopher
protocol this week. While Gopher may be an antiquated protocol for many,
there are still around 150 active servers still out there, it is a simple
alternative for file collections, it makes cURL increasingly more
comprehensive, and it is a simple template to build other protocols from.
It can also be used for other protocols indirectly (example:
gopher://sdf.org:79/0cdkaiser for finger protocol).

Two patches are attached, the main one, and a quickie where I noticed that
I forgot gopher.h in Makefile.inc after I committed the first set. You can
test it against the known servers list at gopher.floodgap.com if you like.

-- 
------------------------------------ personal: http://www.cameronkaiser.com/ --
  Cameron Kaiser * Floodgap Systems * www.floodgap.com * [email protected]
-- In memory of Howard Caine --------------------------------------------------
>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/2] 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/2] 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

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

Reply via email to