/* $Id: sortsieve.c 1550 2005-01-07 12:23:02Z paul $

 Copyright (C) 1999-2004 Aaron Stone aaron at serendipity dot cx

 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., 675 Mass Ave, Cambridge, MA 02139, USA.

 *
 * Functions for running user defined sorting rules
 * on a message in the temporary store, usually
 * just delivering the message to the user's INBOX
 * ...unless they have fancy rules defined, that is :-)
 * 
 */


#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <time.h>
#include <ctype.h>
#include "db.h"
#include "auth.h"
#include "debug.h"
#include "list.h"
#include "dbmail.h"
#include "debug.h"
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include "dbmd5.h"
#include "misc.h"

#include "sortsieve.h"
#include "sort.h"
#include <sieve2_interface.h>

extern struct list smtpItems, sysItems;

/* typedef sort_action {
 *   int method,
 *   char *destination,
 *   char *message
 * } sort_action_t;
 * */

/* Pull up the relevant sieve scripts for this
 * user and begin running them against the header
 * and possibly the body of the message.
 *
 * Returns 0 on success, -1 on failure,
 * and +1 on success but with memory leaking.
 * In the +1 case, if called from a daemon
 * such as dbmail-lmtpd, the daemon should
 * finish storing the message and restart.
 * */
int sortsieve_msgsort(u64_t useridnr, char *header, u64_t headersize,
		      u64_t messagesize, struct list *actions)
{
	sieve2_message_t *m;
	sieve2_support_t *p;
	sieve2_script_t *s;
	sieve2_action_t *a;
	sieve2_loader_t scriptloader, msgloader;
	char *scriptname = NULL, *script = NULL, *freestr = NULL;
	int res = 0, ret = 0;

	/* Pass the address of the char *script, and it will come
	 * back magically allocated. Don't forget to free it later! */
	res = db_get_sievescript_active(useridnr, &scriptname);
	if (res < 0) {
		printf("db_get_sievescript_active() returns %d\n", res);
		ret = -1;
		goto no_free;
	}

	printf("Looking up script [%s]\n", scriptname);
	res = db_get_sievescript_byname(useridnr, scriptname, &script);
	if (res < 0) {
		printf("db_get_sievescript_byname() returns %d\n", res);
		ret = -1;
		goto char_scriptname_free;
	}

	res = sieve2_action_alloc(&a);
	if (res != SIEVE2_OK) {
		printf("sieve2_action_alloc() returns %d\n", res);
		ret = -1;
		goto char_script_free;
	}

	res = sieve2_support_alloc(&p);
	if (res != SIEVE2_OK) {
		printf("sieve2_support_alloc() returns %d\n", res);
		ret = -1;
		goto action_free;
	}

	res = sieve2_support_register(p, SIEVE2_ACTION_FILEINTO);
	res = sieve2_support_register(p, SIEVE2_ACTION_REDIRECT);
	res = sieve2_support_register(p, SIEVE2_ACTION_REJECT);
//  res = sieve2_support_register(p, SIEVE2_ACTION_NOTIFY);

	res = sieve2_script_alloc(&s);
	if (res != SIEVE2_OK) {
		printf("sieve2_script_alloc() returns %d\n", res);
		ret = -1;
		goto support_free;
	}

	res = sieve2_support_bind(p, s);
	if (res != SIEVE2_OK) {
		printf("sieve2_support_bind() returns %d\n", res);
		ret = -1;
		goto script_free;
	}

	res = sieve2_script_parse(s, script);
	if (res != SIEVE2_OK) {
		printf("sieve2_script_parse() returns %d: %s\n", res,
		       sieve2_errstr(res, &freestr));
		dm_free(freestr);
		ret = -1;
		goto script_free;
	}

	res = sieve2_message_alloc(&m);
	if (res != SIEVE2_OK) {
		printf("sieve2_message_alloc() returns %d\n", res);
		ret = -1;
		goto script_free;
	}

	res =
	    sieve2_message_register(m, &messagesize, SIEVE2_MESSAGE_SIZE);
	if (res != SIEVE2_OK) {
		printf("sieve2_message_register() returns %d\n", res);
		ret = -1;
		goto message_free;
	}
	res = sieve2_message_register(m, header, SIEVE2_MESSAGE_HEADER);
	if (res != SIEVE2_OK) {
		printf("sieve2_message_register() returns %d\n", res);
		ret = -1;
		goto message_free;
	}

	res = sieve2_script_exec(s, m, a);
	if (res != SIEVE2_OK) {
		printf("sieve2_execute_script() returns %d\n", res);
		ret = -1;
		goto message_free;
	}

	res = sortsieve_unroll_action(a, actions);
	if (res != SIEVE2_OK && res != SIEVE2_DONE) {
		printf("unroll_action() returns %d\n", res);
		ret = -1;
		goto action_free;
	}

      message_free:
	res = sieve2_message_free(m);
	if (res != SIEVE2_OK) {
		printf("sieve2_message_free() returns %d\n", res);
		ret = 1;
	}

      script_free:
	res = sieve2_script_free(s);
	if (res != SIEVE2_OK) {
		printf("sieve2_script_free() returns %d\n", res);
		ret = 1;
	}

      support_free:
	res = sieve2_support_free(p);
	if (res != SIEVE2_OK) {
		printf("sieve2_support_free() returns %d\n", res);
		ret = 1;
	}

      action_free:
	res = sieve2_action_free(a);
	if (res != SIEVE2_OK) {
		printf("sieve2_action_free() returns %d\n", res);
		ret = 1;
	}

	/* Good thing we're not forgetting ;-) */
      char_script_free:
	if (script != NULL)
		dm_free(script);
      char_scriptname_free:
	if (scriptname != NULL)
		dm_free(scriptname);

      no_free:
	return ret;
}

