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