/*
 * © 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 <time.h>
#include <sys/time.h>
#include <grp.h>

/* All possible categories in a restriction */
const char *names[] = {
	"user",
	"group",
	"host",
	"domain",
};

/* For validation date/time computation */
typedef struct marsdate {
	int date;
	int time;
} marsdate;

/* Restriction handler */
typedef int  (*restrictproc) (request*,request*,request*,void*);

typedef struct restriction_handler {
	const char  *name;
	restrictproc proc;
} restriction_handler;

static const char* alias2group(const char* alias)
{
	static request *aliases = 0;

	if(!aliases)
		aliases = empty_request("alias");

	if(count_values(aliases,alias) == 0)
	{
		struct group *grmain = 0;
		gid_t gid;
		char grname[1024];

		if( (grmain = getgrnam(alias)) != NULL)
		{
			gid = grmain->gr_gid;
			strcpy(grname,grmain->gr_name);

			setgrent();
			while ( (grmain = getgrent()) )
				if(gid == grmain->gr_gid)
					if(strlen(grname) > strlen(grmain->gr_name))
					{
						gid = grmain->gr_gid;
						strcpy(grname,grmain->gr_name);
					}

			endgrent();
			set_value(aliases,alias,grname);
			marslog(LOG_DBUG,"unalias: %s to %s",alias,grname);
		}
		else
		{
			marslog(LOG_WARN,"unknown group '%s'. Setting to 'unknown'",alias);
			set_value(aliases,alias,"unknown");
		}
	}

	return get_value(aliases,alias,0);
}

/* SIMPLE restriction */
static int handle_simple(request *req, request *restriction, request* reply, void *data)
{
    const char *par = get_value(restriction,"param",0);
    const char *val = get_value(restriction,"value",0);
	boolean    match = false;

    int i   = 0;
    const char *v = 0;

	marslog(LOG_DBUG,"Handle simple par=%s, val=%s",par,val);
	if(is_number(val))
	{
		while((v = get_value(req,par,i++)) != 0)
			if(atoi(v) == atoi(val))
				match = true;
	}
	else
		while((v = get_value(req,par,i++)) != 0)
			if(strcmp(val,upcase(v)) == 0)
				match = true;

    return match;
}
/* end of SIMPLE restriction */

/* NOT restriction */
static int handle_not(request *req, request *restriction, request* reply, void *data)
{
    const char *par = get_value(restriction,"param",0);
    const char *val = get_value(restriction,"value",0);
	boolean    match = false;

    int i   = 0;
    const char *v = 0;

	marslog(LOG_DBUG,"Handle not par=%s, val=%s",par,val);
	if(is_number(val))
	{
		while((v = get_value(req,par,i++)) != 0)
			if(atoi(v) != atoi(val))
				match = true;
	}
	else
		while((v = get_value(req,par,i++)) != 0)
			if(strcmp(val,upcase(v)) != 0)
				match = true;

	/* The parameter is not found in the request */
	if(v == 0 && i == 1)
		match = true;

    return match;
}
/* end of NOT restriction */

/* DATE restriction */
static void marsdates(request *r, marsdate* max, marsdate *min, marsdate* today, boolean include_step)
{
	int i;
	int v;
	int maxstep = 0;
	int minstep = INT_MAX;
	int steps   = count_values(r,"STEP");
	int fcmonth_days = 0;

	time_t now;
	struct tm *t;

	if(!steps || !include_step)
	{
		/* the case of observations */
		maxstep = 0;
		minstep = 0;
	}
	else
		for(i=0;i<count_values(r,"STEP");i++)
		{
			int n = atoi(get_value(r,"STEP",i));
			if(n>maxstep) maxstep = n;
			if(n<minstep) minstep = n;
		}

	for(i=0;i<count_values(r,"DATE");i++)
	{
		const char* p = get_value(r,"DATE",i);
		int n;
		if(is_number(p))
			n = atoi(p);
		else
		{
			long julian = 0,second = 0; boolean isjul;
			parsedate(p,&julian,&second,&isjul);
			n = mars_julian_to_date(julian,mars.y2k);
		}
		if(n>max->date) max->date = n;
		if(n<min->date) min->date = n;
	}

	/* Consider 30 day months */
	for(i=0;i<count_values(r,"FCMONTH");i++)
	{
		const char* p = get_value(r,"FCMONTH",i);
		int n = atoi(p) * 30;
		if(n>fcmonth_days) fcmonth_days = n;
	}

	for(i=0;i<count_values(r,"TIME");i++)
	{
		const char *time = get_value(r,"TIME",i);
		/* when a range of times is not expanded */
		if(is_number(time))
		{
			int n = atoi(get_value(r,"TIME",i));
			if(n>max->time) max->time = n;
			if(n<min->time) min->time = n;
		}
	}

	v = mars_date_to_julian(max->date) + (max->time/100 + maxstep)/24;
	if(mars.valid_data_includes_fcmonth)
		v += fcmonth_days;
	max->date=mars_julian_to_date(v,mars.y2k);
	max->time=(max->time/100 + maxstep)%24;

	v = mars_date_to_julian(min->date) + (min->time/100 + minstep)/24;
	min->date=mars_julian_to_date(v,mars.y2k);
	min->time=(min->time/100 + minstep)%24;

	time(&now);
	t = gmtime(&now);

	today->date = (t->tm_year+1900) * 10000 + (t->tm_mon+1)* 100 + t->tm_mday;
	today->time = t->tm_hour;
}

