//============================================================================= // File: mailbox.cpp // Contents: Definitions for DwMailbox // Maintainer: Doug Sauder // WWW: http://www.fwb.gulf.net/~dwsauder/mimepp.html // // Copyright (c) 1996, 1997 Douglas W. Sauder // All rights reserved. // // IN NO EVENT SHALL DOUGLAS W. SAUDER BE LIABLE TO ANY PARTY FOR DIRECT, // INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT OF // THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF DOUGLAS W. SAUDER // HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. // // DOUGLAS W. SAUDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT // NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A // PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" // BASIS, AND DOUGLAS W. SAUDER HAS NO OBLIGATION TO PROVIDE MAINTENANCE, // SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. // //============================================================================= #define DW_IMPLEMENTATION #include #include #include #include #include #include #include #include #include void RemoveCrAndLf(DwString& aStr); const char* const DwMailbox::sClassName = "DwMailbox"; DwMailbox* (*DwMailbox::sNewMailbox)(const DwString&, DwMessageComponent*) = 0; DwMailbox* DwMailbox::NewMailbox(const DwString& aStr, DwMessageComponent* aParent) { if (sNewMailbox) { return sNewMailbox(aStr, aParent); } else { return new DwMailbox(aStr, aParent); } } DwMailbox::DwMailbox() { mClassId = kCidMailbox; mClassName = sClassName; } DwMailbox::DwMailbox(const DwMailbox& aMailbox) : DwAddress(aMailbox), mFullName(aMailbox.mFullName), mRoute(aMailbox.mRoute), mLocalPart(aMailbox.mLocalPart), mDomain(aMailbox.mDomain) { mClassId = kCidMailbox; mClassName = sClassName; } DwMailbox::DwMailbox(const DwString& aStr, DwMessageComponent* aParent) : DwAddress(aStr, aParent) { mClassId = kCidMailbox; mClassName = sClassName; } DwMailbox::~DwMailbox() { } const DwMailbox& DwMailbox::operator = (const DwMailbox& aMailbox) { if (this == &aMailbox) return *this; DwAddress::operator = (aMailbox); mFullName = aMailbox.mFullName; mRoute = aMailbox.mRoute; mLocalPart = aMailbox.mLocalPart; mDomain = aMailbox.mDomain; return *this; } const DwString& DwMailbox::FullName() const { return mFullName; } void DwMailbox::SetFullName(const DwString& aFullName) { mFullName = aFullName; SetModified(); } const DwString& DwMailbox::Route() const { return mRoute; } void DwMailbox::SetRoute(const DwString& aRoute) { mRoute = aRoute; SetModified(); } const DwString& DwMailbox::LocalPart() const { return mLocalPart; } void DwMailbox::SetLocalPart(const DwString& aLocalPart) { mLocalPart = aLocalPart; SetModified(); } const DwString& DwMailbox::Domain() const { return mDomain; } void DwMailbox::SetDomain(const DwString& aDomain) { mDomain = aDomain; SetModified(); } // Some mailboxes to test // // John Doe // John@acme.com (John Doe) // John.Doe@acme.com (John Doe) // John.Doe (Jr) @acme.com (John Doe) // John <@domain1.com,@domain2.com:jdoe@acme.com> // // <@node1.[128.129.130.131],@node2.uu.edu: // jdoe(John Doe)@node3.[131.130.129.128]> (John Doe) // void DwMailbox::Parse() { mIsModified = 0; DwString emptyString(""); DwString space(" "); int isFirstPhraseNull = 1; int isSimpleAddress = 1; DwString firstPhrase(emptyString); DwString lastComment(emptyString); mRoute = emptyString; mLocalPart = emptyString; mDomain = emptyString; mFullName = emptyString; DwRfc822Tokenizer tokenizer(mString); int ch; enum { eStart, // start eLtSeen, // less-than-seen eInRoute, // in-route eInAddrSpec, // in-addr-spec eAtSeen, // at-seen eGtSeen // greater-than-seen }; // Start state -- terminated by '<' or '@' int type = tokenizer.Type(); int state = eStart; while (state == eStart && type != eTkNull) { switch (type) { case eTkSpecial: ch = tokenizer.Token()[0]; switch (ch) { case '@': state = eAtSeen; break; case '<': isSimpleAddress = 0; mLocalPart = emptyString; state = eLtSeen; break; case '.': mLocalPart += tokenizer.Token(); break; } break; case eTkAtom: case eTkQuotedString: if (isFirstPhraseNull) { firstPhrase = tokenizer.Token(); isFirstPhraseNull = 0; } else { firstPhrase += space; firstPhrase += tokenizer.Token(); } mLocalPart += tokenizer.Token(); break; case eTkComment: tokenizer.StripDelimiters(); lastComment = tokenizer.Token(); break; } ++tokenizer; type = tokenizer.Type(); } // Less-than-seen state -- process only one valid token and transit to // in-route state or in-addr-spec state while (state == eLtSeen && type != eTkNull) { switch (type) { case eTkSpecial: ch = tokenizer.Token()[0]; switch (ch) { case '@': // '@' immediately following '<' indicates a route mRoute = tokenizer.Token(); state = eInRoute; break; } break; case eTkAtom: case eTkQuotedString: mLocalPart = tokenizer.Token(); state = eInAddrSpec; break; } ++tokenizer; type = tokenizer.Type(); } // In-route state -- terminated by ':' while (state == eInRoute && type != eTkNull) { switch (type) { case eTkSpecial: ch = tokenizer.Token()[0]; switch (ch) { case ':': state = eInAddrSpec; break; case '@': case ',': case '.': mRoute += tokenizer.Token(); break; } break; case eTkAtom: mRoute += tokenizer.Token(); break; case eTkDomainLiteral: mRoute += tokenizer.Token(); break; } ++tokenizer; type = tokenizer.Type(); } // in-addr-spec state -- terminated by '@' while (state == eInAddrSpec && type != eTkNull) { switch (type) { case eTkSpecial: ch = tokenizer.Token()[0]; switch (ch) { case '@': state = eAtSeen; break; case '.': mLocalPart += tokenizer.Token(); break; } break; case eTkAtom: case eTkQuotedString: mLocalPart += tokenizer.Token(); break; } ++tokenizer; type = tokenizer.Type(); } // at-seen state -- terminated by '>' or end of string while (state == eAtSeen && type != eTkNull) { switch (type) { case eTkSpecial: ch = tokenizer.Token()[0]; switch (ch) { case '>': state = eGtSeen; break; case '.': mDomain += tokenizer.Token(); break; } break; case eTkAtom: mDomain += tokenizer.Token(); break; case eTkDomainLiteral: mDomain += tokenizer.Token(); break; case eTkComment: tokenizer.StripDelimiters(); lastComment = tokenizer.Token(); break; } ++tokenizer; type = tokenizer.Type(); } // greater-than-seen state -- terminated by end of string while (state == eGtSeen && type != eTkNull) { switch (type) { case eTkComment: tokenizer.StripDelimiters(); lastComment = tokenizer.Token(); break; } ++tokenizer; type = tokenizer.Type(); } // Get full name, if possible if (isSimpleAddress) { mFullName = lastComment; } else if (firstPhrase != emptyString) { mFullName = firstPhrase; } else if (lastComment != emptyString) { mFullName = lastComment; } // Check validity if (mLocalPart.length() > 0) { mIsValid = 1; } else { mIsValid = 0; } // Remove CR or LF from local-part or full name RemoveCrAndLf(mFullName); RemoveCrAndLf(mLocalPart); } void DwMailbox::Assemble() { if (!mIsModified) return; mIsValid = 1; if (mLocalPart.length() == 0 || mDomain.length() == 0) { mIsValid = 0; mString = ""; return; } mString = ""; if (mFullName.length() > 0) { mString += mFullName; mString += " "; } mString += "<"; if (mRoute.length() > 0) { mString += mRoute; mString += ":"; } mString += mLocalPart; mString += "@"; mString += mDomain; mString += ">"; mIsModified = 0; } DwMessageComponent* DwMailbox::Clone() const { return new DwMailbox(*this); } #if defined(DW_DEBUG_VERSION) void DwMailbox::PrintDebugInfo(std::ostream& aStrm, int /*aDepth*/) const { aStrm << "---------------- Debug info for DwMailbox class ----------------\n"; _PrintDebugInfo(aStrm); } #else void DwMailbox::PrintDebugInfo(std::ostream& , int) const {} #endif // defined(DW_DEBUG_VERSION) #if defined(DW_DEBUG_VERSION) void DwMailbox::_PrintDebugInfo(std::ostream& aStrm) const { DwAddress::_PrintDebugInfo(aStrm); aStrm << "Full Name: " << mFullName << '\n'; aStrm << "Route: " << mRoute << '\n'; aStrm << "Local Part: " << mLocalPart << '\n'; aStrm << "Domain: " << mDomain << '\n'; } #else void DwMailbox::_PrintDebugInfo(std::ostream& ) const {} #endif // defined(DW_DEBUG_VERSION) void DwMailbox::CheckInvariants() const { #if defined(DW_DEBUG_VERSION) DwAddress::CheckInvariants(); mFullName.CheckInvariants(); mRoute.CheckInvariants(); mLocalPart.CheckInvariants(); mDomain.CheckInvariants(); #endif // defined(DW_DEBUG_VERSION) } void RemoveCrAndLf(DwString& aStr) { // Do a quick check to see if at least one CR or LF is present size_t n = aStr.find_first_of("\r\n"); if (n == DwString::npos) return; // At least one CR or LF is present, so copy the string const DwString& in = aStr; size_t inLen = in.length(); DwString out; out.reserve(inLen); int lastChar = 0; size_t i = 0; while (i < inLen) { int ch = in[i]; if (ch == (int) '\r') { out += ' '; } else if (ch == (int) '\n') { if (lastChar != (int) '\r') { out += ' '; } } else { out += (char) ch; } lastChar = ch; ++i; } aStr = out; }