1 /* $NetBSD: file_http.cpp,v 1.9 2004/08/06 18:33:09 uch 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 * 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 #include <libsa_string.h> 43 44 #define wcsicmp _wcsicmp 45 static int WCE210_WSAStartup(WORD, LPWSADATA); 46 static int WCE210_WSACleanup(void); 47 48 HttpFile::HttpFile(Console *&cons) 49 : File(cons), 50 _req_get("GET "), 51 _req_head("HEAD "), 52 _req_host(" HTTP/1.0\r\nHOST: "), 53 _req_ua("\r\nUser-Agent: HPCBOOT/ZERO(1st impact; Windows CE; " 54 #if defined MIPS 55 "MIPS" 56 #elif defined ARM 57 "ARM" 58 #elif defined SH3 59 "SH3" 60 #elif defined SH4 61 "SH4" 62 #else 63 "Unknown" 64 #endif 65 ")\r\n\r\n") 66 { 67 68 _server_name[0] = '\0'; 69 _debug = 1; 70 _memory_cache = TRUE; 71 // _memory_cache = FALSE; // not recomended. 72 _buffer = 0; 73 _reset_state(); 74 DPRINTF((TEXT("FileManager: HTTP\n"))); 75 76 #if _WIN32_WCE <= 200 77 _wsa_startup = WCE210_WSAStartup; 78 _wsa_cleanup = WCE210_WSACleanup; 79 #else 80 if (WinCEVersion.dwMajorVersion > 3 || 81 (WinCEVersion.dwMajorVersion > 2) && 82 (WinCEVersion.dwMinorVersion >= 11)) { 83 _wsa_startup = WSAStartup; 84 _wsa_cleanup = WSACleanup; 85 } else { 86 _wsa_startup = WCE210_WSAStartup; 87 _wsa_cleanup = WCE210_WSACleanup; 88 } 89 #endif 90 } 91 92 int 93 WCE210_WSAStartup(WORD ver, LPWSADATA data) 94 { 95 96 data->wVersion = ver; 97 data->wHighVersion = ver; 98 data->szDescription[0] = '\0'; 99 data->szSystemStatus[0] = '\0'; 100 data->iMaxSockets = 10; 101 data->iMaxUdpDg = 0; 102 data->lpVendorInfo = NULL; 103 104 return (0); 105 } 106 107 int 108 WCE210_WSACleanup() 109 { 110 111 return (0); 112 } 113 114 HttpFile::~HttpFile(void) 115 { 116 if (_buffer) 117 free(_buffer); 118 _wsa_cleanup(); 119 } 120 121 void 122 HttpFile::_reset_state(void) 123 { 124 _ascii_filename[0] = '\0'; 125 _cached = FALSE; 126 if (_buffer) 127 free(_buffer); 128 _buffer = 0; 129 _header_size = 0; 130 _cur_pos = 0; 131 } 132 133 BOOL 134 HttpFile::setRoot(TCHAR *server) 135 { 136 SOCKET h; 137 int ret, port; 138 139 // parse server name and its port # 140 TCHAR sep[] = TEXT(":/"); 141 142 TCHAR *token = wcstok(server, sep); 143 for (int i = 0; i < 3 && token; i++, token = wcstok(0, sep)) { 144 switch(i) { 145 case 0: 146 if (wcsicmp(token, TEXT("http"))) { 147 return FALSE; 148 } 149 break; 150 case 1: 151 if (!_to_ascii(_server_name, token, MAX_PATH)) 152 return FALSE; 153 port = 80; 154 break; 155 case 2: 156 port = _wtoi(token); 157 break; 158 } 159 } 160 161 WORD version = MAKEWORD(1, 1); 162 ret = _wsa_startup(version, &_winsock); 163 if (ret != 0) { 164 DPRINTF((TEXT("WinSock initialize failed.\n"))); 165 return FALSE; 166 } 167 if (LOBYTE(_winsock.wVersion) != 1 || 168 HIBYTE(_winsock.wVersion) != 1) { 169 DPRINTF((TEXT("can't use WinSock DLL.\n"))); 170 return FALSE; 171 } 172 173 h = socket(AF_INET, SOCK_STREAM, 0); 174 if (h == INVALID_SOCKET) { 175 DPRINTF((TEXT("can't open socket. cause=%d\n"), 176 WSAGetLastError())); 177 return FALSE; 178 } 179 180 memset(&_sockaddr, 0, sizeof(sockaddr_in)); 181 _sockaddr.sin_family = AF_INET; 182 _sockaddr.sin_port = htons(port); 183 184 struct hostent *entry = gethostbyname(_server_name); 185 if (entry == 0) { 186 _sockaddr.sin_addr.S_un.S_addr = inet_addr(_server_name); 187 if (_sockaddr.sin_addr.S_un.S_addr == INADDR_NONE) { 188 DPRINTF((TEXT("can't get host by name.\n"))); 189 return FALSE; 190 } 191 u_int8_t *b = &_sockaddr.sin_addr.S_un.S_un_b.s_b1; 192 DPRINTF((TEXT("%d.%d.%d.%d "), b[0], b[1], b[2], b[3])); 193 if (connect(h,(const struct sockaddr *)&_sockaddr, 194 sizeof(struct sockaddr_in)) == 0) 195 goto connected; 196 } else { 197 for (u_int8_t **addr_list =(u_int8_t **)entry->h_addr_list; 198 *addr_list; addr_list++) { 199 u_int8_t *b = &_sockaddr.sin_addr.S_un.S_un_b.s_b1; 200 for (int i = 0; i < 4; i++) 201 b[i] = addr_list[0][i]; 202 203 DPRINTF((TEXT("%d.%d.%d.%d "), b[0], b[1], b[2],b[3])); 204 if (connect(h,(const struct sockaddr *)&_sockaddr, 205 sizeof(struct sockaddr_in)) == 0) 206 goto connected; 207 } 208 } 209 DPRINTF((TEXT("can't connect server.\n"))); 210 return FALSE; 211 212 connected: 213 DPRINTF((TEXT("(%S) connected.\n"), _server_name)); 214 closesocket(h); 215 216 return TRUE; 217 } 218 219 BOOL 220 HttpFile::open(const TCHAR *name, u_int32_t flag) 221 { 222 223 _reset_state(); 224 225 return _to_ascii(_ascii_filename, name, MAX_PATH); 226 } 227 228 size_t 229 HttpFile::_read_from_cache(void *buf, size_t bytes, off_t ofs) 230 { 231 size_t transfer; 232 233 if (ofs >= _buffer_size) 234 return 0; 235 236 transfer = ofs + bytes > _buffer_size ? _buffer_size - ofs : bytes; 237 238 memcpy(buf, &_buffer[ofs], transfer); 239 240 return transfer; 241 } 242 243 BOOL 244 HttpFile::seek(off_t offset) 245 { 246 _cur_pos = offset; 247 248 return TRUE; 249 } 250 251 size_t 252 HttpFile::read(void *buf, size_t bytes, off_t offset) 253 { 254 char *b; 255 off_t ofs; 256 257 if (offset != -1) { 258 ofs = offset; 259 } else { 260 ofs = _cur_pos; 261 _cur_pos += bytes; 262 } 263 264 if (_memory_cache && _cached) 265 return _read_from_cache(buf, bytes, ofs); 266 267 // HEAD request(get header size). 268 if (_header_size == 0) 269 _buffer_size = _parse_header(_header_size); 270 271 // reconnect 272 Socket sock(_sockaddr); 273 SOCKET h; 274 if ((h = sock) == INVALID_SOCKET) 275 return 0; 276 277 // GET request 278 strcpy(_request, _req_get); 279 _set_request(); 280 send(h, _request, strlen(_request), 0); 281 282 // skip header. 283 b = static_cast <char *>(malloc(_header_size)); 284 _recv_buffer(h, b, _header_size); 285 free(b); 286 287 // read contents. 288 size_t readed; 289 if (_memory_cache) { 290 _buffer = static_cast <char *>(malloc(_buffer_size)); 291 _recv_buffer(h, _buffer, _buffer_size); 292 _cached = TRUE; 293 return _read_from_cache(buf, bytes, ofs); 294 } else { 295 int i, n = ofs / bytes; 296 b = static_cast <char *>(buf); 297 298 for (readed = 0, i = 0; i < n; i++) 299 readed += _recv_buffer(h, b, bytes); 300 if ((n =(ofs % bytes))) 301 readed += _recv_buffer(h, b, n); 302 DPRINTF((TEXT("skip contents %d byte.\n"), readed)); 303 304 readed = _recv_buffer(h, b, bytes); 305 } 306 return readed; 307 } 308 309 size_t 310 HttpFile::_parse_header(size_t &header_size) 311 { 312 int cnt, ret; 313 char *buf; 314 size_t sz = 0; 315 316 // reconnect. 317 Socket sock(_sockaddr); 318 SOCKET h; 319 if ((h = sock) == INVALID_SOCKET) { 320 DPRINTF((TEXT("can't open socket.\n"))); 321 return 0; 322 } 323 324 // HEAD request 325 strcpy(_request, _req_head); 326 _set_request(); 327 send(h, _request, strlen(_request), 0); 328 329 // Receive and search Content-Length: field. 330 if ((buf = static_cast<char *>(malloc(TMP_BUFFER_SIZE))) == 0) { 331 DPRINTF((TEXT("can't allocate receive buffer.\n"))); 332 return 0; 333 } 334 335 BOOL found = FALSE; 336 for (cnt = 0; ret = _recv_buffer(h, buf, TMP_BUFFER_SIZE - 1); 337 cnt += ret) { 338 buf[ret] = '\0'; 339 char sep[] = " :\r\n"; 340 char *token = libsa::strtok(buf, sep); 341 while (token) { 342 DPRINTFN(2, (TEXT("+token: %S\n"), token)); 343 if (libsa::stricmp(token, "content-length") == 0) { 344 DPRINTFN(2, (TEXT("*token: %S\n"), token)); 345 token = libsa::strtok(0, sep); 346 sz = atoi(token); 347 found = TRUE; 348 DPRINTFN(2, (TEXT("*content-length=%d\n"), sz)); 349 } else { 350 token = libsa::strtok(0, sep); 351 } 352 } 353 } 354 header_size = cnt; 355 356 if (!found) { 357 DPRINTF((TEXT("No Content-Length.\n"))); 358 } else { 359 DPRINTF((TEXT 360 ("open http://%S%S - header %d byte contents %d byte\n"), 361 _server_name, _ascii_filename, header_size, sz)); 362 } 363 free(buf); 364 365 return sz; 366 } 367 368 size_t 369 HttpFile::_recv_buffer(SOCKET h, char *buf, size_t size) 370 { 371 size_t cnt, total = 0; 372 373 do { 374 cnt = recv(h, buf + total, size - total, 0); 375 total += cnt; 376 DPRINTFN(2,(TEXT("size %d readed %d byte(+%d)\n"), 377 size, total, cnt)); 378 } while (total < size && cnt > 0); 379 380 DPRINTFN(1,(TEXT("total read %d byte\n"), total)); 381 return total; 382 } 383 384 void 385 HttpFile::_set_request(void) 386 { 387 388 strcat(_request, _ascii_filename); 389 strcat(_request, _req_host); 390 strcat(_request, _server_name); 391 strcat(_request, _req_ua); 392 } 393 394 Socket::Socket(struct sockaddr_in &sock) 395 : _sockaddr(sock) 396 { 397 398 _socket = socket(AF_INET, SOCK_STREAM, 0); 399 if (_socket != INVALID_SOCKET) 400 connect(_socket, 401 reinterpret_cast <const struct sockaddr *>(&_sockaddr), 402 sizeof(struct sockaddr_in)); 403 } 404 405 Socket::~Socket(void) 406 { 407 408 if (_socket != INVALID_SOCKET) 409 closesocket(_socket); 410 } 411