Large refactoring and renaming
`gemini_open' is now `gemini_connect' `gemini_close' is now `gemini_fini' `gemini_connect' will try to use the `socketfd' struct member, if valid. `gemini_read' now returns 0 whenever the response header has been read. `gemini_read' no longer handles user-input (status 10) itself. `gemini_connect_query' has been added so callers can handle user-input. `libgeminiclient.c' no longer needs `readpassphrase' and `libbsd'.
This commit is contained in:
parent
3f17926bce
commit
a96621e3fd
24
Makefile
24
Makefile
|
@ -1,21 +1,23 @@
|
|||
.POSIX:
|
||||
MAGIC= if pkg-config --exists libtls; then \
|
||||
export CFLAGS="$$CFLAGS `pkg-config --cflags libtls`"; \
|
||||
export LDFLAGS="$$LDFLAGS `pkg-config --libs libtls`"; \
|
||||
else \
|
||||
export LDFLAGS="$$LDFLAGS -ltls"; \
|
||||
fi; \
|
||||
if pkg-config --exists libbsd; then \
|
||||
export CFLAGS="$$CFLAGS `pkg-config --cflags libbsd`"; \
|
||||
export LDFLAGS="$$LDFLAGS `pkg-config --libs libbsd`"; \
|
||||
MAGICTLS=\
|
||||
if pkg-config --exists libtls; then\
|
||||
export CFLAGS="$$CFLAGS `pkg-config --cflags libtls`";\
|
||||
export LDFLAGS="$$LDFLAGS `pkg-config --libs libtls`";\
|
||||
else\
|
||||
export LDFLAGS="$$LDFLAGS -ltls";\
|
||||
fi
|
||||
MAGICBSD=\
|
||||
if pkg-config --exists libbsd; then\
|
||||
export CFLAGS="$$CFLAGS `pkg-config --cflags libbsd`";\
|
||||
export LDFLAGS="$$LDFLAGS `pkg-config --libs libbsd`";\
|
||||
fi
|
||||
default: magic-libgeminiclient.a
|
||||
all: libgeminiclient.a gemini-cat
|
||||
magic: magic-libgeminiclient.a magic-gemini-cat
|
||||
magic-libgeminiclient.a:
|
||||
@${MAGIC}; ${MAKE} ${MAKEFLAGS} libgeminiclient.a
|
||||
@${MAGICTLS}; ${MAKE} ${MAKEFLAGS} libgeminiclient.a
|
||||
magic-gemini-cat:
|
||||
@${MAGIC}; ${MAKE} ${MAKEFLAGS} gemini-cat
|
||||
@${MAGICTLS}; ${MAGICBSD}; ${MAKE} ${MAKEFLAGS} gemini-cat
|
||||
libgeminiclient.a: libgeminiclient.o
|
||||
${AR} ${ARFLAGS} $@ libgeminiclient.o
|
||||
libgeminiclient.o: libgeminiclient.c libgeminiclient.h
|
||||
|
|
45
gemini-cat.c
45
gemini-cat.c
|
@ -8,6 +8,7 @@
|
|||
#include <unistd.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <readpassphrase.h>
|
||||
#include <sysexits.h>
|
||||
|
||||
#include <tls.h>
|
||||
|
@ -25,8 +26,10 @@ main(int argc, char *argv[])
|
|||
{
|
||||
char b[BUFFER_SIZE];
|
||||
struct gemini gemini = GEMINI_INITIALIZER;
|
||||
const char *query = NULL;
|
||||
ssize_t r;
|
||||
size_t i;
|
||||
int prompt = 0;
|
||||
int rawout;
|
||||
int c;
|
||||
|
||||
|
@ -48,7 +51,7 @@ main(int argc, char *argv[])
|
|||
gemini.certfile = optarg;
|
||||
break;
|
||||
case 'i':
|
||||
gemini.flags |= GEMINI_PROMPT;
|
||||
prompt = 1;
|
||||
break;
|
||||
case 'k':
|
||||
gemini.keyfile = optarg;
|
||||
|
@ -70,8 +73,7 @@ main(int argc, char *argv[])
|
|||
warnx("Invalid number: %s", optarg);
|
||||
break;
|
||||
case 's':
|
||||
gemini.flags |=
|
||||
GEMINI_PROMPT | GEMINI_SECPROMPT;
|
||||
prompt = 2;
|
||||
break;
|
||||
case 't':
|
||||
gemini.tofufile = optarg;
|
||||
|
@ -89,9 +91,12 @@ main(int argc, char *argv[])
|
|||
pledge("stdio rpath flock inet dns tty", NULL) != 0)
|
||||
err(EX_NOPERM, "Failed pledge(2)");
|
||||
#endif
|
||||
for (i = 0; i < argc; i++) {
|
||||
if (gemini_open(&gemini, argv[i]) != 0)
|
||||
for (i = 0; i < argc; i += (query == NULL)) {
|
||||
if (gemini_connect_query(&gemini, argv[i], query)
|
||||
!= 0) {
|
||||
warn("Failed to connect to %s", argv[i]);
|
||||
goto err;
|
||||
}
|
||||
#ifdef __OpenBSD__
|
||||
if (i == 0 &&
|
||||
pledge("stdio rpath flock inet dns tty", NULL) != 0)
|
||||
|
@ -102,14 +107,38 @@ main(int argc, char *argv[])
|
|||
r = stripcntrl(b, sizeof(b), b, r);
|
||||
(void)fwrite(b, 1, r, stdout);
|
||||
}
|
||||
if (r < 0)
|
||||
if (r < 0) {
|
||||
warn("Gemini read error");
|
||||
goto err;
|
||||
}
|
||||
query = NULL;
|
||||
switch (gemini.status / 10) {
|
||||
case 1:
|
||||
if (prompt > 0 && readpassphrase(gemini.meta, b,
|
||||
sizeof(b), prompt > 1 ? RPP_ECHO_OFF :
|
||||
RPP_ECHO_ON) == NULL) {
|
||||
warn("Could not get user-input");
|
||||
goto err;
|
||||
}
|
||||
query = prompt > 0 ? b : NULL;
|
||||
break;
|
||||
case 2:
|
||||
break;
|
||||
case 6:
|
||||
warnx("Gemini certificate error %02hd: %s",
|
||||
gemini.status, gemini.meta);
|
||||
break;
|
||||
default:
|
||||
warnx("Gemini error %02hd: %s", gemini.status,
|
||||
gemini.meta);
|
||||
break;
|
||||
}
|
||||
gemini_reset(&gemini);
|
||||
}
|
||||
gemini_close(&gemini);
|
||||
gemini_fini(&gemini);
|
||||
return (0);
|
||||
err:
|
||||
gemini_close(&gemini);
|
||||
gemini_fini(&gemini);
|
||||
return (EX_SOFTWARE);
|
||||
usage:
|
||||
(void)fprintf(stderr, "usage: gemini-cat [-RSisw] "
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
.Dd Apr 30, 2020
|
||||
.Dd May 1, 2020
|
||||
.Dt LIBGEMINICLIENT 3
|
||||
.Os
|
||||
.Sh NAME
|
||||
.Nm gemini_create , gemini_init , gemini_open , gemini_read ,
|
||||
.Nm gemini_reset , gemini_close , gemini_destroy
|
||||
.Nm gemini_create , gemini_init , gemini_connect ,
|
||||
.Nm gemini_connect_query , gemini_read , gemini_reset , gemini_fini ,
|
||||
.Nm gemini_destroy
|
||||
.Sh SYNOPSIS
|
||||
.In libgeminiclient.h
|
||||
.Ft struct gemini *
|
||||
|
@ -11,13 +12,16 @@
|
|||
.Ft void
|
||||
.Fn gemini_init "struct gemini *ctx"
|
||||
.Ft int
|
||||
.Fn gemini_open "struct gemini *ctx" "const char *url"
|
||||
.Fn gemini_connect "struct gemini *ctx" "const char *url"
|
||||
.Ft int
|
||||
.Fn gemini_connect_query "struct gemini *ctx" "const char *url" \
|
||||
"const char *query"
|
||||
.Ft ssize_t
|
||||
.Fn gemini_read "struct gemini *ctx" "void *buffer" "size_t size"
|
||||
.Ft void
|
||||
.Fn gemini_reset "struct gemini *ctx"
|
||||
.Ft void
|
||||
.Fn gemini_close "struct gemini *ctx"
|
||||
.Fn gemini_fini "struct gemini *ctx"
|
||||
.Ft void
|
||||
.Fn gemini_destroy "struct gemini *ctx"
|
||||
.Sh DESCRIPTION
|
||||
|
@ -25,25 +29,35 @@
|
|||
allocates a
|
||||
.Vt gemini
|
||||
structure and calls
|
||||
.Fn gemini_init on it.
|
||||
.Fn gemini_init
|
||||
on it.
|
||||
.Pp
|
||||
.Fn gemini_init
|
||||
Initialized the provided
|
||||
Initializes the provided
|
||||
.Vt gemini
|
||||
structure.
|
||||
.Pp
|
||||
.Fn gemini_open
|
||||
.Fn gemini_connect
|
||||
opens a connection to the provided
|
||||
.Fa url ,
|
||||
and sets up the
|
||||
.Vt gemini
|
||||
structure for reading.
|
||||
.Pp
|
||||
.Fn gemini_connect_query
|
||||
calls
|
||||
.Fn gemini_connect
|
||||
with the escaped
|
||||
.Fa query
|
||||
appended to
|
||||
.Fa url
|
||||
separated by a question-mark.
|
||||
.Pp
|
||||
.Fn gemini_read
|
||||
attempts to read using the
|
||||
.Vt gemini
|
||||
structure previously set up by
|
||||
.Fn gemini_open ,
|
||||
.Fn gemini_connect ,
|
||||
sending the request and parsing the response as needed.
|
||||
After a successful return the
|
||||
.Fa meta ,
|
||||
|
@ -82,8 +96,7 @@ META contains additional information.
|
|||
prepares the provided
|
||||
.Vt gemini
|
||||
structure to be reused for
|
||||
.Pp
|
||||
.Fn gemini_open
|
||||
.Fn gemini_connect
|
||||
while keeping reusable data.
|
||||
This prevents user-configurable information from being reset,
|
||||
and prevents
|
||||
|
@ -221,13 +234,15 @@ returns a pointer to a newly initialized
|
|||
.Vt gemini
|
||||
structure, or a NULL on a failure.
|
||||
.Pp
|
||||
.Fn gemini_open
|
||||
.Fn gemini_connect
|
||||
and
|
||||
.Fn gemini_connect_query
|
||||
returns a 0 on success and a -1 on an error.
|
||||
.Pp
|
||||
.Fn gemini_read
|
||||
returns a -1 on an error, otherwise it returns the amount of data
|
||||
read into the buffer, with 0 only being returned when there is nothing
|
||||
left to read.
|
||||
left to read after successfully reading the response header.
|
||||
.Sh EXAMPLES
|
||||
The following example shows how you initialize various values:
|
||||
.Bd -literal -offset indent -compact
|
||||
|
@ -238,7 +253,7 @@ gemini->tofufile = tofufile;
|
|||
gemini->flags |= GEMINI_TOFU_WRITE;
|
||||
gemini->port = port;
|
||||
while ((url = get_url()) != NULL)
|
||||
if (gemini_open(gemini) != 0)
|
||||
if (gemini_connect(gemini) != 0)
|
||||
continue;
|
||||
...
|
||||
gemini_reset(gemini);
|
||||
|
@ -251,13 +266,18 @@ codes yourself:
|
|||
.Bd -literal -offset indent -compact
|
||||
gemini->maxredirects = 0;
|
||||
while ((url = get_url()) != NULL) {
|
||||
if (gemini_open(gemini, url) != 0) {
|
||||
if (gemini_connect(gemini, url) != 0) {
|
||||
warn("Could not open: %s", url);
|
||||
continue;
|
||||
}
|
||||
while ((r = gemini_read(gemini, buf, bufsize)) > 0) {
|
||||
...
|
||||
}
|
||||
if (r < 0) {
|
||||
warn("Error while reading");
|
||||
gemini_reset(gemini);
|
||||
continue;
|
||||
}
|
||||
switch (gemini->status / 10) {
|
||||
...
|
||||
case 3:
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
#include <time.h>
|
||||
|
||||
#include <err.h>
|
||||
#include <readpassphrase.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
@ -40,18 +39,9 @@ enum {
|
|||
#define ISASCIIALPHA(c) (ISASCIILOWER(c) || ISASCIIUPPER(c))
|
||||
#define ISASCIIALNUM(c) (ISASCIIALPHA(c) || ISASCIIDIGIT(c))
|
||||
|
||||
#define ISASCIILOWER(c) ((c) >= 'a' && (c) <= 'z')
|
||||
#define ISASCIIUPPER(c) ((c) >= 'A' && (c) <= 'Z')
|
||||
#define ISASCIIDIGIT(c) ((c) >= '0' && (c) <= '9')
|
||||
#define ISASCIIXDIGIT(c) (ISASCIIDIGIT(c) || \
|
||||
((c) >= 'A' && (c) <= 'F') || ((c) >= 'a' && (c) <= 'f'))
|
||||
#define ISASCIIALPHA(c) (ISASCIILOWER(c) || ISASCIIUPPER(c))
|
||||
#define ISASCIIALNUM(c) (ISASCIIALPHA(c) || ISASCIIDIGIT(c))
|
||||
|
||||
#define GET_HOSTNAME_PORT 0x01
|
||||
#define GET_HOSTNAME_VALID 0x02
|
||||
|
||||
static int gemini_bounce(struct gemini *, const char *);
|
||||
static int set_tofu(struct gemini_tofu **, const char *,
|
||||
const char *, time_t);
|
||||
static int load_tofu(struct gemini_tofu **, int);
|
||||
|
@ -78,6 +68,7 @@ gemini_init(struct gemini *g)
|
|||
g->keylen = 0;
|
||||
g->certlen = 0;
|
||||
g->index = 0;
|
||||
g->socketfd = -1;
|
||||
g->tofufd = -1;
|
||||
g->tofumod = 0;
|
||||
g->flags = 0;
|
||||
|
@ -108,6 +99,8 @@ gemini_reset(struct gemini *g)
|
|||
g->state = GEMINI_STATE_INIT;
|
||||
g->redirects = 0;
|
||||
g->status = 0;
|
||||
if (g->port == 0)
|
||||
g->port = GEMINI_DEFAULT_PORT;
|
||||
}
|
||||
|
||||
struct gemini *
|
||||
|
@ -122,7 +115,34 @@ gemini_create(void)
|
|||
}
|
||||
|
||||
int
|
||||
gemini_open(struct gemini *g, const char *url)
|
||||
gemini_connect_query(struct gemini *g, const char *url, const char *q)
|
||||
{
|
||||
char b[GEMINI_URL_MAX + 1];
|
||||
size_t urllen;
|
||||
size_t querylen;
|
||||
|
||||
if ((urllen = strlen(url)) > GEMINI_URL_MAX) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
(void)memcpy(b, url, urllen);
|
||||
b[urllen] = '\0';
|
||||
if (q != NULL) {
|
||||
b[urllen] = '?';
|
||||
querylen = strlen(q);
|
||||
if (urllen == GEMINI_URL_MAX ||
|
||||
escapequery(b + urllen + 1,
|
||||
GEMINI_URL_MAX - urllen - 1 + 1, q, querylen) >
|
||||
GEMINI_URL_MAX - urllen - 1) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
return (gemini_connect(g, b));
|
||||
}
|
||||
|
||||
int
|
||||
gemini_connect(struct gemini *g, const char *url)
|
||||
{
|
||||
char portstr[16];
|
||||
const char *hash;
|
||||
|
@ -133,9 +153,8 @@ gemini_open(struct gemini *g, const char *url)
|
|||
int e;
|
||||
short port;
|
||||
|
||||
if (g->port == 0)
|
||||
g->port = GEMINI_DEFAULT_PORT;
|
||||
if (g->state != GEMINI_STATE_INIT || g->port < 0) {
|
||||
gemini_reset(g);
|
||||
if (g->port < 0) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
|
@ -152,8 +171,8 @@ gemini_open(struct gemini *g, const char *url)
|
|||
portptr = portstr + i + 1;
|
||||
/* Extract the `hostname' */
|
||||
if ((i = get_hostname(g->request, GEMINI_HOSTNAME_MAX + 1,
|
||||
url, urllen, GET_HOSTNAME_PORT | GET_HOSTNAME_VALID))
|
||||
== 0 || i > GEMINI_URL_MAX) {
|
||||
url, urllen, (g->socketfd < 0 ? GET_HOSTNAME_PORT : 0) |
|
||||
GET_HOSTNAME_VALID)) == 0 || i > GEMINI_URL_MAX) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
}
|
||||
|
@ -181,9 +200,10 @@ gemini_open(struct gemini *g, const char *url)
|
|||
}
|
||||
/* Connect */
|
||||
if (tls_configure(g->tls, g->tls_config) != 0 ||
|
||||
tls_connect(g->tls, g->request,
|
||||
(strchr(g->request, ':') == NULL ? portptr : NULL)) != 0 ||
|
||||
tls_handshake(g->tls)) {
|
||||
(g->socketfd >= 0 && tls_connect_socket(g->tls, g->socketfd,
|
||||
g->request) != 0) || (g->socketfd < 0 && tls_connect(g->tls,
|
||||
g->request, (strchr(g->request, ':') == NULL ? portptr :
|
||||
NULL))) || (e = tls_handshake(g->tls)) == -1) {
|
||||
warnx("TLS Error: %s", tls_error(g->tls));
|
||||
goto err;
|
||||
}
|
||||
|
@ -217,61 +237,27 @@ gemini_open(struct gemini *g, const char *url)
|
|||
* input gemini://example.com/?inputdata
|
||||
* redirect gemini://example.com/some/path
|
||||
*/
|
||||
if ((e = set_tofu(&g->tofu, g->request, hash,
|
||||
tls_peer_cert_notafter(g->tls))) < 0)
|
||||
goto err;
|
||||
if (e > 0)
|
||||
switch (set_tofu(&g->tofu, g->request, hash,
|
||||
tls_peer_cert_notafter(g->tls))) {
|
||||
case 0:
|
||||
break;
|
||||
case 1:
|
||||
g->tofumod = 1;
|
||||
break;
|
||||
default:
|
||||
goto err;
|
||||
}
|
||||
/* Construct the request */
|
||||
(void)memcpy(g->request, url, urllen);
|
||||
g->request[g->reqlen - 2] = '\r';
|
||||
g->request[g->reqlen - 1] = '\n';
|
||||
g->state = GEMINI_STATE_REQUEST;
|
||||
return (0);
|
||||
return (e);
|
||||
err:
|
||||
gemini_reset(g);
|
||||
return (-1);
|
||||
}
|
||||
|
||||
void
|
||||
gemini_close(struct gemini *g)
|
||||
{
|
||||
|
||||
if ((g->flags & GEMINI_TOFU_WRITE) && g->tofufd >= 0 &&
|
||||
g->tofumod && (lseek(g->tofufd, 0, SEEK_SET) < 0 ||
|
||||
write_tofu(g->tofu, g->tofufd) != 0 ||
|
||||
ftruncate(g->tofufd, lseek(g->tofufd, 0, SEEK_CUR)) != 0))
|
||||
warn("Could not write: %s", g->tofufile);
|
||||
if (g->tofu != NULL) {
|
||||
free_tofu(g->tofu);
|
||||
g->tofu = NULL;
|
||||
}
|
||||
if (g->tofufd >= 0) {
|
||||
(void)close(g->tofufd);
|
||||
g->tofufd = -1;
|
||||
}
|
||||
g->tofumod = 0;
|
||||
if (g->keymem != NULL) {
|
||||
free(g->keymem);
|
||||
g->keymem = NULL;
|
||||
g->keylen = 0;
|
||||
}
|
||||
if (g->certmem != NULL) {
|
||||
free(g->certmem);
|
||||
g->certmem = NULL;
|
||||
g->certlen = 0;
|
||||
}
|
||||
gemini_reset(g);
|
||||
}
|
||||
|
||||
void
|
||||
gemini_destroy(struct gemini *g)
|
||||
{
|
||||
|
||||
gemini_destroy(g);
|
||||
free(g);
|
||||
}
|
||||
|
||||
ssize_t
|
||||
gemini_read(struct gemini *g, void *b, size_t bs)
|
||||
{
|
||||
|
@ -287,19 +273,21 @@ gemini_read(struct gemini *g, void *b, size_t bs)
|
|||
if (g->state == GEMINI_STATE_DONE)
|
||||
return (0);
|
||||
if (g->state == GEMINI_STATE_REQUEST) {
|
||||
if (tls_write(g->tls, g->request, g->reqlen)
|
||||
!= g->reqlen)
|
||||
goto errt;
|
||||
while (g->index < g->reqlen) {
|
||||
if ((r = tls_write(g->tls,
|
||||
g->request + g->index,
|
||||
g->reqlen - g->index)) <= 0)
|
||||
goto errt;
|
||||
g->index += r;
|
||||
}
|
||||
g->status = 0;
|
||||
g->index = 0;
|
||||
g->state = GEMINI_STATE_RESPONSE;
|
||||
}
|
||||
for (;;) {
|
||||
i = i != r ? i : 0;
|
||||
if (i == 0 && (r = tls_read(g->tls, b, bs)) < 0)
|
||||
if (i == 0 && (r = tls_read(g->tls, b, bs)) <= 0)
|
||||
goto errt;
|
||||
if (r == 0)
|
||||
goto eof;
|
||||
switch (g->state) {
|
||||
case GEMINI_STATE_RESPONSE:
|
||||
/* This is (probably) over-engineered. */
|
||||
|
@ -315,8 +303,8 @@ gemini_read(struct gemini *g, void *b, size_t bs)
|
|||
break;
|
||||
case GEMINI_STATE_RESPONSE_WHITESPACE:
|
||||
/* The unspecified maximum is kind-of absurd. */
|
||||
for (; i < r &&
|
||||
(cb[i] == ' ' || cb[i] == '\t'); i++)
|
||||
for (; i < r && (cb[i] == ' ' || cb[i] == '\t');
|
||||
i++)
|
||||
/* do nothing */;
|
||||
if (i == r)
|
||||
break;
|
||||
|
@ -357,49 +345,17 @@ gemini_read(struct gemini *g, void *b, size_t bs)
|
|||
case 0:
|
||||
goto errinval;
|
||||
case 1:
|
||||
r = -1 /* Do not refill the buffer */;
|
||||
if (g->flags & GEMINI_PROMPT)
|
||||
c = RPP_ECHO_ON;
|
||||
else if (g->flags & GEMINI_SECPROMPT)
|
||||
c = RPP_ECHO_OFF;
|
||||
else
|
||||
goto errunsup;
|
||||
/* Remove any control-characters */
|
||||
for (i = 0, j = 0; i < g->metalen;
|
||||
i++)
|
||||
if (!iscntrl(g->meta[i]))
|
||||
g->meta[j++] =
|
||||
g->meta[i];
|
||||
for (i = 0; i < g->reqlen &&
|
||||
g->request[i] != '?'; i++)
|
||||
/* do nothing */;
|
||||
if (i == g->reqlen)
|
||||
g->request[i -= 2] = '?';
|
||||
cb = g->request + ++i;
|
||||
j = GEMINI_URL_MAX - i;
|
||||
if (readpassphrase(g->meta, cb, j, c)
|
||||
== NULL)
|
||||
goto err;
|
||||
/*
|
||||
* readpassphrase(3) already truncates
|
||||
* the query, so there is no reason to
|
||||
* prevent the escape function from
|
||||
* doing so as well.
|
||||
*/
|
||||
(void)escapequery(cb, j, cb,
|
||||
strlen(cb));
|
||||
g->request[GEMINI_URL_MAX] = '\0';
|
||||
if (gemini_bounce(g, g->request) != 0)
|
||||
goto err;
|
||||
return (gemini_read(g, b, bs));
|
||||
goto done;
|
||||
case 2:
|
||||
g->state = GEMINI_STATE_READ;
|
||||
break;
|
||||
case 3:
|
||||
if (g->maxredirects == 0)
|
||||
goto done;
|
||||
if (g->redirects >= g->maxredirects)
|
||||
goto errredir;
|
||||
g->redirects++;
|
||||
if (gemini_bounce(g, g->meta) != 0)
|
||||
if (gemini_connect_query(g, g->meta, NULL) != 0)
|
||||
goto err;
|
||||
return (gemini_read(g, b, bs));
|
||||
case 4:
|
||||
|
@ -407,7 +363,7 @@ gemini_read(struct gemini *g, void *b, size_t bs)
|
|||
case 5:
|
||||
goto errg;
|
||||
case 6:
|
||||
goto errcert;
|
||||
goto done;
|
||||
case 7:
|
||||
goto errinval;
|
||||
case 8:
|
||||
|
@ -433,20 +389,19 @@ eof:
|
|||
warnx("Unexpected EOF");
|
||||
goto err;
|
||||
}
|
||||
done:
|
||||
g->state = GEMINI_STATE_DONE;
|
||||
return (0);
|
||||
errt:
|
||||
if (r == 0)
|
||||
goto eof;
|
||||
if (r != -1)
|
||||
return (r);
|
||||
warnx("TLS Error: %s", tls_error(g->tls));
|
||||
goto err;
|
||||
errg:
|
||||
warnx("Gemini Error %02hd: %s", g->status, g->meta);
|
||||
goto err;
|
||||
errr:
|
||||
warnx("Too many redirects");
|
||||
goto err;
|
||||
errcert:
|
||||
warnx("Certificate Error %02hd: %s", g->status, g->meta);
|
||||
goto err;
|
||||
goto done;
|
||||
errinval:
|
||||
warnx("Invalid response");
|
||||
goto err;
|
||||
|
@ -461,22 +416,44 @@ err:
|
|||
return (-1);
|
||||
}
|
||||
|
||||
int
|
||||
gemini_bounce(struct gemini *g, const char *url)
|
||||
void
|
||||
gemini_fini(struct gemini *g)
|
||||
{
|
||||
char b[GEMINI_URL_MAX + 1];
|
||||
size_t l;
|
||||
|
||||
gemini_reset(g);
|
||||
if ((l = strlen(url)) > GEMINI_URL_MAX) {
|
||||
errno = EINVAL;
|
||||
return (-1);
|
||||
if ((g->flags & GEMINI_TOFU_WRITE) && g->tofufd >= 0 &&
|
||||
g->tofumod && (lseek(g->tofufd, 0, SEEK_SET) < 0 ||
|
||||
write_tofu(g->tofu, g->tofufd) != 0 ||
|
||||
ftruncate(g->tofufd, lseek(g->tofufd, 0, SEEK_CUR)) != 0))
|
||||
warn("Could not write: %s", g->tofufile);
|
||||
if (g->tofu != NULL) {
|
||||
free_tofu(g->tofu);
|
||||
g->tofu = NULL;
|
||||
}
|
||||
(void)memcpy(b, url, l);
|
||||
b[l] = '\0';
|
||||
return (gemini_open(g, b));
|
||||
if (g->tofufd >= 0) {
|
||||
(void)close(g->tofufd);
|
||||
g->tofufd = -1;
|
||||
}
|
||||
g->tofumod = 0;
|
||||
if (g->keymem != NULL) {
|
||||
free(g->keymem);
|
||||
g->keymem = NULL;
|
||||
g->keylen = 0;
|
||||
}
|
||||
if (g->certmem != NULL) {
|
||||
free(g->certmem);
|
||||
g->certmem = NULL;
|
||||
g->certlen = 0;
|
||||
}
|
||||
gemini_reset(g);
|
||||
}
|
||||
|
||||
void
|
||||
gemini_destroy(struct gemini *g)
|
||||
{
|
||||
|
||||
gemini_fini(g);
|
||||
free(g);
|
||||
}
|
||||
|
||||
int
|
||||
set_tofu(struct gemini_tofu **tofu, const char *host, const char *hash,
|
||||
|
@ -764,20 +741,23 @@ get_hostname(char *dst, size_t dl, const char *src, size_t sl, int f)
|
|||
"~$&'()*+,-.;=_~")) == hs)
|
||||
return (0);
|
||||
/* Include the `:port', if requested. */
|
||||
if ((f & GET_HOSTNAME_PORT) && he < sl && src[he] == ':')
|
||||
for (he++; he < sl && ISASCIIDIGIT(src[he]); he++)
|
||||
v = he;
|
||||
if (v < sl && src[v] == ':')
|
||||
for (v++; v < sl && ISASCIIDIGIT(src[v]); v++)
|
||||
/* do nothing */;
|
||||
if (f & GET_HOSTNAME_PORT)
|
||||
he = v;
|
||||
if (f & GET_HOSTNAME_VALID) {
|
||||
v = he;
|
||||
if (v < sl && src[v] == '/')
|
||||
v += 1 + pctmatch(src + v + 1, sl - v - 1,
|
||||
"~$&'()*+,-./:;=@_~");
|
||||
"!$&'()*+,-./:;=@_~");
|
||||
if (v < sl && src[v] == '?')
|
||||
v += 1 + pctmatch(src + v + 1, sl - v - 1,
|
||||
"~$&'()*+,-./:;=?@_~");
|
||||
"!$&'()*+,-./:;=?@_~");
|
||||
if (v < sl && src[v] == '#')
|
||||
v += 1 + pctmatch(src + v + 1, sl - v - 1,
|
||||
"~$&'()*+,-./:;=?@_~");
|
||||
"!$&'()*+,-./:;=?@_~");
|
||||
if (v != sl)
|
||||
return (0);
|
||||
}
|
||||
|
|
|
@ -7,24 +7,12 @@
|
|||
|
||||
#include <tls.h>
|
||||
|
||||
#ifndef GEMINI_HOSTNAME_MAX
|
||||
#define GEMINI_HOSTNAME_MAX 1024
|
||||
#endif
|
||||
#ifndef GEMINI_URL_MAX
|
||||
#define GEMINI_URL_MAX 1024
|
||||
#endif
|
||||
#ifndef GEMINI_META_MAX
|
||||
#define GEMINI_META_MAX 1024
|
||||
#endif
|
||||
#ifndef GEMINI_REDIRECT_MAX
|
||||
#define GEMINI_REDIRECT_MAX 5
|
||||
#endif
|
||||
|
||||
enum {
|
||||
GEMINI_PROMPT = 0x01,
|
||||
GEMINI_SECPROMPT = 0x02,
|
||||
GEMINI_TOFU_WRITE = 0x08
|
||||
};
|
||||
#define GEMINI_TOFU_WRITE 0x01
|
||||
|
||||
struct gemini_tofu {
|
||||
struct gemini_tofu *next;
|
||||
|
@ -35,7 +23,7 @@ struct gemini_tofu {
|
|||
|
||||
#define GEMINI_INITIALIZER \
|
||||
{ { 0 }, { 0 }, NULL, NULL, NULL, NULL, NULL, NULL, \
|
||||
NULL, NULL, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, \
|
||||
NULL, NULL, 0, 0, 0, 0, 0, -1, -1, 0, 0, 0, 0, \
|
||||
GEMINI_REDIRECT_MAX, 0, 0 }
|
||||
struct gemini {
|
||||
char meta[GEMINI_META_MAX + 1];
|
||||
|
@ -53,6 +41,7 @@ struct gemini {
|
|||
size_t keylen;
|
||||
size_t certlen;
|
||||
size_t index;
|
||||
int socketfd;
|
||||
int tofufd;
|
||||
int tofumod;
|
||||
int flags;
|
||||
|
@ -63,12 +52,14 @@ struct gemini {
|
|||
short port;
|
||||
};
|
||||
|
||||
struct gemini *gemini_create(void);
|
||||
void gemini_init(struct gemini *);
|
||||
void gemini_reset(struct gemini *);
|
||||
struct gemini *gemini_create(void);
|
||||
int gemini_open(struct gemini *, const char *);
|
||||
void gemini_close(struct gemini *);
|
||||
void gemini_destroy(struct gemini *);
|
||||
int gemini_connect_query(struct gemini *, const char *,
|
||||
const char *);
|
||||
int gemini_connect(struct gemini *, const char *);
|
||||
ssize_t gemini_read(struct gemini *, void *, size_t);
|
||||
void gemini_fini(struct gemini *);
|
||||
void gemini_destroy(struct gemini *);
|
||||
|
||||
#endif
|
||||
|
|
Loading…
Reference in New Issue