/*
 * NIS+ Passdb Backend
 * Copyright (C) Andrew Tridgell 1992-1998 Modified by Jeremy Allison 1995.
 * Copyright (C) Benny Holmgren 1998 <bigfoot@astrakan.hgs.se> 
 * Copyright (C) Luke Kenneth Casson Leighton 1996-1998.
 * Copyright (C) Toomas Soome <tsoome@ut.ee> 2001
 * Copyright (C) Jelmer Vernooij 2002
 * 
 * This program is free software; you can redistribute it and/or modify it under
 * the terms of the GNU General Public License as published by the Free
 * Software Foundation; either version 2 of the License, or (at your option)
 * any later version.
 * 
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 * 
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc., 675
 * Mass Ave, Cambridge, MA 02139, USA.
 */

#include "includes.h"

#ifdef BROKEN_NISPLUS_INCLUDE_FILES

/*
 * The following lines are needed due to buggy include files
 * in Solaris 2.6 which define GROUP in both /usr/include/sys/acl.h and
 * also in /usr/include/rpcsvc/nis.h. The definitions conflict. JRA.
 * Also GROUP_OBJ is defined as 0x4 in /usr/include/sys/acl.h and as
 * an enum in /usr/include/rpcsvc/nis.h.
 */


#if defined(GROUP)
#undef GROUP
#endif

#if defined(GROUP_OBJ)
#undef GROUP_OBJ
#endif

#endif

#include <rpcsvc/nis.h>

/***************************************************************

 the fields for the NIS+ table, generated from mknissmbpwtbl.sh, are:

		name=S,nogw=r 
		uid=S,nogw=r 
		user_rid=S,nogw=r
		smb_grpid=,nw+r
		group_rid=,nw+r
		acb=,nw+r

		lmpwd=C,nw=,g=r,o=rm 
		ntpwd=C,nw=,g=r,o=rm 

		logon_t=,nw+r 
		logoff_t=,nw+r 
		kick_t=,nw+r 
		pwdlset_t=,nw+r 
		pwdlchg_t=,nw+r 
		pwdmchg_t=,nw+r 
		                
		full_name=,nw+r 
		home_dir=,nw+r 
		dir_drive=,nw+r 
		logon_script=,nw+r 
		profile_path=,nw+r 
		acct_desc=,nw+r 
		workstations=,nw+r 
		                   
		hours=,nw+r 

****************************************************************/

#define NPF_NAME          0
#define NPF_UID           1
#define NPF_USER_RID      2
#define NPF_SMB_GRPID     3
#define NPF_GROUP_RID     4
#define NPF_ACB           5
#define NPF_LMPWD         6
#define NPF_NTPWD         7
#define NPF_LOGON_T       8
#define NPF_LOGOFF_T      9
#define NPF_KICK_T        10
#define NPF_PWDLSET_T     11
#define NPF_PWDCCHG_T     12
#define NPF_PWDMCHG_T     13
#define NPF_FULL_NAME     14
#define NPF_HOME_DIR      15
#define NPF_DIR_DRIVE     16
#define NPF_LOGON_SCRIPT  17
#define NPF_PROFILE_PATH  18
#define NPF_ACCT_DESC     19
#define NPF_WORKSTATIONS  20
#define NPF_HOURS         21

struct nisplus_private_info {
	nis_result *result;
	int enum_entry;
	char *location;
};

static char *make_nisname_from_user_rid (uint32 rid, char *pfile);
static char *make_nisname_from_name (const char *user_name, char *pfile);
static void get_single_attribute (const nis_object * new_obj, int col,
				  char *val, int len);;
static BOOL make_sam_from_nisp_object (SAM_ACCOUNT * pw_buf,
				       const nis_object * obj);
static BOOL make_sam_from_nisresult (SAM_ACCOUNT * pw_buf,
				     const nis_result * result);;
static void set_single_attribute (nis_object * new_obj, int col,
				  const char *val, int len, int flags);
static BOOL init_nisp_from_sam (nis_object * obj, const SAM_ACCOUNT * sampass,
				nis_object * old);
static nis_result *nisp_get_nis_list (const char *nisname,
				      unsigned int flags);

/***************************************************************
 Start enumeration of the passwd list.
****************************************************************/

static NTSTATUS nisplussam_setsampwent (struct pdb_methods *methods, BOOL update)
{
	struct nisplus_private_info *private =
		(struct nisplus_private_info *) methods->private_data;

	char *sp;
	pstring pfiletmp;

	if ((sp = strrchr (private->location, '/')))
		safe_strcpy (pfiletmp, sp + 1, sizeof (pfiletmp) - 1);
	else
		safe_strcpy (pfiletmp, p, sizeof (pfiletmp) - 1);
	safe_strcat (pfiletmp, ".org_dir",
		     sizeof (pfiletmp) - strlen (pfiletmp) - 1);

	pdb_endsampwent ();	/* just in case */
	global_nisp_ent->result = nisp_get_nis_list (pfiletmp, 0);
	global_nisp_ent->enum_entry = 0;
	if (global_nisp_ent->result != NULL) 
		return NT_STATUS_UNSUCCESSFUL;
	else
		return NT_STATUS_OK;
}

/***************************************************************
 End enumeration of the passwd list.
****************************************************************/

static void nisplussam_endsampwent (struct pdb_methods *methods)
{
	struct nisplus_private_info *global_nisp_ent =
		(struct nisplus_private_info *) methods->private_data;
	if (global_nisp_ent->result)
		nis_freeresult (global_nisp_ent->result);
	global_nisp_ent->result = NULL;
	global_nisp_ent->enum_entry = 0;
}

/*****************************************************************
 Get one SAM_ACCOUNT from the list (next in line)
*****************************************************************/

static NTSTATUS nisplussam_getsampwent (struct pdb_methods *methods,
				    SAM_ACCOUNT * user)
{
	NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
	struct nisplus_private_info *global_nisp_ent =
		(struct nisplus_private_info *) methods->private_data;
	int enum_entry = (int) (global_nisp_ent->enum_entry);
	nis_result *result = global_nisp_ent->result;

	if (user == NULL) {
		DEBUG (0, ("SAM_ACCOUNT is NULL.\n"));
		return nt_status;
	}

	if (result == NULL || enum_entry < 0 || enum_entry >= (NIS_RES_NUMOBJ (result) - 1)) {
		return nt_status;
	}

	if (!make_sam_from_nisp_object(user, &NIS_RES_OBJECT (result)[enum_entry])) {
		DEBUG (0, ("Bad SAM_ACCOUNT entry returned from NIS+!\n"));
		return nt_status;
	}
	(int) (global_nisp_ent->enum_entry)++;

	return nt_status;
}

/******************************************************************
 Lookup a name in the SAM database
******************************************************************/

static NTSTATUS nisplussam_getsampwnam (struct pdb_methods *methods,
				    SAM_ACCOUNT * user, const char *sname)
{
	/* Static buffers we will return. */
	NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
	nis_result *result = NULL;
	pstring nisname;
	BOOL ret;
	struct nisplus_private_info *private =
		(struct nisplus_private_info *) methods->private_data;

	if (!private->location || !(*private->location)) {
		DEBUG (0, ("No SMB password file set\n"));
		return nt_status;
	}
	if (strrchr (private->location, '/'))
		private->location = strrchr (private->location, '/') + 1;

	slprintf (nisname, sizeof (nisname) - 1, "[name=%s],%s.org_dir",
		  sname, private->location);
	DEBUG (10, ("search by nisname: %s\n", nisname));

	/* Search the table. */

	if (!(result = nisp_get_nis_list (nisname, 0))) {
		return nt_status;
	}

	ret = make_sam_from_nisresult (user, result);
	nis_freeresult (result);

	if (ret) nt_status = NT_STATUS_OK;

	return nt_status;
}

