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

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

#ifdef MINGW32
	// for LPGROUP_INFO_3, functions
	#include <windows.h>
	#include <lm.h>
#else
	// for group, functions
	#include <grp.h>
#endif

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

#define MAXBUFFER (32*1024)

#ifdef RUDIMENTS_NAMESPACE
namespace rudiments {
#endif

class groupentryprivate {
	friend class groupentry;
	private:
		#ifndef MINGW32
			group		*_grp;
			#if defined(RUDIMENTS_HAVE_GETGRNAM_R) && \
				defined(RUDIMENTS_HAVE_GETGRGID_R)
				group		_grpbuffer;
				char		*_buffer;
			#endif
		#else
			LPGROUP_INFO_3	_buffer;
			char		*_name;
		#endif
};

// LAME: not in the class
#if (!defined(RUDIMENTS_HAVE_GETGRNAM_R) || !defined(RUDIMENTS_HAVE_GETGRUID_R))
static mutex	*_gemutex;
#endif

groupentry::groupentry() {
	pvt=new groupentryprivate;
	pvt->_grp=NULL;
#ifndef MINGW32
	#if defined(RUDIMENTS_HAVE_GETGRNAM_R) && \
		defined(RUDIMENTS_HAVE_GETGRGID_R)
		rawbuffer::zero(&pvt->_grpbuffer,sizeof(pvt->_grpbuffer));
		pvt->_buffer=NULL;
	#endif
#else
	pvt->_buffer=NULL;
	pvb->_name=NULL;
#endif
}

groupentry::groupentry(const groupentry &g) {
	pvt=new groupentryprivate;
	initialize(g.getName());
}

groupentry &groupentry::operator=(const groupentry &g) {
	if (this!=&g) {
		initialize(g.getName());
	}
	return *this;
}

groupentry::~groupentry() {
#ifndef MINGW32
	#if defined(RUDIMENTS_HAVE_GETGRNAM_R) && \
		defined(RUDIMENTS_HAVE_GETGRGID_R)
		delete[] pvt->_buffer;
	#endif
	delete pvt;
#else
	if (pvt->_buffer) {
		NetApiBufferFree(pvt->_buffer);
	}
	delete[] pvt->_name;
#endif
}

const char *groupentry::getName() const {
#ifndef MINGW32
	return pvt->_grp->gr_name;
#else
	return pvt->_name;
#endif
}

const char *groupentry::getPassword() const {
#ifndef MINGW32
	return pvt->_grp->gr_passwd;
#else
	// FIXME:
	return "";
#endif
}

gid_t groupentry::getGroupId() const {
#ifndef MINGW32
	return pvt->_grp->gr_gid;
#else
	// FIXME:
	return 0;
#endif
}

#ifdef MINGW32
static char *members[]={NULL};
#endif

const char * const *groupentry::getMembers() const {
#ifndef MINGW32
	return pvt->_grp->gr_mem;
#else
	// FIXME:
	return members;
#endif
}

bool groupentry::needsMutex() {
	#if !defined(RUDIMENTS_HAVE_GETGRNAM_R) || \
		!defined(RUDIMENTS_HAVE_GETGRUID_R)
		return true;
	#else
		return false;
	#endif
}

void groupentry::setMutex(mutex *mtx) {
	#if !defined(RUDIMENTS_HAVE_GETGRNAM_R) || \
		!defined(RUDIMENTS_HAVE_GETGRUID_R)
		_gemutex=mtx;
	#endif
}

bool groupentry::initialize(const char *groupname) {
	return initialize(groupname,0);
}

bool groupentry::initialize(gid_t groupid) {
	return initialize(NULL,groupid);
}

#ifndef MINGW32
bool groupentry::initialize(const char *groupname, gid_t groupid) {

	#if defined(RUDIMENTS_HAVE_GETGRNAM_R) && \
		defined(RUDIMENTS_HAVE_GETGRGID_R)
		if (pvt->_grp) {
			pvt->_grp=NULL;
			delete[] pvt->_buffer;
			pvt->_buffer=NULL;
		}
		// getgrnam_r and getgrgid_t are goofy.
		// They will retrieve an arbitrarily large amount of data, but
		// require that you pass them a pre-allocated buffer.  If the
		// buffer is too small, they return 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_GETGRNAM_R_5) && \
				defined(RUDIMENTS_HAVE_GETGRGID_R_5)
			if (!((groupname)
				?(getgrnam_r(groupname,
						&pvt->_grpbuffer,
						pvt->_buffer,size,
						&pvt->_grp))
				:(getgrgid_r(groupid,
						&pvt->_grpbuffer,
						pvt->_buffer,size,
						&pvt->_grp)))) {
				return (pvt->_grp!=NULL);
			}
			#elif defined(RUDIMENTS_HAVE_GETGRNAM_R_4) && \
				defined(RUDIMENTS_HAVE_GETGRGID_R_4)
			if ((groupname)
				?(pvt->_grp=getgrnam_r(groupname,
							&pvt->_grpbuffer,
							pvt->_buffer,size))
				:(pvt->_grp=getgrgid_r(groupid,
							&pvt->_grpbuffer,
							pvt->_buffer,size))) {
				return true;
			}
			#endif
			delete[] pvt->_buffer;
			pvt->_buffer=NULL;
			pvt->_grp=NULL;
			if (error::getErrorNumber()!=ENOMEM) {
				return false;
			}
		}
		return false;
	#else
		pvt->_grp=NULL;
		return (!(_gemutex && !_gemutex->lock()) &&
			((pvt->_grp=((groupname)
				?getgrnam(groupname)
				:getgrgid(groupid)))!=NULL) &&
			!(_gemutex && !_gemutex->unlock()));
	#endif
}
#else
bool groupentry::initialize(const char *groupname, gid_t groupid) {

	if (pvt->_buffer) {
		NetApiBufferFree(pvt->_buffer);
	}
	delete[] pvt->_name;
	pvt->_name=NULL;

	if (groupname) {	
		if (NetGroupGetInfo(NULL,groupname,3,
				reinterpret_cast<LPBYTE *>(&pvt->_buffer))!=
				NERR_SUCCESS) {
			return false;
		}
		pvt->_name=new char[charstring::length(
					pvt->_buffer->usri23_name)]+1;
		wsprintf(pvt->_name,"%s",pvt->_buffer->usri23_name);
	} else {
#error implement me...
	}
	return true;
}
#endif

