/* 

Written by Beau Kuiper <support@muddleftpd.cx>

Use, modification and distribution is allowed without limitation,
warranty, or liability of any kind.

*/

#include "config.h"
#include "auth.h"
#include <sys/time.h>
#include <pwd.h>
#include <time.h>

#ifdef HAVE_GETSPNAM
#include <shadow.h>
#endif

/* This file contains code to autheticate against unix password files */

typedef struct 
{
	struct passwd *passent;
#ifdef HAVE_GETSPNAM
	struct spwd *sppassent;
#endif
} PASSWDSTRUCT;

int getday(void)
{
	time_t currenttime = time(NULL);
	struct tm *currenttm = localtime(&currenttime);
	int yeardif = currenttm->tm_year - 70;
	int days;
	
	days = yeardif * 365 + currenttm->tm_yday;
	
	/* THIS STUFF IS Y2K COMPLIANT */
	days += (yeardif + 1) / 4;
		
	/* I don't ever expect this code to be used beyond 2100, but so did
	   too many cobol programmers in the 1970's !! */
	if (currenttm->tm_year > 200)		/* simple algorithm */
	{
		yeardif -= 31;
		days -= (yeardif) / 100; /* take centaries off */
		days += (yeardif) / 400; /* add on 400 year leapyears */
	}
	return(days);
} 
	
void *gethandle(void *peer, void *tset, char *username, int *err)
{
	PASSWDSTRUCT *newhandle;
	char *passwdmode;
	
	newhandle = mallocwrapper(sizeof(PASSWDSTRUCT));
	passwdmode = mktokconfstr(tset, auth_getcursectionid(peer), "passwdmode", "");
		
	newhandle->passent = getpwnam(username);
	if (newhandle->passent == NULL)
		goto error;

#ifdef HAVE_GETSPNAM
	
	/* this will try to autodetect shadow passwordness */
	if ((strcasecmp(passwdmode, "shadow") == 0) || 
	    (strcmp(newhandle->passent->pw_passwd, "x") == 0))
	{
		if (strcasecmp(passwdmode, "normal") == 0)
			newhandle->sppassent = NULL;
		else
		{
			newhandle->sppassent = getspnam(username);
			if (newhandle->sppassent == NULL)
				goto error;
		}
	}
	else
		newhandle->sppassent = NULL;

#endif
	freewrapper(passwdmode);
	*err = AUTH_OK;
	return(newhandle);

error:
	freewrapper(passwdmode);
	*err = AUTH_USERNKNOW;
	freewrapper(newhandle);
	return(NULL);
}

void freehandle(void *handle)
{
	freewrapper(handle);
}

int chkpasswd(void *h, char *password, char **errmsg)
{
	PASSWDSTRUCT *handle = (PASSWDSTRUCT *)h;

#ifdef HAVE_GETSPNAM
	if (handle->sppassent)
	{
		int days = getday();	
		int expiredate = handle->sppassent->sp_max + handle->sppassent->sp_lstchg +
			         + handle->sppassent->sp_inact;

		/* do the account expiration checking! */		
		if ((days > handle->sppassent->sp_expire) && 
		   (handle->sppassent->sp_expire != -1))
		{
			*errmsg = safe_snprintf("Account has expired");
			return(FALSE);
		}
		
		if ((handle->sppassent->sp_inact != -1) && (handle->sppassent->sp_max != -1)
		   && (handle->sppassent->sp_lstchg != -1) && (days > expiredate))
		{
			*errmsg = safe_snprintf("Account disabled, needs new password");
			return(FALSE);
		}
		
		return(chkpassword(handle->sppassent->sp_pwdp, password));
	}	
#endif 
	return(chkpassword(handle->passent->pw_passwd, password));
}

char *gethomedir(void *h)
{
	return(((PASSWDSTRUCT *)h)->passent->pw_dir);
}

char *getrootdir(void *h)
{
	return("/");
}

uid_t getuseruid(void *h)
{
	return(((PASSWDSTRUCT *)h)->passent->pw_uid);
}

gid_t getusergid(void *h)
{
	return(((PASSWDSTRUCT *)h)->passent->pw_gid);
}

gid_t *getusersupgid(void *h)
{
	return(getusergrouplist(((PASSWDSTRUCT *)h)->passent->pw_name));
}