/***************************************************************************
 Search by sid
 **************************************************************************/

static NTSTATUS nisplussam_getsampwrid (struct pdb_methods *methods,
				    SAM_ACCOUNT * user, uint32 rid)
{
	NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
	nis_result *result;
	char *nisname;
	BOOL ret;
	char *sp;
	pstring pfiletmp;
	struct nisplus_private_info *private =
		(struct nisplus_private_info *) methods->private_data;

	if (!private->location || !(*private->location)) {
		DEBUG (0, ("no SMB password file set\n"));
		return nt_status;
	}

	if ((sp = strrchr (private->location, '/')))
		safe_strcpy (pfiletmp, sp + 1, sizeof (pfiletmp) - 1);
	else
		safe_strcpy (pfiletmp, private->location, sizeof (pfiletmp) - 1);
	safe_strcat (pfiletmp, ".org_dir",
		     sizeof (pfiletmp) - strlen (pfiletmp) - 1);

	nisname = make_nisname_from_user_rid (rid, pfiletmp);

	DEBUG (10, ("search by rid: %s\n", nisname));

	/* Search the table. */

	if (!(result = nisp_get_nis_list (nisname, 0))) {
		return nt_status;
	}

	ret = make_sam_from_nisresult (user, result);
	nis_freeresult (result);

	if (ret) nt_status = NT_STATUS_OK;

	return nt_status;
}

static NTSTATUS nisplussam_getsampwsid (struct pdb_methods *methods,
				    SAM_ACCOUNT * user, const DOM_SID * sid)
{
	uint32 rid;

	if (!sid_peek_check_rid (get_global_sam_sid (), sid, &rid))
		return NT_STATUS_UNSUCCESSFUL;
	return nisplussam_getsampwrid (methods, user, rid);
}



/***************************************************************************
 Delete a SAM_ACCOUNT
****************************************************************************/

static NTSTATUS nisplussam_delete_sam_account (struct pdb_methods *methods,
						SAM_ACCOUNT * user)
{
	NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
	const char *sname;
	pstring nisname;
	nis_result *result, *delresult;
	nis_object *obj;
	struct nisplus_private_info *private =
		(struct nisplus_private_info *) methods->private_data;

	if (!user) {
		DEBUG (0, ("no SAM_ACCOUNT specified!\n"));
		return nt_status;
	}

	sname = pdb_get_username (user);

	if (!private->location || !(*private->location)) {
		DEBUG (0, ("no SMB password file set\n"));
		return nt_status;
	}

	if (strrchr (private->location, '/'))
		private->location = strrchr (private->location, '/') + 1;

	slprintf (nisname, sizeof (nisname) - 1, "[name=%s],%s.org_dir",
		  sname, private->location);

	/* Search the table. */

	if (!(result = nisp_get_nis_list (nisname,
					  MASTER_ONLY | FOLLOW_LINKS |
					  FOLLOW_PATH | EXPAND_NAME |
					  HARD_LOOKUP))) {
		return nt_status;
	}

	if (result->status != NIS_SUCCESS || NIS_RES_NUMOBJ (result) <= 0) {
		/* User not found. */
		DEBUG (0, ("user not found in NIS+\n"));
		nis_freeresult (result);
		return nt_status;
	}

	obj = NIS_RES_OBJECT (result);
	slprintf (nisname, sizeof (nisname) - 1, "[name=%s],%s.%s", sname,
		  obj->zo_name, obj->zo_domain);

	DEBUG (10, ("removing name: %s\n", nisname));
	delresult = nis_remove_entry (nisname, obj,
				      MASTER_ONLY | REM_MULTIPLE | ALL_RESULTS
				      | FOLLOW_PATH | EXPAND_NAME |
				      HARD_LOOKUP);

	nis_freeresult (result);

	if (delresult->status != NIS_SUCCESS) {
		DEBUG (0, ("NIS+ table update failed: %s %s\n",
			   nisname, nis_sperrno (delresult->status)));
		nis_freeresult (delresult);
		return nt_status;
	}
	nis_freeresult (delresult);

	return NT_STATUS_OK;
}

/***************************************************************************
 Modifies an existing SAM_ACCOUNT
****************************************************************************/

static NTSTATUS nisplussam_update_sam_account (struct pdb_methods *methods,
					   SAM_ACCOUNT * newpwd)
{
	NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
	nis_result *result, *addresult;
	nis_object *obj;
	nis_object new_obj;
	entry_col *ecol;
	int ta_maxcol;
	struct nisplus_private_info *private =
		(struct nisplus_private_info *) methods->private_data;
	pstring nisname;

	if (!private->location || !(*private->location)) {
		DEBUG (0, ("no SMB password file set\n"));
		return nt_status;
	}
	if (strrchr (private->location, '/'))
		private->location = strrchr (private->location, '/') + 1;

	slprintf (nisname, sizeof (nisname) - 1, "[name=%s],%s.org_dir",
		  pdb_get_username (newpwd), private->location);

	DEBUG (10, ("search by name: %s\n", nisname));

	/* Search the table. */

	if (!
	    (result =
	     nisp_get_nis_list (nisname,
				MASTER_ONLY | FOLLOW_LINKS | FOLLOW_PATH |
				EXPAND_NAME | HARD_LOOKUP))) {
		return ne_status;
	}

	if (result->status != NIS_SUCCESS || NIS_RES_NUMOBJ (result) <= 0) {
		/* User not found. */
		DEBUG (0, ("user not found in NIS+\n"));
		nis_freeresult (result);
		return nt_status;
	}

	obj = NIS_RES_OBJECT (result);
	DEBUG (6, ("entry found in %s\n", obj->zo_domain));

	/* we must create new stub object with EN_MODIFIED flag.
	   this is because obj from result is going to be freed and
	   we do not want to break it or cause memory leaks or corruption.
	 */

	memmove ((char *) &new_obj, obj, sizeof (new_obj));
	ta_maxcol = obj->TA_data.ta_maxcol;

	if (!(ecol = (entry_col *) malloc (ta_maxcol * sizeof (entry_col)))) {
		DEBUG (0, ("memory allocation failure\n"));
		nis_freeresult (result);
		return nt_status;
	}

	memmove ((char *) ecol, obj->EN_data.en_cols.en_cols_val,
		 ta_maxcol * sizeof (entry_col));
	new_obj.EN_data.en_cols.en_cols_val = ecol;
	new_obj.EN_data.en_cols.en_cols_len = ta_maxcol;

	if (init_nisp_from_sam (&new_obj, newpwd, obj) == True) {
		slprintf (nisname, sizeof (nisname) - 1, "[name=%s],%s.%s",
			  pdb_get_username (newpwd), private->location, obj->zo_domain);

		DEBUG (10, ("NIS+ table update: %s\n", nisname));
		addresult =
			nis_modify_entry (nisname, &new_obj,
					  MOD_SAMEOBJ | FOLLOW_PATH |
					  EXPAND_NAME | HARD_LOOKUP);

		if (addresult->status != NIS_SUCCESS) {
			DEBUG (0, ("NIS+ table update failed: %s %s\n",
				   nisname, nis_sperrno (addresult->status)));
			nis_freeresult (addresult);
			nis_freeresult (result);
			free (ecol);
			return nt_status;
		}

		DEBUG (6, ("password changed\n"));
		nis_freeresult (addresult);
	} else {
		DEBUG (6, ("nothing to change!\n"));
	}

	free (ecol);
	nis_freeresult (result);

	return NT_STATUS_OK;
}

