shithub: gemnine

Download patch

ref: 5f38cbaf0e77fde61e9c14cd9d4e4c258772645c
parent: 44a1cdb2993372a26ec325fccadfaa570eb00e6f
author: Sigrid Haflínudóttir <ftrvxmtrx@gmail.com>
date: Mon May 18 10:17:12 EDT 2020

pass all of the URLs torture tests

--- a/main.c
+++ b/main.c
@@ -24,6 +24,49 @@
 
 #pragma varargck type "E" char*
 
+char *
+urlto(Url *url, char *u)
+{
+	char *e, *trail;
+	int len;
+
+	if((len = strlen(u)) < 1)
+		return "";
+	trail = u[len-1] == '/' ? "/" : "";
+
+	if(*u == '/'){
+		if(u[1] == '/') /* no protocol */
+			return smprint("gemini://%s%s", cleanname(u+2), trail);
+
+		/* absolute url, no scheme */
+		return smprint("gemini://%s:%s%s%s", url->server, url->port, cleanname(u), trail);
+	}
+
+	/* with scheme */
+	if((e = strpbrk(u, ":/")) != nil && e[0] == ':' && e[1] == '/' && e[2] == '/'){
+		e[2] = 0;
+		e = cleanname(e+3);
+		return smprint("%s/%s%s", u, e, trail);
+	}
+
+	/* chars not allowed */
+	if(strpbrk(u, ":") != nil)
+		return strdup(u);
+
+	/* relative, no scheme */
+	len = strlen(url->url);
+	if(url->url[len-1] == '/') /* easy */
+		u = smprint("%s/%s%s", url->url, u, trail);
+	else{
+		/* replace the last element */
+		if((e = strrchr(url->url, '/')) != nil)
+			len = e - url->url;
+		u = smprint("%.*s/%s%s", len, url->url, u, trail);
+	}
+	cleanname(strchr(strchr(u, ':') + 3, '/'));
+	return u;
+}
+
 Url *
 parseurl(char *url)
 {
@@ -90,7 +133,7 @@
 {
 	Thumbprint *th;
 	Response *r;
-	char *s, buf[256];
+	char *s, buf[1024];
 	TLSconn conn;
 	int i, ok, len, oldfd;
 
@@ -99,8 +142,10 @@
 	if((r->url = parseurl(url)) == nil)
 		goto err;
 
-	if((r->fd = dial(netmkaddr(r->url->server, "tcp", r->url->port), nil, nil, nil)) < 0)
+	if((r->fd = dial(netmkaddr(r->url->server, "tcp", r->url->port), nil, nil, nil)) < 0){
+		werrstr("dial: %r");
 		goto err;
+	}
 	th = initThumbprints("/sys/lib/ssl/gemini", nil, "x509");
 	memset(&conn, 0, sizeof(conn));
 	conn.serverName = r->url->server;
@@ -107,8 +152,10 @@
 	oldfd = r->fd;
 	r->fd = tlsClient(oldfd, &conn);
 	close(oldfd);
-	if(r->fd < 0)
+	if(r->fd < 0){
+		werrstr("tls: %r");
 		goto err;
+	}
 
 	/* FIXME find a way to trust on the first run */
 	if(th != nil){
@@ -123,8 +170,10 @@
 
 	fprint(r->fd, "%s\r\n", r->url->url);
 	for(len = 0; len < sizeof(buf)-1; len++){
-		if((i = read(r->fd, buf+len, 1)) < 0)
+		if((i = read(r->fd, buf+len, 1)) < 0){
+			werrstr("read: %r");
 			goto err;
+		}
 		if(i == 0 || buf[len] == '\n')
 			break;
 	}
@@ -147,8 +196,10 @@
 	}else if(r->status >= 20 && r->status < 30){ /* success */
 		r->mime = strdup(s[0] ? s : "text/gemini");
 	}else if(r->status >= 30 && r->status < 40){ /* redirect */
+		s = urlto(r->url, s);
 		freeresponse(r);
 		r = request(s);
+		free(s);
 	}else if(r->status >= 40 && r->status < 50){
 		werrstr("temporary failure: %s", s);
 		goto err;
@@ -316,21 +367,19 @@
 			while((s = Brdstr(&body, '\n', 1)) != nil){
 				if((len = Blinelen(&body)) > 0)
 					s[len] = 0;
+				for(len--; len >= 0 && (s[len] == '\r' || s[len] == '\n'); len--)
+					s[len] = 0;
 				if(s[0] == '=' && s[1] == '>'){
-					t = s + 2;
-					while(isspace(*t))
-						t++;
-					u = t;
-					if((t = strpbrk(t, " :/")) == nil || t[0] != ':' || t[1] != '/' || t[2] != '/'){ /* no scheme */
-						if(*u == '/'){ /* absolute */
-							Bprint(&out, "=> gemini://%s:%s/%s\n", r->url->server, r->url->port, u+1);
-						}else{
-							len = strlen(r->url->url);
-							Bprint(&out, "=> %s%s%s\n", r->url->url, r->url->url[len-1] != '/' ? "/" : "", u);
-						}
-					}else{
-						Bprint(&out, "%s\n", s);
-					}
+					u = s + 2;
+					while(isspace(*u))
+						u++;
+					if((t = strpbrk(u, " \t")) != nil)
+						*t++ = 0;
+					else
+						t = "";
+					u = urlto(r->url, u);
+					Bprint(&out, "→ %s %s\n", u, t);
+					free(u);
 				}else{
 					Bprint(&out, "%s\n", s);
 				}