1/*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License as
4 * published by the Free Software Foundation; either version 2 of
5 * the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
15 * MA 02111-1307 USA
16 */
17/*
18 * Generic HTTP routines
19 *
20 * Copyright 2001, ASUSTeK Inc.
21 * All Rights Reserved.
22 *
23 * This is UNPUBLISHED PROPRIETARY SOURCE CODE of ASUSTeK Inc.;
24 * the contents of this file may not be disclosed to third parties, copied or
25 * duplicated in any form, in whole or in part, without the prior written
26 * permission of ASUSTeK Inc..
27 *
28 */
29
30#include <stdio.h>
31#include <stdlib.h>
32#include <limits.h>
33#include <ctype.h>
34#include <string.h>
35#include <signal.h>
36#include <time.h>
37#include <unistd.h>
38#include <errno.h>
39#include <sys/socket.h>
40#include <netinet/in.h>
41#include <arpa/inet.h>
42
43#include <shutils.h>
44
45static char *
46base64enc(const char *p, char *buf, int len)
47{
48	char al[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
49		"0123456789+/";
50	char *s = buf;
51
52	while (*p) {
53		if (s >= buf+len-4)
54			break;
55		*(s++) = al[(*p >> 2) & 0x3F];
56		*(s++) = al[((*p << 4) & 0x30) | ((*(p+1) >> 4) & 0x0F)];
57		*s = *(s+1) = '=';
58		*(s+2) = 0;
59		if (! *(++p)) break;
60		*(s++) = al[((*p << 2) & 0x3C) | ((*(p+1) >> 6) & 0x03)];
61		if (! *(++p)) break;
62		*(s++) = al[*(p++) & 0x3F];
63	}
64
65	return buf;
66}
67
68enum {
69	METHOD_GET,
70	METHOD_POST
71};
72
73static int
74wget(int method, const char *server, char *buf, size_t count, off_t offset)
75{
76	char url[PATH_MAX] = { 0 }, *s;
77	char *host = url, *path = "", auth[128] = { 0 }, line[512];
78	unsigned short port = 80;
79	int fd;
80	FILE *fp;
81	struct sockaddr_in sin;
82	int chunked = 0, len = 0;
83
84	strncpy(url, server, sizeof(url));
85
86	/* Parse URL */
87	if (!strncmp(url, "http://", 7)) {
88		port = 80;
89		host = url + 7;
90	}
91	if ((s = strchr(host, '/'))) {
92		*s++ = '\0';
93		path = s;
94	}
95	if ((s = strchr(host, '@'))) {
96		*s++ = '\0';
97		base64enc(host, auth, sizeof(auth));
98		host = s;
99	}
100	if ((s = strchr(host, ':'))) {
101		*s++ = '\0';
102		port = atoi(s);
103	}
104
105	/* Open socket */
106	if (!inet_aton(host, &sin.sin_addr))
107		return (-(errno = EINVAL));
108	sin.sin_family = AF_INET;
109	sin.sin_port = htons(port);
110
111	dprintf("Connecting to %s:%u...\n", host, port);
112	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0 ||
113	    connect(fd, (struct sockaddr *) &sin, sizeof(sin)) < 0 ||
114	    !(fp = fdopen(fd, "r+"))) {
115		perror(host);
116		if (fd >= 0)
117			close(fd);
118		return -errno;
119	}
120	dprintf("connected!\n");
121
122	/* Send HTTP request */
123	fprintf(fp, "%s /%s HTTP/1.1\r\n", method == METHOD_POST ? "POST" : "GET", path);
124	fprintf(fp, "Host: %s\r\n", host);
125	fprintf(fp, "User-Agent: wget\r\n");
126	if (strlen(auth))
127		fprintf(fp, "Authorization: Basic %s\r\n", auth);
128	if (offset)
129		fprintf(fp, "Range: bytes=%ld-\r\n", offset);
130	if (method == METHOD_POST) {
131		fprintf(fp, "Content-Type: application/x-www-form-urlencoded\r\n");
132		fprintf(fp, "Content-Length: %d\r\n\r\n", strlen(buf));
133		fputs(buf, fp);
134	} else
135		fprintf(fp,"Connection: close\r\n\r\n");
136
137	/* Check HTTP response */
138	dprintf("HTTP request sent, awaiting response...\n");
139	if (fgets(line, sizeof(line), fp)) {
140		dprintf("%s", line);
141		for (s = line; *s && !isspace(*s); s++);
142		for (; isspace(*s); s++);
143		switch (atoi(s)) {
144		case 200: if (offset) goto done; else break;
145		case 206: if (offset) break; else goto done;
146		default: goto done;
147		}
148	}
149
150	/* Parse headers */
151	while (fgets(line, sizeof(line), fp)) {
152		dprintf("%s", line);
153		for (s = line; *s == '\r'; s++);
154		if (*s == '\n')
155			break;
156		if (!strncasecmp(s, "Content-Length:", 15)) {
157			for (s += 15; isblank(*s); s++);
158			chomp(s);
159			len = atoi(s);
160		}
161		else if (!strncasecmp(s, "Transfer-Encoding:", 18)) {
162			for (s += 18; isblank(*s); s++);
163			chomp(s);
164			if (!strncasecmp(s, "chunked", 7))
165				chunked = 1;
166		}
167	}
168
169	if (chunked && fgets(line, sizeof(line), fp))
170		len = strtol(line, NULL, 16);
171
172	len = (len > count) ? count : len;
173	len = fread(buf, 1, len, fp);
174
175 done:
176	/* Close socket */
177	fflush(fp);
178	fclose(fp);
179	return len;
180}
181
182int
183http_get(const char *server, char *buf, size_t count, off_t offset)
184{
185	return wget(METHOD_GET, server, buf, count, offset);
186}
187
188int
189http_post(const char *server, char *buf, size_t count)
190{
191	/* No continuation generally possible with POST */
192	return wget(METHOD_POST, server, buf, count, 0);
193}
194