#include "args.h"
#include "config.h"

/*
 * Copyright (c) 1986, 2014 by The Trustees of Columbia University in
 * the City of New York.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  + Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 *  + 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.
 *
 *  + Neither the name of Columbia University nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 */

#include "mm.h"
#include "parse.h"
#include "cmds.h"
#include "message.h"

extern msgvec *cf;

static keywrd header_keys[] = {
    { "to", 0, TO },
    { "cc", 0, CC },
    { "bcc", 0, BCC },
    { "fcc", 0, FCC },
    { "from", 0, FROM },
    { "date", 0, DATE },
    { "subject", 0, SUBJECT },
    { "reply-to", 0, REPLY_TO },
    { "in-reply-to", 0, IN_REPLY_TO },
    { "resent-to", 0, RESENT_TO },
    { "resent-date", 0, RESENT_DATE },
    { "resent-from", 0, RESENT_FROM },
    { "sender", 0, SENDER },
    { "references", 0, REFERENCES },
    { "comments", 0, COMMENTS },
    { "message-id", 0, MESSAGE_ID },
    { "keywords", 0, KEYWORDS },
    { "encrypted", 0, ENCRYPTED },
    { "received", 0, RECEIVED },
    { "mime-version", 0, MIME_VERSION },
    { "content-type", 0, CONTENT_TYPE },
    { "content-transfer-encoding", 0, TRANSFER_ENCODING },
};

static keytab header_tab = { (sizeof(header_keys)/ sizeof(keywrd)),
				 header_keys };

static brktab hdrbrk = {		/* break table for field name */
    {					/* letters only in first position */
      0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x20,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
    },
    {					/* letters, digits and hyphens here */
      0xff, 0xff, 0xff, 0xff, 0xb2, 0xe8, 0x00, 0x3f,
      0x80, 0x00, 0x00, 0x16, 0x80, 0x00, 0x00, 0x1f
    }					/* (also +%_.!) */
};

static fdb header_fdb = { _CMKEY, 0, nil, (pdat) &header_tab,"header field, ",
			  NULL, &hdrbrk};
static fdb other = { _CMFLD, FLD_EMPTY, NULL, NULL, NULL, NULL, &hdrbrk };
static fdb colon = { _CMTOK, 0, nil, (pdat) ":" };
static fdb text = { _CMTXT };

#define pv pv_		/* avoid warnings about shadowed global */

/*
 * take a 'text' mail message, and convert it to internal format.
 * parse out all of the headers, etc.
 */
static mail_msg mesg;

#define used used_		/* avoid warnings about shadowed global */

mail_msg *
#if HAVE_STDC
parse_msg(message *msg)
#else /* K&R style */
parse_msg(msg)
message *msg;
#endif /* HAVE_STDC */
{
    char *s = msg->text;
    char *s1;
    int next;
    int retval;
    pval pv;
    fdb *used;
    int parselen;
    headers *h;
    int len;
    keylist kl, k;

    bzero (&mesg, sizeof (mail_msg));
    while((s1 = next_header(&s,&next), s1)) {
	int l;
	char *s1save;
	stripspaces(s1);
	l = strlen(s1);
	s1 = (char*)realloc(s1, l + 2);
	s1save = s1;
	s1[l] = '\n';
	s1[l+1] = '\0';
	retval = match(s1, (int)strlen(s1),
		       fdbchn(&header_fdb, &other,
			      NULL), &pv, &used, &parselen);

	if (retval == CMxOK) {
	    char *name = (char *)malloc(parselen+1);
	    strncpy(name,s1,parselen);
	    name[parselen] = '\0';
	    s1 += parselen;
	    if (used == &header_fdb) {
		int which = pv._pvkey;
		if (match(s1, (int)strlen(s1), fdbchn(&colon, nil),
			  &pv, &used, &parselen) == CMxOK) {
		    h = new_header(which, name, HEAD_KNOWN, &mesg);
		    free(name);
		    s1 += parselen;
		}
		else {
		    free(name);
		    free(s1save); /* cleanup s1 */
		    break;
		}
		switch (which) {
		  case TO:
		  case CC:
		  case BCC:
		  case RESENT_TO:
		    h->address = (addresslist *) malloc(sizeof(addresslist));
		    h->address->first = h->address->last = NULL;
		    match_addresses(h->address,
				    (const char**)&s1,
				    (int)strlen(s1));
		    break;
		  case KEYWORDS:
		    h->keys = match_keylist(s1);
		    if (!(mode& MM_SEND)) {
			k = h->keys;
			while(k && *k) {
			    cf->keywords = add_keyword(*k, cf->keywords);
			    k++;
			}
		    }
		    break;
		  case FCC:
		    k = kl = match_keylist(s1);
		    h->address = (addresslist *) malloc(sizeof(addresslist));
		    h->address->first = h->address->last = NULL;
		    while (k && *k) {
		      add_addresslist(h->address, *k, ADR_FILE);
		      k++;
		    }
		    free_keylist(kl);
		    break;
		  case FROM:
		  case DATE:
		  case SUBJECT:
		  case REPLY_TO:
		  case IN_REPLY_TO:
		  case RESENT_DATE:
		  case RESENT_FROM:
		  case SENDER:
		  case REFERENCES:
		  case COMMENTS:
		  case MESSAGE_ID:
		  case ENCRYPTED:
		  case RECEIVED:
		  case MIME_VERSION:
		  case CONTENT_TYPE:
		  case TRANSFER_ENCODING:
		    skip_spaces(&s1);
		    retval = match(s1, (int)strlen(s1),
				   fdbchn(&text,nil), &pv,
				   &used, &parselen);
		    h->string = (char *)malloc(parselen+1);
		    strncpy(h->string,s1,parselen);
		    s1 += parselen;
		    /* zero-terminate and trim trailing whitespace */
		    do { h->string[parselen] = '\0'; }
		    while (--parselen >= 0 && isspace (h->string[parselen]));
		    break;
		}
	    }
	    else {
		if (match(s1, (int)strlen(s1), fdbchn(&colon, nil),
			  &pv, &used, &parselen) == CMxOK) {
		    h = new_header(USER_HEADERS, name, HEAD_UNKNOWN,
				   &mesg);
		    free(name);
		    s1 += parselen;
		    skip_spaces(&s1);
		    retval = match(s1, (int)strlen(s1), fdbchn(&text,nil), &pv,
				   &used, &parselen);
		    h->string = (char *)malloc(parselen+1);
		    strncpy(h->string,s1,parselen);
		    s1 += parselen;
		    /* zero-terminate and trim trailing whitespace */
		    do { h->string[parselen] = '\0'; }
		    while (--parselen >= 0 && isspace (h->string[parselen]));
		}
		else {
		    /*
		     * this line is not a valid header, so back up to previous
		     * newline and treat that as the start of the message body.
		     */
		    char *cp = s - 2;	/* skip over \n */

		    while (cp > msg->text && *cp != '\n')
			--cp;
		    if (*cp == '\n')
			s = cp;
		    free(name);
		    free(s1save);
		    break;
		}
	    }
	}
	else {
	    /* see code above */
	    char *cp = s - 2;

	    while (cp > msg->text && *cp != '\n')
		--cp;
	    if (*cp == '\n')
		s = cp;
	    free(s1save);
	    break;
	}
	free(s1save);
	if (!next) break;
    }
    if (*s == '\n')			/* if leading newline... */
	s++;				/* ...separator, not body */
    len = msg->size - (s - msg->text);
    mesg.body = (char*)malloc(len+1);
    strncpy(mesg.body,s,len);
    mesg.body[len] = '\0';
    return(&mesg);
}
