1/*	$NetBSD: file_http.cpp,v 1.11 2006/03/05 04:05:39 uwe Exp $	*/
2
3/*-
4 * Copyright (c) 2001, 2002, 2004 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by UCHIYAMA Yasushi.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32#include <file.h>
33#include <file_http.h>
34#include <console.h>
35#include <libsa_string.h>
36
37#define	wcsicmp		_wcsicmp
38static int WCE210_WSAStartup(WORD, LPWSADATA);
39static int WCE210_WSACleanup(void);
40
41HttpFile::HttpFile(Console *&cons)
42	: File(cons),
43	  _req_get("GET "),
44	  _req_head("HEAD "),
45	  _req_host(" HTTP/1.0\r\nHOST: "),
46	  _req_ua("\r\nUser-Agent: HPCBOOT/ZERO(1st impact; Windows CE; "
47#if defined MIPS
48	      "MIPS"
49#elif defined ARM
50	      "ARM"
51#elif defined SH3
52	      "SH3"
53#elif defined SH4
54	      "SH4"
55#else
56	      "Unknown"
57#endif
58	      ")\r\n\r\n")
59{
60
61	_server_name[0] = '\0';
62	_debug = 1;
63	_memory_cache = TRUE;
64	//    _memory_cache = FALSE; // not recomended.
65	_buffer = 0;
66	_reset_state();
67	DPRINTF((TEXT("FileManager: HTTP\n")));
68
69#if _WIN32_WCE <= 200
70	_wsa_startup = WCE210_WSAStartup;
71	_wsa_cleanup = WCE210_WSACleanup;
72#else
73	if (WinCEVersion.dwMajorVersion > 3 ||
74	    (WinCEVersion.dwMajorVersion > 2) &&
75	    (WinCEVersion.dwMinorVersion >= 11)) {
76		_wsa_startup = WSAStartup;
77		_wsa_cleanup = WSACleanup;
78	} else {
79		_wsa_startup = WCE210_WSAStartup;
80		_wsa_cleanup = WCE210_WSACleanup;
81	}
82#endif
83}
84
85int
86WCE210_WSAStartup(WORD ver, LPWSADATA data)
87{
88
89	data->wVersion		= ver;
90	data->wHighVersion	= ver;
91	data->szDescription[0]	= '\0';
92	data->szSystemStatus[0]	= '\0';
93	data->iMaxSockets	= 10;
94	data->iMaxUdpDg		= 0;
95	data->lpVendorInfo	= NULL;
96
97	return (0);
98}
99
100int
101WCE210_WSACleanup()
102{
103
104	return (0);
105}
106
107HttpFile::~HttpFile(void)
108{
109	if (_buffer)
110		free(_buffer);
111	_wsa_cleanup();
112}
113
114void
115HttpFile::_reset_state(void)
116{
117	_ascii_filename[0] = '\0';
118	_cached = FALSE;
119	if (_buffer)
120		free(_buffer);
121	_buffer = 0;
122	_header_size = 0;
123	_cur_pos = 0;
124}
125
126BOOL
127HttpFile::setRoot(TCHAR *server)
128{
129	SOCKET h;
130	int ret, port;
131
132	// parse server name and its port #
133	TCHAR sep[] = TEXT(":/");
134
135	TCHAR *token = wcstok(server, sep);
136	for (int i = 0; i < 3 && token; i++, token = wcstok(0, sep)) {
137		switch(i) {
138		case 0:
139			if (wcsicmp(token, TEXT("http"))) {
140				return FALSE;
141			}
142			break;
143		case 1:
144			if (!_to_ascii(_server_name, token, MAX_PATH))
145				return FALSE;
146			port = 80;
147			break;
148		case 2:
149			port = _wtoi(token);
150			break;
151		}
152	}
153
154	WORD version = MAKEWORD(1, 1);
155	ret = _wsa_startup(version, &_winsock);
156	if (ret != 0) {
157		DPRINTF((TEXT("WinSock initialize failed.\n")));
158		return FALSE;
159	}
160	if (LOBYTE(_winsock.wVersion) != 1 ||
161	    HIBYTE(_winsock.wVersion) != 1) {
162		DPRINTF((TEXT("can't use WinSock DLL.\n")));
163		return FALSE;
164	}
165
166	h = socket(AF_INET, SOCK_STREAM, 0);
167	if (h == INVALID_SOCKET) {
168		DPRINTF((TEXT("can't open socket. cause=%d\n"),
169		    WSAGetLastError()));
170		return FALSE;
171	}
172
173	memset(&_sockaddr, 0, sizeof(sockaddr_in));
174	_sockaddr.sin_family = AF_INET;
175	_sockaddr.sin_port = htons(port);
176
177	struct hostent *entry = gethostbyname(_server_name);
178	if (entry == 0) {
179		_sockaddr.sin_addr.S_un.S_addr = inet_addr(_server_name);
180		if (_sockaddr.sin_addr.S_un.S_addr == INADDR_NONE) {
181			DPRINTF((TEXT("can't get host by name.\n")));
182			return FALSE;
183		}
184		uint8_t *b = &_sockaddr.sin_addr.S_un.S_un_b.s_b1;
185		DPRINTF((TEXT("%d.%d.%d.%d "), b[0], b[1], b[2], b[3]));
186		if (connect(h,(const struct sockaddr *)&_sockaddr,
187		    sizeof(struct sockaddr_in)) == 0)
188			goto connected;
189	} else {
190		for (uint8_t **addr_list =(uint8_t **)entry->h_addr_list;
191		    *addr_list; addr_list++) {
192			uint8_t *b = &_sockaddr.sin_addr.S_un.S_un_b.s_b1;
193			for (int i = 0; i < 4; i++)
194				b[i] = addr_list[0][i];
195
196			DPRINTF((TEXT("%d.%d.%d.%d "), b[0], b[1], b[2],b[3]));
197			if (connect(h,(const struct sockaddr *)&_sockaddr,
198			    sizeof(struct sockaddr_in)) == 0)
199				goto connected;
200		}
201	}
202	DPRINTF((TEXT("can't connect server.\n")));
203	return FALSE;
204
205 connected:
206	DPRINTF((TEXT("(%S) connected.\n"), _server_name));
207	closesocket(h);
208
209	return TRUE;
210}
211
212BOOL
213HttpFile::open(const TCHAR *name, uint32_t flag)
214{
215
216	_reset_state();
217
218	return _to_ascii(_ascii_filename, name, MAX_PATH);
219}
220
221size_t
222HttpFile::_read_from_cache(void *buf, size_t bytes, off_t ofs)
223{
224	size_t transfer;
225
226	if (ofs >= _buffer_size)
227		return 0;
228
229	transfer = ofs + bytes > _buffer_size ? _buffer_size - ofs : bytes;
230
231	memcpy(buf, &_buffer[ofs], transfer);
232
233	return transfer;
234}
235
236BOOL
237HttpFile::seek(off_t offset)
238{
239	_cur_pos = offset;
240
241	return TRUE;
242}
243
244size_t
245HttpFile::read(void *buf, size_t bytes, off_t offset)
246{
247	char *b;
248	off_t ofs;
249
250	if (offset != -1) {
251		ofs = offset;
252	} else {
253		ofs = _cur_pos;
254		_cur_pos += bytes;
255	}
256
257	if (_memory_cache && _cached)
258		return _read_from_cache(buf, bytes, ofs);
259
260	// HEAD request(get header size).
261	if (_header_size == 0)
262		_buffer_size = _parse_header(_header_size);
263
264	// reconnect
265	Socket sock(_sockaddr);
266	SOCKET h;
267	if ((h = sock) == INVALID_SOCKET)
268		return 0;
269
270	// GET request
271	strcpy(_request, _req_get);
272	_set_request();
273	send(h, _request, strlen(_request), 0);
274
275	// skip header.
276	b = static_cast <char *>(malloc(_header_size));
277	_recv_buffer(h, b, _header_size);
278	free(b);
279
280	// read contents.
281	size_t readed;
282	if (_memory_cache) {
283		_buffer = static_cast <char *>(malloc(_buffer_size));
284		_recv_buffer(h, _buffer, _buffer_size);
285		_cached = TRUE;
286		return _read_from_cache(buf, bytes, ofs);
287	} else {
288		int i, n = ofs / bytes;
289		b = static_cast <char *>(buf);
290
291		for (readed = 0, i = 0; i < n; i++)
292			readed += _recv_buffer(h, b, bytes);
293		if ((n =(ofs % bytes)))
294			readed += _recv_buffer(h, b, n);
295		DPRINTF((TEXT("skip contents %d byte.\n"), readed));
296
297		readed = _recv_buffer(h, b, bytes);
298	}
299	return readed;
300}
301
302size_t
303HttpFile::_parse_header(size_t &header_size)
304{
305	int cnt, ret;
306	char *buf;
307	size_t sz = 0;
308
309	// reconnect.
310	Socket sock(_sockaddr);
311	SOCKET h;
312	if ((h = sock) == INVALID_SOCKET) {
313		DPRINTF((TEXT("can't open socket.\n")));
314		return 0;
315	}
316
317	// HEAD request
318	strcpy(_request, _req_head);
319	_set_request();
320	send(h, _request, strlen(_request), 0);
321
322	// Receive and search Content-Length: field.
323	if ((buf = static_cast<char *>(malloc(TMP_BUFFER_SIZE))) == 0) {
324		DPRINTF((TEXT("can't allocate receive buffer.\n")));
325		return 0;
326	}
327
328	BOOL found = FALSE;
329	for (cnt = 0; ret = _recv_buffer(h, buf, TMP_BUFFER_SIZE - 1);
330	    cnt += ret) {
331		buf[ret] = '\0';
332		char sep[] = " :\r\n";
333		char *token = libsa::strtok(buf, sep);
334		while (token) {
335			DPRINTFN(2, (TEXT("+token: %S\n"), token));
336			if (libsa::stricmp(token, "content-length") == 0) {
337				DPRINTFN(2, (TEXT("*token: %S\n"), token));
338				token = libsa::strtok(0, sep);
339				sz = atoi(token);
340				found = TRUE;
341				DPRINTFN(2, (TEXT("*content-length=%d\n"), sz));
342			} else {
343				token = libsa::strtok(0, sep);
344			}
345		}
346	}
347	header_size = cnt;
348
349	if (!found) {
350		DPRINTF((TEXT("No Content-Length.\n")));
351	} else {
352		DPRINTF((TEXT
353		    ("open http://%S%S - header %d byte contents %d byte\n"),
354		    _server_name, _ascii_filename, header_size, sz));
355	}
356	free(buf);
357
358	return sz;
359}
360
361size_t
362HttpFile::_recv_buffer(SOCKET h, char *buf, size_t size)
363{
364	size_t cnt, total = 0;
365
366	do {
367		cnt = recv(h, buf + total, size - total, 0);
368		total += cnt;
369		DPRINTFN(2,(TEXT("size %d readed %d byte(+%d)\n"),
370		    size, total, cnt));
371	} while (total < size && cnt > 0);
372
373	DPRINTFN(1,(TEXT("total read %d byte\n"), total));
374	return total;
375}
376
377void
378HttpFile::_set_request(void)
379{
380
381	strcat(_request, _ascii_filename);
382	strcat(_request, _req_host);
383	strcat(_request, _server_name);
384	strcat(_request, _req_ua);
385}
386
387Socket::Socket(struct sockaddr_in &sock)
388	: _sockaddr(sock)
389{
390
391	_socket = socket(AF_INET, SOCK_STREAM, 0);
392	if (_socket != INVALID_SOCKET)
393		connect(_socket,
394		    reinterpret_cast <const struct sockaddr *>(&_sockaddr),
395		    sizeof(struct sockaddr_in));
396}
397
398Socket::~Socket(void)
399{
400
401	if (_socket != INVALID_SOCKET)
402		closesocket(_socket);
403}
404