/*
 * © Copyright 1996-2012 ECMWF.
 * 
 * This software is licensed under the terms of the Apache Licence Version 2.0
 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. 
 * In applying this licence, ECMWF does not waive the privileges and immunities 
 * granted to it by virtue of its status as an intergovernmental organisation nor
 * does it submit to any jurisdiction.
 */

#include "mars.h"
#include <pwd.h>
#include <grp.h>
#include <errno.h>



void add_groups(const char *u,request *r)
{
#ifdef ECMWF
	struct group *gr;
	char **cp;
	int found = false;

	setgrent();
	while (gr = getgrent()) {

		for (cp = gr->gr_mem; cp && *cp; cp++)
			if (EQ(*cp, u)) 
			{
				add_value(r,"group","%s",gr->gr_name);
				found=true;
			}
	}

	if(!found)
	{
		FILE *f = mail_open(mars.dhsmail,"getgrent failed for %s",u);
		mail_request(f,"Environment :",r);
		mail_close(f);

		marslog(LOG_WARN,"Unable to get group information, getgrent");
		add_value(r,"group","unknown");
	}

	endgrent();
#endif
}

#ifdef ECMWF
#define READ_EMS_MAX_TRY 3
static void read_ems_cache(const char* who,request* env)
{
	char buf[1024];
	FILE* in = NULL;
	typedef enum reason { 
		  notfound, 
		  cantopen
	} reason;
	reason why;
	int retry = 1;

	/* Try to read cached file several times, in case of updates happenning */
	while(retry++ < READ_EMS_MAX_TRY)
	{
		if((in = fopen(mars.emsfile,"r")) != NULL)
		{
			while(fgets(buf,sizeof(buf)-1,in))
			{
				char user[1024]; 
				char category[1024];
				sscanf(buf,"%s %s",user,category);
				if(EQ(user,who))
				{
					marslog(LOG_DBUG,"Using EMS categories '%s'",category);
					set_list(env,"category",category);
					fclose(in);
					return;
				}
			}

			fclose(in);
			if(mars.marslite_mode == 1)
			{
				const char *c = getenv("MARS_EMS_CATEGORY_MARSLITE_MODE");
				if(c == NULL)
					c = "public";
				set_list(env,"category",c);
				return;
			}

			marslog(LOG_WARN,"Can't find EMS categories for user '%s'",who);
			why = notfound;
		}
		else
		{
			marslog(LOG_EROR|LOG_PERR,"Cannot open %s",mars.emsfile);
			why = cantopen;
		}
		sleep(2);
	}

	/* Inform about problems with EMS */
	{
		int e = errno;
		FILE *f = mail_open(mars.dhsmail,"read_ems_cache failed for %s",who);
		switch(why)
		{
			case notfound:
				mail_msg(f,"Can't find EMS categories for user '%s'",who);
				mail_msg(f,"in EMS file %s",mars.emsfile);
				break;
			case cantopen:
				mail_msg(f,"Cannot open %s",mars.emsfile);
				if(e)
					mail_msg(f,"(%s)",strerror(e));
				break;
			default:
				mail_msg(f,"Unknown reason (%d) for user '%s'",why,who);
				break;
		}
		mail_request(f,"Environment :",env);
		mail_close(f);
	}

	marslog(LOG_WARN,"Using NIS group information");
	/* temp fix */ add_groups(who,env);
}


#define READ_EMS_ACCOUNT_MAX_TRY 5
static void read_ems_accounts(const char* who,request* env)
{
	char buf[1024];
	FILE* in = NULL;
	typedef enum reason { 
		  notfound, 
		  cantopen
	} reason;
	reason why;
	int retry = 1;

	/* Try to read cached file several times, in case of updates happenning */
	while(retry++ < READ_EMS_ACCOUNT_MAX_TRY)
	{
		if((in = fopen(mars.emsaccountsfile,"r")) != NULL)
		{
			while(fgets(buf,sizeof(buf)-1,in))
			{
				char user[1024]; 
				char account[1024];
				sscanf(buf,"%s %s",user,account);
				if(EQ(user,who))
				{
					marslog(LOG_DBUG,"Using EMS accounts '%s'",account);
					set_list(env,"account",account);
					fclose(in);
					return;
				}
			}

			fclose(in);
			if(mars.marslite_mode == 1)
			{
				const char *c = getenv("MARS_EMS_ACCOUNT_MARSLITE_MODE");
				if(c == NULL)
					c = "dspublic";
				set_list(env,"account",c);
				return;
			}
			marslog(LOG_WARN,"Can't find EMS accounts for user '%s'",who);
			why = notfound;
		}
		else
		{
			marslog(LOG_EROR|LOG_PERR,"Cannot open %s",mars.emsaccountsfile);
			why = cantopen;
		}
		sleep(2);
	}

	/* Inform about problems with EMS */
	{
		if(mars.enable_ecaccount_email)
		{
			int e = errno;
			FILE *f = mail_open(mars.dhsmail,"read_ems_accounts failed for %s",who);
			switch(why)
			{
				case notfound:
					mail_msg(f,"Can't find EMS accounts for user '%s'",who);
					mail_msg(f,"in EMS file %s",mars.emsaccountsfile);
					break;
				case cantopen:
					mail_msg(f,"Cannot open %s",mars.emsaccountsfile);
					if(e>0)
						mail_msg(f,"(%s)",strerror(e));
					break;
				default:
					mail_msg(f,"Unknown reason (%d) for user '%s'",why,who);
					break;
			}
			mail_request(f,"Environment :",env);
			mail_close(f);
		}
	}

	/* temp fix */ 
	set_value(env,"account","unknown");
}

