/*-
 * Copyright (c) 2009 The NetBSD Foundation, Inc.
 * All rights reserved.
 *
 * This code is derived from software contributed to The NetBSD Foundation
 * by Alistair Crooks (agc@NetBSD.org)
 *
 * 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.
 *
 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
 * ``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 FOUNDATION OR CONTRIBUTORS
 * 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.
 */
/*
 * Copyright (c) 2005-2008 Nominet UK (www.nic.uk)
 * All rights reserved.
 * Contributors: Ben Laurie, Rachel Willmer. The Contributors have asserted
 * their moral rights under the UK Copyright Design and Patents Act 1988 to
 * be recorded as the authors of this copyright work.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License.
 *
 * You may obtain a copy of the License at
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/** \file
 * \brief Parser for OpenPGP packets
 */
#include "netpgp/config-netpgp.h"

#ifdef HAVE_SYS_CDEFS_H
#include <sys/cdefs.h>
#endif

#if defined(__NetBSD__)
__COPYRIGHT("@(#) Copyright (c) 2009 The NetBSD Foundation, Inc. All rights reserved.");
__RCSID("$NetBSD$");
#endif

#include <sys/types.h>
#include <sys/param.h>

#ifdef HAVE_OPENSSL_CAST_H
#include <openssl/cast.h>
#endif

#include <stdarg.h>
#include <stdlib.h>
#include <string.h>

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#ifdef HAVE_LIMITS_H
#include <limits.h>
#endif

#include "netpgp/packet.h"
#include "netpgp/packet-parse.h"
#include "netpgp/keyring.h"
#include "netpgp/errors.h"
#include "netpgp/packet-show.h"
#include "netpgp/create.h"
#include "netpgp/readerwriter.h"
#include "netpgp/netpgpdefs.h"
#include "netpgp/crypto.h"
#include "netpgp/netpgpdigest.h"

#define ERRP(cbinfo, cont, err)	do {					\
	cont.u.error = err;						\
	CALLBACK(PGP_PARSER_ERROR, cbinfo, &cont);			\
	return 0;							\
	/*NOTREACHED*/							\
} while(/*CONSTCOND*/0)

/**
 * limread_data reads the specified amount of the subregion's data
 * into a data_t structure
 *
 * \param data	Empty structure which will be filled with data
 * \param len	Number of octets to read
 * \param subregion
 * \param stream	How to parse
 *
 * \return 1 on success, 0 on failure; if 0 is returned, data cannot be used
 */
static int
limread_data(pgp_data_t *data, unsigned len,
		  pgp_region_t *subregion, pgp_stream_t *stream)
{
	data->len = len;

	if (subregion->length - subregion->readc < len) {
		(void) fprintf(stderr, "limread_data: bad length\n");
		return 0;
	}

	data->contents = calloc(1, data->len);
	if (!data->contents) {
		return 0;
	}

	int read_ok = pgp_limited_read(stream, data->contents, data->len, subregion,
			&stream->errors, &stream->readinfo, &stream->cbinfo);
	if( !read_ok ) {
		free(data->contents); // EDIT BY MR: fix memory leak
		data->contents = NULL;
		return 0;
	}

	return 1;
}

/**
 * read_data reads the remainder of the subregion's data
 * into a data_t structure
 *
 * \param data
 * \param subregion
 * \param stream
 *
 * \return 1 on success, 0 on failure
 */
static int
read_data(pgp_data_t *data, pgp_region_t *region, pgp_stream_t *stream)
{
	int	cc;

	cc = region->length - region->readc;
	return (cc >= 0) ? limread_data(data, (unsigned)cc, region, stream) : 0;
}

/**
 * Reads the remainder of the subregion as a string.
 * It is the user's responsibility to free the memory allocated here.
 */

static int
read_unsig_str(uint8_t **str, pgp_region_t *subregion,
		     pgp_stream_t *stream)
{
	size_t	len;

	len = subregion->length - subregion->readc;
	if ((*str = calloc(1, len + 1)) == NULL) {
		return 0;
	}
	if (len &&
	    !pgp_limited_read(stream, *str, len, subregion, &stream->errors,
				     &stream->readinfo, &stream->cbinfo)) {
		return 0;
	}
	(*str)[len] = '\0';
	return 1;
}

static int
read_string(char **str, pgp_region_t *subregion, pgp_stream_t *stream)
{
	return read_unsig_str((uint8_t **) str, subregion, stream);
}

void
pgp_init_subregion(pgp_region_t *subregion, pgp_region_t *region)
{
	(void) memset(subregion, 0x0, sizeof(*subregion));
	subregion->parent = region;
}

/*
 * XXX: replace pgp_ptag_t with something more appropriate for limiting reads
 */

/* data from partial blocks is queued up in virtual block in stream */
static int
read_partial_data(pgp_stream_t *stream,
                  pgp_reader_t *readinfo,
                  void *dest, size_t length)
{
    unsigned	n;

    if (pgp_get_debug_level(__FILE__)) {
        (void) fprintf(stderr, "fd_reader: coalesced data, off %d\n",
                       readinfo->virtualoff);
    }
    n = MIN(readinfo->virtualc - readinfo->virtualoff, (unsigned)length);
    (void) memcpy(dest, &readinfo->virtualpkt[readinfo->virtualoff], n);
    readinfo->virtualoff += n;
    if (readinfo->virtualoff == readinfo->virtualc) {
        free(readinfo->virtualpkt);
        readinfo->virtualpkt = NULL;
        readinfo->virtualc = readinfo->virtualoff = 0;
    }
    return (int)n;
}


/**
 * low-level function to read data from reader function
 *
 * Use this function, rather than calling the reader directly.
 *
 * If the accumulate flag is set in *stream, the function
 * adds the read data to the accumulated data, and updates
 * the accumulated length. This is useful if, for example,
 * the application wants access to the raw data as well as the
 * parsed data.
 *
 * This function will also try to read the entire amount asked for, but not
 * if it is over INT_MAX. Obviously many callers will know that they
 * never ask for that much and so can avoid the extra complexity of
 * dealing with return codes and filled-in lengths.
 *
 * \param *dest
 * \param *plength
 * \param flags
 * \param *stream
 *
 * \return PGP_R_OK
 * \return PGP_R_PARTIAL_READ
 * \return PGP_R_EOF
 * \return PGP_R_EARLY_EOF
 *
 * \sa #pgp_reader_ret_t for details of return codes
 */

static int
sub_base_read(pgp_stream_t *stream, void *dest, size_t length, pgp_error_t **errors,
	      pgp_reader_t *readinfo, pgp_cbdata_t *cbinfo)
{
	size_t          n;

	/* reading more than this would look like an error */
	if (length > INT_MAX)
		length = INT_MAX;

	for (n = 0; n < length;) {
		int	r;

        if (!readinfo->coalescing && readinfo->virtualc && readinfo->virtualoff < readinfo->virtualc) {
            r = read_partial_data(stream, readinfo, (char*) dest + n, length - n);
        }else{
            r = readinfo->reader(stream, (char *) dest + n,
                                 length - n, errors,
                                 readinfo, cbinfo);
        }
		if (r > (int)(length - n)) {
			(void) fprintf(stderr, "sub_base_read: bad read\n");
			return 0;
		}
		if (r < 0) {
			return r;
		}
		if (r == 0) {
			break;
		}
		n += (unsigned)r;
	}

	if (n == 0) {
		return 0;
	}
	if (readinfo->accumulate) {
		if (readinfo->asize < readinfo->alength) {
			(void) fprintf(stderr, "sub_base_read: bad size\n");
			return 0;
		}
		if (readinfo->alength + n > readinfo->asize) {
			uint8_t	*temp;

			readinfo->asize = (readinfo->asize * 2) + (unsigned)n;
			temp = realloc(readinfo->accumulated, readinfo->asize);
			if (temp == NULL) {
				(void) fprintf(stderr,
					"sub_base_read: bad alloc\n");
				return 0;
			}
			readinfo->accumulated = temp;
		}
		if (readinfo->asize < readinfo->alength + n) {
			(void) fprintf(stderr, "sub_base_read: bad realloc\n");
			return 0;
		}
		(void) memcpy(readinfo->accumulated + readinfo->alength, dest,
				n);
	}
	/* we track length anyway, because it is used for packet offsets */
	readinfo->alength += (unsigned)n;
	/* and also the position */
	readinfo->position += (unsigned)n;

	return (int)n;
}

int
pgp_stacked_read(pgp_stream_t *stream, void *dest, size_t length, pgp_error_t **errors,
		 pgp_reader_t *readinfo, pgp_cbdata_t *cbinfo)
{
	return sub_base_read(stream, dest, length, errors, readinfo->next, cbinfo);
}

/* This will do a full read so long as length < MAX_INT */
static int
base_read(uint8_t *dest, size_t length, pgp_stream_t *stream)
{
	return sub_base_read(stream, dest, length, &stream->errors, &stream->readinfo,
			     &stream->cbinfo);
}

/*
 * Read a full size_t's worth. If the return is < than length, then
 * *last_read tells you why - < 0 for an error, == 0 for EOF
 */

static size_t
full_read(pgp_stream_t *stream, uint8_t *dest,
		size_t length,
		int *last_read,
		pgp_error_t **errors,
		pgp_reader_t *readinfo,
		pgp_cbdata_t *cbinfo)
{
	size_t          t;
	int             r = 0;	/* preset in case some loon calls with length
				 * == 0 */

	for (t = 0; t < length;) {
		r = sub_base_read(stream, dest + t, length - t, errors, readinfo,
				cbinfo);
		if (r <= 0) {
			*last_read = r;
			return t;
		}
		t += (size_t)r;
	}

	*last_read = r;

	return t;
}



/** Read a scalar value of selected length from reader.
 *
 * Read an unsigned scalar value from reader in Big Endian representation.
 *
 * This function does not know or care about packet boundaries. It
 * also assumes that an EOF is an error.
 *
 * \param *result	The scalar value is stored here
 * \param *reader	Our reader
 * \param length	How many bytes to read
 * \return		1 on success, 0 on failure
 */
static unsigned
_read_scalar(unsigned *result, unsigned length,
	     pgp_stream_t *stream)
{
	unsigned        t = 0;

	if (length > sizeof(*result)) {
		(void) fprintf(stderr, "_read_scalar: bad length\n");
		return 0;
	}

	while (length--) {
		uint8_t	c;
		int	r;

		r = base_read(&c, 1, stream);
		if (r != 1)
			return 0;
		t = (t << 8) + c;
	}

	*result = t;
	return 1;
}

/**
 * \ingroup Core_ReadPackets
 * \brief Read bytes from a region within the packet.
 *
 * Read length bytes into the buffer pointed to by *dest.
 * Make sure we do not read over the packet boundary.
 * Updates the Packet Tag's pgp_ptag_t::readc.
 *
 * If length would make us read over the packet boundary, or if
 * reading fails, we call the callback with an error.
 *
 * Note that if the region is indeterminate, this can return a short
 * read - check region->last_read for the length. EOF is indicated by
 * a success return and region->last_read == 0 in this case (for a
 * region of known length, EOF is an error).
 *
 * This function makes sure to respect packet boundaries.
 *
 * \param dest		The destination buffer
 * \param length	How many bytes to read
 * \param region	Pointer to packet region
 * \param errors    Error stack
 * \param readinfo		Reader info
 * \param cbinfo	Callback info
 * \return		1 on success, 0 on error
 */
unsigned
pgp_limited_read(pgp_stream_t *stream, uint8_t *dest,
			size_t length,
			pgp_region_t *region,
			pgp_error_t **errors,
			pgp_reader_t *readinfo,
			pgp_cbdata_t *cbinfo)
{
	size_t	r;
	int	lr;

	if (!region->indeterminate &&
	    region->readc + length > region->length) {
		PGP_ERROR_1(errors, PGP_E_P_NOT_ENOUGH_DATA, "%s",
		    "Not enough data");
		return 0;
	}
	r = full_read(stream, dest, length, &lr, errors, readinfo, cbinfo);
	if (lr < 0) {
		PGP_ERROR_1(errors, PGP_E_R_READ_FAILED, "%s", "Read failed");
		return 0;
	}
	if (!region->indeterminate && r != length) {
		PGP_ERROR_1(errors, PGP_E_R_READ_FAILED, "%s", "Read failed");
		return 0;
	}
	region->last_read = (unsigned)r;
	do {
		region->readc += (unsigned)r;
		if (region->parent && region->length > region->parent->length) {
			(void) fprintf(stderr,
				"ops_limited_read: bad length\n");
			return 0;
		}
	} while ((region = region->parent) != NULL);
	return 1;
}

/**
   \ingroup Core_ReadPackets
   \brief Call pgp_limited_read on next in stack
*/
unsigned
pgp_stacked_limited_read(pgp_stream_t *stream, uint8_t *dest, unsigned length,
			 pgp_region_t *region,
			 pgp_error_t **errors,
			 pgp_reader_t *readinfo,
			 pgp_cbdata_t *cbinfo)
{
	return pgp_limited_read(stream, dest, length, region, errors,
				readinfo->next, cbinfo);
}

static unsigned
limread(uint8_t *dest, unsigned length,
	     pgp_region_t *region, pgp_stream_t *info)
{
	return pgp_limited_read(info, dest, length, region, &info->errors,
				&info->readinfo, &info->cbinfo);
}

#if 0
static unsigned
exact_limread(uint8_t *dest, unsigned len,
		   pgp_region_t *region,
		   pgp_stream_t *stream)
{
	unsigned   ret;

	stream->exact_read = 1;
	ret = limread(dest, len, region, stream);
	stream->exact_read = 0;
	return ret;
}
#endif

/** Skip over length bytes of this packet.
 *
 * Calls limread() to skip over some data.
 *
 * This function makes sure to respect packet boundaries.
 *
 * \param length	How many bytes to skip
 * \param *region	Pointer to packet region
 * \param *stream	How to parse
 * \return		1 on success, 0 on error (calls the cb with PGP_PARSER_ERROR in limread()).
 */
static int
limskip(unsigned length, pgp_region_t *region, pgp_stream_t *stream)
{
	uint8_t   buf[NETPGP_BUFSIZ];

	while (length > 0) {
		unsigned	n = length % NETPGP_BUFSIZ;

		if (!limread(buf, n, region, stream)) {
			return 0;
		}
		length -= n;
	}
	return 1;
}

/** Read a scalar.
 *
 * Read a big-endian scalar of length bytes, respecting packet
 * boundaries (by calling limread() to read the raw data).
 *
 * This function makes sure to respect packet boundaries.
 *
 * \param *dest		The scalar value is stored here
 * \param length	How many bytes make up this scalar (at most 4)
 * \param *region	Pointer to current packet region
 * \param *stream	How to parse
 * \param *cb		The callback
 * \return		1 on success, 0 on error (calls the cb with PGP_PARSER_ERROR in limread()).
 *
 * \see RFC4880 3.1
 */
static int
limread_scalar(unsigned *dest,
			unsigned len,
			pgp_region_t *region,
			pgp_stream_t *stream)
{
	uint8_t		c[4] = "";
	unsigned        t;
	unsigned        n;

	if (len > 4) {
		(void) fprintf(stderr, "limread_scalar: bad length\n");
		return 0;
	}
	/*LINTED*/
	if (/*CONSTCOND*/sizeof(*dest) < 4) {
		(void) fprintf(stderr, "limread_scalar: bad dest\n");
		return 0;
	}
	if (!limread(c, len, region, stream)) {
		return 0;
	}
	for (t = 0, n = 0; n < len; ++n) {
		t = (t << 8) + c[n];
	}
	*dest = t;
	return 1;
}

/** Read a scalar.
 *
 * Read a big-endian scalar of length bytes, respecting packet
 * boundaries (by calling limread() to read the raw data).
 *
 * The value read is stored in a size_t, which is a different size
 * from an unsigned on some platforms.
 *
 * This function makes sure to respect packet boundaries.
 *
 * \param *dest		The scalar value is stored here
 * \param length	How many bytes make up this scalar (at most 4)
 * \param *region	Pointer to current packet region
 * \param *stream	How to parse
 * \param *cb		The callback
 * \return		1 on success, 0 on error (calls the cb with PGP_PARSER_ERROR in limread()).
 *
 * \see RFC4880 3.1
 */
