1 // Copyright (c) 1995 James Clark
2 // See the file COPYING for copying permission.
3 #pragma ident "%Z%%M% %I% %E% SMI"
4
5 #ifdef __GNUG__
6 #pragma implementation
7 #endif
8
9 // FIXME This implementation won't work on an EBCDIC machine.
10
11 #include "splib.h"
12 #ifdef WINSOCK
13 #include <winsock.h>
14 #define readsocket(s, p, n) ::recv(s, p, n, 0)
15 #define writesocket(s, p, n) ::send(s, p, n, 0)
16 #define errnosocket (WSAGetLastError())
17 #define SocketMessageArg(n) WinsockMessageArg(n)
18 #define SOCKET_EINTR (WSAEINTR)
19 #define SP_HAVE_SOCKET
20 #else
21 #ifdef SP_HAVE_SOCKET
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <netdb.h>
25 #include <netinet/in.h>
26 #include <arpa/inet.h>
27 #ifdef SP_INCLUDE_UNISTD_H
28 #include <unistd.h>
29 #endif
30
31 #ifdef SP_INCLUDE_OSFCN_H
32 #include <osfcn.h>
33 #endif
34
35 #ifdef SP_DECLARE_H_ERRNO
36 extern int h_errno;
37 #endif
38
39 typedef int SOCKET;
40 #define SOCKET_ERROR (-1)
41 #define INVALID_SOCKET (-1)
42 #define SOCKET_EINTR (EINTR)
43 #define closesocket(s) close(s)
44 #define writesocket(fd, p, n) ::write(fd, p, n)
45 #define readsocket(s, p, n) ::read(s, p, n)
46 #define errnosocket (errno)
47 #define SocketMessageArg(n) ErrnoMessageArg(n)
48 #include "ErrnoMessageArg.h"
49
50 #endif /* SP_HAVE_SOCKET */
51
52 #endif /* not WINSOCK */
53
54 #include "URLStorage.h"
55 #include "URLStorageMessages.h"
56 #include "RewindStorageObject.h"
57 #include "UnivCharsetDesc.h"
58 #include "MessageArg.h"
59 #include "MessageBuilder.h"
60 #include "macros.h"
61
62 #include <stdlib.h>
63 #include <string.h>
64 #include <errno.h>
65 #include <stddef.h>
66 #include <ctype.h>
67 #include <stdio.h>
68
69 #ifdef SP_NAMESPACE
70 namespace SP_NAMESPACE {
71 #endif
72
73 static UnivCharsetDesc::Range range = { 0, 128, 0 };
74 static CharsetInfo iso646Charset(UnivCharsetDesc(&range, 1));
75
76 #ifdef SP_HAVE_SOCKET
77
78 class HttpSocketStorageObject : public RewindStorageObject {
79 public:
80 HttpSocketStorageObject(SOCKET fd, Boolean mayRewind, const StringC &hostStr);
81 ~HttpSocketStorageObject();
82 Boolean open(const String<char> &path, Messenger &);
83 Boolean read(char *buf, size_t bufSize, Messenger &mgr, size_t &nread);
84 Boolean seekToStart(Messenger &);
85 static SOCKET openHttp(const String<char> &host,
86 unsigned short port,
87 const StringC &hostStr,
88 Messenger &mgr);
89 private:
90 HttpSocketStorageObject(const HttpSocketStorageObject &); // undefined
91 void operator=(const HttpSocketStorageObject &); // undefined
92 Boolean readHeader(Messenger &);
93 Boolean readLine(Messenger &mgr, String<char> &line, String<char> &leftOver);
94 static Boolean parseStatus(const char *&ptr, int &val);
95 StringC hostStr_;
96 String<char> path_;
97 Boolean eof_;
98 SOCKET fd_;
99 };
100
101 #ifdef WINSOCK
102
103 class WinsockMessageArg : public MessageArg {
104 public:
WinsockMessageArg(int n)105 WinsockMessageArg(int n) : n_(n) { }
copy() const106 MessageArg *copy() const { return new WinsockMessageArg(*this); }
107 void append(MessageBuilder &) const;
108 private:
109 int n_;
110 };
111
append(MessageBuilder & builder) const112 void WinsockMessageArg::append(MessageBuilder &builder) const
113 {
114 // I can't figure out how to get a string associated
115 // with this error number. FormatMessage() doesn't seem
116 // to work.
117 builder.appendFragment(URLStorageMessages::winsockErrorNumber);
118 builder.appendNumber(n_);
119 }
120
121 class WinsockIniter {
122 public:
123 WinsockIniter();
124 ~WinsockIniter();
125 Boolean init(Messenger &mgr);
126 private:
127 Boolean inited_;
128 Boolean initSuccess_;
129 };
130
131 static WinsockIniter winsockIniter;
132
WinsockIniter()133 WinsockIniter::WinsockIniter()
134 : inited_(0)
135 {
136 }
137
~WinsockIniter()138 WinsockIniter::~WinsockIniter()
139 {
140 if (inited_ && initSuccess_)
141 (void)WSACleanup();
142 }
143
init(Messenger & mgr)144 Boolean WinsockIniter::init(Messenger &mgr)
145 {
146 if (!inited_) {
147 inited_ = 1;
148 initSuccess_ = 0;
149 WORD version = MAKEWORD(1, 1);
150 WSADATA wsaData;
151 int err = WSAStartup(version, &wsaData);
152 if (err)
153 mgr.message(URLStorageMessages::winsockInitialize,
154 WinsockMessageArg(err));
155 else if (LOBYTE(wsaData.wVersion) != 1
156 || HIBYTE(wsaData.wVersion) != 1) {
157 mgr.message(URLStorageMessages::winsockVersion);
158 WSACleanup();
159 }
160 else
161 initSuccess_ = 1;
162 }
163 return initSuccess_;
164 }
165
166 #endif /* WINSOCK */
167
168 #endif /* SP_HAVE_SOCKET */
169
URLStorageManager(const char * type)170 URLStorageManager::URLStorageManager(const char *type)
171 : type_(type), IdStorageManager(&iso646Charset)
172 {
173 }
174
type() const175 const char *URLStorageManager::type() const
176 {
177 return type_;
178 }
179
guessIsId(const StringC & id,const CharsetInfo & charset) const180 Boolean URLStorageManager::guessIsId(const StringC &id,
181 const CharsetInfo &charset) const
182 {
183 if (id.size() < 8)
184 return 0;
185 size_t i = 0;
186 for (const char *s = "http://"; *s; s++, i++)
187 if (id[i] != charset.execToDesc(*s)
188 && (!islower(*s) || id[i] != charset.execToDesc(toupper(*s))))
189 return 0;
190 return 1;
191 }
192
makeStorageObject(const StringC & specId,const StringC & baseId,Boolean,Boolean mayRewind,Messenger & mgr,StringC & id)193 StorageObject *URLStorageManager::makeStorageObject(const StringC &specId,
194 const StringC &baseId,
195 Boolean,
196 Boolean mayRewind,
197 Messenger &mgr,
198 StringC &id)
199 {
200 #ifdef SP_HAVE_SOCKET
201 id = specId;
202 resolveRelative(baseId, id, 0);
203 if (id.size() < 5
204 || (id[0] != 'h' && id[0] != 'H')
205 || (id[1] != 't' && id[1] != 'T')
206 || (id[2] != 't' && id[2] != 'T')
207 || (id[3] != 'p' && id[3] != 'P')
208 || id[4] != ':') {
209 mgr.message(URLStorageMessages::onlyHTTP);
210 return 0;
211 }
212 if (id.size() < 7 || id[5] != '/' || id[6] != '/') {
213 mgr.message(URLStorageMessages::badRelative,
214 StringMessageArg(id));
215 return 0;
216 }
217 size_t i = 7;
218 String<char> host;
219 while (i < id.size()) {
220 if (id[i] == '/')
221 break;
222 if (id[i] == ':')
223 break;
224 host += char(id[i]);
225 i++;
226 }
227 if (host.size() == 0) {
228 mgr.message(URLStorageMessages::emptyHost,
229 StringMessageArg(id));
230 return 0;
231 }
232 unsigned short port;
233 if (i < id.size() && id[i] == ':') {
234 i++;
235 String<char> digits;
236 while (i < id.size() && id[i] != '/') {
237 digits += char(id[i]);
238 i++;
239 }
240 if (digits.size() == 0) {
241 mgr.message(URLStorageMessages::emptyPort,
242 StringMessageArg(id));
243 return 0;
244 }
245 digits += '\0';
246 char *endptr;
247 long n = strtol(digits.data(), &endptr, 10);
248 if (endptr != digits.data() + digits.size() - 1
249 || n < 0
250 || n > 65535L) {
251 mgr.message(URLStorageMessages::invalidPort,
252 StringMessageArg(id));
253 return 0;
254 }
255 port = (unsigned short)n;
256 }
257 else
258 port = 80;
259 String<char> path;
260 if (i < id.size()) {
261 while (i < id.size() && id[i] != '#') {
262 path += char(id[i]);
263 i++;
264 }
265 }
266 if (path.size() == 0)
267 path += '/';
268
269 StringC hostStr;
270 for (i = 0; i < host.size(); i++)
271 hostStr += host[i];
272 host += '\0';
273 SOCKET fd = HttpSocketStorageObject::openHttp(host, port, hostStr, mgr);
274 if (fd == INVALID_SOCKET)
275 return 0;
276 HttpSocketStorageObject *p
277 = new HttpSocketStorageObject(fd, mayRewind, hostStr);
278 if (!p->open(path, mgr)) {
279 delete p;
280 return 0;
281 }
282 return p;
283 #else /* not SP_HAVE_SOCKET */
284 ParentLocationMessenger(mgr).message(URLStorageMessages::notSupported);
285 return 0;
286 #endif /* not SP_HAVE_SOCKET */
287 }
288
resolveRelative(const StringC & baseId,StringC & id,Boolean) const289 Boolean URLStorageManager::resolveRelative(const StringC &baseId,
290 StringC &id,
291 Boolean) const
292 {
293 static const char schemeChars[] =
294 "abcdefghijklmnopqrstuvwxyz"
295 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
296 "01234567879"
297 "+-.";
298 size_t i;
299 // If it has a scheme, it is absolute.
300 for (i = 0; i < id.size(); i++) {
301 if (id[i] == ':') {
302 if (i == 0)
303 break;
304 else
305 return 1;
306 }
307 else if (!strchr(schemeChars, id[i]))
308 break;
309 }
310 for (i = 0; i < id.size(); i++) {
311 if (id[i] != '/')
312 break;
313 }
314 size_t slashCount = i;
315 if (slashCount > 0) {
316 Boolean foundSameSlash = 0;
317 size_t sameSlashPos;
318 for (size_t j = 0; j < baseId.size(); j++) {
319 size_t thisSlashCount = 0;
320 for (size_t k = j; k < baseId.size() && baseId[k] == '/'; k++)
321 thisSlashCount++;
322 if (thisSlashCount == slashCount && !foundSameSlash) {
323 foundSameSlash = 1;
324 sameSlashPos = j;
325 }
326 else if (thisSlashCount > slashCount)
327 foundSameSlash = 0;
328 }
329 if (foundSameSlash) {
330 StringC tem(baseId.data(), sameSlashPos);
331 tem += id;
332 tem.swap(id);
333 }
334 }
335 else {
336 size_t j;
337 for (j = baseId.size(); j > 0; j--)
338 if (baseId[j - 1] == '/')
339 break;
340 if (j > 0) {
341 StringC tem(baseId.data(), j);
342 tem += id;
343 tem.swap(id);
344 }
345 }
346 // FIXME remove xxx/../, and /.
347 return 1;
348 }
349
transformNeutral(StringC & str,Boolean fold,Messenger &) const350 Boolean URLStorageManager::transformNeutral(StringC &str, Boolean fold,
351 Messenger &) const
352 {
353 if (fold)
354 for (size_t i = 0; i < str.size(); i++) {
355 Char c = str[i];
356 if (c <= (unsigned char)-1)
357 str[i] = tolower(str[i]);
358 }
359 return 1;
360 }
361
362 #ifdef SP_HAVE_SOCKET
363
openHttp(const String<char> & host,unsigned short port,const StringC & hostStr,Messenger & mgr)364 SOCKET HttpSocketStorageObject::openHttp(const String<char> &host,
365 unsigned short port,
366 const StringC &hostStr,
367 Messenger &mgr)
368 {
369 #ifdef WINSOCK
370 if (!winsockIniter.init(mgr))
371 return INVALID_SOCKET;
372 #endif
373 struct sockaddr_in sock;
374 sock.sin_family = AF_INET;
375 sock.sin_port = htons(port);
376 if (isdigit((unsigned char)host[0])) {
377 unsigned long n = inet_addr(host.data());
378 if (n == (unsigned long)-1) {
379 ParentLocationMessenger(mgr).message(URLStorageMessages::invalidHostNumber,
380 StringMessageArg(hostStr));
381 return INVALID_SOCKET;
382 }
383 sock.sin_addr.s_addr = n;
384 }
385 else {
386 struct hostent *hp = gethostbyname(host.data());
387 if (!hp) {
388 const MessageType1 *message;
389 switch (h_errno) {
390 case HOST_NOT_FOUND:
391 message = &URLStorageMessages::hostNotFound;
392 break;
393 case TRY_AGAIN:
394 message = &URLStorageMessages::hostTryAgain;
395 break;
396 case NO_RECOVERY:
397 message = &URLStorageMessages::hostNoRecovery;
398 break;
399 case NO_DATA:
400 #ifdef NO_ADDRESS
401 #if NO_ADDRESS != NO_DATA
402 case NO_ADDRESS:
403 #endif
404 #endif
405 message = &URLStorageMessages::hostNoData;
406 break;
407 default:
408 #ifdef WINSOCK
409 ParentLocationMessenger(mgr).message(URLStorageMessages::hostOtherError,
410 StringMessageArg(hostStr),
411 WinsockMessageArg(h_errno));
412 return INVALID_SOCKET;
413 #else
414 message = &URLStorageMessages::hostUnknownError;
415 break;
416 #endif
417 }
418 ParentLocationMessenger(mgr).message(*message,
419 StringMessageArg(hostStr));
420 return INVALID_SOCKET;
421 }
422 memcpy(&sock.sin_addr, hp->h_addr, hp->h_length);
423 }
424 SOCKET fd = socket(PF_INET, SOCK_STREAM, 0);
425 if (fd == INVALID_SOCKET) {
426 ParentLocationMessenger(mgr).message(URLStorageMessages::cannotCreateSocket,
427 SocketMessageArg(errnosocket));
428 return INVALID_SOCKET;
429 }
430 if (connect(fd, (struct sockaddr *)&sock, sizeof(sock)) == SOCKET_ERROR) {
431 ParentLocationMessenger(mgr).message(URLStorageMessages::cannotConnect,
432 StringMessageArg(hostStr),
433 SocketMessageArg(errnosocket));
434 (void)closesocket(fd);
435 return INVALID_SOCKET;
436 }
437 return fd;
438 }
439
HttpSocketStorageObject(SOCKET fd,Boolean mayRewind,const StringC & hostStr)440 HttpSocketStorageObject::HttpSocketStorageObject(SOCKET fd,
441 Boolean mayRewind,
442 const StringC &hostStr)
443
444 : RewindStorageObject(mayRewind, 0), hostStr_(hostStr), fd_(fd), eof_(0)
445 {
446 }
447
~HttpSocketStorageObject()448 HttpSocketStorageObject::~HttpSocketStorageObject()
449 {
450 if (fd_ != INVALID_SOCKET)
451 (void)closesocket(fd_);
452 }
453
open(const String<char> & path,Messenger & mgr)454 Boolean HttpSocketStorageObject::open(const String<char> &path, Messenger &mgr)
455 {
456 path_ = path;
457 String<char> request;
458 request.append("GET ", 4);
459 request += path_;
460 request += ' ';
461 request.append("HTTP/1.0\r\n", 10);
462 request.append("Accept: */*\r\n", 13);
463 request.append("\r\n", 2);
464 // FIXME check length of write
465 if (writesocket(fd_, request.data(), request.size()) == SOCKET_ERROR) {
466 ParentLocationMessenger(mgr).message(URLStorageMessages::writeError,
467 StringMessageArg(hostStr_),
468 SocketMessageArg(errnosocket));
469 (void)closesocket(fd_);
470 fd_ = INVALID_SOCKET;
471 return 0;
472 }
473 if (!readHeader(mgr)) {
474 (void)closesocket(fd_);
475 fd_ = INVALID_SOCKET;
476 return 0;
477 }
478 return 1;
479 }
480
readHeader(Messenger & mgr)481 Boolean HttpSocketStorageObject::readHeader(Messenger &mgr)
482 {
483 String<char> buf;
484 String<char> leftOver;
485 if (!readLine(mgr, buf, leftOver))
486 return 0;
487 buf += '\0';
488 const char *ptr = &buf[0];
489 int val;
490 if (!parseStatus(ptr, val)) {
491 if (buf.size() > 0)
492 unread(buf.data(), buf.size() - 1);
493 return 1;
494 }
495 if (val < 200 || val >= 300) {
496 StringC reason;
497 while (*ptr && *ptr != '\n' && *ptr != '\r') {
498 reason += Char(*ptr);
499 ptr++;
500 }
501 StringC pathStr;
502 for (size_t i = 0; i < path_.size(); i++)
503 pathStr += path_[i];
504 ParentLocationMessenger(mgr).message(URLStorageMessages::getFailed,
505 StringMessageArg(hostStr_),
506 StringMessageArg(pathStr),
507 StringMessageArg(reason));
508 return 0;
509 }
510
511 for (;;) {
512 if (!readLine(mgr, buf, leftOver))
513 return 0;
514 if (buf.size() == 0 || buf[0] == '\r' || buf[0] == '\n')
515 break;
516 }
517 if (leftOver.size())
518 unread(leftOver.data(), leftOver.size());
519 return 1;
520 }
521
522 // Status line must start with: "HTTP/" 1*DIGIT "." 1*DIGIT SP 3DIGIT SP
523
parseStatus(const char * & ptr,int & val)524 Boolean HttpSocketStorageObject::parseStatus(const char *&ptr, int &val)
525 {
526 static const char ver[] = "HTTP/";
527 for (const char *v = ver; *v; v++, ptr++)
528 if (*v != *ptr)
529 return 0;
530 if (!isdigit((unsigned char)*ptr))
531 return 0;
532 do {
533 ++ptr;
534 } while (isdigit((unsigned char)*ptr));
535 if (*ptr != '.')
536 return 0;
537 ptr++;
538 if (!isdigit((unsigned char)*ptr))
539 return 0;
540 do {
541 ++ptr;
542 } while (isdigit((unsigned char)*ptr));
543 if (*ptr != ' ')
544 return 0;
545 ptr++;
546 val = 0;
547 for (int i = 0; i < 3; i++, ptr++) {
548 if (!isdigit((unsigned char)*ptr))
549 return 0;
550 val = val*10 + *ptr - '0';
551 }
552 if (*ptr != ' ')
553 return 0;
554 ptr++;
555 return 1;
556 }
557
558 // True will be returned for an empty line.
559
readLine(Messenger & mgr,String<char> & line,String<char> & leftOver)560 Boolean HttpSocketStorageObject::readLine(Messenger &mgr,
561 String<char> &line,
562 String<char> &leftOver)
563 {
564 line.resize(0);
565 Boolean hadCr = 0;
566 Boolean gotLine = 0;
567 size_t li;
568 for (li = 0; li < leftOver.size(); li++) {
569 if (leftOver[li] == '\r') {
570 if (hadCr) {
571 gotLine = 1;
572 break;
573 }
574 line += '\r';
575 hadCr = 1;
576 }
577 else if (leftOver[li] == '\n') {
578 line += '\n';
579 li++;
580 gotLine = 1;
581 break;
582 }
583 else if (hadCr) {
584 gotLine = 1;
585 break;
586 }
587 else
588 line += leftOver[li];
589 }
590 if (gotLine) {
591 for (size_t i = li; i < leftOver.size(); i++)
592 leftOver[i - li] = leftOver[i];
593 leftOver.resize(leftOver.size() - li);
594 return 1;
595 }
596 leftOver.resize(0);
597 if (eof_)
598 return 1;
599 for (;;) {
600 char c;
601 long n;
602 do {
603 n = readsocket(fd_, &c, 1);
604 } while (n < 0 && errnosocket == SOCKET_EINTR);
605 if (n == 0) {
606 (void)closesocket(fd_);
607 eof_ = 1;
608 return 1;
609 }
610 if (n < 0) {
611 ParentLocationMessenger(mgr).message(URLStorageMessages::readError,
612 StringMessageArg(hostStr_),
613 SocketMessageArg(errnosocket));
614 (void)closesocket(fd_);
615 fd_ = INVALID_SOCKET;
616 return 0;
617 }
618 switch (c) {
619 case '\r':
620 if (hadCr) {
621 leftOver += c;
622 return 1;
623 }
624 hadCr = 1;
625 line += c;
626 break;
627 case '\n':
628 line += c;
629 return 1;
630 default:
631 if (hadCr) {
632 leftOver += c;
633 return 1;
634 }
635 line += c;
636 break;
637 }
638 }
639 return 0; // not reached
640 }
641
read(char * buf,size_t bufSize,Messenger & mgr,size_t & nread)642 Boolean HttpSocketStorageObject::read(char *buf, size_t bufSize, Messenger &mgr,
643 size_t &nread)
644 {
645 if (readSaved(buf, bufSize, nread))
646 return 1;
647 if (fd_ == INVALID_SOCKET || eof_)
648 return 0;
649 long n;
650 do {
651 n = readsocket(fd_, buf, bufSize);
652 } while (n < 0 && errnosocket == SOCKET_EINTR);
653 if (n > 0) {
654 nread = size_t(n);
655 saveBytes(buf, nread);
656 return 1;
657 }
658 if (n < 0) {
659 ParentLocationMessenger(mgr).message(URLStorageMessages::readError,
660 StringMessageArg(hostStr_),
661 SocketMessageArg(errnosocket));
662 fd_ = INVALID_SOCKET;
663 }
664 else {
665 eof_ = 1;
666 if (closesocket(fd_) == SOCKET_ERROR)
667 ParentLocationMessenger(mgr).message(URLStorageMessages::closeError,
668 StringMessageArg(hostStr_),
669 SocketMessageArg(errnosocket));
670 fd_ = INVALID_SOCKET;
671 }
672 return 0;
673 }
674
seekToStart(Messenger &)675 Boolean HttpSocketStorageObject::seekToStart(Messenger &)
676 {
677 CANNOT_HAPPEN();
678 return 0;
679 }
680
681 #endif /* SP_HAVE_SOCKET */
682
683 #ifdef SP_NAMESPACE
684 }
685 #endif
686