/* sieved.c -- bytecode decompiler
 * Jen Smith
 *
 * Copyright (c) 1994-2008 Carnegie Mellon University.  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 "Carnegie Mellon University" must not be used to
 *    endorse or promote products derived from this software without
 *    prior written permission. For permission or any legal
 *    details, please contact
 *      Carnegie Mellon University
 *      Center for Technology Transfer and Enterprise Creation
 *      4615 Forbes Avenue
 *      Suite 302
 *      Pittsburgh, PA  15213
 *      (412) 268-7393, fax: (412) 268-7395
 *      innovation@andrew.cmu.edu
 *
 * 4. Redistributions of any form whatsoever must retain the following
 *    acknowledgment:
 *    "This product includes software developed by Computing Services
 *     at Carnegie Mellon University (http://www.cmu.edu/computing/)."
 *
 * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
 * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
 * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF 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.
 *
 * $Id: sieved.c,v 1.10 2010/01/06 17:01:59 murch Exp $
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "sieve_interface.h"

#include "bytecode.h"
#include "script.h"

#include "xmalloc.h"
#include <sys/types.h> 
#include <sys/stat.h>
#include <fcntl.h> 
#include <unistd.h> 
#include <netinet/in.h>

#include <string.h>

#include "map.h"

/* config.c stuff */
const int config_need_data = 0;

static void dump2(bytecode_input_t *d, int len);
static int dump2_test(bytecode_input_t * d, int i);
 
/* from bc_eval.c */
int unwrap_string(bytecode_input_t *bc, int pos, const char **str, int *len);

/*this is called by xmalloc*/
void fatal(const char *s, int code)
{  
    fprintf(stderr, "Fatal error: %s (%d)\r\n", s, code);
                           
    exit(1);
}

static int load(int fd, bytecode_input_t ** d)
{  
    const char * data=NULL;
    struct stat sbuf;
    unsigned long len=0;
    
    if (fstat(fd, &sbuf) == -1) {
	fprintf(stderr, "IOERROR: fstating sieve script: %m");
	return SIEVE_FAIL;
    }
    
    /*this reads in data and length from file*/
    map_refresh(fd, 1, &(data), &len, sbuf.st_size,
		"sievescript", "");
    *d=(bytecode_input_t *)data;
    
    printf("\n");
    
    return (len/sizeof(int));
}


int main(int argc, char * argv[])
{
    bytecode_input_t *bc = NULL;
    int script_fd;
    
    unsigned long len;
    
    if (argc!=2) {
	 fprintf(stderr, "usage:\n %s script\n", argv[0]);
	 exit(1);
    }

    /*get script*/
    script_fd = open(argv[1], O_RDONLY);
    if (script_fd == -1) 
    {
	fprintf(stderr, "can not open script '%s'\n", argv[1]);
	exit(1);
    }
    
    len=load(script_fd,&bc);
    close(script_fd);
    
    if (bc) {
	dump2(bc, len );
	exit(0);
    } else {
	exit(1);
    }
}

static int write_list(int list_len, int i, bytecode_input_t * d)
{
    int x;
    i++;
    for (x=0; x<list_len; x++)
    {
	const char *data;
	int len;
	
	i = unwrap_string(d, i, &data, &len);
	
	printf("{%d}%s\n", len, data);
    }
    return i;
}

static int printComparison(bytecode_input_t *d ,int i)
{
    printf("Comparison: ");
    switch(ntohl(d[i].value))
    {
    case B_IS: printf("Is"); break;
    case B_CONTAINS:printf("Contains"); break;
    case B_MATCHES: printf("Matches"); break;
    case B_REGEX: printf("Regex"); break;
    case B_COUNT:
	printf("Count");
	
	switch(ntohl(d[i+1].value))
	{
	case B_GT: printf(" greater than "); break;   
	case B_GE: printf(" greater than or equal "); break;
	case B_LT: printf(" less than "); break;
	case B_LE: printf(" less than or equal "); break;
	case B_NE: printf(" not equal "); break;
	case B_EQ: printf(" equal "); break;
	}

	break;
    case B_VALUE:
	printf("Value");
	
	switch(ntohl(d[i+1].value))
	{
	case B_GT: printf(" greater than "); break;   
	case B_GE: printf(" greater than or equal ");break;
	case B_LT: printf(" less than ");    break;
	case B_LE: printf(" less than or equal ");break;
	case B_NE: printf(" not equal ");    break;
	case B_EQ: printf(" equal ");break;
	}
	
	break;
    default:
	exit(1);
    }

    switch (ntohl(d[i+2].value))
    {
    case B_ASCIICASEMAP: printf("   (ascii-casemap) "); break;
    case B_OCTET: printf("    (octet) "); break;
    case B_ASCIINUMERIC:  printf("   (ascii-numeric) "); break;
    default: exit(1);
    }
    
    printf("\n");
    return i+3;
}


