/* $Id: addrbook_util.cpp,v 1.5 2003/02/23 17:50:41 cfreeze Exp $ */
/*******************************************************************************
 *   This program is part of the XFMail 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-2001 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 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 General Public License for more details.                              *
 *                                                                             *
 *   You should have received a copy of the GNU 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.  *
 *                                                                             *
 *   Additional Permission granted:                                            *
 *                                                                             *
 *   This program is designed to use the XForms library, so we consider        *
 *   permission to link to that non-GPL-compatible library is implicit.        *
 *   However, in case this is not considered so, we explicitly state:          *
 *                                                                             *
 *   "As a special exception, the Archimedes Project, with the permission      *
 *    of all earlier copyright holders, formally gives permission to link      *
 *    this program with the XForms library, and distribute the resulting       *
 *    executable without the source code for XForms in the source              *
 *    distribution".                                                           *
 *                                                                             *
 ******************************************************************************/

#include <config.h>
#include <fmail.h>
#include <umail.h> // For display_msg()
#include <cfgfile.h>

#ifdef WITH_LDAP
#include "fldap.h"
#endif

#include "addrbook_util.h"
#include "addrbookdb.h"

AddressBookDB addrbookdb;

void add_each_addr(struct _mail_addr *addr, string bookname)
{
	struct _mail_addr *taddr;

	for (; addr != NULL; addr = addr->next_addr) {
		taddr = addr->next_addr;
		addr->next_addr = NULL;
		if (addrbookdb.FindBook(bookname)->FindEntry(addr) == NULL)
			addrbookdb.FindBook(bookname)->AddEntry(new AddressBookEntry(addr));
		addr->next_addr = taddr;
	}
}

void add_msg_addr(struct _mail_msg *msg, string bookname) {
	
	/* XXX Need to update UI if we add while it is open */
	/*
	if(ready)
		return;
	*/

	if(msg == NULL)
		return;

	if(msg->header == NULL)
		return;

	if (addrbookdb.FindBook(bookname) == NULL && !addrbookdb.NewBook(bookname))
	    return;

	/* Add all message headers */
	add_each_addr(msg->header->From, bookname);
	add_each_addr(msg->header->To, bookname);
	add_each_addr(msg->header->Cc, bookname);
	add_each_addr(msg->header->Bcc, bookname);
}

struct _mail_addr *find_addr(struct _mail_addr *addr) {
	AddressBookEntry *entry;

	if(addr == NULL)
		return NULL;

	if ((entry = addrbookdb.FindEntry(addr)) == NULL)
		return NULL;

	return entry->GetAddress();
}

struct _mail_addr *find_alias(char *name) {
	AddressBook::iterator p;

	if (addrbookdb.FindBook("default") == NULL)
			return NULL;

	/* XXX: support should be included in addressbook */
	for (p = addrbookdb.FindBook("default")->begin();
		 p != addrbookdb.FindBook("default")->end(); p++)
			if ((*p)->GetType() == ADDRESSBOOKENTRY_ALIAS &&
				(*p)->GetDescription() == string(name))
					return (*p)->GetAddress();

	return NULL;
}

