Commit Diff


commit - 6acf1bb453dd5584d4567d939b3cc148c300f187
commit + 62c0f697ddc7396384280f2f8bac7a092700f1ba
blob - ef1c974197379078a2b295149504fc5bfa103738
blob + 9a44d9597949d1e4c46f5778bb4d34da0f552753
--- iri.c
+++ iri.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Omar Polo <op@omarpolo.com>
+ * Copyright (c) 2022, 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
@@ -761,4 +761,76 @@ iri_setquery(struct iri *iri, const char *p)
  err:
 	errno = ENOBUFS;
 	return (-1);
+}
+
+int
+iri_urlescape(const char *path, char *buf, size_t len)
+{
+	const char	*hex = "0123456789abcdef";
+	const uint8_t	*p = path;
+
+	while (*p) {
+		if (len == 0)
+			break;
+
+		if (unreserved(*p) || sub_delims(*p) ||
+		    *p == ':' || *p == '@' ||
+		    *p == '/') {
+			*buf++ = *p++;
+			len--;
+			continue;
+		}
+
+		if (len < 3)
+			break;
+		*buf++ = '%';
+		*buf++ = hex[*p >> 4];
+		*buf++ = hex[*p & 0xf];
+		len -= 3;
+		p++;
+	}
+
+	if (len == 0 || *p)
+		return (-1);
+
+	*buf = '\0';
+	return (0);
+}
+
+int
+iri_urlunescape(const char *str, char *buf, size_t len)
+{
+	char		 t[3];
+	unsigned long	 l;
+
+	t[2] = '\0';
+
+	while (*str) {
+		if (len == 0)
+			return (-1);
+
+		if (*str != '%') {
+			*buf++ = *str++;
+			len--;
+			continue;
+		}
+
+		if (!isxdigit((unsigned char)str[1]) ||
+		    !isxdigit((unsigned char)str[2]))
+			return (-1);
+	
+		t[0] = str[1];
+		t[1] = str[2];
+
+		/* we know it's a proper number and will fit a char */
+		l = strtol(t, NULL, 16);
+		*buf++ = (unsigned char)l;
+		len--;
+		str += 3;
+	}
+
+	if (len == 0)
+		return (-1);
+	*buf = '\0';
+	return (0);
 }
blob - 03ea7a8b0a497bc4ec9b9924db76cda1c03af23c
blob + 164785cba484bbb4faf96b930929940ae8537fbb
--- iri.h
+++ iri.h
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2022 Omar Polo <op@omarpolo.com>
+ * Copyright (c) 2022, 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
@@ -44,4 +44,7 @@ int	iri_human(const struct iri *, char *, size_t);
 int	iri_setport(struct iri *, const char *);
 int	iri_setquery(struct iri *, const char *);
 
+int	iri_urlescape(const char *, char *, size_t);
+int	iri_urlunescape(const char *, char *, size_t);
+
 #endif /* IRI_H */
blob - efd08aea46d99986a0dcae05a99a738784ac5a27
blob + ba333328471a966bb716d45587b30befead9bf0d
--- parser_gophermap.c
+++ parser_gophermap.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
@@ -21,6 +21,7 @@
 #include <string.h>
 
 #include "parser.h"
+#include "iri.h"
 #include "utils.h"
 
 #ifndef LINE_MAX
@@ -84,13 +85,28 @@ static int
 gm_parse(struct parser *p, const char *buf, size_t size)
 {
 	return parser_foreach_line(p, buf, size, gm_foreach_line);
+}
+
+static int
+selector2uri(struct gm_selector *s, char *buf, size_t len)
+{
+	int	 r;
+
+	r = snprintf(buf, len, "gopher://%s:%s/%c%s",
+	    s->addr, s->port, s->type, *s->selector != '/' ? "/" : "");
+	if (r < 0 || (size_t)r >= len)
+		return (-1);
+
+	buf += r;
+	len -= r;
+	return (iri_urlescape(s->selector, buf, len));
 }
 
 static inline int
 emit_line(struct parser *p, enum line_type type, struct gm_selector *s)
 {
 	struct line *l;
-	char buf[LINE_MAX], b[2] = {0};
+	char buf[LINE_MAX];
 
 	if ((l = calloc(1, sizeof(*l))) == NULL)
 		goto err;
@@ -102,18 +118,8 @@ emit_line(struct parser *p, enum line_type type, struc
 	case LINE_LINK:
 		if (s->type == 'h' && !strncmp(s->selector, "URL:", 4)) {
 			strlcpy(buf, s->selector+4, sizeof(buf));
-		} else {
-			strlcpy(buf, "gopher://", sizeof(buf));
-			strlcat(buf, s->addr, sizeof(buf));
-			strlcat(buf, ":", sizeof(buf));
-			strlcat(buf, s->port, sizeof(buf));
-			strlcat(buf, "/", sizeof(buf));
-			b[0] = s->type;
-			strlcat(buf, b, sizeof(buf));
-			if (*s->selector != '/')
-				strlcat(buf, "/", sizeof(buf));
-			strlcat(buf, s->selector, sizeof(buf));
-		}
+		} else if (selector2uri(s, buf, sizeof(buf)) == -1)
+			goto err;
 
 		if ((l->alt = strdup(buf)) == NULL)
 			goto err;
blob - 4e71ecbaff97f7964d344324aed229fd8d1cf793
blob + 80420dcfbd8080e2692413ee333b8a0fb9339839
--- telescope.c
+++ telescope.c
@@ -676,7 +676,8 @@ load_gopher_url(struct tab *tab, const char *url)
 		return;
 	}
 
-	strlcpy(req.req, path, sizeof(req.req));
+	if (iri_urlunescape(path, req.req, sizeof(req.req)) == -1)
+		strlcpy(req.req, path, sizeof(req.req));
 	if (tab->iri.iri_flags & IH_QUERY) {
 		strlcat(req.req, "?", sizeof(req.req));
 		strlcat(req.req, tab->iri.iri_query, sizeof(req.req));