1
0
mirror of https://git.sr.ht/~sircmpwn/gmni synced 2024-11-23 04:51:59 +01:00

Fix memory leaks

This commit is contained in:
Eyal Sawady 2020-09-23 12:50:25 -04:00 committed by Drew DeVault
parent 0976b0e446
commit 174fbd5d09
6 changed files with 84 additions and 16 deletions

@ -44,5 +44,6 @@ struct gemini_tofu {
void gemini_tofu_init(struct gemini_tofu *tofu, void gemini_tofu_init(struct gemini_tofu *tofu,
SSL_CTX *ssl_ctx, tofu_callback_t *cb, void *data); SSL_CTX *ssl_ctx, tofu_callback_t *cb, void *data);
void gemini_tofu_finish(struct gemini_tofu *tofu);
#endif #endif

@ -118,11 +118,14 @@ gemini_request(const char *url, struct gemini_options *options,
} else { } else {
if (strcmp(scheme, "gemini") != 0) { if (strcmp(scheme, "gemini") != 0) {
res = GEMINI_ERR_NOT_GEMINI; res = GEMINI_ERR_NOT_GEMINI;
free(scheme);
goto cleanup; goto cleanup;
} }
free(scheme);
} }
if (curl_url_get(uri, CURLUPART_HOST, &host, 0) != CURLUE_OK) { if (curl_url_get(uri, CURLUPART_HOST, &host, 0) != CURLUE_OK) {
res = GEMINI_ERR_INVALID_URL; res = GEMINI_ERR_INVALID_URL;
free(host);
goto cleanup; goto cleanup;
} }
@ -139,6 +142,7 @@ gemini_request(const char *url, struct gemini_options *options,
BIO *sbio = BIO_new(BIO_f_ssl()); BIO *sbio = BIO_new(BIO_f_ssl());
res = gemini_connect(uri, options, resp, &resp->fd); res = gemini_connect(uri, options, resp, &resp->fd);
if (res != GEMINI_OK) { if (res != GEMINI_OK) {
free(host);
goto cleanup; goto cleanup;
} }
@ -146,11 +150,14 @@ gemini_request(const char *url, struct gemini_options *options,
assert(resp->ssl); assert(resp->ssl);
SSL_set_connect_state(resp->ssl); SSL_set_connect_state(resp->ssl);
if ((r = SSL_set1_host(resp->ssl, host)) != 1) { if ((r = SSL_set1_host(resp->ssl, host)) != 1) {
free(host);
goto ssl_error; goto ssl_error;
} }
if ((r = SSL_set_tlsext_host_name(resp->ssl, host)) != 1) { if ((r = SSL_set_tlsext_host_name(resp->ssl, host)) != 1) {
free(host);
goto ssl_error; goto ssl_error;
} }
free(host);
if ((r = SSL_set_fd(resp->ssl, resp->fd)) != 1) { if ((r = SSL_set_fd(resp->ssl, resp->fd)) != 1) {
goto ssl_error; goto ssl_error;
} }
@ -235,15 +242,16 @@ gemini_response_finish(struct gemini_response *resp)
} }
if (resp->bio) { if (resp->bio) {
BIO_free(BIO_pop(resp->bio)); // ssl bio BIO_free_all(resp->bio);
BIO_free(resp->bio); // buffered bio
resp->bio = NULL; resp->bio = NULL;
} }
if (resp->ssl) { if (resp->ssl) {
SSL_free(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); free(resp->meta);
resp->ssl = NULL; resp->ssl = NULL;

@ -336,6 +336,8 @@ next:
gemini_response_finish(&resp); gemini_response_finish(&resp);
} }
SSL_CTX_free(opts.ssl_ctx);
free(url); free(url);
gemini_tofu_finish(&cfg.tofu);
return ret; return ret;
} }

