/****************************************************************************
*	Author		:	G Todd													*
*	Language 	:	Borland C++ 3.1											*
*	Logfile		:	nettime.c												*
*	Project		:	DIS KA9Q.												*
*	Date 		:	24 Aug 92												*
*	Revision 	:	1.1		GT	Originate.									*
*				:	1.2		MT	"auto", "maxcorrect" and "mincorrect".		*
*	21 Sep 92	:	1.3		MT	Fix correction reports.						*
*				:			GT	Disable idle timeout while setting the time.*
*	28 Sep 92	:	1.4		GT	Wait for next timer tick after setting the	*
*				:				time.										*
*	09 Apr 93	:	1.5		GT	Fix spurious error message from do_set ().	*
*				:				Increase connect timeout.					*
*				:				Fix smtptick () call.						*
*	13 Apr 93	:	1.6		GT	Time setting tweaks.						*
*				:				"time delay" command.						*
*	08 May 93	:	1.9		GT	Fix warnings.								*
*****************************************************************************
*	Purpose		:	Routines for setting KA9Q's time from the remote host.	*
*				:	Uses the protocol described in RFC 868.					*
*****************************************************************************
*				:	Copyright Giles Todd 1992.  All rights reserved.		*
*				:	The right of Giles Todd to be identified as the author	*
*				:	of this work has been asserted by him in accordance		*
*				:	with the Copyrights, Designs and Patents Act, 1988.		*
*				:	No restrictions on the use of this module with KA9Q.	*
*****************************************************************************
$Id: nettime.c 1.9 93/07/16 11:46:57 ROOT_DOS Exp $
*
*	ATARI Version by David Nash - dnash@chaos.demon.co.uk
*
*  __stdargs bg_set, pause->Pause
*  don't swap bytes for little endian order
*
*  24.11.94 DFN - Remove stdargs from do_delay
*
****************************************************************************/

#include	<stdio.h>
#include	<time.h>
#include	"global.h"
#include	"mbuf.h"
#include	"cmdparse.h"
#include	"proc.h"
#include	"socket.h"
#include	"timer.h"
#include	"netuser.h"
#include	"commands.h"
#include	"ip.h"
#include	"smtp.h"
#include	"pppfsm.h"

#if	defined (time)
#undef	time								/* remove macro					*/
#endif

extern int ext_dokicks(void);

int done_set_time = 0;	/* have we set the time since this open?	*/

/****************************************************************************
*	Static function declarations.											*
****************************************************************************/

static int do_auto __ARGS((int argc, char **argv, void *p));
static int do_delay __ARGS((int argc, char **argv, void *p));
static int do_server __ARGS((int argc, char **argv, void *p));
static int do_maxcorrect __ARGS((int argc, char **argv, void *p));
static int do_mincorrect __ARGS((int argc, char **argv, void *p));
static int do_read __ARGS((int argc, char **argv, void *p));
static int do_set __ARGS((int argc, char **argv, void *p));
static void __stdargs bg_set __ARGS((int s, void *unused, void *p));
static void start_of_connection __ARGS((void));

/****************************************************************************
*	Static data.															*
****************************************************************************/

static time_t correction = 0L;				/* difference in seconds 		*/
											/* between remote and PC clock	*/
static int32 server = 0L;					/* time server address			*/
static int auto_time_set = 0;				/* set time on PPP open?		*/
static struct proc *setting_the_time = NULLPROC;
static int32 delay = 10000L;				/* delay for setting up routes	*/
static time_t max_correction = 0;			/* !0 is max correction allowed */
static time_t min_correction = 0;			/* !0 is min correction allowed */
static struct cmds time_cmds[] =
	{
	{ "auto",		do_auto,	0,		0,	NULLCHAR },
	{ "delay",	do_delay,	0,		0,	NULLCHAR },
	{ "maxcorrect",do_maxcorrect,0,	0,	NULLCHAR },
	{ "mincorrect",do_mincorrect,0,	0,	NULLCHAR },
	{ "read",		do_read,	0,		0,	NULLCHAR },
	{ "server",	do_server,	0,		0,	NULLCHAR },
	{ "set",		do_set,		0,		0,	NULLCHAR },
	{ NULLCHAR }
	};


