/*
 *
 * cache.c
 *
 * NIS map cache maintance code
 *
 * Author: Landon Fuller <landonblue@circus-foundation.org>
 *
 * Copyright (c) 2000-2001 InfoSpace, Inc. 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 InfoSpace, Inc.
 *         and its contributors.
 * 4. Neither the name of InfoSpace, Inc nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * ``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 THE FOUNDATION OR CONTRIBUTORS
 * 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.
 *
 */

#include "../../../autoconf.h"

#include <stdlib.h>
#include <stdio.h>
#ifdef HAVE_STRINGS_H
#include <strings.h>
#else
#include <string.h>
#endif
#include <errno.h>
#include <pthread.h>
#include <semaphore.h>
#include <lber.h>
#include <ldap.h>

#include "yp.h"

#include "ldap.h"
#include "cache.h"
extern struct ldaprefs ldaprefs;

static char *user_attributes [] = {
	"uid",
	"userpassword",
	"uidnumber",
	"gidnumber",
	"gecos",
	"homedirectory",
	"loginshell",
	NULL
};

static char *group_attributes [] = {
	"cn",
	"userpassword",
	"gidnumber",
	"memberuid",
	NULL
};

static char *nis_attributes [] = {
	"cn",
	"nismapentry",
	"nismapname",
	NULL
};

static char *fix_password(char **values)
{
	char **valptr;
	char *pwd = NULL;
	char *buffer;
	
	for (valptr = values; *valptr != NULL; valptr++)
	{
		if(!strncasecmp(*valptr, CRYPT, CRYPT_SIZE))
		{
			pwd = *valptr;
			break;
		}
		
	}
	if (pwd == NULL)
	{
		pwd = "x";
	} else {
		pwd += CRYPT_SIZE;
	}

	buffer = safe_strdup(pwd);

	return(buffer);
}

static void fill_pwinfo(struct pwinfo *pwinfo)
{
	pwinfo->uid = NULL;
	pwinfo->userpassword = NULL;
	pwinfo->uidnumber = NULL;
	pwinfo->gidnumber = NULL;
	pwinfo->gecos = NULL;
	pwinfo->homedirectory = NULL;
	pwinfo->loginshell = NULL;
	pwinfo->size = 0;
}

static void fill_gwinfo(struct gwinfo *gwinfo)
{
	gwinfo->cn = NULL;
	gwinfo->userpassword = NULL;
	gwinfo->gidnumber = NULL;
	gwinfo->memberuid = NULL;
	gwinfo->size = 0;
}

static void fill_nisinfo(struct nisinfo *nisinfo)
{
	nisinfo->cn = NULL;
	nisinfo->nismapentry = NULL;
	nisinfo->nismapname = NULL;
}

static int check_pwinfo(struct pwinfo *pwinfo)
{
	if (!pwinfo->uid)
	{
		return (YP_YPERR);
	}
	if (!pwinfo->userpassword)
	{
		return (YP_YPERR);
	}
	if (!pwinfo->uidnumber)
	{
		return (YP_YPERR);
	}
	if (!pwinfo->gidnumber)
	{
		return (YP_YPERR);
	}
	if (!pwinfo->gecos)
	{
		pwinfo->gecos = safe_strdup("");
	}
	if (!pwinfo->homedirectory)
	{
		return (YP_YPERR);
	}
	if (!pwinfo->loginshell)
	{
		return (YP_YPERR);
	}
	return (YP_TRUE);
}

static int check_gwinfo(struct gwinfo *gwinfo)
{
	if (!gwinfo->cn)
	{
		return (YP_YPERR);
	}
	if (!gwinfo->userpassword)
	{
		return (YP_YPERR);
	}
	if (!gwinfo->gidnumber)
	{
		return (YP_YPERR);
	}
	if (!gwinfo->memberuid)
	{
		gwinfo->memberuid = safe_strdup("");
	}
	return (YP_TRUE);
}

