You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
525 lines
16 KiB
525 lines
16 KiB
// --------------------------------------------------------------------------------
|
|
// 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());
|
|
}
|