/****************************************************************************
*	do_time																	*
*	Time commands driver.													*
****************************************************************************/

int do_time (argc, argv, p)
int argc;
char **argv;
void *p;
	{
	return (subcmd (time_cmds, argc, argv, p));
	}	/* int do_time (argc, argv, p) */


/****************************************************************************
*	do_server																*
*	Display or set the timer server address.								*
****************************************************************************/

static int do_server (argc, argv, p)
int argc;
char **argv;
void *p;
	{
	int32 n;								/* temporary					*/

	if (argc < 2)
		{
		tprintf ("%s\n", inet_ntoa (server));
		}
	else if ((n = resolve (argv[1])) == 0)
		{
		tprintf (Badhost, argv[1]);
		return (1);
		}
	else
		server = n;

	return (0);
	}	/* static int do_server (argc, argv, p) */
	

/****************************************************************************
*	do_read																	*
*	Read the time from the remote and set the correction value.				*
****************************************************************************/

static int do_read (argc, argv, p)
int argc;
char **argv;
void *p;
	{
	struct sockaddr_in fsocket;				/* socket address				*/
	int s;									/* socket handle				*/
	union
		{
		time_t time;
		char bytes[sizeof (time_t)];
		} result;							/* data from remote				*/
#ifndef ATARI
	char t;									/* temporary					*/
#endif	
	char *cp;								/* -> error message				*/
	time_t pctime;							/* PC time						*/

	if (server == 0L)
		{
		tprintf (Badhost, "0.0.0.0");
		return (1);
		}
		
	/* Set up the connection. */

	fsocket.sin_family = AF_INET;
	fsocket.sin_addr.s_addr = server;
	fsocket.sin_port = IPPORT_TIME;
	s = socket (AF_INET, SOCK_STREAM, 0);
	sockmode (s, SOCK_BINARY);
	alarm (connect_wait_val);				/* set timeout					*/
	if (connect (s, (char *) &fsocket, SOCKSIZE) != 0)
		{
		alarm (0L);
		cp = sockerr (s);
		tprintf ("TIME: %s connect failed: %s\n", psocket (&fsocket),
				 cp != NULLCHAR ? cp : "");
		(void) close_s (s);
		done_set_time = FALSE;
		return (1);
		}

	alarm (0L);

	/* Try to receive the time. */

	if (recv (s, result.bytes, sizeof (result.bytes), 0) !=
		sizeof (result.bytes))
		{
		tprintf ("TIME: receive failed\n");
		close_s (s);
		done_set_time = FALSE;
		return (1);
		}

	close_s (s);

	/* Calculate the correction value. */

#ifndef ATARI	
	t = result.bytes[0];
	result.bytes[0] = result.bytes[3];
	result.bytes[3] = t;
	t = result.bytes[1];
	result.bytes[1] = result.bytes[2];
	result.bytes[2] = t;
#endif
	
	result.time -= 2208988800L;				/* adjust epoch					*/
		
	pctime = time (NULL);
	correction = result.time - pctime;
	tprintf ("TIME: correction = %ld seconds\n", correction);
	pctime = ka9q_time (NULL);
	tprintf ("Time now (GMT): %s", asctime (gmtime (&pctime)));
	return (0);
	}	/* static int do_read (argc, argv, p) */


/****************************************************************************
*	do_set																	*
*	Set the PC's clock from the time server.								*
****************************************************************************/