static int check_nisinfo(struct nisinfo *nisinfo)
{
	if (!nisinfo->cn)
	{
		return (YP_YPERR);
	}
	if (!nisinfo->nismapentry)
	{
		return (YP_YPERR);
	}
	if (!nisinfo->nismapname)
	{
		return (YP_YPERR);
	}
	return (YP_TRUE);
}

static void clean_pwinfo(struct pwinfo *pwinfo)
{
	if(pwinfo->uid)
		free(pwinfo->uid);
	if(pwinfo->userpassword)
		free(pwinfo->userpassword);
	if(pwinfo->uidnumber)
		free(pwinfo->uidnumber);
	if(pwinfo->gidnumber)
		free(pwinfo->gidnumber);
	if(pwinfo->gecos)
		free(pwinfo->gecos);
	if(pwinfo->homedirectory)
		free(pwinfo->homedirectory);
	if(pwinfo->loginshell)
		free(pwinfo->loginshell);
	free(pwinfo);
}

static void clean_gwinfo(struct gwinfo *gwinfo)
{
	if (gwinfo->cn)
		free(gwinfo->cn);
	if (gwinfo->userpassword)
		free(gwinfo->userpassword);
	if (gwinfo->gidnumber)
		free(gwinfo->gidnumber);
	if (gwinfo->memberuid)
		free(gwinfo->memberuid);
	free(gwinfo);
}

static void clean_nisinfo(struct nisinfo *nisinfo)
{
	if(nisinfo->cn)
		free(nisinfo->cn);
	if(nisinfo->nismapentry)
		free(nisinfo->nismapentry);
	if(nisinfo->nismapname)
		free(nisinfo->nismapname);
	free(nisinfo);
}


int assemble_passwd(valdat *val, LDAPMessage *user, struct pwinfo *pwinfo)
{
	ypstat rval;
	char *attr;
	BerElement *ber = NULL;

	rval = YP_YPERR;
	fill_pwinfo(pwinfo);
	
	for (attr = ldap_first_attribute (ldaprefs.ldap, user, &ber); attr; attr = ldap_next_attribute (ldaprefs.ldap, user, ber))
	{
		char **values;

		values = ldap_get_values (ldaprefs.ldap, user, attr);
		if (values)
		{
			if(!strcasecmp(attr, "uid"))
			{
				pwinfo->uid = safe_strdup(values[0]);
				pwinfo->size += strlen(values[0]);
			} else
			if(!strcasecmp(attr, "userpassword"))
			{
				pwinfo->userpassword = safe_strdup("*");
				pwinfo->size += strlen("*");
			} else
			if(!strcasecmp(attr, "uidnumber"))
			{
				pwinfo->uidnumber = safe_strdup(values[0]);
				pwinfo->size += strlen(values[0]);
			} else
			if(!strcasecmp(attr, "gidnumber"))
			{
				pwinfo->gidnumber = safe_strdup(values[0]);
				pwinfo->size += strlen(values[0]);
			} else
			if(!strcasecmp(attr, "gecos"))
			{
				pwinfo->gecos = safe_strdup(values[0]);
				pwinfo->size += strlen(values[0]);
			} else
			if(!strcasecmp(attr, "homedirectory"))
			{
				pwinfo->homedirectory = safe_strdup(values[0]);
				pwinfo->size += strlen(values[0]);
			} else
			if(!strcasecmp(attr, "loginshell"))
			{
				pwinfo->loginshell = safe_strdup(values[0]);
				pwinfo->size += strlen(values[0]);
			}
			ldap_value_free(values);
		}
	}
	if (check_pwinfo(pwinfo) == YP_TRUE)
	{
		val->valdat_len = pwinfo->size + PWLINE_SIZE;
		val->valdat_val = safe_malloc(val->valdat_len + 1);
		sprintf(val->valdat_val, PWLINE, pwinfo->uid, pwinfo->userpassword, pwinfo->uidnumber, pwinfo->gidnumber, pwinfo->gecos, pwinfo->homedirectory, pwinfo->loginshell);
		rval = YP_TRUE;
	} else {
		rval = YP_NOKEY;
	}

	return (rval);
}

