/** * 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 "lib_manifest2.h" #include <string.h> #define CHAR_CR (uint8_t)0x0D #define CHAR_LF (uint8_t)0x0A #define CHAR_TAB (uint8_t)0x09 #ifdef LIB_TOOL_IMPLEMENTATION #include "exos_trace.h" #define LOG_ERROR(pContext, msg, ...) log_error("%s - line %d: " msg, pContext->pManifestName, pContext->nLine, __VA_ARGS__) static void log_error(const char* msg, ...) { va_list arg_list; va_start(arg_list, msg); exosTraceVPrintf("LIB_MANIFEST2", EXOS_TRACE_ORG_APPLI, K_PRINT_ERROR_LOG, msg, &arg_list); va_end(arg_list); } #else /* No error messages on the target */ #ifdef __SYMBIAN32__ #define LOG_ERROR(pContext...) #else #define LOG_ERROR(...) #endif #endif void libManifest2InitContext( LIB_MANIFEST2_CONTEXT* pContext) { pContext->nOffset = 0; pContext->nLine = 1; pContext->nSectionStartOffset = 0; } #define CHARACTER_NAME_FIRST 1 #define CHARACTER_NAME_SUBSEQUENT 2 #define CHARACTER_SECTION_NAME 3 static bool static_checkCharacter(uint8_t x, uint32_t nType) { /* [A-Za-z0-9] is acceptable for everyone */ if (x >= (uint8_t)'a' && x <= (uint8_t)'z') { return true; } if (x >=(uint8_t)'A' && x <= (uint8_t)'Z') { return true; } if (x >= (uint8_t)'0' && x <= (uint8_t)'9') { return true; } if (nType == CHARACTER_NAME_FIRST) { return false; } /* Subsequent property name or section name characters can be [_.-] */ if (x == (uint8_t)'_' || x == (uint8_t)'.' || x == (uint8_t)'-') { return true; } if (nType == CHARACTER_NAME_SUBSEQUENT) { return false; } /* Space is also allowed in section names */ if (x == (uint8_t)' ') { return true; } return false; } static bool static_sectionNameEqualCaseInsensitive( uint8_t* pName1, uint32_t nName1Length, uint8_t* pName2, uint32_t nName2Length) { uint32_t i; if (nName1Length != nName2Length) { return false; } for (i = 0; i < nName1Length; i++) { uint8_t x1 = pName1[i]; uint8_t x2 = pName2[i]; /* This code assumes the characters have been checked before */ if ((x1 & ~0x20) != (x2 & ~0x20)) { return false; } } return true; } static S_RESULT static_libManifest2GetNextItemInternal( LIB_MANIFEST2_CONTEXT* pContext, OUT uint8_t** ppName, OUT uint32_t* pNameLength, OUT uint8_t** ppValue, OUT uint32_t* pValueLength) { S_RESULT nResult = S_ERROR_BAD_FORMAT; uint8_t* pCurrent = pContext->pManifestContent + pContext->nOffset; uint8_t* pEnd = pContext->pManifestContent + pContext->nManifestLength; uint8_t* pLastNonWhitespaceChar; uint32_t nCurrentSequenceCount; uint32_t nCurrentChar; if (pContext->nType != LIB_MANIFEST2_TYPE_COMPILED) { /* Skip leading BOM if we're at the start */ if (pCurrent == pContext->pManifestContent) { /* We're at the start. Skip leading BOM if present */ /* Note that the UTF-8 encoding of the BOM marker is EF BB BF */ if (pContext->nManifestLength >= 3 && pCurrent[0] == 0xEF && pCurrent[1] == 0xBB && pCurrent[2] == 0xBF) { pCurrent += 3; } } /* Skip comments and newlines */ while (pCurrent < pEnd) { if (*pCurrent == (uint8_t)'#') { /* This is the start of a comment. Skip until end of line or end of file */ pCurrent++; while (pCurrent < pEnd && *pCurrent != CHAR_LF && *pCurrent != CHAR_CR) { if (*pCurrent == 0) { LOG_ERROR(pContext, "NUL character forbidden"); goto error; } pCurrent++; } } else if (*pCurrent == CHAR_CR) { /* Check if a LF follows */ pCurrent++; if (pCurrent < pEnd && *pCurrent == CHAR_LF) { pCurrent++; } pContext->nLine++; } else if (*pCurrent == CHAR_LF) { pCurrent++; pContext->nLine++; } else if (*pCurrent == ' ' || *pCurrent == '\t') { /* this is the start of a all-whitespace line */ /* NOTE: this is not allowed by the current spec: spec update needed */ pCurrent++; while (pCurrent < pEnd) { if (*pCurrent == CHAR_LF || *pCurrent == CHAR_CR) { /* End-of-line reached */ break; } if (! (*pCurrent == ' ' || *pCurrent == '\t')) { LOG_ERROR(pContext, "A line starting with whitespaces must contain only whitespaces. Illegal character: 0x%02X", *pCurrent); goto error; } pCurrent++; } } else { break; } } } if (pCurrent >= pEnd) { /* No more properties */ nResult = S_ERROR_ITEM_NOT_FOUND; goto error; } if (pContext->nType == LIB_MANIFEST2_TYPE_SOURCE_WITH_SECTIONS) { if (*pCurrent == '[') { /* This is a section descriptor */ pCurrent++; *ppName = pCurrent; *ppValue = NULL; *pValueLength = 0; while (true) { if (pCurrent >= pEnd) { LOG_ERROR(pContext, "EOF reached within a section name"); goto error; } if (*pCurrent == ']') { /* End of section name */ *pNameLength = pCurrent - *ppName; pCurrent++; /* Skip spaces and tabs. Note that this is a deviation from the current spec (see SWIS). Spec must be updated */ while (pCurrent < pEnd) { if (*pCurrent == ' ' || *pCurrent == '\t') { pCurrent++; } else if (*pCurrent == CHAR_CR || *pCurrent == CHAR_LF) { /* End of line */ break; } else { LOG_ERROR(pContext, "Non-space character follows a sectino header: 0x02X", *pCurrent); } } pContext->nOffset = pCurrent - pContext->pManifestContent; pContext->nSectionStartOffset = pContext->nOffset; return S_SUCCESS; } /* Check section name character */ if (!static_checkCharacter(*pCurrent, CHARACTER_SECTION_NAME)) { LOG_ERROR(pContext, "Invalid character for a section name: 0x%02X", *pCurrent); goto error; } pCurrent++; } } if (pContext->nSectionStartOffset == 0) { /* No section has been found yet. This is a bad format */ LOG_ERROR(pContext, "Property found outside any section"); goto error; } } *ppName = pCurrent; /* Check first character of name is in [A-Za-z0-9] */ if (!static_checkCharacter(*pCurrent, CHARACTER_NAME_FIRST)) { LOG_ERROR(pContext, "Invalid first character for a property name: 0x%02X", *pCurrent); goto error; } pCurrent++; pLastNonWhitespaceChar = pCurrent; while (true) { if (pCurrent == pEnd) { LOG_ERROR(pContext, "EOF reached within a property name"); goto error; } if (*pCurrent == ':') { /* Colon reached */ break; } if (pContext->nType != LIB_MANIFEST2_TYPE_COMPILED) { /* In source manifest, allow space characters before the colon. This is a deviation from the spec. Spec must be updated */ if (*pCurrent == ' ' || *pCurrent == '\t') { pCurrent++; continue; } } if (!static_checkCharacter(*pCurrent, CHARACTER_NAME_SUBSEQUENT)) { LOG_ERROR(pContext, "Invalid character for a property name: 0x%02X", *pCurrent); goto error; } if (pContext->nType != LIB_MANIFEST2_TYPE_COMPILED) { /* Even in a source manifest, property name cannot contain spaces! */ if (pCurrent != pLastNonWhitespaceChar) { LOG_ERROR(pContext, "Property name cannot contain spaces"); goto error; } } pCurrent++; pLastNonWhitespaceChar = pCurrent; } *pNameLength = pLastNonWhitespaceChar - *ppName; pCurrent++; /* Skip spaces and tabs on the right of the colon */ while (pCurrent < pEnd && (*pCurrent == ' ' || *pCurrent == '\t')) { pCurrent++; } *ppValue = pCurrent; pLastNonWhitespaceChar = pCurrent-1; nCurrentSequenceCount = 0; nCurrentChar = 0; while (pCurrent < pEnd) { uint32_t x; x = *pCurrent; if ((x & 0x80) == 0) { if (nCurrentSequenceCount != 0) { /* We were expecting a 10xxxxxx byte: ill-formed UTF-8 */ LOG_ERROR(pContext, "Invalid UTF-8 sequence"); goto error; } else if (x == 0) { /* The null character is forbidden */ LOG_ERROR(pContext, "NUL character forbidden"); goto error; } /* We have a well-formed Unicode character */ nCurrentChar = x; } else if ((x & 0xC0) == 0xC0) { /* Start of a sequence */ if (nCurrentSequenceCount != 0) { /* We were expecting a 10xxxxxx byte: ill-formed UTF-8 */ LOG_ERROR(pContext, "Invalid UTF-8 sequence"); goto error; } else if ((x & 0xE0) == 0xC0) { /* 1 byte follows */ nCurrentChar = x & 0x1F; nCurrentSequenceCount = 1; if ((x & 0x1E) == 0) { /* Illegal UTF-8: overlong encoding of character in the [0x00-0x7F] range (must use 1-byte encoding, not a 2-byte encoding) */ LOG_ERROR(pContext, "Invalid UTF-8 sequence"); goto error; } } else if ((x & 0xF0) == 0xE0) { /* 2 bytes follow */ nCurrentChar = x & 0x0F; nCurrentSequenceCount = 2; } else if ((x & 0xF8) == 0xF0) { /* 3 bytes follow */ nCurrentChar = x & 0x07; nCurrentSequenceCount = 3; } else { /* Illegal start of sequence */ LOG_ERROR(pContext, "Invalid UTF-8 sequence"); goto error; } } else if ((x & 0xC0) == 0x80) { /* Continuation byte */ if (nCurrentSequenceCount == 0) { /* We were expecting a sequence start, not a continuation byte */ LOG_ERROR(pContext, "Invalid UTF-8 sequence"); goto error; } else { if (nCurrentSequenceCount == 2) { /* We're in a 3-byte sequence, check that we're not using an overlong sequence */ if (nCurrentChar == 0 && (x & 0x20) == 0) { /* The character starts with at least 5 zero bits, so has fewer than 11 bits. It should have used a 2-byte sequence, not a 3-byte sequence */ LOG_ERROR(pContext, "Invalid UTF-8 sequence"); goto error; } } else if (nCurrentSequenceCount == 3) { if (nCurrentChar == 0 && (x & 0x30) == 0) { /* The character starts with at least 5 zero bits, so has fewer than 16 bits. It should have used a 3-byte sequence, not a 4-byte sequence */ LOG_ERROR(pContext, "Invalid UTF-8 sequence"); goto error; } } nCurrentSequenceCount--; nCurrentChar = (nCurrentChar << 6) | (x & 0x3F); } } else { /* Illegal byte */ LOG_ERROR(pContext, "Invalid UTF-8 sequence"); goto error; } if (nCurrentSequenceCount == 0) { /* nCurrentChar contains the current Unicode character */ /* check character */ if ((nCurrentChar >= 0xD800 && nCurrentChar < 0xE000) || nCurrentChar >= 0x110000) { /* Illegal code point */ LOG_ERROR(pContext, "Invalid UTF-8 code point 0x%X", nCurrentChar); goto error; } if (*pCurrent == CHAR_CR) { if (pContext->nType == LIB_MANIFEST2_TYPE_COMPILED) { /* Check if a LF follows */ pCurrent++; if (pCurrent < pEnd && *pCurrent == CHAR_LF) { pCurrent++; } pContext->nLine++; } goto end; } else if (*pCurrent == CHAR_LF) { if (pContext->nType == LIB_MANIFEST2_TYPE_COMPILED) { pCurrent++; pContext->nLine++; } goto end; } } if (*pCurrent != ' ' && *pCurrent != CHAR_TAB) { /* It's a non-whitespace char */ pLastNonWhitespaceChar = pCurrent; } pCurrent++; } /* Hit the end of the manifest; Check that we're not in the middle of a sequence */ if (nCurrentSequenceCount != 0) { LOG_ERROR(pContext, "File ends in the middle of an UTF-8 sequence"); goto error; } end: *pValueLength = pLastNonWhitespaceChar - *ppValue + 1; pContext->nOffset = pCurrent - pContext->pManifestContent; return S_SUCCESS; error: *ppName = NULL; *pNameLength = 0; *ppValue = NULL; *pValueLength = 0; return nResult; } S_RESULT libManifest2GetNextItem( LIB_MANIFEST2_CONTEXT* pContext, OUT uint8_t** ppName, OUT uint32_t* pNameLength, OUT uint8_t** ppValue, OUT uint32_t* pValueLength) { if (pContext->nType == LIB_MANIFEST2_TYPE_COMPILED) { /* Don't check for duplicates in binary manifests */ return static_libManifest2GetNextItemInternal( pContext, ppName, pNameLength, ppValue, pValueLength); } else { uint32_t nOriginalOffset = pContext->nOffset; uint32_t nOffset; uint32_t nLine; uint32_t nSectionStartOffset; S_RESULT nResult; uint8_t* pDupName; uint32_t nDupNameLength; uint8_t* pDupValue; uint32_t nDupValueLength; /* First get the item */ nResult = static_libManifest2GetNextItemInternal( pContext, ppName, pNameLength, ppValue, pValueLength); if (nResult != S_SUCCESS) { return nResult; } /* Save the state of the parser */ nOffset = pContext->nOffset; nLine = pContext->nLine; nSectionStartOffset = pContext->nSectionStartOffset; if (pContext->nType == LIB_MANIFEST2_TYPE_SOURCE) { pContext->nOffset = 0; } else if (*ppValue == NULL) { /* The item was a section header. Iterate on all section headers and check for duplicates */ pContext->nOffset = 0; } else { if (nSectionStartOffset == 0) { LOG_ERROR(pContext, "Property definition outside any section"); goto bad_format; } /* Iterate only on the properties in the section */ pContext->nOffset = nSectionStartOffset; } while (pContext->nOffset < nOriginalOffset) { static_libManifest2GetNextItemInternal( pContext, &pDupName, &nDupNameLength, &pDupValue, &nDupValueLength); if (pContext->nType == LIB_MANIFEST2_TYPE_SOURCE_WITH_SECTIONS && *ppValue == NULL) { /* Check for duplicate section names */ if (pDupValue == NULL && static_sectionNameEqualCaseInsensitive( *ppName, *pNameLength, pDupName, nDupNameLength)) { pContext->nOffset = nOffset; pContext->nLine = nLine; pContext->nSectionStartOffset = nSectionStartOffset; LOG_ERROR(pContext, "Duplicate section %.*s", nDupNameLength, pDupName); goto bad_format; } } else { /* Check for duplicate property name */ if (nDupNameLength == *pNameLength && memcmp(pDupName, *ppName, nDupNameLength) == 0) { /* Duplicated property */ pContext->nOffset = nOffset; pContext->nLine = nLine; pContext->nSectionStartOffset = nSectionStartOffset; LOG_ERROR(pContext,"Duplicate property %.*s", nDupNameLength, pDupName); goto bad_format; } } } /* Everything's fine. restore context and exit */ /* Restore the context */ pContext->nOffset = nOffset; pContext->nLine = nLine; pContext->nSectionStartOffset = nSectionStartOffset; return S_SUCCESS; bad_format: *ppName = NULL; *pNameLength = 0; *ppValue = NULL; *pValueLength = 0; return S_ERROR_BAD_FORMAT; } } S_RESULT libManifest2CheckFormat( LIB_MANIFEST2_CONTEXT* pContext, uint32_t* pnItemCount) { uint32_t nPropertyCount = 0; uint8_t* pName; uint32_t nNameLength; uint8_t* pValue; uint32_t nValueLength; S_RESULT nResult; pContext->nOffset = 0; pContext->nLine = 1; pContext->nSectionStartOffset = 0; while (true) { nResult = libManifest2GetNextItem( pContext, &pName, &nNameLength, &pValue, &nValueLength); if (nResult == S_ERROR_ITEM_NOT_FOUND) { if (pnItemCount != NULL) { *pnItemCount = nPropertyCount; } return S_SUCCESS; } if (nResult != S_SUCCESS) { return nResult; } nPropertyCount++; } }