/*
 * mts.c -- definitions for the mail transport system
 *
 * $Id: mts.c,v 1.3 2003/09/30 16:58:43 gbburkhardt Exp $
 *
 * This code is Copyright (c) 2002, by the authors of nmh.  See the
 * COPYRIGHT file in the root directory of the nmh distribution for
 * complete copyright information.
 */

#include <h/mh.h>   /* for snprintf() */
#include <h/nmh.h>

#define nmhetcdir(file) NMHETCDIR#file

#include <ctype.h>
#include <stdio.h>
#include <h/mts.h>
#include <pwd.h>
#include <netdb.h>

#ifdef HAVE_SYS_UTSNAME_H
# include <sys/utsname.h>
#endif

#define	NOTOK   (-1)
#define	OK        0

/*
 * static prototypes
 */
static char *tailor_value (char *);
static void getuserinfo (void);

/*
 * *mmdfldir and *uucpldir are the maildrop directories.  If maildrops
 * are kept in the user's home directory, then these should be empty
 * strings.  In this case, the appropriate ...lfil array should contain
 * the name of the file in the user's home directory.  Usually, this is
 * something like ".mail".
 */

/*
 * nmh mail transport interface customization file
 */
static char *mtsconf = nmhetcdir(/mts.conf);

static char *localname   = "";
static char *localdomain = "";
static char *systemname  = "";

char *mmdfldir = MAILSPOOL;
char *mmdflfil = "";
char *uucpldir = "/usr/spool/mail";
char *uucplfil = "";

char *mmdlm1 = "\001\001\001\001\n";
char *mmdlm2 = "\001\001\001\001\n";

/* Cache the username and fullname of the user */
static char username[BUFSIZ];
static char fullname[BUFSIZ];

/* Variables for username masquerading: */
       boolean  draft_from_masquerading = FALSE;  /* also used from post.c */
static boolean  mmailid_masquerading = FALSE;
       boolean  username_extension_masquerading = FALSE;  /* " from addrsbr.c */
static char*    masquerade = "";

/*
 * MTS specific variables
 */
#if defined(SMTPMTS)
static char *sm_method = "smtp";
int  sm_mts    = MTS_SMTP;
char *hostable = nmhetcdir(/hosts);
char *sendmail = SENDMAILPATH;
#endif

/*
 * SMTP/POP stuff
 */
char *clientname = NULL;
char *servers    = "localhost \01localnet";
char *pophost    = "";

/*
 * BBoards-specific variables
 */
char *bb_domain = "";


/*
 * POP BBoards-specific variables
 */
#ifdef	BPOP
char *popbbhost = "";
char *popbbuser = "";
char *popbblist = nmhetcdir(/hosts.popbb);
#endif /* BPOP */

/*
 * Global MailDelivery file
 */
char *maildelivery = nmhetcdir(/maildelivery);


/*
 * Aliasing Facility (doesn't belong here)
 */
int Everyone = NOTOK;
static char *everyone = "-1";
char *NoShell = "";

/*
 * Customize the MTS settings for nmh by adjusting
 * the file mts.conf in the nmh etc directory.
 */

struct bind {
    char *keyword;
    char **value;
};

static struct bind binds[] = {
    { "localname", &localname },
    { "localdomain", &localdomain },
    { "systemname", &systemname },
    { "mmdfldir", &mmdfldir },
    { "mmdflfil", &mmdflfil },
    { "uucpldir", &uucpldir },
    { "uucplfil", &uucplfil },
    { "mmdelim1", &mmdlm1 },
    { "mmdelim2", &mmdlm2 },
    { "masquerade", &masquerade },

#if defined(SMTPMTS)
    { "mts",      &sm_method },
    { "hostable", &hostable  },
    { "sendmail", &sendmail  },
#endif

    { "clientname",  &clientname },
    { "servers", &servers },
    { "pophost", &pophost },
    { "bbdomain", &bb_domain },

#ifdef BPOP
    { "popbbhost", &popbbhost },
    { "popbbuser", &popbbuser },
    { "popbblist", &popbblist },
#endif

#ifdef NNTP
    { "nntphost", &popbbhost },
#endif

    { "maildelivery", &maildelivery },
    { "everyone", &everyone },
    { "noshell", &NoShell },
    { NULL, NULL }
};


/*
 * Read the configuration file for the nmh interface
 * to the mail transport system (MTS).
 */