static int handle_date(request *req, request *restriction, request *reply, void *data)
{
	int      hours = atoi(get_value(restriction,"value",0));
	const char *param = get_value(restriction,"param",0);
	boolean reverse = (param != 0) && (strcmp(param,"ETAD")==0);
	marsdate max   = { 0,0,};
	marsdate min   = {INT_MAX, INT_MAX,};
	marsdate today = { 0,0,};
	const char *type = get_value(req,"TYPE",0);
	boolean include_step = true;

	marslog(LOG_DBUG,"Handle date par=%s, val=%d",param,hours);
	/* Climatology does not have a date, and no restrictions */
	if(type && EQ(type,"CL"))
		return false;

	marsdates(req,&max,&min,&today,include_step);

	if(mars.debug)
	{
		marslog(LOG_DBUG,"Maximum date %d, time %d",max.date,max.time);
		marslog(LOG_DBUG,"Minimum date %d, time %d",min.date,min.time);
		marslog(LOG_DBUG,"Now date %d, time %d",today.date,today.time);
	}

	if(reverse)
	{
		/* long requestdate   = (min.date + (min.time+hours)/24)*100 + (min.time + hours)%24; */
		int reqjulian      = mars_date_to_julian(min.date)+ (min.time + hours)/24;
		long requestdate   =  mars_julian_to_date(reqjulian,mars.y2k)*100 + (min.time + hours)%24;
		long todaydate     = today.date*100 +today.time;
		if((requestdate) <= (todaydate))
		{
			marslog(LOG_DBUG,"requestdate (%ld) <= todaydate (%ld)",requestdate,todaydate);
			return true;
		}
	}
	else
	{
		int reqjulian      = mars_date_to_julian(max.date)+ (max.time + hours)/24;
		long requestdate   =  mars_julian_to_date(reqjulian,mars.y2k)*100 + (max.time + hours)%24;

		/* long requestdate   = (max.date + (max.time + hours)/24)*100 + (max.time + hours)%24; */
		long todaydate     = today.date*100 +today.time;

		if(requestdate >= todaydate)
		{
			marslog(LOG_DBUG,"requestdate (%ld) >= todaydate (%ld)",requestdate,todaydate);
			return true;
		}
	}

	return false;
}
/* end of DATE restriction */

static int handle_basetime(request *req, request *restriction, request *reply, void *data)
{
	int      hours = atoi(get_value(restriction,"value",0));
	marsdate max   = { 0,0,};
	marsdate min   = {INT_MAX, INT_MAX,};
	marsdate today = { 0,0,};
	const char *type = get_value(req,"TYPE",0);
	boolean  include_step = false;

	marslog(LOG_DBUG,"Handle basetime val=%d",hours);
	/* Climatology does not have a date, and no restrictions */
	if(type && EQ(type,"CL"))
		return false;

	marsdates(req,&max,&min,&today,include_step);

	if(mars.debug)
	{
		marslog(LOG_DBUG,"Maximum date %d, time %d",max.date,max.time);
		marslog(LOG_DBUG,"Minimum date %d, time %d",min.date,min.time);
		marslog(LOG_DBUG,"Now date %d, time %d",today.date,today.time);
	}

	{
		int reqjulian      = mars_date_to_julian(max.date)+ (max.time + hours)/24;
		long requestdate   =  mars_julian_to_date(reqjulian,mars.y2k)*100 + (max.time + hours)%24;

		/* long requestdate   = (max.date + (max.time + hours)/24)*100 + (max.time + hours)%24; */
		long todaydate     = today.date*100 +today.time;

		if(requestdate >= todaydate)
		{
			marslog(LOG_DBUG,"requestdate (%ld) >= todaydate (%ld)",requestdate,todaydate);
			return true;
		}
	}

	return false;
}
/* end of DATE restriction */


