/*	Copyright (c) 1984 AT&T	*/
/*	  All Rights Reserved  	*/

/*	THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF AT&T	*/
/*	The copyright notice above does not evidence any   	*/
/*	actual or intended publication of such source code.	*/

#ident	"$Header: getspent.c 1.15 90/05/18 $@(#)libsec:getspent.c	1.1"

/*
 * This code has been modified to support the optional shadow password
 * hashing scheme.  Routine changed is getspnam().
 */

/* 
 * $Copyright:	$
 * Copyright (c) 1984, 1985, 1986, 1987, 1988, 1989, 1990 
 * Sequent Computer Systems, Inc.   All rights reserved.
 *  
 * This software is furnished under a license and may be used
 * only in accordance with the terms of that license and with the
 * inclusion of the above copyright notice.   This software may not
 * be provided or otherwise made available to, or used by, any
 * other person.  No title to or ownership of the software is
 * hereby transferred.
 *
 */

#include <sys/param.h>
#include <stdio.h>
#include <shadow.h>
#include <ndbm.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/fcntl.h>
#include <errno.h>

static FILE *spf = NULL ;
static char line[BUFSIZ+1] ;
static struct spwd spwd ;
extern int errno;

void
setspent()
{
	if(spf == NULL) {
		spf = fopen(SHADOW, "r") ;
	}
	else
		rewind(spf) ;
}

void
endspent()
{
	if(spf != NULL) {
		(void) fclose(spf) ;
		spf = NULL ;
	}
}

static char *
spskip(p)
register char *p ;
{
	while(*p && *p != ':' && *p != '\n')
		++p ;
	if(*p == '\n')
		*p = '\0' ;
	else if(*p)
		*p++ = '\0' ;
	return(p) ;
}

/* 	The getspent function will return a NULL for an end of 
	file indication or a bad entry
*/
struct spwd *
getspent()
{
	extern struct spwd *fgetspent() ;

	if(spf == NULL) {
		if((spf = fopen(SHADOW, "r")) == NULL)
			return (NULL) ;
	}
	return (fgetspent(spf)) ;
}

struct spwd *
fgetspent(f)
FILE *f ;
{
	register char *p ;
	char *end ;
	long	x, strtol() ;
	void *memchr() ;

	p = fgets(line, BUFSIZ, f) ;
	if(p == NULL)
		return (NULL) ;
	spwd.sp_namp = p ;
	p = spskip(p) ;
	spwd.sp_pwdp = p ;
	p = spskip(p) ;

	x = strtol(p, &end, 10) ;
	if (end != memchr(p, ':', strlen(p))) {
		/* check for numeric value */
		errno = EINVAL;
		return (NULL) ;
	}
	if (end == p)	
		spwd.sp_lstchg = -1 ;
	else		
		spwd.sp_lstchg = x ;
	p = spskip(p) ;
	x = strtol(p, &end, 10) ;	
	if (end != memchr(p, ':', strlen(p))) {
		/* check for numeric value */
		errno = EINVAL;
		return (NULL) ;
	}
	if (end == p)	
		spwd.sp_min = -1 ;
	else		
		spwd.sp_min = x ;
	p = spskip(p) ;
	x = strtol(p, &end, 10) ;	
	/*
	 * The last character is either a newline, or
	 * another separator.  This modification is to
	 * support layered product binary compatability
	 * on the B1 DYNIX product.
	 */
	if ((end != memchr(p, '\n', strlen(p))) &&
		(end != memchr(p, ':', strlen(p)))) {
		/* check for numeric value */
		errno = EINVAL;
		return (NULL) ;
	}
	if (end == p)	
		spwd.sp_max = -1 ;
	else		
		spwd.sp_max = x ;

	return(&spwd) ;
}

