/* Compile with cc -std -o setexp setexp.c -lsecurity -laud -lm */
/* For T4.0-1 and later, add -ldb before the -lm */
/* For T5.0-18 and later, add -levm -lclu before the -ldb */

/* Usage:
 *	setexp [+]{interval} user...

 * The interval specification can be a mixture of several time units,
 * such as "2d8h4m" for 2 days, 8 hours, and 4 minutes.  The legal
 * suffixes are "w" for weeks, "d" for days, "h" for hours, "m" for
 * minutes, and "s" for seconds.  If no suffix is seen, "d" is assumed.
 * If the interval is given with the leading "+" to mark it as a relative
 * interval, the target users' u_expdate fields will be incremented by
 * the given amount if they are already set.  If it's not relative, or
 * the u_expdate field is not already set, then the specified interval
 * from 'now' will be set as the u_expdate value.
 */

/* get most of the world's header files */
#include <siad.h>
#include <prot.h>

/*
 * Set up some defines for determining whether we're running (at least)
 * as recent a version as t4.0-1 so we can work with old or new interfaces
 * with "mere" recompilation, rather than requiring code changes.
 */

#ifdef AUTH_ESCAP_API_VER

/* the es interfaces exist--use them */
#define	GETPRPWNAM	getespwnam
#define	GETPRPWNAMSTR	"getespwnam: "
#define	PUTPRPWNAM	putespwnam
#define	PUTPRPWNAMSTR	"putespwnam: "
#define	PRPASSWD	struct es_passwd
#define	UFLDx		ufld->
#define	UFLGx		uflg->
#define	SFLDx		sfld->
#define	SFLGx		sflg->

#define	USE_ESCAP	1

#else /* old interfaces only */

#define	GETPRPWNAM	getprpwnam
#define	GETPRPWNAMSTR	"getprpwnam: "
#define	PUTPRPWNAM	putprpwnam
#define	PUTPRPWNAMSTR	"putprpwnam: "
#define	PRPASSWD	struct pr_passwd
#define	UFLDx		ufld.
#define	UFLGx		uflg.
#define	SFLDx		sfld.
#define	SFLGx		sflg.

#define	USE_ESCAP	0

#endif	/* check for interface type */

/* forward declaration */
int get_interval __((const uchar_t *, time_t *));

int
main(int argc, char **argv)
{
	PRPASSWD *prp;
#if !USE_ESCAP
	PRPASSWD pbuf;
#endif
	char *ispec;
	time_t now;
	time_t exp_time;
	time_t interval;
	int relative;
	int xstat;		/* exit status */
	int i;

	/* required libsecurity overhead, which really ought to get fixed
	 * one of these days....
	 */
	set_auth_parameters(argc, argv);
	initprivs();

	/* if necessary, a getopt step could be added here.  verbose versus
	 * silent comes to mind.
	 */
	if (argc < 3) {
		(void) fprintf(stderr, "usage: %s [+]{interval} users...\n",
			       argv[0]);
		return 1;
	}

	/* handle the interval spec */
	if (argv[1][0] == '+') {
		relative = 1;
		ispec = &argv[1][1];
	}
	else {
		relative = 0;
		ispec = &argv[1][0];
	}
	if (!get_interval((uchar_t*)ispec, &interval)) {
		(void) fprintf(stderr, "invalid interval specified: %s\n",
			       ispec);
		return 1;
	}
	/* cache non-relative value, just in case */
	now = time((time_t*)NULL);
	exp_time = now + interval;

	/* loop over users applying the expiration interval */
	xstat = 0;		/* assume success until known otherwise */
	for (i = 2; i < argc; i++) {
		prp = GETPRPWNAM(argv[i]);
		if (!prp) {
			(void) fputs(GETPRPWNAMSTR, stderr);
			(void) fflush(stderr);
			perror(argv[i]);
			xstat = 1;
			continue;
		}
#if USE_ESCAP
		prp = copyespwent(prp);
		if (!prp) {
			(void) fputs("copyespwent: ", stderr);
			(void) fflush(stderr);
			perror(argv[i]);
			xstat = 1;
			continue;
		}
#else /* pre-v4 */
		pbuf = *prp;
		prp = &pbuf;
#endif
		if (relative && prp->UFLGx fg_expdate) {
			prp->UFLDx fd_expdate += interval;
		}
		else {
			prp->UFLDx fd_expdate = exp_time;
			prp->UFLGx fg_expdate = 1; /* mark it valid */
		}
		if (PUTPRPWNAM(argv[i], prp) != 1) {
			(void) fputs(PUTPRPWNAMSTR, stderr);
			(void) fflush(stderr);
			perror(argv[i]);
			xstat = 1;
		}
		else {
                        (void) printf("%-10s%s", argv[i],
                                      ctime(&prp->UFLDx fd_expdate));
		}
#if USE_ESCAP
		free(prp);
#endif
	}

	return xstat;
}

/* stolen and massaged from sendmail-8.7.1 */

/*
**  get_interval -- get a time interval
**
**	Takes a time as an ascii string with a trailing character
**	giving units:
**	  s -- seconds
**	  m -- minutes
**	  h -- hours
**	  d -- days (default)
**	  w -- weeks
**	For example, "3d12h" is three and a half days.
**
**	Parameters:
**		p -- pointer to ascii time.
**		t -- pointer to resulting time.
**
**	Returns:
**		1 for well-formed string, 0 for bad input.
**
**	Side Effects:
**		*t is zeroed, and the interval accumulated in it.
*/

int
get_interval(p, t)
const uchar_t *p;
time_t *t;
{
	time_t n;
	uchar_t c;

	*t = 0;
	while (*p != '\0')
	{
		n = 0;
		if (*p && !(isascii(*p) && isdigit(*p))) {
			/* each sub-expression must have digit */
			return 0;
		}
		while ((c = *p++) != '\0' && isascii(c) && isdigit(c))
			n = n * 10 + (c - '0');
		if (c == '\0')
		{
			c = 'd';
			p--;
		}
		else if (strchr("wdhms", c) == NULL)
		{
			return 0;
		}
		switch ((int)c)
		{
		  case 'w':		/* weeks */
			n *= 7;
			/*FALLTHROUGH*/
		  case 'd':		/* days */
		  default:
			n *= 24;
			/*FALLTHROUGH*/
		  case 'h':		/* hours */
			n *= 60;
			/*FALLTHROUGH*/
		  case 'm':		/* minutes */
			n *= 60;
			/*FALLTHROUGH*/
		  case 's':		/* seconds */
			break;
		}
		*t += n;
	}

	return 1;
}


