/* $Id$ */

/*
 * Copyright (c) 2006 Nicholas Marriott <nicholas.marriott@gmail.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

#include <sys/types.h>

#include <fnmatch.h>

#include "fdm.h"
#include "match.h"

int	match_attachment_match(struct mail_ctx *, struct expritem *);
void	match_attachment_desc(struct expritem *, char *, size_t);

struct match match_attachment = {
	"attachment",
	match_attachment_match,
	match_attachment_desc
};

int
match_attachment_match(struct mail_ctx *mctx, struct expritem *ei)
{
	struct match_attachment_data	*data = ei->data;
	struct account			*a = mctx->account;
	struct mail			*m = mctx->mail;
	struct attach			*at;
	size_t				 size;
	u_int				 n;
	char				*value = NULL;

	if (!m->attach_built) {
		/* Fill attachments. */
		m->attach = attach_build(m);
		if (m->attach != NULL)
			attach_log(m->attach, "%s: attachment", a->name);
		else
			log_debug3("%s: no attachments", a->name);
		m->attach_built = 1;
	}

	if (data->op == ATTACHOP_COUNT || data->op == ATTACHOP_TOTALSIZE) {
		size = 0;
		n = 0;
		at = m->attach;
		while (at != NULL) {
			size += at->size;
			n++;

			at = attach_visit(at, NULL);
		}
		switch (data->op) {
		case ATTACHOP_COUNT:
			switch (data->cmp) {
			case CMP_EQ:
				if (n == data->value.num)
					return (MATCH_TRUE);
				return (MATCH_FALSE);
			case CMP_NE:
				if (n != data->value.num)
					return (MATCH_TRUE);
				return (MATCH_FALSE);
			case CMP_LT:
				if (n < data->value.num)
					return (MATCH_TRUE);
				return (MATCH_FALSE);
			case CMP_GT:
				if (n > data->value.num)
					return (MATCH_TRUE);
				return (MATCH_FALSE);
			default:
				return (MATCH_ERROR);
			}
		case ATTACHOP_TOTALSIZE:
			switch (data->cmp) {
			case CMP_LT:
				if (size < data->value.size)
					return (MATCH_TRUE);
				return (MATCH_FALSE);
			case CMP_GT:
				if (size > data->value.size)
					return (MATCH_TRUE);
				return (MATCH_FALSE);
			default:
				return (MATCH_ERROR);
			}
		default:
			return (MATCH_ERROR);
		}
	}

	/* If no attachments, none of the following conditions are true. */
	if (m->attach == NULL)
		return (MATCH_FALSE);

	/* For any type or name matches, construct the value. */
	if (data->op == ATTACHOP_ANYTYPE || data->op == ATTACHOP_ANYNAME)
		value = replacestr(&data->value.str, m->tags, m, &m->rml);

	at = m->attach;
	while (at != NULL) {
		switch (data->op) {
		case ATTACHOP_ANYSIZE:
			switch (data->cmp) {
			case CMP_LT:
				if (at->size < data->value.size)
					return (MATCH_TRUE);
				break;
			case CMP_GT:
				if (at->size > data->value.size)
					return (MATCH_TRUE);
				break;
			default:
				return (MATCH_ERROR);
			}
			break;
		case ATTACHOP_ANYTYPE:
			if (at->type == NULL)
				break;

			if (fnmatch(value, at->type, FNM_CASEFOLD) == 0) {
				xfree(value);
				return (MATCH_TRUE);
			}
			break;
		case ATTACHOP_ANYNAME:
			if (at->name == NULL)
				break;

			if (fnmatch(value, at->name, FNM_CASEFOLD) == 0) {
				xfree(value);
				return (MATCH_TRUE);
			}
			break;
		default:
			return (MATCH_ERROR);
		}

		at = attach_visit(at, NULL);
	}

	if (value != NULL)
		xfree(value);
	return (MATCH_FALSE);
}

void
match_attachment_desc(struct expritem *ei, char *buf, size_t len)
{
	struct match_attachment_data	*data = ei->data;
	const char			*cmp = "";

	if (data->cmp == CMP_LT)
		cmp = "<";
	else if (data->cmp == CMP_GT)
		cmp = ">";
	else if (data->cmp == CMP_EQ)
		cmp = "==";
	else if (data->cmp == CMP_NE)
		cmp = "!=";

	switch (data->op) {
	case ATTACHOP_COUNT:
		xsnprintf(buf, len,
		    "attachment count %s %lld", cmp, data->value.num);
		break;
	case ATTACHOP_TOTALSIZE:
		xsnprintf(buf, len,
		    "attachment total-size %s %lld", cmp, data->value.num);
		break;
	case ATTACHOP_ANYSIZE:
		xsnprintf(buf, len,
		    "attachment any-size %s %lld", cmp, data->value.num);
		break;
	case ATTACHOP_ANYTYPE:
		xsnprintf(buf, len,
		    "attachment any-type \"%s\"", data->value.str.str);
		break;
	case ATTACHOP_ANYNAME:
		xsnprintf(buf, len,
		    "attachment any-name \"%s\"", data->value.str.str);
		break;
	default:
		if (len > 0)
			*buf = '\0';
		break;
	}
}
