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.

525 lines
16 KiB

  1. // --------------------------------------------------------------------------------
  2. // Addparse.cpp
  3. // Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
  4. // --------------------------------------------------------------------------------
  5. #include "pch.hxx"
  6. #include "addparse.h"
  7. #include "bytebuff.h"
  8. #include "shlwapi.h"
  9. #include <demand.h> // must be last!
  10. // --------------------------------------------------------------------------------
  11. // Constants
  12. // --------------------------------------------------------------------------------
  13. static const WCHAR c_wszAddressDelims[] = L"\",<(;";
  14. static const WCHAR c_wszRfc822MustQuote[] = L"()<>,;:\\\"[] ";
  15. static const WCHAR c_wszSpace[] = L" ";
  16. static const WCHAR c_wszEmpty[] = L"";
  17. // --------------------------------------------------------------------------------
  18. // CAddressParser::Init
  19. // --------------------------------------------------------------------------------
  20. void CAddressParser::Init(LPCWSTR pszAddress, ULONG cchAddress)
  21. {
  22. // Give the byte buffers some static space to reduce memory allocations
  23. m_cFriendly.Init(m_rgbStatic1, sizeof(m_rgbStatic1));
  24. m_cEmail.Init(m_rgbStatic2, sizeof(m_rgbStatic2));
  25. // Init the string parser
  26. m_cString.Init(pszAddress, cchAddress, PSF_NOTRAILWS | PSF_NOFRONTWS);
  27. }
  28. // --------------------------------------------------------------------------------
  29. // CAddressParser::Next
  30. // --------------------------------------------------------------------------------
  31. HRESULT CAddressParser::Next(void)
  32. {
  33. // Locals
  34. HRESULT hr=S_OK;
  35. WCHAR chToken;
  36. // Reset current Friendlay and Email Buffers
  37. m_cFriendly.SetSize(0);
  38. m_cEmail.SetSize(0);
  39. // Outer Loop
  40. while(1)
  41. {
  42. // Skip White Space
  43. chToken = m_cString.ChSkipWhite();
  44. // Done...
  45. if (L'\0' == chToken)
  46. break;
  47. // Parse until we hit a token
  48. chToken = m_cString.ChParse(c_wszAddressDelims, PSF_ESCAPED | PSF_NOTRAILWS);
  49. // No data was read
  50. if (0 == m_cString.CbValue())
  51. {
  52. // End of string hit
  53. if (L'\0' == chToken)
  54. break;
  55. // Otherwise, comma or semicolon and we have data
  56. else if ((L',' == chToken) || (L';' == chToken))
  57. {
  58. // If we have data, were done
  59. if (m_cFriendly.CbData() || m_cEmail.CbData())
  60. break;
  61. // Otherwise, continue
  62. else
  63. continue;
  64. }
  65. }
  66. // Email Addresses are never quoted
  67. if (L'\"' == chToken)
  68. {
  69. // AppendUnsure
  70. CHECKHR(hr = _HrAppendUnsure(L'\0', L'\0'));
  71. // Parse parameter value
  72. chToken = m_cString.ChParse(L'\"', L'\"', PSF_ESCAPED);
  73. // Raid-47099: We need to parse: "CN=first last/O=xyz> org/C=US"@xyz.innosoft.com
  74. CHECKHR(hr = _HrQuotedEmail(&chToken));
  75. // Returns S_OK if it was processed
  76. if (S_FALSE == hr)
  77. {
  78. // Write to Friendly
  79. CHECKHR(hr = _HrAppendFriendly());
  80. }
  81. }
  82. // Otherwise, < always flushes to email
  83. else if (L'<' == chToken)
  84. {
  85. // AppendUnsure
  86. CHECKHR(hr = _HrAppendFriendly());
  87. // Parse parameter value
  88. chToken = m_cString.ChParse(L">", 0);
  89. // Didn't find the end bracket
  90. if (L'>' == chToken)
  91. {
  92. // Write Friendly Name
  93. CHECKHR(hr = m_cEmail.Append((LPBYTE)m_cString.PszValue(), m_cString.CbValue()));
  94. }
  95. // Otherwise...
  96. else
  97. {
  98. // Should have an Email Address
  99. CHECKHR(hr = _HrAppendUnsure(L'<', L'>'));
  100. }
  101. }
  102. // Otherwise
  103. else
  104. {
  105. // AppendUnsure
  106. CHECKHR(hr = _HrAppendUnsure(L'\0', L'\0'));
  107. // If right paren, search to end
  108. if (L'(' == chToken)
  109. {
  110. // Parse to ending paren...
  111. chToken = m_cString.ChParse(L'(', L')', PSF_ESCAPED);
  112. // AppendUnsure
  113. CHECKHR(hr = _HrAppendUnsure(L'(', L')'));
  114. }
  115. }
  116. // Done
  117. if ((L',' == chToken) || (L';' == chToken))
  118. break;
  119. }
  120. // If friendly name has data, append a null, check email and return
  121. if (m_cFriendly.CbData())
  122. {
  123. // Append a Null
  124. m_cFriendly.Append((LPBYTE)c_wszEmpty, sizeof(WCHAR));
  125. // If Email is not empty, append a null
  126. if (m_cEmail.CbData())
  127. m_cEmail.Append((LPBYTE)c_wszEmpty, sizeof(WCHAR));
  128. }
  129. // Otherwise, if email has data, append null and return
  130. else if (m_cEmail.CbData())
  131. {
  132. // If Email is not empty, append a null
  133. m_cEmail.Append((LPBYTE)c_wszEmpty, sizeof(WCHAR));
  134. }
  135. // Are we really done ?
  136. else if (L'\0' == chToken)
  137. {
  138. hr = TrapError(MIME_E_NO_DATA);
  139. goto exit;
  140. }
  141. // Skip Commas and semicolons
  142. if (L',' == chToken)
  143. m_cString.ChSkip(L",");
  144. else if (L';' == chToken)
  145. m_cString.ChSkip(L";");
  146. exit:
  147. // Done
  148. return hr;
  149. }
  150. // --------------------------------------------------------------------------------
  151. // CAddressParser::_HrQuotedEmail
  152. // --------------------------------------------------------------------------------
  153. HRESULT CAddressParser::_HrQuotedEmail(WCHAR *pchToken)
  154. {
  155. // Locals
  156. HRESULT hr=S_OK;
  157. ULONG cchT=0;
  158. WCHAR chDelim;
  159. BOOL fSeenAt=FALSE;
  160. WCHAR ch;
  161. WCHAR szToken[2];
  162. // Invalid Arg
  163. Assert(pchToken);
  164. // We should have some data
  165. if (0 == m_cString.CbValue())
  166. return S_OK;
  167. // Get the character
  168. ch = m_cString.ChPeekNext(0);
  169. // Check for DBCS
  170. if (L'@' != ch)
  171. return S_FALSE;
  172. // Look ahead and check for: "CN=first last/O=xyz> org/C=US"@xyz.innosoft.com
  173. while(1)
  174. {
  175. // Get the character
  176. ch = m_cString.ChPeekNext(cchT);
  177. // Breaking Character
  178. if (L'\0' == ch || L' ' == ch || L',' == ch || L';' == ch || L'<' == ch || L'>' == ch || L'(' == ch || L')' == ch)
  179. break;
  180. // At Sign?
  181. if (L'@' == ch)
  182. fSeenAt = TRUE;
  183. // Increment
  184. cchT++;
  185. }
  186. // No At Sign
  187. if (0 == cchT || FALSE == fSeenAt)
  188. return S_FALSE;
  189. // Append Email Address
  190. CHECKHR(hr = m_cEmail.Append((LPBYTE)c_wszDoubleQuote, 2));
  191. // Append Email Address
  192. CHECKHR(hr = m_cEmail.Append((LPBYTE)m_cString.PszValue(), m_cString.CbValue()));
  193. // Append Email Address
  194. CHECKHR(hr = m_cEmail.Append((LPBYTE)c_wszDoubleQuote, 2));
  195. // Setup szToken
  196. szToken[0] = (L'\0' == ch) ? L' ' : ch;
  197. szToken[1] = L'\0';
  198. // Seek to next space
  199. ch = m_cString.ChParse(szToken, PSF_NOCOMMENTS);
  200. Assert(szToken[0] == ch || L'\0' == ch);
  201. // If there is data
  202. if (m_cString.CbValue() > 0)
  203. {
  204. // Append the Email Address
  205. CHECKHR(hr = m_cEmail.Append((LPBYTE)m_cString.PszValue(), m_cString.CbValue()));
  206. }
  207. // End Token
  208. *pchToken = szToken[0];
  209. exit:
  210. // Done
  211. return hr;
  212. }
  213. // --------------------------------------------------------------------------------
  214. // CAddressParser::_HrIsEmailAddress
  215. // --------------------------------------------------------------------------------
  216. HRESULT CAddressParser::_HrIsEmailAddress(WCHAR chStart, WCHAR chEnd, BOOL *pfIsEmail)
  217. {
  218. // Locals
  219. HRESULT hr=S_OK;
  220. WCHAR chToken;
  221. CStringParserW cString;
  222. // Invalid Arg
  223. Assert(pfIsEmail);
  224. // Init
  225. *pfIsEmail = FALSE;
  226. // Init
  227. cString.Init(m_cString.PszValue(), m_cString.CchValue(), PSF_NOCOMMENTS | PSF_ESCAPED | PSF_NOTRAILWS | PSF_NOFRONTWS);
  228. // Parse to the end to remove comments
  229. if (L'\0' != cString.ChParse(c_wszEmpty) || 0 == cString.CbValue())
  230. return S_OK;
  231. // Parse String
  232. if (NULL == StrChrW(cString.PszValue(), L' '))
  233. {
  234. // If in brackets, then its an email address for sure
  235. if (L'<' == chStart && L'>' == chEnd)
  236. {
  237. // Is Email
  238. *pfIsEmail = TRUE;
  239. // Write Friendly Name
  240. CHECKHR(hr = m_cEmail.Append((LPBYTE)cString.PszValue(), cString.CbValue()));
  241. }
  242. // Look for the last '@' sign and see if their are escapeable chars before the at sign
  243. else
  244. {
  245. // Locals
  246. LPWSTR pszT=(LPWSTR)cString.PszValue();
  247. LPWSTR pszLastAt=NULL;
  248. ULONG cQuoteBeforeAt=0;
  249. ULONG cQuoteAfterAt=0;
  250. // Raid - 62104: Outlook98 doesn't handle Lotus Domino RFC822 Address Construction
  251. while(*pszT)
  252. {
  253. // Check for '@' sign
  254. if (L'@' == *pszT)
  255. {
  256. // If we already saw an at sign, move cQuoteAfterAt to cQuoteBeforeAt
  257. if (pszLastAt)
  258. {
  259. cQuoteBeforeAt += cQuoteAfterAt;
  260. cQuoteAfterAt = 0;
  261. }
  262. // Save Last At
  263. pszLastAt = pszT;
  264. }
  265. // See if *pszT is in c_szRfc822MustQuote
  266. else if (NULL != StrChrW(c_wszRfc822MustQuote, *pszT))
  267. {
  268. // If we've seen an at sign, track quote after at
  269. if (pszLastAt)
  270. cQuoteAfterAt++;
  271. else
  272. cQuoteBeforeAt++;
  273. }
  274. // Increment
  275. pszT++;
  276. }
  277. // Only if we saw an '@' sign
  278. if (NULL != pszLastAt)
  279. {
  280. // Is Email
  281. *pfIsEmail = TRUE;
  282. // If there were not chars that need quoting...
  283. if (0 == cQuoteBeforeAt)
  284. {
  285. // Write Friendly Name
  286. CHECKHR(hr = m_cEmail.Append((LPBYTE)cString.PszValue(), cString.CbValue()));
  287. }
  288. // "Mailroute_TstSCC1[BOFATEST.MRTSTSCC]%SSW%EMAILDOM%BETA"@bankamerica.com
  289. else
  290. {
  291. // Locals
  292. ULONG cbComplete=cString.CbValue();
  293. ULONG cbFirstPart=(ULONG)(pszLastAt - cString.PszValue());
  294. ULONG cbLastPart=cbComplete - cbFirstPart;
  295. // Append Doulbe Quote
  296. CHECKHR(hr = m_cEmail.Append((LPBYTE)c_wszDoubleQuote, 2));
  297. // Append Firt part before last at
  298. CHECKHR(hr = m_cEmail.Append((LPBYTE)cString.PszValue(), cbFirstPart));
  299. // Append Email Address
  300. CHECKHR(hr = m_cEmail.Append((LPBYTE)c_wszDoubleQuote, 2));
  301. // Append Firt part before last at
  302. CHECKHR(hr = m_cEmail.Append((LPBYTE)pszLastAt, cbLastPart));
  303. }
  304. }
  305. }
  306. }
  307. exit:
  308. // Done
  309. return hr;
  310. }
  311. // --------------------------------------------------------------------------------
  312. // CAddressParser::_HrAppendUnsure
  313. // --------------------------------------------------------------------------------
  314. HRESULT CAddressParser::_HrAppendUnsure(WCHAR chStart, WCHAR chEnd)
  315. {
  316. // Locals
  317. HRESULT hr=S_OK;
  318. BOOL fIsEmail=FALSE;
  319. // We have data
  320. if (0 == m_cString.CbValue())
  321. goto exit;
  322. // Email is not set yet ?
  323. if (m_cEmail.CbData() == 0)
  324. {
  325. // Is current parsed string an address ?
  326. CHECKHR(hr = _HrIsEmailAddress(chStart, chEnd, &fIsEmail));
  327. }
  328. // Not an Eamil Address
  329. if (FALSE == fIsEmail && m_cString.CbValue() > 0)
  330. {
  331. // Append a space
  332. if (m_cFriendly.CbData() > 0)
  333. {
  334. // Add a space
  335. CHECKHR(hr = m_cFriendly.Append((LPBYTE)c_wszSpace, sizeof(WCHAR)));
  336. // Start Character
  337. if (chStart)
  338. {
  339. // Append Start Delimiter
  340. CHECKHR(hr = m_cFriendly.Append((LPBYTE)&chStart, sizeof(WCHAR)));
  341. }
  342. }
  343. // Otherwise, don't write ending terminator
  344. else
  345. chEnd = L'\0';
  346. // Write Friendly Name
  347. CHECKHR(hr = m_cFriendly.Append((LPBYTE)m_cString.PszValue(), m_cString.CbValue()));
  348. // Start Character
  349. if (chEnd)
  350. {
  351. // Append Start Delimiter
  352. CHECKHR(hr = m_cFriendly.Append((LPBYTE)&chEnd, sizeof(WCHAR)));
  353. }
  354. }
  355. exit:
  356. // Done
  357. return hr;
  358. }
  359. // --------------------------------------------------------------------------------
  360. // CAddressParser::_HrAppendFriendly
  361. // --------------------------------------------------------------------------------
  362. HRESULT CAddressParser::_HrAppendFriendly(void)
  363. {
  364. // Locals
  365. HRESULT hr=S_OK;
  366. // We should have some data
  367. if (0 == m_cString.CbValue())
  368. return S_OK;
  369. // Append a space
  370. if (m_cFriendly.CbData() > 0)
  371. {
  372. // Add a space
  373. CHECKHR(hr = m_cFriendly.Append((LPBYTE)c_wszSpace, sizeof(WCHAR)));
  374. }
  375. // Write Friendly Name
  376. CHECKHR(hr = m_cFriendly.Append((LPBYTE)m_cString.PszValue(), m_cString.CbValue()));
  377. exit:
  378. // Done
  379. return hr;
  380. }
  381. // --------------------------------------------------------------------------------
  382. // CAddressParser::PszFriendly
  383. // --------------------------------------------------------------------------------
  384. LPCWSTR CAddressParser::PszFriendly(void)
  385. {
  386. // We should have one or the other
  387. if (0 == m_cFriendly.CbData() && 0 == m_cEmail.CbData())
  388. {
  389. AssertSz(FALSE, "This is a bug in CAddressParser, should never have an empty friendly and email.");
  390. return c_wszEmpty;
  391. }
  392. // Return It
  393. return (m_cFriendly.CbData() ? (LPCWSTR)m_cFriendly.PbData() : PszEmail());
  394. }
  395. // --------------------------------------------------------------------------------
  396. // CAddressParser::CchFriendly
  397. // --------------------------------------------------------------------------------
  398. ULONG CAddressParser::CchFriendly(void)
  399. {
  400. // We should have one or the other
  401. if (0 == m_cFriendly.CbData() && 0 == m_cEmail.CbData())
  402. {
  403. AssertSz(FALSE, "This is a bug in CAddressParser, should never have an empty friendly and email.");
  404. return 0;
  405. }
  406. // Return It
  407. return (m_cFriendly.CbData() ? (m_cFriendly.CbData() - sizeof(WCHAR)) / sizeof(WCHAR) : CchEmail());
  408. }
  409. // --------------------------------------------------------------------------------
  410. // CAddressParser::PszEmail
  411. // --------------------------------------------------------------------------------
  412. LPCWSTR CAddressParser::PszEmail(void)
  413. {
  414. // We should have one or the other
  415. if (0 == m_cFriendly.CbData() && 0 == m_cEmail.CbData())
  416. {
  417. AssertSz(FALSE, "This is a bug in CAddressParser, should never have an empty friendly and email.");
  418. return c_wszEmpty;
  419. }
  420. // Return It
  421. return (m_cEmail.CbData() ? (LPCWSTR)m_cEmail.PbData() : PszFriendly());
  422. }
  423. // --------------------------------------------------------------------------------
  424. // CAddressParser::CchEmail
  425. // --------------------------------------------------------------------------------
  426. ULONG CAddressParser::CchEmail(void)
  427. {
  428. // We should have one or the other
  429. if (0 == m_cFriendly.CbData() && 0 == m_cEmail.CbData())
  430. {
  431. AssertSz(FALSE, "This is a bug in CAddressParser, should never have an empty friendly and email.");
  432. return 0;
  433. }
  434. // Return It
  435. return (m_cEmail.CbData() ? (m_cEmail.CbData() - sizeof(WCHAR)) / sizeof(WCHAR) : CchFriendly());
  436. }