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

/* OBS restriction */
static int handle_restrictedobs(request* req, request* restriction, request* reply, void* data) {
    marslog(LOG_DBUG, "handle_restrictedobs() user has privileged access to observations");
    mars.privileged = 1;
    return false;
}
/* end of OBS 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,
    },
    {
        "restrictedobs",
        handle_restrictedobs,
    },
};

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(mars.authmail, "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));
    }
}