/***************************************************************************
 Adds an existing SAM_ACCOUNT
****************************************************************************/

static NTSTATUS nisplussam_add_sam_account (struct pdb_methods *methods,
					SAM_ACCOUNT * newpwd)
{
	NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL;
	int local_user = 0;
	char *pfile;
	pstring pfiletmp;
	char *nisname;
	nis_result *result = NULL, *tblresult = NULL;
	nis_object new_obj;
	entry_col *ecol;
	int ta_maxcol;

	/*
	 * 1. find user domain.
	 *   a. try nis search in passwd.org_dir - if found use domain from result.
	 *   b. try getpwnam. this may be needed if user is defined
	 *      in /etc/passwd file (or elsewere) and not in passwd.org_dir.
	 *      if found, use host default domain.
	 *   c. exit with False - no such user.
	 *
	 * 2. add user
	 *   a. find smbpasswd table
	 *      search pfile in user domain if not found, try host default
	 *      domain. 
	 *   b. smbpasswd domain is found, fill data and add entry.
	 *
	 * pfile should contain ONLY table name, org_dir will be concated.
	 * so, at first we will clear path prefix from pfile, and
	 * then we will use pfiletmp as playground to put together full
	 * nisname string.
	 * such approach will make it possible to specify samba private dir
	 * AND still use NIS+ table. as all domain related data is normally
	 * stored in org_dir.DOMAIN, this should be ok do do.
	 */

	pfile = private->location;
	if (strrchr (pfile, '/'))
		pfile = strrchr (pfile, '/') + 1;

	/*
	 * Check if user is already there.
	 */
	safe_strcpy (pfiletmp, pfile, sizeof (pfiletmp) - 1);
	safe_strcat (pfiletmp, ".org_dir",
		     sizeof (pfiletmp) - strlen (pfiletmp) - 1);

	if (pdb_get_username (newpwd) != NULL) {
		nisname = make_nisname_from_name (pdb_get_username (newpwd),
						  pfiletmp);
	} else {
		return nt_status;
	}

	if (!
	    (result =
	     nisp_get_nis_list (nisname,
				MASTER_ONLY | FOLLOW_LINKS | FOLLOW_PATH |
				EXPAND_NAME | HARD_LOOKUP))) {
		return nt_status;
	}
	if (result->status != NIS_SUCCESS && result->status != NIS_NOTFOUND) {
		DEBUG (3, ("nis_list failure: %s: %s\n",
			   nisname, nis_sperrno (result->status)));
		nis_freeresult (result);
		return nt_status;
	}

	if (result->status == NIS_SUCCESS && NIS_RES_NUMOBJ (result) > 0) {
		DEBUG (3, ("User already exists in NIS+ password db: %s\n",
			   pfile));
		nis_freeresult (result);
		return nt_status;
	}

	nis_freeresult (result);	/* no such user, free results */

	/*
	 * check for user in unix password database. we need this to get
	 * domain, where smbpasswd entry should be stored.
	 */

	nisname = make_nisname_from_name (pdb_get_username (newpwd),
					  "passwd.org_dir");

	result = nisp_get_nis_list (nisname,
				    MASTER_ONLY | FOLLOW_LINKS | FOLLOW_PATH |
				    EXPAND_NAME | HARD_LOOKUP);

	if (result->status != NIS_SUCCESS || NIS_RES_NUMOBJ (result) <= 0) {
		struct passwd *passwd;

		DEBUG (3, ("nis_list failure: %s: %s\n",
			   nisname, nis_sperrno (result->status)));
		nis_freeresult (result);

		if (!(passwd = getpwnam_alloc (pdb_get_username (newpwd)))) {
			/* no such user in system! */
			return nt_status;
		}
		passwd_free (&passwd);

		/* 
		 * user is defined, but not in passwd.org_dir.
		 */
		local_user = 1;
	} else {
		safe_strcpy (pfiletmp, pfile, sizeof (pfiletmp) - 1);
		safe_strcat (pfiletmp, ".",
			     sizeof (pfiletmp) - strlen (pfiletmp) - 1);
		safe_strcat (pfiletmp, NIS_RES_OBJECT (result)->zo_domain,
			     sizeof (pfiletmp) - strlen (pfiletmp) - 1);
		nis_freeresult (result);	/* not needed any more */

		tblresult = nisp_get_nis_list (pfiletmp,
					       MASTER_ONLY | FOLLOW_LINKS |
					       FOLLOW_PATH | EXPAND_NAME |
					       HARD_LOOKUP);
	}

	if (local_user || tblresult->status != NIS_SUCCESS) {
		/*
		 * no user domain or
		 * smbpasswd table not found in user domain, fallback to
		 * default domain.
		 */
		if (!local_user)	/* free previous failed search result */
			nis_freeresult (tblresult);

		safe_strcpy (pfiletmp, pfile, sizeof (pfiletmp) - 1);
		safe_strcat (pfiletmp, ".org_dir",
			     sizeof (pfiletmp) - strlen (pfiletmp) - 1);
		tblresult = nis_lookup (pfiletmp, MASTER_ONLY | FOLLOW_LINKS |
					FOLLOW_PATH | EXPAND_NAME |
					HARD_LOOKUP);
		if (tblresult->status != NIS_SUCCESS) {
			/* still nothing. bail out */
			nis_freeresult (tblresult);
			DEBUG (3, ("nis_lookup failure: %s\n",
				   nis_sperrno (tblresult->status)));
			return nt_status;
		}
		/* we need full name for nis_add_entry() */
		safe_strcpy (pfiletmp, pfile, sizeof (pfiletmp) - 1);
		safe_strcat (pfiletmp, ".",
			     sizeof (pfiletmp) - strlen (pfiletmp) - 1);
		safe_strcat (pfiletmp, NIS_RES_OBJECT (tblresult)->zo_domain,
			     sizeof (pfiletmp) - strlen (pfiletmp) - 1);
	}

	memset ((char *) &new_obj, 0, sizeof (new_obj));
	/* fill entry headers */
	/* we do not free these. */
	new_obj.zo_name = NIS_RES_OBJECT (tblresult)->zo_name;
	new_obj.zo_owner = NIS_RES_OBJECT (tblresult)->zo_owner;
	new_obj.zo_group = NIS_RES_OBJECT (tblresult)->zo_group;
	new_obj.zo_domain = NIS_RES_OBJECT (tblresult)->zo_domain;
	/* uints */
	new_obj.zo_access = NIS_RES_OBJECT (tblresult)->zo_access;
	new_obj.zo_ttl = NIS_RES_OBJECT (tblresult)->zo_ttl;

	new_obj.zo_data.zo_type = ENTRY_OBJ;
	new_obj.EN_data.en_type = NIS_RES_OBJECT (tblresult)->TA_data.ta_type;

	ta_maxcol = NIS_RES_OBJECT (tblresult)->TA_data.ta_maxcol;

	if (!(ecol = (entry_col *) malloc (ta_maxcol * sizeof (entry_col)))) {
		DEBUG (0, ("memory allocation failure\n"));
		nis_freeresult (tblresult);
		return nt_status;
	}

	memset ((char *) ecol, 0, ta_maxcol * sizeof (entry_col));
	new_obj.EN_data.en_cols.en_cols_val = ecol;
	new_obj.EN_data.en_cols.en_cols_len = ta_maxcol;

	init_nisp_from_sam (&new_obj, newpwd, NULL);

	DEBUG (10, ("add NIS+ entry: %s\n", nisname));
	result = nis_add_entry (pfiletmp, &new_obj, 0);

	free (ecol);		/* free allocated entry space */

	if (result->status != NIS_SUCCESS) {
		DEBUG (3, ("NIS+ table update failed: %s,%s\n",
			   nisname, nis_sperrno (result->status)));
		nis_freeresult (tblresult);
		nis_freeresult (result);
		return nt_status;
	}

	nis_freeresult (tblresult);
	nis_freeresult (result);

	return NT_STATUS_OK;
}

