/*====================================================================*
 *
 *   Copyright (c) 2013 Qualcomm Atheros, Inc.
 *
 *   All rights reserved.
 *
 *   Redistribution and use in source and binary forms, with or 
 *   without modification, are permitted (subject to the limitations 
 *   in the disclaimer below) 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 name of Qualcomm Atheros nor the names of 
 *     its contributors may be used to endorse or promote products 
 *     derived from this software without specific prior written 
 *     permission.
 *
 *   NO EXPRESS OR IMPLIED LICENSES TO ANY PARTY'S PATENT RIGHTS ARE 
 *   GRANTED BY THIS LICENSE. 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 OWNER 
 *   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.  
 *
 *--------------------------------------------------------------------*/

/*====================================================================*
 *
 *   getpib.c - PIB Data Extractor
 *
 *   Contributor(s):
 *      Charles Maier <cmaier@qca.qualcomm.com>
 *
 *--------------------------------------------------------------------*/

/*====================================================================*
 *   system header files;
 *--------------------------------------------------------------------*/

#include <unistd.h>
#include <stdlib.h>
#include <limits.h>
#include <string.h>
#include <ctype.h>

/*====================================================================*
 *   custom header files;
 *--------------------------------------------------------------------*/

#include "../tools/getoptv.h"
#include "../tools/memory.h"
#include "../tools/number.h"
#include "../tools/error.h"
#include "../tools/types.h"
#include "../tools/flags.h"
#include "../tools/files.h"
#include "../pib/pib.h"
#include "../nvm/nvm.h"

/*====================================================================*
 *   custom source files;
 *--------------------------------------------------------------------*/

#ifndef MAKEFILE
#include "../tools/getoptv.c"
#include "../tools/putoptv.c"
#include "../tools/version.c"
#include "../tools/uintspec.c"
#include "../tools/basespec.c"
#include "../tools/todigit.c"
#include "../tools/hexout.c"
#include "../tools/error.c"
#include "../tools/checksum32.c"
#include "../tools/fdchecksum32.c"
#endif

#ifndef MAKEFILE
#include "../nvm/nvmseek2.c"
#endif

/*====================================================================*
 *   constants;
 *--------------------------------------------------------------------*/

#define GETPIB_COMMA ' '
#define GETPIB_TOOBIG "object '%s' exceeds extent of " SIZE_T_SPEC " bytes"
#define GETPIB_NOSIZE "object '%s' has no length"

#define GETPIB_VERBOSE (1 << 0)
#define GETPIB_SILENCE (1 << 1)
#define GETPIB_NEWLINE (1 << 2)

/*====================================================================*
 *
 *   void getmemory (byte const * memory, size_t extent, char const * object, size_t length);
 *
 *--------------------------------------------------------------------*/

static void getmemory (byte const * memory, size_t extent, char const * object, size_t length) 

{ 
	if (length > extent) 
	{ 
		error (1, ECANCELED, GETPIB_TOOBIG, object, length); 
	} 
	hexout (memory, length, ':', '\0', stdout); 
	return; 
} 

/*====================================================================*
 *
 *   void getstring (byte const * memory, size_t extent, char const * object, size_t length);
 *
 *--------------------------------------------------------------------*/

static void getstring (byte const * memory, size_t extent, char const * object, size_t length) 

{ 
	char const * string = (char const *) (memory); 
	if (length > extent) 
	{ 
		error (1, ECANCELED, GETPIB_TOOBIG, object, length); 
	} 
	while (isprint (* string) && (length--)) 
	{ 
		putc (* string++, stdout); 
	} 
	return; 
} 

/*====================================================================*
 *
 *   void snatch (int argc, char const * argv [], byte const * memory, size_t extent, char comma);
 *
 *   extract and print the specified data objects from memory; comma
 *   delimits consecutive objects on output; 
 *
 *--------------------------------------------------------------------*/

static void snatch (int argc, char const * argv [], byte const * memory, size_t extent, char comma) 

