/*
 # $Id: morpher.c,v 1.7 1998/04/10 10:27:56 fbm Exp fbm $
 # Copyright (C) 1997,1998 Farrell McKay
 # All rights reserved.
 #
 # This file is part of the Fortify distribution, a toolkit for
 # upgrading the cryptographic strength of the Netscape Navigator
 # web browser, authored by Farrell McKay.
 #
 # This toolkit is provided to the recipient under the
 # following terms and conditions:-
 #   1.  This copyright notice must not be removed or modified.
 #   2.  This toolkit may not be reproduced or included in any commercial
 #       media distribution, or commercial publication (for example CD-ROM,
 #       disk, book, magazine, journal) without first obtaining the author's
 #       express permission.
 #   3.  This toolkit, or any component of this toolkit, may not be
 #       commercially resold, redeveloped, rewritten, enhanced or otherwise
 #       used as the basis for commercial venture, without first obtaining
 #       the author's express permission.
 #   4.  Subject to the above conditions being observed (1-3), this toolkit
 #       may be freely reproduced or redistributed.
 #   5.  This software is provided "as-is", without express or implied
 #       warranty.  In no event shall the author be liable for any direct,
 #       indirect or consequential damages however caused.
 #   6.  Subject to the above conditions being observed (1-5),
 #       this toolkit may be used at no cost to the recipient.
 #
 # Farrell McKay			contact@fortify.net
 */

#include <ctype.h>
#include <stdlib.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>

#include "misc.h"
#include "index.h"
#include "morpher.h"
#include "os2lx.h"


static void
show_vec(FILE *f, unsigned char *src, int len, int format)
{
	if (len > 0) {
		if (format == VEC_NUM_FMT) {
			char pre = '[';
			while (len-- > 0) {
				fprintf(f, "%c%02x", pre, (unsigned int) *src++);
				pre = ' ';
			}
			fprintf(f, "]");
		} else if (format == VEC_STR_FMT) {
			fputc('"', f);
			for (; len-- > 0; src++) {
				if (isprint(*src))
					fputc(*src, f);
				else
					fprintf(f, "\\%03o", (unsigned int) *src);
			}
			fputc('"', f);
		}
	}
}

#define MAXMORPHLEN	1024

static int
apply_morphs(int tgt, morph_set *ms, int old_grade, int new_grade, int nowrite, int verbose, int *count)
{
	int		i;
	int		cmp;
	int		candidates = 0;
	morph_t		*p;
	off_t		offset;
	unsigned char	tmp[MAXMORPHLEN];
	vec_t		*old, *new;
	int		errs = 0;

	for (i = 0; i < ms->morphs_sz; i++) {
		p = ms->morphs[i];
		if (p->is_context)
			continue;

		if (new_grade < old_grade && p->grade <= old_grade && p->grade > new_grade) {
			old = &p->new;
			new = &p->old;
		}
		else if (new_grade > old_grade && p->grade > old_grade && p->grade <= new_grade) {
			old = &p->old;
			new = &p->new;
		}
		else
			continue;

		candidates++;
		if (new->len > MAXMORPHLEN) {
			errs++;
			fprintf(stderr,
				"Morph \"%s\": woops, this morph is too big!\n",
				p->name);
			continue;
		}
		if (verbose) {
			cmp = memcmp((void *) old->data, (void *) new->data, old->len);
			if (cmp == 0) {
				fprintf(stderr,
					"Morph \"%s\": Warning: morph changes nothing ??!\n",
					p->name);
			}

			printf("At file offset 0x%06lx, replacing ", p->filepos);
			show_vec(stdout, old->data, old->len, old->format);
			printf(" with ");
			show_vec(stdout, new->data, new->len, new->format);
			printf("\n");
		}

		offset = lseek(tgt, (off_t) p->filepos, SEEK_SET);
		if (offset == -1) {
			errs++;
			fprintf(stderr,
				"Morph \"%s\": error locating offset 0x%08lx [run-time 0x%lx]: ",
				p->name, p->filepos, p->base + p->offset);
			perror("");
			continue;
		}

		if (read(tgt, (char *) tmp, old->len) != old->len) {
			errs++;
			fprintf(stderr,
				"Morph \"%s\": error reading %d bytes at 0x%08lx: ",
				p->name, old->len, p->filepos);
			perror("");
			continue;
		}

		cmp = memcmp((void *) tmp, (void *) old->data, (size_t) old->len);
		if (cmp != 0) {
			errs++;
			fprintf(stderr, "Morph \"%s\": data mismatch: expected ", p->name);
			show_vec(stderr, old->data, old->len, VEC_NUM_FMT);
			fprintf(stderr, ", found ");
			show_vec(stderr, tmp, old->len, VEC_NUM_FMT);
			fprintf(stderr, "\n");
			continue;
		}

		if (!nowrite) {
			offset = lseek(tgt, (off_t) p->filepos, SEEK_SET);
			if (offset == -1) {
				errs++;
				fprintf(stderr,
					"Morph \"%s\": error repositioning at address 0x%08lx [run-time 0x%lx]: ",
					p->name, p->filepos, p->base + p->offset);
				perror("");
				continue;
			}
			if (write(tgt, (char *) new->data, new->len) != new->len) {
				errs++;
				fprintf(stderr,
					"Morph \"%s\": error writing data to 0x%08lx [run-time 0x%lx]: ",
					p->name, p->filepos, p->base + p->offset);
				perror("");
				continue;
			}
		}
	}
	*count = candidates;
	return errs;
}

int
morpher(char *target_name, char *morph_file, index_entry_t *ent, int nowrite, int old_grade, int new_grade, int verbose)
{
	int		i, tgt, errs;
	lxfile_t	*lx = NULL;
	int		candidates = 0;
	FILE		*mf;
	morph_set	*ms;

	tgt = open(target_name, OPENFL(O_RDWR), 0666);
	if (tgt == -1) {
		fprintf(stderr, "Cannot open \"%s\": ", target_name);
		perror("");
		fprintf(stderr, "Perhaps you are running Netscape at the moment?\n");
		return 1;
	}

	mf = fopen(morph_file, "r");
	if (mf == NULL) {
		fprintf(stderr, "Woops: cannot open \"%s\": ", morph_file);
		perror("");
		fprintf(stderr, "Perhaps your Fortify distribution was not unpacked correctly?\n");
		return 1;
	}

	ms = parse(mf);

	if (is_lx_entry(ent))
		lx = lxFile_is_lx(tgt);

	for (i = 0; i < ms->morphs_sz; i++) {
		morph_t *p = ms->morphs[i];
		if (lx)
			p->filepos = lxFile_get_offset(lx, p->base + p->offset);
		else
			p->filepos = p->base + p->offset - ms->offsets[p->seg];
	}

	errs = apply_morphs(tgt, ms, old_grade, new_grade, 1, verbose, (int *) &candidates);
	if (errs == 0 && ms->nmorphs > 0 && nowrite == 0) {
		errs = apply_morphs(tgt, ms, old_grade, new_grade, nowrite, 0, (int *) &candidates);
	}
	else {
		fprintf(stderr,
			"%d errors from %d potential morphs.\n",
			errs, candidates);
		fprintf(stderr, "No changes made to \"%s\".\n", target_name);
	}

	close(tgt);
	lxFile_free(lx);
	fclose(mf);
	return ((errs > 0)? 1: 0);
}
