/*
**  Copyright 2000-2004 University of Illinois Board of Trustees
**  Copyright 2000-2004 Mark D. Roth
**  All rights reserved.
**
**  redirection.c - libphclient code to select entries from the server
**
**  Mark D. Roth <roth@feep.net>
*/

#include <internal.h>

#include <errno.h>
#include <ctype.h>

#ifdef STDC_HEADERS
# include <string.h>
# include <stdlib.h>
#endif

#include <strings.h>


/*
** FIXME: can I generalize this further to help simplify ph_email_resolve() ?
*/
static int
_ph_get_alias_field(PH *ph, char *alias, char *field, char **valuep)
{
	int i, save_errno;
	char *getfields[2];
	struct ph_fieldselector query[2];
	ph_memblock_t *blockp;
	ph_entry *entries;

	/* construct query */
	query[0].pfs_field = "alias";
	query[0].pfs_operation = '=';
	query[0].pfs_value = alias;
	memset(&(query[1]), 0, sizeof(struct ph_fieldselector));
	getfields[0] = field;
	getfields[1] = NULL;

	if (ph_mmgr_malloc(ph->ph_mmgr, 0, 0, 0,
			   (ph_free_func_t)ph_free_entries,
			   &blockp) == -1)
		return -1;

	i = ph_query(ph, query, getfields,
		     (ph_entry **)&(ph_mmgr_ptr(blockp)));

	if (i <= 0)
	{
		save_errno = errno;
		ph_mmgr_free(blockp, 1);
		errno = save_errno;
		return i;
	}

	entries = (ph_entry *)ph_mmgr_ptr(blockp);

	if (strcasecmp(entries[0][0].pfv_field, field) != 0)
	{
		ph_mmgr_free(blockp, 1);
		errno = EINVAL;
		return -1;
	}

	if (entries[0][0].pfv_code != -(LR_OK)
	    || entries[0][0].pfv_value[0] == '\0')
	{
		ph_mmgr_free(blockp, 1);
		return PH_ERR_DATAERR;
	}

	if (valuep != NULL)
	{
		*valuep = strdup(entries[0][0].pfv_value);
		if (*valuep == NULL)
		{
			save_errno = errno;
			ph_mmgr_free(blockp, 1);
			errno = save_errno;
			return -1;
		}
	}

	ph_mmgr_free(blockp, 1);
	return 0;
}


/*
** ph_advertised_email() - returns alias-based email address for alias
** returns:
**	0				success
**	-1				error (sets errno)
**	PH_ERR_NOMATCH			alias does not exist
**	PH_ERR_DATAERR			no email field
*/
int
ph_advertised_email(PH *ph, char *alias, int confirm_alias,
		    char **advertised_email)
{
	int i, save_errno;
	char *maildomain, *mailfield;
	void *ptr;

	/* check siteinfo for the field name */
	i = ph_get_siteinfo(ph, "mailfield", &mailfield);
	if (i == -1)
		return -1;
	if (i == PH_ERR_DATAERR)
	{
		errno = EINVAL;
		return -1;
	}

	/* shortcut */
	if (strcasecmp(mailfield, "alias") == 0 && !confirm_alias)
		*advertised_email = strdup(alias);
	else
	{
		i = _ph_get_alias_field(ph, alias, mailfield,
					advertised_email);
		if (i != 0)
			return i;
	}

	/* check siteinfo for the maildomain and append it if found */
	i = ph_get_siteinfo(ph, "maildomain", &maildomain);
	if (i == -1)
		return -1;
	if (i == 0)
	{
		ptr = realloc(*advertised_email,
			      strlen(*advertised_email) + strlen(maildomain) + 2);
		if (ptr == NULL)
		{
			save_errno = errno;
			free(*advertised_email);
			*advertised_email = NULL;
			errno = save_errno;
			return -1;
		}

		*advertised_email = (char *)ptr;
		sprintf(*advertised_email + strlen(*advertised_email), "@%s",
			maildomain);
	}

	return 0;
}


/* create a multiple-word name query */
static int
rewrite_name(char *field, char *user, struct ph_fieldselector **phf)
{
	char buf[PH_BUF_SIZE];
	int i, numfields;
	char *fieldp, *nextp;
	void *ptr;

	strlcpy(buf, user, sizeof(buf));
	for (i = 0; buf[i] != '\0'; i++)
		if (ispunct((int)buf[i]) && buf[i] != '*')
			buf[i] = ' ';

	/* if the name is all spaces, bail out */
	if (strspn(buf, " ") == strlen(buf))
		return PH_ERR_DATAERR;

	numfields = 0;
	nextp = buf;
	*phf = NULL;
	while ((fieldp = strsep(&nextp, " ")) != NULL)
	{
		if (*fieldp == '\0')
			continue;

		ptr = realloc(*phf, (numfields + 2) * sizeof(struct ph_fieldselector));
		if (ptr == NULL)
			return -1;
		*phf = (struct ph_fieldselector *)ptr;
		memset(&((*phf)[numfields]), 0,
		       2 * sizeof(struct ph_fieldselector));
		(*phf)[numfields].pfs_field = NULL;
		(*phf)[numfields].pfs_operation = '=';
		(*phf)[numfields].pfs_value = strdup(fieldp);
		if ((*phf)[numfields].pfs_value == NULL)
			return -1;
		numfields++;
	}

	return 0;
}


