The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2007 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | #include <parser_dm.h> |
| 18 | #include <parser_dcf.h> |
| 19 | #include <svc_drm.h> |
| 20 | #include "log.h" |
| 21 | |
| 22 | #define DRM_SKIP_SPACE_TAB(p) while( (*(p) == ' ') || (*(p) == '\t') ) \ |
| 23 | p++ |
| 24 | |
| 25 | typedef enum _DM_PARSE_STATUS { |
| 26 | DM_PARSE_START, |
| 27 | DM_PARSING_RIGHTS, |
| 28 | DM_PARSING_CONTENT, |
| 29 | DM_PARSE_END |
| 30 | } DM_PARSE_STATUS; |
| 31 | |
| 32 | static int drm_strnicmp(const uint8_t* s1, const uint8_t* s2, int32_t n) |
| 33 | { |
| 34 | if (n < 0 || NULL == s1 || NULL == s2) |
| 35 | return -1; |
| 36 | |
| 37 | if (n == 0) |
| 38 | return 0; |
| 39 | |
| 40 | while (n-- != 0 && tolower(*s1) == tolower(*s2)) |
| 41 | { |
| 42 | if (n == 0 || *s1 == '\0' || *s2 == '\0') |
| 43 | break; |
| 44 | s1++; |
| 45 | s2++; |
| 46 | } |
| 47 | |
| 48 | return tolower(*s1) - tolower(*s2); |
| 49 | } |
| 50 | |
| 51 | const uint8_t * drm_strnstr(const uint8_t * str, const uint8_t * strSearch, int32_t len) |
| 52 | { |
| 53 | int32_t i, stringLen; |
| 54 | |
| 55 | if (NULL == str || NULL == strSearch || len <= 0) |
| 56 | return NULL; |
| 57 | |
| 58 | stringLen = strlen((char *)strSearch); |
| 59 | for (i = 0; i < len - stringLen + 1; i++) { |
| 60 | if (str[i] == *strSearch && 0 == memcmp(str + i, strSearch, stringLen)) |
| 61 | return str + i; |
| 62 | } |
| 63 | return NULL; |
| 64 | } |
| 65 | |
| 66 | /* See parser_dm.h */ |
| 67 | int32_t drm_parseDM(const uint8_t *buffer, int32_t bufferLen, T_DRM_DM_Info *pDmInfo) |
| 68 | { |
| 69 | const uint8_t *pStart = NULL, *pEnd = NULL; |
| 70 | const uint8_t *pBufferEnd; |
| 71 | int32_t contentLen, leftLen; |
| 72 | DM_PARSE_STATUS status = DM_PARSE_START; |
| 73 | int32_t boundaryLen; |
| 74 | |
| 75 | if (NULL == buffer || bufferLen <= 0 || NULL == pDmInfo) |
| 76 | return FALSE; |
| 77 | |
| 78 | /* Find the end of the input buffer */ |
| 79 | pBufferEnd = buffer + bufferLen; |
| 80 | leftLen = bufferLen; |
| 81 | |
| 82 | /* Find out the boundary */ |
| 83 | pStart = drm_strnstr(buffer, (uint8_t *)"--", bufferLen); |
| 84 | if (NULL == pStart) |
| 85 | return FALSE; /* No boundary error */ |
| 86 | pEnd = pStart; |
| 87 | |
| 88 | /* Record the boundary */ |
| 89 | pEnd = drm_strnstr(pStart, (uint8_t *)DRM_NEW_LINE_CRLF, leftLen); |
| 90 | /* if can not find the CRLF, return FALSE */ |
| 91 | if (NULL == pEnd) |
| 92 | return FALSE; |
Nick Kralevich | 25619b2 | 2013-04-29 10:49:47 -0700 | [diff] [blame^] | 93 | if ((pEnd - pStart) >= MAX_CONTENT_BOUNDARY_LEN) |
| 94 | return FALSE; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 95 | strncpy((char *)pDmInfo->boundary, (char *)pStart, pEnd - pStart); |
Nick Kralevich | 25619b2 | 2013-04-29 10:49:47 -0700 | [diff] [blame^] | 96 | pDmInfo->boundary[MAX_CONTENT_BOUNDARY_LEN - 1] = 0; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 97 | boundaryLen = strlen((char *)pDmInfo->boundary) + 2; /* 2 means: '\r' and '\n' */ |
| 98 | |
| 99 | pEnd += 2; /* skip the '\r' and '\n' */ |
| 100 | pStart = pEnd; |
| 101 | leftLen = pBufferEnd - pStart; |
| 102 | do { |
| 103 | pDmInfo->transferEncoding = DRM_MESSAGE_CODING_7BIT; /* According RFC2045 chapter 6.1, the default value should be 7bit.*/ |
| 104 | strcpy((char *)pDmInfo->contentType, "text/plain"); /* According RFC2045 chapter 5.2, the default value should be "text/plain". */ |
| 105 | |
| 106 | /* Deal the header information */ |
| 107 | while ((('\r' != *pStart) || ('\n' != *(pStart + 1))) && pStart < pBufferEnd) { |
| 108 | pEnd = drm_strnstr(pStart, (uint8_t *)DRM_NEW_LINE_CRLF, leftLen); |
| 109 | if (NULL == pEnd) |
| 110 | return FALSE; |
| 111 | |
| 112 | if (0 != pDmInfo->deliveryType) { /* This means the delivery type has been confirmed */ |
| 113 | if (0 == strncmp((char *)pStart, HEADERS_TRANSFER_CODING, HEADERS_TRANSFER_CODING_LEN)) { |
| 114 | pStart += HEADERS_TRANSFER_CODING_LEN; |
| 115 | DRM_SKIP_SPACE_TAB(pStart); |
| 116 | |
| 117 | if (0 == strncmp((char *)pStart, TRANSFER_CODING_TYPE_7BIT, pEnd - pStart)) |
| 118 | pDmInfo->transferEncoding = DRM_MESSAGE_CODING_7BIT; |
| 119 | else if (0 == strncmp((char *)pStart, TRANSFER_CODING_TYPE_8BIT, pEnd - pStart)) |
| 120 | pDmInfo->transferEncoding = DRM_MESSAGE_CODING_8BIT; |
| 121 | else if (0 == strncmp((char *)pStart, TRANSFER_CODING_TYPE_BINARY, pEnd - pStart)) |
| 122 | pDmInfo->transferEncoding = DRM_MESSAGE_CODING_BINARY; |
| 123 | else if (0 == strncmp((char *)pStart, TRANSFER_CODING_TYPE_BASE64, pEnd - pStart)) |
| 124 | pDmInfo->transferEncoding = DRM_MESSAGE_CODING_BASE64; |
| 125 | else |
| 126 | return FALSE; /* Unknown transferCoding error */ |
| 127 | } else if (0 == drm_strnicmp(pStart, (uint8_t *)HEADERS_CONTENT_TYPE, HEADERS_CONTENT_TYPE_LEN)) { |
| 128 | pStart += HEADERS_CONTENT_TYPE_LEN; |
| 129 | DRM_SKIP_SPACE_TAB(pStart); |
| 130 | |
| 131 | if (pEnd - pStart > 0) { |
Nick Kralevich | 25619b2 | 2013-04-29 10:49:47 -0700 | [diff] [blame^] | 132 | if ((pEnd - pStart) >= MAX_CONTENT_TYPE_LEN) |
| 133 | return FALSE; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 134 | strncpy((char *)pDmInfo->contentType, (char *)pStart, pEnd - pStart); |
| 135 | pDmInfo->contentType[pEnd - pStart] = '\0'; |
| 136 | } |
| 137 | } else if (0 == drm_strnicmp(pStart, (uint8_t *)HEADERS_CONTENT_ID, HEADERS_CONTENT_ID_LEN)) { |
| 138 | uint8_t tmpBuf[MAX_CONTENT_ID] = {0}; |
| 139 | uint8_t *pTmp; |
| 140 | |
| 141 | pStart += HEADERS_CONTENT_ID_LEN; |
| 142 | DRM_SKIP_SPACE_TAB(pStart); |
| 143 | |
| 144 | /* error: more than one content id */ |
| 145 | if(drm_strnstr(pStart, (uint8_t*)HEADERS_CONTENT_ID, pBufferEnd - pStart)){ |
Steve Block | 5baa3a6 | 2011-12-20 16:23:08 +0000 | [diff] [blame] | 146 | ALOGD("drm_dmParser: error: more than one content id\r\n"); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 147 | return FALSE; |
| 148 | } |
| 149 | |
| 150 | status = DM_PARSING_CONTENT; /* can go here means that the rights object has been parsed. */ |
| 151 | |
| 152 | /* Change the format from <...> to cid:... */ |
| 153 | if (NULL != (pTmp = (uint8_t *)memchr((char *)pStart, '<', pEnd - pStart))) { |
Nick Kralevich | 25619b2 | 2013-04-29 10:49:47 -0700 | [diff] [blame^] | 154 | if ((pEnd - pTmp - 1) >= (int) sizeof(tmpBuf)) |
| 155 | return FALSE; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 156 | strncpy((char *)tmpBuf, (char *)(pTmp + 1), pEnd - pTmp - 1); |
Nick Kralevich | 25619b2 | 2013-04-29 10:49:47 -0700 | [diff] [blame^] | 157 | tmpBuf[MAX_CONTENT_ID - 1] = 0; |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 158 | |
| 159 | if (NULL != (pTmp = (uint8_t *)memchr((char *)tmpBuf, '>', pEnd - pTmp - 1))) { |
| 160 | *pTmp = '\0'; |
| 161 | |
| 162 | memset(pDmInfo->contentID, 0, MAX_CONTENT_ID); |
Nick Kralevich | 25619b2 | 2013-04-29 10:49:47 -0700 | [diff] [blame^] | 163 | snprintf((char *)pDmInfo->contentID, MAX_CONTENT_ID, "%s%s", "cid:", (int8_t *)tmpBuf); |
The Android Open Source Project | 9066cfe | 2009-03-03 19:31:44 -0800 | [diff] [blame] | 164 | } |
| 165 | } |
| 166 | } |
| 167 | } else { /* First confirm delivery type, Forward_Lock, Combined Delivery or Separate Delivery */ |
| 168 | if (0 == drm_strnicmp(pStart, (uint8_t *)HEADERS_CONTENT_TYPE, HEADERS_CONTENT_TYPE_LEN)) { |
| 169 | pStart += HEADERS_CONTENT_TYPE_LEN; |
| 170 | DRM_SKIP_SPACE_TAB(pStart); |
| 171 | |
| 172 | if (pEnd - pStart > 0) { |
| 173 | strncpy((char *)pDmInfo->contentType, (char *)pStart, pEnd - pStart); |
| 174 | pDmInfo->contentType[pEnd - pStart] = '\0'; |
| 175 | } |
| 176 | |
| 177 | if (0 == strcmp((char *)pDmInfo->contentType, DRM_MIME_TYPE_RIGHTS_XML)) { |
| 178 | pDmInfo->deliveryType = COMBINED_DELIVERY; |
| 179 | status = DM_PARSING_RIGHTS; |
| 180 | } |
| 181 | else if (0 == strcmp((char *)pDmInfo->contentType, DRM_MIME_TYPE_CONTENT)) { |
| 182 | pDmInfo->deliveryType = SEPARATE_DELIVERY_FL; |
| 183 | status = DM_PARSING_CONTENT; |
| 184 | } |
| 185 | else if (0 == pDmInfo->deliveryType) { |
| 186 | pDmInfo->deliveryType = FORWARD_LOCK; |
| 187 | status = DM_PARSING_CONTENT; |
| 188 | } |
| 189 | } |
| 190 | } |
| 191 | pEnd += 2; /* skip the '\r' and '\n' */ |
| 192 | pStart = pEnd; |
| 193 | leftLen = pBufferEnd - pStart; |
| 194 | } |
| 195 | pStart += 2; /* skip the second CRLF: "\r\n" */ |
| 196 | pEnd = pStart; |
| 197 | |
| 198 | /* Deal the content part, including rel or real content */ |
| 199 | while (leftLen > 0) { |
| 200 | if (NULL == (pEnd = memchr(pEnd, '\r', leftLen))) { |
| 201 | pEnd = pBufferEnd; |
| 202 | break; /* no boundary found */ |
| 203 | } |
| 204 | |
| 205 | leftLen = pBufferEnd - pEnd; |
| 206 | if (leftLen < boundaryLen) { |
| 207 | pEnd = pBufferEnd; |
| 208 | break; /* here means may be the boundary has been split */ |
| 209 | } |
| 210 | |
| 211 | if (('\n' == *(pEnd + 1)) && (0 == memcmp(pEnd + 2, pDmInfo->boundary, strlen((char *)pDmInfo->boundary)))) |
| 212 | break; /* find the boundary here */ |
| 213 | |
| 214 | pEnd++; |
| 215 | leftLen--; |
| 216 | } |
| 217 | |
| 218 | if (pEnd >= pBufferEnd) |
| 219 | contentLen = DRM_UNKNOWN_DATA_LEN; |
| 220 | else |
| 221 | contentLen = pEnd - pStart; |
| 222 | |
| 223 | switch(pDmInfo->deliveryType) { |
| 224 | case FORWARD_LOCK: |
| 225 | pDmInfo->contentLen = contentLen; |
| 226 | pDmInfo->contentOffset = pStart - buffer; |
| 227 | status = DM_PARSE_END; |
| 228 | break; |
| 229 | case COMBINED_DELIVERY: |
| 230 | if (DM_PARSING_RIGHTS == status) { |
| 231 | pDmInfo->rightsLen = contentLen; |
| 232 | pDmInfo->rightsOffset = pStart - buffer; |
| 233 | } else { |
| 234 | pDmInfo->contentLen = contentLen; |
| 235 | pDmInfo->contentOffset = pStart - buffer; |
| 236 | status = DM_PARSE_END; |
| 237 | } |
| 238 | break; |
| 239 | case SEPARATE_DELIVERY_FL: |
| 240 | { |
| 241 | T_DRM_DCF_Info dcfInfo; |
| 242 | uint8_t* pEncData = NULL; |
| 243 | |
| 244 | memset(&dcfInfo, 0, sizeof(T_DRM_DCF_Info)); |
| 245 | if (DRM_UNKNOWN_DATA_LEN == contentLen) |
| 246 | contentLen = pEnd - pStart; |
| 247 | if (FALSE == drm_dcfParser(pStart, contentLen, &dcfInfo, &pEncData)) |
| 248 | return FALSE; |
| 249 | |
| 250 | pDmInfo->contentLen = dcfInfo.EncryptedDataLen; |
| 251 | pDmInfo->contentOffset = pEncData - buffer; |
| 252 | strcpy((char *)pDmInfo->contentType, (char *)dcfInfo.ContentType); |
| 253 | strcpy((char *)pDmInfo->contentID, (char *)dcfInfo.ContentURI); |
| 254 | strcpy((char *)pDmInfo->rightsIssuer, (char *)dcfInfo.Rights_Issuer); |
| 255 | status = DM_PARSE_END; |
| 256 | } |
| 257 | break; |
| 258 | default: |
| 259 | return FALSE; |
| 260 | } |
| 261 | |
| 262 | if (DM_PARSING_RIGHTS == status) { |
| 263 | /* Here means the rights object data has been completed, boundary must exist */ |
| 264 | leftLen = pBufferEnd - pEnd; |
| 265 | pStart = drm_strnstr(pEnd, pDmInfo->boundary, leftLen); |
| 266 | if (NULL == pStart) |
| 267 | return FALSE; |
| 268 | leftLen = pBufferEnd - pStart; |
| 269 | pEnd = drm_strnstr(pStart, (uint8_t *)DRM_NEW_LINE_CRLF, leftLen); |
| 270 | if (NULL == pEnd) |
| 271 | return FALSE; /* only rights object, no media object, error */ |
| 272 | |
| 273 | pEnd += 2; /* skip the "\r\n" */ |
| 274 | pStart = pEnd; |
| 275 | } |
| 276 | } while (DM_PARSE_END != status); |
| 277 | |
| 278 | return TRUE; |
| 279 | } |