void
mts_init (char *name)
{
    char *bp, *cp, buffer[BUFSIZ];
    struct bind *b;
    FILE *fp;
    static int inited = 0;

    if (inited++ || (fp = fopen (mtsconf, "r")) == NULL)
	return;

    while (fgets (buffer, sizeof(buffer), fp)) {
	if (!(cp = strchr(buffer, '\n')))
	    break;
	*cp = 0;
	if (*buffer == '#' || *buffer == '\0')
	    continue;
	if (!(bp = strchr(buffer, ':')))
	    break;
	*bp++ = 0;
	while (isspace (*bp))
	    *bp++ = 0;

	for (b = binds; b->keyword; b++)
	    if (!strcmp (buffer, b->keyword))
		break;
	if (b->keyword && (cp = tailor_value (bp)))
	    *b->value = cp;
    }

    fclose (fp);

    Everyone = atoi (everyone);

    if (strstr(masquerade, "draft_from") != NULL)
	draft_from_masquerading = TRUE;

    if (strstr(masquerade, "mmailid") != NULL)
	mmailid_masquerading = TRUE;

    if (strstr(masquerade, "username_extension") != NULL)
	username_extension_masquerading = TRUE;

#ifdef SMTPMTS
    if (strcmp(sm_method, "smtp") == 0)
        sm_mts = MTS_SMTP;
    else if (strcmp(sm_method, "sendmail") == 0)
        sm_mts = MTS_SENDMAIL;
    else {
        advise(NULL, "unsupported \"mts\" value in mts.conf: %s", sm_method);
        sm_mts = MTS_SMTP;
    }
#endif
}


#define	QUOTE	'\\'

/*
 * Convert escaped values, malloc some new space,
 * and copy string to malloc'ed memory.
 */

static char *
tailor_value (char *s)
{
    int i, r;
    char *bp;
    char buffer[BUFSIZ];
    size_t len;

    for (bp = buffer; *s; bp++, s++) {
	if (*s != QUOTE) {
	    *bp = *s;
	} else {
	    switch (*++s) {
		case 'b': *bp = '\b'; break;
		case 'f': *bp = '\f'; break;
		case 'n': *bp = '\n'; break;
		case 't': *bp = '\t'; break;

		case 0: s--;
		case QUOTE: 
		    *bp = QUOTE;
		    break;

		default: 
		    if (!isdigit (*s)) {
			*bp++ = QUOTE;
			*bp = *s;
		    }
		    r = *s != '0' ? 10 : 8;
		    for (i = 0; isdigit (*s); s++)
			i = i * r + *s - '0';
		    s--;
		    *bp = toascii (i);
		    break;
	    }
	}
    }
    *bp = 0;

    len = strlen (buffer) + 1;
    if ((bp = malloc (len)))
	memcpy (bp, buffer, len);

    return bp;
}

/*
 * Get the fully qualified name of the local host.
 */

char *
LocalName (void)
{
    static char buffer[BUFSIZ] = "";
    struct hostent *hp;

#ifdef HAVE_UNAME
    struct utsname name;
#endif

    /* check if we have cached the local name */
    if (buffer[0])
	return buffer;

    mts_init ("mts");

    /* check if the mts.conf file specifies a "localname" */
    if (*localname) {
	strncpy (buffer, localname, sizeof(buffer));
    } else {
#ifdef HAVE_UNAME
	/* first get our local name */
	uname (&name);
	strncpy (buffer, name.nodename, sizeof(buffer));
#else
	/* first get our local name */
	gethostname (buffer, sizeof(buffer));
#endif
#ifdef HAVE_SETHOSTENT
	sethostent (1);
#endif 
	/* now fully qualify our name */
	if ((hp = gethostbyname (buffer)))
	    strncpy (buffer, hp->h_name, sizeof(buffer));
    }

    /*
     * If the mts.conf file specifies a "localdomain",
     * we append that now.  This should rarely be needed.
     */
    if (*localdomain) {
	strcat (buffer, ".");
	strcat (buffer, localdomain);
    }

    return buffer;
}


/*
 * This is only for UUCP mail.  It gets the hostname
 * as part of the UUCP "domain".
 */

char *
SystemName (void)
{
    static char buffer[BUFSIZ] = "";

#ifdef HAVE_UNAME
    struct utsname name;
#endif

    /* check if we have cached the system name */
    if (buffer[0])
	return buffer;

    mts_init ("mts");

    /* check if mts.conf file specifies a "systemname" */
    if (*systemname) {
	strncpy (buffer, systemname, sizeof(buffer));
	return buffer;
    }

#ifdef HAVE_UNAME
    uname (&name);
    strncpy (buffer, name.nodename, sizeof(buffer));
#else
    gethostname (buffer, sizeof(buffer));
#endif

    return buffer;
}


/*
 * Get the username of current user
 */

char *
getusername (void)
{
    if (username[0] == '\0')
	getuserinfo();

    return username;
}


/*
 * Get full name of current user (typically from GECOS
 * field of password file).
 */

char *
getfullname (void)
{
    if (username[0] == '\0')
	getuserinfo();

    return fullname;
}


