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