1 /* $NetBSD: file_http.cpp,v 1.7 2002/03/02 22:01:57 uch 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