/* dofile.c */

#include "cbc.h"

/*
 * CBC en/decrypts a file given the IV
 */

int dofile();
char *mktemp();
char *crypt();

int dofile(source, IV, mode)
char *source, *IV;
short mode;
{	
	char *target;		/* tmp file */
	char buffer[8];		/* data buffer */
	char previous[8];	/* previous data buffer */
	char tmp[8];		/* yet another data buffer */
	char header[11];	/* encoded key for sanity */

	int in, out;

	short first_block;	/* first block flag */
	short count;		/* byte count */

	/* create tmp file */
	target = mktemp("RAM:cipherXXX.XXX");
	if(!*target) {
		fprintf(stderr, "_mktemp failed\n");
		return(BAD);
	}

	if((in = open(source, O_RDONLY)) < 0){
		perror(source);
		return(R_ERR);
	}

	if((out = open(target, O_WRONLY | O_TRUNC | O_CREAT)) < 0) {
		close(out);
		perror(target);
		return(W_ERR);
	}

	if(mode & DECRYPT && !(mode & IGNORE)) {
		/*
		 * decryption is on, so we check for
		 * encoded header to match with key
		 */
		count = read(in, header, 11);
		if(count != 11 ||
			strncmp(header, crypt(IV, IV) + 2, 11)) {
				close(in);
				close(out);

				if(count < 0) {
					perror(source);
					return(R_ERR);
				}
				else return(MAT_ERR);
		}
	}
	else if(!(mode & IGNORE)) {
		/*
		 * write encoded key to target file
		 */
		count = write(out, crypt(IV, IV) + 2, 11);
		if(count != 11) {
			close(in);
			close(out);
			perror(target);
			return(W_ERR);
		}
	}

	first_block = TRUE;

	do{
		/*** read block ***/
		if((count = read(in, buffer, 8)) < 0) {
			close(in);
			close(out);
			perror("_read");
			return(R_ERR);
		}


		/*** decode ***/
		if(mode & DECRYPT) {
			if(first_block) strcopy(previous, IV, 8);
			else strcopy(previous, tmp, 8);
			
			if(count == 8){
				strcopy(tmp, buffer, 8);
				dcrypt(buffer, 1);
				strsum(buffer, previous);
			}
		}
		/*** encode ***/
		else {
			if(first_block) strcopy(previous, IV, 8);
			
			if(count == 8){
				strsum(buffer, previous);
				dcrypt(buffer, 0);
				strcopy(previous, buffer, 8);
			}
		}

		/*** cipher last short block ***/
		if(count != 8){
			dcrypt(previous, 0);
			strsum(buffer, previous);
		}	

		/*** write block ***/
		if((write(out, buffer, count)) < 0) {
			close(in);
			close(out);
			perror("_write");
			return(R_ERR);
		}

		first_block = FALSE;
		
	} while(count == 8);

	/* close & move */
	if(close(in) || close(out) || mv(target, source)) {
		perror("_close");
		return(BAD);
	}

	return(OK);
}
