// -------------------------------------------------------------------------------- // Contain.cpp // Copyright (c)1993-1995 Microsoft Corporation, All Rights Reserved // -------------------------------------------------------------------------------- #include "pch.hxx" #include "containx.h" #include "internat.h" #include "inetstm.h" #include "dllmain.h" #include "olealloc.h" #include "objheap.h" #include "vstream.h" #include "addparse.h" #include "enumhead.h" #include "addrenum.h" #include "stackstr.h" #include "stmlock.h" #include "enumprop.h" #include "smime.h" #ifndef WIN16 #include "wchar.h" #endif // !WIN16 #include "symcache.h" #ifdef MAC #include #endif // MAC #include "mimeapi.h" #ifndef MAC #include #endif // !MAC #include "demand.h" #include "mimeutil.h" //#define TRACEPARSE 1 // -------------------------------------------------------------------------------- // Hash Table Stats // -------------------------------------------------------------------------------- #ifdef DEBUG DWORD g_cSetPidLookups = 0; DWORD g_cHashLookups = 0; DWORD g_cHashInserts = 0; DWORD g_cHashCollides = 0; #endif // -------------------------------------------------------------------------------- // Default Header Options // -------------------------------------------------------------------------------- const HEADOPTIONS g_rDefHeadOptions = { NULL, // hCharset DEF_CBMAX_HEADER_LINE, // OID_CBMAX_HEADER_LINE DEF_ALLOW_8BIT_HEADER, // OID_ALLOW_8BIT_HEADER DEF_SAVE_FORMAT, // OID_SAVE_FORMAT DEF_NO_DEFAULT_CNTTYPE, // OID_NO_DEFAULT_CNTTYPE DEF_HEADER_RELOAD_TYPE_PROPSET // OID_HEADER_REALOD_TYPE }; // -------------------------------------------------------------------------------- // ENCODINGTABLE // -------------------------------------------------------------------------------- const ENCODINGTABLE g_rgEncoding[] = { { STR_ENC_7BIT, IET_7BIT }, { STR_ENC_QP, IET_QP }, { STR_ENC_BASE64, IET_BASE64 }, { STR_ENC_UUENCODE, IET_UUENCODE }, { STR_ENC_XUUENCODE, IET_UUENCODE }, { STR_ENC_XUUE, IET_UUENCODE }, { STR_ENC_8BIT, IET_8BIT }, { STR_ENC_BINARY, IET_BINARY }, { STR_ENC_BINHEX40, IET_BINHEX40 } }; // -------------------------------------------------------------------------------- // CMimePropertyContainer::CMimePropertyContainer // -------------------------------------------------------------------------------- CMimePropertyContainer::CMimePropertyContainer(void) { // Basic Stuff m_cRef = 1; m_dwState = 0; m_cProps = 0; m_wTag = 0; m_cbSize = 0; m_cbStart = 0; m_pStmLock = NULL; // Default Options CopyMemory(&m_rOptions, &g_rDefHeadOptions, sizeof(HEADOPTIONS)); m_rOptions.pDefaultCharset = CIntlGlobals::GetDefHeadCset(); // Address Table ZeroMemory(&m_rAdrTable, sizeof(ADDRESSTABLE)); // Header Table ZeroMemory(&m_rHdrTable, sizeof(HEADERTABLE)); // Dispatch Call Stack ZeroMemory(&m_rTrigger, sizeof(TRIGGERCALLSTACK)); // Property Indexes ZeroMemory(m_prgIndex, sizeof(m_prgIndex)); ZeroMemory(m_prgHashTable, sizeof(m_prgHashTable)); // Thread Safety InitializeCriticalSection(&m_cs); } // -------------------------------------------------------------------------------- // CMimePropertyContainer::~CMimePropertyContainer // -------------------------------------------------------------------------------- CMimePropertyContainer::~CMimePropertyContainer(void) { // I better not have any dispatch calls on the stack Assert(m_rTrigger.cCalls == 0); // Free Hash Table _FreeHashTableElements(); // Free the Address Table SafeMemFree(m_rAdrTable.prgpAdr); // Free the Header Table SafeMemFree(m_rHdrTable.prgpRow); // Release Stream SafeRelease(m_pStmLock); // Delete CS DeleteCriticalSection(&m_cs); } // -------------------------------------------------------------------------------- // CMimePropertyContainer::QueryInterface // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::QueryInterface(REFIID riid, LPVOID *ppv) { // Locals HRESULT hr=S_OK; // check params if (ppv == NULL) return TrapError(E_INVALIDARG); // Find IID if (IID_IUnknown == riid) *ppv = (IUnknown *)(IMimePropertySet *)this; else if (IID_IPersist == riid) *ppv = (IPersist *)(IMimePropertySet *)this; else if (IID_IPersistStreamInit == riid) *ppv = (IPersistStreamInit *)this; else if (IID_IMimePropertySet == riid) *ppv = (IMimePropertySet *)this; else if (IID_IMimeHeaderTable == riid) *ppv = (IMimeHeaderTable *)this; else if (IID_IMimeAddressTable == riid) *ppv = (IMimeAddressTable *)this; else if (IID_IMimeAddressTableW == riid) *ppv = (IMimeAddressTableW *)this; else if (IID_CMimePropertyContainer == riid) *ppv = (CMimePropertyContainer *)this; else { *ppv = NULL; hr = TrapError(E_NOINTERFACE); goto exit; } // AddRef It ((IUnknown *)*ppv)->AddRef(); exit: // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::AddRef // -------------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CMimePropertyContainer::AddRef(void) { return InterlockedIncrement(&m_cRef); } // -------------------------------------------------------------------------------- // CMimePropertyContainer::Release // -------------------------------------------------------------------------------- STDMETHODIMP_(ULONG) CMimePropertyContainer::Release(void) { LONG cRef = InterlockedDecrement(&m_cRef); if (0 == cRef) delete this; return (ULONG)cRef; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::IsState // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::IsState(DWORD dwState) { EnterCriticalSection(&m_cs); HRESULT hr = (ISFLAGSET(m_dwState, dwState)) ? S_OK : S_FALSE; LeaveCriticalSection(&m_cs); return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::ClearState // -------------------------------------------------------------------------------- void CMimePropertyContainer::ClearState(DWORD dwState) { EnterCriticalSection(&m_cs); FLAGCLEAR(m_dwState, dwState); LeaveCriticalSection(&m_cs); } // -------------------------------------------------------------------------------- // CMimePropertyContainer::DwGetState // -------------------------------------------------------------------------------- DWORD CMimePropertyContainer::DwGetState(LPDWORD pdwState) { Assert(pdwState); EnterCriticalSection(&m_cs); DWORD dw = m_dwState; LeaveCriticalSection(&m_cs); return dw; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::SetState // -------------------------------------------------------------------------------- void CMimePropertyContainer::SetState(DWORD dwState) { EnterCriticalSection(&m_cs); FLAGSET(m_dwState, dwState); LeaveCriticalSection(&m_cs); } // -------------------------------------------------------------------------------- // CMimePropertyContainer::GetClassID // -------------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::GetClassID(CLSID *pClassID) { // Copy Class Id CopyMemory(pClassID, &IID_IMimePropertySet, sizeof(CLSID)); // Done return S_OK; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::GetSizeMax // -------------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::GetSizeMax(ULARGE_INTEGER *pcbSize) { // Locals HRESULT hr=S_OK; IStream *pStream=NULL; ULONG cbSize; // Invalid Arg if (NULL == pcbSize) return TrapError(E_INVALIDARG); // Thread Safety EnterCriticalSection(&m_cs); // If Dirty if (ISFLAGSET(m_dwState, COSTATE_DIRTY)) { // Create temp stream CHECKALLOC(pStream = new CVirtualStream); // Commit CHECKHR(hr = Save(pStream, FALSE)); // Get the Stream Size CHECKHR(hr = HrGetStreamSize(pStream, &cbSize)); } // Otherwise, m_cbSize should be set to current size else cbSize = m_cbSize; // Return the Size #ifdef MAC ULISet32(*pcbSize, cbSize); #else // !MAC pcbSize->QuadPart = cbSize; #endif // MAC exit: // Cleanup SafeRelease(pStream); // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::IsDirty // -------------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::IsDirty(void) { EnterCriticalSection(&m_cs); HRESULT hr = (ISFLAGSET(m_dwState, COSTATE_DIRTY)) ? S_OK : S_FALSE; LeaveCriticalSection(&m_cs); return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_ReloadInitNew // -------------------------------------------------------------------------------- void CMimePropertyContainer::_ReloadInitNew(void) { // Handle all reload types switch(m_rOptions.ReloadType) { // Default behavior is no InitNew case RELOAD_HEADER_NONE: return; // InitNew everytime Load is called case RELOAD_HEADER_RESET: InitNew(); break; // Merge or replace headers case RELOAD_HEADER_APPEND: SafeRelease(m_pStmLock); break; case RELOAD_HEADER_REPLACE: SafeRelease(m_pStmLock); _SetStateOnAllProps(PRSTATE_EXIST_BEFORE_LOAD); break; } } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_SetStateOnAllProps (only first level properties) // -------------------------------------------------------------------------------- void CMimePropertyContainer::_SetStateOnAllProps(DWORD dwState) { // Locals ULONG i; LPPROPERTY pProperty; // Do I have any groups if (0 == m_cProps) return; // Loop through the item table for (i=0; idwState, dwState); // Goto Next pProperty = pProperty->pNextHash; } } } // -------------------------------------------------------------------------------- // CMimePropertyContainer::InitNew // -------------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::InitNew(void) { // Thread Safety EnterCriticalSection(&m_cs); // No dispatchs better be out... Assert(m_rTrigger.cCalls == 0); // Free the PropTable _FreeHashTableElements(); // Release the Stream SafeRelease(m_pStmLock); // Reset m_wTag m_wTag = LOWORD(GetTickCount()); while(m_wTag == 0 || m_wTag == 0xffff) m_wTag++; // Clear State m_dwState = 0; m_cbSize = 0; m_cbStart = 0; // Thread Safety LeaveCriticalSection(&m_cs); // Done return S_OK; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::Load // -------------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::Load(IStream *pStream) { // Locals HRESULT hr=S_OK; CStreamLockBytes *pStmLock=NULL; CInternetStream cInternet; ULONG cbOffset; // check params if (NULL == pStream) return TrapError(E_INVALIDARG); // Wrap pStream in a pStmLock CHECKALLOC(pStmLock = new CStreamLockBytes(pStream)); // Get Current Stream Position CHECKHR(hr = HrGetStreamPos(pStream, &cbOffset)); // Create text stream object cInternet.InitNew(cbOffset, pStmLock); // Load from text stream object CHECKHR(hr = Load(&cInternet)); exit: // Cleanup SafeRelease(pStmLock); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::Load // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::Load(CInternetStream *pInternet) { // Locals HRESULT hr=S_OK; ULONG cbData, cbStart, cboffStart, cboffEnd; LONG cboffColon; LPSTR psz; DWORD dwRowNumber=1; LPPROPSYMBOL pSymbol; LPPROPERTY pProperty; MIMEVARIANT rValue; PROPSTRINGA rHeader; // Thread Safety EnterCriticalSection(&m_cs); // Get Starting Position m_cbStart = pInternet->DwGetOffset(); // Initialize the PropValue rValue.type = MVT_STRINGA; // Reload InitNewType _ReloadInitNew(); // Read Headers into Rows while(1) { // Mark Start of this header cboffStart = pInternet->DwGetOffset(); // Read header line... CHECKHR(hr = pInternet->HrReadHeaderLine(&rHeader, &cboffColon)); Assert(ISVALIDSTRINGA(&rHeader)); // Are we done - empty line signals end of header if (*rHeader.pszVal == '\0') { // Compute Header Size m_cbSize = (LONG)(pInternet->DwGetOffset() - m_cbStart); // Done break; } // If no colon found if (-1 == cboffColon) { // Use the Illegal Symbol pSymbol = SYM_ATT_ILLEGAL; } // Otherwise... else { // Skip whitespace cbData = cboffColon; psz = rHeader.pszVal; #if 0 while(*psz && (*psz == ' ' || *psz == '\t')) { cbData--; psz++; } #endif // Save Header Name Assert(rHeader.pszVal[cboffColon] == ':'); *(rHeader.pszVal + cboffColon) = '\0'; // Find Global Property hr = g_pSymCache->HrOpenSymbol(rHeader.pszVal, TRUE, &pSymbol); // Replace the colon *(rHeader.pszVal + cboffColon) = ':'; // Bad Header Name or Failure if (FAILED(hr)) { // Unknown Failure if (MIME_E_INVALID_HEADER_NAME != hr) { TrapError(hr); goto exit; } // Use the Illegal Symbol pSymbol = SYM_ATT_ILLEGAL; // Were are S_OK hr = S_OK; } } // Assert pSymbol Assert(pSymbol); // If Not Illegal if (PID_ATT_ILLEGAL == pSymbol->dwPropId) { cbData = rHeader.cchVal; psz = rHeader.pszVal; cboffColon = 0; } // Otherwise else { // We better have a symbol Assert(rHeader.pszVal[cboffColon] == ':'); // Step over space between colon and first character cbData = (rHeader.cchVal - cboffColon - 1); psz = rHeader.pszVal + cboffColon + 1; if (*psz == ' ' || *psz == '\t') { cbData--; psz++; } } // Invalid Arg Assert(psz && psz[cbData] == '\0'); // Append a Property if (RELOAD_HEADER_REPLACE == m_rOptions.ReloadType) { // Dos the property pSymbol already exist? if (SUCCEEDED(_HrFindProperty(pSymbol, &pProperty))) { // Did the property exist before the load if (ISFLAGSET(pProperty->dwState, PRSTATE_EXIST_BEFORE_LOAD)) { // Delete the Property SideAssert(SUCCEEDED(DeleteProp(pSymbol))); } } } // Simply append any existing property CHECKHR(hr = _HrAppendProperty(pSymbol, &pProperty)); // Setup Property Value rValue.rStringA.pszVal = psz; rValue.rStringA.cchVal = cbData; // Store the data on the property CHECKHR(hr = _HrSetPropertyValue(pProperty, PDF_ENCODED, &rValue, FALSE)); // Still Trying to detect a character set... if (!ISFLAGSET(m_dwState, COSTATE_CSETTAGGED) && PID_ATT_ILLEGAL != pSymbol->dwPropId) { // Content-Type charset=xxx if (PID_HDR_CNTTYPE == pSymbol->dwPropId && NULL != m_prgIndex[PID_PAR_CHARSET]) { // Locals LPPROPERTY pProperty; LPINETCSETINFO pCharset; // Did we have a charset=xxxx yet pProperty = m_prgIndex[PID_PAR_CHARSET]; // Make sure it is a valid string property Assert(ISSTRINGA(&pProperty->rValue)); // Get charset handle... if (SUCCEEDED(g_pInternat->HrOpenCharset(pProperty->rValue.rStringA.pszVal, &pCharset))) { // We are tagged FLAGSET(m_dwState, COSTATE_CSETTAGGED); // Save the charset m_rOptions.pDefaultCharset = pCharset; } } // Otherwise, is the property encoded in an rfc1522 charset? else if (!ISFLAGSET(m_dwState, COSTATE_1522CSETTAG) && pProperty->pCharset) { // The header is tagged with a 1522 charset FLAGSET(m_dwState, COSTATE_1522CSETTAG); // Assume that charset m_rOptions.pDefaultCharset = pProperty->pCharset; } } // Set The Row Number pProperty->dwRowNumber = dwRowNumber++; // Set Start Offset pProperty->cboffStart = cboffStart; // Map cboffColon from Line to Stream offset pProperty->cboffColon = cboffColon + pProperty->cboffStart; // Save cbOffEnd pProperty->cboffEnd = pInternet->DwGetOffset(); } // Save the Stream Assert(NULL == m_pStmLock); // Get the stream object from the text stream pInternet->GetLockBytes(&m_pStmLock); // If not character set tagged if (!ISFLAGSET(m_dwState, COSTATE_CSETTAGGED)) { // If not tagged with a 1522 charset, use the default if (!ISFLAGSET(m_dwState, COSTATE_1522CSETTAG) && CIntlGlobals::GetDefHeadCset()) { // Assume the Default character Set m_rOptions.pDefaultCharset = CIntlGlobals::GetDefHeadCset(); } // Lookup Charset Info if (m_rOptions.pDefaultCharset) { // Locals MIMEVARIANT rValue; // Setup Variant rValue.type = MVT_STRINGA; rValue.rStringA.pszVal = m_rOptions.pDefaultCharset->szName; rValue.rStringA.cchVal = lstrlen(m_rOptions.pDefaultCharset->szName); // Set the Charset Attribute SideAssert(SUCCEEDED(SetProp(SYM_PAR_CHARSET, 0, &rValue))); } } // We better have a charset Assert(m_rOptions.pDefaultCharset); // Make sure we are not dirty FLAGCLEAR(m_dwState, COSTATE_DIRTY); // Any Illegal lines found ? hr = (NULL == m_prgIndex[PID_ATT_ILLEGAL]) ? S_OK : MIME_S_ILLEGAL_LINES_FOUND; exit: // Failure if (FAILED(hr)) InitNew(); // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_HrGetHeaderTableSaveIndex // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::_HrGetHeaderTableSaveIndex(ULONG *pcRows, LPROWINDEX *pprgIndex) { // Locals HRESULT hr=S_OK; ULONG i; LPPROPERTY pRow; ULONG cRows=0; LPROWINDEX prgIndex=NULL; ULONG cSymbols=g_pSymCache->GetCount(); DWORD dwMaxRow=0; // Invalid Arg Assert(pcRows && pprgIndex); // Init Row Count *pcRows = 0; *pprgIndex = NULL; // Allocate pprgdwIndex based on m_rHdrTable.cRows (this is the max) CHECKALLOC(prgIndex = (LPROWINDEX)g_pMalloc->Alloc(sizeof(ROWINDEX) * m_rHdrTable.cRows)); // Zero ZeroMemory(prgIndex, sizeof(ROWINDEX) * m_rHdrTable.cRows); // I need to find the larged pProperty->dwRowNumber so that I can order the rows better for (i=0; idwState, PRSTATE_USERSETROWNUM)) if (m_rHdrTable.prgpRow[i]->dwRowNumber > dwMaxRow) dwMaxRow = m_rHdrTable.prgpRow[i]->dwRowNumber; } // Compute Position Weight for all items in the table for (i=0; ipSymbol->dwFlags, MPF_MIME)) continue; // Init dwPosWeight prgIndex[cRows].dwWeight = 0; prgIndex[cRows].hRow = pRow->hRow; // Unknonw Row Number if (0 == pRow->dwRowNumber) { // Compute the Row Weigth Assert(pRow->pSymbol->dwRowNumber != 0); prgIndex[cRows].dwWeight = (ULONG)((pRow->pSymbol->dwRowNumber * 1000) / m_rHdrTable.cRows); } // User set the row number else if (ISFLAGSET(pRow->dwState, PRSTATE_USERSETROWNUM)) { // Compute the Row Weigth prgIndex[cRows].dwWeight = (ULONG)((pRow->dwRowNumber * 1000) / m_rHdrTable.cRows); } // Otheriwse, this row number have been in the original row set from ::Load else if (dwMaxRow > 0) { // Weight within original row set DWORD dw1 = (DWORD)((pRow->dwRowNumber * 100) / dwMaxRow); // Compute global symbol row number DWORD dwRow = (DWORD)((float)((float)dw1 / (float)100) * cSymbols); // Compute the Row Weigth prgIndex[cRows].dwWeight = (ULONG)((dwRow * 1000) / m_rHdrTable.cRows); } // Increment Row Count cRows++; } // Set the Sort Order Index of all the rows if (cRows > 0) _SortHeaderTableSaveIndex(0, cRows - 1, prgIndex); // Return the Index *pprgIndex = prgIndex; *pcRows = cRows; prgIndex = NULL; exit: // Cleanup SafeMemFree(prgIndex); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_SortHeaderTableSaveIndex // -------------------------------------------------------------------------------- void CMimePropertyContainer::_SortHeaderTableSaveIndex(LONG left, LONG right, LPROWINDEX prgIndex) { // Locals register long i, j; ROWINDEX k, temp; i = left; j = right; CopyMemory(&k, &prgIndex[(i + j) / 2], sizeof(ROWINDEX)); do { while(prgIndex[i].dwWeight < k.dwWeight && i < right) i++; while (prgIndex[j].dwWeight > k.dwWeight && j > left) j--; if (i <= j) { CopyMemory(&temp, &prgIndex[i], sizeof(ROWINDEX)); CopyMemory(&prgIndex[i], &prgIndex[j], sizeof(ROWINDEX)); CopyMemory(&prgIndex[j], &temp, sizeof(ROWINDEX)); i++; j--; } } while (i <= j); if (left < j) _SortHeaderTableSaveIndex(left, j, prgIndex); if (i < right) _SortHeaderTableSaveIndex(i, right, prgIndex); } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_FIsValidHAddress // -------------------------------------------------------------------------------- BOOL CMimePropertyContainer::_FIsValidHAddress(HADDRESS hAddress) { // Invalid Sig or index if ((WORD)(HADDRESSTICK(hAddress)) != m_wTag || HADDRESSINDEX(hAddress) >= m_rAdrTable.cAdrs) return FALSE; // Row has been deleted if (NULL == m_rAdrTable.prgpAdr[HADDRESSINDEX(hAddress)]) return FALSE; // Otherwise, its valid return TRUE; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_FIsValidHRow // -------------------------------------------------------------------------------- BOOL CMimePropertyContainer::_FIsValidHRow(HHEADERROW hRow) { // Invalid Sig or index if ((WORD)(HROWTICK(hRow)) != m_wTag || HROWINDEX(hRow) >= m_rHdrTable.cRows) return FALSE; // Row has been deleted if (NULL == m_rHdrTable.prgpRow[HROWINDEX(hRow)]) return FALSE; // Otherwise, its valid return TRUE; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::Save // -------------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::Save(LPSTREAM pStream, BOOL fClearDirty) { // Locals HRESULT hr=S_OK; LPPROPERTY pRow; LPPROPERTY pProperty; ULONG i, j, cbWrote, cRows; MIMEVARIANT rValue; LPROWINDEX prgIndex=NULL; INETCSETINFO rCharset; // Invalid Arg if (NULL == pStream) return TrapError(E_INVALIDARG); // Thread Safety EnterCriticalSection(&m_cs); // There Better be a content type if (FALSE == m_rOptions.fNoDefCntType && NULL == m_prgIndex[PID_HDR_CNTTYPE]) { // Set the content Type SideAssert(SUCCEEDED(SetProp(SYM_HDR_CNTTYPE, STR_MIME_TEXT_PLAIN))); } // Validate the Charset if (m_rOptions.pDefaultCharset) { // Internet Encoded and Windows Encoding are CPI_AUTODETECT if (CP_JAUTODETECT == m_rOptions.pDefaultCharset->cpiInternet || 50222 == m_rOptions.pDefaultCharset->cpiInternet || 50221 == m_rOptions.pDefaultCharset->cpiInternet) { // Only for _autodetect if (CP_JAUTODETECT == m_rOptions.pDefaultCharset->cpiInternet) { // Change Charset SideAssert(SUCCEEDED(g_pInternat->HrOpenCharset(c_szISO2022JP, &m_rOptions.pDefaultCharset))); } // Reset It if (m_prgIndex[PID_PAR_CHARSET]) { // Set the Charset... SideAssert(SUCCEEDED(SetProp(SYM_PAR_CHARSET, c_szISO2022JP))); } } } // This builds an inverted index on the header rows sorted by postion weight CHECKHR(hr = _HrGetHeaderTableSaveIndex(&cRows, &prgIndex)); // Speicify data type rValue.type = MVT_STREAM; rValue.pStream = pStream; // Loop through the rows for (i=0; ipSymbol->dwFlags, MPF_ADDRESS)) { // Loop through remainin items and mark as saved for (j=i+1; jpSymbol->dwAdrType == pRow->pSymbol->dwAdrType) prgIndex[j].fSaved = TRUE; } } #endif } // Make sure we are not dirty if (fClearDirty) FLAGCLEAR(m_dwState, COSTATE_DIRTY); exit: // Cleanup SafeMemFree(prgIndex); // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_FreeHashTableElements // -------------------------------------------------------------------------------- void CMimePropertyContainer::_FreeHashTableElements(void) { // Locals ULONG i; LPPROPERTY pCurrHash, pNextHash; // Do I have any groups if (0 == m_cProps) return; // Loop through the item table for (i=0; ipNextHash; // Release This Chain _FreePropertyChain(pCurrHash); // Goto Next pCurrHash = pNextHash; } // Set to NULL m_prgHashTable[i] = NULL; } } // Empty the arrays ZeroMemory(m_prgIndex, sizeof(m_prgIndex)); // No Groups m_cProps = 0; m_rAdrTable.cAdrs = 0; m_rHdrTable.cRows = 0; m_rHdrTable.cEmpty = 0; m_rAdrTable.cEmpty = 0; } // --------------------------------------------------------------------------- // CMimePropertyContainer::_FreePropertyChain // --------------------------------------------------------------------------- void CMimePropertyContainer::_FreePropertyChain(LPPROPERTY pProperty) { // Locals LPPROPERTY pCurrValue, pNextValue; // Walk this list pCurrValue = pProperty; while(pCurrValue) { // Save Next Item pNextValue = pCurrValue->pNextValue; // Remove from header table if (pCurrValue->hRow) _UnlinkHeaderRow(pCurrValue->hRow); // Remove from address table if (pCurrValue->pGroup) _UnlinkAddressGroup(pCurrValue); // Free this item ObjectHeap_FreeProperty(pCurrValue); // Goto next pCurrValue = pNextValue; } } // --------------------------------------------------------------------------- // CMimePropertyContainer::_UnlinkHeaderRow // --------------------------------------------------------------------------- void CMimePropertyContainer::_UnlinkHeaderRow(HHEADERROW hRow) { // Validate the Handle Assert(_FIsValidHRow(hRow)); // Get the row m_rHdrTable.prgpRow[HROWINDEX(hRow)] = NULL; // Increment Empty Count m_rHdrTable.cEmpty++; } // --------------------------------------------------------------------------- // CMimePropertyContainer::_UnlinkAddressGroup // --------------------------------------------------------------------------- void CMimePropertyContainer::_UnlinkAddressGroup(LPPROPERTY pProperty) { // Invalid Arg Assert(pProperty && pProperty->pGroup); // Free This Address Chain _FreeAddressChain(pProperty->pGroup); // Prepare for unlink LPPROPERTY pNext = pProperty->pGroup->pNext; LPPROPERTY pPrev = pProperty->pGroup->pPrev; // If Previious... if (pPrev) { Assert(pPrev->pGroup); pPrev->pGroup->pNext = pNext; } // If Next if (pNext) { Assert(pNext->pGroup); pNext->pGroup->pPrev = pPrev; } // Was this the header ? if (m_rAdrTable.pHead == pProperty) m_rAdrTable.pHead = pNext; if (m_rAdrTable.pTail == pProperty) m_rAdrTable.pTail = pPrev; // Clear the Group ZeroMemory(pProperty->pGroup, sizeof(ADDRESSGROUP)); } // --------------------------------------------------------------------------- // CMimePropertyContainer::_FreeAddressChain // --------------------------------------------------------------------------- void CMimePropertyContainer::_FreeAddressChain(LPADDRESSGROUP pGroup) { // Locals LPMIMEADDRESS pCurr; LPMIMEADDRESS pNext; // Loop through data structures pCurr = pGroup->pHead; while(pCurr) { // Set Next pNext = pCurr->pNext; // Unlink this address _FreeAddress(pCurr); // Goto Next pCurr = pNext; } // Fixup the Group pGroup->pHead = NULL; pGroup->pTail = NULL; pGroup->cAdrs = 0; } // --------------------------------------------------------------------------- // CMimePropertyContainer::_FreeAddress // --------------------------------------------------------------------------- void CMimePropertyContainer::_FreeAddress(LPMIMEADDRESS pAddress) { // Validate the Handle Assert(_FIsValidHAddress(pAddress->hThis)); // Get the row m_rAdrTable.prgpAdr[HADDRESSINDEX(pAddress->hThis)] = NULL; // Increment Empty Count m_rAdrTable.cEmpty++; // Free pCurr ObjectHeap_FreeAddress(pAddress); } // --------------------------------------------------------------------------- // CMimePropertyContainer::_UnlinkAddress // --------------------------------------------------------------------------- void CMimePropertyContainer::_UnlinkAddress(LPMIMEADDRESS pAddress) { // Invalid Arg Assert(pAddress && pAddress->pGroup); // Prepare for unlink LPMIMEADDRESS pNext = pAddress->pNext; LPMIMEADDRESS pPrev = pAddress->pPrev; // If Previious... if (pPrev) { Assert(pPrev->pGroup && pPrev->pGroup == pAddress->pGroup); pPrev->pNext = pNext; } // If Next if (pNext) { Assert(pNext->pGroup && pNext->pGroup == pAddress->pGroup); pNext->pPrev = pPrev; } // Was this the header ? if (pAddress->pGroup->pHead == pAddress) pAddress->pGroup->pHead = pNext; if (pAddress->pGroup->pTail == pAddress) pAddress->pGroup->pTail = pPrev; // Decrement Group Count pAddress->pGroup->cAdrs--; // Address group is dirty pAddress->pGroup->fDirty = TRUE; // Cleanup pAddress pAddress->pNext = NULL; pAddress->pPrev = NULL; pAddress->pGroup = NULL; } // --------------------------------------------------------------------------- // CMimePropertyContainer::_HrFindFirstProperty // --------------------------------------------------------------------------- HRESULT CMimePropertyContainer::_HrFindFirstProperty(LPFINDPROPERTY pFind, LPPROPERTY *ppProperty) { // Validate pFind Assert(pFind->pszPrefix && pFind->pszName) Assert(pFind->pszPrefix[pFind->cchPrefix] == '\0' && pFind->pszName[pFind->cchName] == '\0'); // Start with first hash table bucket pFind->wHashIndex = 0; // Start with first property in the hash table pFind->pProperty = m_prgHashTable[pFind->wHashIndex]; // Find Next return _HrFindNextProperty(pFind, ppProperty); } // --------------------------------------------------------------------------- // CMimePropertyContainer::_HrFindNextProperty // --------------------------------------------------------------------------- HRESULT CMimePropertyContainer::_HrFindNextProperty(LPFINDPROPERTY pFind, LPPROPERTY *ppProperty) { // Locals HRESULT hr=S_OK; LPPROPSYMBOL pSymbol; // Init *ppProperty = NULL; // Continue walking buckets while (1) { // Continue looping chain while (pFind->pProperty) { // Good Symbol Assert(SUCCEEDED(HrIsValidSymbol(pFind->pProperty->pSymbol))); // Readability pSymbol = pFind->pProperty->pSymbol; // Should I delete this one if (pSymbol->cchName >= pFind->cchPrefix + pFind->cchName) { // Compare Prefix if (StrCmpNI(pSymbol->pszName, pFind->pszPrefix, pFind->cchPrefix) == 0) { // Compare Name if (StrCmpNI(pSymbol->pszName + pFind->cchPrefix, pFind->pszName, pFind->cchName) == 0) { // We found a property *ppProperty = pFind->pProperty; // Goto the next in the chain pFind->pProperty = pFind->pProperty->pNextHash; // Done goto exit; } } } // Next in chain pFind->pProperty = pFind->pProperty->pNextHash; } // Next Bucket pFind->wHashIndex++; // Done if (pFind->wHashIndex >= CBUCKETS) break; // If not done, goto first item in the bucket pFind->pProperty = m_prgHashTable[pFind->wHashIndex]; } // NOt Found hr = MIME_E_NOT_FOUND; exit: // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_HrFindProperty // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::_HrFindProperty(LPPROPSYMBOL pSymbol, LPPROPERTY *ppProperty) { // Locals HRESULT hr=S_OK; BOOL fTryName=FALSE; // Invalid Arg Assert(pSymbol && ppProperty); // By Known Symbol if (ISKNOWNPID(pSymbol->dwPropId)) { // Stats #ifdef DEBUG g_cSetPidLookups++; #endif // Is there data if (m_prgIndex[pSymbol->dwPropId]) { // Set It (could be NULL) *ppProperty = m_prgIndex[pSymbol->dwPropId]; // Done goto exit; } } // Otherwise, lookup by name else { // Stats #ifdef DEBUG g_cHashLookups++; #endif // Loop Assert(pSymbol->wHashIndex < CBUCKETS); for (LPPROPERTY pProperty=m_prgHashTable[pSymbol->wHashIndex]; pProperty!=NULL; pProperty=pProperty->pNextHash) { // Compare if (pProperty && pProperty->pSymbol->dwPropId == pSymbol->dwPropId) { // Validate Hash Index Assert(pProperty->pSymbol->wHashIndex == pSymbol->wHashIndex); // Set Return *ppProperty = pProperty; // Done goto exit; } } } // Not Found hr = MIME_E_NOT_FOUND; exit: // Not Found return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_HrOpenProperty // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::_HrOpenProperty(LPPROPSYMBOL pSymbol, LPPROPERTY *ppProperty) { // If we dont find it, try to create it if (FAILED(_HrFindProperty(pSymbol, ppProperty))) return TrapError(_HrCreateProperty(pSymbol, ppProperty)); // We Found It, return return S_OK; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_HrCreateProperty // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::_HrCreateProperty(LPPROPSYMBOL pSymbol, LPPROPERTY *ppProperty) { // Locals HRESULT hr=S_OK; LPPROPERTY pProperty; #ifdef DEBUG LPPROPERTY pTemp; #endif // Invalid Arg Assert(pSymbol && ppProperty); // Allocate an item... CHECKHR(hr = ObjectHeap_HrAllocProperty(&pProperty)); // Set the symbol pProperty->pSymbol = pSymbol; // The Property Better Not Exist Yet (Assumes caller did a FindProperty before CreateProperty) Assert(_HrFindProperty(pSymbol, &pTemp) == MIME_E_NOT_FOUND); // MPF_HEADER if (ISFLAGSET(pSymbol->dwFlags, MPF_HEADER)) { // Insert into the header table CHECKHR(hr = _HrAppendHeaderTable(pProperty)); } // MPF_ADDRESS if (ISFLAGSET(pSymbol->dwFlags, MPF_ADDRESS)) { // Insert into the header table CHECKHR(hr = _HrAppendAddressTable(pProperty)); } // Stats #ifdef DEBUG g_cHashInserts++; if (m_prgHashTable[pSymbol->wHashIndex]) g_cHashCollides++; #endif // Set Next Hash Item Assert(pSymbol->wHashIndex < CBUCKETS); pProperty->pNextHash = m_prgHashTable[pSymbol->wHashIndex]; // New Properties are places as the head of the overflow chain m_prgHashTable[pSymbol->wHashIndex] = pProperty; // Insert into Known Property Index if (ISKNOWNPID(pSymbol->dwPropId)) { Assert(m_prgIndex[pSymbol->dwPropId] == NULL); m_prgIndex[pSymbol->dwPropId] = pProperty; } // PRSTATE_PARENT FLAGSET(pProperty->dwState, PRSTATE_PARENT); // Return this prop *ppProperty = pProperty; // Count Properties m_cProps++; exit: // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_HrAppendProperty // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::_HrAppendProperty(LPPROPSYMBOL pSymbol, LPPROPERTY *ppProperty) { // Locals HRESULT hr=S_OK; LPPROPERTY pParent, pAppend; // Invalid Arg Assert(pSymbol && ppProperty); // Does pTag already exist ? if (SUCCEEDED(_HrFindProperty(pSymbol, &pParent))) { // Better be a parent property Assert(ISFLAGSET(pParent->dwState, PRSTATE_PARENT)); // Allocate an item... CHECKHR(hr = ObjectHeap_HrAllocProperty(&pAppend)); // pSymbol From pTag pAppend->pSymbol = pParent->pSymbol; // If this is a header property, insert into the header table if (ISFLAGSET(pSymbol->dwFlags, MPF_HEADER)) { // Insert into the header table CHECKHR(hr = _HrAppendHeaderTable(pAppend)); } // MPF_ADDRESS if (ISFLAGSET(pSymbol->dwFlags, MPF_ADDRESS)) { // Insert into the header table CHECKHR(hr = _HrAppendAddressTable(pAppend)); } // Update pParent->pTailData if (NULL == pParent->pNextValue) { Assert(NULL == pParent->pTailValue); pParent->pNextValue = pAppend; pParent->pTailValue = pAppend; } else { Assert(pParent->pTailValue && pParent->pTailValue->pNextValue == NULL); pParent->pTailValue->pNextValue = pAppend; pParent->pTailValue = pAppend; } // Return this prop *ppProperty = pAppend; // Count Properties m_cProps++; } // Otherwise, create a new property else { // Create It CHECKHR(hr = _HrCreateProperty(pSymbol, ppProperty)); } exit: // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_HrAppendAddressGroup // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::_HrAppendAddressGroup(LPADDRESSGROUP pGroup, LPMIMEADDRESS *ppAddress) { // Locals HRESULT hr=S_OK; ULONG i=0; BOOL fUsingEmpty=FALSE; LPMIMEADDRESS pAddress; // Use Empty Cell if (m_rAdrTable.cEmpty) { // Find First Empty Cell.. for (i=0; i m_rAdrTable.cAlloc) { // Grow my current property value array CHECKHR(hr = HrRealloc((LPVOID *)&m_rAdrTable.prgpAdr, sizeof(LPMIMEADDRESS) * (m_rAdrTable.cAlloc + 10))); // Increment alloc size m_rAdrTable.cAlloc += 10; } // Index to use i = m_rAdrTable.cAdrs; } // Allocate an address props structure CHECKHR(hr = ObjectHeap_HrAllocAddress(&pAddress)); // Assign a Handle pAddress->hThis = HADDRESSMAKE(i); // Link the Address into the group _LinkAddress(pAddress, pGroup); // Put it into the Array m_rAdrTable.prgpAdr[i] = pAddress; // Return It *ppAddress = pAddress; // If not using empty cell, increment body count if (FALSE == fUsingEmpty) m_rAdrTable.cAdrs++; else m_rAdrTable.cEmpty--; exit: // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_LinkAddress // -------------------------------------------------------------------------------- void CMimePropertyContainer::_LinkAddress(LPMIMEADDRESS pAddress, LPADDRESSGROUP pGroup) { // Validate Arg Assert(pAddress && pGroup && NULL == pAddress->pNext && NULL == pAddress->pPrev); // Put pGroup into pAddress pAddress->pGroup = pGroup; // Link into the list if (NULL == pGroup->pHead) { Assert(NULL == pGroup->pTail); pGroup->pHead = pAddress; pGroup->pTail = pAddress; } else { Assert(pGroup->pTail && pGroup->pTail->pNext == NULL); pGroup->pTail->pNext = pAddress; pAddress->pPrev = pGroup->pTail; pGroup->pTail = pAddress; } // Increment Count pGroup->cAdrs++; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_HrAppendAddressTable // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::_HrAppendAddressTable(LPPROPERTY pProperty) { // Locals HRESULT hr=S_OK; // Invalid Arg Assert(pProperty && NULL == pProperty->pGroup); // New Group CHECKALLOC(pProperty->pGroup = (LPADDRESSGROUP)g_pMalloc->Alloc(sizeof(ADDRESSGROUP))); // ZeroInit ZeroMemory(pProperty->pGroup, sizeof(ADDRESSGROUP)); // Link this group if (NULL == m_rAdrTable.pHead) { Assert(m_rAdrTable.pTail == NULL); m_rAdrTable.pHead = pProperty; m_rAdrTable.pTail = pProperty; } else { Assert(m_rAdrTable.pTail && m_rAdrTable.pTail->pGroup && m_rAdrTable.pTail->pGroup->pNext == NULL); m_rAdrTable.pTail->pGroup->pNext = pProperty; pProperty->pGroup->pPrev = m_rAdrTable.pTail; m_rAdrTable.pTail = pProperty; } exit: // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_HrAppendHeaderTable // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::_HrAppendHeaderTable(LPPROPERTY pProperty) { // Locals HRESULT hr=S_OK; ULONG i=0; BOOL fUsingEmpty=FALSE; // Invalid Arg Assert(pProperty && NULL == pProperty->hRow); // Use Empty Cell if (m_rHdrTable.cEmpty) { // Find First Empty Cell.. for (i=0; i m_rHdrTable.cAlloc) { // Grow my current property value array CHECKHR(hr = HrRealloc((LPVOID *)&m_rHdrTable.prgpRow, sizeof(LPPROPERTY) * (m_rHdrTable.cAlloc + 10))); // Increment alloc size m_rHdrTable.cAlloc += 10; } // Index to use i = m_rHdrTable.cRows; } // Save Property index table m_rHdrTable.prgpRow[i] = pProperty; // Set Handle pProperty->hRow = HROWMAKE(i); // If not using empty cell, increment body count if (FALSE == fUsingEmpty) m_rHdrTable.cRows++; else m_rHdrTable.cEmpty--; exit: // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::IsPropSet // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::IsPropSet(LPCSTR pszName) { // Locals HRESULT hr=S_FALSE; LPPROPSYMBOL pSymbol; LPPROPERTY pProperty; // Invalid Arg if (NULL == pszName) return TrapError(E_INVALIDARG); // Thread Safety EnterCriticalSection(&m_cs); // Find the Symbol if (FAILED(g_pSymCache->HrOpenSymbol(pszName, FALSE, &pSymbol))) goto exit; // Find the Property if (FAILED(_HrFindProperty(pSymbol, &pProperty))) goto exit; // Its Set hr = S_OK; exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::GetPropInfo // -------------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::GetPropInfo(LPCSTR pszName, LPMIMEPROPINFO pInfo) { // Locals HRESULT hr=S_OK; LPPROPSYMBOL pSymbol; LPPROPERTY pProperty; LPPROPERTY pCurr; // Invalid Arg if (NULL == pszName || NULL == pInfo) return TrapError(E_INVALIDARG); // Thread Safety EnterCriticalSection(&m_cs); // Find the Symbol CHECKHR(hr = g_pSymCache->HrOpenSymbol(pszName, FALSE, &pSymbol)); // Find the Property CHECKHR(hr = _HrFindProperty(pSymbol, &pProperty)); // PIM_CHARSET if (ISFLAGSET(pInfo->dwMask, PIM_CHARSET)) { // Get Charset pInfo->hCharset = pProperty->pCharset ? pProperty->pCharset->hCharset : m_rOptions.pDefaultCharset->hCharset; } // PIM_ENCODINGTYPE if (ISFLAGSET(pInfo->dwMask, PIM_ENCODINGTYPE)) { // Get Encoding pInfo->ietEncoding = pProperty->ietValue; } // PIM_ROWNUMBER if (ISFLAGSET(pInfo->dwMask, PIM_ROWNUMBER)) pInfo->dwRowNumber = pProperty->dwRowNumber; // PIM_FLAGS if (ISFLAGSET(pInfo->dwMask, PIM_FLAGS)) pInfo->dwFlags = pProperty->pSymbol->dwFlags; // PIM_PROPID if (ISFLAGSET(pInfo->dwMask, PIM_PROPID)) pInfo->dwPropId = pProperty->pSymbol->dwPropId; // PIM_VALUES if (ISFLAGSET(pInfo->dwMask, PIM_VALUES)) { // Let me count the ways for(pCurr=pProperty, pInfo->cValues=0; pCurr!=NULL; pCurr=pCurr->pNextValue) pInfo->cValues++; } // PIM_VTCURRENT if (ISFLAGSET(pInfo->dwMask, PIM_VTCURRENT)) pInfo->vtCurrent = MimeVT_To_PropVT(&pProperty->rValue); // PIM_VTDEFAULT if (ISFLAGSET(pInfo->dwMask, PIM_VTDEFAULT)) pInfo->vtDefault = pProperty->pSymbol->vtDefault; exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::SetPropInfo // -------------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::SetPropInfo(LPCSTR pszName, LPCMIMEPROPINFO pInfo) { // Locals HRESULT hr=S_OK; LPPROPSYMBOL pSymbol; LPPROPERTY pProperty; LPPROPERTY pCurr; LPINETCSETINFO pCharset; // Invalid Arg if (NULL == pszName || NULL == pInfo) return TrapError(E_INVALIDARG); // Thread Safety EnterCriticalSection(&m_cs); // Find the Symbol CHECKHR(hr = g_pSymCache->HrOpenSymbol(pszName, FALSE, &pSymbol)); // Find the Property CHECKHR(hr = _HrFindProperty(pSymbol, &pProperty)); // Set All Values with the property information for(pCurr=pProperty; pCurr!=NULL; pCurr=pCurr->pNextValue) { // PIM_CHARSET if (ISFLAGSET(pInfo->dwMask, PIM_CHARSET)) { // Open the Charset if (SUCCEEDED(g_pInternat->HrOpenCharset(pInfo->hCharset, &pCharset))) pProperty->pCharset = pCharset; } // PIM_ENCODED if (ISFLAGSET(pInfo->dwMask, PIM_ENCODINGTYPE)) { // Change Encoding State of the mime Variant pProperty->ietValue = (IET_ENCODED == pInfo->ietEncoding) ? IET_ENCODED : IET_DECODED; } // PIM_ROWNUMBER if (ISFLAGSET(pInfo->dwMask, PIM_ROWNUMBER)) { // Save the Row Number pCurr->dwRowNumber = pInfo->dwRowNumber; // Make a note that the use set this row number so the save order doesn't get hosed FLAGSET(pCurr->dwState, PRSTATE_USERSETROWNUM); } } exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::EnumProps // -------------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::EnumProps(DWORD dwFlags, IMimeEnumProperties **ppEnum) { // Locals HRESULT hr=S_OK; ULONG i, cProps=0, cAlloc=0; LPENUMPROPERTY prgProp=NULL; LPPROPERTY pCurrProp; CMimeEnumProperties *pEnum=NULL; // Invalid Arg if (NULL == ppEnum) return TrapError(E_INVALIDARG); // Init *ppEnum = NULL; // Thread Safety EnterCriticalSection(&m_cs); // Loop through the item table for (i=0; ipNextHash) { // Grow the array ? if (cProps + 1 > cAlloc) { // Realloc CHECKALLOC(prgProp = (LPENUMPROPERTY)g_pMalloc->Realloc((LPVOID)prgProp, sizeof(ENUMPROPERTY) * (cAlloc + 10))); // Increment cAlloc cAlloc += 10; } // hRow prgProp[cProps].hRow = pCurrProp->hRow; // dwPropId prgProp[cProps].dwPropId = pCurrProp->pSymbol->dwPropId; // Init Name to Null prgProp[cProps].pszName = NULL; // Name if (ISFLAGSET(dwFlags, EPF_NONAME) == FALSE) { // Return name CHECKALLOC(prgProp[cProps].pszName = PszDupA(pCurrProp->pSymbol->pszName)); } // Increment iProp cProps++; } } // Allocate CHECKALLOC(pEnum = new CMimeEnumProperties); // Initialize CHECKHR(hr = pEnum->HrInit(0, cProps, prgProp, FALSE)); // Don't Free pEnumRow prgProp = NULL; cProps = 0; // Return it (*ppEnum) = (IMimeEnumProperties *)pEnum; (*ppEnum)->AddRef(); exit: // Cleanup SafeRelease(pEnum); g_pMoleAlloc->FreeEnumPropertyArray(cProps, prgProp, TRUE); // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::BindToObject // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::BindToObject(REFIID riid, void **ppvObject) { return TrapError(QueryInterface(riid, ppvObject)); } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_HrBuildAddressString // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::_HrBuildAddressString(LPPROPERTY pProperty, DWORD dwFlags, LPMIMEVARIANT pValue) { // Locals HRESULT hr=S_OK; ULONG cAddrsWrote=0; LPSTREAM pStream=NULL; CByteStream cByteStream; LPPROPERTY pCurrValue; MIMEVARIANT rValue; ADDRESSFORMAT format; LPINETCSETINFO pCharsetSource=NULL; // Invalid Arg Assert(pProperty && pValue); // Variant Not Supported if (MVT_VARIANT == pValue->type) return TrapError(MIME_E_VARTYPE_NO_CONVERT); if (MVT_STRINGW == pValue->type && ISFLAGSET(dwFlags, PDF_ENCODED)) return TrapError(MIME_E_VARTYPE_NO_CONVERT); // Init ZeroMemory(&rValue, sizeof(MIMEVARIANT)); // I need a stream to write to... if (MVT_STREAM == pValue->type) { // Validate the stream if (NULL == pValue->pStream) { hr = TrapError(E_INVALIDARG); goto exit; } // Save the Stream pStream = pValue->pStream; } // Otherwise, create my own stream else pStream = &cByteStream; // Decide on a format if (ISFLAGSET(dwFlags, PDF_HEADERFORMAT)) format = AFT_RFC822_TRANSMIT; else if (ISFLAGSET(dwFlags, PDF_ENCODED)) format = AFT_RFC822_ENCODED; else format = AFT_DISPLAY_BOTH; // If Writing Transmit (Write Header Name) if (ISFLAGSET(dwFlags, PDF_NAMEINDATA)) { // Write the header name CHECKHR(hr = pStream->Write(pProperty->pSymbol->pszName, pProperty->pSymbol->cchName, NULL)); // Write Colon CHECKHR(hr = pStream->Write(c_szColonSpace, lstrlen(c_szColonSpace), NULL)); } // Save with no encoding if (ISFLAGSET(pProperty->dwState, PRSTATE_SAVENOENCODE)) { // Better be groupdirty Assert(ISFLAGSET(pProperty->dwState, PRSTATE_NEEDPARSE)); // Convert Data... rValue.type = MVT_STRINGA; // Destination is not encoded pCharsetSource = pProperty->pCharset ? pProperty->pCharset : m_rOptions.pDefaultCharset; // Convert It CHECKHR(hr = HrConvertVariant(pProperty, CVF_NOALLOC | PDF_ENCODED, &rValue)); // Write it to the stream CHECKHR(hr = pStream->Write(rValue.rStringA.pszVal, rValue.rStringA.cchVal, NULL)); } // Otherwise, normal save else { // Loop through parsed addresses... for (pCurrValue=pProperty; pCurrValue!=NULL; pCurrValue=pCurrValue->pNextValue) { // We should have an address Assert(pCurrValue->pGroup && ISFLAGSET(pCurrValue->pSymbol->dwFlags, MPF_ADDRESS)); // Does the Property need to be parsed ? CHECKHR(hr = _HrParseInternetAddress(pCurrValue)); // Tell each address group object to write its data to the stream if (pCurrValue->pGroup) { // Write the data CHECKHR(hr = _HrSaveAddressGroup(pCurrValue, pStream, &cAddrsWrote, format, VT_LPSTR)); } // Set It Yet ? if (NULL == pCharsetSource) { pCharsetSource = pCurrValue->pCharset ? pCurrValue->pCharset : m_rOptions.pDefaultCharset; } // No Vectoring if (FALSE == ISFLAGSET(dwFlags, PDF_VECTOR)) break; } } // Transmit if (ISFLAGSET(dwFlags, PDF_HEADERFORMAT)) { // Final CRLF if Transmit Format CHECKHR(hr = pStream->Write(g_szCRLF, lstrlen(g_szCRLF), NULL)); } // Final CRLF if (cAddrsWrote || ISFLAGSET(dwFlags, PDF_NAMEINDATA) || ISFLAGSET(dwFlags,PDF_HEADERFORMAT)) { // MVT_STRINGA if (MVT_STRINGA == pValue->type) { // pStream better be the byte stream Assert(pStream == &cByteStream); // Get string from stream... CHECKHR(hr = cByteStream.HrAcquireStringA(&pValue->rStringA.cchVal, &pValue->rStringA.pszVal, ACQ_DISPLACE)); } // MVT_STRINGW else if (MVT_STRINGW == pValue->type) { // Locals CODEPAGEID cpSource=CP_ACP; PROPSTRINGA rStringA; // Init ZeroMemory(&rStringA, sizeof(PROPSTRINGA)); // pStream better be the byte stream Assert(pStream == &cByteStream); // Get string from stream... CHECKHR(hr = cByteStream.HrAcquireStringA(&rStringA.cchVal, &rStringA.pszVal, ACQ_COPY)); // Determine cpSoruce if (pCharsetSource) { // If Encoded, use internet codepage, otherwise, use Windows codepage cpSource = ISFLAGSET(dwFlags, PDF_ENCODED) ? pCharsetSource->cpiInternet : MimeOleGetWindowsCPEx(pCharsetSource); } // Convert to Unicode CHECKHR(hr = g_pInternat->HrMultiByteToWideChar(cpSource, &rStringA, &pValue->rStringW)); } else Assert(MVT_STREAM == pValue->type); } // No Data else { hr = MIME_E_NO_DATA; goto exit; } exit: // Cleanup MimeVariantFree(&rValue); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_HrBuildParameterString // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::_HrBuildParameterString(LPPROPERTY pProperty, DWORD dwFlags, LPMIMEVARIANT pValue) { // Locals HRESULT hr=S_OK, hrFind; LPSTR pszParamName; LPSTR pszEscape=NULL; FINDPROPERTY rFind; LPPROPERTY pParameter; LPSTREAM pStream=NULL; CByteStream cByteStream; BOOL fQuoted; ULONG cWrote=0; MIMEVARIANT rValue; // Invalid Arg Assert(pProperty && pProperty->pNextValue == NULL && pValue); Assert(ISSTRINGA(&pProperty->rValue)); // Variant Not Supported if (MVT_VARIANT == pValue->type) return TrapError(MIME_E_VARTYPE_NO_CONVERT); if (MVT_STRINGW == pValue->type && ISFLAGSET(dwFlags, PDF_ENCODED)) return TrapError(MIME_E_VARTYPE_NO_CONVERT); // Init rValue ZeroMemory(&rValue, sizeof(MIMEVARIANT)); // I need a stream to write to... if (MVT_STREAM == pValue->type) { // Validate the stream if (NULL == pValue->pStream) { hr = TrapError(E_INVALIDARG); goto exit; } // Save the Stream pStream = pValue->pStream; } // Otherwise, create my own stream else pStream = &cByteStream; // If Writing Transmit (Write Header Name) if (ISFLAGSET(dwFlags, PDF_NAMEINDATA)) { // Write the header name CHECKHR(hr = pStream->Write(pProperty->pSymbol->pszName, pProperty->pSymbol->cchName, NULL)); // Write Colon CHECKHR(hr = pStream->Write(c_szColonSpace, lstrlen(c_szColonSpace), NULL)); } // Write First Prop CHECKHR(hr = pStream->Write(pProperty->rValue.rStringA.pszVal, pProperty->rValue.rStringA.cchVal, NULL)); // We wrote one item cWrote = 1; // Initialize rFind ZeroMemory(&rFind, sizeof(FINDPROPERTY)); rFind.pszPrefix = "par:"; rFind.cchPrefix = 4; rFind.pszName = pProperty->pSymbol->pszName; rFind.cchName = pProperty->pSymbol->cchName; // Find First.. hrFind = _HrFindFirstProperty(&rFind, &pParameter); while(SUCCEEDED(hrFind) && pParameter) { // Transmit Format if (ISFLAGSET(dwFlags, PDF_HEADERFORMAT)) { // Write ';\r\n\t' CHECKHR(hr = pStream->Write(c_szParamFold, lstrlen(c_szParamFold), NULL)); } // Otherwise else { // Write ';\r\n\t' CHECKHR(hr = pStream->Write(c_szSemiColonSpace, lstrlen(c_szSemiColonSpace), NULL)); } // Get Parameter Name pszParamName = PszScanToCharA((LPSTR)pParameter->pSymbol->pszName, ':'); pszParamName++; pszParamName = PszScanToCharA(pszParamName, ':'); pszParamName++; // Write the name CHECKHR(hr = pStream->Write(pszParamName, lstrlen(pszParamName), NULL)); // Write the property... CHECKHR(hr = pStream->Write(c_szEqual, lstrlen(c_szEqual), NULL)); // Convert Data... rValue.type = MVT_STRINGA; // Convert It CHECKHR(hr = HrConvertVariant(pParameter, CVF_NOALLOC | PDF_ENCODED, &rValue)); // Quoted fQuoted = FALSE; if (lstrcmpi(pszParamName, (LPSTR)c_szBoundary) == 0 || lstrcmpi(pszParamName, (LPSTR)c_szFileName) == 0 || lstrcmpi(pszParamName, (LPSTR)c_szName) == 0 || lstrcmpi(pszParamName, (LPSTR)c_szID) == 0 || lstrcmpi(pszParamName, (LPSTR)c_szCharset) == 0) fQuoted = TRUE; // Otherwise, check for must quote characters else { // Loop the string for (ULONG i=0; iHrOpenSymbol(rName.pszVal, FALSE, &pParameter))) pParameter = NULL; // For "par:content-disposition:filename" property assume no escaped chars if (pParameter && (pParameter->dwPropId == PID_PAR_FILENAME)) dwFlags &= ~PSF_ESCAPED; // Raid-48365: FE-J:OExpress: JIS file name of attachment is not decoded correctly. while(1) { // Parse parameter value ch = cString.ChParse('\"', '\"', dwFlags); if ('\0' == ch) break; // Not a international parameter if (pParameter && !ISFLAGSET(pParameter->dwFlags, MPF_INETCSET)) break; // Skip White Space ch = cString.ChSkipWhite(); if ('\0' == ch || ';' == ch) break; // Put a quote back into the string CHECKHR(hr = cString.HrAppendValue('\"')); // Add PSF_NORESET flag FLAGSET(dwFlags, PSF_NORESET); } // If no value, were done if (0 == cString.CchValue()) goto exit; } else Assert(';' == chToken || '\0' == chToken); // Setup Value rValue.type = MVT_STRINGA; rValue.rStringA.pszVal = (LPSTR)cString.PszValue(); rValue.rStringA.cchVal = cString.CchValue(); // Set the property CHECKHR(hr = SetProp(rName.pszVal, PDF_ENCODED, &rValue)); // If last token was a '"', then seek to semicolon if ('\"' == chToken) { // Parse parameter value chToken = cString.ChParse(";"); } } exit: // Cleanup STACKSTRING_FREE(rName); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_HrParseInternetAddress // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::_HrParseInternetAddress(LPPROPERTY pProperty) { // Locals HRESULT hr=S_OK; MIMEVARIANT rSource; LPMIMEADDRESS pAddress; LPINETCSETINFO pCharset=NULL; CAddressParser cAdrParse; // Invalid Arg Assert(pProperty && pProperty->pSymbol && ISFLAGSET(pProperty->pSymbol->dwFlags, MPF_ADDRESS) && pProperty->pGroup); // Init ZeroMemory(&rSource, sizeof(MIMEVARIANT)); // If the property does not need PRSTATE_NEEDPARSE, return if (!ISFLAGSET(pProperty->dwState, PRSTATE_NEEDPARSE)) goto exit; // Setup rSource rSource.type = MVT_STRINGW; // Convert to multibyte CHECKHR(hr = HrConvertVariant(pProperty, CVF_NOALLOC, &rSource)); // Figure out pCharset if (pProperty->pCharset) pCharset = pProperty->pCharset; else if (m_rOptions.pDefaultCharset) pCharset = m_rOptions.pDefaultCharset; else if (CIntlGlobals::GetDefHeadCset()) pCharset = CIntlGlobals::GetDefHeadCset(); // Initialize Parse Structure cAdrParse.Init(rSource.rStringW.pszVal, rSource.rStringW.cchVal); // Parse while(SUCCEEDED(cAdrParse.Next())) { // Append an Address CHECKHR(hr = _HrAppendAddressGroup(pProperty->pGroup, &pAddress)); // Set the Address Type pAddress->dwAdrType = pProperty->pSymbol->dwAdrType; // Store Friendly Name CHECKHR(hr = HrSetAddressTokenW(cAdrParse.PszFriendly(), cAdrParse.CchFriendly(), &pAddress->rFriendly)); // Store Email CHECKHR(hr = HrSetAddressTokenW(cAdrParse.PszEmail(), cAdrParse.CchEmail(), &pAddress->rEmail)); // Save the Address pAddress->pCharset = pCharset; } // No sync needed anymore FLAGCLEAR(pProperty->dwState, PRSTATE_NEEDPARSE); exit: // Cleanup MimeVariantFree(&rSource); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_HrStoreVariantValue // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::_HrStoreVariantValue(LPPROPERTY pProperty, DWORD dwFlags, LPCMIMEVARIANT pValue) { // Locals HRESULT hr=S_OK; ULONG cbSource=0; LPBYTE pbSource=NULL; LPBYTE *ppbDest=NULL; ULONG *pcbDest=NULL; LPBYTE pbNewBlob; ULONG cbNewBlob; // Invalid Arg Assert(pProperty && pValue); // Handle Data Type switch(pValue->type) { case MVT_STRINGA: // Invalid Arg if (ISVALIDSTRINGA(&pValue->rStringA) == FALSE) { hr = TrapError(E_INVALIDARG); goto exit; } // Set Byte Source pbSource = (LPBYTE)pValue->rStringA.pszVal; // Set Source Byte Count cbSource = pValue->rStringA.cchVal + 1; // Set Destination Byte Pointer ppbDest = (LPBYTE *)&(pProperty->rValue.rStringA.pszVal); // Save Length Now pProperty->rValue.rStringA.cchVal = pValue->rStringA.cchVal; break; case MVT_STRINGW: // Invalid Arg if (ISVALIDSTRINGW(&pValue->rStringW) == FALSE) { hr = TrapError(E_INVALIDARG); goto exit; } // Set Byte Source pbSource = (LPBYTE)pValue->rStringW.pszVal; // Set Source Byte Count cbSource = ((pValue->rStringW.cchVal + 1) * sizeof(WCHAR)); // Set Destination Byte Pointer ppbDest = (LPBYTE *)&(pProperty->rValue.rStringW.pszVal); // Save Length Now pProperty->rValue.rStringW.cchVal = pValue->rStringW.cchVal; break; case MVT_VARIANT: pProperty->rValue.rVariant.vt = pValue->rVariant.vt; switch(pValue->rVariant.vt) { case VT_FILETIME: CopyMemory(&pProperty->rValue.rVariant.filetime, &pValue->rVariant.filetime, sizeof(FILETIME)); pbSource = (LPBYTE)&pValue->rVariant.filetime; cbSource = sizeof(pValue->rVariant.filetime); break; case VT_I4: pProperty->rValue.rVariant.lVal = pValue->rVariant.lVal; pbSource = (LPBYTE)&pValue->rVariant.lVal; cbSource = sizeof(pValue->rVariant.lVal); break; case VT_UI4: pProperty->rValue.rVariant.ulVal = pValue->rVariant.ulVal; pbSource = (LPBYTE)&pValue->rVariant.ulVal; cbSource = sizeof(pValue->rVariant.ulVal); break; default: Assert(FALSE); hr = TrapError(MIME_E_INVALID_VARTYPE); goto exit; } break; default: Assert(FALSE); hr = TrapError(E_FAIL); goto exit; } // Better have a source Assert(cbSource && cbSource); // Store the data if (cbSource > pProperty->cbAlloc) { // Fits into static buffer ? if (cbSource <= sizeof(pProperty->rgbScratch)) { // If not reallocing... if (ISFLAGSET(pProperty->dwState, PRSTATE_ALLOCATED)) { Assert(pProperty->pbBlob); g_pMalloc->Free(pProperty->pbBlob); FLAGCLEAR(pProperty->dwState, PRSTATE_ALLOCATED); } // Use Scratch Buffer pProperty->pbBlob = pProperty->rgbScratch; pProperty->cbAlloc = sizeof(pProperty->rgbScratch); } // Was my current buffer allocated else { // If not reallocing... if (!ISFLAGSET(pProperty->dwState, PRSTATE_ALLOCATED)) { pProperty->pbBlob = NULL; pProperty->cbAlloc = 0; } else Assert(pProperty->cbAlloc > sizeof(pProperty->rgbScratch) && g_pMalloc->DidAlloc(pProperty->pbBlob) == 1); // Compute Size of new blob cbNewBlob = pProperty->cbAlloc + (cbSource - pProperty->cbAlloc); // Realloc New Blob CHECKALLOC(pbNewBlob = (LPBYTE)g_pMalloc->Realloc((LPVOID)pProperty->pbBlob, cbNewBlob)); // We've allocated it FLAGSET(pProperty->dwState, PRSTATE_ALLOCATED); // Assume the new blob pProperty->pbBlob = pbNewBlob; pProperty->cbAlloc = cbNewBlob; } } // Copy the data CopyMemory(pProperty->pbBlob, pbSource, cbSource); // Set Size of Data in m_pbBlob pProperty->cbBlob = cbSource; // If there is a ppbDest assign to it if (ppbDest) *ppbDest = pProperty->pbBlob; // Save Encoding Type pProperty->ietValue = (ISFLAGSET(dwFlags, PDF_ENCODED)) ? IET_ENCODED : IET_DECODED; // PRSTATE_SAVENOENCODE if (ISFLAGSET(dwFlags, PDF_SAVENOENCODE)) FLAGSET(pProperty->dwState, PRSTATE_SAVENOENCODE); // Save Type pProperty->rValue.type = pValue->type; exit: // Failure if (FAILED(hr)) ZeroMemory(&pProperty->rValue, sizeof(MIMEVARIANT)); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::GetProp // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::GetProp(LPCSTR pszName, LPSTR *ppszData) { // Locals HRESULT hr=S_OK; MIMEVARIANT rValue; LPPROPSYMBOL pSymbol; // Invalid Arg Assert(pszName && ppszData); // Init *ppszData = NULL; // Open Property Symbol CHECKHR(hr = g_pSymCache->HrOpenSymbol(pszName, FALSE, &pSymbol)); // Init the variant rValue.type = MVT_STRINGA; // Get Property by Symbol CHECKHR(hr = GetProp(pSymbol, 0, &rValue)); // Set the string Assert(rValue.rStringA.pszVal); *ppszData = rValue.rStringA.pszVal; exit: // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::GetProp // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::GetProp(LPPROPSYMBOL pSymbol, LPSTR *ppszData) { // Locals HRESULT hr=S_OK; MIMEVARIANT rValue; // Invalid Arg Assert(pSymbol && ppszData); // Init *ppszData = NULL; // Init the variant rValue.type = MVT_STRINGA; // Get Property by Symbol CHECKHR(hr = GetProp(pSymbol, 0, &rValue)); // Set the string Assert(rValue.rStringA.pszVal); *ppszData = rValue.rStringA.pszVal; exit: // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::GetPropW // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::GetPropW(LPPROPSYMBOL pSymbol, LPWSTR *ppwszData) { // Locals HRESULT hr=S_OK; MIMEVARIANT rValue; // Invalid Arg Assert(pSymbol && ppwszData); // Init *ppwszData = NULL; // Init the variant rValue.type = MVT_STRINGW; // Get Property by Symbol CHECKHR(hr = GetProp(pSymbol, 0, &rValue)); // Set the string Assert(rValue.rStringW.pszVal); *ppwszData = rValue.rStringW.pszVal; exit: // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::GetProp // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::GetProp(LPCSTR pszName, DWORD dwFlags, LPPROPVARIANT pVariant) { // Locals HRESULT hr=S_OK; LPPROPSYMBOL pSymbol; MIMEVARIANT rValue; // Invaid Arg if (NULL == pszName || NULL == pVariant) return TrapError(E_INVALIDARG); // Open Property Symbol CHECKHR(hr = g_pSymCache->HrOpenSymbol(pszName, FALSE, &pSymbol)); // Symbol Better have a supported variant type Assert(ISSUPPORTEDVT(pSymbol->vtDefault)); // Set rValue Variant if (VT_EMPTY == pVariant->vt) rValue.rVariant.vt = pVariant->vt = pSymbol->vtDefault; else rValue.rVariant.vt = pVariant->vt; // Map to MIMEVARIANT if (VT_LPSTR == pVariant->vt || VT_EMPTY == pVariant->vt) rValue.type = MVT_STRINGA; else if (VT_LPWSTR == pVariant->vt) rValue.type = MVT_STRINGW; else rValue.type = MVT_VARIANT; // Get Property by Symbol CHECKHR(hr = GetProp(pSymbol, dwFlags, &rValue)); // Map to PROPVARIANT if (MVT_STRINGA == rValue.type) pVariant->pszVal = rValue.rStringA.pszVal; else if (MVT_STRINGW == rValue.type) pVariant->pwszVal = rValue.rStringW.pszVal; else CopyMemory(pVariant, &rValue.rVariant, sizeof(PROPVARIANT)); exit: // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::GetProp // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::GetProp(LPPROPSYMBOL pSymbol, DWORD dwFlags, LPMIMEVARIANT pValue) { // Locals HRESULT hr=S_OK; LPPROPERTY pProperty; // Thread Safety EnterCriticalSection(&m_cs); // Find the property hr = _HrFindProperty(pSymbol, &pProperty); // Failure if (FAILED(hr)) { // See if there is a default value for this property if (MIME_E_NOT_FOUND != hr) { hr = TrapError(hr); goto exit; } // Dispatch Default Request, otherwise, hr is still equal to MIME_E_NOT_FOUND.... if (ISTRIGGERED(pSymbol, IST_GETDEFAULT)) { // Property Dispatch CHECKHR(hr = _HrCallSymbolTrigger(pSymbol, IST_GETDEFAULT, dwFlags, pValue)); } } // Otherwise, get the property data else { // Raid-62460: Dependency Hack to make sure the GetProp works the same when getting addresses // PDF_VECTOR is always supported by _HrBuildAddressString, which gets called by _HrGetPropertyValue if (ISFLAGSET(pSymbol->dwFlags, MPF_ADDRESS)) FLAGSET(dwFlags, PDF_VECTOR); // Get the property value CHECKHR(hr = _HrGetPropertyValue(pProperty, dwFlags, pValue)); } exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::SetProp // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::SetProp(LPCSTR pszName, DWORD dwFlags, LPCMIMEVARIANT pValue) { // Locals HRESULT hr=S_OK; LPPROPSYMBOL pSymbol; // Invalid Arg Assert(pszName && pValue); // Open Property Symbol CHECKHR(hr = g_pSymCache->HrOpenSymbol(pszName, TRUE, &pSymbol)); // Get Property by Symbol CHECKHR(hr = SetProp(pSymbol, dwFlags, pValue)); exit: // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::SetProp // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::SetProp(LPCSTR pszName, LPCSTR pszData) { // Locals HRESULT hr=S_OK; LPPROPSYMBOL pSymbol; MIMEVARIANT rValue; // Invalid Arg Assert(pszName && pszData); // Open Property Symbol CHECKHR(hr = g_pSymCache->HrOpenSymbol(pszName, TRUE, &pSymbol)); // Init the variant rValue.type = MVT_STRINGA; rValue.rStringA.pszVal = (LPSTR)pszData; rValue.rStringA.cchVal = lstrlen(pszData); // Get Property by Symbol CHECKHR(hr = SetProp(pSymbol, 0, &rValue)); exit: // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::SetProp // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::SetProp(LPPROPSYMBOL pSymbol, LPCSTR pszData) { // Locals HRESULT hr=S_OK; MIMEVARIANT rValue; // Invalid Arg Assert(pSymbol && pszData); // Init the variant rValue.type = MVT_STRINGA; rValue.rStringA.pszVal = (LPSTR)pszData; rValue.rStringA.cchVal = lstrlen(pszData); // Get Property by Symbol CHECKHR(hr = SetProp(pSymbol, 0, &rValue)); exit: // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::SetProp // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::SetProp(LPCSTR pszName, DWORD dwFlags, LPCPROPVARIANT pVariant) { // Locals HRESULT hr=S_OK; LPPROPSYMBOL pSymbol; MIMEVARIANT rValue; // Invalid Arg if (NULL == pszName || NULL == pVariant) return TrapError(E_INVALIDARG); // Open Property Symbol CHECKHR(hr = g_pSymCache->HrOpenSymbol(pszName, TRUE, &pSymbol)); // MVT_STRINGW if (VT_LPSTR == pVariant->vt) { // Invalid Arg if (NULL == pVariant->pszVal) { hr = TrapError(E_INVALIDARG); goto exit; } // Setup rValue rValue.type = MVT_STRINGA; rValue.rStringA.pszVal = pVariant->pszVal; rValue.rStringA.cchVal = lstrlen(pVariant->pszVal); } // MVT_STRINGW else if (VT_LPWSTR == pVariant->vt) { // Invalid Arg if (NULL == pVariant->pwszVal) { hr = TrapError(E_INVALIDARG); goto exit; } // Fill rValue rValue.type = MVT_STRINGW; rValue.rStringW.pszVal = pVariant->pwszVal; rValue.rStringW.cchVal = lstrlenW(pVariant->pwszVal); } // MVT_VARIANT else { rValue.type = MVT_VARIANT; CopyMemory(&rValue.rVariant, pVariant, sizeof(PROPVARIANT)); } // Get Property by Symbol CHECKHR(hr = SetProp(pSymbol, dwFlags, &rValue)); exit: // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::SetProp // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::SetProp(LPPROPSYMBOL pSymbol, DWORD dwFlags, LPCMIMEVARIANT pValue) { // Locals HRESULT hr=S_OK; LPPROPERTY pProperty=NULL; // Invalid Arg Assert(pSymbol && pValue); // Thread Safety EnterCriticalSection(&m_cs); // Read-Only if (ISFLAGSET(pSymbol->dwFlags, MPF_READONLY)) { AssertSz(FALSE, "This property has the MPF_READONLY flag."); hr = TrapError(MIME_E_READ_ONLY); goto exit; } // Find the property if (FAILED(_HrFindProperty(pSymbol, &pProperty))) { // Create it CHECKHR(hr = _HrCreateProperty(pSymbol, &pProperty)); } // This better be a root property Assert(ISFLAGSET(pProperty->dwState, PRSTATE_PARENT)); // Remove multi-values if (pProperty->pNextValue) { // Free the chain _FreePropertyChain(pProperty->pNextValue); // No more pNextValue or pTailValue pProperty->pNextValue = pProperty->pTailValue = NULL; } // Store the data CHECKHR(hr = _HrSetPropertyValue(pProperty, dwFlags, pValue, FALSE)); // Dirty if (!ISFLAGSET(pSymbol->dwFlags, MPF_NODIRTY)) FLAGSET(m_dwState, COSTATE_DIRTY); exit: // Failure if (FAILED(hr) && pProperty) { // Delete the Property _UnlinkProperty(pProperty); } // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::AppendProp // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::AppendProp(LPCSTR pszName, DWORD dwFlags, LPPROPVARIANT pVariant) { // Locals HRESULT hr=S_OK; LPPROPSYMBOL pSymbol; MIMEVARIANT rValue; // Invalid Arg if (NULL == pszName || NULL == pVariant) return TrapError(E_INVALIDARG); // Open Property Symbol CHECKHR(hr = g_pSymCache->HrOpenSymbol(pszName, TRUE, &pSymbol)); // MVT_STRINGW if (VT_LPSTR == pVariant->vt) { // Invalid Arg if (NULL == pVariant->pszVal) { hr = TrapError(E_INVALIDARG); goto exit; } // Fill rValue rValue.type = MVT_STRINGA; rValue.rStringA.pszVal = pVariant->pszVal; rValue.rStringA.cchVal = lstrlen(pVariant->pszVal); } // MVT_STRINGW else if (VT_LPWSTR == pVariant->vt) { // Invalid Arg if (NULL == pVariant->pwszVal) { hr = TrapError(E_INVALIDARG); goto exit; } // Fill rValue rValue.type = MVT_STRINGW; rValue.rStringW.pszVal = pVariant->pwszVal; rValue.rStringW.cchVal = lstrlenW(pVariant->pwszVal); } // MVT_VARIANT else { rValue.type = MVT_VARIANT; CopyMemory(&rValue.rVariant, pVariant, sizeof(PROPVARIANT)); } // Get Property by Symbol CHECKHR(hr = AppendProp(pSymbol, dwFlags, &rValue)); exit: // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::AppendProp // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::AppendProp(LPPROPSYMBOL pSymbol, DWORD dwFlags, LPMIMEVARIANT pValue) { // Locals HRESULT hr=S_OK; LPPROPERTY pProperty=NULL; BOOL fAppended=FALSE; // Invalid Arg Assert(pSymbol && pValue); // Thread Safety EnterCriticalSection(&m_cs); // Read-Only if (ISFLAGSET(pSymbol->dwFlags, MPF_READONLY)) { AssertSz(FALSE, "This property has the MPF_READONLY flag."); hr = TrapError(MIME_E_READ_ONLY); goto exit; } // Find the Property if (FAILED(_HrFindProperty(pSymbol, &pProperty))) { // If not found... treat as basic set prop... CHECKHR(hr = SetProp(pSymbol, dwFlags, pValue)); } // Otherwise, of not multiline, fail else { // Its appended fAppended = TRUE; // Append a property CHECKHR(hr = _HrAppendProperty(pSymbol, &pProperty)); // Store the data CHECKHR(hr = _HrSetPropertyValue(pProperty, dwFlags, pValue, FALSE)); // I am now dirty if (!ISFLAGSET(pSymbol->dwFlags, MPF_NODIRTY)) FLAGSET(m_dwState, COSTATE_DIRTY); } exit: // Failure if (FAILED(hr) && pProperty && fAppended) { // Delete the Property _UnlinkProperty(pProperty); } // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_UnlinkProperty // -------------------------------------------------------------------------------- void CMimePropertyContainer::_UnlinkProperty(LPPROPERTY pProperty, LPPROPERTY *ppNextHash) { // Locals LPPROPERTY pCurrHash; LPPROPERTY pNextHash; LPPROPERTY pPrevHash=NULL; #ifdef DEBUG BOOL fUnlinked=FALSE; #endif // Invalid Arg Assert(pProperty && pProperty->pSymbol && ISFLAGSET(pProperty->dwState, PRSTATE_PARENT) && pProperty->pSymbol->wHashIndex < CBUCKETS); // Remove from array if (ISKNOWNPID(pProperty->pSymbol->dwPropId)) m_prgIndex[pProperty->pSymbol->dwPropId] = NULL; // Include Parameters if (ISFLAGSET(pProperty->pSymbol->dwFlags, MPF_HASPARAMS)) _DeleteLinkedParameters(pProperty); // Remove Property from the hash table for (pCurrHash=m_prgHashTable[pProperty->pSymbol->wHashIndex]; pCurrHash!=NULL; pCurrHash=pCurrHash->pNextHash) { // Is this pProp if (pCurrHash == pProperty) { // NextHash pNextHash = pCurrHash->pNextHash; // Set Previous if (pPrevHash) pPrevHash->pNextHash = pNextHash; else m_prgHashTable[pProperty->pSymbol->wHashIndex] = pNextHash; // Free pCurrHash _FreePropertyChain(pCurrHash); // Set this after I set pCurr in case *ppNextHash is &pProperty if (ppNextHash) *ppNextHash = pNextHash; // One less property m_cProps--; #ifdef DEBUG fUnlinked = TRUE; #endif // Done break; } // Set Previous pPrevHash = pCurrHash; } // We better have found it Assert(fUnlinked); } // -------------------------------------------------------------------------------- // CMimePropertyContainer::DeleteProp // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::DeleteProp(LPCSTR pszName) { // Locals HRESULT hr=S_OK; LPPROPSYMBOL pSymbol; // Invalid Arg Assert(pszName); // Open Property Symbol CHECKHR(hr = g_pSymCache->HrOpenSymbol(pszName, FALSE, &pSymbol)); // Delete by symbol CHECKHR(hr = DeleteProp(pSymbol)); exit: // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::DeleteProp // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::DeleteProp(LPPROPSYMBOL pSymbol) { // Locals HRESULT hr=S_OK; LPPROPERTY pProperty; // Invalid Arg Assert(pSymbol); // Thread Safety EnterCriticalSection(&m_cs); // Find the property CHECKHR(hr = _HrFindProperty(pSymbol, &pProperty)); // Delete Prop _UnlinkProperty(pProperty); // Cascade Delete Dispatch if (ISTRIGGERED(pSymbol, IST_DELETEPROP)) { // Property Dispatch CHECKHR(hr = _HrCallSymbolTrigger(pSymbol, IST_DELETEPROP, 0, NULL)); } // Dirty if (!ISFLAGSET(pSymbol->dwFlags, MPF_NODIRTY)) FLAGSET(m_dwState, COSTATE_DIRTY); exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_DeleteLinkedParameters // -------------------------------------------------------------------------------- void CMimePropertyContainer::_DeleteLinkedParameters(LPPROPERTY pProperty) { // Locals HRESULT hrFind; FINDPROPERTY rFind; LPPROPERTY pParameter; // Invalid Arg Assert(pProperty && ISFLAGSET(pProperty->pSymbol->dwFlags, MPF_HASPARAMS)); // Initialize rFind ZeroMemory(&rFind, sizeof(FINDPROPERTY)); rFind.pszPrefix = "par:"; rFind.cchPrefix = 4; rFind.pszName = pProperty->pSymbol->pszName; rFind.cchName = pProperty->pSymbol->cchName; // Find First.. hrFind = _HrFindFirstProperty(&rFind, &pParameter); // While we find them, delete them while (SUCCEEDED(hrFind) && pParameter) { // Raid-13506 - Basically PID_ATT_FILENAME doesn't get removed when all other associated props are gone. if (ISTRIGGERED(pParameter->pSymbol, IST_DELETEPROP)) { // Call the Trigger _HrCallSymbolTrigger(pParameter->pSymbol, IST_DELETEPROP, 0, NULL); } // Remove the parameter _UnlinkProperty(pParameter, &rFind.pProperty); // Find Next hrFind = _HrFindNextProperty(&rFind, &pParameter); } } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_FExcept // -------------------------------------------------------------------------------- BOOL CMimePropertyContainer::_FExcept(LPPROPSYMBOL pSymbol, ULONG cNames, LPCSTR *prgszName) { // Verify the array for (ULONG i=0; idwPropId == STRTOPID(prgszName[i])) return TRUE; // Else if pSymbol is linked to prgszName[i] else if (pSymbol->pLink && pSymbol->pLink->dwPropId == STRTOPID(prgszName[i])) return TRUE; } // Otherwise, by name else { // Compare by name if (lstrcmpi(pSymbol->pszName, prgszName[i]) == 0) return TRUE; // Otherwise if pSymbol is linked to prgszName[i] else if (pSymbol->pLink && lstrcmpi(pSymbol->pLink->pszName, prgszName[i]) == 0) return TRUE; } } // Not Except return FALSE; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::DeleteExcept // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::DeleteExcept(ULONG cNames, LPCSTR *prgszName) { // Locals HRESULT hr=S_OK; LPPROPERTY pProperty; ULONG i; // Invalid Arg if ((0 == cNames && NULL != prgszName) || (NULL == prgszName && 0 != cNames)) return TrapError(E_INVALIDARG); // Thread Safety EnterCriticalSection(&m_cs); // Delete Everything if (0 == cNames) { // Free the PropTable _FreeHashTableElements(); } // Otherwise else { // Loop through the item table for (i=0; ipSymbol, cNames, prgszName)) _UnlinkProperty(pProperty, &pProperty); else pProperty = pProperty->pNextHash; } } } // Dirty FLAGSET(m_dwState, COSTATE_DIRTY); // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::QueryProp // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::QueryProp(LPCSTR pszName, LPCSTR pszCriteria, boolean fSubString, boolean fCaseSensitive) { // Locals HRESULT hr=S_OK; LPPROPSYMBOL pSymbol; // Invalid Arg Assert(pszName); // Open Property Symbol CHECKHR(hr = g_pSymCache->HrOpenSymbol(pszName, FALSE, &pSymbol)); // Get Property by Symbol CHECKHR(hr = QueryProp(pSymbol, pszCriteria, fSubString, fCaseSensitive)); exit: // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::QueryProp // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::QueryProp(LPPROPSYMBOL pSymbol, LPCSTR pszCriteria, boolean fSubString, boolean fCaseSensitive) { // Locals HRESULT hr=S_OK; LPPROPERTY pProperty, pCurrProp; LPCSTR pszSearch; MIMEVARIANT rValue; // Parameters if (NULL == pSymbol || NULL == pszCriteria) return TrapError(E_INVALIDARG); // Init STACKSTRING_DEFINE(rCritLower, 255); ZeroMemory(&rValue, sizeof(MIMEVARIANT)); // Init pszsearch pszSearch = pszCriteria; // Thread Safety EnterCriticalSection(&m_cs); // Find the Property if (FAILED(_HrFindProperty(pSymbol, &pProperty))) { hr = S_FALSE; goto exit; } // Need Lower Case...? if (TRUE == fSubString && FALSE == fCaseSensitive) { // Get Length ULONG cchCriteria = lstrlen(pszCriteria); // Get the length of pszCritieria STACKSTRING_SETSIZE(rCritLower, cchCriteria + 1); // Copy It CopyMemory(rCritLower.pszVal, pszCriteria, cchCriteria + 1); // Lower Case... CharLower(rCritLower.pszVal); // Set Search pszSearch = rCritLower.pszVal; } // Walk multiline properties... for (pCurrProp=pProperty; pCurrProp!=NULL; pCurrProp=pCurrProp->pNextValue) { // Better have the same symbol Assert(pCurrProp->pSymbol == pSymbol); // If Address... if (ISFLAGSET(pCurrProp->pSymbol->dwFlags, MPF_ADDRESS)) { // Better have an address group Assert(pCurrProp->pGroup); // Search the address group if (_HrQueryAddressGroup(pCurrProp, pszSearch, fSubString, fCaseSensitive) == S_OK) goto exit; } // Otherwise else { // Convert to a MVT_STRINGA rValue.type = MVT_STRINGA; // Convert to string CHECKHR(hr = HrConvertVariant(pCurrProp, CVF_NOALLOC, &rValue)); // Query String if (MimeOleQueryString(rValue.rStringA.pszVal, pszSearch, fSubString, fCaseSensitive) == S_OK) goto exit; // Cleanup MimeVariantFree(&rValue); } } // Not Equal hr = S_FALSE; exit: // Cleanup STACKSTRING_FREE(rCritLower); MimeVariantFree(&rValue); // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::GetCharset // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::GetCharset(LPHCHARSET phCharset) { // Invalid Arg if (NULL == phCharset) return TrapError(E_INVALIDARG); // Init *phCharset = NULL; // Thread Safety EnterCriticalSection(&m_cs); // No Charset... Assert(m_rOptions.pDefaultCharset); // Return *phCharset = m_rOptions.pDefaultCharset->hCharset; // Thread Safety LeaveCriticalSection(&m_cs); // Done return S_OK; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::SetCharset // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::SetCharset(HCHARSET hCharset, CSETAPPLYTYPE applytype) { // Locals HRESULT hr=S_OK; LPINETCSETINFO pCharset; LPCODEPAGEINFO pCodePage; MIMEVARIANT rValue; LPINETCSETINFO pCset; LPPROPERTY pProperty; // Invalid Arg if (NULL == hCharset) return TrapError(E_INVALIDARG); // Thread Safety EnterCriticalSection(&m_cs); // IE v. 5.0 37562 multiple charsets are ignored on inbound message // if we are already tagged and called with CSET_APPLY_UNTAGGED then don't overwrite // the existing charset. if(CSET_APPLY_UNTAGGED == applytype && ISFLAGSET(m_dwState, COSTATE_CSETTAGGED)) goto exit; // Lookiup Charset Info CHECKHR(hr = g_pInternat->HrOpenCharset(hCharset, &pCharset)); #ifdef OLD // See attachemnt to bug 40626 // RAID-22767 - FE-H : Athena Mail: Header should be encoded to the "EUC-KR" for the Korean if (SUCCEEDED(g_pInternat->HrFindCodePage(pCharset->cpiInternet, &pCodePage)) && pCodePage->dwMask & ILM_HEADERCSET) { // Map New Charset... if (SUCCEEDED(g_pInternat->HrOpenCharset(pCodePage->szHeaderCset, &pCset))) { // Example: When hCharset == ISO-2022-KR, we map to EUC-KR == hHeaderCset, and we use that as // the header of the message, but we set param charset=iso-2022-kr and encode the body // using iso-2022-kr. This is why rCsetInfo contains iso-2022-kr and not euc-kr. pCharset = pCset; } } #else // !OLD // We always use now WebCharSet (see attachment message to bug 40626 if (SUCCEEDED(g_pInternat->HrFindCodePage(pCharset->cpiInternet, &pCodePage)) && pCodePage->dwMask & ILM_WEBCSET) { // Map New Charset... if (SUCCEEDED(g_pInternat->HrOpenCharset(pCodePage->szWebCset, &pCset))) pCharset = pCset; } #endif // OLD // Setup a variant rValue.type = MVT_STRINGA; rValue.rStringA.pszVal = pCharset->szName; rValue.rStringA.cchVal = lstrlen(pCharset->szName); // Set the Charset Attribute SideAssert(SUCCEEDED(SetProp(SYM_PAR_CHARSET, 0, &rValue))); // Return m_rOptions.pDefaultCharset = pCharset; // Remove any specific charset information on each property if (CSET_APPLY_ALL == applytype && m_cProps > 0) { // Locals LPPROPERTY pCurrHash; LPPROPERTY pCurrValue; // Loop through the item table for (ULONG i=0; ipNextHash) { // Walk the multi-value chain for (pCurrValue=pCurrHash; pCurrValue!=NULL; pCurrValue=pCurrValue->pNextValue) { // This will force it to use the default pCurrValue->pCharset = NULL; } } } } // Raid-38725: FE: Selecting EUC does not immediately change the encoding of sender name in preview pane // // $$HACKHACK$$ - This block of code is a hack because it only works if an address group has been parsed // and not modified. Only in this case, will the new charset be applied to the addresses. for (pProperty=m_rAdrTable.pHead; pProperty!=NULL; pProperty=pProperty->pGroup->pNext) { // If the property has been parsed into addresses if (!ISFLAGSET(pProperty->dwState, PRSTATE_NEEDPARSE)) { // We should have an address group Assert(pProperty->pGroup); // If we have an address group and its dirty if (pProperty->pGroup && FALSE == pProperty->pGroup->fDirty) { // Free the curent list of parsed addresses _FreeAddressChain(pProperty->pGroup); // Not Dirty pProperty->pGroup->fDirty = FALSE; // Reset the parsing flag FLAGSET(pProperty->dwState, PRSTATE_NEEDPARSE); } } } // Tag It ? if (CSET_APPLY_TAG_ALL == applytype) { // Mark as being tagged FLAGSET(m_dwState, COSTATE_CSETTAGGED); } // Dirty FLAGSET(m_dwState, COSTATE_DIRTY); exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::GetParameters // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::GetParameters(LPCSTR pszName, ULONG *pcParams, LPMIMEPARAMINFO *pprgParam) { // Locals HRESULT hr=S_OK, hrFind; FINDPROPERTY rFind; LPMIMEPARAMINFO prgParam=NULL; ULONG cParams=0, cAlloc=0; LPSTR pszParamName; LPPROPERTY pParameter; MIMEVARIANT rValue; LPPROPSYMBOL pSymbol; // Parameters if (NULL == pszName || NULL == pcParams || NULL == pprgParam) return TrapError(E_INVALIDARG); // Init *pcParams = 0; *pprgParam = NULL; // Thread Safety EnterCriticalSection(&m_cs); // Find Symbol from pszName CHECKHR(hr = g_pSymCache->HrOpenSymbol(pszName, FALSE, &pSymbol)); // Initialize rFind ZeroMemory(&rFind, sizeof(FINDPROPERTY)); rFind.pszPrefix = "par:"; rFind.cchPrefix = 4; rFind.pszName = pSymbol->pszName; rFind.cchName = pSymbol->cchName; // Find First.. hrFind = _HrFindFirstProperty(&rFind, &pParameter); // While we find them, delete them while (SUCCEEDED(hrFind) && pParameter) { // Grow my array if (cParams + 1 >= cAlloc) { // Realloc CHECKHR(hr = HrRealloc((LPVOID *)&prgParam, sizeof(MIMEPARAMINFO) * (cAlloc + 5))); // Inc cAlloc cAlloc+=5; } // Get Parameter Name pszParamName = PszScanToCharA((LPSTR)pParameter->pSymbol->pszName, ':'); pszParamName++; pszParamName = PszScanToCharA(pszParamName, ':'); pszParamName++; // Copy Name CHECKALLOC(prgParam[cParams].pszName = PszDupA(pszParamName)); // Copy Data rValue.type = MVT_STRINGA; CHECKHR(hr = GetProp(pParameter->pSymbol, 0, &rValue)); // Save this prgParam[cParams].pszData = rValue.rStringA.pszVal; // Increment cParams cParams++; // Find Next hrFind = _HrFindNextProperty(&rFind, &pParameter); } // Return it *pcParams = cParams; *pprgParam = prgParam; exit: // Failure... if (FAILED(hr) && prgParam) g_pMoleAlloc->FreeParamInfoArray(cParams, prgParam, TRUE); // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } #ifndef WIN16 // -------------------------------------------------------------------------------- // CMimePropertyContainer::HrResolveURL // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::HrResolveURL(LPRESOLVEURLINFO pURL) { // Locals HRESULT hr=S_OK; LPWSTR pwszTemp=NULL; LPSTR pszBase=NULL; LPSTR pszContentID=NULL; LPSTR pszLocation=NULL; LPSTR pszAbsURL1=NULL; LPSTR pszAbsURL2=NULL; LPSTR pszT=NULL; ULONG cch; // Invalid Arg Assert(pURL); // Init Stack Strings STACKSTRING_DEFINE(rCleanCID, 255); // Thread Safety EnterCriticalSection(&m_cs); // Content-Location if(SUCCEEDED(GetPropW(SYM_HDR_CNTLOC, &pwszTemp))) { cch = lstrlenW(pwszTemp) + 1; if(SUCCEEDED(HrAlloc((LPVOID *)&pszLocation, cch * sizeof(WCHAR)))) WideCharToMultiByte(CP_ACP, 0, pwszTemp, -1, pszLocation, cch * sizeof(WCHAR), NULL, NULL); MemFree(pwszTemp); } // Content-ID if(SUCCEEDED(GetPropW(SYM_HDR_CNTID, &pwszTemp))) { cch = lstrlenW(pwszTemp) + 1; if(SUCCEEDED(HrAlloc((LPVOID *)&pszContentID, cch * sizeof(WCHAR)))) WideCharToMultiByte(CP_ACP, 0, pwszTemp, -1, pszContentID, cch * sizeof(WCHAR), NULL, NULL); MemFree(pwszTemp); } // Content-Base if(SUCCEEDED(GetPropW(SYM_HDR_CNTBASE, &pwszTemp))) { cch = lstrlenW(pwszTemp) + 1; if(SUCCEEDED(HrAlloc((LPVOID *)&pszBase, cch * sizeof(WCHAR)))) WideCharToMultiByte(CP_ACP, 0, pwszTemp, -1, pszBase, cch * sizeof(WCHAR), NULL, NULL); MemFree(pwszTemp); } // Both Null, no match if (!pszLocation && !pszContentID) { hr = TrapError(MIME_E_NOT_FOUND); goto exit; } // If URL is a CID if (TRUE == pURL->fIsCID) { // Locals ULONG cb; if(pszLocation) { // Match char for char if (MimeOleCompareUrl(pszLocation, TRUE, pURL->pszURL, FALSE) == S_OK) goto exit; } if(pszContentID) { // Match char for char minus cid: if (MimeOleCompareUrlSimple(pURL->pszURL, pszContentID) == S_OK) goto exit; // Dup the string CHECKALLOC(pszT = PszDupA(pURL->pszURL)); // Strip leading and trailing whitespace cb = lstrlen(pszT); UlStripWhitespace(pszT, TRUE, TRUE, &cb); // Get Stack Stream Read for STACKSTRING_SETSIZE(rCleanCID, cb + 4); // Format the string wnsprintf(rCleanCID.pszVal, (cb + 4), "<%s>", pszT); // Match char for char minus cid: if (MimeOleCompareUrlSimple(rCleanCID.pszVal, pszContentID) == S_OK) goto exit; } } // Otherwise, non-CID resolution else if (pszLocation) { // Raid-62579: Athena: Need to support MHTML content-base inheritance if (NULL == pszBase && pURL->pszInheritBase) { // Jimmy up a fake base pszBase = StrDupA(pURL->pszInheritBase); } // Part Has Base if (NULL != pszBase) { // Combine URLs CHECKHR(hr = MimeOleCombineURL(pszBase, lstrlen(pszBase), pszLocation, lstrlen(pszLocation), TRUE, &pszAbsURL1)); // URI has no base if (NULL == pURL->pszBase) { // Compare if (MimeOleCompareUrlSimple(pURL->pszURL, pszAbsURL1) == S_OK) goto exit; } // URI Has a Base else { // Combine URLs CHECKHR(hr = MimeOleCombineURL(pURL->pszBase, lstrlen(pURL->pszBase), pURL->pszURL, lstrlen(pURL->pszURL), FALSE, &pszAbsURL2)); // Compare if (MimeOleCompareUrlSimple(pszAbsURL1, pszAbsURL2) == S_OK) goto exit; } } // Part has no base else { // URI has no base if (NULL == pURL->pszBase) { // Compare if (MimeOleCompareUrl(pszLocation, TRUE, pURL->pszURL, FALSE) == S_OK) goto exit; } // URI Has a Base else { // Combine URLs CHECKHR(hr = MimeOleCombineURL(pURL->pszBase, lstrlen(pURL->pszBase), pURL->pszURL, lstrlen(pURL->pszURL), FALSE, &pszAbsURL2)); // Compare if (MimeOleCompareUrl(pszLocation, TRUE, pszAbsURL2, FALSE) == S_OK) goto exit; } } } // Not Found hr = TrapError(MIME_E_NOT_FOUND); exit: // Cleanup STACKSTRING_FREE(rCleanCID); MemFree(pszBase); MemFree(pszContentID); MemFree(pszLocation); MemFree(pszAbsURL1); MemFree(pszAbsURL2); MemFree(pszT); // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::IsContentType // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::IsContentType(LPCSTR pszPriType, LPCSTR pszSubType) { // Locals HRESULT hr=S_OK; // Wildcard everyting if (NULL == pszPriType && NULL == pszSubType) return S_OK; // Thread Safety EnterCriticalSection(&m_cs); // Get Known LPPROPERTY pCntType = m_prgIndex[PID_ATT_PRITYPE]; LPPROPERTY pSubType = m_prgIndex[PID_ATT_SUBTYPE]; // No Data if (NULL == pCntType || NULL == pSubType || !ISSTRINGA(&pCntType->rValue) || !ISSTRINGA(&pSubType->rValue)) { // Compare Against STR_CNT_TEXT if (pszPriType && lstrcmpi(pszPriType, STR_CNT_TEXT) != 0) { hr = S_FALSE; goto exit; } // Compare Against STR_CNT_TEXT if (pszSubType && lstrcmpi(pszSubType, STR_SUB_PLAIN) != 0) { hr = S_FALSE; goto exit; } } else { // Comparing pszPriType if (pszPriType && lstrcmpi(pszPriType, pCntType->rValue.rStringA.pszVal) != 0) { hr = S_FALSE; goto exit; } // Comparing pszSubType if (pszSubType && lstrcmpi(pszSubType, pSubType->rValue.rStringA.pszVal) != 0) { hr = S_FALSE; goto exit; } } exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::IsContentTypeW // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::IsContentTypeW(LPCWSTR pszPriType, LPCWSTR pszSubType) { // Locals HRESULT hr=S_OK; LPWSTR pszT1=NULL; LPWSTR pszT2=NULL; // Wildcard everyting if (NULL == pszPriType && NULL == pszSubType) return S_OK; // Thread Safety EnterCriticalSection(&m_cs); // Get Known LPPROPERTY pCntType = m_prgIndex[PID_ATT_PRITYPE]; LPPROPERTY pSubType = m_prgIndex[PID_ATT_SUBTYPE]; // No Data if (NULL == pCntType || NULL == pSubType || !ISSTRINGA(&pCntType->rValue) || !ISSTRINGA(&pSubType->rValue)) { // Compare Against STR_CNT_TEXT if (pszPriType && StrCmpIW(pszPriType, L"text") != 0) { hr = S_FALSE; goto exit; } // Compare Against STR_CNT_TEXT if (pszSubType && StrCmpIW(pszSubType, L"plain") != 0) { hr = S_FALSE; goto exit; } } else { // Compare pszPriType if (pszPriType) { // To Unicode IF_NULLEXIT(pszT1 = PszToUnicode(CP_ACP, pCntType->rValue.rStringA.pszVal)); // Compare if (StrCmpIW(pszPriType, pszT1) != 0) { hr = S_FALSE; goto exit; } } // Compare pszSubType if (pszSubType) { // To Unicode IF_NULLEXIT(pszT2 = PszToUnicode(CP_ACP, pSubType->rValue.rStringA.pszVal)); // Comparing pszSubType if (StrCmpIW(pszSubType, pszT2) != 0) { hr = S_FALSE; goto exit; } } } exit: // Thread Safety LeaveCriticalSection(&m_cs); // Cleanup MemFree(pszT1); MemFree(pszT2); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::Clone // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::Clone(IMimePropertySet **ppPropertySet) { // Locals HRESULT hr=S_OK; LPCONTAINER pContainer=NULL; // InvalidArg if (NULL == ppPropertySet) return TrapError(E_INVALIDARG); // Init *ppPropertySet = NULL; // Thread Safety EnterCriticalSection(&m_cs); // Ask the container to clone itself CHECKHR(hr = Clone(&pContainer)); // Bind to the IID_IMimeHeaderTable View CHECKHR(hr = pContainer->QueryInterface(IID_IMimePropertySet, (LPVOID *)ppPropertySet)); exit: // Cleanup SafeRelease(pContainer); // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::Clone // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::Clone(LPCONTAINER *ppContainer) { // Locals HRESULT hr=S_OK; LPCONTAINER pContainer=NULL; // Invalid ARg if (NULL == ppContainer) return TrapError(E_INVALIDARG); // Init *ppContainer = NULL; // Thread Safety EnterCriticalSection(&m_cs); // Create new container, NULL == no outer property set CHECKALLOC(pContainer = new CMimePropertyContainer); // Init that new container CHECKHR(hr = pContainer->InitNew()); // Interate the Properties CHECKHR(hr = _HrClonePropertiesTo(pContainer)); // If I have a stream, give it to the new table if (m_pStmLock) { // Just pass m_pStmLock into pTable pContainer->m_pStmLock = m_pStmLock; pContainer->m_pStmLock->AddRef(); pContainer->m_cbStart = m_cbStart; pContainer->m_cbSize = m_cbSize; } // Give it my state pContainer->m_dwState = m_dwState; // Give it my options pContainer->m_rOptions.pDefaultCharset = m_rOptions.pDefaultCharset; pContainer->m_rOptions.cbMaxLine = m_rOptions.cbMaxLine; pContainer->m_rOptions.fAllow8bit = m_rOptions.fAllow8bit; // Return Clone (*ppContainer) = pContainer; (*ppContainer)->AddRef(); exit: // Cleanup SafeRelease(pContainer); // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_HrClonePropertiesTo // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::_HrClonePropertiesTo(LPCONTAINER pContainer) { // Locals HRESULT hr=S_OK; LPPROPERTY pCurrHash, pCurrValue, pDestProp; // Invalid Arg Assert(pContainer); // Loop through the item table for (ULONG i=0; ipNextHash) { // Walk multiple Values for (pCurrValue=pCurrHash; pCurrValue!=NULL; pCurrValue=pCurrValue->pNextValue) { // Linked Attributes are Not Copied if (ISFLAGSET(pCurrValue->pSymbol->dwFlags, MPF_ATTRIBUTE) && NULL != pCurrValue->pSymbol->pLink) continue; // Does the Property need to be parsed ? if (ISFLAGSET(pCurrValue->pSymbol->dwFlags, MPF_ADDRESS)) { // Make sure the address is parsed CHECKHR(hr = _HrParseInternetAddress(pCurrValue)); } // Insert Copy of pCurrValue into pContiner CHECKHR(hr = pContainer->HrInsertCopy(pCurrValue, FALSE)); } } } exit: // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_HrCopyProperty // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::_HrCopyProperty(LPPROPERTY pProperty, LPCONTAINER pDest, BOOL fFromMovePropos) { // Locals HRESULT hr=S_OK; LPPROPERTY pCurrValue; // Walk multiple Values for (pCurrValue=pProperty; pCurrValue!=NULL; pCurrValue=pCurrValue->pNextValue) { // Does the Property need to be parsed ? if (ISFLAGSET(pCurrValue->pSymbol->dwFlags, MPF_ADDRESS)) { // Make sure the address is parsed CHECKHR(hr = _HrParseInternetAddress(pCurrValue)); } // Insert pProperty into pDest CHECKHR(hr = pDest->HrInsertCopy(pCurrValue, fFromMovePropos)); } // If pCurrHash has Parameters, copy those over as well if (ISFLAGSET(pProperty->pSymbol->dwFlags, MPF_HASPARAMS)) { // Copy Parameters CHECKHR(hr = _HrCopyParameters(pProperty, pDest)); } exit: // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_HrCopyParameters // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::_HrCopyParameters(LPPROPERTY pProperty, LPCONTAINER pDest) { // Locals HRESULT hr=S_OK; HRESULT hrFind; FINDPROPERTY rFind; LPPROPERTY pParameter; // Invalid Arg Assert(pProperty && ISFLAGSET(pProperty->pSymbol->dwFlags, MPF_HASPARAMS)); // Initialize rFind ZeroMemory(&rFind, sizeof(FINDPROPERTY)); rFind.pszPrefix = "par:"; rFind.cchPrefix = 4; rFind.pszName = pProperty->pSymbol->pszName; rFind.cchName = pProperty->pSymbol->cchName; // Find First.. hrFind = _HrFindFirstProperty(&rFind, &pParameter); // While we find them, delete them while (SUCCEEDED(hrFind) && pParameter) { // Remove the parameter CHECKHR(hr = pDest->HrInsertCopy(pParameter, FALSE)); // Find Next hrFind = _HrFindNextProperty(&rFind, &pParameter); } exit: // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::HrInsertCopy // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::HrInsertCopy(LPPROPERTY pSource, BOOL fFromMovePropos) { // Locals HRESULT hr=S_OK; LPPROPERTY pDest; LPMIMEADDRESS pAddress; LPMIMEADDRESS pNew; // Invalid Arg Assert(pSource); // Thread Safety EnterCriticalSection(&m_cs); // Append a new property to the CHECKHR(hr = _HrAppendProperty(pSource->pSymbol, &pDest)); // If this is an address... if (ISFLAGSET(pSource->pSymbol->dwFlags, MPF_ADDRESS)) { // Both Address Group Better Exist Assert(pSource->pGroup && pDest->pGroup && !ISFLAGSET(pSource->dwState, PRSTATE_NEEDPARSE)); // Loop Infos... for (pAddress=pSource->pGroup->pHead; pAddress!=NULL; pAddress=pAddress->pNext) { // Append pDest->pGroup CHECKHR(hr = _HrAppendAddressGroup(pDest->pGroup, &pNew)); // Copy Current to New CHECKHR(hr = HrMimeAddressCopy(pAddress, pNew)); } } // Otheriwse, just set the variant data on pDest else { // Set It CHECKHR(hr = _HrSetPropertyValue(pDest, ((pSource->ietValue == IET_ENCODED) ? PDF_ENCODED : 0), &pSource->rValue, fFromMovePropos)); } // Copy the State pDest->dwState = pSource->dwState; pDest->dwRowNumber = pSource->dwRowNumber; pDest->cboffStart = pSource->cboffStart; pDest->cboffColon = pSource->cboffColon; pDest->cboffEnd = pSource->cboffEnd; exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::CopyProps // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::CopyProps(ULONG cNames, LPCSTR *prgszName, IMimePropertySet *pPropertySet) { // Locals HRESULT hr=S_OK; ULONG i; LPPROPSYMBOL pSymbol; LPPROPERTY pProperty, pCurrValue, pCurrHash, pNextHash; LPCONTAINER pDest=NULL; // Invalid ARg if ((0 == cNames && NULL != prgszName) || (NULL == prgszName && 0 != cNames) || NULL == pPropertySet) return TrapError(E_INVALIDARG); // Thread Safety EnterCriticalSection(&m_cs); // QI for destination continer CHECKHR(hr = pPropertySet->BindToObject(IID_CMimePropertyContainer, (LPVOID *)&pDest)); // Raid-62016: CDO: Bodypart promotion causes loss of charset // Delete All Properties if (0 == cNames) { // Loop through the item table for (i=0; ipNextHash) { // Delete from Destination Container pDest->DeleteProp(pCurrHash->pSymbol); } } } // Otherwise, copy selected properties else { // Call Into InetPropSet for (i=0; iHrOpenSymbol(prgszName[i], FALSE, &pSymbol))) { // Find the Property if (SUCCEEDED(_HrFindProperty(pSymbol, &pProperty))) { // Delete from Destination Container pDest->DeleteProp(pSymbol); } } } } // Move All Properties if (0 == cNames) { // Loop through the item table for (i=0; ipNextHash) { // Copy the Property To CHECKHR(hr = _HrCopyProperty(pCurrHash, pDest, FALSE)); } } } // Otherwise, copy selected properties else { // Call Into InetPropSet for (i=0; iHrOpenSymbol(prgszName[i], FALSE, &pSymbol))) { // Find the Property if (SUCCEEDED(_HrFindProperty(pSymbol, &pProperty))) { // Copy the Property To CHECKHR(hr = _HrCopyProperty(pProperty, pDest, FALSE)); } } } } exit: // Cleanup SafeRelease(pDest); // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::MoveProps // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::MoveProps(ULONG cNames, LPCSTR *prgszName, IMimePropertySet *pPropertySet) { // Locals HRESULT hr=S_OK; ULONG i; LPPROPSYMBOL pSymbol; LPPROPERTY pProperty; LPPROPERTY pCurrHash; LPCONTAINER pDest=NULL; // Invalid ARg if ((0 == cNames && NULL != prgszName) || (NULL == prgszName && 0 != cNames) || NULL == pPropertySet) return TrapError(E_INVALIDARG); // Thread Safety EnterCriticalSection(&m_cs); // QI for destination continer CHECKHR(hr = pPropertySet->BindToObject(IID_CMimePropertyContainer, (LPVOID *)&pDest)); // Raid-62016: CDO: Bodypart promotion causes loss of charset // Delete Properties in the Destination First if (0 == cNames) { // Loop through the item table for (i=0; ipNextHash) { // Delete from Destination Container pDest->DeleteProp(pCurrHash->pSymbol); } } } // Otherwise, selective delete else { // Call Into InetPropSet for (i=0; iHrOpenSymbol(prgszName[i], FALSE, &pSymbol))) { // Find the Property if (SUCCEEDED(_HrFindProperty(pSymbol, &pProperty))) { // Delete from Destination Container pDest->DeleteProp(pSymbol); } } } } // Move All Properties if (0 == cNames) { // Loop through the item table for (i=0; iHrOpenSymbol(prgszName[i], FALSE, &pSymbol))) { // Find the Property if (SUCCEEDED(_HrFindProperty(pSymbol, &pProperty))) { // Copy the Property To CHECKHR(hr = _HrCopyProperty(pProperty, pDest, FALSE)); // Delete pProperty _UnlinkProperty(pProperty); } } } } // Dirty FLAGSET(m_dwState, COSTATE_DIRTY); exit: // Cleanup SafeRelease(pDest); // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::SetOption // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::SetOption(const TYPEDID oid, LPCPROPVARIANT pVariant) { // Locals HRESULT hr=S_OK; // check params if (NULL == pVariant) return TrapError(E_INVALIDARG); // Thread Safety EnterCriticalSection(&m_cs); // Handle Optid switch(oid) { // ----------------------------------------------------------------------- case OID_HEADER_RELOAD_TYPE: if (pVariant->ulVal > RELOAD_HEADER_REPLACE) { hr = TrapError(MIME_E_INVALID_OPTION_VALUE); goto exit; } if (m_rOptions.ReloadType != (RELOADTYPE)pVariant->ulVal) { FLAGSET(m_dwState, COSTATE_DIRTY); m_rOptions.ReloadType = (RELOADTYPE)pVariant->ulVal; } break; // ----------------------------------------------------------------------- case OID_NO_DEFAULT_CNTTYPE: if (m_rOptions.fNoDefCntType != (pVariant->boolVal ? TRUE : FALSE)) m_rOptions.fNoDefCntType = pVariant->boolVal ? TRUE : FALSE; break; // ----------------------------------------------------------------------- case OID_ALLOW_8BIT_HEADER: if (m_rOptions.fAllow8bit != (pVariant->boolVal ? TRUE : FALSE)) { FLAGSET(m_dwState, COSTATE_DIRTY); m_rOptions.fAllow8bit = pVariant->boolVal ? TRUE : FALSE; } break; // ----------------------------------------------------------------------- case OID_CBMAX_HEADER_LINE: if (pVariant->ulVal < MIN_CBMAX_HEADER_LINE || pVariant->ulVal > MAX_CBMAX_HEADER_LINE) { hr = TrapError(MIME_E_INVALID_OPTION_VALUE); goto exit; } if (m_rOptions.cbMaxLine != pVariant->ulVal) { FLAGSET(m_dwState, COSTATE_DIRTY); m_rOptions.cbMaxLine = pVariant->ulVal; } break; // ----------------------------------------------------------------------- case OID_SAVE_FORMAT: if (SAVE_RFC822 != pVariant->ulVal && SAVE_RFC1521 != pVariant->ulVal) { hr = TrapError(MIME_E_INVALID_OPTION_VALUE); goto exit; } if (m_rOptions.savetype != (MIMESAVETYPE)pVariant->ulVal) { FLAGSET(m_dwState, COSTATE_DIRTY); m_rOptions.savetype = (MIMESAVETYPE)pVariant->ulVal; } break; // ----------------------------------------------------------------------- default: hr = TrapError(MIME_E_INVALID_OPTION_ID); goto exit; } exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::GetOption // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::GetOption(const TYPEDID oid, LPPROPVARIANT pVariant) { // Locals HRESULT hr=S_OK; // check params if (NULL == pVariant) return TrapError(E_INVALIDARG); pVariant->vt = TYPEDID_TYPE(oid); // Thread Safety EnterCriticalSection(&m_cs); // Handle Optid switch(oid) { // ----------------------------------------------------------------------- case OID_HEADER_RELOAD_TYPE: pVariant->ulVal = m_rOptions.ReloadType; break; // ----------------------------------------------------------------------- case OID_NO_DEFAULT_CNTTYPE: pVariant->boolVal = (VARIANT_BOOL) !!m_rOptions.fNoDefCntType; break; // ----------------------------------------------------------------------- case OID_ALLOW_8BIT_HEADER: pVariant->boolVal = (VARIANT_BOOL) !!m_rOptions.fAllow8bit; break; // ----------------------------------------------------------------------- case OID_CBMAX_HEADER_LINE: pVariant->ulVal = m_rOptions.cbMaxLine; break; // ----------------------------------------------------------------------- case OID_SAVE_FORMAT: pVariant->ulVal = (ULONG)m_rOptions.savetype; break; // ----------------------------------------------------------------------- default: pVariant->vt = VT_NULL; hr = TrapError(MIME_E_INVALID_OPTION_ID); goto exit; } exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::DwGetMessageFlags // -------------------------------------------------------------------------------- DWORD CMimePropertyContainer::DwGetMessageFlags(BOOL fHideTnef) { // Locals DWORD dwFlags=0; // Thread Safety EnterCriticalSection(&m_cs); // Get pritype/subtype LPCSTR pszPriType = PSZDEFPROPSTRINGA(m_prgIndex[PID_ATT_PRITYPE], STR_CNT_TEXT); LPCSTR pszSubType = PSZDEFPROPSTRINGA(m_prgIndex[PID_ATT_SUBTYPE], STR_SUB_PLAIN); LPCSTR pszCntDisp = PSZDEFPROPSTRINGA(m_prgIndex[PID_HDR_CNTDISP], STR_DIS_INLINE); // Mime if (m_prgIndex[PID_HDR_MIMEVER]) FLAGSET(dwFlags, IMF_MIME); // VoiceMail if (S_OK == IsPropSet(STR_HDR_XVOICEMAIL)) FLAGSET(dwFlags, IMF_VOICEMAIL); // IMF_NEWS if (m_prgIndex[PID_HDR_XNEWSRDR] || m_prgIndex[PID_HDR_NEWSGROUPS] || m_prgIndex[PID_HDR_NEWSGROUP] || m_prgIndex[PID_HDR_PATH]) FLAGSET(dwFlags, IMF_NEWS); // text if (lstrcmpi(pszPriType, STR_CNT_TEXT) == 0) { // There is text FLAGSET(dwFlags, IMF_TEXT); // text/plain if (lstrcmpi(pszSubType, STR_SUB_PLAIN) == 0) FLAGSET(dwFlags, IMF_PLAIN); // text/html else if (lstrcmpi(pszSubType, STR_SUB_HTML) == 0) FLAGSET(dwFlags, IMF_HTML); // text/enriched = text/html else if (lstrcmpi(pszSubType, STR_SUB_ENRICHED) == 0) FLAGSET(dwFlags, IMF_HTML); // text/v-card else if (lstrcmpi(pszSubType, STR_SUB_VCARD) == 0) FLAGSET(dwFlags, IMF_HASVCARD); } // multipart else if (lstrcmpi(pszPriType, STR_CNT_MULTIPART) == 0) { // Multipart FLAGSET(dwFlags, IMF_MULTIPART); // multipart/related if (lstrcmpi(pszSubType, STR_SUB_RELATED) == 0) FLAGSET(dwFlags, IMF_MHTML); // multipart/signed else if (0 == lstrcmpi(pszSubType, STR_SUB_SIGNED)) if (IsSMimeProtocol(this)) FLAGSET(dwFlags, IMF_SIGNED | IMF_SECURE); } // message/partial else if (lstrcmpi(pszPriType, STR_CNT_MESSAGE) == 0 && lstrcmpi(pszSubType, STR_SUB_PARTIAL) == 0) FLAGSET(dwFlags, IMF_PARTIAL); // application else if (lstrcmpi(pszPriType, STR_CNT_APPLICATION) == 0) { // application/ms-tnef if (0 == lstrcmpi(pszSubType, STR_SUB_MSTNEF)) FLAGSET(dwFlags, IMF_TNEF); // application/x-pkcs7-mime else if (0 == lstrcmpi(pszSubType, STR_SUB_XPKCS7MIME) || 0 == lstrcmpi(pszSubType, STR_SUB_PKCS7MIME)) // nonstandard FLAGSET(dwFlags, IMF_SECURE); } // Raid-37086 - Cset Tagged if (ISFLAGSET(m_dwState, COSTATE_CSETTAGGED)) FLAGSET(dwFlags, IMF_CSETTAGGED); // Attachment... if (!ISFLAGSET(dwFlags, IMF_MULTIPART) && (FALSE == fHideTnef || !ISFLAGSET(dwFlags, IMF_TNEF))) { // Marked as an attachment ? if (!ISFLAGSET(dwFlags, IMF_HASVCARD) && !ISFLAGSET(dwFlags, IMF_SECURE) && 0 != lstrcmpi(pszSubType, STR_SUB_PKCS7SIG) && 0 != lstrcmpi(pszSubType, STR_SUB_XPKCS7SIG)) // Raid-1960 { // Not Rendered Yet if (NULL == m_prgIndex[PID_ATT_RENDERED] || 0 == m_prgIndex[PID_ATT_RENDERED]->rValue.rVariant.ulVal) { // Marked as an Attachment if (lstrcmpi(pszCntDisp, STR_DIS_ATTACHMENT) == 0) FLAGSET(dwFlags, IMF_ATTACHMENTS); // Is there a Content-Type: xxx; name=xxx else if (NULL != m_prgIndex[PID_PAR_NAME]) FLAGSET(dwFlags, IMF_ATTACHMENTS); // Is there a Content-Disposition: xxx; filename=xxx else if (NULL != m_prgIndex[PID_PAR_FILENAME]) FLAGSET(dwFlags, IMF_ATTACHMENTS); // Else if it is not marked as text else if (ISFLAGSET(dwFlags, IMF_TEXT) == FALSE) FLAGSET(dwFlags, IMF_ATTACHMENTS); // If not text/plain and not text/html else if (lstrcmpi(pszSubType, STR_SUB_PLAIN) != 0 && lstrcmpi(pszSubType, STR_SUB_HTML) != 0 && lstrcmpi(pszSubType, STR_SUB_ENRICHED) != 0) FLAGSET(dwFlags, IMF_ATTACHMENTS); } } } // Thread Safety LeaveCriticalSection(&m_cs); // Done return dwFlags; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::GetEncodingType // -------------------------------------------------------------------------------- ENCODINGTYPE CMimePropertyContainer::GetEncodingType(void) { // Locals ENCODINGTYPE ietEncoding=IET_7BIT; // Thread Safety EnterCriticalSection(&m_cs); // Get pritype/subtype LPPROPERTY pCntXfer = m_prgIndex[PID_HDR_CNTXFER]; // Do we have data the I like ? if (pCntXfer && ISSTRINGA(&pCntXfer->rValue)) { // Local CStringParser cString; // cString... cString.Init(pCntXfer->rValue.rStringA.pszVal, pCntXfer->rValue.rStringA.cchVal, PSF_NOTRAILWS | PSF_NOFRONTWS | PSF_NOCOMMENTS); // Parse to end, remove white space and comments SideAssert('\0' == cString.ChParse("")); // Loop the table for (ULONG i=0; iHrOpenSymbol(pszHeader, TRUE, ppSymbol)); exit: // Cleanup if (pszHeader != szHeader) SafeMemFree(pszHeader); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_HrParseInlineHeaderName // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::_HrParseInlineHeaderName(LPCSTR pszData, LPSTR pszScratch, ULONG cchScratch, LPSTR *ppszHeader, ULONG *pcboffColon) { // Locals HRESULT hr=S_OK; LPSTR psz=(LPSTR)pszData, pszStart; ULONG i=0; // Invalid Arg Assert(pszData && pszScratch && ppszHeader && pcboffColon); // Lets Parse the name out and find the symbol while (*psz && (' ' == *psz || '\t' == *psz)) { i++; psz++; } // Done if ('\0' == *psz) { hr = TrapError(MIME_E_INVALID_HEADER_NAME); goto exit; } // Seek to the colon pszStart = psz; while (*psz && ':' != *psz) { i++; psz++; } // Set Colon Position (*pcboffColon) = i; // Done if ('\0' == *psz || 0 == i) { hr = TrapError(MIME_E_INVALID_HEADER_NAME); goto exit; } // Copy the name if (i + 1 <= cchScratch) *ppszHeader = pszScratch; // Otherwise, allocate else { // Allocate space for the name *ppszHeader = PszAllocA(i + 1); if (NULL == *ppszHeader) { hr = TrapError(E_OUTOFMEMORY); goto exit; } } // Copy the data CopyMemory(*ppszHeader, pszStart, i); // Null *((*ppszHeader) + i) = '\0'; exit: // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::FindFirstRow // -------------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::FindFirstRow(LPFINDHEADER pFindHeader, LPHHEADERROW phRow) { // Invalid Arg if (NULL == pFindHeader) return TrapError(E_INVALIDARG); // Init pFindHeader pFindHeader->dwReserved = 0; // FindNext return FindNextRow(pFindHeader, phRow); } // -------------------------------------------------------------------------------- // CMimePropertyContainer::FindNextRow // -------------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::FindNextRow(LPFINDHEADER pFindHeader, LPHHEADERROW phRow) { // Locals HRESULT hr=S_OK; LPPROPERTY pRow; // InvalidArg if (NULL == pFindHeader || NULL == phRow) return TrapError(E_INVALIDARG); // Init *phRow = NULL; // Thread Safety EnterCriticalSection(&m_cs); // Loop through the table for (ULONG i=pFindHeader->dwReserved; ipszHeader || lstrcmpi(pRow->pSymbol->pszName, pFindHeader->pszHeader) == 0) { // Save Index of next item to search pFindHeader->dwReserved = i + 1; // Return the handle *phRow = pRow->hRow; // Done goto exit; } } // Not Found pFindHeader->dwReserved = m_rHdrTable.cRows; hr = MIME_E_NOT_FOUND; exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::CountRows // -------------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::CountRows(LPCSTR pszHeader, ULONG *pcRows) { // Locals LPPROPERTY pRow; // InvalidArg if (NULL == pcRows) return TrapError(E_INVALIDARG); // Init *pcRows = 0; // Thread Safety EnterCriticalSection(&m_cs); // Loop through the table for (ULONG i=0; ipSymbol->pszName, pszHeader) == 0) (*pcRows)++; } // Thread Safety LeaveCriticalSection(&m_cs); // Done return S_OK; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::AppendRow // -------------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::AppendRow(LPCSTR pszHeader, DWORD dwFlags, LPCSTR pszData, ULONG cchData, LPHHEADERROW phRow) { // Locals HRESULT hr=S_OK; LPPROPSYMBOL pSymbol=NULL; ULONG cboffColon; LPPROPERTY pProperty; // InvalidArg if (NULL == pszData || '\0' != pszData[cchData]) return TrapError(E_INVALIDARG); // Init if (phRow) *phRow = NULL; // Thread Safety EnterCriticalSection(&m_cs); // If we have a header, lookup the symbol if (pszHeader) { // HTF_NAMEINDATA better not be set Assert(!ISFLAGSET(dwFlags, HTF_NAMEINDATA)); // Lookup the symbol CHECKHR(hr = g_pSymCache->HrOpenSymbol(pszHeader, TRUE, &pSymbol)); // Create a row CHECKHR(hr = _HrAppendProperty(pSymbol, &pProperty)); // Set the Data on this row CHECKHR(hr = SetRowData(pProperty->hRow, dwFlags, pszData, cchData)); } // Otherwise... else if (ISFLAGSET(dwFlags, HTF_NAMEINDATA)) { // GetInlineSymbol CHECKHR(hr = _HrGetInlineSymbol(pszData, &pSymbol, &cboffColon)); // Create a row CHECKHR(hr = _HrAppendProperty(pSymbol, &pProperty)); // Remove IHF_NAMELINE FLAGCLEAR(dwFlags, HTF_NAMEINDATA); // Set the Data on this row Assert(cboffColon + 1 < cchData); CHECKHR(hr = SetRowData(pProperty->hRow, dwFlags, pszData + cboffColon + 1, cchData - cboffColon - 1)); } // Otherwise, failed else { hr = TrapError(E_INVALIDARG); goto exit; } // Return phRow if (phRow && pProperty) *phRow = pProperty->hRow; exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::DeleteRow // -------------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::DeleteRow(HHEADERROW hRow) { // Locals HRESULT hr=S_OK; LPPROPERTY pRow; // Thread Safety EnterCriticalSection(&m_cs); // Validate the Handle CHECKEXP(_FIsValidHRow(hRow) == FALSE, MIME_E_INVALID_HANDLE); // Get the row pRow = PRowFromHRow(hRow); // Standard Delete Prop CHECKHR(hr = DeleteProp(pRow->pSymbol)); exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::GetRowData // -------------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::GetRowData(HHEADERROW hRow, DWORD dwFlags, LPSTR *ppszData, ULONG *pcchData) { // Locals HRESULT hr=S_OK; ULONG cchData=0; LPPROPERTY pRow; MIMEVARIANT rValue; DWORD dwPropFlags; // Init if (ppszData) *ppszData = NULL; if (pcchData) *pcchData = 0; // Thread Safety EnterCriticalSection(&m_cs); // Validate the Handle CHECKEXP(_FIsValidHRow(hRow) == FALSE, MIME_E_INVALID_HANDLE); // Get the row pRow = PRowFromHRow(hRow); // Compute dwPropFlags dwPropFlags = PDF_HEADERFORMAT | ((dwFlags & HTF_NAMEINDATA) ? PDF_NAMEINDATA : 0); // Speicify data type rValue.type = MVT_STRINGA; // Ask the value for the data CHECKHR(hr = _HrGetPropertyValue(pRow, dwPropFlags, &rValue)); // Want Length cchData = rValue.rStringA.cchVal; // Want the data if (ppszData) { *ppszData = rValue.rStringA.pszVal; rValue.rStringA.pszVal = NULL; } // Else Free It else SafeMemFree(rValue.rStringA.pszVal); // Verify the NULL Assert(ppszData ? '\0' == *((*ppszData) + cchData) : TRUE); // Return Length ? if (pcchData) *pcchData = cchData; exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::SetRowData // -------------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::SetRowData(HHEADERROW hRow, DWORD dwFlags, LPCSTR pszData, ULONG cchData) { // Locals HRESULT hr=S_OK; LPPROPERTY pRow; MIMEVARIANT rValue; ULONG cboffColon; LPPROPSYMBOL pSymbol; LPSTR psz=(LPSTR)pszData; // InvalidArg if (NULL == pszData || '\0' != pszData[cchData]) return TrapError(E_INVALIDARG); // Thread Safety EnterCriticalSection(&m_cs); // Validate the Handle CHECKEXP(_FIsValidHRow(hRow) == FALSE, MIME_E_INVALID_HANDLE); // Get the row pRow = PRowFromHRow(hRow); // If HTF_NAMEINDATA if (ISFLAGSET(dwFlags, HTF_NAMEINDATA)) { // Extract the name CHECKHR(hr = _HrGetInlineSymbol(pszData, &pSymbol, &cboffColon)); // Symbol Must be the same if (pRow->pSymbol != pSymbol) { hr = TrapError(E_FAIL); goto exit; } // Adjust pszData Assert(cboffColon < cchData); psz = (LPSTR)(pszData + cboffColon + 1); cchData = cchData - cboffColon - 1; Assert(psz[cchData] == '\0'); } // Setup the variant rValue.type = MVT_STRINGA; rValue.rStringA.pszVal = psz; rValue.rStringA.cchVal = cchData; // Tell value about the new row data CHECKHR(hr = _HrSetPropertyValue(pRow, 0, &rValue, FALSE)); // Clear Position Information pRow->cboffStart = 0; pRow->cboffColon = 0; pRow->cboffEnd = 0; exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::GetRowInfo // -------------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::GetRowInfo(HHEADERROW hRow, LPHEADERROWINFO pInfo) { // Locals HRESULT hr=S_OK; LPPROPERTY pRow; // InvalidArg if (NULL == pInfo) return TrapError(E_INVALIDARG); // Thread Safety EnterCriticalSection(&m_cs); // Validate the Handle CHECKEXP(_FIsValidHRow(hRow) == FALSE, MIME_E_INVALID_HANDLE); // Get the row pRow = PRowFromHRow(hRow); // Copy the row info pInfo->dwRowNumber = pRow->dwRowNumber; pInfo->cboffStart = pRow->cboffStart; pInfo->cboffColon = pRow->cboffColon; pInfo->cboffEnd = pRow->cboffEnd; exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::SetRowNumber // -------------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::SetRowNumber(HHEADERROW hRow, DWORD dwRowNumber) { // Locals HRESULT hr=S_OK; LPPROPERTY pRow; // Thread Safety EnterCriticalSection(&m_cs); // Validate the Handle CHECKEXP(_FIsValidHRow(hRow) == FALSE, MIME_E_INVALID_HANDLE); // Get the row pRow = PRowFromHRow(hRow); // Copy the row info pRow->dwRowNumber = dwRowNumber; exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::EnumRows // -------------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::EnumRows(LPCSTR pszHeader, DWORD dwFlags, IMimeEnumHeaderRows **ppEnum) { // Locals HRESULT hr=S_OK; ULONG i, iEnum=0, cEnumCount; LPENUMHEADERROW pEnumRow=NULL; LPPROPERTY pRow; CMimeEnumHeaderRows *pEnum=NULL; LPROWINDEX prgIndex=NULL; ULONG cRows; // check params if (NULL == ppEnum) return TrapError(E_INVALIDARG); // Init *ppEnum = NULL; // Thread Safety EnterCriticalSection(&m_cs); // This builds an inverted index on the header rows sorted by postion weight CHECKHR(hr = _HrGetHeaderTableSaveIndex(&cRows, &prgIndex)); // Lets Count the Rows CHECKHR(hr = CountRows(pszHeader, &cEnumCount)); // Allocate pEnumRow CHECKALLOC(pEnumRow = (LPENUMHEADERROW)g_pMalloc->Alloc(cEnumCount * sizeof(ENUMHEADERROW))); // ZeroInit ZeroMemory(pEnumRow, cEnumCount * sizeof(ENUMHEADERROW)); // Loop through the rows for (i=0; ipSymbol->pszName) == 0) { // Valide Assert(iEnum < cEnumCount); // Set the symbol on this enum row pEnumRow[iEnum].dwReserved = (DWORD_PTR)pRow->pSymbol; // Lets always give the handle pEnumRow[iEnum].hRow = pRow->hRow; // If Enumerating only handles... if (!ISFLAGSET(dwFlags, HTF_ENUMHANDLESONLY)) { // Get the data for this enum row CHECKHR(hr = GetRowData(pRow->hRow, dwFlags, &pEnumRow[iEnum].pszData, &pEnumRow[iEnum].cchData)); } // Increment iEnum iEnum++; } } // Allocate CHECKALLOC(pEnum = new CMimeEnumHeaderRows); // Initialize CHECKHR(hr = pEnum->HrInit(0, dwFlags, cEnumCount, pEnumRow, FALSE)); // Don't Free pEnumRow pEnumRow = NULL; // Return it (*ppEnum) = (IMimeEnumHeaderRows *)pEnum; (*ppEnum)->AddRef(); exit: // Cleanup SafeRelease(pEnum); SafeMemFree(prgIndex); if (pEnumRow) g_pMoleAlloc->FreeEnumHeaderRowArray(cEnumCount, pEnumRow, TRUE); // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::Clone // -------------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::Clone(IMimeHeaderTable **ppTable) { // Locals HRESULT hr=S_OK; LPCONTAINER pContainer=NULL; // InvalidArg if (NULL == ppTable) return TrapError(E_INVALIDARG); // Init *ppTable = NULL; // Thread Safety EnterCriticalSection(&m_cs); // Ask the container to clone itself CHECKHR(hr = Clone(&pContainer)); // Bind to the IID_IMimeHeaderTable View CHECKHR(hr = pContainer->QueryInterface(IID_IMimeHeaderTable, (LPVOID *)ppTable)); exit: // Cleanup SafeRelease(pContainer); // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_HrSaveAddressGroup // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::_HrSaveAddressGroup(LPPROPERTY pProperty, IStream *pStream, ULONG *pcAddrsWrote, ADDRESSFORMAT format, VARTYPE vtFormat) { // Locals HRESULT hr=S_OK; LPMIMEADDRESS pAddress; // Invalid Arg Assert(pProperty && pProperty->pGroup && pStream && pcAddrsWrote); Assert(!ISFLAGSET(pProperty->dwState, PRSTATE_NEEDPARSE)); // Loop Infos... for (pAddress=pProperty->pGroup->pHead; pAddress!=NULL; pAddress=pAddress->pNext) { // Multibyte if (VT_LPSTR == vtFormat) { // Tell the Address Info object to write its display information CHECKHR(hr = _HrSaveAddressA(pProperty, pAddress, pStream, pcAddrsWrote, format)); } // Otherwise else { // Validate Assert(VT_LPWSTR == vtFormat); // Tell the Address Info object to write its display information CHECKHR(hr = _HrSaveAddressW(pProperty, pAddress, pStream, pcAddrsWrote, format)); } // Increment cAddresses Count (*pcAddrsWrote)++; } exit: // Done return hr; } /////////////////////////////////////////////////////////////////////////////// // bIsInLineEncodedA // // [PaulHi] 6/29/99 // Helper function to determine if single byte string contains RFC1522 inline // encoding. // Format is: "=?[charset]?[encoding]?[data]?=". /////////////////////////////////////////////////////////////////////////////// BOOL bIsInLineEncodedA(LPCSTR pcszName) { Assert(pcszName); int nState = 0; // 0-Begin,charset; 1-encoding; 2-data; 3-Ending while(*pcszName) { // Check for beginning delimiter. if ( (*pcszName == '=') && (*(pcszName+1) == '?') ) { // Set/reset state nState = 1; pcszName += 1; // Skip past. } else { switch (nState) { case 1: case 2: // Find encoding, data bodies. if (*pcszName == '?') { ++nState; ++pcszName; // Skip past body } break; case 3: // Find ending delimiter. if ( (*pcszName == '?') && (*(pcszName+1) == '=') ) return TRUE; break; } } if (*pcszName != '\0') { if (IsDBCSLeadByte(*pcszName)) ++pcszName; ++pcszName; } } return FALSE; } // ---------------------------------------------------------------------------- // CMimePropertyContainer::_HrSaveAddressA // ---------------------------------------------------------------------------- HRESULT CMimePropertyContainer::_HrSaveAddressA(LPPROPERTY pProperty, LPMIMEADDRESS pAddress, IStream *pStream, ULONG *pcAddrsWrote, ADDRESSFORMAT format) { // Locals HRESULT hr=S_OK; LPWSTR pszNameW=NULL; LPSTR pszNameA=NULL; LPSTR pszEmailA=NULL; BOOL fWriteEmail=FALSE; LPWSTR pszEscape=NULL; BOOL fRFC822=FALSE; BOOL fRFC1522=FALSE; DWORD dwFlags; MIMEVARIANT rSource; MIMEVARIANT rDest; // Invalid Arg Assert(pProperty && pAddress && pStream && pcAddrsWrote); // Init Dest ZeroMemory(&rDest, sizeof(MIMEVARIANT)); // Deleted or Empty continue if (FIsEmptyW(pAddress->rFriendly.psz) && FIsEmptyW(pAddress->rEmail.psz)) { Assert(FALSE); goto exit; } // RFC822 Format if (AFT_RFC822_TRANSMIT == format || AFT_RFC822_ENCODED == format || AFT_RFC822_DECODED == format) fRFC822 = TRUE; // Decide Delimiter if (*pcAddrsWrote > 0) { // AFT_RFC822_TRANSMIT if (AFT_RFC822_TRANSMIT == format) { // ',\r\n\t' CHECKHR (hr = pStream->Write(c_szAddressFold, lstrlen(c_szAddressFold), NULL)); } // AFT_RFC822_DECODED, AFT_RFC822_ENCODED else if (AFT_RFC822_DECODED == format || AFT_RFC822_ENCODED == format) { // ', ' CHECKHR(hr = pStream->Write(c_szCommaSpace, lstrlen(c_szCommaSpace), NULL)); } // AFT_DISPLAY_FRIENDLY, AFT_DISPLAY_EMAIL, AFT_DISPLAY_BOTH else { // '; ' CHECKHR(hr = pStream->Write(c_szSemiColonSpace, lstrlen(c_szSemiColonSpace), NULL)); } } // Only format that excludes writing the email name if (AFT_DISPLAY_FRIENDLY != format && FIsEmptyW(pAddress->rEmail.psz) == FALSE) fWriteEmail = TRUE; // Only format that excludes writing the display name if (AFT_DISPLAY_EMAIL != format && FIsEmptyW(pAddress->rFriendly.psz) == FALSE) { // Should we write the name if ((AFT_RFC822_TRANSMIT == format || AFT_DISPLAY_BOTH == format) && fWriteEmail && StrStrW(pAddress->rFriendly.psz, pAddress->rEmail.psz)) pszNameA = NULL; else { // Setup Types rDest.type = MVT_STRINGA; rSource.type = MVT_STRINGW; // Init pszName pszNameW = pAddress->rFriendly.psz; // Escape It if (fRFC822 && MimeOleEscapeStringW(pszNameW, &pszEscape) == S_OK) { // Escaped pszNameW = pszEscape; rSource.rStringW.pszVal = pszNameW; rSource.rStringW.cchVal = lstrlenW(pszNameW); } // Otherwise else { rSource.rStringW.pszVal = pAddress->rFriendly.psz; rSource.rStringW.cchVal = pAddress->rFriendly.cch; } // Encoded if (AFT_RFC822_ENCODED == format || AFT_RFC822_TRANSMIT == format) dwFlags = CVF_NOALLOC | PDF_ENCODED; else dwFlags = CVF_NOALLOC; // Convert to ansi if (SUCCEEDED(HrConvertVariant(pProperty->pSymbol, pAddress->pCharset, IET_DECODED, dwFlags, 0, &rSource, &rDest, &fRFC1522))) { // Set pszNameA pszNameA = rDest.rStringA.pszVal; } } } // Write Display Name ? if (NULL != pszNameA) { // [PaulHi] 6/29/99 Raid 81539 // Double quote all display names unless they are in-line encoded. BOOL fInLineEncoded = bIsInLineEncodedA(pszNameA); // if (fRFC822 && !fRFC1522) // Write Quote if ((AFT_DISPLAY_FRIENDLY != format) && !fInLineEncoded) { // Write It CHECKHR(hr = pStream->Write(c_szDoubleQuote, lstrlen(c_szDoubleQuote), NULL)); } // Write display name CHECKHR(hr = pStream->Write(pszNameA, lstrlen(pszNameA), NULL)); // Write Quote if ((AFT_DISPLAY_FRIENDLY != format) && !fInLineEncoded) { // Write It CHECKHR (hr = pStream->Write(c_szDoubleQuote, lstrlen(c_szDoubleQuote), NULL)); } } // Write Email if (TRUE == fWriteEmail) { // Set Start LPCSTR pszStart = pszNameA ? c_szEmailSpaceStart : c_szEmailStart; // Begin Email '>' CHECKHR(hr = pStream->Write(pszStart, lstrlen(pszStart), NULL)); // Convert to ansi CHECKALLOC(pszEmailA = PszToANSI(CP_ACP, pAddress->rEmail.psz)); // Write email CHECKHR(hr = pStream->Write(pszEmailA, lstrlen(pszEmailA), NULL)); // End Email '>' CHECKHR(hr = pStream->Write(c_szEmailEnd, lstrlen(c_szEmailEnd), NULL)); } exit: // Cleanup SafeMemFree(pszEscape); SafeMemFree(pszEmailA); MimeVariantFree(&rDest); // Done return hr; } /////////////////////////////////////////////////////////////////////////////// // bIsInLineEncodedW // // [PaulHi] 6/29/99 // Helper function to determine if double byte string contains RFC1522 inline // encoding. // Format is: "=?[charset]?[encoding]?[data]?=". /////////////////////////////////////////////////////////////////////////////// BOOL bIsInLineEncodedW(LPCWSTR pcwszName) { Assert(pcwszName); int nState = 0; // 0-Begin,charset; 1-encoding; 2-data; 3-Ending while(*pcwszName) { if ( (*pcwszName == L'=') && (*(pcwszName+1) == L'?') ) { // Set/reset state. nState = 1; ++pcwszName; // Skip past } else { switch (nState) { case 1: case 2: // Find encoding, data bodies. if (*pcwszName == L'?') { ++nState; ++pcwszName; // Skip past body } break; case 3: // Find ending delimiter. if ( (*pcwszName == L'?') && (*(pcwszName+1) == L'=') ) return TRUE; break; } } if (*pcwszName != 0) ++pcwszName; } return FALSE; } // ---------------------------------------------------------------------------- // CMimePropertyContainer::_HrSaveAddressW // ---------------------------------------------------------------------------- HRESULT CMimePropertyContainer::_HrSaveAddressW(LPPROPERTY pProperty, LPMIMEADDRESS pAddress, IStream *pStream, ULONG *pcAddrsWrote, ADDRESSFORMAT format) { // Locals HRESULT hr=S_OK; LPWSTR pszNameW=NULL; BOOL fWriteEmail=FALSE; LPWSTR pszEscape=NULL; BOOL fRFC822=FALSE; BOOL fRFC1522=FALSE; MIMEVARIANT rSource; MIMEVARIANT rDest; // Invalid Arg Assert(pProperty && pAddress && pStream && pcAddrsWrote); // Init Dest ZeroMemory(&rDest, sizeof(MIMEVARIANT)); // Deleted or Empty continue if (FIsEmptyW(pAddress->rFriendly.psz) && FIsEmptyW(pAddress->rEmail.psz)) { Assert(FALSE); goto exit; } // RFC822 Format if (AFT_RFC822_TRANSMIT == format || AFT_RFC822_ENCODED == format || AFT_RFC822_DECODED == format) fRFC822 = TRUE; // Decide Delimiter if (*pcAddrsWrote > 0) { // AFT_RFC822_TRANSMIT if (AFT_RFC822_TRANSMIT == format) { // ',\r\n\t' CHECKHR (hr = pStream->Write(c_wszAddressFold, lstrlenW(c_wszAddressFold) * sizeof(WCHAR), NULL)); } // AFT_RFC822_DECODED, AFT_RFC822_ENCODED else if (AFT_RFC822_DECODED == format || AFT_RFC822_ENCODED == format) { // ', ' CHECKHR(hr = pStream->Write(c_wszCommaSpace, lstrlenW(c_wszCommaSpace) * sizeof(WCHAR), NULL)); } // AFT_DISPLAY_FRIENDLY, AFT_DISPLAY_EMAIL, AFT_DISPLAY_BOTH else { // '; ' CHECKHR(hr = pStream->Write(c_wszSemiColonSpace, lstrlenW(c_wszSemiColonSpace) * sizeof(WCHAR), NULL)); } } // Only format that excludes writing the email name if (AFT_DISPLAY_FRIENDLY != format && FIsEmptyW(pAddress->rEmail.psz) == FALSE) fWriteEmail = TRUE; // Only format that excludes writing the display name if (AFT_DISPLAY_EMAIL != format && FIsEmptyW(pAddress->rFriendly.psz) == FALSE) { // Should we write the name if ((AFT_RFC822_TRANSMIT == format || AFT_DISPLAY_BOTH == format) && fWriteEmail && StrStrW(pAddress->rFriendly.psz, pAddress->rEmail.psz)) pszNameW = NULL; else { // Setup Types rDest.type = MVT_STRINGW; rSource.type = MVT_STRINGW; // Init pszName pszNameW = pAddress->rFriendly.psz; // Escape It if (fRFC822 && MimeOleEscapeStringW(pszNameW, &pszEscape) == S_OK) { // Escaped pszNameW = pszEscape; rSource.rStringW.pszVal = pszNameW; rSource.rStringW.cchVal = lstrlenW(pszNameW); } // Otherwise else { rSource.rStringW.pszVal = pAddress->rFriendly.psz; rSource.rStringW.cchVal = pAddress->rFriendly.cch; } // Encoded if (AFT_RFC822_ENCODED == format || AFT_RFC822_TRANSMIT == format) { // Convert to ansi if (SUCCEEDED(HrConvertVariant(pProperty->pSymbol, pAddress->pCharset, IET_DECODED, CVF_NOALLOC | PDF_ENCODED, 0, &rSource, &rDest, &fRFC1522))) { // Set pszNameA pszNameW = rDest.rStringW.pszVal; } } } } // Write Display Name ? if (NULL != pszNameW) { // [PaulHi] 6/29/99 Raid 81539 // Double quote all display names unless they are in-line encoded. BOOL fInLineEncoded = bIsInLineEncodedW(pszNameW); // if (fRFC822 && !fRFC1522) // Write Quote if ((AFT_DISPLAY_FRIENDLY != format) && !fInLineEncoded) { // Write It CHECKHR(hr = pStream->Write(c_wszDoubleQuote, lstrlenW(c_wszDoubleQuote) * sizeof(WCHAR), NULL)); } // Write display name CHECKHR(hr = pStream->Write(pszNameW, lstrlenW(pszNameW) * sizeof(WCHAR), NULL)); // Write Quote if ((AFT_DISPLAY_FRIENDLY != format) && !fInLineEncoded) { // Write It CHECKHR (hr = pStream->Write(c_wszDoubleQuote, lstrlenW(c_wszDoubleQuote) * sizeof(WCHAR), NULL)); } } // Write Email if (TRUE == fWriteEmail) { // Set Start LPCWSTR pszStart = pszNameW ? c_wszEmailSpaceStart : c_wszEmailStart; // Begin Email '>' CHECKHR(hr = pStream->Write(pszStart, lstrlenW(pszStart) * sizeof(WCHAR), NULL)); // Write email CHECKHR(hr = pStream->Write(pAddress->rEmail.psz, pAddress->rEmail.cch * sizeof(WCHAR), NULL)); // End Email '>' CHECKHR(hr = pStream->Write(c_wszEmailEnd, lstrlenW(c_wszEmailEnd) * sizeof(WCHAR), NULL)); } exit: // Cleanup SafeMemFree(pszEscape); MimeVariantFree(&rDest); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_HrQueryAddressGroup // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::_HrQueryAddressGroup(LPPROPERTY pProperty, LPCSTR pszCriteria, boolean fSubString, boolean fCaseSensitive) { // Locals HRESULT hr=S_OK; LPMIMEADDRESS pAddress; // Invalid Arg Assert(pProperty && pProperty->pGroup && pszCriteria); // Does the Property need to be parsed ? CHECKHR(hr = _HrParseInternetAddress(pProperty)); // Loop Infos... for (pAddress=pProperty->pGroup->pHead; pAddress!=NULL; pAddress=pAddress->pNext) { // Tell the Address Info object to write its display information if (_HrQueryAddress(pProperty, pAddress, pszCriteria, fSubString, fCaseSensitive) == S_OK) goto exit; } // Not Found hr = S_FALSE; exit: // Done return hr; } // ---------------------------------------------------------------------------- // CMimePropertyContainer::_HrQueryAddress // ---------------------------------------------------------------------------- HRESULT CMimePropertyContainer::_HrQueryAddress(LPPROPERTY pProperty, LPMIMEADDRESS pAddress, LPCSTR pszCriteria, boolean fSubString, boolean fCaseSensitive) { // Locals HRESULT hr=S_OK; LPWSTR pwszCriteria=NULL; // Invalid Arg Assert(pProperty && pAddress && pszCriteria); // Convert to Unicode CHECKALLOC(pwszCriteria = PszToUnicode(CP_ACP, pszCriteria)); // Query Email Address First if (MimeOleQueryStringW(pAddress->rEmail.psz, pwszCriteria, fSubString, fCaseSensitive) == S_OK) goto exit; // Query Display Address First if (MimeOleQueryStringW(pAddress->rFriendly.psz, pwszCriteria, fSubString, fCaseSensitive) == S_OK) goto exit; // Not Found hr = S_FALSE; exit: // Cleanup SafeMemFree(pwszCriteria); // Done return hr; } // ---------------------------------------------------------------------------- // CMimePropertyContainer::Append // ---------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::Append(DWORD dwAdrType, ENCODINGTYPE ietFriendly, LPCSTR pszFriendly, LPCSTR pszEmail, LPHADDRESS phAddress) { // Locals HRESULT hr=S_OK; ADDRESSPROPS rProps; // Setup rProps ZeroMemory(&rProps, sizeof(rProps)); // Set AddrTyupe rProps.dwProps = IAP_ADRTYPE | IAP_ENCODING; rProps.dwAdrType = dwAdrType; rProps.ietFriendly = ietFriendly; // Set pszFriendly if (pszFriendly) { FLAGSET(rProps.dwProps, IAP_FRIENDLY); rProps.pszFriendly = (LPSTR)pszFriendly; } // Set pszEmail if (pszEmail) { FLAGSET(rProps.dwProps, IAP_EMAIL); rProps.pszEmail = (LPSTR)pszEmail; } // Set the Email Address CHECKHR(hr = Insert(&rProps, phAddress)); exit: // Done return hr; } // ---------------------------------------------------------------------------- // CMimePropertyContainer::AppendW // ---------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::AppendW(DWORD dwAdrType, ENCODINGTYPE ietFriendly, LPCWSTR pwszFriendly, LPCWSTR pwszEmail, LPHADDRESS phAddress) { // Locals HRESULT hr=S_OK; ADDRESSPROPS rProps; LPSTR pszFriendly = NULL, pszEmail = NULL; // Setup rProps ZeroMemory(&rProps, sizeof(rProps)); // Set AddrTyupe rProps.dwProps = IAP_ADRTYPE | IAP_ENCODING; rProps.dwAdrType = dwAdrType; rProps.ietFriendly = ietFriendly; // Set pszFriendly if (pwszFriendly) { FLAGSET(rProps.dwProps, IAP_FRIENDLYW); rProps.pszFriendlyW = (LPWSTR)pwszFriendly; IF_NULLEXIT(pszFriendly = PszToANSI(CP_ACP, pwszFriendly)); FLAGSET(rProps.dwProps, IAP_FRIENDLY); rProps.pszFriendly = pszFriendly; } // Set pszEmail if (pwszEmail) { IF_NULLEXIT(pszEmail = PszToANSI(CP_ACP, pwszEmail)); FLAGSET(rProps.dwProps, IAP_EMAIL); rProps.pszEmail = pszEmail; } // Set the Email Address CHECKHR(hr = Insert(&rProps, phAddress)); exit: MemFree(pszFriendly); MemFree(pszEmail); return hr; } // ---------------------------------------------------------------------------- // CMimePropertyContainer::Insert // ---------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::Insert(LPADDRESSPROPS pProps, LPHADDRESS phAddress) { // Locals HRESULT hr=S_OK; LPPROPSYMBOL pSymbol; LPPROPERTY pProperty; LPMIMEADDRESS pAddress; // Invalid Arg if (NULL == pProps) return TrapError(E_INVALIDARG); // Must have an Email Address and Address Type if (!ISFLAGSET(pProps->dwProps, IAP_ADRTYPE) || (ISFLAGSET(pProps->dwProps, IAP_EMAIL) && FIsEmptyA(pProps->pszEmail))) return TrapError(E_INVALIDARG); // Init if (phAddress) *phAddress = NULL; // Thread Safety EnterCriticalSection(&m_cs); // Get Header CHECKHR(hr = g_pSymCache->HrOpenSymbol(pProps->dwAdrType, &pSymbol)); // Open the group CHECKHR(hr = _HrOpenProperty(pSymbol, &pProperty)); // Does the Property need to be parsed ? CHECKHR(hr = _HrParseInternetAddress(pProperty)); // Append an Address to the group CHECKHR(hr = _HrAppendAddressGroup(pProperty->pGroup, &pAddress)); // The group is dirty Assert(pAddress->pGroup); pAddress->pGroup->fDirty = TRUE; // Set the Address Type pAddress->dwAdrType = pProps->dwAdrType; // Copy Address Props to Mime Address CHECKHR(hr = SetProps(pAddress->hThis, pProps)); // Return the Handle if (phAddress) *phAddress = pAddress->hThis; exit: // Failure if (FAILED(hr) && pAddress) Delete(pAddress->hThis); // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_GetAddressCodePageId // -------------------------------------------------------------------------------- CODEPAGEID CMimePropertyContainer::_GetAddressCodePageId(LPINETCSETINFO pCharset, ENCODINGTYPE ietEncoding) { // Locals CODEPAGEID cpiCodePage=CP_ACP; // No Charset Yet if (NULL == pCharset) { // Try to use the default if (m_rOptions.pDefaultCharset) pCharset = m_rOptions.pDefaultCharset; // Use the global default else if (CIntlGlobals::GetDefHeadCset()) pCharset = CIntlGlobals::GetDefHeadCset(); } // If we have a charset, compute the friendly name codepage if (pCharset) { // Decoded if (IET_DECODED == ietEncoding) { // Get Windows cpiCodePage = (CP_UNICODE == pCharset->cpiWindows) ? CP_ACP : MimeOleGetWindowsCPEx(pCharset); } // Otherwise else { // Use Internet Codepage cpiCodePage = (CP_UNICODE == pCharset->cpiInternet) ? CP_ACP : pCharset->cpiInternet; } } // Done return(cpiCodePage); } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_HrSetAddressProps // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::_HrSetAddressProps(LPADDRESSPROPS pProps, LPMIMEADDRESS pAddress) { // Locals HRESULT hr=S_OK; LPINETCSETINFO pCharset=NULL; LPWSTR pszFriendlyW=NULL; LPWSTR pszEmailW=NULL; ENCODINGTYPE ietFriendly; // Set ietFriendly ietFriendly = (ISFLAGSET(pProps->dwProps, IAP_ENCODING)) ? pProps->ietFriendly : IET_DECODED; // IAP_ADRTYPE if (ISFLAGSET(pProps->dwProps, IAP_ADRTYPE)) pAddress->dwAdrType = pProps->dwAdrType; // IAP_HCHARSET if (ISFLAGSET(pProps->dwProps, IAP_CHARSET) && pProps->hCharset) { // Resolve to pCharset if (SUCCEEDED(g_pInternat->HrOpenCharset(pProps->hCharset, &pCharset))) pAddress->pCharset = pCharset; } // IAP_CERTSTATE if (ISFLAGSET(pProps->dwProps, IAP_CERTSTATE)) pAddress->certstate = pProps->certstate; // IAP_COOKIE if (ISFLAGSET(pProps->dwProps, IAP_COOKIE)) pAddress->dwCookie = pProps->dwCookie; // IAP_FRIENDLYW if (ISFLAGSET(pProps->dwProps, IAP_FRIENDLYW) && pProps->pszFriendlyW) { // Set It CHECKHR(hr = HrSetAddressTokenW(pProps->pszFriendlyW, lstrlenW(pProps->pszFriendlyW), &pAddress->rFriendly)); } // IAP_FRIENDLY else if (ISFLAGSET(pProps->dwProps, IAP_FRIENDLY) && pProps->pszFriendly) { // If the string is encoded, then we have to convert from cpiInternet to a Unicode if (IET_DECODED != ietFriendly) { // No Charset Yet if (NULL == pCharset) { // Try to use the default if (m_rOptions.pDefaultCharset) pCharset = m_rOptions.pDefaultCharset; // Use the global default else if (CIntlGlobals::GetDefHeadCset()) pCharset = CIntlGlobals::GetDefHeadCset(); } // If we have a charset if (pCharset) { // Locals RFC1522INFO Rfc1522Info={0}; PROPVARIANT Decoded; // rfc1522 ? Rfc1522Info.fRfc1522Allowed = TRUE; // Init Decoded.vt = VT_LPWSTR; // Decode the header if (SUCCEEDED(g_pInternat->DecodeHeader(pCharset->hCharset, pProps->pszFriendly, &Decoded, &Rfc1522Info))) { // Set pszFriendlyW = Decoded.pwszVal; } } } // Otherwise, just convert to unicode else { // Convert To Unicode pszFriendlyW = PszToUnicode(_GetAddressCodePageId(pCharset, IET_DECODED), pProps->pszFriendly); } // If we haven't set pszFriendlyW, then just copy pszFriendly if (NULL == pszFriendlyW) { // Convert from CP_ACP to unicode CHECKALLOC(pszFriendlyW = PszToUnicode(CP_ACP, pProps->pszFriendly)); } // Set It CHECKHR(hr = HrSetAddressTokenW(pszFriendlyW, lstrlenW(pszFriendlyW), &pAddress->rFriendly)); } // IAP_EMAIL if (ISFLAGSET(pProps->dwProps, IAP_EMAIL) && pProps->pszEmail) { // Convert To Unicode CHECKALLOC(pszEmailW = PszToUnicode(CP_ACP, pProps->pszEmail)); // Set It CHECKHR(hr = HrSetAddressTokenW(pszEmailW, lstrlenW(pszEmailW), &pAddress->rEmail)); } // IAP_SIGNING_PRINT if (ISFLAGSET(pProps->dwProps, IAP_SIGNING_PRINT) && pProps->tbSigning.pBlobData) { // Free Current Blob SafeMemFree(pAddress->tbSigning.pBlobData); pAddress->tbSigning.cbSize = 0; // Dup CHECKHR(hr = HrCopyBlob(&pProps->tbSigning, &pAddress->tbSigning)); } // IAP_ENCRYPTION_PRINT if (ISFLAGSET(pProps->dwProps, IAP_ENCRYPTION_PRINT) && pProps->tbEncryption.pBlobData) { // Free Current Blob SafeMemFree(pAddress->tbEncryption.pBlobData); pAddress->tbEncryption.cbSize = 0; // Dup CHECKHR(hr = HrCopyBlob(&pProps->tbEncryption, &pAddress->tbEncryption)); } // pAddress->pGroup is Dirty Assert(pAddress->pGroup); if (pAddress->pGroup) pAddress->pGroup->fDirty = TRUE; exit: // Cleanup SafeMemFree(pszFriendlyW); SafeMemFree(pszEmailW); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::_HrGetAddressProps // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::_HrGetAddressProps(LPADDRESSPROPS pProps, LPMIMEADDRESS pAddress) { // Locals HRESULT hr=S_OK; // IAP_CHARSET if (ISFLAGSET(pProps->dwProps, IAP_CHARSET)) { if (pAddress->pCharset && pAddress->pCharset->hCharset) { pProps->hCharset = pAddress->pCharset->hCharset; } else { pProps->hCharset = NULL; FLAGCLEAR(pProps->dwProps, IAP_CHARSET); } } // IAP_HANDLE if (ISFLAGSET(pProps->dwProps, IAP_HANDLE)) { Assert(pAddress->hThis); pProps->hAddress = pAddress->hThis; } // IAP_ADRTYPE if (ISFLAGSET(pProps->dwProps, IAP_ADRTYPE)) { Assert(pAddress->dwAdrType); pProps->dwAdrType = pAddress->dwAdrType; } // IAP_COOKIE if (ISFLAGSET(pProps->dwProps, IAP_COOKIE)) { pProps->dwCookie = pAddress->dwCookie; } // IAP_CERTSTATE if (ISFLAGSET(pProps->dwProps, IAP_CERTSTATE)) { pProps->certstate = pAddress->certstate; } // IAP_ENCODING if (ISFLAGSET(pProps->dwProps, IAP_ENCODING)) { pProps->ietFriendly = IET_DECODED; } // IAP_FRIENDLY if (ISFLAGSET(pProps->dwProps, IAP_FRIENDLY)) { // Decode if (pAddress->rFriendly.psz) { // Compute the correct codepage... CHECKALLOC(pProps->pszFriendly = PszToANSI(_GetAddressCodePageId(pAddress->pCharset, IET_DECODED), pAddress->rFriendly.psz)); } else { pProps->pszFriendly = NULL; FLAGCLEAR(pProps->dwProps, IAP_FRIENDLY); } } // IAT_FRIENDLYW if (ISFLAGSET(pProps->dwProps, IAP_FRIENDLYW)) { // Get the email address if (pAddress->rFriendly.psz) { CHECKALLOC(pProps->pszFriendlyW = PszDupW(pAddress->rFriendly.psz)); } else { pProps->pszFriendlyW = NULL; FLAGCLEAR(pProps->dwProps, IAP_FRIENDLYW); } } // IAP_EMAIL if (ISFLAGSET(pProps->dwProps, IAP_EMAIL)) { // Get the email address if (pAddress->rEmail.psz) { CHECKALLOC(pProps->pszEmail = PszToANSI(CP_ACP, pAddress->rEmail.psz)); } else { pProps->pszEmail = NULL; FLAGCLEAR(pProps->dwProps, IAP_EMAIL); } } // IAP_SIGNING_PRINT if (ISFLAGSET(pProps->dwProps, IAP_SIGNING_PRINT)) { if (pAddress->tbSigning.pBlobData) { CHECKHR(hr = HrCopyBlob(&pAddress->tbSigning, &pProps->tbSigning)); } else { pProps->tbSigning.pBlobData = NULL; pProps->tbSigning.cbSize = 0; FLAGCLEAR(pProps->dwProps, IAP_SIGNING_PRINT); } } // IAP_ENCRYPTION_PRINT if (ISFLAGSET(pProps->dwProps, IAP_ENCRYPTION_PRINT)) { if (pAddress->tbEncryption.pBlobData) { CHECKHR(hr = HrCopyBlob(&pAddress->tbEncryption, &pProps->tbEncryption)); } else { pProps->tbEncryption.pBlobData = NULL; pProps->tbEncryption.cbSize = 0; FLAGCLEAR(pProps->dwProps, IAP_ENCRYPTION_PRINT); } } exit: // Done return hr; } // ---------------------------------------------------------------------------- // CMimePropertyContainer::SetProps // ---------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::SetProps(HADDRESS hAddress, LPADDRESSPROPS pProps) { // Locals HRESULT hr=S_OK; LPPROPSYMBOL pSymbol; LPPROPERTY pProperty; LPMIMEADDRESS pAddress; // Invalid Arg if (NULL == pProps) return TrapError(E_INVALIDARG); // Must have an Email Address if (ISFLAGSET(pProps->dwProps, IAP_EMAIL) && FIsEmptyA(pProps->pszEmail)) return TrapError(E_INVALIDARG); // Thread Safety EnterCriticalSection(&m_cs); // Invalid Handle if (_FIsValidHAddress(hAddress) == FALSE) { hr = TrapError(MIME_E_INVALID_HANDLE); goto exit; } // Deref pAddress = HADDRESSGET(hAddress); // Changing Address Type if (ISFLAGSET(pProps->dwProps, IAP_ADRTYPE) && pProps->dwAdrType != pAddress->dwAdrType) { // Unlink this address from this group _UnlinkAddress(pAddress); // Get Header CHECKHR(hr = g_pSymCache->HrOpenSymbol(pProps->dwAdrType, &pSymbol)); // Open the group CHECKHR(hr = _HrOpenProperty(pSymbol, &pProperty)); // Does the Property need to be parsed ? CHECKHR(hr = _HrParseInternetAddress(pProperty)); // LinkAddress _LinkAddress(pAddress, pProperty->pGroup); // Dirty pProperty->pGroup->fDirty = TRUE; } // Changing other properties CHECKHR(hr = _HrSetAddressProps(pProps, pAddress)); exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // ---------------------------------------------------------------------------- // CMimePropertyContainer::GetProps // ---------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::GetProps(HADDRESS hAddress, LPADDRESSPROPS pProps) { // Locals HRESULT hr=S_OK; LPMIMEADDRESS pAddress; // Invalid Arg if (NULL == pProps) return TrapError(E_INVALIDARG); // Thread Safety EnterCriticalSection(&m_cs); // Invalid Handle if (_FIsValidHAddress(hAddress) == FALSE) { hr = TrapError(MIME_E_INVALID_HANDLE); goto exit; } // Deref pAddress = HADDRESSGET(hAddress); // Changing Email Address to Null CHECKHR(hr = _HrGetAddressProps(pProps, pAddress)); exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // ---------------------------------------------------------------------------- // CMimePropertyContainer::GetSender // ---------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::GetSender(LPADDRESSPROPS pProps) { // Locals HRESULT hr=S_OK; LPPROPERTY pProperty; LPPROPERTY pSender=NULL; HADDRESS hAddress=NULL; // Invalid Arg if (NULL == pProps) return TrapError(E_INVALIDARG); // Thread Safety EnterCriticalSection(&m_cs); // Find first from for (pProperty=m_rAdrTable.pHead; pProperty!=NULL; pProperty=pProperty->pGroup->pNext) { // Not the type I want if (ISFLAGSET(pProperty->pSymbol->dwAdrType, IAT_FROM)) { // Does the Property need to be parsed ? CHECKHR(hr = _HrParseInternetAddress(pProperty)); // Take the first address if (pProperty->pGroup->pHead) hAddress = pProperty->pGroup->pHead->hThis; // Done break; } // Look for Sender: if (ISFLAGSET(pProperty->pSymbol->dwAdrType, IAT_SENDER) && NULL == pSender) { // Does the Property need to be parsed ? CHECKHR(hr = _HrParseInternetAddress(pProperty)); // Sender Property pSender = pProperty; } } // Is there a sender group if (NULL == hAddress && NULL != pSender && NULL != pSender->pGroup->pHead) hAddress = pSender->pGroup->pHead->hThis; // No Address if (NULL == hAddress) { hr = TrapError(MIME_E_NOT_FOUND); goto exit; } // Get Props CHECKHR(hr = GetProps(hAddress, pProps)); exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // ---------------------------------------------------------------------------- // CMimePropertyContainer::CountTypes // ---------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::CountTypes(DWORD dwAdrTypes, ULONG *pcAdrs) { // Locals HRESULT hr=S_OK; LPPROPERTY pProperty; // Invalid Arg if (NULL == pcAdrs) return TrapError(E_INVALIDARG); // Thread Safety EnterCriticalSection(&m_cs); // Init *pcAdrs = 0; // Loop through groups for (pProperty=m_rAdrTable.pHead; pProperty!=NULL; pProperty=pProperty->pGroup->pNext) { // Not the type I want if (ISFLAGSET(dwAdrTypes, pProperty->pSymbol->dwAdrType)) { // Does the Property need to be parsed ? CHECKHR(hr = _HrParseInternetAddress(pProperty)); // Increment Count (*pcAdrs) += pProperty->pGroup->cAdrs; } } exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // ---------------------------------------------------------------------------- // CMimePropertyContainer::GetTypes // ---------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::GetTypes(DWORD dwAdrTypes, DWORD dwProps, LPADDRESSLIST pList) { // Locals HRESULT hr=S_OK; ULONG iAddress; LPPROPERTY pProperty; LPMIMEADDRESS pAddress; // Invalid Arg if (NULL == pList) return TrapError(E_INVALIDARG); // Init ZeroMemory(pList, sizeof(ADDRESSLIST)); // Thread Safety EnterCriticalSection(&m_cs); // Loop through groups CHECKHR(hr = CountTypes(dwAdrTypes, &pList->cAdrs)); // Nothing.. if (0 == pList->cAdrs) goto exit; // Allocate an array CHECKHR(hr = HrAlloc((LPVOID *)&pList->prgAdr, pList->cAdrs * sizeof(ADDRESSPROPS))); // Init ZeroMemory(pList->prgAdr, pList->cAdrs * sizeof(ADDRESSPROPS)); // Fill with types... for (iAddress=0, pProperty=m_rAdrTable.pHead; pProperty!=NULL; pProperty=pProperty->pGroup->pNext) { // Not the type I want if (!ISFLAGSET(dwAdrTypes, pProperty->pSymbol->dwAdrType)) continue; // Does the Property need to be parsed ? CHECKHR(hr = _HrParseInternetAddress(pProperty)); // Loop Infos... for (pAddress=pProperty->pGroup->pHead; pAddress!=NULL; pAddress=pAddress->pNext) { // Verify Size... Assert(iAddress < pList->cAdrs); // Zeromemory ZeroMemory(&pList->prgAdr[iAddress], sizeof(ADDRESSPROPS)); // Set Desired Props pList->prgAdr[iAddress].dwProps = dwProps; // Get the Address Props CHECKHR(hr = _HrGetAddressProps(&pList->prgAdr[iAddress], pAddress)); // Increment piCurrent iAddress++; } } exit: // Failure.. if (FAILED(hr)) { g_pMoleAlloc->FreeAddressList(pList); ZeroMemory(pList, sizeof(ADDRESSLIST)); } // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // ---------------------------------------------------------------------------- // CMimePropertyContainer::EnumTypes // ---------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::EnumTypes(DWORD dwAdrTypes, DWORD dwProps, IMimeEnumAddressTypes **ppEnum) { // Locals HRESULT hr=S_OK; CMimeEnumAddressTypes *pEnum=NULL; ADDRESSLIST rList; // Invalid Arg if (NULL == ppEnum) return TrapError(E_INVALIDARG); // Init out param in case of error *ppEnum = NULL; // Init rList ZeroMemory(&rList, sizeof(ADDRESSLIST)); // Thread Safety EnterCriticalSection(&m_cs); // Get the address lsit CHECKHR(hr = GetTypes(dwAdrTypes, dwProps, &rList)); // Create a new Enumerator CHECKALLOC(pEnum = new CMimeEnumAddressTypes); // Init CHECKHR(hr = pEnum->HrInit((IMimeAddressTable *)this, 0, &rList, FALSE)); // Clear rList rList.cAdrs = 0; rList.prgAdr = NULL; // Return it *ppEnum = pEnum; (*ppEnum)->AddRef(); exit: // Cleanup SafeRelease(pEnum); if (rList.cAdrs) g_pMoleAlloc->FreeAddressList(&rList); // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // ---------------------------------------------------------------------------- // CMimePropertyContainer::Delete // ---------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::Delete(HADDRESS hAddress) { // Locals HRESULT hr=S_OK; LPMIMEADDRESS pAddress; // Thread Safety EnterCriticalSection(&m_cs); // Invalid Handle if (_FIsValidHAddress(hAddress) == FALSE) { hr = TrapError(MIME_E_INVALID_HANDLE); goto exit; } // Deref Address pAddress = HADDRESSGET(hAddress); // Unlink this address _UnlinkAddress(pAddress); // Unlink this address _FreeAddress(pAddress); exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // ---------------------------------------------------------------------------- // CMimePropertyContainer::DeleteTypes // ---------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::DeleteTypes(DWORD dwAdrTypes) { // Locals LPPROPERTY pProperty; BOOL fFound; // Thread Safety EnterCriticalSection(&m_cs); // While there are address types while(dwAdrTypes) { // Reset fFound fFound = FALSE; // Search for first delete-able address type for (pProperty=m_rAdrTable.pHead; pProperty!=NULL; pProperty=pProperty->pGroup->pNext) { // Not the type I want if (ISFLAGSET(dwAdrTypes, pProperty->pSymbol->dwAdrType)) { // We found a properyt fFound = TRUE; // Clear this address type ad being deleted FLAGCLEAR(dwAdrTypes, pProperty->pSymbol->dwAdrType); // Unlink this property _UnlinkProperty(pProperty); // Done break; } } // No Property Found if (FALSE == fFound) break; } // Thread Safety LeaveCriticalSection(&m_cs); // Done return S_OK; } // ---------------------------------------------------------------------------- // GetFormatW // ---------------------------------------------------------------------------- HRESULT CMimePropertyContainer::GetFormatW(DWORD dwAdrType, ADDRESSFORMAT format, LPWSTR *ppwszFormat) { // Locals HRESULT hr=S_OK; PROPVARIANT Variant; // Trace TraceCall("CMimePropertyContainer::GetFormatW"); // Invalid Args if (NULL == ppwszFormat) return(TraceResult(E_INVALIDARG)); // Thread Safety EnterCriticalSection(&m_cs); // Init ZeroMemory(&Variant, sizeof(PROPVARIANT)); // I want a unicode string Variant.vt = VT_LPWSTR; // Get the address format CHECKHR(hr = _GetFormatBase(dwAdrType, format, &Variant)); // Return the String *ppwszFormat = Variant.pwszVal; exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return(hr); } // ---------------------------------------------------------------------------- // CMimePropertyContainer::GetFormat // ---------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::GetFormat(DWORD dwAdrType, ADDRESSFORMAT format, LPSTR *ppszFormat) { // Locals HRESULT hr=S_OK; PROPVARIANT Variant; // Trace TraceCall("CMimePropertyContainer::GetFormat"); // Invalid Args if (NULL == ppszFormat) return(TraceResult(E_INVALIDARG)); // Thread Safety EnterCriticalSection(&m_cs); // Init ZeroMemory(&Variant, sizeof(PROPVARIANT)); // I want a unicode string Variant.vt = VT_LPSTR; // Get the address format CHECKHR(hr = _GetFormatBase(dwAdrType, format, &Variant)); // Return the String *ppszFormat = Variant.pszVal; exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return(hr); } // ---------------------------------------------------------------------------- // CMimePropertyContainer::_GetFormatBase // ---------------------------------------------------------------------------- HRESULT CMimePropertyContainer::_GetFormatBase(DWORD dwAdrType, ADDRESSFORMAT format, LPPROPVARIANT pVariant) { // Locals HRESULT hr=S_OK; CByteStream cByteStream; ULONG cAddrsWrote=0; LPPROPERTY pProperty; // Validate Assert(pVariant && (VT_LPWSTR == pVariant->vt || VT_LPSTR == pVariant->vt)); // Fill with types... for (pProperty=m_rAdrTable.pHead; pProperty!=NULL; pProperty=pProperty->pGroup->pNext) { // Not the type I want if (!ISFLAGSET(dwAdrType, pProperty->pSymbol->dwAdrType)) continue; // Does the Property need to be parsed ? CHECKHR(hr = _HrParseInternetAddress(pProperty)); // Tell the group object to write its display address into pStream CHECKHR(hr = _HrSaveAddressGroup(pProperty, &cByteStream, &cAddrsWrote, format, pVariant->vt)); } // Did we write any for this address tyep ? if (cAddrsWrote) { // Multibyte if (VT_LPSTR == pVariant->vt) { // Get Text CHECKHR(hr = cByteStream.HrAcquireStringA(NULL, &pVariant->pszVal, ACQ_DISPLACE)); } // Otherwise, unicode else { // Validate Assert(VT_LPWSTR == pVariant->vt); // Get Text CHECKHR(hr = cByteStream.HrAcquireStringW(NULL, &pVariant->pwszVal, ACQ_DISPLACE)); } } else hr = MIME_E_NO_DATA; exit: // Done return hr; } // ---------------------------------------------------------------------------- // CMimePropertyContainer::AppendRfc822 // ---------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::AppendRfc822(DWORD dwAdrType, ENCODINGTYPE ietEncoding, LPCSTR pszRfc822Adr) { // Locals HRESULT hr=S_OK; MIMEVARIANT rValue; LPPROPSYMBOL pSymbol; // Invalid Arg if (NULL == pszRfc822Adr) return TrapError(E_INVALIDARG); // Thread Safety EnterCriticalSection(&m_cs); // Get Header CHECKHR(hr = g_pSymCache->HrOpenSymbol(dwAdrType, &pSymbol)); // MimeVariant rValue.type = MVT_STRINGA; rValue.rStringA.pszVal = (LPSTR)pszRfc822Adr; rValue.rStringA.cchVal = lstrlen(pszRfc822Adr); // Store as a property CHECKHR(hr = AppendProp(pSymbol, (IET_ENCODED == ietEncoding) ? PDF_ENCODED : 0, &rValue)); exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // ---------------------------------------------------------------------------- // CMimePropertyContainer::AppendRfc822W // ---------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::AppendRfc822W(DWORD dwAdrType, ENCODINGTYPE ietEncoding, LPCWSTR pwszRfc822Adr) { // Locals HRESULT hr=S_OK; MIMEVARIANT rValue; LPPROPSYMBOL pSymbol; // Invalid Arg if (NULL == pwszRfc822Adr) return TrapError(E_INVALIDARG); // Thread Safety EnterCriticalSection(&m_cs); // Get Header CHECKHR(hr = g_pSymCache->HrOpenSymbol(dwAdrType, &pSymbol)); // MimeVariant rValue.type = MVT_STRINGW; rValue.rStringW.pszVal = (LPWSTR)pwszRfc822Adr; rValue.rStringW.cchVal = lstrlenW(pwszRfc822Adr); // Store as a property CHECKHR(hr = AppendProp(pSymbol, (IET_ENCODED == ietEncoding) ? PDF_ENCODED : 0, &rValue)); exit: // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // ---------------------------------------------------------------------------- // CMimePropertyContainer::ParseRfc822 // ---------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::ParseRfc822(DWORD dwAdrType, ENCODINGTYPE ietEncoding, LPCSTR pszRfc822Adr, LPADDRESSLIST pList) { // Locals HRESULT hr=S_OK; LPPROPSYMBOL pSymbol; LPADDRESSPROPS pAddress; ULONG cAlloc=0; LPWSTR pwszData=NULL; PROPVARIANT rDecoded; RFC1522INFO rRfc1522Info; CAddressParser cAdrParse; CODEPAGEID cpiAddress=CP_ACP; INETCSETINFO CsetInfo; // Invalid Arg if (NULL == pszRfc822Adr || NULL == pList) return TrapError(E_INVALIDARG); // LocalInit ZeroMemory(&rDecoded, sizeof(PROPVARIANT)); // ZeroParse ZeroMemory(pList, sizeof(ADDRESSLIST)); // Get codepage cpiAddress = _GetAddressCodePageId(NULL, IET_DECODED); // Get Header CHECKHR(hr = g_pSymCache->HrOpenSymbol(dwAdrType, &pSymbol)); // Setup rfc1522Info rRfc1522Info.hRfc1522Cset = NULL; // Decode... if (IET_DECODED != ietEncoding) { // Setup rfc1522Info rRfc1522Info.fRfc1522Allowed = TRUE; rRfc1522Info.fAllow8bit = FALSE; rDecoded.vt = VT_LPWSTR; // Check for 1522 Encoding... if (SUCCEEDED(g_pInternat->DecodeHeader(NULL, pszRfc822Adr, &rDecoded, &rRfc1522Info))) { // Set the data pwszData = rDecoded.pwszVal; // Get the pCharset if (rRfc1522Info.hRfc1522Cset) { // Get the charset info if (SUCCEEDED(MimeOleGetCharsetInfo(rRfc1522Info.hRfc1522Cset, &CsetInfo))) { // Set cpiAddress cpiAddress = MimeOleGetWindowsCPEx(&CsetInfo); // Can't be unicode if (CP_UNICODE == cpiAddress) cpiAddress = CP_ACP; } } } } // Otherwise, convert to unicode... else { // Convert CHECKALLOC(pwszData = PszToUnicode(cpiAddress, pszRfc822Adr)); } // Initialize Parse Structure cAdrParse.Init(pwszData, lstrlenW(pwszData)); // Parse while(SUCCEEDED(cAdrParse.Next())) { // Grow my address array ? if (pList->cAdrs + 1 > cAlloc) { // Realloc the array CHECKHR(hr = HrRealloc((LPVOID *)&pList->prgAdr, sizeof(ADDRESSPROPS) * (cAlloc + 5))); // Increment alloc size cAlloc += 5; } // Readability pAddress = &pList->prgAdr[pList->cAdrs]; // Init ZeroMemory(pAddress, sizeof(*pAddress)); // Copy the Friendly Name CHECKALLOC(pAddress->pszFriendly = PszToANSI(cpiAddress, cAdrParse.PszFriendly())); // Copy the Email Name CHECKALLOC(pAddress->pszEmail = PszToANSI(CP_ACP, cAdrParse.PszEmail())); // Charset if (rRfc1522Info.hRfc1522Cset) { pAddress->hCharset = rRfc1522Info.hRfc1522Cset; FLAGSET(pAddress->dwProps, IAP_CHARSET); } // Encoding pAddress->ietFriendly = IET_DECODED; // Set Property Mask FLAGSET(pAddress->dwProps, IAP_FRIENDLY | IAP_EMAIL | IAP_ENCODING); // Increment Count pList->cAdrs++; } exit: // Failure if (FAILED(hr)) g_pMoleAlloc->FreeAddressList(pList); // Cleanup MemFree(pwszData); // Done return hr; } // ---------------------------------------------------------------------------- // CMimePropertyContainer::ParseRfc822W // ---------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::ParseRfc822W(DWORD dwAdrType, LPCWSTR pwszRfc822Adr, LPADDRESSLIST pList) { // Locals HRESULT hr=S_OK; LPPROPSYMBOL pSymbol; LPADDRESSPROPS pAddress; ULONG cAlloc=0; PROPVARIANT rDecoded = {0}; RFC1522INFO rRfc1522Info; CAddressParser cAdrParse; // Invalid Arg if (NULL == pwszRfc822Adr || NULL == pList) return TrapError(E_INVALIDARG); // ZeroParse ZeroMemory(pList, sizeof(*pList)); // Get Header CHECKHR(hr = g_pSymCache->HrOpenSymbol(dwAdrType, &pSymbol)); // Setup rfc1522Info rRfc1522Info.hRfc1522Cset = NULL; // Initialize Parse Structure cAdrParse.Init(pwszRfc822Adr, lstrlenW(pwszRfc822Adr)); // Parse while(SUCCEEDED(cAdrParse.Next())) { // Grow my address array ? if (pList->cAdrs + 1 > cAlloc) { // Realloc the array CHECKHR(hr = HrRealloc((LPVOID *)&pList->prgAdr, sizeof(ADDRESSPROPS) * (cAlloc + 5))); // Increment alloc size cAlloc += 5; } // Readability pAddress = &pList->prgAdr[pList->cAdrs]; // Init ZeroMemory(pAddress, sizeof(*pAddress)); IF_NULLEXIT(pAddress->pszFriendlyW = StrDupW(cAdrParse.PszFriendly())); IF_NULLEXIT(pAddress->pszFriendly = PszToANSI(CP_ACP, pAddress->pszFriendlyW)); // Copy the Email Name CHECKALLOC(pAddress->pszEmail = PszToANSI(CP_ACP, cAdrParse.PszEmail())); // Charset if (rRfc1522Info.hRfc1522Cset) { pAddress->hCharset = rRfc1522Info.hRfc1522Cset; FLAGSET(pAddress->dwProps, IAP_CHARSET); } // Encoding pAddress->ietFriendly = IET_DECODED; // Set Property Mask FLAGSET(pAddress->dwProps, IAP_FRIENDLY | IAP_EMAIL | IAP_ENCODING | IAP_FRIENDLYW); // Increment Count pList->cAdrs++; } exit: // Failure if (FAILED(hr)) g_pMoleAlloc->FreeAddressList(pList); // Done return hr; } // ---------------------------------------------------------------------------- // CMimePropertyContainer::Clone // ---------------------------------------------------------------------------- STDMETHODIMP CMimePropertyContainer::Clone(IMimeAddressTable **ppTable) { // Locals HRESULT hr=S_OK; LPCONTAINER pContainer=NULL; // InvalidArg if (NULL == ppTable) return TrapError(E_INVALIDARG); // Init *ppTable = NULL; // Thread Safety EnterCriticalSection(&m_cs); // Ask the container to clone itself CHECKHR(hr = Clone(&pContainer)); // Bind to the IID_IMimeHeaderTable View CHECKHR(hr = pContainer->QueryInterface(IID_IMimeAddressTable, (LPVOID *)ppTable)); exit: // Cleanup SafeRelease(pContainer); // Thread Safety LeaveCriticalSection(&m_cs); // Done return hr; } // -------------------------------------------------------------------------------- // CMimePropertyContainer::HrGenerateFileName // -------------------------------------------------------------------------------- HRESULT CMimePropertyContainer::_HrGenerateFileName(LPCWSTR pszSuggest, DWORD dwFlags, LPMIMEVARIANT pValue) { // Locals HRESULT hr=S_OK; LPWSTR pszDefExt=NULL; LPWSTR pszData=NULL; LPWSTR pszFree=NULL; LPCSTR pszCntType=NULL; LPPROPERTY pProperty; MIMEVARIANT rSource; // Compute Content Type pszCntType = PSZDEFPROPSTRINGA(m_prgIndex[PID_HDR_CNTTYPE], STR_MIME_TEXT_PLAIN); // No suggestion yet if (NULL == pszSuggest) { // Get as Unicode rSource.type = MVT_STRINGW; // Compute Subject as suggested base file name... if (SUCCEEDED(GetProp(SYM_HDR_SUBJECT, 0, &rSource))) { // Save as new suggest and free it later pszSuggest = pszFree = rSource.rStringW.pszVal; } // If still nothing, then get the content-description header if (NULL == pszSuggest) { // Get Content-Description as unicode if (SUCCEEDED(GetProp(SYM_HDR_CNTDESC, 0, &rSource))) { // Save as new suggest and free it later pszSuggest = pszFree = rSource.rStringW.pszVal; } } } // message/rfc822 if (lstrcmpi(pszCntType, (LPSTR)STR_MIME_MSG_RFC822) == 0) { // If there is a news header, use c_szDotNws if (ISFLAGSET(m_dwState, COSTATE_RFC822NEWS)) pszDefExt = (LPWSTR)c_wszDotNws; else pszDefExt = (LPWSTR)c_wszDotEml; // I will never lookup message/rfc822 extension pszCntType = NULL; } // message/disposition-notification else if (lstrcmpi(pszCntType, "message/disposition-notification") == 0) pszDefExt = (LPWSTR)c_wszDotTxt; // Still no default else if (StrCmpNI(pszCntType, STR_CNT_TEXT, lstrlen(STR_CNT_TEXT)) == 0) pszDefExt = (LPWSTR)c_wszDotTxt; // Generate a filename based on the content type... CHECKHR(hr = MimeOleGenerateFileNameW(pszCntType, pszSuggest, pszDefExt, &pszData)); // Setup rSource ZeroMemory(&rSource, sizeof(MIMEVARIANT)); rSource.type = MVT_STRINGW; rSource.rStringW.pszVal = pszData; rSource.rStringW.cchVal = lstrlenW(pszData); // Return per user request CHECKHR(hr = HrConvertVariant(SYM_ATT_GENFNAME, NULL, IET_DECODED, dwFlags, 0, &rSource, pValue)); exit: // Cleanup SafeMemFree(pszData); SafeMemFree(pszFree); // Done return hr; } #endif // !WIN16