/* expand address list by parsing aliases, macros and eliminating duplicates */
struct _mail_addr *expand_addr_list(struct _mail_msg *msg,
									struct _mail_addr *addr) {
	struct _mail_addr *expanded = NULL, *addr1, *addr2, *aprev, *alias;
	int do_expand, total = 0;

	if(!addr)
		return NULL;

	addr1 = addr;
	aprev = NULL;
	while(addr1) {
		if(total++ >= MAX_RECP_NUM)
			break;
		do_expand = 0;
		alias = NULL;
		if(msg && (addr1->name == NULL) && (addr1->comment == NULL)) {
			if((!strcmp(addr1->addr, "$from") ||
				!strcmp(addr1->addr, "$f")) && msg->header->From) {
				alias = copy_address(msg->header->From);
				alias->next_addr = addr1->next_addr;
				addr1->next_addr = NULL;
				discard_address(addr1);
				if(aprev)
					aprev->next_addr = alias;
				else
					addr = alias;
				addr1 = alias;
				do_expand = -1;
			} else
				if((!strcmp(addr1->addr, "$sender") ||
					!strcmp(addr1->addr, "$s")) && msg->header->Sender) {
				alias = copy_address(msg->header->Sender);
				alias->next_addr = addr1->next_addr;
				addr1->next_addr = NULL;
				discard_address(addr1);
				if(aprev)
					aprev->next_addr = alias;
				else
					addr = alias;
				addr1 = alias;
				do_expand = -1;
			}
		}

		if((do_expand >= 0) &&
		   (addr1->name == NULL) &&
		   (addr1->comment == NULL) &&
		   (strchr(addr1->addr, '@') == NULL)) {
		   if((alias = find_alias(addr1->addr)) != NULL) {
			   do_expand = 1;
	   } 
#ifdef WITH_LDAP
	   else if ((alias = find_ldap_expansion(addr1->addr)) != NULL) {
		   do_expand = 2;
	   }
#endif
	}

		if(do_expand > 0) {
			addr2 = expanded;
			while(addr2) {
				if(!strcasecmp(addr2->addr, addr1->addr)) {
					do_expand = 0;
					if(aprev)
						aprev->next_addr = addr1->next_addr;
					else
						addr = addr1->next_addr;
					addr1->next_addr = NULL;
					discard_address(addr1);
					addr1 = aprev ? aprev->next_addr : addr;
					break;
				}
				addr2 = addr2->next_addr;
			}

			if(do_expand > 0) {
				alias = copy_address_chain(alias);
				addr2 = alias;
				while(addr2->next_addr && total++)
					addr2 = addr2->next_addr;
				addr2->next_addr = addr1->next_addr;
				addr1->next_addr = expanded;
				expanded = addr1;
				if(aprev)
					aprev->next_addr = alias;
				else
					addr = alias;
				addr1 = alias;
			}
			continue;
		}

		aprev = addr1;
		addr1 = addr1->next_addr;
	}

	discard_address(expanded);

	addr1 = addr;
	aprev = NULL;
	while(addr1) {
		addr2 = addr1->next_addr;

		if(is_newsgroup_addr(addr1, 1)) {
			if(aprev)
				aprev->next_addr = addr1->next_addr;
			else
				addr = addr1->next_addr;
			addr2 = addr1->next_addr;
			addr1->next_addr = NULL;
			discard_address(addr1);
			addr1 = addr2;
			if(addr1 == NULL)
				break;
			continue;
		}

		while((addr2 != NULL) && (addr2->addr != NULL)) {
			if(!strcasecmp(addr1->addr, addr2->addr)) {
				if(aprev)
					aprev->next_addr = addr1->next_addr;
				else
					addr = addr1->next_addr;
				addr2 = addr1->next_addr;
				addr1->next_addr = NULL;
				discard_address(addr1);
				addr1 = addr2;
			}
			if(addr1 == NULL)
				break;
			addr2 = addr2->next_addr;
		}

		if(addr1 == NULL)
			break;
		aprev = addr1;
		addr1 = addr1->next_addr;
	}

	return addr;
}

/*
 * convert_addrbook_mailrc()
 *
 * Convert from a mailrc aliases file to an XFMail format address book.
 */
bool
convert_addrbook_mailrc(FILE *in, FILE *out)
{
	AddressBookEntry entry;
	struct _mail_addr *addr;
	const char aliascmd[] = "alias";
	char buf[256];
	char *cp, *cp2;
	unsigned int nwrote = 0;

	while (fgets(buf, sizeof(buf), in) != NULL) {
		strip_newline(buf);

		cp = strtok(buf, " \t\n");
		if (cp == NULL)
			continue;

		/* Check whether we match the a[lias] command */
		if ((cp2 = strstr(aliascmd, cp)) == NULL || cp2 != aliascmd)
			continue;

		cp = strtok(NULL, " \t\n");
		if (cp == NULL)
			continue;

		entry.SetDescription(cp);
		entry.SetType(ADDRESSBOOKENTRY_ALIAS);
		
		/* The remaining part we must manually parse. */
		cp = cp + strlen(cp) + 1;
		while (*cp != '\0' && isspace(*cp))
			cp++;
		
		if (strlen(cp) <= 0)
			continue;

		/* Check for balanced ' or " */
		if ((*cp == '\'' || *cp == '"') && ((cp2 = strchr(cp + 1, *cp)) != NULL)) {
			cp++;
			*cp2 = '\0';
		} else if ((cp2 = strchr(cp, ' ')) != NULL) {
			*cp2 = '\0';
		}

		if ((addr = get_address(cp, ADDR_IGNORE_COMMAS)) == NULL) {
			display_msg(MSG_LOG, __func__, "illegal address, '%s'", cp);
			continue;
		}

		entry.SetAddress(addr);
		discard_address(addr);
		if (entry.Write(out))
			nwrote++;
	}

	return nwrote ? true : false;
}

/*
 * convert_addrbook_pine()
 *
 * Convert from a pine address book to an XFMail format addressbook.
 *
 * NOTE: This is cut-and-pasted from the old version.
 */
