/* $Id: addrbookentry.cpp,v 1.6 2004/01/02 03:54:01 fesnel Exp $ */
/*******************************************************************************
 *   This program is part of a library used by the Archimedes email client     * 
 *                                                                             *
 *   Copyright : (C) 1995-1998 Gennady B. Sorokopud (gena@NetVision.net.il)    *
 *               (C) 1995 Ugen. J. S. Antsilevich (ugen@latte.worldbank.org)   *
 *               (C) 1998-2004 by the Archimedes Project                       *
 *                   http://sourceforge.net/projects/archimedes                *
 *                                                                             *
 *             --------------------------------------------                    *
 *                                                                             *
 *   This program is free software; you can redistribute it and/or modify      *
 *   it under the terms of the GNU Library General Public License as published *
 *   by the Free Software Foundation; either version 2 of the License, or      *
 *   (at your option) any later version.                                       *
 *                                                                             *
 *   This program is distributed in the hope that it will be useful,           *
 *   but WITHOUT ANY WARRANTY, without even the implied warranty of            *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the             *
 *   GNU Library General Public License for more details.                      *
 *                                                                             *
 *   You should have received a copy of the GNU Library General Public License *
 *   along with this program; if not, write to the Free Software               *
 *   Foundation, Inc., 59 Temple Place, Suite 330, Boston MA 02111-1307, USA.  *
 *                                                                             *
 ******************************************************************************/

#include <stdio.h>
#include <string.h>
#include <sys/types.h>

#include <string>

using namespace std;

#include "fmail.h"
#include "addrbookentry.h"


AddressBookEntry::AddressBookEntry(const char *addr, string desc)
{

	description = desc;
	addrs = laddr = NULL;
	type = ADDRESSBOOKENTRY_SINGLE;
	size = 0;
	AddAddress(addr);
}

AddressBookEntry::AddressBookEntry(struct _mail_addr *addr)
{

	addrs = laddr = NULL;
	size = 0;
	type = ADDRESSBOOKENTRY_SINGLE;
	AddAddress(addr);
}

AddressBookEntry::AddressBookEntry(const AddressBookEntry &entry)
{

	addrs = laddr = NULL;
	size = 0;
	*this = entry;
}

AddressBookEntry::~AddressBookEntry()
{

	clear();
}

bool
AddressBookEntry::AddAddress(const char *addr)
{
	struct _mail_addr *taddr;

	if (addr == NULL)
	    return false;

	taddr = get_address(addr, ADDR_IGNORE_COMMAS);
	if (taddr == NULL)
		return false;

	append_addr(taddr);
	return true;
}

bool
AddressBookEntry::AddAddress(struct _mail_addr *addr)
{
	struct _mail_addr *taddr;

	if (addr == NULL)
	    return false;

	taddr = copy_address_chain(addr);
	if (taddr == NULL)
		return false;

	append_addr(taddr);
	return true;
}

bool
AddressBookEntry::DeleteAddress(const char *addr)
{
	struct _mail_addr *taddr;
	bool ret;

	if (addr == NULL)
		return false;
	taddr = get_address(addr, ADDR_IGNORE_COMMAS);
	if (taddr == NULL)
		return false;

	ret = DeleteAddress(taddr);
	discard_address(taddr);
	return ret;
}

bool
AddressBookEntry::DeleteAddress(struct _mail_addr *addr)
{
	struct _mail_addr *ma, *pma;

	if (addr == NULL)
		return false;

	for (ma = addrs, pma = NULL; ma != NULL; pma = ma, ma = ma->next_addr)
		if (strcmp(ma->addr, addr->addr) == 0) {
			if (pma == NULL)
				addrs = ma->next_addr;
			else
				pma->next_addr = ma->next_addr;
			if (laddr == ma)
				laddr = pma;
			ma->next_addr = NULL;
			discard_address(ma);
			size--;
			break;
		}
	return (ma == NULL) ? false : true;
}		

void
AddressBookEntry::SetAddress(const char *addr)
{
	struct _mail_addr *taddr;

	if (addr == NULL)
		return;
	taddr = get_address(addr, ADDR_IGNORE_COMMAS);
	if (taddr == NULL)
		return;

	if (addrs != NULL) {
		discard_address(addrs);
		addrs = laddr = NULL;
		size = 0;
	}
	append_addr(taddr);
}

void
AddressBookEntry::SetAddress(struct _mail_addr *addr)
{
	struct _mail_addr *taddr;

	if (addr == NULL)
		return;

	taddr = copy_address_chain(addr);
	if (taddr == NULL)
		return;

	if (addrs != NULL) {
		discard_address(addrs);
		addrs = laddr = NULL;
		size = 0;
	}
	append_addr(taddr);
}

void
AddressBookEntry::SetDescription(string desc)
{

	description = desc;
}

void
AddressBookEntry::SetType(ADDRESSBOOKENTRY_TYPE type)
{

	this->type = type;
}

bool
AddressBookEntry::Write(FILE *fp)
{
	struct _mail_addr *addr;

	fprintf(fp, "@ %s\n", description.c_str());
	addr = addrs;
	while (addr != NULL) {
		fprintf(fp, " %s\n", get_full_addr_line(addr));
		if (addr->pgpid != NULL && *addr->pgpid != '\0')
			fprintf(fp, " PGPId:%s\n", addr->pgpid);
		addr = addr->next_addr;
	}
	if (ferror(fp))
		return false;
	return true;
}

/*
 * RETURNS:
 *	0	Successful
 * <0	Error/EOF
 * >0	Invalid file format
 */