static int
limread_size_t(size_t *dest,
				unsigned length,
				pgp_region_t *region,
				pgp_stream_t *stream)
{
	unsigned        tmp;

	/*
	 * Note that because the scalar is at most 4 bytes, we don't care if
	 * size_t is bigger than usigned
	 */
	if (!limread_scalar(&tmp, length, region, stream))
		return 0;

	*dest = tmp;
	return 1;
}

/** Read a timestamp.
 *
 * Timestamps in OpenPGP are unix time, i.e. seconds since The Epoch (1.1.1970).  They are stored in an unsigned scalar
 * of 4 bytes.
 *
 * This function reads the timestamp using limread_scalar().
 *
 * This function makes sure to respect packet boundaries.
 *
 * \param *dest		The timestamp is stored here
 * \param *ptag		Pointer to current packet's Packet Tag.
 * \param *reader	Our reader
 * \param *cb		The callback
 * \return		see limread_scalar()
 *
 * \see RFC4880 3.5
 */
static int
limited_read_time(time_t *dest, pgp_region_t *region,
		  pgp_stream_t *stream)
{
	uint8_t	c;
	time_t	mytime = 0;
	int	i;

	/*
         * Cannot assume that time_t is 4 octets long -
	 * SunOS 5.10 and NetBSD both have 64-bit time_ts.
         */
	if (/* CONSTCOND */sizeof(time_t) == 4) {
		return limread_scalar((unsigned *)(void *)dest, 4, region, stream);
	}
	for (i = 0; i < 4; i++) {
		if (!limread(&c, 1, region, stream)) {
			return 0;
		}
		mytime = (mytime << 8) + c;
	}
	*dest = mytime;
	return 1;
}

/**
 * \ingroup Core_MPI
 * Read a multiprecision integer.
 *
 * Large numbers (multiprecision integers, MPI) are stored in OpenPGP in two parts.  First there is a 2 byte scalar
 * indicating the length of the following MPI in Bits.  Then follow the bits that make up the actual number, most
 * significant bits first (Big Endian).  The most significant bit in the MPI is supposed to be 1 (unless the MPI is
 * encrypted - then it may be different as the bit count refers to the plain text but the bits are encrypted).
 *
 * Unused bits (i.e. those filling up the most significant byte from the left to the first bits that counts) are
 * supposed to be cleared - I guess. XXX - does anything actually say so?
 *
 * This function makes sure to respect packet boundaries.
 *
 * \param **pgn		return the integer there - the BIGNUM is created by BN_bin2bn() and probably needs to be freed
 * 				by the caller XXX right ben?
 * \param *ptag		Pointer to current packet's Packet Tag.
 * \param *reader	Our reader
 * \param *cb		The callback
 * \return		1 on success, 0 on error (by limread_scalar() or limread() or if the MPI is not properly formed (XXX
 * 				 see comment below - the callback is called with a PGP_PARSER_ERROR in case of an error)
 *
 * \see RFC4880 3.2
 */
static int
limread_mpi(BIGNUM **pbn, pgp_region_t *region, pgp_stream_t *stream)
{
	uint8_t   buf[NETPGP_BUFSIZ] = "";
					/* an MPI has a 2 byte length part.
					 * Length is given in bits, so the
					 * largest we should ever need for
					 * the buffer is NETPGP_BUFSIZ bytes. */
	unsigned        length;
	unsigned        nonzero;
	unsigned	ret;

	//stream->reading_mpi_len = 1;
	ret = (unsigned)limread_scalar(&length, 2, region, stream);

	//stream->reading_mpi_len = 0;
	if (!ret)
		return 0;

	nonzero = length & 7;	/* there should be this many zero bits in the
				 * MS byte */
	if (!nonzero)
		nonzero = 8;
	length = (length + 7) / 8;

	if (length == 0) {
		/* if we try to read a length of 0, then fail */
		if (pgp_get_debug_level(__FILE__)) {
			(void) fprintf(stderr, "limread_mpi: 0 length\n");
		}
		return 0;
	}
	if (length > NETPGP_BUFSIZ) {
		(void) fprintf(stderr, "limread_mpi: bad length\n");
		return 0;
	}
	if (!limread(buf, length, region, stream)) {
		return 0;
	}
	if (((unsigned)buf[0] >> nonzero) != 0 ||
	    !((unsigned)buf[0] & (1U << (nonzero - 1U)))) {
		PGP_ERROR_1(&stream->errors, PGP_E_P_MPI_FORMAT_ERROR,
		    "%s", "MPI Format error");
		/* XXX: Ben, one part of
		 * this constraint does
		 * not apply to
		 * encrypted MPIs the
		 * draft says. -- peter */
		return 0;
	}
	*pbn = BN_bin2bn(buf, (int)length, NULL);
	return 1;
}

static unsigned read_new_length(unsigned *, pgp_stream_t *);

/* allocate space, read, and stash data away in a virtual pkt */
static void
streamread(pgp_stream_t *stream, unsigned c)
{
	int	cc;
    pgp_reader_t *readinfo = &stream->readinfo;

	readinfo->virtualpkt = realloc(readinfo->virtualpkt, readinfo->virtualc + c);
	cc = readinfo->reader(stream, &readinfo->virtualpkt[readinfo->virtualc],
		c, &stream->errors, readinfo, &stream->cbinfo);
	readinfo->virtualc += cc;
}

/* coalesce all the partial blocks together */
static int
coalesce_blocks(pgp_stream_t *stream, unsigned length)
{
	unsigned	c;
    unsigned    r;

    pgp_reader_t *readinfo = &stream->readinfo;
	readinfo->coalescing = 1;

	/* already read a partial block length - prime the array */
	streamread(stream, length);
	while ((r = read_new_length(&c, stream)) && readinfo->partial_read) {
		/* length we read is partial - add to end of array */
		streamread(stream, c);
	}
	/* not partial - add the last extent to the end of the array */
	if(r > 0) streamread(stream, c);
	readinfo->coalescing = 0;
	return 1;
}

/** Read some data with a New-Format length from reader.
 *
 * \sa Internet-Draft RFC4880.txt Section 4.2.2
 *
 * \param *length	Where the decoded length will be put
 * \param *stream	How to parse
 * \return		1 if OK, else 0
 *
 */

static unsigned
read_new_length(unsigned *length, pgp_stream_t *stream)
{
	uint8_t   c;
    pgp_reader_t *readinfo = &stream->readinfo;

	readinfo->partial_read = 0;
	if (base_read(&c, 1, stream) != 1) {
		return 0;
	}
	if (c < 192) {
		/* 1. One-octet packet */
		*length = c;
		return 1;
	}
	if (c < 224) {
		/* 2. Two-octet packet */
		unsigned        t = (c - 192) << 8;

		if (base_read(&c, 1, stream) != 1) {
			return 0;
		}
		*length = t + c + 192;
		return 1;
	}
	if (c < 255) {
		/* 3. Partial Body Length */
		readinfo->partial_read = 1;
		*length = 1 << (c & 0x1f);
		if (!readinfo->coalescing) {
			/* we have been called from coalesce_blocks -
			 * just return with the partial length */
			coalesce_blocks(stream, *length);
			*length = readinfo->virtualc;
		}
		return 1;
	}
	/* 4. Five-Octet packet */
	return _read_scalar(length, 4, stream);
}

/** Read the length information for a new format Packet Tag.
 *
 * New style Packet Tags encode the length in one to five octets.  This function reads the right amount of bytes and
 * decodes it to the proper length information.
 *
 * This function makes sure to respect packet boundaries.
 *
 * \param *length	return the length here
 * \param *ptag		Pointer to current packet's Packet Tag.
 * \param *reader	Our reader
 * \param *cb		The callback
 * \return		1 on success, 0 on error (by limread_scalar() or limread() or if the MPI is not properly formed (XXX
 * 				 see comment below)
 *
 * \see RFC4880 4.2.2
 * \see pgp_ptag_t
 */
static int
limited_read_new_length(unsigned *length, pgp_region_t *region,
			pgp_stream_t *stream)
{
	uint8_t   c = 0x0;
    pgp_reader_t *readinfo = &stream->readinfo;

	if (!limread(&c, 1, region, stream)) {
		return 0;
	}
	if (c < 192) {
		*length = c;
		return 1;
	}
	if (c < 224) {
		unsigned        t = (c - 192) << 8;

		if (!limread(&c, 1, region, stream)) {
			return 0;
		}
		*length = t + c + 192;
		return 1;
	}
	if (c < 255) {
		readinfo->partial_read = 1;
		*length = 1 << (c & 0x1f);
		if (!readinfo->coalescing) {
			/* we have been called from coalesce_blocks -
			 * just return with the partial length */
			coalesce_blocks(stream, *length);
			*length = readinfo->virtualc;
		}
		return 1;
	}
	return limread_scalar(length, 4, region, stream);
}

/**
\ingroup Core_Create
\brief Free allocated memory
*/
void
pgp_data_free(pgp_data_t *data)
{
	free(data->contents);
	data->contents = NULL;
	data->len = 0;
}

void
pgp_data_dup(pgp_data_t *dst, const pgp_data_t *src)
{
	dst->contents = calloc(1, src->len);
    memcpy(dst->contents, src->contents, src->len);
	dst->len = src->len;
}

/**
\ingroup Core_Create
\brief Free allocated memory
*/
static void
string_free(char **str)
{
	free(*str);
	*str = NULL;
}

/**
\ingroup Core_Create
\brief Free allocated memory
*/
/* ! Free packet memory, set pointer to NULL */
void
pgp_subpacket_free(pgp_subpacket_t *packet)
{
	free(packet->raw);
	packet->raw = NULL;
}

/**
\ingroup Core_Create
\brief Free allocated memory
*/
static void
headers_free(pgp_headers_t *headers)
{
	unsigned        n;

	for (n = 0; n < headers->headerc; ++n) {
		free(headers->headers[n].key);
		free(headers->headers[n].value);
	}
	free(headers->headers);
	headers->headers = NULL;
}

/**
\ingroup Core_Create
\brief Free allocated memory
*/
static void
cleartext_trailer_free(struct pgp_hash_t **trailer)
{
	free(*trailer);
	*trailer = NULL;
}

/**
\ingroup Core_Create
\brief Free allocated memory
*/
static void
cmd_get_passphrase_free(pgp_seckey_passphrase_t *skp)
{
	if (skp->passphrase && *skp->passphrase) {
		free(*skp->passphrase);
		*skp->passphrase = NULL;
	}
}

/**
\ingroup Core_Create
\brief Free allocated memory
*/
static void
free_BN(BIGNUM **pp)
{
	if (pp==NULL || *pp==NULL) {
		return;
	}

	BN_free(*pp);
	*pp = NULL;
}

static void
dup_BN(BIGNUM **dst, const BIGNUM *src)
{
	*dst = BN_dup(src);
}

void
copy_sig_info(pgp_sig_info_t *dst, const pgp_sig_info_t *src)
{
	(void) memcpy(dst, src, sizeof(*src));

	if ((dst->v4_hashed = calloc(1, src->v4_hashlen)) == NULL) {
		(void) fprintf(stderr, "copy_sig_info: bad alloc\n");
	} else {
		(void) memcpy(dst->v4_hashed, src->v4_hashed, src->v4_hashlen);
	}

	switch (src->key_alg) {
	case PGP_PKA_RSA:
	case PGP_PKA_RSA_SIGN_ONLY:
		dup_BN(&dst->sig.rsa.sig, src->sig.rsa.sig);
		break;

	case PGP_PKA_DSA:
		dup_BN(&dst->sig.dsa.r, src->sig.dsa.r);
		dup_BN(&dst->sig.dsa.s, src->sig.dsa.s);
		break;

	case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
		dup_BN(&dst->sig.elgamal.r, src->sig.elgamal.r);
		dup_BN(&dst->sig.elgamal.s, src->sig.elgamal.s);
		break;

	case PGP_PKA_PRIVATE00:
	case PGP_PKA_PRIVATE01:
	case PGP_PKA_PRIVATE02:
	case PGP_PKA_PRIVATE03:
	case PGP_PKA_PRIVATE04:
	case PGP_PKA_PRIVATE05:
	case PGP_PKA_PRIVATE06:
	case PGP_PKA_PRIVATE07:
	case PGP_PKA_PRIVATE08:
	case PGP_PKA_PRIVATE09:
	case PGP_PKA_PRIVATE10:
		pgp_data_dup(&dst->sig.unknown, &src->sig.unknown);
		break;

	default:
		(void) fprintf(stderr, "sig_dup: bad sig type\n");
	}
}
/**
 * \ingroup Core_Create
 * \brief Free the memory used when parsing a signature
 * \param sig
 */
void
pgp_free_sig_info(pgp_sig_info_t *info)
{
    free(info->v4_hashed);

	switch (info->key_alg) {
	case PGP_PKA_RSA:
	case PGP_PKA_RSA_SIGN_ONLY:
		free_BN(&info->sig.rsa.sig);
		break;

	case PGP_PKA_DSA:
		free_BN(&info->sig.dsa.r);
		free_BN(&info->sig.dsa.s);
		break;

	case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
		free_BN(&info->sig.elgamal.r);
		free_BN(&info->sig.elgamal.s);
		break;

	case PGP_PKA_PRIVATE00:
	case PGP_PKA_PRIVATE01:
	case PGP_PKA_PRIVATE02:
	case PGP_PKA_PRIVATE03:
	case PGP_PKA_PRIVATE04:
	case PGP_PKA_PRIVATE05:
	case PGP_PKA_PRIVATE06:
	case PGP_PKA_PRIVATE07:
	case PGP_PKA_PRIVATE08:
	case PGP_PKA_PRIVATE09:
	case PGP_PKA_PRIVATE10:
		pgp_data_free(&info->sig.unknown);
		break;

	default:
		(void) fprintf(stderr, "info-free: bad info-type\n");
	}
    memset(info, 0, sizeof(pgp_sig_info_t));
}

static void
sig_free(pgp_sig_t *sig)
{
    pgp_free_sig_info(&sig->info);
}