bool
convert_addrbook_pine(FILE *in, FILE *out)
{
	AddressBookEntry entry;
	struct _mail_addr *addr;
	char buf[256], buf1[256], *p, *p1, *comm;
	unsigned int nwrote = 0;

	buf1[0] = '\0';
	buf[0] = '\0';
	if(fgets(buf, sizeof(buf), in) == NULL) {
		display_msg(MSG_LOG, __func__, "Empty address book");
		return false;
	}

	while(1) {
		if(*buf1 != '\0')
			strcpy(buf, buf1);
		else if(*buf == '\0')
			break;

		if((*buf == '#') || (*buf == ' ')) {
			buf1[0] = '\0';
			if(fgets(buf, sizeof(buf), in) == NULL)
				break;
			continue;
		}

		strip_newline(buf);

		buf1[0] = '\0';

		while(fgets(buf1, sizeof(buf1), in) != NULL) {
			if(*buf1 == '#')
				continue;

			if(*buf1 != ' ')
				break;

			strip_newline(buf1);
			if((strlen(buf1) + strlen(buf) + 2) >= sizeof(buf)) {
				display_msg(MSG_LOG, __func__,
							"input buffer too long, truncating");
				break;
			}

			p = buf1;
			while(*p == ' ')
				p++;
			strcat(buf, " ");
			strcat(buf, p);
			buf1[0] = '\0';
		}

		if((p = strchr(buf, '\t')) == NULL) {
			if(strlen(buf) > 32)
				buf[32] = '\0';
			display_msg(MSG_LOG, __func__,
						"invalid entry in address book: %s", buf);
			buf[0] = '\0';
			continue;
		}

		*p++ = '\0';

		if(strlen(buf) > 16)
			buf[16] = '\0';

		entry.SetDescription(buf);
		entry.SetType(ADDRESSBOOKENTRY_ALIAS);

		comm = p;
		if((p1 = strchr(p, '\t')) == NULL) {
			if(strlen(p) > 32)
				p[32] = '\0';
			display_msg(MSG_LOG, __func__,
						"invalid entry in address book: %s", p);
			buf[0] = '\0';
			continue;
		}

		*p1 = '\0';
		p = ++p1;
		if(*p == '(')
			p++;

		if((p1 = strchr(p, '\t')) == NULL)
			p1 = p + strlen(p);
		else
			*p1-- = '\0';

		if(*p1 == ')')
			*p1 = '\0';

		if((addr = get_address(p, 0)) == NULL) {
			if(strlen(p) > 32)
				p[32] = '\0';
			display_msg(MSG_LOG, __func__,
						"invalid address entry in address book: %s", p);
			buf[0] = '\0';
			continue;
		}

		if((addr->num == 1) && comm) {
			if(addr->name == NULL)
				addr->name = strdup(comm);
			else if(addr->comment == NULL)
				addr->comment = strdup(comm);
		}

		entry.SetAddress(addr);
		discard_address(addr);

		if (entry.Write(out))
				nwrote++;
		buf[0] = '\0';
	}

	return (nwrote > 0 ? true : false);
}


/*
 * convert_addrbook_text()
 *
 * Convert from a text file with the first line declaring the aliased
 * address.
 */
bool
convert_addrbook_text(FILE *in, FILE *out)
{
	AddressBookEntry entry;
	struct _mail_addr *addr;
	char buf[256];
	unsigned int nread = 0;

	if (fgets(buf, sizeof(buf), in) == NULL)
	    return false;

	strip_newline(buf);

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

	entry.SetDescription(buf);
	entry.SetType(ADDRESSBOOKENTRY_ALIAS);

	while (fgets(buf, sizeof(buf), in) != NULL) {
		strip_newline(buf);
		if (strlen(buf) == 0)
		    break;

		if ((addr = get_address(buf, ADDR_IGNORE_COMMAS)) == NULL) {
			display_msg(MSG_LOG, __func__, "illegal address, '%s'", buf);
			continue;
		}

		entry.AddAddress(addr);
		discard_address(addr);
		nread++;
	}

	if (nread > 0)
	    return entry.Write(out);
	else
	    return false;
}


bool
save_addressbook(string bookname)
{
	AddressBook *book;

	if ((book = addrbookdb.FindBook(bookname)) == NULL)
		return false;

	return book->Save(configdir);
}

bool
load_addressbook(string bookname)
{
	AddressBook *book;

	if ((book = addrbookdb.FindBook(bookname)) == NULL)
		return false;

	return book->Load(configdir);
}

bool
load_addressbooks()
{

	return addrbookdb.Load(configdir);
}

bool
save_addressbooks()
{

	return addrbookdb.Save(configdir);
}
