1106 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			1106 lines
		
	
	
		
			32 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // --------------------------------------------------------------------------------
 | ||
| // rfc1522.cpp
 | ||
| // Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved
 | ||
| // Steven J. Bailey
 | ||
| // --------------------------------------------------------------------------------
 | ||
| #include "pch.hxx"
 | ||
| #include "internat.h"
 | ||
| #include "dllmain.h"
 | ||
| #include "inetconv.h"
 | ||
| #include "strconst.h"
 | ||
| #include "variantx.h"
 | ||
| #include "mimeapi.h"
 | ||
| #include "demand.h"
 | ||
| 
 | ||
| // --------------------------------------------------------------------------------
 | ||
| // ISQPESCAPE(_ch)
 | ||
| // --------------------------------------------------------------------------------
 | ||
| #define ISQPESCAPE(_ch) \
 | ||
|     (IS_EXTENDED(_ch) || _ch == '?' || _ch == '=' || \
 | ||
|                         _ch == '_' || _ch == '"' || \
 | ||
|                         _ch == '<' || _ch == '>' || \
 | ||
|                         _ch == '(' || _ch == ')' || \
 | ||
|                         _ch == '[' || _ch == ']' || \
 | ||
|                         _ch == ',')
 | ||
| 
 | ||
| // --------------------------------------------------------------------------------
 | ||
| // RFC1522OUT
 | ||
| // --------------------------------------------------------------------------------
 | ||
| typedef struct tagRFC1522OUT {
 | ||
|     BOOL            fWrite;
 | ||
|     CHAR            szBuffer[512];
 | ||
|     ULONG           iBuffer;
 | ||
|     LPSTREAM        pstm;
 | ||
| } RFC1522OUT, *LPRFC1522OUT;
 | ||
| 
 | ||
| // --------------------------------------------------------------------------------
 | ||
| // HrRfc1522WriteDone
 | ||
| // --------------------------------------------------------------------------------
 | ||
| HRESULT HrRfc1522WriteDone(LPRFC1522OUT pOut, LPSTR *ppszRfc1522)
 | ||
| {
 | ||
|     // We better be writing
 | ||
|     Assert(pOut->fWrite && ppszRfc1522);
 | ||
| 
 | ||
|     // Init
 | ||
|     *ppszRfc1522 = NULL;
 | ||
| 
 | ||
|     // If we haven't created the stream yet, just use the buffer...
 | ||
|     if (NULL == pOut->pstm)
 | ||
|     {
 | ||
|         // No data
 | ||
|         if (0 == pOut->iBuffer)
 | ||
|             return S_OK;
 | ||
| 
 | ||
|         // Allocate
 | ||
|         *ppszRfc1522 = PszAllocA(pOut->iBuffer + 1);
 | ||
|         if (NULL == *ppszRfc1522)
 | ||
|             return TrapError(E_OUTOFMEMORY); 
 | ||
| 
 | ||
|         // Copy data
 | ||
|         CopyMemory(*ppszRfc1522, pOut->szBuffer, pOut->iBuffer);
 | ||
| 
 | ||
|         // Null term
 | ||
|         *((*ppszRfc1522) + pOut->iBuffer) = '\0';
 | ||
|     }
 | ||
| 
 | ||
|     // Otherwise, do stream
 | ||
|     else
 | ||
|     {
 | ||
|         // Commit final data to stream...
 | ||
|         if (0 != pOut->iBuffer)
 | ||
|         {
 | ||
|             if (FAILED(pOut->pstm->Write(pOut->szBuffer, pOut->iBuffer, NULL)))
 | ||
|                 return TrapError(E_OUTOFMEMORY); 
 | ||
|         }
 | ||
| 
 | ||
|         // Commit the stream
 | ||
|         if (FAILED(pOut->pstm->Commit(STGC_DEFAULT)))
 | ||
|             return TrapError(E_OUTOFMEMORY); 
 | ||
| 
 | ||
|         // Convert the stream to an ANSI string
 | ||
|         *ppszRfc1522 = PszFromANSIStreamA(pOut->pstm);
 | ||
|         Assert(NULL != *ppszRfc1522);
 | ||
| 
 | ||
|         // Release the stream
 | ||
|         SafeRelease(pOut->pstm);
 | ||
|     }
 | ||
| 
 | ||
|     // Done
 | ||
|     return S_OK;
 | ||
| }
 | ||
| 
 | ||
| // --------------------------------------------------------------------------------
 | ||
| // HrRfc1522Write
 | ||
| // --------------------------------------------------------------------------------
 | ||
| HRESULT HrRfc1522Write(LPRFC1522OUT pOut, UCHAR ch)
 | ||
