xref: /reactos/dll/win32/mapi32/util.c (revision 803b5e13)
1 /*
2  * MAPI Utility functions
3  *
4  * Copyright 2004 Jon Griffiths
5  * Copyright 2009 Owen Rudge for CodeWeavers
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20  */
21 
22 #include <stdarg.h>
23 #include <stdio.h>
24 
25 #define COBJMACROS
26 
27 #include "windef.h"
28 #include "winbase.h"
29 #include "winreg.h"
30 #include "winuser.h"
31 #include "winerror.h"
32 #include "winternl.h"
33 #include "objbase.h"
34 #include "shlwapi.h"
35 #include "wine/debug.h"
36 #include "mapival.h"
37 #include "xcmc.h"
38 #include "msi.h"
39 #include "util.h"
40 
41 WINE_DEFAULT_DEBUG_CHANNEL(mapi);
42 
43 static const BYTE digitsToHex[] = {
44   0,1,2,3,4,5,6,7,8,9,0xff,0xff,0xff,0xff,0xff,0xff,0xff,10,11,12,13,14,15,
45   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,
46   0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff,10,11,12,13,
47   14,15 };
48 
49 MAPI_FUNCTIONS mapiFunctions;
50 
51 /**************************************************************************
52  *  ScInitMapiUtil (MAPI32.33)
53  *
54  * Initialise Mapi utility functions.
55  *
56  * PARAMS
57  *  ulReserved [I] Reserved, pass 0.
58  *
59  * RETURNS
60  *  Success: S_OK. Mapi utility functions may be called.
61  *  Failure: MAPI_E_INVALID_PARAMETER, if ulReserved is not 0.
62  *
63  * NOTES
64  *  Your application does not need to call this function unless it does not
65  *  call MAPIInitialize()/MAPIUninitialize().
66  */
67 SCODE WINAPI ScInitMapiUtil(ULONG ulReserved)
68 {
69     if (mapiFunctions.ScInitMapiUtil)
70         return mapiFunctions.ScInitMapiUtil(ulReserved);
71 
72     FIXME("(0x%08x)stub!\n", ulReserved);
73     if (ulReserved)
74         return MAPI_E_INVALID_PARAMETER;
75     return S_OK;
76 }
77 
78 /**************************************************************************
79  *  DeinitMapiUtil (MAPI32.34)
80  *
81  * Uninitialise Mapi utility functions.
82  *
83  * PARAMS
84  *  None.
85  *
86  * RETURNS
87  *  Nothing.
88  *
89  * NOTES
90  *  Your application does not need to call this function unless it does not
91  *  call MAPIInitialize()/MAPIUninitialize().
92  */
93 VOID WINAPI DeinitMapiUtil(void)
94 {
95     if (mapiFunctions.DeinitMapiUtil)
96         mapiFunctions.DeinitMapiUtil();
97     else
98         FIXME("()stub!\n");
99 }
100 
101 typedef LPVOID *LPMAPIALLOCBUFFER;
102 
103 /**************************************************************************
104  *  MAPIAllocateBuffer   (MAPI32.12)
105  *  MAPIAllocateBuffer@8 (MAPI32.13)
106  *
107  * Allocate a block of memory.
108  *
109  * PARAMS
110  *  cbSize    [I] Size of the block to allocate in bytes
111  *  lppBuffer [O] Destination for pointer to allocated memory
112  *
113  * RETURNS
114  *  Success: S_OK. *lppBuffer is filled with a pointer to a memory block of
115  *           length cbSize bytes.
116  *  Failure: MAPI_E_INVALID_PARAMETER, if lppBuffer is NULL.
117  *           MAPI_E_NOT_ENOUGH_MEMORY, if the memory allocation fails.
118  *
119  * NOTES
120  *  Memory allocated with this function should be freed with MAPIFreeBuffer().
121  *  Further allocations of memory may be linked to the pointer returned using
122  *  MAPIAllocateMore(). Linked allocations are freed when the initial pointer
123  *  is feed.
124  */
125 SCODE WINAPI MAPIAllocateBuffer(ULONG cbSize, LPVOID *lppBuffer)
126 {
127     LPMAPIALLOCBUFFER lpBuff;
128 
129     TRACE("(%d,%p)\n", cbSize, lppBuffer);
130 
131     if (mapiFunctions.MAPIAllocateBuffer)
132         return mapiFunctions.MAPIAllocateBuffer(cbSize, lppBuffer);
133 
134     if (!lppBuffer)
135         return E_INVALIDARG;
136 
137     lpBuff = HeapAlloc(GetProcessHeap(), 0, cbSize + sizeof(*lpBuff));
138     if (!lpBuff)
139         return MAPI_E_NOT_ENOUGH_MEMORY;
140 
141     TRACE("initial allocation:%p, returning %p\n", lpBuff, lpBuff + 1);
142     *lpBuff++ = NULL;
143     *lppBuffer = lpBuff;
144     return S_OK;
145 }
146 
147 /**************************************************************************
148  *  MAPIAllocateMore    (MAPI32.14)
149  *  MAPIAllocateMore@12 (MAPI32.15)
150  *
151  * Allocate a block of memory linked to a previous allocation.
152  *
153  * PARAMS
154  *  cbSize    [I] Size of the block to allocate in bytes
155  *  lpOrig    [I] Initial allocation to link to, from MAPIAllocateBuffer()
156  *  lppBuffer [O] Destination for pointer to allocated memory
157  *
158  * RETURNS
159  *  Success: S_OK. *lppBuffer is filled with a pointer to a memory block of
160  *           length cbSize bytes.
161  *  Failure: MAPI_E_INVALID_PARAMETER, if lpOrig or lppBuffer is invalid.
162  *           MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails.
163  *
164  * NOTES
165  *  Memory allocated with this function and stored in *lppBuffer is freed
166  *  when lpOrig is passed to MAPIFreeBuffer(). It should not be freed independently.
167  */
168 SCODE WINAPI MAPIAllocateMore(ULONG cbSize, LPVOID lpOrig, LPVOID *lppBuffer)
169 {
170     LPMAPIALLOCBUFFER lpBuff = lpOrig;
171 
172     TRACE("(%d,%p,%p)\n", cbSize, lpOrig, lppBuffer);
173 
174     if (mapiFunctions.MAPIAllocateMore)
175         return mapiFunctions.MAPIAllocateMore(cbSize, lpOrig, lppBuffer);
176 
177     if (!lppBuffer || !lpBuff || !--lpBuff)
178         return E_INVALIDARG;
179 
180     /* Find the last allocation in the chain */
181     while (*lpBuff)
182     {
183         TRACE("linked:%p->%p\n", lpBuff, *lpBuff);
184         lpBuff = *lpBuff;
185     }
186 
187     if (SUCCEEDED(MAPIAllocateBuffer(cbSize, lppBuffer)))
188     {
189         *lpBuff = ((LPMAPIALLOCBUFFER)*lppBuffer) - 1;
190         TRACE("linking %p->%p\n", lpBuff, *lpBuff);
191     }
192     return *lppBuffer ? S_OK : MAPI_E_NOT_ENOUGH_MEMORY;
193 }
194 
195 /**************************************************************************
196  *  MAPIFreeBuffer   (MAPI32.16)
197  *  MAPIFreeBuffer@4 (MAPI32.17)
198  *
199  * Free a block of memory and any linked allocations associated with it.
200  *
201  * PARAMS
202  *  lpBuffer [I] Memory to free, returned from MAPIAllocateBuffer()
203  *
204  * RETURNS
205  *  S_OK.
206  */
207 ULONG WINAPI MAPIFreeBuffer(LPVOID lpBuffer)
208 {
209     LPMAPIALLOCBUFFER lpBuff = lpBuffer;
210 
211     TRACE("(%p)\n", lpBuffer);
212 
213     if (mapiFunctions.MAPIFreeBuffer)
214         return mapiFunctions.MAPIFreeBuffer(lpBuffer);
215 
216     if (lpBuff && --lpBuff)
217     {
218         while (lpBuff)
219         {
220             LPVOID lpFree = lpBuff;
221 
222             lpBuff = *lpBuff;
223 
224             TRACE("linked:%p->%p, freeing %p\n", lpFree, lpBuff, lpFree);
225             HeapFree(GetProcessHeap(), 0, lpFree);
226         }
227     }
228     return S_OK;
229 }
230 
231 /**************************************************************************
232  *  WrapProgress@20 (MAPI32.41)
233  */
234 HRESULT WINAPI WrapProgress(PVOID unk1, PVOID unk2, PVOID unk3, PVOID unk4, PVOID unk5)
235 {
236     /* Native does not implement this function */
237     return MAPI_E_NO_SUPPORT;
238 }
239 
240 /*************************************************************************
241  * HrDispatchNotifications@4 (MAPI32.239)
242  */
243 HRESULT WINAPI HrDispatchNotifications(ULONG flags)
244 {
245     FIXME("(%08x)\n", flags);
246     return S_OK;
247 }
248 
249 /*************************************************************************
250  * HrThisThreadAdviseSink@8 (MAPI32.42)
251  *
252  * Ensure that an advise sink is only notified in its originating thread.
253  *
254  * PARAMS
255  *  lpSink     [I] IMAPIAdviseSink interface to be protected
256  *  lppNewSink [I] Destination for wrapper IMAPIAdviseSink interface
257  *
258  * RETURNS
259  * Success: S_OK. *lppNewSink contains a new sink to use in place of lpSink.
260  * Failure: E_INVALIDARG, if any parameter is invalid.
261  */
262 HRESULT WINAPI HrThisThreadAdviseSink(LPMAPIADVISESINK lpSink, LPMAPIADVISESINK* lppNewSink)
263 {
264     if (mapiFunctions.HrThisThreadAdviseSink)
265         return mapiFunctions.HrThisThreadAdviseSink(lpSink, lppNewSink);
266 
267     FIXME("(%p,%p)semi-stub\n", lpSink, lppNewSink);
268 
269     if (!lpSink || !lppNewSink)
270         return E_INVALIDARG;
271 
272     /* Don't wrap the sink for now, just copy it */
273     *lppNewSink = lpSink;
274     IMAPIAdviseSink_AddRef(lpSink);
275     return S_OK;
276 }
277 
278 /*************************************************************************
279  * FBinFromHex (MAPI32.44)
280  *
281  * Create an array of binary data from a string.
282  *
283  * PARAMS
284  *  lpszHex [I] String to convert to binary data
285  *  lpOut   [O] Destination for resulting binary data
286  *
287  * RETURNS
288  *  Success: TRUE. lpOut contains the decoded binary data.
289  *  Failure: FALSE, if lpszHex does not represent a binary string.
290  *
291  * NOTES
292  *  - lpOut must be at least half the length of lpszHex in bytes.
293  *  - Although the Mapi headers prototype this function as both
294  *    Ascii and Unicode, there is only one (Ascii) implementation. This
295  *    means that lpszHex is treated as an Ascii string (i.e. a single NUL
296  *    character in the byte stream terminates the string).
297  */
298 BOOL WINAPI FBinFromHex(LPWSTR lpszHex, LPBYTE lpOut)
299 {
300     LPSTR lpStr = (LPSTR)lpszHex;
301 
302     TRACE("(%p,%p)\n", lpszHex, lpOut);
303 
304     while (*lpStr)
305     {
306         if (lpStr[0] < '0' || lpStr[0] > 'f' || digitsToHex[lpStr[0] - '0'] == 0xff ||
307             lpStr[1] < '0' || lpStr[1] > 'f' || digitsToHex[lpStr[1] - '0'] == 0xff)
308             return FALSE;
309 
310         *lpOut++ = (digitsToHex[lpStr[0] - '0'] << 4) | digitsToHex[lpStr[1] - '0'];
311         lpStr += 2;
312     }
313     return TRUE;
314 }
315 
316 /*************************************************************************
317  * HexFromBin (MAPI32.45)
318  *
319  * Create a string from an array of binary data.
320  *
321  * PARAMS
322  *  lpHex   [I] Binary data to convert to string
323  *  iCount  [I] Length of lpHex in bytes
324  *  lpszOut [O] Destination for resulting hex string
325  *
326  * RETURNS
327  *  Nothing.
328  *
329  * NOTES
330  *  - lpszOut must be at least 2 * iCount + 1 bytes characters long.
331  *  - Although the Mapi headers prototype this function as both
332  *    Ascii and Unicode, there is only one (Ascii) implementation. This
333  *    means that the resulting string is not properly NUL terminated
334  *    if the caller expects it to be a Unicode string.
335  */
336 void WINAPI HexFromBin(LPBYTE lpHex, int iCount, LPWSTR lpszOut)
337 {
338     static const char hexDigits[] = { "0123456789ABCDEF" };
339     LPSTR lpStr = (LPSTR)lpszOut;
340 
341     TRACE("(%p,%d,%p)\n", lpHex, iCount, lpszOut);
342 
343     while (iCount-- > 0)
344     {
345         *lpStr++ = hexDigits[*lpHex >> 4];
346         *lpStr++ = hexDigits[*lpHex & 0xf];
347         lpHex++;
348     }
349     *lpStr = '\0';
350 }
351 
352 /*************************************************************************
353  * SwapPlong@8 (MAPI32.47)
354  *
355  * Swap the bytes in a ULONG array.
356  *
357  * PARAMS
358  *  lpData [O] Array to swap bytes in
359  *  ulLen  [I] Number of ULONG element to swap the bytes of
360  *
361  * RETURNS
362  *  Nothing.
363  */
364 VOID WINAPI SwapPlong(PULONG lpData, ULONG ulLen)
365 {
366     ULONG i;
367 
368     for (i = 0; i < ulLen; i++)
369         lpData[i] = RtlUlongByteSwap(lpData[i]);
370 }
371 
372 /*************************************************************************
373  * SwapPword@8 (MAPI32.48)
374  *
375  * Swap the bytes in a USHORT array.
376  *
377  * PARAMS
378  *  lpData [O] Array to swap bytes in
379  *  ulLen  [I] Number of USHORT element to swap the bytes of
380  *
381  * RETURNS
382  *  Nothing.
383  */
384 VOID WINAPI SwapPword(PUSHORT lpData, ULONG ulLen)
385 {
386     ULONG i;
387 
388     for (i = 0; i < ulLen; i++)
389         lpData[i] = RtlUshortByteSwap(lpData[i]);
390 }
391 
392 /**************************************************************************
393  *  MNLS_lstrlenW@4 (MAPI32.62)
394  *
395  * Calculate the length of a Unicode string.
396  *
397  * PARAMS
398  *  lpszStr [I] String to calculate the length of
399  *
400  * RETURNS
401  *  The length of lpszStr in Unicode characters.
402  */
403 ULONG WINAPI MNLS_lstrlenW(LPCWSTR lpszStr)
404 {
405     TRACE("(%s)\n", debugstr_w(lpszStr));
406     return lstrlenW(lpszStr);
407 }
408 
409 /*************************************************************************
410  * MNLS_lstrcmpW@8 (MAPI32.63)
411  *
412  * Compare two Unicode strings.
413  *
414  * PARAMS
415  *  lpszLeft  [I] First string to compare
416  *  lpszRight [I] Second string to compare
417  *
418  * RETURNS
419  *  An integer less than, equal to or greater than 0, indicating that
420  *  lpszLeft is less than, the same, or greater than lpszRight.
421  */
422 INT WINAPI MNLS_lstrcmpW(LPCWSTR lpszLeft, LPCWSTR lpszRight)
423 {
424     TRACE("(%s,%s)\n", debugstr_w(lpszLeft), debugstr_w(lpszRight));
425     return lstrcmpW(lpszLeft, lpszRight);
426 }
427 
428 /*************************************************************************
429  * MNLS_lstrcpyW@8 (MAPI32.64)
430  *
431  * Copy a Unicode string to another string.
432  *
433  * PARAMS
434  *  lpszDest [O] Destination string
435  *  lpszSrc  [I] Source string
436  *
437  * RETURNS
438  *  The length lpszDest in Unicode characters.
439  */
440 ULONG WINAPI MNLS_lstrcpyW(LPWSTR lpszDest, LPCWSTR lpszSrc)
441 {
442     ULONG len;
443 
444     TRACE("(%p,%s)\n", lpszDest, debugstr_w(lpszSrc));
445     len = (lstrlenW(lpszSrc) + 1) * sizeof(WCHAR);
446     memcpy(lpszDest, lpszSrc, len);
447     return len;
448 }
449 
450 /*************************************************************************
451  * MNLS_CompareStringW@12 (MAPI32.65)
452  *
453  * Compare two Unicode strings.
454  *
455  * PARAMS
456  *  dwCp      [I] Code page for the comparison
457  *  lpszLeft  [I] First string to compare
458  *  lpszRight [I] Second string to compare
459  *
460  * RETURNS
461  *  CSTR_LESS_THAN, CSTR_EQUAL or CSTR_GREATER_THAN, indicating that
462  *  lpszLeft is less than, the same, or greater than lpszRight.
463  */
464 INT WINAPI MNLS_CompareStringW(DWORD dwCp, LPCWSTR lpszLeft, LPCWSTR lpszRight)
465 {
466     INT ret;
467 
468     TRACE("0x%08x,%s,%s\n", dwCp, debugstr_w(lpszLeft), debugstr_w(lpszRight));
469     ret = MNLS_lstrcmpW(lpszLeft, lpszRight);
470     return ret < 0 ? CSTR_LESS_THAN : ret ? CSTR_GREATER_THAN : CSTR_EQUAL;
471 }
472 
473 /**************************************************************************
474  *  FEqualNames@8 (MAPI32.72)
475  *
476  * Compare two Mapi names.
477  *
478  * PARAMS
479  *  lpName1 [I] First name to compare to lpName2
480  *  lpName2 [I] Second name to compare to lpName1
481  *
482  * RETURNS
483  *  TRUE, if the names are the same,
484  *  FALSE, Otherwise.
485  */
486 BOOL WINAPI FEqualNames(LPMAPINAMEID lpName1, LPMAPINAMEID lpName2)
487 {
488     TRACE("(%p,%p)\n", lpName1, lpName2);
489 
490     if (!lpName1 || !lpName2 ||
491         !IsEqualGUID(lpName1->lpguid, lpName2->lpguid) ||
492         lpName1->ulKind != lpName2->ulKind)
493         return FALSE;
494 
495     if (lpName1->ulKind == MNID_STRING)
496         return !lstrcmpW(lpName1->Kind.lpwstrName, lpName2->Kind.lpwstrName);
497 
498     return lpName1->Kind.lID == lpName2->Kind.lID;
499 }
500 
501 /**************************************************************************
502  *  IsBadBoundedStringPtr@8 (MAPI32.71)
503  *
504  * Determine if a string pointer is valid.
505  *
506  * PARAMS
507  *  lpszStr [I] String to check
508  *  ulLen   [I] Maximum length of lpszStr
509  *
510  * RETURNS
511  *  TRUE, if lpszStr is invalid or longer than ulLen,
512  *  FALSE, otherwise.
513  */
514 BOOL WINAPI IsBadBoundedStringPtr(LPCSTR lpszStr, ULONG ulLen)
515 {
516     if (!lpszStr || IsBadStringPtrA(lpszStr, -1) || strlen(lpszStr) >= ulLen)
517         return TRUE;
518     return FALSE;
519 }
520 
521 /**************************************************************************
522  *  FtAddFt@16 (MAPI32.121)
523  *
524  * Add two FILETIME's together.
525  *
526  * PARAMS
527  *  ftLeft  [I] FILETIME to add to ftRight
528  *  ftRight [I] FILETIME to add to ftLeft
529  *
530  * RETURNS
531  *  The sum of ftLeft and ftRight
532  */
533 LONGLONG WINAPI MAPI32_FtAddFt(FILETIME ftLeft, FILETIME ftRight)
534 {
535     LONGLONG *pl = (LONGLONG*)&ftLeft, *pr = (LONGLONG*)&ftRight;
536 
537     return *pl + *pr;
538 }
539 
540 /**************************************************************************
541  *  FtSubFt@16 (MAPI32.123)
542  *
543  * Subtract two FILETIME's together.
544  *
545  * PARAMS
546  *  ftLeft  [I] Initial FILETIME
547  *  ftRight [I] FILETIME to subtract from ftLeft
548  *
549  * RETURNS
550  *  The remainder after ftRight is subtracted from ftLeft.
551  */
552 LONGLONG WINAPI MAPI32_FtSubFt(FILETIME ftLeft, FILETIME ftRight)
553 {
554     LONGLONG *pl = (LONGLONG*)&ftLeft, *pr = (LONGLONG*)&ftRight;
555 
556     return *pr - *pl;
557 }
558 
559 /**************************************************************************
560  *  FtMulDw@12 (MAPI32.124)
561  *
562  * Multiply a FILETIME by a DWORD.
563  *
564  * PARAMS
565  *  dwLeft  [I] DWORD to multiply with ftRight
566  *  ftRight [I] FILETIME to multiply with dwLeft
567  *
568  * RETURNS
569  *  The product of dwLeft and ftRight
570  */
571 LONGLONG WINAPI MAPI32_FtMulDw(DWORD dwLeft, FILETIME ftRight)
572 {
573     LONGLONG *pr = (LONGLONG*)&ftRight;
574 
575     return (LONGLONG)dwLeft * (*pr);
576 }
577 
578 /**************************************************************************
579  *  FtMulDwDw@8 (MAPI32.125)
580  *
581  * Multiply two DWORD, giving the result as a FILETIME.
582  *
583  * PARAMS
584  *  dwLeft  [I] DWORD to multiply with dwRight
585  *  dwRight [I] DWORD to multiply with dwLeft
586  *
587  * RETURNS
588  *  The product of ftMultiplier and ftMultiplicand as a FILETIME.
589  */
590 LONGLONG WINAPI MAPI32_FtMulDwDw(DWORD dwLeft, DWORD dwRight)
591 {
592     return (LONGLONG)dwLeft * (LONGLONG)dwRight;
593 }
594 
595 /**************************************************************************
596  *  FtNegFt@8 (MAPI32.126)
597  *
598  * Negate a FILETIME.
599  *
600  * PARAMS
601  *  ft [I] FILETIME to negate
602  *
603  * RETURNS
604  *  The negation of ft.
605  */
606 LONGLONG WINAPI MAPI32_FtNegFt(FILETIME ft)
607 {
608     LONGLONG *p = (LONGLONG*)&ft;
609 
610     return - *p;
611 }
612 
613 /**************************************************************************
614  *  UlAddRef@4 (MAPI32.128)
615  *
616  * Add a reference to an object.
617  *
618  * PARAMS
619  *  lpUnk [I] Object to add a reference to.
620  *
621  * RETURNS
622  *  The new reference count of the object, or 0 if lpUnk is NULL.
623  *
624  * NOTES
625  * See IUnknown_AddRef.
626  */
627 ULONG WINAPI UlAddRef(void *lpUnk)
628 {
629     TRACE("(%p)\n", lpUnk);
630 
631     if (!lpUnk)
632         return 0UL;
633     return IUnknown_AddRef((LPUNKNOWN)lpUnk);
634 }
635 
636 /**************************************************************************
637  *  UlRelease@4 (MAPI32.129)
638  *
639  * Remove a reference from an object.
640  *
641  * PARAMS
642  *  lpUnk [I] Object to remove reference from.
643  *
644  * RETURNS
645  *  The new reference count of the object, or 0 if lpUnk is NULL. If lpUnk is
646  *  non-NULL and this function returns 0, the object pointed to by lpUnk has
647  *  been released.
648  *
649  * NOTES
650  * See IUnknown_Release.
651  */
652 ULONG WINAPI UlRelease(void *lpUnk)
653 {
654     TRACE("(%p)\n", lpUnk);
655 
656     if (!lpUnk)
657         return 0UL;
658     return IUnknown_Release((LPUNKNOWN)lpUnk);
659 }
660 
661 /**************************************************************************
662  *  UFromSz@4 (MAPI32.133)
663  *
664  * Read an integer from a string
665  *
666  * PARAMS
667  *  lpszStr [I] String to read the integer from.
668  *
669  * RETURNS
670  *  Success: The integer read from lpszStr.
671  *  Failure: 0, if the first character in lpszStr is not 0-9.
672  *
673  * NOTES
674  *  This function does not accept whitespace and stops at the first non-digit
675  *  character.
676  */
677 UINT WINAPI UFromSz(LPCSTR lpszStr)
678 {
679     ULONG ulRet = 0;
680 
681     TRACE("(%s)\n", debugstr_a(lpszStr));
682 
683     if (lpszStr)
684     {
685         while (*lpszStr >= '0' && *lpszStr <= '9')
686         {
687             ulRet = ulRet * 10 + (*lpszStr - '0');
688             lpszStr++;
689         }
690     }
691     return ulRet;
692 }
693 
694 /*************************************************************************
695  * OpenStreamOnFile@24 (MAPI32.147)
696  *
697  * Create a stream on a file.
698  *
699  * PARAMS
700  *  lpAlloc    [I] Memory allocation function
701  *  lpFree     [I] Memory free function
702  *  ulFlags    [I] Flags controlling the opening process
703  *  lpszPath   [I] Path of file to create stream on
704  *  lpszPrefix [I] Prefix of the temporary file name (if ulFlags includes SOF_UNIQUEFILENAME)
705  *  lppStream  [O] Destination for created stream
706  *
707  * RETURNS
708  * Success: S_OK. lppStream contains the new stream object
709  * Failure: E_INVALIDARG if any parameter is invalid, or an HRESULT error code
710  *          describing the error.
711  */
712 HRESULT WINAPI OpenStreamOnFile(LPALLOCATEBUFFER lpAlloc, LPFREEBUFFER lpFree,
713                                 ULONG ulFlags, LPWSTR lpszPath, LPWSTR lpszPrefix,
714                                 LPSTREAM *lppStream)
715 {
716     WCHAR szBuff[MAX_PATH];
717     DWORD dwMode = STGM_READWRITE, dwAttributes = 0;
718     HRESULT hRet;
719 
720     TRACE("(%p,%p,0x%08x,%s,%s,%p)\n", lpAlloc, lpFree, ulFlags,
721           debugstr_a((LPSTR)lpszPath), debugstr_a((LPSTR)lpszPrefix), lppStream);
722 
723     if (mapiFunctions.OpenStreamOnFile)
724         return mapiFunctions.OpenStreamOnFile(lpAlloc, lpFree, ulFlags, lpszPath, lpszPrefix, lppStream);
725 
726     if (lppStream)
727         *lppStream = NULL;
728 
729     if (ulFlags & SOF_UNIQUEFILENAME)
730     {
731         FIXME("Should generate a temporary name\n");
732         return E_INVALIDARG;
733     }
734 
735     if (!lpszPath || !lppStream)
736         return E_INVALIDARG;
737 
738     /* FIXME: Should probably munge mode and attributes, and should handle
739      *        Unicode arguments (I assume MAPI_UNICODE is set in ulFlags if
740      *        we are being passed Unicode strings; MSDN doesn't say).
741      *        This implementation is just enough for Outlook97 to start.
742      */
743     MultiByteToWideChar(CP_ACP, 0, (LPSTR)lpszPath, -1, szBuff, MAX_PATH);
744     hRet = SHCreateStreamOnFileEx(szBuff, dwMode, dwAttributes, TRUE,
745                                   NULL, lppStream);
746     return hRet;
747 }
748 
749 /*************************************************************************
750  * UlFromSzHex@4 (MAPI32.155)
751  *
752  * Read an integer from a hexadecimal string.
753  *
754  * PARAMS
755  *  lpSzHex [I] String containing the hexadecimal number to read
756  *
757  * RETURNS
758  * Success: The number represented by lpszHex.
759  * Failure: 0, if lpszHex does not contain a hex string.
760  *
761  * NOTES
762  *  This function does not accept whitespace and stops at the first non-hex
763  *  character.
764  */
765 ULONG WINAPI UlFromSzHex(LPCWSTR lpszHex)
766 {
767     LPCSTR lpStr = (LPCSTR)lpszHex;
768     ULONG ulRet = 0;
769 
770     TRACE("(%s)\n", debugstr_a(lpStr));
771 
772     while (*lpStr)
773     {
774         if (lpStr[0] < '0' || lpStr[0] > 'f' || digitsToHex[lpStr[0] - '0'] == 0xff ||
775             lpStr[1] < '0' || lpStr[1] > 'f' || digitsToHex[lpStr[1] - '0'] == 0xff)
776             break;
777 
778         ulRet = ulRet * 16 + ((digitsToHex[lpStr[0] - '0'] << 4) | digitsToHex[lpStr[1] - '0']);
779         lpStr += 2;
780     }
781     return ulRet;
782 }
783 
784 /************************************************************************
785  * FBadEntryList@4 (MAPI32.190)
786  *
787  * Determine is an entry list is invalid.
788  *
789  * PARAMS
790  *  lpEntryList [I] List to check
791  *
792  * RETURNS
793  *  TRUE, if lpEntryList is invalid,
794  *  FALSE, otherwise.
795  */
796 BOOL WINAPI FBadEntryList(LPENTRYLIST lpEntryList)
797 {
798     ULONG i;
799 
800     if (IsBadReadPtr(lpEntryList, sizeof(*lpEntryList)) ||
801         IsBadReadPtr(lpEntryList->lpbin,
802                      lpEntryList->cValues * sizeof(*lpEntryList->lpbin)))
803         return TRUE;
804 
805     for (i = 0; i < lpEntryList->cValues; i++)
806         if(IsBadReadPtr(lpEntryList->lpbin[i].lpb, lpEntryList->lpbin[i].cb))
807             return TRUE;
808 
809     return FALSE;
810 }
811 
812 /*************************************************************************
813  * CbOfEncoded@4 (MAPI32.207)
814  *
815  * Return the length of an encoded string.
816  *
817  * PARAMS
818  *  lpSzEnc [I] Encoded string to get the length of.
819  *
820  * RETURNS
821  * The length of the encoded string in bytes.
822  */
823 ULONG WINAPI CbOfEncoded(LPCSTR lpszEnc)
824 {
825     ULONG ulRet = 0;
826 
827     TRACE("(%s)\n", debugstr_a(lpszEnc));
828 
829     if (lpszEnc)
830         ulRet = (((strlen(lpszEnc) | 3) >> 2) + 1) * 3;
831     return ulRet;
832 }
833 
834 /*************************************************************************
835  * cmc_query_configuration (MAPI32.235)
836  *
837  * Retrieves the configuration information for the installed CMC
838  *
839  * PARAMS
840  *  session          [I]   MAPI session handle
841  *  item             [I]   Enumerated variable that identifies which
842  *                         configuration information is being requested
843  *  reference        [O]   Buffer where configuration information is written
844  *  config_extensions[I/O] Path of file to create stream on
845  *
846  * RETURNS
847  * A CMD define
848  */
849 CMC_return_code WINAPI cmc_query_configuration(
850   CMC_session_id session,
851   CMC_enum item,
852   CMC_buffer reference,
853   CMC_extension  *config_extensions)
854 {
855 	FIXME("stub\n");
856 	return CMC_E_NOT_SUPPORTED;
857 }
858 
859 /**************************************************************************
860  *  FGetComponentPath   (MAPI32.254)
861  *  FGetComponentPath@20 (MAPI32.255)
862  *
863  * Return the installed component path, usually to the private mapi32.dll.
864  *
865  * PARAMS
866  *  component       [I] Component ID
867  *  qualifier       [I] Application LCID
868  *  dll_path        [O] returned component path
869  *  dll_path_length [I] component path length
870  *  install         [I] install mode
871  *
872  * RETURNS
873  *  Success: TRUE.
874  *  Failure: FALSE.
875  *
876  * NOTES
877  *  Previously documented in Q229700 "How to locate the correct path
878  *  to the Mapisvc.inf file in Microsoft Outlook".
879  */
880 BOOL WINAPI FGetComponentPath(LPCSTR component, LPCSTR qualifier, LPSTR dll_path,
881                               DWORD dll_path_length, BOOL install)
882 {
883     BOOL ret = FALSE;
884     HMODULE hmsi;
885 
886     TRACE("%s %s %p %u %d\n", component, qualifier, dll_path, dll_path_length, install);
887 
888     if (mapiFunctions.FGetComponentPath)
889         return mapiFunctions.FGetComponentPath(component, qualifier, dll_path, dll_path_length, install);
890 
891     dll_path[0] = 0;
892 
893     hmsi = LoadLibraryA("msi.dll");
894     if (hmsi)
895     {
896         UINT (WINAPI *pMsiProvideQualifiedComponentA)(LPCSTR, LPCSTR, DWORD, LPSTR, LPDWORD);
897 
898         pMsiProvideQualifiedComponentA = (void *)GetProcAddress(hmsi, "MsiProvideQualifiedComponentA");
899         if (pMsiProvideQualifiedComponentA)
900         {
901             static const char * const fmt[] = { "%d\\NT", "%d\\95", "%d" };
902             char lcid_ver[20];
903             UINT i;
904 
905             for (i = 0; i < ARRAY_SIZE(fmt); i++)
906             {
907                 /* FIXME: what's the correct behaviour here? */
908                 if (!qualifier || qualifier == lcid_ver)
909                 {
910                     sprintf(lcid_ver, fmt[i], GetUserDefaultUILanguage());
911                     qualifier = lcid_ver;
912                 }
913 
914                 if (pMsiProvideQualifiedComponentA(component, qualifier,
915                         install ? INSTALLMODE_DEFAULT : INSTALLMODE_EXISTING,
916                         dll_path, &dll_path_length) == ERROR_SUCCESS)
917                 {
918                     ret = TRUE;
919                     break;
920                 }
921 
922                 if (qualifier != lcid_ver) break;
923             }
924         }
925         FreeLibrary(hmsi);
926     }
927     return ret;
928 }
929 
930 /**************************************************************************
931  *  HrQueryAllRows   (MAPI32.75)
932  */
933 HRESULT WINAPI HrQueryAllRows(LPMAPITABLE lpTable, LPSPropTagArray lpPropTags,
934     LPSRestriction lpRestriction, LPSSortOrderSet lpSortOrderSet,
935     LONG crowsMax, LPSRowSet *lppRows)
936 {
937     if (mapiFunctions.HrQueryAllRows)
938         return mapiFunctions.HrQueryAllRows(lpTable, lpPropTags, lpRestriction, lpSortOrderSet, crowsMax, lppRows);
939 
940     FIXME("(%p, %p, %p, %p, %d, %p): stub\n", lpTable, lpPropTags, lpRestriction, lpSortOrderSet, crowsMax, lppRows);
941     *lppRows = NULL;
942     return MAPI_E_CALL_FAILED;
943 }
944 
945 /**************************************************************************
946  *  WrapCompressedRTFStream   (MAPI32.186)
947  */
948 HRESULT WINAPI WrapCompressedRTFStream(LPSTREAM compressed, ULONG flags, LPSTREAM *uncompressed)
949 {
950     if (mapiFunctions.WrapCompressedRTFStream)
951         return mapiFunctions.WrapCompressedRTFStream(compressed, flags, uncompressed);
952 
953     FIXME("(%p, 0x%08x, %p): stub\n", compressed, flags, uncompressed);
954     return MAPI_E_NO_SUPPORT;
955 }
956 
957 static HMODULE mapi_provider;
958 static HMODULE mapi_ex_provider;
959 
960 /**************************************************************************
961  *  load_mapi_provider
962  *
963  * Attempts to load a MAPI provider from the specified registry key.
964  *
965  * Returns a handle to the loaded module in `mapi_provider' if successful.
966  */
967 static void load_mapi_provider(HKEY hkeyMail, LPCWSTR valueName, HMODULE *mapi_provider)
968 {
969     static const WCHAR mapi32_dll[] = {'m','a','p','i','3','2','.','d','l','l',0 };
970 
971     DWORD dwType, dwLen = 0;
972     LPWSTR dllPath;
973 
974     /* Check if we have a value set for DLLPath */
975     if ((RegQueryValueExW(hkeyMail, valueName, NULL, &dwType, NULL, &dwLen) == ERROR_SUCCESS) &&
976         ((dwType == REG_SZ) || (dwType == REG_EXPAND_SZ)) && (dwLen > 0))
977     {
978         dllPath = HeapAlloc(GetProcessHeap(), 0, dwLen);
979 
980         if (dllPath)
981         {
982             RegQueryValueExW(hkeyMail, valueName, NULL, NULL, (LPBYTE)dllPath, &dwLen);
983 
984             /* Check that this value doesn't refer to mapi32.dll (eg, as Outlook does) */
985             if (lstrcmpiW(dllPath, mapi32_dll) != 0)
986             {
987                 if (dwType == REG_EXPAND_SZ)
988                 {
989                     DWORD dwExpandLen;
990                     LPWSTR dllPathExpanded;
991 
992                     /* Expand the path if necessary */
993                     dwExpandLen = ExpandEnvironmentStringsW(dllPath, NULL, 0);
994                     dllPathExpanded = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * dwExpandLen + 1);
995 
996                     if (dllPathExpanded)
997                     {
998                         ExpandEnvironmentStringsW(dllPath, dllPathExpanded, dwExpandLen + 1);
999 
1000                         HeapFree(GetProcessHeap(), 0, dllPath);
1001                         dllPath = dllPathExpanded;
1002                     }
1003                 }
1004 
1005                 /* Load the DLL */
1006                 TRACE("loading %s\n", debugstr_w(dllPath));
1007                 *mapi_provider = LoadLibraryW(dllPath);
1008             }
1009 
1010             HeapFree(GetProcessHeap(), 0, dllPath);
1011         }
1012     }
1013 }
1014 
1015 /**************************************************************************
1016  *  load_mapi_providers
1017  *
1018  * Scans the registry for MAPI providers and attempts to load a Simple and
1019  * Extended MAPI library.
1020  *
1021  * Returns TRUE if at least one library loaded, FALSE otherwise.
1022  */
1023 void load_mapi_providers(void)
1024 {
1025     static const WCHAR regkey_mail[] = {
1026         'S','o','f','t','w','a','r','e','\\','C','l','i','e','n','t','s','\\',
1027         'M','a','i','l',0 };
1028 
1029     static const WCHAR regkey_dllpath[] = {'D','L','L','P','a','t','h',0 };
1030     static const WCHAR regkey_dllpath_ex[] = {'D','L','L','P','a','t','h','E','x',0 };
1031     static const WCHAR regkey_backslash[] = { '\\', 0 };
1032 
1033     HKEY hkeyMail;
1034     DWORD dwType, dwLen = 0;
1035     LPWSTR appName = NULL, appKey = NULL;
1036 
1037     TRACE("()\n");
1038 
1039     /* Open the Mail key */
1040     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, regkey_mail, 0, KEY_READ, &hkeyMail) != ERROR_SUCCESS)
1041         return;
1042 
1043     /* Check if we have a default value set, and the length of it */
1044     if ((RegQueryValueExW(hkeyMail, NULL, NULL, &dwType, NULL, &dwLen) != ERROR_SUCCESS) ||
1045         !((dwType == REG_SZ) || (dwType == REG_EXPAND_SZ)) || (dwLen == 0))
1046         goto cleanUp;
1047 
1048     appName = HeapAlloc(GetProcessHeap(), 0, dwLen);
1049 
1050     if (!appName)
1051         goto cleanUp;
1052 
1053     /* Get the value, and get the path to the app key */
1054     RegQueryValueExW(hkeyMail, NULL, NULL, NULL, (LPBYTE)appName, &dwLen);
1055 
1056     TRACE("appName: %s\n", debugstr_w(appName));
1057 
1058     appKey = HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR) * (lstrlenW(regkey_mail) +
1059         lstrlenW(regkey_backslash) + lstrlenW(appName) + 1));
1060 
1061     if (!appKey)
1062         goto cleanUp;
1063 
1064     lstrcpyW(appKey, regkey_mail);
1065     lstrcatW(appKey, regkey_backslash);
1066     lstrcatW(appKey, appName);
1067 
1068     RegCloseKey(hkeyMail);
1069 
1070     TRACE("appKey: %s\n", debugstr_w(appKey));
1071 
1072     /* Open the app's key */
1073     if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, appKey, 0, KEY_READ, &hkeyMail) != ERROR_SUCCESS)
1074         goto cleanUp;
1075 
1076     /* Try to load the providers */
1077     load_mapi_provider(hkeyMail, regkey_dllpath, &mapi_provider);
1078     load_mapi_provider(hkeyMail, regkey_dllpath_ex, &mapi_ex_provider);
1079 
1080     /* Now try to load our function pointers */
1081     ZeroMemory(&mapiFunctions, sizeof(mapiFunctions));
1082 
1083     /* Simple MAPI functions */
1084     if (mapi_provider)
1085     {
1086         mapiFunctions.MAPIAddress = (void*) GetProcAddress(mapi_provider, "MAPIAddress");
1087         mapiFunctions.MAPIDeleteMail = (void*) GetProcAddress(mapi_provider, "MAPIDeleteMail");
1088         mapiFunctions.MAPIDetails = (void*) GetProcAddress(mapi_provider, "MAPIDetails");
1089         mapiFunctions.MAPIFindNext = (void*) GetProcAddress(mapi_provider, "MAPIFindNext");
1090         mapiFunctions.MAPILogoff = (void*) GetProcAddress(mapi_provider, "MAPILogoff");
1091         mapiFunctions.MAPILogon = (void*) GetProcAddress(mapi_provider, "MAPILogon");
1092         mapiFunctions.MAPIReadMail = (void*) GetProcAddress(mapi_provider, "MAPIReadMail");
1093         mapiFunctions.MAPIResolveName = (void*) GetProcAddress(mapi_provider, "MAPIResolveName");
1094         mapiFunctions.MAPISaveMail = (void*) GetProcAddress(mapi_provider, "MAPISaveMail");
1095         mapiFunctions.MAPISendDocuments = (void*) GetProcAddress(mapi_provider, "MAPISendDocuments");
1096         mapiFunctions.MAPISendMail = (void*) GetProcAddress(mapi_provider, "MAPISendMail");
1097         mapiFunctions.MAPISendMailW = (void*) GetProcAddress(mapi_provider, "MAPISendMailW");
1098     }
1099 
1100     /* Extended MAPI functions */
1101     if (mapi_ex_provider)
1102     {
1103         mapiFunctions.MAPIInitialize = (void*) GetProcAddress(mapi_ex_provider, "MAPIInitialize");
1104         mapiFunctions.MAPILogonEx = (void*) GetProcAddress(mapi_ex_provider, "MAPILogonEx");
1105         mapiFunctions.MAPIUninitialize = (void*) GetProcAddress(mapi_ex_provider, "MAPIUninitialize");
1106 
1107         mapiFunctions.DeinitMapiUtil = (void*) GetProcAddress(mapi_ex_provider, "DeinitMapiUtil@0");
1108         mapiFunctions.DllCanUnloadNow = (void*) GetProcAddress(mapi_ex_provider, "DllCanUnloadNow");
1109         mapiFunctions.DllGetClassObject = (void*) GetProcAddress(mapi_ex_provider, "DllGetClassObject");
1110         mapiFunctions.FGetComponentPath = (void*) GetProcAddress(mapi_ex_provider, "FGetComponentPath");
1111         mapiFunctions.HrThisThreadAdviseSink = (void*) GetProcAddress(mapi_ex_provider, "HrThisThreadAdviseSink@8");
1112         mapiFunctions.HrQueryAllRows = (void*) GetProcAddress(mapi_ex_provider, "HrQueryAllRows@24");
1113         mapiFunctions.MAPIAdminProfiles = (void*) GetProcAddress(mapi_ex_provider, "MAPIAdminProfiles");
1114         mapiFunctions.MAPIAllocateBuffer = (void*) GetProcAddress(mapi_ex_provider, "MAPIAllocateBuffer");
1115         mapiFunctions.MAPIAllocateMore = (void*) GetProcAddress(mapi_ex_provider, "MAPIAllocateMore");
1116         mapiFunctions.MAPIFreeBuffer = (void*) GetProcAddress(mapi_ex_provider, "MAPIFreeBuffer");
1117         mapiFunctions.MAPIGetDefaultMalloc = (void*) GetProcAddress(mapi_ex_provider, "MAPIGetDefaultMalloc@0");
1118         mapiFunctions.MAPIOpenLocalFormContainer = (void *) GetProcAddress(mapi_ex_provider, "MAPIOpenLocalFormContainer");
1119         mapiFunctions.OpenStreamOnFile = (void*) GetProcAddress(mapi_ex_provider, "OpenStreamOnFile@24");
1120         mapiFunctions.ScInitMapiUtil = (void*) GetProcAddress(mapi_ex_provider, "ScInitMapiUtil@4");
1121         mapiFunctions.WrapCompressedRTFStream = (void*) GetProcAddress(mapi_ex_provider, "WrapCompressedRTFStream@12");
1122     }
1123 
1124 cleanUp:
1125     RegCloseKey(hkeyMail);
1126     HeapFree(GetProcessHeap(), 0, appKey);
1127     HeapFree(GetProcessHeap(), 0, appName);
1128 }
1129 
1130 /**************************************************************************
1131  *  unload_mapi_providers
1132  *
1133  * Unloads any loaded MAPI libraries.
1134  */
1135 void unload_mapi_providers(void)
1136 {
1137     TRACE("()\n");
1138 
1139     FreeLibrary(mapi_provider);
1140     FreeLibrary(mapi_ex_provider);
1141 }
1142