From ad077b8f3f7ef52b3669979c7fa6fc49b56a99bc Mon Sep 17 00:00:00 2001
From: aleks-f <alex@pocoproject.org>
Date: Sun, 10 Mar 2013 23:36:04 -0500
Subject: [PATCH] MailMessage: attachments saving and read/write

MailMessage: attachments saving support and consistent read/write
---
 CHANGELOG                               |   1 +
 Net/Net_CE_vs90.vcproj                  |   4 +
 Net/Net_vs100.vcxproj                   |   2 +
 Net/Net_vs100.vcxproj.filters           |   6 ++
 Net/Net_vs110.vcxproj                   |   2 +
 Net/Net_vs110.vcxproj.filters           |   6 ++
 Net/Net_vs71.vcproj                     |   4 +
 Net/Net_vs80.vcproj                     |   4 +
 Net/Net_vs90.vcproj                     |   4 +
 Net/Net_x64_vs100.vcxproj               |   2 +
 Net/Net_x64_vs100.vcxproj.filters       |   6 ++
 Net/Net_x64_vs110.vcxproj               |   2 +
 Net/Net_x64_vs110.vcxproj.filters       |   6 ++
 Net/Net_x64_vs90.vcproj                 |   4 +
 Net/include/Poco/Net/FilePartSource.h   |   1 +
 Net/include/Poco/Net/MailMessage.h      | 101 ++++++++++++------
 Net/include/Poco/Net/PartSource.h       |   2 +-
 Net/include/Poco/Net/PartStore.h        | 128 +++++++++++++++++++++++
 Net/include/Poco/Net/StringPartSource.h |   2 +-
 Net/src/MailMessage.cpp                 | 133 +++++++++++++++++++++---
 Net/src/PartSource.cpp                  |   2 +-
 Net/src/PartStore.cpp                   | 103 ++++++++++++++++++
 Net/src/StringPartSource.cpp            |   2 +-
 Net/testsuite/src/MailMessageTest.cpp   | 123 +++++++++++++++++++++-
 Net/testsuite/src/MailMessageTest.h     |   2 +
 25 files changed, 600 insertions(+), 52 deletions(-)
 create mode 100644 Net/include/Poco/Net/PartStore.h
 create mode 100644 Net/src/PartStore.cpp

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"/>
 				<File
 					RelativePath=".\include\Poco\Net\PartSource.h"/>
+				<File
+					RelativePath=".\include\Poco\Net\PartStore.h"/>
 				<File
 					RelativePath=".\include\Poco\Net\QuotedPrintableDecoder.h"/>
 				<File
@@ -581,6 +583,8 @@
 					RelativePath=".\src\PartHandler.cpp"/>
 				<File
 					RelativePath=".\src\PartSource.cpp"/>
+				<File
+					RelativePath=".\src\PartStore.cpp"/>
 				<File
 					RelativePath=".\src\QuotedPrintableDecoder.cpp"/>
 				<File
diff --git a/Net/Net_vs100.vcxproj b/Net/Net_vs100.vcxproj
index 781dc997c..561530f09 100644
--- a/Net/Net_vs100.vcxproj
+++ b/Net/Net_vs100.vcxproj
@@ -276,6 +276,7 @@
     <ClInclude Include="include\Poco\Net\NetworkInterface.h" />
     <ClInclude Include="include\Poco\Net\ParallelSocketAcceptor.h" />
     <ClInclude Include="include\Poco\Net\ParallelSocketReactor.h" />
+    <ClInclude Include="include\Poco\Net\PartStore.h" />
     <ClInclude Include="include\Poco\Net\SocketAddress.h" />
     <ClInclude Include="include\Poco\Net\SocketDefs.h" />
     <ClInclude Include="include\Poco\Net\DatagramSocket.h" />
@@ -374,6 +375,7 @@
     <ClCompile Include="src\Net.cpp" />
     <ClCompile Include="src\NetException.cpp" />
     <ClCompile Include="src\NetworkInterface.cpp" />
