diff --git a/include/tofu.h b/include/tofu.h index 29aa9bc..a88167b 100644 --- a/include/tofu.h +++ b/include/tofu.h @@ -44,5 +44,6 @@ struct gemini_tofu { 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 2d39f56..bb508e0 100644 --- a/src/client.c +++ b/src/client.c @@ -118,11 +118,14 @@ gemini_request(const char *url, struct gemini_options *options, } 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 @@ gemini_request(const char *url, struct gemini_options *options, 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 @@ gemini_request(const char *url, struct gemini_options *options, 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 @@ gemini_response_finish(struct gemini_response *resp) } 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 c13e0cd..4af98fb 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 5d45ffe..a2717fb 100644 --- a/src/gmnlm.c +++ b/src/gmnlm.c @@ -83,6 +83,7 @@ history_free(struct history *history) return; } history_free(history->next); + free(history->url); free(history); } @@ -93,6 +94,9 @@ set_url(struct browser *browser, char *new_url, struct history **history) 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) { struct history *next = calloc(1, sizeof(struct history)); @@ -130,17 +134,19 @@ trim_ws(char *in) 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 @@ save_bookmark(struct browser *browser) 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 @@ save_bookmark(struct browser *browser) fprintf(browser->tty, "Bookmark saved: %s\n", title ? title : browser->plain_url); + if (title != NULL) { + free(title); + } } static void @@ -411,12 +420,14 @@ repeat: } 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 @@ repeat: text = NULL; } } + if (text == NULL) { + gemini_token_finish(&tok); + } while (col >= ws.ws_col) { col -= ws.ws_col; @@ -510,8 +524,16 @@ repeat: 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 @@ repeat: } } + gemini_token_finish(&tok); gemini_parser_finish(&p); return false; } @@ -617,6 +640,7 @@ do_requests(struct browser *browser, struct gemini_response *resp) CURLUPART_SCHEME, &scheme, 0); assert(uc == CURLUE_OK); // Invariant if (strcmp(scheme, "file") == 0) { + free(scheme); requesting = false; char *path; @@ -630,6 +654,7 @@ do_requests(struct browser *browser, struct gemini_response *resp) FILE *fp = fopen(path, "r"); if (!fp) { resp->status = GEMINI_STATUS_NOT_FOUND; + free(path); break; } @@ -643,9 +668,14 @@ do_requests(struct browser *browser, struct gemini_response *resp) } 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 @@ do_requests(struct browser *browser, struct gemini_response *resp) 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 @@ main(int argc, char *argv[]) break; default: fprintf(stderr, "fatal: unknown flag %c\n", c); + curl_url_cleanup(browser.url); return 1; } } @@ -841,6 +872,7 @@ main(int argc, char *argv[]) static char prompt[4096]; if (do_requests(&browser, &resp)) { // Skip prompts + gemini_response_finish(&resp); goto next; } @@ -880,8 +912,16 @@ next:; 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 5b0f013..2f78e46 100644 --- a/src/parser.c +++ b/src/parser.c @@ -35,15 +35,14 @@ gemini_parser_next(struct gemini_parser *p, struct gemini_token *tok) 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 @@ gemini_parser_next(struct gemini_parser *p, struct gemini_token *tok) } 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 354e211..45e1275 100644 --- a/src/tofu.c +++ b/src/tofu.c @@ -150,7 +150,7 @@ gemini_tofu_init(struct gemini_tofu *tofu, {.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 @@ gemini_tofu_init(struct gemini_tofu *tofu, 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 @@ gemini_tofu_init(struct gemini_tofu *tofu, } 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 @@ gemini_tofu_init(struct gemini_tofu *tofu, tok = strtok(NULL, " "); assert(tok); if (strcmp(tok, "SHA-512") != 0) { + free(host->host); free(host); continue; } @@ -199,4 +202,19 @@ gemini_tofu_init(struct gemini_tofu *tofu, 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); + } }