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

  1. /*
  2. * Copyright (c) 1985 Regents of the University of California.
  3. * All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms are permitted provided
  6. * that: (1) source distributions retain this entire copyright notice and
  7. * comment, and (2) distributions including binaries display the following
  8. * acknowledgement: ``This product includes software developed by the
  9. * University of California, Berkeley and its contributors'' in the
  10. * documentation or other materials provided with the distribution and in
  11. * all advertising materials mentioning features or use of this software.
  12. * Neither the name of the University nor the names of its contributors may
  13. * be used to endorse or promote products derived from this software without
  14. * specific prior written permission.
  15. * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
  16. * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
  17. * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  18. */
  19. /*++
  20. Copyright (c) 1994 Microsoft Corporation
  21. Module Name :
  22. address.cxx
  23. Abstract:
  24. This module defines the module for the address (CAddr)
  25. class.
  26. Author:
  27. Rohan Phillips ( Rohanp ) 11-Dec-1995
  28. Project:
  29. SMTP Server DLL
  30. Functions Exported:
  31. Revision History:
  32. --*/
  33. /************************************************************
  34. * Include Headers
  35. ************************************************************/
  36. #include <windows.h>
  37. #include <dbgtrace.h>
  38. #include <cpool.h>
  39. #include <string.h>
  40. #include <listmacr.h>
  41. #include <abtype.h>
  42. #include <abook.h>
  43. #include <address.hxx>
  44. //initialize the pool
  45. CPool CAddr::Pool(ADDRESS_SIGNATURE_VALID);
  46. #if defined(TDC)
  47. LPFNAB_FREE_MEMORY CAddr::pfnABFreeMemory = NULL;
  48. #endif
  49. //
  50. // Statics
  51. //
  52. static BOOL pStripAddrQuotes( char *lpszAddress, char **lpDomain );
  53. static BOOL pStripAddrSpaces(char *lpszAddress);
  54. // Quick and dirty string validation
  55. static BOOL pValidateStringPtr(LPSTR lpwszString, DWORD dwMaxLength)
  56. {
  57. if (IsBadStringPtr((LPCTSTR)lpwszString, dwMaxLength))
  58. return(FALSE);
  59. while (dwMaxLength--)
  60. if (*lpwszString++ == 0)
  61. return(TRUE);
  62. return(FALSE);
  63. }
  64. //This routine scans an address for illegal characters
  65. //in a name
  66. BOOL IsInvalidAddr(char *Address)
  67. {
  68. char * addr = Address;
  69. for (; *addr != '\0'; addr++)
  70. {
  71. if ((*addr & 0340) == 0200)
  72. break;
  73. }
  74. if (*addr == '\0')
  75. {
  76. return FALSE;
  77. }
  78. SetLastError (ERROR_INVALID_DATA);
  79. return TRUE;
  80. }
  81. // Forward declaration of pValidateLocalPartOrDomain
  82. BOOL pValidateLocalPartOrDomain(char *lpszStart,
  83. DWORD dwLength,
  84. BOOL fLocalPart);
  85. /*++
  86. Name :
  87. CAddr:CAddr
  88. Description:
  89. This is the default constructor for this class.
  90. It just initializes the member variables.
  91. Arguments: None
  92. Returns:
  93. nothing
  94. Limitations:
  95. --*/
  96. CAddr::CAddr(void)
  97. {
  98. m_Signature = ADDRESS_SIGNATURE_VALID;
  99. m_Flags = ADDRESS_NO_DOMAIN;
  100. m_PlainAddrSize = 0;
  101. m_DomainOffset = NULL;
  102. m_HashInfo = NULL;
  103. m_Error = 0;
  104. m_listEntry.Flink = NULL;
  105. m_listEntry.Blink = NULL;
  106. }
  107. CAddr::~CAddr(VOID)
  108. {
  109. m_Signature = ADDRESS_SIGNATURE_FREE;
  110. m_Flags = 0;
  111. m_listEntry.Flink = NULL;
  112. m_listEntry.Blink = NULL;
  113. m_HashInfo = NULL;
  114. }
  115. /*++
  116. Name :
  117. CAddr:CAddr(char * address)
  118. Description:
  119. This is the default constructor for this class.
  120. It just initializes the member variables.
  121. Arguments: None
  122. Returns:
  123. nothing
  124. Limitations:
  125. --*/
  126. CAddr::CAddr(char * Address)
  127. {
  128. m_Signature = ADDRESS_SIGNATURE_VALID;
  129. m_Flags = ADDRESS_NO_DOMAIN;
  130. m_PlainAddrSize = 0;
  131. m_DomainOffset = NULL;
  132. m_HashInfo= NULL;
  133. m_Error = 0;
  134. m_listEntry.Flink = NULL;
  135. m_listEntry.Blink = NULL;
  136. if(Address == NULL)
  137. {
  138. SetLastError(ERROR_INVALID_DATA);
  139. return;
  140. }
  141. // This is an email name, extract its clean representation
  142. ExtractCleanEmailName( m_PlainAddress,
  143. &m_DomainOffset,
  144. &m_PlainAddrSize,
  145. Address);
  146. if (m_PlainAddrSize > MAX_INTERNET_NAME)
  147. {
  148. m_PlainAddrSize = 0;
  149. SetLastError (ERROR_INVALID_DATA);
  150. }
  151. else
  152. {
  153. if(m_DomainOffset)
  154. m_Flags &= (DWORD) ~ADDRESS_NO_DOMAIN; //turn off the no domain flag.
  155. }
  156. }
  157. /*++
  158. Name :
  159. CAddr::InitializeAddress
  160. Description:
  161. This function parses an RFC address, stripping out
  162. all comments, and gets back an internet address
  163. Arguments:
  164. Address - RCF address from client
  165. ADDRTYPE - specify if address came from FROM or RCPT command
  166. Returns:
  167. TRUE if the RFC addressed was correctly parsed.
  168. FALSE otherwise.
  169. --*/
  170. BOOL CAddr::InitializeAddress(char * Address, ADDRTYPE NameType)
  171. {
  172. if(::IsInvalidAddr(Address))
  173. return(FALSE);
  174. m_PlainAddress[0] = '\0';
  175. m_DomainOffset = NULL;
  176. m_PlainAddrSize = 0;
  177. // We have a special case for domains treated as addresses
  178. if (NameType == CLEANDOMAIN)
  179. {
  180. DWORD AddressSize = 0;
  181. char szCleanAddress[MAX_DOMAIN_NAME+1];
  182. AddressSize = lstrlen(Address);
  183. if (AddressSize > MAX_DOMAIN_NAME)
  184. {
  185. SetLastError(ERROR_INVALID_DATA);
  186. return(FALSE);
  187. }
  188. // We want an ABSOLUTELY clean domain name, but we will strip
  189. // out leading and trailing spaces for them
  190. lstrcpy(szCleanAddress, Address);
  191. if (!pStripAddrSpaces(szCleanAddress))
  192. {
  193. SetLastError(ERROR_INVALID_DATA);
  194. return(FALSE);
  195. }
  196. AddressSize = lstrlen(szCleanAddress);
  197. if (!pValidateLocalPartOrDomain(szCleanAddress, AddressSize, FALSE))
  198. {
  199. SetLastError(ERROR_INVALID_DATA);
  200. return(FALSE);
  201. }
  202. // This is a clean domain name, fill in the fields, and
  203. // return a positively
  204. lstrcpy(m_PlainAddress, szCleanAddress);
  205. m_PlainAddrSize = lstrlen(szCleanAddress);
  206. m_Flags &= (DWORD) ~ADDRESS_NO_DOMAIN;
  207. return(TRUE);
  208. }
  209. // This is an email name, extract its clean representation
  210. if (!ExtractCleanEmailName( m_PlainAddress,
  211. &m_DomainOffset,
  212. &m_PlainAddrSize,
  213. Address))
  214. return(FALSE);
  215. // Take care of the flag
  216. if (!m_DomainOffset)
  217. m_Flags &= (DWORD) ~ADDRESS_NO_DOMAIN;
  218. // Further, we validate the whole local-part[@domain] email name
  219. // Special case: if the address is <>, then we skip the validation
  220. if (strcmp(m_PlainAddress, "<>"))
  221. {
  222. if (!ValidateCleanEmailName(m_PlainAddress, m_DomainOffset))
  223. {
  224. SetLastError(ERROR_INVALID_DATA);
  225. return(FALSE);
  226. }
  227. }
  228. else if (NameType != FROMADDR)
  229. {
  230. SetLastError(ERROR_INVALID_DATA);
  231. return(FALSE);
  232. }
  233. // Take care of other anomalies ...
  234. if (m_DomainOffset)
  235. {
  236. // Make sure there is a local-part
  237. if (m_DomainOffset <= m_PlainAddress)
  238. {
  239. SetLastError(ERROR_INVALID_DATA);
  240. return(FALSE);
  241. }
  242. // EMPIRE WTOP Bug 47233: Need to accept longer email names if they add up
  243. // to less than MAX_INTERNET_NAME. This is for replication of users across
  244. // a site connector. Due to this reason, we removed the user name check to
  245. // make sure it is less than MAX_EMAIL_NAME.
  246. }
  247. else
  248. {
  249. // We have no domain, make sure it is not an empty string
  250. if (m_PlainAddress[0] == '\0')
  251. {
  252. SetLastError(ERROR_INVALID_DATA);
  253. return(FALSE);
  254. }
  255. // EMPIRE WTOP Bug 47233: Need to accept longer email names if they add up
  256. // to less than MAX_INTERNET_NAME. This is for replication of users across
  257. // a site connector. Due to this reason, we removed the user name check to
  258. // make sure it is less than MAX_EMAIL_NAME.
  259. }
  260. return(TRUE);
  261. }
  262. /*++
  263. Name :
  264. CAddr::CreateAddress
  265. Description:
  266. This function allocates memory for a CAddr
  267. class and calls StripAddrComments to
  268. Arguments:
  269. pszDest - Destination where new address should go
  270. pszSrc - Source buffer of address to strip comments from
  271. Returns:
  272. a pointer to a CAddr if comments were stripped and the format
  273. of the address is legal.
  274. NULL otherwise
  275. --*/
  276. CAddr * CAddr::CreateAddress(char * Address, ADDRTYPE NameType)
  277. {
  278. CAddr * NewAddress;
  279. //create the memory for the address
  280. NewAddress = new CAddr ();
  281. if (!NewAddress)
  282. {
  283. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  284. return NULL;
  285. }
  286. //now initialize it
  287. if (!NewAddress->InitializeAddress(Address, NameType))
  288. {
  289. delete NewAddress;
  290. return NULL;
  291. }
  292. //we have a good address...or so we think
  293. return NewAddress;
  294. }
  295. /*++
  296. Name :
  297. CAddr::CreateKnownAddress
  298. Description:
  299. This function allocates memory for a CAddr
  300. class and sets "Address" as the internet
  301. address for this class. No error checking
  302. is done. It is assumed that the caller
  303. performed all necessary error checking
  304. Arguments:
  305. Address - Internet Address to initialize
  306. the class with
  307. Returns:
  308. a pointer to a CAddr if memory could be allocated
  309. NULL otherwise
  310. --*/
  311. CAddr * CAddr::CreateKnownAddress(char * Address)
  312. {
  313. CAddr * NewAddress = NULL;
  314. //create the memory for the address
  315. NewAddress = new CAddr (Address);
  316. if (!NewAddress)
  317. {
  318. SetLastError (ERROR_NOT_ENOUGH_MEMORY);
  319. return NULL;
  320. }
  321. if(!NewAddress->GetAddrSize())
  322. {
  323. delete NewAddress;
  324. NewAddress = NULL;
  325. }
  326. return NewAddress;
  327. }
  328. /*++
  329. Name :
  330. CAddr::ReplaceAddress
  331. Description:
  332. Replaces the address stored in this
  333. CAddr with the one passed in
  334. Arguments:
  335. Address - new address
  336. Returns:
  337. TRUE if the address could be replaced.
  338. FALSE if we run out of memory, the new
  339. address is NULL, or the size of the
  340. address is zero
  341. --*/
  342. BOOL CAddr::ReplaceAddress(const char * Address)
  343. {
  344. _ASSERT(IsValid());
  345. if(Address == NULL)
  346. {
  347. SetLastError(ERROR_INVALID_DATA);
  348. return FALSE;
  349. }
  350. ExtractCleanEmailName( m_PlainAddress,
  351. &m_DomainOffset,
  352. &m_PlainAddrSize,
  353. (char *)Address);
  354. if (m_PlainAddrSize > MAX_INTERNET_NAME)
  355. {
  356. SetLastError(ERROR_INVALID_DATA);
  357. return FALSE;
  358. }
  359. if(m_DomainOffset)
  360. m_Flags &= (DWORD) ~ADDRESS_NO_DOMAIN; //turn off the no domain flag.
  361. return TRUE;
  362. }
  363. /*++
  364. Name :
  365. CAddr::CAddr
  366. Description:
  367. returns a pointer to the 1st CAddr in the local list
  368. Arguments:
  369. a pointer to a PLIST_ENTRY
  370. Returns:
  371. --*/
  372. CAddr * CAddr::GetFirstAddress(PLIST_ENTRY HeadOfList, PLIST_ENTRY * AddressLink)
  373. {
  374. PLIST_ENTRY ListEntry = NULL;
  375. CAddr * FirstAddress = NULL;
  376. //if the list is not empty, get the first address
  377. if(!IsListEmpty (HeadOfList))
  378. {
  379. ListEntry = HeadOfList->Flink;
  380. FirstAddress = CONTAINING_RECORD( ListEntry, CAddr, m_listEntry);
  381. _ASSERT(FirstAddress->IsValid());
  382. *AddressLink = ListEntry->Flink; //get the next link
  383. }
  384. return FirstAddress;
  385. }
  386. /*++
  387. Name :
  388. CAddr::GetNextAddress
  389. Description:
  390. returns a pointer to the next CAddr in the local list
  391. Arguments:
  392. a pointer to a PLIST_ENTRY
  393. Returns:
  394. --*/
  395. CAddr * CAddr::GetNextAddress(PLIST_ENTRY HeadOfList, PLIST_ENTRY * AddressLink)
  396. {
  397. CAddr * NextAddress = NULL;
  398. //make sure we are not at the end of the list
  399. if((*AddressLink) != HeadOfList)
  400. {
  401. NextAddress = CONTAINING_RECORD(*AddressLink, CAddr, m_listEntry);
  402. _ASSERT(NextAddress->IsValid());
  403. *AddressLink = (*AddressLink)->Flink;
  404. }
  405. return NextAddress;
  406. }
  407. /*++
  408. Name :
  409. CAddr::RemoveAllAddrs
  410. Description:
  411. Deletes all address from a CAddr list
  412. Arguments:
  413. a pointer to the head of the list
  414. Returns:
  415. --*/
  416. void CAddr::RemoveAllAddrs(PLIST_ENTRY HeadOfList)
  417. {
  418. PLIST_ENTRY pEntry;
  419. CAddr * pAddr;
  420. // Remove all addresses
  421. while (!IsListEmpty (HeadOfList))
  422. {
  423. pEntry = RemoveHeadList(HeadOfList);
  424. pAddr = CONTAINING_RECORD( pEntry, CAddr, m_listEntry);
  425. delete pAddr;
  426. }
  427. }
  428. /*++
  429. Name :
  430. CAddr::RemoveAddress
  431. Description:
  432. removes an address from a list
  433. Arguments:
  434. a pointer to a PLIST_ENTRY
  435. Returns:
  436. --*/
  437. void CAddr::RemoveAddress(IN OUT CAddr * pEntry)
  438. {
  439. if(pEntry != NULL)
  440. {
  441. _ASSERT(pEntry->IsValid());
  442. //Remove from list of addresses
  443. RemoveEntryList( &pEntry->QueryListEntry());
  444. }
  445. }
  446. /*++
  447. Name :
  448. CAddr::InsertAddrHeadList
  449. Description:
  450. insert an address into the head of a list
  451. Arguments:
  452. a pointer to a PLIST_ENTRY
  453. Returns:
  454. --*/
  455. void CAddr::InsertAddrHeadList(PLIST_ENTRY HeadOfList, CAddr *pEntry)
  456. {
  457. _ASSERT(pEntry);
  458. _ASSERT(pEntry->IsValid());
  459. InsertHeadList(HeadOfList, &pEntry->QueryListEntry());
  460. }
  461. /*++
  462. Name :
  463. CAddr::InsertAddrTailList
  464. Description:
  465. insert an address into the head of a list
  466. Arguments:
  467. a pointer to a PLIST_ENTRY
  468. Returns:
  469. --*/
  470. void CAddr::InsertAddrTailList(PLIST_ENTRY HeadOfList, CAddr *pEntry)
  471. {
  472. _ASSERT(pEntry);
  473. _ASSERT(pEntry->IsValid());
  474. InsertTailList(HeadOfList, &pEntry->QueryListEntry());
  475. }
  476. // ========================================================================
  477. //
  478. // Validation Parser stuff added by KeithLau on 7/25/96
  479. //
  480. #define QUOTE_SQUARE 0x1
  481. #define QUOTE_ANGLE 0x2
  482. #define QUOTE_QUOTES 0x4
  483. #define QUOTE_PARENTHESES 0x8
  484. #define MAX_QUOTE_TYPES 3
  485. static char acOpen[MAX_QUOTE_TYPES] = { '[', '<', '\"' };
  486. static char acClose[MAX_QUOTE_TYPES] = { ']', '>', '\"' };
  487. static char *pFindNextUnquotedOccurrence(char *lpszString,
  488. char cSearch,
  489. LPBOOL lpfNotFound,
  490. DWORD dwDefaultState);
  491. static inline BOOL IsControl(char ch)
  492. {
  493. return( ((ch >= 0) && (ch <= 31)) || (ch == 127) );
  494. }
  495. static BOOL IsSpecial(char ch)
  496. {
  497. switch (ch)
  498. {
  499. case '(':
  500. case ')':
  501. case '<':
  502. case '>':
  503. case '@':
  504. case ',':
  505. case ':':
  506. case ';':
  507. case '\\':
  508. case '\"':
  509. case '.':
  510. case '[':
  511. case ']':
  512. return(TRUE);
  513. default:
  514. return(FALSE);
  515. }
  516. }
  517. static BOOL pIsSpecialOrSpace(char ch)
  518. {
  519. return((ch == ' ') || (ch == '\t') || (ch == '\0') || IsSpecial(ch));
  520. }
  521. static BOOL pValidateAsciiString(char *lpszString)
  522. {
  523. // Verifies that a string only contains ASCII chars (0-127)
  524. // Relies totally on upstream pointer checking
  525. _ASSERT(lpszString);
  526. if (!lpszString)
  527. return(FALSE);
  528. while (*lpszString)
  529. {
  530. if ((*lpszString >= 0) && (*lpszString <= 127))
  531. lpszString++;
  532. else
  533. return(FALSE);
  534. }
  535. return(TRUE);
  536. }
  537. static BOOL pValidateAtom(char *lpszStart, DWORD dwLength)
  538. {
  539. // Atoms can be any ASCII char, except specials, controls, and spaces
  540. // Note zero-length atom is invalid
  541. _ASSERT(!IsBadStringPtr(lpszStart, dwLength));
  542. if (!dwLength)
  543. return(FALSE);
  544. while (dwLength)
  545. {
  546. dwLength--;
  547. if ((*lpszStart == ' ') ||
  548. (IsSpecial(*lpszStart)) ||
  549. (IsControl(*lpszStart))
  550. )
  551. {
  552. // Process quoted (escape) char
  553. if (*lpszStart == '\\')
  554. {
  555. if (!dwLength)
  556. return(FALSE);
  557. else
  558. {
  559. lpszStart++;
  560. dwLength--;
  561. }
  562. }
  563. else
  564. return(FALSE);
  565. }
  566. lpszStart++;
  567. }
  568. return(TRUE);
  569. }
  570. static BOOL pValidateAtomNoWildcard(char *lpszStart, DWORD dwLength)
  571. {
  572. // Atoms can be any ASCII char, except specials, controls, and spaces
  573. // Note zero-length atom is invalid
  574. _ASSERT(!IsBadStringPtr(lpszStart, dwLength));
  575. if (!dwLength)
  576. return(FALSE);
  577. while (dwLength)
  578. {
  579. // Apart from the usual, we also dislike the asterisk wildcard character.
  580. // This is just for domains.
  581. dwLength--;
  582. if ((*lpszStart == ' ') ||
  583. (*lpszStart == '*') ||
  584. (IsSpecial(*lpszStart)) ||
  585. (IsControl(*lpszStart))
  586. )
  587. return(FALSE);
  588. lpszStart++;
  589. }
  590. return(TRUE);
  591. }
  592. static BOOL pValidateQtext(char *lpszStart, DWORD dwLength)
  593. {
  594. // Qtext can be any ASCII char, except '\"', '\\', and CR
  595. // Note zero-length is valid
  596. _ASSERT(!IsBadStringPtr(lpszStart, dwLength));
  597. while (dwLength)
  598. {
  599. dwLength--;
  600. if ((*lpszStart == '\"') ||
  601. (*lpszStart == '\r')
  602. )
  603. return(FALSE);
  604. // Process quoted (escape) char
  605. if (*lpszStart == '\\')
  606. {
  607. if (!dwLength)
  608. return(FALSE);
  609. else
  610. {
  611. lpszStart++;
  612. dwLength--;
  613. }
  614. }
  615. lpszStart++;
  616. }
  617. return(TRUE);
  618. }
  619. static BOOL pValidateDtext(char *lpszStart, DWORD dwLength)
  620. {
  621. // Dtext can be any ASCII char, except '\"', '\\', and CR
  622. // Note zero-length is valid
  623. _ASSERT(!IsBadStringPtr(lpszStart, dwLength));
  624. while (dwLength)
  625. {
  626. dwLength--;
  627. if ((*lpszStart == '[') ||
  628. (*lpszStart == ']') ||
  629. (*lpszStart == '\r')
  630. )
  631. return(FALSE);
  632. // Process quoted (escape) char
  633. if (*lpszStart == '\\')
  634. {
  635. if (!dwLength)
  636. return(FALSE);
  637. else
  638. {
  639. lpszStart++;
  640. dwLength--;
  641. }
  642. }
  643. lpszStart++;
  644. }
  645. return(TRUE);
  646. }
  647. static BOOL pValidateQuotedString(char *lpszStart, DWORD dwLength)
  648. {
  649. // Quoted-stirngs are quotes between Qtext
  650. // an empty Qtext is valid
  651. _ASSERT(!IsBadStringPtr(lpszStart, dwLength));
  652. if (dwLength < 2)
  653. return(FALSE);
  654. if ((*lpszStart != '\"') ||
  655. (lpszStart[dwLength-1] != '\"'))
  656. return(FALSE);
  657. return(pValidateQtext(lpszStart + 1, dwLength - 2));
  658. }
  659. static BOOL pValidateDomainLiteral(char *lpszStart, DWORD dwLength)
  660. {
  661. // Domain-literals are square braces between Dtext
  662. // an empty Dtext is valid
  663. _ASSERT(!IsBadStringPtr(lpszStart, dwLength));
  664. if (dwLength < 2)
  665. return(FALSE);
  666. if ((*lpszStart != '[') ||
  667. (lpszStart[dwLength-1] != ']'))
  668. return(FALSE);
  669. return(pValidateDtext(lpszStart + 1, dwLength - 2));
  670. }
  671. static BOOL pValidateWord(char *lpszStart, DWORD dwLength)
  672. {
  673. // A word is any sequence of atoms and quoted-strings
  674. // words may not be zero-length by inheritance
  675. _ASSERT(!IsBadStringPtr(lpszStart, dwLength));
  676. if ((pValidateAtom(lpszStart, dwLength)) ||
  677. (pValidateQuotedString(lpszStart, dwLength)))
  678. return(TRUE);
  679. return(FALSE);
  680. }
  681. static BOOL pValidateSubdomain(char *lpszStart, DWORD dwLength)
  682. {
  683. // A subdomain may be an atom or domain-literal
  684. // words may not be zero-length by inheritance
  685. _ASSERT(!IsBadStringPtr(lpszStart, dwLength));
  686. if ((pValidateAtomNoWildcard(lpszStart, dwLength)) ||
  687. (pValidateDomainLiteral(lpszStart, dwLength)))
  688. return(TRUE);
  689. return(FALSE);
  690. }
  691. //
  692. // Since validating the local part is so similar to validating the
  693. // domain part, we write one function to do both. If fLocalPart
  694. // is TRUE, we treat it as the local part of an email address, if
  695. // it is FALSE, we treat it as the domain part.
  696. //
  697. static BOOL pValidateLocalPartOrDomain(char *lpszStart, DWORD dwLength, BOOL fLocalPart)
  698. {
  699. // A domain is one or more dot-delimited sub-domains, where a local-part
  700. // is one or more dot-delimited words
  701. // We rely on upstream calls to make sure the string is properly
  702. // NULL-terminated
  703. char *lpszBegin, *lpszEnd;
  704. BOOL fNotFound;
  705. _ASSERT(!IsBadStringPtr(lpszStart, dwLength));
  706. lpszBegin = lpszStart;
  707. lpszEnd = (char *)NULL;
  708. while (lpszEnd = pFindNextUnquotedOccurrence(lpszBegin, '.', &fNotFound, 0))
  709. {
  710. // If it starts with a dot or has 2 dots in a row, we fail!
  711. if (lpszBegin == lpszEnd)
  712. return(FALSE);
  713. // Now, check if the chunk is indeed a valid token
  714. if (fLocalPart)
  715. {
  716. if (!pValidateWord(lpszBegin, (DWORD)(lpszEnd - lpszBegin)))
  717. return(FALSE);
  718. }
  719. else
  720. {
  721. if (!pValidateSubdomain(lpszBegin, (DWORD)(lpszEnd - lpszBegin)))
  722. return(FALSE);
  723. }
  724. // Allright, go to the next guy
  725. lpszBegin = lpszEnd + 1;
  726. }
  727. if (!fNotFound)
  728. return(FALSE);
  729. // If it ends with a dot, it's also bad
  730. lpszEnd = lpszStart + dwLength;
  731. if (lpszEnd == lpszBegin)
  732. return(FALSE);
  733. // Don't forget the last chunk
  734. if (fLocalPart)
  735. return(pValidateWord(lpszBegin, (DWORD)(lpszEnd - lpszBegin)));
  736. else
  737. return(pValidateSubdomain(lpszBegin, (DWORD)(lpszEnd - lpszBegin)));
  738. }
  739. static BOOL pValidatePhrase(char *lpszStart, DWORD dwLength)
  740. {
  741. // A phrase is a collection of words, possibly separated
  742. // by spaces
  743. // We don't validate for now ...
  744. _ASSERT(!IsBadStringPtr(lpszStart, dwLength));
  745. return(TRUE);
  746. }
  747. //
  748. // The following functions attempt to extract a clean addr-spec
  749. // in the form of local-part[@domain] from a generic address
  750. // Note that groups are not supported at this point
  751. //
  752. static BOOL pExtractAddressFromRouteAddress(char *lpszStart,
  753. DWORD dwLength,
  754. char *lpCleanAddress)
  755. {
  756. // A route address is in the form:
  757. // phrase < [1#(@ domain) :] addr-spec >
  758. char *lpMarker;
  759. char *lpRoute;
  760. BOOL fNotFound;
  761. _ASSERT(!IsBadStringPtr(lpszStart, dwLength));
  762. // First, find the opening angle brace
  763. lpMarker = pFindNextUnquotedOccurrence(lpszStart, '<', &fNotFound, 0);
  764. if (!lpMarker)
  765. return(FALSE);
  766. // Between lpStart and lpMarker is the phrase
  767. _ASSERT(lpMarker >= lpszStart);
  768. if (!pValidatePhrase(lpszStart, (DWORD)(lpMarker - lpszStart)))
  769. return(FALSE);
  770. // Now, find the closing angle bracket
  771. _ASSERT(*lpMarker == '<');
  772. lpszStart = lpMarker + 1;
  773. lpMarker = pFindNextUnquotedOccurrence(lpszStart, '>',
  774. &fNotFound, QUOTE_ANGLE);
  775. if (!lpMarker)
  776. return(FALSE);
  777. _ASSERT(*lpMarker == '>');
  778. // There should be nothing but white space after the closing brace
  779. //Trim the whitespace or flag an error
  780. char * lpTemp = lpMarker;
  781. while(*++lpTemp != '\0')
  782. {
  783. if(*lpTemp != ' ' && *lpTemp != '\t')
  784. return(FALSE);
  785. }
  786. *(lpMarker + 1) = '\0';
  787. // The special address <> is reserved for NDR, and should be
  788. // allowed
  789. if (lpszStart == lpMarker)
  790. {
  791. lpCleanAddress[0] = '<';
  792. lpCleanAddress[1] = '>';
  793. lpCleanAddress[2] = '\0';
  794. return(TRUE);
  795. }
  796. // The stuff enclosed in the angle braces is an addr-spec, and
  797. // optionally preceded by route specifiers. We don't care about
  798. // route specifiers, but we have to skip them.
  799. lpRoute = pFindNextUnquotedOccurrence(lpszStart, ':', &fNotFound, 0);
  800. if (!lpRoute)
  801. {
  802. // If we have no colon, then the whole lump should be the
  803. // addr-spec
  804. _ASSERT(lpMarker >= lpszStart);
  805. *lpMarker++ = '\0';
  806. lstrcpyn(lpCleanAddress, lpszStart, (DWORD)(lpMarker - lpszStart));
  807. return(TRUE);
  808. }
  809. else
  810. {
  811. // We found a colon, the stuff to its right should be the
  812. // addr-spec. As usual, we don't validate the route specifiers
  813. lpRoute++;
  814. _ASSERT(lpMarker >= lpRoute);
  815. *lpMarker++ = '\0';
  816. lstrcpyn(lpCleanAddress, lpRoute, (DWORD)(lpMarker - lpRoute));
  817. return(TRUE);
  818. }
  819. }
  820. static BOOL pExtractAddressFromMailbox( char *lpszStart,
  821. DWORD dwLength,
  822. char *lpCleanAddress)
  823. {
  824. // A mailbox can either be an addr-spec, or a route address
  825. _ASSERT(!IsBadStringPtr(lpszStart, dwLength));
  826. if (pExtractAddressFromRouteAddress(lpszStart, dwLength, lpCleanAddress))
  827. return(TRUE);
  828. // If it's not a route address, trhen it should ba an addr-spec
  829. lstrcpyn(lpCleanAddress, lpszStart, dwLength + 1);
  830. return(TRUE);
  831. }
  832. static BOOL pExtractAddressFromGroup( char *lpszStart,
  833. DWORD dwLength,
  834. char *lpCleanAddress)
  835. {
  836. // We always return false
  837. return(FALSE);
  838. /*
  839. // A group is in the form:
  840. // phrase : [#mailbox] ;
  841. char *lpMarker;
  842. char *lpSemiColon;
  843. BOOL fNotFound;
  844. _ASSERT(!IsBadStringPtr(lpszStart, dwLength));
  845. // First, find the opening angle brace
  846. lpMarker = pFindNextUnquotedOccurrence(lpszStart, ':', &fNotFound, 0);
  847. if (!lpMarker || fNotFound)
  848. return(FALSE);
  849. // Between lpStart and lpMarker is the phrase
  850. _ASSERT(lpMarker >= lpszStart);
  851. if (!pValidatePhrase(lpszStart, (DWORD)(lpMarker - lpszStart)))
  852. return(FALSE);
  853. // Between the colon
  854. lpMarker++;
  855. lpSemiColon = pFindNextUnquotedOccurrence(lpMarker, ';', &fNotFound, 0);
  856. if (!lpSemiColon || fNotFound)
  857. return(FALSE);
  858. _ASSERT(lpSemiColon >= lpMarker);
  859. if (lpSemiColon == lpMarker)
  860. ;
  861. */
  862. }
  863. static BOOL pExtractAddress( char *lpszStart,
  864. DWORD dwLength,
  865. char *lpCleanAddress)
  866. {
  867. // A address is either an mailbox, or a group
  868. _ASSERT(!IsBadStringPtr(lpszStart, dwLength));
  869. *lpCleanAddress = '\0';
  870. if (pExtractAddressFromMailbox(lpszStart, dwLength, lpCleanAddress) ||
  871. pExtractAddressFromGroup(lpszStart, dwLength, lpCleanAddress))
  872. return(TRUE);
  873. return(FALSE);
  874. }
  875. // This function finds braces pairs in a given string, and returns
  876. // pointers to the start and end of the first occurence of a
  877. // [nested] pair of braces. The starting and ending character
  878. // may be specified by the caller (starting and ending chars
  879. // must be unique).
  880. static char *pFindNextUnquotedOccurrence(char *lpszString,
  881. char cSearch,
  882. LPBOOL lpfNotFound,
  883. DWORD dwDefaultState)
  884. {
  885. DWORD dwState = dwDefaultState;
  886. DWORD i;
  887. char ch;
  888. char *lpStart = lpszString;
  889. BOOL fFallThru;
  890. *lpfNotFound = FALSE;
  891. // If dwState is 0, then we are not inside any kind of quotes
  892. while (ch = *lpStart)
  893. {
  894. // If we are not in any quotes, and the char is found,
  895. // then we are done!
  896. if (!dwState && (ch == cSearch))
  897. return(lpStart);
  898. // Another disgusting kludge, BUT WORKS!!
  899. // For closing parentheses, we don't it in the
  900. // acClose[] set, so we have to explicitly check for them
  901. // here, since pMatchParentheses is very dependent on this.
  902. // For open parentheses, similar case: since parentheses
  903. // nest, we allow multiple open braces
  904. if (dwState == QUOTE_PARENTHESES)
  905. {
  906. if (((cSearch == ')') || (cSearch == '(')) &&
  907. (ch == cSearch))
  908. return(lpStart);
  909. }
  910. // If it is a quoted char, we can skip it and the following
  911. // char right away ... If the char following a quote '\' is
  912. // the terminating NULL, we have an error.
  913. if (ch == '\\')
  914. {
  915. lpStart++;
  916. if (!*lpStart)
  917. return(NULL);
  918. // Quoted pairs not allowed outside quotes!
  919. // if (!dwState)
  920. // return(NULL);
  921. }
  922. else
  923. {
  924. // See if we have a state change ...
  925. for (i = 0; i < MAX_QUOTE_TYPES; i++)
  926. {
  927. // Check the close case, too
  928. fFallThru = TRUE;
  929. // See if we have an opening quote of any sort
  930. if (ch == acOpen[i])
  931. {
  932. // If it is an open brace, it shouldn't be a close brace
  933. // EXCEPT: quotes.
  934. fFallThru = FALSE;
  935. // Special case for quotes: open = close
  936. if (dwState & (1 << i))
  937. {
  938. // This is not a quoted pair, error!
  939. // If it is a quote, and if the current state is
  940. // inside quotes, then we let it
  941. if ((ch == '\"') && (dwState == QUOTE_QUOTES))
  942. fFallThru = TRUE;
  943. else
  944. return(NULL);
  945. }
  946. else if (!dwState)
  947. {
  948. // We are not in any quotes, so we can safely
  949. // claim we are now inside quotes
  950. dwState |= (1 << i);
  951. }
  952. }
  953. // See if we have an closing quote of any sort
  954. if (fFallThru && (ch == acClose[i]))
  955. {
  956. if (dwState & (1 << i))
  957. {
  958. // We are closing the correct kind of quote,
  959. // so we cancel it ...
  960. dwState = 0;
  961. // Do a second check, in case we are looking
  962. // for a close quote
  963. if (ch == cSearch)
  964. return(lpStart);
  965. }
  966. else if (!dwState)
  967. {
  968. // We are not in any quotes, so we have
  969. // unmatched quotes!
  970. return(NULL);
  971. }
  972. }
  973. }
  974. }
  975. lpStart++;
  976. }
  977. *lpfNotFound = TRUE;
  978. return(NULL);
  979. }
  980. static BOOL pMatchParentheses( char **ppszStart,
  981. char **ppszEnd,
  982. LPDWORD lpdwPairs)
  983. {
  984. DWORD dwIteration = 0;
  985. DWORD dwState = 0;
  986. char *lpszString = *ppszStart;
  987. char *lpStart;
  988. char *lpEnd;
  989. BOOL fNotFound = FALSE;
  990. *lpdwPairs = 0;
  991. lpStart = *ppszStart;
  992. lpEnd = *ppszEnd;
  993. for (;;)
  994. {
  995. // Find open brace
  996. if (!(lpStart = pFindNextUnquotedOccurrence(lpStart, '(', &fNotFound, dwState)))
  997. {
  998. // If it's not found, we're done, else it's a format error!
  999. if (fNotFound)
  1000. break;
  1001. else
  1002. return(FALSE);
  1003. }
  1004. // Save the start position, and since we are inside the first open
  1005. // parenthesis, we force suppress all quotes mode in subsequent
  1006. // open parenthesis searches
  1007. if (!dwIteration)
  1008. {
  1009. *ppszStart = lpStart;
  1010. dwState = QUOTE_PARENTHESES;
  1011. }
  1012. // If iteration > 0 and the start pointer surpasses
  1013. // the end pointer, we have the end of the first set
  1014. // of [nested] braces
  1015. if (dwIteration && (lpStart > lpEnd))
  1016. break;
  1017. // Find close brace
  1018. if (!(lpEnd = pFindNextUnquotedOccurrence(lpEnd, ')',
  1019. &fNotFound, QUOTE_PARENTHESES)))
  1020. return(FALSE);
  1021. // Open brace pointer must always be in front of close
  1022. // brace.
  1023. if (lpStart > lpEnd)
  1024. return(FALSE);
  1025. // Next iteration
  1026. lpStart++;
  1027. lpEnd++;
  1028. dwIteration++;
  1029. }
  1030. // Fill in the end pointer and leave (start ptr already
  1031. // filled in)
  1032. if (dwIteration)
  1033. lpEnd--;
  1034. *lpdwPairs = dwIteration;
  1035. *ppszEnd = lpEnd;
  1036. return(TRUE);
  1037. }
  1038. /*++
  1039. Name :
  1040. pStripAddrComments
  1041. Description:
  1042. This function strips comments from an RFC address
  1043. Arguments:
  1044. lpszAddress - Original address comes in, and clean addres comes out
  1045. Returns:
  1046. TRUE if comments were stripped and the format
  1047. of the address is legal.
  1048. FALSE otherwise
  1049. --*/
  1050. static BOOL pStripAddrComments(char *lpszAddress)
  1051. {
  1052. char *lpCopyStart;
  1053. char *lpStart, *lpEnd;
  1054. DWORD dwPairs;
  1055. DWORD dwCopyLen;
  1056. // First, we strip the comments
  1057. // We call the function above to find matching parenthesis pairs
  1058. lpStart = lpszAddress;
  1059. lpEnd = lpszAddress;
  1060. do
  1061. {
  1062. // Mark the actual start
  1063. lpCopyStart = lpEnd;
  1064. if (!pMatchParentheses(&lpStart, &lpEnd, &dwPairs))
  1065. {
  1066. // Failed!
  1067. SetLastError(ERROR_INVALID_DATA);
  1068. return(FALSE);
  1069. }
  1070. if (dwPairs)
  1071. {
  1072. // If fFound, then we found some comments
  1073. _ASSERT(*lpStart == '(');
  1074. _ASSERT(*lpEnd == ')');
  1075. // Copy the stuff over, excluding comments
  1076. _ASSERT(lpStart >= lpCopyStart);
  1077. dwCopyLen = (DWORD)(lpStart - lpCopyStart);
  1078. // Reset the pointer, and match again ...
  1079. lpEnd++;
  1080. lpStart = lpEnd;
  1081. }
  1082. else
  1083. {
  1084. dwCopyLen = lstrlen(lpCopyStart);
  1085. }
  1086. while (dwCopyLen--)
  1087. {
  1088. *lpszAddress++ = *lpCopyStart++;
  1089. }
  1090. } while (dwPairs);
  1091. // Terminate the string
  1092. *lpszAddress = '\0';
  1093. return(TRUE);
  1094. }
  1095. /*++
  1096. Name :
  1097. pStripAddrSpaces
  1098. Description:
  1099. This function strips extraneous spaces from an RFC address
  1100. An extraneous space is one that is not in a quoted pair, and
  1101. not inside any quoting pairs
  1102. Arguments:
  1103. lpszAddress - Original address comes in, and clean addres comes out
  1104. Returns:
  1105. TRUE if spaces were stripped
  1106. FALSE if any quotes/braces mismatch, or parameter error
  1107. --*/
  1108. static BOOL pStripAddrSpaces(char *lpszAddress)
  1109. {
  1110. char *lpszWrite;
  1111. char *lpszCopyStart;
  1112. char *lpszSearch;
  1113. DWORD dwCopyLen, i;
  1114. BOOL fNotFound;
  1115. BOOL fValidSpace;
  1116. char cSet[2] = { ' ', '\t' };
  1117. // First, get rid of spaces, then TABs
  1118. for (i = 0; i < 2; i++)
  1119. {
  1120. lpszWrite = lpszAddress;
  1121. lpszCopyStart = lpszAddress;
  1122. lpszSearch = lpszAddress;
  1123. do
  1124. {
  1125. lpszCopyStart = lpszSearch;
  1126. // Find unquoted space
  1127. lpszSearch = pFindNextUnquotedOccurrence(lpszSearch,
  1128. cSet[i], &fNotFound, 0);
  1129. // We cannot just allow casual spaces; An unquoted space
  1130. // must satisfy one or more of the following:
  1131. // 1) Leading space
  1132. // 2) Trailiing space
  1133. // 3) A space or TAB is in either side or both sides of the space
  1134. // 4) A special character is on either or both sides of the space
  1135. if (lpszSearch)
  1136. {
  1137. // Make sure it satisfies the above
  1138. fValidSpace = FALSE;
  1139. if (lpszSearch > lpszAddress)
  1140. {
  1141. if (pIsSpecialOrSpace(*(lpszSearch - 1)))
  1142. fValidSpace = TRUE;
  1143. }
  1144. else
  1145. fValidSpace = TRUE;
  1146. if (pIsSpecialOrSpace(*(lpszSearch + 1)))
  1147. fValidSpace = TRUE;
  1148. if (!fValidSpace)
  1149. {
  1150. SetLastError(ERROR_INVALID_DATA);
  1151. return(FALSE);
  1152. }
  1153. _ASSERT(lpszSearch >= lpszCopyStart);
  1154. dwCopyLen = (DWORD)(lpszSearch - lpszCopyStart);
  1155. lpszSearch++;
  1156. }
  1157. else
  1158. dwCopyLen = lstrlen(lpszCopyStart);
  1159. // 1) Leading spaces are automatically stripped!
  1160. while (dwCopyLen--)
  1161. {
  1162. *lpszWrite++ = *lpszCopyStart++;
  1163. }
  1164. } while (lpszSearch);
  1165. // If the reason it failed is not because it cannot find one,
  1166. // we have a formatting error here!
  1167. if (!fNotFound)
  1168. {
  1169. SetLastError(ERROR_INVALID_DATA);
  1170. return(FALSE);
  1171. }
  1172. *lpszWrite = '\0';
  1173. }
  1174. return(TRUE);
  1175. }
  1176. /*++
  1177. Name :
  1178. pStripAddrQuotes
  1179. Description:
  1180. This function strips all quotes in the local part of an address.
  1181. All quotes pairs within quotes are also collapsed.
  1182. Arguments:
  1183. lpszLocalPart - Local part comes in, and comes out without quotes
  1184. Returns:
  1185. TRUE if quotes were stripped
  1186. FALSE if any quotes/braces mismatch, or parameter error
  1187. --*/
  1188. static BOOL pStripAddrQuotes(char *lpszLocalPart, char **lpDomain)
  1189. {
  1190. char *lpszWrite;
  1191. char *lpszCopyStart;
  1192. char *lpszSearch;
  1193. DWORD dwCopyLen;
  1194. DWORD dwState = 0;
  1195. BOOL fNotFound;
  1196. _ASSERT(lpDomain);
  1197. // First, get rid of quotes
  1198. lpszWrite = lpszLocalPart;
  1199. lpszCopyStart = lpszLocalPart;
  1200. lpszSearch = lpszLocalPart;
  1201. do
  1202. {
  1203. lpszCopyStart = lpszSearch;
  1204. // Find next quote
  1205. lpszSearch = pFindNextUnquotedOccurrence(lpszSearch,
  1206. '\"', &fNotFound, dwState);
  1207. if (lpszSearch)
  1208. {
  1209. // Toggle the state
  1210. dwState = (dwState)?0:QUOTE_QUOTES;
  1211. // Found a quote, copy all the stuff before the space
  1212. _ASSERT(lpszSearch >= lpszCopyStart);
  1213. dwCopyLen = (DWORD)(lpszSearch - lpszCopyStart);
  1214. lpszSearch++;
  1215. // Move the domain offset back each time we find
  1216. // an unquoted quote
  1217. if (*lpDomain)
  1218. (*lpDomain)--;
  1219. }
  1220. else
  1221. dwCopyLen = lstrlen(lpszCopyStart);
  1222. while (dwCopyLen--)
  1223. {
  1224. // Another caveat: since we are stripping out the
  1225. // quotes, we must also take care of the quoted
  1226. // pairs. That is, we must remove the backslash
  1227. // delimiting each quoted pair.
  1228. if (*lpszCopyStart == '\\')
  1229. {
  1230. // If the last char of the string is a backslash, or
  1231. // if the backslash is not within quotes, error!
  1232. // CAUTION: if dwState == QUOTE_QUOTES, this means
  1233. // that the stuff we are copying is OUTSIDE of the quote,
  1234. // since we are copying stuff before the found quote
  1235. if ((dwState == QUOTE_QUOTES) || !dwCopyLen)
  1236. {
  1237. SetLastError(ERROR_INVALID_DATA);
  1238. return(FALSE);
  1239. }
  1240. lpszCopyStart++;
  1241. dwCopyLen--;
  1242. }
  1243. *lpszWrite++ = *lpszCopyStart++;
  1244. }
  1245. } while (lpszSearch);
  1246. *lpszWrite = '\0';
  1247. // If the reason it failed is not because it cannot find one,
  1248. // we have a formatting error here!
  1249. if (!fNotFound)
  1250. {
  1251. SetLastError(ERROR_INVALID_DATA);
  1252. return(FALSE);
  1253. }
  1254. // If we are left with no closing quote, then we also have an error
  1255. if (dwState == QUOTE_QUOTES)
  1256. {
  1257. SetLastError(ERROR_INVALID_DATA);
  1258. return(FALSE);
  1259. }
  1260. return(TRUE);
  1261. }
  1262. /*++
  1263. Name :
  1264. pStripUucpRoutes
  1265. Description:
  1266. This function strips all UUCP routing paths
  1267. Arguments:
  1268. lpszAddress - lpszAddress comes in, and comes out without UUCP paths
  1269. Returns:
  1270. TRUE if UUCP routes were stripped
  1271. FALSE if any quotes/braces mismatch, or parameter error
  1272. --*/
  1273. static BOOL pStripUucpRoutes(char *lpszAddress)
  1274. {
  1275. char *lpszDomainOffset;
  1276. char *lpszCopyStart;
  1277. char *lpszSearch;
  1278. BOOL fNotFound;
  1279. if (!(lpszDomainOffset = pFindNextUnquotedOccurrence(lpszAddress, '@', &fNotFound, 0)))
  1280. if (!fNotFound)
  1281. return(FALSE);
  1282. lpszSearch = lpszAddress;
  1283. lpszCopyStart = NULL;
  1284. // Find the last BANG
  1285. while (lpszSearch = pFindNextUnquotedOccurrence(lpszSearch, '!', &fNotFound, 0))
  1286. {
  1287. // If an unquoted bang occurs after the @ sign, we will return error
  1288. if (lpszDomainOffset && (lpszSearch > lpszDomainOffset))
  1289. {
  1290. SetLastError(ERROR_INVALID_DATA);
  1291. return(FALSE);
  1292. }
  1293. lpszCopyStart = lpszSearch++;
  1294. }
  1295. // If the reason is other than not found, we have an error
  1296. if (!fNotFound)
  1297. {
  1298. SetLastError(ERROR_INVALID_DATA);
  1299. return(FALSE);
  1300. }
  1301. if (lpszCopyStart)
  1302. {
  1303. lpszCopyStart++;
  1304. while (*lpszCopyStart)
  1305. *lpszAddress++ = *lpszCopyStart++;
  1306. *lpszAddress = '\0';
  1307. }
  1308. return(TRUE);
  1309. }
  1310. /*++
  1311. Name :
  1312. CAddr::ValidateDomainName
  1313. Description:
  1314. Determines whether a specified domain name is valid
  1315. Arguments:
  1316. lpszDomainName - ANSI domain name string to validate
  1317. Returns:
  1318. TRUE if valid, FALSE if not
  1319. --*/
  1320. BOOL CAddr::ValidateDomainName(char *lpszDomainName)
  1321. {
  1322. char szClean[MAX_INTERNET_NAME+1];
  1323. _ASSERT(lpszDomainName);
  1324. // Routine checking
  1325. if ((!lpszDomainName) ||
  1326. (!pValidateStringPtr(lpszDomainName, MAX_INTERNET_NAME+1)))
  1327. return(FALSE);
  1328. if (!pValidateAsciiString(lpszDomainName))
  1329. return(FALSE);
  1330. // Strip all comments
  1331. lstrcpy(szClean, lpszDomainName);
  1332. if (!pStripAddrComments(szClean))
  1333. return(FALSE);
  1334. if (!pStripAddrSpaces(szClean))
  1335. return(FALSE);
  1336. // Call our appropriate private
  1337. return(pValidateLocalPartOrDomain(szClean, lstrlen(szClean), FALSE));
  1338. }
  1339. /*++
  1340. Name :
  1341. CAddr::ExtractCleanEmailName
  1342. Description:
  1343. Extracts an absolutely clean email name from an address, quotes
  1344. are NOT stripped for the local part.
  1345. Arguments:
  1346. lpszCleanEmail - Pre allocated buffer to return the clean address
  1347. ppszDomainOffset - Pointer to '@' symbol separating the local-part
  1348. and the domain; NULL if not domain is specified
  1349. in the address.
  1350. lpdwCleanEmailLength - Length of the whole clean email returned
  1351. lpszSource - Source address to clean up
  1352. Returns:
  1353. TRUE if valid, FALSE if not
  1354. --*/
  1355. BOOL CAddr::ExtractCleanEmailName( char *lpszCleanEmail,
  1356. char **ppszDomainOffset,
  1357. DWORD *lpdwCleanEmailLength,
  1358. char *lpszSource)
  1359. {
  1360. char szClean[MAX_INTERNET_NAME+1];
  1361. char *lpDomainOffset;
  1362. BOOL fNotFound;
  1363. TraceFunctEnter("CAddr::ExtractCleanEmailName");
  1364. _ASSERT(lpszSource);
  1365. _ASSERT(lpszCleanEmail);
  1366. _ASSERT(ppszDomainOffset);
  1367. _ASSERT(lpdwCleanEmailLength);
  1368. // Routine checking
  1369. if (!lpszSource ||
  1370. !pValidateStringPtr(lpszSource, MAX_INTERNET_NAME+1) ||
  1371. !lpszCleanEmail ||
  1372. IsBadWritePtr(lpszCleanEmail, MAX_INTERNET_NAME+1) ||
  1373. !ppszDomainOffset ||
  1374. IsBadWritePtr(ppszDomainOffset, sizeof(char *)) ||
  1375. !lpdwCleanEmailLength ||
  1376. IsBadWritePtr(lpdwCleanEmailLength, sizeof(DWORD)))
  1377. {
  1378. SetLastError(ERROR_INVALID_DATA);
  1379. goto LeaveWithError;
  1380. }
  1381. szClean[0] = '\0';
  1382. if (!pValidateAsciiString(lpszSource)) {
  1383. SetLastError(ERROR_INVALID_DATA);
  1384. goto LeaveWithError;
  1385. }
  1386. // Strip all comments and spaces
  1387. lstrcpy(szClean, lpszSource);
  1388. StateTrace(0, " Source: %s", szClean);
  1389. if (!pStripAddrComments(szClean))
  1390. goto LeaveWithError;
  1391. StateTrace(0, " Comments stripped: %s", szClean);
  1392. // Extract the clean email name in a simple local-part@domain
  1393. // form. However, the local part may still have UUCP headers
  1394. if (!pExtractAddress(szClean, lstrlen(szClean), lpszCleanEmail))
  1395. {
  1396. SetLastError(ERROR_INVALID_DATA);
  1397. goto LeaveWithError;
  1398. }
  1399. StateTrace(0, " Address: %s", lpszCleanEmail);
  1400. // Strip comments again ...
  1401. if (!pStripAddrComments(lpszCleanEmail)) {
  1402. SetLastError(ERROR_INVALID_DATA);
  1403. goto LeaveWithError;
  1404. }
  1405. StateTrace(0, " Comments stripped (2): %s", lpszCleanEmail);
  1406. if (!pStripAddrSpaces(lpszCleanEmail)) {
  1407. SetLastError(ERROR_INVALID_DATA);
  1408. goto LeaveWithError;
  1409. }
  1410. StateTrace(0, " Spaces stripped: %s", lpszCleanEmail);
  1411. // Now we examine the clean address, and try to locate the
  1412. // local-part and the domain
  1413. lpDomainOffset = pFindNextUnquotedOccurrence(lpszCleanEmail,
  1414. '@', &fNotFound, 0);
  1415. if (lpDomainOffset)
  1416. {
  1417. _ASSERT(lpDomainOffset >= lpszCleanEmail);
  1418. if (lpDomainOffset == lpszCleanEmail)
  1419. {
  1420. // First char cannot be '@'
  1421. SetLastError(ERROR_INVALID_DATA);
  1422. goto LeaveWithError;
  1423. }
  1424. }
  1425. else
  1426. {
  1427. // If it's not found, we assume there's no domain
  1428. if (!fNotFound)
  1429. {
  1430. SetLastError(ERROR_INVALID_DATA);
  1431. goto LeaveWithError;
  1432. }
  1433. }
  1434. *lpdwCleanEmailLength = lstrlen(lpszCleanEmail);
  1435. *ppszDomainOffset = lpDomainOffset;
  1436. TraceFunctLeave();
  1437. return(TRUE);
  1438. LeaveWithError:
  1439. ErrorTrace(0, "CAddr::ExtractCleanEmailName failed");
  1440. TraceFunctLeave();
  1441. return(FALSE);
  1442. }
  1443. /*++
  1444. Name :
  1445. CAddr::ValidateCleanEmailName
  1446. Description:
  1447. Determines whether a specified email name is valid.
  1448. The input email name must be clean, i.e. no comments,
  1449. spaces, routing specifiers, etc.
  1450. Arguments:
  1451. lpszCleanEmailName - ANSI email name string to validate
  1452. lpszDomainOffset - Pointer to '@' sign in email string
  1453. Returns:
  1454. TRUE if valid, FALSE if not
  1455. --*/
  1456. BOOL CAddr::ValidateCleanEmailName( char *lpszCleanEmailName,
  1457. char *lpszDomainOffset)
  1458. {
  1459. DWORD dwLength, dwDomainLength;
  1460. TraceFunctEnterEx(0, "CAddr::ValidateCleanEmailName");
  1461. _ASSERT(lpszCleanEmailName);
  1462. if (!lpszCleanEmailName ||
  1463. !pValidateStringPtr(lpszCleanEmailName, MAX_INTERNET_NAME+1))
  1464. {
  1465. SetLastError(ERROR_INVALID_DATA);
  1466. goto LeaveWithError;
  1467. }
  1468. if (lpszDomainOffset)
  1469. {
  1470. _ASSERT(*lpszDomainOffset == '@');
  1471. _ASSERT(lpszDomainOffset > lpszCleanEmailName);
  1472. dwLength = (DWORD)(lpszDomainOffset - lpszCleanEmailName);
  1473. dwDomainLength = lstrlen(lpszCleanEmailName) - dwLength - 1;
  1474. *lpszDomainOffset++ = '\0';
  1475. }
  1476. else
  1477. dwLength = lstrlen(lpszCleanEmailName);
  1478. StateTrace(0, " Local-part: %s", lpszCleanEmailName);
  1479. if (!pValidateLocalPartOrDomain(lpszCleanEmailName,
  1480. dwLength,
  1481. TRUE))
  1482. {
  1483. ErrorTrace(0, "Invalid local part");
  1484. goto LeaveWithError;
  1485. }
  1486. if (lpszDomainOffset)
  1487. {
  1488. StateTrace(0, " Domain: %s", lpszDomainOffset);
  1489. if (!pValidateLocalPartOrDomain(lpszDomainOffset,
  1490. dwDomainLength, FALSE))
  1491. {
  1492. ErrorTrace(0, "Invalid domain");
  1493. goto LeaveWithError;
  1494. }
  1495. }
  1496. // Restore the string ...
  1497. if (lpszDomainOffset)
  1498. *--lpszDomainOffset = '@';
  1499. TraceFunctLeave();
  1500. return(TRUE);
  1501. LeaveWithError:
  1502. if (lpszDomainOffset)
  1503. if (*--lpszDomainOffset == '\0')
  1504. *lpszDomainOffset = '@';
  1505. ErrorTrace(0, "CAddr::ValidateCleanEmailName failed");
  1506. TraceFunctLeave();
  1507. return(FALSE);
  1508. }
  1509. /*++
  1510. Name :
  1511. CAddr::ValidateEmailName
  1512. Description:
  1513. Determines whether a specified email name is valid
  1514. Arguments:
  1515. lpszEmailName - ANSI email name string to validate
  1516. fDomainOptional - TRUE if domain is optional, FLASE forces
  1517. a domain to be included.
  1518. Returns:
  1519. TRUE if valid, FALSE if not
  1520. --*/
  1521. BOOL CAddr::ValidateEmailName( char *lpszEmailName,
  1522. BOOL fDomainOptional)
  1523. {
  1524. char szSource[MAX_INTERNET_NAME+1];
  1525. char *lpDomainOffset;
  1526. DWORD dwLength;
  1527. szSource[0] = '\0';
  1528. if (!ExtractCleanEmailName( szSource,
  1529. &lpDomainOffset,
  1530. &dwLength,
  1531. lpszEmailName))
  1532. return(FALSE);
  1533. if (!fDomainOptional && !lpDomainOffset)
  1534. return(FALSE);
  1535. return(ValidateCleanEmailName(szSource, lpDomainOffset));
  1536. }
  1537. /*++
  1538. Name :
  1539. CAddr::FindStartOfDomain
  1540. Description:
  1541. Finds the start of the domain part from a CLEAN email
  1542. address. The clean address may not contain
  1543. any comments, routing specifications, UUCP addresses,
  1544. and the such. No validation is done for the "clean
  1545. address".
  1546. Arguments:
  1547. lpszCleanEmail - ANSI CLEAN email name string whose local
  1548. part to extract
  1549. Returns:
  1550. A pointer to the '@' sign separating the local part and
  1551. the domain. NULL if the '@' sign is not found. Note that
  1552. '@' signs enclosed in quotes or in a proper quoted pair
  1553. are skipped.
  1554. --*/
  1555. CHAR * CAddr::FindStartOfDomain(CHAR *lpszCleanEmail)
  1556. {
  1557. BOOL fNotFound;
  1558. return(pFindNextUnquotedOccurrence(lpszCleanEmail,
  1559. '@', &fNotFound, 0));
  1560. }
  1561. //---[ CAddr::GetRFC822AddressCount ]------------------------------------------
  1562. //
  1563. //
  1564. // Description:
  1565. // Counts the number of addresses in a RFC822 list of addresses.
  1566. // Parameters:
  1567. // IN szAddressList List of addresses to count
  1568. // Returns:
  1569. // # of recipients in list
  1570. // History:
  1571. // 2/17/99 - MikeSwa Created
  1572. //
  1573. //-----------------------------------------------------------------------------
  1574. DWORD CAddr::GetRFC822AddressCount(char *szAddressList)
  1575. {
  1576. DWORD cRecips = 0;
  1577. LPSTR szCommentStart = szAddressList;
  1578. LPSTR szCommentEnd = szAddressList;
  1579. LPSTR szSearchStart = szAddressList;
  1580. LPSTR szSearchDelimiter = NULL; //ptr to delimiter
  1581. LPSTR szLastDelimiter = NULL; //ptr to last delimiter found
  1582. BOOL fSeenAlphaNum = FALSE;
  1583. BOOL fInQuote = TRUE;
  1584. DWORD dwPairs = 0;
  1585. CHAR chSaved = '\0';
  1586. CHAR *pchChanged = NULL;
  1587. BOOL fDelimiterNotFound = TRUE;
  1588. CHAR rgchDelimiters[] = {',', ';', '\0'};
  1589. //We look for RFC822 delimiters (, or ;) that are not in comments (which
  1590. //are delimited by ()'s or which are in quotes.
  1591. //No string... no recips
  1592. if (!szAddressList)
  1593. goto Exit;
  1594. //If we have any string... we start out with 1 recip
  1595. cRecips++;
  1596. // First, we strip the comments
  1597. // We call the function above to find matching parenthesis pairs
  1598. do
  1599. {
  1600. //Set start of search to start of string being scanned for ()'s
  1601. szSearchStart = szCommentStart;
  1602. if (!pMatchParentheses(&szCommentStart, &szCommentEnd, &dwPairs))
  1603. {
  1604. // Failed!
  1605. cRecips = 0;
  1606. goto Exit;
  1607. }
  1608. if (dwPairs) //we have comments
  1609. {
  1610. //We found some comments
  1611. _ASSERT(*szCommentStart == '(');
  1612. _ASSERT(*szCommentEnd == ')');
  1613. //If the first character of our search was a '('... then we
  1614. //should not bother searching for delimiters
  1615. if (szSearchStart == szCommentStart)
  1616. {
  1617. szCommentStart = szCommentEnd + 1;
  1618. continue;
  1619. }
  1620. //Set the end of our search for delimiters & set to NULL character
  1621. pchChanged = szCommentStart;
  1622. chSaved = *szCommentStart;
  1623. *szCommentStart = '\0';
  1624. // Reset the pointers, and match again ...
  1625. szCommentEnd++;
  1626. szCommentStart = szCommentEnd;
  1627. }
  1628. else
  1629. {
  1630. //We found no further comments... there is no need to save a character
  1631. chSaved = '\0';
  1632. pchChanged = NULL;
  1633. }
  1634. //Now we will search for unqoted delimiters in this uncommented section
  1635. //Iterate over all the delimiters we have
  1636. for (CHAR *pchDelimiter = rgchDelimiters; *pchDelimiter; pchDelimiter++)
  1637. {
  1638. szSearchDelimiter = szSearchStart;
  1639. do
  1640. {
  1641. szSearchDelimiter = pFindNextUnquotedOccurrence(
  1642. szSearchDelimiter, *pchDelimiter,
  1643. &fDelimiterNotFound, 0);
  1644. if (!fDelimiterNotFound)
  1645. {
  1646. _ASSERT(*pchDelimiter == *szSearchDelimiter);
  1647. cRecips++;
  1648. if (szSearchDelimiter && (szSearchDelimiter > szLastDelimiter))
  1649. szLastDelimiter = szSearchDelimiter;
  1650. }
  1651. else
  1652. {
  1653. //We know we won't find anymore in this section
  1654. break;
  1655. }
  1656. //Keep on looping while we are still finding delimiters and we are not
  1657. //at the end of the string
  1658. } while (!fDelimiterNotFound &&
  1659. szSearchDelimiter && *szSearchDelimiter &&
  1660. *(++szSearchDelimiter));
  1661. }
  1662. //Restore changed character
  1663. if (pchChanged && ('\0' != chSaved))
  1664. *pchChanged = chSaved;
  1665. } while (dwPairs);
  1666. //Make sure the last delimiter was not at the end of the buffer
  1667. if (szLastDelimiter && cRecips && *szLastDelimiter)
  1668. {
  1669. while (*(++szLastDelimiter))
  1670. {
  1671. //if it is not a space... count it as a recipient
  1672. if (!isspace((UCHAR)*szLastDelimiter))
  1673. goto Exit;
  1674. }
  1675. //Only whitespace after last delimiter... we have counted 1 too many recips
  1676. cRecips--;
  1677. }
  1678. Exit:
  1679. return cRecips;
  1680. }
  1681. //---[ IsRecipientInRFC822AddressList ]----------------------------------------
  1682. //
  1683. //
  1684. // Description:
  1685. // Determines if a given recipient is in the given RFC822 formatted
  1686. // recipient list.
  1687. // Parameters:
  1688. // IN szAddressList Address list to check in
  1689. // IN szRecip Recipient Address to check for
  1690. // Returns:
  1691. // TRUE if there was a match
  1692. // FALSE if there was no match
  1693. // History:
  1694. // 2/17/99 - MikeSwa Created
  1695. //
  1696. //-----------------------------------------------------------------------------
  1697. BOOL CAddr::IsRecipientInRFC822AddressList(char *szAddressList, char *szRecip)
  1698. {
  1699. LPSTR szRecipEnd = NULL;
  1700. CHAR chSaved = '\0';
  1701. LPSTR szCurrentAddress = szAddressList;
  1702. BOOL fFound = FALSE;
  1703. DWORD cbAddress;
  1704. if (!szAddressList || !szRecip)
  1705. goto Exit;
  1706. //Convert everything to lower case so we match correctly
  1707. szCurrentAddress = szAddressList;
  1708. do
  1709. {
  1710. *szCurrentAddress = (CHAR) tolower(*szCurrentAddress);
  1711. } while(*(++szCurrentAddress));
  1712. szCurrentAddress = szRecip;
  1713. do
  1714. {
  1715. *szCurrentAddress = (CHAR) tolower(*szCurrentAddress);
  1716. } while(*(++szCurrentAddress));
  1717. //skip past white space in recipient
  1718. while (*szRecip && isspace((UCHAR)*szRecip))
  1719. szRecip++;
  1720. //Find and skip past extranious trailing whitespace
  1721. cbAddress = strlen(szRecip);
  1722. szRecipEnd = szRecip + cbAddress/sizeof(CHAR);
  1723. szRecipEnd--;
  1724. while (isspace((UCHAR)*szRecipEnd))
  1725. {
  1726. cbAddress--;
  1727. szRecipEnd--;
  1728. }
  1729. //Make szRecipEnd point to last space
  1730. szRecipEnd++;
  1731. //Null terminate before trailing whitespace
  1732. chSaved = *szRecipEnd;
  1733. *szRecipEnd = '\0';
  1734. //Search for addresss as substring, and see if it looks like a lone address
  1735. for (szCurrentAddress = strstr(szAddressList, szRecip);
  1736. szCurrentAddress && !fFound;
  1737. szCurrentAddress = strstr(++szCurrentAddress, szRecip))
  1738. {
  1739. //look for surrounding characters to not match partial addresses
  1740. //We don't want "user" to match "user1" or "user@foo" or "foo@user"
  1741. if ((szCurrentAddress != szAddressList))
  1742. {
  1743. if (!pIsSpecialOrSpace(*(szCurrentAddress-1)) ||
  1744. ('@' == *(szCurrentAddress-1)))
  1745. continue;
  1746. }
  1747. if (szCurrentAddress[cbAddress/sizeof(CHAR)])
  1748. {
  1749. if (!pIsSpecialOrSpace(szCurrentAddress[cbAddress/sizeof(CHAR)]) ||
  1750. ('@' == szCurrentAddress[cbAddress/sizeof(CHAR)]))
  1751. continue;
  1752. }
  1753. //The address looks like a match
  1754. fFound = TRUE;
  1755. break;
  1756. }
  1757. Exit:
  1758. //Restore saved space
  1759. if (szRecipEnd && chSaved)
  1760. *szRecipEnd = chSaved;
  1761. return fFound;
  1762. }