// // VMime library (http://www.vmime.org) // Copyright (C) 2002 Vincent Richard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as // published by the Free Software Foundation; either version 3 of // the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // Linking this library statically or dynamically with other modules is making // a combined work based on this library. Thus, the terms and conditions of // the GNU General Public License cover the whole combination. // #include "vmime/mailbox.hpp" #include "vmime/parserHelpers.hpp" #include "vmime/utility/outputStreamStringAdapter.hpp" namespace vmime { mailbox::mailbox() { } mailbox::mailbox(const mailbox& mbox) : address(), m_name(mbox.m_name), m_email(mbox.m_email) { } mailbox::mailbox(const emailAddress& email) : m_email(email) { } mailbox::mailbox(const text& name, const emailAddress& email) : m_name(name), m_email(email) { } /* RFC #2822: 3.4. ADDRESS SPECIFICATION mailbox = name-addr / addr-spec name-addr = [display-name] angle-addr angle-addr = [CFWS] "<" addr-spec ">" [CFWS] / obs-angle-addr */ void mailbox::parseImpl( const parsingContext& ctx, const string& buffer, const size_t position, const size_t end, size_t* newPosition ) { const char* const pend = buffer.data() + end; const char* const pstart = buffer.data() + position; const char* p = pstart; // Current state for parsing machine enum States { State_None, State_String, State_Name, State_Bracket, State_Address_Bracketed, State_Address_Unbracketed, State_NameAt, }; States state = State_None; // Temporary buffers for extracted name and address string name, address; unsigned int hold = 0; bool hadBrackets = false; while (p < pend) { if (state == State_None) { while (p < pend && parserHelpers::isSpace(*p)) ++p; if (*p == ',' || *p == ';') /* Completely new mailbox -- stop parsing this one. */ break; if (*p == '<') { state = State_Bracket; continue; } if (*p == '"') { // Quoted string state = State_String; ++p; continue; } state = State_Name; } else if (state == State_String) { bool escaped = false; for (; p < pend; ++p) { if (escaped) { name += *p; escaped = false; continue; } else if (*p == '\\') { escaped = true; continue; } if (*p == '"') { ++p; state = State_None; break; } name += *p; } } else if (state == State_Address_Bracketed) { bool escaped = false; int comment = 0; for (; p < pend; ++p) { if (escaped) { if (!comment) { address += *p; name += *p; ++hold; } escaped = false; } else if (comment) { if (*p == '\\') { escaped = true; } else if (*p == '(') { ++comment; } else if (*p == ')') { --comment; } } else if (*p == '(') { ++comment; } else if (*p == '\\') { escaped = true; } else if (*p == '<') { state = State_Bracket; break; } else if (*p == '>') { hadBrackets = true; name += *p++; ++hold; state = State_Name; break; } else if (parserHelpers::isSpace(*p)) { name += *p; ++hold; } else { address += *p; name += *p; ++hold; } } } else if (state == State_Address_Unbracketed) { bool escaped = false; int comment = 0; for (; p < pend; ++p) { if (escaped) { if (!comment) { address += *p; name += *p; ++hold; } escaped = false; } else if (comment) { if (*p == '\\') escaped = true; else if (*p == '(') ++comment; else if (*p == ')') --comment; } else if (*p == '(') { ++comment; } else if (*p == '\\') { escaped = true; } else if (*p == '<') { state = State_Bracket; break; } else if (*p == '>') { name += *p++; ++hold; state = State_None; break; } else if (*p == ',' || *p == ';') { state = State_None; break; } else if (parserHelpers::isSpace(*p)) { state = State_Name; break; } else { address += *p; name += *p; ++hold; } } } else if (state == State_Name) { bool escaped = false; unsigned int comment = 0, at_hold = 0; for (; p < pend; ++p) { if (escaped) { if (!comment) { name += *p; ++at_hold; } escaped = false; } else if (comment) { if (*p == '\\') escaped = true; else if (*p == '(') ++comment; else if (*p == ')') --comment; } else if (*p == '\\') { escaped = true; } else if (*p == '(') { ++comment; } else if (*p == '<') { state = State_Bracket; break; } else if (*p == '@') { hold = at_hold; state = State_NameAt; break; } else if (parserHelpers::isSpace(*p)) { name += *p; ++hold; at_hold = 1; } else { name += *p; hold = 0; ++at_hold; } } } else if (state == State_Bracket) { string::const_iterator q = name.cend(); unsigned int nh = 0; while (nh < hold && q != name.cbegin() && parserHelpers::isSpace(*(q - 1))) { --q; ++nh; } hold = nh; name += *p++; ++hold; if (!address.empty()) // If we found a '<' here, it means that the address // starts _only_ here...and the stuff we have parsed // before belongs actually to the display name! address.clear(); state = State_Address_Bracketed; } else if (state == State_NameAt) { name += *p++; ++hold; // (*) Actually, we were parsing the local-part of an address // and not a display name... // Back up to the last whitespace and treat as address string::const_iterator q = name.cend(); unsigned int nh = 0; while (nh < hold && q != name.cbegin() && !parserHelpers::isSpace(*(q - 1))) { --q; ++nh; } address.assign(q, name.cend()); while (nh < hold && q != name.cbegin() && parserHelpers::isSpace(*(q - 1))) { --q; ++nh; } hold = nh; state = State_Address_Unbracketed; } } if (hold <= name.size()) name.erase(name.size() - hold); // Swap name and address when no address was found // (email address is mandatory, whereas name is optional). if (address.empty() && !name.empty() && !hadBrackets) { m_name.removeAllWords(); m_email.parse(ctx, name); } else { text::decodeAndUnfold(ctx, name, &m_name); m_email.parse(ctx, address); } setParsedBounds(position, position + (p - pstart)); if (newPosition) { *newPosition = position + (p - pstart); } } void mailbox::generateImpl( const generationContext& ctx, utility::outputStream& os, const size_t curLinePos, size_t* newLinePos ) const { string generatedEmail; utility::outputStreamStringAdapter generatedEmailStream(generatedEmail); m_email.generate(ctx, generatedEmailStream, 0, NULL); if (m_name.isEmpty()) { size_t pos = curLinePos; // No display name is specified, only email address. if (curLinePos + generatedEmail.length() > ctx.getMaxLineLength()) { os << NEW_LINE_SEQUENCE; pos = NEW_LINE_SEQUENCE.length(); } os << generatedEmail; pos += generatedEmail.length(); if (newLinePos) { *newLinePos = pos; } } else { // We have to encode the name: // - if it contains characters in a charset different from "US-ASCII", // - and/or if it contains one or more of these special chars: // CR LF TAB " ; , < > ( ) @ / ? . = : // these special chars only require quoting, not full encoding // Check whether there are words that are not "US-ASCII" // and/or contain the special chars. bool forceEncode = false; for (size_t w = 0 ; !forceEncode && w != m_name.getWordCount() ; ++w) { if (m_name.getWordAt(w)->getCharset() != charset(charsets::US_ASCII)) { forceEncode = true; } } size_t pos = curLinePos; m_name.encodeAndFold( ctx, os, pos, &pos, text::QUOTE_IF_POSSIBLE | (forceEncode ? text::FORCE_ENCODING : 0) ); if (pos + generatedEmail.length() + 3 > ctx.getMaxLineLength()) { os << NEW_LINE_SEQUENCE; pos = NEW_LINE_SEQUENCE.length(); } os << " <" << generatedEmail << ">"; pos += 2 + generatedEmail.length() + 1; if (newLinePos) { *newLinePos = pos; } } } bool mailbox::operator==(const class mailbox& mailbox) const { return m_name == mailbox.m_name && m_email == mailbox.m_email; } bool mailbox::operator!=(const class mailbox& mailbox) const { return !(*this == mailbox); } void mailbox::copyFrom(const component& other) { const mailbox& source = dynamic_cast (other); m_name = source.m_name; m_email = source.m_email; } mailbox& mailbox::operator=(const mailbox& other) { copyFrom(other); return *this; } shared_ptr mailbox::clone() const { return make_shared (*this); } bool mailbox::isEmpty() const { return m_email.isEmpty(); } void mailbox::clear() { m_name.removeAllWords(); m_email = emailAddress(); } bool mailbox::isGroup() const { return false; } const text& mailbox::getName() const { return m_name; } void mailbox::setName(const text& name) { m_name = name; } const emailAddress& mailbox::getEmail() const { return m_email; } void mailbox::setEmail(const emailAddress& email) { m_email = email; } const std::vector > mailbox::getChildComponents() { return std::vector >(); } } // vmime