diff --git a/Makefile b/Makefile index 2304c9f..383d2fa 100644 --- a/Makefile +++ b/Makefile @@ -11,6 +11,11 @@ CFLAGS += -D_GNU_SOURCE -DPREFIX=\"$(PREFIX)\" -DCONFDIR=\"$(CONFDIR)\" -DGPLAC LDFLAGS ?= LDFLAGS += $(shell pkg-config --libs libcurl libssl libcrypto) MIMETYPES = +WITH_TITAN ?= 1 +ifeq ($(WITH_TITAN),1) + CFLAGS += -DGPLACES_WITH_TITAN + MIMETYPES := $(MIMETYPES);x-scheme-handler/titan +endif WITH_GOPHER ?= 1 ifeq ($(WITH_GOPHER),1) CFLAGS += -DGPLACES_WITH_GOPHER @@ -63,7 +68,7 @@ all: $(BIN) gplacesrc gplaces.desktop $(BIN): $(OBJ) $(CC) $(CFLAGS) -o $(BIN) $(OBJ) $(LDFLAGS) -gplaces.o: gplaces.c gopher.c gophers.c spartan.c finger.c guppy.c tcp.c socket.c +gplaces.o: gplaces.c titan.c gopher.c gophers.c spartan.c finger.c guppy.c tcp.c socket.c gplaces.desktop: gplaces.desktop.in @sed "s~^MimeType=.*~&$(MIMETYPES)~" $< > $@ diff --git a/README.md b/README.md index 91e6f9e..8397dbb 100644 --- a/README.md +++ b/README.md @@ -27,6 +27,7 @@ gplaces is originally a Gemini port of the delve Gopher client by Sebastian Stei * sh-style history with $XDG_DATA_HOME/gplaces_history or ~/.gplaces_history * UTF-8 word wrapping * configurable external pager +* optional Titan support * optional Gopher support * optional gophers:// (Gopher+TLS+TOFU) support * optional Spartan support diff --git a/com.github.dimkr.gplaces.appdata.xml b/com.github.dimkr.gplaces.appdata.xml index 58bc9ac..7bbe78e 100644 --- a/com.github.dimkr.gplaces.appdata.xml +++ b/com.github.dimkr.gplaces.appdata.xml @@ -18,7 +18,7 @@
gplaces should be fairly straightforward to use for anyone used to the command-line and man pages. Type "readme" for a short introduction.
diff --git a/finger.c b/finger.c
index dad88cf..e362d9b 100644
--- a/finger.c
+++ b/finger.c
@@ -2,7 +2,7 @@
================================================================================
gplaces - a simple terminal Gemini client
- Copyright (C) 2022, 2023 Dima Krasner
+ Copyright (C) 2022 - 2024 Dima Krasner
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,12 +19,13 @@
================================================================================
*/
-static void *finger_download(const Selector *sel, URL *url, char **mime, Parser *parser, int ask) {
+static void *finger_download(const Selector *sel, URL *url, char **mime, Parser *parser, unsigned int redirs, int ask) {
static char buffer[1024 + 3]; /* path\r\n\0 */
char *user = NULL;
int fd = -1, len = 0;
(void)sel;
+ (void)redirs;
(void)ask;
switch (curl_url_get(url->cu, CURLUPART_USER, &user, 0)) {
@@ -48,4 +49,4 @@ static void *finger_download(const Selector *sel, URL *url, char **mime, Parser
}
-const Protocol finger = {"finger", "79", tcp_read, tcp_peek, socket_error, tcp_close, finger_download};
+const Protocol finger = {"finger", "79", tcp_read, tcp_peek, socket_error, tcp_close, finger_download, set_fragment};
diff --git a/gopher.c b/gopher.c
index 79ec2d2..b894eab 100644
--- a/gopher.c
+++ b/gopher.c
@@ -2,7 +2,7 @@
================================================================================
gplaces - a simple terminal Gemini client
- Copyright (C) 2022, 2023 Dima Krasner
+ Copyright (C) 2022 - 2024 Dima Krasner
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -58,17 +58,17 @@ static void parse_gophermap_line(char *line, int *pre, Selector **sel, SelectorL
/*============================================================================*/
static char *gopher_request(const Selector *sel, URL *url, int ask, int *len, size_t skip) {
static char buffer[1024 + 3]; /* path\r\n\0 */
- char *input = NULL, *query = NULL;
+ char *input = NULL, *fragment = NULL;
const char *path, *end;
if (url->path[0] == '/' && url->path[1] == '7') {
- switch (curl_url_get(url->cu, CURLUPART_QUERY, &query, CURLU_URLDECODE)) {
- case CURLUE_OK: input = query; break;
- case CURLUE_NO_QUERY: break;
+ switch (curl_url_get(url->cu, CURLUPART_FRAGMENT, &fragment, 0)) {
+ case CURLUE_OK: input = fragment; break;
+ case CURLUE_NO_FRAGMENT: break;
default: return NULL;
}
if (input == NULL) {
- if (!ask || (input = bestline(color ? "\33[35mQuery>\33[0m " : "Query> ")) == NULL || !set_input(url, input)) return NULL;
+ if (!ask || (input = bestline(color ? "\33[35mQuery>\33[0m " : "Query> ")) == NULL || !set_fragment(url, input)) return NULL;
if (interactive) { bestlineHistoryAdd(input); bestlineHistoryAdd(url->url); }
*len = snprintf(buffer, sizeof(buffer), "%s\t%s\r\n", sel->rawurl + skip + strcspn(sel->rawurl + skip, "/") + 2, input);
} else {
@@ -76,8 +76,8 @@ static char *gopher_request(const Selector *sel, URL *url, int ask, int *len, si
if ((end = strrchr(path, '?')) == NULL) *len = snprintf(buffer, sizeof(buffer), "%s\t%s\r\n", path, input);
else *len = snprintf(buffer, sizeof(buffer), "%.*s\t%s\r\n", (int)(end - path), path, input);
}
- if (input != query) free(input);
- curl_free(query);
+ if (input != fragment) free(input);
+ curl_free(fragment);
} else if (url->path[0] == '/' && url->path[1] != '\0') *len = snprintf(buffer, sizeof(buffer), "%s\r\n", sel->rawurl + skip + strcspn(sel->rawurl + skip, "/") + 2);
else *len = snprintf(buffer, sizeof(buffer), "%s\r\n", sel->rawurl + skip + strcspn(sel->rawurl + skip, "/"));
@@ -124,10 +124,12 @@ static void gopher_type(void *c, const URL *url, char **mime, Parser *parser) {
}
-static void *gopher_download(const Selector *sel, URL *url, char **mime, Parser *parser, int ask) {
+static void *gopher_download(const Selector *sel, URL *url, char **mime, Parser *parser, unsigned int redirs, int ask) {
char *buffer;
int fd = -1, len;
+ (void)redirs;
+
if ((buffer = gopher_request(sel, url, ask, &len, 9)) == NULL || (fd = socket_connect(url, SOCK_STREAM)) == -1) goto fail;
if (sendall(fd, buffer, len, MSG_NOSIGNAL) != len) {
if (errno == EAGAIN || errno == EWOULDBLOCK) error(0, "cannot send request to `%s`:`%s`: cancelled", url->host, url->port);
@@ -142,4 +144,4 @@ static void *gopher_download(const Selector *sel, URL *url, char **mime, Parser
}
-const Protocol gopher = {"gopher", "70", tcp_read, tcp_peek, socket_error, tcp_close, gopher_download};
+const Protocol gopher = {"gopher", "70", tcp_read, tcp_peek, socket_error, tcp_close, gopher_download, set_fragment};
diff --git a/gophers.c b/gophers.c
index 94b2d6f..3d65481 100644
--- a/gophers.c
+++ b/gophers.c
@@ -2,7 +2,7 @@
================================================================================
gplaces - a simple terminal Gemini client
- Copyright (C) 2022, 2023 Dima Krasner
+ Copyright (C) 2022 - 2024 Dima Krasner
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,12 +19,14 @@
================================================================================
*/
-static void *gophers_download(const Selector *sel, URL *url, char **mime, Parser *parser, int ask) {
+static void *gophers_download(const Selector *sel, URL *url, char **mime, Parser *parser, unsigned int redirs, int ask) {
char *buffer;
SSL_CTX *ctx = NULL;
SSL *ssl = NULL;
int len, err;
+ (void)redirs;
+
if ((ctx = SSL_CTX_new(TLS_client_method())) == NULL) return NULL;
SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL);
@@ -43,4 +45,4 @@ static void *gophers_download(const Selector *sel, URL *url, char **mime, Parser
}
-const Protocol gophers = {"gophers", "70", ssl_read, ssl_peek, ssl_error, ssl_close, gophers_download};
+const Protocol gophers = {"gophers", "70", ssl_read, ssl_peek, ssl_error, ssl_close, gophers_download, set_fragment};
diff --git a/gplaces.c b/gplaces.c
index 475de68..5736084 100644
--- a/gplaces.c
+++ b/gplaces.c
@@ -81,7 +81,8 @@ typedef struct Protocol {
int (*peek)(void *, void *, int);
int (*error)(const URL *, void *, int);
void (*close)(void *);
- void *(*download)(const Selector *, URL *, char **mime, Parser *, int ask);
+ void *(*download)(const Selector *, URL *, char **mime, Parser *, unsigned int redirs, int ask);
+ int (*set_input)(URL *url, const char *input);
} Protocol;
struct Selector {
@@ -128,6 +129,9 @@ typedef struct Help {
/*============================================================================*/
const Protocol gemini;
+#ifdef GPLACES_WITH_TITAN
+const Protocol titan;
+#endif
#ifdef GPLACES_WITH_GOPHERS
const Protocol gophers;
#endif
@@ -288,7 +292,7 @@ static void free_selectors(SelectorList *list) {
}
-static int set_input(URL *url, const char *input) {
+static int set_query(URL *url, const char *input) {
char *query, *tmp;
if ((query = curl_easy_escape(NULL, input, 0)) == NULL) return 0;
if (curl_url_set(url->cu, CURLUPART_QUERY, query, CURLU_NON_SUPPORT_SCHEME) != CURLUE_OK || curl_url_get(url->cu, CURLUPART_URL, &tmp, 0) != CURLUE_OK) { curl_free(query); return 0; }
@@ -298,6 +302,16 @@ static int set_input(URL *url, const char *input) {
}
+#if defined(GPLACES_WITH_TITAN) || defined(GPLACES_WITH_GOPHER) || defined(GPLACES_WITH_GOPHERS) || defined(GPLACSE_WITH_FINGER)
+static int set_fragment(URL *url, const char *input) {
+ char *tmp;
+ if (curl_url_set(url->cu, CURLUPART_FRAGMENT, input, CURLU_NON_SUPPORT_SCHEME) != CURLUE_OK || curl_url_get(url->cu, CURLUPART_URL, &tmp, 0) != CURLUE_OK) return 0;
+ curl_free(url->url); url->url = tmp;
+ return 1;
+}
+#endif
+
+
static int parse_url(URL *url, const char *rawurl, const char *from, const char *input) {
static char buffer[1024];
#if defined(GPLACES_USE_LIBIDN2) || defined(GPLACES_USE_LIBIDN)
@@ -328,30 +342,14 @@ static int parse_url(URL *url, const char *rawurl, const char *from, const char
valid:
#endif
- if (input != NULL && input[0] != '\0' && !set_input(url, input)) return 0;
- else if ((input == NULL || input[0] == '\0') && curl_url_get(url->cu, CURLUPART_URL, &url->url, 0) != CURLUE_OK) return 0;
-
- if (curl_url_get(url->cu, CURLUPART_SCHEME, &url->scheme, 0) != CURLUE_OK || (!(file = (strcmp(url->scheme, "file") == 0)) && curl_url_get(url->cu, CURLUPART_HOST, &url->host, 0) != CURLUE_OK)) return 0;
-
-#if defined(GPLACES_USE_LIBIDN2) || defined(GPLACES_USE_LIBIDN)
- #ifdef GPLACES_USE_LIBIDN2
- if (!file && (idn2_to_ascii_8z(url->host, &host, IDN2_NONTRANSITIONAL) == IDN2_OK || idn2_to_ascii_8z(url->host, &host, IDN2_TRANSITIONAL) == IDN2_OK)) {
- #elif defined(GPLACES_USE_LIBIDN)
- if (!file && idna_to_ascii_8z(url->host, &host, 0) == IDNA_SUCCESS) {
- #endif
- if (curl_url_set(url->cu, CURLUPART_HOST, host, 0) != CURLUE_OK) { free(host); return 0; }
- free(host);
- curl_free(url->host); url->host = NULL;
- if (curl_url_get(url->cu, CURLUPART_HOST, &url->host, 0) != CURLUE_OK) return 0;
- }
-#endif
-
- if (curl_url_get(url->cu, CURLUPART_PATH, &url->path, 0) != CURLUE_OK) return 0;
-
- if (file) return 1;
+ if (curl_url_get(url->cu, CURLUPART_SCHEME, &url->scheme, 0) != CURLUE_OK) return 0;
if (strcmp(url->scheme, "gemini") == 0) {
url->proto = &gemini;
+#ifdef GPLACES_WITH_TITAN
+ } else if (strcmp(url->scheme, "titan") == 0) {
+ url->proto = &titan;
+#endif
#ifdef GPLACES_WITH_GOPHER
} else if (strcmp(url->scheme, "gopher") == 0) {
url->proto = &gopher;
@@ -374,6 +372,28 @@ static int parse_url(URL *url, const char *rawurl, const char *from, const char
#endif
}
+ if (input != NULL && input[0] != '\0' && !url->proto->set_input(url, input)) return 0;
+ else if ((input == NULL || input[0] == '\0') && curl_url_get(url->cu, CURLUPART_URL, &url->url, 0) != CURLUE_OK) return 0;
+
+ if (!(file = (strcmp(url->scheme, "file")) == 0) && curl_url_get(url->cu, CURLUPART_HOST, &url->host, 0) != CURLUE_OK) return 0;
+
+#if defined(GPLACES_USE_LIBIDN2) || defined(GPLACES_USE_LIBIDN)
+ #ifdef GPLACES_USE_LIBIDN2
+ if (!file && (idn2_to_ascii_8z(url->host, &host, IDN2_NONTRANSITIONAL) == IDN2_OK || idn2_to_ascii_8z(url->host, &host, IDN2_TRANSITIONAL) == IDN2_OK)) {
+ #elif defined(GPLACES_USE_LIBIDN)
+ if (!file && idna_to_ascii_8z(url->host, &host, 0) == IDNA_SUCCESS) {
+ #endif
+ if (curl_url_set(url->cu, CURLUPART_HOST, host, 0) != CURLUE_OK) { free(host); return 0; }
+ free(host);
+ curl_free(url->host); url->host = NULL;
+ if (curl_url_get(url->cu, CURLUPART_HOST, &url->host, 0) != CURLUE_OK) return 0;
+ }
+#endif
+
+ if (curl_url_get(url->cu, CURLUPART_PATH, &url->path, 0) != CURLUE_OK) return 0;
+
+ if (file) return 1;
+
switch (curl_url_get(url->cu, CURLUPART_PORT, &url->port, 0)) {
case CURLUE_OK: break;
case CURLUE_NO_PORT:
@@ -960,7 +980,7 @@ static int save_body(const URL *url, void *c, FILE *fp) {
}
-static int do_download(URL *url, SSL **body, char **mime, int ask) {
+static int ssl_download(URL *url, SSL **body, char **mime, int request(const URL *, SSL *, void *), void *p, int ask) {
static char crtpath[1024], keypath[1024], suffix[1024], buffer[1024], data[2 + 1 + 1024 + 2 + 1]; /* 99 meta\r\n\0 */
struct stat stbuf;
const char *home;
@@ -1015,8 +1035,7 @@ static int do_download(URL *url, SSL **body, char **mime, int ask) {
if ((ssl = ssl_connect(url, ctx, ask)) == NULL) goto fail;
- len = snprintf(buffer, sizeof(buffer), "%s\r\n", url->url);
- if ((err = SSL_get_error(ssl, SSL_write(ssl, buffer, len >= (int)sizeof(buffer) ? (int)sizeof(buffer) - 1 : len))) != SSL_ERROR_NONE) {
+ if ((err = request(url, ssl, p)) != SSL_ERROR_NONE) {
if (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE) error(0, "cannot send request to `%s`:`%s`: cancelled", url->host, url->port);
else error(0, "cannot send request to `%s`:`%s`: error %d", url->host, url->port, err);
goto fail;
@@ -1047,7 +1066,7 @@ static int do_download(URL *url, SSL **body, char **mime, int ask) {
if ((line = bestline(buffer)) == NULL) goto fail;
if (data[1] != '1' && interactive) bestlineHistoryAdd(line);
if (data[1] == '1') bestlineMaskModeDisable();
- if (!set_input(url, line)) { free(line); goto fail; }
+ if (!set_query(url, line)) { free(line); goto fail; }
free(line);
if (data[1] != '1' && interactive) bestlineHistoryAdd(url->url);
break;
@@ -1087,21 +1106,30 @@ static int do_download(URL *url, SSL **body, char **mime, int ask) {
}
-static void sigint(int sig) {
- (void)sig;
+/*============================================================================*/
+static int gemini_request(const URL *url, SSL *ssl, void *p) {
+ static char buffer[1024];
+ int len;
+
+ (void)p;
+
+ len = snprintf(buffer, sizeof(buffer), "%s\r\n", url->url);
+ return SSL_get_error(ssl, SSL_write(ssl, buffer, len >= (int)sizeof(buffer) ? (int)sizeof(buffer) - 1 : len));
}
-static void *gemini_download(const Selector *sel, URL *url, char **mime, Parser *parser, int ask) {
+static void *gemini_download(const Selector *sel, URL *url, char **mime, Parser *parser, unsigned int redirs, int ask) {
SSL *ssl = NULL;
- int status, redirs = 0;
+ int status = -1;
(void)sel;
do {
- status = do_download(url, &ssl, mime, ask);
+ status = ssl_download(url, &ssl, mime, gemini_request, NULL, ask);
if (status >= 20 && status <= 29) break;
- } while ((status >= 10 && status <= 19) || (status >= 60 && status <= 69) || (status >= 30 && status <= 39 && ++redirs < 5));
+ } while ((status >= 10 && status <= 19) || (status >= 60 && status <= 69) || (status >= 30 && status <= 39 && ++redirs < 5 && url->proto->download == gemini_download));
+
+ if (redirs < 5 && url->proto->download != gemini_download) return url->proto->download(sel, url, mime, parser, redirs, ask);
if (ssl != NULL && strncmp(*mime, "text/gemini", 11) == 0) *parser = parse_gemtext_line;
else if (ssl != NULL && (!interactive || strncmp(*mime, "text/plain", 10) == 0)) *parser = parse_plaintext_line;
@@ -1111,10 +1139,13 @@ static void *gemini_download(const Selector *sel, URL *url, char **mime, Parser
}
-const Protocol gemini = {"gemini", "1965", ssl_read, ssl_peek, ssl_error, ssl_close, gemini_download};
+const Protocol gemini = {"gemini", "1965", ssl_read, ssl_peek, ssl_error, ssl_close, gemini_download, set_query};
/*============================================================================*/
+#ifdef GPLACES_WITH_TITAN
+ #include "titan.c"
+#endif
#if defined(GPLACES_WITH_GOPHER) || defined(GPLACES_WITH_SPARTAN) || defined(GPLACES_WITH_FINGER) || defined(GPLACES_WITH_GUPPY)
#include "socket.c"
#endif
@@ -1174,7 +1205,7 @@ static void stream_to_handler(const Selector *sel, URL *url, const char *filenam
if (pipe(fds) == -1) return;
if (fcntl(fds[1], F_SETFD, FD_CLOEXEC) == 0 && (fp = fdopen(fds[1], "w")) != NULL) {
setbuf(fp, NULL);
- if ((c = url->proto->download(sel, url, &mime, &parser, 1)) != NULL) {
+ if ((c = url->proto->download(sel, url, &mime, &parser, 0, 1)) != NULL) {
if ((handler = find_mime_handler(mime)) != NULL && (pid = start_handler(handler, filename, command, sizeof(command), sel, url, fds[0])) > 0) {
close(fds[0]); fds[0] = -1;
save_body(url, c, fp);
@@ -1219,7 +1250,7 @@ static void download_to_file(const Selector *sel, URL *url, const char *def) {
}
if ((fp = fopen(filename, "wb")) == NULL) error(0, "cannot create file `%s`: %s", filename, strerror(errno));
else {
- if ((c = url->proto->download(sel, url, &mime, &parser, 1)) != NULL) {
+ if ((c = url->proto->download(sel, url, &mime, &parser, 0, 1)) != NULL) {
ret = save_body(url, c, fp);
url->proto->close(c);
}
@@ -1266,7 +1297,7 @@ static SelectorList download_text(const Selector *sel, URL *url, int ask, int ha
size_t parsed, length = 0, total = 0, prog = 0;
int received, pre = 0, width, ok = 0, links = 0;
- if (url->proto == NULL || (c = url->proto->download(sel, url, &mime, &parser, ask)) == NULL) goto out;
+ if (url->proto == NULL || (c = url->proto->download(sel, url, &mime, &parser, 0, ask)) == NULL) goto out;
if (parser == NULL) {
if (handle) save_and_handle(sel, url, c, mime);
goto out;
@@ -1795,6 +1826,11 @@ static void quit_client() {
}
+static void sigint(int sig) {
+ (void)sig;
+}
+
+
int main(int argc, char **argv) {
struct sigaction sa = {.sa_handler = sigint};
diff --git a/guppy.c b/guppy.c
index 4897796..ae5ab90 100644
--- a/guppy.c
+++ b/guppy.c
@@ -2,7 +2,7 @@
================================================================================
gplaces - a simple terminal Gemini client
- Copyright (C) 2022, 2023 Dima Krasner
+ Copyright (C) 2022 - 2024 Dima Krasner
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -115,7 +115,7 @@ static int do_guppy_download(URL *url, GuppySocket *s, char **mime, int ask) {
free(chunk);
if ((input = bestline(prompt)) == NULL) return 4;
if (interactive) bestlineHistoryAdd(input);
- if (!set_input(url, input)) { free(input); return 4; }
+ if (!set_query(url, input)) { free(input); return 4; }
free(input);
if (interactive) bestlineHistoryAdd(url->url);
} else if (chunk->buffer[0] == '3') {
@@ -170,9 +170,9 @@ static int do_guppy_download(URL *url, GuppySocket *s, char **mime, int ask) {
}
-static void *guppy_download(const Selector *sel, URL *url, char **mime, Parser *parser, int ask) {
+static void *guppy_download(const Selector *sel, URL *url, char **mime, Parser *parser, unsigned int redirs, int ask) {
GuppySocket *s = NULL;
- int status, redirs = 0;
+ int status;
(void)sel;
@@ -186,7 +186,9 @@ static void *guppy_download(const Selector *sel, URL *url, char **mime, Parser *
status = do_guppy_download(url, s, mime, ask);
/* stop on success, on error or when the redirect limit is exhausted */
if (status > 5) break;
- } while (((status == 1) || (status == 3)) && ++redirs < 5);
+ } while (status == 1 || ((status == 3 && ++redirs < 5 && url->proto->download == guppy_download)));
+
+ if (redirs < 5 && url->proto->download != guppy_download) { guppy_close(s); return url->proto->download(sel, url, mime, parser, redirs, ask); }
if (status > 6 && strncmp(*mime, "text/gemini", 11) == 0) *parser = parse_gemtext_line;
else if (status > 6 && strncmp(*mime, "text/plain", 10) == 0) *parser = parse_plaintext_line;
@@ -270,4 +272,4 @@ static int guppy_read(void *c, void *buffer, int length) {
}
-const Protocol guppy = {"guppy", "6775", guppy_read, NULL, socket_error, guppy_close, guppy_download};
+const Protocol guppy = {"guppy", "6775", guppy_read, NULL, socket_error, guppy_close, guppy_download, set_query};
diff --git a/spartan.c b/spartan.c
index 0d1270f..5b261a9 100644
--- a/spartan.c
+++ b/spartan.c
@@ -2,7 +2,7 @@
================================================================================
gplaces - a simple terminal Gemini client
- Copyright (C) 2022, 2023 Dima Krasner
+ Copyright (C) 2022 - 2024 Dima Krasner
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -76,11 +76,11 @@ static int do_spartan_download(URL *url, int *body, char **mime, const char *inp
}
-static void *spartan_download(const Selector *sel, URL *url, char **mime, Parser *parser, int ask) {
+static void *spartan_download(const Selector *sel, URL *url, char **mime, Parser *parser, unsigned int redirs, int ask) {
char *input = NULL, *query = NULL;
size_t inputlen = 0;
static int fd = -1;
- int status, redirs = 0;
+ int status;
switch (curl_url_get(url->cu, CURLUPART_QUERY, &query, 0)) {
case CURLUE_OK: input = query; break;
@@ -88,7 +88,7 @@ static void *spartan_download(const Selector *sel, URL *url, char **mime, Parser
default: return NULL;
}
if (sel->prompt && (input == NULL || *input == '\0')) {
- if (!ask || (input = bestline(color ? "\33[35mData>\33[0m " : "Data> ")) == NULL || !set_input(url, input)) goto fail;
+ if (!ask || (input = bestline(color ? "\33[35mData>\33[0m " : "Data> ")) == NULL || !set_query(url, input)) goto fail;
if (interactive) { bestlineHistoryAdd(input); bestlineHistoryAdd(url->url); }
}
if (input != NULL) inputlen = strlen(input);
@@ -96,7 +96,9 @@ static void *spartan_download(const Selector *sel, URL *url, char **mime, Parser
do {
status = do_spartan_download(url, &fd, mime, input, inputlen, ask);
if (status == 2) break;
- } while (status == 3 && ++redirs < 5);
+ } while (status == 3 && ++redirs < 5 && url->proto->download == spartan_download);
+
+ if (redirs < 5 && url->proto->download != spartan_download) return url->proto->download(sel, url, mime, parser, redirs, ask);
if (fd != -1 && strncmp(*mime, "text/gemini", 11) == 0) *parser = parse_spartan_line;
else if (fd != -1 && strncmp(*mime, "text/plain", 10) == 0) *parser = parse_plaintext_line;
@@ -110,4 +112,4 @@ static void *spartan_download(const Selector *sel, URL *url, char **mime, Parser
}
-const Protocol spartan = {"spartan", "300", tcp_read, tcp_peek, socket_error, tcp_close, spartan_download};
+const Protocol spartan = {"spartan", "300", tcp_read, tcp_peek, socket_error, tcp_close, spartan_download, set_query};
diff --git a/titan.c b/titan.c
new file mode 100644
index 0000000..8167368
--- /dev/null
+++ b/titan.c
@@ -0,0 +1,120 @@
+/*
+================================================================================
+
+ gplaces - a simple terminal Gemini client
+ Copyright (C) 2024 Dima Krasner
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see