int assemble_master_passwd(valdat *val, LDAPMessage *user, struct pwinfo *pwinfo)
{
	ypstat rval;
	char *attr;
	BerElement *ber = NULL;

	rval = YP_YPERR;
	fill_pwinfo(pwinfo);
	
	for (attr = ldap_first_attribute (ldaprefs.ldap, user, &ber); attr; attr = ldap_next_attribute (ldaprefs.ldap, user, ber))
	{
		char **values;

		values = ldap_get_values (ldaprefs.ldap, user, attr);
		if (values)
		{
			if(!strcasecmp(attr, "uid"))
			{
				pwinfo->uid = safe_strdup(values[0]);
				pwinfo->size += strlen(values[0]);
			} else
			if(!strcasecmp(attr, "userpassword"))
			{
				pwinfo->userpassword = fix_password(values);
				pwinfo->size += strlen(pwinfo->userpassword);
			} else
			if(!strcasecmp(attr, "uidnumber"))
			{
				pwinfo->uidnumber = safe_strdup(values[0]);
				pwinfo->size += strlen(values[0]);
			} else
			if(!strcasecmp(attr, "gidnumber"))
			{
				pwinfo->gidnumber = safe_strdup(values[0]);
				pwinfo->size += strlen(values[0]);
			} else
			if(!strcasecmp(attr, "gecos"))
			{
				pwinfo->gecos = safe_strdup(values[0]);
				pwinfo->size += strlen(values[0]);
			} else
			if(!strcasecmp(attr, "homedirectory"))
			{
				pwinfo->homedirectory = safe_strdup(values[0]);
				pwinfo->size += strlen(values[0]);
			} else
			if(!strcasecmp(attr, "loginshell"))
			{
				pwinfo->loginshell = safe_strdup(values[0]);
				pwinfo->size += strlen(values[0]);
			}
			ldap_value_free(values);
		}
	}
	if (check_pwinfo(pwinfo) == YP_TRUE)
	{
		val->valdat_len = pwinfo->size + MPWLINE_SIZE;
		val->valdat_val = safe_malloc(val->valdat_len + 1);
		sprintf(val->valdat_val, MPWLINE, pwinfo->uid, pwinfo->userpassword, pwinfo->uidnumber, pwinfo->gidnumber, pwinfo->gecos, pwinfo->homedirectory, pwinfo->loginshell);
		rval = YP_TRUE;
	} else {
		rval = YP_NOKEY;
	}

	return (rval);
}

int assemble_group(valdat *val, LDAPMessage *group, struct gwinfo *gwinfo)
{
	ypstat rval;
	char *attr;
	BerElement *ber = NULL;

	rval = YP_YPERR;
	fill_gwinfo(gwinfo);
	
	for (attr = ldap_first_attribute (ldaprefs.ldap, group, &ber); attr; attr = ldap_next_attribute (ldaprefs.ldap, group, ber))
	{
		char **values;

		values = ldap_get_values (ldaprefs.ldap, group, attr);
		if (values)
		{
			if(!strcasecmp(attr, "cn"))
			{
				gwinfo->cn = safe_strdup(values[0]);
				gwinfo->size += strlen(values[0]);
			} else
			if(!strcasecmp(attr, "userpassword"))
			{
				gwinfo->userpassword = fix_password(values);
				gwinfo->size += strlen(gwinfo->userpassword);
			} else
			if(!strcasecmp(attr, "gidnumber"))
			{
				gwinfo->gidnumber = safe_strdup(values[0]);
				gwinfo->size += strlen(values[0]);
			} else
			if(!strcasecmp(attr, "memberUid"))
			{
				gwinfo->memberuid = safe_strdup(values[0]);
				gwinfo->size += strlen(values[0]);
			}
			ldap_value_free(values);
		}
	}
	if (check_gwinfo(gwinfo) == YP_TRUE)
	{
		val->valdat_len = gwinfo->size + GWLINE_SIZE;
		val->valdat_val = safe_malloc(val->valdat_len + 1);
		sprintf(val->valdat_val, GWLINE, gwinfo->cn, gwinfo->userpassword, gwinfo->gidnumber, gwinfo->memberuid);
		rval = YP_TRUE;
	} else {
		rval = YP_NOKEY;
	}

	return (rval);
}

