MailMessage: attachments saving and read/write

MailMessage: attachments saving support and consistent read/write
This commit is contained in:
aleks-f 2013-03-10 23:36:04 -05:00
parent 46c3d74c5f
commit ad077b8f3f
25 changed files with 600 additions and 52 deletions

View File

@ -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)
==========================

View File

@ -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

View File

@ -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" />

View File

@ -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" />

View File

@ -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"/>

View File

@ -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>

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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" />

View File

@ -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>

View File

@ -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"/>

View File

@ -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>

View File

@ -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

View File

@ -43,6 +43,7 @@
#include "Poco/Net/Net.h"
#include "Poco/Net/PartSource.h"
#include "Poco/FileStream.h"
#include "Poco/TemporaryFile.h"
namespace Poco {

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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

View File

@ -64,7 +64,7 @@ namespace
}
const std::string& PartSource::filename()
const std::string& PartSource::filename() const
{
return EMPTY;
}

103
Net/src/PartStore.cpp Normal file
View File

@ -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

View File

@ -74,7 +74,7 @@ std::istream& StringPartSource::stream()
}
const std::string& StringPartSource::filename()
const std::string& StringPartSource::filename() const
{
return _filename;
}

View File

@ -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;

View File

@ -51,6 +51,8 @@ public:
void testWriteBase64();
void testWriteManyRecipients();
void testWriteMultiPart();
void testReadWriteMultiPart();
void testReadWriteMultiPartStore();
void testReadQP();
void testRead8Bit();
void testReadMultiPart();