~ft/gemnine

5f38cbaf0e77fde61e9c14cd9d4e4c258772645c — Sigrid Haflínudóttir 2 years ago 44a1cdb
pass all of the URLs torture tests
1 files changed, 67 insertions(+), 18 deletions(-)

M main.c
M main.c => main.c +67 -18
@@ 24,6 24,49 @@ struct Response {

#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 @@ request(char *url)
{
	Thumbprint *th;
	Response *r;
	char *s, buf[256];
	char *s, buf[1024];
	TLSconn conn;
	int i, ok, len, oldfd;



@@ 99,16 142,20 @@ request(char *url)
	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;
	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 @@ request(char *url)

	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 @@ request(char *url)
	}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 @@ nextreq:
			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);
				}