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