#include <u.h>
#include <libc.h>
#include <libsec.h>
#include <ctype.h>
#include "gemnine.h"
static char *statuses[] = {
[Cinput] = "input",
[Cinputnoecho] = "sensitive input",
[Csuccess] = "success",
[Credirtmp] = "redirect - temporary",
[Credirperm] = "redirect - permanent",
[Cfailtmp] = "temporary failure",
[Cunavail] = "server unavailable",
[Ccgierr] = "cgi error",
[Cproxyerr] = "proxy error",
[Cslowdown] = "slow down",
[Cfailperm] = "permanent failure",
[Cnotfound] = "not found",
[Cgone] = "gone",
[Cnoproxy] = "proxy request refused",
[Cbadrequest] = "bad request",
[Ccertrequired] = "client certificate required",
[Ccertnotauth] = "certificate not authorized",
[Ccertinvalid] = "certificate not valid",
};
Response *
request(Url *url)
{
Thumbprint *th;
Response *r;
char *s, buf[1024], *port;
TLSconn conn;
int i, ok, len, oldfd;
Url *u;
r = calloc(1, sizeof(*r));
r->fd = -1;
r->url = url;
if((port = url->port) == nil)
port = "1965";
if((r->fd = dial(netmkaddr(url->host, "tcp", 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->host;
oldfd = r->fd;
r->fd = tlsClient(oldfd, &conn);
close(oldfd);
if(r->fd < 0){
werrstr("tls: %r");
goto err;
}
/* FIXME find a way to trust on the first run */
if(th != nil){
ok = okCertificate(conn.cert, conn.certlen, th);
freeThumbprints(th);
free(conn.cert);
if(!ok){
//fprint(2, "echo 'x509 %r server=%s' >>/sys/lib/ssl/gemini\n", r->url->server);
//werrstr("untrusted cert");
//goto err;
}
}
fprint(r->fd, "%s\r\n", r->url->full);
for(len = 0; len < sizeof(buf)-1; len++){
if((i = read(r->fd, buf+len, 1)) < 0){
werrstr("read: %r");
goto err;
}
if(i == 0 || buf[len] == '\n')
break;
}
s = buf;
s[len] = 0;
for(len--; len >= 0 && (s[len] == '\r' || s[len] == '\n'); len--)
s[len] = 0;
if(s[0] < '0' || s[0] > '9' || s[1] < '0' || s[1] > '9'){
werrstr("invalid status");
goto err;
}
r->code = 10*(int)(s[0]-'0') + s[1] - '0';
s += 2;
while(isspace(*s))
s++;
if(r->code < nelem(statuses))
r->status = statuses[r->code];
if(r->status == nil)
r->status = "???";
if(r->code >= 10 && r->code < 20){ /* input */
r->prompt = estrdup(s);
}else if(r->code >= 20 && r->code < 30){ /* success */
r->mime = estrdup(s[0] ? s : "text/gemini");
}else if(r->code >= 30 && r->code < 40){ /* redirect */
if((u = urlparse(r->url, s)) == nil){
werrstr("invalid redirect");
goto err;
}
u->free = 1;
freeresponse(r);
r = request(u);
}else{
r->meta = estrdup(s);
werrstr(r->status);
goto err;
}
err:
return r;
}
void
freeresponse(Response *r)
{
if(r == nil)
return;
close(r->fd);
if(r->url->free)
freeurl(r->url);
free(r->mime);
free(r->prompt);
free(r->meta);
free(r);
}