int assemble_nis(valdat *val, LDAPMessage *record, struct nisinfo *nisinfo)
{
	ypstat rval;
	char *attr;
	BerElement *ber = NULL;

	rval = YP_YPERR;
	fill_nisinfo(nisinfo);
	
	for (attr = ldap_first_attribute (ldaprefs.ldap, record, &ber); attr; attr = ldap_next_attribute (ldaprefs.ldap, record, ber))
	{
		char **values;

		values = ldap_get_values (ldaprefs.ldap, record, attr);
		if (values)
		{
			if(!strcasecmp(attr, "cn"))
			{
				nisinfo->cn = safe_strdup(values[0]);
			} else
			if(!strcasecmp(attr, "nismapentry"))
			{
				nisinfo->nismapentry = safe_strdup(values[0]);
			} else
			if(!strcasecmp(attr, "nismapname"))
			{
				nisinfo->nismapname = safe_strdup(values[0]);
			}
			ldap_value_free(values);
		}
	}
	if (check_nisinfo(nisinfo) == YP_TRUE)
	{
		val->valdat_len = strlen(nisinfo->nismapentry);
		val->valdat_val = safe_strdup(nisinfo->nismapentry);
		rval = YP_TRUE;
	} else {
		rval = YP_NOKEY;
	}

	return (rval);
}

ypstat check_unique(struct mapcache *mapcache, char *key)
{
	struct mapentry *mapentry_current;
	ypstat rval;

	rval = YP_YPERR;
	if(mapcache != NULL)
	{
		mapentry_current = mapcache->mapentry;
		for(mapentry_current = mapcache->mapentry; mapentry_current; mapentry_current = mapentry_current->next)
		{
			if (!strcmp(mapentry_current->key, key))
			{
				return (YP_TRUE);
			}
		}
			rval = YP_NOKEY;
	}

	return (rval);
}

struct mapcache *create_cache (ypmaplist *maps)
{
	LDAPMessage *ldapreturn, *record;
	struct ypmaplist *maplist_current;
	struct mapentry *mapentry_current;
	struct mapcache *mapcache_current, *mapcache_head;
	struct pwinfo *pwinfo;
	struct gwinfo *gwinfo;
	struct nisinfo *nisinfo;
	char *base, *filter;
	valdat *val;
	int rval;

	mapcache_current = mapcache_head = ldapreturn = record = NULL;
	val = safe_malloc(sizeof(valdat));

