1/*
2 * Generic HTTP routines
3 *
4 * Copyright (C) 2010, Broadcom Corporation. All Rights Reserved.
5 *
6 * Permission to use, copy, modify, and/or distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
9 *
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
13 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
15 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
16 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 *
18 * $Id: http.c,v 1.11 2005/08/09 22:18:32 Exp $
19 */
20
21#include <stdio.h>
22#include <stdlib.h>
23#include <limits.h>
24#include <ctype.h>
25#include <string.h>
26#include <signal.h>
27#include <time.h>
28#include <unistd.h>
29#include <errno.h>
30#include <sys/socket.h>
31#include <netinet/in.h>
32#include <arpa/inet.h>
33
34#include <shutils.h>
35
36static char *
37base64enc(const char *p, char *buf, int len)
38{
39	char al[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
40		"0123456789+/";
41	char *s = buf;
42
43	while (*p) {
44		if (s >= buf+len-4)
45			break;
46		*(s++) = al[(*p >> 2) & 0x3F];
47		*(s++) = al[((*p << 4) & 0x30) | ((*(p+1) >> 4) & 0x0F)];
48		*s = *(s+1) = '=';
49		*(s+2) = 0;
50		if (! *(++p)) break;
51		*(s++) = al[((*p << 2) & 0x3C) | ((*(p+1) >> 6) & 0x03)];
52		if (! *(++p)) break;
53		*(s++) = al[*(p++) & 0x3F];
54	}
55
56	return buf;
57}
58
59enum {
60	METHOD_GET,
61	METHOD_POST
62};
63
64static int
65wget(int method, const char *server, char *buf, size_t count, off_t offset)
66{
67	char url[PATH_MAX] = { 0 }, *s;
68	char *host = url, *path = "", auth[128] = { 0 }, line[512];
69	unsigned short port = 80;
70	int fd;
71	FILE *fp;
72	struct sockaddr_in sin;
73	int chunked = 0, len = 0;
74
75	if (server == NULL || !strcmp(server, "")) {
76		dprintf("wget: null server input\n");
77		return (0);
78	}
79
80	strncpy(url, server, sizeof(url));
81
82	/* Parse URL */
83	if (!strncmp(url, "http://", 7)) {
84		port = 80;
85		host = url + 7;
86	}
87	if ((s = strchr(host, '/'))) {
88		*s++ = '\0';
89		path = s;
90	}
91	if ((s = strchr(host, '@'))) {
92		*s++ = '\0';
93		base64enc(host, auth, sizeof(auth));
94		host = s;
95	}
96	if ((s = strchr(host, ':'))) {
97		*s++ = '\0';
98		port = atoi(s);
99	}
100
101	/* Open socket */
102	if (!inet_aton(host, &sin.sin_addr))
103		return 0;
104	sin.sin_family = AF_INET;
105	sin.sin_port = htons(port);
106
107	dprintf("Connecting to %s:%u...\n", host, port);
108	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ||
109	    connect(fd, (struct sockaddr *) &sin, sizeof(sin)) < 0 ||
110	    !(fp = fdopen(fd, "r+"))) {
111		perror(host);
112		if (fd >= 0)
113			close(fd);
114		return 0;
115	}
116	dprintf("connected!\n");
117
118	/* Send HTTP request */
119	fprintf(fp, "%s /%s HTTP/1.1\r\n", method == METHOD_POST ? "POST" : "GET", path);
120	fprintf(fp, "Host: %s\r\n", host);
121	fprintf(fp, "User-Agent: wget\r\n");
122	if (strlen(auth))
123		fprintf(fp, "Authorization: Basic %s\r\n", auth);
124	if (offset)
125		fprintf(fp, "Range: bytes=%ld-\r\n", offset);
126	if (method == METHOD_POST) {
127		fprintf(fp, "Content-Type: application/x-www-form-urlencoded\r\n");
128		fprintf(fp, "Content-Length: %d\r\n\r\n", (int) strlen(buf));
129		fputs(buf, fp);
130	} else
131		fprintf(fp, "Connection: close\r\n\r\n");
132
133	/* Check HTTP response */
134	dprintf("HTTP request sent, awaiting response...\n");
135	if (fgets(line, sizeof(line), fp)) {
136		dprintf("%s", line);
137		for (s = line; *s && !isspace((int)*s); s++);
138		for (; isspace((int)*s); s++);
139		switch (atoi(s)) {
140		case 200: if (offset) goto done; else break;
141		case 206: if (offset) break; else goto done;
142		default: goto done;
143		}
144	}
145
146	/* Parse headers */
147	while (fgets(line, sizeof(line), fp)) {
148		dprintf("%s", line);
149		for (s = line; *s == '\r'; s++);
150		if (*s == '\n')
151			break;
152		if (!strncasecmp(s, "Content-Length:", 15)) {
153			for (s += 15; isblank(*s); s++);
154			chomp(s);
155			len = atoi(s);
156		}
157		else if (!strncasecmp(s, "Transfer-Encoding:", 18)) {
158			for (s += 18; isblank(*s); s++);
159			chomp(s);
160			if (!strncasecmp(s, "chunked", 7))
161				chunked = 1;
162		}
163	}
164
165	if (chunked && fgets(line, sizeof(line), fp))
166		len = strtol(line, NULL, 16);
167
168	len = (len > count) ? count : len;
169	len = fread(buf, 1, len, fp);
170
171done:
172	/* Close socket */
173	fflush(fp);
174	fclose(fp);
175	return len;
176}
177
178int
179http_get(const char *server, char *buf, size_t count, off_t offset)
180{
181	return wget(METHOD_GET, server, buf, count, offset);
182}
183
184int
185http_post(const char *server, char *buf, size_t count)
186{
187	/* No continuation generally possible with POST */
188	return wget(METHOD_POST, server, buf, count, 0);
189}
190