/***************************************************************
 make_nisname_from_user_rid
 ****************************************************************/
static char *make_nisname_from_user_rid (uint32 rid, char *pfile)
{
	static pstring nisname;

	safe_strcpy (nisname, "[user_rid=", sizeof (nisname) - 1);
	slprintf (nisname, sizeof (nisname) - 1, "%s%d", nisname, rid);
	safe_strcat (nisname, "],", sizeof (nisname) - strlen (nisname) - 1);
	safe_strcat (nisname, pfile, sizeof (nisname) - strlen (nisname) - 1);

	return nisname;
}

/***************************************************************
 make_nisname_from_name
 ****************************************************************/
static char *make_nisname_from_name (const char *user_name, char *pfile)
{
	static pstring nisname;

	safe_strcpy (nisname, "[name=", sizeof (nisname) - 1);
	safe_strcat (nisname, user_name,
		     sizeof (nisname) - strlen (nisname) - 1);
	safe_strcat (nisname, "],", sizeof (nisname) - strlen (nisname) - 1);
	safe_strcat (nisname, pfile, sizeof (nisname) - strlen (nisname) - 1);

	return nisname;
}

/*************************************************************************
 gets a NIS+ attribute
 *************************************************************************/
static void get_single_attribute (const nis_object * new_obj, int col,
				  char *val, int len)
{
	int entry_len;

	if (new_obj == NULL || val == NULL)
		return;

	entry_len = ENTRY_LEN (new_obj, col);
	if (len > entry_len) {
		len = entry_len;
	}

	safe_strcpy (val, ENTRY_VAL (new_obj, col), len - 1);
}

/************************************************************************
 makes a struct sam_passwd from a NIS+ object.
 ************************************************************************/
