This file is part of MXE. See index.html for further information. Commits from master branch of git://github.com/kisli/vmime rebased onto version 0.9.1 tarball files. From 17ff5157ffdc749f60b8285f84e64ac5e06d4283 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Tue, 16 Nov 2010 13:28:05 +0000 Subject: [PATCH 01/42] Started version 0.9.2. diff --git a/ChangeLog b/ChangeLog index 871d055..8fdcdb0 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,12 @@ +VERSION 0.9.2svn +================ + +2010-11-16 Vincent Richard + + * Started version 0.9.2. + + VERSION 0.9.1 ============= diff --git a/SConstruct b/SConstruct index fb01edf..55f9223 100644 --- a/SConstruct +++ b/SConstruct @@ -29,7 +29,7 @@ import string # Package version number packageVersionMajor = 0 packageVersionMinor = 9 -packageVersionMicro = 1 +packageVersionMicro = 2 # API version number (libtool) # -- 1.7.10.4 From c12ee2b267b9dcfd092a298dfd9a8eec81ab3a0b Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Tue, 30 Nov 2010 14:57:03 +0000 Subject: [PATCH 02/42] Initialize and delete object. diff --git a/vmime/net/imap/IMAPParser.hpp b/vmime/net/imap/IMAPParser.hpp index 0f3e9ec..d71c3ca 100644 --- a/vmime/net/imap/IMAPParser.hpp +++ b/vmime/net/imap/IMAPParser.hpp @@ -3823,7 +3823,9 @@ public: msg_att_item() : m_date_time(NULL), m_number(NULL), m_envelope(NULL), - m_uniqueid(NULL), m_nstring(NULL), m_body(NULL), m_flag_list(NULL) + m_uniqueid(NULL), m_nstring(NULL), m_body(NULL), m_flag_list(NULL), + m_section(NULL) + { } @@ -3836,6 +3838,7 @@ public: delete (m_nstring); delete (m_body); delete (m_flag_list); + delete (m_section); } void go(IMAPParser& parser, string& line, string::size_type* currentPos) -- 1.7.10.4 From fd277afe87485c9d3377964794b76006c6d36a56 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Wed, 8 Dec 2010 08:52:54 +0000 Subject: [PATCH 03/42] No extra space between ':' and '<' in MAIL FROM and RCPT TO. Wait for server response after QUIT and before closing connection. diff --git a/src/net/smtp/SMTPTransport.cpp b/src/net/smtp/SMTPTransport.cpp index 204daae..d9fb7b8 100644 --- a/src/net/smtp/SMTPTransport.cpp +++ b/src/net/smtp/SMTPTransport.cpp @@ -516,6 +516,7 @@ void SMTPTransport::internalDisconnect() try { sendRequest("QUIT"); + readResponse(); } catch (exception&) { @@ -565,7 +566,7 @@ void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients // Emit the "MAIL" command ref resp; - sendRequest("MAIL FROM: <" + expeditor.getEmail() + ">"); + sendRequest("MAIL FROM:<" + expeditor.getEmail() + ">"); if ((resp = readResponse())->getCode() != 250) { @@ -578,7 +579,7 @@ void SMTPTransport::send(const mailbox& expeditor, const mailboxList& recipients { const mailbox& mbox = *recipients.getMailboxAt(i); - sendRequest("RCPT TO: <" + mbox.getEmail() + ">"); + sendRequest("RCPT TO:<" + mbox.getEmail() + ">"); if ((resp = readResponse())->getCode() != 250) { -- 1.7.10.4 From d64da50e879c0e480d2e65c43e3b903c3e80101f Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Fri, 10 Dec 2010 16:24:06 +0000 Subject: [PATCH 04/42] Fixed unit test after bug fix. diff --git a/tests/net/smtp/SMTPTransportTest.cpp b/tests/net/smtp/SMTPTransportTest.cpp index 5015552..6552f9e 100644 --- a/tests/net/smtp/SMTPTransportTest.cpp +++ b/tests/net/smtp/SMTPTransportTest.cpp @@ -165,7 +165,7 @@ public: } else if (cmd == "MAIL") { - VASSERT_EQ("MAIL", std::string("MAIL FROM: "), line); + VASSERT_EQ("MAIL", std::string("MAIL FROM:"), line); localSend("250 OK\r\n"); } -- 1.7.10.4 From 130d0aabda2a9988913ad201390796775dc16a65 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Fri, 10 Dec 2010 16:54:38 +0000 Subject: [PATCH 05/42] Fixed boundary parsing (thanks to John van der Kamp, Zarafa). diff --git a/src/body.cpp b/src/body.cpp index 13dff6b..738d3e7 100644 --- a/src/body.cpp +++ b/src/body.cpp @@ -127,10 +127,30 @@ void body::parse(const string& buffer, const string::size_type position, const string boundarySep("--" + boundary); string::size_type partStart = position; - string::size_type pos = buffer.find(boundarySep, position); + string::size_type pos = position; bool lastPart = false; + while (pos != string::npos && pos < end) + { + pos = buffer.find(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()] == '-' + ) + ) + ) + { + break; + } + + // boundary not a beginning of line, or just a prefix of another, continue the search. + pos++; + } + if (pos != string::npos && pos < end) { m_prologText = string(buffer.begin() + position, buffer.begin() + pos); @@ -181,7 +201,26 @@ void body::parse(const string& buffer, const string::size_type position, } partStart = pos; - pos = buffer.find(boundarySep, partStart); + + while (pos != string::npos && pos < end) + { + pos = buffer.find(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()] == '-' + ) + ) + ) + { + break; + } + + // boundary not a beginning of line, or just a prefix of another, continue the search. + pos++; + } } m_contents = vmime::create (); diff --git a/tests/parser/bodyPartTest.cpp b/tests/parser/bodyPartTest.cpp index 12c4f74..df2bf85 100644 --- a/tests/parser/bodyPartTest.cpp +++ b/tests/parser/bodyPartTest.cpp @@ -84,7 +84,7 @@ VMIME_TEST_SUITE_BEGIN vmime::string str = "Content-Type: multipart/mixed; boundary=\"MY-BOUNDARY\"" "\r\n\r\n" - "--MY-BOUNDARY\r\nHEADER1\r\n\r\nBODY1" + "--MY-BOUNDARY\r\nHEADER1\r\n\r\nBODY1\r\n" "--MY-BOUNDARY\r\nHEADER2\r\n\r\nBODY2"; vmime::bodyPart p; -- 1.7.10.4 From c63f37c888798f0e7e99aa03afda16445a72b7b2 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Fri, 21 Jan 2011 15:28:06 +0000 Subject: [PATCH 06/42] Fixed possible infinite loop (thanks to John van der Kamp, Zarafa). diff --git a/src/word.cpp b/src/word.cpp index db720dc..1c1c1a6 100644 --- a/src/word.cpp +++ b/src/word.cpp @@ -386,7 +386,7 @@ void word::generate(utility::outputStream& os, const string::size_type maxLineLe maxRunLength = std::max(maxRunLength, curRunLength); - if (maxRunLength >= maxLineLength - 3) + if (((flags & text::FORCE_NO_ENCODING) == 0) && maxRunLength >= maxLineLength - 3) { // Generate with encoding forced generate(os, maxLineLength, curLinePos, newLinePos, flags | text::FORCE_ENCODING, state); diff --git a/tests/parser/textTest.cpp b/tests/parser/textTest.cpp index b84f376..746ac94 100644 --- a/tests/parser/textTest.cpp +++ b/tests/parser/textTest.cpp @@ -52,6 +52,7 @@ VMIME_TEST_SUITE_BEGIN VMIME_TEST(testWhitespaceMBox) VMIME_TEST(testFoldingAscii) + VMIME_TEST(testForcedNonEncoding) VMIME_TEST_LIST_END @@ -442,5 +443,15 @@ VMIME_TEST_SUITE_BEGIN " =?us-ascii?Q?9012345678901234567890123456789?=", w.generate(50)); } + void testForcedNonEncoding() + { + // Testing long unbreakable and unencodable header + vmime::relay r; + r.parse(" from User (Ee9GMqZQ8t7IQwftfAFHd2KyScCYRrFSJ50tKEoXv2bVCG4HcPU80GGWiFabAvG77FekpGgF1h@[127.0.0.1]) by servername.hostname.com\n\t" + "with esmtp id 1NGTS9-2C0sqG0; Fri, 4 Dec 2009 09:23:49 +0100"); + + VASSERT_EQ("received.long", "from User\r\n (Ee9GMqZQ8t7IQwftfAFHd2KyScCYRrFSJ50tKEoXv2bVCG4HcPU80GGWiFabAvG77FekpGgF1h@[127.0.0.1])\r\n by servername.hostname.com with esmtp id 1NGTS9-2C0sqG0; Fri, 4 Dec 2009\r\n 09:23:49 +0100", r.generate(78)); + } + VMIME_TEST_SUITE_END -- 1.7.10.4 From 1fafad8f913e700b350e6915de8be710fc2d1ced Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Fri, 28 Jan 2011 12:11:08 +0000 Subject: [PATCH 07/42] Fixed possible read to invalid memory location (thanks to Alexander Konovalov). diff --git a/src/word.cpp b/src/word.cpp index 1c1c1a6..fa08d33 100644 --- a/src/word.cpp +++ b/src/word.cpp @@ -460,7 +460,7 @@ void word::generate(utility::outputStream& os, const string::size_type maxLineLe os << string(curLineStart, p); - if (parserHelpers::isSpace(*(p - 1))) + if (p != m_buffer.begin() && parserHelpers::isSpace(*(p - 1))) state->lastCharIsSpace = true; else state->lastCharIsSpace = false; -- 1.7.10.4 From 73298423f695d7c4441d44619e4b7f9de75f566e Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Wed, 9 Mar 2011 18:03:31 +0000 Subject: [PATCH 08/42] Fixed bug #3174903. Fixed word parsing when buffer does not end with NL. Fixed 'no encoding' when forced. diff --git a/src/body.cpp b/src/body.cpp index 738d3e7..8596833 100644 --- a/src/body.cpp +++ b/src/body.cpp @@ -153,7 +153,10 @@ void body::parse(const string& buffer, const string::size_type position, if (pos != string::npos && pos < end) { - m_prologText = string(buffer.begin() + position, buffer.begin() + pos); + vmime::text text; + text.parse(buffer, position, pos); + + m_prologText = text.getWholeBuffer(); } for (int index = 0 ; !lastPart && (pos != string::npos) && (pos < end) ; ++index) @@ -246,7 +249,10 @@ void body::parse(const string& buffer, const string::size_type position, // Treat remaining text as epilog else if (partStart < end) { - m_epilogText = string(buffer.begin() + partStart, buffer.begin() + end); + vmime::text text; + text.parse(buffer, partStart, end); + + m_epilogText = text.getWholeBuffer(); } } // Treat the contents as 'simple' data @@ -333,7 +339,7 @@ void body::generate(utility::outputStream& os, const string::size_type maxLineLe if (!prologText.empty()) { - text prolog(word(prologText, getCharset())); + text prolog(prologText, vmime::charset("us-ascii")); prolog.encodeAndFold(os, maxLineLength, 0, NULL, text::FORCE_NO_ENCODING | text::NO_NEW_LINE_SEQUENCE); @@ -356,7 +362,7 @@ void body::generate(utility::outputStream& os, const string::size_type maxLineLe if (!epilogText.empty()) { - text epilog(word(epilogText, getCharset())); + text epilog(epilogText, vmime::charset("us-ascii")); epilog.encodeAndFold(os, maxLineLength, 0, NULL, text::FORCE_NO_ENCODING | text::NO_NEW_LINE_SEQUENCE); diff --git a/src/word.cpp b/src/word.cpp index fa08d33..aeaa737 100644 --- a/src/word.cpp +++ b/src/word.cpp @@ -102,7 +102,9 @@ ref word::parseNext(const string& buffer, const string::size_type positio ++pos; unencoded += buffer.substr(startPos, endPos - startPos); - unencoded += ' '; + + if (pos != end) // ignore white-spaces at end + unencoded += ' '; startPos = pos; continue; @@ -191,14 +193,15 @@ ref word::parseNext(const string& buffer, const string::size_type positio ++pos; } - // Treat unencoded text at the end of the buffer - if (end != startPos) - { - if (startPos != pos && !isFirst && prevIsEncoded) - unencoded += whiteSpaces; + if (startPos != end && !isFirst && prevIsEncoded) + unencoded += whiteSpaces; + if (startPos != end) unencoded += buffer.substr(startPos, end - startPos); + // Treat unencoded text at the end of the buffer + if (!unencoded.empty()) + { ref w = vmime::create (unencoded, charset(charsets::US_ASCII)); w->setParsedBounds(position, end); @@ -337,12 +340,14 @@ void word::generate(utility::outputStream& os, const string::size_type maxLineLe state = &defaultGeneratorState; // Find out if encoding is forced or required by contents + charset - bool encodingNeeded = (flags & text::FORCE_ENCODING) != 0; + bool encodingNeeded = false; - if (encodingNeeded == false) - encodingNeeded = wordEncoder::isEncodingNeeded(m_buffer, m_charset); - else if ((flags & text::FORCE_NO_ENCODING) != 0) + if ((flags & text::FORCE_NO_ENCODING) != 0) encodingNeeded = false; + else if ((flags & text::FORCE_ENCODING) != 0) + encodingNeeded = true; + else // auto-detect + encodingNeeded = wordEncoder::isEncodingNeeded(m_buffer, m_charset); // If possible and requested (with flag), quote the buffer (no folding is performed). // Quoting is possible if and only if: diff --git a/tests/parser/bodyPartTest.cpp b/tests/parser/bodyPartTest.cpp index df2bf85..b129913 100644 --- a/tests/parser/bodyPartTest.cpp +++ b/tests/parser/bodyPartTest.cpp @@ -34,6 +34,8 @@ VMIME_TEST_SUITE_BEGIN VMIME_TEST(testParse) VMIME_TEST(testGenerate) VMIME_TEST(testParseMissingLastBoundary) + VMIME_TEST(testPrologEpilog) + VMIME_TEST(testPrologEncoding) VMIME_TEST_LIST_END @@ -105,5 +107,79 @@ VMIME_TEST_SUITE_BEGIN VASSERT_EQ("1", "Foo: bar\r\n\r\nBaz", p1.generate()); } + void testPrologEpilog() + { + const char testMail[] = + "To: test@vmime.org\r\n" + "From: test@vmime.org\r\n" + "Subject: Prolog and epilog test\r\n" + "Content-Type: multipart/mixed; \r\n" + " boundary=\"=_boundary\"\r\n" + "\r\n" + "Prolog text\r\n" + "--=_boundary\r\n" + "Content-Type: text/plain\r\n" + "\r\n" + "Part1\r\n" + "--=_boundary--\r\n" + "Epilog text"; + + vmime::bodyPart part; + part.parse(testMail); + + VASSERT_EQ("prolog", "Prolog text", part.getBody()->getPrologText()); + VASSERT_EQ("epilog", "Epilog text", part.getBody()->getEpilogText()); + } + + // Test for bug fix: prolog should not be encoded + // http://sourceforge.net/tracker/?func=detail&atid=525568&aid=3174903&group_id=69724 + void testPrologEncoding() + { + const char testmail[] = + "To: test@vmime.org\r\n" + "From: test@vmime.org\r\n" + "Subject: Prolog encoding test\r\n" + "Content-Type: multipart/mixed; \r\n" + " boundary=\"=_+ZWjySayKqSf2CyrfnNpaAcO6-G1HpoXdHZ4YyswAWqEY39Q\"\r\n" + "\r\n" + "This is a multi-part message in MIME format. Your mail reader does not\r\n" + "understand MIME message format.\r\n" + "--=_+ZWjySayKqSf2CyrfnNpaAcO6-G1HpoXdHZ4YyswAWqEY39Q\r\n" + "Content-Type: text/html; charset=windows-1251\r\n" + "Content-Transfer-Encoding: quoted-printable\r\n" + "\r\n" + "=DD=F2=EE =F2=E5=EA=F1=F2=EE=E2=E0=FF =F7=E0=F1=F2=FC =F1=EB=EE=E6=ED=EE=E3=\r\n" + "=EE =F1=EE=EE=E1=F9=E5=ED=E8=FF\r\n" + "--=_+ZWjySayKqSf2CyrfnNpaAcO6-G1HpoXdHZ4YyswAWqEY39Q\r\n" + "Content-Type: application/octet-stream; charset=windows-1251\r\n" + "Content-Disposition: attachment; filename=FNS.zip\r\n" + "Content-Transfer-Encoding: base64\r\n" + "\r\n" + "UEsDBB...snap...EEAAAAAA==\r\n" + "--=_+ZWjySayKqSf2CyrfnNpaAcO6-G1HpoXdHZ4YyswAWqEY39Q--\r\n" + "Epilog text"; + + vmime::ref msg = vmime::create(); + + std::string istr(testmail); + + std::string ostr; + vmime::utility::outputStreamStringAdapter out(ostr); + + for (int i = 0 ; i < 10 ; ++i) + { + ostr.clear(); + + msg->parse(istr); + msg->generate(out); + + istr = ostr; + } + + VASSERT_EQ("prolog", "This is a multi-part message in MIME format. Your mail reader" + " does not understand MIME message format.", msg->getBody()->getPrologText()); + VASSERT_EQ("epilog", "Epilog text", msg->getBody()->getEpilogText()); + } + VMIME_TEST_SUITE_END -- 1.7.10.4 From 5f5757b9d4bb0febb1e2183578eb91e801a08038 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Sun, 27 Mar 2011 11:26:55 +0000 Subject: [PATCH 09/42] Allow static linking in mingw-cross-env. Added 'iconv' and uses 'ws2_32' instead of 'winsock32' (#3213487). diff --git a/SConstruct b/SConstruct index 55f9223..177f5b4 100644 --- a/SConstruct +++ b/SConstruct @@ -1089,7 +1089,7 @@ def generateAutotools(target, source, env): vmime_pc_in.write("Description: " + packageDescription + "\n") vmime_pc_in.write("Version: @VERSION@\n") vmime_pc_in.write("Requires: @GSASL_REQUIRED@\n") - vmime_pc_in.write("Libs: -L${libdir} -l@GENERIC_VERSIONED_LIBRARY_NAME@ @GSASL_LIBS@ @LIBGNUTLS_LIBS@ @VMIME_ADDITIONAL_PC_LIBS@\n") + vmime_pc_in.write("Libs: -L${libdir} -l@GENERIC_VERSIONED_LIBRARY_NAME@ @GSASL_LIBS@ @LIBGNUTLS_LIBS@ @LIBICONV@ @PTHREAD_LIBS@ @VMIME_ADDITIONAL_PC_LIBS@\n") #vmime_pc_in.write("Cflags: -I${includedir}/@GENERIC_VERSIONED_LIBRARY_NAME@\n") vmime_pc_in.write("Cflags: -I${includedir}/ @LIBGNUTLS_CFLAGS@\n") vmime_pc_in.close() @@ -1709,7 +1709,7 @@ fi # -- Link with Winsock (Windows) if test "x$VMIME_DETECT_PLATFORM" = "xwindows"; then - VMIME_ADDITIONAL_PC_LIBS="$VMIME_ADDITIONAL_PC_LIBS -lwsock32" + VMIME_ADDITIONAL_PC_LIBS="$VMIME_ADDITIONAL_PC_LIBS -lws2_32" fi # -- getaddrinfo (POSIX) -- 1.7.10.4 From 2b48b4a68ce3e9b9b1a3f485123af5938a568324 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Thu, 31 Mar 2011 19:13:03 +0000 Subject: [PATCH 10/42] Flush stateful data from iconv (thanks to John van der Kamp, Zarafa). diff --git a/src/charsetConverter.cpp b/src/charsetConverter.cpp index 38b9e5e..2135788 100644 --- a/src/charsetConverter.cpp +++ b/src/charsetConverter.cpp @@ -119,6 +119,7 @@ void charsetConverter::convert(utility::inputStream& in, utility::outputStream& size_t inPos = 0; bool prevIsInvalid = false; + bool breakAfterNext = false; while (true) { @@ -126,11 +127,12 @@ void charsetConverter::convert(utility::inputStream& in, utility::outputStream& size_t inLength = static_cast (in.read(inBuffer + inPos, sizeof(inBuffer) - inPos) + inPos); size_t outLength = sizeof(outBuffer); - const char* inPtr = inBuffer; + const char* inPtr = breakAfterNext ? NULL : inBuffer; + size_t *ptrLength = breakAfterNext ? NULL : &inLength; char* outPtr = outBuffer; // Convert input bytes - if (iconv(cd, ICONV_HACK(&inPtr), &inLength, + if (iconv(cd, ICONV_HACK(&inPtr), ptrLength, &outPtr, &outLength) == static_cast (-1)) { // Illegal input sequence or input sequence has no equivalent @@ -170,9 +172,12 @@ void charsetConverter::convert(utility::inputStream& in, utility::outputStream& prevIsInvalid = false; } - // Check for end of data - if (in.eof() && inPos == 0) + if (breakAfterNext) break; + + // Check for end of data, loop again to flush stateful data from iconv + if (in.eof() && inPos == 0) + breakAfterNext = true; } } diff --git a/tests/parser/charsetTest.cpp b/tests/parser/charsetTest.cpp index 8ad71d7..54a09a7 100644 --- a/tests/parser/charsetTest.cpp +++ b/tests/parser/charsetTest.cpp @@ -100,6 +100,7 @@ VMIME_TEST_SUITE_BEGIN VMIME_TEST(testFilterValid1) VMIME_TEST(testFilterValid2) VMIME_TEST(testFilterValid3) + VMIME_TEST(testEncodingHebrew1255) // Test invalid input VMIME_TEST(testFilterInvalid1) @@ -227,6 +228,15 @@ VMIME_TEST_SUITE_BEGIN VASSERT_EQ("1", toHex(expectedOut), toHex(actualOut)); } + void testEncodingHebrew1255() + { + // hewbrew string in windows-1255 charset + const char data[] = "\xe9\xf9\xf7\xf8\xe9\xf9\xf8\xf7\xe9\xe9\xf9"; + vmime::word w = vmime::word(data, "windows-1255"); + vmime::string encoded = w.generate(); + // less than 60% ascii, base64 received + VASSERT_EQ("1", "=?windows-1255?B?6fn3+On5+Pfp6fk=?=", encoded); + } // Conversion to hexadecimal for easier debugging static const vmime::string toHex(const vmime::string str) -- 1.7.10.4 From 8d2e039c5201e144ff08e2ff7cf9efe77fe4b3d0 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Fri, 10 Jun 2011 19:39:09 +0000 Subject: [PATCH 11/42] Requested email change. diff --git a/AUTHORS b/AUTHORS index 20a0181..bbddb30 100644 --- a/AUTHORS +++ b/AUTHORS @@ -21,7 +21,7 @@ AUTHORS file. - Rafael Fernandez - Xin Li - Benjamin Biron - - Bertrand Benoit + - Bertrand Benoit - Tim Teulings - Georg Sauthoff - Pierre Thierry (patches for STL algorithms) -- 1.7.10.4 From cc6317f28ae0b61fea36e1bc78b09dc8300579f8 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Tue, 14 Jun 2011 18:37:54 +0000 Subject: [PATCH 12/42] Fixed compilation issue following namespace change. diff --git a/examples/example7.cpp b/examples/example7.cpp index 1ddb3d0..243b1da 100644 --- a/examples/example7.cpp +++ b/examples/example7.cpp @@ -43,18 +43,18 @@ int main() vmime::platform::setHandler(); // Enumerate encoders - vmime::encoderFactory* ef = vmime::encoderFactory::getInstance(); + vmime::utility::encoder::encoderFactory* ef = vmime::utility::encoder::encoderFactory::getInstance(); std::cout << "Available encoders:" << std::endl; for (int i = 0 ; i < ef->getEncoderCount() ; ++i) { - vmime::ref + vmime::ref enc = ef->getEncoderAt(i); std::cout << " * " << enc->getName() << std::endl; - vmime::ref e = enc->create(); + vmime::ref e = enc->create(); std::vector props = e->getAvailableProperties(); -- 1.7.10.4 From a916d12d44ac43fc8e4729e0a91f4d6243f29a11 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Sun, 19 Jun 2011 17:51:33 +0000 Subject: [PATCH 13/42] Fixed parsing of an attachment filename that is between 66 and 76 characters long (Zarafa). diff --git a/src/parameter.cpp b/src/parameter.cpp index 91a7e5c..f59d5ab 100644 --- a/src/parameter.cpp +++ b/src/parameter.cpp @@ -281,7 +281,8 @@ void parameter::generate(utility::outputStream& os, const string::size_type maxL bool needQuoting = false; string::size_type valueLength = 0; - for (string::size_type i = 0 ; (i < value.length()) && (pos + valueLength < maxLineLength - 4) ; ++i, ++valueLength) + // Use worst-case length name.length()+2 for 'name=' part of line + for (string::size_type i = 0 ; (i < value.length()) && (pos + name.length() + 2 + valueLength < maxLineLength - 4) ; ++i, ++valueLength) { switch (value[i]) { -- 1.7.10.4 From 9735165c57000a6368e91ce8852206a20930c1ca Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Sun, 19 Jun 2011 18:08:12 +0000 Subject: [PATCH 14/42] Correctly generate attachment names which are long and have high characters for Outlook Express (Zarafa). diff --git a/src/parameter.cpp b/src/parameter.cpp index f59d5ab..d757e1b 100644 --- a/src/parameter.cpp +++ b/src/parameter.cpp @@ -268,17 +268,19 @@ void parameter::generate(utility::outputStream& os, const string::size_type maxL // value is to be generated. // A stream for a temporary storage - std::ostringstream sevenBitBuffer; + std::string sevenBitBuffer; + utility::outputStreamStringAdapter sevenBitStream(sevenBitBuffer); string::size_type pos = curLinePos; if (pos + name.length() + 10 + value.length() > maxLineLength) { - sevenBitBuffer << NEW_LINE_SEQUENCE; + sevenBitStream << NEW_LINE_SEQUENCE; pos = NEW_LINE_SEQUENCE_LENGTH; } bool needQuoting = false; + bool needQuotedPrintable = false; string::size_type valueLength = 0; // Use worst-case length name.length()+2 for 'name=' part of line @@ -308,6 +310,16 @@ void parameter::generate(utility::outputStream& os, const string::size_type maxL needQuoting = true; break; + + default: + + if (!parserHelpers::isAscii(value[i])) + { + needQuotedPrintable = true; + needQuoting = true; + } + + break; } } @@ -315,12 +327,12 @@ void parameter::generate(utility::outputStream& os, const string::size_type maxL if (needQuoting) { - sevenBitBuffer << name << "=\""; + sevenBitStream << name << "=\""; pos += name.length() + 2; } else { - sevenBitBuffer << name << "="; + sevenBitStream << name << "="; pos += name.length() + 1; } @@ -332,29 +344,43 @@ void parameter::generate(utility::outputStream& os, const string::size_type maxL const bool alwaysEncode = m_value.getCharset().getRecommendedEncoding(recommendedEnc); bool extended = alwaysEncode; - for (string::size_type i = 0 ; (i < value.length()) && (pos < maxLineLength - 4) ; ++i) + if (needQuotedPrintable) { - const char_t c = value[i]; - - if (/* needQuoting && */ (c == '"' || c == '\\')) // 'needQuoting' is implicit - { - sevenBitBuffer << '\\' << value[i]; // escape 'x' with '\x' - pos += 2; - } - else if (parserHelpers::isAscii(c)) - { - sevenBitBuffer << value[i]; - ++pos; - } - else + // 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); + pos += sevenBitBuffer.length() - oldLen; + extended = true; // also send with RFC-2231 encoding + } + else + { + // Do not chop off this value, but just add the complete name as one header line. + for (string::size_type i = 0 ; i < value.length() ; ++i) { - extended = true; + const char_t c = value[i]; + + if (/* needQuoting && */ (c == '"' || c == '\\')) // 'needQuoting' is implicit + { + sevenBitStream << '\\' << value[i]; // escape 'x' with '\x' + pos += 2; + } + else if (parserHelpers::isAscii(c)) + { + sevenBitStream << value[i]; + ++pos; + } + else + { + extended = true; + } } - } + + } // !needQuotedPrintable if (needQuoting) { - sevenBitBuffer << '"'; + sevenBitStream << '"'; ++pos; } @@ -532,7 +558,7 @@ void parameter::generate(utility::outputStream& os, const string::size_type maxL // "7bit/us-ascii" will suffice in this case. // Output what has been stored in temporary buffer so far - os << sevenBitBuffer.str(); + os << sevenBitBuffer; } #endif // !VMIME_ALWAYS_GENERATE_7BIT_PARAMETER -- 1.7.10.4 From 8d69ad6849d8d6b211674942157f2af8bcd51c26 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Sun, 19 Jun 2011 18:16:49 +0000 Subject: [PATCH 15/42] Alias for UTF-7 charset. diff --git a/src/charset.cpp b/src/charset.cpp index e043186..0fda450 100644 --- a/src/charset.cpp +++ b/src/charset.cpp @@ -45,6 +45,9 @@ charset::charset() charset::charset(const string& name) : m_name(name) { + // If we receive this rfc-1642 valid MIME charset, convert it to something usefull for iconv + if (utility::stringUtils::isStringEqualNoCase(m_name, "unicode-1-1-utf-7")) + m_name = "utf-7"; } @@ -60,6 +63,10 @@ void charset::parse(const string& buffer, const string::size_type position, m_name = utility::stringUtils::trim (string(buffer.begin() + position, buffer.begin() + end)); + // If we parsed this rfc-1642 valid MIME charset, convert it to something usefull for iconv + if (utility::stringUtils::isStringEqualNoCase(m_name, "unicode-1-1-utf-7")) + m_name = "utf-7"; + setParsedBounds(position, end); if (newPosition) -- 1.7.10.4 From ccd95daf9cdd7171fc2027afa5d0ad80b0475ded Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Sun, 19 Jun 2011 18:39:35 +0000 Subject: [PATCH 16/42] Fixed messageBuilder to accept an empty mailbox group in 'To:' field, to allow for undisclosed-recipients (Zarafa). diff --git a/src/messageBuilder.cpp b/src/messageBuilder.cpp index 870d59e..3597b3a 100644 --- a/src/messageBuilder.cpp +++ b/src/messageBuilder.cpp @@ -51,17 +51,15 @@ ref messageBuilder::construct() const // Generate the header fields msg->getHeader()->Subject()->setValue(m_subject); - if (m_from.isEmpty()) - throw exceptions::no_expeditor(); - - if ((m_to.isEmpty() || m_to.getAddressAt(0)->isEmpty()) && + if (((m_to.isEmpty()) || (m_to.getAddressAt(0)->isEmpty() && !m_to.getAddressAt(0)->isGroup())) && (m_cc.isEmpty() || m_cc.getAddressAt(0)->isEmpty()) && (m_bcc.isEmpty() || m_bcc.getAddressAt(0)->isEmpty())) { throw exceptions::no_recipient(); } - msg->getHeader()->From()->setValue(m_from); + if (!m_from.isEmpty()) + msg->getHeader()->From()->setValue(m_from); if (!m_to.isEmpty()) msg->getHeader()->To()->setValue(m_to); -- 1.7.10.4 From 583e25bcdee132e53e0792cd8f0d8e535cabb743 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Sun, 19 Jun 2011 18:49:55 +0000 Subject: [PATCH 17/42] Added support for mailboxes that specify an (encoded) full name with an empty email address, set by a <> marker (Zarafa). diff --git a/src/mailbox.cpp b/src/mailbox.cpp index 5cb0139..fea7479 100644 --- a/src/mailbox.cpp +++ b/src/mailbox.cpp @@ -88,6 +88,7 @@ void mailbox::parse(const string& buffer, const string::size_type position, // Temporary buffers for extracted name and address string name; string address; + bool hadBrackets = false; while (p < pend) { @@ -283,6 +284,7 @@ void mailbox::parse(const string& buffer, const string::size_type position, } else if (*p == '>') { + hadBrackets = true; break; } else if (!parserHelpers::isSpace(*p)) @@ -309,7 +311,7 @@ void mailbox::parse(const string& buffer, const string::size_type position, // Swap name and address when no address was found // (email address is mandatory, whereas name is optional). - if (address.empty() && !name.empty()) + if (address.empty() && !name.empty() && !hadBrackets) { m_email.clear(); m_email.reserve(name.size()); diff --git a/tests/parser/mailboxTest.cpp b/tests/parser/mailboxTest.cpp index 8411daa..9ebadca 100644 --- a/tests/parser/mailboxTest.cpp +++ b/tests/parser/mailboxTest.cpp @@ -32,6 +32,7 @@ VMIME_TEST_SUITE_BEGIN VMIME_TEST_LIST_BEGIN VMIME_TEST(testParse) + VMIME_TEST(testEmptyEmailAddress) VMIME_TEST_LIST_END @@ -113,5 +114,19 @@ VMIME_TEST_SUITE_BEGIN } } + void testEmptyEmailAddress() + { + vmime::addressList addrList; + addrList.parse("\"Full Name\" <>"); + + VASSERT_EQ("count", 1, addrList.getAddressCount()); + VASSERT_EQ("!group", false, addrList.getAddressAt(0)->isGroup()); + + vmime::ref mbox = addrList.getAddressAt(0).dynamicCast (); + + VASSERT_EQ("name", "Full Name", mbox->getName()); + VASSERT_EQ("email", "", mbox->getEmail()); + } + VMIME_TEST_SUITE_END -- 1.7.10.4 From 461b92f84d5c16b297d33610fcd89fc7ca5a161a Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Fri, 24 Jun 2011 15:46:23 +0000 Subject: [PATCH 18/42] Added missing libs in pkg-config file. diff --git a/SConstruct b/SConstruct index 177f5b4..37c0ac6 100644 --- a/SConstruct +++ b/SConstruct @@ -1089,7 +1089,7 @@ def generateAutotools(target, source, env): vmime_pc_in.write("Description: " + packageDescription + "\n") vmime_pc_in.write("Version: @VERSION@\n") vmime_pc_in.write("Requires: @GSASL_REQUIRED@\n") - vmime_pc_in.write("Libs: -L${libdir} -l@GENERIC_VERSIONED_LIBRARY_NAME@ @GSASL_LIBS@ @LIBGNUTLS_LIBS@ @LIBICONV@ @PTHREAD_LIBS@ @VMIME_ADDITIONAL_PC_LIBS@\n") + vmime_pc_in.write("Libs: -L${libdir} -l@GENERIC_VERSIONED_LIBRARY_NAME@ @GSASL_LIBS@ @LIBGNUTLS_LIBS@ @LIBICONV@ @PTHREAD_LIBS@ @LIBICONV@ @PTHREAD_LIBS@ @VMIME_ADDITIONAL_PC_LIBS@\n") #vmime_pc_in.write("Cflags: -I${includedir}/@GENERIC_VERSIONED_LIBRARY_NAME@\n") vmime_pc_in.write("Cflags: -I${includedir}/ @LIBGNUTLS_CFLAGS@\n") vmime_pc_in.close() -- 1.7.10.4 From 2b2c0abd02a17ccff7d49e266b9854f4ea47f8e4 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Sat, 25 Jun 2011 17:07:53 +0000 Subject: [PATCH 19/42] Fixed parsing of empty body parts (thanks to John van der Kamp, from Zarafa). diff --git a/src/body.cpp b/src/body.cpp index 8596833..9d7d57f 100644 --- a/src/body.cpp +++ b/src/body.cpp @@ -197,6 +197,11 @@ void body::parse(const string& buffer, const string::size_type position, { ref part = vmime::create (); + // End before start may happen on empty bodyparts (directly + // successive boundaries without even a line-break) + if (partEnd < partStart) + std::swap(partStart, partEnd); + part->parse(buffer, partStart, partEnd, NULL); part->m_parent = m_part; diff --git a/tests/parser/bodyPartTest.cpp b/tests/parser/bodyPartTest.cpp index b129913..075b8f9 100644 --- a/tests/parser/bodyPartTest.cpp +++ b/tests/parser/bodyPartTest.cpp @@ -36,6 +36,7 @@ VMIME_TEST_SUITE_BEGIN VMIME_TEST(testParseMissingLastBoundary) VMIME_TEST(testPrologEpilog) VMIME_TEST(testPrologEncoding) + VMIME_TEST(testSuccessiveBoundaries) VMIME_TEST_LIST_END @@ -181,5 +182,23 @@ VMIME_TEST_SUITE_BEGIN VASSERT_EQ("epilog", "Epilog text", msg->getBody()->getEpilogText()); } + void testSuccessiveBoundaries() + { + vmime::string str = + "Content-Type: multipart/mixed; boundary=\"MY-BOUNDARY\"" + "\r\n\r\n" + "--MY-BOUNDARY\r\nHEADER1\r\n\r\nBODY1\r\n" + "--MY-BOUNDARY\r\n" + "--MY-BOUNDARY--\r\n"; + + 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", "", extractContents(p.getBody()->getPartAt(1)->getBody()->getContents())); + } + VMIME_TEST_SUITE_END -- 1.7.10.4 From 2648d744da0e2e744c7959999ac513c3016072b4 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Sun, 26 Jun 2011 08:19:11 +0000 Subject: [PATCH 20/42] Use gnutls_priority_set_direct() instead of GNUTLS deprecated functions. diff --git a/SConstruct b/SConstruct index 37c0ac6..01ad3f3 100644 --- a/SConstruct +++ b/SConstruct @@ -816,6 +816,7 @@ else: config_hpp.write('// -- TLS/SSL support\n') if env['with_tls'] == 'yes': config_hpp.write('#define VMIME_HAVE_TLS_SUPPORT 1\n') + config_hpp.write('#define HAVE_GNUTLS_PRIORITY_FUNCS 1\n') else: config_hpp.write('#define VMIME_HAVE_TLS_SUPPORT 0\n') @@ -1626,11 +1627,42 @@ if test "x$conf_tls" = "xyes"; then else AC_MSG_ERROR(can't find an usable version of GNU TLS library) fi + + # -- check for gnutls_priority_set_direct() function + if test "x$have_gnutls" = "xyes"; then + AC_MSG_CHECKING(for gnutls_priority_set_direct) + + LIBS_save="$LIBS" + LIBS="$LIBS $LIBGNUTLS_LIBS" + CPPFLAGS_save="$CPPFLAGS" + CPPFLAGS="$CPPFLAGS $LIBGNUTLS_CFLAGS" + + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], + [gnutls_session s; gnutls_priority_set_direct(s, NULL, NULL);])], + [have_gnutls_priority_funcs=yes], + [have_gnutls_priority_funcs=no]) + + CPPFLAGS="$CPPFLAGS_save" + LIBS="$LIBS_save" + + AC_MSG_RESULT([$have_gnutls_priority_funcs]) + + if test "x$have_gnutls_priority_funcs" = "xyes"; then + AM_CONDITIONAL(HAVE_GNUTLS_PRIORITY_FUNCS, true) + HAVE_GNUTLS_PRIORITY_FUNCS=1 + else + AM_CONDITIONAL(HAVE_GNUTLS_PRIORITY_FUNCS, false) + HAVE_GNUTLS_PRIORITY_FUNCS=0 + fi + fi else AM_CONDITIONAL(VMIME_HAVE_TLS_SUPPORT, false) VMIME_HAVE_TLS_SUPPORT=0 fi +AC_SUBST(LIBGNUTLS_CFLAGS) +AC_SUBST(LIBGNUTLS_LIBS) + # ** platform handlers VMIME_BUILTIN_PLATFORMS='' @@ -1919,6 +1951,7 @@ typedef unsigned ${VMIME_TYPE_INT32} vmime_uint32; #define VMIME_HAVE_SASL_SUPPORT ${VMIME_HAVE_SASL_SUPPORT} // -- TLS support #define VMIME_HAVE_TLS_SUPPORT ${VMIME_HAVE_TLS_SUPPORT} +#define HAVE_GNUTLS_PRIORITY_FUNCS ${HAVE_GNUTLS_PRIORITY_FUNCS} // -- Messaging support #define VMIME_HAVE_MESSAGING_FEATURES ${VMIME_HAVE_MESSAGING_FEATURES} """) diff --git a/src/net/tls/TLSSession.cpp b/src/net/tls/TLSSession.cpp index 010c007..af73a05 100644 --- a/src/net/tls/TLSSession.cpp +++ b/src/net/tls/TLSSession.cpp @@ -123,6 +123,21 @@ TLSSession::TLSSession(ref cv) // Sets some default priority on the ciphers, key exchange methods, // macs and compression methods. +#if HAVE_GNUTLS_PRIORITY_FUNCS + + if ((res = gnutls_priority_set_direct + (*m_gnutlsSession, "NORMAL:%SSL3_RECORD_VERSION", NULL)) != 0) + { + if ((res = gnutls_priority_set_direct + (*m_gnutlsSession, "NORMAL", NULL)) != 0) + { + throwTLSException + ("gnutls_priority_set_direct", res); + } + } + +#else // !HAVE_GNUTLS_PRIORITY_FUNCS + gnutls_set_default_priority(*m_gnutlsSession); // Sets the priority on the certificate types supported by gnutls. @@ -197,6 +212,8 @@ TLSSession::TLSSession(ref cv) gnutls_compression_set_priority(*m_gnutlsSession, compressionPriority); +#endif // !HAVE_GNUTLS_PRIORITY_FUNCS + // Initialize credentials gnutls_credentials_set(*m_gnutlsSession, GNUTLS_CRD_ANON, g_gnutlsGlobal.anonCred); -- 1.7.10.4 From 1060121ffd4315c3158ffc001040f4f705514e7a Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Sun, 26 Jun 2011 12:47:25 +0000 Subject: [PATCH 21/42] Fixed encoding of whitespace. Fixed old test case. diff --git a/src/text.cpp b/src/text.cpp index 2454456..66c3b35 100644 --- a/src/text.cpp +++ b/src/text.cpp @@ -320,12 +320,6 @@ void text::createFromString(const string& in, const charset& ch) } else { - if (count) - { - ref w = getWordAt(getWordCount() - 1); - w->getBuffer() += ' '; - } - appendWord(vmime::create (chunk, charset(charsets::US_ASCII))); diff --git a/tests/parser/textTest.cpp b/tests/parser/textTest.cpp index 746ac94..43ec836 100644 --- a/tests/parser/textTest.cpp +++ b/tests/parser/textTest.cpp @@ -53,6 +53,8 @@ VMIME_TEST_SUITE_BEGIN VMIME_TEST(testFoldingAscii) VMIME_TEST(testForcedNonEncoding) + + VMIME_TEST(testBugFix20110511) VMIME_TEST_LIST_END @@ -149,7 +151,7 @@ VMIME_TEST_SUITE_BEGIN VASSERT_EQ("2.1", 3, t2.getWordCount()); VASSERT_EQ("2.2", "some ASCII characters and special chars: ", t2.getWordAt(0)->getBuffer()); VASSERT_EQ("2.3", vmime::charset(vmime::charsets::US_ASCII), t2.getWordAt(0)->getCharset()); - VASSERT_EQ("2.4", "\xf1\xf2\xf3\xf4 ", t2.getWordAt(1)->getBuffer()); + VASSERT_EQ("2.4", "\xf1\xf2\xf3\xf4", t2.getWordAt(1)->getBuffer()); VASSERT_EQ("2.5", c2, t2.getWordAt(1)->getCharset()); VASSERT_EQ("2.6", "and then more ASCII chars.", t2.getWordAt(2)->getBuffer()); VASSERT_EQ("2.7", vmime::charset(vmime::charsets::US_ASCII), t2.getWordAt(2)->getCharset()); @@ -453,5 +455,43 @@ VMIME_TEST_SUITE_BEGIN VASSERT_EQ("received.long", "from User\r\n (Ee9GMqZQ8t7IQwftfAFHd2KyScCYRrFSJ50tKEoXv2bVCG4HcPU80GGWiFabAvG77FekpGgF1h@[127.0.0.1])\r\n by servername.hostname.com with esmtp id 1NGTS9-2C0sqG0; Fri, 4 Dec 2009\r\n 09:23:49 +0100", r.generate(78)); } + void testBugFix20110511() + { + /* + + Using the latest version of vmime (0.9.1), encoding the following string: Jean + Gwenaël Dutourd will result in: + Jean =?utf-8?Q?Gwena=C3=ABl_?= Dutourd + However, decoding this will result in Jean Gwenaël Dutourd (notice two spaces + between the last 2 words). The encoder adds a _ after the second word, but + since the last word is not encoded, the space between them is not ignored, and + is decoded into an additional space. + + See: http://sourceforge.net/projects/vmime/forums/forum/237357/topic/4531365 + + */ + + const std::string DECODED_TEXT = "Jean Gwenaël Dutourd"; + const std::string ENCODED_TEXT = "Jean =?utf-8?Q?Gwena=C3=ABl?= Dutourd"; + + // Encode + VASSERT_EQ("encode", ENCODED_TEXT, + vmime::text::newFromString(DECODED_TEXT, vmime::charset("utf-8"))->generate()); + + // Decode + vmime::text t; + t.parse(ENCODED_TEXT); + + // -- words + std::ostringstream oss; oss << t; + VASSERT_EQ("decode1", + "[text: [[word: charset=us-ascii, buffer=Jean ]," + "[word: charset=utf-8, buffer=Gwenaël]," + "[word: charset=us-ascii, buffer= Dutourd]]]", oss.str()); + + // -- getWholeBuffer + VASSERT_EQ("decode2", DECODED_TEXT, t.getWholeBuffer()); + } + VMIME_TEST_SUITE_END -- 1.7.10.4 From dc6dc039fc0edccf4630894fa6ed8cd4bf3bb3ce Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Sat, 20 Aug 2011 06:35:06 +0000 Subject: [PATCH 22/42] Use gnutls_strerror() for reporting errors. diff --git a/src/net/tls/TLSSession.cpp b/src/net/tls/TLSSession.cpp index af73a05..7426a73 100644 --- a/src/net/tls/TLSSession.cpp +++ b/src/net/tls/TLSSession.cpp @@ -41,6 +41,9 @@ //#define GNUTLS_DEBUG 1 +#include +#include + #if VMIME_DEBUG && GNUTLS_DEBUG #include #endif // VMIME_DEBUG && GNUTLS_DEBUG @@ -257,119 +260,14 @@ ref TLSSession::getCertificateVerifier() void TLSSession::throwTLSException(const string& fname, const int code) { - string msg = fname + "() returned "; - -#define ERROR(x) \ - case x: msg += #x; break; - - switch (code) - { - ERROR(GNUTLS_E_SUCCESS) - ERROR(GNUTLS_E_UNKNOWN_COMPRESSION_ALGORITHM) - ERROR(GNUTLS_E_UNKNOWN_CIPHER_TYPE) - ERROR(GNUTLS_E_LARGE_PACKET) - ERROR(GNUTLS_E_UNSUPPORTED_VERSION_PACKET) - ERROR(GNUTLS_E_UNEXPECTED_PACKET_LENGTH) - ERROR(GNUTLS_E_INVALID_SESSION) - ERROR(GNUTLS_E_FATAL_ALERT_RECEIVED) - ERROR(GNUTLS_E_UNEXPECTED_PACKET) - ERROR(GNUTLS_E_WARNING_ALERT_RECEIVED) - ERROR(GNUTLS_E_ERROR_IN_FINISHED_PACKET) - ERROR(GNUTLS_E_UNEXPECTED_HANDSHAKE_PACKET) - ERROR(GNUTLS_E_UNKNOWN_CIPHER_SUITE) - ERROR(GNUTLS_E_UNWANTED_ALGORITHM) - ERROR(GNUTLS_E_MPI_SCAN_FAILED) - ERROR(GNUTLS_E_DECRYPTION_FAILED) - ERROR(GNUTLS_E_MEMORY_ERROR) - ERROR(GNUTLS_E_DECOMPRESSION_FAILED) - ERROR(GNUTLS_E_COMPRESSION_FAILED) - ERROR(GNUTLS_E_AGAIN) - ERROR(GNUTLS_E_EXPIRED) - ERROR(GNUTLS_E_DB_ERROR) - ERROR(GNUTLS_E_SRP_PWD_ERROR) - ERROR(GNUTLS_E_INSUFFICIENT_CREDENTIALS) - ERROR(GNUTLS_E_HASH_FAILED) - ERROR(GNUTLS_E_BASE64_DECODING_ERROR) - ERROR(GNUTLS_E_MPI_PRINT_FAILED) - ERROR(GNUTLS_E_REHANDSHAKE) - ERROR(GNUTLS_E_GOT_APPLICATION_DATA) - ERROR(GNUTLS_E_RECORD_LIMIT_REACHED) - ERROR(GNUTLS_E_ENCRYPTION_FAILED) - ERROR(GNUTLS_E_PK_ENCRYPTION_FAILED) - ERROR(GNUTLS_E_PK_DECRYPTION_FAILED) - ERROR(GNUTLS_E_PK_SIGN_FAILED) - ERROR(GNUTLS_E_X509_UNSUPPORTED_CRITICAL_EXTENSION) - ERROR(GNUTLS_E_KEY_USAGE_VIOLATION) - ERROR(GNUTLS_E_NO_CERTIFICATE_FOUND) - ERROR(GNUTLS_E_INVALID_REQUEST) - ERROR(GNUTLS_E_SHORT_MEMORY_BUFFER) - ERROR(GNUTLS_E_INTERRUPTED) - ERROR(GNUTLS_E_PUSH_ERROR) - ERROR(GNUTLS_E_PULL_ERROR) - ERROR(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER) - ERROR(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) - ERROR(GNUTLS_E_PKCS1_WRONG_PAD) - ERROR(GNUTLS_E_RECEIVED_ILLEGAL_EXTENSION) - ERROR(GNUTLS_E_INTERNAL_ERROR) - ERROR(GNUTLS_E_DH_PRIME_UNACCEPTABLE) - ERROR(GNUTLS_E_FILE_ERROR) - ERROR(GNUTLS_E_TOO_MANY_EMPTY_PACKETS) - ERROR(GNUTLS_E_UNKNOWN_PK_ALGORITHM) - ERROR(GNUTLS_E_INIT_LIBEXTRA) - ERROR(GNUTLS_E_LIBRARY_VERSION_MISMATCH) - ERROR(GNUTLS_E_NO_TEMPORARY_RSA_PARAMS) - ERROR(GNUTLS_E_LZO_INIT_FAILED) - ERROR(GNUTLS_E_NO_COMPRESSION_ALGORITHMS) - ERROR(GNUTLS_E_NO_CIPHER_SUITES) - ERROR(GNUTLS_E_OPENPGP_GETKEY_FAILED) - ERROR(GNUTLS_E_PK_SIG_VERIFY_FAILED) - ERROR(GNUTLS_E_ILLEGAL_SRP_USERNAME) - ERROR(GNUTLS_E_SRP_PWD_PARSING_ERROR) - ERROR(GNUTLS_E_NO_TEMPORARY_DH_PARAMS) - ERROR(GNUTLS_E_ASN1_ELEMENT_NOT_FOUND) - ERROR(GNUTLS_E_ASN1_IDENTIFIER_NOT_FOUND) - ERROR(GNUTLS_E_ASN1_DER_ERROR) - ERROR(GNUTLS_E_ASN1_VALUE_NOT_FOUND) - ERROR(GNUTLS_E_ASN1_GENERIC_ERROR) - ERROR(GNUTLS_E_ASN1_VALUE_NOT_VALID) - ERROR(GNUTLS_E_ASN1_TAG_ERROR) - ERROR(GNUTLS_E_ASN1_TAG_IMPLICIT) - ERROR(GNUTLS_E_ASN1_TYPE_ANY_ERROR) - ERROR(GNUTLS_E_ASN1_SYNTAX_ERROR) - ERROR(GNUTLS_E_ASN1_DER_OVERFLOW) - //ERROR(GNUTLS_E_OPENPGP_TRUSTDB_VERSION_UNSUPPORTED) - ERROR(GNUTLS_E_OPENPGP_UID_REVOKED) - ERROR(GNUTLS_E_CERTIFICATE_ERROR) - //ERROR(GNUTLS_E_X509_CERTIFICATE_ERROR) - ERROR(GNUTLS_E_CERTIFICATE_KEY_MISMATCH) - ERROR(GNUTLS_E_UNSUPPORTED_CERTIFICATE_TYPE) - ERROR(GNUTLS_E_X509_UNKNOWN_SAN) - ERROR(GNUTLS_E_OPENPGP_FINGERPRINT_UNSUPPORTED) - ERROR(GNUTLS_E_X509_UNSUPPORTED_ATTRIBUTE) - ERROR(GNUTLS_E_UNKNOWN_HASH_ALGORITHM) - ERROR(GNUTLS_E_UNKNOWN_PKCS_CONTENT_TYPE) - ERROR(GNUTLS_E_UNKNOWN_PKCS_BAG_TYPE) - ERROR(GNUTLS_E_INVALID_PASSWORD) - ERROR(GNUTLS_E_MAC_VERIFY_FAILED) - ERROR(GNUTLS_E_CONSTRAINT_ERROR) - ERROR(GNUTLS_E_BASE64_ENCODING_ERROR) - ERROR(GNUTLS_E_INCOMPATIBLE_GCRYPT_LIBRARY) - //ERROR(GNUTLS_E_INCOMPATIBLE_CRYPTO_LIBRARY) - ERROR(GNUTLS_E_INCOMPATIBLE_LIBTASN1_LIBRARY) - ERROR(GNUTLS_E_OPENPGP_KEYRING_ERROR) - ERROR(GNUTLS_E_X509_UNSUPPORTED_OID) - //ERROR(GNUTLS_E_RANDOM_FAILED) - ERROR(GNUTLS_E_UNIMPLEMENTED_FEATURE) - - default: - - msg += "unknown error"; - break; - } + std::ostringstream msg; -#undef ERROR + msg << fname + "() returned code "; + msg << std::hex << code; + msg << ": "; + msg << gnutls_strerror(code); - throw exceptions::tls_exception(msg); + throw exceptions::tls_exception(msg.str()); } -- 1.7.10.4 From 7ea6fc3737ef36407e1c90f3aa05f89a39bdefb7 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Sun, 21 Aug 2011 08:55:46 +0000 Subject: [PATCH 23/42] Removed dependency on gcrypt for gnutls version >= 2.12. diff --git a/src/net/tls/TLSSession.cpp b/src/net/tls/TLSSession.cpp index 7426a73..d3f6d49 100644 --- a/src/net/tls/TLSSession.cpp +++ b/src/net/tls/TLSSession.cpp @@ -26,9 +26,17 @@ #include "vmime/config.hpp" +// Dependency on gcrypt is not needed since GNU TLS version 2.12. +// See here: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=638651 +#if GNUTLS_VERSION_NUMBER <= 0x020b00 +# define VMIME_GNUTLS_NEEDS_GCRYPT 1 +#endif + #if VMIME_HAVE_PTHREAD # include -# include +# if VMIME_GNUTLS_NEEDS_GCRYPT +# include +# endif # include #endif // VMIME_HAVE_PTHREAD @@ -49,7 +57,7 @@ #endif // VMIME_DEBUG && GNUTLS_DEBUG -#if VMIME_HAVE_PTHREAD && defined(GCRY_THREAD_OPTION_PTHREAD_IMPL) +#if VMIME_HAVE_PTHREAD && VMIME_GNUTLS_NEEDS_GCRYPT && defined(GCRY_THREAD_OPTION_PTHREAD_IMPL) extern "C" { GCRY_THREAD_OPTION_PTHREAD_IMPL; @@ -70,7 +78,9 @@ struct TLSGlobal TLSGlobal() { #if VMIME_HAVE_PTHREAD && defined(GCRY_THREAD_OPTION_PTHREAD_IMPL) + #if VMIME_GNUTLS_NEEDS_GCRYPT gcry_control(GCRYCTL_SET_THREAD_CBS, &gcry_threads_pthread); + #endif // VMIME_GNUTLS_NEEDS_GCRYPT #endif // VMIME_HAVE_PTHREAD && defined(GCRY_THREAD_OPTION_PTHREAD_IMPL gnutls_global_init(); -- 1.7.10.4 From f21c55be642b166a2f0518ace2b179bed3916b23 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Sun, 21 Aug 2011 09:04:46 +0000 Subject: [PATCH 24/42] Fixed HAVE_GNUTLS_PRIORITY_FUNCS never defined when configured with no TLS support. diff --git a/SConstruct b/SConstruct index 01ad3f3..11e884b 100644 --- a/SConstruct +++ b/SConstruct @@ -1654,10 +1654,16 @@ if test "x$conf_tls" = "xyes"; then AM_CONDITIONAL(HAVE_GNUTLS_PRIORITY_FUNCS, false) HAVE_GNUTLS_PRIORITY_FUNCS=0 fi + else + AM_CONDITIONAL(HAVE_GNUTLS_PRIORITY_FUNCS, false) + HAVE_GNUTLS_PRIORITY_FUNCS=0 fi else AM_CONDITIONAL(VMIME_HAVE_TLS_SUPPORT, false) VMIME_HAVE_TLS_SUPPORT=0 + + AM_CONDITIONAL(HAVE_GNUTLS_PRIORITY_FUNCS, false) + HAVE_GNUTLS_PRIORITY_FUNCS=0 fi AC_SUBST(LIBGNUTLS_CFLAGS) -- 1.7.10.4 From d4e66226a696745adafa1767210580f8fbb7ae00 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Tue, 15 Nov 2011 11:40:42 +0000 Subject: [PATCH 25/42] GNU TLS 3 has no 'extra' (thanks to mabrand). diff --git a/src/net/tls/TLSSession.cpp b/src/net/tls/TLSSession.cpp index d3f6d49..cb50acc 100644 --- a/src/net/tls/TLSSession.cpp +++ b/src/net/tls/TLSSession.cpp @@ -22,7 +22,9 @@ // #include +#if GNUTLS_VERSION_NUMBER < 0x030000 #include +#endif #include "vmime/config.hpp" -- 1.7.10.4 From bacbe512e406d22f6acc83597fcdfc2d624cf82b Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Tue, 15 Nov 2011 11:46:07 +0000 Subject: [PATCH 26/42] Set Diffie-Hellman prime size (bug SF#3434852). diff --git a/src/net/tls/TLSSession.cpp b/src/net/tls/TLSSession.cpp index cb50acc..0606808 100644 --- a/src/net/tls/TLSSession.cpp +++ b/src/net/tls/TLSSession.cpp @@ -139,6 +139,7 @@ TLSSession::TLSSession(ref cv) // Sets some default priority on the ciphers, key exchange methods, // macs and compression methods. #if HAVE_GNUTLS_PRIORITY_FUNCS + gnutls_dh_set_prime_bits(*m_gnutlsSession, 128); if ((res = gnutls_priority_set_direct (*m_gnutlsSession, "NORMAL:%SSL3_RECORD_VERSION", NULL)) != 0) -- 1.7.10.4 From 6574b60a303c5d864e840aa23959656bb2803485 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Thu, 22 Dec 2011 08:51:28 +0000 Subject: [PATCH 27/42] Updated coding conventions. diff --git a/HACKING b/HACKING index 4f35a53..f51d738 100644 --- a/HACKING +++ b/HACKING @@ -1,10 +1,10 @@ -This file contains coding guidelines for VMime. You should follow these -guidelines if you want to contribute to VMime. It guarantees some minimal -quality of the code. +This file contains coding guidelines for VMime. You should follow them +if you want to contribute to VMime. The rules below are not guidelines +or recommendations, but strict rules. -1. General guidelines +1. General rules 1.1. Language 1.2. Unit tests 1.3. CVS @@ -18,19 +18,22 @@ quality of the code. 2.5. Line length 2.6. Spaces and parentheses 2.7. End-of-line character + 2.8. Short functions + 2.9. Limit Variable Scope 3. Naming conventions 3.1. Classes 3.2. Variables/parameters/member variables 3.3. Member variables 3.4. Files 3.5. Namespaces + 3.6. Constants 4. Comments 5. Miscellaneous -1. General guidelines -===================== +1. General rules +================ 1.1. Language ------------- @@ -50,7 +53,7 @@ When you fix a bug, also add a new test case to ensure the bug will not happen anymore. -1.3. CVS +1.3. SVN -------- Each commit MUST be done with a message ('-m' flag) that briefly describes what @@ -154,7 +157,11 @@ Except when body spans over multiple lines: 2.5. Line length ---------------- -Line length should not exceed 80 characters. +Each line of text should not exceed 80 characters. + +Exception: if a comment line contains an example command or a literal URL +longer than 100 characters, that line may be longer than 100 characters +for ease of cut and paste. 2.6. Spaces and parentheses @@ -193,6 +200,30 @@ Configure your editor to use "\n" (UNIX convention) for end-of-line sequence, and not "\r\n" (Windows), nor "\n\r", nor any other combination. +2.8. Short functions +-------------------- + +To the extent that it is feasible, functions should be kept small and focused. +It is, however, recognized that long functions are sometimes appropriate, so no +hard limit is placed on method length. If a function exceeds 40 lines or so, +think about whether it can be broken up without harming the structure of the +program. + + +2.9. Limit Variable Scope +------------------------- + +The scope of local variables should be kept to a minimum. By doing so, you +increase the readability and maintainability of your code and reduce the +likelihood of error. Each variable should be declared in the innermost block +that encloses all uses of the variable. + +Local variables should be declared at the point they are first used. Nearly +every local variable declaration should contain an initializer. If you don't +yet have enough information to initialize a variable sensibly, you should +postpone the declaration until you do. + + 3. Naming conventions ===================== @@ -255,6 +286,12 @@ Implementation files must be placed in 'src/' directory. Namespaces are named exactly like variables. +3.6. Constants +-------------- + +Constants are ALL_CAPS_WITH_UNDERSCORES. + + 4. Comments =========== -- 1.7.10.4 From 130e5223dea0af2f8d9d01cca7845be4e1a08d13 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Thu, 5 Apr 2012 11:46:39 +0200 Subject: [PATCH 28/42] Added function to retrieve sequence numbers of messages whose UID is greater or equal than a specified UID (thanks to Zahi Mashael). diff --git a/src/net/imap/IMAPFolder.cpp b/src/net/imap/IMAPFolder.cpp index 0122d21..50a2f2b 100644 --- a/src/net/imap/IMAPFolder.cpp +++ b/src/net/imap/IMAPFolder.cpp @@ -1772,6 +1772,62 @@ void IMAPFolder::status(int& count, int& unseen) } +std::vector IMAPFolder::getMessageNumbersStartingOnUID(const message::uid& uid) +{ + std::vector v; + + std::ostringstream command; + command.imbue(std::locale::classic()); + + command << "SEARCH UID " << uid; + + // Send the request + m_connection->send(true, command.str(), true); + + // Get the response + utility::auto_ptr resp(m_connection->readResponse()); + + if (resp->isBad() || + resp->response_done()->response_tagged()->resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("SEARCH", + m_connection->getParser()->lastLine(), "bad response"); + } + + const std::vector & respDataList = resp->continue_req_or_response_data(); + + for (std::vector ::const_iterator + it = respDataList.begin() ; it != respDataList.end() ; ++it) + { + if ((*it)->response_data() == NULL) + { + throw exceptions::command_error("SEARCH", + m_connection->getParser()->lastLine(), "invalid response"); + } + + const IMAPParser::mailbox_data* mailboxData = + (*it)->response_data()->mailbox_data(); + + // We are only interested in responses of type "SEARCH" + if (mailboxData == NULL || + mailboxData->type() != IMAPParser::mailbox_data::SEARCH) + { + continue; + } + + for (std::vector ::const_iterator + it = mailboxData->search_nz_number_list().begin() ; + it != mailboxData->search_nz_number_list().end(); + ++it) + { + v.push_back((*it)->value()); + } + } + + return v; +} + + } // imap } // net } // vmime diff --git a/src/net/maildir/maildirFolder.cpp b/src/net/maildir/maildirFolder.cpp index dd680c9..d11ae3b 100644 --- a/src/net/maildir/maildirFolder.cpp +++ b/src/net/maildir/maildirFolder.cpp @@ -1363,6 +1363,12 @@ const utility::file::path maildirFolder::getMessageFSPath(const int number) cons } +std::vector maildirFolder::getMessageNumbersStartingOnUID(const message::uid& /* uid */) +{ + throw exceptions::operation_not_supported(); +} + + } // maildir } // net } // vmime diff --git a/src/net/pop3/POP3Folder.cpp b/src/net/pop3/POP3Folder.cpp index d5fc687..e085609 100644 --- a/src/net/pop3/POP3Folder.cpp +++ b/src/net/pop3/POP3Folder.cpp @@ -843,6 +843,12 @@ void POP3Folder::expunge() } +std::vector POP3Folder::getMessageNumbersStartingOnUID(const message::uid& /* uid */) +{ + throw exceptions::operation_not_supported(); +} + + } // pop3 } // net } // vmime diff --git a/vmime/net/folder.hpp b/vmime/net/folder.hpp index b20e9c9..df9cbaf 100644 --- a/vmime/net/folder.hpp +++ b/vmime/net/folder.hpp @@ -383,6 +383,13 @@ public: */ virtual int getFetchCapabilities() const = 0; + /** Return the sequence numbers of messages whose UID equal or greater than uid + * + * @param uid the uid of the first message + * @throw net_exception if an error occurs + */ + virtual std::vector getMessageNumbersStartingOnUID(const message::uid& uid) = 0; + // Event listeners void addMessageChangedListener(events::messageChangedListener* l); void removeMessageChangedListener(events::messageChangedListener* l); diff --git a/vmime/net/imap/IMAPFolder.hpp b/vmime/net/imap/IMAPFolder.hpp index dec3878..cc52596 100644 --- a/vmime/net/imap/IMAPFolder.hpp +++ b/vmime/net/imap/IMAPFolder.hpp @@ -120,6 +120,8 @@ public: int getFetchCapabilities() const; + std::vector getMessageNumbersStartingOnUID(const message::uid& uid); + private: void registerMessage(IMAPMessage* msg); diff --git a/vmime/net/maildir/maildirFolder.hpp b/vmime/net/maildir/maildirFolder.hpp index 7474b1a..68b5b89 100644 --- a/vmime/net/maildir/maildirFolder.hpp +++ b/vmime/net/maildir/maildirFolder.hpp @@ -121,6 +121,8 @@ public: int getFetchCapabilities() const; + std::vector getMessageNumbersStartingOnUID(const message::uid& uid); + private: void scanFolder(); diff --git a/vmime/net/pop3/POP3Folder.hpp b/vmime/net/pop3/POP3Folder.hpp index abaa8eb..c482908 100644 --- a/vmime/net/pop3/POP3Folder.hpp +++ b/vmime/net/pop3/POP3Folder.hpp @@ -119,6 +119,8 @@ public: int getFetchCapabilities() const; + std::vector getMessageNumbersStartingOnUID(const message::uid& uid); + private: void registerMessage(POP3Message* msg); -- 1.7.10.4 From 3f1a565b8b532f0d11a13d3f6d763b00c8ce625b Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Thu, 5 Apr 2012 11:55:07 +0200 Subject: [PATCH 29/42] Added .gitignore. diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..44e03a8 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +*.o +*.swp +build/ + +/libvmime.a +/vmime.pc +/vmime/config.hpp + +# SConstruct +.sconsign.dblite +/options.cache -- 1.7.10.4 From 5937bcda0fac9cb80d0cecbaa663ecdfe2839c09 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Thu, 5 Apr 2012 12:08:01 +0200 Subject: [PATCH 30/42] Added check before dereferencing. diff --git a/vmime/utility/smartPtr.hpp b/vmime/utility/smartPtr.hpp index c448632..df63685 100644 --- a/vmime/utility/smartPtr.hpp +++ b/vmime/utility/smartPtr.hpp @@ -338,7 +338,9 @@ protected: { if (m_ptr) { - m_ptr->getRefManager()->releaseStrong(); + if (m_ptr->getRefManager()) + m_ptr->getRefManager()->releaseStrong(); + m_ptr = 0; } } -- 1.7.10.4 From b0d74ce63ea9563ef4b218bce2497bd668dfad29 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Thu, 5 Apr 2012 12:34:51 +0200 Subject: [PATCH 31/42] Updated README. diff --git a/README b/README index 6921cea..7db9175 100644 --- a/README +++ b/README @@ -1,2 +1,30 @@ -TODO +VMime is a powerful C++ class library for working with RFC-822 and MIME messages +and Internet messaging services like IMAP, POP or SMTP. + +With VMime you can parse, generate and modify messages, and also connect to store +and transport services to receive or send messages over the Internet. The library +offers all the features to build a complete mail client. + +Key Features +------------ + +* it is free software! GNU GPL license (Commercial licenses available!) +* fully RFC-compliant implementation +* object-oriented and modular design +* very easy-to-use (intuitive design) +* well documented code +* very high reliability +* maximum portability + +Features Overview +----------------- + +* RFC-2822 and multipart messages +* aggregate documents and embedded objects +* 8-bit MIME and encoded word extensions +* full support for attachments +* POP3, IMAP, SMTP, maildir and sendmail +* SSL/TLS security layer and X.509 certificates (using GNU TLS) +* SASL authentication (using GNU SASL) + -- 1.7.10.4 From 350fada21a4f11c2f633a3cde1f2195efefe7e32 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Thu, 5 Apr 2012 22:10:54 +0200 Subject: [PATCH 32/42] Added test: Ensure '7bit' encoding is used when body is 7-bit only. diff --git a/tests/parser/bodyPartTest.cpp b/tests/parser/bodyPartTest.cpp index 075b8f9..e1d47a3 100644 --- a/tests/parser/bodyPartTest.cpp +++ b/tests/parser/bodyPartTest.cpp @@ -37,6 +37,7 @@ VMIME_TEST_SUITE_BEGIN VMIME_TEST(testPrologEpilog) VMIME_TEST(testPrologEncoding) VMIME_TEST(testSuccessiveBoundaries) + VMIME_TEST(testGenerate7bit) VMIME_TEST_LIST_END @@ -200,5 +201,18 @@ VMIME_TEST_SUITE_BEGIN VASSERT_EQ("part2-body", "", extractContents(p.getBody()->getPartAt(1)->getBody()->getContents())); } + /** Ensure '7bit' encoding is used when body is 7-bit only. */ + void testGenerate7bit() + { + vmime::ref p1 = vmime::create (); + p1->setText(vmime::create ("Part1 is US-ASCII only.")); + + vmime::ref msg = vmime::create (); + p1->generateIn(msg, msg); + + vmime::ref header1 = msg->getBody()->getPartAt(0)->getHeader(); + VASSERT_EQ("1", "7bit", header1->ContentTransferEncoding()->getValue()->generate()); + } + VMIME_TEST_SUITE_END -- 1.7.10.4 From 6c877ea41a2e408df61ac6f988c3bae7e0821141 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Thu, 5 Apr 2012 22:29:32 +0200 Subject: [PATCH 33/42] Added tests for Quoted-Printable encoding. diff --git a/tests/utility/encoderTest.cpp b/tests/utility/encoderTest.cpp index f2d42b6..b2d6bc8 100644 --- a/tests/utility/encoderTest.cpp +++ b/tests/utility/encoderTest.cpp @@ -33,6 +33,8 @@ VMIME_TEST_SUITE_BEGIN VMIME_TEST_LIST_BEGIN VMIME_TEST(testBase64) VMIME_TEST(testQuotedPrintable) + VMIME_TEST(testQuotedPrintable_SoftLineBreaks) + VMIME_TEST(testQuotedPrintable_CRLF) VMIME_TEST(testQuotedPrintable_RFC2047) VMIME_TEST_LIST_END @@ -288,6 +290,35 @@ VMIME_TEST_SUITE_BEGIN } } + /** Tests Soft Line Breaks (RFC-2047/6.7(5). */ + void testQuotedPrintable_SoftLineBreaks() + { + VASSERT_EQ("1", "Now's the time=\r\n" + " for all folk =\r\n" + "to come to the=\r\n" + " aid of their =\r\n" + "country.", + encode("quoted-printable", "Now's the time for all folk " + "to come to the aid of their country.", 15)); + } + + /** In text mode, ensure line breaks in QP-encoded text are represented + * by a CRLF sequence, as per RFC-2047/6.7(4). */ + void testQuotedPrintable_CRLF() + { + vmime::propertySet encProps; + + // in "text" mode + encProps["text"] = true; + VASSERT_EQ("text", "line1\r\nline2", + encode("quoted-printable", "line1\r\nline2", 80, encProps)); + + // in "binary" mode + encProps["text"] = false; + VASSERT_EQ("binary", "line1=0D=0Aline2", + encode("quoted-printable", "line1\r\nline2", 80, encProps)); + } + void testQuotedPrintable_RFC2047() { /* -- 1.7.10.4 From e88f062ab58654aee3cf45f94e8a5dd6c1256279 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Thu, 5 Apr 2012 23:15:04 +0200 Subject: [PATCH 34/42] Fixed wrong encoding of line breaks in QP-encoded text (issue #7). diff --git a/src/encoding.cpp b/src/encoding.cpp index 0919d44..b4e79db 100644 --- a/src/encoding.cpp +++ b/src/encoding.cpp @@ -34,19 +34,28 @@ namespace vmime encoding::encoding() - : m_name(encodingTypes::SEVEN_BIT) + : m_name(encodingTypes::SEVEN_BIT), + m_usage(USAGE_UNKNOWN) { } encoding::encoding(const string& name) - : m_name(utility::stringUtils::toLower(name)) + : m_name(utility::stringUtils::toLower(name)), + m_usage(USAGE_UNKNOWN) +{ +} + + +encoding::encoding(const string& name, const EncodingUsage usage) + : m_name(utility::stringUtils::toLower(name)), + m_usage(usage) { } encoding::encoding(const encoding& enc) - : headerFieldValue(), m_name(enc.m_name) + : headerFieldValue(), m_name(enc.m_name), m_usage(enc.m_usage) { } @@ -54,6 +63,8 @@ encoding::encoding(const encoding& enc) void encoding::parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition) { + m_usage = USAGE_UNKNOWN; + m_name = utility::stringUtils::toLower(utility::stringUtils::trim (utility::stringUtils::unquote(utility::stringUtils::trim (string(buffer.begin() + position, buffer.begin() + end))))); @@ -80,7 +91,14 @@ void encoding::generate(utility::outputStream& os, const string::size_type /* ma ref encoding::getEncoder() const { - return (utility::encoder::encoderFactory::getInstance()->create(generate())); + ref encoder = + utility::encoder::encoderFactory::getInstance()->create(generate()); + + // FIXME: this should not be here (move me into QP encoder instead?) + if (m_usage == USAGE_TEXT && m_name == encodingTypes::QUOTED_PRINTABLE) + encoder->getProperties()["text"] = true; + + return encoder; } @@ -94,6 +112,7 @@ encoding& encoding::operator=(const encoding& other) encoding& encoding::operator=(const string& name) { m_name = utility::stringUtils::toLower(name); + m_usage = USAGE_UNKNOWN; return (*this); } @@ -167,6 +186,8 @@ const encoding encoding::decideImpl const encoding encoding::decide (ref data, const EncodingUsage usage) { + encoding enc; + if (usage == USAGE_TEXT && data->isBuffered() && data->getLength() > 0 && data->getLength() < 32768) { @@ -177,12 +198,16 @@ const encoding encoding::decide data->extract(os); os.flush(); - return decideImpl(buffer.begin(), buffer.end()); + enc = decideImpl(buffer.begin(), buffer.end()); } else { - return encoding(encodingTypes::BASE64); + enc = encoding(encodingTypes::BASE64); } + + enc.setUsage(usage); + + return enc; } @@ -194,7 +219,10 @@ const encoding encoding::decide(ref data, encoding recEncoding; if (chset.getRecommendedEncoding(recEncoding)) + { + recEncoding.setUsage(usage); return recEncoding; + } } return decide(data, usage); @@ -227,6 +255,18 @@ void encoding::setName(const string& name) } +encoding::EncodingUsage encoding::getUsage() const +{ + return m_usage; +} + + +void encoding::setUsage(const EncodingUsage usage) +{ + m_usage = usage; +} + + const std::vector > encoding::getChildComponents() const { return std::vector >(); diff --git a/src/utility/encoder/qpEncoder.cpp b/src/utility/encoder/qpEncoder.cpp index aa95022..ab8db2e 100644 --- a/src/utility/encoder/qpEncoder.cpp +++ b/src/utility/encoder/qpEncoder.cpp @@ -292,14 +292,15 @@ utility::stream::size_type qpEncoder::encode(utility::inputStream& in, case 13: // CR case 10: // LF { - // Text mode (where using CRLF or LF or ... does not - // care for a new line...) - if (text) + // RFC-2045/6.7(4) + + // Text data + if (text && !rfc2047) { outBuffer[outBufferPos++] = c; ++curCol; } - // Binary mode (where CR and LF bytes are important!) + // Binary data else { QP_ENCODE_HEX(c); diff --git a/tests/parser/bodyPartTest.cpp b/tests/parser/bodyPartTest.cpp index e1d47a3..9d51262 100644 --- a/tests/parser/bodyPartTest.cpp +++ b/tests/parser/bodyPartTest.cpp @@ -38,6 +38,7 @@ VMIME_TEST_SUITE_BEGIN VMIME_TEST(testPrologEncoding) VMIME_TEST(testSuccessiveBoundaries) VMIME_TEST(testGenerate7bit) + VMIME_TEST(testTextUsageForQPEncoding) VMIME_TEST_LIST_END @@ -214,5 +215,28 @@ VMIME_TEST_SUITE_BEGIN VASSERT_EQ("1", "7bit", header1->ContentTransferEncoding()->getValue()->generate()); } + void testTextUsageForQPEncoding() + { + vmime::ref part = vmime::create (); + part->setText(vmime::create ("Part1-line1\r\nPart1-line2\r\n\x89")); + + vmime::ref msg = vmime::create (); + part->generateIn(msg, msg); + + vmime::ref body = msg->getBody()->getPartAt(0)->getBody(); + vmime::ref header = msg->getBody()->getPartAt(0)->getHeader(); + + std::ostringstream oss; + vmime::utility::outputStreamAdapter os(oss); + body->generate(os, 80); + + VASSERT_EQ("1", "quoted-printable", header->ContentTransferEncoding()->getValue()->generate()); + + // This should *NOT* be: + // Part1-line1=0D=0APart1-line2=0D=0A=89 + VASSERT_EQ("2", "Part1-line1\r\nPart1-line2\r\n=89", oss.str()); + } + + VMIME_TEST_SUITE_END diff --git a/vmime/encoding.hpp b/vmime/encoding.hpp index ba78081..42f5246 100644 --- a/vmime/encoding.hpp +++ b/vmime/encoding.hpp @@ -47,6 +47,7 @@ public: enum EncodingUsage { + USAGE_UNKNOWN, USAGE_TEXT, /**< Use for body text. */ USAGE_BINARY_DATA /**< Use for attachment, image... */ }; @@ -54,6 +55,7 @@ public: encoding(); explicit encoding(const string& name); + encoding(const string& name, const EncodingUsage usage); encoding(const encoding& enc); public: @@ -72,6 +74,19 @@ public: */ void setName(const string& name); + /** Return the type of contents this encoding is used for. + * See the EncodingUsage enum. + */ + EncodingUsage getUsage() const; + + /** Set the type of contents this encoding is used for. + * See the EncodingUsage enum. + * + * @param usage type of contents + */ + void setUsage(const EncodingUsage usage); + + encoding& operator=(const encoding& other); encoding& operator=(const string& name); @@ -113,6 +128,7 @@ public: private: string m_name; + EncodingUsage m_usage; /** Decide which encoding to use based on the specified data. * -- 1.7.10.4 From ea77bdba96588345090e3de81d9d6af116edeeb5 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Fri, 6 Apr 2012 22:26:18 +0200 Subject: [PATCH 35/42] Fixed memory leak. diff --git a/src/net/tls/TLSSocket.cpp b/src/net/tls/TLSSocket.cpp index dab0338..3cccc1e 100644 --- a/src/net/tls/TLSSocket.cpp +++ b/src/net/tls/TLSSocket.cpp @@ -50,6 +50,12 @@ TLSSocket::TLSSocket(ref session, ref sok) TLSSocket::~TLSSocket() { + if (m_ex) + { + delete m_ex; + m_ex = NULL; + } + try { disconnect(); -- 1.7.10.4 From 440d491fd6da134fcb5f19416743e8f2044556bf Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Sat, 14 Apr 2012 13:46:05 +0200 Subject: [PATCH 36/42] Split stream.hpp/.cpp into multiple source files. diff --git a/SConstruct b/SConstruct index 11e884b..ea5c4eb 100644 --- a/SConstruct +++ b/SConstruct @@ -144,6 +144,20 @@ libvmime_sources = [ 'utility/smartPtr.cpp', 'utility/smartPtr.hpp', 'utility/smartPtrInt.cpp', 'utility/smartPtrInt.hpp', 'utility/stream.cpp', 'utility/stream.hpp', + 'utility/streamUtils.cpp', 'utility/streamUtils.hpp', + 'utility/filteredStream.cpp', 'utility/filteredStream.hpp', + 'utility/inputStream.cpp', 'utility/inputStream.hpp', + 'utility/inputStreamAdapter.cpp', 'utility/inputStreamAdapter.hpp', + 'utility/inputStreamByteBufferAdapter.cpp', 'utility/inputStreamByteBufferAdapter.hpp', + 'utility/inputStreamPointerAdapter.cpp', 'utility/inputStreamPointerAdapter.hpp', + 'utility/inputStreamSocketAdapter.cpp', 'utility/inputStreamSocketAdapter.hpp', + 'utility/inputStreamStringAdapter.cpp', 'utility/inputStreamStringAdapter.hpp', + 'utility/inputStreamStringProxyAdapter.cpp', 'utility/inputStreamStringProxyAdapter.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/stringProxy.cpp', 'utility/stringProxy.hpp', 'utility/stringUtils.cpp', 'utility/stringUtils.hpp', 'utility/url.cpp', 'utility/url.hpp', diff --git a/src/charsetConverter.cpp b/src/charsetConverter.cpp index 2135788..cf75bdd 100644 --- a/src/charsetConverter.cpp +++ b/src/charsetConverter.cpp @@ -23,6 +23,8 @@ #include "vmime/charsetConverter.hpp" #include "vmime/exception.hpp" +#include "vmime/utility/inputStreamStringAdapter.hpp" +#include "vmime/utility/outputStreamStringAdapter.hpp" extern "C" diff --git a/src/component.cpp b/src/component.cpp index fbf677b..139cf66 100644 --- a/src/component.cpp +++ b/src/component.cpp @@ -23,6 +23,7 @@ #include "vmime/component.hpp" #include "vmime/base.hpp" +#include "vmime/utility/outputStreamAdapter.hpp" #include diff --git a/src/encoding.cpp b/src/encoding.cpp index b4e79db..5d99ab6 100644 --- a/src/encoding.cpp +++ b/src/encoding.cpp @@ -24,6 +24,7 @@ #include "vmime/encoding.hpp" #include "vmime/contentHandler.hpp" +#include "vmime/utility/outputStreamStringAdapter.hpp" #include "vmime/utility/encoder/encoderFactory.hpp" #include diff --git a/src/fileAttachment.cpp b/src/fileAttachment.cpp index da7c4b7..cb23cd0 100644 --- a/src/fileAttachment.cpp +++ b/src/fileAttachment.cpp @@ -28,6 +28,7 @@ #include "vmime/exception.hpp" #include "vmime/streamContentHandler.hpp" +#include "vmime/utility/inputStreamPointerAdapter.hpp" #include "vmime/contentDispositionField.hpp" diff --git a/src/generatedMessageAttachment.cpp b/src/generatedMessageAttachment.cpp index e9bd1a6..443a9d3 100644 --- a/src/generatedMessageAttachment.cpp +++ b/src/generatedMessageAttachment.cpp @@ -23,6 +23,8 @@ #include "vmime/generatedMessageAttachment.hpp" +#include "vmime/utility/outputStreamAdapter.hpp" + namespace vmime { diff --git a/src/htmlTextPart.cpp b/src/htmlTextPart.cpp index c845b57..98524af 100644 --- a/src/htmlTextPart.cpp +++ b/src/htmlTextPart.cpp @@ -31,6 +31,8 @@ #include "vmime/emptyContentHandler.hpp" #include "vmime/stringContentHandler.hpp" +#include "vmime/utility/outputStreamAdapter.hpp" + namespace vmime { diff --git a/src/mdn/MDNHelper.cpp b/src/mdn/MDNHelper.cpp index b419b85..1dd7ff3 100644 --- a/src/mdn/MDNHelper.cpp +++ b/src/mdn/MDNHelper.cpp @@ -31,6 +31,8 @@ #include "vmime/path.hpp" #include "vmime/dateTime.hpp" +#include "vmime/utility/outputStreamAdapter.hpp" + namespace vmime { namespace mdn { diff --git a/src/mdn/receivedMDNInfos.cpp b/src/mdn/receivedMDNInfos.cpp index cff211c..f97a58d 100644 --- a/src/mdn/receivedMDNInfos.cpp +++ b/src/mdn/receivedMDNInfos.cpp @@ -23,6 +23,8 @@ #include "vmime/mdn/receivedMDNInfos.hpp" +#include "vmime/utility/outputStreamAdapter.hpp" + namespace vmime { namespace mdn { diff --git a/src/message.cpp b/src/message.cpp index 6f4b046..1b4f086 100644 --- a/src/message.cpp +++ b/src/message.cpp @@ -24,6 +24,8 @@ #include "vmime/message.hpp" #include "vmime/options.hpp" +#include "vmime/utility/outputStreamAdapter.hpp" + #include diff --git a/src/net/imap/IMAPFolder.cpp b/src/net/imap/IMAPFolder.cpp index 50a2f2b..81bf386 100644 --- a/src/net/imap/IMAPFolder.cpp +++ b/src/net/imap/IMAPFolder.cpp @@ -34,6 +34,8 @@ #include "vmime/exception.hpp" #include "vmime/utility/smartPtr.hpp" +#include "vmime/utility/outputStreamAdapter.hpp" + #include #include diff --git a/src/net/imap/IMAPMessage.cpp b/src/net/imap/IMAPMessage.cpp index bc661ed..702d5f2 100644 --- a/src/net/imap/IMAPMessage.cpp +++ b/src/net/imap/IMAPMessage.cpp @@ -31,6 +31,8 @@ #include "vmime/net/imap/IMAPPart.hpp" #include "vmime/net/imap/IMAPMessagePartContentHandler.hpp" +#include "vmime/utility/outputStreamAdapter.hpp" + #include #include #include diff --git a/src/net/imap/IMAPMessagePartContentHandler.cpp b/src/net/imap/IMAPMessagePartContentHandler.cpp index 4e6ba97..85c6ec2 100644 --- a/src/net/imap/IMAPMessagePartContentHandler.cpp +++ b/src/net/imap/IMAPMessagePartContentHandler.cpp @@ -23,6 +23,9 @@ #include "vmime/net/imap/IMAPMessagePartContentHandler.hpp" +#include "vmime/utility/outputStreamAdapter.hpp" +#include "vmime/utility/inputStreamStringProxyAdapter.hpp" + namespace vmime { namespace net { diff --git a/src/net/maildir/maildirFolder.cpp b/src/net/maildir/maildirFolder.cpp index d11ae3b..8c4b275 100644 --- a/src/net/maildir/maildirFolder.cpp +++ b/src/net/maildir/maildirFolder.cpp @@ -35,6 +35,9 @@ #include "vmime/exception.hpp" #include "vmime/platform.hpp" +#include "vmime/utility/outputStreamAdapter.hpp" +#include "vmime/utility/inputStreamStringAdapter.hpp" + namespace vmime { namespace net { diff --git a/src/net/maildir/maildirMessage.cpp b/src/net/maildir/maildirMessage.cpp index 51cd1ba..4ab75e7 100644 --- a/src/net/maildir/maildirMessage.cpp +++ b/src/net/maildir/maildirMessage.cpp @@ -31,6 +31,8 @@ #include "vmime/exception.hpp" #include "vmime/platform.hpp" +#include "vmime/utility/outputStreamAdapter.hpp" + namespace vmime { namespace net { diff --git a/src/net/pop3/POP3Message.cpp b/src/net/pop3/POP3Message.cpp index 50f4f87..69ef004 100644 --- a/src/net/pop3/POP3Message.cpp +++ b/src/net/pop3/POP3Message.cpp @@ -25,6 +25,8 @@ #include "vmime/net/pop3/POP3Folder.hpp" #include "vmime/net/pop3/POP3Store.hpp" +#include "vmime/utility/outputStreamAdapter.hpp" + #include diff --git a/src/net/pop3/POP3Store.cpp b/src/net/pop3/POP3Store.cpp index 9d554c6..793112a 100644 --- a/src/net/pop3/POP3Store.cpp +++ b/src/net/pop3/POP3Store.cpp @@ -30,6 +30,7 @@ #include "vmime/security/digest/messageDigestFactory.hpp" #include "vmime/utility/filteredStream.hpp" #include "vmime/utility/stringUtils.hpp" +#include "vmime/utility/inputStreamSocketAdapter.hpp" #include "vmime/net/defaultConnectionInfos.hpp" diff --git a/src/net/sendmail/sendmailTransport.cpp b/src/net/sendmail/sendmailTransport.cpp index 53ff0d1..e7762cc 100644 --- a/src/net/sendmail/sendmailTransport.cpp +++ b/src/net/sendmail/sendmailTransport.cpp @@ -32,6 +32,8 @@ #include "vmime/utility/childProcess.hpp" #include "vmime/utility/smartPtr.hpp" +#include "vmime/utility/streamUtils.hpp" + #include "vmime/net/defaultConnectionInfos.hpp" #include "vmime/config.hpp" diff --git a/src/net/smtp/SMTPTransport.cpp b/src/net/smtp/SMTPTransport.cpp index d9fb7b8..bbbea75 100644 --- a/src/net/smtp/SMTPTransport.cpp +++ b/src/net/smtp/SMTPTransport.cpp @@ -30,6 +30,8 @@ #include "vmime/utility/filteredStream.hpp" #include "vmime/utility/stringUtils.hpp" +#include "vmime/utility/outputStreamSocketAdapter.hpp" +#include "vmime/utility/streamUtils.hpp" #include "vmime/net/defaultConnectionInfos.hpp" diff --git a/src/net/transport.cpp b/src/net/transport.cpp index dd4663d..f8ca7b7 100644 --- a/src/net/transport.cpp +++ b/src/net/transport.cpp @@ -27,6 +27,9 @@ #include "vmime/mailboxList.hpp" #include "vmime/message.hpp" +#include "vmime/utility/outputStreamAdapter.hpp" +#include "vmime/utility/inputStreamStringAdapter.hpp" + namespace vmime { namespace net { diff --git a/src/parameter.cpp b/src/parameter.cpp index d757e1b..ccbe1a5 100644 --- a/src/parameter.cpp +++ b/src/parameter.cpp @@ -27,6 +27,9 @@ #include "vmime/text.hpp" #include "vmime/encoding.hpp" +#include "vmime/utility/outputStreamAdapter.hpp" +#include "vmime/utility/outputStreamStringAdapter.hpp" + namespace vmime { diff --git a/src/parsedMessageAttachment.cpp b/src/parsedMessageAttachment.cpp index bde56aa..cb7d71d 100644 --- a/src/parsedMessageAttachment.cpp +++ b/src/parsedMessageAttachment.cpp @@ -26,6 +26,8 @@ #include "vmime/stringContentHandler.hpp" #include "vmime/contentDisposition.hpp" +#include "vmime/utility/outputStreamAdapter.hpp" + namespace vmime { diff --git a/src/security/cert/X509Certificate.cpp b/src/security/cert/X509Certificate.cpp index 1cd079c..8df4e5e 100644 --- a/src/security/cert/X509Certificate.cpp +++ b/src/security/cert/X509Certificate.cpp @@ -28,6 +28,8 @@ #include "vmime/security/cert/X509Certificate.hpp" +#include "vmime/utility/outputStreamByteArrayAdapter.hpp" + namespace vmime { namespace security { diff --git a/src/security/sasl/SASLContext.cpp b/src/security/sasl/SASLContext.cpp index 51c2bed..4bb33c1 100644 --- a/src/security/sasl/SASLContext.cpp +++ b/src/security/sasl/SASLContext.cpp @@ -33,6 +33,9 @@ #include "vmime/utility/encoder/encoderFactory.hpp" #include "vmime/utility/stream.hpp" +#include "vmime/utility/outputStreamStringAdapter.hpp" +#include "vmime/utility/inputStreamStringAdapter.hpp" +#include "vmime/utility/inputStreamByteBufferAdapter.hpp" namespace vmime { diff --git a/src/streamContentHandler.cpp b/src/streamContentHandler.cpp index 2ebd073..89a36b4 100644 --- a/src/streamContentHandler.cpp +++ b/src/streamContentHandler.cpp @@ -23,6 +23,10 @@ #include "vmime/streamContentHandler.hpp" +#include "vmime/utility/outputStreamAdapter.hpp" +#include "vmime/utility/inputStreamStringAdapter.hpp" +#include "vmime/utility/streamUtils.hpp" + namespace vmime { diff --git a/src/stringContentHandler.cpp b/src/stringContentHandler.cpp index 4e85a6c..5a1e72c 100644 --- a/src/stringContentHandler.cpp +++ b/src/stringContentHandler.cpp @@ -23,6 +23,10 @@ #include "vmime/stringContentHandler.hpp" +#include "vmime/utility/inputStreamStringAdapter.hpp" +#include "vmime/utility/inputStreamStringProxyAdapter.hpp" +#include "vmime/utility/outputStreamAdapter.hpp" + namespace vmime { diff --git a/src/utility/encoder/defaultEncoder.cpp b/src/utility/encoder/defaultEncoder.cpp index 4d0ffb5..e2d226e 100644 --- a/src/utility/encoder/defaultEncoder.cpp +++ b/src/utility/encoder/defaultEncoder.cpp @@ -23,6 +23,8 @@ #include "vmime/utility/encoder/defaultEncoder.hpp" +#include "vmime/utility/streamUtils.hpp" + namespace vmime { namespace utility { diff --git a/src/utility/inputStream.cpp b/src/utility/inputStream.cpp new file mode 100644 index 0000000..dd0adf4 --- /dev/null +++ b/src/utility/inputStream.cpp @@ -0,0 +1,33 @@ +// +// 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/inputStream.hpp" + + +namespace vmime { +namespace utility { + + +} // utility +} // vmime + diff --git a/src/utility/inputStreamAdapter.cpp b/src/utility/inputStreamAdapter.cpp new file mode 100644 index 0000000..b44b084 --- /dev/null +++ b/src/utility/inputStreamAdapter.cpp @@ -0,0 +1,70 @@ +// +// 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/inputStreamAdapter.hpp" + + +namespace vmime { +namespace utility { + + +inputStreamAdapter::inputStreamAdapter(std::istream& is) + : m_stream(is) +{ +} + + +bool inputStreamAdapter::eof() const +{ + return (m_stream.eof()); +} + + +void inputStreamAdapter::reset() +{ + m_stream.exceptions(std::ios_base::badbit); + m_stream.seekg(0, std::ios::beg); + m_stream.clear(); +} + + +stream::size_type inputStreamAdapter::read + (value_type* const data, const size_type count) +{ + m_stream.exceptions(std::ios_base::badbit); + m_stream.read(data, count); + return (m_stream.gcount()); +} + + +stream::size_type inputStreamAdapter::skip(const size_type count) +{ + m_stream.exceptions(std::ios_base::badbit); + m_stream.ignore(count); + return (m_stream.gcount()); +} + + +} // utility +} // vmime + diff --git a/src/utility/inputStreamByteBufferAdapter.cpp b/src/utility/inputStreamByteBufferAdapter.cpp new file mode 100644 index 0000000..92e779f --- /dev/null +++ b/src/utility/inputStreamByteBufferAdapter.cpp @@ -0,0 +1,90 @@ +// +// 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/inputStreamByteBufferAdapter.hpp" + + +namespace vmime { +namespace utility { + + +inputStreamByteBufferAdapter::inputStreamByteBufferAdapter(const byte_t* buffer, const size_type length) + : m_buffer(buffer), m_length(length), m_pos(0) +{ +} + + +bool inputStreamByteBufferAdapter::eof() const +{ + return m_pos >= m_length; +} + + +void inputStreamByteBufferAdapter::reset() +{ + m_pos = 0; +} + + +stream::size_type inputStreamByteBufferAdapter::read + (value_type* const data, const size_type count) +{ + const size_type remaining = m_length - m_pos; + + if (remaining < count) + { + std::copy(m_buffer + m_pos, m_buffer + m_pos + remaining, data); + m_pos += remaining; + + return remaining; + } + else + { + std::copy(m_buffer + m_pos, m_buffer + m_pos + count, data); + m_pos += count; + + return count; + } +} + + +stream::size_type inputStreamByteBufferAdapter::skip(const size_type count) +{ + const size_type remaining = m_length - m_pos; + + if (remaining < count) + { + m_pos += remaining; + return remaining; + } + else + { + m_pos += count; + return count; + } +} + + +} // utility +} // vmime + diff --git a/src/utility/inputStreamPointerAdapter.cpp b/src/utility/inputStreamPointerAdapter.cpp new file mode 100644 index 0000000..4d03e30 --- /dev/null +++ b/src/utility/inputStreamPointerAdapter.cpp @@ -0,0 +1,46 @@ +// +// 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/inputStreamPointerAdapter.hpp" + + +namespace vmime { +namespace utility { + + +inputStreamPointerAdapter::inputStreamPointerAdapter(std::istream* is, const bool own) + : inputStreamAdapter(*is), m_stream(is), m_own(own) +{ +} + + +inputStreamPointerAdapter::~inputStreamPointerAdapter() +{ + if (m_own) + delete (m_stream); +} + + +} // utility +} // vmime + diff --git a/src/utility/inputStreamSocketAdapter.cpp b/src/utility/inputStreamSocketAdapter.cpp new file mode 100644 index 0000000..b93cc3c --- /dev/null +++ b/src/utility/inputStreamSocketAdapter.cpp @@ -0,0 +1,82 @@ +// +// 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/inputStreamSocketAdapter.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +#include "vmime/net/socket.hpp" + + +namespace vmime { +namespace utility { + + +inputStreamSocketAdapter::inputStreamSocketAdapter(net::socket& sok) + : m_socket(sok) +{ +} + + +bool inputStreamSocketAdapter::eof() const +{ + // Can't know... + return false; +} + + +void inputStreamSocketAdapter::reset() +{ + // Not supported +} + + +stream::size_type inputStreamSocketAdapter::read + (value_type* const data, const size_type count) +{ + return m_socket.receiveRaw(data, count); +} + + +stream::size_type inputStreamSocketAdapter::skip + (const size_type /* count */) +{ + // Not supported + return 0; +} + + +stream::size_type inputStreamSocketAdapter::getBlockSize() +{ + return m_socket.getBlockSize(); +} + + +} // utility +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + diff --git a/src/utility/inputStreamStringAdapter.cpp b/src/utility/inputStreamStringAdapter.cpp new file mode 100644 index 0000000..31c9fda --- /dev/null +++ b/src/utility/inputStreamStringAdapter.cpp @@ -0,0 +1,94 @@ +// +// 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/inputStreamStringAdapter.hpp" + + +namespace vmime { +namespace utility { + + +inputStreamStringAdapter::inputStreamStringAdapter(const string& buffer) + : m_buffer(buffer), m_begin(0), m_end(buffer.length()), m_pos(0) +{ +} + + +inputStreamStringAdapter::inputStreamStringAdapter(const string& buffer, + const string::size_type begin, const string::size_type end) + : m_buffer(buffer), m_begin(begin), m_end(end), m_pos(begin) +{ +} + + +bool inputStreamStringAdapter::eof() const +{ + return (m_pos >= m_end); +} + + +void inputStreamStringAdapter::reset() +{ + m_pos = m_begin; +} + + +stream::size_type inputStreamStringAdapter::read + (value_type* const data, const size_type count) +{ + if (m_pos + count >= m_end) + { + const size_type remaining = m_end - m_pos; + + std::copy(m_buffer.begin() + m_pos, m_buffer.end(), data); + m_pos = m_end; + return (remaining); + } + else + { + std::copy(m_buffer.begin() + m_pos, m_buffer.begin() + m_pos + count, data); + m_pos += count; + return (count); + } +} + + +stream::size_type inputStreamStringAdapter::skip(const size_type count) +{ + if (m_pos + count >= m_end) + { + const size_type remaining = m_end - m_pos; + m_pos = m_end; + return (remaining); + } + else + { + m_pos += count; + return (count); + } +} + + +} // utility +} // vmime + diff --git a/src/utility/inputStreamStringProxyAdapter.cpp b/src/utility/inputStreamStringProxyAdapter.cpp new file mode 100644 index 0000000..5e4b60b --- /dev/null +++ b/src/utility/inputStreamStringProxyAdapter.cpp @@ -0,0 +1,89 @@ +// +// 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/inputStreamStringProxyAdapter.hpp" +#include "vmime/utility/stringProxy.hpp" + + +namespace vmime { +namespace utility { + + +inputStreamStringProxyAdapter::inputStreamStringProxyAdapter(const stringProxy& buffer) + : m_buffer(buffer), m_pos(0) +{ +} + + +bool inputStreamStringProxyAdapter::eof() const +{ + return (m_pos >= m_buffer.length()); +} + + +void inputStreamStringProxyAdapter::reset() +{ + m_pos = 0; +} + + +stream::size_type inputStreamStringProxyAdapter::read + (value_type* const data, const size_type count) +{ + const size_type remaining = m_buffer.length() - m_pos; + + if (count > remaining) + { + std::copy(m_buffer.it_begin() + m_pos, m_buffer.it_end(), data); + m_pos = m_buffer.length(); + return (remaining); + } + else + { + std::copy(m_buffer.it_begin() + m_pos, m_buffer.it_begin() + m_pos + count, data); + m_pos += count; + return (count); + } +} + + +stream::size_type inputStreamStringProxyAdapter::skip(const size_type count) +{ + const size_type remaining = m_buffer.length() - m_pos; + + if (count > remaining) + { + m_pos = m_buffer.length(); + return (remaining); + } + else + { + m_pos += count; + return (count); + } +} + + +} // utility +} // vmime + diff --git a/src/utility/outputStream.cpp b/src/utility/outputStream.cpp new file mode 100644 index 0000000..8a65db5 --- /dev/null +++ b/src/utility/outputStream.cpp @@ -0,0 +1,33 @@ +// +// 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/outputStream.hpp" + + +namespace vmime { +namespace utility { + + +} // utility +} // vmime + diff --git a/src/utility/outputStreamAdapter.cpp b/src/utility/outputStreamAdapter.cpp new file mode 100644 index 0000000..2da94f1 --- /dev/null +++ b/src/utility/outputStreamAdapter.cpp @@ -0,0 +1,54 @@ +// +// 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/outputStreamAdapter.hpp" + + +namespace vmime { +namespace utility { + + +outputStreamAdapter::outputStreamAdapter(std::ostream& os) + : m_stream(os) +{ +} + + +void outputStreamAdapter::write + (const value_type* const data, const size_type count) +{ + m_stream.exceptions(std::ios_base::badbit); + m_stream.write(data, count); +} + + +void outputStreamAdapter::flush() +{ + m_stream.exceptions(std::ios_base::badbit); + m_stream.flush(); +} + + +} // utility +} // vmime + diff --git a/src/utility/outputStreamByteArrayAdapter.cpp b/src/utility/outputStreamByteArrayAdapter.cpp new file mode 100644 index 0000000..97b27d2 --- /dev/null +++ b/src/utility/outputStreamByteArrayAdapter.cpp @@ -0,0 +1,51 @@ +// +// 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/outputStreamByteArrayAdapter.hpp" + + +namespace vmime { +namespace utility { + + +outputStreamByteArrayAdapter::outputStreamByteArrayAdapter(byteArray& array) + : m_array(array) +{ +} + + +void outputStreamByteArrayAdapter::write(const value_type* const data, const size_type count) +{ + m_array.insert(m_array.end(), data, data + count); +} + + +void outputStreamByteArrayAdapter::flush() +{ + // Do nothing +} + + +} // utility +} // vmime + diff --git a/src/utility/outputStreamSocketAdapter.cpp b/src/utility/outputStreamSocketAdapter.cpp new file mode 100644 index 0000000..d933e73 --- /dev/null +++ b/src/utility/outputStreamSocketAdapter.cpp @@ -0,0 +1,68 @@ +// +// 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/outputStreamSocketAdapter.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +#include "vmime/net/socket.hpp" + + +namespace vmime { +namespace utility { + + +outputStreamSocketAdapter::outputStreamSocketAdapter(net::socket& sok) + : m_socket(sok) +{ +} + + +void outputStreamSocketAdapter::write + (const value_type* const data, const size_type count) +{ + m_socket.sendRaw(data, count); +} + + +void outputStreamSocketAdapter::flush() +{ + // Do nothing +} + + +stream::size_type outputStreamSocketAdapter::getBlockSize() +{ + return m_socket.getBlockSize(); +} + + + +} // utility +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + diff --git a/src/utility/outputStreamStringAdapter.cpp b/src/utility/outputStreamStringAdapter.cpp new file mode 100644 index 0000000..62b2a72 --- /dev/null +++ b/src/utility/outputStreamStringAdapter.cpp @@ -0,0 +1,51 @@ +// +// 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/outputStreamStringAdapter.hpp" + + +namespace vmime { +namespace utility { + + +outputStreamStringAdapter::outputStreamStringAdapter(string& buffer) + : m_buffer(buffer) +{ +} + + +void outputStreamStringAdapter::write(const value_type* const data, const size_type count) +{ + m_buffer.append(data, count); +} + + +void outputStreamStringAdapter::flush() +{ + // Do nothing +} + + +} // utility +} // vmime + diff --git a/src/utility/stream.cpp b/src/utility/stream.cpp index ec30b7d..1c940c2 100644 --- a/src/utility/stream.cpp +++ b/src/utility/stream.cpp @@ -22,503 +22,18 @@ // #include "vmime/utility/stream.hpp" -#include "vmime/utility/stringProxy.hpp" -#include // for std::copy -#include // for std::back_inserter - -#if VMIME_HAVE_MESSAGING_FEATURES - #include "vmime/net/socket.hpp" -#endif namespace vmime { namespace utility { -// stream - stream::size_type stream::getBlockSize() { return 32768; // 32 KB } -// Helpers - -outputStream& operator<<(outputStream& os, const stream::value_type c) -{ - os.write(&c, 1); - return (os); -} - - -outputStream& operator<<(outputStream& os, const string& str) -{ - os.write(str.data(), str.length()); - return (os); -} - - -stream::size_type bufferedStreamCopy(inputStream& is, outputStream& os) -{ - return bufferedStreamCopy(is, os, 0, NULL); -} - - -stream::size_type bufferedStreamCopy(inputStream& is, outputStream& os, - const stream::size_type length, progressListener* progress) -{ - const stream::size_type blockSize = - std::min(is.getBlockSize(), os.getBlockSize()); - - std::vector vbuffer(blockSize); - - stream::value_type* buffer = &vbuffer.front(); - stream::size_type total = 0; - - if (progress != NULL) - progress->start(length); - - while (!is.eof()) - { - const stream::size_type read = is.read(buffer, blockSize); - - if (read != 0) - { - os.write(buffer, read); - total += read; - - if (progress != NULL) - progress->progress(total, std::max(total, length)); - } - } - - if (progress != NULL) - progress->stop(total); - - return (total); -} - - - -// outputStreamAdapter - -outputStreamAdapter::outputStreamAdapter(std::ostream& os) - : m_stream(os) -{ -} - - -void outputStreamAdapter::write - (const value_type* const data, const size_type count) -{ - m_stream.exceptions(std::ios_base::badbit); - m_stream.write(data, count); -} - - -void outputStreamAdapter::flush() -{ - m_stream.exceptions(std::ios_base::badbit); - m_stream.flush(); -} - - - -// outputStreamStringAdapter - -outputStreamStringAdapter::outputStreamStringAdapter(string& buffer) - : m_buffer(buffer) -{ -} - - -void outputStreamStringAdapter::write(const value_type* const data, const size_type count) -{ - m_buffer.append(data, count); -} - - -void outputStreamStringAdapter::flush() -{ - // Do nothing -} - - - -// outputStreamByteArrayAdapter - -outputStreamByteArrayAdapter::outputStreamByteArrayAdapter(byteArray& array) - : m_array(array) -{ -} - - -void outputStreamByteArrayAdapter::write(const value_type* const data, const size_type count) -{ - m_array.insert(m_array.end(), data, data + count); -} - - -void outputStreamByteArrayAdapter::flush() -{ - // Do nothing -} - - - -// inputStreamAdapter - -inputStreamAdapter::inputStreamAdapter(std::istream& is) - : m_stream(is) -{ -} - - -bool inputStreamAdapter::eof() const -{ - return (m_stream.eof()); -} - - -void inputStreamAdapter::reset() -{ - m_stream.exceptions(std::ios_base::badbit); - m_stream.seekg(0, std::ios::beg); - m_stream.clear(); -} - - -stream::size_type inputStreamAdapter::read - (value_type* const data, const size_type count) -{ - m_stream.exceptions(std::ios_base::badbit); - m_stream.read(data, count); - return (m_stream.gcount()); -} - - -stream::size_type inputStreamAdapter::skip(const size_type count) -{ - m_stream.exceptions(std::ios_base::badbit); - m_stream.ignore(count); - return (m_stream.gcount()); -} - - - -// inputStreamStringAdapter - -inputStreamStringAdapter::inputStreamStringAdapter(const string& buffer) - : m_buffer(buffer), m_begin(0), m_end(buffer.length()), m_pos(0) -{ -} - - -inputStreamStringAdapter::inputStreamStringAdapter(const string& buffer, - const string::size_type begin, const string::size_type end) - : m_buffer(buffer), m_begin(begin), m_end(end), m_pos(begin) -{ -} - - -bool inputStreamStringAdapter::eof() const -{ - return (m_pos >= m_end); -} - - -void inputStreamStringAdapter::reset() -{ - m_pos = m_begin; -} - - -stream::size_type inputStreamStringAdapter::read - (value_type* const data, const size_type count) -{ - if (m_pos + count >= m_end) - { - const size_type remaining = m_end - m_pos; - - std::copy(m_buffer.begin() + m_pos, m_buffer.end(), data); - m_pos = m_end; - return (remaining); - } - else - { - std::copy(m_buffer.begin() + m_pos, m_buffer.begin() + m_pos + count, data); - m_pos += count; - return (count); - } -} - - -stream::size_type inputStreamStringAdapter::skip(const size_type count) -{ - if (m_pos + count >= m_end) - { - const size_type remaining = m_end - m_pos; - m_pos = m_end; - return (remaining); - } - else - { - m_pos += count; - return (count); - } -} - - - -// inputStreamStringProxyAdapter - -inputStreamStringProxyAdapter::inputStreamStringProxyAdapter(const stringProxy& buffer) - : m_buffer(buffer), m_pos(0) -{ -} - - -bool inputStreamStringProxyAdapter::eof() const -{ - return (m_pos >= m_buffer.length()); -} - - -void inputStreamStringProxyAdapter::reset() -{ - m_pos = 0; -} - - -stream::size_type inputStreamStringProxyAdapter::read - (value_type* const data, const size_type count) -{ - const size_type remaining = m_buffer.length() - m_pos; - - if (count > remaining) - { - std::copy(m_buffer.it_begin() + m_pos, m_buffer.it_end(), data); - m_pos = m_buffer.length(); - return (remaining); - } - else - { - std::copy(m_buffer.it_begin() + m_pos, m_buffer.it_begin() + m_pos + count, data); - m_pos += count; - return (count); - } -} - - -stream::size_type inputStreamStringProxyAdapter::skip(const size_type count) -{ - const size_type remaining = m_buffer.length() - m_pos; - - if (count > remaining) - { - m_pos = m_buffer.length(); - return (remaining); - } - else - { - m_pos += count; - return (count); - } -} - - - -// inputStreamPointerAdapter - -inputStreamPointerAdapter::inputStreamPointerAdapter(std::istream* is, const bool own) - : m_stream(is), m_own(own) -{ -} - - -inputStreamPointerAdapter::inputStreamPointerAdapter(const inputStreamPointerAdapter&) - : inputStream(), m_stream(NULL), m_own(false) -{ - // Not copiable -} - - -inputStreamPointerAdapter::~inputStreamPointerAdapter() -{ - if (m_own) - delete (m_stream); -} - - -bool inputStreamPointerAdapter::eof() const -{ - return (m_stream->eof()); -} - - -void inputStreamPointerAdapter::reset() -{ - m_stream->exceptions(std::ios_base::badbit); - m_stream->seekg(0, std::ios::beg); - m_stream->clear(); -} - - -stream::size_type inputStreamPointerAdapter::read - (value_type* const data, const size_type count) -{ - m_stream->exceptions(std::ios_base::badbit); - m_stream->read(data, count); - return (m_stream->gcount()); -} - - -stream::size_type inputStreamPointerAdapter::skip(const size_type count) -{ - m_stream->exceptions(std::ios_base::badbit); - m_stream->ignore(count); - return (m_stream->gcount()); -} - - - -// inputStreamByteBufferAdapter - -inputStreamByteBufferAdapter::inputStreamByteBufferAdapter(const byte_t* buffer, const size_type length) - : m_buffer(buffer), m_length(length), m_pos(0) -{ -} - - -bool inputStreamByteBufferAdapter::eof() const -{ - return m_pos >= m_length; -} - - -void inputStreamByteBufferAdapter::reset() -{ - m_pos = 0; -} - - -stream::size_type inputStreamByteBufferAdapter::read - (value_type* const data, const size_type count) -{ - const size_type remaining = m_length - m_pos; - - if (remaining < count) - { - std::copy(m_buffer + m_pos, m_buffer + m_pos + remaining, data); - m_pos += remaining; - - return remaining; - } - else - { - std::copy(m_buffer + m_pos, m_buffer + m_pos + count, data); - m_pos += count; - - return count; - } -} - - -stream::size_type inputStreamByteBufferAdapter::skip(const size_type count) -{ - const size_type remaining = m_length - m_pos; - - if (remaining < count) - { - m_pos += remaining; - return remaining; - } - else - { - m_pos += count; - return count; - } -} - - - -#ifdef VMIME_HAVE_MESSAGING_FEATURES - - -// outputStreamSocketAdapter - -outputStreamSocketAdapter::outputStreamSocketAdapter(net::socket& sok) - : m_socket(sok) -{ -} - - -void outputStreamSocketAdapter::write - (const value_type* const data, const size_type count) -{ - m_socket.sendRaw(data, count); -} - - -void outputStreamSocketAdapter::flush() -{ - // Do nothing -} - - -stream::size_type outputStreamSocketAdapter::getBlockSize() -{ - return m_socket.getBlockSize(); -} - - - -// inputStreamSocketAdapter - -inputStreamSocketAdapter::inputStreamSocketAdapter(net::socket& sok) - : m_socket(sok) -{ -} - - -bool inputStreamSocketAdapter::eof() const -{ - // Can't know... - return false; -} - - -void inputStreamSocketAdapter::reset() -{ - // Not supported -} - - -stream::size_type inputStreamSocketAdapter::read - (value_type* const data, const size_type count) -{ - return m_socket.receiveRaw(data, count); -} - - -stream::size_type inputStreamSocketAdapter::skip - (const size_type /* count */) -{ - // Not supported - return 0; -} - - -stream::size_type inputStreamSocketAdapter::getBlockSize() -{ - return m_socket.getBlockSize(); -} - - -#endif // VMIME_HAVE_MESSAGING_FEATURES - - } // utility } // vmime diff --git a/src/utility/streamUtils.cpp b/src/utility/streamUtils.cpp new file mode 100644 index 0000000..f1d3b9d --- /dev/null +++ b/src/utility/streamUtils.cpp @@ -0,0 +1,92 @@ +// +// 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/streamUtils.hpp" + +#include // for std::copy +#include // for std::back_inserter + + + +namespace vmime { +namespace utility { + + +outputStream& operator<<(outputStream& os, const stream::value_type c) +{ + os.write(&c, 1); + return (os); +} + + +outputStream& operator<<(outputStream& os, const string& str) +{ + os.write(str.data(), str.length()); + return (os); +} + + +stream::size_type bufferedStreamCopy(inputStream& is, outputStream& os) +{ + return bufferedStreamCopy(is, os, 0, NULL); +} + + +stream::size_type bufferedStreamCopy(inputStream& is, outputStream& os, + const stream::size_type length, progressListener* progress) +{ + const stream::size_type blockSize = + std::min(is.getBlockSize(), os.getBlockSize()); + + std::vector vbuffer(blockSize); + + stream::value_type* buffer = &vbuffer.front(); + stream::size_type total = 0; + + if (progress != NULL) + progress->start(length); + + while (!is.eof()) + { + const stream::size_type read = is.read(buffer, blockSize); + + if (read != 0) + { + os.write(buffer, read); + total += read; + + if (progress != NULL) + progress->progress(total, std::max(total, length)); + } + } + + if (progress != NULL) + progress->stop(total); + + return (total); +} + + +} // utility +} // vmime + diff --git a/src/utility/stringProxy.cpp b/src/utility/stringProxy.cpp index a4ba6d2..74344b5 100644 --- a/src/utility/stringProxy.cpp +++ b/src/utility/stringProxy.cpp @@ -23,6 +23,8 @@ #include "vmime/utility/stringProxy.hpp" +#include "vmime/utility/outputStreamAdapter.hpp" + #include #include diff --git a/src/word.cpp b/src/word.cpp index aeaa737..79060a1 100644 --- a/src/word.cpp +++ b/src/word.cpp @@ -28,6 +28,9 @@ #include "vmime/utility/smartPtr.hpp" #include "vmime/parserHelpers.hpp" +#include "vmime/utility/outputStreamStringAdapter.hpp" +#include "vmime/utility/inputStreamStringAdapter.hpp" + #include "vmime/utility/encoder/encoder.hpp" #include "vmime/utility/encoder/b64Encoder.hpp" #include "vmime/utility/encoder/qpEncoder.hpp" diff --git a/src/wordEncoder.cpp b/src/wordEncoder.cpp index 67bd7a1..194a189 100644 --- a/src/wordEncoder.cpp +++ b/src/wordEncoder.cpp @@ -33,6 +33,9 @@ #include "vmime/utility/stringUtils.hpp" +#include "vmime/utility/outputStreamStringAdapter.hpp" +#include "vmime/utility/inputStreamStringAdapter.hpp" + namespace vmime { diff --git a/vmime/base.hpp b/vmime/base.hpp index 60e637d..b794031 100644 --- a/vmime/base.hpp +++ b/vmime/base.hpp @@ -35,7 +35,6 @@ #include "vmime/config.hpp" #include "vmime/types.hpp" #include "vmime/constants.hpp" -#include "vmime/utility/stream.hpp" #include "vmime/utility/smartPtr.hpp" @@ -255,7 +254,26 @@ namespace vmime return y.dynamicCast (); } + /** Inherit from this class to indicate the subclass is not copyable, + * ie. you want to prohibit copy construction and copy assignment. + */ + class noncopyable + { + protected: + + noncopyable() { } + virtual ~noncopyable() { } + + private: + + noncopyable(const noncopyable&); + void operator=(const noncopyable&); + }; + } // vmime +#include "vmime/utility/stream.hpp" + + #endif // VMIME_BASE_HPP_INCLUDED diff --git a/vmime/charset.hpp b/vmime/charset.hpp index b2e241c..5f5e8e5 100644 --- a/vmime/charset.hpp +++ b/vmime/charset.hpp @@ -26,6 +26,8 @@ #include "vmime/base.hpp" +#include "vmime/utility/inputStream.hpp" +#include "vmime/utility/outputStream.hpp" #include "vmime/component.hpp" diff --git a/vmime/component.hpp b/vmime/component.hpp index b38127f..12b0406 100644 --- a/vmime/component.hpp +++ b/vmime/component.hpp @@ -26,6 +26,8 @@ #include "vmime/base.hpp" +#include "vmime/utility/inputStream.hpp" +#include "vmime/utility/outputStream.hpp" namespace vmime diff --git a/vmime/net/imap/IMAPParser.hpp b/vmime/net/imap/IMAPParser.hpp index d71c3ca..f430510 100644 --- a/vmime/net/imap/IMAPParser.hpp +++ b/vmime/net/imap/IMAPParser.hpp @@ -37,6 +37,9 @@ #include "vmime/utility/encoder/b64Encoder.hpp" #include "vmime/utility/encoder/qpEncoder.hpp" +#include "vmime/utility/inputStreamStringAdapter.hpp" +#include "vmime/utility/outputStreamStringAdapter.hpp" + #include "vmime/platform.hpp" #include "vmime/net/timeoutHandler.hpp" @@ -3825,7 +3828,7 @@ public: : m_date_time(NULL), m_number(NULL), m_envelope(NULL), m_uniqueid(NULL), m_nstring(NULL), m_body(NULL), m_flag_list(NULL), m_section(NULL) - + { } diff --git a/vmime/utility/filteredStream.hpp b/vmime/utility/filteredStream.hpp index 00be785..2a55edd 100644 --- a/vmime/utility/filteredStream.hpp +++ b/vmime/utility/filteredStream.hpp @@ -27,7 +27,8 @@ #include -#include "vmime/utility/stream.hpp" +#include "vmime/utility/inputStream.hpp" +#include "vmime/utility/outputStream.hpp" namespace vmime { diff --git a/vmime/utility/inputStream.hpp b/vmime/utility/inputStream.hpp new file mode 100644 index 0000000..4a76a7d --- /dev/null +++ b/vmime/utility/inputStream.hpp @@ -0,0 +1,76 @@ +// +// 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_INPUTSTREAM_HPP_INCLUDED +#define VMIME_UTILITY_INPUTSTREAM_HPP_INCLUDED + + +#include "vmime/utility/stream.hpp" + + +namespace vmime { +namespace utility { + + +/** Simple input stream. + */ + +class inputStream : public stream +{ +public: + + /** Test for end of stream (no more data to read). + * + * @return true if we have reached the end of stream, false otherwise + */ + virtual bool eof() const = 0; + + /** Set the read pointer to the beginning of the stream. + * + * @warning WARNING: this may not work for all stream types. + */ + virtual void reset() = 0; + + /** Read data from the stream. + * + * @param data will receive the data read + * @param count maximum number of bytes to read + * @return number of bytes read + */ + virtual size_type read(value_type* const data, const size_type count) = 0; + + /** Skip a number of bytes. + * + * @param count maximum number of bytes to ignore + * @return number of bytes skipped + */ + virtual size_type skip(const size_type count) = 0; +}; + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_INPUTSTREAM_HPP_INCLUDED + diff --git a/vmime/utility/inputStreamAdapter.hpp b/vmime/utility/inputStreamAdapter.hpp new file mode 100644 index 0000000..278ab52 --- /dev/null +++ b/vmime/utility/inputStreamAdapter.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_INPUTSTREAMADAPTER_HPP_INCLUDED +#define VMIME_UTILITY_INPUTSTREAMADAPTER_HPP_INCLUDED + + +#include "vmime/utility/inputStream.hpp" + +#include + + +namespace vmime { +namespace utility { + + +/** An adapter class for C++ standard input streams. + */ + +class inputStreamAdapter : public inputStream +{ +public: + + /** @param is input stream to wrap + */ + inputStreamAdapter(std::istream& is); + + bool eof() const; + void reset(); + size_type read(value_type* const data, const size_type count); + size_type skip(const size_type count); + +private: + + std::istream& m_stream; +}; + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_INPUTSTREAMADAPTER_HPP_INCLUDED + diff --git a/vmime/utility/inputStreamByteBufferAdapter.hpp b/vmime/utility/inputStreamByteBufferAdapter.hpp new file mode 100644 index 0000000..0f6a442 --- /dev/null +++ b/vmime/utility/inputStreamByteBufferAdapter.hpp @@ -0,0 +1,63 @@ +// +// 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_INPUTSTREAMBYTEBUFFERADAPTER_HPP_INCLUDED +#define VMIME_UTILITY_INPUTSTREAMBYTEBUFFERADAPTER_HPP_INCLUDED + + +#include "vmime/utility/inputStream.hpp" + + +namespace vmime { +namespace utility { + + +/** An adapter class for reading from an array of bytes. + */ + +class inputStreamByteBufferAdapter : public inputStream +{ +public: + + inputStreamByteBufferAdapter(const byte_t* buffer, 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); + +private: + + const byte_t* m_buffer; + const size_type m_length; + + size_type m_pos; +}; + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_INPUTSTREAMBYTEBUFFERADAPTER_HPP_INCLUDED + diff --git a/vmime/utility/inputStreamPointerAdapter.hpp b/vmime/utility/inputStreamPointerAdapter.hpp new file mode 100644 index 0000000..44e9bad --- /dev/null +++ b/vmime/utility/inputStreamPointerAdapter.hpp @@ -0,0 +1,63 @@ +// +// 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_INPUTSTREAMPOINTERADAPTER_HPP_INCLUDED +#define VMIME_UTILITY_INPUTSTREAMPOINTERADAPTER_HPP_INCLUDED + + +#include "vmime/utility/inputStreamAdapter.hpp" + +#include + + +namespace vmime { +namespace utility { + + +/** An adapter class for pointer to C++ standard input stream. + */ + +class inputStreamPointerAdapter : public inputStreamAdapter +{ +public: + + /** @param is input stream to wrap + * @param own if set to 'true', the pointer will be deleted when + * this object is destroyed + */ + inputStreamPointerAdapter(std::istream* is, const bool own = true); + ~inputStreamPointerAdapter(); + +private: + + std::istream* m_stream; + const bool m_own; +}; + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_INPUTSTREAMPOINTERADAPTER_HPP_INCLUDED + diff --git a/vmime/utility/inputStreamSocketAdapter.hpp b/vmime/utility/inputStreamSocketAdapter.hpp new file mode 100644 index 0000000..0f99c21 --- /dev/null +++ b/vmime/utility/inputStreamSocketAdapter.hpp @@ -0,0 +1,77 @@ +// +// 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_INPUTSTREAMSOCKETADAPTER_HPP_INCLUDED +#define VMIME_UTILITY_INPUTSTREAMSOCKETADAPTER_HPP_INCLUDED + + +#include "vmime/utility/inputStream.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +namespace vmime { +namespace net { + class socket; // forward reference +} // net +} // vmime + + +namespace vmime { +namespace utility { + + +/** An input stream that is connected to a socket. + */ + +class inputStreamSocketAdapter : public inputStream +{ +public: + + inputStreamSocketAdapter(net::socket& sok); + + 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 getBlockSize(); + +private: + + inputStreamSocketAdapter(const inputStreamSocketAdapter&); + + net::socket& m_socket; +}; + + +} // utility +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + + +#endif // VMIME_UTILITY_INPUTSTREAMSOCKETADAPTER_HPP_INCLUDED + diff --git a/vmime/utility/inputStreamStringAdapter.hpp b/vmime/utility/inputStreamStringAdapter.hpp new file mode 100644 index 0000000..a7d986f --- /dev/null +++ b/vmime/utility/inputStreamStringAdapter.hpp @@ -0,0 +1,66 @@ +// +// 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_INPUTSTREAMSTRINGADAPTER_HPP_INCLUDED +#define VMIME_UTILITY_INPUTSTREAMSTRINGADAPTER_HPP_INCLUDED + + +#include "vmime/utility/inputStream.hpp" + + +namespace vmime { +namespace utility { + + +/** An adapter class for string input. + */ + +class inputStreamStringAdapter : public inputStream +{ +public: + + inputStreamStringAdapter(const string& buffer); + inputStreamStringAdapter(const string& buffer, const string::size_type begin, const string::size_type end); + + bool eof() const; + void reset(); + size_type read(value_type* const data, const size_type count); + size_type skip(const size_type count); + +private: + + inputStreamStringAdapter(const inputStreamStringAdapter&); + + const string m_buffer; // do _NOT_ keep a reference... + const string::size_type m_begin; + const string::size_type m_end; + string::size_type m_pos; +}; + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_INPUTSTREAMSTRINGADAPTER_HPP_INCLUDED + diff --git a/vmime/utility/inputStreamStringProxyAdapter.hpp b/vmime/utility/inputStreamStringProxyAdapter.hpp new file mode 100644 index 0000000..74b3f60 --- /dev/null +++ b/vmime/utility/inputStreamStringProxyAdapter.hpp @@ -0,0 +1,68 @@ +// +// 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_INPUTSTREAMSTRINGPROXYADAPTER_HPP_INCLUDED +#define VMIME_UTILITY_INPUTSTREAMSTRINGPROXYADAPTER_HPP_INCLUDED + + +#include "vmime/utility/inputStream.hpp" + + +namespace vmime { +namespace utility { + + +class stringProxy; + + +/** An adapter class for stringProxy input. + */ + +class inputStreamStringProxyAdapter : public inputStream +{ +public: + + /** @param buffer stringProxy object to wrap + */ + inputStreamStringProxyAdapter(const stringProxy& buffer); + + bool eof() const; + void reset(); + size_type read(value_type* const data, const size_type count); + size_type skip(const size_type count); + +private: + + inputStreamStringProxyAdapter(const inputStreamStringProxyAdapter&); + + const stringProxy& m_buffer; + string::size_type m_pos; +}; + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_INPUTSTREAMSTRINGPROXYADAPTER_HPP_INCLUDED + diff --git a/vmime/utility/outputStream.hpp b/vmime/utility/outputStream.hpp new file mode 100644 index 0000000..7372d20 --- /dev/null +++ b/vmime/utility/outputStream.hpp @@ -0,0 +1,107 @@ +// +// 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_OUTPUTSTREAM_HPP_INCLUDED +#define VMIME_UTILITY_OUTPUTSTREAM_HPP_INCLUDED + + +#include "vmime/utility/stream.hpp" + + +#if defined(_MSC_VER) && (_MSC_VER <= 1200) // VC++6 +# include +#endif + + +namespace vmime { +namespace utility { + + +/** Simple output stream. + */ + +class outputStream : public stream +{ +public: + + /** Write data to the stream. + * + * @param data buffer containing data to write + * @param count number of bytes to write + */ + virtual void write(const value_type* const data, const size_type count) = 0; + + /** Flush this output stream and forces any buffered output + * bytes to be written out to the stream. + */ + virtual void flush() = 0; +}; + + +// Helpers functions + +outputStream& operator<<(outputStream& os, const string& str); +outputStream& operator<<(outputStream& os, const stream::value_type c); + + +#if defined(_MSC_VER) && (_MSC_VER <= 1200) // Internal compiler error with VC++6 + +inline outputStream& operator<<(outputStream& os, const char* str) +{ + os.write(str, ::strlen(str)); + return (os); +} + +#else + +template +outputStream& operator<<(outputStream& os, const char (&str)[N]) +{ + os.write(str, N - 1); + return (os); +} + +#endif // defined(_MSC_VER) && (_MSC_VER <= 1200) + + +template +outputStream& operator<<(outputStream& os, const T& t) +{ + std::ostringstream oss; + oss.imbue(std::locale::classic()); // no formatting + + oss << t; + + os << oss.str(); + + return (os); +} + + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_OUTPUTSTREAM_HPP_INCLUDED + diff --git a/vmime/utility/outputStreamAdapter.hpp b/vmime/utility/outputStreamAdapter.hpp new file mode 100644 index 0000000..be55d8d --- /dev/null +++ b/vmime/utility/outputStreamAdapter.hpp @@ -0,0 +1,62 @@ +// +// 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_OUTPUTSTREAMADAPTER_HPP_INCLUDED +#define VMIME_UTILITY_OUTPUTSTREAMADAPTER_HPP_INCLUDED + + +#include "vmime/utility/outputStream.hpp" + +#include + + +namespace vmime { +namespace utility { + + +/** An adapter class for C++ standard output streams. + */ + +class outputStreamAdapter : public outputStream +{ +public: + + /** @param os output stream to wrap + */ + outputStreamAdapter(std::ostream& os); + + void write(const value_type* const data, const size_type count); + void flush(); + +private: + + std::ostream& m_stream; +}; + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_OUTPUTSTREAMADAPTER_HPP_INCLUDED + diff --git a/vmime/utility/outputStreamByteArrayAdapter.hpp b/vmime/utility/outputStreamByteArrayAdapter.hpp new file mode 100644 index 0000000..bf7d839 --- /dev/null +++ b/vmime/utility/outputStreamByteArrayAdapter.hpp @@ -0,0 +1,58 @@ +// +// 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_OUTPUTSTREAMBYTEARRAYADAPTER_HPP_INCLUDED +#define VMIME_UTILITY_OUTPUTSTREAMBYTEARRAYADAPTER_HPP_INCLUDED + + +#include "vmime/utility/outputStream.hpp" + + +namespace vmime { +namespace utility { + + +/** An adapter class for byte array output. + */ + +class outputStreamByteArrayAdapter : public outputStream +{ +public: + + outputStreamByteArrayAdapter(byteArray& array); + + void write(const value_type* const data, const size_type count); + void flush(); + +private: + + byteArray& m_array; +}; + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_OUTPUTSTREAMBYTEARRAYADAPTER_HPP_INCLUDED + diff --git a/vmime/utility/outputStreamSocketAdapter.hpp b/vmime/utility/outputStreamSocketAdapter.hpp new file mode 100644 index 0000000..e3d3eb0 --- /dev/null +++ b/vmime/utility/outputStreamSocketAdapter.hpp @@ -0,0 +1,75 @@ +// +// 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_OUTPUTSTREAMSOCKETADAPTER_HPP_INCLUDED +#define VMIME_UTILITY_OUTPUTSTREAMSOCKETADAPTER_HPP_INCLUDED + + +#include "vmime/utility/outputStream.hpp" + + +#if VMIME_HAVE_MESSAGING_FEATURES + + +namespace vmime { +namespace net { + class socket; // forward reference +} // net +} // vmime + + +namespace vmime { +namespace utility { + + +/** An output stream that is connected to a socket. + */ + +class outputStreamSocketAdapter : public outputStream +{ +public: + + outputStreamSocketAdapter(net::socket& sok); + + void write(const value_type* const data, const size_type count); + void flush(); + + size_type getBlockSize(); + +private: + + outputStreamSocketAdapter(const outputStreamSocketAdapter&); + + net::socket& m_socket; +}; + + +} // utility +} // vmime + + +#endif // VMIME_HAVE_MESSAGING_FEATURES + + +#endif // VMIME_UTILITY_OUTPUTSTREAMSOCKETADAPTER_HPP_INCLUDED + diff --git a/vmime/utility/outputStreamStringAdapter.hpp b/vmime/utility/outputStreamStringAdapter.hpp new file mode 100644 index 0000000..8c8b304 --- /dev/null +++ b/vmime/utility/outputStreamStringAdapter.hpp @@ -0,0 +1,59 @@ +// +// 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_OUTPUTSTREAMSTRINGADAPTER_HPP_INCLUDED +#define VMIME_UTILITY_OUTPUTSTREAMSTRINGADAPTER_HPP_INCLUDED + + +#include "vmime/utility/outputStream.hpp" + + +namespace vmime { +namespace utility { + + +/** An adapter class for string output. + */ + +class outputStreamStringAdapter : public outputStream +{ +public: + + outputStreamStringAdapter(string& buffer); + + void write(const value_type* const data, const size_type count); + void flush(); + +size_type getBlockSize(){return 8192;} +private: + + string& m_buffer; +}; + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_OUTPUTSTREAMSTRINGADAPTER_HPP_INCLUDED + diff --git a/vmime/utility/stream.hpp b/vmime/utility/stream.hpp index 1faab55..566ab9d 100644 --- a/vmime/utility/stream.hpp +++ b/vmime/utility/stream.hpp @@ -25,40 +25,22 @@ #define VMIME_UTILITY_STREAM_HPP_INCLUDED -#include -#include #include #include "vmime/config.hpp" #include "vmime/types.hpp" - -#include "vmime/utility/progressListener.hpp" - - -#if VMIME_HAVE_MESSAGING_FEATURES - namespace vmime { - namespace net { - class socket; // forward reference - } // net - } // vmime -#endif - -#if defined(_MSC_VER) && (_MSC_VER <= 1200) // VC++6 -# include -#endif +#include "vmime/base.hpp" namespace vmime { namespace utility { -class stringProxy; - /** Base class for input/output stream. */ -class stream : public object +class stream : public object, private noncopyable { public: @@ -81,365 +63,6 @@ public: }; - -/** Simple output stream. - */ - -class outputStream : public stream -{ -public: - - /** Write data to the stream. - * - * @param data buffer containing data to write - * @param count number of bytes to write - */ - virtual void write(const value_type* const data, const size_type count) = 0; - - /** Flush this output stream and forces any buffered output - * bytes to be written out to the stream. - */ - virtual void flush() = 0; -}; - - - -/** Simple input stream. - */ - -class inputStream : public stream -{ -public: - - /** Test for end of stream (no more data to read). - * - * @return true if we have reached the end of stream, false otherwise - */ - virtual bool eof() const = 0; - - /** Set the read pointer to the beginning of the stream. - * - * @warning WARNING: this may not work for all stream types. - */ - virtual void reset() = 0; - - /** Read data from the stream. - * - * @param data will receive the data read - * @param count maximum number of bytes to read - * @return number of bytes read - */ - virtual size_type read(value_type* const data, const size_type count) = 0; - - /** Skip a number of bytes. - * - * @param count maximum number of bytes to ignore - * @return number of bytes skipped - */ - virtual size_type skip(const size_type count) = 0; -}; - - - -// Helpers functions - -outputStream& operator<<(outputStream& os, const string& str); -outputStream& operator<<(outputStream& os, const stream::value_type c); - - -#if defined(_MSC_VER) && (_MSC_VER <= 1200) // Internal compiler error with VC++6 - -inline outputStream& operator<<(outputStream& os, const char* str) -{ - os.write(str, ::strlen(str)); - return (os); -} - -#else - -template -outputStream& operator<<(outputStream& os, const char (&str)[N]) -{ - os.write(str, N - 1); - return (os); -} - -#endif // defined(_MSC_VER) && (_MSC_VER <= 1200) - - -template -outputStream& operator<<(outputStream& os, const T& t) -{ - std::ostringstream oss; - oss.imbue(std::locale::classic()); // no formatting - - oss << t; - - os << oss.str(); - - return (os); -} - - -/** Copy data from one stream into another stream using a buffered method. - * - * @param is input stream (source data) - * @param os output stream (destination for data) - * @return number of bytes copied - */ - -stream::size_type bufferedStreamCopy(inputStream& is, outputStream& os); - -/** Copy data from one stream into another stream using a buffered method - * and notify progress state of the operation. - * - * @param is input stream (source data) - * @param os output stream (destination for data) - * @param length predicted number of bytes to copy - * @param progress listener to notify - * @return number of bytes copied - */ - -stream::size_type bufferedStreamCopy(inputStream& is, outputStream& os, - const stream::size_type length, progressListener* progress); - - -// Adapters - - -/** An adapter class for C++ standard output streams. - */ - -class outputStreamAdapter : public outputStream -{ -public: - - /** @param os output stream to wrap - */ - outputStreamAdapter(std::ostream& os); - - void write(const value_type* const data, const size_type count); - void flush(); - -private: - - std::ostream& m_stream; -}; - - -/** An adapter class for string output. - */ - -class outputStreamStringAdapter : public outputStream -{ -public: - - outputStreamStringAdapter(string& buffer); - - void write(const value_type* const data, const size_type count); - void flush(); - -size_type getBlockSize(){return 8192;} -private: - - string& m_buffer; -}; - - -/** An adapter class for byte array output. - */ - -class outputStreamByteArrayAdapter : public outputStream -{ -public: - - outputStreamByteArrayAdapter(byteArray& array); - - void write(const value_type* const data, const size_type count); - void flush(); - -private: - - byteArray& m_array; -}; - - -/** An adapter class for C++ standard input streams. - */ - -class inputStreamAdapter : public inputStream -{ -public: - - /** @param is input stream to wrap - */ - inputStreamAdapter(std::istream& is); - - bool eof() const; - void reset(); - size_type read(value_type* const data, const size_type count); - size_type skip(const size_type count); - -private: - - std::istream& m_stream; -}; - - -/** An adapter class for string input. - */ - -class inputStreamStringAdapter : public inputStream -{ -public: - - inputStreamStringAdapter(const string& buffer); - inputStreamStringAdapter(const string& buffer, const string::size_type begin, const string::size_type end); - - bool eof() const; - void reset(); - size_type read(value_type* const data, const size_type count); - size_type skip(const size_type count); - -private: - - inputStreamStringAdapter(const inputStreamStringAdapter&); - - const string m_buffer; // do _NOT_ keep a reference... - const string::size_type m_begin; - const string::size_type m_end; - string::size_type m_pos; -}; - - -/** An adapter class for stringProxy input. - */ - -class inputStreamStringProxyAdapter : public inputStream -{ -public: - - /** @param buffer stringProxy object to wrap - */ - inputStreamStringProxyAdapter(const stringProxy& buffer); - - bool eof() const; - void reset(); - size_type read(value_type* const data, const size_type count); - size_type skip(const size_type count); - -private: - - inputStreamStringProxyAdapter(const inputStreamStringProxyAdapter&); - - const stringProxy& m_buffer; - string::size_type m_pos; -}; - - -/** An adapter class for pointer to C++ standard input stream. - */ - -class inputStreamPointerAdapter : public inputStream -{ -public: - - /** @param is input stream to wrap - * @param own if set to 'true', the pointer will be deleted when - * this object is destroyed - */ - inputStreamPointerAdapter(std::istream* is, const bool own = true); - ~inputStreamPointerAdapter(); - - bool eof() const; - void reset(); - size_type read(value_type* const data, const size_type count); - size_type skip(const size_type count); - -private: - - inputStreamPointerAdapter(const inputStreamPointerAdapter&); - - std::istream* m_stream; - const bool m_own; -}; - - -/** An adapter class for reading from an array of bytes. - */ - -class inputStreamByteBufferAdapter : public inputStream -{ -public: - - inputStreamByteBufferAdapter(const byte_t* buffer, 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); - -private: - - const byte_t* m_buffer; - const size_type m_length; - - size_type m_pos; -}; - - -#if VMIME_HAVE_MESSAGING_FEATURES - - -/** An output stream that is connected to a socket. - */ - -class outputStreamSocketAdapter : public outputStream -{ -public: - - outputStreamSocketAdapter(net::socket& sok); - - void write(const value_type* const data, const size_type count); - void flush(); - - size_type getBlockSize(); - -private: - - outputStreamSocketAdapter(const outputStreamSocketAdapter&); - - net::socket& m_socket; -}; - - -/** An input stream that is connected to a socket. - */ - -class inputStreamSocketAdapter : public inputStream -{ -public: - - inputStreamSocketAdapter(net::socket& sok); - - 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 getBlockSize(); - -private: - - inputStreamSocketAdapter(const inputStreamSocketAdapter&); - - net::socket& m_socket; -}; - - -#endif // VMIME_HAVE_MESSAGING_FEATURES - - } // utility } // vmime diff --git a/vmime/utility/streamUtils.hpp b/vmime/utility/streamUtils.hpp new file mode 100644 index 0000000..cdf70aa --- /dev/null +++ b/vmime/utility/streamUtils.hpp @@ -0,0 +1,66 @@ +// +// 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_STREAMUTILS_HPP_INCLUDED +#define VMIME_UTILITY_STREAMUTILS_HPP_INCLUDED + + +#include "vmime/utility/inputStream.hpp" +#include "vmime/utility/outputStream.hpp" + +#include "vmime/utility/progressListener.hpp" + + +namespace vmime { +namespace utility { + + +/** Copy data from one stream into another stream using a buffered method. + * + * @param is input stream (source data) + * @param os output stream (destination for data) + * @return number of bytes copied + */ + +stream::size_type bufferedStreamCopy(inputStream& is, outputStream& os); + +/** Copy data from one stream into another stream using a buffered method + * and notify progress state of the operation. + * + * @param is input stream (source data) + * @param os output stream (destination for data) + * @param length predicted number of bytes to copy + * @param progress listener to notify + * @return number of bytes copied + */ + +stream::size_type bufferedStreamCopy(inputStream& is, outputStream& os, + const stream::size_type length, progressListener* progress); + + +} // utility +} // vmime + + +#endif // VMIME_UTILITY_STREAMUTILS_HPP_INCLUDED + diff --git a/vmime/utility/stringProxy.hpp b/vmime/utility/stringProxy.hpp index 21c65ea..66a6dfd 100644 --- a/vmime/utility/stringProxy.hpp +++ b/vmime/utility/stringProxy.hpp @@ -29,6 +29,7 @@ #include "vmime/types.hpp" #include "vmime/utility/stream.hpp" +#include "vmime/utility/outputStream.hpp" #include "vmime/utility/progressListener.hpp" diff --git a/vmime/vmime.hpp b/vmime/vmime.hpp index f187b9e..fd04853 100644 --- a/vmime/vmime.hpp +++ b/vmime/vmime.hpp @@ -68,6 +68,22 @@ // Encoders #include "vmime/utility/encoder/encoderFactory.hpp" +// Streams +#include "vmime/utility/filteredStream.hpp" +#include "vmime/utility/inputStream.hpp" +#include "vmime/utility/inputStreamAdapter.hpp" +#include "vmime/utility/inputStreamByteBufferAdapter.hpp" +#include "vmime/utility/inputStreamPointerAdapter.hpp" +#include "vmime/utility/inputStreamSocketAdapter.hpp" +#include "vmime/utility/inputStreamStringAdapter.hpp" +#include "vmime/utility/inputStreamStringProxyAdapter.hpp" +#include "vmime/utility/outputStream.hpp" +#include "vmime/utility/outputStreamAdapter.hpp" +#include "vmime/utility/outputStreamByteArrayAdapter.hpp" +#include "vmime/utility/outputStreamSocketAdapter.hpp" +#include "vmime/utility/outputStreamStringAdapter.hpp" +#include "vmime/utility/streamUtils.hpp" + // Message builder/parser #include "vmime/messageBuilder.hpp" #include "vmime/messageParser.hpp" -- 1.7.10.4 From be30b47f09c5358db2ac8e42fa2bb4a14ec24c51 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Mon, 16 Apr 2012 22:32:33 +0200 Subject: [PATCH 37/42] Added ability to parse directly from an input stream (eg. file). This allows very big messages to be parsed without loading the whole message data into memory. diff --git a/ChangeLog b/ChangeLog index 8fdcdb0..1b5b2cf 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 ea5c4eb..2690172 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 31a2a3d..f06460d 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 9d7d57f..732fa8b 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; + + parser->seek(pos); + + if (pos + 2 < end && parser->matchBytes("--", 2)) + { + pos += 2; + } + else + { + pos = parser->findNext("\n--", position); - if ((pos != string::npos) && (pos < end)) + if ((pos != utility::stream::npos) && (pos + 3 < end)) + pos += 3; // skip \n-- + } + + if ((pos != utility::stream::npos) && (pos < end)) { - pos += 3; + parser->seek(pos); - const string::size_type start = 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]))); - char_t c = buffer[pos]; - string::size_type length = 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; - - boundary = string(buffer.begin() + start, - buffer.begin() + pos); + while (boundaryLen != 0 && + parserHelpers::isSpace(boundaryBytes[boundaryLen - 1])) + { + boundaryLen--; + } + + 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); - - if (pos == string::npos || - ((pos == 0 || buffer[pos - 1] == '\n') && - (buffer[pos + boundarySep.length()] == '\r' || - buffer[pos + boundarySep.length()] == '\n' || - buffer[pos + boundarySep.length()] == '-' - ) - ) - ) + pos = parser->findNext(boundarySep, pos); + + 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); - - if (pos == string::npos || - ((pos == 0 || buffer[pos - 1] == '\n') && - (buffer[pos + boundarySep.length()] == '\r' || - buffer[pos + boundarySep.length()] == '\n' || - buffer[pos + boundarySep.length()] == '-' - ) - ) - ) + pos = parser->findNext(boundarySep, pos); + + 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 7d60461..522cbb2 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 0fda450..705664f 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 139cf66..e93aacf 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(); - - std::vector > list; - - const std::vector >::size_type count = constList.size(); +} // vmime - 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 0ab7c45..253dbba 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 089a900..0d97b2f 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 b8059a7..24a4579 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 5d99ab6..343a822 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 443aab8..fcdca2c 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 d1d4236..a8460aa 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 fea7479..dfdccad 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 94f7ba6..c37444a 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 0023d9d..f87fb48 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 725f933..627b276 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 1b4f086..3fa9b6a 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 961fb63..1f4b186 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 08103d0..0a5c9a0 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 ccbe1a5..58d9a3e 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 464990e..756d02f 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 37a4090..d92bb0a 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 ec529eb..4087a21 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 624612a..5da786e 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 5cd454f..97f793d 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 89a36b4..14837d2 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 66c3b35..91b81e1 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 b44b084..441307b 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 92e779f..907f1ee 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 31c9fda..9b8fb0c 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 5e4b60b..feecddd 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 0000000..7a38ef1 --- /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 0000000..348618c --- /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 1c940c2..67c1f33 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 f1d3b9d..f7ea62f 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 79060a1..2876ddf 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 9d51262..deb4b9c 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 2e537c0..9dc283c 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 9e83d6b..bd5bbb9 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 aa0f040..5f36d90 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 5f5e8e5..26abb4f 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 12b0406..5e6f393 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 9d1749b..abd2e1a 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 8e99640..053f4a6 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 05bfca2..7bdc832 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 42f5246..4322b29 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 95a9326..ed555b0 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 50494c9..61e01ee 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; +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; -protected: - static ref parseNext(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition = NULL); + 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 2072be8..2099355 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 0061d5b..1433141 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 11e4e79..1b480c1 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 658b21f..18182f0 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 f3be229..9767564 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; - - const string generate(const string::size_type maxLineLength = options::getInstance()->message.maxLineLength(), const string::size_type curLinePos = 0) const; +public: - void parse(const string& buffer); + // 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; + + 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 3686b11..ac408e6 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; - -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; /** 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 5dfb840..6736d0a 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 e1b13a1..0773ea6 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 2940ca3..d2c934f 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: - - using headerField::parse; - using headerField::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: - const std::vector > getChildComponents() 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/parserHelpers.hpp b/vmime/parserHelpers.hpp index 9b075f7..d4f1246 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 beaa72b..eec8dfc 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 70986df..704b7b0 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 6e1c8fb..f417032 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 583ad80..dbaedf2 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 15e11ae..778ce86 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 278ab52..bd4d21e 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 0f6a442..b3dafd9 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 a7d986f..18a9083 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 74b3f60..dc52637 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 0000000..c24fa44 --- /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 0000000..c2ab1bb --- /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 0000000..5ebccc6 --- /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 566ab9d..78be827 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 cdf70aa..87c8fc5 100644 --- a/vmime/utility/streamUtils.hpp +++ b/vmime/utility/streamUtils.hpp @@ -45,6 +45,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. * * @param is input stream (source data) diff --git a/vmime/word.hpp b/vmime/word.hpp index ad848ec..492aab5 100644 --- a/vmime/word.hpp +++ b/vmime/word.hpp @@ -128,21 +128,52 @@ public: #endif - 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 generate(utility::outputStream& os, const string::size_type maxLineLength, const string::size_type curLinePos, string::size_type* newLinePos, const int flags, generatorState* state) 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; - const std::vector > getChildComponents() const; +public: -private: + using component::generate; - 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); +#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 + + const std::vector > getChildComponents(); + +private: - static const std::vector > parseMultiple(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, + 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); // The "m_buffer" of this word holds the data, and this data is encoded -- 1.7.10.4 From 2e05e574fde890c7ec6dd9f3930d06b1b492ea80 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Fri, 27 Apr 2012 08:34:26 +0200 Subject: [PATCH 38/42] Fixed duplicate file reference (thanks to Enes Albay). diff --git a/SConstruct b/SConstruct index 2690172..1f3c7c9 100644 --- a/SConstruct +++ b/SConstruct @@ -137,7 +137,6 @@ libvmime_sources = [ 'utility/childProcess.hpp', 'utility/file.hpp', 'utility/datetimeUtils.cpp', 'utility/datetimeUtils.hpp', - 'utility/filteredStream.cpp', 'utility/filteredStream.hpp', 'utility/path.cpp', 'utility/path.hpp', 'utility/progressListener.cpp', 'utility/progressListener.hpp', 'utility/random.cpp', 'utility/random.hpp', -- 1.7.10.4 From 799629fd8b21a716f3e3abc6e6a5264555470d85 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Fri, 6 Jul 2012 18:45:02 +0200 Subject: [PATCH 39/42] Fixed issue #10. diff --git a/src/net/imap/IMAPMessage.cpp b/src/net/imap/IMAPMessage.cpp index 702d5f2..8006920 100644 --- a/src/net/imap/IMAPMessage.cpp +++ b/src/net/imap/IMAPMessage.cpp @@ -279,8 +279,6 @@ void IMAPMessage::extract(ref p, utility::outputStream& os, { if (headerOnly) command << "HEADER"; - else - command << "TEXT"; } else { -- 1.7.10.4 From 72cf7a025f7764998609683904eea1046a766d97 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Sat, 28 Jul 2012 13:01:48 +0200 Subject: [PATCH 40/42] Added functions to get messages by UID (IMAP only for now). diff --git a/src/net/imap/IMAPFolder.cpp b/src/net/imap/IMAPFolder.cpp index 81bf386..3d8c17e 100644 --- a/src/net/imap/IMAPFolder.cpp +++ b/src/net/imap/IMAPFolder.cpp @@ -208,7 +208,7 @@ void IMAPFolder::open(const int mode, bool failIfModeIsNotAvailable) { case IMAPParser::resp_text_code::UIDVALIDITY: - m_uidValidity = code->nz_number()->value(); + m_uidValidity = static_cast (code->nz_number()->value()); break; default: @@ -550,6 +550,109 @@ std::vector > IMAPFolder::getMessages(const std::vector & nu } +ref IMAPFolder::getMessageByUID(const message::uid& uid) +{ + std::vector uids; + uids.push_back(uid); + + std::vector > msgs = getMessagesByUID(uids); + + if (msgs.size() == 0) + throw exceptions::message_not_found(); + + return msgs[0]; +} + + +std::vector > IMAPFolder::getMessagesByUID(const std::vector & uids) +{ + if (!isOpen()) + throw exceptions::illegal_state("Folder not open"); + + if (uids.size() == 0) + return std::vector >(); + + // C: . UID FETCH uuuu1,uuuu2,uuuu3 UID + // S: * nnnn1 FETCH (UID uuuu1) + // S: * nnnn2 FETCH (UID uuuu2) + // S: * nnnn3 FETCH (UID uuuu3) + // S: . OK UID FETCH completed + + // Prepare command and arguments + std::ostringstream cmd; + cmd.imbue(std::locale::classic()); + + cmd << "UID FETCH " << IMAPUtils::extractUIDFromGlobalUID(uids[0]); + + for (unsigned int i = 1, n = uids.size() ; i < n ; ++i) + cmd << "," << IMAPUtils::extractUIDFromGlobalUID(uids[i]); + + cmd << " UID"; + + // Send the request + m_connection->send(true, cmd.str(), true); + + // Get the response + utility::auto_ptr resp(m_connection->readResponse()); + + if (resp->isBad() || resp->response_done()->response_tagged()-> + resp_cond_state()->status() != IMAPParser::resp_cond_state::OK) + { + throw exceptions::command_error("UID FETCH ... UID", m_connection->getParser()->lastLine(), "bad response"); + } + + // Process the response + const std::vector & respDataList = + resp->continue_req_or_response_data(); + + std::vector > messages; + + for (std::vector ::const_iterator + it = respDataList.begin() ; it != respDataList.end() ; ++it) + { + if ((*it)->response_data() == NULL) + { + throw exceptions::command_error("UID FETCH ... UID", + m_connection->getParser()->lastLine(), "invalid response"); + } + + const IMAPParser::message_data* messageData = + (*it)->response_data()->message_data(); + + // We are only interested in responses of type "FETCH" + if (messageData == NULL || messageData->type() != IMAPParser::message_data::FETCH) + continue; + + // Get Process fetch response for this message + const int msgNum = static_cast (messageData->number()); + message::uid msgUID, msgFullUID; + + // Find UID in message attributes + const std::vector atts = messageData->msg_att()->items(); + + for (std::vector ::const_iterator + it = atts.begin() ; it != atts.end() ; ++it) + { + if ((*it)->type() == IMAPParser::msg_att_item::UID) + { + msgFullUID = IMAPUtils::makeGlobalUID(m_uidValidity, (*it)->unique_id()->value()); + msgUID = (*it)->unique_id()->value(); + + break; + } + } + + if (!msgUID.empty()) + { + ref thisFolder = thisRef().dynamicCast (); + messages.push_back(vmime::create (thisFolder, msgNum, msgFullUID)); + } + } + + return messages; +} + + int IMAPFolder::getMessageCount() { if (!isOpen()) @@ -730,7 +833,7 @@ void IMAPFolder::fetchMessages(std::vector >& msg, const int opti if (msg != numberToMsg.end()) { - (*msg).second->processFetchResponse(options, messageData->msg_att()); + (*msg).second->processFetchResponse(options, messageData); if (progress) progress->progress(++current, total); @@ -1781,7 +1884,7 @@ std::vector IMAPFolder::getMessageNumbersStartingOnUID(const message::uid& std::ostringstream command; command.imbue(std::locale::classic()); - command << "SEARCH UID " << uid; + command << "SEARCH UID " << uid << ":*"; // Send the request m_connection->send(true, command.str(), true); diff --git a/src/net/imap/IMAPMessage.cpp b/src/net/imap/IMAPMessage.cpp index 8006920..7202a7d 100644 --- a/src/net/imap/IMAPMessage.cpp +++ b/src/net/imap/IMAPMessage.cpp @@ -98,6 +98,14 @@ IMAPMessage::IMAPMessage(ref folder, const int num) } +IMAPMessage::IMAPMessage(ref folder, const int num, const uid& uniqueId) + : m_folder(folder), m_num(num), m_size(-1), m_flags(FLAG_UNDEFINED), + m_expunged(false), m_uid(uniqueId), m_structure(NULL) +{ + folder->registerMessage(this); +} + + IMAPMessage::~IMAPMessage() { ref folder = m_folder.acquire(); @@ -271,7 +279,11 @@ void IMAPMessage::extract(ref p, utility::outputStream& os, std::ostringstream command; command.imbue(std::locale::classic()); - command << "FETCH " << m_num << " BODY"; + if (m_uid.empty()) + command << "FETCH " << m_num << " BODY"; + else + command << "UID FETCH " << IMAPUtils::extractUIDFromGlobalUID(m_uid) << " BODY"; + if (peek) command << ".PEEK"; command << "["; @@ -361,19 +373,18 @@ void IMAPMessage::fetch(ref msgFolder, const int options) continue; // Process fetch response for this message - processFetchResponse(options, messageData->msg_att()); + processFetchResponse(options, messageData); } } void IMAPMessage::processFetchResponse - (const int options, const IMAPParser::msg_att* msgAtt) + (const int options, const IMAPParser::message_data* msgData) { ref folder = m_folder.acquire(); // Get message attributes - const std::vector atts = - msgAtt->items(); + const std::vector atts = msgData->msg_att()->items(); int flags = 0; @@ -389,12 +400,7 @@ void IMAPMessage::processFetchResponse } case IMAPParser::msg_att_item::UID: { - std::ostringstream oss; - oss.imbue(std::locale::classic()); - - oss << folder->m_uidValidity << ":" << (*it)->unique_id()->value(); - - m_uid = oss.str(); + m_uid = IMAPUtils::makeGlobalUID(folder->m_uidValidity, (*it)->unique_id()->value()); break; } case IMAPParser::msg_att_item::ENVELOPE: diff --git a/src/net/imap/IMAPUtils.cpp b/src/net/imap/IMAPUtils.cpp index 0d6fc47..eceac16 100644 --- a/src/net/imap/IMAPUtils.cpp +++ b/src/net/imap/IMAPUtils.cpp @@ -540,6 +540,24 @@ const string IMAPUtils::listToSet(const std::vector & list, const int max, // static +const string IMAPUtils::listToSet(const std::vector & list) +{ + if (list.size() == 0) + return ""; + + std::ostringstream res; + res.imbue(std::locale::classic()); + + res << extractUIDFromGlobalUID(list[0]); + + for (unsigned int i = 1, n = list.size() ; i < n ; ++i) + res << "," << extractUIDFromGlobalUID(list[i]); + + return res.str(); +} + + +// static const string IMAPUtils::dateTime(const vmime::datetime& date) { std::ostringstream res; @@ -609,7 +627,8 @@ const string IMAPUtils::dateTime(const vmime::datetime& date) // static -const string IMAPUtils::buildFetchRequest(const std::vector & list, const int options) +const string IMAPUtils::buildFetchRequestImpl + (const std::string& mode, const std::string& set, const int options) { // Example: // C: A654 FETCH 2:4 (FLAGS BODY[HEADER.FIELDS (DATE FROM)]) @@ -671,7 +690,10 @@ const string IMAPUtils::buildFetchRequest(const std::vector & list, const i std::ostringstream command; command.imbue(std::locale::classic()); - command << "FETCH " << listToSet(list, -1, false) << " ("; + if (mode == "uid") + command << "UID FETCH " << set << " ("; + else + command << "FETCH " << set << " ("; for (std::vector ::const_iterator it = items.begin() ; it != items.end() ; ++it) @@ -687,6 +709,20 @@ const string IMAPUtils::buildFetchRequest(const std::vector & list, const i // static +const string IMAPUtils::buildFetchRequest(const std::vector & list, const int options) +{ + return buildFetchRequestImpl("number", listToSet(list, -1, false), options); +} + + +// static +const string IMAPUtils::buildFetchRequest(const std::vector & list, const int options) +{ + return buildFetchRequestImpl("uid", listToSet(list), options); +} + + +// static void IMAPUtils::convertAddressList (const IMAPParser::address_list& src, mailboxList& dest) { @@ -706,6 +742,46 @@ void IMAPUtils::convertAddressList } +// static +unsigned int IMAPUtils::extractUIDFromGlobalUID(const message::uid& uid) +{ + message::uid::size_type colonPos = uid.find(':'); + + if (colonPos == message::uid::npos) + { + std::istringstream iss(uid); + iss.imbue(std::locale::classic()); + + unsigned int n = 0; + iss >> n; + + return n; + } + else + { + std::istringstream iss(uid.substr(colonPos + 1)); + iss.imbue(std::locale::classic()); + + unsigned int n = 0; + iss >> n; + + return n; + } +} + + +// static +const message::uid IMAPUtils::makeGlobalUID(const unsigned int UIDValidity, const unsigned int messageUID) +{ + std::ostringstream oss; + oss.imbue(std::locale::classic()); + + oss << UIDValidity << ":" << messageUID; + + return message::uid(oss.str()); +} + + } // imap } // net } // vmime diff --git a/src/net/maildir/maildirFolder.cpp b/src/net/maildir/maildirFolder.cpp index 8c4b275..b606cda 100644 --- a/src/net/maildir/maildirFolder.cpp +++ b/src/net/maildir/maildirFolder.cpp @@ -444,6 +444,18 @@ std::vector > maildirFolder::getMessages(const std::vector & } +ref maildirFolder::getMessageByUID(const message::uid& /* uid */) +{ + throw exceptions::operation_not_supported(); +} + + +std::vector > maildirFolder::getMessagesByUID(const std::vector & /* uids */) +{ + throw exceptions::operation_not_supported(); +} + + int maildirFolder::getMessageCount() { return (m_messageCount); diff --git a/src/net/pop3/POP3Folder.cpp b/src/net/pop3/POP3Folder.cpp index e085609..21e7a8b 100644 --- a/src/net/pop3/POP3Folder.cpp +++ b/src/net/pop3/POP3Folder.cpp @@ -249,6 +249,18 @@ std::vector > POP3Folder::getMessages(const int from, const int t } +ref POP3Folder::getMessageByUID(const message::uid& /* uid */) +{ + throw exceptions::operation_not_supported(); +} + + +std::vector > POP3Folder::getMessagesByUID(const std::vector & /* uids */) +{ + throw exceptions::operation_not_supported(); +} + + std::vector > POP3Folder::getMessages(const std::vector & nums) { ref store = m_store.acquire(); diff --git a/vmime/net/folder.hpp b/vmime/net/folder.hpp index df9cbaf..a50ee0e 100644 --- a/vmime/net/folder.hpp +++ b/vmime/net/folder.hpp @@ -169,7 +169,7 @@ public: */ virtual bool isOpen() const = 0; - /** Get a new reference to a message in this folder. + /** Get a new reference to a message in this folder, given its number. * * @param num message sequence number * @return a new object referencing the specified message @@ -177,7 +177,7 @@ public: */ virtual ref getMessage(const int num) = 0; - /** Get new references to messages in this folder. + /** Get new references to messages in this folder, given their numbers. * * @param from sequence number of the first message to get * @param to sequence number of the last message to get @@ -186,14 +186,30 @@ public: */ virtual std::vector > getMessages(const int from = 1, const int to = -1) = 0; - /** Get new references to messages in this folder. + /** Get new references to messages in this folder, given their numbers. * - * @param nums sequence numbers of the messages to delete + * @param nums sequence numbers of the messages to retrieve * @return new objects referencing the specified messages * @throw net_exception if an error occurs */ virtual std::vector > getMessages(const std::vector & nums) = 0; + /** Get message in this folder, given its UID. + * + * @param uid UID of message to retrieve + * @return a new object referencing the specified message + * @throw net_exception if an error occurs + */ + virtual ref getMessageByUID(const message::uid& uid) = 0; + + /** Get messages in this folder, given their UIDs. + * + * @param uids UIDs of messages to retrieve + * @return new objects referencing the specified messages + * @throw net_exception if an error occurs + */ + virtual std::vector > getMessagesByUID(const std::vector & uids) = 0; + /** Return the number of messages in this folder. * * @return number of messages in the folder diff --git a/vmime/net/imap/IMAPFolder.hpp b/vmime/net/imap/IMAPFolder.hpp index cc52596..3337858 100644 --- a/vmime/net/imap/IMAPFolder.hpp +++ b/vmime/net/imap/IMAPFolder.hpp @@ -84,6 +84,12 @@ public: ref getMessage(const int num); std::vector > getMessages(const int from = 1, const int to = -1); std::vector > getMessages(const std::vector & nums); + + ref getMessageByUID(const message::uid& uid); + std::vector > getMessagesByUID(const std::vector & uids); + + std::vector getMessageNumbersStartingOnUID(const message::uid& uid); + int getMessageCount(); ref getFolder(const folder::path::component& name); @@ -120,8 +126,6 @@ public: int getFetchCapabilities() const; - std::vector getMessageNumbersStartingOnUID(const message::uid& uid); - private: void registerMessage(IMAPMessage* msg); @@ -152,7 +156,7 @@ private: int m_messageCount; - int m_uidValidity; + unsigned int m_uidValidity; std::vector m_messages; }; diff --git a/vmime/net/imap/IMAPMessage.hpp b/vmime/net/imap/IMAPMessage.hpp index edbf69f..fbba6e7 100644 --- a/vmime/net/imap/IMAPMessage.hpp +++ b/vmime/net/imap/IMAPMessage.hpp @@ -50,6 +50,7 @@ private: friend class vmime::creator; // vmime::create IMAPMessage(ref folder, const int num); + IMAPMessage(ref folder, const int num, const uid& uniqueId); IMAPMessage(const IMAPMessage&) : message() { } ~IMAPMessage(); @@ -83,7 +84,7 @@ private: void fetch(ref folder, const int options); - void processFetchResponse(const int options, const IMAPParser::msg_att* msgAtt); + void processFetchResponse(const int options, const IMAPParser::message_data* msgData); /** Recursively fetch part header for all parts in the structure. * diff --git a/vmime/net/imap/IMAPUtils.hpp b/vmime/net/imap/IMAPUtils.hpp index d1ed5c8..9c9c420 100644 --- a/vmime/net/imap/IMAPUtils.hpp +++ b/vmime/net/imap/IMAPUtils.hpp @@ -29,6 +29,7 @@ #include "vmime/dateTime.hpp" #include "vmime/net/folder.hpp" +#include "vmime/net/message.hpp" #include "vmime/net/imap/IMAPParser.hpp" #include "vmime/mailboxList.hpp" @@ -65,8 +66,8 @@ public: static const string messageFlagList(const int flags); - /** Build an "IMAP set" given a list. The function tries to group - * consecutive message numbers to reduce the list. + /** Build an "IMAP set" given a list of message numbers. The function tries + * to group consecutive message numbers to reduce the list. * * Example: * IN = "1,2,3,4,5,7,8,13,15,16,17" @@ -81,6 +82,13 @@ public: static const string listToSet(const std::vector & list, const int max = -1, const bool alreadySorted = false); + /** Build an "IMAP set" set given a list of message UIDs. + * + * @param list list of message UIDs + * @return a set corresponding to the list + */ + static const string listToSet(const std::vector & list); + /** Format a date/time to IMAP date/time format. * * @param date date/time to format @@ -88,7 +96,7 @@ public: */ static const string dateTime(const vmime::datetime& date); - /** Construct a fetch request for the specified messages. + /** Construct a fetch request for the specified messages, designated by their sequence numbers. * * @param list list of message numbers * @param options fetch options @@ -96,12 +104,40 @@ public: */ static const string buildFetchRequest(const std::vector & list, const int options); + /** Construct a fetch request for the specified messages, designated by their UIDs. + * + * @param list list of message UIDs + * @param options fetch options + * @return fetch request + */ + static const string buildFetchRequest(const std::vector & list, const int options); + /** Convert a parser-style address list to a mailbox list. * * @param src input address list * @param dest output mailbox list */ static void convertAddressList(const IMAPParser::address_list& src, mailboxList& dest); + + /** Extract the message UID from a globally unique UID. + * + * @param uid globally unique UID (as returned by makeGlobalUID(), for example) + * @return message UID + */ + static unsigned int extractUIDFromGlobalUID(const message::uid& uid); + + /** Construct a globally unique UID from UID Validity and a message UID. + * + * @param UIDValidity UID Validity of the folder + * @param messageUID UID of the message + * @return global UID + */ + static const message::uid makeGlobalUID(const unsigned int UIDValidity, const unsigned int messageUID); + +private: + + static const string buildFetchRequestImpl + (const std::string& mode, const std::string& set, const int options); }; diff --git a/vmime/net/maildir/maildirFolder.hpp b/vmime/net/maildir/maildirFolder.hpp index 68b5b89..c9ba899 100644 --- a/vmime/net/maildir/maildirFolder.hpp +++ b/vmime/net/maildir/maildirFolder.hpp @@ -85,6 +85,10 @@ public: ref getMessage(const int num); std::vector > getMessages(const int from = 1, const int to = -1); std::vector > getMessages(const std::vector & nums); + + ref getMessageByUID(const message::uid& uid); + std::vector > getMessagesByUID(const std::vector & uids); + int getMessageCount(); ref getFolder(const folder::path::component& name); diff --git a/vmime/net/pop3/POP3Folder.hpp b/vmime/net/pop3/POP3Folder.hpp index c482908..090f948 100644 --- a/vmime/net/pop3/POP3Folder.hpp +++ b/vmime/net/pop3/POP3Folder.hpp @@ -83,6 +83,10 @@ public: ref getMessage(const int num); std::vector > getMessages(const int from = 1, const int to = -1); std::vector > getMessages(const std::vector & nums); + + ref getMessageByUID(const message::uid& uid); + std::vector > getMessagesByUID(const std::vector & uids); + int getMessageCount(); ref getFolder(const folder::path::component& name); -- 1.7.10.4 From 7501f61214b06a35c8fce7772fd48dded2cad335 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Mon, 30 Jul 2012 11:23:23 +0200 Subject: [PATCH 41/42] Fixed compilation warning. diff --git a/vmime/utility/parserInputStreamAdapter.hpp b/vmime/utility/parserInputStreamAdapter.hpp index c24fa44..861e75b 100644 --- a/vmime/utility/parserInputStreamAdapter.hpp +++ b/vmime/utility/parserInputStreamAdapter.hpp @@ -82,7 +82,7 @@ public: m_stream->seek(initialPos); - return (readBytes == 1 ? buffer[0] : 0); + return (readBytes == 1 ? buffer[0] : static_cast (0)); } catch (...) { @@ -101,7 +101,7 @@ public: value_type buffer[1]; const size_type readBytes = m_stream->read(buffer, 1); - return (readBytes == 1 ? buffer[0] : 0); + return (readBytes == 1 ? buffer[0] : static_cast (0)); } /** Check whether the bytes following the current position match -- 1.7.10.4 From f9f9b3bf52c76e1803855d1c44147f68ec9f62f2 Mon Sep 17 00:00:00 2001 From: Vincent Richard Date: Mon, 30 Jul 2012 20:45:17 +0200 Subject: [PATCH 42/42] Fixed body part extraction (only body should be extracted). diff --git a/src/net/imap/IMAPMessage.cpp b/src/net/imap/IMAPMessage.cpp index 7202a7d..808f7d1 100644 --- a/src/net/imap/IMAPMessage.cpp +++ b/src/net/imap/IMAPMessage.cpp @@ -192,7 +192,7 @@ void IMAPMessage::extract(utility::outputStream& os, utility::progressListener* if (!folder) throw exceptions::folder_not_found(); - extract(NULL, os, progress, start, length, false, peek); + extractImpl(NULL, os, progress, start, length, EXTRACT_HEADER | EXTRACT_BODY | (peek ? EXTRACT_PEEK : 0)); } @@ -205,7 +205,7 @@ void IMAPMessage::extractPart if (!folder) throw exceptions::folder_not_found(); - extract(p, os, progress, start, length, false, peek); + extractImpl(p, os, progress, start, length, EXTRACT_HEADER | EXTRACT_BODY | (peek ? EXTRACT_PEEK : 0)); } @@ -219,7 +219,7 @@ void IMAPMessage::fetchPartHeader(ref p) std::ostringstream oss; utility::outputStreamAdapter ossAdapter(oss); - extract(p, ossAdapter, NULL, 0, -1, true, true); + extractImpl(p, ossAdapter, NULL, 0, -1, EXTRACT_HEADER | EXTRACT_PEEK); p.dynamicCast ()->getOrCreateHeader().parse(oss.str()); } @@ -240,9 +240,9 @@ void IMAPMessage::fetchPartHeaderForStructure(ref str) } -void IMAPMessage::extract(ref p, utility::outputStream& os, +void IMAPMessage::extractImpl(ref p, utility::outputStream& os, utility::progressListener* progress, const int start, - const int length, const bool headerOnly, const bool peek) const + const int length, const int extractFlags) const { ref folder = m_folder.acquire(); @@ -284,18 +284,45 @@ void IMAPMessage::extract(ref p, utility::outputStream& os, else command << "UID FETCH " << IMAPUtils::extractUIDFromGlobalUID(m_uid) << " BODY"; - if (peek) command << ".PEEK"; + /* + BODY[] header + body + BODY.PEEK[] header + body (peek) + BODY[HEADER] header + BODY.PEEK[HEADER] header (peek) + BODY[TEXT] body + BODY.PEEK[TEXT] body (peek) + */ + + if (extractFlags & EXTRACT_PEEK) + command << ".PEEK"; + command << "["; if (section.str().empty()) { - if (headerOnly) + // header + body + if ((extractFlags & EXTRACT_HEADER) && (extractFlags & EXTRACT_BODY)) + command << ""; + // body only + else if (extractFlags & EXTRACT_BODY) + command << "TEXT"; + // header only + else if (extractFlags & EXTRACT_HEADER) command << "HEADER"; } else { command << section.str(); - if (headerOnly) command << ".MIME"; // "MIME" not "HEADER" for parts + + // header + body + if ((extractFlags & EXTRACT_HEADER) && (extractFlags & EXTRACT_BODY)) + *((int *) 0)=42;//throw exceptions::operation_not_supported(); + // body only + else if (extractFlags & EXTRACT_BODY) + command << ".TEXT"; + // header only + else if (extractFlags & EXTRACT_HEADER) + command << ".MIME"; // "MIME" not "HEADER" for parts } command << "]"; @@ -318,7 +345,7 @@ void IMAPMessage::extract(ref p, utility::outputStream& os, } - if (!headerOnly) + if (extractFlags & EXTRACT_BODY) { // TODO: update the flags (eg. flag "\Seen" may have been set) } diff --git a/src/net/imap/IMAPMessagePartContentHandler.cpp b/src/net/imap/IMAPMessagePartContentHandler.cpp index 85c6ec2..c2cd647 100644 --- a/src/net/imap/IMAPMessagePartContentHandler.cpp +++ b/src/net/imap/IMAPMessagePartContentHandler.cpp @@ -121,7 +121,7 @@ void IMAPMessagePartContentHandler::extract // No decoding to perform if (!isEncoded()) { - msg->extractPart(part, os, progress); + msg->extractImpl(part, os, progress, 0, -1, IMAPMessage::EXTRACT_BODY); } // Need to decode data else @@ -130,7 +130,7 @@ void IMAPMessagePartContentHandler::extract std::ostringstream oss; utility::outputStreamAdapter tmp(oss); - msg->extractPart(part, tmp, NULL); + msg->extractImpl(part, tmp, NULL, 0, -1, IMAPMessage::EXTRACT_BODY); // Encode temporary buffer to output stream utility::inputStreamStringAdapter is(oss.str()); diff --git a/vmime/net/imap/IMAPMessage.hpp b/vmime/net/imap/IMAPMessage.hpp index fbba6e7..06f8091 100644 --- a/vmime/net/imap/IMAPMessage.hpp +++ b/vmime/net/imap/IMAPMessage.hpp @@ -47,6 +47,7 @@ class IMAPMessage : public message private: friend class IMAPFolder; + friend class IMAPMessagePartContentHandler; friend class vmime::creator; // vmime::create IMAPMessage(ref folder, const int num); @@ -101,7 +102,16 @@ private: */ void constructParsedMessage(ref parentPart, ref str, int level = 0); - void extract(ref p, utility::outputStream& os, utility::progressListener* progress, const int start, const int length, const bool headerOnly, const bool peek) const; + + enum ExtractFlags + { + EXTRACT_HEADER = 0x1, + EXTRACT_BODY = 0x2, + EXTRACT_PEEK = 0x10 + }; + + void extractImpl(ref p, utility::outputStream& os, utility::progressListener* progress, + const int start, const int length, const int extractFlags) const; ref
getOrCreateHeader(); -- 1.7.10.4