diff --git a/CHANGELOG b/CHANGELOG
index 37b143ac5..61734faca 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -21,6 +21,7 @@ Release 1.5.2 (2013-03-??)
- fixed GH #118: JSON::Object::stringify endless loop
- added Recursive and SortedDirectoryIterator (Marian Krivos)
- added ListMap (map-like container with preserving insertion order)
+- MailMessage: attachments saving support and consistent read/write
Release 1.5.1 (2013-01-11)
==========================
diff --git a/Net/Net_CE_vs90.vcproj b/Net/Net_CE_vs90.vcproj
index 47a58b2ec..f3bc53560 100644
--- a/Net/Net_CE_vs90.vcproj
+++ b/Net/Net_CE_vs90.vcproj
@@ -554,6 +554,8 @@
RelativePath=".\include\Poco\Net\PartHandler.h"/>
+
+
+
@@ -374,6 +375,7 @@
+
diff --git a/Net/Net_vs100.vcxproj.filters b/Net/Net_vs100.vcxproj.filters
index 5b0a5cf62..3d2e72cae 100644
--- a/Net/Net_vs100.vcxproj.filters
+++ b/Net/Net_vs100.vcxproj.filters
@@ -423,6 +423,9 @@
NetCore\Header Files
+
+ Messages\Header Files
+
@@ -701,6 +704,9 @@
NetCore\Source Files
+
+ Messages\Source Files
+
diff --git a/Net/Net_vs110.vcxproj b/Net/Net_vs110.vcxproj
index 7d99b806d..43fc6dbe7 100644
--- a/Net/Net_vs110.vcxproj
+++ b/Net/Net_vs110.vcxproj
@@ -304,6 +304,7 @@
+
@@ -403,6 +404,7 @@
+
diff --git a/Net/Net_vs110.vcxproj.filters b/Net/Net_vs110.vcxproj.filters
index 7bca18a6b..b6e78d737 100644
--- a/Net/Net_vs110.vcxproj.filters
+++ b/Net/Net_vs110.vcxproj.filters
@@ -222,6 +222,9 @@
Messages\Header Files
+
+ Messages\Header Files
+
Messages\Header Files
@@ -515,6 +518,9 @@
Messages\Source Files
+
+ Messages\Source Files
+
Messages\Source Files
diff --git a/Net/Net_vs71.vcproj b/Net/Net_vs71.vcproj
index 440c65da4..4ce770675 100644
--- a/Net/Net_vs71.vcproj
+++ b/Net/Net_vs71.vcproj
@@ -486,6 +486,8 @@
RelativePath=".\include\Poco\Net\PartHandler.h"/>
+
+
+
+
+
+
+
@@ -395,6 +396,7 @@
+
diff --git a/Net/Net_x64_vs100.vcxproj.filters b/Net/Net_x64_vs100.vcxproj.filters
index 392d423af..f86ba9bfe 100644
--- a/Net/Net_x64_vs100.vcxproj.filters
+++ b/Net/Net_x64_vs100.vcxproj.filters
@@ -219,6 +219,9 @@
Messages\Header Files
+
+ Messages\Header Files
+
Messages\Header Files
@@ -512,6 +515,9 @@
Messages\Source Files
+
+ Messages\Source Files
+
Messages\Source Files
diff --git a/Net/Net_x64_vs110.vcxproj b/Net/Net_x64_vs110.vcxproj
index 810f6dfc9..f68514ef3 100644
--- a/Net/Net_x64_vs110.vcxproj
+++ b/Net/Net_x64_vs110.vcxproj
@@ -302,6 +302,7 @@
+
@@ -401,6 +402,7 @@
+
diff --git a/Net/Net_x64_vs110.vcxproj.filters b/Net/Net_x64_vs110.vcxproj.filters
index 18dd8f2dd..3f97fdc73 100644
--- a/Net/Net_x64_vs110.vcxproj.filters
+++ b/Net/Net_x64_vs110.vcxproj.filters
@@ -222,6 +222,9 @@
Messages\Header Files
+
+ Messages\Header Files
+
Messages\Header Files
@@ -515,6 +518,9 @@
Messages\Source Files
+
+ Messages\Source Files
+
Messages\Source Files
diff --git a/Net/Net_x64_vs90.vcproj b/Net/Net_x64_vs90.vcproj
index d087f6a66..9e42a53fa 100644
--- a/Net/Net_x64_vs90.vcproj
+++ b/Net/Net_x64_vs90.vcproj
@@ -511,6 +511,8 @@
RelativePath=".\include\Poco\Net\PartHandler.h"/>
+
+
@@ -86,12 +87,28 @@ public:
ENCODING_BASE64
};
- MailMessage();
+ struct Part
+ {
+ std::string name;
+ PartSource* pSource;
+ ContentDisposition disposition;
+ ContentTransferEncoding encoding;
+ };
+
+ typedef std::vector PartVec;
+
+ MailMessage(PartStoreFactory* pStoreFactory = 0);
/// Creates an empty MailMessage.
+ ///
+ /// If pStoreFactory is not null, message attachments will be
+ /// handled by the object created by the factory. Most
+ /// common reason is to temporarily save attachments to
+ /// the file system in order to avoid potential memory
+ /// exhaustion when attachment files are very large.
virtual ~MailMessage();
/// Destroys the MailMessage.
-
+
void addRecipient(const MailRecipient& recipient);
/// Adds a recipient for the message.
@@ -164,7 +181,10 @@ public:
bool isMultipart() const;
/// Returns true iff the message is a multipart message.
- void addPart(const std::string& name, PartSource* pSource, ContentDisposition disposition, ContentTransferEncoding encoding);
+ void addPart(const std::string& name,
+ PartSource* pSource,
+ ContentDisposition disposition,
+ ContentTransferEncoding encoding);
/// Adds a part/attachment to the mail message.
///
/// The MailMessage takes ownership of the PartSource and deletes it
@@ -178,7 +198,8 @@ public:
/// To include non-ASCII characters in the part name or filename,
/// use RFC 2047 word encoding (see encodeWord()).
- void addContent(PartSource* pSource, ContentTransferEncoding encoding = ENCODING_QUOTED_PRINTABLE);
+ void addContent(PartSource* pSource,
+ ContentTransferEncoding encoding = ENCODING_QUOTED_PRINTABLE);
/// Adds a part to the mail message by calling
/// addPart("", pSource, CONTENT_INLINE, encoding);
///
@@ -186,8 +207,10 @@ public:
/// must not contain any non-ASCII characters.
/// To include non-ASCII characters in the part name or filename,
/// use RFC 2047 word encoding (see encodeWord()).
-
- void addAttachment(const std::string& name, PartSource* pSource, ContentTransferEncoding encoding = ENCODING_BASE64);
+
+ void addAttachment(const std::string& name,
+ PartSource* pSource,
+ ContentTransferEncoding encoding = ENCODING_BASE64);
/// Adds an attachment to the mail message by calling
/// addPart(name, pSource, CONTENT_ATTACHMENT, encoding);
///
@@ -196,6 +219,19 @@ public:
/// To include non-ASCII characters in the part name or filename,
/// use RFC 2047 word encoding (see encodeWord()).
+ PartSource* getPartStore(const std::string& content,
+ const std::string& mediaType,
+ const std::string& filename = "");
+ /// Returns either built-in default (StringPartSource) part store or,
+ /// if the part store factory was provided during contruction,
+ /// the one created by PartStoreFactory.
+ /// Returned part store is allocated on the heap; it is caller's responsibility
+ /// to delete it after use. Typical use is handler passing it back to MailMessage,
+ /// which takes care of the cleanup.
+
+ const PartVec& parts() const;
+ /// Returns const reference to the vector containing part stores.
+
void read(std::istream& istr, PartHandler& handler);
/// Reads the MailMessage from the given input stream.
///
@@ -212,7 +248,7 @@ public:
void write(std::ostream& ostr) const;
/// Writes the mail message to the given output stream.
-
+
static std::string encodeWord(const std::string& text, const std::string& charset = "UTF-8");
/// If the given string contains non-ASCII characters,
/// encodes the given string using RFC 2047 "Q" word encoding.
@@ -223,15 +259,25 @@ public:
/// Returns the encoded string, or the original string if it
/// consists only of ASCII characters.
+ static const std::string HEADER_SUBJECT;
+ static const std::string HEADER_FROM;
+ static const std::string HEADER_TO;
+ static const std::string HEADER_CC;
+ static const std::string HEADER_BCC;
+ static const std::string HEADER_DATE;
+ static const std::string HEADER_CONTENT_TYPE;
+ static const std::string HEADER_CONTENT_TRANSFER_ENCODING;
+ static const std::string HEADER_CONTENT_DISPOSITION;
+ static const std::string HEADER_CONTENT_ID;
+ static const std::string HEADER_MIME_VERSION;
+ static const std::string EMPTY_HEADER;
+ static const std::string TEXT_PLAIN;
+ static const std::string CTE_7BIT;
+ static const std::string CTE_8BIT;
+ static const std::string CTE_QUOTED_PRINTABLE;
+ static const std::string CTE_BASE64;
+
protected:
- struct Part
- {
- std::string name;
- PartSource* pSource;
- ContentDisposition disposition;
- ContentTransferEncoding encoding;
- };
- typedef std::vector PartVec;
void makeMultipart();
void writeHeader(const MessageHeader& header, std::ostream& ostr) const;
@@ -247,23 +293,6 @@ protected:
static int lineLength(const std::string& str);
static void appendRecipient(const MailRecipient& recipient, std::string& str);
- static const std::string HEADER_SUBJECT;
- static const std::string HEADER_FROM;
- static const std::string HEADER_TO;
- static const std::string HEADER_CC;
- static const std::string HEADER_BCC;
- static const std::string HEADER_DATE;
- static const std::string HEADER_CONTENT_TYPE;
- static const std::string HEADER_CONTENT_TRANSFER_ENCODING;
- static const std::string HEADER_CONTENT_DISPOSITION;
- static const std::string HEADER_MIME_VERSION;
- static const std::string EMPTY_HEADER;
- static const std::string TEXT_PLAIN;
- static const std::string CTE_7BIT;
- static const std::string CTE_8BIT;
- static const std::string CTE_QUOTED_PRINTABLE;
- static const std::string CTE_BASE64;
-
private:
MailMessage(const MailMessage&);
MailMessage& operator = (const MailMessage&);
@@ -272,6 +301,8 @@ private:
PartVec _parts;
std::string _content;
ContentTransferEncoding _encoding;
+ mutable std::string _boundary;
+ PartStoreFactory* _pStoreFactory;
};
@@ -290,6 +321,12 @@ inline const std::string& MailMessage::getContent() const
}
+inline const MailMessage::PartVec& MailMessage::parts() const
+{
+ return _parts;
+}
+
+
} } // namespace Poco::Net
diff --git a/Net/include/Poco/Net/PartSource.h b/Net/include/Poco/Net/PartSource.h
index 39b1c2085..acb4bfdc8 100644
--- a/Net/include/Poco/Net/PartSource.h
+++ b/Net/include/Poco/Net/PartSource.h
@@ -60,7 +60,7 @@ public:
///
/// Subclasses must override this method.
- virtual const std::string& filename();
+ virtual const std::string& filename() const;
/// Returns the filename for the part or attachment.
///
/// May be overridded by subclasses. The default
diff --git a/Net/include/Poco/Net/PartStore.h b/Net/include/Poco/Net/PartStore.h
new file mode 100644
index 000000000..fb776fdd4
--- /dev/null
+++ b/Net/include/Poco/Net/PartStore.h
@@ -0,0 +1,128 @@
+//
+// PartStore.h
+//
+// $Id: //poco/1.4/Net/include/Poco/Net/PartStore.h#1 $
+//
+// Library: Net
+// Package: Messages
+// Module: PartStore
+//
+// Definition of the PartStore class.
+//
+// Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH.
+// and Contributors.
+//
+// Permission is hereby granted, free of charge, to any person or organization
+// obtaining a copy of the software and accompanying documentation covered by
+// this license (the "Software") to use, reproduce, display, distribute,
+// execute, and transmit the Software, and to prepare derivative works of the
+// Software, and to permit third-parties to whom the Software is furnished to
+// do so, all subject to the following:
+//
+// The copyright notices in the Software and this entire statement, including
+// the above license grant, this restriction and the following disclaimer,
+// must be included in all copies of the Software, in whole or in part, and
+// all derivative works of the Software, unless such copies or derivative
+// works are solely in the form of machine-executable object code generated by
+// a source language processor.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+//
+
+
+#ifndef Net_PartStore_INCLUDED
+#define Net_PartStore_INCLUDED
+
+
+#include "Poco/Net/Net.h"
+#include "Poco/Net/PartSource.h"
+#include "Poco/FileStream.h"
+
+
+namespace Poco {
+namespace Net {
+
+
+class Net_API PartStore: public PartSource
+ /// A parent class for part stores storing message parts.
+{
+public:
+ PartStore(const std::string& mediaType);
+ /// Creates the PartStore for the given MIME type.
+
+ ~PartStore();
+ /// Destroys the PartFileStore.
+
+private:
+ PartStore();
+};
+
+
+class Net_API FilePartStore: public PartStore
+ /// An implementation of PartSource for persisting
+ /// parts (usually email attachment files) to the file system.
+{
+public:
+ FilePartStore(const std::string& content, const std::string& mediaType, const std::string& filename = "");
+ /// Creates the FilePartStore for the given MIME type.
+ /// For security purposes, attachment filename is NOT used to save file to the file system.
+ /// A unique temporary file name is used to persist the file.
+ /// The given filename parameter is the message part (attachment) filename (see filename()) only.
+ ///
+ /// Throws an exception if the file cannot be opened.
+
+ ~FilePartStore();
+ /// Destroys the FilePartStore.
+
+ std::istream& stream();
+ /// Returns a file input stream for the given file.
+
+ const std::string& filename() const;
+ /// Returns the filename portion of the path.
+ /// This is the name under which the file is known
+ /// to the user of this class (typically, MailMessage
+ /// class). The real name of the file as saved
+ /// to the filesystem can be obtained by calling
+ /// path() member function.
+
+ const std::string& path() const;
+ /// Returns the full path to the file as saved
+ /// to the file system. For security reasons,
+ /// file is not saved under the real file name
+ /// (as specified by the user).
+
+private:
+ std::string _filename;
+ std::string _path;
+ Poco::FileStream _fstr;
+};
+
+
+class PartStoreFactory
+ /// Parent factory class for part stores creation.
+{
+public:
+ virtual PartSource* createPartStore(const std::string& content, const std::string& mediaType, const std::string& filename = "") = 0;
+};
+
+
+class FilePartStoreFactory: public PartStoreFactory
+{
+public:
+ PartSource* createPartStore(const std::string& content, const std::string& mediaType, const std::string& filename = "")
+ {
+ return new FilePartStore(content, mediaType, filename);
+ }
+};
+
+
+} } // namespace Poco::Net
+
+
+#endif // Net_PartStore_INCLUDED
diff --git a/Net/include/Poco/Net/StringPartSource.h b/Net/include/Poco/Net/StringPartSource.h
index 9b77953eb..92bbaac22 100644
--- a/Net/include/Poco/Net/StringPartSource.h
+++ b/Net/include/Poco/Net/StringPartSource.h
@@ -72,7 +72,7 @@ public:
std::istream& stream();
/// Returns a string input stream for the string.
- const std::string& filename();
+ const std::string& filename() const;
/// Returns the filename portion of the path.
private:
diff --git a/Net/src/MailMessage.cpp b/Net/src/MailMessage.cpp
index e60bc00d8..88ba24704 100644
--- a/Net/src/MailMessage.cpp
+++ b/Net/src/MailMessage.cpp
@@ -40,8 +40,10 @@
#include "Poco/Net/MultipartWriter.h"
#include "Poco/Net/PartSource.h"
#include "Poco/Net/PartHandler.h"
+#include "Poco/Net/StringPartSource.h"
#include "Poco/Net/QuotedPrintableEncoder.h"
#include "Poco/Net/QuotedPrintableDecoder.h"
+#include "Poco/Net/NameValueCollection.h"
#include "Poco/Base64Encoder.h"
#include "Poco/Base64Decoder.h"
#include "Poco/StreamCopier.h"
@@ -49,6 +51,7 @@
#include "Poco/DateTimeFormatter.h"
#include "Poco/DateTimeParser.h"
#include "Poco/String.h"
+#include "Poco/StringTokenizer.h"
#include "Poco/StreamCopier.h"
#include "Poco/NumberFormatter.h"
#include
@@ -60,6 +63,7 @@ using Poco::StreamCopier;
using Poco::DateTimeFormat;
using Poco::DateTimeFormatter;
using Poco::DateTimeParser;
+using Poco::StringTokenizer;
using Poco::icompare;
@@ -69,21 +73,107 @@ namespace Net {
namespace
{
- class StringPartHandler: public PartHandler
+ class MultiPartHandler: public PartHandler
+ /// This is a default part handler for multipart messages, used when there
+ /// is no external handler provided to he MailMessage. This handler
+ /// will handle all types of message parts, including attachments.
{
public:
- StringPartHandler(std::string& content):
- _str(content)
+ MultiPartHandler(MailMessage* pMsg): _pMsg(pMsg)
+ /// Creates multi part handler.
+ /// The pMsg pointer points to the calling MailMessage
+ /// and will be used to properly populate it, so the
+ /// message content could be written out unmodified
+ /// in its entirety, including attachments.
{
}
- ~StringPartHandler()
+ ~MultiPartHandler()
+ /// Destroys string part handler.
{
}
void handlePart(const MessageHeader& header, std::istream& stream)
+ /// Handles a part. If message pointer was provided at construction time,
+ /// the message pointed to will be properly populated so it could be written
+ /// back out at a later point in time.
{
- Poco::StreamCopier::copyToString(stream, _str);
+ std::string tmp;
+ Poco::StreamCopier::copyToString(stream, tmp);
+ if (_pMsg)
+ {
+
+ MailMessage::ContentTransferEncoding cte = MailMessage::ENCODING_7BIT;
+ std::string enc = header[MailMessage::HEADER_CONTENT_TRANSFER_ENCODING];
+ if (enc == MailMessage::CTE_8BIT)
+ cte = MailMessage::ENCODING_8BIT;
+ else if (enc == MailMessage::CTE_QUOTED_PRINTABLE)
+ cte = MailMessage::ENCODING_QUOTED_PRINTABLE;
+ else if (enc == MailMessage::CTE_BASE64)
+ cte = MailMessage::ENCODING_BASE64;
+
+ NameValueCollection::ConstIterator it = header.begin();
+ NameValueCollection::ConstIterator end = header.end();
+ PartSource* pPS = _pMsg->getPartStore(tmp,
+ header[MailMessage::HEADER_CONTENT_TYPE],
+ getFileNameFromDisp(it->second));
+ poco_check_ptr (pPS);
+ for (; it != end; ++it)
+ {
+ if (MailMessage::HEADER_CONTENT_DISPOSITION == it->first)
+ {
+ if (it->second == "inline") _pMsg->addContent(pPS, cte);
+ else _pMsg->addAttachment("", pPS, cte);
+ }
+
+ pPS->headers().set(it->first, it->second);
+ }
+ }
+ }
+
+ private:
+ std::string getFileNameFromDisp(const std::string& str)
+ {
+ StringTokenizer st(str, ";=", StringTokenizer::TOK_IGNORE_EMPTY | StringTokenizer::TOK_TRIM);
+ StringTokenizer::Iterator it = st.begin();
+ StringTokenizer::Iterator end = st.end();
+ for (; it != end; ++it) { if (*it == "filename") break; }
+ if (it != end)
+ {
+ ++it;
+ if (it == end) return "";
+ return *it;
+ }
+ return "";
+ }
+
+ MailMessage* _pMsg;
+ };
+
+
+ class StringPartHandler: public PartHandler
+ /// This is a default part handler, used when there is no
+ /// external handler provided to the MailMessage. This handler
+ /// handles only single-part messages.
+ {
+ public:
+ StringPartHandler(std::string& content): _str(content)
+ /// Creates string part handler.
+ /// The content parameter represents the part content.
+ {
+ }
+
+ ~StringPartHandler()
+ /// Destroys string part handler.
+ {
+ }
+
+ void handlePart(const MessageHeader& header, std::istream& stream)
+ /// Handles a part.
+ {
+ std::string tmp;
+ Poco::StreamCopier::copyToString(stream, tmp);
+ _str.append(tmp);
}
private:
@@ -101,6 +191,7 @@ const std::string MailMessage::HEADER_DATE("Date");
const std::string MailMessage::HEADER_CONTENT_TYPE("Content-Type");
const std::string MailMessage::HEADER_CONTENT_TRANSFER_ENCODING("Content-Transfer-Encoding");
const std::string MailMessage::HEADER_CONTENT_DISPOSITION("Content-Disposition");
+const std::string MailMessage::HEADER_CONTENT_ID("Content-ID");
const std::string MailMessage::HEADER_MIME_VERSION("Mime-Version");
const std::string MailMessage::EMPTY_HEADER;
const std::string MailMessage::TEXT_PLAIN("text/plain");
@@ -110,7 +201,8 @@ const std::string MailMessage::CTE_QUOTED_PRINTABLE("quoted-printable");
const std::string MailMessage::CTE_BASE64("base64");
-MailMessage::MailMessage()
+MailMessage::MailMessage(PartStoreFactory* pStoreFactory):
+ _pStoreFactory(pStoreFactory)
{
Poco::Timestamp now;
setDate(now);
@@ -263,8 +355,16 @@ void MailMessage::read(std::istream& istr, PartHandler& handler)
void MailMessage::read(std::istream& istr)
{
readHeader(istr);
- StringPartHandler handler(_content);
- readPart(istr, *this, handler);
+ if (isMultipart())
+ {
+ MultiPartHandler handler(this);
+ readMultipart(istr, handler);
+ }
+ else
+ {
+ StringPartHandler handler(_content);
+ readPart(istr, *this, handler);
+ }
}
@@ -304,14 +404,14 @@ void MailMessage::writeHeader(const MessageHeader& header, std::ostream& ostr) c
void MailMessage::writeMultipart(MessageHeader& header, std::ostream& ostr) const
{
- std::string boundary(MultipartWriter::createBoundary());
+ if (_boundary.empty()) _boundary = MultipartWriter::createBoundary();
MediaType mediaType(getContentType());
- mediaType.setParameter("boundary", boundary);
+ mediaType.setParameter("boundary", _boundary);
header.set(HEADER_CONTENT_TYPE, mediaType.toString());
header.set(HEADER_MIME_VERSION, "1.0");
writeHeader(header, ostr);
- MultipartWriter writer(ostr, boundary);
+ MultipartWriter writer(ostr, _boundary);
for (PartVec::const_iterator it = _parts.begin(); it != _parts.end(); ++it)
{
writePart(writer, *it);
@@ -384,8 +484,8 @@ void MailMessage::readHeader(std::istream& istr)
void MailMessage::readMultipart(std::istream& istr, PartHandler& handler)
{
MediaType contentType(getContentType());
- std::string boundary = contentType.getParameter("boundary");
- MultipartReader reader(istr, boundary);
+ _boundary = contentType.getParameter("boundary");
+ MultipartReader reader(istr, _boundary);
while (reader.hasNextPart())
{
MessageHeader partHeader;
@@ -580,4 +680,11 @@ std::string MailMessage::encodeWord(const std::string& text, const std::string&
}
+PartSource* MailMessage::getPartStore(const std::string& content, const std::string& mediaType, const std::string& filename)
+{
+ if (!_pStoreFactory) return new StringPartSource(content, mediaType, filename);
+ else return _pStoreFactory->createPartStore(content, mediaType, filename);
+}
+
+
} } // namespace Poco::Net
diff --git a/Net/src/PartSource.cpp b/Net/src/PartSource.cpp
index a09f6ebf1..1bb1c1be2 100644
--- a/Net/src/PartSource.cpp
+++ b/Net/src/PartSource.cpp
@@ -64,7 +64,7 @@ namespace
}
-const std::string& PartSource::filename()
+const std::string& PartSource::filename() const
{
return EMPTY;
}
diff --git a/Net/src/PartStore.cpp b/Net/src/PartStore.cpp
new file mode 100644
index 000000000..f144ec45d
--- /dev/null
+++ b/Net/src/PartStore.cpp
@@ -0,0 +1,103 @@
+//
+// PartStore.cpp
+//
+// $Id: //poco/1.4/Net/src/PartStore.cpp#1 $
+//
+// Library: Net
+// Package: Messages
+// Module: PartStore
+//
+// Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH.
+// and Contributors.
+//
+// Permission is hereby granted, free of charge, to any person or organization
+// obtaining a copy of the software and accompanying documentation covered by
+// this license (the "Software") to use, reproduce, display, distribute,
+// execute, and transmit the Software, and to prepare derivative works of the
+// Software, and to permit third-parties to whom the Software is furnished to
+// do so, all subject to the following:
+//
+// The copyright notices in the Software and this entire statement, including
+// the above license grant, this restriction and the following disclaimer,
+// must be included in all copies of the Software, in whole or in part, and
+// all derivative works of the Software, unless such copies or derivative
+// works are solely in the form of machine-executable object code generated by
+// a source language processor.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
+// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
+// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
+// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+// DEALINGS IN THE SOFTWARE.
+//
+
+
+#include "Poco/Net/PartStore.h"
+#include "Poco/TemporaryFile.h"
+#include "Poco/File.h"
+#include "Poco/Exception.h"
+
+
+namespace Poco {
+namespace Net {
+
+
+/// PartStore
+
+PartStore::PartStore(const std::string& mediaType): PartSource(mediaType)
+{
+}
+
+
+PartStore::~PartStore()
+{
+}
+
+
+/// FilePartStore
+
+FilePartStore::FilePartStore(const std::string& content, const std::string& mediaType, const std::string& filename):
+ PartStore(mediaType),
+ _filename(filename),
+ _path(TemporaryFile::tempName()),
+ _fstr(_path)
+{
+ _fstr << content << std::flush;
+ _fstr.seekg(0, std::ios::beg);
+}
+
+
+FilePartStore::~FilePartStore()
+{
+ try
+ {
+ _fstr.close();
+ File(_path).remove();
+ }
+ catch (Exception&)
+ {
+ }
+}
+
+
+std::istream& FilePartStore::stream()
+{
+ return _fstr;
+}
+
+
+const std::string& FilePartStore::filename() const
+{
+ return _filename;
+}
+
+
+const std::string& FilePartStore::path() const
+{
+ return _path;
+}
+
+
+} } // namespace Poco::Net
diff --git a/Net/src/StringPartSource.cpp b/Net/src/StringPartSource.cpp
index b2eb4260a..eb391ab81 100644
--- a/Net/src/StringPartSource.cpp
+++ b/Net/src/StringPartSource.cpp
@@ -74,7 +74,7 @@ std::istream& StringPartSource::stream()
}
-const std::string& StringPartSource::filename()
+const std::string& StringPartSource::filename() const
{
return _filename;
}
diff --git a/Net/testsuite/src/MailMessageTest.cpp b/Net/testsuite/src/MailMessageTest.cpp
index cfe137c1c..d58fcce74 100644
--- a/Net/testsuite/src/MailMessageTest.cpp
+++ b/Net/testsuite/src/MailMessageTest.cpp
@@ -37,8 +37,11 @@
#include "Poco/Net/MailRecipient.h"
#include "Poco/Net/PartHandler.h"
#include "Poco/Net/StringPartSource.h"
+#include "Poco/Net/PartStore.h"
#include "Poco/Net/MediaType.h"
#include "Poco/Timestamp.h"
+#include "Poco/FileStream.h"
+#include "Poco/String.h"
#include
#include
@@ -49,7 +52,12 @@ using Poco::Net::MessageHeader;
using Poco::Net::PartHandler;
using Poco::Net::MediaType;
using Poco::Net::StringPartSource;
+using Poco::Net::FilePartStoreFactory;
+using Poco::Net::FilePartStore;
using Poco::Timestamp;
+using Poco::FileInputStream;
+using Poco::replaceInPlace;
+using Poco::icompare;
namespace
@@ -135,6 +143,7 @@ void MailMessageTest::testWriteQP()
std::ostringstream str;
message.write(str);
std::string s = str.str();
+
assert (s ==
"Date: Thu, 1 Jan 1970 00:00:00 GMT\r\n"
"Content-Type: text/plain\r\n"
@@ -302,9 +311,9 @@ void MailMessageTest::testWriteMultiPart()
"VGhpcyBpcyBzb21lIGJpbmFyeSBkYXRhLiBSZWFsbHku\r\n"
"--$--\r\n"
);
- std::string::size_type p2 = s.rfind("--");
- std::string::size_type p1 = s.rfind("--", p2 - 1);
- std::string boundary(s, p1 + 2, p2 - 2 - p1);
+ std::string::size_type p1 = s.find('=') + 1;
+ std::string::size_type p2 = s.find('\r', p1);
+ std::string boundary(s, p1, p2 - p1);
std::string msg;
for (std::string::const_iterator it = rawMsg.begin(); it != rawMsg.end(); ++it)
{
@@ -313,6 +322,7 @@ void MailMessageTest::testWriteMultiPart()
else
msg += *it;
}
+
assert (s == msg);
}
@@ -416,6 +426,111 @@ void MailMessageTest::testReadMultiPart()
}
+void MailMessageTest::testReadWriteMultiPart()
+{
+ std::string msgin(
+ "Content-Type: multipart/mixed; boundary=MIME_boundary_31E8A8D61DF53389\r\n"
+ "Date: Thu, 1 Jan 1970 00:00:00 GMT\r\n"
+ "From: poco@appinf.com\r\n"
+ "Mime-Version: 1.0\r\n"
+ "Subject: Test Message\r\n"
+ "To: John Doe \r\n"
+ "\r\n"
+ "--MIME_boundary_31E8A8D61DF53389\r\n"
+ "Content-Disposition: inline\r\n"
+ "Content-Transfer-Encoding: 8bit\r\n"
+ "Content-Type: text/plain\r\n"
+ "\r\n"
+ "Hello World!\r\n"
+ "\r\n"
+ "--MIME_boundary_31E8A8D61DF53389\r\n"
+ "Content-Disposition: attachment; filename=sample.dat\r\n"
+ "Content-ID: abcd1234\r\n"
+ "Content-Transfer-Encoding: base64\r\n"
+ "Content-Type: application/octet-stream; name=sample\r\n"
+ "\r\n"
+ "VGhpcyBpcyBzb21lIGJpbmFyeSBkYXRhLiBSZWFsbHku\r\n"
+ "--MIME_boundary_31E8A8D61DF53389--\r\n"
+ );
+
+ std::istringstream istr(msgin);
+ std::ostringstream ostr;
+ MailMessage message;
+
+ message.read(istr);
+ message.write(ostr);
+
+ std::string msgout(ostr.str());
+ assert (msgout == msgin);
+}
+
+
+void MailMessageTest::testReadWriteMultiPartStore()
+{
+ std::string msgin(
+ "Content-Type: multipart/mixed; boundary=MIME_boundary_31E8A8D61DF53389\r\n"
+ "Date: Thu, 1 Jan 1970 00:00:00 GMT\r\n"
+ "From: poco@appinf.com\r\n"
+ "Mime-Version: 1.0\r\n"
+ "Subject: Test Message\r\n"
+ "To: John Doe \r\n"
+ "\r\n"
+ "--MIME_boundary_31E8A8D61DF53389\r\n"
+ "Content-Disposition: inline\r\n"
+ "Content-Transfer-Encoding: 8bit\r\n"
+ "Content-Type: text/plain\r\n"
+ "\r\n"
+ "Hello World!\r\n"
+ "\r\n"
+ "--MIME_boundary_31E8A8D61DF53389\r\n"
+ "Content-Disposition: attachment; filename=sample.dat\r\n"
+ "Content-ID: abcd1234\r\n"
+ "Content-Transfer-Encoding: base64\r\n"
+ "Content-Type: application/octet-stream; name=sample\r\n"
+ "\r\n"
+ "VGhpcyBpcyBzb21lIGJpbmFyeSBkYXRhLiBSZWFsbHku\r\n"
+ "--MIME_boundary_31E8A8D61DF53389--\r\n"
+ );
+
+ std::istringstream istr(msgin);
+ std::ostringstream ostr;
+ FilePartStoreFactory pfsf;
+ MailMessage message(&pfsf);
+
+ message.read(istr);
+
+ MailMessage::PartVec::const_iterator it = message.parts().begin();
+ MailMessage::PartVec::const_iterator end = message.parts().end();
+ for (; it != end; ++it)
+ {
+ FilePartStore* fps = dynamic_cast(it->pSource);
+ if (fps && fps->filename().size())
+ {
+ std::string filename = fps->filename();
+ assert (filename == "sample.dat");
+ std::string path = fps->path();
+ // for security reasons, the filesystem temporary
+ // filename is not the same as attachment name
+ std::size_t sz = (path.size() > filename.size()) ? filename.size() : path.size();
+ assert (0 != icompare(path, path.size() - sz, sz, path));
+
+ Poco::FileInputStream fis(path);
+ assert (fis.good());
+ std::string read;
+ std::string line;
+ while (std::getline(fis, line)) read += line;
+
+ assert (!read.empty());
+ assert (read == "This is some binary data. Really.");
+ }
+ }
+
+ message.write(ostr);
+ std::string msgout(ostr.str());
+ assert (msgout == msgin);
+}
+
+
void MailMessageTest::testEncodeWord()
{
std::string plain("this is pure ASCII");
@@ -460,6 +575,8 @@ CppUnit::Test* MailMessageTest::suite()
CppUnit_addTest(pSuite, MailMessageTest, testReadQP);
CppUnit_addTest(pSuite, MailMessageTest, testRead8Bit);
CppUnit_addTest(pSuite, MailMessageTest, testReadMultiPart);
+ CppUnit_addTest(pSuite, MailMessageTest, testReadWriteMultiPart);
+ CppUnit_addTest(pSuite, MailMessageTest, testReadWriteMultiPartStore);
CppUnit_addTest(pSuite, MailMessageTest, testEncodeWord);
return pSuite;
diff --git a/Net/testsuite/src/MailMessageTest.h b/Net/testsuite/src/MailMessageTest.h
index 4843cdfb7..2a9c3f437 100644
--- a/Net/testsuite/src/MailMessageTest.h
+++ b/Net/testsuite/src/MailMessageTest.h
@@ -51,6 +51,8 @@ public:
void testWriteBase64();
void testWriteManyRecipients();
void testWriteMultiPart();
+ void testReadWriteMultiPart();
+ void testReadWriteMultiPartStore();
void testReadQP();
void testRead8Bit();
void testReadMultiPart();