/*
 *   Copyright (c) 1998 David Parsons. All rights reserved.
 *
 *   Redistribution and use in source and binary forms, with or without
 *   modification, are permitted provided that the following conditions
 *   are met:
 *  1. Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *  2. Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in
 *     the documentation and/or other materials provided with the
 *     distribution.
 *  3. All advertising materials mentioning features or use of this
 *     software must display the following acknowledgement:
 *
 *   This product includes software developed by David Parsons
 *   (orc@pell.chi.il.us)
 *
 *  4. My name may not be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 *  THIS SOFTWARE IS PROVIDED BY DAVID PARSONS ``AS IS'' AND ANY
 *  EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 *  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 *  PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DAVID
 *  PARSONS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 *  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
 *  TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *  DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 *  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 *  LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
 *  IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 *  THE POSSIBILITY OF SUCH DAMAGE.
 */

static const char rcsid[] = "$Id: s.aliases.cc 1.2 00/05/21 16:33:42-00:00 orc $";

/*
 * aliases:  sendmail alias administration
 *
 * main screen:
 *                               
 *                  +------(-)+
 *                  |alias    |
 *                  |alias    |
 *                  +------(+)+
 *         add] [modify] [delete] [done]
 *
 * add/modify screen
 *                  +-------+
 *                  | alias |
 *                  +-------+
 *
 *                  +-------------+
 *                  |user to add  |  [add]
 *                  +-------------+
 *                  |user list    |  [delete]
 *                  |             |
 *                  +-------------+
 */

extern "C" {

#include <ndialog.h>
#include <dialog.h>

#include <unistd.h>
#include <ctype.h>
#include <stdlib.h>

#include <pwd.h>
#include <grp.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <limits.h>
}

#include <basis/Aliases.H>

#include <string.h>

static Aliases aliases;		/* current mail aliases */

static LIA aliasmenu;		/* the display menu of mail aliases */
static LIA users;		/* if we're modifying or creating a new
				 * mail alias, the users defined in
				 * this alias
				 */

/* display objects we might want to modify on the fly, or have
 * other reasons we might be interested in them
 */
static ndObject o_list = 0, o_name, o_content, o_add;

/* scratch area that holds a new alias name
 */
#define USERSIZE	60
#define ALIASSIZE	20
static char newalias[ALIASSIZE];

/* callbacks from the list-of-aliases menu.
 */
static int addormodifyaliases(ndObject obj, ndDisplay display);
static int deletealias(ndObject obj, ndDisplay display);
static int chk_alias(ndObject obj, ndDisplay display);

/*
 * addToAliasList() is the foreach callback that actually populates
 * the aliasemenu LIA
 */
static int
addToAliasList(char *name, KA* content)
{
    unsigned int j;

    addToLIA(aliasmenu, name, name, 0);
    return 0;
} /* addToAliasList */


/*
 * getAliasList() builds a aliases list.
 */
static void
getAliasList()
{
    deleteLIA(aliasmenu);
    aliasmenu = newLIA(0,0);

    aliases.foreach(addToAliasList);
} /* getAliasList() */


/*
 * makeAliasMenu() makes up the menu that presents a
 * list of aliasmenu to manipulate.
 */
static ndObject
makeAliasMenu()
{
    ndObject chain;

    getAliasList();

    o_list = newRadioList(6,0,ALIASSIZE,12,LIAcount(aliasmenu), LIAlist(aliasmenu),
		    0, "", MENU_SELECTION|ALWAYS_HIGHLIT,
		    addormodifyaliases, "aliases.html#modify");

    o_add = newButton(0, " ADD  ", addormodifyaliases, "aliases.html#add");

    chain = ObjChain(o_list, o_add);
    chain = ObjChain(chain,  newButton(1, "MODIFY", addormodifyaliases, "aliases.html#modify"));
    chain = ObjChain(chain,  newButton(2, "DELETE", deletealias, "aliases.html#delete"));
    return ObjChain(chain, newOKButton(3, " DONE ", 0, 0));
} /* makealiasesmanu */


/*
 * add_user() is the callback for adding a user to an alias.  If that
 * username is invalid or already exists in the alias, we can't add it.
 */
int
add_user(ndObject object, ndDisplay display)
{
    char *user = (char*)objDataPtr(object);
    char *p;
    int x;

    for (p = user; *p; ++p)
	if (*p == '"') {
	    Error("Using a \" in a mail alias is not advised");
	    goto fail;
	}

    for (x=0; x < LIAcount(users); x++)
	if (strcasecmp(user, LIAlist(users)[x].item) == 0) {
	    Error("%s is already in this mail alias", user);
    fail:
	    memset(user, 0, USERSIZE);
	    setObjCursor(object, 0);
	    drawObj(object,display);
	    return 0;
	}
    return 1;
} /* add_user */


/*
 * finalalias() does the final check to see if an alias is okay.  Basically,
 * all we do is make sure the alias name is okay, then copy the users into it,
 * commit, and return.
 */
static int
finalalias(ndObject object, ndDisplay display)
{
    int rc;
    int ix;
    char *p;

    if (writable(o_name) && (rc = chk_alias(o_name, display)) == 0)
	return rc;

    /* zero out the alias */
    aliases[(char*)objDataPtr(o_name)] = 0;

    /* and populate it, also building up a buffer to correct the
     * display of it in the main menu
     */
    for (ix=0; ix < LIAcount(users); ix++)
	aliases[(char*)objDataPtr(o_name)] += LIAlist(users)[ix].item;

    aliases.commit();
    return 1;
} /* finalalias */


