/********************************************************************************/
/*										*/
/*			    TSS and Application File Utilities			*/
/*			     Written by Ken Goldman				*/
/*		       IBM Thomas J. Watson Research Center			*/
/*										*/
/* (c) Copyright IBM Corporation 2015 - 2019					*/
/*										*/
/* All rights reserved.								*/
/* 										*/
/* Redistribution and use in source and binary forms, with or without		*/
/* modification, are permitted provided that the following conditions are	*/
/* met:										*/
/* 										*/
/* Redistributions of source code must retain the above copyright notice,	*/
/* this list of conditions and the following disclaimer.			*/
/* 										*/
/* 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.		*/
/* 										*/
/* Neither the names of the IBM Corporation nor the names of its		*/
/* contributors may be used to endorse or promote products derived from		*/
/* this software without specific prior written permission.			*/
/* 										*/
/* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT		*/
/* HOLDER 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.		*/
/********************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include <ibmtss/tssresponsecode.h>
#include <ibmtss/tsserror.h>
#include <ibmtss/tssprint.h>
#include <ibmtss/tssfile.h>

extern int tssVerbose;
extern int tssVverbose;

/* TSS_File_Open() opens the 'filename' for 'mode'
 */

int TSS_File_Open(FILE **file,
		  const char *filename,
		  const char* mode)
{
    int 	rc = 0;
		    
    if (rc == 0) {
	*file = fopen(filename, mode);
	if (*file == NULL) {
	    if (tssVerbose) printf("TSS_File_Open: Error opening %s for %s, %s\n",
				   filename, mode, strerror(errno));
	    rc = TSS_RC_FILE_OPEN;
	}
    }
    return rc;
}

/* TSS_File_ReadBinaryFile() reads 'filename'.  The results are put into 'data', which must be freed
   by the caller.  'length' indicates the number of bytes read.
   
*/

TPM_RC TSS_File_ReadBinaryFile(unsigned char **data,     /* must be freed by caller */
			       size_t *length,
			       const char *filename) 
{
    int		rc = 0;
    long	lrc;
    size_t	src;
    int		irc;
    FILE	*file = NULL;

    *data = NULL;
    *length = 0;
    /* open the file */
    if (rc == 0) {
	rc = TSS_File_Open(&file, filename, "rb");				/* closed @1 */
    }
    /* determine the file length */
    if (rc == 0) {
	irc = fseek(file, 0L, SEEK_END);	/* seek to end of file */
	if (irc == -1L) {
	    if (tssVerbose) printf("TSS_File_ReadBinaryFile: Error seeking to end of %s\n",
				   filename);
	    rc = TSS_RC_FILE_SEEK;
	}
    }
    if (rc == 0) {
	lrc = ftell(file);			/* get position in the stream */
	if (lrc == -1L) {
	    if (tssVerbose) printf("TSS_File_ReadBinaryFile: Error ftell'ing %s\n", filename);
	    rc = TSS_RC_FILE_FTELL;
	}
	else {
	    *length = (size_t)lrc;		/* save the length */
	}
    }
    if (rc == 0) {
	irc = fseek(file, 0L, SEEK_SET);	/* seek back to the beginning of the file */
	if (irc == -1L) {
	    if (tssVerbose) printf("TSS_File_ReadBinaryFile: Error seeking to beginning of %s\n",
				   filename);
	    rc = TSS_RC_FILE_SEEK;
	}
    }
    /* allocate a buffer for the actual data */
    if ((rc == 0) && (*length != 0)) {
	rc = TSS_Malloc(data, (uint32_t)*length);
    }
    /* read the contents of the file into the data buffer */
    if ((rc == 0) && *length != 0) {
	src = fread(*data, 1, *length, file);
	if (src != *length) {
	    if (tssVerbose)
		printf("TSS_File_ReadBinaryFile: Error reading %s, %u bytes, got %lu\n",
		       filename, (unsigned int)*length, (unsigned long)src);
	    rc = TSS_RC_FILE_READ;
	}
    }
    if (file != NULL) {
	irc = fclose(file);		/* @1 */
	if (irc != 0) {
	    if (tssVerbose) printf("TSS_File_ReadBinaryFile: Error closing %s\n",
				   filename);
	    rc = TSS_RC_FILE_CLOSE;
	}
    }
    if (rc != 0) {
	if (tssVerbose) printf("TSS_File_ReadBinaryFile: Error reading %s\n", filename);
	free(*data);
	*data = NULL;
    }
    return rc;
}

/* TSS_File_WriteBinaryFile() writes 'data' of 'length' to 'filename'
 */

