2 * CDE - Common Desktop Environment
4 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
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)
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
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
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.
28 #pragma implementation
31 // FIXME This implementation won't work on an EBCDIC machine.
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
44 #include <sys/types.h>
45 #include <sys/socket.h>
47 #include <netinet/in.h>
48 #include <arpa/inet.h>
49 #ifdef SP_INCLUDE_UNISTD_H
53 #ifdef SP_INCLUDE_OSFCN_H
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"
68 #endif /* SP_HAVE_SOCKET */
70 #endif /* not WINSOCK */
72 #include "URLStorage.h"
73 #include "URLStorageMessages.h"
74 #include "RewindStorageObject.h"
75 #include "UnivCharsetDesc.h"
76 #include "MessageArg.h"
77 #include "MessageBuilder.h"
88 namespace SP_NAMESPACE {
91 static UnivCharsetDesc::Range range = { 0, 128, 0 };
92 static UnivCharsetDesc iso646Charset(&range, 1);
96 class HttpSocketStorageObject : public RewindStorageObject {
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,
105 const StringC &hostStr,
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);
120 class WinsockMessageArg : public MessageArg {
122 WinsockMessageArg(int n) : n_(n) { }
123 MessageArg *copy() const { return new WinsockMessageArg(*this); }
124 void append(MessageBuilder &) const;
129 void WinsockMessageArg::append(MessageBuilder &builder) const
131 // I can't figure out how to get a string associated
132 // with this error number. FormatMessage() doesn't seem
134 builder.appendFragment(URLStorageMessages::winsockErrorNumber);
135 builder.appendNumber(n_);
138 class WinsockIniter {
142 Boolean init(Messenger &mgr);
145 Boolean initSuccess_;
148 static WinsockIniter winsockIniter;
150 WinsockIniter::WinsockIniter()
155 WinsockIniter::~WinsockIniter()
157 if (inited_ && initSuccess_)
161 Boolean WinsockIniter::init(Messenger &mgr)
166 WORD version = MAKEWORD(1, 1);
168 int err = WSAStartup(version, &wsaData);
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);
185 #endif /* SP_HAVE_SOCKET */
187 URLStorageManager::URLStorageManager(const char *type)
188 : type_(type), IdStorageManager(iso646Charset)
192 const char *URLStorageManager::type() const
197 Boolean URLStorageManager::guessIsId(const StringC &id,
198 const CharsetInfo &charset) const
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))))
210 StorageObject *URLStorageManager::makeStorageObject(const StringC &specId,
211 const StringC &baseId,
217 #ifdef SP_HAVE_SOCKET
219 resolveRelative(baseId, id, 0);
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')
226 mgr.message(URLStorageMessages::onlyHTTP);
229 if (id.size() < 7 || id[5] != '/' || id[6] != '/') {
230 mgr.message(URLStorageMessages::badRelative,
231 StringMessageArg(id));
236 while (i < id.size()) {
244 if (host.size() == 0) {
245 mgr.message(URLStorageMessages::emptyHost,
246 StringMessageArg(id));
250 if (i < id.size() && id[i] == ':') {
253 while (i < id.size() && id[i] != '/') {
254 digits += char(id[i]);
257 if (digits.size() == 0) {
258 mgr.message(URLStorageMessages::emptyPort,
259 StringMessageArg(id));
264 long n = strtol(digits.data(), &endptr, 10);
265 if (endptr != digits.data() + digits.size() - 1
268 mgr.message(URLStorageMessages::invalidPort,
269 StringMessageArg(id));
272 port = (unsigned short)n;
278 while (i < id.size() && id[i] != '#') {
283 if (path.size() == 0)
287 for (i = 0; i < host.size(); i++)
290 SOCKET fd = HttpSocketStorageObject::openHttp(host, port, hostStr, mgr);
291 if (fd == INVALID_SOCKET)
293 HttpSocketStorageObject *p
294 = new HttpSocketStorageObject(fd, mayRewind, hostStr);
295 if (!p->open(path, mgr)) {
300 #else /* not SP_HAVE_SOCKET */
301 ParentLocationMessenger(mgr).message(URLStorageMessages::notSupported);
303 #endif /* not SP_HAVE_SOCKET */
306 Boolean URLStorageManager::resolveRelative(const StringC &baseId,
310 static const char schemeChars[] =
311 "abcdefghijklmnopqrstuvwxyz"
312 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
316 // If it has a scheme, it is absolute.
317 for (i = 0; i < id.size(); i++) {
324 else if (!strchr(schemeChars, id[i]))
327 for (i = 0; i < id.size(); i++) {
331 size_t slashCount = i;
332 if (slashCount > 0) {
333 Boolean foundSameSlash = 0;
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++)
339 if (thisSlashCount == slashCount && !foundSameSlash) {
343 else if (thisSlashCount > slashCount)
346 if (foundSameSlash) {
347 StringC tem(baseId.data(), sameSlashPos);
354 for (j = baseId.size(); j > 0; j--)
355 if (baseId[j - 1] == '/')
358 StringC tem(baseId.data(), j);
363 // FIXME remove xxx/../, and /.
367 Boolean URLStorageManager::transformNeutral(StringC &str, Boolean fold,
371 for (size_t i = 0; i < str.size(); i++) {
373 if (c <= (unsigned char)-1)
374 str[i] = tolower(str[i]);
379 #ifdef SP_HAVE_SOCKET
381 SOCKET HttpSocketStorageObject::openHttp(const String<char> &host,
383 const StringC &hostStr,
387 if (!winsockIniter.init(mgr))
388 return INVALID_SOCKET;
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;
400 sock.sin_addr.s_addr = n;
403 struct hostent *hp = gethostbyname(host.data());
405 const MessageType1 *message;
408 message = &URLStorageMessages::hostNotFound;
411 message = &URLStorageMessages::hostTryAgain;
414 message = &URLStorageMessages::hostNoRecovery;
418 #if NO_ADDRESS != NO_DATA
422 message = &URLStorageMessages::hostNoData;
426 ParentLocationMessenger(mgr).message(URLStorageMessages::hostOtherError,
427 StringMessageArg(hostStr),
428 WinsockMessageArg(h_errno));
429 return INVALID_SOCKET;
431 message = &URLStorageMessages::hostUnknownError;
435 ParentLocationMessenger(mgr).message(*message,
436 StringMessageArg(hostStr));
437 return INVALID_SOCKET;
439 memcpy(&sock.sin_addr, hp->h_addr, hp->h_length);
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;
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;
457 HttpSocketStorageObject::HttpSocketStorageObject(SOCKET fd,
459 const StringC &hostStr)
461 : RewindStorageObject(mayRewind, 0), hostStr_(hostStr), fd_(fd), eof_(0)
465 HttpSocketStorageObject::~HttpSocketStorageObject()
467 if (fd_ != INVALID_SOCKET)
468 (void)closesocket(fd_);
471 Boolean HttpSocketStorageObject::open(const String<char> &path, Messenger &mgr)
474 String<char> request;
475 request.append("GET ", 4);
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;
488 if (!readHeader(mgr)) {
489 (void)closesocket(fd_);
490 fd_ = INVALID_SOCKET;
496 Boolean HttpSocketStorageObject::readHeader(Messenger &mgr)
499 String<char> leftOver;
500 if (!readLine(mgr, buf, leftOver))
503 static const char ver[] = "HTTP/1.0";
505 for (i = 0; ver[i]; i++)
506 if (ver[i] != buf[i])
510 unread(buf.data(), buf.size() - 1);
513 const char *ptr = &buf[i];
514 while (isspace((unsigned char)*ptr))
517 while (isdigit((unsigned char)*ptr)) {
518 val = val*10 + *ptr - '0';
521 while (isspace((unsigned char)*ptr))
523 if (val < 200 || val >= 300) {
525 while (*ptr && *ptr != '\n' && *ptr != '\r') {
526 reason += Char(*ptr);
530 for (size_t i = 0; i < path_.size(); i++)
532 ParentLocationMessenger(mgr).message(URLStorageMessages::getFailed,
533 StringMessageArg(hostStr_),
534 StringMessageArg(pathStr),
535 StringMessageArg(reason));
540 if (!readLine(mgr, buf, leftOver))
542 if (buf.size() == 0 || buf[0] == '\r' || buf[0] == '\n')
546 unread(leftOver.data(), leftOver.size());
550 // True will be returned for an empty line.
552 Boolean HttpSocketStorageObject::readLine(Messenger &mgr,
554 String<char> &leftOver)
560 for (li = 0; li < leftOver.size(); li++) {
561 if (leftOver[li] == '\r') {
569 else if (leftOver[li] == '\n') {
580 line += leftOver[li];
583 for (size_t i = li; i < leftOver.size(); i++)
584 leftOver[i - li] = leftOver[i];
585 leftOver.resize(leftOver.size() - li);
595 n = readsocket(fd_, &c, 1);
596 } while (n < 0 && errnosocket == SOCKET_EINTR);
598 (void)closesocket(fd_);
603 ParentLocationMessenger(mgr).message(URLStorageMessages::readError,
604 StringMessageArg(hostStr_),
605 SocketMessageArg(errnosocket));
606 (void)closesocket(fd_);
607 fd_ = INVALID_SOCKET;
631 return 0; // not reached
634 Boolean HttpSocketStorageObject::read(char *buf, size_t bufSize, Messenger &mgr,
637 if (readSaved(buf, bufSize, nread))
639 if (fd_ == INVALID_SOCKET || eof_)
643 n = readsocket(fd_, buf, bufSize);
644 } while (n < 0 && errnosocket == SOCKET_EINTR);
647 saveBytes(buf, nread);
651 ParentLocationMessenger(mgr).message(URLStorageMessages::readError,
652 StringMessageArg(hostStr_),
653 SocketMessageArg(errnosocket));
654 fd_ = INVALID_SOCKET;
658 if (closesocket(fd_) == SOCKET_ERROR)
659 ParentLocationMessenger(mgr).message(URLStorageMessages::closeError,
660 StringMessageArg(hostStr_),
661 SocketMessageArg(errnosocket));
662 fd_ = INVALID_SOCKET;
667 Boolean HttpSocketStorageObject::seekToStart(Messenger &)
673 #endif /* SP_HAVE_SOCKET */