typedef struct seasonaldate_ {
	long  date;
	long  yyyymm;
	long  dd;
	float dayofmonth;
	long  time;
} seasonaldate ;

/* DAYOFMONTH restriction */
static int handle_dayofmonth(request *r, request *restriction, request *reply, void *d)
{
	float  dayofmonth = atof(get_value(restriction,"value",0));
	time_t now;
	struct tm *t;
	seasonaldate today;
	seasonaldate data;
	int i = 0;

	marslog(LOG_DBUG,"Handle dayofmonth %f",dayofmonth);

	/* Get GMT date/time, not localtime (to avoid users manipulate TZ env. variable) */
	time(&now);
	t = gmtime(&now);

	today.yyyymm = (t->tm_year+1900) * 100 + (t->tm_mon+1);
	today.dd     = t->tm_mday;
	today.time   = t->tm_hour;
	today.dayofmonth = (float)t->tm_mday + (float)today.time/24.0;

	/* Get data's date */
	data.date = -1;
	for(i=0;i<count_values(r,"DATE");i++)
	{
		const char* p = get_value(r,"DATE",i);
		int n;
		if(is_number(p))
			n = atoi(p);
		else
		{
			long julian = 0,second = 0; boolean isjul;
			parsedate(p,&julian,&second,&isjul);
			n = mars_julian_to_date(julian,mars.y2k);
		}
		if(n>data.date) data.date = n;
	}

	/* Get data's time */
	data.time=-1;
	for(i=0;i<count_values(r,"TIME");i++)
	{
		const char *time = get_value(r,"TIME",i);
		if(is_number(time))
		{
			int n = atoi(get_value(r,"TIME",i));
			if(n>data.time) data.time = n;
		}
	}

	data.yyyymm = (int)(data.date/100);
	data.dd     = (int)(data.date%100);


	marslog(LOG_DBUG,"Today's month %ld",today.yyyymm);
	marslog(LOG_DBUG,"Data's month %ld",data.yyyymm);
	marslog(LOG_DBUG,"Today's dayofmonth %lf",today.dayofmonth);
	marslog(LOG_DBUG,"Restriction's dayofmonth %lf",dayofmonth);
	/* The logic:
	   Note that data in the future, ie (today.yyyymm < data.yyyymm), is always restricted */
	if( (today.yyyymm > data.yyyymm) ||
		((today.yyyymm == data.yyyymm) && (today.dayofmonth >= dayofmonth)))
	{
		marslog(LOG_DBUG,"Data is not restricted");
		return false;
	}

	marslog(LOG_DBUG,"Data is RESTRICTED");
	return true;
}
/* end of DAYOFMONTH restriction */


/* AND restriction */
static int handle_and(request *req, request *restriction, request *reply, void *data)
{
	request *left  = get_subrequest(restriction,"left",0);
	request *right = get_subrequest(restriction,"right",0);

	boolean ll = handle_restriction(req,left,reply,data);
	boolean rr = handle_restriction(req,right,reply,data);

	free_all_requests(left);
	free_all_requests(right);

	if(ll && rr)
		return true;

	return false;
}
/* end of AND restriction */

/* FILTER restriction */
static int handle_filter(request *req, request *restriction, request *reply, void *data)
{
    const char *par = get_value(restriction,"param",0);
    const char *val = get_value(restriction,"value",0);
	boolean found = false;

    int i   = 0;
    const char *v = 0;

	marslog(LOG_DBUG,"Handle filter par=%s, val=%s",par,val);
	if(is_number(val))
	{
		while((v = get_value(req,par,i++)) != 0 && !found)
			if(atoi(v) == atoi(val))
				found = true;
	}
	else
		while((v = get_value(req,par,i++)) != 0 && !found)
			if(strcmp(val,upcase(v)) == 0)
				found = true;

	if(found)
	{
		set_value(reply,"filter",par);
		add_value(reply,"filter",val);
	}
    return false;
}
/* end of FILTER restriction */


