Blame lib/URLStorage.cxx

Packit 8a864e
// Copyright (c) 1995 James Clark
Packit 8a864e
// See the file COPYING for copying permission.
Packit 8a864e
Packit 8a864e
#ifdef __GNUG__
Packit 8a864e
#pragma implementation
Packit 8a864e
#endif
Packit 8a864e
Packit 8a864e
// FIXME This implementation won't work on an EBCDIC machine.
Packit 8a864e
Packit 8a864e
#include "splib.h"
Packit 8a864e
#ifdef WINSOCK
Packit 8a864e
#include <winsock.h>
Packit 8a864e
#define readsocket(s, p, n) ::recv(s, p, n, 0)
Packit 8a864e
#define writesocket(s, p, n) ::send(s, p, n, 0)
Packit 8a864e
#define errnosocket (WSAGetLastError())
Packit 8a864e
#define SocketMessageArg(n) WinsockMessageArg(n)
Packit 8a864e
#define SOCKET_EINTR (WSAEINTR)
Packit 8a864e
#define SP_HAVE_SOCKET
Packit 8a864e
#else
Packit 8a864e
#ifdef SP_HAVE_SOCKET
Packit 8a864e
#include <stdio.h>
Packit 8a864e
#include <sys/types.h>
Packit 8a864e
#include <sys/socket.h>
Packit 8a864e
#include <netdb.h>
Packit 8a864e
#include <netinet/in.h>
Packit 8a864e
#include <arpa/inet.h>
Packit 8a864e
#ifdef SP_INCLUDE_UNISTD_H
Packit 8a864e
#include <unistd.h>
Packit 8a864e
#endif
Packit 8a864e
Packit 8a864e
#ifdef SP_INCLUDE_OSFCN_H
Packit 8a864e
#include <osfcn.h>
Packit 8a864e
#endif
Packit 8a864e
Packit 8a864e
#ifdef SP_DECLARE_H_ERRNO
Packit 8a864e
extern int h_errno;
Packit 8a864e
#endif
Packit 8a864e
Packit 8a864e
typedef int SOCKET;
Packit 8a864e
#define SOCKET_ERROR (-1)
Packit 8a864e
#define INVALID_SOCKET (-1)
Packit 8a864e
#define SOCKET_EINTR (EINTR)
Packit 8a864e
#define closesocket(s) close(s)
Packit 8a864e
#define writesocket(fd, p, n) ::write(fd, p, n)
Packit 8a864e
#define readsocket(s, p, n) ::read(s, p, n)
Packit 8a864e
#define errnosocket (errno)
Packit 8a864e
#define SocketMessageArg(n) ErrnoMessageArg(n)
Packit 8a864e
#include "ErrnoMessageArg.h"
Packit 8a864e
Packit 8a864e
#endif /* SP_HAVE_SOCKET */
Packit 8a864e
Packit 8a864e
#endif /* not WINSOCK */
Packit 8a864e
Packit 8a864e
#include "URLStorage.h"
Packit 8a864e
#include "URLStorageMessages.h"
Packit 8a864e
#include "RewindStorageObject.h"
Packit 8a864e
#include "UnivCharsetDesc.h"
Packit 8a864e
#include "MessageArg.h"
Packit 8a864e
#include "MessageBuilder.h"
Packit 8a864e
#include "macros.h"
Packit 8a864e
Packit 8a864e
#include <stdlib.h>
Packit 8a864e
#include <string.h>
Packit 8a864e
#include <errno.h>
Packit 8a864e
#include <stddef.h>
Packit 8a864e
#include <ctype.h>
Packit 8a864e
#include <stdio.h>
Packit 8a864e
Packit 8a864e
Packit 8a864e
#ifdef SP_NAMESPACE
Packit 8a864e
namespace SP_NAMESPACE {
Packit 8a864e
#endif
Packit 8a864e
Packit 8a864e
static UnivCharsetDesc::Range range = { 0, 128, 0 };
Packit 8a864e
static CharsetInfo iso646Charset(UnivCharsetDesc(&range, 1));
Packit 8a864e
Packit 8a864e
#ifdef SP_HAVE_SOCKET
Packit 8a864e
Packit 8a864e
typedef enum {
Packit 8a864e
	HTTP_OK ,
Packit 8a864e
	HTTP_REDIRECT ,
Packit 8a864e
	HTTP_ERROR
Packit 8a864e
} HTTP_RESPONSE_TYPE ;
Packit 8a864e
Packit 8a864e
class HttpSocketStorageObject : public RewindStorageObject {
Packit 8a864e
public:
Packit 8a864e
  HttpSocketStorageObject(SOCKET fd, Boolean mayRewind, const StringC &hostStr);
Packit 8a864e
  ~HttpSocketStorageObject();
Packit 8a864e
  HTTP_RESPONSE_TYPE open(const String<char> &host,
Packit 8a864e
               unsigned short port,
Packit 8a864e
               const String<char> &path,
Packit 8a864e
               Messenger &,
Packit 8a864e
		char[]);
Packit 8a864e
  Boolean read(char *buf, size_t bufSize, Messenger &mgr, size_t &nread);
Packit 8a864e
  Boolean seekToStart(Messenger &);
Packit 8a864e
  static SOCKET openHttp(const String<char> &host,
Packit 8a864e
			 unsigned short port,
Packit 8a864e
			 const StringC &hostStr,
Packit 8a864e
			 Messenger &mgr);
Packit 8a864e
private:
Packit 8a864e
  HttpSocketStorageObject(const HttpSocketStorageObject &); // undefined
Packit 8a864e
  void operator=(const HttpSocketStorageObject &); // undefined
Packit 8a864e
  HTTP_RESPONSE_TYPE readHeader(Messenger &, char[]);
Packit 8a864e
  Boolean readLine(Messenger &mgr, String<char> &line, String<char> &leftOver);
Packit 8a864e
  static Boolean parseStatus(const char *&ptr, int &val;;
Packit 8a864e
  StringC hostStr_;
Packit 8a864e
  String<char> path_;
Packit 8a864e
  Boolean eof_;
Packit 8a864e
  SOCKET fd_;
Packit 8a864e
};
Packit 8a864e
Packit 8a864e
#ifdef WINSOCK
Packit 8a864e
Packit 8a864e
class WinsockMessageArg : public MessageArg {
Packit 8a864e
public:
Packit 8a864e
  WinsockMessageArg(int n) : n_(n) { }
Packit 8a864e
  MessageArg *copy() const { return new WinsockMessageArg(*this); }
Packit 8a864e
  void append(MessageBuilder &) const;
Packit 8a864e
private:
Packit 8a864e
  int n_;
Packit 8a864e
};
Packit 8a864e
Packit 8a864e
void WinsockMessageArg::append(MessageBuilder &builder) const
Packit 8a864e
{
Packit 8a864e
  // I can't figure out how to get a string associated
Packit 8a864e
  // with this error number.  FormatMessage() doesn't seem
Packit 8a864e
  // to work.
Packit 8a864e
  builder.appendFragment(URLStorageMessages::winsockErrorNumber);
Packit 8a864e
  builder.appendNumber(n_);
Packit 8a864e
}
Packit 8a864e
Packit 8a864e
class WinsockIniter {
Packit 8a864e
public:
Packit 8a864e
  WinsockIniter();
Packit 8a864e
  ~WinsockIniter();
Packit 8a864e
  Boolean init(Messenger &mgr);
Packit 8a864e
private:
Packit 8a864e
  Boolean inited_;
Packit 8a864e
  Boolean initSuccess_;
Packit 8a864e
};
Packit 8a864e
Packit 8a864e
static WinsockIniter winsockIniter;
Packit 8a864e
Packit 8a864e
WinsockIniter::WinsockIniter()
Packit 8a864e
: inited_(0)
Packit 8a864e
{
Packit 8a864e
}
Packit 8a864e
Packit 8a864e
WinsockIniter::~WinsockIniter()
Packit 8a864e
{
Packit 8a864e
  if (inited_ && initSuccess_)
Packit 8a864e
    (void)WSACleanup();
Packit 8a864e
}
Packit 8a864e
Packit 8a864e
Boolean WinsockIniter::init(Messenger &mgr)
Packit 8a864e
{
Packit 8a864e
  if (!inited_) {
Packit 8a864e
    inited_ = 1;
Packit 8a864e
    initSuccess_ = 0;
Packit 8a864e
    WORD version = MAKEWORD(1, 1);
Packit 8a864e
    WSADATA wsaData;
Packit 8a864e
    int err = WSAStartup(version, &wsaData);
Packit 8a864e
    if (err)
Packit 8a864e
      mgr.message(URLStorageMessages::winsockInitialize,
Packit 8a864e
		  WinsockMessageArg(err));
Packit 8a864e
    else if (LOBYTE(wsaData.wVersion) != 1
Packit 8a864e
	     || HIBYTE(wsaData.wVersion) != 1) {
Packit 8a864e
      mgr.message(URLStorageMessages::winsockVersion);
Packit 8a864e
      WSACleanup();
Packit 8a864e
    }
Packit 8a864e
    else
Packit 8a864e
      initSuccess_ = 1;
Packit 8a864e
  }
Packit 8a864e
  return initSuccess_;
Packit 8a864e
}
Packit 8a864e
Packit 8a864e
#endif /* WINSOCK */
Packit 8a864e
Packit 8a864e
#endif /* SP_HAVE_SOCKET */
Packit 8a864e
Packit 8a864e
URLStorageManager::URLStorageManager(const char *type)
Packit 8a864e
: type_(type), IdStorageManager(&iso646Charset)
Packit 8a864e
{
Packit 8a864e
}
Packit 8a864e
Packit 8a864e
const char *URLStorageManager::type() const
Packit 8a864e
{
Packit 8a864e
  return type_;
Packit 8a864e
}
Packit 8a864e
Packit 8a864e
Boolean URLStorageManager::guessIsId(const StringC &id,
Packit 8a864e
				     const CharsetInfo &charset) const
Packit 8a864e
{
Packit 8a864e
  if (id.size() < 8)
Packit 8a864e
    return 0;
Packit 8a864e
  size_t i = 0;
Packit 8a864e
  for (const char *s = "http://"; *s; s++, i++)
Packit 8a864e
    if (id[i] != charset.execToDesc(*s)
Packit 8a864e
	&& (!islower(*s) || id[i] != charset.execToDesc(toupper(*s))))
Packit 8a864e
      return 0;
Packit 8a864e
  return 1;
Packit 8a864e
}
Packit 8a864e
Packit 8a864e
inline int strdiff(const char* str, char* buf) {
Packit 8a864e
  if ( ! *buf )
Packit 8a864e
    return 1 ;
Packit 8a864e
  if ( strlen(buf) <= strlen (str) )
Packit 8a864e
    return 2 ;
Packit 8a864e
//  if ( strncasecmp(buf, str, strlen(str)) )
Packit 8a864e
  for ( int i = 0; i < strlen(str); ++i)
Packit 8a864e
    if ( tolower(buf[i]) != tolower(str[i]) )
Packit 8a864e
      return 3 ;
Packit 8a864e
  return 0 ;
Packit 8a864e
}
Packit 8a864e
Packit 8a864e
StorageObject *URLStorageManager::makeStorageObject(const StringC &specId,
Packit 8a864e
						    const StringC &baseId,
Packit 8a864e
						    Boolean,
Packit 8a864e
						    Boolean mayRewind,
Packit 8a864e
						    Messenger &mgr,
Packit 8a864e
						    StringC &id)
Packit 8a864e
{
Packit 8a864e
#ifdef SP_HAVE_SOCKET
Packit 8a864e
  id = specId;
Packit 8a864e
  resolveRelative(baseId, id, 0);
Packit 8a864e
  if (id.size() < 5
Packit 8a864e
      || (id[0] != 'h' && id[0] != 'H')
Packit 8a864e
      || (id[1] != 't' && id[1] != 'T')
Packit 8a864e
      || (id[2] != 't' && id[2] != 'T')
Packit 8a864e
      || (id[3] != 'p' && id[3] != 'P')
Packit 8a864e
      || id[4] != ':') {
Packit 8a864e
    mgr.message(URLStorageMessages::onlyHTTP);
Packit 8a864e
    return 0;
Packit 8a864e
  }
Packit 8a864e
  if (id.size() < 7 || id[5] != '/' || id[6] != '/') {
Packit 8a864e
    mgr.message(URLStorageMessages::badRelative,
Packit 8a864e
		StringMessageArg(id));
Packit 8a864e
    return 0;
Packit 8a864e
  }
Packit 8a864e
  size_t i = 7;
Packit 8a864e
  String<char> host;
Packit 8a864e
  while (i < id.size()) {
Packit 8a864e
    if (id[i] == '/')
Packit 8a864e
      break;
Packit 8a864e
    if (id[i] == ':')
Packit 8a864e
      break;
Packit 8a864e
    host += char(id[i]);
Packit 8a864e
    i++;
Packit 8a864e
  }
Packit 8a864e
  if (host.size() == 0) {
Packit 8a864e
    mgr.message(URLStorageMessages::emptyHost,
Packit 8a864e
		StringMessageArg(id));
Packit 8a864e
    return 0;
Packit 8a864e
  }
Packit 8a864e
  unsigned short port;
Packit 8a864e
  if (i < id.size() && id[i] == ':') {
Packit 8a864e
    i++;
Packit 8a864e
    String<char> digits;
Packit 8a864e
    while (i < id.size() && id[i] != '/') {
Packit 8a864e
      digits += char(id[i]);
Packit 8a864e
      i++;
Packit 8a864e
    }
Packit 8a864e
    if (digits.size() == 0) {
Packit 8a864e
      mgr.message(URLStorageMessages::emptyPort,
Packit 8a864e
		  StringMessageArg(id));
Packit 8a864e
      return 0;
Packit 8a864e
    }
Packit 8a864e
    digits += '\0';
Packit 8a864e
    char *endptr;
Packit 8a864e
    long n = strtol(digits.data(), &endptr, 10);
Packit 8a864e
    if (endptr != digits.data() + digits.size() - 1
Packit 8a864e
	|| n < 0
Packit 8a864e
	|| n > 65535L) {
Packit 8a864e
      mgr.message(URLStorageMessages::invalidPort,
Packit 8a864e
		  StringMessageArg(id));
Packit 8a864e
      return 0;
Packit 8a864e
    }
Packit 8a864e
    port = (unsigned short)n;
Packit 8a864e
  }
Packit 8a864e
  else
Packit 8a864e
    port = 80;
Packit 8a864e
  String<char> path;
Packit 8a864e
  if (i < id.size()) {
Packit 8a864e
    while (i < id.size() && id[i] != '#') {
Packit 8a864e
      path += char(id[i]);
Packit 8a864e
      i++;
Packit 8a864e
    }
Packit 8a864e
  }
Packit 8a864e
Packit 8a864e
  for ( int tries=0; tries<20; ++tries ) {
Packit 8a864e
    if (path.size() == 0)
Packit 8a864e
      path += '/';
Packit 8a864e
    StringC hostStr;
Packit 8a864e
    for (i = 0; i < host.size(); i++)
Packit 8a864e
      hostStr += host[i];
Packit 8a864e
Packit 8a864e
// Support HTTP redirect but limit number against an infinite loop
Packit 8a864e
Packit 8a864e
    SOCKET fd = HttpSocketStorageObject::openHttp(host, port, hostStr, mgr);
Packit 8a864e
    if (fd == INVALID_SOCKET)
Packit 8a864e
      return 0;
Packit 8a864e
    HttpSocketStorageObject *p
Packit 8a864e
      = new HttpSocketStorageObject(fd, mayRewind, hostStr);
Packit 8a864e
    char locbuf[256] ;
Packit 8a864e
    static String<char> nullStringC("", 0) ;
Packit 8a864e
    char* line ;
Packit 8a864e
    switch (p->open(host, port, path, mgr, locbuf)) {
Packit 8a864e
      case HTTP_OK:
Packit 8a864e
        return p ;
Packit 8a864e
      case HTTP_REDIRECT:
Packit 8a864e
//        (void)closesocket(fd);
Packit 8a864e
	delete p ;
Packit 8a864e
Packit 8a864e
	// reassign host, port and path
Packit 8a864e
	// and go round the loop again
Packit 8a864e
	line = locbuf ;
Packit 8a864e
Packit 8a864e
	if ( strdiff ("location:", line) )
Packit 8a864e
	  return 0 ;
Packit 8a864e
     	line += strlen("location:") ;
Packit 8a864e
Packit 8a864e
Packit 8a864e
      while ( isspace(*line) )
Packit 8a864e
	++line ;
Packit 8a864e
Packit 8a864e
	// call ourself recursively with our new URL
Packit 8a864e
Packit 8a864e
	// construct message - there must be a better way even without
Packit 8a864e
	// hacking on other source files
Packit 8a864e
	{ 
Packit 8a864e
          StringC sline ;
Packit 8a864e
	  for ( char* x = line; *x; ++x)
Packit 8a864e
	    sline +=  (Char)(*x) ;
Packit 8a864e
	  mgr.message(URLStorageMessages::Redirect, StringMessageArg(sline)) ;
Packit 8a864e
	}
Packit 8a864e
Packit 8a864e
	host = nullStringC ;
Packit 8a864e
	path = nullStringC ;
Packit 8a864e
	port = 0 ;
Packit 8a864e
Packit 8a864e
	if ( strdiff ("http://", line) )
Packit 8a864e
	  return 0 ;
Packit 8a864e
	line += strlen("http://") ;
Packit 8a864e
Packit 8a864e
	while ( *line != ':' && *line != '/' )
Packit 8a864e
	  host += *line++ ;
Packit 8a864e
Packit 8a864e
	if ( *line == ':' )
Packit 8a864e
          while ( isdigit(*++line) )
Packit 8a864e
	    port = 10 * port + ( *line - '0' ) ;
Packit 8a864e
	else
Packit 8a864e
	  port = 80 ;
Packit 8a864e
Packit 8a864e
	while ( *line && ! isspace(*line) )
Packit 8a864e
          path += *line++ ;
Packit 8a864e
Packit 8a864e
	break ;
Packit 8a864e
Packit 8a864e
      case HTTP_ERROR:
Packit 8a864e
        delete p;
Packit 8a864e
        return 0;
Packit 8a864e
    }
Packit 8a864e
  }
Packit 8a864e
  return 0 ;	// we were in an infinite redirection loop
Packit 8a864e
Packit 8a864e
#else /* not SP_HAVE_SOCKET */
Packit 8a864e
  ParentLocationMessenger(mgr).message(URLStorageMessages::notSupported);
Packit 8a864e
  return 0;
Packit 8a864e
#endif /* not SP_HAVE_SOCKET */
Packit 8a864e
}
Packit 8a864e
Packit 8a864e
Boolean URLStorageManager::resolveRelative(const StringC &baseId,
Packit 8a864e
					   StringC &id,
Packit 8a864e
					   Boolean) const
Packit 8a864e
{
Packit 8a864e
  static const char schemeChars[] = 
Packit 8a864e
    "abcdefghijklmnopqrstuvwxyz"
Packit 8a864e
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
Packit 8a864e
    "01234567879"
Packit 8a864e
    "+-.";
Packit 8a864e
  size_t i;
Packit 8a864e
  // If it has a scheme, it is absolute.
Packit 8a864e
  for (i = 0; i < id.size(); i++) {
Packit 8a864e
    if (id[i] == ':') {
Packit 8a864e
      if (i == 0)
Packit 8a864e
	break;
Packit 8a864e
      else
Packit 8a864e
	return 1;
Packit 8a864e
    }
Packit 8a864e
    else if (!strchr(schemeChars, id[i]))
Packit 8a864e
      break;
Packit 8a864e
  }
Packit 8a864e
  for (i = 0; i < id.size(); i++) {
Packit 8a864e
    if (id[i] != '/')
Packit 8a864e
      break;
Packit 8a864e
  }
Packit 8a864e
  size_t slashCount = i;
Packit 8a864e
  if (slashCount > 0) {
Packit 8a864e
    Boolean foundSameSlash = 0;
Packit 8a864e
    size_t sameSlashPos;
Packit 8a864e
    for (size_t j = 0; j < baseId.size(); j++) {
Packit 8a864e
      size_t thisSlashCount = 0;
Packit 8a864e
      for (size_t k = j; k < baseId.size() && baseId[k] == '/'; k++)
Packit 8a864e
	thisSlashCount++;
Packit 8a864e
      if (thisSlashCount == slashCount && !foundSameSlash) {
Packit 8a864e
	foundSameSlash = 1;
Packit 8a864e
	sameSlashPos = j;
Packit 8a864e
      }
Packit 8a864e
      else if (thisSlashCount > slashCount)
Packit 8a864e
	foundSameSlash = 0;
Packit 8a864e
    }
Packit 8a864e
    if (foundSameSlash) {
Packit 8a864e
      StringC tem(baseId.data(), sameSlashPos);
Packit 8a864e
      tem += id;
Packit 8a864e
      tem.swap(id);
Packit 8a864e
    }
Packit 8a864e
  }
Packit 8a864e
  else {
Packit 8a864e
    size_t j;
Packit 8a864e
    for (j = baseId.size(); j > 0; j--)
Packit 8a864e
      if (baseId[j - 1] == '/')
Packit 8a864e
	break;
Packit 8a864e
    if (j > 0) {
Packit 8a864e
      StringC tem(baseId.data(), j);
Packit 8a864e
      tem += id;
Packit 8a864e
      tem.swap(id);
Packit 8a864e
    }
Packit 8a864e
  }
Packit 8a864e
  // FIXME remove xxx/../, and /.
Packit 8a864e
  return 1;
Packit 8a864e
}
Packit 8a864e
Packit 8a864e
Boolean URLStorageManager::transformNeutral(StringC &str, Boolean fold,
Packit 8a864e
					    Messenger &) const
Packit 8a864e
{
Packit 8a864e
  if (fold)
Packit 8a864e
    for (size_t i = 0; i < str.size(); i++) {
Packit 8a864e
      Char c = str[i];
Packit 8a864e
      if (c <= (unsigned char)-1)
Packit 8a864e
	str[i] = tolower(str[i]);
Packit 8a864e
    }
Packit 8a864e
  return 1;
Packit 8a864e
}
Packit 8a864e
Packit 8a864e
#ifdef SP_HAVE_SOCKET
Packit 8a864e
Packit 8a864e
SOCKET HttpSocketStorageObject::openHttp(const String<char> &theHost,
Packit 8a864e
					unsigned short port,
Packit 8a864e
					const StringC &hostStr,
Packit 8a864e
					Messenger &mgr)
Packit 8a864e
{
Packit 8a864e
  String<char> host = theHost;
Packit 8a864e
  host += '\0';
Packit 8a864e
Packit 8a864e
#ifdef WINSOCK
Packit 8a864e
  if (!winsockIniter.init(mgr))
Packit 8a864e
    return INVALID_SOCKET;
Packit 8a864e
#endif
Packit 8a864e
  struct sockaddr_in sock;
Packit 8a864e
  sock.sin_family = AF_INET;
Packit 8a864e
  sock.sin_port = htons(port);
Packit 8a864e
  if (isdigit((unsigned char)host[0])) {
Packit 8a864e
    unsigned long n = inet_addr(host.data());
Packit 8a864e
    if (n == (unsigned long)-1) {
Packit 8a864e
      ParentLocationMessenger(mgr).message(URLStorageMessages::invalidHostNumber,
Packit 8a864e
					   StringMessageArg(hostStr));
Packit 8a864e
      return INVALID_SOCKET;
Packit 8a864e
    }
Packit 8a864e
    sock.sin_addr.s_addr = n;
Packit 8a864e
  }
Packit 8a864e
  else {
Packit 8a864e
    struct hostent *hp = gethostbyname(host.data());
Packit 8a864e
    if (!hp) {
Packit 8a864e
      const MessageType1 *message;
Packit 8a864e
      switch (h_errno) {
Packit 8a864e
      case HOST_NOT_FOUND:
Packit 8a864e
	message = &URLStorageMessages::hostNotFound;
Packit 8a864e
	break;
Packit 8a864e
      case TRY_AGAIN:
Packit 8a864e
	message = &URLStorageMessages::hostTryAgain;
Packit 8a864e
	break;
Packit 8a864e
      case NO_RECOVERY:
Packit 8a864e
	message = &URLStorageMessages::hostNoRecovery;
Packit 8a864e
	break;
Packit 8a864e
      case NO_DATA:
Packit 8a864e
#ifdef NO_ADDRESS
Packit 8a864e
#if NO_ADDRESS != NO_DATA
Packit 8a864e
      case NO_ADDRESS:
Packit 8a864e
#endif
Packit 8a864e
#endif
Packit 8a864e
	message = &URLStorageMessages::hostNoData;
Packit 8a864e
	break;
Packit 8a864e
      default:
Packit 8a864e
#ifdef WINSOCK
Packit 8a864e
	ParentLocationMessenger(mgr).message(URLStorageMessages::hostOtherError,
Packit 8a864e
					     StringMessageArg(hostStr),
Packit 8a864e
					     WinsockMessageArg(h_errno));
Packit 8a864e
	return INVALID_SOCKET;
Packit 8a864e
#else
Packit 8a864e
	message = &URLStorageMessages::hostUnknownError;
Packit 8a864e
	break;
Packit 8a864e
#endif
Packit 8a864e
      }
Packit 8a864e
      ParentLocationMessenger(mgr).message(*message,
Packit 8a864e
					   StringMessageArg(hostStr));
Packit 8a864e
      return INVALID_SOCKET;
Packit 8a864e
    }
Packit 8a864e
    memcpy(&sock.sin_addr, hp->h_addr, hp->h_length);
Packit 8a864e
  }
Packit 8a864e
  SOCKET fd = socket(PF_INET, SOCK_STREAM, 0);
Packit 8a864e
  if (fd == INVALID_SOCKET) {
Packit 8a864e
    ParentLocationMessenger(mgr).message(URLStorageMessages::cannotCreateSocket,
Packit 8a864e
					 SocketMessageArg(errnosocket));
Packit 8a864e
    return INVALID_SOCKET;
Packit 8a864e
  }
Packit 8a864e
  if (connect(fd, (struct sockaddr *)&sock, sizeof(sock)) == SOCKET_ERROR) {
Packit 8a864e
    ParentLocationMessenger(mgr).message(URLStorageMessages::cannotConnect,
Packit 8a864e
					 StringMessageArg(hostStr),
Packit 8a864e
					 SocketMessageArg(errnosocket));
Packit 8a864e
    (void)closesocket(fd);
Packit 8a864e
    return INVALID_SOCKET;
Packit 8a864e
  }
Packit 8a864e
  return fd;
Packit 8a864e
}
Packit 8a864e
Packit 8a864e
HttpSocketStorageObject::HttpSocketStorageObject(SOCKET fd,
Packit 8a864e
					       Boolean mayRewind,
Packit 8a864e
					       const StringC &hostStr)
Packit 8a864e
Packit 8a864e
: RewindStorageObject(mayRewind, 0), hostStr_(hostStr), fd_(fd), eof_(0)
Packit 8a864e
{
Packit 8a864e
}
Packit 8a864e
Packit 8a864e
HttpSocketStorageObject::~HttpSocketStorageObject()
Packit 8a864e
{
Packit 8a864e
  if (fd_ != INVALID_SOCKET)
Packit 8a864e
    (void)closesocket(fd_);
Packit 8a864e
}
Packit 8a864e
Packit 8a864e
HTTP_RESPONSE_TYPE HttpSocketStorageObject::open(const String<char> &host,
Packit 8a864e
				     unsigned short port,
Packit 8a864e
	 	 		     const String<char> &path,
Packit 8a864e
				     Messenger &mgr,
Packit 8a864e
					char locbuf[])
Packit 8a864e
{
Packit 8a864e
  path_ = path;
Packit 8a864e
  String<char> request;
Packit 8a864e
  request.append("GET ", 4);
Packit 8a864e
  request += path_;
Packit 8a864e
  request += ' ';
Packit 8a864e
  request.append("HTTP/1.0\r\n", 10);
Packit 8a864e
  request.append("Host: ", 6);
Packit 8a864e
  if (!isdigit((unsigned char)host[0])) {
Packit 8a864e
    request += host;
Packit 8a864e
    if (port != 80) {
Packit 8a864e
      char portstr[sizeof(unsigned short)*3 + 1];
Packit 8a864e
      sprintf(portstr, "%u", port);
Packit 8a864e
      request.append(":", 1);
Packit 8a864e
      request.append(portstr, strlen(portstr));
Packit 8a864e
    } 
Packit 8a864e
  }
Packit 8a864e
  request.append("\r\n", 2);
Packit 8a864e
  char* http_ua = getenv("SP_HTTP_USER_AGENT") ;
Packit 8a864e
  if ( ! http_ua )
Packit 8a864e
    http_ua = "libosp 1.5" ;
Packit 8a864e
  request.append("User-Agent: ", 12) ;
Packit 8a864e
  request.append(http_ua, strlen(http_ua)) ;
Packit 8a864e
  request.append("\r\n", 2);
Packit 8a864e
  const char* http_accept = getenv("SP_HTTP_ACCEPT") ;
Packit 8a864e
  if ( http_accept ) {
Packit 8a864e
    request.append("Accept: ", 8) ;
Packit 8a864e
    request.append(http_accept, strlen(http_accept)) ;
Packit 8a864e
    request.append("\r\n", 2);
Packit 8a864e
  }
Packit 8a864e
  request.append("\r\n", 2);
Packit 8a864e
Packit 8a864e
  // FIXME check length of write
Packit 8a864e
  if (writesocket(fd_, request.data(), request.size()) == SOCKET_ERROR) {
Packit 8a864e
    ParentLocationMessenger(mgr).message(URLStorageMessages::writeError,
Packit 8a864e
					 StringMessageArg(hostStr_),
Packit 8a864e
					 SocketMessageArg(errnosocket));
Packit 8a864e
    (void)closesocket(fd_);
Packit 8a864e
    fd_ = INVALID_SOCKET;
Packit 8a864e
    return HTTP_ERROR ;
Packit 8a864e
  }
Packit 8a864e
  switch ( readHeader(mgr, locbuf) ) {
Packit 8a864e
    case HTTP_OK:
Packit 8a864e
	return HTTP_OK ;
Packit 8a864e
    case HTTP_REDIRECT:
Packit 8a864e
      (void)closesocket(fd_);
Packit 8a864e
	return HTTP_REDIRECT ;
Packit 8a864e
    case HTTP_ERROR:
Packit 8a864e
      (void)closesocket(fd_);
Packit 8a864e
      fd_ = INVALID_SOCKET;
Packit 8a864e
      return HTTP_ERROR ;
Packit 8a864e
  }
Packit 8a864e
  return HTTP_ERROR ;
Packit 8a864e
}
Packit 8a864e
Packit 8a864e
HTTP_RESPONSE_TYPE HttpSocketStorageObject::readHeader(Messenger &mgr,
Packit 8a864e
							char locbuf[])
Packit 8a864e
{
Packit 8a864e
  String<char> buf ;
Packit 8a864e
  String<char> leftOver;
Packit 8a864e
  if (!readLine(mgr, buf, leftOver))
Packit 8a864e
    return HTTP_ERROR;
Packit 8a864e
  buf += '\0';
Packit 8a864e
  const char *ptr = &buf[0];
Packit 8a864e
  int val;
Packit 8a864e
  if (!parseStatus(ptr, val)) {
Packit 8a864e
    if (buf.size() > 0)
Packit 8a864e
      unread(buf.data(), buf.size() - 1);
Packit 8a864e
    return HTTP_OK;
Packit 8a864e
  }
Packit 8a864e
  if (val < 200 || val >= 400) {
Packit 8a864e
    StringC reason;
Packit 8a864e
    while (*ptr && *ptr != '\n' && *ptr != '\r') {
Packit 8a864e
      reason += Char(*ptr);
Packit 8a864e
      ptr++;
Packit 8a864e
    }
Packit 8a864e
    StringC pathStr;
Packit 8a864e
    for (size_t i = 0; i < path_.size(); i++)
Packit 8a864e
      pathStr += path_[i];
Packit 8a864e
    ParentLocationMessenger(mgr).message(URLStorageMessages::getFailed,
Packit 8a864e
					 StringMessageArg(hostStr_),
Packit 8a864e
					 StringMessageArg(pathStr),
Packit 8a864e
					 StringMessageArg(reason));
Packit 8a864e
    return HTTP_ERROR;
Packit 8a864e
  }
Packit 8a864e
  for (;;) {
Packit 8a864e
    if (!readLine(mgr, buf, leftOver))
Packit 8a864e
      return HTTP_ERROR;
Packit 8a864e
    if ( ! strdiff("location:", (char*) buf.data() ) ) {
Packit 8a864e
      unsigned int sz = buf.size() > 255 ? 255 : buf.size() ;
Packit 8a864e
      strncpy(locbuf, buf.data(), sz) ;
Packit 8a864e
      locbuf[sz] = 0 ;
Packit 8a864e
      for (int i=0; i
Packit 8a864e
	if ( locbuf[i] == '\r' || locbuf[i] == '\n' ) {
Packit 8a864e
	  locbuf[i] = 0 ;
Packit 8a864e
	  break ;
Packit 8a864e
	}
Packit 8a864e
    }
Packit 8a864e
    if (buf.size() == 0 || buf[0] == '\r' || buf[0] == '\n')
Packit 8a864e
      break;
Packit 8a864e
  }
Packit 8a864e
  if (leftOver.size())
Packit 8a864e
    unread(leftOver.data(), leftOver.size());
Packit 8a864e
  return ( val < 300 ) ? HTTP_OK : HTTP_REDIRECT ;
Packit 8a864e
}
Packit 8a864e
Packit 8a864e
// Status line must start with: "HTTP/" 1*DIGIT "." 1*DIGIT SP 3DIGIT SP
Packit 8a864e
Packit 8a864e
Boolean HttpSocketStorageObject::parseStatus(const char *&ptr, int &val)
Packit 8a864e
{
Packit 8a864e
  static const char ver[] = "HTTP/";
Packit 8a864e
  for (const char *v = ver; *v; v++, ptr++)
Packit 8a864e
    if (*v != *ptr)
Packit 8a864e
      return 0;
Packit 8a864e
  if (!isdigit((unsigned char)*ptr))
Packit 8a864e
    return 0;
Packit 8a864e
  do {
Packit 8a864e
    ++ptr;
Packit 8a864e
  } while (isdigit((unsigned char)*ptr));
Packit 8a864e
  if (*ptr != '.')
Packit 8a864e
    return 0;
Packit 8a864e
  ptr++;
Packit 8a864e
  if (!isdigit((unsigned char)*ptr))
Packit 8a864e
    return 0;
Packit 8a864e
  do {
Packit 8a864e
    ++ptr;
Packit 8a864e
  } while (isdigit((unsigned char)*ptr));
Packit 8a864e
  if (*ptr != ' ')
Packit 8a864e
    return 0;
Packit 8a864e
  ptr++;
Packit 8a864e
  val = 0;
Packit 8a864e
  for (int i = 0; i < 3; i++, ptr++) {
Packit 8a864e
    if (!isdigit((unsigned char)*ptr))
Packit 8a864e
      return 0;
Packit 8a864e
    val = val*10 + *ptr - '0';
Packit 8a864e
  }
Packit 8a864e
  if (*ptr != ' ')
Packit 8a864e
    return 0;
Packit 8a864e
  ptr++;
Packit 8a864e
  return 1;
Packit 8a864e
}
Packit 8a864e
Packit 8a864e
// True will be returned for an empty line.
Packit 8a864e
Packit 8a864e
Boolean HttpSocketStorageObject::readLine(Messenger &mgr,
Packit 8a864e
					  String<char> &line,
Packit 8a864e
					  String<char> &leftOver)
Packit 8a864e
{
Packit 8a864e
  line.resize(0);
Packit 8a864e
  Boolean hadCr = 0;
Packit 8a864e
  Boolean gotLine = 0;
Packit 8a864e
  size_t li;
Packit 8a864e
  for (li = 0; li < leftOver.size(); li++) {
Packit 8a864e
    if (leftOver[li] == '\r') {
Packit 8a864e
      if (hadCr) {
Packit 8a864e
	gotLine = 1;
Packit 8a864e
	break;
Packit 8a864e
      }
Packit 8a864e
      line += '\r';
Packit 8a864e
      hadCr = 1;
Packit 8a864e
    }
Packit 8a864e
    else if (leftOver[li] == '\n') {
Packit 8a864e
      line += '\n';
Packit 8a864e
      li++;
Packit 8a864e
      gotLine = 1;
Packit 8a864e
      break;
Packit 8a864e
    }
Packit 8a864e
    else if (hadCr) {
Packit 8a864e
      gotLine = 1;
Packit 8a864e
      break;
Packit 8a864e
    }
Packit 8a864e
    else
Packit 8a864e
      line += leftOver[li];
Packit 8a864e
  }
Packit 8a864e
  if (gotLine) {
Packit 8a864e
    for (size_t i = li; i < leftOver.size(); i++)
Packit 8a864e
      leftOver[i - li] = leftOver[i];
Packit 8a864e
    leftOver.resize(leftOver.size() - li);
Packit 8a864e
    return 1;
Packit 8a864e
  }
Packit 8a864e
  leftOver.resize(0);
Packit 8a864e
  if (eof_)
Packit 8a864e
    return 1;
Packit 8a864e
  for (;;) {
Packit 8a864e
    char c;
Packit 8a864e
    long n;
Packit 8a864e
    do {
Packit 8a864e
      n = readsocket(fd_, &c, 1);
Packit 8a864e
    } while (n < 0 && errnosocket == SOCKET_EINTR);
Packit 8a864e
    if (n == 0) {
Packit 8a864e
      (void)closesocket(fd_);
Packit 8a864e
      eof_ = 1;
Packit 8a864e
      return 1;
Packit 8a864e
    }
Packit 8a864e
    if (n < 0) {
Packit 8a864e
      ParentLocationMessenger(mgr).message(URLStorageMessages::readError,
Packit 8a864e
					   StringMessageArg(hostStr_),
Packit 8a864e
					   SocketMessageArg(errnosocket));
Packit 8a864e
      (void)closesocket(fd_);
Packit 8a864e
      fd_ = INVALID_SOCKET;
Packit 8a864e
      return 0;
Packit 8a864e
    }
Packit 8a864e
    switch (c) {
Packit 8a864e
    case '\r':
Packit 8a864e
      if (hadCr) {
Packit 8a864e
	leftOver += c;
Packit 8a864e
	return 1;
Packit 8a864e
      }
Packit 8a864e
      hadCr = 1;
Packit 8a864e
      line += c;
Packit 8a864e
      break;
Packit 8a864e
    case '\n':
Packit 8a864e
      line += c;
Packit 8a864e
      return 1;
Packit 8a864e
    default:
Packit 8a864e
      if (hadCr) {
Packit 8a864e
	leftOver += c;
Packit 8a864e
	return 1;
Packit 8a864e
      }
Packit 8a864e
      line += c;
Packit 8a864e
      break;
Packit 8a864e
    }
Packit 8a864e
  }
Packit 8a864e
  return 0;			// not reached
Packit 8a864e
}
Packit 8a864e
Packit 8a864e
Boolean HttpSocketStorageObject::read(char *buf, size_t bufSize, Messenger &mgr,
Packit 8a864e
				     size_t &nread)
Packit 8a864e
{
Packit 8a864e
  if (readSaved(buf, bufSize, nread))
Packit 8a864e
    return 1;
Packit 8a864e
  if (fd_ == INVALID_SOCKET || eof_)
Packit 8a864e
    return 0;
Packit 8a864e
  long n;
Packit 8a864e
  do {
Packit 8a864e
    n = readsocket(fd_, buf, bufSize);
Packit 8a864e
  } while (n < 0 && errnosocket == SOCKET_EINTR);
Packit 8a864e
  if (n > 0) {
Packit 8a864e
    nread = size_t(n);
Packit 8a864e
    saveBytes(buf, nread);
Packit 8a864e
    return 1;
Packit 8a864e
  }
Packit 8a864e
  if (n < 0) {
Packit 8a864e
    ParentLocationMessenger(mgr).message(URLStorageMessages::readError,
Packit 8a864e
					 StringMessageArg(hostStr_),
Packit 8a864e
					 SocketMessageArg(errnosocket));
Packit 8a864e
    fd_ = INVALID_SOCKET;
Packit 8a864e
  }
Packit 8a864e
  else {
Packit 8a864e
    eof_ = 1;
Packit 8a864e
    if (closesocket(fd_) == SOCKET_ERROR)
Packit 8a864e
      ParentLocationMessenger(mgr).message(URLStorageMessages::closeError,
Packit 8a864e
					   StringMessageArg(hostStr_),
Packit 8a864e
					   SocketMessageArg(errnosocket));
Packit 8a864e
    fd_ = INVALID_SOCKET;
Packit 8a864e
  }
Packit 8a864e
  return 0;
Packit 8a864e
}
Packit 8a864e
Packit 8a864e
Boolean HttpSocketStorageObject::seekToStart(Messenger &)
Packit 8a864e
{
Packit 8a864e
  CANNOT_HAPPEN();
Packit 8a864e
  return 0;
Packit 8a864e
}
Packit 8a864e
Packit 8a864e
#endif /* SP_HAVE_SOCKET */
Packit 8a864e
Packit 8a864e
#ifdef SP_NAMESPACE
Packit 8a864e
}
Packit 8a864e
#endif