Commit Diff


commit - 12472189518006a105ca309e5443be98a646b8a1
commit + 5a39f5931f4b5be1d57d75c7f25cff8adae5f235
blob - 00c6e94de447c08d07ccb19a922584839ab9e024
blob + cd2a4d10283bf4ad5deabc320552d4c15cff03a3
--- certs.c
+++ certs.c
@@ -49,18 +49,17 @@
 
 /* client certificate */
 struct ccert {
-	char	*line;	/* fields below points inside here */
 	char	*host;
 	char	*port;
 	char	*path;
 	char	*cert;
 };
 
-static struct cert_store {
+static struct cstore {
 	struct ccert	*certs;
 	size_t		 len;
 	size_t		 cap;
-} cert_store;
+} cert_store, temp_store;
 
 char		**identities;
 static size_t	  id_len, id_cap;
@@ -122,70 +121,76 @@ certs_cmp(const void *a, const void *b)
 }
 
 static int
-certs_store_add(const char *l)
+certs_store_add(struct cstore *cstore, const char *host, const char *port,
+    const char *path, const char *cert)
 {
-	size_t			 newcap;
-	void			*t;
-	char			*line, *host, *port, *path, *cert;
+	struct ccert	*c;
+	void		*t;
+	size_t		 newcap;
 
-	if ((line = strdup(l)) == NULL)
-		return (-1);
+	if (cstore->len == cstore->cap) {
+		newcap = cstore->cap + 8;
+		t = reallocarray(cstore->certs, newcap,
+		    sizeof(*cstore->certs));
+		if (t == NULL)
+			return (-1);
+		cstore->certs = t;
+		cstore->cap = newcap;
+	}
 
-	host = line;
-	while (isspace((unsigned char)*host))
-		++host;
+	c = &cstore->certs[cstore->len];
+	if ((c->host = strdup(host)) == NULL ||
+	    (c->port = strdup(port)) == NULL ||
+	    (c->path = strdup(path)) == NULL ||
+	    (c->cert = strdup(cert)) == NULL) {
+		free(c->host);
+		free(c->port);
+		free(c->path);
+		free(c->cert);
+		memset(c, 0, sizeof(*c));
+	}
+	cstore->len++;
 
-	if (*host == '#') {
-		free(line);
+	return (0);
+}
+
+static int
+certs_store_parse_line(struct cstore *cstore, char *line)
+{
+	char		*host, *port, *path, *cert;
+
+	while (isspace((unsigned char)*line))
+		++line;
+	if (*line == '#' || *line == '\0')
 		return (0);
-	}
 
+	host = line;
+
 	port = host + strcspn(host, " \t");
 	if (*port == '\0')
-		goto err;
+		return (-1);
 	*port++ = '\0';
 	while (isspace((unsigned char)*port))
 		++port;
 
 	path = port + strcspn(port, " \t");
 	if (*path == '\0')
-		goto err;
+		return (-1);
 	*path++ = '\0';
 	while (isspace((unsigned char)*path))
 		++path;
 
 	cert = path + strcspn(path, " \t");
 	if (*cert == '\0')
-		goto err;
+		return (-1);
 	*cert++ = '\0';
 	while (isspace((unsigned char)*cert))
 		++cert;
 
 	if (*cert == '\0')
-		goto err;
+		return (-1);
 
-	if (cert_store.len == cert_store.cap) {
-		newcap = cert_store.cap + 8;
-		t = reallocarray(cert_store.certs, newcap,
-		    sizeof(*cert_store.certs));
-		if (t == NULL)
-			goto err;
-		cert_store.certs = t;
-		cert_store.cap = newcap;
-	}
-
-	cert_store.certs[cert_store.len].line = line;
-	cert_store.certs[cert_store.len].host = host;
-	cert_store.certs[cert_store.len].port = port;
-	cert_store.certs[cert_store.len].path = path;
-	cert_store.certs[cert_store.len].cert = cert;
-	cert_store.len++;
-
-	return (0);
-
- err:
-	free(line);
-	return (-1);
+	return (certs_store_add(cstore, host, port, path, cert));
 }
 
 int
@@ -226,7 +231,7 @@ certs_init(const char *certfile)
 		if (line[linelen - 1] == '\n')
 			line[--linelen] = '\0';
 
