|
|
// --------------------------------------------------------------------------------
// Addparse.cpp
// Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
// --------------------------------------------------------------------------------
#include "pch.hxx"
#include "addparse.h"
#include "bytebuff.h"
#include "shlwapi.h"
#include <demand.h> // must be last!
// --------------------------------------------------------------------------------
// Constants
// --------------------------------------------------------------------------------
static const WCHAR c_wszAddressDelims[] = L"\",<(;"; static const WCHAR c_wszRfc822MustQuote[] = L"()<>,;:\\\"[] "; static const WCHAR c_wszSpace[] = L" "; static const WCHAR c_wszEmpty[] = L"";
// --------------------------------------------------------------------------------
// CAddressParser::Init
// --------------------------------------------------------------------------------
void CAddressParser::Init(LPCWSTR pszAddress, ULONG cchAddress) { // Give the byte buffers some static space to reduce memory allocations
m_cFriendly.Init(m_rgbStatic1, sizeof(m_rgbStatic1)); m_cEmail.Init(m_rgbStatic2, sizeof(m_rgbStatic2));
// Init the string parser
m_cString.Init(pszAddress, cchAddress, PSF_NOTRAILWS | PSF_NOFRONTWS); }
// --------------------------------------------------------------------------------
// CAddressParser::Next
// --------------------------------------------------------------------------------
HRESULT CAddressParser::Next(void) { // Locals
HRESULT hr=S_OK; WCHAR chToken;
// Reset current Friendlay and Email Buffers
m_cFriendly.SetSize(0); m_cEmail.SetSize(0);
// Outer Loop
while(1) { // Skip White Space
chToken = m_cString.ChSkipWhite();
// Done...
if (L'\0' == chToken) break;
// Parse until we hit a token
chToken = m_cString.ChParse(c_wszAddressDelims, PSF_ESCAPED | PSF_NOTRAILWS);
// No data was read
if (0 == m_cString.CbValue()) { // End of string hit
if (L'\0' == chToken) break;
// Otherwise, comma or semicolon and we have data
else if ((L',' == chToken) || (L';' == chToken)) { // If we have data, were done
if (m_cFriendly.CbData() || m_cEmail.CbData()) break;
// Otherwise, continue
else continue; } }
// Email Addresses are never quoted
if (L'\"' == chToken) { // AppendUnsure
CHECKHR(hr = _HrAppendUnsure(L'\0', L'\0'));
// Parse parameter value
chToken = m_cString.ChParse(L'\"', L'\"', PSF_ESCAPED);
// Raid-47099: We need to parse: "CN=first last/O=xyz> org/C=US"@xyz.innosoft.com
CHECKHR(hr = _HrQuotedEmail(&chToken));
// Returns S_OK if it was processed
if (S_FALSE == hr) { // Write to Friendly
CHECKHR(hr = _HrAppendFriendly()); } }
// Otherwise, < always flushes to email
else if (L'<' == chToken) { // AppendUnsure
CHECKHR(hr = _HrAppendFriendly());
// Parse parameter value
chToken = m_cString.ChParse(L">", 0);
// Didn't find the end bracket
if (L'>' == chToken) { // Write Friendly Name
CHECKHR(hr = m_cEmail.Append((LPBYTE)m_cString.PszValue(), m_cString.CbValue())); }
// Otherwise...
else { // Should have an Email Address
CHECKHR(hr = _HrAppendUnsure(L'<', L'>')); } }
// Otherwise
else { // AppendUnsure
CHECKHR(hr = _HrAppendUnsure(L'\0', L'\0'));
// If right paren, search to end
if (L'(' == chToken) { // Parse to ending paren...
chToken = m_cString.ChParse(L'(', L')', PSF_ESCAPED);
// AppendUnsure
CHECKHR(hr = _HrAppendUnsure(L'(', L')')); } }
// Done
if ((L',' == chToken) || (L';' == chToken)) break; }
// If friendly name has data, append a null, check email and return
if (m_cFriendly.CbData()) { // Append a Null
m_cFriendly.Append((LPBYTE)c_wszEmpty, sizeof(WCHAR));
// If Email is not empty, append a null
if (m_cEmail.CbData()) m_cEmail.Append((LPBYTE)c_wszEmpty, sizeof(WCHAR)); }
// Otherwise, if email has data, append null and return
else if (m_cEmail.CbData()) { // If Email is not empty, append a null
m_cEmail.Append((LPBYTE)c_wszEmpty, sizeof(WCHAR)); }
// Are we really done ?
else if (L'\0' == chToken) { hr = TrapError(MIME_E_NO_DATA); goto exit; }
// Skip Commas and semicolons
if (L',' == chToken) m_cString.ChSkip(L","); else if (L';' == chToken) m_cString.ChSkip(L";");
exit: // Done
return hr; }
// --------------------------------------------------------------------------------
// CAddressParser::_HrQuotedEmail
// --------------------------------------------------------------------------------
HRESULT CAddressParser::_HrQuotedEmail(WCHAR *pchToken) { // Locals
HRESULT hr=S_OK; ULONG cchT=0; WCHAR chDelim; BOOL fSeenAt=FALSE; WCHAR ch; WCHAR szToken[2];
// Invalid Arg
Assert(pchToken);
// We should have some data
if (0 == m_cString.CbValue()) return S_OK;
// Get the character
ch = m_cString.ChPeekNext(0);
// Check for DBCS
if (L'@' != ch) return S_FALSE;
// Look ahead and check for: "CN=first last/O=xyz> org/C=US"@xyz.innosoft.com
while(1) { // Get the character
ch = m_cString.ChPeekNext(cchT);
// Breaking Character
if (L'\0' == ch || L' ' == ch || L',' == ch || L';' == ch || L'<' == ch || L'>' == ch || L'(' == ch || L')' == ch) break;
// At Sign?
if (L'@' == ch) fSeenAt = TRUE;
// Increment
cchT++; }
// No At Sign
if (0 == cchT || FALSE == fSeenAt) return S_FALSE;
// Append Email Address
CHECKHR(hr = m_cEmail.Append((LPBYTE)c_wszDoubleQuote, 2));
// Append Email Address
CHECKHR(hr = m_cEmail.Append((LPBYTE)m_cString.PszValue(), m_cString.CbValue()));
// Append Email Address
CHECKHR(hr = m_cEmail.Append((LPBYTE)c_wszDoubleQuote, 2));
// Setup szToken
szToken[0] = (L'\0' == ch) ? L' ' : ch; szToken[1] = L'\0';
// Seek to next space
ch = m_cString.ChParse(szToken, PSF_NOCOMMENTS); Assert(szToken[0] == ch || L'\0' == ch);
// If there is data
if (m_cString.CbValue() > 0) { // Append the Email Address
CHECKHR(hr = m_cEmail.Append((LPBYTE)m_cString.PszValue(), m_cString.CbValue())); }
// End Token
*pchToken = szToken[0];
exit: // Done
return hr; }
// --------------------------------------------------------------------------------
// CAddressParser::_HrIsEmailAddress
// --------------------------------------------------------------------------------
HRESULT CAddressParser::_HrIsEmailAddress(WCHAR chStart, WCHAR chEnd, BOOL *pfIsEmail) { // Locals
HRESULT hr=S_OK; WCHAR chToken; CStringParserW cString;
// Invalid Arg
Assert(pfIsEmail);
// Init
*pfIsEmail = FALSE;
// Init
cString.Init(m_cString.PszValue(), m_cString.CchValue(), PSF_NOCOMMENTS | PSF_ESCAPED | PSF_NOTRAILWS | PSF_NOFRONTWS);
// Parse to the end to remove comments
if (L'\0' != cString.ChParse(c_wszEmpty) || 0 == cString.CbValue()) return S_OK;
// Parse String
if (NULL == StrChrW(cString.PszValue(), L' ')) { // If in brackets, then its an email address for sure
if (L'<' == chStart && L'>' == chEnd) { // Is Email
*pfIsEmail = TRUE;
// Write Friendly Name
CHECKHR(hr = m_cEmail.Append((LPBYTE)cString.PszValue(), cString.CbValue())); }
// Look for the last '@' sign and see if their are escapeable chars before the at sign
else { // Locals
LPWSTR pszT=(LPWSTR)cString.PszValue(); LPWSTR pszLastAt=NULL; ULONG cQuoteBeforeAt=0; ULONG cQuoteAfterAt=0;
// Raid - 62104: Outlook98 doesn't handle Lotus Domino RFC822 Address Construction
while(*pszT) { // Check for '@' sign
if (L'@' == *pszT) { // If we already saw an at sign, move cQuoteAfterAt to cQuoteBeforeAt
if (pszLastAt) { cQuoteBeforeAt += cQuoteAfterAt; cQuoteAfterAt = 0; }
// Save Last At
pszLastAt = pszT; }
// See if *pszT is in c_szRfc822MustQuote
else if (NULL != StrChrW(c_wszRfc822MustQuote, *pszT)) { // If we've seen an at sign, track quote after at
if (pszLastAt) cQuoteAfterAt++; else cQuoteBeforeAt++; }
// Increment
pszT++; }
// Only if we saw an '@' sign
if (NULL != pszLastAt) { // Is Email
*pfIsEmail = TRUE;
// If there were not chars that need quoting...
if (0 == cQuoteBeforeAt) { // Write Friendly Name
CHECKHR(hr = m_cEmail.Append((LPBYTE)cString.PszValue(), cString.CbValue())); }
// "Mailroute_TstSCC1[BOFATEST.MRTSTSCC]%SSW%EMAILDOM%BETA"@bankamerica.com
else { // Locals
ULONG cbComplete=cString.CbValue(); ULONG cbFirstPart=(ULONG)(pszLastAt - cString.PszValue()); ULONG cbLastPart=cbComplete - cbFirstPart;
// Append Doulbe Quote
CHECKHR(hr = m_cEmail.Append((LPBYTE)c_wszDoubleQuote, 2));
// Append Firt part before last at
CHECKHR(hr = m_cEmail.Append((LPBYTE)cString.PszValue(), cbFirstPart));
// Append Email Address
CHECKHR(hr = m_cEmail.Append((LPBYTE)c_wszDoubleQuote, 2));
// Append Firt part before last at
CHECKHR(hr = m_cEmail.Append((LPBYTE)pszLastAt, cbLastPart)); } } } }
exit: // Done
return hr; }
// --------------------------------------------------------------------------------
// CAddressParser::_HrAppendUnsure
// --------------------------------------------------------------------------------
HRESULT CAddressParser::_HrAppendUnsure(WCHAR chStart, WCHAR chEnd) { // Locals
HRESULT hr=S_OK; BOOL fIsEmail=FALSE;
// We have data
if (0 == m_cString.CbValue()) goto exit;
// Email is not set yet ?
if (m_cEmail.CbData() == 0) { // Is current parsed string an address ?
CHECKHR(hr = _HrIsEmailAddress(chStart, chEnd, &fIsEmail)); }
// Not an Eamil Address
if (FALSE == fIsEmail && m_cString.CbValue() > 0) { // Append a space
if (m_cFriendly.CbData() > 0) { // Add a space
CHECKHR(hr = m_cFriendly.Append((LPBYTE)c_wszSpace, sizeof(WCHAR)));
// Start Character
if (chStart) { // Append Start Delimiter
CHECKHR(hr = m_cFriendly.Append((LPBYTE)&chStart, sizeof(WCHAR))); } }
// Otherwise, don't write ending terminator
else chEnd = L'\0';
// Write Friendly Name
CHECKHR(hr = m_cFriendly.Append((LPBYTE)m_cString.PszValue(), m_cString.CbValue()));
// Start Character
if (chEnd) { // Append Start Delimiter
CHECKHR(hr = m_cFriendly.Append((LPBYTE)&chEnd, sizeof(WCHAR))); } }
exit: // Done
return hr; }
// --------------------------------------------------------------------------------
// CAddressParser::_HrAppendFriendly
// --------------------------------------------------------------------------------
HRESULT CAddressParser::_HrAppendFriendly(void) { // Locals
HRESULT hr=S_OK;
// We should have some data
if (0 == m_cString.CbValue()) return S_OK;
// Append a space
if (m_cFriendly.CbData() > 0) { // Add a space
CHECKHR(hr = m_cFriendly.Append((LPBYTE)c_wszSpace, sizeof(WCHAR))); }
// Write Friendly Name
CHECKHR(hr = m_cFriendly.Append((LPBYTE)m_cString.PszValue(), m_cString.CbValue()));
exit: // Done
return hr; }
// --------------------------------------------------------------------------------
// CAddressParser::PszFriendly
// --------------------------------------------------------------------------------
LPCWSTR CAddressParser::PszFriendly(void) { // We should have one or the other
if (0 == m_cFriendly.CbData() && 0 == m_cEmail.CbData()) { AssertSz(FALSE, "This is a bug in CAddressParser, should never have an empty friendly and email."); return c_wszEmpty; }
// Return It
return (m_cFriendly.CbData() ? (LPCWSTR)m_cFriendly.PbData() : PszEmail()); }
// --------------------------------------------------------------------------------
// CAddressParser::CchFriendly
// --------------------------------------------------------------------------------
ULONG CAddressParser::CchFriendly(void) { // We should have one or the other
if (0 == m_cFriendly.CbData() && 0 == m_cEmail.CbData()) { AssertSz(FALSE, "This is a bug in CAddressParser, should never have an empty friendly and email."); return 0; }
// Return It
return (m_cFriendly.CbData() ? (m_cFriendly.CbData() - sizeof(WCHAR)) / sizeof(WCHAR) : CchEmail()); }
// --------------------------------------------------------------------------------
// CAddressParser::PszEmail
// --------------------------------------------------------------------------------
LPCWSTR CAddressParser::PszEmail(void) { // We should have one or the other
if (0 == m_cFriendly.CbData() && 0 == m_cEmail.CbData()) { AssertSz(FALSE, "This is a bug in CAddressParser, should never have an empty friendly and email."); return c_wszEmpty; }
// Return It
return (m_cEmail.CbData() ? (LPCWSTR)m_cEmail.PbData() : PszFriendly()); }
// --------------------------------------------------------------------------------
// CAddressParser::CchEmail
// --------------------------------------------------------------------------------
ULONG CAddressParser::CchEmail(void) { // We should have one or the other
if (0 == m_cFriendly.CbData() && 0 == m_cEmail.CbData()) { AssertSz(FALSE, "This is a bug in CAddressParser, should never have an empty friendly and email."); return 0; }
// Return It
return (m_cEmail.CbData() ? (m_cEmail.CbData() - sizeof(WCHAR)) / sizeof(WCHAR) : CchFriendly()); }
|