+    <ClCompile Include="src\PartStore.cpp" />
     <ClCompile Include="src\SocketAddress.cpp" />
     <ClCompile Include="src\DatagramSocket.cpp" />
     <ClCompile Include="src\DatagramSocketImpl.cpp" />
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 @@
     <ClInclude Include="include\Poco\Net\IPAddressImpl.h">
       <Filter>NetCore\Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="include\Poco\Net\PartStore.h">
+      <Filter>Messages\Header Files</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="src\DNS.cpp">
@@ -701,6 +704,9 @@
     <ClCompile Include="src\IPAddressImpl.cpp">
       <Filter>NetCore\Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="src\PartStore.cpp">
+      <Filter>Messages\Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="..\DLLVersion.rc" />
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 @@
     <ClInclude Include="include\Poco\Net\NullPartHandler.h"/>
     <ClInclude Include="include\Poco\Net\PartHandler.h"/>
     <ClInclude Include="include\Poco\Net\PartSource.h"/>
+    <ClInclude Include="include\Poco\Net\PartStore.h"/>
     <ClInclude Include="include\Poco\Net\QuotedPrintableDecoder.h"/>
     <ClInclude Include="include\Poco\Net\QuotedPrintableEncoder.h"/>
     <ClInclude Include="include\Poco\Net\StringPartSource.h"/>
@@ -403,6 +404,7 @@
     <ClCompile Include="src\NullPartHandler.cpp"/>
     <ClCompile Include="src\PartHandler.cpp"/>
     <ClCompile Include="src\PartSource.cpp"/>
+    <ClCompile Include="src\PartStore.cpp"/>
     <ClCompile Include="src\QuotedPrintableDecoder.cpp"/>
     <ClCompile Include="src\QuotedPrintableEncoder.cpp"/>
     <ClCompile Include="src\StringPartSource.cpp"/>
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 @@
     <ClInclude Include="include\Poco\Net\PartSource.h">
       <Filter>Messages\Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="include\Poco\Net\PartStore.h">
+      <Filter>Messages\Header Files</Filter>
+    </ClInclude>
     <ClInclude Include="include\Poco\Net\QuotedPrintableDecoder.h">
       <Filter>Messages\Header Files</Filter>
     </ClInclude>
@@ -515,6 +518,9 @@
     <ClCompile Include="src\PartSource.cpp">
       <Filter>Messages\Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="src\PartStore.cpp">
+      <Filter>Messages\Source Files</Filter>
+    </ClCompile>
     <ClCompile Include="src\QuotedPrintableDecoder.cpp">
       <Filter>Messages\Source Files</Filter>
     </ClCompile>
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"/>
 				<File
 					RelativePath=".\include\Poco\Net\PartSource.h"/>
+				<File
+					RelativePath=".\include\Poco\Net\PartStore.h"/>
 				<File
 					RelativePath=".\include\Poco\Net\QuotedPrintableDecoder.h"/>
 				<File
@@ -513,6 +515,8 @@
 					RelativePath=".\src\PartHandler.cpp"/>
 				<File
 					RelativePath=".\src\PartSource.cpp"/>
+				<File
+					RelativePath=".\src\PartStore.cpp"/>
 				<File
 					RelativePath=".\src\QuotedPrintableDecoder.cpp"/>
 				<File
diff --git a/Net/Net_vs80.vcproj b/Net/Net_vs80.vcproj
index a811928ab..a506a14d0 100644
--- a/Net/Net_vs80.vcproj
+++ b/Net/Net_vs80.vcproj
@@ -507,6 +507,8 @@
 					RelativePath=".\include\Poco\Net\PartHandler.h"/>
 				<File
 					RelativePath=".\include\Poco\Net\PartSource.h"/>
+				<File
+					RelativePath=".\include\Poco\Net\PartStore.h"/>
 				<File
 					RelativePath=".\include\Poco\Net\QuotedPrintableDecoder.h"/>
 				<File