static BOOL make_sam_from_nisp_object (SAM_ACCOUNT * pw_buf,
				       const nis_object * obj)
{
	char *ptr;
	pstring full_name;	/* this must be translated to dos code page */
	pstring acct_desc;	/* this must be translated to dos code page */
	pstring home_dir;	/* set default value from smb.conf for user */
	pstring home_drive;	/* set default value from smb.conf for user */
	pstring logon_script;	/* set default value from smb.conf for user */
	pstring profile_path;	/* set default value from smb.conf for user */
	pstring hours;
	int hours_len;
	unsigned char smbpwd[16];
	unsigned char smbntpwd[16];


	/*
	 * time values. note: this code assumes 32bit time_t!
	 */

	/* Don't change these timestamp settings without a good reason.  They are
	   important for NT member server compatibility. */

	pdb_set_logon_time (pw_buf, (time_t) 0, PDB_DEFAULT);
	ptr = (uchar *) ENTRY_VAL (obj, NPF_LOGON_T);
	if (ptr && *ptr && (StrnCaseCmp (ptr, "LNT-", 4) == 0)) {
		int i;

		ptr += 4;
		for (i = 0; i < 8; i++) {
			if (ptr[i] == '\0' || !isxdigit (ptr[i]))
				break;
		}
		if (i == 8) {
			pdb_set_logon_time (pw_buf,
					    (time_t) strtol (ptr, NULL, 16),
					    PDB_SET);
		}
	}

	pdb_set_logoff_time (pw_buf, get_time_t_max (), PDB_DEFAULT);
	ptr = (uchar *) ENTRY_VAL (obj, NPF_LOGOFF_T);
	if (ptr && *ptr && (StrnCaseCmp (ptr, "LOT-", 4) == 0)) {
		int i;

		ptr += 4;
		for (i = 0; i < 8; i++) {
			if (ptr[i] == '\0' || !isxdigit (ptr[i]))
				break;
		}
		if (i == 8) {
			pdb_set_logoff_time (pw_buf,
					     (time_t) strtol (ptr, NULL, 16),
					     PDB_SET);
		}
	}

	pdb_set_kickoff_time (pw_buf, get_time_t_max (), PDB_DEFAULT);
	ptr = (uchar *) ENTRY_VAL (obj, NPF_KICK_T);
	if (ptr && *ptr && (StrnCaseCmp (ptr, "KOT-", 4) == 0)) {
		int i;

		ptr += 4;
		for (i = 0; i < 8; i++) {
			if (ptr[i] == '\0' || !isxdigit (ptr[i]))
				break;
		}
		if (i == 8) {
			pdb_set_kickoff_time (pw_buf,
					      (time_t) strtol (ptr, NULL, 16),
					      PDB_SET);
		}
	}

	pdb_set_pass_last_set_time (pw_buf, (time_t) 0, PDB_DEFAULT);
	ptr = (uchar *) ENTRY_VAL (obj, NPF_PWDLSET_T);
	if (ptr && *ptr && (StrnCaseCmp (ptr, "LCT-", 4) == 0)) {
		int i;

		ptr += 4;
		for (i = 0; i < 8; i++) {
			if (ptr[i] == '\0' || !isxdigit (ptr[i]))
				break;
		}
		if (i == 8) {
			pdb_set_pass_last_set_time (pw_buf,
						    (time_t) strtol (ptr,
								     NULL,
								     16),
						     PDB_SET);
		}
	}

	pdb_set_pass_can_change_time (pw_buf, (time_t) 0, PDB_DEFAULT);
	ptr = (uchar *) ENTRY_VAL (obj, NPF_PWDCCHG_T);
	if (ptr && *ptr && (StrnCaseCmp (ptr, "CCT-", 4) == 0)) {
		int i;

		ptr += 4;
		for (i = 0; i < 8; i++) {
			if (ptr[i] == '\0' || !isxdigit (ptr[i]))
				break;
		}
		if (i == 8) {
			pdb_set_pass_can_change_time (pw_buf,
						      (time_t) strtol (ptr,
								       NULL,
								       16),
						      PDB_SET);
		}
	}

	pdb_set_pass_must_change_time (pw_buf, get_time_t_max (), PDB_DEFAULT);	/* Password never expires. */
	ptr = (uchar *) ENTRY_VAL (obj, NPF_PWDMCHG_T);
	if (ptr && *ptr && (StrnCaseCmp (ptr, "MCT-", 4) == 0)) {
		int i;

		ptr += 4;
		for (i = 0; i < 8; i++) {
			if (ptr[i] == '\0' || !isxdigit (ptr[i]))
				break;
		}
		if (i == 8) {
			pdb_set_pass_must_change_time (pw_buf,
						       (time_t) strtol (ptr,
									NULL,
									16),
						       PDB_SET);
		}
	}

	/* string values */
	pdb_set_username (pw_buf, ENTRY_VAL (obj, NPF_NAME), PDB_SET);
	pdb_set_domain (pw_buf, lp_workgroup (), PDB_DEFAULT);
	/* pdb_set_nt_username() -- cant set it here... */

	get_single_attribute (obj, NPF_FULL_NAME, full_name,
			      sizeof (pstring));
#if 0
	unix_to_dos (full_name, True);
#endif
	pdb_set_fullname (pw_buf, full_name, PDB_SET);

	pdb_set_acct_ctrl (pw_buf, pdb_decode_acct_ctrl (ENTRY_VAL (obj,
								    NPF_ACB), PDB_SET));

	get_single_attribute (obj, NPF_ACCT_DESC, acct_desc,
			      sizeof (pstring));
#if 0
	unix_to_dos (acct_desc, True);
#endif
	pdb_set_acct_desc (pw_buf, acct_desc, PDB_SET);

	pdb_set_workstations (pw_buf, ENTRY_VAL (obj, NPF_WORKSTATIONS), PDB_SET);
	pdb_set_munged_dial (pw_buf, NULL, PDB_DEFAULT);

	pdb_set_user_sid_from_rid (pw_buf,
				   atoi (ENTRY_VAL (obj, NPF_USER_RID)), PDB_SET);
	pdb_set_group_sid_from_rid (pw_buf,
				    atoi (ENTRY_VAL (obj, NPF_GROUP_RID)), PDB_SET);

	/* values, must exist for user */
	if (!(pdb_get_acct_ctrl (pw_buf) & ACB_WSTRUST)) {

		get_single_attribute (obj, NPF_HOME_DIR, home_dir,
				      sizeof (pstring));
		if (!(home_dir && *home_dir)) {
			pstrcpy (home_dir, lp_logon_home ());
			pdb_set_homedir (pw_buf, home_dir, PDB_DEFAULT);
		} else
			pdb_set_homedir (pw_buf, home_dir, PDB_SET);

		get_single_attribute (obj, NPF_DIR_DRIVE, home_drive,
				      sizeof (pstring));
		if (!(home_drive && *home_drive)) {
			pstrcpy (home_drive, lp_logon_drive ());
			pdb_set_dir_drive (pw_buf, home_drive, PDB_DEFAULT);
		} else
			pdb_set_dir_drive (pw_buf, home_drive, PDB_SET);

		get_single_attribute (obj, NPF_LOGON_SCRIPT, logon_script,
				      sizeof (pstring));
		if (!(logon_script && *logon_script)) {
			pstrcpy (logon_script, lp_logon_script ());
			pdb_set_logon_script (pw_buf, logon_script, PDB_DEFAULT);
		} else
			pdb_set_logon_script (pw_buf, logon_script, PDB_SET);

		get_single_attribute (obj, NPF_PROFILE_PATH, profile_path,
				      sizeof (pstring));
		if (!(profile_path && *profile_path)) {
			pstrcpy (profile_path, lp_logon_path ());
			pdb_set_profile_path (pw_buf, profile_path, PDB_DEFAULT);
		} else
			pdb_set_profile_path (pw_buf, profile_path, PDB_SET);

	} else {
		/* lkclXXXX this is OBSERVED behaviour by NT PDCs, enforced here. */
		pdb_set_group_sid_from_rid (pw_buf, DOMAIN_GROUP_RID_USERS, PDB_DEFAULT);
	}

	/* Check the lanman password column. */
	ptr = (char *) ENTRY_VAL (obj, NPF_LMPWD);
	if (!pdb_set_lanman_passwd (pw_buf, NULL, PDB_DEFAULT))
		return False;

	if (!strncasecmp (ptr, "NO PASSWORD", 11)) {
		pdb_set_acct_ctrl (pw_buf,
				   pdb_get_acct_ctrl (pw_buf) | ACB_PWNOTREQ, PDB_SET);
	} else {
		if (strlen (ptr) != 32 || !pdb_gethexpwd (ptr, smbpwd)) {
			DEBUG (0, ("malformed LM pwd entry: %s.\n",
				   pdb_get_username (pw_buf)));
			return False;
		}
		if (!pdb_set_lanman_passwd (pw_buf, smbpwd, PDB_SET))
			return False;
	}

	/* Check the NT password column. */
	ptr = ENTRY_VAL (obj, NPF_NTPWD);
	if (!pdb_set_nt_passwd (pw_buf, NULL, PDB_DEFAULT))
		return False;

	if (!(pdb_get_acct_ctrl (pw_buf) & ACB_PWNOTREQ) &&
	    strncasecmp (ptr, "NO PASSWORD", 11)) {
		if (strlen (ptr) != 32 || !pdb_gethexpwd (ptr, smbntpwd)) {
			DEBUG (0, ("malformed NT pwd entry:\ %s.\n",
				   pdb_get_username (pw_buf)));
			return False;
		}
		if (!pdb_set_nt_passwd (pw_buf, smbntpwd, PDB_SET))
			return False;
	}

	pdb_set_unknown_3 (pw_buf, 0xffffff, PDB_DEFAULT);	/* don't know */
	pdb_set_logon_divs (pw_buf, 168, PDB_DEFAULT);	/* hours per week */

	if ((hours_len = ENTRY_LEN (obj, NPF_HOURS)) == 21) {
		memcpy (hours, ENTRY_VAL (obj, NPF_HOURS), hours_len);
	} else {
		hours_len = 21;	/* 21 times 8 bits = 168 */
		/* available at all hours */
		memset (hours, 0xff, hours_len);
	}
	pdb_set_hours_len (pw_buf, hours_len, PDB_SET);
	pdb_set_hours (pw_buf, hours, PDB_SET);

	pdb_set_unknown_5 (pw_buf, 0x00020000, PDB_DEFAULT);	/* don't know */
	pdb_set_unknown_6 (pw_buf, 0x000004ec, PDB_DEFAULT);	/* don't know */

	return True;
}

/************************************************************************
 makes a struct sam_passwd from a NIS+ result.
 ************************************************************************/
static BOOL make_sam_from_nisresult (SAM_ACCOUNT * pw_buf,
				     const nis_result * result)
{
	if (pw_buf == NULL || result == NULL)
		return False;

	if (result->status != NIS_SUCCESS && result->status != NIS_NOTFOUND) {
		DEBUG (0, ("NIS+ lookup failure: %s\n",
			   nis_sperrno (result->status)));
		return False;
	}

	/* User not found. */
	if (NIS_RES_NUMOBJ (result) <= 0) {
		DEBUG (10, ("user not found in NIS+\n"));
		return False;
	}

	if (NIS_RES_NUMOBJ (result) > 1) {
		DEBUG (10,
		       ("WARNING: Multiple entries for user in NIS+ table!\n"));
	}

	/* Grab the first hit. */
	return make_sam_from_nisp_object (pw_buf,
					  &NIS_RES_OBJECT (result)[0]);
}

/*************************************************************************
 sets a NIS+ attribute
 *************************************************************************/