static int dump2_test(bytecode_input_t * d, int i)
{
    int l,x;
    switch(ntohl(d[i].value)) {
    case BC_FALSE:
	printf("false");
	i++;
	break;
    case BC_TRUE:
	printf("true");
	i++;
	break;
    case BC_NOT:/*2*/
	/* XXX 
	   there is a value being skipped in the second pass...
	   no idea what it does, but it isn't carried to here...
	   see bytecodee.c */
	printf(" not(");
	i=dump2_test(d, i+1);
	printf(")\n");
	break;
    case BC_EXISTS:
	printf("exists");
	i=write_list(ntohl(d[i+1].len), i+2, d);
	break;
    case BC_SIZE:
	printf("size");
	if (ntohl(d[i+1].value)==B_OVER) {
	    /* over */
	    printf("over %d", ntohl(d[i+2].value));
	} else {
	    /* under */
	    printf("under %d", ntohl(d[i+2].value));
	}
	i+=3;
	break;
    case BC_ANYOF:/*5*/
	printf("any of \n(");
	l=ntohl(d[i+1].len);
	i+=3;
	
	for (x=0; x<l; x++)
	{
	    i=dump2_test(d,i);
	    if((x+1)<l)
		printf(" OR ");
	}
	
	printf(")\n");	 
	break;
    case BC_ALLOF:/*6*/
	printf("all of \n(");
	l=ntohl(d[i+1].len);
	i+=3;
	
	for (x=0; x<l; x++)
	{
	    i=dump2_test(d,i);
	    if((x+1)<l)
		printf(" AND ");
	}
	
	printf(")\n");
	break;
    case BC_ADDRESS:/*7*/
	printf("Address [");
	i=printComparison(d, i+1);
	printf("               type: ");
	switch(ntohl(d[i++].value))
	{
	case B_ALL: printf("all"); break;
	case B_LOCALPART:printf("localpart"); break;
	case B_DOMAIN:printf("domain"); break;
	case B_USER:printf("user"); break;
	case B_DETAIL:printf("detail"); break;
	}
	printf("              Headers:");
	i=write_list(ntohl(d[i].len), i+1, d);
	printf("              Data:");
	i=write_list(ntohl(d[i].len), i+1, d);
	printf("             ]\n");
	break;
    case BC_ENVELOPE:/*8*/
	printf("Envelope [");
	i=printComparison(d, i+1);
	printf("                type: ");
	switch(ntohl(d[i++].value))
	{
	case B_ALL: printf("all"); break;
	case B_LOCALPART:printf("localpart"); break;
	case B_DOMAIN:printf("domain"); break;
	case B_USER:printf("user"); break;
	case B_DETAIL:printf("detail"); break;
	}
	printf("              Headers:");
	i=write_list(ntohl(d[i].len), i+1, d);
	printf("              Data:");
	i=write_list(ntohl(d[i].len), i+1, d);
	printf("             ]\n");
	break;
    case BC_HEADER:/*9*/
	printf("Header [");
	i= printComparison(d, i+1);
	printf("              Headers: ");
	i=write_list(ntohl(d[i].len), i+1, d);
	printf("              Data: ");
	i=write_list(ntohl(d[i].len), i+1, d);
	printf("             ]\n");
	break;
    case BC_BODY:/*10*/
	printf("Body [");
	i=printComparison(d, i+1);
	printf("              Transform: ");
	switch(ntohl(d[i++].value))
	{
	case B_RAW: printf("raw"); break;
	case B_TEXT:printf("text"); break;
	case B_CONTENT:printf("content"); break;
	}
	printf("\tOffset: %d\n", ntohl(d[i++].value));
	printf("              Content-Types:");
	i=write_list(ntohl(d[i].len), i+1, d);
	printf("              Data:");
	i=write_list(ntohl(d[i].len), i+1, d);
	printf("             ]\n");
	break;
    default:
	printf("WERT %d ", ntohl(d[i].value));
    }   
    return i;
}

