/* @(#)idcache.c	1.29 09/11/16 Copyright 1993, 1995-2009 J. Schilling */
#include <schily/mconfig.h>
#ifndef lint
static	UConst char sccsid[] =
	"@(#)idcache.c	1.29 09/11/16 Copyright 1993, 1995-2009 J. Schilling";
#endif
/*
 *	UID/GID caching functions
 *
 *	Copyright (c) 1993, 1995-2009 J. Schilling
 */
/*
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * See the file CDDL.Schily.txt in this distribution for details.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file CDDL.Schily.txt from this distribution.
 */

#include <schily/stdio.h>
#include <schily/standard.h>
#include <schily/utypes.h>
#include <schily/pwd.h>
#include <schily/grp.h>
#include <schily/string.h>
#include <schily/schily.h>

#define	TUNMLEN	32			/* Max. UID name len (POSIX)	*/
#define	TGNMLEN	32			/* Max. GID name len (POSIX)	*/

#define	C_SIZE	16			/* Cache size (cachee is static) */

typedef struct u_id {
	uid_t	uid;
	char	name[TUNMLEN+1];
	char	valid;
} uidc_t;

typedef struct g_id {
	gid_t	gid;
	char	name[TGNMLEN+1];
	char	valid;
} gidc_t;

LOCAL	uidc_t	uidcache[C_SIZE];
LOCAL	int	lastuidx;		/* Last index for new entry */

LOCAL	gidc_t	gidcache[C_SIZE];
LOCAL	int	lastgidx;		/* Last index for new entry */

LOCAL	uid_t	_uid_nobody;		/* Uid for user "nobody"    */
LOCAL	gid_t	_gid_nobody;		/* Gid for user "nobody"    */
LOCAL	BOOL	name_init = FALSE;

EXPORT	BOOL	ic_nameuid	__PR((char *name, int namelen, uid_t uid));
EXPORT	BOOL	ic_uidname	__PR((char *name, int namelen, uid_t *uidp));
EXPORT	BOOL	ic_namegid	__PR((char *name, int namelen, gid_t gid));
EXPORT	BOOL 	ic_gidname	__PR((char *name, int namelen, gid_t *gidp));
LOCAL	void	nameinit	__PR((void));
EXPORT	uid_t	ic_uid_nobody	__PR((void));
EXPORT	gid_t	ic_gid_nobody	__PR((void));

/*
 * Get name from uid
 */
#ifdef	PROTOTYPES
EXPORT BOOL
ic_nameuid(char *name, int namelen, uid_t uid)
#else
EXPORT BOOL
ic_nameuid(name, namelen, uid)
	char	*name;
	int	namelen;
	uid_t	uid;
#endif
{
	struct passwd	*pw;
	register int	i;
	register uidc_t	*idp;

	for (i = 0, idp = uidcache; i < C_SIZE; i++, idp++) {
		if (idp->valid == 0)		/* Entry not yet filled */
			break;
		if (idp->uid == uid)
			goto out;
	}
	idp = &uidcache[lastuidx++];		/* Round robin fill next ent */
	if (lastuidx >= C_SIZE)
		lastuidx = 0;

	idp->uid = uid;
	idp->name[0] = '\0';
	idp->valid = 1;
	if ((pw = getpwuid(uid)) != NULL) {
		strncpy(idp->name, pw->pw_name, TUNMLEN);
		idp->name[TUNMLEN] = '\0';
		/*
		 * XXX We should find a better method than shortening the cache
		 */
		if (namelen <= (TUNMLEN+1))
			idp->name[namelen-1] = 0;
	}
out:
	strcpy(name, idp->name);
	return (name[0] != '\0');
}

/*
 * Get uid from name
 */
EXPORT BOOL
ic_uidname(name, namelen, uidp)
	char	*name;
	int	namelen;
	uid_t	*uidp;
{
	struct passwd	*pw;
	register int	len = namelen > TUNMLEN?TUNMLEN:namelen;
	register int	i;
	register uidc_t	*idp;

	if (name[0] == '\0') {
		*uidp = ic_uid_nobody();	/* Return UID_NOBODY */
		return (FALSE);
	}

	for (i = 0, idp = uidcache; i < C_SIZE; i++, idp++) {
		if (idp->valid == 0)		/* Entry not yet filled */
			break;
		if (name[0] == idp->name[0] &&
					strncmp(name, idp->name, len) == 0) {
			*uidp = idp->uid;
			if (idp->valid == 2) {	/* Name not found */
				*uidp = ic_uid_nobody(); /* Return UID_NOBODY */
				return (FALSE);
			}
			return (TRUE);
		}
	}
	idp = &uidcache[lastuidx++];		/* Round robin fill next ent */
	if (lastuidx >= C_SIZE)
		lastuidx = 0;

	idp->uid = 0;
	idp->name[0] = '\0';
	strncpy(idp->name, name, len);
	idp->name[len] = '\0';
	idp->valid = 1;
	if ((pw = getpwnam(idp->name)) != NULL) {
		idp->uid = pw->pw_uid;
		*uidp = idp->uid;
		return (TRUE);
	} else {
		idp->valid = 2;			/* Mark name as not found */
		*uidp = ic_uid_nobody();	/* Return UID_NOBODY */
		return (FALSE);
	}
}

