1 /* $NetBSD: file_http.cpp,v 1.2 2001/03/04 16:59:47 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 struct hostent *entry = gethostbyname(_server_name); 143 if (entry == 0) { 144 DPRINTF((TEXT("can't get host by name.\n"))); 145 return FALSE; 146 } 147 148 memset(&_sockaddr, 0, sizeof(sockaddr_in)); 149 _sockaddr.sin_family = AF_INET; 150 _sockaddr.sin_port = htons(port); 151 for (u_int8_t **addr_list =(u_int8_t **)entry->h_addr_list; 152 *addr_list; addr_list++) { 153 u_int8_t *b = &_sockaddr.sin_addr.S_un.S_un_b.s_b1; 154 for (int i = 0; i < 4; i++) 155 b[i] = addr_list[0][i]; 156 157 DPRINTF((TEXT("%d.%d.%d.%d "), b[0], b[1], b[2], b[3])); 158 if (connect(h,(const struct sockaddr *)&_sockaddr, 159 sizeof(struct sockaddr_in)) == 0) 160 goto connected; 161 } 162 DPRINTF((TEXT("can't connect server.\n"))); 163 return FALSE; 164 165 connected: 166 DPRINTF((TEXT(" \"%S\"\n"), _server_name)); 167 closesocket(h); 168 169 return TRUE; 170 } 171 172 BOOL 173 HttpFile::open(const TCHAR *name, u_int32_t flag) 174 { 175 _reset_state(); 176 return _to_ascii(_ascii_filename, name, MAX_PATH); 177 } 178 179 size_t 180 HttpFile::_read_from_cache(void *buf, size_t bytes, off_t ofs) 181 { 182 size_t transfer = ofs + bytes > _buffer_size 183 ? _buffer_size - ofs : bytes; 184 memcpy(buf, &_buffer[ofs], transfer); 185 return transfer; 186 } 187 188 BOOL 189 HttpFile::seek(off_t offset) 190 { 191 _cur_pos = offset; 192 193 return TRUE; 194 } 195 196 size_t 197 HttpFile::read(void *buf, size_t bytes, off_t offset) 198 { 199 char *b; 200 off_t ofs; 201 202 if (offset != -1) { 203 ofs = offset; 204 } else { 205 ofs = _cur_pos; 206 _cur_pos += bytes; 207 } 208 209 if (_memory_cache && _cached) 210 return _read_from_cache(buf, bytes, ofs); 211 212 // HEAD request(get header size). 213 if (_header_size == 0) 214 _buffer_size = _parse_header(_header_size); 215 216 // reconnect 217 Socket sock(_sockaddr); 218 SOCKET h; 219 if ((h = sock) == INVALID_SOCKET) 220 return 0; 221 222 // GET request 223 strcpy(_request, _req_get); 224 _set_request(); 225 send(h, _request, strlen(_request), 0); 226 227 // skip header. 228 b = static_cast <char *>(malloc(_header_size)); 229 _recv_buffer(h, b, _header_size); 230 free(b); 231 232 // read contents. 233 size_t readed; 234 if (_memory_cache) { 235 _buffer = static_cast <char *>(malloc(_buffer_size)); 236 _recv_buffer(h, _buffer, _buffer_size); 237 _cached = TRUE; 238 return _read_from_cache(buf, bytes, ofs); 239 } else { 240 int i, n = ofs / bytes; 241 b = static_cast <char *>(buf); 242 243 for (readed = 0, i = 0; i < n; i++) 244 readed += _recv_buffer(h, b, bytes); 245 if ((n =(ofs % bytes))) 246 readed += _recv_buffer(h, b, n); 247 DPRINTF((TEXT("skip contents %d byte.\n"), readed)); 248 249 readed = _recv_buffer(h, b, bytes); 250 } 251 return readed; 252 } 253 254 size_t 255 HttpFile::_parse_header(size_t &header_size) 256 { 257 size_t sz = 0; 258 // reconnect. 259 Socket sock(_sockaddr); 260 SOCKET h; 261 if ((h = sock) == INVALID_SOCKET) 262 return 0; 263 264 // HEAD request 265 strcpy(_request, _req_head); 266 _set_request(); 267 send(h, _request, strlen(_request), 0); 268 269 // receive. 270 char __buf[TMP_BUFFER_SIZE]; 271 int cnt, ret; 272 273 for (cnt = 0; 274 ret = _recv_buffer(h, __buf, TMP_BUFFER_SIZE); cnt += ret) { 275 char sep[] = " :\r\n"; 276 char *token = strtok(__buf, sep); 277 while (token) { 278 if (__stricmp(token, "content-length") == 0) { 279 token = strtok(0, sep); 280 sz = atoi(token); 281 DPRINTF((TEXT("content-length=%d\n"), sz)); 282 } else 283 token = strtok(0, sep); 284 } 285 } 286 header_size = cnt; 287 DPRINTF((TEXT("header %d byte contents %d byte\n"), header_size, sz)); 288 289 return sz; 290 } 291 292 size_t 293 HttpFile::_recv_buffer(SOCKET h, char *buf, size_t size) 294 { 295 size_t cnt, total = 0; 296 297 do { 298 cnt = recv(h, buf + total, size - total, 0); 299 total += cnt; 300 DPRINTFN(2,(TEXT("size %d readed %d byte(+%d)\n"), 301 size, total, cnt)); 302 } while (total < size && cnt > 0); 303 304 305 DPRINTFN(1,(TEXT("total read %d byte\n"), total)); 306 return total; 307 } 308 309 void 310 HttpFile::_set_request(void) 311 { 312 strcat(_request, _ascii_filename); 313 strcat(_request, _req_host); 314 strcat(_request, _server_name); 315 strcat(_request, _req_ua); 316 } 317 318 static int 319 __stricmp(const char *s1, const char *s2) 320 { 321 const unsigned char *us1 = 322 reinterpret_cast <const unsigned char *>(s1); 323 const unsigned char *us2 = 324 reinterpret_cast <const unsigned char *>(s2); 325 326 while (tolower(*us1) == tolower(*us2++)) 327 if (*us1++ == '\0') 328 return 0; 329 return tolower(*us1) - tolower(*--us2); 330 } 331 332 Socket::Socket(struct sockaddr_in &sock) 333 : _sockaddr(sock) 334 { 335 _socket = socket(AF_INET, SOCK_STREAM, 0); 336 if (_socket != INVALID_SOCKET) 337 connect(_socket, 338 reinterpret_cast <const struct sockaddr *>(&_sockaddr), 339 sizeof(struct sockaddr_in)); 340 } 341 342 Socket::~Socket(void) 343 { 344 if (_socket != INVALID_SOCKET) 345 closesocket(_socket); 346 } 347