aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBen Burwell <ben@benburwell.com>2020-05-21 15:00:11 -0400
committerBen Burwell <ben@benburwell.com>2020-05-21 18:38:31 -0400
commitc365f2cb4ffcbef02d2e9a5e17aa9d7f1fe81df7 (patch)
tree6edbb7f1c12a8afbde06a56423a1c5092a9dbacd
parent5d965b48987457eb0db01c2eb65712aec37e49f5 (diff)
Add gemini protocol support
The gemini protocol's "speculative specification" is documented at the following URLs: https://gemini.circumlunar.space/docs/spec-spec.txt gopher://gemini.circumlunar.space/1/docs/spec-spec.txt gemini://gemini.circumlunar.space/docs/spec-spec.txt This patch introduces preliminary support for version 0.11.0 of the specification, as of March 1st 2020.
-rw-r--r--CMakeLists.txt4
-rwxr-xr-xconfigure.ac19
-rw-r--r--docs/CURL-DISABLE.md4
-rw-r--r--include/curl/curl.h1
-rw-r--r--lib/Makefile.inc2
-rw-r--r--lib/curl_setup.h3
-rw-r--r--lib/gemini.c179
-rw-r--r--lib/gemini.h29
-rw-r--r--lib/url.c11
-rw-r--r--lib/urldata.h1
-rw-r--r--lib/version.c3
-rw-r--r--src/tool_libinfo.c1
-rw-r--r--src/tool_paramhlp.c1
-rw-r--r--src/tool_setopt.c1
14 files changed, 258 insertions, 1 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index a311e1259..cb7bfb03b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -169,6 +169,8 @@ option(CURL_DISABLE_IMAP "to disable IMAP" OFF)
mark_as_advanced(CURL_DISABLE_IMAP)
option(CURL_DISABLE_SMTP "to disable SMTP" OFF)
mark_as_advanced(CURL_DISABLE_SMTP)
+option(CURL_DISABLE_GEMINI "to disable Gemini" OFF)
+mark_as_advanced(CURL_DISABLE_GEMINI)
option(CURL_DISABLE_GOPHER "to disable Gopher" OFF)
mark_as_advanced(CURL_DISABLE_GOPHER)
option(CURL_ENABLE_MQTT "to enable MQTT" OFF)
@@ -188,6 +190,7 @@ if(HTTP_ONLY)
set(CURL_DISABLE_SMB ON)
set(CURL_DISABLE_SMTP ON)
set(CURL_DISABLE_GOPHER ON)
+ set(CURL_DISABLE_GEMINI ON)
endif()
option(CURL_DISABLE_COOKIES "to disable cookies support" OFF)
@@ -1363,6 +1366,7 @@ _add_if("LDAPS" NOT CURL_DISABLE_LDAPS AND
(NOT USE_OPENLDAP AND HAVE_LDAP_SSL)))
_add_if("DICT" NOT CURL_DISABLE_DICT)
_add_if("TFTP" NOT CURL_DISABLE_TFTP)
+_add_if("GEMINI" NOT CURL_DISABLE_GEMINI)
_add_if("GOPHER" NOT CURL_DISABLE_GOPHER)
_add_if("POP3" NOT CURL_DISABLE_POP3)
_add_if("POP3S" NOT CURL_DISABLE_POP3 AND SSL_ENABLED)
diff --git a/configure.ac b/configure.ac
index 9e1fdbeba..5b69f99ed 100755
--- a/configure.ac
+++ b/configure.ac
@@ -621,6 +621,22 @@ AC_HELP_STRING([--disable-smtp],[Disable SMTP support]),
AC_MSG_RESULT(yes)
)
+AC_MSG_CHECKING([whether to support gemini])
+AC_ARG_ENABLE(gemini,
+AC_HELP_STRING([--enable-gemini],[Enable Gemini support])
+AC_HELP_STRING([--disable-gemini],[Disable Gemini support]),
+[ case "$enableval" in
+ no)
+ AC_MSG_RESULT(no)
+ AC_DEFINE(CURL_DISABLE_GEMINI, 1, [to disable Gemini])
+ AC_SUBST(CURL_DISABLE_GEMINI, [1])
+ ;;
+ *) AC_MSG_RESULT(yes)
+ ;;
+ esac ],
+ AC_MSG_RESULT(yes)
+)
+
AC_MSG_CHECKING([whether to support gopher])
AC_ARG_ENABLE(gopher,
AC_HELP_STRING([--enable-gopher],[Enable Gopher support])
@@ -4864,6 +4880,9 @@ fi
if test "x$CURL_DISABLE_TFTP" != "x1"; then
SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS TFTP"
fi
+if test "x$CURL_DISABLE_GEMINI" != "x1"; then
+ SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS GEMINI"
+fi
if test "x$CURL_DISABLE_GOPHER" != "x1"; then
SUPPORT_PROTOCOLS="$SUPPORT_PROTOCOLS GOPHER"
fi
diff --git a/docs/CURL-DISABLE.md b/docs/CURL-DISABLE.md
index 83436b473..e42878c6b 100644
--- a/docs/CURL-DISABLE.md
+++ b/docs/CURL-DISABLE.md
@@ -24,6 +24,10 @@ Disable the FILE protocol
Disable the FTP (and FTPS) protocol
+## CURL_DISABLE_GEMINI
+
+Disable the Gemini protocol.
+
## CURL_DISABLE_GOPHER
Disable the GOPHER protocol.
diff --git a/include/curl/curl.h b/include/curl/curl.h
index 17d0384f0..71695538d 100644
--- a/include/curl/curl.h
+++ b/include/curl/curl.h
@@ -942,6 +942,7 @@ typedef enum {
#define CURLPROTO_SMB (1<<26)
#define CURLPROTO_SMBS (1<<27)
#define CURLPROTO_MQTT (1<<28)
+#define CURLPROTO_GEMINI (1<<29)
#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 08b0f73a3..e4789b5b5 100644
--- a/lib/Makefile.inc
+++ b/lib/Makefile.inc
@@ -51,6 +51,7 @@ LIB_CFILES = altsvc.c amigaos.c asyn-ares.c asyn-thread.c base64.c \
curl_ntlm_core.c curl_ntlm_wb.c curl_path.c curl_range.c curl_rtmp.c \
curl_sasl.c curl_sspi.c curl_threads.c dict.c dotdot.c easy.c escape.c \
file.c fileinfo.c formdata.c ftp.c url.c ftplistparser.c getenv.c getinfo.c \
+ gemini.c \
gopher.c hash.c hmac.c hostasyn.c hostcheck.c hostip.c hostip4.c hostip6.c \
hostsyn.c http.c http2.c http_chunks.c http_digest.c http_negotiate.c \
http_ntlm.c http_proxy.c idn_win32.c if2ip.c imap.c inet_ntop.c inet_pton.c \
@@ -70,6 +71,7 @@ LIB_HFILES = altsvc.h amigaos.h arpa_telnet.h asyn.h conncache.h connect.h \
curl_printf.h curl_range.h curl_rtmp.h curl_sasl.h curl_sec.h curl_setup.h \
curl_setup_once.h curl_sha256.h curl_sspi.h curl_threads.h curlx.h dict.h \
dotdot.h easyif.h escape.h file.h fileinfo.h formdata.h ftp.h url.h \
+ gemini.h \
ftplistparser.h getinfo.h gopher.h hash.h hostcheck.h hostip.h http.h \
http2.h http_chunks.h http_digest.h http_negotiate.h http_ntlm.h \
http_proxy.h if2ip.h imap.h inet_ntop.h inet_pton.h llist.h memdebug.h \
diff --git a/lib/curl_setup.h b/lib/curl_setup.h
index a86a235b3..a102e0519 100644
--- a/lib/curl_setup.h
+++ b/lib/curl_setup.h
@@ -196,6 +196,9 @@
# ifndef CURL_DISABLE_GOPHER
# define CURL_DISABLE_GOPHER
# endif
+# ifndef CURL_DISABLE_GEMINI
+# define CURL_DISABLE_GEMINI
+# endif
# ifndef CURL_DISABLE_SMB
# define CURL_DISABLE_SMB
# endif
diff --git a/lib/gemini.c b/lib/gemini.c
new file mode 100644
index 000000000..bd1953d82
--- /dev/null
+++ b/lib/gemini.c
@@ -0,0 +1,179 @@
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2020, Daniel Stenberg, <daniel@haxx.se>, 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 https://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 "curl_setup.h"
+
+#ifndef CURL_DISABLE_GEMINI
+
+#include "urldata.h"
+#include <curl/curl.h>
+#include "transfer.h"
+#include "sendf.h"
+#include "connect.h"
+#include "progress.h"
+#include "gemini.h"
+#include "select.h"
+#include "strdup.h"
+#include "url.h"
+#include "escape.h"
+#include "warnless.h"
+#include "curl_printf.h"
+#include "curl_memory.h"
+#include "vtls/vtls.h"
+/* The last #include file should be: */
+#include "memdebug.h"
+
+/*
+ * Forward declarations.
+ */
+
+static CURLcode gemini_do(struct connectdata *conn, bool *done);
+static CURLcode gemini_connecting(struct connectdata *conn, bool *done);
+CURLcode Curl_gemini_connect(struct connectdata *conn, bool *done);
+
+/*
+ * Gemini protocol handler.
+ */
+
+const struct Curl_handler Curl_handler_gemini = {
+ "GEMINI", /* scheme */
+ ZERO_NULL, /* setup_connection */
+ gemini_do, /* do_it */
+ ZERO_NULL, /* done */
+ ZERO_NULL, /* do_more */
+ Curl_gemini_connect, /* connect_it */
+ gemini_connecting, /* connecting */
+ ZERO_NULL, /* doing */
+ ZERO_NULL, /* proto_getsock */
+ ZERO_NULL, /* doing_getsock */
+ ZERO_NULL, /* domore_getsock */
+ ZERO_NULL, /* perform_getsock */
+ ZERO_NULL, /* disconnect */
+ ZERO_NULL, /* readwrite */
+ ZERO_NULL, /* connection_check */
+ PORT_GEMINI, /* defport */
+ CURLPROTO_GEMINI, /* protocol */
+ PROTOPT_NONE /* flags */
+};
+
+static CURLcode gemini_do(struct connectdata *conn, bool *done)
+{
+ CURLcode result = CURLE_OK;
+ struct Curl_easy *data = conn->data;
+ curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
+ timediff_t timeout_ms;
+ ssize_t amount, k;
+ size_t len;
+ int what;
+ char *req = NULL;
+ char *u = NULL;
+
+ *done = TRUE; /* unconditionally */
+
+ /* format the request URL */
+ if (curl_url_get(data->state.uh, CURLUPART_URL, &u, 0) != CURLUE_OK)
+ return CURLE_URL_MALFORMAT;
+
+ /* build the request line */
+ req = aprintf("%s\r\n", u);
+ if (!req)
+ return CURLE_OUT_OF_MEMORY;
+
+ len = strlen(req);
+
+ /* We use Curl_write instead of Curl_sendf to make sure the entire buffer is
+ sent, which could be sizeable with long selectors. */
+ k = curlx_uztosz(len);
+
+ for(;;) {
+ result = Curl_write(conn, sockfd, req, k, &amount);
+ if(!result) { /* Which may not have written it all! */
+ result = Curl_client_write(conn, CLIENTWRITE_HEADER, req, amount);
+ if(result)
+ break;
+
+ k -= amount;
+ req += amount;
+ if(k < 1)
+ break; /* but it did write it all */
+ }
+ else
+ break;
+
+ timeout_ms = Curl_timeleft(conn->data, NULL, FALSE);
+ if(timeout_ms < 0) {
+ result = CURLE_OPERATION_TIMEDOUT;
+ break;
+ }
+ if(!timeout_ms)
+ timeout_ms = TIMEDIFF_T_MAX;
+
+ /* Don't busyloop. The entire loop thing is a work-around as it causes a
+ BLOCKING behavior which is a NO-NO. This function should rather be
+ split up in a do and a doing piece where the pieces that aren't
+ possible to send now will be sent in the doing function repeatedly
+ until the entire request is sent.
+ */
+ what = SOCKET_WRITABLE(sockfd, timeout_ms);
+ if(what < 0) {
+ result = CURLE_SEND_ERROR;
+ break;
+ }
+ else if(!what) {
+ result = CURLE_OPERATION_TIMEDOUT;
+ break;
+ }
+ }
+
+ Curl_setup_transfer(data, FIRSTSOCKET, -1, FALSE, -1);
+ return CURLE_OK;
+}
+
+static CURLcode gemini_connecting(struct connectdata *conn, bool *done)
+{
+ CURLcode result;
+ DEBUGASSERT((conn) && (conn->handler->flags & PROTOPT_SSL));
+
+ /* perform SSL initialization for this socket */
+ result = Curl_ssl_connect_nonblocking(conn, FIRSTSOCKET, done);
+ if(result)
+ connclose(conn, "Failed TLS connection");
+
+ return result;
+}
+
+/*
+ * Curl_http_connect() performs HTTP stuff to do at connect-time, called from
+ * the generic Curl_connect().
+ */
+CURLcode Curl_gemini_connect(struct connectdata *conn, bool *done)
+{
+ CURLcode result;
+
+ /* perform SSL initialization */
+ result = gemini_connecting(conn, done);
+ if(result)
+ return result;
+
+ return CURLE_OK;
+}
+#endif /*CURL_DISABLE_GEMINI*/
diff --git a/lib/gemini.h b/lib/gemini.h
new file mode 100644
index 000000000..54cc6afd0
--- /dev/null
+++ b/lib/gemini.h
@@ -0,0 +1,29 @@
+#ifndef HEADER_CURL_GEMINI_H
+#define HEADER_CURL_GEMINI_H
+/***************************************************************************
+ * _ _ ____ _
+ * Project ___| | | | _ \| |
+ * / __| | | | |_) | |
+ * | (__| |_| | _ <| |___
+ * \___|\___/|_| \_\_____|
+ *
+ * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, 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 https://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_GEMINI
+extern const struct Curl_handler Curl_handler_gemini;
+#endif
+
+#endif /* HEADER_CURL_GEMINI_H */
diff --git a/lib/url.c b/lib/url.c
index 0173dc88a..e2b63cf72 100644
--- a/lib/url.c
+++ b/lib/url.c
@@ -113,6 +113,7 @@ bool curl_win32_idn_to_ascii(const char *in, char **out);
#include "inet_ntop.h"
#include "http_ntlm.h"
#include "curl_rtmp.h"
+#include "gemini.h"
#include "gopher.h"
#include "mqtt.h"
#include "http_proxy.h"
@@ -145,7 +146,7 @@ static unsigned int get_protocol_family(unsigned int protocol);
* Protocol table. Schemes (roughly) in 2019 popularity order:
*
* HTTPS, HTTP, FTP, FTPS, SFTP, FILE, SCP, SMTP, LDAP, IMAPS, TELNET, IMAP,
- * LDAPS, SMTPS, TFTP, SMB, POP3, GOPHER POP3S, RTSP, RTMP, SMBS, DICT
+ * LDAPS, SMTPS, TFTP, SMB, POP3, GOPHER POP3S, RTSP, RTMP, SMBS, DICT, GEMINI
*/
static const struct Curl_handler * const protocols[] = {
@@ -249,6 +250,10 @@ static const struct Curl_handler * const protocols[] = {
&Curl_handler_dict,
#endif
+#ifndef CURL_DISABLE_GEMINI
+ &Curl_handler_gemini,
+#endif
+
(struct Curl_handler *) NULL
};
@@ -4093,6 +4098,10 @@ static unsigned int get_protocol_family(unsigned int protocol)
family = CURLPROTO_GOPHER;
break;
+ case CURLPROTO_GEMINI:
+ family = CURLPROTO_GEMINI;
+ break;
+
case CURLPROTO_SMB:
case CURLPROTO_SMBS:
family = CURLPROTO_SMB;
diff --git a/lib/urldata.h b/lib/urldata.h
index 9b4ce5f5b..46321c5fd 100644
--- a/lib/urldata.h
+++ b/lib/urldata.h
@@ -49,6 +49,7 @@
#define PORT_RTMPT PORT_HTTP
#define PORT_RTMPS PORT_HTTPS
#define PORT_GOPHER 70
+#define PORT_GEMINI 1965
#define PORT_MQTT 1883
#define DICT_MATCH "/MATCH:"
diff --git a/lib/version.c b/lib/version.c
index 14e509606..87c69a187 100644
--- a/lib/version.c
+++ b/lib/version.c
@@ -250,6 +250,9 @@ static const char * const protocols[] = {
#if defined(USE_SSL) && !defined(CURL_DISABLE_FTP)
"ftps",
#endif
+#ifndef CURL_DISABLE_GEMINI
+ "gemini",
+#endif
#ifndef CURL_DISABLE_GOPHER
"gopher",
#endif
diff --git a/src/tool_libinfo.c b/src/tool_libinfo.c
index de7ec4d18..4c26376b8 100644
--- a/src/tool_libinfo.c
+++ b/src/tool_libinfo.c
@@ -54,6 +54,7 @@ CURLcode get_libcurl_info(void)
{ "file", CURLPROTO_FILE },
{ "ftp", CURLPROTO_FTP },
{ "ftps", CURLPROTO_FTPS },
+ { "gemini", CURLPROTO_GEMINI },
{ "gopher", CURLPROTO_GOPHER },
{ "http", CURLPROTO_HTTP },
{ "https", CURLPROTO_HTTPS },
diff --git a/src/tool_paramhlp.c b/src/tool_paramhlp.c
index c375bcc82..f6be25a52 100644
--- a/src/tool_paramhlp.c
+++ b/src/tool_paramhlp.c
@@ -335,6 +335,7 @@ long proto2num(struct OperationConfig *config, long *val, const char *str)
{ "smtp", CURLPROTO_SMTP },
{ "smtps", CURLPROTO_SMTPS },
{ "rtsp", CURLPROTO_RTSP },
+ { "gemini", CURLPROTO_GEMINI },
{ "gopher", CURLPROTO_GOPHER },
{ "smb", CURLPROTO_SMB },
{ "smbs", CURLPROTO_SMBS },
diff --git a/src/tool_setopt.c b/src/tool_setopt.c
index 9858d49c9..383600ab7 100644
--- a/src/tool_setopt.c
+++ b/src/tool_setopt.c
@@ -145,6 +145,7 @@ const struct NameValue setopt_nv_CURLPROTO[] = {
NV(CURLPROTO_FILE),
NV(CURLPROTO_FTP),
NV(CURLPROTO_FTPS),
+ NV(CURLPROTO_GEMINI),
NV(CURLPROTO_GOPHER),
NV(CURLPROTO_HTTP),
NV(CURLPROTO_HTTPS),