TPM_RC TSS_File_WriteBinaryFile(const unsigned char *data,
				size_t length,
				const char *filename) 
{
    long	rc = 0;
    size_t	src;
    int		irc;
    FILE	*file = NULL;

    /* open the file */
    if (rc == 0) {
	rc = TSS_File_Open(&file, filename, "wb");	/* closed @1 */
    }
    /* write the contents of the data buffer into the file */
    if (rc == 0) {
	src = fwrite(data, 1, length, file);
	if (src != length) {
	    if (tssVerbose)
		printf("TSS_File_WriteBinaryFile: Error writing %s, %lu bytes, got %lu\n",
		       filename, (unsigned long)length, (unsigned long)src);
	    rc = TSS_RC_FILE_WRITE;
	}
    }
    if (file != NULL) {
	irc = fclose(file);		/* @1 */
	if (irc != 0) {
	    if (tssVerbose) printf("TSS_File_WriteBinaryFile: Error closing %s\n",
				   filename);
	    rc = TSS_RC_FILE_CLOSE;
	}
    }
    return rc;
}

/* TSS_File_ReadStructure() is a general purpose "read a structure" function.
   
   It reads the filename, and then unmarshals the structure using "unmarshalFunction".
*/

TPM_RC TSS_File_ReadStructure(void 			*structure,
			      UnmarshalFunction_t 	unmarshalFunction,
			      const char 		*filename)
{
    TPM_RC 	rc = 0;
    uint8_t	*buffer = NULL;		/* for the free */
    uint8_t	*buffer1 = NULL;	/* for unmarshaling */
    size_t 	length = 0;

    if (rc == 0) {
	rc = TSS_File_ReadBinaryFile(&buffer,     /* freed @1 */
				     &length,
				     filename);
    }
    if (rc == 0) {
	uint32_t ilength = (uint32_t)length;
	buffer1 = buffer;
	rc = unmarshalFunction(structure, &buffer1, &ilength);
    }
    free(buffer);	/* @1 */
    return rc;
}

/* TSS_File_ReadStructureFlag() is a general purpose "read a structure" function.

   It reads the filename, and then unmarshals the structure using "unmarshalFunction".

   It is similar to TSS_File_ReadStructure() but is used when the structure unmarshal function
   requires the allowNull flag.
*/

TPM_RC TSS_File_ReadStructureFlag(void 				*structure,
				  UnmarshalFunctionFlag_t 	unmarshalFunction,
				  BOOL 				allowNull,
				  const char 			*filename)
{
    TPM_RC 	rc = 0;
    uint8_t	*buffer = NULL;		/* for the free */
    uint8_t	*buffer1 = NULL;	/* for unmarshaling */
    size_t 	length = 0;

    if (rc == 0) {
	rc = TSS_File_ReadBinaryFile(&buffer,     /* freed @1 */
				     &length,
				     filename);
    }
    if (rc == 0) {
	uint32_t ilength = (uint32_t)length;
	buffer1 = buffer;
	rc = unmarshalFunction(structure, &buffer1, &ilength, allowNull);
    }
    free(buffer);	/* @1 */
    return rc;
}

/* TSS_File_WriteStructure() is a general purpose "write a structure" function.
   
   It marshals the structure using "marshalFunction", and then writes it to filename.
*/

TPM_RC TSS_File_WriteStructure(void 			*structure,
			       MarshalFunction_t 	marshalFunction,
			       const char 		*filename)
{
    TPM_RC 	rc = 0;
    uint16_t	written = 0;
    uint8_t	*buffer = NULL;		/* for the free */

    if (rc == 0) {
	rc = TSS_Structure_Marshal(&buffer,	/* freed @1 */
				   &written,
				   structure,
				   marshalFunction);
    }
    if (rc == 0) {
	rc = TSS_File_WriteBinaryFile(buffer,
				      written,
				      filename); 
    }
    free(buffer);	/* @1 */
    return rc;
}

/* TSS_File_Read2B() reads 'filename' and copies the data to 'tpm2b', checking targetSize

 */

TPM_RC TSS_File_Read2B(TPM2B 		*tpm2b,
		       uint16_t 	targetSize,
		       const char 	*filename)
{
    TPM_RC 	rc = 0;
    uint8_t	*buffer = NULL;
    size_t 	length = 0;
    
    if (rc == 0) {
	rc = TSS_File_ReadBinaryFile(&buffer,     /* freed @1 */
				     &length,
				     filename);
    }
    if (rc == 0) {
	if (length > 0xffff) {	/* overflow TPM2B uint16_t */
	    if (tssVerbose) printf("TSS_File_Read2B: size %u greater than 0xffff\n",
				   (unsigned int)length);	
	    rc = TSS_RC_INSUFFICIENT_BUFFER;
	}
    }
    /* copy it into the TPM2B */
    if (rc == 0) {
	rc = TSS_TPM2B_Create(tpm2b, buffer, (uint16_t)length, targetSize);
    }
    free(buffer);	/* @1 */
    return rc;
}

/* FIXME need to add - ignore failure if does not exist */

TPM_RC TSS_File_DeleteFile(const char *filename) 
{
    TPM_RC 	rc = 0;
    int		irc;
    
    if (rc == 0) {
	irc = remove(filename);
	if (irc != 0) {
	    rc = TSS_RC_FILE_REMOVE;
	}
    }
    return rc;
}
