1/*
2 * Copyright 2012, 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 "ParsedMessage.h"
18
19#include <ctype.h>
20#include <media/stagefright/foundation/ABuffer.h>
21#include <media/stagefright/foundation/ADebug.h>
22
23namespace android {
24
25// static
26sp<ParsedMessage> ParsedMessage::Parse(
27        const char *data, size_t size, bool noMoreData, size_t *length) {
28    sp<ParsedMessage> msg = new ParsedMessage;
29    ssize_t res = msg->parse(data, size, noMoreData);
30
31    if (res < 0) {
32        *length = 0;
33        return NULL;
34    }
35
36    *length = res;
37    return msg;
38}
39
40ParsedMessage::ParsedMessage() {
41}
42
43ParsedMessage::~ParsedMessage() {
44}
45
46bool ParsedMessage::findString(const char *name, AString *value) const {
47    AString key = name;
48    key.tolower();
49
50    ssize_t index = mDict.indexOfKey(key);
51
52    if (index < 0) {
53        value->clear();
54
55        return false;
56    }
57
58    *value = mDict.valueAt(index);
59    return true;
60}
61
62bool ParsedMessage::findInt32(const char *name, int32_t *value) const {
63    AString stringValue;
64
65    if (!findString(name, &stringValue)) {
66        return false;
67    }
68
69    char *end;
70    *value = strtol(stringValue.c_str(), &end, 10);
71
72    if (end == stringValue.c_str() || *end != '\0') {
73        *value = 0;
74        return false;
75    }
76
77    return true;
78}
79
80const char *ParsedMessage::getContent() const {
81    return mContent.c_str();
82}
83
84ssize_t ParsedMessage::parse(const char *data, size_t size, bool noMoreData) {
85    if (size == 0) {
86        return -1;
87    }
88
89    ssize_t lastDictIndex = -1;
90
91    size_t offset = 0;
92    while (offset < size) {
93        size_t lineEndOffset = offset;
94        while (lineEndOffset + 1 < size
95                && (data[lineEndOffset] != '\r'
96                        || data[lineEndOffset + 1] != '\n')) {
97            ++lineEndOffset;
98        }
99
100        if (lineEndOffset + 1 >= size) {
101            return -1;
102        }
103
104        AString line(&data[offset], lineEndOffset - offset);
105
106        if (offset == 0) {
107            // Special handling for the request/status line.
108
109            mDict.add(AString("_"), line);
110            offset = lineEndOffset + 2;
111
112            continue;
113        }
114
115        if (lineEndOffset == offset) {
116            offset += 2;
117            break;
118        }
119
120        if (line.c_str()[0] == ' ' || line.c_str()[0] == '\t') {
121            // Support for folded header values.
122
123            if (lastDictIndex >= 0) {
124                // Otherwise it's malformed since the first header line
125                // cannot continue anything...
126
127                AString &value = mDict.editValueAt(lastDictIndex);
128                value.append(line);
129            }
130
131            offset = lineEndOffset + 2;
132            continue;
133        }
134
135        ssize_t colonPos = line.find(":");
136        if (colonPos >= 0) {
137            AString key(line, 0, colonPos);
138            key.trim();
139            key.tolower();
140
141            line.erase(0, colonPos + 1);
142
143            lastDictIndex = mDict.add(key, line);
144        }
145
146        offset = lineEndOffset + 2;
147    }
148
149    for (size_t i = 0; i < mDict.size(); ++i) {
150        mDict.editValueAt(i).trim();
151    }
152
153    // Found the end of headers.
154
155    int32_t contentLength;
156    if (!findInt32("content-length", &contentLength) || contentLength < 0) {
157        contentLength = 0;
158    }
159
160    size_t totalLength = offset + contentLength;
161
162    if (size < totalLength) {
163        return -1;
164    }
165
166    mContent.setTo(&data[offset], contentLength);
167
168    return totalLength;
169}
170
171void ParsedMessage::getRequestField(size_t index, AString *field) const {
172    AString line;
173    CHECK(findString("_", &line));
174
175    size_t prevOffset = 0;
176    size_t offset = 0;
177    for (size_t i = 0; i <= index; ++i) {
178        ssize_t spacePos = line.find(" ", offset);
179
180        if (spacePos < 0) {
181            spacePos = line.size();
182        }
183
184        prevOffset = offset;
185        offset = spacePos + 1;
186    }
187
188    field->setTo(line, prevOffset, offset - prevOffset - 1);
189}
190
191bool ParsedMessage::getStatusCode(int32_t *statusCode) const {
192    AString statusCodeString;
193    getRequestField(1, &statusCodeString);
194
195    char *end;
196    *statusCode = strtol(statusCodeString.c_str(), &end, 10);
197
198    if (*end != '\0' || end == statusCodeString.c_str()
199            || (*statusCode) < 100 || (*statusCode) > 999) {
200        *statusCode = 0;
201        return false;
202    }
203
204    return true;
205}
206
207AString ParsedMessage::debugString() const {
208    AString line;
209    CHECK(findString("_", &line));
210
211    line.append("\n");
212
213    for (size_t i = 0; i < mDict.size(); ++i) {
214        const AString &key = mDict.keyAt(i);
215        const AString &value = mDict.valueAt(i);
216
217        if (key == AString("_")) {
218            continue;
219        }
220
221        line.append(key);
222        line.append(": ");
223        line.append(value);
224        line.append("\n");
225    }
226
227    line.append("\n");
228    line.append(mContent);
229
230    return line;
231}
232
233// static
234bool ParsedMessage::GetAttribute(
235        const char *s, const char *key, AString *value) {
236    value->clear();
237
238    size_t keyLen = strlen(key);
239
240    for (;;) {
241        while (isspace(*s)) {
242            ++s;
243        }
244
245        const char *colonPos = strchr(s, ';');
246
247        size_t len =
248            (colonPos == NULL) ? strlen(s) : colonPos - s;
249
250        if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) {
251            value->setTo(&s[keyLen + 1], len - keyLen - 1);
252            return true;
253        }
254
255        if (colonPos == NULL) {
256            return false;
257        }
258
259        s = colonPos + 1;
260    }
261}
262
263// static
264bool ParsedMessage::GetInt32Attribute(
265        const char *s, const char *key, int32_t *value) {
266    AString stringValue;
267    if (!GetAttribute(s, key, &stringValue)) {
268        *value = 0;
269        return false;
270    }
271
272    char *end;
273    *value = strtol(stringValue.c_str(), &end, 10);
274
275    if (end == stringValue.c_str() || *end != '\0') {
276        *value = 0;
277        return false;
278    }
279
280    return true;
281}
282
283}  // namespace android
284
285