#endif
static request *env = NULL;

#ifdef ECMWF
boolean user_in_ABC_account(const char *user, const char *account, const request *env)
{
	int     i     = 0;
	boolean match = false;
	const char *v = NULL;

	while((v = get_value(env,"account",i++)) != 0)
	{
		if(EQ(account,v))
			match = true;
	}
	return match;
}

#endif

request *get_environ(void)
{
	char buf[1024];
	unsigned long id;
	char *useWebmars     = getenv("WEBMARS_USER");
	char *emosChangeUser = getenv("EMOS_CHANGE_USER");
	const char *who      = user(NULL);
	char *useTest        = getenv("MARS_ENV");
	const char *abc      = strcache(getenv("ECACCOUNT"));
	const char *owner    = strcache(getenv("ECREALOWNER"));
	char *environment    = getenv("ENVIRONMENT");
	char *marsClientHost = getenv("MARS_CLIENT_HOSTNAME");

	if(env) return env;

	if(EQ(who,"bamboo") || EQ(who,"deploy") || (EQ(who,"worker") && !mars.marslite_mode) )
		who = user("max");

	if(useTest && EQ(who,"max"))
	{
		env = read_request_file(useTest);
		marslog(LOG_WARN,"User %s has changed the environment",who);
		print_all_requests(env);
		return env;
	}

	env = new_request(strcache("environ"),NULL);

#if defined(ECMWF) && !defined(NOEMS)
	if((useWebmars && EQ(who,"max")) ||
	   (emosChangeUser && EQ(who,"emos")) ||
	   (useWebmars && EQ(who,"worker") && mars.marslite_mode))
	{
		char *u = useWebmars;
		if(emosChangeUser)
			u = emosChangeUser;
		set_value(env,"user",u);
		if(getenv("MARS_EMS_CATEGORIES"))
		{
			if(mars.show_hosts)
				marslog(LOG_INFO,"Using EMS categories '%s'",getenv("MARS_EMS_CATEGORIES"));
			set_list(env,"category",getenv("MARS_EMS_CATEGORIES"));
		}
		else
		{
			if(mars.show_hosts)
				marslog(LOG_WARN,"User '%s' executes request as user '%s'",who,u);
			if(mars.emsfile)
				read_ems_cache(u,env);
			else
				add_groups(u,env);
		}
		if(mars.show_hosts)
			print_all_requests(env);
		who = u;
	}
	else
	{
		set_value(env,"user",who);
		if(mars.emsfile)
			read_ems_cache(who,env);
		else
			add_groups(who,env);
	}

	if(mars.debug_ems)
	{
		marslog(LOG_INFO,"MARS environment for '%s':",who);
		print_all_requests(env);
	}

	if(owner)
		set_value(env,"owner",owner);

	if(abc)
	{
		const char *name = who;
		if(mars.emsaccountsfile)
		{
			if(owner)
				name = owner;
			read_ems_accounts(name,env);
		}

		/* Check that the value provided for this user is valid */
		if(!user_in_ABC_account(name,abc,env))
		{
			if(mars.enable_ecaccount_email)
			{
				FILE *f = mail_open(mars.dhsmail,"MARS ABC account mismatch for user %s [%s] , account %s",name,who,abc);
				mail_request(f,"Environment :",env);
				mail_close(f);
			}
			/* For now, log users and continue even if their account doesn't match */
		}
		set_value(env,"abc",abc);
	}

	/* No EMS category. Stop here */
	if(get_value(env,"category",0) == NULL)
	{
		marslog(LOG_WARN,"Unable to find EMS category for user '%s'",who);
		if(mars.enforce_ems_category)
		{
			marslog(LOG_EROR,"MARS cannot continue without an EMS category");
			marslog(LOG_EROR,"Please, contact the MARS team. Exit");
			marsexit(1);
		}
		else
			marslog(LOG_WARN,"MARS has been configured to continue even without EMS category");
	}

	if(mars.marslite_mode && mars.webmars_request_id)
	{
		marslog(LOG_INFO,"Web API request id: %s",mars.webmars_request_id);
		set_value(env,"wrid",mars.webmars_request_id);
	}
#endif
	if(environment)
		set_value(env,"environment",environment);

	if(gethostname(buf,sizeof(buf)) == 0)
		set_value(env,"host",buf);

	if(marsClientHost)
		set_value(env,"host",marsClientHost);

	if(getdomainname(buf,sizeof(buf)) == 0)
		set_value(env,"domain",buf);

	set_value(env,"sourcebranch","%s",marssourcebranch());

#ifndef hpux
	id = (unsigned long)gethostid();
	sprintf(buf,"%lu.%lu.%lu.%lu",
		(id & 0xff000000) >> 24,
		(id & 0x00ff0000) >> 16,
		(id & 0x0000ff00) >>  8,
		(id & 0x000000ff));

	add_value(env,"host",buf);
#endif
		
	set_value(env,"pid","%d",getpid());

	return env;
}

void set_environ(request *e)
{
	free_all_requests(env);
	env = clone_all_requests(e);
}

void new_user(const char* u)
{
	request *e = get_environ();	
	set_value(e,"user","%s",u);
	unset_value(e,"group");
	add_groups(u,e);
#ifdef ECMWF
	if(mars.emsfile)
		read_ems_cache(u,e);
	if(mars.emsaccountsfile)
		read_ems_accounts(u,e);
	if(mars.force_setting_ecaccount)
	{
		const char *v = NULL;
		if(v = get_value(e,"account",0))
			set_value(e,"abc",v);
	}
#endif
	set_value(e,"pid","%d",getpid());
}