/* replace all punctuation characters in alias query with dashes */
static int
rewrite_alias(char *field, char *user, struct ph_fieldselector **phf)
{
	char buf[PH_BUF_SIZE];
	int i;

	strlcpy(buf, user, sizeof(buf));
	for (i = 0; buf[i] != '\0'; i++)
		if (ispunct((int)buf[i]))
			buf[i] = '-';

	*phf = (struct ph_fieldselector *)calloc(2, sizeof(struct ph_fieldselector));
	if (*phf == NULL)
		return -1;

	(*phf)[0].pfs_operation = '=';
	(*phf)[0].pfs_field = strdup(field);
	(*phf)[0].pfs_value = strdup(buf);
	if ((*phf)[0].pfs_field == NULL || (*phf)[0].pfs_value == NULL)
		return -1;

	return 0;
}


/* default action - look up user without rewriting */
static int
no_rewrite(char *field, char *user, struct ph_fieldselector **phf)
{
	*phf = (struct ph_fieldselector *)calloc(2, sizeof(struct ph_fieldselector));
	if (*phf == NULL)
		return -1;

	(*phf)[0].pfs_operation = '=';
	(*phf)[0].pfs_field = strdup(field);
	(*phf)[0].pfs_value = strdup(user);
	if ((*phf)[0].pfs_field == NULL || (*phf)[0].pfs_value == NULL)
		return -1;

	return 0;
}


typedef int (*rewritefunc_t)(char *, char *, struct ph_fieldselector **);

struct field_rewrite_map
{
	char *field;
	rewritefunc_t rewrite_funcs[3];
};
typedef struct field_rewrite_map field_rewrite_map_t;

static const field_rewrite_map_t rewrite_map[] = {
  { "alias",	{ rewrite_alias,	NULL } },
  { "name",	{ no_rewrite,		rewrite_name,	NULL } },
  { NULL,	{ no_rewrite,		NULL } }
};