/* FILTEROUT restriction */
static int handle_filterout(request *req, request *restriction, request *reply, void *data)
{
    const char *par = get_value(restriction,"param",0);
    const char *val = 0;
	int i = 0;
	request *filter = 0;

	marslog(LOG_DBUG,"Handle filterout par=%s",par);
	/* If the request does not contain the parameter, stop here */
	if(count_values(req,par) == 0)
		return false;

	filter = empty_request(par);

	/* For each value in the request, check whether it is restricted */
	while((val = get_value(req,par,i++)) != NULL)
	{
		int j = 0;
		const char *vrestr = 0;
		boolean found = false;

		if(is_number(val))
		{
			int v = atoi(val);
			while(!found && (vrestr = get_value(restriction,"value",j++)) != NULL)
				found = (v == atoi(vrestr));
		}
		else
			while(!found && (vrestr = get_value(restriction,"value",j++)) != NULL)
				found = (strcmp(upcase(val),vrestr)==0);


		if(found)
			add_value(filter,"remove","%s",val);
		else
			add_value(filter,"keep","%s",val);

	}

	if(mars.debug)
	{
		marslog(LOG_DBUG,"Filter out the following request:");
		print_all_requests(filter);
	}

	/* Set the values not found in the restriction */
	if(count_values(filter,"keep") != 0)
	{
		valcpy(req,filter,(char *)par,"keep");
	}
	else
	{
		marslog(LOG_WARN,"Values provided for %s are restricted",(char *)par);
		/* marslog(LOG_EXIT,"Please, contact Service Desk for any queries"); */
		set_value(filter,"accept","no");
		return true;
	}

	free_all_requests(filter);

    return false;
}
/* end of FILTEROUT restriction */

static restriction_handler handlers[] = {
	{ "simple",    handle_simple},
	{ "not",       handle_not},
	{ "date",      handle_date},
	{ "and",       handle_and,},
	{ "filter",    handle_filter,},
	{ "filterout", handle_filterout,},
	{ "dayofmonth",handle_dayofmonth,},
	{ "hoursfrombasetime",  handle_basetime,},
};

boolean handle_restriction(request *req, request *r, request *reply, void *data)
{
	int i = 0;
	request *restriction = r;

	while(restriction)
	{
		restrictproc handler = 0;
		for(i=0;i<NUMBER(handlers) && !handler;i++)
			if(strcmp(handlers[i].name,restriction->name)==0)
				handler = handlers[i].proc;

		if(handler)
		{
			if(handler(req,restriction,reply,data))
			{
				const char *info = no_quotes(get_value(restriction,"info",0));
				const char *url = get_value(restriction,"url",0);
				set_value(reply,"accept","no");
				if(info)
					set_value(reply,"info","restricted access to %s data.",info);
				if(url)
				{
					set_value(reply,"url","For more information, look at ");
					add_value(reply,"url","%s.",url);
				}
				return true;
			}
		}
		else
		{
			FILE *f  = mail_open("mar","Mars account internal error");
			mail_msg(f,"Internal error. No handler for restriction '%s'",restriction->name);
			mail_request(f,"Restriction rule:",restriction);
			mail_close(f);
		}
		restriction = restriction->next;
	}

	set_value(reply,"accept","yes");
	set_value(reply,"reqid","-1");
	return false;
}

static request* mars_environment(request *environ, request *r)
{
	request *defaults  = 0;
	int     nameidx   = 0;
	request *nenviron = 0;

	defaults = r;
	while(defaults)
	{
		const char *category = names[nameidx];

		if(strcmp(defaults->name,"default") == 0)
		{
			const char *restricted = 0;
			int i = 0;

			/* Scan all categories */
			while((restricted = get_value(defaults,category,i++)) != 0)
			{
				const char *user = 0;
				int j = 0;
				if(strcmp(restricted,"*") == 0)
				{
					nenviron = clone_one_request(environ);
					return nenviron;
				}

				while((user = get_value(environ,category,j++)) != 0)
				{
					if(category == names[1])
						user = alias2group(user);

					if(strcmp(restricted,user) == 0)
					{
						const char *marsrest = 0;
						int k = 0;

						/* Create MARS environment with MARS restrictions */
						/* except those to which the user belong          */
						nenviron = empty_request("environ");
						while((marsrest = get_value(defaults,"restriction",k++)) != 0)
						{
							const char *marsgroup = 0;
							int l = 0;
							boolean belong = false;

							/* Check if user belongs to the restriction group */
							while((marsgroup = get_value(environ,"group",l++)) != 0 && !belong)
								if(strcmp(alias2group(marsgroup),marsrest) == 0)
									belong = true;

							/* If not in this group, the restriction applies */
							if(!belong)
								add_value(nenviron,"group",marsrest);
						}
						return nenviron;
					}
				}
			}
		}

		defaults = defaults->next;
		if(!defaults && (++nameidx < NUMBER(names)))
			defaults = r;
	}

	return nenviron;
}