/**
\ingroup Core_Create
\brief Free allocated memory
*/
/* ! Free any memory allocated when parsing the packet content */
void
pgp_parser_content_free(pgp_packet_t *c)
{
	switch (c->tag) {
	case PGP_PARSER_PTAG:
	case PGP_PTAG_CT_COMPRESSED:
	case PGP_PTAG_SS_CREATION_TIME:
	case PGP_PTAG_SS_EXPIRATION_TIME:
	case PGP_PTAG_SS_KEY_EXPIRY:
	case PGP_PTAG_SS_TRUST:
	case PGP_PTAG_SS_ISSUER_KEY_ID:
	case PGP_PTAG_CT_1_PASS_SIG:
	case PGP_PTAG_SS_PRIMARY_USER_ID:
	case PGP_PTAG_SS_REVOCABLE:
	case PGP_PTAG_SS_REVOCATION_KEY:
	case PGP_PTAG_CT_LITDATA_HEADER:
	case PGP_PTAG_CT_LITDATA_BODY:
	case PGP_PTAG_CT_SIGNED_CLEARTEXT_BODY:
	case PGP_PTAG_CT_UNARMOURED_TEXT:
	case PGP_PTAG_CT_ARMOUR_TRAILER:
	case PGP_PTAG_CT_SIGNATURE_HEADER:
	case PGP_PTAG_CT_SE_DATA_HEADER:
	case PGP_PTAG_CT_SE_IP_DATA_HEADER:
	case PGP_PTAG_CT_SE_IP_DATA_BODY:
	case PGP_PTAG_CT_MDC:
	case PGP_GET_SECKEY:
		break;

	case PGP_PTAG_CT_SIGNED_CLEARTEXT_HEADER:
		headers_free(&c->u.cleartext_head);
		break;

	case PGP_PTAG_CT_ARMOUR_HEADER:
		headers_free(&c->u.armour_header.headers);
		break;

	case PGP_PTAG_CT_SIGNED_CLEARTEXT_TRAILER:
		cleartext_trailer_free(&c->u.cleartext_trailer);
		break;

	case PGP_PTAG_CT_TRUST:
		pgp_data_free(&c->u.trust);
		break;

	case PGP_PTAG_CT_SIGNATURE:
	case PGP_PTAG_CT_SIGNATURE_FOOTER:
		sig_free(&c->u.sig);
		break;

	case PGP_PTAG_CT_PUBLIC_KEY:
	case PGP_PTAG_CT_PUBLIC_SUBKEY:
		pgp_pubkey_free(&c->u.pubkey);
		break;

	case PGP_PTAG_CT_USER_ID:
		pgp_userid_free(&c->u.userid);
		break;

	case PGP_PTAG_SS_SIGNERS_USER_ID:
		pgp_userid_free(&c->u.ss_signer);
		break;

	case PGP_PTAG_CT_USER_ATTR:
		pgp_data_free(&c->u.userattr);
		break;

	case PGP_PTAG_SS_PREFERRED_SKA:
		pgp_data_free(&c->u.ss_skapref);
		break;

	case PGP_PTAG_SS_PREFERRED_HASH:
		pgp_data_free(&c->u.ss_hashpref);
		break;

	case PGP_PTAG_SS_PREF_COMPRESS:
		pgp_data_free(&c->u.ss_zpref);
		break;

	case PGP_PTAG_SS_KEY_FLAGS:
		pgp_data_free(&c->u.ss_key_flags);
		break;

	case PGP_PTAG_SS_KEYSERV_PREFS:
		pgp_data_free(&c->u.ss_key_server_prefs);
		break;

	case PGP_PTAG_SS_FEATURES:
		pgp_data_free(&c->u.ss_features);
		break;

	case PGP_PTAG_SS_NOTATION_DATA:
		pgp_data_free(&c->u.ss_notation.name);
		pgp_data_free(&c->u.ss_notation.value);
		break;

	case PGP_PTAG_SS_REGEXP:
		string_free(&c->u.ss_regexp);
		break;

	case PGP_PTAG_SS_POLICY_URI:
		string_free(&c->u.ss_policy);
		break;

	case PGP_PTAG_SS_PREF_KEYSERV:
		string_free(&c->u.ss_keyserv);
		break;

	case PGP_PTAG_SS_USERDEFINED00:
	case PGP_PTAG_SS_USERDEFINED01:
	case PGP_PTAG_SS_USERDEFINED02:
	case PGP_PTAG_SS_USERDEFINED03:
	case PGP_PTAG_SS_USERDEFINED04:
	case PGP_PTAG_SS_USERDEFINED05:
	case PGP_PTAG_SS_USERDEFINED06:
	case PGP_PTAG_SS_USERDEFINED07:
	case PGP_PTAG_SS_USERDEFINED08:
	case PGP_PTAG_SS_USERDEFINED09:
	case PGP_PTAG_SS_USERDEFINED10:
		pgp_data_free(&c->u.ss_userdef);
		break;

	case PGP_PTAG_SS_RESERVED:
		pgp_data_free(&c->u.ss_unknown);
		break;

	case PGP_PTAG_SS_REVOCATION_REASON:
		string_free(&c->u.ss_revocation.reason);
		break;

	case PGP_PTAG_SS_EMBEDDED_SIGNATURE:
		pgp_data_free(&c->u.ss_embedded_sig);
		break;

	case PGP_PARSER_PACKET_END:
		pgp_subpacket_free(&c->u.packet);
		break;

	case PGP_PARSER_ERROR:
	case PGP_PARSER_ERRCODE:
		break;

	case PGP_PTAG_CT_SECRET_KEY:
	case PGP_PTAG_CT_ENCRYPTED_SECRET_KEY:
		pgp_seckey_free(&c->u.seckey);
		break;

	case PGP_PTAG_CT_PK_SESSION_KEY:
	case PGP_PTAG_CT_ENCRYPTED_PK_SESSION_KEY:
		pgp_pk_sesskey_free(&c->u.pk_sesskey);
		break;

	case PGP_GET_PASSPHRASE:
		cmd_get_passphrase_free(&c->u.skey_passphrase);
		break;

	default:
		fprintf(stderr, "Can't free %d (0x%x)\n", c->tag, c->tag);
	}
}

/**
\ingroup Core_Create
\brief Free allocated memory
*/
void
pgp_pk_sesskey_free(pgp_pk_sesskey_t *sk)
{
	switch (sk->alg) {
	case PGP_PKA_RSA:
		free_BN(&sk->params.rsa.encrypted_m);
		break;

	case PGP_PKA_ELGAMAL:
		free_BN(&sk->params.elgamal.g_to_k);
		free_BN(&sk->params.elgamal.encrypted_m);
		break;

	default:
		(void) fprintf(stderr, "pgp_pk_sesskey_free: bad alg\n");
		break;
	}
}

/**
\ingroup Core_Create
\brief Free allocated memory
*/
/* ! Free the memory used when parsing a public key */
void
pgp_pubkey_free(pgp_pubkey_t *p)
{
	if (p==NULL) {
		return;
	}

	switch (p->alg) {
	case PGP_PKA_RSA:
	case PGP_PKA_RSA_ENCRYPT_ONLY:
	case PGP_PKA_RSA_SIGN_ONLY:
		free_BN(&p->key.rsa.n);
		free_BN(&p->key.rsa.e);
		break;

	case PGP_PKA_DSA:
		free_BN(&p->key.dsa.p);
		free_BN(&p->key.dsa.q);
		free_BN(&p->key.dsa.g);
		free_BN(&p->key.dsa.y);
		break;

	case PGP_PKA_ELGAMAL:
	case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
		free_BN(&p->key.elgamal.p);
		free_BN(&p->key.elgamal.g);
		free_BN(&p->key.elgamal.y);
		break;

	case PGP_PKA_NOTHING:
		/* nothing to free */
		break;

	default:
		(void) fprintf(stderr, "pgp_pubkey_free: bad alg\n");
	}
    memset(p, 0, sizeof(*p));
}

int
pgp_pubkey_dup(pgp_pubkey_t *dst, pgp_pubkey_t *src)
{
    memcpy(dst, src, sizeof(*src));

	switch (src->alg) {
	case PGP_PKA_RSA:
	case PGP_PKA_RSA_ENCRYPT_ONLY:
	case PGP_PKA_RSA_SIGN_ONLY:
		dup_BN(&dst->key.rsa.n, src->key.rsa.n);
		dup_BN(&dst->key.rsa.e, src->key.rsa.e);
		break;

	case PGP_PKA_DSA:
		dup_BN(&dst->key.dsa.p, src->key.dsa.p);
		dup_BN(&dst->key.dsa.q, src->key.dsa.q);
		dup_BN(&dst->key.dsa.g, src->key.dsa.g);
		dup_BN(&dst->key.dsa.y, src->key.dsa.y);
		break;

	case PGP_PKA_ELGAMAL:
	case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
		dup_BN(&dst->key.elgamal.p, src->key.elgamal.p);
		dup_BN(&dst->key.elgamal.g, src->key.elgamal.g);
		dup_BN(&dst->key.elgamal.y, src->key.elgamal.y);
		break;

	case PGP_PKA_NOTHING:
		/* nothing to dup */
		break;

	default:
		(void) fprintf(stderr, "pgp_pubkey_dup: bad alg\n");
        return 0;
	}

    /*TODO alloc error handling */
    return 1;
}
/**
   \ingroup Core_ReadPackets
*/
static int
parse_pubkey_data(pgp_pubkey_t *key, pgp_region_t *region,
		      pgp_stream_t *stream)
{
	uint8_t   c = 0x0;

	if (region->readc != 0) {
		/* We should not have read anything so far */
		(void) fprintf(stderr, "parse_pubkey_data: bad length\n");
		return 0;
	}
	if (!limread(&c, 1, region, stream)) {
		return 0;
	}
	key->version = (pgp_version_t)c;
	switch (key->version) {
	case PGP_V2:
	case PGP_V3:
	case PGP_V4:
		break;
	default:
		PGP_ERROR_1(&stream->errors, PGP_E_PROTO_BAD_PUBLIC_KEY_VRSN,
			    "Bad public key version (0x%02x)", key->version);
		return 0;
	}
	if (!limited_read_time(&key->birthtime, region, stream)) {
		return 0;
	}

	key->days_valid = 0;
	if ((key->version == 2 || key->version == 3) &&
	    !limread_scalar(&key->days_valid, 2, region, stream)) {
		return 0;
	}

	if (!limread(&c, 1, region, stream)) {
		return 0;
	}
	key->alg = c;

	switch (key->alg) {
	case PGP_PKA_DSA:
		if (!limread_mpi(&key->key.dsa.p, region, stream) ||
		    !limread_mpi(&key->key.dsa.q, region, stream) ||
		    !limread_mpi(&key->key.dsa.g, region, stream) ||
		    !limread_mpi(&key->key.dsa.y, region, stream)) {
			return 0;
		}
		break;

	case PGP_PKA_RSA:
	case PGP_PKA_RSA_ENCRYPT_ONLY:
	case PGP_PKA_RSA_SIGN_ONLY:
		if (!limread_mpi(&key->key.rsa.n, region, stream) ||
		    !limread_mpi(&key->key.rsa.e, region, stream)) {
			return 0;
		}
		break;

	case PGP_PKA_ELGAMAL:
	case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
		if (!limread_mpi(&key->key.elgamal.p, region, stream) ||
		    !limread_mpi(&key->key.elgamal.g, region, stream) ||
		    !limread_mpi(&key->key.elgamal.y, region, stream)) {
			return 0;
		}
		break;

	default:
		PGP_ERROR_1(&stream->errors,
			PGP_E_ALG_UNSUPPORTED_PUBLIC_KEY_ALG,
			"Unsupported Public Key algorithm (%s)",
			pgp_show_pka(key->alg));
		return 0;
	}

	return 1;
}


/**
 * \ingroup Core_ReadPackets
 * \brief Parse a public key packet.
 *
 * This function parses an entire v3 (== v2) or v4 public key packet for RSA, ElGamal, and DSA keys.
 *
 * Once the key has been parsed successfully, it is passed to the callback.
 *
 * \param *ptag		Pointer to the current Packet Tag.  This function should consume the entire packet.
 * \param *reader	Our reader
 * \param *cb		The callback
 * \return		1 on success, 0 on error
 *
 * \see RFC4880 5.5.2
 */
static int
parse_pubkey(pgp_content_enum tag, pgp_region_t *region,
		 pgp_stream_t *stream)
{
	pgp_packet_t pkt;

	if (!parse_pubkey_data(&pkt.u.pubkey, region, stream)) {
		(void) fprintf(stderr, "parse_pubkey: parse_pubkey_data failed\n");
		return 0;
	}

	/* XXX: this test should be done for all packets, surely? */
	if (region->readc != region->length) {
		PGP_ERROR_1(&stream->errors, PGP_E_R_UNCONSUMED_DATA,
			    "Unconsumed data (%d)", region->length - region->readc);
		return 0;
	}
	CALLBACK(tag, &stream->cbinfo, &pkt);

	return 1;
}

/**
 * \ingroup Core_ReadPackets
 * \brief Parse one user attribute packet.
 *
 * User attribute packets contain one or more attribute subpackets.
 * For now, handle the whole packet as raw data.
 */

static int
parse_userattr(pgp_region_t *region, pgp_stream_t *stream)
{

	pgp_packet_t pkt;

	/*
	 * xxx- treat as raw data for now. Could break down further into
	 * attribute sub-packets later - rachel
	 */
	if (region->readc != 0) {
		/* We should not have read anything so far */
		(void) fprintf(stderr, "parse_userattr: bad length\n");
		return 0;
	}
	if (!read_data(&pkt.u.userattr, region, stream)) {
		return 0;
	}
	CALLBACK(PGP_PTAG_CT_USER_ATTR, &stream->cbinfo, &pkt);
	return 1;
}

/**
\ingroup Core_Create
\brief Free allocated memory
*/
/* ! Free the memory used when parsing this packet type */
void
pgp_userid_free(uint8_t **id)
{
	free(*id);
	*id = NULL;
}

/**
 * \ingroup Core_ReadPackets
 * \brief Parse a user id.
 *
 * This function parses an user id packet, which is basically just a char array the size of the packet.
 *
 * The char array is to be treated as an UTF-8 string.
 *
 * The userid gets null terminated by this function.  Freeing it is the responsibility of the caller.
 *
 * Once the userid has been parsed successfully, it is passed to the callback.
 *
 * \param *ptag		Pointer to the Packet Tag.  This function should consume the entire packet.
 * \param *reader	Our reader
 * \param *cb		The callback
 * \return		1 on success, 0 on error
 *
 * \see RFC4880 5.11
 */
static int
parse_userid(pgp_region_t *region, pgp_stream_t *stream)
{
	pgp_packet_t pkt;

	 if (region->readc != 0) {
		/* We should not have read anything so far */
		(void) fprintf(stderr, "parse_userid: bad length\n");
		return 0;
	}

	if ((pkt.u.userid = calloc(1, region->length + 1)) == NULL) {
		(void) fprintf(stderr, "parse_userid: bad alloc\n");
		return 0;
	}

	if (region->length &&
	    !limread(pkt.u.userid, region->length, region,
			stream)) {
		return 0;
	}
	pkt.u.userid[region->length] = 0x0;
	CALLBACK(PGP_PTAG_CT_USER_ID, &stream->cbinfo, &pkt);
	return 1;
}

#if 0 // EDIT BY MR - sighash not needed
static pgp_hash_t     *
parse_hash_find(pgp_stream_t *stream, const uint8_t *keyid)
{
	pgp_hashtype_t	*hp;
	size_t			 n;

	for (n = 0, hp = stream->hashes; n < stream->hashc; n++, hp++) {
		if (memcmp(hp->keyid, keyid, PGP_KEY_ID_SIZE) == 0) {
			return &hp->hash;
		}
	}
	return NULL;
}
#endif

/**
 * \ingroup Core_Parse
 * \brief Parse a version 3 signature.
 *
 * This function parses an version 3 signature packet, handling RSA and DSA signatures.
 *
 * Once the signature has been parsed successfully, it is passed to the callback.
 *
 * \param *ptag		Pointer to the Packet Tag.  This function should consume the entire packet.
 * \param *reader	Our reader
 * \param *cb		The callback
 * \return		1 on success, 0 on error
 *
 * \see RFC4880 5.2.2
 */
static int
parse_v3_sig(pgp_region_t *region,
		   pgp_stream_t *stream)
{
	pgp_packet_t	pkt;
	uint8_t		c = 0x0;

	/* clear signature */
	(void) memset(&pkt.u.sig, 0x0, sizeof(pkt.u.sig));

	pkt.u.sig.info.version = PGP_V3;

	/* hash info length */
	if (!limread(&c, 1, region, stream)) {
		return 0;
	}
	if (c != 5) {
		ERRP(&stream->cbinfo, pkt, "bad hash info length");
	}

	if (!limread(&c, 1, region, stream)) {
		return 0;
	}
	pkt.u.sig.info.type = (pgp_sig_type_t)c;
	/* XXX: check signature type */

	if (!limited_read_time(&pkt.u.sig.info.birthtime, region, stream)) {
		return 0;
	}
	pkt.u.sig.info.birthtime_set = 1;

	if (!limread(pkt.u.sig.info.signer_id, PGP_KEY_ID_SIZE, region,
			stream)) {
		return 0;
	}
	pkt.u.sig.info.signer_id_set = 1;

	if (!limread(&c, 1, region, stream)) {
		return 0;
	}
	pkt.u.sig.info.key_alg = (pgp_pubkey_alg_t)c;
	/* XXX: check algorithm */

	if (!limread(&c, 1, region, stream)) {
		return 0;
	}
	pkt.u.sig.info.hash_alg = (pgp_hash_alg_t)c;
	/* XXX: check algorithm */

	if (!limread(pkt.u.sig.hash2, 2, region, stream)) {
		return 0;
	}

	switch (pkt.u.sig.info.key_alg) {
	case PGP_PKA_RSA:
	case PGP_PKA_RSA_SIGN_ONLY:
		if (!limread_mpi(&pkt.u.sig.info.sig.rsa.sig, region, stream)) {
			return 0;
		}
		break;

	case PGP_PKA_DSA:
		if (!limread_mpi(&pkt.u.sig.info.sig.dsa.r, region, stream) ||
		    !limread_mpi(&pkt.u.sig.info.sig.dsa.s, region, stream)) {
			return 0;
		}
		break;

	case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
		if (!limread_mpi(&pkt.u.sig.info.sig.elgamal.r, region,
				stream) ||
		    !limread_mpi(&pkt.u.sig.info.sig.elgamal.s, region,
		    		stream)) {
			return 0;
		}
		break;

	default:
		PGP_ERROR_1(&stream->errors,
			PGP_E_ALG_UNSUPPORTED_SIGNATURE_ALG,
			"Unsupported signature key algorithm (%s)",
			pgp_show_pka(pkt.u.sig.info.key_alg));
		return 0;
	}

	if (region->readc != region->length) {
		PGP_ERROR_1(&stream->errors, PGP_E_R_UNCONSUMED_DATA,
			"Unconsumed data (%d)",
			region->length - region->readc);
		return 0;
	}

	#if 0 // EDIT BY MR - sighash not needed
	if (pkt.u.sig.info.signer_id_set) {
		pkt.u.sig.hash = parse_hash_find(stream,
				pkt.u.sig.info.signer_id);
	}
	#endif

	CALLBACK(PGP_PTAG_CT_SIGNATURE, &stream->cbinfo, &pkt);
	return 1;
}