@@ -534,6 +536,8 @@
 					RelativePath=".\src\PartHandler.cpp"/>
 				<File
 					RelativePath=".\src\PartSource.cpp"/>
+				<File
+					RelativePath=".\src\PartStore.cpp"/>
 				<File
 					RelativePath=".\src\QuotedPrintableDecoder.cpp"/>
 				<File
diff --git a/Net/Net_vs90.vcproj b/Net/Net_vs90.vcproj
index 986b2f433..0021f9a5b 100644
--- a/Net/Net_vs90.vcproj
+++ b/Net/Net_vs90.vcproj
@@ -506,6 +506,8 @@
 					RelativePath=".\include\Poco\Net\PartHandler.h"/>
 				<File
 					RelativePath=".\include\Poco\Net\PartSource.h"/>
+				<File
+					RelativePath=".\include\Poco\Net\PartStore.h"/>
 				<File
 					RelativePath=".\include\Poco\Net\QuotedPrintableDecoder.h"/>
 				<File
@@ -533,6 +535,8 @@
 					RelativePath=".\src\PartHandler.cpp"/>
 				<File
 					RelativePath=".\src\PartSource.cpp"/>
+				<File
+					RelativePath=".\src\PartStore.cpp"/>
 				<File
 					RelativePath=".\src\QuotedPrintableDecoder.cpp"/>
 				<File
diff --git a/Net/Net_x64_vs100.vcxproj b/Net/Net_x64_vs100.vcxproj
index 189446b97..9c1ef4e66 100644
--- a/Net/Net_x64_vs100.vcxproj
+++ b/Net/Net_x64_vs100.vcxproj
@@ -296,6 +296,7 @@
     <ClInclude Include="include\Poco\Net\NullPartHandler.h" />
     <ClInclude Include="include\Poco\Net\PartHandler.h" />
     <ClInclude Include="include\Poco\Net\PartSource.h" />
+    <ClInclude Include="include\Poco\Net\PartStore.h" />
     <ClInclude Include="include\Poco\Net\QuotedPrintableDecoder.h" />
     <ClInclude Include="include\Poco\Net\QuotedPrintableEncoder.h" />
     <ClInclude Include="include\Poco\Net\StringPartSource.h" />
@@ -395,6 +396,7 @@
     <ClCompile Include="src\NullPartHandler.cpp" />
     <ClCompile Include="src\PartHandler.cpp" />
     <ClCompile Include="src\PartSource.cpp" />
+    <ClCompile Include="src\PartStore.cpp" />
     <ClCompile Include="src\QuotedPrintableDecoder.cpp" />
     <ClCompile Include="src\QuotedPrintableEncoder.cpp" />
     <ClCompile Include="src\StringPartSource.cpp" />
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 @@
     <ClInclude Include="include\Poco\Net\PartSource.h">
       <Filter>Messages\Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="include\Poco\Net\PartStore.h">
+      <Filter>Messages\Header Files</Filter>
+    </ClInclude>
     <ClInclude Include="include\Poco\Net\QuotedPrintableDecoder.h">
       <Filter>Messages\Header Files</Filter>
     </ClInclude>
@@ -512,6 +515,9 @@
     <ClCompile Include="src\PartSource.cpp">
       <Filter>Messages\Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="src\PartStore.cpp">
+      <Filter>Messages\Source Files</Filter>
+    </ClCompile>
     <ClCompile Include="src\QuotedPrintableDecoder.cpp">
       <Filter>Messages\Source Files</Filter>
     </ClCompile>
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 @@
     <ClInclude Include="include\Poco\Net\NullPartHandler.h"/>
     <ClInclude Include="include\Poco\Net\PartHandler.h"/>
     <ClInclude Include="include\Poco\Net\PartSource.h"/>
