#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "win32.h"

#ifdef HAVE_STDIO_H
#include <stdio.h>
#endif
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#ifdef HAVE_SYS_TYPES_H
#include <sys/types.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_LIBCONFIG
#include <libconfig.h>
#endif
#ifdef HAVE_SYSLOG_H
#include <syslog.h>
#endif
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif

#include "htp.h"

int print_help(const char *shortvar, const char *var, const char *arguments, const char *value, lc_flags_t flags, void *extra) {
	printf("htpdate version " PACKAGE_VERSION "\n");
	printf("Usage: htpdate [-anh] [-P proxy] [-U user] [-p pass]\n");
	printf("               <host> [<host> [<host> [...]]]\n");
	printf("   -a        Allow htpdate to slew the time instead of setting it.\n");
	printf("   -n        Do not actually set the clock, just print the new time.\n");
	printf("   -h        This help information.\n");
	printf("   -P proxy  Proxy to connect through.\n");
	printf("   -U user   Username for proxy authentication.\n");
	printf("   -p pass   Password for proxy authentication.\n");
	printf("   host      Host to query for time, in the format of hostname[:port]\n");
	exit(EXIT_SUCCESS);
}

int main(int argc, char *argv[]) {
	unsigned int argind = 0, timeind = 0;
	unsigned int totaltimeservers = 0;
	struct timeserver_st timeservers[128];
	struct timeval *newtime;
	double delta = 0.0;
	char *host = NULL, *portstr = NULL;
	char *proxyhost = NULL, *proxyportstr = NULL;
	char *proxy_username = NULL, *proxy_password = NULL;
	char strtime[128];
	int port = -1, proxyport = -1;
	int allowadj = 0;
	int queryonly = 0;
	int lc_p_ret;

	htp_init();

#ifdef HAVE_OPENLOG
	openlog("htpdate", LOG_PID, LOG_USER);
#endif

	lc_register_var("AdjustTime", LC_VAR_BOOL_BY_EXISTANCE, &allowadj, 'a');
	lc_register_var("QueryOnly", LC_VAR_BOOL_BY_EXISTANCE, &queryonly, 'n');
	lc_register_var("ProxyUser", LC_VAR_STRING, &proxy_username, 'U');
	lc_register_var("ProxyPass", LC_VAR_STRING, &proxy_password, 'p');
	lc_register_var("ProxyHost", LC_VAR_STRING, &proxyhost, 'P');
	lc_register_callback(NULL, 'h', LC_VAR_NONE, print_help, NULL);
	lc_p_ret = lc_process(argc, argv, "htpdate", LC_CONF_SPACE, SYSCONFDIR "/htpdate.conf");
	if (lc_p_ret < 0) {
		fprintf(stderr, "Error processing configuration information: %s.\n", lc_geterrstr());
		return(EXIT_FAILURE);
	}

	/* Process each argument as a host:port pair. */
	for (argind = lc_optind; argind < (unsigned int) argc; argind++) {
		if (argind >= (sizeof(timeservers)/sizeof(timeservers[0]))) {
			break;
		}

		host = argv[argind];

		portstr = strchr(host, ':');
		if (portstr != NULL) {
			/* Here, we modify argv.. good idea? */
			portstr[0] = '\0';

			portstr++;
			port = atoi(portstr);
			if (port <= 0) {
				fprintf(stderr, "Invalid port: %s.\n", portstr);
				continue;
			}
		} else {
			port = 80;
		}

		timeservers[timeind].host = host;
		timeservers[timeind].port = port;

		timeind++;
	}

	totaltimeservers = timeind;

	if (totaltimeservers == 0) {
		fprintf(stderr, "No servers specified.\n");
		return(EXIT_FAILURE);
	}

	if (proxyhost == NULL) {
		proxyhost = getenv("http_proxy");
		if (proxyhost == NULL) {
			proxyhost = getenv("HTTP_PROXY");
		}
	}

	if (proxyhost != NULL) {
		proxyportstr = strrchr(proxyhost, ':');
		if (proxyportstr != NULL) {
			*proxyportstr = '\0';
			proxyportstr++;
			proxyport = atoi(proxyportstr);
			if (proxyport <= 0) {
				printf("Illegal proxy port: %s.\n", proxyportstr);
				return(EXIT_FAILURE);
			}
		} else {
			proxyport = 8080;
		}
	}

	newtime = htp_calctime(timeservers, totaltimeservers, proxyhost, proxyport, proxy_username, proxy_password);

	if (newtime == NULL) {
		fprintf(stderr, "No servers could be reached.\n");
		return(EXIT_FAILURE);
	}

	if (!queryonly) {
		delta = set_clock(newtime, allowadj, 1);
	}

	strftime(strtime, sizeof(strtime), "%a %b %d %H:%M:%S %Y", localtime(&newtime->tv_sec));

	printf("%s [+ %f] (delta: %f)\n", strtime, ((double) newtime->tv_usec) / 1000000.0, delta);

	return(EXIT_SUCCESS);
}
