|  | /* | 
|  | * MAPI Utility functions | 
|  | * | 
|  | * Copyright 2004 Jon Griffiths | 
|  | * | 
|  | * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
|  | */ | 
|  |  | 
|  | #include <stdarg.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" | 
|  |  | 
|  | 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 }; | 
|  |  | 
|  | /************************************************************************** | 
|  | *  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) | 
|  | { | 
|  | FIXME("(0x%08lx)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) | 
|  | { | 
|  | 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("(%ld,%p)\n", 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("(%ld,%p,%p)\n", 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 (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) | 
|  | { | 
|  | 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%08lx,%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 = CharNextA(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%08lx,%s,%s,%p)\n", lpAlloc, lpFree, ulFlags, | 
|  | debugstr_a((LPSTR)lpszPath), debugstr_a((LPSTR)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) | 
|  | { | 
|  | LPSTR lpStr = (LPSTR)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"); | 
|  | return CMC_E_NOT_SUPPORTED; | 
|  | } |