blob: 4b4a2dadf87a7cc14c2a294a98bd3a924ad28446 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
25typedef 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
32static 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
51const 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 */
67int32_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 Kralevich25619b22013-04-29 10:49:47 -070093 if ((pEnd - pStart) >= MAX_CONTENT_BOUNDARY_LEN)
94 return FALSE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 strncpy((char *)pDmInfo->boundary, (char *)pStart, pEnd - pStart);
Nick Kralevich25619b22013-04-29 10:49:47 -070096 pDmInfo->boundary[MAX_CONTENT_BOUNDARY_LEN - 1] = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097 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 Kralevich25619b22013-04-29 10:49:47 -0700132 if ((pEnd - pStart) >= MAX_CONTENT_TYPE_LEN)
133 return FALSE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134 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 Block5baa3a62011-12-20 16:23:08 +0000146 ALOGD("drm_dmParser: error: more than one content id\r\n");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800147 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 Kralevich25619b22013-04-29 10:49:47 -0700154 if ((pEnd - pTmp - 1) >= (int) sizeof(tmpBuf))
155 return FALSE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800156 strncpy((char *)tmpBuf, (char *)(pTmp + 1), pEnd - pTmp - 1);
Nick Kralevich25619b22013-04-29 10:49:47 -0700157 tmpBuf[MAX_CONTENT_ID - 1] = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800158
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 Kralevich25619b22013-04-29 10:49:47 -0700163 snprintf((char *)pDmInfo->contentID, MAX_CONTENT_ID, "%s%s", "cid:", (int8_t *)tmpBuf);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 }
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}