	for(maplist_current = maps; maplist_current; maplist_current = maplist_current->next)
	{
		mapcache_current = safe_malloc(sizeof(struct mapcache));
		mapcache_current->map = (mapname *) safe_strdup(maplist_current->map);
		mapcache_current->mapentry = NULL;
		mapcache_current->sem = safe_malloc(sizeof(sem_t));
		sem_init(mapcache_current->sem, 0, 1);
		printf("Building cache for: %s\n", maplist_current->map);

		if (!strcmp((char *) mapcache_current->map, "passwd.byname"))
		{
			/* XXX Add preferences for filter and basedn */
			base = safe_malloc(strlen(ldaprefs.basedn) + strlen("ou=People,") + 1);
			sprintf(base, "%s%s", "ou=People,", ldaprefs.basedn);
			filter = safe_strdup("(objectClass=posixAccount)");

			if(ldap_search_s(ldaprefs.ldap,
						base,
						LDAP_SCOPE_ONELEVEL,
						filter,
						user_attributes,
						0,
						&ldapreturn) != -1)
			{
				for (record = ldap_first_entry(ldaprefs.ldap, ldapreturn); record; record = ldap_next_entry(ldaprefs.ldap, record))
				{
					pwinfo = safe_malloc(sizeof(struct pwinfo));
					rval = assemble_passwd(val, record, pwinfo);
					if(rval == YP_TRUE);
					{
						if(check_unique(mapcache_current, pwinfo->uid) == YP_NOKEY)
						{
							mapentry_current = safe_malloc(sizeof(struct mapentry));
							mapentry_current->key = safe_strdup(pwinfo->uid);
							mapentry_current->val = val->valdat_val;
							mapentry_current->next = mapcache_current->mapentry;
							mapcache_current->mapentry = mapentry_current;
						}
					}
					clean_pwinfo(pwinfo);
				}
			}
			ldap_msgfree(ldapreturn);
			free(filter);
			free(base);
		}
		else if (!strcmp((char *) mapcache_current->map, "passwd.byuid"))
		{
			/* XXX Add preferences for filter and basedn */
			base = safe_malloc(strlen(ldaprefs.basedn) + strlen("ou=People,") + 1);
			sprintf(base, "%s%s", "ou=People,", ldaprefs.basedn);
			filter = safe_strdup("(objectClass=posixAccount)");

			if(ldap_search_s(ldaprefs.ldap,
						base,
						LDAP_SCOPE_ONELEVEL,
						filter,
						user_attributes,
						0,
						&ldapreturn) != -1)
			{
				for (record = ldap_first_entry(ldaprefs.ldap, ldapreturn); record; record = ldap_next_entry(ldaprefs.ldap, record))
				{
					pwinfo = safe_malloc(sizeof(struct pwinfo));
					rval = assemble_passwd(val, record, pwinfo);
					if(rval == YP_TRUE);
					{
						if(check_unique(mapcache_current, pwinfo->uidnumber) == YP_NOKEY)
						{
							mapentry_current = safe_malloc(sizeof(struct mapentry));
							mapentry_current->key = safe_strdup(pwinfo->uidnumber);
							mapentry_current->val = val->valdat_val;
							mapentry_current->next = mapcache_current->mapentry;
							mapcache_current->mapentry = mapentry_current;
						}
					}
					clean_pwinfo(pwinfo);
				}
			}
			ldap_msgfree(ldapreturn);
			free(filter);
			free(base);
		}
		else if (!strcmp((char *) mapcache_current->map, "group.byname"))
		{
			/* XXX Add preferences for filter and basedn */
			base = safe_malloc(strlen(ldaprefs.basedn) + strlen("ou=Group,") + 1);
			sprintf(base, "%s%s", "ou=Group,", ldaprefs.basedn);
			filter = safe_strdup("(objectClass=posixGroup)");

			if(ldap_search_s(ldaprefs.ldap,
						base,
						LDAP_SCOPE_ONELEVEL,
						filter,
						group_attributes,
						0,
						&ldapreturn) != -1)
			{
				for (record = ldap_first_entry(ldaprefs.ldap, ldapreturn); record; record = ldap_next_entry(ldaprefs.ldap, record))
				{
					gwinfo = safe_malloc(sizeof(struct gwinfo));
					rval = assemble_group(val, record, gwinfo);
					if(rval == YP_TRUE);
					{
						if(check_unique(mapcache_current, gwinfo->cn) == YP_NOKEY)
						{
							mapentry_current = safe_malloc(sizeof(struct mapentry));
							mapentry_current->key = safe_strdup(gwinfo->cn);
							mapentry_current->val = val->valdat_val;
							mapentry_current->next = mapcache_current->mapentry;
							mapcache_current->mapentry = mapentry_current;
						}
					}
					clean_gwinfo(gwinfo);
				}
			}
			ldap_msgfree(ldapreturn);
			free(filter);
			free(base);
		}
		else if (!strcmp((char *) mapcache_current->map, "group.bygid"))
		{
			/* XXX Add preferences for filter and basedn */
			base = safe_malloc(strlen(ldaprefs.basedn) + strlen("ou=Group,") + 1);
			sprintf(base, "%s%s", "ou=Group,", ldaprefs.basedn);
			filter = safe_strdup("(objectClass=posixGroup)");

			if(ldap_search_s(ldaprefs.ldap,
						base,
						LDAP_SCOPE_ONELEVEL,
						filter,
						group_attributes,
						0,
						&ldapreturn) != -1)
			{
				for (record = ldap_first_entry(ldaprefs.ldap, ldapreturn); record; record = ldap_next_entry(ldaprefs.ldap, record))
				{
					gwinfo = safe_malloc(sizeof(struct gwinfo));
					rval = assemble_group(val, record, gwinfo);
					if(rval == YP_TRUE);
					{
						if(check_unique(mapcache_current, gwinfo->cn) == YP_NOKEY)
						{
							mapentry_current = safe_malloc(sizeof(struct mapentry));
							mapentry_current->key = safe_strdup(gwinfo->gidnumber);
							mapentry_current->val = val->valdat_val;
							mapentry_current->next = mapcache_current->mapentry;
							mapcache_current->mapentry = mapentry_current;
						}
					}
					clean_gwinfo(gwinfo);
				}
			}
			ldap_msgfree(ldapreturn);
			free(filter);
			free(base);
		} else {
			/* XXX Add preferences for filter and basedn */
			base = safe_malloc(strlen(ldaprefs.basedn) + strlen("ou=NisMapName,") + strlen((char *) mapcache_current->map) + 1);
			sprintf(base, "nisMapName=%s,%s", (char *) mapcache_current->map, ldaprefs.basedn);
			filter = safe_strdup("(objectClass=nisObject)");

			if(ldap_search_s(ldaprefs.ldap,
						base,
						LDAP_SCOPE_ONELEVEL,
						filter,
						nis_attributes,
						0,
						&ldapreturn) != -1)
			{
				for (record = ldap_first_entry(ldaprefs.ldap, ldapreturn); record; record = ldap_next_entry(ldaprefs.ldap, record))
				{
					nisinfo = safe_malloc(sizeof(struct nisinfo));
					rval = assemble_nis(val, record, nisinfo);
					if(rval == YP_TRUE);
					{
						if(check_unique(mapcache_current, nisinfo->cn) == YP_NOKEY)
						{
							mapentry_current = safe_malloc(sizeof(struct mapentry));
							mapentry_current->key = safe_strdup(nisinfo->cn);
							mapentry_current->val = val->valdat_val;
							mapentry_current->next = mapcache_current->mapentry;
							mapcache_current->mapentry = mapentry_current;
						}
					}
					clean_nisinfo(nisinfo);
				}
			}
			ldap_msgfree(ldapreturn);
			free(filter);
			free(base);
		}
		mapcache_current->time = time(NULL);
		mapcache_current->next = mapcache_head;
		mapcache_head = mapcache_current;
	}
	return (mapcache_head);
}