static void dump2(bytecode_input_t *d, int bc_len)
{
    int i;
    int version;
    const char *data;
    int len;

    if (!d) return;
    
    if (memcmp(d, BYTECODE_MAGIC, BYTECODE_MAGIC_LEN)) {
	printf("not a bytecode file [magic number test failed]\n");
	return;
    }

    i = BYTECODE_MAGIC_LEN / sizeof(bytecode_input_t);

    version = ntohl(d[i].op);
    printf("Sievecode version %d\n", version);
    
    for(i++; i<bc_len;) 
    {
	int copy = 0;

	printf("%d: ",i);

	switch(ntohl(d[i++].op)) {
	    
	case B_STOP:/*0*/
	    printf("STOP\n");
	    break;
	    
	case B_KEEP:/*1*/
	    printf("KEEP\n");
	    break;
	    
	case B_DISCARD:/*2*/
	    printf("DISCARD\n");
	    break;
	    
	case B_REJECT:/*3*/
	    i = unwrap_string(d, i, &data, &len);
	    printf("REJECT {%d}%s\n", len, data);
	    break;

	case B_FILEINTO: /*19*/
	    copy = ntohl(d[i++].value);
	    /* fall through */
	case B_FILEINTO_ORIG: /*4*/
	    i = unwrap_string(d, i, &data, &len);
	    printf("FILEINTO COPY(%d) FOLDER({%d}%s)\n",copy,len,data);
	    break;

	case B_REDIRECT: /*20*/
	    copy = ntohl(d[i++].value);
	    /* fall through */
	case B_REDIRECT_ORIG: /*5*/
	    i = unwrap_string(d, i, &data, &len);
	    printf("REDIRECT COPY(%d) ADDRESS({%d}%s)\n",copy,len,data);
	    break;
	     
	case B_IF:/*6*/
	    printf("IF (ends at %d)", ntohl(d[i].value));

            /* there is no short circuiting involved here*/
	    i = dump2_test(d,i+1);
	    printf("\n");

	    break;

	case B_MARK:/*7*/
	    printf("MARK\n");
	    break;

	case B_UNMARK:/*8*/
	    printf("UNMARK\n");
	    break;

	case B_ADDFLAG: /*9*/
	    printf("ADDFLAG  {%d}\n",ntohl(d[i].len));
	    i=write_list(ntohl(d[i].len),i+1,d);
	    break;

	case B_SETFLAG: /*10*/
	    printf("SETFLAG  {%d}\n",ntohl(d[i].len));
	    i=write_list(ntohl(d[i].len),i+1,d);
	    break;
	    
	case B_REMOVEFLAG: /*11*/
	    printf("REMOVEFLAG  {%d}\n",ntohl(d[i].len));
	    i=write_list(ntohl(d[i].len),i+1,d);
	    break;
	    
	case B_DENOTIFY:/*12*/
	    printf("DENOTIFY\n");
	    printf("            PRIORITY(%d) Comparison type %d (relat %d)\n",
		   ntohl(d[i].value), ntohl(d[i+1].value), ntohl(d[i+2].value));
	    i+=3;

	    i = unwrap_string(d, i+1, &data, &len);
	    
	    printf("           ({%d}%s)\n", len, (!data ? "[nil]" : data));
	    break;
	    
	case B_NOTIFY: /*13*/
	    i = unwrap_string(d, i, &data, &len);

	    printf("NOTIFY METHOD({%d}%s)\n",len,data);

	    i = unwrap_string(d, i, &data, &len);

	    printf("            ID({%d}%s) OPTIONS ", len,
		   (!data ? "[nil]" : data));

	    i=write_list(ntohl(d[i].len),i+1,d);
	    
	    printf("            PRIORITY(%d)\n", ntohl(d[i].value));
      	    i++;
		  
	    i = unwrap_string(d, i, &data, &len);

	    printf("            MESSAGE({%d}%s)\n", len, data);

	    break;

	case B_VACATION:/*14*/
	    printf("VACATION\n");
	    /*add address list here!*/
	    i=write_list(ntohl(d[i].len),i+1,d);

	    i = unwrap_string(d, i, &data, &len);
	  
	    printf("%d SUBJ({%d}%s) \n",i, len, (!data ? "[nil]" : data));
	    
	    i = unwrap_string(d, i, &data, &len);

	    printf("%d MESG({%d}%s) \n", i, len, (!data ? "[nil]" : data));

	    printf("DAYS(%d) MIME(%d)\n", ntohl(d[i].value), ntohl(d[i+1].value));
	    i+=2;

	    if (version >= 0x05) {
		i = unwrap_string(d, i, &data, &len);

		printf("%d FROM({%d}%s) \n",i, len, (!data ? "[nil]" : data));

		i = unwrap_string(d, i, &data, &len);

		printf("%d HANDLE({%d}%s) \n",i, len, (!data ? "[nil]" : data));
	    }

	    break;
	case B_NULL:/*15*/
	    printf("NULL\n");
	    break;
	case B_JUMP:/*16*/
	    printf("JUMP %d\n", ntohl(d[i].jump));
	    i+=1;
	    break;

	case B_INCLUDE:/*17*/
	    printf("INCLUDE ");
	    switch (ntohl(d[i].value)) {
	    case B_PERSONAL: printf("Personal"); break;
	    case B_GLOBAL: printf("Global"); break;
	    }
	    i = unwrap_string(d, i+1, &data, &len);
	    printf(" {%d}%s\n", len, data);
	    break;

	case B_RETURN:/*18*/
	    printf("RETURN\n");
	    break;
	    
	default:
	    printf("%d (NOT AN OP)\n",ntohl(d[i-1].op));
	    exit(1);
	}
    }
    printf("full len is: %d\n", bc_len);
}


