PageRenderTime 38ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/indra/llmessage/llmime.cpp

https://bitbucket.org/lindenlab/viewer-beta/
C++ | 629 lines | 406 code | 54 blank | 169 comment | 57 complexity | 0d8929309d60c5c7d902f694624027bf MD5 | raw file
Possible License(s): LGPL-2.1
  1. /**
  2. * @file llmime.cpp
  3. * @author Phoenix
  4. * @date 2006-12-20
  5. * @brief Implementation of mime tools.
  6. *
  7. * $LicenseInfo:firstyear=2006&license=viewerlgpl$
  8. * Second Life Viewer Source Code
  9. * Copyright (C) 2010, Linden Research, Inc.
  10. *
  11. * This library is free software; you can redistribute it and/or
  12. * modify it under the terms of the GNU Lesser General Public
  13. * License as published by the Free Software Foundation;
  14. * version 2.1 of the License only.
  15. *
  16. * This library is distributed in the hope that it will be useful,
  17. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  19. * Lesser General Public License for more details.
  20. *
  21. * You should have received a copy of the GNU Lesser General Public
  22. * License along with this library; if not, write to the Free Software
  23. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  24. *
  25. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  26. * $/LicenseInfo$
  27. */
  28. #include "linden_common.h"
  29. #include "llmime.h"
  30. #include <vector>
  31. #include "llmemorystream.h"
  32. /**
  33. * Useful constants.
  34. */
  35. // Headers specified in rfc-2045 will be canonicalized below.
  36. static const std::string CONTENT_LENGTH("Content-Length");
  37. static const std::string CONTENT_TYPE("Content-Type");
  38. static const S32 KNOWN_HEADER_COUNT = 6;
  39. static const std::string KNOWN_HEADER[KNOWN_HEADER_COUNT] =
  40. {
  41. CONTENT_LENGTH,
  42. CONTENT_TYPE,
  43. std::string("MIME-Version"),
  44. std::string("Content-Transfer-Encoding"),
  45. std::string("Content-ID"),
  46. std::string("Content-Description"),
  47. };
  48. // parser helpers
  49. static const std::string MULTIPART("multipart");
  50. static const std::string BOUNDARY("boundary");
  51. static const std::string END_OF_CONTENT_PARAMETER("\r\n ;\t");
  52. static const std::string SEPARATOR_PREFIX("--");
  53. //static const std::string SEPARATOR_SUFFIX("\r\n");
  54. /*
  55. Content-Type: multipart/mixed; boundary="segment"
  56. Content-Length: 24832
  57. --segment
  58. Content-Type: image/j2c
  59. Content-Length: 23715
  60. <data>
  61. --segment
  62. Content-Type: text/xml; charset=UTF-8
  63. <meta data>
  64. EOF
  65. */
  66. /**
  67. * LLMimeIndex
  68. */
  69. /**
  70. * @class LLMimeIndex::Impl
  71. * @brief Implementation details of the mime index class.
  72. * @see LLMimeIndex
  73. */
  74. class LLMimeIndex::Impl
  75. {
  76. public:
  77. Impl() : mOffset(-1), mUseCount(1)
  78. {}
  79. Impl(LLSD headers, S32 offset) :
  80. mHeaders(headers), mOffset(offset), mUseCount(1)
  81. {}
  82. public:
  83. LLSD mHeaders;
  84. S32 mOffset;
  85. S32 mUseCount;
  86. typedef std::vector<LLMimeIndex> sub_part_t;
  87. sub_part_t mAttachments;
  88. };
  89. LLSD LLMimeIndex::headers() const
  90. {
  91. return mImpl->mHeaders;
  92. }
  93. S32 LLMimeIndex::offset() const
  94. {
  95. return mImpl->mOffset;
  96. }
  97. S32 LLMimeIndex::contentLength() const
  98. {
  99. // Find the content length in the headers.
  100. S32 length = -1;
  101. LLSD content_length = mImpl->mHeaders[CONTENT_LENGTH];
  102. if(content_length.isDefined())
  103. {
  104. length = content_length.asInteger();
  105. }
  106. return length;
  107. }
  108. std::string LLMimeIndex::contentType() const
  109. {
  110. std::string type;
  111. LLSD content_type = mImpl->mHeaders[CONTENT_TYPE];
  112. if(content_type.isDefined())
  113. {
  114. type = content_type.asString();
  115. }
  116. return type;
  117. }
  118. bool LLMimeIndex::isMultipart() const
  119. {
  120. bool multipart = false;
  121. LLSD content_type = mImpl->mHeaders[CONTENT_TYPE];
  122. if(content_type.isDefined())
  123. {
  124. std::string type = content_type.asString();
  125. int comp = type.compare(0, MULTIPART.size(), MULTIPART);
  126. if(0 == comp)
  127. {
  128. multipart = true;
  129. }
  130. }
  131. return multipart;
  132. }
  133. S32 LLMimeIndex::subPartCount() const
  134. {
  135. return mImpl->mAttachments.size();
  136. }
  137. LLMimeIndex LLMimeIndex::subPart(S32 index) const
  138. {
  139. LLMimeIndex part;
  140. if((index >= 0) && (index < (S32)mImpl->mAttachments.size()))
  141. {
  142. part = mImpl->mAttachments[index];
  143. }
  144. return part;
  145. }
  146. LLMimeIndex::LLMimeIndex() : mImpl(new LLMimeIndex::Impl)
  147. {
  148. }
  149. LLMimeIndex::LLMimeIndex(LLSD headers, S32 content_offset) :
  150. mImpl(new LLMimeIndex::Impl(headers, content_offset))
  151. {
  152. }
  153. LLMimeIndex::LLMimeIndex(const LLMimeIndex& mime) :
  154. mImpl(mime.mImpl)
  155. {
  156. ++mImpl->mUseCount;
  157. }
  158. LLMimeIndex::~LLMimeIndex()
  159. {
  160. if(0 == --mImpl->mUseCount)
  161. {
  162. delete mImpl;
  163. }
  164. }
  165. LLMimeIndex& LLMimeIndex::operator=(const LLMimeIndex& mime)
  166. {
  167. // Increment use count first so that we handle self assignment
  168. // automatically.
  169. ++mime.mImpl->mUseCount;
  170. if(0 == --mImpl->mUseCount)
  171. {
  172. delete mImpl;
  173. }
  174. mImpl = mime.mImpl;
  175. return *this;
  176. }
  177. bool LLMimeIndex::attachSubPart(LLMimeIndex sub_part)
  178. {
  179. // *FIX: Should we check for multi-part?
  180. if(mImpl->mAttachments.size() < S32_MAX)
  181. {
  182. mImpl->mAttachments.push_back(sub_part);
  183. return true;
  184. }
  185. return false;
  186. }
  187. /**
  188. * LLMimeParser
  189. */
  190. /**
  191. * @class LLMimeParser::Impl
  192. * @brief Implementation details of the mime parser class.
  193. * @see LLMimeParser
  194. */
  195. class LLMimeParser::Impl
  196. {
  197. public:
  198. // @brief Constructor.
  199. Impl();
  200. // @brief Reset this for a new parse.
  201. void reset();
  202. /**
  203. * @brief Parse a mime entity to find the index information.
  204. *
  205. * This method will scan the istr until a single complete mime
  206. * entity is read, an EOF, or limit bytes have been scanned. The
  207. * istr will be modified by this parsing, so pass in a temporary
  208. * stream or rewind/reset the stream after this call.
  209. * @param istr An istream which contains a mime entity.
  210. * @param limit The maximum number of bytes to scan.
  211. * @param separator The multipart separator if it is known.
  212. * @param is_subpart Set true if parsing a multipart sub part.
  213. * @param index[out] The parsed output.
  214. * @return Returns true if an index was parsed and no errors occurred.
  215. */
  216. bool parseIndex(
  217. std::istream& istr,
  218. S32 limit,
  219. const std::string& separator,
  220. bool is_subpart,
  221. LLMimeIndex& index);
  222. protected:
  223. /**
  224. * @brief parse the headers.
  225. *
  226. * At the end of a successful parse, mScanCount will be at the
  227. * start of the content.
  228. * @param istr The input stream.
  229. * @param limit maximum number of bytes to process
  230. * @param headers[out] A map of the headers found.
  231. * @return Returns true if the parse was successful.
  232. */
  233. bool parseHeaders(std::istream& istr, S32 limit, LLSD& headers);
  234. /**
  235. * @brief Figure out the separator string from a content type header.
  236. *
  237. * @param multipart_content_type The content type value from the headers.
  238. * @return Returns the separator string.
  239. */
  240. std::string findSeparator(std::string multipart_content_type);
  241. /**
  242. * @brief Scan through istr past the separator.
  243. *
  244. * @param istr The input stream.
  245. * @param limit Maximum number of bytes to scan.
  246. * @param separator The multipart separator.
  247. */
  248. void scanPastSeparator(
  249. std::istream& istr,
  250. S32 limit,
  251. const std::string& separator);
  252. /**
  253. * @brief Scan through istr past the content of the current mime part.
  254. *
  255. * @param istr The input stream.
  256. * @param limit Maximum number of bytes to scan.
  257. * @param headers The headers for this mime part.
  258. * @param separator The multipart separator if known.
  259. */
  260. void scanPastContent(
  261. std::istream& istr,
  262. S32 limit,
  263. LLSD headers,
  264. const std::string separator);
  265. /**
  266. * @brief Eat CRLF.
  267. *
  268. * This method has no concept of the limit, so ensure you have at
  269. * least 2 characters left to eat before hitting the limit. This
  270. * method will increment mScanCount as it goes.
  271. * @param istr The input stream.
  272. * @return Returns true if CRLF was found and consumed off of istr.
  273. */
  274. bool eatCRLF(std::istream& istr);
  275. // @brief Returns true if parsing should continue.
  276. bool continueParse() const { return (!mError && mContinue); }
  277. // @brief anonymous enumeration for parse buffer size.
  278. enum
  279. {
  280. LINE_BUFFER_LENGTH = 1024
  281. };
  282. protected:
  283. S32 mScanCount;
  284. bool mContinue;
  285. bool mError;
  286. char mBuffer[LINE_BUFFER_LENGTH];
  287. };
  288. LLMimeParser::Impl::Impl()
  289. {
  290. reset();
  291. }
  292. void LLMimeParser::Impl::reset()
  293. {
  294. mScanCount = 0;
  295. mContinue = true;
  296. mError = false;
  297. mBuffer[0] = '\0';
  298. }
  299. bool LLMimeParser::Impl::parseIndex(
  300. std::istream& istr,
  301. S32 limit,
  302. const std::string& separator,
  303. bool is_subpart,
  304. LLMimeIndex& index)
  305. {
  306. LLSD headers;
  307. bool parsed_something = false;
  308. if(parseHeaders(istr, limit, headers))
  309. {
  310. parsed_something = true;
  311. LLMimeIndex mime(headers, mScanCount);
  312. index = mime;
  313. if(index.isMultipart())
  314. {
  315. // Figure out the separator, scan past it, and recurse.
  316. std::string ct = headers[CONTENT_TYPE].asString();
  317. std::string sep = findSeparator(ct);
  318. scanPastSeparator(istr, limit, sep);
  319. while(continueParse() && parseIndex(istr, limit, sep, true, mime))
  320. {
  321. index.attachSubPart(mime);
  322. }
  323. }
  324. else
  325. {
  326. // Scan to the end of content.
  327. scanPastContent(istr, limit, headers, separator);
  328. if(is_subpart)
  329. {
  330. scanPastSeparator(istr, limit, separator);
  331. }
  332. }
  333. }
  334. if(mError) return false;
  335. return parsed_something;
  336. }
  337. bool LLMimeParser::Impl::parseHeaders(
  338. std::istream& istr,
  339. S32 limit,
  340. LLSD& headers)
  341. {
  342. while(continueParse())
  343. {
  344. // Get the next line.
  345. // We subtract 1 from the limit so that we make sure
  346. // not to read past limit when we get() the newline.
  347. S32 max_get = llmin((S32)LINE_BUFFER_LENGTH, limit - mScanCount - 1);
  348. istr.getline(mBuffer, max_get, '\r');
  349. mScanCount += istr.gcount();
  350. int c = istr.get();
  351. if(EOF == c)
  352. {
  353. mContinue = false;
  354. return false;
  355. }
  356. ++mScanCount;
  357. if(c != '\n')
  358. {
  359. mError = true;
  360. return false;
  361. }
  362. if(mScanCount >= limit)
  363. {
  364. mContinue = false;
  365. }
  366. // Check if that's the end of headers.
  367. if('\0' == mBuffer[0])
  368. {
  369. break;
  370. }
  371. // Split out the name and value.
  372. // *NOTE: The use of strchr() here is safe since mBuffer is
  373. // guaranteed to be NULL terminated from the call to getline()
  374. // above.
  375. char* colon = strchr(mBuffer, ':');
  376. if(!colon)
  377. {
  378. mError = true;
  379. return false;
  380. }
  381. // Cononicalize the name part, and store the name: value in
  382. // the headers structure. We do this by iterating through
  383. // 'known' headers and replacing the value found with the
  384. // correct one.
  385. // *NOTE: Not so efficient, but iterating through a small
  386. // subset should not be too much of an issue.
  387. std::string name(mBuffer, colon++ - mBuffer);
  388. while(isspace(*colon)) ++colon;
  389. std::string value(colon);
  390. for(S32 ii = 0; ii < KNOWN_HEADER_COUNT; ++ii)
  391. {
  392. if(0 == LLStringUtil::compareInsensitive(name, KNOWN_HEADER[ii]))
  393. {
  394. name = KNOWN_HEADER[ii];
  395. break;
  396. }
  397. }
  398. headers[name] = value;
  399. }
  400. if(headers.isUndefined()) return false;
  401. return true;
  402. }
  403. std::string LLMimeParser::Impl::findSeparator(std::string header)
  404. {
  405. // 01234567890
  406. //Content-Type: multipart/mixed; boundary="segment"
  407. std::string separator;
  408. std::string::size_type pos = header.find(BOUNDARY);
  409. if(std::string::npos == pos) return separator;
  410. pos += BOUNDARY.size() + 1;
  411. std::string::size_type end;
  412. if(header[pos] == '"')
  413. {
  414. // the boundary is quoted, find the end from pos, and take the
  415. // substring.
  416. end = header.find('"', ++pos);
  417. if(std::string::npos == end)
  418. {
  419. // poorly formed boundary.
  420. mError = true;
  421. }
  422. }
  423. else
  424. {
  425. // otherwise, it's every character until a whitespace, end of
  426. // line, or another parameter begins.
  427. end = header.find_first_of(END_OF_CONTENT_PARAMETER, pos);
  428. if(std::string::npos == end)
  429. {
  430. // it goes to the end of the string.
  431. end = header.size();
  432. }
  433. }
  434. if(!mError) separator = header.substr(pos, end - pos);
  435. return separator;
  436. }
  437. void LLMimeParser::Impl::scanPastSeparator(
  438. std::istream& istr,
  439. S32 limit,
  440. const std::string& sep)
  441. {
  442. std::ostringstream ostr;
  443. ostr << SEPARATOR_PREFIX << sep;
  444. std::string separator = ostr.str();
  445. bool found_separator = false;
  446. while(!found_separator && continueParse())
  447. {
  448. // Subtract 1 from the limit so that we make sure not to read
  449. // past limit when we get() the newline.
  450. S32 max_get = llmin((S32)LINE_BUFFER_LENGTH, limit - mScanCount - 1);
  451. istr.getline(mBuffer, max_get, '\r');
  452. mScanCount += istr.gcount();
  453. if(istr.gcount() >= LINE_BUFFER_LENGTH - 1)
  454. {
  455. // that's way too long to be a separator, so ignore it.
  456. continue;
  457. }
  458. int c = istr.get();
  459. if(EOF == c)
  460. {
  461. mContinue = false;
  462. return;
  463. }
  464. ++mScanCount;
  465. if(c != '\n')
  466. {
  467. mError = true;
  468. return;
  469. }
  470. if(mScanCount >= limit)
  471. {
  472. mContinue = false;
  473. }
  474. if(0 == LLStringUtil::compareStrings(std::string(mBuffer), separator))
  475. {
  476. found_separator = true;
  477. }
  478. }
  479. }
  480. void LLMimeParser::Impl::scanPastContent(
  481. std::istream& istr,
  482. S32 limit,
  483. LLSD headers,
  484. const std::string separator)
  485. {
  486. if(headers.has(CONTENT_LENGTH))
  487. {
  488. S32 content_length = headers[CONTENT_LENGTH].asInteger();
  489. // Subtract 2 here for the \r\n after the content.
  490. S32 max_skip = llmin(content_length, limit - mScanCount - 2);
  491. istr.ignore(max_skip);
  492. mScanCount += max_skip;
  493. // *NOTE: Check for hitting the limit and eof here before
  494. // checking for the trailing EOF, because our mime parser has
  495. // to gracefully handle incomplete mime entites.
  496. if((mScanCount >= limit) || istr.eof())
  497. {
  498. mContinue = false;
  499. }
  500. else if(!eatCRLF(istr))
  501. {
  502. mError = true;
  503. return;
  504. }
  505. }
  506. }
  507. bool LLMimeParser::Impl::eatCRLF(std::istream& istr)
  508. {
  509. int c = istr.get();
  510. ++mScanCount;
  511. if(c != '\r')
  512. {
  513. return false;
  514. }
  515. c = istr.get();
  516. ++mScanCount;
  517. if(c != '\n')
  518. {
  519. return false;
  520. }
  521. return true;
  522. }
  523. LLMimeParser::LLMimeParser() : mImpl(* new LLMimeParser::Impl)
  524. {
  525. }
  526. LLMimeParser::~LLMimeParser()
  527. {
  528. delete & mImpl;
  529. }
  530. void LLMimeParser::reset()
  531. {
  532. mImpl.reset();
  533. }
  534. bool LLMimeParser::parseIndex(std::istream& istr, LLMimeIndex& index)
  535. {
  536. std::string separator;
  537. return mImpl.parseIndex(istr, S32_MAX, separator, false, index);
  538. }
  539. bool LLMimeParser::parseIndex(
  540. const std::vector<U8>& buffer,
  541. LLMimeIndex& index)
  542. {
  543. LLMemoryStream mstr(&buffer[0], buffer.size());
  544. return parseIndex(mstr, buffer.size() + 1, index);
  545. }
  546. bool LLMimeParser::parseIndex(
  547. std::istream& istr,
  548. S32 limit,
  549. LLMimeIndex& index)
  550. {
  551. std::string separator;
  552. return mImpl.parseIndex(istr, limit, separator, false, index);
  553. }
  554. bool LLMimeParser::parseIndex(const U8* buffer, S32 length, LLMimeIndex& index)
  555. {
  556. LLMemoryStream mstr(buffer, length);
  557. return parseIndex(mstr, length + 1, index);
  558. }
  559. /*
  560. bool LLMimeParser::verify(std::istream& isr, LLMimeIndex& index) const
  561. {
  562. return false;
  563. }
  564. bool LLMimeParser::verify(U8* buffer, S32 length, LLMimeIndex& index) const
  565. {
  566. LLMemoryStream mstr(buffer, length);
  567. return verify(mstr, index);
  568. }
  569. */