struct spwd *
getspnam(name)
char	*name ;
{
	register struct spwd *p ;

#ifndef CCS

	DBM *db;
	datum d, k;
	int fd, c;
	FILE *fp;
	union {
		char s_c[4];
		int  s_s;
	}s;
	struct stat sb1, sb2;
	char buf[BUFSIZ];

	/* Try and open the data base for the shadow file. */

	db = dbm_open(SHADOW_DBM, O_RDONLY, 0444);
	if (db == (DBM *) 0)
		goto nohash;

	/*
	 * Shadow data base exists.  Look up the desired name.
	 * If not found retrieve the shadow status from the data base
	 * and compare with status now.  See if data file size matches
	 * that recorded in the data base and and that the modification
	 * time of the data base is as recent as the data file.
	 */

	strcpy(buf, "n_");
	strcat(buf, name);
	k.dptr = buf;
	k.dsize = strlen(buf);
	d = dbm_fetch(db, k);
	if (d.dptr == (char *) 0) {		/* not found */
		if (dbm_error(db)) {
			(void) dbm_close(db);
			goto rebuild;
		}
		if (strcmp(name, "root") == 0) {
			(void) dbm_close(db);
			goto nohash;
		}
		k.dptr = "pw_stat_struct";
		k.dsize = strlen("pw_stat_struct");
		d = dbm_fetch(db, k);
		if (d.dptr == (char *) 0) {	/* should not happen */
			(void) dbm_close(db);
			goto nohash;
		}
		(void) memcpy(&sb1, d.dptr, sizeof(sb1));
		(void) dbm_close(db);
		if (stat(SHADOW, &sb2) != 0)	/* should not happen */
			goto nohash;
		if (sb1.st_size == sb2.st_size) {
			strcpy(buf, SHADOW_DBM);
			strcat(buf, ".pag");
			if (stat(buf, &sb1) != 0)
				goto nohash;
			if (sb1.st_mtime < sb2.st_mtime)
				goto rebuild;
			return((struct spwd *) NULL);
		} else
			goto rebuild;
	} else {				/* found */
		s.s_c[0] = d.dptr[0];
		s.s_c[1] = d.dptr[1];
		s.s_c[2] = d.dptr[2];
		s.s_c[3] = d.dptr[3];
		(void) dbm_close(db);
		if (s.s_s <= 0)
			goto nohash;
		if ((fp = fopen(SHADOW, "r")) == NULL)
			goto nohash;
		s.s_s -= 1;		/* back up past newline */
		if (fseek(fp, s.s_s, 0) < 0) {
			fclose(fp);
			goto rebuild;
		}
		c = getc(fp);
		if (c == -1) {
			fclose(fp);
			goto nohash;
		}
		if (c != '\n') {
			fclose(fp);
			goto rebuild;
		}
		p = fgetspent(fp);
		(void) fclose(fp);
		if (p == (struct spwd *) NULL)
			goto rebuild;
		if (strcmp(p->sp_namp, name) == 0)
			return(p);
		/* going to fall into rebuild */
	}
	
rebuild:
	if ((fd = open(PWH_PIPE, O_WRONLY|O_NDELAY, 0)) != -1) {
		(void) write(fd, "s", 1);
		(void) close(fd);
	}

nohash:

#endif	/* not CCS */

	setspent() ;
	while ( (p = getspent()) && strcmp(name, p->sp_namp) )
		;
	endspent() ;
	return (p) ;
}

int
putspent(p, f)
register struct spwd *p ;
register FILE *f ;
{
	(void) fprintf ( f, "%s:%s:", p->sp_namp, p->sp_pwdp ) ;
	if ( p->sp_lstchg >= 0 )
	   (void) fprintf ( f, "%d:", p->sp_lstchg ) ;
	else
	   (void) fprintf ( f, ":" ) ;
	if ( p->sp_min >= 0 )
	   (void) fprintf ( f, "%d:", p->sp_min ) ;
	else
	   (void) fprintf ( f, ":" ) ;
	if ( p->sp_max >= 0 )
	   (void) fprintf ( f, "%d\n", p->sp_max ) ;
	else
	   (void) fprintf ( f, "\n" ) ;

	return(ferror(f)) ;
}