/**
 * \ingroup Core_ReadPackets
 * \brief Parse one signature sub-packet.
 *
 * Version 4 signatures can have an arbitrary amount of (hashed and
 * unhashed) subpackets.  Subpackets are used to hold optional
 * attributes of subpackets.
 *
 * This function parses one such signature subpacket.
 *
 * Once the subpacket has been parsed successfully, it is passed to the callback.
 *
 * \param *ptag		Pointer to the Packet Tag.  This function should consume the entire subpacket.
 * \param *reader	Our reader
 * \param *cb		The callback
 * \return		1 on success, 0 on error
 *
 * \see RFC4880 5.2.3
 */
static int
parse_one_sig_subpacket(pgp_sig_t *sig,
			      pgp_region_t *region,
			      pgp_stream_t *stream)
{
	pgp_region_t	subregion;
	pgp_packet_t	pkt;
	uint8_t		bools = 0x0;
	uint8_t		c = 0x0;
	unsigned	doread = 1;
	unsigned        t8;
	unsigned        t7;

	pgp_init_subregion(&subregion, region);
	if (!limited_read_new_length(&subregion.length, region, stream)) {
		return 0;
	}

	if (subregion.length > region->length) {
		ERRP(&stream->cbinfo, pkt, "Subpacket too long");
	}

	if (!limread(&c, 1, &subregion, stream)) {
		return 0;
	}

	t8 = (c & 0x7f) / 8;
	t7 = 1 << (c & 7);

	pkt.critical = (unsigned)c >> 7;
	pkt.tag = (pgp_content_enum)(PGP_PTAG_SIG_SUBPKT_BASE + (c & 0x7f));

	/* Application wants it delivered raw */
	if (stream->ss_raw[t8] & t7) {
		pkt.u.ss_raw.tag = pkt.tag;
		pkt.u.ss_raw.length = subregion.length - 1;
		pkt.u.ss_raw.raw = calloc(1, pkt.u.ss_raw.length);
		if (pkt.u.ss_raw.raw == NULL) {
			(void) fprintf(stderr, "parse_one_sig_subpacket: bad alloc\n");
			return 0;
		}
		if (!limread(pkt.u.ss_raw.raw, (unsigned)pkt.u.ss_raw.length,
				&subregion, stream)) {
			return 0;
		}
		CALLBACK(PGP_PTAG_RAW_SS, &stream->cbinfo, &pkt);
		return 1;
	}
	switch (pkt.tag) {
	case PGP_PTAG_SS_CREATION_TIME:
	case PGP_PTAG_SS_EXPIRATION_TIME:
	case PGP_PTAG_SS_KEY_EXPIRY:
		if (!limited_read_time(&pkt.u.ss_time, &subregion, stream))
			return 0;
		if (pkt.tag == PGP_PTAG_SS_CREATION_TIME) {
			sig->info.birthtime = pkt.u.ss_time;
			sig->info.birthtime_set = 1;
		}
		if (pkt.tag == PGP_PTAG_SS_EXPIRATION_TIME) {
			sig->info.duration = pkt.u.ss_time;
			sig->info.duration_set = 1;
		}
		if (pkt.tag == PGP_PTAG_SS_KEY_EXPIRY) {
			sig->info.key_expiry = pkt.u.ss_time;
			sig->info.key_expiry_set = 1;
		}
		break;

	case PGP_PTAG_SS_TRUST:
		if (!limread(&pkt.u.ss_trust.level, 1, &subregion, stream) ||
		    !limread(&pkt.u.ss_trust.amount, 1, &subregion, stream)) {
			return 0;
		}
		break;

	case PGP_PTAG_SS_REVOCABLE:
		if (!limread(&bools, 1, &subregion, stream)) {
			return 0;
		}
		pkt.u.ss_revocable = !!bools;
		break;

	case PGP_PTAG_SS_ISSUER_KEY_ID:
		if (!limread(pkt.u.ss_issuer, PGP_KEY_ID_SIZE, &subregion, stream)) {
			return 0;
		}
		(void) memcpy(sig->info.signer_id, pkt.u.ss_issuer, PGP_KEY_ID_SIZE);
		sig->info.signer_id_set = 1;
		break;

	case PGP_PTAG_SS_PREFERRED_SKA:
		if (!read_data(&pkt.u.ss_skapref, &subregion, stream)) {
			return 0;
		}
		break;

	case PGP_PTAG_SS_PREFERRED_HASH:
		if (!read_data(&pkt.u.ss_hashpref, &subregion, stream)) {
			return 0;
		}
		break;

	case PGP_PTAG_SS_PREF_COMPRESS:
		if (!read_data(&pkt.u.ss_zpref, &subregion, stream)) {
			return 0;
		}
		break;

	case PGP_PTAG_SS_PRIMARY_USER_ID:
		if (!limread(&bools, 1, &subregion, stream)) {
			return 0;
		}
		pkt.u.ss_primary_userid = !!bools;
		sig->info.primary_userid = pkt.u.ss_primary_userid;
		break;

	case PGP_PTAG_SS_KEY_FLAGS:
		if (!read_data(&pkt.u.ss_key_flags, &subregion, stream)) {
			return 0;
		}
		if(pkt.u.ss_key_flags.len > 0){
            /* Only one byte is defined in rfc4880 for now */
            sig->info.key_flags = pkt.u.ss_key_flags.contents[0];
            sig->info.key_flags_set = 1;
        }
		break;

	case PGP_PTAG_SS_KEYSERV_PREFS:
		if (!read_data(&pkt.u.ss_key_server_prefs, &subregion, stream)) {
			return 0;
		}
		break;

	case PGP_PTAG_SS_FEATURES:
		if (!read_data(&pkt.u.ss_features, &subregion, stream)) {
			return 0;
		}
		break;

	case PGP_PTAG_SS_SIGNERS_USER_ID:
		if (!read_unsig_str(&pkt.u.ss_signer, &subregion, stream)) {
			return 0;
		}
		break;

	case PGP_PTAG_SS_EMBEDDED_SIGNATURE:
		/* \todo should do something with this sig? */
		if (!read_data(&pkt.u.ss_embedded_sig, &subregion, stream)) {
			return 0;
		}
		break;

	case PGP_PTAG_SS_NOTATION_DATA:
		if (!limread_data(&pkt.u.ss_notation.flags, 4,
				&subregion, stream)) {
			return 0;
		}
		if (!limread_size_t(&pkt.u.ss_notation.name.len, 2,
				&subregion, stream)) {
			return 0;
		}
		if (!limread_size_t(&pkt.u.ss_notation.value.len, 2,
				&subregion, stream)) {
			return 0;
		}
		if (!limread_data(&pkt.u.ss_notation.name,
				(unsigned)pkt.u.ss_notation.name.len,
				&subregion, stream)) {
			return 0;
		}
		if (!limread_data(&pkt.u.ss_notation.value,
			   (unsigned)pkt.u.ss_notation.value.len,
			   &subregion, stream)) {
			return 0;
		}
		break;

	case PGP_PTAG_SS_POLICY_URI:
		if (!read_string(&pkt.u.ss_policy, &subregion, stream)) {
			return 0;
		}
		break;

	case PGP_PTAG_SS_REGEXP:
		if (!read_string(&pkt.u.ss_regexp, &subregion, stream)) {
			return 0;
		}
		break;

	case PGP_PTAG_SS_PREF_KEYSERV:
		if (!read_string(&pkt.u.ss_keyserv, &subregion, stream)) {
			return 0;
		}
		break;

	case PGP_PTAG_SS_USERDEFINED00:
	case PGP_PTAG_SS_USERDEFINED01:
	case PGP_PTAG_SS_USERDEFINED02:
	case PGP_PTAG_SS_USERDEFINED03:
	case PGP_PTAG_SS_USERDEFINED04:
	case PGP_PTAG_SS_USERDEFINED05:
	case PGP_PTAG_SS_USERDEFINED06:
	case PGP_PTAG_SS_USERDEFINED07:
	case PGP_PTAG_SS_USERDEFINED08:
	case PGP_PTAG_SS_USERDEFINED09:
	case PGP_PTAG_SS_USERDEFINED10:
		if (!read_data(&pkt.u.ss_userdef, &subregion, stream)) {
			return 0;
		}
		break;

	case PGP_PTAG_SS_RESERVED:
		if (!read_data(&pkt.u.ss_unknown, &subregion, stream)) {
			return 0;
		}
		break;

	case PGP_PTAG_SS_REVOCATION_REASON:
		/* first byte is the machine-readable code */
		if (!limread(&pkt.u.ss_revocation.code, 1, &subregion, stream)) {
			return 0;
		}
		/* the rest is a human-readable UTF-8 string */
		if (!read_string(&pkt.u.ss_revocation.reason, &subregion,
				stream)) {
			return 0;
		}
		break;

	case PGP_PTAG_SS_REVOCATION_KEY:
		/* octet 0 = class. Bit 0x80 must be set */
		if (!limread(&pkt.u.ss_revocation_key.class, 1,
				&subregion, stream)) {
			return 0;
		}
		if (!(pkt.u.ss_revocation_key.class & 0x80)) {
			printf("Warning: PGP_PTAG_SS_REVOCATION_KEY class: "
			       "Bit 0x80 should be set\n");
			return 0;
		}
		/* octet 1 = algid */
		if (!limread(&pkt.u.ss_revocation_key.algid, 1,
				&subregion, stream)) {
			return 0;
		}
		/* octets 2-21 = fingerprint */
		if (!limread(&pkt.u.ss_revocation_key.fingerprint[0],
				PGP_FINGERPRINT_SIZE, &subregion, stream)) {
			return 0;
		}
		break;

	default:
		if (stream->ss_parsed[t8] & t7) {
			PGP_ERROR_1(&stream->errors, PGP_E_PROTO_UNKNOWN_SS,
				    "Unknown signature subpacket type (%d)",
				    c & 0x7f);
		}
		doread = 0;
		break;
	}

	/* Application doesn't want it delivered parsed */
	if (!(stream->ss_parsed[t8] & t7)) {
		if (pkt.critical) {
			PGP_ERROR_1(&stream->errors,
				PGP_E_PROTO_CRITICAL_SS_IGNORED,
				"Critical signature subpacket ignored (%d)",
				c & 0x7f);
		}
		if (!doread &&
		    !limskip(subregion.length - 1, &subregion, stream)) {
			return 0;
		}
		if (doread) {
			pgp_parser_content_free(&pkt);
		}
		return 1;
	}
	if (doread && subregion.readc != subregion.length) {
		PGP_ERROR_1(&stream->errors, PGP_E_R_UNCONSUMED_DATA,
			    "Unconsumed data (%d)",
			    subregion.length - subregion.readc);
        pgp_parser_content_free(&pkt);
		return 0;
	}
	CALLBACK(pkt.tag, &stream->cbinfo, &pkt);
	return 1;
}

/**
 * \ingroup Core_ReadPackets
 * \brief Parse several signature subpackets.
 *
 * Hashed and unhashed subpacket sets are preceded by an octet count that specifies the length of the complete set.
 * This function parses this length and then calls parse_one_sig_subpacket() for each subpacket until the
 * entire set is consumed.
 *
 * This function does not call the callback directly, parse_one_sig_subpacket() does for each subpacket.
 *
 * \param *ptag		Pointer to the Packet Tag.
 * \param *reader	Our reader
 * \param *cb		The callback
 * \return		1 on success, 0 on error
 *
 * \see RFC4880 5.2.3
 */
static int
parse_sig_subpkts(pgp_sig_t *sig,
			   pgp_region_t *region,
			   pgp_stream_t *stream)
{
	pgp_region_t	subregion;
	pgp_packet_t	pkt;

	pgp_init_subregion(&subregion, region);
	if (!limread_scalar(&subregion.length, 2, region, stream)) {
		return 0;
	}

	if (subregion.length > region->length) {
		ERRP(&stream->cbinfo, pkt, "Subpacket set too long");
	}

	while (subregion.readc < subregion.length) {
		if (!parse_one_sig_subpacket(sig, &subregion, stream)) {
			return 0;
		}
	}

	if (subregion.readc != subregion.length) {
		if (!limskip(subregion.length - subregion.readc,
				&subregion, stream)) {
			ERRP(&stream->cbinfo, pkt,
"parse_sig_subpkts: subpacket length read mismatch");
		}
		ERRP(&stream->cbinfo, pkt, "Subpacket length mismatch");
	}
	return 1;
}

/**
 * \ingroup Core_ReadPackets
 * \brief Parse a version 4 signature.
 *
 * This function parses a version 4 signature including all its hashed and unhashed subpackets.
 *
 * Once the signature packet has been parsed successfully, it is passed to the callback.
 *
 * \param *ptag		Pointer to the Packet Tag.
 * \param *reader	Our reader
 * \param *cb		The callback
 * \return		1 on success, 0 on error
 *
 * \see RFC4880 5.2.3
 */