int
AddressBookEntry::Read(FILE *fp)
{
#ifdef HAVE_FSEEKO
	off_t orig_off, curr_off;
#else
	long orig_off, curr_off;
#endif
	struct _mail_addr *addr;
	size_t len;
	char buf[256], *ret, *p;
	int pgpid_ok_here;

#ifdef HAVE_FSEEKO
	orig_off = curr_off = ftello(fp);
#else
	orig_off = curr_off = ftell(fp);
#endif
	if (fgets(buf, sizeof(buf), fp) == NULL)
		return -1;

	curr_off += strlen(buf);
	if (strncmp(buf, "@ ", 2) != 0) {
		/* Seek back to beginning */
#ifdef HAVE_FSEEKO
		fseeko(fp, orig_off, SEEK_SET);
#else
		fseek(fp, orig_off, SEEK_SET);
#endif
		return 1;
	}
	strip_newline(buf);
	p = rem_tr_space(buf + 2);
	if (p != NULL && *p != '\0')
		SetDescription(p);
	else
		SetDescription("");

	pgpid_ok_here = 0;
	while ((ret = fgets(buf, sizeof(buf), fp)) != NULL) {
		if (buf[0] != ' ')
			break;
  
		curr_off += strlen(buf);
		len = strlen(buf);
		strip_newline(buf);
		p = rem_tr_space(buf);
		if (strlen(p) == 0)
			continue;

		if (pgpid_ok_here && strncmp(p, "PGPId:", 6) == 0) {
			p += 6;
			while (isspace(*p))
				p++;
			if (strncmp(p, "0x", 2) == 0)
				laddr->pgpid = strdup(p);
			pgpid_ok_here = 0;
			continue;
		}

		if ((addr = get_address(p, ADDR_IGNORE_COMMAS)) == NULL)
			continue;

		AddAddress(addr);
		discard_address(addr);
		pgpid_ok_here = 1;
	}
	if (ret == NULL && (Size() == 0 || !feof(fp)))
		return -1;
	if (Size() == 0) {
#ifdef HAVE_FSEEKO
		fseeko(fp, orig_off, SEEK_SET);
#else
		fseek(fp, orig_off, SEEK_SET);
#endif
		return 1;
	}
#ifdef HAVE_FSEEKO
	fseeko(fp, curr_off, SEEK_SET);
#else
	fseek(fp, curr_off, SEEK_SET);
#endif
	SetType(GetDescription().length() > 0 ? ADDRESSBOOKENTRY_ALIAS : ADDRESSBOOKENTRY_SINGLE);
	return 0;
}

bool
AddressBookEntry::operator<(const AddressBookEntry &entry)
{

	return (compare(*this, entry) < 0);
}

bool
AddressBookEntry::operator>(const AddressBookEntry &entry)
{

	return (compare(*this, entry) > 0);
}

bool
AddressBookEntry::operator==(const AddressBookEntry &entry)
{

	return (compare(*this, entry) == 0);
}

bool
AddressBookEntry::operator<(const AddressBookEntry *entry)
{

	return (compare(this, entry) < 0);
}

bool
AddressBookEntry::operator>(const AddressBookEntry *entry)
{

	return (compare(this, entry) > 0);
}

bool
AddressBookEntry::operator==(const AddressBookEntry *entry)
{

	return (compare(this, entry) == 0);
}

bool
AddressBookEntry::Match(const char *addr) const
{
	const struct _mail_addr *paddr;
	struct _mail_addr *taddr;

	if (addr == NULL)
		return false;

	// If we match an alias name than it's a match
	if (description == addr)
		return true;

	taddr = get_address(addr, ADDR_IGNORE_COMMAS);
	if (taddr == NULL)
		return false;

	/* determine whether address matches any of the address contained */
	for (paddr = addrs; paddr != NULL; paddr = paddr->next_addr)
		if (strcasecmp(paddr->addr, taddr->addr) == 0) {
			discard_address(taddr);
			return true;
		}

	return false;
}

bool
AddressBookEntry::Match(struct _mail_addr *addr) const
{
	const struct _mail_addr *paddr;

	if (addr == NULL)
		return false;

	/* determine whether address matches any of the address contained */
	for (paddr = addrs; paddr != NULL; paddr = paddr->next_addr)
		if (strcasecmp(paddr->addr, addr->addr) == 0)
			return true;

	return false;
}

AddressBookEntry &
AddressBookEntry::operator=(const AddressBookEntry &entry)
{

	/* self-assignment */
	if (this == &entry)
		return *this;

	clear();
	append_addr(copy_address_chain(entry.addrs));
	description = entry.description;
		
	return *this;
}

int
AddressBookEntry::compare(const AddressBookEntry &e1, const AddressBookEntry &e2)
{

	return compare(&e1, &e2);
}

int
AddressBookEntry::compare(const AddressBookEntry *e1, const AddressBookEntry *e2)
{
	string s1, s2;

	if (e1->description.length() > 0)
		s1 = e1->description;
	else if (e1->addrs->addr != NULL)
		s1 = string(e1->addrs->addr);
	else
		return 0;

	if (e2->description.length() > 0)
		s2 = e2->description;
	else if (e2->addrs->addr != NULL)
		s2 = string(e2->addrs->addr);
	else
		return 0;

	if (s1.length() == 0 || s2.length() == 0)
		return 0;

	return strcasecmp(s1.c_str(), s2.c_str());
}

void
AddressBookEntry::clear()
{

	if (addrs != NULL)
		discard_address(addrs);
	addrs = laddr = NULL;
	description = string("");
	size = 0;
	type = ADDRESSBOOKENTRY_SINGLE;
}

void
AddressBookEntry::append_addr(struct _mail_addr *addr)
{

	if (addrs == NULL)
		addrs = addr;
	else
		laddr->next_addr = addr;

	/* reset 'laddr' to the last address and update size */
	for (laddr = addr, size++; laddr->next_addr != NULL;
		 laddr = laddr->next_addr, size++);
}