@ -83,6 +83,7 @@ history_free(struct history *history)
return; return;
} }
history_free(history->next); history_free(history->next);
free(history->url);
free(history); free(history);
} }
@ -93,6 +94,9 @@ set_url(struct browser *browser, char *new_url, struct history **history)
fprintf(stderr, "Error: invalid URL\n"); fprintf(stderr, "Error: invalid URL\n");
return false; return false;
} }
if (browser->plain_url != NULL) {
free(browser->plain_url);
}
curl_url_get(browser->url, CURLUPART_URL, &browser->plain_url, 0); curl_url_get(browser->url, CURLUPART_URL, &browser->plain_url, 0);
if (history) { if (history) {
struct history *next = calloc(1, sizeof(struct history)); struct history *next = calloc(1, sizeof(struct history));
@ -130,17 +134,19 @@ trim_ws(char *in)
static void static void
save_bookmark(struct browser *browser) save_bookmark(struct browser *browser)
{ {
const char *path_fmt = get_data_pathfmt(); char *path_fmt = get_data_pathfmt();
static char path[PATH_MAX+1]; static char path[PATH_MAX+1];
snprintf(path, sizeof(path), path_fmt, "bookmarks.gmi"); snprintf(path, sizeof(path), path_fmt, "bookmarks.gmi");
if (mkdirs(dirname(path), 0755) != 0) { if (mkdirs(dirname(path), 0755) != 0) {
snprintf(path, sizeof(path), path_fmt, "bookmarks.gmi"); snprintf(path, sizeof(path), path_fmt, "bookmarks.gmi");
free(path_fmt);
fprintf(stderr, "Error creating directory %s: %s\n", fprintf(stderr, "Error creating directory %s: %s\n",
dirname(path), strerror(errno)); dirname(path), strerror(errno));
return; return;
} }
snprintf(path, sizeof(path), path_fmt, "bookmarks.gmi"); snprintf(path, sizeof(path), path_fmt, "bookmarks.gmi");
free(path_fmt);
FILE *f = fopen(path, "a"); FILE *f = fopen(path, "a");
if (!f) { if (!f) {
fprintf(stderr, "Error opening %s for writing: %s\n", fprintf(stderr, "Error opening %s for writing: %s\n",
@ -150,7 +156,7 @@ save_bookmark(struct browser *browser)
char *title = browser->page_title; char *title = browser->page_title;
if (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, fprintf(f, "=> %s%s%s\n", browser->plain_url,
@ -159,6 +165,9 @@ save_bookmark(struct browser *browser)
fprintf(browser->tty, "Bookmark saved: %s\n", fprintf(browser->tty, "Bookmark saved: %s\n",
title ? title : browser->plain_url); title ? title : browser->plain_url);
if (title != NULL) {
free(title);
}
} }
static void static void
@ -411,12 +420,14 @@ repeat:
} }
break; break;
case GEMINI_PREFORMATTED_BEGIN: case GEMINI_PREFORMATTED_BEGIN:
gemini_token_finish(&tok);
/* fallthrough */
case GEMINI_PREFORMATTED_END: case GEMINI_PREFORMATTED_END:
continue; // Not used continue; // Not used
case GEMINI_PREFORMATTED_TEXT: case GEMINI_PREFORMATTED_TEXT:
col += fprintf(out, "` "); col += fprintf(out, "` ");
if (text == NULL) { if (text == NULL) {
text = tok.text; text = tok.preformatted;
} }
break; break;
case GEMINI_HEADING: case GEMINI_HEADING:
@ -484,6 +495,9 @@ repeat:
text = NULL; text = NULL;
} }
} }
if (text == NULL) {
gemini_token_finish(&tok);
}
while (col >= ws.ws_col) { while (col >= ws.ws_col) {
col -= ws.ws_col; col -= ws.ws_col;
@ -510,8 +524,16 @@ repeat:
break; break;
case PROMPT_QUIT: case PROMPT_QUIT:
browser->running = false; browser->running = false;
if (text != NULL) {
gemini_token_finish(&tok);
}
gemini_parser_finish(&p);
return true; return true;
case PROMPT_ANSWERED: case PROMPT_ANSWERED:
if (text != NULL) {
gemini_token_finish(&tok);
}
gemini_parser_finish(&p);
return true; return true;
case PROMPT_NEXT: case PROMPT_NEXT:
searching = true; searching = true;
@ -523,6 +545,7 @@ repeat:
} }
} }
gemini_token_finish(&tok);
gemini_parser_finish(&p); gemini_parser_finish(&p);
return false; return false;
} }
@ -617,6 +640,7 @@ do_requests(struct browser *browser, struct gemini_response *resp)
CURLUPART_SCHEME, &scheme, 0); CURLUPART_SCHEME, &scheme, 0);
assert(uc == CURLUE_OK); // Invariant assert(uc == CURLUE_OK); // Invariant
if (strcmp(scheme, "file") == 0) { if (strcmp(scheme, "file") == 0) {
free(scheme);
requesting = false; requesting = false;
char *path; char *path;
@ -630,6 +654,7 @@ do_requests(struct browser *browser, struct gemini_response *resp)
FILE *fp = fopen(path, "r"); FILE *fp = fopen(path, "r");
if (!fp) { if (!fp) {
resp->status = GEMINI_STATUS_NOT_FOUND; resp->status = GEMINI_STATUS_NOT_FOUND;
free(path);
break; break;
} }
@ -643,9 +668,14 @@ do_requests(struct browser *browser, struct gemini_response *resp)
} else { } else {
resp->meta = strdup("application/x-octet-stream"); resp->meta = strdup("application/x-octet-stream");
} }
free(path);
resp->status = GEMINI_STATUS_SUCCESS; resp->status = GEMINI_STATUS_SUCCESS;
resp->fd = -1;
resp->ssl = NULL;
resp->ssl_ctx = NULL;
return display_response(browser, resp); return display_response(browser, resp);
} }
free(scheme);
enum gemini_result res = gemini_request(browser->plain_url, enum gemini_result res = gemini_request(browser->plain_url,
&browser->opts, resp); &browser->opts, resp);
@ -672,7 +702,7 @@ do_requests(struct browser *browser, struct gemini_response *resp)
case GEMINI_STATUS_CLASS_REDIRECT: case GEMINI_STATUS_CLASS_REDIRECT:
if (++nredir >= 5) { if (++nredir >= 5) {
requesting = false; requesting = false;
fprintf(stderr, "Error: maximum redirects (5) exceeded"); fprintf(stderr, "Error: maximum redirects (5) exceeded\n");
break; break;
} }
fprintf(stderr, "Following redirect to %s\n", resp->meta); fprintf(stderr, "Following redirect to %s\n", resp->meta);
@ -816,6 +846,7 @@ main(int argc, char *argv[])
break; break;
default: default:
fprintf(stderr, "fatal: unknown flag %c\n", c); fprintf(stderr, "fatal: unknown flag %c\n", c);
curl_url_cleanup(browser.url);
return 1; return 1;
} }
} }
@ -841,6 +872,7 @@ main(int argc, char *argv[])
static char prompt[4096]; static char prompt[4096];
if (do_requests(&browser, &resp)) { if (do_requests(&browser, &resp)) {
// Skip prompts // Skip prompts
gemini_response_finish(&resp);
goto next; goto next;
} }
@ -880,8 +912,16 @@ next:;
browser.links = NULL; 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); SSL_CTX_free(browser.opts.ssl_ctx);
curl_url_cleanup(browser.url); curl_url_cleanup(browser.url);
free(browser.page_title);
free(browser.plain_url);
fclose(browser.tty);
return 0; return 0;
} }