/*
 * Find the user's username and full name, and cache them.
 * Also, handle "mmailid" username masquerading controlled from the GECOS field
 * of the passwd file. 
 */

static void
getuserinfo (void)
{
    register char *cp, *np;
    register struct passwd *pw;

#ifdef KPOP
    uid_t uid;

    uid = getuid ();
    if (uid == geteuid () && (cp = getenv ("USER")) != NULL
	&& (pw = getpwnam (cp)) != NULL)
      strncpy (username, cp, sizeof(username));
    else if ((pw = getpwuid (uid)) == NULL
	     || pw->pw_name == NULL
	     || *pw->pw_name == '\0') {
#else /* KPOP */
    if ((pw = getpwuid (getuid ())) == NULL
	    || pw->pw_name == NULL
	    || *pw->pw_name == '\0') {
#endif /* KPOP */
	strncpy (username, "unknown", sizeof(username));
	snprintf (fullname, sizeof(fullname), "The Unknown User-ID (%d)",
		(int) getuid ());
	return;
    }

    np = pw->pw_gecos;

    /* Get the user's real name from the GECOS field.  Stop once we hit a ',',
       which some OSes use to separate other 'finger' information in the GECOS
       field, like phone number.  Also, if mmailid masquerading is turned on due
       to "mmailid" appearing on the "masquerade:" line of mts.conf, stop if we
       hit a '<' (which should precede any ','s). */
#ifndef BSD42
    if (mmailid_masquerading)
	/* Stop at ',' or '<'. */
	for (cp = fullname; *np != '\0' && *np != ',' && *np != '<';
	     *cp++ = *np++)
	    continue;
    else
	/* Allow '<' as a legal character of the user's name.  This code is
	   basically a duplicate of the code above the "else" -- we don't
	   collapse it down to one copy and put the mmailid_masquerading check
	   inside the loop with "(x ? y : z)" because that's inefficient and the
	   value'll never change while it's in there. */
	for (cp = fullname; *np != '\0' && *np != ',';
	     *cp++ = *np++)
	    continue;
#else /* BSD42 */
    /* On BSD(-derived) systems, the system utilities that deal with the GECOS
       field (finger, mail, sendmail, etc.) translate any '&' character in it to
       the login name, with the first letter capitalized.  So, for instance,
       fingering a user "bob" with the GECOS field "& Jones" would reveal him to
       be "In real life: Bob Jones".  Surprisingly, though, the OS doesn't do
       the translation for you, so we have to do it manually here. */
    if (mmailid_masquerading)
	/* Stop at ',' or '<'. */
	for (cp = fullname;
	     *np != '\0' && *np != ',' && *np != '<';) {
	    if (*np == '&')	{	/* blech! */
		strcpy (cp, pw->pw_name);
		*cp = toupper(*cp);
		while (*cp)
		    cp++;
		np++;
	    } else {
		*cp++ = *np++;
	    }
	}
    else
	/* Allow '<' as a legal character of the user's name.  This code is
	   basically a duplicate of the code above the "else" -- we don't
	   collapse it down to one copy and put the mmailid_masquerading check
	   inside the loop with "(x ? y : z)" because that's inefficient and the
	   value'll never change while it's in there. */
	for (cp = fullname;
	     *np != '\0' && *np != ',';) {
	    if (*np == '&')	{	/* blech! */
		strcpy (cp, pw->pw_name);
		*cp = toupper(*cp);
		while (*cp)
		    cp++;
		np++;
	    } else {
		*cp++ = *np++;
	    }
	}
#endif /* BSD42 */
    *cp = '\0';

    if (mmailid_masquerading) {
	/* Do mmailid processing.  The GECOS field should have the form
	   "Full Name <fakeusername>".  For instance,
	   "Dan Harkless <Dan.Harkless>".  Naturally, you'll want your MTA to
	   have an alias (e.g. in /etc/aliases) from "fakeusername" to your
	   account name.  */ 
	if (*np)
	    np++;
	for (cp = username; *np && *np != '>'; *cp++ = *np++)
	    continue;
	*cp = '\0';
    }
    if (!mmailid_masquerading || *np == '\0')
	strncpy (username, pw->pw_name, sizeof(username));

    /* The $SIGNATURE environment variable overrides the GECOS field's idea of
       your real name. */
    if ((cp = getenv ("SIGNATURE")) && *cp)
	strncpy (fullname, cp, sizeof(fullname));

    if (strchr(fullname, '.')) {		/*  quote any .'s */
	char tmp[BUFSIZ];

	/* should quote "'s too */
	snprintf (tmp, sizeof(tmp), "\"%s\"", fullname);
	strncpy (fullname, tmp, sizeof(fullname));
    }

    return;
}


syntax highlighted by Code2HTML, v. 0.9.1