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);
}