Leaked source code of windows server 2003
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.
 
 
 
 
 
 

2171 lines
50 KiB

/*
* Copyright (c) 1985 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted provided
* that: (1) source distributions retain this entire copyright notice and
* comment, and (2) distributions including binaries display the following
* acknowledgement: ``This product includes software developed by the
* University of California, Berkeley and its contributors'' in the
* documentation or other materials provided with the distribution and in
* all advertising materials mentioning features or use of this software.
* Neither the name of the University nor the names of its contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
/*++
Copyright (c) 1994 Microsoft Corporation
Module Name :
address.cxx
Abstract:
This module defines the module for the address (CAddr)
class.
Author:
Rohan Phillips ( Rohanp ) 11-Dec-1995
Project:
SMTP Server DLL
Functions Exported:
Revision History:
--*/
/************************************************************
* Include Headers
************************************************************/
#include <windows.h>
#include <dbgtrace.h>
#include <cpool.h>
#include <string.h>
#include <listmacr.h>
#include <abtype.h>
#include <abook.h>
#include <address.hxx>
//initialize the pool
CPool CAddr::Pool(ADDRESS_SIGNATURE_VALID);
#if defined(TDC)
LPFNAB_FREE_MEMORY CAddr::pfnABFreeMemory = NULL;
#endif
//
// Statics
//
static BOOL pStripAddrQuotes( char *lpszAddress, char **lpDomain );
static BOOL pStripAddrSpaces(char *lpszAddress);
// Quick and dirty string validation
static BOOL pValidateStringPtr(LPSTR lpwszString, DWORD dwMaxLength)
{
if (IsBadStringPtr((LPCTSTR)lpwszString, dwMaxLength))
return(FALSE);
while (dwMaxLength--)
if (*lpwszString++ == 0)
return(TRUE);
return(FALSE);
}
//This routine scans an address for illegal characters
//in a name
BOOL IsInvalidAddr(char *Address)
{
char * addr = Address;
for (; *addr != '\0'; addr++)
{
if ((*addr & 0340) == 0200)
break;
}
if (*addr == '\0')
{
return FALSE;
}
SetLastError (ERROR_INVALID_DATA);
return TRUE;
}
// Forward declaration of pValidateLocalPartOrDomain
BOOL pValidateLocalPartOrDomain(char *lpszStart,
DWORD dwLength,
BOOL fLocalPart);
/*++
Name :
CAddr:CAddr
Description:
This is the default constructor for this class.
It just initializes the member variables.
Arguments: None
Returns:
nothing
Limitations:
--*/
CAddr::CAddr(void)
{
m_Signature = ADDRESS_SIGNATURE_VALID;
m_Flags = ADDRESS_NO_DOMAIN;
m_PlainAddrSize = 0;
m_DomainOffset = NULL;
m_HashInfo = NULL;
m_Error = 0;
m_listEntry.Flink = NULL;
m_listEntry.Blink = NULL;
}
CAddr::~CAddr(VOID)
{
m_Signature = ADDRESS_SIGNATURE_FREE;
m_Flags = 0;
m_listEntry.Flink = NULL;
m_listEntry.Blink = NULL;
m_HashInfo = NULL;
}
/*++
Name :
CAddr:CAddr(char * address)
Description:
This is the default constructor for this class.
It just initializes the member variables.
Arguments: None
Returns:
nothing
Limitations:
--*/
CAddr::CAddr(char * Address)
{
m_Signature = ADDRESS_SIGNATURE_VALID;
m_Flags = ADDRESS_NO_DOMAIN;
m_PlainAddrSize = 0;
m_DomainOffset = NULL;
m_HashInfo= NULL;
m_Error = 0;
m_listEntry.Flink = NULL;
m_listEntry.Blink = NULL;
if(Address == NULL)
{
SetLastError(ERROR_INVALID_DATA);
return;
}
// This is an email name, extract its clean representation
ExtractCleanEmailName( m_PlainAddress,
&m_DomainOffset,
&m_PlainAddrSize,
Address);
if (m_PlainAddrSize > MAX_INTERNET_NAME)
{
m_PlainAddrSize = 0;
SetLastError (ERROR_INVALID_DATA);
}
else
{
if(m_DomainOffset)
m_Flags &= (DWORD) ~ADDRESS_NO_DOMAIN; //turn off the no domain flag.
}
}
/*++
Name :
CAddr::InitializeAddress
Description:
This function parses an RFC address, stripping out
all comments, and gets back an internet address
Arguments:
Address - RCF address from client
ADDRTYPE - specify if address came from FROM or RCPT command
Returns:
TRUE if the RFC addressed was correctly parsed.
FALSE otherwise.
--*/
BOOL CAddr::InitializeAddress(char * Address, ADDRTYPE NameType)
{
if(::IsInvalidAddr(Address))
return(FALSE);
m_PlainAddress[0] = '\0';
m_DomainOffset = NULL;
m_PlainAddrSize = 0;
// We have a special case for domains treated as addresses
if (NameType == CLEANDOMAIN)
{
DWORD AddressSize = 0;
char szCleanAddress[MAX_DOMAIN_NAME+1];
AddressSize = lstrlen(Address);
if (AddressSize > MAX_DOMAIN_NAME)
{
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
// We want an ABSOLUTELY clean domain name, but we will strip
// out leading and trailing spaces for them
lstrcpy(szCleanAddress, Address);
if (!pStripAddrSpaces(szCleanAddress))
{
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
AddressSize = lstrlen(szCleanAddress);
if (!pValidateLocalPartOrDomain(szCleanAddress, AddressSize, FALSE))
{
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
// This is a clean domain name, fill in the fields, and
// return a positively
lstrcpy(m_PlainAddress, szCleanAddress);
m_PlainAddrSize = lstrlen(szCleanAddress);
m_Flags &= (DWORD) ~ADDRESS_NO_DOMAIN;
return(TRUE);
}
// This is an email name, extract its clean representation
if (!ExtractCleanEmailName( m_PlainAddress,
&m_DomainOffset,
&m_PlainAddrSize,
Address))
return(FALSE);
// Take care of the flag
if (!m_DomainOffset)
m_Flags &= (DWORD) ~ADDRESS_NO_DOMAIN;
// Further, we validate the whole local-part[@domain] email name
// Special case: if the address is <>, then we skip the validation
if (strcmp(m_PlainAddress, "<>"))
{
if (!ValidateCleanEmailName(m_PlainAddress, m_DomainOffset))
{
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
}
else if (NameType != FROMADDR)
{
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
// Take care of other anomalies ...
if (m_DomainOffset)
{
// Make sure there is a local-part
if (m_DomainOffset <= m_PlainAddress)
{
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
// EMPIRE WTOP Bug 47233: Need to accept longer email names if they add up
// to less than MAX_INTERNET_NAME. This is for replication of users across
// a site connector. Due to this reason, we removed the user name check to
// make sure it is less than MAX_EMAIL_NAME.
}
else
{
// We have no domain, make sure it is not an empty string
if (m_PlainAddress[0] == '\0')
{
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
// EMPIRE WTOP Bug 47233: Need to accept longer email names if they add up
// to less than MAX_INTERNET_NAME. This is for replication of users across
// a site connector. Due to this reason, we removed the user name check to
// make sure it is less than MAX_EMAIL_NAME.
}
return(TRUE);
}
/*++
Name :
CAddr::CreateAddress
Description:
This function allocates memory for a CAddr
class and calls StripAddrComments to
Arguments:
pszDest - Destination where new address should go
pszSrc - Source buffer of address to strip comments from
Returns:
a pointer to a CAddr if comments were stripped and the format
of the address is legal.
NULL otherwise
--*/
CAddr * CAddr::CreateAddress(char * Address, ADDRTYPE NameType)
{
CAddr * NewAddress;
//create the memory for the address
NewAddress = new CAddr ();
if (!NewAddress)
{
SetLastError (ERROR_NOT_ENOUGH_MEMORY);
return NULL;
}
//now initialize it
if (!NewAddress->InitializeAddress(Address, NameType))
{
delete NewAddress;
return NULL;
}
//we have a good address...or so we think
return NewAddress;
}
/*++
Name :
CAddr::CreateKnownAddress
Description:
This function allocates memory for a CAddr
class and sets "Address" as the internet
address for this class. No error checking
is done. It is assumed that the caller
performed all necessary error checking
Arguments:
Address - Internet Address to initialize
the class with
Returns:
a pointer to a CAddr if memory could be allocated
NULL otherwise
--*/
CAddr * CAddr::CreateKnownAddress(char * Address)
{
CAddr * NewAddress = NULL;
//create the memory for the address
NewAddress = new CAddr (Address);
if (!NewAddress)
{
SetLastError (ERROR_NOT_ENOUGH_MEMORY);
return NULL;
}
if(!NewAddress->GetAddrSize())
{
delete NewAddress;
NewAddress = NULL;
}
return NewAddress;
}
/*++
Name :
CAddr::ReplaceAddress
Description:
Replaces the address stored in this
CAddr with the one passed in
Arguments:
Address - new address
Returns:
TRUE if the address could be replaced.
FALSE if we run out of memory, the new
address is NULL, or the size of the
address is zero
--*/
BOOL CAddr::ReplaceAddress(const char * Address)
{
_ASSERT(IsValid());
if(Address == NULL)
{
SetLastError(ERROR_INVALID_DATA);
return FALSE;
}
ExtractCleanEmailName( m_PlainAddress,
&m_DomainOffset,
&m_PlainAddrSize,
(char *)Address);
if (m_PlainAddrSize > MAX_INTERNET_NAME)
{
SetLastError(ERROR_INVALID_DATA);
return FALSE;
}
if(m_DomainOffset)
m_Flags &= (DWORD) ~ADDRESS_NO_DOMAIN; //turn off the no domain flag.
return TRUE;
}
/*++
Name :
CAddr::CAddr
Description:
returns a pointer to the 1st CAddr in the local list
Arguments:
a pointer to a PLIST_ENTRY
Returns:
--*/
CAddr * CAddr::GetFirstAddress(PLIST_ENTRY HeadOfList, PLIST_ENTRY * AddressLink)
{
PLIST_ENTRY ListEntry = NULL;
CAddr * FirstAddress = NULL;
//if the list is not empty, get the first address
if(!IsListEmpty (HeadOfList))
{
ListEntry = HeadOfList->Flink;
FirstAddress = CONTAINING_RECORD( ListEntry, CAddr, m_listEntry);
_ASSERT(FirstAddress->IsValid());
*AddressLink = ListEntry->Flink; //get the next link
}
return FirstAddress;
}
/*++
Name :
CAddr::GetNextAddress
Description:
returns a pointer to the next CAddr in the local list
Arguments:
a pointer to a PLIST_ENTRY
Returns:
--*/
CAddr * CAddr::GetNextAddress(PLIST_ENTRY HeadOfList, PLIST_ENTRY * AddressLink)
{
CAddr * NextAddress = NULL;
//make sure we are not at the end of the list
if((*AddressLink) != HeadOfList)
{
NextAddress = CONTAINING_RECORD(*AddressLink, CAddr, m_listEntry);
_ASSERT(NextAddress->IsValid());
*AddressLink = (*AddressLink)->Flink;
}
return NextAddress;
}
/*++
Name :
CAddr::RemoveAllAddrs
Description:
Deletes all address from a CAddr list
Arguments:
a pointer to the head of the list
Returns:
--*/
void CAddr::RemoveAllAddrs(PLIST_ENTRY HeadOfList)
{
PLIST_ENTRY pEntry;
CAddr * pAddr;
// Remove all addresses
while (!IsListEmpty (HeadOfList))
{
pEntry = RemoveHeadList(HeadOfList);
pAddr = CONTAINING_RECORD( pEntry, CAddr, m_listEntry);
delete pAddr;
}
}
/*++
Name :
CAddr::RemoveAddress
Description:
removes an address from a list
Arguments:
a pointer to a PLIST_ENTRY
Returns:
--*/
void CAddr::RemoveAddress(IN OUT CAddr * pEntry)
{
if(pEntry != NULL)
{
_ASSERT(pEntry->IsValid());
//Remove from list of addresses
RemoveEntryList( &pEntry->QueryListEntry());
}
}
/*++
Name :
CAddr::InsertAddrHeadList
Description:
insert an address into the head of a list
Arguments:
a pointer to a PLIST_ENTRY
Returns:
--*/
void CAddr::InsertAddrHeadList(PLIST_ENTRY HeadOfList, CAddr *pEntry)
{
_ASSERT(pEntry);
_ASSERT(pEntry->IsValid());
InsertHeadList(HeadOfList, &pEntry->QueryListEntry());
}
/*++
Name :
CAddr::InsertAddrTailList
Description:
insert an address into the head of a list
Arguments:
a pointer to a PLIST_ENTRY
Returns:
--*/
void CAddr::InsertAddrTailList(PLIST_ENTRY HeadOfList, CAddr *pEntry)
{
_ASSERT(pEntry);
_ASSERT(pEntry->IsValid());
InsertTailList(HeadOfList, &pEntry->QueryListEntry());
}
// ========================================================================
//
// Validation Parser stuff added by KeithLau on 7/25/96
//
#define QUOTE_SQUARE 0x1
#define QUOTE_ANGLE 0x2
#define QUOTE_QUOTES 0x4
#define QUOTE_PARENTHESES 0x8
#define MAX_QUOTE_TYPES 3
static char acOpen[MAX_QUOTE_TYPES] = { '[', '<', '\"' };
static char acClose[MAX_QUOTE_TYPES] = { ']', '>', '\"' };
static char *pFindNextUnquotedOccurrence(char *lpszString,
char cSearch,
LPBOOL lpfNotFound,
DWORD dwDefaultState);
static inline BOOL IsControl(char ch)
{
return( ((ch >= 0) && (ch <= 31)) || (ch == 127) );
}
static BOOL IsSpecial(char ch)
{
switch (ch)
{
case '(':
case ')':
case '<':
case '>':
case '@':
case ',':
case ':':
case ';':
case '\\':
case '\"':
case '.':
case '[':
case ']':
return(TRUE);
default:
return(FALSE);
}
}
static BOOL pIsSpecialOrSpace(char ch)
{
return((ch == ' ') || (ch == '\t') || (ch == '\0') || IsSpecial(ch));
}
static BOOL pValidateAsciiString(char *lpszString)
{
// Verifies that a string only contains ASCII chars (0-127)
// Relies totally on upstream pointer checking
_ASSERT(lpszString);
if (!lpszString)
return(FALSE);
while (*lpszString)
{
if ((*lpszString >= 0) && (*lpszString <= 127))
lpszString++;
else
return(FALSE);
}
return(TRUE);
}
static BOOL pValidateAtom(char *lpszStart, DWORD dwLength)
{
// Atoms can be any ASCII char, except specials, controls, and spaces
// Note zero-length atom is invalid
_ASSERT(!IsBadStringPtr(lpszStart, dwLength));
if (!dwLength)
return(FALSE);
while (dwLength)
{
dwLength--;
if ((*lpszStart == ' ') ||
(IsSpecial(*lpszStart)) ||
(IsControl(*lpszStart))
)
{
// Process quoted (escape) char
if (*lpszStart == '\\')
{
if (!dwLength)
return(FALSE);
else
{
lpszStart++;
dwLength--;
}
}
else
return(FALSE);
}
lpszStart++;
}
return(TRUE);
}
static BOOL pValidateAtomNoWildcard(char *lpszStart, DWORD dwLength)
{
// Atoms can be any ASCII char, except specials, controls, and spaces
// Note zero-length atom is invalid
_ASSERT(!IsBadStringPtr(lpszStart, dwLength));
if (!dwLength)
return(FALSE);
while (dwLength)
{
// Apart from the usual, we also dislike the asterisk wildcard character.
// This is just for domains.
dwLength--;
if ((*lpszStart == ' ') ||
(*lpszStart == '*') ||
(IsSpecial(*lpszStart)) ||
(IsControl(*lpszStart))
)
return(FALSE);
lpszStart++;
}
return(TRUE);
}
static BOOL pValidateQtext(char *lpszStart, DWORD dwLength)
{
// Qtext can be any ASCII char, except '\"', '\\', and CR
// Note zero-length is valid
_ASSERT(!IsBadStringPtr(lpszStart, dwLength));
while (dwLength)
{
dwLength--;
if ((*lpszStart == '\"') ||
(*lpszStart == '\r')
)
return(FALSE);
// Process quoted (escape) char
if (*lpszStart == '\\')
{
if (!dwLength)
return(FALSE);
else
{
lpszStart++;
dwLength--;
}
}
lpszStart++;
}
return(TRUE);
}
static BOOL pValidateDtext(char *lpszStart, DWORD dwLength)
{
// Dtext can be any ASCII char, except '\"', '\\', and CR
// Note zero-length is valid
_ASSERT(!IsBadStringPtr(lpszStart, dwLength));
while (dwLength)
{
dwLength--;
if ((*lpszStart == '[') ||
(*lpszStart == ']') ||
(*lpszStart == '\r')
)
return(FALSE);
// Process quoted (escape) char
if (*lpszStart == '\\')
{
if (!dwLength)
return(FALSE);
else
{
lpszStart++;
dwLength--;
}
}
lpszStart++;
}
return(TRUE);
}
static BOOL pValidateQuotedString(char *lpszStart, DWORD dwLength)
{
// Quoted-stirngs are quotes between Qtext
// an empty Qtext is valid
_ASSERT(!IsBadStringPtr(lpszStart, dwLength));
if (dwLength < 2)
return(FALSE);
if ((*lpszStart != '\"') ||
(lpszStart[dwLength-1] != '\"'))
return(FALSE);
return(pValidateQtext(lpszStart + 1, dwLength - 2));
}
static BOOL pValidateDomainLiteral(char *lpszStart, DWORD dwLength)
{
// Domain-literals are square braces between Dtext
// an empty Dtext is valid
_ASSERT(!IsBadStringPtr(lpszStart, dwLength));
if (dwLength < 2)
return(FALSE);
if ((*lpszStart != '[') ||
(lpszStart[dwLength-1] != ']'))
return(FALSE);
return(pValidateDtext(lpszStart + 1, dwLength - 2));
}
static BOOL pValidateWord(char *lpszStart, DWORD dwLength)
{
// A word is any sequence of atoms and quoted-strings
// words may not be zero-length by inheritance
_ASSERT(!IsBadStringPtr(lpszStart, dwLength));
if ((pValidateAtom(lpszStart, dwLength)) ||
(pValidateQuotedString(lpszStart, dwLength)))
return(TRUE);
return(FALSE);
}
static BOOL pValidateSubdomain(char *lpszStart, DWORD dwLength)
{
// A subdomain may be an atom or domain-literal
// words may not be zero-length by inheritance
_ASSERT(!IsBadStringPtr(lpszStart, dwLength));
if ((pValidateAtomNoWildcard(lpszStart, dwLength)) ||
(pValidateDomainLiteral(lpszStart, dwLength)))
return(TRUE);
return(FALSE);
}
//
// Since validating the local part is so similar to validating the
// domain part, we write one function to do both. If fLocalPart
// is TRUE, we treat it as the local part of an email address, if
// it is FALSE, we treat it as the domain part.
//
static BOOL pValidateLocalPartOrDomain(char *lpszStart, DWORD dwLength, BOOL fLocalPart)
{
// A domain is one or more dot-delimited sub-domains, where a local-part
// is one or more dot-delimited words
// We rely on upstream calls to make sure the string is properly
// NULL-terminated
char *lpszBegin, *lpszEnd;
BOOL fNotFound;
_ASSERT(!IsBadStringPtr(lpszStart, dwLength));
lpszBegin = lpszStart;
lpszEnd = (char *)NULL;
while (lpszEnd = pFindNextUnquotedOccurrence(lpszBegin, '.', &fNotFound, 0))
{
// If it starts with a dot or has 2 dots in a row, we fail!
if (lpszBegin == lpszEnd)
return(FALSE);
// Now, check if the chunk is indeed a valid token
if (fLocalPart)
{
if (!pValidateWord(lpszBegin, (DWORD)(lpszEnd - lpszBegin)))
return(FALSE);
}
else
{
if (!pValidateSubdomain(lpszBegin, (DWORD)(lpszEnd - lpszBegin)))
return(FALSE);
}
// Allright, go to the next guy
lpszBegin = lpszEnd + 1;
}
if (!fNotFound)
return(FALSE);
// If it ends with a dot, it's also bad
lpszEnd = lpszStart + dwLength;
if (lpszEnd == lpszBegin)
return(FALSE);
// Don't forget the last chunk
if (fLocalPart)
return(pValidateWord(lpszBegin, (DWORD)(lpszEnd - lpszBegin)));
else
return(pValidateSubdomain(lpszBegin, (DWORD)(lpszEnd - lpszBegin)));
}
static BOOL pValidatePhrase(char *lpszStart, DWORD dwLength)
{
// A phrase is a collection of words, possibly separated
// by spaces
// We don't validate for now ...
_ASSERT(!IsBadStringPtr(lpszStart, dwLength));
return(TRUE);
}
//
// The following functions attempt to extract a clean addr-spec
// in the form of local-part[@domain] from a generic address
// Note that groups are not supported at this point
//
static BOOL pExtractAddressFromRouteAddress(char *lpszStart,
DWORD dwLength,
char *lpCleanAddress)
{
// A route address is in the form:
// phrase < [1#(@ domain) :] addr-spec >
char *lpMarker;
char *lpRoute;
BOOL fNotFound;
_ASSERT(!IsBadStringPtr(lpszStart, dwLength));
// First, find the opening angle brace
lpMarker = pFindNextUnquotedOccurrence(lpszStart, '<', &fNotFound, 0);
if (!lpMarker)
return(FALSE);
// Between lpStart and lpMarker is the phrase
_ASSERT(lpMarker >= lpszStart);
if (!pValidatePhrase(lpszStart, (DWORD)(lpMarker - lpszStart)))
return(FALSE);
// Now, find the closing angle bracket
_ASSERT(*lpMarker == '<');
lpszStart = lpMarker + 1;
lpMarker = pFindNextUnquotedOccurrence(lpszStart, '>',
&fNotFound, QUOTE_ANGLE);
if (!lpMarker)
return(FALSE);
_ASSERT(*lpMarker == '>');
// There should be nothing but white space after the closing brace
//Trim the whitespace or flag an error
char * lpTemp = lpMarker;
while(*++lpTemp != '\0')
{
if(*lpTemp != ' ' && *lpTemp != '\t')
return(FALSE);
}
*(lpMarker + 1) = '\0';
// The special address <> is reserved for NDR, and should be
// allowed
if (lpszStart == lpMarker)
{
lpCleanAddress[0] = '<';
lpCleanAddress[1] = '>';
lpCleanAddress[2] = '\0';
return(TRUE);
}
// The stuff enclosed in the angle braces is an addr-spec, and
// optionally preceded by route specifiers. We don't care about
// route specifiers, but we have to skip them.
lpRoute = pFindNextUnquotedOccurrence(lpszStart, ':', &fNotFound, 0);
if (!lpRoute)
{
// If we have no colon, then the whole lump should be the
// addr-spec
_ASSERT(lpMarker >= lpszStart);
*lpMarker++ = '\0';
lstrcpyn(lpCleanAddress, lpszStart, (DWORD)(lpMarker - lpszStart));
return(TRUE);
}
else
{
// We found a colon, the stuff to its right should be the
// addr-spec. As usual, we don't validate the route specifiers
lpRoute++;
_ASSERT(lpMarker >= lpRoute);
*lpMarker++ = '\0';
lstrcpyn(lpCleanAddress, lpRoute, (DWORD)(lpMarker - lpRoute));
return(TRUE);
}
}
static BOOL pExtractAddressFromMailbox( char *lpszStart,
DWORD dwLength,
char *lpCleanAddress)
{
// A mailbox can either be an addr-spec, or a route address
_ASSERT(!IsBadStringPtr(lpszStart, dwLength));
if (pExtractAddressFromRouteAddress(lpszStart, dwLength, lpCleanAddress))
return(TRUE);
// If it's not a route address, trhen it should ba an addr-spec
lstrcpyn(lpCleanAddress, lpszStart, dwLength + 1);
return(TRUE);
}
static BOOL pExtractAddressFromGroup( char *lpszStart,
DWORD dwLength,
char *lpCleanAddress)
{
// We always return false
return(FALSE);
/*
// A group is in the form:
// phrase : [#mailbox] ;
char *lpMarker;
char *lpSemiColon;
BOOL fNotFound;
_ASSERT(!IsBadStringPtr(lpszStart, dwLength));
// First, find the opening angle brace
lpMarker = pFindNextUnquotedOccurrence(lpszStart, ':', &fNotFound, 0);
if (!lpMarker || fNotFound)
return(FALSE);
// Between lpStart and lpMarker is the phrase
_ASSERT(lpMarker >= lpszStart);
if (!pValidatePhrase(lpszStart, (DWORD)(lpMarker - lpszStart)))
return(FALSE);
// Between the colon
lpMarker++;
lpSemiColon = pFindNextUnquotedOccurrence(lpMarker, ';', &fNotFound, 0);
if (!lpSemiColon || fNotFound)
return(FALSE);
_ASSERT(lpSemiColon >= lpMarker);
if (lpSemiColon == lpMarker)
;
*/
}
static BOOL pExtractAddress( char *lpszStart,
DWORD dwLength,
char *lpCleanAddress)
{
// A address is either an mailbox, or a group
_ASSERT(!IsBadStringPtr(lpszStart, dwLength));
*lpCleanAddress = '\0';
if (pExtractAddressFromMailbox(lpszStart, dwLength, lpCleanAddress) ||
pExtractAddressFromGroup(lpszStart, dwLength, lpCleanAddress))
return(TRUE);
return(FALSE);
}
// This function finds braces pairs in a given string, and returns
// pointers to the start and end of the first occurence of a
// [nested] pair of braces. The starting and ending character
// may be specified by the caller (starting and ending chars
// must be unique).
static char *pFindNextUnquotedOccurrence(char *lpszString,
char cSearch,
LPBOOL lpfNotFound,
DWORD dwDefaultState)
{
DWORD dwState = dwDefaultState;
DWORD i;
char ch;
char *lpStart = lpszString;
BOOL fFallThru;
*lpfNotFound = FALSE;
// If dwState is 0, then we are not inside any kind of quotes
while (ch = *lpStart)
{
// If we are not in any quotes, and the char is found,
// then we are done!
if (!dwState && (ch == cSearch))
return(lpStart);
// Another disgusting kludge, BUT WORKS!!
// For closing parentheses, we don't it in the
// acClose[] set, so we have to explicitly check for them
// here, since pMatchParentheses is very dependent on this.
// For open parentheses, similar case: since parentheses
// nest, we allow multiple open braces
if (dwState == QUOTE_PARENTHESES)
{
if (((cSearch == ')') || (cSearch == '(')) &&
(ch == cSearch))
return(lpStart);
}
// If it is a quoted char, we can skip it and the following
// char right away ... If the char following a quote '\' is
// the terminating NULL, we have an error.
if (ch == '\\')
{
lpStart++;
if (!*lpStart)
return(NULL);
// Quoted pairs not allowed outside quotes!
// if (!dwState)
// return(NULL);
}
else
{
// See if we have a state change ...
for (i = 0; i < MAX_QUOTE_TYPES; i++)
{
// Check the close case, too
fFallThru = TRUE;
// See if we have an opening quote of any sort
if (ch == acOpen[i])
{
// If it is an open brace, it shouldn't be a close brace
// EXCEPT: quotes.
fFallThru = FALSE;
// Special case for quotes: open = close
if (dwState & (1 << i))
{
// This is not a quoted pair, error!
// If it is a quote, and if the current state is
// inside quotes, then we let it
if ((ch == '\"') && (dwState == QUOTE_QUOTES))
fFallThru = TRUE;
else
return(NULL);
}
else if (!dwState)
{
// We are not in any quotes, so we can safely
// claim we are now inside quotes
dwState |= (1 << i);
}
}
// See if we have an closing quote of any sort
if (fFallThru && (ch == acClose[i]))
{
if (dwState & (1 << i))
{
// We are closing the correct kind of quote,
// so we cancel it ...
dwState = 0;
// Do a second check, in case we are looking
// for a close quote
if (ch == cSearch)
return(lpStart);
}
else if (!dwState)
{
// We are not in any quotes, so we have
// unmatched quotes!
return(NULL);
}
}
}
}
lpStart++;
}
*lpfNotFound = TRUE;
return(NULL);
}
static BOOL pMatchParentheses( char **ppszStart,
char **ppszEnd,
LPDWORD lpdwPairs)
{
DWORD dwIteration = 0;
DWORD dwState = 0;
char *lpszString = *ppszStart;
char *lpStart;
char *lpEnd;
BOOL fNotFound = FALSE;
*lpdwPairs = 0;
lpStart = *ppszStart;
lpEnd = *ppszEnd;
for (;;)
{
// Find open brace
if (!(lpStart = pFindNextUnquotedOccurrence(lpStart, '(', &fNotFound, dwState)))
{
// If it's not found, we're done, else it's a format error!
if (fNotFound)
break;
else
return(FALSE);
}
// Save the start position, and since we are inside the first open
// parenthesis, we force suppress all quotes mode in subsequent
// open parenthesis searches
if (!dwIteration)
{
*ppszStart = lpStart;
dwState = QUOTE_PARENTHESES;
}
// If iteration > 0 and the start pointer surpasses
// the end pointer, we have the end of the first set
// of [nested] braces
if (dwIteration && (lpStart > lpEnd))
break;
// Find close brace
if (!(lpEnd = pFindNextUnquotedOccurrence(lpEnd, ')',
&fNotFound, QUOTE_PARENTHESES)))
return(FALSE);
// Open brace pointer must always be in front of close
// brace.
if (lpStart > lpEnd)
return(FALSE);
// Next iteration
lpStart++;
lpEnd++;
dwIteration++;
}
// Fill in the end pointer and leave (start ptr already
// filled in)
if (dwIteration)
lpEnd--;
*lpdwPairs = dwIteration;
*ppszEnd = lpEnd;
return(TRUE);
}
/*++
Name :
pStripAddrComments
Description:
This function strips comments from an RFC address
Arguments:
lpszAddress - Original address comes in, and clean addres comes out
Returns:
TRUE if comments were stripped and the format
of the address is legal.
FALSE otherwise
--*/
static BOOL pStripAddrComments(char *lpszAddress)
{
char *lpCopyStart;
char *lpStart, *lpEnd;
DWORD dwPairs;
DWORD dwCopyLen;
// First, we strip the comments
// We call the function above to find matching parenthesis pairs
lpStart = lpszAddress;
lpEnd = lpszAddress;
do
{
// Mark the actual start
lpCopyStart = lpEnd;
if (!pMatchParentheses(&lpStart, &lpEnd, &dwPairs))
{
// Failed!
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
if (dwPairs)
{
// If fFound, then we found some comments
_ASSERT(*lpStart == '(');
_ASSERT(*lpEnd == ')');
// Copy the stuff over, excluding comments
_ASSERT(lpStart >= lpCopyStart);
dwCopyLen = (DWORD)(lpStart - lpCopyStart);
// Reset the pointer, and match again ...
lpEnd++;
lpStart = lpEnd;
}
else
{
dwCopyLen = lstrlen(lpCopyStart);
}
while (dwCopyLen--)
{
*lpszAddress++ = *lpCopyStart++;
}
} while (dwPairs);
// Terminate the string
*lpszAddress = '\0';
return(TRUE);
}
/*++
Name :
pStripAddrSpaces
Description:
This function strips extraneous spaces from an RFC address
An extraneous space is one that is not in a quoted pair, and
not inside any quoting pairs
Arguments:
lpszAddress - Original address comes in, and clean addres comes out
Returns:
TRUE if spaces were stripped
FALSE if any quotes/braces mismatch, or parameter error
--*/
static BOOL pStripAddrSpaces(char *lpszAddress)
{
char *lpszWrite;
char *lpszCopyStart;
char *lpszSearch;
DWORD dwCopyLen, i;
BOOL fNotFound;
BOOL fValidSpace;
char cSet[2] = { ' ', '\t' };
// First, get rid of spaces, then TABs
for (i = 0; i < 2; i++)
{
lpszWrite = lpszAddress;
lpszCopyStart = lpszAddress;
lpszSearch = lpszAddress;
do
{
lpszCopyStart = lpszSearch;
// Find unquoted space
lpszSearch = pFindNextUnquotedOccurrence(lpszSearch,
cSet[i], &fNotFound, 0);
// We cannot just allow casual spaces; An unquoted space
// must satisfy one or more of the following:
// 1) Leading space
// 2) Trailiing space
// 3) A space or TAB is in either side or both sides of the space
// 4) A special character is on either or both sides of the space
if (lpszSearch)
{
// Make sure it satisfies the above
fValidSpace = FALSE;
if (lpszSearch > lpszAddress)
{
if (pIsSpecialOrSpace(*(lpszSearch - 1)))
fValidSpace = TRUE;
}
else
fValidSpace = TRUE;
if (pIsSpecialOrSpace(*(lpszSearch + 1)))
fValidSpace = TRUE;
if (!fValidSpace)
{
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
_ASSERT(lpszSearch >= lpszCopyStart);
dwCopyLen = (DWORD)(lpszSearch - lpszCopyStart);
lpszSearch++;
}
else
dwCopyLen = lstrlen(lpszCopyStart);
// 1) Leading spaces are automatically stripped!
while (dwCopyLen--)
{
*lpszWrite++ = *lpszCopyStart++;
}
} while (lpszSearch);
// If the reason it failed is not because it cannot find one,
// we have a formatting error here!
if (!fNotFound)
{
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
*lpszWrite = '\0';
}
return(TRUE);
}
/*++
Name :
pStripAddrQuotes
Description:
This function strips all quotes in the local part of an address.
All quotes pairs within quotes are also collapsed.
Arguments:
lpszLocalPart - Local part comes in, and comes out without quotes
Returns:
TRUE if quotes were stripped
FALSE if any quotes/braces mismatch, or parameter error
--*/
static BOOL pStripAddrQuotes(char *lpszLocalPart, char **lpDomain)
{
char *lpszWrite;
char *lpszCopyStart;
char *lpszSearch;
DWORD dwCopyLen;
DWORD dwState = 0;
BOOL fNotFound;
_ASSERT(lpDomain);
// First, get rid of quotes
lpszWrite = lpszLocalPart;
lpszCopyStart = lpszLocalPart;
lpszSearch = lpszLocalPart;
do
{
lpszCopyStart = lpszSearch;
// Find next quote
lpszSearch = pFindNextUnquotedOccurrence(lpszSearch,
'\"', &fNotFound, dwState);
if (lpszSearch)
{
// Toggle the state
dwState = (dwState)?0:QUOTE_QUOTES;
// Found a quote, copy all the stuff before the space
_ASSERT(lpszSearch >= lpszCopyStart);
dwCopyLen = (DWORD)(lpszSearch - lpszCopyStart);
lpszSearch++;
// Move the domain offset back each time we find
// an unquoted quote
if (*lpDomain)
(*lpDomain)--;
}
else
dwCopyLen = lstrlen(lpszCopyStart);
while (dwCopyLen--)
{
// Another caveat: since we are stripping out the
// quotes, we must also take care of the quoted
// pairs. That is, we must remove the backslash
// delimiting each quoted pair.
if (*lpszCopyStart == '\\')
{
// If the last char of the string is a backslash, or
// if the backslash is not within quotes, error!
// CAUTION: if dwState == QUOTE_QUOTES, this means
// that the stuff we are copying is OUTSIDE of the quote,
// since we are copying stuff before the found quote
if ((dwState == QUOTE_QUOTES) || !dwCopyLen)
{
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
lpszCopyStart++;
dwCopyLen--;
}
*lpszWrite++ = *lpszCopyStart++;
}
} while (lpszSearch);
*lpszWrite = '\0';
// If the reason it failed is not because it cannot find one,
// we have a formatting error here!
if (!fNotFound)
{
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
// If we are left with no closing quote, then we also have an error
if (dwState == QUOTE_QUOTES)
{
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
return(TRUE);
}
/*++
Name :
pStripUucpRoutes
Description:
This function strips all UUCP routing paths
Arguments:
lpszAddress - lpszAddress comes in, and comes out without UUCP paths
Returns:
TRUE if UUCP routes were stripped
FALSE if any quotes/braces mismatch, or parameter error
--*/
static BOOL pStripUucpRoutes(char *lpszAddress)
{
char *lpszDomainOffset;
char *lpszCopyStart;
char *lpszSearch;
BOOL fNotFound;
if (!(lpszDomainOffset = pFindNextUnquotedOccurrence(lpszAddress, '@', &fNotFound, 0)))
if (!fNotFound)
return(FALSE);
lpszSearch = lpszAddress;
lpszCopyStart = NULL;
// Find the last BANG
while (lpszSearch = pFindNextUnquotedOccurrence(lpszSearch, '!', &fNotFound, 0))
{
// If an unquoted bang occurs after the @ sign, we will return error
if (lpszDomainOffset && (lpszSearch > lpszDomainOffset))
{
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
lpszCopyStart = lpszSearch++;
}
// If the reason is other than not found, we have an error
if (!fNotFound)
{
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
if (lpszCopyStart)
{
lpszCopyStart++;
while (*lpszCopyStart)
*lpszAddress++ = *lpszCopyStart++;
*lpszAddress = '\0';
}
return(TRUE);
}
/*++
Name :
CAddr::ValidateDomainName
Description:
Determines whether a specified domain name is valid
Arguments:
lpszDomainName - ANSI domain name string to validate
Returns:
TRUE if valid, FALSE if not
--*/
BOOL CAddr::ValidateDomainName(char *lpszDomainName)
{
char szClean[MAX_INTERNET_NAME+1];
_ASSERT(lpszDomainName);
// Routine checking
if ((!lpszDomainName) ||
(!pValidateStringPtr(lpszDomainName, MAX_INTERNET_NAME+1)))
return(FALSE);
if (!pValidateAsciiString(lpszDomainName))
return(FALSE);
// Strip all comments
lstrcpy(szClean, lpszDomainName);
if (!pStripAddrComments(szClean))
return(FALSE);
if (!pStripAddrSpaces(szClean))
return(FALSE);
// Call our appropriate private
return(pValidateLocalPartOrDomain(szClean, lstrlen(szClean), FALSE));
}
/*++
Name :
CAddr::ExtractCleanEmailName
Description:
Extracts an absolutely clean email name from an address, quotes
are NOT stripped for the local part.
Arguments:
lpszCleanEmail - Pre allocated buffer to return the clean address
ppszDomainOffset - Pointer to '@' symbol separating the local-part
and the domain; NULL if not domain is specified
in the address.
lpdwCleanEmailLength - Length of the whole clean email returned
lpszSource - Source address to clean up
Returns:
TRUE if valid, FALSE if not
--*/
BOOL CAddr::ExtractCleanEmailName( char *lpszCleanEmail,
char **ppszDomainOffset,
DWORD *lpdwCleanEmailLength,
char *lpszSource)
{
char szClean[MAX_INTERNET_NAME+1];
char *lpDomainOffset;
BOOL fNotFound;
TraceFunctEnter("CAddr::ExtractCleanEmailName");
_ASSERT(lpszSource);
_ASSERT(lpszCleanEmail);
_ASSERT(ppszDomainOffset);
_ASSERT(lpdwCleanEmailLength);
// Routine checking
if (!lpszSource ||
!pValidateStringPtr(lpszSource, MAX_INTERNET_NAME+1) ||
!lpszCleanEmail ||
IsBadWritePtr(lpszCleanEmail, MAX_INTERNET_NAME+1) ||
!ppszDomainOffset ||
IsBadWritePtr(ppszDomainOffset, sizeof(char *)) ||
!lpdwCleanEmailLength ||
IsBadWritePtr(lpdwCleanEmailLength, sizeof(DWORD)))
{
SetLastError(ERROR_INVALID_DATA);
goto LeaveWithError;
}
szClean[0] = '\0';
if (!pValidateAsciiString(lpszSource)) {
SetLastError(ERROR_INVALID_DATA);
goto LeaveWithError;
}
// Strip all comments and spaces
lstrcpy(szClean, lpszSource);
StateTrace(0, " Source: %s", szClean);
if (!pStripAddrComments(szClean))
goto LeaveWithError;
StateTrace(0, " Comments stripped: %s", szClean);
// Extract the clean email name in a simple local-part@domain
// form. However, the local part may still have UUCP headers
if (!pExtractAddress(szClean, lstrlen(szClean), lpszCleanEmail))
{
SetLastError(ERROR_INVALID_DATA);
goto LeaveWithError;
}
StateTrace(0, " Address: %s", lpszCleanEmail);
// Strip comments again ...
if (!pStripAddrComments(lpszCleanEmail)) {
SetLastError(ERROR_INVALID_DATA);
goto LeaveWithError;
}
StateTrace(0, " Comments stripped (2): %s", lpszCleanEmail);
if (!pStripAddrSpaces(lpszCleanEmail)) {
SetLastError(ERROR_INVALID_DATA);
goto LeaveWithError;
}
StateTrace(0, " Spaces stripped: %s", lpszCleanEmail);
// Now we examine the clean address, and try to locate the
// local-part and the domain
lpDomainOffset = pFindNextUnquotedOccurrence(lpszCleanEmail,
'@', &fNotFound, 0);
if (lpDomainOffset)
{
_ASSERT(lpDomainOffset >= lpszCleanEmail);
if (lpDomainOffset == lpszCleanEmail)
{
// First char cannot be '@'
SetLastError(ERROR_INVALID_DATA);
goto LeaveWithError;
}
}
else
{
// If it's not found, we assume there's no domain
if (!fNotFound)
{
SetLastError(ERROR_INVALID_DATA);
goto LeaveWithError;
}
}
*lpdwCleanEmailLength = lstrlen(lpszCleanEmail);
*ppszDomainOffset = lpDomainOffset;
TraceFunctLeave();
return(TRUE);
LeaveWithError:
ErrorTrace(0, "CAddr::ExtractCleanEmailName failed");
TraceFunctLeave();
return(FALSE);
}
/*++
Name :
CAddr::ValidateCleanEmailName
Description:
Determines whether a specified email name is valid.
The input email name must be clean, i.e. no comments,
spaces, routing specifiers, etc.
Arguments:
lpszCleanEmailName - ANSI email name string to validate
lpszDomainOffset - Pointer to '@' sign in email string
Returns:
TRUE if valid, FALSE if not
--*/
BOOL CAddr::ValidateCleanEmailName( char *lpszCleanEmailName,
char *lpszDomainOffset)
{
DWORD dwLength, dwDomainLength;
TraceFunctEnterEx(0, "CAddr::ValidateCleanEmailName");
_ASSERT(lpszCleanEmailName);
if (!lpszCleanEmailName ||
!pValidateStringPtr(lpszCleanEmailName, MAX_INTERNET_NAME+1))
{
SetLastError(ERROR_INVALID_DATA);
goto LeaveWithError;
}
if (lpszDomainOffset)
{
_ASSERT(*lpszDomainOffset == '@');
_ASSERT(lpszDomainOffset > lpszCleanEmailName);
dwLength = (DWORD)(lpszDomainOffset - lpszCleanEmailName);
dwDomainLength = lstrlen(lpszCleanEmailName) - dwLength - 1;
*lpszDomainOffset++ = '\0';
}
else
dwLength = lstrlen(lpszCleanEmailName);
StateTrace(0, " Local-part: %s", lpszCleanEmailName);
if (!pValidateLocalPartOrDomain(lpszCleanEmailName,
dwLength,
TRUE))
{
ErrorTrace(0, "Invalid local part");
goto LeaveWithError;
}
if (lpszDomainOffset)
{
StateTrace(0, " Domain: %s", lpszDomainOffset);
if (!pValidateLocalPartOrDomain(lpszDomainOffset,
dwDomainLength, FALSE))
{
ErrorTrace(0, "Invalid domain");
goto LeaveWithError;
}
}
// Restore the string ...
if (lpszDomainOffset)
*--lpszDomainOffset = '@';
TraceFunctLeave();
return(TRUE);
LeaveWithError:
if (lpszDomainOffset)
if (*--lpszDomainOffset == '\0')
*lpszDomainOffset = '@';
ErrorTrace(0, "CAddr::ValidateCleanEmailName failed");
TraceFunctLeave();
return(FALSE);
}
/*++
Name :
CAddr::ValidateEmailName
Description:
Determines whether a specified email name is valid
Arguments:
lpszEmailName - ANSI email name string to validate
fDomainOptional - TRUE if domain is optional, FLASE forces
a domain to be included.
Returns:
TRUE if valid, FALSE if not
--*/
BOOL CAddr::ValidateEmailName( char *lpszEmailName,
BOOL fDomainOptional)
{
char szSource[MAX_INTERNET_NAME+1];
char *lpDomainOffset;
DWORD dwLength;
szSource[0] = '\0';
if (!ExtractCleanEmailName( szSource,
&lpDomainOffset,
&dwLength,
lpszEmailName))
return(FALSE);
if (!fDomainOptional && !lpDomainOffset)
return(FALSE);
return(ValidateCleanEmailName(szSource, lpDomainOffset));
}
/*++
Name :
CAddr::FindStartOfDomain
Description:
Finds the start of the domain part from a CLEAN email
address. The clean address may not contain
any comments, routing specifications, UUCP addresses,
and the such. No validation is done for the "clean
address".
Arguments:
lpszCleanEmail - ANSI CLEAN email name string whose local
part to extract
Returns:
A pointer to the '@' sign separating the local part and
the domain. NULL if the '@' sign is not found. Note that
'@' signs enclosed in quotes or in a proper quoted pair
are skipped.
--*/
CHAR * CAddr::FindStartOfDomain(CHAR *lpszCleanEmail)
{
BOOL fNotFound;
return(pFindNextUnquotedOccurrence(lpszCleanEmail,
'@', &fNotFound, 0));
}
//---[ CAddr::GetRFC822AddressCount ]------------------------------------------
//
//
// Description:
// Counts the number of addresses in a RFC822 list of addresses.
// Parameters:
// IN szAddressList List of addresses to count
// Returns:
// # of recipients in list
// History:
// 2/17/99 - MikeSwa Created
//
//-----------------------------------------------------------------------------
DWORD CAddr::GetRFC822AddressCount(char *szAddressList)
{
DWORD cRecips = 0;
LPSTR szCommentStart = szAddressList;
LPSTR szCommentEnd = szAddressList;
LPSTR szSearchStart = szAddressList;
LPSTR szSearchDelimiter = NULL; //ptr to delimiter
LPSTR szLastDelimiter = NULL; //ptr to last delimiter found
BOOL fSeenAlphaNum = FALSE;
BOOL fInQuote = TRUE;
DWORD dwPairs = 0;
CHAR chSaved = '\0';
CHAR *pchChanged = NULL;
BOOL fDelimiterNotFound = TRUE;
CHAR rgchDelimiters[] = {',', ';', '\0'};
//We look for RFC822 delimiters (, or ;) that are not in comments (which
//are delimited by ()'s or which are in quotes.
//No string... no recips
if (!szAddressList)
goto Exit;
//If we have any string... we start out with 1 recip
cRecips++;
// First, we strip the comments
// We call the function above to find matching parenthesis pairs
do
{
//Set start of search to start of string being scanned for ()'s
szSearchStart = szCommentStart;
if (!pMatchParentheses(&szCommentStart, &szCommentEnd, &dwPairs))
{
// Failed!
cRecips = 0;
goto Exit;
}
if (dwPairs) //we have comments
{
//We found some comments
_ASSERT(*szCommentStart == '(');
_ASSERT(*szCommentEnd == ')');
//If the first character of our search was a '('... then we
//should not bother searching for delimiters
if (szSearchStart == szCommentStart)
{
szCommentStart = szCommentEnd + 1;
continue;
}
//Set the end of our search for delimiters & set to NULL character
pchChanged = szCommentStart;
chSaved = *szCommentStart;
*szCommentStart = '\0';
// Reset the pointers, and match again ...
szCommentEnd++;
szCommentStart = szCommentEnd;
}
else
{
//We found no further comments... there is no need to save a character
chSaved = '\0';
pchChanged = NULL;
}
//Now we will search for unqoted delimiters in this uncommented section
//Iterate over all the delimiters we have
for (CHAR *pchDelimiter = rgchDelimiters; *pchDelimiter; pchDelimiter++)
{
szSearchDelimiter = szSearchStart;
do
{
szSearchDelimiter = pFindNextUnquotedOccurrence(
szSearchDelimiter, *pchDelimiter,
&fDelimiterNotFound, 0);
if (!fDelimiterNotFound)
{
_ASSERT(*pchDelimiter == *szSearchDelimiter);
cRecips++;
if (szSearchDelimiter && (szSearchDelimiter > szLastDelimiter))
szLastDelimiter = szSearchDelimiter;
}
else
{
//We know we won't find anymore in this section
break;
}
//Keep on looping while we are still finding delimiters and we are not
//at the end of the string
} while (!fDelimiterNotFound &&
szSearchDelimiter && *szSearchDelimiter &&
*(++szSearchDelimiter));
}
//Restore changed character
if (pchChanged && ('\0' != chSaved))
*pchChanged = chSaved;
} while (dwPairs);
//Make sure the last delimiter was not at the end of the buffer
if (szLastDelimiter && cRecips && *szLastDelimiter)
{
while (*(++szLastDelimiter))
{
//if it is not a space... count it as a recipient
if (!isspace((UCHAR)*szLastDelimiter))
goto Exit;
}
//Only whitespace after last delimiter... we have counted 1 too many recips
cRecips--;
}
Exit:
return cRecips;
}
//---[ IsRecipientInRFC822AddressList ]----------------------------------------
//
//
// Description:
// Determines if a given recipient is in the given RFC822 formatted
// recipient list.
// Parameters:
// IN szAddressList Address list to check in
// IN szRecip Recipient Address to check for
// Returns:
// TRUE if there was a match
// FALSE if there was no match
// History:
// 2/17/99 - MikeSwa Created
//
//-----------------------------------------------------------------------------
BOOL CAddr::IsRecipientInRFC822AddressList(char *szAddressList, char *szRecip)
{
LPSTR szRecipEnd = NULL;
CHAR chSaved = '\0';
LPSTR szCurrentAddress = szAddressList;
BOOL fFound = FALSE;
DWORD cbAddress;
if (!szAddressList || !szRecip)
goto Exit;
//Convert everything to lower case so we match correctly
szCurrentAddress = szAddressList;
do
{
*szCurrentAddress = (CHAR) tolower(*szCurrentAddress);
} while(*(++szCurrentAddress));
szCurrentAddress = szRecip;
do
{
*szCurrentAddress = (CHAR) tolower(*szCurrentAddress);
} while(*(++szCurrentAddress));
//skip past white space in recipient
while (*szRecip && isspace((UCHAR)*szRecip))
szRecip++;
//Find and skip past extranious trailing whitespace
cbAddress = strlen(szRecip);
szRecipEnd = szRecip + cbAddress/sizeof(CHAR);
szRecipEnd--;
while (isspace((UCHAR)*szRecipEnd))
{
cbAddress--;
szRecipEnd--;
}
//Make szRecipEnd point to last space
szRecipEnd++;
//Null terminate before trailing whitespace
chSaved = *szRecipEnd;
*szRecipEnd = '\0';
//Search for addresss as substring, and see if it looks like a lone address
for (szCurrentAddress = strstr(szAddressList, szRecip);
szCurrentAddress && !fFound;
szCurrentAddress = strstr(++szCurrentAddress, szRecip))
{
//look for surrounding characters to not match partial addresses
//We don't want "user" to match "user1" or "user@foo" or "foo@user"
if ((szCurrentAddress != szAddressList))
{
if (!pIsSpecialOrSpace(*(szCurrentAddress-1)) ||
('@' == *(szCurrentAddress-1)))
continue;
}
if (szCurrentAddress[cbAddress/sizeof(CHAR)])
{
if (!pIsSpecialOrSpace(szCurrentAddress[cbAddress/sizeof(CHAR)]) ||
('@' == szCurrentAddress[cbAddress/sizeof(CHAR)]))
continue;
}
//The address looks like a match
fFound = TRUE;
break;
}
Exit:
//Restore saved space
if (szRecipEnd && chSaved)
*szRecipEnd = chSaved;
return fFound;
}