| {
 | ||
|     // If not saving data..
 | ||
|     if (!pOut->fWrite)
 | ||
|         return S_OK;
 | ||
| 
 | ||
|     // If buffer + 1 is full, dump to stream
 | ||
|     if (pOut->iBuffer + 1 > sizeof(pOut->szBuffer))
 | ||
|     {
 | ||
|         // Do I have a stream yet...
 | ||
|         if (NULL == pOut->pstm)
 | ||
|         {
 | ||
|             // Create stream
 | ||
|             if (FAILED(MimeOleCreateVirtualStream(&pOut->pstm)))
 | ||
|                 return TrapError(E_OUTOFMEMORY);
 | ||
|         }
 | ||
| 
 | ||
|         // Write buffer to the stream
 | ||
|         if (FAILED(pOut->pstm->Write(pOut->szBuffer, pOut->iBuffer, NULL)))
 | ||
|             return TrapError(E_OUTOFMEMORY); 
 | ||
| 
 | ||
|         // Reset buffers
 | ||
|         pOut->iBuffer = 0;
 | ||
|     }
 | ||
| 
 | ||
|     // Add character to the buffer
 | ||
|     pOut->szBuffer[pOut->iBuffer++] = ch;
 | ||
| 
 | ||
|     // Done
 | ||
|     return S_OK;
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| // --------------------------------------------------------------------------------
 | ||
| // HrRfc1522WriteStr
 | ||
| // --------------------------------------------------------------------------------
 | ||
| HRESULT HrRfc1522WriteStr(LPRFC1522OUT pOut, LPSTR psz, LONG cb)
 | ||
| {
 | ||
|     HRESULT hr;
 | ||
|     while(cb)
 | ||
|     {
 | ||
|         hr = HrRfc1522Write(pOut, *psz);
 | ||
|         if (FAILED(hr))
 | ||
|             return hr;
 | ||
|         psz++;
 | ||
|         cb--;
 | ||
|     }
 | ||
| 
 | ||
|     return S_OK;
 | ||
| }
 | ||
| 
 | ||
| inline BOOL IsRfc1522Token(UCHAR ch)
 | ||
| {
 | ||
|                         //   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
 | ||
|     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
 | ||
|                              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
 | ||
|                              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
 | ||
|                              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
 | ||
|                              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
 | ||
|                              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
 | ||
|                              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
 | ||
|                              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
 | ||
| 
 | ||
|     Assert(sizeof(abToken)/sizeof(abToken[1])==256);
 | ||
|     Assert(abToken['(']==FALSE);
 | ||
|     Assert(abToken[')']==FALSE);
 | ||
|     Assert(abToken['<']==FALSE);
 | ||
|     Assert(abToken['>']==FALSE);
 | ||
|     Assert(abToken['@']==FALSE);
 | ||
|     Assert(abToken[',']==FALSE);
 | ||
|     Assert(abToken[';']==FALSE);
 | ||
|     Assert(abToken[':']==FALSE);
 | ||
|     Assert(abToken['/']==FALSE);
 | ||
|     Assert(abToken['[']==FALSE);
 | ||
|     Assert(abToken[']']==FALSE);
 | ||
|     Assert(abToken['?']==FALSE);
 | ||
|     Assert(abToken['.']==FALSE);
 | ||
|     Assert(abToken['=']==FALSE);
 | ||
| 
 | ||
|     return (BOOL) abToken[ch];
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| // --------------------------------------------------------------------------------
 | ||
| // PszRfc1522Find
 | ||
| //
 | ||
| // Find an RFC1522 word.  If the string pointer passed in is NULL, then NULL is
 | ||
| // returned.  If a word is not found, then a pointer to the terminatng NULL is
 | ||
| // returned.  If a word is found, then a pointer to the word is returned, and
 | ||
| // an output parameter for whether the word was preceeded by non-blank characters
 | ||
| // is set.
 | ||
| //
 | ||
| // If a word is not found, then the output parameter is undefined.
 | ||
| //
 | ||
| // The output parameter is optional - it may be NULL, in which case the value
 | ||
| // will not be stored.
 | ||
| //
 | ||
| // --------------------------------------------------------------------------------
 | ||
| LPSTR PszRfc1522Find(LPSTR psz,
 | ||
|                      BOOL *pbNonBlankLeading)
 | ||
| {
 | ||
|     LPSTR pszCharset;
 | ||
| 
 | ||
|     if (!psz || !*psz)
 | ||
|     {
 | ||
|         goto exit;
 | ||
|     }
 | ||
| 
 | ||
|     if (pbNonBlankLeading)
 | ||
|     {
 | ||
|         *pbNonBlankLeading = FALSE;
 | ||
|     }
 | ||
| 
 | ||
|     // Skip over any leading blanks.
 | ||
|     while (*psz && (*psz == ' ' || *psz == '\t' || *psz == '\r' || *psz == '\n'))
 | ||
|     {
 | ||
|         psz++;
 | ||
|     }
 | ||
| 
 | ||
|     if (*psz && (psz[0] != '=' || psz[1] != '?'))
 | ||
|     {
 | ||
| again:
 | ||
|         // If we end up here (either through the if above or by a goto from below),
 | ||
|         // it means that we have some number of non-blank characters before the
 | ||
|         // RFC1522 word.
 | ||
|         if (pbNonBlankLeading)
 | ||
|         {
 | ||
|             *pbNonBlankLeading = TRUE;
 | ||
|         }
 | ||
|     }
 | ||
|     // Skip until we find an =?.
 | ||
|     while (*psz && (psz[0] != '=' || psz[1] != '?'))
 | ||
|     {
 | ||
|         psz++;
 | ||
|     }
 | ||
|     if (!*psz)
 | ||
|     {
 | ||
|         // End of the string.
 | ||
|         goto exit;
 | ||
|     }
 | ||
|     Assert(psz[0] == '=' && psz[1] == '?');
 | ||
| 
 | ||
|     // Parse out the charset.
 | ||
|     pszCharset = psz;
 | ||
|     psz += 2;
 | ||
|     while (IsRfc1522Token(*psz))
 | ||
|     {
 | ||
|         psz++;
 | ||
|     }
 | ||
|     if (!*psz)
 | ||
|     {
 | ||
|         // End of the string.
 | ||
|         goto exit;
 | ||
|     }
 | ||
|     if (*psz != '?')
 | ||
|     {
 | ||
|         // Malformed.
 | ||
|         goto again;
 | ||
|     }
 | ||
|     Assert(*psz == '?');
 | ||
| 
 | ||
|     // Parse out the encoding.
 | ||
|     psz++;
 | ||
|     while (IsRfc1522Token(*psz))
 | ||
|     {
 | ||
|         psz++;
 | ||
|     }
 | ||
|     if (!*psz)
 | ||
|     {
 | ||
|         // End of the string.
 | ||
|         goto exit;
 | ||
|     }
 | ||
|     if (*psz != '?')
 | ||
|     {
 | ||
|         // Malformed.
 | ||
|         goto again;
 | ||
|     }
 | ||
|     Assert(*psz == '?');
 | ||
| 
 | ||
|     // Parse out the data.
 | ||
|     psz++;
 | ||
|     while (*psz && (psz[0] != '?' || psz[1] != '='))
 | ||
|     {
 | ||
|         psz++;
 | ||
|     }
 | ||
|     if (!*psz)
 | ||
|     {
 | ||
|         // End of the string.
 | ||
|         goto exit;
 | ||
|     }
 | ||
|     Assert(psz[0] == '?' && psz[1] == '=');
 | ||
| 
 | ||
|     psz = pszCharset;
 | ||
| 
 | ||
| exit:
 | ||
|     return psz;
 | ||
| }
 | ||
| 
 | ||
| // --------------------------------------------------------------------------------
 | ||
| // PszRfc1522Decode - *(*ppsz) -> '?'
 | ||
| // --------------------------------------------------------------------------------
 | ||
| LPSTR PszRfc1522Decode(LPSTR psz, CHAR chEncoding, LPRFC1522OUT pOut)
 | ||
| {
 | ||
|     // Check params
 | ||
|     Assert(pOut && psz && *psz == '?');
 | ||
|     Assert(chEncoding == 'B' || chEncoding == 'b' || chEncoding == 'Q' || chEncoding == 'q');
 | ||
| 
 | ||
|     // Step over '?'
 | ||
|     psz++;
 | ||
| 
 | ||
|     // Done...
 | ||
|     if ('\0' == *psz)
 | ||
|         return psz;
 | ||
| 
 | ||
|     // Q encoding
 | ||
|     if ('Q' == chEncoding || 'q' == chEncoding)
 | ||
|     {
 | ||
|         // Locals
 | ||
|         UCHAR   uch;
 | ||
|         CHAR    ch,
 | ||
|                 ch1,
 | ||
|                 ch2;
 | ||
|         LPSTR   pszMark;
 | ||
| 
 | ||
|         // While we have characters and '?=' is not found...
 | ||
|         while(*psz && (psz[0] != '?' || psz[1] != '='))
 | ||
|         {
 | ||
|             // Get next char
 | ||
|             uch = *psz++;
 | ||
| 
 | ||
|             // Encoded hex pair i.e. '=1d'
 | ||
|             if (uch == '=')
 | ||
|             {
 | ||
|                 // Mark position
 | ||
|                 pszMark = psz;
 | ||
| 
 | ||
|                 // Hex char 1 - If no null...
 | ||
|                 if (*psz != '\0') ch1 = ChConvertFromHex (*psz++);
 | ||
|                 else ch1 = (char)255;
 | ||
| 
 | ||
|                 // Hex char 2 - if no null
 | ||
|                 if (*psz != '\0') ch2 = ChConvertFromHex (*psz++);
 | ||
|                 else ch2 = (char)255;
 | ||
|                 
 | ||
|                 //////////////////////////////////////////////////////////////////
 | ||
|                 // raid x5-69640 - Incomplete QP encoded letter causes <20>
 | ||
|                 // This is a sign-extension bug - we need to compare against
 | ||
|                 // (char)255 instead of 255...
 | ||
|                 //
 | ||
|                 // If both are = 255, its an equal sign
 | ||
|                 if (ch1 == (char)255 || ch2 == (char)255)
 | ||
|                 //^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | ||
|                 {
 | ||
|                     if (FAILED(HrRfc1522Write(pOut, '=')))
 | ||
|                         return NULL;
 | ||
|                     psz = pszMark;
 | ||
|                 }
 | ||
| 
 | ||
|                 // Otherwise, build character
 | ||
|                 else
 | ||
|                 {
 | ||
|                     ch = ( ch1 << 4 ) | ch2;
 | ||
|                     if (FAILED(HrRfc1522Write(pOut, ch)))
 | ||
|                         return NULL;
 | ||
|                 }
 | ||
|             }
 | ||
| 
 | ||
|             // _ equals space...
 | ||
|             else if( uch == '_' )
 | ||
|             {
 | ||
|                 if (FAILED(HrRfc1522Write(pOut, ' ')))
 | ||
|                     return NULL;
 | ||
|             }
 | ||
| 
 | ||
|             // Otherwise, just append the character
 | ||
|             else
 | ||
|             {
 | ||
|                 if (FAILED(HrRfc1522Write(pOut, uch)))
 | ||
|                     return NULL;
 | ||
|             }
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     // B encoding
 | ||
|     else
 | ||
|     {
 | ||
|         // Locals
 | ||
|         ULONG  cbIn=0,
 | ||
|                cbPad=0;
 | ||
|         UCHAR  ucIn[4], 
 | ||
|                uch;
 | ||
| 
 | ||
|         // While we have characters and '?=' is not found...
 | ||
|         while(*psz && (psz[0] != '?' || psz[1] != '='))
 | ||
|         {
 | ||
|             // Gets 4 legal Base64 characters, ignores if illegal
 | ||
|             uch = *psz++;
 | ||
| 
 | ||
|             // Decode base64 character
 | ||
|             ucIn[cbIn] = DECODE64(uch) ;
 | ||
| 
 | ||
|             // Inc count
 | ||
|             if (ucIn[cbIn] < 64 || (uch == '=' && cbIn > 1))
 | ||
|                 cbIn++;
 | ||
| 
 | ||
|             // Pad it
 | ||
|             if (uch == '=' && cbIn > 1)
 | ||
|                 cbPad++;
 | ||
| 
 | ||
|             // Outputs when 4 legal Base64 characters are in the buffer
 | ||
|             if (cbIn == 4)
 | ||
|             {
 | ||
|                 if (cbPad < 3) 
 | ||
|                 {
 | ||
|                     if (FAILED(HrRfc1522Write(pOut, (ucIn[0] << 2) | (ucIn[1] >> 4))))
 | ||
|                         return NULL;
 | ||
|                 }
 | ||
|                 if (cbPad < 2) 
 | ||
|                 {
 | ||
|                     if (FAILED(HrRfc1522Write(pOut, (ucIn[1] << 4) | (ucIn[2] >> 2))))
 | ||
|                         return NULL;
 | ||
|                 }
 | ||
|                 if (cbPad < 1) 
 | ||
|                 {
 | ||
|                     if (FAILED(HrRfc1522Write(pOut, (ucIn[2] << 6) | (ucIn[3]))))
 | ||
|                         return NULL;
 | ||
|                 }
 | ||
|                 cbIn = 0;
 | ||
|             }
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     // Finish stepping
 | ||
|     if ('?' == *psz)
 | ||
|         psz++;
 | ||
|     if ('=' == *psz)
 | ||
|         psz++;
 | ||
| 
 | ||
|     // Done
 | ||
|     return psz;
 | ||
| }
 | ||
| 
 | ||
| // --------------------------------------------------------------------------------
 | ||
| // PszRfc1522GetEncoding - *(*ppsz) -> '?'
 | ||
| //
 | ||
| // Returns NULL if '?X?' is not found... or B b Q q is not found
 | ||
| // --------------------------------------------------------------------------------
 | ||
| LPSTR PszRfc1522GetEncoding(LPSTR psz, CHAR *pchEncoding)
 | ||
| {
 | ||
|     // Done
 | ||
|     if ('\0' == *psz)
 | ||
|         return NULL;
 | ||
| 
 | ||
|     // Should be pointing to '='
 | ||
|     if ('?' != *psz)
 | ||
|         return NULL;
 | ||
| 
 | ||
|     // Next character
 | ||
|     psz++;
 | ||
| 
 | ||
|     // Done
 | ||
|     if ('\0' == *psz)
 | ||
|         return NULL;
 | ||
| 
 | ||
|     // Save encoding...
 | ||
|     *pchEncoding = *psz;
 | ||
| 
 | ||
|     // Step over encoding character
 | ||
|     psz++;
 | ||
| 
 | ||
|     // Done
 | ||
|     if ('\0' == *psz)
 | ||
|         return NULL;
 | ||
| 
 | ||
|     // Should be pointing to '='
 | ||
|     if ('?' != *psz)
 | ||
|         return NULL;
 | ||
| 
 | ||
|     // Invalid encoding
 | ||
|     if ('B' != *pchEncoding && 'b' != *pchEncoding && 'Q' != *pchEncoding && 'q' != *pchEncoding)
 | ||
|         return NULL;
 | ||
| 
 | ||
|     // Continue
 | ||
|     return psz;
 | ||
| }
 | ||
| 
 | ||
| // --------------------------------------------------------------------------------
 | ||
| // PszRfc1522GetCset - *(*ppsz) -> '='
 | ||
| //
 | ||
| // Returns NULL if '=?CHARSET?' is not found
 | ||
| // --------------------------------------------------------------------------------
 | ||
| LPSTR PszRfc1522GetCset(LPSTR psz, LPSTR pszCharset, ULONG cchmax)
 | ||
| {
 | ||
|     // Locals
 | ||
|     LPSTR   pszStart, pszEnd;
 | ||
| 
 | ||
|     // Done
 | ||
|     if ('\0' == *psz)
 | ||
|         return NULL;
 | ||
| 
 | ||
|     // Should be pointing to '='
 | ||
|     if ('=' != *psz)
 | ||
|         return NULL;
 | ||
| 
 | ||
|     // Next character
 | ||
|     psz++;
 | ||
| 
 | ||
|     // Done
 | ||
|     if ('\0' == *psz)
 | ||
|         return NULL;
 | ||
| 
 | ||
|     // Should be pointing to '?'
 | ||
|     if ('?' != *psz)
 | ||
|         return NULL;
 | ||
| 
 | ||
|     // Step over '?'
 | ||
|     psz++;
 | ||
| 
 | ||
|     // Done
 | ||
|     if ('\0' == *psz)
 | ||
|         return NULL;
 | ||
| 
 | ||
|     // Save Start
 | ||
|     pszStart = psz;
 | ||
| 
 | ||
|     // Seek to next '?'
 | ||
|     while(*psz && *psz != '?')
 | ||
|         psz++;
 | ||
| 
 | ||
|     // Done
 | ||
|     if ('\0' == *psz)
 | ||
|         return NULL;
 | ||
| 
 | ||
|     // Save end
 | ||
|     pszEnd = psz;
 | ||
|     Assert(*pszEnd == '?');
 | ||
| 
 | ||
|     // Charset name is too large...
 | ||
|     if ((ULONG)(pszEnd - pszStart) > cchmax)
 | ||
|         return NULL;
 | ||
| 
 | ||
|     // Copy charset
 | ||
|     *pszEnd = '\0';
 | ||
| #ifndef WIN16
 | ||
|     lstrcpynA(pszCharset, pszStart, cchmax);
 | ||
| #else
 | ||
|     lstrcpyn(pszCharset, pszStart, cchmax);
 | ||
| #endif
 | ||
|     *pszEnd = '?';
 | ||
| 
 | ||
|     // Continue
 | ||
|     return psz;
 | ||
| }
 | ||
| 
 | ||
| // --------------------------------------------------------------------------------
 | ||
| // HrRfc1522EncodeBase64
 | ||
| // --------------------------------------------------------------------------------
 | ||
| HRESULT HrRfc1522EncodeBase64(UCHAR *pb, ULONG cb, LPSTREAM pStream)
 | ||
| {
 | ||
|     // Locals
 | ||
|     HRESULT         hr=S_OK;
 | ||
|     BYTE            rgbEncoded[1024];
 | ||
|     ULONG           cbEncoded=0;
 | ||
|     ULONG           i;
 | ||
|     UCHAR           uch[3];
 | ||
| 
 | ||
|     // Encodes 3 characters at a time
 | ||
|     for (i=0; i<cb; i+=3)
 | ||
|     {
 | ||
|         // Setup Buffer
 | ||
|         uch[0] = pb[i];
 | ||
|         uch[1] = (i + 1 < cb) ? pb[i + 1] : '\0';
 | ||
|         uch[2] = (i + 2 < cb) ? pb[i + 2] : '\0';
 | ||
| 
 | ||
|         // Encode first tow
 | ||
|         rgbEncoded[cbEncoded++] = g_rgchEncodeBase64[(uch[0] >> 2) & 0x3F];
 | ||
|         rgbEncoded[cbEncoded++] = g_rgchEncodeBase64[(uch[0] << 4 | uch[1] >> 4) & 0x3F];
 | ||
| 
 | ||
|         // Encode Next
 | ||
|         if (i + 1 < cb)
 | ||
|             rgbEncoded[cbEncoded++] = g_rgchEncodeBase64[(uch[1] << 2 | uch[2] >> 6) & 0x3F];
 | ||
|         else
 | ||
|             rgbEncoded[cbEncoded++] = '=';
 | ||
| 
 | ||
|         // Encode Net
 | ||
|         if (i + 2 < cb)
 | ||
|             rgbEncoded[cbEncoded++] = g_rgchEncodeBase64[(uch[2]) & 0x3F];
 | ||
|         else
 | ||
|             rgbEncoded[cbEncoded++] = '=';
 | ||
|     }
 | ||
| 
 | ||
|     // Write rgbEncoded
 | ||
|     CHECKHR(hr = pStream->Write(rgbEncoded, cbEncoded, NULL));
 | ||
| 
 | ||
| exit:
 | ||
|     // Done
 | ||
|     return hr;
 | ||
| }
 | ||
| 
 | ||
| // --------------------------------------------------------------------------------
 | ||
| // HrRfc1522EncodeQP
 | ||
| // --------------------------------------------------------------------------------
 | ||
| HRESULT HrRfc1522EncodeQP(UCHAR *pb, ULONG cb, LPSTREAM pStream)
 | ||
| {
 | ||
|     // Locals
 | ||
|     HRESULT     hr=S_OK;
 | ||
|     BYTE        rgbEncoded[1024];
 | ||
|     ULONG       cbEncoded=0;
 | ||
|     ULONG       i;
 | ||
| 
 | ||
|     // Loop through buffer
 | ||
|     for (i=0; i<cb; i++)
 | ||
|     {
 | ||
|         // Replace spaces with underscore - this is a more portable character
 | ||
|         if (pb[i] == ' ')
 | ||
|             rgbEncoded[cbEncoded++] = '_';
 | ||
| 
 | ||
|         // Otherwise, if this is an escapeable character
 | ||
|         else if (ISQPESCAPE(pb[i]))
 | ||
|         {
 | ||
|             // Write equal sign (start of an encodedn QP character
 | ||
|             rgbEncoded[cbEncoded++] = '=';
 | ||
|             rgbEncoded[cbEncoded++] = g_rgchHex[pb[i] >> 4];
 | ||
|             rgbEncoded[cbEncoded++] = g_rgchHex[pb[i] & 0x0F];
 | ||
|         }
 | ||
| 
 | ||
|         // Otherwise, just write the char as is
 | ||
|         else
 | ||
|             rgbEncoded[cbEncoded++] = pb[i];
 | ||
|     }
 | ||
| 
 | ||
|     // Write rgbEncoded
 | ||
|     CHECKHR(hr = pStream->Write(rgbEncoded, cbEncoded, NULL));
 | ||
| 
 | ||
| exit:
 | ||
|     // Done
 | ||
|     return hr;
 | ||
| }
 | ||
| 
 | ||
| // --------------------------------------------------------------------------------
 | ||
| // FContainsExtended
 | ||
| // --------------------------------------------------------------------------------
 | ||
| BOOL FContainsExtended(LPPROPSTRINGA pStringA, ULONG *pcExtended)
 | ||
| {
 | ||
|     // Invalid Arg
 | ||
|     Assert(ISVALIDSTRINGA(pStringA) && pcExtended);
 | ||
| 
 | ||
|     // Init
 | ||
|     *pcExtended = 0;
 | ||
| 
 | ||
|     // Look for an extended character
 | ||
|     for (ULONG cch=0; cch<pStringA->cchVal; cch++)
 | ||
|     {
 | ||
|         // Is this an extended char
 | ||
|         if (IS_EXTENDED(pStringA->pszVal[cch]))
 | ||
|         {
 | ||
|             // Count
 | ||
|             (*pcExtended)++;
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     // Done
 | ||
|     return ((*pcExtended) > 0) ? TRUE : FALSE;
 | ||
| }
 | ||
| 
 | ||
| #define IS_EXTENDED_W(wch) \
 | ||
|     ((wch > 126 || wch < 32) && wch != L'\t' && wch != L'\n' && wch != L'\r')
 | ||
| 
 | ||
| // --------------------------------------------------------------------------------
 | ||
| // FContainsExtendedW
 | ||
| // --------------------------------------------------------------------------------
 | ||
| BOOL FContainsExtendedW(LPPROPSTRINGW pStringW, ULONG *pcExtended)
 | ||
| {
 | ||
|     // Invalid Arg
 | ||
|     Assert(ISVALIDSTRINGW(pStringW) && pcExtended);
 | ||
| 
 | ||
|     // Init
 | ||
|     *pcExtended = 0;
 | ||
| 
 | ||
|     // Look for an extended character
 | ||
|     for (ULONG cch=0; cch<pStringW->cchVal; cch++)
 | ||
|     {
 | ||
|         // Is this an extended char
 | ||
|         if (IS_EXTENDED_W(pStringW->pszVal[cch]))
 | ||
|         {
 | ||
|             // Count
 | ||
|             (*pcExtended)++;
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     // Done
 | ||
|     return ((*pcExtended) > 0) ? TRUE : FALSE;
 | ||
| }
 | ||
| 
 | ||
| // --------------------------------------------------------------------------------
 | ||
| // HrRfc1522Encode
 | ||
| // --------------------------------------------------------------------------------
 | ||
| MIMEOLEAPI MimeOleRfc1522Encode(
 | ||
|             /* in */        LPCSTR              pszValue,
 | ||
|             /* in */        HCHARSET            hCharset,
 | ||
|             /* out */       LPSTR               *ppszEncoded)
 | ||
| {
 | ||
|     // Locals
 | ||
|     HRESULT             hr=S_OK;
 | ||
|     LPINETCSETINFO      pCharset;
 | ||
|     MIMEVARIANT         rSource;
 | ||
|     MIMEVARIANT         rDest;
 | ||
|     CODEPAGEID          cpiSource;
 | ||
|     CODEPAGEID          cpiDest;
 | ||
| 
 | ||
|     // Invalid Arg
 | ||
|     if (NULL == pszValue || NULL == hCharset || NULL == ppszEncoded)
 | ||
|         return TrapError(E_INVALIDARG);
 | ||
| 
 | ||
|     // Init rDest
 | ||
|     ZeroMemory(&rDest, sizeof(MIMEVARIANT));
 | ||
| 
 | ||
|     // Setup rSource
 | ||
|     rSource.type = MVT_STRINGA;
 | ||
|     rSource.rStringA.pszVal = (LPSTR)pszValue;
 | ||
|     rSource.rStringA.cchVal = lstrlen(pszValue);
 | ||
| 
 | ||
|     // Open the Character Set
 | ||
|     CHECKHR(hr = g_pInternat->HrOpenCharset(hCharset, &pCharset));
 | ||
| 
 | ||
|     // Setup rDest
 | ||
|     rDest.type = MVT_STRINGA;
 | ||
| 
 | ||
|     // Convert the String
 | ||
|     CHECKHR(hr = g_pInternat->HrConvertString(pCharset->cpiWindows, pCharset->cpiInternet, &rSource, &rDest));
 | ||
| 
 | ||
|     // Setup Source and Dest Charset
 | ||
|     cpiSource = pCharset->cpiWindows;
 | ||
|     cpiDest = pCharset->cpiInternet;
 | ||
| 
 | ||
|     // Adjust the Codepages
 | ||
|     CHECKHR(hr = g_pInternat->HrValidateCodepages(&rSource, &rDest, NULL, NULL, &cpiSource, &cpiDest));
 | ||
| 
 | ||
|     // 1522 Encode this dude
 | ||
|     CHECKHR(hr = HrRfc1522Encode(&rSource, &rDest, cpiSource, cpiDest, pCharset->szName, ppszEncoded));
 | ||
| 
 | ||
| exit:
 | ||
|     // Cleanup
 | ||
|     MimeVariantFree(&rDest);
 | ||
| 
 | ||
|     // Done
 | ||
|     return hr;
 | ||
| }
 | ||
| 
 | ||
| // --------------------------------------------------------------------------------
 | ||
| // HrRfc1522Encode
 | ||
| // --------------------------------------------------------------------------------
 | ||
| HRESULT HrRfc1522Encode(LPMIMEVARIANT pSource, LPMIMEVARIANT pDest, CODEPAGEID cpiSource,
 | ||
|     CODEPAGEID cpiDest, LPCSTR pszCharset, LPSTR *ppszEncoded)
 | ||
| {
 | ||
|     // Locals
 | ||
|     HRESULT         hr=S_OK;
 | ||
|     CByteStream     cStream;
 | ||
|     CHAR            chEncoding;
 | ||
|     ULONG           cExtended=0;
 | ||
|     LPBYTE          pb;
 | ||
|     ULONG           cb;
 | ||
|     ULONG           i=0;
 | ||
|     ULONG           cbFirstTry;
 | ||
|     ULONG           cbRead;
 | ||
|     BLOB            rBlobSource;
 | ||
|     BLOB            rBlobCset;
 | ||
|     ULONG           cTrys;
 | ||
|     CHAR            szEncoding[1];
 | ||
|     ULONG           iBefore;
 | ||
|     ULONG           iAfter;
 | ||
|     ULONG           cbExtra;
 | ||
|     ULARGE_INTEGER  uli;
 | ||
|     LARGE_INTEGER   li;
 | ||
|     //IStream        *pStream=NULL;
 | ||
| 
 | ||
|     // Invalid Arg
 | ||
|     Assert(pSource && pDest && pszCharset && ppszEncoded);
 | ||
|     Assert(MVT_STRINGW == pSource->type ? CP_UNICODE == cpiSource : CP_UNICODE != cpiSource);
 | ||
|     Assert(cpiDest != CP_UNICODE && MVT_STRINGA == pDest->type);
 | ||
| 
 | ||
|     // Init
 | ||
|     *ppszEncoded = NULL;
 | ||
|     uli.HighPart = 0;
 | ||
|     li.HighPart = 0;
 | ||
|     rBlobCset.pBlobData = NULL;
 | ||
| 
 | ||
|     // Raid-50014: Will will always rfc1522 encode utf encodings
 | ||
|     // Raid-50788: Cannot post UTF news messages
 | ||
|     if (MVT_STRINGW != pSource->type)
 | ||
|     {
 | ||
|         // If it does not contain 8bit, then no rfc1522 encoding is needed
 | ||
|         if (FALSE == FContainsExtended(&pSource->rStringA, &cExtended))
 | ||
|         {
 | ||
|             hr = E_FAIL;
 | ||
|             goto exit;
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     // We should be converting to UTF...
 | ||
|     else
 | ||
|     {
 | ||
|         // Must be encoding into utf
 | ||
|         Assert(65000 == cpiDest || 65001 == cpiDest);
 | ||
| 
 | ||
|         // If it does not contain 8bit, then no rfc1522 encoding is needed
 | ||
|         if (FALSE == FContainsExtendedW(&pSource->rStringW, &cExtended))
 | ||
|         {
 | ||
|             hr = E_FAIL;
 | ||
|             goto exit;
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     // Create a Stream
 | ||
|     // CHECKHR(hr = CreateStreamOnHGlobal(NULL, TRUE, &pStream));
 | ||
| 
 | ||
|     // Compute Encoding...
 | ||
|     chEncoding = (((cExtended * 100) / pSource->rStringA.cchVal) >= 17) ? 'B' : 'Q';
 | ||
| 
 | ||
|     // RAID-21673: If DBCS source codepage, then, encode the entire string so that I don't fragment the character set encoding.
 | ||
|     if (IsDBCSCodePage(cpiSource))
 | ||
|         chEncoding = 'B';
 | ||
| 
 | ||
|     // Set szEncoding
 | ||
|     szEncoding[0] = chEncoding;
 | ||
| 
 | ||
|     // Setup Encoding Loop
 | ||
|     pb = (MVT_STRINGW == pSource->type) ? (LPBYTE)pSource->rStringW.pszVal : (LPBYTE)pSource->rStringA.pszVal;
 | ||
|     cb = (MVT_STRINGW == pSource->type) ? (pSource->rStringW.cchVal * sizeof(WCHAR)) : pSource->rStringA.cchVal;
 | ||
| 
 | ||
|     // Adjust pszCharset
 | ||
|     if (CP_JAUTODETECT == cpiDest || 50222 == cpiDest || 50221 == cpiDest)
 | ||
|         pszCharset = (LPSTR)c_szISO2022JP;
 | ||
| 
 | ||
|     // Compute cbExtra - =?cchCharset?1?<cbFirstTry>?=
 | ||
|     cbExtra = 2 + lstrlen(pszCharset) + 3 + 2;
 | ||
| 
 | ||
|     // Compute cbFirstTry
 | ||
|     cbFirstTry = 76 - cbExtra;
 | ||
| 
 | ||
|     // cbFirstTry needs to be even if we are encoding unicode
 | ||
|     cbFirstTry -= (cbFirstTry % 2);    
 | ||
| 
 | ||
|     // Adjust cbFirstTry if its greater than cb
 | ||
|     cbFirstTry = min(cb, cbFirstTry);
 | ||
| 
 | ||
|     // Make sure cbFirstTry is even ... That's a good starting point,
 | ||
|     // but we need to make sure that we're not breaking on a leading byte
 | ||
|     // On Korean (and other DBCS locales) we can have a mix of
 | ||
|     // SBCs and MBCs, so just being even isn't enough
 | ||
|     if(pb && (MVT_STRINGW != pSource->type))
 | ||
|     {
 | ||
|         LPCSTR pszEnd = (LPCSTR)&pb[cbFirstTry],
 | ||
|                pszNewEnd = NULL;
 | ||
| 
 | ||
|         pszNewEnd = CharPrevExA((WORD)cpiSource, (LPCSTR)pb, pszEnd, 0);
 | ||
|         if(pszNewEnd && (pszNewEnd == (pszEnd - 1)) && IsDBCSLeadByteEx(cpiSource, *pszNewEnd))
 | ||
|             cbFirstTry-= (ULONG)(pszEnd - pszNewEnd);
 | ||
|     }
 | ||
| 
 | ||
|     // Loop until we have encoded the entire string
 | ||
|     while (i < cb)
 | ||
|     {
 | ||
|         // Write Prefix
 | ||
|         CHECKHR(hr = cStream.Write("=?", 2, NULL));
 | ||
|         CHECKHR(hr = cStream.Write(pszCharset, lstrlen(pszCharset), NULL));
 | ||
|         CHECKHR(hr = cStream.Write("?", 1, NULL));
 | ||
|         CHECKHR(hr = cStream.Write(szEncoding, 1, NULL));
 | ||
|         CHECKHR(hr = cStream.Write("?", 1, NULL));
 | ||
| 
 | ||
|         // Compute Try Amount
 | ||
|         rBlobSource.cbSize = min(cb - i, cbFirstTry);
 | ||
|         rBlobSource.pBlobData = (LPBYTE)(pb + i);
 | ||
| 
 | ||
|         // Get Index
 | ||
|         CHECKHR(hr = HrGetStreamPos(&cStream, &iBefore));
 | ||
| 
 | ||
|         // Encoded blocks until we get one at a good length
 | ||
|         for (cTrys=0;;cTrys++)
 | ||
|         {
 | ||
|             // Too many Trys ?
 | ||
|             if (cTrys > 100)
 | ||
|             {
 | ||
|                 AssertSz(FALSE, "Too many rfc1522 encode buffer reduction attemps, failing (No rfc1522 encoding will be applied).");
 | ||
|                 hr = TrapError(E_FAIL);
 | ||
|                 goto exit;
 | ||
|             }
 | ||
| 
 | ||
|             // Memory Leak
 | ||
|             Assert(NULL == rBlobCset.pBlobData);
 | ||
| 
 | ||
|             // Convert Block
 | ||
|             CHECKHR(hr = g_pInternat->ConvertBuffer(cpiSource, cpiDest, &rBlobSource, &rBlobCset, &cbRead));
 | ||
| 
 | ||
|             // Problem
 | ||
|             if (cbRead == 0)
 | ||
|             {
 | ||
|                 AssertSz(FALSE, "Bad buffer conversion");
 | ||
|                 hr = TrapError(E_FAIL);
 | ||
|                 goto exit;
 | ||
|             }
 | ||
| 
 | ||
|             // Validate
 | ||
|             Assert(cbRead <= rBlobSource.cbSize);
 | ||
| 
 | ||
|             // 'B' Encoding
 | ||
|             if ('B' == chEncoding)
 | ||
|             {
 | ||
|                 // ApplyBase64
 | ||
|                 CHECKHR(hr = HrRfc1522EncodeBase64(rBlobCset.pBlobData, rBlobCset.cbSize, &cStream));
 | ||
|             }
 | ||
|             else
 | ||
|             {
 | ||
|                 // ApplyQP
 | ||
|                 CHECKHR(hr = HrRfc1522EncodeQP(rBlobCset.pBlobData, rBlobCset.cbSize, &cStream));
 | ||
|             }
 | ||
| 
 | ||
|             // Get Index
 | ||
|             CHECKHR(hr = HrGetStreamPos(&cStream, &iAfter));
 | ||
| 
 | ||
|             // Validate
 | ||
|             Assert(iAfter > iBefore);
 | ||
| 
 | ||
|             // Too big ?
 | ||
|             if ((iAfter - iBefore) + cbExtra <= 76)
 | ||
|                 break;
 | ||
| 
 | ||
|             // Problem
 | ||
|             if (rBlobSource.cbSize <= 5)
 | ||
|             {
 | ||
|                 Assert(FALSE);
 | ||
|                 hr = TrapError(E_FAIL);
 | ||
|                 goto exit;
 | ||
|             }
 | ||
| 
 | ||
|             // Cleanup
 | ||
|             SafeMemFree(rBlobCset.pBlobData);
 | ||
| 
 | ||
|             // Seek Back to iBefore
 | ||
|             uli.LowPart = iBefore;
 | ||
|             cStream.SetSize(uli);
 | ||
| 
 | ||
|             // Seek Backwards
 | ||
|             li.LowPart = iBefore;
 | ||
|             cStream.Seek(li, STREAM_SEEK_SET, NULL);
 | ||
| 
 | ||
|             // Compute Inflation Rate
 | ||
|             if (0 == cTrys)
 | ||
|                 rBlobSource.cbSize = (((76 - cbExtra) * rBlobSource.cbSize) / (iAfter - iBefore));
 | ||
| 
 | ||
|             // Otherwise, start taking off 5 bytes
 | ||
|             else
 | ||
|                 rBlobSource.cbSize -= 5;
 | ||
| 
 | ||
|             // Make sure it is even ... That's a good starting point,
 | ||
|             // but we need to make sure that we're not breaking on a leading byte
 | ||
|             // On Korean (and other DBCS locales) we can have a mix of
 | ||
|             // SBCs and MBCs, so just being even isn't enough
 | ||
|             rBlobSource.cbSize -= (rBlobSource.cbSize % 2);
 | ||
|             if(rBlobSource.pBlobData && (MVT_STRINGW != pSource->type))
 | ||
|             {
 | ||
|                 LPCSTR pszEnd = (LPCSTR)&rBlobSource.pBlobData[rBlobSource.cbSize],
 | ||
|                        pszNewEnd = NULL;
 | ||
| 
 | ||
|                 pszNewEnd = CharPrevExA((WORD)cpiSource, (LPCSTR)rBlobSource.pBlobData, pszEnd, 0);
 | ||
|                 if(pszNewEnd && (pszNewEnd == (pszEnd - 1)) && IsDBCSLeadByteEx(cpiSource, *pszNewEnd))
 | ||
|                     rBlobSource.cbSize-= (ULONG)(pszEnd - pszNewEnd);
 | ||
|             }
 | ||
| 
 | ||
|             // Should be less than cb
 | ||
|             Assert(rBlobSource.cbSize < cb);
 | ||
|         }
 | ||
| 
 | ||
|         // Write termination
 | ||
|         CHECKHR(hr = cStream.Write("?=", 2, NULL));
 | ||
| 
 | ||
|         // Increment i
 | ||
|         i += cbRead;
 | ||
| 
 | ||
|         // Cleanup
 | ||
|         SafeMemFree(rBlobCset.pBlobData);
 | ||
| 
 | ||
|         // Write folding
 | ||
|         if (i < cb)
 | ||
|         {
 | ||
|             // Write Fold
 | ||
|             CHECKHR(hr = cStream.Write(c_szCRLFTab, lstrlen(c_szCRLFTab), NULL));
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     // Return the encoded string
 | ||
|     CHECKHR(hr = cStream.HrAcquireStringA(&cb, ppszEncoded, ACQ_DISPLACE));
 | ||
|     //cStream.Commit(STGC_DEFAULT);
 | ||
|     //CHECKALLOC(*ppszEncoded = PszFromANSIStreamA(pStream));
 | ||
| 
 | ||
| exit:
 | ||
|     // Cleanup
 | ||
|     //ReleaseObj(pStream);
 | ||
|     g_pMalloc->Free(rBlobCset.pBlobData);
 | ||
| 
 | ||
|     // Done
 | ||
|     return hr;
 | ||
| }
 | ||
| 
 | ||
| // --------------------------------------------------------------------------------
 | ||
| // MimeOleRfc1522Decode
 | ||
| // --------------------------------------------------------------------------------
 | ||
| MIMEOLEAPI MimeOleRfc1522Decode(LPCSTR pszValue, LPSTR pszCharset, ULONG cchmax, LPSTR *ppszDecoded)
 | ||
| {
 | ||
|     // Locals
 | ||
|     HRESULT             hrEncoded=E_FAIL,
 | ||
|                         hrCsetFound=E_FAIL;
 | ||
|     RFC1522OUT          rOut;
 | ||
|     LPSTR               psz=(LPSTR)pszValue,
 | ||
|                         pszNew;
 | ||
|     CHAR                szCset[CCHMAX_CSET_NAME],
 | ||
|                         chEncoding;
 | ||
|     BOOL                bNonBlankLeading;
 | ||
| 
 | ||
|     // check params
 | ||
|     if (NULL == pszValue)
 | ||
|         return TrapError(E_INVALIDARG);
 | ||
| 
 | ||
|     // Init out structure
 | ||
|     ZeroMemory(&rOut, sizeof(RFC1522OUT));
 | ||
| 
 | ||
|     // Save data..
 | ||
|     if (ppszDecoded)
 | ||
|         rOut.fWrite = TRUE;
 | ||
| 
 | ||
|     // Start decoding loop...
 | ||
|     while(psz && *psz)
 | ||
|     {
 | ||
|         // Seek to start of 1522 encoding...
 | ||
|         pszNew = PszRfc1522Find(psz, &bNonBlankLeading);
 | ||
|         Assert(pszNew!=NULL);
 | ||
| 
 | ||
|         if (bNonBlankLeading || psz == pszValue || !*psz)
 | ||
|         {
 | ||
|             // Either we found non-blank characters before the word,
 | ||
|             // or this is the first word on the line, or we didn't
 | ||
|             // find a word.  Whatever, we need to write all of the
 | ||
|             // data before the word.
 | ||
|             if (FAILED(HrRfc1522WriteStr(&rOut, psz, (LONG) (pszNew-psz))))
 | ||
|             {
 | ||
|                 break;
 | ||
|             }
 | ||
|         }
 | ||
|         // If didn't find start.. were done
 | ||
|         if (!*pszNew)
 | ||
|             break;
 | ||
| 
 | ||
|         // Set psz to new position
 | ||
|         psz = pszNew;
 | ||
| 
 | ||
|         // Get charset
 | ||
|         pszNew = PszRfc1522GetCset(psz, szCset, ARRAYSIZE(szCset));
 | ||
| 
 | ||
|         // If didn't parse charset correctly, continue
 | ||
|         if (NULL == pszNew)
 | ||
|         {
 | ||
|             psz++;
 | ||
|             continue;
 | ||
|         }
 | ||
| 
 | ||
|         // Character set was found
 | ||
|         hrCsetFound = S_OK;
 | ||
|         
 | ||
|         // Was caller just looking for the charset...
 | ||
|         if (NULL == ppszDecoded)
 | ||
|             break;
 | ||
| 
 | ||
|         // Otherwise, parse encoding
 | ||
|         pszNew = PszRfc1522GetEncoding(pszNew, &chEncoding);
 | ||
| 
 | ||
|         // If didn't parse charset correctly, continue
 | ||
|         if (NULL == pszNew)
 | ||
|         {
 | ||
|             psz++;
 | ||
|             continue;
 | ||
|         }
 | ||
| 
 | ||
|         // Decode the text to the end - THIS SHOULD NEVER FAIL...
 | ||
|         psz = PszRfc1522Decode(pszNew, chEncoding, &rOut);
 | ||
| 
 | ||
|         // It is a valid encoded string
 | ||
|         if (psz)
 | ||
|             hrEncoded = S_OK;
 | ||
|     }
 | ||
| 
 | ||
|     // Were we actually decoding...
 | ||
|     if (ppszDecoded && hrEncoded == S_OK)
 | ||
|     {
 | ||
|         // Commit the stream
 | ||
|         if (FAILED(HrRfc1522WriteDone(&rOut, ppszDecoded)))
 | ||
|         {
 | ||
|             *ppszDecoded = NULL;
 | ||
|             hrEncoded = S_FALSE;
 | ||
|         }
 | ||
|     }
 | ||
| 
 | ||
|     // Otherwise, return charset...
 | ||
|     if (pszCharset && hrCsetFound == S_OK)
 | ||
|         lstrcpyn(pszCharset, szCset, cchmax);
 | ||
| 
 | ||
|     // Cleanup
 | ||
|     SafeRelease(rOut.pstm);
 | ||
| 
 | ||
|     // Done
 | ||
|     return ppszDecoded ? hrEncoded : hrCsetFound;
 | ||
| }
 |