/*
 * Get name from gid
 */
#ifdef	PROTOTYPES
EXPORT BOOL
ic_namegid(char *name, int namelen, gid_t gid)
#else
EXPORT BOOL
ic_namegid(name, namelen, gid)
	char	*name;
	int	namelen;
	gid_t	gid;
#endif
{
	struct group	*gr;
	register int	i;
	register gidc_t	*idp;

	for (i = 0, idp = gidcache; i < C_SIZE; i++, idp++) {
		if (idp->valid == 0)		/* Entry not yet filled */
			break;
		if (idp->gid == gid)
			goto out;
	}
	idp = &gidcache[lastgidx++];		/* Round robin fill next ent */
	if (lastgidx >= C_SIZE)
		lastgidx = 0;

	idp->gid = gid;
	idp->name[0] = '\0';
	idp->valid = 1;
	if ((gr = getgrgid(gid)) != NULL) {
		strncpy(idp->name, gr->gr_name, TGNMLEN);
		idp->name[TGNMLEN] = '\0';
		/*
		 * XXX We should find a better method than shortening the cache
		 */
		if (namelen <= (TGNMLEN+1))
			idp->name[namelen-1] = 0;
	}
out:
	strcpy(name, idp->name);
	return (name[0] != '\0');
}

/*
 * Get gid from name
 */
EXPORT BOOL
ic_gidname(name, namelen, gidp)
	char	*name;
	int	namelen;
	gid_t	*gidp;
{
	struct group	*gr;
	register int	len = namelen > TGNMLEN?TGNMLEN:namelen;
	register int	i;
	register gidc_t	*idp;

	if (name[0] == '\0') {
		*gidp = ic_gid_nobody();	/* Return GID_NOBODY */
		return (FALSE);
	}

	for (i = 0, idp = gidcache; i < C_SIZE; i++, idp++) {
		if (idp->valid == 0)		/* Entry not yet filled */
			break;
		if (name[0] == idp->name[0] &&
					strncmp(name, idp->name, len) == 0) {
			*gidp = idp->gid;
			if (idp->valid == 2) {	/* Name not found */
				*gidp = ic_gid_nobody(); /* Return GID_NOBODY */
				return (FALSE);
			}
			return (TRUE);
		}
	}
	idp = &gidcache[lastgidx++];		/* Round robin fill next ent */
	if (lastgidx >= C_SIZE)
		lastgidx = 0;

	idp->gid = 0;
	idp->name[0] = '\0';
	strncpy(idp->name, name, len);
	idp->name[len] = '\0';
	idp->valid = 1;
	if ((gr = getgrnam(idp->name)) != NULL) {
		idp->gid = gr->gr_gid;
		*gidp = idp->gid;
		return (TRUE);
	} else {
		idp->valid = 2;			/* Mark name as not found */
		*gidp = ic_gid_nobody();	/* Return GID_NOBODY */
		return (FALSE);
	}
}

#include <schily/param.h>

#ifndef	UID_NOBODY
#define	UID_NOBODY	65534		/* The old SunOS-4.x and *BSD value */
#endif
#ifndef	GID_NOBODY
#define	GID_NOBODY	65534		/* The old SunOS-4.x and *BSD value */
#endif

LOCAL void
nameinit()
{
	char	*name;
	int	namelen;
	uid_t	uid;
	gid_t	gid;

	/*
	 * Make sure that uidname()/gidname() do not call nameinit().
	 */
	name_init = TRUE;

	name = "nobody";
	namelen = strlen(name);
	if (!ic_uidname(name, namelen, &uid))
		uid = UID_NOBODY;
	_uid_nobody = uid;

	if (!ic_gidname(name, namelen, &gid))
		gid = GID_NOBODY;
	_gid_nobody = gid;
}

EXPORT uid_t
ic_uid_nobody()
{
	if (!name_init)
		nameinit();
	return (_uid_nobody);
}

EXPORT gid_t
ic_gid_nobody()
{
	if (!name_init)
		nameinit();
	return (_gid_nobody);
}