-		if (certs_store_add(line) == -1) {
+		if (certs_store_parse_line(&cert_store, line) == -1) {
 			fclose(fp);
 			free(line);
 			return (-1);
@@ -283,25 +288,121 @@ path_under(const char *cpath, const char *tpath)
 	return (cpath[-1] == '/');
 }
 
-const char *
-cert_for(struct iri *iri)
+static struct ccert *
+find_cert_for(struct cstore *cstore, struct iri *iri)
 {
 	struct ccert	*c;
 	size_t		 i;
 
-	for (i = 0; i < cert_store.len; ++i) {
-		c = &cert_store.certs[i];
+	for (i = 0; i < cstore->len; ++i) {
+		c = &cstore->certs[i];
 
 		if (!strcmp(c->host, iri->iri_host) &&
 		    !strcmp(c->port, iri->iri_portstr) &&
 		    path_under(c->path, iri->iri_path))
-			return (c->cert);
+			return (c);
 	}
 
 	return (NULL);
 }
 
+const char *
+cert_for(struct iri *iri)
+{
+	struct ccert	*c;
+
+	if ((c = find_cert_for(&temp_store, iri)) != NULL)
+		return (c->cert);
+	if ((c = find_cert_for(&cert_store, iri)) != NULL)
+		return (c->cert);
+	return (NULL);
+}
+
+static int
+write_cert_file(void)
+{
+	struct ccert	*c;
+	FILE		*fp;
+	char		 sfn[PATH_MAX];
+	size_t		 i;
+	int		 fd, r;
+
+	strlcpy(sfn, certs_file_tmp, sizeof(sfn));
+	if ((fd = mkstemp(sfn)) == -1 ||
+	    (fp = fdopen(fd, "w")) == NULL) {
+		if (fd != -1) {
+			unlink(sfn);
+			close(fd);
+		}
+		return (-1);
+	}
+
+	for (i = 0; i < cert_store.len; ++i) {
+		c = &cert_store.certs[i];
+		if (c->cert == NULL)
+			continue;
+
+		r = fprintf(fp, "%s\t%s\t%s\t%s\n", c->host, c->port,
+		    c->path, c->cert);
+		if (r < 0) {
+			fclose(fp);
+			unlink(sfn);
+			return (-1);
+		}
+	}
+
+	if (ferror(fp)) {
+		fclose(fp);
+		unlink(sfn);
+		return (-1);
+	}
+
+	if (fclose(fp) == EOF) {
+		unlink(sfn);
+		return (-1);
+	}
+
+	if (rename(sfn, certs_file) == -1) {
+		unlink(sfn);
+		return (-1);
+	}
+
+	return (0);
+}
+
 int
+cert_save_for(const char *cert, struct iri *i, int persist)
+{
+	struct cstore	*cstore;
+	struct ccert	*c;
+	char		*d;
+
+	cstore = persist ? &cert_store : &temp_store;
+
+	if ((c = find_cert_for(cstore, i)) != NULL) {
+		if ((d = strdup(cert)) == NULL)
+			return (-1);
+
+		free(c->cert);
+		c->cert = d;
+
+		return (0);
+	}
+
+	if (certs_store_add(cstore, i->iri_host, i->iri_portstr, i->iri_path,
+	    cert) == -1)
+		return (-1);
+
+	qsort(cert_store.certs, cert_store.len, sizeof(*cert_store.certs),
+	    certs_cmp);
+
+	if (persist && write_cert_file() == -1)
+		return (-1);
+
+	return (0);
+}
+
+int
 cert_open(const char *cert)
 {
 	char		 path[PATH_MAX];
blob - 02ed3f170e25607b1fa02036f749359c51db5996
blob + 3512f77a2d20b2adb66315efa49e2a2f15052ba3
--- certs.h
+++ certs.h
@@ -25,5 +25,6 @@ extern char	**identities;
 int		 certs_init(const char *);
 const char	*ccert(const char *);
 const char	*cert_for(struct iri *);
+int		 cert_save_for(const char *, struct iri *, int);
 int		 cert_open(const char *);
 int		 cert_new(const char *, const char *, const char *, int);
blob - f0bf316ca2ca85c7efad6ecfa3d4bc3f73e22cb5
blob + 8a32941b4aad1fa405ae08f275800dd1c45b6693
--- cmd.c
+++ cmd.c
@@ -1093,3 +1093,14 @@ cmd_up(struct buffer *buffer)
 {
 	load_url_in_tab(current_tab, "..", NULL, LU_MODE_NOCACHE);
 }
+
+void
+cmd_use_certificate(struct buffer *buffer)
+{
+	GUARD_RECURSIVE_MINIBUFFER();
+
+	enter_minibuffer(sensible_self_insert, uc_select, exit_minibuffer,
+	    NULL, compl_uc, NULL, 1);
+	strlcpy(ministate.prompt, "Select certificate: ",
+	    sizeof(ministate.prompt));
+}
blob - c215bb0e2afa6871f4f93d31fd8c9224af20a3a8
blob + 5f194e9f36baf000919951aaf3efe24470856f86
--- cmd.h
+++ cmd.h
@@ -82,4 +82,5 @@ CMD(cmd_toggle_downloads,	"Toggle the downloads side w
 CMD(cmd_toggle_help,		"Toggle side window with help.");
 CMD(cmd_toggle_pre_wrap,	"Toggle the wrapping of preformatted blocks.");
 CMD(cmd_up,			"Go up one level.");
+CMD(cmd_use_certificate,	"Use a certificate for the current page.");
 CMD(cmd_write_buffer,		"Save the current page to the disk.");
blob - f6243324b8c79bb4caa3af734dd71f9c490da953
blob + ded602c8ff80b3d4913b94345460da61c038babd
--- compl.c
+++ compl.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ * Copyright (c) 2021, 2024 Omar Polo <op@omarpolo.com>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -18,6 +18,7 @@
 
 #include <stdlib.h>
 
+#include "certs.h"
 #include "compl.h"
 #include "hist.h"
 #include "telescope.h"
@@ -158,3 +159,22 @@ compl_toc(void **data, void **ret, const char **descr)
 	*line = TAILQ_NEXT(l, lines);
 	return text;
 }
+
+/*
+ * Provide completions for use-certificate
+ */
+const char *
+compl_uc(void **data, void **ret, const char **descr)
+{
+	const char	***state = (const char ***)data;
+
+	/* first time: init the state */
+	if (*state == NULL)
+		*state = (const char **)identities;
+
+	if (**state == NULL)
+		return NULL;
+
+	/* XXX filling descr too would be nice */
+	return *((*state)++);
+}
blob - c2d3ddef9936e81d473da6e75de86d4af340c7e3
blob + 3fbb6ecf9ea517f229c1381d0b0ae97faf0c71c0
--- compl.h
+++ compl.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2021 Omar Polo <op@omarpolo.com>
+ * Copyright (c) 2021, 2024 Omar Polo <op@omarpolo.com>
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
@@ -23,5 +23,6 @@ const char	*compl_ts(void **, void **, const char **);
 const char	*compl_ls(void **, void **, const char **);
 const char	*compl_swiper(void **, void **, const char **);
 const char	*compl_toc(void **, void **, const char **);
+const char	*compl_uc(void **, void **, const char **);
 
 #endif
blob - e9606e181d1cb50517cabfd4d25d55fc9d872828
blob + b05beff923e24cff4ab21a19147baaa6fb5db07b
--- minibuffer.c
+++ minibuffer.c
@@ -22,6 +22,7 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include "certs.h"
 #include "fs.h"
 #include "hist.h"
 #include "iri.h"
@@ -426,6 +427,34 @@ toc_select(void)
 }
 
 static void
+save_cert_for_site_cb(int r, struct tab *tab)
+{
+	cert_save_for(tab->client_cert, &tab->iri, r);
+}
+
+void
+uc_select(void)
+{
+	const char	*name;
+
+	name = minibuffer_compl_text();
+	if (!strcmp(name, "Generate new certificate")) {
+		message("Not implemented yet!");
+		return;
+	}
+
+	if ((current_tab->client_cert = ccert(name)) == NULL) {
+		message("Certificate %s not found", name);
+		return;
+	}
+
+	exit_minibuffer();
+
+	yornp("Always use this cert on this page?", save_cert_for_site_cb,
+	    current_tab);
+}
+
+static void
 yornp_self_insert(void)
 {
 	if (thiskey.key != 'y' && thiskey.key != 'n') {
blob - d993896dd79343bae46a2ba6dc48362c4b802c8a
blob + 693f6b6e018dab5defaa9c193f993d8496332733
--- minibuffer.h
+++ minibuffer.h
@@ -84,6 +84,7 @@ void	 ts_select(void);
 void	 ls_select(void);
 void	 swiper_select(void);
 void	 toc_select(void);
+void	 uc_select(void);
 
 void	 enter_minibuffer(void(*)(void), void(*)(void), void(*)(void),
     struct hist *, complfn *, void *, int);
blob - c50a70155a36fb4151dc2ecd9f8e6e49345412d1
blob + 6e35e4e642563038362b5bad6eaf81a619958a40
--- telescope.c
+++ telescope.c
@@ -372,6 +372,8 @@ handle_request_response(struct tab *tab)
 			    LU_MODE_NOCACHE);
 	} else { /* 4x, 5x & 6x */
 		load_page_from_str(tab, err_pages[tab->code]);
+		if (tab->code >= 60)
+			cmd_use_certificate(&tab->buffer);
 	}
 }