static void set_single_attribute (nis_object * new_obj, int col,
				  const char *val, int len, int flags)
{
	if (new_obj == NULL)
		return;

	ENTRY_VAL (new_obj, col) = val;
	ENTRY_LEN (new_obj, col) = len + 1;

	if (flags != 0) {
		new_obj->EN_data.en_cols.en_cols_val[col].ec_flags = flags;
	}
}

/***************************************************************
 copy or modify nis object. this object is used to add or update
 nisplus table entry.
 ****************************************************************/
static BOOL init_nisp_from_sam (nis_object * obj, const SAM_ACCOUNT * sampass,
				nis_object * old)
{
	/*
	 * Fill nis_object for entry add or update.
	 * if we are updateing, we have to find out differences and set
	 * EN_MODIFIED flag. also set need_to_modify to trigger
	 * nis_modify_entry() call in pdb_update_sam_account().
	 *
	 * TODO:
	 *   get data from SAM
	 *   if (modify) get data from nis_object, compare and store if
	 *               different + set EN_MODIFIED and need_to_modify
	 *   else
	 *               store
	 */
	BOOL need_to_modify = False;
	const char *name = pdb_get_username (sampass);	/* from SAM */

	uint32 u_rid;
	uint32 g_rid; 
	/* these must be static or allocate and free entry columns! */
	static fstring uid;	/* from SAM */
	static fstring user_rid;	/* from SAM */
	static fstring gid;	/* from SAM */
	static fstring group_rid;	/* from SAM */
	char *acb;		/* from SAM */
	static fstring smb_passwd;	/* from SAM */
	static fstring smb_nt_passwd;	/* from SAM */
	static fstring logon_t;	/* from SAM */
	static fstring logoff_t;	/* from SAM */
	static fstring kickoff_t;	/* from SAM */
	static fstring pwdlset_t;	/* from SAM */
	static fstring pwdlchg_t;	/* from SAM */
	static fstring pwdmchg_t;	/* from SAM */
	static fstring full_name;	/* from SAM */
	static fstring acct_desc;	/* from SAM */
	static char empty[1];	/* just an empty string */

	if (!(u_rid = pdb_get_user_rid (sampass)))
		return False;
	if (!(g_rid = pdb_get_group_rid (sampass)))
		return False;

	slprintf (uid, sizeof (uid) - 1, "%u", fallback_pdb_user_rid_to_uid (u_rid));
	slprintf (user_rid, sizeof (user_rid) - 1, "%u", u_rid);
	slprintf (gid, sizeof (gid) - 1, "%u", fallback_pdb_group_rid_to_uid (g_rid));
	slprintf (group_rid, sizeof (group_rid) - 1, "%u", g_rid);

	acb = pdb_encode_acct_ctrl (pdb_get_acct_ctrl (sampass),
				    NEW_PW_FORMAT_SPACE_PADDED_LEN);
	pdb_sethexpwd (smb_passwd, pdb_get_lanman_passwd (sampass),
		       pdb_get_acct_ctrl (sampass));
	pdb_sethexpwd (smb_nt_passwd, pdb_get_nt_passwd (sampass),
		       pdb_get_acct_ctrl (sampass));
	slprintf (logon_t, 13, "LNT-%08X",
		  (uint32) pdb_get_logon_time (sampass));
	slprintf (logoff_t, 13, "LOT-%08X",
		  (uint32) pdb_get_logoff_time (sampass));
	slprintf (kickoff_t, 13, "KOT-%08X",
		  (uint32) pdb_get_kickoff_time (sampass));
	slprintf (pwdlset_t, 13, "LCT-%08X",
		  (uint32) pdb_get_pass_last_set_time (sampass));
	slprintf (pwdlchg_t, 13, "CCT-%08X",
		  (uint32) pdb_get_pass_can_change_time (sampass));
	slprintf (pwdmchg_t, 13, "MCT-%08X",
		  (uint32) pdb_get_pass_must_change_time (sampass));
	safe_strcpy (full_name, pdb_get_fullname (sampass),
		     sizeof (full_name) - 1);
	safe_strcpy (acct_desc, pdb_get_acct_desc (sampass),
		     sizeof (acct_desc) - 1);

#if 0

	/* Not sure what to do with these guys. -tpot */

	dos_to_unix (full_name, True);
	dos_to_unix (acct_desc, True);

#endif

	if (old) {
		/* name */
		if (strcmp (ENTRY_VAL (old, NPF_NAME), name)) {
			need_to_modify = True;
			set_single_attribute (obj, NPF_NAME, name,
					      strlen (name), EN_MODIFIED);
		}


		/* uid */
		if (!ENTRY_VAL (old, NPF_UID) || strcmp (ENTRY_VAL (old, NPF_UID), uid)) {
				need_to_modify = True;
				set_single_attribute (obj, NPF_UID, uid, strlen (uid), EN_MODIFIED);
		}

		/* user_rid */
		if (!ENTRY_VAL (old, NPF_USER_RID) || strcmp (ENTRY_VAL (old, NPF_USER_RID), user_rid)) {
				need_to_modify = True;
				set_single_attribute (obj, NPF_USER_RID, user_rid, strlen (user_rid), EN_MODIFIED);
		}

		/* smb_grpid */
		if (!ENTRY_VAL (old, NPF_SMB_GRPID) || strcmp (ENTRY_VAL (old, NPF_SMB_GRPID), gid)) {
				need_to_modify = True;
				set_single_attribute (obj, NPF_SMB_GRPID, gid, strlen (gid), EN_MODIFIED);
		}

		/* group_rid */
		if (!ENTRY_VAL (old, NPF_GROUP_RID) || strcmp (ENTRY_VAL (old, NPF_GROUP_RID), group_rid)) {
				need_to_modify = True;
				set_single_attribute (obj, NPF_GROUP_RID, group_rid, strlen (group_rid), EN_MODIFIED);
		}

		/* acb */
		if (!ENTRY_VAL (old, NPF_ACB) ||
		    strcmp (ENTRY_VAL (old, NPF_ACB), acb)) {
			need_to_modify = True;
			set_single_attribute (obj, NPF_ACB, acb, strlen (acb),
					      EN_MODIFIED);
		}

		/* lmpwd */
		if (!ENTRY_VAL (old, NPF_LMPWD) ||
		    strcmp (ENTRY_VAL (old, NPF_LMPWD), smb_passwd)) {
			need_to_modify = True;
			set_single_attribute (obj, NPF_LMPWD, smb_passwd,
					      strlen (smb_passwd),
					      EN_CRYPT | EN_MODIFIED);
		}

		/* ntpwd */
		if (!ENTRY_VAL (old, NPF_NTPWD) ||
		    strcmp (ENTRY_VAL (old, NPF_NTPWD), smb_nt_passwd)) {
			need_to_modify = True;
			set_single_attribute (obj, NPF_NTPWD, smb_nt_passwd,
					      strlen (smb_nt_passwd),
					      EN_CRYPT | EN_MODIFIED);
		}

		/* logon_t */
		if (pdb_get_logon_time (sampass) &&
		    (!ENTRY_VAL (old, NPF_LOGON_T) ||
		     strcmp (ENTRY_VAL (old, NPF_LOGON_T), logon_t))) {
			need_to_modify = True;
			set_single_attribute (obj, NPF_LOGON_T, logon_t,
					      strlen (logon_t), EN_MODIFIED);
		}

		/* logoff_t */
		if (pdb_get_logoff_time (sampass) &&
		    (!ENTRY_VAL (old, NPF_LOGOFF_T) ||
		     strcmp (ENTRY_VAL (old, NPF_LOGOFF_T), logoff_t))) {
			need_to_modify = True;
			set_single_attribute (obj, NPF_LOGOFF_T, logoff_t,
					      strlen (logoff_t), EN_MODIFIED);
		}

		/* kick_t */
		if (pdb_get_kickoff_time (sampass) &&
		    (!ENTRY_VAL (old, NPF_KICK_T) ||
		     strcmp (ENTRY_VAL (old, NPF_KICK_T), kickoff_t))) {
			need_to_modify = True;
			set_single_attribute (obj, NPF_KICK_T, kickoff_t,
					      strlen (kickoff_t),
					      EN_MODIFIED);
		}

		/* pwdlset_t */
		if (pdb_get_pass_last_set_time (sampass) &&
		    (!ENTRY_VAL (old, NPF_PWDLSET_T) ||
		     strcmp (ENTRY_VAL (old, NPF_PWDLSET_T), pwdlset_t))) {
			need_to_modify = True;
			set_single_attribute (obj, NPF_PWDLSET_T, pwdlset_t,
					      strlen (pwdlset_t),
					      EN_MODIFIED);
		}

		/* pwdlchg_t */
		if (pdb_get_pass_can_change_time (sampass) &&
		    (!ENTRY_VAL (old, NPF_PWDCCHG_T) ||
		     strcmp (ENTRY_VAL (old, NPF_PWDCCHG_T), pwdlchg_t))) {
			need_to_modify = True;
			set_single_attribute (obj, NPF_PWDCCHG_T, pwdlchg_t,
					      strlen (pwdlchg_t),
					      EN_MODIFIED);
		}

		/* pwdmchg_t */
		if (pdb_get_pass_must_change_time (sampass) &&
		    (!ENTRY_VAL (old, NPF_PWDMCHG_T) ||
		     strcmp (ENTRY_VAL (old, NPF_PWDMCHG_T), pwdmchg_t))) {
			need_to_modify = True;
			set_single_attribute (obj, NPF_PWDMCHG_T, pwdmchg_t,
					      strlen (pwdmchg_t),
					      EN_MODIFIED);
		}

		/* full_name */
		/* must support set, unset and change */
		if ((pdb_get_fullname (sampass) &&
		     !ENTRY_VAL (old, NPF_FULL_NAME)) ||
		    (ENTRY_VAL (old, NPF_FULL_NAME) &&
		     !pdb_get_fullname (sampass)) ||
		    (ENTRY_VAL (old, NPF_FULL_NAME) &&
		     pdb_get_fullname (sampass) &&
		     strcmp (ENTRY_VAL (old, NPF_FULL_NAME), full_name))) {
			need_to_modify = True;
			set_single_attribute (obj, NPF_FULL_NAME, full_name,
					      strlen (full_name),
					      EN_MODIFIED);
		}

		/* home_dir */
		/* must support set, unset and change */
		if ((pdb_get_homedir (sampass) &&
		     !ENTRY_VAL (old, NPF_HOME_DIR)) ||
		    (ENTRY_VAL (old, NPF_HOME_DIR) &&
		     !pdb_get_homedir (sampass)) ||
		    (ENTRY_VAL (old, NPF_HOME_DIR) &&
		     pdb_get_homedir (sampass) &&
		     strcmp (ENTRY_VAL (old, NPF_HOME_DIR),
			     pdb_get_homedir (sampass)))) {
			need_to_modify = True;
			set_single_attribute (obj, NPF_HOME_DIR,
					      pdb_get_homedir (sampass),
					      strlen (pdb_get_homedir
						      (sampass)),
					      EN_MODIFIED);
		}

		/* dir_drive */
		/* must support set, unset and change */
		if ((pdb_get_dir_drive (sampass) &&
		     !ENTRY_VAL (old, NPF_DIR_DRIVE)) ||
		    (ENTRY_VAL (old, NPF_DIR_DRIVE) &&
		     !pdb_get_dir_drive (sampass)) ||
		    (ENTRY_VAL (old, NPF_DIR_DRIVE) &&
		     pdb_get_dir_drive (sampass) &&
		     strcmp (ENTRY_VAL (old, NPF_DIR_DRIVE),
			     pdb_get_dir_drive (sampass)))) {
			need_to_modify = True;
			set_single_attribute (obj, NPF_DIR_DRIVE,
					      pdb_get_dir_drive (sampass),
					      strlen (pdb_get_dir_drive
						      (sampass)),
					      EN_MODIFIED);
		}

		/* logon_script */
		/* must support set, unset and change */
		if (((pdb_get_logon_script (sampass) &&
		      !ENTRY_VAL (old, NPF_LOGON_SCRIPT)) ||
		     ((ENTRY_VAL (old, NPF_LOGON_SCRIPT) &&
		       (!pdb_get_logon_script (sampass)))) ||
		     ((ENTRY_VAL (old, NPF_LOGON_SCRIPT) &&
		       pdb_get_logon_script (sampass) &&
		       strcmp (ENTRY_VAL (old, NPF_LOGON_SCRIPT),
			       pdb_get_logon_script (sampass)))))) {
			need_to_modify = True;
			set_single_attribute (obj, NPF_LOGON_SCRIPT,
					      pdb_get_logon_script (sampass),
					      strlen (pdb_get_logon_script
						      (sampass)),
					      EN_MODIFIED);
		}

		/* profile_path */
		/* must support set, unset and change */
		if ((pdb_get_profile_path (sampass) &&
		     !ENTRY_VAL (old, NPF_PROFILE_PATH)) ||
		    (ENTRY_VAL (old, NPF_PROFILE_PATH) &&
		     !pdb_get_profile_path (sampass)) ||
		    (ENTRY_VAL (old, NPF_PROFILE_PATH) &&
		     pdb_get_profile_path (sampass) &&
		     strcmp (ENTRY_VAL (old, NPF_PROFILE_PATH),
			     pdb_get_profile_path (sampass)))) {
			need_to_modify = True;
			set_single_attribute (obj, NPF_PROFILE_PATH,
					      pdb_get_profile_path (sampass),
					      strlen (pdb_get_profile_path
						      (sampass)),
					      EN_MODIFIED);
		}

		/* acct_desc */
		/* must support set, unset and change */
		if ((pdb_get_acct_desc (sampass) &&
		     !ENTRY_VAL (old, NPF_ACCT_DESC)) ||
		    (ENTRY_VAL (old, NPF_ACCT_DESC) &&
		     !pdb_get_acct_desc (sampass)) ||
		    (ENTRY_VAL (old, NPF_ACCT_DESC) &&
		     pdb_get_acct_desc (sampass) &&
		     strcmp (ENTRY_VAL (old, NPF_ACCT_DESC), acct_desc))) {
			need_to_modify = True;
			set_single_attribute (obj, NPF_ACCT_DESC, acct_desc,
					      strlen (acct_desc),
					      EN_MODIFIED);
		}

		/* workstations */
		/* must support set, unset and change */
		if ((pdb_get_workstations (sampass) &&
		     !ENTRY_VAL (old, NPF_WORKSTATIONS)) ||
		    (ENTRY_VAL (old, NPF_WORKSTATIONS) &&
		     !pdb_get_workstations (sampass)) ||
		    (ENTRY_VAL (old, NPF_WORKSTATIONS) &&
		     (pdb_get_workstations (sampass)) &&
		     strcmp (ENTRY_VAL (old, NPF_WORKSTATIONS),
			     pdb_get_workstations (sampass)))) {
			need_to_modify = True;
			set_single_attribute (obj, NPF_WORKSTATIONS,
					      pdb_get_workstations (sampass),
					      strlen (pdb_get_workstations
						      (sampass)),
					      EN_MODIFIED);
		}

		/* hours */
		if ((pdb_get_hours_len (sampass) !=
		     ENTRY_LEN (old, NPF_HOURS))
		    || memcmp (pdb_get_hours (sampass),
			       ENTRY_VAL (old, NPF_HOURS), ENTRY_LEN (old,
								      NPF_HOURS)))
		{
			need_to_modify = True;
			/* set_single_attribute will add 1 for len ... */
			set_single_attribute (obj, NPF_HOURS,
					      pdb_get_hours (sampass),
					      pdb_get_hours_len (sampass) - 1,
					      EN_MODIFIED);
		}
	} else {
		const char *homedir, *dirdrive, *logon_script, *profile_path,
			*workstations;

		*empty = '\0';	/* empty string */

		set_single_attribute (obj, NPF_NAME, name, strlen (name), 0);
		set_single_attribute (obj, NPF_UID, uid, strlen (uid), 0);
		set_single_attribute (obj, NPF_USER_RID, user_rid,
				      strlen (user_rid), 0);
		set_single_attribute (obj, NPF_SMB_GRPID, gid, strlen (gid),
				      0);
		set_single_attribute (obj, NPF_GROUP_RID, group_rid,
				      strlen (group_rid), 0);
		set_single_attribute (obj, NPF_ACB, acb, strlen (acb), 0);
		set_single_attribute (obj, NPF_LMPWD, smb_passwd,
				      strlen (smb_passwd), EN_CRYPT);
		set_single_attribute (obj, NPF_NTPWD, smb_nt_passwd,
				      strlen (smb_nt_passwd), EN_CRYPT);
		set_single_attribute (obj, NPF_LOGON_T, logon_t,
				      strlen (logon_t), 0);
		set_single_attribute (obj, NPF_LOGOFF_T, logoff_t,
				      strlen (logoff_t), 0);
		set_single_attribute (obj, NPF_KICK_T, kickoff_t,
				      strlen (kickoff_t), 0);
		set_single_attribute (obj, NPF_PWDLSET_T, pwdlset_t,
				      strlen (pwdlset_t), 0);
		set_single_attribute (obj, NPF_PWDCCHG_T, pwdlchg_t,
				      strlen (pwdlchg_t), 0);
		set_single_attribute (obj, NPF_PWDMCHG_T, pwdmchg_t,
				      strlen (pwdmchg_t), 0);
		set_single_attribute (obj, NPF_FULL_NAME,
				      full_name, strlen (full_name), 0);

		if (!(homedir = pdb_get_homedir (sampass)))
			homedir = empty;

		set_single_attribute (obj, NPF_HOME_DIR,
				      homedir, strlen (homedir), 0);

		if (!(dirdrive = pdb_get_dir_drive (sampass)))
			dirdrive = empty;

		set_single_attribute (obj, NPF_DIR_DRIVE,
				      dirdrive, strlen (dirdrive), 0);

		if (!(logon_script = pdb_get_logon_script (sampass)))
			logon_script = empty;

		set_single_attribute (obj, NPF_LOGON_SCRIPT,
				      logon_script, strlen (logon_script), 0);

		if (!(profile_path = pdb_get_profile_path (sampass)))
			profile_path = empty;

		set_single_attribute (obj, NPF_PROFILE_PATH,
				      profile_path, strlen (profile_path), 0);

		set_single_attribute (obj, NPF_ACCT_DESC,
				      acct_desc, strlen (acct_desc), 0);

		if (!(workstations = pdb_get_workstations (sampass)))
			workstations = empty;

		set_single_attribute (obj, NPF_WORKSTATIONS,
				      workstations, strlen (workstations), 0);

		/* set_single_attribute will add 1 for len ... */
		set_single_attribute (obj, NPF_HOURS,
				      pdb_get_hours (sampass),
				      pdb_get_hours_len (sampass) - 1, 0);
	}

	return need_to_modify;
}