bool groupentry::getPassword(const char *groupname, char **password) {
	groupentry	grp;
	if (grp.initialize(groupname)) {
		*password=charstring::duplicate(grp.getPassword());
		return true;
	}
	return false;
}

bool groupentry::getGroupId(const char *groupname, gid_t *groupid) {
	groupentry	grp;
	if (grp.initialize(groupname)) {
		*groupid=grp.getGroupId();
		return true;
	}
	return false;
}

bool groupentry::getMembers(const char *groupname, char ***members) {
	groupentry	grp;
	if (grp.initialize(groupname)) {
		int	counter;
		for (counter=0; grp.getMembers()[counter]; counter++);
		char	**memb=new char *[counter+1];
		memb[counter]=NULL;
		for (int i=0; i<counter; i++) {
			memb[i]=charstring::duplicate(grp.getMembers()[i]);
		}
		*members=memb;
		return true;
	}
	return false;
}

bool groupentry::getName(gid_t groupid, char **name) {
	groupentry	grp;
	if (grp.initialize(groupid)) {
		*name=charstring::duplicate(grp.getName());
		return true;
	}
	return false;
}

bool groupentry::getPassword(gid_t groupid, char **password) {
	groupentry	grp;
	if (grp.initialize(groupid)) {
		*password=charstring::duplicate(grp.getPassword());
		return true;
	}
	return false;
}

bool groupentry::getMembers(gid_t groupid, char ***members) {
	groupentry	grp;
	if (grp.initialize(groupid)) {
		int	counter;
		for (counter=0; grp.getMembers()[counter]; counter++);
		char	**memb=new char *[counter+1];
		memb[counter]=NULL;
		for (int i=0; i<counter; i++) {
			memb[i]=charstring::duplicate(grp.getMembers()[i]);
		}
		*members=memb;
		return true;
	}
	return false;
}

void groupentry::print() const {

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

	printf("Name: %s\n",getName());
	printf("Password: %s\n",getPassword());
	printf("Group Id: %d\n",getGroupId());
	printf("Members:\n");
	for (int i=0; getMembers()[i]; i++) {
		printf("	%s\n",getMembers()[i]);
	}
}

#ifdef RUDIMENTS_NAMESPACE
}
#endif


syntax highlighted by Code2HTML, v. 0.9.1