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