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')