static request* mars_ems_categories(request *environ, request *r)
{
	request *rule  = 0;
	request *nenviron = empty_request("environ");


	rule = r;
	while(rule)
	{
		if(strcmp(rule->name,"rule") == 0)
		{
			const char *rulcat = 0;
			int i = 0;
			int match = 0;


			/* Scan all categories */
			while((rulcat = get_value(rule,"category",i++)) != 0 && !match)
			{
				const char *usercat = 0;
				int j = 0;

				while((usercat = get_value(environ,"category",j++)) != 0 && !match)
				{
					if(strcmp(rulcat,usercat) == 0)
					{
						if(mars.debug)
						{
							marslog(LOG_DBUG,"Got match on '%s'",rulcat);
							print_one_request(environ);
							print_one_request(rule);
						}
						match++;
					}
				}
			}

			if(match)
			{
				const char *p = 0;
				int k = 0;
				while((p = get_value(rule,"restrict",k++)) != 0)
					add_unique_value(nenviron,"group",p);
			}
		}
		rule = rule->next;
	}

	rule = r;
	while(rule)
	{
		if(strcmp(rule->name,"rule") == 0)
		{
			const char *rulcat = 0;
			int i = 0;
			int match = 0;


			/* Scan all categories */
			while((rulcat = get_value(rule,"category",i++)) != 0 && !match)
			{
				const char *usercat = 0;
				int j = 0;

				while((usercat = get_value(environ,"category",j++)) != 0 && !match)
				{
					if(strcmp(rulcat,usercat) == 0)
					{
						if(mars.debug)
						{
							marslog(LOG_DBUG,"Got match on '%s'",rulcat);
							print_one_request(environ);
							print_one_request(rule);
						}
						match++;
					}
				}
			}

			if(match)
			{
				const char *p = 0;
				int k = 0;
				while((p = get_value(rule,"grant",k++)) != 0)
				{
					if(strcmp(p,"all") == 0)
					{
						unset_value(nenviron,"group");
						return nenviron;
					}
					unset_param_value(nenviron,"group",p);
				}

			}
		}
		rule = rule->next;
	}

	return nenviron;
}

static void validate_user(request *req, request *env, request *reply)
{
#if !defined(NOAUTH)

	static request *r = 0;
	request *restriction = 0;
	request *access      = 0;
	int     nameidx = 0;
	int     i       = 0;
	int     j       = 0;
	request *environ = 0;

	if(!r) r = read_request_file(mars.authfile);

	if(get_value(env,"category",0) != NULL)
	{
		environ = mars_ems_categories(env,r);
	}
	else
	{
		/* Get 'default' mars restrictions */
		environ = mars_environment(env,r);
	}

	if(mars.debug)
	{
		marslog(LOG_DBUG,"Default mars environment");
		if(environ)
			print_all_requests(environ);
		else
			marslog(LOG_DBUG,"(empty)");
	}

	if(!environ)
	{
		set_value(reply,"accept","no");
		set_value(reply,"info","User is not registered");

		return;
	}

	/* Scan restriction requests, following 'names' order */
	access = r;
	while(access)
	{
		const char *category = names[nameidx];
		const char *restricted = 0;

		if(strcmp(access->name,"access") == 0)
		{

			i = 0;
			/* Scan a given category */
			while((restricted = get_value(access,category,i++)) != 0)
			{
				const char *user = 0;
				j = 0;
				while((user = get_value(environ,category,j++)) != 0)
				{
					if(strcmp(restricted,user) == 0)
					{
						request *more = get_subrequest(access,"restriction",0);
						if(!restriction)
							restriction = more;
						else
						{
							request *s = restriction;
							while(s->next) s = s->next;
							s->next = more;
						}
					}
				}
			}
		}

		access = access->next;
		if(!access && (++nameidx < NUMBER(names)))
			access = r;
	}

	handle_restriction(req,restriction,reply,NULL);

	if(restriction)
		free_all_requests(restriction);

	if(environ)
		free_all_requests(environ);
#endif
}