{ 
	size_t length = 0; 
	size_t offset = 0; 
	if (! (argc) || ! (* argv)) 
	{ 
		error (1, ECANCELED, "Need an offset"); 
	} 
	offset = (size_t) (basespec (* argv, 16, sizeof (uint32_t))); 
	if (offset > extent) 
	{ 
		error (1, ECANCELED, "offset " SIZE_T_SPEC " exceeds extent of " SIZE_T_SPEC " bytes", offset, extent); 
	} 
	memory += offset; 
	extent -= offset; 
	argc--; 
	argv++; 
	while ((argc) && (* argv)) 
	{ 
		char const * object = * argv; 
		argc--; 
		argv++; 
		if (! strcmp (object, "byte")) 
		{ 
			uint8_t * number = (uint8_t *) (memory); 
			if (sizeof (* number) > extent) 
			{ 
				error (1, ECANCELED, GETPIB_TOOBIG, object, extent); 
			} 
			printf ("%u", * number); 
			memory += sizeof (* number); 
			extent -= sizeof (* number); 
		} 
		else if (! strcmp (object, "word")) 
		{ 
			uint16_t * number = (uint16_t *) (memory); 
			if (sizeof (* number) > extent) 
			{ 
				error (1, ECANCELED, GETPIB_TOOBIG, object, extent); 
			} 
			printf ("%u", LE16TOH (* number)); 
			memory += sizeof (* number); 
			extent -= sizeof (* number); 
		} 
		else if (! strcmp (object, "long")) 
		{ 
			uint32_t * number = (uint32_t *) (memory); 
			if (sizeof (* number) > extent) 
			{ 
				error (1, ECANCELED, GETPIB_TOOBIG, object, extent); 
			} 
			printf ("%u", LE32TOH (* number)); 
			memory += sizeof (* number); 
			extent -= sizeof (* number); 
		} 
		else if (! strcmp (object, "huge")) 
		{ 
			uint64_t * number = (uint64_t *) (memory); 
			if (sizeof (* number) > extent) 
			{ 
				error (1, ECANCELED, GETPIB_TOOBIG, object, extent); 
			} 
			printf ("%llu", LE64TOH (* number)); 
			memory += sizeof (* number); 
			extent -= sizeof (* number); 
		} 

#if 1

		else if (! strcmp (object, "xbyte")) 
		{ 
			uint8_t * number = (uint8_t *) (memory); 
			if (sizeof (* number) > extent) 
			{ 
				error (1, ECANCELED, GETPIB_TOOBIG, object, extent); 
			} 
			printf ("0x%02X", * number); 
			memory += sizeof (* number); 
			extent -= sizeof (* number); 
		} 
		else if (! strcmp (object, "xword")) 
		{ 
			uint16_t * number = (uint16_t *) (memory); 
			if (sizeof (* number) > extent) 
			{ 
				error (1, ECANCELED, GETPIB_TOOBIG, object, extent); 
			} 
			printf ("0x%04X", LE16TOH (* number)); 
			memory += sizeof (* number); 
			extent -= sizeof (* number); 
		} 
		else if (! strcmp (object, "xlong")) 
		{ 
			uint32_t * number = (uint32_t *) (memory); 
			if (sizeof (* number) > extent) 
			{ 
				error (1, ECANCELED, GETPIB_TOOBIG, object, extent); 
			} 
			printf ("0x%08X", LE32TOH (* number)); 
			memory += sizeof (* number); 
			extent -= sizeof (* number); 
		} 
		else if (! strcmp (object, "xhuge")) 
		{ 
			uint64_t * number = (uint64_t *) (memory); 
			if (sizeof (* number) > extent) 
			{ 
				error (1, ECANCELED, GETPIB_TOOBIG, object, extent); 
			} 
			printf ("0x%016llX", LE64TOH (* number)); 
			memory += sizeof (* number); 
			extent -= sizeof (* number); 
		} 

#endif

		else if (! strcmp (object, "mac")) 
		{ 
			length = ETHER_ADDR_LEN; 
			if (length > extent) 
			{ 
				error (1, ECANCELED, GETPIB_TOOBIG, object, extent); 
			} 
			getmemory (memory, extent, object, length); 
			memory += length; 
			extent -= length; 
		} 
		else if (! strcmp (object, "key")) 
		{ 
			length = PIB_KEY_LEN; 
			if (length > extent) 
			{ 
				error (1, ECANCELED, GETPIB_TOOBIG, object, extent); 
			} 
			getmemory (memory, extent, object, length); 
			memory += length; 
			extent -= length; 
		} 
		else if (! strcmp (object, "hfid")) 
		{ 
			length = PIB_HFID_LEN; 
			if (length > extent) 
			{ 
				error (1, ECANCELED, GETPIB_TOOBIG, object, extent); 
			} 
			getstring (memory, extent, object, length); 
			memory += length; 
			extent -= length; 
		} 

#if 1

		else if (! strcmp (object, "adminusername") || ! strcmp (object, "adminpassword") || ! strcmp (object, "accessusername")) 
		{ 
			length = PIB_NAME_LEN +  1; 
			if (length > extent) 
			{ 
				error (1, ECANCELED, GETPIB_TOOBIG, object, extent); 
			} 
			getstring (memory, extent, object, length); 
			memory += length; 
			extent -= length; 
		} 
		else if (! strcmp (object, "accesspassword")) 
		{ 
			length = PIB_HFID_LEN +  1; 
			if (length > extent) 
			{ 
				error (1, ECANCELED, GETPIB_TOOBIG, object, extent); 
			} 
			getstring (memory, extent, object, length); 
			memory += length; 
			extent -= length; 
		} 
		else if (! strcmp (object, "username") || ! strcmp (object, "password") || ! strcmp (object, "url")) 
		{ 
			length = PIB_TEXT_LEN +  1; 
			getstring (memory, extent, object, length); 
			memory += length; 
			extent -= length; 
		} 

#endif

		else if (! strcmp (object, "data")) 
		{ 
			if (! * argv) 
			{ 
				error (1, EINVAL, GETPIB_NOSIZE, object); 
			} 
			length = (unsigned) (uintspec (* argv, 1, extent)); 
			hexout (memory, length, 0, 0, stdout); 
			memory += length; 
			extent -= length; 
			argc--; 
			argv++; 
		} 
		else if (! strcmp (object, "text")) 
		{ 
			if (! * argv) 
			{ 
				error (1, EINVAL, GETPIB_NOSIZE, object); 
			} 
			length = (unsigned) (uintspec (* argv, 1, extent)); 
			getstring (memory, extent, object, length); 
			memory += length; 
			extent -= length; 
			argc--; 
			argv++; 
		} 
		else if (! strcmp (object, "skip")) 
		{ 
			if (! * argv) 
			{ 
				error (1, EINVAL, GETPIB_NOSIZE, object); 
			} 
			length = (unsigned) (uintspec (* argv, 1, extent)); 
			memory += length; 
			extent -= length; 
			argc--; 
			argv++; 
			continue; 
		} 
		else 
		{ 
			error (1, ENOTSUP, "%s", object); 
		} 
		if ((argc) && (* argv)) 
		{ 
			putc (comma, stdout); 
		} 
	} 
	return; 
} 

