// // MailMessage.cpp // // $Id: //poco/1.3/Net/src/MailMessage.cpp#1 $ // // 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 #include 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) { int ch = stream.get(); while (ch >= 0) { _str += (char) ch; ch = stream.get(); } } 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; 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); } break; case ENCODING_BASE64: { Base64Encoder encoder(ostr); StreamCopier::copyStream(istr, encoder); } 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_8BIT; 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); } } } // namespace Poco::Net