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