/*
 * MakeAddOrModifyAliasMenu() gives us a form that we can use either to create a new aliases or
 * modify the attributes for an existing aliases.
 *
 *           Alias     [           ]
 *                     +-----------+
 *                     |           |  [add]
 *                     +-----------+
 *                     |           |  [delete]
 *                     |           |
 *                     +-----------+
 */
static ndObject
MakeAddOrModifyAliasMenu(char *alias)
{
    ndObject chain ;
    unsigned int x;

    deleteLIA(users);
    users = newLIA(0,0);

    if (alias) {
	o_name  = newString(0,0,ALIASSIZE,ALIASSIZE, alias, 0, "Alias", chk_alias, "alias.html#name");
	setReadonly(o_name);
	for (x=0; x < aliases[alias].count(); x++)
	    addToLIA(users, aliases[alias][x], aliases[alias][x], 0);
    }
    else
	o_name = newString(0,0,ALIASSIZE,ALIASSIZE, newalias, 0, "Alias", chk_alias, "alias.html#name");

    o_content = newListWidget(5,3,USERSIZE,6,users, 0, add_user, 0, 0, "alias.html#users");

    chain = ObjChain(o_name, o_content);

    chain = ObjChain(chain, newOKButton(0, "APPLY", finalalias, "aliases.html"));
    chain = ObjChain(chain, newCancelButton(2, "CANCEL", 0, "aliases.html"));

    return chain;
} /* MakeAddOrModifyAliasMenu */


/*
 * chk_alias() makes sure the alias is legitimate (no spaces or '!', '%', '@')
 * and that it's not already in the aliases list.
 */
static int
chk_alias(ndObject obj, ndDisplay display)
{
    char *alias = (char*)objDataPtr(o_name);
    int len;
    static char forbidden[] = "!%@()<>[]^\\/.,~`'\"";

    for (len=0; strchr(forbidden, *alias) == 0 && (*alias > 32); ++alias)
	++len;

    if (*alias) {
	Error("Bad character in alias name");
	return 0;
    }
    if (len == 0) {
	Error("No alias name given");
	return 0;
    }
    if (aliases.locate((char*)objDataPtr(o_name)) >= 0) {
	Error("This alias already exists");
	return 0;
    }
    return 1;
} /* chk_alias */



/*
 * addormodifyaliases() adds a new mail alias or modifies an existing
 * one.
 */
static int
addormodifyaliases(ndObject obj, ndDisplay display)
{
    ndObject chain;
    int rc;

    if (obj == o_add) {
	chain = MakeAddOrModifyAliasMenu(0);
	rc = MENU(chain, -1, -1, "Create a new alias", 0, 0);
    }
    else {
	int sel = currentSelection(o_list);
	chain = MakeAddOrModifyAliasMenu(getObjList(o_list)[sel].id);
	rc = MENU(chain, -1, -1, getObjList(o_list)[sel].id, 0, 0);
    }

    deleteObjChain(chain);

    return 0;
} /* addormodifyaliases */


/*
 * deletealias() deletes an alias, if it's permitted.  The required aliases
 * are MAILER-DAEMON and postmaster, and if you try and delete them much
 * squacking and complaining will ensue.
 */
int
deletealias(ndObject obj, ndDisplay display)
{
    int sel = currentSelection(o_list);
    char *id = getObjList(o_list)[sel].id;
    char bfr[80];

    if (strcasecmp(id, "MAILER-DAEMON") == 0 || strcasecmp(id, "postmaster") == 0) {
	Error("Mail standard RFC822 requires that you have the\n"
	      "%s alias.", id);
	return 0;
    }
    sprintf(bfr, "Really delete %s?", id);
    if (dialog_yesno(0, (unsigned char*)bfr, -1, -1) == 0) {
	aliases.drop(id);
	aliases.commit();
	delFromLIA(aliasmenu, sel);
	setObjData(o_list, 0, LIAlist(aliasmenu), LIAcount(aliasmenu));
	setObjCursor(o_list, (sel > 0) ? (sel-1) : 0);
	touchObj(o_list);
	drawObj(o_list, display);
    }
    return 0;
} /* deletealias */


/*
 * exitmenu() is a dummy callback that simply tells MENU() to exit
 */
static int
exitmenu(ndObject obj, ndDisplay display)
{
    return -1;	/* Flee! All is discovered! */
} /* exitmenu */


/*
 * aliases, in mortal flesh
 */
extern "C" int
doit(int argc, char **argv)
{
    int rc;
    ndObject chain;


    aliasmenu = newLIA(0,0);
    users     = newLIA(0,0);

    if (argc <= 1) {
	chain = makeAliasMenu();
	rc = MENU(chain, -1, -1, "Mail alias administration", 0, 0);
	deleteObjChain(chain);
    }
    else if (aliases.locate(argv[1]) >= 0) {
	chain = MakeAddOrModifyAliasMenu(argv[1]);
	rc = MENU(chain, -1, -1, argv[1], 0, 0);
	deleteObjChain(chain);
    }

    return (rc == MENU_OK) ? 0 : 1;
} /* doit */