static int do_set (argc, argv, p)
int argc;
char **argv;
void *p;
	{
	time_t n;								/* temporary					*/
	int correction_abs;

	if (correction == 0L)
		{
		/* Set up the correction. */

		if (do_read (0, NULL, NULL) != 0)
			return (1);
			
		}

	correction_abs = (int) labs(correction);

	if (max_correction && correction_abs > max_correction)
		{
		tprintf("TIME: required correction (%ld) greater than maximum (%ld)\n",
			correction, max_correction);
		correction = 0L;
		return (0);
		}

	if (min_correction && correction_abs < min_correction)
		{
		tprintf("TIME: required correction (%ld) less than minimum (%ld)\n",
			correction, min_correction);
		correction = 0L;
		return (0);
		}

	n = ka9q_time (NULL);					/* get tweaked time				*/
	(void) stime (&n);						/* and set the PC clock			*/
	log(-1,"PC clock adjusted by %ld at %s (server %s)",
									correction, ctime(&n), inet_ntoa(server));
	correction = 0L;						/* no need for a correction now	*/
	tprintf ("TIME: PC clock set\n");
	return (0);
	}	/* static int do_set (argc, argv, p) */



/*---------------------------------------------------------------------------
	do_delay - Display or set the timer delay
---------------------------------------------------------------------------*/

static int do_delay(int argc, char *argv[], void *p)
{
	if (argc < 2) {
		tprintf ("auto kick delay: %lu\n", delay / 1000L);
		return 0;
	}

	delay = atol (argv[1]) * 1000L;			/* set timeout					*/

	return 0;
}
	

/****************************************************************************
*	ka9q_time																*
*	Replacement for library "time ()" function.  Called by other modules	*
*	via nasty macro substitution.											*
****************************************************************************/

time_t ka9q_time (t)
time_t *t;
	{
	time_t rc;								/* return value					*/

	(void) time (&rc);						/* get the PC's idea			*/
	rc += correction;
	if (t != NULL)
		*t = rc;

	return (rc);
	}	/* time_t ka9q_time (t) */

	
/****************************************************************************
*	do_auto																	*
*	Should we do a 'time set' every time ppp ipcp opens? 					*
****************************************************************************/

static int do_auto (argc, argv, p)
int argc;
char **argv;
void *p;
	{
	return setbool(&auto_time_set, "TIME SET auto mode", argc, argv);
	}

int try_set_time (argc, argv, p)
int argc;
char **argv;
void *p;
	{
	struct ppp_s *ppp_p = p;
	
	done_set_time = 1;
	if (!auto_time_set)
		return 0;
	if (setting_the_time == NULLPROC)
		setting_the_time = newproc("set time", 512, bg_set, 0, NULL, ppp_p, 0);
	return 0;
	}

static void start_of_connection()
	{
	int32 zero = 0;
	smtptick((void *) zero);
	ext_dokicks();
	}

static void __stdargs bg_set (int s, void *unused, void *p)
{
	int32 save_idle_durn = 0L;
	struct ppp_s *ppp_p = p;
	
	Pause (delay);

	if (ppp_p->idle_durn != 0L)
		{
		/* Stop the PPP idle timer. */

		stop_timer (&(ppp_p->idle_timer));
		save_idle_durn = ppp_p->idle_durn;
		ppp_p->idle_durn = 0L;
		}
		
	do_set(0, NULL, NULL);
	setting_the_time = NULLPROC;
	if (save_idle_durn != 0L)
		{
		/* Restart the PPP idle timer. */
#ifdef ATARI										/* pwait(Tick) hangs routine			*/
		pwait(NULL);
#else		
		pwait ((void *)&Tick);					/* wait for next timer tick			*/
#endif		
		ppp_p->idle_durn = save_idle_durn;
		set_timer (&(ppp_p->idle_timer), ppp_p->idle_durn);
		start_timer (&(ppp_p->idle_timer));
		}
	start_of_connection();
	}

static int
do_maxcorrect(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	if(argc < 2){
		tprintf("TIME: maximum correction: %d\n", max_correction);
		return 0;
	}
	max_correction = atoi(argv[1]);
	return 0;
}

static int
do_mincorrect(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	if(argc < 2){
		tprintf("TIME: minimum correction: %d\n", min_correction);
		return 0;
	}
	min_correction = atoi(argv[1]);
	return 0;
}