@ -35,15 +35,14 @@ gemini_parser_next(struct gemini_parser *p, struct gemini_token *tok)
memset(tok, 0, sizeof(*tok)); memset(tok, 0, sizeof(*tok));
int eof = 0; int eof = 0;
while (!strstr(p->buf, "\n")) { while (!strchr(p->buf, '\n')) {
if (p->bufln == p->bufsz) { while (p->bufln >= p->bufsz - 1) {
p->bufsz *= 2; p->bufsz *= 2;
char *buf = realloc(p->buf, p->bufsz); p->buf = realloc(p->buf, p->bufsz);
assert(buf); assert(p->buf);
p->buf = 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) { if (n == -1) {
return -1; return -1;
} else if (n == 0) { } else if (n == 0) {
@ -55,7 +54,7 @@ gemini_parser_next(struct gemini_parser *p, struct gemini_token *tok)
} }
char *end; char *end;
if ((end = strstr(p->buf, "\n")) != NULL) { if ((end = strchr(p->buf, '\n')) != NULL) {
*end = 0; *end = 0;
} }

@ -150,7 +150,7 @@ gemini_tofu_init(struct gemini_tofu *tofu,
{.var = "XDG_DATA_HOME", .path = "/gmni/%s"}, {.var = "XDG_DATA_HOME", .path = "/gmni/%s"},
{.var = "HOME", .path = "/.local/share/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), snprintf(tofu->known_hosts_path, sizeof(tofu->known_hosts_path),
path_fmt, "known_hosts"); path_fmt, "known_hosts");
@ -164,6 +164,7 @@ gemini_tofu_init(struct gemini_tofu *tofu,
snprintf(tofu->known_hosts_path, sizeof(tofu->known_hosts_path), snprintf(tofu->known_hosts_path, sizeof(tofu->known_hosts_path),
path_fmt, "known_hosts"); path_fmt, "known_hosts");
free(path_fmt);
tofu->callback = cb; tofu->callback = cb;
tofu->cb_data = cb_data; tofu->cb_data = cb_data;
@ -175,6 +176,7 @@ gemini_tofu_init(struct gemini_tofu *tofu,
} }
size_t n = 0; size_t n = 0;
char *line = NULL; char *line = NULL;
tofu->known_hosts = NULL;
while (getline(&line, &n, f) != -1) { while (getline(&line, &n, f) != -1) {
struct known_host *host = calloc(1, sizeof(struct known_host)); struct known_host *host = calloc(1, sizeof(struct known_host));
char *tok = strtok(line, " "); char *tok = strtok(line, " ");
@ -184,6 +186,7 @@ gemini_tofu_init(struct gemini_tofu *tofu,
tok = strtok(NULL, " "); tok = strtok(NULL, " ");
assert(tok); assert(tok);
if (strcmp(tok, "SHA-512") != 0) { if (strcmp(tok, "SHA-512") != 0) {
free(host->host);
free(host); free(host);
continue; continue;
} }
@ -199,4 +202,19 @@ gemini_tofu_init(struct gemini_tofu *tofu,
host->next = tofu->known_hosts; host->next = tofu->known_hosts;
tofu->known_hosts = host; 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);
}
} }