| /* | |
| * MAPI Utility functions | |
| * | |
| * Copyright 2004 Jon Griffiths | |
| * Copyright 2009 Owen Rudge for CodeWeavers | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | |
| */ | |
| #include <stdarg.h> | |
| #include <stdio.h> | |
| #define COBJMACROS | |
| #define NONAMELESSUNION | |
| #define NONAMELESSSTRUCT | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "winreg.h" | |
| #include "winuser.h" | |
| #include "winerror.h" | |
| #include "winternl.h" | |
| #include "objbase.h" | |
| #include "shlwapi.h" | |
| #include "wine/debug.h" | |
| #include "wine/unicode.h" | |
| #include "mapival.h" | |
| #include "xcmc.h" | |
| #include "msi.h" | |
| #include "util.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(mapi); | |
| static const BYTE digitsToHex[] = { | |
| 0,1,2,3,4,5,6,7,8,9,0xff,0xff,0xff,0xff,0xff,0xff,0xff,10,11,12,13,14,15, | |
| 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff, | |
| 0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,10,11,12,13, | |
| 14,15 }; | |
| MAPI_FUNCTIONS mapiFunctions; | |
| /************************************************************************** | |
| * ScInitMapiUtil (MAPI32.33) | |
| * | |
| * Initialise Mapi utility functions. | |
| * | |
| * PARAMS | |
| * ulReserved [I] Reserved, pass 0. | |
| * | |
| * RETURNS | |
| * Success: S_OK. Mapi utility functions may be called. | |
| * Failure: MAPI_E_INVALID_PARAMETER, if ulReserved is not 0. | |
| * | |
| * NOTES | |
| * Your application does not need to call this function unless it does not | |
| * call MAPIInitialize()/MAPIUninitialize(). | |
| */ | |
| SCODE WINAPI ScInitMapiUtil(ULONG ulReserved) | |
| { | |
| if (mapiFunctions.ScInitMapiUtil) | |
| return mapiFunctions.ScInitMapiUtil(ulReserved); | |
| FIXME("(0x%08x)stub!\n", ulReserved); | |
| if (ulReserved) | |
| return MAPI_E_INVALID_PARAMETER; | |
| return S_OK; | |
| } | |
| /************************************************************************** | |
| * DeinitMapiUtil (MAPI32.34) | |
| * | |
| * Uninitialise Mapi utility functions. | |
| * | |
| * PARAMS | |
| * None. | |
| * | |
| * RETURNS | |
| * Nothing. | |
| * | |
| * NOTES | |
| * Your application does not need to call this function unless it does not | |
| * call MAPIInitialize()/MAPIUninitialize(). | |
| */ | |
| VOID WINAPI DeinitMapiUtil(void) | |
| { | |
| if (mapiFunctions.DeinitMapiUtil) | |
| mapiFunctions.DeinitMapiUtil(); | |
| else | |
| FIXME("()stub!\n"); | |
| } | |
| typedef LPVOID *LPMAPIALLOCBUFFER; | |
| /************************************************************************** | |
| * MAPIAllocateBuffer (MAPI32.12) | |
| * MAPIAllocateBuffer@8 (MAPI32.13) | |
| * | |
| * Allocate a block of memory. | |
| * | |
| * PARAMS | |
| * cbSize [I] Size of the block to allocate in bytes | |
| * lppBuffer [O] Destination for pointer to allocated memory | |
| * | |
| * RETURNS | |
| * Success: S_OK. *lppBuffer is filled with a pointer to a memory block of | |
| * length cbSize bytes. | |
| * Failure: MAPI_E_INVALID_PARAMETER, if lppBuffer is NULL. | |
| * MAPI_E_NOT_ENOUGH_MEMORY, if the memory allocation fails. | |
| * | |
| * NOTES | |
| * Memory allocated with this function should be freed with MAPIFreeBuffer(). | |
| * Further allocations of memory may be linked to the pointer returned using | |
| * MAPIAllocateMore(). Linked allocations are freed when the initial pointer | |
| * is feed. | |
| */ | |
| SCODE WINAPI MAPIAllocateBuffer(ULONG cbSize, LPVOID *lppBuffer) | |
| { | |
| LPMAPIALLOCBUFFER lpBuff; | |
| TRACE("(%d,%p)\n", cbSize, lppBuffer); | |
| if (mapiFunctions.MAPIAllocateBuffer) | |
| return mapiFunctions.MAPIAllocateBuffer(cbSize, lppBuffer); | |
| if (!lppBuffer) | |
| return E_INVALIDARG; | |
| lpBuff = HeapAlloc(GetProcessHeap(), 0, cbSize + sizeof(*lpBuff)); | |
| if (!lpBuff) | |
| return MAPI_E_NOT_ENOUGH_MEMORY; | |
| TRACE("initial allocation:%p, returning %p\n", lpBuff, lpBuff + 1); | |
| *lpBuff++ = NULL; | |
| *lppBuffer = lpBuff; | |
| return S_OK; | |
| } | |
| /************************************************************************** | |
| * MAPIAllocateMore (MAPI32.14) | |
| * MAPIAllocateMore@12 (MAPI32.15) | |
| * | |
| * Allocate a block of memory linked to a previous allocation. | |
| * | |
| * PARAMS | |
| * cbSize [I] Size of the block to allocate in bytes | |
| * lpOrig [I] Initial allocation to link to, from MAPIAllocateBuffer() | |
| * lppBuffer [O] Destination for pointer to allocated memory | |
| * | |
| * RETURNS | |
| * Success: S_OK. *lppBuffer is filled with a pointer to a memory block of | |
| * length cbSize bytes. | |
| * Failure: MAPI_E_INVALID_PARAMETER, if lpOrig or lppBuffer is invalid. | |
| * MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails. | |
| * | |
| * NOTES | |
| * Memory allocated with this function and stored in *lppBuffer is freed | |
| * when lpOrig is passed to MAPIFreeBuffer(). It should not be freed independently. | |
| */ | |
| SCODE WINAPI MAPIAllocateMore(ULONG cbSize, LPVOID lpOrig, LPVOID *lppBuffer) | |
| { | |
| LPMAPIALLOCBUFFER lpBuff = lpOrig; | |
| TRACE("(%d,%p,%p)\n", cbSize, lpOrig, lppBuffer); | |
| if (mapiFunctions.MAPIAllocateMore) | |
| return mapiFunctions.MAPIAllocateMore(cbSize, lpOrig, lppBuffer); | |
| if (!lppBuffer || !lpBuff || !--lpBuff) | |
| return E_INVALIDARG; | |
| /* Find the last allocation in the chain */ | |
| while (*lpBuff) | |
| { | |
| TRACE("linked:%p->%p\n", lpBuff, *lpBuff); | |
| lpBuff = *lpBuff; | |
| } | |
| if (SUCCEEDED(MAPIAllocateBuffer(cbSize, lppBuffer))) | |
| { | |
| *lpBuff = ((LPMAPIALLOCBUFFER)*lppBuffer) - 1; | |
| TRACE("linking %p->%p\n", lpBuff, *lpBuff); | |
| } | |
| return *lppBuffer ? S_OK : MAPI_E_NOT_ENOUGH_MEMORY; | |
| } | |
| /************************************************************************** | |
| * MAPIFreeBuffer (MAPI32.16) | |
| * MAPIFreeBuffer@4 (MAPI32.17) | |
| * | |
| * Free a block of memory and any linked allocations associated with it. | |
| * | |
| * PARAMS | |
| * lpBuffer [I] Memory to free, returned from MAPIAllocateBuffer() | |
| * | |
| * RETURNS | |
| * S_OK. | |
| */ | |
| ULONG WINAPI MAPIFreeBuffer(LPVOID lpBuffer) | |
| { | |
| LPMAPIALLOCBUFFER lpBuff = lpBuffer; | |
| TRACE("(%p)\n", lpBuffer); | |
| if (mapiFunctions.MAPIFreeBuffer) | |
| return mapiFunctions.MAPIFreeBuffer(lpBuffer); | |
| if (lpBuff && --lpBuff) | |
| { | |
| while (lpBuff) | |
| { | |
| LPVOID lpFree = lpBuff; | |
| lpBuff = *lpBuff; | |
| TRACE("linked:%p->%p, freeing %p\n", lpFree, lpBuff, lpFree); | |
| HeapFree(GetProcessHeap(), 0, lpFree); | |
| } | |
| } | |
| return S_OK; | |
| } | |
| /************************************************************************** | |
| * WrapProgress@20 (MAPI32.41) | |
| */ | |
| HRESULT WINAPI WrapProgress(PVOID unk1, PVOID unk2, PVOID unk3, PVOID unk4, PVOID unk5) | |
| { | |
| /* Native does not implement this function */ | |
| return MAPI_E_NO_SUPPORT; | |
| } | |
| /************************************************************************* | |
| * HrThisThreadAdviseSink@8 (MAPI32.42) | |
| * | |
| * Ensure that an advise sink is only notified in its originating thread. | |
| * | |
| * PARAMS | |
| * lpSink [I] IMAPIAdviseSink interface to be protected | |
| * lppNewSink [I] Destination for wrapper IMAPIAdviseSink interface | |
| * | |
| * RETURNS | |
| * Success: S_OK. *lppNewSink contains a new sink to use in place of lpSink. | |
| * Failure: E_INVALIDARG, if any parameter is invalid. | |
| */ | |
| HRESULT WINAPI HrThisThreadAdviseSink(LPMAPIADVISESINK lpSink, LPMAPIADVISESINK* lppNewSink) | |
| { | |
| if (mapiFunctions.HrThisThreadAdviseSink) | |
| return mapiFunctions.HrThisThreadAdviseSink(lpSink, lppNewSink); | |
| FIXME("(%p,%p)semi-stub\n", lpSink, lppNewSink); | |
| if (!lpSink || !lppNewSink) | |
| return E_INVALIDARG; | |
| /* Don't wrap the sink for now, just copy it */ | |
| *lppNewSink = lpSink; | |
| IMAPIAdviseSink_AddRef(lpSink); | |
| return S_OK; | |
| } | |
| /************************************************************************* | |
| * FBinFromHex (MAPI32.44) | |
| * | |
| * Create an array of binary data from a string. | |
| * | |
| * PARAMS | |
| * lpszHex [I] String to convert to binary data | |
| * lpOut [O] Destination for resulting binary data | |
| * | |
| * RETURNS | |
| * Success: TRUE. lpOut contains the decoded binary data. | |
| * Failure: FALSE, if lpszHex does not represent a binary string. | |
| * | |
| * NOTES | |
| * - lpOut must be at least half the length of lpszHex in bytes. | |
| * - Although the Mapi headers prototype this function as both | |
| * Ascii and Unicode, there is only one (Ascii) implementation. This | |
| * means that lpszHex is treated as an Ascii string (i.e. a single NUL | |
| * character in the byte stream terminates the string). | |
| */ | |
| BOOL WINAPI FBinFromHex(LPWSTR lpszHex, LPBYTE lpOut) | |
| { | |
| LPSTR lpStr = (LPSTR)lpszHex; | |
| TRACE("(%p,%p)\n", lpszHex, lpOut); | |
| while (*lpStr) | |
| { | |
| if (lpStr[0] < '0' || lpStr[0] > 'f' || digitsToHex[lpStr[0] - '0'] == 0xff || | |
| lpStr[1] < '0' || lpStr[1] > 'f' || digitsToHex[lpStr[1] - '0'] == 0xff) | |
| return FALSE; | |
| *lpOut++ = (digitsToHex[lpStr[0] - '0'] << 4) | digitsToHex[lpStr[1] - '0']; | |
| lpStr += 2; | |
| } | |
| return TRUE; | |
| } | |
| /************************************************************************* | |
| * HexFromBin (MAPI32.45) | |
| * | |
| * Create a string from an array of binary data. | |
| * | |
| * PARAMS | |
| * lpHex [I] Binary data to convert to string | |
| * iCount [I] Length of lpHex in bytes | |
| * lpszOut [O] Destination for resulting hex string | |
| * | |
| * RETURNS | |
| * Nothing. | |
| * | |
| * NOTES | |
| * - lpszOut must be at least 2 * iCount + 1 bytes characters long. | |
| * - Although the Mapi headers prototype this function as both | |
| * Ascii and Unicode, there is only one (Ascii) implementation. This | |
| * means that the resulting string is not properly NUL terminated | |
| * if the caller expects it to be a Unicode string. | |
| */ | |
| void WINAPI HexFromBin(LPBYTE lpHex, int iCount, LPWSTR lpszOut) | |
| { | |
| static const char hexDigits[] = { "0123456789ABCDEF" }; | |
| LPSTR lpStr = (LPSTR)lpszOut; | |
| TRACE("(%p,%d,%p)\n", lpHex, iCount, lpszOut); | |
| while (iCount-- > 0) | |
| { | |
| *lpStr++ = hexDigits[*lpHex >> 4]; | |
| *lpStr++ = hexDigits[*lpHex & 0xf]; | |
| lpHex++; | |
| } | |
| *lpStr = '\0'; | |
| } | |
| /************************************************************************* | |
| * SwapPlong@8 (MAPI32.47) | |
| * | |
| * Swap the bytes in a ULONG array. | |
| * | |
| * PARAMS | |
| * lpData [O] Array to swap bytes in | |
| * ulLen [I] Number of ULONG element to swap the bytes of | |
| * | |
| * RETURNS | |
| * Nothing. | |
| */ | |
| VOID WINAPI SwapPlong(PULONG lpData, ULONG ulLen) | |
| { | |
| ULONG i; | |
| for (i = 0; i < ulLen; i++) | |
| lpData[i] = RtlUlongByteSwap(lpData[i]); | |
| } | |
| /************************************************************************* | |
| * SwapPword@8 (MAPI32.48) | |
| * | |
| * Swap the bytes in a USHORT array. | |
| * | |
| * PARAMS | |
| * lpData [O] Array to swap bytes in | |
| * ulLen [I] Number of USHORT element to swap the bytes of | |
| * | |
| * RETURNS | |
| * Nothing. | |
| */ | |
| VOID WINAPI SwapPword(PUSHORT lpData, ULONG ulLen) | |
| { | |
| ULONG i; | |
| for (i = 0; i < ulLen; i++) | |
| lpData[i] = RtlUshortByteSwap(lpData[i]); | |
| } | |
| /************************************************************************** | |
| * MNLS_lstrlenW@4 (MAPI32.62) | |
| * | |
| * Calculate the length of a Unicode string. | |
| * | |
| * PARAMS | |
| * lpszStr [I] String to calculate the length of | |
| * | |
| * RETURNS | |
| * The length of lpszStr in Unicode characters. | |
| */ | |
| ULONG WINAPI MNLS_lstrlenW(LPCWSTR lpszStr) | |
| { | |
| TRACE("(%s)\n", debugstr_w(lpszStr)); | |
| return strlenW(lpszStr); | |
| } | |
| /************************************************************************* | |
| * MNLS_lstrcmpW@8 (MAPI32.63) | |
| * | |
| * Compare two Unicode strings. | |
| * | |
| * PARAMS | |
| * lpszLeft [I] First string to compare | |
| * lpszRight [I] Second string to compare | |
| * | |
| * RETURNS | |
| * An integer less than, equal to or greater than 0, indicating that | |
| * lpszLeft is less than, the same, or greater than lpszRight. | |
| */ | |
| INT WINAPI MNLS_lstrcmpW(LPCWSTR lpszLeft, LPCWSTR lpszRight) | |
| { | |
| TRACE("(%s,%s)\n", debugstr_w(lpszLeft), debugstr_w(lpszRight)); | |
| return strcmpW(lpszLeft, lpszRight); | |
| } | |
| /************************************************************************* | |
| * MNLS_lstrcpyW@8 (MAPI32.64) | |
| * | |
| * Copy a Unicode string to another string. | |
| * | |
| * PARAMS | |
| * lpszDest [O] Destination string | |
| * lpszSrc [I] Source string | |
| * | |
| * RETURNS | |
| * The length lpszDest in Unicode characters. | |
| */ | |
| ULONG WINAPI MNLS_lstrcpyW(LPWSTR lpszDest, LPCWSTR lpszSrc) | |
| { | |
| ULONG len; | |
| TRACE("(%p,%s)\n", lpszDest, debugstr_w(lpszSrc)); | |
| len = (strlenW(lpszSrc) + 1) * sizeof(WCHAR); | |
| memcpy(lpszDest, lpszSrc, len); | |
| return len; | |
| } | |
| /************************************************************************* | |
| * MNLS_CompareStringW@12 (MAPI32.65) | |
| * | |
| * Compare two Unicode strings. | |
| * | |
| * PARAMS | |
| * dwCp [I] Code page for the comparison | |
| * lpszLeft [I] First string to compare | |
| * lpszRight [I] Second string to compare | |
| * | |
| * RETURNS | |
| * CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN, indicating that | |
| * lpszLeft is less than, the same, or greater than lpszRight. | |
| */ | |
| INT WINAPI MNLS_CompareStringW(DWORD dwCp, LPCWSTR lpszLeft, LPCWSTR lpszRight) | |
| { | |
| INT ret; | |
| TRACE("0x%08x,%s,%s\n", dwCp, debugstr_w(lpszLeft), debugstr_w(lpszRight)); | |
| ret = MNLS_lstrcmpW(lpszLeft, lpszRight); | |
| return ret < 0 ? CSTR_LESS_THAN : ret ? CSTR_GREATER_THAN : CSTR_EQUAL; | |
| } | |
| /************************************************************************** | |
| * FEqualNames@8 (MAPI32.72) | |
| * | |
| * Compare two Mapi names. | |
| * | |
| * PARAMS | |
| * lpName1 [I] First name to compare to lpName2 | |
| * lpName2 [I] Second name to compare to lpName1 | |
| * | |
| * RETURNS | |
| * TRUE, if the names are the same, | |
| * FALSE, Otherwise. | |
| */ | |
| BOOL WINAPI FEqualNames(LPMAPINAMEID lpName1, LPMAPINAMEID lpName2) | |
| { | |
| TRACE("(%p,%p)\n", lpName1, lpName2); | |
| if (!lpName1 || !lpName2 || | |
| !IsEqualGUID(lpName1->lpguid, lpName2->lpguid) || | |
| lpName1->ulKind != lpName2->ulKind) | |
| return FALSE; | |
| if (lpName1->ulKind == MNID_STRING) | |
| return !strcmpW(lpName1->Kind.lpwstrName, lpName2->Kind.lpwstrName); | |
| return lpName1->Kind.lID == lpName2->Kind.lID ? TRUE : FALSE; | |
| } | |
| /************************************************************************** | |
| * IsBadBoundedStringPtr@8 (MAPI32.71) | |
| * | |
| * Determine if a string pointer is valid. | |
| * | |
| * PARAMS | |
| * lpszStr [I] String to check | |
| * ulLen [I] Maximum length of lpszStr | |
| * | |
| * RETURNS | |
| * TRUE, if lpszStr is invalid or longer than ulLen, | |
| * FALSE, otherwise. | |
| */ | |
| BOOL WINAPI IsBadBoundedStringPtr(LPCSTR lpszStr, ULONG ulLen) | |
| { | |
| if (!lpszStr || IsBadStringPtrA(lpszStr, -1) || strlen(lpszStr) >= ulLen) | |
| return TRUE; | |
| return FALSE; | |
| } | |
| /************************************************************************** | |
| * FtAddFt@16 (MAPI32.121) | |
| * | |
| * Add two FILETIME's together. | |
| * | |
| * PARAMS | |
| * ftLeft [I] FILETIME to add to ftRight | |
| * ftRight [I] FILETIME to add to ftLeft | |
| * | |
| * RETURNS | |
| * The sum of ftLeft and ftRight | |
| */ | |
| LONGLONG WINAPI MAPI32_FtAddFt(FILETIME ftLeft, FILETIME ftRight) | |
| { | |
| LONGLONG *pl = (LONGLONG*)&ftLeft, *pr = (LONGLONG*)&ftRight; | |
| return *pl + *pr; | |
| } | |
| /************************************************************************** | |
| * FtSubFt@16 (MAPI32.123) | |
| * | |
| * Subtract two FILETIME's together. | |
| * | |
| * PARAMS | |
| * ftLeft [I] Initial FILETIME | |
| * ftRight [I] FILETIME to subtract from ftLeft | |
| * | |
| * RETURNS | |
| * The remainder after ftRight is subtracted from ftLeft. | |
| */ | |
| LONGLONG WINAPI MAPI32_FtSubFt(FILETIME ftLeft, FILETIME ftRight) | |
| { | |
| LONGLONG *pl = (LONGLONG*)&ftLeft, *pr = (LONGLONG*)&ftRight; | |
| return *pr - *pl; | |
| } | |
| /************************************************************************** | |
| * FtMulDw@12 (MAPI32.124) | |
| * | |
| * Multiply a FILETIME by a DWORD. | |
| * | |
| * PARAMS | |
| * dwLeft [I] DWORD to multiply with ftRight | |
| * ftRight [I] FILETIME to multiply with dwLeft | |
| * | |
| * RETURNS | |
| * The product of dwLeft and ftRight | |
| */ | |
| LONGLONG WINAPI MAPI32_FtMulDw(DWORD dwLeft, FILETIME ftRight) | |
| { | |
| LONGLONG *pr = (LONGLONG*)&ftRight; | |
| return (LONGLONG)dwLeft * (*pr); | |
| } | |
| /************************************************************************** | |
| * FtMulDwDw@8 (MAPI32.125) | |
| * | |
| * Multiply two DWORD, giving the result as a FILETIME. | |
| * | |
| * PARAMS | |
| * dwLeft [I] DWORD to multiply with dwRight | |
| * dwRight [I] DWORD to multiply with dwLeft | |
| * | |
| * RETURNS | |
| * The product of ftMultiplier and ftMultiplicand as a FILETIME. | |
| */ | |
| LONGLONG WINAPI MAPI32_FtMulDwDw(DWORD dwLeft, DWORD dwRight) | |
| { | |
| return (LONGLONG)dwLeft * (LONGLONG)dwRight; | |
| } | |
| /************************************************************************** | |
| * FtNegFt@8 (MAPI32.126) | |
| * | |
| * Negate a FILETIME. | |
| * | |
| * PARAMS | |
| * ft [I] FILETIME to negate | |
| * | |
| * RETURNS | |
| * The negation of ft. | |
| */ | |
| LONGLONG WINAPI MAPI32_FtNegFt(FILETIME ft) | |
| { | |
| LONGLONG *p = (LONGLONG*)&ft; | |
| return - *p; | |
| } | |
| /************************************************************************** | |
| * UlAddRef@4 (MAPI32.128) | |
| * | |
| * Add a reference to an object. | |
| * | |
| * PARAMS | |
| * lpUnk [I] Object to add a reference to. | |
| * | |
| * RETURNS | |
| * The new reference count of the object, or 0 if lpUnk is NULL. | |
| * | |
| * NOTES | |
| * See IUnknown_AddRef. | |
| */ | |
| ULONG WINAPI UlAddRef(void *lpUnk) | |
| { | |
| TRACE("(%p)\n", lpUnk); | |
| if (!lpUnk) | |
| return 0UL; | |
| return IUnknown_AddRef((LPUNKNOWN)lpUnk); | |
| } | |
| /************************************************************************** | |
| * UlRelease@4 (MAPI32.129) | |
| * | |
| * Remove a reference from an object. | |
| * | |
| * PARAMS | |
| * lpUnk [I] Object to remove reference from. | |
| * | |
| * RETURNS | |
| * The new reference count of the object, or 0 if lpUnk is NULL. If lpUnk is | |
| * non-NULL and this function returns 0, the object pointed to by lpUnk has | |
| * been released. | |
| * | |
| * NOTES | |
| * See IUnknown_Release. | |
| */ | |
| ULONG WINAPI UlRelease(void *lpUnk) | |
| { | |
| TRACE("(%p)\n", lpUnk); | |
| if (!lpUnk) | |
| return 0UL; | |
| return IUnknown_Release((LPUNKNOWN)lpUnk); | |
| } | |
| /************************************************************************** | |
| * UFromSz@4 (MAPI32.133) | |
| * | |
| * Read an integer from a string | |
| * | |
| * PARAMS | |
| * lpszStr [I] String to read the integer from. | |
| * | |
| * RETURNS | |
| * Success: The integer read from lpszStr. | |
| * Failure: 0, if the first character in lpszStr is not 0-9. | |
| * | |
| * NOTES | |
| * This function does not accept whitespace and stops at the first non-digit | |
| * character. | |
| */ | |
| UINT WINAPI UFromSz(LPCSTR lpszStr) | |
| { | |
| ULONG ulRet = 0; | |
| TRACE("(%s)\n", debugstr_a(lpszStr)); | |
| if (lpszStr) | |
| { | |
| while (*lpszStr >= '0' && *lpszStr <= '9') | |
| { | |
| ulRet = ulRet * 10 + (*lpszStr - '0'); | |
| lpszStr++; | |
| } | |
| } | |
| return ulRet; | |
| } | |
| /************************************************************************* | |
| * OpenStreamOnFile@24 (MAPI32.147) | |
| * | |
| * Create a stream on a file. | |
| * | |
| * PARAMS | |
| * lpAlloc [I] Memory allocation function | |
| * lpFree [I] Memory free function | |
| * ulFlags [I] Flags controlling the opening process | |
| * lpszPath [I] Path of file to create stream on | |
| * lpszPrefix [I] Prefix of the temporary file name (if ulFlags includes SOF_UNIQUEFILENAME) | |
| * lppStream [O] Destination for created stream | |
| * | |
| * RETURNS | |
| * Success: S_OK. lppStream contains the new stream object | |
| * Failure: E_INVALIDARG if any parameter is invalid, or an HRESULT error code | |
| * describing the error. | |
| */ | |
| HRESULT WINAPI OpenStreamOnFile(LPALLOCATEBUFFER lpAlloc, LPFREEBUFFER lpFree, | |
| ULONG ulFlags, LPWSTR lpszPath, LPWSTR lpszPrefix, | |
| LPSTREAM *lppStream) | |
| { | |
| WCHAR szBuff[MAX_PATH]; | |
| DWORD dwMode = STGM_READWRITE, dwAttributes = 0; | |
| HRESULT hRet; | |
| TRACE("(%p,%p,0x%08x,%s,%s,%p)\n", lpAlloc, lpFree, ulFlags, | |
| debugstr_a((LPSTR)lpszPath), debugstr_a((LPSTR)lpszPrefix), lppStream); | |
| if (mapiFunctions.OpenStreamOnFile) | |
| return mapiFunctions.OpenStreamOnFile(lpAlloc, lpFree, ulFlags, lpszPath, lpszPrefix, lppStream); | |
| if (lppStream) | |
| *lppStream = NULL; | |
| if (ulFlags & SOF_UNIQUEFILENAME) | |
| { | |
| FIXME("Should generate a temporary name\n"); | |
| return E_INVALIDARG; | |
| } | |
| if (!lpszPath || !lppStream) | |
| return E_INVALIDARG; | |
| /* FIXME: Should probably munge mode and attributes, and should handle | |
| * Unicode arguments (I assume MAPI_UNICODE is set in ulFlags if | |
| * we are being passed Unicode strings; MSDN doesn't say). | |
| * This implementation is just enough for Outlook97 to start. | |
| */ | |
| MultiByteToWideChar(CP_ACP, 0, (LPSTR)lpszPath, -1, szBuff, MAX_PATH); | |
| hRet = SHCreateStreamOnFileEx(szBuff, dwMode, dwAttributes, TRUE, | |
| NULL, lppStream); | |
| return hRet; | |
| } | |
| /************************************************************************* | |
| * UlFromSzHex@4 (MAPI32.155) | |
| * | |
| * Read an integer from a hexadecimal string. | |
| * | |
| * PARAMS | |
| * lpSzHex [I] String containing the hexadecimal number to read | |
| * | |
| * RETURNS | |
| * Success: The number represented by lpszHex. | |
| * Failure: 0, if lpszHex does not contain a hex string. | |
| * | |
| * NOTES | |
| * This function does not accept whitespace and stops at the first non-hex | |
| * character. | |
| */ | |
| ULONG WINAPI UlFromSzHex(LPCWSTR lpszHex) | |
| { | |
| LPCSTR lpStr = (LPCSTR)lpszHex; | |
| ULONG ulRet = 0; | |
| TRACE("(%s)\n", debugstr_a(lpStr)); | |
| while (*lpStr) | |
| { | |
| if (lpStr[0] < '0' || lpStr[0] > 'f' || digitsToHex[lpStr[0] - '0'] == 0xff || | |
| lpStr[1] < '0' || lpStr[1] > 'f' || digitsToHex[lpStr[1] - '0'] == 0xff) | |
| break; | |
| ulRet = ulRet * 16 + ((digitsToHex[lpStr[0] - '0'] << 4) | digitsToHex[lpStr[1] - '0']); | |
| lpStr += 2; | |
| } | |
| return ulRet; | |
| } | |
| /************************************************************************ | |
| * FBadEntryList@4 (MAPI32.190) | |
| * | |
| * Determine is an entry list is invalid. | |
| * | |
| * PARAMS | |
| * lpEntryList [I] List to check | |
| * | |
| * RETURNS | |
| * TRUE, if lpEntryList is invalid, | |
| * FALSE, otherwise. | |
| */ | |
| BOOL WINAPI FBadEntryList(LPENTRYLIST lpEntryList) | |
| { | |
| ULONG i; | |
| if (IsBadReadPtr(lpEntryList, sizeof(*lpEntryList)) || | |
| IsBadReadPtr(lpEntryList->lpbin, | |
| lpEntryList->cValues * sizeof(*lpEntryList->lpbin))) | |
| return TRUE; | |
| for (i = 0; i < lpEntryList->cValues; i++) | |
| if(IsBadReadPtr(lpEntryList->lpbin[i].lpb, lpEntryList->lpbin[i].cb)) | |
| return TRUE; | |
| return FALSE; | |
| } | |
| /************************************************************************* | |
| * CbOfEncoded@4 (MAPI32.207) | |
| * | |
| * Return the length of an encoded string. | |
| * | |
| * PARAMS | |
| * lpSzEnc [I] Encoded string to get the length of. | |
| * | |
| * RETURNS | |
| * The length of the encoded string in bytes. | |
| */ | |
| ULONG WINAPI CbOfEncoded(LPCSTR lpszEnc) | |
| { | |
| ULONG ulRet = 0; | |
| TRACE("(%s)\n", debugstr_a(lpszEnc)); | |
| if (lpszEnc) | |
| ulRet = (((strlen(lpszEnc) | 3) >> 2) + 1) * 3; | |
| return ulRet; | |
| } | |
| /************************************************************************* | |
| * cmc_query_configuration (MAPI32.235) | |
| * | |
| * Retrieves the configuration information for the installed CMC | |
| * | |
| * PARAMS | |
| * session [I] MAPI session handle | |
| * item [I] Enumerated variable that identifies which | |
| * configuration information is being requested | |
| * reference [O] Buffer where configuration information is written | |
| * config_extensions[I/O] Path of file to create stream on | |
| * | |
| * RETURNS | |
| * A CMD define | |
| */ | |
| CMC_return_code WINAPI cmc_query_configuration( | |
| CMC_session_id session, | |
| CMC_enum item, | |
| CMC_buffer reference, | |
| CMC_extension *config_extensions) | |
| { | |
| FIXME("stub\n"); | |
| return CMC_E_NOT_SUPPORTED; | |
| } | |
| /************************************************************************** | |
| * FGetComponentPath (MAPI32.254) | |
| * FGetComponentPath@20 (MAPI32.255) | |
| * | |
| * Return the installed component path, usually to the private mapi32.dll. | |
| * | |
| * PARAMS | |
| * component [I] Component ID | |
| * qualifier [I] Application LCID | |
| * dll_path [O] returned component path | |
| * dll_path_length [I] component path length | |
| * install [I] install mode | |
| * | |
| * RETURNS | |
| * Success: TRUE. | |
| * Failure: FALSE. | |
| * | |
| * NOTES | |
| * Previously documented in Q229700 "How to locate the correct path | |
| * to the Mapisvc.inf file in Microsoft Outlook". | |
| */ | |
| BOOL WINAPI FGetComponentPath(LPCSTR component, LPCSTR qualifier, LPSTR dll_path, | |
| DWORD dll_path_length, BOOL install) | |
| { | |
| BOOL ret = FALSE; | |
| HMODULE hmsi; | |
| TRACE("%s %s %p %u %d\n", component, qualifier, dll_path, dll_path_length, install); | |
| if (mapiFunctions.FGetComponentPath) | |
| return mapiFunctions.FGetComponentPath(component, qualifier, dll_path, dll_path_length, install); | |
| dll_path[0] = 0; | |
| hmsi = LoadLibraryA("msi.dll"); | |
| if (hmsi) | |
| { | |
| UINT (WINAPI *pMsiProvideQualifiedComponentA)(LPCSTR, LPCSTR, DWORD, LPSTR, LPDWORD); | |
| pMsiProvideQualifiedComponentA = (void *)GetProcAddress(hmsi, "MsiProvideQualifiedComponentA"); | |
| if (pMsiProvideQualifiedComponentA) | |
| { | |
| static const char * const fmt[] = { "%d\\NT", "%d\\95", "%d" }; | |
| char lcid_ver[20]; | |
| UINT i; | |
| for (i = 0; i < sizeof(fmt)/sizeof(fmt[0]); i++) | |
| { | |
| /* FIXME: what's the correct behaviour here? */ | |
| if (!qualifier || qualifier == lcid_ver) | |
| { | |
| sprintf(lcid_ver, fmt[i], GetUserDefaultUILanguage()); | |
| qualifier = lcid_ver; | |
| } | |
| if (pMsiProvideQualifiedComponentA(component, qualifier, | |
| install ? INSTALLMODE_DEFAULT : INSTALLMODE_EXISTING, | |
| dll_path, &dll_path_length) == ERROR_SUCCESS) | |
| { | |
| ret = TRUE; | |
| break; | |
| } | |
| if (qualifier != lcid_ver) break; | |
| } | |
| } | |
| FreeLibrary(hmsi); | |
| } | |
| return ret; | |
| } | |
| /************************************************************************** | |
| * HrQueryAllRows (MAPI32.75) | |
| */ | |
| HRESULT WINAPI HrQueryAllRows(LPMAPITABLE lpTable, LPSPropTagArray lpPropTags, | |
| LPSRestriction lpRestriction, LPSSortOrderSet lpSortOrderSet, | |
| LONG crowsMax, LPSRowSet *lppRows) | |
| { | |
| if (mapiFunctions.HrQueryAllRows) | |
| return mapiFunctions.HrQueryAllRows(lpTable, lpPropTags, lpRestriction, lpSortOrderSet, crowsMax, lppRows); | |
| FIXME("(%p, %p, %p, %p, %d, %p): stub\n", lpTable, lpPropTags, lpRestriction, lpSortOrderSet, crowsMax, lppRows); | |
| *lppRows = NULL; | |
| return MAPI_E_CALL_FAILED; | |
| } | |
| static HMODULE mapi_provider; | |
| static HMODULE mapi_ex_provider; | |
| /************************************************************************** | |
| * load_mapi_provider | |
| * | |
| * Attempts to load a MAPI provider from the specified registry key. | |
| * | |
| * Returns a handle to the loaded module in `mapi_provider' if successful. | |
| */ | |
| static void load_mapi_provider(HKEY hkeyMail, LPCWSTR valueName, HMODULE *mapi_provider) | |
| { | |
| static const WCHAR mapi32_dll[] = {'m','a','p','i','3','2','.','d','l','l',0 }; | |
| DWORD dwType, dwLen = 0; | |
| LPWSTR dllPath; | |
| /* Check if we have a value set for DLLPath */ | |
| if ((RegQueryValueExW(hkeyMail, valueName, NULL, &dwType, NULL, &dwLen) == ERROR_SUCCESS) && | |
| ((dwType == REG_SZ) || (dwType == REG_EXPAND_SZ)) && (dwLen > 0)) | |
| { | |
| dllPath = HeapAlloc(GetProcessHeap(), 0, dwLen); | |
| if (dllPath) | |
| { | |
| RegQueryValueExW(hkeyMail, valueName, NULL, NULL, (LPBYTE)dllPath, &dwLen); | |
| /* Check that this value doesn't refer to mapi32.dll (eg, as Outlook does) */ | |
| if (lstrcmpiW(dllPath, mapi32_dll) != 0) | |
| { | |
| if (dwType == REG_EXPAND_SZ) | |
| { | |
| DWORD dwExpandLen; | |
| LPWSTR dllPathExpanded; | |
| /* Expand the path if necessary */ | |
| dwExpandLen = ExpandEnvironmentStringsW(dllPath, NULL, 0); | |
| dllPathExpanded = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * dwExpandLen + 1); | |
| if (dllPathExpanded) | |
| { | |
| ExpandEnvironmentStringsW(dllPath, dllPathExpanded, dwExpandLen + 1); | |
| HeapFree(GetProcessHeap(), 0, dllPath); | |
| dllPath = dllPathExpanded; | |
| } | |
| } | |
| /* Load the DLL */ | |
| TRACE("loading %s\n", debugstr_w(dllPath)); | |
| *mapi_provider = LoadLibraryW(dllPath); | |
| } | |
| HeapFree(GetProcessHeap(), 0, dllPath); | |
| } | |
| } | |
| } | |
| /************************************************************************** | |
| * load_mapi_providers | |
| * | |
| * Scans the registry for MAPI providers and attempts to load a Simple and | |
| * Extended MAPI library. | |
| * | |
| * Returns TRUE if at least one library loaded, FALSE otherwise. | |
| */ | |
| void load_mapi_providers(void) | |
| { | |
| static const WCHAR regkey_mail[] = { | |
| 'S','o','f','t','w','a','r','e','\\','C','l','i','e','n','t','s','\\', | |
| 'M','a','i','l',0 }; | |
| static const WCHAR regkey_dllpath[] = {'D','L','L','P','a','t','h',0 }; | |
| static const WCHAR regkey_dllpath_ex[] = {'D','L','L','P','a','t','h','E','x',0 }; | |
| static const WCHAR regkey_backslash[] = { '\\', 0 }; | |
| HKEY hkeyMail; | |
| DWORD dwType, dwLen = 0; | |
| LPWSTR appName = NULL, appKey = NULL; | |
| TRACE("()\n"); | |
| /* Open the Mail key */ | |
| if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, regkey_mail, 0, KEY_READ, &hkeyMail) != ERROR_SUCCESS) | |
| return; | |
| /* Check if we have a default value set, and the length of it */ | |
| if ((RegQueryValueExW(hkeyMail, NULL, NULL, &dwType, NULL, &dwLen) != ERROR_SUCCESS) || | |
| !((dwType == REG_SZ) || (dwType == REG_EXPAND_SZ)) || (dwLen == 0)) | |
| goto cleanUp; | |
| appName = HeapAlloc(GetProcessHeap(), 0, dwLen); | |
| if (!appName) | |
| goto cleanUp; | |
| /* Get the value, and get the path to the app key */ | |
| RegQueryValueExW(hkeyMail, NULL, NULL, NULL, (LPBYTE)appName, &dwLen); | |
| TRACE("appName: %s\n", debugstr_w(appName)); | |
| appKey = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * (lstrlenW(regkey_mail) + | |
| lstrlenW(regkey_backslash) + lstrlenW(appName) + 1)); | |
| if (!appKey) | |
| goto cleanUp; | |
| lstrcpyW(appKey, regkey_mail); | |
| lstrcatW(appKey, regkey_backslash); | |
| lstrcatW(appKey, appName); | |
| RegCloseKey(hkeyMail); | |
| TRACE("appKey: %s\n", debugstr_w(appKey)); | |
| /* Open the app's key */ | |
| if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, appKey, 0, KEY_READ, &hkeyMail) != ERROR_SUCCESS) | |
| goto cleanUp; | |
| /* Try to load the providers */ | |
| load_mapi_provider(hkeyMail, regkey_dllpath, &mapi_provider); | |
| load_mapi_provider(hkeyMail, regkey_dllpath_ex, &mapi_ex_provider); | |
| /* Now try to load our function pointers */ | |
| ZeroMemory(&mapiFunctions, sizeof(mapiFunctions)); | |
| /* Simple MAPI functions */ | |
| if (mapi_provider) | |
| { | |
| mapiFunctions.MAPIAddress = (void*) GetProcAddress(mapi_provider, "MAPIAddress"); | |
| mapiFunctions.MAPIDeleteMail = (void*) GetProcAddress(mapi_provider, "MAPIDeleteMail"); | |
| mapiFunctions.MAPIDetails = (void*) GetProcAddress(mapi_provider, "MAPIDetails"); | |
| mapiFunctions.MAPIFindNext = (void*) GetProcAddress(mapi_provider, "MAPIFindNext"); | |
| mapiFunctions.MAPILogoff = (void*) GetProcAddress(mapi_provider, "MAPILogoff"); | |
| mapiFunctions.MAPILogon = (void*) GetProcAddress(mapi_provider, "MAPILogon"); | |
| mapiFunctions.MAPIReadMail = (void*) GetProcAddress(mapi_provider, "MAPIReadMail"); | |
| mapiFunctions.MAPIResolveName = (void*) GetProcAddress(mapi_provider, "MAPIResolveName"); | |
| mapiFunctions.MAPISaveMail = (void*) GetProcAddress(mapi_provider, "MAPISaveMail"); | |
| mapiFunctions.MAPISendDocuments = (void*) GetProcAddress(mapi_provider, "MAPISendDocuments"); | |
| mapiFunctions.MAPISendMail = (void*) GetProcAddress(mapi_provider, "MAPISendMail"); | |
| } | |
| /* Extended MAPI functions */ | |
| if (mapi_ex_provider) | |
| { | |
| mapiFunctions.MAPIInitialize = (void*) GetProcAddress(mapi_ex_provider, "MAPIInitialize"); | |
| mapiFunctions.MAPILogonEx = (void*) GetProcAddress(mapi_ex_provider, "MAPILogonEx"); | |
| mapiFunctions.MAPIUninitialize = (void*) GetProcAddress(mapi_ex_provider, "MAPIUninitialize"); | |
| mapiFunctions.DeinitMapiUtil = (void*) GetProcAddress(mapi_ex_provider, "DeinitMapiUtil@0"); | |
| mapiFunctions.DllCanUnloadNow = (void*) GetProcAddress(mapi_ex_provider, "DllCanUnloadNow"); | |
| mapiFunctions.DllGetClassObject = (void*) GetProcAddress(mapi_ex_provider, "DllGetClassObject"); | |
| mapiFunctions.FGetComponentPath = (void*) GetProcAddress(mapi_ex_provider, "FGetComponentPath"); | |
| mapiFunctions.HrThisThreadAdviseSink = (void*) GetProcAddress(mapi_ex_provider, "HrThisThreadAdviseSink@8"); | |
| mapiFunctions.HrQueryAllRows = (void*) GetProcAddress(mapi_ex_provider, "HrQueryAllRows@24"); | |
| mapiFunctions.MAPIAdminProfiles = (void*) GetProcAddress(mapi_ex_provider, "MAPIAdminProfiles"); | |
| mapiFunctions.MAPIAllocateBuffer = (void*) GetProcAddress(mapi_ex_provider, "MAPIAllocateBuffer"); | |
| mapiFunctions.MAPIAllocateMore = (void*) GetProcAddress(mapi_ex_provider, "MAPIAllocateMore"); | |
| mapiFunctions.MAPIFreeBuffer = (void*) GetProcAddress(mapi_ex_provider, "MAPIFreeBuffer"); | |
| mapiFunctions.MAPIGetDefaultMalloc = (void*) GetProcAddress(mapi_ex_provider, "MAPIGetDefaultMalloc@0"); | |
| mapiFunctions.MAPIOpenLocalFormContainer = (void *) GetProcAddress(mapi_ex_provider, "MAPIOpenLocalFormContainer"); | |
| mapiFunctions.OpenStreamOnFile = (void*) GetProcAddress(mapi_ex_provider, "OpenStreamOnFile@24"); | |
| mapiFunctions.ScInitMapiUtil = (void*) GetProcAddress(mapi_ex_provider, "ScInitMapiUtil@4"); | |
| } | |
| cleanUp: | |
| RegCloseKey(hkeyMail); | |
| HeapFree(GetProcessHeap(), 0, appKey); | |
| HeapFree(GetProcessHeap(), 0, appName); | |
| } | |
| /************************************************************************** | |
| * unload_mapi_providers | |
| * | |
| * Unloads any loaded MAPI libraries. | |
| */ | |
| void unload_mapi_providers(void) | |
| { | |
| TRACE("()\n"); | |
| FreeLibrary(mapi_provider); | |
| FreeLibrary(mapi_ex_provider); | |
| } |