last

git clone git://oldgit.suckless.org/last/
Log | Files | Refs

http.c (3821B)


      1 #include <u.h>
      2 #include <libc.h>
      3 #include <bio.h>
      4 #include "last.h"
      5 
      6 #define HTTPVER "HTTP/1.0"
      7 
      8 char*
      9 query(const char *path, const char *fmt) {
     10 	char *p, *q;
     11 	int i;
     12 
     13 	i = 0;
     14 	if(path)
     15 		i = strlen(path)+1;
     16 
     17 	p = malloc(i+strlen(fmt)+1);
     18 	for(q=p+i; *fmt; q++, fmt++)
     19 		if(*fmt == ' ')
     20 			*q = '&';
     21 		else
     22 			*q = *fmt;
     23 	*q = '\0';
     24 	
     25 	if(i) {
     26 		memcpy(p, path, i-1);
     27 		p[i-1] = '?';
     28 	}
     29 	setmalloctag(p, getcallerpc(&path));
     30 	return p;
     31 }
     32 
     33 int
     34 parseuri(char *uri, char **host, char **path) {
     35 	char *p, *q;
     36 	char c;
     37 
     38 	if(strncmp(uri, "http://", 7))
     39 		return 1;
     40 	q = uri+7;
     41 	for(p=q; c = *p; p++)
     42 		if(c == '/')
     43 			break;
     44 		else if(c == ':')
     45 			*p = '!';
     46 	if(c != '/')
     47 		return 1;
     48 
     49 	memmove(uri, "tcp!", 4);
     50 	memmove(uri+4, q, p-q);
     51 	uri[p-q+4] = '\0';
     52 
     53 	*host = uri;
     54 	*path = p;
     55 	return 0;
     56 }
     57 
     58 int
     59 hconv(Fmt *f) {
     60 	char *s, *p;
     61 	int ret, c;
     62 
     63 	ret = 0;
     64 	s = va_arg(f->args, char*);
     65 	for(; *s; s=p) {
     66 		for(p=s; (c = *p); p++) {
     67 			if(c & 0x80 || !isprint(c) || isspace(c))
     68 				break;
     69 			if(c == '%' && !(f->flags & FmtComma))
     70 				break;
     71 			switch(c) {
     72 			default:
     73 				continue;
     74 			case '<':
     75 			case '>':
     76 			case '{':
     77 			case '}':
     78 			case '|':
     79 			case '\\':
     80 			case ';':
     81 			case '&':
     82 			case '=':
     83 			case '@':
     84 			case '/':
     85 			case '?':
     86 			case '+':
     87 				break;
     88 			}
     89 			break;
     90 		}
     91 		if(p != s)
     92 			ret += fmtprint(f, "%.*s", p-s, s);
     93 		if(c) {
     94 			if(c == ' ')
     95 				ret += fmtstrcpy(f, "+");
     96 			else
     97 				ret += fmtprint(f, "%%%02uX", (uchar)c);
     98 			p++;
     99 		}
    100 	}
    101 	return ret;
    102 }
    103 
    104 int
    105 Hconv(Fmt *f) {
    106 	char *s, *p;
    107 	int ret, c;
    108 
    109 	ret = 0;
    110 	s = va_arg(f->args, char*);
    111 	for(; *s; s=p) {
    112 		for(p=s; (c = *p); p++) {
    113 			if(!isprint(c) || isspace(c) || c & 0x80)
    114 				break;
    115 			switch(c) {
    116 			default:
    117 				continue;
    118 			case '%':
    119 			case '<':
    120 			case '>':
    121 			case '{':
    122 			case '}':
    123 			case '|':
    124 			case '\\':
    125 				break;
    126 			}
    127 			break;
    128 		}
    129 		if(p != s)
    130 			ret += fmtprint(f, "%.*s", p-s, s);
    131 		if(c) {
    132 			ret += fmtprint(f, "%%%02uX", (uchar)c);
    133 			p++;
    134 		}
    135 	}
    136 	return ret;
    137 }
    138 
    139 void
    140 httpinit(void) {
    141 	fmtinstall('h', hconv);
    142 	fmtinstall('H', Hconv);
    143 }
    144 
    145 #define error(...) do{werrstr(__VA_ARGS__); goto error;}while(0)
    146 
    147 static char
    148 	Ebadresp[] = "Bad HTTP response code",
    149 	Ebadhdr[] = "Bad HTTP response header";
    150 
    151 Biobuf*
    152 httpget(char *host, char *path) {
    153 	Biobuf *bi, bo;
    154 	char *s, *p;
    155 	ulong code;
    156 	int fd, n, nredir, c;
    157 
    158 	s = nil;
    159 	nredir = 0;
    160 again:
    161 	if(nredir++ > 50) {
    162 		werrstr("redirect loop");
    163 		return nil;
    164 	}
    165 
    166 	fd = dial(netmkaddr(host, "tcp", "http"), 0, 0, 0);
    167 	if(fd < 0)
    168 		return nil;
    169 
    170 	if(debug['h'])
    171 		print("GET %s %q\n", host, path);
    172 	Binit(&bo, fd, OWRITE);
    173 	Bprint(&bo, "GET %s %s\r\n", path, HTTPVER);
    174 	Bprint(&bo, "Host: %s\r\n", host);
    175 	Bprint(&bo, "User-Agent: http.c Plan 9\r\n");
    176 	Bprint(&bo, "Accept: */*\r\n");
    177 	Bprint(&bo, "\r\n");
    178 	Bterm(&bo);
    179 
    180 	if(s)
    181 		free(s);
    182 
    183 	bi = Bfdopen(fd, OREAD);
    184 
    185 	s = Brdline(bi, '\n');
    186 	if(s == nil)
    187 		error("%s: no data", Ebadresp);
    188 
    189 	n = BLINELEN(bi);
    190 	if(n < 2)
    191 		error(Ebadresp);
    192 	if(s[n-2] != '\r')
    193 		error(Ebadresp);
    194 	s[n-2] = '\0';
    195 
    196 	if(debug['h'])
    197 		print("Resp: %s\n", s);
    198 
    199 	if(strncmp(s, HTTPVER " ", sizeof(HTTPVER)))
    200 		error("%s: bad version response", Ebadresp);
    201 	s += sizeof(HTTPVER);
    202 
    203 	code = strtoul(s, &p, 10);
    204 	if(p == s || !isspace(p[0]))
    205 		error("%s: non-numeric response code", Ebadresp);
    206 
    207 	if(code != 200 && code != 301 && code != 302)
    208 		error("http: %s", s);
    209 
    210 	for(;;) {
    211 		s = Brdline(bi, '\n');
    212 		n = BLINELEN(bi);
    213 		if(n < 2 || s[n-2] != '\r')
    214 			error(Ebadhdr);
    215 		if(n == 2)
    216 			break;
    217 		s[n-2] = '\0';
    218 		if(debug['h'])
    219 			print("Headr: %s\n", s);
    220 		p = strchr(s, ':');
    221 		if(p == nil)
    222 			error(Ebadhdr);
    223 		*p++ = '\0';
    224 		if((code == 301 || code == 302) && !cistrcmp(s, "Location")) {
    225 			while((c = *p) && isspace(c))
    226 				p++;
    227 			s = strdup(p);
    228 			parseuri(s, &host, &path);
    229 			Bterm(bi);
    230 			goto again;
    231 		}
    232 	}
    233 
    234 	return bi;
    235 
    236 error:
    237 	Bterm(bi);
    238 	return nil;
    239 }
    240