+    <ClInclude Include="include\Poco\Net\PartStore.h"/>
     <ClInclude Include="include\Poco\Net\QuotedPrintableDecoder.h"/>
     <ClInclude Include="include\Poco\Net\QuotedPrintableEncoder.h"/>
     <ClInclude Include="include\Poco\Net\StringPartSource.h"/>
@@ -401,6 +402,7 @@
     <ClCompile Include="src\NullPartHandler.cpp"/>
     <ClCompile Include="src\PartHandler.cpp"/>
     <ClCompile Include="src\PartSource.cpp"/>
+    <ClCompile Include="src\PartStore.cpp"/>
     <ClCompile Include="src\QuotedPrintableDecoder.cpp"/>
     <ClCompile Include="src\QuotedPrintableEncoder.cpp"/>
     <ClCompile Include="src\StringPartSource.cpp"/>
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 @@
     <ClInclude Include="include\Poco\Net\PartSource.h">
       <Filter>Messages\Header Files</Filter>
     </ClInclude>
+    <ClInclude Include="include\Poco\Net\PartStore.h">
+      <Filter>Messages\Header Files</Filter>
+    </ClInclude>
     <ClInclude Include="include\Poco\Net\QuotedPrintableDecoder.h">
       <Filter>Messages\Header Files</Filter>
     </ClInclude>
@@ -515,6 +518,9 @@
     <ClCompile Include="src\PartSource.cpp">
       <Filter>Messages\Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="src\PartStore.cpp">
+      <Filter>Messages\Source Files</Filter>
+    </ClCompile>
     <ClCompile Include="src\QuotedPrintableDecoder.cpp">
       <Filter>Messages\Source Files</Filter>
     </ClCompile>
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"/>
 				<File
 					RelativePath=".\include\Poco\Net\PartSource.h"/>
+				<File
+					RelativePath=".\include\Poco\Net\PartStore.h"/>
 				<File
 					RelativePath=".\include\Poco\Net\QuotedPrintableDecoder.h"/>
 				<File
@@ -538,6 +540,8 @@
 					RelativePath=".\src\PartHandler.cpp"/>
 				<File
 					RelativePath=".\src\PartSource.cpp"/>
+				<File
+					RelativePath=".\src\PartStore.cpp"/>
 				<File
 					RelativePath=".\src\QuotedPrintableDecoder.cpp"/>
 				<File
diff --git a/Net/include/Poco/Net/FilePartSource.h b/Net/include/Poco/Net/FilePartSource.h
index 58f1079a3..e1a0cef78 100644
--- a/Net/include/Poco/Net/FilePartSource.h
+++ b/Net/include/Poco/Net/FilePartSource.h
@@ -43,6 +43,7 @@
 #include "Poco/Net/Net.h"
 #include "Poco/Net/PartSource.h"
 #include "Poco/FileStream.h"
+#include "Poco/TemporaryFile.h"
 
 
 namespace Poco {
diff --git a/Net/include/Poco/Net/MailMessage.h b/Net/include/Poco/Net/MailMessage.h
index 459996684..9d5553c33 100644
--- a/Net/include/Poco/Net/MailMessage.h
+++ b/Net/include/Poco/Net/MailMessage.h
@@ -43,6 +43,7 @@
 #include "Poco/Net/Net.h"
 #include "Poco/Net/MessageHeader.h"
 #include "Poco/Net/MailRecipient.h"
+#include "Poco/Net/PartStore.h"
 #include "Poco/Timestamp.h"
 #include <vector>
 
@@ -86,12 +87,28 @@ public:
 		ENCODING_BASE64
 	};
 
-	MailMessage();
+	struct Part
+	{
+		std::string             name;
+		PartSource*             pSource;
+		ContentDisposition      disposition;
+		ContentTransferEncoding encoding;
+	};
+	
+	typedef std::vector<Part> 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<Part> 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 <sstream>
@@ -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 <sstream>
 #include <vector>
 
@@ -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 <john.doe@no.where>\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 <john.doe@no.where>\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<FilePartStore*>(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();