static request *change_environment(request *real)
{
	const char *useTest    = getenv("MARS_ENV");
	const char *who        = get_value(real,"user",0);

	if(!who || !EQ(who,"max"))
		return 0;

	if(useTest)
	{
		request *s = read_request_file(useTest);
		marslog(LOG_WARN,"User %s has changed the environment",who);
		print_all_requests(s);
		return s;
	}
	return 0;
}

err local_validate_request(request *req, request *env)
{
	request *reply   = empty_request("reply");
	err     ret      = 0;
	const char *p    = 0;

	validate_user(req,env,reply);

	if(mars.debug)
	{
		marslog(LOG_DBUG,"Environment:");
		print_all_requests(env);
		marslog(LOG_DBUG,"Authentication:");
		print_all_requests(reply);
	}

	p = get_value(reply,"accept",0);
	if(p && (*p == 'n'))
	{
		int i = 0;

		marslog(LOG_EROR,"Request validation failed:");
		while( (p = get_value(reply,"info",i++)) )
			marslog(LOG_EROR,"%s",p);

		i=0;
		while( (p = get_value(reply,"url",i++)) )
			marslog(LOG_EROR,"%s",p);

		marslog(LOG_EROR,"For any queries, please, contact Service Desk");
		ret = -2;
	}

	p = get_value(reply,"filter",0);
	if(p) mars.restriction = true;

	p = get_value(reply,"reqid",0);
	if(p) mars.request_id = atol(p);
	set_value(req,"_REQID","%ld",mars.request_id);

	if(ret)
	{
		FILE *f = mail_open(mars.authmail,"Mars account failure");
		const char *inf = 0;
		int k = 0;
		while((inf = get_value(reply,"info",k++)) != 0)
			mail_msg(f,"%s",inf);
		mail_request(f,"MARS Environment:",env);
		mail_request(f,"MARS Request:",req);
		mail_close(f);
	}

	free_all_requests(reply);

	return ret;
}

void print_user_restrictions(request *env, request *auth)
{
	request *environ     = 0;
	request *access      = 0;
	request *restriction = 0;
	int      nameidx     = 0;

	if(!env)
	{
		marslog(LOG_EROR,"Empty environment");
		return;
	}

	if(!auth)
	{
		marslog(LOG_EROR,"Empty authentication file");
		return;
	}

	environ = mars_environment(env,auth);
	if(!environ)
	{
		const char *user = get_value(env,"user",0);
		if(user)
			printf("User '%s' is not registered\n",user);
		return;
	}

	access = auth;
	while(access)
	{
		const char *category = names[nameidx];
		const char *restricted = 0;

		if(strcmp(access->name,"access") == 0)
		{
			int i = 0;
			/* Scan a given category */
			while((restricted = get_value(access,category,i++)) != 0)
			{
				const char *user = 0;
				int j = 0;
				while((user = get_value(environ,category,j++)) != 0)
				{
					if(strcmp(restricted,user) == 0)
					{
						static boolean z00 = false;
						if(strcmp(restricted,"mars0z")==0)
							z00 = true;
						if(! (strcmp(restricted,"mars0a") == 0 && z00))
						{
							request *more = get_subrequest(access,"restriction",0);
							if(!restriction)
								restriction = more;
							else
							{
								request *s = restriction;
								while(s->next) s = s->next;
								s->next = more;
							}
						}
					}
				}
			}
		}

		access = access->next;
		if(!access && (++nameidx < NUMBER(names)))
			access = auth;
	}

	if(restriction)
	{
		request *r = restriction;
		printf("User '%s' has restricted/filtered access to the following MARS data :\n",get_value(env,"user",0));
		while(r)
		{
			const char *msg = get_value(r,"info",0);
			if(msg)
				printf("  - %s\n",msg);
			r = r->next;
		}
	}
	else
	{
		printf("User '%s' has unrestricted access to MARS data\n",get_value(env,"user",0));
	}
}