/*====================================================================*
 *
 *   signed pibimage1 (int argc, char const * argv [], char comma);
 * 
 *   read an entire flat parameter file into memory, edit it, save 
 *   it and display it;
 *
 *   Contributor(s):
 *	Charles Maier <cmaier@qca.qualcomm.com>
 *
 *--------------------------------------------------------------------*/

static signed pibimage1 (int argc, char const * argv [], char comma) 

{ 
	signed fd; 
	off_t extent; 
	byte * memory; 
	if ((fd = open (* argv, O_BINARY | O_RDWR)) == - 1) 
	{ 
		error (1, errno, FILE_CANTOPEN, * argv); 
	} 
	if ((extent = lseek (fd, 0, SEEK_END)) == - 1) 
	{ 
		error (1, errno, FILE_CANTSIZE, * argv); 
	} 
	if (lseek (fd, 0, SEEK_SET)) 
	{ 
		error (1, errno, FILE_CANTHOME, * argv); 
	} 
	if (! (memory = malloc (extent))) 
	{ 
		error (1, errno, FILE_CANTLOAD, * argv); 
	} 
	if (read (fd, memory, extent) != extent) 
	{ 
		error (1, errno, FILE_CANTREAD, * argv); 
	} 
	close (fd); 
	snatch (argc - 1, argv +  1, memory, extent, comma); 
	free (memory); 
	return (0); 
} 

