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