/*
**  Copyright 2000-2004 University of Illinois Board of Trustees
**  Copyright 2000-2004 Mark D. Roth
**  All rights reserved.
**
**  fields.c - libphclient code to parse server field data
**
**  Mark D. Roth <roth@feep.net>
*/

#include <internal.h>

#include <errno.h>

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

#include <strings.h>


struct attriblist
{
	char *name;
	unsigned long value;
};
typedef struct attriblist attriblist_t;

static attriblist_t attributes[] = {
  { "Always",		PH_ATTRIB_ALWAYS },
  { "Any",		PH_ATTRIB_ANY },
  { "Change",		PH_ATTRIB_CHANGE },
  { "Default",		PH_ATTRIB_DEFAULT },
  { "Encrypt",		PH_ATTRIB_ENCRYPT },
  { "ForcePub",		PH_ATTRIB_FORCEPUB },
  { "Indexed",		PH_ATTRIB_INDEXED },
  { "LocalPub",		PH_ATTRIB_LOCALPUB },
  { "Lookup",		PH_ATTRIB_LOOKUP },
  { "NoMeta",		PH_ATTRIB_NOMETA },
  { "NoPeople",		PH_ATTRIB_NOPEOPLE },
  { "Private",		PH_ATTRIB_PRIVATE },
  { "Public",		PH_ATTRIB_PUBLIC },
  { "Sacred",		PH_ATTRIB_SACRED },
  { "Turn",		PH_ATTRIB_TURN },
  { "Unique",		PH_ATTRIB_UNIQUE },
  { "PrivLookup",	PH_ATTRIB_PRIVLOOKUP },
  { "SacredPerson",	PH_ATTRIB_SACREDPERSON },
  { NULL,		0 }
};


int
ph_is_fieldinfo_cached(PH *ph)
{
	return (ph->ph_fieldlist != NULL ? 1 : 0);
}


/* ph_free_fieldlist() - free a field list generated by ph_field_list(). */
static void
ph_free_fieldlist(struct ph_fieldinfo *fi)
{
	int i, j;

#ifdef DEBUG
	printf("==> ph_free_fieldlist(fi=0x%lx)\n", fi);
#endif

	for (i = 0; fi[i].pf_fnames != NULL; i++)
	{
		for (j = 0; fi[i].pf_fnames[j] != NULL; j++)
			free(fi[i].pf_fnames[j]);
		free(fi[i].pf_fnames);
		if (fi[i].pf_description != NULL)
			free(fi[i].pf_description);
	}

	free(fi);
}


/* maps an attribute string to the corresponding value */
static unsigned long
ph_field_attrib_value(char *buf)
{
	int i;

	for (i = 0; attributes[i].value != 0; i++)
		if (strcasecmp(buf, attributes[i].name) == 0)
			break;

	return attributes[i].value;
}


/*
** returns bitmask representation for a space-delimited string of
** multiple attributes
*/
static unsigned long
ph_encode_field_attributes(char *buf)
{
	char *attribp, *nextp;
	unsigned long bitmask = 0;

	for (nextp = buf; (attribp = strsep(&nextp, " ")) != NULL;)
	{
		if (*attribp == '\0')
			continue;
		BIT_SET(bitmask, ph_field_attrib_value(attribp));
	}

	return bitmask;
}


#if 0
/* maps an attribute value to the corresponding string */
static char *
ph_field_attrib_string(unsigned long attrib)
{
	int i;

	for (i = 0; attributes[i].value != 0; i++)
		if (attributes[i].value == attrib)
			break;

	return attributes[i].name;
}
#endif


/*
** writes a string of space-delimited attribute names corresponding to
** the numeric bitmask
*/
void
ph_decode_field_attributes(unsigned long bitmask, char *buf, size_t bufsize)
{
	int i;
	size_t buflen = 0;

	buf[0] = '\0';

	for (i = 0; attributes[i].value != 0; i++)
		if (BIT_ISSET(bitmask, attributes[i].value))
			buflen += snprintf(buf + buflen, bufsize - buflen,
					   "%s ", attributes[i].name);
}