/*
** ph_email_resolve() - returns the expanded email address for user
** returns:
**	0				success
**	-1				error (sets errno)
**	PH_ERR_NOMATCH			no single match found
**	PH_ERR_DATAERR			no email field
*/
int
ph_email_resolve(PH *ph, char *user, char *fields, char **real_email)
{
	int i, save_errno;
	char *getfields[2];
	char *mailbox, *mailmatches;
	char buf[PH_BUF_SIZE];
	char *fieldp, *nextp;
	ph_memblock_t *qblockp, *eblockp;
	const field_rewrite_map_t *rewritep;
	const rewritefunc_t *rwfunc;
	struct ph_fieldselector *query;
	ph_entry *entries;

	/* check siteinfo for the mailbox field (usually "email") */
	i = ph_get_siteinfo(ph, "mailbox", &mailbox);
	if (i == -1)
		return -1;
	if (i == PH_ERR_DATAERR)
	{
		errno = EINVAL;
		return -1;
	}
	getfields[0] = mailbox;
	getfields[1] = NULL;
#ifdef DEBUG
	printf("ph_email_resolve(): mailbox=\"%s\"\n", mailbox);
#endif

	/* set list of fields to match */
	if (fields != NULL
	    && *fields != '\0')
		mailmatches = fields;
	else
	{
		i = ph_get_siteinfo(ph, "mailmatches", &mailmatches);
		if (i == -1)
			return -1;
		if (i == PH_ERR_DATAERR)
			mailmatches = PH_DEFAULT_MAILMATCHES;
	}
#ifdef DEBUG
	printf("ph_email_resolve(): mailmatches=\"%s\"\n", mailmatches);
#endif

	/* copy mailmatches to a temp buffer so we don't modify the original */
	strlcpy(buf, mailmatches, sizeof(buf));
	nextp = buf;

	/* iterate through the field list to find a match */
	while ((fieldp = strsep(&nextp, " :")) != NULL)
	{
		if (*fieldp == '\0')
			continue;

#ifdef DEBUG
		printf("ph_email_resolve(): trying field %s\n", fieldp);
#endif

		/* find the appropriate rewrite function(s) for the field */
		for (rewritep = rewrite_map;
		     rewritep->field != NULL;
		     rewritep++)
		{
#ifdef DEBUG
			printf("comparing \"%s\" and \"%s\"\n",
			       fieldp, rewritep->field);
#endif
			if (strcasecmp(fieldp, rewritep->field) == 0)
				break;
		}

		/* perform a query with each specified rewrite function */
		for (rwfunc = rewritep->rewrite_funcs;
		     *rwfunc != NULL;
		     rwfunc++)
		{
			if (ph_mmgr_malloc(ph->ph_mmgr, ph_MMGR_ARRAY, 0, 0,
					   (ph_free_func_t)ph_free_selectors,
					   &qblockp) == -1)
				return -1;

			/* do rewrite and perform query */
#ifdef DEBUG
			printf("calling rewrite function at 0x%lx (%s)\n",
			       *rwfunc,
			       (*rwfunc == rewrite_alias ? "rewrite_alias" :
				(*rwfunc == rewrite_name ? "rewrite_name" :
				 "no_rewrite")));
#endif
			i = (**rwfunc)(fieldp, user,
				       (struct ph_fieldselector **)&(ph_mmgr_ptr(qblockp)));
			switch (i)
			{
			case 0:
				break;

			case -1:
				save_errno = errno;
				ph_mmgr_free(qblockp, 1);
				errno = save_errno;
				return -1;

			default:
				/*
				** special case:
				** PH_ERR_DATAERR can be returned by
				** rewrite_name() when user name is
				** all punctuation chars or spaces
				*/
				ph_mmgr_free(qblockp, 1);
				return i;
			}

#ifdef DEBUG
			printf("rewrote \"%s\" to \"%s\"\n",
			       user,
			       ((struct ph_fieldselector *)(ph_mmgr_ptr(qblockp)))[0].pfs_value);
#endif

			query = (struct ph_fieldselector *)ph_mmgr_ptr(qblockp);

			if (ph_mmgr_malloc(ph->ph_mmgr, ph_MMGR_ARRAY, 0, 0,
					   (ph_free_func_t)ph_free_entries,
					   &eblockp) == -1)
				return -1;

#ifdef DEBUG
			puts("calling ph_query()");
#endif
			i = ph_query(ph, query, getfields,
				     (ph_entry **)&(ph_mmgr_ptr(eblockp)));

			/* free query */
#ifdef DEBUG
			puts("free()'ing query memory");
#endif
			ph_mmgr_free(qblockp, 1);

			/* if ph_query() returned an error above */
#ifdef DEBUG
			printf("ph_query() returned %d\n", i);
#endif
			if (i == -1 || i == PH_ERR_DATAERR)
			{
				save_errno = errno;
				ph_mmgr_free(eblockp, 1);
				errno = save_errno;
				return i;
			}

			entries = (ph_entry *)ph_mmgr_ptr(eblockp);

			/* one match found - return it */
			if (i == 1)
			{
				/* if the one match is an error... */
				if (entries[0][0].pfv_code != -(LR_OK))
				{
					ph_mmgr_free(eblockp, 1);
					return PH_ERR_DATAERR;
				}

				/*
				** terminate string at first whitespace or
				** comma (in case more than one email
				** address is listed)
				*/
				nextp = entries[0][0].pfv_value;
				while (*nextp != '\0'
				       && !isspace((int)*nextp)
				       && (*nextp != ','))
					nextp++;
				*nextp = '\0';

#ifdef DEBUG
				puts("returning match");
#endif
				*real_email = strdup(entries[0][0].pfv_value);
				if (*real_email == NULL)
				{
					save_errno = errno;
					ph_mmgr_free(eblockp, 1);
					errno = save_errno;
					return -1;
				}

				ph_mmgr_free(eblockp, 1);
				return 0;
			}

			/* more than one match found - clean up */
			ph_mmgr_free(eblockp, 1);
		}
	}

	/* no matches found */
	return PH_ERR_NOMATCH;
}


/*
** ph_advertised_www() - returns alias-based URL for alias
** returns:
**	0				success
**	-1				error (sets errno)
**	PH_ERR_NOMATCH			alias does not exist
**	PH_ERR_DATAERR			no www field
*/
int
ph_advertised_www(PH *ph, char *alias, int confirm_alias,
		  char **advertised_url)
{
	char *wwwredirect;
	int i, code = 0, save_errno = 0;

	/* check siteinfo for the wwwredirect field */
	i = ph_get_siteinfo(ph, "wwwredirect", &wwwredirect);
	if (i == -1)
		return -1;
	if (i == PH_ERR_DATAERR)
		return ph_www_resolve(ph, alias, advertised_url);

	/* do a query to make sure that the alias exists */
	if (confirm_alias)
	{
		i = _ph_get_alias_field(ph, alias, "www", NULL);
		if (i != 0)
			return i;
	}

	*advertised_url = (char *)malloc(strlen(wwwredirect) + strlen(alias) + 1);
	if (*advertised_url == NULL)
		return -1;
	sprintf(*advertised_url, "%s%s", wwwredirect, alias);
	return 0;
}


/*
** ph_www_resolve() - returns the expanded URL for user
** returns:
**	0				success
**	-1				error (sets errno)
**	PH_ERR_NOMATCH			no single entry matched
**	PH_ERR_DATAERR			no www field
*/
int
ph_www_resolve(PH *ph, char *user, char **real_url)
{
	return _ph_get_alias_field(ph, user, "www", real_url);
}




syntax highlighted by Code2HTML, v. 0.9.1