/* $Id: mailaddr.cpp,v 1.7 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.  *
 *                                                                             *
 ******************************************************************************/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include <string>
#include <list>
#include <stdio.h>

using std::string;
using std::list;

#include "fmail.h"		// Needed for struct _mail_addr
#include "mailaddr.h"

MailAddress::MailAddress()
{ }

MailAddress::MailAddress(string fullAddress, int flags)
{

	if (!setFromFull(fullAddress, flags))
		throw EInvalidAddress();
}

MailAddress::MailAddress(string addr, string name, string comment,
	string pgpid)
{

	setAddress(addr);
	setName(name);
	setComment(comment);
	setPGPId(pgpid);
}

MailAddress::MailAddress(const MailAddressList &maddrlist)
{

	if (maddrlist.size() == 0)
		throw EInvalidAddress();

	*this = maddrlist.front();
}

MailAddress::MailAddress(const struct _mail_addr *addr)
{

	if (addr == NULL)
		throw EInvalidAddress();

	_addr = addr->addr ? addr->addr : "";
	_name = addr->name ? addr->name : "";
	_comment = addr->comment ? addr->comment : "";
	_pgpid = addr->pgpid ? addr->pgpid : "";
}

string
MailAddress::Address() const
{

	return _addr;
}

string
MailAddress::Name() const
{

	return _name;
}

string
MailAddress::Comment() const
{

	return _comment;
}

string
MailAddress::PGPId() const
{

	return _pgpid;
}

string
MailAddress::buildFull() const
{
	char addrline[256];

	if (_addr.length() == 0)
		addrline[0] = '\0';
	else if (_name.length() == 0 && _comment.length() == 0)
		snprintf(addrline, sizeof(addrline), "%s", _addr.c_str());
	else if (_name.length() > 0 && _comment.length() > 0)
		snprintf(addrline, sizeof(addrline), "%s (%s) <%s>", _name.c_str(),
			_comment.c_str(), _addr.c_str());
	else if (_name.length() > 0)
		snprintf(addrline, sizeof(addrline), "%s <%s>", _name.c_str(),
			_addr.c_str());
	else
		snprintf(addrline, sizeof(addrline), "(%s) <%s>", _comment.c_str(),
			_addr.c_str());
	return addrline;
}

string
MailAddress::buildShort() const
{

	if (_addr.length() == 0)
		return "";
	else
		return _addr;
}

void
MailAddress::setAddress(string addr)
{

	_addr = addr;
}

void
MailAddress::setName(string name)
{

	_name = name;
}

void
MailAddress::setComment(string comment)
{

	_comment = comment;
}

void
MailAddress::setPGPId(string pgpid)
{

	_pgpid = pgpid;
}

/* setFromFull
 *
 * Set the current address structure from the address string.
 * Parse address to a MailAddressList and take the first address
 * as the one to use.
 */
bool
MailAddress::setFromFull(string fullAddress, int flags)
{
	MailAddressList addrlist;

	addrlist = parseAddressString(fullAddress, flags | MAILADDR_GET_FIRST);
	if (addrlist.empty())
		return false;

	*this = addrlist.front();
	return true;
}

struct _mail_addr *
MailAddress::toOldAddress() const
{
	struct _mail_addr *addr;

	addr = (struct _mail_addr *)malloc(sizeof(struct _mail_addr));

	addr->addr = _addr.length() > 0 ? strdup(_addr.c_str()) : NULL;
	addr->name = _name.length() > 0 ? strdup(_name.c_str()) : NULL;
	addr->comment = _comment.length() > 0 ? strdup(_comment.c_str()) : NULL;
	addr->pgpid = _pgpid.length() > 0 ? strdup(_pgpid.c_str()) : NULL;

	return addr;
}

bool
MailAddress::operator<(const MailAddress &addr)
{

	if (_name.length() > 0 && addr._name.length() > 0)
		return (_name < addr._name);
	else if (_name.length() > 0)
		return (_name < addr._addr);
	else if (addr._name.length() > 0)
		return (_addr < addr._name);
	else if (_comment.length() > 0 && addr._comment.length() > 0)
		return (_comment < addr._comment);
	else if (_comment.length() > 0)
		return (_comment < addr._addr);
	else if (addr._comment.length() > 0)
		return (_addr < addr._comment);
	else
		return _addr < addr._addr;
}