void maintain_cache (void)
{

}


static struct mapcache *getcache(mapname *map)
{
	struct mapcache *mapcache_current;

	mapcache_current = ldaprefs.mapcache;
	while(mapcache_current)
	{
		if (!strcmp((char *)mapcache_current->map, (char *)map))
		{
			return (mapcache_current);
		}
		mapcache_current = mapcache_current->next;
	}

	return (NULL);
}

ypstat yp_get_record(keydat *key, mapname *map, valdat *val)
{
	struct mapcache *mapcache_current;
	struct mapentry *mapentry_current;
	ypstat rval;
	char *tempkey;

	tempkey = safe_malloc(key->keydat_len + 1);
	bcopy(key->keydat_val, tempkey, key->keydat_len);
	tempkey[key->keydat_len] = '\0';

	rval = YP_YPERR;
	mapcache_current = getcache(map);
	if(mapcache_current != NULL)
	{
		if (!strcmp((char *)mapcache_current->map, (char *) map))
		{
			if(mapcache_current->mapentry == NULL)
			{
				free(tempkey);
				return (YP_NOMAP);
			}
			mapentry_current = mapcache_current->mapentry;
			while(mapentry_current)
			{
				if (!strcmp(mapentry_current->key, (char *)tempkey))
				{
					val->valdat_val = safe_strdup(mapentry_current->val);
					val->valdat_len = strlen(mapentry_current->val);
					key->keydat_val = safe_strdup(mapentry_current->key);
					key->keydat_len = strlen(mapentry_current->key);
					free(tempkey);
					return (YP_TRUE);
				}
				mapentry_current = mapentry_current->next;
			}
			rval = YP_NOKEY;
		}
	} else {
		rval = YP_NOMAP;
	}

	free(tempkey);
	return (rval);
}
			

