/* @(#) $Id: user.c,v 1.3 1992/07/11 11:48:49 tron Exp $ */

/*
 *    Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
 *    Copyright (C) 1992  Ronald S. Karr
 * 
 * See the file COPYING, distributed with smail, for restriction
 * and warranty information.
 */

/*
 * user.c:
 *	direct mail to a transport which will deliver to local user
 *	mailboxes.  Match only local addresses which are login names
 *	on the local host.
 *
 * Specifications for the user directing driver:
 *
 *	private attribute data:
 *	    transport (name):  the name of the transport to use in delivering
 *		mail to local users.
 */
#include <stdio.h>
#include <pwd.h>
#include "defs.h"
#include "../smail.h"
#include "../smailconf.h"
#include "../parse.h"
#include "../addr.h"
#include "../log.h"
#include "../direct.h"
#include "../transport.h"
#include "../exitcodes.h"
#include "../dys.h"
#include "user.h"
#ifndef DEPEND
# include "../extern.h"
# include "../debug.h"
# include "../error.h"
#endif


/*
 * dtd_user - direct to local user mailboxes
 */
/*ARGSUSED*/
struct addr *
dtd_user(dp, in, out, new, defer, fail)
    struct director *dp;		/* director entry */
    struct addr *in;			/* input local-form addrs */
    struct addr **out;			/* output resolved addrs */
    struct addr **new;			/* output new addrs to resolve */
    struct addr **defer;		/* addrs to defer to a later time */
    struct addr **fail;			/* unresolvable addrs */
{
    register struct addr *cur;		/* temp for processing input */
    struct addr *pass = NULL;		/* addrs to pass to next director */
    struct addr *next;			/* next value for cur */
    struct user_private *priv = (struct user_private *)dp->private;

    DEBUG(DBG_DRIVER_HI, "dtd_user called\n");
    for (cur = in; cur; cur = next) {
	next = cur->succ;

	if (priv->prefix) {
	    struct addr *new;
	    int len = strlen(priv->prefix);

	    /*
	     * if we are testing against a prefix, strip the prefix and
	     * lookup the name in the passwd file.  If found, return a
	     * new fully resolved addr.
	     */
	    if (strncmpic(priv->prefix, cur->remainder, len) != 0) {
		cur->succ = pass;
		pass = cur;
		continue;		/* did not start with prefix */
	    }
	    new = alloc_addr();
	    new->remainder = COPY_STRING(cur->remainder + len);
	    director_user_info(new);
	    if (new->flags&ADDR_NOTUSER) {
		/* we did not match a user on the local host */
		xfree(new->remainder);
		xfree((char *)new);
		cur->succ = pass;
		pass = cur;
		continue;
	    }
	    new->in_addr = COPY_STRING(new->remainder);
	    new->director = dp;
	    new->parent = cur;
	    cur = new;
	} else {
	    /* fill in any user information */
	    director_user_info(cur);

	    /* don't match it if it is not a local user */
	    if (cur->flags&ADDR_NOTUSER) {
		cur->succ = pass;
		pass = cur;
		continue;
	    }
	}

	DEBUG2(DBG_DRIVER_LO, "director %s matched user %s\n",
	       dp->name, cur->remainder);
	cur->director = dp;		/* matched address */

	/* attach the transport */
	cur->transport = find_transport(priv->transport);
	if (cur->transport == NULL) {
	    /*
	     * ERR_122 - user transport not specified
	     *
	     * DESCRIPTION
	     *      No transport attribute was specified for a user
	     *      director.  The attribute is required.
	     *
	     * ACTIONS
	     *      Defer the message with a configuration error.
	     *
	     * RESOLUTION
	     *      The director file should be edited to specify the
	     *      correct transport for local delivery.
	     */
	    cur->error = note_error(ERR_CONFERR|ERR_122,
				  xprintf("director %s: transport not defined",
					  dp->name));
	    cur->succ = *defer;
	    *defer = cur;
	    continue;
	}
	cur->next_addr = COPY_STRING(cur->remainder);
	cur->succ = *out;
	*out = cur;
    }

    return pass;			/* return addrs for next director */
}

/*
 * dtv_user - verify a user on the local host
 */
/*ARGSUSED*/
void
dtv_user(dp, in, retry, okay, defer, fail)
    struct director *dp;		/* director entry */
    struct addr *in;			/* input local-form addrs */
    struct addr **retry;		/* output list of unmatched addrs */
    struct addr **okay;			/* output list of verified addrs */
    struct addr **defer;		/* temporariliy unverifiable addrs */
    struct addr **fail;			/* unverified addrs */
{
    register struct addr *cur;		/* temp for processing input */
    struct addr *next;			/* next value for cur */
    struct user_private *priv = (struct user_private *)dp->private;

    DEBUG(DBG_DRIVER_HI, "dtv_user called\n");

    /* loop over all of the input addrs */
    for (cur = in; cur; cur = next) {
	next = cur->succ;

	if (priv->prefix) {
	    int len = strlen(priv->prefix);

	    /*
	     * if we are testing against a prefix, strip the prefix and
	     * see if the remaining string matches a local user
	     */
	    if (strncmpic(priv->prefix, cur->remainder, len) == 0 &&
		getpwbyname(cur->remainder + len))
	    {
		/* matched */
		cur->succ = *okay;
		*okay = cur;
		continue;
	    }
	} else {
	    /* fill in any user information */
	    director_user_info(cur);

	    /* match only if this is a user */
	    if (cur->flags&ADDR_ISUSER) {
		cur->succ = *okay;
		*okay = cur;
		continue;
	    }
	}

	/* didn't match */
	cur->succ = *retry;
	*retry = cur;
    }
}

/*
 * dtb_user - read the configuration file attributes
 */
char *
dtb_user(dp, attrs)
    struct director *dp;		/* director entry being defined */
    struct attribute *attrs;		/* list of per-driver attributes */
{
    char *error;
    static struct attr_table user_attributes[] = {
	{ "transport", t_string, NULL, NULL, OFFSET(user_private, transport) },
	{ "prefix", t_string, NULL, NULL, OFFSET(user_private, prefix) },
    };
    static struct attr_table *end_user_attributes = ENDTABLE(user_attributes);
    static struct user_private user_template = {
	"local",
	NULL,
    };
    struct user_private *priv;		/* new user_private structure */

    /* copy the template private data */
    priv = (struct user_private *)xmalloc(sizeof(*priv));
    (void) memcpy((char *)priv, (char *)&user_template, sizeof(*priv));

    dp->private = (char *)priv;
    /* fill in the attributes of the private data */
    error = fill_attributes((char *)priv,
			    attrs,
			    &dp->flags,
			    user_attributes,
			    end_user_attributes);

    if (error) {
	return error;
    } else {
	if (priv->transport == NULL) {
	    return "transport attribute required";
	}
	if (find_transport(priv->transport) == NULL) {
	    return xprintf("unknown transport: %s", priv->transport);
	}
	return NULL;
    }
}
