1
0
mirror of https://github.com/rkd77/elinks.git synced 2024-12-04 14:46:47 -05:00

[gemini] Show error page for some errors.

This commit is contained in:
Witold Filipczyk 2021-07-03 09:45:11 +02:00
parent 61247f6a32
commit 391d463a46
6 changed files with 220 additions and 18 deletions

View File

@ -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"

View File

@ -1,6 +1,6 @@
top_builddir=../../..
include $(top_builddir)/Makefile.config
OBJS = gemini.o
OBJS = codes.o gemini.o
include $(top_srcdir)/Makefile.lib

180
src/protocol/gemini/codes.c Normal file
View File

@ -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,
"<html>\n"
" <head><title>%s</title></head>\n"
" <body>\n"
" <h1 align=\"left\">%s: %s</h1>\n"
#ifndef CONFIG_SMALL
" <hr />\n"
" <p>\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,
" </p>\n"
" <p>\n"
" URI: <a href=\"%s\">%s</a>\n", struri(uri), struri(uri));
#endif
add_format_to_string(&string,
#ifndef CONFIG_SMALL
" </p>\n"
" <hr />\n"
#endif
" </body>\n"
"</html>\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);
}

View File

@ -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 */

View File

@ -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);

View File

@ -1 +1 @@
srcs += files('gemini.c')
srcs += files('codes.c', 'gemini.c')