static int
parse_v4_sig(pgp_region_t *region, pgp_stream_t *stream)
{
	pgp_packet_t	pkt;
	uint8_t		c = 0x0;

	if (pgp_get_debug_level(__FILE__)) {
		fprintf(stderr, "\nparse_v4_sig\n");
	}
	/* clear signature */
	(void) memset(&pkt.u.sig, 0x0, sizeof(pkt.u.sig));

	/*
	 * We need to hash the packet data from version through the hashed
	 * subpacket data
	 */

	pkt.u.sig.v4_hashstart = stream->readinfo.alength - 1;

	/* Set version,type,algorithms */

	pkt.u.sig.info.version = PGP_V4;

	if (!limread(&c, 1, region, stream)) {
		return 0;
	}
	pkt.u.sig.info.type = (pgp_sig_type_t)c;
	if (pgp_get_debug_level(__FILE__)) {
		fprintf(stderr, "signature type=%d (%s)\n",
			pkt.u.sig.info.type,
			pgp_show_sig_type(pkt.u.sig.info.type));
	}
	/* XXX: check signature type */

	if (!limread(&c, 1, region, stream)) {
		return 0;
	}
	pkt.u.sig.info.key_alg = (pgp_pubkey_alg_t)c;
	/* XXX: check key algorithm */
	if (pgp_get_debug_level(__FILE__)) {
		(void) fprintf(stderr, "key_alg=%d (%s)\n",
			pkt.u.sig.info.key_alg,
			pgp_show_pka(pkt.u.sig.info.key_alg));
	}
	if (!limread(&c, 1, region, stream)) {
		return 0;
	}
	pkt.u.sig.info.hash_alg = (pgp_hash_alg_t)c;
	/* XXX: check hash algorithm */
	if (pgp_get_debug_level(__FILE__)) {
		fprintf(stderr, "hash_alg=%d %s\n",
			pkt.u.sig.info.hash_alg,
		  pgp_show_hash_alg(pkt.u.sig.info.hash_alg));
	}
	CALLBACK(PGP_PTAG_CT_SIGNATURE_HEADER, &stream->cbinfo, &pkt);

	if (!parse_sig_subpkts(&pkt.u.sig, region, stream)) {
		return 0;
	}

	pkt.u.sig.info.v4_hashlen = stream->readinfo.alength
					- pkt.u.sig.v4_hashstart;
	if (pgp_get_debug_level(__FILE__)) {
		fprintf(stderr, "v4_hashlen=%zd\n", pkt.u.sig.info.v4_hashlen);
	}

	/* copy hashed subpackets */
	if (pkt.u.sig.info.v4_hashed) {
		free(pkt.u.sig.info.v4_hashed);
	}
	pkt.u.sig.info.v4_hashed = calloc(1, pkt.u.sig.info.v4_hashlen);
	if (pkt.u.sig.info.v4_hashed == NULL) {
		(void) fprintf(stderr, "parse_v4_sig: bad alloc\n");
		return 0;
	}

	if (!stream->readinfo.accumulate) {
		/* We must accumulate, else we can't check the signature */
		fprintf(stderr, "*** ERROR: must set accumulate to 1\n");
        goto error_unalloc_v4_hashed;
	}
	(void) memcpy(pkt.u.sig.info.v4_hashed,
	       stream->readinfo.accumulated + pkt.u.sig.v4_hashstart,
	       pkt.u.sig.info.v4_hashlen);

	if (!parse_sig_subpkts(&pkt.u.sig, region, stream)) {
        goto error_unalloc_v4_hashed;
	}

	if (!limread(pkt.u.sig.hash2, 2, region, stream)) {
        goto error_unalloc_v4_hashed;
	}

	switch (pkt.u.sig.info.key_alg) {
	case PGP_PKA_RSA:
		if (!limread_mpi(&pkt.u.sig.info.sig.rsa.sig, region, stream)) {
            goto error_unalloc_v4_hashed;
		}
		if (pgp_get_debug_level(__FILE__)) {
			(void) fprintf(stderr, "parse_v4_sig: RSA: sig is\n");
			BN_print_fp(stderr, pkt.u.sig.info.sig.rsa.sig);
			(void) fprintf(stderr, "\n");
		}
		break;

	case PGP_PKA_DSA:
		if (!limread_mpi(&pkt.u.sig.info.sig.dsa.r, region, stream)) {
			/*
			 * usually if this fails, it just means we've reached
			 * the end of the keyring
			 */
			if (pgp_get_debug_level(__FILE__)) {
				(void) fprintf(stderr,
				"Error reading DSA r field in signature");
			}
            goto error_unalloc_v4_hashed;
		}
		if (!limread_mpi(&pkt.u.sig.info.sig.dsa.s, region, stream)) {
			ERRP(&stream->cbinfo, pkt,
			"Error reading DSA s field in signature");
		}
		break;

	case PGP_PKA_ELGAMAL_ENCRYPT_OR_SIGN:
		if (!limread_mpi(&pkt.u.sig.info.sig.elgamal.r, region,
				stream) ||
		    !limread_mpi(&pkt.u.sig.info.sig.elgamal.s, region,
		    		stream)) {
                goto error_unalloc_v4_hashed;
		}
		break;

	case PGP_PKA_PRIVATE00:
	case PGP_PKA_PRIVATE01:
	case PGP_PKA_PRIVATE02:
	case PGP_PKA_PRIVATE03:
	case PGP_PKA_PRIVATE04:
	case PGP_PKA_PRIVATE05:
	case PGP_PKA_PRIVATE06:
	case PGP_PKA_PRIVATE07:
	case PGP_PKA_PRIVATE08:
	case PGP_PKA_PRIVATE09:
	case PGP_PKA_PRIVATE10:
		if (!read_data(&pkt.u.sig.info.sig.unknown, region, stream)) {
            goto error_unalloc_v4_hashed;
		}
		break;

	default:
		PGP_ERROR_1(&stream->errors, PGP_E_ALG_UNSUPPORTED_SIGNATURE_ALG,
			    "Bad v4 signature key algorithm (%s)",
			    pgp_show_pka(pkt.u.sig.info.key_alg));
            goto error_unalloc_v4_hashed;
	}
	if (region->readc != region->length) {
		PGP_ERROR_1(&stream->errors, PGP_E_R_UNCONSUMED_DATA,
			    "Unconsumed data (%d)",
			    region->length - region->readc);
        goto error_unalloc_v4_hashed;
	}
	CALLBACK(PGP_PTAG_CT_SIGNATURE_FOOTER, &stream->cbinfo, &pkt);
	return 1;

error_unalloc_v4_hashed:
    free(pkt.u.sig.info.v4_hashed);
    return 0;

}

/**
 * \ingroup Core_ReadPackets
 * \brief Parse a signature subpacket.
 *
 * This function calls the appropriate function to handle v3 or v4 signatures.
 *
 * Once the signature packet has been parsed successfully, it is passed to the callback.
 *
 * \param *ptag		Pointer to the Packet Tag.
 * \param *reader	Our reader
 * \param *cb		The callback
 * \return		1 on success, 0 on error
 */
static int
parse_sig(pgp_region_t *region, pgp_stream_t *stream)
{
	pgp_packet_t	pkt;
	uint8_t		c = 0x0;

	if (region->readc != 0) {
		/* We should not have read anything so far */
		(void) fprintf(stderr, "parse_sig: bad length\n");
		return 0;
	}

	(void) memset(&pkt, 0x0, sizeof(pkt));
	if (!limread(&c, 1, region, stream)) {
		return 0;
	}
	if (c == 2 || c == 3) {
		return parse_v3_sig(region, stream);
	}
	if (c == 4) {
		return parse_v4_sig(region, stream);
	}
	PGP_ERROR_1(&stream->errors, PGP_E_PROTO_BAD_SIGNATURE_VRSN,
		    "Bad signature version (%d)", c);
	return 0;
}

/**
 \ingroup Core_ReadPackets
 \brief Parse Compressed packet
*/
static int
parse_compressed(pgp_region_t *region, pgp_stream_t *stream)
{
	pgp_packet_t	pkt;
	uint8_t		c = 0x0;

	if (!limread(&c, 1, region, stream)) {
		return 0;
	}

	pkt.u.compressed = (pgp_compression_type_t)c;

	CALLBACK(PGP_PTAG_CT_COMPRESSED, &stream->cbinfo, &pkt);

	/*
	 * The content of a compressed data packet is more OpenPGP packets
	 * once decompressed, so recursively handle them
	 */

	return pgp_decompress(region, stream, pkt.u.compressed);
}

/* XXX: this could be improved by sharing all hashes that are the */
/* same, then duping them just before checking the signature. */
static void
parse_hash_init(pgp_stream_t *stream, pgp_hash_alg_t type,
		    const uint8_t *keyid)
{
	pgp_hashtype_t *hash;

	hash = realloc(stream->hashes,
			      (stream->hashc + 1) * sizeof(*stream->hashes));
	if (hash == NULL) {
		(void) fprintf(stderr, "parse_hash_init: bad alloc 0\n");
		/* just continue and die here */
		/* XXX - agc - no way to return failure */
		exit(70); // EDIT BY MR - cannot allocate a few bytes? if this happends, we cannot do anything about it
	} else {
		stream->hashes = hash;
	}
	hash = &stream->hashes[stream->hashc++];

	pgp_hash_any(&hash->hash, type);
	if (!hash->hash.init(&hash->hash)) {
		(void) fprintf(stderr, "parse_hash_init: bad alloc\n");
		/* just continue and die here */
		/* XXX - agc - no way to return failure */
	}
	(void) memcpy(hash->keyid, keyid, sizeof(hash->keyid));
}


// EDIT BY MR: this function was declared but not implemented
void parse_hash_finish(pgp_stream_t* stream)
{
	if( stream->hashes ) {
		uint8_t		hashbuf[NETPGP_BUFSIZ];
		for (int i = 0; i<stream->hashc; i++) {
			stream->hashes[i].hash.finish(&stream->hashes[i].hash, hashbuf);
		}
		free(stream->hashes);
		stream->hashes = NULL;
	}
}

/**
   \ingroup Core_ReadPackets
   \brief Parse a One Pass Signature packet
*/
static int
parse_one_pass(pgp_region_t * region, pgp_stream_t * stream)
{
	pgp_packet_t	pkt;
	uint8_t		c = 0x0;

	if (!limread(&pkt.u.one_pass_sig.version, 1, region, stream)) {
		return 0;
	}
	if (pkt.u.one_pass_sig.version != 3) {
		PGP_ERROR_1(&stream->errors, PGP_E_PROTO_BAD_ONE_PASS_SIG_VRSN,
			    "Bad one-pass signature version (%d)",
			    pkt.u.one_pass_sig.version);
		return 0;
	}
	if (!limread(&c, 1, region, stream)) {
		return 0;
	}
	pkt.u.one_pass_sig.sig_type = (pgp_sig_type_t)c;

	if (!limread(&c, 1, region, stream)) {
		return 0;
	}
	pkt.u.one_pass_sig.hash_alg = (pgp_hash_alg_t)c;

	if (!limread(&c, 1, region, stream)) {
		return 0;
	}
	pkt.u.one_pass_sig.key_alg = (pgp_pubkey_alg_t)c;

	if (!limread(pkt.u.one_pass_sig.keyid,
			  (unsigned)sizeof(pkt.u.one_pass_sig.keyid),
			  region, stream)) {
		return 0;
	}

	if (!limread(&c, 1, region, stream)) {
		return 0;
	}
	pkt.u.one_pass_sig.nested = !!c;
	CALLBACK(PGP_PTAG_CT_1_PASS_SIG, &stream->cbinfo, &pkt);
	/* XXX: we should, perhaps, let the app choose whether to hash or not */
	parse_hash_init(stream, pkt.u.one_pass_sig.hash_alg,
			    pkt.u.one_pass_sig.keyid);
	return 1;
}

/**
 \ingroup Core_ReadPackets
 \brief Parse a Trust packet
*/
static int
parse_trust(pgp_region_t *region, pgp_stream_t *stream)
{
	pgp_packet_t pkt;

	if (!read_data(&pkt.u.trust, region, stream)) {
		return 0;
	}
	CALLBACK(PGP_PTAG_CT_TRUST, &stream->cbinfo, &pkt);
	return 1;
}

static void
parse_hash_data(pgp_stream_t *stream, const void *data,
		    size_t length)
{
	size_t          n;

	for (n = 0; n < stream->hashc; ++n) {
		stream->hashes[n].hash.add(&stream->hashes[n].hash, data, (unsigned)length);
	}
}

/**
   \ingroup Core_ReadPackets
   \brief Parse a Literal Data packet
*/
static int
parse_litdata(pgp_region_t *region, pgp_stream_t *stream)
{
	pgp_memory_t	*mem;
	pgp_packet_t	 pkt;
	uint8_t		 c = 0x0;

	memset(&pkt, 0, sizeof(pgp_packet_t));

	if (!limread(&c, 1, region, stream)) {
		return 0;
	}
	pkt.u.litdata_header.format = (pgp_litdata_enum)c;
	if (!limread(&c, 1, region, stream)) {
		return 0;
	}
	if (!limread((uint8_t *)pkt.u.litdata_header.filename,
			(unsigned)c, region, stream)) {
		return 0;
	}
	pkt.u.litdata_header.filename[c] = '\0';
	if (!limited_read_time(&pkt.u.litdata_header.mtime, region, stream)) {
		return 0;
	}
	CALLBACK(PGP_PTAG_CT_LITDATA_HEADER, &stream->cbinfo, &pkt);
	mem = pkt.u.litdata_body.mem = pgp_memory_new();
	pgp_memory_init(mem,
			(unsigned)((region->length * 101) / 100) + 12);
	pkt.u.litdata_body.data = mem->buf;

	while (region->readc < region->length) {
		unsigned        readc = region->length - region->readc;

		if (!limread(mem->buf, readc, region, stream)) {
			return 0;
		}
		pkt.u.litdata_body.length = readc;
		parse_hash_data(stream, pkt.u.litdata_body.data, region->length);
		CALLBACK(PGP_PTAG_CT_LITDATA_BODY, &stream->cbinfo, &pkt);
	}

	/* XXX - get rid of mem here? */
	pgp_memory_free(mem); // EDIT BY MR - fix memory leak

	return 1;
}

/**
 * \ingroup Core_Create
 *
 * pgp_seckey_free() frees the memory associated with "key". Note that
 * the key itself is not freed.
 *
 * \param key
 */

void
pgp_seckey_free(pgp_seckey_t *key)
{
	switch (key->pubkey.alg) {
	case PGP_PKA_RSA:
	case PGP_PKA_RSA_ENCRYPT_ONLY:
	case PGP_PKA_RSA_SIGN_ONLY:
		free_BN(&key->key.rsa.d);
		free_BN(&key->key.rsa.p);
		free_BN(&key->key.rsa.q);
		free_BN(&key->key.rsa.u);
		break;

	case PGP_PKA_DSA:
		free_BN(&key->key.dsa.x);
		break;

	default:
		(void) fprintf(stderr,
			"pgp_seckey_free: Unknown algorithm: %d (%s)\n",
			key->pubkey.alg,
			pgp_show_pka(key->pubkey.alg));
	}
	free(key->checkhash);
    pgp_pubkey_free(&key->pubkey);
    memset(key, 0, sizeof(*key));
}

int
pgp_seckey_dup(pgp_seckey_t *dst, pgp_seckey_t *src)
{
    memcpy(dst, src, sizeof(*src));

	switch (src->pubkey.alg) {
	case PGP_PKA_RSA:
	case PGP_PKA_RSA_ENCRYPT_ONLY:
	case PGP_PKA_RSA_SIGN_ONLY:
		dup_BN(&dst->key.rsa.d, src->key.rsa.d);
		dup_BN(&dst->key.rsa.p, src->key.rsa.p);
		dup_BN(&dst->key.rsa.q, src->key.rsa.q);
		dup_BN(&dst->key.rsa.u, src->key.rsa.u);
		break;

	case PGP_PKA_DSA:
		dup_BN(&dst->key.dsa.x, src->key.dsa.x);
		break;

	default:
		(void) fprintf(stderr,
			"pgp_seckey_dup: Unknown algorithm: %d (%s)\n",
			src->pubkey.alg,
			pgp_show_pka(src->pubkey.alg));
        return 0;
	}

    if(src->checkhash) {
        if ((dst->checkhash = calloc(1, PGP_CHECKHASH_SIZE)) == NULL) {
            (void) fprintf(stderr, "pgp_seckey_dup: bad alloc\n");
            return 0;
        }else{
            memcpy(dst->checkhash, src->checkhash, PGP_CHECKHASH_SIZE);
        }
    }

    pgp_pubkey_dup(&dst->pubkey, &src->pubkey);

    /*TODO alloc error handling */
    return 1;
}

static int
consume_packet(pgp_region_t *region, pgp_stream_t *stream, unsigned warn)
{
	pgp_packet_t	pkt;
	pgp_data_t	remainder;

	if (region->indeterminate) {
		ERRP(&stream->cbinfo, pkt,
			"Can't consume indeterminate packets");
	}

	if (read_data(&remainder, region, stream)) {
		/* now throw it away */
		pgp_data_free(&remainder);
		if (warn) {
			PGP_ERROR_1(&stream->errors, PGP_E_P_PACKET_CONSUMED,
			    "%s", "Warning: packet consumer");
		}
		return 1;
	}
	PGP_ERROR_1(&stream->errors, PGP_E_P_PACKET_NOT_CONSUMED,
	    "%s", (warn) ? "Warning: Packet was not consumed" :
	    "Packet was not consumed");
	return warn;
}

/**
 * \ingroup Core_ReadPackets
 * \brief Parse a secret key
 */
