182 lines
3.8 KiB
C
182 lines
3.8 KiB
C
#include <ctype.h>
|
|
#include <limits.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <time.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
#include <err.h>
|
|
#include <sysexits.h>
|
|
|
|
#ifdef LIBBSD
|
|
#include <bsd/readpassphrase.h>
|
|
#else
|
|
#include <readpassphrase.h>
|
|
#endif
|
|
|
|
#include <tls.h>
|
|
|
|
#include <libgeminiclient.h>
|
|
|
|
#ifndef BUFFER_SIZE
|
|
#define BUFFER_SIZE 4096
|
|
#endif
|
|
|
|
static ssize_t stripcntrl(char *, size_t, const char *, size_t);
|
|
|
|
int
|
|
main(int argc, char *argv[])
|
|
{
|
|
char b[BUFFER_SIZE];
|
|
struct gemini gemini = GEMINI_INITIALIZER;
|
|
const char *query = NULL;
|
|
char *p;
|
|
ssize_t r;
|
|
size_t i;
|
|
int prompt = 0;
|
|
int rawout;
|
|
int c;
|
|
|
|
gemini_envinit(&gemini);
|
|
if ((p = getenv("GEMINI_TOFU_WRITE")) != NULL && *p != '\0')
|
|
gemini.flags |= GEMINI_TOFU_WRITE;
|
|
rawout = !isatty(STDOUT_FILENO);
|
|
#ifdef __OpenBSD__
|
|
if (pledge("stdio cpath rpath wpath flock inet dns tty", NULL)
|
|
!= 0)
|
|
err(EX_NOPERM, "Failed pledge(2)");
|
|
#endif
|
|
while ((c = getopt(argc, argv, "RSc:h:ik:p:r:st:w")) != -1)
|
|
switch (c) {
|
|
case 'R':
|
|
rawout = 1;
|
|
break;
|
|
case 'S':
|
|
rawout = 0;
|
|
break;
|
|
case 'c':
|
|
gemini.certfile = optarg;
|
|
break;
|
|
case 'h':
|
|
gemini.proxy = optarg;
|
|
break;
|
|
case 'i':
|
|
prompt = 1;
|
|
break;
|
|
case 'k':
|
|
gemini.keyfile = optarg;
|
|
break;
|
|
case 'p':
|
|
for (i = 0, gemini.port = 0;
|
|
optarg[i] >= '0' && optarg[i] <= '9'; i++)
|
|
gemini.port = gemini.port * 10 +
|
|
optarg[i] - '0';
|
|
break;
|
|
case 'r':
|
|
for (i = 0, gemini.maxredirects = 0;
|
|
gemini.maxredirects <= (INT_MAX - 9) / 10 &&
|
|
optarg[i] >= '0' && optarg[i] <= '9';
|
|
gemini.maxredirects = gemini.maxredirects *
|
|
10 + (optarg[i] - '0'), i++)
|
|
/* do nothing */;
|
|
if (i == 0 || optarg[i] != '\0')
|
|
warnx("Invalid number: %s", optarg);
|
|
break;
|
|
case 's':
|
|
prompt = 2;
|
|
break;
|
|
case 't':
|
|
gemini.tofufile = optarg;
|
|
break;
|
|
case 'w':
|
|
gemini.flags |= GEMINI_TOFU_WRITE;
|
|
break;
|
|
default:
|
|
goto usage;
|
|
}
|
|
argc -= optind;
|
|
argv += optind;
|
|
#ifdef __OpenBSD__
|
|
if (!(gemini.flags & GEMINI_TOFU_WRITE) &&
|
|
pledge("stdio rpath flock inet dns tty", NULL) != 0)
|
|
err(EX_NOPERM, "Failed pledge(2)");
|
|
#endif
|
|
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)
|
|
err(EX_NOPERM, "Failed pledge(2)");
|
|
#endif
|
|
while ((r = gemini_read(&gemini, b, sizeof(b))) > 0) {
|
|
if (!rawout)
|
|
r = stripcntrl(b, sizeof(b), b, r);
|
|
(void)fwrite(b, 1, r, stdout);
|
|
}
|
|
if (r < 0) {
|
|
warn("Gemini read error");
|
|
goto err;
|
|
}
|
|
query = NULL;
|
|
switch (gemini.status / 10) {
|
|
case 1:
|
|
if (prompt == 0) {
|
|
warnx("User-input is not enabled");
|
|
break;
|
|
}
|
|
if (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 4:
|
|
break;
|
|
case 5:
|
|
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_fini(&gemini);
|
|
return (0);
|
|
err:
|
|
gemini_fini(&gemini);
|
|
return (EX_SOFTWARE);
|
|
usage:
|
|
(void)fprintf(stderr, "usage: gemini-cat [-RSisw] "
|
|
"[-c cert-file] [-h proxy-host] [-k key-file]\n"
|
|
" [-p port] [-r max-redirects] "
|
|
"[-t tofu-file] URL...\n");
|
|
return (EX_USAGE);
|
|
}
|
|
|
|
ssize_t
|
|
stripcntrl(char *dst, size_t dl, const char *src, size_t sl)
|
|
{
|
|
size_t i;
|
|
size_t j;
|
|
|
|
for (i = 0, j = 0; i < sl && j < dl; i++)
|
|
if (!iscntrl(src[i]) || isspace(src[i]))
|
|
dst[j++] = src[i];
|
|
return (j);
|
|
}
|