ypstat yp_first_record(keydat *key, mapname *map, valdat *val)
{
	ypstat rval;
	struct mapcache *mapcache_current;

	rval = YP_YPERR;
	mapcache_current = getcache(map);
	if(mapcache_current != NULL)
	{
		if(mapcache_current->mapentry != NULL)
		{
			val->valdat_val = safe_strdup(mapcache_current->mapentry->val);
			val->valdat_len = strlen(mapcache_current->mapentry->val);
			key->keydat_val = safe_strdup(mapcache_current->mapentry->key);
			key->keydat_len = strlen(mapcache_current->mapentry->key);
			rval = YP_TRUE;
		} else {
			rval = YP_NOMAP;
		}
	} else {
		rval = YP_NOMAP;
	}

	return (rval);
}

ypstat yp_next_record(keydat *key, mapname *map, valdat *val)
{
	struct mapcache *mapcache_current;
	struct mapentry *mapentry_current;
	ypstat rval;
	char *tempkey;

	/* NIS clients will iterate through the entire map, calling only
	 * ypproc_nextbykey. Thus, the first call will have no key and
	 * a keylen of 0, so return the result of yp_first_record */
	if (key->keydat_len == 0)
	{
		rval = yp_first_record(key, map, val);
		return (rval);
	}
	tempkey = safe_malloc(key->keydat_len + 1);
	bcopy(key->keydat_val, tempkey, key->keydat_len);
	tempkey[key->keydat_len] = '\0';

	rval = YP_YPERR;
	mapcache_current = getcache(map);
	if(mapcache_current != NULL)
	{
		if(mapcache_current->mapentry == NULL)
		{
			return (YP_NOMAP);
		}
		if (!strcmp((char *)mapcache_current->map, (char *) map))
		{
			mapentry_current = mapcache_current->mapentry;
			while(mapentry_current)
			{
				if (!strcmp(mapentry_current->key, (char *)tempkey))
				{
					free(tempkey);
					if (mapentry_current->next != NULL)
					{
						val->valdat_val = safe_strdup(mapentry_current->next->val);
						val->valdat_len = strlen(mapentry_current->next->val);
						key->keydat_val = safe_strdup(mapentry_current->next->key);
						key->keydat_len = strlen(mapentry_current->next->key);
						return (YP_TRUE);
					} else {
						key->keydat_len = 0;
						return (YP_NOKEY);
					}
				}
				mapentry_current = mapentry_current->next;
			}
			rval = YP_NOKEY;
		}
	} else {
		rval = YP_NOMAP;
	}
	free(tempkey);
	return (rval);
}


syntax highlighted by Code2HTML, v. 0.9.1