/*
** ph_retrieve_fieldinfo() - obtain a list of fields supported by the PH server.
** returns:
**	0				success
**	-1				error (sets errno)
*/
int
ph_retrieve_fieldinfo(PH *ph)
{
	int numfields = 0, numaliases = 0;
	int i, code, id, save_errno;
	char *fieldnames, *value, *name;
	char buf[PH_BUF_SIZE];
	void *ptr;
	ph_memblock_t *blockp;
	struct ph_fieldinfo *phfi;

#ifdef DEBUG
	puts("==> ph_retrieve_fieldinfo()");
#endif

	/* don't load if already cached */
	if (ph_is_fieldinfo_cached(ph))
		return 0;

	/* tell the server to list field aliases, if supported */
	ph_set_option(ph, "fieldaliases", NULL);

	if (ph_mmgr_malloc(ph->ph_mmgr, ph_MMGR_ARRAY,
			   0, sizeof(struct ph_fieldinfo),
			   (ph_free_func_t)ph_free_fieldlist,
			   &blockp) == -1)
		return -1;
	phfi = (struct ph_fieldinfo *)ph_mmgr_ptr(blockp);

	if (ph_send_command(ph, "fields") == -1)
		return -1;

	while ((i = ph_get_response(ph, &code, buf, sizeof(buf))) == 0)
	{
#ifdef DEBUG
		printf("ph_retrieve_fieldinfo(): code=%d  buf=\"%s\"\n", code,
		       buf);
#endif

		/* check the response code */
		if (code < LR_OK && code > 0)
			continue;
		if (code == LR_OK)
		{
			ph->ph_fieldlist = phfi;
			ph_mmgr_free(blockp, 0);
			return 0;
		}
		if (code != -(LR_OK))
		{
			ph_mmgr_free(blockp, 1);
			errno = EINVAL;
			return -1;
		}

		/* parse line from server */
		if (ph_parse_response(buf, &id, &fieldnames, &value) == -1)
		{
			ph_mmgr_free(blockp, 1);
			errno = EINVAL;
			return -1;
		}
#ifdef DEBUG
		printf("id=%d  fieldnames=\"%s\"  value=\"%s\"\n", id,
		       fieldnames, value);
#endif

		/* allocate memory if it's a new entry */
		if (numfields == 0
		    || id != phfi[numfields - 1].pf_id)
		{
#ifdef DEBUG
			printf("allocating memory - numfields = %d\n",
			       numfields);
#endif

			if (ph_mmgr_array_add(blockp, numfields + 2) == -1)
			{
				save_errno = errno;
				ph_mmgr_free(blockp, 1);
				errno = save_errno;
				return -1;
			}
			phfi = (struct ph_fieldinfo *)ph_mmgr_ptr(blockp);

			/* set field id */
			phfi[numfields].pf_id = id;

			/* set field names */
			numaliases = 0;
			while ((name = strsep(&fieldnames, " ,")) != NULL)
			{
				ptr = realloc(phfi[numfields].pf_fnames,
					     (numaliases + 2) * sizeof(char *));
				if (ptr == NULL)
				{
					save_errno = errno;
					ph_mmgr_free(blockp, 1);
					errno = save_errno;
					return -1;
				}
				phfi[numfields].pf_fnames = (char **)ptr;

				phfi[numfields].pf_fnames[numaliases] = strdup(name);
				phfi[numfields].pf_fnames[++numaliases] = NULL;
			}

			if (phfi[numfields].pf_fnames[0] == NULL)
			{
				save_errno = errno;
				ph_mmgr_free(blockp, 1);
				errno = save_errno;
				return -1;
			}

			/* set max_size */
			i = sscanf(value, "max %d",
				   &(phfi[numfields].pf_max_size));
			if (i != 1)
			{
				ph_mmgr_free(blockp, 1);
				errno = EINVAL;
				return -1;
			}
#ifdef DEBUG
			printf("max_size = %d\n", phfi[numfields].pf_max_size);
#endif

			/* extract the field attributes */
#ifdef DEBUG
			printf("attributes = \"%s\"\n", value + 4);
#endif
			phfi[numfields].pf_attrib = ph_encode_field_attributes(strchr(value + 4, ' '));

			numfields++;

		}
		else	/* second line for this field */
		{

			/* otherwise, extract the description */
#ifdef DEBUG
			printf("description = \"%s\"\n", value);
#endif
			phfi[numfields - 1].pf_description = strdup(value);
			if (phfi[numfields - 1].pf_description == NULL)
			{
				save_errno = errno;
				ph_mmgr_free(blockp, 1);
				errno = save_errno;
				return -1;
			}
		}

		/* end of loop - call ph_get_response() again */
	}

	/* in case ph_get_response() returned an error */
	save_errno = errno;
	ph_mmgr_free(blockp, 1);
	errno = save_errno;
	return -1;
}


/*
** ph_get_fieldinfo() - return the description for a given field.
** returns:
**	0				success
**	-1				error (sets errno)
**	PH_ERR_DATAERR			unknown field
*/
int
ph_get_fieldinfo(PH *ph, char *fieldname, struct ph_fieldinfo **fieldinfo)
{
	int i, j;

	if (!ph_is_fieldinfo_cached(ph)
	    && ph_retrieve_fieldinfo(ph) == -1)
		return -1;

	for (i = 0; ph->ph_fieldlist[i].pf_fnames != NULL; i++)
		for (j = 0; ph->ph_fieldlist[i].pf_fnames[j] != NULL; j++)
			if (strcasecmp(ph->ph_fieldlist[i].pf_fnames[j],
				       fieldname) == 0)
			{
				*fieldinfo = &(ph->ph_fieldlist[i]);
				return 0;
			}

	return PH_ERR_DATAERR;
}


/*
** ph_fieldinfo_iterate() - return the next field info structure.
** returns:
**	1				data returned
**	0				no more data
**	-1				error (sets errno)
*/
int
ph_fieldinfo_iterate(PH *ph, struct ph_fieldinfo **fieldinfo)
{
	if (!ph_is_fieldinfo_cached(ph)
	    && ph_retrieve_fieldinfo(ph) == -1)
		return -1;

	/* first entry */
	if (*fieldinfo == NULL)
	{
		*fieldinfo = &(ph->ph_fieldlist[0]);
		return 1;
	}

	/* next element */
	(*fieldinfo)++;
	if ((*fieldinfo)->pf_fnames != NULL)
		return 1;

	/* end of list */
	*fieldinfo = NULL;
	return 0;
}


/*
** ph_free_fieldinfo() - free fieldlist associated with ph.
*/
void
ph_free_fieldinfo(PH *ph)
{
	if (ph->ph_fieldlist != NULL)
		ph_free_fieldlist(ph->ph_fieldlist);
	ph->ph_fieldlist = NULL;
}




syntax highlighted by Code2HTML, v. 0.9.1