1 /* $NetBSD: file_http.cpp,v 1.5 2001/05/08 18:51:22 uch Exp $ */ 2 3 /*- 4 * Copyright (c) 2001 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 #if _WIN32_WCE < 210 44 #define tolower(c) ((c) - 'A' + 'a') 45 #define wcsicmp _wcsicmp 46 #endif 47 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 _server_name[0] = '\0'; 70 _debug = TRUE; 71 _memory_cache = TRUE; 72 // _memory_cache = FALSE; // not recomended. 73 _buffer = 0; 74 _reset_state(); 75 DPRINTF((TEXT("File: HTTP\n"))); 76 } 77 78 HttpFile::~HttpFile(void) 79 { 80 if (_buffer) 81 free(_buffer); 82 WSACleanup(); 83 } 84 85 void 86 HttpFile::_reset_state(void) 87 { 88 _ascii_filename[0] = '\0'; 89 _cached = FALSE; 90 if (_buffer) 91 free(_buffer); 92 _buffer = 0; 93 _header_size = 0; 94 _cur_pos = 0; 95 } 96 97 BOOL 98 HttpFile::setRoot(TCHAR *server) 99 { 100 SOCKET h; 101 int ret, port; 102 103 // parse server name and its port # 104 TCHAR sep[] = TEXT(":/"); 105 TCHAR *token = wcstok(server, sep); 106 for (int i = 0; i < 3 && token; i++, token = wcstok(0, sep)) { 107 switch(i) { 108 case 0: 109 if (wcsicmp(token, TEXT("http"))) { 110 return FALSE; 111 } 112 break; 113 case 1: 114 if (!_to_ascii(_server_name, token, MAX_PATH)) 115 return FALSE; 116 port = 80; 117 break; 118 case 2: 119 port = _wtoi(token); 120 break; 121 } 122 } 123 124 ret = WSAStartup(MAKEWORD(1, 1), &_winsock); 125 if (ret != 0) { 126 DPRINTF((TEXT("WinSock initialize failed.\n"))); 127 return FALSE; 128 } 129 if (LOBYTE(_winsock.wVersion) != 1 || 130 HIBYTE(_winsock.wVersion) != 1) { 131 DPRINTF((TEXT("can't use WinSock DLL.\n"))); 132 return FALSE; 133 } 134 135 h = socket(AF_INET, SOCK_STREAM, 0); 136 if (h == INVALID_SOCKET) { 137 DPRINTF((TEXT("can't open socket. cause=%d\n"), 138 WSAGetLastError())); 139 return FALSE; 140 } 141 142 memset(&_sockaddr, 0, sizeof(sockaddr_in)); 143 _sockaddr.sin_family = AF_INET; 144 _sockaddr.sin_port = htons(port); 145 146 struct hostent *entry = gethostbyname(_server_name); 147 if (entry == 0) { 148 _sockaddr.sin_addr.S_un.S_addr = inet_addr(_server_name); 149 if (_sockaddr.sin_addr.S_un.S_addr == INADDR_NONE) { 150 DPRINTF((TEXT("can't get host by name.\n"))); 151 return FALSE; 152 } 153 u_int8_t *b = &_sockaddr.sin_addr.S_un.S_un_b.s_b1; 154 DPRINTF((TEXT("%d.%d.%d.%d "), b[0], b[1], b[2], b[3])); 155 if (connect(h,(const struct sockaddr *)&_sockaddr, 156 sizeof(struct sockaddr_in)) == 0) 157 goto connected; 158 } else { 159 for (u_int8_t **addr_list =(u_int8_t **)entry->h_addr_list; 160 *addr_list; addr_list++) { 161 u_int8_t *b = &_sockaddr.sin_addr.S_un.S_un_b.s_b1; 162 for (int i = 0; i < 4; i++) 163 b[i] = addr_list[0][i]; 164 165 DPRINTF((TEXT("%d.%d.%d.%d "), b[0], b[1], b[2],b[3])); 166 if (connect(h,(const struct sockaddr *)&_sockaddr, 167 sizeof(struct sockaddr_in)) == 0) 168 goto connected; 169 } 170 } 171 DPRINTF((TEXT("can't connect server.\n"))); 172 return FALSE; 173 174 connected: 175 DPRINTF((TEXT(" \"%S\"\n"), _server_name)); 176 closesocket(h); 177 178 return TRUE; 179 } 180 181 BOOL 182 HttpFile::open(const TCHAR *name, u_int32_t flag) 183 { 184 _reset_state(); 185 return _to_ascii(_ascii_filename, name, MAX_PATH); 186 } 187 188 size_t 189 HttpFile::_read_from_cache(void *buf, size_t bytes, off_t ofs) 190 { 191 size_t transfer; 192 193 if (ofs >= _buffer_size) 194 return 0; 195 196 transfer = ofs + bytes > _buffer_size ? _buffer_size - ofs : bytes; 197 198 memcpy(buf, &_buffer[ofs], transfer); 199 200 return transfer; 201 } 202 203 BOOL 204 HttpFile::seek(off_t offset) 205 { 206 _cur_pos = offset; 207 208 return TRUE; 209 } 210 211 size_t 212 HttpFile::read(void *buf, size_t bytes, off_t offset) 213 { 214 char *b; 215 off_t ofs; 216 217 if (offset != -1) { 218 ofs = offset; 219 } else { 220 ofs = _cur_pos; 221 _cur_pos += bytes; 222 } 223 224 if (_memory_cache && _cached) 225 return _read_from_cache(buf, bytes, ofs); 226 227 // HEAD request(get header size). 228 if (_header_size == 0) 229 _buffer_size = _parse_header(_header_size); 230 231 // reconnect 232 Socket sock(_sockaddr); 233 SOCKET h; 234 if ((h = sock) == INVALID_SOCKET) 235 return 0; 236 237 // GET request 238 strcpy(_request, _req_get); 239 _set_request(); 240 send(h, _request, strlen(_request), 0); 241 242 // skip header. 243 b = static_cast <char *>(malloc(_header_size)); 244 _recv_buffer(h, b, _header_size); 245 free(b); 246 247 // read contents. 248 size_t readed; 249 if (_memory_cache) { 250 _buffer = static_cast <char *>(malloc(_buffer_size)); 251 _recv_buffer(h, _buffer, _buffer_size); 252 _cached = TRUE; 253 return _read_from_cache(buf, bytes, ofs); 254 } else { 255 int i, n = ofs / bytes; 256 b = static_cast <char *>(buf); 257 258 for (readed = 0, i = 0; i < n; i++) 259 readed += _recv_buffer(h, b, bytes); 260 if ((n =(ofs % bytes))) 261 readed += _recv_buffer(h, b, n); 262 DPRINTF((TEXT("skip contents %d byte.\n"), readed)); 263 264 readed = _recv_buffer(h, b, bytes); 265 } 266 return readed; 267 } 268 269 size_t 270 HttpFile::_parse_header(size_t &header_size) 271 { 272 size_t sz = 0; 273 // reconnect. 274 Socket sock(_sockaddr); 275 SOCKET h; 276 if ((h = sock) == INVALID_SOCKET) 277 return 0; 278 279 // HEAD request 280 strcpy(_request, _req_head); 281 _set_request(); 282 send(h, _request, strlen(_request), 0); 283 284 // receive. 285 char __buf[TMP_BUFFER_SIZE]; 286 int cnt, ret; 287 288 for (cnt = 0; 289 ret = _recv_buffer(h, __buf, TMP_BUFFER_SIZE); cnt += ret) { 290 char sep[] = " :\r\n"; 291 char *token = strtok(__buf, sep); 292 while (token) { 293 if (__stricmp(token, "content-length") == 0) { 294 token = strtok(0, sep); 295 sz = atoi(token); 296 DPRINTF((TEXT("content-length=%d\n"), sz)); 297 } else 298 token = strtok(0, sep); 299 } 300 } 301 header_size = cnt; 302 DPRINTF((TEXT("header %d byte contents %d byte\n"), header_size, sz)); 303 304 return sz; 305 } 306 307 size_t 308 HttpFile::_recv_buffer(SOCKET h, char *buf, size_t size) 309 { 310 size_t cnt, total = 0; 311 312 do { 313 cnt = recv(h, buf + total, size - total, 0); 314 total += cnt; 315 DPRINTFN(2,(TEXT("size %d readed %d byte(+%d)\n"), 316 size, total, cnt)); 317 } while (total < size && cnt > 0); 318 319 320 DPRINTFN(1,(TEXT("total read %d byte\n"), total)); 321 return total; 322 } 323 324 void 325 HttpFile::_set_request(void) 326 { 327 strcat(_request, _ascii_filename); 328 strcat(_request, _req_host); 329 strcat(_request, _server_name); 330 strcat(_request, _req_ua); 331 } 332 333 static int 334 __stricmp(const char *s1, const char *s2) 335 { 336 const unsigned char *us1 = 337 reinterpret_cast <const unsigned char *>(s1); 338 const unsigned char *us2 = 339 reinterpret_cast <const unsigned char *>(s2); 340 341 while (tolower(*us1) == tolower(*us2++)) 342 if (*us1++ == '\0') 343 return 0; 344 return tolower(*us1) - tolower(*--us2); 345 } 346 347 Socket::Socket(struct sockaddr_in &sock) 348 : _sockaddr(sock) 349 { 350 _socket = socket(AF_INET, SOCK_STREAM, 0); 351 if (_socket != INVALID_SOCKET) 352 connect(_socket, 353 reinterpret_cast <const struct sockaddr *>(&_sockaddr), 354 sizeof(struct sockaddr_in)); 355 } 356 357 Socket::~Socket(void) 358 { 359 if (_socket != INVALID_SOCKET) 360 closesocket(_socket); 361 } 362