diff options
Diffstat (limited to 'src/emailAddress.cpp')
-rw-r--r-- | src/emailAddress.cpp | 513 |
1 files changed, 513 insertions, 0 deletions
diff --git a/src/emailAddress.cpp b/src/emailAddress.cpp new file mode 100644 index 00000000..09d08780 --- /dev/null +++ b/src/emailAddress.cpp @@ -0,0 +1,513 @@ +// +// VMime library (http://www.vmime.org) +// Copyright (C) 2002-2013 Vincent Richard <[email protected]> +// +// 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/emailAddress.hpp" + +#include "vmime/platform.hpp" + +#include "vmime/parserHelpers.hpp" +#include "vmime/utility/outputStreamStringAdapter.hpp" +#include "vmime/utility/stringUtils.hpp" + + +namespace vmime +{ + + +emailAddress::emailAddress() +{ +} + + +emailAddress::emailAddress(const emailAddress& eml) + : component(), m_localName(eml.m_localName), m_domainName(eml.m_domainName) +{ +} + + +emailAddress::emailAddress(const string& email) +{ + parse(email); +} + + +emailAddress::emailAddress(const char* email) +{ + parse(email); +} + + +emailAddress::emailAddress(const string& localName, const string& domainName) + : component(), m_localName(word(localName, vmime::charsets::UTF_8)), + m_domainName(word(domainName, vmime::charsets::UTF_8)) +{ +} + + +emailAddress::emailAddress(const word& localName, const word& domainName) + : component(), m_localName(localName), m_domainName(domainName) +{ +} + + +void emailAddress::parseImpl + (const parsingContext& /* ctx */, const string& buffer, const string::size_type position, + const string::size_type end, string::size_type* newPosition) +{ + const string::value_type* const pend = buffer.data() + end; + const string::value_type* const pstart = buffer.data() + position; + const string::value_type* p = pstart; + + enum ParserStates + { + State_Before, + State_LocalPartStart, + State_LocalPartMiddle, + State_LocalPartComment, + State_LocalPartQuoted, + State_DomainPartStart, + State_DomainPartMiddle, + State_DomainPartComment, + State_End, + State_Error + } state = State_Before; + + std::ostringstream localPart; + std::ostringstream domainPart; + + bool escapeNext = false; // for quoting + bool prevIsDot = false; + bool atFound = false; + bool stop = false; + int commentLevel = 0; + + while (p < pend && !stop) + { + const string::value_type c = *p; + + if ((localPart.str().length() + domainPart.str().length()) >= 256) + { + state = State_Error; + break; + } + + switch (state) + { + case State_Before: + + if (parserHelpers::isSpace(c)) + ++p; + else + state = State_LocalPartStart; + + case State_LocalPartStart: + + if (c == '"') + { + state = State_LocalPartQuoted; + ++p; + } + else if (c == '(') + { + state = State_LocalPartComment; + ++commentLevel; + ++p; + } + else + { + state = State_LocalPartMiddle; + localPart << c; + ++p; + } + + break; + + case State_LocalPartComment: + + if (escapeNext) + { + escapeNext = false; + ++p; + } + else if (c == '\\') + { + escapeNext = true; + ++p; + } + else if (c == '(') + { + ++commentLevel; + ++p; + } + else if (c == ')') + { + if (--commentLevel == 0) + { + // End of comment + state = State_LocalPartMiddle; + } + + ++p; + } + else + { + // Comment continues + ++p; + } + + break; + + case State_LocalPartQuoted: + + if (escapeNext) + { + escapeNext = false; + + if (c == '"' || c == '\\') + { + localPart << c; + ++p; + } + else + { + // This char cannot be escaped + state = State_Error; + } + } + else if (c == '"') + { + // End of quoted string + state = State_LocalPartMiddle; + ++p; + } + else if (c == '\\') + { + escapeNext = true; + ++p; + } + else + { + localPart << c; + ++p; + } + + break; + + case State_LocalPartMiddle: + + if (c == '.') + { + prevIsDot = true; + localPart << c; + ++p; + } + else if (c == '"' && prevIsDot) + { + prevIsDot = false; + state = State_LocalPartQuoted; + ++p; + } + else if (c == '(') + { + // By allowing comments anywhere in the local part, + // we are more permissive than RFC-2822 + state = State_LocalPartComment; + ++commentLevel; + ++p; + } + else if (c == '@') + { + atFound = true; + state = State_DomainPartStart; + ++p; + } + else if (parserHelpers::isSpace(c)) + { + // Allow not specifying domain part + state = State_End; + } + else + { + prevIsDot = false; + localPart << c; + ++p; + } + + break; + + case State_DomainPartStart: + + if (c == '(') + { + state = State_DomainPartComment; + ++commentLevel; + ++p; + } + else + { + state = State_DomainPartMiddle; + domainPart << c; + ++p; + } + + break; + + case State_DomainPartMiddle: + + if (parserHelpers::isSpace(c)) + { + state = State_End; + } + else if (c == '(') + { + // By allowing comments anywhere in the domain part, + // we are more permissive than RFC-2822 + state = State_DomainPartComment; + ++commentLevel; + ++p; + } + else + { + domainPart << c; + ++p; + } + + break; + + case State_DomainPartComment: + + if (escapeNext) + { + escapeNext = false; + ++p; + } + else if (c == '\\') + { + escapeNext = true; + ++p; + } + else if (c == '(') + { + ++commentLevel; + ++p; + } + else if (c == ')') + { + if (--commentLevel == 0) + { + // End of comment + state = State_DomainPartMiddle; + } + + ++p; + } + else + { + // Comment continues + ++p; + } + + break; + + case State_End: + case State_Error: + + stop = true; + break; + } + } + + if (p == pend && state != State_Error) + { + if (state == State_DomainPartMiddle) + state = State_End; + else if (state == State_LocalPartMiddle) + state = State_End; // allow not specifying domain part + } + + if (state != State_End) + { + m_localName = word("invalid", vmime::charsets::UTF_8); + m_domainName = word("invalid", vmime::charsets::UTF_8); + } + else + { + // If the domain part is missing, use local host name + if (domainPart.str().empty() && !atFound) + domainPart << platform::getHandler()->getHostName(); + + m_localName = word(localPart.str(), vmime::charsets::UTF_8); + m_domainName = word(domainPart.str(), vmime::charsets::UTF_8); + } + + setParsedBounds(position, p - pend); + + if (newPosition) + *newPosition = p - pend; +} + + +static const string domainNameToIDNA(const string& domainName) +{ + std::ostringstream idnaDomain; + string::size_type p = 0; + + for (string::size_type n = domainName.find('.', p) ; + (n = domainName.find('.', p)) != string::npos ; p = n + 1) + { + string idnaPart; + charset::convert(string(domainName.begin() + p, domainName.begin() + n), + idnaPart, vmime::charsets::UTF_8, vmime::charsets::IDNA); + + idnaDomain << idnaPart << '.'; + } + + if (p < domainName.length()) + { + string idnaPart; + charset::convert(string(domainName.begin() + p, domainName.end()), + idnaPart, vmime::charsets::UTF_8, vmime::charsets::IDNA); + + idnaDomain << idnaPart; + } + + return idnaDomain.str(); +} + + +void emailAddress::generateImpl + (const generationContext& ctx, utility::outputStream& os, + const string::size_type curLinePos, string::size_type* newLinePos) const +{ + string localPart, domainPart; + + if (ctx.getInternationalizedEmailSupport() && + (!utility::stringUtils::is7bit(m_localName.getBuffer()) || + !utility::stringUtils::is7bit(m_domainName.getBuffer()))) + { + // Local part + string localPartUTF8(m_localName.getConvertedText(vmime::charsets::UTF_8)); + word localPartWord(localPartUTF8, vmime::charsets::UTF_8); + + vmime::utility::outputStreamStringAdapter os(localPart); + localPartWord.generate(ctx, os, 0, NULL, text::FORCE_NO_ENCODING | text::QUOTE_IF_NEEDED, NULL); + + // Domain part + domainPart = m_domainName.getConvertedText(vmime::charsets::UTF_8); + } + else + { + // Local part + vmime::utility::outputStreamStringAdapter os(localPart); + m_localName.generate(ctx, os, 0, NULL, text::QUOTE_IF_NEEDED, NULL); + + // Domain part as IDNA + domainPart = domainNameToIDNA(m_domainName.getConvertedText(vmime::charsets::UTF_8)); + } + + os << localPart + << "@" + << domainPart; + + if (newLinePos) + { + *newLinePos = curLinePos + + localPart.length() + + 1 // @ + + domainPart.length(); + } +} + + +bool emailAddress::operator==(const class emailAddress& eml) const +{ + return (m_localName == eml.m_localName && + m_domainName == eml.m_domainName); +} + + +bool emailAddress::operator!=(const class emailAddress& eml) const +{ + return !(*this == eml); +} + + +void emailAddress::copyFrom(const component& other) +{ + const emailAddress& source = dynamic_cast <const emailAddress&>(other); + + m_localName = source.m_localName; + m_domainName = source.m_domainName; +} + + +emailAddress& emailAddress::operator=(const emailAddress& other) +{ + copyFrom(other); + return (*this); +} + + +ref <component>emailAddress::clone() const +{ + return vmime::create <emailAddress>(*this); +} + + +const word& emailAddress::getLocalName() const +{ + return m_localName; +} + + +void emailAddress::setLocalName(const word& localName) +{ + m_localName = localName; +} + + +const word& emailAddress::getDomainName() const +{ + return m_domainName; +} + + +void emailAddress::setDomainName(const word& domainName) +{ + m_domainName = domainName; +} + + +const std::vector <ref <component> > emailAddress::getChildComponents() +{ + return std::vector <ref <component> >(); +} + + +bool emailAddress::isEmpty() const +{ + return m_localName.isEmpty(); +} + + +} // vmime |