xref: /onnv-gate/usr/src/cmd/man/src/util/nsgmls.src/lib/URLStorage.cxx (revision 0:68f95e015346)
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