/*
* Copyright (C) 2007 The Android Open Source Project
*
* 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.
*/
#include <parser_dm.h>
#include <parser_dcf.h>
#include <svc_drm.h>
#include "log.h"
#define DRM_SKIP_SPACE_TAB(p) while( (*(p) == ' ') || (*(p) == '\t') ) \
p++
typedef enum _DM_PARSE_STATUS {
DM_PARSE_START,
DM_PARSING_RIGHTS,
DM_PARSING_CONTENT,
DM_PARSE_END
} DM_PARSE_STATUS;
static int drm_strnicmp(const uint8_t* s1, const uint8_t* s2, int32_t n)
{
if (n < 0 || NULL == s1 || NULL == s2)
return -1;
if (n == 0)
return 0;
while (n-- != 0 && tolower(*s1) == tolower(*s2))
{
if (n == 0 || *s1 == '\0' || *s2 == '\0')
break;
s1++;
s2++;
}
return tolower(*s1) - tolower(*s2);
}
const uint8_t * drm_strnstr(const uint8_t * str, const uint8_t * strSearch, int32_t len)
{
int32_t i, stringLen;
if (NULL == str || NULL == strSearch || len <= 0)
return NULL;
stringLen = strlen((char *)strSearch);
for (i = 0; i < len - stringLen + 1; i++) {
if (str[i] == *strSearch && 0 == memcmp(str + i, strSearch, stringLen))
return str + i;
}
return NULL;
}
/* See parser_dm.h */
int32_t drm_parseDM(const uint8_t *buffer, int32_t bufferLen, T_DRM_DM_Info *pDmInfo)
{
const uint8_t *pStart = NULL, *pEnd = NULL;
const uint8_t *pBufferEnd;
int32_t contentLen, leftLen;
DM_PARSE_STATUS status = DM_PARSE_START;
int32_t boundaryLen;
if (NULL == buffer || bufferLen <= 0 || NULL == pDmInfo)
return FALSE;
/* Find the end of the input buffer */
pBufferEnd = buffer + bufferLen;
leftLen = bufferLen;
/* Find out the boundary */
pStart = drm_strnstr(buffer, (uint8_t *)"--", bufferLen);
if (NULL == pStart)
return FALSE; /* No boundary error */
pEnd = pStart;
/* Record the boundary */
pEnd = drm_strnstr(pStart, (uint8_t *)DRM_NEW_LINE_CRLF, leftLen);
/* if can not find the CRLF, return FALSE */
if (NULL == pEnd)
return FALSE;
strncpy((char *)pDmInfo->boundary, (char *)pStart, pEnd - pStart);
boundaryLen = strlen((char *)pDmInfo->boundary) + 2; /* 2 means: '\r' and '\n' */
pEnd += 2; /* skip the '\r' and '\n' */
pStart = pEnd;
leftLen = pBufferEnd - pStart;
do {
pDmInfo->transferEncoding = DRM_MESSAGE_CODING_7BIT; /* According RFC2045 chapter 6.1, the default value should be 7bit.*/
strcpy((char *)pDmInfo->contentType, "text/plain"); /* According RFC2045 chapter 5.2, the default value should be "text/plain". */
/* Deal the header information */
while ((('\r' != *pStart) || ('\n' != *(pStart + 1))) && pStart < pBufferEnd) {
pEnd = drm_strnstr(pStart, (uint8_t *)DRM_NEW_LINE_CRLF, leftLen);
if (NULL == pEnd)
return FALSE;
if (0 != pDmInfo->deliveryType) { /* This means the delivery type has been confirmed */
if (0 == strncmp((char *)pStart, HEADERS_TRANSFER_CODING, HEADERS_TRANSFER_CODING_LEN)) {
pStart += HEADERS_TRANSFER_CODING_LEN;
DRM_SKIP_SPACE_TAB(pStart);
if (0 == strncmp((char *)pStart, TRANSFER_CODING_TYPE_7BIT, pEnd - pStart))
pDmInfo->transferEncoding = DRM_MESSAGE_CODING_7BIT;
else if (0 == strncmp((char *)pStart, TRANSFER_CODING_TYPE_8BIT, pEnd - pStart))
pDmInfo->transferEncoding = DRM_MESSAGE_CODING_8BIT;
else if (0 == strncmp((char *)pStart, TRANSFER_CODING_TYPE_BINARY, pEnd - pStart))
pDmInfo->transferEncoding = DRM_MESSAGE_CODING_BINARY;
else if (0 == strncmp((char *)pStart, TRANSFER_CODING_TYPE_BASE64, pEnd - pStart))
pDmInfo->transferEncoding = DRM_MESSAGE_CODING_BASE64;
else
return FALSE; /* Unknown transferCoding error */
} else if (0 == drm_strnicmp(pStart, (uint8_t *)HEADERS_CONTENT_TYPE, HEADERS_CONTENT_TYPE_LEN)) {
pStart += HEADERS_CONTENT_TYPE_LEN;
DRM_SKIP_SPACE_TAB(pStart);
if (pEnd - pStart > 0) {
strncpy((char *)pDmInfo->contentType, (char *)pStart, pEnd - pStart);
pDmInfo->contentType[pEnd - pStart] = '\0';
}
} else if (0 == drm_strnicmp(pStart, (uint8_t *)HEADERS_CONTENT_ID, HEADERS_CONTENT_ID_LEN)) {
uint8_t tmpBuf[MAX_CONTENT_ID] = {0};
uint8_t *pTmp;
pStart += HEADERS_CONTENT_ID_LEN;
DRM_SKIP_SPACE_TAB(pStart);
/* error: more than one content id */
if(drm_strnstr(pStart, (uint8_t*)HEADERS_CONTENT_ID, pBufferEnd - pStart)){
LOGD("drm_dmParser: error: more than one content id\r\n");
return FALSE;
}
status = DM_PARSING_CONTENT; /* can go here means that the rights object has been parsed. */
/* Change the format from <...> to cid:... */
if (NULL != (pTmp = (uint8_t *)memchr((char *)pStart, '<', pEnd - pStart))) {
strncpy((char *)tmpBuf, (char *)(pTmp + 1), pEnd - pTmp - 1);
if (NULL != (pTmp = (uint8_t *)memchr((char *)tmpBuf, '>', pEnd - pTmp - 1))) {
*pTmp = '\0';
memset(pDmInfo->contentID, 0, MAX_CONTENT_ID);
sprintf((char *)pDmInfo->contentID, "%s%s", "cid:", (int8_t *)tmpBuf);
}
}
}
} else { /* First confirm delivery type, Forward_Lock, Combined Delivery or Separate Delivery */
if (0 == drm_strnicmp(pStart, (uint8_t *)HEADERS_CONTENT_TYPE, HEADERS_CONTENT_TYPE_LEN)) {
pStart += HEADERS_CONTENT_TYPE_LEN;
DRM_SKIP_SPACE_TAB(pStart);
if (pEnd - pStart > 0) {
strncpy((char *)pDmInfo->contentType, (char *)pStart, pEnd - pStart);
pDmInfo->contentType[pEnd - pStart] = '\0';
}
if (0 == strcmp((char *)pDmInfo->contentType, DRM_MIME_TYPE_RIGHTS_XML)) {
pDmInfo->deliveryType = COMBINED_DELIVERY;
status = DM_PARSING_RIGHTS;
}
else if (0 == strcmp((char *)pDmInfo->contentType, DRM_MIME_TYPE_CONTENT)) {
pDmInfo->deliveryType = SEPARATE_DELIVERY_FL;
status = DM_PARSING_CONTENT;
}
else if (0 == pDmInfo->deliveryType) {
pDmInfo->deliveryType = FORWARD_LOCK;
status = DM_PARSING_CONTENT;
}
}
}
pEnd += 2; /* skip the '\r' and '\n' */
pStart = pEnd;
leftLen = pBufferEnd - pStart;
}
pStart += 2; /* skip the second CRLF: "\r\n" */
pEnd = pStart;
/* Deal the content part, including rel or real content */
while (leftLen > 0) {
if (NULL == (pEnd = memchr(pEnd, '\r', leftLen))) {
pEnd = pBufferEnd;
break; /* no boundary found */
}
leftLen = pBufferEnd - pEnd;
if (leftLen < boundaryLen) {
pEnd = pBufferEnd;
break; /* here means may be the boundary has been split */
}
if (('\n' == *(pEnd + 1)) && (0 == memcmp(pEnd + 2, pDmInfo->boundary, strlen((char *)pDmInfo->boundary))))
break; /* find the boundary here */
pEnd++;
leftLen--;
}
if (pEnd >= pBufferEnd)
contentLen = DRM_UNKNOWN_DATA_LEN;
else
contentLen = pEnd - pStart;
switch(pDmInfo->deliveryType) {
case FORWARD_LOCK:
pDmInfo->contentLen = contentLen;
pDmInfo->contentOffset = pStart - buffer;
status = DM_PARSE_END;
break;
case COMBINED_DELIVERY:
if (DM_PARSING_RIGHTS == status) {
pDmInfo->rightsLen = contentLen;
pDmInfo->rightsOffset = pStart - buffer;
} else {
pDmInfo->contentLen = contentLen;
pDmInfo->contentOffset = pStart - buffer;
status = DM_PARSE_END;
}
break;
case SEPARATE_DELIVERY_FL:
{
T_DRM_DCF_Info dcfInfo;
uint8_t* pEncData = NULL;
memset(&dcfInfo, 0, sizeof(T_DRM_DCF_Info));
if (DRM_UNKNOWN_DATA_LEN == contentLen)
contentLen = pEnd - pStart;
if (FALSE == drm_dcfParser(pStart, contentLen, &dcfInfo, &pEncData))
return FALSE;
pDmInfo->contentLen = dcfInfo.EncryptedDataLen;
pDmInfo->contentOffset = pEncData - buffer;
strcpy((char *)pDmInfo->contentType, (char *)dcfInfo.ContentType);
strcpy((char *)pDmInfo->contentID, (char *)dcfInfo.ContentURI);
strcpy((char *)pDmInfo->rightsIssuer, (char *)dcfInfo.Rights_Issuer);
status = DM_PARSE_END;
}
break;
default:
return FALSE;
}
if (DM_PARSING_RIGHTS == status) {
/* Here means the rights object data has been completed, boundary must exist */
leftLen = pBufferEnd - pEnd;
pStart = drm_strnstr(pEnd, pDmInfo->boundary, leftLen);
if (NULL == pStart)
return FALSE;
leftLen = pBufferEnd - pStart;
pEnd = drm_strnstr(pStart, (uint8_t *)DRM_NEW_LINE_CRLF, leftLen);
if (NULL == pEnd)
return FALSE; /* only rights object, no media object, error */
pEnd += 2; /* skip the "\r\n" */
pStart = pEnd;
}
} while (DM_PARSE_END != status);
return TRUE;
}