/* vi:ts=4:sw=4
 *
 *	url.c
 *	open URL
 *
 *	code contributions By:	Atsushi Nakamura	anaka@mrit.mei.co.jp
 */
#ifdef MDOMAIN
#include "vim.h"
#include "globals.h"
#include "proto.h"
#include "param.h"
#include "ops.h"
#ifdef JP
# include "jp.h"
#endif
#include <fcntl.h>
#if !defined(pyr) && !defined(NOT_BOTH_TIME)
# include <time.h>			/* on some systems time.h should not be
							   included together with sys/time.h */
#endif
#ifndef M_XENIX
# include <sys/types.h>
#endif
#ifdef SYSV_UNIX
# if defined(__sgi) || defined(UTS2) || defined(UTS4) || defined(AUX3)
#  include <sys/time.h>
# endif
# if defined(M_XENIX) || defined(SCO)
#  include <stropts.h>
# endif
# if defined(M_XENIX) || defined(SCO) || defined(UNICOS) || defined(AIX31)
#  include <sys/select.h>
# else
#  if !defined(AUX3)
#    include <poll.h>
#  endif
# endif
# if defined(SCO) || defined(ISC)
#  include <sys/stream.h>
#  include <sys/ptem.h>
# endif
# if defined(M_UNIX) && !defined(SCO)
#  include <sys/time.h>
# endif       /* M_UNIX */
#else	/* SYSV_UNIX */
# include <sys/time.h>
#endif	/* !SYSV_UNIX */
#if defined(FD_ZERO) && defined(SYSV_UNIX)
# undef FD_ZERO
#endif
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <errno.h>
#ifdef _POSIX_VERSION
#include <sys/utsname.h>
#endif
#include <pwd.h>
#ifdef JP
extern char *UAJVersion;
#else
extern char *UAVersion;
#endif
extern gettimeofday();
#define BUFSIZE 100
#ifndef MAXHOSTNAMELEN
# define MAXHOSTNAMELEN 64
#endif
#ifndef MAXPATHLEN
# define MAXPATHLEN 1024
#endif
#define MAXPROTO 20
#define MAXPORT  20
#define MAXURL   (MAXHOSTNAMELEN + MAXPORT + MAXPATHLEN)
static char *dow[7] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
static char *moy[12]= { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
						"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
static char hostname[MAXHOSTNAMELEN + 1] = "";
static char loginname[MAXHOSTNAMELEN + 1] = "";
static int  uid, pid, count = 0;
static char domainname[MAXHOSTNAMELEN + 1] = MDOMAIN;
static char curl[MAXURL + 1] = "";
static int	parse_url(), read_line(), send_line(), fd_wait();
static void	const_url(), pinfo_init();
	int
is_url(url)
	char *url;
{
	char proto[MAXPROTO];
	proto[0] = NUL;
	parse_url(url, proto, NULL, NULL, NULL);
	if (			  !strcmp(proto, "http")
	 ||	( *p_proxy && !strcmp(proto, "ftp")) )
		return 1;
    return 0;
}
	void
url_init(url)
	char *url;
{
	p_curl = curl;
    if (! *curl)
		const_url(curl, "http", "localhost", "", "");
	if (!url)
	  return;
	if (is_url(url))
	{
		strncpy(curl, url, MAXURL);
		curl[MAXURL] = NUL;
	}
}
enum e_proto { PROTO_HTTP, PROTO_FTP, PROTO_ERROR };
int url_open(url, mode, total)
  char *url;
  int  mode, *total;
{
    char proto[MAXPROTO + 1];
    char host[MAXHOSTNAMELEN + 1];
    char port[MAXPORT + 1];
    char path[MAXPATHLEN + 1], lpath[MAXPATHLEN + 1];
	char furl[MAXURL + 1];
	char cbuf[MAXURL + 11];
	char temp[20], status[20], type[20];
	char *cp, *cp2;
	int  size;
    int fd;
    struct sockaddr_in	dest;
    struct hostent	*hostinfo;
    struct servent	*servinfo;
	struct tm		*tm;
	time_t now;
    enum e_proto protocol;
	if (mode != O_RDONLY && mode != O_WRONLY)
		return -1;
	if (!*curl)
		url_init(NULL);
	if (!loginname[0])
		pinfo_init();
    parse_url(url, NULL, NULL, NULL, lpath);
    parse_url(curl, proto, host, port, path);
    if (parse_url(url, proto, host, port, path))
		return -1;
    const_url(furl, proto, host, port, path);
	if (*p_proxy)
	{
		/* parse proxy server */
		cp = p_proxy;
		cp2 = host;
		while(*cp && *cp != ':')
			*cp2 ++ = *cp ++;
		*cp2 = NUL;
		if (*cp == ':')
			strcpy(port, cp + 1);
		strcpy(proto, "http");
	}
    if      (! strcmp(proto, "http") )
		protocol = PROTO_HTTP;
    else if (! strcmp(proto, "ftp") )
		protocol = PROTO_FTP;
	else
		protocol = PROTO_ERROR;
	smsg("Resolving %s", host);
    if (! (hostinfo = gethostbyname(host)))
	{
		emsg2("Host %s unknown.", host);
		return -1;
    }
	msg("");
	memset((void *)&dest, 0, sizeof(dest));
	dest.sin_family = AF_INET;
	dest.sin_port   = htons(atoi(port));
	if (!dest.sin_port)
    {
		if (*port)
		{
			if (! (servinfo = getservbyname(port, NULL)) )
			{
				emsg2("Service '%s' unknown.", port);
				return -1;
			}
			dest.sin_port = servinfo -> s_port;
		}
		else if ((servinfo = getservbyname(proto, NULL)))
			dest.sin_port = servinfo -> s_port;
		else
			switch(protocol)
			{
				case PROTO_HTTP:
					dest.sin_port = htons(80);
					break;
				case PROTO_FTP:
					dest.sin_port = htons(21);
					break;
				default:
					emsg2("Default Service for '%s' unknown.", proto);
					return -1;
			}
    }
	memcpy(&dest.sin_addr, hostinfo->h_addr, hostinfo->h_length);
	if((fd = socket(PF_INET, SOCK_STREAM, 0)) < 0)
	{
		emsg("Socket allocation error.");
		return -1;
	}
	smsg("Connecting to %s", host);
	fflush(stderr);
	if(connect(fd, (struct sockaddr *)&dest, sizeof(dest)) == -1)
	{
	    switch(errno)
		{
			case EACCES:
				emsg("Search permission denied.");
				break;
			case EADDRNOTAVAIL:
				emsg("Address not available.");
				break;
			case ECONNREFUSED:
				emsg("Connection rejected.");
				break;
			case ENETUNREACH:
				emsg("Network unreachable.");
				break;
			case ETIMEDOUT:
				emsg("Connection timed out.");
				break;
		}
		return -1;
	}
	switch(protocol)
	{
		case PROTO_HTTP:
			/* header request */
			smsg("sending request to %s.", host);
			if (p_ntr)
				fprintf(stderr, "\n");
			if (mode == O_WRONLY)
				sprintf(cbuf, "PUT %s HTTP/1.0", *p_proxy ? furl : path);
			else
				sprintf(cbuf, "GET %s HTTP/1.0", *p_proxy ? furl : path);
			if (send_line(fd, cbuf))
				goto fail;
			/* general header*/
			/* send date and time in RFC1123 format */
			now = time(NULL);
			tm = gmtime(&now);
			sprintf(cbuf, "DATE: %s, %02d %s %4d %02d:%02d:%02d GMT",
							dow[tm->tm_wday], tm->tm_mday, moy[tm->tm_mon],
							1900 + tm->tm_year,
							tm->tm_hour, tm->tm_min, tm->tm_sec);
			if (send_line(fd, cbuf))
				goto fail;
			/* send message ID in RFC822 format */
			sprintf(cbuf, "Message-ID: <%04X%x.%s@%s.%s>",
							pid, count++ , loginname, hostname, domainname);
			if (send_line(fd, cbuf))
				goto fail;
			/* send MIME Version*/
			if (send_line(fd, "MIME-Version: 1.0"))
				goto fail;
			/* request header*/
			/* send Accept */
			if (send_line(fd, "Accept: */*"))
				goto fail;
			/* send Accept Character set */
			if (send_line(fd, "Accept-Charset: ISO-2022-JP"))
				goto fail;
			/* send Accept Encoding */
			if (send_line(fd, "Accept-Encoding: *"))
				goto fail;
			/* send user agent */
#ifdef JP
			sprintf(cbuf, "User-Agent: %s", UAJVersion);
#else
			sprintf(cbuf, "User-Agent: %s", UAVersion);
#endif
			if (send_line(fd, cbuf))
				goto fail;
			/* send from */
			sprintf(cbuf, "From: %s@%s.%s", loginname, hostname, domainname);
			if (send_line(fd, cbuf))
				goto fail;
			/* end of header */
			if (send_line(fd, ""))
				goto fail;
			if(mode == O_WRONLY && fd_wait(fd, 15) <= 0)
				goto fail;
			/* read response line */
			smsg("waiting response from %s", host);
			if (p_ntr)
				fprintf(stderr, "\n");
			if ((size = read_line(fd, cbuf)) < 0)
				goto fail;
			sscanf(cbuf, "%s %s", temp, status);
			if (status[0] != '2')
			{
				/* show all error messages */
				smsg("%s\n", cbuf);
				while((size = read_line(fd, cbuf)))
					if (size < 0)
						break;
				close(fd);
				goto fail;
			}
			/* read response header */
			*total = -1;
			type[0] = NUL;
			while((size = read_line(fd, cbuf)))
			{
				char *cp;
				if (size < 0)
					goto fail;
				for(cp = cbuf; *cp && *cp != ':'; cp++)
					if ('A' <= *cp && *cp <= 'Z')
						*cp -= 'A' - 'a';
				if      (!strncmp("content-length", cbuf, 14))
					sscanf(cbuf, "%[^:]: %d", temp, total);
				else if (!strncmp("content-type",   cbuf, 12))
					sscanf(cbuf, "%[^:]: %s", temp, type);
				/*
					not implemented yet.
				*/
			}
			if (p_ntr)
			{
				wait_return();
				updateScreen(CLEAR);
			}
			smsg("%d bytes %s", *total, type);
/*			if (strncmp(type, "text", 4))
			{
				emsg2("File format(%s) is not text.", type);
				close(fd);
				goto fail;
			}
*/
			break;
		case PROTO_FTP:
		default:
			/*
				Not implemented yet.
			*/
			break;
	}
/*
    strcpy(curl, furl);
	p_curl = curl;
*/
	return fd;
fail:
	if (p_ntr)
	{
		fprintf(stderr, "Error....\n");
		wait_return();
	}
	return -1;
}
	static int
fd_wait(fd, sec)
	int fd;
	int sec;
{
#ifdef UNIX
#if defined(SYSV_UNIX) && !defined(M_XENIX) && !defined(UNICOS) && !defined(AUX3)
	struct pollfd fds[2];
	fds[0].fd = 0;
	fds[1].fd = fd;
	fds[0].events =
	fds[1].events = POLLIN;
	if (!poll(fds, 2, sec * 1000))
		return 0;
	if ((fds[0].revents & (POLLIN | POLLERR | POLLHUP))
	  ||(fds[1].revents & (POLLERR | POLLHUP)))
		return -1;
# else
extern int select __ARGS((int, fd_set *, fd_set *, fd_set *, struct timeval *));
	fd_set fds;
	struct timeval timeout;
	FD_ZERO(&fds);
	FD_SET(fd, &fds);
	FD_SET(0, &fds);
	timeout.tv_sec = sec;
	timeout.tv_usec = 0;
	if (select(32, &fds, NULL, NULL, &timeout) <= 0)
		return 0;
	if (!FD_ISSET(fd, &fds))
		return -1;
# endif
	return 1;
#else
	return 1;
#endif
}
	static void
const_url(url, proto, host, port, path)
	char *url, *proto, *host, *port, *path;
{
	sprintf(url, "%s://%s", proto, host);
	if (*port)
		sprintf(url + strlen(url), ":%s", port);
	strcat(url, path);
}
	static int
parse_url(url, proto, host, port, path)
	char *url, *proto, *host, *port, *path;
{
    char *cp;
	char *orgpath;
    /* protocol */
    for(cp = url; *cp && *cp != ':'; cp++);
    if (*cp)
    {
		if (cp - url > MAXPROTO)
		return -1;
		if (proto)
		{
			if (port && strncmp(proto, url, strlen(proto)))
				*port = NUL;
			while(url != cp)
				*proto++ = *url++;
			*proto = NUL;
		}
		url = cp = cp + 1;
	}
	else
		cp = url;
	/* host address & port number */
	if (*cp == '/' && *(cp + 1) == '/')
	{
		url = cp = cp + 2;
		for(; *cp && *cp != ':' && *cp != '/' ; cp++);
		if (cp - url > MAXHOSTNAMELEN)
			return -1;
		if (host)
		{
			while(url != cp)
				*host++ = *url++;
			*host = NUL;
		}
		if (*cp == ':')
		{
			for(url = cp = cp + 1; *cp && *cp != '/'; cp++);
			if (cp - url > MAXPROTO)
				return -1;
			if (port)
				while(url != cp)
					*port++ = *url++;
		}
		if (port)
		  *port = NUL;
	}
	/* path */
	if (!path)
		return 0;
	if (*cp)
	{
		orgpath = path;
		if (*cp != '/')
		{
			for(;*path; path++);
			for(;*path != '/' && path != orgpath; path--);
		}
	
		if ((int)strlen(cp) + (path - orgpath) > MAXPATHLEN)
			return -1;
		if (*cp != '/')
			*path++ = '/';
		while(*cp)
			*path++ = *cp++; 
	}
	*path = NUL;
	return 0;
}
	static int 
send_line(fd, line)
	int fd;
	char *line;
{
	if (p_ntr)
		fprintf(stderr, "< %s\n", line);
	if (write(fd, line, strlen(line)) >= 0
	 && write(fd, "\r\n", 2)          >= 0 )
		return 0;
	close(fd);
	return -1;
}
	static int
read_line(fd, line)
	int fd;
	char *line;
{
	char c, *top;
	int i, size;
	switch(fd_wait(fd, p_nto))
	{
		case 0:
			emsg("Timed out.");
			close(fd);
			return -1;
		case -1:
			emsg("Interrupted.");
			close(fd);
			return -1;
		default:
			break;
	}
	top = line;
	for(size = 0; 1; size++, line++)
	{
   		i = read(fd, &c, 1);
		if (i == 0)
		{
			close(fd);
			return -1;
		}
		if (c == '\r')
		{
			read(fd, &c, 1);
			break;
		}
		if (c == '\n')
			break;
		*line = c;
	}
	*line = NUL;
	if (p_ntr)
		fprintf(stderr, "> %s\n", top);
	return size;
}
	static void
pinfo_init()
{
	char *cp, *cp2;
#ifdef _POSIX_VERSION
	struct utsname uts;
	uname(&uts);
	cp = uts.nodename;
	cp2 = hostname;
	while(*cp && cp2 - hostname < MAXHOSTNAMELEN)
		*cp2 ++ = *cp ++;
	*cp2 = NUL;
#else
	gethostname(hostname, MAXHOSTNAMELEN);
#endif
	if ((cp2 = strchr(hostname, '.')) != NULL)
		*cp2 = NUL;
	uid = getuid();
	cp = getpwuid(uid)->pw_name;
	cp2 = loginname;
	while(*cp && cp2 - loginname < MAXHOSTNAMELEN)
		*cp2 ++ = *cp ++;
	pid = getpid();
	if ((cp = getenv("DOMAINNAME")))
	{
		if ((int)strlen(cp) < MAXHOSTNAMELEN)
			strcpy(domainname, cp);
		else
			emsg("Environment variable DOMAINNAME too long.");
	}
	else if (!strcmp(domainname, "localhost"))
		emsg("Recompile this program setting MDOMAIN....");
}
	int
do_nget(args)
	char *args;
{
	char *url, *file;
	char *cp;
	int rfd, wfd;
	int i, size, total;
/*	int bps; */
	url = args;
	file = "";
	for(cp = args; *cp && *cp != ' ' && *cp != '\t'; cp++);
	if (args == cp)
	{
		emsg("URL and filename are required.");
		return -1;
	}
	else if (*cp)
	{	/* 2 parameters are specified */
		*cp++ = NUL;
		for(; *cp && (*cp == ' ' || *cp == '\t'); cp++);
		file = cp;
		for(; *cp && *cp != ' ' && *cp != '\t'; cp++);
		/* more than 2 parameters */
		if (*cp)
		{
			emsg("Only URL and filename are required.");
			return -1;
		}
	}
	if (! *file)
	{	/* if file is not specified */
		for(cp = url; *cp; cp++);
		if (* (cp - 1) == '/')
		{
			emsg("Filename is required.");
			return -1;
		}
		for(;cp != url && *cp != '/' && *cp !=  ':'; cp--);
		if (*cp == '/' || *cp == ':')
			cp++;
		file = cp;
	}
	if (!is_url(url))
	{
		emsg2("%s is not URL.", url);
		return -1;
	}
	if ((rfd = url_open(url, O_RDONLY, &total)) < 0)
		return -1;
	if ((wfd = open(file, O_WRONLY | O_CREAT | O_EXCL, 0744)) < 0)
	{
		emsg2("file '%s' cannot open for writing.", file);
		return -1;
	}
	size = 0;
	while((i = read(rfd, IObuff, IOSIZE)))
	{
		write(wfd, IObuff, i);
		size += i;
/*		bps = speed(i);	*/
		if (size > 1024)
			smsg("%d/%d Kbytes", size >> 10, total >> 10);
		else
			smsg("%d/%d bytes", size, total);
	}
	smsg("%d bytes transferred", size);
	close(rfd);
	close(wfd);
	return 0;
}
/*
	static int
speed(size)
	int size;
{
	static struct timeval tprev = { 0, 0 };
	struct timeval	now, diff;
	int rate;
	gettimeofday(&now, NULL);
	rate = 0;
	if (tprev.tv_sec || tprev.tv_usec)
	{
		diff.tv_sec  = now.tv_sec  - tprev.tv_sec;
		diff.tv_usec = now.tv_usec - tprev.tv_usec;
		if (diff.tv_usec < 0)
		{
			diff.tv_sec --;
			diff.tv_usec += 1000000;
		}
		size <<= 8;
		diff.tv_sec <<= 8;
		diff.tv_usec <<= 8;
		diff.tv_sec += (diff.tv_usec / 1000000);
		if (diff.tv_sec)
			rate = size / diff.tv_sec;
		else
			rate = -1;
	}
	tprev = now;
	return rate;
}
*/
#endif /* MDOMAIN */
syntax highlighted by Code2HTML, v. 0.9.1