/*
**  timed.c
*/

#include <proto/dos.h>
#include <proto/exec.h>
#include <proto/socket.h>

#include <string.h>
#include <stdlib.h>
#include <sys/syslog.h>
#include "timed_rev.h"

static const char ver[] = VERSTAG;

#pragma msg 105 ignore

#define TEMPLATE "NAME/A,SOCKPTR/A/N,ID/A/N,ARGS/F"

struct Args {
	char *name;
	long *sockptr;
	long *id;
	char *args;
};

struct global {
	struct ExecBase *e;
	struct DosLibrary *d;
	struct Library *s;
	int errno;
};

#define SysBase  g->e
#define DOSBase  g->d
#define SockBase g->s

static void machtime_dg (struct global *g, int);
static void daytime_dg (struct global *g, int);
static void machtime_stream (struct global *g, int);
static void daytime_stream (struct global *g, int);
static void SPrintf (struct global *g, char *buf, char *ctl, ...);
static int  openlibs (struct global *g);
static void closelibs (struct global *g);
static int  reply_inet (struct global *g, long id);

struct inetmsg {
	struct Message msg;
	ULONG id;
};

long
timed (void)
{
	int s;
	long result = RETURN_FAIL;
	struct RDArgs *rdargs;
	struct global glob, *g = &glob;
	struct Args arg;

	SysBase = *((struct ExecBase **) 4L);

	if (openlibs (g))
	{
		memset ((char *) &arg, 0, sizeof (struct Args));
		if (rdargs = ReadArgs (TEMPLATE, (LONG *) &arg, NULL))
		{
			setup_sockets (5, &g->errno);

			/* now get our socket, if we can */
			if (*arg.sockptr && ((s = s_inherit ((void *) *arg.sockptr)) >= 0))
			{
				result = stricmp (arg.name, "time");

				if (arg.args && *arg.args == 'U')
				{
					/* UDP */
					if (result)
						daytime_dg (g, s);      /* service name of daytime */
					else
						machtime_dg (g, s);     /* service name of time */
				}
				else
				{
					/* TCP */
					if (result)
						daytime_stream (g, s);  /* service name of daytime */
					else
						machtime_stream (g, s); /* service name of time */
				}

				result = RETURN_OK;
				s_close (s);
			}

			/* if id was nonzero, then we have to inform inetd that we are done */
			if (*arg.id)
			{
				result = reply_inet (g, *arg.id);
			}

			cleanup_sockets ();

			FreeArgs (rdargs);
		}
		closelibs (g);
	}

	return result;
}

static void
daytime (struct global *g, char *str)
{
	struct DateTime dt;
	char day [20], date [20], time [20];

	dt.dat_Format  = FORMAT_DOS;
	dt.dat_Flags   = 0;
	dt.dat_StrDay  = day;
	dt.dat_StrTime = time;
	dt.dat_StrDate = date;
	DateStamp (&dt.dat_Stamp);
	DateToStr (&dt);
	SPrintf (g, str, "%s %s %s\r\n", day, date, time);
}


/* Return human-readable time of day */
static void
daytime_stream (struct global *g, int s)
{
	char buffer [128];

	daytime (g, buffer);
	send (s, buffer, strlen (buffer), 0);
}

static void
daytime_dg (struct global *g, int s)
{
	char buffer [128];
	struct sockaddr sa;
	int size;

	size = sizeof (sa);
	if (recvfrom (s, buffer, sizeof (buffer), 0, &sa, &size) < 0)
		return;
	daytime (g, buffer);
	sendto (s, buffer, strlen (buffer), 0, &sa, sizeof (sa));
}


/*
 * Return a machine readable date and time, in the form of the
 * number of seconds since midnight, Jan 1, 1900.  Since DateStamp
 * returns the number of seconds since midnight, Jan 1, 1978,
 * we must add 246144960 seconds.
 */


static long
machtime (struct global *g)
{
	struct DateStamp ds;
	long t;

	DateStamp (&ds);
	t = (ds.ds_Days * 86400) + (ds.ds_Minute * 60) + ((long)(ds.ds_Tick / 50));
	t += 2461449600;

	return t;
}


static void
machtime_stream (struct global *g, int s)
{
	long result;

	result = machtime (g);
	send (s, (char *) &result, sizeof (result), 0);
}

static void
machtime_dg (struct global *g, int s)
{
	long result;
	struct sockaddr sa;
	int size;

	size = sizeof (sa);
	if (recvfrom (s, (char *) &result, sizeof (result), 0, &sa, &size) < 0)
		return;

	result = machtime (g);
	sendto (s, (char *) &result, sizeof (result), 0, &sa, sizeof (sa));
}

static void SPrintf (struct global *g, char *buf, char *ctl, ...)
{
	RawDoFmt (ctl, (long *)(&ctl + 1), (void (*))"\x16\xC0\x4E\x75", buf);
}

static int
openlibs (struct global *g)
{
	if (g->d = (struct DosLibrary *) OpenLibrary ("dos.library", 0))
	{
		if (g->s = OpenLibrary ("inet:libs/socket.library", 0))
		{
			return 1;
		}
		CloseLibrary ((struct Library *) g->d);
		g->d = NULL;
	}
	return 0;
}

static void closelibs (struct global *g)
{
	/* don't call me if these libraries didn't get opened! */

	CloseLibrary ((struct Library *) g->d);
	CloseLibrary (g->s);

	g->d = NULL;
	g->s = NULL;

	return;
}

static int
reply_inet (struct global *g, long id)
{
	int
		result = RETURN_OK;
	struct MsgPort
		*replyport,
		*msgport;
	struct inetmsg
		inet_message;

	if (replyport = CreateMsgPort ())
	{
		inet_message.id = id;
		inet_message.msg.mn_Node.ln_Type = NT_MESSAGE;
		inet_message.msg.mn_Length = sizeof (struct inetmsg);
		inet_message.msg.mn_ReplyPort = replyport;

		Forbid ();
		if (msgport = FindPort ("inetd"))
		{
			PutMsg (msgport, (struct Message *) &inet_message);
			Permit ();
			/* we can't exit until we received a reply */
			WaitPort (replyport);
		}
		else
		{
			Permit ();
			s_syslog (LOG_ERR, "TIMED: Couldn't find inetd port\n");
			result = RETURN_FAIL;
		}
		DeleteMsgPort (replyport);
	}
	else
	{
		s_syslog (LOG_ERR, "TIMED: Couldn't create reply port\n");
		result = RETURN_FAIL;
	}

	return result;
}
