Skip to content

Commit

Permalink
add support for permanent redirects
Browse files Browse the repository at this point in the history
  • Loading branch information
dimkr authored Apr 21, 2023
1 parent 89e4568 commit e147e94
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 47 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ gplaces is originally a Gemini port of the [delve](https://github.com/kieselstei
- variables
- ~command aliases~ use variables
- sh-style history with $XDG_DATA_HOME/gplaces_history or ~/.gplaces_history
- permanent redirects with $XDG_DATA_HOME/gplaces_redirects or ~/.gplaces_redirects
- VT100 compatible with ANSI escape sequences
- with [NO_COLOR](https://no-color.org/) support
- no exotic external dependencies, no NIH
Expand Down Expand Up @@ -115,4 +116,4 @@ additional documentation and more details are available in `man gplaces`. type `
## Statistic
Language|files|blank|comment|code
:-------|-------:|-------:|-------:|-------:
C|1|256|60|1153
C|1|267|61|1193
144 changes: 98 additions & 46 deletions gplaces.c
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,39 @@ static int get_var_integer(const char *name, int def) {
}


/*============================================================================*/
static void *map_file(const char *name, int *fd, size_t *size) {
static char path[1024];
struct stat stbuf;
const char *home;
void *p;

if ((home = getenv("XDG_DATA_HOME")) != NULL) snprintf(path, sizeof(path), "%s/gplaces_%s", home, name);
else if ((home = getenv("HOME")) != NULL) snprintf(path, sizeof(path), "%s/.gplaces_%s", home, name);
else return NULL;

if ((*fd = open(path, O_RDWR | O_CREAT | O_APPEND, 0600)) == -1) return NULL;
if (fstat(*fd, &stbuf) == -1) { close(*fd); return NULL; }
if ((*size = (size_t)stbuf.st_size) == 0) return NULL;
if ((p = mmap(NULL, stbuf.st_size % SIZE_MAX, PROT_READ | PROT_WRITE, MAP_SHARED, *fd, 0)) == MAP_FAILED) { close(*fd); return NULL; }
return p;
}


__attribute__((format(printf, 2, 3)))
static int append_line(int fd, const char *fmt, ...) {
va_list va;
FILE *fp;
int ret;
if ((fp = fdopen(fd, "a")) == NULL) return 0;
va_start(va, fmt);
ret = vfprintf(fp, fmt, va) > 0;
va_end(va);
fclose(fp);
return ret;
}


/*============================================================================*/
static Selector *new_selector(const char type) {
Selector *new = calloc(1, sizeof(Selector));
Expand Down Expand Up @@ -253,6 +286,42 @@ static int parse_url(Selector *sel, const char *from, const char *input) {
}


static int redirect(Selector *sel, const char *to, size_t len) {
char *from = sel->url; sel->url = NULL;
curl_free(sel->scheme); sel->scheme = NULL;
curl_free(sel->host); sel->host = NULL;
if (sel->port != defport) { curl_free(sel->port); }; sel->port = NULL;
curl_free(sel->path); sel->path = NULL;
curl_url_cleanup(sel->cu); sel->cu = NULL;
free(sel->rawurl); if ((sel->rawurl = len > 0 ? strndup(to, len) : strdup(to)) == NULL) error(1, "cannot allocate new string");
if (!parse_url(sel, from, NULL)) { curl_free(from); return 0; }
curl_free(from);
fprintf(stderr, "redirected to `%s`\n", sel->url);
return 1;
}


static int perm_redirect(Selector *sel, const char *to) {
size_t size = 1, len;
const char *p, *start, *end;
int fd, ret = 20, found = 0;

len = strlen(sel->url);

if ((p = map_file("redirs", &fd, &size)) == NULL && size > 0) return 40;
else if (p != NULL) {
for (end = p; !found && (start = memmem(end, size - (end - p), sel->url, len)) != NULL; end = start + len + 1) {
if (!(found = ((start == p || *(start - 1) == '\n') && size - (start - p) >= len + 2 && start[len] == ' ' && start[len + 1] != '\n'))) continue;
ret = redirect(sel, &start[len + 1], strcspn(&start[len + 1], " \n")) ? 31 : 40;
}
munmap((void *)p, size);
}
if (to != NULL && !found) ret = (append_line(fd, "%s %s\n", sel->url, to) && redirect(sel, to, -1)) ? 31 : 40;
close(fd);
return ret;
}


static Selector *find_selector(SelectorList *list, int index) {
Selector *sel;
long i = 0;
Expand Down Expand Up @@ -567,15 +636,13 @@ static void print_gemtext(FILE *fp, SelectorList *list, const char *filter) {

/*============================================================================*/
static int tofu(X509 *cert, const char *host, int ask) {
static char hosts[1024], buffer[1024], hex[EVP_MAX_MD_SIZE * 2 + 1];
static char buffer[1024], hex[EVP_MAX_MD_SIZE * 2 + 1];
static unsigned char md[EVP_MAX_MD_SIZE];
struct stat stbuf;
size_t hlen;
FILE *fp = NULL;
const char *home, *end, *p = MAP_FAILED;
char *start, *line;
size_t size = 1, hlen;
const char *p, *end;
char *line, *start;
unsigned int mdlen, i;
int fd, found = 0, trust = 0;
int fd = -1, found = 0, trust = 0;

if (X509_digest(cert, EVP_sha512(), md, &mdlen) == 0) return 0;

Expand All @@ -587,36 +654,26 @@ static int tofu(X509 *cert, const char *host, int ask) {

hlen = strlen(host);

if ((home = getenv("XDG_DATA_HOME")) != NULL) snprintf(hosts, sizeof(hosts), "%s/gplaces_hosts", home);
else if ((home = getenv("HOME")) != NULL) snprintf(hosts, sizeof(hosts), "%s/.gplaces_hosts", home);
else return 0;

if ((fd = open(hosts, O_RDWR | O_CREAT | O_APPEND, 0600)) != -1) {
if (fstat(fd, &stbuf) == -1) { close(fd); return 0; }
if (stbuf.st_size > 0) {
if ((p = mmap(NULL, stbuf.st_size % SIZE_MAX, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) { close(fd); return 0; }
for (end = (const char *)p; !found && (start = memmem(end, stbuf.st_size - (end - p), host, hlen)) != NULL; end = start + hlen + 1) {
if (!(found = ((start == p || *(start - 1) == '\n') && (size_t)stbuf.st_size - (start - p) >= hlen + 2 && start[hlen] == ' ' && start[hlen + 1] != '\n'))) continue;
if ((size_t)stbuf.st_size - (start - p) < hlen + 1 + mdlen * 2 + 1 || start[hlen + 1 + mdlen * 2] != '\n') break;
if ((trust = memcmp(&start[hlen + 1], hex, mdlen * 2) == 0) || !ask) break;
if (color) snprintf(buffer, sizeof(buffer), "\33[35mTrust new certificate for `%s`? (y/n)>\33[0m ", host);
else snprintf(buffer, sizeof(buffer), "Trust new certificate for `%s`? (y/n)> ", host);
if ((line = bestline(buffer)) != NULL) {
if (*line == 'y' || *line == 'Y') {
memcpy(&start[hlen + 1], hex, mdlen * 2);
trust = 1;
}
free(line);
if ((p = map_file("hosts", &fd, &size)) == NULL && size > 0) return 0;
else if (p != NULL) {
for (end = p; !found && (start = memmem(end, size - (end - p), host, hlen)) != NULL; end = start + hlen + 1) {
if (!(found = ((start == p || *(start - 1) == '\n') && size - (start - p) >= hlen + 2 && start[hlen] == ' ' && start[hlen + 1] != '\n'))) continue;
if (size - (start - p) < hlen + 1 + mdlen * 2 + 1 || start[hlen + 1 + mdlen * 2] != '\n') break;
if ((trust = memcmp(&start[hlen + 1], hex, mdlen * 2) == 0) || !ask) break;
if (color) snprintf(buffer, sizeof(buffer), "\33[35mTrust new certificate for `%s`? (y/n)>\33[0m ", host);
else snprintf(buffer, sizeof(buffer), "Trust new certificate for `%s`? (y/n)> ", host);
if ((line = bestline(buffer)) != NULL) {
if (*line == 'y' || *line == 'Y') {
memcpy(&start[hlen + 1], hex, mdlen * 2);
trust = 1;
}
free(line);
}
munmap((void *)p, stbuf.st_size);
munmap((void *)p, size);
}
if (!found && (fp = fdopen(fd, "w")) != NULL) {
trust = fprintf(fp, "%s %s\n", host, hex) > 0;
fclose(fp);
} else close(fd);
} else return 0;

}
if (!found) trust = append_line(fd, "%s %s\n", host, hex);
close(fd);
return trust;
}

Expand Down Expand Up @@ -727,10 +784,13 @@ static int do_download(Selector *sel, SSL **body, char **mime, int ask) {
struct stat stbuf;
const char *home;
SSL_CTX *ctx = NULL;
char *crlf, *meta = &data[3], *line, *url, *from;
int off, len, i, total, received, ret = 40, err = 0;
char *crlf, *meta = &data[3], *line, *url;
int redir, off, len, i, total, received, ret = 40, err = 0;
SSL *ssl = NULL;

if ((redir = perm_redirect(sel, NULL)) == 31) return 31;
else if (redir != 20) goto fail;

if ((home = getenv("XDG_DATA_HOME")) != NULL) {
if ((off = snprintf(crtpath, sizeof(crtpath), "%s/gplaces_%s", home, sel->host)) >= (int)sizeof(crtpath)) goto fail;;
} else if ((home = getenv("HOME")) == NULL || (off = snprintf(crtpath, sizeof(crtpath), "%s/.gplaces_%s", home, sel->host)) >= (int)sizeof(crtpath)) goto fail;
Expand Down Expand Up @@ -814,16 +874,8 @@ static int do_download(Selector *sel, SSL **body, char **mime, int ask) {

case '3':
if (!*meta) goto fail;
curl_free(sel->scheme); sel->scheme = NULL;
curl_free(sel->host); sel->host = NULL;
if (sel->port != defport) { curl_free(sel->port); }; sel->port = NULL;
curl_free(sel->path); sel->path = NULL;
curl_url_cleanup(sel->cu); sel->cu = NULL;
from = sel->url;
free(sel->rawurl); sel->rawurl = str_copy(meta);
if (!parse_url(sel, from, NULL)) { curl_free(from); goto fail; }
curl_free(from);
fprintf(stderr, "redirected to `%s`\n", sel->url);
if (data[1] == '1' && !perm_redirect(sel, meta)) goto fail;
else if (data[1] != '1' && !redirect(sel, meta, total - 2)) goto fail;
break;

case '6':
Expand Down

0 comments on commit e147e94

Please sign in to comment.