// Copyright (c) 2003 David Muse
// See the COPYING file for more information

#include <rudiments/shadowentry.h>
#include <rudiments/charstring.h>
#include <rudiments/rawbuffer.h>
#include <rudiments/error.h>

// for spwd, functions
#include <shadow.h>

#include <stdio.h>
#include <stdlib.h>

#define MAXBUFFER	(32*1024)

#ifdef RUDIMENTS_NAMESPACE
namespace rudiments {
#endif

class shadowentryprivate {
	friend class shadowentry;
	private:
		spwd	*_sp;
		#if defined(RUDIMENTS_HAVE_GETSPNAM_R)
			spwd	_spbuffer;
			char	*_buffer;
		#endif
};

// LAME: not in the class
#if !defined(RUDIMENTS_HAVE_GETSPNAM_R)
static mutex	*_spmutex;
#endif


shadowentry::shadowentry() {
	pvt=new shadowentryprivate;
	pvt->_sp=NULL;
	#ifdef RUDIMENTS_HAVE_GETSPNAM_R
		rawbuffer::zero(&pvt->_spbuffer,sizeof(pvt->_spbuffer));
		pvt->_buffer=NULL;
	#endif
}

shadowentry::shadowentry(const shadowentry &s) {
	pvt=new shadowentryprivate;
	initialize(s.getName());
}

shadowentry &shadowentry::operator=(const shadowentry &s) {
	pvt=new shadowentryprivate;
	if (this!=&s) {
		initialize(s.getName());
	}
	return *this;
}

shadowentry::~shadowentry() {
	#ifdef RUDIMENTS_HAVE_GETSPNAM_R
		delete[] pvt->_buffer;
	#endif
	delete pvt;
}

const char *shadowentry::getName() const {
	return pvt->_sp->sp_namp;
}

const char *shadowentry::getEncryptedPassword() const {
	return pvt->_sp->sp_pwdp;
}

long shadowentry::getLastChangeDate() const {
	return pvt->_sp->sp_lstchg;
}

int shadowentry::getDaysBeforeChangeAllowed() const {
	return pvt->_sp->sp_min;
}

int shadowentry::getDaysBeforeChangeRequired() const {
	return pvt->_sp->sp_max;
}

int shadowentry::getDaysBeforeExpirationWarning() const {
#ifdef RUDIMENTS_HAVE_SP_WARN
	return pvt->_sp->sp_warn;
#else
	return -1;
#endif
}

int shadowentry::getDaysOfInactivityAllowed() const {
#ifdef RUDIMENTS_HAVE_SP_INACT
	return pvt->_sp->sp_inact;
#else
	return -1;
#endif
}

int shadowentry::getExpirationDate() const {
#ifdef RUDIMENTS_HAVE_SP_EXPIRE
	return pvt->_sp->sp_expire;
#else
	return -1;
#endif
}

int shadowentry::getFlag() const {
#ifdef RUDIMENTS_HAVE_SP_FLAG
	return pvt->_sp->sp_flag;
#else
	return -1;
#endif
}

bool shadowentry::needsMutex() {
	#if !defined(RUDIMENTS_HAVE_GETSPNAM_R)
		return true;
	#else
		return false;
	#endif
}

void shadowentry::setMutex(mutex *mtx) {
	#if !defined(RUDIMENTS_HAVE_GETSPNAM_R)
		_spmutex=mtx;
	#endif
}

bool shadowentry::initialize(const char *username) {

	#ifdef RUDIMENTS_HAVE_GETSPNAM_R
		if (pvt->_sp) {
			pvt->_sp=NULL;
			delete[] pvt->_buffer;
			pvt->_buffer=NULL;
		}
		// getspnam_r is goofy.
		// It will retrieve an arbitrarily large amount of data, but
		// requires that you pass it a pre-allocated buffer.  If the
		// buffer is too small, it returns an ENOMEM and you have to
		// just make the buffer bigger and try again.
		for (int size=1024; size<MAXBUFFER; size=size+1024) {
			pvt->_buffer=new char[size];
			#if defined(RUDIMENTS_HAVE_GETSPNAM_R_5)
			if (!getspnam_r(username,&pvt->_spbuffer,
					pvt->_buffer,size,&pvt->_sp)) {
				return (pvt->_sp!=NULL);
			}
			#elif defined(RUDIMENTS_HAVE_GETSPNAM_R_4)
			if ((pvt->_sp=getspnam_r(username,
					&pvt->_spbuffer,pvt->_buffer,size))) {
				return true;
			}
			#endif
			delete[] pvt->_buffer;
			pvt->_buffer=NULL;
			pvt->_sp=NULL;
			if (error::getErrorNumber()!=ENOMEM) {
				return false;
			}
		}
		return false;
	#else
		pvt->_sp=NULL;
		return (!(_spmutex && !_spmutex->lock()) &&
			((pvt->_sp=getspnam(
				const_cast<char *>(username)))!=NULL) &&
			!(_spmutex && !_spmutex->unlock()));
	#endif
}

bool shadowentry::getEncryptedPassword(const char *username, char **password) {
	shadowentry	sp;
	if (sp.initialize(username)) {
		*password=charstring::duplicate(sp.getEncryptedPassword());
		return true;
	}
	return false;
}

bool shadowentry::getLastChangeDate(const char *username, long *lstchg) {
	shadowentry	sp;
	if (sp.initialize(username)) {
		*lstchg=sp.getLastChangeDate();
		return true;
	}
	return false;
}

bool shadowentry::getDaysBeforeChangeAllowed(const char *username, int *min) {
	shadowentry	sp;
	if (sp.initialize(username)) {
		*min=sp.getDaysBeforeChangeAllowed();
		return true;
	}
	return false;
}

bool shadowentry::getDaysBeforeChangeRequired(const char *username, int *max) {
	shadowentry	sp;
	if (sp.initialize(username)) {
		*max=sp.getDaysBeforeChangeRequired();
		return true;
	}
	return false;
}

bool shadowentry::getDaysBeforeExpirationWarning(const char *username,
								int *warn) {
#ifdef RUDIMENTS_HAVE_SP_WARN
	shadowentry	sp;
	if (sp.initialize(username)) {
		*warn=sp.getDaysBeforeExpirationWarning();
		return true;
	}
	return false;
#else
	*warn=-1;
	return true;
#endif
}

bool shadowentry::getDaysOfInactivityAllowed(const char *username, int *inact) {
#ifdef RUDIMENTS_HAVE_SP_INACT
	shadowentry	sp;
	if (sp.initialize(username)) {
		*inact=sp.getDaysOfInactivityAllowed();
		return true;
	}
	return false;
#else
	*inact=-1;
	return true;
#endif
}

bool shadowentry::getExpirationDate(const char *username, int *expire) {
#ifdef RUDIMENTS_HAVE_SP_EXPIRE
	shadowentry	sp;
	if (sp.initialize(username)) {
		*expire=sp.getExpirationDate();
		return true;
	}
	return false;
#else
	*expire=-1;
	return true;
#endif
}

bool shadowentry::getFlag(const char *username, int *flag) {
#ifdef RUDIMENTS_HAVE_SP_FLAG
	shadowentry	sp;
	if (sp.initialize(username)) {
		*flag=sp.getFlag();
		return true;
	}
	return false;
#else
	*flag=-1;
	return true;
#endif
}

void shadowentry::print() const {

	if (!pvt->_sp) {
		return;
	}

	printf("Name: %s\n",getName());
	printf("Encrypted Password: %s\n",getEncryptedPassword());
	printf("Last Change: %ld\n",getLastChangeDate());
	printf("Days Before Change Allowed: %d\n",
				getDaysBeforeChangeAllowed());
	printf("Days Before Change Required: %d\n",
				getDaysBeforeChangeRequired());
	printf("Days Before Expiration Warning: %d\n",
				getDaysBeforeExpirationWarning());
	printf("Days Of Inactivity Allowed: %d\n",
				getDaysOfInactivityAllowed());
	printf("Expiration Date: %d\n",getExpirationDate());
	printf("Flag: %d\n",getFlag());
}

#ifdef RUDIMENTS_NAMESPACE
}
#endif


syntax highlighted by Code2HTML, v. 0.9.1