/*====================================================================*
 *
 *   signed pibimage2 (int argc, char const * argv [], char comma);
 * 
 *   read an entire flat parameter file into memory, edit it, save 
 *   it and display it;
 *
 *   Contributor(s):
 *	Charles Maier <cmaier@qca.qualcomm.com>
 *
 *--------------------------------------------------------------------*/

static signed pibimage2 (int argc, char const * argv [], char comma) 

{ 
	struct nvm_header2 header; 
	signed fd; 
	off_t extent; 
	byte * memory; 
	if ((fd = open (* argv, O_BINARY | O_RDWR)) == - 1) 
	{ 
		error (1, errno, FILE_CANTOPEN, * argv); 
	} 
	if (nvmseek2 (fd, * argv, & header, NVM_IMAGE_PIB)) 
	{ 
		error (1, errno, "Can't find PIB image in %s", * argv); 
	} 
	extent = LE32TOH (header.ImageLength); 
	if (! (memory = malloc (extent))) 
	{ 
		error (1, errno, FILE_CANTLOAD, * argv); 
	} 
	if (read (fd, memory, extent) != extent) 
	{ 
		error (1, errno, FILE_CANTREAD, * argv); 
	} 
	close (fd); 
	snatch (argc - 1, argv +  1, memory, extent, comma); 
	free (memory); 
	return (0); 
} 

/*====================================================================*
 *
 *   signed function (int argc, char const * argv [], char comma);
 *
 *   call an appropriate parameter edit function based on the file 
 *   header;
 *
 *   older parameter files are flat with their own header; newer ones
 *   are image chains where one of image contains the parameter block;
 *
 *   
 *   Contributor(s):
 *	Charles Maier <cmaier@qca.qualcomm.com>
 *
 *--------------------------------------------------------------------*/

static signed function (int argc, char const * argv [], char comma) 

{ 
	uint32_t version; 
	signed status; 
	signed fd; 
	if ((fd = open (* argv, O_BINARY | O_RDWR)) == - 1) 
	{ 
		error (1, errno, FILE_CANTOPEN, * argv); 
	} 
	if (read (fd, & version, sizeof (version)) != sizeof (version)) 
	{ 
		error (1, errno, FILE_CANTREAD, * argv); 
	} 
	close (fd); 
	if (LE32TOH (version) == 0x00010001) 
	{ 
		status = pibimage2 (argc, argv, comma); 
	} 
	else 
	{ 
		status = pibimage1 (argc, argv, comma); 
	} 
	return (status); 
} 

/*====================================================================*
 *
 *   int main (int argc, char const * argv []);
 *   
 *
 *--------------------------------------------------------------------*/

int main (int argc, char const * argv []) 

{ 
	static char const * optv [] = 
	{ 
		"c:qvn", 
		"file offset type [size]\n\n\tstandard-length types are 'byte'|'word'|'long'|'huge'|'hfid'|'mac'|'key'\n\tvariable-length types are 'data'|'text'|'skip' and need a size", 
		"PIB Data Extractor", 
		"c c\tobject separator is (c) [" LITERAL (GETPIB_COMMA) "]", 
		"n\tappend newline", 
		"q\tquiet mode", 
		"v\tverbose mode", 
		(char const *) (0)
	}; 
	flag_t flags = (flag_t) (0); 
	char comma = GETPIB_COMMA; 
	signed c; 
	optind = 1; 
	opterr = 1; 
	while (~ (c = getoptv (argc, argv, optv))) 
	{ 
		switch (c) 
		{ 
		case 'c': 
			comma = * optarg; 
			break; 
		case 'n': 
			_setbits (flags, GETPIB_NEWLINE); 
			break; 
		case 'q': 
			_setbits (flags, GETPIB_SILENCE); 
			break; 
		case 'v': 
			_setbits (flags, GETPIB_VERBOSE); 
			break; 
		default: 
			break; 
		} 
	} 
	argc -= optind; 
	argv += optind; 
	if ((argc) && (* argv)) 
	{ 
		function (argc, argv, comma); 
		if (_anyset (flags, GETPIB_NEWLINE)) 
		{ 
			putc ('\n', stdout); 
		} 
	} 
	return (0); 
}