bool
MailAddress::operator>(const MailAddress &addr)
{

	return !(*this < addr || *this == addr);
}

bool
MailAddress::operator==(const MailAddress &addr)
{

	return (_addr == addr._addr);
}

bool
MailAddress::operator!=(const MailAddress &addr)
{

	return !(*this == addr);
}


MailAddressList parseAddressString(string str, int flags)
{
	MailAddressList addrlist;
    char addr[256];
	char name[256];
	char comment[256];
	char *cur, c;
	const char *brkstr, *p, *p1, *p2, *pStr;
	int len, curlen;
#define ABUF_LEN    200

	if(str.length() == 0)
		return addrlist;

	addr[0] = name[0] = comment[0] = '\0';
	curlen = 0;

	cur = addr;
	pStr = str.c_str();
	brkstr = (flags & MAILADDR_IGNORE_COMMAS) ? "<(\'\"" : "<(,\'\"";

	while(1) {
		if((p = strpbrk(pStr, brkstr)) != NULL) {
			c = *p;
			len = ABUF_LEN - curlen;
			if(len < 0)
				len = 0;
			snprintf(cur, len, "%.*s", p - pStr, pStr);
			len = strlen(cur);
			cur = cur + len;
			curlen += len;
			pStr = p;
		} else {
			len = ABUF_LEN - curlen;
			if(len < 0)
				len = 0;
			snprintf(cur, len, "%s", pStr);
			c = '\0';
		}

		switch(c) {
			case '(':
				pStr++;
				if((p = strchr(pStr, ')')) == NULL) {
					*cur = c;
					cur++;
					*cur = '\0';
					break;
				}

				for(p1 = pStr, p2 = p; p1 < p2; p1++) {
					if(*p1 == '(') {
						if((p = strchr(++p, ')')) == NULL)
							break;
					}
				}

				if(p == NULL) {
					*cur = c;
					cur++;
					*cur = '\0';
					break;
				}

				if(comment[0] == '\0')
					snprintf(comment, sizeof(comment), "%.*s", p - pStr, pStr);

				pStr = p;
				pStr++;
				if(addr[0] != '\0') {
					cur = name + strlen(name);
					curlen = strlen(name);
				} else {
					cur = addr + strlen(addr);
					curlen = strlen(addr);
				}
				break;

			case '<':
				pStr++;
				if((p = strchr(pStr, '>')) == NULL) {
					*cur = c;
					cur++;
					*cur = '\0';
					break;
				}

				if(addr[0] != '\0') {
					if(name[0] == '\0')
						snprintf(name, sizeof(name), "%s", addr);
					addr[0] = '\0';
				}

				snprintf(addr, sizeof(addr), "%.*s", p - pStr, pStr);

				p++;
				pStr = p;
				cur = name + strlen(name);
				curlen = strlen(name);
				break;

			case '\'':
			case '"':
				pStr++;
				*cur++ = c;
				curlen++;

				if(strchr(pStr, c) == NULL)
					break;

				p = pStr;
				while(*p && (*p != c) && (curlen < ABUF_LEN)) {
					*cur++ = *p;
					curlen++;
					if(*p == '\\') {
						p++;
						*cur++ = *p;
						curlen++;
					}
					p++;
				}

				if(*p == c) {
					*cur++ = c;
					curlen++;
					*cur = '\0';
					pStr = ++p;
				} else
					pStr = p;
				break;

			case ',':
			case '\0':
				if(c == ',')
					pStr++;

				addrlist.push_back(
					MailAddress(rem_tr_space(addr),
						rem_tr_spacequotes(name),
						rem_tr_space(comment)));

				if(c == '\0' || (flags & MAILADDR_GET_FIRST) != 0)
					return addrlist;

				addr[0] = name[0] = comment[0] = '\0';
				cur = addr;
				curlen = 0;
				break;
		}
	}
	return addrlist;
}
