/* $Id: refile.c,v 1.34 2015/09/29 13:17:42 onoe Exp $ */

/*-
 * Copyright (c) 1998-2001 Atsushi Onoe
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/types.h>

#include <ctype.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "cue.h"


static int
refile_parse(struct state *state, struct msginfo *msg, char *buf, int guess)
{
	int i, j;
	char *p, *q;
	struct folder *fl;

	for (i = 0, p = buf; i < MAX_REFILE && p != NULL; i++, p = q) {
		if ((q = strchr(p, ',')))
			*q = '\0';
		fl = folder_open(p, 0);
		if (q)
			*q++ = ',';
		if (fl == NULL) {
			if (guess)
				return 0;
			if (!edit_yn(state, "No such folder: %s: Create ? ",
			    p)) {
				strlcpy(state->status, "Quit",
				    sizeof(state->status));
				return 0;
			}
			if (builddir(p + 1, 0777) < 0) {
				snprintf(state->status, sizeof(state->status),
				    "Create %s: %s", p, strerror(errno));
				return 0;
			}
			fl = folder_open(p, 0);
			if (fl == NULL) {
				snprintf(state->status, sizeof(state->status),
				    "Failed to create folder: %s", p);
				return 0;
			}
		}
		msg->refile[i] = fl->name.ptr;
	}
	for (j = i; j < MAX_REFILE; j++)
		msg->refile[j] = NULL;
	return i;
}

int
refile_guess(struct state *state, struct msginfo *msg)
{
	cbuf_t *cbh, *cbv, *cbf, cbtmp;
	struct refile_list *rl;
	struct header *hdr;
	int i, n, neg;
	const char *p, *ep;
	u_char *u;
	int tocc;
	char buf[LINE_WIDTH];

	conf_update(state);
	if (state->message == NULL)
		return 0;
	n = state->message->hdrs;

	for (rl = state->config.refile_list; rl; rl = rl->next) {
  and:
		cbh = &rl->hdr;
		if (*CP(cbh) == '!') {
			cbtmp = *cbh;
			cbh = &cbtmp;
			cbh->ptr++;
			cbh->len--;
			neg = 1;
		} else
			neg = 0;
		tocc = CMATCH("To/Cc:", cbh);

		for (i = 0, hdr = state->message->hdr; i < n; i++, hdr++) {
			u = (u_char *)CP(&hdr->buf);
			if (tocc) {
				/* optimize for CSUBMATCH */
				if (u[2] != ':')
					continue;
				if (tolower(*u) == 't' && tolower(u[1]) == 'o')
					goto search;
				if (tolower(*u) == 'c' && tolower(u[1]) == 'c')
					goto search;
				continue;
			}
			if (CL(&hdr->buf) < CL(cbh) ||
			    tolower(*u) != tolower(*(u_char *)CP(cbh)) ||
			    strncasecmp((char *)u, CP(cbh), CL(cbh)) != 0)
				continue;
  search:
			cbv = &rl->pat;
			p = CP(&hdr->val);
			ep = CE(&hdr->val) - CL(cbv);
			for ( ; p <= ep; p++) {
				if (*p == *CP(cbv)
				&&  strncasecmp(p, CP(cbv), CL(cbv)) == 0)
					break;
			}
			if ((p <= ep && !neg) || (p > ep && neg))
				goto match;
		}
		while (CP(&rl->fol) == NULL) {
			rl = rl->next;
			if (rl == NULL)
				return 0;	/* config error */
		}
	}
	return 0;

  match:
	cbf = &rl->fol;
	if (CP(cbf) == NULL
	&&  (rl = rl->next) != NULL)
		goto and;
	strlcpy(buf, CP(cbf), sizeof(buf));
	return refile_parse(state, msg, buf, 1);
}

int
refile_folder(struct state *state)
{
	char buf[LINE_WIDTH];
	struct msginfo *msg;
	int guess = 0;
	int i;

	message_open(state, 1);
	disp_msgmode(state, 1);
	disp_update(state);

	msg = &state->folder->msg[state->folder->pos];
	if (msg->mark != MARK_REFILE)
		guess = refile_guess(state, msg);
	strlcpy(state->status, "Folder name", sizeof(state->status));
	if (guess) {
		for (i = 0; i < guess; i++) {
			strlcat(state->status, i == 0 ? " (" : ",",
			    sizeof(state->status));
			strlcat(state->status, msg->refile[i],
			    sizeof(state->status));
		}
		strlcat(state->status, ")", sizeof(state->status));
	}
	strlcat(state->status, ": ", sizeof(state->status));
	if (msg->mark != MARK_REFILE) {
		buf[0] = '+';
		buf[1] = '\0';
	} else {
		buf[0] = '\0';
		for (i = 0; i < MAX_REFILE; i++) {
			if (msg->refile[i] == NULL)
				break;
			if (i)
				strlcat(buf, ",", sizeof(buf));
			strlcat(buf, msg->refile[i], sizeof(buf));
		}
	}
	if (edit_stline(state, buf, sizeof(buf), folder_completion) == NULL) {
		strlcpy(state->status, "Quit", sizeof(state->status));
		return 0;
	}
	if (buf[0] != '+' || buf[1] != '\0') {
		if (buf[0] != '+') {
			snprintf(state->status, sizeof(state->status),
			    "No such folder: %s", buf);
			return 0;
		}
		guess = refile_parse(state, msg, buf, 0);
	}
	if (!guess) {
		strlcpy(state->status, "Quit", sizeof(state->status));
		return 0;
	}
	for (i = 0; i < MAX_REFILE; i++)
		state->refile[i] = msg->refile[i];
	if (msg->mark == 0)
		state->folder->nmark++;
	msg->mark = MARK_REFILE;
	state->status[0] = '\0';
	return guess;
}

int
refile_again(struct state *state, struct msginfo *msg)
{
	int i;

	if (msg->mark == MARK_DELETE) {
		strlcpy(state->status, "Already marked as delete",
		    sizeof(state->status));
		return 0;
	}
	if (msg->mark == MARK_REFILE) {
		strlcpy(state->status, "Already marked as refile",
		    sizeof(state->status));
		return 0;
	}
	if (state->refile[0] == NULL) {
		strlcpy(state->status, "Not refiled yet",
		    sizeof(state->status));
		return 0;
	}

	for (i = 0; i < MAX_REFILE; i++) {
		msg->refile[i] = state->refile[i];
		if (msg->mark == 0)
			state->folder->nmark++;
		msg->mark = MARK_REFILE;
	}
	state->status[0] = '\0';
	return i;
}
