/*
 * Copyright (c) 1994 - 2001 by Sun Microsystems, Inc.
 * All rights reserved.
 */

#pragma ident	"@(#)sun_compat.c	1.20	01/08/27 SMI"

#ifndef lint
static char id[] = "@(#)sun_compat.c	1.20 (Sun) 08/27/01";
#endif /* not lint */

#include "sendmail.h"
#include <nsswitch.h>
#ifdef INTER
#include <locale.h>
#endif

#undef NIS	/* symbol conflict in nis.h */
#include <rpcsvc/nis.h>

#define DEF_ACTION		{1, 0, 0, 0}     /* name service switch data */
#define EN_len			zo_data.objdata_u.en_data.en_cols.en_cols_len
#define EN_col(col)		zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val

#ifdef SUN_LOOKUP_MACRO
# define	SENDMAIL_MAP_NISPLUS	"sendmailvars.org_dir"
# define	SENDMAIL_MAP_FILE	"/etc/mail/sendmailvars"
#endif

extern int getdomainname();

void
init_md_sun()
{
        struct stat sbuf;

        /* Check for large file descriptor */
        if (fstat(fileno(stdin), &sbuf) < 0)
        {
                if (errno == EOVERFLOW)
                {
                        perror("stdin");
                        exit(EX_NOINPUT);
                }
        }
#ifdef INTER
	setlocale(LC_ALL, "");
#endif
}


#ifdef SUN_INIT_DOMAIN
/* this is mainly for backward compatibility in Sun environment */
char *
sun_init_domain()
{
	/*
	 * Get the domain name from the kernel.
	 * If it does not start with a leading dot, then remove
	 * the first component.  Since leading dots are funny Unix
	 * files, we treat a leading "+" the same as a leading dot.
	 * Finally, force there to be at least one dot in the domain name
	 * (i.e. top-level domains are not allowed, like "com", must be
	 * something like "sun.com").
	 */
        char buf[MAXNAME];
        char *period, *autodomain;
        
        if (getdomainname(buf, sizeof buf) < 0)
                return NULL;

	if (strlen(buf) == 0)
		return NULL;

	if (tTd(0, 20))
		printf("domainname = %s\n", buf);

        if (buf[0] == '+')
                buf[0] = '.';
        period = strchr(buf, '.');
        if (period == NULL)
                autodomain = buf;
        else
		autodomain = period+1;
        if (strchr(autodomain, '.') == NULL)
                return newstr(buf);
	else
		return newstr(autodomain);
}
#endif /* SUN_INIT_DOMAIN */

#ifdef SUN_DEFAULT_VALUES
static void
fix_mailers()
{
#ifdef SUN_CONTENT_LENGTH
	char buf[100];

	/*
	 * Since sendmail no longer computes the Content-Length: header,
	 * we have extended mail.local with a -F option to handle the file
	 * mailer function.  mail.local already knows how to compute the
	 * Content-Length: header.
	 */

	(void)strcpy(buf, "*file*, P=/usr/lib/mail.local, F=lsDFMPESouqn9, T=DNS/RFC822/X-Unix, A=mail.local -F \201u");
        makemailer(buf);
#endif
}

/* ARGSUSED */
void
sun_pre_defaults(e)
ENVELOPE *e;
{
}

/* ARGSUSED */
void
sun_post_defaults(e)
ENVELOPE *e;
{
	if (VendorCode == VENDOR_SUN)
		fix_mailers();
}
#endif /* SUN_DEFAULT_VALUES */

#ifdef SUN_LOOKUP_MACRO
static void files_TableLookUp();
static void nisplus_TableLookUp();

static char *
next_word(buf, word)
	char buf[];
	char **word;
{
	register char *p;
	register char *wd;
	char last_char;

	p = buf;
	while (*p != '\0' && isspace(*p))
		p++;
	wd = p;
	while (*p != '\0' && !isspace(*p))
		p++;
	last_char = *p;
	*p = '\0';
	*word = wd;
	if (last_char)
	{
		p++;
		while (*p != '\0' && isspace(*p))
			p++;
	}
	return (p);
}

/*
**  LOOKUP_DATA - lookup value of macro/class
**
*/

#ifndef NSW_SENDMAILVARS_DB
/* this define should be in /usr/include/nsswitch.h */
#define	NSW_SENDMAILVARS_DB "sendmailvars"
#endif

static struct __nsw_lookup lkp1 = { "nisplus", DEF_ACTION, NULL, NULL};
static struct __nsw_lookup lkp0 = { "files", DEF_ACTION, NULL, &lkp1};
static struct __nsw_switchconfig mailconf_default =
				    { 0, NSW_SENDMAILVARS_DB, 1, &lkp0};
static struct __nsw_lookup *mailconf_nsw = NULL;

