diff --git a/ChangeLog b/ChangeLog index 8fdcdb05..1b5b2cf7 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,12 @@ VERSION 0.9.2svn ================ +2012-04-16 Vincent Richard + + * MIME Parser can now operate directly on an input stream (eg. file). + This allows very big messages to be parsed without loading the whole + message data into memory. + 2010-11-16 Vincent Richard * Started version 0.9.2. diff --git a/SConstruct b/SConstruct index ea5c4eb1..2690172e 100644 --- a/SConstruct +++ b/SConstruct @@ -153,11 +153,14 @@ libvmime_sources = [ 'utility/inputStreamSocketAdapter.cpp', 'utility/inputStreamSocketAdapter.hpp', 'utility/inputStreamStringAdapter.cpp', 'utility/inputStreamStringAdapter.hpp', 'utility/inputStreamStringProxyAdapter.cpp', 'utility/inputStreamStringProxyAdapter.hpp', + 'utility/seekableInputStream.hpp', + 'utility/seekableInputStreamRegionAdapter.cpp', 'utility/seekableInputStreamRegionAdapter.hpp', 'utility/outputStream.cpp', 'utility/outputStream.hpp', 'utility/outputStreamAdapter.cpp', 'utility/outputStreamAdapter.hpp', 'utility/outputStreamByteArrayAdapter.cpp', 'utility/outputStreamByteArrayAdapter.hpp', 'utility/outputStreamSocketAdapter.cpp', 'utility/outputStreamSocketAdapter.hpp', 'utility/outputStreamStringAdapter.cpp', 'utility/outputStreamStringAdapter.hpp', + 'utility/parserInputStreamAdapter.cpp', 'utility/parserInputStreamAdapter.hpp', 'utility/stringProxy.cpp', 'utility/stringProxy.hpp', 'utility/stringUtils.cpp', 'utility/stringUtils.hpp', 'utility/url.cpp', 'utility/url.hpp', diff --git a/src/addressList.cpp b/src/addressList.cpp index 31a2a3d8..f06460d3 100644 --- a/src/addressList.cpp +++ b/src/addressList.cpp @@ -50,7 +50,7 @@ addressList::~addressList() } -void addressList::parse(const string& buffer, const string::size_type position, +void addressList::parseImpl(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition) { removeAllAddresses(); @@ -72,7 +72,7 @@ void addressList::parse(const string& buffer, const string::size_type position, } -void addressList::generate(utility::outputStream& os, const string::size_type maxLineLength, +void addressList::generateImpl(utility::outputStream& os, const string::size_type maxLineLength, const string::size_type curLinePos, string::size_type* newLinePos) const { string::size_type pos = curLinePos; @@ -248,9 +248,9 @@ const std::vector > addressList::getAddressList() } -const std::vector > addressList::getChildComponents() const +const std::vector > addressList::getChildComponents() { - std::vector > list; + std::vector > list; copy_vector(m_list, list); diff --git a/src/body.cpp b/src/body.cpp index 9d7d57f9..732fa8b5 100644 --- a/src/body.cpp +++ b/src/body.cpp @@ -31,10 +31,13 @@ #include "vmime/utility/random.hpp" +#include "vmime/utility/seekableInputStreamRegionAdapter.hpp" + #include "vmime/parserHelpers.hpp" #include "vmime/emptyContentHandler.hpp" #include "vmime/stringContentHandler.hpp" +#include "vmime/streamContentHandler.hpp" namespace vmime @@ -52,11 +55,28 @@ body::~body() } -void body::parse(const string& buffer, const string::size_type position, - const string::size_type end, string::size_type* newPosition) +void body::parseImpl + (ref parser, + const utility::stream::size_type position, + const utility::stream::size_type end, + utility::stream::size_type* newPosition) { removeAllParts(); + m_prologText.clear(); + m_epilogText.clear(); + + if (end == position) + { + + setParsedBounds(position, end); + + if (newPosition) + *newPosition = end; + + return; + } + // Check whether the body is a MIME-multipart bool isMultipart = false; string boundary; @@ -80,37 +100,61 @@ void body::parse(const string& buffer, const string::size_type position, { // No "boundary" parameter specified: we can try to // guess it by scanning the body contents... - string::size_type pos = buffer.find("\n--", position); + utility::stream::size_type pos = position; - if ((pos != string::npos) && (pos < end)) + parser->seek(pos); + + if (pos + 2 < end && parser->matchBytes("--", 2)) { - pos += 3; + pos += 2; + } + else + { + pos = parser->findNext("\n--", position); - const string::size_type start = pos; + if ((pos != utility::stream::npos) && (pos + 3 < end)) + pos += 3; // skip \n-- + } - char_t c = buffer[pos]; - string::size_type length = 0; + if ((pos != utility::stream::npos) && (pos < end)) + { + parser->seek(pos); + // Read some bytes after boundary separator + utility::stream::value_type buffer[256]; + const utility::stream::size_type bufferLen = + parser->read(buffer, std::min(end - pos, sizeof(buffer) / sizeof(buffer[0]))); + + buffer[sizeof(buffer) / sizeof(buffer[0]) - 1] = '\0'; + + // Extract boundary from buffer (stop at first CR or LF). // We have to stop after a reasonnably long boundary length (100) // not to take the whole body contents for a boundary... - while (pos < end && length < 100 && !(c == '\r' || c == '\n')) + string::value_type boundaryBytes[100]; + string::size_type boundaryLen = 0; + + for (string::value_type c = buffer[0] ; + boundaryLen < bufferLen && boundaryLen < 100 && !(c == '\r' || c == '\n') ; + c = buffer[++boundaryLen]) { - ++length; - c = buffer[pos++]; + boundaryBytes[boundaryLen] = buffer[boundaryLen]; } - if (pos < end && length < 100) + if (boundaryLen >= 1 && boundaryLen < 100) { // RFC #1521, Page 31: // "...the boundary parameter, which consists of 1 to 70 // characters from a set of characters known to be very // robust through email gateways, and NOT ending with // white space..." - while (pos != start && parserHelpers::isSpace(buffer[pos - 1])) - --pos; + while (boundaryLen != 0 && + parserHelpers::isSpace(boundaryBytes[boundaryLen - 1])) + { + boundaryLen--; + } - boundary = string(buffer.begin() + start, - buffer.begin() + pos); + if (boundaryLen >= 1) + boundary = string(boundaryBytes, boundaryBytes + boundaryLen); } } } @@ -126,51 +170,79 @@ void body::parse(const string& buffer, const string::size_type position, { const string boundarySep("--" + boundary); - string::size_type partStart = position; - string::size_type pos = position; + utility::stream::size_type partStart = position; + utility::stream::size_type pos = position; bool lastPart = false; - while (pos != string::npos && pos < end) + while (pos != utility::stream::npos && pos < end) { - pos = buffer.find(boundarySep, pos); + pos = parser->findNext(boundarySep, pos); - if (pos == string::npos || - ((pos == 0 || buffer[pos - 1] == '\n') && - (buffer[pos + boundarySep.length()] == '\r' || - buffer[pos + boundarySep.length()] == '\n' || - buffer[pos + boundarySep.length()] == '-' - ) - ) - ) + if (pos == utility::stream::npos) + break; // not found + + if (pos != 0) { - break; + parser->seek(pos - 1); + + if (parser->peekByte() != '\n') + { + // Boundary is not at a beginning of a line + pos++; + continue; + } + + parser->skip(1 + boundarySep.length()); + } + else + { + parser->seek(pos + boundarySep.length()); } - // boundary not a beginning of line, or just a prefix of another, continue the search. + const utility::stream::value_type next = parser->peekByte(); + + if (next == '\r' || next == '\n' || next == '-') + break; + + // Boundary is a prefix of another, continue the search pos++; } - if (pos != string::npos && pos < end) + if (pos != utility::stream::npos && pos < end) { vmime::text text; - text.parse(buffer, position, pos); + text.parse(parser, position, pos); m_prologText = text.getWholeBuffer(); } - for (int index = 0 ; !lastPart && (pos != string::npos) && (pos < end) ; ++index) + for (int index = 0 ; !lastPart && (pos != utility::stream::npos) && (pos < end) ; ++index) { - string::size_type partEnd = pos; + utility::stream::size_type partEnd = pos; // Get rid of the [CR]LF just before the boundary string - if (pos >= (position + 1) && buffer[pos - 1] == '\n') --partEnd; - if (pos >= (position + 2) && buffer[pos - 2] == '\r') --partEnd; + if (pos >= (position + 1)) + { + parser->seek(pos - 1); + + if (parser->peekByte() == '\n') + --partEnd; + } + + if (pos >= (position + 2)) + { + parser->seek(pos - 2); + + if (parser->peekByte() == '\r') + --partEnd; + } // Check whether it is the last part (boundary terminated by "--") pos += boundarySep.length(); + parser->seek(pos); - if (pos + 1 < end && buffer[pos] == '-' && buffer[pos + 1] == '-') + if (pos + 1 < end && parser->matchBytes("--", 2)) { lastPart = true; pos += 2; @@ -180,15 +252,15 @@ void body::parse(const string& buffer, const string::size_type position, // "...(If a boundary appears to end with white space, the // white space must be presumed to have been added by a // gateway, and must be deleted.)..." - while (pos < end && (buffer[pos] == ' ' || buffer[pos] == '\t')) - ++pos; + parser->seek(pos); + pos += parser->skipIf(parserHelpers::isSpaceOrTab, end); // End of boundary line - if (pos + 1 < end && buffer[pos] == '\r' && buffer[pos + 1] =='\n') + if (pos + 1 < end && parser->matchBytes("\r\n", 2)) { pos += 2; } - else if (pos < end && buffer[pos] == '\n') + else if (pos < end && parser->peekByte() == '\n') { ++pos; } @@ -202,7 +274,7 @@ void body::parse(const string& buffer, const string::size_type position, if (partEnd < partStart) std::swap(partStart, partEnd); - part->parse(buffer, partStart, partEnd, NULL); + part->parse(parser, partStart, partEnd, NULL); part->m_parent = m_part; m_parts.push_back(part); @@ -210,23 +282,37 @@ void body::parse(const string& buffer, const string::size_type position, partStart = pos; - while (pos != string::npos && pos < end) + while (pos != utility::stream::npos && pos < end) { - pos = buffer.find(boundarySep, pos); + pos = parser->findNext(boundarySep, pos); - if (pos == string::npos || - ((pos == 0 || buffer[pos - 1] == '\n') && - (buffer[pos + boundarySep.length()] == '\r' || - buffer[pos + boundarySep.length()] == '\n' || - buffer[pos + boundarySep.length()] == '-' - ) - ) - ) + if (pos == utility::stream::npos) + break; // not found + + if (pos != 0) { - break; + parser->seek(pos - 1); + + if (parser->peekByte() != '\n') + { + // Boundary is not at a beginning of a line + pos++; + continue; + } + + parser->skip(1 + boundarySep.length()); + } + else + { + parser->seek(pos + boundarySep.length()); } - // boundary not a beginning of line, or just a prefix of another, continue the search. + const utility::stream::value_type next = parser->peekByte(); + + if (next == '\r' || next == '\n' || next == '-') + break; + + // Boundary is a prefix of another, continue the search pos++; } } @@ -234,13 +320,13 @@ void body::parse(const string& buffer, const string::size_type position, m_contents = vmime::create (); // Last part was not found: recover from missing boundary - if (!lastPart && pos == string::npos) + if (!lastPart && pos == utility::stream::npos) { ref part = vmime::create (); try { - part->parse(buffer, partStart, end); + part->parse(parser, partStart, end); } catch (std::exception&) { @@ -255,7 +341,7 @@ void body::parse(const string& buffer, const string::size_type position, else if (partStart < end) { vmime::text text; - text.parse(buffer, partStart, end); + text.parse(parser, partStart, end); m_epilogText = text.getWholeBuffer(); } @@ -282,7 +368,13 @@ void body::parse(const string& buffer, const string::size_type position, } // Extract the (encoded) contents - m_contents = vmime::create (buffer, position, end, enc); + const utility::stream::size_type length = end - position; + + ref contentStream = + vmime::create + (parser->getUnderlyingStream(), position, length); + + m_contents = vmime::create (contentStream, length, enc); } setParsedBounds(position, end); @@ -292,7 +384,7 @@ void body::parse(const string& buffer, const string::size_type position, } -void body::generate(utility::outputStream& os, const string::size_type maxLineLength, +void body::generateImpl(utility::outputStream& os, const string::size_type maxLineLength, const string::size_type /* curLinePos */, string::size_type* newLinePos) const { // MIME-Multipart @@ -862,9 +954,9 @@ const std::vector > body::getPartList() } -const std::vector > body::getChildComponents() const +const std::vector > body::getChildComponents() { - std::vector > list; + std::vector > list; copy_vector(m_parts, list); diff --git a/src/bodyPart.cpp b/src/bodyPart.cpp index 7d60461e..522cbb23 100644 --- a/src/bodyPart.cpp +++ b/src/bodyPart.cpp @@ -46,15 +46,18 @@ bodyPart::bodyPart(weak_ref parentPart) } -void bodyPart::parse(const string& buffer, const string::size_type position, - const string::size_type end, string::size_type* newPosition) +void bodyPart::parseImpl + (ref parser, + const utility::stream::size_type position, + const utility::stream::size_type end, + utility::stream::size_type* newPosition) { // Parse the headers string::size_type pos = position; - m_header->parse(buffer, pos, end, &pos); + m_header->parse(parser, pos, end, &pos); // Parse the body contents - m_body->parse(buffer, pos, end, NULL); + m_body->parse(parser, pos, end, NULL); setParsedBounds(position, end); @@ -63,7 +66,7 @@ void bodyPart::parse(const string& buffer, const string::size_type position, } -void bodyPart::generate(utility::outputStream& os, const string::size_type maxLineLength, +void bodyPart::generateImpl(utility::outputStream& os, const string::size_type maxLineLength, const string::size_type /* curLinePos */, string::size_type* newLinePos) const { m_header->generate(os, maxLineLength); @@ -142,9 +145,9 @@ ref bodyPart::getParentPart() const } -const std::vector > bodyPart::getChildComponents() const +const std::vector > bodyPart::getChildComponents() { - std::vector > list; + std::vector > list; list.push_back(m_header); list.push_back(m_body); diff --git a/src/charset.cpp b/src/charset.cpp index 0fda4506..705664f4 100644 --- a/src/charset.cpp +++ b/src/charset.cpp @@ -57,7 +57,7 @@ charset::charset(const char* name) } -void charset::parse(const string& buffer, const string::size_type position, +void charset::parseImpl(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition) { m_name = utility::stringUtils::trim @@ -74,7 +74,7 @@ void charset::parse(const string& buffer, const string::size_type position, } -void charset::generate(utility::outputStream& os, const string::size_type /* maxLineLength */, +void charset::generateImpl(utility::outputStream& os, const string::size_type /* maxLineLength */, const string::size_type curLinePos, string::size_type* newLinePos) const { os << m_name; @@ -142,9 +142,9 @@ void charset::copyFrom(const component& other) } -const std::vector > charset::getChildComponents() const +const std::vector > charset::getChildComponents() { - return std::vector >(); + return std::vector >(); } diff --git a/src/component.cpp b/src/component.cpp index 139cf664..e93aacf3 100644 --- a/src/component.cpp +++ b/src/component.cpp @@ -23,6 +23,9 @@ #include "vmime/component.hpp" #include "vmime/base.hpp" + +#include "vmime/utility/streamUtils.hpp" +#include "vmime/utility/inputStreamStringAdapter.hpp" #include "vmime/utility/outputStreamAdapter.hpp" #include @@ -43,9 +46,102 @@ component::~component() } +void component::parse + (ref inputStream, const utility::stream::size_type length) +{ + parse(inputStream, 0, length, NULL); +} + + +void component::parse + (ref inputStream, const utility::stream::size_type position, + const utility::stream::size_type end, utility::stream::size_type* newPosition) +{ + m_parsedOffset = m_parsedLength = 0; + + ref seekableStream = + inputStream.dynamicCast (); + + if (seekableStream == NULL || end == 0) + { + // Read the whole stream into a buffer + std::ostringstream oss; + utility::outputStreamAdapter ossAdapter(oss); + + utility::bufferedStreamCopyRange(*inputStream, ossAdapter, position, end - position); + + const string buffer = oss.str(); + parseImpl(buffer, 0, buffer.length(), NULL); + } + else + { + ref parser = + vmime::create (seekableStream); + + parseImpl(parser, position, end, newPosition); + } +} + + void component::parse(const string& buffer) { - parse(buffer, 0, buffer.length(), NULL); + m_parsedOffset = m_parsedLength = 0; + + parseImpl(buffer, 0, buffer.length(), NULL); +} + + +void component::parse + (const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + m_parsedOffset = m_parsedLength = 0; + + parseImpl(buffer, position, end, newPosition); +} + + +void component::offsetParsedBounds(const utility::stream::size_type offset) +{ + // Offset parsed bounds of this component + if (m_parsedLength != 0) + m_parsedOffset += offset; + + // Offset parsed bounds of our children + std::vector > children = getChildComponents(); + + for (unsigned int i = 0, n = children.size() ; i < n ; ++i) + children[i]->offsetParsedBounds(offset); +} + + +void component::parseImpl + (ref parser, const utility::stream::size_type position, + const utility::stream::size_type end, utility::stream::size_type* newPosition) +{ + const std::string buffer = parser->extract(position, end); + parseImpl(buffer, 0, buffer.length(), newPosition); + + // Recursivey offset parsed bounds on children + if (position != 0) + offsetParsedBounds(position); + + if (newPosition != NULL) + *newPosition += position; +} + + +void component::parseImpl + (const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + ref stream = + vmime::create (buffer); + + ref parser = + vmime::create (stream); + + parseImpl(parser, position, end, newPosition); } @@ -61,6 +157,26 @@ const string component::generate(const string::size_type maxLineLength, } +void component::generate + (utility::outputStream& os, + const string::size_type maxLineLength, + const string::size_type curLinePos, + string::size_type* newLinePos) const +{ + generateImpl(os, maxLineLength, curLinePos, newLinePos); +} + + +void component::generate + (ref os, + const string::size_type maxLineLength, + const string::size_type curLinePos, + string::size_type* newLinePos) const +{ + generateImpl(*os, maxLineLength, curLinePos, newLinePos); +} + + string::size_type component::getParsedOffset() const { return (m_parsedOffset); @@ -80,22 +196,5 @@ void component::setParsedBounds(const string::size_type start, const string::siz } -const std::vector > component::getChildComponents() -{ - const std::vector > constList = - const_cast (this)->getChildComponents(); +} // vmime - std::vector > list; - - const std::vector >::size_type count = constList.size(); - - list.resize(count); - - for (std::vector >::size_type i = 0 ; i < count ; ++i) - list[i] = constList[i].constCast (); - - return (list); -} - - -} diff --git a/src/contentDisposition.cpp b/src/contentDisposition.cpp index 0ab7c458..253dbba5 100644 --- a/src/contentDisposition.cpp +++ b/src/contentDisposition.cpp @@ -47,7 +47,7 @@ contentDisposition::contentDisposition(const contentDisposition& type) } -void contentDisposition::parse(const string& buffer, const string::size_type position, +void contentDisposition::parseImpl(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition) { m_name = utility::stringUtils::trim(utility::stringUtils::toLower @@ -60,7 +60,7 @@ void contentDisposition::parse(const string& buffer, const string::size_type pos } -void contentDisposition::generate(utility::outputStream& os, const string::size_type /* maxLineLength */, +void contentDisposition::generateImpl(utility::outputStream& os, const string::size_type /* maxLineLength */, const string::size_type curLinePos, string::size_type* newLinePos) const { os << m_name; @@ -122,9 +122,9 @@ void contentDisposition::setName(const string& name) } -const std::vector > contentDisposition::getChildComponents() const +const std::vector > contentDisposition::getChildComponents() { - return std::vector >(); + return std::vector >(); } diff --git a/src/dateTime.cpp b/src/dateTime.cpp index 089a9006..0d97b2f6 100644 --- a/src/dateTime.cpp +++ b/src/dateTime.cpp @@ -67,7 +67,7 @@ zone = "UT" / "GMT" ; Universal Time */ -void datetime::parse(const string& buffer, const string::size_type position, +void datetime::parseImpl(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition) { const string::value_type* const pend = buffer.data() + end; @@ -588,7 +588,7 @@ void datetime::parse(const string& buffer, const string::size_type position, } -void datetime::generate(utility::outputStream& os, const string::size_type /* maxLineLength */, +void datetime::generateImpl(utility::outputStream& os, const string::size_type /* maxLineLength */, const string::size_type curLinePos, string::size_type* newLinePos) const { static const string::value_type* dayNames[] = @@ -784,9 +784,9 @@ ref datetime::clone() const } -const std::vector > datetime::getChildComponents() const +const std::vector > datetime::getChildComponents() { - return std::vector >(); + return std::vector >(); } diff --git a/src/disposition.cpp b/src/disposition.cpp index b8059a77..24a45798 100644 --- a/src/disposition.cpp +++ b/src/disposition.cpp @@ -79,9 +79,9 @@ disposition& disposition::operator=(const disposition& other) } -const std::vector > disposition::getChildComponents() const +const std::vector > disposition::getChildComponents() { - return std::vector >(); + return std::vector >(); } @@ -171,7 +171,7 @@ const std::vector disposition::getModifierList() const } -void disposition::parse(const string& buffer, const string::size_type position, +void disposition::parseImpl(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition) { // disposition-mode ";" disposition-type @@ -276,7 +276,7 @@ void disposition::parse(const string& buffer, const string::size_type position, } -void disposition::generate(utility::outputStream& os, const string::size_type maxLineLength, +void disposition::generateImpl(utility::outputStream& os, const string::size_type maxLineLength, const string::size_type curLinePos, string::size_type* newLinePos) const { string::size_type pos = curLinePos; diff --git a/src/encoding.cpp b/src/encoding.cpp index 5d99ab63..343a8223 100644 --- a/src/encoding.cpp +++ b/src/encoding.cpp @@ -61,7 +61,7 @@ encoding::encoding(const encoding& enc) } -void encoding::parse(const string& buffer, const string::size_type position, +void encoding::parseImpl(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition) { m_usage = USAGE_UNKNOWN; @@ -80,7 +80,7 @@ void encoding::parse(const string& buffer, const string::size_type position, } -void encoding::generate(utility::outputStream& os, const string::size_type /* maxLineLength */, +void encoding::generateImpl(utility::outputStream& os, const string::size_type /* maxLineLength */, const string::size_type curLinePos, string::size_type* newLinePos) const { os << m_name; @@ -268,9 +268,9 @@ void encoding::setUsage(const EncodingUsage usage) } -const std::vector > encoding::getChildComponents() const +const std::vector > encoding::getChildComponents() { - return std::vector >(); + return std::vector >(); } diff --git a/src/header.cpp b/src/header.cpp index 443aab8b..fcdca2c9 100644 --- a/src/header.cpp +++ b/src/header.cpp @@ -61,7 +61,7 @@ field-body-contents = specials tokens, or else consisting of texts> */ -void header::parse(const string& buffer, const string::size_type position, +void header::parseImpl(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition) { string::size_type pos = position; @@ -83,7 +83,7 @@ void header::parse(const string& buffer, const string::size_type position, } -void header::generate(utility::outputStream& os, const string::size_type maxLineLength, +void header::generateImpl(utility::outputStream& os, const string::size_type maxLineLength, const string::size_type /* curLinePos */, string::size_type* newLinePos) const { // Generate the fields @@ -337,9 +337,9 @@ const std::vector > header::getFieldList() } -const std::vector > header::getChildComponents() const +const std::vector > header::getChildComponents() { - std::vector > list; + std::vector > list; copy_vector(m_fields, list); diff --git a/src/headerField.cpp b/src/headerField.cpp index d1d42368..a8460aaa 100644 --- a/src/headerField.cpp +++ b/src/headerField.cpp @@ -262,14 +262,14 @@ ref headerField::parseNext(const string& buffer, const string::siz } -void headerField::parse(const string& buffer, const string::size_type position, const string::size_type end, +void headerField::parseImpl(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition) { m_value->parse(buffer, position, end, newPosition); } -void headerField::generate(utility::outputStream& os, const string::size_type maxLineLength, +void headerField::generateImpl(utility::outputStream& os, const string::size_type maxLineLength, const string::size_type curLinePos, string::size_type* newLinePos) const { os << m_name + ": "; @@ -296,9 +296,9 @@ bool headerField::isCustom() const } -const std::vector > headerField::getChildComponents() const +const std::vector > headerField::getChildComponents() { - std::vector > list; + std::vector > list; if (m_value) list.push_back(m_value); diff --git a/src/mailbox.cpp b/src/mailbox.cpp index fea74797..dfdccad7 100644 --- a/src/mailbox.cpp +++ b/src/mailbox.cpp @@ -65,7 +65,7 @@ angle-addr = [CFWS] "<" addr-spec ">" [CFWS] / obs-angle-addr */ -void mailbox::parse(const string& buffer, const string::size_type position, +void mailbox::parseImpl(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition) { const string::value_type* const pend = buffer.data() + end; @@ -343,7 +343,7 @@ void mailbox::parse(const string& buffer, const string::size_type position, } -void mailbox::generate(utility::outputStream& os, const string::size_type maxLineLength, +void mailbox::generateImpl(utility::outputStream& os, const string::size_type maxLineLength, const string::size_type curLinePos, string::size_type* newLinePos) const { if (m_name.isEmpty()) @@ -514,9 +514,9 @@ void mailbox::setEmail(const string& email) } -const std::vector > mailbox::getChildComponents() const +const std::vector > mailbox::getChildComponents() { - return std::vector >(); + return std::vector >(); } diff --git a/src/mailboxGroup.cpp b/src/mailboxGroup.cpp index 94f7ba69..c37444a0 100644 --- a/src/mailboxGroup.cpp +++ b/src/mailboxGroup.cpp @@ -54,7 +54,7 @@ mailboxGroup::~mailboxGroup() } -void mailboxGroup::parse(const string& buffer, const string::size_type position, +void mailboxGroup::parseImpl(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition) { const string::value_type* const pend = buffer.data() + end; @@ -111,7 +111,7 @@ void mailboxGroup::parse(const string& buffer, const string::size_type position, } -void mailboxGroup::generate(utility::outputStream& os, const string::size_type maxLineLength, +void mailboxGroup::generateImpl(utility::outputStream& os, const string::size_type maxLineLength, const string::size_type curLinePos, string::size_type* newLinePos) const { // We have to encode the name: @@ -348,9 +348,9 @@ const std::vector > mailboxGroup::getMailboxList() } -const std::vector > mailboxGroup::getChildComponents() const +const std::vector > mailboxGroup::getChildComponents() { - std::vector > list; + std::vector > list; copy_vector(m_list, list); diff --git a/src/mailboxList.cpp b/src/mailboxList.cpp index 0023d9d0..f87fb48d 100644 --- a/src/mailboxList.cpp +++ b/src/mailboxList.cpp @@ -190,20 +190,20 @@ mailboxList& mailboxList::operator=(const mailboxList& other) } -const std::vector > mailboxList::getChildComponents() const +const std::vector > mailboxList::getChildComponents() { return (m_list.getChildComponents()); } -void mailboxList::parse(const string& buffer, const string::size_type position, +void mailboxList::parseImpl(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition) { m_list.parse(buffer, position, end, newPosition); } -void mailboxList::generate(utility::outputStream& os, const string::size_type maxLineLength, +void mailboxList::generateImpl(utility::outputStream& os, const string::size_type maxLineLength, const string::size_type curLinePos, string::size_type* newLinePos) const { m_list.generate(os, maxLineLength, curLinePos, newLinePos); diff --git a/src/mediaType.cpp b/src/mediaType.cpp index 725f9338..627b2768 100644 --- a/src/mediaType.cpp +++ b/src/mediaType.cpp @@ -48,7 +48,7 @@ mediaType::mediaType(const string& type, const string& subType) } -void mediaType::parse(const string& buffer, const string::size_type position, +void mediaType::parseImpl(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition) { const string::value_type* const pend = buffer.data() + end; @@ -82,7 +82,7 @@ void mediaType::parse(const string& buffer, const string::size_type position, } -void mediaType::generate(utility::outputStream& os, const string::size_type maxLineLength, +void mediaType::generateImpl(utility::outputStream& os, const string::size_type maxLineLength, const string::size_type curLinePos, string::size_type* newLinePos) const { const string value = m_type + "/" + m_subType; @@ -176,9 +176,9 @@ void mediaType::setFromString(const string& type) } -const std::vector > mediaType::getChildComponents() const +const std::vector > mediaType::getChildComponents() { - return std::vector >(); + return std::vector >(); } diff --git a/src/message.cpp b/src/message.cpp index 1b4f0867..3fa9b6a3 100644 --- a/src/message.cpp +++ b/src/message.cpp @@ -61,9 +61,14 @@ const string message::generate(const string::size_type maxLineLength, } -void message::parse(const string& buffer) + +void message::generate + (ref os, + const string::size_type maxLineLength, + const string::size_type curLinePos, + string::size_type* newLinePos) const { - bodyPart::parse(buffer); + bodyPart::generate(os, maxLineLength, curLinePos, newLinePos); } diff --git a/src/messageId.cpp b/src/messageId.cpp index 961fb637..1f4b1863 100644 --- a/src/messageId.cpp +++ b/src/messageId.cpp @@ -61,7 +61,7 @@ messageId::messageId(const string& left, const string& right) msg-id = [CFWS] "<" id-left "@" id-right ">" [CFWS] */ -void messageId::parse(const string& buffer, const string::size_type position, +void messageId::parseImpl(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition) { const string::value_type* const pend = buffer.data() + end; @@ -185,7 +185,7 @@ const string messageId::getId() const } -void messageId::generate(utility::outputStream& os, const string::size_type maxLineLength, +void messageId::generateImpl(utility::outputStream& os, const string::size_type maxLineLength, const string::size_type curLinePos, string::size_type* newLinePos) const { string::size_type pos = curLinePos; @@ -288,9 +288,9 @@ void messageId::setRight(const string& right) } -const std::vector > messageId::getChildComponents() const +const std::vector > messageId::getChildComponents() { - return std::vector >(); + return std::vector >(); } diff --git a/src/messageIdSequence.cpp b/src/messageIdSequence.cpp index 08103d07..0a5c9a01 100644 --- a/src/messageIdSequence.cpp +++ b/src/messageIdSequence.cpp @@ -74,9 +74,9 @@ messageIdSequence& messageIdSequence::operator=(const messageIdSequence& other) } -const std::vector > messageIdSequence::getChildComponents() const +const std::vector > messageIdSequence::getChildComponents() { - std::vector > res; + std::vector > res; copy_vector(m_list, res); @@ -84,7 +84,7 @@ const std::vector > messageIdSequence::getChildComponents } -void messageIdSequence::parse(const string& buffer, const string::size_type position, +void messageIdSequence::parseImpl(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition) { removeAllMessageIds(); @@ -106,7 +106,7 @@ void messageIdSequence::parse(const string& buffer, const string::size_type posi } -void messageIdSequence::generate(utility::outputStream& os, const string::size_type maxLineLength, +void messageIdSequence::generateImpl(utility::outputStream& os, const string::size_type maxLineLength, const string::size_type curLinePos, string::size_type* newLinePos) const { string::size_type pos = curLinePos; diff --git a/src/parameter.cpp b/src/parameter.cpp index ccbe1a5b..58d9a3eb 100644 --- a/src/parameter.cpp +++ b/src/parameter.cpp @@ -36,19 +36,19 @@ namespace vmime parameter::parameter(const string& name) - : m_name(name) + : m_name(name), m_value(vmime::create ()) { } parameter::parameter(const string& name, const word& value) - : m_name(name), m_value(value) + : m_name(name), m_value(vmime::create (value)) { } parameter::parameter(const string& name, const string& value) - : m_name(name), m_value(value) + : m_name(name), m_value(vmime::create (value)) { } @@ -73,7 +73,7 @@ void parameter::copyFrom(const component& other) const parameter& param = dynamic_cast (other); m_name = param.m_name; - m_value.copyFrom(param.m_value); + m_value->copyFrom(*param.m_value); } @@ -92,7 +92,7 @@ const string& parameter::getName() const const word& parameter::getValue() const { - return m_value; + return *m_value; } @@ -109,15 +109,15 @@ void parameter::setValue(const component& value) void parameter::setValue(const word& value) { - m_value = value; + *m_value = value; } -void parameter::parse(const string& buffer, const string::size_type position, +void parameter::parseImpl(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition) { - m_value.setBuffer(string(buffer.begin() + position, buffer.begin() + end)); - m_value.setCharset(charset(charsets::US_ASCII)); + m_value->setBuffer(string(buffer.begin() + position, buffer.begin() + end)); + m_value->setCharset(charset(charsets::US_ASCII)); if (newPosition) *newPosition = end; @@ -248,16 +248,16 @@ void parameter::parse(const std::vector & chunks) } } - m_value.setBuffer(value.str()); - m_value.setCharset(ch); + m_value->setBuffer(value.str()); + m_value->setCharset(ch); } -void parameter::generate(utility::outputStream& os, const string::size_type maxLineLength, +void parameter::generateImpl(utility::outputStream& os, const string::size_type maxLineLength, const string::size_type curLinePos, string::size_type* newLinePos) const { const string& name = m_name; - const string& value = m_value.getBuffer(); + const string& value = m_value->getBuffer(); // For compatibility with implementations that do not understand RFC-2231, // also generate a normal "7bit/us-ascii" parameter @@ -344,7 +344,7 @@ void parameter::generate(utility::outputStream& os, const string::size_type maxL // 7-bit (ASCII) bytes in the input will be used to determine if // we need to encode the whole buffer. encoding recommendedEnc; - const bool alwaysEncode = m_value.getCharset().getRecommendedEncoding(recommendedEnc); + const bool alwaysEncode = m_value->getCharset().getRecommendedEncoding(recommendedEnc); bool extended = alwaysEncode; if (needQuotedPrintable) @@ -352,7 +352,7 @@ void parameter::generate(utility::outputStream& os, const string::size_type maxL // Send the name in quoted-printable, so outlook express et.al. // will understand the real filename size_t oldLen = sevenBitBuffer.length(); - m_value.generate(sevenBitStream); + m_value->generate(sevenBitStream); pos += sevenBitBuffer.length() - oldLen; extended = true; // also send with RFC-2231 encoding } @@ -429,7 +429,7 @@ void parameter::generate(utility::outputStream& os, const string::size_type maxL // + at least 5 characters for the value const string::size_type firstSectionLength = name.length() + 4 /* *0*= */ + 2 /* '' */ - + m_value.getCharset().getName().length(); + + m_value->getCharset().getName().length(); if (pos + firstSectionLength + 5 >= maxLineLength) { @@ -539,7 +539,7 @@ void parameter::generate(utility::outputStream& os, const string::size_type maxL if (sectionNumber == 0) { - os << m_value.getCharset().getName(); + os << m_value->getCharset().getName(); os << '\'' << /* No language */ '\''; } @@ -570,11 +570,11 @@ void parameter::generate(utility::outputStream& os, const string::size_type maxL } -const std::vector > parameter::getChildComponents() const +const std::vector > parameter::getChildComponents() { - std::vector > list; + std::vector > list; - list.push_back(ref ::fromPtr(&m_value)); + list.push_back(m_value); return list; } diff --git a/src/parameterizedHeaderField.cpp b/src/parameterizedHeaderField.cpp index 464990e9..756d02f2 100644 --- a/src/parameterizedHeaderField.cpp +++ b/src/parameterizedHeaderField.cpp @@ -78,7 +78,7 @@ struct paramInfo #endif // VMIME_BUILDING_DOC -void parameterizedHeaderField::parse(const string& buffer, const string::size_type position, +void parameterizedHeaderField::parseImpl(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition) { const string::value_type* const pend = buffer.data() + end; @@ -328,13 +328,13 @@ void parameterizedHeaderField::parse(const string& buffer, const string::size_ty } -void parameterizedHeaderField::generate(utility::outputStream& os, const string::size_type maxLineLength, +void parameterizedHeaderField::generateImpl(utility::outputStream& os, const string::size_type maxLineLength, const string::size_type curLinePos, string::size_type* newLinePos) const { string::size_type pos = curLinePos; // Parent header field - headerField::generate(os, maxLineLength, pos, &pos); + headerField::generateImpl(os, maxLineLength, pos, &pos); // Parameters for (std::vector >::const_iterator @@ -552,11 +552,11 @@ const std::vector > parameterizedHeaderField::getParameterList( } -const std::vector > parameterizedHeaderField::getChildComponents() const +const std::vector > parameterizedHeaderField::getChildComponents() { - std::vector > list = headerField::getChildComponents(); + std::vector > list = headerField::getChildComponents(); - for (std::vector >::const_iterator it = m_params.begin() ; + for (std::vector >::iterator it = m_params.begin() ; it != m_params.end() ; ++it) { list.push_back(*it); diff --git a/src/path.cpp b/src/path.cpp index 37a40908..d92bb0a6 100644 --- a/src/path.cpp +++ b/src/path.cpp @@ -106,14 +106,14 @@ path& path::operator=(const path& other) } -const std::vector > path::getChildComponents() const +const std::vector > path::getChildComponents() { - return std::vector >(); + return std::vector >(); } -void path::parse(const string& buffer, const string::size_type position, - const string::size_type end, string::size_type* newPosition) +void path::parseImpl(const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) { string::size_type pos = position; @@ -165,8 +165,8 @@ void path::parse(const string& buffer, const string::size_type position, } -void path::generate(utility::outputStream& os, const string::size_type /* maxLineLength */, - const string::size_type curLinePos, string::size_type* newLinePos) const +void path::generateImpl(utility::outputStream& os, const string::size_type /* maxLineLength */, + const string::size_type curLinePos, string::size_type* newLinePos) const { if (m_localPart.empty() && m_domain.empty()) { diff --git a/src/platforms/posix/posixFile.cpp b/src/platforms/posix/posixFile.cpp index ec529eb6..4087a21f 100644 --- a/src/platforms/posix/posixFile.cpp +++ b/src/platforms/posix/posixFile.cpp @@ -224,6 +224,26 @@ vmime::utility::stream::size_type posixFileReaderInputStream::skip(const size_ty } +vmime::utility::stream::size_type posixFileReaderInputStream::getPosition() const +{ + const off_t curPos = ::lseek(m_fd, 0, SEEK_CUR); + + if (curPos == off_t(-1)) + posixFileSystemFactory::reportError(m_path, errno); + + return static_cast (curPos); +} + + +void posixFileReaderInputStream::seek(const size_type pos) +{ + const off_t newPos = ::lseek(m_fd, pos, SEEK_SET); + + if (newPos == off_t(-1)) + posixFileSystemFactory::reportError(m_path, errno); +} + + // // posixFileWriter diff --git a/src/platforms/windows/windowsFile.cpp b/src/platforms/windows/windowsFile.cpp index 624612af..5da786e5 100644 --- a/src/platforms/windows/windowsFile.cpp +++ b/src/platforms/windows/windowsFile.cpp @@ -479,6 +479,24 @@ vmime::utility::stream::size_type windowsFileReaderInputStream::skip(const size_ return (dwNewPos - dwCurPos); } +vmime::utility::stream::size_type windowsFileReaderInputStream::getPosition() const +{ + DWORD dwCurPos = SetFilePointer(m_hFile, 0, NULL, FILE_CURRENT); + + if (dwCurPos == INVALID_SET_FILE_POINTER) + windowsFileSystemFactory::reportError(m_path, GetLastError()); + + return static_cast (dwCurPos); +} + +void windowsFileReaderInputStream::seek(const size_type pos) +{ + DWORD dwNewPos = SetFilePointer(m_hFile, (LONG)pos, NULL, FILE_BEGIN); + + if (dwNewPos == INVALID_SET_FILE_POINTER) + windowsFileSystemFactory::reportError(m_path, GetLastError()); +} + windowsFileWriter::windowsFileWriter(const vmime::utility::file::path& path, const vmime::string& nativePath) : m_path(path), m_nativePath(nativePath) { diff --git a/src/relay.cpp b/src/relay.cpp index 5cd454fc..97f793db 100644 --- a/src/relay.cpp +++ b/src/relay.cpp @@ -57,7 +57,7 @@ relay::relay(const relay& r) ["for" addr-spec] ; initial form */ -void relay::parse(const string& buffer, const string::size_type position, +void relay::parseImpl(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition) { const string::value_type* const pend = buffer.data() + end; @@ -198,7 +198,7 @@ void relay::parse(const string& buffer, const string::size_type position, } -void relay::generate(utility::outputStream& os, const string::size_type maxLineLength, +void relay::generateImpl(utility::outputStream& os, const string::size_type maxLineLength, const string::size_type curLinePos, string::size_type* newLinePos) const { std::ostringstream oss; @@ -338,10 +338,10 @@ std::vector & relay::getWithList() } -const std::vector > relay::getChildComponents() const +const std::vector > relay::getChildComponents() { // TODO: should fields inherit from 'component'? (using typeAdapter) - return std::vector >(); + return std::vector >(); } diff --git a/src/streamContentHandler.cpp b/src/streamContentHandler.cpp index 89a36b4f..14837d26 100644 --- a/src/streamContentHandler.cpp +++ b/src/streamContentHandler.cpp @@ -25,6 +25,7 @@ #include "vmime/utility/outputStreamAdapter.hpp" #include "vmime/utility/inputStreamStringAdapter.hpp" +#include "vmime/utility/seekableInputStream.hpp" #include "vmime/utility/streamUtils.hpp" @@ -207,6 +208,9 @@ const vmime::encoding& streamContentHandler::getEncoding() const bool streamContentHandler::isBuffered() const { + if (m_stream.dynamicCast () != NULL) + return true; + // FIXME: some streams can be resetted return false; } diff --git a/src/text.cpp b/src/text.cpp index 66c3b353..91b81e1b 100644 --- a/src/text.cpp +++ b/src/text.cpp @@ -67,7 +67,7 @@ text::~text() } -void text::parse(const string& buffer, const string::size_type position, +void text::parseImpl(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition) { removeAllWords(); @@ -85,7 +85,7 @@ void text::parse(const string& buffer, const string::size_type position, } -void text::generate(utility::outputStream& os, const string::size_type maxLineLength, +void text::generateImpl(utility::outputStream& os, const string::size_type maxLineLength, const string::size_type curLinePos, string::size_type* newLinePos) const { encodeAndFold(os, maxLineLength, curLinePos, newLinePos, 0); @@ -389,9 +389,9 @@ text* text::decodeAndUnfold(const string& in, text* generateInExisting) } -const std::vector > text::getChildComponents() const +const std::vector > text::getChildComponents() { - std::vector > list; + std::vector > list; copy_vector(m_words, list); diff --git a/src/utility/inputStreamAdapter.cpp b/src/utility/inputStreamAdapter.cpp index b44b0841..441307b5 100644 --- a/src/utility/inputStreamAdapter.cpp +++ b/src/utility/inputStreamAdapter.cpp @@ -65,6 +65,18 @@ stream::size_type inputStreamAdapter::skip(const size_type count) } +stream::size_type inputStreamAdapter::getPosition() const +{ + return m_stream.tellg(); +} + + +void inputStreamAdapter::seek(const size_type pos) +{ + m_stream.seekg(pos, std::ios_base::beg); +} + + } // utility } // vmime diff --git a/src/utility/inputStreamByteBufferAdapter.cpp b/src/utility/inputStreamByteBufferAdapter.cpp index 92e779fe..907f1eec 100644 --- a/src/utility/inputStreamByteBufferAdapter.cpp +++ b/src/utility/inputStreamByteBufferAdapter.cpp @@ -85,6 +85,19 @@ stream::size_type inputStreamByteBufferAdapter::skip(const size_type count) } +stream::size_type inputStreamByteBufferAdapter::getPosition() const +{ + return m_pos; +} + + +void inputStreamByteBufferAdapter::seek(const size_type pos) +{ + if (pos <= m_length) + m_pos = pos; +} + + } // utility } // vmime diff --git a/src/utility/inputStreamStringAdapter.cpp b/src/utility/inputStreamStringAdapter.cpp index 31c9fdaf..9b8fb0c7 100644 --- a/src/utility/inputStreamStringAdapter.cpp +++ b/src/utility/inputStreamStringAdapter.cpp @@ -89,6 +89,19 @@ stream::size_type inputStreamStringAdapter::skip(const size_type count) } +stream::size_type inputStreamStringAdapter::getPosition() const +{ + return m_pos - m_begin; +} + + +void inputStreamStringAdapter::seek(const size_type pos) +{ + if (m_begin + pos <= m_end) + m_pos = m_begin + pos; +} + + } // utility } // vmime diff --git a/src/utility/inputStreamStringProxyAdapter.cpp b/src/utility/inputStreamStringProxyAdapter.cpp index 5e4b60b9..feecddd5 100644 --- a/src/utility/inputStreamStringProxyAdapter.cpp +++ b/src/utility/inputStreamStringProxyAdapter.cpp @@ -84,6 +84,19 @@ stream::size_type inputStreamStringProxyAdapter::skip(const size_type count) } +stream::size_type inputStreamStringProxyAdapter::getPosition() const +{ + return m_pos; +} + + +void inputStreamStringProxyAdapter::seek(const size_type pos) +{ + if (pos <= m_buffer.length()) + m_pos = pos; +} + + } // utility } // vmime diff --git a/src/utility/parserInputStreamAdapter.cpp b/src/utility/parserInputStreamAdapter.cpp new file mode 100644 index 00000000..7a38ef1b --- /dev/null +++ b/src/utility/parserInputStreamAdapter.cpp @@ -0,0 +1,162 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2012 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "vmime/utility/parserInputStreamAdapter.hpp" + + +namespace vmime { +namespace utility { + + +parserInputStreamAdapter::parserInputStreamAdapter(ref stream) + : m_stream(stream) +{ +} + + +bool parserInputStreamAdapter::eof() const +{ + return m_stream->eof(); +} + + +void parserInputStreamAdapter::reset() +{ + m_stream->reset(); +} + + +stream::size_type parserInputStreamAdapter::read + (value_type* const data, const size_type count) +{ + return m_stream->read(data, count); +} + + +ref parserInputStreamAdapter::getUnderlyingStream() +{ + return m_stream; +} + + +const string parserInputStreamAdapter::extract(const size_type begin, const size_type end) const +{ + const size_type initialPos = m_stream->getPosition(); + + try + { + value_type *buffer = new value_type[end - begin + 1]; + + m_stream->seek(begin); + + const size_type readBytes = m_stream->read(buffer, end - begin); + buffer[readBytes] = '\0'; + + m_stream->seek(initialPos); + + string str(buffer, buffer + readBytes); + delete [] buffer; + + return str; + } + catch (...) + { + m_stream->seek(initialPos); + throw; + } +} + + +stream::size_type parserInputStreamAdapter::findNext + (const std::string& token, const size_type startPosition) +{ + static const unsigned int BUFFER_SIZE = 4096; + + // Token must not be longer than BUFFER_SIZE/2 + if (token.empty() || token.length() > BUFFER_SIZE / 2) + return npos; + + const size_type initialPos = getPosition(); + + seek(startPosition); + + try + { + value_type findBuffer[BUFFER_SIZE]; + value_type* findBuffer1 = findBuffer; + value_type* findBuffer2 = findBuffer + (BUFFER_SIZE / 2) * sizeof(value_type); + + size_type findBufferLen = 0; + size_type findBufferOffset = 0; + + // Fill in initial buffer + findBufferLen = read(findBuffer, BUFFER_SIZE * sizeof(value_type)); + + for (;;) + { + // Find token + for (value_type *begin = findBuffer, *end = findBuffer + findBufferLen - token.length() ; + begin <= end ; ++begin) + { + if (begin[0] == token[0] && + (token.length() == 1 || + memcmp(static_cast (&begin[1]), + static_cast (token.data() + 1), + token.length() - 1) == 0)) + { + seek(initialPos); + return startPosition + findBufferOffset + (begin - findBuffer); + } + } + + // Rotate buffer + memcpy(findBuffer1, findBuffer2, (BUFFER_SIZE / 2) * sizeof(value_type)); + + // Read more bytes + if (findBufferLen < BUFFER_SIZE && eof()) + { + break; + } + else + { + const size_type bytesRead = read(findBuffer2, (BUFFER_SIZE / 2) * sizeof(value_type)); + findBufferLen = (BUFFER_SIZE / 2) + bytesRead; + findBufferOffset += (BUFFER_SIZE / 2); + } + } + + seek(initialPos); + } + catch (...) + { + seek(initialPos); + throw; + } + + return npos; +} + + +} // utility +} // vmime + diff --git a/src/utility/seekableInputStreamRegionAdapter.cpp b/src/utility/seekableInputStreamRegionAdapter.cpp new file mode 100644 index 00000000..348618c0 --- /dev/null +++ b/src/utility/seekableInputStreamRegionAdapter.cpp @@ -0,0 +1,95 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2012 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#include "vmime/utility/seekableInputStreamRegionAdapter.hpp" + + +namespace vmime { +namespace utility { + + +seekableInputStreamRegionAdapter::seekableInputStreamRegionAdapter + (ref stream, const size_type begin, const size_type length) + : m_stream(stream), m_begin(begin), m_length(length) +{ +} + + +bool seekableInputStreamRegionAdapter::eof() const +{ + return getPosition() >= m_length; +} + + +void seekableInputStreamRegionAdapter::reset() +{ + m_stream->seek(m_begin); +} + + +stream::size_type seekableInputStreamRegionAdapter::read + (value_type* const data, const size_type count) +{ + if (getPosition() + count >= m_length) + { + const size_type remaining = m_length - getPosition(); + return m_stream->read(data, remaining); + } + else + { + return m_stream->read(data, count); + } +} + + +stream::size_type seekableInputStreamRegionAdapter::skip(const size_type count) +{ + if (getPosition() + count >= m_length) + { + const size_type remaining = m_length - getPosition(); + m_stream->skip(remaining); + return remaining; + } + else + { + m_stream->skip(count); + return count; + } +} + + +stream::size_type seekableInputStreamRegionAdapter::getPosition() const +{ + return m_stream->getPosition() - m_begin; +} + + +void seekableInputStreamRegionAdapter::seek(const size_type pos) +{ + m_stream->seek(m_begin + pos); +} + + +} // utility +} // vmime + diff --git a/src/utility/stream.cpp b/src/utility/stream.cpp index 1c940c2a..67c1f330 100644 --- a/src/utility/stream.cpp +++ b/src/utility/stream.cpp @@ -29,6 +29,9 @@ namespace vmime { namespace utility { +const stream::size_type stream::npos = static_cast (vmime::string::npos); + + stream::size_type stream::getBlockSize() { return 32768; // 32 KB @@ -37,3 +40,4 @@ stream::size_type stream::getBlockSize() } // utility } // vmime + diff --git a/src/utility/streamUtils.cpp b/src/utility/streamUtils.cpp index f1d3b9de..f7ea62f3 100644 --- a/src/utility/streamUtils.cpp +++ b/src/utility/streamUtils.cpp @@ -52,6 +52,35 @@ stream::size_type bufferedStreamCopy(inputStream& is, outputStream& os) } +stream::size_type bufferedStreamCopyRange(inputStream& is, outputStream& os, + const stream::size_type start, const stream::size_type length) +{ + const stream::size_type blockSize = + std::min(is.getBlockSize(), os.getBlockSize()); + + is.skip(start); + + std::vector vbuffer(blockSize); + + stream::value_type* buffer = &vbuffer.front(); + stream::size_type total = 0; + + while (!is.eof() && total < length) + { + const stream::size_type remaining = std::min(length - total, blockSize); + const stream::size_type read = is.read(buffer, blockSize); + + if (read != 0) + { + os.write(buffer, read); + total += read; + } + } + + return total; +} + + stream::size_type bufferedStreamCopy(inputStream& is, outputStream& os, const stream::size_type length, progressListener* progress) { diff --git a/src/word.cpp b/src/word.cpp index 79060a18..2876ddfb 100644 --- a/src/word.cpp +++ b/src/word.cpp @@ -241,7 +241,7 @@ const std::vector > word::parseMultiple(const string& buffer, const } -void word::parse(const string& buffer, const string::size_type position, +void word::parseImpl(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition) { if (position + 6 < end && // 6 = "=?(.+)?(.*)?=" @@ -324,7 +324,7 @@ void word::parse(const string& buffer, const string::size_type position, } -void word::generate(utility::outputStream& os, const string::size_type maxLineLength, +void word::generateImpl(utility::outputStream& os, const string::size_type maxLineLength, const string::size_type curLinePos, string::size_type* newLinePos) const { generate(os, maxLineLength, curLinePos, newLinePos, 0, NULL); @@ -743,9 +743,9 @@ void word::setBuffer(const string& buffer) } -const std::vector > word::getChildComponents() const +const std::vector > word::getChildComponents() { - return std::vector >(); + return std::vector >(); } diff --git a/tests/parser/bodyPartTest.cpp b/tests/parser/bodyPartTest.cpp index 9d51262a..deb4b9c5 100644 --- a/tests/parser/bodyPartTest.cpp +++ b/tests/parser/bodyPartTest.cpp @@ -33,12 +33,14 @@ VMIME_TEST_SUITE_BEGIN VMIME_TEST_LIST_BEGIN VMIME_TEST(testParse) VMIME_TEST(testGenerate) + VMIME_TEST(testParseGuessBoundary) VMIME_TEST(testParseMissingLastBoundary) VMIME_TEST(testPrologEpilog) VMIME_TEST(testPrologEncoding) VMIME_TEST(testSuccessiveBoundaries) VMIME_TEST(testGenerate7bit) VMIME_TEST(testTextUsageForQPEncoding) + VMIME_TEST(testParseVeryBigMessage) VMIME_TEST_LIST_END @@ -237,6 +239,93 @@ VMIME_TEST_SUITE_BEGIN VASSERT_EQ("2", "Part1-line1\r\nPart1-line2\r\n=89", oss.str()); } + void testParseGuessBoundary() + { + // Boundary is not specified in "Content-Type" field + // Parser will try to guess it from message contents. + + vmime::string str = + "Content-Type: multipart/mixed" + "\r\n\r\n" + "--UNKNOWN-BOUNDARY\r\nHEADER1\r\n\r\nBODY1\r\n" + "--UNKNOWN-BOUNDARY\r\nHEADER2\r\n\r\nBODY2\r\n" + "--UNKNOWN-BOUNDARY--"; + + vmime::bodyPart p; + p.parse(str); + + VASSERT_EQ("count", 2, p.getBody()->getPartCount()); + + VASSERT_EQ("part1-body", "BODY1", extractContents(p.getBody()->getPartAt(0)->getBody()->getContents())); + VASSERT_EQ("part2-body", "BODY2", extractContents(p.getBody()->getPartAt(1)->getBody()->getContents())); + } + + void testParseVeryBigMessage() + { + // When parsing from a seekable input stream, body contents should not + // be kept in memory in a "stringContentHandler" object. Instead, content + // should be accessible via a "streamContentHandler" object. + + static const std::string BODY1_BEGIN = "BEGIN1BEGIN1BEGIN1"; + static const std::string BODY1_LINE = "BODY1BODY1BODY1BODY1BODY1BODY1BODY1BODY1BODY1BODY1BODY1BODY1BODY1"; + static const std::string BODY1_END = "END1END1"; + static const unsigned int BODY1_REPEAT = 35000; + static const unsigned int BODY1_LENGTH = + BODY1_BEGIN.length() + BODY1_LINE.length() * BODY1_REPEAT + BODY1_END.length(); + + static const std::string BODY2_LINE = "BODY2BODY2BODY2BODY2BODY2BODY2BODY2BODY2BODY2BODY2BODY2BODY2BODY2"; + static const unsigned int BODY2_REPEAT = 20000; + + std::ostringstream oss; + oss << "Content-Type: multipart/mixed; boundary=\"MY-BOUNDARY\"" + << "\r\n\r\n" + << "--MY-BOUNDARY\r\n" + << "HEADER1\r\n" + << "\r\n"; + + oss << BODY1_BEGIN; + + for (unsigned int i = 0 ; i < BODY1_REPEAT ; ++i) + oss << BODY1_LINE; + + oss << BODY1_END; + + oss << "\r\n" + << "--MY-BOUNDARY\r\n" + << "HEADER2\r\n" + << "\r\n"; + + for (unsigned int i = 0 ; i < BODY2_REPEAT ; ++i) + oss << BODY2_LINE; + + oss << "\r\n" + << "--MY-BOUNDARY--\r\n"; + + vmime::ref is = + vmime::create (oss.str()); + + vmime::ref msg = vmime::create (); + msg->parse(is, oss.str().length()); + + vmime::ref body1 = msg->getBody()->getPartAt(0)->getBody(); + vmime::ref body1Cts = body1->getContents(); + + vmime::ref body2 = msg->getBody()->getPartAt(1)->getBody(); + vmime::ref body2Cts = body2->getContents(); + + vmime::string body1CtsExtracted; + vmime::utility::outputStreamStringAdapter body1CtsExtractStream(body1CtsExtracted); + body1Cts->extract(body1CtsExtractStream); + + VASSERT_EQ("1.1", BODY1_LENGTH, body1Cts->getLength()); + VASSERT("1.2", body1Cts.dynamicCast () != NULL); + VASSERT_EQ("1.3", BODY1_LENGTH, body1CtsExtracted.length()); + VASSERT_EQ("1.4", BODY1_BEGIN, body1CtsExtracted.substr(0, BODY1_BEGIN.length())); + VASSERT_EQ("1.5", BODY1_END, body1CtsExtracted.substr(BODY1_LENGTH - BODY1_END.length(), BODY1_END.length())); + + VASSERT_EQ("2.1", BODY2_LINE.length() * BODY2_REPEAT, body2Cts->getLength()); + VASSERT("2.2", body2Cts.dynamicCast () != NULL); + } VMIME_TEST_SUITE_END diff --git a/vmime/addressList.hpp b/vmime/addressList.hpp index 2e537c05..9dc283c7 100644 --- a/vmime/addressList.hpp +++ b/vmime/addressList.hpp @@ -56,7 +56,7 @@ public: addressList& operator=(const addressList& other); addressList& operator=(const mailboxList& other); - const std::vector > getChildComponents() const; + const std::vector > getChildComponents(); /** Add a address at the end of the list. @@ -163,14 +163,20 @@ private: std::vector > m_list; -public: - - using component::parse; - using component::generate; +protected: // Component parsing & assembling - void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); - void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; + void parseImpl + (const string& buffer, + const string::size_type position, + const string::size_type end, + string::size_type* newPosition = NULL); + + void generateImpl + (utility::outputStream& os, + const string::size_type maxLineLength = lineLengthLimits::infinite, + const string::size_type curLinePos = 0, + string::size_type* newLinePos = NULL) const; }; diff --git a/vmime/body.hpp b/vmime/body.hpp index 9e83d6b5..bd5bbb92 100644 --- a/vmime/body.hpp +++ b/vmime/body.hpp @@ -278,7 +278,7 @@ public: void copyFrom(const component& other); body& operator=(const body& other); - const std::vector > getChildComponents() const; + const std::vector > getChildComponents(); private: @@ -299,14 +299,20 @@ private: void initNewPart(ref part); -public: - - using component::parse; - using component::generate; +protected: // Component parsing & assembling - void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); - void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; + void parseImpl + (ref parser, + const utility::stream::size_type position, + const utility::stream::size_type end, + utility::stream::size_type* newPosition = NULL); + + void generateImpl + (utility::outputStream& os, + const string::size_type maxLineLength = lineLengthLimits::infinite, + const string::size_type curLinePos = 0, + string::size_type* newLinePos = NULL) const; }; diff --git a/vmime/bodyPart.hpp b/vmime/bodyPart.hpp index aa0f0408..5f36d903 100644 --- a/vmime/bodyPart.hpp +++ b/vmime/bodyPart.hpp @@ -89,7 +89,7 @@ public: void copyFrom(const component& other); bodyPart& operator=(const bodyPart& other); - const std::vector > getChildComponents() const; + const std::vector > getChildComponents(); private: @@ -98,14 +98,20 @@ private: weak_ref m_parent; -public: - - using component::parse; - using component::generate; +protected: // Component parsing & assembling - void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); - void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; + void parseImpl + (ref parser, + const utility::stream::size_type position, + const utility::stream::size_type end, + utility::stream::size_type* newPosition = NULL); + + void generateImpl + (utility::outputStream& os, + const string::size_type maxLineLength = lineLengthLimits::infinite, + const string::size_type curLinePos = 0, + string::size_type* newLinePos = NULL) const; }; diff --git a/vmime/charset.hpp b/vmime/charset.hpp index 5f5e8e58..26abb4f8 100644 --- a/vmime/charset.hpp +++ b/vmime/charset.hpp @@ -62,7 +62,7 @@ public: bool operator==(const charset& value) const; bool operator!=(const charset& value) const; - const std::vector > getChildComponents() const; + const std::vector > getChildComponents(); /** Gets the recommended encoding for this charset. * Note: there may be no recommended encoding. @@ -117,14 +117,20 @@ private: string m_name; -public: - - using component::parse; - using component::generate; +protected: // Component parsing & assembling - void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); - void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; + void parseImpl + (const string& buffer, + const string::size_type position, + const string::size_type end, + string::size_type* newPosition = NULL); + + void generateImpl + (utility::outputStream& os, + const string::size_type maxLineLength = lineLengthLimits::infinite, + const string::size_type curLinePos = 0, + string::size_type* newLinePos = NULL) const; }; diff --git a/vmime/component.hpp b/vmime/component.hpp index 12b04060..5e6f393d 100644 --- a/vmime/component.hpp +++ b/vmime/component.hpp @@ -27,6 +27,8 @@ #include "vmime/base.hpp" #include "vmime/utility/inputStream.hpp" +#include "vmime/utility/seekableInputStream.hpp" +#include "vmime/utility/parserInputStreamAdapter.hpp" #include "vmime/utility/outputStream.hpp" @@ -51,6 +53,12 @@ public: */ void parse(const string& buffer); + /** Parse RFC-822/MIME data for this component. If stream is not seekable, + * or if length is not specified, entire contents of the stream will + * be loaded into memory before parsing. + */ + void parse(ref inputStream, const utility::stream::size_type length); + /** Parse RFC-822/MIME data for this component. * * @param buffer input buffer @@ -58,7 +66,26 @@ public: * @param end end position in the input buffer * @param newPosition will receive the new position in the input buffer */ - virtual void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL) = 0; + void parse + (const string& buffer, + const string::size_type position, + const string::size_type end, + string::size_type* newPosition = NULL); + + /** Parse RFC-822/MIME data for this component. If stream is not seekable, + * or if end position is not specified, entire contents of the stream will + * be loaded into memory before parsing. + * + * @param inputStream stream from which to read data + * @param position current position in the input stream + * @param end end position in the input stream + * @param newPosition will receive the new position in the input stream + */ + void parse + (ref inputStream, + const utility::stream::size_type position, + const utility::stream::size_type end, + utility::stream::size_type* newPosition = NULL); /** Generate RFC-2822/MIME data for this component. * @@ -68,16 +95,35 @@ public: * @param curLinePos length of the current line in the output buffer * @return generated data */ - const string generate(const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0) const; + virtual const string generate + (const string::size_type maxLineLength = lineLengthLimits::infinite, + const string::size_type curLinePos = 0) const; /** Generate RFC-2822/MIME data for this component. * - * @param os output stream + * @param outputStream output stream * @param maxLineLength maximum line length for output * @param curLinePos length of the current line in the output buffer * @param newLinePos will receive the new line position (length of the last line written) */ - virtual void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const = 0; + virtual void generate + (utility::outputStream& outputStream, + const string::size_type maxLineLength = lineLengthLimits::infinite, + const string::size_type curLinePos = 0, + string::size_type* newLinePos = NULL) const; + + /** Generate RFC-2822/MIME data for this component. + * + * @param outputStream output stream + * @param maxLineLength maximum line length for output + * @param curLinePos length of the current line in the output buffer + * @param newLinePos will receive the new line position (length of the last line written) + */ + virtual void generate + (ref outputStream, + const string::size_type maxLineLength = lineLengthLimits::infinite, + const string::size_type curLinePos = 0, + string::size_type* newLinePos = NULL) const; /** Clone this component. * @@ -95,41 +141,56 @@ public: virtual void copyFrom(const component& other) = 0; /** Return the start position of this component in the - * parsed message contents. + * parsed message contents. Use for debugging only. * * @return start position in parsed buffer * or 0 if this component has not been parsed */ - string::size_type getParsedOffset() const; + utility::stream::size_type getParsedOffset() const; /** Return the length of this component in the - * parsed message contents. + * parsed message contents. Use for debugging only. * * @return length of the component in parsed buffer * or 0 if this component has not been parsed */ - string::size_type getParsedLength() const; + utility::stream::size_type getParsedLength() const; /** Return the list of children of this component. * * @return list of child components */ - const std::vector > getChildComponents(); - - /** Return the list of children of this component (const version). - * - * @return list of child components - */ - virtual const std::vector > getChildComponents() const = 0; + virtual const std::vector > getChildComponents() = 0; protected: - void setParsedBounds(const string::size_type start, const string::size_type end); + void setParsedBounds(const utility::stream::size_type start, const utility::stream::size_type end); + + // AT LEAST ONE of these parseImpl() functions MUST be implemented in derived class + virtual void parseImpl + (ref parser, + const utility::stream::size_type position, + const utility::stream::size_type end, + utility::stream::size_type* newPosition = NULL); + + virtual void parseImpl + (const string& buffer, + const string::size_type position, + const string::size_type end, + string::size_type* newPosition = NULL); + + virtual void generateImpl + (utility::outputStream& os, + const string::size_type maxLineLength = lineLengthLimits::infinite, + const string::size_type curLinePos = 0, + string::size_type* newLinePos = NULL) const = 0; private: - string::size_type m_parsedOffset; - string::size_type m_parsedLength; + void offsetParsedBounds(const utility::stream::size_type offset); + + utility::stream::size_type m_parsedOffset; + utility::stream::size_type m_parsedLength; }; diff --git a/vmime/contentDisposition.hpp b/vmime/contentDisposition.hpp index 9d1749be..abd2e1a4 100644 --- a/vmime/contentDisposition.hpp +++ b/vmime/contentDisposition.hpp @@ -63,7 +63,7 @@ public: void copyFrom(const component& other); contentDisposition& operator=(const contentDisposition& other); - const std::vector > getChildComponents() const; + const std::vector > getChildComponents(); contentDisposition& operator=(const string& name); @@ -75,14 +75,20 @@ private: string m_name; -public: - - using component::parse; - using component::generate; +protected: // Component parsing & assembling - void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); - void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; + void parseImpl + (const string& buffer, + const string::size_type position, + const string::size_type end, + string::size_type* newPosition = NULL); + + void generateImpl + (utility::outputStream& os, + const string::size_type maxLineLength = lineLengthLimits::infinite, + const string::size_type curLinePos = 0, + string::size_type* newLinePos = NULL) const; }; diff --git a/vmime/dateTime.hpp b/vmime/dateTime.hpp index 8e996409..053f4a69 100644 --- a/vmime/dateTime.hpp +++ b/vmime/dateTime.hpp @@ -237,16 +237,22 @@ public: // Current date and time static const datetime now(); - const std::vector > getChildComponents() const; + const std::vector > getChildComponents(); -public: - - using component::parse; - using component::generate; +protected: // Component parsing & assembling - void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); - void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; + void parseImpl + (const string& buffer, + const string::size_type position, + const string::size_type end, + string::size_type* newPosition = NULL); + + void generateImpl + (utility::outputStream& os, + const string::size_type maxLineLength = lineLengthLimits::infinite, + const string::size_type curLinePos = 0, + string::size_type* newLinePos = NULL) const; }; diff --git a/vmime/disposition.hpp b/vmime/disposition.hpp index 05bfca2b..7bdc832f 100644 --- a/vmime/disposition.hpp +++ b/vmime/disposition.hpp @@ -50,7 +50,7 @@ public: void copyFrom(const component& other); disposition& operator=(const disposition& other); - const std::vector > getChildComponents() const; + const std::vector > getChildComponents(); /** Set the disposition action mode. @@ -134,14 +134,20 @@ private: std::vector m_modifiers; -public: - - using component::parse; - using component::generate; +protected: // Component parsing & assembling - void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); - void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; + void parseImpl + (const string& buffer, + const string::size_type position, + const string::size_type end, + string::size_type* newPosition = NULL); + + void generateImpl + (utility::outputStream& os, + const string::size_type maxLineLength = lineLengthLimits::infinite, + const string::size_type curLinePos = 0, + string::size_type* newLinePos = NULL) const; }; diff --git a/vmime/encoding.hpp b/vmime/encoding.hpp index 42f5246d..4322b298 100644 --- a/vmime/encoding.hpp +++ b/vmime/encoding.hpp @@ -93,7 +93,7 @@ public: bool operator==(const encoding& value) const; bool operator!=(const encoding& value) const; - const std::vector > getChildComponents() const; + const std::vector > getChildComponents(); /** Decide which encoding to use based on the specified data. * @@ -141,14 +141,20 @@ private: */ static const encoding decideImpl(const string::const_iterator begin, const string::const_iterator end); -public: - - using component::parse; - using component::generate; +protected: // Component parsing & assembling - void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); - void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; + void parseImpl + (const string& buffer, + const string::size_type position, + const string::size_type end, + string::size_type* newPosition = NULL); + + void generateImpl + (utility::outputStream& os, + const string::size_type maxLineLength = lineLengthLimits::infinite, + const string::size_type curLinePos = 0, + string::size_type* newLinePos = NULL) const; }; diff --git a/vmime/header.hpp b/vmime/header.hpp index 95a93267..ed555b0c 100644 --- a/vmime/header.hpp +++ b/vmime/header.hpp @@ -220,7 +220,7 @@ public: void copyFrom(const component& other); header& operator=(const header& other); - const std::vector > getChildComponents() const; + const std::vector > getChildComponents(); private: @@ -251,14 +251,20 @@ private: string m_name; }; -public: - - using component::parse; - using component::generate; +protected: // Component parsing & assembling - void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); - void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; + void parseImpl + (const string& buffer, + const string::size_type position, + const string::size_type end, + string::size_type* newPosition = NULL); + + void generateImpl + (utility::outputStream& os, + const string::size_type maxLineLength = lineLengthLimits::infinite, + const string::size_type curLinePos = 0, + string::size_type* newLinePos = NULL) const; }; diff --git a/vmime/headerField.hpp b/vmime/headerField.hpp index 50494c94..61e01eee 100644 --- a/vmime/headerField.hpp +++ b/vmime/headerField.hpp @@ -59,7 +59,7 @@ public: void copyFrom(const component& other); headerField& operator=(const headerField& other); - const std::vector > getChildComponents() const; + const std::vector > getChildComponents(); /** Sets the name of this field. * @@ -118,15 +118,26 @@ public: void setValue(const string& value); - using component::parse; - using component::generate; - - void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); - void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; - protected: - static ref parseNext(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + void parseImpl + (const string& buffer, + const string::size_type position, + const string::size_type end, + string::size_type* newPosition = NULL); + + void generateImpl + (utility::outputStream& os, + const string::size_type maxLineLength = lineLengthLimits::infinite, + const string::size_type curLinePos = 0, + string::size_type* newLinePos = NULL) const; + + + static ref parseNext + (const string& buffer, + const string::size_type position, + const string::size_type end, + string::size_type* newPosition = NULL); string m_name; diff --git a/vmime/mailbox.hpp b/vmime/mailbox.hpp index 2072be86..2099355d 100644 --- a/vmime/mailbox.hpp +++ b/vmime/mailbox.hpp @@ -85,7 +85,7 @@ public: void clear(); - const std::vector > getChildComponents() const; + const std::vector > getChildComponents(); bool isGroup() const; @@ -101,8 +101,17 @@ public: using address::generate; // Component parsing & assembling - void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); - void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; + void parseImpl + (const string& buffer, + const string::size_type position, + const string::size_type end, + string::size_type* newPosition = NULL); + + void generateImpl + (utility::outputStream& os, + const string::size_type maxLineLength = lineLengthLimits::infinite, + const string::size_type curLinePos = 0, + string::size_type* newLinePos = NULL) const; }; diff --git a/vmime/mailboxGroup.hpp b/vmime/mailboxGroup.hpp index 0061d5b4..14331410 100644 --- a/vmime/mailboxGroup.hpp +++ b/vmime/mailboxGroup.hpp @@ -52,7 +52,7 @@ public: ref clone() const; mailboxGroup& operator=(const component& other); - const std::vector > getChildComponents() const; + const std::vector > getChildComponents(); /** Return the name of the group. * @@ -165,14 +165,20 @@ private: text m_name; std::vector > m_list; -public: - - using address::parse; - using address::generate; +protected: // Component parsing & assembling - void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); - void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; + void parseImpl + (const string& buffer, + const string::size_type position, + const string::size_type end, + string::size_type* newPosition = NULL); + + void generateImpl + (utility::outputStream& os, + const string::size_type maxLineLength = lineLengthLimits::infinite, + const string::size_type curLinePos = 0, + string::size_type* newLinePos = NULL) const; }; diff --git a/vmime/mailboxList.hpp b/vmime/mailboxList.hpp index 11e4e798..1b480c17 100644 --- a/vmime/mailboxList.hpp +++ b/vmime/mailboxList.hpp @@ -51,7 +51,7 @@ public: void copyFrom(const component& other); mailboxList& operator=(const mailboxList& other); - const std::vector > getChildComponents() const; + const std::vector > getChildComponents(); /** Add a mailbox at the end of the list. * @@ -155,14 +155,20 @@ private: addressList m_list; -public: - - using component::parse; - using component::generate; +protected: // Component parsing & assembling - void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); - void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; + void parseImpl + (const string& buffer, + const string::size_type position, + const string::size_type end, + string::size_type* newPosition = NULL); + + void generateImpl + (utility::outputStream& os, + const string::size_type maxLineLength = lineLengthLimits::infinite, + const string::size_type curLinePos = 0, + string::size_type* newLinePos = NULL) const; }; diff --git a/vmime/mediaType.hpp b/vmime/mediaType.hpp index 658b21fe..18182f0c 100644 --- a/vmime/mediaType.hpp +++ b/vmime/mediaType.hpp @@ -55,7 +55,7 @@ public: void copyFrom(const component& other); mediaType& operator=(const mediaType& other); - const std::vector > getChildComponents() const; + const std::vector > getChildComponents(); /** Return the media type. * See the constants in vmime::mediaTypes. @@ -97,14 +97,18 @@ protected: string m_type; string m_subType; -public: - - using component::parse; - using component::generate; - // Component parsing & assembling - void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); - void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; + void parseImpl + (const string& buffer, + const string::size_type position, + const string::size_type end, + string::size_type* newPosition = NULL); + + void generateImpl + (utility::outputStream& os, + const string::size_type maxLineLength = lineLengthLimits::infinite, + const string::size_type curLinePos = 0, + string::size_type* newLinePos = NULL) const; }; diff --git a/vmime/message.hpp b/vmime/message.hpp index f3be2294..9767564d 100644 --- a/vmime/message.hpp +++ b/vmime/message.hpp @@ -43,12 +43,25 @@ public: message(); - // Component parsing & assembling - void generate(utility::outputStream& os, const string::size_type maxLineLength = options::getInstance()->message.maxLineLength(), const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +public: - const string generate(const string::size_type maxLineLength = options::getInstance()->message.maxLineLength(), const string::size_type curLinePos = 0) const; + // Override default generate() functions so that we can change + // the default 'maxLineLength' value + void generate + (utility::outputStream& os, + const string::size_type maxLineLength = options::getInstance()->message.maxLineLength(), + const string::size_type curLinePos = 0, + string::size_type* newLinePos = NULL) const; - void parse(const string& buffer); + const string generate + (const string::size_type maxLineLength = options::getInstance()->message.maxLineLength(), + const string::size_type curLinePos = 0) const; + + void generate + (ref os, + const string::size_type maxLineLength = lineLengthLimits::infinite, + const string::size_type curLinePos = 0, + string::size_type* newLinePos = NULL) const; }; diff --git a/vmime/messageId.hpp b/vmime/messageId.hpp index 3686b11c..ac408e63 100644 --- a/vmime/messageId.hpp +++ b/vmime/messageId.hpp @@ -97,23 +97,27 @@ public: void copyFrom(const component& other); messageId& operator=(const messageId& other); - const std::vector > getChildComponents() const; + const std::vector > getChildComponents(); private: string m_left; string m_right; -public: - - using component::parse; - using component::generate; +protected: // Component parsing & assembling - void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); - void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; + void parseImpl + (const string& buffer, + const string::size_type position, + const string::size_type end, + string::size_type* newPosition = NULL); -protected: + void generateImpl + (utility::outputStream& os, + const string::size_type maxLineLength = lineLengthLimits::infinite, + const string::size_type curLinePos = 0, + string::size_type* newLinePos = NULL) const; /** Parse a message-id from an input buffer. * @@ -123,7 +127,11 @@ protected: * @param newPosition will receive the new position in the input buffer * @return a new message-id object, or null if no more message-id can be parsed from the input buffer */ - static ref parseNext(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition); + static ref parseNext + (const string& buffer, + const string::size_type position, + const string::size_type end, + string::size_type* newPosition); }; diff --git a/vmime/messageIdSequence.hpp b/vmime/messageIdSequence.hpp index 5dfb840a..6736d0a9 100644 --- a/vmime/messageIdSequence.hpp +++ b/vmime/messageIdSequence.hpp @@ -49,7 +49,7 @@ public: void copyFrom(const component& other); messageIdSequence& operator=(const messageIdSequence& other); - const std::vector > getChildComponents() const; + const std::vector > getChildComponents(); /** Add a message-id at the end of the list. @@ -148,14 +148,20 @@ private: std::vector > m_list; -public: - - using component::parse; - using component::generate; +protected: // Component parsing & assembling - void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); - void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; + void parseImpl + (const string& buffer, + const string::size_type position, + const string::size_type end, + string::size_type* newPosition = NULL); + + void generateImpl + (utility::outputStream& os, + const string::size_type maxLineLength = lineLengthLimits::infinite, + const string::size_type curLinePos = 0, + string::size_type* newLinePos = NULL) const; }; diff --git a/vmime/parameter.hpp b/vmime/parameter.hpp index e1b13a12..0773ea6b 100644 --- a/vmime/parameter.hpp +++ b/vmime/parameter.hpp @@ -67,7 +67,7 @@ public: void copyFrom(const component& other); parameter& operator=(const parameter& other); - const std::vector > getChildComponents() const; + const std::vector > getChildComponents(); /** Return the name of this parameter. * @@ -104,7 +104,7 @@ public: const T getValueAs() const { T ret; - ret.parse(m_value.getBuffer()); + ret.parse(m_value->getBuffer()); return ret; } @@ -122,11 +122,19 @@ public: void setValue(const word& value); - using component::parse; - using component::generate; +protected: - void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); - void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; + void parseImpl + (const string& buffer, + const string::size_type position, + const string::size_type end, + string::size_type* newPosition = NULL); + + void generateImpl + (utility::outputStream& os, + const string::size_type maxLineLength = lineLengthLimits::infinite, + const string::size_type curLinePos = 0, + string::size_type* newLinePos = NULL) const; private: @@ -134,7 +142,7 @@ private: string m_name; - word m_value; + ref m_value; }; diff --git a/vmime/parameterizedHeaderField.hpp b/vmime/parameterizedHeaderField.hpp index 2940ca3f..d2c934f7 100644 --- a/vmime/parameterizedHeaderField.hpp +++ b/vmime/parameterizedHeaderField.hpp @@ -172,19 +172,25 @@ public: */ const std::vector > getParameterList(); + const std::vector > getChildComponents(); + private: std::vector > m_params; -public: +protected: - using headerField::parse; - using headerField::generate; + void parseImpl + (const string& buffer, + const string::size_type position, + const string::size_type end, + string::size_type* newPosition = NULL); - void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); - void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; - - const std::vector > getChildComponents() const; + void generateImpl + (utility::outputStream& os, + const string::size_type maxLineLength = lineLengthLimits::infinite, + const string::size_type curLinePos = 0, + string::size_type* newLinePos = NULL) const; }; diff --git a/vmime/parserHelpers.hpp b/vmime/parserHelpers.hpp index 9b075f73..d4f12465 100644 --- a/vmime/parserHelpers.hpp +++ b/vmime/parserHelpers.hpp @@ -45,6 +45,10 @@ public: return (c == ' ' || c == '\t' || c == '\n' || c == '\r'); } + static bool isSpaceOrTab(const char_t c) + { + return (c == ' ' || c == '\t'); + } static bool isDigit(const char_t c) { diff --git a/vmime/path.hpp b/vmime/path.hpp index beaa72b7..eec8dfcb 100644 --- a/vmime/path.hpp +++ b/vmime/path.hpp @@ -76,21 +76,26 @@ public: ref clone() const; path& operator=(const path& other); - const std::vector > getChildComponents() const; + const std::vector > getChildComponents(); protected: string m_localPart; string m_domain; -public: - - using component::parse; - using component::generate; // Component parsing & assembling - void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); - void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; + void parseImpl + (const string& buffer, + const string::size_type position, + const string::size_type end, + string::size_type* newPosition = NULL); + + void generateImpl + (utility::outputStream& os, + const string::size_type maxLineLength = lineLengthLimits::infinite, + const string::size_type curLinePos = 0, + string::size_type* newLinePos = NULL) const; }; diff --git a/vmime/platforms/posix/posixFile.hpp b/vmime/platforms/posix/posixFile.hpp index 70986dff..704b7b00 100644 --- a/vmime/platforms/posix/posixFile.hpp +++ b/vmime/platforms/posix/posixFile.hpp @@ -26,6 +26,7 @@ #include "vmime/utility/file.hpp" +#include "vmime/utility/seekableInputStream.hpp" #if VMIME_HAVE_FILESYSTEM_FEATURES @@ -57,7 +58,7 @@ private: -class posixFileReaderInputStream : public vmime::utility::inputStream +class posixFileReaderInputStream : public vmime::utility::seekableInputStream { public: @@ -72,6 +73,9 @@ public: size_type skip(const size_type count); + size_type getPosition() const; + void seek(const size_type pos); + private: const vmime::utility::file::path m_path; diff --git a/vmime/platforms/windows/windowsFile.hpp b/vmime/platforms/windows/windowsFile.hpp index 6e1c8fbb..f4170329 100644 --- a/vmime/platforms/windows/windowsFile.hpp +++ b/vmime/platforms/windows/windowsFile.hpp @@ -26,6 +26,7 @@ #include "vmime/utility/file.hpp" +#include "vmime/utility/seekableInputStream.hpp" #include @@ -157,6 +158,8 @@ public: void reset(); size_type read(value_type* const data, const size_type count); size_type skip(const size_type count); + size_type getPosition() const; + void seek(const size_type pos); private: diff --git a/vmime/relay.hpp b/vmime/relay.hpp index 583ad804..dbaedf2c 100644 --- a/vmime/relay.hpp +++ b/vmime/relay.hpp @@ -51,7 +51,7 @@ public: void copyFrom(const component& other); relay& operator=(const relay& other); - const std::vector > getChildComponents() const; + const std::vector > getChildComponents(); const string& getFrom() const; void setFrom(const string& from); @@ -85,13 +85,19 @@ private: datetime m_date; -public: +protected: - using component::parse; - using component::generate; + void parseImpl + (const string& buffer, + const string::size_type position, + const string::size_type end, + string::size_type* newPosition = NULL); - void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); - void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; + void generateImpl + (utility::outputStream& os, + const string::size_type maxLineLength = lineLengthLimits::infinite, + const string::size_type curLinePos = 0, + string::size_type* newLinePos = NULL) const; }; diff --git a/vmime/text.hpp b/vmime/text.hpp index 15e11ae6..778ce865 100644 --- a/vmime/text.hpp +++ b/vmime/text.hpp @@ -58,7 +58,7 @@ public: text& operator=(const component& other); text& operator=(const text& other); - const std::vector > getChildComponents() const; + const std::vector > getChildComponents(); /** Add a word at the end of the list. * @@ -226,13 +226,20 @@ public: */ static text* decodeAndUnfold(const string& in, text* generateInExisting); - - using component::parse; - using component::generate; +protected: // Component parsing & assembling - void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); - void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; + void parseImpl + (const string& buffer, + const string::size_type position, + const string::size_type end, + string::size_type* newPosition = NULL); + + void generateImpl + (utility::outputStream& os, + const string::size_type maxLineLength = lineLengthLimits::infinite, + const string::size_type curLinePos = 0, + string::size_type* newLinePos = NULL) const; private: diff --git a/vmime/utility/inputStreamAdapter.hpp b/vmime/utility/inputStreamAdapter.hpp index 278ab529..bd4d21e8 100644 --- a/vmime/utility/inputStreamAdapter.hpp +++ b/vmime/utility/inputStreamAdapter.hpp @@ -25,7 +25,7 @@ #define VMIME_UTILITY_INPUTSTREAMADAPTER_HPP_INCLUDED -#include "vmime/utility/inputStream.hpp" +#include "vmime/utility/seekableInputStream.hpp" #include @@ -37,7 +37,7 @@ namespace utility { /** An adapter class for C++ standard input streams. */ -class inputStreamAdapter : public inputStream +class inputStreamAdapter : public seekableInputStream { public: @@ -49,6 +49,8 @@ public: void reset(); size_type read(value_type* const data, const size_type count); size_type skip(const size_type count); + size_type getPosition() const; + void seek(const size_type pos); private: diff --git a/vmime/utility/inputStreamByteBufferAdapter.hpp b/vmime/utility/inputStreamByteBufferAdapter.hpp index 0f6a442d..b3dafd92 100644 --- a/vmime/utility/inputStreamByteBufferAdapter.hpp +++ b/vmime/utility/inputStreamByteBufferAdapter.hpp @@ -25,7 +25,7 @@ #define VMIME_UTILITY_INPUTSTREAMBYTEBUFFERADAPTER_HPP_INCLUDED -#include "vmime/utility/inputStream.hpp" +#include "vmime/utility/seekableInputStream.hpp" namespace vmime { @@ -35,7 +35,7 @@ namespace utility { /** An adapter class for reading from an array of bytes. */ -class inputStreamByteBufferAdapter : public inputStream +class inputStreamByteBufferAdapter : public seekableInputStream { public: @@ -45,6 +45,8 @@ public: void reset(); size_type read(value_type* const data, const size_type count); size_type skip(const size_type count); + size_type getPosition() const; + void seek(const size_type pos); private: diff --git a/vmime/utility/inputStreamStringAdapter.hpp b/vmime/utility/inputStreamStringAdapter.hpp index a7d986f1..18a9083c 100644 --- a/vmime/utility/inputStreamStringAdapter.hpp +++ b/vmime/utility/inputStreamStringAdapter.hpp @@ -25,7 +25,7 @@ #define VMIME_UTILITY_INPUTSTREAMSTRINGADAPTER_HPP_INCLUDED -#include "vmime/utility/inputStream.hpp" +#include "vmime/utility/seekableInputStream.hpp" namespace vmime { @@ -35,7 +35,7 @@ namespace utility { /** An adapter class for string input. */ -class inputStreamStringAdapter : public inputStream +class inputStreamStringAdapter : public seekableInputStream { public: @@ -46,6 +46,8 @@ public: void reset(); size_type read(value_type* const data, const size_type count); size_type skip(const size_type count); + size_type getPosition() const; + void seek(const size_type pos); private: diff --git a/vmime/utility/inputStreamStringProxyAdapter.hpp b/vmime/utility/inputStreamStringProxyAdapter.hpp index 74b3f604..dc52637f 100644 --- a/vmime/utility/inputStreamStringProxyAdapter.hpp +++ b/vmime/utility/inputStreamStringProxyAdapter.hpp @@ -25,7 +25,7 @@ #define VMIME_UTILITY_INPUTSTREAMSTRINGPROXYADAPTER_HPP_INCLUDED -#include "vmime/utility/inputStream.hpp" +#include "vmime/utility/seekableInputStream.hpp" namespace vmime { @@ -38,7 +38,7 @@ class stringProxy; /** An adapter class for stringProxy input. */ -class inputStreamStringProxyAdapter : public inputStream +class inputStreamStringProxyAdapter : public seekableInputStream { public: @@ -50,6 +50,8 @@ public: void reset(); size_type read(value_type* const data, const size_type count); size_type skip(const size_type count); + size_type getPosition() const; + void seek(const size_type pos); private: diff --git a/vmime/utility/parserInputStreamAdapter.hpp b/vmime/utility/parserInputStreamAdapter.hpp new file mode 100644 index 00000000..c24fa449 --- /dev/null +++ b/vmime/utility/parserInputStreamAdapter.hpp @@ -0,0 +1,173 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2012 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#ifndef VMIME_UTILITY_PARSERINPUTSTREAMADAPTER_HPP_INCLUDED +#define VMIME_UTILITY_PARSERINPUTSTREAMADAPTER_HPP_INCLUDED + + +#include "vmime/utility/seekableInputStream.hpp" + +#include + + +namespace vmime { +namespace utility { + + +/** An adapter class used for parsing from an input stream. + */ + +class parserInputStreamAdapter : public seekableInputStream +{ +public: + + /** @param is input stream to wrap + */ + parserInputStreamAdapter(ref inputStream); + + ref getUnderlyingStream(); + + bool eof() const; + void reset(); + size_type read(value_type* const data, const size_type count); + + void seek(const size_type pos) + { + m_stream->seek(pos); + } + + size_type skip(const size_type count) + { + return m_stream->skip(count); + } + + size_type getPosition() const + { + return m_stream->getPosition(); + } + + /** Get the byte at the current position without updating the + * current position. + * + * @return byte at the current position + */ + value_type peekByte() const + { + const size_type initialPos = m_stream->getPosition(); + + try + { + value_type buffer[1]; + const size_type readBytes = m_stream->read(buffer, 1); + + m_stream->seek(initialPos); + + return (readBytes == 1 ? buffer[0] : 0); + } + catch (...) + { + m_stream->seek(initialPos); + throw; + } + } + + /** Get the byte at the current position and advance current + * position by one byte. + * + * @return byte at the current position + */ + value_type getByte() + { + value_type buffer[1]; + const size_type readBytes = m_stream->read(buffer, 1); + + return (readBytes == 1 ? buffer[0] : 0); + } + + /** Check whether the bytes following the current position match + * the specified bytes. Position is not updated. + * + * @param bytes bytes to compare + * @param length number of bytes + * @return true if the next bytes match the pattern, false otherwise + */ + bool matchBytes(const value_type* bytes, const size_type length) const + { + const size_type initialPos = m_stream->getPosition(); + + try + { + value_type buffer[32]; + const size_type readBytes = m_stream->read(buffer, length); + + m_stream->seek(initialPos); + + return readBytes == length && + ::memcmp(bytes, buffer, length) == 0; + } + catch (...) + { + m_stream->seek(initialPos); + throw; + } + } + + const string extract(const size_type begin, const size_type end) const; + + /** Skips bytes matching a predicate from the current position. + * The current position is updated to the next following byte + * which does not match the predicate. + * + * @param pred predicate + * @param endPosition stop at this position (or at end of the stream, + * whichever comes first) + * @return number of bytes skipped + */ + template + size_type skipIf(PREDICATE pred, const size_type endPosition) + { + const size_type initialPos = getPosition(); + size_type pos = initialPos; + + while (!m_stream->eof() && pos < endPosition && pred(getByte())) + ++pos; + + m_stream->seek(pos); + + return pos - initialPos; + } + + size_type findNext(const std::string& token, const size_type startPosition = 0); + +private: + + mutable ref m_stream; +}; + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_PARSERINPUTSTREAMADAPTER_HPP_INCLUDED + diff --git a/vmime/utility/seekableInputStream.hpp b/vmime/utility/seekableInputStream.hpp new file mode 100644 index 00000000..c2ab1bb0 --- /dev/null +++ b/vmime/utility/seekableInputStream.hpp @@ -0,0 +1,64 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2012 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#ifndef VMIME_UTILITY_SEEKABLEINPUTSTREAM_HPP_INCLUDED +#define VMIME_UTILITY_SEEKABLEINPUTSTREAM_HPP_INCLUDED + + +#include "vmime/utility/inputStream.hpp" + + +namespace vmime { +namespace utility { + + +/** An input stream that allows seeking within the input. + */ + +class seekableInputStream : public inputStream +{ +public: + + /** Returns the current position in this stream. + * + * @return the offset from the beginning of the stream, in bytes, + * at which the next read occurs + */ + virtual size_type getPosition() const = 0; + + /** Sets the position, measured from the beginning of this stream, + * at which the next read occurs. + * + * @param pos the offset position, measured in bytes from the + * beginning of the stream, at which to set the stream pointer. + */ + virtual void seek(const size_type pos) = 0; +}; + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_SEEKABLEINPUTSTREAM_HPP_INCLUDED + diff --git a/vmime/utility/seekableInputStreamRegionAdapter.hpp b/vmime/utility/seekableInputStreamRegionAdapter.hpp new file mode 100644 index 00000000..5ebccc62 --- /dev/null +++ b/vmime/utility/seekableInputStreamRegionAdapter.hpp @@ -0,0 +1,71 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2012 Vincent Richard +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 3 of +// the License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License along +// with this program; if not, write to the Free Software Foundation, Inc., +// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +// +// Linking this library statically or dynamically with other modules is making +// a combined work based on this library. Thus, the terms and conditions of +// the GNU General Public License cover the whole combination. +// + +#ifndef VMIME_UTILITY_SEEKABLEINPUTSTREAMREGIONADAPTER_HPP_INCLUDED +#define VMIME_UTILITY_SEEKABLEINPUTSTREAMREGIONADAPTER_HPP_INCLUDED + + +#include "vmime/utility/seekableInputStream.hpp" + + +namespace vmime { +namespace utility { + + +/** An adapter for reading a limited region of a seekable input stream. + */ + +class seekableInputStreamRegionAdapter : public seekableInputStream +{ +public: + + /** Creates a new adapter for a seekableInputStream. + * + * @param stream source stream + * @param begin start position in source stream + * @param length region length in source stream + */ + seekableInputStreamRegionAdapter(ref stream, + const size_type begin, const size_type length); + + bool eof() const; + void reset(); + size_type read(value_type* const data, const size_type count); + size_type skip(const size_type count); + size_type getPosition() const; + void seek(const size_type pos); + +private: + + ref m_stream; + size_type m_begin; + size_type m_length; +}; + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_SEEKABLEINPUTSTREAMREGIONADAPTER_HPP_INCLUDED + diff --git a/vmime/utility/stream.hpp b/vmime/utility/stream.hpp index 566ab9df..78be8277 100644 --- a/vmime/utility/stream.hpp +++ b/vmime/utility/stream.hpp @@ -54,6 +54,10 @@ public: */ typedef string::size_type size_type; + /** Constant value with the greatest possible value for an element of type size_type. + */ + static const size_type npos; + /** Return the preferred maximum block size when reading * from or writing to this stream. * diff --git a/vmime/utility/streamUtils.hpp b/vmime/utility/streamUtils.hpp index cdf70aad..87c8fc58 100644 --- a/vmime/utility/streamUtils.hpp +++ b/vmime/utility/streamUtils.hpp @@ -44,6 +44,19 @@ namespace utility { stream::size_type bufferedStreamCopy(inputStream& is, outputStream& os); +/** Copy data from one stream into another stream using a buffered method + * and copying only a specified range of data. + * + * @param is input stream (source data) + * @param os output stream (destination for data) + * @param start number of bytes to ignore before starting copying + * @param length maximum number of bytes to copy + * @return number of bytes copied + */ + +stream::size_type bufferedStreamCopyRange(inputStream& is, outputStream& os, + const stream::size_type start, const stream::size_type length); + /** Copy data from one stream into another stream using a buffered method * and notify progress state of the operation. * diff --git a/vmime/word.hpp b/vmime/word.hpp index ad848eca..492aab5e 100644 --- a/vmime/word.hpp +++ b/vmime/word.hpp @@ -128,21 +128,52 @@ public: #endif - using component::parse; +protected: + + void parseImpl + (const string& buffer, + const string::size_type position, + const string::size_type end, + string::size_type* newPosition = NULL); + + void generateImpl + (utility::outputStream& os, + const string::size_type maxLineLength = lineLengthLimits::infinite, + const string::size_type curLinePos = 0, + string::size_type* newLinePos = NULL) const; + +public: + using component::generate; - void parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); - void generate(utility::outputStream& os, const string::size_type maxLineLength = lineLengthLimits::infinite, const string::size_type curLinePos = 0, string::size_type* newLinePos = NULL) const; +#ifndef VMIME_BUILDING_DOC + void generate + (utility::outputStream& os, + const string::size_type maxLineLength, + const string::size_type curLinePos, + string::size_type* newLinePos, + const int flags, + generatorState* state) const; +#endif - void generate(utility::outputStream& os, const string::size_type maxLineLength, const string::size_type curLinePos, string::size_type* newLinePos, const int flags, generatorState* state) const; - - const std::vector > getChildComponents() const; + const std::vector > getChildComponents(); private: - static ref parseNext(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition, bool prevIsEncoded, bool* isEncoded, bool isFirst); + static ref parseNext + (const string& buffer, + const string::size_type position, + const string::size_type end, + string::size_type* newPosition, + bool prevIsEncoded, + bool* isEncoded, + bool isFirst); - static const std::vector > parseMultiple(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition); + static const std::vector > parseMultiple + (const string& buffer, + const string::size_type position, + const string::size_type end, + string::size_type* newPosition); // The "m_buffer" of this word holds the data, and this data is encoded