static int
parse_seckey(pgp_content_enum tag, pgp_region_t *region, pgp_stream_t *stream)
{
	pgp_packet_t		pkt;
	pgp_region_t		encregion;
	pgp_region_t	       *saved_region = NULL;
	pgp_crypt_t		decrypt;
	pgp_hash_t		checkhash;
	unsigned		blocksize;
	unsigned		crypted;
	uint8_t			c = 0x0;
	int			ret = 1;

	if (pgp_get_debug_level(__FILE__)) {
		fprintf(stderr, "\n---------\nparse_seckey:\n");
		fprintf(stderr,
			"region length=%u, readc=%u, remainder=%u\n",
			region->length, region->readc,
			region->length - region->readc);
	}
	(void) memset(&pkt, 0x0, sizeof(pkt));
	if (!parse_pubkey_data(&pkt.u.seckey.pubkey, region, stream)) {
		return 0;
	}
	if (pgp_get_debug_level(__FILE__)) {
		fprintf(stderr, "parse_seckey: public key parsed\n");
		//pgp_print_pubkey(&pkt.u.seckey.pubkey);
	}
	//stream->reading_v3_secret = (pkt.u.seckey.pubkey.version != PGP_V4);
    if (pkt.u.seckey.pubkey.version != PGP_V4){
        (void) fprintf(stderr,
                       "parse_seckey: Only V4 is supported\n");
        return 0;
    }

	if (!limread(&c, 1, region, stream)) {
		return 0;
	}
	pkt.u.seckey.s2k_usage = (pgp_s2k_usage_t)c;

	if (pkt.u.seckey.s2k_usage == PGP_S2KU_ENCRYPTED ||
	    pkt.u.seckey.s2k_usage == PGP_S2KU_ENCRYPTED_AND_HASHED) {
		if (!limread(&c, 1, region, stream)) {
			return 0;
		}
		pkt.u.seckey.alg = (pgp_symm_alg_t)c;
		if (!limread(&c, 1, region, stream)) {
			return 0;
		}
		pkt.u.seckey.s2k_specifier = (pgp_s2k_specifier_t)c;
		switch (pkt.u.seckey.s2k_specifier) {
		case PGP_S2KS_SIMPLE:
		case PGP_S2KS_SALTED:
		case PGP_S2KS_ITERATED_AND_SALTED:
			break;
		default:
			(void) fprintf(stderr,
				"parse_seckey: bad seckey\n");
			return 0;
		}
		if (!limread(&c, 1, region, stream)) {
			return 0;
		}
		pkt.u.seckey.hash_alg = (pgp_hash_alg_t)c;
		if (pkt.u.seckey.s2k_specifier != PGP_S2KS_SIMPLE &&
		    !limread(pkt.u.seckey.salt, 8, region, stream)) {
			return 0;
		}
		if (pkt.u.seckey.s2k_specifier ==
					PGP_S2KS_ITERATED_AND_SALTED) {
			if (!limread(&c, 1, region, stream)) {
				return 0;
			}
			pkt.u.seckey.octetc =
				(16 + ((unsigned)c & 15)) <<
						(((unsigned)c >> 4) + 6);
		}
	} else if (pkt.u.seckey.s2k_usage != PGP_S2KU_NONE) {
		/* this is V3 style, looks just like a V4 simple hash */
		pkt.u.seckey.alg = (pgp_symm_alg_t)c;
		pkt.u.seckey.s2k_usage = PGP_S2KU_ENCRYPTED;
		pkt.u.seckey.s2k_specifier = PGP_S2KS_SIMPLE;
		pkt.u.seckey.hash_alg = PGP_HASH_MD5;
	}
	crypted = pkt.u.seckey.s2k_usage == PGP_S2KU_ENCRYPTED ||
		pkt.u.seckey.s2k_usage == PGP_S2KU_ENCRYPTED_AND_HASHED;

	if (crypted) {
		pgp_packet_t	seckey;
		pgp_hash_t	hashes[(PGP_MAX_KEY_SIZE + PGP_MIN_HASH_SIZE - 1) / PGP_MIN_HASH_SIZE];
		unsigned	passlen;
		uint8_t   	key[PGP_MAX_KEY_SIZE + PGP_MAX_HASH_SIZE];
		char           *passphrase;
		int             hashsize;
		int             keysize;
		int             n;

		if (pgp_get_debug_level(__FILE__)) {
			(void) fprintf(stderr, "crypted seckey\n");
		}
		blocksize = pgp_block_size(pkt.u.seckey.alg);
		if (blocksize == 0 || blocksize > PGP_MAX_BLOCK_SIZE) {
			(void) fprintf(stderr,
				"parse_seckey: bad blocksize\n");
			return 0;
		}

		if (!limread(pkt.u.seckey.iv, blocksize, region, stream)) {
			return 0;
		}
		(void) memset(&seckey, 0x0, sizeof(seckey));
		passphrase = NULL;
		seckey.u.skey_passphrase.passphrase = &passphrase;
		seckey.u.skey_passphrase.seckey = &pkt.u.seckey;
		CALLBACK(PGP_GET_PASSPHRASE, &stream->cbinfo, &seckey);
		if (!passphrase) {
			if (pgp_get_debug_level(__FILE__)) {
				/* \todo make into proper error */
				(void) fprintf(stderr,
				"parse_seckey: can't get passphrase\n");
			}
			if (!consume_packet(region, stream, 0)) {
				return 0;
			}

			CALLBACK(PGP_PTAG_CT_ENCRYPTED_SECRET_KEY,
				&stream->cbinfo, &pkt);

			return 1;
		}
		keysize = pgp_key_size(pkt.u.seckey.alg);
		if (keysize == 0 || keysize > PGP_MAX_KEY_SIZE) {
			(void) fprintf(stderr,
				"parse_seckey: bad keysize\n");
			return 0;
		}

		/* Hardcoded SHA1 for just now */
		pkt.u.seckey.hash_alg = PGP_HASH_SHA1;
		hashsize = pgp_hash_size(pkt.u.seckey.hash_alg);
		if (hashsize == 0 || hashsize > PGP_MAX_HASH_SIZE) {
			(void) fprintf(stderr,
				"parse_seckey: bad hashsize\n");
			return 0;
		}

		for (n = 0; n * hashsize < keysize; ++n) {
			int             i;

			pgp_hash_any(&hashes[n],
				pkt.u.seckey.hash_alg);
			if (!hashes[n].init(&hashes[n])) {
				(void) fprintf(stderr,
					"parse_seckey: bad alloc\n");
				return 0;
			}
			/* preload hashes with zeroes... */
			for (i = 0; i < n; ++i) {
				hashes[n].add(&hashes[n],
					(const uint8_t *) "", 1);
			}
		}
		passlen = (unsigned)strlen(passphrase);
		for (n = 0; n * hashsize < keysize; ++n) {
			unsigned        i;

			switch (pkt.u.seckey.s2k_specifier) {
			case PGP_S2KS_SALTED:
				hashes[n].add(&hashes[n],
					pkt.u.seckey.salt,
					PGP_SALT_SIZE);
				/* FALLTHROUGH */
			case PGP_S2KS_SIMPLE:
				hashes[n].add(&hashes[n],
					(uint8_t *)passphrase, (unsigned)passlen);
				break;

			case PGP_S2KS_ITERATED_AND_SALTED:
				for (i = 0; i < pkt.u.seckey.octetc;
						i += passlen + PGP_SALT_SIZE) {
					unsigned	j;

					j = passlen + PGP_SALT_SIZE;
					if (i + j > pkt.u.seckey.octetc && i != 0) {
						j = pkt.u.seckey.octetc - i;
					}
					hashes[n].add(&hashes[n],
						pkt.u.seckey.salt,
						(unsigned)(j > PGP_SALT_SIZE) ?
							PGP_SALT_SIZE : j);
					if (j > PGP_SALT_SIZE) {
						hashes[n].add(&hashes[n],
						(uint8_t *) passphrase,
						j - PGP_SALT_SIZE);
					}
				}
				break;
			default:
				break;
			}
		}

		for (n = 0; n * hashsize < keysize; ++n) {
			int	r;

			r = hashes[n].finish(&hashes[n], key + n * hashsize);
			if (r != hashsize) {
				(void) fprintf(stderr,
					"parse_seckey: bad r\n");
				return 0;
			}
		}

		pgp_forget(passphrase, passlen);

		if( !pgp_crypt_any(&decrypt, pkt.u.seckey.alg) ) {
			return 0; // EDIT BY MR
		}
		if (pgp_get_debug_level(__FILE__)) {
			hexdump(stderr, "input iv", pkt.u.seckey.iv, pgp_block_size(pkt.u.seckey.alg));
			hexdump(stderr, "key", key, CAST_KEY_LENGTH);
		}
		decrypt.set_iv(&decrypt, pkt.u.seckey.iv);
		decrypt.set_crypt_key(&decrypt, key);

		/* now read encrypted data */

		pgp_reader_push_decrypt(stream, &decrypt, region);

		/*
		 * Since all known encryption for PGP doesn't compress, we
		 * can limit to the same length as the current region (for
		 * now).
		 */
		pgp_init_subregion(&encregion, NULL);
		encregion.length = region->length - region->readc;
		if (pkt.u.seckey.pubkey.version != PGP_V4) {
			encregion.length -= 2;
		}
		saved_region = region;
		region = &encregion;
	}
	if (pgp_get_debug_level(__FILE__)) {
		fprintf(stderr, "parse_seckey: end of crypted passphrase\n");
	}

    /* XXX - Hard-coded SHA1 here ?? Check */
	if (pkt.u.seckey.s2k_usage == PGP_S2KU_ENCRYPTED_AND_HASHED) {
        pkt.u.seckey.checkhash = calloc(1, PGP_SHA1_HASH_SIZE);
		if (pkt.u.seckey.checkhash == NULL) {
			(void) fprintf(stderr, "parse_seckey: bad alloc\n");
			return 0;
		}
		pgp_hash_sha1(&checkhash);
		pgp_reader_push_hash(stream, &checkhash);
	} else {
		pgp_reader_push_sum16(stream);
	}
	if (pgp_get_debug_level(__FILE__)) {
		fprintf(stderr, "parse_seckey: checkhash, reading MPIs\n");
	}
	switch (pkt.u.seckey.pubkey.alg) {
	case PGP_PKA_RSA:
	case PGP_PKA_RSA_ENCRYPT_ONLY:
	case PGP_PKA_RSA_SIGN_ONLY:
		if (!limread_mpi(&pkt.u.seckey.key.rsa.d, region, stream) ||
		    !limread_mpi(&pkt.u.seckey.key.rsa.p, region, stream) ||
		    !limread_mpi(&pkt.u.seckey.key.rsa.q, region, stream) ||
		    !limread_mpi(&pkt.u.seckey.key.rsa.u, region, stream) /*||
            !pgp_rsa_private_check(&pkt.u.seckey)*/) {
			ret = 0;
		}
		break;

	case PGP_PKA_DSA:
            if (!limread_mpi(&pkt.u.seckey.key.dsa.x, region, stream) /*||
                !pgp_dsa_private_check(&pkt.u.seckey)*/) {
			ret = 0;
		}
		break;

	case PGP_PKA_ELGAMAL:
            if (!limread_mpi(&pkt.u.seckey.key.elgamal.x, region, stream) /*||
                !pgp_elgamal_private_check(&pkt.u.seckey)*/) {
			ret = 0;
		}
		break;

	default:
		PGP_ERROR_2(&stream->errors,
			PGP_E_ALG_UNSUPPORTED_PUBLIC_KEY_ALG,
			"Unsupported Public Key algorithm %d (%s)",
			pkt.u.seckey.pubkey.alg,
			pgp_show_pka(pkt.u.seckey.pubkey.alg));
		ret = 0;
	}

	if (pgp_get_debug_level(__FILE__)) {
		(void) fprintf(stderr, "4 MPIs read\n");
	}
	//stream->reading_v3_secret = 0;

	if (pkt.u.seckey.s2k_usage == PGP_S2KU_ENCRYPTED_AND_HASHED) {
		uint8_t   hash[PGP_CHECKHASH_SIZE];

		pgp_reader_pop_hash(stream);
		checkhash.finish(&checkhash, hash);

		if (crypted &&
		    pkt.u.seckey.pubkey.version != PGP_V4) {
			pgp_reader_pop_decrypt(stream);
			region = saved_region;
		}
		if (ret) {
			if (!limread(pkt.u.seckey.checkhash,
				PGP_CHECKHASH_SIZE, region, stream)) {
				return 0;
			}

			if (memcmp(hash, pkt.u.seckey.checkhash,
					PGP_CHECKHASH_SIZE) != 0) {
				ERRP(&stream->cbinfo, pkt,
					"Hash mismatch in secret key");
			}
		}
	} else {
		uint16_t  sum;

		sum = pgp_reader_pop_sum16(stream);
		if (crypted &&
		    pkt.u.seckey.pubkey.version != PGP_V4) {
			pgp_reader_pop_decrypt(stream);
			region = saved_region;
		}
		if (ret) {
			if (!limread_scalar(&pkt.u.seckey.checksum, 2,
					region, stream))
				return 0;

			if (sum != pkt.u.seckey.checksum) {
				ERRP(&stream->cbinfo, pkt,
					"Checksum mismatch in secret key");
			}
		}
	}

	if (crypted && pkt.u.seckey.pubkey.version == PGP_V4) {
		pgp_reader_pop_decrypt(stream);
	}
	if (region == NULL) {
		(void) fprintf(stderr, "parse_seckey: NULL region\n");
		return 0;
	}
	if (ret && region->readc != region->length) {
		(void) fprintf(stderr, "parse_seckey: bad length\n");
		return 0;
	}
	if (!ret) {
		return 0;
	}
	CALLBACK(tag, &stream->cbinfo, &pkt);
	if (pgp_get_debug_level(__FILE__)) {
		(void) fprintf(stderr, "--- end of parse_seckey\n\n");
	}
	return 1;
}