int sortsieve_unroll_action(sieve2_action_t * a, struct list *actions)
{
	int res = SIEVE2_OK;
	int code;
	void *action_context;

	/* Working variables to set up
	 * the struct then nodeadd it */
	sort_action_t *tmpsa = NULL;
	char *tmpdest = NULL;
	char *tmpmsg = NULL;
	int tmpmeth = 0;

	while (res == SIEVE2_OK) {
		if ((tmpsa = malloc(sizeof(sort_action_t))) == NULL)
			break;
		res = sieve2_action_next(&a, &code, &action_context);
		if (res == SIEVE2_DONE) {
			printf("We've reached the end.\n");
			break;
		} else if (res != SIEVE2_OK) {
			printf("Error in action list.\n");
			break;
		}
		printf("Action code is: %d\n", code);

		switch (code) {
		case SIEVE2_ACTION_REDIRECT:
			{
				sieve2_redirect_context_t *context =
				    (sieve2_redirect_context_t *)
				    action_context;
				printf("Action is REDIRECT: ");
				printf("Destination is %s\n",
				       context->addr);
				tmpmeth = SA_REDIRECT;
				tmpdest = dm_strdup(context->addr);
				break;
			}
		case SIEVE2_ACTION_REJECT:
			{
				sieve2_reject_context_t *context =
				    (sieve2_reject_context_t *)
				    action_context;
				printf("Action is REJECT: ");
				printf("Message is %s\n", context->msg);
				tmpmeth = SA_REJECT;
				tmpmsg = dm_strdup(context->msg);
				break;
			}
		case SIEVE2_ACTION_DISCARD:
			printf("Action is DISCARD\n");
			tmpmeth = SA_DISCARD;
			break;
		case SIEVE2_ACTION_FILEINTO:
			{
				sieve2_fileinto_context_t *context =
				    (sieve2_fileinto_context_t *)
				    action_context;
				printf("Action is FILEINTO: ");
				printf("Destination is %s\n",
				       context->mailbox);
				tmpmeth = SA_FILEINTO;
				tmpdest = dm_strdup(context->mailbox);
				break;
			}
		case SIEVE2_ACTION_NOTIFY:
			{
				sieve2_notify_context_t *context =
				    (sieve2_notify_context_t *)
				    action_context;
				printf("Action is NOTIFY: \n");
				// FIXME: Prefer to have a function for this?
				while (context != NULL) {
					printf("  ID \"%s\" is %s\n",
					       context->id,
					       (context->
						isactive ? "ACTIVE" :
						"INACTIVE"));
					printf("    Method is %s\n",
					       context->method);
					printf("    Priority is %s\n",
					       context->priority);
					printf("    Message is %s\n",
					       context->message);
					if (context->options != NULL) {
						size_t opt = 0;
						while (context->
						       options[opt] !=
						       NULL) {
							printf
							    ("    Options are %s\n",
							     context->
							     options[opt]);
							opt++;
						}
					}
					context = context->next;
				}
				break;
			}
		case SIEVE2_ACTION_KEEP:
			printf("Action is KEEP\n");
			break;
		default:
			printf("Unrecognized action code: %d\n", code);
			break;
		}		/* case */

		tmpsa->method = tmpmeth;
		tmpsa->destination = tmpdest;
		tmpsa->message = tmpmsg;

		list_nodeadd(actions, tmpsa, sizeof(sort_action_t));

		dm_free(tmpsa);
		tmpsa = NULL;

	}			/* while */

	if (tmpsa != NULL)
		dm_free(tmpsa);

	return res;
}

/* Return 0 on script OK, 1 on script error. */
int sortsieve_script_validate(char *script, char **errmsg)
{
	if (sieve2_validate(t, s, p, e) == SIEVE2_OK) {
		*errmsg = NULL;
		return 0;
	} else {
		*errmsg = "Script error...";
		return 1;
	}
}


syntax highlighted by Code2HTML, v. 0.9.1