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.

1102 lines
32 KiB

  1. // --------------------------------------------------------------------------------
  2. // rfc1522.cpp
  3. // Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
  4. // Steven J. Bailey
  5. // --------------------------------------------------------------------------------
  6. #include "pch.hxx"
  7. #include "internat.h"
  8. #include "dllmain.h"
  9. #include "inetconv.h"
  10. #include "strconst.h"
  11. #include "variantx.h"
  12. #include "mimeapi.h"
  13. #include "demand.h"
  14. #include "shlwapi.h"
  15. // --------------------------------------------------------------------------------
  16. // ISQPESCAPE(_ch)
  17. // --------------------------------------------------------------------------------
  18. #define ISQPESCAPE(_ch) \
  19. (IS_EXTENDED(_ch) || _ch == '?' || _ch == '=' || \
  20. _ch == '_' || _ch == '"' || \
  21. _ch == '<' || _ch == '>' || \
  22. _ch == '(' || _ch == ')' || \
  23. _ch == '[' || _ch == ']' || \
  24. _ch == ',')
  25. // --------------------------------------------------------------------------------
  26. // RFC1522OUT
  27. // --------------------------------------------------------------------------------
  28. typedef struct tagRFC1522OUT {
  29. BOOL fWrite;
  30. CHAR szBuffer[512];
  31. ULONG iBuffer;
  32. LPSTREAM pstm;
  33. } RFC1522OUT, *LPRFC1522OUT;
  34. // --------------------------------------------------------------------------------
  35. // HrRfc1522WriteDone
  36. // --------------------------------------------------------------------------------
  37. HRESULT HrRfc1522WriteDone(LPRFC1522OUT pOut, LPSTR *ppszRfc1522)
  38. {
  39. // We better be writing
  40. Assert(pOut->fWrite && ppszRfc1522);
  41. // Init
  42. *ppszRfc1522 = NULL;
  43. // If we haven't created the stream yet, just use the buffer...
  44. if (NULL == pOut->pstm)
  45. {
  46. // No data
  47. if (0 == pOut->iBuffer)
  48. return S_OK;
  49. // Allocate
  50. *ppszRfc1522 = PszAllocA(pOut->iBuffer + 1);
  51. if (NULL == *ppszRfc1522)
  52. return TrapError(E_OUTOFMEMORY);
  53. // Copy data
  54. CopyMemory(*ppszRfc1522, pOut->szBuffer, pOut->iBuffer);
  55. // Null term
  56. *((*ppszRfc1522) + pOut->iBuffer) = '\0';
  57. }
  58. // Otherwise, do stream
  59. else
  60. {
  61. // Commit final data to stream...
  62. if (0 != pOut->iBuffer)
  63. {
  64. if (FAILED(pOut->pstm->Write(pOut->szBuffer, pOut->iBuffer, NULL)))
  65. return TrapError(E_OUTOFMEMORY);
  66. }
  67. // Commit the stream
  68. if (FAILED(pOut->pstm->Commit(STGC_DEFAULT)))
  69. return TrapError(E_OUTOFMEMORY);
  70. // Convert the stream to an ANSI string
  71. *ppszRfc1522 = PszFromANSIStreamA(pOut->pstm);
  72. Assert(NULL != *ppszRfc1522);
  73. // Release the stream
  74. SafeRelease(pOut->pstm);
  75. }
  76. // Done
  77. return S_OK;
  78. }
  79. // --------------------------------------------------------------------------------
  80. // HrRfc1522Write
  81. // --------------------------------------------------------------------------------
  82. HRESULT HrRfc1522Write(LPRFC1522OUT pOut, UCHAR ch)
  83. {
  84. // If not saving data..
  85. if (!pOut->fWrite)
  86. return S_OK;
  87. // If buffer + 1 is full, dump to stream
  88. if (pOut->iBuffer + 1 > sizeof(pOut->szBuffer))
  89. {
  90. // Do I have a stream yet...
  91. if (NULL == pOut->pstm)
  92. {
  93. // Create stream
  94. if (FAILED(MimeOleCreateVirtualStream(&pOut->pstm)))
  95. return TrapError(E_OUTOFMEMORY);
  96. }
  97. // Write buffer to the stream
  98. if (FAILED(pOut->pstm->Write(pOut->szBuffer, pOut->iBuffer, NULL)))
  99. return TrapError(E_OUTOFMEMORY);
  100. // Reset buffers
  101. pOut->iBuffer = 0;
  102. }
  103. // Add character to the buffer
  104. pOut->szBuffer[pOut->iBuffer++] = ch;
  105. // Done
  106. return S_OK;
  107. }
  108. // --------------------------------------------------------------------------------
  109. // HrRfc1522WriteStr
  110. // --------------------------------------------------------------------------------
  111. HRESULT HrRfc1522WriteStr(LPRFC1522OUT pOut, LPSTR psz, LONG cb)
  112. {
  113. HRESULT hr;
  114. while(cb)
  115. {
  116. hr = HrRfc1522Write(pOut, *psz);
  117. if (FAILED(hr))
  118. return hr;
  119. psz++;
  120. cb--;
  121. }
  122. return S_OK;
  123. }
  124. inline BOOL IsRfc1522Token(UCHAR ch)
  125. {
  126. // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  127. static UINT abToken[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 000-031
  128. 1,1,1,1,1,1,1,1,0,0,1,1,0,1,0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0, // 032-063
  129. 0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,1, // 064-095
  130. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 096-127
  131. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 128-159
  132. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 160-191
  133. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // 192-223
  134. 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}; // 224-255
  135. Assert(sizeof(abToken)/sizeof(abToken[1])==256);
  136. Assert(abToken['(']==FALSE);
  137. Assert(abToken[')']==FALSE);
  138. Assert(abToken['<']==FALSE);
  139. Assert(abToken['>']==FALSE);
  140. Assert(abToken['@']==FALSE);
  141. Assert(abToken[',']==FALSE);
  142. Assert(abToken[';']==FALSE);
  143. Assert(abToken[':']==FALSE);
  144. Assert(abToken['/']==FALSE);
  145. Assert(abToken['[']==FALSE);
  146. Assert(abToken[']']==FALSE);
  147. Assert(abToken['?']==FALSE);
  148. Assert(abToken['.']==FALSE);
  149. Assert(abToken['=']==FALSE);
  150. return (BOOL) abToken[ch];
  151. }
  152. // --------------------------------------------------------------------------------
  153. // PszRfc1522Find
  154. //
  155. // Find an RFC1522 word. If the string pointer passed in is NULL, then NULL is
  156. // returned. If a word is not found, then a pointer to the terminatng NULL is
  157. // returned. If a word is found, then a pointer to the word is returned, and
  158. // an output parameter for whether the word was preceeded by non-blank characters
  159. // is set.
  160. //
  161. // If a word is not found, then the output parameter is undefined.
  162. //
  163. // The output parameter is optional - it may be NULL, in which case the value
  164. // will not be stored.
  165. //
  166. // --------------------------------------------------------------------------------
  167. LPSTR PszRfc1522Find(LPSTR psz,
  168. BOOL *pbNonBlankLeading)
  169. {
  170. LPSTR pszCharset;
  171. if (!psz || !*psz)
  172. {
  173. goto exit;
  174. }
  175. if (pbNonBlankLeading)
  176. {
  177. *pbNonBlankLeading = FALSE;
  178. }
  179. // Skip over any leading blanks.
  180. while (*psz && (*psz == ' ' || *psz == '\t' || *psz == '\r' || *psz == '\n'))
  181. {
  182. psz++;
  183. }
  184. if (*psz && (psz[0] != '=' || psz[1] != '?'))
  185. {
  186. again:
  187. // If we end up here (either through the if above or by a goto from below),
  188. // it means that we have some number of non-blank characters before the
  189. // RFC1522 word.
  190. if (pbNonBlankLeading)
  191. {
  192. *pbNonBlankLeading = TRUE;
  193. }
  194. }
  195. // Skip until we find an =?.
  196. while (*psz && (psz[0] != '=' || psz[1] != '?'))
  197. {
  198. psz++;
  199. }
  200. if (!*psz)
  201. {
  202. // End of the string.
  203. goto exit;
  204. }
  205. Assert(psz[0] == '=' && psz[1] == '?');
  206. // Parse out the charset.
  207. pszCharset = psz;
  208. psz += 2;
  209. while (IsRfc1522Token(*psz))
  210. {
  211. psz++;
  212. }
  213. if (!*psz)
  214. {
  215. // End of the string.
  216. goto exit;
  217. }
  218. if (*psz != '?')
  219. {
  220. // Malformed.
  221. goto again;
  222. }
  223. Assert(*psz == '?');
  224. // Parse out the encoding.
  225. psz++;
  226. while (IsRfc1522Token(*psz))
  227. {
  228. psz++;
  229. }
  230. if (!*psz)
  231. {
  232. // End of the string.
  233. goto exit;
  234. }
  235. if (*psz != '?')
  236. {
  237. // Malformed.
  238. goto again;
  239. }
  240. Assert(*psz == '?');
  241. // Parse out the data.
  242. psz++;
  243. while (*psz && (psz[0] != '?' || psz[1] != '='))
  244. {
  245. psz++;
  246. }
  247. if (!*psz)
  248. {
  249. // End of the string.
  250. goto exit;
  251. }
  252. Assert(psz[0] == '?' && psz[1] == '=');
  253. psz = pszCharset;
  254. exit:
  255. return psz;
  256. }
  257. // --------------------------------------------------------------------------------
  258. // PszRfc1522Decode - *(*ppsz) -> '?'
  259. // --------------------------------------------------------------------------------
  260. LPSTR PszRfc1522Decode(LPSTR psz, CHAR chEncoding, LPRFC1522OUT pOut)
  261. {
  262. // Check params
  263. Assert(pOut && psz && *psz == '?');
  264. Assert(chEncoding == 'B' || chEncoding == 'b' || chEncoding == 'Q' || chEncoding == 'q');
  265. // Step over '?'
  266. psz++;
  267. // Done...
  268. if ('\0' == *psz)
  269. return psz;
  270. // Q encoding
  271. if ('Q' == chEncoding || 'q' == chEncoding)
  272. {
  273. // Locals
  274. UCHAR uch;
  275. CHAR ch,
  276. ch1,
  277. ch2;
  278. LPSTR pszMark;
  279. // While we have characters and '?=' is not found...
  280. while(*psz && (psz[0] != '?' || psz[1] != '='))
  281. {
  282. // Get next char
  283. uch = *psz++;
  284. // Encoded hex pair i.e. '=1d'
  285. if (uch == '=')
  286. {
  287. // Mark position
  288. pszMark = psz;
  289. // Hex char 1 - If no null...
  290. if (*psz != '\0') ch1 = ChConvertFromHex (*psz++);
  291. else ch1 = (char)255;
  292. // Hex char 2 - if no null
  293. if (*psz != '\0') ch2 = ChConvertFromHex (*psz++);
  294. else ch2 = (char)255;
  295. //////////////////////////////////////////////////////////////////
  296. // raid x5-69640 - Incomplete QP encoded letter causes �
  297. // This is a sign-extension bug - we need to compare against
  298. // (char)255 instead of 255...
  299. //
  300. // If both are = 255, its an equal sign
  301. if (ch1 == (char)255 || ch2 == (char)255)
  302. //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  303. {
  304. if (FAILED(HrRfc1522Write(pOut, '=')))
  305. return NULL;
  306. psz = pszMark;
  307. }
  308. // Otherwise, build character
  309. else
  310. {
  311. ch = ( ch1 << 4 ) | ch2;
  312. if (FAILED(HrRfc1522Write(pOut, ch)))
  313. return NULL;
  314. }
  315. }
  316. // _ equals space...
  317. else if( uch == '_' )
  318. {
  319. if (FAILED(HrRfc1522Write(pOut, ' ')))
  320. return NULL;
  321. }
  322. // Otherwise, just append the character
  323. else
  324. {
  325. if (FAILED(HrRfc1522Write(pOut, uch)))
  326. return NULL;
  327. }
  328. }
  329. }
  330. // B encoding
  331. else
  332. {
  333. // Locals
  334. ULONG cbIn=0,
  335. cbPad=0;
  336. UCHAR ucIn[4],
  337. uch;
  338. // While we have characters and '?=' is not found...
  339. while(*psz && (psz[0] != '?' || psz[1] != '='))
  340. {
  341. // Gets 4 legal Base64 characters, ignores if illegal
  342. uch = *psz++;
  343. // Decode base64 character
  344. ucIn[cbIn] = DECODE64(uch) ;
  345. // Inc count
  346. if (ucIn[cbIn] < 64 || (uch == '=' && cbIn > 1))
  347. cbIn++;
  348. // Pad it
  349. if (uch == '=' && cbIn > 1)
  350. cbPad++;
  351. // Outputs when 4 legal Base64 characters are in the buffer
  352. if (cbIn == 4)
  353. {
  354. if (cbPad < 3)
  355. {
  356. if (FAILED(HrRfc1522Write(pOut, (ucIn[0] << 2) | (ucIn[1] >> 4))))
  357. return NULL;
  358. }
  359. if (cbPad < 2)
  360. {
  361. if (FAILED(HrRfc1522Write(pOut, (ucIn[1] << 4) | (ucIn[2] >> 2))))
  362. return NULL;
  363. }
  364. if (cbPad < 1)
  365. {
  366. if (FAILED(HrRfc1522Write(pOut, (ucIn[2] << 6) | (ucIn[3]))))
  367. return NULL;
  368. }
  369. cbIn = 0;
  370. }
  371. }
  372. }
  373. // Finish stepping
  374. if ('?' == *psz)
  375. psz++;
  376. if ('=' == *psz)
  377. psz++;
  378. // Done
  379. return psz;
  380. }
  381. // --------------------------------------------------------------------------------
  382. // PszRfc1522GetEncoding - *(*ppsz) -> '?'
  383. //
  384. // Returns NULL if '?X?' is not found... or B b Q q is not found
  385. // --------------------------------------------------------------------------------
  386. LPSTR PszRfc1522GetEncoding(LPSTR psz, CHAR *pchEncoding)
  387. {
  388. // Done
  389. if ('\0' == *psz)
  390. return NULL;
  391. // Should be pointing to '='
  392. if ('?' != *psz)
  393. return NULL;
  394. // Next character
  395. psz++;
  396. // Done
  397. if ('\0' == *psz)
  398. return NULL;
  399. // Save encoding...
  400. *pchEncoding = *psz;
  401. // Step over encoding character
  402. psz++;
  403. // Done
  404. if ('\0' == *psz)
  405. return NULL;
  406. // Should be pointing to '='
  407. if ('?' != *psz)
  408. return NULL;
  409. // Invalid encoding
  410. if ('B' != *pchEncoding && 'b' != *pchEncoding && 'Q' != *pchEncoding && 'q' != *pchEncoding)
  411. return NULL;
  412. // Continue
  413. return psz;
  414. }
  415. // --------------------------------------------------------------------------------
  416. // PszRfc1522GetCset - *(*ppsz) -> '='
  417. //
  418. // Returns NULL if '=?CHARSET?' is not found
  419. // --------------------------------------------------------------------------------
  420. LPSTR PszRfc1522GetCset(LPSTR psz, LPSTR pszCharset, ULONG cchmax)
  421. {
  422. // Locals
  423. LPSTR pszStart, pszEnd;
  424. // Done
  425. if ('\0' == *psz)
  426. return NULL;
  427. // Should be pointing to '='
  428. if ('=' != *psz)
  429. return NULL;
  430. // Next character
  431. psz++;
  432. // Done
  433. if ('\0' == *psz)
  434. return NULL;
  435. // Should be pointing to '?'
  436. if ('?' != *psz)
  437. return NULL;
  438. // Step over '?'
  439. psz++;
  440. // Done
  441. if ('\0' == *psz)
  442. return NULL;
  443. // Save Start
  444. pszStart = psz;
  445. // Seek to next '?'
  446. while(*psz && *psz != '?')
  447. psz++;
  448. // Done
  449. if ('\0' == *psz)
  450. return NULL;
  451. // Save end
  452. pszEnd = psz;
  453. Assert(*pszEnd == '?');
  454. // Charset name is too large...
  455. if ((ULONG)(pszEnd - pszStart) > cchmax)
  456. return NULL;
  457. // Copy charset
  458. *pszEnd = '\0';
  459. StrCpyNA(pszCharset, pszStart, cchmax);
  460. *pszEnd = '?';
  461. // Continue
  462. return psz;
  463. }
  464. // --------------------------------------------------------------------------------
  465. // HrRfc1522EncodeBase64
  466. // --------------------------------------------------------------------------------
  467. HRESULT HrRfc1522EncodeBase64(UCHAR *pb, ULONG cb, LPSTREAM pStream)
  468. {
  469. // Locals
  470. HRESULT hr=S_OK;
  471. BYTE rgbEncoded[1024];
  472. ULONG cbEncoded=0;
  473. ULONG i;
  474. UCHAR uch[3];
  475. // Encodes 3 characters at a time
  476. for (i=0; i<cb; i+=3)
  477. {
  478. // Setup Buffer
  479. uch[0] = pb[i];
  480. uch[1] = (i + 1 < cb) ? pb[i + 1] : '\0';
  481. uch[2] = (i + 2 < cb) ? pb[i + 2] : '\0';
  482. // Encode first tow
  483. rgbEncoded[cbEncoded++] = g_rgchEncodeBase64[(uch[0] >> 2) & 0x3F];
  484. rgbEncoded[cbEncoded++] = g_rgchEncodeBase64[(uch[0] << 4 | uch[1] >> 4) & 0x3F];
  485. // Encode Next
  486. if (i + 1 < cb)
  487. rgbEncoded[cbEncoded++] = g_rgchEncodeBase64[(uch[1] << 2 | uch[2] >> 6) & 0x3F];
  488. else
  489. rgbEncoded[cbEncoded++] = '=';
  490. // Encode Net
  491. if (i + 2 < cb)
  492. rgbEncoded[cbEncoded++] = g_rgchEncodeBase64[(uch[2]) & 0x3F];
  493. else
  494. rgbEncoded[cbEncoded++] = '=';
  495. }
  496. // Write rgbEncoded
  497. CHECKHR(hr = pStream->Write(rgbEncoded, cbEncoded, NULL));
  498. exit:
  499. // Done
  500. return hr;
  501. }
  502. // --------------------------------------------------------------------------------
  503. // HrRfc1522EncodeQP
  504. // --------------------------------------------------------------------------------
  505. HRESULT HrRfc1522EncodeQP(UCHAR *pb, ULONG cb, LPSTREAM pStream)
  506. {
  507. // Locals
  508. HRESULT hr=S_OK;
  509. BYTE rgbEncoded[1024];
  510. ULONG cbEncoded=0;
  511. ULONG i;
  512. // Loop through buffer
  513. for (i=0; i<cb; i++)
  514. {
  515. // Replace spaces with underscore - this is a more portable character
  516. if (pb[i] == ' ')
  517. rgbEncoded[cbEncoded++] = '_';
  518. // Otherwise, if this is an escapeable character
  519. else if (ISQPESCAPE(pb[i]))
  520. {
  521. // Write equal sign (start of an encodedn QP character
  522. rgbEncoded[cbEncoded++] = '=';
  523. rgbEncoded[cbEncoded++] = g_rgchHex[pb[i] >> 4];
  524. rgbEncoded[cbEncoded++] = g_rgchHex[pb[i] & 0x0F];
  525. }
  526. // Otherwise, just write the char as is
  527. else
  528. rgbEncoded[cbEncoded++] = pb[i];
  529. }
  530. // Write rgbEncoded
  531. CHECKHR(hr = pStream->Write(rgbEncoded, cbEncoded, NULL));
  532. exit:
  533. // Done
  534. return hr;
  535. }
  536. // --------------------------------------------------------------------------------
  537. // FContainsExtended
  538. // --------------------------------------------------------------------------------
  539. BOOL FContainsExtended(LPPROPSTRINGA pStringA, ULONG *pcExtended)
  540. {
  541. // Invalid Arg
  542. Assert(ISVALIDSTRINGA(pStringA) && pcExtended);
  543. // Init
  544. *pcExtended = 0;
  545. // Look for an extended character
  546. for (ULONG cch=0; cch<pStringA->cchVal; cch++)
  547. {
  548. // Is this an extended char
  549. if (IS_EXTENDED(pStringA->pszVal[cch]))
  550. {
  551. // Count
  552. (*pcExtended)++;
  553. }
  554. }
  555. // Done
  556. return ((*pcExtended) > 0) ? TRUE : FALSE;
  557. }
  558. #define IS_EXTENDED_W(wch) \
  559. ((wch > 126 || wch < 32) && wch != L'\t' && wch != L'\n' && wch != L'\r')
  560. // --------------------------------------------------------------------------------
  561. // FContainsExtendedW
  562. // --------------------------------------------------------------------------------
  563. BOOL FContainsExtendedW(LPPROPSTRINGW pStringW, ULONG *pcExtended)
  564. {
  565. // Invalid Arg
  566. Assert(ISVALIDSTRINGW(pStringW) && pcExtended);
  567. // Init
  568. *pcExtended = 0;
  569. // Look for an extended character
  570. for (ULONG cch=0; cch<pStringW->cchVal; cch++)
  571. {
  572. // Is this an extended char
  573. if (IS_EXTENDED_W(pStringW->pszVal[cch]))
  574. {
  575. // Count
  576. (*pcExtended)++;
  577. }
  578. }
  579. // Done
  580. return ((*pcExtended) > 0) ? TRUE : FALSE;
  581. }
  582. // --------------------------------------------------------------------------------
  583. // HrRfc1522Encode
  584. // --------------------------------------------------------------------------------
  585. MIMEOLEAPI MimeOleRfc1522Encode(
  586. /* in */ LPCSTR pszValue,
  587. /* in */ HCHARSET hCharset,
  588. /* out */ LPSTR *ppszEncoded)
  589. {
  590. // Locals
  591. HRESULT hr=S_OK;
  592. LPINETCSETINFO pCharset;
  593. MIMEVARIANT rSource;
  594. MIMEVARIANT rDest;
  595. CODEPAGEID cpiSource;
  596. CODEPAGEID cpiDest;
  597. // Invalid Arg
  598. if (NULL == pszValue || NULL == hCharset || NULL == ppszEncoded)
  599. return TrapError(E_INVALIDARG);
  600. // Init rDest
  601. ZeroMemory(&rDest, sizeof(MIMEVARIANT));
  602. // Setup rSource
  603. rSource.type = MVT_STRINGA;
  604. rSource.rStringA.pszVal = (LPSTR)pszValue;
  605. rSource.rStringA.cchVal = lstrlen(pszValue);
  606. // Open the Character Set
  607. CHECKHR(hr = g_pInternat->HrOpenCharset(hCharset, &pCharset));
  608. // Setup rDest
  609. rDest.type = MVT_STRINGA;
  610. // Convert the String
  611. CHECKHR(hr = g_pInternat->HrConvertString(pCharset->cpiWindows, pCharset->cpiInternet, &rSource, &rDest));
  612. // Setup Source and Dest Charset
  613. cpiSource = pCharset->cpiWindows;
  614. cpiDest = pCharset->cpiInternet;
  615. // Adjust the Codepages
  616. CHECKHR(hr = g_pInternat->HrValidateCodepages(&rSource, &rDest, NULL, NULL, &cpiSource, &cpiDest));
  617. // 1522 Encode this dude
  618. CHECKHR(hr = HrRfc1522Encode(&rSource, &rDest, cpiSource, cpiDest, pCharset->szName, ppszEncoded));
  619. exit:
  620. // Cleanup
  621. MimeVariantFree(&rDest);
  622. // Done
  623. return hr;
  624. }
  625. // --------------------------------------------------------------------------------
  626. // HrRfc1522Encode
  627. // --------------------------------------------------------------------------------
  628. HRESULT HrRfc1522Encode(LPMIMEVARIANT pSource, LPMIMEVARIANT pDest, CODEPAGEID cpiSource,
  629. CODEPAGEID cpiDest, LPCSTR pszCharset, LPSTR *ppszEncoded)
  630. {
  631. // Locals
  632. HRESULT hr=S_OK;
  633. CByteStream cStream;
  634. CHAR chEncoding;
  635. ULONG cExtended=0;
  636. LPBYTE pb;
  637. ULONG cb;
  638. ULONG i=0;
  639. ULONG cbFirstTry;
  640. ULONG cbRead;
  641. BLOB rBlobSource;
  642. BLOB rBlobCset;
  643. ULONG cTrys;
  644. CHAR szEncoding[1];
  645. ULONG iBefore;
  646. ULONG iAfter;
  647. ULONG cbExtra;
  648. ULARGE_INTEGER uli;
  649. LARGE_INTEGER li;
  650. //IStream *pStream=NULL;
  651. // Invalid Arg
  652. Assert(pSource && pDest && pszCharset && ppszEncoded);
  653. Assert(MVT_STRINGW == pSource->type ? CP_UNICODE == cpiSource : CP_UNICODE != cpiSource);
  654. Assert(cpiDest != CP_UNICODE && MVT_STRINGA == pDest->type);
  655. // Init
  656. *ppszEncoded = NULL;
  657. uli.HighPart = 0;
  658. li.HighPart = 0;
  659. rBlobCset.pBlobData = NULL;
  660. // Raid-50014: Will will always rfc1522 encode utf encodings
  661. // Raid-50788: Cannot post UTF news messages
  662. if (MVT_STRINGW != pSource->type)
  663. {
  664. // If it does not contain 8bit, then no rfc1522 encoding is needed
  665. if (FALSE == FContainsExtended(&pSource->rStringA, &cExtended))
  666. {
  667. hr = E_FAIL;
  668. goto exit;
  669. }
  670. }
  671. // We should be converting to UTF...
  672. else
  673. {
  674. // Must be encoding into utf
  675. Assert(65000 == cpiDest || 65001 == cpiDest);
  676. // If it does not contain 8bit, then no rfc1522 encoding is needed
  677. if (FALSE == FContainsExtendedW(&pSource->rStringW, &cExtended))
  678. {
  679. hr = E_FAIL;
  680. goto exit;
  681. }
  682. }
  683. // Create a Stream
  684. // CHECKHR(hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream));
  685. // Compute Encoding...
  686. chEncoding = (((cExtended * 100) / pSource->rStringA.cchVal) >= 17) ? 'B' : 'Q';
  687. // RAID-21673: If DBCS source codepage, then, encode the entire string so that I don't fragment the character set encoding.
  688. if (IsDBCSCodePage(cpiSource))
  689. chEncoding = 'B';
  690. // Set szEncoding
  691. szEncoding[0] = chEncoding;
  692. // Setup Encoding Loop
  693. pb = (MVT_STRINGW == pSource->type) ? (LPBYTE)pSource->rStringW.pszVal : (LPBYTE)pSource->rStringA.pszVal;
  694. cb = (MVT_STRINGW == pSource->type) ? (pSource->rStringW.cchVal * sizeof(WCHAR)) : pSource->rStringA.cchVal;
  695. // Adjust pszCharset
  696. if (CP_JAUTODETECT == cpiDest || 50222 == cpiDest || 50221 == cpiDest)
  697. pszCharset = (LPSTR)c_szISO2022JP;
  698. // Compute cbExtra - =?cchCharset?1?<cbFirstTry>?=
  699. cbExtra = 2 + lstrlen(pszCharset) + 3 + 2;
  700. // Compute cbFirstTry
  701. cbFirstTry = 76 - cbExtra;
  702. // cbFirstTry needs to be even if we are encoding unicode
  703. cbFirstTry -= (cbFirstTry % 2);
  704. // Adjust cbFirstTry if its greater than cb
  705. cbFirstTry = min(cb, cbFirstTry);
  706. // Make sure cbFirstTry is even ... That's a good starting point,
  707. // but we need to make sure that we're not breaking on a leading byte
  708. // On Korean (and other DBCS locales) we can have a mix of
  709. // SBCs and MBCs, so just being even isn't enough
  710. if(pb && (MVT_STRINGW != pSource->type))
  711. {
  712. LPCSTR pszEnd = (LPCSTR)&pb[cbFirstTry],
  713. pszNewEnd = NULL;
  714. pszNewEnd = CharPrevExA((WORD)cpiSource, (LPCSTR)pb, pszEnd, 0);
  715. if(pszNewEnd && (pszNewEnd == (pszEnd - 1)) && IsDBCSLeadByteEx(cpiSource, *pszNewEnd))
  716. cbFirstTry-= (ULONG)(pszEnd - pszNewEnd);
  717. }
  718. // Loop until we have encoded the entire string
  719. while (i < cb)
  720. {
  721. // Write Prefix
  722. CHECKHR(hr = cStream.Write("=?", 2, NULL));
  723. CHECKHR(hr = cStream.Write(pszCharset, lstrlen(pszCharset), NULL));
  724. CHECKHR(hr = cStream.Write("?", 1, NULL));
  725. CHECKHR(hr = cStream.Write(szEncoding, 1, NULL));
  726. CHECKHR(hr = cStream.Write("?", 1, NULL));
  727. // Compute Try Amount
  728. rBlobSource.cbSize = min(cb - i, cbFirstTry);
  729. rBlobSource.pBlobData = (LPBYTE)(pb + i);
  730. // Get Index
  731. CHECKHR(hr = HrGetStreamPos(&cStream, &iBefore));
  732. // Encoded blocks until we get one at a good length
  733. for (cTrys=0;;cTrys++)
  734. {
  735. // Too many Trys ?
  736. if (cTrys > 100)
  737. {
  738. AssertSz(FALSE, "Too many rfc1522 encode buffer reduction attemps, failing (No rfc1522 encoding will be applied).");
  739. hr = TrapError(E_FAIL);
  740. goto exit;
  741. }
  742. // Memory Leak
  743. Assert(NULL == rBlobCset.pBlobData);
  744. // Convert Block
  745. CHECKHR(hr = g_pInternat->ConvertBuffer(cpiSource, cpiDest, &rBlobSource, &rBlobCset, &cbRead));
  746. // Problem
  747. if (cbRead == 0)
  748. {
  749. AssertSz(FALSE, "Bad buffer conversion");
  750. hr = TrapError(E_FAIL);
  751. goto exit;
  752. }
  753. // Validate
  754. Assert(cbRead <= rBlobSource.cbSize);
  755. // 'B' Encoding
  756. if ('B' == chEncoding)
  757. {
  758. // ApplyBase64
  759. CHECKHR(hr = HrRfc1522EncodeBase64(rBlobCset.pBlobData, rBlobCset.cbSize, &cStream));
  760. }
  761. else
  762. {
  763. // ApplyQP
  764. CHECKHR(hr = HrRfc1522EncodeQP(rBlobCset.pBlobData, rBlobCset.cbSize, &cStream));
  765. }
  766. // Get Index
  767. CHECKHR(hr = HrGetStreamPos(&cStream, &iAfter));
  768. // Validate
  769. Assert(iAfter > iBefore);
  770. // Too big ?
  771. if ((iAfter - iBefore) + cbExtra <= 76)
  772. break;
  773. // Problem
  774. if (rBlobSource.cbSize <= 5)
  775. {
  776. Assert(FALSE);
  777. hr = TrapError(E_FAIL);
  778. goto exit;
  779. }
  780. // Cleanup
  781. SafeMemFree(rBlobCset.pBlobData);
  782. // Seek Back to iBefore
  783. uli.LowPart = iBefore;
  784. cStream.SetSize(uli);
  785. // Seek Backwards
  786. li.LowPart = iBefore;
  787. cStream.Seek(li, STREAM_SEEK_SET, NULL);
  788. // Compute Inflation Rate
  789. if (0 == cTrys)
  790. rBlobSource.cbSize = (((76 - cbExtra) * rBlobSource.cbSize) / (iAfter - iBefore));
  791. // Otherwise, start taking off 5 bytes
  792. else
  793. rBlobSource.cbSize -= 5;
  794. // Make sure it is even ... That's a good starting point,
  795. // but we need to make sure that we're not breaking on a leading byte
  796. // On Korean (and other DBCS locales) we can have a mix of
  797. // SBCs and MBCs, so just being even isn't enough
  798. rBlobSource.cbSize -= (rBlobSource.cbSize % 2);
  799. if(rBlobSource.pBlobData && (MVT_STRINGW != pSource->type))
  800. {
  801. LPCSTR pszEnd = (LPCSTR)&rBlobSource.pBlobData[rBlobSource.cbSize],
  802. pszNewEnd = NULL;
  803. pszNewEnd = CharPrevExA((WORD)cpiSource, (LPCSTR)rBlobSource.pBlobData, pszEnd, 0);
  804. if(pszNewEnd && (pszNewEnd == (pszEnd - 1)) && IsDBCSLeadByteEx(cpiSource, *pszNewEnd))
  805. rBlobSource.cbSize-= (ULONG)(pszEnd - pszNewEnd);
  806. }
  807. // Should be less than cb
  808. Assert(rBlobSource.cbSize < cb);
  809. }
  810. // Write termination
  811. CHECKHR(hr = cStream.Write("?=", 2, NULL));
  812. // Increment i
  813. i += cbRead;
  814. // Cleanup
  815. SafeMemFree(rBlobCset.pBlobData);
  816. // Write folding
  817. if (i < cb)
  818. {
  819. // Write Fold
  820. CHECKHR(hr = cStream.Write(c_szCRLFTab, lstrlen(c_szCRLFTab), NULL));
  821. }
  822. }
  823. // Return the encoded string
  824. CHECKHR(hr = cStream.HrAcquireStringA(&cb, ppszEncoded, ACQ_DISPLACE));
  825. //cStream.Commit(STGC_DEFAULT);
  826. //CHECKALLOC(*ppszEncoded = PszFromANSIStreamA(pStream));
  827. exit:
  828. // Cleanup
  829. //ReleaseObj(pStream);
  830. g_pMalloc->Free(rBlobCset.pBlobData);
  831. // Done
  832. return hr;
  833. }
  834. // --------------------------------------------------------------------------------
  835. // MimeOleRfc1522Decode
  836. // --------------------------------------------------------------------------------
  837. MIMEOLEAPI MimeOleRfc1522Decode(LPCSTR pszValue, LPSTR pszCharset, ULONG cchmax, LPSTR *ppszDecoded)
  838. {
  839. // Locals
  840. HRESULT hrEncoded=E_FAIL,
  841. hrCsetFound=E_FAIL;
  842. RFC1522OUT rOut;
  843. LPSTR psz=(LPSTR)pszValue,
  844. pszNew;
  845. CHAR szCset[CCHMAX_CSET_NAME],
  846. chEncoding;
  847. BOOL bNonBlankLeading;
  848. // check params
  849. if (NULL == pszValue)
  850. return TrapError(E_INVALIDARG);
  851. // Init out structure
  852. ZeroMemory(&rOut, sizeof(RFC1522OUT));
  853. // Save data..
  854. if (ppszDecoded)
  855. rOut.fWrite = TRUE;
  856. // Start decoding loop...
  857. while(psz && *psz)
  858. {
  859. // Seek to start of 1522 encoding...
  860. pszNew = PszRfc1522Find(psz, &bNonBlankLeading);
  861. Assert(pszNew!=NULL);
  862. if (bNonBlankLeading || psz == pszValue || !*psz)
  863. {
  864. // Either we found non-blank characters before the word,
  865. // or this is the first word on the line, or we didn't
  866. // find a word. Whatever, we need to write all of the
  867. // data before the word.
  868. if (FAILED(HrRfc1522WriteStr(&rOut, psz, (LONG) (pszNew-psz))))
  869. {
  870. break;
  871. }
  872. }
  873. // If didn't find start.. were done
  874. if (!*pszNew)
  875. break;
  876. // Set psz to new position
  877. psz = pszNew;
  878. // Get charset
  879. pszNew = PszRfc1522GetCset(psz, szCset, ARRAYSIZE(szCset));
  880. // If didn't parse charset correctly, continue
  881. if (NULL == pszNew)
  882. {
  883. psz++;
  884. continue;
  885. }
  886. // Character set was found
  887. hrCsetFound = S_OK;
  888. // Was caller just looking for the charset...
  889. if (NULL == ppszDecoded)
  890. break;
  891. // Otherwise, parse encoding
  892. pszNew = PszRfc1522GetEncoding(pszNew, &chEncoding);
  893. // If didn't parse charset correctly, continue
  894. if (NULL == pszNew)
  895. {
  896. psz++;
  897. continue;
  898. }
  899. // Decode the text to the end - THIS SHOULD NEVER FAIL...
  900. psz = PszRfc1522Decode(pszNew, chEncoding, &rOut);
  901. // It is a valid encoded string
  902. if (psz)
  903. hrEncoded = S_OK;
  904. }
  905. // Were we actually decoding...
  906. if (ppszDecoded && hrEncoded == S_OK)
  907. {
  908. // Commit the stream
  909. if (FAILED(HrRfc1522WriteDone(&rOut, ppszDecoded)))
  910. {
  911. *ppszDecoded = NULL;
  912. hrEncoded = S_FALSE;
  913. }
  914. }
  915. // Otherwise, return charset...
  916. if (pszCharset && hrCsetFound == S_OK)
  917. StrCpyN(pszCharset, szCset, cchmax);
  918. // Cleanup
  919. SafeRelease(rOut.pstm);
  920. // Done
  921. return ppszDecoded ? hrEncoded : hrCsetFound;
  922. }