diff --git a/include/tofu.h b/include/tofu.h
index 29aa9bc21567868cafb25a09dbc25ea0685ab01c..a88167ba0fb6606b2b170e5005c55131f1861972 100644
--- a/include/tofu.h
+++ b/include/tofu.h
@@ -44,5 +44,6 @@ };
void gemini_tofu_init(struct gemini_tofu *tofu,
SSL_CTX *ssl_ctx, tofu_callback_t *cb, void *data);
+void gemini_tofu_finish(struct gemini_tofu *tofu);
#endif
diff --git a/src/client.c b/src/client.c
index 2d39f56cb8e45f7b708e5a5dea1ec6fa84e188c9..bb508e04c8d41fe12aa99ae42b8fa30d4f3a9a2e 100644
--- a/src/client.c
+++ b/src/client.c
@@ -118,11 +118,14 @@ goto cleanup;
} else {
if (strcmp(scheme, "gemini") != 0) {
res = GEMINI_ERR_NOT_GEMINI;
+ free(scheme);
goto cleanup;
}
+ free(scheme);
}
if (curl_url_get(uri, CURLUPART_HOST, &host, 0) != CURLUE_OK) {
res = GEMINI_ERR_INVALID_URL;
+ free(host);
goto cleanup;
}
@@ -139,6 +142,7 @@ int r;
BIO *sbio = BIO_new(BIO_f_ssl());
res = gemini_connect(uri, options, resp, &resp->fd);
if (res != GEMINI_OK) {
+ free(host);
goto cleanup;
}
@@ -146,11 +150,14 @@ resp->ssl = SSL_new(resp->ssl_ctx);
assert(resp->ssl);
SSL_set_connect_state(resp->ssl);
if ((r = SSL_set1_host(resp->ssl, host)) != 1) {
+ free(host);
goto ssl_error;
}
if ((r = SSL_set_tlsext_host_name(resp->ssl, host)) != 1) {
+ free(host);
goto ssl_error;
}
+ free(host);
if ((r = SSL_set_fd(resp->ssl, resp->fd)) != 1) {
goto ssl_error;
}
@@ -235,15 +242,16 @@ resp->fd = -1;
}
if (resp->bio) {
- BIO_free(BIO_pop(resp->bio)); // ssl bio
- BIO_free(resp->bio); // buffered bio
+ BIO_free_all(resp->bio);
resp->bio = NULL;
}
if (resp->ssl) {
SSL_free(resp->ssl);
}
- SSL_CTX_free(resp->ssl_ctx);
+ if (resp->ssl_ctx) {
+ SSL_CTX_free(resp->ssl_ctx);
+ }
free(resp->meta);
resp->ssl = NULL;
diff --git a/src/gmni.c b/src/gmni.c
index c13e0cd55623f557a8dc676d69aea54661b11faf..4af98fbbe70b09ce5a7fce46863438b0487d8738 100644
--- a/src/gmni.c
+++ b/src/gmni.c
@@ -336,6 +336,8 @@ next:
gemini_response_finish(&resp);
}
+ SSL_CTX_free(opts.ssl_ctx);
free(url);
+ gemini_tofu_finish(&cfg.tofu);
return ret;
}
diff --git a/src/gmnlm.c b/src/gmnlm.c
index 5d45ffe7372f71ab32461bf71a590cc494269e96..a2717fb84c76fd50a000ceda540af54c60396252 100644
--- a/src/gmnlm.c
+++ b/src/gmnlm.c
@@ -83,6 +83,7 @@ if (!history) {
return;
}
history_free(history->next);
+ free(history->url);
free(history);
}
@@ -92,6 +93,9 @@ {
if (curl_url_set(browser->url, CURLUPART_URL, new_url, 0) != CURLUE_OK) {
fprintf(stderr, "Error: invalid URL\n");
return false;
+ }
+ if (browser->plain_url != NULL) {
+ free(browser->plain_url);
}
curl_url_get(browser->url, CURLUPART_URL, &browser->plain_url, 0);
if (history) {
@@ -130,17 +134,19 @@
static void
save_bookmark(struct browser *browser)
{
- const char *path_fmt = get_data_pathfmt();
+ char *path_fmt = get_data_pathfmt();
static char path[PATH_MAX+1];
snprintf(path, sizeof(path), path_fmt, "bookmarks.gmi");
if (mkdirs(dirname(path), 0755) != 0) {
snprintf(path, sizeof(path), path_fmt, "bookmarks.gmi");
+ free(path_fmt);
fprintf(stderr, "Error creating directory %s: %s\n",
dirname(path), strerror(errno));
return;
}
snprintf(path, sizeof(path), path_fmt, "bookmarks.gmi");
+ free(path_fmt);
FILE *f = fopen(path, "a");
if (!f) {
fprintf(stderr, "Error opening %s for writing: %s\n",
@@ -150,7 +156,7 @@ }
char *title = browser->page_title;
if (title) {
- title = trim_ws(browser->page_title);
+ title = trim_ws(strdup(browser->page_title));
}
fprintf(f, "=> %s%s%s\n", browser->plain_url,
@@ -159,6 +165,9 @@ fclose(f);
fprintf(browser->tty, "Bookmark saved: %s\n",
title ? title : browser->plain_url);
+ if (title != NULL) {
+ free(title);
+ }
}
static void
@@ -411,12 +420,14 @@ col += fprintf(out, " ");
}
break;
case GEMINI_PREFORMATTED_BEGIN:
+ gemini_token_finish(&tok);
+ /* fallthrough */
case GEMINI_PREFORMATTED_END:
continue; // Not used
case GEMINI_PREFORMATTED_TEXT:
col += fprintf(out, "` ");
if (text == NULL) {
- text = tok.text;
+ text = tok.preformatted;
}
break;
case GEMINI_HEADING:
@@ -484,6 +495,9 @@ if (!text[0]) {
text = NULL;
}
}
+ if (text == NULL) {
+ gemini_token_finish(&tok);
+ }
while (col >= ws.ws_col) {
col -= ws.ws_col;
@@ -510,8 +524,16 @@ case PROMPT_MORE:
break;
case PROMPT_QUIT:
browser->running = false;
+ if (text != NULL) {
+ gemini_token_finish(&tok);
+ }
+ gemini_parser_finish(&p);
return true;
case PROMPT_ANSWERED:
+ if (text != NULL) {
+ gemini_token_finish(&tok);
+ }
+ gemini_parser_finish(&p);
return true;
case PROMPT_NEXT:
searching = true;
@@ -523,6 +545,7 @@ row = col = 0;
}
}
+ gemini_token_finish(&tok);
gemini_parser_finish(&p);
return false;
}
@@ -617,6 +640,7 @@ CURLUcode uc = curl_url_get(browser->url,
CURLUPART_SCHEME, &scheme, 0);
assert(uc == CURLUE_OK); // Invariant
if (strcmp(scheme, "file") == 0) {
+ free(scheme);
requesting = false;
char *path;
@@ -630,6 +654,7 @@
FILE *fp = fopen(path, "r");
if (!fp) {
resp->status = GEMINI_STATUS_NOT_FOUND;
+ free(path);
break;
}
@@ -643,9 +668,14 @@ resp->meta = strdup("text/plain");
} else {
resp->meta = strdup("application/x-octet-stream");
}
+ free(path);
resp->status = GEMINI_STATUS_SUCCESS;
+ resp->fd = -1;
+ resp->ssl = NULL;
+ resp->ssl_ctx = NULL;
return display_response(browser, resp);
}
+ free(scheme);
enum gemini_result res = gemini_request(browser->plain_url,
&browser->opts, resp);
@@ -672,7 +702,7 @@ break;
case GEMINI_STATUS_CLASS_REDIRECT:
if (++nredir >= 5) {
requesting = false;
- fprintf(stderr, "Error: maximum redirects (5) exceeded");
+ fprintf(stderr, "Error: maximum redirects (5) exceeded\n");
break;
}
fprintf(stderr, "Following redirect to %s\n", resp->meta);
@@ -816,6 +846,7 @@ browser.unicode = false;
break;
default:
fprintf(stderr, "fatal: unknown flag %c\n", c);
+ curl_url_cleanup(browser.url);
return 1;
}
}
@@ -841,6 +872,7 @@ while (browser.running) {
static char prompt[4096];
if (do_requests(&browser, &resp)) {
// Skip prompts
+ gemini_response_finish(&resp);
goto next;
}
@@ -880,8 +912,16 @@ }
browser.links = NULL;
}
- history_free(browser.history);
+ gemini_tofu_finish(&browser.tofu);
+ struct history *hist = browser.history;
+ while (hist && hist->prev) {
+ hist = hist->prev;
+ }
+ history_free(hist);
SSL_CTX_free(browser.opts.ssl_ctx);
curl_url_cleanup(browser.url);
+ free(browser.page_title);
+ free(browser.plain_url);
+ fclose(browser.tty);
return 0;
}
diff --git a/src/parser.c b/src/parser.c
index 5b0f01399d934f593cdf987e096dd2cfb134d114..2f78e4641c08b86f5dc50cc9776ea80ec7f9aead 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -35,15 +35,14 @@ {
memset(tok, 0, sizeof(*tok));
int eof = 0;
- while (!strstr(p->buf, "\n")) {
- if (p->bufln == p->bufsz) {
+ while (!strchr(p->buf, '\n')) {
+ while (p->bufln >= p->bufsz - 1) {
p->bufsz *= 2;
- char *buf = realloc(p->buf, p->bufsz);
- assert(buf);
- p->buf = buf;
+ p->buf = realloc(p->buf, p->bufsz);
+ assert(p->buf);
}
- int n = BIO_read(p->f, &p->buf[p->bufln], p->bufsz - p->bufln);
+ ssize_t n = BIO_read(p->f, &p->buf[p->bufln], p->bufsz - p->bufln - 1);
if (n == -1) {
return -1;
} else if (n == 0) {
@@ -55,7 +54,7 @@ p->buf[p->bufln] = 0;
}
char *end;
- if ((end = strstr(p->buf, "\n")) != NULL) {
+ if ((end = strchr(p->buf, '\n')) != NULL) {
*end = 0;
}
diff --git a/src/tofu.c b/src/tofu.c
index 354e211c856451bdea120bbb1aed5917099eb285..45e1275d568cfcb4aacfe7f86788b76fff97916b 100644
--- a/src/tofu.c
+++ b/src/tofu.c
@@ -150,7 +150,7 @@ {.var = "GMNIDATA", .path = "/%s"},
{.var = "XDG_DATA_HOME", .path = "/gmni/%s"},
{.var = "HOME", .path = "/.local/share/gmni/%s"}
};
- const char *path_fmt = getpath(paths, sizeof(paths) / sizeof(paths[0]));
+ char *path_fmt = getpath(paths, sizeof(paths) / sizeof(paths[0]));
snprintf(tofu->known_hosts_path, sizeof(tofu->known_hosts_path),
path_fmt, "known_hosts");
@@ -164,6 +164,7 @@ }
snprintf(tofu->known_hosts_path, sizeof(tofu->known_hosts_path),
path_fmt, "known_hosts");
+ free(path_fmt);
tofu->callback = cb;
tofu->cb_data = cb_data;
@@ -175,6 +176,7 @@ return;
}
size_t n = 0;
char *line = NULL;
+ tofu->known_hosts = NULL;
while (getline(&line, &n, f) != -1) {
struct known_host *host = calloc(1, sizeof(struct known_host));
char *tok = strtok(line, " ");
@@ -184,6 +186,7 @@
tok = strtok(NULL, " ");
assert(tok);
if (strcmp(tok, "SHA-512") != 0) {
+ free(host->host);
free(host);
continue;
}
@@ -198,5 +201,20 @@ host->expires = strtoul(tok, NULL, 10);
host->next = tofu->known_hosts;
tofu->known_hosts = host;
+ }
+ free(line);
+ fclose(f);
+}
+
+void
+gemini_tofu_finish(struct gemini_tofu *tofu)
+{
+ struct known_host *host = tofu->known_hosts;
+ while (host) {
+ struct known_host *tmp = host;
+ host = host->next;
+ free(tmp->host);
+ free(tmp->fingerprint);
+ free(tmp);
}
}