/**
   \ingroup Core_ReadPackets
   \brief Parse a Public Key Session Key packet
*/
static int
parse_pk_sesskey(pgp_region_t *region,
		     pgp_stream_t *stream)
{
	const pgp_seckey_t	*secret;
	pgp_packet_t		 sesskey;
	pgp_packet_t		 pkt;
	uint8_t			*iv;
	uint8_t		   	 c = 0x0;
	uint8_t			 cs[2];
	unsigned		 k;
	BIGNUM			*g_to_k;
	BIGNUM			*enc_m;
	int			 n;
	uint8_t		 	 unencoded_m_buf[1024];

	if (!limread(&c, 1, region, stream)) {
		(void) fprintf(stderr, "parse_pk_sesskey - can't read char in region\n");
		return 0;
	}
	pkt.u.pk_sesskey.version = c;
	if (pkt.u.pk_sesskey.version != 3) {
		PGP_ERROR_1(&stream->errors, PGP_E_PROTO_BAD_PKSK_VRSN,
			"Bad public-key encrypted session key version (%d)",
			    pkt.u.pk_sesskey.version);
		return 0;
	}
	if (!limread(pkt.u.pk_sesskey.key_id,
			  (unsigned)sizeof(pkt.u.pk_sesskey.key_id), region, stream)) {
		return 0;
	}
	if (pgp_get_debug_level(__FILE__)) {
		hexdump(stderr, "sesskey: pubkey id", pkt.u.pk_sesskey.key_id, sizeof(pkt.u.pk_sesskey.key_id));
	}
	if (!limread(&c, 1, region, stream)) {
		return 0;
	}
	pkt.u.pk_sesskey.alg = (pgp_pubkey_alg_t)c;
	switch (pkt.u.pk_sesskey.alg) {
	case PGP_PKA_RSA:
		if (!limread_mpi(&pkt.u.pk_sesskey.params.rsa.encrypted_m,
				      region, stream)) {
			return 0;
		}
		enc_m = pkt.u.pk_sesskey.params.rsa.encrypted_m;
		g_to_k = NULL;
		break;

	case PGP_PKA_DSA:
	case PGP_PKA_ELGAMAL:
		if (!limread_mpi(&pkt.u.pk_sesskey.params.elgamal.g_to_k,
				      region, stream) ||
		    !limread_mpi(
			&pkt.u.pk_sesskey.params.elgamal.encrypted_m,
					 region, stream)) {
			return 0;
		}
		g_to_k = pkt.u.pk_sesskey.params.elgamal.g_to_k;
		enc_m = pkt.u.pk_sesskey.params.elgamal.encrypted_m;
		break;

	default:
		PGP_ERROR_1(&stream->errors,
			PGP_E_ALG_UNSUPPORTED_PUBLIC_KEY_ALG,
			"Unknown public key algorithm in session key (%s)",
			pgp_show_pka(pkt.u.pk_sesskey.alg));
		return 0;
	}

	(void) memset(&sesskey, 0x0, sizeof(sesskey));
	secret = NULL;
	sesskey.u.get_seckey.seckey = &secret;
	sesskey.u.get_seckey.pk_sesskey = &pkt.u.pk_sesskey;

	if (pgp_get_debug_level(__FILE__)) {
		(void) fprintf(stderr, "getting secret key via callback\n");
	}

	CALLBACK(PGP_GET_SECKEY, &stream->cbinfo, &sesskey);

	if (pgp_get_debug_level(__FILE__)) {
		(void) fprintf(stderr, "got secret key via callback\n");
	}
	if (!secret) {
		CALLBACK(PGP_PTAG_CT_ENCRYPTED_PK_SESSION_KEY, &stream->cbinfo,
			&pkt);
		return 1;
	}
	n = pgp_decrypt_decode_mpi(unencoded_m_buf,
		(unsigned)sizeof(unencoded_m_buf), g_to_k, enc_m, secret);

	if (n < 1) {
		ERRP(&stream->cbinfo, pkt, "decrypted message too short");
		return 0;
	}

	/* PKA */
	pkt.u.pk_sesskey.symm_alg = (pgp_symm_alg_t)unencoded_m_buf[0];
	if (pgp_get_debug_level(__FILE__)) {
		(void) fprintf(stderr, "symm alg %d\n", pkt.u.pk_sesskey.symm_alg);
	}

	if (!pgp_is_sa_supported(pkt.u.pk_sesskey.symm_alg)) {
		/* ERR1P */
		PGP_ERROR_1(&stream->errors, PGP_E_ALG_UNSUPPORTED_SYMMETRIC_ALG,
			    "Symmetric algorithm %s not supported",
			    pgp_show_symm_alg(
				pkt.u.pk_sesskey.symm_alg));
		return 0;
	}
	k = pgp_key_size(pkt.u.pk_sesskey.symm_alg);
	if (pgp_get_debug_level(__FILE__)) {
		(void) fprintf(stderr, "key size %d\n", k);
	}

	if ((unsigned) n != k + 3) {
		PGP_ERROR_2(&stream->errors, PGP_E_PROTO_DECRYPTED_MSG_WRONG_LEN,
		      "decrypted message wrong length (got %d expected %d)",
			    n, k + 3);
		return 0;
	}
	if (k > sizeof(pkt.u.pk_sesskey.key)) {
		(void) fprintf(stderr, "parse_pk_sesskey: bad keylength\n");
		return 0;
	}

	(void) memcpy(pkt.u.pk_sesskey.key, unencoded_m_buf + 1, k);

	if (pgp_get_debug_level(__FILE__)) {
		hexdump(stderr, "recovered sesskey", pkt.u.pk_sesskey.key, k);
	}
	pkt.u.pk_sesskey.checksum = unencoded_m_buf[k + 1] +
			(unencoded_m_buf[k + 2] << 8);
	if (pgp_get_debug_level(__FILE__)) {
		(void) fprintf(stderr, "session key checksum: %2x %2x\n",
			unencoded_m_buf[k + 1], unencoded_m_buf[k + 2]);
	}

	/* Check checksum */
	pgp_calc_sesskey_checksum(&pkt.u.pk_sesskey, &cs[0]);
	if (unencoded_m_buf[k + 1] != cs[0] ||
	    unencoded_m_buf[k + 2] != cs[1]) {
		PGP_ERROR_4(&stream->errors, PGP_E_PROTO_BAD_SK_CHECKSUM,
		"Session key checksum wrong: expected %2x %2x, got %2x %2x",
		cs[0], cs[1], unencoded_m_buf[k + 1],
		unencoded_m_buf[k + 2]);
		return 0;
	}

	if (pgp_get_debug_level(__FILE__)) {
		(void) fprintf(stderr, "getting pk session key via callback\n");
	}
	/* all is well */
	CALLBACK(PGP_PTAG_CT_PK_SESSION_KEY, &stream->cbinfo, &pkt);
	if (pgp_get_debug_level(__FILE__)) {
		(void) fprintf(stderr, "got pk session key via callback\n");
	}

	if( !pgp_crypt_any(&stream->decrypt, pkt.u.pk_sesskey.symm_alg) ) {
		return 0; // EDIT BY MR
	}
	iv = calloc(1, stream->decrypt.blocksize);
	if (iv == NULL) {
		(void) fprintf(stderr, "parse_pk_sesskey: bad alloc\n");
		return 0;
	}
	stream->decrypt.set_iv(&stream->decrypt, iv);
	stream->decrypt.set_crypt_key(&stream->decrypt, pkt.u.pk_sesskey.key);
	pgp_encrypt_init(&stream->decrypt);
	free(iv);
	return 1;
}

// EDIT BY MR
uint8_t* pgp_s2k_do(const char* passphrase,
                    int wanted_key_len,
                    pgp_s2k_specifier_t s2k_spec, pgp_hash_alg_t s2k_hash_algo, const uint8_t* s2k_salt, int s2k_iter_id)
{
	#define     S2K_MIN(X, Y) (((X) < (Y))? (X) : (Y))
	unsigned    done = 0;
	unsigned    i = 0;
	int         passphrase_len = strlen(passphrase);
	pgp_hash_t  hash;
	#define     EXPBIAS 6
	int         s2k_iter_count = (16 + (s2k_iter_id & 15)) << ((s2k_iter_id >> 4) + EXPBIAS);
	uint8_t     *key = calloc(1, wanted_key_len);
	if( key == NULL ) {
		return NULL;
	}

	for (done = 0, i = 0; done < wanted_key_len; i++) {
		unsigned    hashsize;
		unsigned    j;
		unsigned    needed;
		unsigned    size;
		uint8_t     zero = 0;
		uint8_t     *hashed;

		pgp_hash_any(&hash, s2k_hash_algo);
		hashsize = pgp_hash_size(s2k_hash_algo);
		needed = wanted_key_len - done;
		size = S2K_MIN(needed, hashsize);
		if ((hashed = calloc(1, hashsize)) == NULL) {
			free(key);
			return NULL;
		}
		if (!hash.init(&hash)) {
			free(hashed);
			free(key);
			return NULL;
		}

		/* preload if iterating  */
		for (j = 0; j < i; j++) {
			/*
			 * Coverity shows a DEADCODE error on this
			 * line. This is expected since the hardcoded
			 * use of SHA1 and CAST5 means that it will
			 * not used. This will change however when
			 * other algorithms are supported.
			 */
			hash.add(&hash, &zero, 1);
		}

		if (s2k_spec == PGP_S2KS_ITERATED_AND_SALTED )
		{
			int remaining_octets = s2k_iter_count;
			while( 1 )
			{
				int hash_now = S2K_MIN(PGP_SALT_SIZE, remaining_octets);
				hash.add(&hash, s2k_salt, hash_now);
				remaining_octets -= hash_now;
				if( remaining_octets<=0 ) {
					break;
				}

				hash_now = S2K_MIN(passphrase_len, remaining_octets);
				hash.add(&hash, (uint8_t*)passphrase, hash_now);
				remaining_octets -= hash_now;
				if( remaining_octets<=0 ) {
					break;
				}
			}
		}
		else
		{
			if (s2k_spec == PGP_S2KS_SALTED) {
				hash.add(&hash, s2k_salt, PGP_SALT_SIZE);
			}
			hash.add(&hash, (uint8_t*)passphrase, (unsigned)passphrase_len);
		}

		hash.finish(&hash, hashed);

		/*
		 * if more in hash than is needed by session key, use
		 * the leftmost octets
		 */
		(void) memcpy(&key[i * hashsize], hashed, (unsigned)size);
		done += (unsigned)size;
		free(hashed);
		if (done > wanted_key_len) {
			free(key);
			return NULL;
		}
	}
	return key;
}
// /EDIT BY MR

// EDIT BY MR - parse Symmetric-Key Encrypted Session Key Packets (Tag 3)
static int parse_sk_sesskey(pgp_region_t *region, pgp_stream_t *stream)
{
	int         success = 0;
	uint8_t     version = 0, algo = 0;
	pgp_crypt_t ci;
	uint8_t     s2k_spec = 0, s2k_hash_algo = 0, s2k_salt[PGP_SALT_SIZE], s2k_iter_id = 0;
	uint8_t*    encr_session_key = NULL, *decr_session_key = NULL;
	int         session_key_bytes = 0;
	uint8_t*    iv = NULL;
	uint8_t*    key = NULL;

	if( region == NULL || stream == NULL || stream->cbinfo.cryptinfo.symm_passphrase == NULL ) {
		goto cleanup;
	}

	/* 2 bytes - Version & algorithm  */
	if( !limread(&version, 1, region, stream) || version != 4
	 || !limread(&algo, 1, region, stream) ) {
		goto cleanup;
	}

	/* n bytes - S2K specifier */
	if (!limread(&s2k_spec, 1, region, stream)
	 || (s2k_spec!=PGP_S2KS_SIMPLE && s2k_spec!=PGP_S2KS_SALTED && s2k_spec!=PGP_S2KS_ITERATED_AND_SALTED)
	 || !limread(&s2k_hash_algo, 1, region, stream) ) {
		goto cleanup;
	}

	if( s2k_spec==PGP_S2KS_SALTED || s2k_spec==PGP_S2KS_ITERATED_AND_SALTED ) {
		if (!limread(s2k_salt, PGP_SALT_SIZE, region, stream)) {
			goto cleanup;
		}
	}

	if( s2k_spec==PGP_S2KS_ITERATED_AND_SALTED ) {
		if (!limread(&s2k_iter_id, 1, region, stream)) {
			goto cleanup;
		}
	}

	/* calculate the key from the passphrase */
	if( !pgp_crypt_any(&ci, algo) ) {
		goto cleanup;
	}
	if( (key = pgp_s2k_do(stream->cbinfo.cryptinfo.symm_passphrase, ci.keysize,
						  s2k_spec, s2k_hash_algo, s2k_salt, s2k_iter_id)) == NULL ) {
		goto cleanup;
	}

	/* set up stream->decrypt so that PGP_PTAG_CT_SE_IP_DATA can decrypt the data
	as it does for "Public-Key Encrypted Session Key Packets (Tag 1)" */
	if( region->length > region->readc )
	{
		/* n bytes - Encrypted session key _is_ present. From RFC 4880:
		"[...] The result of applying the S2K algorithm to the passphrase is
		used to decrypt just that encrypted session key field, using CFB mode
		with an IV of all zeros. The decryption result consists of a one-octet
		algorithm identifier that specifies the symmetric-key encryption
		algorithm used to encrypt the following Symmetrically Encrypted Data
		packet, followed by the session key octets themselves. */

		/* read the data */
		session_key_bytes = region->length - region->readc;
		if( (encr_session_key=malloc(session_key_bytes)) == NULL
		 || (decr_session_key=malloc(session_key_bytes)) == NULL
		 || !limread(encr_session_key, session_key_bytes, region, stream) ) {
			goto cleanup;
		}

		/* decrypt the read data */
		iv = calloc(1, ci.blocksize); if( iv == NULL ) { goto cleanup; } // EDIT BY MR
		ci.set_iv(&ci, iv);
		free(iv);
		iv = NULL;
		ci.set_crypt_key(&ci, key);
		pgp_encrypt_init(&ci);
		pgp_decrypt_se_ip(&ci, decr_session_key, encr_session_key, session_key_bytes); /* pgp_decrypt_se_ip() decrypts using CFB mode, pgp_decrypt_se() would not */
		ci.decrypt_finish(&ci);

		/* use decrypted data as key */
		algo = decr_session_key[0];
		if( !pgp_crypt_any(&stream->decrypt, algo)
		 || stream->decrypt.keysize != (session_key_bytes-1/*skip algo identifier*/) ) {
			goto cleanup;
		}
		iv = calloc(1, stream->decrypt.blocksize); if( iv == NULL ) { goto cleanup; }
		stream->decrypt.set_iv(&stream->decrypt, iv);
		stream->decrypt.set_crypt_key(&stream->decrypt, &decr_session_key[1]/*skip algo identifier*/);
		pgp_encrypt_init(&stream->decrypt);
	}
	else
	{
		/* 0 bytes - Encrypted session key is _not_ present. From RFC 4880:
		"[...] The S2K algorithm applied to the passphrase produces the session
		key for decrypting the file, using the symmetric cipher algorithm from
		the Symmetric-Key Encrypted Session Key packet." */
		if( !pgp_crypt_any(&stream->decrypt, algo) ) {
			goto cleanup;
		}
		iv = calloc(1, stream->decrypt.blocksize); if( iv == NULL ) { goto cleanup; }
		stream->decrypt.set_iv(&stream->decrypt, iv);
		stream->decrypt.set_crypt_key(&stream->decrypt, key);
		pgp_encrypt_init(&stream->decrypt);
	}

	success = 1;
	stream->cbinfo.gotpass = 1;

cleanup:
	free(key);
	free(iv);
	free(encr_session_key);
	free(decr_session_key);
	return success;
}
// /EDIT BY MR

#if 0
static int
decrypt_se_data(pgp_content_enum tag, pgp_region_t *region,
		    pgp_stream_t *stream)
{
	pgp_crypt_t	*decrypt;
	const int	 printerrors = 1;
	int		 r = 1;

	decrypt = pgp_get_decrypt(stream);
	if (decrypt) {
		pgp_region_t	encregion;
		unsigned	b = (unsigned)decrypt->blocksize;
		uint8_t		buf[PGP_MAX_BLOCK_SIZE + 2] = "";

		pgp_reader_push_decrypt(stream, decrypt, region);

		pgp_init_subregion(&encregion, NULL);
		encregion.length = b + 2;

		if (!exact_limread(buf, b + 2, &encregion, stream)) {
			return 0;
		}
		if (buf[b - 2] != buf[b] || buf[b - 1] != buf[b + 1]) {
			pgp_reader_pop_decrypt(stream);
			PGP_ERROR_4(&stream->errors,
				PGP_E_PROTO_BAD_SYMMETRIC_DECRYPT,
				"Bad symmetric decrypt (%02x%02x vs %02x%02x)",
				buf[b - 2], buf[b - 1], buf[b], buf[b + 1]);
			return 0;
		}
		if (tag == PGP_PTAG_CT_SE_DATA_BODY) {
			decrypt->decrypt_resync(decrypt);
			decrypt->block_encrypt(decrypt, decrypt->civ,
					decrypt->civ);
		}
		r = pgp_parse(stream, !printerrors);

		pgp_reader_pop_decrypt(stream);
	} else {
		pgp_packet_t pkt;

		while (region->readc < region->length) {
			unsigned        len;

			len = region->length - region->readc;
			if (len > sizeof(pkt.u.se_data_body.data))
				len = sizeof(pkt.u.se_data_body.data);

			if (!limread(pkt.u.se_data_body.data, len,
					region, stream)) {
				return 0;
			}
			pkt.u.se_data_body.length = len;
			CALLBACK(tag, &stream->cbinfo, &pkt);
		}
	}

	return r;
}
#endif

static int
decrypt_se_ip_data(pgp_content_enum tag, pgp_region_t *region,
		       pgp_stream_t *stream)
{
	pgp_crypt_t	*decrypt;
	const int	 printerrors = 1;
	int		 r = 1;

	decrypt = pgp_get_decrypt(stream);
	if (decrypt) {
		if (pgp_get_debug_level(__FILE__)) {
			(void) fprintf(stderr, "decrypt_se_ip_data: decrypt\n");
		}
		pgp_reader_push_decrypt(stream, decrypt, region);
		pgp_reader_push_se_ip_data(stream, decrypt, region);

		r = pgp_parse(stream, !printerrors);

		pgp_reader_pop_se_ip_data(stream);
		pgp_reader_pop_decrypt(stream);
	} else {
		pgp_packet_t pkt;

		if (pgp_get_debug_level(__FILE__)) {
			(void) fprintf(stderr, "decrypt_se_ip_data: no decrypt\n");
		}
		while (region->readc < region->length) {
			unsigned        len;

			len = region->length - region->readc;
			if (len > sizeof(pkt.u.se_data_body.data)) {
				len = sizeof(pkt.u.se_data_body.data);
			}

			if (!limread(pkt.u.se_data_body.data,
					len, region, stream)) {
				return 0;
			}

			pkt.u.se_data_body.length = len;

			CALLBACK(tag, &stream->cbinfo, &pkt);
		}
	}

	return r;
}

