mirror of
https://github.com/pocoproject/poco.git
synced 2025-10-28 03:20:11 +01:00
new trunk (base for 1.5)
windows build only
This commit is contained in:
577
Net/src/MailMessage.cpp
Normal file
577
Net/src/MailMessage.cpp
Normal file
@@ -0,0 +1,577 @@
|
||||
//
|
||||
// MailMessage.cpp
|
||||
//
|
||||
// $Id: //poco/1.4/Net/src/MailMessage.cpp#2 $
|
||||
//
|
||||
// Library: Net
|
||||
// Package: Mail
|
||||
// Module: MailMessage
|
||||
//
|
||||
// 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/MailMessage.h"
|
||||
#include "Poco/Net/MediaType.h"
|
||||
#include "Poco/Net/MultipartReader.h"
|
||||
#include "Poco/Net/MultipartWriter.h"
|
||||
#include "Poco/Net/PartSource.h"
|
||||
#include "Poco/Net/PartHandler.h"
|
||||
#include "Poco/Net/QuotedPrintableEncoder.h"
|
||||
#include "Poco/Net/QuotedPrintableDecoder.h"
|
||||
#include "Poco/Base64Encoder.h"
|
||||
#include "Poco/Base64Decoder.h"
|
||||
#include "Poco/StreamCopier.h"
|
||||
#include "Poco/DateTimeFormat.h"
|
||||
#include "Poco/DateTimeFormatter.h"
|
||||
#include "Poco/DateTimeParser.h"
|
||||
#include "Poco/String.h"
|
||||
#include "Poco/StreamCopier.h"
|
||||
#include "Poco/NumberFormatter.h"
|
||||
#include <sstream>
|
||||
|
||||
|
||||
using Poco::Base64Encoder;
|
||||
using Poco::Base64Decoder;
|
||||
using Poco::StreamCopier;
|
||||
using Poco::DateTimeFormat;
|
||||
using Poco::DateTimeFormatter;
|
||||
using Poco::DateTimeParser;
|
||||
using Poco::icompare;
|
||||
|
||||
|
||||
namespace Poco {
|
||||
namespace Net {
|
||||
|
||||
|
||||
namespace
|
||||
{
|
||||
class StringPartHandler: public PartHandler
|
||||
{
|
||||
public:
|
||||
StringPartHandler(std::string& content):
|
||||
_str(content)
|
||||
{
|
||||
}
|
||||
|
||||
~StringPartHandler()
|
||||
{
|
||||
}
|
||||
|
||||
void handlePart(const MessageHeader& header, std::istream& stream)
|
||||
{
|
||||
Poco::StreamCopier::copyToString(stream, _str);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string& _str;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
const std::string MailMessage::HEADER_SUBJECT("Subject");
|
||||
const std::string MailMessage::HEADER_FROM("From");
|
||||
const std::string MailMessage::HEADER_TO("To");
|
||||
const std::string MailMessage::HEADER_CC("CC");
|
||||
const std::string MailMessage::HEADER_BCC("BCC");
|
||||
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_MIME_VERSION("Mime-Version");
|
||||
const std::string MailMessage::EMPTY_HEADER;
|
||||
const std::string MailMessage::TEXT_PLAIN("text/plain");
|
||||
const std::string MailMessage::CTE_7BIT("7bit");
|
||||
const std::string MailMessage::CTE_8BIT("8bit");
|
||||
const std::string MailMessage::CTE_QUOTED_PRINTABLE("quoted-printable");
|
||||
const std::string MailMessage::CTE_BASE64("base64");
|
||||
|
||||
|
||||
MailMessage::MailMessage()
|
||||
{
|
||||
Poco::Timestamp now;
|
||||
setDate(now);
|
||||
setContentType("text/plain");
|
||||
}
|
||||
|
||||
|
||||
MailMessage::~MailMessage()
|
||||
{
|
||||
for (PartVec::iterator it = _parts.begin(); it != _parts.end(); ++it)
|
||||
{
|
||||
delete it->pSource;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MailMessage::addRecipient(const MailRecipient& recipient)
|
||||
{
|
||||
_recipients.push_back(recipient);
|
||||
}
|
||||
|
||||
|
||||
void MailMessage::setSender(const std::string& sender)
|
||||
{
|
||||
set(HEADER_FROM, sender);
|
||||
}
|
||||
|
||||
|
||||
const std::string& MailMessage::getSender() const
|
||||
{
|
||||
if (has(HEADER_FROM))
|
||||
return get(HEADER_FROM);
|
||||
else
|
||||
return EMPTY_HEADER;
|
||||
}
|
||||
|
||||
|
||||
void MailMessage::setSubject(const std::string& subject)
|
||||
{
|
||||
set(HEADER_SUBJECT, subject);
|
||||
}
|
||||
|
||||
|
||||
const std::string& MailMessage::getSubject() const
|
||||
{
|
||||
if (has(HEADER_SUBJECT))
|
||||
return get(HEADER_SUBJECT);
|
||||
else
|
||||
return EMPTY_HEADER;
|
||||
}
|
||||
|
||||
|
||||
void MailMessage::setContent(const std::string& content, ContentTransferEncoding encoding)
|
||||
{
|
||||
_content = content;
|
||||
_encoding = encoding;
|
||||
set(HEADER_CONTENT_TRANSFER_ENCODING, contentTransferEncodingToString(encoding));
|
||||
}
|
||||
|
||||
|
||||
void MailMessage::setContentType(const std::string& mediaType)
|
||||
{
|
||||
set(HEADER_CONTENT_TYPE, mediaType);
|
||||
}
|
||||
|
||||
|
||||
void MailMessage::setContentType(const MediaType& mediaType)
|
||||
{
|
||||
setContentType(mediaType.toString());
|
||||
}
|
||||
|
||||
|
||||
const std::string& MailMessage::getContentType() const
|
||||
{
|
||||
if (has(HEADER_CONTENT_TYPE))
|
||||
return get(HEADER_CONTENT_TYPE);
|
||||
else
|
||||
return TEXT_PLAIN;
|
||||
}
|
||||
|
||||
|
||||
void MailMessage::setDate(const Poco::Timestamp& dateTime)
|
||||
{
|
||||
set(HEADER_DATE, DateTimeFormatter::format(dateTime, DateTimeFormat::RFC1123_FORMAT));
|
||||
}
|
||||
|
||||
|
||||
Poco::Timestamp MailMessage::getDate() const
|
||||
{
|
||||
const std::string& dateTime = get(HEADER_DATE);
|
||||
int tzd;
|
||||
return DateTimeParser::parse(dateTime, tzd).timestamp();
|
||||
}
|
||||
|
||||
|
||||
bool MailMessage::isMultipart() const
|
||||
{
|
||||
MediaType mediaType = getContentType();
|
||||
return mediaType.matches("multipart");
|
||||
}
|
||||
|
||||
|
||||
void MailMessage::addPart(const std::string& name, PartSource* pSource, ContentDisposition disposition, ContentTransferEncoding encoding)
|
||||
{
|
||||
poco_check_ptr (pSource);
|
||||
|
||||
makeMultipart();
|
||||
Part part;
|
||||
part.name = name;
|
||||
part.pSource = pSource;
|
||||
part.disposition = disposition;
|
||||
part.encoding = encoding;
|
||||
_parts.push_back(part);
|
||||
}
|
||||
|
||||
|
||||
void MailMessage::addContent(PartSource* pSource, ContentTransferEncoding encoding)
|
||||
{
|
||||
addPart("", pSource, CONTENT_INLINE, encoding);
|
||||
}
|
||||
|
||||
|
||||
void MailMessage::addAttachment(const std::string& name, PartSource* pSource, ContentTransferEncoding encoding)
|
||||
{
|
||||
addPart(name, pSource, CONTENT_ATTACHMENT, encoding);
|
||||
}
|
||||
|
||||
|
||||
void MailMessage::read(std::istream& istr, PartHandler& handler)
|
||||
{
|
||||
readHeader(istr);
|
||||
if (isMultipart())
|
||||
{
|
||||
readMultipart(istr, handler);
|
||||
}
|
||||
else
|
||||
{
|
||||
StringPartHandler handler(_content);
|
||||
readPart(istr, *this, handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MailMessage::read(std::istream& istr)
|
||||
{
|
||||
readHeader(istr);
|
||||
StringPartHandler handler(_content);
|
||||
readPart(istr, *this, handler);
|
||||
}
|
||||
|
||||
|
||||
void MailMessage::write(std::ostream& ostr) const
|
||||
{
|
||||
MessageHeader header(*this);
|
||||
setRecipientHeaders(header);
|
||||
if (isMultipart())
|
||||
{
|
||||
writeMultipart(header, ostr);
|
||||
}
|
||||
else
|
||||
{
|
||||
writeHeader(header, ostr);
|
||||
std::istringstream istr(_content);
|
||||
writeEncoded(istr, ostr, _encoding);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MailMessage::makeMultipart()
|
||||
{
|
||||
if (!isMultipart())
|
||||
{
|
||||
MediaType mediaType("multipart", "mixed");
|
||||
setContentType(mediaType);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MailMessage::writeHeader(const MessageHeader& header, std::ostream& ostr) const
|
||||
{
|
||||
header.write(ostr);
|
||||
ostr << "\r\n";
|
||||
}
|
||||
|
||||
|
||||
void MailMessage::writeMultipart(MessageHeader& header, std::ostream& ostr) const
|
||||
{
|
||||
std::string boundary(MultipartWriter::createBoundary());
|
||||
MediaType mediaType(getContentType());
|
||||
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);
|
||||
for (PartVec::const_iterator it = _parts.begin(); it != _parts.end(); ++it)
|
||||
{
|
||||
writePart(writer, *it);
|
||||
}
|
||||
writer.close();
|
||||
}
|
||||
|
||||
|
||||
void MailMessage::writePart(MultipartWriter& writer, const Part& part) const
|
||||
{
|
||||
MessageHeader partHeader(part.pSource->headers());
|
||||
MediaType mediaType(part.pSource->mediaType());
|
||||
if (!part.name.empty())
|
||||
mediaType.setParameter("name", part.name);
|
||||
partHeader.set(HEADER_CONTENT_TYPE, mediaType.toString());
|
||||
partHeader.set(HEADER_CONTENT_TRANSFER_ENCODING, contentTransferEncodingToString(part.encoding));
|
||||
std::string disposition;
|
||||
if (part.disposition == CONTENT_ATTACHMENT)
|
||||
{
|
||||
disposition = "attachment";
|
||||
const std::string& filename = part.pSource->filename();
|
||||
if (!filename.empty())
|
||||
{
|
||||
disposition.append("; filename=");
|
||||
quote(filename, disposition);
|
||||
}
|
||||
}
|
||||
else disposition = "inline";
|
||||
partHeader.set(HEADER_CONTENT_DISPOSITION, disposition);
|
||||
writer.nextPart(partHeader);
|
||||
writeEncoded(part.pSource->stream(), writer.stream(), part.encoding);
|
||||
}
|
||||
|
||||
|
||||
void MailMessage::writeEncoded(std::istream& istr, std::ostream& ostr, ContentTransferEncoding encoding) const
|
||||
{
|
||||
switch (encoding)
|
||||
{
|
||||
case ENCODING_7BIT:
|
||||
case ENCODING_8BIT:
|
||||
StreamCopier::copyStream(istr, ostr);
|
||||
break;
|
||||
case ENCODING_QUOTED_PRINTABLE:
|
||||
{
|
||||
QuotedPrintableEncoder encoder(ostr);
|
||||
StreamCopier::copyStream(istr, encoder);
|
||||
encoder.close();
|
||||
}
|
||||
break;
|
||||
case ENCODING_BASE64:
|
||||
{
|
||||
Base64Encoder encoder(ostr);
|
||||
StreamCopier::copyStream(istr, encoder);
|
||||
encoder.close();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MailMessage::readHeader(std::istream& istr)
|
||||
{
|
||||
clear();
|
||||
MessageHeader::read(istr);
|
||||
istr.get(); // \r
|
||||
istr.get(); // \n
|
||||
}
|
||||
|
||||
|
||||
void MailMessage::readMultipart(std::istream& istr, PartHandler& handler)
|
||||
{
|
||||
MediaType contentType(getContentType());
|
||||
std::string boundary = contentType.getParameter("boundary");
|
||||
MultipartReader reader(istr, boundary);
|
||||
while (reader.hasNextPart())
|
||||
{
|
||||
MessageHeader partHeader;
|
||||
reader.nextPart(partHeader);
|
||||
readPart(reader.stream(), partHeader, handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MailMessage::readPart(std::istream& istr, const MessageHeader& header, PartHandler& handler)
|
||||
{
|
||||
std::string encoding;
|
||||
if (header.has(HEADER_CONTENT_TRANSFER_ENCODING))
|
||||
{
|
||||
encoding = header.get(HEADER_CONTENT_TRANSFER_ENCODING);
|
||||
// get rid of a parameter if one is set
|
||||
std::string::size_type pos = encoding.find(';');
|
||||
if (pos != std::string::npos)
|
||||
encoding.resize(pos);
|
||||
}
|
||||
if (icompare(encoding, CTE_QUOTED_PRINTABLE) == 0)
|
||||
{
|
||||
QuotedPrintableDecoder decoder(istr);
|
||||
handlePart(decoder, header, handler);
|
||||
}
|
||||
else if (icompare(encoding, CTE_BASE64) == 0)
|
||||
{
|
||||
Base64Decoder decoder(istr);
|
||||
handlePart(decoder, header, handler);
|
||||
}
|
||||
else
|
||||
{
|
||||
handlePart(istr, header, handler);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void MailMessage::handlePart(std::istream& istr, const MessageHeader& header, PartHandler& handler)
|
||||
{
|
||||
handler.handlePart(header, istr);
|
||||
// Read remaining characters from stream in case
|
||||
// the handler failed to read the complete stream.
|
||||
while (istr.good()) istr.get();
|
||||
}
|
||||
|
||||
|
||||
void MailMessage::setRecipientHeaders(MessageHeader& headers) const
|
||||
{
|
||||
std::string to;
|
||||
std::string cc;
|
||||
std::string bcc;
|
||||
|
||||
for (Recipients::const_iterator it = _recipients.begin(); it != _recipients.end(); ++it)
|
||||
{
|
||||
switch (it->getType())
|
||||
{
|
||||
case MailRecipient::PRIMARY_RECIPIENT:
|
||||
appendRecipient(*it, to);
|
||||
break;
|
||||
case MailRecipient::CC_RECIPIENT:
|
||||
appendRecipient(*it, cc);
|
||||
break;
|
||||
case MailRecipient::BCC_RECIPIENT:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!to.empty()) headers.set(HEADER_TO, to);
|
||||
if (!cc.empty()) headers.set(HEADER_CC, cc);
|
||||
}
|
||||
|
||||
|
||||
const std::string& MailMessage::contentTransferEncodingToString(ContentTransferEncoding encoding)
|
||||
{
|
||||
switch (encoding)
|
||||
{
|
||||
case ENCODING_7BIT:
|
||||
return CTE_7BIT;
|
||||
case ENCODING_8BIT:
|
||||
return CTE_8BIT;
|
||||
case ENCODING_QUOTED_PRINTABLE:
|
||||
return CTE_QUOTED_PRINTABLE;
|
||||
case ENCODING_BASE64:
|
||||
return CTE_BASE64;
|
||||
default:
|
||||
poco_bugcheck();
|
||||
}
|
||||
return CTE_7BIT;
|
||||
}
|
||||
|
||||
|
||||
int MailMessage::lineLength(const std::string& str)
|
||||
{
|
||||
int n = 0;
|
||||
std::string::const_reverse_iterator it = str.rbegin();
|
||||
std::string::const_reverse_iterator end = str.rend();
|
||||
while (it != end && *it != '\n') { ++n; ++it; }
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
void MailMessage::appendRecipient(const MailRecipient& recipient, std::string& str)
|
||||
{
|
||||
if (!str.empty()) str.append(", ");
|
||||
const std::string& realName = recipient.getRealName();
|
||||
const std::string& address = recipient.getAddress();
|
||||
std::string rec;
|
||||
if (!realName.empty())
|
||||
{
|
||||
quote(realName, rec, true);
|
||||
rec.append(" ");
|
||||
}
|
||||
rec.append("<");
|
||||
rec.append(address);
|
||||
rec.append(">");
|
||||
if (lineLength(str) + rec.length() > 70) str.append("\r\n\t");
|
||||
str.append(rec);
|
||||
}
|
||||
|
||||
|
||||
std::string MailMessage::encodeWord(const std::string& text, const std::string& charset)
|
||||
{
|
||||
bool containsNonASCII = false;
|
||||
for (std::string::const_iterator it = text.begin(); it != text.end(); ++it)
|
||||
{
|
||||
if (static_cast<unsigned char>(*it) > 127)
|
||||
{
|
||||
containsNonASCII = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!containsNonASCII) return text;
|
||||
|
||||
std::string encodedText;
|
||||
std::string::size_type lineLength = 0;
|
||||
for (std::string::const_iterator it = text.begin(); it != text.end(); ++it)
|
||||
{
|
||||
if (lineLength == 0)
|
||||
{
|
||||
encodedText += "=?";
|
||||
encodedText += charset;
|
||||
encodedText += "?q?";
|
||||
lineLength += charset.length() + 5;
|
||||
}
|
||||
switch (*it)
|
||||
{
|
||||
case ' ':
|
||||
encodedText += '_';
|
||||
lineLength++;
|
||||
break;
|
||||
case '=':
|
||||
case '?':
|
||||
case '_':
|
||||
case '(':
|
||||
case ')':
|
||||
case '[':
|
||||
case ']':
|
||||
case '<':
|
||||
case '>':
|
||||
case ',':
|
||||
case ';':
|
||||
case ':':
|
||||
case '.':
|
||||
case '@':
|
||||
encodedText += '=';
|
||||
NumberFormatter::appendHex(encodedText, static_cast<unsigned>(static_cast<unsigned char>(*it)), 2);
|
||||
lineLength += 3;
|
||||
break;
|
||||
default:
|
||||
if (*it > 32 && *it < 127)
|
||||
{
|
||||
encodedText += *it;
|
||||
lineLength++;
|
||||
}
|
||||
else
|
||||
{
|
||||
encodedText += '=';
|
||||
NumberFormatter::appendHex(encodedText, static_cast<unsigned>(static_cast<unsigned char>(*it)), 2);
|
||||
lineLength += 3;
|
||||
}
|
||||
}
|
||||
if ((lineLength >= 64 && (*it == ' ' || *it == '\t' || *it == '\r' || *it == '\n')) || lineLength >= 72)
|
||||
{
|
||||
encodedText += "?=\r\n ";
|
||||
lineLength = 0;
|
||||
}
|
||||
}
|
||||
if (lineLength > 0)
|
||||
{
|
||||
encodedText += "?=";
|
||||
}
|
||||
return encodedText;
|
||||
}
|
||||
|
||||
|
||||
} } // namespace Poco::Net
|
||||
Reference in New Issue
Block a user