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

/*-
 * Copyright (c) 2003 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.
 */
#ifdef USE_UUDES

#include <sys/types.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>

#include "openssl/evp.h"

#include "cue.h"

extern char ruue[];

/* support for uuencoded DES encrypted message */

int
uudes_decrypt(struct state *state)
{
	struct filedb *fdb;
	int i, j, cnt;
	char c;
	int code;
	const char *p, *s, *ep;
	u_char dbuf[8], key[8 + 1];
	struct abuf abuf;
	ccbuf_t *cb, from, ebuf;
	ccbuf_t *ccb;
	EVP_CIPHER_CTX cipher;

	message_open(state, 0);
	fdb = state->message;
	message_parseall(fdb);
	ccb = &fdb->hdr_val[HT_FROM];
	if (CP(ccb) == NULL) {
		from.ptr = "";
		from.len = 0;
	} else {
		(void)message_parse_addr(CP(ccb), CE(ccb), &from);
	}

	if ((fdb->flags & FDB_SIGNED) ||
	    (fdb->flags & FDB_MULTIPART) ||
	    !CMATCH("Text/Plain", &fdb->type))
		return 1;

	/* check legacy DES MESSAGE */
	for (i = fdb->hdr_lines; (cb = fdb_read(fdb, i)) != NULL; i++) {
		if (CSUBMATCH("begin ", cb))
			break;
	}
	if (cb == NULL)
		return 0;
	ebuf.ptr = CP(cb);
	p = CE(cb);
	for (; (cb = fdb_read(fdb, i)) != NULL; i++) {
		if (CMATCH("end", cb))
			break;
	}
	if (cb == NULL)
		return 0;
	ebuf.len = CE(cb) - ebuf.ptr;
	ep = CP(cb);

	/* re-read not to reorder headers */
	abuf.size = CL(&fdb->mmap);	/* estimated size */
	abuf.buf.ptr = malloc(abuf.size);
	abuf.buf.len = 0;
	copy_abuf(&abuf, CP(&fdb->mmap), CP(&ebuf) - CP(&fdb->mmap));
	copy_abuf(&abuf, "-----BEGIN DES DECRYPTED MESSAGE-----\n", 38);


	memset(key, 0, sizeof(key));
	read_passwd("DES password: ", (char *)key, sizeof(key));
	for (i = 0; i < 8; i++) {
		c = 0;
		for (j = 1; j < 8; j++)
			if (((0x80 >> j) & key[i]) != 0)
				c++;
		if (c & 1)
			key[i] &= ~0x80;
		else
			key[i] |= 0x80;
	}
	memset(dbuf, 0, sizeof(dbuf));	/* null IV */
	EVP_CipherInit(&cipher, EVP_des_cbc(), key, dbuf, 0);

	j = 0;
	while (p < ep) {
		if (*p == '\n') {
			p++;
			continue;
		}
		s = p;
		while (p < ep) {
			if (*p++ == '\n')
				break;
		}
		cnt = ruue[*(unsigned char *)s];
		if (cnt < 0) {
			if (s + 3 < p && strncmp(s, "end", 3) == 0)
				return 0;
			break;
		}
		s++;
		while (cnt > 0) {
			for (i = 0, code = 0; i < 4; i++, s++) {
				if (s >= p)
					return 0;
				code <<= 6;
				c = ruue[*(unsigned char *)s];
				if (c < 0)
					return 0;
				code |= c;
			}
			for (i = 0; i < 3; i++) {
				if (cnt-- == 0)
					break;
				c = (code >> 16) & 0xff;
				code <<= 8;
				dbuf[j++] = c;
				if (j == 8) {
					EVP_Cipher(&cipher, dbuf, dbuf, j);
					copy_abuf(&abuf, (char *)dbuf, j);
					j = 0;
				}
			}
		}
		if (++s != p)
			break;
	}

	/* last block contains padding */
	CADDE(&abuf.buf, -8);
	p = CE(&abuf.buf);
	j = p[7];
	if (j < 0 || j > 7)
		return 0;
	CADDE(&abuf.buf, j);

	copy_abuf(&abuf, "-----END DES DECRYPTED MESSAGE-----\n", 36);
	copy_abuf(&abuf, CE(&ebuf), CE(&fdb->mmap) - CE(&ebuf));
	fdb_purge(fdb->name);
	fdb = fdb_open(fdb->name);
	fdb_replace(fdb, &abuf.buf);
	fdb->flags |= FDB_NOMMAP | FDB_DECRYPTED;
	fdb->flags &= ~FDB_ENCRYPTED;
	folder_purge(state, state->folder->msg[state->folder->pos].num);
	state->message = fdb;
	message_open(state, 1);

	strlcpy(state->status, "DES Decrypted", sizeof(state->status));
	return 1;
}
#endif /* USE_UUDES */
