/*
 *   Copyright (c) 1998 David Parsons. All rights reserved.
 *   
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions
 *   are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *  3. All advertising materials mentioning features or use of this
 *     software must display the following acknowledgement:
 *     
 *   This product includes software developed by David Parsons
 *   (orc@pell.chi.il.us)
 *
 *  4. My name may not be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *     
 *  THIS SOFTWARE IS PROVIDED BY DAVID PARSONS ``AS IS'' AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 *  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 *  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID
 *  PARSONS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 *  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 *  IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 *  THE POSSIBILITY OF SUCH DAMAGE.
 */

static const char rcsid[] = "$Id: s.Passwd.cc 1.3 01/03/11 17:58:55-08:00 orc@downbelow.pell.portland.or.us $";

/*
 * password file mangling routines
 */

#include "Passwd.H"
#include <malloc.h>
#include <stdlib.h>
#include <string.h>

typedef int (*vvfv) (const void*, const void*);

/*
 * Passwd(): open the password file.
 */
Passwd::Passwd()
{
    struct passwd *pwd;

    pwinfo = (struct pw_pwinfo*)malloc(1);
    nrpw = 0;

    while ((pwd = getpwent()) != 0) {
	pwinfo = (struct pw_pwinfo*)realloc(pwinfo, (2+nrpw)*sizeof pwinfo[0]);

	pwinfo[nrpw].pwd.pw_name = strdup(pwd->pw_name);
	pwinfo[nrpw].pwd.pw_name = strdup(pwd->pw_name);
	pwinfo[nrpw].pwd.pw_passwd = strdup(pwd->pw_passwd);
	pwinfo[nrpw].pwd.pw_uid = pwd->pw_uid;
	pwinfo[nrpw].pwd.pw_gid = pwd->pw_gid;
	pwinfo[nrpw].pwd.pw_gecos = strdup(pwd->pw_gecos);
	pwinfo[nrpw].pwd.pw_dir = strdup(pwd->pw_dir);
	pwinfo[nrpw].pwd.pw_shell = pwd->pw_shell ? strdup(pwd->pw_shell) : 0;
	pwinfo[nrpw].status = OK;
	pwinfo[nrpw].checksum = checksum(pwinfo[nrpw]);

	nrpw++;
    }
    qsort(pwinfo, nrpw, sizeof pwinfo[0], (vvfv) pwcmp);
} /* Passwd::Passwd */

/*
 * Goodbye cruel password file
 */
Passwd::~Passwd()
{
    int x;

    for (x = 0; x < nrpw; x++) {
	free(pwinfo[nrpw].pwd.pw_name);
	free(pwinfo[nrpw].pwd.pw_passwd);
	free(pwinfo[nrpw].pwd.pw_gecos);
	free(pwinfo[nrpw].pwd.pw_dir);
	free(pwinfo[nrpw].pwd.pw_shell);
    }
    if (pwinfo)
	free(pwinfo);
} /* Passwd::~Passwd */


/*
 * [] gets a user by name
 */
struct passwd *
Passwd::operator [] (char *index)
{
    return find(index);
} /* Passwd::oper [] (char*) */


/*
 * [] gets a user by uid
 */
struct passwd *
Passwd::operator [] (int uid)
{
    return find(uid);
} /* Passwd::oper [] (int) */


/*
 * find() gets a user by name, procedurally
 */
struct passwd *
Passwd::find(char *index)
{
    int x;
#if 0
    struct pw_pwinfo ent;
    struct pw_pwinfo *result;

    ent.pwd.pw_name = index;

    result = bsearch(&ent, pwinfo, nrpw, sizeof *pwinfo, (vvfv) pwcmp);

    return result ? &(result->pwd) : 0;
#else
    for (x=0; x < nrpw; x++)
	if (pwinfo[x].status != DELETED && strcmp(index, pwinfo[x].pwd.pw_name) == 0)
	    return &(pwinfo[x].pwd);
    return 0;
#endif
} /* find (char*) */


/*
 * find() gets a user by uid, procedurally
 */
struct passwd *
Passwd::find(int uid)
{
    int x;

    for (x = 0; x < nrpw; x++)
	if (pwinfo[x].status != DELETED && pwinfo[x].pwd.pw_uid == uid)
	    return &(pwinfo[x].pwd);

    return 0;
} /* Passwd::find (int) */


/*
 * Utility functions
 */


/*
 * checksum() builds a checksum of all the fields in the struct passwd
 */
int
Passwd::checksum(struct pw_pwinfo &ent)
{
    int chksum;

    chksum = chk(ent.pwd.pw_name, strlen(ent.pwd.pw_name));
    chksum ^= chk(ent.pwd.pw_passwd, strlen(ent.pwd.pw_passwd));
    chksum ^= chk((char*)&(ent.pwd.pw_uid), sizeof ent.pwd.pw_uid);
    chksum ^= chk((char*)&(ent.pwd.pw_gid), sizeof ent.pwd.pw_gid);
    chksum ^= chk(ent.pwd.pw_gecos, strlen(ent.pwd.pw_gecos));
    chksum ^= chk(ent.pwd.pw_dir, strlen(ent.pwd.pw_dir));
    chksum ^= chk(ent.pwd.pw_shell, strlen(ent.pwd.pw_shell));

    return chksum;
} /* Passwd::checksum */


/*
 * chk() is a subchecksummer for checksum()
 */
int
Passwd::chk(char *str, int len)
{
    int chksum = 0;

    while (len-- > 0) {
	chksum <<= 1;
	chksum ^= (unsigned char)(*str++);
    }
    return chksum;
} /* Passwd::chk */


/*
 * pwcmp() compares two password entries
 */
int
Passwd::pwcmp(struct pw_pwinfo *a, struct pw_pwinfo *b)
{
    return strcmp(a->pwd.pw_name, b->pwd.pw_name);
} /* Passwd::pwcmp */



#ifdef TEST
main(int argc, char** argv)
{
    Passwd password;
    int x;
    struct passwd *user;

    for (x = 1; x < argc; x++) {

	if ((user = password[argv[x]]) != 0) {
	    printf("[%s, %s, %d, %d, %s, %s, %s]\n",
		user->pw_name, user->pw_passwd, user->pw_uid,
		user->pw_gid, user->pw_gecos, user->pw_dir,
		user->pw_shell);
	}
	else
	    printf("%s: no such user\n", argv[x]);
    }
}
#endif
