/**
* Copyright(c) 2011 Trusted Logic. 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 name Trusted Logic 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
* 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.
*/
#include <stdlib.h>
#include <assert.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#if defined(_WIN32_WCE)
#include "os_wm.h"
#else
#include <errno.h>
#endif
#include "smc_properties_parser.h"
#include "lib_manifest2.h"
#include "lib_uuid.h"
#include "s_error.h"
/* ---------------------------------------------------------------------------------
Defines
---------------------------------------------------------------------------------*/
#define STRUE "true"
#define SFALSE "false"
#if defined(_WIN32_WCE)
#define GET_LAST_ERR GetLastError()
#else
#define GET_LAST_ERR errno
#endif
#if defined (LINUX) || defined (__SYMBIAN32__) || defined (ANDROID)
#define STRICMP strcasecmp
#elif defined(_WIN32_WCE)
#define STRICMP _stricmp
#else
#define STRICMP stricmp
#endif
/* ---------------------------------------------------------------------------------
Logs and Traces.
---------------------------------------------------------------------------------*/
#ifdef __SYMBIAN32__
#include "os_symbian.h"
#elif NDEBUG
/* Compile-out the traces */
#define TRACE_ERROR(...)
#define TRACE_WARNING(...)
#define TRACE_INFO(...)
#else
#include <stdarg.h>
static void TRACE_ERROR(const char* format, ...)
{
va_list ap;
va_start(ap, format);
fprintf(stderr, "TRACE: ERROR: ");
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
va_end(ap);
}
static void TRACE_WARNING(const char* format, ...)
{
va_list ap;
va_start(ap, format);
fprintf(stderr, "TRACE: WARNING: ");
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
va_end(ap);
}
static void TRACE_INFO(const char* format, ...)
{
va_list ap;
va_start(ap, format);
fprintf(stderr, "TRACE: ");
vfprintf(stderr, format, ap);
fprintf(stderr, "\n");
va_end(ap);
}
#endif /* NDEBUG */
/* ---------------------------------------------------------------------------------
private functions.
---------------------------------------------------------------------------------*/
static NODE* static_listFindNodeElement(NODE* pList,char* pName,bool bIsCaseSensitive)
{
int32_t nCmp;
assert(pName!=NULL);
while (pList!=NULL)
{
if (bIsCaseSensitive)
{
nCmp=strcmp(pName,pList->pName);
}
else
{
nCmp=STRICMP(pName,pList->pName);
}
if (nCmp>0)
{
pList=pList->pRight;
}
else if (nCmp<0)
{
pList=pList->pLeft;
}
else
{
break;
}
}
return pList;
}
static S_RESULT static_listSortedAddNode(NODE* pList,NODE* pNode)
{
int32_t nCmp;
do {
nCmp=strcmp(pNode->pName,pList->pName);
if (nCmp>0)
{
if (pList->pRight!=NULL)
{
pList=pList->pRight;
}
else
{
pList->pRight=pNode;
/* update linked list */
pNode->pPrevious=pList;
pNode->pNext=pList->pNext;
if (pList->pNext!=NULL)
{
pList->pNext->pPrevious=pNode;
}
pList->pNext=pNode;
return S_SUCCESS;
}
}
else if (nCmp<0)
{
if (pList->pLeft!=NULL)
{
pList=pList->pLeft;
}
else
{
pList->pLeft=pNode;
/* update linked list */
pNode->pNext=pList;
pNode->pPrevious=pList->pPrevious;
if (pList->pPrevious!=NULL)
{
pList->pPrevious->pNext=pNode;
}
pList->pPrevious=pNode;
return S_SUCCESS;
}
}
} while (nCmp!=0);
TRACE_ERROR("%s already exist !\n",pNode->pName);
return S_ERROR_ITEM_EXISTS;
}
static S_RESULT SMCPropListSortedAdd(LIST* pList,NODE* pNode)
{
S_RESULT nResult;
assert(pList!=NULL && pNode!=NULL);
if (pNode->pName==NULL)
{
TRACE_ERROR("Trying to insert a NULL node name !\n");
return S_ERROR_BAD_PARAMETERS;
}
if (pList->pRoot==NULL)
{
pList->pRoot=pNode;
pList->pFirst=pNode;
return S_SUCCESS;
}
else
{
nResult=static_listSortedAddNode(pList->pRoot,pNode);
/* update the first node of the linked list */
if (nResult==S_SUCCESS && pNode->pPrevious==NULL)
{
pList->pFirst=pNode;
}
}
return nResult;
}
static NODE* SMCPropListFindElement(LIST* pList,char* pName,bool bIsCaseSensitive)
{
if (pList->pRoot!=NULL)
{
return static_listFindNodeElement(pList->pRoot,pName,bIsCaseSensitive);
}
return NULL;
}
static S_RESULT SMCPropYacc(uint8_t* pBuffer, uint32_t nBufferLength,
CONF_FILE* pConfFile, SERVICE_SECTION* pService)
{
S_RESULT nError=S_SUCCESS;
LIST *pPublicPropertyList=NULL;
LIST *pPrivatePropertyList=NULL;
PROPERTY* pProperty=NULL;
SERVICE_SECTION* pServSection;
SERVICE_SECTION* pPreviousService=NULL;
uint8_t* pName;
uint32_t nNameLength;
uint8_t* pValue;
uint32_t nValueLength;
char* pNameZ = NULL;
char* pValueZ = NULL;
LIB_MANIFEST2_CONTEXT sParserContext;
char serviceManifestName[1024];
sParserContext.pManifestName = "Configuration File";
sParserContext.pManifestContent = pBuffer;
sParserContext.nManifestLength = nBufferLength;
sParserContext.nType = LIB_MANIFEST2_TYPE_SOURCE_WITH_SECTIONS;
if (pService!=NULL)
{
pPublicPropertyList=&pService->sPublicPropertyList;
pPrivatePropertyList=&pService->sPrivatePropertyList;
/* read inside a service compiled manifest */
sParserContext.nType = LIB_MANIFEST2_TYPE_COMPILED;
sprintf(serviceManifestName, "%s(manifest)", pService->sNode.pName);
sParserContext.pManifestName = serviceManifestName;
}
libManifest2InitContext(&sParserContext);
while (true)
{
nError = libManifest2GetNextItem(
&sParserContext,
&pName,
&nNameLength,
&pValue,
&nValueLength);
if (nError == S_ERROR_ITEM_NOT_FOUND)
{
/* End of parsing */
nError = S_SUCCESS;
break;
}
else if (nError != S_SUCCESS)
{
/* Error */
goto error;
}
/* Duplicate name and value in as zero-terminated strings */
/* Unclean: those strings are not going to be deallocated
This is not a problem because this code is run in a tool
*/
pNameZ = malloc(nNameLength+1);
if (pNameZ == NULL)
{
nError = S_ERROR_OUT_OF_MEMORY;
goto error;
}
memcpy(pNameZ, pName, nNameLength);
pNameZ[nNameLength] = 0;
if (pValue == NULL)
{
/* It's a section */
if (STRICMP(pNameZ, SYSTEM_SECTION_NAME) == 0)
{
free(pNameZ);
pPublicPropertyList=&pConfFile->sSystemSectionPropertyList;
}
else
{
pServSection=(SERVICE_SECTION*)SMCPropListFindElement(
&pConfFile->sDriverSectionList,
pNameZ,
false);
if (pServSection==NULL)
{
pServSection=(SERVICE_SECTION*)SMCPropListFindElement(
&pConfFile->sPreinstalledSectionList,
pNameZ,
false);
}
if (pServSection==NULL)
{
pServSection=(SERVICE_SECTION*)SMCPropListFindElement(
&pConfFile->sSectionList,
pNameZ,
false);
if (pServSection==NULL)
{
nError=S_ERROR_ITEM_NOT_FOUND;
goto error;
}
}
free(pNameZ);
pServSection->inSCF=true;
if (pPreviousService!=NULL)
{
pPreviousService->pNextInSCF=pServSection;
}
else
{
pConfFile->pFirstSectionInSCF=pServSection;
}
pPreviousService=pServSection;
pPublicPropertyList=&pServSection->sPublicPropertyList;
pPrivatePropertyList=&pServSection->sPrivatePropertyList;
}
}
else
{
/* It's a property definition */
pValueZ = malloc(nValueLength+1);
if (pValueZ == NULL)
{
nError = S_ERROR_OUT_OF_MEMORY;
goto error;
}
memcpy(pValueZ, pValue, nValueLength);
pValueZ[nValueLength] = 0;
pProperty=(PROPERTY*)malloc(sizeof(PROPERTY));
if (pProperty==NULL)
{
nError=S_ERROR_OUT_OF_MEMORY;
goto error;
}
memset(pProperty, 0x00, sizeof(PROPERTY));
pProperty->sNode.pName=pNameZ;
pProperty->pValue=pValueZ;
if (pPrivatePropertyList==NULL)
{
nError=SMCPropListSortedAdd(pPublicPropertyList,(NODE*)pProperty);
if (nError!=S_SUCCESS)
{
goto error;
}
}
else
{
if (strcmp(pProperty->sNode.pName,CONFIG_SERVICE_ID_PROPERTY_NAME) == 0)
{
if (pService!=NULL)
{
pService->sNode.pName=malloc(nValueLength+1);
if (pService->sNode.pName==NULL)
{
nError=S_ERROR_OUT_OF_MEMORY;
goto error;
}
#if defined (LINUX) || defined (__SYMBIAN32__) || defined(ANDROID)
{
// put each char of the value in uppercase
char* p=pProperty->pValue;
while(*p)
{
*p=toupper(*p);
p++;
}
}
#else
_strupr(pProperty->pValue);
#endif
memcpy(pService->sNode.pName,pProperty->pValue,nValueLength+1);
if (!libUUIDFromString((const uint8_t*)pProperty->pValue,&pService->sUUID))
{
nError=S_ERROR_WRONG_SIGNATURE;
goto error;
}
{
S_UUID sNullUUID;
memset(&sNullUUID,0,sizeof(S_UUID));
if (!memcmp(&pService->sUUID,&sNullUUID,sizeof(S_UUID)))
{
nError=S_ERROR_WRONG_SIGNATURE;
goto error;
}
}
}
}
if ((nValueLength > strlen(CONFIG_PROPERTY_NAME)) &&
(memcmp(pProperty->sNode.pName, CONFIG_PROPERTY_NAME, strlen(CONFIG_PROPERTY_NAME)) == 0))
{
nError=SMCPropListSortedAdd(pPrivatePropertyList,(NODE*)pProperty);
}
else
{
nError=SMCPropListSortedAdd(pPublicPropertyList,(NODE*)pProperty);
}
if (nError!=S_SUCCESS)
{
goto error;
}
}
}
}
error:
if (nError!=S_SUCCESS)
{
switch (nError)
{
case S_ERROR_BAD_FORMAT:
/* Error message already output */
break;
case S_ERROR_WRONG_SIGNATURE:
TRACE_ERROR("Configuration file: wrong service UUID: %s\n", pValueZ);
break;
case S_ERROR_OUT_OF_MEMORY:
TRACE_ERROR("Out of memory\n");
break;
case S_ERROR_ITEM_NOT_FOUND:
TRACE_ERROR("Configuration file: service \"%s\" not found\n", pNameZ);
break;
}
}
return nError;
}
S_RESULT static_readFile(const char* pFilename, void** ppFile, uint32_t* pnFileLength)
{
S_RESULT nResult = S_SUCCESS;
long nFilesize;
FILE* pFile = NULL;
void *pBuff = NULL;
// open file and get its size...
if ((pFile = fopen(pFilename, "rb")) == NULL)
{
TRACE_ERROR("static_readFile: fopen(%s) failed [%d]", pFilename, GET_LAST_ERR);
nResult = S_ERROR_ITEM_NOT_FOUND;
return nResult;
}
if (fseek(pFile, 0, SEEK_END) != 0)
{
TRACE_ERROR("static_readFile: fseek(%s) failed [%d]", pFilename, GET_LAST_ERR);
nResult = S_ERROR_UNDERLYING_OS;
goto error;
}
nFilesize = ftell(pFile);
if (nFilesize < 0)
{
TRACE_ERROR("static_readFile: ftell(%s) failed [%d]", pFilename, GET_LAST_ERR);
nResult = S_ERROR_UNDERLYING_OS;
goto error;
}
rewind(pFile);
// allocate the buffer
pBuff = malloc(nFilesize + 1);
if (pBuff == NULL)
{
TRACE_ERROR("static_readFile: out of memory");
nResult = S_ERROR_OUT_OF_MEMORY;
goto error;
}
// read the file
if (fread(pBuff, sizeof(uint8_t), (size_t)nFilesize, pFile) != (size_t)nFilesize)
{
TRACE_ERROR("static_readFile: fread failed [%d]", GET_LAST_ERR);
nResult = S_ERROR_UNDERLYING_OS;
goto error;
}
((char*)pBuff)[nFilesize] = 0;
*ppFile = pBuff;
*pnFileLength = nFilesize;
return S_SUCCESS;
error:
if (pBuff != NULL)
free(pBuff);
fclose(pFile);
*ppFile = NULL;
*pnFileLength = 0;
return nResult;
}
/* ---------------------------------------------------------------------------------
API functions.
---------------------------------------------------------------------------------*/
char* SMCPropGetSystemProperty(CONF_FILE* pConfFile, char* pPropertyName)
{
PROPERTY* pProperty;
pProperty=(PROPERTY*)SMCPropListFindElement(
&pConfFile->sSystemSectionPropertyList,
pPropertyName,
true);
if (pProperty!=NULL)
{
return pProperty->pValue;
}
return NULL;
}
uint32_t SMCPropGetSystemPropertyAsInt(CONF_FILE* pConfFile, char* pPropertyName)
{
uint32_t nValue;
char* pValue=SMCPropGetSystemProperty(pConfFile,pPropertyName);
if (libString2GetStringAsInt(pValue, &nValue) == S_SUCCESS)
{
return nValue;
}
return 0;
}
S_RESULT SMCPropParseConfigFile(char* pConfigFilename,CONF_FILE* pConfFile)
{
S_RESULT nError=S_SUCCESS;
void* pFile;
uint32_t nFileLength;
bool bReuseManifest;
assert(pConfFile!=NULL);
TRACE_INFO("Processing configuration file '%s'", pConfigFilename);
if(pConfigFilename != NULL)
{
nError=static_readFile(pConfigFilename,&pFile,&nFileLength);
if (nError!=S_SUCCESS)
{
goto error;
}
bReuseManifest = true;
}
else
{
assert(0);
}
nError=SMCPropYacc(pFile,nFileLength,pConfFile,NULL);
if(pConfigFilename != NULL)
{
free(pFile);
}
error:
return nError;
}