diff --git a/src/document/renderer.c b/src/document/renderer.c
index cf3ac35b2..daab73054 100644
--- a/src/document/renderer.c
+++ b/src/document/renderer.c
@@ -26,8 +26,10 @@
#endif
#include "document/renderer.h"
#include "document/view.h"
+#ifdef CONFIG_ECMASCRIPT
#include "ecmascript/ecmascript.h"
#include "ecmascript/spidermonkey/document.h"
+#endif
#include "encoding/encoding.h"
#include "intl/charsets.h"
#include "main/main.h"
diff --git a/src/protocol/gemini/Makefile b/src/protocol/gemini/Makefile
index 9039144a3..017f769c2 100644
--- a/src/protocol/gemini/Makefile
+++ b/src/protocol/gemini/Makefile
@@ -1,6 +1,6 @@
top_builddir=../../..
include $(top_builddir)/Makefile.config
-OBJS = gemini.o
+OBJS = codes.o gemini.o
include $(top_srcdir)/Makefile.lib
diff --git a/src/protocol/gemini/codes.c b/src/protocol/gemini/codes.c
new file mode 100644
index 000000000..67949efe2
--- /dev/null
+++ b/src/protocol/gemini/codes.c
@@ -0,0 +1,180 @@
+/* Gemini response codes */
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE /* Needed for asprintf() */
+#endif
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "elinks.h"
+
+#include "cache/cache.h"
+#include "intl/gettext/libintl.h"
+#include "network/connection.h"
+#include "protocol/gemini/codes.h"
+#include "protocol/uri.h"
+#include "session/session.h"
+#include "session/task.h"
+#include "terminal/terminal.h"
+#include "terminal/window.h"
+#include "util/snprintf.h"
+#include "viewer/text/draw.h"
+
+
+struct gemini_code {
+ int num;
+ const char *str;
+};
+
+static const struct gemini_code gemini_code[] = {
+ { 10, "INPUT" },
+ { 21, "SENSITIVE INPUT" },
+ { 20, "SUCCESS" },
+ { 30, "REDIRECT - TEMPORARY" },
+ { 31, "REDIRECT - PERMANENT" },
+ { 40, "TEMPORARY FAILURE" },
+ { 41, "SERVER UNAVAILABLE" },
+ { 42, "CGI ERROR" },
+ { 43, "PROXY ERROR" },
+ { 44, "SLOW DOWN" },
+ { 50, "PERMANENT FAILURE" },
+ { 51, "NOT FOUND" },
+ { 52, "GONE" },
+ { 53, "PROXY REQUEST REFUSED" },
+ { 59, "BAD REQUEST" },
+ { 60, "CLIENT CERTIFICATE REQUIRED" },
+ { 61, "CERTIFICATE NOT AUTHORISED" },
+ { 62, "CERTIFICATE NOT VALID" },
+};
+
+static int
+compare_gemini_codes(const void *key, const void *element)
+{
+ int first = (long) key;
+ int second = ((const struct gemini_code *) element)->num;
+
+ return first - second;
+}
+
+static const char *
+gemini_code_to_string(int code)
+{
+ const struct gemini_code *element
+ = bsearch((void *) (long) code, gemini_code,
+ sizeof_array(gemini_code),
+ sizeof(*element),
+ compare_gemini_codes);
+
+ if (element) return element->str;
+
+ return NULL;
+}
+
+static char *
+get_gemini_error_document(struct terminal *term, struct uri *uri, int code)
+{
+ const char *codestr = gemini_code_to_string(code);
+ char *title = asprintfa(_("Gemini error %02d", term), code);
+ struct string string;
+
+ if (!codestr) codestr = "UNKNOWN ERROR";
+
+ if (!init_string(&string)) {
+ mem_free_if(title);
+ return NULL;
+ }
+
+ add_format_to_string(&string,
+ "\n"
+ "
%s\n"
+ " \n"
+ " %s: %s
\n"
+#ifndef CONFIG_SMALL
+ "
\n"
+ " \n"
+#endif
+ , title, title, codestr);
+
+#ifndef CONFIG_SMALL
+ add_format_to_string(&string, _(
+ " An error occurred on the server while fetching the document you\n"
+ " requested.\n",
+ term));
+
+ add_format_to_string(&string,
+ "
\n"
+ " \n"
+ " URI: %s\n", struri(uri), struri(uri));
+#endif
+ add_format_to_string(&string,
+#ifndef CONFIG_SMALL
+ "
\n"
+ "
\n"
+#endif
+ " \n"
+ "\n");
+
+ mem_free_if(title);
+
+ return string.source;
+}
+
+struct gemini_error_info {
+ int code;
+ struct uri *uri;
+};
+
+static void
+show_gemini_error_document(struct session *ses, void *data)
+{
+ struct gemini_error_info *info = data;
+ struct terminal *term = ses->tab->term;
+ struct cache_entry *cached = find_in_cache(info->uri);
+ struct cache_entry *cache = cached ? cached : get_cache_entry(info->uri);
+ char *str = NULL;
+
+ if (cache) str = get_gemini_error_document(term, info->uri, info->code);
+
+ if (str) {
+ /* The codepage that _("foo", term) used when it was
+ * called by get_gemini_error_document. */
+ const int gettext_codepage = get_terminal_codepage(term);
+
+ if (cached) delete_entry_content(cache);
+
+ /* If we run out of memory here, it's perhaps better
+ * to display a malformatted error message than none
+ * at all. */
+ mem_free_set(&cache->content_type, stracpy("text/html"));
+ mem_free_set(&cache->head,
+ straconcat("\r\nContent-Type: text/html; charset=",
+ get_cp_mime_name(gettext_codepage),
+ "\r\n", (char *) NULL));
+ add_fragment(cache, 0, str, strlen(str));
+ mem_free(str);
+
+ draw_formatted(ses, 1);
+ }
+
+ done_uri(info->uri);
+ mem_free(info);
+}
+
+
+void
+gemini_error_document(struct connection *conn, int code)
+{
+ struct gemini_error_info *info;
+
+ assert(conn && conn->uri);
+
+ info = mem_calloc(1, sizeof(*info));
+ if (!info) return;
+
+ info->code = code;
+ info->uri = get_uri_reference(conn->uri);
+
+ add_questions_entry(show_gemini_error_document, info);
+}
diff --git a/src/protocol/gemini/codes.h b/src/protocol/gemini/codes.h
new file mode 100644
index 000000000..23b02f588
--- /dev/null
+++ b/src/protocol/gemini/codes.h
@@ -0,0 +1,19 @@
+
+#ifndef EL__PROTOCOL_GEMINI_CODES_H
+#define EL__PROTOCOL_GEMINI_CODES_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct connection;
+
+/* Gemini response codes device. */
+
+void gemini_error_document(struct connection *conn, int code);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EL__PROTOCOL_HTTP_CODES_H */
diff --git a/src/protocol/gemini/gemini.c b/src/protocol/gemini/gemini.c
index 2b8b09eae..38a989cec 100644
--- a/src/protocol/gemini/gemini.c
+++ b/src/protocol/gemini/gemini.c
@@ -25,6 +25,7 @@
#include "osdep/osdep.h"
#include "osdep/sysname.h"
#include "protocol/date.h"
+#include "protocol/gemini/codes.h"
#include "protocol/gemini/gemini.h"
#include "protocol/header.h"
#include "protocol/uri.h"
@@ -158,18 +159,17 @@ read_gemini_data_done(struct connection *conn)
/* There's no content but an error so just print
* that instead of nothing. */
-// if (!conn->from) {
-// if (http->code >= 400) {
-// http_error_document(conn, http->code);
-//
-// } else {
-// /* This is not an error, thus fine. No need generate any
-// * document, as this may be empty and it's not a problem.
-// * In case of 3xx, we're probably just getting kicked to
-// * another page anyway. And in case of 2xx, the document
-// * may indeed be empty and thus the user should see it so. */
-// }
-// }
+ if (!conn->from) {
+ if (gemini->code >= 40) {
+ gemini_error_document(conn, gemini->code);
+ } else {
+ /* This is not an error, thus fine. No need generate any
+ * document, as this may be empty and it's not a problem.
+ * In case of 3xx, we're probably just getting kicked to
+ * another page anyway. And in case of 2xx, the document
+ * may indeed be empty and thus the user should see it so. */
+ }
+ }
gemini_end_request(conn, connection_state(S_OK), 0);
}
@@ -271,8 +271,7 @@ gemini_got_header(struct socket *socket, struct read_buffer *rb)
struct connection_state state = (!is_in_state(conn->state, S_PROC)
? connection_state(S_GETH)
: connection_state(S_PROC));
- int a, h = 20;
- int cf;
+ int a, h = 40;
if (socket->state == SOCKET_CLOSED) {
retry_connection(conn, connection_state(S_CANT_READ));
@@ -294,9 +293,12 @@ again:
if ((a && get_gemini_code(rb, &h))
|| ((h >= 40) || h < 10)) {
- abort_connection(conn, connection_state(S_HTTP_ERROR));
+ gemini->code = h;
+ mem_free_set(&conn->cached->content_type, stracpy("text/html"));
+ read_gemini_data_done(conn);
return;
}
+ gemini->code = h;
if (h >= 30 && h < 40) {
char *url = memacpy(rb->data + 3, a - 4);
@@ -313,7 +315,6 @@ again:
init_string(&head_string);
add_to_string(&head_string, "\nContent-Type:");
add_bytes_to_string(&head_string, rb->data + 2, a);
- gemini->code = h;
if (!conn->cached) {
done_string(&head_string);
diff --git a/src/protocol/gemini/meson.build b/src/protocol/gemini/meson.build
index 8ddabb5bb..a51eccfdd 100644
--- a/src/protocol/gemini/meson.build
+++ b/src/protocol/gemini/meson.build
@@ -1 +1 @@
-srcs += files('gemini.c')
+srcs += files('codes.c', 'gemini.c')