static
lookup_data(search_key, answer_buf, bufsize)
	char search_key[];
	char answer_buf[];
	int  bufsize;
{
	int nserr;
	enum __nsw_parse_err pserr;
	struct __nsw_switchconfig *nsw_conf = NULL;
	struct __nsw_lookup *lk;

	if (!mailconf_nsw)
	{
		if (!(nsw_conf = __nsw_getconfig(NSW_SENDMAILVARS_DB, &pserr)))
			nsw_conf = &mailconf_default;
		mailconf_nsw = nsw_conf->lookups;
	}

	for (lk = mailconf_nsw; lk; lk = lk->next)
	{
		nserr = __NSW_UNAVAIL;
		if (strcmp(lk->service_name, "nisplus") == 0)
			nisplus_TableLookUp(&nserr, search_key,
				    answer_buf, &bufsize);
		else
		{
			if (strcmp(lk->service_name, "files") == 0)
				files_TableLookUp(&nserr, search_key,
				    answer_buf, &bufsize);
			else
				syslog(LOG_CRIT,
			    	    "can't Lookup data via name service \"%s\"",
					lk->service_name);
		}
		if (__NSW_ACTION(lk, nserr) == __NSW_RETURN)
			break;
	}
	return (nserr);
}

/*
 * Parse the 'L' and 'G' lines from the config file.
 */

void
sun_lg_config_line(bp, e)
	char *bp;
	ENVELOPE *e;
{
	char answer[MAXLINE];
	char *search_key;
	char *word;
        auto char *ep;
        int mid;
        register char *p;

	mid = macid_parse(&bp[1], &ep);
	if (mid == -1)
		return;

	(void)next_word(ep, &search_key);
	if (lookup_data(search_key, answer, MAXLINE))
	{
		/*
		 * XXX fail silently for now
		 * syslog(LOG_CRIT, "can't Lookup \"%s\"", search_key);
		 */
		return;
	}

	if (bp[0] ==  'L')
		macdefine(&e->e_macro, A_PERM, bp[1], newstr(answer));
	else
	{	/* i.e bp[0] == 'G' */
		p = next_word(answer, &word);
		while (*word)
		{
			setclass(bp[1], word);
			p = next_word(p, &word);
		}
	}
}

void
files_TableLookUp(nserrp, search_key, answer_buf, bufsizep)
	int *nserrp;
	char search_key[];
	char answer_buf[];
	int *bufsizep;
{
	SM_FILE_T *cf;
	char buf[MAXLINE];
	long sff = SFF_OPENASROOT|SFF_REGONLY;

	if ((search_key == NULL) || (answer_buf == NULL) || (*bufsizep <= 0))
	{
		*nserrp = -1; /* bad param */
		return;
	}

	answer_buf[0] = '\0';
	cf = safefopen(SENDMAIL_MAP_FILE, O_RDONLY, 0, sff);
	if (cf == NULL)
	{
		*nserrp = __NSW_UNAVAIL;
		return;
	}

	*nserrp = __NSW_NOTFOUND;
	while (fgetfolded(buf, sizeof (buf), cf) != NULL)
	{
		char *p;
		char last_char;

		p = buf;
		if ((*p == '#') || (*p == ' ') || (*p == '\t'))
			continue; /* skip comment/blank line */
		/* find end of first field */
		while (*p)
		{
			if ((*p == ' ') || (*p == '\t'))
				break;
			p++;
		}

		last_char = *p;
		*p = '\0';
		if (!strcmp(buf, search_key))
		{
			*nserrp = __NSW_SUCCESS;
			if (last_char)
				p++;
			/* strip leading spaces */
			while ((*p == ' ') || (*p == '\t'))
				p++;
			answer_buf[*bufsizep - 1]  = '\0';
			(void) strncpy(answer_buf, p, *bufsizep - 1);
			*bufsizep = strlen(answer_buf) + 1;
			break;
		}
	}
	(void) sm_io_close(cf, SM_TIME_DEFAULT);
}

void
nisplus_TableLookUp(nserrp, search_key, answer_buf, bufsizep)
	int *nserrp;
	char search_key[];
	char answer_buf[];
	int *bufsizep;
{

	char table_name[MAXNAME];
	int  column_id;

	nis_result *result;
	char qbuf[MAXLINE];

	if ((search_key == NULL) || (answer_buf == NULL) || (*bufsizep <= 0))
	{
		*nserrp = -1; /* bad param */
		return;
	}

	*nserrp = __NSW_UNAVAIL;

	(void) snprintf(table_name, sizeof table_name, "%s.%s",
			SENDMAIL_MAP_NISPLUS, nis_local_directory());
	column_id = 1;

	/* construct the query */
	(void) snprintf(qbuf, sizeof qbuf, "[%s=%s],%s", "key", search_key,
		table_name);

	result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL);
	if (result->status == NIS_SUCCESS)
	{
		int count;

		if ((count = (result->objects).objects_len) != 1)
			syslog(LOG_CRIT,
			    "Lookup error, expected 1 entry, got (%d)", count);
		else
		{
			if (NIS_RES_OBJECT(result)->EN_len <= column_id)
				syslog(LOG_CRIT,
				"Lookup error, no such column (%d)", column_id);
			else
			{
				*nserrp = __NSW_SUCCESS;
				answer_buf[*bufsizep - 1] = '\0';
				(void) strncpy(answer_buf,
				  ((NIS_RES_OBJECT(result))->EN_col(column_id)),
				  *bufsizep - 1);
				/* set the length of the result */
				*bufsizep = strlen(answer_buf) + 1;
			}
		}
	}
	else
		*nserrp = __NSW_NOTFOUND;
	nis_freeresult(result);
}
#endif /* SUN_LOOKUP_MACRO */