#if 0
/**
   \ingroup Core_ReadPackets
   \brief Read a Symmetrically Encrypted packet
*/
static int
parse_se_data(pgp_region_t *region, pgp_stream_t *stream)
{
	pgp_packet_t pkt;

	/* there's no info to go with this, so just announce it */
	CALLBACK(PGP_PTAG_CT_SE_DATA_HEADER, &stream->cbinfo, &pkt);

	/*
	 * The content of an encrypted data packet is more OpenPGP packets
	 * once decrypted, so recursively handle them
	 */
	return decrypt_se_data(PGP_PTAG_CT_SE_DATA_BODY, region, stream);
}
#endif

/**
   \ingroup Core_ReadPackets
   \brief Read a Symmetrically Encrypted Integrity Protected packet
*/
static int
parse_se_ip_data(pgp_region_t *region, pgp_stream_t *stream)
{
	pgp_packet_t	pkt;
	uint8_t		c = 0x0;

	if (!limread(&c, 1, region, stream)) {
		return 0;
	}
	pkt.u.se_ip_data_header = c;
	if (pgp_get_debug_level(__FILE__)) {
		(void) fprintf(stderr, "parse_se_ip_data: data header %d\n", c);
	}
	if (pkt.u.se_ip_data_header != PGP_SE_IP_DATA_VERSION) {
		(void) fprintf(stderr, "parse_se_ip_data: bad version\n");
		return 0;
	}

	if (pgp_get_debug_level(__FILE__)) {
		(void) fprintf(stderr, "parse_se_ip_data: region %d,%d\n",
			region->readc, region->length);
		hexdump(stderr, "compressed region", stream->readinfo.virtualpkt, stream->readinfo.virtualc);
	}
	/*
	 * The content of an encrypted data packet is more OpenPGP packets
	 * once decrypted, so recursively handle them
	 */
	return decrypt_se_ip_data(PGP_PTAG_CT_SE_IP_DATA_BODY, region, stream);
}

/**
   \ingroup Core_ReadPackets
   \brief Read a MDC packet
*/
static int
parse_mdc(pgp_region_t *region, pgp_stream_t *stream)
{
	pgp_packet_t pkt;

	pkt.u.mdc.length = PGP_SHA1_HASH_SIZE;
	if ((pkt.u.mdc.data = calloc(1, PGP_SHA1_HASH_SIZE)) == NULL) {
		(void) fprintf(stderr, "parse_mdc: bad alloc\n");
		return 0;
	}
	if (!limread(pkt.u.mdc.data, PGP_SHA1_HASH_SIZE, region, stream)) {
		return 0;
	}
	CALLBACK(PGP_PTAG_CT_MDC, &stream->cbinfo, &pkt);
	free(pkt.u.mdc.data);
	return 1;
}

/**
 * \ingroup Core_ReadPackets
 * \brief Parse one packet.
 *
 * This function parses the packet tag.  It computes the value of the
 * content tag and then calls the appropriate function to handle the
 * content.
 *
 * \param *stream	How to parse
 * \param *pktlen	On return, will contain number of bytes in packet
 * \return 1 on success, 0 on error, -1 on EOF */
static int
parse_packet(pgp_stream_t *stream, uint32_t *pktlen)
{
	pgp_packet_t	pkt;
	pgp_region_t	region;
	uint8_t		ptag;
	unsigned	indeterminate = 0;
	int		ret;

	pkt.u.ptag.position = stream->readinfo.position;

	ret = base_read(&ptag, 1, stream);

	if (pgp_get_debug_level(__FILE__)) {
		(void) fprintf(stderr,
			"parse_packet: base_read returned %d, ptag %d\n",
			ret, ptag);
	}

	/* errors in the base read are effectively EOF. */
	if (ret <= 0) {
		return -1;
	}

	*pktlen = 0;

	if (!(ptag & PGP_PTAG_ALWAYS_SET)) {
		pkt.u.error = "Format error (ptag bit not set)";
		CALLBACK(PGP_PARSER_ERROR, &stream->cbinfo, &pkt);
		return 0;
	}
	pkt.u.ptag.new_format = !!(ptag & PGP_PTAG_NEW_FORMAT);
	if (pkt.u.ptag.new_format) {
		pkt.u.ptag.type = (ptag & PGP_PTAG_NF_CONTENT_TAG_MASK);
		pkt.u.ptag.length_type = 0;
		if (!read_new_length(&pkt.u.ptag.length, stream)) {
			return 0;
		}
	} else {
		unsigned   rb;

		pkt.u.ptag.type = ((unsigned)ptag &
				PGP_PTAG_OF_CONTENT_TAG_MASK)
			>> PGP_PTAG_OF_CONTENT_TAG_SHIFT;
		pkt.u.ptag.length_type = ptag & PGP_PTAG_OF_LENGTH_TYPE_MASK;
		switch (pkt.u.ptag.length_type) {
		case PGP_PTAG_OLD_LEN_1:
			rb = _read_scalar(&pkt.u.ptag.length, 1, stream);
			break;

		case PGP_PTAG_OLD_LEN_2:
			rb = _read_scalar(&pkt.u.ptag.length, 2, stream);
			break;

		case PGP_PTAG_OLD_LEN_4:
			rb = _read_scalar(&pkt.u.ptag.length, 4, stream);
			break;

		case PGP_PTAG_OLD_LEN_INDETERMINATE:
			pkt.u.ptag.length = 0;
			indeterminate = 1;
			rb = 1;
			break;
		}
		if (!rb) {
			return 0;
		}
	}

	CALLBACK(PGP_PARSER_PTAG, &stream->cbinfo, &pkt);

	pgp_init_subregion(&region, NULL);
	region.length = pkt.u.ptag.length;
	region.indeterminate = indeterminate;
	if (pgp_get_debug_level(__FILE__)) {
		(void) fprintf(stderr, "parse_packet: type %u\n",
			       pkt.u.ptag.type);
	}
	switch (pkt.u.ptag.type) {
	case PGP_PTAG_CT_SIGNATURE:
		ret = parse_sig(&region, stream);
		break;

	case PGP_PTAG_CT_PUBLIC_KEY:
	case PGP_PTAG_CT_PUBLIC_SUBKEY:
		ret = parse_pubkey((pgp_content_enum)pkt.u.ptag.type, &region, stream);
		break;

	case PGP_PTAG_CT_TRUST:
		ret = parse_trust(&region, stream);
		break;

	case PGP_PTAG_CT_USER_ID:
		ret = parse_userid(&region, stream);
		break;

	case PGP_PTAG_CT_COMPRESSED:
		ret = parse_compressed(&region, stream);
		break;

	case PGP_PTAG_CT_1_PASS_SIG:
		ret = parse_one_pass(&region, stream);
		break;

	case PGP_PTAG_CT_LITDATA:
		ret = parse_litdata(&region, stream);
		break;

	case PGP_PTAG_CT_USER_ATTR:
		ret = parse_userattr(&region, stream);
		break;

	case PGP_PTAG_CT_SECRET_KEY:
	case PGP_PTAG_CT_SECRET_SUBKEY:
		ret = parse_seckey((pgp_content_enum)pkt.u.ptag.type, &region, stream);
		break;

	case PGP_PTAG_CT_PK_SESSION_KEY:
		ret = parse_pk_sesskey(&region, stream);
		break;

	case PGP_PTAG_CT_SK_SESSION_KEY:
		ret = parse_sk_sesskey(&region, stream);
		break;

	case PGP_PTAG_CT_SE_DATA:
        // SE_DATA CURRENTLY BROKEN
        ret = 0;
		//ret = parse_se_data(&region, stream);
		break;

	case PGP_PTAG_CT_SE_IP_DATA:
		ret = parse_se_ip_data(&region, stream);
		break;

	case PGP_PTAG_CT_MDC:
		ret = parse_mdc(&region, stream);
		break;

	default:
		PGP_ERROR_1(&stream->errors, PGP_E_P_UNKNOWN_TAG,
			    "Unknown content tag 0x%x",
			    pkt.u.ptag.type);
		ret = 0;
	}

	/* Ensure that the entire packet has been consumed */

	if (region.length != region.readc && !region.indeterminate) {
		if (!consume_packet(&region, stream, 0)) {
			ret = -1;
		}
	}

	/* also consume it if there's been an error? */
	/* \todo decide what to do about an error on an */
	/* indeterminate packet */
	if (ret == 0) {
		if (!consume_packet(&region, stream, 0)) {
			ret = -1;
		}
	}
	/* set pktlen */

	*pktlen = stream->readinfo.alength;

	/* do callback on entire packet, if desired and there was no error */

	if (ret > 0 && stream->readinfo.accumulate) {
		pkt.u.packet.length = stream->readinfo.alength;
		pkt.u.packet.raw = stream->readinfo.accumulated;
		stream->readinfo.accumulated = NULL;
		stream->readinfo.asize = 0;
		CALLBACK(PGP_PARSER_PACKET_END, &stream->cbinfo, &pkt);
	}
	stream->readinfo.alength = 0;

	return (ret < 0) ? -1 : (ret) ? 1 : 0;
}

/**
 * \ingroup Core_ReadPackets
 *
 * \brief Parse packets from an input stream until EOF or error.
 *
 * \details Setup the necessary parsing configuration in "stream"
 * before calling pgp_parse().
 *
 * That information includes :
 *
 * - a "reader" function to be used to get the data to be parsed
 *
 * - a "callback" function to be called when this library has identified
 * a parseable object within the data
 *
 * - whether the calling function wants the signature subpackets
 * returned raw, parsed or not at all.
 *
 * After returning, stream->errors holds any errors encountered while parsing.
 *
 * \param stream	Parsing configuration
 * \return		1 on success in all packets, 0 on error in any packet
 *
 * \sa CoreAPI Overview
 *
 * \sa pgp_print_errors()
 *
 */

int
pgp_parse(pgp_stream_t *stream, const int perrors)
{
	uint32_t   pktlen;
	int             r;

	do {
		r = parse_packet(stream, &pktlen);
	} while (r != -1);
	/*if (perrors) { -- REMOVED BY MR
		pgp_print_errors(stream->errors);
	}*/
	return (stream->errors == NULL);
}

/**
 * \ingroup Core_ReadPackets
 *
 * \brief Specifies whether one or more signature
 * subpacket types should be returned parsed; or raw; or ignored.
 *
 * \param	stream	Pointer to previously allocated structure
 * \param	tag	Packet tag. PGP_PTAG_SS_ALL for all SS tags; or one individual signature subpacket tag
 * \param	type	Parse type
 * \todo Make all packet types optional, not just subpackets */
void
pgp_parse_options(pgp_stream_t *stream,
		  pgp_content_enum tag,
		  pgp_parse_type_t type)
{
	unsigned	t7;
	unsigned	t8;

	if (tag == PGP_PTAG_SS_ALL) {
		int             n;

		for (n = 0; n < 256; ++n) {
			pgp_parse_options(stream,
				PGP_PTAG_SIG_SUBPKT_BASE + n,
				type);
		}
		return;
	}
	if (tag < PGP_PTAG_SIG_SUBPKT_BASE ||
	    tag > PGP_PTAG_SIG_SUBPKT_BASE + NTAGS - 1) {
		(void) fprintf(stderr, "pgp_parse_options: bad tag\n");
		return;
	}
	t8 = (tag - PGP_PTAG_SIG_SUBPKT_BASE) / 8;
	t7 = 1 << ((tag - PGP_PTAG_SIG_SUBPKT_BASE) & 7);
	switch (type) {
	case PGP_PARSE_RAW:
		stream->ss_raw[t8] |= t7;
		stream->ss_parsed[t8] &= ~t7;
		break;

	case PGP_PARSE_PARSED:
		stream->ss_raw[t8] &= ~t7;
		stream->ss_parsed[t8] |= t7;
		break;

	case PGP_PARSE_IGNORE:
		stream->ss_raw[t8] &= ~t7;
		stream->ss_parsed[t8] &= ~t7;
		break;
	}
}

/**
\ingroup Core_ReadPackets
\brief Free pgp_stream_t struct and its contents
*/
void
pgp_stream_delete(pgp_stream_t *stream)
{
	pgp_cbdata_t	*cbinfo;
	pgp_cbdata_t	*next;
    pgp_cryptinfo_t *cryptinfo = &stream->cbinfo.cryptinfo;

	parse_hash_finish(stream); // EDIT BY MR: fix memory leak

	for (cbinfo = stream->cbinfo.next; cbinfo; cbinfo = next) {
		next = cbinfo->next;
		free(cbinfo);
	}

    FREE_ARRAY(cryptinfo, recipients_key_ids);

	if (stream->readinfo.destroyer) {
		stream->readinfo.destroyer(&stream->readinfo);
	}
	pgp_free_errors(stream->errors);
	if (stream->readinfo.accumulated) {
		free(stream->readinfo.accumulated);
	}
	free(stream);
}

/**
\ingroup Core_ReadPackets
\brief Returns the parse_info's reader_info
\return Pointer to the reader_info inside the parse_info
*/
pgp_reader_t *
pgp_readinfo(pgp_stream_t *stream)
{
	return &stream->readinfo;
}

/**
\ingroup Core_ReadPackets
\brief Sets the parse_info's callback
This is used when adding the first callback in a stack of callbacks.
\sa pgp_callback_push()
*/

void
pgp_set_callback(pgp_stream_t *stream, pgp_cbfunc_t *cb, void *arg)
{
	stream->cbinfo.cbfunc = cb;
	stream->cbinfo.arg = arg;
	stream->cbinfo.errors = &stream->errors;
}

/**
\ingroup Core_ReadPackets
\brief Adds a further callback to a stack of callbacks
\sa pgp_set_callback()
*/
void
pgp_callback_push(pgp_stream_t *stream, pgp_cbfunc_t *cb, void *arg)
{
	pgp_cbdata_t	*cbinfo;

	if ((cbinfo = calloc(1, sizeof(*cbinfo))) == NULL) {
		(void) fprintf(stderr, "pgp_callback_push: bad alloc\n");
		return;
	}
	(void) memcpy(cbinfo, &stream->cbinfo, sizeof(*cbinfo));
	cbinfo->io = stream->io;
	stream->cbinfo.next = cbinfo;
	pgp_set_callback(stream, cb, arg);
}

/**
\ingroup Core_ReadPackets
\brief Returns callback's arg
*/
void *
pgp_callback_arg(pgp_cbdata_t *cbinfo)
{
	return cbinfo->arg;
}

/**
\ingroup Core_ReadPackets
\brief Returns callback's errors
*/
void *
pgp_callback_errors(pgp_cbdata_t *cbinfo)
{
	return cbinfo->errors;
}

/**
\ingroup Core_ReadPackets
\brief Calls the parse_cb_info's callback if present
\return Return value from callback, if present; else PGP_FINISHED
*/
pgp_cb_ret_t
pgp_callback(const pgp_packet_t *pkt, pgp_cbdata_t *cbinfo)
{
	return (cbinfo->cbfunc) ? cbinfo->cbfunc(pkt, cbinfo) : PGP_FINISHED;
}

/**
\ingroup Core_ReadPackets
\brief Calls the next callback  in the stack
\return Return value from callback
*/
pgp_cb_ret_t
pgp_stacked_callback(const pgp_packet_t *pkt, pgp_cbdata_t *cbinfo)
{
	return pgp_callback(pkt, cbinfo->next);
}

/**
\ingroup Core_ReadPackets
\brief Returns the parse_info's errors
\return parse_info's errors
*/
pgp_error_t    *
pgp_stream_get_errors(pgp_stream_t *stream)
{
	return stream->errors;
}

pgp_crypt_t    *
pgp_get_decrypt(pgp_stream_t *stream)
{
	return (stream->decrypt.alg) ? &stream->decrypt : NULL;
}
