#include "netutils.h"

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <sys/types.h>
#ifdef HAVE_SYS_SELECT_H
#include <sys/select.h>
#endif
#include <ctype.h>
#include <netdb.h>
#include <sys/utsname.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>

#include <klocale.h>

#define SET_ERROR(msg) { \
    if (error) *error = msg; \
	return FALSE; }

bool getHostname(QString &name, QString *error)
{
	struct utsname thishost;
	if ( uname(&thishost)==0 ) name = thishost.nodename;
    else {
	    name = getenv("HOSTNAME"); /* last resort */
		if (name.isNull()) SET_ERROR(i18n("cannot get hostname"));
	}
	return TRUE;
}

bool getInetAddresses(const char *name, QString &hostAddr, QString &hostName,
					  QString *error)
{
	struct hostent *host;

	if ( strlen(name)==0 ) SET_ERROR(i18n("empty hostname"));

	/* try to guess */
	if ( !isdigit(name[0]) ) /* an Internet address */
		host = gethostbyname(name);
	else                    /* an Internet symbolic name */
		host = gethostbyaddr(name, strlen(name), AF_INET);
	if ( host==0 ) SET_ERROR(i18n("unknown hostname : %1").arg(name));

	hostAddr = host->h_addr;
	hostName = host->h_name;
	return TRUE;
}

bool createInetSocket(int &sock, QString *error)
{
	sock = socket(PF_INET, SOCK_STREAM, 0);
    if ( sock==-1 ) SET_ERROR(i18n("cannot create socket"));
	return TRUE;
}

bool closeSocket(int sock, QString *error)
{
	if ( close(sock)==-1 ) SET_ERROR(i18n("cannot close socket"));
	return TRUE;
}

bool connectInetSocket(int socket, unsigned short int port,
					   const char *serverName, QString *error)
{
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(port);
	struct hostent *hostinfo;

	hostinfo = gethostbyname(serverName);
	if ( hostinfo==0 ) SET_ERROR(i18n("unknown host %1").arg(serverName));
	sin.sin_addr = *(struct in_addr*)hostinfo->h_addr;
	
	if ( connect(socket, (struct sockaddr *)&sin, sizeof(sin))==-1 )
		SET_ERROR(i18n("cannot connect socket"));
	return TRUE;
}

bool bindInetSocket(int socket, unsigned short int port, int backlog,
					QString *error)
{
	struct sockaddr_in sin;
	sin.sin_family = AF_INET;
	sin.sin_port = htons(port);
	sin.sin_addr.s_addr = htonl(INADDR_ANY);

	if ( bind(socket, (struct sockaddr *) &sin, sizeof (sin))==-1 )
		SET_ERROR(i18n("bind error"));
	if ( listen(socket, backlog)==-1 )
		SET_ERROR(i18n("listen error"));
	return TRUE;;
}

bool acceptInetSocket(int socket, int &newSocket,
					  QString &name, QString &address, QString *error)
{
	struct sockaddr_in sin;
	ksize_t addrlen = (ksize_t)sizeof(sin);
	
	newSocket = accept(socket, (struct sockaddr *)&sin, &addrlen);
	if ( newSocket==-1 )
		SET_ERROR(i18n("unknown connection from unknown host ??"));
	
	address = inet_ntoa(sin.sin_addr);
	struct hostent *host = gethostbyaddr((const char *)address,
										 address.length(), AF_INET);
	if (host) name = host->h_name;
	else name = "";
	return TRUE;
}

bool checkPortValidity(const char *portString, unsigned short int &port,
					   QString *error)
{
	QString str = portString;
	bool ok;
	uint tport = str.toUInt(&ok);
	if (!ok)
		SET_ERROR(i18n("invalid string : does not represent an unsigned int"));
	return checkPortValidity(tport, port, error);
}

bool checkPortValidity(uint portInt, unsigned short int &port, QString *error)
{
	if ( portInt<MIN_USER_PORT || portInt>MAX_USER_PORT )
		SET_ERROR(i18n("port number not in useable range [%1, %2]")
				  .arg(MIN_USER_PORT).arg(MAX_USER_PORT));
	port = (unsigned short int)portInt;
	return TRUE;
}


