| /* |
| * 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; |
| } |