aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/linden/indra/llmessage/llmime.cpp
diff options
context:
space:
mode:
authorJacek Antonelli2008-08-15 23:44:46 -0500
committerJacek Antonelli2008-08-15 23:44:46 -0500
commit38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4 (patch)
treeadca584755d22ca041a2dbfc35d4eca01f70b32c /linden/indra/llmessage/llmime.cpp
parentREADME.txt (diff)
downloadmeta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.zip
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.gz
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.bz2
meta-impy-38d6d37f2d982fa959e9e8a4a3f7e1ccfad7b5d4.tar.xz
Second Life viewer sources 1.13.2.12
Diffstat (limited to '')
-rw-r--r--linden/indra/llmessage/llmime.cpp632
1 files changed, 632 insertions, 0 deletions
diff --git a/linden/indra/llmessage/llmime.cpp b/linden/indra/llmessage/llmime.cpp
new file mode 100644
index 0000000..2786ea8
--- /dev/null
+++ b/linden/indra/llmessage/llmime.cpp
@@ -0,0 +1,632 @@
1/**
2 * @file llmime.cpp
3 * @author Phoenix
4 * @date 2006-12-20
5 * @brief Implementation of mime tools.
6 *
7 * Copyright (c) 2006-2007, Linden Research, Inc.
8 *
9 * The source code in this file ("Source Code") is provided by Linden Lab
10 * to you under the terms of the GNU General Public License, version 2.0
11 * ("GPL"), unless you have obtained a separate licensing agreement
12 * ("Other License"), formally executed by you and Linden Lab. Terms of
13 * the GPL can be found in doc/GPL-license.txt in this distribution, or
14 * online at http://secondlife.com/developers/opensource/gplv2
15 *
16 * There are special exceptions to the terms and conditions of the GPL as
17 * it is applied to this Source Code. View the full text of the exception
18 * in the file doc/FLOSS-exception.txt in this software distribution, or
19 * online at http://secondlife.com/developers/opensource/flossexception
20 *
21 * By copying, modifying or distributing this software, you acknowledge
22 * that you have read and understood your obligations described above,
23 * and agree to abide by those obligations.
24 *
25 * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
26 * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
27 * COMPLETENESS OR PERFORMANCE.
28 */
29
30#include "linden_common.h"
31#include "llmime.h"
32
33#include <vector>
34
35#include "llmemorystream.h"
36
37/**
38 * Useful constants.
39 */
40// Headers specified in rfc-2045 will be canonicalized below.
41static const std::string CONTENT_LENGTH("Content-Length");
42static const std::string CONTENT_TYPE("Content-Type");
43static const S32 KNOWN_HEADER_COUNT = 6;
44static const std::string KNOWN_HEADER[KNOWN_HEADER_COUNT] =
45{
46 CONTENT_LENGTH,
47 CONTENT_TYPE,
48 std::string("MIME-Version"),
49 std::string("Content-Transfer-Encoding"),
50 std::string("Content-ID"),
51 std::string("Content-Description"),
52};
53
54// parser helpers
55static const std::string MULTIPART("multipart");
56static const std::string BOUNDARY("boundary");
57static const std::string END_OF_CONTENT_PARAMETER("\r\n ;\t");
58static const std::string SEPARATOR_PREFIX("--");
59//static const std::string SEPARATOR_SUFFIX("\r\n");
60
61/*
62Content-Type: multipart/mixed; boundary="segment"
63Content-Length: 24832
64
65--segment
66Content-Type: image/j2c
67Content-Length: 23715
68
69<data>
70
71--segment
72Content-Type: text/xml; charset=UTF-8
73
74<meta data>
75EOF
76
77*/
78
79/**
80 * LLMimeIndex
81 */
82
83/**
84 * @class LLMimeIndex::Impl
85 * @brief Implementation details of the mime index class.
86 * @see LLMimeIndex
87 */
88class LLMimeIndex::Impl
89{
90public:
91 Impl() : mOffset(-1), mUseCount(1)
92 {}
93 Impl(LLSD headers, S32 offset) :
94 mHeaders(headers), mOffset(offset), mUseCount(1)
95 {}
96public:
97 LLSD mHeaders;
98 S32 mOffset;
99 S32 mUseCount;
100
101 typedef std::vector<LLMimeIndex> sub_part_t;
102 sub_part_t mAttachments;
103};
104
105LLSD LLMimeIndex::headers() const
106{
107 return mImpl->mHeaders;
108}
109
110S32 LLMimeIndex::offset() const
111{
112 return mImpl->mOffset;
113}
114
115S32 LLMimeIndex::contentLength() const
116{
117 // Find the content length in the headers.
118 S32 length = -1;
119 LLSD content_length = mImpl->mHeaders[CONTENT_LENGTH];
120 if(content_length.isDefined())
121 {
122 length = content_length.asInteger();
123 }
124 return length;
125}
126
127std::string LLMimeIndex::contentType() const
128{
129 std::string type;
130 LLSD content_type = mImpl->mHeaders[CONTENT_TYPE];
131 if(content_type.isDefined())
132 {
133 type = content_type.asString();
134 }
135 return type;
136}
137
138bool LLMimeIndex::isMultipart() const
139{
140 bool multipart = false;
141 LLSD content_type = mImpl->mHeaders[CONTENT_TYPE];
142 if(content_type.isDefined())
143 {
144 std::string type = content_type.asString();
145 int comp = type.compare(0, MULTIPART.size(), MULTIPART);
146 if(0 == comp)
147 {
148 multipart = true;
149 }
150 }
151 return multipart;
152}
153
154S32 LLMimeIndex::subPartCount() const
155{
156 return mImpl->mAttachments.size();
157}
158
159LLMimeIndex LLMimeIndex::subPart(S32 index) const
160{
161 LLMimeIndex part;
162 if((index >= 0) && (index < (S32)mImpl->mAttachments.size()))
163 {
164 part = mImpl->mAttachments[index];
165 }
166 return part;
167}
168
169LLMimeIndex::LLMimeIndex() : mImpl(new LLMimeIndex::Impl)
170{
171}
172
173LLMimeIndex::LLMimeIndex(LLSD headers, S32 content_offset) :
174 mImpl(new LLMimeIndex::Impl(headers, content_offset))
175{
176}
177
178LLMimeIndex::LLMimeIndex(const LLMimeIndex& mime) :
179 mImpl(mime.mImpl)
180{
181 ++mImpl->mUseCount;
182}
183
184LLMimeIndex::~LLMimeIndex()
185{
186 if(0 == --mImpl->mUseCount)
187 {
188 delete mImpl;
189 }
190}
191
192LLMimeIndex& LLMimeIndex::operator=(const LLMimeIndex& mime)
193{
194 // Increment use count first so that we handle self assignment
195 // automatically.
196 ++mime.mImpl->mUseCount;
197 if(0 == --mImpl->mUseCount)
198 {
199 delete mImpl;
200 }
201 mImpl = mime.mImpl;
202 return *this;
203}
204
205bool LLMimeIndex::attachSubPart(LLMimeIndex sub_part)
206{
207 // *FIX: Should we check for multi-part?
208 if(mImpl->mAttachments.size() < S32_MAX)
209 {
210 mImpl->mAttachments.push_back(sub_part);
211 return true;
212 }
213 return false;
214}
215
216/**
217 * LLMimeParser
218 */
219/**
220 * @class LLMimeParser::Impl
221 * @brief Implementation details of the mime parser class.
222 * @see LLMimeParser
223 */
224class LLMimeParser::Impl
225{
226public:
227 // @brief Constructor.
228 Impl();
229
230 // @brief Reset this for a new parse.
231 void reset();
232
233 /**
234 * @brief Parse a mime entity to find the index information.
235 *
236 * This method will scan the istr until a single complete mime
237 * entity is read, an EOF, or limit bytes have been scanned. The
238 * istr will be modified by this parsing, so pass in a temporary
239 * stream or rewind/reset the stream after this call.
240 * @param istr An istream which contains a mime entity.
241 * @param limit The maximum number of bytes to scan.
242 * @param separator The multipart separator if it is known.
243 * @param is_subpart Set true if parsing a multipart sub part.
244 * @param index[out] The parsed output.
245 * @return Returns true if an index was parsed and no errors occurred.
246 */
247 bool parseIndex(
248 std::istream& istr,
249 S32 limit,
250 const std::string& separator,
251 bool is_subpart,
252 LLMimeIndex& index);
253
254protected:
255 /**
256 * @brief parse the headers.
257 *
258 * At the end of a successful parse, mScanCount will be at the
259 * start of the content.
260 * @param istr The input stream.
261 * @param limit maximum number of bytes to process
262 * @param headers[out] A map of the headers found.
263 * @return Returns true if the parse was successful.
264 */
265 bool parseHeaders(std::istream& istr, S32 limit, LLSD& headers);
266
267 /**
268 * @brief Figure out the separator string from a content type header.
269 *
270 * @param multipart_content_type The content type value from the headers.
271 * @return Returns the separator string.
272 */
273 std::string findSeparator(std::string multipart_content_type);
274
275 /**
276 * @brief Scan through istr past the separator.
277 *
278 * @param istr The input stream.
279 * @param limit Maximum number of bytes to scan.
280 * @param separator The multipart separator.
281 */
282 void scanPastSeparator(
283 std::istream& istr,
284 S32 limit,
285 const std::string& separator);
286
287 /**
288 * @brief Scan through istr past the content of the current mime part.
289 *
290 * @param istr The input stream.
291 * @param limit Maximum number of bytes to scan.
292 * @param headers The headers for this mime part.
293 * @param separator The multipart separator if known.
294 */
295 void scanPastContent(
296 std::istream& istr,
297 S32 limit,
298 LLSD headers,
299 const std::string separator);
300
301 /**
302 * @brief Eat CRLF.
303 *
304 * This method has no concept of the limit, so ensure you have at
305 * least 2 characters left to eat before hitting the limit. This
306 * method will increment mScanCount as it goes.
307 * @param istr The input stream.
308 * @return Returns true if CRLF was found and consumed off of istr.
309 */
310 bool eatCRLF(std::istream& istr);
311
312 // @brief Returns true if parsing should continue.
313 bool continueParse() const { return (!mError && mContinue); }
314
315 // @brief anonymous enumeration for parse buffer size.
316 enum
317 {
318 LINE_BUFFER_LENGTH = 1024
319 };
320
321protected:
322 S32 mScanCount;
323 bool mContinue;
324 bool mError;
325 char mBuffer[LINE_BUFFER_LENGTH];
326};
327
328LLMimeParser::Impl::Impl()
329{
330 reset();
331}
332
333void LLMimeParser::Impl::reset()
334{
335 mScanCount = 0;
336 mContinue = true;
337 mError = false;
338 mBuffer[0] = '\0';
339}
340
341bool LLMimeParser::Impl::parseIndex(
342 std::istream& istr,
343 S32 limit,
344 const std::string& separator,
345 bool is_subpart,
346 LLMimeIndex& index)
347{
348 LLSD headers;
349 bool parsed_something = false;
350 if(parseHeaders(istr, limit, headers))
351 {
352 parsed_something = true;
353 LLMimeIndex mime(headers, mScanCount);
354 index = mime;
355 if(index.isMultipart())
356 {
357 // Figure out the separator, scan past it, and recurse.
358 std::string ct = headers[CONTENT_TYPE].asString();
359 std::string sep = findSeparator(ct);
360 scanPastSeparator(istr, limit, sep);
361 while(continueParse() && parseIndex(istr, limit, sep, true, mime))
362 {
363 index.attachSubPart(mime);
364 }
365 }
366 else
367 {
368 // Scan to the end of content.
369 scanPastContent(istr, limit, headers, separator);
370 if(is_subpart)
371 {
372 scanPastSeparator(istr, limit, separator);
373 }
374 }
375 }
376 if(mError) return false;
377 return parsed_something;
378}
379
380bool LLMimeParser::Impl::parseHeaders(
381 std::istream& istr,
382 S32 limit,
383 LLSD& headers)
384{
385 while(continueParse())
386 {
387 // Get the next line.
388 // We subtract 1 from the limit so that we make sure
389 // not to read past limit when we get() the newline.
390 S32 max_get = llmin((S32)LINE_BUFFER_LENGTH, limit - mScanCount - 1);
391 istr.getline(mBuffer, max_get, '\r');
392 mScanCount += istr.gcount();
393 int c = istr.get();
394 if(EOF == c)
395 {
396 mContinue = false;
397 return false;
398 }
399 ++mScanCount;
400 if(c != '\n')
401 {
402 mError = true;
403 return false;
404 }
405 if(mScanCount >= limit)
406 {
407 mContinue = false;
408 }
409
410 // Check if that's the end of headers.
411 if('\0' == mBuffer[0])
412 {
413 break;
414 }
415
416 // Split out the name and value.
417 // *NOTE: The use of strchr() here is safe since mBuffer is
418 // guaranteed to be NULL terminated from the call to getline()
419 // above.
420 char* colon = strchr(mBuffer, ':');
421 if(!colon)
422 {
423 mError = true;
424 return false;
425 }
426
427 // Cononicalize the name part, and store the name: value in
428 // the headers structure. We do this by iterating through
429 // 'known' headers and replacing the value found with the
430 // correct one.
431 // *NOTE: Not so efficient, but iterating through a small
432 // subset should not be too much of an issue.
433 std::string name(mBuffer, colon++ - mBuffer);
434 while(isspace(*colon)) ++colon;
435 std::string value(colon);
436 for(S32 ii = 0; ii < KNOWN_HEADER_COUNT; ++ii)
437 {
438 if(0 == LLString::compareInsensitive(
439 name.c_str(),
440 KNOWN_HEADER[ii].c_str()))
441 {
442 name = KNOWN_HEADER[ii];
443 break;
444 }
445 }
446 headers[name] = value;
447 }
448 if(headers.isUndefined()) return false;
449 return true;
450}
451
452std::string LLMimeParser::Impl::findSeparator(std::string header)
453{
454 // 01234567890
455 //Content-Type: multipart/mixed; boundary="segment"
456 std::string separator;
457 std::string::size_type pos = header.find(BOUNDARY);
458 if(std::string::npos == pos) return separator;
459 pos += BOUNDARY.size() + 1;
460 std::string::size_type end;
461 if(header[pos] == '"')
462 {
463 // the boundary is quoted, find the end from pos, and take the
464 // substring.
465 end = header.find('"', ++pos);
466 if(std::string::npos == end)
467 {
468 // poorly formed boundary.
469 mError = true;
470 }
471 }
472 else
473 {
474 // otherwise, it's every character until a whitespace, end of
475 // line, or another parameter begins.
476 end = header.find_first_of(END_OF_CONTENT_PARAMETER, pos);
477 if(std::string::npos == end)
478 {
479 // it goes to the end of the string.
480 end = header.size();
481 }
482 }
483 if(!mError) separator = header.substr(pos, end - pos);
484 return separator;
485}
486
487void LLMimeParser::Impl::scanPastSeparator(
488 std::istream& istr,
489 S32 limit,
490 const std::string& sep)
491{
492 std::ostringstream ostr;
493 ostr << SEPARATOR_PREFIX << sep;
494 std::string separator = ostr.str();
495 bool found_separator = false;
496 while(!found_separator && continueParse())
497 {
498 // Subtract 1 from the limit so that we make sure not to read
499 // past limit when we get() the newline.
500 S32 max_get = llmin((S32)LINE_BUFFER_LENGTH, limit - mScanCount - 1);
501 istr.getline(mBuffer, max_get, '\r');
502 mScanCount += istr.gcount();
503 if(istr.gcount() >= LINE_BUFFER_LENGTH - 1)
504 {
505 // that's way too long to be a separator, so ignore it.
506 continue;
507 }
508 int c = istr.get();
509 if(EOF == c)
510 {
511 mContinue = false;
512 return;
513 }
514 ++mScanCount;
515 if(c != '\n')
516 {
517 mError = true;
518 return;
519 }
520 if(mScanCount >= limit)
521 {
522 mContinue = false;
523 }
524 if(0 == LLString::compareStrings(mBuffer, separator.c_str()))
525 {
526 found_separator = true;
527 }
528 }
529}
530
531void LLMimeParser::Impl::scanPastContent(
532 std::istream& istr,
533 S32 limit,
534 LLSD headers,
535 const std::string separator)
536{
537 if(headers.has(CONTENT_LENGTH))
538 {
539 S32 content_length = headers[CONTENT_LENGTH].asInteger();
540 // Subtract 2 here for the \r\n after the content.
541 S32 max_skip = llmin(content_length, limit - mScanCount - 2);
542 istr.ignore(max_skip);
543 mScanCount += max_skip;
544
545 // *NOTE: Check for hitting the limit and eof here before
546 // checking for the trailing EOF, because our mime parser has
547 // to gracefully handle incomplete mime entites.
548 if((mScanCount >= limit) || istr.eof())
549 {
550 mContinue = false;
551 }
552 else if(!eatCRLF(istr))
553 {
554 mError = true;
555 return;
556 }
557 }
558}
559
560bool LLMimeParser::Impl::eatCRLF(std::istream& istr)
561{
562 int c = istr.get();
563 ++mScanCount;
564 if(c != '\r')
565 {
566 return false;
567 }
568 c = istr.get();
569 ++mScanCount;
570 if(c != '\n')
571 {
572 return false;
573 }
574 return true;
575}
576
577
578LLMimeParser::LLMimeParser() : mImpl(* new LLMimeParser::Impl)
579{
580}
581
582LLMimeParser::~LLMimeParser()
583{
584 delete & mImpl;
585}
586
587void LLMimeParser::reset()
588{
589 mImpl.reset();
590}
591
592bool LLMimeParser::parseIndex(std::istream& istr, LLMimeIndex& index)
593{
594 std::string separator;
595 return mImpl.parseIndex(istr, S32_MAX, separator, false, index);
596}
597
598bool LLMimeParser::parseIndex(
599 const std::vector<U8>& buffer,
600 LLMimeIndex& index)
601{
602 LLMemoryStream mstr(&buffer[0], buffer.size());
603 return parseIndex(mstr, buffer.size() + 1, index);
604}
605
606bool LLMimeParser::parseIndex(
607 std::istream& istr,
608 S32 limit,
609 LLMimeIndex& index)
610{
611 std::string separator;
612 return mImpl.parseIndex(istr, limit, separator, false, index);
613}
614
615bool LLMimeParser::parseIndex(const U8* buffer, S32 length, LLMimeIndex& index)
616{
617 LLMemoryStream mstr(buffer, length);
618 return parseIndex(mstr, length + 1, index);
619}
620
621/*
622bool LLMimeParser::verify(std::istream& isr, LLMimeIndex& index) const
623{
624 return false;
625}
626
627bool LLMimeParser::verify(U8* buffer, S32 length, LLMimeIndex& index) const
628{
629 LLMemoryStream mstr(buffer, length);
630 return verify(mstr, index);
631}
632*/