/***************************************************************
 calls nis_list, returns results.
 ****************************************************************/
static nis_result *nisp_get_nis_list (const char *nisname, unsigned int flags)
{
	nis_result *result;
	int i;

	if (!flags)
		flags = FOLLOW_LINKS | FOLLOW_PATH | EXPAND_NAME |
			HARD_LOOKUP;

	for (i = 0; i < 2; i++) {
		alarm (60);	/* hopefully ok for long searches */
		result = nis_list (nisname, flags, NULL, NULL);

		alarm (0);
		CatchSignal (SIGALRM, SIGNAL_CAST SIG_DFL);

		if (!(flags & MASTER_ONLY) && NIS_RES_NUMOBJ (result) <= 0) {
			/* nis replicas are not in sync perhaps?
			 * this can happen, if account was just added.
			 */
			DEBUG (10, ("will try master only\n"));
			nis_freeresult (result);
			flags |= MASTER_ONLY;
		} else
			break;
	}
	return result;
}

static void free_private_data(void **vp)
{
	struct nisplus_private_info **private = (struct nisplus_private_info **)vp;

	if ((*private)->result) {
		nis_freeresult ((*private)->result);
	}

	free(*private);

        /* No need to free any further, as it is talloc()ed */
}

NTSTATUS pdb_init_nisplussam (PDB_CONTEXT * pdb_context,
			      PDB_METHODS ** pdb_method, const char *location)
{
	NTSTATUS nt_status;
	struct nisplus_private_info *private = malloc (sizeof (struct nisplus_private_info));

	ZERO_STRUCT(private);
	p->location = talloc_strdup(pdb_context->mem_ctx, location);

	if (!NT_STATUS_IS_OK
	    (nt_status =
	     make_pdb_methods (pdb_context->mem_ctx, pdb_method))) {
		return nt_status;
	}

	(*pdb_method)->name = "nisplussam";

	/* Functions your pdb module doesn't provide should be set 
	 * to NULL */

	(*pdb_method)->setsampwent = nisplussam_setsampwent;
	(*pdb_method)->endsampwent = nisplussam_endsampwent;
	(*pdb_method)->getsampwent = nisplussam_getsampwent;
	(*pdb_method)->getsampwnam = nisplussam_getsampwnam;
	(*pdb_method)->getsampwsid = nisplussam_getsampwsid;
	(*pdb_method)->add_sam_account = nisplussam_add_sam_account;
	(*pdb_method)->update_sam_account = nisplussam_update_sam_account;
	(*pdb_method)->delete_sam_account = nisplussam_delete_sam_account;
	(*pdb_method)->free_private_data = free_private_data;
	(*pdb_method)->private_data = private;

	return NT_STATUS_OK;
}

NTSTATUS pdb_nisplus_init(void)
{
	return smb_register_passdb(PASSDB_INTERFACE_VERSION, "nisplussam", pdb_init_nisplussam);
}