diff --git a/reactos/dll/lzexpand/lz32.def b/reactos/dll/lzexpand/lz32.def new file mode 100644 index 00000000000..6ecbbbe3517 --- /dev/null +++ b/reactos/dll/lzexpand/lz32.def @@ -0,0 +1,19 @@ +; File generated automatically from lzexpand/lz32.spec; do not edit! + +LIBRARY lz32.dll + +EXPORTS +CopyLZFile@8=KERNEL32.CopyLZFile +GetExpandedNameA@8=KERNEL32.GetExpandedNameA +GetExpandedNameW@8=KERNEL32.GetExpandedNameW +LZClose@4=KERNEL32.LZClose +;LZCloseFile +LZCopy@8=KERNEL32.LZCopy +;LZCreateFileW +LZDone@0=KERNEL32.LZDone +LZInit@4=KERNEL32.LZInit +LZOpenFileA@12=KERNEL32.LZOpenFileA +LZOpenFileW@12=KERNEL32.LZOpenFileW +LZRead@12=KERNEL32.LZRead +LZSeek@12=KERNEL32.LZSeek +LZStart@0=KERNEL32.LZStart diff --git a/reactos/dll/lzexpand/lz32.rc b/reactos/dll/lzexpand/lz32.rc new file mode 100644 index 00000000000..e1a9dac7337 --- /dev/null +++ b/reactos/dll/lzexpand/lz32.rc @@ -0,0 +1,5 @@ +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "Lempel-Ziv Expander\0" +#define REACTOS_STR_INTERNAL_NAME "lz32\0" +#define REACTOS_STR_ORIGINAL_FILENAME "lz32.dll\0" +#include diff --git a/reactos/dll/lzexpand/lz32.xml b/reactos/dll/lzexpand/lz32.xml new file mode 100644 index 00000000000..67cd488a54f --- /dev/null +++ b/reactos/dll/lzexpand/lz32.xml @@ -0,0 +1,10 @@ + + + . + + + ntdll + kernel32 + lzexpand_main.c + lz32.rc + diff --git a/reactos/dll/lzexpand/lzexpand_main.c b/reactos/dll/lzexpand/lzexpand_main.c new file mode 100644 index 00000000000..0d71cdb3249 --- /dev/null +++ b/reactos/dll/lzexpand/lzexpand_main.c @@ -0,0 +1,29 @@ +/* + * LZ Decompression functions + * + * Copyright 1996 Marcus Meissner + * + * 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 "windows.h" + +BOOL STDCALL +DllMain(HANDLE hDll, + DWORD dwReason, + LPVOID lpReserved) +{ + return TRUE; +} diff --git a/reactos/dll/mapi32/imalloc.c b/reactos/dll/mapi32/imalloc.c new file mode 100644 index 00000000000..1ea94425075 --- /dev/null +++ b/reactos/dll/mapi32/imalloc.c @@ -0,0 +1,182 @@ +/* + * MAPI Default IMalloc implementation + * + * 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 + +#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 "mapiutil.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mapi); + +static const IMallocVtbl MAPI_IMalloc_vt; + +typedef struct +{ + const IMallocVtbl *lpVtbl; + LONG lRef; +} MAPI_IMALLOC; + +static MAPI_IMALLOC MAPI_IMalloc = { &MAPI_IMalloc_vt, 0u }; + +extern LONG MAPI_ObjectCount; /* In mapi32_main.c */ + +/************************************************************************* + * MAPIGetDefaultMalloc@0 (MAPI32.59) + * + * Get the default MAPI IMalloc interface. + * + * PARAMS + * None. + * + * RETURNS + * A pointer to the MAPI default allocator. + */ +LPMALLOC WINAPI MAPIGetDefaultMalloc(void) +{ + TRACE("()\n"); + + IMalloc_AddRef((LPMALLOC)&MAPI_IMalloc); + return (LPMALLOC)&MAPI_IMalloc; +} + +/************************************************************************** + * IMAPIMalloc_QueryInterface + */ +static HRESULT WINAPI IMAPIMalloc_fnQueryInterface(LPMALLOC iface, REFIID refiid, + LPVOID *ppvObj) +{ + TRACE("(%s,%p)\n", debugstr_guid(refiid), ppvObj); + + if (IsEqualIID(refiid, &IID_IUnknown) || + IsEqualIID(refiid, &IID_IMalloc)) + { + *ppvObj = (LPMALLOC) &MAPI_IMalloc; + TRACE("Returning IMalloc (%p)\n", *ppvObj); + return S_OK; + } + TRACE("Returning E_NOINTERFACE\n"); + return E_NOINTERFACE; +} + +/************************************************************************** + * IMAPIMalloc_AddRef + */ +static ULONG WINAPI IMAPIMalloc_fnAddRef(LPMALLOC iface) +{ + TRACE("(%p)\n", iface); + InterlockedIncrement(&MAPI_ObjectCount); + return 1u; +} + +/************************************************************************** + * IMAPIMalloc_Release + */ +static ULONG WINAPI IMAPIMalloc_fnRelease(LPMALLOC iface) +{ + TRACE("(%p)\n", iface); + InterlockedDecrement(&MAPI_ObjectCount); + return 1u; +} + +/************************************************************************** + * IMAPIMalloc_Alloc + */ +static LPVOID WINAPI IMAPIMalloc_fnAlloc(LPMALLOC iface, DWORD cb) +{ + TRACE("(%p)->(%ld)\n", iface, cb); + + return LocalAlloc(LMEM_FIXED, cb); +} + +/************************************************************************** + * IMAPIMalloc_Realloc + */ +static LPVOID WINAPI IMAPIMalloc_fnRealloc(LPMALLOC iface, LPVOID pv, DWORD cb) +{ + TRACE("(%p)->(%p, %ld)\n", iface, pv, cb); + + if (!pv) + return LocalAlloc(LMEM_FIXED, cb); + + if (cb) + return LocalReAlloc((HANDLE) pv, cb, LMEM_MOVEABLE); + + LocalFree((HANDLE) pv); + return NULL; +} + +/************************************************************************** + * IMAPIMalloc_Free + */ +static void WINAPI IMAPIMalloc_fnFree(LPMALLOC iface, LPVOID pv) +{ + TRACE("(%p)->(%p)\n", iface, pv); + LocalFree((HANDLE) pv); +} + +/************************************************************************** + * IMAPIMalloc_GetSize + */ +static DWORD WINAPI IMAPIMalloc_fnGetSize(LPMALLOC iface, LPVOID pv) +{ + TRACE("(%p)->(%p)\n", iface, pv); + return LocalSize((HANDLE) pv); +} + +/************************************************************************** + * IMAPIMalloc_DidAlloc + */ +static INT WINAPI IMAPIMalloc_fnDidAlloc(LPMALLOC iface, LPVOID pv) +{ + TRACE("(%p)->(%p)\n", iface, pv); + return -1; +} + +/************************************************************************** + * IMAPIMalloc_HeapMinimize + */ +static void WINAPI IMAPIMalloc_fnHeapMinimize(LPMALLOC iface) +{ + TRACE("(%p)\n", iface); +} + +static const IMallocVtbl MAPI_IMalloc_vt = +{ + IMAPIMalloc_fnQueryInterface, + IMAPIMalloc_fnAddRef, + IMAPIMalloc_fnRelease, + IMAPIMalloc_fnAlloc, + IMAPIMalloc_fnRealloc, + IMAPIMalloc_fnFree, + IMAPIMalloc_fnGetSize, + IMAPIMalloc_fnDidAlloc, + IMAPIMalloc_fnHeapMinimize +}; diff --git a/reactos/dll/mapi32/mapi32.spec b/reactos/dll/mapi32/mapi32.spec new file mode 100644 index 00000000000..c94bc12a0ce --- /dev/null +++ b/reactos/dll/mapi32/mapi32.spec @@ -0,0 +1,191 @@ + 8 stub @ + 10 stdcall MAPILogonEx(long ptr ptr long ptr) + 11 stdcall MAPILogonEx@20(long ptr ptr long ptr) MAPILogonEx + 12 stdcall MAPIAllocateBuffer(long ptr) + 13 stdcall MAPIAllocateBuffer@8(long ptr) MAPIAllocateBuffer + 14 stdcall MAPIAllocateMore(long ptr ptr) + 15 stdcall MAPIAllocateMore@12(long ptr ptr) MAPIAllocateMore + 16 stdcall MAPIFreeBuffer(ptr) + 17 stdcall MAPIFreeBuffer@4(ptr) MAPIFreeBuffer + 18 stub MAPIAdminProfiles + 19 stub MAPIAdminProfiles@8 + 20 stdcall MAPIInitialize(ptr) + 21 stdcall MAPIInitialize@4(ptr) MAPIInitialize + 22 stdcall MAPIUninitialize() + 23 stdcall MAPIUninitialize@0() MAPIUninitialize + 24 stub PRProviderInit + 25 stub LAUNCHWIZARD + 26 stub LaunchWizard@20 + 27 stub DllGetClassObject + 28 stdcall -private DllCanUnloadNow() + 29 stub MAPIOpenFormMgr + 30 stub MAPIOpenFormMgr@8 + 31 stub MAPIOpenLocalFormContainer + 32 stub MAPIOpenLocalFormContainer@4 + 33 stdcall ScInitMapiUtil@4(long) ScInitMapiUtil + 34 stdcall DeinitMapiUtil@0() DeinitMapiUtil + 35 stub ScGenerateMuid@4 + 36 stub HrAllocAdviseSink@12 + 41 stdcall WrapProgress@20(ptr ptr ptr ptr ptr) WrapProgress + 42 stdcall HrThisThreadAdviseSink@8(ptr ptr) HrThisThreadAdviseSink + 43 stub ScBinFromHexBounded@12 + 44 stdcall FBinFromHex@8(ptr ptr) FBinFromHex + 45 stdcall HexFromBin@12(ptr long ptr) HexFromBin + 46 stub BuildDisplayTable@40 + 47 stdcall SwapPlong@8(ptr long) SwapPlong + 48 stdcall SwapPword@8(ptr long) SwapPword + 49 stub MAPIInitIdle@4 + 50 stub MAPIDeinitIdle@0 + 51 stub InstallFilterHook@4 + 52 stub FtgRegisterIdleRoutine@20 + 53 stub EnableIdleRoutine@8 + 54 stub DeregisterIdleRoutine@4 + 55 stub ChangeIdleRoutine@28 + 59 stdcall MAPIGetDefaultMalloc@0() MAPIGetDefaultMalloc + 60 stdcall CreateIProp@24(ptr ptr ptr ptr ptr ptr) CreateIProp + 61 stub CreateTable@36 + 62 stdcall MNLS_lstrlenW@4(wstr) MNLS_lstrlenW + 63 stdcall MNLS_lstrcmpW@8(wstr wstr) MNLS_lstrcmpW + 64 stdcall MNLS_lstrcpyW@8(ptr wstr) MNLS_lstrcpyW + 65 stdcall MNLS_CompareStringW@24(long wstr wstr) MNLS_CompareStringW + 66 stdcall MNLS_MultiByteToWideChar@24(long long str long ptr long) kernel32.MultiByteToWideChar + 67 stdcall MNLS_WideCharToMultiByte@32(long long wstr long ptr long ptr ptr) kernel32.WideCharToMultiByte + 68 stdcall MNLS_IsBadStringPtrW@8(ptr long) kernel32.IsBadStringPtrW + 72 stdcall FEqualNames@8(ptr ptr) FEqualNames + 73 stub WrapStoreEntryID@24 + 74 stdcall IsBadBoundedStringPtr@8(ptr long) IsBadBoundedStringPtr + 75 stub HrQueryAllRows@24 + 76 stdcall PropCopyMore@16(ptr ptr ptr ptr) PropCopyMore + 77 stdcall UlPropSize@4(ptr) UlPropSize + 78 stdcall FPropContainsProp@12(ptr ptr long) FPropContainsProp + 79 stdcall FPropCompareProp@12(ptr long ptr) FPropCompareProp + 80 stdcall LPropCompareProp@8(ptr ptr) LPropCompareProp + 81 stub HrAddColumns@16 + 82 stub HrAddColumnsEx@20 +121 stdcall -ret64 FtAddFt@16(double double) MAPI32_FtAddFt +122 stub FtAdcFt@20 +123 stdcall -ret64 FtSubFt@16(double double) MAPI32_FtSubFt +124 stdcall -ret64 FtMulDw@12(long double) MAPI32_FtMulDw +125 stdcall -ret64 FtMulDwDw@8(long long) MAPI32_FtMulDwDw +126 stdcall -ret64 FtNegFt@8(double) MAPI32_FtNegFt +127 stub FtDivFtBogus@20 +128 stdcall UlAddRef@4(ptr) UlAddRef +129 stdcall UlRelease@4(ptr) UlRelease +130 stdcall SzFindCh@8(str long) shlwapi.StrChrA +131 stdcall SzFindLastCh@8(str str long) shlwapi.StrRChrA +132 stdcall SzFindSz@8(str str) shlwapi.StrStrA +133 stdcall UFromSz@4(str) UFromSz +135 stdcall HrGetOneProp@12(ptr long ptr) HrGetOneProp +136 stdcall HrSetOneProp@8(ptr ptr) HrSetOneProp +137 stdcall FPropExists@8(ptr long) FPropExists +138 stdcall PpropFindProp@12(ptr long long) PpropFindProp +139 stdcall FreePadrlist@4(ptr) FreePadrlist +140 stdcall FreeProws@4(ptr) FreeProws +141 stub HrSzFromEntryID@12 +142 stub HrEntryIDFromSz@12 +143 stub HrComposeEID@28 +144 stub HrDecomposeEID@28 +145 stub HrComposeMsgID@24 +146 stub HrDecomposeMsgID@24 +147 stdcall OpenStreamOnFile@24(ptr ptr ptr ptr ptr ptr) OpenStreamOnFile +148 stdcall OpenStreamOnFile(ptr ptr ptr ptr ptr ptr) +149 stub OpenTnefStream@28 +150 stub OpenTnefStream +151 stub OpenTnefStreamEx@32 +152 stub OpenTnefStreamEx +153 stub GetTnefStreamCodepage@12 +154 stub GetTnefStreamCodepage +155 stdcall UlFromSzHex@4(ptr) UlFromSzHex +156 stub UNKOBJ_ScAllocate@12 +157 stub UNKOBJ_ScAllocateMore@16 +158 stub UNKOBJ_Free@8 +159 stub UNKOBJ_FreeRows@8 +160 stub UNKOBJ_ScCOAllocate@12 +161 stub UNKOBJ_ScCOReallocate@12 +162 stub UNKOBJ_COFree@8 +163 stub UNKOBJ_ScSzFromIdsAlloc@20 +164 stub ScCountNotifications@12 +165 stub ScCopyNotifications@16 +166 stub ScRelocNotifications@20 +170 stdcall ScCountProps@12(long ptr ptr) ScCountProps +171 stdcall ScCopyProps@16(long ptr ptr ptr) ScCopyProps +172 stdcall ScRelocProps@20(long ptr ptr ptr ptr) ScRelocProps +173 stdcall LpValFindProp@12(long long ptr) LpValFindProp +174 stdcall ScDupPropset@16(long ptr ptr ptr) ScDupPropset +175 stdcall FBadRglpszA@8(ptr long) FBadRglpszA +176 stdcall FBadRglpszW@8(ptr long) FBadRglpszW +177 stdcall FBadRowSet@4(ptr) FBadRowSet +178 stub FBadRglpNameID@8 +179 stdcall FBadPropTag@4(long) FBadPropTag +180 stdcall FBadRow@4(ptr) FBadRow +181 stdcall FBadProp@4(ptr) FBadProp +182 stdcall FBadColumnSet@4(ptr) FBadColumnSet +183 stub RTFSync@12 +184 stub RTFSync +185 stub WrapCompressedRTFStream@12 +186 stub WrapCompressedRTFStream +187 stub __ValidateParameters@8 +188 stub __CPPValidateParameters@8 +189 stub FBadSortOrderSet@4 +190 stdcall FBadEntryList@4(ptr) FBadEntryList +191 stub FBadRestriction@4 +192 stub ScUNCFromLocalPath@12 +193 stub ScLocalPathFromUNC@12 +194 stub HrIStorageFromStream@16 +195 stub HrValidateIPMSubtree@20 +196 stub OpenIMsgSession@12 +197 stub CloseIMsgSession@4 +198 stub OpenIMsgOnIStg@44 +199 stub SetAttribIMsgOnIStg@16 +200 stub GetAttribIMsgOnIStg@12 +201 stub MapStorageSCode@4 +202 stub ScMAPIXFromCMC +203 stub ScMAPIXFromSMAPI +204 stub EncodeID@12 +205 stub FDecodeID@12 +206 stub CchOfEncoding@4 +207 stdcall CbOfEncoded@4(ptr) CbOfEncoded +208 stub MAPISendDocuments +209 stdcall MAPILogon(long ptr ptr long long ptr) +210 stub MAPILogoff +211 stub MAPISendMail +212 stub MAPISaveMail +213 stub MAPIReadMail +214 stub MAPIFindNext +215 stub MAPIDeleteMail +217 stub MAPIAddress +218 stub MAPIDetails +219 stub MAPIResolveName +220 stub BMAPISendMail +221 stub BMAPISaveMail +222 stub BMAPIReadMail +223 stub BMAPIGetReadMail +224 stub BMAPIFindNext +225 stub BMAPIAddress +226 stub BMAPIGetAddress +227 stub BMAPIDetails +228 stub BMAPIResolveName +229 stub cmc_act_on +230 stub cmc_free +231 stub cmc_list +232 stub cmc_logoff +233 stub cmc_logon +234 stub cmc_look_up +235 stdcall cmc_query_configuration( long long ptr ptr ) +236 stub cmc_read +237 stub cmc_send +238 stub cmc_send_documents +239 stub HrDispatchNotifications@4 +241 stub HrValidateParameters@8 +244 stub ScCreateConversationIndex@16 +246 stub HrGetOmiProvidersFlags +247 stub HrGetOmiProvidersFlags@8 +248 stub HrSetOmiProvidersFlagsInvalid +249 stub HrSetOmiProvidersFlagsInvalid@4 +250 stub GetOutlookVersion +251 stub GetOutlookVersion@0 +252 stub FixMAPI +253 stub FixMAPI@0 +# This entry point is sometimes used to detect if the mapi dll came from Outlook +#254 stub FGetComponentPath +#255 stub FGetComponentPath@20 diff --git a/reactos/dll/mapi32/mapi32.xml b/reactos/dll/mapi32/mapi32.xml new file mode 100644 index 00000000000..7db672d354a --- /dev/null +++ b/reactos/dll/mapi32/mapi32.xml @@ -0,0 +1,19 @@ + + + . + include/wine + + 0x501 + + ntdll + kernel32 + shlwapi + wine + uuid + advapi32 + mapi32_main.c + imalloc.c + prop.c + util.c + mapi32.spec + diff --git a/reactos/dll/mapi32/mapi32_main.c b/reactos/dll/mapi32/mapi32_main.c new file mode 100644 index 00000000000..5fadb5a2864 --- /dev/null +++ b/reactos/dll/mapi32/mapi32_main.c @@ -0,0 +1,94 @@ +/* + * MAPI basics + * + * Copyright 2001 CodeWeavers Inc. + * + * 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 + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "objbase.h" +#include "mapix.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mapi); + +LONG MAPI_ObjectCount = 0; + +/*********************************************************************** + * DllMain (MAPI32.init) + */ +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID fImpLoad) +{ + TRACE("(%p,%ld,%p)\n", hinstDLL, fdwReason, fImpLoad); + + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hinstDLL); + break; + case DLL_PROCESS_DETACH: + TRACE("DLL_PROCESS_DETACH: %ld objects remaining\n", MAPI_ObjectCount); + break; + } + return TRUE; +} + +/*********************************************************************** + * DllCanUnloadNow (MAPI32.28) + * + * Determine if this dll can be unloaded from the callers address space. + * + * PARAMS + * None. + * + * RETURNS + * S_OK, if the dll can be unloaded, + * S_FALSE, otherwise. + */ +HRESULT WINAPI DllCanUnloadNow(void) +{ + return MAPI_ObjectCount == 0 ? S_OK : S_FALSE; +} + +HRESULT WINAPI MAPIInitialize ( LPVOID lpMapiInit ) +{ + ERR("Stub\n"); + return MAPI_E_NOT_INITIALIZED; +} + +ULONG WINAPI MAPILogon(ULONG ulUIParam, LPSTR lpszProfileName, LPSTR +lpszPassword, FLAGS flFlags, ULONG ulReserver, LPLHANDLE lplhSession) +{ + ERR("Stub\n"); + return MAPI_E_LOGON_FAILED; +} + +HRESULT WINAPI MAPILogonEx(ULONG_PTR ulUIParam, LPWSTR lpszProfileName, + LPWSTR lpszPassword, ULONG flFlags, + LPMAPISESSION *lppSession) +{ + ERR("Stub\n"); + return MAPI_E_LOGON_FAILED; +} + +VOID WINAPI MAPIUninitialize(void) +{ + ERR("Stub\n"); +} diff --git a/reactos/dll/mapi32/prop.c b/reactos/dll/mapi32/prop.c new file mode 100644 index 00000000000..2477b21aaae --- /dev/null +++ b/reactos/dll/mapi32/prop.c @@ -0,0 +1,2549 @@ +/* + * Property 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 +#define NONAMELESSUNION +#define NONAMELESSSTRUCT +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winerror.h" +#include "winternl.h" +#include "objbase.h" +#include "shlwapi.h" +#include "wine/list.h" +#include "wine/debug.h" +#include "wine/unicode.h" +#include "mapival.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mapi); + +BOOL WINAPI FBadRglpszA(LPSTR*,ULONG); + +/* Internal: Check if a property value array is invalid */ +static inline ULONG PROP_BadArray(LPSPropValue lpProp, size_t elemSize) +{ + return IsBadReadPtr(lpProp->Value.MVi.lpi, lpProp->Value.MVi.cValues * elemSize); +} + +/************************************************************************* + * PropCopyMore@16 (MAPI32.76) + * + * Copy a property value. + * + * PARAMS + * lpDest [O] Destination for the copied value + * lpSrc [I] Property value to copy to lpDest + * lpMore [I] Linked memory allocation function (pass MAPIAllocateMore()) + * lpOrig [I] Original allocation to which memory will be linked + * + * RETURNS + * Success: S_OK. lpDest contains a deep copy of lpSrc. + * Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid, + * MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails. + * + * NOTES + * Any elements within the property returned should not be individually + * freed, as they will be freed when lpOrig is. + */ +SCODE WINAPI PropCopyMore(LPSPropValue lpDest, LPSPropValue lpSrc, + ALLOCATEMORE *lpMore, LPVOID lpOrig) +{ + ULONG ulLen, i; + SCODE scode = S_OK; + + TRACE("(%p,%p,%p,%p)\n", lpDest, lpSrc, lpMore, lpOrig); + + if (!lpDest || IsBadWritePtr(lpDest, sizeof(SPropValue)) || + FBadProp(lpSrc) || !lpMore) + return MAPI_E_INVALID_PARAMETER; + + /* Shallow copy first, this is sufficient for properties without pointers */ + *lpDest = *lpSrc; + + switch (PROP_TYPE(lpSrc->ulPropTag)) + { + case PT_CLSID: + scode = lpMore(sizeof(GUID), lpOrig, (LPVOID*)&lpDest->Value.lpguid); + if (SUCCEEDED(scode)) + memcpy(lpDest->Value.lpguid, lpSrc->Value.lpguid, sizeof(GUID)); + break; + case PT_STRING8: + ulLen = lstrlenA(lpSrc->Value.lpszA) + 1u; + scode = lpMore(ulLen, lpOrig, (LPVOID*)&lpDest->Value.lpszA); + if (SUCCEEDED(scode)) + memcpy(lpDest->Value.lpszA, lpSrc->Value.lpszA, ulLen); + break; + case PT_UNICODE: + ulLen = (strlenW(lpSrc->Value.lpszW) + 1u) * sizeof(WCHAR); + scode = lpMore(ulLen, lpOrig, (LPVOID*)&lpDest->Value.lpszW); + if (SUCCEEDED(scode)) + memcpy(lpDest->Value.lpszW, lpSrc->Value.lpszW, ulLen); + break; + case PT_BINARY: + scode = lpMore(lpSrc->Value.bin.cb, lpOrig, (LPVOID*)&lpDest->Value.bin.lpb); + if (SUCCEEDED(scode)) + memcpy(lpDest->Value.bin.lpb, lpSrc->Value.bin.lpb, lpSrc->Value.bin.cb); + break; + default: + if (lpSrc->ulPropTag & MV_FLAG) + { + ulLen = UlPropSize(lpSrc); + + if (PROP_TYPE(lpSrc->ulPropTag) == PT_MV_STRING8 || + PROP_TYPE(lpSrc->ulPropTag) == PT_MV_UNICODE) + { + /* UlPropSize doesn't account for the string pointers */ + ulLen += lpSrc->Value.MVszA.cValues * sizeof(char*); + } + else if (PROP_TYPE(lpSrc->ulPropTag) == PT_MV_BINARY) + { + /* UlPropSize doesn't account for the SBinary structs */ + ulLen += lpSrc->Value.MVbin.cValues * sizeof(SBinary); + } + + lpDest->Value.MVi.cValues = lpSrc->Value.MVi.cValues; + scode = lpMore(ulLen, lpOrig, (LPVOID*)&lpDest->Value.MVi.lpi); + if (FAILED(scode)) + break; + + /* Note that we could allocate the memory for each value in a + * multi-value property seperately, however if an allocation failed + * we would be left with a bunch of allocated memory, which (while + * not really leaked) is unusable until lpOrig is freed. So for + * strings and binary arrays we make a single allocation for all + * of the data. This is consistent since individual elements can't + * be freed anyway. + */ + + switch (PROP_TYPE(lpSrc->ulPropTag)) + { + case PT_MV_STRING8: + { + char *lpNextStr = (char*)(lpDest->Value.MVszA.lppszA + + lpDest->Value.MVszA.cValues); + + for (i = 0; i < lpSrc->Value.MVszA.cValues; i++) + { + ULONG ulStrLen = lstrlenA(lpSrc->Value.MVszA.lppszA[i]) + 1u; + + lpDest->Value.MVszA.lppszA[i] = lpNextStr; + memcpy(lpNextStr, lpSrc->Value.MVszA.lppszA[i], ulStrLen); + lpNextStr += ulStrLen; + } + break; + } + case PT_MV_UNICODE: + { + WCHAR *lpNextStr = (WCHAR*)(lpDest->Value.MVszW.lppszW + + lpDest->Value.MVszW.cValues); + + for (i = 0; i < lpSrc->Value.MVszW.cValues; i++) + { + ULONG ulStrLen = strlenW(lpSrc->Value.MVszW.lppszW[i]) + 1u; + + lpDest->Value.MVszW.lppszW[i] = lpNextStr; + memcpy(lpNextStr, lpSrc->Value.MVszW.lppszW[i], ulStrLen * sizeof(WCHAR)); + lpNextStr += ulStrLen; + } + break; + } + case PT_MV_BINARY: + { + LPBYTE lpNext = (LPBYTE)(lpDest->Value.MVbin.lpbin + + lpDest->Value.MVbin.cValues); + + for (i = 0; i < lpSrc->Value.MVszW.cValues; i++) + { + lpDest->Value.MVbin.lpbin[i].cb = lpSrc->Value.MVbin.lpbin[i].cb; + lpDest->Value.MVbin.lpbin[i].lpb = lpNext; + memcpy(lpNext, lpSrc->Value.MVbin.lpbin[i].lpb, lpDest->Value.MVbin.lpbin[i].cb); + lpNext += lpDest->Value.MVbin.lpbin[i].cb; + } + break; + } + default: + /* No embedded pointers, just copy the data over */ + memcpy(lpDest->Value.MVi.lpi, lpSrc->Value.MVi.lpi, ulLen); + break; + } + break; + } + } + return scode; +} + +/************************************************************************* + * UlPropSize@4 (MAPI32.77) + * + * Determine the size of a property in bytes. + * + * PARAMS + * lpProp [I] Property to determine the size of + * + * RETURNS + * Success: The size of the value in lpProp. + * Failure: 0, if a multi-value (array) property is invalid or the type of lpProp + * is unknown. + * + * NOTES + * - The size returned does not include the size of the SPropValue struct + * or the size of the array of pointers for multi-valued properties that + * contain pointers (such as PT_MV_STRING8 or PT-MV_UNICODE). + * - MSDN incorrectly states that this function returns MAPI_E_CALL_FAILED if + * lpProp is invalid. In reality no checking is performed and this function + * will crash if passed an invalid property, or return 0 if the property + * type is PT_OBJECT or is unknown. + */ +ULONG WINAPI UlPropSize(LPSPropValue lpProp) +{ + ULONG ulRet = 1u, i; + + TRACE("(%p)\n", lpProp); + + switch (PROP_TYPE(lpProp->ulPropTag)) + { + case PT_MV_I2: ulRet = lpProp->Value.MVi.cValues; + case PT_BOOLEAN: + case PT_I2: ulRet *= sizeof(USHORT); + break; + case PT_MV_I4: ulRet = lpProp->Value.MVl.cValues; + case PT_ERROR: + case PT_I4: ulRet *= sizeof(LONG); + break; + case PT_MV_I8: ulRet = lpProp->Value.MVli.cValues; + case PT_I8: ulRet *= sizeof(LONG64); + break; + case PT_MV_R4: ulRet = lpProp->Value.MVflt.cValues; + case PT_R4: ulRet *= sizeof(float); + break; + case PT_MV_APPTIME: + case PT_MV_R8: ulRet = lpProp->Value.MVdbl.cValues; + case PT_APPTIME: + case PT_R8: ulRet *= sizeof(double); + break; + case PT_MV_CURRENCY: ulRet = lpProp->Value.MVcur.cValues; + case PT_CURRENCY: ulRet *= sizeof(CY); + break; + case PT_MV_SYSTIME: ulRet = lpProp->Value.MVft.cValues; + case PT_SYSTIME: ulRet *= sizeof(FILETIME); + break; + case PT_MV_CLSID: ulRet = lpProp->Value.MVguid.cValues; + case PT_CLSID: ulRet *= sizeof(GUID); + break; + case PT_MV_STRING8: ulRet = 0u; + for (i = 0; i < lpProp->Value.MVszA.cValues; i++) + ulRet += (lstrlenA(lpProp->Value.MVszA.lppszA[i]) + 1u); + break; + case PT_STRING8: ulRet = lstrlenA(lpProp->Value.lpszA) + 1u; + break; + case PT_MV_UNICODE: ulRet = 0u; + for (i = 0; i < lpProp->Value.MVszW.cValues; i++) + ulRet += (strlenW(lpProp->Value.MVszW.lppszW[i]) + 1u); + ulRet *= sizeof(WCHAR); + break; + case PT_UNICODE: ulRet = (lstrlenW(lpProp->Value.lpszW) + 1u) * sizeof(WCHAR); + break; + case PT_MV_BINARY: ulRet = 0u; + for (i = 0; i < lpProp->Value.MVbin.cValues; i++) + ulRet += lpProp->Value.MVbin.lpbin[i].cb; + break; + case PT_BINARY: ulRet = lpProp->Value.bin.cb; + break; + case PT_OBJECT: + default: ulRet = 0u; + break; + } + + return ulRet; +} + +/************************************************************************* + * FPropContainsProp@12 (MAPI32.78) + * + * Find a property with a given property tag in a property array. + * + * PARAMS + * lpHaystack [I] Property to match to + * lpNeedle [I] Property to find in lpHaystack + * ulFuzzy [I] Flags controlling match type and strictness (FL_* flags from "mapidefs.h") + * + * RETURNS + * TRUE, if lpNeedle matches lpHaystack according to the criteria of ulFuzzy. + * + * NOTES + * Only property types of PT_STRING8 and PT_BINARY are handled by this function. + */ +BOOL WINAPI FPropContainsProp(LPSPropValue lpHaystack, LPSPropValue lpNeedle, ULONG ulFuzzy) +{ + TRACE("(%p,%p,0x%08lx)\n", lpHaystack, lpNeedle, ulFuzzy); + + if (FBadProp(lpHaystack) || FBadProp(lpNeedle) || + PROP_TYPE(lpHaystack->ulPropTag) != PROP_TYPE(lpNeedle->ulPropTag)) + return FALSE; + + /* FIXME: Do later versions support Unicode as well? */ + + if (PROP_TYPE(lpHaystack->ulPropTag) == PT_STRING8) + { + DWORD dwFlags = 0, dwNeedleLen, dwHaystackLen; + + if (ulFuzzy & FL_IGNORECASE) + dwFlags |= NORM_IGNORECASE; + if (ulFuzzy & FL_IGNORENONSPACE) + dwFlags |= NORM_IGNORENONSPACE; + if (ulFuzzy & FL_LOOSE) + dwFlags |= (NORM_IGNORECASE|NORM_IGNORENONSPACE|NORM_IGNORESYMBOLS); + + dwNeedleLen = lstrlenA(lpNeedle->Value.lpszA); + dwHaystackLen = lstrlenA(lpHaystack->Value.lpszA); + + if ((ulFuzzy & (FL_SUBSTRING|FL_PREFIX)) == FL_PREFIX) + { + if (dwNeedleLen <= dwHaystackLen && + CompareStringA(LOCALE_USER_DEFAULT, dwFlags, + lpHaystack->Value.lpszA, dwNeedleLen, + lpNeedle->Value.lpszA, dwNeedleLen) == CSTR_EQUAL) + return TRUE; /* needle is a prefix of haystack */ + } + else if ((ulFuzzy & (FL_SUBSTRING|FL_PREFIX)) == FL_SUBSTRING) + { + LPSTR (WINAPI *pStrChrFn)(LPCSTR,WORD) = StrChrA; + LPSTR lpStr = lpHaystack->Value.lpszA; + + if (dwFlags & NORM_IGNORECASE) + pStrChrFn = StrChrIA; + + while ((lpStr = pStrChrFn(lpStr, *lpNeedle->Value.lpszA)) != NULL) + { + dwHaystackLen -= (lpStr - lpHaystack->Value.lpszA); + if (dwNeedleLen <= dwHaystackLen && + CompareStringA(LOCALE_USER_DEFAULT, dwFlags, + lpStr, dwNeedleLen, + lpNeedle->Value.lpszA, dwNeedleLen) == CSTR_EQUAL) + return TRUE; /* needle is a substring of haystack */ + lpStr++; + } + } + else if (CompareStringA(LOCALE_USER_DEFAULT, dwFlags, + lpHaystack->Value.lpszA, dwHaystackLen, + lpNeedle->Value.lpszA, dwNeedleLen) == CSTR_EQUAL) + return TRUE; /* full string match */ + } + else if (PROP_TYPE(lpHaystack->ulPropTag) == PT_BINARY) + { + if ((ulFuzzy & (FL_SUBSTRING|FL_PREFIX)) == FL_PREFIX) + { + if (lpNeedle->Value.bin.cb <= lpHaystack->Value.bin.cb && + !memcmp(lpNeedle->Value.bin.lpb, lpHaystack->Value.bin.lpb, + lpNeedle->Value.bin.cb)) + return TRUE; /* needle is a prefix of haystack */ + } + else if ((ulFuzzy & (FL_SUBSTRING|FL_PREFIX)) == FL_SUBSTRING) + { + ULONG ulLen = lpHaystack->Value.bin.cb; + LPBYTE lpb = lpHaystack->Value.bin.lpb; + + while ((lpb = memchr(lpb, *lpNeedle->Value.bin.lpb, ulLen)) != NULL) + { + ulLen = lpHaystack->Value.bin.cb - (lpb - lpHaystack->Value.bin.lpb); + if (lpNeedle->Value.bin.cb <= ulLen && + !memcmp(lpNeedle->Value.bin.lpb, lpb, lpNeedle->Value.bin.cb)) + return TRUE; /* needle is a substring of haystack */ + lpb++; + } + } + else if (!LPropCompareProp(lpHaystack, lpNeedle)) + return TRUE; /* needle is an exact match with haystack */ + + } + return FALSE; +} + +/************************************************************************* + * FPropCompareProp@12 (MAPI32.79) + * + * Compare two properties. + * + * PARAMS + * lpPropLeft [I] Left hand property to compare to lpPropRight + * ulOp [I] Comparison operator (RELOP_* enum from "mapidefs.h") + * lpPropRight [I] Right hand property to compare to lpPropLeft + * + * RETURNS + * TRUE, if the comparison is true, FALSE otherwise. + */ +BOOL WINAPI FPropCompareProp(LPSPropValue lpPropLeft, ULONG ulOp, LPSPropValue lpPropRight) +{ + LONG iCmp; + + TRACE("(%p,%ld,%p)\n", lpPropLeft, ulOp, lpPropRight); + + if (ulOp > RELOP_RE || FBadProp(lpPropLeft) || FBadProp(lpPropRight)) + return FALSE; + + if (ulOp == RELOP_RE) + { + FIXME("Comparison operator RELOP_RE not yet implemented!\n"); + return FALSE; + } + + iCmp = LPropCompareProp(lpPropLeft, lpPropRight); + + switch (ulOp) + { + case RELOP_LT: return iCmp < 0 ? TRUE : FALSE; + case RELOP_LE: return iCmp <= 0 ? TRUE : FALSE; + case RELOP_GT: return iCmp > 0 ? TRUE : FALSE; + case RELOP_GE: return iCmp >= 0 ? TRUE : FALSE; + case RELOP_EQ: return iCmp == 0 ? TRUE : FALSE; + case RELOP_NE: return iCmp != 0 ? TRUE : FALSE; + } + return FALSE; +} + +/************************************************************************* + * LPropCompareProp@8 (MAPI32.80) + * + * Compare two properties. + * + * PARAMS + * lpPropLeft [I] Left hand property to compare to lpPropRight + * lpPropRight [I] Right hand property to compare to lpPropLeft + * + * RETURNS + * An integer less than, equal to or greater than 0, indicating that + * lpszStr is less than, the same, or greater than lpszComp. + */ +LONG WINAPI LPropCompareProp(LPSPropValue lpPropLeft, LPSPropValue lpPropRight) +{ + LONG iRet; + + TRACE("(%p->0x%08lx,%p->0x%08lx)\n", lpPropLeft, lpPropLeft->ulPropTag, + lpPropRight, lpPropRight->ulPropTag); + + /* If the properties are not the same, sort by property type */ + if (PROP_TYPE(lpPropLeft->ulPropTag) != PROP_TYPE(lpPropRight->ulPropTag)) + return (LONG)PROP_TYPE(lpPropLeft->ulPropTag) - (LONG)PROP_TYPE(lpPropRight->ulPropTag); + + switch (PROP_TYPE(lpPropLeft->ulPropTag)) + { + case PT_UNSPECIFIED: + case PT_NULL: + return 0; /* NULLs are equal */ + case PT_I2: + return lpPropLeft->Value.i - lpPropRight->Value.i; + case PT_I4: + return lpPropLeft->Value.l - lpPropRight->Value.l; + case PT_I8: + if (lpPropLeft->Value.li.QuadPart > lpPropRight->Value.li.QuadPart) + return 1; + if (lpPropLeft->Value.li.QuadPart == lpPropRight->Value.li.QuadPart) + return 0; + return -1; + case PT_R4: + if (lpPropLeft->Value.flt > lpPropRight->Value.flt) + return 1; + if (lpPropLeft->Value.flt == lpPropRight->Value.flt) + return 0; + return -1; + case PT_APPTIME: + case PT_R8: + if (lpPropLeft->Value.dbl > lpPropRight->Value.dbl) + return 1; + if (lpPropLeft->Value.dbl == lpPropRight->Value.dbl) + return 0; + return -1; + case PT_CURRENCY: + if (lpPropLeft->Value.cur.int64 > lpPropRight->Value.cur.int64) + return 1; + if (lpPropLeft->Value.cur.int64 == lpPropRight->Value.cur.int64) + return 0; + return -1; + case PT_SYSTIME: + return CompareFileTime(&lpPropLeft->Value.ft, &lpPropRight->Value.ft); + case PT_BOOLEAN: + return (lpPropLeft->Value.b ? 1 : 0) - (lpPropRight->Value.b ? 1 : 0); + case PT_BINARY: + if (lpPropLeft->Value.bin.cb == lpPropRight->Value.bin.cb) + iRet = memcmp(lpPropLeft->Value.bin.lpb, lpPropRight->Value.bin.lpb, + lpPropLeft->Value.bin.cb); + else + { + iRet = memcmp(lpPropLeft->Value.bin.lpb, lpPropRight->Value.bin.lpb, + min(lpPropLeft->Value.bin.cb, lpPropRight->Value.bin.cb)); + + if (!iRet) + iRet = lpPropLeft->Value.bin.cb - lpPropRight->Value.bin.cb; + } + return iRet; + case PT_STRING8: + return lstrcmpA(lpPropLeft->Value.lpszA, lpPropRight->Value.lpszA); + case PT_UNICODE: + return strcmpW(lpPropLeft->Value.lpszW, lpPropRight->Value.lpszW); + case PT_ERROR: + if (lpPropLeft->Value.err > lpPropRight->Value.err) + return 1; + if (lpPropLeft->Value.err == lpPropRight->Value.err) + return 0; + return -1; + case PT_CLSID: + return memcmp(lpPropLeft->Value.lpguid, lpPropRight->Value.lpguid, + sizeof(GUID)); + } + FIXME("Unhandled property type %ld", PROP_TYPE(lpPropLeft->ulPropTag)); + return 0; +} + +/************************************************************************* + * HrGetOneProp@8 (MAPI32.135) + * + * Get a property value from an IMAPIProp object. + * + * PARAMS + * lpIProp [I] IMAPIProp object to get the property value in + * ulPropTag [I] Property tag of the property to get + * lppProp [O] Destination for the returned property + * + * RETURNS + * Success: S_OK. *lppProp contains the property value requested. + * Failure: MAPI_E_NOT_FOUND, if no property value has the tag given by ulPropTag. + */ +HRESULT WINAPI HrGetOneProp(LPMAPIPROP lpIProp, ULONG ulPropTag, LPSPropValue *lppProp) +{ + SPropTagArray pta; + ULONG ulCount; + HRESULT hRet; + + TRACE("(%p,%ld,%p)\n", lpIProp, ulPropTag, lppProp); + + pta.cValues = 1u; + pta.aulPropTag[0] = ulPropTag; + hRet = IMAPIProp_GetProps(lpIProp, &pta, 0u, &ulCount, lppProp); + if (hRet == MAPI_W_ERRORS_RETURNED) + { + MAPIFreeBuffer(*lppProp); + *lppProp = NULL; + hRet = MAPI_E_NOT_FOUND; + } + return hRet; +} + +/************************************************************************* + * HrSetOneProp@8 (MAPI32.136) + * + * Set a property value in an IMAPIProp object. + * + * PARAMS + * lpIProp [I] IMAPIProp object to set the property value in + * lpProp [I] Property value to set + * + * RETURNS + * Success: S_OK. The value in lpProp is set in lpIProp. + * Failure: An error result from IMAPIProp_SetProps(). + */ +HRESULT WINAPI HrSetOneProp(LPMAPIPROP lpIProp, LPSPropValue lpProp) +{ + TRACE("(%p,%p)\n", lpIProp, lpProp); + + return IMAPIProp_SetProps(lpIProp, 1u, lpProp, NULL); +} + +/************************************************************************* + * FPropExists@8 (MAPI32.137) + * + * Find a property with a given property tag in an IMAPIProp object. + * + * PARAMS + * lpIProp [I] IMAPIProp object to find the property tag in + * ulPropTag [I] Property tag to find + * + * RETURNS + * TRUE, if ulPropTag matches a property held in lpIProp, + * FALSE, otherwise. + * + * NOTES + * if ulPropTag has a property type of PT_UNSPECIFIED, then only the property + * Ids need to match for a successful match to occur. + */ + BOOL WINAPI FPropExists(LPMAPIPROP lpIProp, ULONG ulPropTag) + { + BOOL bRet = FALSE; + + TRACE("(%p,%ld)\n", lpIProp, ulPropTag); + + if (lpIProp) + { + LPSPropTagArray lpTags; + ULONG i; + + if (FAILED(IMAPIProp_GetPropList(lpIProp, 0u, &lpTags))) + return FALSE; + + for (i = 0; i < lpTags->cValues; i++) + { + if (!FBadPropTag(lpTags->aulPropTag[i]) && + (lpTags->aulPropTag[i] == ulPropTag || + (PROP_TYPE(ulPropTag) == PT_UNSPECIFIED && + PROP_ID(lpTags->aulPropTag[i]) == lpTags->aulPropTag[i]))) + { + bRet = TRUE; + break; + } + } + MAPIFreeBuffer(lpTags); + } + return bRet; +} + +/************************************************************************* + * PpropFindProp@12 (MAPI32.138) + * + * Find a property with a given property tag in a property array. + * + * PARAMS + * lpProps [I] Property array to search + * cValues [I] Number of properties in lpProps + * ulPropTag [I] Property tag to find + * + * RETURNS + * A pointer to the matching property, or NULL if none was found. + * + * NOTES + * if ulPropTag has a property type of PT_UNSPECIFIED, then only the property + * Ids need to match for a successful match to occur. + */ +LPSPropValue WINAPI PpropFindProp(LPSPropValue lpProps, ULONG cValues, ULONG ulPropTag) +{ + TRACE("(%p,%ld,%ld)\n", lpProps, cValues, ulPropTag); + + if (lpProps && cValues) + { + ULONG i; + for (i = 0; i < cValues; i++) + { + if (!FBadPropTag(lpProps[i].ulPropTag) && + (lpProps[i].ulPropTag == ulPropTag || + (PROP_TYPE(ulPropTag) == PT_UNSPECIFIED && + PROP_ID(lpProps[i].ulPropTag) == PROP_ID(ulPropTag)))) + return &lpProps[i]; + } + } + return NULL; +} + +/************************************************************************* + * FreePadrlist@4 (MAPI32.139) + * + * Free the memory used by an address book list. + * + * PARAMS + * lpAddrs [I] Address book list to free + * + * RETURNS + * Nothing. + */ +VOID WINAPI FreePadrlist(LPADRLIST lpAddrs) +{ + TRACE("(%p)\n", lpAddrs); + + /* Structures are binary compatible; use the same implementation */ + return FreeProws((LPSRowSet)lpAddrs); +} + +/************************************************************************* + * FreeProws@4 (MAPI32.140) + * + * Free the memory used by a row set. + * + * PARAMS + * lpRowSet [I] Row set to free + * + * RETURNS + * Nothing. + */ +VOID WINAPI FreeProws(LPSRowSet lpRowSet) +{ + TRACE("(%p)\n", lpRowSet); + + if (lpRowSet) + { + ULONG i; + + for (i = 0; i < lpRowSet->cRows; i++) + MAPIFreeBuffer(lpRowSet->aRow[i].lpProps); + + MAPIFreeBuffer(lpRowSet); + } +} + +/************************************************************************* + * ScCountProps@12 (MAPI32.170) + * + * Validate and determine the length of an array of properties. + * + * PARAMS + * iCount [I] Length of the lpProps array + * lpProps [I] Array of properties to validate/size + * pcBytes [O] If non-NULL, destination for the size of the property array + * + * RETURNS + * Success: S_OK. If pcBytes is non-NULL, it contains the size of the propery array. + * Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid or validation + * of the property array fails. + */ +SCODE WINAPI ScCountProps(INT iCount, LPSPropValue lpProps, ULONG *pcBytes) +{ + ULONG i, ulCount = iCount, ulBytes = 0; + + TRACE("(%d,%p,%p)\n", iCount, lpProps, pcBytes); + + if (iCount <= 0 || !lpProps || + IsBadReadPtr(lpProps, iCount * sizeof(SPropValue))) + return MAPI_E_INVALID_PARAMETER; + + for (i = 0; i < ulCount; i++) + { + ULONG ulPropSize = 0; + + if (FBadProp(&lpProps[i]) || lpProps[i].ulPropTag == PROP_ID_NULL || + lpProps[i].ulPropTag == PROP_ID_INVALID) + return MAPI_E_INVALID_PARAMETER; + + if (PROP_TYPE(lpProps[i].ulPropTag) != PT_OBJECT) + { + ulPropSize = UlPropSize(&lpProps[i]); + if (!ulPropSize) + return MAPI_E_INVALID_PARAMETER; + } + + switch (PROP_TYPE(lpProps[i].ulPropTag)) + { + case PT_STRING8: + case PT_UNICODE: + case PT_CLSID: + case PT_BINARY: + case PT_MV_I2: + case PT_MV_I4: + case PT_MV_I8: + case PT_MV_R4: + case PT_MV_R8: + case PT_MV_CURRENCY: + case PT_MV_SYSTIME: + case PT_MV_APPTIME: + ulPropSize += sizeof(SPropValue); + break; + case PT_MV_CLSID: + ulPropSize += lpProps[i].Value.MVszA.cValues * sizeof(char*) + sizeof(SPropValue); + break; + case PT_MV_STRING8: + case PT_MV_UNICODE: + ulPropSize += lpProps[i].Value.MVszA.cValues * sizeof(char*) + sizeof(SPropValue); + break; + case PT_MV_BINARY: + ulPropSize += lpProps[i].Value.MVbin.cValues * sizeof(SBinary) + sizeof(SPropValue); + break; + default: + ulPropSize = sizeof(SPropValue); + break; + } + ulBytes += ulPropSize; + } + if (pcBytes) + *pcBytes = ulBytes; + + return S_OK; +} + +/************************************************************************* + * ScCopyProps@16 (MAPI32.171) + * + * Copy an array of property values into a buffer suited for serialisation. + * + * PARAMS + * cValues [I] Number of properties in lpProps + * lpProps [I] Property array to copy + * lpDst [O] Destination for the serialised data + * lpCount [O] If non-NULL, destination for the number of bytes of data written to lpDst + * + * RETURNS + * Success: S_OK. lpDst contains the serialised data from lpProps. + * Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid. + * + * NOTES + * The resulting property value array is stored in a contiguous block starting at lpDst. + */ +SCODE WINAPI ScCopyProps(int cValues, LPSPropValue lpProps, LPVOID lpDst, ULONG *lpCount) +{ + LPSPropValue lpDest = (LPSPropValue)lpDst; + char *lpDataDest = (char *)(lpDest + cValues); + ULONG ulLen, i; + int iter; + + TRACE("(%d,%p,%p,%p)\n", cValues, lpProps, lpDst, lpCount); + + if (!lpProps || cValues < 0 || !lpDest) + return MAPI_E_INVALID_PARAMETER; + + memcpy(lpDst, lpProps, cValues * sizeof(SPropValue)); + + for (iter = 0; iter < cValues; iter++) + { + switch (PROP_TYPE(lpProps->ulPropTag)) + { + case PT_CLSID: + lpDest->Value.lpguid = (LPGUID)lpDataDest; + memcpy(lpDest->Value.lpguid, lpProps->Value.lpguid, sizeof(GUID)); + lpDataDest += sizeof(GUID); + break; + case PT_STRING8: + ulLen = lstrlenA(lpProps->Value.lpszA) + 1u; + lpDest->Value.lpszA = lpDataDest; + memcpy(lpDest->Value.lpszA, lpProps->Value.lpszA, ulLen); + lpDataDest += ulLen; + break; + case PT_UNICODE: + ulLen = (strlenW(lpProps->Value.lpszW) + 1u) * sizeof(WCHAR); + lpDest->Value.lpszW = (LPWSTR)lpDataDest; + memcpy(lpDest->Value.lpszW, lpProps->Value.lpszW, ulLen); + lpDataDest += ulLen; + break; + case PT_BINARY: + lpDest->Value.bin.lpb = (LPBYTE)lpDataDest; + memcpy(lpDest->Value.bin.lpb, lpProps->Value.bin.lpb, lpProps->Value.bin.cb); + lpDataDest += lpProps->Value.bin.cb; + break; + default: + if (lpProps->ulPropTag & MV_FLAG) + { + lpDest->Value.MVi.cValues = lpProps->Value.MVi.cValues; + /* Note: Assignment uses lppszA but covers all cases by union aliasing */ + lpDest->Value.MVszA.lppszA = (char**)lpDataDest; + + switch (PROP_TYPE(lpProps->ulPropTag)) + { + case PT_MV_STRING8: + { + lpDataDest += lpProps->Value.MVszA.cValues * sizeof(char *); + + for (i = 0; i < lpProps->Value.MVszA.cValues; i++) + { + ULONG ulStrLen = lstrlenA(lpProps->Value.MVszA.lppszA[i]) + 1u; + + lpDest->Value.MVszA.lppszA[i] = lpDataDest; + memcpy(lpDataDest, lpProps->Value.MVszA.lppszA[i], ulStrLen); + lpDataDest += ulStrLen; + } + break; + } + case PT_MV_UNICODE: + { + lpDataDest += lpProps->Value.MVszW.cValues * sizeof(WCHAR *); + + for (i = 0; i < lpProps->Value.MVszW.cValues; i++) + { + ULONG ulStrLen = (strlenW(lpProps->Value.MVszW.lppszW[i]) + 1u) * sizeof(WCHAR); + + lpDest->Value.MVszW.lppszW[i] = (LPWSTR)lpDataDest; + memcpy(lpDataDest, lpProps->Value.MVszW.lppszW[i], ulStrLen); + lpDataDest += ulStrLen; + } + break; + } + case PT_MV_BINARY: + { + lpDataDest += lpProps->Value.MVszW.cValues * sizeof(SBinary); + + for (i = 0; i < lpProps->Value.MVszW.cValues; i++) + { + lpDest->Value.MVbin.lpbin[i].cb = lpProps->Value.MVbin.lpbin[i].cb; + lpDest->Value.MVbin.lpbin[i].lpb = (LPBYTE)lpDataDest; + memcpy(lpDataDest, lpProps->Value.MVbin.lpbin[i].lpb, lpDest->Value.MVbin.lpbin[i].cb); + lpDataDest += lpDest->Value.MVbin.lpbin[i].cb; + } + break; + } + default: + /* No embedded pointers, just copy the data over */ + ulLen = UlPropSize(lpProps); + memcpy(lpDest->Value.MVi.lpi, lpProps->Value.MVi.lpi, ulLen); + lpDataDest += ulLen; + break; + } + break; + } + } + lpDest++; + lpProps++; + } + if (lpCount) + *lpCount = lpDataDest - (char *)lpDst; + + return S_OK; +} + +/************************************************************************* + * ScRelocProps@20 (MAPI32.172) + * + * Relocate the pointers in an array of property values after it has been copied. + * + * PARAMS + * cValues [I] Number of properties in lpProps + * lpProps [O] Property array to relocate the pointers in. + * lpOld [I] Position where the data was copied from + * lpNew [I] Position where the data was copied to + * lpCount [O] If non-NULL, destination for the number of bytes of data at lpDst + * + * RETURNS + * Success: S_OK. Any pointers in lpProps are relocated. + * Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid. + * + * NOTES + * MSDN states that this function can be used for serialisation by passing + * NULL as either lpOld or lpNew, thus converting any pointers in lpProps + * between offsets and pointers. This does not work in native (it crashes), + * and cannot be made to work in Wine because the original interface design + * is deficient. The only use left for this function is to remap pointers + * in a contiguous property array that has been copied with memcpy() to + * another memory location. + */ +SCODE WINAPI ScRelocProps(int cValues, LPSPropValue lpProps, LPVOID lpOld, + LPVOID lpNew, ULONG *lpCount) +{ + static const BOOL bBadPtr = TRUE; /* Windows bug - Assumes source is bad */ + LPSPropValue lpDest = (LPSPropValue)lpProps; + ULONG ulCount = cValues * sizeof(SPropValue); + ULONG ulLen, i; + int iter; + + TRACE("(%d,%p,%p,%p,%p)\n", cValues, lpProps, lpOld, lpNew, lpCount); + + if (!lpProps || cValues < 0 || !lpOld || !lpNew) + return MAPI_E_INVALID_PARAMETER; + + /* The reason native doesn't work as MSDN states is that it assumes that + * the lpProps pointer contains valid pointers. This is obviously not + * true if the array is being read back from serialisation (the pointers + * are just offsets). Native can't actually work converting the pointers to + * offsets either, because it converts any array pointers to offsets then + * _dereferences the offset_ in order to convert the array elements! + * + * The code below would handle both cases except that the design of this + * function makes it impossible to know when the pointers in lpProps are + * valid. If both lpOld and lpNew are non-NULL, native reads the pointers + * after converting them, so we must do the same. It seems this + * functionality was never tested by MS. + */ + +#define RELOC_PTR(p) (((char*)(p)) - (char*)lpOld + (char*)lpNew) + + for (iter = 0; iter < cValues; iter++) + { + switch (PROP_TYPE(lpDest->ulPropTag)) + { + case PT_CLSID: + lpDest->Value.lpguid = (LPGUID)RELOC_PTR(lpDest->Value.lpguid); + ulCount += sizeof(GUID); + break; + case PT_STRING8: + ulLen = bBadPtr ? 0 : lstrlenA(lpDest->Value.lpszA) + 1u; + lpDest->Value.lpszA = (LPSTR)RELOC_PTR(lpDest->Value.lpszA); + if (bBadPtr) + ulLen = lstrlenA(lpDest->Value.lpszA) + 1u; + ulCount += ulLen; + break; + case PT_UNICODE: + ulLen = bBadPtr ? 0 : (lstrlenW(lpDest->Value.lpszW) + 1u) * sizeof(WCHAR); + lpDest->Value.lpszW = (LPWSTR)RELOC_PTR(lpDest->Value.lpszW); + if (bBadPtr) + ulLen = (strlenW(lpDest->Value.lpszW) + 1u) * sizeof(WCHAR); + ulCount += ulLen; + break; + case PT_BINARY: + lpDest->Value.bin.lpb = (LPBYTE)RELOC_PTR(lpDest->Value.bin.lpb); + ulCount += lpDest->Value.bin.cb; + break; + default: + if (lpDest->ulPropTag & MV_FLAG) + { + /* Since we have to access the array elements, don't map the + * array unless it is invalid (otherwise, map it at the end) + */ + if (bBadPtr) + lpDest->Value.MVszA.lppszA = (LPSTR*)RELOC_PTR(lpDest->Value.MVszA.lppszA); + + switch (PROP_TYPE(lpProps->ulPropTag)) + { + case PT_MV_STRING8: + { + ulCount += lpDest->Value.MVszA.cValues * sizeof(char *); + + for (i = 0; i < lpDest->Value.MVszA.cValues; i++) + { + ULONG ulStrLen = bBadPtr ? 0 : lstrlenA(lpDest->Value.MVszA.lppszA[i]) + 1u; + + lpDest->Value.MVszA.lppszA[i] = (LPSTR)RELOC_PTR(lpDest->Value.MVszA.lppszA[i]); + if (bBadPtr) + ulStrLen = lstrlenA(lpDest->Value.MVszA.lppszA[i]) + 1u; + ulCount += ulStrLen; + } + break; + } + case PT_MV_UNICODE: + { + ulCount += lpDest->Value.MVszW.cValues * sizeof(WCHAR *); + + for (i = 0; i < lpDest->Value.MVszW.cValues; i++) + { + ULONG ulStrLen = bBadPtr ? 0 : (strlenW(lpDest->Value.MVszW.lppszW[i]) + 1u) * sizeof(WCHAR); + + lpDest->Value.MVszW.lppszW[i] = (LPWSTR)RELOC_PTR(lpDest->Value.MVszW.lppszW[i]); + if (bBadPtr) + ulStrLen = (strlenW(lpDest->Value.MVszW.lppszW[i]) + 1u) * sizeof(WCHAR); + ulCount += ulStrLen; + } + break; + } + case PT_MV_BINARY: + { + ulCount += lpDest->Value.MVszW.cValues * sizeof(SBinary); + + for (i = 0; i < lpDest->Value.MVszW.cValues; i++) + { + lpDest->Value.MVbin.lpbin[i].lpb = (LPBYTE)RELOC_PTR(lpDest->Value.MVbin.lpbin[i].lpb); + ulCount += lpDest->Value.MVbin.lpbin[i].cb; + } + break; + } + default: + ulCount += UlPropSize(lpDest); + break; + } + if (!bBadPtr) + lpDest->Value.MVszA.lppszA = (LPSTR*)RELOC_PTR(lpDest->Value.MVszA.lppszA); + break; + } + } + lpDest++; + } + if (lpCount) + *lpCount = ulCount; + + return S_OK; +} + +/************************************************************************* + * LpValFindProp@12 (MAPI32.173) + * + * Find a property with a given property id in a property array. + * + * PARAMS + * ulPropTag [I] Property tag containing property id to find + * cValues [I] Number of properties in lpProps + * lpProps [I] Property array to search + * + * RETURNS + * A pointer to the matching property, or NULL if none was found. + * + * NOTES + * This function matches only on the property id and does not care if the + * property types differ. + */ +LPSPropValue WINAPI LpValFindProp(ULONG ulPropTag, ULONG cValues, LPSPropValue lpProps) +{ + TRACE("(%ld,%ld,%p)\n", ulPropTag, cValues, lpProps); + + if (lpProps && cValues) + { + ULONG i; + for (i = 0; i < cValues; i++) + { + if (PROP_ID(ulPropTag) == PROP_ID(lpProps[i].ulPropTag)) + return &lpProps[i]; + } + } + return NULL; +} + +/************************************************************************* + * ScDupPropset@16 (MAPI32.174) + * + * Duplicate a property value array into a contiguous block of memory. + * + * PARAMS + * cValues [I] Number of properties in lpProps + * lpProps [I] Property array to duplicate + * lpAlloc [I] Memory allocation function, use MAPIAllocateBuffer() + * lpNewProp [O] Destination for the newly duplicated property value array + * + * RETURNS + * Success: S_OK. *lpNewProp contains the duplicated array. + * Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid, + * MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails. + */ +SCODE WINAPI ScDupPropset(int cValues, LPSPropValue lpProps, + LPALLOCATEBUFFER lpAlloc, LPSPropValue *lpNewProp) +{ + ULONG ulCount; + SCODE sc; + + TRACE("(%d,%p,%p,%p)\n", cValues, lpProps, lpAlloc, lpNewProp); + + sc = ScCountProps(cValues, lpProps, &ulCount); + if (SUCCEEDED(sc)) + { + sc = lpAlloc(ulCount, (LPVOID*)lpNewProp); + if (SUCCEEDED(sc)) + sc = ScCopyProps(cValues, lpProps, *lpNewProp, &ulCount); + } + return sc; +} + +/************************************************************************* + * FBadRglpszA@8 (MAPI32.175) + * + * Determine if an array of strings is invalid + * + * PARAMS + * lppszStrs [I] Array of strings to check + * ulCount [I] Number of strings in lppszStrs + * + * RETURNS + * TRUE, if lppszStrs is invalid, FALSE otherwise. + */ +BOOL WINAPI FBadRglpszA(LPSTR *lppszStrs, ULONG ulCount) +{ + ULONG i; + + TRACE("(%p,%ld)\n", lppszStrs, ulCount); + + if (!ulCount) + return FALSE; + + if (!lppszStrs || IsBadReadPtr(lppszStrs, ulCount * sizeof(LPWSTR))) + return TRUE; + + for (i = 0; i < ulCount; i++) + { + if (!lppszStrs[i] || IsBadStringPtrA(lppszStrs[i], -1)) + return TRUE; + } + return FALSE; +} + +/************************************************************************* + * FBadRglpszW@8 (MAPI32.176) + * + * See FBadRglpszA. + */ +BOOL WINAPI FBadRglpszW(LPWSTR *lppszStrs, ULONG ulCount) +{ + ULONG i; + + TRACE("(%p,%ld)\n", lppszStrs, ulCount); + + if (!ulCount) + return FALSE; + + if (!lppszStrs || IsBadReadPtr(lppszStrs, ulCount * sizeof(LPWSTR))) + return TRUE; + + for (i = 0; i < ulCount; i++) + { + if (!lppszStrs[i] || IsBadStringPtrW(lppszStrs[i], -1)) + return TRUE; + } + return FALSE; +} + +/************************************************************************* + * FBadRowSet@4 (MAPI32.177) + * + * Determine if a row is invalid + * + * PARAMS + * lpRow [I] Row to check + * + * RETURNS + * TRUE, if lpRow is invalid, FALSE otherwise. + */ +BOOL WINAPI FBadRowSet(LPSRowSet lpRowSet) +{ + ULONG i; + TRACE("(%p)\n", lpRowSet); + + if (!lpRowSet || IsBadReadPtr(lpRowSet, CbSRowSet(lpRowSet))) + return TRUE; + + for (i = 0; i < lpRowSet->cRows; i++) + { + if (FBadRow(&lpRowSet->aRow[i])) + return TRUE; + } + return FALSE; +} + +/************************************************************************* + * FBadPropTag@4 (MAPI32.179) + * + * Determine if a property tag is invalid + * + * PARAMS + * ulPropTag [I] Property tag to check + * + * RETURNS + * TRUE, if ulPropTag is invalid, FALSE otherwise. + */ +ULONG WINAPI FBadPropTag(ULONG ulPropTag) +{ + TRACE("(0x%08lx)\n", ulPropTag); + + switch (ulPropTag & (~MV_FLAG & PROP_TYPE_MASK)) + { + case PT_UNSPECIFIED: + case PT_NULL: + case PT_I2: + case PT_LONG: + case PT_R4: + case PT_DOUBLE: + case PT_CURRENCY: + case PT_APPTIME: + case PT_ERROR: + case PT_BOOLEAN: + case PT_OBJECT: + case PT_I8: + case PT_STRING8: + case PT_UNICODE: + case PT_SYSTIME: + case PT_CLSID: + case PT_BINARY: + return FALSE; + } + return TRUE; +} + +/************************************************************************* + * FBadRow@4 (MAPI32.180) + * + * Determine if a row is invalid + * + * PARAMS + * lpRow [I] Row to check + * + * RETURNS + * TRUE, if lpRow is invalid, FALSE otherwise. + */ +ULONG WINAPI FBadRow(LPSRow lpRow) +{ + ULONG i; + TRACE("(%p)\n", lpRow); + + if (!lpRow || IsBadReadPtr(lpRow, sizeof(SRow)) || !lpRow->lpProps || + IsBadReadPtr(lpRow->lpProps, lpRow->cValues * sizeof(SPropValue))) + return TRUE; + + for (i = 0; i < lpRow->cValues; i++) + { + if (FBadProp(&lpRow->lpProps[i])) + return TRUE; + } + return FALSE; +} + +/************************************************************************* + * FBadProp@4 (MAPI32.181) + * + * Determine if a property is invalid + * + * PARAMS + * lpProp [I] Property to check + * + * RETURNS + * TRUE, if lpProp is invalid, FALSE otherwise. + */ +ULONG WINAPI FBadProp(LPSPropValue lpProp) +{ + if (!lpProp || IsBadReadPtr(lpProp, sizeof(SPropValue)) || + FBadPropTag(lpProp->ulPropTag)) + return TRUE; + + switch (PROP_TYPE(lpProp->ulPropTag)) + { + /* Single value properties containing pointers */ + case PT_STRING8: + if (!lpProp->Value.lpszA || IsBadStringPtrA(lpProp->Value.lpszA, -1)) + return TRUE; + break; + case PT_UNICODE: + if (!lpProp->Value.lpszW || IsBadStringPtrW(lpProp->Value.lpszW, -1)) + return TRUE; + break; + case PT_BINARY: + if (IsBadReadPtr(lpProp->Value.bin.lpb, lpProp->Value.bin.cb)) + return TRUE; + break; + case PT_CLSID: + if (IsBadReadPtr(lpProp->Value.lpguid, sizeof(GUID))) + return TRUE; + break; + + /* Multiple value properties (arrays) containing no pointers */ + case PT_MV_I2: + return PROP_BadArray(lpProp, sizeof(SHORT)); + case PT_MV_LONG: + return PROP_BadArray(lpProp, sizeof(LONG)); + case PT_MV_LONGLONG: + return PROP_BadArray(lpProp, sizeof(LONG64)); + case PT_MV_FLOAT: + return PROP_BadArray(lpProp, sizeof(float)); + case PT_MV_SYSTIME: + return PROP_BadArray(lpProp, sizeof(FILETIME)); + case PT_MV_APPTIME: + case PT_MV_DOUBLE: + return PROP_BadArray(lpProp, sizeof(double)); + case PT_MV_CURRENCY: + return PROP_BadArray(lpProp, sizeof(CY)); + case PT_MV_CLSID: + return PROP_BadArray(lpProp, sizeof(GUID)); + + /* Multiple value properties containing pointers */ + case PT_MV_STRING8: + return FBadRglpszA(lpProp->Value.MVszA.lppszA, + lpProp->Value.MVszA.cValues); + case PT_MV_UNICODE: + return FBadRglpszW(lpProp->Value.MVszW.lppszW, + lpProp->Value.MVszW.cValues); + case PT_MV_BINARY: + return FBadEntryList((LPENTRYLIST)&lpProp->Value.MVbin); + } + return FALSE; +} + +/************************************************************************* + * FBadColumnSet@4 (MAPI32.182) + * + * Determine if an array of property tags is invalid + * + * PARAMS + * lpCols [I] Property tag array to check + * + * RETURNS + * TRUE, if lpCols is invalid, FALSE otherwise. + */ +ULONG WINAPI FBadColumnSet(LPSPropTagArray lpCols) +{ + ULONG ulRet = FALSE, i; + + TRACE("(%p)\n", lpCols); + + if (!lpCols || IsBadReadPtr(lpCols, CbSPropTagArray(lpCols))) + ulRet = TRUE; + else + { + for (i = 0; i < lpCols->cValues; i++) + { + if ((lpCols->aulPropTag[i] & PROP_TYPE_MASK) == PT_ERROR || + FBadPropTag(lpCols->aulPropTag[i])) + { + ulRet = TRUE; + break; + } + } + } + TRACE("Returning %s\n", ulRet ? "TRUE" : "FALSE"); + return ulRet; +} + + +/************************************************************************** + * IMAPIProp {MAPI32} + * + * The default Mapi interface for manipulating object properties. + * + * DESCRIPTION + * This object provides an interface to an objects properties. It is exposed + * by several types of Mapi objects in order to simplify the querying and + * modification of properties. + * + * METHODS + */ + +/* A single property in a property data collection */ +typedef struct +{ + struct list entry; + ULONG ulAccess; /* The property value access level */ + LPSPropValue value; /* The property value */ +} IPropDataItem, *LPIPropDataItem; + + /* The main property data collection structure */ +typedef struct +{ + const IPropDataVtbl *lpVtbl; + LONG lRef; /* Reference count */ + ALLOCATEBUFFER *lpAlloc; /* Memory allocation routine */ + ALLOCATEMORE *lpMore; /* Linked memory allocation routine */ + FREEBUFFER *lpFree; /* Memory free routine */ + ULONG ulObjAccess; /* Object access level */ + ULONG ulNumValues; /* Number of items in values list */ + struct list values; /* List of property values */ + RTL_CRITICAL_SECTION cs; /* Lock for thread safety */ +} IPropDataImpl; + +/* Internal - Get a property value, assumes lock is held */ +static IPropDataItem *IMAPIPROP_GetValue(IPropDataImpl *This, ULONG ulPropTag) +{ + struct list *cursor; + + LIST_FOR_EACH(cursor, &This->values) + { + LPIPropDataItem current = LIST_ENTRY(cursor, IPropDataItem, entry); + /* Note that propery types don't have to match, just Id's */ + if (PROP_ID(current->value->ulPropTag) == PROP_ID(ulPropTag)) + return current; + } + return NULL; +} + +/* Internal - Add a new property value, assumes lock is held */ +static IPropDataItem *IMAPIPROP_AddValue(IPropDataImpl *This, + LPSPropValue lpProp) +{ + LPVOID lpMem; + LPIPropDataItem lpNew; + HRESULT hRet; + + hRet = This->lpAlloc(sizeof(IPropDataItem), &lpMem); + + if (SUCCEEDED(hRet)) + { + lpNew = lpMem; + lpNew->ulAccess = IPROP_READWRITE; + + /* Allocate the value seperately so we can update it easily */ + lpMem = NULL; + hRet = This->lpAlloc(sizeof(SPropValue), &lpMem); + if (SUCCEEDED(hRet)) + { + lpNew->value = lpMem; + + hRet = PropCopyMore(lpNew->value, lpProp, This->lpMore, lpMem); + if (SUCCEEDED(hRet)) + { + list_add_tail(&This->values, &lpNew->entry); + This->ulNumValues++; + return lpNew; + } + This->lpFree(lpNew->value); + } + This->lpFree(lpNew); + } + return NULL; +} + +/* Internal - Lock an IPropData object */ +static inline void IMAPIPROP_Lock(IPropDataImpl *This) +{ + RtlEnterCriticalSection(&This->cs); +} + +/* Internal - Unlock an IPropData object */ +static inline void IMAPIPROP_Unlock(IPropDataImpl *This) +{ + RtlLeaveCriticalSection(&This->cs); +} + +/* This one seems to be missing from mapidefs.h */ +#define CbNewSPropProblemArray(c) \ + (offsetof(SPropProblemArray,aProblem)+(c)*sizeof(SPropProblem)) + +/************************************************************************** + * IMAPIProp_QueryInterface {MAPI32} + * + * Inherited method from the IUnknown Interface. + * See IUnknown_QueryInterface. + * + * NOTES + * This object exposes the following interfaces: + * - IUnknown() : The default interface for all COM-Objects. + * - IMAPIProp() : The default Mapi interface for manipulating object properties. + */ +static inline HRESULT WINAPI +IMAPIProp_fnQueryInterface(LPMAPIPROP iface, REFIID riid, LPVOID *ppvObj) +{ + IPropDataImpl *This = (IPropDataImpl*)iface; + + TRACE("(%p,%s,%p)\n", This, debugstr_guid(riid), ppvObj); + + if (!ppvObj || !riid) + return MAPI_E_INVALID_PARAMETER; + + *ppvObj = NULL; + + if(IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_IMAPIProp) || + IsEqualIID(riid, &IID_IMAPIPropData)) + { + *ppvObj = This; + IPropData_AddRef(iface); + TRACE("returning %p\n", *ppvObj); + return S_OK; + } + + TRACE("returning E_NOINTERFACE\n"); + return MAPI_E_INTERFACE_NOT_SUPPORTED; +} + +/************************************************************************** + * IMAPIProp_AddRef {MAPI32} + * + * Inherited method from the IUnknown Interface. + * See IUnknown_AddRef. + */ +static inline ULONG WINAPI IMAPIProp_fnAddRef(LPMAPIPROP iface) +{ + IPropDataImpl *This = (IPropDataImpl*)iface; + + TRACE("(%p)->(count before=%lu)\n", This, This->lRef); + + return InterlockedIncrement(&This->lRef); +} + +/************************************************************************** + * IMAPIProp_Release {MAPI32} + * + * Inherited method from the IUnknown Interface. + * See IUnknown_Release. + */ +static inline ULONG WINAPI IMAPIProp_fnRelease(LPMAPIPROP iface) +{ + IPropDataImpl *This = (IPropDataImpl*)iface; + LONG lRef; + + TRACE("(%p)->(count before=%lu)\n", This, This->lRef); + + lRef = InterlockedDecrement(&This->lRef); + if (!lRef) + { + TRACE("Destroying IPropData (%p)\n",This); + + /* Note: No need to lock, since no other thread is referencing iface */ + while (!list_empty(&This->values)) + { + struct list *head = list_head(&This->values); + LPIPropDataItem current = LIST_ENTRY(head, IPropDataItem, entry); + list_remove(head); + This->lpFree(current->value); + This->lpFree(current); + } + RtlDeleteCriticalSection(&This->cs); + This->lpFree(This); + } + return (ULONG)lRef; +} + +/************************************************************************** + * IMAPIProp_GetLastError {MAPI32} + * + * Get information about the last error that ocurred in an IMAPIProp object. + * + * PARAMS + * iface [I] IMAPIProp object that experienced the error + * hRes [I] Result of the call that returned an error + * ulFlags [I] 0=return Ascii strings, MAPI_UNICODE=return Unicode strings + * lppError [O] Destination for detailed error information + * + * RETURNS + * Success: S_OK. *lppError contains details about the last error. + * Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid, + * MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails. + * + * NOTES + * - If this function succeeds, the returned information in *lppError must be + * freed using MAPIFreeBuffer() once the caller is finished with it. + * - It is possible for this function to suceed and set *lppError to NULL, + * if there is no further information to report about hRes. + */ +static inline HRESULT WINAPI +IMAPIProp_fnGetLastError(LPMAPIPROP iface, HRESULT hRes, + ULONG ulFlags, LPMAPIERROR *lppError) +{ + TRACE("(%p,0x%08lX,0x%08lX,%p)\n", iface, hRes, ulFlags, lppError); + + if (!lppError || SUCCEEDED(hRes) || (ulFlags & ~MAPI_UNICODE)) + return MAPI_E_INVALID_PARAMETER; + + *lppError = NULL; + return S_OK; +} + +/************************************************************************** + * IMAPIProp_SaveChanges {MAPI32} + * + * Update any changes made to a tansactional IMAPIProp object. + * + * PARAMS + * iface [I] IMAPIProp object to update + * ulFlags [I] Flags controlling the update. + * + * RETURNS + * Success: S_OK. Any outstanding changes are committed to the object. + * Failure: An HRESULT error code describing the error. + */ +static inline HRESULT WINAPI +IMAPIProp_fnSaveChanges(LPMAPIPROP iface, ULONG ulFlags) +{ + TRACE("(%p,0x%08lX)\n", iface, ulFlags); + + /* Since this object is not transacted we do not need to implement this */ + /* FIXME: Should we set the access levels to clean? */ + return S_OK; +} + +/************************************************************************** + * IMAPIProp_GetProps {MAPI32} + * + * Get property values from an IMAPIProp object. + * + * PARAMS + * iface [I] IMAPIProp object to get the property values from + * lpTags [I] Property tage of property values to be retrieved + * ulFlags [I] Return 0=Ascii MAPI_UNICODE=Unicode strings for + * unspecified types + * lpCount [O] Destination for number of properties returned + * lppProps [O] Destination for returned property values + * + * RETURNS + * Success: S_OK. *lppProps and *lpCount are updated. + * Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid. + * MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails, or + * MAPI_W_ERRORS_RETURNED if not all properties were retrieved + * successfully. + * NOTES + * - If MAPI_W_ERRORS_RETURNED is returned, any properties that could not be + * retrieved from iface are present in lppProps with their type + * changed to PT_ERROR and Id unchanged. + */ +static inline HRESULT WINAPI +IMAPIProp_fnGetProps(LPMAPIPROP iface, LPSPropTagArray lpTags, + ULONG ulFlags, ULONG *lpCount, LPSPropValue *lppProps) +{ + ULONG i; + HRESULT hRet = S_OK; + IPropDataImpl *This = (IPropDataImpl*)iface; + + TRACE("(%p,%p,0x%08lx,%p,%p) stub\n", iface, lpTags, ulFlags, + lpCount, lppProps); + + if (!iface || ulFlags & ~MAPI_UNICODE || !lpTags || *lpCount || !lppProps) + return MAPI_E_INVALID_PARAMETER; + + FIXME("semi-stub, flags not supported\n"); + + *lpCount = lpTags->cValues; + *lppProps = NULL; + + if (*lpCount) + { + hRet = MAPIAllocateBuffer(*lpCount * sizeof(SPropValue), (LPVOID*)lppProps); + if (FAILED(hRet)) + return hRet; + + IMAPIPROP_Lock(This); + + for (i = 0; i < lpTags->cValues; i++) + { + HRESULT hRetTmp = E_INVALIDARG; + LPIPropDataItem item; + + item = IMAPIPROP_GetValue(This, lpTags->aulPropTag[i]); + + if (item) + hRetTmp = PropCopyMore(&(*lppProps)[i], item->value, + This->lpMore, *lppProps); + if (FAILED(hRetTmp)) + { + hRet = MAPI_W_ERRORS_RETURNED; + (*lppProps)[i].ulPropTag = + CHANGE_PROP_TYPE(lpTags->aulPropTag[i], PT_ERROR); + } + } + + IMAPIPROP_Unlock(This); + } + return hRet; +} + +/************************************************************************** + * MAPIProp_GetPropList {MAPI32} + * + * Get the list of property tags for all values in an IMAPIProp object. + * + * PARAMS + * iface [I] IMAPIProp object to get the property tag list from + * ulFlags [I] Return 0=Ascii MAPI_UNICODE=Unicode strings for + * unspecified types + * lppTags [O] Destination for the retrieved peoperty tag list + * + * RETURNS + * Success: S_OK. *lppTags contains the tags for all available properties. + * Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid. + * MAPI_E_BAD_CHARWIDTH, if Ascii or Unicode strings are requested + * and that type of string is not supported. + */ +static inline HRESULT WINAPI +IMAPIProp_fnGetPropList(LPMAPIPROP iface, ULONG ulFlags, + LPSPropTagArray *lppTags) +{ + IPropDataImpl *This = (IPropDataImpl*)iface; + ULONG i; + HRESULT hRet; + + TRACE("(%p,0x%08lx,%p) stub\n", iface, ulFlags, lppTags); + + if (!iface || ulFlags & ~MAPI_UNICODE || !lppTags) + return MAPI_E_INVALID_PARAMETER; + + FIXME("semi-stub, flags not supported\n"); + + *lppTags = NULL; + + IMAPIPROP_Lock(This); + + hRet = MAPIAllocateBuffer(CbNewSPropTagArray(This->ulNumValues), + (LPVOID*)lppTags); + if (SUCCEEDED(hRet)) + { + struct list *cursor; + + i = 0; + LIST_FOR_EACH(cursor, &This->values) + { + LPIPropDataItem current = LIST_ENTRY(cursor, IPropDataItem, entry); + (*lppTags)->aulPropTag[i] = current->value->ulPropTag; + i++; + } + (*lppTags)->cValues = This->ulNumValues; + } + + IMAPIPROP_Unlock(This); + return hRet; +} + +/************************************************************************** + * IMAPIProp_OpenProperty {MAPI32} + * + * Not documented at this time. + * + * RETURNS + * An HRESULT success/failure code. + */ +static inline HRESULT WINAPI +IMAPIProp_fnOpenProperty(LPMAPIPROP iface, ULONG ulPropTag, LPCIID iid, + ULONG ulOpts, ULONG ulFlags, LPUNKNOWN *lpUnk) +{ + FIXME("(%p,%lu,%s,%lu,0x%08lx,%p) stub\n", iface, ulPropTag, + debugstr_guid(iid), ulOpts, ulFlags, lpUnk); + return MAPI_E_NO_SUPPORT; +} + + +/************************************************************************** + * IMAPIProp_SetProps {MAPI32} + * + * Add or edit the property values in an IMAPIProp object. + * + * PARAMS + * iface [I] IMAPIProp object to get the property tag list from + * ulValues [I] Number of properties in lpProps + * lpProps [I] Property values to set + * lppProbs [O] Optional destination for any problems that occurred + * + * RETURNS + * Success: S_OK. The properties in lpProps are added to iface if they don't + * exist, or changed to the values in lpProps if they do + * Failure: An HRESULT error code describing the error + */ +static inline HRESULT WINAPI +IMAPIProp_fnSetProps(LPMAPIPROP iface, ULONG ulValues, + LPSPropValue lpProps, LPSPropProblemArray *lppProbs) +{ + IPropDataImpl *This = (IPropDataImpl*)iface; + HRESULT hRet = S_OK; + ULONG i; + + TRACE("(%p,%lu,%p,%p)\n", iface, ulValues, lpProps, lppProbs); + + if (!iface || !lpProps) + return MAPI_E_INVALID_PARAMETER; + + for (i = 0; i < ulValues; i++) + { + if (FBadProp(&lpProps[i]) || + PROP_TYPE(lpProps[i].ulPropTag) == PT_OBJECT || + PROP_TYPE(lpProps[i].ulPropTag) == PT_NULL) + return MAPI_E_INVALID_PARAMETER; + } + + IMAPIPROP_Lock(This); + + /* FIXME: Under what circumstances is lpProbs created? */ + for (i = 0; i < ulValues; i++) + { + LPIPropDataItem item = IMAPIPROP_GetValue(This, lpProps[i].ulPropTag); + + if (item) + { + HRESULT hRetTmp; + LPVOID lpMem = NULL; + + /* Found, so update the existing value */ + if (item->value->ulPropTag != lpProps[i].ulPropTag) + FIXME("semi-stub, overwriting type (not coercing)\n"); + + hRetTmp = This->lpAlloc(sizeof(SPropValue), &lpMem); + if (SUCCEEDED(hRetTmp)) + { + hRetTmp = PropCopyMore(lpMem, &lpProps[i], This->lpMore, lpMem); + if (SUCCEEDED(hRetTmp)) + { + This->lpFree(item->value); + item->value = lpMem; + continue; + } + This->lpFree(lpMem); + } + hRet = hRetTmp; + } + else + { + /* Add new value */ + if (!(item = IMAPIPROP_AddValue(This, &lpProps[i]))) + hRet = MAPI_E_NOT_ENOUGH_MEMORY; + } + } + + IMAPIPROP_Unlock(This); + return hRet; +} + +/************************************************************************** + * IMAPIProp_DeleteProps {MAPI32} + * + * Delete one or more property values from an IMAPIProp object. + * + * PARAMS + * iface [I] IMAPIProp object to remove property values from. + * lpTags [I] Collection of property Id's to remove from iface. + * lppProbs [O] Destination for problems encountered, if any. + * + * RETURNS + * Success: S_OK. Any properties in iface matching property Id's in lpTags have + * been deleted. If lppProbs is non-NULL it contains details of any + * errors that occurred. + * Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid. + * E_ACCESSDENIED, if this object was created using CreateIProp() and + * a subsequent call to IPropData_SetObjAcess() was made specifying + * IPROP_READONLY as the access type. + * + * NOTES + * - lppProbs will not be populated for cases where a property Id is present + * in lpTags but not in iface. + * - lppProbs should be deleted with MAPIFreeBuffer() if returned. + */ +static inline HRESULT WINAPI +IMAPIProp_fnDeleteProps(LPMAPIPROP iface, LPSPropTagArray lpTags, + LPSPropProblemArray *lppProbs) +{ + IPropDataImpl *This = (IPropDataImpl*)iface; + ULONG i, numProbs = 0; + HRESULT hRet = S_OK; + + TRACE("(%p,%p,%p)\n", iface, lpTags, lppProbs); + + if (!iface || !lpTags) + return MAPI_E_INVALID_PARAMETER; + + if (lppProbs) + *lppProbs = NULL; + + for (i = 0; i < lpTags->cValues; i++) + { + if (FBadPropTag(lpTags->aulPropTag[i]) || + PROP_TYPE(lpTags->aulPropTag[i]) == PT_OBJECT || + PROP_TYPE(lpTags->aulPropTag[i]) == PT_NULL) + return MAPI_E_INVALID_PARAMETER; + } + + IMAPIPROP_Lock(This); + + if (This->ulObjAccess != IPROP_READWRITE) + { + IMAPIPROP_Unlock(This); + return E_ACCESSDENIED; + } + + for (i = 0; i < lpTags->cValues; i++) + { + LPIPropDataItem item = IMAPIPROP_GetValue(This, lpTags->aulPropTag[i]); + + if (item) + { + if (item->ulAccess & IPROP_READWRITE) + { + /* Everything hunky-dory, remove the item */ + list_remove(&item->entry); + This->lpFree(item->value); /* Also frees value pointers */ + This->lpFree(item); + This->ulNumValues--; + } + else if (lppProbs) + { + /* Can't write the value. Create/populate problems array */ + if (!*lppProbs) + { + /* Create problems array */ + ULONG ulSize = CbNewSPropProblemArray(lpTags->cValues - i); + HRESULT hRetTmp = MAPIAllocateBuffer(ulSize, (LPVOID*)lppProbs); + if (FAILED(hRetTmp)) + hRet = hRetTmp; + } + if (*lppProbs) + { + LPSPropProblem lpProb = &(*lppProbs)->aProblem[numProbs]; + lpProb->ulIndex = i; + lpProb->ulPropTag = lpTags->aulPropTag[i]; + lpProb->scode = E_ACCESSDENIED; + numProbs++; + } + } + } + } + if (lppProbs && *lppProbs) + (*lppProbs)->cProblem = numProbs; + + IMAPIPROP_Unlock(This); + return hRet; +} + + +/************************************************************************** + * IMAPIProp_CopyTo {MAPI32} + * + * Not documented at this time. + * + * RETURNS + * An HRESULT success/failure code. + */ +static inline HRESULT WINAPI +IMAPIProp_fnCopyTo(LPMAPIPROP iface, ULONG niids, LPCIID lpiidExcl, + LPSPropTagArray lpPropsExcl, ULONG ulParam, + LPMAPIPROGRESS lpIProgress, LPCIID lpIfaceIid, LPVOID lpDstObj, + ULONG ulFlags, LPSPropProblemArray *lppProbs) +{ + FIXME("(%p,%lu,%p,%p,%lx,%p,%s,%p,0x%08lX,%p) stub\n", iface, niids, + lpiidExcl, lpPropsExcl, ulParam, lpIProgress, + debugstr_guid(lpIfaceIid), lpDstObj, ulFlags, lppProbs); + return MAPI_E_NO_SUPPORT; +} + +/************************************************************************** + * IMAPIProp_CopyProps {MAPI32} + * + * Not documented at this time. + * + * RETURNS + * An HRESULT success/failure code. + */ +static inline HRESULT WINAPI +IMAPIProp_fnCopyProps(LPMAPIPROP iface, LPSPropTagArray lpInclProps, + ULONG ulParam, LPMAPIPROGRESS lpIProgress, LPCIID lpIface, + LPVOID lpDstObj, ULONG ulFlags, + LPSPropProblemArray *lppProbs) +{ + FIXME("(%p,%p,%lx,%p,%s,%p,0x%08lX,%p) stub\n", iface, lpInclProps, + ulParam, lpIProgress, debugstr_guid(lpIface), lpDstObj, ulFlags, + lppProbs); + return MAPI_E_NO_SUPPORT; +} + +/************************************************************************** + * IMAPIProp_GetNamesFromIDs {MAPI32} + * + * Get the names of properties from their identifiers. + * + * PARAMS + * iface [I] IMAPIProp object to operate on + * lppPropTags [I/O] Property identifiers to get the names for, or NULL to + * get all names + * iid [I] Property set identifier, or NULL + * ulFlags [I] MAPI_NO_IDS=Don't return numeric named properties, + * or MAPI_NO_STRINGS=Don't return strings + * lpCount [O] Destination for number of properties returned + * lpppNames [O] Destination for returned names + * + * RETURNS + * Success: S_OK. *lppPropTags and lpppNames contain the returned + * name/identifiers. + * Failure: MAPI_E_NO_SUPPORT, if the object does not support named properties, + * MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails, or + * MAPI_W_ERRORS_RETURNED if not all properties were retrieved + * successfully. + */ +static inline HRESULT WINAPI +IMAPIProp_fnGetNamesFromIDs(LPMAPIPROP iface, LPSPropTagArray *lppPropTags, + LPGUID iid, ULONG ulFlags, ULONG *lpCount, + LPMAPINAMEID **lpppNames) +{ + FIXME("(%p,%p,%s,0x%08lX,%p,%p) stub\n", iface, lppPropTags, + debugstr_guid(iid), ulFlags, lpCount, lpppNames); + return MAPI_E_NO_SUPPORT; +} + +/************************************************************************** + * IMAPIProp_GetIDsFromNames {MAPI32} + * + * Get property identifiers associated with one or more named properties. + * + * PARAMS + * iface [I] IMAPIProp object to operate on + * ulNames [I] Number of names in lppNames + * lppNames [I] Names to query or create, or NULL to query all names + * ulFlags [I] Pass MAPI_CREATE to create new named properties + * lppPropTags [O] Destination for queried or created property identifiers + * + * RETURNS + * Success: S_OK. *lppPropTags contains the property tags created or requested. + * Failure: MAPI_E_NO_SUPPORT, if the object does not support named properties, + * MAPI_E_TOO_BIG, if the object cannot process the number of + * properties involved. + * MAPI_E_NOT_ENOUGH_MEMORY, if memory allocation fails, or + * MAPI_W_ERRORS_RETURNED if not all properties were retrieved + * successfully. + */ +static inline HRESULT WINAPI +IMAPIProp_fnGetIDsFromNames(LPMAPIPROP iface, ULONG ulNames, + LPMAPINAMEID *lppNames, ULONG ulFlags, + LPSPropTagArray *lppPropTags) +{ + FIXME("(%p,%ld,%p,0x%08lX,%p) stub\n", + iface, ulNames, lppNames, ulFlags, lppPropTags); + return MAPI_E_NO_SUPPORT; +} + +/************************************************************************** + * IPropData {MAPI32} + * + * A default Mapi interface to provide manipulation of object properties. + * + * DESCRIPTION + * This object provides a default interface suitable in some cases as an + * implementation of the IMAPIProp interface (which has no default + * implementation). In addition to the IMAPIProp() methods inherited, this + * interface allows read/write control over access to the object and its + * individual properties. + * + * To obtain the default implementation of this interface from Mapi, call + * CreateIProp(). + * + * METHODS + */ + +/************************************************************************** + * IPropData_QueryInterface {MAPI32} + * + * Inherited method from the IMAPIProp Interface. + * See IMAPIProp_QueryInterface. + */ +static HRESULT WINAPI +IPropData_fnQueryInterface(LPPROPDATA iface, REFIID riid, LPVOID *ppvObj) +{ + return IMAPIProp_fnQueryInterface((LPMAPIPROP)iface, riid, ppvObj); +} + +/************************************************************************** + * IPropData_AddRef {MAPI32} + * + * Inherited method from the IMAPIProp Interface. + * See IMAPIProp_AddRef. + */ +static ULONG WINAPI +IPropData_fnAddRef(LPPROPDATA iface) +{ + return IMAPIProp_fnAddRef((LPMAPIPROP)iface); +} + +/************************************************************************** + * IPropData_Release {MAPI32} + * + * Inherited method from the IMAPIProp Interface. + * See IMAPIProp_Release. + */ +static ULONG WINAPI +IPropData_fnRelease(LPPROPDATA iface) +{ + return IMAPIProp_fnRelease((LPMAPIPROP)iface); +} + +/************************************************************************** + * IPropData_GetLastError {MAPI32} + * + * Inherited method from the IMAPIProp Interface. + * See IMAPIProp_GetLastError. + */ +static HRESULT WINAPI +IPropData_fnGetLastError(LPPROPDATA iface, HRESULT hRes, ULONG ulFlags, + LPMAPIERROR *lppError) +{ + return IMAPIProp_fnGetLastError((LPMAPIPROP)iface, hRes, ulFlags, lppError); +} + +/************************************************************************** + * IPropData_SaveChanges {MAPI32} + * + * Inherited method from the IMAPIProp Interface. + * See IMAPIProp_SaveChanges. + */ +static HRESULT WINAPI +IPropData_fnSaveChanges(LPPROPDATA iface, ULONG ulFlags) +{ + return IMAPIProp_fnSaveChanges((LPMAPIPROP)iface, ulFlags); +} + +/************************************************************************** + * IPropData_GetProps {MAPI32} + * + * Inherited method from the IMAPIProp Interface. + * See IMAPIProp_GetProps. + */ +static HRESULT WINAPI +IPropData_fnGetProps(LPPROPDATA iface, LPSPropTagArray lpPropTags, + ULONG ulFlags, ULONG *lpCount, LPSPropValue *lppProps) +{ + return IMAPIProp_fnGetProps((LPMAPIPROP)iface, lpPropTags, ulFlags, + lpCount, lppProps); +} + +/************************************************************************** + * IPropData_GetPropList {MAPI32} + * + * Inherited method from the IMAPIProp Interface. + * See IMAPIProp_GetPropList. + */ +static HRESULT WINAPI +IPropData_fnGetPropList(LPPROPDATA iface, ULONG ulFlags, + LPSPropTagArray *lppPropTags) +{ + return IMAPIProp_fnGetPropList((LPMAPIPROP)iface, ulFlags, lppPropTags); +} + +/************************************************************************** + * IPropData_OpenProperty {MAPI32} + * + * Inherited method from the IMAPIProp Interface. + * See IMAPIProp_OpenProperty. + */ +static HRESULT WINAPI +IPropData_fnOpenProperty(LPPROPDATA iface, ULONG ulPropTag, LPCIID iid, + ULONG ulOpts, ULONG ulFlags, LPUNKNOWN *lpUnk) +{ + return IMAPIProp_fnOpenProperty((LPMAPIPROP)iface, ulPropTag, iid, + ulOpts, ulFlags, lpUnk); +} + +/************************************************************************** + * IPropData_SetProps {MAPI32} + * + * Inherited method from the IMAPIProp Interface. + * See IMAPIProp_SetProps. + */ +static HRESULT WINAPI +IPropData_fnSetProps(LPPROPDATA iface, ULONG cValues, LPSPropValue lpProps, + LPSPropProblemArray *lppProbs) +{ + return IMAPIProp_fnSetProps((LPMAPIPROP)iface, cValues, lpProps, lppProbs); +} + +/************************************************************************** + * IPropData_DeleteProps {MAPI32} + * + * Inherited method from the IMAPIProp Interface. + * See IMAPIProp_DeleteProps. + */ +static HRESULT WINAPI +IPropData_fnDeleteProps(LPPROPDATA iface, LPSPropTagArray lpPropTags, + LPSPropProblemArray *lppProbs) +{ + return IMAPIProp_fnDeleteProps((LPMAPIPROP)iface, lpPropTags, lppProbs); +} + +/************************************************************************** + * IPropData_CopyTo {MAPI32} + * + * Inherited method from the IMAPIProp Interface. + * See IMAPIProp_CopyTo. + */ +static HRESULT WINAPI +IPropData_fnCopyTo(LPPROPDATA iface, ULONG ciidExclude, LPCIID lpIid, + LPSPropTagArray lpProps, ULONG ulParam, + LPMAPIPROGRESS lpProgress, LPCIID lpIface, LPVOID lpDst, + ULONG ulFlags, LPSPropProblemArray *lppProbs) +{ + return IMAPIProp_fnCopyTo((LPMAPIPROP)iface, ciidExclude, lpIid, lpProps, + ulParam, lpProgress, lpIface, lpDst, + ulFlags, lppProbs); +} + +/************************************************************************** + * IPropData_CopyProps {MAPI32} + * + * Inherited method from the IMAPIProp Interface. + * See IMAPIProp_CopyProps. + */ +static HRESULT WINAPI +IPropData_fnCopyProps(LPPROPDATA iface, LPSPropTagArray lpProps, + ULONG ulParam, LPMAPIPROGRESS lpProgress, LPCIID lpIface, + LPVOID lpDst, ULONG ulFlags, LPSPropProblemArray *lppProbs) +{ + return IMAPIProp_fnCopyProps((LPMAPIPROP)iface, lpProps, ulParam, + lpProgress, lpIface, lpDst, ulFlags, lppProbs); +} + +/************************************************************************** + * IPropData_GetNamesFromIDs {MAPI32} + * + * Inherited method from the IMAPIProp Interface. + * See IMAPIProp_GetNamesFromIDs. + */ +static HRESULT WINAPI +IPropData_fnGetNamesFromIDs(LPPROPDATA iface, LPSPropTagArray *lppPropTags, + LPGUID iid, ULONG ulFlags, ULONG *lpCount, + LPMAPINAMEID **lpppNames) +{ + return IMAPIProp_fnGetNamesFromIDs((LPMAPIPROP)iface, lppPropTags, iid, + ulFlags, lpCount, lpppNames); +} + +/************************************************************************** + * IPropData_GetIDsFromNames {MAPI32} + * + * Inherited method from the IMAPIProp Interface. + * See IMAPIProp_GetIDsFromNames. + */ +static HRESULT WINAPI +IPropData_fnGetIDsFromNames(LPPROPDATA iface, ULONG ulNames, + LPMAPINAMEID *lppNames, ULONG ulFlags, + LPSPropTagArray *lppPropTags) +{ + return IMAPIProp_fnGetIDsFromNames((LPMAPIPROP)iface, ulNames, lppNames, + ulFlags, lppPropTags); +} + +/************************************************************************** + * IPropData_HrSetObjAccess {MAPI32} + * + * Set the access level of an IPropData object. + * + * PARAMS + * iface [I] IPropData object to set the access on + * ulAccess [I] Either IPROP_READONLY or IPROP_READWRITE for read or + * read/write access respectively. + * + * RETURNS + * Success: S_OK. The objects access level is changed. + * Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid. + */ +static HRESULT WINAPI +IPropData_fnHrSetObjAccess(LPPROPDATA iface, ULONG ulAccess) +{ + IPropDataImpl *This = (IPropDataImpl*)iface; + + TRACE("(%p,%lx)\n", iface, ulAccess); + + if (!iface || ulAccess < IPROP_READONLY || ulAccess > IPROP_READWRITE) + return MAPI_E_INVALID_PARAMETER; + + IMAPIPROP_Lock(This); + + This->ulObjAccess = ulAccess; + + IMAPIPROP_Unlock(This); + return S_OK; +} + +/* Internal - determine if an access value is bad */ +static inline BOOL PROP_IsBadAccess(ULONG ulAccess) +{ + switch (ulAccess) + { + case IPROP_READONLY|IPROP_CLEAN: + case IPROP_READONLY|IPROP_DIRTY: + case IPROP_READWRITE|IPROP_CLEAN: + case IPROP_READWRITE|IPROP_DIRTY: + return FALSE; + } + return TRUE; +} + +/************************************************************************** + * IPropData_HrSetPropAccess {MAPI32} + * + * Set the access levels for a group of property values in an IPropData object. + * + * PARAMS + * iface [I] IPropData object to set access levels in. + * lpTags [I] List of property Id's to set access for. + * lpAccess [O] Access level for each property in lpTags. + * + * RETURNS + * Success: S_OK. The access level of each property value in lpTags that is + * present in iface is changed. + * Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid. + * + * NOTES + * - Each access level in lpAccess must contain at least one of IPROP_READONLY + * or IPROP_READWRITE, but not both, and also IPROP_CLEAN or IPROP_DIRTY, + * but not both. No other bits should be set. + * - If a property Id in lpTags is not present in iface, it is ignored. + */ +static HRESULT WINAPI +IPropData_fnHrSetPropAccess(LPPROPDATA iface, LPSPropTagArray lpTags, + ULONG *lpAccess) +{ + IPropDataImpl *This = (IPropDataImpl*)iface; + + ULONG i; + + TRACE("(%p,%p,%p)\n", iface, lpTags, lpAccess); + + if (!iface || !lpTags || !lpAccess) + return MAPI_E_INVALID_PARAMETER; + + for (i = 0; i < lpTags->cValues; i++) + { + if (FBadPropTag(lpTags->aulPropTag[i]) || PROP_IsBadAccess(lpAccess[i])) + return MAPI_E_INVALID_PARAMETER; + } + + IMAPIPROP_Lock(This); + + for (i = 0; i < lpTags->cValues; i++) + { + LPIPropDataItem item = IMAPIPROP_GetValue(This, lpTags->aulPropTag[i]); + + if (item) + item->ulAccess = lpAccess[i]; + } + + IMAPIPROP_Unlock(This); + return S_OK; +} + +/************************************************************************** + * IPropData_HrGetPropAccess {MAPI32} + * + * Get the access levels for a group of property values in an IPropData object. + * + * PARAMS + * iface [I] IPropData object to get access levels from. + * lppTags [O] Destination for the list of property Id's in iface. + * lppAccess [O] Destination for access level for each property in lppTags. + * + * RETURNS + * Success: S_OK. lppTags and lppAccess contain the property Id's and the + * Access level of each property value in iface. + * Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid, or + * MAPI_E_NOT_ENOUGH_MEMORY if memory allocation fails. + * + * NOTES + * - *lppTags and *lppAccess should be freed with MAPIFreeBuffer() by the caller. + */ +static HRESULT WINAPI +IPropData_fnHrGetPropAccess(LPPROPDATA iface, LPSPropTagArray *lppTags, + ULONG **lppAccess) +{ + IPropDataImpl *This = (IPropDataImpl*)iface; + LPVOID lpMem; + HRESULT hRet; + ULONG i; + + TRACE("(%p,%p,%p) stub\n", iface, lppTags, lppAccess); + + if (!iface || !lppTags || !lppAccess) + return MAPI_E_INVALID_PARAMETER; + + *lppTags = NULL; + *lppAccess = NULL; + + IMAPIPROP_Lock(This); + + hRet = This->lpAlloc(CbNewSPropTagArray(This->ulNumValues), &lpMem); + if (SUCCEEDED(hRet)) + { + *lppTags = lpMem; + + hRet = This->lpAlloc(This->ulNumValues * sizeof(ULONG), &lpMem); + if (SUCCEEDED(hRet)) + { + struct list *cursor; + + *lppAccess = lpMem; + (*lppTags)->cValues = This->ulNumValues; + + i = 0; + LIST_FOR_EACH(cursor, &This->values) + { + LPIPropDataItem item = LIST_ENTRY(cursor, IPropDataItem, entry); + (*lppTags)->aulPropTag[i] = item->value->ulPropTag; + (*lppAccess)[i] = item->ulAccess; + i++; + } + IMAPIPROP_Unlock(This); + return S_OK; + } + This->lpFree(*lppTags); + *lppTags = 0; + } + IMAPIPROP_Unlock(This); + return MAPI_E_NOT_ENOUGH_MEMORY; +} + +/************************************************************************** + * IPropData_HrAddObjProps {MAPI32} + * + * Not documented at this time. + * + * RETURNS + * An HRESULT success/failure code. + */ +static HRESULT WINAPI +IPropData_fnHrAddObjProps(LPPROPDATA iface, LPSPropTagArray lpTags, + LPSPropProblemArray *lppProbs) +{ +#if 0 + ULONG i; + HRESULT hRet; + LPSPropValue lpValues; +#endif + + FIXME("(%p,%p,%p) stub\n", iface, lpTags, lppProbs); + + if (!iface || !lpTags) + return MAPI_E_INVALID_PARAMETER; + + /* FIXME: Below is the obvious implementation, adding all the properties + * in lpTags to the object. However, it doesn't appear that this + * is what this function does. + */ + return S_OK; +#if 0 + if (!lpTags->cValues) + return S_OK; + + lpValues = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + lpTags->cValues * sizeof(SPropValue)); + if (!lpValues) + return MAPI_E_NOT_ENOUGH_MEMORY; + + for (i = 0; i < lpTags->cValues; i++) + lpValues[i].ulPropTag = lpTags->aulPropTag[i]; + + hRet = IPropData_SetProps(iface, lpTags->cValues, lpValues, lppProbs); + HeapFree(GetProcessHeap(), 0, lpValues); + return hRet; +#endif +} + +static const IPropDataVtbl IPropDataImpl_vtbl = +{ + IPropData_fnQueryInterface, + IPropData_fnAddRef, + IPropData_fnRelease, + IPropData_fnGetLastError, + IPropData_fnSaveChanges, + IPropData_fnGetProps, + IPropData_fnGetPropList, + IPropData_fnOpenProperty, + IPropData_fnSetProps, + IPropData_fnDeleteProps, + IPropData_fnCopyTo, + IPropData_fnCopyProps, + IPropData_fnGetNamesFromIDs, + IPropData_fnGetIDsFromNames, + IPropData_fnHrSetObjAccess, + IPropData_fnHrSetPropAccess, + IPropData_fnHrGetPropAccess, + IPropData_fnHrAddObjProps +}; + +/************************************************************************* + * CreateIProp@24 (MAPI32.60) + * + * Create an IPropData object. + * + * PARAMS + * iid [I] GUID of the object to create. Use &IID_IMAPIPropData or NULL + * lpAlloc [I] Memory allocation function. Use MAPIAllocateBuffer() + * lpMore [I] Linked memory allocation function. Use MAPIAllocateMore() + * lpFree [I] Memory free function. Use MAPIFreeBuffer() + * lpReserved [I] Reserved, set to NULL + * lppPropData [O] Destination for created IPropData object + * + * RETURNS + * Success: S_OK. *lppPropData contains the newly created object. + * Failure: MAPI_E_INTERFACE_NOT_SUPPORTED, if iid is non-NULL and not supported, + * MAPI_E_INVALID_PARAMETER, if any parameter is invalid + */ +SCODE WINAPI CreateIProp(LPCIID iid, ALLOCATEBUFFER *lpAlloc, + ALLOCATEMORE *lpMore, FREEBUFFER *lpFree, + LPVOID lpReserved, LPPROPDATA *lppPropData) +{ + IPropDataImpl *lpPropData; + SCODE scode; + + TRACE("(%s,%p,%p,%p,%p,%p)\n", debugstr_guid(iid), lpAlloc, lpMore, lpFree, + lpReserved, lppPropData); + + if (lppPropData) + *lppPropData = NULL; + + if (iid && !IsEqualGUID(iid, &IID_IMAPIPropData)) + return MAPI_E_INTERFACE_NOT_SUPPORTED; + + if (!lpAlloc || !lpMore || !lpFree || lpReserved || !lppPropData) + return MAPI_E_INVALID_PARAMETER; + + scode = lpAlloc(sizeof(IPropDataImpl), (LPVOID*)&lpPropData); + + if (SUCCEEDED(scode)) + { + lpPropData->lpVtbl = &IPropDataImpl_vtbl; + lpPropData->lRef = 1; + lpPropData->lpAlloc = lpAlloc; + lpPropData->lpMore = lpMore; + lpPropData->lpFree = lpFree; + lpPropData->ulObjAccess = IPROP_READWRITE; + lpPropData->ulNumValues = 0; + list_init(&lpPropData->values); + RtlInitializeCriticalSection(&lpPropData->cs); + *lppPropData = (LPPROPDATA)lpPropData; + } + return scode; +} diff --git a/reactos/dll/mapi32/util.c b/reactos/dll/mapi32/util.c new file mode 100644 index 00000000000..3b7fcc24b77 --- /dev/null +++ b/reactos/dll/mapi32/util.c @@ -0,0 +1,823 @@ +/* + * 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 + +#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; +} diff --git a/reactos/dll/mmdrv/auxil.c b/reactos/dll/mmdrv/auxil.c new file mode 100644 index 00000000000..b3a7f73f6d0 --- /dev/null +++ b/reactos/dll/mmdrv/auxil.c @@ -0,0 +1,106 @@ +/* + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Multimedia + * FILE: lib/mmdrv/auxil.c + * PURPOSE: Multimedia User Mode Driver + * PROGRAMMER: Andrew Greenwood + * Aleksey Bragin + * UPDATE HISTORY: + * Mar 16, 2004: Created skeleton implementation + */ +#include "mmdrv.h" +#include "wave.h" + + +#define NDEBUG +#include + +APIENTRY DWORD auxMessage(UINT dwId, + UINT uMessage, + DWORD dwUser, + DWORD dwParam1, + DWORD dwParam2) + +{ + MMRESULT Result; + AUX_DD_VOLUME Volume; + + DPRINT("auxMessage\n"); + + + // the following cases are documented by DDK + switch (uMessage) + { + case AUXDM_GETDEVCAPS: + DPRINT("AUXDM_GETDEVCAPS"); + return GetDeviceCapabilities(dwId, AuxDevice, (LPBYTE)dwParam1, (DWORD)dwParam2); + + case AUXDM_GETNUMDEVS: + DPRINT("AUXDM_GETNUMDEVS"); + return GetDeviceCount(AuxDevice); + + case AUXDM_GETVOLUME: + DPRINT("AUXDM_GETVOLUME"); + Result = AuxGetAudio(dwId, (PBYTE) &Volume, sizeof(Volume)); + + if (Result == MMSYSERR_NOERROR) + { + *(LPDWORD)dwParam1 = (DWORD)MAKELONG(HIWORD(Volume.Left), HIWORD(Volume.Right)); + } + return Result; + + + case AUXDM_SETVOLUME: + DPRINT("AUXDM_SETVOLUME"); + + Volume.Right = HIWORD(dwParam1) << 16; + Volume.Left = LOWORD(dwParam1) << 16; + + return AuxSetAudio(dwId, (PBYTE)&Volume, sizeof(Volume)); + + } + + return MMSYSERR_NOERROR; +} + + +DWORD AuxGetAudio(DWORD dwID, PBYTE pVolume, DWORD sizeVolume) +{ + HANDLE DeviceHandle; + MMRESULT Result; + DWORD BytesReturned; + + Result = OpenDevice(AuxDevice, dwID, &DeviceHandle, GENERIC_READ); + if (Result != MMSYSERR_NOERROR) + return Result; + + + Result = DeviceIoControl(DeviceHandle, IOCTL_AUX_GET_VOLUME, NULL, 0, (LPVOID)pVolume, sizeVolume, + &BytesReturned, NULL) ? MMSYSERR_NOERROR : TranslateStatus(); + + + CloseHandle(DeviceHandle); + + return Result; + } + +DWORD AuxSetAudio(DWORD dwID, PBYTE pVolume, DWORD sizeVolume) +{ + HANDLE DeviceHandle; + MMRESULT Result; + DWORD BytesReturned; + + Result = OpenDevice(AuxDevice, dwID, &DeviceHandle, GENERIC_READ); + if (Result != MMSYSERR_NOERROR) + return Result; + + Result = DeviceIoControl(DeviceHandle, IOCTL_AUX_SET_VOLUME, (LPVOID)pVolume, sizeVolume, NULL, 0, + &BytesReturned, NULL) ? MMSYSERR_NOERROR : TranslateStatus(); + + + CloseHandle(DeviceHandle); + + return Result; + } + diff --git a/reactos/dll/mmdrv/entry.c b/reactos/dll/mmdrv/entry.c new file mode 100644 index 00000000000..d2beefe0e89 --- /dev/null +++ b/reactos/dll/mmdrv/entry.c @@ -0,0 +1,136 @@ +/* + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Multimedia + * FILE: lib/mmdrv/entry.c + * PURPOSE: Multimedia User Mode Driver + * PROGRAMMER: Andrew Greenwood + * Aleksey Bragin + * UPDATE HISTORY: + * Jan 30, 2004: Imported into ReactOS tree (Greenwood) + * Mar 16, 2004: Cleaned up a bit (Bragin) + */ + + +#include "mmdrv.h" + +#define NDEBUG +#include + +#define EXPORT __declspec(dllexport) + +CRITICAL_SECTION DriverSection; + +APIENTRY LONG DriverProc(DWORD DriverID, HANDLE DriverHandle, UINT Message, + LONG Param1, LONG Param2) +{ + DPRINT("DriverProc\n"); + +// HINSTANCE Module; + + switch(Message) + { + case DRV_LOAD : + DPRINT("DRV_LOAD\n"); + return TRUE; // dont need to do any more +/* + Module = GetDriverModuleHandle(DriverHandle); + + // Create our process heap + Heap = GetProcessHeap(); + if (Heap == NULL) + return FALSE; + + DisableThreadLibraryCalls(Module); + InitializeCriticalSection(&CS); + + // + // Load our device list + // + +// if (sndFindDevices() != MMSYSERR_NOERROR) { +// DeleteCriticalSection(&mmDrvCritSec); +// return FALSE; +// } + + return TRUE; +*/ +// return 1L; + + case DRV_FREE : + DPRINT("DRV_FREE\n"); + +// TerminateMidi(); +// TerminateWave(); + +// DeleteCriticalSection(&CS); + return 1L; + + case DRV_OPEN : + DPRINT("DRV_OPEN\n"); + return 1L; + + case DRV_CLOSE : + DPRINT("DRV_CLOSE\n"); + return 1L; + + case DRV_ENABLE : + DPRINT("DRV_ENABLE\n"); + return 1L; + + case DRV_DISABLE : + DPRINT("DRV_DISABLE\n"); + return 1L; + + case DRV_QUERYCONFIGURE : + DPRINT("DRV_QUERYCONFIGURE\n"); + return 0L; + + case DRV_CONFIGURE : + DPRINT("DRV_CONFIGURE\n"); + return 0L; + + case DRV_INSTALL : + DPRINT("DRV_INSTALL\n"); + return DRVCNF_RESTART; + + default : + DPRINT("?\n"); + return DefDriverProc(DriverID, DriverHandle, Message, Param1, Param2); + }; +} + + +BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD Reason, LPVOID Reserved) +{ + DPRINT("DllMain called!\n"); + + if (Reason == DLL_PROCESS_ATTACH) + { + DisableThreadLibraryCalls(hInstance); + + // Create our heap + Heap = HeapCreate(0, 800, 0); + if (Heap == NULL) + return FALSE; + + InitializeCriticalSection(&CS); + + // OK to do this now?? + FindDevices(); + + } + else if (Reason == DLL_PROCESS_DETACH) + { + // We need to do cleanup here... +// TerminateMidi(); +// TerminateWave(); + + DeleteCriticalSection(&CS); + HeapDestroy(Heap); + } + + return TRUE; +} + +/* EOF */ diff --git a/reactos/dll/mmdrv/midi.c b/reactos/dll/mmdrv/midi.c new file mode 100644 index 00000000000..ac2299af615 --- /dev/null +++ b/reactos/dll/mmdrv/midi.c @@ -0,0 +1,357 @@ +/* + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Multimedia + * FILE: lib/mmdrv/midi.c + * PURPOSE: Multimedia User Mode Driver + * PROGRAMMER: Andrew Greenwood + * UPDATE HISTORY: + * Jan 30, 2004: Imported into ReactOS tree + */ + +#include "mmdrv.h" +#include "wave.h" + +#define NDEBUG +#include + +// MIDI device instance information +// +#define LOCAL_DATA_SIZE 20 +typedef struct _LOCALMIDIHDR { + OVERLAPPED Ovl; + DWORD BytesReturned; + struct _LOCALMIDIHDR *lpNext; + BOOL Done; + PVOID pClient; + // MIDI_DD_INPUT_DATA MidiData; + BYTE ExtraData[LOCAL_DATA_SIZE - sizeof(ULONG)]; + +} LOCALMIDIHDR, *PLOCALMIDIHDR; + +#define LOCAL_MIDI_BUFFERS 8 + +typedef struct { + + BOOL fMidiInStarted; + DWORD dwMsg; + DWORD dwCurData; + BYTE status; + BOOLEAN fSysex; + BOOLEAN Bad; + BYTE bBytesLeft; + BYTE bBytePos; + DWORD dwCurTime; + DWORD dwMsgTime; + + + PLOCALMIDIHDR DeviceQueue; + + LOCALMIDIHDR + Bufs[LOCAL_MIDI_BUFFERS]; + + +} LOCALMIDIDATA, *PLOCALMIDIDATA; + + +typedef struct tag_MIDIALLOC { + struct tag_MIDIALLOC *Next; // Chain of devices + UINT DeviceNumber; // Number of device + UINT DeviceType; // MidiInput or MidiOutput + DWORD dwCallback; // client's callback + DWORD dwInstance; // client's instance data + HMIDI hMidi; // handle for stream + HANDLE DeviceHandle; // Midi device handle + LPMIDIHDR lpMIQueue; // Buffers sent to device + // This is only required so that + // CLOSE knows when things have + // really finished. + // notify. This is only accessed + // on the device thread and its + // apcs so does not need any + // synchronized access. + HANDLE Event; // Event for driver syncrhonization + // and notification of auxiliary + // task operation completion. +// MIDITHREADFUNCTION AuxFunction; // Function for thread to perform + union { + LPMIDIHDR pHdr; // Buffer to pass in aux task + ULONG State; // State to set + struct { + ULONG Function; // IOCTL to use + PBYTE pData; // Data to set or get + ULONG DataLen; // Length of data + } GetSetData; + + } AuxParam; + // 0 means terminate task. + HANDLE ThreadHandle; // Handle for termination ONLY + HANDLE AuxEvent1; // Aux thread waits on this + HANDLE AuxEvent2; // Aux thread caller waits on this + DWORD AuxReturnCode; // Return code from Aux task + DWORD dwFlags; // Open flags + PLOCALMIDIDATA Mid; // Extra midi input structures + int l; // Helper global for modMidiLength + +} MIDIALLOC, *PMIDIALLOC; + +PMIDIALLOC MidiHandleList; // Our chain of wave handles + + + +static DWORD OpenMidiDevice(UINT DeviceType, DWORD ID, DWORD User, DWORD Param1, DWORD Param2) +{ + PMIDIALLOC pClient = NULL; + MMRESULT Result = MMSYSERR_NOERROR; + + // Check ID? + DPRINT("OpenMidiDevice()\n"); + + switch(DeviceType) + { + case MidiOutDevice : + pClient = (PMIDIALLOC) HeapAlloc(Heap, 0, sizeof(MIDIALLOC)); + if ( pClient ) memset(pClient, 0, sizeof(MIDIALLOC)); + break; + + case MidiInDevice : + pClient = (PMIDIALLOC) HeapAlloc(Heap, 0, sizeof(MIDIALLOC) + sizeof(LOCALMIDIDATA)); + if ( pClient ) memset(pClient, 0, sizeof(MIDIALLOC) + sizeof(LOCALMIDIDATA)); + break; + }; + + if ( !pClient ) + return MMSYSERR_NOMEM; + + if (DeviceType == MidiInDevice) + { + int i; + pClient->Mid = (PLOCALMIDIDATA)(pClient + 1); + for (i = 0 ;i < LOCAL_MIDI_BUFFERS ; i++) + { + pClient->Mid->Bufs[i].pClient = pClient; + } + } + + pClient->DeviceType = DeviceType; + pClient->dwCallback = ((LPMIDIOPENDESC)Param1)->dwCallback; + pClient->dwInstance = ((LPMIDIOPENDESC)Param1)->dwInstance; + pClient->hMidi = ((LPMIDIOPENDESC)Param1)->hMidi; + pClient->dwFlags = Param2; + + Result = OpenDevice(DeviceType, ID, &pClient->DeviceHandle, (GENERIC_READ | GENERIC_WRITE)); + + if ( Result != MMSYSERR_NOERROR ) + { + // cleanup + return Result; + } + + pClient->Event = CreateEvent(NULL, FALSE, FALSE, NULL); + + if ( !pClient->Event ) + { + // cleanup + return MMSYSERR_NOMEM; + } + + if (DeviceType == MidiInDevice) + { + + pClient->AuxEvent1 = CreateEvent(NULL, FALSE, FALSE, NULL); + if (pClient->AuxEvent1 == NULL) + { + // cleanup + return MMSYSERR_NOMEM; + } + + pClient->AuxEvent2 = CreateEvent(NULL, FALSE, FALSE, NULL); + if (pClient->AuxEvent2 == NULL) + { + // cleanup + return MMSYSERR_NOMEM; + } + + + // TaskCreate + + + WaitForSingleObject(pClient->AuxEvent2, INFINITE); + } + + PMIDIALLOC *pUserHandle; + pUserHandle = (PMIDIALLOC*) User; + *pUserHandle = pClient; + + // callback + + return MMSYSERR_NOERROR; +} + + + +static DWORD WriteMidi(PBYTE pData, ULONG Length, PMIDIALLOC pClient) +{ + DWORD BytesReturned; + + DPRINT("IOCTL_MIDI_PLAY == %d [%x]\n", IOCTL_MIDI_PLAY, IOCTL_MIDI_PLAY); + + if ( !DeviceIoControl(pClient->DeviceHandle, IOCTL_MIDI_PLAY, (PVOID)pData, + Length, NULL, 0, &BytesReturned, NULL)) + return TranslateStatus(); + + return MMSYSERR_NOERROR; +} + + +static int GetMidiLength(PMIDIALLOC pClient, BYTE b) +{ + if (b >= 0xF8) + { + // Realtime message - leave running status + return 1; // Write one byte + } + + switch (b) + { + case 0xF0: case 0xF4: case 0xF5: case 0xF6: case 0xF7: + pClient->l = 1; + return pClient->l; + + case 0xF1: case 0xF3: + pClient->l = 2; + return pClient->l; + + case 0xF2: + pClient->l = 3; + return pClient->l; + } + + switch (b & 0xF0) + { + case 0x80: case 0x90: case 0xA0: case 0xB0: case 0xE0: + pClient->l = 3; + return pClient->l; + + case 0xC0: case 0xD0: + pClient->l = 2; + return pClient->l; + } + + return (pClient->l - 1); // uses previous value if data byte (running status) +} + + + +/* ---------------------------------------------------------------------------- + Exported functions +----------------------------------------------------------------------------- */ + +APIENTRY DWORD midMessage(DWORD dwId, DWORD dwMessage, DWORD dwUser, DWORD dwParam1, DWORD dwParam2) +{ + DPRINT("midMessage\n"); + return MMSYSERR_NOERROR; + + switch (dwMessage) { + case MIDM_GETNUMDEVS: + DPRINT("MIDM_GETNUMDEVS"); + return GetDeviceCount(MidiInDevice); + + case MIDM_GETDEVCAPS: + DPRINT("MIDM_GETDEVCAPS"); + return GetDeviceCapabilities(dwId, MidiInDevice, (LPBYTE)dwParam1, (DWORD)dwParam2); + + case MIDM_OPEN: + DPRINT("MIDM_OPEN"); + return MMSYSERR_NOERROR; + + case MIDM_CLOSE: + DPRINT("MIDM_CLOSE"); + return MMSYSERR_NOERROR; + + case MIDM_ADDBUFFER: + DPRINT("MIDM_ADDBUFFER"); + return MMSYSERR_NOERROR; + + case MIDM_STOP: + DPRINT("MIDM_PAUSE"); + return MMSYSERR_NOERROR; + + case MIDM_START: + DPRINT("MIDM_RESTART"); + return MMSYSERR_NOERROR; + + case MIDM_RESET: + DPRINT("MIDM_RESET"); + return MMSYSERR_NOERROR; + + default: + return MMSYSERR_NOTSUPPORTED; + } + + // the function should never get to this point + //FIXME: Would it be wise to assert here? + return MMSYSERR_NOTSUPPORTED; +} + +APIENTRY DWORD modMessage(DWORD ID, DWORD Message, DWORD User, DWORD Param1, DWORD Param2) +{ + DPRINT("modMessage\n"); + + switch(Message) + { + case MODM_GETNUMDEVS: + DPRINT("MODM_GETNUMDEVS == %d\n", (int)GetDeviceCount(MidiOutDevice)); + return GetDeviceCount(MidiOutDevice); + + case MODM_GETDEVCAPS: + DPRINT("MODM_GETDEVCAPS"); + return GetDeviceCapabilities(ID, MidiOutDevice, (LPBYTE)Param1, (DWORD)Param2); + + case MODM_OPEN : + return OpenMidiDevice(MidiOutDevice, ID, User, Param1, Param2); + + case MODM_CLOSE: + DPRINT("MODM_CLOSE"); + return MMSYSERR_NOTSUPPORTED; + + case MODM_DATA: + DPRINT("MODM_DATA"); + + int i; + BYTE b[4]; + for (i = 0; i < 4; i ++) { + b[i] = (BYTE)(Param1 % 256); + Param1 /= 256; + } + return WriteMidi(b, GetMidiLength((PMIDIALLOC)User, b[0]), + (PMIDIALLOC)User); + + case MODM_LONGDATA: + DPRINT("MODM_LONGDATA"); + return MMSYSERR_NOTSUPPORTED; + + case MODM_RESET: + DPRINT("MODM_RESET"); + return MMSYSERR_NOTSUPPORTED; + + case MODM_SETVOLUME: + DPRINT("MODM_SETVOLUME"); + return MMSYSERR_NOTSUPPORTED; + + case MODM_GETVOLUME: + DPRINT("MODM_GETVOLUME"); + return MMSYSERR_NOTSUPPORTED; + + case MODM_CACHEPATCHES: + DPRINT("MODM_CACHEPATCHES"); + return MMSYSERR_NOTSUPPORTED; + + case MODM_CACHEDRUMPATCHES: + DPRINT("MODM_CACHEDRUMPATCHES"); + return MMSYSERR_NOTSUPPORTED; + + }; + + return MMSYSERR_NOTSUPPORTED; +} diff --git a/reactos/dll/mmdrv/mmddk.h b/reactos/dll/mmdrv/mmddk.h new file mode 100644 index 00000000000..2598b084e32 --- /dev/null +++ b/reactos/dll/mmdrv/mmddk.h @@ -0,0 +1,471 @@ +/* -*- tab-width: 8; c-basic-offset: 4 -*- */ + +/***************************************************************************** + * Copyright 1998, Luiz Otavio L. Zorzella + * 1999, Eric Pouech + * + * Purpose: multimedia declarations (external to WINMM & MMSYSTEM DLLs + * for other DLLs (MCI, drivers...)) + * + * 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 + * + ***************************************************************************** + */ +#ifndef __MMDDK_H +#define __MMDDK_H + +#include +#include + +typedef VOID (TASKCALLBACK) (DWORD dwInst); + +typedef TASKCALLBACK FAR *LPTASKCALLBACK; + +UINT APIENTRY mmTaskCreate(LPTASKCALLBACK lpfn, HANDLE FAR * lph, DWORD dwInst); +VOID APIENTRY mmTaskBlock(DWORD h); +BOOL APIENTRY mmTaskSignal(DWORD h); +VOID APIENTRY mmTaskYield(VOID); +DWORD APIENTRY mmGetCurrentTask(VOID); + + +#define MAX_MIDIINDRV (16) +/* For now I'm making 16 the maximum number of midi devices one can + * have. This should be more than enough for everybody. But as a purist, + * I intend to make it unbounded in the future, as soon as I figure + * a good way to do so. + */ +#define MAX_MIDIOUTDRV (16) + +/* ================================== + * Multimedia DDK compatible part + * ================================== */ + +#include + +#define DRVM_INIT 100 +#define DRVM_EXIT 101 +#define DRVM_DISABLE 102 +#define DRVM_ENABLE 103 + +/* messages that have IOCTL format + * dw1 = NULL or handle + * dw2 = NULL or ptr to DRVM_IOCTL_DATA + * return is MMRESULT + */ +#define DRVM_IOCTL 0x100 +#define DRVM_ADD_THRU (DRVM_IOCTL+1) +#define DRVM_REMOVE_THRU (DRVM_IOCTL+2) +#define DRVM_IOCTL_LAST (DRVM_IOCTL+5) +typedef struct { + DWORD dwSize; /* size of this structure */ + DWORD dwCmd; /* IOCTL command code, 0x80000000 and above reserved for system */ +} DRVM_IOCTL_DATA, *LPDRVM_IOCTL_DATA; + +/* command code ranges for dwCmd field of DRVM_IOCTL message + * - codes from 0 to 0x7FFFFFFF are user defined + * - codes from 0x80000000 to 0xFFFFFFFF are reserved for future definition by microsoft + */ +#define DRVM_IOCTL_CMD_USER 0x00000000L +#define DRVM_IOCTL_CMD_SYSTEM 0x80000000L + +#define DRVM_MAPPER 0x2000 +#define DRVM_USER 0x4000 +#define DRVM_MAPPER_STATUS (DRVM_MAPPER+0) +#define DRVM_MAPPER_RECONFIGURE (DRVM_MAPPER+1) +#define DRVM_MAPPER_PREFERRED_GET (DRVM_MAPPER+21) +#define DRVM_MAPPER_CONSOLEVOICECOM_GET (DRVM_MAPPER+23) + +#define DRV_QUERYDRVENTRY (DRV_RESERVED + 1) +#define DRV_QUERYDEVNODE (DRV_RESERVED + 2) +#define DRV_QUERYNAME (DRV_RESERVED + 3) +#define DRV_QUERYDRIVERIDS (DRV_RESERVED + 4) +#define DRV_QUERYMAPPABLE (DRV_RESERVED + 5) +#define DRV_QUERYMODULE (DRV_RESERVED + 9) +#define DRV_PNPINSTALL (DRV_RESERVED + 11) +#define DRV_QUERYDEVICEINTERFACE (DRV_RESERVED + 12) +#define DRV_QUERYDEVICEINTERFACESIZE (DRV_RESERVED + 13) +#define DRV_QUERYSTRINGID (DRV_RESERVED + 14) +#define DRV_QUERYSTRINGIDSIZE (DRV_RESERVED + 15) +#define DRV_QUERYIDFROMSTRINGID (DRV_RESERVED + 16) +#ifdef __WINESRC__ +#define DRV_QUERYDSOUNDIFACE (DRV_RESERVED + 20) +#define DRV_QUERYDSOUNDDESC (DRV_RESERVED + 21) +#define DRV_QUERYDSOUNDGUID (DRV_RESERVED + 22) +#endif + +#define WODM_INIT DRVM_INIT +#define WODM_GETNUMDEVS 3 +#define WODM_GETDEVCAPS 4 +#define WODM_OPEN 5 +#define WODM_CLOSE 6 +#define WODM_PREPARE 7 +#define WODM_UNPREPARE 8 +#define WODM_WRITE 9 +#define WODM_PAUSE 10 +#define WODM_RESTART 11 +#define WODM_RESET 12 +#define WODM_GETPOS 13 +#define WODM_GETPITCH 14 +#define WODM_SETPITCH 15 +#define WODM_GETVOLUME 16 +#define WODM_SETVOLUME 17 +#define WODM_GETPLAYBACKRATE 18 +#define WODM_SETPLAYBACKRATE 19 +#define WODM_BREAKLOOP 20 +#define WODM_PREFERRED 21 + +#define WODM_MAPPER_STATUS (DRVM_MAPPER_STATUS + 0) +#define WAVEOUT_MAPPER_STATUS_DEVICE 0 +#define WAVEOUT_MAPPER_STATUS_MAPPED 1 +#define WAVEOUT_MAPPER_STATUS_FORMAT 2 + +#define WODM_BUSY 21 + +#define WIDM_INIT DRVM_INIT +#define WIDM_GETNUMDEVS 50 +#define WIDM_GETDEVCAPS 51 +#define WIDM_OPEN 52 +#define WIDM_CLOSE 53 +#define WIDM_PREPARE 54 +#define WIDM_UNPREPARE 55 +#define WIDM_ADDBUFFER 56 +#define WIDM_START 57 +#define WIDM_STOP 58 +#define WIDM_RESET 59 +#define WIDM_GETPOS 60 +#define WIDM_PREFERRED 61 +#define WIDM_MAPPER_STATUS (DRVM_MAPPER_STATUS + 0) +#define WAVEIN_MAPPER_STATUS_DEVICE 0 +#define WAVEIN_MAPPER_STATUS_MAPPED 1 +#define WAVEIN_MAPPER_STATUS_FORMAT 2 + +#define MODM_INIT DRVM_INIT +#define MODM_GETNUMDEVS 1 +#define MODM_GETDEVCAPS 2 +#define MODM_OPEN 3 +#define MODM_CLOSE 4 +#define MODM_PREPARE 5 +#define MODM_UNPREPARE 6 +#define MODM_DATA 7 +#define MODM_LONGDATA 8 +#define MODM_RESET 9 +#define MODM_GETVOLUME 10 +#define MODM_SETVOLUME 11 +#define MODM_CACHEPATCHES 12 +#define MODM_CACHEDRUMPATCHES 13 + +#define MIDM_INIT DRVM_INIT +#define MIDM_GETNUMDEVS 53 +#define MIDM_GETDEVCAPS 54 +#define MIDM_OPEN 55 +#define MIDM_CLOSE 56 +#define MIDM_PREPARE 57 +#define MIDM_UNPREPARE 58 +#define MIDM_ADDBUFFER 59 +#define MIDM_START 60 +#define MIDM_STOP 61 +#define MIDM_RESET 62 + + +#define AUXM_INIT DRVM_INIT +#define AUXDM_GETNUMDEVS 3 +#define AUXDM_GETDEVCAPS 4 +#define AUXDM_GETVOLUME 5 +#define AUXDM_SETVOLUME 6 + +#define MXDM_INIT DRVM_INIT +#define MXDM_USER DRVM_USER +#define MXDM_MAPPER DRVM_MAPPER + +#define MXDM_GETNUMDEVS 1 +#define MXDM_GETDEVCAPS 2 +#define MXDM_OPEN 3 +#define MXDM_CLOSE 4 +#define MXDM_GETLINEINFO 5 +#define MXDM_GETLINECONTROLS 6 +#define MXDM_GETCONTROLDETAILS 7 +#define MXDM_SETCONTROLDETAILS 8 + +/* pre-defined joystick types */ +#define JOY_HW_NONE 0 +#define JOY_HW_CUSTOM 1 +#define JOY_HW_2A_2B_GENERIC 2 +#define JOY_HW_2A_4B_GENERIC 3 +#define JOY_HW_2B_GAMEPAD 4 +#define JOY_HW_2B_FLIGHTYOKE 5 +#define JOY_HW_2B_FLIGHTYOKETHROTTLE 6 +#define JOY_HW_3A_2B_GENERIC 7 +#define JOY_HW_3A_4B_GENERIC 8 +#define JOY_HW_4B_GAMEPAD 9 +#define JOY_HW_4B_FLIGHTYOKE 10 +#define JOY_HW_4B_FLIGHTYOKETHROTTLE 11 +#define JOY_HW_LASTENTRY 12 + +/* calibration flags */ +#define JOY_ISCAL_XY 0x00000001l /* XY are calibrated */ +#define JOY_ISCAL_Z 0x00000002l /* Z is calibrated */ +#define JOY_ISCAL_R 0x00000004l /* R is calibrated */ +#define JOY_ISCAL_U 0x00000008l /* U is calibrated */ +#define JOY_ISCAL_V 0x00000010l /* V is calibrated */ +#define JOY_ISCAL_POV 0x00000020l /* POV is calibrated */ + +/* point of view constants */ +#define JOY_POV_NUMDIRS 4 +#define JOY_POVVAL_FORWARD 0 +#define JOY_POVVAL_BACKWARD 1 +#define JOY_POVVAL_LEFT 2 +#define JOY_POVVAL_RIGHT 3 + +/* Specific settings for joystick hardware */ +#define JOY_HWS_HASZ 0x00000001l /* has Z info? */ +#define JOY_HWS_HASPOV 0x00000002l /* point of view hat present */ +#define JOY_HWS_POVISBUTTONCOMBOS 0x00000004l /* pov done through combo of buttons */ +#define JOY_HWS_POVISPOLL 0x00000008l /* pov done through polling */ +#define JOY_HWS_ISYOKE 0x00000010l /* joystick is a flight yoke */ +#define JOY_HWS_ISGAMEPAD 0x00000020l /* joystick is a game pad */ +#define JOY_HWS_ISCARCTRL 0x00000040l /* joystick is a car controller */ +/* X defaults to J1 X axis */ +#define JOY_HWS_XISJ1Y 0x00000080l /* X is on J1 Y axis */ +#define JOY_HWS_XISJ2X 0x00000100l /* X is on J2 X axis */ +#define JOY_HWS_XISJ2Y 0x00000200l /* X is on J2 Y axis */ +/* Y defaults to J1 Y axis */ +#define JOY_HWS_YISJ1X 0x00000400l /* Y is on J1 X axis */ +#define JOY_HWS_YISJ2X 0x00000800l /* Y is on J2 X axis */ +#define JOY_HWS_YISJ2Y 0x00001000l /* Y is on J2 Y axis */ +/* Z defaults to J2 Y axis */ +#define JOY_HWS_ZISJ1X 0x00002000l /* Z is on J1 X axis */ +#define JOY_HWS_ZISJ1Y 0x00004000l /* Z is on J1 Y axis */ +#define JOY_HWS_ZISJ2X 0x00008000l /* Z is on J2 X axis */ +/* POV defaults to J2 Y axis, if it is not button based */ +#define JOY_HWS_POVISJ1X 0x00010000l /* pov done through J1 X axis */ +#define JOY_HWS_POVISJ1Y 0x00020000l /* pov done through J1 Y axis */ +#define JOY_HWS_POVISJ2X 0x00040000l /* pov done through J2 X axis */ +/* R defaults to J2 X axis */ +#define JOY_HWS_HASR 0x00080000l /* has R (4th axis) info */ +#define JOY_HWS_RISJ1X 0x00100000l /* R done through J1 X axis */ +#define JOY_HWS_RISJ1Y 0x00200000l /* R done through J1 Y axis */ +#define JOY_HWS_RISJ2Y 0x00400000l /* R done through J2 X axis */ +/* U & V for future hardware */ +#define JOY_HWS_HASU 0x00800000l /* has U (5th axis) info */ +#define JOY_HWS_HASV 0x01000000l /* has V (6th axis) info */ + +/* Usage settings */ +#define JOY_US_HASRUDDER 0x00000001l /* joystick configured with rudder */ +#define JOY_US_PRESENT 0x00000002l /* is joystick actually present? */ +#define JOY_US_ISOEM 0x00000004l /* joystick is an OEM defined type */ + + +/* struct for storing x,y, z, and rudder values */ +typedef struct joypos_tag { + DWORD dwX; + DWORD dwY; + DWORD dwZ; + DWORD dwR; + DWORD dwU; + DWORD dwV; +} JOYPOS, *LPJOYPOS; + +/* struct for storing ranges */ +typedef struct joyrange_tag { + JOYPOS jpMin; + JOYPOS jpMax; + JOYPOS jpCenter; +} JOYRANGE,*LPJOYRANGE; + +typedef struct joyreguservalues_tag { + DWORD dwTimeOut; /* value at which to timeout joystick polling */ + JOYRANGE jrvRanges; /* range of values app wants returned for axes */ + JOYPOS jpDeadZone; /* area around center to be considered + as "dead". specified as a percentage + (0-100). Only X & Y handled by system driver */ +} JOYREGUSERVALUES, *LPJOYREGUSERVALUES; + +typedef struct joyreghwsettings_tag { + DWORD dwFlags; + DWORD dwNumButtons; /* number of buttons */ +} JOYREGHWSETTINGS, *LPJOYHWSETTINGS; + +/* range of values returned by the hardware (filled in by calibration) */ +typedef struct joyreghwvalues_tag { + JOYRANGE jrvHardware; /* values returned by hardware */ + DWORD dwPOVValues[JOY_POV_NUMDIRS];/* POV values returned by hardware */ + DWORD dwCalFlags; /* what has been calibrated */ +} JOYREGHWVALUES, *LPJOYREGHWVALUES; + +/* hardware configuration */ +typedef struct joyreghwconfig_tag { + JOYREGHWSETTINGS hws; /* hardware settings */ + DWORD dwUsageSettings;/* usage settings */ + JOYREGHWVALUES hwv; /* values returned by hardware */ + DWORD dwType; /* type of joystick */ + DWORD dwReserved; /* reserved for OEM drivers */ +} JOYREGHWCONFIG, *LPJOYREGHWCONFIG; + +/* joystick calibration info structure */ +typedef struct joycalibrate_tag { + UINT wXbase; + UINT wXdelta; + UINT wYbase; + UINT wYdelta; + UINT wZbase; + UINT wZdelta; +} JOYCALIBRATE; +typedef JOYCALIBRATE *LPJOYCALIBRATE; + +/* prototype for joystick message function */ +typedef UINT (CALLBACK * JOYDEVMSGPROC)(DWORD dwID, UINT uMessage, LPARAM lParam1, LPARAM lParam2); +typedef JOYDEVMSGPROC *LPJOYDEVMSGPROC; + +/* messages sent to joystick driver's DriverProc() function */ +#define JDD_GETNUMDEVS (DRV_RESERVED + 0x0001) +#define JDD_GETDEVCAPS (DRV_RESERVED + 0x0002) +#define JDD_GETPOS (DRV_RESERVED + 0x0101) +#define JDD_SETCALIBRATION (DRV_RESERVED + 0x0102) +#define JDD_CONFIGCHANGED (DRV_RESERVED + 0x0103) +#define JDD_GETPOSEX (DRV_RESERVED + 0x0104) + +#define MCI_MAX_DEVICE_TYPE_LENGTH 80 + +#define MCI_FALSE (MCI_STRING_OFFSET + 19) +#define MCI_TRUE (MCI_STRING_OFFSET + 20) + +#define MCI_FORMAT_RETURN_BASE MCI_FORMAT_MILLISECONDS_S +#define MCI_FORMAT_MILLISECONDS_S (MCI_STRING_OFFSET + 21) +#define MCI_FORMAT_HMS_S (MCI_STRING_OFFSET + 22) +#define MCI_FORMAT_MSF_S (MCI_STRING_OFFSET + 23) +#define MCI_FORMAT_FRAMES_S (MCI_STRING_OFFSET + 24) +#define MCI_FORMAT_SMPTE_24_S (MCI_STRING_OFFSET + 25) +#define MCI_FORMAT_SMPTE_25_S (MCI_STRING_OFFSET + 26) +#define MCI_FORMAT_SMPTE_30_S (MCI_STRING_OFFSET + 27) +#define MCI_FORMAT_SMPTE_30DROP_S (MCI_STRING_OFFSET + 28) +#define MCI_FORMAT_BYTES_S (MCI_STRING_OFFSET + 29) +#define MCI_FORMAT_SAMPLES_S (MCI_STRING_OFFSET + 30) +#define MCI_FORMAT_TMSF_S (MCI_STRING_OFFSET + 31) + +#define MCI_VD_FORMAT_TRACK_S (MCI_VD_OFFSET + 5) + +#define WAVE_FORMAT_PCM_S (MCI_WAVE_OFFSET + 0) +#define WAVE_MAPPER_S (MCI_WAVE_OFFSET + 1) + +#define MCI_SEQ_MAPPER_S (MCI_SEQ_OFFSET + 5) +#define MCI_SEQ_FILE_S (MCI_SEQ_OFFSET + 6) +#define MCI_SEQ_MIDI_S (MCI_SEQ_OFFSET + 7) +#define MCI_SEQ_SMPTE_S (MCI_SEQ_OFFSET + 8) +#define MCI_SEQ_FORMAT_SONGPTR_S (MCI_SEQ_OFFSET + 9) +#define MCI_SEQ_NONE_S (MCI_SEQ_OFFSET + 10) +#define MIDIMAPPER_S (MCI_SEQ_OFFSET + 11) + +#define MCI_RESOURCE_RETURNED 0x00010000 /* resource ID */ +#define MCI_COLONIZED3_RETURN 0x00020000 /* colonized ID, 3 bytes data */ +#define MCI_COLONIZED4_RETURN 0x00040000 /* colonized ID, 4 bytes data */ +#define MCI_INTEGER_RETURNED 0x00080000 /* integer conversion needed */ +#define MCI_RESOURCE_DRIVER 0x00100000 /* driver owns returned resource */ + +#define MCI_NO_COMMAND_TABLE 0xFFFF + +#define MCI_COMMAND_HEAD 0 +#define MCI_STRING 1 +#define MCI_INTEGER 2 +#define MCI_END_COMMAND 3 +#define MCI_RETURN 4 +#define MCI_FLAG 5 +#define MCI_END_COMMAND_LIST 6 +#define MCI_RECT 7 +#define MCI_CONSTANT 8 +#define MCI_END_CONSTANT 9 + +#define MAKEMCIRESOURCE(wRet, wRes) MAKELRESULT((wRet), (wRes)) + +typedef struct { + DWORD dwCallback; + DWORD dwInstance; + HMIDIOUT hMidi; + DWORD dwFlags; +} PORTALLOC, *LPPORTALLOC; + +typedef struct { + HWAVE hWave; + LPWAVEFORMATEX lpFormat; + DWORD dwCallback; + DWORD dwInstance; + UINT uMappedDeviceID; + DWORD dnDevNode; +} WAVEOPENDESC, *LPWAVEOPENDESC; + +typedef struct { + DWORD dwStreamID; + WORD wDeviceID; +} MIDIOPENSTRMID; + +typedef struct { + HMIDI hMidi; + DWORD dwCallback; + DWORD dwInstance; + DWORD dnDevNode; + DWORD cIds; + MIDIOPENSTRMID rgIds; +} MIDIOPENDESC, *LPMIDIOPENDESC; + +typedef struct tMIXEROPENDESC +{ + HMIXEROBJ hmx; + LPVOID pReserved0; + DWORD dwCallback; + DWORD dwInstance; +} MIXEROPENDESC, *LPMIXEROPENDESC; + +typedef struct { + UINT wDeviceID; /* device ID */ + LPSTR lpstrParams; /* parameter string for entry in SYSTEM.INI */ + UINT wCustomCommandTable; /* custom command table (0xFFFF if none) * filled in by the driver */ + UINT wType; /* driver type (filled in by the driver) */ +} MCI_OPEN_DRIVER_PARMSA, *LPMCI_OPEN_DRIVER_PARMSA; + +typedef struct { + UINT wDeviceID; /* device ID */ + LPWSTR lpstrParams; /* parameter string for entry in SYSTEM.INI */ + UINT wCustomCommandTable; /* custom command table (0xFFFF if none) * filled in by the driver */ + UINT wType; /* driver type (filled in by the driver) */ +} MCI_OPEN_DRIVER_PARMSW, *LPMCI_OPEN_DRIVER_PARMSW; +//DECL_WINELIB_TYPE_AW(MCI_OPEN_DRIVER_PARMS) +//DECL_WINELIB_TYPE_AW(LPMCI_OPEN_DRIVER_PARMS) + +DWORD WINAPI mciGetDriverData(UINT uDeviceID); +BOOL WINAPI mciSetDriverData(UINT uDeviceID, DWORD dwData); +UINT WINAPI mciDriverYield(UINT uDeviceID); +BOOL WINAPI mciDriverNotify(HWND hwndCallback, UINT uDeviceID, + UINT uStatus); +UINT WINAPI mciLoadCommandResource(HINSTANCE hInstance, + LPCWSTR lpResName, UINT uType); +BOOL WINAPI mciFreeCommandResource(UINT uTable); + +#define DCB_NULL 0x0000 +#define DCB_WINDOW 0x0001 /* dwCallback is a HWND */ +#define DCB_TASK 0x0002 /* dwCallback is a HTASK */ +#define DCB_FUNCTION 0x0003 /* dwCallback is a FARPROC */ +#define DCB_EVENT 0x0005 /* dwCallback is an EVENT Handler */ +#define DCB_TYPEMASK 0x0007 +#define DCB_NOSWITCH 0x0008 /* don't switch stacks for callback */ + +BOOL WINAPI DriverCallback(DWORD dwCallBack, UINT uFlags, HDRVR hDev, + UINT wMsg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2); + +#ifdef __WINESRC__ +#define WAVE_DIRECTSOUND 0x0080 +#endif + +#include + +#endif /* __MMDDK_H */ diff --git a/reactos/dll/mmdrv/mmdef.h b/reactos/dll/mmdrv/mmdef.h new file mode 100644 index 00000000000..acc842ed0b2 --- /dev/null +++ b/reactos/dll/mmdrv/mmdef.h @@ -0,0 +1,94 @@ +/* + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS kernel + * FILE: lib/mmdrv/mmdef.h + * PURPOSE: Multimedia Definitions (for mmdrv.dll) + * PROGRAMMER: Andrew Greenwood + * + */ + +#ifndef __INCLUDES_MMDEF_H__ +#define __INCLUDES_MMDEF_H__ + +//#define UNICODE + +#define EXPORT __declspec(dllexport) + + +//#include +//#include +//#include +//#include + +// This needs to be done to get winioctl.h to work: +//typedef unsigned __int64 DWORD64, *PDWORD64; + +//#include +//#include "mmddk.h" + + +#define SOUND_MAX_DEVICE_NAME 1024 // GUESSWORK +#define SOUND_MAX_DEVICES 256 // GUESSWORK + + +// If the root is \Device and the Device type is +// WaveIn and the device number is 2, the full name is \Device\WaveIn2 + +#define WAVE_IN_DEVICE_NAME "\\Device\\WaveIn" +#define WAVE_IN_DEVICE_NAME_U L"\\Device\\WaveIn" +#define WAVE_OUT_DEVICE_NAME "\\Device\\WaveOut" +#define WAVE_OUT_DEVICE_NAME_U L"\\Device\\WaveOut" + +#define MIDI_IN_DEVICE_NAME "\\Device\\MidiIn" +#define MIDI_IN_DEVICE_NAME_U L"\\Device\\MidiIn" +#define MIDI_OUT_DEVICE_NAME "\\Device\\MidiOut" +#define MIDI_OUT_DEVICE_NAME_U L"\\Device\\MidiOut" + +#define AUX_DEVICE_NAME "\\Device\\MMAux" +#define AUX_DEVICE_NAME_U L"\\Device\\MMAux" + + +#define IOCTL_SOUND_BASE FILE_DEVICE_SOUND +#define IOCTL_WAVE_BASE 0x0000 +#define IOCTL_MIDI_BASE 0x0080 +#define IOCTL_AUX_BASE 0x0100 + +// Wave device driver IOCTLs + +#define IOCTL_WAVE_QUERY_FORMAT CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0001, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_WAVE_SET_FORMAT CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0002, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_WAVE_GET_CAPABILITIES CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0003, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_WAVE_SET_STATE CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0004, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_WAVE_GET_STATE CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0005, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_WAVE_GET_POSITION CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0006, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_WAVE_SET_VOLUME CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0007, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_WAVE_GET_VOLUME CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0008, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_WAVE_SET_PITCH CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0009, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_WAVE_GET_PITCH CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000A, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_WAVE_SET_PLAYBACK_RATE CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000B, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_WAVE_GET_PLAYBACK_RATE CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000C, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_WAVE_PLAY CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000D, METHOD_IN_DIRECT, FILE_WRITE_ACCESS) +#define IOCTL_WAVE_RECORD CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000E, METHOD_OUT_DIRECT, FILE_WRITE_ACCESS) +#define IOCTL_WAVE_BREAK_LOOP CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000F, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_WAVE_SET_LOW_PRIORITY CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0010, METHOD_BUFFERED, FILE_WRITE_ACCESS) + +// MIDI device driver IOCTLs + +#define IOCTL_MIDI_GET_CAPABILITIES CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0001, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_MIDI_SET_STATE CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0002, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_MIDI_GET_STATE CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0003, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_MIDI_SET_VOLUME CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0004, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_MIDI_GET_VOLUME CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0005, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_MIDI_PLAY CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0006, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_MIDI_RECORD CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0007, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_MIDI_CACHE_PATCHES CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0008, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_MIDI_CACHE_DRUM_PATCHES CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0009, METHOD_BUFFERED, FILE_WRITE_ACCESS) + +// AUX device driver IOCTLs +#define IOCTL_AUX_GET_CAPABILITIES CTL_CODE(IOCTL_SOUND_BASE, IOCTL_AUX_BASE + 0x0001, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_AUX_SET_VOLUME CTL_CODE(IOCTL_SOUND_BASE, IOCTL_AUX_BASE + 0x0002, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_AUX_GET_VOLUME CTL_CODE(IOCTL_SOUND_BASE, IOCTL_AUX_BASE + 0x0003, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_SOUND_GET_CHANGED_VOLUME CTL_CODE(IOCTL_SOUND_BASE, IOCTL_AUX_BASE + 0x0004, METHOD_BUFFERED, FILE_READ_ACCESS) + +#endif diff --git a/reactos/dll/mmdrv/mmdrv.def b/reactos/dll/mmdrv/mmdrv.def new file mode 100644 index 00000000000..28e058838e7 --- /dev/null +++ b/reactos/dll/mmdrv/mmdrv.def @@ -0,0 +1,14 @@ +; $Id$ +; +; mmdrv.def +; +; ReactOS Operating System +; +LIBRARY mmdrv.dll +EXPORTS +DriverProc@20 +widMessage@20 +wodMessage@20 +midMessage@20 +modMessage@20 +auxMessage@20 diff --git a/reactos/dll/mmdrv/mmdrv.h b/reactos/dll/mmdrv/mmdrv.h new file mode 100644 index 00000000000..f9211d362a4 --- /dev/null +++ b/reactos/dll/mmdrv/mmdrv.h @@ -0,0 +1,118 @@ +/* + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Multimedia + * FILE: lib/mmdrv/mmdrv.h + * PURPOSE: Multimedia User Mode Driver (header) + * PROGRAMMER: Andrew Greenwood + * Aleksey Bragin + * UPDATE HISTORY: + * Jan 30, 2004: Imported into ReactOS tree + */ + +#ifndef __INCLUDES_MMDRV_H__ +#define __INCLUDES_MMDRV_H__ + +//#define UNICODE + +#define EXPORT __declspec(dllexport) + + +#include +#include +#include +#include + +// This needs to be done to get winioctl.h to work: +//typedef unsigned __int64 DWORD64, *PDWORD64; + +#include +//#include "mmddk.h" + +#include "mmdef.h" + +ULONG DbgPrint(PCH Format, ...); + +/* +#define SOUND_MAX_DEVICE_NAME 1024 // GUESSWORK +#define SOUND_MAX_DEVICES 256 // GUESSWORK +*/ + +// If the root is \Device and the Device type is +// WaveIn and the device number is 2, the full name is \Device\WaveIn2 + +#define WAVE_IN_DEVICE_NAME "\\Device\\WaveIn" +#define WAVE_IN_DEVICE_NAME_U L"\\Device\\WaveIn" +#define WAVE_OUT_DEVICE_NAME "\\Device\\WaveOut" +#define WAVE_OUT_DEVICE_NAME_U L"\\Device\\WaveOut" + +#define MIDI_IN_DEVICE_NAME "\\Device\\MidiIn" +#define MIDI_IN_DEVICE_NAME_U L"\\Device\\MidiIn" +#define MIDI_OUT_DEVICE_NAME "\\Device\\MidiOut" +#define MIDI_OUT_DEVICE_NAME_U L"\\Device\\MidiOut" + +#define AUX_DEVICE_NAME "\\Device\\MMAux" +#define AUX_DEVICE_NAME_U L"\\Device\\MMAux" + +/* +#define IOCTL_SOUND_BASE FILE_DEVICE_SOUND +#define IOCTL_WAVE_BASE 0x0000 +#define IOCTL_MIDI_BASE 0x0080 + +// Wave device driver IOCTLs + +#define IOCTL_WAVE_QUERY_FORMAT CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0001, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_WAVE_SET_FORMAT CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0002, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_WAVE_GET_CAPABILITIES CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0003, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_WAVE_SET_STATE CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0004, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_WAVE_GET_STATE CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0005, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_WAVE_GET_POSITION CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0006, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_WAVE_SET_VOLUME CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0007, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_WAVE_GET_VOLUME CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0008, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_WAVE_SET_PITCH CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0009, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_WAVE_GET_PITCH CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000A, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_WAVE_SET_PLAYBACK_RATE CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000B, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_WAVE_GET_PLAYBACK_RATE CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000C, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_WAVE_PLAY CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000D, METHOD_IN_DIRECT, FILE_WRITE_ACCESS) +#define IOCTL_WAVE_RECORD CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000E, METHOD_OUT_DIRECT, FILE_WRITE_ACCESS) +#define IOCTL_WAVE_BREAK_LOOP CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x000F, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_WAVE_SET_LOW_PRIORITY CTL_CODE(IOCTL_SOUND_BASE, IOCTL_WAVE_BASE + 0x0010, METHOD_BUFFERED, FILE_WRITE_ACCESS) + +// MIDI device driver IOCTLs + +#define IOCTL_MIDI_GET_CAPABILITIES CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0001, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_MIDI_SET_STATE CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0002, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_MIDI_GET_STATE CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0003, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_MIDI_SET_VOLUME CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0004, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_MIDI_GET_VOLUME CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0005, METHOD_BUFFERED, FILE_READ_ACCESS) +#define IOCTL_MIDI_PLAY CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0006, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_MIDI_RECORD CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0007, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_MIDI_CACHE_PATCHES CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0008, METHOD_BUFFERED, FILE_WRITE_ACCESS) +#define IOCTL_MIDI_CACHE_DRUM_PATCHES CTL_CODE(IOCTL_SOUND_BASE, IOCTL_MIDI_BASE + 0x0009, METHOD_BUFFERED, FILE_WRITE_ACCESS) +*/ + + +CRITICAL_SECTION CS; // Serialize access to device lists + +HANDLE Heap; + + enum { + InvalidDevice, + WaveInDevice, + WaveOutDevice, + MidiInDevice, + MidiOutDevice, + AuxDevice +}; + +MMRESULT OpenDevice(UINT DeviceType, DWORD ID, PHANDLE pDeviceHandle, + DWORD Access); + +MMRESULT FindDevices(); + +DWORD GetDeviceCount(UINT DeviceType); + +DWORD TranslateStatus(void); + + +#endif diff --git a/reactos/dll/mmdrv/mmdrv.xml b/reactos/dll/mmdrv/mmdrv.xml new file mode 100644 index 00000000000..f41b2642da5 --- /dev/null +++ b/reactos/dll/mmdrv/mmdrv.xml @@ -0,0 +1,15 @@ + + + . + + + + ntdll + kernel32 + winmm + auxil.c + entry.c + midi.c + utils.c + wave.c + diff --git a/reactos/dll/mmdrv/utils.c b/reactos/dll/mmdrv/utils.c new file mode 100644 index 00000000000..fbe81061389 --- /dev/null +++ b/reactos/dll/mmdrv/utils.c @@ -0,0 +1,230 @@ +/* + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Multimedia + * FILE: lib/mmdrv/utils.c + * PURPOSE: Multimedia User Mode Driver (utility functions) + * PROGRAMMER: Andrew Greenwood + * UPDATE HISTORY: + * Jan 30, 2004: Imported into ReactOS tree + */ + +#include "mmdrv.h" + +#define NDEBUG +#include + +typedef struct _DEVICE_LIST +{ + struct _DEVICE_LIST *Next; + DWORD DeviceType; + ULONG CardIndex; + PVOID DeviceInstanceData; + ULONG DeviceInstanceDataSize; + WCHAR Name[1]; +} DEVICE_LIST, *PDEVICE_LIST; + +PDEVICE_LIST DeviceList; + + +DWORD TranslateStatus(void) +{ + switch(GetLastError()) + { + case NO_ERROR : + case ERROR_IO_PENDING : + return MMSYSERR_NOERROR; + + case ERROR_BUSY : + return MMSYSERR_ALLOCATED; + + case ERROR_NOT_SUPPORTED : + case ERROR_INVALID_FUNCTION : + return MMSYSERR_NOTSUPPORTED; + + case ERROR_NOT_ENOUGH_MEMORY : + return MMSYSERR_NOMEM; + + case ERROR_ACCESS_DENIED : + return MMSYSERR_BADDEVICEID; + + case ERROR_INSUFFICIENT_BUFFER : + return MMSYSERR_INVALPARAM; + + default : + return MMSYSERR_ERROR; + }; +} + + + +MMRESULT OpenDevice(UINT DeviceType, DWORD ID, PHANDLE pDeviceHandle, + DWORD Access) +{ + DPRINT("OpenDevice()\n"); + WCHAR DeviceName[SOUND_MAX_DEVICE_NAME]; + *pDeviceHandle = INVALID_HANDLE_VALUE; + + if (ID > SOUND_MAX_DEVICES) + return MMSYSERR_BADDEVICEID; + + switch(DeviceType) + { + case WaveOutDevice : + wsprintf(DeviceName, L"\\\\.%ls%d", WAVE_OUT_DEVICE_NAME_U + strlen("\\Device"), ID); + break; + case WaveInDevice : + wsprintf(DeviceName, L"\\\\.%ls%d", WAVE_IN_DEVICE_NAME_U + strlen("\\Device"), ID); + break; + case MidiOutDevice : + wsprintf(DeviceName, L"\\\\.%ls%d", MIDI_OUT_DEVICE_NAME_U + strlen("\\Device"), ID); + break; + case MidiInDevice : + wsprintf(DeviceName, L"\\\\.%ls%d", MIDI_IN_DEVICE_NAME_U + strlen("\\Device"), ID); + break; + case AuxDevice : + wsprintf(DeviceName, L"\\\\.%ls%d", AUX_DEVICE_NAME_U + strlen("\\Device"), ID); + break; + default : + DPRINT("No Auido Device Found"); + return MMSYSERR_BADDEVICEID; /* Maybe we should change error code */ + }; + + DPRINT("Attempting to open %S\n", DeviceName); + + *pDeviceHandle = CreateFile(DeviceName, Access, FILE_SHARE_WRITE, NULL, + OPEN_EXISTING, Access != GENERIC_READ ? FILE_FLAG_OVERLAPPED : 0, + NULL); + + DPRINT("DeviceHandle == 0x%x\n", (int)*pDeviceHandle); + + if (pDeviceHandle == INVALID_HANDLE_VALUE) + return TranslateStatus(); + + return MMSYSERR_NOERROR; +} + + +// DEVICE LIST MANAGEMENT + + +BOOL AddDeviceToList(PDEVICE_LIST* pList, DWORD DeviceType, DWORD CardIndex, + LPWSTR Name) +{ + PDEVICE_LIST pNewDevice; + + DPRINT("AddDeviceToList()\n"); + + pNewDevice = (PDEVICE_LIST) HeapAlloc(Heap, 0, + sizeof(DEVICE_LIST) + lstrlen(Name) * sizeof(WCHAR)); + + if ( !pNewDevice ) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return FALSE; + } + + pNewDevice->DeviceType = DeviceType; + pNewDevice->CardIndex = CardIndex; + lstrcpy(pNewDevice->Name, Name); + pNewDevice->DeviceInstanceData = NULL; + pNewDevice->Next = *pList; + *pList = pNewDevice; + + DPRINT("Success!\n"); + + return TRUE; +} + + +VOID FreeDeviceList() +{ + PDEVICE_LIST pDevice; + + DPRINT("FreeDeviceList()\n"); + + while (DeviceList) + { + pDevice = DeviceList; + DeviceList = pDevice->Next; + + if (pDevice->DeviceInstanceData) + HeapFree(Heap, 0, (LPVOID)pDevice->DeviceInstanceData); + + HeapFree(Heap, 0, (LPSTR)pDevice); + } +} + + +MMRESULT FindDevices() +{ +// DWORD Index; +// HKEY DriverKey; + + DPRINT("Finding devices\n"); + +// DriverKey = OpenParametersKey(); +// see drvutil.c of MS DDK for how this SHOULD be done... + + + SHORT i; + HANDLE h; + WCHAR DeviceName[SOUND_MAX_DEVICE_NAME]; + + for (i=0; OpenDevice(WaveOutDevice, i, &h, GENERIC_READ) == MMSYSERR_NOERROR; i++) + { + wsprintf(DeviceName, L"WaveOut%d\0", i); + CloseHandle(h); + AddDeviceToList(&DeviceList, WaveOutDevice, 0, DeviceName); + } + + for (i=0; OpenDevice(WaveInDevice, i, &h, GENERIC_READ) == MMSYSERR_NOERROR; i++) + { + wsprintf(DeviceName, L"WaveIn%d\0", i); + CloseHandle(h); + AddDeviceToList(&DeviceList, WaveInDevice, 0, DeviceName); + } + + for (i=0; OpenDevice(MidiOutDevice, i, &h, GENERIC_READ) == MMSYSERR_NOERROR; i++) + { + wsprintf(DeviceName, L"MidiOut%d\0", i); + CloseHandle(h); + AddDeviceToList(&DeviceList, MidiOutDevice, 0, DeviceName); + } + + for (i=0; OpenDevice(MidiInDevice, i, &h, GENERIC_READ) == MMSYSERR_NOERROR; i++) + { + wsprintf(DeviceName, L"MidiIn%d\0", i); + CloseHandle(h); + AddDeviceToList(&DeviceList, MidiInDevice, 0, DeviceName); + } + + for (i=0; OpenDevice(AuxDevice, i, &h, GENERIC_READ) == MMSYSERR_NOERROR; i++) + { + wsprintf(DeviceName, L"Aux%d\0", i); + CloseHandle(h); + AddDeviceToList(&DeviceList, AuxDevice, 0, DeviceName); + } + + + // MIDI Out 0: MPU-401 UART + // AddDeviceToList(&DeviceList, MidiOutDevice, 0, L"MidiOut0"); + // Wave Out 0: Sound Blaster 16 (ok?) + // AddDeviceToList(&DeviceList, WaveOutDevice, 0, L"WaveOut0"); + + return MMSYSERR_NOERROR; // ok? +} + + + +DWORD GetDeviceCount(UINT DeviceType) +{ + int i; + PDEVICE_LIST List; + + for (List = DeviceList, i = 0; List != NULL; List = List->Next) + if (List->DeviceType == DeviceType) + i ++; + + return i; +} diff --git a/reactos/dll/mmdrv/wave.c b/reactos/dll/mmdrv/wave.c new file mode 100644 index 00000000000..fd0292fdd15 --- /dev/null +++ b/reactos/dll/mmdrv/wave.c @@ -0,0 +1,1048 @@ +/* + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Multimedia + * FILE: lib/mmdrv/wave.c + * PURPOSE: Multimedia User Mode Driver + * PROGRAMMER: Andrew Greenwood + * Aleksey Bragin (aleksey at studiocerebral.com) + * UPDATE HISTORY: + * Jan 30, 2004: Imported into ReactOS tree (Greenwood) + * Mar 16, 2004: Implemented some funcs (Bragin) + */ + +#include "mmdrv.h" +#include "wave.h" + +#define NDEBUG +#include + +#define WHDR_COMPLETE 0x80000000 +#define MAX_BUFFER_SIZE 8192 +#define MAX_WAVE_BYTES 5*MAX_BUFFER_SIZE + +PWAVEALLOC WaveLists; + +static MMRESULT waveReadWrite(PWAVEALLOC pClient); +void wavePartialOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped); +void waveOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped); +void waveLoopOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped); +void waveBreakOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped); +static MMRESULT waveSetState(PWAVEALLOC pClient, ULONG State); + +/* ============================ + * INTERNAL + * functions start here + * ============================ + */ + +MMRESULT GetDeviceCapabilities(DWORD ID, UINT DeviceType, + LPBYTE pCaps, DWORD Size) +{ + HANDLE DeviceHandle = NULL; + MMRESULT Result = MMSYSERR_NOERROR; + DWORD BytesReturned = 0; + + // Open the wave device + + Result = OpenDevice(DeviceType, ID, &DeviceHandle, GENERIC_READ); + if (Result != MMSYSERR_NOERROR) + return Result; + + if ((DeviceType == WaveOutDevice) || (DeviceType == WaveInDevice)) + { + Result = DeviceIoControl(DeviceHandle, IOCTL_WAVE_GET_CAPABILITIES, + NULL, 0, (LPVOID)pCaps, Size, + &BytesReturned, NULL) ? MMSYSERR_NOERROR : TranslateStatus(); + } + + else if ((DeviceType == MidiInDevice) || (DeviceType == MidiOutDevice)) + { + Result = DeviceIoControl(DeviceHandle, IOCTL_MIDI_GET_CAPABILITIES, + NULL, 0, (LPVOID)pCaps, Size, + &BytesReturned, NULL) ? MMSYSERR_NOERROR : TranslateStatus(); + } + + else if (DeviceType == AuxDevice) + { + Result = DeviceIoControl(DeviceHandle, IOCTL_AUX_GET_CAPABILITIES, + NULL, 0, (LPVOID)pCaps, Size, + &BytesReturned, NULL) ? MMSYSERR_NOERROR : TranslateStatus(); + } + + // Close the handle and return the result code + CloseHandle(DeviceHandle); + + return Result; +} + +static DWORD waveThread(LPVOID lpParameter) +{ + + PWAVEALLOC pClient = (PWAVEALLOC)lpParameter; + BOOL Terminate = FALSE; + + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL); + SetEvent(pClient->AuxEvent2); + WaitForSingleObject(pClient->AuxEvent1, INFINITE); + + for (;;) + { + switch (pClient->AuxFunction) + { + case WaveThreadAddBuffer: + { + LPWAVEHDR *pHdrSearching; + + if (pClient->DeviceType == WaveInDevice) + pClient->AuxParam.pHdr->dwBytesRecorded = 0; + + pHdrSearching = &pClient->DeviceQueue; + pClient->AuxParam.pHdr->lpNext = NULL; + + while (*pHdrSearching) + { + pHdrSearching = &(*pHdrSearching)->lpNext; + } + + if (pClient->NextBuffer == NULL) + { + pClient->BufferPosition = 0; + pClient->NextBuffer = pClient->AuxParam.pHdr; + + } + + *pHdrSearching = pClient->AuxParam.pHdr; + + pClient->AuxReturnCode = waveReadWrite(pClient); + } + break; + + case WaveThreadSetState: + pClient->AuxReturnCode = waveSetState(pClient, pClient->AuxParam.State); + + if (pClient->AuxParam.State == WAVE_DD_RESET) + { + PWAVEHDR pHdr; + + pClient->LoopHead = NULL; + pClient->AuxReturnCode = MMSYSERR_NOERROR; + for (pHdr = pClient->DeviceQueue; pHdr != NULL; pHdr = pHdr->lpNext) + { + pHdr->dwFlags |= WHDR_COMPLETE; + } + + pClient->BufferPosition = 0; + pClient->NextBuffer = NULL; + } + else + { + if (pClient->DeviceType == WaveInDevice && pClient->AuxReturnCode == MMSYSERR_NOERROR) + { + if (pClient->AuxParam.State == WAVE_DD_STOP) + { + if (pClient->DeviceQueue) + { + while (!(pClient->DeviceQueue->dwFlags & WHDR_COMPLETE) && + pClient->BytesOutstanding != 0) + { + waveSetState(pClient, WAVE_DD_RECORD); + pClient->AuxReturnCode = waveSetState(pClient, WAVE_DD_STOP); + if (pClient->AuxReturnCode != MMSYSERR_NOERROR) + break; + + } + if (pClient->AuxReturnCode == MMSYSERR_NOERROR) + { + pClient->DeviceQueue->dwFlags |= WHDR_COMPLETE; + if (pClient->NextBuffer == pClient->DeviceQueue) + { + pClient->NextBuffer = pClient->DeviceQueue->lpNext; + pClient->BufferPosition = 0; + } + } + } + } + else + { + if (pClient->AuxParam.State == WAVE_DD_RECORD) + pClient->AuxReturnCode = waveReadWrite(pClient); + } + } + } + + break; + + case WaveThreadGetData: + { + OVERLAPPED Overlap; + DWORD BytesReturned; + + // FIXME + // Assert(hDev != NULL); + + memset(&Overlap, 0, sizeof(Overlap)); + + Overlap.hEvent = pClient->Event; + + if (!DeviceIoControl(pClient->hDev, pClient->AuxParam.GetSetData.Function, NULL, 0, + pClient->AuxParam.GetSetData.pData, pClient->AuxParam.GetSetData.DataLen, + &BytesReturned, &Overlap)) + { + DWORD cbTransfer; + + if (GetLastError() != ERROR_IO_PENDING) + pClient->AuxReturnCode = TranslateStatus(); + else + { + + if (!GetOverlappedResult(pClient->hDev, &Overlap, &cbTransfer, TRUE)) + pClient->AuxReturnCode = TranslateStatus(); + } + } + else + { + while (SetEvent(pClient->Event) && WaitForSingleObjectEx(pClient->Event, 0, TRUE) == + WAIT_IO_COMPLETION) {} + + pClient->AuxReturnCode = MMSYSERR_NOERROR; + } + } + break; + + case WaveThreadSetData: + { + OVERLAPPED Overlap; + DWORD BytesReturned; + memset((PVOID)&Overlap, 0, sizeof(Overlap)); + Overlap.hEvent = pClient->Event; + + if (!DeviceIoControl(pClient->hDev, pClient->AuxParam.GetSetData.Function, + pClient->AuxParam.GetSetData.pData, pClient->AuxParam.GetSetData.DataLen, + NULL, 0, &BytesReturned, &Overlap)) + { + DWORD cbTransfer; + if (GetLastError() == ERROR_IO_PENDING) + { + if (!GetOverlappedResult(pClient->hDev, &Overlap, &cbTransfer, TRUE)) + pClient->AuxReturnCode = TranslateStatus(); + } + else + pClient->AuxReturnCode = TranslateStatus(); + + } + else + { + while (SleepEx(0, TRUE) == WAIT_IO_COMPLETION) {} + + pClient->AuxReturnCode = MMSYSERR_NOERROR; + } + } + break; + + case WaveThreadBreakLoop: + pClient->AuxReturnCode = MMSYSERR_NOERROR; + if (pClient->LoopHead) + pClient->LoopCount = 0; + break; + + case WaveThreadClose: + if (pClient->DeviceQueue != NULL) + pClient->AuxReturnCode = WAVERR_STILLPLAYING; + else + pClient->AuxReturnCode = MMSYSERR_NOERROR; + break; + + case WaveThreadTerminate: + Terminate = TRUE; + break; + + default: + DPRINT("WaveThread Error"); + break; + + } + + pClient->AuxFunction = WaveThreadInvalid; + + while (pClient->DeviceQueue && (pClient->DeviceQueue->dwFlags & WHDR_COMPLETE)) + { + PWAVEHDR pHdr; + PWAVEALLOC pWav; + + pHdr = pClient->DeviceQueue; + pClient->DeviceQueue = pHdr->lpNext; + + pHdr->dwFlags &= ~WHDR_COMPLETE; + pHdr->dwFlags &= ~WHDR_INQUEUE; + pHdr->lpNext = NULL; + pHdr->dwFlags |= WHDR_DONE; + + pWav = (PWAVEALLOC)pHdr->reserved; + + if (pWav->dwCallback) + { + DriverCallback(pWav->dwCallback, HIWORD(pWav->dwFlags), (HDRVR)pWav->hWave, + pClient->DeviceType == WaveOutDevice ? WOM_DONE : WIM_DATA, + pWav->dwInstance, (DWORD)pHdr, 0L); + } + } + + waveReadWrite(pClient); + + if (Terminate) return 1; + SetEvent(pClient->AuxEvent2); + while (WaitForSingleObjectEx(pClient->AuxEvent1, INFINITE, TRUE) == WAIT_IO_COMPLETION) + { + while (pClient->DeviceQueue && (pClient->DeviceQueue->dwFlags & WHDR_COMPLETE)) + { + PWAVEHDR pHdr; + PWAVEALLOC pWav; + + pHdr = pClient->DeviceQueue; + pClient->DeviceQueue = pHdr->lpNext; + + pHdr->dwFlags &= ~WHDR_COMPLETE; + pHdr->dwFlags &= ~WHDR_INQUEUE; + pHdr->lpNext = NULL; + pHdr->dwFlags |= WHDR_DONE; + + pWav = (PWAVEALLOC)pHdr->reserved; + + if (pWav->dwCallback) + { + DriverCallback(pWav->dwCallback, HIWORD(pWav->dwFlags), (HDRVR)pWav->hWave, + pClient->DeviceType == WaveOutDevice ? WOM_DONE : WIM_DATA, + pWav->dwInstance, (DWORD)pHdr, 0L); + } + } + + waveReadWrite(pClient); + } + } + + + return MMSYSERR_NOERROR; +} + + +static MMRESULT waveReadWrite(PWAVEALLOC pClient) +{ + DWORD dwSize; + BOOL Result = FALSE; + + + while (pClient->NextBuffer) + { + PWAVEHDR pHdr; + + pHdr = pClient->NextBuffer; + + //FIXME + //assert(!(pHdr->dwFlags & (WHDR_DONE | WHDR_COMPLETE))); + //assert(pClient->DeviceQueue != NULL); + + + dwSize = pHdr->dwBufferLength - pClient->BufferPosition; + if (dwSize > MAX_BUFFER_SIZE) + dwSize = MAX_BUFFER_SIZE; + + + if (dwSize + pClient->BytesOutstanding <= MAX_WAVE_BYTES) + { + PWAVEOVL pWaveOvl; + + if (pClient->BufferPosition == 0) + { + if (pClient->NextBuffer && (pClient->NextBuffer->dwFlags & WHDR_BEGINLOOP) && + pClient->NextBuffer != pClient->LoopHead) + { + pClient->LoopCount = pClient->NextBuffer->dwLoops; + pClient->LoopHead = pClient->NextBuffer; + if (pClient->LoopCount > 0) + pClient->LoopCount--; + } + + if (pClient->LoopCount == 0) + pClient->LoopHead = NULL; + } + + pWaveOvl = (PWAVEOVL)HeapAlloc(Heap, HEAP_ZERO_MEMORY, sizeof(*pWaveOvl)); + + if (pWaveOvl == NULL) + return MMSYSERR_NOMEM; + + pWaveOvl->WaveHdr = pHdr; + + if (pClient->DeviceType == WaveOutDevice) + { + Result = WriteFileEx(pClient->hDev, + (PBYTE)pHdr->lpData + pClient->BufferPosition, + dwSize, + (LPOVERLAPPED)pWaveOvl, + (LPOVERLAPPED_COMPLETION_ROUTINE) + (pHdr->dwBufferLength != + pClient->BufferPosition + dwSize ? wavePartialOvl : NULL != pClient->LoopHead ? + waveLoopOvl : waveOvl)); + } + else if (pClient->DeviceType == WaveInDevice) + { + Result = ReadFileEx(pClient->hDev, (PBYTE)pHdr->lpData + pClient->BufferPosition, + dwSize, (LPOVERLAPPED)pWaveOvl, + (LPOVERLAPPED_COMPLETION_ROUTINE) + (pHdr->dwBufferLength != + pClient->BufferPosition + dwSize ? wavePartialOvl : NULL != pClient->LoopHead ? + waveLoopOvl : waveOvl)); + } + + + if (!Result && GetLastError() != ERROR_IO_PENDING) + { + HeapFree(Heap, 0, (LPSTR)pWaveOvl); + + if (pClient->BytesOutstanding == 0) + { + PWAVEHDR pHdr; + for (pHdr = pClient->DeviceQueue; pHdr != NULL; pHdr = pHdr->lpNext) + { + pHdr->dwFlags |= WHDR_COMPLETE; + } + + pClient->NextBuffer = NULL; + pClient->BufferPosition = 0; + + } + return TranslateStatus(); + + } + else + { + pClient->BufferPosition += dwSize; + pClient->BytesOutstanding += dwSize; + if (pClient->BufferPosition == pHdr->dwBufferLength) + { + + if (!pClient->LoopHead || !(pHdr->dwFlags & WHDR_ENDLOOP)) + pClient->NextBuffer = pHdr->lpNext; + else + { + if (pClient->LoopCount != 0) + { + pClient->NextBuffer = pClient->LoopHead; + pClient->LoopCount--; + } + else + { + pClient->DummyWaveOvl.WaveHdr = pClient->LoopHead; + + Result = WriteFileEx(pClient->hDev, (PVOID)pHdr->lpData, 0, + &pClient->DummyWaveOvl.Ovl, + (LPOVERLAPPED_COMPLETION_ROUTINE)waveBreakOvl); + + if (Result || GetLastError() == ERROR_IO_PENDING) + { + pClient->NextBuffer = pHdr->lpNext; + pClient->LoopHead = NULL; + + } + } + } + pClient->BufferPosition = 0; + } + } + + + } + else + break; + } + return MMSYSERR_NOERROR; +} +static MMRESULT waveSetState(PWAVEALLOC pClient, ULONG State) +{ + OVERLAPPED Overlap; + DWORD BytesReturned; + + memset((PVOID)&Overlap, 0, sizeof(Overlap)); + + Overlap.hEvent = pClient->Event; + + if (!DeviceIoControl(pClient->hDev, IOCTL_WAVE_SET_STATE, + &State, sizeof(State), NULL, 0, &BytesReturned, &Overlap)) + { + DWORD cbTransfer; + if (GetLastError() == ERROR_IO_PENDING) + { + if (!GetOverlappedResult(pClient->hDev, &Overlap, &cbTransfer, TRUE)) + return TranslateStatus(); + } + else + return TranslateStatus(); + + } + + while (SleepEx(0, TRUE) == WAIT_IO_COMPLETION) {} + return MMSYSERR_NOERROR; +} + +void wavePartialOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped) +{ + LPWAVEHDR pHdr; + PWAVEALLOC pClient; + + pHdr = ((PWAVEOVL)pOverlapped)->WaveHdr; + pClient = (PWAVEALLOC)pHdr->reserved; + + + /* FIXME + Assert(pHdr->dwFlags & WHDR_INQUEUE); + Assert(!(pHdr->dwFlags & WHDR_COMPLETE)); + */ + + pClient->BytesOutstanding -= MAX_BUFFER_SIZE; + + if (pClient->DeviceType == WaveInDevice) + pHdr->dwBytesRecorded += BytesTransferred; + HeapFree(Heap, 0, (LPSTR)pOverlapped); +} + +void waveBreakOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped) +{ + ((PWAVEOVL)pOverlapped)->WaveHdr->dwFlags |= WHDR_COMPLETE; +} + +void waveLoopOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped) +{ + DWORD dwFlags; + PWAVEHDR pHdr; + + pHdr = ((PWAVEOVL)pOverlapped)->WaveHdr; + dwFlags = pHdr->dwFlags; + waveOvl(dwErrorCode, BytesTransferred, pOverlapped); + pHdr->dwFlags = dwFlags; +} + +void waveOvl(DWORD dwErrorCode, DWORD BytesTransferred, LPOVERLAPPED pOverlapped) +{ + PWAVEHDR pHdr; + PWAVEALLOC pClient; + + pHdr = ((PWAVEOVL)pOverlapped)->WaveHdr; + pClient = (PWAVEALLOC)pHdr->reserved; + + /* FIXME + Assert(pHdr->dwFlags & WHDR_INQUEUE); + Assert(!(pHdr->dwFlags & WHDR_COMPLETE)); + */ + + pHdr->dwFlags |= WHDR_COMPLETE; + + if (pHdr->dwFlags & WHDR_BEGINLOOP) + { + PWAVEHDR pHdrSearch; + for (pHdrSearch = pClient->DeviceQueue ; pHdrSearch != pHdr ; pHdrSearch = pHdrSearch->lpNext) + { + //Assert(pHdrSearch != NULL); + pHdrSearch->dwFlags |= WHDR_COMPLETE; + } + } + + if (pHdr->dwBufferLength) + pClient->BytesOutstanding -= (pHdr->dwBufferLength - 1) % MAX_BUFFER_SIZE + 1; + + if (pClient->DeviceType == WaveInDevice) + pHdr->dwBytesRecorded += BytesTransferred; + + HeapFree(Heap, 0, (LPSTR)pOverlapped); + +} + + + + + +static MMRESULT OpenWaveDevice(UINT DeviceType, + DWORD id, + DWORD dwUser, + DWORD dwParam1, + DWORD dwParam2) +{ + // TODO: Implement + PWAVEALLOC pClient = (PWAVEALLOC)dwUser; + MMRESULT mResult; + BOOL Result; + DWORD BytesReturned; + LPWAVEFORMATEX pFormats; + PWAVEALLOC *pUserHandle = NULL; + HANDLE hDevice; + + pFormats = (LPWAVEFORMATEX)((LPWAVEOPENDESC)dwParam1)->lpFormat; + + if (dwParam2 & WAVE_FORMAT_QUERY) + { + mResult = OpenDevice(DeviceType, id, &hDevice, GENERIC_READ); + if (mResult != MMSYSERR_NOERROR) + return mResult; + + Result = DeviceIoControl(hDevice, IOCTL_WAVE_QUERY_FORMAT, (PVOID)pFormats, + pFormats->wFormatTag == WAVE_FORMAT_PCM ? + sizeof(PCMWAVEFORMAT) : sizeof(WAVEFORMATEX) + pFormats->cbSize, + NULL, 0, &BytesReturned, NULL); + CloseHandle(hDevice); + return Result ? MMSYSERR_NOERROR : GetLastError() == ERROR_NOT_SUPPORTED ? WAVERR_BADFORMAT : TranslateStatus(); + } + + EnterCriticalSection(&CS); + + for (pClient = WaveLists; pClient != NULL; pClient = pClient->Next) + { + if (pClient->DeviceNumber == id && pClient->DeviceType == DeviceType) + { + if (pClient->hDev != INVALID_HANDLE_VALUE) + { + LeaveCriticalSection(&CS); + return MMSYSERR_ALLOCATED; + } + break; + } + } + + if (pClient == NULL) + { + pClient = (PWAVEALLOC)HeapAlloc(Heap, HEAP_ZERO_MEMORY, sizeof(WAVEALLOC)); + if (pClient == NULL) + { + LeaveCriticalSection(&CS); + return MMSYSERR_NOMEM; + } + + pClient->DeviceNumber = id; + pClient->Next = WaveLists; + pClient->DeviceType = DeviceType; + + WaveLists = pClient; + } + + pClient->hWave = ((LPWAVEOPENDESC)dwParam1)->hWave; + pClient->dwInstance = ((LPWAVEOPENDESC)dwParam1)->dwInstance; + pClient->dwFlags = dwParam2; + pClient->dwCallback = ((LPWAVEOPENDESC)dwParam1)->dwCallback; + pClient->hDev = INVALID_HANDLE_VALUE; + pClient->NextBuffer = NULL; + pClient->DeviceQueue = NULL; + pClient->LoopHead = NULL; + pClient->LoopCount = 0; + pClient->BytesOutstanding = 0; + pClient->BufferPosition = 0; + + + + + mResult = OpenDevice(DeviceType, id, &pClient->hDev, (GENERIC_READ | GENERIC_WRITE)); + + if (mResult != MMSYSERR_NOERROR) + { + LeaveCriticalSection(&CS); + return mResult; + } + + if (!DeviceIoControl(pClient->hDev,IOCTL_WAVE_SET_FORMAT, (PVOID)pFormats, + pFormats->wFormatTag == WAVE_FORMAT_PCM ? + sizeof(PCMWAVEFORMAT) : sizeof(WAVEFORMATEX) + pFormats->cbSize, + NULL, 0, &BytesReturned, NULL)) + { + CloseHandle(pClient->hDev); + pClient->hDev = INVALID_HANDLE_VALUE; + LeaveCriticalSection(&CS); + return GetLastError() == ERROR_NOT_SUPPORTED ? WAVERR_BADFORMAT : TranslateStatus(); + } + + LeaveCriticalSection(&CS); + + if (!pClient->Event) + { + pClient->Event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (pClient->Event == NULL) + { + // Cleanup + return MMSYSERR_NOMEM; + } + + pClient->AuxEvent1 = CreateEvent(NULL, FALSE, FALSE, NULL); + if (!pClient->AuxEvent1) + { + // Cleanup + return MMSYSERR_NOMEM; + } + + pClient->AuxEvent2 = CreateEvent(NULL, FALSE, FALSE, NULL); + if (!pClient->AuxEvent2) + { + // Cleanup + return MMSYSERR_NOMEM; + } + + + mResult = mmTaskCreate((LPTASKCALLBACK)waveThread, &pClient->ThreadHandle, (DWORD)pClient); + if ( mResult != MMSYSERR_NOERROR) + { + // Cleanup + return MMSYSERR_NOMEM; + } + + WaitForSingleObject(pClient->AuxEvent2, INFINITE); + } + + *pUserHandle = pClient; + pUserHandle = (PWAVEALLOC *)dwUser; + + + if (pClient->dwCallback) + { + DriverCallback(pClient->dwCallback, HIWORD(pClient->dwFlags), + (HDRVR)pClient->hWave, DeviceType == WaveOutDevice ? WOM_OPEN : WIM_OPEN, + pClient->dwInstance, 0L, 0L); + } + + return MMSYSERR_NOERROR; +} + + +//FIXME: MS-specific code, except for name of the func! +MMRESULT GetPositionWaveDevice(PWAVEALLOC pClient, LPMMTIME lpmmt, DWORD dwSize) +{ + /* + WAVE_DD_POSITION PositionData; + MMRESULT mErr; + + if (dwSize < sizeof(MMTIME)) + return MMSYSERR_ERROR; + + // + // Get the current position from the driver + // + mErr = sndGetHandleData(pClient->hDev, + sizeof(PositionData), + &PositionData, + IOCTL_WAVE_GET_POSITION, + pClient->Event); + + if (mErr == MMSYSERR_NOERROR) { + if (lpmmt->wType == TIME_BYTES) { + lpmmt->u.cb = PositionData.ByteCount; + } + + // default is samples. + else { + lpmmt->wType = TIME_SAMPLES; + lpmmt->u.sample = PositionData.SampleCount; + } + } + + return mErr;*/ return MMSYSERR_NOERROR; +} + + + +MMRESULT soundSetData(UINT DeviceType, UINT DeviceId, UINT Length, PBYTE Data, + ULONG Ioctl) +{ + HANDLE hDevcie; + MMRESULT Result; + DWORD BytesReturned; + + Result = OpenDevice(DeviceType, DeviceId, &hDevcie, GENERIC_READ); + if (Result != MMSYSERR_NOERROR) + return Result; + + Result = DeviceIoControl(hDevcie, Ioctl, Data, Length, NULL, 0, &BytesReturned, NULL) ? + MMSYSERR_NOERROR : TranslateStatus(); + CloseHandle(hDevcie); + + return Result; +} + +MMRESULT soundGetData(UINT DeviceType, UINT DeviceId, UINT Length, PBYTE Data, + ULONG Ioctl) +{ + HANDLE hDevice; + MMRESULT Result; + DWORD BytesReturned; + + Result = OpenDevice(DeviceType, DeviceId, &hDevice, GENERIC_READ); + if (Result != MMSYSERR_NOERROR) + return Result; + + Result = DeviceIoControl(hDevice, Ioctl, NULL, 0, (LPVOID)Data, Length, + &BytesReturned, NULL) ? MMSYSERR_NOERROR : TranslateStatus(); + CloseHandle(hDevice); + return Result; +} + + +/* ============================ + * EXPORT + * functions start here + * ============================ + */ + +/* + * @implemented + */ +APIENTRY DWORD wodMessage(DWORD dwId, DWORD dwMessage, DWORD dwUser, DWORD dwParam1, DWORD dwParam2) +{ + PWAVEALLOC pTask = (PWAVEALLOC)dwUser; + + switch (dwMessage) { + case WODM_GETNUMDEVS: + DPRINT("WODM_GETNUMDEVS"); + return GetDeviceCount(WaveOutDevice); + + case WODM_GETDEVCAPS: + DPRINT("WODM_GETDEVCAPS"); + return GetDeviceCapabilities(dwId, WaveOutDevice, (LPBYTE)dwParam1, + (DWORD)dwParam2); + + case WODM_OPEN: + DPRINT("WODM_OPEN"); + return OpenWaveDevice(WaveOutDevice, dwId, dwUser, dwParam1, dwParam2); + + case WODM_CLOSE: + { + DPRINT("WODM_CLOSE"); + + // 1. Check if the task is ready to complete + pTask->AuxFunction = WaveThreadClose; + SetEvent(pTask->AuxEvent1); + WaitForSingleObject(pTask->AuxEvent2, INFINITE); + + if ( pTask->AuxReturnCode != MMSYSERR_NOERROR) + { + return pTask->AuxReturnCode; + } + else + + { + if (pTask->dwCallback) + { + DriverCallback(pTask->dwCallback, HIWORD(pTask->dwFlags), (HDRVR)pTask->hWave, + WOM_CLOSE, pTask->dwInstance, 0L, 0L); + } + } + + + // 2. Close the device + if (pTask->hDev != INVALID_HANDLE_VALUE) { + CloseHandle(pTask->hDev); + + EnterCriticalSection(&CS); + pTask->hDev = INVALID_HANDLE_VALUE; + LeaveCriticalSection(&CS); + } + + return MMSYSERR_NOERROR; + }; + + case WODM_WRITE: + { + LPWAVEHDR pWaveHdr = (LPWAVEHDR)dwParam1; + + DPRINT("WODM_WRITE"); + + if (dwParam1 != 0) + return MMSYSERR_INVALPARAM; + + if ((pWaveHdr->dwFlags & ~(WHDR_INQUEUE|WHDR_DONE|WHDR_PREPARED|WHDR_BEGINLOOP|WHDR_ENDLOOP))) + return MMSYSERR_INVALPARAM; + + pWaveHdr->dwFlags &= (WHDR_INQUEUE|WHDR_DONE|WHDR_PREPARED|WHDR_BEGINLOOP|WHDR_ENDLOOP); + + if ((pWaveHdr->dwFlags & WHDR_PREPARED) == 0) + return MMSYSERR_INVALPARAM; + + // Check, if the wave header is already prepared + if (!(pWaveHdr->dwFlags & WHDR_PREPARED)) + return WAVERR_UNPREPARED; + + // If it's already located in the queue, this op is impossible + if (pWaveHdr->dwFlags & WHDR_INQUEUE ) + return ( WAVERR_STILLPLAYING ); + + // save WAVEALLOC pointer in the WaveHeader + pWaveHdr->reserved = dwUser; + + + pWaveHdr->dwFlags |= WHDR_INQUEUE; + pWaveHdr->dwFlags &= ~WHDR_DONE; + pTask->AuxParam.pHdr = pWaveHdr; + + pTask->AuxFunction = WaveThreadAddBuffer; + SetEvent(pTask->AuxEvent1); + WaitForSingleObject(pTask->AuxEvent2, INFINITE); + return pTask->AuxReturnCode; + } + + case WODM_PAUSE: + DPRINT("WODM_PAUSE"); + pTask->AuxParam.State = WAVE_DD_STOP; + + pTask->AuxFunction = WaveThreadSetState; + SetEvent(pTask->AuxEvent1); + WaitForSingleObject(pTask->AuxEvent2, INFINITE); + return pTask->AuxReturnCode; + + case WODM_RESTART: + DPRINT("WODM_RESTART"); + pTask->AuxParam.State = WAVE_DD_PLAY; + + pTask->AuxFunction = WaveThreadSetState; + SetEvent(pTask->AuxEvent1); + WaitForSingleObject(pTask->AuxEvent2, INFINITE); + return pTask->AuxReturnCode; + + case WODM_RESET: + DPRINT("WODM_RESET"); + pTask->AuxParam.State = WAVE_DD_RESET; + + pTask->AuxFunction = WaveThreadSetState; + SetEvent(pTask->AuxEvent1); + WaitForSingleObject(pTask->AuxEvent2, INFINITE); + return pTask->AuxReturnCode; + + case WODM_BREAKLOOP: + DPRINT("WODM_BREAKLOOP"); + + pTask->AuxFunction = WaveThreadBreakLoop; + SetEvent(pTask->AuxEvent1); + WaitForSingleObject(pTask->AuxEvent2, INFINITE); + return pTask->AuxReturnCode; + + case WODM_GETPOS: + DPRINT("WODM_GETPOS"); + return GetPositionWaveDevice(pTask, (LPMMTIME)dwParam1, dwParam2); + + case WODM_SETPITCH: + DPRINT("WODM_SETPITCH"); + pTask->AuxParam.GetSetData.pData = (PBYTE)&dwParam1; + pTask->AuxParam.GetSetData.DataLen = sizeof(DWORD); + pTask->AuxParam.GetSetData.Function = IOCTL_WAVE_SET_PITCH; + + pTask->AuxFunction = WaveThreadSetData; + SetEvent(pTask->AuxEvent1); + WaitForSingleObject(pTask->AuxEvent2, INFINITE); + return pTask->AuxReturnCode; + + case WODM_SETVOLUME: + DPRINT("WODM_SETVOLUME"); + { + WAVE_DD_VOLUME Vol; + Vol.Left = LOWORD(dwParam1) << 16; + Vol.Right = HIWORD(dwParam1) << 16; + + return soundSetData(WaveOutDevice, dwId, sizeof(Vol), + (PBYTE)&Vol, IOCTL_WAVE_SET_VOLUME); + } + + case WODM_SETPLAYBACKRATE: + DPRINT("WODM_SETPLAYBACKRATE"); + pTask->AuxParam.GetSetData.pData = (PBYTE)&dwParam1; + pTask->AuxParam.GetSetData.DataLen = sizeof(DWORD); + pTask->AuxParam.GetSetData.Function = IOCTL_WAVE_SET_PLAYBACK_RATE; + + pTask->AuxFunction = WaveThreadSetData; + SetEvent(pTask->AuxEvent1); + WaitForSingleObject(pTask->AuxEvent2, INFINITE); + return pTask->AuxReturnCode; + + + case WODM_GETPITCH: + DPRINT("WODM_GETPITCH"); + pTask->AuxParam.GetSetData.pData = (PBYTE)dwParam1; + pTask->AuxParam.GetSetData.DataLen = sizeof(DWORD); + pTask->AuxParam.GetSetData.Function = IOCTL_WAVE_GET_PITCH; + + pTask->AuxFunction = WaveThreadGetData; + SetEvent(pTask->AuxEvent1); + WaitForSingleObject(pTask->AuxEvent2, INFINITE); + return pTask->AuxReturnCode; + + case WODM_GETVOLUME: + DPRINT("WODM_GETVOLUME"); + { + WAVE_DD_VOLUME Vol = {}; + DWORD res; + + res = soundGetData(WaveOutDevice, dwId, sizeof(Vol), + (PBYTE)&Vol, IOCTL_WAVE_GET_VOLUME); + + if (res == MMSYSERR_NOERROR) + *(LPDWORD)dwParam1 = (DWORD)MAKELONG(HIWORD(Vol.Left), HIWORD(Vol.Right)); + + return res; + } + + case WODM_GETPLAYBACKRATE: + DPRINT("WODM_GETPLAYBACKRATE"); + pTask->AuxParam.GetSetData.pData = (PBYTE)dwParam1; + pTask->AuxParam.GetSetData.DataLen = sizeof(DWORD); + pTask->AuxParam.GetSetData.Function = IOCTL_WAVE_GET_PLAYBACK_RATE; + + pTask->AuxFunction = WaveThreadGetData; + SetEvent(pTask->AuxEvent1); + WaitForSingleObject(pTask->AuxEvent2, INFINITE); + return pTask->AuxReturnCode; + + default: + return MMSYSERR_NOTSUPPORTED; + } + + // This point of execution should never be reached + return MMSYSERR_NOTSUPPORTED; +} + + +/* + * @implemented + */ +APIENTRY DWORD widMessage(DWORD dwId, DWORD dwMessage, DWORD dwUser, DWORD dwParam1, DWORD dwParam2) +{ + DPRINT("widMessage\n"); + + switch (dwMessage) + { + case WIDM_GETNUMDEVS: + DPRINT("WIDM_GETNUMDEVS"); + return GetDeviceCount(WaveInDevice); + + case WIDM_GETDEVCAPS: + DPRINT("WODM_GETDEVCAPS"); + return GetDeviceCapabilities(dwId, WaveInDevice, (LPBYTE)dwParam1, (DWORD)dwParam2); + + case WIDM_OPEN: + DPRINT("WIDM_OPEN"); + return OpenWaveDevice(WaveInDevice, dwId, dwUser, dwParam1, dwParam2); + + case WIDM_CLOSE: + return MMSYSERR_NOERROR; + + case WIDM_ADDBUFFER: + return MMSYSERR_NOERROR; + + case WIDM_STOP: + return MMSYSERR_NOERROR; + + case WIDM_START: + return MMSYSERR_NOERROR; + + case WIDM_RESET: + return MMSYSERR_NOERROR; + + case WIDM_GETPOS: + return MMSYSERR_NOERROR; + + + default : + return MMSYSERR_NOTSUPPORTED; + } +} + diff --git a/reactos/dll/mmdrv/wave.h b/reactos/dll/mmdrv/wave.h new file mode 100644 index 00000000000..42daed25a26 --- /dev/null +++ b/reactos/dll/mmdrv/wave.h @@ -0,0 +1,92 @@ + +// FIXME: Should be moved somewhere else? +typedef struct _WAVE_DD_VOLUME { + ULONG Left; + ULONG Right; +} WAVE_DD_VOLUME, *PWAVE_DD_VOLUME; + +// driver +#define WAVE_DD_STOP 0x0001 +#define WAVE_DD_PLAY 0x0002 // output devices only +#define WAVE_DD_RECORD 0x0003 // input devices only +#define WAVE_DD_RESET 0x0004 + +// ioctl +#define WAVE_DD_IDLE 0x0000 +#define WAVE_DD_STOPPED 0x0001 // stopped +#define WAVE_DD_PLAYING 0x0002 // output devices only +#define WAVE_DD_RECORDING 0x0003 // input devices only + + + +typedef enum { + WaveThreadInvalid, + WaveThreadAddBuffer, + WaveThreadSetState, + WaveThreadSetData, + WaveThreadGetData, + WaveThreadBreakLoop, + WaveThreadClose, + WaveThreadTerminate +} WAVETHREADFUNCTION; + +// WARNING: MS code below!! +typedef struct { + OVERLAPPED Ovl; + LPWAVEHDR WaveHdr; +} WAVEOVL, *PWAVEOVL; + +// WARNING: MS code below!! +// per allocation structure for wave +typedef struct tag_WAVEALLOC { + struct tag_WAVEALLOC *Next; // Chaining + UINT DeviceNumber; // Which device + UINT DeviceType; // WaveInput or WaveOutput + DWORD dwCallback; // client's callback + DWORD dwInstance; // client's instance data + DWORD dwFlags; // Open flags + HWAVE hWave; // handle for stream + + HANDLE hDev; // Wave device handle + LPWAVEHDR DeviceQueue; // Buffers queued by application + LPWAVEHDR NextBuffer; // Next buffer to send to device + DWORD BufferPosition; // How far we're into a large buffer + DWORD BytesOutstanding; + // Bytes being processed by device + LPWAVEHDR LoopHead; // Start of loop if any + DWORD LoopCount; // Number more loops to go + + WAVEOVL DummyWaveOvl; // For break loop + // + HANDLE Event; // Event for driver syncrhonization + // and notification of auxiliary + // task operation completion. + WAVETHREADFUNCTION AuxFunction; // Function for thread to perform + union { + LPWAVEHDR pHdr; // Buffer to pass in aux task + ULONG State; // State to set + struct { + ULONG Function; // IOCTL to use + PBYTE pData; // Data to set or get + ULONG DataLen; // Length of data + } GetSetData; + + } AuxParam; + // 0 means terminate task. + HANDLE AuxEvent1; // Aux thread waits on this + HANDLE AuxEvent2; // Caller of Aux thread waits on this + HANDLE ThreadHandle; // Handle for thread termination ONLY + MMRESULT AuxReturnCode; // Return code from Aux task +}WAVEALLOC, *PWAVEALLOC; + +/* Misc should move to own header */ +MMRESULT GetDeviceCapabilities(DWORD ID, UINT DeviceType, + LPBYTE pCaps, DWORD Size); + +DWORD AuxGetAudio(DWORD dwID, PBYTE pVolume, DWORD sizeVolume); +DWORD AuxSetAudio(DWORD dwID, PBYTE pVolume, DWORD sizeVolume); + +typedef struct _AUX_DD_VOLUME { + ULONG Left; + ULONG Right; +} AUX_DD_VOLUME, *PAUX_DD_VOLUME; diff --git a/reactos/dll/mpr/Makefile.in b/reactos/dll/mpr/Makefile.in new file mode 100644 index 00000000000..988a58b4f87 --- /dev/null +++ b/reactos/dll/mpr/Makefile.in @@ -0,0 +1,22 @@ +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = ../.. +SRCDIR = @srcdir@ +VPATH = @srcdir@ +MODULE = mpr.dll +IMPORTLIB = libmpr.$(IMPLIBEXT) +IMPORTS = user32 advapi32 kernel32 +EXTRALIBS = $(LIBUNICODE) + +C_SRCS = \ + auth.c \ + mpr_main.c \ + multinet.c \ + nps.c \ + pwcache.c \ + wnet.c + +RC_SRCS = mpr.rc + +@MAKE_DLL_RULES@ + +### Dependencies: diff --git a/reactos/dll/mpr/auth.c b/reactos/dll/mpr/auth.c new file mode 100644 index 00000000000..531ca8a60fe --- /dev/null +++ b/reactos/dll/mpr/auth.c @@ -0,0 +1,95 @@ +/* + * MPR Authentication and Logon functions + * + * Copyright 1999 Ulrich Weigand + * + * 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 + +#include "windef.h" +#include "winbase.h" +#include "winnetwk.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mpr); + + +/***************************************************************** + * WNetLogoffA [MPR.@] + */ +DWORD WINAPI WNetLogoffA( LPCSTR lpProvider, HWND hwndOwner ) +{ + FIXME( "(%s, %p): stub\n", debugstr_a(lpProvider), hwndOwner ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/***************************************************************** + * WNetLogoffW [MPR.@] + */ +DWORD WINAPI WNetLogoffW( LPCWSTR lpProvider, HWND hwndOwner ) +{ + FIXME( "(%s, %p): stub\n", debugstr_w(lpProvider), hwndOwner ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/***************************************************************** + * WNetLogonA [MPR.@] + */ +DWORD WINAPI WNetLogonA( LPCSTR lpProvider, HWND hwndOwner ) +{ + FIXME( "(%s, %p): stub\n", debugstr_a(lpProvider), hwndOwner ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/***************************************************************** + * WNetLogonW [MPR.@] + */ +DWORD WINAPI WNetLogonW( LPCWSTR lpProvider, HWND hwndOwner ) +{ + FIXME( "(%s, %p): stub\n", debugstr_w(lpProvider), hwndOwner ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/***************************************************************** + * WNetVerifyPasswordA [MPR.@] + */ +DWORD WINAPI WNetVerifyPasswordA( LPCSTR lpszPassword, BOOL *pfMatch ) +{ + FIXME( "(%p, %p): stub\n", lpszPassword, pfMatch ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/***************************************************************** + * WNetVerifyPasswordW [MPR.@] + */ +DWORD WINAPI WNetVerifyPasswordW( LPCWSTR lpszPassword, BOOL *pfMatch ) +{ + FIXME( "(%p, %p): stub\n", lpszPassword, pfMatch ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} diff --git a/reactos/dll/mpr/mpr.rc b/reactos/dll/mpr/mpr.rc new file mode 100644 index 00000000000..418d41d4e9a --- /dev/null +++ b/reactos/dll/mpr/mpr.rc @@ -0,0 +1,42 @@ +/* + * MPR dll resources + * + * Copyright (C) 2004 Juan Lang + * + * 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 "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "mprres.h" + +#include "version.rc" + +#include "mpr_Bg.rc" +#include "mpr_Cs.rc" +#include "mpr_De.rc" +#include "mpr_En.rc" +#include "mpr_Es.rc" +#include "mpr_Fr.rc" +#include "mpr_It.rc" +#include "mpr_Ja.rc" +#include "mpr_Ko.rc" +#include "mpr_Nl.rc" +#include "mpr_No.rc" +#include "mpr_Pt.rc" +#include "mpr_Ru.rc" +#include "mpr_Sv.rc" +#include "mpr_Hu.rc" +#include "mpr_Uk.rc" diff --git a/reactos/dll/mpr/mpr.spec b/reactos/dll/mpr/mpr.spec new file mode 100644 index 00000000000..c383c75966a --- /dev/null +++ b/reactos/dll/mpr/mpr.spec @@ -0,0 +1,114 @@ +# ordinal exports + 1 stub @ + 2 stub @ + 3 stub @ + 4 stub @ + 5 stub @ + 6 stub @ + 7 stub @ + 8 stub @ + 9 stub @ +12 stub @ +13 stub @ +14 stub @ +15 stub @ +16 stub @ +17 stub @ +18 stub @ +19 stub @ +20 stub @ +21 stub @ +22 stdcall @(long) MPR_Alloc +23 stdcall @(ptr long) MPR_ReAlloc +24 stdcall @(ptr) MPR_Free +25 stdcall @(ptr long) _MPR_25 + +@ stdcall -private DllCanUnloadNow() +@ stub DllGetClassObject +@ stdcall MultinetGetConnectionPerformanceA(ptr ptr) +@ stdcall MultinetGetConnectionPerformanceW(ptr ptr) +@ stdcall MultinetGetErrorTextA(long ptr long) +@ stdcall MultinetGetErrorTextW(long ptr long) +@ stdcall NPSAuthenticationDialogA(ptr) +@ stdcall NPSCopyStringA(str ptr ptr) +@ stdcall NPSDeviceGetNumberA(str ptr ptr) +@ stdcall NPSDeviceGetStringA(long long ptr long) +@ stdcall NPSGetProviderHandleA(ptr) +@ stdcall NPSGetProviderNameA(long ptr) +@ stdcall NPSGetSectionNameA(long ptr) +@ stdcall NPSNotifyGetContextA(ptr) +@ stdcall NPSNotifyRegisterA(long ptr) +@ stdcall NPSSetCustomTextA(str) +@ stdcall NPSSetExtendedErrorA(long str) +@ stub PwdChangePasswordA +@ stub PwdChangePasswordW +@ stub PwdGetPasswordStatusA +@ stub PwdGetPasswordStatusW +@ stub PwdSetPasswordStatusA +@ stub PwdSetPasswordStatusW +@ stdcall WNetAddConnection2A(ptr str str long) +@ stdcall WNetAddConnection2W(ptr wstr wstr long) +@ stdcall WNetAddConnection3A(long ptr str str long) +@ stdcall WNetAddConnection3W(long ptr wstr wstr long) +@ stdcall WNetAddConnectionA(str str str) +@ stdcall WNetAddConnectionW(wstr wstr wstr) +@ stdcall WNetCachePassword(str long str long long long) +@ stdcall WNetCancelConnection2A(str long long) +@ stdcall WNetCancelConnection2W(wstr long long) +@ stdcall WNetCancelConnectionA(str long) +@ stdcall WNetCancelConnectionW(wstr long) +@ stdcall WNetCloseEnum(long) +@ stdcall WNetConnectionDialog1A(ptr) +@ stdcall WNetConnectionDialog1W(ptr) +@ stdcall WNetConnectionDialog(long long) +@ stdcall WNetDisconnectDialog1A(ptr) +@ stdcall WNetDisconnectDialog1W(ptr) +@ stdcall WNetDisconnectDialog(long long) +@ stdcall WNetEnumCachedPasswords(str long long ptr long) +@ stdcall WNetEnumResourceA(long ptr ptr ptr) +@ stdcall WNetEnumResourceW(long ptr ptr ptr) +@ stub WNetFMXEditPerm +@ stub WNetFMXGetPermCaps +@ stub WNetFMXGetPermHelp +@ stub WNetFormatNetworkNameA +@ stub WNetFormatNetworkNameW +@ stdcall WNetGetCachedPassword(ptr long ptr ptr long) +@ stdcall WNetGetConnectionA(str ptr ptr) +@ stdcall WNetGetConnectionW(wstr ptr ptr) +@ stub WNetGetDirectoryTypeA +@ stub WNetGetHomeDirectoryA +@ stub WNetGetHomeDirectoryW +@ stdcall WNetGetLastErrorA(ptr ptr long ptr long) +@ stdcall WNetGetLastErrorW(ptr ptr long ptr long) +@ stdcall WNetGetNetworkInformationA(str ptr) +@ stdcall WNetGetNetworkInformationW(wstr ptr) +@ stub WNetGetPropertyTextA +@ stdcall WNetGetProviderNameA(long ptr ptr) +@ stdcall WNetGetProviderNameW(long ptr ptr) +@ stdcall WNetGetResourceInformationA(ptr ptr ptr ptr) +@ stdcall WNetGetResourceInformationW(ptr ptr ptr ptr) +@ stdcall WNetGetResourceParentA(ptr ptr ptr) +@ stdcall WNetGetResourceParentW(ptr ptr ptr) +@ stdcall WNetGetUniversalNameA (str long ptr ptr) +@ stdcall WNetGetUniversalNameW (wstr long ptr ptr) +@ stdcall WNetGetUserA(str ptr ptr) +@ stdcall WNetGetUserW(wstr wstr ptr) +@ stdcall WNetLogoffA(str long) +@ stdcall WNetLogoffW(wstr long) +@ stdcall WNetLogonA(str long) +@ stub WNetLogonNotify +@ stdcall WNetLogonW(wstr long) +@ stdcall WNetOpenEnumA(long long long ptr ptr) +@ stdcall WNetOpenEnumW(long long long ptr ptr) +@ stub WNetPasswordChangeNotify +@ stub WNetPropertyDialogA +@ stdcall WNetRemoveCachedPassword(long long long) +@ stub WNetRestoreConnection +@ stdcall WNetRestoreConnectionA(long str) +@ stdcall WNetRestoreConnectionW(long wstr) +@ stdcall WNetSetConnectionA(str long ptr) +@ stdcall WNetSetConnectionW(wstr long ptr) +@ stdcall WNetUseConnectionA(long ptr str str long str ptr ptr) +@ stdcall WNetUseConnectionW(long ptr wstr wstr long wstr ptr ptr) +@ stdcall WNetVerifyPasswordA(str ptr) +@ stdcall WNetVerifyPasswordW(wstr ptr) diff --git a/reactos/dll/mpr/mpr.xml b/reactos/dll/mpr/mpr.xml new file mode 100644 index 00000000000..62c87441039 --- /dev/null +++ b/reactos/dll/mpr/mpr.xml @@ -0,0 +1,25 @@ + + + . + include/wine + + + + + 0x600 + 0x501 + 0x501 + wine + ntdll + kernel32 + advapi32 + user32 + auth.c + mpr_main.c + multinet.c + nps.c + pwcache.c + wnet.c + mpr.rc + mpr.spec + diff --git a/reactos/dll/mpr/mpr_Bg.rc b/reactos/dll/mpr/mpr_Bg.rc new file mode 100644 index 00000000000..615e0938b85 --- /dev/null +++ b/reactos/dll/mpr/mpr_Bg.rc @@ -0,0 +1,46 @@ +/* + * MPR Bulgarian resource + * + * Copyright 2005 Milko Krachounov + * + * 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 + */ + +LANGUAGE LANG_BULGARIAN, SUBLANG_DEFAULT + +STRINGTABLE DISCARDABLE +{ + IDS_ENTIRENETWORK "Öÿëàòà ìðåæà" +} + +IDD_PROXYDLG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 250, 154 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Âúâåäåòå ìðåæîâà ïàðîëà" +FONT 8, "MS Shell Dlg" +{ + LTEXT "Âúâåäåòå âàøåòî ïîòðåáèòåëñêî èìå è ïàðîëà:", IDC_EXPLAIN, 40, 6, 150, 15 + LTEXT "Ïðîêñè", -1, 40, 26, 50, 10 +/* LTEXT "Realm", -1, 40, 46, 50, 10 */ + LTEXT "Ïîòðåáèòåë", -1, 40, 66, 50, 10 + LTEXT "Ïàðîëà", -1, 40, 86, 50, 10 + LTEXT "" IDC_PROXY, 80, 26, 150, 14, 0 + LTEXT "" IDC_REALM, 80, 46, 150, 14, 0 + EDITTEXT IDC_USERNAME, 80, 66, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP + EDITTEXT IDC_PASSWORD, 80, 86, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | ES_PASSWORD + CHECKBOX "&Save this password (Insecure)", IDC_SAVEPASSWORD, + 80, 106, 150, 12, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP + PUSHBUTTON "OK", IDOK, 98, 126, 56, 14, WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON + PUSHBUTTON "Îòìåíè", IDCANCEL, 158, 126, 56, 14, WS_GROUP | WS_TABSTOP +} diff --git a/reactos/dll/mpr/mpr_Cs.rc b/reactos/dll/mpr/mpr_Cs.rc new file mode 100644 index 00000000000..cda57e32d63 --- /dev/null +++ b/reactos/dll/mpr/mpr_Cs.rc @@ -0,0 +1,46 @@ +/* + * Czech MPR dll resources + * Copyright (C) 2004 Juan Lang + * Copyright (C) 2004, 2005 David Kredba + * + * 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 + */ + +LANGUAGE LANG_CZECH, SUBLANG_DEFAULT + +STRINGTABLE DISCARDABLE +{ + IDS_ENTIRENETWORK "Celá sí»" +} + +IDD_PROXYDLG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 250, 154 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Zadání sí»ového hesla" +FONT 8, "MS Shell Dlg" +{ + LTEXT "Prosím zadejte své u¾ivatelské jméno a heslo:", IDC_EXPLAIN, 40, 6, 150, 15 + LTEXT "Proxy", -1, 40, 26, 50, 10 +/* LTEXT "Realm", -1, 40, 46, 50, 10 */ + LTEXT "U¾ivatel", -1, 40, 66, 50, 10 + LTEXT "Heslo", -1, 40, 86, 50, 10 + LTEXT "" IDC_PROXY, 80, 26, 150, 14, 0 + LTEXT "" IDC_REALM, 80, 46, 150, 14, 0 + EDITTEXT IDC_USERNAME, 80, 66, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP + EDITTEXT IDC_PASSWORD, 80, 86, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | ES_PASSWORD + CHECKBOX "&Ulo¾it toto heslo (Není bezpeèné) ?", IDC_SAVEPASSWORD, + 80, 106, 150, 12, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP + PUSHBUTTON "OK", IDOK, 98, 126, 56, 14, WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON + PUSHBUTTON "Zru¹it", IDCANCEL, 158, 126, 56, 14, WS_GROUP | WS_TABSTOP +} diff --git a/reactos/dll/mpr/mpr_De.rc b/reactos/dll/mpr/mpr_De.rc new file mode 100644 index 00000000000..a91f7b33e92 --- /dev/null +++ b/reactos/dll/mpr/mpr_De.rc @@ -0,0 +1,46 @@ +/* + * MPR dll resources + * + * Copyright (C) 2004 Henning Gerhardt + * + * 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 + */ + +LANGUAGE LANG_GERMAN, SUBLANG_DEFAULT + +STRINGTABLE DISCARDABLE +{ + IDS_ENTIRENETWORK "Gesamtes Netzwerk" +} + +IDD_PROXYDLG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 250, 154 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Netzwerkkennung eingeben" +FONT 8, "MS Shell Dlg" +{ + LTEXT "Bitte geben Sie Benutzernamen und Kennwort ein:", IDC_EXPLAIN, 40, 6, 150, 15 + LTEXT "Proxy", -1, 40, 26, 50, 10 +/* LTEXT "Bereich", -1, 40, 46, 50, 10 */ + LTEXT "Benutzername", -1, 40, 66, 50, 10 + LTEXT "Kennwort", -1, 40, 86, 50, 10 + LTEXT "" IDC_PROXY, 80, 26, 150, 14, 0 + LTEXT "" IDC_REALM, 80, 46, 150, 14, 0 + EDITTEXT IDC_USERNAME, 80, 66, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP + EDITTEXT IDC_PASSWORD, 80, 86, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | ES_PASSWORD + CHECKBOX "Dieses Kennwort speichern (unsicher)", IDC_SAVEPASSWORD, + 80, 106, 150, 12, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP + PUSHBUTTON "OK", IDOK, 98, 126, 56, 14, WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON + PUSHBUTTON "Abbrechen", IDCANCEL, 158, 126, 56, 14, WS_GROUP | WS_TABSTOP +} diff --git a/reactos/dll/mpr/mpr_En.rc b/reactos/dll/mpr/mpr_En.rc new file mode 100644 index 00000000000..461a53924ac --- /dev/null +++ b/reactos/dll/mpr/mpr_En.rc @@ -0,0 +1,46 @@ +/* + * MPR dll resources + * + * Copyright (C) 2004 Juan Lang + * + * 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 + */ + +LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT + +STRINGTABLE DISCARDABLE +{ + IDS_ENTIRENETWORK "Entire Network" +} + +IDD_PROXYDLG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 250, 154 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Enter Network Password" +FONT 8, "MS Shell Dlg" +{ + LTEXT "Please enter your username and password:", IDC_EXPLAIN, 40, 6, 150, 15 + LTEXT "Proxy", -1, 40, 26, 50, 10 +/* LTEXT "Realm", -1, 40, 46, 50, 10 */ + LTEXT "User", -1, 40, 66, 50, 10 + LTEXT "Password", -1, 40, 86, 50, 10 + LTEXT "" IDC_PROXY, 80, 26, 150, 14, 0 + LTEXT "" IDC_REALM, 80, 46, 150, 14, 0 + EDITTEXT IDC_USERNAME, 80, 66, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP + EDITTEXT IDC_PASSWORD, 80, 86, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | ES_PASSWORD + CHECKBOX "&Save this password (Insecure)", IDC_SAVEPASSWORD, + 80, 106, 150, 12, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP + PUSHBUTTON "OK", IDOK, 98, 126, 56, 14, WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON + PUSHBUTTON "Cancel", IDCANCEL, 158, 126, 56, 14, WS_GROUP | WS_TABSTOP +} diff --git a/reactos/dll/mpr/mpr_Es.rc b/reactos/dll/mpr/mpr_Es.rc new file mode 100644 index 00000000000..370e3cf14c5 --- /dev/null +++ b/reactos/dll/mpr/mpr_Es.rc @@ -0,0 +1,46 @@ +/* + * MPR dll Spanish resources + * + * Copyright (C) 2004, 2005 José Manuel Ferrer Ortiz + * + * 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 + */ + +LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL + +STRINGTABLE DISCARDABLE +{ + IDS_ENTIRENETWORK "Toda la red" +} + +IDD_PROXYDLG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 250, 154 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Introduzca contraseña de red" +FONT 8, "MS Shell Dlg" +{ + LTEXT "Por favor, introduzca su nombre de usuario y contraseña:", IDC_EXPLAIN, 40, 6, 150, 15 + LTEXT "Proxy", -1, 40, 26, 50, 10 +/* LTEXT "Realm", -1, 40, 46, 50, 10 */ + LTEXT "Usuario", -1, 40, 66, 50, 10 + LTEXT "Contraseña", -1, 40, 86, 50, 10 + LTEXT "" IDC_PROXY, 80, 26, 150, 14, 0 + LTEXT "" IDC_REALM, 80, 46, 150, 14, 0 + EDITTEXT IDC_USERNAME, 80, 66, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP + EDITTEXT IDC_PASSWORD, 80, 86, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | ES_PASSWORD + CHECKBOX "&Guardar esta contraseña (Inseguro)", IDC_SAVEPASSWORD, + 80, 106, 150, 12, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP + PUSHBUTTON "Aceptar", IDOK, 98, 126, 56, 14, WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON + PUSHBUTTON "Cancelar", IDCANCEL, 158, 126, 56, 14, WS_GROUP | WS_TABSTOP +} diff --git a/reactos/dll/mpr/mpr_Fr.rc b/reactos/dll/mpr/mpr_Fr.rc new file mode 100644 index 00000000000..5e2d9997261 --- /dev/null +++ b/reactos/dll/mpr/mpr_Fr.rc @@ -0,0 +1,47 @@ +/* + * MPR dll resources + * French language support + * + * Copyright (C) 2005 Jonathan Ernst + * + * 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 + */ + +LANGUAGE LANG_FRENCH, SUBLANG_NEUTRAL + +STRINGTABLE DISCARDABLE +{ + IDS_ENTIRENETWORK "Le réseau entier" +} + +IDD_PROXYDLG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 250, 154 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Entrez le mot de passe réseau" +FONT 8, "MS Shell Dlg" +{ + LTEXT "Veuillez saisir votre nom d'utilisateur et votre mot de passe:", IDC_EXPLAIN, 40, 6, 150, 15 + LTEXT "Proxy", -1, 40, 26, 50, 10 +/* LTEXT "Realm", -1, 40, 46, 50, 10 */ + LTEXT "Utilisateur", -1, 40, 66, 50, 10 + LTEXT "Mot de passe", -1, 40, 86, 50, 10 + LTEXT "" IDC_PROXY, 80, 26, 150, 14, 0 + LTEXT "" IDC_REALM, 80, 46, 150, 14, 0 + EDITTEXT IDC_USERNAME, 80, 66, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP + EDITTEXT IDC_PASSWORD, 80, 86, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | ES_PASSWORD + CHECKBOX "&Enregistrer ce mot de passe (risqué)", IDC_SAVEPASSWORD, + 80, 106, 150, 12, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP + PUSHBUTTON "OK", IDOK, 98, 126, 56, 14, WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON + PUSHBUTTON "Annuler", IDCANCEL, 158, 126, 56, 14, WS_GROUP | WS_TABSTOP +} diff --git a/reactos/dll/mpr/mpr_Hu.rc b/reactos/dll/mpr/mpr_Hu.rc new file mode 100644 index 00000000000..df01bb32706 --- /dev/null +++ b/reactos/dll/mpr/mpr_Hu.rc @@ -0,0 +1,47 @@ +/* + * MPR dll resources + * + * Copyright (C) 2004 Juan Lang + * Copyright (C) 2005 Gergely Risko + * + * 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 + */ + +LANGUAGE LANG_HUNGARIAN, SUBLANG_NEUTRAL + +STRINGTABLE DISCARDABLE +{ + IDS_ENTIRENETWORK "Teljes álózat" +} + +IDD_PROXYDLG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 250, 154 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Hálózati Bejelentkezés" +FONT 8, "MS Shell Dlg" +{ + LTEXT "Írd be a felhasználó nevet és jelszót:", IDC_EXPLAIN, 40, 6, 150, 15 + LTEXT "Proxy", -1, 40, 26, 50, 10 +/* LTEXT "Realm", -1, 40, 46, 50, 10 */ + LTEXT "Felhasználó", -1, 40, 66, 50, 10 + LTEXT "Jelszó", -1, 40, 86, 50, 10 + LTEXT "" IDC_PROXY, 80, 26, 150, 14, 0 + LTEXT "" IDC_REALM, 80, 46, 150, 14, 0 + EDITTEXT IDC_USERNAME, 80, 66, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP + EDITTEXT IDC_PASSWORD, 80, 86, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | ES_PASSWORD + CHECKBOX "Jelszó &mentése (Nem biztonságos)", IDC_SAVEPASSWORD, + 80, 106, 150, 12, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP + PUSHBUTTON "OK", IDOK, 98, 126, 56, 14, WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON + PUSHBUTTON "Mégse", IDCANCEL, 158, 126, 56, 14, WS_GROUP | WS_TABSTOP +} diff --git a/reactos/dll/mpr/mpr_It.rc b/reactos/dll/mpr/mpr_It.rc new file mode 100644 index 00000000000..1a733729a14 --- /dev/null +++ b/reactos/dll/mpr/mpr_It.rc @@ -0,0 +1,26 @@ +/* + * MPR dll Italian resources + * + * Copyright 2004 Ivan Leo Puoti + * + * 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 + */ + +LANGUAGE LANG_ITALIAN, SUBLANG_DEFAULT + +STRINGTABLE DISCARDABLE +{ + IDS_ENTIRENETWORK "Tutta la rete" +} diff --git a/reactos/dll/mpr/mpr_Ja.rc b/reactos/dll/mpr/mpr_Ja.rc new file mode 100644 index 00000000000..9481b09e619 --- /dev/null +++ b/reactos/dll/mpr/mpr_Ja.rc @@ -0,0 +1,46 @@ +/* + * MPR dll resources + * + * Copyright (C) 2004 Hajime Segawa + * + * 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 + */ + +LANGUAGE LANG_JAPANESE, SUBLANG_NEUTRAL + +STRINGTABLE DISCARDABLE +{ + IDS_ENTIRENETWORK "ƒlƒbƒgƒ[ƒN‘S‘Ì" +} + +IDD_PROXYDLG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 250, 154 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "ƒlƒbƒgƒ[ƒNƒpƒXƒ[ƒh‚Ì“ü—Í" +FONT 9, "MS UI Gothic" +{ + LTEXT "ƒ†[ƒU[–¼‚ƃpƒXƒ[ƒh‚ð“ü—Í‚µ‚ĉº‚³‚¢:", IDC_EXPLAIN, 40, 6, 150, 15 + LTEXT "ƒvƒƒLƒV", -1, 40, 26, 50, 10 +/* LTEXT "Realm", -1, 40, 46, 50, 10 */ + LTEXT "ƒ†[ƒU–¼", -1, 40, 66, 50, 10 + LTEXT "ƒpƒXƒ[ƒh", -1, 40, 86, 50, 10 + LTEXT "" IDC_PROXY, 80, 26, 150, 14, 0 + LTEXT "" IDC_REALM, 80, 46, 150, 14, 0 + EDITTEXT IDC_USERNAME, 80, 66, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP + EDITTEXT IDC_PASSWORD, 80, 86, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | ES_PASSWORD + CHECKBOX "ƒpƒXƒ[ƒh‚ð•Û‘¶‚·‚é(&S) (ˆÀ‘S‚ł͂ ‚è‚Ü‚¹‚ñ)", IDC_SAVEPASSWORD, + 80, 106, 150, 12, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP + PUSHBUTTON "OK", IDOK, 98, 126, 56, 14, WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON + PUSHBUTTON "ƒLƒƒƒ“ƒZƒ‹", IDCANCEL, 158, 126, 56, 14, WS_GROUP | WS_TABSTOP +} diff --git a/reactos/dll/mpr/mpr_Ko.rc b/reactos/dll/mpr/mpr_Ko.rc new file mode 100644 index 00000000000..d4481220da3 --- /dev/null +++ b/reactos/dll/mpr/mpr_Ko.rc @@ -0,0 +1,47 @@ +/* + * MPR dll resources + * + * Copyright (C) 2004 Juan Lang + * Copyright 2005 YunSong Hwang + * + * 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 + */ + +LANGUAGE LANG_KOREAN, SUBLANG_DEFAULT + +STRINGTABLE DISCARDABLE +{ + IDS_ENTIRENETWORK "Àüü ³×Æ®¿öÅ©" +} + +IDD_PROXYDLG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 250, 154 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "³×Æ®¿öÅ© ¾ÏÈ£ ÀÔ·Â" +FONT 8, "MS Shell Dlg" +{ + LTEXT "´ç½ÅÀÇ »ç¿ëÀÚÀ̸§°ú ¾ÏÈ£¸¦ ÀÔ·ÂÇϽÿÀ:", IDC_EXPLAIN, 40, 6, 150, 15 + LTEXT "ÇÁ·Ï½Ã", -1, 40, 26, 50, 10 +/* LTEXT "Realm", -1, 40, 46, 50, 10 */ + LTEXT "»ç¿ëÀÚ", -1, 40, 66, 50, 10 + LTEXT "¾ÏÈ£", -1, 40, 86, 50, 10 + LTEXT "" IDC_PROXY, 80, 26, 150, 14, 0 + LTEXT "" IDC_REALM, 80, 46, 150, 14, 0 + EDITTEXT IDC_USERNAME, 80, 66, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP + EDITTEXT IDC_PASSWORD, 80, 86, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | ES_PASSWORD + CHECKBOX "¾ÏÈ£ ÀúÀå(&S) (Insecure)", IDC_SAVEPASSWORD, + 80, 106, 150, 12, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP + PUSHBUTTON "È®ÀÎ", IDOK, 98, 126, 56, 14, WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON + PUSHBUTTON "Ãë¼Ò", IDCANCEL, 158, 126, 56, 14, WS_GROUP | WS_TABSTOP +} diff --git a/reactos/dll/mpr/mpr_Nl.rc b/reactos/dll/mpr/mpr_Nl.rc new file mode 100644 index 00000000000..a0569cc6613 --- /dev/null +++ b/reactos/dll/mpr/mpr_Nl.rc @@ -0,0 +1,26 @@ +/* + * Dutch resources for MPR + * + * Copyright (C) 2004 Hans Leidekker + * + * 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 + */ + +LANGUAGE LANG_DUTCH, SUBLANG_DEFAULT + +STRINGTABLE DISCARDABLE +{ + IDS_ENTIRENETWORK "Gehele netwerk" +} diff --git a/reactos/dll/mpr/mpr_No.rc b/reactos/dll/mpr/mpr_No.rc new file mode 100644 index 00000000000..60b7acd6ec5 --- /dev/null +++ b/reactos/dll/mpr/mpr_No.rc @@ -0,0 +1,46 @@ +/* + * MPR dll resources for Norwegian Bokmål + * + * Copyright (C) 2005 Alexander N. Sørnes + * + * 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 + */ + +LANGUAGE LANG_NORWEGIAN, SUBLANG_NORWEGIAN_BOKMAL + +STRINGTABLE DISCARDABLE +{ + IDS_ENTIRENETWORK "Hele nettverket" +} + +IDD_PROXYDLG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 250, 154 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Skriv inn nettverkspassord" +FONT 8, "MS Shell Dlg" +{ + LTEXT "Skriv inn brukernavnet og passordet ditt:", IDC_EXPLAIN, 40, 6, 150, 15 + LTEXT "Mellomtjener", -1, 40, 26, 50, 10 +/* LTEXT "Område", -1, 40, 46, 50, 10 */ + LTEXT "Bruker", -1, 40, 66, 50, 10 + LTEXT "Passord", -1, 40, 86, 50, 10 + LTEXT "" IDC_PROXY, 80, 26, 150, 14, 0 + LTEXT "" IDC_REALM, 80, 46, 150, 14, 0 + EDITTEXT IDC_USERNAME, 80, 66, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP + EDITTEXT IDC_PASSWORD, 80, 86, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | ES_PASSWORD + CHECKBOX "Lagre dette pa&ssordet (usikkert)", IDC_SAVEPASSWORD, + 80, 106, 150, 12, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP + PUSHBUTTON "OK", IDOK, 98, 126, 56, 14, WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON + PUSHBUTTON "Avbryt", IDCANCEL, 158, 126, 56, 14, WS_GROUP | WS_TABSTOP +} diff --git a/reactos/dll/mpr/mpr_Pt.rc b/reactos/dll/mpr/mpr_Pt.rc new file mode 100644 index 00000000000..e22c49c8cf7 --- /dev/null +++ b/reactos/dll/mpr/mpr_Pt.rc @@ -0,0 +1,46 @@ +/* + * MPR dll resources + * + * Copyright (C) 2004 Marcelo Duarte + * + * 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 + */ + +LANGUAGE LANG_PORTUGUESE, SUBLANG_DEFAULT + +STRINGTABLE DISCARDABLE +{ + IDS_ENTIRENETWORK "Toda a rede" +} + +IDD_PROXYDLG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 250, 154 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Entre a senha da rede" +FONT 8, "MS Shell Dlg" +{ + LTEXT "Por favor, entre como o nome de usuário e a senha:", IDC_EXPLAIN, 40, 6, 150, 15 + LTEXT "Proxy", -1, 40, 26, 50, 10 +/* LTEXT "Realm", -1, 40, 46, 50, 10 */ + LTEXT "Usuário", -1, 40, 66, 50, 10 + LTEXT "Senha", -1, 40, 86, 50, 10 + LTEXT "" IDC_PROXY, 80, 26, 150, 14, 0 + LTEXT "" IDC_REALM, 80, 46, 150, 14, 0 + EDITTEXT IDC_USERNAME, 80, 66, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP + EDITTEXT IDC_PASSWORD, 80, 86, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | ES_PASSWORD + CHECKBOX "&Salvar esta senha (Inseguro)", IDC_SAVEPASSWORD, + 80, 106, 150, 12, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP + PUSHBUTTON "OK", IDOK, 98, 126, 56, 14, WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON + PUSHBUTTON "Cancelar", IDCANCEL, 158, 126, 56, 14, WS_GROUP | WS_TABSTOP +} diff --git a/reactos/dll/mpr/mpr_Ru.rc b/reactos/dll/mpr/mpr_Ru.rc new file mode 100644 index 00000000000..bd821b4e107 --- /dev/null +++ b/reactos/dll/mpr/mpr_Ru.rc @@ -0,0 +1,46 @@ +/* + * MPR dll resources + * + * Copyright (C) 2005 Mikhail Y. Zvyozdochkin + * + * 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 + */ + +LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT + +STRINGTABLE DISCARDABLE +{ + IDS_ENTIRENETWORK "Âñÿ ñåòü" +} + +IDD_PROXYDLG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 250, 154 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Ââåäèòå Ñåòåâîé Ïàðîëü" +FONT 8, "MS Shell Dlg" +{ + LTEXT "Ïîæàëóéñòà, ââåäèòå Âàøè èìÿ è ïàðîëü:", IDC_EXPLAIN, 40, 6, 150, 15 + LTEXT "Ïðîêñè-ñåðâåð", -1, 40, 26, 50, 10 +/* LTEXT "Realm", -1, 40, 46, 50, 10 */ + LTEXT "Èìÿ ïîëüçîâàòåëÿ", -1, 40, 66, 50, 10 + LTEXT "Ïàðîëü", -1, 40, 86, 50, 10 + LTEXT "" IDC_PROXY, 80, 26, 150, 14, 0 + LTEXT "" IDC_REALM, 80, 46, 150, 14, 0 + EDITTEXT IDC_USERNAME, 80, 66, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP + EDITTEXT IDC_PASSWORD, 80, 86, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | ES_PASSWORD + CHECKBOX "&Ñîõðàíèòü ïàðîëü (íåáåçîïàñíî)", IDC_SAVEPASSWORD, + 80, 106, 150, 12, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP + PUSHBUTTON "OK", IDOK, 98, 126, 56, 14, WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON + PUSHBUTTON "Îòìåíà", IDCANCEL, 158, 126, 56, 14, WS_GROUP | WS_TABSTOP +} diff --git a/reactos/dll/mpr/mpr_Sv.rc b/reactos/dll/mpr/mpr_Sv.rc new file mode 100644 index 00000000000..b1ee8daa299 --- /dev/null +++ b/reactos/dll/mpr/mpr_Sv.rc @@ -0,0 +1,46 @@ +/* + * MPR dll Swedish resources + * + * Copyright (C) 2005 Andreas Bjerkeholt + * + * 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 + */ + +LANGUAGE LANG_SWEDISH, SUBLANG_DEFAULT + +STRINGTABLE DISCARDABLE +{ + IDS_ENTIRENETWORK "Hela nätverket" +} + +IDD_PROXYDLG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 250, 154 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Ange nätverkslösenord" +FONT 8, "MS Shell Dlg" +{ + LTEXT "Ange ditt användarnamn och lösenord:", IDC_EXPLAIN, 40, 6, 150, 15 + LTEXT "Proxy", -1, 40, 26, 50, 10 +/* LTEXT "Realm", -1, 40, 46, 50, 10 */ + LTEXT "Användare", -1, 40, 66, 50, 10 + LTEXT "Lösenord", -1, 40, 86, 50, 10 + LTEXT "" IDC_PROXY, 80, 26, 150, 14, 0 + LTEXT "" IDC_REALM, 80, 46, 150, 14, 0 + EDITTEXT IDC_USERNAME, 80, 66, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP + EDITTEXT IDC_PASSWORD, 80, 86, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | ES_PASSWORD + CHECKBOX "&Spara detta lösenord (Osäkert)", IDC_SAVEPASSWORD, + 80, 106, 150, 12, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP + PUSHBUTTON "OK", IDOK, 98, 126, 56, 14, WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON + PUSHBUTTON "Avbryt", IDCANCEL, 158, 126, 56, 14, WS_GROUP | WS_TABSTOP +} diff --git a/reactos/dll/mpr/mpr_Uk.rc b/reactos/dll/mpr/mpr_Uk.rc new file mode 100644 index 00000000000..c5181f11208 --- /dev/null +++ b/reactos/dll/mpr/mpr_Uk.rc @@ -0,0 +1,46 @@ +/* + * MPR dll resources (Ukrainian) + * + * Copyright 2006 Artem Reznikov + * + * 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 + */ + +LANGUAGE LANG_UKRAINIAN, SUBLANG_DEFAULT + +STRINGTABLE DISCARDABLE +{ + IDS_ENTIRENETWORK "Âñÿ Ìåðåæà" +} + +IDD_PROXYDLG DIALOG LOADONCALL MOVEABLE DISCARDABLE 36, 24, 250, 154 +STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Ââåä³òü Ìåðåæíèé Ïàðîëü" +FONT 8, "MS Shell Dlg" +{ + LTEXT "Áóäü ëàñêà, ââåä³òü Âàø³ ³ì'ÿ òà ïàðîëü:", IDC_EXPLAIN, 40, 6, 150, 15 + LTEXT "Ïðîêñ³", -1, 40, 26, 50, 10 +/* LTEXT "Realm", -1, 40, 46, 50, 10 */ + LTEXT "Êîðèñòóâà÷", -1, 40, 66, 50, 10 + LTEXT "Ïàðîëü", -1, 40, 86, 50, 10 + LTEXT "" IDC_PROXY, 80, 26, 150, 14, 0 + LTEXT "" IDC_REALM, 80, 46, 150, 14, 0 + EDITTEXT IDC_USERNAME, 80, 66, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP + EDITTEXT IDC_PASSWORD, 80, 86, 150, 14, ES_AUTOHSCROLL | WS_BORDER | WS_TABSTOP | ES_PASSWORD + CHECKBOX "&Çáåðåãòè öåé ïàðîëü (íåáåçïå÷íî)", IDC_SAVEPASSWORD, + 80, 106, 150, 12, BS_AUTOCHECKBOX | WS_GROUP | WS_TABSTOP + PUSHBUTTON "OK", IDOK, 98, 126, 56, 14, WS_GROUP | WS_TABSTOP | BS_DEFPUSHBUTTON + PUSHBUTTON "Ñêàñóâàòè", IDCANCEL, 158, 126, 56, 14, WS_GROUP | WS_TABSTOP +} diff --git a/reactos/dll/mpr/mpr_main.c b/reactos/dll/mpr/mpr_main.c new file mode 100644 index 00000000000..b9446496150 --- /dev/null +++ b/reactos/dll/mpr/mpr_main.c @@ -0,0 +1,101 @@ +/* + * MPR undocumented functions + * + * Copyright 1999 Ulrich Weigand + * + * 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 + +#include "windef.h" +#include "winbase.h" +#include "objbase.h" +#include "winnetwk.h" +#include "wine/debug.h" +#include "wnetpriv.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mpr); + + /* + * FIXME: The following routines should use a private heap ... + */ + +/***************************************************************** + * @ [MPR.22] + */ +LPVOID WINAPI MPR_Alloc( DWORD dwSize ) +{ + return HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize ); +} + +/***************************************************************** + * @ [MPR.23] + */ +LPVOID WINAPI MPR_ReAlloc( LPVOID lpSrc, DWORD dwSize ) +{ + if ( lpSrc ) + return HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, lpSrc, dwSize ); + else + return HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, dwSize ); +} + +/***************************************************************** + * @ [MPR.24] + */ +BOOL WINAPI MPR_Free( LPVOID lpMem ) +{ + if ( lpMem ) + return HeapFree( GetProcessHeap(), 0, lpMem ); + else + return FALSE; +} + +/***************************************************************** + * @ [MPR.25] + */ +BOOL WINAPI _MPR_25( LPBYTE lpMem, INT len ) +{ + FIXME( "(%p, %d): stub\n", lpMem, len ); + + return FALSE; +} + +/***************************************************************** + * DllCanUnloadNow [MPR.@] + */ +HRESULT WINAPI DllCanUnloadNow(void) +{ + FIXME("Stub\n"); + return S_OK; +} + +/***************************************************************** + * DllMain [MPR.init] + */ +BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + switch (fdwReason) { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls( hinstDLL ); + wnetInit(hinstDLL); + break; + + case DLL_PROCESS_DETACH: + wnetFree(); + break; + } + return TRUE; +} diff --git a/reactos/dll/mpr/mprres.h b/reactos/dll/mpr/mprres.h new file mode 100644 index 00000000000..fa9cf8be7d6 --- /dev/null +++ b/reactos/dll/mpr/mprres.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2004 Juan Lang + * + * 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 + */ +#ifndef __WINE_MPRRES_H__ +#define __WINE_MPRRES_H__ + +#define IDS_ENTIRENETWORK 1 + +#define IDD_PROXYDLG 0x400 + +#define IDC_PROXY 0x401 +#define IDC_REALM 0x402 +#define IDC_USERNAME 0x403 +#define IDC_PASSWORD 0x404 +#define IDC_SAVEPASSWORD 0x405 +#define IDC_EXPLAIN 0x406 + +#endif /* ndef __WINE_MPRRES_H__ */ diff --git a/reactos/dll/mpr/multinet.c b/reactos/dll/mpr/multinet.c new file mode 100644 index 00000000000..aa8cfb8908b --- /dev/null +++ b/reactos/dll/mpr/multinet.c @@ -0,0 +1,81 @@ +/* + * MPR Multinet functions + * + * Copyright 1999 Ulrich Weigand + * + * 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 + +#include "windef.h" +#include "winbase.h" +#include "winnetwk.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mpr); + + +/***************************************************************** + * MultinetGetConnectionPerformanceA [MPR.@] + * + * RETURNS + * Success: NO_ERROR + * Failure: ERROR_NOT_SUPPORTED, ERROR_NOT_CONNECTED, + * ERROR_NO_NET_OR_BAD_PATH, ERROR_BAD_DEVICE, + * ERROR_BAD_NET_NAME, ERROR_INVALID_PARAMETER, + * ERROR_NO_NETWORK, ERROR_EXTENDED_ERROR + */ +DWORD WINAPI MultinetGetConnectionPerformanceA( + LPNETRESOURCEA lpNetResource, + LPNETCONNECTINFOSTRUCT lpNetConnectInfoStruct ) +{ + FIXME( "(%p, %p): stub\n", lpNetResource, lpNetConnectInfoStruct ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/***************************************************************** + * MultinetGetConnectionPerformanceW [MPR.@] + */ +DWORD WINAPI MultinetGetConnectionPerformanceW( + LPNETRESOURCEW lpNetResource, + LPNETCONNECTINFOSTRUCT lpNetConnectInfoStruct ) +{ + FIXME( "(%p, %p): stub\n", lpNetResource, lpNetConnectInfoStruct ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/***************************************************************** + * MultinetGetErrorTextA [MPR.@] + */ +DWORD WINAPI MultinetGetErrorTextA( DWORD x, DWORD y, DWORD z ) +{ + FIXME( "(%lx, %lx, %lx): stub\n", x, y, z ); + return 0; +} + +/***************************************************************** + * MultinetGetErrorTextW [MPR.@] + */ +DWORD WINAPI MultinetGetErrorTextW( DWORD x, DWORD y, DWORD z ) +{ + FIXME( "(%lx, %lx, %lx ): stub\n", x, y, z ); + return 0; +} + diff --git a/reactos/dll/mpr/netspi.h b/reactos/dll/mpr/netspi.h new file mode 100644 index 00000000000..7695584aec2 --- /dev/null +++ b/reactos/dll/mpr/netspi.h @@ -0,0 +1,153 @@ +/* + * MPR - Network provider services + * Warning: this file apparently existed as part of the Win98 DDK. Some of + * the declarations in it conflict with those in the Platform SDK's npapi.h, + * therefore this header was made private. Don't try to include both headers. + * + * Copyright (C) 1999 Ulrich Weigand + * + * 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 + */ + +#ifndef _NETSPI_H_ +#define _NETSPI_H_ + +#include "windef.h" +#include "winnetwk.h" + +/* + * Note: The Unicode variants of all these routines/structures + * apparently don't exist, at least not in Win95 ... + */ + +#define HPROVIDER LPVOID +typedef HPROVIDER *PHPROVIDER; + +typedef struct +{ + DWORD cbStructure; + HWND hwndOwner; + LPCSTR lpResource; + LPSTR lpUsername; + DWORD cbUsername; + LPSTR lpPassword; + DWORD cbPassword; + LPSTR lpOrgUnit; + DWORD cbOrgUnit; + LPCSTR lpOUTitle; + LPCSTR lpExplainText; + LPCSTR lpDefaultUserName; + DWORD dwFlags; + +} AUTHDLGSTRUCTA, *LPAUTHDLGSTRUCTA; + +DECL_WINELIB_TYPE_AW(AUTHDLGSTRUCT) +DECL_WINELIB_TYPE_AW(LPAUTHDLGSTRUCT) + +#define AUTHDLG_ENABLECACHE 0x00000001 +#define AUTHDLG_CHECKCACHE 0x00000002 +#define AUTHDLG_CACHEINVALID 0x00000004 +#define AUTHDLG_USE_DEFAULT_NAME 0x00000008 +#define AUTHDLG_CHECKDEFAULT_NAME 0x00000010 +#define AUTHDLG_LOGON 0x00000020 + +#define AUTHDLG_ENABLECACHE 0x00000001 +#define AUTHDLG_CHECKCACHE 0x00000002 +#define AUTHDLG_CACHEINVALID 0x00000004 +#define AUTHDLG_USE_DEFAULT_NAME 0x00000008 +#define AUTHDLG_CHECKDEFAULT_NAME 0x00000010 +#define AUTHDLG_LOGON 0x00000020 + +DWORD WINAPI NPSAuthenticationDialogA(LPAUTHDLGSTRUCTA); +#define NPSAuthenticationDialog WINELIB_NAME_AW(NPSAuthenticationDialog) +DWORD WINAPI NPSGetProviderHandleA(PHPROVIDER); +#define NPSGetProviderHandle WINELIB_NAME_AW(NPSGetProviderHandle) +DWORD WINAPI NPSGetProviderNameA(HPROVIDER,LPCSTR *); +#define NPSGetProviderName WINELIB_NAME_AW(NPSGetProviderName) +DWORD WINAPI NPSGetSectionNameA(HPROVIDER,LPCSTR *lpszSectionName); +#define NPSGetSectionName WINELIB_NAME_AW(NPSGetSectionName) +DWORD WINAPI NPSSetExtendedErrorA(DWORD,LPSTR); +#define NPSSetExtendedError WINELIB_NAME_AW(NPSSetExtendedError) +VOID WINAPI NPSSetCustomTextA(LPSTR); +#define NPSSetCustomText WINELIB_NAME_AW(NPSSetCustomText) +DWORD WINAPI NPSCopyStringA(LPCSTR,LPVOID,LPDWORD); +#define NPSCopyString WINELIB_NAME_AW(NPSCopyString) +DWORD WINAPI NPSDeviceGetNumberA(LPSTR,LPDWORD,LPDWORD); +#define NPSDeviceGetNumber WINELIB_NAME_AW(NPSDeviceGetNumber) +DWORD WINAPI NPSDeviceGetStringA(DWORD,DWORD,LPSTR,LPDWORD); +#define NPSDeviceGetString WINELIB_NAME_AW(NPSDeviceGetString) + + +enum NOTIFYTYPE { NotifyAddConnection, + NotifyCancelConnection, + NotifyGetConnectionPerformance }; + +#define NOTIFY_PRE 0x00 +#define NOTIFY_POST 0x01 + +typedef struct +{ + DWORD cbStructure; + DWORD dwNotifyStatus; + DWORD dwOperationStatus; + LPVOID lpNPContext; + +} NOTIFYINFO, *LPNOTIFYINFO; + +typedef struct +{ + DWORD cbStructure; + HWND hwndOwner; + NETRESOURCEA NetResource; + DWORD dwAddFlags; + LPSTR lpAccessName; + LPDWORD lpBufferSize; + DWORD dwResult; + DWORD dwAddContext; + +} NOTIFYADDA, *LPNOTIFYADDA; + +#define CONNECT_CTXT_RESTORE 0x00000001 +#define CONNECT_CTXT_GLOBAL 0x00000002 +#define CONNECT_CTXT_PROVIDER 0x00000004 +#define CONNECT_CTXT_SINGLE 0x00000008 + +typedef struct +{ + DWORD cbStructure; + LPSTR lpName; + LPSTR lpProvider; + DWORD dwFlags; + BOOL fForce; + +} NOTIFYCANCELA, *LPNOTIFYCANCELA; + +typedef struct +{ + DWORD cbStructure; + LPSTR lpRemoteName; + LPSTR lpProviderName; + LPNETCONNECTINFOSTRUCT lpNetConnectInfo; + +} NOTIFYPERFORMANCEA, *LPNOTIFYPERFORMANCEA; + +typedef DWORD (CALLBACK *NOTIFYCALLBACK)(LPNOTIFYINFO,LPVOID); + +DWORD WINAPI NPSNotifyRegisterA(enum NOTIFYTYPE,NOTIFYCALLBACK); +#define NPSNotifyRegister WINELIB_NAME_AW(NPSNotifyRegister) +LPVOID WINAPI NPSNotifyGetContextA(NOTIFYCALLBACK); +#define NPSNotifyGetContext WINELIB_NAME_AW(NPSNotifyGetContext) + +#endif /* _NETSPI_H_ */ diff --git a/reactos/dll/mpr/nps.c b/reactos/dll/mpr/nps.c new file mode 100644 index 00000000000..7fe15565b89 --- /dev/null +++ b/reactos/dll/mpr/nps.c @@ -0,0 +1,213 @@ +/* + * MPR Network Provider Services functions + * + * Copyright 1999 Ulrich Weigand + * Copyright 2004 Mike McCormack for CodeWeavers Inc. + * + * 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 "config.h" + +#include + +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "netspi.h" +#include "wine/debug.h" +#include "winerror.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mpr); + +#include "wine/unicode.h" + +#include "mprres.h" + +/*********************************************************************** + * NPS_ProxyPasswordDialog + */ +static INT_PTR WINAPI NPS_ProxyPasswordDialog( + HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam ) +{ + HWND hitem; + LPAUTHDLGSTRUCTA lpAuthDlgStruct; + + if( uMsg == WM_INITDIALOG ) + { + TRACE("WM_INITDIALOG (%08lx)\n", lParam); + + /* save the parameter list */ + lpAuthDlgStruct = (LPAUTHDLGSTRUCTA) lParam; + SetWindowLongPtrW( hdlg, GWLP_USERDATA, lParam ); + + if( lpAuthDlgStruct->lpExplainText ) + { + hitem = GetDlgItem( hdlg, IDC_EXPLAIN ); + SetWindowTextA( hitem, lpAuthDlgStruct->lpExplainText ); + } + + /* extract the Realm from the proxy response and show it */ + if( lpAuthDlgStruct->lpResource ) + { + hitem = GetDlgItem( hdlg, IDC_REALM ); + SetWindowTextA( hitem, lpAuthDlgStruct->lpResource ); + } + + return TRUE; + } + + lpAuthDlgStruct = (LPAUTHDLGSTRUCTA) GetWindowLongPtrW( hdlg, GWLP_USERDATA ); + + switch( uMsg ) + { + case WM_COMMAND: + if( wParam == IDOK ) + { + WCHAR username[0x20], password[0x20]; + + username[0] = 0; + hitem = GetDlgItem( hdlg, IDC_USERNAME ); + if( hitem ) + GetWindowTextA( hitem, lpAuthDlgStruct->lpUsername, lpAuthDlgStruct->cbUsername ); + + password[0] = 0; + hitem = GetDlgItem( hdlg, IDC_PASSWORD ); + if( hitem ) + GetWindowTextA( hitem, lpAuthDlgStruct->lpPassword, lpAuthDlgStruct->cbPassword ); + + EndDialog( hdlg, WN_SUCCESS ); + return TRUE; + } + if( wParam == IDCANCEL ) + { + EndDialog( hdlg, WN_CANCEL ); + return TRUE; + } + break; + } + return FALSE; +} + +/***************************************************************** + * NPSAuthenticationDialogA [MPR.@] + */ +DWORD WINAPI NPSAuthenticationDialogA( LPAUTHDLGSTRUCTA lpAuthDlgStruct ) +{ + HMODULE hwininet = GetModuleHandleA( "mpr.dll" ); + + TRACE("%p\n", lpAuthDlgStruct); + + if( !lpAuthDlgStruct ) + return WN_BAD_POINTER; + if( lpAuthDlgStruct->cbStructure < sizeof *lpAuthDlgStruct ) + return WN_BAD_POINTER; + + TRACE("%s %s %s\n",lpAuthDlgStruct->lpResource, + lpAuthDlgStruct->lpOUTitle, lpAuthDlgStruct->lpExplainText); + + return DialogBoxParamW( hwininet, MAKEINTRESOURCEW( IDD_PROXYDLG ), + lpAuthDlgStruct->hwndOwner, NPS_ProxyPasswordDialog, + (LPARAM) lpAuthDlgStruct ); +} + +/***************************************************************** + * NPSGetProviderHandleA [MPR.@] + */ +DWORD WINAPI NPSGetProviderHandleA( PHPROVIDER phProvider ) +{ + FIXME( "(%p): stub\n", phProvider ); + return WN_NOT_SUPPORTED; +} + +/***************************************************************** + * NPSGetProviderNameA [MPR.@] + */ +DWORD WINAPI NPSGetProviderNameA( HPROVIDER hProvider, LPCSTR *lpszProviderName ) +{ + FIXME( "(%p, %p): stub\n", hProvider, lpszProviderName ); + return WN_NOT_SUPPORTED; +} + +/***************************************************************** + * NPSGetSectionNameA [MPR.@] + */ +DWORD WINAPI NPSGetSectionNameA( HPROVIDER hProvider, LPCSTR *lpszSectionName ) +{ + FIXME( "(%p, %p): stub\n", hProvider, lpszSectionName ); + return WN_NOT_SUPPORTED; +} + +/***************************************************************** + * NPSSetExtendedErrorA [MPR.@] + */ +DWORD WINAPI NPSSetExtendedErrorA( DWORD NetSpecificError, LPSTR lpExtendedErrorText ) +{ + FIXME( "(%08lx, %s): stub\n", NetSpecificError, debugstr_a(lpExtendedErrorText) ); + return WN_NOT_SUPPORTED; +} + +/***************************************************************** + * NPSSetCustomTextA [MPR.@] + */ +VOID WINAPI NPSSetCustomTextA( LPSTR lpCustomErrorText ) +{ + FIXME( "(%s): stub\n", debugstr_a(lpCustomErrorText) ); +} + +/***************************************************************** + * NPSCopyStringA [MPR.@] + */ +DWORD WINAPI NPSCopyStringA( LPCSTR lpString, LPVOID lpBuffer, LPDWORD lpdwBufferSize ) +{ + FIXME( "(%s, %p, %p): stub\n", debugstr_a(lpString), lpBuffer, lpdwBufferSize ); + return WN_NOT_SUPPORTED; +} + +/***************************************************************** + * NPSDeviceGetNumberA [MPR.@] + */ +DWORD WINAPI NPSDeviceGetNumberA( LPSTR lpLocalName, LPDWORD lpdwNumber, LPDWORD lpdwType ) +{ + FIXME( "(%s, %p, %p): stub\n", debugstr_a(lpLocalName), lpdwNumber, lpdwType ); + return WN_NOT_SUPPORTED; +} + +/***************************************************************** + * NPSDeviceGetStringA [MPR.@] + */ +DWORD WINAPI NPSDeviceGetStringA( DWORD dwNumber, DWORD dwType, LPSTR lpLocalName, LPDWORD lpdwBufferSize ) +{ + FIXME( "(%ld, %ld, %p, %p): stub\n", dwNumber, dwType, lpLocalName, lpdwBufferSize ); + return WN_NOT_SUPPORTED; +} + +/***************************************************************** + * NPSNotifyRegisterA [MPR.@] + */ +DWORD WINAPI NPSNotifyRegisterA( enum NOTIFYTYPE NotifyType, NOTIFYCALLBACK pfNotifyCallBack ) +{ + FIXME( "(%d, %p): stub\n", NotifyType, pfNotifyCallBack ); + return WN_NOT_SUPPORTED; +} + +/***************************************************************** + * NPSNotifyGetContextA [MPR.@] + */ +LPVOID WINAPI NPSNotifyGetContextA( NOTIFYCALLBACK pfNotifyCallBack ) +{ + FIXME( "(%p): stub\n", pfNotifyCallBack ); + return NULL; +} diff --git a/reactos/dll/mpr/pwcache.c b/reactos/dll/mpr/pwcache.c new file mode 100644 index 00000000000..682ec4cecfa --- /dev/null +++ b/reactos/dll/mpr/pwcache.c @@ -0,0 +1,319 @@ +/* + * MPR Password Cache functions + * + * Copyright 1999 Ulrich Weigand + * Copyright 2003,2004 Mike McCormack 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winnetwk.h" +#include "winreg.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mpr); + +static const char mpr_key[] = "Software\\Wine\\Wine\\Mpr\\"; + +static inline BYTE hex( BYTE x ) +{ + if( x <= 9 ) + return x + '0'; + return x + 'A' - 10; +} + +static inline CHAR ctox( CHAR x ) +{ + if( ( x >= '0' ) && ( x <= '9' ) ) + return x - '0'; + if( ( x >= 'A' ) && ( x <= 'F' ) ) + return x - 'A' + 10; + if( ( x >= 'a' ) && ( x <= 'a' ) ) + return x - 'a' + 10; + return -1; +} + +static LPSTR MPR_GetValueName( LPSTR pbResource, WORD cbResource, BYTE nType ) +{ + LPSTR name; + DWORD i; + + name = HeapAlloc( GetProcessHeap(), 0, 6+cbResource*2 ); + if( name ) + sprintf( name, "X-%02X-", nType ); + for(i=0; i>4); + name[6+i*2]=hex(pbResource[i]&0x0f); + } + name[5+i*2]=0; + TRACE( "Value is %s\n", name ); + return name; +} + + +/************************************************************************** + * WNetCachePassword [MPR.@] Saves password in cache + * + * NOTES + * only the parameter count is verifyed + * + * ---- everything below this line might be wrong (js) ----- + * RETURNS + * Success: WN_SUCCESS + * Failure: WN_ACCESS_DENIED, WN_BAD_PASSWORD, WN_BADVALUE, WN_NET_ERROR, + * WN_NOT_SUPPORTED, WN_OUT_OF_MEMORY + */ +DWORD WINAPI WNetCachePassword( + LPSTR pbResource, /* [in] Name of workgroup, computer, or resource */ + WORD cbResource, /* [in] Size of name */ + LPSTR pbPassword, /* [in] Buffer containing password */ + WORD cbPassword, /* [in] Size of password */ + BYTE nType, /* [in] Type of password to cache */ + WORD x) + +{ + HKEY hkey; + DWORD r; + LPSTR valname; + + WARN( "(%p(%s), %d, %p(%s), %d, %d, 0x%08x): totally insecure\n", + pbResource, debugstr_a(pbResource), cbResource, + pbPassword, debugstr_a(pbPassword), cbPassword, + nType, x ); + + /* @@ Wine registry key: HKCU\Software\Wine\Wine\Mpr */ + r = RegCreateKeyA( HKEY_CURRENT_USER, mpr_key, &hkey ); + if( r ) + return WN_ACCESS_DENIED; + + valname = MPR_GetValueName( pbResource, cbResource, nType ); + if( valname ) + { + r = RegSetValueExA( hkey, valname, 0, REG_BINARY, + (LPBYTE)pbPassword, cbPassword ); + if( r ) + r = WN_CANCEL; + else + r = WN_SUCCESS; + HeapFree( GetProcessHeap(), 0, valname ); + } + else + r = WN_OUT_OF_MEMORY; + + RegCloseKey( hkey ); + + return r; +} + +/***************************************************************** + * WNetRemoveCachedPassword [MPR.@] + */ +UINT WINAPI WNetRemoveCachedPassword( + LPSTR pbResource, /* [in] resource ID to delete */ + WORD cbResource, /* [in] number of bytes in the resource ID */ + BYTE nType ) /* [in] Type of the resource to delete */ +{ + HKEY hkey; + DWORD r; + LPSTR valname; + + WARN( "(%p(%s), %d, %d): totally insecure\n", + pbResource, debugstr_a(pbResource), cbResource, nType ); + + /* @@ Wine registry key: HKCU\Software\Wine\Wine\Mpr */ + r = RegCreateKeyA( HKEY_CURRENT_USER, mpr_key, &hkey ); + if( r ) + return WN_ACCESS_DENIED; + + valname = MPR_GetValueName( pbResource, cbResource, nType ); + if( valname ) + { + r = RegDeleteValueA( hkey, valname ); + if( r ) + r = WN_ACCESS_DENIED; + else + r = WN_SUCCESS; + HeapFree( GetProcessHeap(), 0, valname ); + } + else + r = WN_OUT_OF_MEMORY; + + return r; +} + +/***************************************************************** + * WNetGetCachedPassword [MPR.@] Retrieves password from cache + * + * NOTES + * the stub seems to be wrong: + * arg1: ptr 0x40xxxxxx -> (no string) + * arg2: len 36 + * arg3: ptr 0x40xxxxxx -> (no string) + * arg4: ptr 0x40xxxxxx -> 0xc8 + * arg5: type? 4 + * + * ---- everything below this line might be wrong (js) ----- + * RETURNS + * Success: WN_SUCCESS + * Failure: WN_ACCESS_DENIED, WN_BAD_PASSWORD, WN_BAD_VALUE, + * WN_NET_ERROR, WN_NOT_SUPPORTED, WN_OUT_OF_MEMORY + */ +DWORD WINAPI WNetGetCachedPassword( + LPSTR pbResource, /* [in] Name of workgroup, computer, or resource */ + WORD cbResource, /* [in] Size of name */ + LPSTR pbPassword, /* [out] Buffer to receive password */ + LPWORD pcbPassword, /* [out] Receives size of password */ + BYTE nType) /* [in] Type of password to retrieve */ +{ + HKEY hkey; + DWORD r, type = 0, sz; + LPSTR valname; + + WARN( "(%p(%s), %d, %p, %p, %d): totally insecure\n", + pbResource, debugstr_a(pbResource), cbResource, + pbPassword, pcbPassword, nType ); + + memset( pbPassword, 0, *pcbPassword); + + /* @@ Wine registry key: HKCU\Software\Wine\Wine\Mpr */ + r = RegCreateKeyA( HKEY_CURRENT_USER, mpr_key, &hkey ); + if( r ) + return WN_ACCESS_DENIED; + + valname = MPR_GetValueName( pbResource, cbResource, nType ); + if( valname ) + { + sz = *pcbPassword; + r = RegQueryValueExA( hkey, valname, 0, &type, (LPBYTE)pbPassword, &sz ); + *pcbPassword = sz; + if( r ) + r = WN_CANCEL; + else + r = WN_SUCCESS; + HeapFree( GetProcessHeap(), 0, valname ); + } + else + r = WN_OUT_OF_MEMORY; + + return r; +} + +/******************************************************************* + * WNetEnumCachedPasswords [MPR.@] + * + * NOTES + * the parameter count is verifyed + * + * This function is a huge security risk, as virii and such can use + * it to grab all the passwords in the cache. It's bad enough to + * store the passwords (insecurely). + * + * bpPrefix and cbPrefix are used to filter the returned passwords + * the first cbPrefix bytes of the password resource identifier + * should match the same number of bytes in bpPrefix + * + * RETURNS + * Success: WN_SUCCESS (even if no entries were enumerated) + * Failure: WN_ACCESS_DENIED, WN_BAD_PASSWORD, WN_BAD_VALUE, + * WN_NET_ERROR, WN_NOT_SUPPORTED, WN_OUT_OF_MEMORY + */ + +UINT WINAPI WNetEnumCachedPasswords( + LPSTR pbPrefix, /* [in] prefix to filter cache entries */ + WORD cbPrefix, /* [in] number of bytes in Prefix substring */ + BYTE nType, /* [in] match the Type ID of the entry */ + ENUMPASSWORDPROC enumPasswordProc, /* [in] callback function */ + DWORD param) /* [in] parameter passed to enum function */ +{ + HKEY hkey; + DWORD r, type, val_sz, data_sz, i, j, size; + PASSWORD_CACHE_ENTRY *entry; + CHAR val[256], prefix[6]; + + WARN( "(%s, %d, %d, %p, 0x%08lx) totally insecure\n", + debugstr_an(pbPrefix,cbPrefix), cbPrefix, + nType, enumPasswordProc, param ); + + /* @@ Wine registry key: HKCU\Software\Wine\Wine\Mpr */ + r = RegCreateKeyA( HKEY_CURRENT_USER, mpr_key, &hkey ); + if( r ) + return WN_ACCESS_DENIED; + + sprintf(prefix, "X-%02X-", nType ); + + i = 0; + for( i=0; ; i++ ) + { + val_sz = sizeof val; + data_sz = 0; + type = 0; + val[0] = 0; + r = RegEnumValueA( hkey, i, val, &val_sz, NULL, &type, NULL, &data_sz ); + if( r != ERROR_SUCCESS ) + break; + if( type != REG_BINARY ) + continue; + + /* check the value is in the format we expect */ + if( val_sz < sizeof prefix ) + continue; + if( memcmp( prefix, val, 5 ) ) + continue; + + /* decode the value */ + for(j=5; jabResource[0] + val_sz + data_sz; + entry = HeapAlloc( GetProcessHeap(), 0, sizeof *entry + val_sz + data_sz ); + memcpy( entry->abResource, val, val_sz ); + entry->cbEntry = size; + entry->cbResource = val_sz; + entry->cbPassword = data_sz; + entry->iEntry = i; + entry->nType = nType; + r = RegEnumValueA( hkey, i, NULL, &val_sz, NULL, &type, + &entry->abResource[val_sz], &data_sz ); + if( r == ERROR_SUCCESS ) + enumPasswordProc( entry, param ); + HeapFree( GetProcessHeap(), 0, entry ); + } + + RegCloseKey( hkey ); + + return WN_SUCCESS; +} diff --git a/reactos/dll/mpr/version.rc b/reactos/dll/mpr/version.rc new file mode 100644 index 00000000000..f22cc4d5ba1 --- /dev/null +++ b/reactos/dll/mpr/version.rc @@ -0,0 +1,28 @@ +/* + * MPR dll version resources + * + * Copyright (C) 2004 Juan Lang + * + * 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 + */ + +#define WINE_FILEDESCRIPTION_STR "Wine Multiprotocol Router Library" +#define WINE_FILENAME_STR "mpr.dll" +#define WINE_FILEVERSION 5,0,2195,6611 +#define WINE_FILEVERSION_STR "5.00.2195.6611" +#define WINE_PRODUCTVERSION 5,0,2195,6611 +#define WINE_PRODUCTVERSION_STR "5.00.2195.6611" + +#include "wine/wine_common_ver.rc" diff --git a/reactos/dll/mpr/wnet.c b/reactos/dll/mpr/wnet.c new file mode 100644 index 00000000000..3b585c07b13 --- /dev/null +++ b/reactos/dll/mpr/wnet.c @@ -0,0 +1,2038 @@ +/* + * MPR WNet functions + * + * Copyright 1999 Ulrich Weigand + * Copyright 2004 Juan Lang + * + * 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 +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "winnls.h" +#include "winnetwk.h" +#include "npapi.h" +#include "winreg.h" +#include "winuser.h" +#include "wine/debug.h" +#include "wine/unicode.h" +#include "mprres.h" +#include "wnetpriv.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mpr); + +/* Data structures representing network service providers. Assumes only one + * thread creates them, and that they are constant for the life of the process + * (and therefore doesn't synchronize access). + * FIXME: only basic provider data and enumeration-related data are implemented + * so far, need to implement the rest too. + */ +typedef struct _WNetProvider +{ + HMODULE hLib; + PWSTR name; + PF_NPGetCaps getCaps; + DWORD dwSpecVersion; + DWORD dwNetType; + DWORD dwEnumScopes; + PF_NPOpenEnum openEnum; + PF_NPEnumResource enumResource; + PF_NPCloseEnum closeEnum; +} WNetProvider, *PWNetProvider; + +typedef struct _WNetProviderTable +{ + LPWSTR entireNetwork; + DWORD numAllocated; + DWORD numProviders; + WNetProvider table[1]; +} WNetProviderTable, *PWNetProviderTable; + +#define WNET_ENUMERATOR_TYPE_NULL 0 +#define WNET_ENUMERATOR_TYPE_GLOBAL 1 +#define WNET_ENUMERATOR_TYPE_PROVIDER 2 +#define WNET_ENUMERATOR_TYPE_CONTEXT 3 + +/* An WNet enumerator. Note that the type doesn't correspond to the scope of + * the enumeration; it represents one of the following types: + * - a 'null' enumeration, one that contains no members + * - a global enumeration, one that's executed across all providers + * - a provider-specific enumeration, one that's only executed by a single + * provider + * - a context enumeration. I know this contradicts what I just said about + * there being no correspondence between the scope and the type, but it's + * necessary for the special case that a "Entire Network" entry needs to + * be enumerated in an enumeration of the context scope. Thus an enumeration + * of the context scope results in a context type enumerator, which morphs + * into a global enumeration (so the enumeration continues across all + * providers). + */ +typedef struct _WNetEnumerator +{ + DWORD enumType; + DWORD providerIndex; + HANDLE handle; + BOOL providerDone; + DWORD dwScope; + DWORD dwType; + DWORD dwUsage; + LPNETRESOURCEW lpNet; +} WNetEnumerator, *PWNetEnumerator; + +#define BAD_PROVIDER_INDEX (DWORD)0xffffffff + +/* Returns an index (into the global WNetProviderTable) of the provider with + * the given name, or BAD_PROVIDER_INDEX if not found. + */ +static DWORD _findProviderIndexW(LPCWSTR lpProvider); + +PWNetProviderTable providerTable; + +/* + * Global provider table functions + */ + +static void _tryLoadProvider(PCWSTR provider) +{ + static const WCHAR servicePrefix[] = { 'S','y','s','t','e','m','\\', + 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\', + 'S','e','r','v','i','c','e','s','\\',0 }; + static const WCHAR serviceFmt[] = { '%','s','%','s','\\', + 'N','e','t','w','o','r','k','P','r','o','v','i','d','e','r',0 }; + WCHAR serviceName[MAX_PATH]; + HKEY hKey; + + TRACE("%s\n", debugstr_w(provider)); + snprintfW(serviceName, sizeof(serviceName) / sizeof(WCHAR), serviceFmt, + servicePrefix, provider); + serviceName[sizeof(serviceName) / sizeof(WCHAR) - 1] = '\0'; + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, serviceName, 0, KEY_READ, &hKey) == + ERROR_SUCCESS) + { + static const WCHAR szProviderPath[] = { 'P','r','o','v','i','d','e','r', + 'P','a','t','h',0 }; + WCHAR providerPath[MAX_PATH]; + DWORD type, size = sizeof(providerPath); + + if (RegQueryValueExW(hKey, szProviderPath, NULL, &type, + (LPBYTE)providerPath, &size) == ERROR_SUCCESS && type == REG_SZ) + { + static const WCHAR szProviderName[] = { 'N','a','m','e',0 }; + PWSTR name = NULL; + + size = 0; + RegQueryValueExW(hKey, szProviderName, NULL, NULL, NULL, &size); + if (size) + { + name = HeapAlloc(GetProcessHeap(), 0, size); + if (RegQueryValueExW(hKey, szProviderName, NULL, &type, + (LPBYTE)name, &size) != ERROR_SUCCESS || type != REG_SZ) + { + HeapFree(GetProcessHeap(), 0, name); + name = NULL; + } + } + if (name) + { + HMODULE hLib = LoadLibraryW(providerPath); + + if (hLib) + { + PF_NPGetCaps getCaps = (PF_NPGetCaps)GetProcAddress(hLib, + "NPGetCaps"); + + TRACE("loaded lib %p\n", hLib); + if (getCaps) + { + PWNetProvider provider = + &providerTable->table[providerTable->numProviders]; + + provider->hLib = hLib; + provider->name = name; + TRACE("name is %s\n", debugstr_w(name)); + provider->getCaps = getCaps; + provider->dwSpecVersion = getCaps(WNNC_SPEC_VERSION); + provider->dwNetType = getCaps(WNNC_NET_TYPE); + TRACE("net type is 0x%08lx\n", provider->dwNetType); + provider->dwEnumScopes = getCaps(WNNC_ENUMERATION); + if (provider->dwEnumScopes) + { + TRACE("supports enumeration\n"); + provider->openEnum = (PF_NPOpenEnum) + GetProcAddress(hLib, "NPOpenEnum"); + TRACE("openEnum is %p\n", provider->openEnum); + provider->enumResource = (PF_NPEnumResource) + GetProcAddress(hLib, "NPEnumResource"); + TRACE("enumResource is %p\n", + provider->enumResource); + provider->closeEnum = (PF_NPCloseEnum) + GetProcAddress(hLib, "NPCloseEnum"); + TRACE("closeEnum is %p\n", provider->closeEnum); + if (!provider->openEnum || !provider->enumResource + || !provider->closeEnum) + { + provider->openEnum = NULL; + provider->enumResource = NULL; + provider->closeEnum = NULL; + provider->dwEnumScopes = 0; + WARN("Couldn't load enumeration functions\n"); + } + } + providerTable->numProviders++; + } + else + { + WARN("Provider %s didn't export NPGetCaps\n", + debugstr_w(provider)); + HeapFree(GetProcessHeap(), 0, name); + FreeLibrary(hLib); + } + } + else + { + WARN("Couldn't load library %s for provider %s\n", + debugstr_w(providerPath), debugstr_w(provider)); + HeapFree(GetProcessHeap(), 0, name); + } + } + else + { + WARN("Couldn't get provider name for provider %s\n", + debugstr_w(provider)); + } + } + else + WARN("Couldn't open value %s\n", debugstr_w(szProviderPath)); + RegCloseKey(hKey); + } + else + WARN("Couldn't open service key for provider %s\n", + debugstr_w(provider)); +} + +void wnetInit(HINSTANCE hInstDll) +{ + static const WCHAR providerOrderKey[] = { 'S','y','s','t','e','m','\\', + 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\', + 'C','o','n','t','r','o','l','\\', + 'N','e','t','w','o','r','k','P','r','o','v','i','d','e','r','\\', + 'O','r','d','e','r',0 }; + static const WCHAR providerOrder[] = { 'P','r','o','v','i','d','e','r', + 'O','r','d','e','r',0 }; + HKEY hKey; + + if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, providerOrderKey, 0, KEY_READ, &hKey) + == ERROR_SUCCESS) + { + DWORD size = 0; + + RegQueryValueExW(hKey, providerOrder, NULL, NULL, NULL, &size); + if (size) + { + PWSTR providers = HeapAlloc(GetProcessHeap(), 0, size); + + if (providers) + { + DWORD type; + + if (RegQueryValueExW(hKey, providerOrder, NULL, &type, + (LPBYTE)providers, &size) == ERROR_SUCCESS && type == REG_SZ) + { + PWSTR ptr; + DWORD numToAllocate; + + TRACE("provider order is %s\n", debugstr_w(providers)); + /* first count commas as a heuristic for how many to + * allocate space for */ + for (ptr = providers, numToAllocate = 1; ptr; ) + { + ptr = strchrW(ptr, ','); + if (ptr) + numToAllocate++; + } + providerTable = + HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, + sizeof(WNetProviderTable) + + (numToAllocate - 1) * sizeof(WNetProvider)); + if (providerTable) + { + PWSTR ptrPrev; + int entireNetworkLen; + + entireNetworkLen = LoadStringW(hInstDll, + IDS_ENTIRENETWORK, NULL, 0); + providerTable->entireNetwork = HeapAlloc( + GetProcessHeap(), 0, (entireNetworkLen + 1) * + sizeof(WCHAR)); + if (providerTable->entireNetwork) + LoadStringW(hInstDll, IDS_ENTIRENETWORK, + providerTable->entireNetwork, + entireNetworkLen + 1); + providerTable->numAllocated = numToAllocate; + for (ptr = providers; ptr; ) + { + ptrPrev = ptr; + ptr = strchrW(ptr, ','); + if (ptr) + *ptr = '\0'; + _tryLoadProvider(ptrPrev); + } + } + } + HeapFree(GetProcessHeap(), 0, providers); + } + } + RegCloseKey(hKey); + } +} + +void wnetFree(void) +{ + if (providerTable) + { + DWORD i; + + for (i = 0; i < providerTable->numProviders; i++) + { + HeapFree(GetProcessHeap(), 0, providerTable->table[i].name); + FreeModule(providerTable->table[i].hLib); + } + HeapFree(GetProcessHeap(), 0, providerTable->entireNetwork); + HeapFree(GetProcessHeap(), 0, providerTable); + providerTable = NULL; + } +} + +static DWORD _findProviderIndexW(LPCWSTR lpProvider) +{ + DWORD ret = BAD_PROVIDER_INDEX; + + if (providerTable && providerTable->numProviders) + { + DWORD i; + + for (i = 0; i < providerTable->numProviders && + ret == BAD_PROVIDER_INDEX; i++) + if (!strcmpW(lpProvider, providerTable->table[i].name)) + ret = i; + } + return ret; +} + +/* + * Browsing Functions + */ + +static LPNETRESOURCEW _copyNetResourceForEnumW(LPNETRESOURCEW lpNet) +{ + LPNETRESOURCEW ret; + + if (lpNet) + { + ret = HeapAlloc(GetProcessHeap(), 0, sizeof(NETRESOURCEW)); + if (ret) + { + size_t len; + + memcpy(ret, lpNet, sizeof(ret)); + ret->lpLocalName = ret->lpComment = ret->lpProvider = NULL; + if (lpNet->lpRemoteName) + { + len = strlenW(lpNet->lpRemoteName) + 1; + ret->lpRemoteName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + if (ret->lpRemoteName) + strcpyW(ret->lpRemoteName, lpNet->lpRemoteName); + } + } + } + else + ret = NULL; + return ret; +} + +static void _freeEnumNetResource(LPNETRESOURCEW lpNet) +{ + if (lpNet) + { + HeapFree(GetProcessHeap(), 0, lpNet->lpRemoteName); + HeapFree(GetProcessHeap(), 0, lpNet); + } +} + +static PWNetEnumerator _createNullEnumerator(void) +{ + PWNetEnumerator ret = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, sizeof(WNetEnumerator)); + + if (ret) + ret->enumType = WNET_ENUMERATOR_TYPE_NULL; + return ret; +} + +static PWNetEnumerator _createGlobalEnumeratorW(DWORD dwScope, DWORD dwType, + DWORD dwUsage, LPNETRESOURCEW lpNet) +{ + PWNetEnumerator ret = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, sizeof(WNetEnumerator)); + + if (ret) + { + ret->enumType = WNET_ENUMERATOR_TYPE_GLOBAL; + ret->dwScope = dwScope; + ret->dwType = dwType; + ret->dwUsage = dwUsage; + ret->lpNet = _copyNetResourceForEnumW(lpNet); + } + return ret; +} + +static PWNetEnumerator _createProviderEnumerator(DWORD dwScope, DWORD dwType, + DWORD dwUsage, DWORD index, HANDLE handle) +{ + PWNetEnumerator ret; + + if (!providerTable || index >= providerTable->numProviders) + ret = NULL; + else + { + ret = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WNetEnumerator)); + if (ret) + { + ret->enumType = WNET_ENUMERATOR_TYPE_PROVIDER; + ret->providerIndex = index; + ret->dwScope = dwScope; + ret->dwType = dwType; + ret->dwUsage = dwUsage; + ret->handle = handle; + } + } + return ret; +} + +static PWNetEnumerator _createContextEnumerator(DWORD dwScope, DWORD dwType, + DWORD dwUsage) +{ + PWNetEnumerator ret = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, sizeof(WNetEnumerator)); + + if (ret) + { + ret->enumType = WNET_ENUMERATOR_TYPE_CONTEXT; + ret->dwScope = dwScope; + ret->dwType = dwType; + ret->dwUsage = dwUsage; + } + return ret; +} + +/* Thunks the array of wide-string LPNETRESOURCEs lpNetArrayIn into buffer + * lpBuffer, with size *lpBufferSize. lpNetArrayIn contains *lpcCount entries + * to start. On return, *lpcCount reflects the number thunked into lpBuffer. + * Returns WN_SUCCESS on success (all of lpNetArrayIn thunked), WN_MORE_DATA + * if not all members of the array could be thunked, and something else on + * failure. + */ +static DWORD _thunkNetResourceArrayWToA(const LPNETRESOURCEW lpNetArrayIn, + LPDWORD lpcCount, LPVOID lpBuffer, LPDWORD lpBufferSize) +{ + DWORD i, numToThunk, totalBytes, ret; + LPSTR strNext; + + if (!lpNetArrayIn) + return WN_BAD_POINTER; + if (!lpcCount) + return WN_BAD_POINTER; + if (*lpcCount == -1) + return WN_BAD_VALUE; + if (!lpBuffer) + return WN_BAD_POINTER; + if (!lpBufferSize) + return WN_BAD_POINTER; + + for (i = 0, numToThunk = 0, totalBytes = 0; i < *lpcCount; i++) + { + LPNETRESOURCEW lpNet = lpNetArrayIn + i; + + totalBytes += sizeof(NETRESOURCEA); + if (lpNet->lpLocalName) + totalBytes += WideCharToMultiByte(CP_ACP, 0, lpNet->lpLocalName, + -1, NULL, 0, NULL, NULL); + if (lpNet->lpRemoteName) + totalBytes += WideCharToMultiByte(CP_ACP, 0, lpNet->lpRemoteName, + -1, NULL, 0, NULL, NULL); + if (lpNet->lpComment) + totalBytes += WideCharToMultiByte(CP_ACP, 0, lpNet->lpComment, + -1, NULL, 0, NULL, NULL); + if (lpNet->lpProvider) + totalBytes += WideCharToMultiByte(CP_ACP, 0, lpNet->lpProvider, + -1, NULL, 0, NULL, NULL); + if (totalBytes < *lpBufferSize) + numToThunk = i + 1; + } + strNext = (LPSTR)((LPBYTE)lpBuffer + numToThunk * sizeof(NETRESOURCEA)); + for (i = 0; i < numToThunk; i++) + { + LPNETRESOURCEA lpNetOut = (LPNETRESOURCEA)lpBuffer + i; + LPNETRESOURCEW lpNetIn = lpNetArrayIn + i; + + memcpy(lpNetOut, lpNetIn, sizeof(NETRESOURCEA)); + /* lie about string lengths, we already verified how many + * we have space for above + */ + if (lpNetIn->lpLocalName) + { + lpNetOut->lpLocalName = strNext; + strNext += WideCharToMultiByte(CP_ACP, 0, lpNetIn->lpLocalName, -1, + lpNetOut->lpLocalName, *lpBufferSize, NULL, NULL); + } + if (lpNetIn->lpRemoteName) + { + lpNetOut->lpRemoteName = strNext; + strNext += WideCharToMultiByte(CP_ACP, 0, lpNetIn->lpRemoteName, -1, + lpNetOut->lpRemoteName, *lpBufferSize, NULL, NULL); + } + if (lpNetIn->lpComment) + { + lpNetOut->lpComment = strNext; + strNext += WideCharToMultiByte(CP_ACP, 0, lpNetIn->lpComment, -1, + lpNetOut->lpComment, *lpBufferSize, NULL, NULL); + } + if (lpNetIn->lpProvider) + { + lpNetOut->lpProvider = strNext; + strNext += WideCharToMultiByte(CP_ACP, 0, lpNetIn->lpProvider, -1, + lpNetOut->lpProvider, *lpBufferSize, NULL, NULL); + } + } + ret = numToThunk < *lpcCount ? WN_MORE_DATA : WN_SUCCESS; + TRACE("numToThunk is %ld, *lpcCount is %ld, returning %ld\n", numToThunk, + *lpcCount, ret); + return ret; +} + +/* Thunks the array of multibyte-string LPNETRESOURCEs lpNetArrayIn into buffer + * lpBuffer, with size *lpBufferSize. lpNetArrayIn contains *lpcCount entries + * to start. On return, *lpcCount reflects the number thunked into lpBuffer. + * Returns WN_SUCCESS on success (all of lpNetArrayIn thunked), WN_MORE_DATA + * if not all members of the array could be thunked, and something else on + * failure. + */ +static DWORD _thunkNetResourceArrayAToW(const LPNETRESOURCEA lpNetArrayIn, + LPDWORD lpcCount, LPVOID lpBuffer, LPDWORD lpBufferSize) +{ + DWORD i, numToThunk, totalBytes, ret; + LPWSTR strNext; + + if (!lpNetArrayIn) + return WN_BAD_POINTER; + if (!lpcCount) + return WN_BAD_POINTER; + if (*lpcCount == -1) + return WN_BAD_VALUE; + if (!lpBuffer) + return WN_BAD_POINTER; + if (!lpBufferSize) + return WN_BAD_POINTER; + + for (i = 0, numToThunk = 0, totalBytes = 0; i < *lpcCount; i++) + { + LPNETRESOURCEA lpNet = lpNetArrayIn + i; + + totalBytes += sizeof(NETRESOURCEW); + if (lpNet->lpLocalName) + totalBytes += MultiByteToWideChar(CP_ACP, 0, lpNet->lpLocalName, + -1, NULL, 0) * sizeof(WCHAR); + if (lpNet->lpRemoteName) + totalBytes += MultiByteToWideChar(CP_ACP, 0, lpNet->lpRemoteName, + -1, NULL, 0) * sizeof(WCHAR); + if (lpNet->lpComment) + totalBytes += MultiByteToWideChar(CP_ACP, 0, lpNet->lpComment, + -1, NULL, 0) * sizeof(WCHAR); + if (lpNet->lpProvider) + totalBytes += MultiByteToWideChar(CP_ACP, 0, lpNet->lpProvider, + -1, NULL, 0) * sizeof(WCHAR); + if (totalBytes < *lpBufferSize) + numToThunk = i + 1; + } + strNext = (LPWSTR)((LPBYTE)lpBuffer + numToThunk * sizeof(NETRESOURCEW)); + for (i = 0; i < numToThunk; i++) + { + LPNETRESOURCEW lpNetOut = (LPNETRESOURCEW)lpBuffer + i; + LPNETRESOURCEA lpNetIn = lpNetArrayIn + i; + + memcpy(lpNetOut, lpNetIn, sizeof(NETRESOURCEW)); + /* lie about string lengths, we already verified how many + * we have space for above + */ + if (lpNetIn->lpLocalName) + { + lpNetOut->lpLocalName = strNext; + strNext += MultiByteToWideChar(CP_ACP, 0, lpNetIn->lpLocalName, + -1, lpNetOut->lpLocalName, *lpBufferSize); + } + if (lpNetIn->lpRemoteName) + { + lpNetOut->lpRemoteName = strNext; + strNext += MultiByteToWideChar(CP_ACP, 0, lpNetIn->lpRemoteName, + -1, lpNetOut->lpRemoteName, *lpBufferSize); + } + if (lpNetIn->lpComment) + { + lpNetOut->lpComment = strNext; + strNext += MultiByteToWideChar(CP_ACP, 0, lpNetIn->lpComment, + -1, lpNetOut->lpComment, *lpBufferSize); + } + if (lpNetIn->lpProvider) + { + lpNetOut->lpProvider = strNext; + strNext += MultiByteToWideChar(CP_ACP, 0, lpNetIn->lpProvider, + -1, lpNetOut->lpProvider, *lpBufferSize); + } + } + ret = numToThunk < *lpcCount ? WN_MORE_DATA : WN_SUCCESS; + TRACE("numToThunk is %ld, *lpcCount is %ld, returning %ld\n", numToThunk, + *lpcCount, ret); + return ret; +} + +/********************************************************************* + * WNetOpenEnumA [MPR.@] + * + * See comments for WNetOpenEnumW. + */ +DWORD WINAPI WNetOpenEnumA( DWORD dwScope, DWORD dwType, DWORD dwUsage, + LPNETRESOURCEA lpNet, LPHANDLE lphEnum ) +{ + DWORD ret; + + TRACE( "(%08lX, %08lX, %08lX, %p, %p)\n", + dwScope, dwType, dwUsage, lpNet, lphEnum ); + + if (!lphEnum) + ret = WN_BAD_POINTER; + else if (!providerTable || providerTable->numProviders == 0) + ret = WN_NO_NETWORK; + else + { + if (lpNet) + { + LPNETRESOURCEW lpNetWide = NULL; + BYTE buf[1024]; + DWORD size = sizeof(buf), count = 1; + BOOL allocated = FALSE; + + ret = _thunkNetResourceArrayAToW(lpNet, &count, buf, &size); + if (ret == WN_MORE_DATA) + { + lpNetWide = HeapAlloc(GetProcessHeap(), 0, + size); + if (lpNetWide) + { + ret = _thunkNetResourceArrayAToW(lpNet, &count, lpNetWide, + &size); + allocated = TRUE; + } + else + ret = WN_OUT_OF_MEMORY; + } + else if (ret == WN_SUCCESS) + lpNetWide = (LPNETRESOURCEW)buf; + if (ret == WN_SUCCESS) + ret = WNetOpenEnumW(dwScope, dwType, dwUsage, lpNetWide, + lphEnum); + if (allocated && lpNetWide) + HeapFree(GetProcessHeap(), 0, lpNetWide); + } + else + ret = WNetOpenEnumW(dwScope, dwType, dwUsage, NULL, lphEnum); + } + if (ret) + SetLastError(ret); + TRACE("Returning %ld\n", ret); + return ret; +} + +/********************************************************************* + * WNetOpenEnumW [MPR.@] + * + * Network enumeration has way too many parameters, so I'm not positive I got + * them right. What I've got so far: + * + * - If the scope is RESOURCE_GLOBALNET, and no LPNETRESOURCE is passed, + * all the network providers should be enumerated. + * + * - If the scope is RESOURCE_GLOBALNET, and LPNETRESOURCE is passed, and + * and neither the LPNETRESOURCE's lpRemoteName nor the LPNETRESOURCE's + * lpProvider is set, all the network providers should be enumerated. + * (This means the enumeration is a list of network providers, not that the + * enumeration is passed on to the providers.) + * + * - If the scope is RESOURCE_GLOBALNET, and LPNETRESOURCE is passed, and the + * resource matches the "Entire Network" resource (no remote name, no + * provider, comment is the "Entire Network" string), a RESOURCE_GLOBALNET + * enumeration is done on every network provider. + * + * - If the scope is RESOURCE_GLOBALNET, and LPNETRESOURCE is passed, and + * the LPNETRESOURCE's lpProvider is set, enumeration will be passed through + * only to the given network provider. + * + * - If the scope is RESOURCE_GLOBALNET, and LPNETRESOURCE is passed, and + * no lpProvider is set, enumeration will be tried on every network provider, + * in the order in which they're loaded. + * + * - The LPNETRESOURCE should be disregarded for scopes besides + * RESOURCE_GLOBALNET. MSDN states that lpNet must be NULL if dwScope is not + * RESOURCE_GLOBALNET, but Windows doesn't return an error if it isn't NULL. + * + * - If the scope is RESOURCE_CONTEXT, MS includes an "Entire Network" net + * resource in the enumerated list, as well as any machines in your + * workgroup. The machines in your workgroup come from doing a + * RESOURCE_CONTEXT enumeration of every Network Provider. + */ +DWORD WINAPI WNetOpenEnumW( DWORD dwScope, DWORD dwType, DWORD dwUsage, + LPNETRESOURCEW lpNet, LPHANDLE lphEnum ) +{ + DWORD ret; + + TRACE( "(%08lX, %08lX, %08lX, %p, %p)\n", + dwScope, dwType, dwUsage, lpNet, lphEnum ); + + if (!lphEnum) + ret = WN_BAD_POINTER; + else if (!providerTable || providerTable->numProviders == 0) + ret = WN_NO_NETWORK; + else + { + switch (dwScope) + { + case RESOURCE_GLOBALNET: + if (lpNet) + { + if (lpNet->lpProvider) + { + DWORD index = _findProviderIndexW(lpNet->lpProvider); + + if (index != BAD_PROVIDER_INDEX) + { + if (providerTable->table[index].openEnum && + providerTable->table[index].dwEnumScopes & dwScope) + { + HANDLE handle; + + ret = providerTable->table[index].openEnum( + dwScope, dwType, dwUsage, lpNet, &handle); + if (ret == WN_SUCCESS) + { + *lphEnum = + (HANDLE)_createProviderEnumerator( + dwScope, dwType, dwUsage, index, handle); + ret = *lphEnum ? WN_SUCCESS : + WN_OUT_OF_MEMORY; + } + } + else + ret = WN_NOT_SUPPORTED; + } + else + ret = WN_BAD_PROVIDER; + } + else if (lpNet->lpRemoteName) + { + *lphEnum = (HANDLE)_createGlobalEnumeratorW(dwScope, + dwType, dwUsage, lpNet); + ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY; + } + else + { + if (lpNet->lpComment && !strcmpW(lpNet->lpComment, + providerTable->entireNetwork)) + { + /* comment matches the "Entire Network", enumerate + * global scope of every provider + */ + *lphEnum = (HANDLE)_createGlobalEnumeratorW(dwScope, + dwType, dwUsage, lpNet); + } + else + { + /* this is the same as not having passed lpNet */ + *lphEnum = (HANDLE)_createGlobalEnumeratorW(dwScope, + dwType, dwUsage, NULL); + } + ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY; + } + } + else + { + *lphEnum = (HANDLE)_createGlobalEnumeratorW(dwScope, dwType, + dwUsage, lpNet); + ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY; + } + break; + case RESOURCE_CONTEXT: + *lphEnum = (HANDLE)_createContextEnumerator(dwScope, dwType, + dwUsage); + ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY; + break; + case RESOURCE_REMEMBERED: + case RESOURCE_CONNECTED: + *lphEnum = (HANDLE)_createNullEnumerator(); + ret = *lphEnum ? WN_SUCCESS : WN_OUT_OF_MEMORY; + break; + default: + WARN("unknown scope 0x%08lx\n", dwScope); + ret = WN_BAD_VALUE; + } + } + if (ret) + SetLastError(ret); + TRACE("Returning %ld\n", ret); + return ret; +} + +/********************************************************************* + * WNetEnumResourceA [MPR.@] + */ +DWORD WINAPI WNetEnumResourceA( HANDLE hEnum, LPDWORD lpcCount, + LPVOID lpBuffer, LPDWORD lpBufferSize ) +{ + DWORD ret; + + TRACE( "(%p, %p, %p, %p)\n", hEnum, lpcCount, lpBuffer, lpBufferSize ); + + if (!hEnum) + ret = WN_BAD_POINTER; + else if (!lpcCount) + ret = WN_BAD_POINTER; + if (!lpBuffer) + ret = WN_BAD_POINTER; + else if (!lpBufferSize) + ret = WN_BAD_POINTER; + else if (*lpBufferSize < sizeof(NETRESOURCEA)) + { + *lpBufferSize = sizeof(NETRESOURCEA); + ret = WN_MORE_DATA; + } + else + { + DWORD localCount = *lpcCount, localSize = *lpBufferSize; + LPVOID localBuffer = HeapAlloc(GetProcessHeap(), 0, localSize); + + if (localBuffer) + { + ret = WNetEnumResourceW(hEnum, &localCount, localBuffer, + &localSize); + if (ret == WN_SUCCESS || (ret == WN_MORE_DATA && localCount != -1)) + { + /* FIXME: this isn't necessarily going to work in the case of + * WN_MORE_DATA, because our enumerator may have moved on to + * the next provider. MSDN states that a large (16KB) buffer + * size is the appropriate usage of this function, so + * hopefully it won't be an issue. + */ + ret = _thunkNetResourceArrayWToA((LPNETRESOURCEW)localBuffer, + &localCount, lpBuffer, lpBufferSize); + *lpcCount = localCount; + } + HeapFree(GetProcessHeap(), 0, localBuffer); + } + else + ret = WN_OUT_OF_MEMORY; + } + if (ret) + SetLastError(ret); + TRACE("Returning %ld\n", ret); + return ret; +} + +static DWORD _countProviderBytesW(PWNetProvider provider) +{ + DWORD ret; + + if (provider) + { + ret = sizeof(NETRESOURCEW); + ret += 2 * (strlenW(provider->name) + 1) * sizeof(WCHAR); + } + else + ret = 0; + return ret; +} + +static DWORD _enumerateProvidersW(PWNetEnumerator enumerator, LPDWORD lpcCount, + LPVOID lpBuffer, LPDWORD lpBufferSize) +{ + DWORD ret; + + if (!enumerator) + return WN_BAD_POINTER; + if (enumerator->enumType != WNET_ENUMERATOR_TYPE_GLOBAL) + return WN_BAD_VALUE; + if (!lpcCount) + return WN_BAD_POINTER; + if (!lpBuffer) + return WN_BAD_POINTER; + if (!lpBufferSize) + return WN_BAD_POINTER; + if (*lpBufferSize < sizeof(NETRESOURCEA)) + return WN_MORE_DATA; + + if (!providerTable || enumerator->providerIndex >= + providerTable->numProviders) + ret = WN_NO_MORE_ENTRIES; + else + { + DWORD bytes = 0, count = 0, countLimit, i; + LPNETRESOURCEW resource; + LPWSTR strNext; + + countLimit = *lpcCount == -1 ? + providerTable->numProviders - enumerator->providerIndex : *lpcCount; + while (count < countLimit && bytes < *lpBufferSize) + { + DWORD bytesNext = _countProviderBytesW( + &providerTable->table[count + enumerator->providerIndex]); + + if (bytes + bytesNext < *lpBufferSize) + { + bytes += bytesNext; + count++; + } + } + strNext = (LPWSTR)((LPBYTE)lpBuffer + count * sizeof(NETRESOURCEW)); + for (i = 0, resource = (LPNETRESOURCEW)lpBuffer; i < count; + i++, resource++) + { + resource->dwScope = RESOURCE_GLOBALNET; + resource->dwType = RESOURCETYPE_ANY; + resource->dwDisplayType = RESOURCEDISPLAYTYPE_NETWORK; + resource->dwUsage = RESOURCEUSAGE_CONTAINER | + RESOURCEUSAGE_RESERVED; + resource->lpLocalName = NULL; + resource->lpRemoteName = strNext; + strcpyW(resource->lpRemoteName, + providerTable->table[i + enumerator->providerIndex].name); + strNext += strlenW(resource->lpRemoteName) + 1; + resource->lpComment = NULL; + resource->lpProvider = strNext; + strcpyW(resource->lpProvider, + providerTable->table[i + enumerator->providerIndex].name); + strNext += strlenW(resource->lpProvider) + 1; + } + enumerator->providerIndex += count; + *lpcCount = count; + ret = count > 0 ? WN_SUCCESS : WN_MORE_DATA; + } + TRACE("Returning %ld\n", ret); + return ret; +} + +/* Advances the enumerator (assumed to be a global enumerator) to the next + * provider that supports the enumeration scope passed to WNetOpenEnum. Does + * not open a handle with the next provider. + * If the existing handle is NULL, may leave the enumerator unchanged, since + * the current provider may support the desired scope. + * If the existing handle is not NULL, closes it before moving on. + * Returns WN_SUCCESS on success, WN_NO_MORE_ENTRIES if there is no available + * provider, and another error on failure. + */ +static DWORD _globalEnumeratorAdvance(PWNetEnumerator enumerator) +{ + if (!enumerator) + return WN_BAD_POINTER; + if (enumerator->enumType != WNET_ENUMERATOR_TYPE_GLOBAL) + return WN_BAD_VALUE; + if (!providerTable || enumerator->providerIndex >= + providerTable->numProviders) + return WN_NO_MORE_ENTRIES; + + if (enumerator->providerDone) + { + enumerator->providerDone = FALSE; + if (enumerator->handle) + { + providerTable->table[enumerator->providerIndex].closeEnum( + enumerator->handle); + enumerator->handle = NULL; + enumerator->providerIndex++; + } + for (; enumerator->providerIndex < providerTable->numProviders && + !(enumerator->dwScope & providerTable->table + [enumerator->providerIndex].dwEnumScopes); + enumerator->providerIndex++) + ; + } + return enumerator->providerIndex < providerTable->numProviders ? + WN_SUCCESS : WN_NO_MORE_ENTRIES; +} + +/* "Passes through" call to the next provider that supports the enumeration + * type. + * FIXME: if one call to a provider's enumerator succeeds while there's still + * space in lpBuffer, I don't call to the next provider. The caller may not + * expect that it should call EnumResourceW again with a return value of + * WN_SUCCESS (depending what *lpcCount was to begin with). That means strings + * may have to be moved around a bit, ick. + */ +static DWORD _enumerateGlobalPassthroughW(PWNetEnumerator enumerator, + LPDWORD lpcCount, LPVOID lpBuffer, LPDWORD lpBufferSize) +{ + DWORD ret; + + if (!enumerator) + return WN_BAD_POINTER; + if (enumerator->enumType != WNET_ENUMERATOR_TYPE_GLOBAL) + return WN_BAD_VALUE; + if (!lpcCount) + return WN_BAD_POINTER; + if (!lpBuffer) + return WN_BAD_POINTER; + if (!lpBufferSize) + return WN_BAD_POINTER; + if (*lpBufferSize < sizeof(NETRESOURCEW)) + return WN_MORE_DATA; + + ret = _globalEnumeratorAdvance(enumerator); + if (ret == WN_SUCCESS) + { + ret = providerTable->table[enumerator->providerIndex]. + openEnum(enumerator->dwScope, enumerator->dwType, + enumerator->dwUsage, enumerator->lpNet, + &enumerator->handle); + if (ret == WN_SUCCESS) + { + ret = providerTable->table[enumerator->providerIndex]. + enumResource(enumerator->handle, lpcCount, lpBuffer, + lpBufferSize); + if (ret != WN_MORE_DATA) + enumerator->providerDone = TRUE; + } + } + TRACE("Returning %ld\n", ret); + return ret; +} + +static DWORD _enumerateGlobalW(PWNetEnumerator enumerator, LPDWORD lpcCount, + LPVOID lpBuffer, LPDWORD lpBufferSize) +{ + DWORD ret; + + if (!enumerator) + return WN_BAD_POINTER; + if (enumerator->enumType != WNET_ENUMERATOR_TYPE_GLOBAL) + return WN_BAD_VALUE; + if (!lpcCount) + return WN_BAD_POINTER; + if (!lpBuffer) + return WN_BAD_POINTER; + if (!lpBufferSize) + return WN_BAD_POINTER; + if (*lpBufferSize < sizeof(NETRESOURCEW)) + return WN_MORE_DATA; + if (!providerTable) + return WN_NO_NETWORK; + + switch (enumerator->dwScope) + { + case RESOURCE_GLOBALNET: + if (enumerator->lpNet) + ret = _enumerateGlobalPassthroughW(enumerator, lpcCount, + lpBuffer, lpBufferSize); + else + ret = _enumerateProvidersW(enumerator, lpcCount, lpBuffer, + lpBufferSize); + break; + case RESOURCE_CONTEXT: + ret = _enumerateGlobalPassthroughW(enumerator, lpcCount, lpBuffer, + lpBufferSize); + break; + default: + WARN("unexpected scope 0x%08lx\n", enumerator->dwScope); + ret = WN_NO_MORE_ENTRIES; + } + TRACE("Returning %ld\n", ret); + return ret; +} + +static DWORD _enumerateProviderW(PWNetEnumerator enumerator, LPDWORD lpcCount, + LPVOID lpBuffer, LPDWORD lpBufferSize) +{ + if (!enumerator) + return WN_BAD_POINTER; + if (enumerator->enumType != WNET_ENUMERATOR_TYPE_PROVIDER) + return WN_BAD_VALUE; + if (!enumerator->handle) + return WN_BAD_VALUE; + if (!lpcCount) + return WN_BAD_POINTER; + if (!lpBuffer) + return WN_BAD_POINTER; + if (!lpBufferSize) + return WN_BAD_POINTER; + if (!providerTable) + return WN_NO_NETWORK; + if (enumerator->providerIndex >= providerTable->numProviders) + return WN_NO_MORE_ENTRIES; + if (!providerTable->table[enumerator->providerIndex].enumResource) + return WN_BAD_VALUE; + return providerTable->table[enumerator->providerIndex].enumResource( + enumerator->handle, lpcCount, lpBuffer, lpBufferSize); +} + +static DWORD _enumerateContextW(PWNetEnumerator enumerator, LPDWORD lpcCount, + LPVOID lpBuffer, LPDWORD lpBufferSize) +{ + DWORD ret; + size_t cchEntireNetworkLen, bytesNeeded; + + if (!enumerator) + return WN_BAD_POINTER; + if (enumerator->enumType != WNET_ENUMERATOR_TYPE_CONTEXT) + return WN_BAD_VALUE; + if (!lpcCount) + return WN_BAD_POINTER; + if (!lpBuffer) + return WN_BAD_POINTER; + if (!lpBufferSize) + return WN_BAD_POINTER; + if (!providerTable) + return WN_NO_NETWORK; + + cchEntireNetworkLen = strlenW(providerTable->entireNetwork) + 1; + bytesNeeded = sizeof(NETRESOURCEW) + cchEntireNetworkLen * sizeof(WCHAR); + if (*lpBufferSize < bytesNeeded) + { + *lpBufferSize = bytesNeeded; + ret = WN_MORE_DATA; + } + else + { + LPNETRESOURCEW lpNet = (LPNETRESOURCEW)lpBuffer; + + lpNet->dwScope = RESOURCE_GLOBALNET; + lpNet->dwType = enumerator->dwType; + lpNet->dwDisplayType = RESOURCEDISPLAYTYPE_ROOT; + lpNet->dwUsage = RESOURCEUSAGE_CONTAINER; + lpNet->lpLocalName = NULL; + lpNet->lpRemoteName = NULL; + lpNet->lpProvider = NULL; + /* odd, but correct: put comment at end of buffer, so it won't get + * overwritten by subsequent calls to a provider's enumResource + */ + lpNet->lpComment = (LPWSTR)((LPBYTE)lpBuffer + *lpBufferSize - + (cchEntireNetworkLen * sizeof(WCHAR))); + strcpyW(lpNet->lpComment, providerTable->entireNetwork); + ret = WN_SUCCESS; + } + if (ret == WN_SUCCESS) + { + DWORD bufferSize = *lpBufferSize - bytesNeeded; + + /* "Entire Network" entry enumerated--morph this into a global + * enumerator. enumerator->lpNet continues to be NULL, since it has + * no meaning when the scope isn't RESOURCE_GLOBALNET. + */ + enumerator->enumType = WNET_ENUMERATOR_TYPE_GLOBAL; + ret = _enumerateGlobalW(enumerator, lpcCount, + (LPBYTE)lpBuffer + bytesNeeded, &bufferSize); + if (ret == WN_SUCCESS) + { + /* reflect the fact that we already enumerated "Entire Network" */ + lpcCount++; + *lpBufferSize = bufferSize + bytesNeeded; + } + else + { + /* the provider enumeration failed, but we already succeeded in + * enumerating "Entire Network"--leave type as global to allow a + * retry, but indicate success with a count of one. + */ + ret = WN_SUCCESS; + *lpcCount = 1; + *lpBufferSize = bytesNeeded; + } + } + TRACE("Returning %ld\n", ret); + return ret; +} + +/********************************************************************* + * WNetEnumResourceW [MPR.@] + */ +DWORD WINAPI WNetEnumResourceW( HANDLE hEnum, LPDWORD lpcCount, + LPVOID lpBuffer, LPDWORD lpBufferSize ) +{ + DWORD ret; + + TRACE( "(%p, %p, %p, %p)\n", hEnum, lpcCount, lpBuffer, lpBufferSize ); + + if (!hEnum) + ret = WN_BAD_POINTER; + else if (!lpcCount) + ret = WN_BAD_POINTER; + else if (!lpBuffer) + ret = WN_BAD_POINTER; + else if (!lpBufferSize) + ret = WN_BAD_POINTER; + else if (*lpBufferSize < sizeof(NETRESOURCEW)) + { + *lpBufferSize = sizeof(NETRESOURCEW); + ret = WN_MORE_DATA; + } + else + { + PWNetEnumerator enumerator = (PWNetEnumerator)hEnum; + + switch (enumerator->enumType) + { + case WNET_ENUMERATOR_TYPE_NULL: + ret = WN_NO_MORE_ENTRIES; + break; + case WNET_ENUMERATOR_TYPE_GLOBAL: + ret = _enumerateGlobalW(enumerator, lpcCount, lpBuffer, + lpBufferSize); + break; + case WNET_ENUMERATOR_TYPE_PROVIDER: + ret = _enumerateProviderW(enumerator, lpcCount, lpBuffer, + lpBufferSize); + break; + case WNET_ENUMERATOR_TYPE_CONTEXT: + ret = _enumerateContextW(enumerator, lpcCount, lpBuffer, + lpBufferSize); + break; + default: + WARN("bogus enumerator type!\n"); + ret = WN_NO_NETWORK; + } + } + if (ret) + SetLastError(ret); + TRACE("Returning %ld\n", ret); + return ret; +} + +/********************************************************************* + * WNetCloseEnum [MPR.@] + */ +DWORD WINAPI WNetCloseEnum( HANDLE hEnum ) +{ + DWORD ret; + + TRACE( "(%p)\n", hEnum ); + + if (hEnum) + { + PWNetEnumerator enumerator = (PWNetEnumerator)hEnum; + + switch (enumerator->enumType) + { + case WNET_ENUMERATOR_TYPE_NULL: + ret = WN_SUCCESS; + break; + case WNET_ENUMERATOR_TYPE_GLOBAL: + if (enumerator->lpNet) + _freeEnumNetResource(enumerator->lpNet); + if (enumerator->handle) + providerTable->table[enumerator->providerIndex]. + closeEnum(enumerator->handle); + ret = WN_SUCCESS; + break; + case WNET_ENUMERATOR_TYPE_PROVIDER: + if (enumerator->handle) + providerTable->table[enumerator->providerIndex]. + closeEnum(enumerator->handle); + ret = WN_SUCCESS; + break; + default: + WARN("bogus enumerator type!\n"); + ret = WN_BAD_HANDLE; + } + HeapFree(GetProcessHeap(), 0, hEnum); + } + else + ret = WN_BAD_HANDLE; + if (ret) + SetLastError(ret); + TRACE("Returning %ld\n", ret); + return ret; +} + +/********************************************************************* + * WNetGetResourceInformationA [MPR.@] + */ +DWORD WINAPI WNetGetResourceInformationA( LPNETRESOURCEA lpNetResource, + LPVOID lpBuffer, LPDWORD cbBuffer, + LPSTR *lplpSystem ) +{ + FIXME( "(%p, %p, %p, %p): stub\n", + lpNetResource, lpBuffer, cbBuffer, lplpSystem ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/********************************************************************* + * WNetGetResourceInformationW [MPR.@] + */ +DWORD WINAPI WNetGetResourceInformationW( LPNETRESOURCEW lpNetResource, + LPVOID lpBuffer, LPDWORD cbBuffer, + LPWSTR *lplpSystem ) +{ + FIXME( "(%p, %p, %p, %p): stub\n", + lpNetResource, lpBuffer, cbBuffer, lplpSystem ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/********************************************************************* + * WNetGetResourceParentA [MPR.@] + */ +DWORD WINAPI WNetGetResourceParentA( LPNETRESOURCEA lpNetResource, + LPVOID lpBuffer, LPDWORD lpBufferSize ) +{ + FIXME( "(%p, %p, %p): stub\n", + lpNetResource, lpBuffer, lpBufferSize ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/********************************************************************* + * WNetGetResourceParentW [MPR.@] + */ +DWORD WINAPI WNetGetResourceParentW( LPNETRESOURCEW lpNetResource, + LPVOID lpBuffer, LPDWORD lpBufferSize ) +{ + FIXME( "(%p, %p, %p): stub\n", + lpNetResource, lpBuffer, lpBufferSize ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + + + +/* + * Connection Functions + */ + +/********************************************************************* + * WNetAddConnectionA [MPR.@] + */ +DWORD WINAPI WNetAddConnectionA( LPCSTR lpRemoteName, LPCSTR lpPassword, + LPCSTR lpLocalName ) +{ + FIXME( "(%s, %p, %s): stub\n", + debugstr_a(lpRemoteName), lpPassword, debugstr_a(lpLocalName) ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/********************************************************************* + * WNetAddConnectionW [MPR.@] + */ +DWORD WINAPI WNetAddConnectionW( LPCWSTR lpRemoteName, LPCWSTR lpPassword, + LPCWSTR lpLocalName ) +{ + FIXME( "(%s, %p, %s): stub\n", + debugstr_w(lpRemoteName), lpPassword, debugstr_w(lpLocalName) ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/********************************************************************* + * WNetAddConnection2A [MPR.@] + */ +DWORD WINAPI WNetAddConnection2A( LPNETRESOURCEA lpNetResource, + LPCSTR lpPassword, LPCSTR lpUserID, + DWORD dwFlags ) +{ + FIXME( "(%p, %p, %s, 0x%08lX): stub\n", + lpNetResource, lpPassword, debugstr_a(lpUserID), dwFlags ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/********************************************************************* + * WNetAddConnection2W [MPR.@] + */ +DWORD WINAPI WNetAddConnection2W( LPNETRESOURCEW lpNetResource, + LPCWSTR lpPassword, LPCWSTR lpUserID, + DWORD dwFlags ) +{ + FIXME( "(%p, %p, %s, 0x%08lX): stub\n", + lpNetResource, lpPassword, debugstr_w(lpUserID), dwFlags ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/********************************************************************* + * WNetAddConnection3A [MPR.@] + */ +DWORD WINAPI WNetAddConnection3A( HWND hwndOwner, LPNETRESOURCEA lpNetResource, + LPCSTR lpPassword, LPCSTR lpUserID, + DWORD dwFlags ) +{ + FIXME( "(%p, %p, %p, %s, 0x%08lX), stub\n", + hwndOwner, lpNetResource, lpPassword, debugstr_a(lpUserID), dwFlags ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/********************************************************************* + * WNetAddConnection3W [MPR.@] + */ +DWORD WINAPI WNetAddConnection3W( HWND hwndOwner, LPNETRESOURCEW lpNetResource, + LPCWSTR lpPassword, LPCWSTR lpUserID, + DWORD dwFlags ) +{ + FIXME( "(%p, %p, %p, %s, 0x%08lX), stub\n", + hwndOwner, lpNetResource, lpPassword, debugstr_w(lpUserID), dwFlags ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/***************************************************************** + * WNetUseConnectionA [MPR.@] + */ +DWORD WINAPI WNetUseConnectionA( HWND hwndOwner, LPNETRESOURCEA lpNetResource, + LPCSTR lpPassword, LPCSTR lpUserID, DWORD dwFlags, + LPSTR lpAccessName, LPDWORD lpBufferSize, + LPDWORD lpResult ) +{ + FIXME( "(%p, %p, %p, %s, 0x%08lX, %s, %p, %p), stub\n", + hwndOwner, lpNetResource, lpPassword, debugstr_a(lpUserID), dwFlags, + debugstr_a(lpAccessName), lpBufferSize, lpResult ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/***************************************************************** + * WNetUseConnectionW [MPR.@] + */ +DWORD WINAPI WNetUseConnectionW( HWND hwndOwner, LPNETRESOURCEW lpNetResource, + LPCWSTR lpPassword, LPCWSTR lpUserID, DWORD dwFlags, + LPWSTR lpAccessName, LPDWORD lpBufferSize, + LPDWORD lpResult ) +{ + FIXME( "(%p, %p, %p, %s, 0x%08lX, %s, %p, %p), stub\n", + hwndOwner, lpNetResource, lpPassword, debugstr_w(lpUserID), dwFlags, + debugstr_w(lpAccessName), lpBufferSize, lpResult ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/********************************************************************* + * WNetCancelConnectionA [MPR.@] + */ +DWORD WINAPI WNetCancelConnectionA( LPCSTR lpName, BOOL fForce ) +{ + FIXME( "(%s, %d), stub\n", debugstr_a(lpName), fForce ); + + return WN_SUCCESS; +} + +/********************************************************************* + * WNetCancelConnectionW [MPR.@] + */ +DWORD WINAPI WNetCancelConnectionW( LPCWSTR lpName, BOOL fForce ) +{ + FIXME( "(%s, %d), stub\n", debugstr_w(lpName), fForce ); + + return WN_SUCCESS; +} + +/********************************************************************* + * WNetCancelConnection2A [MPR.@] + */ +DWORD WINAPI WNetCancelConnection2A( LPCSTR lpName, DWORD dwFlags, BOOL fForce ) +{ + FIXME( "(%s, %08lX, %d), stub\n", debugstr_a(lpName), dwFlags, fForce ); + + return WN_SUCCESS; +} + +/********************************************************************* + * WNetCancelConnection2W [MPR.@] + */ +DWORD WINAPI WNetCancelConnection2W( LPCWSTR lpName, DWORD dwFlags, BOOL fForce ) +{ + FIXME( "(%s, %08lX, %d), stub\n", debugstr_w(lpName), dwFlags, fForce ); + + return WN_SUCCESS; +} + +/***************************************************************** + * WNetRestoreConnectionA [MPR.@] + */ +DWORD WINAPI WNetRestoreConnectionA( HWND hwndOwner, LPSTR lpszDevice ) +{ + FIXME( "(%p, %s), stub\n", hwndOwner, debugstr_a(lpszDevice) ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/***************************************************************** + * WNetRestoreConnectionW [MPR.@] + */ +DWORD WINAPI WNetRestoreConnectionW( HWND hwndOwner, LPWSTR lpszDevice ) +{ + FIXME( "(%p, %s), stub\n", hwndOwner, debugstr_w(lpszDevice) ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/************************************************************************** + * WNetGetConnectionA [MPR.@] + * + * RETURNS + * - WN_BAD_LOCALNAME lpLocalName makes no sense + * - WN_NOT_CONNECTED drive is a local drive + * - WN_MORE_DATA buffer isn't big enough + * - WN_SUCCESS success (net path in buffer) + * + * FIXME: need to test return values under different errors + */ +DWORD WINAPI WNetGetConnectionA( LPCSTR lpLocalName, + LPSTR lpRemoteName, LPDWORD lpBufferSize ) +{ + DWORD ret; + + if (!lpLocalName) + ret = WN_BAD_POINTER; + else if (!lpRemoteName) + ret = WN_BAD_POINTER; + else if (!lpBufferSize) + ret = WN_BAD_POINTER; + else + { + int len = MultiByteToWideChar(CP_ACP, 0, lpLocalName, -1, NULL, 0); + + if (len) + { + PWSTR wideLocalName = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + + if (wideLocalName) + { + WCHAR wideRemoteStatic[MAX_PATH]; + DWORD wideRemoteSize = sizeof(wideRemoteStatic) / sizeof(WCHAR); + + MultiByteToWideChar(CP_ACP, 0, lpLocalName, -1, wideLocalName, len); + + /* try once without memory allocation */ + ret = WNetGetConnectionW(wideLocalName, wideRemoteStatic, + &wideRemoteSize); + if (ret == WN_SUCCESS) + { + int len = WideCharToMultiByte(CP_ACP, 0, wideRemoteStatic, + -1, NULL, 0, NULL, NULL); + + if (len <= *lpBufferSize) + { + WideCharToMultiByte(CP_ACP, 0, wideRemoteStatic, -1, + lpRemoteName, *lpBufferSize, NULL, NULL); + ret = WN_SUCCESS; + } + else + { + *lpBufferSize = len; + ret = WN_MORE_DATA; + } + } + else if (ret == WN_MORE_DATA) + { + PWSTR wideRemote = HeapAlloc(GetProcessHeap(), 0, + wideRemoteSize * sizeof(WCHAR)); + + if (wideRemote) + { + ret = WNetGetConnectionW(wideLocalName, wideRemote, + &wideRemoteSize); + if (ret == WN_SUCCESS) + { + if (len <= *lpBufferSize) + { + WideCharToMultiByte(CP_ACP, 0, wideRemoteStatic, + -1, lpRemoteName, *lpBufferSize, NULL, NULL); + ret = WN_SUCCESS; + } + else + { + *lpBufferSize = len; + ret = WN_MORE_DATA; + } + } + HeapFree(GetProcessHeap(), 0, wideRemote); + } + else + ret = WN_OUT_OF_MEMORY; + } + HeapFree(GetProcessHeap(), 0, wideLocalName); + } + else + ret = WN_OUT_OF_MEMORY; + } + else + ret = WN_BAD_LOCALNAME; + } + if (ret) + SetLastError(ret); + TRACE("Returning %ld\n", ret); + return ret; +} + +/************************************************************************** + * WNetGetConnectionW [MPR.@] + * + * FIXME: need to test return values under different errors + */ +DWORD WINAPI WNetGetConnectionW( LPCWSTR lpLocalName, + LPWSTR lpRemoteName, LPDWORD lpBufferSize ) +{ + DWORD ret; + + TRACE("(%s, %p, %p)\n", debugstr_w(lpLocalName), lpRemoteName, + lpBufferSize); + + if (!lpLocalName) + ret = WN_BAD_POINTER; + else if (!lpRemoteName) + ret = WN_BAD_POINTER; + else if (!lpBufferSize) + ret = WN_BAD_POINTER; + else if (!lpLocalName[0]) + ret = WN_BAD_LOCALNAME; + else + { + if (lpLocalName[1] == ':') + { + switch(GetDriveTypeW(lpLocalName)) + { + case DRIVE_REMOTE: + { + WCHAR remote[MAX_PATH]; + if (!QueryDosDeviceW( lpLocalName, remote, MAX_PATH )) remote[0] = 0; + if (strlenW(remote) + 1 > *lpBufferSize) + { + *lpBufferSize = strlenW(remote) + 1; + ret = WN_MORE_DATA; + } + else + { + strcpyW( lpRemoteName, remote ); + *lpBufferSize = strlenW(lpRemoteName) + 1; + ret = WN_SUCCESS; + } + break; + } + case DRIVE_REMOVABLE: + case DRIVE_FIXED: + case DRIVE_CDROM: + TRACE("file is local\n"); + ret = WN_NOT_CONNECTED; + break; + default: + ret = WN_BAD_LOCALNAME; + } + } + else + ret = WN_BAD_LOCALNAME; + } + if (ret) + SetLastError(ret); + TRACE("Returning %ld\n", ret); + return ret; +} + +/************************************************************************** + * WNetSetConnectionA [MPR.@] + */ +DWORD WINAPI WNetSetConnectionA( LPCSTR lpName, DWORD dwProperty, + LPVOID pvValue ) +{ + FIXME( "(%s, %08lX, %p): stub\n", debugstr_a(lpName), dwProperty, pvValue ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/************************************************************************** + * WNetSetConnectionW [MPR.@] + */ +DWORD WINAPI WNetSetConnectionW( LPCWSTR lpName, DWORD dwProperty, + LPVOID pvValue ) +{ + FIXME( "(%s, %08lX, %p): stub\n", debugstr_w(lpName), dwProperty, pvValue ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/***************************************************************** + * WNetGetUniversalNameA [MPR.@] + */ +DWORD WINAPI WNetGetUniversalNameA ( LPCSTR lpLocalPath, DWORD dwInfoLevel, + LPVOID lpBuffer, LPDWORD lpBufferSize ) +{ + FIXME( "(%s, 0x%08lX, %p, %p): stub\n", + debugstr_a(lpLocalPath), dwInfoLevel, lpBuffer, lpBufferSize); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/***************************************************************** + * WNetGetUniversalNameW [MPR.@] + */ +DWORD WINAPI WNetGetUniversalNameW ( LPCWSTR lpLocalPath, DWORD dwInfoLevel, + LPVOID lpBuffer, LPDWORD lpBufferSize ) +{ + FIXME( "(%s, 0x%08lX, %p, %p): stub\n", + debugstr_w(lpLocalPath), dwInfoLevel, lpBuffer, lpBufferSize); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + + + +/* + * Other Functions + */ + +/************************************************************************** + * WNetGetUserA [MPR.@] + * + * FIXME: we should not return ourselves, but the owner of the drive lpName + */ +DWORD WINAPI WNetGetUserA( LPCSTR lpName, LPSTR lpUserID, LPDWORD lpBufferSize ) +{ + if (GetUserNameA( lpUserID, lpBufferSize )) return WN_SUCCESS; + return GetLastError(); +} + +/***************************************************************** + * WNetGetUserW [MPR.@] + * + * FIXME: we should not return ourselves, but the owner of the drive lpName + */ +DWORD WINAPI WNetGetUserW( LPCWSTR lpName, LPWSTR lpUserID, LPDWORD lpBufferSize ) +{ + if (GetUserNameW( lpUserID, lpBufferSize )) return WN_SUCCESS; + return GetLastError(); +} + +/********************************************************************* + * WNetConnectionDialog [MPR.@] + */ +DWORD WINAPI WNetConnectionDialog( HWND hwnd, DWORD dwType ) +{ + FIXME( "(%p, %08lX): stub\n", hwnd, dwType ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/********************************************************************* + * WNetConnectionDialog1A [MPR.@] + */ +DWORD WINAPI WNetConnectionDialog1A( LPCONNECTDLGSTRUCTA lpConnDlgStruct ) +{ + FIXME( "(%p): stub\n", lpConnDlgStruct ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/********************************************************************* + * WNetConnectionDialog1W [MPR.@] + */ +DWORD WINAPI WNetConnectionDialog1W( LPCONNECTDLGSTRUCTW lpConnDlgStruct ) +{ + FIXME( "(%p): stub\n", lpConnDlgStruct ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/********************************************************************* + * WNetDisconnectDialog [MPR.@] + */ +DWORD WINAPI WNetDisconnectDialog( HWND hwnd, DWORD dwType ) +{ + FIXME( "(%p, %08lX): stub\n", hwnd, dwType ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/********************************************************************* + * WNetDisconnectDialog1A [MPR.@] + */ +DWORD WINAPI WNetDisconnectDialog1A( LPDISCDLGSTRUCTA lpConnDlgStruct ) +{ + FIXME( "(%p): stub\n", lpConnDlgStruct ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/********************************************************************* + * WNetDisconnectDialog1W [MPR.@] + */ +DWORD WINAPI WNetDisconnectDialog1W( LPDISCDLGSTRUCTW lpConnDlgStruct ) +{ + FIXME( "(%p): stub\n", lpConnDlgStruct ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/********************************************************************* + * WNetGetLastErrorA [MPR.@] + */ +DWORD WINAPI WNetGetLastErrorA( LPDWORD lpError, + LPSTR lpErrorBuf, DWORD nErrorBufSize, + LPSTR lpNameBuf, DWORD nNameBufSize ) +{ + FIXME( "(%p, %p, %ld, %p, %ld): stub\n", + lpError, lpErrorBuf, nErrorBufSize, lpNameBuf, nNameBufSize ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/********************************************************************* + * WNetGetLastErrorW [MPR.@] + */ +DWORD WINAPI WNetGetLastErrorW( LPDWORD lpError, + LPWSTR lpErrorBuf, DWORD nErrorBufSize, + LPWSTR lpNameBuf, DWORD nNameBufSize ) +{ + FIXME( "(%p, %p, %ld, %p, %ld): stub\n", + lpError, lpErrorBuf, nErrorBufSize, lpNameBuf, nNameBufSize ); + + SetLastError(WN_NO_NETWORK); + return WN_NO_NETWORK; +} + +/********************************************************************* + * WNetGetNetworkInformationA [MPR.@] + */ +DWORD WINAPI WNetGetNetworkInformationA( LPCSTR lpProvider, + LPNETINFOSTRUCT lpNetInfoStruct ) +{ + DWORD ret; + + TRACE( "(%s, %p)\n", debugstr_a(lpProvider), lpNetInfoStruct ); + + if (!lpProvider) + ret = WN_BAD_POINTER; + else + { + int len; + + len = MultiByteToWideChar(CP_ACP, 0, lpProvider, -1, NULL, 0); + if (len) + { + LPWSTR wideProvider = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + + if (wideProvider) + { + MultiByteToWideChar(CP_ACP, 0, lpProvider, -1, wideProvider, + len); + ret = WNetGetNetworkInformationW(wideProvider, lpNetInfoStruct); + HeapFree(GetProcessHeap(), 0, wideProvider); + } + else + ret = WN_OUT_OF_MEMORY; + } + else + ret = GetLastError(); + } + if (ret) + SetLastError(ret); + TRACE("Returning %ld\n", ret); + return ret; +} + +/********************************************************************* + * WNetGetNetworkInformationW [MPR.@] + */ +DWORD WINAPI WNetGetNetworkInformationW( LPCWSTR lpProvider, + LPNETINFOSTRUCT lpNetInfoStruct ) +{ + DWORD ret; + + TRACE( "(%s, %p)\n", debugstr_w(lpProvider), lpNetInfoStruct ); + + if (!lpProvider) + ret = WN_BAD_POINTER; + else if (!lpNetInfoStruct) + ret = WN_BAD_POINTER; + else if (lpNetInfoStruct->cbStructure < sizeof(NETINFOSTRUCT)) + ret = WN_BAD_VALUE; + else + { + if (providerTable && providerTable->numProviders) + { + DWORD providerIndex = _findProviderIndexW(lpProvider); + + if (providerIndex != BAD_PROVIDER_INDEX) + { + lpNetInfoStruct->cbStructure = sizeof(NETINFOSTRUCT); + lpNetInfoStruct->dwProviderVersion = + providerTable->table[providerIndex].dwSpecVersion; + lpNetInfoStruct->dwStatus = NO_ERROR; + lpNetInfoStruct->dwCharacteristics = 0; + lpNetInfoStruct->dwHandle = (ULONG_PTR)NULL; + lpNetInfoStruct->wNetType = + HIWORD(providerTable->table[providerIndex].dwNetType); + lpNetInfoStruct->dwPrinters = -1; + lpNetInfoStruct->dwDrives = -1; + ret = WN_SUCCESS; + } + else + ret = WN_BAD_PROVIDER; + } + else + ret = WN_NO_NETWORK; + } + if (ret) + SetLastError(ret); + TRACE("Returning %ld\n", ret); + return ret; +} + +/***************************************************************** + * WNetGetProviderNameA [MPR.@] + */ +DWORD WINAPI WNetGetProviderNameA( DWORD dwNetType, + LPSTR lpProvider, LPDWORD lpBufferSize ) +{ + DWORD ret; + + TRACE("(0x%08lx, %s, %p)\n", dwNetType, debugstr_a(lpProvider), + lpBufferSize); + + if (!lpProvider) + ret = WN_BAD_POINTER; + else if (!lpBufferSize) + ret = WN_BAD_POINTER; + else + { + if (providerTable) + { + DWORD i; + + ret = WN_NO_NETWORK; + for (i = 0; i < providerTable->numProviders && + HIWORD(providerTable->table[i].dwNetType) != HIWORD(dwNetType); + i++) + ; + if (i < providerTable->numProviders) + { + DWORD sizeNeeded = WideCharToMultiByte(CP_ACP, 0, + providerTable->table[i].name, -1, NULL, 0, NULL, NULL); + + if (*lpBufferSize < sizeNeeded) + { + *lpBufferSize = sizeNeeded; + ret = WN_MORE_DATA; + } + else + { + WideCharToMultiByte(CP_ACP, 0, providerTable->table[i].name, + -1, lpProvider, *lpBufferSize, NULL, NULL); + ret = WN_SUCCESS; + /* FIXME: is *lpBufferSize set to the number of characters + * copied? */ + } + } + } + else + ret = WN_NO_NETWORK; + } + if (ret) + SetLastError(ret); + TRACE("Returning %ld\n", ret); + return ret; +} + +/***************************************************************** + * WNetGetProviderNameW [MPR.@] + */ +DWORD WINAPI WNetGetProviderNameW( DWORD dwNetType, + LPWSTR lpProvider, LPDWORD lpBufferSize ) +{ + DWORD ret; + + TRACE("(0x%08lx, %s, %p)\n", dwNetType, debugstr_w(lpProvider), + lpBufferSize); + + if (!lpProvider) + ret = WN_BAD_POINTER; + else if (!lpBufferSize) + ret = WN_BAD_POINTER; + else + { + if (providerTable) + { + DWORD i; + + ret = WN_NO_NETWORK; + for (i = 0; i < providerTable->numProviders && + HIWORD(providerTable->table[i].dwNetType) != HIWORD(dwNetType); + i++) + ; + if (i < providerTable->numProviders) + { + DWORD sizeNeeded = strlenW(providerTable->table[i].name) + 1; + + if (*lpBufferSize < sizeNeeded) + { + *lpBufferSize = sizeNeeded; + ret = WN_MORE_DATA; + } + else + { + strcpyW(lpProvider, providerTable->table[i].name); + ret = WN_SUCCESS; + /* FIXME: is *lpBufferSize set to the number of characters + * copied? */ + } + } + } + else + ret = WN_NO_NETWORK; + } + if (ret) + SetLastError(ret); + TRACE("Returning %ld\n", ret); + return ret; +} diff --git a/reactos/dll/mpr/wnetpriv.h b/reactos/dll/mpr/wnetpriv.h new file mode 100644 index 00000000000..7a275de1445 --- /dev/null +++ b/reactos/dll/mpr/wnetpriv.h @@ -0,0 +1,27 @@ +/* + * WNet private definitions + * + * Copyright (C) 2004 Juan Lang + * + * 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 + */ + +#ifndef __WNET_PRIV_H__ +#define __WNET_PRIV_H__ + +void wnetInit(HINSTANCE hInstDll); +void wnetFree(void); + +#endif /* ndef __WNET_PRIV_H__ */ diff --git a/reactos/dll/msacm/Makefile.in b/reactos/dll/msacm/Makefile.in new file mode 100644 index 00000000000..247b6816e67 --- /dev/null +++ b/reactos/dll/msacm/Makefile.in @@ -0,0 +1,29 @@ +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = ../.. +SRCDIR = @srcdir@ +VPATH = @srcdir@ +MODULE = msacm32.dll +IMPORTS = winmm user32 advapi32 kernel32 +ALTNAMES = msacm.dll + +SPEC_SRCS16 = $(ALTNAMES:.dll=.spec) + +C_SRCS = \ + driver.c \ + filter.c \ + format.c \ + internal.c \ + msacm32_main.c \ + pcmconverter.c \ + stream.c + +C_SRCS16 = \ + msacm_main.c + +RC_SRCS = msacm.rc + +SUBDIRS = tests + +@MAKE_DLL_RULES@ + +### Dependencies: diff --git a/reactos/dll/msacm/driver.c b/reactos/dll/msacm/driver.c new file mode 100644 index 00000000000..dab014f4db3 --- /dev/null +++ b/reactos/dll/msacm/driver.c @@ -0,0 +1,493 @@ +/* -*- tab-width: 8; c-basic-offset: 4 -*- */ + +/* + * MSACM32 library + * + * Copyright 1998 Patrik Stridvall + * 1999 Eric Pouech + * + * 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 "config.h" +#include "wine/port.h" + +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "winreg.h" +#include "mmsystem.h" +#include "mmreg.h" +#include "msacm.h" +#include "msacmdrv.h" +#include "wineacm.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msacm); + +/*********************************************************************** + * acmDriverAddA (MSACM32.@) + */ +MMRESULT WINAPI acmDriverAddA(PHACMDRIVERID phadid, HINSTANCE hinstModule, + LPARAM lParam, DWORD dwPriority, DWORD fdwAdd) +{ + TRACE("(%p, %p, %08lx, %08lx, %08lx)\n", + phadid, hinstModule, lParam, dwPriority, fdwAdd); + + if (!phadid) { + WARN("invalid parameter\n"); + return MMSYSERR_INVALPARAM; + } + + /* Check if any unknown flags */ + if (fdwAdd & + ~(ACM_DRIVERADDF_FUNCTION|ACM_DRIVERADDF_NOTIFYHWND| + ACM_DRIVERADDF_GLOBAL)) { + WARN("invalid flag\n"); + return MMSYSERR_INVALFLAG; + } + + /* Check if any incompatible flags */ + if ((fdwAdd & ACM_DRIVERADDF_FUNCTION) && + (fdwAdd & ACM_DRIVERADDF_NOTIFYHWND)) { + WARN("invalid flag\n"); + return MMSYSERR_INVALFLAG; + } + + /* FIXME: in fact, should GetModuleFileName(hinstModule) and do a + * LoadDriver on it, to be sure we can call SendDriverMessage on the + * hDrvr handle. + */ + *phadid = (HACMDRIVERID) MSACM_RegisterDriver(NULL, NULL, hinstModule); + + /* FIXME: lParam, dwPriority and fdwAdd ignored */ + + return MMSYSERR_NOERROR; +} + +/*********************************************************************** + * acmDriverAddW (MSACM32.@) + * FIXME + * Not implemented + */ +MMRESULT WINAPI acmDriverAddW(PHACMDRIVERID phadid, HINSTANCE hinstModule, + LPARAM lParam, DWORD dwPriority, DWORD fdwAdd) +{ + FIXME("(%p, %p, %ld, %ld, %ld): stub\n", + phadid, hinstModule, lParam, dwPriority, fdwAdd); + + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmDriverClose (MSACM32.@) + */ +MMRESULT WINAPI acmDriverClose(HACMDRIVER had, DWORD fdwClose) +{ + PWINE_ACMDRIVER pad; + PWINE_ACMDRIVERID padid; + PWINE_ACMDRIVER* tpad; + + TRACE("(%p, %08lx)\n", had, fdwClose); + + if (fdwClose) { + WARN("invalid flag\n"); + return MMSYSERR_INVALFLAG; + } + + pad = MSACM_GetDriver(had); + if (!pad) { + WARN("invalid handle\n"); + return MMSYSERR_INVALHANDLE; + } + + padid = pad->obj.pACMDriverID; + + /* remove driver from list */ + for (tpad = &(padid->pACMDriverList); *tpad; *tpad = (*tpad)->pNextACMDriver) { + if (*tpad == pad) { + *tpad = (*tpad)->pNextACMDriver; + break; + } + } + + /* close driver if it has been opened */ + if (pad->hDrvr && !padid->hInstModule) + CloseDriver(pad->hDrvr, 0, 0); + + HeapFree(MSACM_hHeap, 0, pad); + + return MMSYSERR_NOERROR; +} + +/*********************************************************************** + * acmDriverDetailsA (MSACM32.@) + */ +MMRESULT WINAPI acmDriverDetailsA(HACMDRIVERID hadid, PACMDRIVERDETAILSA padd, DWORD fdwDetails) +{ + MMRESULT mmr; + ACMDRIVERDETAILSW addw; + + TRACE("(%p, %p, %08lx)\n", hadid, padd, fdwDetails); + + if (!padd) { + WARN("invalid parameter\n"); + return MMSYSERR_INVALPARAM; + } + + if (padd->cbStruct < 4) { + WARN("invalid parameter\n"); + return MMSYSERR_INVALPARAM; + } + + addw.cbStruct = sizeof(addw); + mmr = acmDriverDetailsW(hadid, &addw, fdwDetails); + if (mmr == 0) { + ACMDRIVERDETAILSA padda; + + padda.fccType = addw.fccType; + padda.fccComp = addw.fccComp; + padda.wMid = addw.wMid; + padda.wPid = addw.wPid; + padda.vdwACM = addw.vdwACM; + padda.vdwDriver = addw.vdwDriver; + padda.fdwSupport = addw.fdwSupport; + padda.cFormatTags = addw.cFormatTags; + padda.cFilterTags = addw.cFilterTags; + padda.hicon = addw.hicon; + WideCharToMultiByte( CP_ACP, 0, addw.szShortName, -1, padda.szShortName, + sizeof(padda.szShortName), NULL, NULL ); + WideCharToMultiByte( CP_ACP, 0, addw.szLongName, -1, padda.szLongName, + sizeof(padda.szLongName), NULL, NULL ); + WideCharToMultiByte( CP_ACP, 0, addw.szCopyright, -1, padda.szCopyright, + sizeof(padda.szCopyright), NULL, NULL ); + WideCharToMultiByte( CP_ACP, 0, addw.szLicensing, -1, padda.szLicensing, + sizeof(padda.szLicensing), NULL, NULL ); + WideCharToMultiByte( CP_ACP, 0, addw.szFeatures, -1, padda.szFeatures, + sizeof(padda.szFeatures), NULL, NULL ); + memcpy(padd, &padda, min(padd->cbStruct, sizeof(*padd))); + } + return mmr; +} + +/*********************************************************************** + * acmDriverDetailsW (MSACM32.@) + */ +MMRESULT WINAPI acmDriverDetailsW(HACMDRIVERID hadid, PACMDRIVERDETAILSW padd, DWORD fdwDetails) +{ + HACMDRIVER acmDrvr; + MMRESULT mmr; + + TRACE("(%p, %p, %08lx)\n", hadid, padd, fdwDetails); + + if (!padd) { + WARN("invalid parameter\n"); + return MMSYSERR_INVALPARAM; + } + + if (padd->cbStruct < 4) { + WARN("invalid parameter\n"); + return MMSYSERR_INVALPARAM; + } + + if (fdwDetails) { + WARN("invalid flag\n"); + return MMSYSERR_INVALFLAG; + } + + mmr = acmDriverOpen(&acmDrvr, hadid, 0); + if (mmr == MMSYSERR_NOERROR) { + ACMDRIVERDETAILSW paddw; + mmr = (MMRESULT)MSACM_Message(acmDrvr, ACMDM_DRIVER_DETAILS, (LPARAM)&paddw, 0); + + acmDriverClose(acmDrvr, 0); + memcpy(padd, &paddw, min(padd->cbStruct, sizeof(*padd))); + } + + return mmr; +} + +/*********************************************************************** + * acmDriverEnum (MSACM32.@) + */ +MMRESULT WINAPI acmDriverEnum(ACMDRIVERENUMCB fnCallback, DWORD dwInstance, DWORD fdwEnum) +{ + PWINE_ACMDRIVERID padid; + DWORD fdwSupport; + + TRACE("(%p, %08lx, %08lx)\n", fnCallback, dwInstance, fdwEnum); + + if (!fnCallback) { + WARN("invalid parameter\n"); + return MMSYSERR_INVALPARAM; + } + + if (fdwEnum & ~(ACM_DRIVERENUMF_NOLOCAL|ACM_DRIVERENUMF_DISABLED)) { + WARN("invalid flag\n"); + return MMSYSERR_INVALFLAG; + } + + for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) { + fdwSupport = padid->fdwSupport; + + if (padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) { + if (fdwEnum & ACM_DRIVERENUMF_DISABLED) + fdwSupport |= ACMDRIVERDETAILS_SUPPORTF_DISABLED; + else + continue; + } + if (!(*fnCallback)((HACMDRIVERID)padid, dwInstance, fdwSupport)) + break; + } + + return MMSYSERR_NOERROR; +} + +/*********************************************************************** + * acmDriverID (MSACM32.@) + */ +MMRESULT WINAPI acmDriverID(HACMOBJ hao, PHACMDRIVERID phadid, DWORD fdwDriverID) +{ + PWINE_ACMOBJ pao; + + TRACE("(%p, %p, %08lx)\n", hao, phadid, fdwDriverID); + + if (fdwDriverID) { + WARN("invalid flag\n"); + return MMSYSERR_INVALFLAG; + } + + pao = MSACM_GetObj(hao, WINE_ACMOBJ_DONTCARE); + if (!pao) { + WARN("invalid handle\n"); + return MMSYSERR_INVALHANDLE; + } + + if (!phadid) { + WARN("invalid parameter\n"); + return MMSYSERR_INVALPARAM; + } + + *phadid = (HACMDRIVERID) pao->pACMDriverID; + + return MMSYSERR_NOERROR; +} + +/*********************************************************************** + * acmDriverMessage (MSACM32.@) + * + */ +LRESULT WINAPI acmDriverMessage(HACMDRIVER had, UINT uMsg, LPARAM lParam1, LPARAM lParam2) +{ + TRACE("(%p, %04x, %08lx, %08lx\n", had, uMsg, lParam1, lParam2); + + if ((uMsg >= ACMDM_USER && uMsg < ACMDM_RESERVED_LOW) || + uMsg == ACMDM_DRIVER_ABOUT || + uMsg == DRV_QUERYCONFIGURE || + uMsg == DRV_CONFIGURE) + return MSACM_Message(had, uMsg, lParam1, lParam2); + + WARN("invalid parameter\n"); + return MMSYSERR_INVALPARAM; +} + +/*********************************************************************** + * acmDriverOpen (MSACM32.@) + */ +MMRESULT WINAPI acmDriverOpen(PHACMDRIVER phad, HACMDRIVERID hadid, DWORD fdwOpen) +{ + PWINE_ACMDRIVERID padid; + PWINE_ACMDRIVER pad = NULL; + MMRESULT ret; + + TRACE("(%p, %p, %08lu)\n", phad, hadid, fdwOpen); + + if (!phad) { + WARN("invalid parameter\n"); + return MMSYSERR_INVALPARAM; + } + + if (fdwOpen) { + WARN("invalid flag\n"); + return MMSYSERR_INVALFLAG; + } + + padid = MSACM_GetDriverID(hadid); + if (!padid) { + WARN("invalid handle\n"); + return MMSYSERR_INVALHANDLE; + } + + pad = HeapAlloc(MSACM_hHeap, 0, sizeof(WINE_ACMDRIVER)); + if (!pad) { + WARN("no memory\n"); + return MMSYSERR_NOMEM; + } + + pad->obj.dwType = WINE_ACMOBJ_DRIVER; + pad->obj.pACMDriverID = padid; + + if (!(pad->hDrvr = (HDRVR)padid->hInstModule)) + { + ACMDRVOPENDESCW adod; + int len; + + /* this is not an externally added driver... need to actually load it */ + if (!padid->pszDriverAlias) + { + ret = MMSYSERR_ERROR; + goto gotError; + } + + adod.cbStruct = sizeof(adod); + adod.fccType = ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC; + adod.fccComp = ACMDRIVERDETAILS_FCCCOMP_UNDEFINED; + adod.dwVersion = acmGetVersion(); + adod.dwFlags = fdwOpen; + adod.dwError = 0; + len = strlen("Drivers32") + 1; + adod.pszSectionName = HeapAlloc(MSACM_hHeap, 0, len * sizeof(WCHAR)); + MultiByteToWideChar(CP_ACP, 0, "Drivers32", -1, (LPWSTR)adod.pszSectionName, len); + adod.pszAliasName = padid->pszDriverAlias; + adod.dnDevNode = 0; + + pad->hDrvr = OpenDriver(padid->pszDriverAlias, NULL, (DWORD)&adod); + + HeapFree(MSACM_hHeap, 0, (LPWSTR)adod.pszSectionName); + if (!pad->hDrvr) + { + ret = adod.dwError; + goto gotError; + } + } + + /* insert new pad at beg of list */ + pad->pNextACMDriver = padid->pACMDriverList; + padid->pACMDriverList = pad; + + /* FIXME: Create a WINE_ACMDRIVER32 */ + *phad = (HACMDRIVER)pad; + TRACE("'%s' => %08lx\n", debugstr_w(padid->pszDriverAlias), (DWORD)pad); + + return MMSYSERR_NOERROR; + gotError: + WARN("failed: ret = %08x\n", ret); + if (pad && !pad->hDrvr) + HeapFree(MSACM_hHeap, 0, pad); + return ret; +} + +/*********************************************************************** + * acmDriverPriority (MSACM32.@) + */ +MMRESULT WINAPI acmDriverPriority(HACMDRIVERID hadid, DWORD dwPriority, DWORD fdwPriority) +{ + PWINE_ACMDRIVERID padid; + CHAR szSubKey[17]; + CHAR szBuffer[256]; + LONG lBufferLength = sizeof(szBuffer); + LONG lError; + HKEY hPriorityKey; + DWORD dwPriorityCounter; + + TRACE("(%p, %08lx, %08lx)\n", hadid, dwPriority, fdwPriority); + + padid = MSACM_GetDriverID(hadid); + if (!padid) { + WARN("invalid handle\n"); + return MMSYSERR_INVALHANDLE; + } + + /* Check for unknown flags */ + if (fdwPriority & + ~(ACM_DRIVERPRIORITYF_ENABLE|ACM_DRIVERPRIORITYF_DISABLE| + ACM_DRIVERPRIORITYF_BEGIN|ACM_DRIVERPRIORITYF_END)) { + WARN("invalid flag\n"); + return MMSYSERR_INVALFLAG; + } + + /* Check for incompatible flags */ + if ((fdwPriority & ACM_DRIVERPRIORITYF_ENABLE) && + (fdwPriority & ACM_DRIVERPRIORITYF_DISABLE)) { + WARN("invalid flag\n"); + return MMSYSERR_INVALFLAG; + } + + /* Check for incompatible flags */ + if ((fdwPriority & ACM_DRIVERPRIORITYF_BEGIN) && + (fdwPriority & ACM_DRIVERPRIORITYF_END)) { + WARN("invalid flag\n"); + return MMSYSERR_INVALFLAG; + } + + lError = RegOpenKeyA(HKEY_CURRENT_USER, + "Software\\Microsoft\\Multimedia\\" + "Audio Compression Manager\\Priority v4.00", + &hPriorityKey + ); + /* FIXME: Create key */ + if (lError != ERROR_SUCCESS) { + WARN("RegOpenKeyA failed\n"); + return MMSYSERR_ERROR; + } + + for (dwPriorityCounter = 1; ; dwPriorityCounter++) { + snprintf(szSubKey, 17, "Priority%ld", dwPriorityCounter); + lError = RegQueryValueA(hPriorityKey, szSubKey, szBuffer, &lBufferLength); + if (lError != ERROR_SUCCESS) + break; + + FIXME("(%p, %ld, %ld): stub (partial)\n", + hadid, dwPriority, fdwPriority); + break; + } + + RegCloseKey(hPriorityKey); + + WARN("RegQueryValueA failed\n"); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmDriverRemove (MSACM32.@) + */ +MMRESULT WINAPI acmDriverRemove(HACMDRIVERID hadid, DWORD fdwRemove) +{ + PWINE_ACMDRIVERID padid; + + TRACE("(%p, %08lx)\n", hadid, fdwRemove); + + padid = MSACM_GetDriverID(hadid); + if (!padid) { + WARN("invalid handle\n"); + return MMSYSERR_INVALHANDLE; + } + + if (fdwRemove) { + WARN("invalid flag\n"); + return MMSYSERR_INVALFLAG; + } + + MSACM_UnregisterDriver(padid); + + return MMSYSERR_NOERROR; +} diff --git a/reactos/dll/msacm/filter.c b/reactos/dll/msacm/filter.c new file mode 100644 index 00000000000..3bce5c23d3c --- /dev/null +++ b/reactos/dll/msacm/filter.c @@ -0,0 +1,454 @@ +/* -*- tab-width: 8; c-basic-offset: 4 -*- */ + +/* + * MSACM32 library + * + * Copyright 1998 Patrik Stridvall + * + * 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 +#include +#include "windef.h" +#include "winbase.h" +#include "winnls.h" +#include "winerror.h" +#include "mmsystem.h" +#include "mmreg.h" +#include "msacm.h" +#include "msacmdrv.h" +#include "wineacm.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msacm); + +/*********************************************************************** + * acmFilterChooseA (MSACM32.@) + */ +MMRESULT WINAPI acmFilterChooseA(PACMFILTERCHOOSEA pafltrc) +{ + FIXME("(%p): stub\n", pafltrc); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmFilterChooseW (MSACM32.@) + */ +MMRESULT WINAPI acmFilterChooseW(PACMFILTERCHOOSEW pafltrc) +{ + FIXME("(%p): stub\n", pafltrc); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmFilterDetailsA (MSACM32.@) + */ +MMRESULT WINAPI acmFilterDetailsA(HACMDRIVER had, PACMFILTERDETAILSA pafd, + DWORD fdwDetails) +{ + ACMFILTERDETAILSW afdw; + MMRESULT mmr; + + memset(&afdw, 0, sizeof(afdw)); + afdw.cbStruct = sizeof(afdw); + afdw.dwFilterIndex = pafd->dwFilterIndex; + afdw.dwFilterTag = pafd->dwFilterTag; + afdw.pwfltr = pafd->pwfltr; + afdw.cbwfltr = pafd->cbwfltr; + + mmr = acmFilterDetailsW(had, &afdw, fdwDetails); + if (mmr == MMSYSERR_NOERROR) { + pafd->dwFilterTag = afdw.dwFilterTag; + pafd->fdwSupport = afdw.fdwSupport; + WideCharToMultiByte( CP_ACP, 0, afdw.szFilter, -1, pafd->szFilter, + sizeof(pafd->szFilter), NULL, NULL ); + } + return mmr; +} + +/*********************************************************************** + * acmFilterDetailsW (MSACM32.@) + */ +MMRESULT WINAPI acmFilterDetailsW(HACMDRIVER had, PACMFILTERDETAILSW pafd, + DWORD fdwDetails) +{ + MMRESULT mmr; + ACMFILTERTAGDETAILSA aftd; + + TRACE("(%p, %p, %ld)\n", had, pafd, fdwDetails); + + memset(&aftd, 0, sizeof(aftd)); + aftd.cbStruct = sizeof(aftd); + + if (pafd->cbStruct < sizeof(*pafd)) return MMSYSERR_INVALPARAM; + + switch (fdwDetails) { + case ACM_FILTERDETAILSF_FILTER: + if (pafd->dwFilterTag != pafd->pwfltr->dwFilterTag) { + mmr = MMSYSERR_INVALPARAM; + break; + } + if (had == NULL) { + PWINE_ACMDRIVERID padid; + + mmr = ACMERR_NOTPOSSIBLE; + for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) { + /* should check for codec only */ + if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) && + acmDriverOpen(&had, (HACMDRIVERID)padid, 0) == 0) { + mmr = MSACM_Message(had, ACMDM_FILTER_DETAILS, + (LPARAM)pafd, (LPARAM)fdwDetails); + acmDriverClose(had, 0); + if (mmr == MMSYSERR_NOERROR) break; + } + } + } else { + mmr = MSACM_Message(had, ACMDM_FILTER_DETAILS, (LPARAM)pafd, fdwDetails); + } + break; + case ACM_FILTERDETAILSF_INDEX: + /* should check pafd->dwFilterIndex < aftd->cStandardFilters */ + mmr = MSACM_Message(had, ACMDM_FILTER_DETAILS, (LPARAM)pafd, fdwDetails); + break; + default: + WARN("Unknown fdwDetails %08lx\n", fdwDetails); + mmr = MMSYSERR_INVALFLAG; + break; + } + + TRACE("=> %d\n", mmr); + return mmr; +} + +struct MSACM_FilterEnumWtoA_Instance { + PACMFILTERDETAILSA pafda; + DWORD dwInstance; + ACMFILTERENUMCBA fnCallback; +}; + +static BOOL CALLBACK MSACM_FilterEnumCallbackWtoA(HACMDRIVERID hadid, + PACMFILTERDETAILSW pafdw, + DWORD dwInstance, + DWORD fdwSupport) +{ + struct MSACM_FilterEnumWtoA_Instance* pafei; + + pafei = (struct MSACM_FilterEnumWtoA_Instance*)dwInstance; + + pafei->pafda->dwFilterIndex = pafdw->dwFilterIndex; + pafei->pafda->dwFilterTag = pafdw->dwFilterTag; + pafei->pafda->fdwSupport = pafdw->fdwSupport; + WideCharToMultiByte( CP_ACP, 0, pafdw->szFilter, -1, pafei->pafda->szFilter, + sizeof(pafei->pafda->szFilter), NULL, NULL ); + + return (pafei->fnCallback)(hadid, pafei->pafda, + pafei->dwInstance, fdwSupport); +} + +/*********************************************************************** + * acmFilterEnumA (MSACM32.@) + */ +MMRESULT WINAPI acmFilterEnumA(HACMDRIVER had, PACMFILTERDETAILSA pafda, + ACMFILTERENUMCBA fnCallback, DWORD dwInstance, + DWORD fdwEnum) +{ + ACMFILTERDETAILSW afdw; + struct MSACM_FilterEnumWtoA_Instance afei; + + memset(&afdw, 0, sizeof(afdw)); + afdw.cbStruct = sizeof(afdw); + afdw.dwFilterIndex = pafda->dwFilterIndex; + afdw.dwFilterTag = pafda->dwFilterTag; + afdw.pwfltr = pafda->pwfltr; + afdw.cbwfltr = pafda->cbwfltr; + + afei.pafda = pafda; + afei.dwInstance = dwInstance; + afei.fnCallback = fnCallback; + + return acmFilterEnumW(had, &afdw, MSACM_FilterEnumCallbackWtoA, + (DWORD)&afei, fdwEnum); +} + +static BOOL MSACM_FilterEnumHelper(PWINE_ACMDRIVERID padid, HACMDRIVER had, + PACMFILTERDETAILSW pafd, + ACMFILTERENUMCBW fnCallback, DWORD dwInstance, + DWORD fdwEnum) +{ + ACMFILTERTAGDETAILSW aftd; + int i, j; + + for (i = 0; i < padid->cFilterTags; i++) { + memset(&aftd, 0, sizeof(aftd)); + aftd.cbStruct = sizeof(aftd); + aftd.dwFilterTagIndex = i; + if (acmFilterTagDetailsW(had, &aftd, ACM_FILTERTAGDETAILSF_INDEX) != MMSYSERR_NOERROR) + continue; + + if ((fdwEnum & ACM_FILTERENUMF_DWFILTERTAG) && + aftd.dwFilterTag != pafd->pwfltr->dwFilterTag) + continue; + + for (j = 0; j < aftd.cStandardFilters; j++) { + pafd->dwFilterIndex = j; + pafd->dwFilterTag = aftd.dwFilterTag; + if (acmFilterDetailsW(had, pafd, ACM_FILTERDETAILSF_INDEX) != MMSYSERR_NOERROR) + continue; + + if (!(fnCallback)((HACMDRIVERID)padid, pafd, dwInstance, padid->fdwSupport)) + return FALSE; + } + } + return TRUE; +} + +/*********************************************************************** + * acmFilterEnumW (MSACM32.@) + */ +MMRESULT WINAPI acmFilterEnumW(HACMDRIVER had, PACMFILTERDETAILSW pafd, + ACMFILTERENUMCBW fnCallback, DWORD dwInstance, + DWORD fdwEnum) +{ + PWINE_ACMDRIVERID padid; + BOOL ret; + + TRACE("(%p, %p, %p, %ld, %ld)\n", + had, pafd, fnCallback, dwInstance, fdwEnum); + + if (pafd->cbStruct < sizeof(*pafd)) return MMSYSERR_INVALPARAM; + + if (fdwEnum & ~(ACM_FILTERENUMF_DWFILTERTAG)) + FIXME("Unsupported fdwEnum values\n"); + + if (had) { + HACMDRIVERID hadid; + + if (acmDriverID((HACMOBJ)had, &hadid, 0) != MMSYSERR_NOERROR) + return MMSYSERR_INVALHANDLE; + MSACM_FilterEnumHelper(MSACM_GetDriverID(hadid), had, pafd, + fnCallback, dwInstance, fdwEnum); + return MMSYSERR_NOERROR; + } + for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) { + /* should check for codec only */ + if ((padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) || + acmDriverOpen(&had, (HACMDRIVERID)padid, 0) != MMSYSERR_NOERROR) + continue; + ret = MSACM_FilterEnumHelper(padid, had, pafd, + fnCallback, dwInstance, fdwEnum); + acmDriverClose(had, 0); + if (!ret) break; + } + return MMSYSERR_NOERROR; +} + +/*********************************************************************** + * acmFilterTagDetailsA (MSACM32.@) + */ +MMRESULT WINAPI acmFilterTagDetailsA(HACMDRIVER had, PACMFILTERTAGDETAILSA paftda, + DWORD fdwDetails) +{ + ACMFILTERTAGDETAILSW aftdw; + MMRESULT mmr; + + memset(&aftdw, 0, sizeof(aftdw)); + aftdw.cbStruct = sizeof(aftdw); + aftdw.dwFilterTagIndex = paftda->dwFilterTagIndex; + aftdw.dwFilterTag = paftda->dwFilterTag; + + mmr = acmFilterTagDetailsW(had, &aftdw, fdwDetails); + if (mmr == MMSYSERR_NOERROR) { + paftda->dwFilterTag = aftdw.dwFilterTag; + paftda->dwFilterTagIndex = aftdw.dwFilterTagIndex; + paftda->cbFilterSize = aftdw.cbFilterSize; + paftda->fdwSupport = aftdw.fdwSupport; + paftda->cStandardFilters = aftdw.cStandardFilters; + WideCharToMultiByte( CP_ACP, 0, aftdw.szFilterTag, -1, paftda->szFilterTag, + sizeof(paftda->szFilterTag), NULL, NULL ); + } + return mmr; +} + +/*********************************************************************** + * acmFilterTagDetailsW (MSACM32.@) + */ +MMRESULT WINAPI acmFilterTagDetailsW(HACMDRIVER had, PACMFILTERTAGDETAILSW paftd, + DWORD fdwDetails) +{ + PWINE_ACMDRIVERID padid; + MMRESULT mmr; + + TRACE("(%p, %p, %ld)\n", had, paftd, fdwDetails); + + if (fdwDetails & ~(ACM_FILTERTAGDETAILSF_FILTERTAG|ACM_FILTERTAGDETAILSF_INDEX| + ACM_FILTERTAGDETAILSF_LARGESTSIZE)) + return MMSYSERR_INVALFLAG; + + switch (fdwDetails) { + case ACM_FILTERTAGDETAILSF_FILTERTAG: + if (had == NULL) { + mmr = ACMERR_NOTPOSSIBLE; + for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) { + /* should check for codec only */ + if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) && + acmDriverOpen(&had, (HACMDRIVERID)padid, 0) == 0) { + mmr = MSACM_Message(had, ACMDM_FILTERTAG_DETAILS, (LPARAM)paftd, fdwDetails); + acmDriverClose(had, 0); + if (mmr == MMSYSERR_NOERROR) break; + } + } + } else { + mmr = MSACM_Message(had, ACMDM_FILTERTAG_DETAILS, (LPARAM)paftd, fdwDetails); + } + break; + + case ACM_FILTERTAGDETAILSF_INDEX: + /* FIXME should check paftd->dwFilterTagIndex < add.cFilterTags */ + mmr = MSACM_Message(had, ACMDM_FILTERTAG_DETAILS, (LPARAM)paftd, fdwDetails); + break; + + case ACM_FILTERTAGDETAILSF_LARGESTSIZE: + if (had == NULL) { + ACMFILTERTAGDETAILSW tmp; + DWORD ft = paftd->dwFilterTag; + + mmr = ACMERR_NOTPOSSIBLE; + for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) { + /* should check for codec only */ + if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) && + acmDriverOpen(&had, (HACMDRIVERID)padid, 0) == 0) { + + memset(&tmp, 0, sizeof(tmp)); + tmp.cbStruct = sizeof(tmp); + tmp.dwFilterTag = ft; + + if (MSACM_Message(had, ACMDM_FILTERTAG_DETAILS, + (LPARAM)&tmp, fdwDetails) == MMSYSERR_NOERROR) { + if (mmr == ACMERR_NOTPOSSIBLE || + paftd->cbFilterSize < tmp.cbFilterSize) { + *paftd = tmp; + mmr = MMSYSERR_NOERROR; + } + } + acmDriverClose(had, 0); + } + } + } else { + mmr = MSACM_Message(had, ACMDM_FILTERTAG_DETAILS, (LPARAM)paftd, fdwDetails); + } + break; + + default: + WARN("Unsupported fdwDetails=%08lx\n", fdwDetails); + mmr = MMSYSERR_ERROR; + } + + if (mmr == MMSYSERR_NOERROR && + paftd->dwFilterTag == WAVE_FORMAT_PCM && paftd->szFilterTag[0] == 0) + MultiByteToWideChar( CP_ACP, 0, "PCM", -1, paftd->szFilterTag, + sizeof(paftd->szFilterTag)/sizeof(WCHAR) ); + + return mmr; +} + +struct MSACM_FilterTagEnumWtoA_Instance { + PACMFILTERTAGDETAILSA paftda; + DWORD dwInstance; + ACMFILTERTAGENUMCBA fnCallback; +}; + +static BOOL CALLBACK MSACM_FilterTagEnumCallbackWtoA(HACMDRIVERID hadid, + PACMFILTERTAGDETAILSW paftdw, + DWORD dwInstance, + DWORD fdwSupport) +{ + struct MSACM_FilterTagEnumWtoA_Instance* paftei; + + paftei = (struct MSACM_FilterTagEnumWtoA_Instance*)dwInstance; + + paftei->paftda->dwFilterTagIndex = paftdw->dwFilterTagIndex; + paftei->paftda->dwFilterTag = paftdw->dwFilterTag; + paftei->paftda->cbFilterSize = paftdw->cbFilterSize; + paftei->paftda->fdwSupport = paftdw->fdwSupport; + paftei->paftda->cStandardFilters = paftdw->cStandardFilters; + WideCharToMultiByte( CP_ACP, 0, paftdw->szFilterTag, -1, paftei->paftda->szFilterTag, + sizeof(paftei->paftda->szFilterTag), NULL, NULL ); + + return (paftei->fnCallback)(hadid, paftei->paftda, + paftei->dwInstance, fdwSupport); +} + +/*********************************************************************** + * acmFilterTagEnumA (MSACM32.@) + */ +MMRESULT WINAPI acmFilterTagEnumA(HACMDRIVER had, PACMFILTERTAGDETAILSA paftda, + ACMFILTERTAGENUMCBA fnCallback, DWORD dwInstance, + DWORD fdwEnum) +{ + ACMFILTERTAGDETAILSW aftdw; + struct MSACM_FilterTagEnumWtoA_Instance aftei; + + memset(&aftdw, 0, sizeof(aftdw)); + aftdw.cbStruct = sizeof(aftdw); + aftdw.dwFilterTagIndex = paftda->dwFilterTagIndex; + aftdw.dwFilterTag = paftda->dwFilterTag; + + aftei.paftda = paftda; + aftei.dwInstance = dwInstance; + aftei.fnCallback = fnCallback; + + return acmFilterTagEnumW(had, &aftdw, MSACM_FilterTagEnumCallbackWtoA, + (DWORD)&aftei, fdwEnum); +} + +/*********************************************************************** + * acmFilterTagEnumW (MSACM32.@) + */ +MMRESULT WINAPI acmFilterTagEnumW(HACMDRIVER had, PACMFILTERTAGDETAILSW paftd, + ACMFILTERTAGENUMCBW fnCallback, DWORD dwInstance, + DWORD fdwEnum) +{ + PWINE_ACMDRIVERID padid; + int i; + + TRACE("(%p, %p, %p, %ld, %ld)\n", + had, paftd, fnCallback, dwInstance, fdwEnum); + + if (paftd->cbStruct < sizeof(*paftd)) return MMSYSERR_INVALPARAM; + + if (had) FIXME("had != NULL, not supported\n"); + + for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) { + /* should check for codec only */ + if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) && + acmDriverOpen(&had, (HACMDRIVERID)padid, 0) == MMSYSERR_NOERROR) { + + for (i = 0; i < padid->cFilterTags; i++) { + paftd->dwFilterTagIndex = i; + if (acmFilterTagDetailsW(had, paftd, ACM_FILTERTAGDETAILSF_INDEX) == MMSYSERR_NOERROR) { + if (!(fnCallback)((HACMDRIVERID)padid, paftd, dwInstance, padid->fdwSupport)) { + padid = NULL; + break; + } + } + } + } + acmDriverClose(had, 0); + } + return MMSYSERR_NOERROR; +} diff --git a/reactos/dll/msacm/format.c b/reactos/dll/msacm/format.c new file mode 100644 index 00000000000..b64f8b0db5d --- /dev/null +++ b/reactos/dll/msacm/format.c @@ -0,0 +1,858 @@ +/* -*- tab-width: 8; c-basic-offset: 4 -*- */ + +/* + * MSACM32 library + * + * Copyright 1998 Patrik Stridvall + * 2000 Eric Pouech + * + * 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 +#include +#include "windef.h" +#include "winbase.h" +#include "winnls.h" +#include "winerror.h" +#include "wingdi.h" +#include "winuser.h" +#include "wine/unicode.h" +#include "wine/debug.h" +#include "mmsystem.h" +#include "mmreg.h" +#include "msacm.h" +#include "msacmdrv.h" +#include "wineacm.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msacm); + +static PACMFORMATCHOOSEA afc; + +struct MSACM_FillFormatData { + HWND hWnd; +#define WINE_ACMFF_TAG 0 +#define WINE_ACMFF_FORMAT 1 +#define WINE_ACMFF_WFX 2 + int mode; + char szFormatTag[ACMFORMATTAGDETAILS_FORMATTAG_CHARS]; + PACMFORMATCHOOSEA afc; + DWORD ret; +}; + +static BOOL CALLBACK MSACM_FillFormatTagsCB(HACMDRIVERID hadid, + PACMFORMATTAGDETAILSA paftd, + DWORD dwInstance, DWORD fdwSupport) +{ + struct MSACM_FillFormatData* affd = (struct MSACM_FillFormatData*)dwInstance; + + switch (affd->mode) { + case WINE_ACMFF_TAG: + if (SendDlgItemMessageA(affd->hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, + CB_FINDSTRINGEXACT, + (WPARAM)-1, (LPARAM)paftd->szFormatTag) == CB_ERR) + SendDlgItemMessageA(affd->hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, + CB_ADDSTRING, 0, (DWORD)paftd->szFormatTag); + break; + case WINE_ACMFF_FORMAT: + if (strcmp(affd->szFormatTag, paftd->szFormatTag) == 0) { + HACMDRIVER had; + + if (acmDriverOpen(&had, hadid, 0) == MMSYSERR_NOERROR) { + ACMFORMATDETAILSA afd; + int i, idx; + MMRESULT mmr; + char buffer[ACMFORMATDETAILS_FORMAT_CHARS+16]; + + afd.cbStruct = sizeof(afd); + afd.dwFormatTag = paftd->dwFormatTag; + afd.pwfx = HeapAlloc(MSACM_hHeap, 0, paftd->cbFormatSize); + if (!afd.pwfx) return FALSE; + afd.pwfx->wFormatTag = paftd->dwFormatTag; + afd.pwfx->cbSize = paftd->cbFormatSize; + afd.cbwfx = paftd->cbFormatSize; + + for (i = 0; i < paftd->cStandardFormats; i++) { + afd.dwFormatIndex = i; + mmr = acmFormatDetailsA(had, &afd, ACM_FORMATDETAILSF_INDEX); + if (mmr == MMSYSERR_NOERROR) { + strncpy(buffer, afd.szFormat, ACMFORMATTAGDETAILS_FORMATTAG_CHARS); + for (idx = strlen(buffer); + idx < ACMFORMATTAGDETAILS_FORMATTAG_CHARS; idx++) + buffer[idx] = ' '; + wsprintfA(buffer + ACMFORMATTAGDETAILS_FORMATTAG_CHARS, + "%d Ko/s", + (afd.pwfx->nAvgBytesPerSec + 512) / 1024); + SendDlgItemMessageA(affd->hWnd, + IDD_ACMFORMATCHOOSE_CMB_FORMAT, + CB_ADDSTRING, 0, (DWORD)buffer); + } + } + acmDriverClose(had, 0); + SendDlgItemMessageA(affd->hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMAT, + CB_SETCURSEL, 0, 0); + HeapFree(MSACM_hHeap, 0, afd.pwfx); + } + } + break; + case WINE_ACMFF_WFX: + if (strcmp(affd->szFormatTag, paftd->szFormatTag) == 0) { + HACMDRIVER had; + + if (acmDriverOpen(&had, hadid, 0) == MMSYSERR_NOERROR) { + ACMFORMATDETAILSA afd; + + afd.cbStruct = sizeof(afd); + afd.dwFormatTag = paftd->dwFormatTag; + afd.pwfx = affd->afc->pwfx; + afd.cbwfx = affd->afc->cbwfx; + + afd.dwFormatIndex = SendDlgItemMessageA(affd->hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMAT, + CB_GETCURSEL, 0, 0); + affd->ret = acmFormatDetailsA(had, &afd, ACM_FORMATDETAILSF_INDEX); + acmDriverClose(had, 0); + return TRUE; + } + } + break; + default: + FIXME("Unknown mode (%d)\n", affd->mode); + break; + } + return TRUE; +} + +static BOOL MSACM_FillFormatTags(HWND hWnd) +{ + ACMFORMATTAGDETAILSA aftd; + struct MSACM_FillFormatData affd; + + memset(&aftd, 0, sizeof(aftd)); + aftd.cbStruct = sizeof(aftd); + + affd.hWnd = hWnd; + affd.mode = WINE_ACMFF_TAG; + + acmFormatTagEnumA(NULL, &aftd, MSACM_FillFormatTagsCB, (DWORD)&affd, 0); + SendDlgItemMessageA(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, CB_SETCURSEL, 0, 0); + return TRUE; +} + +static BOOL MSACM_FillFormat(HWND hWnd) +{ + ACMFORMATTAGDETAILSA aftd; + struct MSACM_FillFormatData affd; + + SendDlgItemMessageA(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMAT, CB_RESETCONTENT, 0, 0); + + memset(&aftd, 0, sizeof(aftd)); + aftd.cbStruct = sizeof(aftd); + + affd.hWnd = hWnd; + affd.mode = WINE_ACMFF_FORMAT; + SendDlgItemMessageA(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, + CB_GETLBTEXT, + SendDlgItemMessageA(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, + CB_GETCURSEL, 0, 0), + (DWORD)affd.szFormatTag); + + acmFormatTagEnumA(NULL, &aftd, MSACM_FillFormatTagsCB, (DWORD)&affd, 0); + SendDlgItemMessageA(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMAT, CB_SETCURSEL, 0, 0); + return TRUE; +} + +static MMRESULT MSACM_GetWFX(HWND hWnd, PACMFORMATCHOOSEA afc) +{ + ACMFORMATTAGDETAILSA aftd; + struct MSACM_FillFormatData affd; + + memset(&aftd, 0, sizeof(aftd)); + aftd.cbStruct = sizeof(aftd); + + affd.hWnd = hWnd; + affd.mode = WINE_ACMFF_WFX; + affd.afc = afc; + affd.ret = MMSYSERR_NOERROR; + SendDlgItemMessageA(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, + CB_GETLBTEXT, + SendDlgItemMessageA(hWnd, IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, + CB_GETCURSEL, 0, 0), + (DWORD)affd.szFormatTag); + + acmFormatTagEnumA(NULL, &aftd, MSACM_FillFormatTagsCB, (DWORD)&affd, 0); + return affd.ret; +} + +static INT_PTR CALLBACK FormatChooseDlgProc(HWND hWnd, UINT msg, + WPARAM wParam, LPARAM lParam) +{ + + TRACE("hwnd=%p msg=%i 0x%08x 0x%08lx\n", hWnd, msg, wParam, lParam ); + + switch (msg) { + case WM_INITDIALOG: + afc = (PACMFORMATCHOOSEA)lParam; + MSACM_FillFormatTags(hWnd); + MSACM_FillFormat(hWnd); + if ((afc->fdwStyle & ~(ACMFORMATCHOOSE_STYLEF_CONTEXTHELP| + ACMFORMATCHOOSE_STYLEF_SHOWHELP)) != 0) + FIXME("Unsupported style %08lx\n", ((PACMFORMATCHOOSEA)lParam)->fdwStyle); + if (!(afc->fdwStyle & ACMFORMATCHOOSE_STYLEF_SHOWHELP)) + ShowWindow(GetDlgItem(hWnd, IDD_ACMFORMATCHOOSE_BTN_HELP), SW_HIDE); + return TRUE; + + case WM_COMMAND: + switch (LOWORD(wParam)) { + case IDOK: + EndDialog(hWnd, MSACM_GetWFX(hWnd, afc)); + return TRUE; + case IDCANCEL: + EndDialog(hWnd, ACMERR_CANCELED); + return TRUE; + case IDD_ACMFORMATCHOOSE_CMB_FORMATTAG: + switch (HIWORD(wParam)) { + case CBN_SELCHANGE: + MSACM_FillFormat(hWnd); + break; + default: + TRACE("Dropped dlgNotif (fmtTag): 0x%08x 0x%08lx\n", + HIWORD(wParam), lParam); + break; + } + break; + case IDD_ACMFORMATCHOOSE_BTN_HELP: + if (afc->fdwStyle & ACMFORMATCHOOSE_STYLEF_SHOWHELP) + SendMessageA(afc->hwndOwner, + RegisterWindowMessageA(ACMHELPMSGSTRINGA), 0L, 0L); + break; + + default: + TRACE("Dropped dlgCmd: ctl=%d ntf=0x%04x 0x%08lx\n", + LOWORD(wParam), HIWORD(wParam), lParam); + break; + } + break; + case WM_CONTEXTMENU: + if (afc->fdwStyle & ACMFORMATCHOOSE_STYLEF_CONTEXTHELP) + SendMessageA(afc->hwndOwner, + RegisterWindowMessageA(ACMHELPMSGCONTEXTMENUA), + wParam, lParam); + break; +#if defined(WM_CONTEXTHELP) + case WM_CONTEXTHELP: + if (afc->fdwStyle & ACMFORMATCHOOSE_STYLEF_CONTEXTHELP) + SendMessageA(afc->hwndOwner, + RegisterWindowMessageA(ACMHELPMSGCONTEXTHELPA), + wParam, lParam); + break; +#endif + default: + TRACE("Dropped dlgMsg: hwnd=%p msg=%i 0x%08x 0x%08lx\n", + hWnd, msg, wParam, lParam ); + break; + } + return FALSE; +} + +/*********************************************************************** + * acmFormatChooseA (MSACM32.@) + */ +MMRESULT WINAPI acmFormatChooseA(PACMFORMATCHOOSEA pafmtc) +{ + return DialogBoxParamA(MSACM_hInstance32, MAKEINTRESOURCEA(DLG_ACMFORMATCHOOSE_ID), + pafmtc->hwndOwner, FormatChooseDlgProc, (INT)pafmtc); +} + +/*********************************************************************** + * acmFormatChooseW (MSACM32.@) + */ +MMRESULT WINAPI acmFormatChooseW(PACMFORMATCHOOSEW pafmtc) +{ + FIXME("(%p): stub\n", pafmtc); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmFormatDetailsA (MSACM32.@) + */ +MMRESULT WINAPI acmFormatDetailsA(HACMDRIVER had, PACMFORMATDETAILSA pafd, + DWORD fdwDetails) +{ + ACMFORMATDETAILSW afdw; + MMRESULT mmr; + + memset(&afdw, 0, sizeof(afdw)); + afdw.cbStruct = sizeof(afdw); + afdw.dwFormatIndex = pafd->dwFormatIndex; + afdw.dwFormatTag = pafd->dwFormatTag; + afdw.pwfx = pafd->pwfx; + afdw.cbwfx = pafd->cbwfx; + + mmr = acmFormatDetailsW(had, &afdw, fdwDetails); + if (mmr == MMSYSERR_NOERROR) { + pafd->dwFormatTag = afdw.dwFormatTag; + pafd->fdwSupport = afdw.fdwSupport; + WideCharToMultiByte( CP_ACP, 0, afdw.szFormat, -1, + pafd->szFormat, sizeof(pafd->szFormat), NULL, NULL ); + } + return mmr; +} + +/*********************************************************************** + * acmFormatDetailsW (MSACM32.@) + */ +MMRESULT WINAPI acmFormatDetailsW(HACMDRIVER had, PACMFORMATDETAILSW pafd, DWORD fdwDetails) +{ + MMRESULT mmr; + static const WCHAR fmt1[] = {'%','d',' ','H','z',0}; + static const WCHAR fmt2[] = {';',' ','%','d',' ','b','i','t','s',0}; + ACMFORMATTAGDETAILSA aftd; + + TRACE("(%p, %p, %ld)\n", had, pafd, fdwDetails); + + memset(&aftd, 0, sizeof(aftd)); + aftd.cbStruct = sizeof(aftd); + + if (pafd->cbStruct < sizeof(*pafd)) return MMSYSERR_INVALPARAM; + + switch (fdwDetails) { + case ACM_FORMATDETAILSF_FORMAT: + if (pafd->dwFormatTag != pafd->pwfx->wFormatTag) { + mmr = MMSYSERR_INVALPARAM; + break; + } + if (had == NULL) { + PWINE_ACMDRIVERID padid; + + mmr = ACMERR_NOTPOSSIBLE; + for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) { + /* should check for codec only */ + if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) && + acmDriverOpen(&had, (HACMDRIVERID)padid, 0) == 0) { + mmr = MSACM_Message(had, ACMDM_FORMAT_DETAILS, (LPARAM)pafd, fdwDetails); + acmDriverClose(had, 0); + if (mmr == MMSYSERR_NOERROR) break; + } + } + } else { + mmr = MSACM_Message(had, ACMDM_FORMAT_DETAILS, (LPARAM)pafd, fdwDetails); + } + break; + case ACM_FORMATDETAILSF_INDEX: + /* should check pafd->dwFormatIndex < aftd->cStandardFormats */ + mmr = MSACM_Message(had, ACMDM_FORMAT_DETAILS, (LPARAM)pafd, fdwDetails); + break; + default: + WARN("Unknown fdwDetails %08lx\n", fdwDetails); + mmr = MMSYSERR_INVALFLAG; + break; + } + + if (mmr == MMSYSERR_NOERROR && pafd->szFormat[0] == (WCHAR)0) { + wsprintfW(pafd->szFormat, fmt1, pafd->pwfx->nSamplesPerSec); + if (pafd->pwfx->wBitsPerSample) { + wsprintfW(pafd->szFormat + lstrlenW(pafd->szFormat), fmt2, + pafd->pwfx->wBitsPerSample); + } + MultiByteToWideChar( CP_ACP, 0, (pafd->pwfx->nChannels == 1) ? "; Mono" : "; Stereo", -1, + pafd->szFormat + strlenW(pafd->szFormat), + sizeof(pafd->szFormat)/sizeof(WCHAR) - strlenW(pafd->szFormat) ); + } + + TRACE("=> %d\n", mmr); + return mmr; +} + +struct MSACM_FormatEnumWtoA_Instance { + PACMFORMATDETAILSA pafda; + DWORD dwInstance; + ACMFORMATENUMCBA fnCallback; +}; + +static BOOL CALLBACK MSACM_FormatEnumCallbackWtoA(HACMDRIVERID hadid, + PACMFORMATDETAILSW pafdw, + DWORD dwInstance, + DWORD fdwSupport) +{ + struct MSACM_FormatEnumWtoA_Instance* pafei; + + pafei = (struct MSACM_FormatEnumWtoA_Instance*)dwInstance; + + pafei->pafda->dwFormatIndex = pafdw->dwFormatIndex; + pafei->pafda->dwFormatTag = pafdw->dwFormatTag; + pafei->pafda->fdwSupport = pafdw->fdwSupport; + WideCharToMultiByte( CP_ACP, 0, pafdw->szFormat, -1, + pafei->pafda->szFormat, sizeof(pafei->pafda->szFormat), NULL, NULL ); + + return (pafei->fnCallback)(hadid, pafei->pafda, + pafei->dwInstance, fdwSupport); +} + +/*********************************************************************** + * acmFormatEnumA (MSACM32.@) + */ +MMRESULT WINAPI acmFormatEnumA(HACMDRIVER had, PACMFORMATDETAILSA pafda, + ACMFORMATENUMCBA fnCallback, DWORD dwInstance, + DWORD fdwEnum) +{ + ACMFORMATDETAILSW afdw; + struct MSACM_FormatEnumWtoA_Instance afei; + + if (!pafda) + return MMSYSERR_INVALPARAM; + + if (pafda->cbStruct < sizeof(*pafda)) + return MMSYSERR_INVALPARAM; + + memset(&afdw, 0, sizeof(afdw)); + afdw.cbStruct = sizeof(afdw); + afdw.dwFormatIndex = pafda->dwFormatIndex; + afdw.dwFormatTag = pafda->dwFormatTag; + afdw.pwfx = pafda->pwfx; + afdw.cbwfx = pafda->cbwfx; + + afei.pafda = pafda; + afei.dwInstance = dwInstance; + afei.fnCallback = fnCallback; + + return acmFormatEnumW(had, &afdw, MSACM_FormatEnumCallbackWtoA, + (DWORD)&afei, fdwEnum); +} + +/*********************************************************************** + * acmFormatEnumW (MSACM32.@) + */ +static BOOL MSACM_FormatEnumHelper(PWINE_ACMDRIVERID padid, HACMDRIVER had, + PACMFORMATDETAILSW pafd, PWAVEFORMATEX pwfxRef, + ACMFORMATENUMCBW fnCallback, DWORD dwInstance, + DWORD fdwEnum) +{ + ACMFORMATTAGDETAILSW aftd; + int i, j; + + for (i = 0; i < padid->cFormatTags; i++) { + memset(&aftd, 0, sizeof(aftd)); + aftd.cbStruct = sizeof(aftd); + aftd.dwFormatTagIndex = i; + if (acmFormatTagDetailsW(had, &aftd, ACM_FORMATTAGDETAILSF_INDEX) != MMSYSERR_NOERROR) + continue; + + if ((fdwEnum & ACM_FORMATENUMF_WFORMATTAG) && aftd.dwFormatTag != pwfxRef->wFormatTag) + continue; + + for (j = 0; j < aftd.cStandardFormats; j++) { + pafd->dwFormatIndex = j; + pafd->dwFormatTag = aftd.dwFormatTag; + if (acmFormatDetailsW(had, pafd, ACM_FORMATDETAILSF_INDEX) != MMSYSERR_NOERROR) + continue; + + if ((fdwEnum & ACM_FORMATENUMF_NCHANNELS) && + pafd->pwfx->nChannels != pwfxRef->nChannels) + continue; + if ((fdwEnum & ACM_FORMATENUMF_NSAMPLESPERSEC) && + pafd->pwfx->nSamplesPerSec != pwfxRef->nSamplesPerSec) + continue; + if ((fdwEnum & ACM_FORMATENUMF_WBITSPERSAMPLE) && + pafd->pwfx->wBitsPerSample != pwfxRef->wBitsPerSample) + continue; + if ((fdwEnum & ACM_FORMATENUMF_HARDWARE) && + !(pafd->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_HARDWARE)) + continue; + + /* more checks to be done on fdwEnum */ + + if (!(fnCallback)((HACMDRIVERID)padid, pafd, dwInstance, padid->fdwSupport)) + return FALSE; + } + /* the "formats" used by the filters are also reported */ + } + return TRUE; +} + +/**********************************************************************/ + +MMRESULT WINAPI acmFormatEnumW(HACMDRIVER had, PACMFORMATDETAILSW pafd, + ACMFORMATENUMCBW fnCallback, DWORD dwInstance, + DWORD fdwEnum) +{ + PWINE_ACMDRIVERID padid; + WAVEFORMATEX wfxRef; + BOOL ret; + + TRACE("(%p, %p, %p, %ld, %ld)\n", + had, pafd, fnCallback, dwInstance, fdwEnum); + + if (!pafd) + return MMSYSERR_INVALPARAM; + + if (pafd->cbStruct < sizeof(*pafd)) + return MMSYSERR_INVALPARAM; + + if (fdwEnum & (ACM_FORMATENUMF_WFORMATTAG|ACM_FORMATENUMF_NCHANNELS| + ACM_FORMATENUMF_NSAMPLESPERSEC|ACM_FORMATENUMF_WBITSPERSAMPLE| + ACM_FORMATENUMF_CONVERT|ACM_FORMATENUMF_SUGGEST)) + wfxRef = *pafd->pwfx; + + if ((fdwEnum & ACM_FORMATENUMF_HARDWARE) && + !(fdwEnum & (ACM_FORMATENUMF_INPUT|ACM_FORMATENUMF_OUTPUT))) + return MMSYSERR_INVALPARAM; + + if ((fdwEnum & ACM_FORMATENUMF_WFORMATTAG) && + (pafd->dwFormatTag != pafd->pwfx->wFormatTag)) + return MMSYSERR_INVALPARAM; + + if (fdwEnum & (ACM_FORMATENUMF_CONVERT|ACM_FORMATENUMF_SUGGEST| + ACM_FORMATENUMF_INPUT|ACM_FORMATENUMF_OUTPUT)) + FIXME("Unsupported fdwEnum values %08lx\n", fdwEnum); + + if (had) { + HACMDRIVERID hadid; + + if (acmDriverID((HACMOBJ)had, &hadid, 0) != MMSYSERR_NOERROR) + return MMSYSERR_INVALHANDLE; + MSACM_FormatEnumHelper(MSACM_GetDriverID(hadid), had, pafd, &wfxRef, + fnCallback, dwInstance, fdwEnum); + return MMSYSERR_NOERROR; + } + for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) { + /* should check for codec only */ + if ((padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) || + acmDriverOpen(&had, (HACMDRIVERID)padid, 0) != MMSYSERR_NOERROR) + continue; + ret = MSACM_FormatEnumHelper(padid, had, pafd, &wfxRef, + fnCallback, dwInstance, fdwEnum); + acmDriverClose(had, 0); + if (!ret) break; + } + return MMSYSERR_NOERROR; +} + +/*********************************************************************** + * acmFormatSuggest (MSACM32.@) + */ +MMRESULT WINAPI acmFormatSuggest(HACMDRIVER had, PWAVEFORMATEX pwfxSrc, + PWAVEFORMATEX pwfxDst, DWORD cbwfxDst, DWORD fdwSuggest) +{ + ACMDRVFORMATSUGGEST adfg; + MMRESULT mmr; + + TRACE("(%p, %p, %p, %ld, %ld)\n", + had, pwfxSrc, pwfxDst, cbwfxDst, fdwSuggest); + + if (fdwSuggest & ~(ACM_FORMATSUGGESTF_NCHANNELS|ACM_FORMATSUGGESTF_NSAMPLESPERSEC| + ACM_FORMATSUGGESTF_WBITSPERSAMPLE|ACM_FORMATSUGGESTF_WFORMATTAG)) + return MMSYSERR_INVALFLAG; + + adfg.cbStruct = sizeof(adfg); + adfg.fdwSuggest = fdwSuggest; + adfg.pwfxSrc = pwfxSrc; + adfg.cbwfxSrc = (pwfxSrc->wFormatTag == WAVE_FORMAT_PCM) ? + sizeof(WAVEFORMATEX) : (sizeof(WAVEFORMATEX) + pwfxSrc->cbSize); + adfg.pwfxDst = pwfxDst; + adfg.cbwfxDst = cbwfxDst; + + if (had == NULL) { + PWINE_ACMDRIVERID padid; + + /* MS doc says: ACM finds the best suggestion. + * Well, first found will be the "best" + */ + mmr = ACMERR_NOTPOSSIBLE; + for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) { + /* should check for codec only */ + if ((padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) || + acmDriverOpen(&had, (HACMDRIVERID)padid, 0) != MMSYSERR_NOERROR) + continue; + + if (MSACM_Message(had, ACMDM_FORMAT_SUGGEST, (LPARAM)&adfg, 0L) == MMSYSERR_NOERROR) { + mmr = MMSYSERR_NOERROR; + break; + } + acmDriverClose(had, 0); + } + } else { + mmr = MSACM_Message(had, ACMDM_FORMAT_SUGGEST, (LPARAM)&adfg, 0L); + } + return mmr; +} + +/*********************************************************************** + * acmFormatTagDetailsA (MSACM32.@) + */ +MMRESULT WINAPI acmFormatTagDetailsA(HACMDRIVER had, PACMFORMATTAGDETAILSA paftda, + DWORD fdwDetails) +{ + ACMFORMATTAGDETAILSW aftdw; + MMRESULT mmr; + + memset(&aftdw, 0, sizeof(aftdw)); + aftdw.cbStruct = sizeof(aftdw); + aftdw.dwFormatTagIndex = paftda->dwFormatTagIndex; + aftdw.dwFormatTag = paftda->dwFormatTag; + + mmr = acmFormatTagDetailsW(had, &aftdw, fdwDetails); + if (mmr == MMSYSERR_NOERROR) { + paftda->dwFormatTag = aftdw.dwFormatTag; + paftda->dwFormatTagIndex = aftdw.dwFormatTagIndex; + paftda->cbFormatSize = aftdw.cbFormatSize; + paftda->fdwSupport = aftdw.fdwSupport; + paftda->cStandardFormats = aftdw.cStandardFormats; + WideCharToMultiByte( CP_ACP, 0, aftdw.szFormatTag, -1, paftda->szFormatTag, + sizeof(paftda->szFormatTag), NULL, NULL ); + } + return mmr; +} + +/*********************************************************************** + * acmFormatTagDetailsW (MSACM32.@) + */ +MMRESULT WINAPI acmFormatTagDetailsW(HACMDRIVER had, PACMFORMATTAGDETAILSW paftd, + DWORD fdwDetails) +{ + PWINE_ACMDRIVERID padid; + MMRESULT mmr = ACMERR_NOTPOSSIBLE; + + TRACE("(%p, %p, %ld)\n", had, paftd, fdwDetails); + + if (fdwDetails & ~(ACM_FORMATTAGDETAILSF_FORMATTAG|ACM_FORMATTAGDETAILSF_INDEX| + ACM_FORMATTAGDETAILSF_LARGESTSIZE)) + return MMSYSERR_INVALFLAG; + + switch (fdwDetails) { + case ACM_FORMATTAGDETAILSF_FORMATTAG: + if (had == NULL) { + for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) { + /* should check for codec only */ + if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) && + MSACM_FindFormatTagInCache(padid, paftd->dwFormatTag, NULL) && + acmDriverOpen(&had, (HACMDRIVERID)padid, 0) == 0) { + mmr = MSACM_Message(had, ACMDM_FORMATTAG_DETAILS, (LPARAM)paftd, fdwDetails); + acmDriverClose(had, 0); + if (mmr == MMSYSERR_NOERROR) break; + } + } + } else { + PWINE_ACMDRIVER pad = MSACM_GetDriver(had); + + if (pad && MSACM_FindFormatTagInCache(pad->obj.pACMDriverID, paftd->dwFormatTag, NULL)) + mmr = MSACM_Message(had, ACMDM_FORMATTAG_DETAILS, (LPARAM)paftd, fdwDetails); + } + break; + + case ACM_FORMATTAGDETAILSF_INDEX: + if (had != NULL) { + PWINE_ACMDRIVER pad = MSACM_GetDriver(had); + + if (pad && paftd->dwFormatTagIndex < pad->obj.pACMDriverID->cFormatTags) + mmr = MSACM_Message(had, ACMDM_FORMATTAG_DETAILS, (LPARAM)paftd, fdwDetails); + } + break; + + case ACM_FORMATTAGDETAILSF_LARGESTSIZE: + if (had == NULL) { + ACMFORMATTAGDETAILSW tmp; + DWORD ft = paftd->dwFormatTag; + + for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) { + /* should check for codec only */ + if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) && + acmDriverOpen(&had, (HACMDRIVERID)padid, 0) == 0) { + + memset(&tmp, 0, sizeof(tmp)); + tmp.cbStruct = sizeof(tmp); + tmp.dwFormatTag = ft; + + if (MSACM_Message(had, ACMDM_FORMATTAG_DETAILS, + (LPARAM)&tmp, fdwDetails) == MMSYSERR_NOERROR) { + if (mmr == ACMERR_NOTPOSSIBLE || + paftd->cbFormatSize < tmp.cbFormatSize) { + *paftd = tmp; + mmr = MMSYSERR_NOERROR; + } + } + acmDriverClose(had, 0); + } + } + } else { + mmr = MSACM_Message(had, ACMDM_FORMATTAG_DETAILS, (LPARAM)paftd, fdwDetails); + } + break; + + default: + WARN("Unsupported fdwDetails=%08lx\n", fdwDetails); + mmr = MMSYSERR_ERROR; + } + + if (mmr == MMSYSERR_NOERROR && + paftd->dwFormatTag == WAVE_FORMAT_PCM && paftd->szFormatTag[0] == 0) + MultiByteToWideChar( CP_ACP, 0, "PCM", -1, paftd->szFormatTag, + sizeof(paftd->szFormatTag)/sizeof(WCHAR) ); + + return mmr; +} + +struct MSACM_FormatTagEnumWtoA_Instance { + PACMFORMATTAGDETAILSA paftda; + DWORD dwInstance; + ACMFORMATTAGENUMCBA fnCallback; +}; + +static BOOL CALLBACK MSACM_FormatTagEnumCallbackWtoA(HACMDRIVERID hadid, + PACMFORMATTAGDETAILSW paftdw, + DWORD dwInstance, + DWORD fdwSupport) +{ + struct MSACM_FormatTagEnumWtoA_Instance* paftei; + + paftei = (struct MSACM_FormatTagEnumWtoA_Instance*)dwInstance; + + paftei->paftda->dwFormatTagIndex = paftdw->dwFormatTagIndex; + paftei->paftda->dwFormatTag = paftdw->dwFormatTag; + paftei->paftda->cbFormatSize = paftdw->cbFormatSize; + paftei->paftda->fdwSupport = paftdw->fdwSupport; + paftei->paftda->cStandardFormats = paftdw->cStandardFormats; + WideCharToMultiByte( CP_ACP, 0, paftdw->szFormatTag, -1, paftei->paftda->szFormatTag, + sizeof(paftei->paftda->szFormatTag), NULL, NULL ); + + return (paftei->fnCallback)(hadid, paftei->paftda, + paftei->dwInstance, fdwSupport); +} + +/*********************************************************************** + * acmFormatTagEnumA (MSACM32.@) + */ +MMRESULT WINAPI acmFormatTagEnumA(HACMDRIVER had, PACMFORMATTAGDETAILSA paftda, + ACMFORMATTAGENUMCBA fnCallback, DWORD dwInstance, + DWORD fdwEnum) +{ + ACMFORMATTAGDETAILSW aftdw; + struct MSACM_FormatTagEnumWtoA_Instance aftei; + + if (!paftda) + return MMSYSERR_INVALPARAM; + + if (paftda->cbStruct < sizeof(*paftda)) + return MMSYSERR_INVALPARAM; + + if (fdwEnum != 0) + return MMSYSERR_INVALFLAG; + + memset(&aftdw, 0, sizeof(aftdw)); + aftdw.cbStruct = sizeof(aftdw); + aftdw.dwFormatTagIndex = paftda->dwFormatTagIndex; + aftdw.dwFormatTag = paftda->dwFormatTag; + + aftei.paftda = paftda; + aftei.dwInstance = dwInstance; + aftei.fnCallback = fnCallback; + + return acmFormatTagEnumW(had, &aftdw, MSACM_FormatTagEnumCallbackWtoA, + (DWORD)&aftei, fdwEnum); +} + +/*********************************************************************** + * acmFormatTagEnumW (MSACM32.@) + */ +MMRESULT WINAPI acmFormatTagEnumW(HACMDRIVER had, PACMFORMATTAGDETAILSW paftd, + ACMFORMATTAGENUMCBW fnCallback, DWORD dwInstance, + DWORD fdwEnum) +{ + PWINE_ACMDRIVERID padid; + int i; + BOOL bPcmDone = FALSE; + + TRACE("(%p, %p, %p, %ld, %ld)\n", + had, paftd, fnCallback, dwInstance, fdwEnum); + + if (!paftd) + return MMSYSERR_INVALPARAM; + + if (paftd->cbStruct < sizeof(*paftd)) + return MMSYSERR_INVALPARAM; + + if (fdwEnum != 0) + return MMSYSERR_INVALFLAG; + + /* (WS) MSDN info page says that if had != 0, then we should find + * the specific driver to get its tags from. Therefore I'm removing + * the FIXME call and adding a search block below. It also seems + * that the lack of this functionality was the responsible for + * codecs to be multiply and incorrectly listed. + */ + + /* if (had) FIXME("had != NULL, not supported\n"); */ + + if (had) { + + if (acmDriverID((HACMOBJ)had, (HACMDRIVERID *)&padid, 0) != MMSYSERR_NOERROR) + return MMSYSERR_INVALHANDLE; + + for (i = 0; i < padid->cFormatTags; i++) { + paftd->dwFormatTagIndex = i; + if (MSACM_Message(had, ACMDM_FORMATTAG_DETAILS, + (LPARAM)paftd, ACM_FORMATTAGDETAILSF_INDEX) == MMSYSERR_NOERROR) { + if (paftd->dwFormatTag == WAVE_FORMAT_PCM) { + if (paftd->szFormatTag[0] == 0) + MultiByteToWideChar( CP_ACP, 0, "PCM", -1, paftd->szFormatTag, + sizeof(paftd->szFormatTag)/sizeof(WCHAR) ); + /* (WS) I'm preserving this PCM hack since it seems to be + * correct. Please notice this block was borrowed from + * below. + */ + if (bPcmDone) continue; + bPcmDone = TRUE; + } + if (!(fnCallback)((HACMDRIVERID)padid, paftd, dwInstance, padid->fdwSupport)) + return MMSYSERR_NOERROR; + } + } + + } + + /* if had==0 then search for the first suitable driver */ + else { + for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) { + /* should check for codec only */ + if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) && + acmDriverOpen(&had, (HACMDRIVERID)padid, 0) == MMSYSERR_NOERROR) { + for (i = 0; i < padid->cFormatTags; i++) { + paftd->dwFormatTagIndex = i; + if (MSACM_Message(had, ACMDM_FORMATTAG_DETAILS, + (LPARAM)paftd, ACM_FORMATTAGDETAILSF_INDEX) == MMSYSERR_NOERROR) { + if (paftd->dwFormatTag == WAVE_FORMAT_PCM) { + if (paftd->szFormatTag[0] == 0) + MultiByteToWideChar( CP_ACP, 0, "PCM", -1, paftd->szFormatTag, + sizeof(paftd->szFormatTag)/sizeof(WCHAR) ); + /* FIXME (EPP): I'm not sure this is the correct + * algorithm (should make more sense to apply the same + * for all already loaded formats, but this will do + * for now + */ + if (bPcmDone) continue; + bPcmDone = TRUE; + } + if (!(fnCallback)((HACMDRIVERID)padid, paftd, dwInstance, padid->fdwSupport)) { + acmDriverClose(had, 0); + return MMSYSERR_NOERROR; + } + } + } + } + acmDriverClose(had, 0); + } + } + return MMSYSERR_NOERROR; +} diff --git a/reactos/dll/msacm/imaadp32/Makefile.in b/reactos/dll/msacm/imaadp32/Makefile.in new file mode 100644 index 00000000000..79cc94e4aee --- /dev/null +++ b/reactos/dll/msacm/imaadp32/Makefile.in @@ -0,0 +1,12 @@ +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = ../../.. +SRCDIR = @srcdir@ +VPATH = @srcdir@ +MODULE = imaadp32.acm +IMPORTS = winmm user32 kernel32 + +C_SRCS = imaadp32.c + +@MAKE_DLL_RULES@ + +### Dependencies: diff --git a/reactos/dll/msacm/imaadp32/imaadp32.acm.spec b/reactos/dll/msacm/imaadp32/imaadp32.acm.spec new file mode 100644 index 00000000000..a0ba1c7e701 --- /dev/null +++ b/reactos/dll/msacm/imaadp32/imaadp32.acm.spec @@ -0,0 +1 @@ +@ stdcall DriverProc (long long long long long) ADPCM_DriverProc diff --git a/reactos/dll/msacm/imaadp32/imaadp32.c b/reactos/dll/msacm/imaadp32/imaadp32.c new file mode 100644 index 00000000000..61d3d00c2bc --- /dev/null +++ b/reactos/dll/msacm/imaadp32/imaadp32.c @@ -0,0 +1,938 @@ +/* + * IMA ADPCM handling + * + * Copyright (C) 2001,2002 Eric Pouech + * + * + * 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 +#include +#include +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "mmsystem.h" +#include "mmreg.h" +#include "msacm.h" +#include "msacmdrv.h" +#include "wine/debug.h" + +/* see http://www.pcisys.net/~melanson/codecs/adpcm.txt for the details */ + +WINE_DEFAULT_DEBUG_CHANNEL(adpcm); + +/*********************************************************************** + * ADPCM_drvOpen + */ +static DWORD ADPCM_drvOpen(LPCSTR str) +{ + return 1; +} + +/*********************************************************************** + * ADPCM_drvClose + */ +static DWORD ADPCM_drvClose(DWORD dwDevID) +{ + return 1; +} + +typedef struct tagAcmAdpcmData +{ + void (*convert)(PACMDRVSTREAMINSTANCE adsi, + const unsigned char*, LPDWORD, unsigned char*, LPDWORD); + /* IMA encoding only */ + BYTE stepIndexL; + BYTE stepIndexR; + /* short sample; */ +} AcmAdpcmData; + +/* table to list all supported formats... those are the basic ones. this + * also helps given a unique index to each of the supported formats + */ +typedef struct +{ + int nChannels; + int nBits; + int rate; +} Format; + +static Format PCM_Formats[] = +{ + {1, 8, 8000}, {2, 8, 8000}, {1, 16, 8000}, {2, 16, 8000}, + {1, 8, 11025}, {2, 8, 11025}, {1, 16, 11025}, {2, 16, 11025}, + {1, 8, 22050}, {2, 8, 22050}, {1, 16, 22050}, {2, 16, 22050}, + {1, 8, 44100}, {2, 8, 44100}, {1, 16, 44100}, {2, 16, 44100}, +}; + +static Format ADPCM_Formats[] = +{ + {1, 4, 8000}, {2, 4, 8000}, {1, 4, 11025}, {2, 4, 11025}, + {1, 4, 22050}, {2, 4, 22050}, {1, 4, 44100}, {2, 4, 44100}, +}; + +#define NUM_PCM_FORMATS (sizeof(PCM_Formats) / sizeof(PCM_Formats[0])) +#define NUM_ADPCM_FORMATS (sizeof(ADPCM_Formats) / sizeof(ADPCM_Formats[0])) + +/*********************************************************************** + * ADPCM_GetFormatIndex + */ +static DWORD ADPCM_GetFormatIndex(LPWAVEFORMATEX wfx) +{ + int i, hi; + Format* fmts; + + switch (wfx->wFormatTag) + { + case WAVE_FORMAT_PCM: + hi = NUM_PCM_FORMATS; + fmts = PCM_Formats; + break; + case WAVE_FORMAT_IMA_ADPCM: + hi = NUM_ADPCM_FORMATS; + fmts = ADPCM_Formats; + break; + default: + return 0xFFFFFFFF; + } + + for (i = 0; i < hi; i++) + { + if (wfx->nChannels == fmts[i].nChannels && + wfx->nSamplesPerSec == fmts[i].rate && + wfx->wBitsPerSample == fmts[i].nBits) + return i; + } + + return 0xFFFFFFFF; +} + +/*********************************************************************** + * R16 + * + * Read a 16 bit sample (correctly handles endianess) + */ +static inline short R16(const unsigned char* src) +{ + return (short)((unsigned short)src[0] | ((unsigned short)src[1] << 8)); +} + +/*********************************************************************** + * W16 + * + * Write a 16 bit sample (correctly handles endianess) + */ +static inline void W16(unsigned char* dst, short s) +{ + dst[0] = LOBYTE(s); + dst[1] = HIBYTE(s); +} + +/* IMA (or DVI) APDCM codec routines */ + +static const unsigned IMA_StepTable[89] = +{ + 7, 8, 9, 10, 11, 12, 13, 14, + 16, 17, 19, 21, 23, 25, 28, 31, + 34, 37, 41, 45, 50, 55, 60, 66, + 73, 80, 88, 97, 107, 118, 130, 143, + 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, + 724, 796, 876, 963, 1060, 1166, 1282, 1411, + 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, + 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, + 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, + 32767 +}; + +static const int IMA_IndexTable[16] = +{ + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8 +}; + +static inline void clamp_step_index(int* stepIndex) +{ + if (*stepIndex < 0 ) *stepIndex = 0; + if (*stepIndex > 88) *stepIndex = 88; +} + +static inline void clamp_sample(int* sample) +{ + if (*sample < -32768) *sample = -32768; + if (*sample > 32767) *sample = 32767; +} + +static inline void process_nibble(unsigned char code, int* stepIndex, int* sample) +{ + unsigned step; + int diff; + + code &= 0x0F; + + step = IMA_StepTable[*stepIndex]; + diff = step >> 3; + if (code & 1) diff += step >> 2; + if (code & 2) diff += step >> 1; + if (code & 4) diff += step; + if (code & 8) *sample -= diff; + else *sample += diff; + clamp_sample(sample); + *stepIndex += IMA_IndexTable[code]; + clamp_step_index(stepIndex); +} + +static inline unsigned char generate_nibble(int in, int* stepIndex, int* sample) +{ + int effdiff, diff = in - *sample; + unsigned step; + unsigned char code; + + if (diff < 0) + { + diff = -diff; + code = 8; + } + else + { + code = 0; + } + + step = IMA_StepTable[*stepIndex]; + effdiff = (step >> 3); + if (diff >= step) + { + code |= 4; + diff -= step; + effdiff += step; + } + step >>= 1; + if (diff >= step) + { + code |= 2; + diff -= step; + effdiff += step; + } + step >>= 1; + if (diff >= step) + { + code |= 1; + effdiff += step; + } + if (code & 8) *sample -= effdiff; + else *sample += effdiff; + clamp_sample(sample); + *stepIndex += IMA_IndexTable[code]; + clamp_step_index(stepIndex); + return code; +} + +static void cvtSSima16K(PACMDRVSTREAMINSTANCE adsi, + const unsigned char* src, LPDWORD nsrc, + unsigned char* dst, LPDWORD ndst) +{ + int i; + int sampleL, sampleR; + int stepIndexL, stepIndexR; + int nsamp_blk = ((LPIMAADPCMWAVEFORMAT)adsi->pwfxSrc)->wSamplesPerBlock; + int nsamp; + /* compute the number of entire blocks we can decode... + * it's the min of the number of entire blocks in source buffer and the number + * of entire blocks in destination buffer + */ + DWORD nblock = min(*nsrc / adsi->pwfxSrc->nBlockAlign, + *ndst / (nsamp_blk * 2 * 2)); + + *nsrc = nblock * adsi->pwfxSrc->nBlockAlign; + *ndst = nblock * (nsamp_blk * 2 * 2); + + nsamp_blk--; /* remove the sample in block header */ + for (; nblock > 0; nblock--) + { + const unsigned char* in_src = src; + + /* handle headers first */ + sampleL = R16(src); + stepIndexL = (unsigned)*(src + 2); + clamp_step_index(&stepIndexL); + src += 4; + W16(dst, sampleL); dst += 2; + + sampleR = R16(src); + stepIndexR = (unsigned)*(src + 2); + clamp_step_index(&stepIndexR); + src += 4; + W16(dst, sampleR); dst += 2; + + for (nsamp = nsamp_blk; nsamp > 0; nsamp -= 8) + { + for (i = 0; i < 4; i++) + { + process_nibble(*src, &stepIndexL, &sampleL); + W16(dst + (2 * i + 0) * 4 + 0, sampleL); + process_nibble(*src++ >> 4, &stepIndexL, &sampleL); + W16(dst + (2 * i + 1) * 4 + 0, sampleL); + } + for (i = 0; i < 4; i++) + { + process_nibble(*src , &stepIndexR, &sampleR); + W16(dst + (2 * i + 0) * 4 + 2, sampleR); + process_nibble(*src++ >>4, &stepIndexR, &sampleR); + W16(dst + (2 * i + 1) * 4 + 2, sampleR); + } + dst += 32; + } + /* we have now to realign the source pointer on block */ + src = in_src + adsi->pwfxSrc->nBlockAlign; + } +} + +static void cvtMMima16K(PACMDRVSTREAMINSTANCE adsi, + const unsigned char* src, LPDWORD nsrc, + unsigned char* dst, LPDWORD ndst) +{ + int sample; + int stepIndex; + int nsamp_blk = ((LPIMAADPCMWAVEFORMAT)adsi->pwfxSrc)->wSamplesPerBlock; + int nsamp; + /* compute the number of entire blocks we can decode... + * it's the min of the number of entire blocks in source buffer and the number + * of entire blocks in destination buffer + */ + DWORD nblock = min(*nsrc / adsi->pwfxSrc->nBlockAlign, + *ndst / (nsamp_blk * 2)); + + *nsrc = nblock * adsi->pwfxSrc->nBlockAlign; + *ndst = nblock * nsamp_blk * 2; + + nsamp_blk--; /* remove the sample in block header */ + for (; nblock > 0; nblock--) + { + const unsigned char* in_src = src; + + /* handle header first */ + sample = R16(src); + stepIndex = (unsigned)*(src + 2); + clamp_step_index(&stepIndex); + src += 4; + W16(dst, sample); dst += 2; + + for (nsamp = nsamp_blk; nsamp > 0; nsamp -= 2) + { + process_nibble(*src, &stepIndex, &sample); + W16(dst, sample); dst += 2; + process_nibble(*src++ >> 4, &stepIndex, &sample); + W16(dst, sample); dst += 2; + } + /* we have now to realign the source pointer on block */ + src = in_src + adsi->pwfxSrc->nBlockAlign; + } +} + +static void cvtSS16imaK(PACMDRVSTREAMINSTANCE adsi, + const unsigned char* src, LPDWORD nsrc, + unsigned char* dst, LPDWORD ndst) +{ + int stepIndexL, stepIndexR; + int sampleL, sampleR; + BYTE code1, code2; + int nsamp_blk = ((LPIMAADPCMWAVEFORMAT)adsi->pwfxDst)->wSamplesPerBlock; + int i, nsamp; + /* compute the number of entire blocks we can decode... + * it's the min of the number of entire blocks in source buffer and the number + * of entire blocks in destination buffer + */ + DWORD nblock = min(*nsrc / (nsamp_blk * 2 * 2), + *ndst / adsi->pwfxDst->nBlockAlign); + + *nsrc = nblock * (nsamp_blk * 2 * 2); + *ndst = nblock * adsi->pwfxDst->nBlockAlign; + + stepIndexL = ((AcmAdpcmData*)adsi->dwDriver)->stepIndexL; + stepIndexR = ((AcmAdpcmData*)adsi->dwDriver)->stepIndexR; + + nsamp_blk--; /* so that we won't count the sample in header while filling the block */ + + for (; nblock > 0; nblock--) + { + char* in_dst = dst; + + /* generate header */ + sampleL = R16(src); src += 2; + W16(dst, sampleL); dst += 2; + *dst = (unsigned char)(unsigned)stepIndexL; + dst += 2; + + sampleR = R16(src); src += 2; + W16(dst, sampleR); dst += 2; + *dst = (unsigned char)(unsigned)stepIndexR; + dst += 2; + + for (nsamp = nsamp_blk; nsamp > 0; nsamp -= 8) + { + for (i = 0; i < 4; i++) + { + code1 = generate_nibble(R16(src + (2 * i + 0) * 2 + 0), + &stepIndexL, &sampleL); + code2 = generate_nibble(R16(src + (2 * i + 1) * 2 + 0), + &stepIndexL, &sampleL); + *dst++ = (code1 << 4) | code2; + } + for (i = 0; i < 4; i++) + { + code1 = generate_nibble(R16(src + (2 * i + 0) * 2 + 1), + &stepIndexR, &sampleR); + code2 = generate_nibble(R16(src + (2 * i + 1) * 2 + 1), + &stepIndexR, &sampleR); + *dst++ = (code1 << 4) | code2; + } + src += 32; + } + dst = in_dst + adsi->pwfxDst->nBlockAlign; + } + ((AcmAdpcmData*)adsi->dwDriver)->stepIndexL = stepIndexL; + ((AcmAdpcmData*)adsi->dwDriver)->stepIndexR = stepIndexR; +} + +static void cvtMM16imaK(PACMDRVSTREAMINSTANCE adsi, + const unsigned char* src, LPDWORD nsrc, + unsigned char* dst, LPDWORD ndst) +{ + int stepIndex; + int sample; + BYTE code1, code2; + int nsamp_blk = ((LPIMAADPCMWAVEFORMAT)adsi->pwfxDst)->wSamplesPerBlock; + int nsamp; + /* compute the number of entire blocks we can decode... + * it's the min of the number of entire blocks in source buffer and the number + * of entire blocks in destination buffer + */ + DWORD nblock = min(*nsrc / (nsamp_blk * 2), + *ndst / adsi->pwfxDst->nBlockAlign); + + *nsrc = nblock * (nsamp_blk * 2); + *ndst = nblock * adsi->pwfxDst->nBlockAlign; + + stepIndex = ((AcmAdpcmData*)adsi->dwDriver)->stepIndexL; + nsamp_blk--; /* so that we won't count the sample in header while filling the block */ + + for (; nblock > 0; nblock--) + { + char* in_dst = dst; + + /* generate header */ + /* FIXME: what about the last effective sample from previous block ??? */ + /* perhaps something like: + * sample += R16(src); + * clamp_sample(sample); + * and with : + * + saving the sample in adsi->dwDriver when all blocks are done + + + reset should set the field in adsi->dwDriver to 0 too + */ + sample = R16(src); src += 2; + W16(dst, sample); dst += 2; + *dst = (unsigned char)(unsigned)stepIndex; + dst += 2; + + for (nsamp = nsamp_blk; nsamp > 0; nsamp -= 2) + { + code1 = generate_nibble(R16(src), &stepIndex, &sample); + src += 2; + code2 = generate_nibble(R16(src), &stepIndex, &sample); + src += 2; + *dst++ = (code1 << 4) | code2; + } + dst = in_dst + adsi->pwfxDst->nBlockAlign; + } + ((AcmAdpcmData*)adsi->dwDriver)->stepIndexL = stepIndex; +} + +/*********************************************************************** + * ADPCM_DriverDetails + * + */ +static LRESULT ADPCM_DriverDetails(PACMDRIVERDETAILSW add) +{ + add->fccType = ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC; + add->fccComp = ACMDRIVERDETAILS_FCCCOMP_UNDEFINED; + add->wMid = 0xFF; + add->wPid = 0x00; + add->vdwACM = 0x01000000; + add->vdwDriver = 0x01000000; + add->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CODEC; + add->cFormatTags = 2; /* PCM, IMA ADPCM */ + add->cFilterTags = 0; + add->hicon = NULL; + MultiByteToWideChar( CP_ACP, 0, "WINE-ADPCM", -1, + add->szShortName, sizeof(add->szShortName)/sizeof(WCHAR) ); + MultiByteToWideChar( CP_ACP, 0, "Wine IMA ADPCM converter", -1, + add->szLongName, sizeof(add->szLongName)/sizeof(WCHAR) ); + MultiByteToWideChar( CP_ACP, 0, "Brought to you by the Wine team...", -1, + add->szCopyright, sizeof(add->szCopyright)/sizeof(WCHAR) ); + MultiByteToWideChar( CP_ACP, 0, "Refer to LICENSE file", -1, + add->szLicensing, sizeof(add->szLicensing)/sizeof(WCHAR) ); + add->szFeatures[0] = 0; + + return MMSYSERR_NOERROR; +} + +/*********************************************************************** + * ADPCM_FormatTagDetails + * + */ +static LRESULT ADPCM_FormatTagDetails(PACMFORMATTAGDETAILSW aftd, DWORD dwQuery) +{ + static WCHAR szPcm[]={'P','C','M',0}; + static WCHAR szImaAdPcm[]={'I','M','A',' ','A','d','P','C','M',0}; + + switch (dwQuery) + { + case ACM_FORMATTAGDETAILSF_INDEX: + if (aftd->dwFormatTagIndex >= 2) return ACMERR_NOTPOSSIBLE; + break; + case ACM_FORMATTAGDETAILSF_LARGESTSIZE: + if (aftd->dwFormatTag == WAVE_FORMAT_UNKNOWN) + { + aftd->dwFormatTagIndex = 1; /* WAVE_FORMAT_IMA_ADPCM is bigger than PCM */ + break; + } + /* fall thru */ + case ACM_FORMATTAGDETAILSF_FORMATTAG: + switch (aftd->dwFormatTag) + { + case WAVE_FORMAT_PCM: aftd->dwFormatTagIndex = 0; break; + case WAVE_FORMAT_IMA_ADPCM: aftd->dwFormatTagIndex = 1; break; + default: return ACMERR_NOTPOSSIBLE; + } + break; + default: + WARN("Unsupported query %08lx\n", dwQuery); + return MMSYSERR_NOTSUPPORTED; + } + + aftd->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CODEC; + switch (aftd->dwFormatTagIndex) + { + case 0: + aftd->dwFormatTag = WAVE_FORMAT_PCM; + aftd->cbFormatSize = sizeof(PCMWAVEFORMAT); + aftd->cStandardFormats = NUM_PCM_FORMATS; + lstrcpyW(aftd->szFormatTag, szPcm); + break; + case 1: + aftd->dwFormatTag = WAVE_FORMAT_IMA_ADPCM; + aftd->cbFormatSize = sizeof(IMAADPCMWAVEFORMAT); + aftd->cStandardFormats = NUM_ADPCM_FORMATS; + lstrcpyW(aftd->szFormatTag, szImaAdPcm); + break; + } + return MMSYSERR_NOERROR; +} + +/*********************************************************************** + * ADPCM_FormatDetails + * + */ +static LRESULT ADPCM_FormatDetails(PACMFORMATDETAILSW afd, DWORD dwQuery) +{ + switch (dwQuery) + { + case ACM_FORMATDETAILSF_FORMAT: + if (ADPCM_GetFormatIndex(afd->pwfx) == 0xFFFFFFFF) return ACMERR_NOTPOSSIBLE; + break; + case ACM_FORMATDETAILSF_INDEX: + afd->pwfx->wFormatTag = afd->dwFormatTag; + switch (afd->dwFormatTag) + { + case WAVE_FORMAT_PCM: + if (afd->dwFormatIndex >= NUM_PCM_FORMATS) return ACMERR_NOTPOSSIBLE; + afd->pwfx->nChannels = PCM_Formats[afd->dwFormatIndex].nChannels; + afd->pwfx->nSamplesPerSec = PCM_Formats[afd->dwFormatIndex].rate; + afd->pwfx->wBitsPerSample = PCM_Formats[afd->dwFormatIndex].nBits; + /* native MSACM uses a PCMWAVEFORMAT structure, so cbSize is not accessible + * afd->pwfx->cbSize = 0; + */ + afd->pwfx->nBlockAlign = + (afd->pwfx->nChannels * afd->pwfx->wBitsPerSample) / 8; + afd->pwfx->nAvgBytesPerSec = + afd->pwfx->nSamplesPerSec * afd->pwfx->nBlockAlign; + break; + case WAVE_FORMAT_IMA_ADPCM: + if (afd->dwFormatIndex >= NUM_ADPCM_FORMATS) return ACMERR_NOTPOSSIBLE; + afd->pwfx->nChannels = ADPCM_Formats[afd->dwFormatIndex].nChannels; + afd->pwfx->nSamplesPerSec = ADPCM_Formats[afd->dwFormatIndex].rate; + afd->pwfx->wBitsPerSample = ADPCM_Formats[afd->dwFormatIndex].nBits; + afd->pwfx->nBlockAlign = 1024; + /* we got 4 bits per sample */ + afd->pwfx->nAvgBytesPerSec = + (afd->pwfx->nSamplesPerSec * 4) / 8; + if (afd->cbwfx >= sizeof(WAVEFORMATEX)) + afd->pwfx->cbSize = sizeof(WORD); + if (afd->cbwfx >= sizeof(IMAADPCMWAVEFORMAT)) + ((IMAADPCMWAVEFORMAT*)afd->pwfx)->wSamplesPerBlock = (1024 - 4 * afd->pwfx->nChannels) * (2 / afd->pwfx->nChannels) + 1; + break; + default: + WARN("Unsupported tag %08lx\n", afd->dwFormatTag); + return MMSYSERR_INVALPARAM; + } + break; + default: + WARN("Unsupported query %08lx\n", dwQuery); + return MMSYSERR_NOTSUPPORTED; + } + afd->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CODEC; + afd->szFormat[0] = 0; /* let MSACM format this for us... */ + + return MMSYSERR_NOERROR; +} + +/*********************************************************************** + * ADPCM_FormatSuggest + * + */ +static LRESULT ADPCM_FormatSuggest(PACMDRVFORMATSUGGEST adfs) +{ + /* some tests ... */ + if (adfs->cbwfxSrc < sizeof(PCMWAVEFORMAT) || + adfs->cbwfxDst < sizeof(PCMWAVEFORMAT) || + ADPCM_GetFormatIndex(adfs->pwfxSrc) == 0xFFFFFFFF) return ACMERR_NOTPOSSIBLE; + /* FIXME: should do those tests against the real size (according to format tag */ + + /* If no suggestion for destination, then copy source value */ + if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_NCHANNELS)) + adfs->pwfxDst->nChannels = adfs->pwfxSrc->nChannels; + if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_NSAMPLESPERSEC)) + adfs->pwfxDst->nSamplesPerSec = adfs->pwfxSrc->nSamplesPerSec; + + if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_WBITSPERSAMPLE)) + { + if (adfs->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM) + adfs->pwfxDst->wBitsPerSample = 4; + else + adfs->pwfxDst->wBitsPerSample = 16; + } + if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_WFORMATTAG)) + { + if (adfs->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM) + adfs->pwfxDst->wFormatTag = WAVE_FORMAT_IMA_ADPCM; + else + adfs->pwfxDst->wFormatTag = WAVE_FORMAT_PCM; + } + + /* check if result is ok */ + if (ADPCM_GetFormatIndex(adfs->pwfxDst) == 0xFFFFFFFF) return ACMERR_NOTPOSSIBLE; + + /* recompute other values */ + switch (adfs->pwfxDst->wFormatTag) + { + case WAVE_FORMAT_PCM: + adfs->pwfxDst->nBlockAlign = (adfs->pwfxDst->nChannels * adfs->pwfxDst->wBitsPerSample) / 8; + adfs->pwfxDst->nAvgBytesPerSec = adfs->pwfxDst->nSamplesPerSec * adfs->pwfxDst->nBlockAlign; + break; + case WAVE_FORMAT_IMA_ADPCM: + adfs->pwfxDst->nBlockAlign = 1024; + /* FIXME: not handling header overhead */ + adfs->pwfxDst->nAvgBytesPerSec = ((adfs->pwfxDst->nSamplesPerSec * 4) / 8) * adfs->pwfxSrc->nChannels; + ((IMAADPCMWAVEFORMAT*)adfs->pwfxDst)->wSamplesPerBlock = (1024 - 4 * adfs->pwfxSrc->nChannels) * (2 / adfs->pwfxSrc->nChannels) + 1; + TRACE("setting spb=%u\n", ((IMAADPCMWAVEFORMAT*)adfs->pwfxDst)->wSamplesPerBlock); + break; + default: + FIXME("\n"); + break; + } + + return MMSYSERR_NOERROR; +} + +/*********************************************************************** + * ADPCM_Reset + * + */ +static void ADPCM_Reset(PACMDRVSTREAMINSTANCE adsi, AcmAdpcmData* aad) +{ + aad->stepIndexL = aad->stepIndexR = 0; +} + +/*********************************************************************** + * ADPCM_StreamOpen + * + */ +static LRESULT ADPCM_StreamOpen(PACMDRVSTREAMINSTANCE adsi) +{ + AcmAdpcmData* aad; + unsigned nspb; + + assert(!(adsi->fdwOpen & ACM_STREAMOPENF_ASYNC)); + + if (ADPCM_GetFormatIndex(adsi->pwfxSrc) == 0xFFFFFFFF || + ADPCM_GetFormatIndex(adsi->pwfxDst) == 0xFFFFFFFF) + return ACMERR_NOTPOSSIBLE; + + aad = HeapAlloc(GetProcessHeap(), 0, sizeof(AcmAdpcmData)); + if (aad == 0) return MMSYSERR_NOMEM; + + adsi->dwDriver = (DWORD)aad; + + if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM && + adsi->pwfxDst->wFormatTag == WAVE_FORMAT_PCM) + { + goto theEnd; + } + else if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_IMA_ADPCM && + adsi->pwfxDst->wFormatTag == WAVE_FORMAT_PCM) + { + /* resampling or mono <=> stereo not available + * ADPCM algo only define 16 bit per sample output + */ + if (adsi->pwfxSrc->nSamplesPerSec != adsi->pwfxDst->nSamplesPerSec || + adsi->pwfxSrc->nChannels != adsi->pwfxDst->nChannels || + adsi->pwfxDst->wBitsPerSample != 16) + goto theEnd; + + nspb = ((LPIMAADPCMWAVEFORMAT)adsi->pwfxSrc)->wSamplesPerBlock; + TRACE("spb=%u\n", nspb); + + /* we check that in a block, after the header, samples are present on + * 4-sample packet pattern + * we also check that the block alignement is bigger than the expected size + */ + if (((nspb - 1) & 3) != 0) goto theEnd; + if ((((nspb - 1) / 2) + 4) * adsi->pwfxSrc->nChannels < adsi->pwfxSrc->nBlockAlign) + goto theEnd; + + /* adpcm decoding... */ + if (adsi->pwfxDst->wBitsPerSample == 16 && adsi->pwfxDst->nChannels == 2) + aad->convert = cvtSSima16K; + if (adsi->pwfxDst->wBitsPerSample == 16 && adsi->pwfxDst->nChannels == 1) + aad->convert = cvtMMima16K; + } + else if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM && + adsi->pwfxDst->wFormatTag == WAVE_FORMAT_IMA_ADPCM) + { + if (adsi->pwfxSrc->nSamplesPerSec != adsi->pwfxDst->nSamplesPerSec || + adsi->pwfxSrc->nChannels != adsi->pwfxDst->nChannels || + adsi->pwfxSrc->wBitsPerSample != 16) + goto theEnd; + + nspb = ((LPIMAADPCMWAVEFORMAT)adsi->pwfxDst)->wSamplesPerBlock; + TRACE("spb=%u\n", nspb); + + /* we check that in a block, after the header, samples are present on + * 4-sample packet pattern + * we also check that the block alignement is bigger than the expected size + */ + if (((nspb - 1) & 3) != 0) goto theEnd; + if ((((nspb - 1) / 2) + 4) * adsi->pwfxDst->nChannels < adsi->pwfxDst->nBlockAlign) + goto theEnd; + + /* adpcm coding... */ + if (adsi->pwfxSrc->wBitsPerSample == 16 && adsi->pwfxSrc->nChannels == 2) + aad->convert = cvtSS16imaK; + if (adsi->pwfxSrc->wBitsPerSample == 16 && adsi->pwfxSrc->nChannels == 1) + aad->convert = cvtMM16imaK; + } + else goto theEnd; + ADPCM_Reset(adsi, aad); + + return MMSYSERR_NOERROR; + + theEnd: + HeapFree(GetProcessHeap(), 0, aad); + adsi->dwDriver = 0L; + return MMSYSERR_NOTSUPPORTED; +} + +/*********************************************************************** + * ADPCM_StreamClose + * + */ +static LRESULT ADPCM_StreamClose(PACMDRVSTREAMINSTANCE adsi) +{ + HeapFree(GetProcessHeap(), 0, (void*)adsi->dwDriver); + return MMSYSERR_NOERROR; +} + +/*********************************************************************** + * ADPCM_round + * + */ +static inline DWORD ADPCM_round(DWORD a, DWORD b, DWORD c) +{ + assert(a && b && c); + /* to be sure, always return an entire number of c... */ + return ((double)a * (double)b + (double)c - 1) / (double)c; +} + +/*********************************************************************** + * ADPCM_StreamSize + * + */ +static LRESULT ADPCM_StreamSize(PACMDRVSTREAMINSTANCE adsi, PACMDRVSTREAMSIZE adss) +{ + switch (adss->fdwSize) + { + case ACM_STREAMSIZEF_DESTINATION: + /* cbDstLength => cbSrcLength */ + if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM && + adsi->pwfxDst->wFormatTag == WAVE_FORMAT_IMA_ADPCM) + { + /* don't take block overhead into account, doesn't matter too much */ + adss->cbSrcLength = adss->cbDstLength * 4; + } + else if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_IMA_ADPCM && + adsi->pwfxDst->wFormatTag == WAVE_FORMAT_PCM) + { + FIXME("misses the block header overhead\n"); + adss->cbSrcLength = 256 + adss->cbDstLength / 4; + } + else + { + return MMSYSERR_NOTSUPPORTED; + } + break; + case ACM_STREAMSIZEF_SOURCE: + /* cbSrcLength => cbDstLength */ + if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM && + adsi->pwfxDst->wFormatTag == WAVE_FORMAT_IMA_ADPCM) + { + FIXME("misses the block header overhead\n"); + adss->cbDstLength = 256 + adss->cbSrcLength / 4; + } + else if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_IMA_ADPCM && + adsi->pwfxDst->wFormatTag == WAVE_FORMAT_PCM) + { + /* don't take block overhead into account, doesn't matter too much */ + adss->cbDstLength = adss->cbSrcLength * 4; + } + else + { + return MMSYSERR_NOTSUPPORTED; + } + break; + default: + WARN("Unsupported query %08lx\n", adss->fdwSize); + return MMSYSERR_NOTSUPPORTED; + } + return MMSYSERR_NOERROR; +} + +/*********************************************************************** + * ADPCM_StreamConvert + * + */ +static LRESULT ADPCM_StreamConvert(PACMDRVSTREAMINSTANCE adsi, PACMDRVSTREAMHEADER adsh) +{ + AcmAdpcmData* aad = (AcmAdpcmData*)adsi->dwDriver; + DWORD nsrc = adsh->cbSrcLength; + DWORD ndst = adsh->cbDstLength; + + if (adsh->fdwConvert & + ~(ACM_STREAMCONVERTF_BLOCKALIGN| + ACM_STREAMCONVERTF_END| + ACM_STREAMCONVERTF_START)) + { + FIXME("Unsupported fdwConvert (%08lx), ignoring it\n", adsh->fdwConvert); + } + /* ACM_STREAMCONVERTF_BLOCKALIGN + * currently all conversions are block aligned, so do nothing for this flag + * ACM_STREAMCONVERTF_END + * no pending data, so do nothing for this flag + */ + if ((adsh->fdwConvert & ACM_STREAMCONVERTF_START)) + { + ADPCM_Reset(adsi, aad); + } + + aad->convert(adsi, adsh->pbSrc, &nsrc, adsh->pbDst, &ndst); + adsh->cbSrcLengthUsed = nsrc; + adsh->cbDstLengthUsed = ndst; + + return MMSYSERR_NOERROR; +} + +/************************************************************************** + * ADPCM_DriverProc [exported] + */ +LRESULT CALLBACK ADPCM_DriverProc(DWORD dwDevID, HDRVR hDriv, UINT wMsg, + LPARAM dwParam1, LPARAM dwParam2) +{ + TRACE("(%08lx %08lx %04x %08lx %08lx);\n", + dwDevID, (DWORD)hDriv, wMsg, dwParam1, dwParam2); + + switch (wMsg) + { + case DRV_LOAD: return 1; + case DRV_FREE: return 1; + case DRV_OPEN: return ADPCM_drvOpen((LPSTR)dwParam1); + case DRV_CLOSE: return ADPCM_drvClose(dwDevID); + case DRV_ENABLE: return 1; + case DRV_DISABLE: return 1; + case DRV_QUERYCONFIGURE: return 1; + case DRV_CONFIGURE: MessageBoxA(0, "MSACM IMA ADPCM filter !", "Wine Driver", MB_OK); return 1; + case DRV_INSTALL: return DRVCNF_RESTART; + case DRV_REMOVE: return DRVCNF_RESTART; + + case ACMDM_DRIVER_NOTIFY: + /* no caching from other ACM drivers is done so far */ + return MMSYSERR_NOERROR; + + case ACMDM_DRIVER_DETAILS: + return ADPCM_DriverDetails((PACMDRIVERDETAILSW)dwParam1); + + case ACMDM_FORMATTAG_DETAILS: + return ADPCM_FormatTagDetails((PACMFORMATTAGDETAILSW)dwParam1, dwParam2); + + case ACMDM_FORMAT_DETAILS: + return ADPCM_FormatDetails((PACMFORMATDETAILSW)dwParam1, dwParam2); + + case ACMDM_FORMAT_SUGGEST: + return ADPCM_FormatSuggest((PACMDRVFORMATSUGGEST)dwParam1); + + case ACMDM_STREAM_OPEN: + return ADPCM_StreamOpen((PACMDRVSTREAMINSTANCE)dwParam1); + + case ACMDM_STREAM_CLOSE: + return ADPCM_StreamClose((PACMDRVSTREAMINSTANCE)dwParam1); + + case ACMDM_STREAM_SIZE: + return ADPCM_StreamSize((PACMDRVSTREAMINSTANCE)dwParam1, (PACMDRVSTREAMSIZE)dwParam2); + + case ACMDM_STREAM_CONVERT: + return ADPCM_StreamConvert((PACMDRVSTREAMINSTANCE)dwParam1, (PACMDRVSTREAMHEADER)dwParam2); + + case ACMDM_HARDWARE_WAVE_CAPS_INPUT: + case ACMDM_HARDWARE_WAVE_CAPS_OUTPUT: + /* this converter is not a hardware driver */ + case ACMDM_FILTERTAG_DETAILS: + case ACMDM_FILTER_DETAILS: + /* this converter is not a filter */ + case ACMDM_STREAM_RESET: + /* only needed for asynchronous driver... we aren't, so just say it */ + return MMSYSERR_NOTSUPPORTED; + case ACMDM_STREAM_PREPARE: + case ACMDM_STREAM_UNPREPARE: + /* nothing special to do here... so don't do anything */ + return MMSYSERR_NOERROR; + + default: + return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2); + } + return 0; +} diff --git a/reactos/dll/msacm/internal.c b/reactos/dll/msacm/internal.c new file mode 100644 index 00000000000..9cb8002a380 --- /dev/null +++ b/reactos/dll/msacm/internal.c @@ -0,0 +1,417 @@ +/* -*- tab-width: 8; c-basic-offset: 4 -*- */ + +/* + * MSACM32 library + * + * Copyright 1998 Patrik Stridvall + * 1999 Eric Pouech + * + * 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 +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winerror.h" +#include "winreg.h" +#include "mmsystem.h" +#include "mmreg.h" +#include "msacm.h" +#include "msacmdrv.h" +#include "wineacm.h" +#include "wine/debug.h" +#include "wine/unicode.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msacm); + +/**********************************************************************/ + +HANDLE MSACM_hHeap = NULL; +PWINE_ACMDRIVERID MSACM_pFirstACMDriverID = NULL; +PWINE_ACMDRIVERID MSACM_pLastACMDriverID = NULL; + +#if 0 +/*********************************************************************** + * MSACM_DumpCache + */ +static void MSACM_DumpCache(PWINE_ACMDRIVERID padid) +{ + unsigned i; + + TRACE("cFilterTags=%lu cFormatTags=%lu fdwSupport=%08lx\n", + padid->cFilterTags, padid->cFormatTags, padid->fdwSupport); + for (i = 0; i < padid->cache->cFormatTags; i++) { + TRACE("\tdwFormatTag=%lu cbwfx=%lu\n", + padid->aFormatTag[i].dwFormatTag, padid->aFormatTag[i].cbwfx); + } +} +#endif + +/*********************************************************************** + * MSACM_FindFormatTagInCache [internal] + * + * Returns TRUE is the format tag fmtTag is present in the cache. + * If so, idx is set to its index. + */ +BOOL MSACM_FindFormatTagInCache(WINE_ACMDRIVERID* padid, DWORD fmtTag, LPDWORD idx) +{ + unsigned i; + + for (i = 0; i < padid->cFormatTags; i++) { + if (padid->aFormatTag[i].dwFormatTag == fmtTag) { + if (idx) *idx = i; + return TRUE; + } + } + return FALSE; +} + +/*********************************************************************** + * MSACM_FillCache + */ +static BOOL MSACM_FillCache(PWINE_ACMDRIVERID padid) +{ + HACMDRIVER had = 0; + int ntag; + ACMDRIVERDETAILSW add; + ACMFORMATTAGDETAILSW aftd; + + if (acmDriverOpen(&had, (HACMDRIVERID)padid, 0) != 0) + return FALSE; + + padid->aFormatTag = NULL; + add.cbStruct = sizeof(add); + if (MSACM_Message(had, ACMDM_DRIVER_DETAILS, (LPARAM)&add, 0)) + goto errCleanUp; + + if (add.cFormatTags > 0) { + padid->aFormatTag = HeapAlloc(MSACM_hHeap, HEAP_ZERO_MEMORY, + add.cFormatTags * sizeof(padid->aFormatTag[0])); + if (!padid->aFormatTag) goto errCleanUp; + } + + padid->cFormatTags = add.cFormatTags; + padid->cFilterTags = add.cFilterTags; + padid->fdwSupport = add.fdwSupport; + + aftd.cbStruct = sizeof(aftd); + + for (ntag = 0; ntag < add.cFormatTags; ntag++) { + aftd.dwFormatTagIndex = ntag; + if (MSACM_Message(had, ACMDM_FORMATTAG_DETAILS, (LPARAM)&aftd, ACM_FORMATTAGDETAILSF_INDEX)) { + TRACE("IIOs (%s)\n", debugstr_w(padid->pszDriverAlias)); + goto errCleanUp; + } + padid->aFormatTag[ntag].dwFormatTag = aftd.dwFormatTag; + padid->aFormatTag[ntag].cbwfx = aftd.cbFormatSize; + } + + acmDriverClose(had, 0); + + return TRUE; + +errCleanUp: + if (had) acmDriverClose(had, 0); + HeapFree(MSACM_hHeap, 0, padid->aFormatTag); + padid->aFormatTag = NULL; + return FALSE; +} + +/*********************************************************************** + * MSACM_GetRegistryKey + */ +static LPWSTR MSACM_GetRegistryKey(const WINE_ACMDRIVERID* padid) +{ + static const WCHAR baseKey[] = {'S','o','f','t','w','a','r','e','\\','M','i','c','r','o','s','o','f','t','\\', + 'A','u','d','i','o','C','o','m','p','r','e','s','s','i','o','n','M','a','n','a','g','e','r','\\', + 'D','r','i','v','e','r','C','a','c','h','e','\\','\0'}; + LPWSTR ret; + int len; + + if (!padid->pszDriverAlias) { + ERR("No alias needed for registry entry\n"); + return NULL; + } + len = strlenW(baseKey); + ret = HeapAlloc(MSACM_hHeap, 0, (len + strlenW(padid->pszDriverAlias) + 1) * sizeof(WCHAR)); + if (!ret) return NULL; + + strcpyW(ret, baseKey); + strcpyW(ret + len, padid->pszDriverAlias); + CharLowerW(ret + len); + return ret; +} + +/*********************************************************************** + * MSACM_ReadCache + */ +static BOOL MSACM_ReadCache(PWINE_ACMDRIVERID padid) +{ + LPWSTR key = MSACM_GetRegistryKey(padid); + HKEY hKey; + DWORD type, size; + + if (!key) return FALSE; + + padid->aFormatTag = NULL; + + if (RegCreateKeyW(HKEY_LOCAL_MACHINE, key, &hKey)) + goto errCleanUp; + + size = sizeof(padid->cFormatTags); + if (RegQueryValueExA(hKey, "cFormatTags", 0, &type, (void*)&padid->cFormatTags, &size)) + goto errCleanUp; + size = sizeof(padid->cFilterTags); + if (RegQueryValueExA(hKey, "cFilterTags", 0, &type, (void*)&padid->cFilterTags, &size)) + goto errCleanUp; + size = sizeof(padid->fdwSupport); + if (RegQueryValueExA(hKey, "fdwSupport", 0, &type, (void*)&padid->fdwSupport, &size)) + goto errCleanUp; + + if (padid->cFormatTags > 0) { + size = padid->cFormatTags * sizeof(padid->aFormatTag[0]); + padid->aFormatTag = HeapAlloc(MSACM_hHeap, HEAP_ZERO_MEMORY, size); + if (!padid->aFormatTag) goto errCleanUp; + if (RegQueryValueExA(hKey, "aFormatTagCache", 0, &type, (void*)padid->aFormatTag, &size)) + goto errCleanUp; + } + HeapFree(MSACM_hHeap, 0, key); + return TRUE; + + errCleanUp: + HeapFree(MSACM_hHeap, 0, key); + HeapFree(MSACM_hHeap, 0, padid->aFormatTag); + padid->aFormatTag = NULL; + RegCloseKey(hKey); + return FALSE; +} + +/*********************************************************************** + * MSACM_WriteCache + */ +static BOOL MSACM_WriteCache(PWINE_ACMDRIVERID padid) +{ + LPWSTR key = MSACM_GetRegistryKey(padid); + HKEY hKey; + + if (!key) return FALSE; + + if (RegCreateKeyW(HKEY_LOCAL_MACHINE, key, &hKey)) + goto errCleanUp; + + if (RegSetValueExA(hKey, "cFormatTags", 0, REG_DWORD, (void*)&padid->cFormatTags, sizeof(DWORD))) + goto errCleanUp; + if (RegSetValueExA(hKey, "cFilterTags", 0, REG_DWORD, (void*)&padid->cFilterTags, sizeof(DWORD))) + goto errCleanUp; + if (RegSetValueExA(hKey, "fdwSupport", 0, REG_DWORD, (void*)&padid->fdwSupport, sizeof(DWORD))) + goto errCleanUp; + if (RegSetValueExA(hKey, "aFormatTagCache", 0, REG_BINARY, + (void*)padid->aFormatTag, + padid->cFormatTags * sizeof(padid->aFormatTag[0]))) + goto errCleanUp; + HeapFree(MSACM_hHeap, 0, key); + return TRUE; + + errCleanUp: + HeapFree(MSACM_hHeap, 0, key); + return FALSE; +} + +/*********************************************************************** + * MSACM_RegisterDriver() + */ +PWINE_ACMDRIVERID MSACM_RegisterDriver(LPCWSTR pszDriverAlias, LPCWSTR pszFileName, + HINSTANCE hinstModule) +{ + PWINE_ACMDRIVERID padid; + + TRACE("(%s, %s, %p)\n", + debugstr_w(pszDriverAlias), debugstr_w(pszFileName), hinstModule); + + padid = (PWINE_ACMDRIVERID) HeapAlloc(MSACM_hHeap, 0, sizeof(WINE_ACMDRIVERID)); + padid->obj.dwType = WINE_ACMOBJ_DRIVERID; + padid->obj.pACMDriverID = padid; + padid->pszDriverAlias = NULL; + if (pszDriverAlias) + { + padid->pszDriverAlias = HeapAlloc( MSACM_hHeap, 0, (strlenW(pszDriverAlias)+1) * sizeof(WCHAR) ); + strcpyW( padid->pszDriverAlias, pszDriverAlias ); + } + padid->pszFileName = NULL; + if (pszFileName) + { + padid->pszFileName = HeapAlloc( MSACM_hHeap, 0, (strlenW(pszFileName)+1) * sizeof(WCHAR) ); + strcpyW( padid->pszFileName, pszFileName ); + } + padid->hInstModule = hinstModule; + + padid->pACMDriverList = NULL; + padid->pNextACMDriverID = NULL; + padid->pPrevACMDriverID = MSACM_pLastACMDriverID; + if (MSACM_pLastACMDriverID) + MSACM_pLastACMDriverID->pNextACMDriverID = padid; + MSACM_pLastACMDriverID = padid; + if (!MSACM_pFirstACMDriverID) + MSACM_pFirstACMDriverID = padid; + /* disable the driver if we cannot load the cache */ + if (!MSACM_ReadCache(padid) && !MSACM_FillCache(padid)) { + WARN("Couldn't load cache for ACM driver (%s)\n", debugstr_w(pszFileName)); + MSACM_UnregisterDriver(padid); + return NULL; + } + return padid; +} + +/*********************************************************************** + * MSACM_RegisterAllDrivers() + */ +void MSACM_RegisterAllDrivers(void) +{ + static const WCHAR msacm32[] = {'m','s','a','c','m','3','2','.','d','l','l','\0'}; + static const WCHAR msacmW[] = {'M','S','A','C','M','.'}; + static const WCHAR drv32[] = {'d','r','i','v','e','r','s','3','2','\0'}; + static const WCHAR sys[] = {'s','y','s','t','e','m','.','i','n','i','\0'}; + static const WCHAR drvkey[] = {'S','o','f','t','w','a','r','e','\\', + 'M','i','c','r','o','s','o','f','t','\\', + 'W','i','n','d','o','w','s',' ','N','T','\\', + 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', + 'D','r','i','v','e','r','s','3','2','\0'}; + DWORD i, cnt = 0, bufLen, lRet; + WCHAR buf[2048], *name, *s; + FILETIME lastWrite; + HKEY hKey; + + /* FIXME: What if the user edits system.ini while the program is running? + * Does Windows handle that? */ + if (MSACM_pFirstACMDriverID) return; + + lRet = RegOpenKeyExW(HKEY_LOCAL_MACHINE, drvkey, 0, KEY_QUERY_VALUE, &hKey); + if (lRet == ERROR_SUCCESS) { + RegQueryInfoKeyW( hKey, 0, 0, 0, &cnt, 0, 0, 0, 0, 0, 0, 0); + for (i = 0; i < cnt; i++) { + bufLen = sizeof(buf) / sizeof(buf[0]); + lRet = RegEnumKeyExW(hKey, i, buf, &bufLen, 0, 0, 0, &lastWrite); + if (lRet != ERROR_SUCCESS) continue; + if (strncmpiW(buf, msacmW, sizeof(msacmW)/sizeof(msacmW[0]))) continue; + if (!(name = strchrW(buf, '='))) continue; + *name = 0; + MSACM_RegisterDriver(buf, name + 1, 0); + } + RegCloseKey( hKey ); + } + + if (GetPrivateProfileSectionW(drv32, buf, sizeof(buf)/sizeof(buf[0]), sys)) + { + for(s = buf; *s; s += strlenW(s) + 1) + { + if (strncmpiW(s, msacmW, sizeof(msacmW)/sizeof(msacmW[0]))) continue; + if (!(name = strchrW(s, '='))) continue; + *name = 0; + MSACM_RegisterDriver(s, name + 1, 0); + *name = '='; + } + } + + MSACM_RegisterDriver(msacm32, msacm32, 0); +} + +/*********************************************************************** + * MSACM_UnregisterDriver() + */ +PWINE_ACMDRIVERID MSACM_UnregisterDriver(PWINE_ACMDRIVERID p) +{ + PWINE_ACMDRIVERID pNextACMDriverID; + + while (p->pACMDriverList) + acmDriverClose((HACMDRIVER) p->pACMDriverList, 0); + + if (p->pszDriverAlias) + HeapFree(MSACM_hHeap, 0, p->pszDriverAlias); + if (p->pszFileName) + HeapFree(MSACM_hHeap, 0, p->pszFileName); + HeapFree(MSACM_hHeap, 0, p->aFormatTag); + + if (p == MSACM_pFirstACMDriverID) + MSACM_pFirstACMDriverID = p->pNextACMDriverID; + if (p == MSACM_pLastACMDriverID) + MSACM_pLastACMDriverID = p->pPrevACMDriverID; + + if (p->pPrevACMDriverID) + p->pPrevACMDriverID->pNextACMDriverID = p->pNextACMDriverID; + if (p->pNextACMDriverID) + p->pNextACMDriverID->pPrevACMDriverID = p->pPrevACMDriverID; + + pNextACMDriverID = p->pNextACMDriverID; + + HeapFree(MSACM_hHeap, 0, p); + + return pNextACMDriverID; +} + +/*********************************************************************** + * MSACM_UnregisterAllDrivers() + */ +void MSACM_UnregisterAllDrivers(void) +{ + PWINE_ACMDRIVERID p = MSACM_pFirstACMDriverID; + + while (p) { + MSACM_WriteCache(p); + p = MSACM_UnregisterDriver(p); + } +} + +/*********************************************************************** + * MSACM_GetObj() + */ +PWINE_ACMOBJ MSACM_GetObj(HACMOBJ hObj, DWORD type) +{ + PWINE_ACMOBJ pao = (PWINE_ACMOBJ)hObj; + + if (pao == NULL || IsBadReadPtr(pao, sizeof(WINE_ACMOBJ)) || + ((type != WINE_ACMOBJ_DONTCARE) && (type != pao->dwType))) + return NULL; + return pao; +} + +/*********************************************************************** + * MSACM_GetDriverID() + */ +PWINE_ACMDRIVERID MSACM_GetDriverID(HACMDRIVERID hDriverID) +{ + return (PWINE_ACMDRIVERID)MSACM_GetObj((HACMOBJ)hDriverID, WINE_ACMOBJ_DRIVERID); +} + +/*********************************************************************** + * MSACM_GetDriver() + */ +PWINE_ACMDRIVER MSACM_GetDriver(HACMDRIVER hDriver) +{ + return (PWINE_ACMDRIVER)MSACM_GetObj((HACMOBJ)hDriver, WINE_ACMOBJ_DRIVER); +} + +/*********************************************************************** + * MSACM_Message() + */ +MMRESULT MSACM_Message(HACMDRIVER had, UINT uMsg, LPARAM lParam1, LPARAM lParam2) +{ + PWINE_ACMDRIVER pad = MSACM_GetDriver(had); + + return pad ? SendDriverMessage(pad->hDrvr, uMsg, lParam1, lParam2) : MMSYSERR_INVALHANDLE; +} diff --git a/reactos/dll/msacm/msacm.rc b/reactos/dll/msacm/msacm.rc new file mode 100644 index 00000000000..60710017a85 --- /dev/null +++ b/reactos/dll/msacm/msacm.rc @@ -0,0 +1,36 @@ +/* + * Top level resource file for MS ACM + * + * Copyright 2000 Eric Pouech + * + * 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 "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "wineacm.h" + +#include "msacm_En.rc" +#include "msacm_De.rc" +#include "msacm_Es.rc" +#include "msacm_Fr.rc" +#include "msacm_It.rc" +#include "msacm_Ja.rc" +#include "msacm_Nl.rc" +#include "msacm_Pt.rc" +#include "msacm_Ru.rc" +#include "msacm_Sv.rc" +#include "msacm_Hu.rc" diff --git a/reactos/dll/msacm/msacm.spec b/reactos/dll/msacm/msacm.spec new file mode 100644 index 00000000000..9f03617534f --- /dev/null +++ b/reactos/dll/msacm/msacm.spec @@ -0,0 +1,40 @@ + 1 stub WEP + 2 stub DRIVERPROC + 3 stub ___EXPORTEDSTUB + 7 pascal acmGetVersion() acmGetVersion16 + 8 pascal -ret16 acmMetrics(word word ptr) acmMetrics16 + 10 pascal -ret16 acmDriverEnum(ptr long long) acmDriverEnum16 + 11 pascal -ret16 acmDriverDetails(word ptr long) acmDriverDetails16 + 12 pascal -ret16 acmDriverAdd(ptr word long long long) acmDriverAdd16 + 13 pascal -ret16 acmDriverRemove(word long) acmDriverRemove16 + 14 pascal -ret16 acmDriverOpen(ptr word long) acmDriverOpen16 + 15 pascal -ret16 acmDriverClose(word long) acmDriverClose16 + 16 pascal acmDriverMessage(word word long long) acmDriverMessage16 + 17 pascal -ret16 acmDriverID(word ptr long) acmDriverID16 + 18 pascal -ret16 acmDriverPriority(word long long) acmDriverPriority16 + 30 pascal -ret16 acmFormatTagDetails(word ptr long) acmFormatTagDetails16 + 31 pascal -ret16 acmFormatTagEnum(word ptr ptr long long) acmFormatTagEnum16 + 40 pascal -ret16 acmFormatChoose(ptr) acmFormatChoose16 + 41 pascal -ret16 acmFormatDetails(word ptr long) acmFormatDetails16 + 42 pascal -ret16 acmFormatEnum(word ptr ptr long long) acmFormatEnum16 + 45 pascal -ret16 acmFormatSuggest(word ptr ptr long long) acmFormatSuggest16 + 50 pascal -ret16 acmFilterTagDetails(word ptr long) acmFilterTagDetails16 + 51 pascal -ret16 acmFilterTagEnum(word ptr ptr long long) acmFilterTagEnum16 + 60 pascal -ret16 acmFilterChoose(ptr) acmFilterChoose16 + 61 pascal -ret16 acmFilterDetails(word ptr long) acmFilterDetails16 + 62 pascal -ret16 acmFilterEnum(word ptr ptr long long) acmFilterEnum16 + 70 pascal -ret16 acmStreamOpen(ptr word ptr ptr ptr long long long) acmStreamOpen16 + 71 pascal -ret16 acmStreamClose(word long) acmStreamClose16 + 72 pascal -ret16 acmStreamSize(word long ptr long) acmStreamSize16 + 75 pascal -ret16 acmStreamConvert(word ptr long) acmStreamConvert16 + 76 pascal -ret16 acmStreamReset(word long) acmStreamReset16 + 77 pascal -ret16 acmStreamPrepareHeader(word ptr long) acmStreamPrepareHeader16 + 78 pascal -ret16 acmStreamUnprepareHeader(word ptr long) acmStreamUnprepareHeader16 +150 stub ACMAPPLICATIONEXIT +175 stub ACMHUGEPAGELOCK +176 stub ACMHUGEPAGEUNLOCK +200 stub ACMOPENCONVERSION +201 stub ACMCLOSECONVERSION +202 stub ACMCONVERT +203 stub ACMCHOOSEFORMAT +255 pascal DllEntryPoint(long word word word long word) MSACM_DllEntryPoint diff --git a/reactos/dll/msacm/msacm.spec.def b/reactos/dll/msacm/msacm.spec.def new file mode 100644 index 00000000000..e69de29bb2d diff --git a/reactos/dll/msacm/msacm32.spec b/reactos/dll/msacm/msacm32.spec new file mode 100644 index 00000000000..9500b53e2cd --- /dev/null +++ b/reactos/dll/msacm/msacm32.spec @@ -0,0 +1,46 @@ +@ stdcall acmDriverAddA(ptr long long long long) +@ stdcall acmDriverAddW(ptr long long long long) +@ stdcall acmDriverClose(long long) +@ stdcall acmDriverDetailsA(long ptr long) +@ stdcall acmDriverDetailsW(long ptr long) +@ stdcall acmDriverEnum(ptr long long) +@ stdcall acmDriverID(long ptr long) +@ stdcall acmDriverMessage(long long long long) +@ stdcall acmDriverOpen(ptr long long) +@ stdcall acmDriverPriority(long long long) +@ stdcall acmDriverRemove(long long) +@ stdcall acmFilterChooseA(ptr) +@ stdcall acmFilterChooseW(ptr) +@ stdcall acmFilterDetailsA(long ptr long) +@ stdcall acmFilterDetailsW(long ptr long) +@ stdcall acmFilterEnumA(long ptr ptr long long) +@ stdcall acmFilterEnumW(long ptr ptr long long) +@ stdcall acmFilterTagDetailsA(long ptr long) +@ stdcall acmFilterTagDetailsW(long ptr long) +@ stdcall acmFilterTagEnumA(long ptr ptr long long) +@ stdcall acmFilterTagEnumW(long ptr ptr long long) +@ stdcall acmFormatChooseA(ptr) +@ stdcall acmFormatChooseW(ptr) +@ stdcall acmFormatDetailsA(long ptr long) +@ stdcall acmFormatDetailsW(long ptr long) +@ stdcall acmFormatEnumA(long ptr ptr long long) +@ stdcall acmFormatEnumW(long ptr ptr long long) +@ stdcall acmFormatSuggest(long ptr ptr long long) +@ stdcall acmFormatTagDetailsA(long ptr long) +@ stdcall acmFormatTagDetailsW(long ptr long) +@ stdcall acmFormatTagEnumA(long ptr ptr long long) +@ stdcall acmFormatTagEnumW(long ptr ptr long long) +@ stdcall acmGetVersion() +@ stub acmMessage32 +@ stdcall acmMetrics(long long ptr) +@ stdcall acmStreamClose(long long) +@ stdcall acmStreamConvert(long ptr long) +@ stdcall acmStreamMessage(long long long long) +@ stdcall acmStreamOpen(ptr long ptr ptr ptr long long long) +@ stdcall acmStreamPrepareHeader(long ptr long) +@ stdcall acmStreamReset(long long) +@ stdcall acmStreamSize(long long ptr long) +@ stdcall acmStreamUnprepareHeader(long ptr long) + +# this is wine only +@ stdcall DriverProc(long long long long long) PCM_DriverProc diff --git a/reactos/dll/msacm/msacm32.xml b/reactos/dll/msacm/msacm32.xml new file mode 100644 index 00000000000..c9f10c64656 --- /dev/null +++ b/reactos/dll/msacm/msacm32.xml @@ -0,0 +1,26 @@ + + + . + include/wine + + + + + 0x600 + 0x501 + 0x501 + wine + ntdll + kernel32 + advapi32 + user32 + winmm + driver.c + filter.c + format.c + internal.c + msacm32_main.c + pcmconverter.c + stream.c + msacm32.spec + diff --git a/reactos/dll/msacm/msacm32_main.c b/reactos/dll/msacm/msacm32_main.c new file mode 100644 index 00000000000..dde962a2dfe --- /dev/null +++ b/reactos/dll/msacm/msacm32_main.c @@ -0,0 +1,233 @@ +/* -*- tab-width: 8; c-basic-offset: 4 -*- */ + +/* + * MSACM32 library + * + * Copyright 1998 Patrik Stridvall + * 1999 Eric Pouech + * + * 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 + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "mmsystem.h" +#include "mmreg.h" +#include "msacm.h" +#include "msacmdrv.h" +#include "wineacm.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msacm); + +/**********************************************************************/ + +HINSTANCE MSACM_hInstance32 = 0; + +/*********************************************************************** + * DllMain (MSACM32.init) + */ +BOOL WINAPI DllMain(HINSTANCE hInstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + TRACE("%p 0x%lx %p\n", hInstDLL, fdwReason, lpvReserved); + + switch (fdwReason) { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hInstDLL); + MSACM_hHeap = HeapCreate(0, 0x10000, 0); + MSACM_hInstance32 = hInstDLL; + MSACM_RegisterAllDrivers(); + break; + case DLL_PROCESS_DETACH: + MSACM_UnregisterAllDrivers(); + HeapDestroy(MSACM_hHeap); + MSACM_hHeap = NULL; + MSACM_hInstance32 = NULL; + break; + default: + break; + } + return TRUE; +} + +/*********************************************************************** + * XRegThunkEntry (MSACM32.1) + * FIXME + * No documentation found. + */ + +/*********************************************************************** + * acmGetVersion (MSACM32.@) + */ +DWORD WINAPI acmGetVersion(void) +{ + OSVERSIONINFOA version; + + version.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); + if (!GetVersionExA( &version )) + return 0x04030000; + + switch (version.dwPlatformId) { + case VER_PLATFORM_WIN32_NT: + return 0x04000565; /* 4.0.1381 */ + default: + FIXME("%lx not supported\n", version.dwPlatformId); + case VER_PLATFORM_WIN32_WINDOWS: + return 0x04030000; /* 4.3.0 */ + } +} + +/*********************************************************************** + * acmMessage32 (MSACM32.35) + * FIXME + * No documentation found. + */ + +/*********************************************************************** + * acmMetrics (MSACM32.@) + */ +MMRESULT WINAPI acmMetrics(HACMOBJ hao, UINT uMetric, LPVOID pMetric) +{ + PWINE_ACMOBJ pao = MSACM_GetObj(hao, WINE_ACMOBJ_DONTCARE); + BOOL bLocal = TRUE; + PWINE_ACMDRIVERID padid; + DWORD val = 0; + int i; + MMRESULT mmr = MMSYSERR_NOERROR; + + TRACE("(%p, %d, %p);\n", hao, uMetric, pMetric); + +#define CheckLocal(padid) (!bLocal || ((padid)->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_LOCAL)) + + switch (uMetric) { + case ACM_METRIC_COUNT_DRIVERS: + bLocal = FALSE; + /* fall through */ + case ACM_METRIC_COUNT_LOCAL_DRIVERS: + if (hao) return MMSYSERR_INVALHANDLE; + if (!pMetric) return MMSYSERR_INVALPARAM; + for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) + if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) && CheckLocal(padid)) + val++; + *(LPDWORD)pMetric = val; + break; + + case ACM_METRIC_COUNT_CODECS: + bLocal = FALSE; + /* fall through */ + case ACM_METRIC_COUNT_LOCAL_CODECS: + if (hao) return MMSYSERR_INVALHANDLE; + if (!pMetric) return MMSYSERR_INVALPARAM; + for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) + if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) && + (padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_CODEC) && + CheckLocal(padid)) + val++; + *(LPDWORD)pMetric = val; + break; + + case ACM_METRIC_COUNT_CONVERTERS: + bLocal = FALSE; + /* fall through */ + case ACM_METRIC_COUNT_LOCAL_CONVERTERS: + if (hao) return MMSYSERR_INVALHANDLE; + if (!pMetric) return MMSYSERR_INVALPARAM; + for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) + if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) && + (padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_CONVERTER) && + CheckLocal(padid)) + val++; + *(LPDWORD)pMetric = val; + break; + + case ACM_METRIC_COUNT_FILTERS: + bLocal = FALSE; + /* fall through */ + case ACM_METRIC_COUNT_LOCAL_FILTERS: + if (hao) return MMSYSERR_INVALHANDLE; + if (!pMetric) return MMSYSERR_INVALPARAM; + for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) + if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) && + (padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_FILTER) && + CheckLocal(padid)) + val++; + *(LPDWORD)pMetric = val; + break; + + case ACM_METRIC_COUNT_DISABLED: + bLocal = FALSE; + /* fall through */ + case ACM_METRIC_COUNT_LOCAL_DISABLED: + if (hao) return MMSYSERR_INVALHANDLE; + if (!pMetric) return MMSYSERR_INVALPARAM; + for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) + if ((padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) && CheckLocal(padid)) + val++; + *(LPDWORD)pMetric = val; + break; + + case ACM_METRIC_MAX_SIZE_FORMAT: + if (hao == NULL) { + for (padid = MSACM_pFirstACMDriverID; padid; padid = padid->pNextACMDriverID) { + if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED)) { + for (i = 0; i < padid->cFormatTags; i++) { + if (val < padid->aFormatTag[i].cbwfx) + val = padid->aFormatTag[i].cbwfx; + } + } + } + } else if (pao != NULL) { + switch (pao->dwType) { + case WINE_ACMOBJ_DRIVER: + case WINE_ACMOBJ_DRIVERID: + padid = pao->pACMDriverID; + break; + default: + return MMSYSERR_INVALHANDLE; + } + if (!(padid->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED)) { + for (i = 0; i < padid->cFormatTags; i++) { + if (val < padid->aFormatTag[i].cbwfx) + val = padid->aFormatTag[i].cbwfx; + } + } + } else { + return MMSYSERR_INVALHANDLE; + } + if (!pMetric) return MMSYSERR_INVALPARAM; + *(LPDWORD)pMetric = val; + break; + + case ACM_METRIC_COUNT_HARDWARE: + if (hao) return MMSYSERR_INVALHANDLE; + if (!pMetric) return MMSYSERR_INVALPARAM; + *(LPDWORD)pMetric = 0; + FIXME("ACM_METRIC_COUNT_HARDWARE not implemented\n"); + break; + + case ACM_METRIC_HARDWARE_WAVE_INPUT: + case ACM_METRIC_HARDWARE_WAVE_OUTPUT: + case ACM_METRIC_MAX_SIZE_FILTER: + case ACM_METRIC_DRIVER_SUPPORT: + case ACM_METRIC_DRIVER_PRIORITY: + default: + FIXME("(%p, %d, %p): stub\n", hao, uMetric, pMetric); + mmr = MMSYSERR_NOTSUPPORTED; + } + return mmr; +} diff --git a/reactos/dll/msacm/msacm_De.rc b/reactos/dll/msacm/msacm_De.rc new file mode 100644 index 00000000000..555885b2280 --- /dev/null +++ b/reactos/dll/msacm/msacm_De.rc @@ -0,0 +1,56 @@ +/* + * German resource file for MS ACM + * + * by Friedrich Stange (dj_smith_reactos@online.de) + * + * 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 + */ + +LANGUAGE LANG_GERMAN, SUBLANG_NEUTRAL + +DLG_ACMFORMATCHOOSE_ID DIALOG DISCARDABLE 10, 20, 225, 100 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Sound auswahl" +FONT 8, "MS Shell Dlg" +BEGIN + + LTEXT "&Name:", -1, 5, 5, 115, 8, NOT WS_GROUP + + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_CUSTOM, 5, 15, 115, 60, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + + PUSHBUTTON "&Speichern als...", IDD_ACMFORMATCHOOSE_BTN_SETNAME, 125, 14, 45, 14 + PUSHBUTTON "&Löschen", IDD_ACMFORMATCHOOSE_BTN_DELNAME, 175, 14, 45, 14 + + LTEXT "&Format:", -1, 5, 41, 44, 8, NOT WS_GROUP + + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, 50, 39, 170, 60, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + + LTEXT "&Eigenschaften:", -1, 5, 59, 44, 8, NOT WS_GROUP + +#if 0 + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP | + CBS_OWNERDRAWFIXED | CBS_HASSTRINGS +#else + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP +#endif + DEFPUSHBUTTON "OK", IDOK, 48, 80, 40, 14 + PUSHBUTTON "Abbrechen", IDCANCEL, 92, 80, 40, 14 + PUSHBUTTON "&Hilfe", IDD_ACMFORMATCHOOSE_BTN_HELP, 136, 80, 40, 14 + +END diff --git a/reactos/dll/msacm/msacm_En.rc b/reactos/dll/msacm/msacm_En.rc new file mode 100644 index 00000000000..acbd0c44349 --- /dev/null +++ b/reactos/dll/msacm/msacm_En.rc @@ -0,0 +1,56 @@ +/* + * English resource file for MS ACM + * + * Copyright 2000 Eric Pouech + * + * 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 + */ + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +DLG_ACMFORMATCHOOSE_ID DIALOG DISCARDABLE 10, 20, 225, 100 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Sound Selection" +FONT 8, "MS Shell Dlg" +BEGIN + + LTEXT "&Name:", -1, 5, 5, 115, 8, NOT WS_GROUP + + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_CUSTOM, 5, 15, 115, 60, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + + PUSHBUTTON "&Save As...", IDD_ACMFORMATCHOOSE_BTN_SETNAME, 125, 14, 45, 14 + PUSHBUTTON "&Remove", IDD_ACMFORMATCHOOSE_BTN_DELNAME, 175, 14, 45, 14 + + LTEXT "&Format:", -1, 5, 41, 44, 8, NOT WS_GROUP + + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, 50, 39, 170, 60, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + + LTEXT "&Attributes:", -1, 5, 59, 44, 8, NOT WS_GROUP + +#if 0 + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP | + CBS_OWNERDRAWFIXED | CBS_HASSTRINGS +#else + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP +#endif + DEFPUSHBUTTON "OK", IDOK, 48, 80, 40, 14 + PUSHBUTTON "Cancel", IDCANCEL, 92, 80, 40, 14 + PUSHBUTTON "&Help", IDD_ACMFORMATCHOOSE_BTN_HELP, 136, 80, 40, 14 + +END diff --git a/reactos/dll/msacm/msacm_Es.rc b/reactos/dll/msacm/msacm_Es.rc new file mode 100644 index 00000000000..3f26252898a --- /dev/null +++ b/reactos/dll/msacm/msacm_Es.rc @@ -0,0 +1,56 @@ +/* + * Spanish resource file for MS ACM + * + * Copyright 2004 José Manuel Ferrer Ortiz + * + * 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 + */ + +LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL + +DLG_ACMFORMATCHOOSE_ID DIALOG DISCARDABLE 10, 20, 225, 100 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Selección de sonido" +FONT 8, "MS Shell Dlg" +BEGIN + + LTEXT "&Nombre:", -1, 5, 5, 115, 8, NOT WS_GROUP + + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_CUSTOM, 5, 15, 115, 60, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + + PUSHBUTTON "&Guardar como...", IDD_ACMFORMATCHOOSE_BTN_SETNAME, 125, 14, 45, 14 + PUSHBUTTON "&Eliminar", IDD_ACMFORMATCHOOSE_BTN_DELNAME, 175, 14, 45, 14 + + LTEXT "&Formato:", -1, 5, 41, 44, 8, NOT WS_GROUP + + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, 50, 39, 170, 60, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + + LTEXT "A&tributos:", -1, 5, 59, 44, 8, NOT WS_GROUP + +#if 0 + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP | + CBS_OWNERDRAWFIXED | CBS_HASSTRINGS +#else + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP +#endif + DEFPUSHBUTTON "Aceptar", IDOK, 48, 80, 40, 14 + PUSHBUTTON "Cancelar", IDCANCEL, 92, 80, 40, 14 + PUSHBUTTON "&Ayuda", IDD_ACMFORMATCHOOSE_BTN_HELP, 136, 80, 40, 14 + +END diff --git a/reactos/dll/msacm/msacm_Fr.rc b/reactos/dll/msacm/msacm_Fr.rc new file mode 100644 index 00000000000..f9efdbad7d9 --- /dev/null +++ b/reactos/dll/msacm/msacm_Fr.rc @@ -0,0 +1,57 @@ +/* + * French resource file for MS ACM + * + * Copyright 2000 Eric Pouech + * Copyright 2003 Vincent Béron + * + * 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 + */ + +LANGUAGE LANG_FRENCH, SUBLANG_NEUTRAL + +DLG_ACMFORMATCHOOSE_ID DIALOG DISCARDABLE 10, 20, 225, 100 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Sélection du son" +FONT 8, "MS Shell Dlg" +BEGIN + + LTEXT "&Nom :", -1, 5, 5, 115, 8, NOT WS_GROUP + + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_CUSTOM, 5, 15, 115, 60, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + + PUSHBUTTON "&Enregistrer sous...", IDD_ACMFORMATCHOOSE_BTN_SETNAME, 125, 14, 45, 14 + PUSHBUTTON "&Retirer", IDD_ACMFORMATCHOOSE_BTN_DELNAME, 175, 14, 45, 14 + + LTEXT "&Format :", -1, 5, 41, 44, 8, NOT WS_GROUP + + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, 50, 39, 170, 60, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + + LTEXT "&Attributs :", -1, 5, 59, 44, 8, NOT WS_GROUP + +#if 0 + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP | + CBS_OWNERDRAWFIXED | CBS_HASSTRINGS +#else + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP +#endif + DEFPUSHBUTTON "OK", IDOK, 48, 80, 40, 14 + PUSHBUTTON "Annuler", IDCANCEL, 92, 80, 40, 14 + PUSHBUTTON "&Aide", IDD_ACMFORMATCHOOSE_BTN_HELP, 136, 80, 40, 14 + +END diff --git a/reactos/dll/msacm/msacm_Hu.rc b/reactos/dll/msacm/msacm_Hu.rc new file mode 100644 index 00000000000..d0baa823caa --- /dev/null +++ b/reactos/dll/msacm/msacm_Hu.rc @@ -0,0 +1,57 @@ +/* + * Hungarian resource file for MS ACM + * + * Copyright 2000 Eric Pouech + * Copyright 2005 Gergely Risko + * + * 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 + */ + +LANGUAGE LANG_HUNGARIAN, SUBLANG_NEUTRAL + +DLG_ACMFORMATCHOOSE_ID DIALOG DISCARDABLE 10, 20, 225, 100 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Hang Kiválasztása" +FONT 8, "MS Shell Dlg" +BEGIN + + LTEXT "&Név:", -1, 5, 5, 115, 8, NOT WS_GROUP + + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_CUSTOM, 5, 15, 115, 60, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + + PUSHBUTTON "&Mentés másként...", IDD_ACMFORMATCHOOSE_BTN_SETNAME, 125, 14, 45, 14 +/* NOT TRANSLATED */ PUSHBUTTON "&Remove", IDD_ACMFORMATCHOOSE_BTN_DELNAME, 175, 14, 45, 14 + + LTEXT "&Formátum:", -1, 5, 41, 44, 8, NOT WS_GROUP + + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, 50, 39, 170, 60, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + + LTEXT "&Attribútumok:", -1, 5, 59, 44, 8, NOT WS_GROUP + +#if 0 + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP | + CBS_OWNERDRAWFIXED | CBS_HASSTRINGS +#else + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP +#endif + DEFPUSHBUTTON "OK", IDOK, 48, 80, 40, 14 + PUSHBUTTON "Mégse", IDCANCEL, 92, 80, 40, 14 + PUSHBUTTON "&Súgó", IDD_ACMFORMATCHOOSE_BTN_HELP, 136, 80, 40, 14 + +END diff --git a/reactos/dll/msacm/msacm_It.rc b/reactos/dll/msacm/msacm_It.rc new file mode 100644 index 00000000000..c01db70511e --- /dev/null +++ b/reactos/dll/msacm/msacm_It.rc @@ -0,0 +1,57 @@ +/* + * Italian resource file for MS ACM + * + * Copyright 2000 Eric Pouech + * Copyright 2003 Ivan Leo Puoti + * + * 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 + */ + +LANGUAGE LANG_ITALIAN, SUBLANG_NEUTRAL + +DLG_ACMFORMATCHOOSE_ID DIALOG DISCARDABLE 10, 20, 225, 100 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Selezione dell'audio" +FONT 8, "MS Shell Dlg" +BEGIN + + LTEXT "&Nome:", -1, 5, 5, 115, 8, NOT WS_GROUP + + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_CUSTOM, 5, 15, 115, 60, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + + PUSHBUTTON "&Salva con nome...", IDD_ACMFORMATCHOOSE_BTN_SETNAME, 125, 14, 45, 14 + PUSHBUTTON "&Rimuovi", IDD_ACMFORMATCHOOSE_BTN_DELNAME, 175, 14, 45, 14 + + LTEXT "&Formato:", -1, 5, 41, 44, 8, NOT WS_GROUP + + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, 50, 39, 170, 60, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + + LTEXT "&Attributi:", -1, 5, 59, 44, 8, NOT WS_GROUP + +#if 0 + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP | + CBS_OWNERDRAWFIXED | CBS_HASSTRINGS +#else + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP +#endif + DEFPUSHBUTTON "OK", IDOK, 48, 80, 40, 14 + PUSHBUTTON "Anulla", IDCANCEL, 92, 80, 40, 14 + PUSHBUTTON "&Aiuto", IDD_ACMFORMATCHOOSE_BTN_HELP, 136, 80, 40, 14 + +END diff --git a/reactos/dll/msacm/msacm_Ja.rc b/reactos/dll/msacm/msacm_Ja.rc new file mode 100644 index 00000000000..01e7ba31372 --- /dev/null +++ b/reactos/dll/msacm/msacm_Ja.rc @@ -0,0 +1,56 @@ +/* + * Japanese resource file for MS ACM + * + * Copyright 2000 Hajime Segawa + * + * 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 + */ + +LANGUAGE LANG_JAPANESE, SUBLANG_DEFAULT + +DLG_ACMFORMATCHOOSE_ID DIALOG DISCARDABLE 10, 20, 225, 100 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "ƒTƒEƒ“ƒh‚Ì‘I‘ð" +FONT 9, "MS UI Gothic" +BEGIN + + LTEXT "–¼‘O(&N):", -1, 5, 5, 115, 8, NOT WS_GROUP + + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_CUSTOM, 5, 15, 115, 60, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + + PUSHBUTTON "–¼‘O‚ð•t‚¯‚ĕۑ¶(&S)...", IDD_ACMFORMATCHOOSE_BTN_SETNAME, 125, 14, 45, 14 + PUSHBUTTON "íœ(&R)", IDD_ACMFORMATCHOOSE_BTN_DELNAME, 175, 14, 45, 14 + + LTEXT "ƒtƒH[ƒ}ƒbƒg(&F):", -1, 5, 41, 44, 8, NOT WS_GROUP + + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, 50, 39, 170, 60, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + + LTEXT "‘®«(&A):", -1, 5, 59, 44, 8, NOT WS_GROUP + +#if 0 + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP | + CBS_OWNERDRAWFIXED | CBS_HASSTRINGS +#else + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP +#endif + DEFPUSHBUTTON "OK", IDOK, 48, 80, 40, 14 + PUSHBUTTON "ƒLƒƒƒ“ƒZƒ‹", IDCANCEL, 92, 80, 40, 14 + PUSHBUTTON "ƒwƒ‹ƒv(&H)", IDD_ACMFORMATCHOOSE_BTN_HELP, 136, 80, 40, 14 + +END diff --git a/reactos/dll/msacm/msacm_Nl.rc b/reactos/dll/msacm/msacm_Nl.rc new file mode 100644 index 00000000000..16343235ad2 --- /dev/null +++ b/reactos/dll/msacm/msacm_Nl.rc @@ -0,0 +1,56 @@ +/* + * msacm (Dutch resources) + * + * Copyright 2003 Hans Leidekker + * + * 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 + */ + +LANGUAGE LANG_DUTCH, SUBLANG_NEUTRAL + +DLG_ACMFORMATCHOOSE_ID DIALOG DISCARDABLE 10, 20, 225, 100 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Geluidskeuze" +FONT 8, "MS Shell Dlg" +BEGIN + + LTEXT "&Naam:", -1, 5, 5, 115, 8, NOT WS_GROUP + + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_CUSTOM, 5, 15, 115, 60, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + + PUSHBUTTON "&Opslaan als...", IDD_ACMFORMATCHOOSE_BTN_SETNAME, 125, 14, 45, 14 + PUSHBUTTON "&Verwijderen", IDD_ACMFORMATCHOOSE_BTN_DELNAME, 175, 14, 45, 14 + + LTEXT "&Formaat:", -1, 5, 41, 44, 8, NOT WS_GROUP + + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, 50, 39, 170, 60, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + + LTEXT "&Attributen:", -1, 5, 59, 44, 8, NOT WS_GROUP + +#if 0 + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP | + CBS_OWNERDRAWFIXED | CBS_HASSTRINGS +#else + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP +#endif + DEFPUSHBUTTON "OK", IDOK, 48, 80, 40, 14 + PUSHBUTTON "Annuleren", IDCANCEL, 92, 80, 40, 14 + PUSHBUTTON "&Help", IDD_ACMFORMATCHOOSE_BTN_HELP, 136, 80, 40, 14 + +END diff --git a/reactos/dll/msacm/msacm_Pt.rc b/reactos/dll/msacm/msacm_Pt.rc new file mode 100644 index 00000000000..062742b858e --- /dev/null +++ b/reactos/dll/msacm/msacm_Pt.rc @@ -0,0 +1,56 @@ +/* + * Portuguese resource file for MS ACM + * + * Copyright 2003 Marcelo Duarte + * + * 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 + */ + +LANGUAGE LANG_PORTUGUESE, SUBLANG_NEUTRAL + +DLG_ACMFORMATCHOOSE_ID DIALOG DISCARDABLE 10, 20, 225, 100 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Seleção de som" +FONT 8, "MS Shell Dlg" +BEGIN + + LTEXT "&Nome:", -1, 5, 5, 115, 8, NOT WS_GROUP + + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_CUSTOM, 5, 15, 115, 60, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + + PUSHBUTTON "&Salvar como...", IDD_ACMFORMATCHOOSE_BTN_SETNAME, 125, 14, 45, 14 + PUSHBUTTON "&Remover", IDD_ACMFORMATCHOOSE_BTN_DELNAME, 175, 14, 45, 14 + + LTEXT "&Formato:", -1, 5, 41, 44, 8, NOT WS_GROUP + + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, 50, 39, 170, 60, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + + LTEXT "&Atributos:", -1, 5, 59, 44, 8, NOT WS_GROUP + +#if 0 + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP | + CBS_OWNERDRAWFIXED | CBS_HASSTRINGS +#else + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP +#endif + DEFPUSHBUTTON "OK", IDOK, 48, 80, 40, 14 + PUSHBUTTON "Cancelar", IDCANCEL, 92, 80, 40, 14 + PUSHBUTTON "Aj&uda", IDD_ACMFORMATCHOOSE_BTN_HELP, 136, 80, 40, 14 + +END diff --git a/reactos/dll/msacm/msacm_Ru.rc b/reactos/dll/msacm/msacm_Ru.rc new file mode 100644 index 00000000000..d665e724602 --- /dev/null +++ b/reactos/dll/msacm/msacm_Ru.rc @@ -0,0 +1,57 @@ +/* + * Russian resource file for MS ACM + * + * Copyright 2005 Mikhail Y. Zvyozdochkin + * + * 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 + */ + +LANGUAGE LANG_RUSSIAN, SUBLANG_NEUTRAL + +DLG_ACMFORMATCHOOSE_ID DIALOG DISCARDABLE 10, 20, 234, 101 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Âûáîð çâóêà" +FONT 8, "MS Shell Dlg" +BEGIN + + LTEXT "&Èìÿ:", -1, 5, 5, 115, 8, NOT WS_GROUP + + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_CUSTOM, 5, 15, 115, 60, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + + PUSHBUTTON "Ñîõð&àíèòü êàê...", IDD_ACMFORMATCHOOSE_BTN_SETNAME, 125, 14, 58, 14 + PUSHBUTTON "&Óäàëèòü", IDD_ACMFORMATCHOOSE_BTN_DELNAME, 187, 14, 38, 14 + + LTEXT "&Ôîðìàò:", -1, 5, 41, 44, 8, NOT WS_GROUP + + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, 50, 39, 177, 60, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + + LTEXT "&Ñâîéñòâà:", -1, 5, 59, 44, 8, NOT WS_GROUP + +#if 0 + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 177, 60, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP | + CBS_OWNERDRAWFIXED | CBS_HASSTRINGS +#else + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 177, 60, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP +#endif + DEFPUSHBUTTON "OK", IDOK, 48, 80, 40, 14 + PUSHBUTTON "Îòìåíà", IDCANCEL, 92, 80, 40, 14 + PUSHBUTTON "Ñ&ïðàâêà", IDD_ACMFORMATCHOOSE_BTN_HELP, 136, 80, 40, 14 + +END + diff --git a/reactos/dll/msacm/msacm_Sv.rc b/reactos/dll/msacm/msacm_Sv.rc new file mode 100644 index 00000000000..aa8cb5edb74 --- /dev/null +++ b/reactos/dll/msacm/msacm_Sv.rc @@ -0,0 +1,56 @@ +/* + * Swedish resource file for MS ACM + * + * Copyright 2005 Anders Bergh + * + * 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 + */ + +LANGUAGE LANG_SWEDISH, SUBLANG_NEUTRAL + +DLG_ACMFORMATCHOOSE_ID DIALOG DISCARDABLE 10, 20, 225, 100 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Ljudval" +FONT 8, "MS Shell Dlg" +BEGIN + + LTEXT "&Namn:", -1, 5, 5, 115, 8, NOT WS_GROUP + + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_CUSTOM, 5, 15, 115, 60, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + + PUSHBUTTON "&Spara som...", IDD_ACMFORMATCHOOSE_BTN_SETNAME, 125, 14, 45, 14 + PUSHBUTTON "&Ta bort", IDD_ACMFORMATCHOOSE_BTN_DELNAME, 175, 14, 45, 14 + + LTEXT "&Format:", -1, 5, 41, 44, 8, NOT WS_GROUP + + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMATTAG, 50, 39, 170, 60, + CBS_DROPDOWNLIST | CBS_SORT | WS_VSCROLL | WS_TABSTOP + + LTEXT "&Attribut:", -1, 5, 59, 44, 8, NOT WS_GROUP + +#if 0 + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP | + CBS_OWNERDRAWFIXED | CBS_HASSTRINGS +#else + COMBOBOX IDD_ACMFORMATCHOOSE_CMB_FORMAT, 50, 57, 170, 60, + CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP +#endif + DEFPUSHBUTTON "OK", IDOK, 48, 80, 40, 14 + PUSHBUTTON "Avbryt", IDCANCEL, 92, 80, 40, 14 + PUSHBUTTON "&Hjälp", IDD_ACMFORMATCHOOSE_BTN_HELP, 136, 80, 40, 14 + +END diff --git a/reactos/dll/msacm/msacm_main.c b/reactos/dll/msacm/msacm_main.c new file mode 100644 index 00000000000..01bfa75c577 --- /dev/null +++ b/reactos/dll/msacm/msacm_main.c @@ -0,0 +1,463 @@ +/* + * MSACM library + * + * Copyright 1998 Patrik Stridvall + * + * 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 + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "mmsystem.h" +#include "mmreg.h" +#include "msacm.h" +#include "msacmdrv.h" +#include "wineacm.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msacm); + +/************************************************************************** + * DllEntryPoint (MSACM.255) + * + * MSACM DLL entry point + * + */ +BOOL WINAPI MSACM_DllEntryPoint(DWORD fdwReason, HINSTANCE16 hinstDLL, WORD ds, + WORD wHeapSize, DWORD dwReserved1, WORD wReserved2) +{ + static HANDLE hndl; + + TRACE("0x%x 0x%lx\n", hinstDLL, fdwReason); + + switch (fdwReason) { + case DLL_PROCESS_ATTACH: + if (!hndl && !(hndl = LoadLibraryA("MSACM32.DLL"))) { + ERR("Could not load sibling MsAcm32.dll\n"); + return FALSE; + } + break; + case DLL_PROCESS_DETACH: + FreeLibrary(hndl); + hndl = 0; + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + } + return TRUE; +} + +/*********************************************************************** + * acmGetVersion (MSACM.7) + */ +DWORD WINAPI acmGetVersion16(void) +{ + return acmGetVersion(); +} + +/*********************************************************************** + * acmMetrics (MSACM.8) + */ + +MMRESULT16 WINAPI acmMetrics16( + HACMOBJ16 hao, UINT16 uMetric, LPVOID pMetric) +{ + FIXME("(0x%04x, %d, %p): semi-stub\n", hao, uMetric, pMetric); + + if(!hao) return acmMetrics(0, uMetric, pMetric); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmDriverEnum (MSACM.10) + */ +MMRESULT16 WINAPI acmDriverEnum16( + ACMDRIVERENUMCB16 fnCallback, DWORD dwInstance, DWORD fdwEnum) +{ + FIXME("(%p, %ld, %ld): stub\n", + fnCallback, dwInstance, fdwEnum + ); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmDriverDetails (MSACM.11) + */ + +MMRESULT16 WINAPI acmDriverDetails16( + HACMDRIVERID16 hadid, LPACMDRIVERDETAILS16 padd, DWORD fdwDetails) +{ + FIXME("(0x%04x, %p, %ld): stub\n", hadid, padd, fdwDetails); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmDriverAdd (MSACM.12) + */ +MMRESULT16 WINAPI acmDriverAdd16( + LPHACMDRIVERID16 phadid, HINSTANCE16 hinstModule, + LPARAM lParam, DWORD dwPriority, DWORD fdwAdd) +{ + FIXME("(%p, 0x%04x, %ld, %ld, %ld): stub\n", + phadid, hinstModule, lParam, dwPriority, fdwAdd + ); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmDriverRemove (MSACM.13) + */ +MMRESULT16 WINAPI acmDriverRemove16( + HACMDRIVERID16 hadid, DWORD fdwRemove) +{ + FIXME("(0x%04x, %ld): stub\n", hadid, fdwRemove); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmDriverOpen (MSACM.14) + */ +MMRESULT16 WINAPI acmDriverOpen16( + LPHACMDRIVER16 phad, HACMDRIVERID16 hadid, DWORD fdwOpen) +{ + FIXME("(%p, 0x%04x, %ld): stub\n", phad, hadid, fdwOpen); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmDriverClose (MSACM.15) + */ +MMRESULT16 WINAPI acmDriverClose16( + HACMDRIVER16 had, DWORD fdwClose) +{ + FIXME("(0x%04x, %ld): stub\n", had, fdwClose); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmDriverMessage (MSACM.16) + */ +LRESULT WINAPI acmDriverMessage16( + HACMDRIVER16 had, UINT16 uMsg, LPARAM lParam1, LPARAM lParam2) +{ + FIXME("(0x%04x, %d, %ld, %ld): stub\n", + had, uMsg, lParam1, lParam2 + ); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return 0; +} + +/*********************************************************************** + * acmDriverID (MSACM.17) + */ +MMRESULT16 WINAPI acmDriverID16( + HACMOBJ16 hao, LPHACMDRIVERID16 phadid, DWORD fdwDriverID) +{ + FIXME("(0x%04x, %p, %ld): stub\n", hao, phadid, fdwDriverID); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmDriverPriority (MSACM.18) + */ +MMRESULT16 WINAPI acmDriverPriority16( + HACMDRIVERID16 hadid, DWORD dwPriority, DWORD fdwPriority) +{ + FIXME("(0x%04x, %ld, %ld): stub\n", + hadid, dwPriority, fdwPriority + ); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmFormatTagDetails (MSACM.30) + */ +MMRESULT16 WINAPI acmFormatTagDetails16( + HACMDRIVER16 had, LPACMFORMATTAGDETAILS16 paftd, DWORD fdwDetails) +{ + FIXME("(0x%04x, %p, %ld): stub\n", had, paftd, fdwDetails); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmFormatTagEnum (MSACM.31) + */ +MMRESULT16 WINAPI acmFormatTagEnum16( + HACMDRIVER16 had, LPACMFORMATTAGDETAILS16 paftd, + ACMFORMATTAGENUMCB16 fnCallback, DWORD dwInstance, DWORD fdwEnum) +{ + FIXME("(0x%04x, %p, %p, %ld, %ld): stub\n", + had, paftd, fnCallback, dwInstance, fdwEnum + ); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmFormatChoose (MSACM.40) + */ +MMRESULT16 WINAPI acmFormatChoose16( + LPACMFORMATCHOOSE16 pafmtc) +{ + FIXME("(%p): stub\n", pafmtc); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmFormatDetails (MSACM.41) + */ +MMRESULT16 WINAPI acmFormatDetails16( + HACMDRIVER16 had, LPACMFORMATDETAILS16 pafd, DWORD fdwDetails) +{ + FIXME("(0x%04x, %p, %ld): stub\n", had, pafd, fdwDetails); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmFormatEnum (MSACM.42) + */ +MMRESULT16 WINAPI acmFormatEnum16( + HACMDRIVER16 had, LPACMFORMATDETAILS16 pafd, + ACMFORMATENUMCB16 fnCallback, DWORD dwInstance, DWORD fdwEnum) +{ + FIXME("(0x%04x, %p, %p, %ld, %ld): stub\n", + had, pafd, fnCallback, dwInstance, fdwEnum + ); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmFormatSuggest (MSACM.45) + */ +MMRESULT16 WINAPI acmFormatSuggest16( + HACMDRIVER16 had, LPWAVEFORMATEX pwfxSrc, + LPWAVEFORMATEX pwfxDst, DWORD cbwfxDst, DWORD fdwSuggest) +{ + FIXME("(0x%04x, %p, %p, %ld, %ld): stub\n", + had, pwfxSrc, pwfxDst, cbwfxDst, fdwSuggest + ); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmFilterTagDetails (MSACM.50) + */ +MMRESULT16 WINAPI acmFilterTagDetails16( + HACMDRIVER16 had, LPACMFILTERTAGDETAILS16 paftd, DWORD fdwDetails) +{ + FIXME("(0x%04x, %p, %ld): stub\n", had, paftd, fdwDetails); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmFilterTagEnum (MSACM.51) + */ +MMRESULT16 WINAPI acmFilterTagEnum16( + HACMDRIVER16 had, LPACMFILTERTAGDETAILS16 paftd, + ACMFILTERTAGENUMCB16 fnCallback, DWORD dwInstance, DWORD fdwEnum) +{ + FIXME("(0x%04x, %p, %p, %ld, %ld): stub\n", + had, paftd, fnCallback, dwInstance, fdwEnum + ); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmFilterChoose (MSACM.60) + */ +MMRESULT16 WINAPI acmFilterChoose16( + LPACMFILTERCHOOSE16 pafltrc) +{ + FIXME("(%p): stub\n", pafltrc); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmFilterDetails (MSACM.61) + */ +MMRESULT16 WINAPI acmFilterDetails16( + HACMDRIVER16 had, LPACMFILTERDETAILS16 pafd, DWORD fdwDetails) +{ + FIXME("(0x%04x, %p, %ld): stub\n", had, pafd, fdwDetails); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmFilterEnum (MSACM.62) + */ +MMRESULT16 WINAPI acmFilterEnum16( + HACMDRIVER16 had, LPACMFILTERDETAILS16 pafd, + ACMFILTERENUMCB16 fnCallback, DWORD dwInstance, DWORD fdwEnum) +{ + FIXME("(0x%04x, %p, %p, %ld, %ld): stub\n", + had, pafd, fnCallback, dwInstance, fdwEnum + ); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmStreamOpen (MSACM.70) + */ +MMRESULT16 WINAPI acmStreamOpen16( + LPHACMSTREAM16 phas, HACMDRIVER16 had, + LPWAVEFORMATEX pwfxSrc, LPWAVEFORMATEX pwfxDst, + LPWAVEFILTER pwfltr, DWORD dwCallback, + DWORD dwInstance, DWORD fdwOpen) +{ + FIXME("(%p, 0x%04x, %p, %p, %p, %ld, %ld, %ld): stub\n", + phas, had, pwfxSrc, pwfxDst, pwfltr, + dwCallback, dwInstance, fdwOpen + ); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmStreamClose (MSACM.71) + */ +MMRESULT16 WINAPI acmStreamClose16( + HACMSTREAM16 has, DWORD fdwClose) +{ + FIXME("(0x%04x, %ld): stub\n", has, fdwClose); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmStreamSize (MSACM.72) + */ +MMRESULT16 WINAPI acmStreamSize16( + HACMSTREAM16 has, DWORD cbInput, + LPDWORD pdwOutputBytes, DWORD fdwSize) +{ + FIXME("(0x%04x, %ld, %p, %ld): stub\n", + has, cbInput, pdwOutputBytes, fdwSize + ); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmStreamConvert (MSACM.75) + */ +MMRESULT16 WINAPI acmStreamConvert16( + HACMSTREAM16 has, LPACMSTREAMHEADER16 pash, DWORD fdwConvert) +{ + FIXME("(0x%04x, %p, %ld): stub\n", has, pash, fdwConvert); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmStreamReset (MSACM.76) + */ +MMRESULT16 WINAPI acmStreamReset16( + HACMSTREAM16 has, DWORD fdwReset) +{ + FIXME("(0x%04x, %ld): stub\n", has, fdwReset); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmStreamPrepareHeader (MSACM.77) + */ +MMRESULT16 WINAPI acmStreamPrepareHeader16( + HACMSTREAM16 has, LPACMSTREAMHEADER16 pash, DWORD fdwPrepare) +{ + FIXME("(0x%04x, %p, %ld): stub\n", has, pash, fdwPrepare); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmStreamUnprepareHeader (MSACM.78) + */ +MMRESULT16 WINAPI acmStreamUnprepareHeader16( + HACMSTREAM16 has, LPACMSTREAMHEADER16 pash, DWORD fdwUnprepare) +{ + FIXME("(0x%04x, %p, %ld): stub\n", + has, pash, fdwUnprepare + ); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * ACMAPPLICATIONEXIT (MSACM.150) + * FIXME + * No documentation found. + */ + +/*********************************************************************** + * ACMHUGEPAGELOCK (MSACM.175) + *FIXME + * No documentation found. + */ + +/*********************************************************************** + * ACMHUGEPAGEUNLOCK (MSACM.176) + * FIXME + * No documentation found. + */ + +/*********************************************************************** + * ACMOPENCONVERSION (MSACM.200) + * FIXME + * No documentation found. + */ + +/*********************************************************************** + * ACMCLOSECONVERSION (MSACM.201) + * FIXME + * No documentation found. + */ + +/*********************************************************************** + * ACMCONVERT (MSACM.202) + * FIXME + * No documentation found. + */ + +/*********************************************************************** + * ACMCHOOSEFORMAT (MSACM.203) + * FIXME + * No documentation found. + */ + + diff --git a/reactos/dll/msacm/msadp32/Makefile.in b/reactos/dll/msacm/msadp32/Makefile.in new file mode 100644 index 00000000000..c331f654c86 --- /dev/null +++ b/reactos/dll/msacm/msadp32/Makefile.in @@ -0,0 +1,12 @@ +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = ../../.. +SRCDIR = @srcdir@ +VPATH = @srcdir@ +MODULE = msadp32.acm +IMPORTS = winmm user32 kernel32 + +C_SRCS = msadp32.c + +@MAKE_DLL_RULES@ + +### Dependencies: diff --git a/reactos/dll/msacm/msadp32/msadp32.acm.spec b/reactos/dll/msacm/msadp32/msadp32.acm.spec new file mode 100644 index 00000000000..a0ba1c7e701 --- /dev/null +++ b/reactos/dll/msacm/msadp32/msadp32.acm.spec @@ -0,0 +1 @@ +@ stdcall DriverProc (long long long long long) ADPCM_DriverProc diff --git a/reactos/dll/msacm/msadp32/msadp32.c b/reactos/dll/msacm/msadp32/msadp32.c new file mode 100644 index 00000000000..dae681c9c90 --- /dev/null +++ b/reactos/dll/msacm/msadp32/msadp32.c @@ -0,0 +1,781 @@ +/* + * MS ADPCM handling + * + * Copyright (C) 2002 Eric Pouech + * + * + * 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 +#include +#include +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "winnls.h" +#include "mmsystem.h" +#include "mmreg.h" +#include "msacm.h" +#include "msacmdrv.h" +#include "wine/debug.h" + +/* see http://www.pcisys.net/~melanson/codecs/adpcm.txt for the details */ + +WINE_DEFAULT_DEBUG_CHANNEL(adpcm); + +/*********************************************************************** + * ADPCM_drvOpen + */ +static DWORD ADPCM_drvOpen(LPCSTR str) +{ + return 1; +} + +/*********************************************************************** + * ADPCM_drvClose + */ +static DWORD ADPCM_drvClose(DWORD dwDevID) +{ + return 1; +} + +typedef struct tagAcmAdpcmData +{ + void (*convert)(PACMDRVSTREAMINSTANCE adsi, + const unsigned char*, LPDWORD, unsigned char*, LPDWORD); +} AcmAdpcmData; + +/* table to list all supported formats... those are the basic ones. this + * also helps given a unique index to each of the supported formats + */ +typedef struct +{ + int nChannels; + int nBits; + int rate; +} Format; + +static Format PCM_Formats[] = +{ + {1, 8, 8000}, {2, 8, 8000}, {1, 16, 8000}, {2, 16, 8000}, + {1, 8, 11025}, {2, 8, 11025}, {1, 16, 11025}, {2, 16, 11025}, + {1, 8, 22050}, {2, 8, 22050}, {1, 16, 22050}, {2, 16, 22050}, + {1, 8, 44100}, {2, 8, 44100}, {1, 16, 44100}, {2, 16, 44100}, +}; + +static Format ADPCM_Formats[] = +{ + {1, 4, 8000}, {2, 4, 8000}, {1, 4, 11025}, {2, 4, 11025}, + {1, 4, 22050}, {2, 4, 22050}, {1, 4, 44100}, {2, 4, 44100}, +}; + +#define NUM_PCM_FORMATS (sizeof(PCM_Formats) / sizeof(PCM_Formats[0])) +#define NUM_ADPCM_FORMATS (sizeof(ADPCM_Formats) / sizeof(ADPCM_Formats[0])) + +static int MS_Delta[] = +{ + 230, 230, 230, 230, 307, 409, 512, 614, + 768, 614, 512, 409, 307, 230, 230, 230 +}; + + +static ADPCMCOEFSET MSADPCM_CoeffSet[] = +{ + {256, 0}, {512, -256}, {0, 0}, {192, 64}, {240, 0}, {460, -208}, {392, -232} +}; + +/*********************************************************************** + * ADPCM_GetFormatIndex + */ +static DWORD ADPCM_GetFormatIndex(WAVEFORMATEX* wfx) +{ + int i, hi; + Format* fmts; + + switch (wfx->wFormatTag) + { + case WAVE_FORMAT_PCM: + hi = NUM_PCM_FORMATS; + fmts = PCM_Formats; + break; + case WAVE_FORMAT_ADPCM: + hi = NUM_ADPCM_FORMATS; + fmts = ADPCM_Formats; + break; + default: + return 0xFFFFFFFF; + } + + for (i = 0; i < hi; i++) + { + if (wfx->nChannels == fmts[i].nChannels && + wfx->nSamplesPerSec == fmts[i].rate && + wfx->wBitsPerSample == fmts[i].nBits) + return i; + } + + return 0xFFFFFFFF; +} + +static void init_wfx_adpcm(ADPCMWAVEFORMAT* awfx) +{ + register WAVEFORMATEX* pwfx = &awfx->wfx; + + /* we assume wFormatTag, nChannels, nSamplesPerSec and wBitsPerSample + * have been initialized... */ + + if (pwfx->wFormatTag != WAVE_FORMAT_ADPCM) {FIXME("wrong FT\n"); return;} + if (ADPCM_GetFormatIndex(pwfx) == 0xFFFFFFFF) {FIXME("wrong fmt\n"); return;} + + switch (pwfx->nSamplesPerSec) + { + case 8000: pwfx->nBlockAlign = 256; break; + case 11025: pwfx->nBlockAlign = 256; break; + case 22050: pwfx->nBlockAlign = 512; break; + default: + case 44100: pwfx->nBlockAlign = 1024; break; + } + pwfx->cbSize = 2 * sizeof(WORD) + 7 * sizeof(ADPCMCOEFSET); + /* 7 is the size of the block head (which contains two samples) */ + + awfx->wSamplesPerBlock = (pwfx->nBlockAlign - (7 * pwfx->nChannels)) * (2 / pwfx->nChannels) + 2; + pwfx->nAvgBytesPerSec = (pwfx->nSamplesPerSec * pwfx->nBlockAlign) / awfx->wSamplesPerBlock; + awfx->wNumCoef = 7; + memcpy(awfx->aCoef, MSADPCM_CoeffSet, 7 * sizeof(ADPCMCOEFSET)); +} + +/*********************************************************************** + * R16 + * + * Read a 16 bit sample (correctly handles endianess) + */ +static inline short R16(const unsigned char* src) +{ + return (short)((unsigned short)src[0] | ((unsigned short)src[1] << 8)); +} + +/*********************************************************************** + * W16 + * + * Write a 16 bit sample (correctly handles endianess) + */ +static inline void W16(unsigned char* dst, short s) +{ + dst[0] = LOBYTE(s); + dst[1] = HIBYTE(s); +} + +static inline void clamp_sample(int* sample) +{ + if (*sample < -32768) *sample = -32768; + if (*sample > 32767) *sample = 32767; +} + +static inline void process_nibble(unsigned nibble, int* idelta, + int* sample1, int* sample2, + const ADPCMCOEFSET* coeff) +{ + int sample; + int snibble; + + /* nibble is in fact a signed 4 bit integer => propagate sign if needed */ + snibble = (nibble & 0x08) ? (nibble - 16) : nibble; + sample = ((*sample1 * coeff->iCoef1) + (*sample2 * coeff->iCoef2)) / 256 + + snibble * *idelta; + clamp_sample(&sample); + + *sample2 = *sample1; + *sample1 = sample; + *idelta = ((MS_Delta[nibble] * *idelta) / 256); + if (*idelta < 16) *idelta = 16; +} + +static void cvtSSms16K(PACMDRVSTREAMINSTANCE adsi, + const unsigned char* src, LPDWORD nsrc, + unsigned char* dst, LPDWORD ndst) +{ + int ideltaL, ideltaR; + int sample1L, sample2L; + int sample1R, sample2R; + ADPCMCOEFSET coeffL, coeffR; + int nsamp; + int nsamp_blk = ((ADPCMWAVEFORMAT*)adsi->pwfxSrc)->wSamplesPerBlock; + DWORD nblock = min(*nsrc / adsi->pwfxSrc->nBlockAlign, + *ndst / (nsamp_blk * 2 * 2)); + + *nsrc = nblock * adsi->pwfxSrc->nBlockAlign; + *ndst = nblock * nsamp_blk * 2 * 2; + + nsamp_blk -= 2; /* see below for samples from block head */ + for (; nblock > 0; nblock--) + { + const unsigned char* in_src = src; + + assert(*src <= 6); + coeffL = MSADPCM_CoeffSet[*src++]; + assert(*src <= 6); + coeffR = MSADPCM_CoeffSet[*src++]; + + ideltaL = R16(src); src += 2; + ideltaR = R16(src); src += 2; + sample1L = R16(src); src += 2; + sample1R = R16(src); src += 2; + sample2L = R16(src); src += 2; + sample2R = R16(src); src += 2; + + /* store samples from block head */ + W16(dst, sample2L); dst += 2; + W16(dst, sample2R); dst += 2; + W16(dst, sample1L); dst += 2; + W16(dst, sample1R); dst += 2; + + for (nsamp = nsamp_blk; nsamp > 0; nsamp--) + { + process_nibble(*src >> 4, &ideltaL, &sample1L, &sample2L, &coeffL); + W16(dst, sample1L); dst += 2; + process_nibble(*src++ & 0x0F, &ideltaR, &sample1R, &sample2R, &coeffR); + W16(dst, sample1R); dst += 2; + } + src = in_src + adsi->pwfxSrc->nBlockAlign; + } +} + +static void cvtMMms16K(PACMDRVSTREAMINSTANCE adsi, + const unsigned char* src, LPDWORD nsrc, + unsigned char* dst, LPDWORD ndst) +{ + int idelta; + int sample1, sample2; + ADPCMCOEFSET coeff; + int nsamp; + int nsamp_blk = ((ADPCMWAVEFORMAT*)adsi->pwfxSrc)->wSamplesPerBlock; + DWORD nblock = min(*nsrc / adsi->pwfxSrc->nBlockAlign, + *ndst / (nsamp_blk * 2)); + + *nsrc = nblock * adsi->pwfxSrc->nBlockAlign; + *ndst = nblock * nsamp_blk * 2; + + nsamp_blk -= 2; /* see below for samples from block head */ + for (; nblock > 0; nblock--) + { + const unsigned char* in_src = src; + + assert(*src <= 6); + coeff = MSADPCM_CoeffSet[*src++]; + + idelta = R16(src); src += 2; + sample1 = R16(src); src += 2; + sample2 = R16(src); src += 2; + + /* store samples from block head */ + W16(dst, sample2); dst += 2; + W16(dst, sample1); dst += 2; + + for (nsamp = nsamp_blk; nsamp > 0; nsamp -= 2) + { + process_nibble(*src >> 4, &idelta, &sample1, &sample2, &coeff); + W16(dst, sample1); dst += 2; + process_nibble(*src++ & 0x0F, &idelta, &sample1, &sample2, &coeff); + W16(dst, sample1); dst += 2; + } + src = in_src + adsi->pwfxSrc->nBlockAlign; + } +} + +#if 0 +static void cvtSS16msK(PACMDRVSTREAMINSTANCE adsi, + const unsigned char* src, LPDWORD nsrc, + unsigned char* dst, LPDWORD ndst) +{ +} + +static void cvtMM16msK(PACMDRVSTREAMINSTANCE adsi, + const unsigned char* src, LPDWORD nsrc, + unsigned char* dst, LPDWORD ndst) +{ +} +#endif + +/*********************************************************************** + * ADPCM_DriverDetails + * + */ +static LRESULT ADPCM_DriverDetails(PACMDRIVERDETAILSW add) +{ + add->fccType = ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC; + add->fccComp = ACMDRIVERDETAILS_FCCCOMP_UNDEFINED; + add->wMid = 0xFF; + add->wPid = 0x00; + add->vdwACM = 0x01000000; + add->vdwDriver = 0x01000000; + add->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CODEC; + add->cFormatTags = 2; /* PCM, MS ADPCM */ + add->cFilterTags = 0; + add->hicon = NULL; + MultiByteToWideChar( CP_ACP, 0, "WINE-MS ADPCM", -1, + add->szShortName, sizeof(add->szShortName)/sizeof(WCHAR) ); + MultiByteToWideChar( CP_ACP, 0, "Wine MS ADPCM converter", -1, + add->szLongName, sizeof(add->szLongName)/sizeof(WCHAR) ); + MultiByteToWideChar( CP_ACP, 0, "Brought to you by the Wine team...", -1, + add->szCopyright, sizeof(add->szCopyright)/sizeof(WCHAR) ); + MultiByteToWideChar( CP_ACP, 0, "Refer to LICENSE file", -1, + add->szLicensing, sizeof(add->szLicensing)/sizeof(WCHAR) ); + add->szFeatures[0] = 0; + + return MMSYSERR_NOERROR; +} + +/*********************************************************************** + * ADPCM_FormatTagDetails + * + */ +static LRESULT ADPCM_FormatTagDetails(PACMFORMATTAGDETAILSW aftd, DWORD dwQuery) +{ + static WCHAR szPcm[]={'P','C','M',0}; + static WCHAR szMsAdPcm[]={'M','S',' ','A','d','P','C','M',0}; + + switch (dwQuery) + { + case ACM_FORMATTAGDETAILSF_INDEX: + if (aftd->dwFormatTagIndex >= 2) return ACMERR_NOTPOSSIBLE; + break; + case ACM_FORMATTAGDETAILSF_LARGESTSIZE: + if (aftd->dwFormatTag == WAVE_FORMAT_UNKNOWN) + { + aftd->dwFormatTagIndex = 1; /* WAVE_FORMAT_ADPCM is bigger than PCM */ + break; + } + /* fall thru */ + case ACM_FORMATTAGDETAILSF_FORMATTAG: + switch (aftd->dwFormatTag) + { + case WAVE_FORMAT_PCM: aftd->dwFormatTagIndex = 0; break; + case WAVE_FORMAT_ADPCM: aftd->dwFormatTagIndex = 1; break; + default: return ACMERR_NOTPOSSIBLE; + } + break; + default: + WARN("Unsupported query %08lx\n", dwQuery); + return MMSYSERR_NOTSUPPORTED; + } + + aftd->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CODEC; + switch (aftd->dwFormatTagIndex) + { + case 0: + aftd->dwFormatTag = WAVE_FORMAT_PCM; + aftd->cbFormatSize = sizeof(PCMWAVEFORMAT); + aftd->cStandardFormats = NUM_PCM_FORMATS; + lstrcpyW(aftd->szFormatTag, szPcm); + break; + case 1: + aftd->dwFormatTag = WAVE_FORMAT_ADPCM; + aftd->cbFormatSize = sizeof(ADPCMWAVEFORMAT) + (7 - 1) * sizeof(ADPCMCOEFSET); + aftd->cStandardFormats = NUM_ADPCM_FORMATS; + lstrcpyW(aftd->szFormatTag, szMsAdPcm); + break; + } + return MMSYSERR_NOERROR; +} + +/*********************************************************************** + * ADPCM_FormatDetails + * + */ +static LRESULT ADPCM_FormatDetails(PACMFORMATDETAILSW afd, DWORD dwQuery) +{ + switch (dwQuery) + { + case ACM_FORMATDETAILSF_FORMAT: + if (ADPCM_GetFormatIndex(afd->pwfx) == 0xFFFFFFFF) return ACMERR_NOTPOSSIBLE; + break; + case ACM_FORMATDETAILSF_INDEX: + afd->pwfx->wFormatTag = afd->dwFormatTag; + switch (afd->dwFormatTag) + { + case WAVE_FORMAT_PCM: + if (afd->dwFormatIndex >= NUM_PCM_FORMATS) return ACMERR_NOTPOSSIBLE; + afd->pwfx->nChannels = PCM_Formats[afd->dwFormatIndex].nChannels; + afd->pwfx->nSamplesPerSec = PCM_Formats[afd->dwFormatIndex].rate; + afd->pwfx->wBitsPerSample = PCM_Formats[afd->dwFormatIndex].nBits; + /* native MSACM uses a PCMWAVEFORMAT structure, so cbSize is not accessible + * afd->pwfx->cbSize = 0; + */ + afd->pwfx->nBlockAlign = + (afd->pwfx->nChannels * afd->pwfx->wBitsPerSample) / 8; + afd->pwfx->nAvgBytesPerSec = + afd->pwfx->nSamplesPerSec * afd->pwfx->nBlockAlign; + break; + case WAVE_FORMAT_ADPCM: + if (afd->dwFormatIndex >= NUM_ADPCM_FORMATS) return ACMERR_NOTPOSSIBLE; + if (afd->cbwfx < sizeof(ADPCMWAVEFORMAT) + (7 - 1) * sizeof(ADPCMCOEFSET)) + return ACMERR_NOTPOSSIBLE; + afd->pwfx->nChannels = ADPCM_Formats[afd->dwFormatIndex].nChannels; + afd->pwfx->nSamplesPerSec = ADPCM_Formats[afd->dwFormatIndex].rate; + afd->pwfx->wBitsPerSample = ADPCM_Formats[afd->dwFormatIndex].nBits; + init_wfx_adpcm((ADPCMWAVEFORMAT*)afd->pwfx); + break; + default: + WARN("Unsupported tag %08lx\n", afd->dwFormatTag); + return MMSYSERR_INVALPARAM; + } + break; + default: + WARN("Unsupported query %08lx\n", dwQuery); + return MMSYSERR_NOTSUPPORTED; + } + afd->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CODEC; + afd->szFormat[0] = 0; /* let MSACM format this for us... */ + + return MMSYSERR_NOERROR; +} + +/*********************************************************************** + * ADPCM_FormatSuggest + * + */ +static LRESULT ADPCM_FormatSuggest(PACMDRVFORMATSUGGEST adfs) +{ + /* some tests ... */ + if (adfs->cbwfxSrc < sizeof(PCMWAVEFORMAT) || + adfs->cbwfxDst < sizeof(PCMWAVEFORMAT) || + ADPCM_GetFormatIndex(adfs->pwfxSrc) == 0xFFFFFFFF) return ACMERR_NOTPOSSIBLE; + /* FIXME: should do those tests against the real size (according to format tag */ + + /* If no suggestion for destination, then copy source value */ + if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_NCHANNELS)) + adfs->pwfxDst->nChannels = adfs->pwfxSrc->nChannels; + if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_NSAMPLESPERSEC)) + adfs->pwfxDst->nSamplesPerSec = adfs->pwfxSrc->nSamplesPerSec; + + if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_WBITSPERSAMPLE)) + { + if (adfs->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM) + adfs->pwfxDst->wBitsPerSample = 4; + else + adfs->pwfxDst->wBitsPerSample = 16; + } + if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_WFORMATTAG)) + { + if (adfs->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM) + adfs->pwfxDst->wFormatTag = WAVE_FORMAT_ADPCM; + else + adfs->pwfxDst->wFormatTag = WAVE_FORMAT_PCM; + } + + /* check if result is ok */ + if (ADPCM_GetFormatIndex(adfs->pwfxDst) == 0xFFFFFFFF) return ACMERR_NOTPOSSIBLE; + + /* recompute other values */ + switch (adfs->pwfxDst->wFormatTag) + { + case WAVE_FORMAT_PCM: + adfs->pwfxDst->nBlockAlign = (adfs->pwfxDst->nChannels * adfs->pwfxDst->wBitsPerSample) / 8; + adfs->pwfxDst->nAvgBytesPerSec = adfs->pwfxDst->nSamplesPerSec * adfs->pwfxDst->nBlockAlign; + break; + case WAVE_FORMAT_ADPCM: + init_wfx_adpcm((ADPCMWAVEFORMAT*)adfs->pwfxDst); + break; + default: + FIXME("\n"); + break; + } + + return MMSYSERR_NOERROR; +} + +/*********************************************************************** + * ADPCM_Reset + * + */ +static void ADPCM_Reset(PACMDRVSTREAMINSTANCE adsi, AcmAdpcmData* aad) +{ +} + +/*********************************************************************** + * ADPCM_StreamOpen + * + */ +static LRESULT ADPCM_StreamOpen(PACMDRVSTREAMINSTANCE adsi) +{ + AcmAdpcmData* aad; + + assert(!(adsi->fdwOpen & ACM_STREAMOPENF_ASYNC)); + + if (ADPCM_GetFormatIndex(adsi->pwfxSrc) == 0xFFFFFFFF || + ADPCM_GetFormatIndex(adsi->pwfxDst) == 0xFFFFFFFF) + return ACMERR_NOTPOSSIBLE; + + aad = HeapAlloc(GetProcessHeap(), 0, sizeof(AcmAdpcmData)); + if (aad == 0) return MMSYSERR_NOMEM; + + adsi->dwDriver = (DWORD)aad; + + if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM && + adsi->pwfxDst->wFormatTag == WAVE_FORMAT_PCM) + { + goto theEnd; + } + else if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_ADPCM && + adsi->pwfxDst->wFormatTag == WAVE_FORMAT_PCM) + { + /* resampling or mono <=> stereo not available + * ADPCM algo only define 16 bit per sample output + */ + if (adsi->pwfxSrc->nSamplesPerSec != adsi->pwfxDst->nSamplesPerSec || + adsi->pwfxSrc->nChannels != adsi->pwfxDst->nChannels || + adsi->pwfxDst->wBitsPerSample != 16) + goto theEnd; + +#if 0 + { + unsigned int nspb = ((IMAADPCMWAVEFORMAT*)adsi->pwfxSrc)->wSamplesPerBlock; + FIXME("spb=%u\n", nspb); + + /* we check that in a block, after the header, samples are present on + * 4-sample packet pattern + * we also check that the block alignement is bigger than the expected size + */ + if (((nspb - 1) & 3) != 0) goto theEnd; + if ((((nspb - 1) / 2) + 4) * adsi->pwfxSrc->nChannels < adsi->pwfxSrc->nBlockAlign) + goto theEnd; + } +#endif + + /* adpcm decoding... */ + if (adsi->pwfxDst->wBitsPerSample == 16 && adsi->pwfxDst->nChannels == 2) + aad->convert = cvtSSms16K; + if (adsi->pwfxDst->wBitsPerSample == 16 && adsi->pwfxDst->nChannels == 1) + aad->convert = cvtMMms16K; + } + else if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM && + adsi->pwfxDst->wFormatTag == WAVE_FORMAT_ADPCM) + { + if (adsi->pwfxSrc->nSamplesPerSec != adsi->pwfxDst->nSamplesPerSec || + adsi->pwfxSrc->nChannels != adsi->pwfxDst->nChannels || + adsi->pwfxSrc->wBitsPerSample != 16) + goto theEnd; +#if 0 + nspb = ((IMAADPCMWAVEFORMAT*)adsi->pwfxDst)->wSamplesPerBlock; + FIXME("spb=%u\n", nspb); + + /* we check that in a block, after the header, samples are present on + * 4-sample packet pattern + * we also check that the block alignement is bigger than the expected size + */ + if (((nspb - 1) & 3) != 0) goto theEnd; + if ((((nspb - 1) / 2) + 4) * adsi->pwfxDst->nChannels < adsi->pwfxDst->nBlockAlign) + goto theEnd; +#endif +#if 0 + /* adpcm coding... */ + if (adsi->pwfxSrc->wBitsPerSample == 16 && adsi->pwfxSrc->nChannels == 2) + aad->convert = cvtSS16msK; + if (adsi->pwfxSrc->wBitsPerSample == 16 && adsi->pwfxSrc->nChannels == 1) + aad->convert = cvtMM16msK; +#endif + FIXME("We don't support encoding yet\n"); + goto theEnd; + } + else goto theEnd; + ADPCM_Reset(adsi, aad); + + return MMSYSERR_NOERROR; + + theEnd: + HeapFree(GetProcessHeap(), 0, aad); + adsi->dwDriver = 0L; + return MMSYSERR_NOTSUPPORTED; +} + +/*********************************************************************** + * ADPCM_StreamClose + * + */ +static LRESULT ADPCM_StreamClose(PACMDRVSTREAMINSTANCE adsi) +{ + HeapFree(GetProcessHeap(), 0, (void*)adsi->dwDriver); + return MMSYSERR_NOERROR; +} + +/*********************************************************************** + * ADPCM_round + * + */ +static inline DWORD ADPCM_round(DWORD a, DWORD b, DWORD c) +{ + assert(a && b && c); + /* to be sure, always return an entire number of c... */ + return ((double)a * (double)b + (double)c - 1) / (double)c; +} + +/*********************************************************************** + * ADPCM_StreamSize + * + */ +static LRESULT ADPCM_StreamSize(PACMDRVSTREAMINSTANCE adsi, PACMDRVSTREAMSIZE adss) +{ + switch (adss->fdwSize) + { + case ACM_STREAMSIZEF_DESTINATION: + /* cbDstLength => cbSrcLength */ + if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM && + adsi->pwfxDst->wFormatTag == WAVE_FORMAT_ADPCM) + { + /* don't take block overhead into account, doesn't matter too much */ + adss->cbSrcLength = adss->cbDstLength * 4; + } + else if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_ADPCM && + adsi->pwfxDst->wFormatTag == WAVE_FORMAT_PCM) + { + FIXME("misses the block header overhead\n"); + adss->cbSrcLength = 256 + adss->cbDstLength / 4; + } + else + { + return MMSYSERR_NOTSUPPORTED; + } + break; + case ACM_STREAMSIZEF_SOURCE: + /* cbSrcLength => cbDstLength */ + if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_PCM && + adsi->pwfxDst->wFormatTag == WAVE_FORMAT_ADPCM) + { + FIXME("misses the block header overhead\n"); + adss->cbDstLength = 256 + adss->cbSrcLength / 4; + } + else if (adsi->pwfxSrc->wFormatTag == WAVE_FORMAT_ADPCM && + adsi->pwfxDst->wFormatTag == WAVE_FORMAT_PCM) + { + /* don't take block overhead into account, doesn't matter too much */ + adss->cbDstLength = adss->cbSrcLength * 4; + } + else + { + return MMSYSERR_NOTSUPPORTED; + } + break; + default: + WARN("Unsupported query %08lx\n", adss->fdwSize); + return MMSYSERR_NOTSUPPORTED; + } + return MMSYSERR_NOERROR; +} + +/*********************************************************************** + * ADPCM_StreamConvert + * + */ +static LRESULT ADPCM_StreamConvert(PACMDRVSTREAMINSTANCE adsi, PACMDRVSTREAMHEADER adsh) +{ + AcmAdpcmData* aad = (AcmAdpcmData*)adsi->dwDriver; + DWORD nsrc = adsh->cbSrcLength; + DWORD ndst = adsh->cbDstLength; + + if (adsh->fdwConvert & + ~(ACM_STREAMCONVERTF_BLOCKALIGN| + ACM_STREAMCONVERTF_END| + ACM_STREAMCONVERTF_START)) + { + FIXME("Unsupported fdwConvert (%08lx), ignoring it\n", adsh->fdwConvert); + } + /* ACM_STREAMCONVERTF_BLOCKALIGN + * currently all conversions are block aligned, so do nothing for this flag + * ACM_STREAMCONVERTF_END + * no pending data, so do nothing for this flag + */ + if ((adsh->fdwConvert & ACM_STREAMCONVERTF_START)) + { + ADPCM_Reset(adsi, aad); + } + + aad->convert(adsi, adsh->pbSrc, &nsrc, adsh->pbDst, &ndst); + adsh->cbSrcLengthUsed = nsrc; + adsh->cbDstLengthUsed = ndst; + + return MMSYSERR_NOERROR; +} + +/************************************************************************** + * ADPCM_DriverProc [exported] + */ +LRESULT CALLBACK ADPCM_DriverProc(DWORD dwDevID, HDRVR hDriv, UINT wMsg, + LPARAM dwParam1, LPARAM dwParam2) +{ + TRACE("(%08lx %08lx %04x %08lx %08lx);\n", + dwDevID, (DWORD)hDriv, wMsg, dwParam1, dwParam2); + + switch (wMsg) + { + case DRV_LOAD: return 1; + case DRV_FREE: return 1; + case DRV_OPEN: return ADPCM_drvOpen((LPSTR)dwParam1); + case DRV_CLOSE: return ADPCM_drvClose(dwDevID); + case DRV_ENABLE: return 1; + case DRV_DISABLE: return 1; + case DRV_QUERYCONFIGURE: return 1; + case DRV_CONFIGURE: MessageBoxA(0, "MSACM MS ADPCM filter !", "Wine Driver", MB_OK); return 1; + case DRV_INSTALL: return DRVCNF_RESTART; + case DRV_REMOVE: return DRVCNF_RESTART; + + case ACMDM_DRIVER_NOTIFY: + /* no caching from other ACM drivers is done so far */ + return MMSYSERR_NOERROR; + + case ACMDM_DRIVER_DETAILS: + return ADPCM_DriverDetails((PACMDRIVERDETAILSW)dwParam1); + + case ACMDM_FORMATTAG_DETAILS: + return ADPCM_FormatTagDetails((PACMFORMATTAGDETAILSW)dwParam1, dwParam2); + + case ACMDM_FORMAT_DETAILS: + return ADPCM_FormatDetails((PACMFORMATDETAILSW)dwParam1, dwParam2); + + case ACMDM_FORMAT_SUGGEST: + return ADPCM_FormatSuggest((PACMDRVFORMATSUGGEST)dwParam1); + + case ACMDM_STREAM_OPEN: + return ADPCM_StreamOpen((PACMDRVSTREAMINSTANCE)dwParam1); + + case ACMDM_STREAM_CLOSE: + return ADPCM_StreamClose((PACMDRVSTREAMINSTANCE)dwParam1); + + case ACMDM_STREAM_SIZE: + return ADPCM_StreamSize((PACMDRVSTREAMINSTANCE)dwParam1, (PACMDRVSTREAMSIZE)dwParam2); + + case ACMDM_STREAM_CONVERT: + return ADPCM_StreamConvert((PACMDRVSTREAMINSTANCE)dwParam1, (PACMDRVSTREAMHEADER)dwParam2); + + case ACMDM_HARDWARE_WAVE_CAPS_INPUT: + case ACMDM_HARDWARE_WAVE_CAPS_OUTPUT: + /* this converter is not a hardware driver */ + case ACMDM_FILTERTAG_DETAILS: + case ACMDM_FILTER_DETAILS: + /* this converter is not a filter */ + case ACMDM_STREAM_RESET: + /* only needed for asynchronous driver... we aren't, so just say it */ + return MMSYSERR_NOTSUPPORTED; + case ACMDM_STREAM_PREPARE: + case ACMDM_STREAM_UNPREPARE: + /* nothing special to do here... so don't do anything */ + return MMSYSERR_NOERROR; + + default: + return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2); + } + return 0; +} diff --git a/reactos/dll/msacm/pcmconverter.c b/reactos/dll/msacm/pcmconverter.c new file mode 100644 index 00000000000..3f335d005ce --- /dev/null +++ b/reactos/dll/msacm/pcmconverter.c @@ -0,0 +1,1223 @@ +/* -*- tab-width: 8; c-basic-offset: 4 -*- */ + +/* + * MSACM32 library + * + * Copyright 2000 Eric Pouech + * + * 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 + * + * FIXME / TODO list + * + most of the computation should be done in fixed point arithmetic + * instead of floating point (16 bits for integral part, and 16 bits + * for fractional part for example) + * + implement PCM_FormatSuggest function + * + get rid of hack for PCM_DriverProc (msacm32.dll shouldn't export + * a DriverProc, but this would require implementing a generic + * embedded driver handling scheme in msacm32.dll which isn't done yet + */ + +#include "config.h" + +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "mmsystem.h" +#include "mmreg.h" +#include "msacm.h" +#include "wingdi.h" +#include "winnls.h" +#include "winuser.h" + +#include "msacmdrv.h" +#include "wineacm.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msacm); + +/*********************************************************************** + * PCM_drvOpen + */ +static DWORD PCM_drvOpen(LPCSTR str, PACMDRVOPENDESCW adod) +{ + TRACE("(%p, %p)\n", str, adod); + + return (adod == NULL) || + (adod->fccType == ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC && + adod->fccComp == ACMDRIVERDETAILS_FCCCOMP_UNDEFINED); +} + +/*********************************************************************** + * PCM_drvClose + */ +static DWORD PCM_drvClose(DWORD dwDevID) +{ + TRACE("(%ld)\n", dwDevID); + + return 1; +} + +#define NUM_PCM_FORMATS (sizeof(PCM_Formats) / sizeof(PCM_Formats[0])) +#define NUM_OF(a,b) (((a)+(b)-1)/(b)) + +/* flags for fdwDriver */ +#define PCM_RESAMPLE 1 + +/* data used while converting */ +typedef struct tagAcmPcmData { + /* conversion routine, depending if rate conversion is required */ + union { + void (*cvtKeepRate)(const unsigned char*, int, unsigned char*); + void (*cvtChangeRate)(struct tagAcmPcmData*, const unsigned char*, + LPDWORD, unsigned char*, LPDWORD); + } cvt; + /* the following fields are used only with rate conversion) */ + DWORD srcPos; /* position in source stream */ + double dstPos; /* position in destination stream */ + double dstIncr; /* value to increment dst stream when src stream + is incremented by 1 */ + /* last source stream value read */ + union { + unsigned char b; /* 8 bit value */ + short s; /* 16 bit value */ + } last[2]; /* two channels max (stereo) */ +} AcmPcmData; + +/* table to list all supported formats... those are the basic ones. this + * also helps given a unique index to each of the supported formats + */ +static struct { + int nChannels; + int nBits; + int rate; +} PCM_Formats[] = { + {1, 8, 8000}, {2, 8, 8000}, {1, 16, 8000}, {2, 16, 8000}, + {1, 8, 11025}, {2, 8, 11025}, {1, 16, 11025}, {2, 16, 11025}, + {1, 8, 22050}, {2, 8, 22050}, {1, 16, 22050}, {2, 16, 22050}, + {1, 8, 44100}, {2, 8, 44100}, {1, 16, 44100}, {2, 16, 44100}, + {1, 8, 48000}, {2, 8, 48000}, {1, 16, 48000}, {2, 16, 48000}, + {1, 8, 96000}, {2, 8, 96000}, {1, 16, 96000}, {2, 16, 96000} +}; + +/*********************************************************************** + * PCM_GetFormatIndex + */ +static DWORD PCM_GetFormatIndex(LPWAVEFORMATEX wfx) +{ + int i; + TRACE("(%p)\n", wfx); + + for (i = 0; i < NUM_PCM_FORMATS; i++) { + if (wfx->nChannels == PCM_Formats[i].nChannels && + wfx->nSamplesPerSec == PCM_Formats[i].rate && + wfx->wBitsPerSample == PCM_Formats[i].nBits) + return i; + } + return 0xFFFFFFFF; +} + +/* PCM Conversions: + * + * parameters: + * + 8 bit unsigned vs 16 bit signed + * + mono vs stereo (1 or 2 channels) + * + sampling rate (8.0, 11.025, 22.05, 44.1 kHz are defined, but algo + * shall work in all cases) + * + * mono => stereo: copy the same sample on Left & Right channels + * stereo =) mono: use the average value of samples from Left & Right channels + * resampling; we lookup for each destination sample the two source adjacent + * samples were src <= dst < src+1 (dst is increased by a fractional + * value which is equivalent to the increment by one on src); then we + * use a linear interpolation between src and src+1 + */ + +/*********************************************************************** + * C816 + * + * Converts a 8 bit sample to a 16 bit one + */ +static inline short C816(unsigned char b) +{ + return (short)((b+(b << 8))-32768); +} + +/*********************************************************************** + * C168 + * + * Converts a 16 bit sample to a 8 bit one (data loss !!) + */ +static inline unsigned char C168(short s) +{ + return HIBYTE(s) ^ (unsigned char)0x80; +} + +/*********************************************************************** + * R16 + * + * Read a 16 bit sample (correctly handles endianess) + */ +static inline short R16(const unsigned char* src) +{ + return (short)((unsigned short)src[0] | ((unsigned short)src[1] << 8)); +} + +/*********************************************************************** + * W16 + * + * Write a 16 bit sample (correctly handles endianess) + */ +static inline void W16(unsigned char* dst, short s) +{ + dst[0] = LOBYTE(s); + dst[1] = HIBYTE(s); +} + +/*********************************************************************** + * M16 + * + * Convert the (l,r) 16 bit stereo sample into a 16 bit mono + * (takes the mid-point of the two values) + */ +static inline short M16(short l, short r) +{ + return (l + r) / 2; +} + +/*********************************************************************** + * M8 + * + * Convert the (l,r) 8 bit stereo sample into a 8 bit mono + * (takes the mid-point of the two values) + */ +static inline unsigned char M8(unsigned char a, unsigned char b) +{ + return (unsigned char)((a + b) / 2); +} + +/* the conversion routines without rate conversion are labelled cvtK + * where : + * is the (M)ono/(S)tereo configuration of input channel + * is the (M)ono/(S)tereo configuration of output channel + * is the number of bits of input channel (8 or 16) + * is the number of bits of output channel (8 or 16) + * + * in the parameters, ns is always the number of samples, so the size of input + * buffer (resp output buffer) is ns * ( == 'Mono' ? 1:2) * ( == 8 ? 1:2) + */ + +static void cvtMM88K(const unsigned char* src, int ns, unsigned char* dst) +{ + TRACE("(%p, %d, %p)\n", src, ns, dst); + memcpy(dst, src, ns); +} + +static void cvtSS88K(const unsigned char* src, int ns, unsigned char* dst) +{ + TRACE("(%p, %d, %p)\n", src, ns, dst); + memcpy(dst, src, ns * 2); +} + +static void cvtMM1616K(const unsigned char* src, int ns, unsigned char* dst) +{ + TRACE("(%p, %d, %p)\n", src, ns, dst); + memcpy(dst, src, ns * 2); +} + +static void cvtSS1616K(const unsigned char* src, int ns, unsigned char* dst) +{ + TRACE("(%p, %d, %p)\n", src, ns, dst); + memcpy(dst, src, ns * 4); +} + +static void cvtMS88K(const unsigned char* src, int ns, unsigned char* dst) +{ + TRACE("(%p, %d, %p)\n", src, ns, dst); + + while (ns--) { + *dst++ = *src; + *dst++ = *src++; + } +} + +static void cvtMS816K(const unsigned char* src, int ns, unsigned char* dst) +{ + short v; + TRACE("(%p, %d, %p)\n", src, ns, dst); + + while (ns--) { + v = C816(*src++); + W16(dst, v); dst += 2; + W16(dst, v); dst += 2; + } +} + +static void cvtMS168K(const unsigned char* src, int ns, unsigned char* dst) +{ + unsigned char v; + TRACE("(%p, %d, %p)\n", src, ns, dst); + + while (ns--) { + v = C168(R16(src)); src += 2; + *dst++ = v; + *dst++ = v; + } +} + +static void cvtMS1616K(const unsigned char* src, int ns, unsigned char* dst) +{ + short v; + TRACE("(%p, %d, %p)\n", src, ns, dst); + + while (ns--) { + v = R16(src); src += 2; + W16(dst, v); dst += 2; + W16(dst, v); dst += 2; + } +} + +static void cvtSM88K(const unsigned char* src, int ns, unsigned char* dst) +{ + TRACE("(%p, %d, %p)\n", src, ns, dst); + + while (ns--) { + *dst++ = M8(src[0], src[1]); + src += 2; + } +} + +static void cvtSM816K(const unsigned char* src, int ns, unsigned char* dst) +{ + short v; + TRACE("(%p, %d, %p)\n", src, ns, dst); + + while (ns--) { + v = M16(C816(src[0]), C816(src[1])); + src += 2; + W16(dst, v); dst += 2; + } +} + +static void cvtSM168K(const unsigned char* src, int ns, unsigned char* dst) +{ + TRACE("(%p, %d, %p)\n", src, ns, dst); + + while (ns--) { + *dst++ = C168(M16(R16(src), R16(src + 2))); + src += 4; + } +} + +static void cvtSM1616K(const unsigned char* src, int ns, unsigned char* dst) +{ + TRACE("(%p, %d, %p)\n", src, ns, dst); + + while (ns--) { + W16(dst, M16(R16(src),R16(src+2))); dst += 2; + src += 4; + } +} + +static void cvtMM816K(const unsigned char* src, int ns, unsigned char* dst) +{ + TRACE("(%p, %d, %p)\n", src, ns, dst); + + while (ns--) { + W16(dst, C816(*src++)); dst += 2; + } +} + +static void cvtSS816K(const unsigned char* src, int ns, unsigned char* dst) +{ + TRACE("(%p, %d, %p)\n", src, ns, dst); + + while (ns--) { + W16(dst, C816(*src++)); dst += 2; + W16(dst, C816(*src++)); dst += 2; + } +} + +static void cvtMM168K(const unsigned char* src, int ns, unsigned char* dst) +{ + TRACE("(%p, %d, %p)\n", src, ns, dst); + + while (ns--) { + *dst++ = C168(R16(src)); src += 2; + } +} + +static void cvtSS168K(const unsigned char* src, int ns, unsigned char* dst) +{ + TRACE("(%p, %d, %p)\n", src, ns, dst); + + while (ns--) { + *dst++ = C168(R16(src)); src += 2; + *dst++ = C168(R16(src)); src += 2; + } +} + +static void (*PCM_ConvertKeepRate[16])(const unsigned char*, int, unsigned char*) = { + cvtSS88K, cvtSM88K, cvtMS88K, cvtMM88K, + cvtSS816K, cvtSM816K, cvtMS816K, cvtMM816K, + cvtSS168K, cvtSM168K, cvtMS168K, cvtMM168K, + cvtSS1616K, cvtSM1616K, cvtMS1616K, cvtMM1616K, +}; + +/*********************************************************************** + * I + * + * Interpolate the value at r (r in ]0, 1]) between the two points v1 and v2 + * Linear interpolation is used + */ +static inline double I(double v1, double v2, double r) +{ + if (0.0 >= r || r > 1.0) FIXME("r!! %f\n", r); + return (1.0 - r) * v1 + r * v2; +} + +static void cvtSS88C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc, + unsigned char* dst, LPDWORD ndst) +{ + double r; + TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst); + + while (*nsrc != 0 && *ndst != 0) { + while ((r = (double)apd->srcPos - apd->dstPos) <= 0) { + if (*nsrc == 0) return; + apd->last[0].b = *src++; + apd->last[1].b = *src++; + apd->srcPos++; + (*nsrc)--; + } + /* now do the interpolation */ + *dst++ = I(apd->last[0].b, src[0], r); + *dst++ = I(apd->last[1].b, src[1], r); + apd->dstPos += apd->dstIncr; + (*ndst)--; + } +} + +/* the conversion routines with rate conversion are labelled cvtC + * where : + * is the (M)ono/(S)tereo configuration of input channel + * is the (M)ono/(S)tereo configuration of output channel + * is the number of bits of input channel (8 or 16) + * is the number of bits of output channel (8 or 16) + * + */ +static void cvtSM88C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc, + unsigned char* dst, LPDWORD ndst) +{ + double r; + TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst); + + while (*nsrc != 0 && *ndst != 0) { + while ((r = (double)apd->srcPos - apd->dstPos) <= 0) { + if (*nsrc == 0) return; + apd->last[0].b = *src++; + apd->last[1].b = *src++; + apd->srcPos++; + (*nsrc)--; + } + /* now do the interpolation */ + if (*nsrc) /* don't go off end of data */ + *dst++ = I(M8(apd->last[0].b, apd->last[1].b), M8(src[0], src[1]), r); + else + *dst++ = M8(apd->last[0].b, apd->last[1].b); + apd->dstPos += apd->dstIncr; + (*ndst)--; + } +} + +static void cvtMS88C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc, + unsigned char* dst, LPDWORD ndst) +{ + double r; + TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst); + + while (*nsrc != 0 && *ndst != 0) { + while ((r = (double)apd->srcPos - apd->dstPos) <= 0) { + if (*nsrc == 0) return; + apd->last[0].b = *src++; + apd->srcPos++; + (*nsrc)--; + } + /* now do the interpolation */ + if (*nsrc) /* don't go off end of data */ + dst[0] = dst[1] = I(apd->last[0].b, src[0], r); + else + dst[0] = dst[1] = apd->last[0].b; + dst += 2; + apd->dstPos += apd->dstIncr; + (*ndst)--; + } +} + +static void cvtMM88C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc, + unsigned char* dst, LPDWORD ndst) +{ + double r; + TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst); + + while (*nsrc != 0 && *ndst != 0) { + while ((r = (double)apd->srcPos - apd->dstPos) <= 0) { + if (*nsrc == 0) return; + apd->last[0].b = *src++; + apd->srcPos++; + (*nsrc)--; + } + /* now do the interpolation */ + if (*nsrc) /* don't go off end of data */ + *dst++ = I(apd->last[0].b, src[0], r); + else + *dst++ = apd->last[0].b; + apd->dstPos += apd->dstIncr; + (*ndst)--; + } +} + +static void cvtSS816C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc, + unsigned char* dst, LPDWORD ndst) +{ + double r; + TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst); + + while (*nsrc != 0 && *ndst != 0) { + while ((r = (double)apd->srcPos - apd->dstPos) <= 0) { + if (*nsrc == 0) return; + apd->last[0].b = *src++; + apd->last[1].b = *src++; + apd->srcPos++; + (*nsrc)--; + } + /* now do the interpolation */ + if (*nsrc) /* don't go off end of data */ + W16(dst, I(C816(apd->last[0].b), C816(src[0]), r)); + else + W16(dst, C816(apd->last[0].b)); + dst += 2; + if (*nsrc) /* don't go off end of data */ + W16(dst, I(C816(apd->last[1].b), C816(src[1]), r)); + else + W16(dst, C816(apd->last[1].b)); + dst += 2; + apd->dstPos += apd->dstIncr; + (*ndst)--; + } +} + +static void cvtSM816C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc, + unsigned char* dst, LPDWORD ndst) +{ + double r; + TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst); + + while (*nsrc != 0 && *ndst != 0) { + while ((r = (double)apd->srcPos - apd->dstPos) <= 0) { + if (*nsrc == 0) return; + apd->last[0].b = *src++; + apd->last[1].b = *src++; + apd->srcPos++; + (*nsrc)--; + } + /* now do the interpolation */ + if (*nsrc) /* don't go off end of data */ + W16(dst, I(M16(C816(apd->last[0].b), C816(apd->last[1].b)), + M16(C816(src[0]), C816(src[1])), r)); + else + W16(dst, M16(C816(apd->last[0].b), C816(apd->last[1].b))); + dst += 2; + apd->dstPos += apd->dstIncr; + (*ndst)--; + } +} + +static void cvtMS816C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc, + unsigned char* dst, LPDWORD ndst) +{ + double r; + short v; + TRACE("(%p, %p, %p->(%ld), %p, %p->(%ld))\n", apd, src, nsrc, *nsrc, dst, ndst, *ndst); + + while (*nsrc != 0 && *ndst != 0) { + while ((r = (double)apd->srcPos - apd->dstPos) <= 0) { + if (*nsrc == 0) return; + apd->last[0].b = *src++; + apd->srcPos++; + (*nsrc)--; + } + /* now do the interpolation */ + if (*nsrc) /* don't go off end of data */ + v = I(C816(apd->last[0].b), C816(src[0]), r); + else + v = C816(apd->last[0].b); + W16(dst, v); dst += 2; + W16(dst, v); dst += 2; + apd->dstPos += apd->dstIncr; + (*ndst)--; + } +} + +static void cvtMM816C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc, + unsigned char* dst, LPDWORD ndst) +{ + double r; + TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst); + + while (*nsrc != 0 && *ndst != 0) { + while ((r = (double)apd->srcPos - apd->dstPos) <= 0) { + if (*nsrc == 0) return; + apd->last[0].b = *src++; + apd->srcPos++; + (*nsrc)--; + } + /* now do the interpolation */ + if (*nsrc) /* don't go off end of data */ + W16(dst, I(C816(apd->last[0].b), C816(src[0]), r)); + else + W16(dst, C816(apd->last[0].b)); + dst += 2; + apd->dstPos += apd->dstIncr; + (*ndst)--; + } +} + +static void cvtSS168C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc, + unsigned char* dst, LPDWORD ndst) +{ + double r; + TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst); + + while (*nsrc != 0 && *ndst != 0) { + while ((r = (double)apd->srcPos - apd->dstPos) <= 0) { + if (*nsrc == 0) return; + apd->last[0].s = R16(src); src += 2; + apd->last[1].s = R16(src); src += 2; + apd->srcPos++; + (*nsrc)--; + } + /* now do the interpolation */ + if (*nsrc) { /* don't go off end of data */ + *dst++ = C168(I(apd->last[0].s, R16(src) , r)); + *dst++ = C168(I(apd->last[1].s, R16(src+2), r)); + } else { + *dst++ = C168(apd->last[0].s); + *dst++ = C168(apd->last[1].s); + } + apd->dstPos += apd->dstIncr; + (*ndst)--; + } +} + +static void cvtSM168C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc, + unsigned char* dst, LPDWORD ndst) +{ + double r; + TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst); + + while (*nsrc != 0 && *ndst != 0) { + while ((r = (double)apd->srcPos - apd->dstPos) <= 0) { + if (*nsrc == 0) return; + apd->last[0].s = R16(src); src += 2; + apd->last[1].s = R16(src); src += 2; + apd->srcPos++; + (*nsrc)--; + } + /* now do the interpolation */ + if (*nsrc) /* don't go off end of data */ + *dst++ = C168(I(M16(apd->last[0].s, apd->last[1].s), + M16(R16(src), R16(src + 2)), r)); + else + *dst++ = C168(M16(apd->last[0].s, apd->last[1].s)); + apd->dstPos += apd->dstIncr; + (*ndst)--; + } +} + + +static void cvtMS168C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc, + unsigned char* dst, LPDWORD ndst) +{ + double r; + TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst); + + while (*nsrc != 0 && *ndst != 0) { + while ((r = (double)apd->srcPos - apd->dstPos) <= 0) { + if (*nsrc == 0) return; + apd->last[0].s = R16(src); src += 2; + apd->srcPos++; + (*nsrc)--; + } + /* now do the interpolation */ + if (*nsrc) /* don't go off end of data */ + dst[0] = dst[1] = C168(I(apd->last[0].s, R16(src), r)); + else + dst[0] = dst[1] = C168(apd->last[0].s); + dst += 2; + apd->dstPos += apd->dstIncr; + (*ndst)--; + } +} + + +static void cvtMM168C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc, + unsigned char* dst, LPDWORD ndst) +{ + double r; + TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst); + + while (*nsrc != 0 && *ndst != 0) { + while ((r = (double)apd->srcPos - apd->dstPos) <= 0) { + if (*nsrc == 0) return; + apd->last[0].s = R16(src); src += 2; + apd->srcPos++; + (*nsrc)--; + } + /* now do the interpolation */ + if (*nsrc) /* don't go off end of data */ + *dst++ = C168(I(apd->last[0].s, R16(src), r)); + else + *dst++ = C168(apd->last[0].s); + apd->dstPos += apd->dstIncr; + (*ndst)--; + } +} + +static void cvtSS1616C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc, + unsigned char* dst, LPDWORD ndst) +{ + double r; + TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst); + + while (*nsrc != 0 && *ndst != 0) { + while ((r = (double)apd->srcPos - apd->dstPos) <= 0) { + if (*nsrc == 0) return; + apd->last[0].s = R16(src); src += 2; + apd->last[1].s = R16(src); src += 2; + apd->srcPos++; + (*nsrc)--; + } + /* now do the interpolation */ + if (*nsrc) /* don't go off end of data */ + W16(dst, I(apd->last[0].s, R16(src), r)); + else + W16(dst, apd->last[0].s); + dst += 2; + if (*nsrc) /* don't go off end of data */ + W16(dst, I(apd->last[1].s, R16(src+2), r)); + else + W16(dst, apd->last[1].s); + dst += 2; + apd->dstPos += apd->dstIncr; + (*ndst)--; + } +} + +static void cvtSM1616C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc, + unsigned char* dst, LPDWORD ndst) +{ + double r; + TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst); + + while (*nsrc != 0 && *ndst != 0) { + while ((r = (double)apd->srcPos - apd->dstPos) <= 0) { + if (*nsrc == 0) return; + apd->last[0].s = R16(src); src += 2; + apd->last[1].s = R16(src); src += 2; + apd->srcPos++; + (*nsrc)--; + } + /* now do the interpolation */ + if (*nsrc) /* don't go off end of data */ + W16(dst, I(M16(apd->last[0].s, apd->last[1].s), + M16(R16(src), R16(src+2)), r)); + else + W16(dst, M16(apd->last[0].s, apd->last[1].s)); + dst += 2; + apd->dstPos += apd->dstIncr; + (*ndst)--; + } +} + +static void cvtMS1616C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc, + unsigned char* dst, LPDWORD ndst) +{ + double r; + short v; + TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst); + + while (*nsrc != 0 && *ndst != 0) { + while ((r = (double)apd->srcPos - apd->dstPos) <= 0) { + if (*nsrc == 0) return; + apd->last[0].s = R16(src); src += 2; + apd->srcPos++; + (*nsrc)--; + } + /* now do the interpolation */ + if (*nsrc) /* don't go off end of data */ + v = I(apd->last[0].s, R16(src), r); + else + v = apd->last[0].s; + W16(dst, v); dst += 2; + W16(dst, v); dst += 2; + apd->dstPos += apd->dstIncr; + (*ndst)--; + } +} + +static void cvtMM1616C(AcmPcmData* apd, const unsigned char* src, LPDWORD nsrc, + unsigned char* dst, LPDWORD ndst) +{ + double r; + TRACE("(%p, %p, %p, %p, %p)\n", apd, src, nsrc, dst, ndst); + + while (*nsrc != 0 && *ndst != 0) { + while ((r = (double)apd->srcPos - apd->dstPos) <= 0) { + if (*nsrc == 0) return; + apd->last[0].s = R16(src); src += 2; + apd->srcPos++; + (*nsrc)--; + } + /* now do the interpolation */ + if (*nsrc) /* don't go off end of data */ + W16(dst, I(apd->last[0].s, R16(src), r)); + else + W16(dst, apd->last[0].s); + dst += 2; + apd->dstPos += apd->dstIncr; + (*ndst)--; + } +} + +static void (*PCM_ConvertChangeRate[16])(AcmPcmData* apd, + const unsigned char* src, LPDWORD nsrc, + unsigned char* dst, LPDWORD ndst) = { + cvtSS88C, cvtSM88C, cvtMS88C, cvtMM88C, + cvtSS816C, cvtSM816C, cvtMS816C, cvtMM816C, + cvtSS168C, cvtSM168C, cvtMS168C, cvtMM168C, + cvtSS1616C, cvtSM1616C, cvtMS1616C, cvtMM1616C, +}; + +/*********************************************************************** + * PCM_DriverDetails + * + */ +static LRESULT PCM_DriverDetails(PACMDRIVERDETAILSW add) +{ + TRACE("(%p)\n", add); + + add->fccType = ACMDRIVERDETAILS_FCCTYPE_AUDIOCODEC; + add->fccComp = ACMDRIVERDETAILS_FCCCOMP_UNDEFINED; + add->wMid = 0xFF; + add->wPid = 0x00; + add->vdwACM = 0x01000000; + add->vdwDriver = 0x01000000; + add->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CONVERTER; + add->cFormatTags = 1; + add->cFilterTags = 0; + add->hicon = NULL; + MultiByteToWideChar( CP_ACP, 0, "WINE-PCM", -1, + add->szShortName, sizeof(add->szShortName)/sizeof(WCHAR) ); + MultiByteToWideChar( CP_ACP, 0, "Wine PCM converter", -1, + add->szLongName, sizeof(add->szLongName)/sizeof(WCHAR) ); + MultiByteToWideChar( CP_ACP, 0, "Brought to you by the Wine team...", -1, + add->szCopyright, sizeof(add->szCopyright)/sizeof(WCHAR) ); + MultiByteToWideChar( CP_ACP, 0, "Refer to LICENSE file", -1, + add->szLicensing, sizeof(add->szLicensing)/sizeof(WCHAR) ); + add->szFeatures[0] = 0; + + return MMSYSERR_NOERROR; +} + +/*********************************************************************** + * PCM_FormatTagDetails + * + */ +static LRESULT PCM_FormatTagDetails(PACMFORMATTAGDETAILSW aftd, DWORD dwQuery) +{ + TRACE("(%p, %08lx)\n", aftd, dwQuery); + + switch (dwQuery) { + case ACM_FORMATTAGDETAILSF_INDEX: + if (aftd->dwFormatTagIndex != 0) { + WARN("not possible\n"); + return ACMERR_NOTPOSSIBLE; + } + break; + case ACM_FORMATTAGDETAILSF_FORMATTAG: + if (aftd->dwFormatTag != WAVE_FORMAT_PCM) { + WARN("not possible\n"); + return ACMERR_NOTPOSSIBLE; + } + break; + case ACM_FORMATTAGDETAILSF_LARGESTSIZE: + if (aftd->dwFormatTag != WAVE_FORMAT_UNKNOWN && + aftd->dwFormatTag != WAVE_FORMAT_PCM) { + WARN("not possible\n"); + return ACMERR_NOTPOSSIBLE; + } + break; + default: + WARN("Unsupported query %08lx\n", dwQuery); + return MMSYSERR_NOTSUPPORTED; + } + + aftd->dwFormatTagIndex = 0; + aftd->dwFormatTag = WAVE_FORMAT_PCM; + aftd->cbFormatSize = sizeof(PCMWAVEFORMAT); + aftd->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CONVERTER; + aftd->cStandardFormats = NUM_PCM_FORMATS; + aftd->szFormatTag[0] = 0; + + return MMSYSERR_NOERROR; +} + +/*********************************************************************** + * PCM_FormatDetails + * + */ +static LRESULT PCM_FormatDetails(PACMFORMATDETAILSW afd, DWORD dwQuery) +{ + TRACE("(%p, %08lx)\n", afd, dwQuery); + + switch (dwQuery) { + case ACM_FORMATDETAILSF_FORMAT: + if (PCM_GetFormatIndex(afd->pwfx) == 0xFFFFFFFF) { + return ACMERR_NOTPOSSIBLE; + WARN("not possible\n"); + } + break; + case ACM_FORMATDETAILSF_INDEX: + assert(afd->dwFormatIndex < NUM_PCM_FORMATS); + afd->pwfx->wFormatTag = WAVE_FORMAT_PCM; + afd->pwfx->nChannels = PCM_Formats[afd->dwFormatIndex].nChannels; + afd->pwfx->nSamplesPerSec = PCM_Formats[afd->dwFormatIndex].rate; + afd->pwfx->wBitsPerSample = PCM_Formats[afd->dwFormatIndex].nBits; + /* native MSACM uses a PCMWAVEFORMAT structure, so cbSize is not + * accessible afd->pwfx->cbSize = 0; + */ + afd->pwfx->nBlockAlign = + (afd->pwfx->nChannels * afd->pwfx->wBitsPerSample) / 8; + afd->pwfx->nAvgBytesPerSec = + afd->pwfx->nSamplesPerSec * afd->pwfx->nBlockAlign; + break; + default: + WARN("Unsupported query %08lx\n", dwQuery); + return MMSYSERR_NOTSUPPORTED; + } + + afd->dwFormatTag = WAVE_FORMAT_PCM; + afd->fdwSupport = ACMDRIVERDETAILS_SUPPORTF_CONVERTER; + afd->szFormat[0] = 0; /* let MSACM format this for us... */ + afd->cbwfx = sizeof(PCMWAVEFORMAT); + + return MMSYSERR_NOERROR; +} + +/*********************************************************************** + * PCM_FormatSuggest + * + */ +static LRESULT PCM_FormatSuggest(PACMDRVFORMATSUGGEST adfs) +{ + TRACE("(%p)\n", adfs); + + /* some tests ... */ + if (adfs->cbwfxSrc < sizeof(PCMWAVEFORMAT) || + adfs->cbwfxDst < sizeof(PCMWAVEFORMAT) || + PCM_GetFormatIndex(adfs->pwfxSrc) == 0xFFFFFFFF) { + WARN("not possible\n"); + return ACMERR_NOTPOSSIBLE; + } + + /* is no suggestion for destination, then copy source value */ + if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_NCHANNELS)) { + adfs->pwfxDst->nChannels = adfs->pwfxSrc->nChannels; + } + if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_NSAMPLESPERSEC)) { + adfs->pwfxDst->nSamplesPerSec = adfs->pwfxSrc->nSamplesPerSec; + } + if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_WBITSPERSAMPLE)) { + adfs->pwfxDst->wBitsPerSample = adfs->pwfxSrc->wBitsPerSample; + } + if (!(adfs->fdwSuggest & ACM_FORMATSUGGESTF_WFORMATTAG)) { + if (adfs->pwfxSrc->wFormatTag != WAVE_FORMAT_PCM) { + WARN("not possible\n"); + return ACMERR_NOTPOSSIBLE; + } + adfs->pwfxDst->wFormatTag = adfs->pwfxSrc->wFormatTag; + } + /* check if result is ok */ + if (PCM_GetFormatIndex(adfs->pwfxDst) == 0xFFFFFFFF) { + WARN("not possible\n"); + return ACMERR_NOTPOSSIBLE; + } + + /* recompute other values */ + adfs->pwfxDst->nBlockAlign = (adfs->pwfxDst->nChannels * adfs->pwfxDst->wBitsPerSample) / 8; + adfs->pwfxDst->nAvgBytesPerSec = adfs->pwfxDst->nSamplesPerSec * adfs->pwfxDst->nBlockAlign; + + return MMSYSERR_NOERROR; +} + +/*********************************************************************** + * PCM_Reset + * + */ +static void PCM_Reset(AcmPcmData* apd, int srcNumBits) +{ + TRACE("(%p, %d)\n", apd, srcNumBits); + + apd->srcPos = 0; + apd->dstPos = 0; + /* initialize with neutral value */ + if (srcNumBits == 16) { + apd->last[0].s = 0; + apd->last[1].s = 0; + } else { + apd->last[0].b = (BYTE)0x80; + apd->last[1].b = (BYTE)0x80; + } +} + +/*********************************************************************** + * PCM_StreamOpen + * + */ +static LRESULT PCM_StreamOpen(PACMDRVSTREAMINSTANCE adsi) +{ + AcmPcmData* apd; + int idx = 0; + + TRACE("(%p)\n", adsi); + + assert(!(adsi->fdwOpen & ACM_STREAMOPENF_ASYNC)); + + if (PCM_GetFormatIndex(adsi->pwfxSrc) == 0xFFFFFFFF || + PCM_GetFormatIndex(adsi->pwfxDst) == 0xFFFFFFFF) { + WARN("not possible\n"); + return ACMERR_NOTPOSSIBLE; + } + + apd = HeapAlloc(GetProcessHeap(), 0, sizeof(AcmPcmData)); + if (apd == 0) { + WARN("no memory\n"); + return MMSYSERR_NOMEM; + } + + adsi->dwDriver = (DWORD)apd; + adsi->fdwDriver = 0; + + if (adsi->pwfxSrc->wBitsPerSample == 16) idx += 8; + if (adsi->pwfxDst->wBitsPerSample == 16) idx += 4; + if (adsi->pwfxSrc->nChannels == 1) idx += 2; + if (adsi->pwfxDst->nChannels == 1) idx += 1; + + if (adsi->pwfxSrc->nSamplesPerSec == adsi->pwfxDst->nSamplesPerSec) { + apd->cvt.cvtKeepRate = PCM_ConvertKeepRate[idx]; + } else { + adsi->fdwDriver |= PCM_RESAMPLE; + apd->dstIncr = (double)(adsi->pwfxSrc->nSamplesPerSec) / + (double)(adsi->pwfxDst->nSamplesPerSec); + PCM_Reset(apd, adsi->pwfxSrc->wBitsPerSample); + apd->cvt.cvtChangeRate = PCM_ConvertChangeRate[idx]; + } + + return MMSYSERR_NOERROR; +} + +/*********************************************************************** + * PCM_StreamClose + * + */ +static LRESULT PCM_StreamClose(PACMDRVSTREAMINSTANCE adsi) +{ + TRACE("(%p)\n", adsi); + + HeapFree(GetProcessHeap(), 0, (void*)adsi->dwDriver); + return MMSYSERR_NOERROR; +} + +/*********************************************************************** + * PCM_round + * + */ +static inline DWORD PCM_round(DWORD a, DWORD b, DWORD c) +{ + assert(c); + /* to be sure, always return an entire number of c... */ + return ((double)a * (double)b + (double)c - 1) / (double)c; +} + +/*********************************************************************** + * PCM_StreamSize + * + */ +static LRESULT PCM_StreamSize(PACMDRVSTREAMINSTANCE adsi, PACMDRVSTREAMSIZE adss) +{ + DWORD srcMask = ~(adsi->pwfxSrc->nBlockAlign - 1); + DWORD dstMask = ~(adsi->pwfxDst->nBlockAlign - 1); + + TRACE("(%p, %p)\n", adsi, adss); + + switch (adss->fdwSize) { + case ACM_STREAMSIZEF_DESTINATION: + /* cbDstLength => cbSrcLength */ + adss->cbSrcLength = PCM_round(adss->cbDstLength & dstMask, + adsi->pwfxSrc->nAvgBytesPerSec, + adsi->pwfxDst->nAvgBytesPerSec) & srcMask; + break; + case ACM_STREAMSIZEF_SOURCE: + /* cbSrcLength => cbDstLength */ + adss->cbDstLength = PCM_round(adss->cbSrcLength & srcMask, + adsi->pwfxDst->nAvgBytesPerSec, + adsi->pwfxSrc->nAvgBytesPerSec) & dstMask; + break; + default: + WARN("Unsupported query %08lx\n", adss->fdwSize); + return MMSYSERR_NOTSUPPORTED; + } + return MMSYSERR_NOERROR; +} + +/*********************************************************************** + * PCM_StreamConvert + * + */ +static LRESULT PCM_StreamConvert(PACMDRVSTREAMINSTANCE adsi, PACMDRVSTREAMHEADER adsh) +{ + AcmPcmData* apd = (AcmPcmData*)adsi->dwDriver; + DWORD nsrc = NUM_OF(adsh->cbSrcLength, adsi->pwfxSrc->nBlockAlign); + DWORD ndst = NUM_OF(adsh->cbDstLength, adsi->pwfxDst->nBlockAlign); + + TRACE("(%p, %p)\n", adsi, adsh); + + TRACE("nsrc=%ld,adsh->cbSrcLength=%ld\n", nsrc, adsh->cbSrcLength); + TRACE("ndst=%ld,adsh->cbDstLength=%ld\n", ndst, adsh->cbDstLength); + TRACE("src [wFormatTag=%u, nChannels=%u, nSamplesPerSec=%lu, nAvgBytesPerSec=%lu, nBlockAlign=%u, wBitsPerSample=%u, cbSize=%u]\n", + adsi->pwfxSrc->wFormatTag, adsi->pwfxSrc->nChannels, adsi->pwfxSrc->nSamplesPerSec, adsi->pwfxSrc->nAvgBytesPerSec, + adsi->pwfxSrc->nBlockAlign, adsi->pwfxSrc->wBitsPerSample, adsi->pwfxSrc->cbSize); + TRACE("dst [wFormatTag=%u, nChannels=%u, nSamplesPerSec=%lu, nAvgBytesPerSec=%lu, nBlockAlign=%u, wBitsPerSample=%u, cbSize=%u]\n", + adsi->pwfxDst->wFormatTag, adsi->pwfxDst->nChannels, adsi->pwfxDst->nSamplesPerSec, adsi->pwfxDst->nAvgBytesPerSec, + adsi->pwfxDst->nBlockAlign, adsi->pwfxDst->wBitsPerSample, adsi->pwfxDst->cbSize); + + if (adsh->fdwConvert & + ~(ACM_STREAMCONVERTF_BLOCKALIGN| + ACM_STREAMCONVERTF_END| + ACM_STREAMCONVERTF_START)) { + FIXME("Unsupported fdwConvert (%08lx), ignoring it\n", adsh->fdwConvert); + } + /* ACM_STREAMCONVERTF_BLOCKALIGN + * currently all conversions are block aligned, so do nothing for this flag + * ACM_STREAMCONVERTF_END + * no pending data, so do nothing for this flag + */ + if ((adsh->fdwConvert & ACM_STREAMCONVERTF_START) && + (adsi->fdwDriver & PCM_RESAMPLE)) { + PCM_Reset(apd, adsi->pwfxSrc->wBitsPerSample); + } + + /* do the job */ + if (adsi->fdwDriver & PCM_RESAMPLE) { + DWORD nsrc2 = nsrc; + DWORD ndst2 = ndst; + + apd->cvt.cvtChangeRate(apd, adsh->pbSrc, &nsrc2, adsh->pbDst, &ndst2); + nsrc -= nsrc2; + ndst -= ndst2; + } else { + if (nsrc < ndst) ndst = nsrc; else nsrc = ndst; + + /* nsrc is now equal to ndst */ + apd->cvt.cvtKeepRate(adsh->pbSrc, nsrc, adsh->pbDst); + } + + adsh->cbSrcLengthUsed = nsrc * adsi->pwfxSrc->nBlockAlign; + adsh->cbDstLengthUsed = ndst * adsi->pwfxDst->nBlockAlign; + + return MMSYSERR_NOERROR; +} + +/************************************************************************** + * DriverProc (MSACM32.@) + */ +LRESULT CALLBACK PCM_DriverProc(DWORD dwDevID, HDRVR hDriv, UINT wMsg, + LPARAM dwParam1, LPARAM dwParam2) +{ + TRACE("(%08lx %08lx %u %08lx %08lx);\n", + dwDevID, (DWORD)hDriv, wMsg, dwParam1, dwParam2); + + switch (wMsg) { + case DRV_LOAD: return 1; + case DRV_FREE: return 1; + case DRV_OPEN: return PCM_drvOpen((LPSTR)dwParam1, (PACMDRVOPENDESCW)dwParam2); + case DRV_CLOSE: return PCM_drvClose(dwDevID); + case DRV_ENABLE: return 1; + case DRV_DISABLE: return 1; + case DRV_QUERYCONFIGURE: return 1; + case DRV_CONFIGURE: MessageBoxA(0, "MSACM PCM filter !", "Wine Driver", MB_OK); return 1; + case DRV_INSTALL: return DRVCNF_RESTART; + case DRV_REMOVE: return DRVCNF_RESTART; + + case ACMDM_DRIVER_NOTIFY: + /* no caching from other ACM drivers is done so far */ + return MMSYSERR_NOERROR; + + case ACMDM_DRIVER_DETAILS: + return PCM_DriverDetails((PACMDRIVERDETAILSW)dwParam1); + + case ACMDM_FORMATTAG_DETAILS: + return PCM_FormatTagDetails((PACMFORMATTAGDETAILSW)dwParam1, dwParam2); + + case ACMDM_FORMAT_DETAILS: + return PCM_FormatDetails((PACMFORMATDETAILSW)dwParam1, dwParam2); + + case ACMDM_FORMAT_SUGGEST: + return PCM_FormatSuggest((PACMDRVFORMATSUGGEST)dwParam1); + + case ACMDM_STREAM_OPEN: + return PCM_StreamOpen((PACMDRVSTREAMINSTANCE)dwParam1); + + case ACMDM_STREAM_CLOSE: + return PCM_StreamClose((PACMDRVSTREAMINSTANCE)dwParam1); + + case ACMDM_STREAM_SIZE: + return PCM_StreamSize((PACMDRVSTREAMINSTANCE)dwParam1, (PACMDRVSTREAMSIZE)dwParam2); + + case ACMDM_STREAM_CONVERT: + return PCM_StreamConvert((PACMDRVSTREAMINSTANCE)dwParam1, (PACMDRVSTREAMHEADER)dwParam2); + + case ACMDM_HARDWARE_WAVE_CAPS_INPUT: + case ACMDM_HARDWARE_WAVE_CAPS_OUTPUT: + /* this converter is not a hardware driver */ + case ACMDM_FILTERTAG_DETAILS: + case ACMDM_FILTER_DETAILS: + /* this converter is not a filter */ + case ACMDM_STREAM_RESET: + /* only needed for asynchronous driver... we aren't, so just say it */ + case ACMDM_STREAM_PREPARE: + case ACMDM_STREAM_UNPREPARE: + /* nothing special to do here... so don't do anything */ + return MMSYSERR_NOTSUPPORTED; + + default: + return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2); + } + return 0; +} diff --git a/reactos/dll/msacm/stream.c b/reactos/dll/msacm/stream.c new file mode 100644 index 00000000000..5c427ec62d4 --- /dev/null +++ b/reactos/dll/msacm/stream.c @@ -0,0 +1,478 @@ +/* -*- tab-width: 8; c-basic-offset: 4 -*- */ + +/* + * MSACM32 library + * + * Copyright 1998 Patrik Stridvall + * 1999 Eric Pouech + * + * 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 + */ + +/* TODO + * + asynchronous conversion is not implemented + * + callback/notification + * * acmStreamMessage + * + properly close ACM streams + */ + +#include +#include +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "mmsystem.h" +#include "mmreg.h" +#include "msacm.h" +#include "msacmdrv.h" +#include "wineacm.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msacm); + +static PWINE_ACMSTREAM ACM_GetStream(HACMSTREAM has) +{ + TRACE("(%p)\n", has); + + return (PWINE_ACMSTREAM)has; +} + +/*********************************************************************** + * acmStreamClose (MSACM32.@) + */ +MMRESULT WINAPI acmStreamClose(HACMSTREAM has, DWORD fdwClose) +{ + PWINE_ACMSTREAM was; + MMRESULT ret; + + TRACE("(%p, %ld)\n", has, fdwClose); + + if ((was = ACM_GetStream(has)) == NULL) { + WARN("invalid handle\n"); + return MMSYSERR_INVALHANDLE; + } + ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_CLOSE, (DWORD)&was->drvInst, 0); + if (ret == MMSYSERR_NOERROR) { + if (was->hAcmDriver) + acmDriverClose(was->hAcmDriver, 0L); + HeapFree(MSACM_hHeap, 0, was); + } + TRACE("=> (%d)\n", ret); + return ret; +} + +/*********************************************************************** + * acmStreamConvert (MSACM32.@) + */ +MMRESULT WINAPI acmStreamConvert(HACMSTREAM has, PACMSTREAMHEADER pash, + DWORD fdwConvert) +{ + PWINE_ACMSTREAM was; + MMRESULT ret = MMSYSERR_NOERROR; + PACMDRVSTREAMHEADER padsh; + + TRACE("(%p, %p, %ld)\n", has, pash, fdwConvert); + + if ((was = ACM_GetStream(has)) == NULL) { + WARN("invalid handle\n"); + return MMSYSERR_INVALHANDLE; + } + if (!pash || pash->cbStruct < sizeof(ACMSTREAMHEADER)) { + WARN("invalid parameter\n"); + return MMSYSERR_INVALPARAM; + } + if (!(pash->fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED)) { + WARN("unprepared header\n"); + return ACMERR_UNPREPARED; + } + + /* Note: the ACMSTREAMHEADER and ACMDRVSTREAMHEADER structs are of same + * size. some fields are private to msacm internals, and are exposed + * in ACMSTREAMHEADER in the dwReservedDriver array + */ + padsh = (PACMDRVSTREAMHEADER)pash; + + /* check that pointers have not been modified */ + if (padsh->pbPreparedSrc != padsh->pbSrc || + padsh->cbPreparedSrcLength < padsh->cbSrcLength || + padsh->pbPreparedDst != padsh->pbDst || + padsh->cbPreparedDstLength < padsh->cbDstLength) { + WARN("invalid parameter\n"); + return MMSYSERR_INVALPARAM; + } + + padsh->fdwConvert = fdwConvert; + + ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_CONVERT, (DWORD)&was->drvInst, (DWORD)padsh); + if (ret == MMSYSERR_NOERROR) { + padsh->fdwStatus |= ACMSTREAMHEADER_STATUSF_DONE; + } + TRACE("=> (%d)\n", ret); + return ret; +} + +/*********************************************************************** + * acmStreamMessage (MSACM32.@) + */ +MMRESULT WINAPI acmStreamMessage(HACMSTREAM has, UINT uMsg, LPARAM lParam1, + LPARAM lParam2) +{ + FIXME("(%p, %u, %ld, %ld): stub\n", has, uMsg, lParam1, lParam2); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); + return MMSYSERR_ERROR; +} + +/*********************************************************************** + * acmStreamOpen (MSACM32.@) + */ +MMRESULT WINAPI acmStreamOpen(PHACMSTREAM phas, HACMDRIVER had, PWAVEFORMATEX pwfxSrc, + PWAVEFORMATEX pwfxDst, PWAVEFILTER pwfltr, DWORD dwCallback, + DWORD dwInstance, DWORD fdwOpen) +{ + PWINE_ACMSTREAM was; + PWINE_ACMDRIVER wad; + MMRESULT ret; + int wfxSrcSize; + int wfxDstSize; + WAVEFORMATEX wfxSrc, wfxDst; + + TRACE("(%p, %p, %p, %p, %p, %ld, %ld, %ld)\n", + phas, had, pwfxSrc, pwfxDst, pwfltr, dwCallback, dwInstance, fdwOpen); + + /* NOTE: pwfxSrc and/or pwfxDst can point to a structure smaller than + * WAVEFORMATEX so don't use them directly when not sure */ + if (pwfxSrc->wFormatTag == WAVE_FORMAT_PCM) { + memcpy(&wfxSrc, pwfxSrc, sizeof(PCMWAVEFORMAT)); + wfxSrc.wBitsPerSample = pwfxSrc->wBitsPerSample; + wfxSrc.cbSize = 0; + pwfxSrc = &wfxSrc; + } + + if (pwfxDst->wFormatTag == WAVE_FORMAT_PCM) { + memcpy(&wfxDst, pwfxDst, sizeof(PCMWAVEFORMAT)); + wfxDst.wBitsPerSample = pwfxDst->wBitsPerSample; + wfxDst.cbSize = 0; + pwfxDst = &wfxDst; + } + + TRACE("src [wFormatTag=%u, nChannels=%u, nSamplesPerSec=%lu, nAvgBytesPerSec=%lu, nBlockAlign=%u, wBitsPerSample=%u, cbSize=%u]\n", + pwfxSrc->wFormatTag, pwfxSrc->nChannels, pwfxSrc->nSamplesPerSec, pwfxSrc->nAvgBytesPerSec, + pwfxSrc->nBlockAlign, pwfxSrc->wBitsPerSample, pwfxSrc->cbSize); + + TRACE("dst [wFormatTag=%u, nChannels=%u, nSamplesPerSec=%lu, nAvgBytesPerSec=%lu, nBlockAlign=%u, wBitsPerSample=%u, cbSize=%u]\n", + pwfxDst->wFormatTag, pwfxDst->nChannels, pwfxDst->nSamplesPerSec, pwfxDst->nAvgBytesPerSec, + pwfxDst->nBlockAlign, pwfxDst->wBitsPerSample, pwfxDst->cbSize); + + /* (WS) In query mode, phas should be NULL. If it is not, then instead + * of returning an error we are making sure it is NULL, preventing some + * applications that pass garbage for phas from crashing. + */ + if (fdwOpen & ACM_STREAMOPENF_QUERY) phas = NULL; + + if (pwfltr && (pwfxSrc->wFormatTag != pwfxDst->wFormatTag)) { + WARN("invalid parameter\n"); + return MMSYSERR_INVALPARAM; + } + + wfxSrcSize = wfxDstSize = sizeof(WAVEFORMATEX); + if (pwfxSrc->wFormatTag != WAVE_FORMAT_PCM) wfxSrcSize += pwfxSrc->cbSize; + if (pwfxDst->wFormatTag != WAVE_FORMAT_PCM) wfxDstSize += pwfxDst->cbSize; + + was = HeapAlloc(MSACM_hHeap, 0, sizeof(*was) + wfxSrcSize + wfxDstSize + + ((pwfltr) ? sizeof(WAVEFILTER) : 0)); + if (was == NULL) { + WARN("no memory\n"); + return MMSYSERR_NOMEM; + } + + was->drvInst.cbStruct = sizeof(was->drvInst); + was->drvInst.pwfxSrc = (PWAVEFORMATEX)((LPSTR)was + sizeof(*was)); + memcpy(was->drvInst.pwfxSrc, pwfxSrc, wfxSrcSize); + was->drvInst.pwfxDst = (PWAVEFORMATEX)((LPSTR)was + sizeof(*was) + wfxSrcSize); + memcpy(was->drvInst.pwfxDst, pwfxDst, wfxDstSize); + if (pwfltr) { + was->drvInst.pwfltr = (PWAVEFILTER)((LPSTR)was + sizeof(*was) + wfxSrcSize + wfxDstSize); + memcpy(was->drvInst.pwfltr, pwfltr, sizeof(WAVEFILTER)); + } else { + was->drvInst.pwfltr = NULL; + } + was->drvInst.dwCallback = dwCallback; + was->drvInst.dwInstance = dwInstance; + was->drvInst.fdwOpen = fdwOpen; + was->drvInst.fdwDriver = 0L; + was->drvInst.dwDriver = 0L; + /* real value will be stored once ACMDM_STREAM_OPEN succeeds */ + was->drvInst.has = 0L; + + if (had) { + if (!(wad = MSACM_GetDriver(had))) { + ret = MMSYSERR_INVALPARAM; + goto errCleanUp; + } + + was->obj.dwType = WINE_ACMOBJ_STREAM; + was->obj.pACMDriverID = wad->obj.pACMDriverID; + was->pDrv = wad; + was->hAcmDriver = 0; /* not to close it in acmStreamClose */ + + ret = SendDriverMessage(wad->hDrvr, ACMDM_STREAM_OPEN, (DWORD)&was->drvInst, 0L); + if (ret != MMSYSERR_NOERROR) + goto errCleanUp; + } else { + PWINE_ACMDRIVERID wadi; + + ret = ACMERR_NOTPOSSIBLE; + for (wadi = MSACM_pFirstACMDriverID; wadi; wadi = wadi->pNextACMDriverID) { + if ((wadi->fdwSupport & ACMDRIVERDETAILS_SUPPORTF_DISABLED) || + !MSACM_FindFormatTagInCache(wadi, pwfxSrc->wFormatTag, NULL) || + !MSACM_FindFormatTagInCache(wadi, pwfxDst->wFormatTag, NULL)) + continue; + ret = acmDriverOpen(&had, (HACMDRIVERID)wadi, 0L); + if (ret != MMSYSERR_NOERROR) + continue; + if ((wad = MSACM_GetDriver(had)) != 0) { + was->obj.dwType = WINE_ACMOBJ_STREAM; + was->obj.pACMDriverID = wad->obj.pACMDriverID; + was->pDrv = wad; + was->hAcmDriver = had; + + ret = SendDriverMessage(wad->hDrvr, ACMDM_STREAM_OPEN, (DWORD)&was->drvInst, 0L); + TRACE("%s => %08x\n", debugstr_w(wadi->pszDriverAlias), ret); + if (ret == MMSYSERR_NOERROR) { + if (fdwOpen & ACM_STREAMOPENF_QUERY) { + acmDriverClose(had, 0L); + } + break; + } + } + /* no match, close this acm driver and try next one */ + acmDriverClose(had, 0L); + } + if (ret != MMSYSERR_NOERROR) { + ret = ACMERR_NOTPOSSIBLE; + goto errCleanUp; + } + } + ret = MMSYSERR_NOERROR; + was->drvInst.has = (HACMSTREAM)was; + if (!(fdwOpen & ACM_STREAMOPENF_QUERY)) { + if (phas) + *phas = (HACMSTREAM)was; + TRACE("=> (%d)\n", ret); + return ret; + } +errCleanUp: + if (phas) + *phas = NULL; + HeapFree(MSACM_hHeap, 0, was); + TRACE("=> (%d)\n", ret); + return ret; +} + + +/*********************************************************************** + * acmStreamPrepareHeader (MSACM32.@) + */ +MMRESULT WINAPI acmStreamPrepareHeader(HACMSTREAM has, PACMSTREAMHEADER pash, + DWORD fdwPrepare) +{ + PWINE_ACMSTREAM was; + MMRESULT ret = MMSYSERR_NOERROR; + PACMDRVSTREAMHEADER padsh; + + TRACE("(%p, %p, %ld)\n", has, pash, fdwPrepare); + + if ((was = ACM_GetStream(has)) == NULL) { + WARN("invalid handle\n"); + return MMSYSERR_INVALHANDLE; + } + if (!pash || pash->cbStruct < sizeof(ACMSTREAMHEADER)) { + WARN("invalid parameter\n"); + return MMSYSERR_INVALPARAM; + } + if (fdwPrepare) + ret = MMSYSERR_INVALFLAG; + + if (pash->fdwStatus & ACMSTREAMHEADER_STATUSF_DONE) + return MMSYSERR_NOERROR; + + /* Note: the ACMSTREAMHEADER and ACMDRVSTREAMHEADER structs are of same + * size. some fields are private to msacm internals, and are exposed + * in ACMSTREAMHEADER in the dwReservedDriver array + */ + padsh = (PACMDRVSTREAMHEADER)pash; + + padsh->fdwConvert = fdwPrepare; + padsh->padshNext = NULL; + padsh->fdwDriver = padsh->dwDriver = 0L; + + padsh->fdwPrepared = 0; + padsh->dwPrepared = 0; + padsh->pbPreparedSrc = 0; + padsh->cbPreparedSrcLength = 0; + padsh->pbPreparedDst = 0; + padsh->cbPreparedDstLength = 0; + + ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_PREPARE, (DWORD)&was->drvInst, (DWORD)padsh); + if (ret == MMSYSERR_NOERROR || ret == MMSYSERR_NOTSUPPORTED) { + ret = MMSYSERR_NOERROR; + padsh->fdwStatus &= ~(ACMSTREAMHEADER_STATUSF_DONE|ACMSTREAMHEADER_STATUSF_INQUEUE); + padsh->fdwStatus |= ACMSTREAMHEADER_STATUSF_PREPARED; + padsh->fdwPrepared = padsh->fdwStatus; + padsh->dwPrepared = 0; + padsh->pbPreparedSrc = padsh->pbSrc; + padsh->cbPreparedSrcLength = padsh->cbSrcLength; + padsh->pbPreparedDst = padsh->pbDst; + padsh->cbPreparedDstLength = padsh->cbDstLength; + } else { + padsh->fdwPrepared = 0; + padsh->dwPrepared = 0; + padsh->pbPreparedSrc = 0; + padsh->cbPreparedSrcLength = 0; + padsh->pbPreparedDst = 0; + padsh->cbPreparedDstLength = 0; + } + TRACE("=> (%d)\n", ret); + return ret; +} + +/*********************************************************************** + * acmStreamReset (MSACM32.@) + */ +MMRESULT WINAPI acmStreamReset(HACMSTREAM has, DWORD fdwReset) +{ + PWINE_ACMSTREAM was; + MMRESULT ret = MMSYSERR_NOERROR; + + TRACE("(%p, %ld)\n", has, fdwReset); + + if (fdwReset) { + WARN("invalid flag\n"); + ret = MMSYSERR_INVALFLAG; + } else if ((was = ACM_GetStream(has)) == NULL) { + WARN("invalid handle\n"); + return MMSYSERR_INVALHANDLE; + } else if (was->drvInst.fdwOpen & ACM_STREAMOPENF_ASYNC) { + ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_RESET, (DWORD)&was->drvInst, 0); + } + TRACE("=> (%d)\n", ret); + return ret; +} + +/*********************************************************************** + * acmStreamSize (MSACM32.@) + */ +MMRESULT WINAPI acmStreamSize(HACMSTREAM has, DWORD cbInput, + LPDWORD pdwOutputBytes, DWORD fdwSize) +{ + PWINE_ACMSTREAM was; + ACMDRVSTREAMSIZE adss; + MMRESULT ret; + + TRACE("(%p, %ld, %p, %ld)\n", has, cbInput, pdwOutputBytes, fdwSize); + + if ((was = ACM_GetStream(has)) == NULL) { + WARN("invalid handle\n"); + return MMSYSERR_INVALHANDLE; + } + if ((fdwSize & ~ACM_STREAMSIZEF_QUERYMASK) != 0) { + WARN("invalid flag\n"); + return MMSYSERR_INVALFLAG; + } + + *pdwOutputBytes = 0L; + + switch (fdwSize & ACM_STREAMSIZEF_QUERYMASK) { + case ACM_STREAMSIZEF_DESTINATION: + adss.cbDstLength = cbInput; + adss.cbSrcLength = 0; + break; + case ACM_STREAMSIZEF_SOURCE: + adss.cbSrcLength = cbInput; + adss.cbDstLength = 0; + break; + default: + WARN("invalid flag\n"); + return MMSYSERR_INVALFLAG; + } + + adss.cbStruct = sizeof(adss); + adss.fdwSize = fdwSize; + ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_SIZE, + (DWORD)&was->drvInst, (DWORD)&adss); + if (ret == MMSYSERR_NOERROR) { + switch (fdwSize & ACM_STREAMSIZEF_QUERYMASK) { + case ACM_STREAMSIZEF_DESTINATION: + *pdwOutputBytes = adss.cbSrcLength; + break; + case ACM_STREAMSIZEF_SOURCE: + *pdwOutputBytes = adss.cbDstLength; + break; + } + } + TRACE("=> (%d) [%lu]\n", ret, *pdwOutputBytes); + return ret; +} + +/*********************************************************************** + * acmStreamUnprepareHeader (MSACM32.@) + */ +MMRESULT WINAPI acmStreamUnprepareHeader(HACMSTREAM has, PACMSTREAMHEADER pash, + DWORD fdwUnprepare) +{ + PWINE_ACMSTREAM was; + MMRESULT ret = MMSYSERR_NOERROR; + PACMDRVSTREAMHEADER padsh; + + TRACE("(%p, %p, %ld)\n", has, pash, fdwUnprepare); + + if ((was = ACM_GetStream(has)) == NULL) { + WARN("invalid handle\n"); + return MMSYSERR_INVALHANDLE; + } + if (!pash || pash->cbStruct < sizeof(ACMSTREAMHEADER)) { + WARN("invalid parameter\n"); + return MMSYSERR_INVALPARAM; + } + if (!(pash->fdwStatus & ACMSTREAMHEADER_STATUSF_PREPARED)) { + WARN("unprepared header\n"); + return ACMERR_UNPREPARED; + } + + /* Note: the ACMSTREAMHEADER and ACMDRVSTREAMHEADER structs are of same + * size. some fields are private to msacm internals, and are exposed + * in ACMSTREAMHEADER in the dwReservedDriver array + */ + padsh = (PACMDRVSTREAMHEADER)pash; + + /* check that pointers have not been modified */ + if (padsh->pbPreparedSrc != padsh->pbSrc || + padsh->cbPreparedSrcLength < padsh->cbSrcLength || + padsh->pbPreparedDst != padsh->pbDst || + padsh->cbPreparedDstLength < padsh->cbDstLength) { + WARN("invalid parameter\n"); + return MMSYSERR_INVALPARAM; + } + + padsh->fdwConvert = fdwUnprepare; + + ret = SendDriverMessage(was->pDrv->hDrvr, ACMDM_STREAM_UNPREPARE, (DWORD)&was->drvInst, (DWORD)padsh); + if (ret == MMSYSERR_NOERROR || ret == MMSYSERR_NOTSUPPORTED) { + ret = MMSYSERR_NOERROR; + padsh->fdwStatus &= ~(ACMSTREAMHEADER_STATUSF_DONE|ACMSTREAMHEADER_STATUSF_INQUEUE|ACMSTREAMHEADER_STATUSF_PREPARED); + } + TRACE("=> (%d)\n", ret); + return ret; +} diff --git a/reactos/dll/msacm/wineacm.h b/reactos/dll/msacm/wineacm.h new file mode 100644 index 00000000000..5244bfb1800 --- /dev/null +++ b/reactos/dll/msacm/wineacm.h @@ -0,0 +1,363 @@ +/* -*- tab-width: 8; c-basic-offset: 4 -*- */ +/* + * Copyright 2000 Eric Pouech + * + * 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 + */ + +#ifndef __WINE_WINEACM_H +#define __WINE_WINEACM_H + +#ifndef __REACTOS__ +#include "wine/windef16.h" +//#include "wine/mmsystem16.h" + +/*********************************************************************** + * Win16 definitions + */ +typedef BOOL16 (CALLBACK *ACMDRIVERENUMCB16)( + HACMDRIVERID16 hadid, DWORD dwInstance, DWORD fdwSupport +); +typedef UINT (CALLBACK *ACMFILTERCHOOSEHOOKPROC16)( + HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam +); +typedef UINT16 (CALLBACK *ACMFORMATCHOOSEHOOKPROC16)( + HWND16 hwnd, UINT16 uMsg, WPARAM16 wParam, LPARAM lParam +); + +typedef struct _ACMDRIVERDETAILS16 +{ + DWORD cbStruct; + + FOURCC fccType; + FOURCC fccComp; + + WORD wMid; + WORD wPid; + + DWORD vdwACM; + DWORD vdwDriver; + + DWORD fdwSupport; + DWORD cFormatTags; + DWORD cFilterTags; + + HICON16 hicon; + + CHAR szShortName[ACMDRIVERDETAILS_SHORTNAME_CHARS]; + CHAR szLongName[ACMDRIVERDETAILS_LONGNAME_CHARS]; + CHAR szCopyright[ACMDRIVERDETAILS_COPYRIGHT_CHARS]; + CHAR szLicensing[ACMDRIVERDETAILS_LICENSING_CHARS]; + CHAR szFeatures[ACMDRIVERDETAILS_FEATURES_CHARS]; +} ACMDRIVERDETAILS16, *NPACMDRIVERDETAILS16, *LPACMDRIVERDETAILS16; + +typedef struct _ACMFILTERCHOOSE16 +{ + DWORD cbStruct; + DWORD fdwStyle; + + HWND16 hwndOwner; + + LPWAVEFILTER pwfltr; + DWORD cbwfltr; + + LPCSTR pszTitle; + + char szFilterTag[ACMFILTERTAGDETAILS_FILTERTAG_CHARS]; + char szFilter[ACMFILTERDETAILS_FILTER_CHARS]; + LPSTR pszName; + DWORD cchName; + + DWORD fdwEnum; + LPWAVEFILTER pwfltrEnum; + + HINSTANCE16 hInstance; + LPCSTR pszTemplateName; + LPARAM lCustData; + ACMFILTERCHOOSEHOOKPROC16 pfnHook; +} ACMFILTERCHOOSE16, *NPACMFILTERCHOOSE16, *LPACMFILTERCHOOSE16; + +typedef struct _ACMFILTERDETAILS16 +{ + DWORD cbStruct; + DWORD dwFilterIndex; + DWORD dwFilterTag; + DWORD fdwSupport; + LPWAVEFILTER pwfltr; + DWORD cbwfltr; + CHAR szFilter[ACMFILTERDETAILS_FILTER_CHARS]; +} ACMFILTERDETAILS16, *NPACMFILTERDETAILS16, *LPACMFILTERDETAILS16; + +typedef struct _ACMFILTERTAGDETAILS16 +{ + DWORD cbStruct; + DWORD dwFilterTagIndex; + DWORD dwFilterTag; + DWORD cbFilterSize; + DWORD fdwSupport; + DWORD cStandardFilters; + CHAR szFilterTag[ACMFILTERTAGDETAILS_FILTERTAG_CHARS]; +} ACMFILTERTAGDETAILS16, *NPACMFILTERTAGDETAILS16, *LPACMFILTERTAGDETAILS16; + +typedef struct _ACMFORMATCHOOSE16 +{ + DWORD cbStruct; + DWORD fdwStyle; + + HWND16 hwndOwner; + + LPWAVEFORMATEX pwfx; + DWORD cbwfx; + LPCSTR pszTitle; + + CHAR szFormatTag[ACMFORMATTAGDETAILS_FORMATTAG_CHARS]; + CHAR szFormat[ACMFORMATDETAILS_FORMAT_CHARS]; + + LPSTR pszName; + DWORD cchName; + + DWORD fdwEnum; + LPWAVEFORMATEX pwfxEnum; + + HINSTANCE16 hInstance; + LPCSTR pszTemplateName; + LPARAM lCustData; + ACMFORMATCHOOSEHOOKPROC16 pfnHook; +} ACMFORMATCHOOSE16, *NPACMFORMATCHOOSE16, *LPACMFORMATCHOOSE16; + +typedef struct _ACMFORMATDETAILS16 +{ + DWORD cbStruct; + DWORD dwFormatIndex; + DWORD dwFormatTag; + DWORD fdwSupport; + LPWAVEFORMATEX pwfx; + DWORD cbwfx; + CHAR szFormat[ACMFORMATDETAILS_FORMAT_CHARS]; +} ACMFORMATDETAILS16, *NPACMFORMATDETAILS16, *LPACMFORMATDETAILS16; + +typedef struct _ACMFORMATTAGDETAILS16 +{ + DWORD cbStruct; + DWORD dwFormatTagIndex; + DWORD dwFormatTag; + DWORD cbFormatSize; + DWORD fdwSupport; + DWORD cStandardFormats; + CHAR szFormatTag[ACMFORMATTAGDETAILS_FORMATTAG_CHARS]; +} ACMFORMATTAGDETAILS16, *NPACMFORMATTAGDETAILS16, *LPACMFORMATTAGDETAILS16; + +typedef ACMSTREAMHEADER ACMSTREAMHEADER16, *NPACMSTREAMHEADER16, *LPACMSTREAMHEADER16; + +typedef BOOL16 (CALLBACK *ACMFILTERENUMCB16)( + HACMDRIVERID16 hadid, LPACMFILTERDETAILS16 pafd, + DWORD dwInstance, DWORD fdwSupport +); + +typedef BOOL16 (CALLBACK *ACMFILTERTAGENUMCB16)( + HACMDRIVERID16 hadid, LPACMFILTERTAGDETAILS16 paftd, + DWORD dwInstance, DWORD fdwSupport +); + +typedef BOOL16 (CALLBACK *ACMFORMATENUMCB16)( + HACMDRIVERID16 hadid, LPACMFORMATDETAILS16 pafd, + DWORD dwInstance, DWORD fdwSupport +); + +typedef BOOL16 (CALLBACK *ACMFORMATTAGENUMCB16)( + HACMDRIVERID16 hadid, LPACMFORMATTAGDETAILS16 paftd, + DWORD dwInstance, DWORD fdwSupport +); + +/*********************************************************************** + * Functions - Win16 + */ + +DWORD WINAPI acmGetVersion16( +); +MMRESULT16 WINAPI acmMetrics16( + HACMOBJ16 hao, UINT16 uMetric, LPVOID pMetric +); +MMRESULT16 WINAPI acmDriverEnum16( + ACMDRIVERENUMCB16 fnCallback, DWORD dwInstance, DWORD fdwEnum +); +MMRESULT16 WINAPI acmDriverDetails16( + HACMDRIVERID16 hadid, LPACMDRIVERDETAILS16 padd, DWORD fdwDetails +); +MMRESULT16 WINAPI acmDriverAdd16( + LPHACMDRIVERID16 phadid, HINSTANCE16 hinstModule, + LPARAM lParam, DWORD dwPriority, DWORD fdwAdd +); +MMRESULT16 WINAPI acmDriverRemove16( + HACMDRIVERID16 hadid, DWORD fdwRemove +); +MMRESULT16 WINAPI acmDriverOpen16( + LPHACMDRIVER16 phad, HACMDRIVERID16 hadid, DWORD fdwOpen +); +MMRESULT16 WINAPI acmDriverClose16( + HACMDRIVER16 had, DWORD fdwClose +); +LRESULT WINAPI acmDriverMessage16( + HACMDRIVER16 had, UINT16 uMsg, LPARAM lParam1, LPARAM lParam2 +); +MMRESULT16 WINAPI acmDriverID16( + HACMOBJ16 hao, LPHACMDRIVERID16 phadid, DWORD fdwDriverID +); +MMRESULT16 WINAPI acmDriverPriority16( + HACMDRIVERID16 hadid, DWORD dwPriority, DWORD fdwPriority +); +MMRESULT16 WINAPI acmFormatTagDetails16( + HACMDRIVER16 had, LPACMFORMATTAGDETAILS16 paftd, DWORD fdwDetails +); +MMRESULT16 WINAPI acmFormatTagEnum16( + HACMDRIVER16 had, LPACMFORMATTAGDETAILS16 paftd, + ACMFORMATTAGENUMCB16 fnCallback, DWORD dwInstance, DWORD fdwEnum +); +MMRESULT16 WINAPI acmFormatChoose16( + LPACMFORMATCHOOSE16 pafmtc +); +MMRESULT16 WINAPI acmFormatDetails16( + HACMDRIVER16 had, LPACMFORMATDETAILS16 pafd, DWORD fdwDetails +); +MMRESULT16 WINAPI acmFormatEnum16( + HACMDRIVER16 had, LPACMFORMATDETAILS16 pafd, + ACMFORMATENUMCB16 fnCallback, DWORD dwInstance, DWORD fdwEnum +); +MMRESULT16 WINAPI acmFormatSuggest16( + HACMDRIVER16 had, LPWAVEFORMATEX pwfxSrc, + LPWAVEFORMATEX pwfxDst, DWORD cbwfxDst, DWORD fdwSuggest +); +MMRESULT16 WINAPI acmFilterTagDetails16( + HACMDRIVER16 had, LPACMFILTERTAGDETAILS16 paftd, DWORD fdwDetails +); +MMRESULT16 WINAPI acmFilterTagEnum16( + HACMDRIVER16 had, LPACMFILTERTAGDETAILS16 paftd, + ACMFILTERTAGENUMCB16 fnCallback, DWORD dwInstance, DWORD fdwEnum +); +MMRESULT16 WINAPI acmFilterChoose16( + LPACMFILTERCHOOSE16 pafltrc +); +MMRESULT16 WINAPI acmFilterDetails16( + HACMDRIVER16 had, LPACMFILTERDETAILS16 pafd, DWORD fdwDetails +); +MMRESULT16 WINAPI acmFilterEnum16( + HACMDRIVER16 had, LPACMFILTERDETAILS16 pafd, + ACMFILTERENUMCB16 fnCallback, DWORD dwInstance, DWORD fdwEnum +); +MMRESULT16 WINAPI acmStreamOpen16( + LPHACMSTREAM16 phas, HACMDRIVER16 had, + LPWAVEFORMATEX pwfxSrc, LPWAVEFORMATEX pwfxDst, + LPWAVEFILTER pwfltr, DWORD dwCallback, + DWORD dwInstance, DWORD fdwOpen +); +MMRESULT16 WINAPI acmStreamClose16( + HACMSTREAM16 has, DWORD fdwClose +); +MMRESULT16 WINAPI acmStreamSize16( + HACMSTREAM16 has, DWORD cbInput, + LPDWORD pdwOutputBytes, DWORD fdwSize +); +MMRESULT16 WINAPI acmStreamConvert16( + HACMSTREAM16 has, LPACMSTREAMHEADER16 pash, DWORD fdwConvert +); +MMRESULT16 WINAPI acmStreamReset16( + HACMSTREAM16 has, DWORD fdwReset +); +MMRESULT16 WINAPI acmStreamPrepareHeader16( + HACMSTREAM16 has, LPACMSTREAMHEADER16 pash, DWORD fdwPrepare +); +MMRESULT16 WINAPI acmStreamUnprepareHeader16( + HACMSTREAM16 has, LPACMSTREAMHEADER16 pash, DWORD fdwUnprepare +); +#endif + +/*********************************************************************** + * Wine specific - Win32 + */ +typedef struct _WINE_ACMDRIVERID *PWINE_ACMDRIVERID; +typedef struct _WINE_ACMDRIVER *PWINE_ACMDRIVER; + +#define WINE_ACMOBJ_DONTCARE 0x5EED0000 +#define WINE_ACMOBJ_DRIVERID 0x5EED0001 +#define WINE_ACMOBJ_DRIVER 0x5EED0002 +#define WINE_ACMOBJ_STREAM 0x5EED0003 + +typedef struct _WINE_ACMOBJ +{ + DWORD dwType; + PWINE_ACMDRIVERID pACMDriverID; +} WINE_ACMOBJ, *PWINE_ACMOBJ; + +typedef struct _WINE_ACMDRIVER +{ + WINE_ACMOBJ obj; + HDRVR hDrvr; + PWINE_ACMDRIVER pNextACMDriver; +} WINE_ACMDRIVER; + +typedef struct _WINE_ACMSTREAM +{ + WINE_ACMOBJ obj; + PWINE_ACMDRIVER pDrv; + ACMDRVSTREAMINSTANCE drvInst; + HACMDRIVER hAcmDriver; +} WINE_ACMSTREAM, *PWINE_ACMSTREAM; + +typedef struct _WINE_ACMDRIVERID +{ + WINE_ACMOBJ obj; + LPWSTR pszDriverAlias; + LPWSTR pszFileName; + HINSTANCE hInstModule; /* NULL if global */ + PWINE_ACMDRIVER pACMDriverList; + PWINE_ACMDRIVERID pNextACMDriverID; + PWINE_ACMDRIVERID pPrevACMDriverID; + /* information about the driver itself, either gotten from registry or driver itself */ + DWORD cFilterTags; + DWORD cFormatTags; + DWORD fdwSupport; + struct { + DWORD dwFormatTag; + DWORD cbwfx; + }* aFormatTag; +} WINE_ACMDRIVERID; + +/* From internal.c */ +extern HANDLE MSACM_hHeap; +extern PWINE_ACMDRIVERID MSACM_pFirstACMDriverID; +extern PWINE_ACMDRIVERID MSACM_pLastACMDriverID; +extern PWINE_ACMDRIVERID MSACM_RegisterDriver(LPCWSTR pszDriverAlias, LPCWSTR pszFileName, + HINSTANCE hinstModule); +extern void MSACM_RegisterAllDrivers(void); +extern PWINE_ACMDRIVERID MSACM_UnregisterDriver(PWINE_ACMDRIVERID p); +extern void MSACM_UnregisterAllDrivers(void); +extern PWINE_ACMDRIVERID MSACM_GetDriverID(HACMDRIVERID hDriverID); +extern PWINE_ACMDRIVER MSACM_GetDriver(HACMDRIVER hDriver); +extern PWINE_ACMOBJ MSACM_GetObj(HACMOBJ hObj, DWORD type); + +extern MMRESULT MSACM_Message(HACMDRIVER, UINT, LPARAM, LPARAM); +extern BOOL MSACM_FindFormatTagInCache(WINE_ACMDRIVERID*, DWORD, LPDWORD); + +/* From msacm32.c */ +extern HINSTANCE MSACM_hInstance32; + +/* From pcmcnvtr.c */ +LRESULT CALLBACK PCM_DriverProc(DWORD dwDevID, HDRVR hDriv, UINT wMsg, + LPARAM dwParam1, LPARAM dwParam2); + +/* Dialog box templates */ +#include "msacmdlg.h" + +#endif /* __WINE_WINEACM_H */ diff --git a/reactos/dll/msafd/include/debug.h b/reactos/dll/msafd/include/debug.h new file mode 100644 index 00000000000..a4eb8fef805 --- /dev/null +++ b/reactos/dll/msafd/include/debug.h @@ -0,0 +1,67 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Ancillary Function Driver DLL + * FILE: include/debug.h + * PURPOSE: Debugging support macros + * DEFINES: DBG - Enable debug output + * NASSERT - Disable assertions + */ +#ifndef __DEBUG_H +#define __DEBUG_H + +#define NORMAL_MASK 0x000000FF +#define SPECIAL_MASK 0xFFFFFF00 +#define MIN_TRACE 0x00000001 +#define MID_TRACE 0x00000002 +#define MAX_TRACE 0x00000003 + +#define DEBUG_CHECK 0x00000100 +#define DEBUG_ULTRA 0xFFFFFFFF + +#ifdef ASSERT +#undef ASSERT +#endif + +#ifdef DBG + +extern DWORD DebugTraceLevel; + +#define AFD_DbgPrint(_t_, _x_) \ + if (((DebugTraceLevel & NORMAL_MASK) >= _t_) || \ + ((DebugTraceLevel & _t_) > NORMAL_MASK)) { \ + DbgPrint("(%hS:%d)(%hS) ", __FILE__, __LINE__, __FUNCTION__); \ + DbgPrint _x_; \ + } + +#ifdef NASSERT +#define ASSERT(x) +#else /* NASSERT */ +#define ASSERT(x) if (!(x)) { AFD_DbgPrint(MIN_TRACE, ("Assertion "#x" failed at %s:%d\n", __FILE__, __LINE__)); ExitProcess(0); } +#endif /* NASSERT */ + +#else /* DBG */ + +#define AFD_DbgPrint(_t_, _x_) + +#define ASSERT_IRQL(x) +#define ASSERT(x) + +#endif /* DBG */ + +#ifdef assert +#undef assert +#endif +#define assert(x) ASSERT(x) + + +#define UNIMPLEMENTED \ + AFD_DbgPrint(MIN_TRACE, ("is unimplemented, please try again later.\n")); + +#define CHECKPOINT \ + AFD_DbgPrint(DEBUG_CHECK, ("\n")); + +#define CP CHECKPOINT + +#endif /* __DEBUG_H */ + +/* EOF */ diff --git a/reactos/dll/msafd/include/helpers.h b/reactos/dll/msafd/include/helpers.h new file mode 100644 index 00000000000..1b73c669df5 --- /dev/null +++ b/reactos/dll/msafd/include/helpers.h @@ -0,0 +1,73 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Ancillary Function Driver DLL + * FILE: include/helpers.h + * PURPOSE: Definitions for helper DLL management + */ +#ifndef __HELPERS_H +#define __HELPERS_H + +//#include + +typedef struct _HELPER_DATA { + LIST_ENTRY Helpers; + LONG RefCount; + HANDLE hInstance; + INT MinWSAddressLength; + INT MaxWSAddressLength; + INT MinTDIAddressLength; + INT MaxTDIAddressLength; + BOOLEAN UseDelayedAcceptance; + PWINSOCK_MAPPING Mapping; + PWSH_OPEN_SOCKET WSHOpenSocket; + PWSH_OPEN_SOCKET2 WSHOpenSocket2; + PWSH_JOIN_LEAF WSHJoinLeaf; + PWSH_NOTIFY WSHNotify; + PWSH_GET_SOCKET_INFORMATION WSHGetSocketInformation; + PWSH_SET_SOCKET_INFORMATION WSHSetSocketInformation; + PWSH_GET_SOCKADDR_TYPE WSHGetSockaddrType; + PWSH_GET_WILDCARD_SOCKEADDR WSHGetWildcardSockaddr; + PWSH_GET_BROADCAST_SOCKADDR WSHGetBroadcastSockaddr; + PWSH_ADDRESS_TO_STRING WSHAddressToString; + PWSH_STRING_TO_ADDRESS WSHStringToAddress; + PWSH_IOCTL WSHIoctl; + WCHAR TransportName[1]; +} HELPER_DATA, *PHELPER_DATA; + +int SockLoadHelperDll( + PWSTR TransportName, + PWINSOCK_MAPPING Mapping, + PHELPER_DATA *HelperDllData +); + +int SockLoadTransportMapping( + PWSTR TransportName, + PWINSOCK_MAPPING *Mapping +); + +int SockLoadTransportList( + PWSTR *TransportList +); + +BOOL SockIsTripleInMapping( + PWINSOCK_MAPPING Mapping, + INT AddressFamily, + INT SocketType, + INT Protocol +); + +int SockGetTdiName( + PINT AddressFamily, + PINT SocketType, + PINT Protocol, + GROUP Group, + DWORD Flags, + PUNICODE_STRING TransportName, + PVOID *HelperDllContext, + PHELPER_DATA *HelperDllData, + PDWORD Events +); + +#endif /* __HELPERS_H */ + +/* EOF */ diff --git a/reactos/dll/msafd/misc/dllmain.c b/reactos/dll/msafd/misc/dllmain.c new file mode 100644 index 00000000000..8cb8eb02a00 --- /dev/null +++ b/reactos/dll/msafd/misc/dllmain.c @@ -0,0 +1,2310 @@ + +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Ancillary Function Driver DLL + * FILE: misc/dllmain.c + * PURPOSE: DLL entry point + * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) + * Alex Ionescu (alex@relsoft.net) + * REVISIONS: + * CSH 01/09-2000 Created + * Alex 16/07/2004 - Complete Rewrite + */ +#include + +#include + +#ifdef DBG +//DWORD DebugTraceLevel = DEBUG_ULTRA; +DWORD DebugTraceLevel = 0; +#endif /* DBG */ + +HANDLE GlobalHeap; +WSPUPCALLTABLE Upcalls; +LPWPUCOMPLETEOVERLAPPEDREQUEST lpWPUCompleteOverlappedRequest; +ULONG SocketCount = 0; +PSOCKET_INFORMATION *Sockets = NULL; +LIST_ENTRY SockHelpersListHead = {NULL}; +ULONG SockAsyncThreadRefCount; +HANDLE SockAsyncHelperAfdHandle; +HANDLE SockAsyncCompletionPort; +BOOLEAN SockAsyncSelectCalled; + +SOCKET +WSPAPI +WSPSocket( + int AddressFamily, + int SocketType, + int Protocol, + LPWSAPROTOCOL_INFOW lpProtocolInfo, + GROUP g, + DWORD dwFlags, + LPINT lpErrno) +/* + * FUNCTION: Creates a new socket + * ARGUMENTS: + * af = Address family + * type = Socket type + * protocol = Protocol type + * lpProtocolInfo = Pointer to protocol information + * g = Reserved + * dwFlags = Socket flags + * lpErrno = Address of buffer for error information + * RETURNS: + * Created socket, or INVALID_SOCKET if it could not be created + */ +{ + OBJECT_ATTRIBUTES Object; + IO_STATUS_BLOCK IOSB; + USHORT SizeOfPacket; + ULONG SizeOfEA; + PAFD_CREATE_PACKET AfdPacket; + HANDLE Sock; + PSOCKET_INFORMATION Socket = NULL, PrevSocket = NULL; + PFILE_FULL_EA_INFORMATION EABuffer = NULL; + PHELPER_DATA HelperData; + PVOID HelperDLLContext; + DWORD HelperEvents; + UNICODE_STRING TransportName; + UNICODE_STRING DevName; + LARGE_INTEGER GroupData; + INT Status; + + AFD_DbgPrint(MAX_TRACE, ("Creating Socket, getting TDI Name\n")); + AFD_DbgPrint(MAX_TRACE, ("AddressFamily (%d) SocketType (%d) Protocol (%d).\n", + AddressFamily, SocketType, Protocol)); + + /* Get Helper Data and Transport */ + Status = SockGetTdiName (&AddressFamily, + &SocketType, + &Protocol, + g, + dwFlags, + &TransportName, + &HelperDLLContext, + &HelperData, + &HelperEvents); + + /* Check for error */ + if (Status != NO_ERROR) { + AFD_DbgPrint(MID_TRACE,("SockGetTdiName: Status %x\n", Status)); + goto error; + } + + /* AFD Device Name */ + RtlInitUnicodeString(&DevName, L"\\Device\\Afd\\Endpoint"); + + /* Set Socket Data */ + Socket = HeapAlloc(GlobalHeap, 0, sizeof(*Socket)); + RtlZeroMemory(Socket, sizeof(*Socket)); + Socket->RefCount = 2; + Socket->Handle = -1; + Socket->SharedData.State = SocketOpen; + Socket->SharedData.AddressFamily = AddressFamily; + Socket->SharedData.SocketType = SocketType; + Socket->SharedData.Protocol = Protocol; + Socket->HelperContext = HelperDLLContext; + Socket->HelperData = HelperData; + Socket->HelperEvents = HelperEvents; + Socket->LocalAddress = &Socket->WSLocalAddress; + Socket->SharedData.SizeOfLocalAddress = HelperData->MaxWSAddressLength; + Socket->RemoteAddress = &Socket->WSRemoteAddress; + Socket->SharedData.SizeOfRemoteAddress = HelperData->MaxWSAddressLength; + Socket->SharedData.UseDelayedAcceptance = HelperData->UseDelayedAcceptance; + Socket->SharedData.CreateFlags = dwFlags; + Socket->SharedData.CatalogEntryId = lpProtocolInfo->dwCatalogEntryId; + Socket->SharedData.ServiceFlags1 = lpProtocolInfo->dwServiceFlags1; + Socket->SharedData.ProviderFlags = lpProtocolInfo->dwProviderFlags; + Socket->SharedData.GroupID = g; + Socket->SharedData.GroupType = 0; + Socket->SharedData.UseSAN = FALSE; + Socket->SharedData.NonBlocking = FALSE; /* Sockets start blocking */ + Socket->SanData = NULL; + + /* Ask alex about this */ + if( Socket->SharedData.SocketType == SOCK_DGRAM || + Socket->SharedData.SocketType == SOCK_RAW ) { + AFD_DbgPrint(MID_TRACE,("Connectionless socket\n")); + Socket->SharedData.ServiceFlags1 |= XP1_CONNECTIONLESS; + } + + /* Packet Size */ + SizeOfPacket = TransportName.Length + sizeof(AFD_CREATE_PACKET) + sizeof(WCHAR); + + /* EA Size */ + SizeOfEA = SizeOfPacket + sizeof(FILE_FULL_EA_INFORMATION) + AFD_PACKET_COMMAND_LENGTH; + + /* Set up EA Buffer */ + EABuffer = HeapAlloc(GlobalHeap, 0, SizeOfEA); + RtlZeroMemory(EABuffer, SizeOfEA); + EABuffer->NextEntryOffset = 0; + EABuffer->Flags = 0; + EABuffer->EaNameLength = AFD_PACKET_COMMAND_LENGTH; + RtlCopyMemory (EABuffer->EaName, + AfdCommand, + AFD_PACKET_COMMAND_LENGTH + 1); + EABuffer->EaValueLength = SizeOfPacket; + + /* Set up AFD Packet */ + AfdPacket = (PAFD_CREATE_PACKET)(EABuffer->EaName + EABuffer->EaNameLength + 1); + AfdPacket->SizeOfTransportName = TransportName.Length; + RtlCopyMemory (AfdPacket->TransportName, + TransportName.Buffer, + TransportName.Length + sizeof(WCHAR)); + AfdPacket->GroupID = g; + + /* Set up Endpoint Flags */ + if ((Socket->SharedData.ServiceFlags1 & XP1_CONNECTIONLESS) != 0) { + if ((SocketType != SOCK_DGRAM) && (SocketType != SOCK_RAW)) { + goto error; /* Only RAW or UDP can be Connectionless */ + } + AfdPacket->EndpointFlags |= AFD_ENDPOINT_CONNECTIONLESS; + } + + if ((Socket->SharedData.ServiceFlags1 & XP1_MESSAGE_ORIENTED) != 0) { + if (SocketType == SOCK_STREAM) { + if ((Socket->SharedData.ServiceFlags1 & XP1_PSEUDO_STREAM) == 0) { + goto error; /* The Provider doesn't actually support Message Oriented Streams */ + } + } + AfdPacket->EndpointFlags |= AFD_ENDPOINT_MESSAGE_ORIENTED; + } + + if (SocketType == SOCK_RAW) AfdPacket->EndpointFlags |= AFD_ENDPOINT_RAW; + + if (dwFlags & (WSA_FLAG_MULTIPOINT_C_ROOT | + WSA_FLAG_MULTIPOINT_C_LEAF | + WSA_FLAG_MULTIPOINT_D_ROOT | + WSA_FLAG_MULTIPOINT_D_LEAF)) { + if ((Socket->SharedData.ServiceFlags1 & XP1_SUPPORT_MULTIPOINT) == 0) { + goto error; /* The Provider doesn't actually support Multipoint */ + } + AfdPacket->EndpointFlags |= AFD_ENDPOINT_MULTIPOINT; + if (dwFlags & WSA_FLAG_MULTIPOINT_C_ROOT) { + if (((Socket->SharedData.ServiceFlags1 & XP1_MULTIPOINT_CONTROL_PLANE) == 0) + || ((dwFlags & WSA_FLAG_MULTIPOINT_C_LEAF) != 0)) { + goto error; /* The Provider doesn't support Control Planes, or you already gave a leaf */ + } + AfdPacket->EndpointFlags |= AFD_ENDPOINT_C_ROOT; + } + if (dwFlags & WSA_FLAG_MULTIPOINT_D_ROOT) { + if (((Socket->SharedData.ServiceFlags1 & XP1_MULTIPOINT_DATA_PLANE) == 0) + || ((dwFlags & WSA_FLAG_MULTIPOINT_D_LEAF) != 0)) { + goto error; /* The Provider doesn't support Data Planes, or you already gave a leaf */ + } + AfdPacket->EndpointFlags |= AFD_ENDPOINT_D_ROOT; + } + } + + /* Set up Object Attributes */ + InitializeObjectAttributes (&Object, + &DevName, + OBJ_CASE_INSENSITIVE | OBJ_INHERIT, + 0, + 0); + + /* Create the Socket as asynchronous. That means we have to block + ourselves after every call to NtDeviceIoControlFile. This is + because the kernel doesn't support overlapping synchronous I/O + requests (made from multiple threads) at this time (Sep 2005) */ + ZwCreateFile(&Sock, + GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, + &Object, + &IOSB, + NULL, + 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN_IF, + 0, + EABuffer, + SizeOfEA); + + /* Save Handle */ + Socket->Handle = (SOCKET)Sock; + + /* XXX See if there's a structure we can reuse -- We need to do this + * more properly. */ + PrevSocket = GetSocketStructure( (SOCKET)Sock ); + + if( PrevSocket ) { + RtlCopyMemory( PrevSocket, Socket, sizeof(*Socket) ); + RtlFreeHeap( GlobalHeap, 0, Socket ); + Socket = PrevSocket; + } + + /* Save Group Info */ + if (g != 0) { + GetSocketInformation(Socket, AFD_INFO_GROUP_ID_TYPE, 0, &GroupData); + + Socket->SharedData.GroupID = GroupData.u.LowPart; + Socket->SharedData.GroupType = GroupData.u.HighPart; + } + + /* Get Window Sizes and Save them */ + GetSocketInformation (Socket, + AFD_INFO_SEND_WINDOW_SIZE, + &Socket->SharedData.SizeOfSendBuffer, + NULL); + GetSocketInformation (Socket, + AFD_INFO_RECEIVE_WINDOW_SIZE, + &Socket->SharedData.SizeOfRecvBuffer, + NULL); + + /* Save in Process Sockets List */ + Sockets[SocketCount] = Socket; + SocketCount ++; + + /* Create the Socket Context */ + CreateContext(Socket); + + /* Notify Winsock */ + Upcalls.lpWPUModifyIFSHandle(1, (SOCKET)Sock, lpErrno); + + /* Return Socket Handle */ + AFD_DbgPrint(MID_TRACE,("Success %x\n", Sock)); + return (SOCKET)Sock; + +error: + AFD_DbgPrint(MID_TRACE,("Ending %x\n", Status)); + + if( lpErrno ) *lpErrno = Status; + + return INVALID_SOCKET; +} + + +DWORD MsafdReturnWithErrno( NTSTATUS Status, LPINT Errno, DWORD Received, + LPDWORD ReturnedBytes ) { + if( ReturnedBytes ) *ReturnedBytes = 0; + if( Errno ) { + switch (Status) { + case STATUS_CANT_WAIT: *Errno = WSAEWOULDBLOCK; break; + case STATUS_TIMEOUT: + case STATUS_SUCCESS: + /* Return Number of bytes Read */ + if( ReturnedBytes ) *ReturnedBytes = Received; break; + case STATUS_END_OF_FILE: *Errno = WSAESHUTDOWN; *ReturnedBytes = 0; break; + case STATUS_PENDING: *Errno = WSA_IO_PENDING; break; + case STATUS_BUFFER_OVERFLOW: *Errno = WSAEMSGSIZE; break; + default: { + DbgPrint("MSAFD: Error %x is unknown\n", Status); + *Errno = WSAEINVAL; break; + } break; + } + } + + /* Success */ + return Status == STATUS_SUCCESS ? 0 : SOCKET_ERROR; +} + + +INT +WSPAPI +WSPCloseSocket( + IN SOCKET Handle, + OUT LPINT lpErrno) +/* + * FUNCTION: Closes an open socket + * ARGUMENTS: + * s = Socket descriptor + * lpErrno = Address of buffer for error information + * RETURNS: + * NO_ERROR, or SOCKET_ERROR if the socket could not be closed + */ +{ + IO_STATUS_BLOCK IoStatusBlock; + PSOCKET_INFORMATION Socket = NULL; + NTSTATUS Status; + HANDLE SockEvent; + AFD_DISCONNECT_INFO DisconnectInfo; + SOCKET_STATE OldState; + + /* Create the Wait Event */ + Status = NtCreateEvent(&SockEvent, + GENERIC_READ | GENERIC_WRITE, + NULL, + 1, + FALSE); + + if(!NT_SUCCESS(Status)) return SOCKET_ERROR; + + /* Get the Socket Structure associate to this Socket*/ + Socket = GetSocketStructure(Handle); + + /* If a Close is already in Process, give up */ + if (Socket->SharedData.State == SocketClosed) { + *lpErrno = WSAENOTSOCK; + return SOCKET_ERROR; + } + + /* Set the state to close */ + OldState = Socket->SharedData.State; + Socket->SharedData.State = SocketClosed; + + /* If SO_LINGER is ON and the Socket is connected, we need to disconnect */ + /* FIXME: Should we do this on Datagram Sockets too? */ + if ((OldState == SocketConnected) && (Socket->SharedData.LingerData.l_onoff)) { + ULONG LingerWait; + ULONG SendsInProgress; + ULONG SleepWait; + + /* We need to respect the timeout */ + SleepWait = 100; + LingerWait = Socket->SharedData.LingerData.l_linger * 1000; + + /* Loop until no more sends are pending, within the timeout */ + while (LingerWait) { + + /* Find out how many Sends are in Progress */ + if (GetSocketInformation(Socket, + AFD_INFO_SENDS_IN_PROGRESS, + &SendsInProgress, + NULL)) { + /* Bail out if anything but NO_ERROR */ + LingerWait = 0; + break; + } + + /* Bail out if no more sends are pending */ + if (!SendsInProgress) break; + + /* + * We have to execute a sleep, so it's kind of like + * a block. If the socket is Nonblock, we cannot + * go on since asyncronous operation is expected + * and we cannot offer it + */ + if (Socket->SharedData.NonBlocking) { + Socket->SharedData.State = OldState; + *lpErrno = WSAEWOULDBLOCK; + return SOCKET_ERROR; + } + + /* Now we can sleep, and decrement the linger wait */ + /* + * FIXME: It seems Windows does some funky acceleration + * since the waiting seems to be longer and longer. I + * don't think this improves performance so much, so we + * wait a fixed time instead. + */ + Sleep(SleepWait); + LingerWait -= SleepWait; + } + + /* + * We have reached the timeout or sends are over. + * Disconnect if the timeout has been reached. + */ + if (LingerWait <= 0) { + + DisconnectInfo.Timeout = RtlConvertLongToLargeInteger(0); + DisconnectInfo.DisconnectType = AFD_DISCONNECT_ABORT; + + /* Send IOCTL */ + Status = NtDeviceIoControlFile((HANDLE)Handle, + SockEvent, + NULL, + NULL, + &IoStatusBlock, + IOCTL_AFD_DISCONNECT, + &DisconnectInfo, + sizeof(DisconnectInfo), + NULL, + 0); + + /* Wait for return */ + if (Status == STATUS_PENDING) { + WaitForSingleObject(SockEvent, INFINITE); + } + } + } + + /* FIXME: We should notify the Helper DLL of WSH_NOTIFY_CLOSE */ + + /* Cleanup Time! */ + Socket->HelperContext = NULL; + Socket->SharedData.AsyncDisabledEvents = -1; + NtClose(Socket->TdiAddressHandle); + Socket->TdiAddressHandle = NULL; + NtClose(Socket->TdiConnectionHandle); + Socket->TdiConnectionHandle = NULL; + + /* Close the handle */ + NtClose((HANDLE)Handle); + + return NO_ERROR; +} + +INT +WSPAPI +WSPBind( + SOCKET Handle, + const struct sockaddr *SocketAddress, + int SocketAddressLength, + LPINT lpErrno) +/* + * FUNCTION: Associates a local address with a socket + * ARGUMENTS: + * s = Socket descriptor + * name = Pointer to local address + * namelen = Length of name + * lpErrno = Address of buffer for error information + * RETURNS: + * 0, or SOCKET_ERROR if the socket could not be bound + */ +{ + IO_STATUS_BLOCK IOSB; + PAFD_BIND_DATA BindData; + PSOCKET_INFORMATION Socket = NULL; + NTSTATUS Status; + UCHAR BindBuffer[0x1A]; + SOCKADDR_INFO SocketInfo; + HANDLE SockEvent; + + Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE, + NULL, 1, FALSE ); + + if( !NT_SUCCESS(Status) ) return -1; + + /* Get the Socket Structure associate to this Socket*/ + Socket = GetSocketStructure(Handle); + + /* Dynamic Structure...ugh */ + BindData = (PAFD_BIND_DATA)BindBuffer; + + /* Set up Address in TDI Format */ + BindData->Address.TAAddressCount = 1; + BindData->Address.Address[0].AddressLength = SocketAddressLength - sizeof(SocketAddress->sa_family); + BindData->Address.Address[0].AddressType = SocketAddress->sa_family; + RtlCopyMemory (BindData->Address.Address[0].Address, + SocketAddress->sa_data, + SocketAddressLength - sizeof(SocketAddress->sa_family)); + + /* Get Address Information */ + Socket->HelperData->WSHGetSockaddrType ((PSOCKADDR)SocketAddress, + SocketAddressLength, + &SocketInfo); + + /* Set the Share Type */ + if (Socket->SharedData.ExclusiveAddressUse) { + BindData->ShareType = AFD_SHARE_EXCLUSIVE; + } + else if (SocketInfo.EndpointInfo == SockaddrEndpointInfoWildcard) { + BindData->ShareType = AFD_SHARE_WILDCARD; + } + else if (Socket->SharedData.ReuseAddresses) { + BindData->ShareType = AFD_SHARE_REUSE; + } else { + BindData->ShareType = AFD_SHARE_UNIQUE; + } + + /* Send IOCTL */ + Status = NtDeviceIoControlFile( (HANDLE)Socket->Handle, + SockEvent, + NULL, + NULL, + &IOSB, + IOCTL_AFD_BIND, + BindData, + 0xA + Socket->SharedData.SizeOfLocalAddress, /* Can't figure out a way to calculate this in C*/ + BindData, + 0xA + Socket->SharedData.SizeOfLocalAddress); /* Can't figure out a way to calculate this C */ + + /* Wait for return */ + if (Status == STATUS_PENDING) { + WaitForSingleObject(SockEvent, INFINITE); + } + + /* Set up Socket Data */ + Socket->SharedData.State = SocketBound; + Socket->TdiAddressHandle = (HANDLE)IOSB.Information; + + NtClose( SockEvent ); + + return MsafdReturnWithErrno + ( IOSB.Status, lpErrno, IOSB.Information, NULL ); +} + +int +WSPAPI +WSPListen( + SOCKET Handle, + int Backlog, + LPINT lpErrno) +{ + IO_STATUS_BLOCK IOSB; + AFD_LISTEN_DATA ListenData; + PSOCKET_INFORMATION Socket = NULL; + HANDLE SockEvent; + NTSTATUS Status; + + Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE, + NULL, 1, FALSE ); + + if( !NT_SUCCESS(Status) ) return -1; + + /* Get the Socket Structure associate to this Socket*/ + Socket = GetSocketStructure(Handle); + + /* Set Up Listen Structure */ + ListenData.UseSAN = FALSE; + ListenData.UseDelayedAcceptance = Socket->SharedData.UseDelayedAcceptance; + ListenData.Backlog = Backlog; + + /* Send IOCTL */ + Status = NtDeviceIoControlFile( (HANDLE)Socket->Handle, + SockEvent, + NULL, + NULL, + &IOSB, + IOCTL_AFD_START_LISTEN, + &ListenData, + sizeof(ListenData), + NULL, + 0); + + /* Wait for return */ + if (Status == STATUS_PENDING) { + WaitForSingleObject(SockEvent, INFINITE); + } + + /* Set to Listening */ + Socket->SharedData.Listening = TRUE; + + NtClose( SockEvent ); + + return MsafdReturnWithErrno + ( IOSB.Status, lpErrno, IOSB.Information, NULL ); +} + + +int +WSPAPI +WSPSelect( + int nfds, + fd_set *readfds, + fd_set *writefds, + fd_set *exceptfds, + struct timeval *timeout, + LPINT lpErrno) +{ + IO_STATUS_BLOCK IOSB; + PAFD_POLL_INFO PollInfo; + NTSTATUS Status; + ULONG HandleCount, OutCount = 0; + ULONG PollBufferSize; + PVOID PollBuffer; + ULONG i, j = 0, x; + HANDLE SockEvent; + BOOL HandleCounted; + + Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE, + NULL, 1, FALSE ); + + if( !NT_SUCCESS(Status) ) return SOCKET_ERROR; + + /* Find out how many sockets we have, and how large the buffer needs + * to be */ + + HandleCount = + ( readfds ? readfds->fd_count : 0 ) + + ( writefds ? writefds->fd_count : 0 ) + + ( exceptfds ? exceptfds->fd_count : 0 ); + + if( HandleCount < 0 || nfds != 0 ) HandleCount = nfds * 3; + + PollBufferSize = sizeof(*PollInfo) + + (HandleCount * sizeof(AFD_HANDLE)); + + AFD_DbgPrint(MID_TRACE,("HandleCount: %d BufferSize: %d\n", + HandleCount, PollBufferSize)); + + /* Allocate */ + PollBuffer = HeapAlloc(GlobalHeap, 0, PollBufferSize); + PollInfo = (PAFD_POLL_INFO)PollBuffer; + + RtlZeroMemory( PollInfo, PollBufferSize ); + + /* Convert Timeout to NT Format */ + if (timeout == NULL) { + PollInfo->Timeout.u.LowPart = -1; + PollInfo->Timeout.u.HighPart = 0x7FFFFFFF; + } else { + PollInfo->Timeout = RtlEnlargedIntegerMultiply + ((timeout->tv_sec * 1000) + timeout->tv_usec, -10000); + } + + /* Number of handles for AFD to Check */ + PollInfo->HandleCount = HandleCount; + PollInfo->Exclusive = FALSE; + + if (readfds != NULL) { + for (i = 0; i < readfds->fd_count; i++, j++) { + PollInfo->Handles[j].Handle = readfds->fd_array[i]; + PollInfo->Handles[j].Events = AFD_EVENT_RECEIVE | + AFD_EVENT_DISCONNECT | + AFD_EVENT_ABORT | + AFD_EVENT_ACCEPT; + } + } + if (writefds != NULL) { + for (i = 0; i < writefds->fd_count; i++, j++) { + PollInfo->Handles[j].Handle = writefds->fd_array[i]; + PollInfo->Handles[j].Events = AFD_EVENT_SEND | + AFD_EVENT_CONNECT; + } + } + if (exceptfds != NULL) { + for (i = 0; i < exceptfds->fd_count; i++, j++) { + PollInfo->Handles[j].Handle = exceptfds->fd_array[i]; + PollInfo->Handles[j].Events = AFD_EVENT_OOB_RECEIVE | + AFD_EVENT_CONNECT_FAIL; + } + } + + /* Send IOCTL */ + Status = NtDeviceIoControlFile( (HANDLE)PollInfo->Handles[0].Handle, + SockEvent, + NULL, + NULL, + &IOSB, + IOCTL_AFD_SELECT, + PollInfo, + PollBufferSize, + PollInfo, + PollBufferSize); + + AFD_DbgPrint(MID_TRACE,("DeviceIoControlFile => %x\n", Status)); + + /* Wait for Completition */ + if (Status == STATUS_PENDING) { + WaitForSingleObject(SockEvent, INFINITE); + } + + /* Clear the Structures */ + if( readfds ) FD_ZERO(readfds); + if( writefds ) FD_ZERO(writefds); + if( exceptfds ) FD_ZERO(exceptfds); + + /* Loop through return structure */ + HandleCount = PollInfo->HandleCount; + + /* Return in FDSET Format */ + for (i = 0; i < HandleCount; i++) { + HandleCounted = FALSE; + for(x = 1; x; x<<=1) { + switch (PollInfo->Handles[i].Events & x) { + case AFD_EVENT_RECEIVE: + case AFD_EVENT_DISCONNECT: + case AFD_EVENT_ABORT: + case AFD_EVENT_ACCEPT: + case AFD_EVENT_CLOSE: + AFD_DbgPrint(MID_TRACE,("Event %x on handle %x\n", + PollInfo->Handles[i].Events, + PollInfo->Handles[i].Handle)); + if (! HandleCounted) { + OutCount++; + HandleCounted = TRUE; + } + if( readfds ) FD_SET(PollInfo->Handles[i].Handle, readfds); + break; + + case AFD_EVENT_SEND: case AFD_EVENT_CONNECT: + AFD_DbgPrint(MID_TRACE,("Event %x on handle %x\n", + PollInfo->Handles[i].Events, + PollInfo->Handles[i].Handle)); + if (! HandleCounted) { + OutCount++; + HandleCounted = TRUE; + } + if( writefds ) FD_SET(PollInfo->Handles[i].Handle, writefds); + break; + + case AFD_EVENT_OOB_RECEIVE: case AFD_EVENT_CONNECT_FAIL: + AFD_DbgPrint(MID_TRACE,("Event %x on handle %x\n", + PollInfo->Handles[i].Events, + PollInfo->Handles[i].Handle)); + if (! HandleCounted) { + OutCount++; + HandleCounted = TRUE; + } + if( exceptfds ) FD_SET(PollInfo->Handles[i].Handle, exceptfds); + break; + } + } + } + + NtClose( SockEvent ); + + AFD_DbgPrint(MID_TRACE,("lpErrno = %x\n", lpErrno)); + + if( lpErrno ) { + switch( IOSB.Status ) { + case STATUS_SUCCESS: + case STATUS_TIMEOUT: *lpErrno = 0; break; + default: *lpErrno = WSAEINVAL; break; + } + } + + AFD_DbgPrint(MID_TRACE,("%d events\n", OutCount)); + + return OutCount; +} + +SOCKET +WSPAPI +WSPAccept( + SOCKET Handle, + struct sockaddr *SocketAddress, + int *SocketAddressLength, + LPCONDITIONPROC lpfnCondition, + DWORD_PTR dwCallbackData, + LPINT lpErrno) +{ + IO_STATUS_BLOCK IOSB; + PAFD_RECEIVED_ACCEPT_DATA ListenReceiveData; + AFD_ACCEPT_DATA AcceptData; + AFD_DEFER_ACCEPT_DATA DeferData; + AFD_PENDING_ACCEPT_DATA PendingAcceptData; + PSOCKET_INFORMATION Socket = NULL; + NTSTATUS Status; + struct fd_set ReadSet; + struct timeval Timeout; + PVOID PendingData = NULL; + ULONG PendingDataLength = 0; + PVOID CalleeDataBuffer; + WSABUF CallerData, CalleeID, CallerID, CalleeData; + PSOCKADDR RemoteAddress = NULL; + GROUP GroupID = 0; + ULONG CallBack; + WSAPROTOCOL_INFOW ProtocolInfo; + SOCKET AcceptSocket; + UCHAR ReceiveBuffer[0x1A]; + HANDLE SockEvent; + + Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE, + NULL, 1, FALSE ); + + if( !NT_SUCCESS(Status) ) { + MsafdReturnWithErrno( Status, lpErrno, 0, NULL ); + return INVALID_SOCKET; + } + + /* Dynamic Structure...ugh */ + ListenReceiveData = (PAFD_RECEIVED_ACCEPT_DATA)ReceiveBuffer; + + /* Get the Socket Structure associate to this Socket*/ + Socket = GetSocketStructure(Handle); + + /* If this is non-blocking, make sure there's something for us to accept */ + FD_ZERO(&ReadSet); + FD_SET(Socket->Handle, &ReadSet); + Timeout.tv_sec=0; + Timeout.tv_usec=0; + + WSPSelect(0, &ReadSet, NULL, NULL, &Timeout, NULL); + + if (ReadSet.fd_array[0] != Socket->Handle) return 0; + + /* Send IOCTL */ + Status = NtDeviceIoControlFile( (HANDLE)Socket->Handle, + SockEvent, + NULL, + NULL, + &IOSB, + IOCTL_AFD_WAIT_FOR_LISTEN, + NULL, + 0, + ListenReceiveData, + 0xA + sizeof(*ListenReceiveData)); + + /* Wait for return */ + if (Status == STATUS_PENDING) { + WaitForSingleObject(SockEvent, INFINITE); + Status = IOSB.Status; + } + + if (!NT_SUCCESS(Status)) { + NtClose( SockEvent ); + MsafdReturnWithErrno( Status, lpErrno, 0, NULL ); + return INVALID_SOCKET; + } + + if (lpfnCondition != NULL) { + if ((Socket->SharedData.ServiceFlags1 & XP1_CONNECT_DATA) != 0) { + /* Find out how much data is pending */ + PendingAcceptData.SequenceNumber = ListenReceiveData->SequenceNumber; + PendingAcceptData.ReturnSize = TRUE; + + /* Send IOCTL */ + Status = NtDeviceIoControlFile( (HANDLE)Socket->Handle, + SockEvent, + NULL, + NULL, + &IOSB, + IOCTL_AFD_GET_PENDING_CONNECT_DATA, + &PendingAcceptData, + sizeof(PendingAcceptData), + &PendingAcceptData, + sizeof(PendingAcceptData)); + + /* Wait for return */ + if (Status == STATUS_PENDING) { + WaitForSingleObject(SockEvent, INFINITE); + Status = IOSB.Status; + } + + if (!NT_SUCCESS(Status)) { + NtClose( SockEvent ); + MsafdReturnWithErrno( Status, lpErrno, 0, NULL ); + return INVALID_SOCKET; + } + + /* How much data to allocate */ + PendingDataLength = IOSB.Information; + + if (PendingDataLength) { + /* Allocate needed space */ + PendingData = HeapAlloc(GlobalHeap, 0, PendingDataLength); + + /* We want the data now */ + PendingAcceptData.ReturnSize = FALSE; + + /* Send IOCTL */ + Status = NtDeviceIoControlFile( (HANDLE)Socket->Handle, + SockEvent, + NULL, + NULL, + &IOSB, + IOCTL_AFD_GET_PENDING_CONNECT_DATA, + &PendingAcceptData, + sizeof(PendingAcceptData), + PendingData, + PendingDataLength); + + /* Wait for return */ + if (Status == STATUS_PENDING) { + WaitForSingleObject(SockEvent, INFINITE); + Status = IOSB.Status; + } + + if (!NT_SUCCESS(Status)) { + NtClose( SockEvent ); + MsafdReturnWithErrno( Status, lpErrno, 0, NULL ); + return INVALID_SOCKET; + } + } + } + + if ((Socket->SharedData.ServiceFlags1 & XP1_QOS_SUPPORTED) != 0) { + /* I don't support this yet */ + } + + /* Build Callee ID */ + CalleeID.buf = (PVOID)Socket->LocalAddress; + CalleeID.len = Socket->SharedData.SizeOfLocalAddress; + + /* Set up Address in SOCKADDR Format */ + RtlCopyMemory (RemoteAddress, + &ListenReceiveData->Address.Address[0].AddressType, + sizeof(*RemoteAddress)); + + /* Build Caller ID */ + CallerID.buf = (PVOID)RemoteAddress; + CallerID.len = sizeof(*RemoteAddress); + + /* Build Caller Data */ + CallerData.buf = PendingData; + CallerData.len = PendingDataLength; + + /* Check if socket supports Conditional Accept */ + if (Socket->SharedData.UseDelayedAcceptance != 0) { + /* Allocate Buffer for Callee Data */ + CalleeDataBuffer = HeapAlloc(GlobalHeap, 0, 4096); + CalleeData.buf = CalleeDataBuffer; + CalleeData.len = 4096; + } else { + /* Nothing */ + CalleeData.buf = 0; + CalleeData.len = 0; + } + + /* Call the Condition Function */ + CallBack = (lpfnCondition)( &CallerID, + CallerData.buf == NULL + ? NULL + : & CallerData, + NULL, + NULL, + &CalleeID, + CalleeData.buf == NULL + ? NULL + : & CalleeData, + &GroupID, + dwCallbackData); + + if (((CallBack == CF_ACCEPT) && GroupID) != 0) { + /* TBD: Check for Validity */ + } + + if (CallBack == CF_ACCEPT) { + + if ((Socket->SharedData.ServiceFlags1 & XP1_QOS_SUPPORTED) != 0) { + /* I don't support this yet */ + } + + if (CalleeData.buf) { + // SockSetConnectData Sockets(SocketID), IOCTL_AFD_SET_CONNECT_DATA, CalleeData.Buffer, CalleeData.BuffSize, 0 + } + + } else { + /* Callback rejected. Build Defer Structure */ + DeferData.SequenceNumber = ListenReceiveData->SequenceNumber; + DeferData.RejectConnection = (CallBack == CF_REJECT); + + /* Send IOCTL */ + Status = NtDeviceIoControlFile( (HANDLE)Socket->Handle, + SockEvent, + NULL, + NULL, + &IOSB, + IOCTL_AFD_DEFER_ACCEPT, + &DeferData, + sizeof(DeferData), + NULL, + 0); + + /* Wait for return */ + if (Status == STATUS_PENDING) { + WaitForSingleObject(SockEvent, INFINITE); + Status = IOSB.Status; + } + + NtClose( SockEvent ); + + if (!NT_SUCCESS(Status)) { + MsafdReturnWithErrno( Status, lpErrno, 0, NULL ); + return INVALID_SOCKET; + } + + if (CallBack == CF_REJECT ) { + *lpErrno = WSAECONNREFUSED; + return INVALID_SOCKET; + } else { + *lpErrno = WSAECONNREFUSED; + return INVALID_SOCKET; + } + } + } + + /* Create a new Socket */ + ProtocolInfo.dwCatalogEntryId = Socket->SharedData.CatalogEntryId; + ProtocolInfo.dwServiceFlags1 = Socket->SharedData.ServiceFlags1; + ProtocolInfo.dwProviderFlags = Socket->SharedData.ProviderFlags; + + AcceptSocket = WSPSocket (Socket->SharedData.AddressFamily, + Socket->SharedData.SocketType, + Socket->SharedData.Protocol, + &ProtocolInfo, + GroupID, + Socket->SharedData.CreateFlags, + NULL); + + /* Set up the Accept Structure */ + AcceptData.ListenHandle = AcceptSocket; + AcceptData.SequenceNumber = ListenReceiveData->SequenceNumber; + + /* Send IOCTL to Accept */ + Status = NtDeviceIoControlFile( (HANDLE)Socket->Handle, + SockEvent, + NULL, + NULL, + &IOSB, + IOCTL_AFD_ACCEPT, + &AcceptData, + sizeof(AcceptData), + NULL, + 0); + + /* Wait for return */ + if (Status == STATUS_PENDING) { + WaitForSingleObject(SockEvent, INFINITE); + Status = IOSB.Status; + } + + if (!NT_SUCCESS(Status)) { + WSPCloseSocket( AcceptSocket, lpErrno ); + MsafdReturnWithErrno( Status, lpErrno, 0, NULL ); + return INVALID_SOCKET; + } + + /* Return Address in SOCKADDR FORMAT */ + if( SocketAddress ) { + RtlCopyMemory (SocketAddress, + &ListenReceiveData->Address.Address[0].AddressType, + sizeof(*RemoteAddress)); + if( SocketAddressLength ) + *SocketAddressLength = + ListenReceiveData->Address.Address[0].AddressLength; + } + + NtClose( SockEvent ); + + /* Re-enable Async Event */ + SockReenableAsyncSelectEvent(Socket, FD_ACCEPT); + + AFD_DbgPrint(MID_TRACE,("Socket %x\n", AcceptSocket)); + + *lpErrno = 0; + + /* Return Socket */ + return AcceptSocket; +} + +int +WSPAPI +WSPConnect( + SOCKET Handle, + const struct sockaddr * SocketAddress, + int SocketAddressLength, + LPWSABUF lpCallerData, + LPWSABUF lpCalleeData, + LPQOS lpSQOS, + LPQOS lpGQOS, + LPINT lpErrno) +{ + IO_STATUS_BLOCK IOSB; + PAFD_CONNECT_INFO ConnectInfo; + PSOCKET_INFORMATION Socket = NULL; + NTSTATUS Status; + UCHAR ConnectBuffer[0x22]; + ULONG ConnectDataLength; + ULONG InConnectDataLength; + INT BindAddressLength; + PSOCKADDR BindAddress; + HANDLE SockEvent; + + Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE, + NULL, 1, FALSE ); + + if( !NT_SUCCESS(Status) ) return -1; + + AFD_DbgPrint(MID_TRACE,("Called\n")); + + /* Get the Socket Structure associate to this Socket*/ + Socket = GetSocketStructure(Handle); + + /* Bind us First */ + if (Socket->SharedData.State == SocketOpen) { + + /* Get the Wildcard Address */ + BindAddressLength = Socket->HelperData->MaxWSAddressLength; + BindAddress = HeapAlloc(GetProcessHeap(), 0, BindAddressLength); + Socket->HelperData->WSHGetWildcardSockaddr (Socket->HelperContext, + BindAddress, + &BindAddressLength); + + /* Bind it */ + WSPBind(Handle, BindAddress, BindAddressLength, NULL); + } + + /* Set the Connect Data */ + if (lpCallerData != NULL) { + ConnectDataLength = lpCallerData->len; + Status = NtDeviceIoControlFile((HANDLE)Handle, + SockEvent, + NULL, + NULL, + &IOSB, + IOCTL_AFD_SET_CONNECT_DATA, + lpCallerData->buf, + ConnectDataLength, + NULL, + 0); + /* Wait for return */ + if (Status == STATUS_PENDING) { + WaitForSingleObject(SockEvent, INFINITE); + Status = IOSB.Status; + } + } + + /* Dynamic Structure...ugh */ + ConnectInfo = (PAFD_CONNECT_INFO)ConnectBuffer; + + /* Set up Address in TDI Format */ + ConnectInfo->RemoteAddress.TAAddressCount = 1; + ConnectInfo->RemoteAddress.Address[0].AddressLength = SocketAddressLength - sizeof(SocketAddress->sa_family); + ConnectInfo->RemoteAddress.Address[0].AddressType = SocketAddress->sa_family; + RtlCopyMemory (ConnectInfo->RemoteAddress.Address[0].Address, + SocketAddress->sa_data, + SocketAddressLength - sizeof(SocketAddress->sa_family)); + + /* + * Disable FD_WRITE and FD_CONNECT + * The latter fixes a race condition where the FD_CONNECT is re-enabled + * at the end of this function right after the Async Thread disables it. + * This should only happen at the *next* WSPConnect + */ + if (Socket->SharedData.AsyncEvents & FD_CONNECT) { + Socket->SharedData.AsyncDisabledEvents |= FD_CONNECT | FD_WRITE; + } + + /* Tell AFD that we want Connection Data back, have it allocate a buffer */ + if (lpCalleeData != NULL) { + InConnectDataLength = lpCalleeData->len; + Status = NtDeviceIoControlFile((HANDLE)Handle, + SockEvent, + NULL, + NULL, + &IOSB, + IOCTL_AFD_SET_CONNECT_DATA_SIZE, + &InConnectDataLength, + sizeof(InConnectDataLength), + NULL, + 0); + + /* Wait for return */ + if (Status == STATUS_PENDING) { + WaitForSingleObject(SockEvent, INFINITE); + Status = IOSB.Status; + } + } + + /* AFD doesn't seem to care if these are invalid, but let's 0 them anyways */ + ConnectInfo->Root = 0; + ConnectInfo->UseSAN = FALSE; + ConnectInfo->Unknown = 0; + + /* FIXME: Handle Async Connect */ + if (Socket->SharedData.NonBlocking) { + AFD_DbgPrint(MIN_TRACE, ("Async Connect UNIMPLEMENTED!\n")); + } + + /* Send IOCTL */ + Status = NtDeviceIoControlFile((HANDLE)Handle, + SockEvent, + NULL, + NULL, + &IOSB, + IOCTL_AFD_CONNECT, + ConnectInfo, + 0x22, + NULL, + 0); + /* Wait for return */ + if (Status == STATUS_PENDING) { + WaitForSingleObject(SockEvent, INFINITE); + Status = IOSB.Status; + } + + /* Get any pending connect data */ + if (lpCalleeData != NULL) { + Status = NtDeviceIoControlFile((HANDLE)Handle, + SockEvent, + NULL, + NULL, + &IOSB, + IOCTL_AFD_GET_CONNECT_DATA, + NULL, + 0, + lpCalleeData->buf, + lpCalleeData->len); + /* Wait for return */ + if (Status == STATUS_PENDING) { + WaitForSingleObject(SockEvent, INFINITE); + Status = IOSB.Status; + } + } + + /* Re-enable Async Event */ + SockReenableAsyncSelectEvent(Socket, FD_WRITE); + + /* FIXME: THIS IS NOT RIGHT!!! HACK HACK HACK! */ + SockReenableAsyncSelectEvent(Socket, FD_CONNECT); + + AFD_DbgPrint(MID_TRACE,("Ending\n")); + + NtClose( SockEvent ); + + return MsafdReturnWithErrno( IOSB.Status, lpErrno, 0, NULL ); +} +int +WSPAPI +WSPShutdown( + SOCKET Handle, + int HowTo, + LPINT lpErrno) + +{ + IO_STATUS_BLOCK IOSB; + AFD_DISCONNECT_INFO DisconnectInfo; + PSOCKET_INFORMATION Socket = NULL; + NTSTATUS Status; + HANDLE SockEvent; + + Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE, + NULL, 1, FALSE ); + + if( !NT_SUCCESS(Status) ) return -1; + + AFD_DbgPrint(MID_TRACE,("Called\n")); + + /* Get the Socket Structure associate to this Socket*/ + Socket = GetSocketStructure(Handle); + + /* Set AFD Disconnect Type */ + switch (HowTo) { + + case SD_RECEIVE: + DisconnectInfo.DisconnectType = AFD_DISCONNECT_RECV; + Socket->SharedData.ReceiveShutdown = TRUE; + break; + + case SD_SEND: + DisconnectInfo.DisconnectType= AFD_DISCONNECT_SEND; + Socket->SharedData.SendShutdown = TRUE; + break; + + case SD_BOTH: + DisconnectInfo.DisconnectType = AFD_DISCONNECT_RECV | AFD_DISCONNECT_SEND; + Socket->SharedData.ReceiveShutdown = TRUE; + Socket->SharedData.SendShutdown = TRUE; + break; + } + + DisconnectInfo.Timeout = RtlConvertLongToLargeInteger(-1); + + /* Send IOCTL */ + Status = NtDeviceIoControlFile((HANDLE)Handle, + SockEvent, + NULL, + NULL, + &IOSB, + IOCTL_AFD_DISCONNECT, + &DisconnectInfo, + sizeof(DisconnectInfo), + NULL, + 0); + + /* Wait for return */ + if (Status == STATUS_PENDING) { + WaitForSingleObject(SockEvent, INFINITE); + } + + AFD_DbgPrint(MID_TRACE,("Ending\n")); + + NtClose( SockEvent ); + + return MsafdReturnWithErrno( IOSB.Status, lpErrno, 0, NULL ); +} + + +INT +WSPAPI +WSPGetSockName( + IN SOCKET Handle, + OUT LPSOCKADDR Name, + IN OUT LPINT NameLength, + OUT LPINT lpErrno) +{ + IO_STATUS_BLOCK IOSB; + ULONG TdiAddressSize; + PTDI_ADDRESS_INFO TdiAddress; + PTRANSPORT_ADDRESS SocketAddress; + PSOCKET_INFORMATION Socket = NULL; + NTSTATUS Status; + HANDLE SockEvent; + + Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE, + NULL, 1, FALSE ); + + if( !NT_SUCCESS(Status) ) return SOCKET_ERROR; + + /* Get the Socket Structure associate to this Socket*/ + Socket = GetSocketStructure(Handle); + + /* Allocate a buffer for the address */ + TdiAddressSize = FIELD_OFFSET(TDI_ADDRESS_INFO, + Address.Address[0].Address) + + Socket->SharedData.SizeOfLocalAddress; + TdiAddress = HeapAlloc(GlobalHeap, 0, TdiAddressSize); + + if ( TdiAddress == NULL ) { + NtClose( SockEvent ); + *lpErrno = WSAENOBUFS; + return SOCKET_ERROR; + } + + SocketAddress = &TdiAddress->Address; + + /* Send IOCTL */ + Status = NtDeviceIoControlFile( (HANDLE)Socket->Handle, + SockEvent, + NULL, + NULL, + &IOSB, + IOCTL_AFD_GET_SOCK_NAME, + NULL, + 0, + TdiAddress, + TdiAddressSize); + + /* Wait for return */ + if (Status == STATUS_PENDING) { + WaitForSingleObject(SockEvent, INFINITE); + Status = IOSB.Status; + } + + NtClose( SockEvent ); + + if (NT_SUCCESS(Status)) { + if (*NameLength >= SocketAddress->Address[0].AddressLength) { + Name->sa_family = SocketAddress->Address[0].AddressType; + RtlCopyMemory (Name->sa_data, + SocketAddress->Address[0].Address, + SocketAddress->Address[0].AddressLength); + *NameLength = 2 + SocketAddress->Address[0].AddressLength; + AFD_DbgPrint + (MID_TRACE, + ("NameLength %d Address: %x Port %x\n", + *NameLength, + ((struct sockaddr_in *)Name)->sin_addr.s_addr, + ((struct sockaddr_in *)Name)->sin_port)); + HeapFree(GlobalHeap, 0, TdiAddress); + return 0; + } else { + HeapFree(GlobalHeap, 0, TdiAddress); + *lpErrno = WSAEFAULT; + return SOCKET_ERROR; + } + } + + return MsafdReturnWithErrno + ( IOSB.Status, lpErrno, 0, NULL ); +} + + +INT +WSPAPI +WSPGetPeerName( + IN SOCKET s, + OUT LPSOCKADDR Name, + IN OUT LPINT NameLength, + OUT LPINT lpErrno) +{ + IO_STATUS_BLOCK IOSB; + ULONG TdiAddressSize; + PTDI_ADDRESS_INFO TdiAddress; + PTRANSPORT_ADDRESS SocketAddress; + PSOCKET_INFORMATION Socket = NULL; + NTSTATUS Status; + HANDLE SockEvent; + + Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE, + NULL, 1, FALSE ); + + if( !NT_SUCCESS(Status) ) return SOCKET_ERROR; + + /* Get the Socket Structure associate to this Socket*/ + Socket = GetSocketStructure(s); + + /* Allocate a buffer for the address */ + TdiAddressSize = FIELD_OFFSET(TDI_ADDRESS_INFO, + Address.Address[0].Address) + + Socket->SharedData.SizeOfLocalAddress; + TdiAddress = HeapAlloc(GlobalHeap, 0, TdiAddressSize); + + if ( TdiAddress == NULL ) { + NtClose( SockEvent ); + *lpErrno = WSAENOBUFS; + return SOCKET_ERROR; + } + + SocketAddress = &TdiAddress->Address; + + /* Send IOCTL */ + Status = NtDeviceIoControlFile( (HANDLE)Socket->Handle, + SockEvent, + NULL, + NULL, + &IOSB, + IOCTL_AFD_GET_PEER_NAME, + NULL, + 0, + TdiAddress, + TdiAddressSize); + + /* Wait for return */ + if (Status == STATUS_PENDING) { + WaitForSingleObject(SockEvent, INFINITE); + Status = IOSB.Status; + } + + NtClose( SockEvent ); + + if (NT_SUCCESS(Status)) { + if (*NameLength >= SocketAddress->Address[0].AddressLength) { + Name->sa_family = SocketAddress->Address[0].AddressType; + RtlCopyMemory (Name->sa_data, + SocketAddress->Address[0].Address, + SocketAddress->Address[0].AddressLength); + *NameLength = 2 + SocketAddress->Address[0].AddressLength; + AFD_DbgPrint + (MID_TRACE, + ("NameLength %d Address: %s Port %x\n", + *NameLength, + ((struct sockaddr_in *)Name)->sin_addr.s_addr, + ((struct sockaddr_in *)Name)->sin_port)); + HeapFree(GlobalHeap, 0, TdiAddress); + return 0; + } else { + HeapFree(GlobalHeap, 0, TdiAddress); + *lpErrno = WSAEFAULT; + return SOCKET_ERROR; + } + } + + return MsafdReturnWithErrno + ( IOSB.Status, lpErrno, 0, NULL ); +} + +INT +WSPAPI +WSPIoctl( + IN SOCKET Handle, + IN DWORD dwIoControlCode, + IN LPVOID lpvInBuffer, + IN DWORD cbInBuffer, + OUT LPVOID lpvOutBuffer, + IN DWORD cbOutBuffer, + OUT LPDWORD lpcbBytesReturned, + IN LPWSAOVERLAPPED lpOverlapped, + IN LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, + IN LPWSATHREADID lpThreadId, + OUT LPINT lpErrno) +{ + PSOCKET_INFORMATION Socket = NULL; + + /* Get the Socket Structure associate to this Socket*/ + Socket = GetSocketStructure(Handle); + + switch( dwIoControlCode ) { + case FIONBIO: + if( cbInBuffer < sizeof(INT) ) return SOCKET_ERROR; + Socket->SharedData.NonBlocking = *((PINT)lpvInBuffer) ? 1 : 0; + AFD_DbgPrint(MID_TRACE,("[%x] Set nonblocking %d\n", + Handle, Socket->SharedData.NonBlocking)); + return 0; + + default: + *lpErrno = WSAEINVAL; + return SOCKET_ERROR; + } +} + + +INT +WSPAPI +WSPGetSockOpt( + IN SOCKET Handle, + IN INT Level, + IN INT OptionName, + OUT CHAR FAR* OptionValue, + IN OUT LPINT OptionLength, + OUT LPINT lpErrno) +{ + PSOCKET_INFORMATION Socket = NULL; + PVOID Buffer; + INT BufferSize; + BOOLEAN BoolBuffer; + + /* Get the Socket Structure associate to this Socket*/ + Socket = GetSocketStructure(Handle); + if (Socket == NULL) + { + *lpErrno = WSAENOTSOCK; + return SOCKET_ERROR; + } + + AFD_DbgPrint(MID_TRACE, ("Called\n")); + + switch (Level) + { + case SOL_SOCKET: + switch (OptionName) + { + case SO_TYPE: + Buffer = &Socket->SharedData.SocketType; + BufferSize = sizeof(INT); + break; + + case SO_RCVBUF: + Buffer = &Socket->SharedData.SizeOfRecvBuffer; + BufferSize = sizeof(INT); + break; + + case SO_SNDBUF: + Buffer = &Socket->SharedData.SizeOfSendBuffer; + BufferSize = sizeof(INT); + break; + + case SO_ACCEPTCONN: + BoolBuffer = Socket->SharedData.Listening; + Buffer = &BoolBuffer; + BufferSize = sizeof(BOOLEAN); + break; + + case SO_BROADCAST: + BoolBuffer = Socket->SharedData.Broadcast; + Buffer = &BoolBuffer; + BufferSize = sizeof(BOOLEAN); + break; + + case SO_DEBUG: + BoolBuffer = Socket->SharedData.Debug; + Buffer = &BoolBuffer; + BufferSize = sizeof(BOOLEAN); + break; + + /* case SO_CONDITIONAL_ACCEPT: */ + case SO_DONTLINGER: + case SO_DONTROUTE: + case SO_ERROR: + case SO_GROUP_ID: + case SO_GROUP_PRIORITY: + case SO_KEEPALIVE: + case SO_LINGER: + case SO_MAX_MSG_SIZE: + case SO_OOBINLINE: + case SO_PROTOCOL_INFO: + case SO_REUSEADDR: + AFD_DbgPrint(MID_TRACE, ("Unimplemented option (%x)\n", + OptionName)); + + default: + *lpErrno = WSAEINVAL; + return SOCKET_ERROR; + } + + if (*OptionLength < BufferSize) + { + *lpErrno = WSAEFAULT; + *OptionLength = BufferSize; + return SOCKET_ERROR; + } + RtlCopyMemory(OptionValue, Buffer, BufferSize); + + return 0; + + case IPPROTO_TCP: /* FIXME */ + default: + *lpErrno = WSAEINVAL; + return SOCKET_ERROR; + } +} + + +INT +WSPAPI +WSPStartup( + IN WORD wVersionRequested, + OUT LPWSPDATA lpWSPData, + IN LPWSAPROTOCOL_INFOW lpProtocolInfo, + IN WSPUPCALLTABLE UpcallTable, + OUT LPWSPPROC_TABLE lpProcTable) +/* + * FUNCTION: Initialize service provider for a client + * ARGUMENTS: + * wVersionRequested = Highest WinSock SPI version that the caller can use + * lpWSPData = Address of WSPDATA structure to initialize + * lpProtocolInfo = Pointer to structure that defines the desired protocol + * UpcallTable = Pointer to upcall table of the WinSock DLL + * lpProcTable = Address of procedure table to initialize + * RETURNS: + * Status of operation + */ +{ + NTSTATUS Status; + + AFD_DbgPrint(MAX_TRACE, ("wVersionRequested (0x%X) \n", wVersionRequested)); + + Status = NO_ERROR; + + Upcalls = UpcallTable; + + if (Status == NO_ERROR) { + lpProcTable->lpWSPAccept = WSPAccept; + lpProcTable->lpWSPAddressToString = WSPAddressToString; + lpProcTable->lpWSPAsyncSelect = WSPAsyncSelect; + lpProcTable->lpWSPBind = WSPBind; + lpProcTable->lpWSPCancelBlockingCall = WSPCancelBlockingCall; + lpProcTable->lpWSPCleanup = WSPCleanup; + lpProcTable->lpWSPCloseSocket = WSPCloseSocket; + lpProcTable->lpWSPConnect = WSPConnect; + lpProcTable->lpWSPDuplicateSocket = WSPDuplicateSocket; + lpProcTable->lpWSPEnumNetworkEvents = WSPEnumNetworkEvents; + lpProcTable->lpWSPEventSelect = WSPEventSelect; + lpProcTable->lpWSPGetOverlappedResult = WSPGetOverlappedResult; + lpProcTable->lpWSPGetPeerName = WSPGetPeerName; + lpProcTable->lpWSPGetSockName = WSPGetSockName; + lpProcTable->lpWSPGetSockOpt = WSPGetSockOpt; + lpProcTable->lpWSPGetQOSByName = WSPGetQOSByName; + lpProcTable->lpWSPIoctl = WSPIoctl; + lpProcTable->lpWSPJoinLeaf = WSPJoinLeaf; + lpProcTable->lpWSPListen = WSPListen; + lpProcTable->lpWSPRecv = WSPRecv; + lpProcTable->lpWSPRecvDisconnect = WSPRecvDisconnect; + lpProcTable->lpWSPRecvFrom = WSPRecvFrom; + lpProcTable->lpWSPSelect = WSPSelect; + lpProcTable->lpWSPSend = WSPSend; + lpProcTable->lpWSPSendDisconnect = WSPSendDisconnect; + lpProcTable->lpWSPSendTo = WSPSendTo; + lpProcTable->lpWSPSetSockOpt = WSPSetSockOpt; + lpProcTable->lpWSPShutdown = WSPShutdown; + lpProcTable->lpWSPSocket = WSPSocket; + lpProcTable->lpWSPStringToAddress = WSPStringToAddress; + + lpWSPData->wVersion = MAKEWORD(2, 2); + lpWSPData->wHighVersion = MAKEWORD(2, 2); + } + + AFD_DbgPrint(MAX_TRACE, ("Status (%d).\n", Status)); + + return Status; +} + + +INT +WSPAPI +WSPCleanup( + OUT LPINT lpErrno) +/* + * FUNCTION: Cleans up service provider for a client + * ARGUMENTS: + * lpErrno = Address of buffer for error information + * RETURNS: + * 0 if successful, or SOCKET_ERROR if not + */ +{ + AFD_DbgPrint(MAX_TRACE, ("\n")); + + + AFD_DbgPrint(MAX_TRACE, ("Leaving.\n")); + + *lpErrno = NO_ERROR; + + return 0; +} + + + +int +GetSocketInformation( + PSOCKET_INFORMATION Socket, + ULONG AfdInformationClass, + PULONG Ulong OPTIONAL, + PLARGE_INTEGER LargeInteger OPTIONAL) +{ + IO_STATUS_BLOCK IOSB; + AFD_INFO InfoData; + NTSTATUS Status; + HANDLE SockEvent; + + Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE, + NULL, 1, FALSE ); + + if( !NT_SUCCESS(Status) ) return -1; + + /* Set Info Class */ + InfoData.InformationClass = AfdInformationClass; + + /* Send IOCTL */ + Status = NtDeviceIoControlFile( (HANDLE)Socket->Handle, + SockEvent, + NULL, + NULL, + &IOSB, + IOCTL_AFD_GET_INFO, + &InfoData, + sizeof(InfoData), + &InfoData, + sizeof(InfoData)); + + /* Wait for return */ + if (Status == STATUS_PENDING) { + WaitForSingleObject(SockEvent, INFINITE); + } + + /* Return Information */ + *Ulong = InfoData.Information.Ulong; + if (LargeInteger != NULL) { + *LargeInteger = InfoData.Information.LargeInteger; + } + + NtClose( SockEvent ); + + return 0; + +} + + +int +SetSocketInformation( + PSOCKET_INFORMATION Socket, + ULONG AfdInformationClass, + PULONG Ulong OPTIONAL, + PLARGE_INTEGER LargeInteger OPTIONAL) +{ + IO_STATUS_BLOCK IOSB; + AFD_INFO InfoData; + NTSTATUS Status; + HANDLE SockEvent; + + Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE, + NULL, 1, FALSE ); + + if( !NT_SUCCESS(Status) ) return -1; + + /* Set Info Class */ + InfoData.InformationClass = AfdInformationClass; + + /* Set Information */ + InfoData.Information.Ulong = *Ulong; + if (LargeInteger != NULL) { + InfoData.Information.LargeInteger = *LargeInteger; + } + + AFD_DbgPrint(MID_TRACE,("XXX Info %x (Data %x)\n", + AfdInformationClass, *Ulong)); + + /* Send IOCTL */ + Status = NtDeviceIoControlFile( (HANDLE)Socket->Handle, + SockEvent, + NULL, + NULL, + &IOSB, + IOCTL_AFD_GET_INFO, + &InfoData, + sizeof(InfoData), + NULL, + 0); + + /* Wait for return */ + if (Status == STATUS_PENDING) { + WaitForSingleObject(SockEvent, INFINITE); + } + + NtClose( SockEvent ); + + return 0; + +} + +PSOCKET_INFORMATION +GetSocketStructure( + SOCKET Handle) +{ + ULONG i; + + for (i=0; iHandle == Handle) { + return Sockets[i]; + } + } + return 0; +} + +int CreateContext(PSOCKET_INFORMATION Socket) +{ + IO_STATUS_BLOCK IOSB; + SOCKET_CONTEXT ContextData; + NTSTATUS Status; + HANDLE SockEvent; + + Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE, + NULL, 1, FALSE ); + + if( !NT_SUCCESS(Status) ) return -1; + + /* Create Context */ + ContextData.SharedData = Socket->SharedData; + ContextData.SizeOfHelperData = 0; + RtlCopyMemory (&ContextData.LocalAddress, + Socket->LocalAddress, + Socket->SharedData.SizeOfLocalAddress); + RtlCopyMemory (&ContextData.RemoteAddress, + Socket->RemoteAddress, + Socket->SharedData.SizeOfRemoteAddress); + + /* Send IOCTL */ + Status = NtDeviceIoControlFile( (HANDLE)Socket->Handle, + SockEvent, + NULL, + NULL, + &IOSB, + IOCTL_AFD_SET_CONTEXT, + &ContextData, + sizeof(ContextData), + NULL, + 0); + + /* Wait for Completition */ + if (Status == STATUS_PENDING) { + WaitForSingleObject(SockEvent, INFINITE); + } + + NtClose( SockEvent ); + + return 0; +} + +BOOLEAN SockCreateOrReferenceAsyncThread(VOID) +{ + HANDLE hAsyncThread; + DWORD AsyncThreadId; + HANDLE AsyncEvent; + OBJECT_HANDLE_ATTRIBUTE_INFORMATION HandleFlags; + NTSTATUS Status; + + /* Check if the Thread Already Exists */ + if (SockAsyncThreadRefCount) { + return TRUE; + } + + /* Create the Completion Port */ + if (!SockAsyncCompletionPort) { + Status = NtCreateIoCompletion(&SockAsyncCompletionPort, + IO_COMPLETION_ALL_ACCESS, + NULL, + 2); // Allow 2 threads only + + /* Protect Handle */ + HandleFlags.ProtectFromClose = TRUE; + HandleFlags.Inherit = FALSE; + Status = NtSetInformationObject(SockAsyncCompletionPort, + ObjectHandleInformation, + &HandleFlags, + sizeof(HandleFlags)); + } + + /* Create the Async Event */ + Status = NtCreateEvent(&AsyncEvent, + EVENT_ALL_ACCESS, + NULL, + NotificationEvent, + FALSE); + + /* Create the Async Thread */ + hAsyncThread = CreateThread(NULL, + 0, + (LPTHREAD_START_ROUTINE)SockAsyncThread, + NULL, + 0, + &AsyncThreadId); + + /* Close the Handle */ + NtClose(hAsyncThread); + + /* Increase the Reference Count */ + SockAsyncThreadRefCount++; + return TRUE; +} + +int SockAsyncThread(PVOID ThreadParam) +{ + PVOID AsyncContext; + PASYNC_COMPLETION_ROUTINE AsyncCompletionRoutine; + IO_STATUS_BLOCK IOSB; + NTSTATUS Status; + + /* Make the Thread Higher Priority */ + SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL); + + /* Do a KQUEUE/WorkItem Style Loop, thanks to IoCompletion Ports */ + do { + Status = NtRemoveIoCompletion (SockAsyncCompletionPort, + (PVOID*)&AsyncCompletionRoutine, + &AsyncContext, + &IOSB, + NULL); + + /* Call the Async Function */ + if (NT_SUCCESS(Status)) { + (*AsyncCompletionRoutine)(AsyncContext, &IOSB); + } else { + /* It Failed, sleep for a second */ + Sleep(1000); + } + } while ((Status != STATUS_TIMEOUT)); + + /* The Thread has Ended */ + return 0; +} + +BOOLEAN SockGetAsyncSelectHelperAfdHandle(VOID) +{ + UNICODE_STRING AfdHelper; + OBJECT_ATTRIBUTES ObjectAttributes; + IO_STATUS_BLOCK IoSb; + NTSTATUS Status; + FILE_COMPLETION_INFORMATION CompletionInfo; + OBJECT_HANDLE_ATTRIBUTE_INFORMATION HandleFlags; + + /* First, make sure we're not already intialized */ + if (SockAsyncHelperAfdHandle) { + return TRUE; + } + + /* Set up Handle Name and Object */ + RtlInitUnicodeString(&AfdHelper, L"\\Device\\Afd\\AsyncSelectHlp" ); + InitializeObjectAttributes(&ObjectAttributes, + &AfdHelper, + OBJ_INHERIT | OBJ_CASE_INSENSITIVE, + NULL, + NULL); + + /* Open the Handle to AFD */ + Status = NtCreateFile(&SockAsyncHelperAfdHandle, + GENERIC_READ | GENERIC_WRITE | SYNCHRONIZE, + &ObjectAttributes, + &IoSb, + NULL, + 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, + FILE_OPEN_IF, + 0, + NULL, + 0); + + /* + * Now Set up the Completion Port Information + * This means that whenever a Poll is finished, the routine will be executed + */ + CompletionInfo.Port = SockAsyncCompletionPort; + CompletionInfo.Key = SockAsyncSelectCompletionRoutine; + Status = NtSetInformationFile(SockAsyncHelperAfdHandle, + &IoSb, + &CompletionInfo, + sizeof(CompletionInfo), + FileCompletionInformation); + + + /* Protect the Handle */ + HandleFlags.ProtectFromClose = TRUE; + HandleFlags.Inherit = FALSE; + Status = NtSetInformationObject(SockAsyncCompletionPort, + ObjectHandleInformation, + &HandleFlags, + sizeof(HandleFlags)); + + + /* Set this variable to true so that Send/Recv/Accept will know wether to renable disabled events */ + SockAsyncSelectCalled = TRUE; + return TRUE; +} + +VOID SockAsyncSelectCompletionRoutine(PVOID Context, PIO_STATUS_BLOCK IoStatusBlock) +{ + + PASYNC_DATA AsyncData = Context; + PSOCKET_INFORMATION Socket; + ULONG x; + + /* Get the Socket */ + Socket = AsyncData->ParentSocket; + + /* Check if the Sequence Number Changed behind our back */ + if (AsyncData->SequenceNumber != Socket->SharedData.SequenceNumber ){ + return; + } + + /* Check we were manually called b/c of a failure */ + if (!NT_SUCCESS(IoStatusBlock->Status)) { + /* FIXME: Perform Upcall */ + return; + } + + for (x = 1; x; x<<=1) { + switch (AsyncData->AsyncSelectInfo.Handles[0].Events & x) { + case AFD_EVENT_RECEIVE: + if (0 != (Socket->SharedData.AsyncEvents & FD_READ) && 0 == (Socket->SharedData.AsyncDisabledEvents & FD_READ)) { + /* Make the Notifcation */ + (Upcalls.lpWPUPostMessage)(Socket->SharedData.hWnd, + Socket->SharedData.wMsg, + Socket->Handle, + WSAMAKESELECTREPLY(FD_READ, 0)); + /* Disable this event until the next read(); */ + Socket->SharedData.AsyncDisabledEvents |= FD_READ; + } + break; + + case AFD_EVENT_OOB_RECEIVE: + if (0 != (Socket->SharedData.AsyncEvents & FD_OOB) && 0 == (Socket->SharedData.AsyncDisabledEvents & FD_OOB)) { + /* Make the Notifcation */ + (Upcalls.lpWPUPostMessage)(Socket->SharedData.hWnd, + Socket->SharedData.wMsg, + Socket->Handle, + WSAMAKESELECTREPLY(FD_OOB, 0)); + /* Disable this event until the next read(); */ + Socket->SharedData.AsyncDisabledEvents |= FD_OOB; + } + break; + + case AFD_EVENT_SEND: + if (0 != (Socket->SharedData.AsyncEvents & FD_WRITE) && 0 == (Socket->SharedData.AsyncDisabledEvents & FD_WRITE)) { + /* Make the Notifcation */ + (Upcalls.lpWPUPostMessage)(Socket->SharedData.hWnd, + Socket->SharedData.wMsg, + Socket->Handle, + WSAMAKESELECTREPLY(FD_WRITE, 0)); + /* Disable this event until the next write(); */ + Socket->SharedData.AsyncDisabledEvents |= FD_WRITE; + } + break; + + /* FIXME: THIS IS NOT RIGHT!!! HACK HACK HACK! */ + case AFD_EVENT_CONNECT: + if (0 != (Socket->SharedData.AsyncEvents & FD_CONNECT) && 0 == (Socket->SharedData.AsyncDisabledEvents & FD_CONNECT)) { + /* Make the Notifcation */ + (Upcalls.lpWPUPostMessage)(Socket->SharedData.hWnd, + Socket->SharedData.wMsg, + Socket->Handle, + WSAMAKESELECTREPLY(FD_CONNECT, 0)); + /* Disable this event forever; */ + Socket->SharedData.AsyncDisabledEvents |= FD_CONNECT; + } + break; + + case AFD_EVENT_ACCEPT: + if (0 != (Socket->SharedData.AsyncEvents & FD_ACCEPT) && 0 == (Socket->SharedData.AsyncDisabledEvents & FD_ACCEPT)) { + /* Make the Notifcation */ + (Upcalls.lpWPUPostMessage)(Socket->SharedData.hWnd, + Socket->SharedData.wMsg, + Socket->Handle, + WSAMAKESELECTREPLY(FD_ACCEPT, 0)); + /* Disable this event until the next accept(); */ + Socket->SharedData.AsyncDisabledEvents |= FD_ACCEPT; + } + break; + + case AFD_EVENT_DISCONNECT: + case AFD_EVENT_ABORT: + case AFD_EVENT_CLOSE: + if (0 != (Socket->SharedData.AsyncEvents & FD_CLOSE) && 0 == (Socket->SharedData.AsyncDisabledEvents & FD_CLOSE)) { + /* Make the Notifcation */ + (Upcalls.lpWPUPostMessage)(Socket->SharedData.hWnd, + Socket->SharedData.wMsg, + Socket->Handle, + WSAMAKESELECTREPLY(FD_CLOSE, 0)); + /* Disable this event forever; */ + Socket->SharedData.AsyncDisabledEvents |= FD_CLOSE; + } + break; + + /* FIXME: Support QOS */ + } + } + + /* Check if there are any events left for us to check */ + if ((Socket->SharedData.AsyncEvents & (~Socket->SharedData.AsyncDisabledEvents)) == 0 ) { + return; + } + + /* Keep Polling */ + SockProcessAsyncSelect(Socket, AsyncData); + return; +} + +VOID SockProcessAsyncSelect(PSOCKET_INFORMATION Socket, PASYNC_DATA AsyncData) +{ + + ULONG lNetworkEvents; + NTSTATUS Status; + + /* Set up the Async Data Event Info */ + AsyncData->AsyncSelectInfo.Timeout.HighPart = 0x7FFFFFFF; + AsyncData->AsyncSelectInfo.Timeout.LowPart = 0xFFFFFFFF; + AsyncData->AsyncSelectInfo.HandleCount = 1; + AsyncData->AsyncSelectInfo.Exclusive = TRUE; + AsyncData->AsyncSelectInfo.Handles[0].Handle = Socket->Handle; + AsyncData->AsyncSelectInfo.Handles[0].Events = 0; + + /* Remove unwanted events */ + lNetworkEvents = Socket->SharedData.AsyncEvents & (~Socket->SharedData.AsyncDisabledEvents); + + /* Set Events to wait for */ + if (lNetworkEvents & FD_READ) { + AsyncData->AsyncSelectInfo.Handles[0].Events |= AFD_EVENT_RECEIVE; + } + + if (lNetworkEvents & FD_WRITE) { + AsyncData->AsyncSelectInfo.Handles[0].Events |= AFD_EVENT_SEND; + } + + if (lNetworkEvents & FD_OOB) { + AsyncData->AsyncSelectInfo.Handles[0].Events |= AFD_EVENT_OOB_RECEIVE; + } + + if (lNetworkEvents & FD_ACCEPT) { + AsyncData->AsyncSelectInfo.Handles[0].Events |= AFD_EVENT_ACCEPT; + } + + /* FIXME: THIS IS NOT RIGHT!!! HACK HACK HACK! */ + if (lNetworkEvents & FD_CONNECT) { + AsyncData->AsyncSelectInfo.Handles[0].Events |= AFD_EVENT_CONNECT | AFD_EVENT_CONNECT_FAIL; + } + + if (lNetworkEvents & FD_CLOSE) { + AsyncData->AsyncSelectInfo.Handles[0].Events |= AFD_EVENT_DISCONNECT | AFD_EVENT_ABORT; + } + + if (lNetworkEvents & FD_QOS) { + AsyncData->AsyncSelectInfo.Handles[0].Events |= AFD_EVENT_QOS; + } + + if (lNetworkEvents & FD_GROUP_QOS) { + AsyncData->AsyncSelectInfo.Handles[0].Events |= AFD_EVENT_GROUP_QOS; + } + + /* Send IOCTL */ + Status = NtDeviceIoControlFile (SockAsyncHelperAfdHandle, + NULL, + NULL, + AsyncData, + &AsyncData->IoStatusBlock, + IOCTL_AFD_SELECT, + &AsyncData->AsyncSelectInfo, + sizeof(AsyncData->AsyncSelectInfo), + &AsyncData->AsyncSelectInfo, + sizeof(AsyncData->AsyncSelectInfo)); + + /* I/O Manager Won't call the completion routine, let's do it manually */ + if (NT_SUCCESS(Status)) { + return; + } else { + AsyncData->IoStatusBlock.Status = Status; + SockAsyncSelectCompletionRoutine(AsyncData, &AsyncData->IoStatusBlock); + } +} + +VOID SockProcessQueuedAsyncSelect(PVOID Context, PIO_STATUS_BLOCK IoStatusBlock) +{ + PASYNC_DATA AsyncData = Context; + BOOL FreeContext = TRUE; + PSOCKET_INFORMATION Socket; + + /* Get the Socket */ + Socket = AsyncData->ParentSocket; + + /* If someone closed it, stop the function */ + if (Socket->SharedData.State != SocketClosed) { + /* Check if the Sequence Number changed by now, in which case quit */ + if (AsyncData->SequenceNumber == Socket->SharedData.SequenceNumber) { + /* Do the actuall select, if needed */ + if ((Socket->SharedData.AsyncEvents & (~Socket->SharedData.AsyncDisabledEvents))) { + SockProcessAsyncSelect(Socket, AsyncData); + FreeContext = FALSE; + } + } + } + + /* Free the Context */ + if (FreeContext) { + HeapFree(GetProcessHeap(), 0, AsyncData); + } + + return; +} + +VOID +SockReenableAsyncSelectEvent ( + IN PSOCKET_INFORMATION Socket, + IN ULONG Event + ) +{ + PASYNC_DATA AsyncData; + + /* Make sure the event is actually disabled */ + if (!(Socket->SharedData.AsyncDisabledEvents & Event)) { + return; + } + + /* Re-enable it */ + Socket->SharedData.AsyncDisabledEvents &= ~Event; + + /* Return if no more events are being polled */ + if ((Socket->SharedData.AsyncEvents & (~Socket->SharedData.AsyncDisabledEvents)) == 0 ) { + return; + } + + /* Wait on new events */ + AsyncData = HeapAlloc(GetProcessHeap(), 0, sizeof(ASYNC_DATA)); + + /* Create the Asynch Thread if Needed */ + SockCreateOrReferenceAsyncThread(); + + /* Increase the sequence number to stop anything else */ + Socket->SharedData.SequenceNumber++; + + /* Set up the Async Data */ + AsyncData->ParentSocket = Socket; + AsyncData->SequenceNumber = Socket->SharedData.SequenceNumber; + + /* Begin Async Select by using I/O Completion */ + NtSetIoCompletion(SockAsyncCompletionPort, + (PVOID)&SockProcessQueuedAsyncSelect, + AsyncData, + 0, + 0); + + /* All done */ + return; +} + +BOOL +STDCALL +DllMain(HANDLE hInstDll, + ULONG dwReason, + PVOID Reserved) +{ + + switch (dwReason) { + case DLL_PROCESS_ATTACH: + + AFD_DbgPrint(MAX_TRACE, ("Loading MSAFD.DLL \n")); + + /* Don't need thread attach notifications + so disable them to improve performance */ + DisableThreadLibraryCalls(hInstDll); + + /* List of DLL Helpers */ + InitializeListHead(&SockHelpersListHead); + + /* Heap to use when allocating */ + GlobalHeap = GetProcessHeap(); + + /* Allocate Heap for 1024 Sockets, can be expanded later */ + Sockets = HeapAlloc(GetProcessHeap(), 0, sizeof(PSOCKET_INFORMATION) * 1024); + + AFD_DbgPrint(MAX_TRACE, ("MSAFD.DLL has been loaded\n")); + + break; + + case DLL_THREAD_ATTACH: + break; + + case DLL_THREAD_DETACH: + break; + + case DLL_PROCESS_DETACH: + break; + } + + AFD_DbgPrint(MAX_TRACE, ("DllMain of msafd.dll (leaving)\n")); + + return TRUE; +} + +/* EOF */ + + diff --git a/reactos/dll/msafd/misc/event.c b/reactos/dll/msafd/misc/event.c new file mode 100644 index 00000000000..9719258eb3d --- /dev/null +++ b/reactos/dll/msafd/misc/event.c @@ -0,0 +1,229 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Ancillary Function Driver DLL + * FILE: misc/event.c + * PURPOSE: Event handling + * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) + * Alex Ionescu (alex@relsoft.net) + * REVISIONS: + * CSH 15/06-2001 Created + * Alex 16/07/2004 - Complete Rewrite + */ + +#include + +#include + +int +WSPAPI +WSPEventSelect( + SOCKET Handle, + WSAEVENT hEventObject, + long lNetworkEvents, + LPINT lpErrno) +{ + IO_STATUS_BLOCK IOSB; + AFD_EVENT_SELECT_INFO EventSelectInfo; + PSOCKET_INFORMATION Socket = NULL; + NTSTATUS Status; + ULONG BlockMode; + HANDLE SockEvent; + + Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE, + NULL, 1, FALSE ); + + if( !NT_SUCCESS(Status) ) return -1; + + /* Get the Socket Structure associate to this Socket*/ + Socket = GetSocketStructure(Handle); + + /* Set Socket to Non-Blocking */ + BlockMode = 1; + SetSocketInformation(Socket, AFD_INFO_BLOCKING_MODE, &BlockMode, NULL); + Socket->SharedData.NonBlocking = TRUE; + + /* Deactivate Async Select if there is one */ + if (Socket->EventObject) { + Socket->SharedData.hWnd = NULL; + Socket->SharedData.wMsg = 0; + Socket->SharedData.AsyncEvents = 0; + Socket->SharedData.SequenceNumber++; // This will kill Async Select after the next completion + } + + /* Set Structure Info */ + EventSelectInfo.EventObject = hEventObject; + EventSelectInfo.Events = 0; + + /* Set Events to wait for */ + if (lNetworkEvents & FD_READ) { + EventSelectInfo.Events |= AFD_EVENT_RECEIVE; + } + + if (lNetworkEvents & FD_WRITE) { + EventSelectInfo.Events |= AFD_EVENT_SEND; + } + + if (lNetworkEvents & FD_OOB) { + EventSelectInfo.Events |= AFD_EVENT_OOB_RECEIVE; + } + + if (lNetworkEvents & FD_ACCEPT) { + EventSelectInfo.Events |= AFD_EVENT_ACCEPT; + } + + if (lNetworkEvents & FD_CONNECT) { + EventSelectInfo.Events |= AFD_EVENT_CONNECT | AFD_EVENT_CONNECT_FAIL; + } + + if (lNetworkEvents & FD_CLOSE) { + EventSelectInfo.Events |= AFD_EVENT_DISCONNECT | AFD_EVENT_ABORT; + } + + if (lNetworkEvents & FD_QOS) { + EventSelectInfo.Events |= AFD_EVENT_QOS; + } + + if (lNetworkEvents & FD_GROUP_QOS) { + EventSelectInfo.Events |= AFD_EVENT_GROUP_QOS; + } + + /* Send IOCTL */ + Status = NtDeviceIoControlFile((HANDLE)Handle, + SockEvent, + NULL, + NULL, + &IOSB, + IOCTL_AFD_EVENT_SELECT, + &EventSelectInfo, + sizeof(EventSelectInfo), + NULL, + 0); + + AFD_DbgPrint(MID_TRACE,("AFD: %x\n", Status)); + + /* Wait for return */ + if (Status == STATUS_PENDING) { + WaitForSingleObject(SockEvent, INFINITE); + } + + AFD_DbgPrint(MID_TRACE,("Waited\n")); + + NtClose( SockEvent ); + + AFD_DbgPrint(MID_TRACE,("Closed event\n")); + + /* Set Socket Data*/ + Socket->EventObject = hEventObject; + Socket->NetworkEvents = lNetworkEvents; + + AFD_DbgPrint(MID_TRACE,("Leaving\n")); + + return 0; +} + + +INT +WSPAPI +WSPEnumNetworkEvents( + IN SOCKET Handle, + IN WSAEVENT hEventObject, + OUT LPWSANETWORKEVENTS lpNetworkEvents, + OUT LPINT lpErrno) +{ + AFD_ENUM_NETWORK_EVENTS_INFO EnumReq; + IO_STATUS_BLOCK IOSB; + PSOCKET_INFORMATION Socket = NULL; + NTSTATUS Status; + HANDLE SockEvent; + + AFD_DbgPrint(MID_TRACE,("Called (lpNetworkEvents %x)\n", lpNetworkEvents)); + + Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE, + NULL, 1, FALSE ); + + if( !NT_SUCCESS(Status) ) { + AFD_DbgPrint(MID_TRACE,("Could not make an event %x\n", Status)); + return -1; + } + + /* Get the Socket Structure associate to this Socket*/ + Socket = GetSocketStructure(Handle); + + EnumReq.Event = hEventObject; + + /* Send IOCTL */ + Status = NtDeviceIoControlFile((HANDLE)Handle, + SockEvent, + NULL, + NULL, + &IOSB, + IOCTL_AFD_ENUM_NETWORK_EVENTS, + &EnumReq, + sizeof(EnumReq), + NULL, + 0); + + AFD_DbgPrint(MID_TRACE,("AFD: %x\n", Status)); + + /* Wait for return */ + if (Status == STATUS_PENDING) { + WaitForSingleObject(SockEvent, INFINITE); + Status = STATUS_SUCCESS; + } + + AFD_DbgPrint(MID_TRACE,("Waited\n")); + + NtClose( SockEvent ); + + AFD_DbgPrint(MID_TRACE,("Closed event\n")); + AFD_DbgPrint(MID_TRACE,("About to touch struct at %x (%d)\n", + lpNetworkEvents, sizeof(*lpNetworkEvents))); + + lpNetworkEvents->lNetworkEvents = 0; + + AFD_DbgPrint(MID_TRACE,("Zeroed struct\n")); + + /* Set Events to wait for */ + if (EnumReq.PollEvents & AFD_EVENT_RECEIVE) { + lpNetworkEvents->lNetworkEvents |= FD_READ; + } + + if (EnumReq.PollEvents & AFD_EVENT_SEND) { + lpNetworkEvents->lNetworkEvents |= FD_WRITE; + } + + if (EnumReq.PollEvents & AFD_EVENT_OOB_RECEIVE) { + lpNetworkEvents->lNetworkEvents |= FD_OOB; + } + + if (EnumReq.PollEvents & AFD_EVENT_ACCEPT) { + lpNetworkEvents->lNetworkEvents |= FD_ACCEPT; + } + + if (EnumReq.PollEvents & + (AFD_EVENT_CONNECT | AFD_EVENT_CONNECT_FAIL)) { + lpNetworkEvents->lNetworkEvents |= FD_CONNECT; + } + + if (EnumReq.PollEvents & + (AFD_EVENT_DISCONNECT | AFD_EVENT_ABORT)) { + lpNetworkEvents->lNetworkEvents |= FD_CLOSE; + } + + if (EnumReq.PollEvents & AFD_EVENT_QOS) { + lpNetworkEvents->lNetworkEvents |= FD_QOS; + } + + if (EnumReq.PollEvents & AFD_EVENT_GROUP_QOS) { + lpNetworkEvents->lNetworkEvents |= FD_GROUP_QOS; + } + + if( NT_SUCCESS(Status) ) *lpErrno = 0; + else *lpErrno = WSAEINVAL; + + AFD_DbgPrint(MID_TRACE,("Leaving\n")); + + return 0; +} + +/* EOF */ diff --git a/reactos/dll/msafd/misc/helpers.c b/reactos/dll/msafd/misc/helpers.c new file mode 100644 index 00000000000..f0bb6a9d5f0 --- /dev/null +++ b/reactos/dll/msafd/misc/helpers.c @@ -0,0 +1,520 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Ancillary Function Driver DLL + * FILE: misc/helpers.c + * PURPOSE: Helper DLL management + * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) + * Alex Ionescu (alex@relsoft.net) + * REVISIONS: + * CSH 01/09-2000 Created + * Alex 16/07/2004 - Complete Rewrite + */ +#include + +#include + +CRITICAL_SECTION HelperDLLDatabaseLock; +LIST_ENTRY HelperDLLDatabaseListHead; + + +INT +SockGetTdiName( + PINT AddressFamily, + PINT SocketType, + PINT Protocol, + GROUP Group, + DWORD Flags, + PUNICODE_STRING TransportName, + PVOID *HelperDllContext, + PHELPER_DATA *HelperDllData, + PDWORD Events) +{ + PHELPER_DATA HelperData; + PWSTR Transports; + PWSTR Transport; + PWINSOCK_MAPPING Mapping; + PLIST_ENTRY Helpers; + INT Status; + + AFD_DbgPrint(MID_TRACE,("Called\n")); + + /* Check in our Current Loaded Helpers */ + for (Helpers = SockHelpersListHead.Flink; + Helpers != &SockHelpersListHead; + Helpers = Helpers->Flink ) { + + HelperData = CONTAINING_RECORD(Helpers, HELPER_DATA, Helpers); + + /* See if this Mapping works for us */ + if (SockIsTripleInMapping (HelperData->Mapping, + *AddressFamily, + *SocketType, + *Protocol)) { + + /* Call the Helper Dll function get the Transport Name */ + if (HelperData->WSHOpenSocket2 == NULL ) { + + /* DLL Doesn't support WSHOpenSocket2, call the old one */ + HelperData->WSHOpenSocket(AddressFamily, + SocketType, + Protocol, + TransportName, + HelperDllContext, + Events + ); + } else { + HelperData->WSHOpenSocket2(AddressFamily, + SocketType, + Protocol, + Group, + Flags, + TransportName, + HelperDllContext, + Events + ); + } + + /* Return the Helper Pointers */ + *HelperDllData = HelperData; + return NO_ERROR; + } + } + + /* Get the Transports available */ + Status = SockLoadTransportList(&Transports); + + /* Check for error */ + if (Status) { + AFD_DbgPrint(MIN_TRACE, ("Can't get transport list\n")); + return Status; + } + + /* Loop through each transport until we find one that can satisfy us */ + for (Transport = Transports; + *Transports != 0; + Transport += wcslen(Transport) + 1) { + AFD_DbgPrint(MID_TRACE, ("Transport: %S\n", Transports)); + + /* See what mapping this Transport supports */ + Status = SockLoadTransportMapping(Transport, &Mapping); + + /* Check for error */ + if (Status) { + AFD_DbgPrint(MIN_TRACE, ("Can't get mapping\n")); + HeapFree(GlobalHeap, 0, Transports); + return Status; + } + + /* See if this Mapping works for us */ + if (SockIsTripleInMapping(Mapping, *AddressFamily, *SocketType, *Protocol)) { + + /* It does, so load the DLL associated with it */ + Status = SockLoadHelperDll(Transport, Mapping, &HelperData); + + /* Check for error */ + if (Status) { + AFD_DbgPrint(MIN_TRACE, ("Can't load helper DLL\n")); + HeapFree(GlobalHeap, 0, Transports); + HeapFree(GlobalHeap, 0, Mapping); + return Status; + } + + /* Call the Helper Dll function get the Transport Name */ + if (HelperData->WSHOpenSocket2 == NULL) { + /* DLL Doesn't support WSHOpenSocket2, call the old one */ + HelperData->WSHOpenSocket(AddressFamily, + SocketType, + Protocol, + TransportName, + HelperDllContext, + Events + ); + } else { + HelperData->WSHOpenSocket2(AddressFamily, + SocketType, + Protocol, + Group, + Flags, + TransportName, + HelperDllContext, + Events + ); + } + + /* Return the Helper Pointers */ + *HelperDllData = HelperData; + /* We actually cache these ... the can't be freed yet */ + /*HeapFree(GlobalHeap, 0, Transports);*/ + /*HeapFree(GlobalHeap, 0, Mapping);*/ + return NO_ERROR; + } + + HeapFree(GlobalHeap, 0, Mapping); + } + HeapFree(GlobalHeap, 0, Transports); + return WSAEINVAL; +} + +INT +SockLoadTransportMapping( + PWSTR TransportName, + PWINSOCK_MAPPING *Mapping) +{ + PWSTR TransportKey; + HKEY KeyHandle; + ULONG MappingSize; + LONG Status; + + AFD_DbgPrint(MID_TRACE,("Called: TransportName %ws\n", TransportName)); + + /* Allocate a Buffer */ + TransportKey = HeapAlloc(GlobalHeap, 0, (54 + wcslen(TransportName)) * sizeof(WCHAR)); + + /* Check for error */ + if (TransportKey == NULL) { + AFD_DbgPrint(MIN_TRACE, ("Buffer allocation failed\n")); + return WSAEINVAL; + } + + /* Generate the right key name */ + wcscpy(TransportKey, L"System\\CurrentControlSet\\Services\\"); + wcscat(TransportKey, TransportName); + wcscat(TransportKey, L"\\Parameters\\Winsock"); + + /* Open the Key */ + Status = RegOpenKeyExW(HKEY_LOCAL_MACHINE, TransportKey, 0, KEY_READ, &KeyHandle); + + /* We don't need the Transport Key anymore */ + HeapFree(GlobalHeap, 0, TransportKey); + + /* Check for error */ + if (Status) { + AFD_DbgPrint(MIN_TRACE, ("Error reading transport mapping registry\n")); + return WSAEINVAL; + } + + /* Find out how much space we need for the Mapping */ + Status = RegQueryValueExW(KeyHandle, L"Mapping", NULL, NULL, NULL, &MappingSize); + + /* Check for error */ + if (Status) { + AFD_DbgPrint(MIN_TRACE, ("Error reading transport mapping registry\n")); + return WSAEINVAL; + } + + /* Allocate Memory for the Mapping */ + *Mapping = HeapAlloc(GlobalHeap, 0, MappingSize); + + /* Check for error */ + if (*Mapping == NULL) { + AFD_DbgPrint(MIN_TRACE, ("Buffer allocation failed\n")); + return WSAEINVAL; + } + + /* Read the Mapping */ + Status = RegQueryValueExW(KeyHandle, L"Mapping", NULL, NULL, (LPBYTE)*Mapping, &MappingSize); + + /* Check for error */ + if (Status) { + AFD_DbgPrint(MIN_TRACE, ("Error reading transport mapping registry\n")); + HeapFree(GlobalHeap, 0, *Mapping); + return WSAEINVAL; + } + + /* Close key and return */ + RegCloseKey(KeyHandle); + return 0; +} + +INT +SockLoadTransportList( + PWSTR *TransportList) +{ + ULONG TransportListSize; + HKEY KeyHandle; + LONG Status; + + AFD_DbgPrint(MID_TRACE,("Called\n")); + + /* Open the Transports Key */ + Status = RegOpenKeyExW (HKEY_LOCAL_MACHINE, + L"SYSTEM\\CurrentControlSet\\Services\\Winsock\\Parameters", + 0, + KEY_READ, + &KeyHandle); + + /* Check for error */ + if (Status) { + AFD_DbgPrint(MIN_TRACE, ("Error reading transport list registry\n")); + return WSAEINVAL; + } + + /* Get the Transport List Size */ + Status = RegQueryValueExW(KeyHandle, + L"Transports", + NULL, + NULL, + NULL, + &TransportListSize); + + /* Check for error */ + if (Status) { + AFD_DbgPrint(MIN_TRACE, ("Error reading transport list registry\n")); + return WSAEINVAL; + } + + /* Allocate Memory for the Transport List */ + *TransportList = HeapAlloc(GlobalHeap, 0, TransportListSize); + + /* Check for error */ + if (*TransportList == NULL) { + AFD_DbgPrint(MIN_TRACE, ("Buffer allocation failed\n")); + return WSAEINVAL; + } + + /* Get the Transports */ + Status = RegQueryValueExW (KeyHandle, + L"Transports", + NULL, + NULL, + (LPBYTE)*TransportList, + &TransportListSize); + + /* Check for error */ + if (Status) { + AFD_DbgPrint(MIN_TRACE, ("Error reading transport list registry\n")); + HeapFree(GlobalHeap, 0, *TransportList); + return WSAEINVAL; + } + + /* Close key and return */ + RegCloseKey(KeyHandle); + return 0; +} + +INT +SockLoadHelperDll( + PWSTR TransportName, + PWINSOCK_MAPPING Mapping, + PHELPER_DATA *HelperDllData) +{ + PHELPER_DATA HelperData; + PWSTR HelperDllName; + PWSTR FullHelperDllName; + ULONG HelperDllNameSize; + PWSTR HelperKey; + HKEY KeyHandle; + ULONG DataSize; + LONG Status; + + /* Allocate space for the Helper Structure and TransportName */ + HelperData = HeapAlloc(GlobalHeap, 0, sizeof(*HelperData) + (wcslen(TransportName) + 1) * sizeof(WCHAR)); + + /* Check for error */ + if (HelperData == NULL) { + AFD_DbgPrint(MIN_TRACE, ("Buffer allocation failed\n")); + return WSAEINVAL; + } + + /* Allocate Space for the Helper DLL Key */ + HelperKey = HeapAlloc(GlobalHeap, 0, (54 + wcslen(TransportName)) * sizeof(WCHAR)); + + /* Check for error */ + if (HelperKey == NULL) { + AFD_DbgPrint(MIN_TRACE, ("Buffer allocation failed\n")); + HeapFree(GlobalHeap, 0, HelperData); + return WSAEINVAL; + } + + /* Generate the right key name */ + wcscpy(HelperKey, L"System\\CurrentControlSet\\Services\\"); + wcscat(HelperKey, TransportName); + wcscat(HelperKey, L"\\Parameters\\Winsock"); + + /* Open the Key */ + Status = RegOpenKeyExW(HKEY_LOCAL_MACHINE, HelperKey, 0, KEY_READ, &KeyHandle); + + HeapFree(GlobalHeap, 0, HelperKey); + + /* Check for error */ + if (Status) { + AFD_DbgPrint(MIN_TRACE, ("Error reading helper DLL parameters\n")); + HeapFree(GlobalHeap, 0, HelperData); + return WSAEINVAL; + } + + /* Read Size of SockAddr Structures */ + DataSize = sizeof(HelperData->MinWSAddressLength); + HelperData->MinWSAddressLength = 16; + RegQueryValueExW (KeyHandle, + L"MinSockaddrLength", + NULL, + NULL, + (LPBYTE)&HelperData->MinWSAddressLength, + &DataSize); + DataSize = sizeof(HelperData->MinWSAddressLength); + HelperData->MaxWSAddressLength = 16; + RegQueryValueExW (KeyHandle, + L"MaxSockaddrLength", + NULL, + NULL, + (LPBYTE)&HelperData->MaxWSAddressLength, + &DataSize); + + /* Size of TDI Structures */ + HelperData->MinTDIAddressLength = HelperData->MinWSAddressLength + 6; + HelperData->MaxTDIAddressLength = HelperData->MaxWSAddressLength + 6; + + /* Read Delayed Acceptance Setting */ + DataSize = sizeof(DWORD); + HelperData->UseDelayedAcceptance = FALSE; + RegQueryValueExW (KeyHandle, + L"UseDelayedAcceptance", + NULL, + NULL, + (LPBYTE)&HelperData->UseDelayedAcceptance, + &DataSize); + + /* Allocate Space for the Helper DLL Names */ + HelperDllName = HeapAlloc(GlobalHeap, 0, 512); + + /* Check for error */ + if (HelperDllName == NULL) { + AFD_DbgPrint(MIN_TRACE, ("Buffer allocation failed\n")); + HeapFree(GlobalHeap, 0, HelperData); + return WSAEINVAL; + } + + FullHelperDllName = HeapAlloc(GlobalHeap, 0, 512); + + /* Check for error */ + if (FullHelperDllName == NULL) { + AFD_DbgPrint(MIN_TRACE, ("Buffer allocation failed\n")); + HeapFree(GlobalHeap, 0, HelperDllName); + HeapFree(GlobalHeap, 0, HelperData); + return WSAEINVAL; + } + + /* Get the name of the Helper DLL*/ + DataSize = 512; + Status = RegQueryValueExW (KeyHandle, + L"HelperDllName", + NULL, + NULL, + (LPBYTE)HelperDllName, + &DataSize); + + /* Check for error */ + if (Status) { + AFD_DbgPrint(MIN_TRACE, ("Error reading helper DLL parameters\n")); + HeapFree(GlobalHeap, 0, FullHelperDllName); + HeapFree(GlobalHeap, 0, HelperDllName); + HeapFree(GlobalHeap, 0, HelperData); + return WSAEINVAL; + } + + /* Get the Full name, expanding Environment Strings */ + HelperDllNameSize = ExpandEnvironmentStringsW (HelperDllName, + FullHelperDllName, + 256); + + /* Load the DLL */ + HelperData->hInstance = LoadLibraryW(FullHelperDllName); + + HeapFree(GlobalHeap, 0, HelperDllName); + HeapFree(GlobalHeap, 0, FullHelperDllName); + + if (HelperData->hInstance == NULL) { + AFD_DbgPrint(MIN_TRACE, ("Error loading helper DLL\n")); + HeapFree(GlobalHeap, 0, HelperData); + return WSAEINVAL; + } + + /* Close Key */ + RegCloseKey(KeyHandle); + + /* Get the Pointers to the Helper Routines */ + HelperData->WSHOpenSocket = (PWSH_OPEN_SOCKET) + GetProcAddress(HelperData->hInstance, + "WSHOpenSocket"); + HelperData->WSHOpenSocket2 = (PWSH_OPEN_SOCKET2) + GetProcAddress(HelperData->hInstance, + "WSHOpenSocket2"); + HelperData->WSHJoinLeaf = (PWSH_JOIN_LEAF) + GetProcAddress(HelperData->hInstance, + "WSHJoinLeaf"); + HelperData->WSHNotify = (PWSH_NOTIFY) + GetProcAddress(HelperData->hInstance, "WSHNotify"); + HelperData->WSHGetSocketInformation = (PWSH_GET_SOCKET_INFORMATION) + GetProcAddress(HelperData->hInstance, + "WSHGetSocketInformation"); + HelperData->WSHSetSocketInformation = (PWSH_SET_SOCKET_INFORMATION) + GetProcAddress(HelperData->hInstance, + "WSHSetSocketInformation"); + HelperData->WSHGetSockaddrType = (PWSH_GET_SOCKADDR_TYPE) + GetProcAddress(HelperData->hInstance, + "WSHGetSockaddrType"); + HelperData->WSHGetWildcardSockaddr = (PWSH_GET_WILDCARD_SOCKEADDR) + GetProcAddress(HelperData->hInstance, + "WSHGetWildcardSockaddr"); + HelperData->WSHGetBroadcastSockaddr = (PWSH_GET_BROADCAST_SOCKADDR) + GetProcAddress(HelperData->hInstance, + "WSHGetBroadcastSockaddr"); + HelperData->WSHAddressToString = (PWSH_ADDRESS_TO_STRING) + GetProcAddress(HelperData->hInstance, + "WSHAddressToString"); + HelperData->WSHStringToAddress = (PWSH_STRING_TO_ADDRESS) + GetProcAddress(HelperData->hInstance, + "WSHStringToAddress"); + HelperData->WSHIoctl = (PWSH_IOCTL) + GetProcAddress(HelperData->hInstance, + "WSHIoctl"); + + /* Save the Mapping Structure and transport name */ + HelperData->Mapping = Mapping; + wcscpy(HelperData->TransportName, TransportName); + + /* Increment Reference Count */ + HelperData->RefCount = 1; + + /* Add it to our list */ + InsertHeadList(&SockHelpersListHead, &HelperData->Helpers); + + /* Return Pointers */ + *HelperDllData = HelperData; + return 0; +} + +BOOL +SockIsTripleInMapping( + PWINSOCK_MAPPING Mapping, + INT AddressFamily, + INT SocketType, + INT Protocol) +{ + /* The Windows version returns more detailed information on which of the 3 parameters failed...we should do this later */ + ULONG Row; + + AFD_DbgPrint(MID_TRACE,("Called, Mapping rows = %d\n", Mapping->Rows)); + + /* Loop through Mapping to Find a matching one */ + for (Row = 0; Row < Mapping->Rows; Row++) { + AFD_DbgPrint(MID_TRACE,("Examining: row %d: AF %d type %d proto %d\n", + Row, + (INT)Mapping->Mapping[Row].AddressFamily, + (INT)Mapping->Mapping[Row].SocketType, + (INT)Mapping->Mapping[Row].Protocol)); + + /* Check of all three values Match */ + if (((INT)Mapping->Mapping[Row].AddressFamily == AddressFamily) && + ((INT)Mapping->Mapping[Row].SocketType == SocketType) && + ((INT)Mapping->Mapping[Row].Protocol == Protocol)) { + AFD_DbgPrint(MID_TRACE,("Found\n")); + return TRUE; + } + } + AFD_DbgPrint(MID_TRACE,("Not found\n")); + return FALSE; +} + +/* EOF */ diff --git a/reactos/dll/msafd/misc/sndrcv.c b/reactos/dll/msafd/misc/sndrcv.c new file mode 100644 index 00000000000..62afc4a8b4c --- /dev/null +++ b/reactos/dll/msafd/misc/sndrcv.c @@ -0,0 +1,620 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Ancillary Function Driver DLL + * FILE: misc/sndrcv.c + * PURPOSE: Send/receive routines + * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) + * Alex Ionescu (alex@relsoft.net) + * REVISIONS: + * CSH 01/09-2000 Created + * Alex 16/07/2004 - Complete Rewrite + */ + +#include + +#include + +INT +WSPAPI +WSPAsyncSelect( + IN SOCKET Handle, + IN HWND hWnd, + IN UINT wMsg, + IN LONG lEvent, + OUT LPINT lpErrno) +{ + PSOCKET_INFORMATION Socket = NULL; + PASYNC_DATA AsyncData; + NTSTATUS Status; + ULONG BlockMode; + + /* Get the Socket Structure associated to this Socket */ + Socket = GetSocketStructure(Handle); + + /* Allocate the Async Data Structure to pass on to the Thread later */ + AsyncData = HeapAlloc(GetProcessHeap(), 0, sizeof(*AsyncData)); + + /* Change the Socket to Non Blocking */ + BlockMode = 1; + SetSocketInformation(Socket, AFD_INFO_BLOCKING_MODE, &BlockMode, NULL); + Socket->SharedData.NonBlocking = TRUE; + + /* Deactive WSPEventSelect */ + if (Socket->SharedData.AsyncEvents) { + WSPEventSelect(Handle, NULL, 0, NULL); + } + + /* Create the Asynch Thread if Needed */ + SockCreateOrReferenceAsyncThread(); + + /* Open a Handle to AFD's Async Helper */ + SockGetAsyncSelectHelperAfdHandle(); + + /* Store Socket Data */ + Socket->SharedData.hWnd = hWnd; + Socket->SharedData.wMsg = wMsg; + Socket->SharedData.AsyncEvents = lEvent; + Socket->SharedData.AsyncDisabledEvents = 0; + Socket->SharedData.SequenceNumber++; + + /* Return if there are no more Events */ + if ((Socket->SharedData.AsyncEvents & (~Socket->SharedData.AsyncDisabledEvents)) == 0) { + HeapFree(GetProcessHeap(), 0, AsyncData); + return 0; + } + + /* Set up the Async Data */ + AsyncData->ParentSocket = Socket; + AsyncData->SequenceNumber = Socket->SharedData.SequenceNumber; + + /* Begin Async Select by using I/O Completion */ + Status = NtSetIoCompletion(SockAsyncCompletionPort, + (PVOID)&SockProcessQueuedAsyncSelect, + AsyncData, + 0, + 0); + + /* Return */ + return ERROR_SUCCESS; +} + + +int +WSPAPI +WSPRecv( + SOCKET Handle, + LPWSABUF lpBuffers, + DWORD dwBufferCount, + LPDWORD lpNumberOfBytesRead, + LPDWORD ReceiveFlags, + LPWSAOVERLAPPED lpOverlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, + LPWSATHREADID lpThreadId, + LPINT lpErrno) +{ + PIO_STATUS_BLOCK IOSB; + IO_STATUS_BLOCK DummyIOSB; + AFD_RECV_INFO RecvInfo; + NTSTATUS Status; + PVOID APCContext; + PVOID APCFunction; + HANDLE Event; + HANDLE SockEvent; + PSOCKET_INFORMATION Socket; + + AFD_DbgPrint(MID_TRACE,("Called (%x)\n", Handle)); + + /* Get the Socket Structure associate to this Socket*/ + Socket = GetSocketStructure(Handle); + + Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE, + NULL, 1, FALSE ); + + if( !NT_SUCCESS(Status) ) return -1; + + /* Set up the Receive Structure */ + RecvInfo.BufferArray = (PAFD_WSABUF)lpBuffers; + RecvInfo.BufferCount = dwBufferCount; + RecvInfo.TdiFlags = 0; + RecvInfo.AfdFlags = Socket->SharedData.NonBlocking ? AFD_IMMEDIATE : 0; + + /* Set the TDI Flags */ + if (*ReceiveFlags == 0) { + RecvInfo.TdiFlags |= TDI_RECEIVE_NORMAL; + + } else { + + if (*ReceiveFlags & MSG_OOB) { + RecvInfo.TdiFlags |= TDI_RECEIVE_EXPEDITED; + } else { + RecvInfo.TdiFlags |= TDI_RECEIVE_NORMAL; + } + + if (*ReceiveFlags & MSG_PEEK) { + RecvInfo.TdiFlags |= TDI_RECEIVE_PEEK; + } + + if (*ReceiveFlags & MSG_PARTIAL) { + RecvInfo.TdiFlags |= TDI_RECEIVE_NORMAL; + } + } + + /* Verifiy if we should use APC */ + + if (lpOverlapped == NULL) { + + /* Not using Overlapped structure, so use normal blocking on event */ + APCContext = NULL; + APCFunction = NULL; + Event = SockEvent; + IOSB = &DummyIOSB; + + } else { + + if (lpCompletionRoutine == NULL) { + + /* Using Overlapped Structure, but no Completition Routine, so no need for APC */ + APCContext = lpOverlapped; + APCFunction = NULL; + Event = lpOverlapped->hEvent; + + } else { + + /* Using Overlapped Structure and a Completition Routine, so use an APC */ + APCFunction = NULL; // should be a private io completition function inside us + APCContext = lpCompletionRoutine; + RecvInfo.AfdFlags = AFD_SKIP_FIO; + } + + IOSB = (PIO_STATUS_BLOCK)&lpOverlapped->Internal; + RecvInfo.AfdFlags |= AFD_OVERLAPPED; + } + + IOSB->Status = STATUS_PENDING; + + /* Send IOCTL */ + Status = NtDeviceIoControlFile((HANDLE)Handle, + SockEvent, + APCFunction, + APCContext, + IOSB, + IOCTL_AFD_RECV, + &RecvInfo, + sizeof(RecvInfo), + NULL, + 0); + + /* Wait for completition of not overlapped */ + if (Status == STATUS_PENDING && lpOverlapped == NULL) { + /* It's up to the protocol to time out recv. We must wait + * until the protocol decides it's had enough. */ + WaitForSingleObject(SockEvent, INFINITE); + Status = IOSB->Status; + } + + NtClose( SockEvent ); + + AFD_DbgPrint(MID_TRACE,("Status %x Information %d\n", + Status, IOSB->Information)); + + /* Return the Flags */ + *ReceiveFlags = 0; + + switch (Status) { + case STATUS_RECEIVE_EXPEDITED: *ReceiveFlags = MSG_OOB; break; + case STATUS_RECEIVE_PARTIAL_EXPEDITED: + *ReceiveFlags = MSG_PARTIAL | MSG_OOB; break; + case STATUS_RECEIVE_PARTIAL: *ReceiveFlags = MSG_PARTIAL; break; + } + + /* Re-enable Async Event */ + if (*ReceiveFlags == MSG_OOB) { + SockReenableAsyncSelectEvent(Socket, FD_OOB); + } else { + SockReenableAsyncSelectEvent(Socket, FD_READ); + } + + return MsafdReturnWithErrno + ( Status, lpErrno, IOSB->Information, lpNumberOfBytesRead ); +} + +int +WSPAPI +WSPRecvFrom( + SOCKET Handle, + LPWSABUF lpBuffers, + DWORD dwBufferCount, + LPDWORD lpNumberOfBytesRead, + LPDWORD ReceiveFlags, + struct sockaddr *SocketAddress, + int *SocketAddressLength, + LPWSAOVERLAPPED lpOverlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, + LPWSATHREADID lpThreadId, + LPINT lpErrno) +{ + PIO_STATUS_BLOCK IOSB; + IO_STATUS_BLOCK DummyIOSB; + AFD_RECV_INFO_UDP RecvInfo; + NTSTATUS Status; + PVOID APCContext; + PVOID APCFunction; + HANDLE Event; + HANDLE SockEvent; + PSOCKET_INFORMATION Socket; + + /* Get the Socket Structure associate to this Socket*/ + Socket = GetSocketStructure(Handle); + + Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE, + NULL, 1, FALSE ); + + if( !NT_SUCCESS(Status) ) return -1; + + /* Set up the Receive Structure */ + RecvInfo.BufferArray = (PAFD_WSABUF)lpBuffers; + RecvInfo.BufferCount = dwBufferCount; + RecvInfo.TdiFlags = 0; + RecvInfo.AfdFlags = Socket->SharedData.NonBlocking ? AFD_IMMEDIATE : 0; + RecvInfo.AddressLength = SocketAddressLength; + RecvInfo.Address = SocketAddress; + + /* Set the TDI Flags */ + if (*ReceiveFlags == 0) { + RecvInfo.TdiFlags |= TDI_RECEIVE_NORMAL; + + } else { + + if (*ReceiveFlags & MSG_OOB) { + RecvInfo.TdiFlags |= TDI_RECEIVE_EXPEDITED; + } else { + RecvInfo.TdiFlags |= TDI_RECEIVE_NORMAL; + } + + if (*ReceiveFlags & MSG_PEEK) { + RecvInfo.TdiFlags |= TDI_RECEIVE_PEEK; + } + + if (*ReceiveFlags & MSG_PARTIAL) { + RecvInfo.TdiFlags |= TDI_RECEIVE_NORMAL; + } + } + + /* Verifiy if we should use APC */ + + if (lpOverlapped == NULL) { + + /* Not using Overlapped structure, so use normal blocking on event */ + APCContext = NULL; + APCFunction = NULL; + Event = SockEvent; + IOSB = &DummyIOSB; + + } else { + + if (lpCompletionRoutine == NULL) { + + /* Using Overlapped Structure, but no Completition Routine, so no need for APC */ + APCContext = lpOverlapped; + APCFunction = NULL; + Event = lpOverlapped->hEvent; + + } else { + + /* Using Overlapped Structure and a Completition Routine, so use an APC */ + APCFunction = NULL; // should be a private io completition function inside us + APCContext = lpCompletionRoutine; + RecvInfo.AfdFlags = AFD_SKIP_FIO; + } + + IOSB = (PIO_STATUS_BLOCK)&lpOverlapped->Internal; + RecvInfo.AfdFlags |= AFD_OVERLAPPED; + } + + IOSB->Status = STATUS_PENDING; + + /* Send IOCTL */ + Status = NtDeviceIoControlFile((HANDLE)Handle, + SockEvent, + APCFunction, + APCContext, + IOSB, + IOCTL_AFD_RECV_DATAGRAM, + &RecvInfo, + sizeof(RecvInfo), + NULL, + 0); + + /* Wait for completition of not overlapped */ + if (Status == STATUS_PENDING && lpOverlapped == NULL) { + WaitForSingleObject(SockEvent, INFINITE); // BUGBUG, shouldn wait infintely for receive... + Status = IOSB->Status; + } + + NtClose( SockEvent ); + + /* Return the Flags */ + *ReceiveFlags = 0; + + switch (Status) { + case STATUS_RECEIVE_EXPEDITED: *ReceiveFlags = MSG_OOB; break; + case STATUS_RECEIVE_PARTIAL_EXPEDITED: + *ReceiveFlags = MSG_PARTIAL | MSG_OOB; break; + case STATUS_RECEIVE_PARTIAL: *ReceiveFlags = MSG_PARTIAL; break; + } + + /* Re-enable Async Event */ + SockReenableAsyncSelectEvent(Socket, FD_READ); + + return MsafdReturnWithErrno + ( Status, lpErrno, IOSB->Information, lpNumberOfBytesRead ); +} + + +int +WSPAPI +WSPSend( + SOCKET Handle, + LPWSABUF lpBuffers, + DWORD dwBufferCount, + LPDWORD lpNumberOfBytesSent, + DWORD iFlags, + LPWSAOVERLAPPED lpOverlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, + LPWSATHREADID lpThreadId, + LPINT lpErrno) +{ + PIO_STATUS_BLOCK IOSB; + IO_STATUS_BLOCK DummyIOSB; + AFD_SEND_INFO SendInfo; + NTSTATUS Status; + PVOID APCContext; + PVOID APCFunction; + HANDLE Event; + HANDLE SockEvent; + PSOCKET_INFORMATION Socket; + + /* Get the Socket Structure associate to this Socket*/ + Socket = GetSocketStructure(Handle); + + Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE, + NULL, 1, FALSE ); + + if( !NT_SUCCESS(Status) ) return -1; + + AFD_DbgPrint(MID_TRACE,("Called\n")); + + /* Set up the Send Structure */ + SendInfo.BufferArray = (PAFD_WSABUF)lpBuffers; + SendInfo.BufferCount = dwBufferCount; + SendInfo.TdiFlags = 0; + SendInfo.AfdFlags = Socket->SharedData.NonBlocking ? AFD_IMMEDIATE : 0; + + /* Set the TDI Flags */ + if (iFlags) { + if (iFlags & MSG_OOB) { + SendInfo.TdiFlags |= TDI_SEND_EXPEDITED; + } + if (iFlags & MSG_PARTIAL) { + SendInfo.TdiFlags |= TDI_SEND_PARTIAL; + } + } + + /* Verifiy if we should use APC */ + if (lpOverlapped == NULL) { + + /* Not using Overlapped structure, so use normal blocking on event */ + APCContext = NULL; + APCFunction = NULL; + Event = SockEvent; + IOSB = &DummyIOSB; + + } else { + + if (lpCompletionRoutine == NULL) { + + /* Using Overlapped Structure, but no Completition Routine, so no need for APC */ + APCContext = lpOverlapped; + APCFunction = NULL; + Event = lpOverlapped->hEvent; + + } else { + + /* Using Overlapped Structure and a Completition Routine, so use an APC */ + APCFunction = NULL; // should be a private io completition function inside us + APCContext = lpCompletionRoutine; + SendInfo.AfdFlags = AFD_SKIP_FIO; + } + + IOSB = (PIO_STATUS_BLOCK)&lpOverlapped->Internal; + SendInfo.AfdFlags |= AFD_OVERLAPPED; + } + + IOSB->Status = STATUS_PENDING; + + /* Send IOCTL */ + Status = NtDeviceIoControlFile((HANDLE)Handle, + SockEvent, + APCFunction, + APCContext, + IOSB, + IOCTL_AFD_SEND, + &SendInfo, + sizeof(SendInfo), + NULL, + 0); + + /* Wait for completition of not overlapped */ + if (Status == STATUS_PENDING && lpOverlapped == NULL) { + WaitForSingleObject(SockEvent, INFINITE); // BUGBUG, shouldn wait infintely for send... + Status = IOSB->Status; + } + + NtClose( SockEvent ); + + if (Status == STATUS_PENDING) { + AFD_DbgPrint(MID_TRACE,("Leaving (Pending)\n")); + return WSA_IO_PENDING; + } + + /* Re-enable Async Event */ + SockReenableAsyncSelectEvent(Socket, FD_WRITE); + + AFD_DbgPrint(MID_TRACE,("Leaving (Success, %d)\n", IOSB->Information)); + + return MsafdReturnWithErrno + ( Status, lpErrno, IOSB->Information, lpNumberOfBytesSent ); +} + +int +WSPAPI +WSPSendTo( + SOCKET Handle, + LPWSABUF lpBuffers, + DWORD dwBufferCount, + LPDWORD lpNumberOfBytesSent, + DWORD iFlags, + const struct sockaddr *SocketAddress, + int SocketAddressLength, + LPWSAOVERLAPPED lpOverlapped, + LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, + LPWSATHREADID lpThreadId, + LPINT lpErrno) +{ + PIO_STATUS_BLOCK IOSB; + IO_STATUS_BLOCK DummyIOSB; + AFD_SEND_INFO_UDP SendInfo; + NTSTATUS Status; + PVOID APCContext; + PVOID APCFunction; + HANDLE Event; + PTRANSPORT_ADDRESS RemoteAddress; + UCHAR TdiBuffer[0x16]; + PSOCKADDR BindAddress; + INT BindAddressLength; + HANDLE SockEvent; + PSOCKET_INFORMATION Socket; + + + /* Get the Socket Structure associate to this Socket*/ + Socket = GetSocketStructure(Handle); + + Status = NtCreateEvent( &SockEvent, GENERIC_READ | GENERIC_WRITE, + NULL, 1, FALSE ); + + if( !NT_SUCCESS(Status) ) return -1; + + /* Bind us First */ + if (Socket->SharedData.State == SocketOpen) { + + /* Get the Wildcard Address */ + BindAddressLength = Socket->HelperData->MaxWSAddressLength; + BindAddress = HeapAlloc(GlobalHeap, 0, BindAddressLength); + Socket->HelperData->WSHGetWildcardSockaddr (Socket->HelperContext, + BindAddress, + &BindAddressLength); + + /* Bind it */ + WSPBind(Handle, BindAddress, BindAddressLength, NULL); + } + + /* Set up Address in TDI Format */ + RemoteAddress = (PTRANSPORT_ADDRESS)TdiBuffer; + RemoteAddress->TAAddressCount = 1; + RemoteAddress->Address[0].AddressLength = SocketAddressLength - sizeof(SocketAddress->sa_family); + RtlCopyMemory(&RemoteAddress->Address[0].AddressType, SocketAddress, SocketAddressLength); + + /* Set up Structure */ + SendInfo.BufferArray = (PAFD_WSABUF)lpBuffers; + SendInfo.AfdFlags = Socket->SharedData.NonBlocking ? AFD_IMMEDIATE : 0; + SendInfo.BufferCount = dwBufferCount; + SendInfo.RemoteAddress = RemoteAddress; + SendInfo.SizeOfRemoteAddress = Socket->HelperData->MaxTDIAddressLength; + + /* Verifiy if we should use APC */ + if (lpOverlapped == NULL) { + + /* Not using Overlapped structure, so use normal blocking on event */ + APCContext = NULL; + APCFunction = NULL; + Event = SockEvent; + IOSB = &DummyIOSB; + + } else { + + if (lpCompletionRoutine == NULL) { + + /* Using Overlapped Structure, but no Completition Routine, so no need for APC */ + APCContext = lpOverlapped; + APCFunction = NULL; + Event = lpOverlapped->hEvent; + + } else { + + /* Using Overlapped Structure and a Completition Routine, so use an APC */ + APCFunction = NULL; // should be a private io completition function inside us + APCContext = lpCompletionRoutine; + SendInfo.AfdFlags = AFD_SKIP_FIO; + } + + IOSB = (PIO_STATUS_BLOCK)&lpOverlapped->Internal; + SendInfo.AfdFlags |= AFD_OVERLAPPED; + } + + /* Send IOCTL */ + Status = NtDeviceIoControlFile((HANDLE)Handle, + SockEvent, + APCFunction, + APCContext, + IOSB, + IOCTL_AFD_SEND_DATAGRAM, + &SendInfo, + sizeof(SendInfo), + NULL, + 0); + + /* Wait for completition of not overlapped */ + if (Status == STATUS_PENDING && lpOverlapped == NULL) { + WaitForSingleObject(SockEvent, INFINITE); // BUGBUG, shouldn wait infintely for send... + Status = IOSB->Status; + } + + NtClose( SockEvent ); + + if (Status == STATUS_PENDING) { + return WSA_IO_PENDING; + } + + /* Re-enable Async Event */ + SockReenableAsyncSelectEvent(Socket, FD_WRITE); + + return MsafdReturnWithErrno + ( Status, lpErrno, IOSB->Information, lpNumberOfBytesSent ); +} +INT +WSPAPI +WSPRecvDisconnect( + IN SOCKET s, + OUT LPWSABUF lpInboundDisconnectData, + OUT LPINT lpErrno) +{ + UNIMPLEMENTED + + return 0; +} + + + +INT +WSPAPI +WSPSendDisconnect( + IN SOCKET s, + IN LPWSABUF lpOutboundDisconnectData, + OUT LPINT lpErrno) +{ + UNIMPLEMENTED + + return 0; +} + +/* EOF */ diff --git a/reactos/dll/msafd/misc/stubs.c b/reactos/dll/msafd/misc/stubs.c new file mode 100644 index 00000000000..6de00a6965d --- /dev/null +++ b/reactos/dll/msafd/misc/stubs.c @@ -0,0 +1,134 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Ancillary Function Driver DLL + * FILE: misc/stubs.c + * PURPOSE: Stubs + * PROGRAMMERS: Casper S. Hornstrup (chorns@users.sourceforge.net) + * REVISIONS: + * CSH 01/09-2000 Created + */ +#include + +#include + +INT +WSPAPI +WSPAddressToString( + IN LPSOCKADDR lpsaAddress, + IN DWORD dwAddressLength, + IN LPWSAPROTOCOL_INFOW lpProtocolInfo, + OUT LPWSTR lpszAddressString, + IN OUT LPDWORD lpdwAddressStringLength, + OUT LPINT lpErrno) +{ + UNIMPLEMENTED + + return 0; +} + + +INT +WSPAPI +WSPCancelBlockingCall( + OUT LPINT lpErrno) +{ + UNIMPLEMENTED + + return 0; +} + + +INT +WSPAPI +WSPDuplicateSocket( + IN SOCKET s, + IN DWORD dwProcessId, + OUT LPWSAPROTOCOL_INFOW lpProtocolInfo, + OUT LPINT lpErrno) +{ + UNIMPLEMENTED + + return 0; +} + + +BOOL +WSPAPI +WSPGetOverlappedResult( + IN SOCKET s, + IN LPWSAOVERLAPPED lpOverlapped, + OUT LPDWORD lpcbTransfer, + IN BOOL fWait, + OUT LPDWORD lpdwFlags, + OUT LPINT lpErrno) +{ + UNIMPLEMENTED + + return FALSE; +} + + +BOOL +WSPAPI +WSPGetQOSByName( + IN SOCKET s, + IN OUT LPWSABUF lpQOSName, + OUT LPQOS lpQOS, + OUT LPINT lpErrno) +{ + UNIMPLEMENTED + + return FALSE; +} + + +SOCKET +WSPAPI +WSPJoinLeaf( + IN SOCKET s, + IN CONST SOCKADDR *name, + IN INT namelen, + IN LPWSABUF lpCallerData, + OUT LPWSABUF lpCalleeData, + IN LPQOS lpSQOS, + IN LPQOS lpGQOS, + IN DWORD dwFlags, + OUT LPINT lpErrno) +{ + UNIMPLEMENTED + + return (SOCKET)0; +} + + +INT +WSPAPI +WSPSetSockOpt( + IN SOCKET s, + IN INT level, + IN INT optname, + IN CONST CHAR FAR* optval, + IN INT optlen, + OUT LPINT lpErrno) +{ + UNIMPLEMENTED + + return 0; +} + +INT +WSPAPI +WSPStringToAddress( + IN LPWSTR AddressString, + IN INT AddressFamily, + IN LPWSAPROTOCOL_INFOW lpProtocolInfo, + OUT LPSOCKADDR lpAddress, + IN OUT LPINT lpAddressLength, + OUT LPINT lpErrno) +{ + UNIMPLEMENTED + + return 0; +} + +/* EOF */ diff --git a/reactos/dll/msafd/msafd.def b/reactos/dll/msafd/msafd.def new file mode 100644 index 00000000000..80ae18c2a43 --- /dev/null +++ b/reactos/dll/msafd/msafd.def @@ -0,0 +1,8 @@ +; MSAFD.DLL - Ancillary Function Driver DLL + +LIBRARY msafd.dll + +EXPORTS +WSPStartup@76 + +; EOF diff --git a/reactos/dll/msafd/msafd.h b/reactos/dll/msafd/msafd.h new file mode 100755 index 00000000000..48ef099e026 --- /dev/null +++ b/reactos/dll/msafd/msafd.h @@ -0,0 +1,475 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Ancillary Function Driver DLL + * FILE: include/msafd.h + * PURPOSE: Ancillary Function Driver DLL header + */ +#ifndef __MSAFD_H +#define __MSAFD_H + +#include +#include + +#define WIN32_NO_STATUS +#include +#include +#include +#define NTOS_MODE_USER +#include + +/* This includes ntsecapi.h so it needs to come after the NDK */ +#include +#include +#include +#include + +extern HANDLE GlobalHeap; +extern WSPUPCALLTABLE Upcalls; +extern LPWPUCOMPLETEOVERLAPPEDREQUEST lpWPUCompleteOverlappedRequest; +extern LIST_ENTRY SockHelpersListHead; +extern HANDLE SockEvent; +extern HANDLE SockAsyncCompletionPort; +extern BOOLEAN SockAsyncSelectCalled; + +typedef enum _SOCKET_STATE { + SocketOpen, + SocketBound, + SocketBoundUdp, + SocketConnected, + SocketClosed +} SOCKET_STATE, *PSOCKET_STATE; + +typedef struct _SOCK_SHARED_INFO { + SOCKET_STATE State; + INT AddressFamily; + INT SocketType; + INT Protocol; + INT SizeOfLocalAddress; + INT SizeOfRemoteAddress; + struct linger LingerData; + ULONG SendTimeout; + ULONG RecvTimeout; + ULONG SizeOfRecvBuffer; + ULONG SizeOfSendBuffer; + struct { + BOOLEAN Listening:1; + BOOLEAN Broadcast:1; + BOOLEAN Debug:1; + BOOLEAN OobInline:1; + BOOLEAN ReuseAddresses:1; + BOOLEAN ExclusiveAddressUse:1; + BOOLEAN NonBlocking:1; + BOOLEAN DontUseWildcard:1; + BOOLEAN ReceiveShutdown:1; + BOOLEAN SendShutdown:1; + BOOLEAN UseDelayedAcceptance:1; + BOOLEAN UseSAN:1; + }; // Flags + DWORD CreateFlags; + DWORD CatalogEntryId; + DWORD ServiceFlags1; + DWORD ProviderFlags; + GROUP GroupID; + DWORD GroupType; + INT GroupPriority; + INT SocketLastError; + HWND hWnd; + LONG Unknown; + DWORD SequenceNumber; + UINT wMsg; + LONG AsyncEvents; + LONG AsyncDisabledEvents; +} SOCK_SHARED_INFO, *PSOCK_SHARED_INFO; + +typedef struct _SOCKET_INFORMATION { + ULONG RefCount; + SOCKET Handle; + SOCK_SHARED_INFO SharedData; + DWORD HelperEvents; + PHELPER_DATA HelperData; + PVOID HelperContext; + PSOCKADDR LocalAddress; + PSOCKADDR RemoteAddress; + HANDLE TdiAddressHandle; + HANDLE TdiConnectionHandle; + PVOID AsyncData; + HANDLE EventObject; + LONG NetworkEvents; + CRITICAL_SECTION Lock; + PVOID SanData; + BOOL TrySAN; + SOCKADDR WSLocalAddress; + SOCKADDR WSRemoteAddress; +} SOCKET_INFORMATION, *PSOCKET_INFORMATION; + + +typedef struct _SOCKET_CONTEXT { + SOCK_SHARED_INFO SharedData; + ULONG SizeOfHelperData; + ULONG Padding; + SOCKADDR LocalAddress; + SOCKADDR RemoteAddress; + /* Plus Helper Data */ +} SOCKET_CONTEXT, *PSOCKET_CONTEXT; + +typedef struct _ASYNC_DATA { + PSOCKET_INFORMATION ParentSocket; + DWORD SequenceNumber; + IO_STATUS_BLOCK IoStatusBlock; + AFD_POLL_INFO AsyncSelectInfo; +} ASYNC_DATA, *PASYNC_DATA; + +SOCKET +WSPAPI +WSPAccept( + IN SOCKET s, + OUT LPSOCKADDR addr, + IN OUT LPINT addrlen, + IN LPCONDITIONPROC lpfnCondition, + IN DWORD dwCallbackData, + OUT LPINT lpErrno); + +INT +WSPAPI +WSPAddressToString( + IN LPSOCKADDR lpsaAddress, + IN DWORD dwAddressLength, + IN LPWSAPROTOCOL_INFOW lpProtocolInfo, + OUT LPWSTR lpszAddressString, + IN OUT LPDWORD lpdwAddressStringLength, + OUT LPINT lpErrno); + +INT +WSPAPI +WSPAsyncSelect( + IN SOCKET s, + IN HWND hWnd, + IN UINT wMsg, + IN LONG lEvent, + OUT LPINT lpErrno); + +INT +WSPAPI WSPBind( + IN SOCKET s, + IN CONST SOCKADDR *name, + IN INT namelen, + OUT LPINT lpErrno); + +INT +WSPAPI +WSPCancelBlockingCall( + OUT LPINT lpErrno); + +INT +WSPAPI +WSPCleanup( + OUT LPINT lpErrno); + +INT +WSPAPI +WSPCloseSocket( + IN SOCKET s, + OUT LPINT lpErrno); + +INT +WSPAPI +WSPConnect( + IN SOCKET s, + IN CONST SOCKADDR *name, + IN INT namelen, + IN LPWSABUF lpCallerData, + OUT LPWSABUF lpCalleeData, + IN LPQOS lpSQOS, + IN LPQOS lpGQOS, + OUT LPINT lpErrno); + +INT +WSPAPI +WSPDuplicateSocket( + IN SOCKET s, + IN DWORD dwProcessId, + OUT LPWSAPROTOCOL_INFOW lpProtocolInfo, + OUT LPINT lpErrno); + +INT +WSPAPI +WSPEnumNetworkEvents( + IN SOCKET s, + IN WSAEVENT hEventObject, + OUT LPWSANETWORKEVENTS lpNetworkEvents, + OUT LPINT lpErrno); + +INT +WSPAPI +WSPEventSelect( + IN SOCKET s, + IN WSAEVENT hEventObject, + IN LONG lNetworkEvents, + OUT LPINT lpErrno); + +BOOL +WSPAPI +WSPGetOverlappedResult( + IN SOCKET s, + IN LPWSAOVERLAPPED lpOverlapped, + OUT LPDWORD lpcbTransfer, + IN BOOL fWait, + OUT LPDWORD lpdwFlags, + OUT LPINT lpErrno); + +INT +WSPAPI +WSPGetPeerName( + IN SOCKET s, + OUT LPSOCKADDR name, + IN OUT LPINT namelen, + OUT LPINT lpErrno); + +BOOL +WSPAPI +WSPGetQOSByName( + IN SOCKET s, + IN OUT LPWSABUF lpQOSName, + OUT LPQOS lpQOS, + OUT LPINT lpErrno); + +INT +WSPAPI +WSPGetSockName( + IN SOCKET s, + OUT LPSOCKADDR name, + IN OUT LPINT namelen, + OUT LPINT lpErrno); + +INT +WSPAPI +WSPGetSockOpt( + IN SOCKET s, + IN INT level, + IN INT optname, + OUT CHAR FAR* optval, + IN OUT LPINT optlen, + OUT LPINT lpErrno); + +INT +WSPAPI +WSPIoctl( + IN SOCKET s, + IN DWORD dwIoControlCode, + IN LPVOID lpvInBuffer, + IN DWORD cbInBuffer, + OUT LPVOID lpvOutBuffer, + IN DWORD cbOutBuffer, + OUT LPDWORD lpcbBytesReturned, + IN LPWSAOVERLAPPED lpOverlapped, + IN LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, + IN LPWSATHREADID lpThreadId, + OUT LPINT lpErrno); + +SOCKET +WSPAPI +WSPJoinLeaf( + IN SOCKET s, + IN CONST SOCKADDR *name, + IN INT namelen, + IN LPWSABUF lpCallerData, + OUT LPWSABUF lpCalleeData, + IN LPQOS lpSQOS, + IN LPQOS lpGQOS, + IN DWORD dwFlags, + OUT LPINT lpErrno); + +INT +WSPAPI +WSPListen( + IN SOCKET s, + IN INT backlog, + OUT LPINT lpErrno); + +INT +WSPAPI +WSPRecv( + IN SOCKET s, + IN OUT LPWSABUF lpBuffers, + IN DWORD dwBufferCount, + OUT LPDWORD lpNumberOfBytesRecvd, + IN OUT LPDWORD lpFlags, + IN LPWSAOVERLAPPED lpOverlapped, + IN LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, + IN LPWSATHREADID lpThreadId, + OUT LPINT lpErrno); + +INT +WSPAPI +WSPRecvDisconnect( + IN SOCKET s, + OUT LPWSABUF lpInboundDisconnectData, + OUT LPINT lpErrno); + +INT +WSPAPI +WSPRecvFrom( + IN SOCKET s, + IN OUT LPWSABUF lpBuffers, + IN DWORD dwBufferCount, + OUT LPDWORD lpNumberOfBytesRecvd, + IN OUT LPDWORD lpFlags, + OUT LPSOCKADDR lpFrom, + IN OUT LPINT lpFromlen, + IN LPWSAOVERLAPPED lpOverlapped, + IN LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, + IN LPWSATHREADID lpThreadId, + OUT LPINT lpErrno); + +INT +WSPAPI +WSPSelect( + IN INT nfds, + IN OUT LPFD_SET readfds, + IN OUT LPFD_SET writefds, + IN OUT LPFD_SET exceptfds, + IN CONST LPTIMEVAL timeout, + OUT LPINT lpErrno); + +INT +WSPAPI +WSPSend( + IN SOCKET s, + IN LPWSABUF lpBuffers, + IN DWORD dwBufferCount, + OUT LPDWORD lpNumberOfBytesSent, + IN DWORD dwFlags, + IN LPWSAOVERLAPPED lpOverlapped, + IN LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, + IN LPWSATHREADID lpThreadId, + OUT LPINT lpErrno); + +INT +WSPAPI +WSPSendDisconnect( + IN SOCKET s, + IN LPWSABUF lpOutboundDisconnectData, + OUT LPINT lpErrno); + +INT +WSPAPI +WSPSendTo( + IN SOCKET s, + IN LPWSABUF lpBuffers, + IN DWORD dwBufferCount, + OUT LPDWORD lpNumberOfBytesSent, + IN DWORD dwFlags, + IN CONST SOCKADDR *lpTo, + IN INT iTolen, + IN LPWSAOVERLAPPED lpOverlapped, + IN LPWSAOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine, + IN LPWSATHREADID lpThreadId, + OUT LPINT lpErrno); + +INT +WSPAPI +WSPSetSockOpt( + IN SOCKET s, + IN INT level, + IN INT optname, + IN CONST CHAR FAR* optval, + IN INT optlen, + OUT LPINT lpErrno); + +INT +WSPAPI +WSPShutdown( + IN SOCKET s, + IN INT how, + OUT LPINT lpErrno); + +SOCKET +WSPAPI +WSPSocket( + IN INT af, + IN INT type, + IN INT protocol, + IN LPWSAPROTOCOL_INFOW lpProtocolInfo, + IN GROUP g, + IN DWORD dwFlags, + OUT LPINT lpErrno); + +INT +WSPAPI +WSPStringToAddress( + IN LPWSTR AddressString, + IN INT AddressFamily, + IN LPWSAPROTOCOL_INFOW lpProtocolInfo, + OUT LPSOCKADDR lpAddress, + IN OUT LPINT lpAddressLength, + OUT LPINT lpErrno); + + +PSOCKET_INFORMATION GetSocketStructure( + SOCKET Handle +); + +VOID DeleteSocketStructure( SOCKET Handle ); + +int GetSocketInformation( + PSOCKET_INFORMATION Socket, + ULONG AfdInformationClass, + PULONG Ulong OPTIONAL, + PLARGE_INTEGER LargeInteger OPTIONAL +); + +int SetSocketInformation( + PSOCKET_INFORMATION Socket, + ULONG AfdInformationClass, + PULONG Ulong OPTIONAL, + PLARGE_INTEGER LargeInteger OPTIONAL +); + +int CreateContext( + PSOCKET_INFORMATION Socket +); + +int SockAsyncThread( + PVOID ThreadParam +); + +VOID +SockProcessAsyncSelect( + PSOCKET_INFORMATION Socket, + PASYNC_DATA AsyncData +); + +VOID +SockAsyncSelectCompletionRoutine( + PVOID Context, + PIO_STATUS_BLOCK IoStatusBlock +); + +BOOLEAN +SockCreateOrReferenceAsyncThread( + VOID +); + +BOOLEAN SockGetAsyncSelectHelperAfdHandle( + VOID +); + +VOID SockProcessQueuedAsyncSelect( + PVOID Context, + PIO_STATUS_BLOCK IoStatusBlock +); + +VOID +SockReenableAsyncSelectEvent ( + IN PSOCKET_INFORMATION Socket, + IN ULONG Event + ); + +DWORD MsafdReturnWithErrno( NTSTATUS Status, LPINT Errno, DWORD Received, + LPDWORD ReturnedBytes ); + +typedef VOID (*PASYNC_COMPLETION_ROUTINE)(PVOID Context, PIO_STATUS_BLOCK IoStatusBlock); + +#endif /* __MSAFD_H */ + +/* EOF */ diff --git a/reactos/dll/msafd/msafd.rc b/reactos/dll/msafd/msafd.rc new file mode 100644 index 00000000000..c0e0d4a5908 --- /dev/null +++ b/reactos/dll/msafd/msafd.rc @@ -0,0 +1,5 @@ +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "Ancillary Function Driver DLL\0" +#define REACTOS_STR_INTERNAL_NAME "msafd\0" +#define REACTOS_STR_ORIGINAL_FILENAME "msafd.dll\0" +#include diff --git a/reactos/dll/msafd/msafd.xml b/reactos/dll/msafd/msafd.xml new file mode 100644 index 00000000000..d2f6ba66ad0 --- /dev/null +++ b/reactos/dll/msafd/msafd.xml @@ -0,0 +1,20 @@ + + + include + + + + + msafd.h + ntdll + kernel32 + advapi32 + + dllmain.c + event.c + helpers.c + sndrcv.c + stubs.c + + msafd.rc + diff --git a/reactos/dll/msgina/msgina.c b/reactos/dll/msgina/msgina.c new file mode 100644 index 00000000000..3659e529a1a --- /dev/null +++ b/reactos/dll/msgina/msgina.c @@ -0,0 +1,562 @@ +/* + * ReactOS kernel + * Copyright (C) 1998, 1999, 2000, 2001 ReactOS Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +/* $Id$ + * + * PROJECT: ReactOS msgina.dll + * FILE: lib/msgina/msgina.c + * PURPOSE: ReactOS Logon GINA DLL + * PROGRAMMER: Thomas Weidenmueller (w3seek@users.sourceforge.net) + * UPDATE HISTORY: + * 24-11-2003 Created + */ +#include +#include +#include "msgina.h" +#include "resource.h" + +#include + +extern HINSTANCE hDllInstance; + +typedef struct _DISPLAYSTATUSMSG +{ + PGINA_CONTEXT Context; + HDESK hDesktop; + DWORD dwOptions; + PWSTR pTitle; + PWSTR pMessage; + HANDLE StartupEvent; +} DISPLAYSTATUSMSG, *PDISPLAYSTATUSMSG; + +INT_PTR CALLBACK +LoggedOnDlgProc( + HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam +) +{ + switch(uMsg) + { + case WM_COMMAND: + { + switch(LOWORD(wParam)) + { + case IDYES: + case IDNO: + { + EndDialog(hwndDlg, LOWORD(wParam)); + break; + } + } + return FALSE; + } + case WM_INITDIALOG: + { + SetFocus(GetDlgItem(hwndDlg, IDNO)); + break; + } + case WM_CLOSE: + { + EndDialog(hwndDlg, IDNO); + return TRUE; + } + } + return FALSE; +} + + +/* + * @implemented + */ +BOOL WINAPI +WlxNegotiate( + DWORD dwWinlogonVersion, + PDWORD pdwDllVersion) +{ + if(!pdwDllVersion || (dwWinlogonVersion < GINA_VERSION)) + return FALSE; + + *pdwDllVersion = GINA_VERSION; + + return TRUE; +} + + +/* + * @implemented + */ +BOOL WINAPI +WlxInitialize( + LPWSTR lpWinsta, + HANDLE hWlx, + PVOID pvReserved, + PVOID pWinlogonFunctions, + PVOID *pWlxContext) +{ + PGINA_CONTEXT pgContext; + + pgContext = (PGINA_CONTEXT)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, sizeof(GINA_CONTEXT)); + if(!pgContext) + return FALSE; + + /* return the context to winlogon */ + *pWlxContext = (PVOID)pgContext; + + pgContext->hDllInstance = hDllInstance; + + /* save pointer to dispatch table */ + pgContext->pWlxFuncs = (PWLX_DISPATCH_VERSION)pWinlogonFunctions; + + /* save the winlogon handle used to call the dispatch functions */ + pgContext->hWlx = hWlx; + + /* save window station */ + pgContext->station = lpWinsta; + + /* clear status window handle */ + pgContext->hStatusWindow = 0; + + /* notify winlogon that we will use the default SAS */ + pgContext->pWlxFuncs->WlxUseCtrlAltDel(hWlx); + + return TRUE; +} + + +/* + * @implemented + */ +BOOL WINAPI +WlxStartApplication( + PVOID pWlxContext, + PWSTR pszDesktopName, + PVOID pEnvironment, + PWSTR pszCmdLine) +{ + PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext; + STARTUPINFO si; + PROCESS_INFORMATION pi; + BOOL Ret; + + si.cb = sizeof(STARTUPINFO); + si.lpReserved = NULL; + si.lpTitle = pszCmdLine; + si.dwX = si.dwY = si.dwXSize = si.dwYSize = 0L; + si.dwFlags = 0; + si.wShowWindow = SW_SHOW; + si.lpReserved2 = NULL; + si.cbReserved2 = 0; + si.lpDesktop = pszDesktopName; + + Ret = CreateProcessAsUser(pgContext->UserToken, + NULL, + pszCmdLine, + NULL, + NULL, + FALSE, + CREATE_UNICODE_ENVIRONMENT, + pEnvironment, + NULL, + &si, + &pi); + + VirtualFree(pEnvironment, 0, MEM_RELEASE); + return Ret; +} + + +/* + * @implemented + */ +BOOL WINAPI +WlxActivateUserShell( + PVOID pWlxContext, + PWSTR pszDesktopName, + PWSTR pszMprLogonScript, + PVOID pEnvironment) +{ + PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext; + STARTUPINFO si; + PROCESS_INFORMATION pi; + HKEY hKey; + DWORD BufSize, ValueType; + WCHAR pszUserInitApp[MAX_PATH]; + WCHAR pszExpUserInitApp[MAX_PATH]; + BOOL Ret; + + /* get the path of userinit */ + if(RegOpenKeyExW(HKEY_LOCAL_MACHINE, + L"SOFTWARE\\ReactOS\\Windows NT\\CurrentVersion\\Winlogon", + 0, KEY_QUERY_VALUE, &hKey) != ERROR_SUCCESS) + {ERR("GINA: Failed: 1\n"); + VirtualFree(pEnvironment, 0, MEM_RELEASE); + return FALSE; + } + BufSize = MAX_PATH * sizeof(WCHAR); + if((RegQueryValueEx(hKey, L"Userinit", NULL, &ValueType, (LPBYTE)pszUserInitApp, + &BufSize) != ERROR_SUCCESS) || + !((ValueType == REG_SZ) || (ValueType == REG_EXPAND_SZ))) + {ERR("GINA: Failed: 2\n"); + RegCloseKey(hKey); + VirtualFree(pEnvironment, 0, MEM_RELEASE); + return FALSE; + } + RegCloseKey(hKey); + + /* start userinit */ + /* FIXME - allow to start more applications that are comma-separated */ + si.cb = sizeof(STARTUPINFO); + si.lpReserved = NULL; + si.lpTitle = L"userinit"; + si.dwX = si.dwY = si.dwXSize = si.dwYSize = 0; + si.dwFlags = 0; + si.wShowWindow = SW_SHOW; + si.lpReserved2 = NULL; + si.cbReserved2 = 0; + si.lpDesktop = pszDesktopName; + + ExpandEnvironmentStrings(pszUserInitApp, pszExpUserInitApp, MAX_PATH); + + Ret = CreateProcessAsUser(pgContext->UserToken, + pszExpUserInitApp, + NULL, + NULL, + NULL, + FALSE, + CREATE_UNICODE_ENVIRONMENT, + pEnvironment, + NULL, + &si, + &pi); + if(!Ret) ERR("GINA: Failed: 3\n"); + VirtualFree(pEnvironment, 0, MEM_RELEASE); + return Ret; +} + + +/* + * @implemented + */ +int WINAPI +WlxLoggedOnSAS( + PVOID pWlxContext, + DWORD dwSasType, + PVOID pReserved) +{ + PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext; + int SasAction = WLX_SAS_ACTION_NONE; + + switch(dwSasType) + { + case WLX_SAS_TYPE_CTRL_ALT_DEL: + { + int Result; + /* display "Are you sure you want to log off?" dialog */ + Result = pgContext->pWlxFuncs->WlxDialogBoxParam(pgContext->hWlx, + pgContext->hDllInstance, + (LPTSTR)IDD_LOGOFF_DLG, + NULL, + LoggedOnDlgProc, + (LPARAM)pgContext); + if(Result == IDOK) + { + SasAction = WLX_SAS_ACTION_LOCK_WKSTA; + } + break; + } + case WLX_SAS_TYPE_SC_INSERT: + { + FIXME("WlxLoggedOnSAS: SasType WLX_SAS_TYPE_SC_INSERT not supported!\n"); + break; + } + case WLX_SAS_TYPE_SC_REMOVE: + { + FIXME("WlxLoggedOnSAS: SasType WLX_SAS_TYPE_SC_REMOVE not supported!\n"); + break; + } + case WLX_SAS_TYPE_TIMEOUT: + { + FIXME("WlxLoggedOnSAS: SasType WLX_SAS_TYPE_TIMEOUT not supported!\n"); + break; + } + default: + { + WARN("WlxLoggedOnSAS: Unknown SasType: 0x%x\n", dwSasType); + break; + } + } + + return SasAction; +} + + +BOOL +CALLBACK +StatusMessageWindowProc( + HWND hwndDlg, + UINT uMsg, + WPARAM wParam, + LPARAM lParam +) +{ + switch(uMsg) + { + case WM_INITDIALOG: + { + PDISPLAYSTATUSMSG msg = (PDISPLAYSTATUSMSG)lParam; + if(!msg) + return FALSE; + + msg->Context->hStatusWindow = hwndDlg; + + if(msg->pTitle) + SetWindowText(hwndDlg, msg->pTitle); + SetDlgItemText(hwndDlg, IDC_STATUSLABEL, msg->pMessage); + if(!msg->Context->SignaledStatusWindowCreated) + { + msg->Context->SignaledStatusWindowCreated = TRUE; + SetEvent(msg->StartupEvent); + } + break; + } + } + return FALSE; +} + + +DWORD WINAPI +StartupWindowThread(LPVOID lpParam) +{ + HDESK OldDesk; + PDISPLAYSTATUSMSG msg = (PDISPLAYSTATUSMSG)lpParam; + + OldDesk = GetThreadDesktop(GetCurrentThreadId()); + + if(!SetThreadDesktop(msg->hDesktop)) + { + HeapFree(GetProcessHeap(), 0, lpParam); + return FALSE; + } + DialogBoxParam(hDllInstance, + MAKEINTRESOURCE(IDD_STATUSWINDOW), + 0, + StatusMessageWindowProc, + (LPARAM)lpParam); + SetThreadDesktop(OldDesk); + + msg->Context->hStatusWindow = 0; + msg->Context->SignaledStatusWindowCreated = FALSE; + + HeapFree(GetProcessHeap(), 0, lpParam); + return TRUE; +} + + +/* + * @implemented + */ +BOOL WINAPI +WlxDisplayStatusMessage( + PVOID pWlxContext, + HDESK hDesktop, + DWORD dwOptions, + PWSTR pTitle, + PWSTR pMessage) +{ + PDISPLAYSTATUSMSG msg; + HANDLE Thread; + DWORD ThreadId; + PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext; + + if(!pgContext->hStatusWindow) + { + msg = (PDISPLAYSTATUSMSG)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(DISPLAYSTATUSMSG)); + if(!msg) + return FALSE; + + msg->Context = pgContext; + msg->dwOptions = dwOptions; + msg->pTitle = pTitle; + msg->pMessage = pMessage; + msg->hDesktop = hDesktop; + + msg->StartupEvent = CreateEvent(NULL, + TRUE, + FALSE, + NULL); + + if(!msg->StartupEvent) + return FALSE; + + Thread = CreateThread(NULL, + 0, + StartupWindowThread, + (PVOID)msg, + 0, + &ThreadId); + if(Thread) + { + CloseHandle(Thread); + WaitForSingleObject(msg->StartupEvent, INFINITE); + CloseHandle(msg->StartupEvent); + return TRUE; + } + + return FALSE; + } + + if(pTitle) + SetWindowText(pgContext->hStatusWindow, pTitle); + + SetDlgItemText(pgContext->hStatusWindow, IDC_STATUSLABEL, pMessage); + + return TRUE; +} + + +/* + * @implemented + */ +BOOL WINAPI +WlxRemoveStatusMessage( + PVOID pWlxContext) +{ + PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext; + if(pgContext->hStatusWindow) + { + EndDialog(pgContext->hStatusWindow, 0); + pgContext->hStatusWindow = 0; + } + + return TRUE; +} + + +/* + * @implemented + */ +VOID WINAPI +WlxDisplaySASNotice( + PVOID pWlxContext) +{ + PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext; + pgContext->pWlxFuncs->WlxSasNotify(pgContext->hWlx, WLX_SAS_TYPE_CTRL_ALT_DEL); +} + + +static PWSTR +DuplicationString(PWSTR Str) +{ + DWORD cb; + PWSTR NewStr; + + cb = (wcslen(Str) + 1) * sizeof(WCHAR); + if((NewStr = LocalAlloc(LMEM_FIXED, cb))) + { + memcpy(NewStr, Str, cb); + } + return NewStr; +} + + +/* + * @unimplemented + */ +int WINAPI +WlxLoggedOutSAS( + PVOID pWlxContext, + DWORD dwSasType, + PLUID pAuthenticationId, + PSID pLogonSid, + PDWORD pdwOptions, + PHANDLE phToken, + PWLX_MPR_NOTIFY_INFO pNprNotifyInfo, + PVOID *pProfile) +{ + PGINA_CONTEXT pgContext = (PGINA_CONTEXT)pWlxContext; + TOKEN_STATISTICS Stats; + DWORD cbStats; + + if(!phToken) + { + WARN("msgina: phToken == NULL!\n"); + return WLX_SAS_ACTION_NONE; + } + + if(!LogonUser(L"Administrator", NULL, L"Secrect", + LOGON32_LOGON_INTERACTIVE, /* FIXME - use LOGON32_LOGON_UNLOCK instead! */ + LOGON32_PROVIDER_DEFAULT, + phToken)) + { + WARN("msgina: Logonuser() failed\n"); + return WLX_SAS_ACTION_NONE; + } + + if(!(*phToken)) + { + WARN("msgina: *phToken == NULL!\n"); + return WLX_SAS_ACTION_NONE; + } + + pgContext->UserToken =*phToken; + + *pdwOptions = 0; + *pProfile =NULL; + + if(!GetTokenInformation(*phToken, + TokenStatistics, + (PVOID)&Stats, + sizeof(TOKEN_STATISTICS), + &cbStats)) + { + WARN("msgina: Couldn't get Autentication id from user token!\n"); + return WLX_SAS_ACTION_NONE; + } + *pAuthenticationId = Stats.AuthenticationId; + pNprNotifyInfo->pszUserName = DuplicationString(L"Administrator"); + pNprNotifyInfo->pszDomain = NULL; + pNprNotifyInfo->pszPassword = DuplicationString(L"Secret"); + pNprNotifyInfo->pszOldPassword = NULL; + + return WLX_SAS_ACTION_LOGON; +} + + +BOOL STDCALL +DllMain( + HINSTANCE hinstDLL, + DWORD dwReason, + LPVOID lpvReserved) +{ + switch (dwReason) + { + case DLL_PROCESS_ATTACH: + /* fall through */ + case DLL_THREAD_ATTACH: + hDllInstance = hinstDLL; + break; + case DLL_THREAD_DETACH: + break; + case DLL_PROCESS_DETACH: + break; + } + return TRUE; +} + diff --git a/reactos/dll/msgina/msgina.def b/reactos/dll/msgina/msgina.def new file mode 100644 index 00000000000..063c4f311ae --- /dev/null +++ b/reactos/dll/msgina/msgina.def @@ -0,0 +1,55 @@ +LIBRARY msgina.dll + +EXPORTS +;MSGINA.1@0 +;MSGINA.2@0 +;MSGINA.3@0 +;MSGINA.4@0 +;MSGINA.5@0 +;MSGINA.6@0 +;MSGINA.7@0 +;MSGINA.8@0 +;MSGINA.9@0 +;MSGINA.10@0 +;MSGINA.11@0 +;MSGINA.12@0 +;MSGINA.13@0 +;MSGINA.14@0 +;MSGINA.15@0 +;MSGINA.16@0 +;MSGINA.17@0 +;MSGINA.18@0 +;MSGINA.19@0 +;MSGINA.20@0 +;MSGINA.21@0 +;MSGINA.22@0 +;MSGINA.23@0 +;MSGINA.24@0 +;MSGINA.25@0 +;MSGINA.26@0 +;MSGINA.27@0 +;MSGINA.28@0 +ShellShutdownDialog@12 +WlxActivateUserShell@16 +WlxDisplayLockedNotice@4 +WlxDisplaySASNotice@4 +WlxInitialize@20 +WlxIsLockOk@4 +WlxIsLogoffOk@4 +WlxLoggedOnSAS@12 +WlxLoggedOutSAS@32 +WlxLogoff@4 +WlxNegotiate@8 +WlxShutdown@8 +WlxWkstaLockedSAS@8 +WlxScreenSaverNotify@8 +WlxStartApplication@16 +WlxDisplayStatusMessage@20 +WlxGetStatusMessage@16 +WlxNetworkProviderLoad@8 +;WlxReconnectNotify@0 +WlxRemoveStatusMessage@4 +WlxDisconnectNotify@4 +WlxGetConsoleSwitchCredentials@8 + +; EOF diff --git a/reactos/dll/msgina/msgina.h b/reactos/dll/msgina/msgina.h new file mode 100644 index 00000000000..71046d8b1ef --- /dev/null +++ b/reactos/dll/msgina/msgina.h @@ -0,0 +1,22 @@ +#ifndef __MSGINA_H +#define __MSGINA_H + +#define GINA_VERSION (WLX_VERSION_1_3) + +#define PWLX_DISPATCH_VERSION PWLX_DISPATCH_VERSION_1_3 + +typedef struct { + HANDLE hWlx; + LPWSTR station; + PWLX_DISPATCH_VERSION pWlxFuncs; + HANDLE hDllInstance; + HANDLE UserToken; + HWND hStatusWindow; + BOOL SignaledStatusWindowCreated; +} GINA_CONTEXT, *PGINA_CONTEXT; + +HINSTANCE hDllInstance; + +#endif /* __MSGINA_H */ + +/* EOF */ diff --git a/reactos/dll/msgina/msgina.rc b/reactos/dll/msgina/msgina.rc new file mode 100644 index 00000000000..d442474f1d3 --- /dev/null +++ b/reactos/dll/msgina/msgina.rc @@ -0,0 +1,34 @@ +#include +#include "resource.h" + +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "ReactOS Logon GINA DLL\0" +#define REACTOS_STR_INTERNAL_NAME "msgina\0" +#define REACTOS_STR_ORIGINAL_FILENAME "msgina.dll\0" +#include + +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +IDI_LOGOFFICON ICON "resources/ico_logoff.ico" + +IDD_LOGOFF_DLG DIALOG 0, 0, 188, 60 +STYLE DS_FIXEDSYS | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU +CAPTION "Log Off ReactOS" +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +FONT 8, "MS Sans Serif" +{ + ICON IDI_LOGOFFICON, IDC_LOGOFFICON, 7, 7, 21, 21, SS_ICON | WS_CHILD | WS_VISIBLE + LTEXT "Are you sure you want to log off?", -1, 35, 16, 146, 8, SS_LEFT | WS_CHILD | WS_VISIBLE | WS_GROUP + PUSHBUTTON "&Log Off", 1, 41, 39, 50, 14, BS_DEFPUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP + PUSHBUTTON "&No", 2, 95, 39, 50, 14, BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE | WS_TABSTOP +} + +IDD_STATUSWINDOW DIALOG 0, 0, 274, 26 +STYLE DS_SETFONT | DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_VISIBLE | WS_CAPTION +CAPTION "Please wait..." +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +FONT 8, "MS Sans Serif" +{ + LTEXT "", IDC_STATUSLABEL, 7, 8, 234, 12, SS_ENDELLIPSIS +} + diff --git a/reactos/dll/msgina/msgina.xml b/reactos/dll/msgina/msgina.xml new file mode 100644 index 00000000000..25eddd2315f --- /dev/null +++ b/reactos/dll/msgina/msgina.xml @@ -0,0 +1,16 @@ + + + . + include + + + + + ntdll + kernel32 + advapi32 + user32 + msgina.c + stubs.c + msgina.rc + diff --git a/reactos/dll/msgina/resource.h b/reactos/dll/msgina/resource.h new file mode 100644 index 00000000000..7c7f0b0db8a --- /dev/null +++ b/reactos/dll/msgina/resource.h @@ -0,0 +1,14 @@ +#ifndef __MSGINA_RESOURCE_H +#define __MSGINA_RESOURCE_H + +#define IDD_LOGOFF_DLG 2250 +#define IDD_STATUSWINDOW 2450 + +#define IDC_LOGOFFICON 21 +#define IDC_STATUSLABEL 2451 + +#define IDI_LOGOFFICON 2251 + +#endif /* __MSGINA_RESOURCE_H */ + +/* EOF */ diff --git a/reactos/dll/msgina/resources/ico_logoff.ico b/reactos/dll/msgina/resources/ico_logoff.ico new file mode 100644 index 00000000000..7bb5488b4aa Binary files /dev/null and b/reactos/dll/msgina/resources/ico_logoff.ico differ diff --git a/reactos/dll/msgina/stubs.c b/reactos/dll/msgina/stubs.c new file mode 100644 index 00000000000..e36fd15897d --- /dev/null +++ b/reactos/dll/msgina/stubs.c @@ -0,0 +1,177 @@ +/* $Id$ + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS msgina.dll + * FILE: lib/msgina/stubs.c + * PURPOSE: msgina.dll stubs + * PROGRAMMER: Thomas Weidenmueller (w3seek@users.sourceforge.net) + * NOTES: If you implement a function, remove it from this file + * UPDATE HISTORY: + * 24-11-2003 Created + */ +#include +#include + +#include + +/* + * @unimplemented + */ +DWORD WINAPI +ShellShutdownDialog( + HWND hParent, + DWORD Unknown, + BOOL bHideLogoff) +{ + /* Return values: + * 0x00: Cancelled/Help + * 0x01: Log off user + * 0x02: Shutdown + * 0x04: Reboot + * 0x10: Standby + * 0x40: Hibernate + */ + UNIMPLEMENTED; + return FALSE; +} + + +/* + * @unimplemented + */ +VOID WINAPI +WlxDisplayLockedNotice( + PVOID pWlxContext) +{ + UNIMPLEMENTED; + return; +} + + +/* + * @unimplemented + */ +BOOL WINAPI +WlxIsLockOk( + PVOID pWlxContext) +{ + UNIMPLEMENTED; + return FALSE; +} + + +/* + * @unimplemented + */ +BOOL WINAPI +WlxIsLogoffOk( + PVOID pWlxContext) +{ + UNIMPLEMENTED; + return FALSE; +} + + +/* + * @unimplemented + */ +VOID WINAPI +WlxLogoff( + PVOID pWlxContext) +{ + UNIMPLEMENTED; + return; +} + + +/* + * @unimplemented + */ +VOID WINAPI +WlxShutdown( + PVOID pWlxContext, + DWORD ShutdownType) +{ + UNIMPLEMENTED; + return; +} + + +/* + * @unimplemented + */ +int WINAPI +WlxWkstaLockedSAS( + PVOID pWlxContext, + DWORD dwSasType) +{ + UNIMPLEMENTED; + return 0; +} + + +/* + * @unimplemented + */ +BOOL WINAPI +WlxScreenSaverNotify( + PVOID pWlxContext, + BOOL *pSecure) +{ + UNIMPLEMENTED; + return FALSE; +} + + +/* + * @unimplemented + */ +BOOL WINAPI +WlxGetStatusMessage( + PVOID pWlxContext, + DWORD *pdwOptions, + PWSTR pMessage, + DWORD dwBufferSize) +{ + UNIMPLEMENTED; + return FALSE; +} + + +/* + * @unimplemented + */ +BOOL WINAPI +WlxNetworkProviderLoad( + PVOID pWlxContext, + PWLX_MPR_NOTIFY_INFO pNprNotifyInfo) +{ + UNIMPLEMENTED; + return FALSE; +} + + +/* + * @unimplemented + */ +VOID WINAPI +WlxDisconnectNotify( + PVOID pWlxContext) +{ + UNIMPLEMENTED; + return; +} + + +/* + * @unimplemented + */ +BOOL WINAPI +WlxGetConsoleSwitchCredentials( + PVOID pWlxContext, + PVOID pCredInfo) +{ + UNIMPLEMENTED; + return FALSE; +} + diff --git a/reactos/dll/msi/Makefile.in b/reactos/dll/msi/Makefile.in new file mode 100644 index 00000000000..e1216aec360 --- /dev/null +++ b/reactos/dll/msi/Makefile.in @@ -0,0 +1,68 @@ +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = ../.. +SRCDIR = @srcdir@ +VPATH = @srcdir@ +MODULE = msi.dll +IMPORTLIB = libmsi.$(IMPLIBEXT) +IMPORTS = shell32 shlwapi cabinet oleaut32 ole32 version user32 gdi32 advapi32 kernel32 +EXTRALIBS = -luuid $(LIBUNICODE) + +C_SRCS = \ + action.c \ + appsearch.c \ + classes.c \ + create.c \ + custom.c \ + database.c \ + delete.c \ + dialog.c \ + distinct.c \ + events.c \ + files.c \ + format.c \ + handle.c \ + helpers.c \ + insert.c \ + install.c \ + msi.c \ + msiquery.c \ + order.c \ + package.c \ + preview.c \ + record.c \ + registry.c \ + regsvr.c \ + select.c \ + source.c \ + string.c \ + suminfo.c \ + table.c \ + tokenize.c \ + update.c \ + upgrade.c \ + where.c + +RC_SRCS = msi.rc + +EXTRA_SRCS = sql.y cond.y +EXTRA_OBJS = sql.tab.o cond.tab.o + +SUBDIRS = tests + +@MAKE_DLL_RULES@ + +sql.tab.c sql.tab.h: sql.y + $(BISON) -p SQL_ -d $(SRCDIR)/sql.y -o sql.tab.c + +cond.tab.c cond.tab.h: cond.y + $(BISON) -p COND_ -d $(SRCDIR)/cond.y -o cond.tab.c + +# hack to allow parallel make +sql.tab.h: sql.tab.c +sql.tab.o: sql.tab.h +cond.tab.h: cond.tab.c +cond.tab.o: cond.tab.h + +tokenize.o: sql.tab.h + +### Dependencies: diff --git a/reactos/dll/msi/action.c b/reactos/dll/msi/action.c new file mode 100644 index 00000000000..ed32e96137d --- /dev/null +++ b/reactos/dll/msi/action.c @@ -0,0 +1,4327 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2004,2005 Aric Stewart 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Pages I need + * +http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/installexecutesequence_table.asp + +http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/standard_actions_reference.asp + */ + +#include + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "winreg.h" +#include "wine/debug.h" +#include "msidefs.h" +#include "msipriv.h" +#include "winuser.h" +#include "shlobj.h" +#include "wine/unicode.h" +#include "winver.h" +#include "action.h" + +#define REG_PROGRESS_VALUE 13200 +#define COMPONENT_PROGRESS_VALUE 24000 + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +/* + * Prototypes + */ +static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran); +static UINT ACTION_ProcessUISequence(MSIPACKAGE *package); +static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI); + +/* + * consts and values used + */ +static const WCHAR c_colon[] = {'C',':','\\',0}; + +static const WCHAR szCreateFolders[] = + {'C','r','e','a','t','e','F','o','l','d','e','r','s',0}; +static const WCHAR szCostFinalize[] = + {'C','o','s','t','F','i','n','a','l','i','z','e',0}; +const WCHAR szInstallFiles[] = + {'I','n','s','t','a','l','l','F','i','l','e','s',0}; +const WCHAR szDuplicateFiles[] = + {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0}; +static const WCHAR szWriteRegistryValues[] = + {'W','r','i','t','e','R','e','g','i','s','t','r','y', + 'V','a','l','u','e','s',0}; +static const WCHAR szCostInitialize[] = + {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0}; +static const WCHAR szFileCost[] = + {'F','i','l','e','C','o','s','t',0}; +static const WCHAR szInstallInitialize[] = + {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0}; +static const WCHAR szInstallValidate[] = + {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0}; +static const WCHAR szLaunchConditions[] = + {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0}; +static const WCHAR szProcessComponents[] = + {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0}; +static const WCHAR szRegisterTypeLibraries[] = + {'R','e','g','i','s','t','e','r','T','y','p','e', + 'L','i','b','r','a','r','i','e','s',0}; +const WCHAR szRegisterClassInfo[] = + {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0}; +const WCHAR szRegisterProgIdInfo[] = + {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0}; +static const WCHAR szCreateShortcuts[] = + {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0}; +static const WCHAR szPublishProduct[] = + {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0}; +static const WCHAR szWriteIniValues[] = + {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0}; +static const WCHAR szSelfRegModules[] = + {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0}; +static const WCHAR szPublishFeatures[] = + {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0}; +static const WCHAR szRegisterProduct[] = + {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0}; +static const WCHAR szInstallExecute[] = + {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0}; +static const WCHAR szInstallExecuteAgain[] = + {'I','n','s','t','a','l','l','E','x','e','c','u','t','e', + 'A','g','a','i','n',0}; +static const WCHAR szInstallFinalize[] = + {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0}; +static const WCHAR szForceReboot[] = + {'F','o','r','c','e','R','e','b','o','o','t',0}; +static const WCHAR szResolveSource[] = + {'R','e','s','o','l','v','e','S','o','u','r','c','e',0}; +const WCHAR szAppSearch[] = + {'A','p','p','S','e','a','r','c','h',0}; +static const WCHAR szAllocateRegistrySpace[] = + {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y', + 'S','p','a','c','e',0}; +static const WCHAR szBindImage[] = + {'B','i','n','d','I','m','a','g','e',0}; +static const WCHAR szCCPSearch[] = + {'C','C','P','S','e','a','r','c','h',0}; +static const WCHAR szDeleteServices[] = + {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0}; +static const WCHAR szDisableRollback[] = + {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0}; +static const WCHAR szExecuteAction[] = + {'E','x','e','c','u','t','e','A','c','t','i','o','n',0}; +const WCHAR szFindRelatedProducts[] = + {'F','i','n','d','R','e','l','a','t','e','d', + 'P','r','o','d','u','c','t','s',0}; +static const WCHAR szInstallAdminPackage[] = + {'I','n','s','t','a','l','l','A','d','m','i','n', + 'P','a','c','k','a','g','e',0}; +static const WCHAR szInstallSFPCatalogFile[] = + {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g', + 'F','i','l','e',0}; +static const WCHAR szIsolateComponents[] = + {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0}; +const WCHAR szMigrateFeatureStates[] = + {'M','i','g','r','a','t','e','F','e','a','t','u','r','e', + 'S','t','a','t','e','s',0}; +const WCHAR szMoveFiles[] = + {'M','o','v','e','F','i','l','e','s',0}; +static const WCHAR szMsiPublishAssemblies[] = + {'M','s','i','P','u','b','l','i','s','h', + 'A','s','s','e','m','b','l','i','e','s',0}; +static const WCHAR szMsiUnpublishAssemblies[] = + {'M','s','i','U','n','p','u','b','l','i','s','h', + 'A','s','s','e','m','b','l','i','e','s',0}; +static const WCHAR szInstallODBC[] = + {'I','n','s','t','a','l','l','O','D','B','C',0}; +static const WCHAR szInstallServices[] = + {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0}; +const WCHAR szPatchFiles[] = + {'P','a','t','c','h','F','i','l','e','s',0}; +static const WCHAR szPublishComponents[] = + {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0}; +static const WCHAR szRegisterComPlus[] = + {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0}; +const WCHAR szRegisterExtensionInfo[] = + {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n', + 'I','n','f','o',0}; +static const WCHAR szRegisterFonts[] = + {'R','e','g','i','s','t','e','r','F','o','n','t','s',0}; +const WCHAR szRegisterMIMEInfo[] = + {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0}; +static const WCHAR szRegisterUser[] = + {'R','e','g','i','s','t','e','r','U','s','e','r',0}; +const WCHAR szRemoveDuplicateFiles[] = + {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e', + 'F','i','l','e','s',0}; +static const WCHAR szRemoveEnvironmentStrings[] = + {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t', + 'S','t','r','i','n','g','s',0}; +const WCHAR szRemoveExistingProducts[] = + {'R','e','m','o','v','e','E','x','i','s','t','i','n','g', + 'P','r','o','d','u','c','t','s',0}; +const WCHAR szRemoveFiles[] = + {'R','e','m','o','v','e','F','i','l','e','s',0}; +static const WCHAR szRemoveFolders[] = + {'R','e','m','o','v','e','F','o','l','d','e','r','s',0}; +static const WCHAR szRemoveIniValues[] = + {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0}; +static const WCHAR szRemoveODBC[] = + {'R','e','m','o','v','e','O','D','B','C',0}; +static const WCHAR szRemoveRegistryValues[] = + {'R','e','m','o','v','e','R','e','g','i','s','t','r','y', + 'V','a','l','u','e','s',0}; +static const WCHAR szRemoveShortcuts[] = + {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0}; +static const WCHAR szRMCCPSearch[] = + {'R','M','C','C','P','S','e','a','r','c','h',0}; +static const WCHAR szScheduleReboot[] = + {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0}; +static const WCHAR szSelfUnregModules[] = + {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0}; +static const WCHAR szSetODBCFolders[] = + {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0}; +static const WCHAR szStartServices[] = + {'S','t','a','r','t','S','e','r','v','i','c','e','s',0}; +static const WCHAR szStopServices[] = + {'S','t','o','p','S','e','r','v','i','c','e','s',0}; +static const WCHAR szUnpublishComponents[] = + {'U','n','p','u','b','l','i','s','h', + 'C','o','m','p','o','n','e','n','t','s',0}; +static const WCHAR szUnpublishFeatures[] = + {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0}; +const WCHAR szUnregisterClassInfo[] = + {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s', + 'I','n','f','o',0}; +static const WCHAR szUnregisterComPlus[] = + {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0}; +const WCHAR szUnregisterExtensionInfo[] = + {'U','n','r','e','g','i','s','t','e','r', + 'E','x','t','e','n','s','i','o','n','I','n','f','o',0}; +static const WCHAR szUnregisterFonts[] = + {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0}; +const WCHAR szUnregisterMIMEInfo[] = + {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0}; +const WCHAR szUnregisterProgIdInfo[] = + {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d', + 'I','n','f','o',0}; +static const WCHAR szUnregisterTypeLibraries[] = + {'U','n','r','e','g','i','s','t','e','r','T','y','p','e', + 'L','i','b','r','a','r','i','e','s',0}; +static const WCHAR szValidateProductID[] = + {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0}; +static const WCHAR szWriteEnvironmentStrings[] = + {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t', + 'S','t','r','i','n','g','s',0}; + +/* action handlers */ +typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*); + +struct _actions { + LPCWSTR action; + STANDARDACTIONHANDLER handler; +}; + +static struct _actions StandardActions[]; + + +/******************************************************** + * helper functions + ********************************************************/ + +static void ce_actiontext(MSIPACKAGE* package, LPCWSTR action) +{ + static const WCHAR szActionText[] = + {'A','c','t','i','o','n','T','e','x','t',0}; + MSIRECORD *row; + + row = MSI_CreateRecord(1); + MSI_RecordSetStringW(row,1,action); + ControlEvent_FireSubscribedEvent(package,szActionText, row); + msiobj_release(&row->hdr); +} + +static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action) +{ + static const WCHAR template_s[]= + {'A','c','t','i','o','n',' ','%','s',':',' ','%','s','.',' ', '%','s', + '.',0}; + static const WCHAR format[] = + {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0}; + static const WCHAR Query_t[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','A','c','t','i','o', 'n','T','e','x','t','`',' ', + 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=', + ' ','\'','%','s','\'',0}; + WCHAR message[1024]; + WCHAR timet[0x100]; + MSIRECORD * row = 0; + LPCWSTR ActionText; + LPWSTR deformated; + + GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100); + + row = MSI_QueryGetRecord( package->db, Query_t, action ); + if (!row) + return; + + ActionText = MSI_RecordGetString(row,2); + deformat_string(package, ActionText, &deformated); + + sprintfW(message,template_s,timet,action,deformated); + ce_actiontext(package, deformated); + msiobj_release(&row->hdr); + + row = MSI_CreateRecord(1); + MSI_RecordSetStringW(row,1,message); + + MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row); + msiobj_release(&row->hdr); + msi_free(deformated); +} + +static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start, + UINT rc) +{ + MSIRECORD * row; + static const WCHAR template_s[]= + {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ', + '%','s', '.',0}; + static const WCHAR template_e[]= + {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ', + '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ', + '%','i','.',0}; + static const WCHAR format[] = + {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0}; + WCHAR message[1024]; + WCHAR timet[0x100]; + + GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100); + if (start) + sprintfW(message,template_s,timet,action); + else + sprintfW(message,template_e,timet,action,rc); + + row = MSI_CreateRecord(1); + MSI_RecordSetStringW(row,1,message); + + MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row); + msiobj_release(&row->hdr); +} + +static int msi_get_property_int( MSIPACKAGE *package, LPCWSTR prop, int def ) +{ + LPWSTR str = msi_dup_property( package, prop ); + int val = str ? atoiW( str ) : def; + msi_free( str ); + return val; +} + +static UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine ) +{ + LPCWSTR ptr,ptr2; + BOOL quote; + DWORD len; + LPWSTR prop = NULL, val = NULL; + + if (!szCommandLine) + return ERROR_SUCCESS; + + ptr = szCommandLine; + + while (*ptr) + { + if (*ptr==' ') + { + ptr++; + continue; + } + + TRACE("Looking at %s\n",debugstr_w(ptr)); + + ptr2 = strchrW(ptr,'='); + if (!ptr2) + { + ERR("command line contains unknown string : %s\n", debugstr_w(ptr)); + break; + } + + quote = FALSE; + + len = ptr2-ptr; + prop = msi_alloc((len+1)*sizeof(WCHAR)); + memcpy(prop,ptr,len*sizeof(WCHAR)); + prop[len]=0; + ptr2++; + + len = 0; + ptr = ptr2; + while (*ptr && (quote || (!quote && *ptr!=' '))) + { + if (*ptr == '"') + quote = !quote; + ptr++; + len++; + } + + if (*ptr2=='"') + { + ptr2++; + len -= 2; + } + val = msi_alloc((len+1)*sizeof(WCHAR)); + memcpy(val,ptr2,len*sizeof(WCHAR)); + val[len] = 0; + + if (lstrlenW(prop) > 0) + { + TRACE("Found commandline property (%s) = (%s)\n", + debugstr_w(prop), debugstr_w(val)); + MSI_SetPropertyW(package,prop,val); + } + msi_free(val); + msi_free(prop); + } + + return ERROR_SUCCESS; +} + + +static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep ) +{ + LPWSTR p, *ret = NULL; + UINT count = 0; + + if (!str) + return ret; + + /* count the number of substrings */ + for ( p = (LPWSTR)str, count = 0; p; count++ ) + { + p = strchrW( p, sep ); + if (p) + p++; + } + + /* allocate space for an array of substring pointers and the substrings */ + ret = msi_alloc( (count+1) * sizeof (LPWSTR) + + (lstrlenW(str)+1) * sizeof(WCHAR) ); + if (!ret) + return ret; + + /* copy the string and set the pointers */ + p = (LPWSTR) &ret[count+1]; + lstrcpyW( p, str ); + for( count = 0; (ret[count] = p); count++ ) + { + p = strchrW( p, sep ); + if (p) + *p++ = 0; + } + + return ret; +} + +static UINT msi_apply_substorage_transform( MSIPACKAGE *package, + MSIDATABASE *patch_db, LPCWSTR name ) +{ + UINT ret = ERROR_FUNCTION_FAILED; + IStorage *stg = NULL; + HRESULT r; + + TRACE("%p %s\n", package, debugstr_w(name) ); + + if (*name++ != ':') + { + ERR("expected a colon in %s\n", debugstr_w(name)); + return ERROR_FUNCTION_FAILED; + } + + r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg ); + if (SUCCEEDED(r)) + { + ret = msi_table_apply_transform( package->db, stg ); + IStorage_Release( stg ); + ret = ERROR_SUCCESS; + } + else + ERR("failed to open substorage %s\n", debugstr_w(name)); + + return ret; +} + +static UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si ) +{ + static const WCHAR szProdID[] = { 'P','r','o','d','u','c','t','I','D',0 }; + LPWSTR guid_list, *guids, product_id; + UINT i, ret = ERROR_FUNCTION_FAILED; + + product_id = msi_dup_property( package, szProdID ); + if (!product_id) + { + /* FIXME: the property ProductID should be written into the DB somewhere */ + ERR("no product ID to check\n"); + return ERROR_SUCCESS; + } + + guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE ); + guids = msi_split_string( guid_list, ';' ); + for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ ) + { + if (!lstrcmpW( guids[i], product_id )) + ret = ERROR_SUCCESS; + } + msi_free( guids ); + msi_free( guid_list ); + msi_free( product_id ); + + return ret; +} + +static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db ) +{ + MSISUMMARYINFO *si; + LPWSTR str, *substorage; + UINT i, r = ERROR_SUCCESS; + + si = MSI_GetSummaryInformationW( patch_db, 0 ); + if (!si) + return ERROR_FUNCTION_FAILED; + + msi_check_patch_applicable( package, si ); + + /* enumerate the substorage */ + str = msi_suminfo_dup_string( si, PID_LASTAUTHOR ); + substorage = msi_split_string( str, ';' ); + for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ ) + r = msi_apply_substorage_transform( package, patch_db, substorage[i] ); + msi_free( substorage ); + msi_free( str ); + + /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */ + + msiobj_release( &si->hdr ); + + return r; +} + +static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file ) +{ + MSIDATABASE *patch_db = NULL; + UINT r; + + TRACE("%p %s\n", package, debugstr_w( file ) ); + + /* FIXME: + * We probably want to make sure we only open a patch collection here. + * Patch collections (.msp) and databases (.msi) have different GUIDs + * but currently MSI_OpenDatabaseW will accept both. + */ + r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db ); + if ( r != ERROR_SUCCESS ) + { + ERR("failed to open patch collection %s\n", debugstr_w( file ) ); + return r; + } + + msi_parse_patch_summary( package, patch_db ); + msiobj_release( &patch_db->hdr ); + + return ERROR_SUCCESS; +} + +/* get the PATCH property, and apply all the patches it specifies */ +static UINT msi_apply_patches( MSIPACKAGE *package ) +{ + static const WCHAR szPatch[] = { 'P','A','T','C','H',0 }; + LPWSTR patch_list, *patches; + UINT i, r = ERROR_SUCCESS; + + patch_list = msi_dup_property( package, szPatch ); + + TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) ); + + patches = msi_split_string( patch_list, ';' ); + for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ ) + r = msi_apply_patch_package( package, patches[i] ); + + msi_free( patches ); + msi_free( patch_list ); + + return r; +} + +static UINT msi_apply_transforms( MSIPACKAGE *package ) +{ + static const WCHAR szTransforms[] = { + 'T','R','A','N','S','F','O','R','M','S',0 }; + LPWSTR xform_list, *xforms; + UINT i, r = ERROR_SUCCESS; + + xform_list = msi_dup_property( package, szTransforms ); + xforms = msi_split_string( xform_list, ';' ); + + for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ ) + { + if (xforms[i][0] == ':') + r = msi_apply_substorage_transform( package, package->db, &xforms[i][1] ); + else + r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 ); + } + + msi_free( xforms ); + msi_free( xform_list ); + + return r; +} + +/**************************************************** + * TOP level entry points + *****************************************************/ + +UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath, + LPCWSTR szCommandLine ) +{ + UINT rc; + BOOL ui = FALSE; + static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0}; + static const WCHAR szAction[] = {'A','C','T','I','O','N',0}; + static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0}; + + MSI_SetPropertyW(package, szAction, szInstall); + + package->script = msi_alloc(sizeof(MSISCRIPT)); + memset(package->script,0,sizeof(MSISCRIPT)); + + package->script->InWhatSequence = SEQUENCE_INSTALL; + + if (szPackagePath) + { + LPWSTR p, check, path; + + package->PackagePath = strdupW(szPackagePath); + path = strdupW(szPackagePath); + p = strrchrW(path,'\\'); + if (p) + { + p++; + *p=0; + } + else + { + msi_free(path); + path = msi_alloc(MAX_PATH*sizeof(WCHAR)); + GetCurrentDirectoryW(MAX_PATH,path); + strcatW(path,cszbs); + } + + check = msi_dup_property( package, cszSourceDir ); + if (!check) + MSI_SetPropertyW(package, cszSourceDir, path); + msi_free(check); + msi_free(path); + } + + msi_parse_command_line( package, szCommandLine ); + + msi_apply_transforms( package ); + msi_apply_patches( package ); + + if ( msi_get_property_int(package, szUILevel, 0) >= INSTALLUILEVEL_REDUCED ) + { + package->script->InWhatSequence |= SEQUENCE_UI; + rc = ACTION_ProcessUISequence(package); + ui = TRUE; + if (rc == ERROR_SUCCESS) + { + package->script->InWhatSequence |= SEQUENCE_EXEC; + rc = ACTION_ProcessExecSequence(package,TRUE); + } + } + else + rc = ACTION_ProcessExecSequence(package,FALSE); + + if (rc == -1) + { + /* install was halted but should be considered a success */ + rc = ERROR_SUCCESS; + } + + package->script->CurrentlyScripting= FALSE; + + /* process the ending type action */ + if (rc == ERROR_SUCCESS) + ACTION_PerformActionSequence(package,-1,ui); + else if (rc == ERROR_INSTALL_USEREXIT) + ACTION_PerformActionSequence(package,-2,ui); + else if (rc == ERROR_INSTALL_SUSPEND) + ACTION_PerformActionSequence(package,-4,ui); + else /* failed */ + ACTION_PerformActionSequence(package,-3,ui); + + /* finish up running custom actions */ + ACTION_FinishCustomActions(package); + + return rc; +} + +static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI) +{ + UINT rc = ERROR_SUCCESS; + MSIRECORD * row = 0; + static const WCHAR ExecSeqQuery[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e', + 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ', + '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0}; + + static const WCHAR UISeqQuery[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e', + '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`', + ' ', '=',' ','%','i',0}; + + if (UI) + row = MSI_QueryGetRecord(package->db, UISeqQuery, seq); + else + row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq); + + if (row) + { + LPCWSTR action, cond; + + TRACE("Running the actions\n"); + + /* check conditions */ + cond = MSI_RecordGetString(row,2); + if (cond) + { + /* this is a hack to skip errors in the condition code */ + if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE) + goto end; + } + + action = MSI_RecordGetString(row,1); + if (!action) + { + ERR("failed to fetch action\n"); + rc = ERROR_FUNCTION_FAILED; + goto end; + } + + if (UI) + rc = ACTION_PerformUIAction(package,action); + else + rc = ACTION_PerformAction(package,action,FALSE); +end: + msiobj_release(&row->hdr); + } + else + rc = ERROR_SUCCESS; + + return rc; +} + +typedef struct { + MSIPACKAGE* package; + BOOL UI; +} iterate_action_param; + +static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param) +{ + iterate_action_param *iap= (iterate_action_param*)param; + UINT rc; + LPCWSTR cond, action; + + action = MSI_RecordGetString(row,1); + if (!action) + { + ERR("Error is retrieving action name\n"); + return ERROR_FUNCTION_FAILED; + } + + /* check conditions */ + cond = MSI_RecordGetString(row,2); + if (cond) + { + /* this is a hack to skip errors in the condition code */ + if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE) + { + TRACE("Skipping action: %s (condition is false)\n", + debugstr_w(action)); + return ERROR_SUCCESS; + } + } + + if (iap->UI) + rc = ACTION_PerformUIAction(iap->package,action); + else + rc = ACTION_PerformAction(iap->package,action,FALSE); + + msi_dialog_check_messages( NULL ); + + if (iap->package->CurrentInstallState != ERROR_SUCCESS ) + rc = iap->package->CurrentInstallState; + + if (rc == ERROR_FUNCTION_NOT_CALLED) + rc = ERROR_SUCCESS; + + if (rc != ERROR_SUCCESS) + ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc); + + return rc; +} + +UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode ) +{ + MSIQUERY * view; + UINT r; + static const WCHAR query[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','%','s','`', + ' ','W','H','E','R','E',' ', + '`','S','e','q','u','e','n','c','e','`',' ', + '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ', + '`','S','e','q','u','e','n','c','e','`',0}; + iterate_action_param iap; + + /* + * FIXME: probably should be checking UILevel in the + * ACTION_PerformUIAction/ACTION_PerformAction + * rather than saving the UI level here. Those + * two functions can be merged too. + */ + iap.package = package; + iap.UI = TRUE; + + TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode ); + + r = MSI_OpenQuery( package->db, &view, query, szTable ); + if (r == ERROR_SUCCESS) + { + r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap ); + msiobj_release(&view->hdr); + } + + return r; +} + +static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran) +{ + MSIQUERY * view; + UINT rc; + static const WCHAR ExecSeqQuery[] = + {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ', + '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e', + 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ', + '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ', + 'O','R','D','E','R',' ', 'B','Y',' ', + '`','S','e','q','u','e','n','c','e','`',0 }; + MSIRECORD * row = 0; + static const WCHAR IVQuery[] = + {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`', + ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l', + 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ', + 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=', + ' ','\'', 'I','n','s','t','a','l','l', + 'V','a','l','i','d','a','t','e','\'', 0}; + INT seq = 0; + iterate_action_param iap; + + iap.package = package; + iap.UI = FALSE; + + if (package->script->ExecuteSequenceRun) + { + TRACE("Execute Sequence already Run\n"); + return ERROR_SUCCESS; + } + + package->script->ExecuteSequenceRun = TRUE; + + /* get the sequence number */ + if (UIran) + { + row = MSI_QueryGetRecord(package->db, IVQuery); + if( !row ) + return ERROR_FUNCTION_FAILED; + seq = MSI_RecordGetInteger(row,1); + msiobj_release(&row->hdr); + } + + rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq); + if (rc == ERROR_SUCCESS) + { + TRACE("Running the actions\n"); + + rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap); + msiobj_release(&view->hdr); + } + + return rc; +} + +static UINT ACTION_ProcessUISequence(MSIPACKAGE *package) +{ + MSIQUERY * view; + UINT rc; + static const WCHAR ExecSeqQuery [] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','I','n','s','t','a','l','l', + 'U','I','S','e','q','u','e','n','c','e','`', + ' ','W','H','E','R','E',' ', + '`','S','e','q','u','e','n','c','e','`',' ', + '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ', + '`','S','e','q','u','e','n','c','e','`',0}; + iterate_action_param iap; + + iap.package = package; + iap.UI = TRUE; + + rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view); + + if (rc == ERROR_SUCCESS) + { + TRACE("Running the actions\n"); + + rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap); + msiobj_release(&view->hdr); + } + + return rc; +} + +/******************************************************** + * ACTION helper functions and functions that perform the actions + *******************************************************/ +static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action, + UINT* rc, BOOL force ) +{ + BOOL ret = FALSE; + BOOL run = force; + int i; + + if (!package) + { + ERR("package was null!\n"); + return FALSE; + } + + if (!run && !package->script->CurrentlyScripting) + run = TRUE; + + if (!run) + { + if (strcmpW(action,szInstallFinalize) == 0 || + strcmpW(action,szInstallExecute) == 0 || + strcmpW(action,szInstallExecuteAgain) == 0) + run = TRUE; + } + + i = 0; + while (StandardActions[i].action != NULL) + { + if (strcmpW(StandardActions[i].action, action)==0) + { + if (!run) + { + ui_actioninfo(package, action, TRUE, 0); + *rc = schedule_action(package,INSTALL_SCRIPT,action); + ui_actioninfo(package, action, FALSE, *rc); + } + else + { + ui_actionstart(package, action); + if (StandardActions[i].handler) + { + *rc = StandardActions[i].handler(package); + } + else + { + FIXME("unhandled standard action %s\n",debugstr_w(action)); + *rc = ERROR_SUCCESS; + } + } + ret = TRUE; + break; + } + i++; + } + return ret; +} + +static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action, + UINT* rc, BOOL force ) +{ + BOOL ret=FALSE; + UINT arc; + + arc = ACTION_CustomAction(package,action, force); + + if (arc != ERROR_CALL_NOT_IMPLEMENTED) + { + *rc = arc; + ret = TRUE; + } + return ret; +} + +/* + * A lot of actions are really important even if they don't do anything + * explicit... Lots of properties are set at the beginning of the installation + * CostFinalize does a bunch of work to translate the directories and such + * + * But until I get write access to the database that is hard, so I am going to + * hack it to see if I can get something to run. + */ +UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, BOOL force) +{ + UINT rc = ERROR_SUCCESS; + BOOL handled; + + TRACE("Performing action (%s)\n",debugstr_w(action)); + + handled = ACTION_HandleStandardAction(package, action, &rc, force); + + if (!handled) + handled = ACTION_HandleCustomAction(package, action, &rc, force); + + if (!handled) + { + FIXME("unhandled msi action %s\n",debugstr_w(action)); + rc = ERROR_FUNCTION_NOT_CALLED; + } + + return rc; +} + +UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action) +{ + UINT rc = ERROR_SUCCESS; + BOOL handled = FALSE; + + TRACE("Performing action (%s)\n",debugstr_w(action)); + + handled = ACTION_HandleStandardAction(package, action, &rc,TRUE); + + if (!handled) + handled = ACTION_HandleCustomAction(package, action, &rc, FALSE); + + if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS ) + handled = TRUE; + + if (!handled) + { + FIXME("unhandled msi action %s\n",debugstr_w(action)); + rc = ERROR_FUNCTION_NOT_CALLED; + } + + return rc; +} + + +/* + * Actual Action Handlers + */ + +static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE *package = (MSIPACKAGE*)param; + LPCWSTR dir; + LPWSTR full_path; + MSIRECORD *uirow; + MSIFOLDER *folder; + + dir = MSI_RecordGetString(row,1); + if (!dir) + { + ERR("Unable to get folder id\n"); + return ERROR_SUCCESS; + } + + full_path = resolve_folder(package,dir,FALSE,FALSE,&folder); + if (!full_path) + { + ERR("Unable to resolve folder id %s\n",debugstr_w(dir)); + return ERROR_SUCCESS; + } + + TRACE("Folder is %s\n",debugstr_w(full_path)); + + /* UI stuff */ + uirow = MSI_CreateRecord(1); + MSI_RecordSetStringW(uirow,1,full_path); + ui_actiondata(package,szCreateFolders,uirow); + msiobj_release( &uirow->hdr ); + + if (folder->State == 0) + create_full_pathW(full_path); + + folder->State = 3; + + msi_free(full_path); + return ERROR_SUCCESS; +} + +/* FIXME: probably should merge this with the above function */ +static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir ) +{ + UINT rc = ERROR_SUCCESS; + MSIFOLDER *folder; + LPWSTR install_path; + + install_path = resolve_folder(package, dir, FALSE, FALSE, &folder); + if (!install_path) + return ERROR_FUNCTION_FAILED; + + /* create the path */ + if (folder->State == 0) + { + create_full_pathW(install_path); + folder->State = 2; + } + msi_free(install_path); + + return rc; +} + +UINT msi_create_component_directories( MSIPACKAGE *package ) +{ + MSICOMPONENT *comp; + + /* create all the folders required by the components are going to install */ + LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry ) + { + if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL)) + continue; + msi_create_directory( package, comp->Directory ); + } + + return ERROR_SUCCESS; +} + +/* + * Also we cannot enable/disable components either, so for now I am just going + * to do all the directories for all the components. + */ +static UINT ACTION_CreateFolders(MSIPACKAGE *package) +{ + static const WCHAR ExecSeqQuery[] = + {'S','E','L','E','C','T',' ', + '`','D','i','r','e','c','t','o','r','y','_','`', + ' ','F','R','O','M',' ', + '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 }; + UINT rc; + MSIQUERY *view; + + /* create all the empty folders specified in the CreateFolder table */ + rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view ); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package); + msiobj_release(&view->hdr); + + msi_create_component_directories( package ); + + return rc; +} + +static MSICOMPONENT* load_component( MSIRECORD * row ) +{ + MSICOMPONENT *comp; + + comp = msi_alloc_zero( sizeof(MSICOMPONENT) ); + if (!comp) + return comp; + + /* fill in the data */ + comp->Component = msi_dup_record_field( row, 1 ); + + TRACE("Loading Component %s\n", debugstr_w(comp->Component)); + + comp->ComponentId = msi_dup_record_field( row, 2 ); + comp->Directory = msi_dup_record_field( row, 3 ); + comp->Attributes = MSI_RecordGetInteger(row,4); + comp->Condition = msi_dup_record_field( row, 5 ); + comp->KeyPath = msi_dup_record_field( row, 6 ); + + comp->Installed = INSTALLSTATE_ABSENT; + comp->Action = INSTALLSTATE_UNKNOWN; + comp->ActionRequest = INSTALLSTATE_UNKNOWN; + + comp->Enabled = TRUE; + + return comp; +} + +typedef struct { + MSIPACKAGE *package; + MSIFEATURE *feature; +} _ilfs; + +static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp ) +{ + ComponentList *cl; + + cl = msi_alloc( sizeof (*cl) ); + if ( !cl ) + return ERROR_NOT_ENOUGH_MEMORY; + cl->component = comp; + list_add_tail( &feature->Components, &cl->entry ); + + return ERROR_SUCCESS; +} + +static UINT iterate_component_check( MSIRECORD *row, LPVOID param ) +{ + _ilfs* ilfs= (_ilfs*)param; + MSIPACKAGE *package = ilfs->package; + MSIFEATURE *feature = ilfs->feature; + MSICOMPONENT *comp; + + comp = load_component( row ); + if (!comp) + return ERROR_FUNCTION_FAILED; + + list_add_tail( &package->components, &comp->entry ); + add_feature_component( feature, comp ); + + TRACE("Loaded new component %p\n", comp); + + return ERROR_SUCCESS; +} + +static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param) +{ + _ilfs* ilfs= (_ilfs*)param; + LPCWSTR component; + DWORD rc; + MSICOMPONENT *comp; + MSIQUERY * view; + static const WCHAR Query[] = + {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ', + '`','C','o','m','p','o','n','e','n','t','`',' ', + 'W','H','E','R','E',' ', + '`','C','o','m','p','o','n','e','n','t','`',' ', + '=','\'','%','s','\'',0}; + + component = MSI_RecordGetString(row,1); + + /* check to see if the component is already loaded */ + comp = get_loaded_component( ilfs->package, component ); + if (comp) + { + TRACE("Component %s already loaded\n", debugstr_w(component) ); + add_feature_component( ilfs->feature, comp ); + return ERROR_SUCCESS; + } + + rc = MSI_OpenQuery(ilfs->package->db, &view, Query, component); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords(view, NULL, iterate_component_check, ilfs); + msiobj_release( &view->hdr ); + + return ERROR_SUCCESS; +} + +static UINT load_feature(MSIRECORD * row, LPVOID param) +{ + MSIPACKAGE* package = (MSIPACKAGE*)param; + MSIFEATURE* feature; + static const WCHAR Query1[] = + {'S','E','L','E','C','T',' ', + '`','C','o','m','p','o','n','e','n','t','_','`', + ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e', + 'C','o','m','p','o','n','e','n','t','s','`',' ', + 'W','H','E','R','E',' ', + '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0}; + MSIQUERY * view; + UINT rc; + _ilfs ilfs; + + /* fill in the data */ + + feature = msi_alloc_zero( sizeof (MSIFEATURE) ); + if (!feature) + return ERROR_NOT_ENOUGH_MEMORY; + + list_init( &feature->Components ); + + feature->Feature = msi_dup_record_field( row, 1 ); + + TRACE("Loading feature %s\n",debugstr_w(feature->Feature)); + + feature->Feature_Parent = msi_dup_record_field( row, 2 ); + feature->Title = msi_dup_record_field( row, 3 ); + feature->Description = msi_dup_record_field( row, 4 ); + + if (!MSI_RecordIsNull(row,5)) + feature->Display = MSI_RecordGetInteger(row,5); + + feature->Level= MSI_RecordGetInteger(row,6); + feature->Directory = msi_dup_record_field( row, 7 ); + feature->Attributes = MSI_RecordGetInteger(row,8); + + feature->Installed = INSTALLSTATE_ABSENT; + feature->Action = INSTALLSTATE_UNKNOWN; + feature->ActionRequest = INSTALLSTATE_UNKNOWN; + + list_add_tail( &package->features, &feature->entry ); + + /* load feature components */ + + rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature ); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + ilfs.package = package; + ilfs.feature = feature; + + MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs); + msiobj_release(&view->hdr); + + return ERROR_SUCCESS; +} + +static UINT load_file(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE* package = (MSIPACKAGE*)param; + LPCWSTR component; + MSIFILE *file; + + /* fill in the data */ + + file = msi_alloc_zero( sizeof (MSIFILE) ); + if (!file) + return ERROR_NOT_ENOUGH_MEMORY; + + file->File = msi_dup_record_field( row, 1 ); + + component = MSI_RecordGetString( row, 2 ); + file->Component = get_loaded_component( package, component ); + + if (!file->Component) + ERR("Unfound Component %s\n",debugstr_w(component)); + + file->FileName = msi_dup_record_field( row, 3 ); + reduce_to_longfilename( file->FileName ); + + file->ShortName = msi_dup_record_field( row, 3 ); + reduce_to_shortfilename( file->ShortName ); + + file->FileSize = MSI_RecordGetInteger( row, 4 ); + file->Version = msi_dup_record_field( row, 5 ); + file->Language = msi_dup_record_field( row, 6 ); + file->Attributes = MSI_RecordGetInteger( row, 7 ); + file->Sequence = MSI_RecordGetInteger( row, 8 ); + + file->state = msifs_invalid; + + TRACE("File Loaded (%s)\n",debugstr_w(file->File)); + + list_add_tail( &package->files, &file->entry ); + + return ERROR_SUCCESS; +} + +static UINT load_all_files(MSIPACKAGE *package) +{ + MSIQUERY * view; + UINT rc; + static const WCHAR Query[] = + {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ', + '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ', + '`','S','e','q','u','e','n','c','e','`', 0}; + + if (!package) + return ERROR_INVALID_HANDLE; + + rc = MSI_DatabaseOpenViewW(package->db, Query, &view); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords(view, NULL, load_file, package); + msiobj_release(&view->hdr); + + return ERROR_SUCCESS; +} + + +/* + * I am not doing any of the costing functionality yet. + * Mostly looking at doing the Component and Feature loading + * + * The native MSI does A LOT of modification to tables here. Mostly adding + * a lot of temporary columns to the Feature and Component tables. + * + * note: Native msi also tracks the short filename. But I am only going to + * track the long ones. Also looking at this directory table + * it appears that the directory table does not get the parents + * resolved base on property only based on their entries in the + * directory table. + */ +static UINT ACTION_CostInitialize(MSIPACKAGE *package) +{ + MSIQUERY * view; + UINT rc; + static const WCHAR Query_all[] = + {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ', + '`','F','e','a','t','u','r','e','`',0}; + static const WCHAR szCosting[] = + {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 }; + static const WCHAR szZero[] = { '0', 0 }; + + if ( 1 == msi_get_property_int( package, szCosting, 0 ) ) + return ERROR_SUCCESS; + + MSI_SetPropertyW(package, szCosting, szZero); + MSI_SetPropertyW(package, cszRootDrive , c_colon); + + rc = MSI_DatabaseOpenViewW(package->db,Query_all,&view); + if (rc != ERROR_SUCCESS) + return rc; + + rc = MSI_IterateRecords(view, NULL, load_feature, package); + msiobj_release(&view->hdr); + + load_all_files(package); + + return ERROR_SUCCESS; +} + +static UINT execute_script(MSIPACKAGE *package, UINT script ) +{ + int i; + UINT rc = ERROR_SUCCESS; + + TRACE("Executing Script %i\n",script); + + for (i = 0; i < package->script->ActionCount[script]; i++) + { + LPWSTR action; + action = package->script->Actions[script][i]; + ui_actionstart(package, action); + TRACE("Executing Action (%s)\n",debugstr_w(action)); + rc = ACTION_PerformAction(package, action, TRUE); + msi_free(package->script->Actions[script][i]); + if (rc != ERROR_SUCCESS) + break; + } + msi_free(package->script->Actions[script]); + + package->script->ActionCount[script] = 0; + package->script->Actions[script] = NULL; + return rc; +} + +static UINT ACTION_FileCost(MSIPACKAGE *package) +{ + return ERROR_SUCCESS; +} + + +static MSIFOLDER *load_folder( MSIPACKAGE *package, LPCWSTR dir ) +{ + static const WCHAR Query[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','D','i','r','e','c', 't','o','r','y','`',' ', + 'W','H','E','R','E',' ', '`', 'D','i','r','e','c','t', 'o','r','y','`', + ' ','=',' ','\'','%','s','\'', + 0}; + LPWSTR ptargetdir, targetdir, srcdir; + LPCWSTR parent; + LPWSTR shortname = NULL; + MSIRECORD * row = 0; + MSIFOLDER *folder; + + TRACE("Looking for dir %s\n",debugstr_w(dir)); + + folder = get_loaded_folder( package, dir ); + if (folder) + return folder; + + TRACE("Working to load %s\n",debugstr_w(dir)); + + folder = msi_alloc_zero( sizeof (MSIFOLDER) ); + if (!folder) + return NULL; + + folder->Directory = strdupW(dir); + + row = MSI_QueryGetRecord(package->db, Query, dir); + if (!row) + return NULL; + + ptargetdir = targetdir = msi_dup_record_field(row,3); + + /* split src and target dir */ + if (strchrW(targetdir,':')) + { + srcdir=strchrW(targetdir,':'); + *srcdir=0; + srcdir ++; + } + else + srcdir=NULL; + + /* for now only pick long filename versions */ + if (strchrW(targetdir,'|')) + { + shortname = targetdir; + targetdir = strchrW(targetdir,'|'); + *targetdir = 0; + targetdir ++; + } + /* for the sourcedir pick the short filename */ + if (srcdir && strchrW(srcdir,'|')) + { + LPWSTR p = strchrW(srcdir,'|'); + *p = 0; + } + + /* now check for root dirs */ + if (targetdir[0] == '.' && targetdir[1] == 0) + targetdir = NULL; + + if (targetdir) + { + TRACE(" TargetDefault = %s\n",debugstr_w(targetdir)); + msi_free( folder->TargetDefault); + folder->TargetDefault = strdupW(targetdir); + } + + if (srcdir) + folder->SourceDefault = strdupW(srcdir); + else if (shortname) + folder->SourceDefault = strdupW(shortname); + else if (targetdir) + folder->SourceDefault = strdupW(targetdir); + msi_free(ptargetdir); + TRACE(" SourceDefault = %s\n", debugstr_w( folder->SourceDefault )); + + parent = MSI_RecordGetString(row,2); + if (parent) + { + folder->Parent = load_folder( package, parent ); + if ( folder->Parent ) + TRACE("loaded parent %p %s\n", folder->Parent, + debugstr_w(folder->Parent->Directory)); + else + ERR("failed to load parent folder %s\n", debugstr_w(parent)); + } + + folder->Property = msi_dup_property( package, dir ); + + msiobj_release(&row->hdr); + + list_add_tail( &package->folders, &folder->entry ); + + TRACE("%s returning %p\n",debugstr_w(dir),folder); + + return folder; +} + +/* scan for and update current install states */ +static void ACTION_UpdateInstallStates(MSIPACKAGE *package) +{ + MSICOMPONENT *comp; + MSIFEATURE *feature; + + LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry ) + { + INSTALLSTATE res; + res = MsiGetComponentPathW( package->ProductCode, + comp->ComponentId, NULL, NULL); + if (res < 0) + res = INSTALLSTATE_ABSENT; + comp->Installed = res; + } + + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + ComponentList *cl; + INSTALLSTATE res = -10; + + LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry ) + { + comp= cl->component; + + if (res == -10) + res = comp->Installed; + else + { + if (res == comp->Installed) + continue; + + if (res != comp->Installed) + res = INSTALLSTATE_INCOMPLETE; + } + } + feature->Installed = res; + } +} + +static BOOL process_state_property (MSIPACKAGE* package, LPCWSTR property, + INSTALLSTATE state) +{ + static const WCHAR all[]={'A','L','L',0}; + LPWSTR override; + MSIFEATURE *feature; + + override = msi_dup_property( package, property ); + if (!override) + return FALSE; + + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + if (strcmpiW(override,all)==0) + { + feature->ActionRequest= state; + feature->Action = state; + } + else + { + LPWSTR ptr = override; + LPWSTR ptr2 = strchrW(override,','); + + while (ptr) + { + if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0) + || (!ptr2 && strcmpW(ptr,feature->Feature)==0)) + { + feature->ActionRequest= state; + feature->Action = state; + break; + } + if (ptr2) + { + ptr=ptr2+1; + ptr2 = strchrW(ptr,','); + } + else + break; + } + } + } + msi_free(override); + + return TRUE; +} + +static UINT SetFeatureStates(MSIPACKAGE *package) +{ + int install_level; + static const WCHAR szlevel[] = + {'I','N','S','T','A','L','L','L','E','V','E','L',0}; + static const WCHAR szAddLocal[] = + {'A','D','D','L','O','C','A','L',0}; + static const WCHAR szRemove[] = + {'R','E','M','O','V','E',0}; + static const WCHAR szReinstall[] = + {'R','E','I','N','S','T','A','L','L',0}; + BOOL override = FALSE; + MSICOMPONENT* component; + MSIFEATURE *feature; + + + /* I do not know if this is where it should happen.. but */ + + TRACE("Checking Install Level\n"); + + install_level = msi_get_property_int( package, szlevel, 1 ); + + /* ok hereis the _real_ rub + * all these activation/deactivation things happen in order and things + * later on the list override things earlier on the list. + * 1) INSTALLLEVEL processing + * 2) ADDLOCAL + * 3) REMOVE + * 4) ADDSOURCE + * 5) ADDDEFAULT + * 6) REINSTALL + * 7) COMPADDLOCAL + * 8) COMPADDSOURCE + * 9) FILEADDLOCAL + * 10) FILEADDSOURCE + * 11) FILEADDDEFAULT + * I have confirmed that if ADDLOCAL is stated then the INSTALLLEVEL is + * ignored for all the features. seems strange, especially since it is not + * documented anywhere, but it is how it works. + * + * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and + * REMOVE are the big ones, since we don't handle administrative installs + * yet anyway. + */ + override |= process_state_property(package,szAddLocal,INSTALLSTATE_LOCAL); + override |= process_state_property(package,szRemove,INSTALLSTATE_ABSENT); + override |= process_state_property(package,szReinstall,INSTALLSTATE_LOCAL); + + if (!override) + { + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + BOOL feature_state = ((feature->Level > 0) && + (feature->Level <= install_level)); + + if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN)) + { + if (feature->Attributes & msidbFeatureAttributesFavorSource) + { + feature->ActionRequest = INSTALLSTATE_SOURCE; + feature->Action = INSTALLSTATE_SOURCE; + } + else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise) + { + feature->ActionRequest = INSTALLSTATE_ADVERTISED; + feature->Action = INSTALLSTATE_ADVERTISED; + } + else + { + feature->ActionRequest = INSTALLSTATE_LOCAL; + feature->Action = INSTALLSTATE_LOCAL; + } + } + } + } + else + { + /* set the Preselected Property */ + static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0}; + static const WCHAR szOne[] = { '1', 0 }; + + MSI_SetPropertyW(package,szPreselected,szOne); + } + + /* + * now we want to enable or disable components base on feature + */ + + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + ComponentList *cl; + + TRACE("Examining Feature %s (Installed %i, Action %i, Request %i)\n", + debugstr_w(feature->Feature), feature->Installed, feature->Action, + feature->ActionRequest); + + LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry ) + { + component = cl->component; + + if (!component->Enabled) + { + component->Action = INSTALLSTATE_UNKNOWN; + component->ActionRequest = INSTALLSTATE_UNKNOWN; + } + else + { + if (feature->Action == INSTALLSTATE_LOCAL) + { + component->Action = INSTALLSTATE_LOCAL; + component->ActionRequest = INSTALLSTATE_LOCAL; + } + else if (feature->ActionRequest == INSTALLSTATE_SOURCE) + { + if ((component->Action == INSTALLSTATE_UNKNOWN) || + (component->Action == INSTALLSTATE_ABSENT) || + (component->Action == INSTALLSTATE_ADVERTISED)) + + { + component->Action = INSTALLSTATE_SOURCE; + component->ActionRequest = INSTALLSTATE_SOURCE; + } + } + else if (feature->ActionRequest == INSTALLSTATE_ADVERTISED) + { + if ((component->Action == INSTALLSTATE_UNKNOWN) || + (component->Action == INSTALLSTATE_ABSENT)) + + { + component->Action = INSTALLSTATE_ADVERTISED; + component->ActionRequest = INSTALLSTATE_ADVERTISED; + } + } + else if (feature->ActionRequest == INSTALLSTATE_ABSENT) + { + if (component->Action == INSTALLSTATE_UNKNOWN) + { + component->Action = INSTALLSTATE_ABSENT; + component->ActionRequest = INSTALLSTATE_ABSENT; + } + } + } + } + } + + LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry ) + { + TRACE("Result: Component %s (Installed %i, Action %i, Request %i)\n", + debugstr_w(component->Component), component->Installed, + component->Action, component->ActionRequest); + } + + + return ERROR_SUCCESS; +} + +static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE *package = (MSIPACKAGE*)param; + LPCWSTR name; + LPWSTR path; + + name = MSI_RecordGetString(row,1); + + /* This helper function now does ALL the work */ + TRACE("Dir %s ...\n",debugstr_w(name)); + load_folder(package,name); + path = resolve_folder(package,name,FALSE,TRUE,NULL); + TRACE("resolves to %s\n",debugstr_w(path)); + msi_free(path); + + return ERROR_SUCCESS; +} + +static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE *package = (MSIPACKAGE*)param; + LPCWSTR name; + MSIFEATURE *feature; + + name = MSI_RecordGetString( row, 1 ); + + feature = get_loaded_feature( package, name ); + if (!feature) + ERR("FAILED to find loaded feature %s\n",debugstr_w(name)); + else + { + LPCWSTR Condition; + Condition = MSI_RecordGetString(row,3); + + if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE) + { + int level = MSI_RecordGetInteger(row,2); + TRACE("Reseting feature %s to level %i\n", debugstr_w(name), level); + feature->Level = level; + } + } + return ERROR_SUCCESS; +} + + +/* + * A lot is done in this function aside from just the costing. + * The costing needs to be implemented at some point but for now I am going + * to focus on the directory building + * + */ +static UINT ACTION_CostFinalize(MSIPACKAGE *package) +{ + static const WCHAR ExecSeqQuery[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','D','i','r','e','c','t','o','r','y','`',0}; + static const WCHAR ConditionQuery[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','C','o','n','d','i','t','i','o','n','`',0}; + static const WCHAR szCosting[] = + {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 }; + static const WCHAR szlevel[] = + {'I','N','S','T','A','L','L','L','E','V','E','L',0}; + static const WCHAR szOne[] = { '1', 0 }; + MSICOMPONENT *comp; + MSIFILE *file; + UINT rc; + MSIQUERY * view; + LPWSTR level; + + if ( 1 == msi_get_property_int( package, szCosting, 0 ) ) + return ERROR_SUCCESS; + + TRACE("Building Directory properties\n"); + + rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view); + if (rc == ERROR_SUCCESS) + { + rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories, + package); + msiobj_release(&view->hdr); + } + + TRACE("File calculations\n"); + + LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry ) + { + MSICOMPONENT* comp = file->Component; + LPWSTR p; + + if (!comp) + continue; + + /* calculate target */ + p = resolve_folder(package, comp->Directory, FALSE, FALSE, NULL); + + msi_free(file->TargetPath); + + TRACE("file %s is named %s\n", + debugstr_w(file->File),debugstr_w(file->FileName)); + + file->TargetPath = build_directory_name(2, p, file->FileName); + + msi_free(p); + + TRACE("file %s resolves to %s\n", + debugstr_w(file->File),debugstr_w(file->TargetPath)); + + if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES) + { + file->state = msifs_missing; + comp->Cost += file->FileSize; + continue; + } + + if (file->Version) + { + DWORD handle; + DWORD versize; + UINT sz; + LPVOID version; + static WCHAR name[] = {'\\',0}; + static const WCHAR name_fmt[] = + {'%','u','.','%','u','.','%','u','.','%','u',0}; + WCHAR filever[0x100]; + VS_FIXEDFILEINFO *lpVer; + + TRACE("Version comparison..\n"); + versize = GetFileVersionInfoSizeW(file->TargetPath,&handle); + version = msi_alloc(versize); + GetFileVersionInfoW(file->TargetPath, 0, versize, version); + + VerQueryValueW(version, (LPWSTR)name, (LPVOID*)&lpVer, &sz); + + sprintfW(filever,name_fmt, + HIWORD(lpVer->dwFileVersionMS), + LOWORD(lpVer->dwFileVersionMS), + HIWORD(lpVer->dwFileVersionLS), + LOWORD(lpVer->dwFileVersionLS)); + + TRACE("new %s old %s\n", debugstr_w(file->Version), + debugstr_w(filever)); + if (strcmpiW(filever,file->Version)<0) + { + file->state = msifs_overwrite; + /* FIXME: cost should be diff in size */ + comp->Cost += file->FileSize; + } + else + file->state = msifs_present; + msi_free(version); + } + else + file->state = msifs_present; + } + + TRACE("Evaluating Condition Table\n"); + + rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view); + if (rc == ERROR_SUCCESS) + { + rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions, + package); + msiobj_release(&view->hdr); + } + + TRACE("Enabling or Disabling Components\n"); + LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry ) + { + if (comp->Condition) + { + if (MSI_EvaluateConditionW(package, + comp->Condition) == MSICONDITION_FALSE) + { + TRACE("Disabling component %s\n", debugstr_w(comp->Component)); + comp->Enabled = FALSE; + } + } + } + + MSI_SetPropertyW(package,szCosting,szOne); + /* set default run level if not set */ + level = msi_dup_property( package, szlevel ); + if (!level) + MSI_SetPropertyW(package,szlevel, szOne); + msi_free(level); + + ACTION_UpdateInstallStates(package); + + return SetFeatureStates(package); +} + +/* OK this value is "interpreted" and then formatted based on the + first few characters */ +static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type, + DWORD *size) +{ + LPSTR data = NULL; + if (value[0]=='#' && value[1]!='#' && value[1]!='%') + { + if (value[1]=='x') + { + LPWSTR ptr; + CHAR byte[5]; + LPWSTR deformated = NULL; + int count; + + deformat_string(package, &value[2], &deformated); + + /* binary value type */ + ptr = deformated; + *type = REG_BINARY; + if (strlenW(ptr)%2) + *size = (strlenW(ptr)/2)+1; + else + *size = strlenW(ptr)/2; + + data = msi_alloc(*size); + + byte[0] = '0'; + byte[1] = 'x'; + byte[4] = 0; + count = 0; + /* if uneven pad with a zero in front */ + if (strlenW(ptr)%2) + { + byte[2]= '0'; + byte[3]= *ptr; + ptr++; + data[count] = (BYTE)strtol(byte,NULL,0); + count ++; + TRACE("Uneven byte count\n"); + } + while (*ptr) + { + byte[2]= *ptr; + ptr++; + byte[3]= *ptr; + ptr++; + data[count] = (BYTE)strtol(byte,NULL,0); + count ++; + } + msi_free(deformated); + + TRACE("Data %li bytes(%i)\n",*size,count); + } + else + { + LPWSTR deformated; + LPWSTR p; + DWORD d = 0; + deformat_string(package, &value[1], &deformated); + + *type=REG_DWORD; + *size = sizeof(DWORD); + data = msi_alloc(*size); + p = deformated; + if (*p == '-') + p++; + while (*p) + { + if ( (*p < '0') || (*p > '9') ) + break; + d *= 10; + d += (*p - '0'); + p++; + } + if (deformated[0] == '-') + d = -d; + *(LPDWORD)data = d; + TRACE("DWORD %li\n",*(LPDWORD)data); + + msi_free(deformated); + } + } + else + { + static const WCHAR szMulti[] = {'[','~',']',0}; + LPCWSTR ptr; + *type=REG_SZ; + + if (value[0]=='#') + { + if (value[1]=='%') + { + ptr = &value[2]; + *type=REG_EXPAND_SZ; + } + else + ptr = &value[1]; + } + else + ptr=value; + + if (strstrW(value,szMulti)) + *type = REG_MULTI_SZ; + + *size = deformat_string(package, ptr,(LPWSTR*)&data); + } + return data; +} + +static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE *package = (MSIPACKAGE*)param; + static const WCHAR szHCR[] = + {'H','K','E','Y','_','C','L','A','S','S','E','S','_', + 'R','O','O','T','\\',0}; + static const WCHAR szHCU[] = + {'H','K','E','Y','_','C','U','R','R','E','N','T','_', + 'U','S','E','R','\\',0}; + static const WCHAR szHLM[] = + {'H','K','E','Y','_','L','O','C','A','L','_', + 'M','A','C','H','I','N','E','\\',0}; + static const WCHAR szHU[] = + {'H','K','E','Y','_','U','S','E','R','S','\\',0}; + + LPSTR value_data = NULL; + HKEY root_key, hkey; + DWORD type,size; + LPWSTR deformated; + LPCWSTR szRoot, component, name, key, value; + MSICOMPONENT *comp; + MSIRECORD * uirow; + LPWSTR uikey; + INT root; + BOOL check_first = FALSE; + UINT rc; + + ui_progress(package,2,0,0,0); + + value = NULL; + key = NULL; + uikey = NULL; + name = NULL; + + component = MSI_RecordGetString(row, 6); + comp = get_loaded_component(package,component); + if (!comp) + return ERROR_SUCCESS; + + if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL)) + { + TRACE("Skipping write due to disabled component %s\n", + debugstr_w(component)); + + comp->Action = comp->Installed; + + return ERROR_SUCCESS; + } + + comp->Action = INSTALLSTATE_LOCAL; + + name = MSI_RecordGetString(row, 4); + if( MSI_RecordIsNull(row,5) && name ) + { + /* null values can have special meanings */ + if (name[0]=='-' && name[1] == 0) + return ERROR_SUCCESS; + else if ((name[0]=='+' && name[1] == 0) || + (name[0] == '*' && name[1] == 0)) + name = NULL; + check_first = TRUE; + } + + root = MSI_RecordGetInteger(row,2); + key = MSI_RecordGetString(row, 3); + + /* get the root key */ + switch (root) + { + case -1: + { + static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0}; + LPWSTR all_users = msi_dup_property( package, szALLUSER ); + if (all_users && all_users[0] == '1') + { + root_key = HKEY_LOCAL_MACHINE; + szRoot = szHLM; + } + else + { + root_key = HKEY_CURRENT_USER; + szRoot = szHCU; + } + msi_free(all_users); + } + break; + case 0: root_key = HKEY_CLASSES_ROOT; + szRoot = szHCR; + break; + case 1: root_key = HKEY_CURRENT_USER; + szRoot = szHCU; + break; + case 2: root_key = HKEY_LOCAL_MACHINE; + szRoot = szHLM; + break; + case 3: root_key = HKEY_USERS; + szRoot = szHU; + break; + default: + ERR("Unknown root %i\n",root); + root_key=NULL; + szRoot = NULL; + break; + } + if (!root_key) + return ERROR_SUCCESS; + + deformat_string(package, key , &deformated); + size = strlenW(deformated) + strlenW(szRoot) + 1; + uikey = msi_alloc(size*sizeof(WCHAR)); + strcpyW(uikey,szRoot); + strcatW(uikey,deformated); + + if (RegCreateKeyW( root_key, deformated, &hkey)) + { + ERR("Could not create key %s\n",debugstr_w(deformated)); + msi_free(deformated); + msi_free(uikey); + return ERROR_SUCCESS; + } + msi_free(deformated); + + value = MSI_RecordGetString(row,5); + if (value) + value_data = parse_value(package, value, &type, &size); + else + { + static const WCHAR szEmpty[] = {0}; + value_data = (LPSTR)strdupW(szEmpty); + size = 0; + type = REG_SZ; + } + + deformat_string(package, name, &deformated); + + /* get the double nulls to terminate SZ_MULTI */ + if (type == REG_MULTI_SZ) + size +=sizeof(WCHAR); + + if (!check_first) + { + TRACE("Setting value %s of %s\n",debugstr_w(deformated), + debugstr_w(uikey)); + RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size); + } + else + { + DWORD sz = 0; + rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz); + if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA) + { + TRACE("value %s of %s checked already exists\n", + debugstr_w(deformated), debugstr_w(uikey)); + } + else + { + TRACE("Checked and setting value %s of %s\n", + debugstr_w(deformated), debugstr_w(uikey)); + if (deformated || size) + RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size); + } + } + RegCloseKey(hkey); + + uirow = MSI_CreateRecord(3); + MSI_RecordSetStringW(uirow,2,deformated); + MSI_RecordSetStringW(uirow,1,uikey); + + if (type == REG_SZ) + MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data); + else + MSI_RecordSetStringW(uirow,3,value); + + ui_actiondata(package,szWriteRegistryValues,uirow); + msiobj_release( &uirow->hdr ); + + msi_free(value_data); + msi_free(deformated); + msi_free(uikey); + + return ERROR_SUCCESS; +} + +static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package) +{ + UINT rc; + MSIQUERY * view; + static const WCHAR ExecSeqQuery[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','R','e','g','i','s','t','r','y','`',0 }; + + if (!package) + return ERROR_INVALID_HANDLE; + + rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + /* increment progress bar each time action data is sent */ + ui_progress(package,1,REG_PROGRESS_VALUE,1,0); + + rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package); + + msiobj_release(&view->hdr); + return rc; +} + +static UINT ACTION_InstallInitialize(MSIPACKAGE *package) +{ + package->script->CurrentlyScripting = TRUE; + + return ERROR_SUCCESS; +} + + +static UINT ACTION_InstallValidate(MSIPACKAGE *package) +{ + MSICOMPONENT *comp; + DWORD progress = 0; + DWORD total = 0; + static const WCHAR q1[]= + {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ', + '`','R','e','g','i','s','t','r','y','`',0}; + UINT rc; + MSIQUERY * view; + MSIFEATURE *feature; + MSIFILE *file; + + TRACE("InstallValidate\n"); + + rc = MSI_DatabaseOpenViewW(package->db, q1, &view); + if (rc == ERROR_SUCCESS) + { + MSI_IterateRecords( view, &progress, NULL, package ); + msiobj_release( &view->hdr ); + total += progress * REG_PROGRESS_VALUE; + } + + LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry ) + total += COMPONENT_PROGRESS_VALUE; + + LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry ) + total += file->FileSize; + + ui_progress(package,0,total,0,0); + + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n", + debugstr_w(feature->Feature), feature->Installed, feature->Action, + feature->ActionRequest); + } + + return ERROR_SUCCESS; +} + +static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE* package = (MSIPACKAGE*)param; + LPCWSTR cond = NULL; + LPCWSTR message = NULL; + static const WCHAR title[]= + {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0}; + + cond = MSI_RecordGetString(row,1); + + if (MSI_EvaluateConditionW(package,cond) != MSICONDITION_TRUE) + { + LPWSTR deformated; + message = MSI_RecordGetString(row,2); + deformat_string(package,message,&deformated); + MessageBoxW(NULL,deformated,title,MB_OK); + msi_free(deformated); + return ERROR_FUNCTION_FAILED; + } + + return ERROR_SUCCESS; +} + +static UINT ACTION_LaunchConditions(MSIPACKAGE *package) +{ + UINT rc; + MSIQUERY * view = NULL; + static const WCHAR ExecSeqQuery[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0}; + + TRACE("Checking launch conditions\n"); + + rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package); + msiobj_release(&view->hdr); + + return rc; +} + +static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp ) +{ + + if (!cmp->KeyPath) + return resolve_folder(package,cmp->Directory,FALSE,FALSE,NULL); + + if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath) + { + MSIRECORD * row = 0; + UINT root,len; + LPWSTR deformated,buffer,deformated_name; + LPCWSTR key,name; + static const WCHAR ExecSeqQuery[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','R','e','g','i','s','t','r','y','`',' ', + 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`', + ' ','=',' ' ,'\'','%','s','\'',0 }; + static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0}; + static const WCHAR fmt2[]= + {'%','0','2','i',':','\\','%','s','\\','%','s',0}; + + row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath); + if (!row) + return NULL; + + root = MSI_RecordGetInteger(row,2); + key = MSI_RecordGetString(row, 3); + name = MSI_RecordGetString(row, 4); + deformat_string(package, key , &deformated); + deformat_string(package, name, &deformated_name); + + len = strlenW(deformated) + 6; + if (deformated_name) + len+=strlenW(deformated_name); + + buffer = msi_alloc( len *sizeof(WCHAR)); + + if (deformated_name) + sprintfW(buffer,fmt2,root,deformated,deformated_name); + else + sprintfW(buffer,fmt,root,deformated); + + msi_free(deformated); + msi_free(deformated_name); + msiobj_release(&row->hdr); + + return buffer; + } + else if (cmp->Attributes & msidbComponentAttributesODBCDataSource) + { + FIXME("UNIMPLEMENTED keypath as ODBC Source\n"); + return NULL; + } + else + { + MSIFILE *file = get_loaded_file( package, cmp->KeyPath ); + + if (file) + return strdupW( file->TargetPath ); + } + return NULL; +} + +static HKEY openSharedDLLsKey(void) +{ + HKEY hkey=0; + static const WCHAR path[] = + {'S','o','f','t','w','a','r','e','\\', + 'M','i','c','r','o','s','o','f','t','\\', + 'W','i','n','d','o','w','s','\\', + 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', + 'S','h','a','r','e','d','D','L','L','s',0}; + + RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey); + return hkey; +} + +static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll) +{ + HKEY hkey; + DWORD count=0; + DWORD type; + DWORD sz = sizeof(count); + DWORD rc; + + hkey = openSharedDLLsKey(); + rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz); + if (rc != ERROR_SUCCESS) + count = 0; + RegCloseKey(hkey); + return count; +} + +static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count) +{ + HKEY hkey; + + hkey = openSharedDLLsKey(); + if (count > 0) + msi_reg_set_val_dword( hkey, path, count ); + else + RegDeleteValueW(hkey,path); + RegCloseKey(hkey); + return count; +} + +/* + * Return TRUE if the count should be written out and FALSE if not + */ +static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp ) +{ + MSIFEATURE *feature; + INT count = 0; + BOOL write = FALSE; + + /* only refcount DLLs */ + if (comp->KeyPath == NULL || + comp->Attributes & msidbComponentAttributesRegistryKeyPath || + comp->Attributes & msidbComponentAttributesODBCDataSource) + write = FALSE; + else + { + count = ACTION_GetSharedDLLsCount( comp->FullKeypath); + write = (count > 0); + + if (comp->Attributes & msidbComponentAttributesSharedDllRefCount) + write = TRUE; + } + + /* increment counts */ + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + ComponentList *cl; + + if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL )) + continue; + + LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry ) + { + if ( cl->component == comp ) + count++; + } + } + + /* decrement counts */ + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + ComponentList *cl; + + if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT )) + continue; + + LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry ) + { + if ( cl->component == comp ) + count--; + } + } + + /* ref count all the files in the component */ + if (write) + { + MSIFILE *file; + + LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry ) + { + if (file->Component == comp) + ACTION_WriteSharedDLLsCount( file->TargetPath, count ); + } + } + + /* add a count for permenent */ + if (comp->Attributes & msidbComponentAttributesPermanent) + count ++; + + comp->RefCount = count; + + if (write) + ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount ); +} + +/* + * Ok further analysis makes me think that this work is + * actually done in the PublishComponents and PublishFeatures + * step, and not here. It appears like the keypath and all that is + * resolved in this step, however actually written in the Publish steps. + * But we will leave it here for now because it is unclear + */ +static UINT ACTION_ProcessComponents(MSIPACKAGE *package) +{ + WCHAR squished_pc[GUID_SIZE]; + WCHAR squished_cc[GUID_SIZE]; + UINT rc; + MSICOMPONENT *comp; + HKEY hkey=0,hkey2=0; + + /* writes the Component and Features values to the registry */ + + rc = MSIREG_OpenComponents(&hkey); + if (rc != ERROR_SUCCESS) + goto end; + + squash_guid(package->ProductCode,squished_pc); + ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0); + + LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry ) + { + ui_progress(package,2,0,0,0); + if (comp->ComponentId) + { + MSIRECORD * uirow; + + squash_guid(comp->ComponentId,squished_cc); + + msi_free(comp->FullKeypath); + comp->FullKeypath = resolve_keypath( package, comp ); + + /* do the refcounting */ + ACTION_RefCountComponent( package, comp ); + + TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n", + debugstr_w(comp->Component), + debugstr_w(squished_cc), + debugstr_w(comp->FullKeypath), + comp->RefCount); + /* + * Write the keypath out if the component is to be registered + * and delete the key if the component is to be deregistered + */ + if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL)) + { + rc = RegCreateKeyW(hkey,squished_cc,&hkey2); + if (rc != ERROR_SUCCESS) + continue; + + if (comp->FullKeypath) + { + msi_reg_set_val_str( hkey2, squished_pc, comp->FullKeypath ); + + if (comp->Attributes & msidbComponentAttributesPermanent) + { + static const WCHAR szPermKey[] = + { '0','0','0','0','0','0','0','0','0','0','0','0', + '0','0','0','0','0','0','0','0','0','0','0','0', + '0','0','0','0','0','0','0','0',0}; + + msi_reg_set_val_str( hkey2, szPermKey, comp->FullKeypath ); + } + + RegCloseKey(hkey2); + + /* UI stuff */ + uirow = MSI_CreateRecord(3); + MSI_RecordSetStringW(uirow,1,package->ProductCode); + MSI_RecordSetStringW(uirow,2,comp->ComponentId); + MSI_RecordSetStringW(uirow,3,comp->FullKeypath); + ui_actiondata(package,szProcessComponents,uirow); + msiobj_release( &uirow->hdr ); + } + } + else if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ABSENT)) + { + DWORD res; + + rc = RegOpenKeyW(hkey,squished_cc,&hkey2); + if (rc != ERROR_SUCCESS) + continue; + + RegDeleteValueW(hkey2,squished_pc); + + /* if the key is empty delete it */ + res = RegEnumKeyExW(hkey2,0,NULL,0,0,NULL,0,NULL); + RegCloseKey(hkey2); + if (res == ERROR_NO_MORE_ITEMS) + RegDeleteKeyW(hkey,squished_cc); + + /* UI stuff */ + uirow = MSI_CreateRecord(2); + MSI_RecordSetStringW(uirow,1,package->ProductCode); + MSI_RecordSetStringW(uirow,2,comp->ComponentId); + ui_actiondata(package,szProcessComponents,uirow); + msiobj_release( &uirow->hdr ); + } + } + } +end: + RegCloseKey(hkey); + return rc; +} + +typedef struct { + CLSID clsid; + LPWSTR source; + + LPWSTR path; + ITypeLib *ptLib; +} typelib_struct; + +static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType, + LPWSTR lpszName, LONG_PTR lParam) +{ + TLIBATTR *attr; + typelib_struct *tl_struct = (typelib_struct*) lParam; + static const WCHAR fmt[] = {'%','s','\\','%','i',0}; + int sz; + HRESULT res; + + if (!IS_INTRESOURCE(lpszName)) + { + ERR("Not Int Resource Name %s\n",debugstr_w(lpszName)); + return TRUE; + } + + sz = strlenW(tl_struct->source)+4; + sz *= sizeof(WCHAR); + + if ((INT)lpszName == 1) + tl_struct->path = strdupW(tl_struct->source); + else + { + tl_struct->path = msi_alloc(sz); + sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName); + } + + TRACE("trying %s\n", debugstr_w(tl_struct->path)); + res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib); + if (!SUCCEEDED(res)) + { + msi_free(tl_struct->path); + tl_struct->path = NULL; + + return TRUE; + } + + ITypeLib_GetLibAttr(tl_struct->ptLib, &attr); + if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid))) + { + ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr); + return FALSE; + } + + msi_free(tl_struct->path); + tl_struct->path = NULL; + + ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr); + ITypeLib_Release(tl_struct->ptLib); + + return TRUE; +} + +static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE* package = (MSIPACKAGE*)param; + LPCWSTR component; + MSICOMPONENT *comp; + MSIFILE *file; + typelib_struct tl_struct; + HMODULE module; + static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0}; + + component = MSI_RecordGetString(row,3); + comp = get_loaded_component(package,component); + if (!comp) + return ERROR_SUCCESS; + + if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL)) + { + TRACE("Skipping typelib reg due to disabled component\n"); + + comp->Action = comp->Installed; + + return ERROR_SUCCESS; + } + + comp->Action = INSTALLSTATE_LOCAL; + + file = get_loaded_file( package, comp->KeyPath ); + if (!file) + return ERROR_SUCCESS; + + module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE ); + if (module) + { + LPCWSTR guid; + guid = MSI_RecordGetString(row,1); + CLSIDFromString((LPWSTR)guid, &tl_struct.clsid); + tl_struct.source = strdupW( file->TargetPath ); + tl_struct.path = NULL; + + EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc, + (LONG_PTR)&tl_struct); + + if (tl_struct.path) + { + LPWSTR help = NULL; + LPCWSTR helpid; + HRESULT res; + + helpid = MSI_RecordGetString(row,6); + + if (helpid) + help = resolve_folder(package,helpid,FALSE,FALSE,NULL); + res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help); + msi_free(help); + + if (!SUCCEEDED(res)) + ERR("Failed to register type library %s\n", + debugstr_w(tl_struct.path)); + else + { + ui_actiondata(package,szRegisterTypeLibraries,row); + + TRACE("Registered %s\n", debugstr_w(tl_struct.path)); + } + + ITypeLib_Release(tl_struct.ptLib); + msi_free(tl_struct.path); + } + else + ERR("Failed to load type library %s\n", + debugstr_w(tl_struct.source)); + + FreeLibrary(module); + msi_free(tl_struct.source); + } + else + ERR("Could not load file! %s\n", debugstr_w(file->TargetPath)); + + return ERROR_SUCCESS; +} + +static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package) +{ + /* + * OK this is a bit confusing.. I am given a _Component key and I believe + * that the file that is being registered as a type library is the "key file + * of that component" which I interpret to mean "The file in the KeyPath of + * that component". + */ + UINT rc; + MSIQUERY * view; + static const WCHAR Query[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','T','y','p','e','L','i','b','`',0}; + + rc = MSI_DatabaseOpenViewW(package->db, Query, &view); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package); + msiobj_release(&view->hdr); + return rc; +} + +static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE *package = (MSIPACKAGE*)param; + LPWSTR target_file, target_folder; + LPCWSTR buffer; + WCHAR filename[0x100]; + DWORD sz; + MSICOMPONENT *comp; + static const WCHAR szlnk[]={'.','l','n','k',0}; + IShellLinkW *sl; + IPersistFile *pf; + HRESULT res; + + buffer = MSI_RecordGetString(row,4); + comp = get_loaded_component(package,buffer); + if (!comp) + return ERROR_SUCCESS; + + if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL )) + { + TRACE("Skipping shortcut creation due to disabled component\n"); + + comp->Action = comp->Installed; + + return ERROR_SUCCESS; + } + + comp->Action = INSTALLSTATE_LOCAL; + + ui_actiondata(package,szCreateShortcuts,row); + + res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, + &IID_IShellLinkW, (LPVOID *) &sl ); + + if (FAILED(res)) + { + ERR("Is IID_IShellLink\n"); + return ERROR_SUCCESS; + } + + res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf ); + if( FAILED( res ) ) + { + ERR("Is IID_IPersistFile\n"); + return ERROR_SUCCESS; + } + + buffer = MSI_RecordGetString(row,2); + target_folder = resolve_folder(package, buffer,FALSE,FALSE,NULL); + + /* may be needed because of a bug somehwere else */ + create_full_pathW(target_folder); + + sz = 0x100; + MSI_RecordGetStringW(row,3,filename,&sz); + reduce_to_longfilename(filename); + if (!strchrW(filename,'.') || strcmpiW(strchrW(filename,'.'),szlnk)) + strcatW(filename,szlnk); + target_file = build_directory_name(2, target_folder, filename); + msi_free(target_folder); + + buffer = MSI_RecordGetString(row,5); + if (strchrW(buffer,'[')) + { + LPWSTR deformated; + deformat_string(package,buffer,&deformated); + IShellLinkW_SetPath(sl,deformated); + msi_free(deformated); + } + else + { + FIXME("poorly handled shortcut format, advertised shortcut\n"); + IShellLinkW_SetPath(sl,comp->FullKeypath); + } + + if (!MSI_RecordIsNull(row,6)) + { + LPWSTR deformated; + buffer = MSI_RecordGetString(row,6); + deformat_string(package,buffer,&deformated); + IShellLinkW_SetArguments(sl,deformated); + msi_free(deformated); + } + + if (!MSI_RecordIsNull(row,7)) + { + buffer = MSI_RecordGetString(row,7); + IShellLinkW_SetDescription(sl,buffer); + } + + if (!MSI_RecordIsNull(row,8)) + IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8)); + + if (!MSI_RecordIsNull(row,9)) + { + LPWSTR Path; + INT index; + + buffer = MSI_RecordGetString(row,9); + + Path = build_icon_path(package,buffer); + index = MSI_RecordGetInteger(row,10); + + IShellLinkW_SetIconLocation(sl,Path,index); + msi_free(Path); + } + + if (!MSI_RecordIsNull(row,11)) + IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11)); + + if (!MSI_RecordIsNull(row,12)) + { + LPWSTR Path; + buffer = MSI_RecordGetString(row,12); + Path = resolve_folder(package, buffer, FALSE, FALSE, NULL); + IShellLinkW_SetWorkingDirectory(sl,Path); + msi_free(Path); + } + + TRACE("Writing shortcut to %s\n",debugstr_w(target_file)); + IPersistFile_Save(pf,target_file,FALSE); + + msi_free(target_file); + + IPersistFile_Release( pf ); + IShellLinkW_Release( sl ); + + return ERROR_SUCCESS; +} + +static UINT ACTION_CreateShortcuts(MSIPACKAGE *package) +{ + UINT rc; + HRESULT res; + MSIQUERY * view; + static const WCHAR Query[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','S','h','o','r','t','c','u','t','`',0}; + + rc = MSI_DatabaseOpenViewW(package->db, Query, &view); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + res = CoInitialize( NULL ); + if (FAILED (res)) + { + ERR("CoInitialize failed\n"); + return ERROR_FUNCTION_FAILED; + } + + rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package); + msiobj_release(&view->hdr); + + CoUninitialize(); + + return rc; +} + +static UINT ITERATE_PublishProduct(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE* package = (MSIPACKAGE*)param; + HANDLE the_file; + LPWSTR FilePath; + LPCWSTR FileName; + CHAR buffer[1024]; + DWORD sz; + UINT rc; + + FileName = MSI_RecordGetString(row,1); + if (!FileName) + { + ERR("Unable to get FileName\n"); + return ERROR_SUCCESS; + } + + FilePath = build_icon_path(package,FileName); + + TRACE("Creating icon file at %s\n",debugstr_w(FilePath)); + + the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + + if (the_file == INVALID_HANDLE_VALUE) + { + ERR("Unable to create file %s\n",debugstr_w(FilePath)); + msi_free(FilePath); + return ERROR_SUCCESS; + } + + do + { + DWORD write; + sz = 1024; + rc = MSI_RecordReadStream(row,2,buffer,&sz); + if (rc != ERROR_SUCCESS) + { + ERR("Failed to get stream\n"); + CloseHandle(the_file); + DeleteFileW(FilePath); + break; + } + WriteFile(the_file,buffer,sz,&write,NULL); + } while (sz == 1024); + + msi_free(FilePath); + + CloseHandle(the_file); + return ERROR_SUCCESS; +} + +/* + * 99% of the work done here is only done for + * advertised installs. However this is where the + * Icon table is processed and written out + * so that is what I am going to do here. + */ +static UINT ACTION_PublishProduct(MSIPACKAGE *package) +{ + UINT rc; + MSIQUERY * view; + static const WCHAR Query[]= + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','I','c','o','n','`',0}; + /* for registry stuff */ + HKEY hkey=0; + HKEY hukey=0; + static const WCHAR szProductLanguage[] = + {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0}; + static const WCHAR szARPProductIcon[] = + {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0}; + static const WCHAR szProductVersion[] = + {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0}; + DWORD langid; + LPWSTR buffer; + DWORD size; + MSIHANDLE hDb, hSumInfo; + + /* write out icon files */ + + rc = MSI_DatabaseOpenViewW(package->db, Query, &view); + if (rc == ERROR_SUCCESS) + { + MSI_IterateRecords(view, NULL, ITERATE_PublishProduct, package); + msiobj_release(&view->hdr); + } + + /* ok there is a lot more done here but i need to figure out what */ + + rc = MSIREG_OpenProductsKey(package->ProductCode,&hkey,TRUE); + if (rc != ERROR_SUCCESS) + goto end; + + rc = MSIREG_OpenUserProductsKey(package->ProductCode,&hukey,TRUE); + if (rc != ERROR_SUCCESS) + goto end; + + + buffer = msi_dup_property( package, INSTALLPROPERTY_PRODUCTNAMEW ); + msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTNAMEW, buffer ); + msi_free(buffer); + + langid = msi_get_property_int( package, szProductLanguage, 0 ); + msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid ); + + buffer = msi_dup_property( package, szARPProductIcon ); + if (buffer) + { + LPWSTR path = build_icon_path(package,buffer); + msi_reg_set_val_str( hukey, INSTALLPROPERTY_PRODUCTICONW, path ); + msi_free( path ); + } + msi_free(buffer); + + buffer = msi_dup_property( package, szProductVersion ); + if (buffer) + { + DWORD verdword = build_version_dword(buffer); + msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword ); + } + msi_free(buffer); + + /* FIXME: Need to write more keys to the user registry */ + + hDb= alloc_msihandle( &package->db->hdr ); + rc = MsiGetSummaryInformationW(hDb, NULL, 0, &hSumInfo); + MsiCloseHandle(hDb); + if (rc == ERROR_SUCCESS) + { + WCHAR guidbuffer[0x200]; + size = 0x200; + rc = MsiSummaryInfoGetPropertyW(hSumInfo, 9, NULL, NULL, NULL, + guidbuffer, &size); + if (rc == ERROR_SUCCESS) + { + WCHAR squashed[GUID_SIZE]; + /* for now we only care about the first guid */ + LPWSTR ptr = strchrW(guidbuffer,';'); + if (ptr) *ptr = 0; + squash_guid(guidbuffer,squashed); + msi_reg_set_val_str( hukey, INSTALLPROPERTY_PACKAGECODEW, squashed ); + } + else + { + ERR("Unable to query Revision_Number...\n"); + rc = ERROR_SUCCESS; + } + MsiCloseHandle(hSumInfo); + } + else + { + ERR("Unable to open Summary Information\n"); + rc = ERROR_SUCCESS; + } + +end: + + RegCloseKey(hkey); + RegCloseKey(hukey); + + return rc; +} + +static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE *package = (MSIPACKAGE*)param; + LPCWSTR component,section,key,value,identifier,filename,dirproperty; + LPWSTR deformated_section, deformated_key, deformated_value; + LPWSTR folder, fullname = NULL; + MSIRECORD * uirow; + INT action; + MSICOMPONENT *comp; + static const WCHAR szWindowsFolder[] = + {'W','i','n','d','o','w','s','F','o','l','d','e','r',0}; + + component = MSI_RecordGetString(row, 8); + comp = get_loaded_component(package,component); + + if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL)) + { + TRACE("Skipping ini file due to disabled component %s\n", + debugstr_w(component)); + + comp->Action = comp->Installed; + + return ERROR_SUCCESS; + } + + comp->Action = INSTALLSTATE_LOCAL; + + identifier = MSI_RecordGetString(row,1); + filename = MSI_RecordGetString(row,2); + dirproperty = MSI_RecordGetString(row,3); + section = MSI_RecordGetString(row,4); + key = MSI_RecordGetString(row,5); + value = MSI_RecordGetString(row,6); + action = MSI_RecordGetInteger(row,7); + + deformat_string(package,section,&deformated_section); + deformat_string(package,key,&deformated_key); + deformat_string(package,value,&deformated_value); + + if (dirproperty) + { + folder = resolve_folder(package, dirproperty, FALSE, FALSE, NULL); + if (!folder) + folder = msi_dup_property( package, dirproperty ); + } + else + folder = msi_dup_property( package, szWindowsFolder ); + + if (!folder) + { + ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty)); + goto cleanup; + } + + fullname = build_directory_name(2, folder, filename); + + if (action == 0) + { + TRACE("Adding value %s to section %s in %s\n", + debugstr_w(deformated_key), debugstr_w(deformated_section), + debugstr_w(fullname)); + WritePrivateProfileStringW(deformated_section, deformated_key, + deformated_value, fullname); + } + else if (action == 1) + { + WCHAR returned[10]; + GetPrivateProfileStringW(deformated_section, deformated_key, NULL, + returned, 10, fullname); + if (returned[0] == 0) + { + TRACE("Adding value %s to section %s in %s\n", + debugstr_w(deformated_key), debugstr_w(deformated_section), + debugstr_w(fullname)); + + WritePrivateProfileStringW(deformated_section, deformated_key, + deformated_value, fullname); + } + } + else if (action == 3) + FIXME("Append to existing section not yet implemented\n"); + + uirow = MSI_CreateRecord(4); + MSI_RecordSetStringW(uirow,1,identifier); + MSI_RecordSetStringW(uirow,2,deformated_section); + MSI_RecordSetStringW(uirow,3,deformated_key); + MSI_RecordSetStringW(uirow,4,deformated_value); + ui_actiondata(package,szWriteIniValues,uirow); + msiobj_release( &uirow->hdr ); +cleanup: + msi_free(fullname); + msi_free(folder); + msi_free(deformated_key); + msi_free(deformated_value); + msi_free(deformated_section); + return ERROR_SUCCESS; +} + +static UINT ACTION_WriteIniValues(MSIPACKAGE *package) +{ + UINT rc; + MSIQUERY * view; + static const WCHAR ExecSeqQuery[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','I','n','i','F','i','l','e','`',0}; + + rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view); + if (rc != ERROR_SUCCESS) + { + TRACE("no IniFile table\n"); + return ERROR_SUCCESS; + } + + rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package); + msiobj_release(&view->hdr); + return rc; +} + +static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE *package = (MSIPACKAGE*)param; + LPCWSTR filename; + LPWSTR FullName; + MSIFILE *file; + DWORD len; + static const WCHAR ExeStr[] = + {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0}; + static const WCHAR close[] = {'\"',0}; + STARTUPINFOW si; + PROCESS_INFORMATION info; + BOOL brc; + + memset(&si,0,sizeof(STARTUPINFOW)); + + filename = MSI_RecordGetString(row,1); + file = get_loaded_file( package, filename ); + + if (!file) + { + ERR("Unable to find file id %s\n",debugstr_w(filename)); + return ERROR_SUCCESS; + } + + len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2; + + FullName = msi_alloc(len*sizeof(WCHAR)); + strcpyW(FullName,ExeStr); + strcatW( FullName, file->TargetPath ); + strcatW(FullName,close); + + TRACE("Registering %s\n",debugstr_w(FullName)); + brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon, + &si, &info); + + if (brc) + msi_dialog_check_messages(info.hProcess); + + msi_free(FullName); + return ERROR_SUCCESS; +} + +static UINT ACTION_SelfRegModules(MSIPACKAGE *package) +{ + UINT rc; + MSIQUERY * view; + static const WCHAR ExecSeqQuery[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','S','e','l','f','R','e','g','`',0}; + + rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view); + if (rc != ERROR_SUCCESS) + { + TRACE("no SelfReg table\n"); + return ERROR_SUCCESS; + } + + MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package); + msiobj_release(&view->hdr); + + return ERROR_SUCCESS; +} + +static UINT ACTION_PublishFeatures(MSIPACKAGE *package) +{ + MSIFEATURE *feature; + UINT rc; + HKEY hkey=0; + HKEY hukey=0; + + rc = MSIREG_OpenFeaturesKey(package->ProductCode,&hkey,TRUE); + if (rc != ERROR_SUCCESS) + goto end; + + rc = MSIREG_OpenUserFeaturesKey(package->ProductCode,&hukey,TRUE); + if (rc != ERROR_SUCCESS) + goto end; + + /* here the guids are base 85 encoded */ + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + ComponentList *cl; + LPWSTR data = NULL; + GUID clsid; + INT size; + BOOL absent = FALSE; + + if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) && + !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) && + !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED )) + absent = TRUE; + + size = 1; + LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry ) + { + size += 21; + } + if (feature->Feature_Parent) + size += strlenW( feature->Feature_Parent )+2; + + data = msi_alloc(size * sizeof(WCHAR)); + + data[0] = 0; + LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry ) + { + MSICOMPONENT* component = cl->component; + WCHAR buf[21]; + + memset(buf,0,sizeof(buf)); + if (component->ComponentId) + { + TRACE("From %s\n",debugstr_w(component->ComponentId)); + CLSIDFromString(component->ComponentId, &clsid); + encode_base85_guid(&clsid,buf); + TRACE("to %s\n",debugstr_w(buf)); + strcatW(data,buf); + } + } + if (feature->Feature_Parent) + { + static const WCHAR sep[] = {'\2',0}; + strcatW(data,sep); + strcatW(data,feature->Feature_Parent); + } + + msi_reg_set_val_str( hkey, feature->Feature, data ); + msi_free(data); + + size = 0; + if (feature->Feature_Parent) + size = strlenW(feature->Feature_Parent)*sizeof(WCHAR); + if (!absent) + { + RegSetValueExW(hukey,feature->Feature,0,REG_SZ, + (LPBYTE)feature->Feature_Parent,size); + } + else + { + size += 2*sizeof(WCHAR); + data = msi_alloc(size); + data[0] = 0x6; + data[1] = 0; + if (feature->Feature_Parent) + strcpyW( &data[1], feature->Feature_Parent ); + RegSetValueExW(hukey,feature->Feature,0,REG_SZ, + (LPBYTE)data,size); + msi_free(data); + } + } + +end: + RegCloseKey(hkey); + RegCloseKey(hukey); + return rc; +} + +static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey ) +{ + static const WCHAR installerPathFmt[] = { + '%','s','\\','I','n','s','t','a','l','l','e','r','\\',0}; + static const WCHAR fmt[] = { + '%','s','\\', + 'I','n','s','t','a','l','l','e','r','\\', + '%','x','.','m','s','i',0}; + static const WCHAR szOriginalDatabase[] = + {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0}; + WCHAR windir[MAX_PATH], path[MAX_PATH], packagefile[MAX_PATH]; + INT num, start; + LPWSTR msiFilePath; + BOOL r; + + /* copy the package locally */ + num = GetTickCount() & 0xffff; + if (!num) + num = 1; + start = num; + GetWindowsDirectoryW( windir, MAX_PATH ); + snprintfW( packagefile, MAX_PATH, fmt, windir, num ); + do + { + HANDLE handle = CreateFileW(packagefile,GENERIC_WRITE, 0, NULL, + CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 ); + if (handle != INVALID_HANDLE_VALUE) + { + CloseHandle(handle); + break; + } + if (GetLastError() != ERROR_FILE_EXISTS && + GetLastError() != ERROR_SHARING_VIOLATION) + break; + if (!(++num & 0xffff)) num = 1; + sprintfW(packagefile,fmt,num); + } while (num != start); + + snprintfW( path, MAX_PATH, installerPathFmt, windir ); + create_full_pathW(path); + + TRACE("Copying to local package %s\n",debugstr_w(packagefile)); + + msiFilePath = msi_dup_property( package, szOriginalDatabase ); + r = CopyFileW( msiFilePath, packagefile, FALSE); + msi_free( msiFilePath ); + + if (!r) + { + ERR("Unable to copy package (%s -> %s) (error %ld)\n", + debugstr_w(msiFilePath), debugstr_w(packagefile), GetLastError()); + return ERROR_FUNCTION_FAILED; + } + + /* FIXME: maybe set this key in ACTION_RegisterProduct instead */ + msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile ); + return ERROR_SUCCESS; +} + +static UINT msi_write_uninstall_property_vals( MSIPACKAGE *package, HKEY hkey ) +{ + LPWSTR prop, val, key; + static const LPCSTR propval[] = { + "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix", + "ARPCONTACT", "Contact", + "ARPCOMMENTS", "Comments", + "ProductName", "DisplayName", + "ProductVersion", "DisplayVersion", + "ARPHELPLINK", "HelpLink", + "ARPHELPTELEPHONE", "HelpTelephone", + "ARPINSTALLLOCATION", "InstallLocation", + "SourceDir", "InstallSource", + "Manufacturer", "Publisher", + "ARPREADME", "Readme", + "ARPSIZE", "Size", + "ARPURLINFOABOUT", "URLInfoAbout", + "ARPURLUPDATEINFO", "URLUpdateInfo", + NULL, + }; + const LPCSTR *p = propval; + + while( *p ) + { + prop = strdupAtoW( *p++ ); + key = strdupAtoW( *p++ ); + val = msi_dup_property( package, prop ); + msi_reg_set_val_str( hkey, key, val ); + msi_free(val); + msi_free(key); + msi_free(prop); + } + return ERROR_SUCCESS; +} + +static UINT ACTION_RegisterProduct(MSIPACKAGE *package) +{ + HKEY hkey=0; + LPWSTR buffer = NULL; + UINT rc; + DWORD size, langid; + static const WCHAR szWindowsInstaller[] = + {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0}; + static const WCHAR szUpgradeCode[] = + {'U','p','g','r','a','d','e','C','o','d','e',0}; + static const WCHAR modpath_fmt[] = + {'M','s','i','E','x','e','c','.','e','x','e',' ', + '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0}; + static const WCHAR szModifyPath[] = + {'M','o','d','i','f','y','P','a','t','h',0}; + static const WCHAR szUninstallString[] = + {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0}; + static const WCHAR szEstimatedSize[] = + {'E','s','t','i','m','a','t','e','d','S','i','z','e',0}; + static const WCHAR szProductLanguage[] = + {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0}; + static const WCHAR szProductVersion[] = + {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0}; + + SYSTEMTIME systime; + static const WCHAR date_fmt[] = {'%','i','%','i','%','i',0}; + LPWSTR upgrade_code; + WCHAR szDate[9]; + + rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE); + if (rc != ERROR_SUCCESS) + return rc; + + /* dump all the info i can grab */ + /* FIXME: Flesh out more information */ + + msi_write_uninstall_property_vals( package, hkey ); + + msi_reg_set_val_dword( hkey, szWindowsInstaller, 1 ); + + msi_make_package_local( package, hkey ); + + /* do ModifyPath and UninstallString */ + size = deformat_string(package,modpath_fmt,&buffer); + RegSetValueExW(hkey,szModifyPath,0,REG_EXPAND_SZ,(LPBYTE)buffer,size); + RegSetValueExW(hkey,szUninstallString,0,REG_EXPAND_SZ,(LPBYTE)buffer,size); + msi_free(buffer); + + /* FIXME: Write real Estimated Size when we have it */ + msi_reg_set_val_dword( hkey, szEstimatedSize, 0 ); + + GetLocalTime(&systime); + sprintfW(szDate,date_fmt,systime.wYear,systime.wMonth,systime.wDay); + msi_reg_set_val_str( hkey, INSTALLPROPERTY_INSTALLDATEW, szDate ); + + langid = msi_get_property_int( package, szProductLanguage, 0 ); + msi_reg_set_val_dword( hkey, INSTALLPROPERTY_LANGUAGEW, langid ); + + buffer = msi_dup_property( package, szProductVersion ); + if (buffer) + { + DWORD verdword = build_version_dword(buffer); + + msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONW, verdword ); + msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword>>24 ); + msi_reg_set_val_dword( hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword>>16)&0x00FF ); + } + msi_free(buffer); + + /* Handle Upgrade Codes */ + upgrade_code = msi_dup_property( package, szUpgradeCode ); + if (upgrade_code) + { + HKEY hkey2; + WCHAR squashed[33]; + MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey2, TRUE); + squash_guid(package->ProductCode,squashed); + msi_reg_set_val_str( hkey2, squashed, NULL ); + RegCloseKey(hkey2); + MSIREG_OpenUserUpgradeCodesKey(upgrade_code, &hkey2, TRUE); + squash_guid(package->ProductCode,squashed); + msi_reg_set_val_str( hkey2, squashed, NULL ); + RegCloseKey(hkey2); + + msi_free(upgrade_code); + } + + RegCloseKey(hkey); + + return ERROR_SUCCESS; +} + +static UINT ACTION_InstallExecute(MSIPACKAGE *package) +{ + return execute_script(package,INSTALL_SCRIPT); +} + +static UINT ACTION_InstallFinalize(MSIPACKAGE *package) +{ + UINT rc; + + /* turn off scheduleing */ + package->script->CurrentlyScripting= FALSE; + + /* first do the same as an InstallExecute */ + rc = ACTION_InstallExecute(package); + if (rc != ERROR_SUCCESS) + return rc; + + /* then handle Commit Actions */ + rc = execute_script(package,COMMIT_SCRIPT); + + return rc; +} + +static UINT ACTION_ForceReboot(MSIPACKAGE *package) +{ + static const WCHAR RunOnce[] = { + 'S','o','f','t','w','a','r','e','\\', + 'M','i','c','r','o','s','o','f','t','\\', + 'W','i','n','d','o','w','s','\\', + 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', + 'R','u','n','O','n','c','e',0}; + static const WCHAR InstallRunOnce[] = { + 'S','o','f','t','w','a','r','e','\\', + 'M','i','c','r','o','s','o','f','t','\\', + 'W','i','n','d','o','w','s','\\', + 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', + 'I','n','s','t','a','l','l','e','r','\\', + 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0}; + + static const WCHAR msiexec_fmt[] = { + '%','s', + '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ', + '\"','%','s','\"',0}; + static const WCHAR install_fmt[] = { + '/','I',' ','\"','%','s','\"',' ', + 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ', + 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0}; + WCHAR buffer[256], sysdir[MAX_PATH]; + HKEY hkey; + WCHAR squished_pc[100]; + + squash_guid(package->ProductCode,squished_pc); + + GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0])); + RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey); + snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir, + squished_pc); + + msi_reg_set_val_str( hkey, squished_pc, buffer ); + RegCloseKey(hkey); + + TRACE("Reboot command %s\n",debugstr_w(buffer)); + + RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey); + sprintfW(buffer,install_fmt,package->ProductCode,squished_pc); + + msi_reg_set_val_str( hkey, squished_pc, buffer ); + RegCloseKey(hkey); + + return ERROR_INSTALL_SUSPEND; +} + +UINT ACTION_ResolveSource(MSIPACKAGE* package) +{ + DWORD attrib; + UINT rc; + /* + * we are currently doing what should be done here in the top level Install + * however for Adminastrative and uninstalls this step will be needed + */ + if (!package->PackagePath) + return ERROR_SUCCESS; + + attrib = GetFileAttributesW(package->PackagePath); + if (attrib == INVALID_FILE_ATTRIBUTES) + { + LPWSTR prompt; + LPWSTR msg; + DWORD size = 0; + + rc = MsiSourceListGetInfoW(package->ProductCode, NULL, + MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT, + INSTALLPROPERTY_DISKPROMPTW,NULL,&size); + if (rc == ERROR_MORE_DATA) + { + prompt = msi_alloc(size * sizeof(WCHAR)); + MsiSourceListGetInfoW(package->ProductCode, NULL, + MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT, + INSTALLPROPERTY_DISKPROMPTW,prompt,&size); + } + else + prompt = strdupW(package->PackagePath); + + msg = generate_error_string(package,1302,1,prompt); + while(attrib == INVALID_FILE_ATTRIBUTES) + { + rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL); + if (rc == IDCANCEL) + { + rc = ERROR_INSTALL_USEREXIT; + break; + } + attrib = GetFileAttributesW(package->PackagePath); + } + msi_free(prompt); + rc = ERROR_SUCCESS; + } + else + return ERROR_SUCCESS; + + return rc; +} + +static UINT ACTION_RegisterUser(MSIPACKAGE *package) +{ + HKEY hkey=0; + LPWSTR buffer; + LPWSTR productid; + UINT rc,i; + + static const WCHAR szPropKeys[][80] = + { + {'P','r','o','d','u','c','t','I','D',0}, + {'U','S','E','R','N','A','M','E',0}, + {'C','O','M','P','A','N','Y','N','A','M','E',0}, + {0}, + }; + + static const WCHAR szRegKeys[][80] = + { + {'P','r','o','d','u','c','t','I','D',0}, + {'R','e','g','O','w','n','e','r',0}, + {'R','e','g','C','o','m','p','a','n','y',0}, + {0}, + }; + + productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW ); + if (!productid) + return ERROR_SUCCESS; + + rc = MSIREG_OpenUninstallKey(package->ProductCode,&hkey,TRUE); + if (rc != ERROR_SUCCESS) + goto end; + + for( i = 0; szPropKeys[i][0]; i++ ) + { + buffer = msi_dup_property( package, szPropKeys[i] ); + msi_reg_set_val_str( hkey, szRegKeys[i], buffer ); + msi_free( buffer ); + } + +end: + msi_free(productid); + RegCloseKey(hkey); + + return ERROR_SUCCESS; +} + + +static UINT ACTION_ExecuteAction(MSIPACKAGE *package) +{ + UINT rc; + + package->script->InWhatSequence |= SEQUENCE_EXEC; + rc = ACTION_ProcessExecSequence(package,FALSE); + return rc; +} + + +/* + * Code based off of code located here + * http://www.codeproject.com/gdi/fontnamefromfile.asp + * + * Using string index 4 (full font name) instead of 1 (family name) + */ +static LPWSTR load_ttfname_from(LPCWSTR filename) +{ + HANDLE handle; + LPWSTR ret = NULL; + int i; + + typedef struct _tagTT_OFFSET_TABLE{ + USHORT uMajorVersion; + USHORT uMinorVersion; + USHORT uNumOfTables; + USHORT uSearchRange; + USHORT uEntrySelector; + USHORT uRangeShift; + }TT_OFFSET_TABLE; + + typedef struct _tagTT_TABLE_DIRECTORY{ + char szTag[4]; /* table name */ + ULONG uCheckSum; /* Check sum */ + ULONG uOffset; /* Offset from beginning of file */ + ULONG uLength; /* length of the table in bytes */ + }TT_TABLE_DIRECTORY; + + typedef struct _tagTT_NAME_TABLE_HEADER{ + USHORT uFSelector; /* format selector. Always 0 */ + USHORT uNRCount; /* Name Records count */ + USHORT uStorageOffset; /* Offset for strings storage, + * from start of the table */ + }TT_NAME_TABLE_HEADER; + + typedef struct _tagTT_NAME_RECORD{ + USHORT uPlatformID; + USHORT uEncodingID; + USHORT uLanguageID; + USHORT uNameID; + USHORT uStringLength; + USHORT uStringOffset; /* from start of storage area */ + }TT_NAME_RECORD; + +#define SWAPWORD(x) MAKEWORD(HIBYTE(x), LOBYTE(x)) +#define SWAPLONG(x) MAKELONG(SWAPWORD(HIWORD(x)), SWAPWORD(LOWORD(x))) + + handle = CreateFileW(filename ,GENERIC_READ, 0, NULL, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL, 0 ); + if (handle != INVALID_HANDLE_VALUE) + { + TT_TABLE_DIRECTORY tblDir; + BOOL bFound = FALSE; + TT_OFFSET_TABLE ttOffsetTable; + DWORD dwRead; + + ReadFile(handle,&ttOffsetTable, sizeof(TT_OFFSET_TABLE),&dwRead,NULL); + ttOffsetTable.uNumOfTables = SWAPWORD(ttOffsetTable.uNumOfTables); + ttOffsetTable.uMajorVersion = SWAPWORD(ttOffsetTable.uMajorVersion); + ttOffsetTable.uMinorVersion = SWAPWORD(ttOffsetTable.uMinorVersion); + + if (ttOffsetTable.uMajorVersion != 1 || + ttOffsetTable.uMinorVersion != 0) + return NULL; + + for (i=0; i< ttOffsetTable.uNumOfTables; i++) + { + ReadFile(handle,&tblDir, sizeof(TT_TABLE_DIRECTORY),&dwRead,NULL); + if (strncmp(tblDir.szTag,"name",4)==0) + { + bFound = TRUE; + tblDir.uLength = SWAPLONG(tblDir.uLength); + tblDir.uOffset = SWAPLONG(tblDir.uOffset); + break; + } + } + + if (bFound) + { + TT_NAME_TABLE_HEADER ttNTHeader; + TT_NAME_RECORD ttRecord; + + SetFilePointer(handle, tblDir.uOffset, NULL, FILE_BEGIN); + ReadFile(handle,&ttNTHeader, sizeof(TT_NAME_TABLE_HEADER), + &dwRead,NULL); + + ttNTHeader.uNRCount = SWAPWORD(ttNTHeader.uNRCount); + ttNTHeader.uStorageOffset = SWAPWORD(ttNTHeader.uStorageOffset); + bFound = FALSE; + for(i=0; i 0) + { + strcat(buf,tt); + ret = strdupAtoW(buf); + msi_free(buf); + break; + } + + msi_free(buf); + SetFilePointer(handle,nPos, NULL, FILE_BEGIN); + } + } + } + CloseHandle(handle); + } + else + ERR("Unable to open font file %s\n", debugstr_w(filename)); + + TRACE("Returning fontname %s\n",debugstr_w(ret)); + return ret; +} + +static UINT ITERATE_RegisterFonts(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE *package = (MSIPACKAGE*)param; + LPWSTR name; + LPCWSTR filename; + MSIFILE *file; + static const WCHAR regfont1[] = + {'S','o','f','t','w','a','r','e','\\', + 'M','i','c','r','o','s','o','f','t','\\', + 'W','i','n','d','o','w','s',' ','N','T','\\', + 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', + 'F','o','n','t','s',0}; + static const WCHAR regfont2[] = + {'S','o','f','t','w','a','r','e','\\', + 'M','i','c','r','o','s','o','f','t','\\', + 'W','i','n','d','o','w','s','\\', + 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', + 'F','o','n','t','s',0}; + HKEY hkey1; + HKEY hkey2; + + filename = MSI_RecordGetString( row, 1 ); + file = get_loaded_file( package, filename ); + if (!file) + { + ERR("Unable to load file\n"); + return ERROR_SUCCESS; + } + + /* check to make sure that component is installed */ + if (!ACTION_VerifyComponentForAction( file->Component, INSTALLSTATE_LOCAL)) + { + TRACE("Skipping: Component not scheduled for install\n"); + return ERROR_SUCCESS; + } + + RegCreateKeyW(HKEY_LOCAL_MACHINE,regfont1,&hkey1); + RegCreateKeyW(HKEY_LOCAL_MACHINE,regfont2,&hkey2); + + if (MSI_RecordIsNull(row,2)) + name = load_ttfname_from( file->TargetPath ); + else + name = msi_dup_record_field(row,2); + + if (name) + { + msi_reg_set_val_str( hkey1, name, file->FileName ); + msi_reg_set_val_str( hkey2, name, file->FileName ); + } + + msi_free(name); + RegCloseKey(hkey1); + RegCloseKey(hkey2); + return ERROR_SUCCESS; +} + +static UINT ACTION_RegisterFonts(MSIPACKAGE *package) +{ + UINT rc; + MSIQUERY * view; + static const WCHAR ExecSeqQuery[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','F','o','n','t','`',0}; + + rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view); + if (rc != ERROR_SUCCESS) + { + TRACE("MSI_DatabaseOpenViewW failed: %d\n", rc); + return ERROR_SUCCESS; + } + + MSI_IterateRecords(view, NULL, ITERATE_RegisterFonts, package); + msiobj_release(&view->hdr); + + return ERROR_SUCCESS; +} + +static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param) +{ + MSIPACKAGE *package = (MSIPACKAGE*)param; + LPCWSTR compgroupid=NULL; + LPCWSTR feature=NULL; + LPCWSTR text = NULL; + LPCWSTR qualifier = NULL; + LPCWSTR component = NULL; + LPWSTR advertise = NULL; + LPWSTR output = NULL; + HKEY hkey; + UINT rc = ERROR_SUCCESS; + MSICOMPONENT *comp; + DWORD sz = 0; + + component = MSI_RecordGetString(rec,3); + comp = get_loaded_component(package,component); + + if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) && + !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) && + !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED )) + { + TRACE("Skipping: Component %s not scheduled for install\n", + debugstr_w(component)); + + return ERROR_SUCCESS; + } + + compgroupid = MSI_RecordGetString(rec,1); + + rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE); + if (rc != ERROR_SUCCESS) + goto end; + + text = MSI_RecordGetString(rec,4); + qualifier = MSI_RecordGetString(rec,2); + feature = MSI_RecordGetString(rec,5); + + advertise = create_component_advertise_string(package, comp, feature); + + sz = strlenW(advertise); + + if (text) + sz += lstrlenW(text); + + sz+=3; + sz *= sizeof(WCHAR); + + output = msi_alloc(sz); + memset(output,0,sz); + strcpyW(output,advertise); + msi_free(advertise); + + if (text) + strcatW(output,text); + + msi_reg_set_val_multi_str( hkey, qualifier, output ); + +end: + RegCloseKey(hkey); + msi_free(output); + + return rc; +} + +/* + * At present I am ignorning the advertised components part of this and only + * focusing on the qualified component sets + */ +static UINT ACTION_PublishComponents(MSIPACKAGE *package) +{ + UINT rc; + MSIQUERY * view; + static const WCHAR ExecSeqQuery[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','P','u','b','l','i','s','h', + 'C','o','m','p','o','n','e','n','t','`',0}; + + rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package); + msiobj_release(&view->hdr); + + return rc; +} + +static UINT msi_unimplemented_action_stub( MSIPACKAGE *package, + LPCSTR action, LPCWSTR table ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ', + 'F','R','O','M',' ','`','%','s','`',0 }; + MSIQUERY *view = NULL; + DWORD count = 0; + UINT r; + + r = MSI_OpenQuery( package->db, &view, query, table ); + if (r == ERROR_SUCCESS) + { + r = MSI_IterateRecords(view, &count, NULL, package); + msiobj_release(&view->hdr); + } + + if (count) + FIXME("%s -> %lu ignored %s table values\n", + action, count, debugstr_w(table)); + + return ERROR_SUCCESS; +} + +static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package ) +{ + TRACE("%p\n", package); + return ERROR_SUCCESS; +} + +static UINT ACTION_RemoveIniValues( MSIPACKAGE *package ) +{ + static const WCHAR table[] = + {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 }; + return msi_unimplemented_action_stub( package, "RemoveIniValues", table ); +} + +static UINT ACTION_MoveFiles( MSIPACKAGE *package ) +{ + static const WCHAR table[] = { 'M','o','v','e','F','i','l','e',0 }; + return msi_unimplemented_action_stub( package, "MoveFiles", table ); +} + +static UINT ACTION_PatchFiles( MSIPACKAGE *package ) +{ + static const WCHAR table[] = { 'P','a','t','c','h',0 }; + return msi_unimplemented_action_stub( package, "PatchFiles", table ); +} + +static UINT ACTION_BindImage( MSIPACKAGE *package ) +{ + static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 }; + return msi_unimplemented_action_stub( package, "BindImage", table ); +} + +static UINT ACTION_IsolateComponents( MSIPACKAGE *package ) +{ + static const WCHAR table[] = { + 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 }; + return msi_unimplemented_action_stub( package, "IsolateComponents", table ); +} + +static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package ) +{ + static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 }; + return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table ); +} + +static UINT ACTION_SelfUnregModules( MSIPACKAGE *package ) +{ + static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 }; + return msi_unimplemented_action_stub( package, "SelfUnregModules", table ); +} + +static UINT ACTION_InstallServices( MSIPACKAGE *package ) +{ + static const WCHAR table[] = { + 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0 }; + return msi_unimplemented_action_stub( package, "InstallServices", table ); +} + +static UINT ACTION_StartServices( MSIPACKAGE *package ) +{ + static const WCHAR table[] = { + 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 }; + return msi_unimplemented_action_stub( package, "StartServices", table ); +} + +static UINT ACTION_StopServices( MSIPACKAGE *package ) +{ + static const WCHAR table[] = { + 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 }; + return msi_unimplemented_action_stub( package, "StopServices", table ); +} + +static UINT ACTION_DeleteServices( MSIPACKAGE *package ) +{ + static const WCHAR table[] = { + 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 }; + return msi_unimplemented_action_stub( package, "DeleteServices", table ); +} + +static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package ) +{ + static const WCHAR table[] = { + 'E','n','v','i','r','o','n','m','e','n','t',0 }; + return msi_unimplemented_action_stub( package, "WriteEnvironmentStrings", table ); +} + +static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package ) +{ + static const WCHAR table[] = { + 'E','n','v','i','r','o','n','m','e','n','t',0 }; + return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table ); +} + +static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package ) +{ + static const WCHAR table[] = { + 'M','s','i','A','s','s','e','m','b','l','y',0 }; + return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table ); +} + +static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package ) +{ + static const WCHAR table[] = { + 'M','s','i','A','s','s','e','m','b','l','y',0 }; + return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table ); +} + +static UINT ACTION_UnregisterFonts( MSIPACKAGE *package ) +{ + static const WCHAR table[] = { 'F','o','n','t',0 }; + return msi_unimplemented_action_stub( package, "UnregisterFonts", table ); +} + +static UINT ACTION_CCPSearch( MSIPACKAGE *package ) +{ + static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 }; + return msi_unimplemented_action_stub( package, "CCPSearch", table ); +} + +static UINT ACTION_RMCCPSearch( MSIPACKAGE *package ) +{ + static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 }; + return msi_unimplemented_action_stub( package, "RMCCPSearch", table ); +} + +static struct _actions StandardActions[] = { + { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace }, + { szAppSearch, ACTION_AppSearch }, + { szBindImage, ACTION_BindImage }, + { szCCPSearch, ACTION_CCPSearch}, + { szCostFinalize, ACTION_CostFinalize }, + { szCostInitialize, ACTION_CostInitialize }, + { szCreateFolders, ACTION_CreateFolders }, + { szCreateShortcuts, ACTION_CreateShortcuts }, + { szDeleteServices, ACTION_DeleteServices }, + { szDisableRollback, NULL}, + { szDuplicateFiles, ACTION_DuplicateFiles }, + { szExecuteAction, ACTION_ExecuteAction }, + { szFileCost, ACTION_FileCost }, + { szFindRelatedProducts, ACTION_FindRelatedProducts }, + { szForceReboot, ACTION_ForceReboot }, + { szInstallAdminPackage, NULL}, + { szInstallExecute, ACTION_InstallExecute }, + { szInstallExecuteAgain, ACTION_InstallExecute }, + { szInstallFiles, ACTION_InstallFiles}, + { szInstallFinalize, ACTION_InstallFinalize }, + { szInstallInitialize, ACTION_InstallInitialize }, + { szInstallSFPCatalogFile, NULL}, + { szInstallValidate, ACTION_InstallValidate }, + { szIsolateComponents, ACTION_IsolateComponents }, + { szLaunchConditions, ACTION_LaunchConditions }, + { szMigrateFeatureStates, ACTION_MigrateFeatureStates }, + { szMoveFiles, ACTION_MoveFiles }, + { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies }, + { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies }, + { szInstallODBC, NULL}, + { szInstallServices, ACTION_InstallServices }, + { szPatchFiles, ACTION_PatchFiles }, + { szProcessComponents, ACTION_ProcessComponents }, + { szPublishComponents, ACTION_PublishComponents }, + { szPublishFeatures, ACTION_PublishFeatures }, + { szPublishProduct, ACTION_PublishProduct }, + { szRegisterClassInfo, ACTION_RegisterClassInfo }, + { szRegisterComPlus, NULL}, + { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo }, + { szRegisterFonts, ACTION_RegisterFonts }, + { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo }, + { szRegisterProduct, ACTION_RegisterProduct }, + { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo }, + { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries }, + { szRegisterUser, ACTION_RegisterUser}, + { szRemoveDuplicateFiles, NULL}, + { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings }, + { szRemoveExistingProducts, NULL}, + { szRemoveFiles, ACTION_RemoveFiles}, + { szRemoveFolders, NULL}, + { szRemoveIniValues, ACTION_RemoveIniValues }, + { szRemoveODBC, NULL}, + { szRemoveRegistryValues, NULL}, + { szRemoveShortcuts, NULL}, + { szResolveSource, ACTION_ResolveSource}, + { szRMCCPSearch, ACTION_RMCCPSearch}, + { szScheduleReboot, NULL}, + { szSelfRegModules, ACTION_SelfRegModules }, + { szSelfUnregModules, ACTION_SelfUnregModules }, + { szSetODBCFolders, NULL}, + { szStartServices, ACTION_StartServices }, + { szStopServices, ACTION_StopServices }, + { szUnpublishComponents, NULL}, + { szUnpublishFeatures, NULL}, + { szUnregisterClassInfo, NULL}, + { szUnregisterComPlus, NULL}, + { szUnregisterExtensionInfo, NULL}, + { szUnregisterFonts, ACTION_UnregisterFonts }, + { szUnregisterMIMEInfo, NULL}, + { szUnregisterProgIdInfo, NULL}, + { szUnregisterTypeLibraries, NULL}, + { szValidateProductID, NULL}, + { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings }, + { szWriteIniValues, ACTION_WriteIniValues }, + { szWriteRegistryValues, ACTION_WriteRegistryValues}, + { NULL, NULL}, +}; diff --git a/reactos/dll/msi/action.h b/reactos/dll/msi/action.h new file mode 100644 index 00000000000..9b3e58d5947 --- /dev/null +++ b/reactos/dll/msi/action.h @@ -0,0 +1,302 @@ +/* + * Common prototypes for Action handlers + * + * Copyright 2005 Aric Stewart 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __MSI_ACTION_H__ +#define __MSI_ACTION_H__ + +#include "wine/list.h" + +typedef struct tagMSIFEATURE +{ + struct list entry; + LPWSTR Feature; + LPWSTR Feature_Parent; + LPWSTR Title; + LPWSTR Description; + INT Display; + INT Level; + LPWSTR Directory; + INT Attributes; + + INSTALLSTATE Installed; + INSTALLSTATE ActionRequest; + INSTALLSTATE Action; + + struct list Components; + + INT Cost; +} MSIFEATURE; + +typedef struct tagMSICOMPONENT +{ + struct list entry; + DWORD magic; + LPWSTR Component; + LPWSTR ComponentId; + LPWSTR Directory; + INT Attributes; + LPWSTR Condition; + LPWSTR KeyPath; + + INSTALLSTATE Installed; + INSTALLSTATE ActionRequest; + INSTALLSTATE Action; + + BOOL Enabled; + INT Cost; + INT RefCount; + + LPWSTR FullKeypath; + LPWSTR AdvertiseString; +} MSICOMPONENT; + +typedef struct tagComponentList +{ + struct list entry; + MSICOMPONENT *component; +} ComponentList; + +typedef struct tagMSIFOLDER +{ + struct list entry; + LPWSTR Directory; + LPWSTR TargetDefault; + LPWSTR SourceDefault; + + LPWSTR ResolvedTarget; + LPWSTR ResolvedSource; + LPWSTR Property; /* initially set property */ + struct tagMSIFOLDER *Parent; + INT State; + /* 0 = uninitialized */ + /* 1 = existing */ + /* 2 = created remove if empty */ + /* 3 = created persist if empty */ + INT Cost; + INT Space; +} MSIFOLDER; + +typedef enum _msi_file_state { + msifs_invalid, + msifs_missing, + msifs_overwrite, + msifs_present, + msifs_installed, + msifs_skipped, +} msi_file_state; + +typedef struct tagMSIFILE +{ + struct list entry; + LPWSTR File; + MSICOMPONENT *Component; + LPWSTR FileName; + LPWSTR ShortName; + INT FileSize; + LPWSTR Version; + LPWSTR Language; + INT Attributes; + INT Sequence; + msi_file_state state; + LPWSTR SourcePath; + LPWSTR TargetPath; +} MSIFILE; + +typedef struct tagMSITEMPFILE +{ + struct list entry; + LPWSTR File; + LPWSTR Path; +} MSITEMPFILE; + +typedef struct tagMSIAPPID +{ + struct list entry; + LPWSTR AppID; /* Primary key */ + LPWSTR RemoteServerName; + LPWSTR LocalServer; + LPWSTR ServiceParameters; + LPWSTR DllSurrogate; + BOOL ActivateAtStorage; + BOOL RunAsInteractiveUser; +} MSIAPPID; + +typedef struct tagMSIPROGID MSIPROGID; + +typedef struct tagMSICLASS +{ + struct list entry; + LPWSTR clsid; /* Primary Key */ + LPWSTR Context; /* Primary Key */ + MSICOMPONENT *Component; + MSIPROGID *ProgID; + LPWSTR ProgIDText; + LPWSTR Description; + MSIAPPID *AppID; + LPWSTR FileTypeMask; + LPWSTR IconPath; + LPWSTR DefInprocHandler; + LPWSTR DefInprocHandler32; + LPWSTR Argument; + MSIFEATURE *Feature; + INT Attributes; + /* not in the table, set during installation */ + BOOL Installed; +} MSICLASS; + +typedef struct tagMSIMIME MSIMIME; + +typedef struct tagMSIEXTENSION +{ + struct list entry; + LPWSTR Extension; /* Primary Key */ + MSICOMPONENT *Component; + MSIPROGID *ProgID; + LPWSTR ProgIDText; + MSIMIME *Mime; + MSIFEATURE *Feature; + /* not in the table, set during installation */ + BOOL Installed; + struct list verbs; +} MSIEXTENSION; + +struct tagMSIPROGID +{ + struct list entry; + LPWSTR ProgID; /* Primary Key */ + MSIPROGID *Parent; + MSICLASS *Class; + LPWSTR Description; + LPWSTR IconPath; + /* not in the table, set during installation */ + BOOL InstallMe; + MSIPROGID *CurVer; + MSIPROGID *VersionInd; +}; + +typedef struct tagMSIVERB +{ + struct list entry; + LPWSTR Verb; + INT Sequence; + LPWSTR Command; + LPWSTR Argument; +} MSIVERB; + +struct tagMSIMIME +{ + struct list entry; + LPWSTR ContentType; /* Primary Key */ + MSIEXTENSION *Extension; + LPWSTR clsid; + MSICLASS *Class; + /* not in the table, set during installation */ + BOOL InstallMe; +}; + +enum SCRIPTS { + INSTALL_SCRIPT = 0, + COMMIT_SCRIPT = 1, + ROLLBACK_SCRIPT = 2, + TOTAL_SCRIPTS = 3 +}; + +#define SEQUENCE_UI 0x1 +#define SEQUENCE_EXEC 0x2 +#define SEQUENCE_INSTALL 0x10 + +typedef struct tagMSISCRIPT +{ + LPWSTR *Actions[TOTAL_SCRIPTS]; + UINT ActionCount[TOTAL_SCRIPTS]; + BOOL ExecuteSequenceRun; + BOOL CurrentlyScripting; + UINT InWhatSequence; + LPWSTR *UniqueActions; + UINT UniqueActionsCount; +} MSISCRIPT; + + +extern UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, BOOL force); +extern UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action); +extern void ACTION_FinishCustomActions( MSIPACKAGE* package); +extern UINT ACTION_CustomAction(MSIPACKAGE *package,const WCHAR *action, BOOL execute); + +/* actions in other modules */ +extern UINT ACTION_AppSearch(MSIPACKAGE *package); +extern UINT ACTION_FindRelatedProducts(MSIPACKAGE *package); +extern UINT ACTION_InstallFiles(MSIPACKAGE *package); +extern UINT ACTION_RemoveFiles(MSIPACKAGE *package); +extern UINT ACTION_DuplicateFiles(MSIPACKAGE *package); +extern UINT ACTION_RegisterClassInfo(MSIPACKAGE *package); +extern UINT ACTION_RegisterProgIdInfo(MSIPACKAGE *package); +extern UINT ACTION_RegisterExtensionInfo(MSIPACKAGE *package); +extern UINT ACTION_RegisterMIMEInfo(MSIPACKAGE *package); + + +/* Helpers */ +extern DWORD deformat_string(MSIPACKAGE *package, LPCWSTR ptr, WCHAR** data ); +extern LPWSTR msi_dup_record_field(MSIRECORD *row, INT index); +extern LPWSTR msi_dup_property(MSIPACKAGE *package, LPCWSTR prop); +extern LPWSTR resolve_folder(MSIPACKAGE *package, LPCWSTR name, BOOL source, + BOOL set_prop, MSIFOLDER **folder); +extern MSICOMPONENT *get_loaded_component( MSIPACKAGE* package, LPCWSTR Component ); +extern MSIFEATURE *get_loaded_feature( MSIPACKAGE* package, LPCWSTR Feature ); +extern MSIFILE *get_loaded_file( MSIPACKAGE* package, LPCWSTR file ); +extern MSIFOLDER *get_loaded_folder( MSIPACKAGE *package, LPCWSTR dir ); +extern int track_tempfile(MSIPACKAGE *package, LPCWSTR name, LPCWSTR path); +extern UINT schedule_action(MSIPACKAGE *package, UINT script, LPCWSTR action); +extern LPWSTR build_icon_path(MSIPACKAGE *, LPCWSTR); +extern DWORD build_version_dword(LPCWSTR); +extern LPWSTR build_directory_name(DWORD , ...); +extern BOOL create_full_pathW(const WCHAR *path); +extern BOOL ACTION_VerifyComponentForAction(MSICOMPONENT*, INSTALLSTATE); +extern BOOL ACTION_VerifyFeatureForAction(MSIFEATURE*, INSTALLSTATE); +extern void reduce_to_longfilename(WCHAR*); +extern void reduce_to_shortfilename(WCHAR*); +extern LPWSTR create_component_advertise_string(MSIPACKAGE*, MSICOMPONENT*, LPCWSTR); +extern void ACTION_UpdateComponentStates(MSIPACKAGE *package, LPCWSTR szFeature); +extern UINT register_unique_action(MSIPACKAGE *, LPCWSTR); +extern BOOL check_unique_action(MSIPACKAGE *, LPCWSTR); +extern WCHAR* generate_error_string(MSIPACKAGE *, UINT, DWORD, ... ); +extern UINT msi_create_component_directories( MSIPACKAGE *package ); + + +/* control event stuff */ +extern VOID ControlEvent_FireSubscribedEvent(MSIPACKAGE *package, LPCWSTR event, + MSIRECORD *data); +extern VOID ControlEvent_CleanupSubscriptions(MSIPACKAGE *package); +extern VOID ControlEvent_SubscribeToEvent(MSIPACKAGE *package, LPCWSTR event, + LPCWSTR control, LPCWSTR attribute); +extern VOID ControlEvent_UnSubscribeToEvent( MSIPACKAGE *package, LPCWSTR event, + LPCWSTR control, LPCWSTR attribute ); + +/* User Interface messages from the actions */ +extern void ui_progress(MSIPACKAGE *, int, int, int, int); +extern void ui_actiondata(MSIPACKAGE *, LPCWSTR, MSIRECORD *); + + +/* string consts use a number of places and defined in helpers.c*/ +extern const WCHAR cszSourceDir[]; +extern const WCHAR szProductCode[]; +extern const WCHAR cszRootDrive[]; +extern const WCHAR cszbs[]; + +#endif /* __MSI_ACTION_H__ */ diff --git a/reactos/dll/msi/appsearch.c b/reactos/dll/msi/appsearch.c new file mode 100644 index 00000000000..4c59c1a0bbb --- /dev/null +++ b/reactos/dll/msi/appsearch.c @@ -0,0 +1,976 @@ +/* + * Implementation of the AppSearch action of the Microsoft Installer (msi.dll) + * + * Copyright 2005 Juan Lang + * + * 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 + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "msi.h" +#include "msiquery.h" +#include "msidefs.h" +#include "winver.h" +#include "wine/unicode.h" +#include "wine/debug.h" +#include "msipriv.h" +#include "action.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +typedef struct tagMSISIGNATURE +{ + LPWSTR Name; /* NOT owned by this structure */ + LPWSTR Property; /* NOT owned by this structure */ + LPWSTR File; + DWORD MinVersionMS; + DWORD MinVersionLS; + DWORD MaxVersionMS; + DWORD MaxVersionLS; + DWORD MinSize; + DWORD MaxSize; + FILETIME MinTime; + FILETIME MaxTime; + LPWSTR Languages; +}MSISIGNATURE; + +static void ACTION_VerStrToInteger(LPCWSTR verStr, PDWORD ms, PDWORD ls) +{ + const WCHAR *ptr; + int x1 = 0, x2 = 0, x3 = 0, x4 = 0; + + x1 = atoiW(verStr); + ptr = strchrW(verStr, '.'); + if (ptr) + { + x2 = atoiW(ptr + 1); + ptr = strchrW(ptr + 1, '.'); + } + if (ptr) + { + x3 = atoiW(ptr + 1); + ptr = strchrW(ptr + 1, '.'); + } + if (ptr) + x4 = atoiW(ptr + 1); + /* FIXME: byte-order dependent? */ + *ms = x1 << 16 | x2; + *ls = x3 << 16 | x4; +} + +/* Fills in sig with the the values from the Signature table, where name is the + * signature to find. Upon return, sig->File will be NULL if the record is not + * found, and not NULL if it is found. + * Warning: clears all fields in sig! + * Returns ERROR_SUCCESS upon success (where not finding the record counts as + * success), something else on error. + */ +static UINT ACTION_AppSearchGetSignature(MSIPACKAGE *package, MSISIGNATURE *sig, + LPCWSTR name) +{ + MSIQUERY *view; + UINT rc; + static const WCHAR ExecSeqQuery[] = { + 's','e','l','e','c','t',' ','*',' ', + 'f','r','o','m',' ', + 'S','i','g','n','a','t','u','r','e',' ', + 'w','h','e','r','e',' ','S','i','g','n','a','t','u','r','e',' ','=',' ', + '\'','%','s','\'',0}; + + TRACE("(package %p, sig %p)\n", package, sig); + memset(sig, 0, sizeof(*sig)); + rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, name); + if (rc == ERROR_SUCCESS) + { + MSIRECORD *row = 0; + DWORD time; + WCHAR *minVersion, *maxVersion; + + rc = MSI_ViewExecute(view, 0); + if (rc != ERROR_SUCCESS) + { + TRACE("MSI_ViewExecute returned %d\n", rc); + goto end; + } + rc = MSI_ViewFetch(view,&row); + if (rc != ERROR_SUCCESS) + { + TRACE("MSI_ViewFetch returned %d\n", rc); + rc = ERROR_SUCCESS; + goto end; + } + + /* get properties */ + sig->File = msi_dup_record_field(row,2); + minVersion = msi_dup_record_field(row,3); + if (minVersion) + { + ACTION_VerStrToInteger(minVersion, &sig->MinVersionMS, + &sig->MinVersionLS); + msi_free( minVersion); + } + maxVersion = msi_dup_record_field(row,4); + if (maxVersion) + { + ACTION_VerStrToInteger(maxVersion, &sig->MaxVersionMS, + &sig->MaxVersionLS); + msi_free( maxVersion); + } + sig->MinSize = MSI_RecordGetInteger(row,5); + if (sig->MinSize == MSI_NULL_INTEGER) + sig->MinSize = 0; + sig->MaxSize = MSI_RecordGetInteger(row,6); + if (sig->MaxSize == MSI_NULL_INTEGER) + sig->MaxSize = 0; + sig->Languages = msi_dup_record_field(row,9); + time = MSI_RecordGetInteger(row,7); + if (time != MSI_NULL_INTEGER) + DosDateTimeToFileTime(HIWORD(time), LOWORD(time), &sig->MinTime); + time = MSI_RecordGetInteger(row,8); + if (time != MSI_NULL_INTEGER) + DosDateTimeToFileTime(HIWORD(time), LOWORD(time), &sig->MaxTime); + TRACE("Found file name %s for Signature_ %s;\n", + debugstr_w(sig->File), debugstr_w(name)); + TRACE("MinVersion is %d.%d.%d.%d\n", HIWORD(sig->MinVersionMS), + LOWORD(sig->MinVersionMS), HIWORD(sig->MinVersionLS), + LOWORD(sig->MinVersionLS)); + TRACE("MaxVersion is %d.%d.%d.%d\n", HIWORD(sig->MaxVersionMS), + LOWORD(sig->MaxVersionMS), HIWORD(sig->MaxVersionLS), + LOWORD(sig->MaxVersionLS)); + TRACE("MinSize is %ld, MaxSize is %ld;\n", sig->MinSize, sig->MaxSize); + TRACE("Languages is %s\n", debugstr_w(sig->Languages)); + +end: + msiobj_release(&row->hdr); + MSI_ViewClose(view); + msiobj_release(&view->hdr); + } + else + { + TRACE("MSI_OpenQuery returned %d\n", rc); + rc = ERROR_SUCCESS; + } + + TRACE("returning %d\n", rc); + return rc; +} + +static UINT ACTION_AppSearchComponents(MSIPACKAGE *package, BOOL *appFound, + MSISIGNATURE *sig) +{ + MSIQUERY *view; + UINT rc; + static const WCHAR ExecSeqQuery[] = { + 's','e','l','e','c','t',' ','*',' ', + 'f','r','o','m',' ', + 'C','o','m','p','L','o','c','a','t','o','r',' ', + 'w','h','e','r','e',' ','S','i','g','n','a','t','u','r','e','_',' ','=',' ', + '\'','%','s','\'',0}; + + TRACE("(package %p, appFound %p, sig %p)\n", package, appFound, sig); + *appFound = FALSE; + rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, sig->Name); + if (rc == ERROR_SUCCESS) + { + MSIRECORD *row = 0; + WCHAR guid[50]; + DWORD sz; + + rc = MSI_ViewExecute(view, 0); + if (rc != ERROR_SUCCESS) + { + TRACE("MSI_ViewExecute returned %d\n", rc); + goto end; + } + rc = MSI_ViewFetch(view,&row); + if (rc != ERROR_SUCCESS) + { + TRACE("MSI_ViewFetch returned %d\n", rc); + rc = ERROR_SUCCESS; + goto end; + } + + /* get GUID */ + guid[0] = 0; + sz=sizeof(guid)/sizeof(guid[0]); + rc = MSI_RecordGetStringW(row,2,guid,&sz); + if (rc != ERROR_SUCCESS) + { + ERR("Error is %x\n",rc); + goto end; + } + FIXME("AppSearch unimplemented for CompLocator table (GUID %s)\n", + debugstr_w(guid)); + +end: + msiobj_release(&row->hdr); + MSI_ViewClose(view); + msiobj_release(&view->hdr); + } + else + { + TRACE("MSI_OpenQuery returned %d\n", rc); + rc = ERROR_SUCCESS; + } + + TRACE("returning %d\n", rc); + return rc; +} + +static UINT ACTION_AppSearchReg(MSIPACKAGE *package, BOOL *appFound, + MSISIGNATURE *sig) +{ + MSIQUERY *view; + UINT rc; + static const WCHAR ExecSeqQuery[] = { + 's','e','l','e','c','t',' ','*',' ', + 'f','r','o','m',' ', + 'R','e','g','L','o','c','a','t','o','r',' ', + 'w','h','e','r','e',' ','S','i','g','n','a','t','u','r','e','_',' ','=',' ', + '\'','%','s','\'',0}; + static const WCHAR dwordFmt[] = { '#','%','d','\0' }; + static const WCHAR expandSzFmt[] = { '#','%','%','%','s','\0' }; + static const WCHAR binFmt[] = { '#','x','%','x','\0' }; + + TRACE("(package %p, appFound %p, sig %p)\n", package, appFound, sig); + *appFound = FALSE; + rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, sig->Name); + if (rc == ERROR_SUCCESS) + { + MSIRECORD *row = 0; + LPWSTR keyPath = NULL, valueName = NULL, propertyValue = NULL; + int root, type; + HKEY rootKey, key = NULL; + DWORD sz = 0, regType, i; + LPBYTE value = NULL; + + rc = MSI_ViewExecute(view, 0); + if (rc != ERROR_SUCCESS) + { + TRACE("MSI_ViewExecute returned %d\n", rc); + goto end; + } + rc = MSI_ViewFetch(view,&row); + if (rc != ERROR_SUCCESS) + { + TRACE("MSI_ViewFetch returned %d\n", rc); + rc = ERROR_SUCCESS; + goto end; + } + + root = MSI_RecordGetInteger(row,2); + keyPath = msi_dup_record_field(row,3); + /* FIXME: keyPath needs to be expanded for properties */ + valueName = msi_dup_record_field(row,4); + /* FIXME: valueName probably does too */ + type = MSI_RecordGetInteger(row,5); + + if ((type & 0x0f) != msidbLocatorTypeRawValue) + { + FIXME("AppSearch unimplemented for type %d (key path %s, value %s)\n", + type, debugstr_w(keyPath), debugstr_w(valueName)); + goto end; + } + + switch (root) + { + case msidbRegistryRootClassesRoot: + rootKey = HKEY_CLASSES_ROOT; + break; + case msidbRegistryRootCurrentUser: + rootKey = HKEY_CURRENT_USER; + break; + case msidbRegistryRootLocalMachine: + rootKey = HKEY_LOCAL_MACHINE; + break; + case msidbRegistryRootUsers: + rootKey = HKEY_USERS; + break; + default: + WARN("Unknown root key %d\n", root); + goto end; + } + + rc = RegCreateKeyW(rootKey, keyPath, &key); + if (rc) + { + TRACE("RegCreateKeyW returned %d\n", rc); + rc = ERROR_SUCCESS; + goto end; + } + rc = RegQueryValueExW(key, valueName, NULL, NULL, NULL, &sz); + if (rc) + { + TRACE("RegQueryValueExW returned %d\n", rc); + rc = ERROR_SUCCESS; + goto end; + } + /* FIXME: sanity-check sz before allocating (is there an upper-limit + * on the value of a property?) + */ + value = msi_alloc( sz); + rc = RegQueryValueExW(key, valueName, NULL, ®Type, value, &sz); + if (rc) + { + TRACE("RegQueryValueExW returned %d\n", rc); + rc = ERROR_SUCCESS; + goto end; + } + + /* bail out if the registry key is empty */ + if (sz == 0) + { + rc = ERROR_SUCCESS; + goto end; + } + + switch (regType) + { + case REG_SZ: + if (*(LPWSTR)value == '#') + { + /* escape leading pound with another */ + propertyValue = msi_alloc( sz + sizeof(WCHAR)); + propertyValue[0] = '#'; + strcpyW(propertyValue + 1, (LPWSTR)value); + } + else + { + propertyValue = msi_alloc( sz); + strcpyW(propertyValue, (LPWSTR)value); + } + break; + case REG_DWORD: + /* 7 chars for digits, 1 for NULL, 1 for #, and 1 for sign + * char if needed + */ + propertyValue = msi_alloc( 10 * sizeof(WCHAR)); + sprintfW(propertyValue, dwordFmt, *(DWORD *)value); + break; + case REG_EXPAND_SZ: + /* space for extra #% characters in front */ + propertyValue = msi_alloc( sz + 2 * sizeof(WCHAR)); + sprintfW(propertyValue, expandSzFmt, (LPWSTR)value); + break; + case REG_BINARY: + /* 3 == length of "#x" */ + propertyValue = msi_alloc( (sz * 3 + 1) * sizeof(WCHAR)); + for (i = 0; i < sz; i++) + sprintfW(propertyValue + i * 3, binFmt, value[i]); + break; + default: + WARN("unimplemented for values of type %ld\n", regType); + goto end; + } + + TRACE("found registry value, setting %s to %s\n", + debugstr_w(sig->Property), debugstr_w(propertyValue)); + rc = MSI_SetPropertyW(package, sig->Property, propertyValue); + *appFound = TRUE; + +end: + msi_free( propertyValue); + msi_free( value); + RegCloseKey(key); + + msi_free( keyPath); + msi_free( valueName); + + msiobj_release(&row->hdr); + MSI_ViewClose(view); + msiobj_release(&view->hdr); + } + else + { + TRACE("MSI_OpenQuery returned %d\n", rc); + rc = ERROR_SUCCESS; + } + + TRACE("returning %d\n", rc); + return rc; +} + +static UINT ACTION_AppSearchIni(MSIPACKAGE *package, BOOL *appFound, + MSISIGNATURE *sig) +{ + MSIQUERY *view; + UINT rc; + static const WCHAR ExecSeqQuery[] = { + 's','e','l','e','c','t',' ','*',' ', + 'f','r','o','m',' ', + 'I','n','i','L','o','c','a','t','o','r',' ', + 'w','h','e','r','e',' ','S','i','g','n','a','t','u','r','e','_',' ','=',' ', + '\'','%','s','\'',0}; + + TRACE("(package %p, appFound %p, sig %p)\n", package, appFound, sig); + *appFound = FALSE; + rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, sig->Name); + if (rc == ERROR_SUCCESS) + { + MSIRECORD *row = 0; + LPWSTR fileName; + + rc = MSI_ViewExecute(view, 0); + if (rc != ERROR_SUCCESS) + { + TRACE("MSI_ViewExecute returned %d\n", rc); + goto end; + } + rc = MSI_ViewFetch(view,&row); + if (rc != ERROR_SUCCESS) + { + TRACE("MSI_ViewFetch returned %d\n", rc); + rc = ERROR_SUCCESS; + goto end; + } + + /* get file name */ + fileName = msi_dup_record_field(row,2); + FIXME("AppSearch unimplemented for IniLocator (ini file name %s)\n", + debugstr_w(fileName)); + msi_free( fileName); + +end: + msiobj_release(&row->hdr); + MSI_ViewClose(view); + msiobj_release(&view->hdr); + } + else + { + TRACE("MSI_OpenQuery returned %d\n", rc); + rc = ERROR_SUCCESS; + } + + + TRACE("returning %d\n", rc); + return rc; +} + +/* Expands the value in src into a path without property names and only + * containing long path names into dst. Replaces at most len characters of dst, + * and always NULL-terminates dst if dst is not NULL and len >= 1. + * May modify src. + * Assumes src and dst are non-overlapping. + * FIXME: return code probably needed: + * - what does AppSearch return if the table values are invalid? + * - what if dst is too small? + */ +static void ACTION_ExpandAnyPath(MSIPACKAGE *package, WCHAR *src, WCHAR *dst, + size_t len) +{ + WCHAR *ptr; + size_t copied = 0; + + if (!src || !dst || !len) + return; + + /* Ignore the short portion of the path, don't think we can use it anyway */ + if ((ptr = strchrW(src, '|'))) + ptr++; + else + ptr = src; + while (*ptr && copied < len - 1) + { + WCHAR *prop = strchrW(ptr, '['); + + if (prop) + { + WCHAR *propEnd = strchrW(prop + 1, ']'); + + if (!propEnd) + { + WARN("Unterminated property name in AnyPath: %s\n", + debugstr_w(prop)); + break; + } + else + { + DWORD propLen; + + *propEnd = 0; + propLen = len - copied - 1; + MSI_GetPropertyW(package, prop + 1, dst + copied, &propLen); + ptr = propEnd + 1; + copied += propLen; + } + } + else + { + size_t toCopy = min(strlenW(ptr) + 1, len - copied - 1); + + memcpy(dst + copied, ptr, toCopy * sizeof(WCHAR)); + ptr += toCopy; + copied += toCopy; + } + } + *(dst + copied) = '\0'; +} + +/* Sets *matches to whether the file (whose path is filePath) matches the + * versions set in sig. + * Return ERROR_SUCCESS in case of success (whether or not the file matches), + * something else if an install-halting error occurs. + */ +static UINT ACTION_FileVersionMatches(MSISIGNATURE *sig, LPCWSTR filePath, + BOOL *matches) +{ + UINT rc = ERROR_SUCCESS; + + *matches = FALSE; + if (sig->Languages) + { + FIXME(": need to check version for languages %s\n", + debugstr_w(sig->Languages)); + } + else + { + DWORD zero, size = GetFileVersionInfoSizeW((LPWSTR)filePath, &zero); + + if (size) + { + LPVOID buf = msi_alloc( size); + + if (buf) + { + static WCHAR rootW[] = { '\\',0 }; + UINT versionLen; + LPVOID subBlock = NULL; + + if (GetFileVersionInfoW((LPWSTR)filePath, 0, size, buf)) + VerQueryValueW(buf, (LPWSTR)rootW, &subBlock, &versionLen); + if (subBlock) + { + VS_FIXEDFILEINFO *info = + (VS_FIXEDFILEINFO *)subBlock; + + TRACE("Comparing file version %d.%d.%d.%d:\n", + HIWORD(info->dwFileVersionMS), + LOWORD(info->dwFileVersionMS), + HIWORD(info->dwFileVersionLS), + LOWORD(info->dwFileVersionLS)); + if (info->dwFileVersionMS < sig->MinVersionMS + || (info->dwFileVersionMS == sig->MinVersionMS && + info->dwFileVersionLS < sig->MinVersionLS)) + { + TRACE("Less than minimum version %d.%d.%d.%d\n", + HIWORD(sig->MinVersionMS), + LOWORD(sig->MinVersionMS), + HIWORD(sig->MinVersionLS), + LOWORD(sig->MinVersionLS)); + } + else if (info->dwFileVersionMS < sig->MinVersionMS + || (info->dwFileVersionMS == sig->MinVersionMS && + info->dwFileVersionLS < sig->MinVersionLS)) + { + TRACE("Greater than minimum version %d.%d.%d.%d\n", + HIWORD(sig->MaxVersionMS), + LOWORD(sig->MaxVersionMS), + HIWORD(sig->MaxVersionLS), + LOWORD(sig->MaxVersionLS)); + } + else + *matches = TRUE; + } + msi_free( buf); + } + else + rc = ERROR_OUTOFMEMORY; + } + } + return rc; +} + +/* Sets *matches to whether the file in findData matches that in sig. + * fullFilePath is assumed to be the full path of the file specified in + * findData, which may be necessary to compare the version. + * Return ERROR_SUCCESS in case of success (whether or not the file matches), + * something else if an install-halting error occurs. + */ +static UINT ACTION_FileMatchesSig(MSISIGNATURE *sig, + LPWIN32_FIND_DATAW findData, LPCWSTR fullFilePath, BOOL *matches) +{ + UINT rc = ERROR_SUCCESS; + + *matches = TRUE; + /* assumes the caller has already ensured the filenames match, so check + * the other fields.. + */ + if (sig->MinTime.dwLowDateTime || sig->MinTime.dwHighDateTime) + { + if (findData->ftCreationTime.dwHighDateTime < + sig->MinTime.dwHighDateTime || + (findData->ftCreationTime.dwHighDateTime == sig->MinTime.dwHighDateTime + && findData->ftCreationTime.dwLowDateTime < + sig->MinTime.dwLowDateTime)) + *matches = FALSE; + } + if (*matches && (sig->MaxTime.dwLowDateTime || sig->MaxTime.dwHighDateTime)) + { + if (findData->ftCreationTime.dwHighDateTime > + sig->MaxTime.dwHighDateTime || + (findData->ftCreationTime.dwHighDateTime == sig->MaxTime.dwHighDateTime + && findData->ftCreationTime.dwLowDateTime > + sig->MaxTime.dwLowDateTime)) + *matches = FALSE; + } + if (*matches && sig->MinSize && findData->nFileSizeLow < sig->MinSize) + *matches = FALSE; + if (*matches && sig->MaxSize && findData->nFileSizeLow > sig->MaxSize) + *matches = FALSE; + if (*matches && (sig->MinVersionMS || sig->MinVersionLS || + sig->MaxVersionMS || sig->MaxVersionLS)) + rc = ACTION_FileVersionMatches(sig, fullFilePath, matches); + return rc; +} + +/* Recursively searches the directory dir for files that match the signature + * sig, up to (depth + 1) levels deep. That is, if depth is 0, it searches dir + * (and only dir). If depth is 1, searches dir and its immediate + * subdirectories. + * Assumes sig->File is not NULL. + * Returns ERROR_SUCCESS on success (which may include non-critical errors), + * something else on failures which should halt the install. + */ +static UINT ACTION_RecurseSearchDirectory(MSIPACKAGE *package, BOOL *appFound, + MSISIGNATURE *sig, LPCWSTR dir, int depth) +{ + static const WCHAR starDotStarW[] = { '*','.','*',0 }; + UINT rc = ERROR_SUCCESS; + size_t dirLen = lstrlenW(dir), fileLen = lstrlenW(sig->File); + WCHAR *buf; + + TRACE("Searching directory %s for file %s, depth %d\n", debugstr_w(dir), + debugstr_w(sig->File), depth); + + if (depth < 0) + return ERROR_INVALID_PARAMETER; + + *appFound = FALSE; + /* We need the buffer in both paths below, so go ahead and allocate it + * here. Add two because we might need to add a backslash if the dir name + * isn't backslash-terminated. + */ + buf = msi_alloc( (dirLen + max(fileLen, lstrlenW(starDotStarW)) + 2) * sizeof(WCHAR)); + if (buf) + { + /* a depth of 0 implies we should search dir, so go ahead and search */ + HANDLE hFind; + WIN32_FIND_DATAW findData; + + memcpy(buf, dir, dirLen * sizeof(WCHAR)); + if (buf[dirLen - 1] != '\\') + buf[dirLen++ - 1] = '\\'; + memcpy(buf + dirLen, sig->File, (fileLen + 1) * sizeof(WCHAR)); + hFind = FindFirstFileW(buf, &findData); + if (hFind != INVALID_HANDLE_VALUE) + { + BOOL matches; + + /* assuming Signature can't contain wildcards for the file name, + * so don't bother with FindNextFileW here. + */ + if (!(rc = ACTION_FileMatchesSig(sig, &findData, buf, &matches)) + && matches) + { + TRACE("found file, setting %s to %s\n", + debugstr_w(sig->Property), debugstr_w(buf)); + rc = MSI_SetPropertyW(package, sig->Property, buf); + *appFound = TRUE; + } + FindClose(hFind); + } + if (rc == ERROR_SUCCESS && !*appFound && depth > 0) + { + HANDLE hFind; + WIN32_FIND_DATAW findData; + + memcpy(buf, dir, dirLen * sizeof(WCHAR)); + if (buf[dirLen - 1] != '\\') + buf[dirLen++ - 1] = '\\'; + lstrcpyW(buf + dirLen, starDotStarW); + hFind = FindFirstFileW(buf, &findData); + if (hFind != INVALID_HANDLE_VALUE) + { + if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + rc = ACTION_RecurseSearchDirectory(package, appFound, + sig, findData.cFileName, depth - 1); + while (rc == ERROR_SUCCESS && !*appFound && + FindNextFileW(hFind, &findData) != 0) + { + if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + rc = ACTION_RecurseSearchDirectory(package, appFound, + sig, findData.cFileName, depth - 1); + } + FindClose(hFind); + } + } + msi_free(buf); + } + else + rc = ERROR_OUTOFMEMORY; + + return rc; +} + +static UINT ACTION_CheckDirectory(MSIPACKAGE *package, MSISIGNATURE *sig, + LPCWSTR dir) +{ + UINT rc = ERROR_SUCCESS; + + if (GetFileAttributesW(dir) & FILE_ATTRIBUTE_DIRECTORY) + { + TRACE("directory exists, setting %s to %s\n", + debugstr_w(sig->Property), debugstr_w(dir)); + rc = MSI_SetPropertyW(package, sig->Property, dir); + } + return rc; +} + +static BOOL ACTION_IsFullPath(LPCWSTR path) +{ + WCHAR first = toupperW(path[0]); + BOOL ret; + + if (first >= 'A' && first <= 'Z' && path[1] == ':') + ret = TRUE; + else if (path[0] == '\\' && path[1] == '\\') + ret = TRUE; + else + ret = FALSE; + return ret; +} + +static UINT ACTION_SearchDirectory(MSIPACKAGE *package, MSISIGNATURE *sig, + LPCWSTR expanded, int depth) +{ + UINT rc; + BOOL found; + + TRACE("%p, %p, %s, %d\n", package, sig, debugstr_w(expanded), depth); + if (ACTION_IsFullPath(expanded)) + { + if (sig->File) + rc = ACTION_RecurseSearchDirectory(package, &found, sig, + expanded, depth); + else + { + /* Recursively searching a directory makes no sense when the + * directory to search is the thing you're trying to find. + */ + rc = ACTION_CheckDirectory(package, sig, expanded); + } + } + else + { + WCHAR pathWithDrive[MAX_PATH] = { 'C',':','\\',0 }; + DWORD drives = GetLogicalDrives(); + int i; + + rc = ERROR_SUCCESS; + found = FALSE; + for (i = 0; rc == ERROR_SUCCESS && !found && i < 26; i++) + if (drives & (1 << drives)) + { + pathWithDrive[0] = 'A' + i; + if (GetDriveTypeW(pathWithDrive) == DRIVE_FIXED) + { + lstrcpynW(pathWithDrive + 3, expanded, + sizeof(pathWithDrive) / sizeof(pathWithDrive[0]) - 3); + if (sig->File) + rc = ACTION_RecurseSearchDirectory(package, &found, sig, + pathWithDrive, depth); + else + rc = ACTION_CheckDirectory(package, sig, pathWithDrive); + } + } + } + TRACE("returning %d\n", rc); + return rc; +} + +static UINT ACTION_AppSearchDr(MSIPACKAGE *package, MSISIGNATURE *sig) +{ + MSIQUERY *view; + UINT rc; + static const WCHAR ExecSeqQuery[] = { + 's','e','l','e','c','t',' ','*',' ', + 'f','r','o','m',' ', + 'D','r','L','o','c','a','t','o','r',' ', + 'w','h','e','r','e',' ','S','i','g','n','a','t','u','r','e','_',' ','=',' ', + '\'','%','s','\'',0}; + + TRACE("(package %p, sig %p)\n", package, sig); + rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, sig->Name); + if (rc == ERROR_SUCCESS) + { + MSIRECORD *row = 0; + WCHAR buffer[MAX_PATH], expanded[MAX_PATH]; + DWORD sz; + int depth; + + rc = MSI_ViewExecute(view, 0); + if (rc != ERROR_SUCCESS) + { + TRACE("MSI_ViewExecute returned %d\n", rc); + goto end; + } + rc = MSI_ViewFetch(view,&row); + if (rc != ERROR_SUCCESS) + { + TRACE("MSI_ViewFetch returned %d\n", rc); + rc = ERROR_SUCCESS; + goto end; + } + + /* check whether parent is set */ + buffer[0] = 0; + sz=sizeof(buffer)/sizeof(buffer[0]); + rc = MSI_RecordGetStringW(row,2,buffer,&sz); + if (rc != ERROR_SUCCESS) + { + ERR("Error is %x\n",rc); + goto end; + } + else if (buffer[0]) + { + FIXME(": searching parent (%s) unimplemented\n", + debugstr_w(buffer)); + goto end; + } + /* no parent, now look for path */ + buffer[0] = 0; + sz=sizeof(buffer)/sizeof(buffer[0]); + rc = MSI_RecordGetStringW(row,3,buffer,&sz); + if (rc != ERROR_SUCCESS) + { + ERR("Error is %x\n",rc); + goto end; + } + if (MSI_RecordIsNull(row,4)) + depth = 0; + else + depth = MSI_RecordGetInteger(row,4); + ACTION_ExpandAnyPath(package, buffer, expanded, + sizeof(expanded) / sizeof(expanded[0])); + rc = ACTION_SearchDirectory(package, sig, expanded, depth); + +end: + msiobj_release(&row->hdr); + MSI_ViewClose(view); + msiobj_release(&view->hdr); + } + else + { + TRACE("MSI_OpenQuery returned %d\n", rc); + rc = ERROR_SUCCESS; + } + + + TRACE("returning %d\n", rc); + return rc; +} + +/* http://msdn.microsoft.com/library/en-us/msi/setup/appsearch_table.asp + * is the best reference for the AppSearch table and how it's used. + */ +UINT ACTION_AppSearch(MSIPACKAGE *package) +{ + MSIQUERY *view; + UINT rc; + static const WCHAR ExecSeqQuery[] = { + 's','e','l','e','c','t',' ','*',' ', + 'f','r','o','m',' ', + 'A','p','p','S','e','a','r','c','h',0}; + + rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery); + if (rc == ERROR_SUCCESS) + { + MSIRECORD *row = 0; + WCHAR propBuf[0x100], sigBuf[0x100]; + DWORD sz; + MSISIGNATURE sig; + BOOL appFound = FALSE; + + rc = MSI_ViewExecute(view, 0); + if (rc != ERROR_SUCCESS) + goto end; + + while (!rc) + { + rc = MSI_ViewFetch(view,&row); + if (rc != ERROR_SUCCESS) + { + rc = ERROR_SUCCESS; + break; + } + + /* get property and signature */ + propBuf[0] = 0; + sz=sizeof(propBuf)/sizeof(propBuf[0]); + rc = MSI_RecordGetStringW(row,1,propBuf,&sz); + if (rc != ERROR_SUCCESS) + { + ERR("Error is %x\n",rc); + msiobj_release(&row->hdr); + break; + } + sigBuf[0] = 0; + sz=sizeof(sigBuf)/sizeof(sigBuf[0]); + rc = MSI_RecordGetStringW(row,2,sigBuf,&sz); + if (rc != ERROR_SUCCESS) + { + ERR("Error is %x\n",rc); + msiobj_release(&row->hdr); + break; + } + TRACE("Searching for Property %s, Signature_ %s\n", + debugstr_w(propBuf), debugstr_w(sigBuf)); + /* This clears all the fields, so set Name and Property afterward */ + rc = ACTION_AppSearchGetSignature(package, &sig, sigBuf); + sig.Name = sigBuf; + sig.Property = propBuf; + if (rc == ERROR_SUCCESS) + { + rc = ACTION_AppSearchComponents(package, &appFound, &sig); + if (rc == ERROR_SUCCESS && !appFound) + { + rc = ACTION_AppSearchReg(package, &appFound, &sig); + if (rc == ERROR_SUCCESS && !appFound) + { + rc = ACTION_AppSearchIni(package, &appFound, &sig); + if (rc == ERROR_SUCCESS && !appFound) + rc = ACTION_AppSearchDr(package, &sig); + } + } + } + msi_free( sig.File); + msi_free( sig.Languages); + msiobj_release(&row->hdr); + } + +end: + MSI_ViewClose(view); + msiobj_release(&view->hdr); + } + else + rc = ERROR_SUCCESS; + + return rc; +} diff --git a/reactos/dll/msi/classes.c b/reactos/dll/msi/classes.c new file mode 100644 index 00000000000..b7799f38059 --- /dev/null +++ b/reactos/dll/msi/classes.c @@ -0,0 +1,1368 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2005 Aric Stewart 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* actions handled in this module + * RegisterClassInfo + * RegisterProgIdInfo + * RegisterExtensionInfo + * RegisterMIMEInfo + * UnRegisterClassInfo (TODO) + * UnRegisterProgIdInfo (TODO) + * UnRegisterExtensionInfo (TODO) + * UnRegisterMIMEInfo (TODO) + */ + +#include + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "winreg.h" +#include "wine/debug.h" +#include "msipriv.h" +#include "winuser.h" +#include "wine/unicode.h" +#include "action.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + + +extern const WCHAR szRegisterClassInfo[]; +extern const WCHAR szRegisterProgIdInfo[]; +extern const WCHAR szRegisterExtensionInfo[]; +extern const WCHAR szRegisterMIMEInfo[]; + +extern const WCHAR szUnregisterClassInfo[]; +extern const WCHAR szUnregisterExtensionInfo[]; +extern const WCHAR szUnregisterMIMEInfo[]; +extern const WCHAR szUnregisterProgIdInfo[]; + +static MSIAPPID *load_appid( MSIPACKAGE* package, MSIRECORD *row ) +{ + LPCWSTR buffer; + MSIAPPID *appid; + + /* fill in the data */ + + appid = msi_alloc_zero( sizeof(MSIAPPID) ); + if (!appid) + return NULL; + + appid->AppID = msi_dup_record_field( row, 1 ); + TRACE("loading appid %s\n", debugstr_w( appid->AppID )); + + buffer = MSI_RecordGetString(row,2); + deformat_string( package, buffer, &appid->RemoteServerName ); + + appid->LocalServer = msi_dup_record_field(row,3); + appid->ServiceParameters = msi_dup_record_field(row,4); + appid->DllSurrogate = msi_dup_record_field(row,5); + + appid->ActivateAtStorage = !MSI_RecordIsNull(row,6); + appid->RunAsInteractiveUser = !MSI_RecordIsNull(row,7); + + list_add_tail( &package->appids, &appid->entry ); + + return appid; +} + +static MSIAPPID *load_given_appid( MSIPACKAGE *package, LPCWSTR name ) +{ + MSIRECORD *row; + MSIAPPID *appid; + static const WCHAR ExecSeqQuery[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','A','p','p','I','d','`',' ','W','H','E','R','E',' ', + '`','A','p','p','I','d','`',' ','=',' ','\'','%','s','\'',0}; + + if (!name) + return NULL; + + /* check for appids already loaded */ + LIST_FOR_EACH_ENTRY( appid, &package->appids, MSIAPPID, entry ) + { + if (lstrcmpiW( appid->AppID, name )==0) + { + TRACE("found appid %s %p\n", debugstr_w(name), appid); + return appid; + } + } + + row = MSI_QueryGetRecord(package->db, ExecSeqQuery, name); + if (!row) + return NULL; + + appid = load_appid(package, row); + msiobj_release(&row->hdr); + + return appid; +} + +static MSIPROGID *load_given_progid(MSIPACKAGE *package, LPCWSTR progid); +static MSICLASS *load_given_class( MSIPACKAGE *package, LPCWSTR classid ); + +static MSIPROGID *load_progid( MSIPACKAGE* package, MSIRECORD *row ) +{ + MSIPROGID *progid; + LPCWSTR buffer; + + /* fill in the data */ + + progid = msi_alloc_zero( sizeof(MSIPROGID) ); + if (!progid) + return NULL; + + list_add_tail( &package->progids, &progid->entry ); + + progid->ProgID = msi_dup_record_field(row,1); + TRACE("loading progid %s\n",debugstr_w(progid->ProgID)); + + buffer = MSI_RecordGetString(row,2); + progid->Parent = load_given_progid(package,buffer); + if (progid->Parent == NULL && buffer) + FIXME("Unknown parent ProgID %s\n",debugstr_w(buffer)); + + buffer = MSI_RecordGetString(row,3); + progid->Class = load_given_class(package,buffer); + if (progid->Class == NULL && buffer) + FIXME("Unknown class %s\n",debugstr_w(buffer)); + + progid->Description = msi_dup_record_field(row,4); + + if (!MSI_RecordIsNull(row,6)) + { + INT icon_index = MSI_RecordGetInteger(row,6); + LPCWSTR FileName = MSI_RecordGetString(row,5); + LPWSTR FilePath; + static const WCHAR fmt[] = {'%','s',',','%','i',0}; + + FilePath = build_icon_path(package,FileName); + + progid->IconPath = msi_alloc( (strlenW(FilePath)+10)* sizeof(WCHAR) ); + + sprintfW(progid->IconPath,fmt,FilePath,icon_index); + + msi_free(FilePath); + } + else + { + buffer = MSI_RecordGetString(row,5); + if (buffer) + progid->IconPath = build_icon_path(package,buffer); + } + + progid->CurVer = NULL; + progid->VersionInd = NULL; + + /* if we have a parent then we may be that parents CurVer */ + if (progid->Parent && progid->Parent != progid) + { + MSIPROGID *parent = progid->Parent; + + while (parent->Parent && parent->Parent != parent) + parent = parent->Parent; + + /* FIXME: need to determing if we are really the CurVer */ + + progid->CurVer = parent; + parent->VersionInd = progid; + } + + return progid; +} + +static MSIPROGID *load_given_progid(MSIPACKAGE *package, LPCWSTR name) +{ + MSIPROGID *progid; + MSIRECORD *row; + static const WCHAR ExecSeqQuery[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','P','r','o','g','I','d','`',' ','W','H','E','R','E',' ', + '`','P','r','o','g','I','d','`',' ','=',' ','\'','%','s','\'',0}; + + if (!name) + return NULL; + + /* check for progids already loaded */ + LIST_FOR_EACH_ENTRY( progid, &package->progids, MSIPROGID, entry ) + { + if (strcmpiW( progid->ProgID,name )==0) + { + TRACE("found progid %s (%p)\n",debugstr_w(name), progid ); + return progid; + } + } + + row = MSI_QueryGetRecord( package->db, ExecSeqQuery, name ); + if (!row) + return NULL; + + progid = load_progid(package, row); + msiobj_release(&row->hdr); + + return progid; +} + +static MSICLASS *load_class( MSIPACKAGE* package, MSIRECORD *row ) +{ + MSICLASS *cls; + DWORD i; + LPCWSTR buffer; + + /* fill in the data */ + + cls = msi_alloc_zero( sizeof(MSICLASS) ); + if (!cls) + return NULL; + + list_add_tail( &package->classes, &cls->entry ); + + cls->clsid = msi_dup_record_field( row, 1 ); + TRACE("loading class %s\n",debugstr_w(cls->clsid)); + cls->Context = msi_dup_record_field( row, 2 ); + buffer = MSI_RecordGetString(row,3); + cls->Component = get_loaded_component(package, buffer); + + cls->ProgIDText = msi_dup_record_field(row,4); + cls->ProgID = load_given_progid(package, cls->ProgIDText); + + cls->Description = msi_dup_record_field(row,5); + + buffer = MSI_RecordGetString(row,6); + if (buffer) + cls->AppID = load_given_appid(package, buffer); + + cls->FileTypeMask = msi_dup_record_field(row,7); + + if (!MSI_RecordIsNull(row,9)) + { + + INT icon_index = MSI_RecordGetInteger(row,9); + LPCWSTR FileName = MSI_RecordGetString(row,8); + LPWSTR FilePath; + static const WCHAR fmt[] = {'%','s',',','%','i',0}; + + FilePath = build_icon_path(package,FileName); + + cls->IconPath = msi_alloc( (strlenW(FilePath)+5)* sizeof(WCHAR) ); + + sprintfW(cls->IconPath,fmt,FilePath,icon_index); + + msi_free(FilePath); + } + else + { + buffer = MSI_RecordGetString(row,8); + if (buffer) + cls->IconPath = build_icon_path(package,buffer); + } + + if (!MSI_RecordIsNull(row,10)) + { + i = MSI_RecordGetInteger(row,10); + if (i != MSI_NULL_INTEGER && i > 0 && i < 4) + { + static const WCHAR ole2[] = {'o','l','e','2','.','d','l','l',0}; + static const WCHAR ole32[] = {'o','l','e','3','2','.','d','l','l',0}; + + switch(i) + { + case 1: + cls->DefInprocHandler = strdupW(ole2); + break; + case 2: + cls->DefInprocHandler32 = strdupW(ole32); + break; + case 3: + cls->DefInprocHandler = strdupW(ole2); + cls->DefInprocHandler32 = strdupW(ole32); + break; + } + } + else + { + cls->DefInprocHandler32 = msi_dup_record_field( row, 10); + reduce_to_longfilename(cls->DefInprocHandler32); + } + } + buffer = MSI_RecordGetString(row,11); + deformat_string(package,buffer,&cls->Argument); + + buffer = MSI_RecordGetString(row,12); + cls->Feature = get_loaded_feature(package,buffer); + + cls->Attributes = MSI_RecordGetInteger(row,13); + + return cls; +} + +/* + * the Class table has 3 primary keys. Generally it is only + * referenced through the first CLSID key. However when loading + * all of the classes we need to make sure we do not ignore rows + * with other Context and ComponentIndexs + */ +static MSICLASS *load_given_class(MSIPACKAGE *package, LPCWSTR classid) +{ + MSICLASS *cls; + MSIRECORD *row; + static const WCHAR ExecSeqQuery[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','C','l','a','s','s','`',' ','W','H','E','R','E',' ', + '`','C','L','S','I','D','`',' ','=',' ','\'','%','s','\'',0}; + + if (!classid) + return NULL; + + /* check for classes already loaded */ + LIST_FOR_EACH_ENTRY( cls, &package->classes, MSICLASS, entry ) + { + if (lstrcmpiW( cls->clsid, classid )==0) + { + TRACE("found class %s (%p)\n",debugstr_w(classid), cls); + return cls; + } + } + + row = MSI_QueryGetRecord(package->db, ExecSeqQuery, classid); + if (!row) + return NULL; + + cls = load_class(package, row); + msiobj_release(&row->hdr); + + return cls; +} + +static MSIEXTENSION *load_given_extension( MSIPACKAGE *package, LPCWSTR extension ); + +static MSIMIME *load_mime( MSIPACKAGE* package, MSIRECORD *row ) +{ + LPCWSTR buffer; + MSIMIME *mt; + + /* fill in the data */ + + mt = msi_alloc_zero( sizeof(MSIMIME) ); + if (!mt) + return mt; + + mt->ContentType = msi_dup_record_field( row, 1 ); + TRACE("loading mime %s\n", debugstr_w(mt->ContentType)); + + buffer = MSI_RecordGetString( row, 2 ); + mt->Extension = load_given_extension( package, buffer ); + + mt->clsid = msi_dup_record_field( row, 3 ); + mt->Class = load_given_class( package, mt->clsid ); + + list_add_tail( &package->mimes, &mt->entry ); + + return mt; +} + +static MSIMIME *load_given_mime( MSIPACKAGE *package, LPCWSTR mime ) +{ + MSIRECORD *row; + static const WCHAR ExecSeqQuery[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','M','I','M','E','`',' ','W','H','E','R','E',' ', + '`','C','o','n','t','e','n','t','T','y','p','e','`',' ','=',' ', + '\'','%','s','\'',0}; + MSIMIME *mt; + + if (!mime) + return NULL; + + /* check for mime already loaded */ + LIST_FOR_EACH_ENTRY( mt, &package->mimes, MSIMIME, entry ) + { + if (strcmpiW(mt->ContentType,mime)==0) + { + TRACE("found mime %s (%p)\n",debugstr_w(mime), mt); + return mt; + } + } + + row = MSI_QueryGetRecord(package->db, ExecSeqQuery, mime); + if (!row) + return NULL; + + mt = load_mime(package, row); + msiobj_release(&row->hdr); + + return mt; +} + +static MSIEXTENSION *load_extension( MSIPACKAGE* package, MSIRECORD *row ) +{ + MSIEXTENSION *ext; + LPCWSTR buffer; + + /* fill in the data */ + + ext = msi_alloc_zero( sizeof(MSIEXTENSION) ); + if (!ext) + return NULL; + + list_init( &ext->verbs ); + + list_add_tail( &package->extensions, &ext->entry ); + + ext->Extension = msi_dup_record_field( row, 1 ); + TRACE("loading extension %s\n", debugstr_w(ext->Extension)); + + buffer = MSI_RecordGetString( row, 2 ); + ext->Component = get_loaded_component( package,buffer ); + + ext->ProgIDText = msi_dup_record_field( row, 3 ); + ext->ProgID = load_given_progid( package, ext->ProgIDText ); + + buffer = MSI_RecordGetString( row, 4 ); + ext->Mime = load_given_mime( package, buffer ); + + buffer = MSI_RecordGetString(row,5); + ext->Feature = get_loaded_feature( package, buffer ); + + return ext; +} + +/* + * While the extension table has 2 primary keys, this function is only looking + * at the Extension key which is what is referenced as a forign key + */ +static MSIEXTENSION *load_given_extension( MSIPACKAGE *package, LPCWSTR name ) +{ + MSIRECORD *row; + MSIEXTENSION *ext; + static const WCHAR ExecSeqQuery[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','E','x','t','e','n','s','i','o','n','`',' ', + 'W','H','E','R','E',' ', + '`','E','x','t','e','n','s','i','o','n','`',' ','=',' ', + '\'','%','s','\'',0}; + + if (!name) + return NULL; + + /* check for extensions already loaded */ + LIST_FOR_EACH_ENTRY( ext, &package->extensions, MSIEXTENSION, entry ) + { + if (strcmpiW( ext->Extension, name )==0) + { + TRACE("extension %s already loaded %p\n", debugstr_w(name), ext); + return ext; + } + } + + row = MSI_QueryGetRecord( package->db, ExecSeqQuery, name ); + if (!row) + return NULL; + + ext = load_extension(package, row); + msiobj_release(&row->hdr); + + return ext; +} + +static UINT iterate_load_verb(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE* package = (MSIPACKAGE*)param; + MSIVERB *verb; + LPCWSTR buffer; + MSIEXTENSION *extension; + + buffer = MSI_RecordGetString(row,1); + extension = load_given_extension( package, buffer ); + if (!extension) + { + ERR("Verb unable to find loaded extension %s\n", debugstr_w(buffer)); + return ERROR_SUCCESS; + } + + /* fill in the data */ + + verb = msi_alloc_zero( sizeof(MSIVERB) ); + if (!verb) + return ERROR_OUTOFMEMORY; + + verb->Verb = msi_dup_record_field(row,2); + TRACE("loading verb %s\n",debugstr_w(verb->Verb)); + verb->Sequence = MSI_RecordGetInteger(row,3); + + buffer = MSI_RecordGetString(row,4); + deformat_string(package,buffer,&verb->Command); + + buffer = MSI_RecordGetString(row,5); + deformat_string(package,buffer,&verb->Argument); + + /* assosiate the verb with the correct extension */ + list_add_tail( &extension->verbs, &verb->entry ); + + return ERROR_SUCCESS; +} + +static UINT iterate_all_classes(MSIRECORD *rec, LPVOID param) +{ + MSICOMPONENT *comp; + LPCWSTR clsid; + LPCWSTR context; + LPCWSTR buffer; + MSIPACKAGE* package =(MSIPACKAGE*)param; + MSICLASS *cls; + BOOL match = FALSE; + + clsid = MSI_RecordGetString(rec,1); + context = MSI_RecordGetString(rec,2); + buffer = MSI_RecordGetString(rec,3); + comp = get_loaded_component(package,buffer); + + LIST_FOR_EACH_ENTRY( cls, &package->classes, MSICLASS, entry ) + { + if (strcmpiW( clsid, cls->clsid )) + continue; + if (strcmpW( context, cls->Context )) + continue; + if (comp == cls->Component) + { + match = TRUE; + break; + } + } + + if (!match) + load_class(package, rec); + + return ERROR_SUCCESS; +} + +static VOID load_all_classes(MSIPACKAGE *package) +{ + UINT rc = ERROR_SUCCESS; + MSIQUERY *view; + + static const WCHAR ExecSeqQuery[] = + {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ', + '`','C','l','a','s','s','`',0}; + + rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view); + if (rc != ERROR_SUCCESS) + return; + + rc = MSI_IterateRecords(view, NULL, iterate_all_classes, package); + msiobj_release(&view->hdr); +} + +static UINT iterate_all_extensions(MSIRECORD *rec, LPVOID param) +{ + MSICOMPONENT *comp; + LPCWSTR buffer; + LPCWSTR extension; + MSIPACKAGE* package =(MSIPACKAGE*)param; + BOOL match = FALSE; + MSIEXTENSION *ext; + + extension = MSI_RecordGetString(rec,1); + buffer = MSI_RecordGetString(rec,2); + comp = get_loaded_component(package,buffer); + + LIST_FOR_EACH_ENTRY( ext, &package->extensions, MSIEXTENSION, entry ) + { + if (strcmpiW(extension,ext->Extension)) + continue; + if (comp == ext->Component) + { + match = TRUE; + break; + } + } + + if (!match) + load_extension(package, rec); + + return ERROR_SUCCESS; +} + +static VOID load_all_extensions(MSIPACKAGE *package) +{ + UINT rc = ERROR_SUCCESS; + MSIQUERY *view; + + static const WCHAR ExecSeqQuery[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','E','x','t','e','n','s','i','o','n','`',0}; + + rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view); + if (rc != ERROR_SUCCESS) + return; + + rc = MSI_IterateRecords(view, NULL, iterate_all_extensions, package); + msiobj_release(&view->hdr); +} + +static UINT iterate_all_progids(MSIRECORD *rec, LPVOID param) +{ + LPCWSTR buffer; + MSIPACKAGE* package =(MSIPACKAGE*)param; + + buffer = MSI_RecordGetString(rec,1); + load_given_progid(package,buffer); + return ERROR_SUCCESS; +} + +static VOID load_all_progids(MSIPACKAGE *package) +{ + UINT rc = ERROR_SUCCESS; + MSIQUERY *view; + + static const WCHAR ExecSeqQuery[] = + {'S','E','L','E','C','T',' ','`','P','r','o','g','I','d','`',' ', + 'F','R','O','M',' ', '`','P','r','o','g','I','d','`',0}; + + rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view); + if (rc != ERROR_SUCCESS) + return; + + rc = MSI_IterateRecords(view, NULL, iterate_all_progids, package); + msiobj_release(&view->hdr); +} + +static VOID load_all_verbs(MSIPACKAGE *package) +{ + UINT rc = ERROR_SUCCESS; + MSIQUERY *view; + + static const WCHAR ExecSeqQuery[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','V','e','r','b','`',0}; + + rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view); + if (rc != ERROR_SUCCESS) + return; + + rc = MSI_IterateRecords(view, NULL, iterate_load_verb, package); + msiobj_release(&view->hdr); +} + +static UINT iterate_all_mimes(MSIRECORD *rec, LPVOID param) +{ + LPCWSTR buffer; + MSIPACKAGE* package =(MSIPACKAGE*)param; + + buffer = MSI_RecordGetString(rec,1); + load_given_mime(package,buffer); + return ERROR_SUCCESS; +} + +static VOID load_all_mimes(MSIPACKAGE *package) +{ + UINT rc = ERROR_SUCCESS; + MSIQUERY *view; + + static const WCHAR ExecSeqQuery[] = + {'S','E','L','E','C','T',' ', + '`','C','o','n','t','e','n','t','T','y','p','e','`', + ' ','F','R','O','M',' ', + '`','M','I','M','E','`',0}; + + rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view); + if (rc != ERROR_SUCCESS) + return; + + rc = MSI_IterateRecords(view, NULL, iterate_all_mimes, package); + msiobj_release(&view->hdr); +} + +static void load_classes_and_such(MSIPACKAGE *package) +{ + TRACE("Loading all the class info and related tables\n"); + + /* check if already loaded */ + if (!list_empty( &package->classes ) || + !list_empty( &package->mimes ) || + !list_empty( &package->extensions ) || + !list_empty( &package->progids ) ) + return; + + load_all_classes(package); + load_all_extensions(package); + load_all_progids(package); + /* these loads must come after the other loads */ + load_all_verbs(package); + load_all_mimes(package); +} + +static void mark_progid_for_install( MSIPACKAGE* package, MSIPROGID *progid ) +{ + MSIPROGID *child; + + if (!progid) + return; + + if (progid->InstallMe == TRUE) + return; + + progid->InstallMe = TRUE; + + /* all children if this is a parent also install */ + LIST_FOR_EACH_ENTRY( child, &package->progids, MSIPROGID, entry ) + { + if (child->Parent == progid) + mark_progid_for_install( package, child ); + } +} + +static void mark_mime_for_install( MSIMIME *mime ) +{ + if (!mime) + return; + mime->InstallMe = TRUE; +} + +LONG msi_reg_set_val_str( HKEY hkey, LPCWSTR name, LPCWSTR value ) +{ + DWORD len = value ? (lstrlenW(value) + 1) * sizeof (WCHAR) : 0; + return RegSetValueExW( hkey, name, 0, REG_SZ, (LPBYTE)value, len ); +} + +LONG msi_reg_set_val_multi_str( HKEY hkey, LPCWSTR name, LPCWSTR value ) +{ + LPCWSTR p = value; + while (*p) p += lstrlenW(p) + 1; + return RegSetValueExW( hkey, name, 0, REG_MULTI_SZ, + (LPBYTE)value, (p + 1 - value) * sizeof(WCHAR) ); +} + +LONG msi_reg_set_val_dword( HKEY hkey, LPCWSTR name, DWORD val ) +{ + return RegSetValueExW( hkey, name, 0, REG_DWORD, (LPBYTE)&val, sizeof (DWORD) ); +} + +LONG msi_reg_set_subkey_val( HKEY hkey, LPCWSTR path, LPCWSTR name, LPCWSTR val ) +{ + HKEY hsubkey = 0; + LONG r; + + r = RegCreateKeyW( hkey, path, &hsubkey ); + if (r != ERROR_SUCCESS) + return r; + r = msi_reg_set_val_str( hsubkey, name, val ); + RegCloseKey( hsubkey ); + return r; +} + +static UINT register_appid(MSIAPPID *appid, LPCWSTR app ) +{ + static const WCHAR szAppID[] = { 'A','p','p','I','D',0 }; + static const WCHAR szRemoteServerName[] = + {'R','e','m','o','t','e','S','e','r','v','e','r','N','a','m','e',0}; + static const WCHAR szLocalService[] = + {'L','o','c','a','l','S','e','r','v','i','c','e',0}; + static const WCHAR szService[] = + {'S','e','r','v','i','c','e','P','a','r','a','m','e','t','e','r','s',0}; + static const WCHAR szDLL[] = + {'D','l','l','S','u','r','r','o','g','a','t','e',0}; + static const WCHAR szActivate[] = + {'A','c','t','i','v','a','t','e','A','s','S','t','o','r','a','g','e',0}; + static const WCHAR szY[] = {'Y',0}; + static const WCHAR szRunAs[] = {'R','u','n','A','s',0}; + static const WCHAR szUser[] = + {'I','n','t','e','r','a','c','t','i','v','e',' ','U','s','e','r',0}; + + HKEY hkey2,hkey3; + + RegCreateKeyW(HKEY_CLASSES_ROOT,szAppID,&hkey2); + RegCreateKeyW( hkey2, appid->AppID, &hkey3 ); + RegCloseKey(hkey2); + msi_reg_set_val_str( hkey3, NULL, app ); + + if (appid->RemoteServerName) + msi_reg_set_val_str( hkey3, szRemoteServerName, appid->RemoteServerName ); + + if (appid->LocalServer) + msi_reg_set_val_str( hkey3, szLocalService, appid->LocalServer ); + + if (appid->ServiceParameters) + msi_reg_set_val_str( hkey3, szService, appid->ServiceParameters ); + + if (appid->DllSurrogate) + msi_reg_set_val_str( hkey3, szDLL, appid->DllSurrogate ); + + if (appid->ActivateAtStorage) + msi_reg_set_val_str( hkey3, szActivate, szY ); + + if (appid->RunAsInteractiveUser) + msi_reg_set_val_str( hkey3, szRunAs, szUser ); + + RegCloseKey(hkey3); + return ERROR_SUCCESS; +} + +UINT ACTION_RegisterClassInfo(MSIPACKAGE *package) +{ + /* + * Again I am assuming the words, "Whose key file represents" when referring + * to a Component as to meaning that Components KeyPath file + */ + + UINT rc; + MSIRECORD *uirow; + static const WCHAR szCLSID[] = { 'C','L','S','I','D',0 }; + static const WCHAR szProgID[] = { 'P','r','o','g','I','D',0 }; + static const WCHAR szVIProgID[] = { 'V','e','r','s','i','o','n','I','n','d','e','p','e','n','d','e','n','t','P','r','o','g','I','D',0 }; + static const WCHAR szAppID[] = { 'A','p','p','I','D',0 }; + static const WCHAR szSpace[] = {' ',0}; + static const WCHAR szInprocServer32[] = {'I','n','p','r','o','c','S','e','r','v','e','r','3','2',0}; + static const WCHAR szFileType_fmt[] = {'F','i','l','e','T','y','p','e','\\','%','s','\\','%','i',0}; + HKEY hkey,hkey2,hkey3; + BOOL install_on_demand = FALSE; + MSICLASS *cls; + + load_classes_and_such(package); + rc = RegCreateKeyW(HKEY_CLASSES_ROOT,szCLSID,&hkey); + if (rc != ERROR_SUCCESS) + return ERROR_FUNCTION_FAILED; + + /* install_on_demand should be set if OLE supports install on demand OLE + * servers. For now i am defaulting to FALSE because i do not know how to + * check, and i am told our builtin OLE does not support it + */ + + LIST_FOR_EACH_ENTRY( cls, &package->classes, MSICLASS, entry ) + { + MSICOMPONENT *comp; + MSIFILE *file; + DWORD size, sz; + LPWSTR argument; + MSIFEATURE *feature; + + comp = cls->Component; + if ( !comp ) + continue; + + feature = cls->Feature; + + /* + * yes. MSDN says that these are based on _Feature_ not on + * Component. So verify the feature is to be installed + */ + if ((!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL )) && + !(install_on_demand && + ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))) + { + TRACE("Skipping class %s reg due to disabled feature %s\n", + debugstr_w(cls->clsid), + debugstr_w(feature->Feature)); + + continue; + } + + TRACE("Registering class %s (%p)\n", debugstr_w(cls->clsid), cls); + + cls->Installed = TRUE; + mark_progid_for_install( package, cls->ProgID ); + + RegCreateKeyW( hkey, cls->clsid, &hkey2 ); + + if (cls->Description) + msi_reg_set_val_str( hkey2, NULL, cls->Description ); + + RegCreateKeyW( hkey2, cls->Context, &hkey3 ); + file = get_loaded_file( package, comp->KeyPath ); + + + /* the context server is a short path name + * except for if it is InprocServer32... + */ + if (strcmpiW( cls->Context, szInprocServer32 )!=0) + { + sz = GetShortPathNameW( file->TargetPath, NULL, 0 ); + if (sz == 0) + { + ERR("Unable to find short path for CLSID COM Server\n"); + argument = NULL; + } + else + { + size = sz * sizeof(WCHAR); + + if (cls->Argument) + { + size += strlenW(cls->Argument) * sizeof(WCHAR); + size += sizeof(WCHAR); + } + + argument = msi_alloc( size + sizeof(WCHAR)); + GetShortPathNameW( file->TargetPath, argument, sz ); + + if (cls->Argument) + { + strcatW(argument,szSpace); + strcatW( argument, cls->Argument ); + } + } + } + else + { + size = lstrlenW( file->TargetPath ) * sizeof(WCHAR); + + if (cls->Argument) + { + size += strlenW(cls->Argument) * sizeof(WCHAR); + size += sizeof(WCHAR); + } + + argument = msi_alloc( size + sizeof(WCHAR)); + strcpyW( argument, file->TargetPath ); + + if (cls->Argument) + { + strcatW(argument,szSpace); + strcatW( argument, cls->Argument ); + } + } + + if (argument) + { + msi_reg_set_val_str( hkey3, NULL, argument ); + msi_free(argument); + } + + RegCloseKey(hkey3); + + if (cls->ProgID || cls->ProgIDText) + { + LPCWSTR progid; + + if (cls->ProgID) + progid = cls->ProgID->ProgID; + else + progid = cls->ProgIDText; + + msi_reg_set_subkey_val( hkey2, szProgID, NULL, progid ); + + if (cls->ProgID && cls->ProgID->VersionInd) + { + msi_reg_set_subkey_val( hkey2, szVIProgID, NULL, + cls->ProgID->VersionInd->ProgID ); + } + } + + if (cls->AppID) + { + MSIAPPID *appid = cls->AppID; + + msi_reg_set_val_str( hkey2, szAppID, appid->AppID ); + + register_appid( appid, cls->Description ); + } + + if (cls->IconPath) + { + static const WCHAR szDefaultIcon[] = + {'D','e','f','a','u','l','t','I','c','o','n',0}; + + msi_reg_set_subkey_val( hkey2, szDefaultIcon, NULL, cls->IconPath ); + } + + if (cls->DefInprocHandler) + { + static const WCHAR szInproc[] = + {'I','n','p','r','o','c','H','a','n','d','l','e','r',0}; + + msi_reg_set_subkey_val( hkey2, szInproc, NULL, cls->DefInprocHandler ); + } + + if (cls->DefInprocHandler32) + { + static const WCHAR szInproc32[] = + {'I','n','p','r','o','c','H','a','n','d','l','e','r','3','2',0}; + + msi_reg_set_subkey_val( hkey2, szInproc32, NULL, cls->DefInprocHandler32 ); + } + + RegCloseKey(hkey2); + + /* if there is a FileTypeMask, register the FileType */ + if (cls->FileTypeMask) + { + LPWSTR ptr, ptr2; + LPWSTR keyname; + INT index = 0; + ptr = cls->FileTypeMask; + while (ptr && *ptr) + { + ptr2 = strchrW(ptr,';'); + if (ptr2) + *ptr2 = 0; + keyname = msi_alloc( (strlenW(szFileType_fmt) + strlenW(cls->clsid) + 4) * sizeof(WCHAR)); + sprintfW( keyname, szFileType_fmt, cls->clsid, index ); + + msi_reg_set_subkey_val( HKEY_CLASSES_ROOT, keyname, NULL, ptr ); + msi_free(keyname); + + if (ptr2) + ptr = ptr2+1; + else + ptr = NULL; + + index ++; + } + } + + uirow = MSI_CreateRecord(1); + + MSI_RecordSetStringW( uirow, 1, cls->clsid ); + ui_actiondata(package,szRegisterClassInfo,uirow); + msiobj_release(&uirow->hdr); + } + + RegCloseKey(hkey); + return rc; +} + +static LPCWSTR get_clsid_of_progid( MSIPROGID *progid ) +{ + while (progid) + { + if (progid->Class) + return progid->Class->clsid; + progid = progid->Parent; + } + return NULL; +} + +static UINT register_progid( MSIPROGID* progid ) +{ + static const WCHAR szCLSID[] = { 'C','L','S','I','D',0 }; + static const WCHAR szDefaultIcon[] = + {'D','e','f','a','u','l','t','I','c','o','n',0}; + static const WCHAR szCurVer[] = + {'C','u','r','V','e','r',0}; + HKEY hkey = 0; + UINT rc; + + rc = RegCreateKeyW( HKEY_CLASSES_ROOT, progid->ProgID, &hkey ); + if (rc == ERROR_SUCCESS) + { + LPCWSTR clsid = get_clsid_of_progid( progid ); + + if (clsid) + msi_reg_set_subkey_val( hkey, szCLSID, NULL, clsid ); + else + ERR("%s has no class\n", debugstr_w( progid->ProgID ) ); + + if (progid->Description) + msi_reg_set_val_str( hkey, NULL, progid->Description ); + + if (progid->IconPath) + msi_reg_set_subkey_val( hkey, szDefaultIcon, NULL, progid->IconPath ); + + /* write out the current version */ + if (progid->CurVer) + msi_reg_set_subkey_val( hkey, szCurVer, NULL, progid->CurVer->ProgID ); + + RegCloseKey(hkey); + } + else + ERR("failed to create key %s\n", debugstr_w( progid->ProgID ) ); + + return rc; +} + +UINT ACTION_RegisterProgIdInfo(MSIPACKAGE *package) +{ + MSIPROGID *progid; + MSIRECORD *uirow; + + load_classes_and_such(package); + + LIST_FOR_EACH_ENTRY( progid, &package->progids, MSIPROGID, entry ) + { + /* check if this progid is to be installed */ + if (progid->Class && progid->Class->Installed) + progid->InstallMe = TRUE; + + if (!progid->InstallMe) + { + TRACE("progid %s not scheduled to be installed\n", + debugstr_w(progid->ProgID)); + continue; + } + + TRACE("Registering progid %s\n", debugstr_w(progid->ProgID)); + + register_progid( progid ); + + uirow = MSI_CreateRecord( 1 ); + MSI_RecordSetStringW( uirow, 1, progid->ProgID ); + ui_actiondata( package, szRegisterProgIdInfo, uirow ); + msiobj_release( &uirow->hdr ); + } + + return ERROR_SUCCESS; +} + +static UINT register_verb(MSIPACKAGE *package, LPCWSTR progid, + MSICOMPONENT* component, MSIEXTENSION* extension, + MSIVERB* verb, INT* Sequence ) +{ + LPWSTR keyname; + HKEY key; + static const WCHAR szShell[] = {'s','h','e','l','l',0}; + static const WCHAR szCommand[] = {'c','o','m','m','a','n','d',0}; + static const WCHAR fmt[] = {'\"','%','s','\"',' ','%','s',0}; + static const WCHAR fmt2[] = {'\"','%','s','\"',0}; + LPWSTR command; + DWORD size; + LPWSTR advertise; + + keyname = build_directory_name(4, progid, szShell, verb->Verb, szCommand); + + TRACE("Making Key %s\n",debugstr_w(keyname)); + RegCreateKeyW(HKEY_CLASSES_ROOT, keyname, &key); + size = strlenW(component->FullKeypath); + if (verb->Argument) + size += strlenW(verb->Argument); + size += 4; + + command = msi_alloc(size * sizeof (WCHAR)); + if (verb->Argument) + sprintfW(command, fmt, component->FullKeypath, verb->Argument); + else + sprintfW(command, fmt2, component->FullKeypath); + + msi_reg_set_val_str( key, NULL, command ); + msi_free(command); + + advertise = create_component_advertise_string(package, component, + extension->Feature->Feature); + + size = strlenW(advertise); + + if (verb->Argument) + size += strlenW(verb->Argument); + size += 4; + + command = msi_alloc(size * sizeof (WCHAR)); + memset(command,0,size*sizeof(WCHAR)); + + strcpyW(command,advertise); + if (verb->Argument) + { + static const WCHAR szSpace[] = {' ',0}; + strcatW(command,szSpace); + strcatW(command,verb->Argument); + } + + msi_reg_set_val_multi_str( key, szCommand, command ); + + RegCloseKey(key); + msi_free(keyname); + msi_free(advertise); + msi_free(command); + + if (verb->Command) + { + keyname = build_directory_name(3, progid, szShell, verb->Verb); + msi_reg_set_subkey_val( HKEY_CLASSES_ROOT, keyname, NULL, verb->Command ); + msi_free(keyname); + } + + if (verb->Sequence != MSI_NULL_INTEGER) + { + if (*Sequence == MSI_NULL_INTEGER || verb->Sequence < *Sequence) + { + *Sequence = verb->Sequence; + keyname = build_directory_name(2, progid, szShell); + msi_reg_set_subkey_val( HKEY_CLASSES_ROOT, keyname, NULL, verb->Verb ); + msi_free(keyname); + } + } + return ERROR_SUCCESS; +} + +UINT ACTION_RegisterExtensionInfo(MSIPACKAGE *package) +{ + static const WCHAR szContentType[] = + {'C','o','n','t','e','n','t',' ','T','y','p','e',0 }; + HKEY hkey; + MSIEXTENSION *ext; + MSIRECORD *uirow; + BOOL install_on_demand = TRUE; + + load_classes_and_such(package); + + /* We need to set install_on_demand based on if the shell handles advertised + * shortcuts and the like. Because Mike McCormack is working on this i am + * going to default to TRUE + */ + + LIST_FOR_EACH_ENTRY( ext, &package->extensions, MSIEXTENSION, entry ) + { + LPWSTR extension; + MSIFEATURE *feature; + + if (!ext->Component) + continue; + + feature = ext->Feature; + + /* + * yes. MSDN says that these are based on _Feature_ not on + * Component. So verify the feature is to be installed + */ + if ((!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL )) && + !(install_on_demand && + ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))) + { + TRACE("Skipping extension %s reg due to disabled feature %s\n", + debugstr_w(ext->Extension), debugstr_w(feature->Feature)); + + continue; + } + + TRACE("Registering extension %s (%p)\n", debugstr_w(ext->Extension), ext); + + ext->Installed = TRUE; + + /* this is only registered if the extension has at least 1 verb + * according to MSDN + */ + if (ext->ProgID && !list_empty( &ext->verbs ) ) + mark_progid_for_install( package, ext->ProgID ); + + mark_mime_for_install(ext->Mime); + + extension = msi_alloc( (lstrlenW( ext->Extension ) + 2)*sizeof(WCHAR) ); + extension[0] = '.'; + lstrcpyW(extension+1,ext->Extension); + + RegCreateKeyW(HKEY_CLASSES_ROOT,extension,&hkey); + msi_free( extension ); + + if (ext->Mime) + msi_reg_set_val_str( hkey, szContentType, ext->Mime->ContentType ); + + if (ext->ProgID || ext->ProgIDText) + { + static const WCHAR szSN[] = + {'\\','S','h','e','l','l','N','e','w',0}; + HKEY hkey2; + LPWSTR newkey; + LPCWSTR progid; + MSIVERB *verb; + INT Sequence = MSI_NULL_INTEGER; + + if (ext->ProgID) + progid = ext->ProgID->ProgID; + else + progid = ext->ProgIDText; + + msi_reg_set_val_str( hkey, NULL, progid ); + + newkey = msi_alloc( (strlenW(progid)+strlenW(szSN)+1) * sizeof(WCHAR)); + + strcpyW(newkey,progid); + strcatW(newkey,szSN); + RegCreateKeyW(hkey,newkey,&hkey2); + RegCloseKey(hkey2); + + msi_free(newkey); + + /* do all the verbs */ + LIST_FOR_EACH_ENTRY( verb, &ext->verbs, MSIVERB, entry ) + { + register_verb( package, progid, ext->Component, + ext, verb, &Sequence); + } + } + + RegCloseKey(hkey); + + uirow = MSI_CreateRecord(1); + MSI_RecordSetStringW( uirow, 1, ext->Extension ); + ui_actiondata(package,szRegisterExtensionInfo,uirow); + msiobj_release(&uirow->hdr); + } + + return ERROR_SUCCESS; +} + +UINT ACTION_RegisterMIMEInfo(MSIPACKAGE *package) +{ + static const WCHAR szExten[] = + {'E','x','t','e','n','s','i','o','n',0 }; + MSIRECORD *uirow; + MSIMIME *mt; + + load_classes_and_such(package); + + LIST_FOR_EACH_ENTRY( mt, &package->mimes, MSIMIME, entry ) + { + LPWSTR extension; + LPCWSTR exten; + LPCWSTR mime; + static const WCHAR fmt[] = + {'M','I','M','E','\\','D','a','t','a','b','a','s','e','\\', + 'C','o','n','t','e','n','t',' ','T','y','p','e','\\', '%','s',0}; + LPWSTR key; + + /* + * check if the MIME is to be installed. Either as requesed by an + * extension or Class + */ + mt->InstallMe = (mt->InstallMe || + (mt->Class && mt->Class->Installed) || + (mt->Extension && mt->Extension->Installed)); + + if (!mt->InstallMe) + { + TRACE("MIME %s not scheduled to be installed\n", + debugstr_w(mt->ContentType)); + continue; + } + + mime = mt->ContentType; + exten = mt->Extension->Extension; + + extension = msi_alloc( (lstrlenW( exten ) + 2)*sizeof(WCHAR) ); + extension[0] = '.'; + lstrcpyW(extension+1,exten); + + key = msi_alloc( (strlenW(mime)+strlenW(fmt)+1) * sizeof(WCHAR) ); + sprintfW(key,fmt,mime); + msi_reg_set_subkey_val( HKEY_CLASSES_ROOT, key, szExten, extension ); + + msi_free(extension); + msi_free(key); + + if (mt->clsid) + FIXME("Handle non null for field 3\n"); + + uirow = MSI_CreateRecord(2); + MSI_RecordSetStringW(uirow,1,mt->ContentType); + MSI_RecordSetStringW(uirow,2,exten); + ui_actiondata(package,szRegisterMIMEInfo,uirow); + msiobj_release(&uirow->hdr); + } + + return ERROR_SUCCESS; +} diff --git a/reactos/dll/msi/cond.tab.c b/reactos/dll/msi/cond.tab.c new file mode 100644 index 00000000000..3cbed28a594 --- /dev/null +++ b/reactos/dll/msi/cond.tab.c @@ -0,0 +1,2239 @@ +/* A Bison parser, made by GNU Bison 1.875c. */ + +/* Skeleton parser for Yacc-like parsing with Bison, + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* As a special exception, when this file is copied by Bison into a + Bison output file, you may use that output file without restriction. + This special exception was added by the Free Software Foundation + in version 1.24 of Bison. */ + +/* Written by Richard Stallman by simplifying the original so called + ``semantic'' parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + +/* If NAME_PREFIX is specified substitute the variables and functions + names. */ +#define yyparse COND_parse +#define yylex COND_lex +#define yyerror COND_error +#define yylval COND_lval +#define yychar COND_char +#define yydebug COND_debug +#define yynerrs COND_nerrs + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + COND_SPACE = 258, + COND_EOF = 259, + COND_OR = 260, + COND_AND = 261, + COND_NOT = 262, + COND_LT = 263, + COND_GT = 264, + COND_EQ = 265, + COND_LPAR = 266, + COND_RPAR = 267, + COND_TILDA = 268, + COND_PERCENT = 269, + COND_DOLLARS = 270, + COND_QUESTION = 271, + COND_AMPER = 272, + COND_EXCLAM = 273, + COND_IDENT = 274, + COND_NUMBER = 275, + COND_LITER = 276, + COND_ERROR = 277 + }; +#endif +#define COND_SPACE 258 +#define COND_EOF 259 +#define COND_OR 260 +#define COND_AND 261 +#define COND_NOT 262 +#define COND_LT 263 +#define COND_GT 264 +#define COND_EQ 265 +#define COND_LPAR 266 +#define COND_RPAR 267 +#define COND_TILDA 268 +#define COND_PERCENT 269 +#define COND_DOLLARS 270 +#define COND_QUESTION 271 +#define COND_AMPER 272 +#define COND_EXCLAM 273 +#define COND_IDENT 274 +#define COND_NUMBER 275 +#define COND_LITER 276 +#define COND_ERROR 277 + + + + +/* Copy the first part of user declarations. */ +#line 1 "./cond.y" + + +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2003 Mike McCormack 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wine/debug.h" +#include "wine/unicode.h" + +#include "msi.h" +#include "msiquery.h" +#include "msipriv.h" + +#define YYLEX_PARAM info +#define YYPARSE_PARAM info + +static int COND_error(const char *str); + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +typedef struct tag_yyinput +{ + MSIPACKAGE *package; + LPCWSTR str; + INT n; + MSICONDITION result; +} COND_input; + +struct cond_str { + LPCWSTR data; + INT len; +}; + +static LPWSTR COND_GetString( struct cond_str *str ); +static LPWSTR COND_GetLiteral( struct cond_str *str ); +static int COND_lex( void *COND_lval, COND_input *info); + +typedef INT (*comp_int)(INT a, INT b); +typedef INT (*comp_str)(LPWSTR a, LPWSTR b, BOOL caseless); +typedef INT (*comp_m1)(LPWSTR a,int b); +typedef INT (*comp_m2)(int a,LPWSTR b); + +static INT comp_lt_i(INT a, INT b); +static INT comp_gt_i(INT a, INT b); +static INT comp_le_i(INT a, INT b); +static INT comp_ge_i(INT a, INT b); +static INT comp_eq_i(INT a, INT b); +static INT comp_ne_i(INT a, INT b); +static INT comp_bitand(INT a, INT b); +static INT comp_highcomp(INT a, INT b); +static INT comp_lowcomp(INT a, INT b); + +static INT comp_eq_s(LPWSTR a, LPWSTR b, BOOL casless); +static INT comp_ne_s(LPWSTR a, LPWSTR b, BOOL casless); +static INT comp_lt_s(LPWSTR a, LPWSTR b, BOOL casless); +static INT comp_gt_s(LPWSTR a, LPWSTR b, BOOL casless); +static INT comp_le_s(LPWSTR a, LPWSTR b, BOOL casless); +static INT comp_ge_s(LPWSTR a, LPWSTR b, BOOL casless); +static INT comp_substring(LPWSTR a, LPWSTR b, BOOL casless); +static INT comp_start(LPWSTR a, LPWSTR b, BOOL casless); +static INT comp_end(LPWSTR a, LPWSTR b, BOOL casless); + +static INT comp_eq_m1(LPWSTR a, INT b); +static INT comp_ne_m1(LPWSTR a, INT b); +static INT comp_lt_m1(LPWSTR a, INT b); +static INT comp_gt_m1(LPWSTR a, INT b); +static INT comp_le_m1(LPWSTR a, INT b); +static INT comp_ge_m1(LPWSTR a, INT b); + +static INT comp_eq_m2(INT a, LPWSTR b); +static INT comp_ne_m2(INT a, LPWSTR b); +static INT comp_lt_m2(INT a, LPWSTR b); +static INT comp_gt_m2(INT a, LPWSTR b); +static INT comp_le_m2(INT a, LPWSTR b); +static INT comp_ge_m2(INT a, LPWSTR b); + + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED) +#line 106 "./cond.y" +typedef union YYSTYPE { + struct cond_str str; + LPWSTR string; + INT value; + comp_int fn_comp_int; + comp_str fn_comp_str; + comp_m1 fn_comp_m1; + comp_m2 fn_comp_m2; +} YYSTYPE; +/* Line 191 of yacc.c. */ +#line 241 "cond.tab.c" +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + +/* Copy the second part of user declarations. */ + + +/* Line 214 of yacc.c. */ +#line 253 "cond.tab.c" + +#if ! defined (yyoverflow) || YYERROR_VERBOSE + +# ifndef YYFREE +# define YYFREE free +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# endif + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# define YYSTACK_ALLOC alloca +# endif +# else +# if defined (alloca) || defined (_ALLOCA_H) +# define YYSTACK_ALLOC alloca +# else +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# else +# if defined (__STDC__) || defined (__cplusplus) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# endif +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# endif +#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */ + + +#if (! defined (yyoverflow) \ + && (! defined (__cplusplus) \ + || (defined (YYSTYPE_IS_TRIVIAL) && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + short yyss; + YYSTYPE yyvs; + }; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (short) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined (__GNUC__) && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + register YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (0) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (0) + +#endif + +#if defined (__STDC__) || defined (__cplusplus) + typedef signed char yysigned_char; +#else + typedef short yysigned_char; +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 30 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 146 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 23 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 17 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 65 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 74 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 277 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const unsigned char yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const unsigned char yyprhs[] = +{ + 0, 0, 3, 5, 7, 11, 13, 17, 19, 22, + 24, 26, 30, 34, 39, 43, 47, 51, 53, 56, + 58, 60, 63, 66, 69, 72, 75, 77, 80, 82, + 84, 87, 90, 93, 96, 99, 101, 104, 106, 108, + 111, 114, 117, 120, 123, 125, 128, 130, 132, 135, + 138, 141, 144, 147, 149, 151, 153, 155, 157, 160, + 163, 166, 169, 171, 174, 176 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const yysigned_char yyrhs[] = +{ + 24, 0, -1, 25, -1, 26, -1, 26, 5, 25, + -1, 27, -1, 26, 6, 27, -1, 28, -1, 7, + 28, -1, 33, -1, 34, -1, 33, 29, 33, -1, + 34, 30, 34, -1, 34, 13, 30, 34, -1, 34, + 31, 33, -1, 33, 32, 34, -1, 11, 25, 12, + -1, 10, -1, 8, 9, -1, 8, -1, 9, -1, + 8, 10, -1, 9, 10, -1, 9, 8, -1, 8, + 8, -1, 9, 9, -1, 10, -1, 8, 9, -1, + 8, -1, 9, -1, 8, 10, -1, 9, 10, -1, + 9, 8, -1, 8, 8, -1, 9, 9, -1, 10, + -1, 8, 9, -1, 8, -1, 9, -1, 8, 10, + -1, 9, 10, -1, 9, 8, -1, 8, 8, -1, + 9, 9, -1, 10, -1, 8, 9, -1, 8, -1, + 9, -1, 8, 10, -1, 9, 10, -1, 9, 8, + -1, 8, 8, -1, 9, 9, -1, 36, -1, 39, + -1, 37, -1, 35, -1, 21, -1, 15, 38, -1, + 16, 38, -1, 17, 38, -1, 18, 38, -1, 38, + -1, 14, 38, -1, 19, -1, 20, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const unsigned short yyrline[] = +{ + 0, 136, 136, 144, 148, 155, 159, 166, 170, 178, + 182, 187, 191, 197, 203, 208, 213, 221, 225, 229, + 233, 237, 241, 246, 250, 254, 262, 266, 270, 274, + 278, 282, 287, 291, 295, 303, 307, 311, 315, 319, + 323, 328, 332, 336, 344, 348, 352, 356, 360, 364, + 369, 373, 377, 384, 388, 395, 399, 406, 415, 424, + 433, 442, 454, 477, 491, 500 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE +/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "COND_SPACE", "COND_EOF", "COND_OR", + "COND_AND", "COND_NOT", "COND_LT", "COND_GT", "COND_EQ", "COND_LPAR", + "COND_RPAR", "COND_TILDA", "COND_PERCENT", "COND_DOLLARS", + "COND_QUESTION", "COND_AMPER", "COND_EXCLAM", "COND_IDENT", + "COND_NUMBER", "COND_LITER", "COND_ERROR", "$accept", "condition", + "expression", "boolean_term", "boolean_factor", "term", "comp_op_i", + "comp_op_s", "comp_op_m1", "comp_op_m2", "value_i", "value_s", "literal", + "symbol_i", "symbol_s", "identifier", "integer", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const unsigned short yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const unsigned char yyr1[] = +{ + 0, 23, 24, 25, 25, 26, 26, 27, 27, 28, + 28, 28, 28, 28, 28, 28, 28, 29, 29, 29, + 29, 29, 29, 29, 29, 29, 30, 30, 30, 30, + 30, 30, 30, 30, 30, 31, 31, 31, 31, 31, + 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, + 32, 32, 32, 33, 33, 34, 34, 35, 36, 36, + 36, 36, 37, 37, 38, 39 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const unsigned char yyr2[] = +{ + 0, 2, 1, 1, 3, 1, 3, 1, 2, 1, + 1, 3, 3, 4, 3, 3, 3, 1, 2, 1, + 1, 2, 2, 2, 2, 2, 1, 2, 1, 1, + 2, 2, 2, 2, 2, 1, 2, 1, 1, 2, + 2, 2, 2, 2, 1, 2, 1, 1, 2, 2, + 2, 2, 2, 1, 1, 1, 1, 1, 2, 2, + 2, 2, 1, 2, 1, 1 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const unsigned char yydefact[] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 64, 65, + 57, 0, 2, 3, 5, 7, 9, 10, 56, 53, + 55, 62, 54, 8, 0, 63, 58, 59, 60, 61, + 1, 0, 0, 19, 20, 17, 0, 0, 37, 38, + 35, 0, 0, 0, 16, 4, 6, 24, 18, 21, + 23, 25, 22, 11, 15, 42, 36, 39, 41, 43, + 40, 28, 29, 26, 0, 12, 14, 33, 27, 30, + 32, 34, 31, 13 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yysigned_char yydefgoto[] = +{ + -1, 11, 12, 13, 14, 15, 36, 42, 43, 37, + 16, 17, 18, 19, 20, 21, 22 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -37 +static const short yypact[] = +{ + -4, 51, -4, -11, -11, -11, -11, -11, -37, -37, + -37, 18, -37, -1, -37, -37, 49, 14, -37, -37, + -37, -37, -37, -37, 19, -37, -37, -37, -37, -37, + -37, -4, -4, 11, 25, 34, 111, 59, 28, 42, + 60, 130, 59, 111, -37, -37, -37, 63, 69, 72, + 73, 81, 82, -37, -37, 85, 91, 94, 95, 103, + 104, 133, 136, -37, 59, -37, -37, -37, -37, -37, + -37, -37, -37, -37 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const short yypgoto[] = +{ + -37, -37, -2, -37, -6, 39, -37, 0, -37, -37, + -34, -36, -37, -37, -37, 129, -37 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -53 +static const yysigned_char yytable[] = +{ + 24, 54, 53, 1, 31, 32, 65, 2, 8, 66, + 3, 4, 5, 6, 7, 8, 9, 10, 30, 47, + 48, 49, 38, 39, 40, -46, 46, 41, 73, 45, + -46, 44, -46, 50, 51, 52, 55, 56, 57, -47, + 23, 64, -28, 0, -47, 0, -47, -28, -44, -28, + 58, 59, 60, -44, 0, -44, -29, 33, 34, 35, + 0, -29, 2, -29, 0, 3, 4, 5, 6, 7, + 8, 9, 10, 3, -26, 0, 0, -51, 8, -26, + 10, -26, -51, -45, -51, 0, -48, -50, -45, 0, + -45, -48, -50, -48, -50, -52, -49, 0, 0, -33, + -52, -49, -52, -49, -33, -27, -33, 0, -30, -32, + -27, 0, -27, -30, -32, -30, -32, -34, -31, 0, + 0, 0, -34, -31, -34, -31, 4, 5, 6, 7, + 0, 9, 25, 26, 27, 28, 29, 0, 61, 62, + 63, 67, 68, 69, 70, 71, 72 +}; + +static const yysigned_char yycheck[] = +{ + 2, 37, 36, 7, 5, 6, 42, 11, 19, 43, + 14, 15, 16, 17, 18, 19, 20, 21, 0, 8, + 9, 10, 8, 9, 10, 14, 32, 13, 64, 31, + 19, 12, 21, 8, 9, 10, 8, 9, 10, 14, + 1, 41, 14, -1, 19, -1, 21, 19, 14, 21, + 8, 9, 10, 19, -1, 21, 14, 8, 9, 10, + -1, 19, 11, 21, -1, 14, 15, 16, 17, 18, + 19, 20, 21, 14, 14, -1, -1, 14, 19, 19, + 21, 21, 19, 14, 21, -1, 14, 14, 19, -1, + 21, 19, 19, 21, 21, 14, 14, -1, -1, 14, + 19, 19, 21, 21, 19, 14, 21, -1, 14, 14, + 19, -1, 21, 19, 19, 21, 21, 14, 14, -1, + -1, -1, 19, 19, 21, 21, 15, 16, 17, 18, + -1, 20, 3, 4, 5, 6, 7, -1, 8, 9, + 10, 8, 9, 10, 8, 9, 10 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const unsigned char yystos[] = +{ + 0, 7, 11, 14, 15, 16, 17, 18, 19, 20, + 21, 24, 25, 26, 27, 28, 33, 34, 35, 36, + 37, 38, 39, 28, 25, 38, 38, 38, 38, 38, + 0, 5, 6, 8, 9, 10, 29, 32, 8, 9, + 10, 13, 30, 31, 12, 25, 27, 8, 9, 10, + 8, 9, 10, 33, 34, 8, 9, 10, 8, 9, + 10, 8, 9, 10, 30, 34, 33, 8, 9, 10, + 8, 9, 10, 34 +}; + +#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__) +# define YYSIZE_T __SIZE_TYPE__ +#endif +#if ! defined (YYSIZE_T) && defined (size_t) +# define YYSIZE_T size_t +#endif +#if ! defined (YYSIZE_T) +# if defined (__STDC__) || defined (__cplusplus) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# endif +#endif +#if ! defined (YYSIZE_T) +# define YYSIZE_T unsigned int +#endif + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror ("syntax error: cannot back up");\ + YYERROR; \ + } \ +while (0) + +#define YYTERROR 1 +#define YYERRCODE 256 + +/* YYLLOC_DEFAULT -- Compute the default location (before the actions + are run). */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + ((Current).first_line = (Rhs)[1].first_line, \ + (Current).first_column = (Rhs)[1].first_column, \ + (Current).last_line = (Rhs)[N].last_line, \ + (Current).last_column = (Rhs)[N].last_column) +#endif + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (&yylval, YYLEX_PARAM) +#else +# define YYLEX yylex (&yylval) +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + +# define YYDSYMPRINT(Args) \ +do { \ + if (yydebug) \ + yysymprint Args; \ +} while (0) + +# define YYDSYMPRINTF(Title, Token, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yysymprint (stderr, \ + Token, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yy_stack_print (short *bottom, short *top) +#else +static void +yy_stack_print (bottom, top) + short *bottom; + short *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (/* Nothing. */; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yy_reduce_print (int yyrule) +#else +static void +yy_reduce_print (yyrule) + int yyrule; +#endif +{ + int yyi; + unsigned int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ", + yyrule - 1, yylno); + /* Print the symbols being reduced, and their result. */ + for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++) + YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]); + YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]); +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (Rule); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YYDSYMPRINT(Args) +# define YYDSYMPRINTF(Title, Token, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#if defined (YYMAXDEPTH) && YYMAXDEPTH == 0 +# undef YYMAXDEPTH +#endif + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined (__GLIBC__) && defined (_STRING_H) +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +static YYSIZE_T +# if defined (__STDC__) || defined (__cplusplus) +yystrlen (const char *yystr) +# else +yystrlen (yystr) + const char *yystr; +# endif +{ + register const char *yys = yystr; + + while (*yys++ != '\0') + continue; + + return yys - yystr - 1; +} +# endif +# endif + +# ifndef yystpcpy +# if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE) +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +# if defined (__STDC__) || defined (__cplusplus) +yystpcpy (char *yydest, const char *yysrc) +# else +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +# endif +{ + register char *yyd = yydest; + register const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +#endif /* !YYERROR_VERBOSE */ + + + +#if YYDEBUG +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep) +#else +static void +yysymprint (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + /* Pacify ``unused variable'' warnings. */ + (void) yyvaluep; + + if (yytype < YYNTOKENS) + { + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); +# ifdef YYPRINT + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# endif + } + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + switch (yytype) + { + default: + break; + } + YYFPRINTF (yyoutput, ")"); +} + +#endif /* ! YYDEBUG */ +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yydestruct (int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yytype, yyvaluep) + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + /* Pacify ``unused variable'' warnings. */ + (void) yyvaluep; + + switch (yytype) + { + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ + +#ifdef YYPARSE_PARAM +# if defined (__STDC__) || defined (__cplusplus) +int yyparse (void *YYPARSE_PARAM); +# else +int yyparse (); +# endif +#else /* ! YYPARSE_PARAM */ +#if defined (__STDC__) || defined (__cplusplus) +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +# if defined (__STDC__) || defined (__cplusplus) +int yyparse (void *YYPARSE_PARAM) +# else +int yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +# endif +#else /* ! YYPARSE_PARAM */ +#if defined (__STDC__) || defined (__cplusplus) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + /* The lookahead symbol. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + register int yystate; + register int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + short yyssa[YYINITDEPTH]; + short *yyss = yyssa; + register short *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + register YYSTYPE *yyvsp; + + + +#define YYPOPSTACK (yyvsp--, yyssp--) + + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + /* When reducing, the number of symbols on the RHS of the reduced + rule. */ + int yylen; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. so pushing a state here evens the stacks. + */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + short *yyss1 = yyss; + + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow ("parser stack overflow", + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyoverflowlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyoverflowlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + short *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyoverflowlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + +/* Do appropriate processing given the current state. */ +/* Read a lookahead token if we need one and don't already have one. */ +/* yyresume: */ + + /* First try to decide what to do without reference to lookahead token. */ + + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YYDSYMPRINTF ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + if (yyn == YYFINAL) + YYACCEPT; + + /* Shift the lookahead token. */ + YYDPRINTF ((stderr, "Shifting token %s, ", yytname[yytoken])); + + /* Discard the token being shifted unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + *++yyvsp = yylval; + + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + yystate = yyn; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: +#line 137 "./cond.y" + { + COND_input* cond = (COND_input*) info; + cond->result = yyvsp[0].value; + ;} + break; + + case 3: +#line 145 "./cond.y" + { + yyval.value = yyvsp[0].value; + ;} + break; + + case 4: +#line 149 "./cond.y" + { + yyval.value = yyvsp[-2].value || yyvsp[0].value; + ;} + break; + + case 5: +#line 156 "./cond.y" + { + yyval.value = yyvsp[0].value; + ;} + break; + + case 6: +#line 160 "./cond.y" + { + yyval.value = yyvsp[-2].value && yyvsp[0].value; + ;} + break; + + case 7: +#line 167 "./cond.y" + { + yyval.value = yyvsp[0].value; + ;} + break; + + case 8: +#line 171 "./cond.y" + { + yyval.value = ! yyvsp[0].value; + ;} + break; + + case 9: +#line 179 "./cond.y" + { + yyval.value = yyvsp[0].value; + ;} + break; + + case 10: +#line 183 "./cond.y" + { + yyval.value = (yyvsp[0].string && yyvsp[0].string[0]) ? MSICONDITION_TRUE : MSICONDITION_FALSE; + msi_free( yyvsp[0].string ); + ;} + break; + + case 11: +#line 188 "./cond.y" + { + yyval.value = yyvsp[-1].fn_comp_int( yyvsp[-2].value, yyvsp[0].value ); + ;} + break; + + case 12: +#line 192 "./cond.y" + { + yyval.value = yyvsp[-1].fn_comp_str( yyvsp[-2].string, yyvsp[0].string, FALSE ); + msi_free( yyvsp[-2].string ); + msi_free( yyvsp[0].string ); + ;} + break; + + case 13: +#line 198 "./cond.y" + { + yyval.value = yyvsp[-1].fn_comp_str( yyvsp[-3].string, yyvsp[0].string, TRUE ); + msi_free( yyvsp[-3].string ); + msi_free( yyvsp[0].string ); + ;} + break; + + case 14: +#line 204 "./cond.y" + { + yyval.value = yyvsp[-1].fn_comp_m1( yyvsp[-2].string, yyvsp[0].value ); + msi_free( yyvsp[-2].string ); + ;} + break; + + case 15: +#line 209 "./cond.y" + { + yyval.value = yyvsp[-1].fn_comp_m2( yyvsp[-2].value, yyvsp[0].string ); + msi_free( yyvsp[0].string ); + ;} + break; + + case 16: +#line 214 "./cond.y" + { + yyval.value = yyvsp[-1].value; + ;} + break; + + case 17: +#line 222 "./cond.y" + { + yyval.fn_comp_int = comp_eq_i; + ;} + break; + + case 18: +#line 226 "./cond.y" + { + yyval.fn_comp_int = comp_ne_i; + ;} + break; + + case 19: +#line 230 "./cond.y" + { + yyval.fn_comp_int = comp_lt_i; + ;} + break; + + case 20: +#line 234 "./cond.y" + { + yyval.fn_comp_int = comp_gt_i; + ;} + break; + + case 21: +#line 238 "./cond.y" + { + yyval.fn_comp_int = comp_le_i; + ;} + break; + + case 22: +#line 242 "./cond.y" + { + yyval.fn_comp_int = comp_ge_i; + ;} + break; + + case 23: +#line 247 "./cond.y" + { + yyval.fn_comp_int = comp_bitand; + ;} + break; + + case 24: +#line 251 "./cond.y" + { + yyval.fn_comp_int = comp_highcomp; + ;} + break; + + case 25: +#line 255 "./cond.y" + { + yyval.fn_comp_int = comp_lowcomp; + ;} + break; + + case 26: +#line 263 "./cond.y" + { + yyval.fn_comp_str = comp_eq_s; + ;} + break; + + case 27: +#line 267 "./cond.y" + { + yyval.fn_comp_str = comp_ne_s; + ;} + break; + + case 28: +#line 271 "./cond.y" + { + yyval.fn_comp_str = comp_lt_s; + ;} + break; + + case 29: +#line 275 "./cond.y" + { + yyval.fn_comp_str = comp_gt_s; + ;} + break; + + case 30: +#line 279 "./cond.y" + { + yyval.fn_comp_str = comp_le_s; + ;} + break; + + case 31: +#line 283 "./cond.y" + { + yyval.fn_comp_str = comp_ge_s; + ;} + break; + + case 32: +#line 288 "./cond.y" + { + yyval.fn_comp_str = comp_substring; + ;} + break; + + case 33: +#line 292 "./cond.y" + { + yyval.fn_comp_str = comp_start; + ;} + break; + + case 34: +#line 296 "./cond.y" + { + yyval.fn_comp_str = comp_end; + ;} + break; + + case 35: +#line 304 "./cond.y" + { + yyval.fn_comp_m1 = comp_eq_m1; + ;} + break; + + case 36: +#line 308 "./cond.y" + { + yyval.fn_comp_m1 = comp_ne_m1; + ;} + break; + + case 37: +#line 312 "./cond.y" + { + yyval.fn_comp_m1 = comp_lt_m1; + ;} + break; + + case 38: +#line 316 "./cond.y" + { + yyval.fn_comp_m1 = comp_gt_m1; + ;} + break; + + case 39: +#line 320 "./cond.y" + { + yyval.fn_comp_m1 = comp_le_m1; + ;} + break; + + case 40: +#line 324 "./cond.y" + { + yyval.fn_comp_m1 = comp_ge_m1; + ;} + break; + + case 41: +#line 329 "./cond.y" + { + yyval.fn_comp_m1 = 0; + ;} + break; + + case 42: +#line 333 "./cond.y" + { + yyval.fn_comp_m1 = 0; + ;} + break; + + case 43: +#line 337 "./cond.y" + { + yyval.fn_comp_m1 = 0; + ;} + break; + + case 44: +#line 345 "./cond.y" + { + yyval.fn_comp_m2 = comp_eq_m2; + ;} + break; + + case 45: +#line 349 "./cond.y" + { + yyval.fn_comp_m2 = comp_ne_m2; + ;} + break; + + case 46: +#line 353 "./cond.y" + { + yyval.fn_comp_m2 = comp_lt_m2; + ;} + break; + + case 47: +#line 357 "./cond.y" + { + yyval.fn_comp_m2 = comp_gt_m2; + ;} + break; + + case 48: +#line 361 "./cond.y" + { + yyval.fn_comp_m2 = comp_le_m2; + ;} + break; + + case 49: +#line 365 "./cond.y" + { + yyval.fn_comp_m2 = comp_ge_m2; + ;} + break; + + case 50: +#line 370 "./cond.y" + { + yyval.fn_comp_m2 = 0; + ;} + break; + + case 51: +#line 374 "./cond.y" + { + yyval.fn_comp_m2 = 0; + ;} + break; + + case 52: +#line 378 "./cond.y" + { + yyval.fn_comp_m2 = 0; + ;} + break; + + case 53: +#line 385 "./cond.y" + { + yyval.value = yyvsp[0].value; + ;} + break; + + case 54: +#line 389 "./cond.y" + { + yyval.value = yyvsp[0].value; + ;} + break; + + case 55: +#line 396 "./cond.y" + { + yyval.string = yyvsp[0].string; + ;} + break; + + case 56: +#line 400 "./cond.y" + { + yyval.string = yyvsp[0].string; + ;} + break; + + case 57: +#line 407 "./cond.y" + { + yyval.string = COND_GetLiteral(&yyvsp[0].str); + if( !yyval.string ) + YYABORT; + ;} + break; + + case 58: +#line 416 "./cond.y" + { + COND_input* cond = (COND_input*) info; + INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN; + + MSI_GetComponentStateW(cond->package, yyvsp[0].string, &install, &action ); + yyval.value = action; + msi_free( yyvsp[0].string ); + ;} + break; + + case 59: +#line 425 "./cond.y" + { + COND_input* cond = (COND_input*) info; + INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN; + + MSI_GetComponentStateW(cond->package, yyvsp[0].string, &install, &action ); + yyval.value = install; + msi_free( yyvsp[0].string ); + ;} + break; + + case 60: +#line 434 "./cond.y" + { + COND_input* cond = (COND_input*) info; + INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN; + + MSI_GetFeatureStateW(cond->package, yyvsp[0].string, &install, &action ); + yyval.value = action; + msi_free( yyvsp[0].string ); + ;} + break; + + case 61: +#line 443 "./cond.y" + { + COND_input* cond = (COND_input*) info; + INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN; + + MSI_GetFeatureStateW(cond->package, yyvsp[0].string, &install, &action ); + yyval.value = install; + msi_free( yyvsp[0].string ); + ;} + break; + + case 62: +#line 455 "./cond.y" + { + DWORD sz; + COND_input* cond = (COND_input*) info; + + sz = 0; + MSI_GetPropertyW(cond->package, yyvsp[0].string, NULL, &sz); + if (sz == 0) + { + yyval.string = msi_alloc( sizeof(WCHAR)); + yyval.string[0] = 0; + } + else + { + sz ++; + yyval.string = msi_alloc( sz*sizeof (WCHAR) ); + + /* Lookup the identifier */ + + MSI_GetPropertyW(cond->package,yyvsp[0].string,yyval.string,&sz); + } + msi_free( yyvsp[0].string ); + ;} + break; + + case 63: +#line 478 "./cond.y" + { + UINT len = GetEnvironmentVariableW( yyvsp[0].string, NULL, 0 ); + if( len++ ) + { + yyval.string = msi_alloc( len*sizeof (WCHAR) ); + if( yyval.string ) + GetEnvironmentVariableW( yyvsp[0].string, yyval.string, len ); + } + msi_free( yyvsp[0].string ); + ;} + break; + + case 64: +#line 492 "./cond.y" + { + yyval.string = COND_GetString(&yyvsp[0].str); + if( !yyval.string ) + YYABORT; + ;} + break; + + case 65: +#line 501 "./cond.y" + { + LPWSTR szNum = COND_GetString(&yyvsp[0].str); + if( !szNum ) + YYABORT; + yyval.value = atoiW( szNum ); + msi_free( szNum ); + ;} + break; + + + } + +/* Line 1000 of yacc.c. */ +#line 1733 "cond.tab.c" + + yyvsp -= yylen; + yyssp -= yylen; + + + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if YYERROR_VERBOSE + yyn = yypact[yystate]; + + if (YYPACT_NINF < yyn && yyn < YYLAST) + { + YYSIZE_T yysize = 0; + int yytype = YYTRANSLATE (yychar); + const char* yyprefix; + char *yymsg; + int yyx; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 0; + + yyprefix = ", expecting "; + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + yysize += yystrlen (yyprefix) + yystrlen (yytname [yyx]); + yycount += 1; + if (yycount == 5) + { + yysize = 0; + break; + } + } + yysize += (sizeof ("syntax error, unexpected ") + + yystrlen (yytname[yytype])); + yymsg = (char *) YYSTACK_ALLOC (yysize); + if (yymsg != 0) + { + char *yyp = yystpcpy (yymsg, "syntax error, unexpected "); + yyp = yystpcpy (yyp, yytname[yytype]); + + if (yycount < 5) + { + yyprefix = ", expecting "; + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + yyp = yystpcpy (yyp, yyprefix); + yyp = yystpcpy (yyp, yytname[yyx]); + yyprefix = " or "; + } + } + yyerror (yymsg); + YYSTACK_FREE (yymsg); + } + else + yyerror ("syntax error; also virtual memory exhausted"); + } + else +#endif /* YYERROR_VERBOSE */ + yyerror ("syntax error"); + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* If at end of input, pop the error token, + then the rest of the stack, then return failure. */ + if (yychar == YYEOF) + for (;;) + { + YYPOPSTACK; + if (yyssp == yyss) + YYABORT; + YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp); + yydestruct (yystos[*yyssp], yyvsp); + } + } + else + { + YYDSYMPRINTF ("Error: discarding", yytoken, &yylval, &yylloc); + yydestruct (yytoken, &yylval); + yychar = YYEMPTY; + + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + +#ifdef __GNUC__ + /* Pacify GCC when the user code never invokes YYERROR and the label + yyerrorlab therefore never appears in user code. */ + if (0) + goto yyerrorlab; +#endif + + yyvsp -= yylen; + yyssp -= yylen; + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp); + yydestruct (yystos[yystate], yyvsp); + YYPOPSTACK; + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + YYDPRINTF ((stderr, "Shifting error token, ")); + + *++yyvsp = yylval; + + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*----------------------------------------------. +| yyoverflowlab -- parser overflow comes here. | +`----------------------------------------------*/ +yyoverflowlab: + yyerror ("parser stack overflow"); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + return yyresult; +} + + +#line 510 "./cond.y" + + + +static int COND_IsAlpha( WCHAR x ) +{ + return( ( ( x >= 'A' ) && ( x <= 'Z' ) ) || + ( ( x >= 'a' ) && ( x <= 'z' ) ) || + ( ( x == '_' ) ) ); +} + +static int COND_IsNumber( WCHAR x ) +{ + return( (( x >= '0' ) && ( x <= '9' )) || (x =='-') || (x =='.') ); +} + + +/* the mess of comparison functions */ + +static INT comp_lt_i(INT a, INT b) +{ return (a < b); } +static INT comp_gt_i(INT a, INT b) +{ return (a > b); } +static INT comp_le_i(INT a, INT b) +{ return (a <= b); } +static INT comp_ge_i(INT a, INT b) +{ return (a >= b); } +static INT comp_eq_i(INT a, INT b) +{ return (a == b); } +static INT comp_ne_i(INT a, INT b) +{ return (a != b); } +static INT comp_bitand(INT a, INT b) +{ return a & b;} +static INT comp_highcomp(INT a, INT b) +{ return HIWORD(a)==b; } +static INT comp_lowcomp(INT a, INT b) +{ return LOWORD(a)==b; } + +static INT comp_eq_s(LPWSTR a, LPWSTR b, BOOL casless) +{ if (casless) return !strcmpiW(a,b); else return !strcmpW(a,b);} +static INT comp_ne_s(LPWSTR a, LPWSTR b, BOOL casless) +{ if (casless) return strcmpiW(a,b); else return strcmpW(a,b);} +static INT comp_lt_s(LPWSTR a, LPWSTR b, BOOL casless) +{ if (casless) return strcmpiW(a,b)<0; else return strcmpW(a,b)<0;} +static INT comp_gt_s(LPWSTR a, LPWSTR b, BOOL casless) +{ if (casless) return strcmpiW(a,b)>0; else return strcmpW(a,b)>0;} +static INT comp_le_s(LPWSTR a, LPWSTR b, BOOL casless) +{ if (casless) return strcmpiW(a,b)<=0; else return strcmpW(a,b)<=0;} +static INT comp_ge_s(LPWSTR a, LPWSTR b, BOOL casless) +{ if (casless) return strcmpiW(a,b)>=0; else return strcmpW(a,b)>=0;} +static INT comp_substring(LPWSTR a, LPWSTR b, BOOL casless) +/* ERROR NOT WORKING REWRITE */ +{ if (casless) return strstrW(a,b)!=NULL; else return strstrW(a,b)!=NULL;} +static INT comp_start(LPWSTR a, LPWSTR b, BOOL casless) +{ if (casless) return strncmpiW(a,b,strlenW(b))==0; + else return strncmpW(a,b,strlenW(b))==0;} +static INT comp_end(LPWSTR a, LPWSTR b, BOOL casless) +{ + int i = strlenW(a); + int j = strlenW(b); + if (j>i) + return 0; + if (casless) return (!strcmpiW(&a[i-j-1],b)); + else return (!strcmpW(&a[i-j-1],b)); +} + + +static INT comp_eq_m1(LPWSTR a, INT b) +{ if (COND_IsNumber(a[0])) return atoiW(a)==b; else return 0;} +static INT comp_ne_m1(LPWSTR a, INT b) +{ if (COND_IsNumber(a[0])) return atoiW(a)!=b; else return 1;} +static INT comp_lt_m1(LPWSTR a, INT b) +{ if (COND_IsNumber(a[0])) return atoiW(a)b; else return 0;} +static INT comp_le_m1(LPWSTR a, INT b) +{ if (COND_IsNumber(a[0])) return atoiW(a)<=b; else return 0;} +static INT comp_ge_m1(LPWSTR a, INT b) +{ if (COND_IsNumber(a[0])) return atoiW(a)>=b; else return 0;} + +static INT comp_eq_m2(INT a, LPWSTR b) +{ if (COND_IsNumber(b[0])) return a == atoiW(b); else return 0;} +static INT comp_ne_m2(INT a, LPWSTR b) +{ if (COND_IsNumber(b[0])) return a != atoiW(b); else return 1;} +static INT comp_lt_m2(INT a, LPWSTR b) +{ if (COND_IsNumber(b[0])) return a < atoiW(b); else return 0;} +static INT comp_gt_m2(INT a, LPWSTR b) +{ if (COND_IsNumber(b[0])) return a > atoiW(b); else return 0;} +static INT comp_le_m2(INT a, LPWSTR b) +{ if (COND_IsNumber(b[0])) return a <= atoiW(b); else return 0;} +static INT comp_ge_m2(INT a, LPWSTR b) +{ if (COND_IsNumber(b[0])) return a >= atoiW(b); else return 0;} + + + +static int COND_IsIdent( WCHAR x ) +{ + return( COND_IsAlpha( x ) || COND_IsNumber( x ) || ( x == '_' ) + || ( x == '#' ) || (x == '.') ); +} + +static int COND_GetOne( struct cond_str *str, COND_input *cond ) +{ + static const WCHAR szNot[] = {'N','O','T',0}; + static const WCHAR szAnd[] = {'A','N','D',0}; + static const WCHAR szOr[] = {'O','R',0}; + WCHAR ch; + int rc, len = 1; + + str->data = &cond->str[cond->n]; + + ch = str->data[0]; + switch( ch ) + { + case 0: return 0; + case '(': rc = COND_LPAR; break; + case ')': rc = COND_RPAR; break; + case '&': rc = COND_AMPER; break; + case '!': rc = COND_EXCLAM; break; + case '$': rc = COND_DOLLARS; break; + case '?': rc = COND_QUESTION; break; + case '%': rc = COND_PERCENT; break; + case ' ': rc = COND_SPACE; break; + case '=': rc = COND_EQ; break; + case '~': rc = COND_TILDA; break; + case '<': rc = COND_LT; break; + case '>': rc = COND_GT; break; + case '"': + { + const WCHAR *ch2 = str->data + 1; + + + while ( *ch2 && *ch2 != '"' ) + ++ch2; + if (*ch2 == '"') + { + len = ch2 - str->data + 1; + rc = COND_LITER; + break; + } + } + ERR("Unterminated string\n"); + rc = COND_ERROR; + break; + default: + if( COND_IsAlpha( ch ) ) + { + while( COND_IsIdent( str->data[len] ) ) + len++; + rc = COND_IDENT; + break; + } + + if( COND_IsNumber( ch ) ) + { + while( COND_IsNumber( str->data[len] ) ) + len++; + rc = COND_NUMBER; + break; + } + + ERR("Got unknown character %c(%x)\n",ch,ch); + rc = COND_ERROR; + break; + } + + /* keyword identifiers */ + if( rc == COND_IDENT ) + { + if( (len==3) && (strncmpiW(str->data,szNot,len)==0) ) + rc = COND_NOT; + else if( (len==3) && (strncmpiW(str->data,szAnd,len)==0) ) + rc = COND_AND; + else if( (len==2) && (strncmpiW(str->data,szOr,len)==0) ) + rc = COND_OR; + } + + cond->n += len; + str->len = len; + + return rc; +} + +static int COND_lex( void *COND_lval, COND_input *cond ) +{ + int rc; + struct cond_str *str = COND_lval; + + do { + rc = COND_GetOne( str, cond ); + } while (rc == COND_SPACE); + + return rc; +} + +static LPWSTR COND_GetString( struct cond_str *str ) +{ + LPWSTR ret; + + ret = msi_alloc( (str->len+1) * sizeof (WCHAR) ); + if( ret ) + { + memcpy( ret, str->data, str->len * sizeof(WCHAR)); + ret[str->len]=0; + } + TRACE("Got identifier %s\n",debugstr_w(ret)); + return ret; +} + +static LPWSTR COND_GetLiteral( struct cond_str *str ) +{ + LPWSTR ret; + + ret = msi_alloc( (str->len-1) * sizeof (WCHAR) ); + if( ret ) + { + memcpy( ret, str->data+1, (str->len-2) * sizeof(WCHAR) ); + ret[str->len - 2]=0; + } + TRACE("Got literal %s\n",debugstr_w(ret)); + return ret; +} + +static int COND_error(const char *str) +{ + return 0; +} + +MSICONDITION MSI_EvaluateConditionW( MSIPACKAGE *package, LPCWSTR szCondition ) +{ + COND_input cond; + MSICONDITION r; + + cond.package = package; + cond.str = szCondition; + cond.n = 0; + cond.result = -1; + + TRACE("Evaluating %s\n",debugstr_w(szCondition)); + + if ( szCondition == NULL || szCondition[0] == 0) + r = MSICONDITION_NONE; + else if ( !COND_parse( &cond ) ) + r = cond.result; + else + r = MSICONDITION_ERROR; + + TRACE("Evaluates to %i\n",r); + return r; +} + +MSICONDITION WINAPI MsiEvaluateConditionW( MSIHANDLE hInstall, LPCWSTR szCondition ) +{ + MSIPACKAGE *package; + UINT ret; + + package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE); + if( !package) + return ERROR_INVALID_HANDLE; + ret = MSI_EvaluateConditionW( package, szCondition ); + msiobj_release( &package->hdr ); + return ret; +} + +MSICONDITION WINAPI MsiEvaluateConditionA( MSIHANDLE hInstall, LPCSTR szCondition ) +{ + LPWSTR szwCond = NULL; + MSICONDITION r; + + if( szCondition ) + { + UINT len = MultiByteToWideChar( CP_ACP, 0, szCondition, -1, NULL, 0 ); + szwCond = msi_alloc( len * sizeof (WCHAR) ); + MultiByteToWideChar( CP_ACP, 0, szCondition, -1, szwCond, len ); + } + + r = MsiEvaluateConditionW( hInstall, szwCond ); + + msi_free( szwCond ); + + return r; +} + diff --git a/reactos/dll/msi/cond.tab.h b/reactos/dll/msi/cond.tab.h new file mode 100644 index 00000000000..f37273b3993 --- /dev/null +++ b/reactos/dll/msi/cond.tab.h @@ -0,0 +1,99 @@ +/* A Bison parser, made by GNU Bison 1.875c. */ + +/* Skeleton parser for Yacc-like parsing with Bison, + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* As a special exception, when this file is copied by Bison into a + Bison output file, you may use that output file without restriction. + This special exception was added by the Free Software Foundation + in version 1.24 of Bison. */ + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + COND_SPACE = 258, + COND_EOF = 259, + COND_OR = 260, + COND_AND = 261, + COND_NOT = 262, + COND_LT = 263, + COND_GT = 264, + COND_EQ = 265, + COND_LPAR = 266, + COND_RPAR = 267, + COND_TILDA = 268, + COND_PERCENT = 269, + COND_DOLLARS = 270, + COND_QUESTION = 271, + COND_AMPER = 272, + COND_EXCLAM = 273, + COND_IDENT = 274, + COND_NUMBER = 275, + COND_LITER = 276, + COND_ERROR = 277 + }; +#endif +#define COND_SPACE 258 +#define COND_EOF 259 +#define COND_OR 260 +#define COND_AND 261 +#define COND_NOT 262 +#define COND_LT 263 +#define COND_GT 264 +#define COND_EQ 265 +#define COND_LPAR 266 +#define COND_RPAR 267 +#define COND_TILDA 268 +#define COND_PERCENT 269 +#define COND_DOLLARS 270 +#define COND_QUESTION 271 +#define COND_AMPER 272 +#define COND_EXCLAM 273 +#define COND_IDENT 274 +#define COND_NUMBER 275 +#define COND_LITER 276 +#define COND_ERROR 277 + + + + +#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED) +#line 106 "./cond.y" +typedef union YYSTYPE { + struct cond_str str; + LPWSTR string; + INT value; + comp_int fn_comp_int; + comp_str fn_comp_str; + comp_m1 fn_comp_m1; + comp_m2 fn_comp_m2; +} YYSTYPE; +/* Line 1275 of yacc.c. */ +#line 91 "cond.tab.h" +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + + + diff --git a/reactos/dll/msi/cond.y b/reactos/dll/msi/cond.y new file mode 100644 index 00000000000..7abf2d34757 --- /dev/null +++ b/reactos/dll/msi/cond.y @@ -0,0 +1,717 @@ +%{ + +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2003 Mike McCormack 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "config.h" + +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "wine/debug.h" +#include "wine/unicode.h" + +#include "msi.h" +#include "msiquery.h" +#include "msipriv.h" +#include "action.h" + +#define YYLEX_PARAM info +#define YYPARSE_PARAM info + +static int COND_error(const char *str); + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +typedef struct tag_yyinput +{ + MSIPACKAGE *package; + LPCWSTR str; + INT n; + MSICONDITION result; +} COND_input; + +struct cond_str { + LPCWSTR data; + INT len; +}; + +static LPWSTR COND_GetString( struct cond_str *str ); +static LPWSTR COND_GetLiteral( struct cond_str *str ); +static int COND_lex( void *COND_lval, COND_input *info); +static const WCHAR szEmpty[] = { 0 }; + +static INT compare_int( INT a, INT operator, INT b ); +static INT compare_string( LPCWSTR a, INT operator, LPCWSTR b ); + +static INT compare_and_free_strings( LPWSTR a, INT op, LPWSTR b ) +{ + INT r; + + r = compare_string( a, op, b ); + msi_free( a ); + msi_free( b ); + return r; +} + +static BOOL num_from_prop( LPCWSTR p, INT *val ) +{ + INT ret = 0, sign = 1; + + if (!p) + return FALSE; + if (*p == '-') + { + sign = -1; + p++; + } + if (!*p) + return FALSE; + while (*p) + { + if( *p < '0' || *p > '9' ) + return FALSE; + ret = ret*10 + (*p - '0'); + p++; + } + *val = ret*sign; + return TRUE; +} + +%} + +%pure-parser + +%union +{ + struct cond_str str; + LPWSTR string; + INT value; +} + +%token COND_SPACE COND_EOF COND_SPACE +%token COND_OR COND_AND COND_NOT COND_XOR COND_IMP COND_EQV +%token COND_LT COND_GT COND_EQ COND_NE COND_GE COND_LE +%token COND_ILT COND_IGT COND_IEQ COND_INE COND_IGE COND_ILE +%token COND_LPAR COND_RPAR COND_TILDA COND_SS COND_ISS +%token COND_ILHS COND_IRHS COND_LHS COND_RHS +%token COND_PERCENT COND_DOLLARS COND_QUESTION COND_AMPER COND_EXCLAM +%token COND_IDENT COND_NUMBER COND_LITER + +%nonassoc COND_ERROR COND_EOF + +%type expression boolean_term boolean_factor +%type value_i integer operator +%type identifier symbol_s value_s literal + +%% + +condition: + expression + { + COND_input* cond = (COND_input*) info; + cond->result = $1; + } + | /* empty */ + { + COND_input* cond = (COND_input*) info; + cond->result = MSICONDITION_NONE; + } + ; + +expression: + boolean_term + { + $$ = $1; + } + | expression COND_OR boolean_term + { + $$ = $1 || $3; + } + | expression COND_IMP boolean_term + { + $$ = !$1 || $3; + } + | expression COND_XOR boolean_term + { + $$ = ( $1 || $3 ) && !( $1 && $3 ); + } + | expression COND_EQV boolean_term + { + $$ = ( $1 && $3 ) || ( !$1 && !$3 ); + } + ; + +boolean_term: + boolean_factor + { + $$ = $1; + } + | boolean_term COND_AND boolean_factor + { + $$ = $1 && $3; + } + ; + +boolean_factor: + COND_NOT boolean_factor + { + $$ = $2 ? 0 : 1; + } + | value_i + { + $$ = $1 ? 1 : 0; + } + | value_s + { + $$ = ($1 && $1[0]) ? 1 : 0; + } + | value_i operator value_i + { + $$ = compare_int( $1, $2, $3 ); + } + | symbol_s operator value_i + { + int num; + if (num_from_prop( $1, &num )) + $$ = compare_int( num, $2, $3 ); + else + $$ = ($2 == COND_NE || $2 == COND_INE ); + } + | value_i operator symbol_s + { + int num; + if (num_from_prop( $3, &num )) + $$ = compare_int( $1, $2, num ); + else + $$ = ($2 == COND_NE || $2 == COND_INE ); + } + | symbol_s operator symbol_s + { + $$ = compare_and_free_strings( $1, $2, $3 ); + } + | symbol_s operator literal + { + $$ = compare_and_free_strings( $1, $2, $3 ); + } + | literal operator symbol_s + { + $$ = compare_and_free_strings( $1, $2, $3 ); + } + | literal operator literal + { + $$ = compare_and_free_strings( $1, $2, $3 ); + } + | literal operator value_i + { + $$ = 0; + } + | value_i operator literal + { + $$ = 0; + } + | COND_LPAR expression COND_RPAR + { + $$ = $2; + } + ; + +operator: + /* common functions */ + COND_EQ { $$ = COND_EQ; } + | COND_NE { $$ = COND_NE; } + | COND_LT { $$ = COND_LT; } + | COND_GT { $$ = COND_GT; } + | COND_LE { $$ = COND_LE; } + | COND_GE { $$ = COND_GE; } + | COND_SS { $$ = COND_SS; } + | COND_IEQ { $$ = COND_IEQ; } + | COND_INE { $$ = COND_INE; } + | COND_ILT { $$ = COND_ILT; } + | COND_IGT { $$ = COND_IGT; } + | COND_ILE { $$ = COND_ILE; } + | COND_IGE { $$ = COND_IGE; } + | COND_ISS { $$ = COND_ISS; } + | COND_LHS { $$ = COND_LHS; } + | COND_RHS { $$ = COND_RHS; } + | COND_ILHS { $$ = COND_ILHS; } + | COND_IRHS { $$ = COND_IRHS; } + ; + +value_s: + symbol_s + { + $$ = $1; + } + | literal + { + $$ = $1; + } + ; + +literal: + COND_LITER + { + $$ = COND_GetLiteral(&$1); + if( !$$ ) + YYABORT; + } + ; + +value_i: + integer + { + $$ = $1; + } + | COND_DOLLARS identifier + { + COND_input* cond = (COND_input*) info; + INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN; + + MSI_GetComponentStateW(cond->package, $2, &install, &action ); + $$ = action; + msi_free( $2 ); + } + | COND_QUESTION identifier + { + COND_input* cond = (COND_input*) info; + INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN; + + MSI_GetComponentStateW(cond->package, $2, &install, &action ); + $$ = install; + msi_free( $2 ); + } + | COND_AMPER identifier + { + COND_input* cond = (COND_input*) info; + INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN; + + MSI_GetFeatureStateW(cond->package, $2, &install, &action ); + $$ = action; + msi_free( $2 ); + } + | COND_EXCLAM identifier + { + COND_input* cond = (COND_input*) info; + INSTALLSTATE install = INSTALLSTATE_UNKNOWN, action = INSTALLSTATE_UNKNOWN; + + MSI_GetFeatureStateW(cond->package, $2, &install, &action ); + $$ = install; + msi_free( $2 ); + } + ; + +symbol_s: + identifier + { + COND_input* cond = (COND_input*) info; + + $$ = msi_dup_property( cond->package, $1 ); + msi_free( $1 ); + } + | COND_PERCENT identifier + { + UINT len = GetEnvironmentVariableW( $2, NULL, 0 ); + $$ = NULL; + if (len++) + { + $$ = msi_alloc( len*sizeof (WCHAR) ); + GetEnvironmentVariableW( $2, $$, len ); + } + msi_free( $2 ); + } + ; + +identifier: + COND_IDENT + { + $$ = COND_GetString(&$1); + if( !$$ ) + YYABORT; + } + ; + +integer: + COND_NUMBER + { + LPWSTR szNum = COND_GetString(&$1); + if( !szNum ) + YYABORT; + $$ = atoiW( szNum ); + msi_free( szNum ); + } + ; + +%% + + +static int COND_IsAlpha( WCHAR x ) +{ + return( ( ( x >= 'A' ) && ( x <= 'Z' ) ) || + ( ( x >= 'a' ) && ( x <= 'z' ) ) || + ( ( x == '_' ) ) ); +} + +static int COND_IsNumber( WCHAR x ) +{ + return( (( x >= '0' ) && ( x <= '9' )) || (x =='-') || (x =='.') ); +} + +static WCHAR *strstriW( const WCHAR *str, const WCHAR *sub ) +{ + LPWSTR strlower, sublower, r; + strlower = CharLowerW( strdupW( str ) ); + sublower = CharLowerW( strdupW( sub ) ); + r = strstrW( strlower, sublower ); + if (r) + r = (LPWSTR)str + (r - strlower); + msi_free( strlower ); + msi_free( sublower ); + return r; +} + +static INT compare_string( LPCWSTR a, INT operator, LPCWSTR b ) +{ + /* null and empty string are equivalent */ + if (!a) a = szEmpty; + if (!b) b = szEmpty; + + /* a or b may be NULL */ + switch (operator) + { + case COND_LT: + return -1 == lstrcmpW( a, b ); + case COND_GT: + return 1 == lstrcmpW( a, b ); + case COND_EQ: + return 0 == lstrcmpW( a, b ); + case COND_NE: + return 0 != lstrcmpW( a, b ); + case COND_GE: + return -1 != lstrcmpW( a, b ); + case COND_LE: + return 1 != lstrcmpW( a, b ); + case COND_SS: /* substring */ + return strstrW( a, b ) ? 1 : 0; + case COND_ILT: + return -1 == lstrcmpiW( a, b ); + case COND_IGT: + return 1 == lstrcmpiW( a, b ); + case COND_IEQ: + return 0 == lstrcmpiW( a, b ); + case COND_INE: + return 0 != lstrcmpiW( a, b ); + case COND_IGE: + return -1 != lstrcmpiW( a, b ); + case COND_ILE: + return 1 != lstrcmpiW( a, b ); + case COND_ISS: + return strstriW( a, b ) ? 1 : 0; + case COND_LHS: + case COND_RHS: + case COND_ILHS: + case COND_IRHS: + ERR("unimplemented string comparison\n"); + break; + default: + ERR("invalid integer operator\n"); + return 0; + } + return 0; +} + + +static INT compare_int( INT a, INT operator, INT b ) +{ + switch (operator) + { + case COND_LT: + case COND_ILT: + return a < b; + case COND_GT: + case COND_IGT: + return a > b; + case COND_EQ: + case COND_IEQ: + return a == b; + case COND_NE: + case COND_INE: + return a != b; + case COND_GE: + case COND_IGE: + return a >= b; + case COND_LE: + case COND_ILE: + return a >= b; + case COND_SS: + case COND_ISS: + return ( a & b ) ? 1 : 0; + case COND_RHS: + return ( ( a & 0xffff ) == b ) ? 1 : 0; + case COND_LHS: + return ( ( (a>>16) & 0xffff ) == b ) ? 1 : 0; + default: + ERR("invalid integer operator\n"); + return 0; + } + return 0; +} + + +static int COND_IsIdent( WCHAR x ) +{ + return( COND_IsAlpha( x ) || COND_IsNumber( x ) || ( x == '_' ) + || ( x == '#' ) || (x == '.') ); +} + +static int COND_GetOperator( COND_input *cond ) +{ + static const struct { + const WCHAR str[4]; + int id; + } table[] = { + { {'~','=',0}, COND_IEQ }, + { {'~','>','=',0}, COND_ILE }, + { {'~','>','<',0}, COND_ISS }, + { {'~','>','>',0}, COND_IRHS }, + { {'~','>',0}, COND_ILT }, + { {'~','<','>',0}, COND_INE }, + { {'~','<','=',0}, COND_IGE }, + { {'~','<','<',0}, COND_ILHS }, + { {'~','<',0}, COND_IGT }, + { {'>','=',0}, COND_GE }, + { {'>','<',0}, COND_SS }, + { {'>','>',0}, COND_LHS }, + { {'>',0}, COND_GT }, + { {'<','>',0}, COND_NE }, + { {'<','=',0}, COND_LE }, + { {'<','<',0}, COND_RHS }, + { {'<',0}, COND_LT }, + { {0}, 0 } + }; + LPCWSTR p = &cond->str[cond->n]; + int i = 0, len; + + while ( 1 ) + { + len = lstrlenW( table[i].str ); + if ( !len || 0 == strncmpW( table[i].str, p, len ) ) + break; + i++; + } + cond->n += len; + return table[i].id; +} + +static int COND_GetOne( struct cond_str *str, COND_input *cond ) +{ + int rc, len = 1; + WCHAR ch; + + str->data = &cond->str[cond->n]; + + ch = str->data[0]; + + switch( ch ) + { + case 0: return 0; + case '(': rc = COND_LPAR; break; + case ')': rc = COND_RPAR; break; + case '&': rc = COND_AMPER; break; + case '!': rc = COND_EXCLAM; break; + case '$': rc = COND_DOLLARS; break; + case '?': rc = COND_QUESTION; break; + case '%': rc = COND_PERCENT; break; + case ' ': rc = COND_SPACE; break; + case '=': rc = COND_EQ; break; + break; + + case '~': + case '<': + case '>': + rc = COND_GetOperator( cond ); + if (!rc) + rc = COND_ERROR; + return rc; + default: + rc = 0; + } + + if ( rc ) + { + cond->n += len; + return rc; + } + + if (ch == '"' ) + { + LPCWSTR p = strchrW( str->data + 1, '"' ); + if (!p) + return COND_ERROR; + len = p - str->data + 1; + rc = COND_LITER; + } + else if( COND_IsAlpha( ch ) ) + { + static const WCHAR szNot[] = {'N','O','T',0}; + static const WCHAR szAnd[] = {'A','N','D',0}; + static const WCHAR szXor[] = {'X','O','R',0}; + static const WCHAR szEqv[] = {'E','Q','V',0}; + static const WCHAR szImp[] = {'I','M','P',0}; + static const WCHAR szOr[] = {'O','R',0}; + + while( COND_IsIdent( str->data[len] ) ) + len++; + rc = COND_IDENT; + + if ( len == 3 ) + { + if ( !strncmpiW( str->data, szNot, len ) ) + rc = COND_NOT; + else if( !strncmpiW( str->data, szAnd, len ) ) + rc = COND_AND; + else if( !strncmpiW( str->data, szXor, len ) ) + rc = COND_XOR; + else if( !strncmpiW( str->data, szEqv, len ) ) + rc = COND_EQV; + else if( !strncmpiW( str->data, szImp, len ) ) + rc = COND_IMP; + } + else if( (len == 2) && !strncmpiW( str->data, szOr, len ) ) + rc = COND_OR; + } + else if( COND_IsNumber( ch ) ) + { + while( COND_IsNumber( str->data[len] ) ) + len++; + rc = COND_NUMBER; + } + else + { + ERR("Got unknown character %c(%x)\n",ch,ch); + return COND_ERROR; + } + + cond->n += len; + str->len = len; + + return rc; +} + +static int COND_lex( void *COND_lval, COND_input *cond ) +{ + int rc; + struct cond_str *str = COND_lval; + + do { + rc = COND_GetOne( str, cond ); + } while (rc == COND_SPACE); + + return rc; +} + +static LPWSTR COND_GetString( struct cond_str *str ) +{ + LPWSTR ret; + + ret = msi_alloc( (str->len+1) * sizeof (WCHAR) ); + if( ret ) + { + memcpy( ret, str->data, str->len * sizeof(WCHAR)); + ret[str->len]=0; + } + TRACE("Got identifier %s\n",debugstr_w(ret)); + return ret; +} + +static LPWSTR COND_GetLiteral( struct cond_str *str ) +{ + LPWSTR ret; + + ret = msi_alloc( (str->len-1) * sizeof (WCHAR) ); + if( ret ) + { + memcpy( ret, str->data+1, (str->len-2) * sizeof(WCHAR) ); + ret[str->len - 2]=0; + } + TRACE("Got literal %s\n",debugstr_w(ret)); + return ret; +} + +static int COND_error(const char *str) +{ + TRACE("%s\n", str ); + return 0; +} + +MSICONDITION MSI_EvaluateConditionW( MSIPACKAGE *package, LPCWSTR szCondition ) +{ + COND_input cond; + MSICONDITION r; + + TRACE("%s\n", debugstr_w( szCondition ) ); + + if ( szCondition == NULL ) + return MSICONDITION_NONE; + + cond.package = package; + cond.str = szCondition; + cond.n = 0; + cond.result = MSICONDITION_ERROR; + + if ( !COND_parse( &cond ) ) + r = cond.result; + else + r = MSICONDITION_ERROR; + + TRACE("%i <- %s\n", r, debugstr_w(szCondition)); + return r; +} + +MSICONDITION WINAPI MsiEvaluateConditionW( MSIHANDLE hInstall, LPCWSTR szCondition ) +{ + MSIPACKAGE *package; + UINT ret; + + package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE); + if( !package) + return MSICONDITION_ERROR; + ret = MSI_EvaluateConditionW( package, szCondition ); + msiobj_release( &package->hdr ); + return ret; +} + +MSICONDITION WINAPI MsiEvaluateConditionA( MSIHANDLE hInstall, LPCSTR szCondition ) +{ + LPWSTR szwCond = NULL; + MSICONDITION r; + + szwCond = strdupAtoW( szCondition ); + if( szCondition && !szwCond ) + return MSICONDITION_ERROR; + + r = MsiEvaluateConditionW( hInstall, szwCond ); + msi_free( szwCond ); + return r; +} diff --git a/reactos/dll/msi/create.c b/reactos/dll/msi/create.c new file mode 100644 index 00000000000..fb93c626bef --- /dev/null +++ b/reactos/dll/msi/create.c @@ -0,0 +1,245 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002-2004 Mike McCormack 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" +#include "winnls.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msidb); + + +/* below is the query interface to a table */ + +typedef struct tagMSICREATEVIEW +{ + MSIVIEW view; + MSIDATABASE *db; + LPWSTR name; + BOOL bIsTemp; + column_info *col_info; +} MSICREATEVIEW; + +static UINT CREATE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ) +{ + MSICREATEVIEW *cv = (MSICREATEVIEW*)view; + + TRACE("%p %d %d %p\n", cv, row, col, val ); + + return ERROR_FUNCTION_FAILED; +} + +static UINT CREATE_execute( struct tagMSIVIEW *view, MSIRECORD *record ) +{ + MSICREATEVIEW *cv = (MSICREATEVIEW*)view; + column_info *col; + UINT r, nField; + static const WCHAR szTables[] = { '_','T','a','b','l','e','s',0 }; + static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 }; + MSIVIEW *tv = NULL; + MSIRECORD *rec = NULL; + + TRACE("%p Table %s (%s)\n", cv, debugstr_w(cv->name), + cv->bIsTemp?"temporary":"permanent"); + + /* only add tables that don't exist already */ + if( TABLE_Exists(cv->db, cv->name ) ) + return ERROR_BAD_QUERY_SYNTAX; + + r = TABLE_CreateView( cv->db, szTables, &tv ); + TRACE("CreateView returned %x\n", r); + if( r ) + return r; + + r = tv->ops->execute( tv, 0 ); + TRACE("tv execute returned %x\n", r); + if( r ) + goto err; + + rec = MSI_CreateRecord( 1 ); + if( !rec ) + goto err; + + r = MSI_RecordSetStringW( rec, 1, cv->name ); + if( r ) + goto err; + + r = tv->ops->insert_row( tv, rec ); + TRACE("insert_row returned %x\n", r); + if( r ) + goto err; + + tv->ops->delete( tv ); + tv = NULL; + + msiobj_release( &rec->hdr ); + + /* add each column to the _Columns table */ + r = TABLE_CreateView( cv->db, szColumns, &tv ); + if( r ) + return r; + + r = tv->ops->execute( tv, 0 ); + TRACE("tv execute returned %x\n", r); + if( r ) + goto err; + + rec = MSI_CreateRecord( 4 ); + if( !rec ) + goto err; + + r = MSI_RecordSetStringW( rec, 1, cv->name ); + if( r ) + goto err; + + /* + * need to set the table, column number, col name and type + * for each column we enter in the table + */ + nField = 1; + for( col = cv->col_info; col; col = col->next ) + { + r = MSI_RecordSetInteger( rec, 2, nField ); + if( r ) + goto err; + + r = MSI_RecordSetStringW( rec, 3, col->column ); + if( r ) + goto err; + + r = MSI_RecordSetInteger( rec, 4, col->type ); + if( r ) + goto err; + + r = tv->ops->insert_row( tv, rec ); + if( r ) + goto err; + + nField++; + } + if( !col ) + r = ERROR_SUCCESS; + +err: + if (rec) + msiobj_release( &rec->hdr ); + /* FIXME: remove values from the string table on error */ + if( tv ) + tv->ops->delete( tv ); + return r; +} + +static UINT CREATE_close( struct tagMSIVIEW *view ) +{ + MSICREATEVIEW *cv = (MSICREATEVIEW*)view; + + TRACE("%p\n", cv); + + return ERROR_SUCCESS; +} + +static UINT CREATE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols ) +{ + MSICREATEVIEW *cv = (MSICREATEVIEW*)view; + + TRACE("%p %p %p\n", cv, rows, cols ); + + return ERROR_FUNCTION_FAILED; +} + +static UINT CREATE_get_column_info( struct tagMSIVIEW *view, + UINT n, LPWSTR *name, UINT *type ) +{ + MSICREATEVIEW *cv = (MSICREATEVIEW*)view; + + TRACE("%p %d %p %p\n", cv, n, name, type ); + + return ERROR_FUNCTION_FAILED; +} + +static UINT CREATE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, + MSIRECORD *rec) +{ + MSICREATEVIEW *cv = (MSICREATEVIEW*)view; + + TRACE("%p %d %p\n", cv, eModifyMode, rec ); + + return ERROR_FUNCTION_FAILED; +} + +static UINT CREATE_delete( struct tagMSIVIEW *view ) +{ + MSICREATEVIEW *cv = (MSICREATEVIEW*)view; + + TRACE("%p\n", cv ); + + msiobj_release( &cv->db->hdr ); + msi_free( cv ); + + return ERROR_SUCCESS; +} + + +MSIVIEWOPS create_ops = +{ + CREATE_fetch_int, + NULL, + NULL, + NULL, + CREATE_execute, + CREATE_close, + CREATE_get_dimensions, + CREATE_get_column_info, + CREATE_modify, + CREATE_delete +}; + +UINT CREATE_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR table, + column_info *col_info, BOOL temp ) +{ + MSICREATEVIEW *cv = NULL; + + TRACE("%p\n", cv ); + + cv = msi_alloc_zero( sizeof *cv ); + if( !cv ) + return ERROR_FUNCTION_FAILED; + + /* fill the structure */ + cv->view.ops = &create_ops; + msiobj_addref( &db->hdr ); + cv->db = db; + cv->name = table; + cv->col_info = col_info; + cv->bIsTemp = temp; + *view = (MSIVIEW*) cv; + + return ERROR_SUCCESS; +} diff --git a/reactos/dll/msi/custom.c b/reactos/dll/msi/custom.c new file mode 100644 index 00000000000..0238066e241 --- /dev/null +++ b/reactos/dll/msi/custom.c @@ -0,0 +1,783 @@ +/* + * Custom Action processing for the Microsoft Installer (msi.dll) + * + * Copyright 2005 Aric Stewart 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Pages I need + * +http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/summary_list_of_all_custom_action_types.asp + */ + +#include +#include + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "winreg.h" +#include "wine/debug.h" +#include "fdi.h" +#include "msi.h" +#include "msidefs.h" +#include "msiquery.h" +#include "fcntl.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" +#include "winnls.h" +#include "winuser.h" +#include "shlobj.h" +#include "wine/unicode.h" +#include "winver.h" +#include "action.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +#define CUSTOM_ACTION_TYPE_MASK 0x3F +static const WCHAR c_collen[] = {'C',':','\\',0}; +static const WCHAR cszTempFolder[]= {'T','e','m','p','F','o','l','d','e','r',0}; + +typedef struct tagMSIRUNNINGACTION +{ + struct list entry; + HANDLE handle; + BOOL process; + LPWSTR name; +} MSIRUNNINGACTION; + +static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source, + LPCWSTR target, const INT type, LPCWSTR action); +static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source, + LPCWSTR target, const INT type, LPCWSTR action); +static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source, + LPCWSTR target, const INT type, LPCWSTR action); +static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source, + LPCWSTR target, const INT type, LPCWSTR action); +static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source, + LPCWSTR target, const INT type, LPCWSTR action); +static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source, + LPCWSTR target, const INT type, LPCWSTR action); + + +static BOOL check_execution_scheduling_options(MSIPACKAGE *package, LPCWSTR action, UINT options) +{ + if (!package->script) + return TRUE; + + if ((options & msidbCustomActionTypeClientRepeat) == + msidbCustomActionTypeClientRepeat) + { + if (!(package->script->InWhatSequence & SEQUENCE_UI && + package->script->InWhatSequence & SEQUENCE_EXEC)) + { + TRACE("Skipping action due to dbCustomActionTypeClientRepeat option.\n"); + return FALSE; + } + } + else if (options & msidbCustomActionTypeFirstSequence) + { + if (package->script->InWhatSequence & SEQUENCE_UI && + package->script->InWhatSequence & SEQUENCE_EXEC ) + { + TRACE("Skipping action due to msidbCustomActionTypeFirstSequence option.\n"); + return FALSE; + } + } + else if (options & msidbCustomActionTypeOncePerProcess) + { + if (check_unique_action(package,action)) + { + TRACE("Skipping action due to msidbCustomActionTypeOncePerProcess option.\n"); + return FALSE; + } + else + register_unique_action(package,action); + } + + return TRUE; +} + +UINT ACTION_CustomAction(MSIPACKAGE *package,LPCWSTR action, BOOL execute) +{ + UINT rc = ERROR_SUCCESS; + MSIRECORD * row = 0; + static const WCHAR ExecSeqQuery[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','C','u','s','t','o' ,'m','A','c','t','i','o','n','`', + ' ','W','H','E','R','E',' ','`','A','c','t','i' ,'o','n','`',' ', + '=',' ','\'','%','s','\'',0}; + UINT type; + LPCWSTR source, target; + WCHAR *deformated=NULL; + + row = MSI_QueryGetRecord( package->db, ExecSeqQuery, action ); + if (!row) + return ERROR_CALL_NOT_IMPLEMENTED; + + type = MSI_RecordGetInteger(row,2); + + source = MSI_RecordGetString(row,3); + target = MSI_RecordGetString(row,4); + + TRACE("Handling custom action %s (%x %s %s)\n",debugstr_w(action),type, + debugstr_w(source), debugstr_w(target)); + + /* handle some of the deferred actions */ + if (type & msidbCustomActionTypeTSAware) + FIXME("msidbCustomActionTypeTSAware not handled\n"); + + if (type & msidbCustomActionTypeInScript) + { + if (type & msidbCustomActionTypeNoImpersonate) + FIXME("msidbCustomActionTypeNoImpersonate not handled\n"); + + if (type & msidbCustomActionTypeRollback) + { + FIXME("Rollback only action... rollbacks not supported yet\n"); + schedule_action(package, ROLLBACK_SCRIPT, action); + rc = ERROR_SUCCESS; + goto end; + } + if (!execute) + { + if (type & msidbCustomActionTypeCommit) + { + TRACE("Deferring Commit Action!\n"); + schedule_action(package, COMMIT_SCRIPT, action); + } + else + { + TRACE("Deferring Action!\n"); + schedule_action(package, INSTALL_SCRIPT, action); + } + + rc = ERROR_SUCCESS; + goto end; + } + else + { + /*Set ActionData*/ + + static const WCHAR szActionData[] = { + 'C','u','s','t','o','m','A','c','t','i','o','n','D','a','t','a',0}; + static const WCHAR szBlank[] = {0}; + LPWSTR actiondata = msi_dup_property( package, action ); + if (actiondata) + MSI_SetPropertyW(package,szActionData,actiondata); + else + MSI_SetPropertyW(package,szActionData,szBlank); + msi_free(actiondata); + } + } + else if (!check_execution_scheduling_options(package,action,type)) + { + rc = ERROR_SUCCESS; + goto end; + } + + switch (type & CUSTOM_ACTION_TYPE_MASK) + { + case 1: /* DLL file stored in a Binary table stream */ + rc = HANDLE_CustomType1(package,source,target,type,action); + break; + case 2: /* EXE file stored in a Binary table strem */ + rc = HANDLE_CustomType2(package,source,target,type,action); + break; + case 18: /*EXE file installed with package */ + rc = HANDLE_CustomType18(package,source,target,type,action); + break; + case 19: /* Error that halts install */ + rc = HANDLE_CustomType19(package,source,target,type,action); + break; + case 50: /*EXE file specified by a property value */ + rc = HANDLE_CustomType50(package,source,target,type,action); + break; + case 34: /*EXE to be run in specified directory */ + rc = HANDLE_CustomType34(package,source,target,type,action); + break; + case 35: /* Directory set with formatted text. */ + deformat_string(package,target,&deformated); + MSI_SetTargetPathW(package, source, deformated); + msi_free(deformated); + break; + case 51: /* Property set with formatted text. */ + deformat_string(package,target,&deformated); + rc = MSI_SetPropertyW(package,source,deformated); + msi_free(deformated); + break; + default: + FIXME("UNHANDLED ACTION TYPE %i (%s %s)\n", + type & CUSTOM_ACTION_TYPE_MASK, debugstr_w(source), + debugstr_w(target)); + } + +end: + msiobj_release(&row->hdr); + return rc; +} + + +static UINT store_binary_to_temp(MSIPACKAGE *package, LPCWSTR source, + LPWSTR tmp_file) +{ + DWORD sz=MAX_PATH; + static const WCHAR f1[] = {'m','s','i',0}; + WCHAR fmt[MAX_PATH]; + + if (MSI_GetPropertyW(package, cszTempFolder, fmt, &sz) != ERROR_SUCCESS) + GetTempPathW(MAX_PATH,fmt); + + if (GetTempFileNameW(fmt,f1,0,tmp_file) == 0) + { + TRACE("Unable to create file\n"); + return ERROR_FUNCTION_FAILED; + } + else + { + /* write out the file */ + UINT rc; + MSIRECORD * row = 0; + static const WCHAR fmt[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','B','i' ,'n','a','r','y','`',' ','W','H','E','R','E', + ' ','`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0}; + HANDLE the_file; + CHAR buffer[1024]; + + the_file = CreateFileW(tmp_file, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + + if (the_file == INVALID_HANDLE_VALUE) + return ERROR_FUNCTION_FAILED; + + row = MSI_QueryGetRecord(package->db, fmt, source); + if (!row) + return ERROR_FUNCTION_FAILED; + + do + { + DWORD write; + sz = 1024; + rc = MSI_RecordReadStream(row,2,buffer,&sz); + if (rc != ERROR_SUCCESS) + { + ERR("Failed to get stream\n"); + CloseHandle(the_file); + DeleteFileW(tmp_file); + break; + } + WriteFile(the_file,buffer,sz,&write,NULL); + } while (sz == 1024); + + CloseHandle(the_file); + + msiobj_release(&row->hdr); + } + + return ERROR_SUCCESS; +} + +static void file_running_action(MSIPACKAGE* package, HANDLE Handle, + BOOL process, LPCWSTR name) +{ + MSIRUNNINGACTION *action; + + action = msi_alloc( sizeof(MSIRUNNINGACTION) ); + + action->handle = Handle; + action->process = process; + action->name = strdupW(name); + + list_add_tail( &package->RunningActions, &action->entry ); +} + +static UINT process_action_return_value(UINT type, HANDLE ThreadHandle) +{ + DWORD rc=0; + + if (type == 2) + { + GetExitCodeProcess(ThreadHandle,&rc); + + if (rc == 0) + return ERROR_SUCCESS; + else + return ERROR_FUNCTION_FAILED; + } + + GetExitCodeThread(ThreadHandle,&rc); + + switch (rc) + { + case ERROR_FUNCTION_NOT_CALLED: + case ERROR_SUCCESS: + case ERROR_INSTALL_USEREXIT: + case ERROR_INSTALL_FAILURE: + return rc; + case ERROR_NO_MORE_ITEMS: + return ERROR_SUCCESS; + default: + ERR("Invalid Return Code %ld\n",rc); + return ERROR_INSTALL_FAILURE; + } +} + +static UINT process_handle(MSIPACKAGE* package, UINT type, + HANDLE ThreadHandle, HANDLE ProcessHandle, + LPCWSTR Name, BOOL *finished) +{ + UINT rc = ERROR_SUCCESS; + + if (!(type & msidbCustomActionTypeAsync)) + { + /* synchronous */ + TRACE("Synchronous Execution of action %s\n",debugstr_w(Name)); + if (ProcessHandle) + msi_dialog_check_messages(ProcessHandle); + else + msi_dialog_check_messages(ThreadHandle); + + if (!(type & msidbCustomActionTypeContinue)) + { + if (ProcessHandle) + rc = process_action_return_value(2,ProcessHandle); + else + rc = process_action_return_value(1,ThreadHandle); + } + + CloseHandle(ThreadHandle); + if (ProcessHandle) + CloseHandle(ProcessHandle); + if (finished) + *finished = TRUE; + } + else + { + TRACE("Asynchronous Execution of action %s\n",debugstr_w(Name)); + /* asynchronous */ + if (type & msidbCustomActionTypeContinue) + { + if (ProcessHandle) + { + file_running_action(package, ProcessHandle, TRUE, Name); + CloseHandle(ThreadHandle); + } + else + file_running_action(package, ThreadHandle, FALSE, Name); + } + else + { + CloseHandle(ThreadHandle); + if (ProcessHandle) + CloseHandle(ProcessHandle); + } + if (finished) + *finished = FALSE; + } + + return rc; +} + + +typedef UINT __stdcall CustomEntry(MSIHANDLE); + +typedef struct +{ + MSIPACKAGE *package; + WCHAR *target; + WCHAR *source; +} thread_struct; + +static DWORD WINAPI ACTION_CallDllFunction(thread_struct *stuff) +{ + HANDLE hModule; + LPSTR proc; + CustomEntry *fn; + DWORD rc = ERROR_SUCCESS; + + TRACE("calling function (%s, %s)\n", debugstr_w(stuff->source), + debugstr_w(stuff->target)); + + hModule = LoadLibraryW(stuff->source); + if (hModule) + { + proc = strdupWtoA( stuff->target ); + fn = (CustomEntry*)GetProcAddress(hModule,proc); + if (fn) + { + MSIHANDLE hPackage; + MSIPACKAGE *package = stuff->package; + + TRACE("Calling function %s\n", proc); + hPackage = alloc_msihandle( &package->hdr ); + if (hPackage) + { + rc = fn( hPackage ); + MsiCloseHandle( hPackage ); + } + else + ERR("Handle for object %p not found\n", package ); + } + else + ERR("Cannot load functon\n"); + + msi_free(proc); + FreeLibrary(hModule); + } + else + ERR("Unable to load library\n"); + msiobj_release( &stuff->package->hdr ); + msi_free(stuff->source); + msi_free(stuff->target); + msi_free(stuff); + return rc; +} + +static DWORD WINAPI DllThread(LPVOID info) +{ + thread_struct *stuff; + DWORD rc = 0; + + TRACE("MSI Thread (0x%lx) started for custom action\n", + GetCurrentThreadId()); + + stuff = (thread_struct*)info; + rc = ACTION_CallDllFunction(stuff); + + TRACE("MSI Thread (0x%lx) finished (rc %li)\n",GetCurrentThreadId(), rc); + /* clse all handles for this thread */ + MsiCloseAllHandles(); + return rc; +} + +static UINT HANDLE_CustomType1(MSIPACKAGE *package, LPCWSTR source, + LPCWSTR target, const INT type, LPCWSTR action) +{ + WCHAR tmp_file[MAX_PATH]; + thread_struct *info; + DWORD ThreadId; + HANDLE ThreadHandle; + UINT rc = ERROR_SUCCESS; + BOOL finished = FALSE; + + store_binary_to_temp(package, source, tmp_file); + + TRACE("Calling function %s from %s\n",debugstr_w(target), + debugstr_w(tmp_file)); + + if (!strchrW(tmp_file,'.')) + { + static const WCHAR dot[]={'.',0}; + strcatW(tmp_file,dot); + } + + info = msi_alloc( sizeof(*info) ); + msiobj_addref( &package->hdr ); + info->package = package; + info->target = strdupW(target); + info->source = strdupW(tmp_file); + + ThreadHandle = CreateThread(NULL,0,DllThread,(LPVOID)info,0,&ThreadId); + + rc = process_handle(package, type, ThreadHandle, NULL, action, &finished ); + + if (!finished) + track_tempfile(package, tmp_file, tmp_file); + else + DeleteFileW(tmp_file); + + return rc; +} + +static UINT HANDLE_CustomType2(MSIPACKAGE *package, LPCWSTR source, + LPCWSTR target, const INT type, LPCWSTR action) +{ + WCHAR tmp_file[MAX_PATH]; + STARTUPINFOW si; + PROCESS_INFORMATION info; + BOOL rc; + INT len; + WCHAR *deformated; + WCHAR *cmd; + static const WCHAR spc[] = {' ',0}; + UINT prc = ERROR_SUCCESS; + BOOL finished = FALSE; + + memset(&si,0,sizeof(STARTUPINFOW)); + + store_binary_to_temp(package, source, tmp_file); + + deformat_string(package,target,&deformated); + + len = strlenW(tmp_file)+2; + + if (deformated) + len += strlenW(deformated); + + cmd = msi_alloc(sizeof(WCHAR)*len); + + strcpyW(cmd,tmp_file); + if (deformated) + { + strcatW(cmd,spc); + strcatW(cmd,deformated); + + msi_free(deformated); + } + + TRACE("executing exe %s\n", debugstr_w(cmd)); + + rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL, + c_collen, &si, &info); + + + if ( !rc ) + { + ERR("Unable to execute command %s\n", debugstr_w(cmd)); + msi_free(cmd); + return ERROR_SUCCESS; + } + msi_free(cmd); + + prc = process_handle(package, type, info.hThread, info.hProcess, action, + &finished); + + if (!finished) + track_tempfile(package, tmp_file, tmp_file); + else + DeleteFileW(tmp_file); + + return prc; +} + +static UINT HANDLE_CustomType18(MSIPACKAGE *package, LPCWSTR source, + LPCWSTR target, const INT type, LPCWSTR action) +{ + STARTUPINFOW si; + PROCESS_INFORMATION info; + BOOL rc; + WCHAR *deformated; + WCHAR *cmd; + INT len; + static const WCHAR spc[] = {' ',0}; + MSIFILE *file; + UINT prc; + + memset(&si,0,sizeof(STARTUPINFOW)); + + file = get_loaded_file(package,source); + if( !file ) + return ERROR_FUNCTION_FAILED; + + len = lstrlenW( file->TargetPath ); + + deformat_string(package,target,&deformated); + if (deformated) + len += strlenW(deformated); + len += 2; + + cmd = msi_alloc(len * sizeof(WCHAR)); + + lstrcpyW( cmd, file->TargetPath); + if (deformated) + { + strcatW(cmd, spc); + strcatW(cmd, deformated); + + msi_free(deformated); + } + + TRACE("executing exe %s\n", debugstr_w(cmd)); + + rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL, + c_collen, &si, &info); + + + if ( !rc ) + { + ERR("Unable to execute command %s\n", debugstr_w(cmd)); + msi_free(cmd); + return ERROR_SUCCESS; + } + msi_free(cmd); + + prc = process_handle(package, type, info.hThread, info.hProcess, action, + NULL); + + return prc; +} + +static UINT HANDLE_CustomType19(MSIPACKAGE *package, LPCWSTR source, + LPCWSTR target, const INT type, LPCWSTR action) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ', + 'F','R','O','M',' ','`','E','r','r','o','r','`',' ', + 'W','H','E','R','E',' ','`','E','r','r','o','r','`',' ','=',' ', + '%','s',0 + }; + MSIRECORD *row = 0; + LPWSTR deformated = NULL; + + deformat_string( package, target, &deformated ); + + /* first try treat the error as a number */ + row = MSI_QueryGetRecord( package->db, query, deformated ); + if( row ) + { + LPCWSTR error = MSI_RecordGetString( row, 1 ); + MessageBoxW( NULL, error, NULL, MB_OK ); + msiobj_release( &row->hdr ); + } + else + MessageBoxW( NULL, deformated, NULL, MB_OK ); + + msi_free( deformated ); + + return ERROR_FUNCTION_FAILED; +} + +static UINT HANDLE_CustomType50(MSIPACKAGE *package, LPCWSTR source, + LPCWSTR target, const INT type, LPCWSTR action) +{ + STARTUPINFOW si; + PROCESS_INFORMATION info; + WCHAR *prop; + BOOL rc; + WCHAR *deformated; + WCHAR *cmd; + INT len; + static const WCHAR spc[] = {' ',0}; + + memset(&si,0,sizeof(STARTUPINFOW)); + memset(&info,0,sizeof(PROCESS_INFORMATION)); + + prop = msi_dup_property( package, source ); + if (!prop) + return ERROR_SUCCESS; + + deformat_string(package,target,&deformated); + len = strlenW(prop) + 2; + if (deformated) + len += strlenW(deformated); + + cmd = msi_alloc(sizeof(WCHAR)*len); + + strcpyW(cmd,prop); + if (deformated) + { + strcatW(cmd,spc); + strcatW(cmd,deformated); + + msi_free(deformated); + } + msi_free(prop); + + TRACE("executing exe %s\n", debugstr_w(cmd)); + + rc = CreateProcessW(NULL, cmd, NULL, NULL, FALSE, 0, NULL, + c_collen, &si, &info); + + + if ( !rc ) + { + ERR("Unable to execute command %s\n", debugstr_w(cmd)); + msi_free(cmd); + return ERROR_SUCCESS; + } + msi_free(cmd); + + return process_handle(package, type, info.hThread, info.hProcess, action, NULL); +} + +static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source, + LPCWSTR target, const INT type, LPCWSTR action) +{ + LPWSTR filename, deformated; + STARTUPINFOW si; + PROCESS_INFORMATION info; + BOOL rc; + UINT prc; + + memset(&si,0,sizeof(STARTUPINFOW)); + + filename = resolve_folder(package, source, FALSE, FALSE, NULL); + + if (!filename) + return ERROR_FUNCTION_FAILED; + + SetCurrentDirectoryW(filename); + msi_free(filename); + + deformat_string(package,target,&deformated); + + if (!deformated) + return ERROR_FUNCTION_FAILED; + + TRACE("executing exe %s\n", debugstr_w(deformated)); + + rc = CreateProcessW(NULL, deformated, NULL, NULL, FALSE, 0, NULL, + c_collen, &si, &info); + + if ( !rc ) + { + ERR("Unable to execute command %s\n", debugstr_w(deformated)); + msi_free(deformated); + return ERROR_SUCCESS; + } + msi_free(deformated); + + prc = process_handle(package, type, info.hThread, info.hProcess, action, + NULL); + + return prc; +} + + +void ACTION_FinishCustomActions(MSIPACKAGE* package) +{ + struct list *item, *cursor; + DWORD rc; + + LIST_FOR_EACH_SAFE( item, cursor, &package->RunningActions ) + { + MSIRUNNINGACTION *action = LIST_ENTRY( item, MSIRUNNINGACTION, entry ); + + TRACE("Checking on action %s\n", debugstr_w(action->name)); + + list_remove( &action->entry ); + + if (action->process) + GetExitCodeProcess( action->handle, &rc ); + else + GetExitCodeThread( action->handle, &rc ); + + if (rc == STILL_ACTIVE) + { + TRACE("Waiting on action %s\n", debugstr_w( action->name) ); + msi_dialog_check_messages( action->handle ); + } + + CloseHandle( action->handle ); + msi_free( action->name ); + msi_free( action ); + } +} diff --git a/reactos/dll/msi/database.c b/reactos/dll/msi/database.c new file mode 100644 index 00000000000..4da53381cf8 --- /dev/null +++ b/reactos/dll/msi/database.c @@ -0,0 +1,346 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002,2003,2004,2005 Mike McCormack 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#define COBJMACROS +#define NONAMELESSUNION + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winnls.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "msipriv.h" +#include "objidl.h" +#include "objbase.h" + +#include "initguid.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +DEFINE_GUID( CLSID_MsiDatabase, 0x000c1084, 0x0000, 0x0000, + 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46); +DEFINE_GUID( CLSID_MsiPatch, 0x000c1086, 0x0000, 0x0000, + 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46); + +/* + * .MSI file format + * + * An .msi file is a structured storage file. + * It contains a number of streams. + * A stream for each table in the database. + * Two streams for the string table in the database. + * Any binary data in a table is a reference to a stream. + */ + +static VOID MSI_CloseDatabase( MSIOBJECTHDR *arg ) +{ + MSIDATABASE *db = (MSIDATABASE *) arg; + DWORD r; + + free_cached_tables( db ); + msi_free_transforms( db ); + msi_destroy_stringtable( db->strings ); + r = IStorage_Release( db->storage ); + if( r ) + ERR("database reference count was not zero (%ld)\n", r); +} + +UINT MSI_OpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIDATABASE **pdb) +{ + IStorage *stg = NULL; + HRESULT r; + MSIDATABASE *db = NULL; + UINT ret = ERROR_FUNCTION_FAILED; + LPCWSTR szMode; + STATSTG stat; + + TRACE("%s %s\n",debugstr_w(szDBPath),debugstr_w(szPersist) ); + + if( !pdb ) + return ERROR_INVALID_PARAMETER; + + szMode = szPersist; + if( HIWORD( szPersist ) ) + { + /* UINT len = lstrlenW( szPerist ) + 1; */ + FIXME("don't support persist files yet\b"); + return ERROR_INVALID_PARAMETER; + /* szMode = msi_alloc( len * sizeof (DWORD) ); */ + } + else if( szPersist == MSIDBOPEN_READONLY ) + { + r = StgOpenStorage( szDBPath, NULL, + STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg); + } + else if( szPersist == MSIDBOPEN_CREATE ) + { + r = StgCreateDocfile( szDBPath, + STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, 0, &stg); + if( r == ERROR_SUCCESS ) + { + IStorage_SetClass( stg, &CLSID_MsiDatabase ); + r = init_string_table( stg ); + } + } + else if( szPersist == MSIDBOPEN_TRANSACT ) + { + r = StgOpenStorage( szDBPath, NULL, + STGM_DIRECT|STGM_READWRITE|STGM_SHARE_EXCLUSIVE, NULL, 0, &stg); + } + else + { + ERR("unknown flag %p\n",szPersist); + return ERROR_INVALID_PARAMETER; + } + + if( FAILED( r ) ) + { + FIXME("open failed r = %08lx!\n",r); + return ERROR_FUNCTION_FAILED; + } + + r = IStorage_Stat( stg, &stat, STATFLAG_NONAME ); + if( FAILED( r ) ) + { + FIXME("Failed to stat storage\n"); + goto end; + } + + if ( !IsEqualGUID( &stat.clsid, &CLSID_MsiDatabase ) && + !IsEqualGUID( &stat.clsid, &CLSID_MsiPatch ) ) + { + ERR("storage GUID is not a MSI database GUID %s\n", + debugstr_guid(&stat.clsid) ); + goto end; + } + + db = alloc_msiobject( MSIHANDLETYPE_DATABASE, sizeof (MSIDATABASE), + MSI_CloseDatabase ); + if( !db ) + { + FIXME("Failed to allocate a handle\n"); + goto end; + } + + if( TRACE_ON( msi ) ) + enum_stream_names( stg ); + + db->storage = stg; + db->mode = szMode; + list_init( &db->tables ); + list_init( &db->transforms ); + + db->strings = load_string_table( stg ); + if( !db->strings ) + goto end; + + ret = ERROR_SUCCESS; + + msiobj_addref( &db->hdr ); + IStorage_AddRef( stg ); + *pdb = db; + +end: + if( db ) + msiobj_release( &db->hdr ); + if( stg ) + IStorage_Release( stg ); + + return ret; +} + +UINT WINAPI MsiOpenDatabaseW(LPCWSTR szDBPath, LPCWSTR szPersist, MSIHANDLE *phDB) +{ + MSIDATABASE *db; + UINT ret; + + TRACE("%s %s %p\n",debugstr_w(szDBPath),debugstr_w(szPersist), phDB); + + ret = MSI_OpenDatabaseW( szDBPath, szPersist, &db ); + if( ret == ERROR_SUCCESS ) + { + *phDB = alloc_msihandle( &db->hdr ); + msiobj_release( &db->hdr ); + } + + return ret; +} + +UINT WINAPI MsiOpenDatabaseA(LPCSTR szDBPath, LPCSTR szPersist, MSIHANDLE *phDB) +{ + HRESULT r = ERROR_FUNCTION_FAILED; + LPWSTR szwDBPath = NULL, szwPersist = NULL; + + TRACE("%s %s %p\n", debugstr_a(szDBPath), debugstr_a(szPersist), phDB); + + if( szDBPath ) + { + szwDBPath = strdupAtoW( szDBPath ); + if( !szwDBPath ) + goto end; + } + + if( HIWORD(szPersist) ) + { + szwPersist = strdupAtoW( szPersist ); + if( !szwPersist ) + goto end; + } + else + szwPersist = (LPWSTR)(DWORD)szPersist; + + r = MsiOpenDatabaseW( szwDBPath, szwPersist, phDB ); + +end: + if( HIWORD(szPersist) ) + msi_free( szwPersist ); + msi_free( szwDBPath ); + + return r; +} + +UINT MSI_DatabaseImport( MSIDATABASE *db, LPCWSTR folder, LPCWSTR file ) +{ + FIXME("%p %s %s\n", db, debugstr_w(folder), debugstr_w(file) ); + + if( folder == NULL || file == NULL ) + return ERROR_INVALID_PARAMETER; + + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiDatabaseImportW(MSIHANDLE handle, LPCWSTR szFolder, LPCWSTR szFilename) +{ + MSIDATABASE *db; + UINT r; + + TRACE("%lx %s %s\n",handle,debugstr_w(szFolder), debugstr_w(szFilename)); + + db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE ); + if( !db ) + return ERROR_INVALID_HANDLE; + r = MSI_DatabaseImport( db, szFolder, szFilename ); + msiobj_release( &db->hdr ); + return r; +} + +UINT WINAPI MsiDatabaseImportA( MSIHANDLE handle, + LPCSTR szFolder, LPCSTR szFilename ) +{ + LPWSTR path = NULL, file = NULL; + UINT r = ERROR_OUTOFMEMORY; + + TRACE("%lx %s %s\n", handle, debugstr_a(szFolder), debugstr_a(szFilename)); + + if( szFolder ) + { + path = strdupAtoW( szFolder ); + if( !path ) + goto end; + } + + if( szFilename ) + { + file = strdupAtoW( szFilename ); + if( !file ) + goto end; + } + + r = MsiDatabaseImportW( handle, path, file ); + +end: + msi_free( path ); + msi_free( file ); + + return r; +} + +UINT MSI_DatabaseExport( MSIDATABASE *db, LPCWSTR table, + LPCWSTR folder, LPCWSTR file ) +{ + FIXME("%p %s %s %s\n", db, debugstr_w(table), + debugstr_w(folder), debugstr_w(file) ); + + if( folder == NULL || file == NULL ) + return ERROR_INVALID_PARAMETER; + + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiDatabaseExportW( MSIHANDLE handle, LPCWSTR szTable, + LPCWSTR szFolder, LPCWSTR szFilename ) +{ + MSIDATABASE *db; + UINT r; + + TRACE("%lx %s %s %s\n", handle, debugstr_w(szTable), + debugstr_w(szFolder), debugstr_w(szFilename)); + + db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE ); + if( !db ) + return ERROR_INVALID_HANDLE; + r = MSI_DatabaseExport( db, szTable, szFolder, szFilename ); + msiobj_release( &db->hdr ); + return r; +} + +UINT WINAPI MsiDatabaseExportA( MSIHANDLE handle, LPCSTR szTable, + LPCSTR szFolder, LPCSTR szFilename ) +{ + LPWSTR path = NULL, file = NULL, table = NULL; + UINT r = ERROR_OUTOFMEMORY; + + TRACE("%lx %s %s %s\n", handle, debugstr_a(szTable), + debugstr_a(szFolder), debugstr_a(szFilename)); + + if( szTable ) + { + table = strdupAtoW( szTable ); + if( !table ) + goto end; + } + + if( szFolder ) + { + path = strdupAtoW( szFolder ); + if( !path ) + goto end; + } + + if( szFilename ) + { + file = strdupAtoW( szFilename ); + if( !file ) + goto end; + } + + r = MsiDatabaseExportW( handle, table, path, file ); + +end: + msi_free( table ); + msi_free( path ); + msi_free( file ); + + return r; +} diff --git a/reactos/dll/msi/delete.c b/reactos/dll/msi/delete.c new file mode 100644 index 00000000000..7f6675fe220 --- /dev/null +++ b/reactos/dll/msi/delete.c @@ -0,0 +1,218 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002-2005 Mike McCormack 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" +#include "winnls.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msidb); + + +/* + * Code to delete rows from a table. + * + * We delete rows by blanking them out rather than trying to remove the row. + * This appears to be what the native MSI does (or tries to do). For the query: + * + * delete from Property + * + * some non-zero entries are left in the table by native MSI. I'm not sure if + * that's a bug in the way I'm running the query, or a just a bug. + */ + +typedef struct tagMSIDELETEVIEW +{ + MSIVIEW view; + MSIDATABASE *db; + MSIVIEW *table; +} MSIDELETEVIEW; + +static UINT DELETE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ) +{ + MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view; + + TRACE("%p %d %d %p\n", dv, row, col, val ); + + return ERROR_FUNCTION_FAILED; +} + +static UINT DELETE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm) +{ + MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view; + + TRACE("%p %d %d %p\n", dv, row, col, stm ); + + return ERROR_FUNCTION_FAILED; +} + +static UINT DELETE_set_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT val ) +{ + MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view; + + TRACE("%p %d %d %04x\n", dv, row, col, val ); + + return ERROR_FUNCTION_FAILED; +} + +static UINT DELETE_insert_row( struct tagMSIVIEW *view, MSIRECORD *record ) +{ + MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view; + + TRACE("%p %p\n", dv, record ); + + return ERROR_FUNCTION_FAILED; +} + +static UINT DELETE_execute( struct tagMSIVIEW *view, MSIRECORD *record ) +{ + MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view; + UINT r, i, j, rows = 0, cols = 0; + + TRACE("%p %p\n", dv, record); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + r = dv->table->ops->execute( dv->table, record ); + if( r != ERROR_SUCCESS ) + return r; + + r = dv->table->ops->get_dimensions( dv->table, &rows, &cols ); + if( r != ERROR_SUCCESS ) + return r; + + TRACE("blanking %d rows\n", rows); + + /* blank out all the rows that match */ + for( i=0; itable->ops->set_int( dv->table, i, j, 0 ); + + return ERROR_SUCCESS; +} + +static UINT DELETE_close( struct tagMSIVIEW *view ) +{ + MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view; + + TRACE("%p\n", dv ); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + return dv->table->ops->close( dv->table ); +} + +static UINT DELETE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols ) +{ + MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view; + + TRACE("%p %p %p\n", dv, rows, cols ); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + *rows = 0; + + return dv->table->ops->get_dimensions( dv->table, NULL, cols ); +} + +static UINT DELETE_get_column_info( struct tagMSIVIEW *view, + UINT n, LPWSTR *name, UINT *type ) +{ + MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view; + + TRACE("%p %d %p %p\n", dv, n, name, type ); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + return dv->table->ops->get_column_info( dv->table, n, name, type ); +} + +static UINT DELETE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, + MSIRECORD *rec ) +{ + MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view; + + TRACE("%p %d %p\n", dv, eModifyMode, rec ); + + return ERROR_FUNCTION_FAILED; +} + +static UINT DELETE_delete( struct tagMSIVIEW *view ) +{ + MSIDELETEVIEW *dv = (MSIDELETEVIEW*)view; + + TRACE("%p\n", dv ); + + if( dv->table ) + dv->table->ops->delete( dv->table ); + + msi_free( dv ); + + return ERROR_SUCCESS; +} + + +MSIVIEWOPS delete_ops = +{ + DELETE_fetch_int, + DELETE_fetch_stream, + DELETE_set_int, + DELETE_insert_row, + DELETE_execute, + DELETE_close, + DELETE_get_dimensions, + DELETE_get_column_info, + DELETE_modify, + DELETE_delete +}; + +UINT DELETE_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table ) +{ + MSIDELETEVIEW *dv = NULL; + + TRACE("%p\n", dv ); + + dv = msi_alloc_zero( sizeof *dv ); + if( !dv ) + return ERROR_FUNCTION_FAILED; + + /* fill the structure */ + dv->view.ops = &delete_ops; + dv->db = db; + dv->table = table; + + *view = &dv->view; + + return ERROR_SUCCESS; +} diff --git a/reactos/dll/msi/dialog.c b/reactos/dll/msi/dialog.c new file mode 100644 index 00000000000..1384e229caa --- /dev/null +++ b/reactos/dll/msi/dialog.c @@ -0,0 +1,2259 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2005 Mike McCormack 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define COBJMACROS +#define NONAMELESSUNION +#define NONAMELESSSTRUCT + +#include + +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "winnls.h" +#include "wingdi.h" +#include "msi.h" +#include "msipriv.h" +#include "msidefs.h" +#include "ocidl.h" +#include "olectl.h" +#include "richedit.h" +#include "commctrl.h" + +#include "wine/debug.h" +#include "wine/unicode.h" + +#include "action.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + + +struct msi_control_tag; +typedef struct msi_control_tag msi_control; +typedef UINT (*msi_handler)( msi_dialog *, msi_control *, WPARAM ); + +struct msi_control_tag +{ + struct list entry; + HWND hwnd; + msi_handler handler; + LPWSTR property; + LPWSTR value; + HBITMAP hBitmap; + HICON hIcon; + LPWSTR tabnext; + WCHAR name[1]; +}; + +typedef struct msi_font_tag +{ + struct msi_font_tag *next; + HFONT hfont; + WCHAR name[1]; +} msi_font; + +struct msi_dialog_tag +{ + MSIPACKAGE *package; + msi_dialog_event_handler event_handler; + BOOL finished; + INT scale; + DWORD attributes; + HWND hwnd; + LPWSTR default_font; + msi_font *font_list; + struct list controls; + HWND hWndFocus; + LPWSTR control_default; + LPWSTR control_cancel; + WCHAR name[1]; +}; + +typedef UINT (*msi_dialog_control_func)( msi_dialog *dialog, MSIRECORD *rec ); +struct control_handler +{ + LPCWSTR control_type; + msi_dialog_control_func func; +}; + +typedef struct +{ + msi_dialog* dialog; + msi_control *parent; + DWORD attributes; +} radio_button_group_descr; + +const WCHAR szMsiDialogClass[] = { + 'M','s','i','D','i','a','l','o','g','C','l','o','s','e','C','l','a','s','s',0 +}; +const WCHAR szMsiHiddenWindow[] = { + 'M','s','i','H','i','d','d','e','n','W','i','n','d','o','w',0 }; +static const WCHAR szStatic[] = { 'S','t','a','t','i','c',0 }; +static const WCHAR szButton[] = { 'B','U','T','T','O','N', 0 }; +static const WCHAR szButtonData[] = { 'M','S','I','D','A','T','A',0 }; +static const WCHAR szText[] = { 'T','e','x','t',0 }; +static const WCHAR szPushButton[] = { 'P','u','s','h','B','u','t','t','o','n',0 }; +static const WCHAR szLine[] = { 'L','i','n','e',0 }; +static const WCHAR szBitmap[] = { 'B','i','t','m','a','p',0 }; +static const WCHAR szCheckBox[] = { 'C','h','e','c','k','B','o','x',0 }; +static const WCHAR szScrollableText[] = { + 'S','c','r','o','l','l','a','b','l','e','T','e','x','t',0 }; +static const WCHAR szComboBox[] = { 'C','o','m','b','o','B','o','x',0 }; +static const WCHAR szEdit[] = { 'E','d','i','t',0 }; +static const WCHAR szMaskedEdit[] = { 'M','a','s','k','e','d','E','d','i','t',0 }; +static const WCHAR szPathEdit[] = { 'P','a','t','h','E','d','i','t',0 }; +static const WCHAR szProgressBar[] = { + 'P','r','o','g','r','e','s','s','B','a','r',0 }; +static const WCHAR szRadioButtonGroup[] = { + 'R','a','d','i','o','B','u','t','t','o','n','G','r','o','u','p',0 }; +static const WCHAR szIcon[] = { 'I','c','o','n',0 }; +static const WCHAR szSelectionTree[] = { + 'S','e','l','e','c','t','i','o','n','T','r','e','e',0 }; + +static UINT msi_dialog_checkbox_handler( msi_dialog *, msi_control *, WPARAM ); +static void msi_dialog_checkbox_sync_state( msi_dialog *, msi_control * ); +static UINT msi_dialog_button_handler( msi_dialog *, msi_control *, WPARAM ); +static UINT msi_dialog_edit_handler( msi_dialog *, msi_control *, WPARAM ); +static UINT msi_dialog_radiogroup_handler( msi_dialog *, msi_control *, WPARAM param ); +static UINT msi_dialog_evaluate_control_conditions( msi_dialog *dialog ); +static LRESULT WINAPI MSIRadioGroup_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + + +/* dialog sequencing */ + +#define WM_MSI_DIALOG_CREATE (WM_USER+0x100) +#define WM_MSI_DIALOG_DESTROY (WM_USER+0x101) + +static DWORD uiThreadId; +static HWND hMsiHiddenWindow; + +static INT msi_dialog_scale_unit( msi_dialog *dialog, INT val ) +{ + return (dialog->scale * val + 5) / 10; +} + +static msi_control *msi_dialog_find_control( msi_dialog *dialog, LPCWSTR name ) +{ + msi_control *control; + + if( !name ) + return NULL; + LIST_FOR_EACH_ENTRY( control, &dialog->controls, msi_control, entry ) + if( !strcmpW( control->name, name ) ) /* FIXME: case sensitive? */ + return control; + return NULL; +} + +static msi_control *msi_dialog_find_control_by_hwnd( msi_dialog *dialog, HWND hwnd ) +{ + msi_control *control; + + LIST_FOR_EACH_ENTRY( control, &dialog->controls, msi_control, entry ) + if( hwnd == control->hwnd ) + return control; + return NULL; +} + +static LPWSTR msi_get_deformatted_field( MSIPACKAGE *package, MSIRECORD *rec, int field ) +{ + LPCWSTR str = MSI_RecordGetString( rec, field ); + LPWSTR ret = NULL; + + if (str) + deformat_string( package, str, &ret ); + return ret; +} + +/* + * msi_dialog_get_style + * + * Extract the {\style} string from the front of the text to display and + * update the pointer. + */ +static LPWSTR msi_dialog_get_style( LPCWSTR p, LPCWSTR *rest ) +{ + LPWSTR ret = NULL; + LPCWSTR q, i; + DWORD len; + + *rest = p; + if( !p ) + return ret; + if( *p++ != '{' ) + return ret; + q = strchrW( p, '}' ); + if( !q ) + return ret; + if( *p == '\\' || *p == '&' ) + p++; + + /* little bit of sanity checking to stop us getting confused with RTF */ + for( i=p; iname, name ); + font->next = dialog->font_list; + dialog->font_list = font; + + memset( &lf, 0, sizeof lf ); + face = MSI_RecordGetString( rec, 2 ); + lf.lfHeight = MSI_RecordGetInteger( rec, 3 ); + style = MSI_RecordGetInteger( rec, 5 ); + if( style & msidbTextStyleStyleBitsBold ) + lf.lfWeight = FW_BOLD; + if( style & msidbTextStyleStyleBitsItalic ) + lf.lfItalic = TRUE; + if( style & msidbTextStyleStyleBitsUnderline ) + lf.lfUnderline = TRUE; + if( style & msidbTextStyleStyleBitsStrike ) + lf.lfStrikeOut = TRUE; + lstrcpynW( lf.lfFaceName, face, LF_FACESIZE ); + + /* adjust the height */ + hdc = GetDC( dialog->hwnd ); + if (hdc) + { + lf.lfHeight = -MulDiv(lf.lfHeight, GetDeviceCaps(hdc, LOGPIXELSY), 72); + ReleaseDC( dialog->hwnd, hdc ); + } + + font->hfont = CreateFontIndirectW( &lf ); + + TRACE("Adding font style %s\n", debugstr_w(font->name) ); + + return ERROR_SUCCESS; +} + +static msi_font *msi_dialog_find_font( msi_dialog *dialog, LPCWSTR name ) +{ + msi_font *font; + + for( font = dialog->font_list; font; font = font->next ) + if( !strcmpW( font->name, name ) ) /* FIXME: case sensitive? */ + break; + + return font; +} + +static UINT msi_dialog_set_font( msi_dialog *dialog, HWND hwnd, LPCWSTR name ) +{ + msi_font *font; + + font = msi_dialog_find_font( dialog, name ); + if( font ) + SendMessageW( hwnd, WM_SETFONT, (WPARAM) font->hfont, TRUE ); + else + ERR("No font entry for %s\n", debugstr_w(name)); + return ERROR_SUCCESS; +} + +static UINT msi_dialog_build_font_list( msi_dialog *dialog ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ', + 'F','R','O','M',' ','`','T','e','x','t','S','t','y','l','e','`',' ',0 + }; + UINT r; + MSIQUERY *view = NULL; + + TRACE("dialog %p\n", dialog ); + + r = MSI_OpenQuery( dialog->package->db, &view, query ); + if( r != ERROR_SUCCESS ) + return r; + + r = MSI_IterateRecords( view, NULL, msi_dialog_add_font, dialog ); + msiobj_release( &view->hdr ); + + return r; +} + +static msi_control *msi_dialog_create_window( msi_dialog *dialog, + MSIRECORD *rec, LPCWSTR szCls, LPCWSTR name, LPCWSTR text, + DWORD style, HWND parent ) +{ + DWORD x, y, width, height; + LPWSTR font = NULL, title_font = NULL; + LPCWSTR title = NULL; + msi_control *control; + + style |= WS_CHILD; + + control = msi_alloc( sizeof *control + strlenW(name)*sizeof(WCHAR) ); + strcpyW( control->name, name ); + list_add_head( &dialog->controls, &control->entry ); + control->handler = NULL; + control->property = NULL; + control->value = NULL; + control->hBitmap = NULL; + control->hIcon = NULL; + control->tabnext = strdupW( MSI_RecordGetString( rec, 11) ); + + x = MSI_RecordGetInteger( rec, 4 ); + y = MSI_RecordGetInteger( rec, 5 ); + width = MSI_RecordGetInteger( rec, 6 ); + height = MSI_RecordGetInteger( rec, 7 ); + + x = msi_dialog_scale_unit( dialog, x ); + y = msi_dialog_scale_unit( dialog, y ); + width = msi_dialog_scale_unit( dialog, width ); + height = msi_dialog_scale_unit( dialog, height ); + + if( text ) + { + deformat_string( dialog->package, text, &title_font ); + font = msi_dialog_get_style( title_font, &title ); + } + + control->hwnd = CreateWindowW( szCls, title, style, + x, y, width, height, parent, NULL, NULL, NULL ); + + TRACE("Dialog %s control %s hwnd %p\n", + debugstr_w(dialog->name), debugstr_w(text), control->hwnd ); + + msi_dialog_set_font( dialog, control->hwnd, + font ? font : dialog->default_font ); + + msi_free( title_font ); + msi_free( font ); + + return control; +} + +static MSIRECORD *msi_get_binary_record( MSIDATABASE *db, LPCWSTR name ) +{ + static const WCHAR query[] = { + 's','e','l','e','c','t',' ','*',' ', + 'f','r','o','m',' ','B','i','n','a','r','y',' ', + 'w','h','e','r','e',' ', + '`','N','a','m','e','`',' ','=',' ','\'','%','s','\'',0 + }; + + return MSI_QueryGetRecord( db, query, name ); +} + +static LPWSTR msi_create_tmp_path(void) +{ + WCHAR tmp[MAX_PATH]; + LPWSTR path = NULL; + static const WCHAR prefix[] = { 'm','s','i',0 }; + DWORD len, r; + + r = GetTempPathW( MAX_PATH, tmp ); + if( !r ) + return path; + len = lstrlenW( tmp ) + 20; + path = msi_alloc( len * sizeof (WCHAR) ); + if( path ) + { + r = GetTempFileNameW( tmp, prefix, 0, path ); + if (!r) + { + msi_free( path ); + path = NULL; + } + } + return path; +} + + +static HANDLE msi_load_image( MSIDATABASE *db, LPCWSTR name, UINT type, + UINT cx, UINT cy, UINT flags ) +{ + MSIRECORD *rec = NULL; + HANDLE himage = NULL; + LPWSTR tmp; + UINT r; + + TRACE("%p %s %u %u %08x\n", db, debugstr_w(name), cx, cy, flags); + + tmp = msi_create_tmp_path(); + if( !tmp ) + return himage; + + rec = msi_get_binary_record( db, name ); + if( rec ) + { + r = MSI_RecordStreamToFile( rec, 2, tmp ); + if( r == ERROR_SUCCESS ) + { + himage = LoadImageW( 0, tmp, type, cx, cy, flags ); + DeleteFileW( tmp ); + } + msiobj_release( &rec->hdr ); + } + + msi_free( tmp ); + return himage; +} + +static HICON msi_load_icon( MSIDATABASE *db, LPCWSTR text, UINT attributes ) +{ + DWORD cx = 0, cy = 0, flags; + + flags = LR_LOADFROMFILE | LR_DEFAULTSIZE; + if( attributes & msidbControlAttributesFixedSize ) + { + flags &= ~LR_DEFAULTSIZE; + if( attributes & msidbControlAttributesIconSize16 ) + { + cx += 16; + cy += 16; + } + if( attributes & msidbControlAttributesIconSize32 ) + { + cx += 32; + cy += 32; + } + /* msidbControlAttributesIconSize48 handled by above logic */ + } + return msi_load_image( db, text, IMAGE_ICON, cx, cy, flags ); +} + + +/* called from the Control Event subscription code */ +void msi_dialog_handle_event( msi_dialog* dialog, LPCWSTR control, + LPCWSTR attribute, MSIRECORD *rec ) +{ + msi_control* ctrl; + LPCWSTR text; + + ctrl = msi_dialog_find_control( dialog, control ); + if (!ctrl) + return; + if( lstrcmpW(attribute, szText) ) + { + ERR("Attribute %s\n", debugstr_w(attribute)); + return; + } + text = MSI_RecordGetString( rec , 1 ); + SetWindowTextW( ctrl->hwnd, text ); + msi_dialog_check_messages( NULL ); +} + +static void msi_dialog_map_events(msi_dialog* dialog, LPCWSTR control) +{ + static WCHAR Query[] = { + 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','E','v','e','n','t','M','a','p','p','i','n','g','`',' ', + 'W','H','E','R','E',' ', + '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',' ', + 'A','N','D',' ', + '`','C','o','n','t','r','o','l','_','`',' ','=',' ','\'','%','s','\'',0 + }; + MSIRECORD *row; + LPCWSTR event, attribute; + + row = MSI_QueryGetRecord( dialog->package->db, Query, dialog->name, control ); + if (!row) + return; + + event = MSI_RecordGetString( row, 3 ); + attribute = MSI_RecordGetString( row, 4 ); + ControlEvent_SubscribeToEvent( dialog->package, event, control, attribute ); + msiobj_release( &row->hdr ); +} + +/* everything except radio buttons */ +static msi_control *msi_dialog_add_control( msi_dialog *dialog, + MSIRECORD *rec, LPCWSTR szCls, DWORD style ) +{ + DWORD attributes; + LPCWSTR text, name; + + name = MSI_RecordGetString( rec, 2 ); + attributes = MSI_RecordGetInteger( rec, 8 ); + text = MSI_RecordGetString( rec, 10 ); + if( attributes & msidbControlAttributesVisible ) + style |= WS_VISIBLE; + if( ~attributes & msidbControlAttributesEnabled ) + style |= WS_DISABLED; + + msi_dialog_map_events(dialog, name); + + return msi_dialog_create_window( dialog, rec, szCls, name, text, + style, dialog->hwnd ); +} + +struct msi_text_info +{ + WNDPROC oldproc; + DWORD attributes; +}; + +/* + * we don't erase our own background, + * so we have to make sure that the parent window redraws first + */ +static void msi_text_on_settext( HWND hWnd ) +{ + HWND hParent; + RECT rc; + + hParent = GetParent( hWnd ); + GetWindowRect( hWnd, &rc ); + MapWindowPoints( NULL, hParent, (LPPOINT) &rc, 2 ); + InvalidateRect( hParent, &rc, TRUE ); +} + +static LRESULT WINAPI +MSIText_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + struct msi_text_info *info; + LRESULT r = 0; + + TRACE("%p %04x %08x %08lx\n", hWnd, msg, wParam, lParam); + + info = GetPropW(hWnd, szButtonData); + + if( msg == WM_CTLCOLORSTATIC && + ( info->attributes & msidbControlAttributesTransparent ) ) + { + SetBkMode( (HDC)wParam, TRANSPARENT ); + return (LRESULT) GetStockObject(NULL_BRUSH); + } + + r = CallWindowProcW(info->oldproc, hWnd, msg, wParam, lParam); + + switch( msg ) + { + case WM_SETTEXT: + msi_text_on_settext( hWnd ); + break; + case WM_NCDESTROY: + msi_free( info ); + RemovePropW( hWnd, szButtonData ); + break; + } + + return r; +} + +static UINT msi_dialog_text_control( msi_dialog *dialog, MSIRECORD *rec ) +{ + msi_control *control; + struct msi_text_info *info; + + TRACE("%p %p\n", dialog, rec); + + control = msi_dialog_add_control( dialog, rec, szStatic, SS_LEFT | WS_GROUP ); + if( !control ) + return ERROR_FUNCTION_FAILED; + + info = msi_alloc( sizeof *info ); + if( !info ) + return ERROR_SUCCESS; + + info->attributes = MSI_RecordGetInteger( rec, 8 ); + if( info->attributes & msidbControlAttributesTransparent ) + SetWindowLongPtrW( control->hwnd, GWL_EXSTYLE, WS_EX_TRANSPARENT ); + + info->oldproc = (WNDPROC) SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC, + (LONG_PTR)MSIText_WndProc ); + SetPropW( control->hwnd, szButtonData, info ); + + return ERROR_SUCCESS; +} + +static UINT msi_dialog_button_control( msi_dialog *dialog, MSIRECORD *rec ) +{ + msi_control *control; + UINT attributes, style; + LPWSTR text; + + TRACE("%p %p\n", dialog, rec); + + style = WS_TABSTOP; + attributes = MSI_RecordGetInteger( rec, 8 ); + if( attributes & msidbControlAttributesIcon ) + style |= BS_ICON; + + control = msi_dialog_add_control( dialog, rec, szButton, style ); + if( !control ) + return ERROR_FUNCTION_FAILED; + + control->handler = msi_dialog_button_handler; + + /* set the icon */ + text = msi_get_deformatted_field( dialog->package, rec, 10 ); + control->hIcon = msi_load_icon( dialog->package->db, text, attributes ); + if( attributes & msidbControlAttributesIcon ) + SendMessageW( control->hwnd, BM_SETIMAGE, IMAGE_ICON, (LPARAM) control->hIcon ); + msi_free( text ); + + return ERROR_SUCCESS; +} + +static LPWSTR msi_get_checkbox_value( msi_dialog *dialog, LPCWSTR prop ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ', + 'F','R','O','M',' ','`','C','h','e','c','k','B','o','x',' ','`', + 'W','H','E','R','E',' ', + '`','P','r','o','p','e','r','t','y','`',' ','=',' ', + '\'','%','s','\'',0 + }; + MSIRECORD *rec = NULL; + LPWSTR ret = NULL; + + /* find if there is a value associated with the checkbox */ + rec = MSI_QueryGetRecord( dialog->package->db, query, prop ); + if (!rec) + return ret; + + ret = msi_get_deformatted_field( dialog->package, rec, 2 ); + if( ret && !ret[0] ) + { + msi_free( ret ); + ret = NULL; + } + msiobj_release( &rec->hdr ); + if (ret) + return ret; + + ret = msi_dup_property( dialog->package, prop ); + if( ret && !ret[0] ) + { + msi_free( ret ); + ret = NULL; + } + + return ret; +} + +static UINT msi_dialog_checkbox_control( msi_dialog *dialog, MSIRECORD *rec ) +{ + msi_control *control; + LPCWSTR prop; + + TRACE("%p %p\n", dialog, rec); + + control = msi_dialog_add_control( dialog, rec, szButton, + BS_CHECKBOX | BS_MULTILINE | WS_TABSTOP ); + control->handler = msi_dialog_checkbox_handler; + prop = MSI_RecordGetString( rec, 9 ); + if( prop ) + { + control->property = strdupW( prop ); + control->value = msi_get_checkbox_value( dialog, prop ); + TRACE("control %s value %s\n", debugstr_w(control->property), + debugstr_w(control->value)); + } + msi_dialog_checkbox_sync_state( dialog, control ); + + return ERROR_SUCCESS; +} + +static UINT msi_dialog_line_control( msi_dialog *dialog, MSIRECORD *rec ) +{ + TRACE("%p %p\n", dialog, rec); + + msi_dialog_add_control( dialog, rec, szStatic, SS_ETCHEDHORZ | SS_SUNKEN ); + return ERROR_SUCCESS; +} + +/******************** Scroll Text ********************************************/ + +struct msi_scrolltext_info +{ + msi_dialog *dialog; + msi_control *control; + WNDPROC oldproc; + HMODULE hRichedit; +}; + +static LRESULT WINAPI +MSIScrollText_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + struct msi_scrolltext_info *info; + HRESULT r; + + TRACE("%p %04x %08x %08lx\n", hWnd, msg, wParam, lParam); + + info = GetPropW( hWnd, szButtonData ); + + r = CallWindowProcW( info->oldproc, hWnd, msg, wParam, lParam ); + + switch( msg ) + { + case WM_NCDESTROY: + FreeLibrary( info->hRichedit ); + msi_free( info ); + RemovePropW( hWnd, szButtonData ); + break; + case WM_VSCROLL: + msi_dialog_button_handler( info->dialog, info->control, BN_CLICKED ); + break; + } + return r; +} + +struct msi_streamin_info +{ + LPSTR string; + DWORD offset; + DWORD length; +}; + +static DWORD CALLBACK +msi_richedit_stream_in( DWORD_PTR arg, LPBYTE buffer, LONG count, LONG *pcb ) +{ + struct msi_streamin_info *info = (struct msi_streamin_info*) arg; + + if( (count + info->offset) > info->length ) + count = info->length - info->offset; + memcpy( buffer, &info->string[ info->offset ], count ); + *pcb = count; + info->offset += count; + + TRACE("%ld/%ld\n", info->offset, info->length); + + return 0; +} + +static void msi_scrolltext_add_text( msi_control *control, LPCWSTR text ) +{ + struct msi_streamin_info info; + EDITSTREAM es; + + info.string = strdupWtoA( text ); + info.offset = 0; + info.length = lstrlenA( info.string ) + 1; + + es.dwCookie = (DWORD_PTR) &info; + es.dwError = 0; + es.pfnCallback = msi_richedit_stream_in; + + SendMessageW( control->hwnd, EM_STREAMIN, SF_RTF, (LPARAM) &es ); + + msi_free( info.string ); +} + +static UINT msi_dialog_scrolltext_control( msi_dialog *dialog, MSIRECORD *rec ) +{ + static const WCHAR szRichEdit20W[] = { + 'R','i','c','h','E','d','i','t','2','0','W',0 + }; + struct msi_scrolltext_info *info; + msi_control *control; + DWORD style; + + info = msi_alloc( sizeof *info ); + if (!info) + return ERROR_FUNCTION_FAILED; + + info->hRichedit = LoadLibraryA("riched20"); + + style = WS_BORDER | ES_MULTILINE | WS_VSCROLL | + ES_READONLY | ES_AUTOVSCROLL | WS_TABSTOP; + control = msi_dialog_add_control( dialog, rec, szRichEdit20W, style ); + if (!control) + { + FreeLibrary( info->hRichedit ); + msi_free( info ); + return ERROR_FUNCTION_FAILED; + } + + info->dialog = dialog; + info->control = control; + + /* subclass the static control */ + info->oldproc = (WNDPROC) SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC, + (LONG_PTR)MSIScrollText_WndProc ); + SetPropW( control->hwnd, szButtonData, info ); + + /* add the text into the richedit */ + msi_scrolltext_add_text( control, MSI_RecordGetString( rec, 10 ) ); + + return ERROR_SUCCESS; +} + +static HBITMAP msi_load_picture( MSIDATABASE *db, LPCWSTR name, + INT cx, INT cy, DWORD flags ) +{ + HBITMAP hOleBitmap = 0, hBitmap = 0, hOldSrcBitmap, hOldDestBitmap; + MSIRECORD *rec = NULL; + IStream *stm = NULL; + IPicture *pic = NULL; + HDC srcdc, destdc; + BITMAP bm; + UINT r; + + rec = msi_get_binary_record( db, name ); + if( !rec ) + goto end; + + r = MSI_RecordGetIStream( rec, 2, &stm ); + msiobj_release( &rec->hdr ); + if( r != ERROR_SUCCESS ) + goto end; + + r = OleLoadPicture( stm, 0, TRUE, &IID_IPicture, (LPVOID*) &pic ); + IStream_Release( stm ); + if( FAILED( r ) ) + { + ERR("failed to load picture\n"); + goto end; + } + + r = IPicture_get_Handle( pic, (OLE_HANDLE*) &hOleBitmap ); + if( FAILED( r ) ) + { + ERR("failed to get bitmap handle\n"); + goto end; + } + + /* make the bitmap the desired size */ + r = GetObjectW( hOleBitmap, sizeof bm, &bm ); + if (r != sizeof bm ) + { + ERR("failed to get bitmap size\n"); + goto end; + } + + if (flags & LR_DEFAULTSIZE) + { + cx = bm.bmWidth; + cy = bm.bmHeight; + } + + srcdc = CreateCompatibleDC( NULL ); + hOldSrcBitmap = SelectObject( srcdc, hOleBitmap ); + destdc = CreateCompatibleDC( NULL ); + hBitmap = CreateCompatibleBitmap( srcdc, cx, cy ); + hOldDestBitmap = SelectObject( destdc, hBitmap ); + StretchBlt( destdc, 0, 0, cx, cy, + srcdc, 0, 0, bm.bmWidth, bm.bmHeight, SRCCOPY); + SelectObject( srcdc, hOldSrcBitmap ); + SelectObject( destdc, hOldDestBitmap ); + DeleteDC( srcdc ); + DeleteDC( destdc ); + +end: + if ( pic ) + IPicture_Release( pic ); + return hBitmap; +} + +static UINT msi_dialog_bitmap_control( msi_dialog *dialog, MSIRECORD *rec ) +{ + UINT cx, cy, flags, style, attributes; + msi_control *control; + LPWSTR text; + + flags = LR_LOADFROMFILE; + style = SS_BITMAP | SS_LEFT | WS_GROUP; + + attributes = MSI_RecordGetInteger( rec, 8 ); + if( attributes & msidbControlAttributesFixedSize ) + { + flags |= LR_DEFAULTSIZE; + style |= SS_CENTERIMAGE; + } + + control = msi_dialog_add_control( dialog, rec, szStatic, style ); + cx = MSI_RecordGetInteger( rec, 6 ); + cy = MSI_RecordGetInteger( rec, 7 ); + cx = msi_dialog_scale_unit( dialog, cx ); + cy = msi_dialog_scale_unit( dialog, cy ); + + text = msi_get_deformatted_field( dialog->package, rec, 10 ); + control->hBitmap = msi_load_picture( dialog->package->db, text, cx, cy, flags ); + if( control->hBitmap ) + SendMessageW( control->hwnd, STM_SETIMAGE, + IMAGE_BITMAP, (LPARAM) control->hBitmap ); + else + ERR("Failed to load bitmap %s\n", debugstr_w(text)); + + msi_free( text ); + + return ERROR_SUCCESS; +} + +static UINT msi_dialog_icon_control( msi_dialog *dialog, MSIRECORD *rec ) +{ + msi_control *control; + DWORD attributes; + LPWSTR text; + + TRACE("\n"); + + control = msi_dialog_add_control( dialog, rec, szStatic, + SS_ICON | SS_CENTERIMAGE | WS_GROUP ); + + attributes = MSI_RecordGetInteger( rec, 8 ); + text = msi_get_deformatted_field( dialog->package, rec, 10 ); + control->hIcon = msi_load_icon( dialog->package->db, text, attributes ); + if( control->hIcon ) + SendMessageW( control->hwnd, STM_SETICON, (WPARAM) control->hIcon, 0 ); + else + ERR("Failed to load bitmap %s\n", debugstr_w(text)); + msi_free( text ); + return ERROR_SUCCESS; +} + +static UINT msi_dialog_combo_control( msi_dialog *dialog, MSIRECORD *rec ) +{ + static const WCHAR szCombo[] = { 'C','O','M','B','O','B','O','X',0 }; + + msi_dialog_add_control( dialog, rec, szCombo, + SS_BITMAP | SS_LEFT | SS_CENTERIMAGE ); + return ERROR_SUCCESS; +} + +static UINT msi_dialog_edit_control( msi_dialog *dialog, MSIRECORD *rec ) +{ + msi_control *control; + LPCWSTR prop; + LPWSTR val; + + control = msi_dialog_add_control( dialog, rec, szEdit, + WS_BORDER | WS_TABSTOP ); + control->handler = msi_dialog_edit_handler; + prop = MSI_RecordGetString( rec, 9 ); + if( prop ) + control->property = strdupW( prop ); + val = msi_dup_property( dialog->package, control->property ); + SetWindowTextW( control->hwnd, val ); + msi_free( val ); + return ERROR_SUCCESS; +} + +/******************** Masked Edit ********************************************/ + +#define MASK_MAX_GROUPS 10 + +struct msi_mask_group +{ + UINT len; + UINT ofs; + WCHAR type; + HWND hwnd; +}; + +struct msi_maskedit_info +{ + msi_dialog *dialog; + WNDPROC oldproc; + HWND hwnd; + LPWSTR prop; + UINT num_chars; + UINT num_groups; + struct msi_mask_group group[MASK_MAX_GROUPS]; +}; + +static BOOL msi_mask_editable( WCHAR type ) +{ + switch (type) + { + case '%': + case '#': + case '&': + case '`': + case '?': + case '^': + return TRUE; + } + return FALSE; +} + +static void msi_mask_control_change( struct msi_maskedit_info *info ) +{ + LPWSTR val; + UINT i, n, r; + + val = msi_alloc( (info->num_chars+1)*sizeof(WCHAR) ); + for( i=0, n=0; inum_groups; i++ ) + { + if( (info->group[i].len + n) > info->num_chars ) + { + ERR("can't fit control %d text into template\n",i); + break; + } + if (!msi_mask_editable(info->group[i].type)) + { + for(r=0; rgroup[i].len; r++) + val[n+r] = info->group[i].type; + val[n+r] = 0; + } + else + { + r = GetWindowTextW( info->group[i].hwnd, &val[n], info->group[i].len+1 ); + if( r != info->group[i].len ) + break; + } + n += r; + } + + TRACE("%d/%d controls were good\n", i, info->num_groups); + + if( i == info->num_groups ) + { + TRACE("Set property %s to %s\n", + debugstr_w(info->prop), debugstr_w(val) ); + CharUpperBuffW( val, info->num_chars ); + MSI_SetPropertyW( info->dialog->package, info->prop, val ); + msi_dialog_evaluate_control_conditions( info->dialog ); + } + msi_free( val ); +} + +/* now move to the next control if necessary */ +static VOID msi_mask_next_control( struct msi_maskedit_info *info, HWND hWnd ) +{ + HWND hWndNext; + UINT len, i; + + for( i=0; inum_groups; i++ ) + if( info->group[i].hwnd == hWnd ) + break; + + /* don't move from the last control */ + if( i >= (info->num_groups-1) ) + return; + + len = SendMessageW( hWnd, WM_GETTEXTLENGTH, 0, 0 ); + if( len < info->group[i].len ) + return; + + hWndNext = GetNextDlgTabItem( GetParent( hWnd ), hWnd, FALSE ); + SetFocus( hWndNext ); +} + +static LRESULT WINAPI +MSIMaskedEdit_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + struct msi_maskedit_info *info; + HRESULT r; + + TRACE("%p %04x %08x %08lx\n", hWnd, msg, wParam, lParam); + + info = GetPropW(hWnd, szButtonData); + + r = CallWindowProcW(info->oldproc, hWnd, msg, wParam, lParam); + + switch( msg ) + { + case WM_COMMAND: + if (HIWORD(wParam) == EN_CHANGE) + { + msi_mask_control_change( info ); + msi_mask_next_control( info, (HWND) lParam ); + } + break; + case WM_NCDESTROY: + msi_free( info->prop ); + msi_free( info ); + RemovePropW( hWnd, szButtonData ); + break; + } + + return r; +} + +/* fish the various bits of the property out and put them in the control */ +static void +msi_maskedit_set_text( struct msi_maskedit_info *info, LPCWSTR text ) +{ + LPCWSTR p; + UINT i; + + p = text; + for( i = 0; i < info->num_groups; i++ ) + { + if( info->group[i].len < lstrlenW( p ) ) + { + LPWSTR chunk = strdupW( p ); + chunk[ info->group[i].len ] = 0; + SetWindowTextW( info->group[i].hwnd, chunk ); + msi_free( chunk ); + } + else + { + SetWindowTextW( info->group[i].hwnd, p ); + break; + } + p += info->group[i].len; + } +} + +static struct msi_maskedit_info * msi_dialog_parse_groups( LPCWSTR mask ) +{ + struct msi_maskedit_info * info = NULL; + int i = 0, n = 0, total = 0; + LPCWSTR p; + + TRACE("masked control, template %s\n", debugstr_w(mask)); + + if( !mask ) + return info; + + info = msi_alloc_zero( sizeof *info ); + if( !info ) + return info; + + p = strchrW(mask, '<'); + if( p ) + p++; + else + p = mask; + + for( i=0; i' ) + break; + + /* count the number of the same identifier */ + for( n=0; p[n] == p[0]; n++ ) + ; + info->group[i].ofs = total; + info->group[i].type = p[0]; + if( p[n] == '=' ) + { + n++; + total++; /* an extra not part of the group */ + } + info->group[i].len = n; + total += n; + p += n; + } + + TRACE("%d characters in %d groups\n", total, i ); + if( i == MASK_MAX_GROUPS ) + ERR("too many groups in PIDTemplate %s\n", debugstr_w(mask)); + + info->num_chars = total; + info->num_groups = i; + + return info; +} + +static void +msi_maskedit_create_children( struct msi_maskedit_info *info, LPCWSTR font ) +{ + DWORD width, height, style, wx, ww; + RECT rect; + HWND hwnd; + UINT i; + + style = WS_CHILD | WS_BORDER | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL; + + GetClientRect( info->hwnd, &rect ); + + width = rect.right - rect.left; + height = rect.bottom - rect.top; + + for( i = 0; i < info->num_groups; i++ ) + { + if (!msi_mask_editable( info->group[i].type )) + continue; + wx = (info->group[i].ofs * width) / info->num_chars; + ww = (info->group[i].len * width) / info->num_chars; + + hwnd = CreateWindowW( szEdit, NULL, style, wx, 0, ww, height, + info->hwnd, NULL, NULL, NULL ); + if( !hwnd ) + { + ERR("failed to create mask edit sub window\n"); + break; + } + + SendMessageW( hwnd, EM_LIMITTEXT, info->group[i].len, 0 ); + + msi_dialog_set_font( info->dialog, hwnd, + font?font:info->dialog->default_font ); + info->group[i].hwnd = hwnd; + } +} + +/* + * office 2003 uses "73931<````=````=````=````=`````>@@@@@" + * delphi 7 uses "" and "" + * filemaker pro 7 uses "<^^^^=^^^^=^^^^=^^^^=^^^^=^^^^=^^^^^>" + */ +static UINT msi_dialog_maskedit_control( msi_dialog *dialog, MSIRECORD *rec ) +{ + LPWSTR font_mask, val = NULL, font; + struct msi_maskedit_info *info = NULL; + UINT ret = ERROR_SUCCESS; + msi_control *control; + LPCWSTR prop, mask; + + TRACE("\n"); + + font_mask = msi_get_deformatted_field( dialog->package, rec, 10 ); + font = msi_dialog_get_style( font_mask, &mask ); + if( !mask ) + { + ERR("mask template is empty\n"); + goto end; + } + + info = msi_dialog_parse_groups( mask ); + if( !info ) + { + ERR("template %s is invalid\n", debugstr_w(mask)); + goto end; + } + + info->dialog = dialog; + + control = msi_dialog_add_control( dialog, rec, szStatic, + SS_OWNERDRAW | WS_GROUP | WS_VISIBLE ); + if( !control ) + { + ERR("Failed to create maskedit container\n"); + ret = ERROR_FUNCTION_FAILED; + goto end; + } + SetWindowLongPtrW( control->hwnd, GWL_EXSTYLE, WS_EX_CONTROLPARENT ); + + info->hwnd = control->hwnd; + + /* subclass the static control */ + info->oldproc = (WNDPROC) SetWindowLongPtrW( info->hwnd, GWLP_WNDPROC, + (LONG_PTR)MSIMaskedEdit_WndProc ); + SetPropW( control->hwnd, szButtonData, info ); + + prop = MSI_RecordGetString( rec, 9 ); + if( prop ) + info->prop = strdupW( prop ); + + msi_maskedit_create_children( info, font ); + + if( prop ) + { + val = msi_dup_property( dialog->package, prop ); + if( val ) + { + msi_maskedit_set_text( info, val ); + msi_free( val ); + } + } + +end: + if( ret != ERROR_SUCCESS ) + msi_free( info ); + msi_free( font_mask ); + msi_free( font ); + return ret; +} + +/******************** Progress Bar *****************************************/ + +static UINT msi_dialog_progress_bar( msi_dialog *dialog, MSIRECORD *rec ) +{ + msi_dialog_add_control( dialog, rec, PROGRESS_CLASSW, WS_VISIBLE ); + return ERROR_SUCCESS; +} + +/******************** Path Edit ********************************************/ + +static UINT msi_dialog_pathedit_control( msi_dialog *dialog, MSIRECORD *rec ) +{ + FIXME("not implemented properly\n"); + return msi_dialog_edit_control( dialog, rec ); +} + +/* radio buttons are a bit different from normal controls */ +static UINT msi_dialog_create_radiobutton( MSIRECORD *rec, LPVOID param ) +{ + radio_button_group_descr *group = (radio_button_group_descr *)param; + msi_dialog *dialog = group->dialog; + msi_control *control; + LPCWSTR prop, text, name; + DWORD style, attributes = group->attributes; + + style = WS_CHILD | BS_AUTORADIOBUTTON | BS_MULTILINE | WS_TABSTOP; + name = MSI_RecordGetString( rec, 3 ); + text = MSI_RecordGetString( rec, 8 ); + if( attributes & 1 ) + style |= WS_VISIBLE; + if( ~attributes & 2 ) + style |= WS_DISABLED; + + control = msi_dialog_create_window( dialog, rec, szButton, name, text, + style, group->parent->hwnd ); + if (!control) + return ERROR_FUNCTION_FAILED; + control->handler = msi_dialog_radiogroup_handler; + + prop = MSI_RecordGetString( rec, 1 ); + if( prop ) + control->property = strdupW( prop ); + + return ERROR_SUCCESS; +} + +static UINT msi_dialog_radiogroup_control( msi_dialog *dialog, MSIRECORD *rec ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ', + 'F','R','O','M',' ','R','a','d','i','o','B','u','t','t','o','n',' ', + 'W','H','E','R','E',' ', + '`','P','r','o','p','e','r','t','y','`',' ','=',' ','\'','%','s','\'',0}; + UINT r; + LPCWSTR prop; + msi_control *control; + MSIQUERY *view = NULL; + radio_button_group_descr group; + MSIPACKAGE *package = dialog->package; + WNDPROC oldproc; + + prop = MSI_RecordGetString( rec, 9 ); + + TRACE("%p %p %s\n", dialog, rec, debugstr_w( prop )); + + /* Create parent group box to hold radio buttons */ + control = msi_dialog_add_control( dialog, rec, szButton, BS_OWNERDRAW|WS_GROUP ); + if( !control ) + return ERROR_FUNCTION_FAILED; + + oldproc = (WNDPROC) SetWindowLongPtrW( control->hwnd, GWLP_WNDPROC, + (LONG_PTR)MSIRadioGroup_WndProc ); + SetPropW(control->hwnd, szButtonData, oldproc); + SetWindowLongPtrW( control->hwnd, GWL_EXSTYLE, WS_EX_CONTROLPARENT ); + + if( prop ) + control->property = strdupW( prop ); + + /* query the Radio Button table for all control in this group */ + r = MSI_OpenQuery( package->db, &view, query, prop ); + if( r != ERROR_SUCCESS ) + { + ERR("query failed for dialog %s radio group %s\n", + debugstr_w(dialog->name), debugstr_w(prop)); + return ERROR_INVALID_PARAMETER; + } + + group.dialog = dialog; + group.parent = control; + group.attributes = MSI_RecordGetInteger( rec, 8 ); + + r = MSI_IterateRecords( view, 0, msi_dialog_create_radiobutton, &group ); + msiobj_release( &view->hdr ); + + return r; +} + +/******************** Selection Tree ***************************************/ + +static void +msi_dialog_tv_add_child_features( MSIPACKAGE *package, HWND hwnd, + LPCWSTR parent, HTREEITEM hParent ) +{ + MSIFEATURE *feature; + TVINSERTSTRUCTW tvis; + HTREEITEM hitem; + + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + if ( lstrcmpW( parent, feature->Feature_Parent ) ) + continue; + + if ( !feature->Title ) + continue; + + memset( &tvis, 0, sizeof tvis ); + tvis.hParent = hParent; + tvis.hInsertAfter = TVI_SORT; + if (feature->Title) + { + tvis.u.item.mask = TVIF_TEXT; + tvis.u.item.pszText = feature->Title; + } + tvis.u.item.lParam = (LPARAM) feature; + hitem = (HTREEITEM) SendMessageW( hwnd, TVM_INSERTITEMW, 0, (LPARAM) &tvis ); + if (!hitem) + continue; + + msi_dialog_tv_add_child_features( package, hwnd, + feature->Feature, hitem ); + } +} + +static UINT msi_dialog_selection_tree( msi_dialog *dialog, MSIRECORD *rec ) +{ + msi_control *control; + LPCWSTR prop; + LPWSTR val; + MSIPACKAGE *package = dialog->package; + + prop = MSI_RecordGetString( rec, 9 ); + val = msi_dup_property( package, prop ); + control = msi_dialog_add_control( dialog, rec, WC_TREEVIEWW, + TVS_HASBUTTONS | WS_GROUP | WS_VSCROLL ); + if (!control) + return ERROR_FUNCTION_FAILED; + + msi_dialog_tv_add_child_features( package, control->hwnd, NULL, NULL ); + + msi_free( val ); + + return ERROR_SUCCESS; +} + +struct control_handler msi_dialog_handler[] = +{ + { szText, msi_dialog_text_control }, + { szPushButton, msi_dialog_button_control }, + { szLine, msi_dialog_line_control }, + { szBitmap, msi_dialog_bitmap_control }, + { szCheckBox, msi_dialog_checkbox_control }, + { szScrollableText, msi_dialog_scrolltext_control }, + { szComboBox, msi_dialog_combo_control }, + { szEdit, msi_dialog_edit_control }, + { szMaskedEdit, msi_dialog_maskedit_control }, + { szPathEdit, msi_dialog_pathedit_control }, + { szProgressBar, msi_dialog_progress_bar }, + { szRadioButtonGroup, msi_dialog_radiogroup_control }, + { szIcon, msi_dialog_icon_control }, + { szSelectionTree, msi_dialog_selection_tree }, +}; + +#define NUM_CONTROL_TYPES (sizeof msi_dialog_handler/sizeof msi_dialog_handler[0]) + +static UINT msi_dialog_create_controls( MSIRECORD *rec, LPVOID param ) +{ + msi_dialog *dialog = param; + LPCWSTR control_type; + UINT i; + + /* find and call the function that can create this type of control */ + control_type = MSI_RecordGetString( rec, 3 ); + for( i=0; ipackage; + + TRACE("%p %s\n", dialog, debugstr_w(dialog->name) ); + + /* query the Control table for all the elements of the control */ + r = MSI_OpenQuery( package->db, &view, query, dialog->name ); + if( r != ERROR_SUCCESS ) + { + ERR("query failed for dialog %s\n", debugstr_w(dialog->name)); + return ERROR_INVALID_PARAMETER; + } + + r = MSI_IterateRecords( view, 0, msi_dialog_create_controls, dialog ); + msiobj_release( &view->hdr ); + + return r; +} + +static UINT msi_dialog_set_control_condition( MSIRECORD *rec, LPVOID param ) +{ + static const WCHAR szHide[] = { 'H','i','d','e',0 }; + static const WCHAR szShow[] = { 'S','h','o','w',0 }; + static const WCHAR szDisable[] = { 'D','i','s','a','b','l','e',0 }; + static const WCHAR szEnable[] = { 'E','n','a','b','l','e',0 }; + msi_dialog *dialog = param; + msi_control *control; + LPCWSTR name, action, condition; + UINT r; + + name = MSI_RecordGetString( rec, 2 ); + action = MSI_RecordGetString( rec, 3 ); + condition = MSI_RecordGetString( rec, 4 ); + r = MSI_EvaluateConditionW( dialog->package, condition ); + control = msi_dialog_find_control( dialog, name ); + if( r == MSICONDITION_TRUE && control ) + { + TRACE("%s control %s\n", debugstr_w(action), debugstr_w(name)); + + /* FIXME: case sensitive? */ + if(!lstrcmpW(action, szHide)) + ShowWindow(control->hwnd, SW_HIDE); + else if(!strcmpW(action, szShow)) + ShowWindow(control->hwnd, SW_SHOW); + else if(!strcmpW(action, szDisable)) + EnableWindow(control->hwnd, FALSE); + else if(!strcmpW(action, szEnable)) + EnableWindow(control->hwnd, TRUE); + else + FIXME("Unhandled action %s\n", debugstr_w(action)); + } + + return ERROR_SUCCESS; +} + +static UINT msi_dialog_evaluate_control_conditions( msi_dialog *dialog ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ', + 'F','R','O','M',' ', + 'C','o','n','t','r','o','l','C','o','n','d','i','t','i','o','n',' ', + 'W','H','E','R','E',' ', + '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',0 + }; + UINT r; + MSIQUERY *view = NULL; + MSIPACKAGE *package = dialog->package; + + TRACE("%p %s\n", dialog, debugstr_w(dialog->name) ); + + /* query the Control table for all the elements of the control */ + r = MSI_OpenQuery( package->db, &view, query, dialog->name ); + if( r != ERROR_SUCCESS ) + { + ERR("query failed for dialog %s\n", debugstr_w(dialog->name)); + return ERROR_INVALID_PARAMETER; + } + + r = MSI_IterateRecords( view, 0, msi_dialog_set_control_condition, dialog ); + msiobj_release( &view->hdr ); + + return r; +} + +UINT msi_dialog_reset( msi_dialog *dialog ) +{ + /* FIXME: should restore the original values of any properties we changed */ + return msi_dialog_evaluate_control_conditions( dialog ); +} + +/* figure out the height of 10 point MS Sans Serif */ +static INT msi_dialog_get_sans_serif_height( HWND hwnd ) +{ + static const WCHAR szSansSerif[] = { + 'M','S',' ','S','a','n','s',' ','S','e','r','i','f',0 }; + LOGFONTW lf; + TEXTMETRICW tm; + BOOL r; + LONG height = 0; + HFONT hFont, hOldFont; + HDC hdc; + + hdc = GetDC( hwnd ); + if (hdc) + { + memset( &lf, 0, sizeof lf ); + lf.lfHeight = MulDiv(10, GetDeviceCaps(hdc, LOGPIXELSY), 72); + strcpyW( lf.lfFaceName, szSansSerif ); + hFont = CreateFontIndirectW(&lf); + if (hFont) + { + hOldFont = SelectObject( hdc, hFont ); + r = GetTextMetricsW( hdc, &tm ); + if (r) + height = tm.tmHeight; + SelectObject( hdc, hOldFont ); + DeleteObject( hFont ); + } + ReleaseDC( hwnd, hdc ); + } + return height; +} + +/* fetch the associated record from the Dialog table */ +static MSIRECORD *msi_get_dialog_record( msi_dialog *dialog ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ', + 'F','R','O','M',' ','D','i','a','l','o','g',' ', + 'W','H','E','R','E',' ', + '`','D','i','a','l','o','g','`',' ','=',' ','\'','%','s','\'',0}; + MSIPACKAGE *package = dialog->package; + MSIRECORD *rec = NULL; + + TRACE("%p %s\n", dialog, debugstr_w(dialog->name) ); + + rec = MSI_QueryGetRecord( package->db, query, dialog->name ); + if( !rec ) + ERR("query failed for dialog %s\n", debugstr_w(dialog->name)); + + return rec; +} + +static void msi_dialog_adjust_dialog_size( msi_dialog *dialog, LPSIZE sz ) +{ + RECT rect; + LONG style; + + /* turn the client size into the window rectangle */ + rect.left = 0; + rect.top = 0; + rect.right = msi_dialog_scale_unit( dialog, sz->cx ); + rect.bottom = msi_dialog_scale_unit( dialog, sz->cy ); + style = GetWindowLongPtrW( dialog->hwnd, GWL_STYLE ); + AdjustWindowRect( &rect, style, FALSE ); + sz->cx = rect.right - rect.left; + sz->cy = rect.bottom - rect.top; +} + +static BOOL msi_control_set_next( msi_control *control, msi_control *next ) +{ + return SetWindowPos( next->hwnd, control->hwnd, 0, 0, 0, 0, + SWP_NOMOVE | SWP_NOOWNERZORDER | SWP_NOREDRAW | + SWP_NOREPOSITION | SWP_NOSENDCHANGING | SWP_NOSIZE ); +} + +static UINT msi_dialog_set_tab_order( msi_dialog *dialog ) +{ + msi_control *control, *tab_next; + + LIST_FOR_EACH_ENTRY( control, &dialog->controls, msi_control, entry ) + { + tab_next = msi_dialog_find_control( dialog, control->tabnext ); + if( !tab_next ) + continue; + msi_control_set_next( control, tab_next ); + } + + return ERROR_SUCCESS; +} + +static void msi_dialog_set_first_control( msi_dialog* dialog, LPCWSTR name ) +{ + msi_control *control; + + control = msi_dialog_find_control( dialog, name ); + if( control ) + dialog->hWndFocus = control->hwnd; + else + dialog->hWndFocus = NULL; +} + +static LRESULT msi_dialog_oncreate( HWND hwnd, LPCREATESTRUCTW cs ) +{ + static const WCHAR df[] = { + 'D','e','f','a','u','l','t','U','I','F','o','n','t',0 }; + static const WCHAR dfv[] = { + 'M','S',' ','S','h','e','l','l',' ','D','l','g',0 }; + msi_dialog *dialog = (msi_dialog*) cs->lpCreateParams; + MSIRECORD *rec = NULL; + LPWSTR title = NULL; + SIZE size; + + TRACE("%p %p\n", dialog, dialog->package); + + dialog->hwnd = hwnd; + SetWindowLongPtrW( hwnd, GWLP_USERDATA, (LONG_PTR) dialog ); + + rec = msi_get_dialog_record( dialog ); + if( !rec ) + { + TRACE("No record found for dialog %s\n", debugstr_w(dialog->name)); + return -1; + } + + dialog->scale = msi_dialog_get_sans_serif_height(dialog->hwnd); + + size.cx = MSI_RecordGetInteger( rec, 4 ); + size.cy = MSI_RecordGetInteger( rec, 5 ); + msi_dialog_adjust_dialog_size( dialog, &size ); + + dialog->attributes = MSI_RecordGetInteger( rec, 6 ); + + dialog->default_font = msi_dup_property( dialog->package, df ); + if (!dialog->default_font) + { + dialog->default_font = strdupW(dfv); + if (!dialog->default_font) return -1; + } + + title = msi_get_deformatted_field( dialog->package, rec, 7 ); + SetWindowTextW( hwnd, title ); + msi_free( title ); + + SetWindowPos( hwnd, 0, 0, 0, size.cx, size.cy, + SWP_NOMOVE | SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOREDRAW ); + + + msi_dialog_build_font_list( dialog ); + msi_dialog_fill_controls( dialog ); + msi_dialog_evaluate_control_conditions( dialog ); + msi_dialog_set_tab_order( dialog ); + msi_dialog_set_first_control( dialog, MSI_RecordGetString( rec, 8 ) ); + msiobj_release( &rec->hdr ); + + return 0; +} + +static UINT msi_dialog_send_event( msi_dialog *dialog, LPCWSTR event, LPCWSTR arg ) +{ + LPWSTR event_fmt = NULL, arg_fmt = NULL; + + TRACE("Sending control event %s %s\n", debugstr_w(event), debugstr_w(arg)); + + deformat_string( dialog->package, event, &event_fmt ); + deformat_string( dialog->package, arg, &arg_fmt ); + + dialog->event_handler( dialog->package, event_fmt, arg_fmt, dialog ); + + msi_free( event_fmt ); + msi_free( arg_fmt ); + + return ERROR_SUCCESS; +} + +static UINT msi_dialog_set_property( msi_dialog *dialog, LPCWSTR event, LPCWSTR arg ) +{ + static const WCHAR szNullArg[] = { '{','}',0 }; + LPWSTR p, prop, arg_fmt = NULL; + UINT len; + + len = strlenW(event); + prop = msi_alloc( len*sizeof(WCHAR)); + strcpyW( prop, &event[1] ); + p = strchrW( prop, ']' ); + if( p && p[1] == 0 ) + { + *p = 0; + if( strcmpW( szNullArg, arg ) ) + deformat_string( dialog->package, arg, &arg_fmt ); + MSI_SetPropertyW( dialog->package, prop, arg_fmt ); + msi_free( arg_fmt ); + } + else + ERR("Badly formatted property string - what happens?\n"); + msi_free( prop ); + return ERROR_SUCCESS; +} + +static UINT msi_dialog_control_event( MSIRECORD *rec, LPVOID param ) +{ + msi_dialog *dialog = param; + LPCWSTR condition, event, arg; + UINT r; + + condition = MSI_RecordGetString( rec, 5 ); + r = MSI_EvaluateConditionW( dialog->package, condition ); + if( r == MSICONDITION_TRUE ) + { + event = MSI_RecordGetString( rec, 3 ); + arg = MSI_RecordGetString( rec, 4 ); + if( event[0] == '[' ) + msi_dialog_set_property( dialog, event, arg ); + else + msi_dialog_send_event( dialog, event, arg ); + } + + return ERROR_SUCCESS; +} + +static UINT msi_dialog_button_handler( msi_dialog *dialog, + msi_control *control, WPARAM param ) +{ + static const WCHAR query[] = { + 'S','E','L','E','C','T',' ','*',' ', + 'F','R','O','M',' ','C','o','n','t','r','o','l','E','v','e','n','t',' ', + 'W','H','E','R','E',' ', + '`','D','i','a','l','o','g','_','`',' ','=',' ','\'','%','s','\'',' ', + 'A','N','D',' ', + '`','C','o','n','t','r','o','l','_','`',' ','=',' ','\'','%','s','\'',' ', + 'O','R','D','E','R',' ','B','Y',' ','`','O','r','d','e','r','i','n','g','`',0 + }; + MSIQUERY *view = NULL; + UINT r; + + if( HIWORD(param) != BN_CLICKED ) + return ERROR_SUCCESS; + + r = MSI_OpenQuery( dialog->package->db, &view, query, + dialog->name, control->name ); + if( r != ERROR_SUCCESS ) + { + ERR("query failed\n"); + return 0; + } + + r = MSI_IterateRecords( view, 0, msi_dialog_control_event, dialog ); + msiobj_release( &view->hdr ); + + return r; +} + +static UINT msi_dialog_get_checkbox_state( msi_dialog *dialog, + msi_control *control ) +{ + WCHAR state[2] = { 0 }; + DWORD sz = 2; + + MSI_GetPropertyW( dialog->package, control->property, state, &sz ); + return state[0] ? 1 : 0; +} + +static void msi_dialog_set_checkbox_state( msi_dialog *dialog, + msi_control *control, UINT state ) +{ + static const WCHAR szState[] = { '1', 0 }; + LPCWSTR val; + + /* if uncheck then the property is set to NULL */ + if (!state) + { + MSI_SetPropertyW( dialog->package, control->property, NULL ); + return; + } + + /* check for a custom state */ + if (control->value && control->value[0]) + val = control->value; + else + val = szState; + + MSI_SetPropertyW( dialog->package, control->property, val ); +} + +static void msi_dialog_checkbox_sync_state( msi_dialog *dialog, + msi_control *control ) +{ + UINT state; + + state = msi_dialog_get_checkbox_state( dialog, control ); + SendMessageW( control->hwnd, BM_SETCHECK, + state ? BST_CHECKED : BST_UNCHECKED, 0 ); +} + +static UINT msi_dialog_checkbox_handler( msi_dialog *dialog, + msi_control *control, WPARAM param ) +{ + UINT state; + + if( HIWORD(param) != BN_CLICKED ) + return ERROR_SUCCESS; + + TRACE("clicked checkbox %s, set %s\n", debugstr_w(control->name), + debugstr_w(control->property)); + + state = msi_dialog_get_checkbox_state( dialog, control ); + state = state ? 0 : 1; + msi_dialog_set_checkbox_state( dialog, control, state ); + msi_dialog_checkbox_sync_state( dialog, control ); + + return msi_dialog_button_handler( dialog, control, param ); +} + +static UINT msi_dialog_edit_handler( msi_dialog *dialog, + msi_control *control, WPARAM param ) +{ + UINT sz, r; + LPWSTR buf; + + if( HIWORD(param) != EN_CHANGE ) + return ERROR_SUCCESS; + + TRACE("edit %s contents changed, set %s\n", debugstr_w(control->name), + debugstr_w(control->property)); + + sz = 0x20; + buf = msi_alloc( sz*sizeof(WCHAR) ); + while( buf ) + { + r = GetWindowTextW( control->hwnd, buf, sz ); + if( r < (sz-1) ) + break; + sz *= 2; + buf = msi_realloc( buf, sz*sizeof(WCHAR) ); + } + + MSI_SetPropertyW( dialog->package, control->property, buf ); + + msi_free( buf ); + + return ERROR_SUCCESS; +} + +static UINT msi_dialog_radiogroup_handler( msi_dialog *dialog, + msi_control *control, WPARAM param ) +{ + if( HIWORD(param) != BN_CLICKED ) + return ERROR_SUCCESS; + + TRACE("clicked radio button %s, set %s\n", debugstr_w(control->name), + debugstr_w(control->property)); + + MSI_SetPropertyW( dialog->package, control->property, control->name ); + + return msi_dialog_button_handler( dialog, control, param ); +} + +static LRESULT msi_dialog_oncommand( msi_dialog *dialog, WPARAM param, HWND hwnd ) +{ + msi_control *control = NULL; + + TRACE("%p %p %08x\n", dialog, hwnd, param); + + switch (param) + { + case 1: /* enter */ + control = msi_dialog_find_control( dialog, dialog->control_default ); + break; + case 2: /* escape */ + control = msi_dialog_find_control( dialog, dialog->control_cancel ); + break; + default: + control = msi_dialog_find_control_by_hwnd( dialog, hwnd ); + } + + if( control ) + { + if( control->handler ) + { + control->handler( dialog, control, param ); + msi_dialog_evaluate_control_conditions( dialog ); + } + } + else + ERR("button click from nowhere %p %d %p\n", dialog, param, hwnd); + return 0; +} + +static void msi_dialog_setfocus( msi_dialog *dialog ) +{ + HWND hwnd = dialog->hWndFocus; + + hwnd = GetNextDlgTabItem( dialog->hwnd, hwnd, TRUE); + hwnd = GetNextDlgTabItem( dialog->hwnd, hwnd, FALSE); + SetFocus( hwnd ); + dialog->hWndFocus = hwnd; +} + +static LRESULT WINAPI MSIDialog_WndProc( HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam ) +{ + msi_dialog *dialog = (LPVOID) GetWindowLongPtrW( hwnd, GWLP_USERDATA ); + + TRACE("0x%04x\n", msg); + + switch (msg) + { + case WM_CREATE: + return msi_dialog_oncreate( hwnd, (LPCREATESTRUCTW)lParam ); + + case WM_COMMAND: + return msi_dialog_oncommand( dialog, wParam, (HWND)lParam ); + + case WM_ACTIVATE: + if( LOWORD(wParam) == WA_INACTIVE ) + dialog->hWndFocus = GetFocus(); + else + msi_dialog_setfocus( dialog ); + return 0; + + case WM_SETFOCUS: + msi_dialog_setfocus( dialog ); + return 0; + + /* bounce back to our subclassed static control */ + case WM_CTLCOLORSTATIC: + return SendMessageW( (HWND) lParam, WM_CTLCOLORSTATIC, wParam, lParam ); + + case WM_DESTROY: + dialog->hwnd = NULL; + return 0; + } + return DefWindowProcW(hwnd, msg, wParam, lParam); +} + +static LRESULT WINAPI MSIRadioGroup_WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + WNDPROC oldproc = (WNDPROC) GetPropW(hWnd, szButtonData); + + TRACE("hWnd %p msg %04x wParam 0x%08x lParam 0x%08lx\n", hWnd, msg, wParam, lParam); + + if (msg == WM_COMMAND) /* Forward notifications to dialog */ + SendMessageW(GetParent(hWnd), msg, wParam, lParam); + + return CallWindowProcW(oldproc, hWnd, msg, wParam, lParam); +} + +static LRESULT WINAPI MSIHiddenWindowProc( HWND hwnd, UINT msg, + WPARAM wParam, LPARAM lParam ) +{ + msi_dialog *dialog = (msi_dialog*) lParam; + + TRACE("%d %p\n", msg, dialog); + + switch (msg) + { + case WM_MSI_DIALOG_CREATE: + return msi_dialog_run_message_loop( dialog ); + case WM_MSI_DIALOG_DESTROY: + msi_dialog_destroy( dialog ); + return 0; + } + return DefWindowProcW( hwnd, msg, wParam, lParam ); +} + +/* functions that interface to other modules within MSI */ + +msi_dialog *msi_dialog_create( MSIPACKAGE* package, LPCWSTR szDialogName, + msi_dialog_event_handler event_handler ) +{ + MSIRECORD *rec = NULL; + msi_dialog *dialog; + + TRACE("%p %s\n", package, debugstr_w(szDialogName)); + + /* allocate the structure for the dialog to use */ + dialog = msi_alloc_zero( sizeof *dialog + sizeof(WCHAR)*strlenW(szDialogName) ); + if( !dialog ) + return NULL; + strcpyW( dialog->name, szDialogName ); + msiobj_addref( &package->hdr ); + dialog->package = package; + dialog->event_handler = event_handler; + dialog->finished = 0; + list_init( &dialog->controls ); + + /* verify that the dialog exists */ + rec = msi_get_dialog_record( dialog ); + if( !rec ) + { + msiobj_release( &package->hdr ); + msi_free( dialog ); + return NULL; + } + dialog->attributes = MSI_RecordGetInteger( rec, 6 ); + dialog->control_default = strdupW( MSI_RecordGetString( rec, 9 ) ); + dialog->control_cancel = strdupW( MSI_RecordGetString( rec, 10 ) ); + msiobj_release( &rec->hdr ); + + return dialog; +} + +static void msi_process_pending_messages( HWND hdlg ) +{ + MSG msg; + + while( PeekMessageW( &msg, 0, 0, 0, PM_REMOVE ) ) + { + if( hdlg && IsDialogMessageW( hdlg, &msg )) + continue; + TranslateMessage( &msg ); + DispatchMessageW( &msg ); + } +} + +void msi_dialog_end_dialog( msi_dialog *dialog ) +{ + TRACE("%p\n", dialog); + dialog->finished = 1; + PostMessageW(dialog->hwnd, WM_NULL, 0, 0); +} + +void msi_dialog_check_messages( HANDLE handle ) +{ + DWORD r; + + /* in threads other than the UI thread, block */ + if( uiThreadId != GetCurrentThreadId() ) + { + if( handle ) + WaitForSingleObject( handle, INFINITE ); + return; + } + + /* there's two choices for the UI thread */ + while (1) + { + msi_process_pending_messages( NULL ); + + if( !handle ) + break; + + /* + * block here until somebody creates a new dialog or + * the handle we're waiting on becomes ready + */ + r = MsgWaitForMultipleObjects( 1, &handle, 0, INFINITE, QS_ALLINPUT ); + if( r == WAIT_OBJECT_0 ) + break; + } +} + +UINT msi_dialog_run_message_loop( msi_dialog *dialog ) +{ + HWND hwnd; + + if( !(dialog->attributes & msidbDialogAttributesVisible) ) + return ERROR_SUCCESS; + + if( uiThreadId != GetCurrentThreadId() ) + return SendMessageW( hMsiHiddenWindow, WM_MSI_DIALOG_CREATE, 0, (LPARAM) dialog ); + + /* create the dialog window, don't show it yet */ + hwnd = CreateWindowW( szMsiDialogClass, dialog->name, WS_OVERLAPPEDWINDOW, + CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, + NULL, NULL, NULL, dialog ); + if( !hwnd ) + { + ERR("Failed to create dialog %s\n", debugstr_w( dialog->name )); + return ERROR_FUNCTION_FAILED; + } + + ShowWindow( hwnd, SW_SHOW ); + /* UpdateWindow( hwnd ); - and causes the transparent static controls not to paint */ + + if( dialog->attributes & msidbDialogAttributesModal ) + { + while( !dialog->finished ) + { + MsgWaitForMultipleObjects( 0, NULL, 0, INFINITE, QS_ALLEVENTS ); + msi_process_pending_messages( dialog->hwnd ); + } + } + else + return ERROR_IO_PENDING; + + return ERROR_SUCCESS; +} + +void msi_dialog_do_preview( msi_dialog *dialog ) +{ + TRACE("\n"); + dialog->attributes |= msidbDialogAttributesVisible; + dialog->attributes &= ~msidbDialogAttributesModal; + msi_dialog_run_message_loop( dialog ); +} + +void msi_dialog_destroy( msi_dialog *dialog ) +{ + if( uiThreadId != GetCurrentThreadId() ) + { + SendMessageW( hMsiHiddenWindow, WM_MSI_DIALOG_DESTROY, 0, (LPARAM) dialog ); + return; + } + + if( dialog->hwnd ) + ShowWindow( dialog->hwnd, SW_HIDE ); + + if( dialog->hwnd ) + DestroyWindow( dialog->hwnd ); + + /* destroy the list of controls */ + while( !list_empty( &dialog->controls ) ) + { + msi_control *t = LIST_ENTRY( list_head( &dialog->controls ), + msi_control, entry ); + list_remove( &t->entry ); + /* leave dialog->hwnd - destroying parent destroys child windows */ + msi_free( t->property ); + msi_free( t->value ); + if( t->hBitmap ) + DeleteObject( t->hBitmap ); + if( t->hIcon ) + DestroyIcon( t->hIcon ); + msi_free( t->tabnext ); + msi_free( t ); + } + + /* destroy the list of fonts */ + while( dialog->font_list ) + { + msi_font *t = dialog->font_list; + dialog->font_list = t->next; + DeleteObject( t->hfont ); + msi_free( t ); + } + msi_free( dialog->default_font ); + + msi_free( dialog->control_default ); + msi_free( dialog->control_cancel ); + msiobj_release( &dialog->package->hdr ); + dialog->package = NULL; + msi_free( dialog ); +} + +BOOL msi_dialog_register_class( void ) +{ + WNDCLASSW cls; + + ZeroMemory( &cls, sizeof cls ); + cls.lpfnWndProc = MSIDialog_WndProc; + cls.hInstance = NULL; + cls.hIcon = LoadIconW(0, (LPWSTR)IDI_APPLICATION); + cls.hCursor = LoadCursorW(0, (LPWSTR)IDC_ARROW); + cls.hbrBackground = (HBRUSH)(COLOR_3DFACE + 1); + cls.lpszMenuName = NULL; + cls.lpszClassName = szMsiDialogClass; + + if( !RegisterClassW( &cls ) ) + return FALSE; + + cls.lpfnWndProc = MSIHiddenWindowProc; + cls.lpszClassName = szMsiHiddenWindow; + + if( !RegisterClassW( &cls ) ) + return FALSE; + + uiThreadId = GetCurrentThreadId(); + + hMsiHiddenWindow = CreateWindowW( szMsiHiddenWindow, NULL, WS_OVERLAPPED, + 0, 0, 100, 100, NULL, NULL, NULL, NULL ); + if( !hMsiHiddenWindow ) + return FALSE; + + return TRUE; +} + +void msi_dialog_unregister_class( void ) +{ + DestroyWindow( hMsiHiddenWindow ); + hMsiHiddenWindow = NULL; + UnregisterClassW( szMsiDialogClass, NULL ); + UnregisterClassW( szMsiHiddenWindow, NULL ); + uiThreadId = 0; +} diff --git a/reactos/dll/msi/distinct.c b/reactos/dll/msi/distinct.c new file mode 100644 index 00000000000..f5b6bd225d8 --- /dev/null +++ b/reactos/dll/msi/distinct.c @@ -0,0 +1,292 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002 Mike McCormack 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" +#include "winnls.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msidb); + +typedef struct tagDISTINCTSET +{ + UINT val; + UINT count; + UINT row; + struct tagDISTINCTSET *nextrow; + struct tagDISTINCTSET *nextcol; +} DISTINCTSET; + +typedef struct tagMSIDISTINCTVIEW +{ + MSIVIEW view; + MSIDATABASE *db; + MSIVIEW *table; + UINT row_count; + UINT *translation; +} MSIDISTINCTVIEW; + +static DISTINCTSET ** distinct_insert( DISTINCTSET **x, UINT val, UINT row ) +{ + /* horrible O(n) find */ + while( *x ) + { + if( (*x)->val == val ) + { + (*x)->count++; + return x; + } + x = &(*x)->nextrow; + } + + /* nothing found, so add one */ + *x = msi_alloc( sizeof (DISTINCTSET) ); + if( *x ) + { + (*x)->val = val; + (*x)->count = 1; + (*x)->row = row; + (*x)->nextrow = NULL; + (*x)->nextcol = NULL; + } + return x; +} + +static void distinct_free( DISTINCTSET *x ) +{ + while( x ) + { + DISTINCTSET *next = x->nextrow; + distinct_free( x->nextcol ); + msi_free( x ); + x = next; + } +} + +static UINT DISTINCT_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ) +{ + MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; + + TRACE("%p %d %d %p\n", dv, row, col, val ); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + if( row >= dv->row_count ) + return ERROR_INVALID_PARAMETER; + + row = dv->translation[ row ]; + + return dv->table->ops->fetch_int( dv->table, row, col, val ); +} + +static UINT DISTINCT_execute( struct tagMSIVIEW *view, MSIRECORD *record ) +{ + MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; + UINT r, i, j, r_count, c_count; + DISTINCTSET *rowset = NULL; + + TRACE("%p %p\n", dv, record); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + r = dv->table->ops->execute( dv->table, record ); + if( r != ERROR_SUCCESS ) + return r; + + r = dv->table->ops->get_dimensions( dv->table, &r_count, &c_count ); + if( r != ERROR_SUCCESS ) + return r; + + dv->translation = msi_alloc( r_count*sizeof(UINT) ); + if( !dv->translation ) + return ERROR_FUNCTION_FAILED; + + /* build it */ + for( i=0; itable->ops->fetch_int( dv->table, i, j, &val ); + if( r != ERROR_SUCCESS ) + { + ERR("Failed to fetch int at %d %d\n", i, j ); + distinct_free( rowset ); + return r; + } + x = distinct_insert( x, val, i ); + if( !*x ) + { + ERR("Failed to insert at %d %d\n", i, j ); + distinct_free( rowset ); + return ERROR_FUNCTION_FAILED; + } + if( j != c_count ) + x = &(*x)->nextcol; + } + + /* check if it was distinct and if so, include it */ + if( (*x)->row == i ) + { + TRACE("Row %d -> %d\n", dv->row_count, i); + dv->translation[dv->row_count++] = i; + } + } + + distinct_free( rowset ); + + return ERROR_SUCCESS; +} + +static UINT DISTINCT_close( struct tagMSIVIEW *view ) +{ + MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; + + TRACE("%p\n", dv ); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + msi_free( dv->translation ); + dv->translation = NULL; + dv->row_count = 0; + + return dv->table->ops->close( dv->table ); +} + +static UINT DISTINCT_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols ) +{ + MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; + + TRACE("%p %p %p\n", dv, rows, cols ); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + if( rows ) + { + if( !dv->translation ) + return ERROR_FUNCTION_FAILED; + *rows = dv->row_count; + } + + return dv->table->ops->get_dimensions( dv->table, NULL, cols ); +} + +static UINT DISTINCT_get_column_info( struct tagMSIVIEW *view, + UINT n, LPWSTR *name, UINT *type ) +{ + MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; + + TRACE("%p %d %p %p\n", dv, n, name, type ); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + return dv->table->ops->get_column_info( dv->table, n, name, type ); +} + +static UINT DISTINCT_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, + MSIRECORD *rec ) +{ + MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; + + TRACE("%p %d %p\n", dv, eModifyMode, rec ); + + if( !dv->table ) + return ERROR_FUNCTION_FAILED; + + return dv->table->ops->modify( dv->table, eModifyMode, rec ); +} + +static UINT DISTINCT_delete( struct tagMSIVIEW *view ) +{ + MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view; + + TRACE("%p\n", dv ); + + if( dv->table ) + dv->table->ops->delete( dv->table ); + + msi_free( dv->translation ); + msiobj_release( &dv->db->hdr ); + msi_free( dv ); + + return ERROR_SUCCESS; +} + + +MSIVIEWOPS distinct_ops = +{ + DISTINCT_fetch_int, + NULL, + NULL, + NULL, + DISTINCT_execute, + DISTINCT_close, + DISTINCT_get_dimensions, + DISTINCT_get_column_info, + DISTINCT_modify, + DISTINCT_delete +}; + +UINT DISTINCT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table ) +{ + MSIDISTINCTVIEW *dv = NULL; + UINT count = 0, r; + + TRACE("%p\n", dv ); + + r = table->ops->get_dimensions( table, NULL, &count ); + if( r != ERROR_SUCCESS ) + { + ERR("can't get table dimensions\n"); + return r; + } + + dv = msi_alloc_zero( sizeof *dv ); + if( !dv ) + return ERROR_FUNCTION_FAILED; + + /* fill the structure */ + dv->view.ops = &distinct_ops; + msiobj_addref( &db->hdr ); + dv->db = db; + dv->table = table; + dv->translation = NULL; + dv->row_count = 0; + *view = (MSIVIEW*) dv; + + return ERROR_SUCCESS; +} diff --git a/reactos/dll/msi/events.c b/reactos/dll/msi/events.c new file mode 100644 index 00000000000..393154a684f --- /dev/null +++ b/reactos/dll/msi/events.c @@ -0,0 +1,410 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2005 Aric Stewart 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* +http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/controlevent_overview.asp +*/ + +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "winreg.h" +#include "msi.h" +#include "msipriv.h" +#include "action.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +typedef UINT (*EVENTHANDLER)(MSIPACKAGE*,LPCWSTR,msi_dialog *); + +struct _events { + LPCSTR event; + EVENTHANDLER handler; +}; + +struct subscriber { + struct list entry; + LPWSTR event; + LPWSTR control; + LPWSTR attribute; +}; + +UINT ControlEvent_HandleControlEvent(MSIPACKAGE *, LPCWSTR, LPCWSTR, msi_dialog*); + +/* + * Create a dialog box and run it if it's modal + */ +static UINT event_do_dialog( MSIPACKAGE *package, LPCWSTR name, BOOL destroy_modeless ) +{ + msi_dialog *dialog; + UINT r; + + /* create a new dialog */ + dialog = msi_dialog_create( package, name, + ControlEvent_HandleControlEvent ); + if( dialog ) + { + /* kill the current modeless dialog */ + if( destroy_modeless && package->dialog ) + { + msi_dialog_destroy( package->dialog ); + package->dialog = NULL; + } + + /* modeless dialogs return an error message */ + r = msi_dialog_run_message_loop( dialog ); + if( r == ERROR_SUCCESS ) + msi_dialog_destroy( dialog ); + else + package->dialog = dialog; + } + else + r = ERROR_FUNCTION_FAILED; + + return r; +} + + +/* + * End a modal dialog box + */ +static UINT ControlEvent_EndDialog(MSIPACKAGE* package, LPCWSTR argument, + msi_dialog* dialog) +{ + static const WCHAR szExit[] = { + 'E','x','i','t',0}; + static const WCHAR szRetry[] = { + 'R','e','t','r','y',0}; + static const WCHAR szIgnore[] = { + 'I','g','n','o','r','e',0}; + static const WCHAR szReturn[] = { + 'R','e','t','u','r','n',0}; + + if (lstrcmpW(argument,szExit)==0) + package->CurrentInstallState = ERROR_INSTALL_USEREXIT; + else if (lstrcmpW(argument, szRetry) == 0) + package->CurrentInstallState = ERROR_INSTALL_SUSPEND; + else if (lstrcmpW(argument, szIgnore) == 0) + package->CurrentInstallState = -1; + else if (lstrcmpW(argument, szReturn) == 0) + package->CurrentInstallState = ERROR_SUCCESS; + else + { + ERR("Unknown argument string %s\n",debugstr_w(argument)); + package->CurrentInstallState = ERROR_FUNCTION_FAILED; + } + + ControlEvent_CleanupSubscriptions(package); + msi_dialog_end_dialog( dialog ); + return ERROR_SUCCESS; +} + +/* + * transition from one modal dialog to another modal dialog + */ +static UINT ControlEvent_NewDialog(MSIPACKAGE* package, LPCWSTR argument, + msi_dialog *dialog) +{ + /* store the name of the next dialog, and signal this one to end */ + package->next_dialog = strdupW(argument); + ControlEvent_CleanupSubscriptions(package); + msi_dialog_end_dialog( dialog ); + return ERROR_SUCCESS; +} + +/* + * Create a new child dialog of an existing modal dialog + */ +static UINT ControlEvent_SpawnDialog(MSIPACKAGE* package, LPCWSTR argument, + msi_dialog *dialog) +{ + /* don't destroy a modeless dialogs that might be our parent */ + event_do_dialog( package, argument, FALSE ); + if( package->CurrentInstallState != ERROR_SUCCESS ) + msi_dialog_end_dialog( dialog ); + return ERROR_SUCCESS; +} + +/* + * Creates a dialog that remains up for a period of time + * based on a condition + */ +static UINT ControlEvent_SpawnWaitDialog(MSIPACKAGE* package, LPCWSTR argument, + msi_dialog* dialog) +{ + FIXME("Doing Nothing\n"); + return ERROR_SUCCESS; +} + +static UINT ControlEvent_DoAction(MSIPACKAGE* package, LPCWSTR argument, + msi_dialog* dialog) +{ + ACTION_PerformAction(package,argument,TRUE); + return ERROR_SUCCESS; +} + +static UINT ControlEvent_AddLocal(MSIPACKAGE* package, LPCWSTR argument, + msi_dialog* dialog) +{ + static const WCHAR szAll[] = {'A','L','L',0}; + MSIFEATURE *feature = NULL; + + if (lstrcmpW(szAll,argument)) + { + MSI_SetFeatureStateW(package,argument,INSTALLSTATE_LOCAL); + } + else + { + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + feature->ActionRequest = INSTALLSTATE_LOCAL; + feature->Action = INSTALLSTATE_LOCAL; + } + ACTION_UpdateComponentStates(package,argument); + } + return ERROR_SUCCESS; +} + +static UINT ControlEvent_Remove(MSIPACKAGE* package, LPCWSTR argument, + msi_dialog* dialog) +{ + static const WCHAR szAll[] = {'A','L','L',0}; + MSIFEATURE *feature = NULL; + + if (lstrcmpW(szAll,argument)) + { + MSI_SetFeatureStateW(package,argument,INSTALLSTATE_ABSENT); + } + else + { + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + feature->ActionRequest = INSTALLSTATE_ABSENT; + feature->Action= INSTALLSTATE_ABSENT; + } + ACTION_UpdateComponentStates(package,argument); + } + return ERROR_SUCCESS; +} + +static UINT ControlEvent_AddSource(MSIPACKAGE* package, LPCWSTR argument, + msi_dialog* dialog) +{ + static const WCHAR szAll[] = {'A','L','L',0}; + MSIFEATURE *feature = NULL; + + if (lstrcmpW(szAll,argument)) + { + MSI_SetFeatureStateW(package,argument,INSTALLSTATE_SOURCE); + } + else + { + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + feature->ActionRequest = INSTALLSTATE_SOURCE; + feature->Action = INSTALLSTATE_SOURCE; + } + ACTION_UpdateComponentStates(package,argument); + } + return ERROR_SUCCESS; +} + +static UINT ControlEvent_SetTargetPath(MSIPACKAGE* package, LPCWSTR argument, + msi_dialog* dialog) +{ + LPWSTR path = msi_dup_property( package, argument ); + UINT r; + /* failure to set the path halts the executing of control events */ + r = MSI_SetTargetPathW(package, argument, path); + msi_free(path); + return r; +} + +static UINT ControlEvent_Reset(MSIPACKAGE* package, LPCWSTR argument, + msi_dialog* dialog) +{ + msi_dialog_reset(dialog); + return ERROR_SUCCESS; +} + +/* + * Subscribed events + */ +static void free_subscriber( struct subscriber *sub ) +{ + msi_free(sub->event); + msi_free(sub->control); + msi_free(sub->attribute); + msi_free(sub); +} + +VOID ControlEvent_SubscribeToEvent( MSIPACKAGE *package, LPCWSTR event, + LPCWSTR control, LPCWSTR attribute ) +{ + struct subscriber *sub; + + sub = msi_alloc(sizeof (*sub)); + if( !sub ) + return; + sub->event = strdupW(event); + sub->control = strdupW(control); + sub->attribute = strdupW(attribute); + list_add_tail( &package->subscriptions, &sub->entry ); +} + +VOID ControlEvent_UnSubscribeToEvent( MSIPACKAGE *package, LPCWSTR event, + LPCWSTR control, LPCWSTR attribute ) +{ + struct list *i, *t; + struct subscriber *sub; + + LIST_FOR_EACH_SAFE( i, t, &package->subscriptions ) + { + sub = LIST_ENTRY( i, struct subscriber, entry ); + + if( lstrcmpiW(sub->control,control) ) + continue; + if( lstrcmpiW(sub->attribute,attribute) ) + continue; + if( lstrcmpiW(sub->event,event) ) + continue; + list_remove( &sub->entry ); + free_subscriber( sub ); + } +} + +VOID ControlEvent_FireSubscribedEvent( MSIPACKAGE *package, LPCWSTR event, + MSIRECORD *rec ) +{ + struct subscriber *sub; + + TRACE("Firing Event %s\n",debugstr_w(event)); + + if (!package->dialog) + return; + + LIST_FOR_EACH_ENTRY( sub, &package->subscriptions, struct subscriber, entry ) + { + if (lstrcmpiW(sub->event, event)) + continue; + msi_dialog_handle_event( package->dialog, sub->control, + sub->attribute, rec ); + } +} + +VOID ControlEvent_CleanupSubscriptions(MSIPACKAGE *package) +{ + struct list *i, *t; + struct subscriber *sub; + + LIST_FOR_EACH_SAFE( i, t, &package->subscriptions ) + { + sub = LIST_ENTRY( i, struct subscriber, entry ); + + list_remove( &sub->entry ); + free_subscriber( sub ); + } +} + +/* + * ACTION_DialogBox() + * + * Return ERROR_SUCCESS if dialog is process and ERROR_FUNCTION_FAILED + * if the given parameter is not a dialog box + */ +UINT ACTION_DialogBox( MSIPACKAGE* package, LPCWSTR szDialogName ) +{ + UINT r = ERROR_SUCCESS; + + if( package->next_dialog ) + ERR("Already a next dialog... ignoring it\n"); + package->next_dialog = NULL; + + /* + * Dialogs are chained by filling in the next_dialog member + * of the package structure, then terminating the current dialog. + * The code below sees the next_dialog member set, and runs the + * next dialog. + * We fall out of the loop below if we come across a modeless + * dialog, as it returns ERROR_IO_PENDING when we try to run + * its message loop. + */ + r = event_do_dialog( package, szDialogName, TRUE ); + while( r == ERROR_SUCCESS && package->next_dialog ) + { + LPWSTR name = package->next_dialog; + + package->next_dialog = NULL; + r = event_do_dialog( package, name, TRUE ); + msi_free( name ); + } + + if( r == ERROR_IO_PENDING ) + r = ERROR_SUCCESS; + + return r; +} + +struct _events Events[] = { + { "EndDialog",ControlEvent_EndDialog }, + { "NewDialog",ControlEvent_NewDialog }, + { "SpawnDialog",ControlEvent_SpawnDialog }, + { "SpawnWaitDialog",ControlEvent_SpawnWaitDialog }, + { "DoAction",ControlEvent_DoAction }, + { "AddLocal",ControlEvent_AddLocal }, + { "Remove",ControlEvent_Remove }, + { "AddSource",ControlEvent_AddSource }, + { "SetTargetPath",ControlEvent_SetTargetPath }, + { "Reset",ControlEvent_Reset }, + { NULL,NULL }, +}; + +UINT ControlEvent_HandleControlEvent(MSIPACKAGE *package, LPCWSTR event, + LPCWSTR argument, msi_dialog* dialog) +{ + int i = 0; + UINT rc = ERROR_SUCCESS; + + TRACE("Handling Control Event %s\n",debugstr_w(event)); + if (!event) + return rc; + + while( Events[i].event != NULL) + { + LPWSTR wevent = strdupAtoW(Events[i].event); + if (lstrcmpW(wevent,event)==0) + { + msi_free(wevent); + rc = Events[i].handler(package,argument,dialog); + return rc; + } + msi_free(wevent); + i++; + } + FIXME("unhandled control event %s arg(%s)\n", + debugstr_w(event), debugstr_w(argument)); + return rc; +} diff --git a/reactos/dll/msi/files.c b/reactos/dll/msi/files.c new file mode 100644 index 00000000000..694f6d668a1 --- /dev/null +++ b/reactos/dll/msi/files.c @@ -0,0 +1,887 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2005 Aric Stewart 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +/* + * Actions dealing with files These are + * + * InstallFiles + * DuplicateFiles + * MoveFiles (TODO) + * PatchFiles (TODO) + * RemoveDuplicateFiles(TODO) + * RemoveFiles(TODO) + */ + +#include + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "fdi.h" +#include "msi.h" +#include "msidefs.h" +#include "msvcrt/fcntl.h" +#include "msipriv.h" +#include "winuser.h" +#include "wine/unicode.h" +#include "action.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +extern const WCHAR szInstallFiles[]; +extern const WCHAR szDuplicateFiles[]; +extern const WCHAR szMoveFiles[]; +extern const WCHAR szPatchFiles[]; +extern const WCHAR szRemoveDuplicateFiles[]; +extern const WCHAR szRemoveFiles[]; + +static const WCHAR cszTempFolder[]= {'T','e','m','p','F','o','l','d','e','r',0}; + + +/* + * This is a helper function for handling embedded cabinet media + */ +static UINT writeout_cabinet_stream(MSIPACKAGE *package, LPCWSTR stream_name, + WCHAR* source) +{ + UINT rc; + USHORT* data; + UINT size; + DWORD write; + HANDLE the_file; + WCHAR tmp[MAX_PATH]; + + rc = read_raw_stream_data(package->db,stream_name,&data,&size); + if (rc != ERROR_SUCCESS) + return rc; + + write = MAX_PATH; + if (MSI_GetPropertyW(package, cszTempFolder, tmp, &write)) + GetTempPathW(MAX_PATH,tmp); + + GetTempFileNameW(tmp,stream_name,0,source); + + track_tempfile(package,strrchrW(source,'\\'), source); + the_file = CreateFileW(source, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + + if (the_file == INVALID_HANDLE_VALUE) + { + ERR("Unable to create file %s\n",debugstr_w(source)); + rc = ERROR_FUNCTION_FAILED; + goto end; + } + + WriteFile(the_file,data,size,&write,NULL); + CloseHandle(the_file); + TRACE("wrote %li bytes to %s\n",write,debugstr_w(source)); +end: + msi_free(data); + return rc; +} + + +/* Support functions for FDI functions */ +typedef struct +{ + MSIPACKAGE* package; + LPCSTR cab_path; +} CabData; + +static void * cabinet_alloc(ULONG cb) +{ + return msi_alloc(cb); +} + +static void cabinet_free(void *pv) +{ + msi_free(pv); +} + +static INT_PTR cabinet_open(char *pszFile, int oflag, int pmode) +{ + HANDLE handle; + DWORD dwAccess = 0; + DWORD dwShareMode = 0; + DWORD dwCreateDisposition = OPEN_EXISTING; + switch (oflag & _O_ACCMODE) + { + case _O_RDONLY: + dwAccess = GENERIC_READ; + dwShareMode = FILE_SHARE_READ | FILE_SHARE_DELETE; + break; + case _O_WRONLY: + dwAccess = GENERIC_WRITE; + dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + break; + case _O_RDWR: + dwAccess = GENERIC_READ | GENERIC_WRITE; + dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE; + break; + } + if ((oflag & (_O_CREAT | _O_EXCL)) == (_O_CREAT | _O_EXCL)) + dwCreateDisposition = CREATE_NEW; + else if (oflag & _O_CREAT) + dwCreateDisposition = CREATE_ALWAYS; + handle = CreateFileA( pszFile, dwAccess, dwShareMode, NULL, + dwCreateDisposition, 0, NULL ); + if (handle == INVALID_HANDLE_VALUE) + return 0; + return (INT_PTR) handle; +} + +static UINT cabinet_read(INT_PTR hf, void *pv, UINT cb) +{ + HANDLE handle = (HANDLE) hf; + DWORD dwRead; + if (ReadFile(handle, pv, cb, &dwRead, NULL)) + return dwRead; + return 0; +} + +static UINT cabinet_write(INT_PTR hf, void *pv, UINT cb) +{ + HANDLE handle = (HANDLE) hf; + DWORD dwWritten; + if (WriteFile(handle, pv, cb, &dwWritten, NULL)) + return dwWritten; + return 0; +} + +static int cabinet_close(INT_PTR hf) +{ + HANDLE handle = (HANDLE) hf; + return CloseHandle(handle) ? 0 : -1; +} + +static long cabinet_seek(INT_PTR hf, long dist, int seektype) +{ + HANDLE handle = (HANDLE) hf; + /* flags are compatible and so are passed straight through */ + return SetFilePointer(handle, dist, NULL, seektype); +} + +static void msi_file_update_ui( MSIPACKAGE *package, MSIFILE *f ) +{ + MSIRECORD *uirow; + LPWSTR uipath, p; + + /* the UI chunk */ + uirow = MSI_CreateRecord( 9 ); + MSI_RecordSetStringW( uirow, 1, f->FileName ); + uipath = strdupW( f->TargetPath ); + p = strrchrW(uipath,'\\'); + if (p) + p[1]=0; + MSI_RecordSetStringW( uirow, 9, uipath); + MSI_RecordSetInteger( uirow, 6, f->FileSize ); + ui_actiondata( package, szInstallFiles, uirow); + msiobj_release( &uirow->hdr ); + msi_free( uipath ); + ui_progress( package, 2, f->FileSize, 0, 0); +} + +static INT_PTR cabinet_notify(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin) +{ + switch (fdint) + { + case fdintCOPY_FILE: + { + CabData *data = (CabData*) pfdin->pv; + HANDLE handle; + LPWSTR file; + MSIFILE *f; + + file = strdupAtoW(pfdin->psz1); + f = get_loaded_file(data->package, file); + msi_free(file); + + if (!f) + { + ERR("Unknown File in Cabinet (%s)\n",debugstr_a(pfdin->psz1)); + return 0; + } + + if (f->state != msifs_missing && f->state != msifs_overwrite) + { + TRACE("Skipping extraction of %s\n",debugstr_a(pfdin->psz1)); + return 0; + } + + msi_file_update_ui( data->package, f ); + + TRACE("extracting %s\n", debugstr_w(f->TargetPath) ); + + handle = CreateFileW( f->TargetPath, GENERIC_READ | GENERIC_WRITE, 0, + NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, NULL ); + if ( handle == INVALID_HANDLE_VALUE ) + { + ERR("failed to create %s (error %ld)\n", + debugstr_w( f->TargetPath ), GetLastError() ); + return 0; + } + + f->state = msifs_installed; + return (INT_PTR) handle; + } + case fdintCLOSE_FILE_INFO: + { + FILETIME ft; + FILETIME ftLocal; + HANDLE handle = (HANDLE) pfdin->hf; + + if (!DosDateTimeToFileTime(pfdin->date, pfdin->time, &ft)) + return -1; + if (!LocalFileTimeToFileTime(&ft, &ftLocal)) + return -1; + if (!SetFileTime(handle, &ftLocal, 0, &ftLocal)) + return -1; + CloseHandle(handle); + return 1; + } + default: + return 0; + } +} + +/*********************************************************************** + * extract_cabinet_file + * + * Extract files from a cab file. + */ +static BOOL extract_cabinet_file(MSIPACKAGE* package, LPCWSTR source, + LPCWSTR path) +{ + HFDI hfdi; + ERF erf; + BOOL ret; + char *cabinet; + char *cab_path; + CabData data; + + TRACE("Extracting %s to %s\n",debugstr_w(source), debugstr_w(path)); + + hfdi = FDICreate(cabinet_alloc, + cabinet_free, + cabinet_open, + cabinet_read, + cabinet_write, + cabinet_close, + cabinet_seek, + 0, + &erf); + if (!hfdi) + { + ERR("FDICreate failed\n"); + return FALSE; + } + + if (!(cabinet = strdupWtoA( source ))) + { + FDIDestroy(hfdi); + return FALSE; + } + if (!(cab_path = strdupWtoA( path ))) + { + FDIDestroy(hfdi); + msi_free(cabinet); + return FALSE; + } + + data.package = package; + data.cab_path = cab_path; + + ret = FDICopy(hfdi, cabinet, "", 0, cabinet_notify, NULL, &data); + + if (!ret) + ERR("FDICopy failed\n"); + + FDIDestroy(hfdi); + + msi_free(cabinet); + msi_free(cab_path); + + return ret; +} + +static VOID set_file_source(MSIPACKAGE* package, MSIFILE* file, MSICOMPONENT* + comp, LPCWSTR path) +{ + if (file->Attributes & msidbFileAttributesNoncompressed) + { + LPWSTR p; + p = resolve_folder(package, comp->Directory, TRUE, FALSE, NULL); + file->SourcePath = build_directory_name(2, p, file->ShortName); + msi_free(p); + } + else + file->SourcePath = build_directory_name(2, path, file->File); +} + +static BOOL check_volume(LPCWSTR path, LPCWSTR want_volume, LPWSTR volume, + UINT *intype) +{ + WCHAR drive[4]; + WCHAR name[MAX_PATH]; + UINT type; + + if (!(path[0] && path[1] == ':')) + return TRUE; + + drive[0] = path[0]; + drive[1] = path[1]; + drive[2] = '\\'; + drive[3] = 0; + TRACE("Checking volume %s .. (%s)\n",debugstr_w(drive), debugstr_w(want_volume)); + type = GetDriveTypeW(drive); + TRACE("drive is of type %x\n",type); + + if (type == DRIVE_UNKNOWN || type == DRIVE_NO_ROOT_DIR || + type == DRIVE_FIXED || type == DRIVE_RAMDISK) + return TRUE; + + GetVolumeInformationW(drive, name, MAX_PATH, NULL, NULL, NULL, NULL, 0); + TRACE("Drive contains %s\n", debugstr_w(name)); + volume = strdupW(name); + if (*intype) + *intype=type; + return (strcmpiW(want_volume,name)==0); +} + +static BOOL check_for_sourcefile(LPCWSTR source) +{ + DWORD attrib = GetFileAttributesW(source); + return (!(attrib == INVALID_FILE_ATTRIBUTES)); +} + +static UINT ready_volume(MSIPACKAGE* package, LPCWSTR path, LPWSTR last_volume, + MSIRECORD *row,UINT *type ) +{ + LPWSTR volume = NULL; + LPCWSTR want_volume = MSI_RecordGetString(row, 5); + BOOL ok = check_volume(path, want_volume, volume, type); + + TRACE("Readying Volume for %s (%s, %s)\n", debugstr_w(path), + debugstr_w(want_volume), debugstr_w(last_volume)); + + if (check_for_sourcefile(path) && !ok) + { + FIXME("Found the Sourcefile but not on the correct volume.(%s,%s,%s)\n", + debugstr_w(path),debugstr_w(want_volume), debugstr_w(volume)); + return ERROR_SUCCESS; + } + + while (!ok) + { + INT rc; + LPCWSTR prompt; + LPWSTR msg; + + prompt = MSI_RecordGetString(row,3); + msg = generate_error_string(package, 1302, 1, prompt); + rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL); + msi_free(volume); + msi_free(msg); + if (rc == IDOK) + ok = check_for_sourcefile(path); + else + return ERROR_INSTALL_USEREXIT; + } + + msi_free(last_volume); + last_volume = strdupW(volume); + return ERROR_SUCCESS; +} + +struct media_info { + UINT last_sequence; + LPWSTR last_volume; + LPWSTR last_path; + DWORD count; + WCHAR source[MAX_PATH]; +}; + +static struct media_info *create_media_info( void ) +{ + struct media_info *mi; + + mi = msi_alloc( sizeof *mi ); + if (mi) + { + mi->last_sequence = 0; + mi->last_volume = NULL; + mi->last_path = NULL; + mi->count = 0; + mi->source[0] = 0; + } + + return mi; +} + +static void free_media_info( struct media_info *mi ) +{ + msi_free( mi->last_path ); + msi_free( mi ); +} + +static UINT ready_media_for_file( MSIPACKAGE *package, struct media_info *mi, + MSIFILE *file ) +{ + UINT rc = ERROR_SUCCESS; + MSIRECORD * row = 0; + static const WCHAR ExecSeqQuery[] = + {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ', + '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ', + '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ','>','=', + ' ','%', 'i',' ','O','R','D','E','R',' ','B','Y',' ', + '`','L','a','s','t','S','e','q','u','e','n','c','e','`',0}; + LPCWSTR cab, volume; + DWORD sz; + INT seq; + UINT type; + LPCWSTR prompt; + MSICOMPONENT *comp = file->Component; + + if (file->Sequence <= mi->last_sequence) + { + set_file_source(package,file,comp,mi->last_path); + TRACE("Media already ready (%u, %u)\n",file->Sequence,mi->last_sequence); + return ERROR_SUCCESS; + } + + mi->count ++; + row = MSI_QueryGetRecord(package->db, ExecSeqQuery, file->Sequence); + if (!row) + { + TRACE("Unable to query row\n"); + return ERROR_FUNCTION_FAILED; + } + + seq = MSI_RecordGetInteger(row,2); + mi->last_sequence = seq; + + volume = MSI_RecordGetString(row, 5); + prompt = MSI_RecordGetString(row, 3); + + msi_free(mi->last_path); + mi->last_path = NULL; + + if (file->Attributes & msidbFileAttributesNoncompressed) + { + mi->last_path = resolve_folder(package, comp->Directory, TRUE, FALSE, NULL); + set_file_source(package,file,comp,mi->last_path); + rc = ready_volume(package, file->SourcePath, mi->last_volume, row,&type); + + MsiSourceListAddMediaDiskW(package->ProductCode, NULL, + MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT, mi->count, volume, + prompt); + + if (type == DRIVE_REMOVABLE || type == DRIVE_CDROM || + type == DRIVE_RAMDISK) + MsiSourceListSetInfoW(package->ProductCode, NULL, + MSIINSTALLCONTEXT_USERMANAGED, + MSICODE_PRODUCT|MSISOURCETYPE_MEDIA, + INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path); + else + MsiSourceListSetInfoW(package->ProductCode, NULL, + MSIINSTALLCONTEXT_USERMANAGED, + MSICODE_PRODUCT|MSISOURCETYPE_NETWORK, + INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path); + msiobj_release(&row->hdr); + return rc; + } + + cab = MSI_RecordGetString(row,4); + if (cab) + { + TRACE("Source is CAB %s\n",debugstr_w(cab)); + /* the stream does not contain the # character */ + if (cab[0]=='#') + { + LPWSTR path; + + writeout_cabinet_stream(package,&cab[1],mi->source); + mi->last_path = strdupW(mi->source); + *(strrchrW(mi->last_path,'\\')+1)=0; + + path = msi_dup_property( package, cszSourceDir ); + + MsiSourceListAddMediaDiskW(package->ProductCode, NULL, + MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT, mi->count, + volume, prompt); + + MsiSourceListSetInfoW(package->ProductCode, NULL, + MSIINSTALLCONTEXT_USERMANAGED, + MSICODE_PRODUCT|MSISOURCETYPE_NETWORK, + INSTALLPROPERTY_LASTUSEDSOURCEW, path); + + msi_free(path); + } + else + { + sz = MAX_PATH; + mi->last_path = msi_alloc(MAX_PATH*sizeof(WCHAR)); + if (MSI_GetPropertyW(package, cszSourceDir, mi->source, &sz)) + { + ERR("No Source dir defined\n"); + rc = ERROR_FUNCTION_FAILED; + } + else + { + strcpyW(mi->last_path,mi->source); + strcatW(mi->source,cab); + + rc = ready_volume(package, mi->source, mi->last_volume, row, &type); + if (type == DRIVE_REMOVABLE || type == DRIVE_CDROM || + type == DRIVE_RAMDISK) + MsiSourceListSetInfoW(package->ProductCode, NULL, + MSIINSTALLCONTEXT_USERMANAGED, + MSICODE_PRODUCT|MSISOURCETYPE_MEDIA, + INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path); + else + MsiSourceListSetInfoW(package->ProductCode, NULL, + MSIINSTALLCONTEXT_USERMANAGED, + MSICODE_PRODUCT|MSISOURCETYPE_NETWORK, + INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path); + + /* extract the cab file into a folder in the temp folder */ + sz = MAX_PATH; + if (MSI_GetPropertyW(package, cszTempFolder,mi->last_path, &sz) + != ERROR_SUCCESS) + GetTempPathW(MAX_PATH,mi->last_path); + } + } + rc = !extract_cabinet_file(package, mi->source, mi->last_path); + } + else + { + sz = MAX_PATH; + mi->last_path = msi_alloc(MAX_PATH*sizeof(WCHAR)); + MSI_GetPropertyW(package,cszSourceDir,mi->source,&sz); + strcpyW(mi->last_path,mi->source); + rc = ready_volume(package, mi->last_path, mi->last_volume, row, &type); + + if (type == DRIVE_REMOVABLE || type == DRIVE_CDROM || + type == DRIVE_RAMDISK) + MsiSourceListSetInfoW(package->ProductCode, NULL, + MSIINSTALLCONTEXT_USERMANAGED, + MSICODE_PRODUCT|MSISOURCETYPE_MEDIA, + INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path); + else + MsiSourceListSetInfoW(package->ProductCode, NULL, + MSIINSTALLCONTEXT_USERMANAGED, + MSICODE_PRODUCT|MSISOURCETYPE_NETWORK, + INSTALLPROPERTY_LASTUSEDSOURCEW, mi->last_path); + } + set_file_source(package, file, comp, mi->last_path); + + MsiSourceListAddMediaDiskW(package->ProductCode, NULL, + MSIINSTALLCONTEXT_USERMANAGED, MSICODE_PRODUCT, mi->count, volume, + prompt); + + msiobj_release(&row->hdr); + + return rc; +} + +static UINT get_file_target(MSIPACKAGE *package, LPCWSTR file_key, + LPWSTR* file_source) +{ + MSIFILE *file; + + LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry ) + { + if (lstrcmpW( file_key, file->File )==0) + { + if (file->state >= msifs_overwrite) + { + *file_source = strdupW( file->TargetPath ); + return ERROR_SUCCESS; + } + else + return ERROR_FILE_NOT_FOUND; + } + } + + return ERROR_FUNCTION_FAILED; +} + +/* + * ACTION_InstallFiles() + * + * For efficiency, this is done in two passes: + * 1) Correct all the TargetPaths and determine what files are to be installed. + * 2) Extract Cabinets and copy files. + */ +UINT ACTION_InstallFiles(MSIPACKAGE *package) +{ + struct media_info *mi; + UINT rc = ERROR_SUCCESS; + LPWSTR ptr; + MSIFILE *file; + + /* increment progress bar each time action data is sent */ + ui_progress(package,1,1,0,0); + + /* handle the keys for the SourceList */ + ptr = strrchrW(package->PackagePath,'\\'); + if (ptr) + { + ptr ++; + MsiSourceListSetInfoW(package->ProductCode, NULL, + MSIINSTALLCONTEXT_USERMANAGED, + MSICODE_PRODUCT, + INSTALLPROPERTY_PACKAGENAMEW, ptr); + } + /* FIXME("Write DiskPrompt\n"); */ + + /* Pass 1 */ + LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry ) + { + if (!ACTION_VerifyComponentForAction( file->Component, INSTALLSTATE_LOCAL )) + { + ui_progress(package,2,file->FileSize,0,0); + TRACE("File %s is not scheduled for install\n", + debugstr_w(file->File)); + + file->state = msifs_skipped; + } + } + + /* + * Despite MSDN specifying that the CreateFolders action + * should be called before InstallFiles, some installers don't + * do that, and they seem to work correctly. We need to create + * directories here to make sure that the files can be copied. + */ + msi_create_component_directories( package ); + + mi = create_media_info(); + + /* Pass 2 */ + LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry ) + { + if (file->state != msifs_missing && file->state != msifs_overwrite) + continue; + + TRACE("Pass 2: %s\n",debugstr_w(file->File)); + + rc = ready_media_for_file( package, mi, file ); + if (rc != ERROR_SUCCESS) + { + ERR("Unable to ready media\n"); + rc = ERROR_FUNCTION_FAILED; + break; + } + + TRACE("file paths %s to %s\n",debugstr_w(file->SourcePath), + debugstr_w(file->TargetPath)); + + if (file->state != msifs_missing && file->state != msifs_overwrite) + continue; + + /* compressed files are extracted in ready_media_for_file */ + if (~file->Attributes & msidbFileAttributesNoncompressed) + { + if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW(file->TargetPath)) + ERR("compressed file wasn't extracted (%s)\n", + debugstr_w(file->TargetPath)); + continue; + } + + rc = CopyFileW(file->SourcePath,file->TargetPath,FALSE); + if (!rc) + { + rc = GetLastError(); + ERR("Unable to copy file (%s -> %s) (error %d)\n", + debugstr_w(file->SourcePath), debugstr_w(file->TargetPath), rc); + if (rc == ERROR_ALREADY_EXISTS && file->state == msifs_overwrite) + { + rc = 0; + } + else if (rc == ERROR_FILE_NOT_FOUND) + { + ERR("Source File Not Found! Continuing\n"); + rc = 0; + } + else if (file->Attributes & msidbFileAttributesVital) + { + ERR("Ignoring Error and continuing (nonvital file)...\n"); + rc = 0; + } + } + else + { + file->state = msifs_installed; + rc = ERROR_SUCCESS; + } + } + + /* cleanup */ + free_media_info( mi ); + return rc; +} + +static UINT ITERATE_DuplicateFiles(MSIRECORD *row, LPVOID param) +{ + MSIPACKAGE *package = (MSIPACKAGE*)param; + WCHAR *file_source = NULL; + WCHAR dest_name[0x100]; + LPWSTR dest_path, dest; + LPCWSTR file_key, component; + DWORD sz; + DWORD rc; + MSICOMPONENT *comp; + + component = MSI_RecordGetString(row,2); + comp = get_loaded_component(package,component); + + if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL )) + { + TRACE("Skipping copy due to disabled component %s\n", + debugstr_w(component)); + + /* the action taken was the same as the current install state */ + comp->Action = comp->Installed; + + return ERROR_SUCCESS; + } + + comp->Action = INSTALLSTATE_LOCAL; + + file_key = MSI_RecordGetString(row,3); + if (!file_key) + { + ERR("Unable to get file key\n"); + return ERROR_FUNCTION_FAILED; + } + + rc = get_file_target(package,file_key,&file_source); + + if (rc != ERROR_SUCCESS) + { + ERR("Original file unknown %s\n",debugstr_w(file_key)); + msi_free(file_source); + return ERROR_SUCCESS; + } + + if (MSI_RecordIsNull(row,4)) + strcpyW(dest_name,strrchrW(file_source,'\\')+1); + else + { + sz=0x100; + MSI_RecordGetStringW(row,4,dest_name,&sz); + reduce_to_longfilename(dest_name); + } + + if (MSI_RecordIsNull(row,5)) + { + LPWSTR p; + dest_path = strdupW(file_source); + p = strrchrW(dest_path,'\\'); + if (p) + *p=0; + } + else + { + LPCWSTR destkey; + destkey = MSI_RecordGetString(row,5); + dest_path = resolve_folder(package, destkey, FALSE,FALSE,NULL); + if (!dest_path) + { + /* try a Property */ + dest_path = msi_dup_property( package, destkey ); + if (!dest_path) + { + FIXME("Unable to get destination folder, try AppSearch properties\n"); + msi_free(file_source); + return ERROR_SUCCESS; + } + } + } + + dest = build_directory_name(2, dest_path, dest_name); + + TRACE("Duplicating file %s to %s\n",debugstr_w(file_source), + debugstr_w(dest)); + + if (strcmpW(file_source,dest)) + rc = !CopyFileW(file_source,dest,TRUE); + else + rc = ERROR_SUCCESS; + + if (rc != ERROR_SUCCESS) + ERR("Failed to copy file %s -> %s, last error %ld\n", + debugstr_w(file_source), debugstr_w(dest_path), GetLastError()); + + FIXME("We should track these duplicate files as well\n"); + + msi_free(dest_path); + msi_free(dest); + msi_free(file_source); + + return ERROR_SUCCESS; +} + +UINT ACTION_DuplicateFiles(MSIPACKAGE *package) +{ + UINT rc; + MSIQUERY * view; + static const WCHAR ExecSeqQuery[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','D','u','p','l','i','c','a','t','e','F','i','l','e','`',0}; + + rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords(view, NULL, ITERATE_DuplicateFiles, package); + msiobj_release(&view->hdr); + + return rc; +} + +UINT ACTION_RemoveFiles( MSIPACKAGE *package ) +{ + MSIFILE *file; + + LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry ) + { + if ( !file->Component ) + continue; + if ( file->Component->Installed == INSTALLSTATE_LOCAL ) + continue; + + if ( file->state == msifs_installed ) + ERR("removing installed file %s\n", debugstr_w(file->TargetPath)); + + if ( file->state != msifs_present ) + continue; + + TRACE("removing %s\n", debugstr_w(file->File) ); + if ( !DeleteFileW( file->TargetPath ) ) + ERR("failed to delete %s\n", debugstr_w(file->TargetPath) ); + file->state = msifs_missing; + } + + return ERROR_SUCCESS; +} diff --git a/reactos/dll/msi/format.c b/reactos/dll/msi/format.c new file mode 100644 index 00000000000..85f90282ab2 --- /dev/null +++ b/reactos/dll/msi/format.c @@ -0,0 +1,735 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2005 Mike McCormack for CodeWeavers + * Copyright 2005 Aric Stewart 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* +http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msiformatrecord.asp + */ + +#include +#include + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msi.h" +#include "msipriv.h" +#include "winnls.h" +#include "wine/unicode.h" +#include "action.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + + +static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr, + WCHAR** data, DWORD len, MSIRECORD* record, + BOOL* in_group); + + +static LPWSTR build_default_format(MSIRECORD* record) +{ + int i; + int count; + LPWSTR rc; + static const WCHAR fmt[] = {'%','i',':',' ','[','%','i',']',' ',0}; + WCHAR buf[11]; + + count = MSI_RecordGetFieldCount(record); + + rc = msi_alloc((11*count)*sizeof(WCHAR)); + rc[0] = 0; + for (i = 1; i <= count; i++) + { + sprintfW(buf,fmt,i,i); + strcatW(rc,buf); + } + return rc; +} + +static const WCHAR* scanW(LPCWSTR buf, WCHAR token, DWORD len) +{ + DWORD i; + for (i = 0; i < len; i++) + if (buf[i] == token) + return &buf[i]; + return NULL; +} + +/* break out helper functions for deformating */ +static LPWSTR deformat_component(MSIPACKAGE* package, LPCWSTR key, DWORD* sz) +{ + LPWSTR value = NULL; + MSICOMPONENT *comp; + + *sz = 0; + if (!package) + return NULL; + + FIXME("component key %s\n", debugstr_w(key)); + comp = get_loaded_component(package,key); + if (comp) + { + value = resolve_folder(package, comp->Directory, FALSE, FALSE, NULL); + *sz = (strlenW(value)) * sizeof(WCHAR); + } + + return value; +} + +static LPWSTR deformat_file(MSIPACKAGE* package, LPCWSTR key, DWORD* sz, + BOOL shortname) +{ + LPWSTR value = NULL; + MSIFILE *file; + + *sz = 0; + + if (!package) + return NULL; + + file = get_loaded_file( package, key ); + if (file) + { + if (!shortname) + { + value = strdupW( file->TargetPath ); + *sz = (strlenW(value)) * sizeof(WCHAR); + } + else + { + DWORD size = 0; + size = GetShortPathNameW( file->TargetPath, NULL, 0 ); + + if (size > 0) + { + *sz = (size-1) * sizeof (WCHAR); + size ++; + value = msi_alloc(size * sizeof(WCHAR)); + GetShortPathNameW( file->TargetPath, value, size ); + } + else + { + ERR("Unable to get ShortPath size (%s)\n", + debugstr_w( file->TargetPath) ); + value = NULL; + *sz = 0; + } + } + } + + return value; +} + +static LPWSTR deformat_environment(MSIPACKAGE* package, LPCWSTR key, + DWORD* chunk) +{ + LPWSTR value = NULL; + DWORD sz; + + sz = GetEnvironmentVariableW(key,NULL,0); + if (sz > 0) + { + sz++; + value = msi_alloc(sz * sizeof(WCHAR)); + GetEnvironmentVariableW(key,value,sz); + *chunk = (strlenW(value)) * sizeof(WCHAR); + } + else + { + ERR("Unknown environment variable %s\n", debugstr_w(key)); + *chunk = 0; + value = NULL; + } + return value; +} + + +static LPWSTR deformat_NULL(DWORD* chunk) +{ + LPWSTR value; + + value = msi_alloc(sizeof(WCHAR)*2); + value[0] = 0; + *chunk = sizeof(WCHAR); + return value; +} + +static LPWSTR deformat_escape(LPCWSTR key, DWORD* chunk) +{ + LPWSTR value; + + value = msi_alloc(sizeof(WCHAR)*2); + value[0] = key[0]; + *chunk = sizeof(WCHAR); + + return value; +} + + +static BOOL is_key_number(LPCWSTR key) +{ + INT index = 0; + if (key[0] == 0) + return FALSE; + + while (isdigitW(key[index])) index++; + if (key[index] == 0) + return TRUE; + else + return FALSE; +} + +static LPWSTR deformat_index(MSIRECORD* record, LPCWSTR key, DWORD* chunk ) +{ + INT index; + LPWSTR value; + + index = atoiW(key); + TRACE("record index %i\n",index); + value = msi_dup_record_field(record,index); + if (value) + *chunk = strlenW(value) * sizeof(WCHAR); + else + { + value = NULL; + *chunk = 0; + } + return value; +} + +static LPWSTR deformat_property(MSIPACKAGE* package, LPCWSTR key, DWORD* chunk) +{ + LPWSTR value; + + if (!package) + return NULL; + + value = msi_dup_property( package, key ); + + if (value) + *chunk = (strlenW(value)) * sizeof(WCHAR); + + return value; +} + +/* + * Groups cannot be nested. They are just treated as from { to next } + */ +static BOOL find_next_group(LPCWSTR source, DWORD len_remaining, + LPWSTR *group, LPCWSTR *mark, + LPCWSTR* mark2) +{ + int i; + BOOL found = FALSE; + + *mark = scanW(source,'{',len_remaining); + if (!*mark) + return FALSE; + + for (i = 1; (*mark - source) + i < len_remaining; i++) + { + if ((*mark)[i] == '}') + { + found = TRUE; + break; + } + } + if (! found) + return FALSE; + + *mark2 = &(*mark)[i]; + + i = *mark2 - *mark; + *group = msi_alloc(i*sizeof(WCHAR)); + + i -= 1; + memcpy(*group,&(*mark)[1],i*sizeof(WCHAR)); + (*group)[i] = 0; + + TRACE("Found group %s\n",debugstr_w(*group)); + return TRUE; +} + + +static BOOL find_next_outermost_key(LPCWSTR source, DWORD len_remaining, + LPWSTR *key, LPCWSTR *mark, LPCWSTR* mark2, + BOOL *nested) +{ + INT count = 0; + INT total_count = 0; + int i; + + *mark = scanW(source,'[',len_remaining); + if (!*mark) + return FALSE; + + count = 1; + total_count = 1; + *nested = FALSE; + for (i = 1; (*mark - source) + i < len_remaining && count > 0; i++) + { + if ((*mark)[i] == '[' && (*mark)[i-1] != '\\') + { + count ++; + total_count ++; + *nested = TRUE; + } + else if ((*mark)[i] == ']' && (*mark)[i-1] != '\\') + { + count --; + } + } + + if (count > 0) + return FALSE; + + *mark2 = &(*mark)[i-1]; + + i = *mark2 - *mark; + *key = msi_alloc(i*sizeof(WCHAR)); + /* do not have the [] in the key */ + i -= 1; + memcpy(*key,&(*mark)[1],i*sizeof(WCHAR)); + (*key)[i] = 0; + + TRACE("Found Key %s\n",debugstr_w(*key)); + return TRUE; +} + +static LPWSTR deformat_group(MSIPACKAGE* package, LPWSTR group, DWORD len, + MSIRECORD* record, DWORD* size) +{ + LPWSTR value = NULL; + LPCWSTR mark, mark2; + LPWSTR key; + BOOL nested; + INT failcount; + static const WCHAR fmt[] = {'{','%','s','}',0}; + UINT sz; + + if (!group || group[0] == 0) + { + *size = 0; + return NULL; + } + /* if no [] then group is returned as is */ + + if (!find_next_outermost_key(group, len, &key, &mark, &mark2, &nested)) + { + *size = (len+2)*sizeof(WCHAR); + value = msi_alloc(*size); + sprintfW(value,fmt,group); + /* do not return size of the null at the end */ + *size = (len+1)*sizeof(WCHAR); + return value; + } + + msi_free(key); + failcount = 0; + sz = deformat_string_internal(package, group, &value, strlenW(group), + record, &failcount); + if (failcount==0) + { + *size = sz * sizeof(WCHAR); + return value; + } + else if (failcount < 0) + { + LPWSTR v2; + + v2 = msi_alloc((sz+2)*sizeof(WCHAR)); + v2[0] = '{'; + memcpy(&v2[1],value,sz*sizeof(WCHAR)); + v2[sz+1]='}'; + msi_free(value); + + *size = (sz+2)*sizeof(WCHAR); + return v2; + } + else + { + msi_free(value); + *size = 0; + return NULL; + } +} + + +/* + * len is in WCHARs + * return is also in WCHARs + */ +static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr, + WCHAR** data, DWORD len, MSIRECORD* record, + INT* failcount) +{ + LPCWSTR mark = NULL; + LPCWSTR mark2 = NULL; + DWORD size=0; + DWORD chunk=0; + LPWSTR key; + LPWSTR value = NULL; + DWORD sz; + LPBYTE newdata = NULL; + const WCHAR* progress = NULL; + BOOL nested; + + if (ptr==NULL) + { + TRACE("Deformatting NULL string\n"); + *data = NULL; + return 0; + } + + TRACE("Starting with %s\n",debugstr_wn(ptr,len)); + + /* scan for special characters... fast exit */ + if ((!scanW(ptr,'[',len) || (scanW(ptr,'[',len) && !scanW(ptr,']',len))) && + (scanW(ptr,'{',len) && !scanW(ptr,'}',len))) + { + /* not formatted */ + *data = msi_alloc((len*sizeof(WCHAR))); + memcpy(*data,ptr,len*sizeof(WCHAR)); + TRACE("Returning %s\n",debugstr_wn(*data,len)); + return len; + } + + progress = ptr; + + while (progress - ptr < len) + { + /* seek out first group if existing */ + if (find_next_group(progress, len - (progress - ptr), &key, + &mark, &mark2)) + { + value = deformat_group(package, key, strlenW(key)+1, record, + &chunk); + msi_free( key ); + key = NULL; + nested = FALSE; + } + /* formatted string located */ + else if (!find_next_outermost_key(progress, len - (progress - ptr), + &key, &mark, &mark2, &nested)) + { + LPBYTE nd2; + + TRACE("after value %s\n", debugstr_wn((LPWSTR)newdata, + size/sizeof(WCHAR))); + chunk = (len - (progress - ptr)) * sizeof(WCHAR); + TRACE("after chunk is %li + %li\n",size,chunk); + if (size) + nd2 = msi_realloc(newdata,(size+chunk)); + else + nd2 = msi_alloc(chunk); + + newdata = nd2; + memcpy(&newdata[size],progress,chunk); + size+=chunk; + break; + } + + if (mark != progress) + { + LPBYTE tgt; + DWORD old_size = size; + INT cnt = (mark - progress); + TRACE("%i (%i) characters before marker\n",cnt,(mark-progress)); + size += cnt * sizeof(WCHAR); + if (!old_size) + tgt = msi_alloc(size); + else + tgt = msi_realloc(newdata,size); + newdata = tgt; + memcpy(&newdata[old_size],progress,(cnt * sizeof(WCHAR))); + } + + progress = mark; + + if (nested) + { + TRACE("Nested key... %s\n",debugstr_w(key)); + deformat_string_internal(package, key, &value, strlenW(key)+1, + record, failcount); + + msi_free(key); + key = value; + } + + TRACE("Current %s .. %s\n",debugstr_wn((LPWSTR)newdata, + size/sizeof(WCHAR)),debugstr_w(key)); + + if (!package) + { + /* only deformat number indexs */ + if (key && is_key_number(key)) + { + value = deformat_index(record,key,&chunk); + if (!chunk && failcount && *failcount >= 0) + (*failcount)++; + } + else + { + if (failcount) + *failcount = -1; + if(key) + { + DWORD keylen = strlenW(key); + chunk = (keylen + 2)*sizeof(WCHAR); + value = msi_alloc(chunk); + value[0] = '['; + memcpy(&value[1],key,keylen*sizeof(WCHAR)); + value[1+keylen] = ']'; + } + } + } + else + { + sz = 0; + if (key) switch (key[0]) + { + case '~': + value = deformat_NULL(&chunk); + break; + case '$': + value = deformat_component(package,&key[1],&chunk); + break; + case '#': + value = deformat_file(package,&key[1], &chunk, FALSE); + break; + case '!': /* should be short path */ + value = deformat_file(package,&key[1], &chunk, TRUE); + break; + case '\\': + value = deformat_escape(&key[1],&chunk); + break; + case '%': + value = deformat_environment(package,&key[1],&chunk); + break; + default: + /* index keys cannot be nested */ + if (is_key_number(key)) + if (!nested) + value = deformat_index(record,key,&chunk); + else + { + static const WCHAR fmt[] = {'[','%','s',']',0}; + value = msi_alloc(10); + sprintfW(value,fmt,key); + chunk = strlenW(value)*sizeof(WCHAR); + } + else + value = deformat_property(package,key,&chunk); + break; + } + } + + msi_free(key); + + if (value!=NULL) + { + LPBYTE nd2; + TRACE("value %s, chunk %li size %li\n",debugstr_w((LPWSTR)value), + chunk, size); + if (size) + nd2= msi_realloc(newdata,(size + chunk)); + else + nd2= msi_alloc(chunk); + newdata = nd2; + memcpy(&newdata[size],value,chunk); + size+=chunk; + msi_free(value); + } + else if (failcount && *failcount >=0 ) + (*failcount)++; + + progress = mark2+1; + } + + TRACE("after everything %s\n",debugstr_wn((LPWSTR)newdata, + size/sizeof(WCHAR))); + + *data = (LPWSTR)newdata; + return size / sizeof(WCHAR); +} + + +UINT MSI_FormatRecordW( MSIPACKAGE* package, MSIRECORD* record, LPWSTR buffer, + DWORD *size ) +{ + LPWSTR deformated; + LPWSTR rec; + DWORD len; + UINT rc = ERROR_INVALID_PARAMETER; + + TRACE("%p %p %p %li\n",package, record ,buffer, *size); + + rec = msi_dup_record_field(record,0); + if (!rec) + rec = build_default_format(record); + + TRACE("(%s)\n",debugstr_w(rec)); + + len = deformat_string_internal(package,rec,&deformated,strlenW(rec), + record, NULL); + + if (buffer) + { + if (*size>len) + { + memcpy(buffer,deformated,len*sizeof(WCHAR)); + rc = ERROR_SUCCESS; + buffer[len] = 0; + } + else + { + if (*size > 0) + { + memcpy(buffer,deformated,(*size)*sizeof(WCHAR)); + buffer[(*size)-1] = 0; + } + rc = ERROR_MORE_DATA; + } + } + else + rc = ERROR_SUCCESS; + + *size = len; + + msi_free(rec); + msi_free(deformated); + return rc; +} + +UINT MSI_FormatRecordA( MSIPACKAGE* package, MSIRECORD* record, LPSTR buffer, + DWORD *size ) +{ + LPWSTR deformated; + LPWSTR rec; + DWORD len,lenA; + UINT rc = ERROR_INVALID_PARAMETER; + + TRACE("%p %p %p %li\n",package, record ,buffer, *size); + + rec = msi_dup_record_field(record,0); + if (!rec) + rec = build_default_format(record); + + TRACE("(%s)\n",debugstr_w(rec)); + + len = deformat_string_internal(package,rec,&deformated,strlenW(rec), + record, NULL); + /* If len is zero then WideCharToMultiByte will return 0 indicating + * failure, but that will do just as well since we are ignoring + * possible errors. + */ + lenA = WideCharToMultiByte(CP_ACP,0,deformated,len,NULL,0,NULL,NULL); + + if (buffer) + { + /* Ditto above */ + WideCharToMultiByte(CP_ACP,0,deformated,len,buffer,*size,NULL, NULL); + if (*size>lenA) + { + rc = ERROR_SUCCESS; + buffer[lenA] = 0; + } + else + { + rc = ERROR_MORE_DATA; + if (*size) + buffer[(*size)-1] = 0; + } + } + else + rc = ERROR_SUCCESS; + + *size = lenA; + + msi_free(rec); + msi_free(deformated); + return rc; +} + + +UINT WINAPI MsiFormatRecordW( MSIHANDLE hInstall, MSIHANDLE hRecord, + LPWSTR szResult, DWORD *sz ) +{ + UINT r = ERROR_INVALID_HANDLE; + MSIPACKAGE *package; + MSIRECORD *record; + + TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz); + + record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD ); + + if (!record) + return ERROR_INVALID_HANDLE; + if (!sz) + { + msiobj_release( &record->hdr ); + if (szResult) + return ERROR_INVALID_PARAMETER; + else + return ERROR_SUCCESS; + } + + package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE ); + + r = MSI_FormatRecordW( package, record, szResult, sz ); + msiobj_release( &record->hdr ); + if (package) + msiobj_release( &package->hdr ); + return r; +} + +UINT WINAPI MsiFormatRecordA( MSIHANDLE hInstall, MSIHANDLE hRecord, + LPSTR szResult, DWORD *sz ) +{ + UINT r = ERROR_INVALID_HANDLE; + MSIPACKAGE *package = NULL; + MSIRECORD *record = NULL; + + TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz); + + record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD ); + + if (!record) + return ERROR_INVALID_HANDLE; + if (!sz) + { + msiobj_release( &record->hdr ); + if (szResult) + return ERROR_INVALID_PARAMETER; + else + return ERROR_SUCCESS; + } + + package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE ); + + r = MSI_FormatRecordA( package, record, szResult, sz ); + msiobj_release( &record->hdr ); + if (package) + msiobj_release( &package->hdr ); + return r; +} diff --git a/reactos/dll/msi/handle.c b/reactos/dll/msi/handle.c new file mode 100644 index 00000000000..52c24395303 --- /dev/null +++ b/reactos/dll/msi/handle.c @@ -0,0 +1,240 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002-2004 Mike McCormack 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "shlwapi.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "msipriv.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +static CRITICAL_SECTION MSI_handle_cs; +static CRITICAL_SECTION_DEBUG MSI_handle_cs_debug = +{ + 0, 0, &MSI_handle_cs, + { &MSI_handle_cs_debug.ProcessLocksList, + &MSI_handle_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": MSI_handle_cs") } +}; +static CRITICAL_SECTION MSI_handle_cs = { &MSI_handle_cs_debug, -1, 0, 0, 0, 0 }; + +static CRITICAL_SECTION MSI_object_cs; +static CRITICAL_SECTION_DEBUG MSI_object_cs_debug = +{ + 0, 0, &MSI_object_cs, + { &MSI_object_cs_debug.ProcessLocksList, + &MSI_object_cs_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": MSI_object_cs") } +}; +static CRITICAL_SECTION MSI_object_cs = { &MSI_object_cs_debug, -1, 0, 0, 0, 0 }; + +typedef struct msi_handle_info_t +{ + MSIOBJECTHDR *obj; + DWORD dwThreadId; +} msi_handle_info; + +static msi_handle_info msihandletable[MSIMAXHANDLES]; + +MSIHANDLE alloc_msihandle( MSIOBJECTHDR *obj ) +{ + MSIHANDLE ret = 0; + UINT i; + + EnterCriticalSection( &MSI_handle_cs ); + + /* find a slot */ + for(i=0; i=MSIMAXHANDLES) || msihandletable[i].obj ) + goto out; + + msiobj_addref( obj ); + msihandletable[i].obj = obj; + msihandletable[i].dwThreadId = GetCurrentThreadId(); + ret = (MSIHANDLE) (i+1); +out: + TRACE("%p -> %ld\n", obj, ret ); + + LeaveCriticalSection( &MSI_handle_cs ); + return ret; +} + +void *msihandle2msiinfo(MSIHANDLE handle, UINT type) +{ + MSIOBJECTHDR *ret = NULL; + + EnterCriticalSection( &MSI_handle_cs ); + handle--; + if( handle<0 ) + goto out; + if( handle>=MSIMAXHANDLES ) + goto out; + if( !msihandletable[handle].obj ) + goto out; + if( msihandletable[handle].obj->magic != MSIHANDLE_MAGIC ) + goto out; + if( type && (msihandletable[handle].obj->type != type) ) + goto out; + ret = msihandletable[handle].obj; + msiobj_addref( ret ); + +out: + LeaveCriticalSection( &MSI_handle_cs ); + + return (void*) ret; +} + +void *alloc_msiobject(UINT type, UINT size, msihandledestructor destroy ) +{ + MSIOBJECTHDR *info; + + info = msi_alloc_zero( size ); + if( info ) + { + info->magic = MSIHANDLE_MAGIC; + info->type = type; + info->refcount = 1; + info->destructor = destroy; + } + + return info; +} + +void msiobj_addref( MSIOBJECTHDR *info ) +{ + TRACE("%p\n", info); + + if( !info ) + return; + + if( info->magic != MSIHANDLE_MAGIC ) + { + ERR("Invalid handle!\n"); + return; + } + + InterlockedIncrement(&info->refcount); +} + +void msiobj_lock( MSIOBJECTHDR *info ) +{ + EnterCriticalSection( &MSI_object_cs ); +} + +void msiobj_unlock( MSIOBJECTHDR *info ) +{ + LeaveCriticalSection( &MSI_object_cs ); +} + +int msiobj_release( MSIOBJECTHDR *info ) +{ + int ret; + + TRACE("%p\n",info); + + if( !info ) + return -1; + + if( info->magic != MSIHANDLE_MAGIC ) + { + ERR("Invalid handle!\n"); + return -1; + } + + ret = InterlockedDecrement( &info->refcount ); + if( ret==0 ) + { + if( info->destructor ) + info->destructor( info ); + msi_free( info ); + TRACE("object %p destroyed\n", info); + } + + return ret; +} + +/*********************************************************** + * MsiCloseHandle [MSI.@] + */ +UINT WINAPI MsiCloseHandle(MSIHANDLE handle) +{ + MSIOBJECTHDR *info; + UINT ret = ERROR_INVALID_HANDLE; + + TRACE("%lx\n",handle); + + EnterCriticalSection( &MSI_handle_cs ); + + info = msihandle2msiinfo(handle, 0); + if( !info ) + goto out; + + if( info->magic != MSIHANDLE_MAGIC ) + { + ERR("Invalid handle!\n"); + goto out; + } + + msiobj_release( info ); + msihandletable[handle-1].obj = NULL; + ret = ERROR_SUCCESS; + + TRACE("handle %lx Destroyed\n", handle); +out: + LeaveCriticalSection( &MSI_handle_cs ); + if( info ) + msiobj_release( info ); + + return ret; +} + +/*********************************************************** + * MsiCloseAllHandles [MSI.@] + * + * Closes all handles owned by the current thread + * + * RETURNS: + * The number of handles closed + */ +UINT WINAPI MsiCloseAllHandles(void) +{ + UINT i, n=0; + + TRACE("\n"); + + for(i=0; i + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msipriv.h" +#include "winuser.h" +#include "wine/unicode.h" +#include "action.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +static const WCHAR cszTargetDir[] = {'T','A','R','G','E','T','D','I','R',0}; +static const WCHAR cszDatabase[]={'D','A','T','A','B','A','S','E',0}; + +const WCHAR cszSourceDir[] = {'S','o','u','r','c','e','D','i','r',0}; +const WCHAR cszRootDrive[] = {'R','O','O','T','D','R','I','V','E',0}; +const WCHAR cszbs[]={'\\',0}; + +DWORD build_version_dword(LPCWSTR version_string) +{ + SHORT major,minor; + WORD build; + DWORD rc = 0x00000000; + LPCWSTR ptr1; + + ptr1 = version_string; + + if (!ptr1) + return rc; + else + major = atoiW(ptr1); + + + if(ptr1) + ptr1 = strchrW(ptr1,'.'); + if (ptr1) + { + ptr1++; + minor = atoiW(ptr1); + } + else + minor = 0; + + if (ptr1) + ptr1 = strchrW(ptr1,'.'); + + if (ptr1) + { + ptr1++; + build = atoiW(ptr1); + } + else + build = 0; + + rc = MAKELONG(build,MAKEWORD(minor,major)); + TRACE("%s -> 0x%lx\n",debugstr_w(version_string),rc); + return rc; +} + +LPWSTR build_icon_path(MSIPACKAGE *package, LPCWSTR icon_name ) +{ + LPWSTR SystemFolder, dest, FilePath; + + static const WCHAR szInstaller[] = + {'M','i','c','r','o','s','o','f','t','\\', + 'I','n','s','t','a','l','l','e','r','\\',0}; + static const WCHAR szFolder[] = + {'A','p','p','D','a','t','a','F','o','l','d','e','r',0}; + + SystemFolder = msi_dup_property( package, szFolder ); + + dest = build_directory_name(3, SystemFolder, szInstaller, package->ProductCode); + + create_full_pathW(dest); + + FilePath = build_directory_name(2, dest, icon_name); + + msi_free(SystemFolder); + msi_free(dest); + return FilePath; +} + +LPWSTR msi_dup_record_field( MSIRECORD *row, INT index ) +{ + return strdupW( MSI_RecordGetString(row,index) ); +} + +LPWSTR msi_dup_property(MSIPACKAGE *package, LPCWSTR prop) +{ + DWORD sz = 0; + LPWSTR str; + UINT r; + + r = MSI_GetPropertyW(package, prop, NULL, &sz); + if (r != ERROR_SUCCESS && r != ERROR_MORE_DATA) + return NULL; + + sz++; + str = msi_alloc(sz*sizeof(WCHAR)); + r = MSI_GetPropertyW(package, prop, str, &sz); + if (r != ERROR_SUCCESS) + { + msi_free(str); + str = NULL; + } + return str; +} + +MSICOMPONENT* get_loaded_component( MSIPACKAGE* package, LPCWSTR Component ) +{ + MSICOMPONENT *comp; + + LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry ) + { + if (lstrcmpW(Component,comp->Component)==0) + return comp; + } + return NULL; +} + +MSIFEATURE* get_loaded_feature(MSIPACKAGE* package, LPCWSTR Feature ) +{ + MSIFEATURE *feature; + + LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry ) + { + if (lstrcmpW( Feature, feature->Feature )==0) + return feature; + } + return NULL; +} + +MSIFILE* get_loaded_file( MSIPACKAGE* package, LPCWSTR key ) +{ + MSIFILE *file; + + LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry ) + { + if (lstrcmpW( key, file->File )==0) + return file; + } + return NULL; +} + +int track_tempfile( MSIPACKAGE *package, LPCWSTR name, LPCWSTR path ) +{ + MSITEMPFILE *temp; + + LIST_FOR_EACH_ENTRY( temp, &package->tempfiles, MSITEMPFILE, entry ) + { + if (lstrcmpW( name, temp->File )==0) + { + TRACE("tempfile %s already exists with path %s\n", + debugstr_w(temp->File), debugstr_w(temp->Path)); + return -1; + } + } + + temp = msi_alloc_zero( sizeof (MSITEMPFILE) ); + if (!temp) + return -1; + + list_add_head( &package->tempfiles, &temp->entry ); + + temp->File = strdupW( name ); + temp->Path = strdupW( path ); + + TRACE("adding tempfile %s with path %s\n", + debugstr_w(temp->File), debugstr_w(temp->Path)); + + return 0; +} + +MSIFOLDER *get_loaded_folder( MSIPACKAGE *package, LPCWSTR dir ) +{ + MSIFOLDER *folder; + + LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry ) + { + if (lstrcmpW( dir, folder->Directory )==0) + return folder; + } + return NULL; +} + +static LPWSTR get_source_root( MSIPACKAGE *package ) +{ + LPWSTR path, p; + + path = msi_dup_property( package, cszSourceDir ); + if (path) + return path; + + path = msi_dup_property( package, cszDatabase ); + if (path) + { + p = strrchrW(path,'\\'); + if (p) + *(p+1) = 0; + } + return path; +} + +LPWSTR resolve_folder(MSIPACKAGE *package, LPCWSTR name, BOOL source, + BOOL set_prop, MSIFOLDER **folder) +{ + MSIFOLDER *f; + LPWSTR p, path = NULL; + + TRACE("Working to resolve %s\n",debugstr_w(name)); + + if (!name) + return NULL; + + /* special resolving for Target and Source root dir */ + if (strcmpW(name,cszTargetDir)==0 || strcmpW(name,cszSourceDir)==0) + { + if (!source) + { + LPWSTR check_path; + check_path = msi_dup_property( package, cszTargetDir ); + if (!check_path) + { + check_path = msi_dup_property( package, cszRootDrive ); + if (set_prop) + MSI_SetPropertyW(package,cszTargetDir,check_path); + } + + /* correct misbuilt target dir */ + path = build_directory_name(2, check_path, NULL); + if (strcmpiW(path,check_path)!=0) + MSI_SetPropertyW(package,cszTargetDir,path); + msi_free(check_path); + } + else + path = get_source_root( package ); + if (folder) + *folder = get_loaded_folder( package, name ); + return path; + } + + f = get_loaded_folder( package, name ); + if (!f) + return NULL; + + if (folder) + *folder = f; + + if (!source && f->ResolvedTarget) + { + path = strdupW( f->ResolvedTarget ); + TRACE(" already resolved to %s\n",debugstr_w(path)); + return path; + } + else if (source && f->ResolvedSource) + { + path = strdupW( f->ResolvedSource ); + TRACE(" (source)already resolved to %s\n",debugstr_w(path)); + return path; + } + else if (!source && f->Property) + { + path = build_directory_name( 2, f->Property, NULL ); + + TRACE(" internally set to %s\n",debugstr_w(path)); + if (set_prop) + MSI_SetPropertyW( package, name, path ); + return path; + } + + if (f->Parent) + { + LPWSTR parent = f->Parent->Directory; + + TRACE(" ! Parent is %s\n", debugstr_w(parent)); + + p = resolve_folder(package, parent, source, set_prop, NULL); + if (!source) + { + TRACE(" TargetDefault = %s\n", debugstr_w(f->TargetDefault)); + + path = build_directory_name( 3, p, f->TargetDefault, NULL ); + f->ResolvedTarget = strdupW( path ); + TRACE("target -> %s\n", debugstr_w(path)); + if (set_prop) + MSI_SetPropertyW(package,name,path); + } + else + { + if (f->SourceDefault && f->SourceDefault[0]!='.') + path = build_directory_name( 3, p, f->SourceDefault, NULL ); + else + path = strdupW(p); + TRACE("source -> %s\n", debugstr_w(path)); + + /* if the directory doesn't exist, use the root */ + if (INVALID_FILE_ATTRIBUTES == GetFileAttributesW( path )) + { + msi_free( path ); + path = get_source_root( package ); + TRACE("defaulting to %s\n", debugstr_w(path)); + } + else + f->ResolvedSource = strdupW( path ); + } + msi_free(p); + } + return path; +} + +/* wrapper to resist a need for a full rewrite right now */ +DWORD deformat_string(MSIPACKAGE *package, LPCWSTR ptr, WCHAR** data ) +{ + if (ptr) + { + MSIRECORD *rec = MSI_CreateRecord(1); + DWORD size = 0; + + MSI_RecordSetStringW(rec,0,ptr); + MSI_FormatRecordW(package,rec,NULL,&size); + if (size >= 0) + { + size++; + *data = msi_alloc(size*sizeof(WCHAR)); + if (size > 1) + MSI_FormatRecordW(package,rec,*data,&size); + else + *data[0] = 0; + msiobj_release( &rec->hdr ); + return sizeof(WCHAR)*size; + } + msiobj_release( &rec->hdr ); + } + + *data = NULL; + return 0; +} + +UINT schedule_action(MSIPACKAGE *package, UINT script, LPCWSTR action) +{ + UINT count; + LPWSTR *newbuf = NULL; + if (script >= TOTAL_SCRIPTS) + { + FIXME("Unknown script requested %i\n",script); + return ERROR_FUNCTION_FAILED; + } + TRACE("Scheduling Action %s in script %i\n",debugstr_w(action), script); + + count = package->script->ActionCount[script]; + package->script->ActionCount[script]++; + if (count != 0) + newbuf = msi_realloc( package->script->Actions[script], + package->script->ActionCount[script]* sizeof(LPWSTR)); + else + newbuf = msi_alloc( sizeof(LPWSTR)); + + newbuf[count] = strdupW(action); + package->script->Actions[script] = newbuf; + + return ERROR_SUCCESS; +} + +static void remove_tracked_tempfiles(MSIPACKAGE* package) +{ + struct list *item, *cursor; + + LIST_FOR_EACH_SAFE( item, cursor, &package->tempfiles ) + { + MSITEMPFILE *temp = LIST_ENTRY( item, MSITEMPFILE, entry ); + + list_remove( &temp->entry ); + TRACE("deleting temp file %s\n", debugstr_w( temp->Path )); + DeleteFileW( temp->Path ); + msi_free( temp->File ); + msi_free( temp->Path ); + msi_free( temp ); + } +} + +static void free_feature( MSIFEATURE *feature ) +{ + struct list *item, *cursor; + + LIST_FOR_EACH_SAFE( item, cursor, &feature->Components ) + { + ComponentList *cl = LIST_ENTRY( item, ComponentList, entry ); + list_remove( &cl->entry ); + msi_free( cl ); + } + msi_free( feature->Feature ); + msi_free( feature->Feature_Parent ); + msi_free( feature->Directory ); + msi_free( feature->Description ); + msi_free( feature->Title ); + msi_free( feature ); +} + +void free_extension( MSIEXTENSION *ext ) +{ + struct list *item, *cursor; + + LIST_FOR_EACH_SAFE( item, cursor, &ext->verbs ) + { + MSIVERB *verb = LIST_ENTRY( item, MSIVERB, entry ); + + list_remove( &verb->entry ); + msi_free( verb->Verb ); + msi_free( verb->Command ); + msi_free( verb->Argument ); + msi_free( verb ); + } + + msi_free( ext->Extension ); + msi_free( ext->ProgIDText ); + msi_free( ext ); +} + +/* Called when the package is being closed */ +void ACTION_free_package_structures( MSIPACKAGE* package) +{ + INT i; + struct list *item, *cursor; + + TRACE("Freeing package action data\n"); + + remove_tracked_tempfiles(package); + + LIST_FOR_EACH_SAFE( item, cursor, &package->features ) + { + MSIFEATURE *feature = LIST_ENTRY( item, MSIFEATURE, entry ); + list_remove( &feature->entry ); + free_feature( feature ); + } + + LIST_FOR_EACH_SAFE( item, cursor, &package->folders ) + { + MSIFOLDER *folder = LIST_ENTRY( item, MSIFOLDER, entry ); + + list_remove( &folder->entry ); + msi_free( folder->Directory ); + msi_free( folder->TargetDefault ); + msi_free( folder->SourceDefault ); + msi_free( folder->ResolvedTarget ); + msi_free( folder->ResolvedSource ); + msi_free( folder->Property ); + msi_free( folder ); + } + + LIST_FOR_EACH_SAFE( item, cursor, &package->components ) + { + MSICOMPONENT *comp = LIST_ENTRY( item, MSICOMPONENT, entry ); + + list_remove( &comp->entry ); + msi_free( comp->Component ); + msi_free( comp->ComponentId ); + msi_free( comp->Directory ); + msi_free( comp->Condition ); + msi_free( comp->KeyPath ); + msi_free( comp->FullKeypath ); + msi_free( comp ); + } + + LIST_FOR_EACH_SAFE( item, cursor, &package->files ) + { + MSIFILE *file = LIST_ENTRY( item, MSIFILE, entry ); + + list_remove( &file->entry ); + msi_free( file->File ); + msi_free( file->FileName ); + msi_free( file->ShortName ); + msi_free( file->Version ); + msi_free( file->Language ); + msi_free( file->SourcePath ); + msi_free( file->TargetPath ); + msi_free( file ); + } + + /* clean up extension, progid, class and verb structures */ + LIST_FOR_EACH_SAFE( item, cursor, &package->classes ) + { + MSICLASS *cls = LIST_ENTRY( item, MSICLASS, entry ); + + list_remove( &cls->entry ); + msi_free( cls->clsid ); + msi_free( cls->Context ); + msi_free( cls->Description ); + msi_free( cls->FileTypeMask ); + msi_free( cls->IconPath ); + msi_free( cls->DefInprocHandler ); + msi_free( cls->DefInprocHandler32 ); + msi_free( cls->Argument ); + msi_free( cls->ProgIDText ); + msi_free( cls ); + } + + LIST_FOR_EACH_SAFE( item, cursor, &package->extensions ) + { + MSIEXTENSION *ext = LIST_ENTRY( item, MSIEXTENSION, entry ); + + list_remove( &ext->entry ); + free_extension( ext ); + } + + LIST_FOR_EACH_SAFE( item, cursor, &package->progids ) + { + MSIPROGID *progid = LIST_ENTRY( item, MSIPROGID, entry ); + + list_remove( &progid->entry ); + msi_free( progid->ProgID ); + msi_free( progid->Description ); + msi_free( progid->IconPath ); + msi_free( progid ); + } + + LIST_FOR_EACH_SAFE( item, cursor, &package->mimes ) + { + MSIMIME *mt = LIST_ENTRY( item, MSIMIME, entry ); + + list_remove( &mt->entry ); + msi_free( mt->clsid ); + msi_free( mt->ContentType ); + msi_free( mt ); + } + + LIST_FOR_EACH_SAFE( item, cursor, &package->appids ) + { + MSIAPPID *appid = LIST_ENTRY( item, MSIAPPID, entry ); + + list_remove( &appid->entry ); + msi_free( appid->AppID ); + msi_free( appid->RemoteServerName ); + msi_free( appid->LocalServer ); + msi_free( appid->ServiceParameters ); + msi_free( appid->DllSurrogate ); + msi_free( appid ); + } + + if (package->script) + { + for (i = 0; i < TOTAL_SCRIPTS; i++) + { + int j; + for (j = 0; j < package->script->ActionCount[i]; j++) + msi_free(package->script->Actions[i][j]); + + msi_free(package->script->Actions[i]); + } + + for (i = 0; i < package->script->UniqueActionsCount; i++) + msi_free(package->script->UniqueActions[i]); + + msi_free(package->script->UniqueActions); + msi_free(package->script); + } + + msi_free(package->PackagePath); + msi_free(package->ProductCode); + msi_free(package->ActionFormat); + msi_free(package->LastAction); + + /* cleanup control event subscriptions */ + ControlEvent_CleanupSubscriptions(package); +} + +/* + * build_directory_name() + * + * This function is to save messing round with directory names + * It handles adding backslashes between path segments, + * and can add \ at the end of the directory name if told to. + * + * It takes a variable number of arguments. + * It always allocates a new string for the result, so make sure + * to free the return value when finished with it. + * + * The first arg is the number of path segments that follow. + * The arguments following count are a list of path segments. + * A path segment may be NULL. + * + * Path segments will be added with a \ separating them. + * A \ will not be added after the last segment, however if the + * last segment is NULL, then the last character will be a \ + * + */ +LPWSTR build_directory_name(DWORD count, ...) +{ + DWORD sz = 1, i; + LPWSTR dir; + va_list va; + + va_start(va,count); + for(i=0; ihdr); + + msi_dialog_check_messages(NULL); +} + +void ui_actiondata(MSIPACKAGE *package, LPCWSTR action, MSIRECORD * record) +{ + static const WCHAR Query_t[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ', + '`','A','c','t','i','o', 'n','T','e','x','t','`',' ', + 'W','H','E','R','E',' ', '`','A','c','t','i','o','n','`',' ','=', + ' ','\'','%','s','\'',0}; + WCHAR message[1024]; + MSIRECORD * row = 0; + DWORD size; + static const WCHAR szActionData[] = + {'A','c','t','i','o','n','D','a','t','a',0}; + + if (!package->LastAction || strcmpW(package->LastAction,action)) + { + row = MSI_QueryGetRecord(package->db, Query_t, action); + if (!row) + return; + + if (MSI_RecordIsNull(row,3)) + { + msiobj_release(&row->hdr); + return; + } + + /* update the cached actionformat */ + msi_free(package->ActionFormat); + package->ActionFormat = msi_dup_record_field(row,3); + + msi_free(package->LastAction); + package->LastAction = strdupW(action); + + msiobj_release(&row->hdr); + } + + MSI_RecordSetStringW(record,0,package->ActionFormat); + size = 1024; + MSI_FormatRecordW(package,record,message,&size); + + row = MSI_CreateRecord(1); + MSI_RecordSetStringW(row,1,message); + + MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row); + + ControlEvent_FireSubscribedEvent(package,szActionData, row); + + msiobj_release(&row->hdr); +} + +BOOL ACTION_VerifyComponentForAction( MSICOMPONENT* comp, INSTALLSTATE check ) +{ + if (!comp) + return FALSE; + + if (comp->Installed == check) + return FALSE; + + if (comp->ActionRequest == check) + return TRUE; + else + return FALSE; +} + +BOOL ACTION_VerifyFeatureForAction( MSIFEATURE* feature, INSTALLSTATE check ) +{ + if (!feature) + return FALSE; + + if (feature->Installed == check) + return FALSE; + + if (feature->ActionRequest == check) + return TRUE; + else + return FALSE; +} + +void reduce_to_longfilename(WCHAR* filename) +{ + LPWSTR p = strchrW(filename,'|'); + if (p) + memmove(filename, p+1, (strlenW(p+1)+1)*sizeof(WCHAR)); +} + +void reduce_to_shortfilename(WCHAR* filename) +{ + LPWSTR p = strchrW(filename,'|'); + if (p) + *p = 0; +} + +LPWSTR create_component_advertise_string(MSIPACKAGE* package, + MSICOMPONENT* component, LPCWSTR feature) +{ + GUID clsid; + WCHAR productid_85[21]; + WCHAR component_85[21]; + /* + * I have a fair bit of confusion as to when a < is used and when a > is + * used. I do not think i have it right... + * + * Ok it appears that the > is used if there is a guid for the compoenent + * and the < is used if not. + */ + static WCHAR fmt1[] = {'%','s','%','s','<',0,0}; + static WCHAR fmt2[] = {'%','s','%','s','>','%','s',0,0}; + LPWSTR output = NULL; + DWORD sz = 0; + + memset(productid_85,0,sizeof(productid_85)); + memset(component_85,0,sizeof(component_85)); + + CLSIDFromString(package->ProductCode, &clsid); + + encode_base85_guid(&clsid,productid_85); + + CLSIDFromString(component->ComponentId, &clsid); + encode_base85_guid(&clsid,component_85); + + TRACE("Doing something with this... %s %s %s\n", + debugstr_w(productid_85), debugstr_w(feature), + debugstr_w(component_85)); + + sz = lstrlenW(productid_85) + lstrlenW(feature); + if (component) + sz += lstrlenW(component_85); + + sz+=3; + sz *= sizeof(WCHAR); + + output = msi_alloc(sz); + memset(output,0,sz); + + if (component) + sprintfW(output,fmt2,productid_85,feature,component_85); + else + sprintfW(output,fmt1,productid_85,feature); + + return output; +} + +/* update compoennt state based on a feature change */ +void ACTION_UpdateComponentStates(MSIPACKAGE *package, LPCWSTR szFeature) +{ + INSTALLSTATE newstate; + MSIFEATURE *feature; + ComponentList *cl; + + feature = get_loaded_feature(package,szFeature); + if (!feature) + return; + + newstate = feature->ActionRequest; + + LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry ) + { + MSICOMPONENT* component = cl->component; + + TRACE("MODIFYING(%i): Component %s (Installed %i, Action %i, Request %i)\n", + newstate, debugstr_w(component->Component), component->Installed, + component->Action, component->ActionRequest); + + if (!component->Enabled) + continue; + + if (newstate == INSTALLSTATE_LOCAL) + { + component->ActionRequest = INSTALLSTATE_LOCAL; + component->Action = INSTALLSTATE_LOCAL; + } + else + { + ComponentList *clist; + MSIFEATURE *f; + + component->ActionRequest = newstate; + component->Action = newstate; + + /*if any other feature wants is local we need to set it local*/ + LIST_FOR_EACH_ENTRY( f, &package->features, MSIFEATURE, entry ) + { + if ( component->ActionRequest != INSTALLSTATE_LOCAL ) + break; + + LIST_FOR_EACH_ENTRY( clist, &f->Components, ComponentList, entry ) + { + if ( clist->component == component ) + { + if (f->ActionRequest == INSTALLSTATE_LOCAL) + { + TRACE("Saved by %s\n", debugstr_w(f->Feature)); + component->ActionRequest = INSTALLSTATE_LOCAL; + component->Action = INSTALLSTATE_LOCAL; + } + break; + } + } + } + } + TRACE("Result (%i): Component %s (Installed %i, Action %i, Request %i)\n", + newstate, debugstr_w(component->Component), component->Installed, + component->Action, component->ActionRequest); + } +} + +UINT register_unique_action(MSIPACKAGE *package, LPCWSTR action) +{ + UINT count; + LPWSTR *newbuf = NULL; + + if (!package->script) + return FALSE; + + TRACE("Registering Action %s as having fun\n",debugstr_w(action)); + + count = package->script->UniqueActionsCount; + package->script->UniqueActionsCount++; + if (count != 0) + newbuf = msi_realloc( package->script->UniqueActions, + package->script->UniqueActionsCount* sizeof(LPWSTR)); + else + newbuf = msi_alloc( sizeof(LPWSTR)); + + newbuf[count] = strdupW(action); + package->script->UniqueActions = newbuf; + + return ERROR_SUCCESS; +} + +BOOL check_unique_action(MSIPACKAGE *package, LPCWSTR action) +{ + INT i; + + if (!package->script) + return FALSE; + + for (i = 0; i < package->script->UniqueActionsCount; i++) + if (!strcmpW(package->script->UniqueActions[i],action)) + return TRUE; + + return FALSE; +} + +WCHAR* generate_error_string(MSIPACKAGE *package, UINT error, DWORD count, ... ) +{ + static const WCHAR query[] = {'S','E','L','E','C','T',' ','`','M','e','s','s','a','g','e','`',' ','F','R','O','M',' ','`','E','r','r','o','r','`',' ','W','H','E','R','E',' ','`','E','r','r','o','r','`',' ','=',' ','%','i',0}; + + MSIRECORD *rec; + MSIRECORD *row; + DWORD size = 0; + DWORD i; + va_list va; + LPCWSTR str; + LPWSTR data; + + row = MSI_QueryGetRecord(package->db, query, error); + if (!row) + return 0; + + rec = MSI_CreateRecord(count+2); + + str = MSI_RecordGetString(row,1); + MSI_RecordSetStringW(rec,0,str); + msiobj_release( &row->hdr ); + MSI_RecordSetInteger(rec,1,error); + + va_start(va,count); + for (i = 0; i < count; i++) + { + str = va_arg(va,LPCWSTR); + MSI_RecordSetStringW(rec,(i+2),str); + } + va_end(va); + + MSI_FormatRecordW(package,rec,NULL,&size); + if (size >= 0) + { + size++; + data = msi_alloc(size*sizeof(WCHAR)); + if (size > 1) + MSI_FormatRecordW(package,rec,data,&size); + else + data[0] = 0; + msiobj_release( &rec->hdr ); + return data; + } + + msiobj_release( &rec->hdr ); + data = NULL; + return data; +} diff --git a/reactos/dll/msi/insert.c b/reactos/dll/msi/insert.c new file mode 100644 index 00000000000..536daeac232 --- /dev/null +++ b/reactos/dll/msi/insert.c @@ -0,0 +1,267 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2004 Mike McCormack 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" +#include "winnls.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msidb); + + +/* below is the query interface to a table */ + +typedef struct tagMSIINSERTVIEW +{ + MSIVIEW view; + MSIDATABASE *db; + BOOL bIsTemp; + MSIVIEW *sv; + column_info *vals; +} MSIINSERTVIEW; + +static UINT INSERT_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ) +{ + MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view; + + TRACE("%p %d %d %p\n", iv, row, col, val ); + + return ERROR_FUNCTION_FAILED; +} + +/* + * INSERT_merge_record + * + * Merge a value_list and a record to create a second record. + * Replace wildcard entries in the valuelist with values from the record + */ +static MSIRECORD *INSERT_merge_record( UINT fields, column_info *vl, MSIRECORD *rec ) +{ + MSIRECORD *merged; + DWORD wildcard_count = 1, i; + const WCHAR *str; + + merged = MSI_CreateRecord( fields ); + for( i=1; i <= fields; i++ ) + { + if( !vl ) + { + TRACE("Not enough elements in the list to insert\n"); + goto err; + } + switch( vl->val->type ) + { + case EXPR_SVAL: + TRACE("field %ld -> %s\n", i, debugstr_w(vl->val->u.sval)); + MSI_RecordSetStringW( merged, i, vl->val->u.sval ); + break; + case EXPR_IVAL: + MSI_RecordSetInteger( merged, i, vl->val->u.ival ); + break; + case EXPR_WILDCARD: + if( !rec ) + goto err; + if( MSI_RecordIsNull( rec, wildcard_count ) ) + goto err; + str = MSI_RecordGetString( rec, wildcard_count ); + MSI_RecordSetStringW( merged, i, str ); + wildcard_count++; + break; + default: + ERR("Unknown expression type %d\n", vl->val->type); + } + vl = vl->next; + } + + return merged; +err: + msiobj_release( &merged->hdr ); + return NULL; +} + +static UINT INSERT_execute( struct tagMSIVIEW *view, MSIRECORD *record ) +{ + MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view; + UINT r, col_count = 0; + MSIVIEW *sv; + MSIRECORD *values = NULL; + + TRACE("%p %p\n", iv, record ); + + sv = iv->sv; + if( !sv ) + return ERROR_FUNCTION_FAILED; + + r = sv->ops->execute( sv, 0 ); + TRACE("tv execute returned %x\n", r); + if( r ) + return r; + + r = sv->ops->get_dimensions( sv, NULL, &col_count ); + if( r ) + goto err; + + /* + * Merge the wildcard values into the list of values provided + * in the query, and create a record containing both. + */ + values = INSERT_merge_record( col_count, iv->vals, record ); + if( !values ) + goto err; + + r = sv->ops->insert_row( sv, values ); + +err: + if( values ) + msiobj_release( &values->hdr ); + + return r; +} + + +static UINT INSERT_close( struct tagMSIVIEW *view ) +{ + MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view; + MSIVIEW *sv; + + TRACE("%p\n", iv); + + sv = iv->sv; + if( !sv ) + return ERROR_FUNCTION_FAILED; + + return sv->ops->close( sv ); +} + +static UINT INSERT_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols ) +{ + MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view; + MSIVIEW *sv; + + TRACE("%p %p %p\n", iv, rows, cols ); + + sv = iv->sv; + if( !sv ) + return ERROR_FUNCTION_FAILED; + + return sv->ops->get_dimensions( sv, rows, cols ); +} + +static UINT INSERT_get_column_info( struct tagMSIVIEW *view, + UINT n, LPWSTR *name, UINT *type ) +{ + MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view; + MSIVIEW *sv; + + TRACE("%p %d %p %p\n", iv, n, name, type ); + + sv = iv->sv; + if( !sv ) + return ERROR_FUNCTION_FAILED; + + return sv->ops->get_column_info( sv, n, name, type ); +} + +static UINT INSERT_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIRECORD *rec) +{ + MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view; + + TRACE("%p %d %p\n", iv, eModifyMode, rec ); + + return ERROR_FUNCTION_FAILED; +} + +static UINT INSERT_delete( struct tagMSIVIEW *view ) +{ + MSIINSERTVIEW *iv = (MSIINSERTVIEW*)view; + MSIVIEW *sv; + + TRACE("%p\n", iv ); + + sv = iv->sv; + if( sv ) + sv->ops->delete( sv ); + msiobj_release( &iv->db->hdr ); + msi_free( iv ); + + return ERROR_SUCCESS; +} + + +MSIVIEWOPS insert_ops = +{ + INSERT_fetch_int, + NULL, + NULL, + NULL, + INSERT_execute, + INSERT_close, + INSERT_get_dimensions, + INSERT_get_column_info, + INSERT_modify, + INSERT_delete +}; + +UINT INSERT_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR table, + column_info *columns, column_info *values, BOOL temp ) +{ + MSIINSERTVIEW *iv = NULL; + UINT r; + MSIVIEW *tv = NULL, *sv = NULL; + + TRACE("%p\n", iv ); + + r = TABLE_CreateView( db, table, &tv ); + if( r != ERROR_SUCCESS ) + return r; + + r = SELECT_CreateView( db, &sv, tv, columns ); + if( r != ERROR_SUCCESS ) + { + if( tv ) + tv->ops->delete( tv ); + return r; + } + + iv = msi_alloc_zero( sizeof *iv ); + if( !iv ) + return ERROR_FUNCTION_FAILED; + + /* fill the structure */ + iv->view.ops = &insert_ops; + msiobj_addref( &db->hdr ); + iv->db = db; + iv->vals = values; + iv->bIsTemp = temp; + iv->sv = sv; + *view = (MSIVIEW*) iv; + + return ERROR_SUCCESS; +} diff --git a/reactos/dll/msi/install.c b/reactos/dll/msi/install.c new file mode 100644 index 00000000000..08b5f700edc --- /dev/null +++ b/reactos/dll/msi/install.c @@ -0,0 +1,764 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2005 Aric Stewart 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* Msi top level apis directly related to installs */ + +#include + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msi.h" +#include "msidefs.h" +#include "msipriv.h" +#include "winuser.h" +#include "wine/unicode.h" +#include "action.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +/*********************************************************************** + * MsiDoActionA (MSI.@) + */ +UINT WINAPI MsiDoActionA( MSIHANDLE hInstall, LPCSTR szAction ) +{ + LPWSTR szwAction; + UINT ret; + + TRACE("%s\n", debugstr_a(szAction)); + + szwAction = strdupAtoW(szAction); + if (szAction && !szwAction) + return ERROR_FUNCTION_FAILED; + + ret = MsiDoActionW( hInstall, szwAction ); + msi_free( szwAction ); + return ret; +} + +/*********************************************************************** + * MsiDoActionW (MSI.@) + */ +UINT WINAPI MsiDoActionW( MSIHANDLE hInstall, LPCWSTR szAction ) +{ + MSIPACKAGE *package; + UINT ret; + + TRACE("%s\n",debugstr_w(szAction)); + + if (!szAction) + return ERROR_INVALID_PARAMETER; + + package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE ); + if (!package) + return ERROR_INVALID_HANDLE; + + ret = ACTION_PerformUIAction( package, szAction ); + msiobj_release( &package->hdr ); + + return ret; +} + +/*********************************************************************** + * MsiSequenceA (MSI.@) + */ +UINT WINAPI MsiSequenceA( MSIHANDLE hInstall, LPCSTR szTable, INT iSequenceMode ) +{ + LPWSTR szwTable; + UINT ret; + + TRACE("%s\n", debugstr_a(szTable)); + + szwTable = strdupAtoW(szTable); + if (szTable && !szwTable) + return ERROR_FUNCTION_FAILED; + + ret = MsiSequenceW( hInstall, szwTable, iSequenceMode ); + msi_free( szwTable ); + return ret; +} + +/*********************************************************************** + * MsiSequenceW (MSI.@) + */ +UINT WINAPI MsiSequenceW( MSIHANDLE hInstall, LPCWSTR szTable, INT iSequenceMode ) +{ + MSIPACKAGE *package; + UINT ret; + + TRACE("%s\n", debugstr_w(szTable)); + + package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE ); + if (!package) + return ERROR_INVALID_HANDLE; + + ret = MSI_Sequence( package, szTable, iSequenceMode ); + msiobj_release( &package->hdr ); + + return ret; +} + +UINT msi_strcpy_to_awstring( LPCWSTR str, awstring *awbuf, DWORD *sz ) +{ + UINT len, r = ERROR_SUCCESS; + + if (awbuf->str.w && !sz ) + return ERROR_INVALID_PARAMETER; + + if (!sz) + return r; + + if (awbuf->unicode) + { + len = lstrlenW( str ); + if (awbuf->str.w) + lstrcpynW( awbuf->str.w, str, *sz ); + } + else + { + len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL ); + if (len) + len--; + WideCharToMultiByte( CP_ACP, 0, str, -1, awbuf->str.a, *sz, NULL, NULL ); + if ( *sz && (len >= *sz) ) + awbuf->str.a[*sz - 1] = 0; + } + + if (awbuf->str.w && len >= *sz) + r = ERROR_MORE_DATA; + *sz = len; + return r; +} + +/*********************************************************************** + * MsiGetTargetPath (internal) + */ +UINT WINAPI MSI_GetTargetPath( MSIHANDLE hInstall, LPCWSTR szFolder, + awstring *szPathBuf, DWORD* pcchPathBuf ) +{ + MSIPACKAGE *package; + LPWSTR path; + UINT r; + + if (!szFolder) + return ERROR_INVALID_PARAMETER; + + package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE ); + if (!package) + return ERROR_INVALID_HANDLE; + + path = resolve_folder( package, szFolder, FALSE, FALSE, NULL ); + msiobj_release( &package->hdr ); + + if (!path) + return ERROR_DIRECTORY; + + r = msi_strcpy_to_awstring( path, szPathBuf, pcchPathBuf ); + msi_free( path ); + return r; +} + +/*********************************************************************** + * MsiGetTargetPathA (MSI.@) + */ +UINT WINAPI MsiGetTargetPathA( MSIHANDLE hInstall, LPCSTR szFolder, + LPSTR szPathBuf, DWORD* pcchPathBuf ) +{ + LPWSTR szwFolder; + awstring path; + UINT r; + + TRACE("%s %p %p\n", debugstr_a(szFolder), szPathBuf, pcchPathBuf); + + szwFolder = strdupAtoW(szFolder); + if (szFolder && !szwFolder) + return ERROR_FUNCTION_FAILED; + + path.unicode = FALSE; + path.str.a = szPathBuf; + + r = MSI_GetTargetPath( hInstall, szwFolder, &path, pcchPathBuf ); + + msi_free( szwFolder ); + + return r; +} + +/*********************************************************************** + * MsiGetTargetPathW (MSI.@) + */ +UINT WINAPI MsiGetTargetPathW( MSIHANDLE hInstall, LPCWSTR szFolder, + LPWSTR szPathBuf, DWORD* pcchPathBuf ) +{ + awstring path; + + TRACE("%s %p %p\n", debugstr_w(szFolder), szPathBuf, pcchPathBuf); + + path.unicode = TRUE; + path.str.w = szPathBuf; + + return MSI_GetTargetPath( hInstall, szFolder, &path, pcchPathBuf ); +} + +/*********************************************************************** + * MsiGetSourcePath (internal) + */ +static UINT MSI_GetSourcePath( MSIHANDLE hInstall, LPCWSTR szFolder, + awstring *szPathBuf, DWORD* pcchPathBuf ) +{ + MSIPACKAGE *package; + LPWSTR path; + UINT r; + + TRACE("%s %p %p\n", debugstr_w(szFolder), szPathBuf, pcchPathBuf ); + + if (!szFolder) + return ERROR_INVALID_PARAMETER; + + package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE); + if (!package) + return ERROR_INVALID_HANDLE; + + if (szPathBuf->str.w && !pcchPathBuf ) + { + msiobj_release( &package->hdr ); + return ERROR_INVALID_PARAMETER; + } + + path = resolve_folder(package, szFolder, TRUE, FALSE, NULL); + msiobj_release( &package->hdr ); + + TRACE("path = %s\n",debugstr_w(path)); + if (!path) + return ERROR_DIRECTORY; + + r = msi_strcpy_to_awstring( path, szPathBuf, pcchPathBuf ); + msi_free( path ); + return r; +} + +/*********************************************************************** + * MsiGetSourcePathA (MSI.@) + */ +UINT WINAPI MsiGetSourcePathA( MSIHANDLE hInstall, LPCSTR szFolder, + LPSTR szPathBuf, DWORD* pcchPathBuf ) +{ + LPWSTR folder; + awstring str; + UINT r; + + TRACE("%s %p %p\n", szFolder, debugstr_a(szPathBuf), pcchPathBuf); + + str.unicode = FALSE; + str.str.a = szPathBuf; + + folder = strdupAtoW( szFolder ); + r = MSI_GetSourcePath( hInstall, folder, &str, pcchPathBuf ); + msi_free( folder ); + + return r; +} + +/*********************************************************************** + * MsiGetSourcePathW (MSI.@) + */ +UINT WINAPI MsiGetSourcePathW( MSIHANDLE hInstall, LPCWSTR szFolder, + LPWSTR szPathBuf, DWORD* pcchPathBuf ) +{ + awstring str; + + TRACE("%s %p %p\n", debugstr_w(szFolder), szPathBuf, pcchPathBuf ); + + str.unicode = TRUE; + str.str.w = szPathBuf; + + return MSI_GetSourcePath( hInstall, szFolder, &str, pcchPathBuf ); +} + +/*********************************************************************** + * MsiSetTargetPathA (MSI.@) + */ +UINT WINAPI MsiSetTargetPathA( MSIHANDLE hInstall, LPCSTR szFolder, + LPCSTR szFolderPath ) +{ + LPWSTR szwFolder = NULL, szwFolderPath = NULL; + UINT rc = ERROR_OUTOFMEMORY; + + if ( !szFolder || !szFolderPath ) + return ERROR_INVALID_PARAMETER; + + szwFolder = strdupAtoW(szFolder); + szwFolderPath = strdupAtoW(szFolderPath); + if (!szwFolder || !szwFolderPath) + goto end; + + rc = MsiSetTargetPathW( hInstall, szwFolder, szwFolderPath ); + +end: + msi_free(szwFolder); + msi_free(szwFolderPath); + + return rc; +} + +/* + * Ok my original interpretation of this was wrong. And it looks like msdn has + * changed a bit also. The given folder path does not have to actually already + * exist, it just cannot be read only and must be a legal folder path. + */ +UINT MSI_SetTargetPathW(MSIPACKAGE *package, LPCWSTR szFolder, + LPCWSTR szFolderPath) +{ + DWORD attrib; + LPWSTR path = NULL; + LPWSTR path2 = NULL; + MSIFOLDER *folder; + + TRACE("(%p %s %s)\n",package, debugstr_w(szFolder),debugstr_w(szFolderPath)); + + attrib = GetFileAttributesW(szFolderPath); + if ( attrib != INVALID_FILE_ATTRIBUTES && + (!(attrib & FILE_ATTRIBUTE_DIRECTORY) || + attrib & FILE_ATTRIBUTE_OFFLINE || + attrib & FILE_ATTRIBUTE_READONLY)) + return ERROR_FUNCTION_FAILED; + + path = resolve_folder(package,szFolder,FALSE,FALSE,&folder); + if (!path) + return ERROR_DIRECTORY; + + if (attrib == INVALID_FILE_ATTRIBUTES) + { + if (!CreateDirectoryW(szFolderPath,NULL)) + { + msi_free( path ); + return ERROR_FUNCTION_FAILED; + } + RemoveDirectoryW(szFolderPath); + } + + msi_free(folder->Property); + folder->Property = build_directory_name(2, szFolderPath, NULL); + + if (lstrcmpiW(path, folder->Property) == 0) + { + /* + * Resolved Target has not really changed, so just + * set this folder and do not recalculate everything. + */ + msi_free(folder->ResolvedTarget); + folder->ResolvedTarget = NULL; + path2 = resolve_folder(package,szFolder,FALSE,TRUE,NULL); + msi_free(path2); + } + else + { + MSIFOLDER *f; + + LIST_FOR_EACH_ENTRY( f, &package->folders, MSIFOLDER, entry ) + { + msi_free(f->ResolvedTarget); + f->ResolvedTarget=NULL; + } + + LIST_FOR_EACH_ENTRY( f, &package->folders, MSIFOLDER, entry ) + { + path2 = resolve_folder(package, f->Directory, FALSE, TRUE, NULL); + msi_free(path2); + } + } + msi_free(path); + + return ERROR_SUCCESS; +} + +/*********************************************************************** + * MsiSetTargetPathW (MSI.@) + */ +UINT WINAPI MsiSetTargetPathW(MSIHANDLE hInstall, LPCWSTR szFolder, + LPCWSTR szFolderPath) +{ + MSIPACKAGE *package; + UINT ret; + + TRACE("(%s %s)\n",debugstr_w(szFolder),debugstr_w(szFolderPath)); + + if ( !szFolder || !szFolderPath ) + return ERROR_INVALID_PARAMETER; + + package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE); + if (!package) + return ERROR_INVALID_HANDLE; + + ret = MSI_SetTargetPathW( package, szFolder, szFolderPath ); + msiobj_release( &package->hdr ); + return ret; +} + +/*********************************************************************** + * MsiGetMode (MSI.@) + * + * Returns an internal installer state (if it is running in a mode iRunMode) + * + * PARAMS + * hInstall [I] Handle to the installation + * hRunMode [I] Checking run mode + * MSIRUNMODE_ADMIN Administrative mode + * MSIRUNMODE_ADVERTISE Advertisement mode + * MSIRUNMODE_MAINTENANCE Maintenance mode + * MSIRUNMODE_ROLLBACKENABLED Rollback is enabled + * MSIRUNMODE_LOGENABLED Log file is writing + * MSIRUNMODE_OPERATIONS Operations in progress?? + * MSIRUNMODE_REBOOTATEND We need to reboot after installation completed + * MSIRUNMODE_REBOOTNOW We need to reboot to continue the installation + * MSIRUNMODE_CABINET Files from cabinet are installed + * MSIRUNMODE_SOURCESHORTNAMES Long names in source files is suppressed + * MSIRUNMODE_TARGETSHORTNAMES Long names in destination files is suppressed + * MSIRUNMODE_RESERVED11 Reserved + * MSIRUNMODE_WINDOWS9X Running under Windows95/98 + * MSIRUNMODE_ZAWENABLED Demand installation is supported + * MSIRUNMODE_RESERVED14 Reserved + * MSIRUNMODE_RESERVED15 Reserved + * MSIRUNMODE_SCHEDULED called from install script + * MSIRUNMODE_ROLLBACK called from rollback script + * MSIRUNMODE_COMMIT called from commit script + * + * RETURNS + * In the state: TRUE + * Not in the state: FALSE + * + */ + +BOOL WINAPI MsiGetMode(MSIHANDLE hInstall, MSIRUNMODE iRunMode) +{ + BOOL r = FALSE; + + switch (iRunMode) + { + case MSIRUNMODE_WINDOWS9X: + if (GetVersion() & 0x80000000) + r = TRUE; + break; + + case MSIRUNMODE_RESERVED11: + case MSIRUNMODE_RESERVED14: + case MSIRUNMODE_RESERVED15: + break; + + case MSIRUNMODE_SCHEDULED: + case MSIRUNMODE_ROLLBACK: + case MSIRUNMODE_COMMIT: + break; + + default: + FIXME("%ld %d\n", hInstall, iRunMode); + r = TRUE; + } + + return r; +} + +/*********************************************************************** + * MsiSetMode (MSI.@) + */ +BOOL WINAPI MsiSetMode(MSIHANDLE hInstall, MSIRUNMODE iRunMode, BOOL fState) +{ + switch (iRunMode) + { + case MSIRUNMODE_RESERVED11: + case MSIRUNMODE_WINDOWS9X: + case MSIRUNMODE_RESERVED14: + case MSIRUNMODE_RESERVED15: + return FALSE; + default: + FIXME("%ld %d %d\n", hInstall, iRunMode, fState); + } + return TRUE; +} + +/*********************************************************************** + * MsiSetFeatureStateA (MSI.@) + * + * According to the docs, when this is called it immediately recalculates + * all the component states as well + */ +UINT WINAPI MsiSetFeatureStateA(MSIHANDLE hInstall, LPCSTR szFeature, + INSTALLSTATE iState) +{ + LPWSTR szwFeature = NULL; + UINT rc; + + szwFeature = strdupAtoW(szFeature); + + if (!szwFeature) + return ERROR_FUNCTION_FAILED; + + rc = MsiSetFeatureStateW(hInstall,szwFeature, iState); + + msi_free(szwFeature); + + return rc; +} + + + +UINT WINAPI MSI_SetFeatureStateW(MSIPACKAGE* package, LPCWSTR szFeature, + INSTALLSTATE iState) +{ + UINT rc = ERROR_SUCCESS; + MSIFEATURE *feature, *child; + + TRACE(" %s to %i\n",debugstr_w(szFeature), iState); + + feature = get_loaded_feature(package,szFeature); + if (!feature) + return ERROR_UNKNOWN_FEATURE; + + if (iState == INSTALLSTATE_ADVERTISED && + feature->Attributes & msidbFeatureAttributesDisallowAdvertise) + return ERROR_FUNCTION_FAILED; + + feature->ActionRequest = iState; + feature->Action = iState; + + ACTION_UpdateComponentStates(package,szFeature); + + /* update all the features that are children of this feature */ + LIST_FOR_EACH_ENTRY( child, &package->features, MSIFEATURE, entry ) + { + if (lstrcmpW(szFeature, child->Feature_Parent) == 0) + MSI_SetFeatureStateW(package, child->Feature, iState); + } + + return rc; +} + +/*********************************************************************** + * MsiSetFeatureStateW (MSI.@) + */ +UINT WINAPI MsiSetFeatureStateW(MSIHANDLE hInstall, LPCWSTR szFeature, + INSTALLSTATE iState) +{ + MSIPACKAGE* package; + UINT rc = ERROR_SUCCESS; + + TRACE(" %s to %i\n",debugstr_w(szFeature), iState); + + package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE); + if (!package) + return ERROR_INVALID_HANDLE; + + rc = MSI_SetFeatureStateW(package,szFeature,iState); + + msiobj_release( &package->hdr ); + return rc; +} + +/*********************************************************************** +* MsiGetFeatureStateA (MSI.@) +*/ +UINT WINAPI MsiGetFeatureStateA(MSIHANDLE hInstall, LPSTR szFeature, + INSTALLSTATE *piInstalled, INSTALLSTATE *piAction) +{ + LPWSTR szwFeature = NULL; + UINT rc; + + szwFeature = strdupAtoW(szFeature); + + rc = MsiGetFeatureStateW(hInstall,szwFeature,piInstalled, piAction); + + msi_free( szwFeature); + + return rc; +} + +UINT MSI_GetFeatureStateW(MSIPACKAGE *package, LPWSTR szFeature, + INSTALLSTATE *piInstalled, INSTALLSTATE *piAction) +{ + MSIFEATURE *feature; + + feature = get_loaded_feature(package,szFeature); + if (!feature) + return ERROR_UNKNOWN_FEATURE; + + if (piInstalled) + *piInstalled = feature->Installed; + + if (piAction) + *piAction = feature->Action; + + TRACE("returning %i %i\n", feature->Installed, feature->Action); + + return ERROR_SUCCESS; +} + +/*********************************************************************** +* MsiGetFeatureStateW (MSI.@) +*/ +UINT WINAPI MsiGetFeatureStateW(MSIHANDLE hInstall, LPWSTR szFeature, + INSTALLSTATE *piInstalled, INSTALLSTATE *piAction) +{ + MSIPACKAGE* package; + UINT ret; + + TRACE("%ld %s %p %p\n", hInstall, debugstr_w(szFeature), piInstalled, +piAction); + + package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE); + if (!package) + return ERROR_INVALID_HANDLE; + ret = MSI_GetFeatureStateW(package, szFeature, piInstalled, piAction); + msiobj_release( &package->hdr ); + return ret; +} + +/*********************************************************************** + * MsiSetComponentStateA (MSI.@) + */ +UINT WINAPI MsiSetComponentStateA(MSIHANDLE hInstall, LPCSTR szComponent, + INSTALLSTATE iState) +{ + UINT rc; + LPWSTR szwComponent = strdupAtoW(szComponent); + + rc = MsiSetComponentStateW(hInstall, szwComponent, iState); + + msi_free(szwComponent); + + return rc; +} + +/*********************************************************************** + * MsiGetComponentStateA (MSI.@) + */ +UINT WINAPI MsiGetComponentStateA(MSIHANDLE hInstall, LPSTR szComponent, + INSTALLSTATE *piInstalled, INSTALLSTATE *piAction) +{ + LPWSTR szwComponent= NULL; + UINT rc; + + szwComponent= strdupAtoW(szComponent); + + rc = MsiGetComponentStateW(hInstall,szwComponent,piInstalled, piAction); + + msi_free( szwComponent); + + return rc; +} + +static UINT MSI_SetComponentStateW(MSIPACKAGE *package, LPCWSTR szComponent, + INSTALLSTATE iState) +{ + MSICOMPONENT *comp; + + TRACE("%p %s %d\n", package, debugstr_w(szComponent), iState); + + comp = get_loaded_component(package, szComponent); + if (!comp) + return ERROR_UNKNOWN_COMPONENT; + + comp->Installed = iState; + + return ERROR_SUCCESS; +} + +UINT MSI_GetComponentStateW(MSIPACKAGE *package, LPWSTR szComponent, + INSTALLSTATE *piInstalled, INSTALLSTATE *piAction) +{ + MSICOMPONENT *comp; + + TRACE("%p %s %p %p\n", package, debugstr_w(szComponent), + piInstalled, piAction); + + comp = get_loaded_component(package,szComponent); + if (!comp) + return ERROR_UNKNOWN_COMPONENT; + + if (piInstalled) + *piInstalled = comp->Installed; + + if (piAction) + *piAction = comp->Action; + + TRACE("states (%i, %i)\n", comp->Installed, comp->Action ); + + return ERROR_SUCCESS; +} + +/*********************************************************************** + * MsiSetComponentStateW (MSI.@) + */ +UINT WINAPI MsiSetComponentStateW(MSIHANDLE hInstall, LPCWSTR szComponent, + INSTALLSTATE iState) +{ + MSIPACKAGE* package; + UINT ret; + + package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE); + if (!package) + return ERROR_INVALID_HANDLE; + ret = MSI_SetComponentStateW(package, szComponent, iState); + msiobj_release(&package->hdr); + return ret; +} + +/*********************************************************************** + * MsiGetComponentStateW (MSI.@) + */ +UINT WINAPI MsiGetComponentStateW(MSIHANDLE hInstall, LPWSTR szComponent, + INSTALLSTATE *piInstalled, INSTALLSTATE *piAction) +{ + MSIPACKAGE* package; + UINT ret; + + TRACE("%ld %s %p %p\n", hInstall, debugstr_w(szComponent), + piInstalled, piAction); + + package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE); + if (!package) + return ERROR_INVALID_HANDLE; + ret = MSI_GetComponentStateW( package, szComponent, piInstalled, piAction); + msiobj_release( &package->hdr ); + return ret; +} + +/*********************************************************************** + * MsiGetLanguage (MSI.@) + */ +LANGID WINAPI MsiGetLanguage(MSIHANDLE hInstall) +{ + MSIPACKAGE* package; + LANGID langid; + LPWSTR buffer; + static const WCHAR szProductLanguage[] = + {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0}; + + package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE); + if (!package) + return ERROR_INVALID_HANDLE; + + buffer = msi_dup_property( package, szProductLanguage ); + langid = atoiW(buffer); + + msi_free(buffer); + msiobj_release (&package->hdr); + return langid; +} diff --git a/reactos/dll/msi/msi.c b/reactos/dll/msi/msi.c new file mode 100644 index 00000000000..404bdf6b402 --- /dev/null +++ b/reactos/dll/msi/msi.c @@ -0,0 +1,2038 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002,2003,2004,2005 Mike McCormack 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#define COBJMACROS +#define NONAMELESSUNION + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winnls.h" +#include "shlwapi.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "msipriv.h" +#include "wincrypt.h" +#include "winver.h" +#include "winuser.h" +#include "shlobj.h" +#include "shobjidl.h" +#include "objidl.h" +#include "wine/unicode.h" +#include "action.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +/* the UI level */ +INSTALLUILEVEL gUILevel = INSTALLUILEVEL_BASIC; +HWND gUIhwnd = 0; +INSTALLUI_HANDLERA gUIHandlerA = NULL; +INSTALLUI_HANDLERW gUIHandlerW = NULL; +DWORD gUIFilter = 0; +LPVOID gUIContext = NULL; +WCHAR gszLogFile[MAX_PATH]; +HINSTANCE msi_hInstance; + +static LONG dll_count; + +static const WCHAR installerW[] = {'\\','I','n','s','t','a','l','l','e','r',0}; + +/* + * Dll lifetime tracking declaration + */ +static void LockModule(void) +{ + InterlockedIncrement(&dll_count); +} + +static void UnlockModule(void) +{ + InterlockedDecrement(&dll_count); +} + + +UINT WINAPI MsiOpenProductA(LPCSTR szProduct, MSIHANDLE *phProduct) +{ + UINT r; + LPWSTR szwProd = NULL; + + TRACE("%s %p\n",debugstr_a(szProduct), phProduct); + + if( szProduct ) + { + szwProd = strdupAtoW( szProduct ); + if( !szwProd ) + return ERROR_OUTOFMEMORY; + } + + r = MsiOpenProductW( szwProd, phProduct ); + + msi_free( szwProd ); + + return r; +} + +static UINT MSI_OpenProductW( LPCWSTR szProduct, MSIPACKAGE **ppackage ) +{ + LPWSTR path = NULL; + UINT r; + HKEY hKeyProduct = NULL; + DWORD count, type; + + TRACE("%s %p\n", debugstr_w(szProduct), ppackage ); + + r = MSIREG_OpenUninstallKey(szProduct,&hKeyProduct,FALSE); + if( r != ERROR_SUCCESS ) + { + r = ERROR_UNKNOWN_PRODUCT; + goto end; + } + + /* find the size of the path */ + type = count = 0; + r = RegQueryValueExW( hKeyProduct, INSTALLPROPERTY_LOCALPACKAGEW, + NULL, &type, NULL, &count ); + if( r != ERROR_SUCCESS ) + { + r = ERROR_UNKNOWN_PRODUCT; + goto end; + } + + /* now alloc and fetch the path of the database to open */ + path = msi_alloc( count ); + if( !path ) + goto end; + + r = RegQueryValueExW( hKeyProduct, INSTALLPROPERTY_LOCALPACKAGEW, + NULL, &type, (LPBYTE) path, &count ); + if( r != ERROR_SUCCESS ) + { + r = ERROR_UNKNOWN_PRODUCT; + goto end; + } + + r = MSI_OpenPackageW( path, ppackage ); + +end: + msi_free( path ); + if( hKeyProduct ) + RegCloseKey( hKeyProduct ); + + return r; +} + +UINT WINAPI MsiOpenProductW( LPCWSTR szProduct, MSIHANDLE *phProduct ) +{ + MSIPACKAGE *package = NULL; + UINT r; + + r = MSI_OpenProductW( szProduct, &package ); + if( r == ERROR_SUCCESS ) + { + *phProduct = alloc_msihandle( &package->hdr ); + msiobj_release( &package->hdr ); + } + return r; +} + +UINT WINAPI MsiAdvertiseProductA(LPCSTR szPackagePath, LPCSTR szScriptfilePath, + LPCSTR szTransforms, LANGID lgidLanguage) +{ + FIXME("%s %s %s %08x\n",debugstr_a(szPackagePath), + debugstr_a(szScriptfilePath), debugstr_a(szTransforms), lgidLanguage); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiAdvertiseProductW(LPCWSTR szPackagePath, LPCWSTR szScriptfilePath, + LPCWSTR szTransforms, LANGID lgidLanguage) +{ + FIXME("%s %s %s %08x\n",debugstr_w(szPackagePath), + debugstr_w(szScriptfilePath), debugstr_w(szTransforms), lgidLanguage); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiAdvertiseProductExA(LPCSTR szPackagePath, LPCSTR szScriptfilePath, + LPCSTR szTransforms, LANGID lgidLanguage, DWORD dwPlatform, DWORD dwOptions) +{ + FIXME("%s %s %s %08x %08lx %08lx\n", debugstr_a(szPackagePath), + debugstr_a(szScriptfilePath), debugstr_a(szTransforms), + lgidLanguage, dwPlatform, dwOptions); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiAdvertiseProductExW( LPCWSTR szPackagePath, LPCWSTR szScriptfilePath, + LPCWSTR szTransforms, LANGID lgidLanguage, DWORD dwPlatform, DWORD dwOptions) +{ + FIXME("%s %s %s %08x %08lx %08lx\n", debugstr_w(szPackagePath), + debugstr_w(szScriptfilePath), debugstr_w(szTransforms), + lgidLanguage, dwPlatform, dwOptions); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiInstallProductA(LPCSTR szPackagePath, LPCSTR szCommandLine) +{ + LPWSTR szwPath = NULL, szwCommand = NULL; + UINT r = ERROR_OUTOFMEMORY; + + TRACE("%s %s\n",debugstr_a(szPackagePath), debugstr_a(szCommandLine)); + + if( szPackagePath ) + { + szwPath = strdupAtoW( szPackagePath ); + if( !szwPath ) + goto end; + } + + if( szCommandLine ) + { + szwCommand = strdupAtoW( szCommandLine ); + if( !szwCommand ) + goto end; + } + + r = MsiInstallProductW( szwPath, szwCommand ); + +end: + msi_free( szwPath ); + msi_free( szwCommand ); + + return r; +} + +UINT WINAPI MsiInstallProductW(LPCWSTR szPackagePath, LPCWSTR szCommandLine) +{ + MSIPACKAGE *package = NULL; + UINT r; + + FIXME("%s %s\n",debugstr_w(szPackagePath), debugstr_w(szCommandLine)); + + r = MSI_OpenPackageW( szPackagePath, &package ); + if (r == ERROR_SUCCESS) + { + r = MSI_InstallPackage( package, szPackagePath, szCommandLine ); + msiobj_release( &package->hdr ); + } + + return r; +} + +UINT WINAPI MsiReinstallProductA(LPCSTR szProduct, DWORD dwReinstallMode) +{ + FIXME("%s %08lx\n", debugstr_a(szProduct), dwReinstallMode); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiReinstallProductW(LPCWSTR szProduct, DWORD dwReinstallMode) +{ + FIXME("%s %08lx\n", debugstr_w(szProduct), dwReinstallMode); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiApplyPatchA(LPCSTR szPatchPackage, LPCSTR szInstallPackage, + INSTALLTYPE eInstallType, LPCSTR szCommandLine) +{ + FIXME("%s %s %d %s\n", debugstr_a(szPatchPackage), debugstr_a(szInstallPackage), + eInstallType, debugstr_a(szCommandLine)); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiApplyPatchW(LPCWSTR szPatchPackage, LPCWSTR szInstallPackage, + INSTALLTYPE eInstallType, LPCWSTR szCommandLine) +{ + FIXME("%s %s %d %s\n", debugstr_w(szPatchPackage), debugstr_w(szInstallPackage), + eInstallType, debugstr_w(szCommandLine)); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiConfigureProductExW(LPCWSTR szProduct, int iInstallLevel, + INSTALLSTATE eInstallState, LPCWSTR szCommandLine) +{ + MSIPACKAGE* package = NULL; + UINT r; + DWORD sz; + WCHAR sourcepath[MAX_PATH]; + WCHAR filename[MAX_PATH]; + static const WCHAR szInstalled[] = { + ' ','I','n','s','t','a','l','l','e','d','=','1',0}; + LPWSTR commandline; + + FIXME("%s %d %d %s\n",debugstr_w(szProduct), iInstallLevel, eInstallState, + debugstr_w(szCommandLine)); + + if (eInstallState != INSTALLSTATE_LOCAL && + eInstallState != INSTALLSTATE_DEFAULT) + { + FIXME("Not implemented for anything other than local installs\n"); + return ERROR_CALL_NOT_IMPLEMENTED; + } + + sz = sizeof(sourcepath); + MsiSourceListGetInfoW(szProduct, NULL, MSIINSTALLCONTEXT_USERMANAGED, + MSICODE_PRODUCT, INSTALLPROPERTY_LASTUSEDSOURCEW, sourcepath, + &sz); + + sz = sizeof(filename); + MsiSourceListGetInfoW(szProduct, NULL, MSIINSTALLCONTEXT_USERMANAGED, + MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAMEW, filename, &sz); + + lstrcatW(sourcepath,filename); + + /* + * ok 1, we need to find the msi file for this product. + * 2, find the source dir for the files + * 3, do the configure/install. + * 4, cleanupany runonce entry. + */ + + r = MSI_OpenProductW( szProduct, &package ); + if (r != ERROR_SUCCESS) + return r; + + sz = lstrlenW(szInstalled) + 1; + + if (szCommandLine) + sz += lstrlenW(szCommandLine); + + commandline = msi_alloc(sz * sizeof(WCHAR)); + if (!commandline ) + { + r = ERROR_OUTOFMEMORY; + goto end; + } + + commandline[0] = 0; + if (szCommandLine) + lstrcpyW(commandline,szCommandLine); + + if (MsiQueryProductStateW(szProduct) != INSTALLSTATE_UNKNOWN) + lstrcatW(commandline,szInstalled); + + r = MSI_InstallPackage( package, sourcepath, commandline ); + + msi_free(commandline); + +end: + msiobj_release( &package->hdr ); + + return r; +} + +UINT WINAPI MsiConfigureProductExA(LPCSTR szProduct, int iInstallLevel, + INSTALLSTATE eInstallState, LPCSTR szCommandLine) +{ + LPWSTR szwProduct = NULL; + LPWSTR szwCommandLine = NULL; + UINT r = ERROR_OUTOFMEMORY; + + if( szProduct ) + { + szwProduct = strdupAtoW( szProduct ); + if( !szwProduct ) + goto end; + } + + if( szCommandLine) + { + szwCommandLine = strdupAtoW( szCommandLine ); + if( !szwCommandLine) + goto end; + } + + r = MsiConfigureProductExW( szwProduct, iInstallLevel, eInstallState, + szwCommandLine ); +end: + msi_free( szwProduct ); + msi_free( szwCommandLine); + + return r; +} + +UINT WINAPI MsiConfigureProductA(LPCSTR szProduct, int iInstallLevel, + INSTALLSTATE eInstallState) +{ + LPWSTR szwProduct = NULL; + UINT r; + + TRACE("%s %d %d\n",debugstr_a(szProduct), iInstallLevel, eInstallState); + + if( szProduct ) + { + szwProduct = strdupAtoW( szProduct ); + if( !szwProduct ) + return ERROR_OUTOFMEMORY; + } + + r = MsiConfigureProductW( szwProduct, iInstallLevel, eInstallState ); + msi_free( szwProduct ); + + return r; +} + +UINT WINAPI MsiConfigureProductW(LPCWSTR szProduct, int iInstallLevel, + INSTALLSTATE eInstallState) +{ + FIXME("%s %d %d\n", debugstr_w(szProduct), iInstallLevel, eInstallState); + + return MsiConfigureProductExW(szProduct, iInstallLevel, eInstallState, NULL); +} + +UINT WINAPI MsiGetProductCodeA(LPCSTR szComponent, LPSTR szBuffer) +{ + LPWSTR szwComponent = NULL; + UINT r; + WCHAR szwBuffer[GUID_SIZE]; + + TRACE("%s %s\n",debugstr_a(szComponent), debugstr_a(szBuffer)); + + if( szComponent ) + { + szwComponent = strdupAtoW( szComponent ); + if( !szwComponent ) + return ERROR_OUTOFMEMORY; + } + + r = MsiGetProductCodeW( szwComponent, szwBuffer ); + + if( ERROR_SUCCESS == r ) + WideCharToMultiByte(CP_ACP, 0, szwBuffer, -1, szBuffer, GUID_SIZE, NULL, NULL); + + msi_free( szwComponent ); + + return r; +} + +UINT WINAPI MsiGetProductCodeW(LPCWSTR szComponent, LPWSTR szBuffer) +{ + UINT rc; + HKEY hkey; + WCHAR szSquished[GUID_SIZE]; + DWORD sz = GUID_SIZE; + static const WCHAR szPermKey[] = + { '0','0','0','0','0','0','0','0','0','0','0','0', + '0','0','0','0','0','0','0','0','0','0','0','0', + '0','0','0','0','0','0','0','0',0}; + + TRACE("%s %p\n",debugstr_w(szComponent), szBuffer); + + if (NULL == szComponent) + return ERROR_INVALID_PARAMETER; + + rc = MSIREG_OpenComponentsKey( szComponent, &hkey, FALSE); + if (rc != ERROR_SUCCESS) + return ERROR_UNKNOWN_COMPONENT; + + rc = RegEnumValueW(hkey, 0, szSquished, &sz, NULL, NULL, NULL, NULL); + if (rc == ERROR_SUCCESS && strcmpW(szSquished,szPermKey)==0) + { + sz = GUID_SIZE; + rc = RegEnumValueW(hkey, 1, szSquished, &sz, NULL, NULL, NULL, NULL); + } + + RegCloseKey(hkey); + + if (rc != ERROR_SUCCESS) + return ERROR_INSTALL_FAILURE; + + unsquash_guid(szSquished, szBuffer); + return ERROR_SUCCESS; +} + +UINT WINAPI MsiGetProductInfoA(LPCSTR szProduct, LPCSTR szAttribute, + LPSTR szBuffer, DWORD *pcchValueBuf) +{ + LPWSTR szwProduct = NULL, szwAttribute = NULL, szwBuffer = NULL; + UINT r = ERROR_OUTOFMEMORY; + DWORD pcchwValueBuf = 0; + + TRACE("%s %s %p %p\n", debugstr_a(szProduct), debugstr_a(szAttribute), + szBuffer, pcchValueBuf); + + if( szProduct ) + { + szwProduct = strdupAtoW( szProduct ); + if( !szwProduct ) + goto end; + } + + if( szAttribute ) + { + szwAttribute = strdupAtoW( szAttribute ); + if( !szwAttribute ) + goto end; + } + + if( szBuffer ) + { + szwBuffer = msi_alloc( (*pcchValueBuf) * sizeof(WCHAR) ); + pcchwValueBuf = *pcchValueBuf; + if( !szwBuffer ) + goto end; + } + + r = MsiGetProductInfoW( szwProduct, szwAttribute, szwBuffer, + &pcchwValueBuf ); + + if( ERROR_SUCCESS == r ) + { + INT old_len = *pcchValueBuf; + *pcchValueBuf = WideCharToMultiByte(CP_ACP, 0, szwBuffer, pcchwValueBuf, + szBuffer, *pcchValueBuf, NULL, NULL); + if (old_len > *pcchValueBuf) + szBuffer[*pcchValueBuf]=0; + } + +end: + msi_free( szwProduct ); + msi_free( szwAttribute ); + msi_free( szwBuffer ); + + return r; +} + +UINT WINAPI MsiGetProductInfoW(LPCWSTR szProduct, LPCWSTR szAttribute, + LPWSTR szBuffer, DWORD *pcchValueBuf) +{ + MSIHANDLE hProduct; + UINT r; + static const WCHAR szProductVersion[] = + {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0}; + static const WCHAR szProductLanguage[] = + {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0}; + + FIXME("%s %s %p %p\n",debugstr_w(szProduct), debugstr_w(szAttribute), + szBuffer, pcchValueBuf); + + if (NULL != szBuffer && NULL == pcchValueBuf) + return ERROR_INVALID_PARAMETER; + if (NULL == szProduct || NULL == szAttribute) + return ERROR_INVALID_PARAMETER; + + /* check for special properties */ + if (strcmpW(szAttribute, INSTALLPROPERTY_PACKAGECODEW)==0) + { + HKEY hkey; + WCHAR squished[GUID_SIZE]; + WCHAR package[200]; + DWORD sz = sizeof(squished); + + r = MSIREG_OpenUserProductsKey(szProduct, &hkey, FALSE); + if (r != ERROR_SUCCESS) + return ERROR_UNKNOWN_PRODUCT; + + r = RegQueryValueExW(hkey, INSTALLPROPERTY_PACKAGECODEW, NULL, NULL, + (LPBYTE)squished, &sz); + if (r != ERROR_SUCCESS) + { + RegCloseKey(hkey); + return ERROR_UNKNOWN_PRODUCT; + } + + unsquash_guid(squished, package); + *pcchValueBuf = strlenW(package); + if (strlenW(package) > *pcchValueBuf) + { + RegCloseKey(hkey); + return ERROR_MORE_DATA; + } + else + strcpyW(szBuffer, package); + + RegCloseKey(hkey); + r = ERROR_SUCCESS; + } + else if (strcmpW(szAttribute, INSTALLPROPERTY_VERSIONSTRINGW)==0) + { + r = MsiOpenProductW(szProduct, &hProduct); + if (ERROR_SUCCESS != r) + return r; + + r = MsiGetPropertyW(hProduct, szProductVersion, szBuffer, pcchValueBuf); + MsiCloseHandle(hProduct); + } + else if (strcmpW(szAttribute, INSTALLPROPERTY_ASSIGNMENTTYPEW)==0) + { + FIXME("0 (zero) if advertised or per user , 1(one) if per machine.\n"); + if (szBuffer) + { + szBuffer[0] = '1'; + szBuffer[1] = 0; + } + if (pcchValueBuf) + *pcchValueBuf = 1; + r = ERROR_SUCCESS; + } + else if (strcmpW(szAttribute, INSTALLPROPERTY_LANGUAGEW)==0) + { + r = MsiOpenProductW(szProduct, &hProduct); + if (ERROR_SUCCESS != r) + return r; + + r = MsiGetPropertyW(hProduct, szProductLanguage, szBuffer, pcchValueBuf); + MsiCloseHandle(hProduct); + } + else + { + r = MsiOpenProductW(szProduct, &hProduct); + if (ERROR_SUCCESS != r) + return r; + + r = MsiGetPropertyW(hProduct, szAttribute, szBuffer, pcchValueBuf); + MsiCloseHandle(hProduct); + } + + return r; +} + +UINT WINAPI MsiEnableLogA(DWORD dwLogMode, LPCSTR szLogFile, DWORD attributes) +{ + LPWSTR szwLogFile = NULL; + UINT r; + + TRACE("%08lx %s %08lx\n", dwLogMode, debugstr_a(szLogFile), attributes); + + if( szLogFile ) + { + szwLogFile = strdupAtoW( szLogFile ); + if( !szwLogFile ) + return ERROR_OUTOFMEMORY; + } + r = MsiEnableLogW( dwLogMode, szwLogFile, attributes ); + msi_free( szwLogFile ); + return r; +} + +UINT WINAPI MsiEnableLogW(DWORD dwLogMode, LPCWSTR szLogFile, DWORD attributes) +{ + HANDLE file = INVALID_HANDLE_VALUE; + + TRACE("%08lx %s %08lx\n", dwLogMode, debugstr_w(szLogFile), attributes); + + lstrcpyW(gszLogFile,szLogFile); + if (!(attributes & INSTALLLOGATTRIBUTES_APPEND)) + DeleteFileW(szLogFile); + file = CreateFileW(szLogFile, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, + FILE_ATTRIBUTE_NORMAL, NULL); + if (file != INVALID_HANDLE_VALUE) + CloseHandle(file); + else + ERR("Unable to enable log %s\n",debugstr_w(szLogFile)); + + return ERROR_SUCCESS; +} + +INSTALLSTATE WINAPI MsiQueryProductStateA(LPCSTR szProduct) +{ + LPWSTR szwProduct = NULL; + INSTALLSTATE r; + + if( szProduct ) + { + szwProduct = strdupAtoW( szProduct ); + if( !szwProduct ) + return ERROR_OUTOFMEMORY; + } + r = MsiQueryProductStateW( szwProduct ); + msi_free( szwProduct ); + return r; +} + +INSTALLSTATE WINAPI MsiQueryProductStateW(LPCWSTR szProduct) +{ + UINT rc; + INSTALLSTATE rrc = INSTALLSTATE_UNKNOWN; + HKEY hkey = 0; + static const WCHAR szWindowsInstaller[] = { + 'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0 }; + DWORD sz; + + TRACE("%s\n", debugstr_w(szProduct)); + + rc = MSIREG_OpenUserProductsKey(szProduct,&hkey,FALSE); + if (rc != ERROR_SUCCESS) + goto end; + + RegCloseKey(hkey); + + rc = MSIREG_OpenUninstallKey(szProduct,&hkey,FALSE); + if (rc != ERROR_SUCCESS) + goto end; + + sz = sizeof(rrc); + rc = RegQueryValueExW(hkey,szWindowsInstaller,NULL,NULL,(LPVOID)&rrc, &sz); + if (rc != ERROR_SUCCESS) + goto end; + + switch (rrc) + { + case 1: + /* default */ + rrc = INSTALLSTATE_DEFAULT; + break; + default: + FIXME("Unknown install state read from registry (%i)\n",rrc); + rrc = INSTALLSTATE_UNKNOWN; + break; + } +end: + RegCloseKey(hkey); + return rrc; +} + +INSTALLUILEVEL WINAPI MsiSetInternalUI(INSTALLUILEVEL dwUILevel, HWND *phWnd) +{ + INSTALLUILEVEL old = gUILevel; + HWND oldwnd = gUIhwnd; + + TRACE("%08x %p\n", dwUILevel, phWnd); + + gUILevel = dwUILevel; + if (phWnd) + { + gUIhwnd = *phWnd; + *phWnd = oldwnd; + } + return old; +} + +INSTALLUI_HANDLERA WINAPI MsiSetExternalUIA(INSTALLUI_HANDLERA puiHandler, + DWORD dwMessageFilter, LPVOID pvContext) +{ + INSTALLUI_HANDLERA prev = gUIHandlerA; + + TRACE("%p %lx %p\n",puiHandler, dwMessageFilter,pvContext); + gUIHandlerA = puiHandler; + gUIFilter = dwMessageFilter; + gUIContext = pvContext; + + return prev; +} + +INSTALLUI_HANDLERW WINAPI MsiSetExternalUIW(INSTALLUI_HANDLERW puiHandler, + DWORD dwMessageFilter, LPVOID pvContext) +{ + INSTALLUI_HANDLERW prev = gUIHandlerW; + + TRACE("%p %lx %p\n",puiHandler,dwMessageFilter,pvContext); + gUIHandlerW = puiHandler; + gUIFilter = dwMessageFilter; + gUIContext = pvContext; + + return prev; +} + +/****************************************************************** + * MsiLoadStringW [MSI.@] + * + * Loads a string from MSI's string resources. + * + * PARAMS + * + * handle [I] only -1 is handled currently + * id [I] id of the string to be loaded + * lpBuffer [O] buffer for the string to be written to + * nBufferMax [I] maximum size of the buffer in characters + * lang [I] the preferred language for the string + * + * RETURNS + * + * If successful, this function returns the language id of the string loaded + * If the function fails, the function returns zero. + * + * NOTES + * + * The type of the first parameter is unknown. LoadString's prototype + * suggests that it might be a module handle. I have made it an MSI handle + * for starters, as -1 is an invalid MSI handle, but not an invalid module + * handle. Maybe strings can be stored in an MSI database somehow. + */ +LANGID WINAPI MsiLoadStringW( MSIHANDLE handle, UINT id, LPWSTR lpBuffer, + int nBufferMax, LANGID lang ) +{ + HRSRC hres; + HGLOBAL hResData; + LPWSTR p; + DWORD i, len; + + TRACE("%ld %u %p %d %d\n", handle, id, lpBuffer, nBufferMax, lang); + + if( handle != -1 ) + FIXME("don't know how to deal with handle = %08lx\n", handle); + + if( !lang ) + lang = GetUserDefaultLangID(); + + hres = FindResourceExW( msi_hInstance, (LPCWSTR) RT_STRING, + (LPWSTR)1, lang ); + if( !hres ) + return 0; + hResData = LoadResource( msi_hInstance, hres ); + if( !hResData ) + return 0; + p = LockResource( hResData ); + if( !p ) + return 0; + + for (i = 0; i < (id&0xf); i++) + p += *p + 1; + len = *p; + + if( nBufferMax <= len ) + return 0; + + memcpy( lpBuffer, p+1, len * sizeof(WCHAR)); + lpBuffer[ len ] = 0; + + TRACE("found -> %s\n", debugstr_w(lpBuffer)); + + return lang; +} + +LANGID WINAPI MsiLoadStringA( MSIHANDLE handle, UINT id, LPSTR lpBuffer, + int nBufferMax, LANGID lang ) +{ + LPWSTR bufW; + LANGID r; + DWORD len; + + bufW = msi_alloc(nBufferMax*sizeof(WCHAR)); + r = MsiLoadStringW(handle, id, bufW, nBufferMax, lang); + if( r ) + { + len = WideCharToMultiByte(CP_ACP, 0, bufW, -1, NULL, 0, NULL, NULL ); + if( len <= nBufferMax ) + WideCharToMultiByte( CP_ACP, 0, bufW, -1, + lpBuffer, nBufferMax, NULL, NULL ); + else + r = 0; + } + msi_free(bufW); + return r; +} + +INSTALLSTATE WINAPI MsiLocateComponentA(LPCSTR szComponent, LPSTR lpPathBuf, + DWORD *pcchBuf) +{ + /* This FIXME will crash some installer + * FIXME("%s %p %p\n", debugstr_a(szComponent), lpPathBuf, pcchBuf); + */ + return INSTALLSTATE_UNKNOWN; +} + +INSTALLSTATE WINAPI MsiLocateComponentW(LPCWSTR szComponent, LPWSTR lpPathBuf, + DWORD *pcchBuf) +{ + FIXME("%s %p %p\n", debugstr_w(szComponent), lpPathBuf, pcchBuf); + return INSTALLSTATE_UNKNOWN; +} + +UINT WINAPI MsiMessageBoxA(HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType, + WORD wLanguageId, DWORD f) +{ + FIXME("%p %s %s %u %08x %08lx\n",hWnd,debugstr_a(lpText),debugstr_a(lpCaption), + uType,wLanguageId,f); + return MessageBoxExA(hWnd,lpText,lpCaption,uType,wLanguageId); +} + +UINT WINAPI MsiMessageBoxW(HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType, + WORD wLanguageId, DWORD f) +{ + FIXME("%p %s %s %u %08x %08lx\n",hWnd,debugstr_w(lpText),debugstr_w(lpCaption), + uType,wLanguageId,f); + return MessageBoxExW(hWnd,lpText,lpCaption,uType,wLanguageId); +} + +UINT WINAPI MsiProvideAssemblyA( LPCSTR szAssemblyName, LPCSTR szAppContext, + DWORD dwInstallMode, DWORD dwAssemblyInfo, LPSTR lpPathBuf, + DWORD* pcchPathBuf ) +{ + FIXME("%s %s %08lx %08lx %p %p\n", debugstr_a(szAssemblyName), + debugstr_a(szAppContext), dwInstallMode, dwAssemblyInfo, lpPathBuf, + pcchPathBuf); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiProvideAssemblyW( LPCWSTR szAssemblyName, LPCWSTR szAppContext, + DWORD dwInstallMode, DWORD dwAssemblyInfo, LPWSTR lpPathBuf, + DWORD* pcchPathBuf ) +{ + FIXME("%s %s %08lx %08lx %p %p\n", debugstr_w(szAssemblyName), + debugstr_w(szAppContext), dwInstallMode, dwAssemblyInfo, lpPathBuf, + pcchPathBuf); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiProvideComponentFromDescriptorA( LPCSTR szDescriptor, + LPSTR szPath, DWORD *pcchPath, DWORD *pcchArgs ) +{ + FIXME("%s %p %p %p\n", debugstr_a(szDescriptor), szPath, pcchPath, pcchArgs ); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiProvideComponentFromDescriptorW( LPCWSTR szDescriptor, + LPWSTR szPath, DWORD *pcchPath, DWORD *pcchArgs ) +{ + FIXME("%s %p %p %p\n", debugstr_w(szDescriptor), szPath, pcchPath, pcchArgs ); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +HRESULT WINAPI MsiGetFileSignatureInformationA( LPCSTR szSignedObjectPath, + DWORD dwFlags, PCCERT_CONTEXT* ppcCertContext, BYTE* pbHashData, + DWORD* pcbHashData) +{ + FIXME("%s %08lx %p %p %p\n", debugstr_a(szSignedObjectPath), dwFlags, + ppcCertContext, pbHashData, pcbHashData); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +HRESULT WINAPI MsiGetFileSignatureInformationW( LPCWSTR szSignedObjectPath, + DWORD dwFlags, PCCERT_CONTEXT* ppcCertContext, BYTE* pbHashData, + DWORD* pcbHashData) +{ + FIXME("%s %08lx %p %p %p\n", debugstr_w(szSignedObjectPath), dwFlags, + ppcCertContext, pbHashData, pcbHashData); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiGetProductPropertyA( MSIHANDLE hProduct, LPCSTR szProperty, + LPSTR szValue, DWORD *pccbValue ) +{ + FIXME("%ld %s %p %p\n", hProduct, debugstr_a(szProperty), szValue, pccbValue); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiGetProductPropertyW( MSIHANDLE hProduct, LPCWSTR szProperty, + LPWSTR szValue, DWORD *pccbValue ) +{ + FIXME("%ld %s %p %p\n", hProduct, debugstr_w(szProperty), szValue, pccbValue); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiVerifyPackageA( LPCSTR szPackage ) +{ + UINT r; + LPWSTR szPack = NULL; + + TRACE("%s\n", debugstr_a(szPackage) ); + + if( szPackage ) + { + szPack = strdupAtoW( szPackage ); + if( !szPack ) + return ERROR_OUTOFMEMORY; + } + + r = MsiVerifyPackageW( szPack ); + + msi_free( szPack ); + + return r; +} + +UINT WINAPI MsiVerifyPackageW( LPCWSTR szPackage ) +{ + MSIHANDLE handle; + UINT r; + + TRACE("%s\n", debugstr_w(szPackage) ); + + r = MsiOpenDatabaseW( szPackage, MSIDBOPEN_READONLY, &handle ); + MsiCloseHandle( handle ); + + return r; +} + +INSTALLSTATE WINAPI MsiGetComponentPathA(LPCSTR szProduct, LPCSTR szComponent, + LPSTR lpPathBuf, DWORD* pcchBuf) +{ + LPWSTR szwProduct = NULL, szwComponent = NULL, lpwPathBuf= NULL; + INSTALLSTATE rc; + UINT incoming_len; + + if( szProduct ) + { + szwProduct = strdupAtoW( szProduct ); + if( !szwProduct) + return ERROR_OUTOFMEMORY; + } + + if( szComponent ) + { + szwComponent = strdupAtoW( szComponent ); + if( !szwComponent ) + { + msi_free( szwProduct); + return ERROR_OUTOFMEMORY; + } + } + + if( pcchBuf && *pcchBuf > 0 ) + { + lpwPathBuf = msi_alloc( *pcchBuf * sizeof(WCHAR)); + incoming_len = *pcchBuf; + } + else + { + lpwPathBuf = NULL; + incoming_len = 0; + } + + rc = MsiGetComponentPathW(szwProduct, szwComponent, lpwPathBuf, pcchBuf); + + msi_free( szwProduct); + msi_free( szwComponent); + if (lpwPathBuf) + { + if (rc != INSTALLSTATE_UNKNOWN) + WideCharToMultiByte(CP_ACP, 0, lpwPathBuf, incoming_len, + lpPathBuf, incoming_len, NULL, NULL); + msi_free( lpwPathBuf); + } + + return rc; +} + +INSTALLSTATE WINAPI MsiGetComponentPathW(LPCWSTR szProduct, LPCWSTR szComponent, + LPWSTR lpPathBuf, DWORD* pcchBuf) +{ + WCHAR squished_pc[GUID_SIZE]; + UINT rc; + INSTALLSTATE rrc = INSTALLSTATE_UNKNOWN; + HKEY hkey = 0; + LPWSTR path = NULL; + DWORD sz, type; + + TRACE("%s %s %p %p\n", debugstr_w(szProduct), + debugstr_w(szComponent), lpPathBuf, pcchBuf); + + if( !szComponent ) + return INSTALLSTATE_INVALIDARG; + if( lpPathBuf && !pcchBuf ) + return INSTALLSTATE_INVALIDARG; + + squash_guid(szProduct,squished_pc); + + rc = MSIREG_OpenProductsKey( szProduct, &hkey, FALSE); + if( rc != ERROR_SUCCESS ) + goto end; + + RegCloseKey(hkey); + + rc = MSIREG_OpenComponentsKey( szComponent, &hkey, FALSE); + if( rc != ERROR_SUCCESS ) + goto end; + + sz = 0; + type = 0; + rc = RegQueryValueExW( hkey, squished_pc, NULL, &type, NULL, &sz ); + if( rc != ERROR_SUCCESS ) + goto end; + if( type != REG_SZ ) + goto end; + + sz += sizeof(WCHAR); + path = msi_alloc( sz ); + if( !path ) + goto end; + + rc = RegQueryValueExW( hkey, squished_pc, NULL, NULL, (LPVOID) path, &sz ); + if( rc != ERROR_SUCCESS ) + goto end; + + TRACE("found path of (%s:%s)(%s)\n", debugstr_w(szComponent), + debugstr_w(szProduct), debugstr_w(path)); + + if (path[0]=='0') + { + FIXME("Registry entry.. check entry\n"); + rrc = INSTALLSTATE_LOCAL; + } + else + { + /* PROBABLY a file */ + if ( GetFileAttributesW(path) != INVALID_FILE_ATTRIBUTES ) + rrc = INSTALLSTATE_LOCAL; + else + rrc = INSTALLSTATE_ABSENT; + } + + if( pcchBuf ) + { + sz = sz / sizeof(WCHAR); + if( *pcchBuf >= sz ) + lstrcpyW( lpPathBuf, path ); + *pcchBuf = sz; + } + +end: + msi_free(path ); + RegCloseKey(hkey); + return rrc; +} + +/****************************************************************** + * MsiQueryFeatureStateA [MSI.@] + */ +INSTALLSTATE WINAPI MsiQueryFeatureStateA(LPCSTR szProduct, LPCSTR szFeature) +{ + LPWSTR szwProduct = NULL, szwFeature= NULL; + INSTALLSTATE rc = INSTALLSTATE_UNKNOWN; + + szwProduct = strdupAtoW( szProduct ); + if ( szProduct && !szwProduct ) + goto end; + + szwFeature = strdupAtoW( szFeature ); + if ( szFeature && !szwFeature ) + goto end; + + rc = MsiQueryFeatureStateW(szwProduct, szwFeature); + +end: + msi_free( szwProduct); + msi_free( szwFeature); + + return rc; +} + +/****************************************************************** + * MsiQueryFeatureStateW [MSI.@] + * + * This does not verify that the Feature is functional. So i am only going to + * check the existence of the key in the registry. This should tell me if it is + * installed. + */ +INSTALLSTATE WINAPI MsiQueryFeatureStateW(LPCWSTR szProduct, LPCWSTR szFeature) +{ + WCHAR squishProduct[GUID_SIZE]; + UINT rc; + DWORD sz = 0; + HKEY hkey; + + TRACE("%s %s\n", debugstr_w(szProduct), debugstr_w(szFeature)); + + if (!szProduct || !szFeature) + return INSTALLSTATE_INVALIDARG; + + if (!squash_guid( szProduct, squishProduct )) + return INSTALLSTATE_INVALIDARG; + + rc = MSIREG_OpenFeaturesKey(szProduct, &hkey, FALSE); + if (rc != ERROR_SUCCESS) + return INSTALLSTATE_UNKNOWN; + + rc = RegQueryValueExW( hkey, szFeature, NULL, NULL, NULL, &sz); + RegCloseKey(hkey); + + if (rc == ERROR_SUCCESS) + return INSTALLSTATE_LOCAL; + + return INSTALLSTATE_UNKNOWN; +} + +/****************************************************************** + * MsiGetFileVersionA [MSI.@] + */ +UINT WINAPI MsiGetFileVersionA(LPCSTR szFilePath, LPSTR lpVersionBuf, + DWORD* pcchVersionBuf, LPSTR lpLangBuf, DWORD* pcchLangBuf) +{ + LPWSTR szwFilePath = NULL, lpwVersionBuff = NULL, lpwLangBuff = NULL; + UINT ret = ERROR_OUTOFMEMORY; + + if( szFilePath ) + { + szwFilePath = strdupAtoW( szFilePath ); + if( !szwFilePath ) + goto end; + } + + if( lpVersionBuf && pcchVersionBuf && *pcchVersionBuf ) + { + lpwVersionBuff = msi_alloc(*pcchVersionBuf*sizeof(WCHAR)); + if( !lpwVersionBuff ) + goto end; + } + + if( lpLangBuf && pcchLangBuf && *pcchLangBuf ) + { + lpwLangBuff = msi_alloc(*pcchVersionBuf*sizeof(WCHAR)); + if( !lpwLangBuff ) + goto end; + } + + ret = MsiGetFileVersionW(szwFilePath, lpwVersionBuff, pcchVersionBuf, + lpwLangBuff, pcchLangBuf); + + if( lpwVersionBuff ) + WideCharToMultiByte(CP_ACP, 0, lpwVersionBuff, -1, + lpVersionBuf, *pcchVersionBuf, NULL, NULL); + if( lpwLangBuff ) + WideCharToMultiByte(CP_ACP, 0, lpwLangBuff, -1, + lpLangBuf, *pcchLangBuf, NULL, NULL); + +end: + msi_free(szwFilePath); + msi_free(lpwVersionBuff); + msi_free(lpwLangBuff); + + return ret; +} + +/****************************************************************** + * MsiGetFileVersionW [MSI.@] + */ +UINT WINAPI MsiGetFileVersionW(LPCWSTR szFilePath, LPWSTR lpVersionBuf, + DWORD* pcchVersionBuf, LPWSTR lpLangBuf, DWORD* pcchLangBuf) +{ + static WCHAR szVersionResource[] = {'\\',0}; + static const WCHAR szVersionFormat[] = { + '%','d','.','%','d','.','%','d','.','%','d',0}; + static const WCHAR szLangFormat[] = {'%','d',0}; + UINT ret = 0; + DWORD dwVerLen; + LPVOID lpVer = NULL; + VS_FIXEDFILEINFO *ffi; + UINT puLen; + WCHAR tmp[32]; + + TRACE("%s %p %ld %p %ld\n", debugstr_w(szFilePath), + lpVersionBuf, pcchVersionBuf?*pcchVersionBuf:0, + lpLangBuf, pcchLangBuf?*pcchLangBuf:0); + + dwVerLen = GetFileVersionInfoSizeW(szFilePath, NULL); + if( !dwVerLen ) + return GetLastError(); + + lpVer = msi_alloc(dwVerLen); + if( !lpVer ) + { + ret = ERROR_OUTOFMEMORY; + goto end; + } + + if( !GetFileVersionInfoW(szFilePath, 0, dwVerLen, lpVer) ) + { + ret = GetLastError(); + goto end; + } + if( lpVersionBuf && pcchVersionBuf && *pcchVersionBuf ) + { + if( VerQueryValueW(lpVer, szVersionResource, (LPVOID*)&ffi, &puLen) && + (puLen > 0) ) + { + wsprintfW(tmp, szVersionFormat, + HIWORD(ffi->dwFileVersionMS), LOWORD(ffi->dwFileVersionMS), + HIWORD(ffi->dwFileVersionLS), LOWORD(ffi->dwFileVersionLS)); + lstrcpynW(lpVersionBuf, tmp, *pcchVersionBuf); + *pcchVersionBuf = lstrlenW(lpVersionBuf); + } + else + { + *lpVersionBuf = 0; + *pcchVersionBuf = 0; + } + } + + if( lpLangBuf && pcchLangBuf && *pcchLangBuf ) + { + DWORD lang = GetUserDefaultLangID(); + + FIXME("Retrieve language from file\n"); + wsprintfW(tmp, szLangFormat, lang); + lstrcpynW(lpLangBuf, tmp, *pcchLangBuf); + *pcchLangBuf = lstrlenW(lpLangBuf); + } + +end: + msi_free(lpVer); + return ret; +} + + +/****************************************************************** + * DllMain + */ +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + switch(fdwReason) + { + case DLL_PROCESS_ATTACH: + msi_hInstance = hinstDLL; + DisableThreadLibraryCalls(hinstDLL); + msi_dialog_register_class(); + break; + case DLL_PROCESS_DETACH: + msi_dialog_unregister_class(); + /* FIXME: Cleanup */ + break; + } + return TRUE; +} + +typedef struct tagIClassFactoryImpl +{ + const IClassFactoryVtbl *lpVtbl; +} IClassFactoryImpl; + +static HRESULT WINAPI MsiCF_QueryInterface(LPCLASSFACTORY iface, + REFIID riid,LPVOID *ppobj) +{ + IClassFactoryImpl *This = (IClassFactoryImpl *)iface; + FIXME("%p %s %p\n",This,debugstr_guid(riid),ppobj); + return E_NOINTERFACE; +} + +static ULONG WINAPI MsiCF_AddRef(LPCLASSFACTORY iface) +{ + LockModule(); + return 2; +} + +static ULONG WINAPI MsiCF_Release(LPCLASSFACTORY iface) +{ + UnlockModule(); + return 1; +} + +static HRESULT WINAPI MsiCF_CreateInstance(LPCLASSFACTORY iface, + LPUNKNOWN pOuter, REFIID riid, LPVOID *ppobj) +{ + IClassFactoryImpl *This = (IClassFactoryImpl *)iface; + + FIXME("%p %p %s %p\n", This, pOuter, debugstr_guid(riid), ppobj); + return E_FAIL; +} + +static HRESULT WINAPI MsiCF_LockServer(LPCLASSFACTORY iface, BOOL dolock) +{ + TRACE("(%p)->(%d)\n", iface, dolock); + + if(dolock) + LockModule(); + else + UnlockModule(); + + return S_OK; +} + +static const IClassFactoryVtbl MsiCF_Vtbl = +{ + MsiCF_QueryInterface, + MsiCF_AddRef, + MsiCF_Release, + MsiCF_CreateInstance, + MsiCF_LockServer +}; + +static IClassFactoryImpl Msi_CF = { &MsiCF_Vtbl }; + +/****************************************************************** + * DllGetClassObject [MSI.@] + */ +HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID *ppv) +{ + TRACE("%s %s %p\n", debugstr_guid(rclsid), debugstr_guid(riid), ppv); + + if( IsEqualCLSID (rclsid, &CLSID_IMsiServer) || + IsEqualCLSID (rclsid, &CLSID_IMsiServerMessage) || + IsEqualCLSID (rclsid, &CLSID_IMsiServerX1) || + IsEqualCLSID (rclsid, &CLSID_IMsiServerX2) || + IsEqualCLSID (rclsid, &CLSID_IMsiServerX3) ) + { + *ppv = (LPVOID) &Msi_CF; + return S_OK; + } + return CLASS_E_CLASSNOTAVAILABLE; +} + +/****************************************************************** + * DllGetVersion [MSI.@] + */ +HRESULT WINAPI DllGetVersion(DLLVERSIONINFO *pdvi) +{ + TRACE("%p\n",pdvi); + + if (pdvi->cbSize != sizeof(DLLVERSIONINFO)) + return E_INVALIDARG; + + pdvi->dwMajorVersion = MSI_MAJORVERSION; + pdvi->dwMinorVersion = MSI_MINORVERSION; + pdvi->dwBuildNumber = MSI_BUILDNUMBER; + pdvi->dwPlatformID = 1; + + return S_OK; +} + +/****************************************************************** + * DllCanUnloadNow [MSI.@] + */ +HRESULT WINAPI DllCanUnloadNow(void) +{ + return dll_count == 0 ? S_OK : S_FALSE; +} + +/*********************************************************************** + * MsiGetFeatureUsageW [MSI.@] + */ +UINT WINAPI MsiGetFeatureUsageW( LPCWSTR szProduct, LPCWSTR szFeature, + DWORD* pdwUseCount, WORD* pwDateUsed ) +{ + FIXME("%s %s %p %p\n",debugstr_w(szProduct), debugstr_w(szFeature), + pdwUseCount, pwDateUsed); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +/*********************************************************************** + * MsiGetFeatureUsageA [MSI.@] + */ +UINT WINAPI MsiGetFeatureUsageA( LPCSTR szProduct, LPCSTR szFeature, + DWORD* pdwUseCount, WORD* pwDateUsed ) +{ + LPWSTR prod = NULL, feat = NULL; + UINT ret = ERROR_OUTOFMEMORY; + + TRACE("%s %s %p %p\n", debugstr_a(szProduct), debugstr_a(szFeature), + pdwUseCount, pwDateUsed); + + prod = strdupAtoW( szProduct ); + if (szProduct && !prod) + goto end; + + feat = strdupAtoW( szFeature ); + if (szFeature && !feat) + goto end; + + ret = MsiGetFeatureUsageW( prod, feat, pdwUseCount, pwDateUsed ); + +end: + msi_free( prod ); + msi_free( feat ); + + return ret; +} + +/*********************************************************************** + * MsiUseFeatureExW [MSI.@] + */ +INSTALLSTATE WINAPI MsiUseFeatureExW( LPCWSTR szProduct, LPCWSTR szFeature, + DWORD dwInstallMode, DWORD dwReserved ) +{ + INSTALLSTATE state; + + TRACE("%s %s %li %li\n", debugstr_w(szProduct), debugstr_w(szFeature), + dwInstallMode, dwReserved); + + state = MsiQueryFeatureStateW( szProduct, szFeature ); + + if (dwReserved) + return INSTALLSTATE_INVALIDARG; + + if (state == INSTALLSTATE_LOCAL && dwInstallMode != INSTALLMODE_NODETECTION) + { + FIXME("mark product %s feature %s as used\n", + debugstr_w(szProduct), debugstr_w(szFeature) ); + } + + return state; +} + +/*********************************************************************** + * MsiUseFeatureExA [MSI.@] + */ +INSTALLSTATE WINAPI MsiUseFeatureExA( LPCSTR szProduct, LPCSTR szFeature, + DWORD dwInstallMode, DWORD dwReserved ) +{ + INSTALLSTATE ret = INSTALLSTATE_UNKNOWN; + LPWSTR prod = NULL, feat = NULL; + + TRACE("%s %s %li %li\n", debugstr_a(szProduct), debugstr_a(szFeature), + dwInstallMode, dwReserved); + + prod = strdupAtoW( szProduct ); + if (szProduct && !prod) + goto end; + + feat = strdupAtoW( szFeature ); + if (szFeature && !feat) + goto end; + + ret = MsiUseFeatureExW( prod, feat, dwInstallMode, dwReserved ); + +end: + msi_free( prod ); + msi_free( feat ); + + return ret; +} + +INSTALLSTATE WINAPI MsiUseFeatureW( LPCWSTR szProduct, LPCWSTR szFeature ) +{ + FIXME("%s %s\n", debugstr_w(szProduct), debugstr_w(szFeature)); + + return INSTALLSTATE_LOCAL; +} + +INSTALLSTATE WINAPI MsiUseFeatureA( LPCSTR szProduct, LPCSTR szFeature ) +{ + INSTALLSTATE ret = INSTALLSTATE_UNKNOWN; + LPWSTR prod = NULL, feat = NULL; + + TRACE("%s %s\n", debugstr_a(szProduct), debugstr_a(szFeature) ); + + prod = strdupAtoW( szProduct ); + if (szProduct && !prod) + goto end; + + feat = strdupAtoW( szFeature ); + if (szFeature && !feat) + goto end; + + ret = MsiUseFeatureW( prod, feat ); + +end: + msi_free( prod ); + msi_free( feat ); + + return ret; +} + +/*********************************************************************** + * MsiProvideQualifiedComponentExW [MSI.@] + */ +UINT WINAPI MsiProvideQualifiedComponentExW(LPCWSTR szComponent, + LPCWSTR szQualifier, DWORD dwInstallMode, LPWSTR szProduct, + DWORD Unused1, DWORD Unused2, LPWSTR lpPathBuf, + DWORD* pcchPathBuf) +{ + HKEY hkey; + UINT rc; + LPWSTR info; + DWORD sz; + WCHAR product[MAX_FEATURE_CHARS+1]; + WCHAR component[MAX_FEATURE_CHARS+1]; + WCHAR feature[MAX_FEATURE_CHARS+1]; + + TRACE("%s %s %li %s %li %li %p %p\n", debugstr_w(szComponent), + debugstr_w(szQualifier), dwInstallMode, debugstr_w(szProduct), + Unused1, Unused2, lpPathBuf, pcchPathBuf); + + rc = MSIREG_OpenUserComponentsKey(szComponent, &hkey, FALSE); + if (rc != ERROR_SUCCESS) + return ERROR_INDEX_ABSENT; + + sz = 0; + rc = RegQueryValueExW( hkey, szQualifier, NULL, NULL, NULL, &sz); + if (sz <= 0) + { + RegCloseKey(hkey); + return ERROR_INDEX_ABSENT; + } + + info = msi_alloc(sz); + rc = RegQueryValueExW( hkey, szQualifier, NULL, NULL, (LPBYTE)info, &sz); + if (rc != ERROR_SUCCESS) + { + RegCloseKey(hkey); + msi_free(info); + return ERROR_INDEX_ABSENT; + } + + MsiDecomposeDescriptorW(info, product, feature, component, &sz); + + if (!szProduct) + rc = MsiGetComponentPathW(product, component, lpPathBuf, pcchPathBuf); + else + rc = MsiGetComponentPathW(szProduct, component, lpPathBuf, pcchPathBuf); + + RegCloseKey(hkey); + msi_free(info); + + if (rc == INSTALLSTATE_LOCAL) + return ERROR_SUCCESS; + else + return ERROR_FILE_NOT_FOUND; +} + +/*********************************************************************** + * MsiProvideQualifiedComponentW [MSI.@] + */ +UINT WINAPI MsiProvideQualifiedComponentW( LPCWSTR szComponent, + LPCWSTR szQualifier, DWORD dwInstallMode, LPWSTR lpPathBuf, + DWORD* pcchPathBuf) +{ + return MsiProvideQualifiedComponentExW(szComponent, szQualifier, + dwInstallMode, NULL, 0, 0, lpPathBuf, pcchPathBuf); +} + +/*********************************************************************** + * MsiProvideQualifiedComponentA [MSI.@] + */ +UINT WINAPI MsiProvideQualifiedComponentA( LPCSTR szComponent, + LPCSTR szQualifier, DWORD dwInstallMode, LPSTR lpPathBuf, + DWORD* pcchPathBuf) +{ + LPWSTR szwComponent, szwQualifier, lpwPathBuf; + DWORD pcchwPathBuf; + UINT rc; + + TRACE("%s %s %li %p %p\n",szComponent, szQualifier, + dwInstallMode, lpPathBuf, pcchPathBuf); + + szwComponent= strdupAtoW( szComponent); + szwQualifier= strdupAtoW( szQualifier); + + lpwPathBuf = msi_alloc(*pcchPathBuf * sizeof(WCHAR)); + + pcchwPathBuf = *pcchPathBuf; + + rc = MsiProvideQualifiedComponentW(szwComponent, szwQualifier, + dwInstallMode, lpwPathBuf, &pcchwPathBuf); + + msi_free(szwComponent); + msi_free(szwQualifier); + *pcchPathBuf = WideCharToMultiByte(CP_ACP, 0, lpwPathBuf, pcchwPathBuf, + lpPathBuf, *pcchPathBuf, NULL, NULL); + + msi_free(lpwPathBuf); + return rc; +} + +USERINFOSTATE WINAPI MsiGetUserInfoW(LPCWSTR szProduct, LPWSTR lpUserNameBuf, + DWORD* pcchUserNameBuf, LPWSTR lpOrgNameBuf, + DWORD* pcchOrgNameBuf, LPWSTR lpSerialBuf, DWORD* pcchSerialBuf) +{ + HKEY hkey; + DWORD sz; + UINT rc = ERROR_SUCCESS,rc2 = ERROR_SUCCESS; + + TRACE("%s %p %p %p %p %p %p\n",debugstr_w(szProduct), lpUserNameBuf, + pcchUserNameBuf, lpOrgNameBuf, pcchOrgNameBuf, lpSerialBuf, + pcchSerialBuf); + + rc = MSIREG_OpenUninstallKey(szProduct, &hkey, FALSE); + if (rc != ERROR_SUCCESS) + return USERINFOSTATE_UNKNOWN; + + if (lpUserNameBuf) + { + sz = *lpUserNameBuf * sizeof(WCHAR); + rc = RegQueryValueExW( hkey, INSTALLPROPERTY_REGOWNERW, NULL, + NULL, (LPBYTE)lpUserNameBuf, + &sz); + } + if (!lpUserNameBuf && pcchUserNameBuf) + { + sz = 0; + rc = RegQueryValueExW( hkey, INSTALLPROPERTY_REGOWNERW, NULL, + NULL, NULL, &sz); + } + + if (pcchUserNameBuf) + *pcchUserNameBuf = sz / sizeof(WCHAR); + + if (lpOrgNameBuf) + { + sz = *pcchOrgNameBuf * sizeof(WCHAR); + rc2 = RegQueryValueExW( hkey, INSTALLPROPERTY_REGCOMPANYW, NULL, + NULL, (LPBYTE)lpOrgNameBuf, &sz); + } + if (!lpOrgNameBuf && pcchOrgNameBuf) + { + sz = 0; + rc2 = RegQueryValueExW( hkey, INSTALLPROPERTY_REGCOMPANYW, NULL, + NULL, NULL, &sz); + } + + if (pcchOrgNameBuf) + *pcchOrgNameBuf = sz / sizeof(WCHAR); + + if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA && + rc2 != ERROR_SUCCESS && rc2 != ERROR_MORE_DATA) + { + RegCloseKey(hkey); + return USERINFOSTATE_ABSENT; + } + + if (lpSerialBuf) + { + sz = *pcchSerialBuf * sizeof(WCHAR); + RegQueryValueExW( hkey, INSTALLPROPERTY_PRODUCTIDW, NULL, NULL, + (LPBYTE)lpSerialBuf, &sz); + } + if (!lpSerialBuf && pcchSerialBuf) + { + sz = 0; + rc = RegQueryValueExW( hkey, INSTALLPROPERTY_PRODUCTIDW, NULL, + NULL, NULL, &sz); + } + if (pcchSerialBuf) + *pcchSerialBuf = sz / sizeof(WCHAR); + + RegCloseKey(hkey); + return USERINFOSTATE_PRESENT; +} + +USERINFOSTATE WINAPI MsiGetUserInfoA(LPCSTR szProduct, LPSTR lpUserNameBuf, + DWORD* pcchUserNameBuf, LPSTR lpOrgNameBuf, + DWORD* pcchOrgNameBuf, LPSTR lpSerialBuf, DWORD* pcchSerialBuf) +{ + FIXME("%s %p %p %p %p %p %p\n",debugstr_a(szProduct), lpUserNameBuf, + pcchUserNameBuf, lpOrgNameBuf, pcchOrgNameBuf, lpSerialBuf, + pcchSerialBuf); + + return USERINFOSTATE_UNKNOWN; +} + +UINT WINAPI MsiCollectUserInfoW(LPCWSTR szProduct) +{ + MSIHANDLE handle; + UINT rc; + MSIPACKAGE *package; + static const WCHAR szFirstRun[] = {'F','i','r','s','t','R','u','n',0}; + + TRACE("(%s)\n",debugstr_w(szProduct)); + + rc = MsiOpenProductW(szProduct,&handle); + if (rc != ERROR_SUCCESS) + return ERROR_INVALID_PARAMETER; + + package = msihandle2msiinfo(handle, MSIHANDLETYPE_PACKAGE); + rc = ACTION_PerformUIAction(package, szFirstRun); + msiobj_release( &package->hdr ); + + MsiCloseHandle(handle); + + return rc; +} + +UINT WINAPI MsiCollectUserInfoA(LPCSTR szProduct) +{ + MSIHANDLE handle; + UINT rc; + MSIPACKAGE *package; + static const WCHAR szFirstRun[] = {'F','i','r','s','t','R','u','n',0}; + + TRACE("(%s)\n",debugstr_a(szProduct)); + + rc = MsiOpenProductA(szProduct,&handle); + if (rc != ERROR_SUCCESS) + return ERROR_INVALID_PARAMETER; + + package = msihandle2msiinfo(handle, MSIHANDLETYPE_PACKAGE); + rc = ACTION_PerformUIAction(package, szFirstRun); + msiobj_release( &package->hdr ); + + MsiCloseHandle(handle); + + return rc; +} + +/*********************************************************************** + * MsiConfigureFeatureA [MSI.@] + */ +UINT WINAPI MsiConfigureFeatureA(LPCSTR szProduct, LPCSTR szFeature, INSTALLSTATE eInstallState) +{ + FIXME("%s %s %i\n", debugstr_a(szProduct), debugstr_a(szFeature), eInstallState); + return ERROR_SUCCESS; +} + +/*********************************************************************** + * MsiConfigureFeatureW [MSI.@] + */ +UINT WINAPI MsiConfigureFeatureW(LPCWSTR szProduct, LPCWSTR szFeature, INSTALLSTATE eInstallState) +{ + FIXME("%s %s %i\n", debugstr_w(szProduct), debugstr_w(szFeature), eInstallState); + return ERROR_SUCCESS; +} + +UINT WINAPI MsiCreateAndVerifyInstallerDirectory(DWORD dwReserved) +{ + WCHAR path[MAX_PATH]; + + if(dwReserved) { + FIXME("Don't know how to handle argument %ld\n", dwReserved); + return ERROR_CALL_NOT_IMPLEMENTED; + } + + if(!GetWindowsDirectoryW(path, MAX_PATH)) { + FIXME("GetWindowsDirectory failed unexpected! Error %ld\n", + GetLastError()); + return ERROR_CALL_NOT_IMPLEMENTED; + } + + strcatW(path, installerW); + + CreateDirectoryW(path, NULL); + + return 0; +} + +/*********************************************************************** + * MsiGetShortcutTargetA [MSI.@] + */ +UINT WINAPI MsiGetShortcutTargetA( LPCSTR szShortcutTarget, + LPSTR szProductCode, LPSTR szFeatureId, + LPSTR szComponentCode ) +{ + LPWSTR target; + const int len = MAX_FEATURE_CHARS+1; + WCHAR product[MAX_FEATURE_CHARS+1], feature[MAX_FEATURE_CHARS+1], component[MAX_FEATURE_CHARS+1]; + UINT r; + + target = strdupAtoW( szShortcutTarget ); + if (szShortcutTarget && !target ) + return ERROR_OUTOFMEMORY; + product[0] = 0; + feature[0] = 0; + component[0] = 0; + r = MsiGetShortcutTargetW( target, product, feature, component ); + msi_free( target ); + if (r == ERROR_SUCCESS) + { + WideCharToMultiByte( CP_ACP, 0, product, -1, szProductCode, len, NULL, NULL ); + WideCharToMultiByte( CP_ACP, 0, feature, -1, szFeatureId, len, NULL, NULL ); + WideCharToMultiByte( CP_ACP, 0, component, -1, szComponentCode, len, NULL, NULL ); + } + return r; +} + +/*********************************************************************** + * MsiGetShortcutTargetW [MSI.@] + */ +UINT WINAPI MsiGetShortcutTargetW( LPCWSTR szShortcutTarget, + LPWSTR szProductCode, LPWSTR szFeatureId, + LPWSTR szComponentCode ) +{ + IShellLinkDataList *dl = NULL; + IPersistFile *pf = NULL; + LPEXP_DARWIN_LINK darwin = NULL; + HRESULT r, init; + + TRACE("%s %p %p %p\n", debugstr_w(szShortcutTarget), + szProductCode, szFeatureId, szComponentCode ); + + init = CoInitialize(NULL); + + r = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, + &IID_IPersistFile, (LPVOID*) &pf ); + if( SUCCEEDED( r ) ) + { + r = IPersistFile_Load( pf, szShortcutTarget, + STGM_READ | STGM_SHARE_DENY_WRITE ); + if( SUCCEEDED( r ) ) + { + r = IPersistFile_QueryInterface( pf, &IID_IShellLinkDataList, + (LPVOID*) &dl ); + if( SUCCEEDED( r ) ) + { + IShellLinkDataList_CopyDataBlock( dl, EXP_DARWIN_ID_SIG, + (LPVOID) &darwin ); + IShellLinkDataList_Release( dl ); + } + } + IPersistFile_Release( pf ); + } + + if (SUCCEEDED(init)) + CoUninitialize(); + + TRACE("darwin = %p\n", darwin); + + if (darwin) + { + DWORD sz; + UINT ret; + + ret = MsiDecomposeDescriptorW( darwin->szwDarwinID, + szProductCode, szFeatureId, szComponentCode, &sz ); + LocalFree( darwin ); + return ret; + } + + return ERROR_FUNCTION_FAILED; +} + +UINT WINAPI MsiReinstallFeatureW( LPCWSTR szProduct, LPCWSTR szFeature, + DWORD dwReinstallMode ) +{ + MSIPACKAGE* package = NULL; + UINT r; + DWORD sz; + WCHAR sourcepath[MAX_PATH]; + WCHAR filename[MAX_PATH]; + static const WCHAR szInstalled[] = { + ' ','L','O','G','V','E','R','B','O','S','E','=','1',' ','I','n','s','t','a','l','l','e','d','=','1',0}; + static const WCHAR fmt[] = {'R','E','I','N','S','T','A','L','L','=','%','s',0}; + static const WCHAR REINSTALLMODE[] = {'R','E','I','N','S','T','A','L','L','M','O','D','E',0}; + WCHAR reinstallmode[11]; + LPWSTR ptr; + LPWSTR commandline; + + FIXME("%s %s %li\n", debugstr_w(szProduct), debugstr_w(szFeature), + dwReinstallMode); + + memset(reinstallmode,0,sizeof(reinstallmode)); + ptr = reinstallmode; + + if (dwReinstallMode & REINSTALLMODE_FILEMISSING) + { *ptr = 'p'; ptr++; } + if (dwReinstallMode & REINSTALLMODE_FILEOLDERVERSION) + { *ptr = 'o'; ptr++; } + if (dwReinstallMode & REINSTALLMODE_FILEEQUALVERSION) + { *ptr = 'w'; ptr++; } + if (dwReinstallMode & REINSTALLMODE_FILEEXACT) + { *ptr = 'd'; ptr++; } + if (dwReinstallMode & REINSTALLMODE_FILEVERIFY) + { *ptr = 'c'; ptr++; } + if (dwReinstallMode & REINSTALLMODE_FILEREPLACE) + { *ptr = 'a'; ptr++; } + if (dwReinstallMode & REINSTALLMODE_USERDATA) + { *ptr = 'u'; ptr++; } + if (dwReinstallMode & REINSTALLMODE_MACHINEDATA) + { *ptr = 'm'; ptr++; } + if (dwReinstallMode & REINSTALLMODE_SHORTCUT) + { *ptr = 's'; ptr++; } + if (dwReinstallMode & REINSTALLMODE_PACKAGE) + { *ptr = 'v'; ptr++; } + + sz = sizeof(sourcepath); + MsiSourceListGetInfoW(szProduct, NULL, MSIINSTALLCONTEXT_USERMANAGED, + MSICODE_PRODUCT, INSTALLPROPERTY_LASTUSEDSOURCEW, sourcepath, + &sz); + + sz = sizeof(filename); + MsiSourceListGetInfoW(szProduct, NULL, MSIINSTALLCONTEXT_USERMANAGED, + MSICODE_PRODUCT, INSTALLPROPERTY_PACKAGENAMEW, filename, &sz); + + strcatW(sourcepath,filename); + + if (dwReinstallMode & REINSTALLMODE_PACKAGE) + r = MSI_OpenPackageW( sourcepath, &package ); + else + r = MSI_OpenProductW( szProduct, &package ); + + if (r != ERROR_SUCCESS) + return r; + + MSI_SetPropertyW(package,REINSTALLMODE,reinstallmode); + + sz = lstrlenW(szInstalled); + sz += lstrlenW(fmt); + sz += lstrlenW(szFeature); + + commandline = msi_alloc(sz * sizeof(WCHAR)); + + sprintfW(commandline,fmt,szFeature); + lstrcatW(commandline,szInstalled); + + r = MSI_InstallPackage( package, sourcepath, commandline ); + + msiobj_release( &package->hdr ); + + msi_free(commandline); + + return r; +} + +UINT WINAPI MsiReinstallFeatureA( LPCSTR szProduct, LPCSTR szFeature, + DWORD dwReinstallMode ) +{ + LPWSTR wszProduct; + LPWSTR wszFeature; + UINT rc; + + TRACE("%s %s %li\n", debugstr_a(szProduct), debugstr_a(szFeature), + dwReinstallMode); + + wszProduct = strdupAtoW(szProduct); + wszFeature = strdupAtoW(szFeature); + + rc = MsiReinstallFeatureW(wszProduct, wszFeature, dwReinstallMode); + + msi_free(wszProduct); + msi_free(wszFeature); + return rc; +} + +/*********************************************************************** + * MsiEnumPatchesA [MSI.@] + */ +UINT WINAPI MsiEnumPatchesA( LPCSTR szProduct, DWORD iPatchIndex, + LPSTR lpPatchBuf, LPSTR lpTransformsBuf, DWORD* pcchTransformsBuf) +{ + FIXME("%s %ld %p %p %p\n", debugstr_a(szProduct), + iPatchIndex, lpPatchBuf, lpTransformsBuf, pcchTransformsBuf); + return ERROR_NO_MORE_ITEMS; +} + +/*********************************************************************** + * MsiEnumPatchesW [MSI.@] + */ +UINT WINAPI MsiEnumPatchesW( LPCWSTR szProduct, DWORD iPatchIndex, + LPWSTR lpPatchBuf, LPWSTR lpTransformsBuf, DWORD* pcchTransformsBuf) +{ + FIXME("%s %ld %p %p %p\n", debugstr_w(szProduct), + iPatchIndex, lpPatchBuf, lpTransformsBuf, pcchTransformsBuf); + return ERROR_NO_MORE_ITEMS; +} + +/*********************************************************************** + * MsiGetFileHashW [MSI.@] + */ +UINT WINAPI MsiGetFileHashW( LPCWSTR szFilePath, DWORD dwOptions, + PMSIFILEHASHINFO pHash ) +{ + FIXME("%s %08lx %p\n", debugstr_w(szFilePath), dwOptions, pHash ); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +/*********************************************************************** + * MsiGetFileHashA [MSI.@] + */ +UINT WINAPI MsiGetFileHashA( LPCSTR szFilePath, DWORD dwOptions, + PMSIFILEHASHINFO pHash ) +{ + FIXME("%s %08lx %p\n", debugstr_a(szFilePath), dwOptions, pHash ); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +/*********************************************************************** + * MsiAdvertiseScriptW [MSI.@] + */ +UINT WINAPI MsiAdvertiseScriptW( LPCWSTR szScriptFile, DWORD dwFlags, + PHKEY phRegData, BOOL fRemoveItems ) +{ + FIXME("%s %08lx %p %d\n", + debugstr_w( szScriptFile ), dwFlags, phRegData, fRemoveItems ); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +/*********************************************************************** + * MsiAdvertiseScriptA [MSI.@] + */ +UINT WINAPI MsiAdvertiseScriptA( LPCSTR szScriptFile, DWORD dwFlags, + PHKEY phRegData, BOOL fRemoveItems ) +{ + FIXME("%s %08lx %p %d\n", + debugstr_a( szScriptFile ), dwFlags, phRegData, fRemoveItems ); + return ERROR_CALL_NOT_IMPLEMENTED; +} diff --git a/reactos/dll/msi/msi.rc b/reactos/dll/msi/msi.rc new file mode 100644 index 00000000000..7f57ef1568c --- /dev/null +++ b/reactos/dll/msi/msi.rc @@ -0,0 +1,42 @@ +/* + * Resources for MSI + * + * Copyright 2005 Mike McCormack + * + * 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 "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "winnls.h" + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL + +#include "version.rc" + +#include "msi_Bg.rc" +#include "msi_De.rc" +#include "msi_En.rc" +#include "msi_Ja.rc" +#include "msi_Es.rc" +#include "msi_Fi.rc" +#include "msi_Fr.rc" +#include "msi_Ko.rc" +#include "msi_Nl.rc" +#include "msi_No.rc" +#include "msi_Pt.rc" +#include "msi_Ru.rc" +#include "msi_Hu.rc" diff --git a/reactos/dll/msi/msi.spec b/reactos/dll/msi/msi.spec new file mode 100644 index 00000000000..802adefa6be --- /dev/null +++ b/reactos/dll/msi/msi.spec @@ -0,0 +1,237 @@ +5 stdcall MsiAdvertiseProductA(str str str long) +6 stdcall MsiAdvertiseProductW(wstr wstr wstr long) +7 stdcall MsiCloseAllHandles() +8 stdcall MsiCloseHandle(long) +9 stdcall MsiCollectUserInfoA(str) +10 stdcall MsiCollectUserInfoW(wstr) +11 stdcall MsiConfigureFeatureA(str str long) +12 stub MsiConfigureFeatureFromDescriptorA +13 stub MsiConfigureFeatureFromDescriptorW +14 stdcall MsiConfigureFeatureW(wstr wstr ptr) +15 stdcall MsiConfigureProductA(str long long) +16 stdcall MsiConfigureProductW(wstr long long) +17 stdcall MsiCreateRecord(long) +18 stdcall MsiDatabaseApplyTransformA(long str long) +19 stdcall MsiDatabaseApplyTransformW(long wstr long) +20 stdcall MsiDatabaseCommit(long) +21 stdcall MsiDatabaseExportA(long str str str) +22 stdcall MsiDatabaseExportW(long wstr wstr wstr) +23 stdcall MsiDatabaseGenerateTransformA(long long str long long) +24 stdcall MsiDatabaseGenerateTransformW(long long wstr long long) +25 stdcall MsiDatabaseGetPrimaryKeysA(long str ptr) +26 stdcall MsiDatabaseGetPrimaryKeysW(long wstr ptr) +27 stdcall MsiDatabaseImportA(str str long) +28 stdcall MsiDatabaseImportW(wstr wstr long) +29 stub MsiDatabaseMergeA +30 stub MsiDatabaseMergeW +31 stdcall MsiDatabaseOpenViewA(long str ptr) +32 stdcall MsiDatabaseOpenViewW(long wstr ptr) +33 stdcall MsiDoActionA(long str) +34 stdcall MsiDoActionW(long wstr) +35 stdcall MsiEnableUIPreview(long ptr) +36 stdcall MsiEnumClientsA(str long ptr) +37 stdcall MsiEnumClientsW(wstr long ptr) +38 stdcall MsiEnumComponentQualifiersA(str long ptr ptr ptr ptr) +39 stdcall MsiEnumComponentQualifiersW(wstr long ptr ptr ptr ptr) +40 stdcall MsiEnumComponentsA(long ptr) +41 stdcall MsiEnumComponentsW(long ptr) +42 stdcall MsiEnumFeaturesA(str long ptr ptr) +43 stdcall MsiEnumFeaturesW(wstr long ptr ptr) +44 stdcall MsiEnumProductsA(long ptr) +45 stdcall MsiEnumProductsW(long ptr) +46 stdcall MsiEvaluateConditionA(long str) +47 stdcall MsiEvaluateConditionW(long wstr) +48 stdcall MsiGetLastErrorRecord() +49 stdcall MsiGetActiveDatabase(long) +50 stdcall MsiGetComponentStateA(long str ptr ptr) +51 stdcall MsiGetComponentStateW(long wstr ptr ptr) +52 stub MsiGetDatabaseState +53 stub MsiGetFeatureCostA +54 stub MsiGetFeatureCostW +55 stub MsiGetFeatureInfoA +56 stub MsiGetFeatureInfoW +57 stdcall MsiGetFeatureStateA(long str ptr ptr) +58 stdcall MsiGetFeatureStateW(long wstr ptr ptr) +59 stdcall MsiGetFeatureUsageA(str str ptr ptr) +60 stdcall MsiGetFeatureUsageW(wstr wstr ptr ptr) +61 stub MsiGetFeatureValidStatesA +62 stub MsiGetFeatureValidStatesW +63 stdcall MsiGetLanguage(long) +64 stdcall MsiGetMode(long long) +65 stdcall MsiGetProductCodeA(str str) +66 stdcall MsiGetProductCodeW(wstr wstr) +67 stdcall MsiGetProductInfoA(str str str long) +68 stub MsiGetProductInfoFromScriptA +69 stub MsiGetProductInfoFromScriptW +70 stdcall MsiGetProductInfoW(wstr wstr wstr long) +71 stdcall MsiGetProductPropertyA(long str ptr ptr) +72 stdcall MsiGetProductPropertyW(long wstr ptr ptr) +73 stdcall MsiGetPropertyA(ptr str ptr ptr) +74 stdcall MsiGetPropertyW(ptr wstr ptr ptr) +75 stdcall MsiGetSourcePathA(long str ptr ptr) +76 stdcall MsiGetSourcePathW(long wstr ptr ptr) +77 stdcall MsiGetSummaryInformationA(long str long ptr) +78 stdcall MsiGetSummaryInformationW(long wstr long ptr) +79 stdcall MsiGetTargetPathA(long str ptr ptr) +80 stdcall MsiGetTargetPathW(long wstr ptr ptr) +81 stdcall MsiGetUserInfoA(str ptr ptr ptr ptr ptr ptr) +82 stdcall MsiGetUserInfoW(wstr ptr ptr ptr ptr ptr ptr) +83 stub MsiInstallMissingComponentA +84 stub MsiInstallMissingComponentW +85 stub MsiInstallMissingFileA +86 stub MsiInstallMissingFileW +87 stdcall MsiInstallProductA(str str) +88 stdcall MsiInstallProductW(wstr wstr) +89 stdcall MsiLocateComponentA(str ptr long) +90 stdcall MsiLocateComponentW(wstr ptr long) +91 stdcall MsiOpenDatabaseA(str str ptr) +92 stdcall MsiOpenDatabaseW(wstr wstr ptr) +93 stdcall MsiOpenPackageA(str ptr) +94 stdcall MsiOpenPackageW(wstr ptr) +95 stdcall MsiOpenProductA(str ptr) +96 stdcall MsiOpenProductW(wstr ptr) +97 stdcall MsiPreviewBillboardA(long str str) +98 stdcall MsiPreviewBillboardW(long wstr wstr) +99 stdcall MsiPreviewDialogA(long str) +100 stdcall MsiPreviewDialogW(long wstr) +101 stub MsiProcessAdvertiseScriptA +102 stub MsiProcessAdvertiseScriptW +103 stdcall MsiProcessMessage(long long long) +104 stub MsiProvideComponentA +105 stdcall MsiProvideComponentFromDescriptorA(str ptr ptr ptr) +106 stdcall MsiProvideComponentFromDescriptorW(wstr ptr ptr ptr) +107 stub MsiProvideComponentW +108 stdcall MsiProvideQualifiedComponentA(str str long ptr ptr) +109 stdcall MsiProvideQualifiedComponentW(wstr wstr long ptr ptr) +110 stdcall MsiQueryFeatureStateA(str str) +111 stdcall MsiQueryFeatureStateW(wstr wstr) +112 stdcall MsiQueryProductStateA(str) +113 stdcall MsiQueryProductStateW(wstr) +114 stdcall MsiRecordDataSize(long long) +115 stdcall MsiRecordGetFieldCount(long) +116 stdcall MsiRecordGetInteger(long long) +117 stdcall MsiRecordGetStringA(long long ptr ptr) +118 stdcall MsiRecordGetStringW(long long ptr ptr) +119 stdcall MsiRecordIsNull(long long) +120 stdcall MsiRecordReadStream(long long ptr ptr) +121 stdcall MsiRecordSetInteger(long long long) +122 stdcall MsiRecordSetStreamA(long long str) +123 stdcall MsiRecordSetStreamW(long long wstr) +124 stdcall MsiRecordSetStringA(long long str) +125 stdcall MsiRecordSetStringW(long long wstr) +126 stdcall MsiReinstallFeatureA(str str long) +127 stub MsiReinstallFeatureFromDescriptorA +128 stub MsiReinstallFeatureFromDescriptorW +129 stdcall MsiReinstallFeatureW(wstr wstr long) +130 stdcall MsiReinstallProductA(str long) +131 stdcall MsiReinstallProductW(wstr long) +132 stdcall MsiSequenceA(long str long) +133 stdcall MsiSequenceW(long wstr long) +134 stdcall MsiSetComponentStateA(long str long) +135 stdcall MsiSetComponentStateW(long wstr long) +136 stdcall MsiSetExternalUIA(ptr long ptr) +137 stdcall MsiSetExternalUIW(ptr long ptr) +138 stdcall MsiSetFeatureStateA(long str long) +139 stdcall MsiSetFeatureStateW(long wstr long) +140 stub MsiSetInstallLevel +141 stdcall MsiSetInternalUI(long ptr) +142 stub MsiVerifyDiskSpace +143 stdcall MsiSetMode(long long long) +144 stdcall MsiSetPropertyA(long str str) +145 stdcall MsiSetPropertyW(long wstr wstr) +146 stdcall MsiSetTargetPathA(long str str) +147 stdcall MsiSetTargetPathW(long wstr wstr) +148 stdcall MsiSummaryInfoGetPropertyA(long long ptr ptr ptr ptr ptr) +149 stdcall MsiSummaryInfoGetPropertyCount(long ptr) +150 stdcall MsiSummaryInfoGetPropertyW(long long ptr ptr ptr ptr ptr) +151 stdcall MsiSummaryInfoPersist(long) +152 stdcall MsiSummaryInfoSetPropertyA(long long long long ptr str) +153 stdcall MsiSummaryInfoSetPropertyW(long long long long ptr wstr) +154 stdcall MsiUseFeatureA(str str) +155 stdcall MsiUseFeatureW(wstr wstr) +156 stdcall MsiVerifyPackageA(str) +157 stdcall MsiVerifyPackageW(wstr) +158 stdcall MsiViewClose(long) +159 stdcall MsiViewExecute(long long) +160 stdcall MsiViewFetch(long ptr) +161 stdcall MsiViewGetErrorA(long ptr ptr) +162 stdcall MsiViewGetErrorW(long ptr ptr) +163 stdcall MsiViewModify(long long long) +164 stdcall MsiDatabaseIsTablePersistentA(long str) +165 stdcall MsiDatabaseIsTablePersistentW(long wstr) +166 stdcall MsiViewGetColumnInfo(long long ptr) +167 stdcall MsiRecordClearData(long) +168 stdcall MsiEnableLogA(long str long) +169 stdcall MsiEnableLogW(long wstr long) +170 stdcall MsiFormatRecordA(long long ptr ptr) +171 stdcall MsiFormatRecordW(long long ptr ptr) +172 stdcall MsiGetComponentPathA(str str ptr ptr) +173 stdcall MsiGetComponentPathW(wstr wstr ptr ptr) +174 stdcall MsiApplyPatchA(str str long str) +175 stdcall MsiApplyPatchW(wstr wstr long wstr) +176 stdcall MsiAdvertiseScriptA(str long ptr long) +177 stdcall MsiAdvertiseScriptW(wstr long ptr long) +178 stub MsiGetPatchInfoA +179 stub MsiGetPatchInfoW +180 stdcall MsiEnumPatchesA(str long ptr ptr ptr) +181 stdcall MsiEnumPatchesW(str long ptr ptr ptr) +182 stdcall -private DllGetVersion(ptr) +183 stub MsiGetProductCodeFromPackageCodeA +184 stub MsiGetProductCodeFromPackageCodeW +185 stub MsiCreateTransformSummaryInfoA +186 stub MsiCreateTransformSummaryInfoW +187 stub MsiQueryFeatureStateFromDescriptorA +188 stub MsiQueryFeatureStateFromDescriptorW +189 stdcall MsiConfigureProductExA(str long long str) +190 stdcall MsiConfigureProductExW(wstr long long wstr) +191 stub MsiInvalidateFeatureCache +192 stdcall MsiUseFeatureExA(str str long long) +193 stdcall MsiUseFeatureExW(wstr wstr long long) +194 stdcall MsiGetFileVersionA(str str ptr str ptr) +195 stdcall MsiGetFileVersionW(wstr wstr ptr wstr ptr) +196 stdcall MsiLoadStringA(long long long long long) +197 stdcall MsiLoadStringW(long long long long long) +198 stdcall MsiMessageBoxA(long long long long long long) +199 stdcall MsiMessageBoxW(long long long long long long) +200 stdcall MsiDecomposeDescriptorA(str ptr ptr ptr ptr) +201 stdcall MsiDecomposeDescriptorW(wstr ptr ptr ptr ptr) +202 stub MsiProvideQualifiedComponentExA +203 stdcall MsiProvideQualifiedComponentExW(wstr wstr long wstr long long ptr ptr) +204 stdcall MsiEnumRelatedProductsA(str long long ptr) +205 stdcall MsiEnumRelatedProductsW(wstr long long ptr) +206 stub MsiSetFeatureAttributesA +207 stub MsiSetFeatureAttributesW +208 stub MsiSourceListClearAllA +209 stub MsiSourceListClearAllW +210 stub MsiSourceListAddSourceA +211 stub MsiSourceListAddSourceW +212 stub MsiSourceListForceResolutionA +213 stub MsiSourceListForceResolutionW +214 stub MsiIsProductElevatedA +215 stub MsiIsProductElevatedW +216 stdcall MsiGetShortcutTargetA(str ptr ptr ptr) +217 stdcall MsiGetShortcutTargetW(wstr ptr ptr ptr) +218 stdcall MsiGetFileHashA(str long ptr) +219 stdcall MsiGetFileHashW(wstr long ptr) +220 stub MsiEnumComponentCostsA +221 stub MsiEnumComponentCostsW +222 stdcall MsiCreateAndVerifyInstallerDirectory(long) +223 stdcall MsiGetFileSignatureInformationA(str long ptr ptr ptr) +224 stdcall MsiGetFileSignatureInformationW(wstr long ptr ptr ptr) +225 stdcall MsiProvideAssemblyA(str str long long str ptr) +226 stdcall MsiProvideAssemblyW(wstr wstr long long wstr ptr) +227 stdcall MsiAdvertiseProductExA(str str str long long long) +228 stdcall MsiAdvertiseProductExW(wstr wstr wstr long long long) +229 stub MsiNotifySidChangeA +230 stub MsiNotifySidChangeW +231 stdcall MsiOpenPackageExA(str long ptr) +232 stdcall MsiOpenPackageExW(wstr long ptr) +233 stub MsiDeleteUserDataA +234 stub MsiDeleteUserDataW +235 stub Migrate10CachedPackagesA +236 stub Migrate10CachedPackagesW + +@ stdcall -private DllCanUnloadNow() +@ stdcall -private DllGetClassObject(ptr ptr ptr) +@ stdcall -private DllRegisterServer() +@ stdcall -private DllUnregisterServer() diff --git a/reactos/dll/msi/msi.xml b/reactos/dll/msi/msi.xml new file mode 100644 index 00000000000..3e745ad88cf --- /dev/null +++ b/reactos/dll/msi/msi.xml @@ -0,0 +1,62 @@ + + + + . + include/wine + + + 0x600 + 0x501 + 0x501 + wine + uuid + ntdll + kernel32 + user32 + gdi32 + advapi32 + shell32 + shlwapi + winmm + cabinet + ole32 + oleaut32 + version + action.c + appsearch.c + classes.c + cond.tab.c + create.c + custom.c + database.c + delete.c + dialog.c + distinct.c + events.c + files.c + format.c + handle.c + helpers.c + insert.c + install.c + msi.c + msiquery.c + order.c + package.c + preview.c + record.c + registry.c + regsvr.c + select.c + source.c + sql.tab.c + string.c + suminfo.c + table.c + tokenize.c + update.c + upgrade.c + where.c + msi.spec + msi.rc + diff --git a/reactos/dll/msi/msi_Bg.rc b/reactos/dll/msi/msi_Bg.rc new file mode 100644 index 00000000000..8a0ead57e79 --- /dev/null +++ b/reactos/dll/msi/msi_Bg.rc @@ -0,0 +1,33 @@ +/* + * Bulgarian resources for MSI + * + * Copyright 2005 Milko Krachounov + * + * 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 + */ + +LANGUAGE LANG_BULGARIAN, SUBLANG_DEFAULT + +STRINGTABLE DISCARDABLE +{ + 5 "ïúòÿò %s íå å íàìåðåí" + 9 "ïîñòàâåòå äèñê %s" + 10 "íåêîðåêòíè ïàðàìåòðè" + 11 "âúâåäåòå ïàïêàòà, êîÿòî ñúäúðæà %s" + 12 "èçòî÷íèêà çà èíñòàëàöèÿ íà ôóíêöèîíàëíîñòòà ëèïñâà" + 13 "ìðåæîâîòî óñòðîéñòâà íóæíî çà ôóíêöèîíàëíîñòòà ëèïñâà " + 14 "ôóíêöèîíàëíîñò îò:" + 15 "èçáåðåòå ïàïêàòà, êîÿòî ñúäúðæà %s" +} diff --git a/reactos/dll/msi/msi_De.rc b/reactos/dll/msi/msi_De.rc new file mode 100644 index 00000000000..0b15276eca8 --- /dev/null +++ b/reactos/dll/msi/msi_De.rc @@ -0,0 +1,33 @@ +/* + * German resources for MSI + * + * Copyright 2005 Henning Gerhardt + * + * 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 + */ + +LANGUAGE LANG_GERMAN, SUBLANG_DEFAULT + +STRINGTABLE DISCARDABLE +{ + 5 "Der Pfad %s wurde nicht gefunden." + 9 "Bitte Disk %s einlegen." + 10 "schlechte Parameter" + 11 "Geben Sie das Verzeichnis ein, dass %s enthält." + 12 "Die Installationsquelle für das Feature fehlt." + 13 "Das Netzwerklaufwerk für das Feature fehlt." + 14 "Feature von:" + 15 "Wählen Sie das Verzeichnis aus, dass %s enthält." +} diff --git a/reactos/dll/msi/msi_En.rc b/reactos/dll/msi/msi_En.rc new file mode 100644 index 00000000000..f3330c88528 --- /dev/null +++ b/reactos/dll/msi/msi_En.rc @@ -0,0 +1,33 @@ +/* + * English resources for MSI + * + * Copyright 2005 Mike McCormack + * + * 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 + */ + +LANGUAGE LANG_ENGLISH, SUBLANG_DEFAULT + +STRINGTABLE DISCARDABLE +{ + 5 "path %s not found" + 9 "insert disk %s" + 10 "bad parameters" + 11 "enter which folder contains %s" + 12 "install source for feature missing" + 13 "network drive for feature missing" + 14 "feature from:" + 15 "choose which folder contains %s" +} diff --git a/reactos/dll/msi/msi_Es.rc b/reactos/dll/msi/msi_Es.rc new file mode 100644 index 00000000000..3d36ff7cde8 --- /dev/null +++ b/reactos/dll/msi/msi_Es.rc @@ -0,0 +1,33 @@ +/* + * Spanish resources for MSI + * + * Copyright 2005 José Manuel Ferrer Ortiz + * + * 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 + */ + +LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL + +STRINGTABLE DISCARDABLE +{ + 5 "ruta %s no encontrada" + 9 "inserte el disco %s" + 10 "parámetros incorrectos" + 11 "introduzca qué carpeta contiene %s" + 12 "instalar fuente para característica ausente" + 13 "unidad de red para característica ausente" + 14 "característica de:" + 15 "elija qué carpeta contiene %s" +} diff --git a/reactos/dll/msi/msi_Fi.rc b/reactos/dll/msi/msi_Fi.rc new file mode 100644 index 00000000000..5de0be8a72e --- /dev/null +++ b/reactos/dll/msi/msi_Fi.rc @@ -0,0 +1,33 @@ +/* + * Finnish resources for MSI + * + * Copyright 2005 Kimmo Myllyvirta + * + * 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 + */ + +LANGUAGE LANG_FINNISH, SUBLANG_DEFAULT + +STRINGTABLE DISCARDABLE +{ + 5 "Polkua %s ei löydy." + 9 "Anna levy %s" + 10 "Virheelliset parametrit." + 11 "Anna kansio, joka sisältää %s" + 12 "Ominaisuuden asennuslähde puuttuu." + 13 "Ominaisuuden verkkolevy puuttuu." + 14 "Ominaisuus:" + 15 "Valitse kansio, joka sisältää %s" +} diff --git a/reactos/dll/msi/msi_Fr.rc b/reactos/dll/msi/msi_Fr.rc new file mode 100644 index 00000000000..6ed10340aac --- /dev/null +++ b/reactos/dll/msi/msi_Fr.rc @@ -0,0 +1,33 @@ +/* + * French resources for MSI + * + * Copyright 2005 Jonathan Ernst + * + * 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 + */ + +LANGUAGE LANG_FRENCH, SUBLANG_NEUTRAL + +STRINGTABLE DISCARDABLE +{ + 5 "Le chemin %s est introuvable" + 9 "insérez le disque %s" + 10 "mauvais paramètres" + 11 "saisissez le nom du dossier contenant %s" + 12 "source d'installation pour la fonctionnalité manquante" + 13 "lecteur réseau pour la fonctionnalité manquant" + 14 "fonctionnalité depuis:" + 15 "sélectionnez le dossier contenant %s" +} diff --git a/reactos/dll/msi/msi_Hu.rc b/reactos/dll/msi/msi_Hu.rc new file mode 100644 index 00000000000..75328314326 --- /dev/null +++ b/reactos/dll/msi/msi_Hu.rc @@ -0,0 +1,34 @@ +/* + * Hungarian resources for MSI + * + * Copyright 2005 Mike McCormack + * Copyright 2005 Gergely Risko + * + * 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 + */ + +LANGUAGE LANG_HUNGARIAN, SUBLANG_NEUTRAL + +STRINGTABLE DISCARDABLE +{ + 5 "Az elérési út: %s nem található" + 9 "Tedd be a következõ lemezt: %s" + 10 "Rossz paraméterek" + 11 "Add meg melyik mappa tartalmazza a következõt: %s" + 12 "Az összetevõ forrása hiányzik" + 13 "Hiányzik a hálózati meghajtó az össszetevõhöz" +/* NOT TRANSLATED */ 14 "feature from:" + 15 "Válaszd ki melyik mappa tartalmazza a következõt: %s" +} diff --git a/reactos/dll/msi/msi_Ja.rc b/reactos/dll/msi/msi_Ja.rc new file mode 100644 index 00000000000..84424c26ef8 --- /dev/null +++ b/reactos/dll/msi/msi_Ja.rc @@ -0,0 +1,13 @@ +LANGUAGE LANG_JAPANESE, SUBLANG_NEUTRAL + +STRINGTABLE DISCARDABLE +{ + 5 "ƒpƒX %s ‚ª‚݂‚©‚è‚Ü‚¹‚ñ‚Å‚µ‚½" + 9 "ƒfƒBƒXƒN %s ‚ð‘}“ü‚µ‚Ä‚­‚¾‚³‚¢" + 10 "–³Œø‚ȃpƒ‰ƒ[ƒ^‚Å‚·" + 11 "%s ‚Ì‚ ‚éƒtƒHƒ‹ƒ_‚ð“ü—Í‚µ‚Ä‚­‚¾‚³‚¢" + 12 "‹@”\\‚̃Cƒ“ƒXƒg[ƒ‹Œ³‚ª‚ ‚è‚Ü‚¹‚ñ" + 13 "‹@”\\‚ª‚ ‚éƒlƒbƒgƒ[ƒN ƒhƒ‰ƒCƒu‚ª‚ ‚è‚Ü‚¹‚ñ" + 14 "‹@”\\‚ÌêŠ:" + 15 "%s ‚Ì‚ ‚éƒtƒHƒ‹ƒ_‚ð‘I‘ð‚µ‚Ä‚­‚¾‚³‚¢" +} diff --git a/reactos/dll/msi/msi_Ko.rc b/reactos/dll/msi/msi_Ko.rc new file mode 100644 index 00000000000..fb4be6bc672 --- /dev/null +++ b/reactos/dll/msi/msi_Ko.rc @@ -0,0 +1,33 @@ +/* + * Korean resources for MSI + * + * Copyright 2005 YunSong Hwang + * + * 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 + */ + +LANGUAGE LANG_KOREAN, SUBLANG_DEFAULT + +STRINGTABLE DISCARDABLE +{ + 5 "%s °æ·Î¸¦ ãÀ»¼ö ¾ø½À´Ï´Ù" + 9 "µð½ºÅ© %s »ðÀÔ" + 10 "Àý¸øµÈ ¸Å°³º¯¼ö" + 11 "%s¸¦ Æ÷ÇÔÇÏ´Â Æú´õ¸¦ ÀÔ·ÂÇϼ¼¿©" + 12 "ºüÁø ºÎºÐ(feature)À» À§ÇÑ ¼³Ä¡ ¿øº»" + 13 "ºüÁø ºÎºÐ(feature)À» À§ÇÑ ³×Æ®¿öÅ© µå¶óÀ̺ê" + 14 "ºÎºÐ(feature)¿¡¼­:" + 15 " %s¸¦ Æ÷ÇÔÇÏ´Â Æú´õ ¼±ÅÃ" +} diff --git a/reactos/dll/msi/msi_Nl.rc b/reactos/dll/msi/msi_Nl.rc new file mode 100644 index 00000000000..aff08e7bf90 --- /dev/null +++ b/reactos/dll/msi/msi_Nl.rc @@ -0,0 +1,33 @@ +/* + * Durch resources for MSI + * + * Copyright 2005 Hans Leidekker + * + * 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 + */ + +LANGUAGE LANG_DUTCH, SUBLANG_DEFAULT + +STRINGTABLE DISCARDABLE +{ + 5 "Pad %s niet gevonden" + 9 "Plaats disk %s" + 10 "Ongeldige parameters" + 11 "Voer de map in die %s bevat" + 12 "De installatiebron van het feature ontbreekt" + 13 "De netwerkschijf met het feature ontbreekt" + 14 "Feature van:" + 15 "Kies de map die %s bevat" +} diff --git a/reactos/dll/msi/msi_No.rc b/reactos/dll/msi/msi_No.rc new file mode 100644 index 00000000000..fbed9fbeec6 --- /dev/null +++ b/reactos/dll/msi/msi_No.rc @@ -0,0 +1,33 @@ +/* + * Norwegian Bokmål resources for MSI + * + * Copyright 2005 Alexander N. Sørnes + * + * 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 + */ + +LANGUAGE LANG_NORWEGIAN, SUBLANG_NORWEGIAN_BOKMAL + +STRINGTABLE DISCARDABLE +{ + 5 "Fant ikke stien '%s'." + 9 "Sett i disk '%s'" + 10 "Gale parametere." + 11 "Oppgi katalogen som inneholder '%s'." + 12 "Egenskapens installasjonskilde mangler." + 13 "Egenskapens nettverksstasjon mangler." + 14 "Egenskap fra::" + 15 "Velg katalogen som inneholder '%s'." +} diff --git a/reactos/dll/msi/msi_Pt.rc b/reactos/dll/msi/msi_Pt.rc new file mode 100644 index 00000000000..aa87ad2fb8e --- /dev/null +++ b/reactos/dll/msi/msi_Pt.rc @@ -0,0 +1,33 @@ +/* + * Portuguese resources for MSI + * + * Copyright 2005 Marcelo Duarte + * + * 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 + */ + +LANGUAGE LANG_PORTUGUESE, SUBLANG_DEFAULT + +STRINGTABLE DISCARDABLE +{ + 5 "caminho %s não encontrado" + 9 "insira disco %s" + 10 "parâmetros inválidos" + 11 "entre qual pasta contém %s" + 12 "instalar fonte para característica faltando" + 13 "drive de rede para característica faltando" + 14 "característica de:" + 15 "escolha qual pasta contém %s" +} diff --git a/reactos/dll/msi/msi_Ru.rc b/reactos/dll/msi/msi_Ru.rc new file mode 100644 index 00000000000..b9f41d79ef8 --- /dev/null +++ b/reactos/dll/msi/msi_Ru.rc @@ -0,0 +1,34 @@ +/* + * Russian resources for MSI + * + * Copyright 2005 Mikhail Y. Zvyozdochkin + * Aleksey Bragin + * + * 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 + */ + +LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT + +STRINGTABLE DISCARDABLE +{ + 5 "ïóòü %s íå íàéäåí" + 9 "âñòàâüòå äèñê %s" + 10 "íåâåðíûå ïàðàìåòðû" + 11 "ââåäèòå, êàêàÿ ïàïêà ñîäåðæèò %s" + 12 "íåäîñòóïåí èñòî÷íèê óñòàíîâêè (ñúåìíûé èëè êîìïàêò-äèñê íå âñòàâëåí â äèñêîâîä)" + 13 "íåäîñòóïåí ñåòåâîé äèñê, ñîäåðæàùèé íåîáõîäèìûé ôàéë" + 14 "ôóíêöèîíàëüíîñòü èç:" + 15 "âûáåðèòå, êàêàÿ ïàïêà ñîäåðæèò %s" +} diff --git a/reactos/dll/msi/msipriv.h b/reactos/dll/msi/msipriv.h new file mode 100644 index 00000000000..575d1218230 --- /dev/null +++ b/reactos/dll/msi/msipriv.h @@ -0,0 +1,507 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002-2005 Mike McCormack 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __WINE_MSI_PRIVATE__ +#define __WINE_MSI_PRIVATE__ + +#include + +#include "windef.h" +#include "winbase.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "winnls.h" +#include "wine/list.h" + +#define MSI_DATASIZEMASK 0x00ff +#define MSITYPE_VALID 0x0100 +#define MSITYPE_LOCALIZABLE 0x200 +#define MSITYPE_STRING 0x0800 +#define MSITYPE_NULLABLE 0x1000 +#define MSITYPE_KEY 0x2000 + +#define MSITYPE_IS_BINARY(type) (((type) & ~MSITYPE_NULLABLE) == (MSITYPE_STRING|MSITYPE_VALID)) + +struct tagMSITABLE; +typedef struct tagMSITABLE MSITABLE; + +struct string_table; +typedef struct string_table string_table; + +struct tagMSIOBJECTHDR; +typedef struct tagMSIOBJECTHDR MSIOBJECTHDR; + +typedef VOID (*msihandledestructor)( MSIOBJECTHDR * ); + +struct tagMSIOBJECTHDR +{ + UINT magic; + UINT type; + LONG refcount; + msihandledestructor destructor; +}; + +typedef struct tagMSIDATABASE +{ + MSIOBJECTHDR hdr; + IStorage *storage; + string_table *strings; + LPCWSTR mode; + struct list tables; + struct list transforms; +} MSIDATABASE; + +typedef struct tagMSIVIEW MSIVIEW; + +typedef struct tagMSIQUERY +{ + MSIOBJECTHDR hdr; + MSIVIEW *view; + UINT row; + MSIDATABASE *db; + struct list mem; +} MSIQUERY; + +/* maybe we can use a Variant instead of doing it ourselves? */ +typedef struct tagMSIFIELD +{ + UINT type; + union + { + INT iVal; + LPWSTR szwVal; + IStream *stream; + } u; +} MSIFIELD; + +typedef struct tagMSIRECORD +{ + MSIOBJECTHDR hdr; + UINT count; /* as passed to MsiCreateRecord */ + MSIFIELD fields[1]; /* nb. array size is count+1 */ +} MSIRECORD; + +typedef struct tagMSIVIEWOPS +{ + /* + * fetch_int - reads one integer from {row,col} in the table + * + * This function should be called after the execute method. + * Data returned by the function should not change until + * close or delete is called. + * To get a string value, query the database's string table with + * the integer value returned from this function. + */ + UINT (*fetch_int)( struct tagMSIVIEW *, UINT row, UINT col, UINT *val ); + + /* + * fetch_int - reads one integer from {row,col} in the table + * + * This function is similar to fetch_int, except fetches a + * stream instead of an integer. + */ + UINT (*fetch_stream)( struct tagMSIVIEW *, UINT row, UINT col, IStream **stm ); + + /* + * get_int - sets one integer at {row,col} in the table + * + * Similar semantics to fetch_int + */ + UINT (*set_int)( struct tagMSIVIEW *, UINT row, UINT col, UINT val ); + + /* + * Inserts a new row into the database from the records contents + */ + UINT (*insert_row)( struct tagMSIVIEW *, MSIRECORD * ); + + /* + * execute - loads the underlying data into memory so it can be read + */ + UINT (*execute)( struct tagMSIVIEW *, MSIRECORD * ); + + /* + * close - clears the data read by execute from memory + */ + UINT (*close)( struct tagMSIVIEW * ); + + /* + * get_dimensions - returns the number of rows or columns in a table. + * + * The number of rows can only be queried after the execute method + * is called. The number of columns can be queried at any time. + */ + UINT (*get_dimensions)( struct tagMSIVIEW *, UINT *rows, UINT *cols ); + + /* + * get_column_info - returns the name and type of a specific column + * + * The name is HeapAlloc'ed by this function and should be freed by + * the caller. + * The column information can be queried at any time. + */ + UINT (*get_column_info)( struct tagMSIVIEW *, UINT n, LPWSTR *name, UINT *type ); + + /* + * modify - not yet implemented properly + */ + UINT (*modify)( struct tagMSIVIEW *, MSIMODIFY, MSIRECORD * ); + + /* + * delete - destroys the structure completely + */ + UINT (*delete)( struct tagMSIVIEW * ); + +} MSIVIEWOPS; + +struct tagMSIVIEW +{ + MSIOBJECTHDR hdr; + MSIVIEWOPS *ops; +}; + +struct msi_dialog_tag; +typedef struct msi_dialog_tag msi_dialog; + +typedef struct tagMSIPACKAGE +{ + MSIOBJECTHDR hdr; + MSIDATABASE *db; + struct list components; + struct list features; + struct list files; + struct list tempfiles; + struct list folders; + LPWSTR ActionFormat; + LPWSTR LastAction; + + struct list classes; + struct list extensions; + struct list progids; + struct list mimes; + struct list appids; + + struct tagMSISCRIPT *script; + + struct list RunningActions; + + LPWSTR PackagePath; + LPWSTR ProductCode; + + UINT CurrentInstallState; + msi_dialog *dialog; + LPWSTR next_dialog; + + struct list subscriptions; +} MSIPACKAGE; + +typedef struct tagMSIPREVIEW +{ + MSIOBJECTHDR hdr; + MSIPACKAGE *package; + msi_dialog *dialog; +} MSIPREVIEW; + +#define MSI_MAX_PROPS 20 + +typedef struct tagMSISUMMARYINFO +{ + MSIOBJECTHDR hdr; + MSIDATABASE *db; + DWORD update_count; + PROPVARIANT property[MSI_MAX_PROPS]; +} MSISUMMARYINFO; + +#define MSIHANDLETYPE_ANY 0 +#define MSIHANDLETYPE_DATABASE 1 +#define MSIHANDLETYPE_SUMMARYINFO 2 +#define MSIHANDLETYPE_VIEW 3 +#define MSIHANDLETYPE_RECORD 4 +#define MSIHANDLETYPE_PACKAGE 5 +#define MSIHANDLETYPE_PREVIEW 6 + +#define MSI_MAJORVERSION 2 +#define MSI_MINORVERSION 0 +#define MSI_BUILDNUMBER 2600 + +#define GUID_SIZE 39 + +#define MSIHANDLE_MAGIC 0x4d434923 +#define MSIMAXHANDLES 0xf0 + +DEFINE_GUID(CLSID_IMsiServer, 0x000C101C,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46); +DEFINE_GUID(CLSID_IMsiServerX1, 0x000C103E,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46); +DEFINE_GUID(CLSID_IMsiServerX2, 0x000C1090,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46); +DEFINE_GUID(CLSID_IMsiServerX3, 0x000C1094,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46); + +DEFINE_GUID(CLSID_IMsiServerMessage, 0x000C101D,0x0000,0x0000,0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46); + +/* handle unicode/ascii output in the Msi* API functions */ +typedef struct { + BOOL unicode; + union { + LPSTR a; + LPWSTR w; + } str; +} awstring; + +typedef struct { + BOOL unicode; + union { + LPCSTR a; + LPCWSTR w; + } str; +} awcstring; + +UINT msi_strcpy_to_awstring( LPCWSTR str, awstring *awbuf, DWORD *sz ); + +/* handle functions */ +extern void *msihandle2msiinfo(MSIHANDLE handle, UINT type); +extern MSIHANDLE alloc_msihandle( MSIOBJECTHDR * ); +extern void *alloc_msiobject(UINT type, UINT size, msihandledestructor destroy ); +extern void msiobj_addref(MSIOBJECTHDR *); +extern int msiobj_release(MSIOBJECTHDR *); +extern void msiobj_lock(MSIOBJECTHDR *); +extern void msiobj_unlock(MSIOBJECTHDR *); + +extern void free_cached_tables( MSIDATABASE *db ); +extern void msi_free_transforms( MSIDATABASE *db ); +extern string_table *load_string_table( IStorage *stg ); +extern UINT MSI_CommitTables( MSIDATABASE *db ); +extern HRESULT init_string_table( IStorage *stg ); + + +/* string table functions */ +extern BOOL msi_addstring( string_table *st, UINT string_no, const CHAR *data, int len, UINT refcount ); +extern BOOL msi_addstringW( string_table *st, UINT string_no, const WCHAR *data, int len, UINT refcount ); +extern UINT msi_id2stringW( string_table *st, UINT string_no, LPWSTR buffer, UINT *sz ); +extern UINT msi_id2stringA( string_table *st, UINT string_no, LPSTR buffer, UINT *sz ); + +extern LPWSTR MSI_makestring( MSIDATABASE *db, UINT stringid); +extern UINT msi_string2idW( string_table *st, LPCWSTR buffer, UINT *id ); +extern UINT msi_string2idA( string_table *st, LPCSTR str, UINT *id ); +extern string_table *msi_init_stringtable( int entries, UINT codepage ); +extern VOID msi_destroy_stringtable( string_table *st ); +extern UINT msi_string_count( string_table *st ); +extern UINT msi_id_refcount( string_table *st, UINT i ); +extern UINT msi_string_totalsize( string_table *st, UINT *last ); +extern UINT msi_strcmp( string_table *st, UINT lval, UINT rval, UINT *res ); +extern const WCHAR *msi_string_lookup_id( string_table *st, UINT id ); +extern UINT msi_string_get_codepage( string_table *st ); + + +extern UINT VIEW_find_column( MSIVIEW *view, LPCWSTR name, UINT *n ); + +extern BOOL TABLE_Exists( MSIDATABASE *db, LPWSTR name ); + +extern UINT read_raw_stream_data( MSIDATABASE*, LPCWSTR stname, + USHORT **pdata, UINT *psz ); + +/* transform functions */ +extern UINT msi_table_apply_transform( MSIDATABASE *db, IStorage *stg ); +extern UINT MSI_DatabaseApplyTransformW( MSIDATABASE *db, + LPCWSTR szTransformFile, int iErrorCond ); + +/* action internals */ +extern UINT MSI_InstallPackage( MSIPACKAGE *, LPCWSTR, LPCWSTR ); +extern void ACTION_free_package_structures( MSIPACKAGE* ); +extern UINT ACTION_DialogBox( MSIPACKAGE*, LPCWSTR); +extern UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode ); + +/* record internals */ +extern UINT MSI_RecordSetIStream( MSIRECORD *, unsigned int, IStream *); +extern UINT MSI_RecordGetIStream( MSIRECORD *, unsigned int, IStream **); +extern const WCHAR *MSI_RecordGetString( MSIRECORD *, unsigned int ); +extern MSIRECORD *MSI_CreateRecord( unsigned int ); +extern UINT MSI_RecordSetInteger( MSIRECORD *, unsigned int, int ); +extern UINT MSI_RecordSetStringW( MSIRECORD *, unsigned int, LPCWSTR ); +extern UINT MSI_RecordSetStringA( MSIRECORD *, unsigned int, LPCSTR ); +extern BOOL MSI_RecordIsNull( MSIRECORD *, unsigned int ); +extern UINT MSI_RecordGetStringW( MSIRECORD * , unsigned int, LPWSTR, DWORD *); +extern UINT MSI_RecordGetStringA( MSIRECORD *, unsigned int, LPSTR, DWORD *); +extern int MSI_RecordGetInteger( MSIRECORD *, unsigned int ); +extern UINT MSI_RecordReadStream( MSIRECORD *, unsigned int, char *, DWORD *); +extern unsigned int MSI_RecordGetFieldCount( MSIRECORD *rec ); +extern UINT MSI_RecordSetStreamW( MSIRECORD *, unsigned int, LPCWSTR ); +extern UINT MSI_RecordSetStreamA( MSIRECORD *, unsigned int, LPCSTR ); +extern UINT MSI_RecordDataSize( MSIRECORD *, unsigned int ); +extern UINT MSI_RecordStreamToFile( MSIRECORD *, unsigned int, LPCWSTR ); + +/* stream internals */ +extern UINT get_raw_stream( MSIHANDLE hdb, LPCWSTR stname, IStream **stm ); +extern UINT db_get_raw_stream( MSIDATABASE *db, LPCWSTR stname, IStream **stm ); +extern void enum_stream_names( IStorage *stg ); + +/* database internals */ +extern UINT MSI_OpenDatabaseW( LPCWSTR, LPCWSTR, MSIDATABASE ** ); +extern UINT MSI_DatabaseOpenViewW(MSIDATABASE *, LPCWSTR, MSIQUERY ** ); +extern UINT MSI_OpenQuery( MSIDATABASE *, MSIQUERY **, LPCWSTR, ... ); +typedef UINT (*record_func)( MSIRECORD *, LPVOID ); +extern UINT MSI_IterateRecords( MSIQUERY *, DWORD *, record_func, LPVOID ); +extern MSIRECORD *MSI_QueryGetRecord( MSIDATABASE *db, LPCWSTR query, ... ); +extern UINT MSI_DatabaseImport( MSIDATABASE *, LPCWSTR, LPCWSTR ); +extern UINT MSI_DatabaseExport( MSIDATABASE *, LPCWSTR, LPCWSTR, LPCWSTR ); +extern UINT MSI_DatabaseGetPrimaryKeys( MSIDATABASE *, LPCWSTR, MSIRECORD ** ); + +/* view internals */ +extern UINT MSI_ViewExecute( MSIQUERY*, MSIRECORD * ); +extern UINT MSI_ViewFetch( MSIQUERY*, MSIRECORD ** ); +extern UINT MSI_ViewClose( MSIQUERY* ); + +/* package internals */ +extern MSIPACKAGE *MSI_CreatePackage( MSIDATABASE * ); +extern UINT MSI_OpenPackageW( LPCWSTR szPackage, MSIPACKAGE ** ); +extern UINT MSI_SetTargetPathW( MSIPACKAGE *, LPCWSTR, LPCWSTR ); +extern UINT MSI_SetPropertyW( MSIPACKAGE *, LPCWSTR, LPCWSTR ); +extern INT MSI_ProcessMessage( MSIPACKAGE *, INSTALLMESSAGE, MSIRECORD * ); +extern UINT MSI_GetPropertyW( MSIPACKAGE *, LPCWSTR, LPWSTR, DWORD * ); +extern UINT MSI_GetPropertyA(MSIPACKAGE *, LPCSTR, LPSTR, DWORD* ); +extern MSICONDITION MSI_EvaluateConditionW( MSIPACKAGE *, LPCWSTR ); +extern UINT MSI_GetComponentStateW( MSIPACKAGE *, LPWSTR, INSTALLSTATE *, INSTALLSTATE * ); +extern UINT MSI_GetFeatureStateW( MSIPACKAGE *, LPWSTR, INSTALLSTATE *, INSTALLSTATE * ); +extern UINT WINAPI MSI_SetFeatureStateW(MSIPACKAGE*, LPCWSTR, INSTALLSTATE ); + +/* for deformating */ +extern UINT MSI_FormatRecordW( MSIPACKAGE *, MSIRECORD *, LPWSTR, DWORD * ); +extern UINT MSI_FormatRecordA( MSIPACKAGE *, MSIRECORD *, LPSTR, DWORD * ); + +/* registry data encoding/decoding functions */ +extern BOOL unsquash_guid(LPCWSTR in, LPWSTR out); +extern BOOL squash_guid(LPCWSTR in, LPWSTR out); +extern BOOL encode_base85_guid(GUID *,LPWSTR); +extern BOOL decode_base85_guid(LPCWSTR,GUID*); +extern UINT MSIREG_OpenUninstallKey(LPCWSTR szProduct, HKEY* key, BOOL create); +extern UINT MSIREG_OpenUserProductsKey(LPCWSTR szProduct, HKEY* key, BOOL create); +extern UINT MSIREG_OpenFeatures(HKEY* key); +extern UINT MSIREG_OpenFeaturesKey(LPCWSTR szProduct, HKEY* key, BOOL create); +extern UINT MSIREG_OpenComponents(HKEY* key); +extern UINT MSIREG_OpenUserComponentsKey(LPCWSTR szComponent, HKEY* key, BOOL create); +extern UINT MSIREG_OpenComponentsKey(LPCWSTR szComponent, HKEY* key, BOOL create); +extern UINT MSIREG_OpenProductsKey(LPCWSTR szProduct, HKEY* key, BOOL create); +extern UINT MSIREG_OpenUserFeaturesKey(LPCWSTR szProduct, HKEY* key, BOOL create); +extern UINT MSIREG_OpenUserComponentsKey(LPCWSTR szComponent, HKEY* key, BOOL create); +extern UINT MSIREG_OpenUpgradeCodesKey(LPCWSTR szProduct, HKEY* key, BOOL create); +extern UINT MSIREG_OpenUserUpgradeCodesKey(LPCWSTR szProduct, HKEY* key, BOOL create); + +extern LONG msi_reg_set_val_str( HKEY hkey, LPCWSTR name, LPCWSTR value ); +extern LONG msi_reg_set_val_multi_str( HKEY hkey, LPCWSTR name, LPCWSTR value ); +extern LONG msi_reg_set_val_dword( HKEY hkey, LPCWSTR name, DWORD val ); + +/* msi dialog interface */ +typedef UINT (*msi_dialog_event_handler)( MSIPACKAGE*, LPCWSTR, LPCWSTR, msi_dialog* ); +extern msi_dialog *msi_dialog_create( MSIPACKAGE*, LPCWSTR, msi_dialog_event_handler ); +extern UINT msi_dialog_run_message_loop( msi_dialog* ); +extern void msi_dialog_end_dialog( msi_dialog* ); +extern void msi_dialog_check_messages( HANDLE ); +extern void msi_dialog_do_preview( msi_dialog* ); +extern void msi_dialog_destroy( msi_dialog* ); +extern BOOL msi_dialog_register_class( void ); +extern void msi_dialog_unregister_class( void ); +extern void msi_dialog_handle_event( msi_dialog*, LPCWSTR, LPCWSTR, MSIRECORD * ); +extern UINT msi_dialog_reset( msi_dialog *dialog ); + +/* preview */ +extern MSIPREVIEW *MSI_EnableUIPreview( MSIDATABASE * ); +extern UINT MSI_PreviewDialogW( MSIPREVIEW *, LPCWSTR ); + +/* summary information */ +extern MSISUMMARYINFO *MSI_GetSummaryInformationW( MSIDATABASE *db, UINT uiUpdateCount ); +extern LPWSTR msi_suminfo_dup_string( MSISUMMARYINFO *si, UINT uiProperty ); + +/* undocumented functions */ +UINT WINAPI MsiCreateAndVerifyInstallerDirectory( DWORD ); +UINT WINAPI MsiDecomposeDescriptorW( LPCWSTR, LPWSTR, LPWSTR, LPWSTR, DWORD * ); +UINT WINAPI MsiDecomposeDescriptorA( LPCSTR, LPSTR, LPSTR, LPSTR, DWORD * ); +LANGID WINAPI MsiLoadStringW( MSIHANDLE, UINT, LPWSTR, int, LANGID ); +LANGID WINAPI MsiLoadStringA( MSIHANDLE, UINT, LPSTR, int, LANGID ); + +/* UI globals */ +extern INSTALLUILEVEL gUILevel; +extern HWND gUIhwnd; +extern INSTALLUI_HANDLERA gUIHandlerA; +extern INSTALLUI_HANDLERW gUIHandlerW; +extern DWORD gUIFilter; +extern LPVOID gUIContext; +extern WCHAR gszLogFile[MAX_PATH]; + +/* memory allocation macro functions */ +static inline void *msi_alloc( size_t len ) +{ + return HeapAlloc( GetProcessHeap(), 0, len ); +} + +static inline void *msi_alloc_zero( size_t len ) +{ + return HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, len ); +} + +static inline void *msi_realloc( void *mem, size_t len ) +{ + return HeapReAlloc( GetProcessHeap(), 0, mem, len ); +} + +static inline void *msi_realloc_zero( void *mem, size_t len ) +{ + return HeapReAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, mem, len ); +} + +static inline BOOL msi_free( void *mem ) +{ + return HeapFree( GetProcessHeap(), 0, mem ); +} + +inline static char *strdupWtoA( LPCWSTR str ) +{ + LPSTR ret = NULL; + DWORD len; + + if (!str) return ret; + len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL); + ret = msi_alloc( len ); + if (ret) + WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL ); + return ret; +} + +inline static LPWSTR strdupAtoW( LPCSTR str ) +{ + LPWSTR ret = NULL; + DWORD len; + + if (!str) return ret; + len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 ); + ret = msi_alloc( len * sizeof(WCHAR) ); + if (ret) + MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len ); + return ret; +} + +inline static LPWSTR strdupW( LPCWSTR src ) +{ + LPWSTR dest; + if (!src) return NULL; + dest = msi_alloc( (lstrlenW(src)+1)*sizeof(WCHAR) ); + if (dest) + lstrcpyW(dest, src); + return dest; +} + +#endif /* __WINE_MSI_PRIVATE__ */ diff --git a/reactos/dll/msi/msiquery.c b/reactos/dll/msi/msiquery.c new file mode 100644 index 00000000000..4824d875473 --- /dev/null +++ b/reactos/dll/msi/msiquery.c @@ -0,0 +1,868 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002-2005 Mike McCormack 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "wine/unicode.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" +#include "winnls.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +static void MSI_CloseView( MSIOBJECTHDR *arg ) +{ + MSIQUERY *query = (MSIQUERY*) arg; + struct list *ptr, *t; + + if( query->view && query->view->ops->delete ) + query->view->ops->delete( query->view ); + msiobj_release( &query->db->hdr ); + + LIST_FOR_EACH_SAFE( ptr, t, &query->mem ) + { + msi_free( ptr ); + } +} + +UINT VIEW_find_column( MSIVIEW *table, LPCWSTR name, UINT *n ) +{ + LPWSTR col_name; + UINT i, count, r; + + r = table->ops->get_dimensions( table, NULL, &count ); + if( r != ERROR_SUCCESS ) + return r; + + for( i=1; i<=count; i++ ) + { + INT x; + + col_name = NULL; + r = table->ops->get_column_info( table, i, &col_name, NULL ); + if( r != ERROR_SUCCESS ) + return r; + x = lstrcmpW( name, col_name ); + msi_free( col_name ); + if( !x ) + { + *n = i; + return ERROR_SUCCESS; + } + } + + return ERROR_INVALID_PARAMETER; +} + +UINT WINAPI MsiDatabaseOpenViewA(MSIHANDLE hdb, + LPCSTR szQuery, MSIHANDLE *phView) +{ + UINT r; + LPWSTR szwQuery; + + TRACE("%ld %s %p\n", hdb, debugstr_a(szQuery), phView); + + if( szQuery ) + { + szwQuery = strdupAtoW( szQuery ); + if( !szwQuery ) + return ERROR_FUNCTION_FAILED; + } + else + szwQuery = NULL; + + r = MsiDatabaseOpenViewW( hdb, szwQuery, phView); + + msi_free( szwQuery ); + return r; +} + +UINT MSI_DatabaseOpenViewW(MSIDATABASE *db, + LPCWSTR szQuery, MSIQUERY **pView) +{ + MSIQUERY *query; + UINT r; + + TRACE("%s %p\n", debugstr_w(szQuery), pView); + + if( !szQuery) + return ERROR_INVALID_PARAMETER; + + /* pre allocate a handle to hold a pointer to the view */ + query = alloc_msiobject( MSIHANDLETYPE_VIEW, sizeof (MSIQUERY), + MSI_CloseView ); + if( !query ) + return ERROR_FUNCTION_FAILED; + + msiobj_addref( &db->hdr ); + query->row = 0; + query->db = db; + query->view = NULL; + list_init( &query->mem ); + + r = MSI_ParseSQL( db, szQuery, &query->view, &query->mem ); + if( r == ERROR_SUCCESS ) + { + msiobj_addref( &query->hdr ); + *pView = query; + } + + msiobj_release( &query->hdr ); + return r; +} + +UINT MSI_OpenQuery( MSIDATABASE *db, MSIQUERY **view, LPCWSTR fmt, ... ) +{ + UINT r; + int size = 100, res; + LPWSTR query; + + /* construct the string */ + for (;;) + { + va_list va; + query = msi_alloc( size*sizeof(WCHAR) ); + va_start(va, fmt); + res = vsnprintfW(query, size, fmt, va); + va_end(va); + if (res == -1) size *= 2; + else if (res >= size) size = res + 1; + else break; + msi_free( query ); + } + /* perform the query */ + r = MSI_DatabaseOpenViewW(db, query, view); + msi_free(query); + return r; +} + +UINT MSI_IterateRecords( MSIQUERY *view, DWORD *count, + record_func func, LPVOID param ) +{ + MSIRECORD *rec = NULL; + UINT r, n = 0, max = 0; + + r = MSI_ViewExecute( view, NULL ); + if( r != ERROR_SUCCESS ) + return r; + + if( count ) + max = *count; + + /* iterate a query */ + for( n = 0; (max == 0) || (n < max); n++ ) + { + r = MSI_ViewFetch( view, &rec ); + if( r != ERROR_SUCCESS ) + break; + if (func) + r = func( rec, param ); + msiobj_release( &rec->hdr ); + if( r != ERROR_SUCCESS ) + break; + } + + MSI_ViewClose( view ); + + if( count ) + *count = n; + + if( r == ERROR_NO_MORE_ITEMS ) + r = ERROR_SUCCESS; + + return r; +} + +/* return a single record from a query */ +MSIRECORD *MSI_QueryGetRecord( MSIDATABASE *db, LPCWSTR fmt, ... ) +{ + MSIRECORD *rec = NULL; + MSIQUERY *view = NULL; + UINT r; + int size = 100, res; + LPWSTR query; + + /* construct the string */ + for (;;) + { + va_list va; + query = msi_alloc( size*sizeof(WCHAR) ); + va_start(va, fmt); + res = vsnprintfW(query, size, fmt, va); + va_end(va); + if (res == -1) size *= 2; + else if (res >= size) size = res + 1; + else break; + msi_free( query ); + } + /* perform the query */ + r = MSI_DatabaseOpenViewW(db, query, &view); + msi_free(query); + + if( r == ERROR_SUCCESS ) + { + MSI_ViewExecute( view, NULL ); + MSI_ViewFetch( view, &rec ); + MSI_ViewClose( view ); + msiobj_release( &view->hdr ); + } + return rec; +} + +UINT WINAPI MsiDatabaseOpenViewW(MSIHANDLE hdb, + LPCWSTR szQuery, MSIHANDLE *phView) +{ + MSIDATABASE *db; + MSIQUERY *query = NULL; + UINT ret; + + TRACE("%s %p\n", debugstr_w(szQuery), phView); + + db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE ); + if( !db ) + return ERROR_INVALID_HANDLE; + + ret = MSI_DatabaseOpenViewW( db, szQuery, &query ); + if( ret == ERROR_SUCCESS ) + { + *phView = alloc_msihandle( &query->hdr ); + msiobj_release( &query->hdr ); + } + msiobj_release( &db->hdr ); + + return ret; +} + +UINT MSI_ViewFetch(MSIQUERY *query, MSIRECORD **prec) +{ + MSIVIEW *view; + MSIRECORD *rec; + UINT row_count = 0, col_count = 0, i, ival, ret, type; + + TRACE("%p %p\n", query, prec ); + + view = query->view; + if( !view ) + return ERROR_FUNCTION_FAILED; + + ret = view->ops->get_dimensions( view, &row_count, &col_count ); + if( ret ) + return ret; + if( !col_count ) + return ERROR_INVALID_PARAMETER; + + if( query->row >= row_count ) + return ERROR_NO_MORE_ITEMS; + + rec = MSI_CreateRecord( col_count ); + if( !rec ) + return ERROR_FUNCTION_FAILED; + + for( i=1; i<=col_count; i++ ) + { + ret = view->ops->get_column_info( view, i, NULL, &type ); + if( ret ) + { + ERR("Error getting column type for %d\n", i ); + continue; + } + if (!MSITYPE_IS_BINARY(type)) + { + ret = view->ops->fetch_int( view, query->row, i, &ival ); + if( ret ) + { + ERR("Error fetching data for %d\n", i ); + continue; + } + if( ! (type & MSITYPE_VALID ) ) + ERR("Invalid type!\n"); + + /* check if it's nul (0) - if so, don't set anything */ + if( !ival ) + continue; + + if( type & MSITYPE_STRING ) + { + LPWSTR sval; + + sval = MSI_makestring( query->db, ival ); + MSI_RecordSetStringW( rec, i, sval ); + msi_free( sval ); + } + else + { + if( (type & MSI_DATASIZEMASK) == 2 ) + MSI_RecordSetInteger( rec, i, ival - (1<<15) ); + else + MSI_RecordSetInteger( rec, i, ival - (1<<31) ); + } + } + else + { + IStream *stm = NULL; + + ret = view->ops->fetch_stream( view, query->row, i, &stm ); + if( ( ret == ERROR_SUCCESS ) && stm ) + { + MSI_RecordSetIStream( rec, i, stm ); + IStream_Release( stm ); + } + else + ERR("failed to get stream\n"); + } + } + query->row ++; + + *prec = rec; + + return ERROR_SUCCESS; +} + +UINT WINAPI MsiViewFetch(MSIHANDLE hView, MSIHANDLE *record) +{ + MSIQUERY *query; + MSIRECORD *rec = NULL; + UINT ret; + + TRACE("%ld %p\n", hView, record); + + query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW ); + if( !query ) + return ERROR_INVALID_HANDLE; + ret = MSI_ViewFetch( query, &rec ); + if( ret == ERROR_SUCCESS ) + { + *record = alloc_msihandle( &rec->hdr ); + msiobj_release( &rec->hdr ); + } + msiobj_release( &query->hdr ); + return ret; +} + +UINT MSI_ViewClose(MSIQUERY *query) +{ + MSIVIEW *view; + + TRACE("%p\n", query ); + + view = query->view; + if( !view ) + return ERROR_FUNCTION_FAILED; + if( !view->ops->close ) + return ERROR_FUNCTION_FAILED; + + return view->ops->close( view ); +} + +UINT WINAPI MsiViewClose(MSIHANDLE hView) +{ + MSIQUERY *query; + UINT ret; + + TRACE("%ld\n", hView ); + + query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW ); + if( !query ) + return ERROR_INVALID_HANDLE; + + ret = MSI_ViewClose( query ); + msiobj_release( &query->hdr ); + return ret; +} + +UINT MSI_ViewExecute(MSIQUERY *query, MSIRECORD *rec ) +{ + MSIVIEW *view; + + TRACE("%p %p\n", query, rec); + + view = query->view; + if( !view ) + return ERROR_FUNCTION_FAILED; + if( !view->ops->execute ) + return ERROR_FUNCTION_FAILED; + query->row = 0; + + return view->ops->execute( view, rec ); +} + +UINT WINAPI MsiViewExecute(MSIHANDLE hView, MSIHANDLE hRec) +{ + MSIQUERY *query; + MSIRECORD *rec = NULL; + UINT ret; + + TRACE("%ld %ld\n", hView, hRec); + + query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW ); + if( !query ) + return ERROR_INVALID_HANDLE; + + if( hRec ) + { + rec = msihandle2msiinfo( hRec, MSIHANDLETYPE_RECORD ); + if( !rec ) + { + ret = ERROR_INVALID_HANDLE; + goto out; + } + } + + msiobj_lock( &rec->hdr ); + ret = MSI_ViewExecute( query, rec ); + msiobj_unlock( &rec->hdr ); + +out: + msiobj_release( &query->hdr ); + if( rec ) + msiobj_release( &rec->hdr ); + + return ret; +} + +static UINT msi_set_record_type_string( MSIRECORD *rec, UINT field, UINT type ) +{ + static const WCHAR fmt[] = { '%','d',0 }; + WCHAR szType[0x10]; + + if (MSITYPE_IS_BINARY(type)) + szType[0] = 'v'; + else if (type & MSITYPE_LOCALIZABLE) + szType[0] = 'l'; + else if (type & MSITYPE_STRING) + szType[0] = 's'; + else + szType[0] = 'i'; + if (type & MSITYPE_NULLABLE) + szType[0] &= ~0x20; + + sprintfW( &szType[1], fmt, (type&0xff) ); + + TRACE("type %04x -> %s\n", type, debugstr_w(szType) ); + + return MSI_RecordSetStringW( rec, field, szType ); +} + +UINT WINAPI MsiViewGetColumnInfo(MSIHANDLE hView, MSICOLINFO info, MSIHANDLE *hRec) +{ + MSIVIEW *view = NULL; + MSIQUERY *query = NULL; + MSIRECORD *rec = NULL; + UINT r = ERROR_FUNCTION_FAILED, i, count = 0, type; + LPWSTR name; + + TRACE("%ld %d %p\n", hView, info, hRec); + + if( !hRec ) + return ERROR_INVALID_PARAMETER; + + if( info != MSICOLINFO_NAMES && info != MSICOLINFO_TYPES ) + return ERROR_INVALID_PARAMETER; + + query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW ); + if( !query ) + return ERROR_INVALID_HANDLE; + + view = query->view; + if( !view ) + goto out; + + if( !view->ops->get_dimensions ) + goto out; + + r = view->ops->get_dimensions( view, NULL, &count ); + if( r ) + goto out; + if( !count ) + { + r = ERROR_INVALID_PARAMETER; + goto out; + } + + rec = MSI_CreateRecord( count ); + if( !rec ) + { + r = ERROR_FUNCTION_FAILED; + goto out; + } + + for( i=0; iops->get_column_info( view, i+1, &name, &type ); + if( r != ERROR_SUCCESS ) + continue; + if (info == MSICOLINFO_NAMES) + MSI_RecordSetStringW( rec, i+1, name ); + else + msi_set_record_type_string( rec, i+1, type); + msi_free( name ); + } + + *hRec = alloc_msihandle( &rec->hdr ); + +out: + msiobj_release( &query->hdr ); + if( rec ) + msiobj_release( &rec->hdr ); + + return r; +} + +UINT WINAPI MsiViewModify( MSIHANDLE hView, MSIMODIFY eModifyMode, + MSIHANDLE hRecord) +{ + MSIVIEW *view = NULL; + MSIQUERY *query = NULL; + MSIRECORD *rec = NULL; + UINT r = ERROR_FUNCTION_FAILED; + + TRACE("%ld %x %ld\n", hView, eModifyMode, hRecord); + + query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW ); + if( !query ) + return ERROR_INVALID_HANDLE; + + view = query->view; + if( !view ) + goto out; + + if( !view->ops->modify ) + goto out; + + rec = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD ); + if( !rec ) + { + r = ERROR_INVALID_HANDLE; + goto out; + } + + r = view->ops->modify( view, eModifyMode, rec ); + +out: + msiobj_release( &query->hdr ); + if( rec ) + msiobj_release( &rec->hdr ); + + return r; +} + +MSIDBERROR WINAPI MsiViewGetErrorW( MSIHANDLE handle, LPWSTR szColumnNameBuffer, + DWORD *pcchBuf ) +{ + MSIQUERY *query = NULL; + static const WCHAR szError[] = { 0 }; + MSIDBERROR r = MSIDBERROR_NOERROR; + int len; + + FIXME("%ld %p %p - returns empty error string\n", + handle, szColumnNameBuffer, pcchBuf ); + + if( !pcchBuf ) + return MSIDBERROR_INVALIDARG; + + query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW ); + if( !query ) + return MSIDBERROR_INVALIDARG; + + len = lstrlenW( szError ); + if( szColumnNameBuffer ) + { + if( *pcchBuf > len ) + lstrcpyW( szColumnNameBuffer, szError ); + else + r = MSIDBERROR_MOREDATA; + } + *pcchBuf = len; + + msiobj_release( &query->hdr ); + return r; +} + +MSIDBERROR WINAPI MsiViewGetErrorA( MSIHANDLE handle, LPSTR szColumnNameBuffer, + DWORD *pcchBuf ) +{ + static const CHAR szError[] = { 0 }; + MSIQUERY *query = NULL; + MSIDBERROR r = MSIDBERROR_NOERROR; + int len; + + FIXME("%ld %p %p - returns empty error string\n", + handle, szColumnNameBuffer, pcchBuf ); + + if( !pcchBuf ) + return MSIDBERROR_INVALIDARG; + + query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW ); + if( !query ) + return MSIDBERROR_INVALIDARG; + + len = lstrlenA( szError ); + if( szColumnNameBuffer ) + { + if( *pcchBuf > len ) + lstrcpyA( szColumnNameBuffer, szError ); + else + r = MSIDBERROR_MOREDATA; + } + *pcchBuf = len; + + msiobj_release( &query->hdr ); + return r; +} + +MSIHANDLE WINAPI MsiGetLastErrorRecord( void ) +{ + FIXME("\n"); + return 0; +} + +DEFINE_GUID( CLSID_MsiTransform, 0x000c1082, 0x0000, 0x0000, 0xc0,0x00,0x00,0x00,0x00,0x00,0x00,0x46); + +UINT MSI_DatabaseApplyTransformW( MSIDATABASE *db, + LPCWSTR szTransformFile, int iErrorCond ) +{ + UINT r; + IStorage *stg = NULL; + + TRACE("%p %s %d\n", db, debugstr_w(szTransformFile), iErrorCond); + + r = StgOpenStorage( szTransformFile, NULL, + STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg); + if( r ) + return r; + + if( TRACE_ON( msi ) ) + enum_stream_names( stg ); + + r = msi_table_apply_transform( db, stg ); + + IStorage_Release( stg ); + + return r; +} + +UINT WINAPI MsiDatabaseApplyTransformW( MSIHANDLE hdb, + LPCWSTR szTransformFile, int iErrorCond) +{ + MSIDATABASE *db; + UINT r; + + db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE ); + if( !db ) + return ERROR_INVALID_HANDLE; + + r = MSI_DatabaseApplyTransformW( db, szTransformFile, iErrorCond ); + msiobj_release( &db->hdr ); + return r; +} + +UINT WINAPI MsiDatabaseApplyTransformA( MSIHANDLE hdb, + LPCSTR szTransformFile, int iErrorCond) +{ + LPWSTR wstr; + UINT ret; + + TRACE("%ld %s %d\n", hdb, debugstr_a(szTransformFile), iErrorCond); + + wstr = strdupAtoW( szTransformFile ); + if( szTransformFile && !wstr ) + return ERROR_NOT_ENOUGH_MEMORY; + + ret = MsiDatabaseApplyTransformW( hdb, wstr, iErrorCond); + + msi_free( wstr ); + + return ret; +} + +UINT WINAPI MsiDatabaseGenerateTransformA( MSIHANDLE hdb, MSIHANDLE hdbref, + LPCSTR szTransformFile, int iReserved1, int iReserved2 ) +{ + FIXME("%ld %ld %s %d %d\n", hdb, hdbref, + debugstr_a(szTransformFile), iReserved1, iReserved2); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiDatabaseGenerateTransformW( MSIHANDLE hdb, MSIHANDLE hdbref, + LPCWSTR szTransformFile, int iReserved1, int iReserved2 ) +{ + FIXME("%ld %ld %s %d %d\n", hdb, hdbref, + debugstr_w(szTransformFile), iReserved1, iReserved2); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiDatabaseCommit( MSIHANDLE hdb ) +{ + MSIDATABASE *db; + UINT r; + + TRACE("%ld\n", hdb); + + db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE ); + if( !db ) + return ERROR_INVALID_HANDLE; + + /* FIXME: lock the database */ + + r = MSI_CommitTables( db ); + + /* FIXME: unlock the database */ + + msiobj_release( &db->hdr ); + + return r; +} + +struct msi_primary_key_record_info +{ + DWORD n; + MSIRECORD *rec; +}; + +static UINT msi_primary_key_iterator( MSIRECORD *rec, LPVOID param ) +{ + struct msi_primary_key_record_info *info = param; + LPCWSTR name; + DWORD type; + + type = MSI_RecordGetInteger( rec, 4 ); + if( type & MSITYPE_KEY ) + { + info->n++; + if( info->rec ) + { + name = MSI_RecordGetString( rec, 3 ); + MSI_RecordSetStringW( info->rec, info->n, name ); + } + } + + return ERROR_SUCCESS; +} + +UINT MSI_DatabaseGetPrimaryKeys( MSIDATABASE *db, + LPCWSTR table, MSIRECORD **prec ) +{ + static const WCHAR sql[] = { + 's','e','l','e','c','t',' ','*',' ', + 'f','r','o','m',' ','`','_','C','o','l','u','m','n','s','`',' ', + 'w','h','e','r','e',' ', + '`','T','a','b','l','e','`',' ','=',' ','\'','%','s','\'',0 }; + struct msi_primary_key_record_info info; + MSIQUERY *query = NULL; + MSIVIEW *view; + UINT r; + + r = MSI_OpenQuery( db, &query, sql, table ); + if( r != ERROR_SUCCESS ) + return r; + + view = query->view; + + /* count the number of primary key records */ + info.n = 0; + info.rec = 0; + r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info ); + if( r == ERROR_SUCCESS ) + { + TRACE("Found %ld primary keys\n", info.n ); + + /* allocate a record and fill in the names of the tables */ + info.rec = MSI_CreateRecord( info.n ); + info.n = 0; + r = MSI_IterateRecords( query, 0, msi_primary_key_iterator, &info ); + if( r == ERROR_SUCCESS ) + *prec = info.rec; + else + msiobj_release( &info.rec->hdr ); + } + msiobj_release( &query->hdr ); + + return r; +} + +UINT WINAPI MsiDatabaseGetPrimaryKeysW( MSIHANDLE hdb, + LPCWSTR table, MSIHANDLE* phRec ) +{ + MSIRECORD *rec = NULL; + MSIDATABASE *db; + UINT r; + + TRACE("%ld %s %p\n", hdb, debugstr_w(table), phRec); + + db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE ); + if( !db ) + return ERROR_INVALID_HANDLE; + + r = MSI_DatabaseGetPrimaryKeys( db, table, &rec ); + if( r == ERROR_SUCCESS ) + { + *phRec = alloc_msihandle( &rec->hdr ); + msiobj_release( &rec->hdr ); + } + msiobj_release( &db->hdr ); + + return r; +} + +UINT WINAPI MsiDatabaseGetPrimaryKeysA(MSIHANDLE hdb, + LPCSTR table, MSIHANDLE* phRec) +{ + LPWSTR szwTable = NULL; + UINT r; + + TRACE("%ld %s %p\n", hdb, debugstr_a(table), phRec); + + if( table ) + { + szwTable = strdupAtoW( table ); + if( !szwTable ) + return ERROR_OUTOFMEMORY; + } + r = MsiDatabaseGetPrimaryKeysW( hdb, szwTable, phRec ); + msi_free( szwTable ); + + return r; +} + +UINT WINAPI MsiDatabaseIsTablePersistentA( + MSIHANDLE hDatabase, LPSTR szTableName) +{ + FIXME("%lx %s\n", hDatabase, debugstr_a(szTableName)); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiDatabaseIsTablePersistentW( + MSIHANDLE hDatabase, LPWSTR szTableName) +{ + FIXME("%lx %s\n", hDatabase, debugstr_w(szTableName)); + return ERROR_CALL_NOT_IMPLEMENTED; +} diff --git a/reactos/dll/msi/order.c b/reactos/dll/msi/order.c new file mode 100644 index 00000000000..27da7c64770 --- /dev/null +++ b/reactos/dll/msi/order.c @@ -0,0 +1,337 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002 Mike McCormack 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" +#include "winnls.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msidb); + + +/* below is the query interface to a table */ + +typedef struct tagMSIORDERVIEW +{ + MSIVIEW view; + MSIDATABASE *db; + MSIVIEW *table; + UINT *reorder; + UINT num_cols; + UINT cols[1]; +} MSIORDERVIEW; + +static UINT ORDER_compare( MSIORDERVIEW *ov, UINT a, UINT b, UINT *swap ) +{ + UINT r, i, a_val = 0, b_val = 0; + + *swap = 0; + for( i=0; inum_cols; i++ ) + { + r = ov->table->ops->fetch_int( ov->table, a, ov->cols[i], &a_val ); + if( r != ERROR_SUCCESS ) + return r; + + r = ov->table->ops->fetch_int( ov->table, b, ov->cols[i], &b_val ); + if( r != ERROR_SUCCESS ) + return r; + + if( a_val != b_val ) + { + if( a_val > b_val ) + *swap = 1; + break; + } + } + + return ERROR_SUCCESS; +} + +static UINT ORDER_mergesort( MSIORDERVIEW *ov, UINT left, UINT right ) +{ + UINT r, centre = (left + right)/2, temp, swap = 0, i, j; + UINT *array = ov->reorder; + + if( left == right ) + return ERROR_SUCCESS; + + /* sort the left half */ + r = ORDER_mergesort( ov, left, centre ); + if( r != ERROR_SUCCESS ) + return r; + + /* sort the right half */ + r = ORDER_mergesort( ov, centre+1, right ); + if( r != ERROR_SUCCESS ) + return r; + + for( i=left, j=centre+1; (i<=centre) && (j<=right); i++ ) + { + r = ORDER_compare( ov, array[i], array[j], &swap ); + if( r != ERROR_SUCCESS ) + return r; + if( swap ) + { + temp = array[j]; + memmove( &array[i+1], &array[i], (j-i)*sizeof (UINT) ); + array[i] = temp; + j++; + centre++; + } + } + + return ERROR_SUCCESS; +} + +static UINT ORDER_verify( MSIORDERVIEW *ov, UINT num_rows ) +{ + UINT i, swap, r; + + for( i=1; ireorder[i-1], ov->reorder[i], &swap ); + if( r != ERROR_SUCCESS ) + return r; + if( !swap ) + continue; + ERR("Bad order! %d\n", i); + return ERROR_FUNCTION_FAILED; + } + + return ERROR_SUCCESS; +} + +static UINT ORDER_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ) +{ + MSIORDERVIEW *ov = (MSIORDERVIEW*)view; + + TRACE("%p %d %d %p\n", ov, row, col, val ); + + if( !ov->table ) + return ERROR_FUNCTION_FAILED; + + row = ov->reorder[ row ]; + + return ov->table->ops->fetch_int( ov->table, row, col, val ); +} + +static UINT ORDER_execute( struct tagMSIVIEW *view, MSIRECORD *record ) +{ + MSIORDERVIEW *ov = (MSIORDERVIEW*)view; + UINT r, num_rows = 0, i; + + TRACE("%p %p\n", ov, record); + + if( !ov->table ) + return ERROR_FUNCTION_FAILED; + + r = ov->table->ops->execute( ov->table, record ); + if( r != ERROR_SUCCESS ) + return r; + + r = ov->table->ops->get_dimensions( ov->table, &num_rows, NULL ); + if( r != ERROR_SUCCESS ) + return r; + + ov->reorder = msi_alloc( num_rows*sizeof(UINT) ); + if( !ov->reorder ) + return ERROR_FUNCTION_FAILED; + + for( i=0; ireorder[i] = i; + + r = ORDER_mergesort( ov, 0, num_rows - 1 ); + if( r != ERROR_SUCCESS ) + return r; + + r = ORDER_verify( ov, num_rows ); + if( r != ERROR_SUCCESS ) + return r; + + return ERROR_SUCCESS; +} + +static UINT ORDER_close( struct tagMSIVIEW *view ) +{ + MSIORDERVIEW *ov = (MSIORDERVIEW*)view; + + TRACE("%p\n", ov ); + + if( !ov->table ) + return ERROR_FUNCTION_FAILED; + + msi_free( ov->reorder ); + ov->reorder = NULL; + + return ov->table->ops->close( ov->table ); +} + +static UINT ORDER_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols ) +{ + MSIORDERVIEW *ov = (MSIORDERVIEW*)view; + + TRACE("%p %p %p\n", ov, rows, cols ); + + if( !ov->table ) + return ERROR_FUNCTION_FAILED; + + return ov->table->ops->get_dimensions( ov->table, rows, cols ); +} + +static UINT ORDER_get_column_info( struct tagMSIVIEW *view, + UINT n, LPWSTR *name, UINT *type ) +{ + MSIORDERVIEW *ov = (MSIORDERVIEW*)view; + + TRACE("%p %d %p %p\n", ov, n, name, type ); + + if( !ov->table ) + return ERROR_FUNCTION_FAILED; + + return ov->table->ops->get_column_info( ov->table, n, name, type ); +} + +static UINT ORDER_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, + MSIRECORD *rec ) +{ + MSIORDERVIEW *ov = (MSIORDERVIEW*)view; + + TRACE("%p %d %p\n", ov, eModifyMode, rec ); + + if( !ov->table ) + return ERROR_FUNCTION_FAILED; + + return ov->table->ops->modify( ov->table, eModifyMode, rec ); +} + +static UINT ORDER_delete( struct tagMSIVIEW *view ) +{ + MSIORDERVIEW *ov = (MSIORDERVIEW*)view; + + TRACE("%p\n", ov ); + + if( ov->table ) + ov->table->ops->delete( ov->table ); + + msi_free( ov->reorder ); + ov->reorder = NULL; + + msiobj_release( &ov->db->hdr ); + msi_free( ov ); + + return ERROR_SUCCESS; +} + + +MSIVIEWOPS order_ops = +{ + ORDER_fetch_int, + NULL, + NULL, + NULL, + ORDER_execute, + ORDER_close, + ORDER_get_dimensions, + ORDER_get_column_info, + ORDER_modify, + ORDER_delete +}; + +static UINT ORDER_AddColumn( MSIORDERVIEW *ov, LPCWSTR name ) +{ + UINT n, count, r; + MSIVIEW *table; + + TRACE("%p adding %s\n", ov, debugstr_w( name ) ); + + if( ov->view.ops != &order_ops ) + return ERROR_FUNCTION_FAILED; + + table = ov->table; + if( !table ) + return ERROR_FUNCTION_FAILED; + if( !table->ops->get_dimensions ) + return ERROR_FUNCTION_FAILED; + if( !table->ops->get_column_info ) + return ERROR_FUNCTION_FAILED; + + r = table->ops->get_dimensions( table, NULL, &count ); + if( r != ERROR_SUCCESS ) + return r; + + if( ov->num_cols >= count ) + return ERROR_FUNCTION_FAILED; + + r = VIEW_find_column( table, name, &n ); + if( r != ERROR_SUCCESS ) + return r; + + ov->cols[ov->num_cols] = n; + TRACE("Ordering by column %s (%d)\n", debugstr_w( name ), n); + + ov->num_cols++; + + return ERROR_SUCCESS; +} + +UINT ORDER_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table, + column_info *columns ) +{ + MSIORDERVIEW *ov = NULL; + UINT count = 0, r; + column_info *x; + + TRACE("%p\n", ov ); + + r = table->ops->get_dimensions( table, NULL, &count ); + if( r != ERROR_SUCCESS ) + { + ERR("can't get table dimensions\n"); + return r; + } + + ov = msi_alloc_zero( sizeof *ov + sizeof (UINT) * count ); + if( !ov ) + return ERROR_FUNCTION_FAILED; + + /* fill the structure */ + ov->view.ops = &order_ops; + msiobj_addref( &db->hdr ); + ov->db = db; + ov->table = table; + ov->reorder = NULL; + ov->num_cols = 0; + *view = (MSIVIEW*) ov; + + for( x = columns; x ; x = x->next ) + ORDER_AddColumn( ov, x->column ); + + return ERROR_SUCCESS; +} diff --git a/reactos/dll/msi/package.c b/reactos/dll/msi/package.c new file mode 100644 index 00000000000..a77f853c6cf --- /dev/null +++ b/reactos/dll/msi/package.c @@ -0,0 +1,904 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2004 Aric Stewart 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define NONAMELESSUNION + +#include +#include +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winnls.h" +#include "shlwapi.h" +#include "wingdi.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "objidl.h" +#include "wincrypt.h" +#include "winuser.h" +#include "shlobj.h" +#include "wine/unicode.h" +#include "objbase.h" + +#include "msipriv.h" +#include "action.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +static void MSI_FreePackage( MSIOBJECTHDR *arg) +{ + MSIPACKAGE *package= (MSIPACKAGE*) arg; + + if( package->dialog ) + msi_dialog_destroy( package->dialog ); + ACTION_free_package_structures(package); + + msiobj_release( &package->db->hdr ); +} + +static UINT clone_properties(MSIDATABASE *db) +{ + MSIQUERY * view = NULL; + UINT rc; + static const WCHAR CreateSql[] = { + 'C','R','E','A','T','E',' ','T','A','B','L','E',' ','`','_','P','r','o', + 'p','e','r','t','y','`',' ','(',' ','`','_','P','r','o','p','e','r','t', + 'y','`',' ','C','H','A','R','(','5','6',')',' ','N','O','T',' ','N','U', + 'L','L',',',' ','`','V','a','l','u','e','`',' ','C','H','A','R','(','9', + '8',')',' ','N','O','T',' ','N','U','L','L',' ','P','R','I','M','A','R', + 'Y',' ','K','E','Y',' ','`','_','P','r','o','p','e','r','t','y','`',')',0}; + static const WCHAR Query[] = { + 'S','E','L','E','C','T',' ','*',' ', + 'F','R','O','M',' ','`','P','r','o','p','e','r','t','y','`',0}; + static const WCHAR Insert[] = { + 'I','N','S','E','R','T',' ','i','n','t','o',' ', + '`','_','P','r','o','p','e','r','t','y','`',' ', + '(','`','_','P','r','o','p','e','r','t','y','`',',', + '`','V','a','l','u','e','`',')',' ', + 'V','A','L','U','E','S',' ','(','?',',','?',')',0}; + + /* create the temporary properties table */ + rc = MSI_DatabaseOpenViewW(db, CreateSql, &view); + if (rc != ERROR_SUCCESS) + return rc; + rc = MSI_ViewExecute(view,0); + MSI_ViewClose(view); + msiobj_release(&view->hdr); + if (rc != ERROR_SUCCESS) + return rc; + + /* clone the existing properties */ + rc = MSI_DatabaseOpenViewW(db, Query, &view); + if (rc != ERROR_SUCCESS) + return rc; + + rc = MSI_ViewExecute(view, 0); + if (rc != ERROR_SUCCESS) + { + MSI_ViewClose(view); + msiobj_release(&view->hdr); + return rc; + } + while (1) + { + MSIRECORD * row; + MSIQUERY * view2; + + rc = MSI_ViewFetch(view,&row); + if (rc != ERROR_SUCCESS) + break; + + rc = MSI_DatabaseOpenViewW(db,Insert,&view2); + if (rc!= ERROR_SUCCESS) + continue; + rc = MSI_ViewExecute(view2,row); + MSI_ViewClose(view2); + msiobj_release(&view2->hdr); + + if (rc == ERROR_SUCCESS) + msiobj_release(&row->hdr); + } + MSI_ViewClose(view); + msiobj_release(&view->hdr); + + return rc; +} + +/* + * set_installed_prop + * + * Sets the "Installed" property to indicate that + * the product is installed for the current user. + */ +static UINT set_installed_prop( MSIPACKAGE *package ) +{ + static const WCHAR szInstalled[] = { + 'I','n','s','t','a','l','l','e','d',0 }; + WCHAR val[2] = { '1', 0 }; + HKEY hkey = 0; + UINT r; + + r = MSIREG_OpenUninstallKey( package->ProductCode, &hkey, FALSE ); + if (r == ERROR_SUCCESS) + { + RegCloseKey( hkey ); + MSI_SetPropertyW( package, szInstalled, val ); + } + + return r; +} + +/* + * There are a whole slew of these we need to set + * + * +http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/properties.asp + */ +static VOID set_installer_properties(MSIPACKAGE *package) +{ + WCHAR pth[MAX_PATH]; + WCHAR *ptr; + OSVERSIONINFOA OSVersion; + MEMORYSTATUSEX msex; + DWORD verval; + WCHAR verstr[10], bufstr[20]; + HDC dc; + + static const WCHAR cszbs[]={'\\',0}; + static const WCHAR CFF[] = +{'C','o','m','m','o','n','F','i','l','e','s','F','o','l','d','e','r',0}; + static const WCHAR PFF[] = +{'P','r','o','g','r','a','m','F','i','l','e','s','F','o','l','d','e','r',0}; + static const WCHAR CADF[] = +{'C','o','m','m','o','n','A','p','p','D','a','t','a','F','o','l','d','e','r',0}; + static const WCHAR FaF[] = +{'F','a','v','o','r','i','t','e','s','F','o','l','d','e','r',0}; + static const WCHAR FoF[] = +{'F','o','n','t','s','F','o','l','d','e','r',0}; + static const WCHAR SendTF[] = +{'S','e','n','d','T','o','F','o','l','d','e','r',0}; + static const WCHAR SMF[] = +{'S','t','a','r','t','M','e','n','u','F','o','l','d','e','r',0}; + static const WCHAR StF[] = +{'S','t','a','r','t','u','p','F','o','l','d','e','r',0}; + static const WCHAR TemplF[] = +{'T','e','m','p','l','a','t','e','F','o','l','d','e','r',0}; + static const WCHAR DF[] = +{'D','e','s','k','t','o','p','F','o','l','d','e','r',0}; + static const WCHAR PMF[] = +{'P','r','o','g','r','a','m','M','e','n','u','F','o','l','d','e','r',0}; + static const WCHAR ATF[] = +{'A','d','m','i','n','T','o','o','l','s','F','o','l','d','e','r',0}; + static const WCHAR ADF[] = +{'A','p','p','D','a','t','a','F','o','l','d','e','r',0}; + static const WCHAR SF[] = +{'S','y','s','t','e','m','F','o','l','d','e','r',0}; + static const WCHAR SF16[] = +{'S','y','s','t','e','m','1','6','F','o','l','d','e','r',0}; + static const WCHAR LADF[] = +{'L','o','c','a','l','A','p','p','D','a','t','a','F','o','l','d','e','r',0}; + static const WCHAR MPF[] = +{'M','y','P','i','c','t','u','r','e','s','F','o','l','d','e','r',0}; + static const WCHAR PF[] = +{'P','e','r','s','o','n','a','l','F','o','l','d','e','r',0}; + static const WCHAR WF[] = +{'W','i','n','d','o','w','s','F','o','l','d','e','r',0}; + static const WCHAR WV[] = +{'W','i','n','d','o','w','s','V','o','l','u','m','e',0}; + static const WCHAR TF[]= +{'T','e','m','p','F','o','l','d','e','r',0}; + static const WCHAR szAdminUser[] = +{'A','d','m','i','n','U','s','e','r',0}; + static const WCHAR szPriv[] = +{'P','r','i','v','i','l','e','g','e','d',0}; + static const WCHAR szOne[] = +{'1',0}; + static const WCHAR v9x[] = { 'V','e','r','s','i','o','n','9','X',0 }; + static const WCHAR vNT[] = { 'V','e','r','s','i','o','n','N','T',0 }; + static const WCHAR szFormat[] = {'%','l','i',0}; + static const WCHAR szWinBuild[] = +{'W','i','n','d','o','w','s','B','u','i','l','d', 0 }; + static const WCHAR szSPL[] = +{'S','e','r','v','i','c','e','P','a','c','k','L','e','v','e','l',0 }; + static const WCHAR szSix[] = {'6',0 }; + + static const WCHAR szVersionMsi[] = { 'V','e','r','s','i','o','n','M','s','i',0 }; + static const WCHAR szPhysicalMemory[] = { 'P','h','y','s','i','c','a','l','M','e','m','o','r','y',0 }; + static const WCHAR szFormat2[] = {'%','l','i','.','%','l','i',0}; +/* Screen properties */ + static const WCHAR szScreenX[] = {'S','c','r','e','e','n','X',0}; + static const WCHAR szScreenY[] = {'S','c','r','e','e','n','Y',0}; + static const WCHAR szColorBits[] = {'C','o','l','o','r','B','i','t','s',0}; + static const WCHAR szScreenFormat[] = {'%','d',0}; + static const WCHAR szIntel[] = { 'I','n','t','e','l',0 }; + SYSTEM_INFO sys_info; + + /* + * Other things that probably should be set: + * + * SystemLanguageID ComputerName UserLanguageID LogonUser VirtualMemory + * Intel ShellAdvSupport DefaultUIFont VersionDatabase PackagecodeChanging + * ProductState CaptionHeight BorderTop BorderSide TextHeight + * RedirectedDllSupport Time Date Privileged + */ + + SHGetFolderPathW(NULL,CSIDL_PROGRAM_FILES_COMMON,NULL,0,pth); + strcatW(pth,cszbs); + MSI_SetPropertyW(package, CFF, pth); + + SHGetFolderPathW(NULL,CSIDL_PROGRAM_FILES,NULL,0,pth); + strcatW(pth,cszbs); + MSI_SetPropertyW(package, PFF, pth); + + SHGetFolderPathW(NULL,CSIDL_COMMON_APPDATA,NULL,0,pth); + strcatW(pth,cszbs); + MSI_SetPropertyW(package, CADF, pth); + + SHGetFolderPathW(NULL,CSIDL_FAVORITES,NULL,0,pth); + strcatW(pth,cszbs); + MSI_SetPropertyW(package, FaF, pth); + + SHGetFolderPathW(NULL,CSIDL_FONTS,NULL,0,pth); + strcatW(pth,cszbs); + MSI_SetPropertyW(package, FoF, pth); + + SHGetFolderPathW(NULL,CSIDL_SENDTO,NULL,0,pth); + strcatW(pth,cszbs); + MSI_SetPropertyW(package, SendTF, pth); + + SHGetFolderPathW(NULL,CSIDL_STARTMENU,NULL,0,pth); + strcatW(pth,cszbs); + MSI_SetPropertyW(package, SMF, pth); + + SHGetFolderPathW(NULL,CSIDL_STARTUP,NULL,0,pth); + strcatW(pth,cszbs); + MSI_SetPropertyW(package, StF, pth); + + SHGetFolderPathW(NULL,CSIDL_TEMPLATES,NULL,0,pth); + strcatW(pth,cszbs); + MSI_SetPropertyW(package, TemplF, pth); + + SHGetFolderPathW(NULL,CSIDL_DESKTOP,NULL,0,pth); + strcatW(pth,cszbs); + MSI_SetPropertyW(package, DF, pth); + + SHGetFolderPathW(NULL,CSIDL_PROGRAMS,NULL,0,pth); + strcatW(pth,cszbs); + MSI_SetPropertyW(package, PMF, pth); + + SHGetFolderPathW(NULL,CSIDL_ADMINTOOLS,NULL,0,pth); + strcatW(pth,cszbs); + MSI_SetPropertyW(package, ATF, pth); + + SHGetFolderPathW(NULL,CSIDL_APPDATA,NULL,0,pth); + strcatW(pth,cszbs); + MSI_SetPropertyW(package, ADF, pth); + + SHGetFolderPathW(NULL,CSIDL_SYSTEM,NULL,0,pth); + strcatW(pth,cszbs); + MSI_SetPropertyW(package, SF, pth); + MSI_SetPropertyW(package, SF16, pth); + + SHGetFolderPathW(NULL,CSIDL_LOCAL_APPDATA,NULL,0,pth); + strcatW(pth,cszbs); + MSI_SetPropertyW(package, LADF, pth); + + SHGetFolderPathW(NULL,CSIDL_MYPICTURES,NULL,0,pth); + strcatW(pth,cszbs); + MSI_SetPropertyW(package, MPF, pth); + + SHGetFolderPathW(NULL,CSIDL_PERSONAL,NULL,0,pth); + strcatW(pth,cszbs); + MSI_SetPropertyW(package, PF, pth); + + SHGetFolderPathW(NULL,CSIDL_WINDOWS,NULL,0,pth); + strcatW(pth,cszbs); + MSI_SetPropertyW(package, WF, pth); + + /* Physical Memory is specified in MB. Using total amount. */ + msex.dwLength = sizeof(msex); + GlobalMemoryStatusEx( &msex ); + sprintfW( bufstr, szScreenFormat, (int)(msex.ullTotalPhys/1024/1024)); + MSI_SetPropertyW(package, szPhysicalMemory, bufstr); + + SHGetFolderPathW(NULL,CSIDL_WINDOWS,NULL,0,pth); + ptr = strchrW(pth,'\\'); + if (ptr) + *(ptr+1) = 0; + MSI_SetPropertyW(package, WV, pth); + + GetTempPathW(MAX_PATH,pth); + MSI_SetPropertyW(package, TF, pth); + + + /* in a wine environment the user is always admin and privileged */ + MSI_SetPropertyW(package,szAdminUser,szOne); + MSI_SetPropertyW(package,szPriv,szOne); + + /* set the os things */ + OSVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA); + GetVersionExA(&OSVersion); + verval = OSVersion.dwMinorVersion+OSVersion.dwMajorVersion*100; + sprintfW(verstr,szFormat,verval); + switch (OSVersion.dwPlatformId) + { + case VER_PLATFORM_WIN32_WINDOWS: + MSI_SetPropertyW(package,v9x,verstr); + break; + case VER_PLATFORM_WIN32_NT: + MSI_SetPropertyW(package,vNT,verstr); + break; + } + sprintfW(verstr,szFormat,OSVersion.dwBuildNumber); + MSI_SetPropertyW(package,szWinBuild,verstr); + /* just fudge this */ + MSI_SetPropertyW(package,szSPL,szSix); + + sprintfW( bufstr, szFormat2, MSI_MAJORVERSION, MSI_MINORVERSION); + MSI_SetPropertyW( package, szVersionMsi, bufstr ); + + GetSystemInfo( &sys_info ); + if (sys_info.u.s.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_INTEL) + { + sprintfW( bufstr, szScreenFormat, sys_info.wProcessorLevel ); + MSI_SetPropertyW( package, szIntel, bufstr ); + } + + /* Screen properties. */ + dc = GetDC(0); + sprintfW( bufstr, szScreenFormat, GetDeviceCaps( dc, HORZRES ) ); + MSI_SetPropertyW( package, szScreenX, bufstr ); + sprintfW( bufstr, szScreenFormat, GetDeviceCaps( dc, VERTRES )); + MSI_SetPropertyW( package, szScreenY, bufstr ); + sprintfW( bufstr, szScreenFormat, GetDeviceCaps( dc, BITSPIXEL )); + MSI_SetPropertyW( package, szColorBits, bufstr ); + ReleaseDC(0, dc); +} + +MSIPACKAGE *MSI_CreatePackage( MSIDATABASE *db ) +{ + static const WCHAR szLevel[] = { 'U','I','L','e','v','e','l',0 }; + static const WCHAR szpi[] = {'%','i',0}; + static const WCHAR szProductCode[] = { + 'P','r','o','d','u','c','t','C','o','d','e',0}; + MSIPACKAGE *package = NULL; + WCHAR uilevel[10]; + + TRACE("%p\n", db); + + package = alloc_msiobject( MSIHANDLETYPE_PACKAGE, sizeof (MSIPACKAGE), + MSI_FreePackage ); + if( package ) + { + msiobj_addref( &db->hdr ); + + package->db = db; + list_init( &package->components ); + list_init( &package->features ); + list_init( &package->files ); + list_init( &package->tempfiles ); + list_init( &package->folders ); + package->ActionFormat = NULL; + package->LastAction = NULL; + package->dialog = NULL; + package->next_dialog = NULL; + list_init( &package->subscriptions ); + list_init( &package->appids ); + list_init( &package->classes ); + list_init( &package->mimes ); + list_init( &package->extensions ); + list_init( &package->progids ); + list_init( &package->RunningActions ); + + /* OK, here is where we do a slew of things to the database to + * prep for all that is to come as a package */ + + clone_properties(db); + set_installer_properties(package); + sprintfW(uilevel,szpi,gUILevel); + MSI_SetPropertyW(package, szLevel, uilevel); + + package->ProductCode = msi_dup_property( package, szProductCode ); + set_installed_prop( package ); + } + + return package; +} + +/* + * copy_package_to_temp [internal] + * + * copy the msi file to a temp file to prevent locking a CD + * with a multi disc install + * + * FIXME: I think this is wrong, and instead of copying the package, + * we should read all the tables to memory, then open the + * database to read binary streams on demand. + */ +static LPCWSTR copy_package_to_temp( LPCWSTR szPackage, LPWSTR filename ) +{ + WCHAR path[MAX_PATH]; + static const WCHAR szMSI[] = {'M','S','I',0}; + + GetTempPathW( MAX_PATH, path ); + GetTempFileNameW( path, szMSI, 0, filename ); + + if( !CopyFileW( szPackage, filename, FALSE ) ) + { + ERR("failed to copy package to temp path %s\n", debugstr_w(filename) ); + return szPackage; + } + + TRACE("Opening relocated package %s\n", debugstr_w( filename )); + return filename; +} + +UINT MSI_OpenPackageW(LPCWSTR szPackage, MSIPACKAGE **pPackage) +{ + MSIDATABASE *db = NULL; + MSIPACKAGE *package; + MSIHANDLE handle; + UINT r; + + TRACE("%s %p\n", debugstr_w(szPackage), pPackage); + + if( szPackage[0] == '#' ) + { + handle = atoiW(&szPackage[1]); + db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE ); + if( !db ) + return ERROR_INVALID_HANDLE; + } + else + { + WCHAR temppath[MAX_PATH]; + LPCWSTR file = copy_package_to_temp( szPackage, temppath ); + + r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &db ); + + if (file != szPackage) + DeleteFileW( file ); + + if( r != ERROR_SUCCESS ) + return r; + } + + package = MSI_CreatePackage( db ); + msiobj_release( &db->hdr ); + if( !package ) + return ERROR_FUNCTION_FAILED; + + /* + * FIXME: I don't think this is right. Maybe we should be storing the + * name of the database in the MSIDATABASE structure and fetching this + * info from there, or maybe this is only relevant to cached databases. + */ + if( szPackage[0] != '#' ) + { + static const WCHAR OriginalDatabase[] = + {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0}; + static const WCHAR Database[] = {'D','A','T','A','B','A','S','E',0}; + + MSI_SetPropertyW( package, OriginalDatabase, szPackage ); + MSI_SetPropertyW( package, Database, szPackage ); + } + + *pPackage = package; + + return ERROR_SUCCESS; +} + +UINT WINAPI MsiOpenPackageExW(LPCWSTR szPackage, DWORD dwOptions, MSIHANDLE *phPackage) +{ + MSIPACKAGE *package = NULL; + UINT ret; + + TRACE("%s %08lx %p\n", debugstr_w(szPackage), dwOptions, phPackage ); + + if( dwOptions ) + FIXME("dwOptions %08lx not supported\n", dwOptions); + + ret = MSI_OpenPackageW( szPackage, &package ); + if( ret == ERROR_SUCCESS ) + { + *phPackage = alloc_msihandle( &package->hdr ); + msiobj_release( &package->hdr ); + } + + return ret; +} + +UINT WINAPI MsiOpenPackageW(LPCWSTR szPackage, MSIHANDLE *phPackage) +{ + return MsiOpenPackageExW( szPackage, 0, phPackage ); +} + +UINT WINAPI MsiOpenPackageExA(LPCSTR szPackage, DWORD dwOptions, MSIHANDLE *phPackage) +{ + LPWSTR szwPack = NULL; + UINT ret; + + if( szPackage ) + { + szwPack = strdupAtoW( szPackage ); + if( !szwPack ) + return ERROR_OUTOFMEMORY; + } + + ret = MsiOpenPackageExW( szwPack, dwOptions, phPackage ); + + msi_free( szwPack ); + + return ret; +} + +UINT WINAPI MsiOpenPackageA(LPCSTR szPackage, MSIHANDLE *phPackage) +{ + return MsiOpenPackageExA( szPackage, 0, phPackage ); +} + +MSIHANDLE WINAPI MsiGetActiveDatabase(MSIHANDLE hInstall) +{ + MSIPACKAGE *package; + MSIHANDLE handle = 0; + + TRACE("(%ld)\n",hInstall); + + package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE); + if( package) + { + handle = alloc_msihandle( &package->db->hdr ); + msiobj_release( &package->hdr ); + } + + return handle; +} + +INT MSI_ProcessMessage( MSIPACKAGE *package, INSTALLMESSAGE eMessageType, + MSIRECORD *record) +{ + DWORD log_type = 0; + LPWSTR message; + DWORD sz; + DWORD total_size = 0; + INT msg_field=1; + INT i; + INT rc; + char *msg; + int len; + + TRACE("%x\n", eMessageType); + rc = 0; + + if ((eMessageType & 0xff000000) == INSTALLMESSAGE_ERROR) + log_type |= INSTALLLOGMODE_ERROR; + if ((eMessageType & 0xff000000) == INSTALLMESSAGE_WARNING) + log_type |= INSTALLLOGMODE_WARNING; + if ((eMessageType & 0xff000000) == INSTALLMESSAGE_USER) + log_type |= INSTALLLOGMODE_USER; + if ((eMessageType & 0xff000000) == INSTALLMESSAGE_INFO) + log_type |= INSTALLLOGMODE_INFO; + if ((eMessageType & 0xff000000) == INSTALLMESSAGE_COMMONDATA) + log_type |= INSTALLLOGMODE_COMMONDATA; + if ((eMessageType & 0xff000000) == INSTALLMESSAGE_ACTIONSTART) + log_type |= INSTALLLOGMODE_ACTIONSTART; + if ((eMessageType & 0xff000000) == INSTALLMESSAGE_ACTIONDATA) + log_type |= INSTALLLOGMODE_ACTIONDATA; + /* just a guess */ + if ((eMessageType & 0xff000000) == INSTALLMESSAGE_PROGRESS) + log_type |= 0x800; + + message = msi_alloc(1*sizeof (WCHAR)); + message[0]=0; + msg_field = MSI_RecordGetFieldCount(record); + for (i = 1; i <= msg_field; i++) + { + LPWSTR tmp; + WCHAR number[3]; + static const WCHAR format[] = { '%','i',':',' ',0}; + static const WCHAR space[] = { ' ',0}; + sz = 0; + MSI_RecordGetStringW(record,i,NULL,&sz); + sz+=4; + total_size+=sz*sizeof(WCHAR); + tmp = msi_alloc(sz*sizeof(WCHAR)); + message = msi_realloc(message,total_size*sizeof (WCHAR)); + + MSI_RecordGetStringW(record,i,tmp,&sz); + + if (msg_field > 1) + { + sprintfW(number,format,i); + strcatW(message,number); + } + strcatW(message,tmp); + if (msg_field > 1) + strcatW(message,space); + + msi_free(tmp); + } + + TRACE("(%p %lx %lx %s)\n",gUIHandlerA, gUIFilter, log_type, + debugstr_w(message)); + + /* convert it to ASCII */ + len = WideCharToMultiByte( CP_ACP, 0, message, -1, + NULL, 0, NULL, NULL ); + msg = msi_alloc( len ); + WideCharToMultiByte( CP_ACP, 0, message, -1, + msg, len, NULL, NULL ); + + if (gUIHandlerA && (gUIFilter & log_type)) + { + rc = gUIHandlerA(gUIContext,eMessageType,msg); + } + + if ((!rc) && (gszLogFile[0]) && !((eMessageType & 0xff000000) == + INSTALLMESSAGE_PROGRESS)) + { + DWORD write; + HANDLE log_file = CreateFileW(gszLogFile,GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); + + if (log_file != INVALID_HANDLE_VALUE) + { + SetFilePointer(log_file,0, NULL, FILE_END); + WriteFile(log_file,msg,strlen(msg),&write,NULL); + WriteFile(log_file,"\n",1,&write,NULL); + CloseHandle(log_file); + } + } + msi_free( msg ); + + msi_free( message); + return ERROR_SUCCESS; +} + +INT WINAPI MsiProcessMessage( MSIHANDLE hInstall, INSTALLMESSAGE eMessageType, + MSIHANDLE hRecord) +{ + UINT ret = ERROR_INVALID_HANDLE; + MSIPACKAGE *package = NULL; + MSIRECORD *record = NULL; + + package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE ); + if( !package ) + return ERROR_INVALID_HANDLE; + + record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD ); + if( !record ) + goto out; + + ret = MSI_ProcessMessage( package, eMessageType, record ); + +out: + msiobj_release( &package->hdr ); + if( record ) + msiobj_release( &record->hdr ); + + return ret; +} + +/* property code */ +UINT WINAPI MsiSetPropertyA( MSIHANDLE hInstall, LPCSTR szName, LPCSTR szValue ) +{ + LPWSTR szwName = NULL, szwValue = NULL; + UINT r = ERROR_OUTOFMEMORY; + + szwName = strdupAtoW( szName ); + if( szName && !szwName ) + goto end; + + szwValue = strdupAtoW( szValue ); + if( szValue && !szwValue ) + goto end; + + r = MsiSetPropertyW( hInstall, szwName, szwValue); + +end: + msi_free( szwName ); + msi_free( szwValue ); + + return r; +} + +UINT MSI_SetPropertyW( MSIPACKAGE *package, LPCWSTR szName, LPCWSTR szValue) +{ + MSIQUERY *view; + MSIRECORD *row; + UINT rc; + DWORD sz = 0; + static const WCHAR Insert[]= + {'I','N','S','E','R','T',' ','i','n','t','o',' ','`','_','P','r','o','p' +,'e','r','t','y','`',' ','(','`','_','P','r','o','p','e','r','t','y','`' +,',','`','V','a','l','u','e','`',')',' ','V','A','L','U','E','S' +,' ','(','?',',','?',')',0}; + static const WCHAR Update[]= + {'U','P','D','A','T','E',' ','_','P','r','o','p','e' +,'r','t','y',' ','s','e','t',' ','`','V','a','l','u','e','`',' ','=' +,' ','?',' ','w','h','e','r','e',' ','`','_','P','r','o','p' +,'e','r','t','y','`',' ','=',' ','\'','%','s','\'',0}; + WCHAR Query[1024]; + + TRACE("%p %s %s\n", package, debugstr_w(szName), debugstr_w(szValue)); + + if (!szName) + return ERROR_INVALID_PARAMETER; + + /* this one is weird... */ + if (!szName[0]) + return szValue ? ERROR_FUNCTION_FAILED : ERROR_SUCCESS; + + rc = MSI_GetPropertyW(package,szName,0,&sz); + if (rc==ERROR_MORE_DATA || rc == ERROR_SUCCESS) + { + sprintfW(Query,Update,szName); + + row = MSI_CreateRecord(1); + MSI_RecordSetStringW(row,1,szValue); + + } + else + { + strcpyW(Query,Insert); + + row = MSI_CreateRecord(2); + MSI_RecordSetStringW(row,1,szName); + MSI_RecordSetStringW(row,2,szValue); + } + + rc = MSI_DatabaseOpenViewW(package->db,Query,&view); + if (rc == ERROR_SUCCESS) + { + rc = MSI_ViewExecute(view,row); + + MSI_ViewClose(view); + msiobj_release(&view->hdr); + } + msiobj_release(&row->hdr); + + return rc; +} + +UINT WINAPI MsiSetPropertyW( MSIHANDLE hInstall, LPCWSTR szName, LPCWSTR szValue) +{ + MSIPACKAGE *package; + UINT ret; + + package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE); + if( !package ) + return ERROR_INVALID_HANDLE; + ret = MSI_SetPropertyW( package, szName, szValue); + msiobj_release( &package->hdr ); + return ret; +} + +static MSIRECORD *MSI_GetPropertyRow( MSIPACKAGE *package, LPCWSTR name ) +{ + static const WCHAR query[]= + {'S','E','L','E','C','T',' ','`','V','a','l','u','e','`',' ', + 'F','R','O','M',' ' ,'`','_','P','r','o','p','e','r','t','y','`', + ' ','W','H','E','R','E',' ' ,'`','_','P','r','o','p','e','r','t','y','`', + '=','\'','%','s','\'',0}; + + if (!name || !name[0]) + return NULL; + + return MSI_QueryGetRecord( package->db, query, name ); +} + +/* internal function, not compatible with MsiGetPropertyW */ +UINT MSI_GetPropertyW( MSIPACKAGE *package, LPCWSTR szName, + LPWSTR szValueBuf, DWORD* pchValueBuf ) +{ + MSIRECORD *row; + UINT rc = ERROR_FUNCTION_FAILED; + + row = MSI_GetPropertyRow( package, szName ); + + if (*pchValueBuf > 0) + szValueBuf[0] = 0; + + if (row) + { + rc = MSI_RecordGetStringW(row,1,szValueBuf,pchValueBuf); + msiobj_release(&row->hdr); + } + + if (rc == ERROR_SUCCESS) + TRACE("returning %s for property %s\n", debugstr_w(szValueBuf), + debugstr_w(szName)); + else if (rc == ERROR_MORE_DATA) + TRACE("need %li sized buffer for %s\n", *pchValueBuf, + debugstr_w(szName)); + else + { + *pchValueBuf = 0; + TRACE("property %s not found\n", debugstr_w(szName)); + } + + return rc; +} + +static UINT MSI_GetProperty( MSIHANDLE handle, LPCWSTR name, + awstring *szValueBuf, DWORD* pchValueBuf ) +{ + static const WCHAR empty[] = {0}; + MSIPACKAGE *package; + MSIRECORD *row = NULL; + UINT r; + LPCWSTR val = NULL; + + TRACE("%lu %s %p %p\n", handle, debugstr_w(name), + szValueBuf->str.w, pchValueBuf ); + + if (!name) + return ERROR_INVALID_PARAMETER; + + package = msihandle2msiinfo( handle, MSIHANDLETYPE_PACKAGE ); + if (!package) + return ERROR_INVALID_HANDLE; + + row = MSI_GetPropertyRow( package, name ); + if (row) + val = MSI_RecordGetString( row, 1 ); + + if (!val) + val = empty; + + r = msi_strcpy_to_awstring( val, szValueBuf, pchValueBuf ); + + if (row) + msiobj_release( &row->hdr ); + msiobj_release( &package->hdr ); + + return r; +} + +UINT WINAPI MsiGetPropertyA( MSIHANDLE hInstall, LPCSTR szName, + LPSTR szValueBuf, DWORD* pchValueBuf ) +{ + awstring val; + LPWSTR name; + UINT r; + + val.unicode = FALSE; + val.str.a = szValueBuf; + + name = strdupAtoW( szName ); + if (szName && !name) + return ERROR_OUTOFMEMORY; + + r = MSI_GetProperty( hInstall, name, &val, pchValueBuf ); + msi_free( name ); + return r; +} + +UINT WINAPI MsiGetPropertyW( MSIHANDLE hInstall, LPCWSTR szName, + LPWSTR szValueBuf, DWORD* pchValueBuf ) +{ + awstring val; + + val.unicode = TRUE; + val.str.w = szValueBuf; + + return MSI_GetProperty( hInstall, szName, &val, pchValueBuf ); +} diff --git a/reactos/dll/msi/preview.c b/reactos/dll/msi/preview.c new file mode 100644 index 00000000000..4c7c62b6022 --- /dev/null +++ b/reactos/dll/msi/preview.c @@ -0,0 +1,166 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2005 Mike McCormack 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "windef.h" +#include "winbase.h" +#include "winnls.h" +#include "msi.h" +#include "msipriv.h" + +#include "wine/debug.h" +#include "wine/unicode.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +static void MSI_ClosePreview( MSIOBJECTHDR *arg ) +{ + MSIPREVIEW *preview = (MSIPREVIEW *) arg; + + msiobj_release( &preview->package->hdr ); +} + +MSIPREVIEW *MSI_EnableUIPreview( MSIDATABASE *db ) +{ + MSIPREVIEW *preview = NULL; + MSIPACKAGE *package; + + package = MSI_CreatePackage( db ); + if( package ) + { + preview = alloc_msiobject( MSIHANDLETYPE_PREVIEW, sizeof (MSIPREVIEW), + MSI_ClosePreview ); + if( preview ) + { + preview->package = package; + preview->dialog = 0; + msiobj_addref( &package->hdr ); + } + msiobj_release( &package->hdr ); + } + return preview; +} + +UINT WINAPI MsiEnableUIPreview( MSIHANDLE hdb, MSIHANDLE* phPreview ) +{ + MSIDATABASE *db; + MSIPREVIEW *preview; + UINT r = ERROR_FUNCTION_FAILED; + + TRACE("%ld %p\n", hdb, phPreview); + + db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE ); + if( !db ) + return ERROR_INVALID_HANDLE; + preview = MSI_EnableUIPreview( db ); + if( preview ) + { + *phPreview = alloc_msihandle( &preview->hdr ); + msiobj_release( &preview->hdr ); + r = ERROR_SUCCESS; + } + msiobj_release( &db->hdr ); + + return r; +} + +static UINT preview_event_handler( MSIPACKAGE *package, LPCWSTR event, + LPCWSTR argument, msi_dialog *dialog ) +{ + MESSAGE("Preview dialog event '%s' (arg='%s')\n", + debugstr_w( event ), debugstr_w( argument )); + return ERROR_SUCCESS; +} + +UINT MSI_PreviewDialogW( MSIPREVIEW *preview, LPCWSTR szDialogName ) +{ + msi_dialog *dialog = NULL; + UINT r = ERROR_SUCCESS; + + if( preview->dialog ) + msi_dialog_destroy( preview->dialog ); + + /* an empty name means we should just destroy the current preview dialog */ + if( szDialogName ) + { + dialog = msi_dialog_create( preview->package, szDialogName, + preview_event_handler ); + if( dialog ) + msi_dialog_do_preview( dialog ); + else + r = ERROR_FUNCTION_FAILED; + } + preview->dialog = dialog; + + return r; +} + +UINT WINAPI MsiPreviewDialogW( MSIHANDLE hPreview, LPCWSTR szDialogName ) +{ + MSIPREVIEW *preview; + UINT r; + + TRACE("%ld %s\n", hPreview, debugstr_w(szDialogName)); + + preview = msihandle2msiinfo( hPreview, MSIHANDLETYPE_PREVIEW ); + if( !preview ) + return ERROR_INVALID_HANDLE; + + r = MSI_PreviewDialogW( preview, szDialogName ); + + msiobj_release( &preview->hdr ); + + return r; +} + +UINT WINAPI MsiPreviewDialogA( MSIHANDLE hPreview, LPCSTR szDialogName ) +{ + UINT r; + LPWSTR strW = NULL; + + TRACE("%ld %s\n", hPreview, debugstr_a(szDialogName)); + + if( szDialogName ) + { + strW = strdupAtoW( szDialogName ); + if( !strW ) + return ERROR_OUTOFMEMORY; + } + r = MsiPreviewDialogW( hPreview, strW ); + msi_free( strW ); + return r; +} + +UINT WINAPI MsiPreviewBillboardW( MSIHANDLE hPreview, LPCWSTR szControlName, + LPCWSTR szBillboard) +{ + FIXME("%ld %s %s\n", hPreview, debugstr_w(szControlName), + debugstr_w(szBillboard)); + return ERROR_CALL_NOT_IMPLEMENTED; +} + +UINT WINAPI MsiPreviewBillboardA( MSIHANDLE hPreview, LPCSTR szControlName, + LPCSTR szBillboard) +{ + FIXME("%ld %s %s\n", hPreview, debugstr_a(szControlName), + debugstr_a(szBillboard)); + return ERROR_CALL_NOT_IMPLEMENTED; +} diff --git a/reactos/dll/msi/query.h b/reactos/dll/msi/query.h new file mode 100644 index 00000000000..e1fa104e603 --- /dev/null +++ b/reactos/dll/msi/query.h @@ -0,0 +1,121 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002 Mike McCormack 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __WINE_MSI_QUERY_H +#define __WINE_MSI_QUERY_H + +#include + +#include "windef.h" +#include "winbase.h" +#include "objbase.h" +#include "objidl.h" +#include "msi.h" +#include "msiquery.h" +#include "msipriv.h" +#include "wine/list.h" + + +#define OP_EQ 1 +#define OP_AND 2 +#define OP_OR 3 +#define OP_GT 4 +#define OP_LT 5 +#define OP_LE 6 +#define OP_GE 7 +#define OP_NE 8 +#define OP_ISNULL 9 +#define OP_NOTNULL 10 + +#define EXPR_COMPLEX 1 +#define EXPR_COLUMN 2 +#define EXPR_COL_NUMBER 3 +#define EXPR_IVAL 4 +#define EXPR_SVAL 5 +#define EXPR_UVAL 6 +#define EXPR_STRCMP 7 +#define EXPR_WILDCARD 9 +#define EXPR_COL_NUMBER_STRING 10 + +struct sql_str { + LPCWSTR data; + INT len; +}; + +typedef struct _column_info +{ + LPCWSTR table; + LPCWSTR column; + UINT type; + struct expr *val; + struct _column_info *next; +} column_info; + +struct complex_expr +{ + UINT op; + struct expr *left; + struct expr *right; +}; + +struct expr +{ + int type; + union + { + struct complex_expr expr; + INT ival; + UINT uval; + LPCWSTR sval; + LPCWSTR column; + UINT col_number; + } u; +}; + +UINT MSI_ParseSQL( MSIDATABASE *db, LPCWSTR command, MSIVIEW **phview, + struct list *mem ); + +UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view ); + +UINT SELECT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table, + column_info *columns ); + +UINT DISTINCT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table ); + +UINT ORDER_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table, + column_info *columns ); + +UINT WHERE_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table, + struct expr *cond ); + +UINT CREATE_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR table, + column_info *col_info, BOOL temp ); + +UINT INSERT_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR table, + column_info *columns, column_info *values, BOOL temp ); + +UINT UPDATE_CreateView( MSIDATABASE *db, MSIVIEW **, LPWSTR table, + column_info *list, struct expr *expr ); + +UINT DELETE_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table ); + +int sqliteGetToken(const WCHAR *z, int *tokenType); + +#endif /* __WINE_MSI_QUERY_H */ diff --git a/reactos/dll/msi/record.c b/reactos/dll/msi/record.c new file mode 100644 index 00000000000..c1f6d1fe8c5 --- /dev/null +++ b/reactos/dll/msi/record.c @@ -0,0 +1,832 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002-2004 Mike McCormack 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#define COBJMACROS + +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "msipriv.h" +#include "objidl.h" +#include "winnls.h" +#include "ole2.h" + +#include "winreg.h" +#include "shlwapi.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msidb); + +#define MSIFIELD_NULL 0 +#define MSIFIELD_INT 1 +#define MSIFIELD_STR 2 +#define MSIFIELD_WSTR 3 +#define MSIFIELD_STREAM 4 + +static void MSI_FreeField( MSIFIELD *field ) +{ + switch( field->type ) + { + case MSIFIELD_NULL: + case MSIFIELD_INT: + break; + case MSIFIELD_WSTR: + msi_free( field->u.szwVal); + break; + case MSIFIELD_STREAM: + IStream_Release( field->u.stream ); + break; + default: + ERR("Invalid field type %d\n", field->type); + } +} + +static void MSI_CloseRecord( MSIOBJECTHDR *arg ) +{ + MSIRECORD *rec = (MSIRECORD *) arg; + UINT i; + + for( i=0; i<=rec->count; i++ ) + MSI_FreeField( &rec->fields[i] ); +} + +MSIRECORD *MSI_CreateRecord( unsigned int cParams ) +{ + MSIRECORD *rec; + UINT len; + + TRACE("%d\n", cParams); + + if( cParams>65535 ) + return NULL; + + len = sizeof (MSIRECORD) + sizeof (MSIFIELD)*cParams; + rec = alloc_msiobject( MSIHANDLETYPE_RECORD, len, MSI_CloseRecord ); + if( rec ) + rec->count = cParams; + return rec; +} + +MSIHANDLE WINAPI MsiCreateRecord( unsigned int cParams ) +{ + MSIRECORD *rec; + MSIHANDLE ret = 0; + + TRACE("%d\n", cParams); + + rec = MSI_CreateRecord( cParams ); + if( rec ) + ret = alloc_msihandle( &rec->hdr ); + msiobj_release( &rec->hdr ); + return ret; +} + +unsigned int MSI_RecordGetFieldCount( MSIRECORD *rec ) +{ + return rec->count; +} + +unsigned int WINAPI MsiRecordGetFieldCount( MSIHANDLE handle ) +{ + MSIRECORD *rec; + UINT ret; + + TRACE("%ld\n", handle ); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return -1; + + msiobj_lock( &rec->hdr ); + ret = MSI_RecordGetFieldCount( rec ); + msiobj_unlock( &rec->hdr ); + msiobj_release( &rec->hdr ); + + return ret; +} + +static BOOL string2intW( LPCWSTR str, int *out ) +{ + int x = 0; + LPCWSTR p = str; + + if( *p == '-' ) /* skip the minus sign */ + p++; + while ( *p ) + { + if( (*p < '0') || (*p > '9') ) + return FALSE; + x *= 10; + x += (*p - '0'); + p++; + } + + if( str[0] == '-' ) /* check if it's negative */ + x = -x; + *out = x; + + return TRUE; +} + +int MSI_RecordGetInteger( MSIRECORD *rec, unsigned int iField) +{ + int ret = 0; + + TRACE("%p %d\n", rec, iField ); + + if( iField > rec->count ) + return MSI_NULL_INTEGER; + + switch( rec->fields[iField].type ) + { + case MSIFIELD_INT: + return rec->fields[iField].u.iVal; + case MSIFIELD_WSTR: + if( string2intW( rec->fields[iField].u.szwVal, &ret ) ) + return ret; + return MSI_NULL_INTEGER; + default: + break; + } + + return MSI_NULL_INTEGER; +} + +int WINAPI MsiRecordGetInteger( MSIHANDLE handle, unsigned int iField) +{ + MSIRECORD *rec; + UINT ret; + + TRACE("%ld %d\n", handle, iField ); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return MSI_NULL_INTEGER; + + msiobj_lock( &rec->hdr ); + ret = MSI_RecordGetInteger( rec, iField ); + msiobj_unlock( &rec->hdr ); + msiobj_release( &rec->hdr ); + + return ret; +} + +UINT WINAPI MsiRecordClearData( MSIHANDLE handle ) +{ + MSIRECORD *rec; + UINT i; + + TRACE("%ld\n", handle ); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return ERROR_INVALID_HANDLE; + + msiobj_lock( &rec->hdr ); + for( i=0; i<=rec->count; i++) + { + MSI_FreeField( &rec->fields[i] ); + rec->fields[i].type = MSIFIELD_NULL; + rec->fields[i].u.iVal = 0; + } + msiobj_unlock( &rec->hdr ); + msiobj_release( &rec->hdr ); + + return ERROR_SUCCESS; +} + +UINT MSI_RecordSetInteger( MSIRECORD *rec, unsigned int iField, int iVal ) +{ + TRACE("%p %u %d\n", rec, iField, iVal); + + if( iField > rec->count ) + return ERROR_INVALID_PARAMETER; + + MSI_FreeField( &rec->fields[iField] ); + rec->fields[iField].type = MSIFIELD_INT; + rec->fields[iField].u.iVal = iVal; + + return ERROR_SUCCESS; +} + +UINT WINAPI MsiRecordSetInteger( MSIHANDLE handle, unsigned int iField, int iVal ) +{ + MSIRECORD *rec; + UINT ret; + + TRACE("%ld %u %d\n", handle, iField, iVal); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return ERROR_INVALID_HANDLE; + + msiobj_lock( &rec->hdr ); + ret = MSI_RecordSetInteger( rec, iField, iVal ); + msiobj_unlock( &rec->hdr ); + msiobj_release( &rec->hdr ); + return ret; +} + +BOOL MSI_RecordIsNull( MSIRECORD *rec, unsigned int iField ) +{ + BOOL r = TRUE; + + TRACE("%p %d\n", rec, iField ); + + r = ( iField > rec->count ) || + ( rec->fields[iField].type == MSIFIELD_NULL ); + + return r; +} + +BOOL WINAPI MsiRecordIsNull( MSIHANDLE handle, unsigned int iField ) +{ + MSIRECORD *rec; + UINT ret; + + TRACE("%ld %d\n", handle, iField ); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return 0; + msiobj_lock( &rec->hdr ); + ret = MSI_RecordIsNull( rec, iField ); + msiobj_unlock( &rec->hdr ); + msiobj_release( &rec->hdr ); + return ret; + +} + +UINT MSI_RecordGetStringA(MSIRECORD *rec, unsigned int iField, + LPSTR szValue, DWORD *pcchValue) +{ + UINT len=0, ret; + CHAR buffer[16]; + + TRACE("%p %d %p %p\n", rec, iField, szValue, pcchValue); + + if( iField > rec->count ) + return ERROR_INVALID_PARAMETER; + + ret = ERROR_SUCCESS; + switch( rec->fields[iField].type ) + { + case MSIFIELD_INT: + wsprintfA(buffer, "%d", rec->fields[iField].u.iVal); + len = lstrlenA( buffer ); + lstrcpynA(szValue, buffer, *pcchValue); + break; + case MSIFIELD_WSTR: + len = WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal, -1, + NULL, 0 , NULL, NULL); + WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal, -1, + szValue, *pcchValue, NULL, NULL); + if( *pcchValue && len>*pcchValue ) + szValue[*pcchValue-1] = 0; + if( len ) + len--; + break; + case MSIFIELD_NULL: + if( *pcchValue > 0 ) + szValue[0] = 0; + break; + default: + ret = ERROR_INVALID_PARAMETER; + break; + } + + if( *pcchValue < len ) + ret = ERROR_MORE_DATA; + *pcchValue = len; + + return ret; +} + +UINT WINAPI MsiRecordGetStringA(MSIHANDLE handle, unsigned int iField, + LPSTR szValue, DWORD *pcchValue) +{ + MSIRECORD *rec; + UINT ret; + + TRACE("%ld %d %p %p\n", handle, iField, szValue, pcchValue); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return ERROR_INVALID_HANDLE; + msiobj_lock( &rec->hdr ); + ret = MSI_RecordGetStringA( rec, iField, szValue, pcchValue); + msiobj_unlock( &rec->hdr ); + msiobj_release( &rec->hdr ); + return ret; +} + +const WCHAR *MSI_RecordGetString( MSIRECORD *rec, unsigned int iField ) +{ + if( iField > rec->count ) + return NULL; + + if( rec->fields[iField].type != MSIFIELD_WSTR ) + return NULL; + + return rec->fields[iField].u.szwVal; +} + +UINT MSI_RecordGetStringW(MSIRECORD *rec, unsigned int iField, + LPWSTR szValue, DWORD *pcchValue) +{ + UINT len=0, ret; + WCHAR buffer[16]; + static const WCHAR szFormat[] = { '%','d',0 }; + + TRACE("%p %d %p %p\n", rec, iField, szValue, pcchValue); + + if( iField > rec->count ) + return ERROR_INVALID_PARAMETER; + + ret = ERROR_SUCCESS; + switch( rec->fields[iField].type ) + { + case MSIFIELD_INT: + wsprintfW(buffer, szFormat, rec->fields[iField].u.iVal); + len = lstrlenW( buffer ); + lstrcpynW(szValue, buffer, *pcchValue); + break; + case MSIFIELD_WSTR: + len = lstrlenW( rec->fields[iField].u.szwVal ); + lstrcpynW(szValue, rec->fields[iField].u.szwVal, *pcchValue); + break; + case MSIFIELD_NULL: + len = 1; + if( *pcchValue > 0 ) + szValue[0] = 0; + default: + break; + } + + if( *pcchValue < len ) + ret = ERROR_MORE_DATA; + *pcchValue = len; + + return ret; +} + +UINT WINAPI MsiRecordGetStringW(MSIHANDLE handle, unsigned int iField, + LPWSTR szValue, DWORD *pcchValue) +{ + MSIRECORD *rec; + UINT ret; + + TRACE("%ld %d %p %p\n", handle, iField, szValue, pcchValue); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return ERROR_INVALID_HANDLE; + + msiobj_lock( &rec->hdr ); + ret = MSI_RecordGetStringW( rec, iField, szValue, pcchValue ); + msiobj_unlock( &rec->hdr ); + msiobj_release( &rec->hdr ); + return ret; +} + +static UINT msi_get_stream_size( IStream *stm ) +{ + STATSTG stat; + HRESULT r; + + r = IStream_Stat( stm, &stat, STATFLAG_NONAME ); + if( FAILED(r) ) + return 0; + return stat.cbSize.QuadPart; +} + +UINT MSI_RecordDataSize(MSIRECORD *rec, unsigned int iField) +{ + TRACE("%p %d\n", rec, iField); + + if( iField > rec->count ) + return 0; + + switch( rec->fields[iField].type ) + { + case MSIFIELD_INT: + return sizeof (INT); + case MSIFIELD_WSTR: + return lstrlenW( rec->fields[iField].u.szwVal ); + case MSIFIELD_NULL: + break; + case MSIFIELD_STREAM: + return msi_get_stream_size( rec->fields[iField].u.stream ); + } + return 0; +} + +UINT WINAPI MsiRecordDataSize(MSIHANDLE handle, unsigned int iField) +{ + MSIRECORD *rec; + UINT ret; + + TRACE("%ld %d\n", handle, iField); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return 0; + msiobj_lock( &rec->hdr ); + ret = MSI_RecordDataSize( rec, iField); + msiobj_unlock( &rec->hdr ); + msiobj_release( &rec->hdr ); + return ret; +} + +UINT MSI_RecordSetStringA( MSIRECORD *rec, unsigned int iField, LPCSTR szValue ) +{ + LPWSTR str; + + TRACE("%p %d %s\n", rec, iField, debugstr_a(szValue)); + + if( iField > rec->count ) + return ERROR_INVALID_FIELD; + + MSI_FreeField( &rec->fields[iField] ); + if( szValue && szValue[0] ) + { + str = strdupAtoW( szValue ); + rec->fields[iField].type = MSIFIELD_WSTR; + rec->fields[iField].u.szwVal = str; + } + else + { + rec->fields[iField].type = MSIFIELD_NULL; + rec->fields[iField].u.szwVal = NULL; + } + + return 0; +} + +UINT WINAPI MsiRecordSetStringA( MSIHANDLE handle, unsigned int iField, LPCSTR szValue ) +{ + MSIRECORD *rec; + UINT ret; + + TRACE("%ld %d %s\n", handle, iField, debugstr_a(szValue)); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return ERROR_INVALID_HANDLE; + msiobj_lock( &rec->hdr ); + ret = MSI_RecordSetStringA( rec, iField, szValue ); + msiobj_unlock( &rec->hdr ); + msiobj_release( &rec->hdr ); + return ret; +} + +UINT MSI_RecordSetStringW( MSIRECORD *rec, unsigned int iField, LPCWSTR szValue ) +{ + LPWSTR str; + + TRACE("%p %d %s\n", rec, iField, debugstr_w(szValue)); + + if( iField > rec->count ) + return ERROR_INVALID_FIELD; + + MSI_FreeField( &rec->fields[iField] ); + + if( szValue && szValue[0] ) + { + str = strdupW( szValue ); + rec->fields[iField].type = MSIFIELD_WSTR; + rec->fields[iField].u.szwVal = str; + } + else + { + rec->fields[iField].type = MSIFIELD_NULL; + rec->fields[iField].u.szwVal = NULL; + } + + return 0; +} + +UINT WINAPI MsiRecordSetStringW( MSIHANDLE handle, unsigned int iField, LPCWSTR szValue ) +{ + MSIRECORD *rec; + UINT ret; + + TRACE("%ld %d %s\n", handle, iField, debugstr_w(szValue)); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return ERROR_INVALID_HANDLE; + + msiobj_lock( &rec->hdr ); + ret = MSI_RecordSetStringW( rec, iField, szValue ); + msiobj_unlock( &rec->hdr ); + msiobj_release( &rec->hdr ); + return ret; +} + +/* read the data in a file into an IStream */ +static UINT RECORD_StreamFromFile(LPCWSTR szFile, IStream **pstm) +{ + DWORD sz, szHighWord = 0, read; + HANDLE handle; + HGLOBAL hGlob = 0; + HRESULT hr; + ULARGE_INTEGER ulSize; + + TRACE("reading %s\n", debugstr_w(szFile)); + + /* read the file into memory */ + handle = CreateFileW(szFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if( handle == INVALID_HANDLE_VALUE ) + return GetLastError(); + sz = GetFileSize(handle, &szHighWord); + if( sz != INVALID_FILE_SIZE && szHighWord == 0 ) + { + hGlob = GlobalAlloc(GMEM_FIXED, sz); + if( hGlob ) + { + BOOL r = ReadFile(handle, hGlob, sz, &read, NULL); + if( !r ) + { + GlobalFree(hGlob); + hGlob = 0; + } + } + } + CloseHandle(handle); + if( !hGlob ) + return ERROR_FUNCTION_FAILED; + + /* make a stream out of it, and set the correct file size */ + hr = CreateStreamOnHGlobal(hGlob, TRUE, pstm); + if( FAILED( hr ) ) + { + GlobalFree(hGlob); + return ERROR_FUNCTION_FAILED; + } + + /* set the correct size - CreateStreamOnHGlobal screws it up */ + ulSize.QuadPart = sz; + IStream_SetSize(*pstm, ulSize); + + TRACE("read %s, %ld bytes into IStream %p\n", debugstr_w(szFile), sz, *pstm); + + return ERROR_SUCCESS; +} + +UINT MSI_RecordSetStreamW(MSIRECORD *rec, unsigned int iField, LPCWSTR szFilename) +{ + IStream *stm = NULL; + HRESULT r; + + if( (iField == 0) || (iField > rec->count) ) + return ERROR_INVALID_PARAMETER; + + /* no filename means we should seek back to the start of the stream */ + if( !szFilename ) + { + LARGE_INTEGER ofs; + ULARGE_INTEGER cur; + + if( rec->fields[iField].type != MSIFIELD_STREAM ) + return ERROR_INVALID_FIELD; + + stm = rec->fields[iField].u.stream; + if( !stm ) + return ERROR_INVALID_FIELD; + + ofs.QuadPart = 0; + r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur ); + if( FAILED( r ) ) + return ERROR_FUNCTION_FAILED; + } + else + { + /* read the file into a stream and save the stream in the record */ + r = RECORD_StreamFromFile(szFilename, &stm); + if( r != ERROR_SUCCESS ) + return r; + + /* if all's good, store it in the record */ + MSI_FreeField( &rec->fields[iField] ); + rec->fields[iField].type = MSIFIELD_STREAM; + rec->fields[iField].u.stream = stm; + } + + return ERROR_SUCCESS; +} + +UINT WINAPI MsiRecordSetStreamA(MSIHANDLE hRecord, unsigned int iField, LPCSTR szFilename) +{ + LPWSTR wstr = NULL; + UINT ret; + + TRACE("%ld %d %s\n", hRecord, iField, debugstr_a(szFilename)); + + if( szFilename ) + { + wstr = strdupAtoW( szFilename ); + if( !wstr ) + return ERROR_OUTOFMEMORY; + } + ret = MsiRecordSetStreamW(hRecord, iField, wstr); + msi_free(wstr); + + return ret; +} + +UINT WINAPI MsiRecordSetStreamW(MSIHANDLE handle, unsigned int iField, LPCWSTR szFilename) +{ + MSIRECORD *rec; + UINT ret; + + TRACE("%ld %d %s\n", handle, iField, debugstr_w(szFilename)); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return ERROR_INVALID_HANDLE; + + msiobj_lock( &rec->hdr ); + ret = MSI_RecordSetStreamW( rec, iField, szFilename ); + msiobj_unlock( &rec->hdr ); + msiobj_release( &rec->hdr ); + return ret; +} + +UINT MSI_RecordReadStream(MSIRECORD *rec, unsigned int iField, char *buf, DWORD *sz) +{ + ULONG count; + HRESULT r; + IStream *stm; + + TRACE("%p %d %p %p\n", rec, iField, buf, sz); + + if( !sz ) + return ERROR_INVALID_PARAMETER; + + if( iField > rec->count) + return ERROR_INVALID_PARAMETER; + + if( rec->fields[iField].type != MSIFIELD_STREAM ) + return ERROR_INVALID_DATATYPE; + + stm = rec->fields[iField].u.stream; + if( !stm ) + return ERROR_INVALID_PARAMETER; + + /* if there's no buffer pointer, calculate the length to the end */ + if( !buf ) + { + LARGE_INTEGER ofs; + ULARGE_INTEGER end, cur; + + ofs.QuadPart = cur.QuadPart = 0; + end.QuadPart = 0; + r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur ); + IStream_Seek( stm, ofs, STREAM_SEEK_END, &end ); + ofs.QuadPart = cur.QuadPart; + IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur ); + *sz = end.QuadPart - cur.QuadPart; + + return ERROR_SUCCESS; + } + + /* read the data */ + count = 0; + r = IStream_Read( stm, buf, *sz, &count ); + if( FAILED( r ) ) + { + *sz = 0; + return ERROR_FUNCTION_FAILED; + } + + *sz = count; + + return ERROR_SUCCESS; +} + +UINT WINAPI MsiRecordReadStream(MSIHANDLE handle, unsigned int iField, char *buf, DWORD *sz) +{ + MSIRECORD *rec; + UINT ret; + + TRACE("%ld %d %p %p\n", handle, iField, buf, sz); + + rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD ); + if( !rec ) + return ERROR_INVALID_HANDLE; + msiobj_lock( &rec->hdr ); + ret = MSI_RecordReadStream( rec, iField, buf, sz ); + msiobj_unlock( &rec->hdr ); + msiobj_release( &rec->hdr ); + return ret; +} + +UINT MSI_RecordSetIStream( MSIRECORD *rec, unsigned int iField, IStream *stm ) +{ + TRACE("%p %d %p\n", rec, iField, stm); + + if( iField > rec->count ) + return ERROR_INVALID_FIELD; + + MSI_FreeField( &rec->fields[iField] ); + + rec->fields[iField].type = MSIFIELD_STREAM; + rec->fields[iField].u.stream = stm; + IStream_AddRef( stm ); + + return ERROR_SUCCESS; +} + +UINT MSI_RecordGetIStream( MSIRECORD *rec, unsigned int iField, IStream **pstm) +{ + TRACE("%p %d %p\n", rec, iField, pstm); + + if( iField > rec->count ) + return ERROR_INVALID_FIELD; + + if( rec->fields[iField].type != MSIFIELD_STREAM ) + return ERROR_INVALID_FIELD; + + *pstm = rec->fields[iField].u.stream; + IStream_AddRef( *pstm ); + + return ERROR_SUCCESS; +} + +static UINT msi_dump_stream_to_file( IStream *stm, LPCWSTR name ) +{ + ULARGE_INTEGER size; + LARGE_INTEGER pos; + IStream *out; + DWORD stgm; + HRESULT r; + + stgm = STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_FAILIFTHERE; + r = SHCreateStreamOnFileW( name, stgm, &out ); + if( FAILED( r ) ) + return ERROR_FUNCTION_FAILED; + + pos.QuadPart = 0; + r = IStream_Seek( stm, pos, STREAM_SEEK_END, &size ); + if( FAILED( r ) ) + goto end; + + pos.QuadPart = 0; + r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL ); + if( FAILED( r ) ) + goto end; + + r = IStream_CopyTo( stm, out, size, NULL, NULL ); + +end: + IStream_Release( out ); + if( FAILED( r ) ) + return ERROR_FUNCTION_FAILED; + return ERROR_SUCCESS; +} + +UINT MSI_RecordStreamToFile( MSIRECORD *rec, unsigned int iField, LPCWSTR name ) +{ + IStream *stm = NULL; + UINT r; + + TRACE("%p %u %s\n", rec, iField, debugstr_w(name)); + + msiobj_lock( &rec->hdr ); + + r = MSI_RecordGetIStream( rec, iField, &stm ); + if( r == ERROR_SUCCESS ) + { + r = msi_dump_stream_to_file( stm, name ); + IStream_Release( stm ); + } + + msiobj_unlock( &rec->hdr ); + + return r; +} diff --git a/reactos/dll/msi/registry.c b/reactos/dll/msi/registry.c new file mode 100644 index 00000000000..528070e0ffd --- /dev/null +++ b/reactos/dll/msi/registry.c @@ -0,0 +1,980 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2005 Mike McCormack for CodeWeavers + * Copyright 2005 Aric Stewart 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#define COBJMACROS +#define NONAMELESSUNION + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winnls.h" +#include "shlwapi.h" +#include "wine/debug.h" +#include "msi.h" +#include "msipriv.h" +#include "wincrypt.h" +#include "wine/unicode.h" +#include "winver.h" +#include "winuser.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + + +/* + * This module will be all the helper functions for registry access by the + * installer bits. + */ +static const WCHAR szUserFeatures_fmt[] = { +'S','o','f','t','w','a','r','e','\\', +'M','i','c','r','o','s','o','f','t','\\', +'I','n','s','t','a','l','l','e','r','\\', +'F','e','a','t','u','r','e','s','\\', +'%','s',0}; + +static const WCHAR szInstaller_Features[] = { +'S','o','f','t','w','a','r','e','\\', +'M','i','c','r','o','s','o','f','t','\\', +'W','i','n','d','o','w','s','\\', +'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', +'I','n','s','t','a','l','l','e','r','\\', +'F','e','a','t','u','r','e','s',0 }; + +static const WCHAR szInstaller_Features_fmt[] = { +'S','o','f','t','w','a','r','e','\\', +'M','i','c','r','o','s','o','f','t','\\', +'W','i','n','d','o','w','s','\\', +'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', +'I','n','s','t','a','l','l','e','r','\\', +'F','e','a','t','u','r','e','s','\\', +'%','s',0}; + +static const WCHAR szInstaller_Components[] = { +'S','o','f','t','w','a','r','e','\\', +'M','i','c','r','o','s','o','f','t','\\', +'W','i','n','d','o','w','s','\\', +'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', +'I','n','s','t','a','l','l','e','r','\\', +'C','o','m','p','o','n','e','n','t','s',0 }; + +static const WCHAR szInstaller_Components_fmt[] = { +'S','o','f','t','w','a','r','e','\\', +'M','i','c','r','o','s','o','f','t','\\', +'W','i','n','d','o','w','s','\\', +'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', +'I','n','s','t','a','l','l','e','r','\\', +'C','o','m','p','o','n','e','n','t','s','\\', +'%','s',0}; + +static const WCHAR szUser_Components_fmt[] = { +'S','o','f','t','w','a','r','e','\\', +'M','i','c','r','o','s','o','f','t','\\', +'I','n','s','t','a','l','l','e','r','\\', +'C','o','m','p','o','n','e','n','t','s','\\', +'%','s',0}; + +static const WCHAR szUninstall_fmt[] = { +'S','o','f','t','w','a','r','e','\\', +'M','i','c','r','o','s','o','f','t','\\', +'W','i','n','d','o','w','s','\\', +'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', +'U','n','i','n','s','t','a','l','l','\\', +'%','s',0 }; + +static const WCHAR szUserProduct_fmt[] = { +'S','o','f','t','w','a','r','e','\\', +'M','i','c','r','o','s','o','f','t','\\', +'I','n','s','t','a','l','l','e','r','\\', +'P','r','o','d','u','c','t','s','\\', +'%','s',0}; + +static const WCHAR szInstaller_Products[] = { +'S','o','f','t','w','a','r','e','\\', +'M','i','c','r','o','s','o','f','t','\\', +'W','i','n','d','o','w','s','\\', +'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', +'I','n','s','t','a','l','l','e','r','\\', +'P','r','o','d','u','c','t','s',0}; + +static const WCHAR szInstaller_Products_fmt[] = { +'S','o','f','t','w','a','r','e','\\', +'M','i','c','r','o','s','o','f','t','\\', +'W','i','n','d','o','w','s','\\', +'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', +'I','n','s','t','a','l','l','e','r','\\', +'P','r','o','d','u','c','t','s','\\', +'%','s',0}; + +static const WCHAR szInstaller_UpgradeCodes[] = { +'S','o','f','t','w','a','r','e','\\', +'M','i','c','r','o','s','o','f','t','\\', +'W','i','n','d','o','w','s','\\', +'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', +'I','n','s','t','a','l','l','e','r','\\', +'U','p','g','r','a','d','e','C','o','d','e','s',0}; + +static const WCHAR szInstaller_UpgradeCodes_fmt[] = { +'S','o','f','t','w','a','r','e','\\', +'M','i','c','r','o','s','o','f','t','\\', +'W','i','n','d','o','w','s','\\', +'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\', +'I','n','s','t','a','l','l','e','r','\\', +'U','p','g','r','a','d','e','C','o','d','e','s','\\', +'%','s',0}; + +static const WCHAR szInstaller_UserUpgradeCodes[] = { +'S','o','f','t','w','a','r','e','\\', +'M','i','c','r','o','s','o','f','t','\\', +'I','n','s','t','a','l','l','e','r','\\', +'U','p','g','r','a','d','e','C','o','d','e','s',0}; + +static const WCHAR szInstaller_UserUpgradeCodes_fmt[] = { +'S','o','f','t','w','a','r','e','\\', +'M','i','c','r','o','s','o','f','t','\\', +'I','n','s','t','a','l','l','e','r','\\', +'U','p','g','r','a','d','e','C','o','d','e','s','\\', +'%','s',0}; + + +#define SQUISH_GUID_SIZE 33 + +BOOL unsquash_guid(LPCWSTR in, LPWSTR out) +{ + DWORD i,n=0; + + out[n++]='{'; + for(i=0; i<8; i++) + out[n++] = in[7-i]; + out[n++]='-'; + for(i=0; i<4; i++) + out[n++] = in[11-i]; + out[n++]='-'; + for(i=0; i<4; i++) + out[n++] = in[15-i]; + out[n++]='-'; + for(i=0; i<2; i++) + { + out[n++] = in[17+i*2]; + out[n++] = in[16+i*2]; + } + out[n++]='-'; + for( ; i<8; i++) + { + out[n++] = in[17+i*2]; + out[n++] = in[16+i*2]; + } + out[n++]='}'; + out[n]=0; + return TRUE; +} + +BOOL squash_guid(LPCWSTR in, LPWSTR out) +{ + DWORD i,n=0; + + if(in[n++] != '{') + return FALSE; + for(i=0; i<8; i++) + out[7-i] = in[n++]; + if(in[n++] != '-') + return FALSE; + for(i=0; i<4; i++) + out[11-i] = in[n++]; + if(in[n++] != '-') + return FALSE; + for(i=0; i<4; i++) + out[15-i] = in[n++]; + if(in[n++] != '-') + return FALSE; + for(i=0; i<2; i++) + { + out[17+i*2] = in[n++]; + out[16+i*2] = in[n++]; + } + if(in[n++] != '-') + return FALSE; + for( ; i<8; i++) + { + out[17+i*2] = in[n++]; + out[16+i*2] = in[n++]; + } + out[32]=0; + if(in[n++] != '}') + return FALSE; + if(in[n]) + return FALSE; + return TRUE; +} + + +/* tables for encoding and decoding base85 */ +static const unsigned char table_dec85[0x80] = { +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,0xff,0xff,0xff,0xff,0xff,0xff, +0xff,0x00,0xff,0xff,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0a,0x0b,0xff, +0x0c,0x0d,0x0e,0x0f,0x10,0x11,0x12,0x13,0x14,0x15,0xff,0xff,0xff,0x16,0xff,0x17, +0x18,0x19,0x1a,0x1b,0x1c,0x1d,0x1e,0x1f,0x20,0x21,0x22,0x23,0x24,0x25,0x26,0x27, +0x28,0x29,0x2a,0x2b,0x2c,0x2d,0x2e,0x2f,0x30,0x31,0x32,0x33,0xff,0x34,0x35,0x36, +0x37,0x38,0x39,0x3a,0x3b,0x3c,0x3d,0x3e,0x3f,0x40,0x41,0x42,0x43,0x44,0x45,0x46, +0x47,0x48,0x49,0x4a,0x4b,0x4c,0x4d,0x4e,0x4f,0x50,0x51,0x52,0xff,0x53,0x54,0xff, +}; + +static const char table_enc85[] = +"!$%&'()*+,-.0123456789=?@ABCDEFGHIJKLMNO" +"PQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwx" +"yz{}~"; + +/* + * Converts a base85 encoded guid into a GUID pointer + * Base85 encoded GUIDs should be 20 characters long. + * + * returns TRUE if successful, FALSE if not + */ +BOOL decode_base85_guid( LPCWSTR str, GUID *guid ) +{ + DWORD i, val = 0, base = 1, *p; + + if (!str) + return FALSE; + + p = (DWORD*) guid; + for( i=0; i<20; i++ ) + { + if( (i%5) == 0 ) + { + val = 0; + base = 1; + } + val += table_dec85[str[i]] * base; + if( str[i] >= 0x80 ) + return FALSE; + if( table_dec85[str[i]] == 0xff ) + return FALSE; + if( (i%5) == 4 ) + p[i/5] = val; + base *= 85; + } + return TRUE; +} + +/* + * Encodes a base85 guid given a GUID pointer + * Caller should provide a 21 character buffer for the encoded string. + * + * returns TRUE if successful, FALSE if not + */ +BOOL encode_base85_guid( GUID *guid, LPWSTR str ) +{ + unsigned int x, *p, i; + + p = (unsigned int*) guid; + for( i=0; i<4; i++ ) + { + x = p[i]; + *str++ = table_enc85[x%85]; + x = x/85; + *str++ = table_enc85[x%85]; + x = x/85; + *str++ = table_enc85[x%85]; + x = x/85; + *str++ = table_enc85[x%85]; + x = x/85; + *str++ = table_enc85[x%85]; + } + *str = 0; + + return TRUE; +} + + +UINT MSIREG_OpenUninstallKey(LPCWSTR szProduct, HKEY* key, BOOL create) +{ + UINT rc; + WCHAR keypath[0x200]; + TRACE("%s\n",debugstr_w(szProduct)); + + sprintfW(keypath,szUninstall_fmt,szProduct); + + if (create) + rc = RegCreateKeyW(HKEY_LOCAL_MACHINE, keypath, key); + else + rc = RegOpenKeyW(HKEY_LOCAL_MACHINE, keypath, key); + + return rc; +} + +UINT MSIREG_OpenUserProductsKey(LPCWSTR szProduct, HKEY* key, BOOL create) +{ + UINT rc; + WCHAR squished_pc[GUID_SIZE]; + WCHAR keypath[0x200]; + + TRACE("%s\n",debugstr_w(szProduct)); + squash_guid(szProduct,squished_pc); + TRACE("squished (%s)\n", debugstr_w(squished_pc)); + + sprintfW(keypath,szUserProduct_fmt,squished_pc); + + if (create) + rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key); + else + rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key); + + return rc; +} + +UINT MSIREG_OpenUserFeaturesKey(LPCWSTR szProduct, HKEY* key, BOOL create) +{ + UINT rc; + WCHAR squished_pc[GUID_SIZE]; + WCHAR keypath[0x200]; + + TRACE("%s\n",debugstr_w(szProduct)); + squash_guid(szProduct,squished_pc); + TRACE("squished (%s)\n", debugstr_w(squished_pc)); + + sprintfW(keypath,szUserFeatures_fmt,squished_pc); + + if (create) + rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key); + else + rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key); + + return rc; +} + +UINT MSIREG_OpenFeatures(HKEY* key) +{ + return RegCreateKeyW(HKEY_LOCAL_MACHINE,szInstaller_Features,key); +} + +UINT MSIREG_OpenFeaturesKey(LPCWSTR szProduct, HKEY* key, BOOL create) +{ + UINT rc; + WCHAR squished_pc[GUID_SIZE]; + WCHAR keypath[0x200]; + + TRACE("%s\n",debugstr_w(szProduct)); + squash_guid(szProduct,squished_pc); + TRACE("squished (%s)\n", debugstr_w(squished_pc)); + + sprintfW(keypath,szInstaller_Features_fmt,squished_pc); + + if (create) + rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key); + else + rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key); + + return rc; +} + +UINT MSIREG_OpenComponents(HKEY* key) +{ + return RegCreateKeyW(HKEY_LOCAL_MACHINE,szInstaller_Components,key); +} + +UINT MSIREG_OpenComponentsKey(LPCWSTR szComponent, HKEY* key, BOOL create) +{ + UINT rc; + WCHAR squished_cc[GUID_SIZE]; + WCHAR keypath[0x200]; + + TRACE("%s\n",debugstr_w(szComponent)); + squash_guid(szComponent,squished_cc); + TRACE("squished (%s)\n", debugstr_w(squished_cc)); + + sprintfW(keypath,szInstaller_Components_fmt,squished_cc); + + if (create) + rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key); + else + rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key); + + return rc; +} + +UINT MSIREG_OpenUserComponentsKey(LPCWSTR szComponent, HKEY* key, BOOL create) +{ + UINT rc; + WCHAR squished_cc[GUID_SIZE]; + WCHAR keypath[0x200]; + + TRACE("%s\n",debugstr_w(szComponent)); + squash_guid(szComponent,squished_cc); + TRACE("squished (%s)\n", debugstr_w(squished_cc)); + + sprintfW(keypath,szUser_Components_fmt,squished_cc); + + if (create) + rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key); + else + rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key); + + return rc; +} + +UINT MSIREG_OpenProductsKey(LPCWSTR szProduct, HKEY* key, BOOL create) +{ + UINT rc; + WCHAR squished_pc[GUID_SIZE]; + WCHAR keypath[0x200]; + + TRACE("%s\n",debugstr_w(szProduct)); + squash_guid(szProduct,squished_pc); + TRACE("squished (%s)\n", debugstr_w(squished_pc)); + + sprintfW(keypath,szInstaller_Products_fmt,squished_pc); + + if (create) + rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key); + else + rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key); + + return rc; +} + +UINT MSIREG_OpenUpgradeCodesKey(LPCWSTR szUpgradeCode, HKEY* key, BOOL create) +{ + UINT rc; + WCHAR squished_pc[GUID_SIZE]; + WCHAR keypath[0x200]; + + TRACE("%s\n",debugstr_w(szUpgradeCode)); + squash_guid(szUpgradeCode,squished_pc); + TRACE("squished (%s)\n", debugstr_w(squished_pc)); + + sprintfW(keypath,szInstaller_UpgradeCodes_fmt,squished_pc); + + if (create) + rc = RegCreateKeyW(HKEY_LOCAL_MACHINE,keypath,key); + else + rc = RegOpenKeyW(HKEY_LOCAL_MACHINE,keypath,key); + + return rc; +} + +UINT MSIREG_OpenUserUpgradeCodesKey(LPCWSTR szUpgradeCode, HKEY* key, BOOL create) +{ + UINT rc; + WCHAR squished_pc[GUID_SIZE]; + WCHAR keypath[0x200]; + + TRACE("%s\n",debugstr_w(szUpgradeCode)); + squash_guid(szUpgradeCode,squished_pc); + TRACE("squished (%s)\n", debugstr_w(squished_pc)); + + sprintfW(keypath,szInstaller_UserUpgradeCodes_fmt,squished_pc); + + if (create) + rc = RegCreateKeyW(HKEY_CURRENT_USER,keypath,key); + else + rc = RegOpenKeyW(HKEY_CURRENT_USER,keypath,key); + + return rc; +} + + +/************************************************************************* + * MsiDecomposeDescriptorW [MSI.@] + * + * Decomposes an MSI descriptor into product, feature and component parts. + * An MSI descriptor is a string of the form: + * [base 85 guid] [feature code] '>' [base 85 guid] + * + * PARAMS + * szDescriptor [I] the descriptor to decompose + * szProduct [O] buffer of MAX_FEATURE_CHARS+1 for the product guid + * szFeature [O] buffer of MAX_FEATURE_CHARS+1 for the feature code + * szComponent [O] buffer of MAX_FEATURE_CHARS+1 for the component guid + * pUsed [O] the length of the descriptor + * + * RETURNS + * ERROR_SUCCESS if everything worked correctly + * ERROR_INVALID_PARAMETER if the descriptor was invalid + * + */ +UINT WINAPI MsiDecomposeDescriptorW( LPCWSTR szDescriptor, LPWSTR szProduct, + LPWSTR szFeature, LPWSTR szComponent, DWORD *pUsed ) +{ + UINT r, len; + LPWSTR p; + GUID product, component; + + TRACE("%s %p %p %p %p\n", debugstr_w(szDescriptor), szProduct, + szFeature, szComponent, pUsed); + + r = decode_base85_guid( szDescriptor, &product ); + if( !r ) + return ERROR_INVALID_PARAMETER; + + TRACE("product %s\n", debugstr_guid( &product )); + + p = strchrW(&szDescriptor[20],'>'); + if( !p ) + return ERROR_INVALID_PARAMETER; + + len = (p - &szDescriptor[20]); + if( len > MAX_FEATURE_CHARS ) + return ERROR_INVALID_PARAMETER; + if (szFeature) + { + memcpy( szFeature, &szDescriptor[20], len*sizeof(WCHAR) ); + szFeature[len] = 0; + } + + TRACE("feature %s\n", debugstr_w( &szDescriptor[20] )); + + r = decode_base85_guid( p+1, &component ); + if( !r ) + return ERROR_INVALID_PARAMETER; + + TRACE("component %s\n", debugstr_guid( &component )); + + if (szProduct) + StringFromGUID2( &product, szProduct, MAX_FEATURE_CHARS+1 ); + if (szComponent) + StringFromGUID2( &component, szComponent, MAX_FEATURE_CHARS+1 ); + len = ( &p[21] - szDescriptor ); + + TRACE("length = %d\n", len); + *pUsed = len; + + return ERROR_SUCCESS; +} + +UINT WINAPI MsiDecomposeDescriptorA( LPCSTR szDescriptor, LPSTR szProduct, + LPSTR szFeature, LPSTR szComponent, DWORD *pUsed ) +{ + WCHAR product[MAX_FEATURE_CHARS+1]; + WCHAR feature[MAX_FEATURE_CHARS+1]; + WCHAR component[MAX_FEATURE_CHARS+1]; + LPWSTR str = NULL, p = NULL, f = NULL, c = NULL; + UINT r; + + TRACE("%s %p %p %p %p\n", debugstr_a(szDescriptor), szProduct, + szFeature, szComponent, pUsed); + + str = strdupAtoW( szDescriptor ); + if( szDescriptor && !str ) + return ERROR_OUTOFMEMORY; + + if (szProduct) + p = product; + if (szFeature) + f = feature; + if (szComponent) + c = component; + + r = MsiDecomposeDescriptorW( str, p, f, c, pUsed ); + + WideCharToMultiByte( CP_ACP, 0, p, MAX_FEATURE_CHARS+1, + szProduct, MAX_FEATURE_CHARS+1, NULL, NULL ); + WideCharToMultiByte( CP_ACP, 0, f, MAX_FEATURE_CHARS+1, + szFeature, MAX_FEATURE_CHARS+1, NULL, NULL ); + WideCharToMultiByte( CP_ACP, 0, c, MAX_FEATURE_CHARS+1, + szComponent, MAX_FEATURE_CHARS+1, NULL, NULL ); + + msi_free( str ); + + return r; +} + +UINT WINAPI MsiEnumProductsA(DWORD index, LPSTR lpguid) +{ + DWORD r; + WCHAR szwGuid[GUID_SIZE]; + + TRACE("%ld %p\n",index,lpguid); + + if (NULL == lpguid) + return ERROR_INVALID_PARAMETER; + r = MsiEnumProductsW(index, szwGuid); + if( r == ERROR_SUCCESS ) + WideCharToMultiByte(CP_ACP, 0, szwGuid, -1, lpguid, GUID_SIZE, NULL, NULL); + + return r; +} + +UINT WINAPI MsiEnumProductsW(DWORD index, LPWSTR lpguid) +{ + HKEY hkeyFeatures = 0; + DWORD r; + WCHAR szKeyName[SQUISH_GUID_SIZE]; + + TRACE("%ld %p\n",index,lpguid); + + if (NULL == lpguid) + return ERROR_INVALID_PARAMETER; + + r = MSIREG_OpenFeatures(&hkeyFeatures); + if( r != ERROR_SUCCESS ) + return ERROR_NO_MORE_ITEMS; + + r = RegEnumKeyW(hkeyFeatures, index, szKeyName, SQUISH_GUID_SIZE); + if( r == ERROR_SUCCESS ) + unsquash_guid(szKeyName, lpguid); + RegCloseKey(hkeyFeatures); + + return r; +} + +UINT WINAPI MsiEnumFeaturesA(LPCSTR szProduct, DWORD index, + LPSTR szFeature, LPSTR szParent) +{ + DWORD r; + WCHAR szwFeature[GUID_SIZE], szwParent[GUID_SIZE]; + LPWSTR szwProduct = NULL; + + TRACE("%s %ld %p %p\n",debugstr_a(szProduct),index,szFeature,szParent); + + if( szProduct ) + { + szwProduct = strdupAtoW( szProduct ); + if( !szwProduct ) + return ERROR_OUTOFMEMORY; + } + + r = MsiEnumFeaturesW(szwProduct, index, szwFeature, szwParent); + if( r == ERROR_SUCCESS ) + { + WideCharToMultiByte(CP_ACP, 0, szwFeature, -1, + szFeature, GUID_SIZE, NULL, NULL); + WideCharToMultiByte(CP_ACP, 0, szwParent, -1, + szParent, GUID_SIZE, NULL, NULL); + } + + msi_free( szwProduct); + + return r; +} + +UINT WINAPI MsiEnumFeaturesW(LPCWSTR szProduct, DWORD index, + LPWSTR szFeature, LPWSTR szParent) +{ + HKEY hkeyProduct = 0; + DWORD r, sz; + + TRACE("%s %ld %p %p\n",debugstr_w(szProduct),index,szFeature,szParent); + + r = MSIREG_OpenFeaturesKey(szProduct,&hkeyProduct,FALSE); + if( r != ERROR_SUCCESS ) + return ERROR_NO_MORE_ITEMS; + + sz = GUID_SIZE; + r = RegEnumValueW(hkeyProduct, index, szFeature, &sz, NULL, NULL, NULL, NULL); + RegCloseKey(hkeyProduct); + + return r; +} + +UINT WINAPI MsiEnumComponentsA(DWORD index, LPSTR lpguid) +{ + DWORD r; + WCHAR szwGuid[GUID_SIZE]; + + TRACE("%ld %p\n",index,lpguid); + + r = MsiEnumComponentsW(index, szwGuid); + if( r == ERROR_SUCCESS ) + WideCharToMultiByte(CP_ACP, 0, szwGuid, -1, lpguid, GUID_SIZE, NULL, NULL); + + return r; +} + +UINT WINAPI MsiEnumComponentsW(DWORD index, LPWSTR lpguid) +{ + HKEY hkeyComponents = 0; + DWORD r; + WCHAR szKeyName[SQUISH_GUID_SIZE]; + + TRACE("%ld %p\n",index,lpguid); + + r = MSIREG_OpenComponents(&hkeyComponents); + if( r != ERROR_SUCCESS ) + return ERROR_NO_MORE_ITEMS; + + r = RegEnumKeyW(hkeyComponents, index, szKeyName, SQUISH_GUID_SIZE); + if( r == ERROR_SUCCESS ) + unsquash_guid(szKeyName, lpguid); + RegCloseKey(hkeyComponents); + + return r; +} + +UINT WINAPI MsiEnumClientsA(LPCSTR szComponent, DWORD index, LPSTR szProduct) +{ + DWORD r; + WCHAR szwProduct[GUID_SIZE]; + LPWSTR szwComponent = NULL; + + TRACE("%s %ld %p\n",debugstr_a(szComponent),index,szProduct); + + if( szComponent ) + { + szwComponent = strdupAtoW( szComponent ); + if( !szwComponent ) + return ERROR_OUTOFMEMORY; + } + + r = MsiEnumClientsW(szComponent?szwComponent:NULL, index, szwProduct); + if( r == ERROR_SUCCESS ) + { + WideCharToMultiByte(CP_ACP, 0, szwProduct, -1, + szProduct, GUID_SIZE, NULL, NULL); + } + + msi_free( szwComponent); + + return r; +} + +UINT WINAPI MsiEnumClientsW(LPCWSTR szComponent, DWORD index, LPWSTR szProduct) +{ + HKEY hkeyComp = 0; + DWORD r, sz; + WCHAR szValName[SQUISH_GUID_SIZE]; + + TRACE("%s %ld %p\n",debugstr_w(szComponent),index,szProduct); + + r = MSIREG_OpenComponentsKey(szComponent,&hkeyComp,FALSE); + if( r != ERROR_SUCCESS ) + return ERROR_NO_MORE_ITEMS; + + sz = SQUISH_GUID_SIZE; + r = RegEnumValueW(hkeyComp, index, szValName, &sz, NULL, NULL, NULL, NULL); + if( r == ERROR_SUCCESS ) + unsquash_guid(szValName, szProduct); + + RegCloseKey(hkeyComp); + + return r; +} + +UINT WINAPI MSI_EnumComponentQualifiers( LPCWSTR szComponent, DWORD iIndex, + awstring *lpQualBuf, DWORD* pcchQual, + awstring *lpAppBuf, DWORD* pcchAppBuf ) +{ + DWORD name_sz, val_sz, name_max, val_max, type, ofs; + LPWSTR name = NULL, val = NULL; + UINT r, r2; + HKEY key; + + TRACE("%s %08lx %p %p %p %p\n", debugstr_w(szComponent), iIndex, + lpQualBuf, pcchQual, lpAppBuf, pcchAppBuf); + + if (!szComponent) + return ERROR_INVALID_PARAMETER; + + r = MSIREG_OpenUserComponentsKey( szComponent, &key, FALSE ); + if (r != ERROR_SUCCESS) + return ERROR_UNKNOWN_COMPONENT; + + /* figure out how big the name is we want to return */ + name_max = 0x10; + r = ERROR_OUTOFMEMORY; + name = msi_alloc( name_max * sizeof(WCHAR) ); + if (!name) + goto end; + + val_max = 0x10; + r = ERROR_OUTOFMEMORY; + val = msi_alloc( val_max ); + if (!val) + goto end; + + /* loop until we allocate enough memory */ + while (1) + { + name_sz = name_max; + val_sz = val_max; + r = RegEnumValueW( key, iIndex, name, &name_sz, + NULL, &type, (LPBYTE)val, &val_sz ); + if (r == ERROR_SUCCESS) + break; + if (r != ERROR_MORE_DATA) + goto end; + + if (type != REG_MULTI_SZ) + { + ERR("component data has wrong type (%ld)\n", type); + goto end; + } + + r = ERROR_OUTOFMEMORY; + if ((name_sz+1) >= name_max) + { + name_max *= 2; + msi_free( name ); + name = msi_alloc( name_max * sizeof (WCHAR) ); + if (!name) + goto end; + continue; + } + if (val_sz > val_max) + { + val_max = val_sz + sizeof (WCHAR); + val = msi_alloc( val_max * sizeof (WCHAR) ); + if (!val) + goto end; + continue; + } + ERR("should be enough data, but isn't %ld %ld\n", name_sz, val_sz ); + goto end; + } + + ofs = 0; + r = MsiDecomposeDescriptorW( val, NULL, NULL, NULL, &ofs ); + if (r != ERROR_SUCCESS) + goto end; + + TRACE("Providing %s and %s\n", debugstr_w(name), debugstr_w(val+ofs)); + + r = msi_strcpy_to_awstring( name, lpQualBuf, pcchQual ); + r2 = msi_strcpy_to_awstring( val+ofs, lpAppBuf, pcchAppBuf ); + + if (r2 != ERROR_SUCCESS) + r = r2; + +end: + msi_free(val); + msi_free(name); + RegCloseKey(key); + + return r; +} + +/************************************************************************* + * MsiEnumComponentQualifiersA [MSI.@] + */ +UINT WINAPI MsiEnumComponentQualifiersA( LPCSTR szComponent, DWORD iIndex, + LPSTR lpQualifierBuf, DWORD* pcchQualifierBuf, + LPSTR lpApplicationDataBuf, DWORD* pcchApplicationDataBuf ) +{ + awstring qual, appdata; + LPWSTR comp; + UINT r; + + TRACE("%s %08lx %p %p %p %p\n", debugstr_a(szComponent), iIndex, + lpQualifierBuf, pcchQualifierBuf, lpApplicationDataBuf, + pcchApplicationDataBuf); + + comp = strdupAtoW( szComponent ); + if (szComponent && !comp) + return ERROR_OUTOFMEMORY; + + qual.unicode = FALSE; + qual.str.a = lpQualifierBuf; + + appdata.unicode = FALSE; + appdata.str.a = lpApplicationDataBuf; + + r = MSI_EnumComponentQualifiers( comp, iIndex, + &qual, pcchQualifierBuf, &appdata, pcchApplicationDataBuf ); + msi_free( comp ); + return r; +} + +/************************************************************************* + * MsiEnumComponentQualifiersW [MSI.@] + */ +UINT WINAPI MsiEnumComponentQualifiersW( LPCWSTR szComponent, DWORD iIndex, + LPWSTR lpQualifierBuf, DWORD* pcchQualifierBuf, + LPWSTR lpApplicationDataBuf, DWORD* pcchApplicationDataBuf ) +{ + awstring qual, appdata; + + TRACE("%s %08lx %p %p %p %p\n", debugstr_w(szComponent), iIndex, + lpQualifierBuf, pcchQualifierBuf, lpApplicationDataBuf, + pcchApplicationDataBuf); + + qual.unicode = TRUE; + qual.str.w = lpQualifierBuf; + + appdata.unicode = TRUE; + appdata.str.w = lpApplicationDataBuf; + + return MSI_EnumComponentQualifiers( szComponent, iIndex, + &qual, pcchQualifierBuf, &appdata, pcchApplicationDataBuf ); +} + +/************************************************************************* + * MsiEnumRelatedProductsW [MSI.@] + * + */ +UINT WINAPI MsiEnumRelatedProductsW(LPCWSTR szUpgradeCode, DWORD dwReserved, + DWORD iProductIndex, LPWSTR lpProductBuf) +{ + UINT r; + HKEY hkey; + WCHAR szKeyName[SQUISH_GUID_SIZE]; + + TRACE("%s %lu %lu %p\n", debugstr_w(szUpgradeCode), dwReserved, + iProductIndex, lpProductBuf); + + if (NULL == szUpgradeCode) + return ERROR_INVALID_PARAMETER; + if (NULL == lpProductBuf) + return ERROR_INVALID_PARAMETER; + + r = MSIREG_OpenUpgradeCodesKey(szUpgradeCode, &hkey, FALSE); + if (r != ERROR_SUCCESS) + return ERROR_NO_MORE_ITEMS; + + r = RegEnumKeyW(hkey, iProductIndex, szKeyName, SQUISH_GUID_SIZE); + if( r == ERROR_SUCCESS ) + unsquash_guid(szKeyName, lpProductBuf); + RegCloseKey(hkey); + + return r; +} + +/************************************************************************* + * MsiEnumRelatedProductsA [MSI.@] + * + */ +UINT WINAPI MsiEnumRelatedProductsA(LPCSTR szUpgradeCode, DWORD dwReserved, + DWORD iProductIndex, LPSTR lpProductBuf) +{ + LPWSTR szwUpgradeCode = NULL; + WCHAR productW[GUID_SIZE]; + UINT r; + + TRACE("%s %lu %lu %p\n", debugstr_a(szUpgradeCode), dwReserved, + iProductIndex, lpProductBuf); + + if (szUpgradeCode) + { + szwUpgradeCode = strdupAtoW( szUpgradeCode ); + if( !szwUpgradeCode ) + return ERROR_OUTOFMEMORY; + } + + r = MsiEnumRelatedProductsW( szwUpgradeCode, dwReserved, + iProductIndex, productW ); + if (r == ERROR_SUCCESS) + { + WideCharToMultiByte( CP_ACP, 0, productW, GUID_SIZE, + lpProductBuf, GUID_SIZE, NULL, NULL ); + } + msi_free( szwUpgradeCode); + return r; +} diff --git a/reactos/dll/msi/regsvr.c b/reactos/dll/msi/regsvr.c new file mode 100644 index 00000000000..f49b78a3bd5 --- /dev/null +++ b/reactos/dll/msi/regsvr.c @@ -0,0 +1,626 @@ +/* + * self-registerable dll functions for msi.dll + * + * Copyright (C) 2004 Raphael Junqueira + * + * 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 "config.h" + +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "winreg.h" +#include "winerror.h" + +#include "ole2.h" +#include "olectl.h" + +#include "wine/debug.h" + +#include "msi.h" +#include "initguid.h" +#include "msipriv.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +/* + * Near the bottom of this file are the exported DllRegisterServer and + * DllUnregisterServer, which make all this worthwhile. + */ + +/*********************************************************************** + * interface for self-registering + */ +struct regsvr_interface { + IID const *iid; /* NULL for end of list */ + LPCSTR name; /* can be NULL to omit */ + IID const *base_iid; /* can be NULL to omit */ + int num_methods; /* can be <0 to omit */ + CLSID const *ps_clsid; /* can be NULL to omit */ + CLSID const *ps_clsid32; /* can be NULL to omit */ +}; + +static HRESULT register_interfaces(struct regsvr_interface const *list); +static HRESULT unregister_interfaces(struct regsvr_interface const *list); + +/** + * @todo: maybe adding typelibs support here + * [Software\\Classes\\CLSID\\{000C1090-0000-0000-C000-000000000046}\\TypeLib] 1080380217 + * @="{000C1092-0000-0000-C000-000000000046}" + */ +struct regsvr_coclass { + CLSID const *clsid; /* NULL for end of list */ + LPCSTR name; /* can be NULL to omit */ + LPCSTR iph32; /* can be NULL to omit */ + LPCSTR ips; /* can be NULL to omit */ + LPCSTR ips32; /* can be NULL to omit */ + LPCSTR ips32_tmodel; /* can be NULL to omit, if apartment, iph32 must be set */ + LPCSTR progid; /* can be NULL to omit */ + LPCSTR viprogid; /* can be NULL to omit */ + LPCSTR progid_extra; /* can be NULL to omit */ +}; + +static HRESULT register_coclasses(struct regsvr_coclass const *list); +static HRESULT unregister_coclasses(struct regsvr_coclass const *list); + +/*********************************************************************** + * static string constants + */ +static WCHAR const interface_keyname[10] = { + 'I', 'n', 't', 'e', 'r', 'f', 'a', 'c', 'e', 0 }; +static WCHAR const base_ifa_keyname[14] = { + 'B', 'a', 's', 'e', 'I', 'n', 't', 'e', 'r', 'f', 'a', 'c', + 'e', 0 }; +static WCHAR const num_methods_keyname[11] = { + 'N', 'u', 'm', 'M', 'e', 't', 'h', 'o', 'd', 's', 0 }; +static WCHAR const ps_clsid_keyname[15] = { + 'P', 'r', 'o', 'x', 'y', 'S', 't', 'u', 'b', 'C', 'l', 's', + 'i', 'd', 0 }; +static WCHAR const ps_clsid32_keyname[17] = { + 'P', 'r', 'o', 'x', 'y', 'S', 't', 'u', 'b', 'C', 'l', 's', + 'i', 'd', '3', '2', 0 }; +static WCHAR const clsid_keyname[6] = { + 'C', 'L', 'S', 'I', 'D', 0 }; +static WCHAR const curver_keyname[7] = { + 'C', 'u', 'r', 'V', 'e', 'r', 0 }; +static WCHAR const iph32_keyname[] = { + 'I', 'n', 'P', 'r', 'o', 'c', 'H', 'a', 'n', 'd', 'l', 'e', 'r', + '3', '2', 0 }; +static WCHAR const ips_keyname[13] = { + 'I', 'n', 'P', 'r', 'o', 'c', 'S', 'e', 'r', 'v', 'e', 'r', + 0 }; +static WCHAR const ips32_keyname[15] = { + 'I', 'n', 'P', 'r', 'o', 'c', 'S', 'e', 'r', 'v', 'e', 'r', + '3', '2', 0 }; +static WCHAR const progid_keyname[7] = { + 'P', 'r', 'o', 'g', 'I', 'D', 0 }; +static WCHAR const viprogid_keyname[25] = { + 'V', 'e', 'r', 's', 'i', 'o', 'n', 'I', 'n', 'd', 'e', 'p', + 'e', 'n', 'd', 'e', 'n', 't', 'P', 'r', 'o', 'g', 'I', 'D', + 0 }; +static char const tmodel_valuename[] = "ThreadingModel"; + +/*********************************************************************** + * static helper functions + */ +static LONG register_key_guid(HKEY base, WCHAR const *name, GUID const *guid); +static LONG register_key_defvalueW(HKEY base, WCHAR const *name, + WCHAR const *value); +static LONG register_key_defvalueA(HKEY base, WCHAR const *name, + char const *value); +static LONG register_progid(WCHAR const *clsid, + char const *progid, char const *curver_progid, + char const *name, char const *extra); +static LONG recursive_delete_key(HKEY key); +static LONG recursive_delete_keyA(HKEY base, char const *name); +static LONG recursive_delete_keyW(HKEY base, WCHAR const *name); + +/*********************************************************************** + * register_interfaces + */ +static HRESULT register_interfaces(struct regsvr_interface const *list) { + LONG res = ERROR_SUCCESS; + HKEY interface_key; + + res = RegCreateKeyExW(HKEY_CLASSES_ROOT, interface_keyname, 0, NULL, 0, + KEY_READ | KEY_WRITE, NULL, &interface_key, NULL); + if (res != ERROR_SUCCESS) goto error_return; + + for (; res == ERROR_SUCCESS && list->iid; ++list) { + WCHAR buf[39]; + HKEY iid_key; + + StringFromGUID2(list->iid, buf, 39); + res = RegCreateKeyExW(interface_key, buf, 0, NULL, 0, + KEY_READ | KEY_WRITE, NULL, &iid_key, NULL); + if (res != ERROR_SUCCESS) goto error_close_interface_key; + + if (list->name) { + res = RegSetValueExA(iid_key, NULL, 0, REG_SZ, + (CONST BYTE*)(list->name), + strlen(list->name) + 1); + if (res != ERROR_SUCCESS) goto error_close_iid_key; + } + + if (list->base_iid) { + register_key_guid(iid_key, base_ifa_keyname, list->base_iid); + if (res != ERROR_SUCCESS) goto error_close_iid_key; + } + + if (0 <= list->num_methods) { + static WCHAR const fmt[3] = { '%', 'd', 0 }; + HKEY key; + + res = RegCreateKeyExW(iid_key, num_methods_keyname, 0, NULL, 0, + KEY_READ | KEY_WRITE, NULL, &key, NULL); + if (res != ERROR_SUCCESS) goto error_close_iid_key; + + wsprintfW(buf, fmt, list->num_methods); + res = RegSetValueExW(key, NULL, 0, REG_SZ, + (CONST BYTE*)buf, + (lstrlenW(buf) + 1) * sizeof(WCHAR)); + RegCloseKey(key); + + if (res != ERROR_SUCCESS) goto error_close_iid_key; + } + + if (list->ps_clsid) { + register_key_guid(iid_key, ps_clsid_keyname, list->ps_clsid); + if (res != ERROR_SUCCESS) goto error_close_iid_key; + } + + if (list->ps_clsid32) { + register_key_guid(iid_key, ps_clsid32_keyname, list->ps_clsid32); + if (res != ERROR_SUCCESS) goto error_close_iid_key; + } + + error_close_iid_key: + RegCloseKey(iid_key); + } + +error_close_interface_key: + RegCloseKey(interface_key); +error_return: + return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK; +} + +/*********************************************************************** + * unregister_interfaces + */ +static HRESULT unregister_interfaces(struct regsvr_interface const *list) { + LONG res = ERROR_SUCCESS; + HKEY interface_key; + + res = RegOpenKeyExW(HKEY_CLASSES_ROOT, interface_keyname, 0, + KEY_READ | KEY_WRITE, &interface_key); + if (res == ERROR_FILE_NOT_FOUND) return S_OK; + if (res != ERROR_SUCCESS) goto error_return; + + for (; res == ERROR_SUCCESS && list->iid; ++list) { + WCHAR buf[39]; + + StringFromGUID2(list->iid, buf, 39); + res = recursive_delete_keyW(interface_key, buf); + } + + RegCloseKey(interface_key); +error_return: + return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK; +} + +/*********************************************************************** + * register_coclasses + */ +static HRESULT register_coclasses(struct regsvr_coclass const *list) { + LONG res = ERROR_SUCCESS; + HKEY coclass_key; + + res = RegCreateKeyExW(HKEY_CLASSES_ROOT, clsid_keyname, 0, NULL, 0, + KEY_READ | KEY_WRITE, NULL, &coclass_key, NULL); + if (res != ERROR_SUCCESS) goto error_return; + + for (; res == ERROR_SUCCESS && list->clsid; ++list) { + WCHAR buf[39]; + HKEY clsid_key; + + StringFromGUID2(list->clsid, buf, 39); + res = RegCreateKeyExW(coclass_key, buf, 0, NULL, 0, + KEY_READ | KEY_WRITE, NULL, &clsid_key, NULL); + if (res != ERROR_SUCCESS) goto error_close_coclass_key; + + if (list->name) { + res = RegSetValueExA(clsid_key, NULL, 0, REG_SZ, + (CONST BYTE*)(list->name), + strlen(list->name) + 1); + if (res != ERROR_SUCCESS) goto error_close_clsid_key; + } + + if (list->iph32) { + HKEY iph32_key; + + res = RegCreateKeyExW(clsid_key, iph32_keyname, 0, NULL, 0, + KEY_READ | KEY_WRITE, NULL, + &iph32_key, NULL); + if (res != ERROR_SUCCESS) goto error_close_clsid_key; + + res = RegSetValueExA(iph32_key, NULL, 0, REG_SZ, + (CONST BYTE*)list->iph32, + lstrlenA(list->iph32) + 1); + RegCloseKey(iph32_key); + if (res != ERROR_SUCCESS) goto error_close_clsid_key; + } + + if (list->ips) { + res = register_key_defvalueA(clsid_key, ips_keyname, list->ips); + if (res != ERROR_SUCCESS) goto error_close_clsid_key; + } + + if (list->ips32) { + HKEY ips32_key; + + res = RegCreateKeyExW(clsid_key, ips32_keyname, 0, NULL, 0, + KEY_READ | KEY_WRITE, NULL, + &ips32_key, NULL); + if (res != ERROR_SUCCESS) goto error_close_clsid_key; + + res = RegSetValueExA(ips32_key, NULL, 0, REG_SZ, + (CONST BYTE*)list->ips32, + lstrlenA(list->ips32) + 1); + if (res == ERROR_SUCCESS && list->ips32_tmodel) + res = RegSetValueExA(ips32_key, tmodel_valuename, 0, REG_SZ, + (CONST BYTE*)list->ips32_tmodel, + strlen(list->ips32_tmodel) + 1); + RegCloseKey(ips32_key); + if (res != ERROR_SUCCESS) goto error_close_clsid_key; + } + + if (list->progid) { + res = register_key_defvalueA(clsid_key, progid_keyname, + list->progid); + if (res != ERROR_SUCCESS) goto error_close_clsid_key; + + res = register_progid(buf, list->progid, NULL, + list->name, list->progid_extra); + if (res != ERROR_SUCCESS) goto error_close_clsid_key; + } + + if (list->viprogid) { + res = register_key_defvalueA(clsid_key, viprogid_keyname, + list->viprogid); + if (res != ERROR_SUCCESS) goto error_close_clsid_key; + + res = register_progid(buf, list->viprogid, list->progid, + list->name, list->progid_extra); + if (res != ERROR_SUCCESS) goto error_close_clsid_key; + } + + error_close_clsid_key: + RegCloseKey(clsid_key); + } + +error_close_coclass_key: + RegCloseKey(coclass_key); +error_return: + return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK; +} + +/*********************************************************************** + * unregister_coclasses + */ +static HRESULT unregister_coclasses(struct regsvr_coclass const *list) { + LONG res = ERROR_SUCCESS; + HKEY coclass_key; + + res = RegOpenKeyExW(HKEY_CLASSES_ROOT, clsid_keyname, 0, + KEY_READ | KEY_WRITE, &coclass_key); + if (res == ERROR_FILE_NOT_FOUND) return S_OK; + if (res != ERROR_SUCCESS) goto error_return; + + for (; res == ERROR_SUCCESS && list->clsid; ++list) { + WCHAR buf[39]; + + StringFromGUID2(list->clsid, buf, 39); + res = recursive_delete_keyW(coclass_key, buf); + if (res != ERROR_SUCCESS) goto error_close_coclass_key; + + if (list->progid) { + res = recursive_delete_keyA(HKEY_CLASSES_ROOT, list->progid); + if (res != ERROR_SUCCESS) goto error_close_coclass_key; + } + + if (list->viprogid) { + res = recursive_delete_keyA(HKEY_CLASSES_ROOT, list->viprogid); + if (res != ERROR_SUCCESS) goto error_close_coclass_key; + } + } + +error_close_coclass_key: + RegCloseKey(coclass_key); +error_return: + return res != ERROR_SUCCESS ? HRESULT_FROM_WIN32(res) : S_OK; +} + +/*********************************************************************** + * regsvr_key_guid + */ +static LONG register_key_guid(HKEY base, WCHAR const *name, GUID const *guid) { + WCHAR buf[39]; + + StringFromGUID2(guid, buf, 39); + return register_key_defvalueW(base, name, buf); +} + +/*********************************************************************** + * regsvr_key_defvalueW + */ +static LONG register_key_defvalueW( + HKEY base, + WCHAR const *name, + WCHAR const *value) { + LONG res; + HKEY key; + + res = RegCreateKeyExW(base, name, 0, NULL, 0, + KEY_READ | KEY_WRITE, NULL, &key, NULL); + if (res != ERROR_SUCCESS) return res; + res = RegSetValueExW(key, NULL, 0, REG_SZ, (CONST BYTE*)value, + (lstrlenW(value) + 1) * sizeof(WCHAR)); + RegCloseKey(key); + return res; +} + +/*********************************************************************** + * regsvr_key_defvalueA + */ +static LONG register_key_defvalueA( + HKEY base, + WCHAR const *name, + char const *value) { + LONG res; + HKEY key; + + res = RegCreateKeyExW(base, name, 0, NULL, 0, + KEY_READ | KEY_WRITE, NULL, &key, NULL); + if (res != ERROR_SUCCESS) return res; + res = RegSetValueExA(key, NULL, 0, REG_SZ, (CONST BYTE*)value, + lstrlenA(value) + 1); + RegCloseKey(key); + return res; +} + +/*********************************************************************** + * regsvr_progid + */ +static LONG register_progid( + WCHAR const *clsid, + char const *progid, + char const *curver_progid, + char const *name, + char const *extra) { + LONG res; + HKEY progid_key; + + res = RegCreateKeyExA(HKEY_CLASSES_ROOT, progid, 0, + NULL, 0, KEY_READ | KEY_WRITE, NULL, + &progid_key, NULL); + if (res != ERROR_SUCCESS) return res; + + if (name) { + res = RegSetValueExA(progid_key, NULL, 0, REG_SZ, + (CONST BYTE*)name, strlen(name) + 1); + if (res != ERROR_SUCCESS) goto error_close_progid_key; + } + + if (clsid) { + res = register_key_defvalueW(progid_key, clsid_keyname, clsid); + if (res != ERROR_SUCCESS) goto error_close_progid_key; + } + + if (curver_progid) { + res = register_key_defvalueA(progid_key, curver_keyname, + curver_progid); + if (res != ERROR_SUCCESS) goto error_close_progid_key; + } + + if (extra) { + HKEY extra_key; + + res = RegCreateKeyExA(progid_key, extra, 0, + NULL, 0, KEY_READ | KEY_WRITE, NULL, + &extra_key, NULL); + if (res == ERROR_SUCCESS) + RegCloseKey(extra_key); + } + +error_close_progid_key: + RegCloseKey(progid_key); + return res; +} + +/*********************************************************************** + * recursive_delete_key + */ +static LONG recursive_delete_key(HKEY key) { + LONG res; + WCHAR subkey_name[MAX_PATH]; + DWORD cName; + HKEY subkey; + + for (;;) { + cName = sizeof(subkey_name) / sizeof(WCHAR); + res = RegEnumKeyExW(key, 0, subkey_name, &cName, + NULL, NULL, NULL, NULL); + if (res != ERROR_SUCCESS && res != ERROR_MORE_DATA) { + res = ERROR_SUCCESS; /* presumably we're done enumerating */ + break; + } + res = RegOpenKeyExW(key, subkey_name, 0, + KEY_READ | KEY_WRITE, &subkey); + if (res == ERROR_FILE_NOT_FOUND) continue; + if (res != ERROR_SUCCESS) break; + + res = recursive_delete_key(subkey); + RegCloseKey(subkey); + if (res != ERROR_SUCCESS) break; + } + + if (res == ERROR_SUCCESS) res = RegDeleteKeyW(key, 0); + return res; +} + +/*********************************************************************** + * recursive_delete_keyA + */ +static LONG recursive_delete_keyA(HKEY base, char const *name) { + LONG res; + HKEY key; + + res = RegOpenKeyExA(base, name, 0, KEY_READ | KEY_WRITE, &key); + if (res == ERROR_FILE_NOT_FOUND) return ERROR_SUCCESS; + if (res != ERROR_SUCCESS) return res; + res = recursive_delete_key(key); + RegCloseKey(key); + return res; +} + +/*********************************************************************** + * recursive_delete_keyW + */ +static LONG recursive_delete_keyW(HKEY base, WCHAR const *name) { + LONG res; + HKEY key; + + res = RegOpenKeyExW(base, name, 0, KEY_READ | KEY_WRITE, &key); + if (res == ERROR_FILE_NOT_FOUND) return ERROR_SUCCESS; + if (res != ERROR_SUCCESS) return res; + res = recursive_delete_key(key); + RegCloseKey(key); + return res; +} + +/*********************************************************************** + * coclass list + */ +static struct regsvr_coclass const coclass_list[] = { + { + &CLSID_IMsiServer, + "Msi install server", + "ole32.dll", + NULL, + "msi.dll", + "Apartment", + "WindowsInstaller.Installer", + NULL + }, + { + &CLSID_IMsiServerMessage, + "Wine Installer Message RPC", + NULL, + NULL, + "msi.dll", + NULL, + "WindowsInstaller.Message", + NULL + }, + { + &CLSID_IMsiServerX1, + "Msi install server", + "ole32.dll", + NULL, + "msi.dll", + "Apartment", + "WindowsInstaller.Installer", + NULL + }, + { + &CLSID_IMsiServerX2, + "Msi install server", + "ole32.dll", + NULL, + "msi.dll", + "Apartment", + "WindowsInstaller.Installer", + NULL + }, + { + &CLSID_IMsiServerX3, + "Msi install server", + "ole32.dll", + NULL, + "msi.dll", + "Apartment", + "WindowsInstaller.Installer", + NULL + }, + { NULL } /* list terminator */ +}; + +/*********************************************************************** + * interface list + */ +/* + * we should declare: (@see ole32/regsvr.c for examples) + [-HKEY_CLASSES_ROOT\Interface\{000C101C-0000-0000-C000-000000000046}] + [-HKEY_CLASSES_ROOT\Interface\{000C101D-0000-0000-C000-000000000046}] + [-HKEY_CLASSES_ROOT\Interface\{000C1025-0000-0000-C000-000000000046}] + [-HKEY_CLASSES_ROOT\Interface\{000C1033-0000-0000-C000-000000000046}] + [-HKEY_CLASSES_ROOT\Interface\{000C1090-0000-0000-C000-000000000046}] + [-HKEY_CLASSES_ROOT\Interface\{000C1093-0000-0000-C000-000000000046}] + [-HKEY_CLASSES_ROOT\Interface\{000C1095-0000-0000-C000-000000000046}] + [-HKEY_CLASSES_ROOT\Interface\{000C109A-0000-0000-C000-000000000046}] + [-HKEY_CLASSES_ROOT\Interface\{000C109B-0000-0000-C000-000000000046}] + [-HKEY_CLASSES_ROOT\Interface\{000C109C-0000-0000-C000-000000000046}] + [-HKEY_CLASSES_ROOT\Interface\{000C109D-0000-0000-C000-000000000046}] + [-HKEY_CLASSES_ROOT\Interface\{000C109E-0000-0000-C000-000000000046}] + [-HKEY_CLASSES_ROOT\Interface\{000C109F-0000-0000-C000-000000000046}] +*/ +static struct regsvr_interface const interface_list[] = { + { NULL } /* list terminator */ +}; + +/*********************************************************************** + * DllRegisterServer (MSI.@) + */ +HRESULT WINAPI DllRegisterServer(void) +{ + HRESULT hr; + + TRACE("\n"); + + hr = register_coclasses(coclass_list); + if (SUCCEEDED(hr)) + hr = register_interfaces(interface_list); + return hr; +} + +/*********************************************************************** + * DllUnregisterServer (MSI.@) + */ +HRESULT WINAPI DllUnregisterServer(void) +{ + HRESULT hr; + + TRACE("\n"); + + hr = unregister_coclasses(coclass_list); + if (SUCCEEDED(hr)) + hr = unregister_interfaces(interface_list); + return hr; +} diff --git a/reactos/dll/msi/select.c b/reactos/dll/msi/select.c new file mode 100644 index 00000000000..9bc2b964d37 --- /dev/null +++ b/reactos/dll/msi/select.c @@ -0,0 +1,288 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002-2004 Mike McCormack 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" +#include "winnls.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msidb); + + +/* below is the query interface to a table */ + +typedef struct tagMSISELECTVIEW +{ + MSIVIEW view; + MSIDATABASE *db; + MSIVIEW *table; + UINT num_cols; + UINT max_cols; + UINT cols[1]; +} MSISELECTVIEW; + +static UINT SELECT_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + + TRACE("%p %d %d %p\n", sv, row, col, val ); + + if( !sv->table ) + return ERROR_FUNCTION_FAILED; + + if( (col==0) || (col>sv->num_cols) ) + return ERROR_FUNCTION_FAILED; + + col = sv->cols[ col - 1 ]; + + return sv->table->ops->fetch_int( sv->table, row, col, val ); +} + +static UINT SELECT_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + + TRACE("%p %d %d %p\n", sv, row, col, stm ); + + if( !sv->table ) + return ERROR_FUNCTION_FAILED; + + if( (col==0) || (col>sv->num_cols) ) + return ERROR_FUNCTION_FAILED; + + col = sv->cols[ col - 1 ]; + + return sv->table->ops->fetch_stream( sv->table, row, col, stm ); +} + +static UINT SELECT_set_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT val ) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + + TRACE("%p %d %d %04x\n", sv, row, col, val ); + + if( !sv->table ) + return ERROR_FUNCTION_FAILED; + + if( (col==0) || (col>sv->num_cols) ) + return ERROR_FUNCTION_FAILED; + + col = sv->cols[ col - 1 ]; + + return sv->table->ops->set_int( sv->table, row, col, val ); +} + +static UINT SELECT_insert_row( struct tagMSIVIEW *view, MSIRECORD *record ) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + + TRACE("%p %p\n", sv, record ); + + if( !sv->table ) + return ERROR_FUNCTION_FAILED; + + return sv->table->ops->insert_row( sv->table, record ); +} + +static UINT SELECT_execute( struct tagMSIVIEW *view, MSIRECORD *record ) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + + TRACE("%p %p\n", sv, record); + + if( !sv->table ) + return ERROR_FUNCTION_FAILED; + + return sv->table->ops->execute( sv->table, record ); +} + +static UINT SELECT_close( struct tagMSIVIEW *view ) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + + TRACE("%p\n", sv ); + + if( !sv->table ) + return ERROR_FUNCTION_FAILED; + + return sv->table->ops->close( sv->table ); +} + +static UINT SELECT_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols ) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + + TRACE("%p %p %p\n", sv, rows, cols ); + + if( !sv->table ) + return ERROR_FUNCTION_FAILED; + + if( cols ) + *cols = sv->num_cols; + + return sv->table->ops->get_dimensions( sv->table, rows, NULL ); +} + +static UINT SELECT_get_column_info( struct tagMSIVIEW *view, + UINT n, LPWSTR *name, UINT *type ) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + + TRACE("%p %d %p %p\n", sv, n, name, type ); + + if( !sv->table ) + return ERROR_FUNCTION_FAILED; + + if( (n==0) || (n>sv->num_cols) ) + return ERROR_FUNCTION_FAILED; + + n = sv->cols[ n - 1 ]; + + return sv->table->ops->get_column_info( sv->table, n, name, type ); +} + +static UINT SELECT_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, + MSIRECORD *rec ) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + + TRACE("%p %d %p\n", sv, eModifyMode, rec ); + + if( !sv->table ) + return ERROR_FUNCTION_FAILED; + + return sv->table->ops->modify( sv->table, eModifyMode, rec ); +} + +static UINT SELECT_delete( struct tagMSIVIEW *view ) +{ + MSISELECTVIEW *sv = (MSISELECTVIEW*)view; + + TRACE("%p\n", sv ); + + if( sv->table ) + sv->table->ops->delete( sv->table ); + sv->table = NULL; + + msi_free( sv ); + + return ERROR_SUCCESS; +} + + +MSIVIEWOPS select_ops = +{ + SELECT_fetch_int, + SELECT_fetch_stream, + SELECT_set_int, + SELECT_insert_row, + SELECT_execute, + SELECT_close, + SELECT_get_dimensions, + SELECT_get_column_info, + SELECT_modify, + SELECT_delete +}; + +static UINT SELECT_AddColumn( MSISELECTVIEW *sv, LPCWSTR name ) +{ + UINT r, n=0; + MSIVIEW *table; + + TRACE("%p adding %s\n", sv, debugstr_w( name ) ); + + if( sv->view.ops != &select_ops ) + return ERROR_FUNCTION_FAILED; + + table = sv->table; + if( !table ) + return ERROR_FUNCTION_FAILED; + if( !table->ops->get_dimensions ) + return ERROR_FUNCTION_FAILED; + if( !table->ops->get_column_info ) + return ERROR_FUNCTION_FAILED; + + if( sv->num_cols >= sv->max_cols ) + return ERROR_FUNCTION_FAILED; + + r = VIEW_find_column( table, name, &n ); + if( r != ERROR_SUCCESS ) + return r; + + sv->cols[sv->num_cols] = n; + TRACE("Translating column %s from %d -> %d\n", + debugstr_w( name ), sv->num_cols, n); + + sv->num_cols++; + + return ERROR_SUCCESS; +} + +UINT SELECT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table, + column_info *columns ) +{ + MSISELECTVIEW *sv = NULL; + UINT count = 0, r; + + TRACE("%p\n", sv ); + + r = table->ops->get_dimensions( table, NULL, &count ); + if( r != ERROR_SUCCESS ) + { + ERR("can't get table dimensions\n"); + return r; + } + + sv = msi_alloc_zero( sizeof *sv + count*sizeof (UINT) ); + if( !sv ) + return ERROR_FUNCTION_FAILED; + + /* fill the structure */ + sv->view.ops = &select_ops; + sv->db = db; + sv->table = table; + sv->num_cols = 0; + sv->max_cols = count; + + while( columns ) + { + r = SELECT_AddColumn( sv, columns->column ); + if( r ) + break; + columns = columns->next; + } + + if( r == ERROR_SUCCESS ) + *view = &sv->view; + else + msi_free( sv ); + + return r; +} diff --git a/reactos/dll/msi/source.c b/reactos/dll/msi/source.c new file mode 100644 index 00000000000..4f67e185f03 --- /dev/null +++ b/reactos/dll/msi/source.c @@ -0,0 +1,560 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2005 Aric Stewart 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#define COBJMACROS +#define NONAMELESSUNION + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winnls.h" +#include "shlwapi.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "msipriv.h" +#include "wincrypt.h" +#include "winver.h" +#include "winuser.h" +#include "wine/unicode.h" +#include "action.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +/* + * These apis are defined in MSI 3.0 + */ + +typedef struct tagMediaInfo +{ + LPWSTR path; + WCHAR szIndex[10]; + WCHAR type; +} media_info; + +static UINT OpenSourceKey(LPCWSTR szProduct, HKEY* key, BOOL user, BOOL create) +{ + HKEY rootkey = 0; + UINT rc; + static const WCHAR szSourceList[] = {'S','o','u','r','c','e','L','i','s','t',0}; + + if (user) + rc = MSIREG_OpenUserProductsKey(szProduct, &rootkey, create); + else + rc = MSIREG_OpenProductsKey(szProduct, &rootkey, create); + + if (rc) + return rc; + + if (create) + rc = RegCreateKeyW(rootkey, szSourceList, key); + else + rc = RegOpenKeyW(rootkey,szSourceList, key); + + return rc; +} + +static UINT OpenMediaSubkey(HKEY rootkey, HKEY *key, BOOL create) +{ + UINT rc; + static const WCHAR media[] = {'M','e','d','i','a',0}; + + if (create) + rc = RegCreateKeyW(rootkey, media, key); + else + rc = RegOpenKeyW(rootkey,media, key); + + return rc; +} + +static UINT OpenNetworkSubkey(HKEY rootkey, HKEY *key, BOOL create) +{ + UINT rc; + static const WCHAR net[] = {'N','e','t',0}; + + if (create) + rc = RegCreateKeyW(rootkey, net, key); + else + rc = RegOpenKeyW(rootkey, net, key); + + return rc; +} + +static UINT OpenURLSubkey(HKEY rootkey, HKEY *key, BOOL create) +{ + UINT rc; + static const WCHAR URL[] = {'U','R','L',0}; + + if (create) + rc = RegCreateKeyW(rootkey, URL, key); + else + rc = RegOpenKeyW(rootkey, URL, key); + + return rc; +} + + +static UINT find_given_source(HKEY key, LPCWSTR szSource, media_info *ss) +{ + DWORD index = 0; + WCHAR szIndex[10]; + DWORD size; + DWORD val_size; + LPWSTR val; + UINT rc = ERROR_SUCCESS; + + while (rc == ERROR_SUCCESS) + { + val = NULL; + val_size = 0; + rc = RegEnumValueW(key, index, szIndex, &size, NULL, NULL, NULL, &val_size); + if (rc != ERROR_NO_MORE_ITEMS) + { + val = msi_alloc(val_size); + RegEnumValueW(key, index, szIndex, &size, NULL, NULL, (LPBYTE)val, + &val_size); + if (lstrcmpiW(szSource,val)==0) + { + ss->path = val; + strcpyW(ss->szIndex,szIndex); + break; + } + else + strcpyW(ss->szIndex,szIndex); + + msi_free(val); + index ++; + } + } + return rc; +} + +/****************************************************************** + * MsiSourceListGetInfoW (MSI.@) + */ +UINT WINAPI MsiSourceListGetInfoW( LPCWSTR szProduct, LPCWSTR szUserSid, + MSIINSTALLCONTEXT dwContext, DWORD dwOptions, + LPCWSTR szProperty, LPWSTR szValue, + LPDWORD pcchValue) +{ + HKEY sourcekey; + UINT rc; + + TRACE("%s %s\n", debugstr_w(szProduct), debugstr_w(szProperty)); + + if (!szProduct || lstrlenW(szProduct) > 39) + return ERROR_INVALID_PARAMETER; + + if (szValue && !pcchValue) + return ERROR_INVALID_PARAMETER; + + if (dwOptions == MSICODE_PATCH) + { + FIXME("Unhandled options MSICODE_PATCH\n"); + return ERROR_FUNCTION_FAILED; + } + + if (szUserSid) + FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid)); + + if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED) + FIXME("Unknown context MSIINSTALLCONTEXT_USERUNMANAGED\n"); + + if (dwContext == MSIINSTALLCONTEXT_MACHINE) + rc = OpenSourceKey(szProduct, &sourcekey, FALSE, FALSE); + else + rc = OpenSourceKey(szProduct, &sourcekey, TRUE, FALSE); + + if (rc != ERROR_SUCCESS) + return ERROR_UNKNOWN_PRODUCT; + + if (strcmpW(szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW) == 0) + { + HKEY key; + rc = OpenMediaSubkey(sourcekey, &key, FALSE); + if (rc == ERROR_SUCCESS) + rc = RegQueryValueExW(key, INSTALLPROPERTY_MEDIAPACKAGEPATHW, + 0, 0, (LPBYTE)szValue, pcchValue); + if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA) + rc = ERROR_UNKNOWN_PROPERTY; + RegCloseKey(key); + } + else if (strcmpW(szProperty, INSTALLPROPERTY_DISKPROMPTW) ==0) + { + HKEY key; + rc = OpenMediaSubkey(sourcekey, &key, FALSE); + if (rc == ERROR_SUCCESS) + rc = RegQueryValueExW(key, INSTALLPROPERTY_DISKPROMPTW, 0, 0, + (LPBYTE)szValue, pcchValue); + if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA) + rc = ERROR_UNKNOWN_PROPERTY; + RegCloseKey(key); + } + else if (strcmpW(szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW)==0) + { + LPWSTR buffer; + DWORD size = 0; + + RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, 0, 0, + NULL, &size); + if (size == 0) + rc = ERROR_UNKNOWN_PROPERTY; + else + { + LPWSTR ptr; + buffer = msi_alloc(size); + rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, + 0, 0, (LPBYTE)buffer,&size); + ptr = strchrW(buffer,';'); + if (ptr) ptr = strchrW(ptr+1,';'); + if (!ptr) + rc = ERROR_UNKNOWN_PROPERTY; + else + { + ptr ++; + lstrcpynW(szValue, ptr, *pcchValue); + if (lstrlenW(ptr) > *pcchValue) + { + *pcchValue = lstrlenW(ptr)+1; + rc = ERROR_MORE_DATA; + } + else + rc = ERROR_SUCCESS; + } + msi_free(buffer); + } + } + else if (strcmpW(INSTALLPROPERTY_LASTUSEDTYPEW, szProperty)==0) + { + LPWSTR buffer; + DWORD size = 0; + + RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, 0, 0, + NULL, &size); + if (size == 0) + rc = ERROR_UNKNOWN_PROPERTY; + else + { + buffer = msi_alloc(size); + rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, + 0, 0, (LPBYTE)buffer,&size); + if (*pcchValue < 1) + { + rc = ERROR_MORE_DATA; + *pcchValue = 1; + } + else + { + szValue[0] = buffer[0]; + rc = ERROR_SUCCESS; + } + msi_free(buffer); + } + } + else if (strcmpW(INSTALLPROPERTY_PACKAGENAMEW, szProperty)==0) + { + rc = RegQueryValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0, 0, + (LPBYTE)szValue, pcchValue); + if (rc != ERROR_SUCCESS && rc != ERROR_MORE_DATA) + rc = ERROR_UNKNOWN_PROPERTY; + } + else + { + FIXME("Unknown property %s\n",debugstr_w(szProperty)); + rc = ERROR_UNKNOWN_PROPERTY; + } + + RegCloseKey(sourcekey); + return rc; +} + +/****************************************************************** + * MsiSourceListSetInfoW (MSI.@) + */ +UINT WINAPI MsiSourceListSetInfoW( LPCWSTR szProduct, LPCWSTR szUserSid, + MSIINSTALLCONTEXT dwContext, DWORD dwOptions, + LPCWSTR szProperty, LPCWSTR szValue) +{ + HKEY sourcekey; + UINT rc; + + TRACE("%s %s %x %lx %s %s\n", debugstr_w(szProduct), debugstr_w(szUserSid), + dwContext, dwOptions, debugstr_w(szProperty), debugstr_w(szValue)); + + if (!szProduct || lstrlenW(szProduct) > 39) + return ERROR_INVALID_PARAMETER; + + if (dwOptions & MSICODE_PATCH) + { + FIXME("Unhandled options MSICODE_PATCH\n"); + return ERROR_FUNCTION_FAILED; + } + + if (szUserSid) + FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid)); + + if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED) + FIXME("Unknown context MSIINSTALLCONTEXT_USERUNMANAGED\n"); + + if (dwContext == MSIINSTALLCONTEXT_MACHINE) + rc = OpenSourceKey(szProduct, &sourcekey, FALSE, TRUE); + else + rc = OpenSourceKey(szProduct, &sourcekey, TRUE, TRUE); + + if (rc != ERROR_SUCCESS) + return ERROR_UNKNOWN_PRODUCT; + + + if (strcmpW(szProperty, INSTALLPROPERTY_MEDIAPACKAGEPATHW) == 0) + { + HKEY key; + DWORD size = lstrlenW(szValue)*sizeof(WCHAR); + rc = OpenMediaSubkey(sourcekey, &key, FALSE); + if (rc == ERROR_SUCCESS) + rc = RegSetValueExW(key, INSTALLPROPERTY_MEDIAPACKAGEPATHW, 0, + REG_SZ, (LPBYTE)szValue, size); + if (rc != ERROR_SUCCESS) + rc = ERROR_UNKNOWN_PROPERTY; + RegCloseKey(key); + } + else if (strcmpW(szProperty, INSTALLPROPERTY_DISKPROMPTW) == 0) + { + HKEY key; + DWORD size = lstrlenW(szValue)*sizeof(WCHAR); + rc = OpenMediaSubkey(sourcekey, &key, FALSE); + if (rc == ERROR_SUCCESS) + rc = RegSetValueExW(key, INSTALLPROPERTY_DISKPROMPTW, 0, + REG_SZ, (LPBYTE)szValue, size); + if (rc != ERROR_SUCCESS) + rc = ERROR_UNKNOWN_PROPERTY; + RegCloseKey(key); + } + else if (strcmpW(szProperty, INSTALLPROPERTY_LASTUSEDSOURCEW)==0) + { + LPWSTR buffer = NULL; + DWORD size; + WCHAR typechar = 'n'; + static const WCHAR LastUsedSource_Fmt[] = {'%','c',';','%','i',';','%','s',0}; + + /* make sure the source is registered */ + MsiSourceListAddSourceExW(szProduct, szUserSid, dwContext, + dwOptions, szValue, 0); + + if (dwOptions & MSISOURCETYPE_NETWORK) + typechar = 'n'; + else if (dwOptions & MSISOURCETYPE_URL) + typechar = 'u'; + else if (dwOptions & MSISOURCETYPE_MEDIA) + typechar = 'm'; + else + ERR("Unknown source type! 0x%lx\n",dwOptions); + + size = (lstrlenW(szValue)+5)*sizeof(WCHAR); + buffer = msi_alloc(size); + sprintfW(buffer, LastUsedSource_Fmt, typechar, 1, szValue); + rc = RegSetValueExW(sourcekey, INSTALLPROPERTY_LASTUSEDSOURCEW, 0, + REG_EXPAND_SZ, (LPBYTE)buffer, size); + if (rc != ERROR_SUCCESS) + rc = ERROR_UNKNOWN_PROPERTY; + msi_free( buffer ); + } + else if (strcmpW(INSTALLPROPERTY_PACKAGENAMEW, szProperty)==0) + { + DWORD size = lstrlenW(szValue)*sizeof(WCHAR); + rc = RegSetValueExW(sourcekey, INSTALLPROPERTY_PACKAGENAMEW, 0, + REG_SZ, (LPBYTE)szValue, size); + if (rc != ERROR_SUCCESS) + rc = ERROR_UNKNOWN_PROPERTY; + } + else + { + FIXME("Unknown property %s\n",debugstr_w(szProperty)); + rc = ERROR_UNKNOWN_PROPERTY; + } + + RegCloseKey(sourcekey); + return rc; + +} + +/****************************************************************** + * MsiSourceListAddSourceExW (MSI.@) + */ +UINT WINAPI MsiSourceListAddSourceExW( LPCWSTR szProduct, LPCWSTR szUserSid, + MSIINSTALLCONTEXT dwContext, DWORD dwOptions, LPCWSTR szSource, + DWORD dwIndex) +{ + HKEY sourcekey; + HKEY typekey; + UINT rc; + media_info source_struct; + + TRACE("%s, %s, %x, %lx, %s, %li\n", debugstr_w(szProduct), + debugstr_w(szUserSid), dwContext, dwOptions, debugstr_w(szSource), + dwIndex); + + if (!szProduct) + return ERROR_INVALID_PARAMETER; + + if (!szSource) + return ERROR_INVALID_PARAMETER; + + if (dwOptions & MSICODE_PATCH) + { + FIXME("Unhandled options MSICODE_PATCH\n"); + return ERROR_FUNCTION_FAILED; + } + + if (szUserSid) + FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid)); + + if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED) + FIXME("Unknown context MSIINSTALLCONTEXT_USERUNMANAGED\n"); + + if (dwContext == MSIINSTALLCONTEXT_MACHINE) + rc = OpenSourceKey(szProduct, &sourcekey, FALSE, TRUE); + else + rc = OpenSourceKey(szProduct, &sourcekey, TRUE, TRUE); + + if (rc != ERROR_SUCCESS) + return ERROR_UNKNOWN_PRODUCT; + + if (dwOptions & MSISOURCETYPE_NETWORK) + rc = OpenNetworkSubkey(sourcekey, &typekey, TRUE); + else if (dwOptions & MSISOURCETYPE_URL) + rc = OpenURLSubkey(sourcekey, &typekey, TRUE); + else + { + ERR("unknown media type: %08lx\n", dwOptions); + RegCloseKey(sourcekey); + return ERROR_FUNCTION_FAILED; + } + + source_struct.szIndex[0] = 0; + if (find_given_source(typekey, szSource, &source_struct)==ERROR_SUCCESS) + { + DWORD current_index = atoiW(source_struct.szIndex); + /* found the source */ + if (dwIndex > 0 && current_index != dwIndex) + FIXME("Need to reorder the sources!\n"); + } + else + { + DWORD current_index = 0; + static const WCHAR fmt[] = {'%','i',0}; + DWORD size = lstrlenW(szSource)*sizeof(WCHAR); + + if (source_struct.szIndex[0]) + current_index = atoiW(source_struct.szIndex); + /* new source */ + if (dwIndex > 0 && dwIndex < current_index) + FIXME("Need to reorder the sources!\n"); + + current_index ++; + sprintfW(source_struct.szIndex,fmt,current_index); + rc = RegSetValueExW(typekey, source_struct.szIndex, 0, REG_EXPAND_SZ, + (LPBYTE)szSource, size); + } + + RegCloseKey(typekey); + RegCloseKey(sourcekey); + return rc; +} + +/****************************************************************** + * MsiSourceListAddMediaDisk(MSI.@) + */ +UINT WINAPI MsiSourceListAddMediaDiskW(LPCWSTR szProduct, LPCWSTR szUserSid, + MSIINSTALLCONTEXT dwContext, DWORD dwOptions, DWORD dwDiskId, + LPCWSTR szVolumeLabel, LPCWSTR szDiskPrompt) +{ + HKEY sourcekey; + HKEY mediakey; + UINT rc; + WCHAR szIndex[10]; + static const WCHAR fmt[] = {'%','i',0}; + static const WCHAR disk_fmt[] = {'%','s',';','%','s',0}; + static const WCHAR empty[1] = {0}; + LPCWSTR pt1,pt2; + LPWSTR buffer; + DWORD size; + + TRACE("%s %s %x %lx %li %s %s\n", debugstr_w(szProduct), + debugstr_w(szUserSid), dwContext, dwOptions, dwDiskId, + debugstr_w(szVolumeLabel), debugstr_w(szDiskPrompt)); + + if (!szProduct || lstrlenW(szProduct) > 39) + return ERROR_INVALID_PARAMETER; + + if (dwOptions & MSICODE_PATCH) + { + FIXME("Unhandled options MSICODE_PATCH\n"); + return ERROR_FUNCTION_FAILED; + } + + if (szUserSid) + FIXME("Unhandled UserSid %s\n",debugstr_w(szUserSid)); + + if (dwContext == MSIINSTALLCONTEXT_USERUNMANAGED) + FIXME("Unknown context MSIINSTALLCONTEXT_USERUNMANAGED\n"); + + if (dwContext == MSIINSTALLCONTEXT_MACHINE) + rc = OpenSourceKey(szProduct, &sourcekey, FALSE, TRUE); + else + rc = OpenSourceKey(szProduct, &sourcekey, TRUE, TRUE); + + if (rc != ERROR_SUCCESS) + return ERROR_UNKNOWN_PRODUCT; + + OpenMediaSubkey(sourcekey,&mediakey,TRUE); + + sprintfW(szIndex,fmt,dwDiskId); + + size = 2; + if (szVolumeLabel) + { + size +=lstrlenW(szVolumeLabel); + pt1 = szVolumeLabel; + } + else + pt1 = empty; + if (szDiskPrompt) + { + size +=lstrlenW(szDiskPrompt); + pt2 = szDiskPrompt; + } + else + pt2 = empty; + + size *=sizeof(WCHAR); + + buffer = msi_alloc(size); + sprintfW(buffer,disk_fmt,pt1,pt2); + + RegSetValueExW(mediakey, szIndex, 0, REG_SZ, (LPBYTE)buffer, size); + msi_free( buffer ); + + RegCloseKey(sourcekey); + RegCloseKey(mediakey); + + return ERROR_SUCCESS; +} diff --git a/reactos/dll/msi/sql.tab.c b/reactos/dll/msi/sql.tab.c new file mode 100644 index 00000000000..f4643950f11 --- /dev/null +++ b/reactos/dll/msi/sql.tab.c @@ -0,0 +1,2484 @@ +/* A Bison parser, made by GNU Bison 1.875c. */ + +/* Skeleton parser for Yacc-like parsing with Bison, + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* As a special exception, when this file is copied by Bison into a + Bison output file, you may use that output file without restriction. + This special exception was added by the Free Software Foundation + in version 1.24 of Bison. */ + +/* Written by Richard Stallman by simplifying the original so called + ``semantic'' parser. */ + +/* All symbols defined below should begin with yy or YY, to avoid + infringing on user name space. This should be done even for local + variables, as they might otherwise be expanded by user macros. + There are some unavoidable exceptions within include files to + define necessary library symbols; they are noted "INFRINGES ON + USER NAME SPACE" below. */ + +/* Identify Bison output. */ +#define YYBISON 1 + +/* Skeleton name. */ +#define YYSKELETON_NAME "yacc.c" + +/* Pure parsers. */ +#define YYPURE 1 + +/* Using locations. */ +#define YYLSP_NEEDED 0 + +/* If NAME_PREFIX is specified substitute the variables and functions + names. */ +#define yyparse SQL_parse +#define yylex SQL_lex +#define yyerror SQL_error +#define yylval SQL_lval +#define yychar SQL_char +#define yydebug SQL_debug +#define yynerrs SQL_nerrs + + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + TK_ABORT = 258, + TK_AFTER = 259, + TK_AGG_FUNCTION = 260, + TK_ALL = 261, + TK_AND = 262, + TK_AS = 263, + TK_ASC = 264, + TK_BEFORE = 265, + TK_BEGIN = 266, + TK_BETWEEN = 267, + TK_BITAND = 268, + TK_BITNOT = 269, + TK_BITOR = 270, + TK_BY = 271, + TK_CASCADE = 272, + TK_CASE = 273, + TK_CHAR = 274, + TK_CHECK = 275, + TK_CLUSTER = 276, + TK_COLLATE = 277, + TK_COLUMN = 278, + TK_COMMA = 279, + TK_COMMENT = 280, + TK_COMMIT = 281, + TK_CONCAT = 282, + TK_CONFLICT = 283, + TK_CONSTRAINT = 284, + TK_COPY = 285, + TK_CREATE = 286, + TK_DEFAULT = 287, + TK_DEFERRABLE = 288, + TK_DEFERRED = 289, + TK_DELETE = 290, + TK_DELIMITERS = 291, + TK_DESC = 292, + TK_DISTINCT = 293, + TK_DOT = 294, + TK_DROP = 295, + TK_EACH = 296, + TK_ELSE = 297, + TK_END = 298, + TK_END_OF_FILE = 299, + TK_EQ = 300, + TK_EXCEPT = 301, + TK_EXPLAIN = 302, + TK_FAIL = 303, + TK_FLOAT = 304, + TK_FOR = 305, + TK_FOREIGN = 306, + TK_FROM = 307, + TK_FUNCTION = 308, + TK_GE = 309, + TK_GLOB = 310, + TK_GROUP = 311, + TK_GT = 312, + TK_HAVING = 313, + TK_HOLD = 314, + TK_IGNORE = 315, + TK_ILLEGAL = 316, + TK_IMMEDIATE = 317, + TK_IN = 318, + TK_INDEX = 319, + TK_INITIALLY = 320, + TK_ID = 321, + TK_INSERT = 322, + TK_INSTEAD = 323, + TK_INT = 324, + TK_INTEGER = 325, + TK_INTERSECT = 326, + TK_INTO = 327, + TK_IS = 328, + TK_ISNULL = 329, + TK_JOIN = 330, + TK_JOIN_KW = 331, + TK_KEY = 332, + TK_LE = 333, + TK_LIKE = 334, + TK_LIMIT = 335, + TK_LONG = 336, + TK_LONGCHAR = 337, + TK_LP = 338, + TK_LSHIFT = 339, + TK_LT = 340, + TK_LOCALIZABLE = 341, + TK_MATCH = 342, + TK_MINUS = 343, + TK_NE = 344, + TK_NOT = 345, + TK_NOTNULL = 346, + TK_NULL = 347, + TK_OBJECT = 348, + TK_OF = 349, + TK_OFFSET = 350, + TK_ON = 351, + TK_OR = 352, + TK_ORACLE_OUTER_JOIN = 353, + TK_ORDER = 354, + TK_PLUS = 355, + TK_PRAGMA = 356, + TK_PRIMARY = 357, + TK_RAISE = 358, + TK_REFERENCES = 359, + TK_REM = 360, + TK_REPLACE = 361, + TK_RESTRICT = 362, + TK_ROLLBACK = 363, + TK_ROW = 364, + TK_RP = 365, + TK_RSHIFT = 366, + TK_SELECT = 367, + TK_SEMI = 368, + TK_SET = 369, + TK_SHORT = 370, + TK_SLASH = 371, + TK_SPACE = 372, + TK_STAR = 373, + TK_STATEMENT = 374, + TK_STRING = 375, + TK_TABLE = 376, + TK_TEMP = 377, + TK_THEN = 378, + TK_TRANSACTION = 379, + TK_TRIGGER = 380, + TK_UMINUS = 381, + TK_UNCLOSED_STRING = 382, + TK_UNION = 383, + TK_UNIQUE = 384, + TK_UPDATE = 385, + TK_UPLUS = 386, + TK_USING = 387, + TK_VACUUM = 388, + TK_VALUES = 389, + TK_VIEW = 390, + TK_WHEN = 391, + TK_WHERE = 392, + TK_WILDCARD = 393, + COLUMN = 395, + FUNCTION = 396, + COMMENT = 397, + UNCLOSED_STRING = 398, + SPACE = 399, + ILLEGAL = 400, + END_OF_FILE = 401 + }; +#endif +#define TK_ABORT 258 +#define TK_AFTER 259 +#define TK_AGG_FUNCTION 260 +#define TK_ALL 261 +#define TK_AND 262 +#define TK_AS 263 +#define TK_ASC 264 +#define TK_BEFORE 265 +#define TK_BEGIN 266 +#define TK_BETWEEN 267 +#define TK_BITAND 268 +#define TK_BITNOT 269 +#define TK_BITOR 270 +#define TK_BY 271 +#define TK_CASCADE 272 +#define TK_CASE 273 +#define TK_CHAR 274 +#define TK_CHECK 275 +#define TK_CLUSTER 276 +#define TK_COLLATE 277 +#define TK_COLUMN 278 +#define TK_COMMA 279 +#define TK_COMMENT 280 +#define TK_COMMIT 281 +#define TK_CONCAT 282 +#define TK_CONFLICT 283 +#define TK_CONSTRAINT 284 +#define TK_COPY 285 +#define TK_CREATE 286 +#define TK_DEFAULT 287 +#define TK_DEFERRABLE 288 +#define TK_DEFERRED 289 +#define TK_DELETE 290 +#define TK_DELIMITERS 291 +#define TK_DESC 292 +#define TK_DISTINCT 293 +#define TK_DOT 294 +#define TK_DROP 295 +#define TK_EACH 296 +#define TK_ELSE 297 +#define TK_END 298 +#define TK_END_OF_FILE 299 +#define TK_EQ 300 +#define TK_EXCEPT 301 +#define TK_EXPLAIN 302 +#define TK_FAIL 303 +#define TK_FLOAT 304 +#define TK_FOR 305 +#define TK_FOREIGN 306 +#define TK_FROM 307 +#define TK_FUNCTION 308 +#define TK_GE 309 +#define TK_GLOB 310 +#define TK_GROUP 311 +#define TK_GT 312 +#define TK_HAVING 313 +#define TK_HOLD 314 +#define TK_IGNORE 315 +#define TK_ILLEGAL 316 +#define TK_IMMEDIATE 317 +#define TK_IN 318 +#define TK_INDEX 319 +#define TK_INITIALLY 320 +#define TK_ID 321 +#define TK_INSERT 322 +#define TK_INSTEAD 323 +#define TK_INT 324 +#define TK_INTEGER 325 +#define TK_INTERSECT 326 +#define TK_INTO 327 +#define TK_IS 328 +#define TK_ISNULL 329 +#define TK_JOIN 330 +#define TK_JOIN_KW 331 +#define TK_KEY 332 +#define TK_LE 333 +#define TK_LIKE 334 +#define TK_LIMIT 335 +#define TK_LONG 336 +#define TK_LONGCHAR 337 +#define TK_LP 338 +#define TK_LSHIFT 339 +#define TK_LT 340 +#define TK_LOCALIZABLE 341 +#define TK_MATCH 342 +#define TK_MINUS 343 +#define TK_NE 344 +#define TK_NOT 345 +#define TK_NOTNULL 346 +#define TK_NULL 347 +#define TK_OBJECT 348 +#define TK_OF 349 +#define TK_OFFSET 350 +#define TK_ON 351 +#define TK_OR 352 +#define TK_ORACLE_OUTER_JOIN 353 +#define TK_ORDER 354 +#define TK_PLUS 355 +#define TK_PRAGMA 356 +#define TK_PRIMARY 357 +#define TK_RAISE 358 +#define TK_REFERENCES 359 +#define TK_REM 360 +#define TK_REPLACE 361 +#define TK_RESTRICT 362 +#define TK_ROLLBACK 363 +#define TK_ROW 364 +#define TK_RP 365 +#define TK_RSHIFT 366 +#define TK_SELECT 367 +#define TK_SEMI 368 +#define TK_SET 369 +#define TK_SHORT 370 +#define TK_SLASH 371 +#define TK_SPACE 372 +#define TK_STAR 373 +#define TK_STATEMENT 374 +#define TK_STRING 375 +#define TK_TABLE 376 +#define TK_TEMP 377 +#define TK_THEN 378 +#define TK_TRANSACTION 379 +#define TK_TRIGGER 380 +#define TK_UMINUS 381 +#define TK_UNCLOSED_STRING 382 +#define TK_UNION 383 +#define TK_UNIQUE 384 +#define TK_UPDATE 385 +#define TK_UPLUS 386 +#define TK_USING 387 +#define TK_VACUUM 388 +#define TK_VALUES 389 +#define TK_VIEW 390 +#define TK_WHEN 391 +#define TK_WHERE 392 +#define TK_WILDCARD 393 +#define COLUMN 395 +#define FUNCTION 396 +#define COMMENT 397 +#define UNCLOSED_STRING 398 +#define SPACE 399 +#define ILLEGAL 400 +#define END_OF_FILE 401 + + + + +/* Copy the first part of user declarations. */ +#line 1 "./sql.y" + + +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002-2004 Mike McCormack 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "config.h" + +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "query.h" +#include "wine/list.h" +#include "wine/debug.h" + +#define YYLEX_PARAM info +#define YYPARSE_PARAM info + +extern int SQL_error(const char *str); + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +typedef struct tag_SQL_input +{ + MSIDATABASE *db; + LPCWSTR command; + DWORD n, len; + MSIVIEW **view; /* view structure for the resulting query */ + struct list *mem; +} SQL_input; + +static LPWSTR SQL_getstring( void *info, struct sql_str *str ); +static INT SQL_getint( void *info ); +static int SQL_lex( void *SQL_lval, SQL_input *info ); + +static void *parser_alloc( void *info, unsigned int sz ); +static column_info *parser_alloc_column( void *info, LPCWSTR table, LPCWSTR column ); + +static BOOL SQL_MarkPrimaryKeys( column_info *cols, column_info *keys); + +static struct expr * EXPR_complex( void *info, struct expr *l, UINT op, struct expr *r ); +static struct expr * EXPR_column( void *info, column_info *column ); +static struct expr * EXPR_ival( void *info, int val ); +static struct expr * EXPR_sval( void *info, struct sql_str * ); +static struct expr * EXPR_wildcard( void *info ); + + + +/* Enabling traces. */ +#ifndef YYDEBUG +# define YYDEBUG 0 +#endif + +/* Enabling verbose error messages. */ +#ifdef YYERROR_VERBOSE +# undef YYERROR_VERBOSE +# define YYERROR_VERBOSE 1 +#else +# define YYERROR_VERBOSE 0 +#endif + +#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED) +#line 72 "./sql.y" +typedef union YYSTYPE { + struct sql_str str; + LPWSTR string; + column_info *column_list; + MSIVIEW *query; + struct expr *expr; + USHORT column_type; + int integer; +} YYSTYPE; +/* Line 191 of yacc.c. */ +#line 453 "sql.tab.c" +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + +/* Copy the second part of user declarations. */ + + +/* Line 214 of yacc.c. */ +#line 465 "sql.tab.c" + +#if ! defined (yyoverflow) || YYERROR_VERBOSE + +# ifndef YYFREE +# define YYFREE free +# endif +# ifndef YYMALLOC +# define YYMALLOC malloc +# endif + +/* The parser invokes alloca or malloc; define the necessary symbols. */ + +# ifdef YYSTACK_USE_ALLOCA +# if YYSTACK_USE_ALLOCA +# define YYSTACK_ALLOC alloca +# endif +# else +# if defined (alloca) || defined (_ALLOCA_H) +# define YYSTACK_ALLOC alloca +# else +# ifdef __GNUC__ +# define YYSTACK_ALLOC __builtin_alloca +# endif +# endif +# endif + +# ifdef YYSTACK_ALLOC + /* Pacify GCC's `empty if-body' warning. */ +# define YYSTACK_FREE(Ptr) do { /* empty */; } while (0) +# else +# if defined (__STDC__) || defined (__cplusplus) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# endif +# define YYSTACK_ALLOC YYMALLOC +# define YYSTACK_FREE YYFREE +# endif +#endif /* ! defined (yyoverflow) || YYERROR_VERBOSE */ + + +#if (! defined (yyoverflow) \ + && (! defined (__cplusplus) \ + || (defined (YYSTYPE_IS_TRIVIAL) && YYSTYPE_IS_TRIVIAL))) + +/* A type that is properly aligned for any stack member. */ +union yyalloc +{ + short yyss; + YYSTYPE yyvs; + }; + +/* The size of the maximum gap between one aligned stack and the next. */ +# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1) + +/* The size of an array large to enough to hold all stacks, each with + N elements. */ +# define YYSTACK_BYTES(N) \ + ((N) * (sizeof (short) + sizeof (YYSTYPE)) \ + + YYSTACK_GAP_MAXIMUM) + +/* Copy COUNT objects from FROM to TO. The source and destination do + not overlap. */ +# ifndef YYCOPY +# if defined (__GNUC__) && 1 < __GNUC__ +# define YYCOPY(To, From, Count) \ + __builtin_memcpy (To, From, (Count) * sizeof (*(From))) +# else +# define YYCOPY(To, From, Count) \ + do \ + { \ + register YYSIZE_T yyi; \ + for (yyi = 0; yyi < (Count); yyi++) \ + (To)[yyi] = (From)[yyi]; \ + } \ + while (0) +# endif +# endif + +/* Relocate STACK from its old location to the new one. The + local variables YYSIZE and YYSTACKSIZE give the old and new number of + elements in the stack, and YYPTR gives the new location of the + stack. Advance YYPTR to a properly aligned location for the next + stack. */ +# define YYSTACK_RELOCATE(Stack) \ + do \ + { \ + YYSIZE_T yynewbytes; \ + YYCOPY (&yyptr->Stack, Stack, yysize); \ + Stack = &yyptr->Stack; \ + yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \ + yyptr += yynewbytes / sizeof (*yyptr); \ + } \ + while (0) + +#endif + +#if defined (__STDC__) || defined (__cplusplus) + typedef signed char yysigned_char; +#else + typedef short yysigned_char; +#endif + +/* YYFINAL -- State number of the termination state. */ +#define YYFINAL 29 +/* YYLAST -- Last index in YYTABLE. */ +#define YYLAST 124 + +/* YYNTOKENS -- Number of terminals. */ +#define YYNTOKENS 147 +/* YYNNTS -- Number of nonterminals. */ +#define YYNNTS 31 +/* YYNRULES -- Number of rules. */ +#define YYNRULES 69 +/* YYNRULES -- Number of states. */ +#define YYNSTATES 127 + +/* YYTRANSLATE(YYLEX) -- Bison symbol number corresponding to YYLEX. */ +#define YYUNDEFTOK 2 +#define YYMAXUTOK 401 + +#define YYTRANSLATE(YYX) \ + ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK) + +/* YYTRANSLATE[YYLEX] -- Bison symbol number corresponding to YYLEX. */ +static const unsigned char yytranslate[] = +{ + 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 1, 2, 3, 4, + 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, + 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, + 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, + 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, + 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, + 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, + 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, + 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, + 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, + 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, + 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, + 145, 146 +}; + +#if YYDEBUG +/* YYPRHS[YYN] -- Index of the first RHS symbol of rule number YYN in + YYRHS. */ +static const unsigned char yyprhs[] = +{ + 0, 0, 3, 5, 7, 9, 11, 13, 15, 26, + 38, 45, 53, 60, 63, 68, 72, 74, 77, 79, + 82, 84, 88, 90, 95, 97, 99, 101, 103, 105, + 107, 112, 114, 117, 121, 124, 126, 130, 132, 134, + 138, 141, 145, 149, 153, 157, 161, 165, 169, 173, + 177, 181, 185, 190, 192, 194, 196, 200, 202, 206, + 210, 212, 215, 217, 219, 221, 225, 227, 229, 231 +}; + +/* YYRHS -- A `-1'-separated list of the rules' RHS. */ +static const short yyrhs[] = +{ + 148, 0, -1, 149, -1, 161, -1, 151, -1, 150, + -1, 152, -1, 153, -1, 67, 72, 175, 83, 164, + 110, 134, 83, 169, 110, -1, 67, 72, 175, 83, + 164, 110, 134, 83, 169, 110, 122, -1, 31, 121, + 175, 83, 154, 110, -1, 31, 121, 175, 83, 154, + 110, 59, -1, 130, 175, 114, 170, 137, 167, -1, + 35, 165, -1, 155, 102, 77, 164, -1, 155, 24, + 156, -1, 156, -1, 174, 157, -1, 158, -1, 158, + 86, -1, 159, -1, 159, 90, 92, -1, 19, -1, + 19, 83, 160, 110, -1, 82, -1, 115, -1, 69, + -1, 81, -1, 93, -1, 177, -1, 162, 99, 16, + 164, -1, 162, -1, 112, 163, -1, 112, 38, 163, + -1, 164, 165, -1, 174, -1, 174, 24, 164, -1, + 118, -1, 166, -1, 166, 137, 167, -1, 52, 175, + -1, 83, 167, 110, -1, 173, 45, 173, -1, 167, + 7, 167, -1, 167, 97, 167, -1, 173, 45, 168, + -1, 173, 57, 168, -1, 173, 85, 168, -1, 173, + 78, 168, -1, 173, 54, 168, -1, 173, 89, 168, + -1, 173, 73, 92, -1, 173, 73, 90, 92, -1, + 173, -1, 172, -1, 172, -1, 172, 24, 169, -1, + 171, -1, 171, 24, 170, -1, 174, 45, 172, -1, + 177, -1, 88, 177, -1, 120, -1, 138, -1, 174, + -1, 175, 39, 176, -1, 176, -1, 176, -1, 66, + -1, 70, -1 +}; + +/* YYRLINE[YYN] -- source line where rule number YYN was defined. */ +static const unsigned short yyrline[] = +{ + 0, 138, 138, 146, 147, 148, 149, 150, 154, 165, + 178, 190, 205, 218, 231, 241, 251, 258, 266, 270, + 277, 281, 288, 292, 296, 300, 304, 308, 312, 319, + 328, 340, 344, 348, 364, 385, 386, 390, 397, 398, + 414, 427, 433, 439, 445, 451, 457, 463, 469, 475, + 481, 487, 493, 502, 503, 507, 514, 525, 526, 534, + 542, 548, 554, 560, 569, 578, 584, 593, 600, 609 +}; +#endif + +#if YYDEBUG || YYERROR_VERBOSE +/* YYTNME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM. + First, the terminals, then, starting at YYNTOKENS, nonterminals. */ +static const char *const yytname[] = +{ + "$end", "error", "$undefined", "TK_ABORT", "TK_AFTER", + "TK_AGG_FUNCTION", "TK_ALL", "TK_AND", "TK_AS", "TK_ASC", "TK_BEFORE", + "TK_BEGIN", "TK_BETWEEN", "TK_BITAND", "TK_BITNOT", "TK_BITOR", "TK_BY", + "TK_CASCADE", "TK_CASE", "TK_CHAR", "TK_CHECK", "TK_CLUSTER", + "TK_COLLATE", "TK_COLUMN", "TK_COMMA", "TK_COMMENT", "TK_COMMIT", + "TK_CONCAT", "TK_CONFLICT", "TK_CONSTRAINT", "TK_COPY", "TK_CREATE", + "TK_DEFAULT", "TK_DEFERRABLE", "TK_DEFERRED", "TK_DELETE", + "TK_DELIMITERS", "TK_DESC", "TK_DISTINCT", "TK_DOT", "TK_DROP", + "TK_EACH", "TK_ELSE", "TK_END", "TK_END_OF_FILE", "TK_EQ", "TK_EXCEPT", + "TK_EXPLAIN", "TK_FAIL", "TK_FLOAT", "TK_FOR", "TK_FOREIGN", "TK_FROM", + "TK_FUNCTION", "TK_GE", "TK_GLOB", "TK_GROUP", "TK_GT", "TK_HAVING", + "TK_HOLD", "TK_IGNORE", "TK_ILLEGAL", "TK_IMMEDIATE", "TK_IN", + "TK_INDEX", "TK_INITIALLY", "TK_ID", "TK_INSERT", "TK_INSTEAD", "TK_INT", + "TK_INTEGER", "TK_INTERSECT", "TK_INTO", "TK_IS", "TK_ISNULL", "TK_JOIN", + "TK_JOIN_KW", "TK_KEY", "TK_LE", "TK_LIKE", "TK_LIMIT", "TK_LONG", + "TK_LONGCHAR", "TK_LP", "TK_LSHIFT", "TK_LT", "TK_LOCALIZABLE", + "TK_MATCH", "TK_MINUS", "TK_NE", "TK_NOT", "TK_NOTNULL", "TK_NULL", + "TK_OBJECT", "TK_OF", "TK_OFFSET", "TK_ON", "TK_OR", + "TK_ORACLE_OUTER_JOIN", "TK_ORDER", "TK_PLUS", "TK_PRAGMA", "TK_PRIMARY", + "TK_RAISE", "TK_REFERENCES", "TK_REM", "TK_REPLACE", "TK_RESTRICT", + "TK_ROLLBACK", "TK_ROW", "TK_RP", "TK_RSHIFT", "TK_SELECT", "TK_SEMI", + "TK_SET", "TK_SHORT", "TK_SLASH", "TK_SPACE", "TK_STAR", "TK_STATEMENT", + "TK_STRING", "TK_TABLE", "TK_TEMP", "TK_THEN", "TK_TRANSACTION", + "TK_TRIGGER", "TK_UMINUS", "TK_UNCLOSED_STRING", "TK_UNION", "TK_UNIQUE", + "TK_UPDATE", "TK_UPLUS", "TK_USING", "TK_VACUUM", "TK_VALUES", "TK_VIEW", + "TK_WHEN", "TK_WHERE", "TK_WILDCARD", "AGG_FUNCTION.", "COLUMN", + "FUNCTION", "COMMENT", "UNCLOSED_STRING", "SPACE", "ILLEGAL", + "END_OF_FILE", "$accept", "query", "onequery", "oneinsert", "onecreate", + "oneupdate", "onedelete", "table_def", "column_def", "column_and_type", + "column_type", "data_type_l", "data_type", "data_count", "oneselect", + "unorderedsel", "selectfrom", "selcollist", "from", "fromtable", "expr", + "val", "constlist", "update_assign_list", "column_assignment", + "const_val", "column_val", "column", "table", "id", "number", 0 +}; +#endif + +# ifdef YYPRINT +/* YYTOKNUM[YYLEX-NUM] -- Internal token number corresponding to + token YYLEX-NUM. */ +static const unsigned short yytoknum[] = +{ + 0, 256, 257, 258, 259, 260, 261, 262, 263, 264, + 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, + 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, + 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, + 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, + 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, + 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, + 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, + 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, + 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, + 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, + 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, + 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, + 395, 396, 397, 398, 399, 400, 401 +}; +# endif + +/* YYR1[YYN] -- Symbol number of symbol that rule YYN derives. */ +static const unsigned char yyr1[] = +{ + 0, 147, 148, 149, 149, 149, 149, 149, 150, 150, + 151, 151, 152, 153, 154, 155, 155, 156, 157, 157, + 158, 158, 159, 159, 159, 159, 159, 159, 159, 160, + 161, 161, 162, 162, 163, 164, 164, 164, 165, 165, + 166, 167, 167, 167, 167, 167, 167, 167, 167, 167, + 167, 167, 167, 168, 168, 169, 169, 170, 170, 171, + 172, 172, 172, 172, 173, 174, 174, 175, 176, 177 +}; + +/* YYR2[YYN] -- Number of symbols composing right hand side of rule YYN. */ +static const unsigned char yyr2[] = +{ + 0, 2, 1, 1, 1, 1, 1, 1, 10, 11, + 6, 7, 6, 2, 4, 3, 1, 2, 1, 2, + 1, 3, 1, 4, 1, 1, 1, 1, 1, 1, + 4, 1, 2, 3, 2, 1, 3, 1, 1, 3, + 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, + 3, 3, 4, 1, 1, 1, 3, 1, 3, 3, + 1, 2, 1, 1, 1, 3, 1, 1, 1, 1 +}; + +/* YYDEFACT[STATE-NAME] -- Default rule to reduce with in state + STATE-NUM when YYTABLE doesn't specify something else to do. Zero + means the default is an error. */ +static const unsigned char yydefact[] = +{ + 0, 0, 0, 0, 0, 0, 0, 2, 5, 4, + 6, 7, 3, 31, 0, 0, 13, 38, 0, 0, + 68, 37, 32, 0, 35, 0, 66, 0, 67, 1, + 0, 0, 40, 0, 0, 33, 34, 0, 0, 0, + 0, 0, 0, 39, 0, 64, 0, 36, 65, 0, + 57, 0, 30, 0, 0, 16, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 10, 0, 0, 22, 26, 27, 24, 28, 25, + 17, 18, 20, 41, 43, 44, 69, 0, 62, 63, + 45, 54, 42, 60, 49, 53, 46, 0, 51, 48, + 47, 50, 0, 12, 58, 59, 11, 15, 0, 0, + 19, 0, 61, 52, 0, 14, 0, 29, 21, 0, + 23, 0, 55, 8, 0, 9, 56 +}; + +/* YYDEFGOTO[NTERM-NUM]. */ +static const yysigned_char yydefgoto[] = +{ + -1, 6, 7, 8, 9, 10, 11, 53, 54, 55, + 80, 81, 82, 116, 12, 13, 22, 23, 16, 17, + 43, 90, 121, 49, 50, 91, 44, 45, 25, 26, + 93 +}; + +/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing + STATE-NUM. */ +#define YYPACT_NINF -111 +static const yysigned_char yypact[] = +{ + -28, -110, -38, -46, -32, -42, 29, -111, -111, -111, + -111, -111, -111, -67, -42, -42, -111, -99, -42, -48, + -111, -111, -111, -38, 17, 7, 11, -62, -111, -111, + 37, -22, -111, -43, -16, -111, -111, -48, -42, -42, + -48, -42, -43, -3, -29, -111, -48, -111, -111, -68, + 48, 28, -111, -36, -19, -111, -18, -5, -43, -43, + -58, -58, -58, -70, -58, -58, -58, -34, -43, -42, + -61, 20, -42, 5, 2, -111, -111, -111, -111, -111, + -111, 1, -2, -111, -3, -3, -111, 19, -111, -111, + -111, -111, -111, -111, -111, -111, -111, -1, -111, -111, + -111, -111, -41, -3, -111, -111, -111, -111, -48, 19, + -111, 3, -111, -111, 13, -111, -12, -111, -111, -61, + -111, -9, 91, -13, -61, -111, -111 +}; + +/* YYPGOTO[NTERM-NUM]. */ +static const yysigned_char yypgoto[] = +{ + -111, -111, -111, -111, -111, -111, -111, -111, -111, 47, + -111, -111, -111, -111, -111, -111, 101, -27, 98, -111, + -11, 52, 0, 53, -111, -53, 46, -4, 85, 40, + -66 +}; + +/* YYTABLE[YYPACT[STATE-NUM]]. What to do in state STATE-NUM. If + positive, shift that token. If negative, reduce the rule which + number is the opposite. If zero, do what YYDEFACT says. + If YYTABLE_NINF, syntax error. */ +#define YYTABLE_NINF -68 +static const yysigned_char yytable[] = +{ + 24, 74, 58, 1, 58, 72, 19, 2, 20, 86, + 47, 14, 86, 52, 15, 24, 60, 105, 20, 67, + 97, 112, 98, 20, 20, 61, 18, 87, 62, 29, + 87, 57, 30, 24, 20, 51, 24, 56, 33, 3, + 42, 37, 24, 117, 63, 28, 38, 84, 85, 64, + -67, 75, 39, 40, 28, 28, 65, 103, 28, 88, + 66, 41, 88, 76, 77, 51, 122, 46, 56, 68, + 21, 122, 69, 70, 71, 78, 102, 89, 48, 106, + 89, 115, 108, 73, 4, 109, 21, 110, 111, 86, + 27, 113, 59, 114, 59, 118, 119, 79, 120, 31, + 32, 123, 5, 34, 24, 83, 92, 95, 95, 125, + 95, 95, 95, 94, 96, 124, 99, 100, 101, 107, + 35, 36, 104, 0, 126 +}; + +static const short yycheck[] = +{ + 4, 19, 7, 31, 7, 24, 38, 35, 66, 70, + 37, 121, 70, 40, 52, 19, 45, 70, 66, 46, + 90, 87, 92, 66, 66, 54, 72, 88, 57, 0, + 88, 42, 99, 37, 66, 39, 40, 41, 137, 67, + 83, 24, 46, 109, 73, 5, 39, 58, 59, 78, + 39, 69, 114, 16, 14, 15, 85, 68, 18, 120, + 89, 83, 120, 81, 82, 69, 119, 83, 72, 137, + 118, 124, 24, 45, 110, 93, 110, 138, 38, 59, + 138, 108, 77, 102, 112, 83, 118, 86, 90, 70, + 5, 92, 97, 134, 97, 92, 83, 115, 110, 14, + 15, 110, 130, 18, 108, 110, 60, 61, 62, 122, + 64, 65, 66, 61, 62, 24, 64, 65, 66, 72, + 19, 23, 69, -1, 124 +}; + +/* YYSTOS[STATE-NUM] -- The (internal number of the) accessing + symbol of state STATE-NUM. */ +static const unsigned char yystos[] = +{ + 0, 31, 35, 67, 112, 130, 148, 149, 150, 151, + 152, 153, 161, 162, 121, 52, 165, 166, 72, 38, + 66, 118, 163, 164, 174, 175, 176, 175, 176, 0, + 99, 175, 175, 137, 175, 163, 165, 24, 39, 114, + 16, 83, 83, 167, 173, 174, 83, 164, 176, 170, + 171, 174, 164, 154, 155, 156, 174, 167, 7, 97, + 45, 54, 57, 73, 78, 85, 89, 164, 137, 24, + 45, 110, 24, 102, 19, 69, 81, 82, 93, 115, + 157, 158, 159, 110, 167, 167, 70, 88, 120, 138, + 168, 172, 173, 177, 168, 173, 168, 90, 92, 168, + 168, 168, 110, 167, 170, 172, 59, 156, 77, 83, + 86, 90, 177, 92, 134, 164, 160, 177, 92, 83, + 110, 169, 172, 110, 24, 122, 169 +}; + +#if ! defined (YYSIZE_T) && defined (__SIZE_TYPE__) +# define YYSIZE_T __SIZE_TYPE__ +#endif +#if ! defined (YYSIZE_T) && defined (size_t) +# define YYSIZE_T size_t +#endif +#if ! defined (YYSIZE_T) +# if defined (__STDC__) || defined (__cplusplus) +# include /* INFRINGES ON USER NAME SPACE */ +# define YYSIZE_T size_t +# endif +#endif +#if ! defined (YYSIZE_T) +# define YYSIZE_T unsigned int +#endif + +#define yyerrok (yyerrstatus = 0) +#define yyclearin (yychar = YYEMPTY) +#define YYEMPTY (-2) +#define YYEOF 0 + +#define YYACCEPT goto yyacceptlab +#define YYABORT goto yyabortlab +#define YYERROR goto yyerrorlab + + +/* Like YYERROR except do call yyerror. This remains here temporarily + to ease the transition to the new meaning of YYERROR, for GCC. + Once GCC version 2 has supplanted version 1, this can go. */ + +#define YYFAIL goto yyerrlab + +#define YYRECOVERING() (!!yyerrstatus) + +#define YYBACKUP(Token, Value) \ +do \ + if (yychar == YYEMPTY && yylen == 1) \ + { \ + yychar = (Token); \ + yylval = (Value); \ + yytoken = YYTRANSLATE (yychar); \ + YYPOPSTACK; \ + goto yybackup; \ + } \ + else \ + { \ + yyerror ("syntax error: cannot back up");\ + YYERROR; \ + } \ +while (0) + +#define YYTERROR 1 +#define YYERRCODE 256 + +/* YYLLOC_DEFAULT -- Compute the default location (before the actions + are run). */ + +#ifndef YYLLOC_DEFAULT +# define YYLLOC_DEFAULT(Current, Rhs, N) \ + ((Current).first_line = (Rhs)[1].first_line, \ + (Current).first_column = (Rhs)[1].first_column, \ + (Current).last_line = (Rhs)[N].last_line, \ + (Current).last_column = (Rhs)[N].last_column) +#endif + +/* YYLEX -- calling `yylex' with the right arguments. */ + +#ifdef YYLEX_PARAM +# define YYLEX yylex (&yylval, YYLEX_PARAM) +#else +# define YYLEX yylex (&yylval) +#endif + +/* Enable debugging if requested. */ +#if YYDEBUG + +# ifndef YYFPRINTF +# include /* INFRINGES ON USER NAME SPACE */ +# define YYFPRINTF fprintf +# endif + +# define YYDPRINTF(Args) \ +do { \ + if (yydebug) \ + YYFPRINTF Args; \ +} while (0) + +# define YYDSYMPRINT(Args) \ +do { \ + if (yydebug) \ + yysymprint Args; \ +} while (0) + +# define YYDSYMPRINTF(Title, Token, Value, Location) \ +do { \ + if (yydebug) \ + { \ + YYFPRINTF (stderr, "%s ", Title); \ + yysymprint (stderr, \ + Token, Value); \ + YYFPRINTF (stderr, "\n"); \ + } \ +} while (0) + +/*------------------------------------------------------------------. +| yy_stack_print -- Print the state stack from its BOTTOM up to its | +| TOP (included). | +`------------------------------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yy_stack_print (short *bottom, short *top) +#else +static void +yy_stack_print (bottom, top) + short *bottom; + short *top; +#endif +{ + YYFPRINTF (stderr, "Stack now"); + for (/* Nothing. */; bottom <= top; ++bottom) + YYFPRINTF (stderr, " %d", *bottom); + YYFPRINTF (stderr, "\n"); +} + +# define YY_STACK_PRINT(Bottom, Top) \ +do { \ + if (yydebug) \ + yy_stack_print ((Bottom), (Top)); \ +} while (0) + + +/*------------------------------------------------. +| Report that the YYRULE is going to be reduced. | +`------------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yy_reduce_print (int yyrule) +#else +static void +yy_reduce_print (yyrule) + int yyrule; +#endif +{ + int yyi; + unsigned int yylno = yyrline[yyrule]; + YYFPRINTF (stderr, "Reducing stack by rule %d (line %u), ", + yyrule - 1, yylno); + /* Print the symbols being reduced, and their result. */ + for (yyi = yyprhs[yyrule]; 0 <= yyrhs[yyi]; yyi++) + YYFPRINTF (stderr, "%s ", yytname [yyrhs[yyi]]); + YYFPRINTF (stderr, "-> %s\n", yytname [yyr1[yyrule]]); +} + +# define YY_REDUCE_PRINT(Rule) \ +do { \ + if (yydebug) \ + yy_reduce_print (Rule); \ +} while (0) + +/* Nonzero means print parse trace. It is left uninitialized so that + multiple parsers can coexist. */ +int yydebug; +#else /* !YYDEBUG */ +# define YYDPRINTF(Args) +# define YYDSYMPRINT(Args) +# define YYDSYMPRINTF(Title, Token, Value, Location) +# define YY_STACK_PRINT(Bottom, Top) +# define YY_REDUCE_PRINT(Rule) +#endif /* !YYDEBUG */ + + +/* YYINITDEPTH -- initial size of the parser's stacks. */ +#ifndef YYINITDEPTH +# define YYINITDEPTH 200 +#endif + +/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only + if the built-in stack extension method is used). + + Do not make this value too large; the results are undefined if + SIZE_MAX < YYSTACK_BYTES (YYMAXDEPTH) + evaluated with infinite-precision integer arithmetic. */ + +#if defined (YYMAXDEPTH) && YYMAXDEPTH == 0 +# undef YYMAXDEPTH +#endif + +#ifndef YYMAXDEPTH +# define YYMAXDEPTH 10000 +#endif + + + +#if YYERROR_VERBOSE + +# ifndef yystrlen +# if defined (__GLIBC__) && defined (_STRING_H) +# define yystrlen strlen +# else +/* Return the length of YYSTR. */ +static YYSIZE_T +# if defined (__STDC__) || defined (__cplusplus) +yystrlen (const char *yystr) +# else +yystrlen (yystr) + const char *yystr; +# endif +{ + register const char *yys = yystr; + + while (*yys++ != '\0') + continue; + + return yys - yystr - 1; +} +# endif +# endif + +# ifndef yystpcpy +# if defined (__GLIBC__) && defined (_STRING_H) && defined (_GNU_SOURCE) +# define yystpcpy stpcpy +# else +/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in + YYDEST. */ +static char * +# if defined (__STDC__) || defined (__cplusplus) +yystpcpy (char *yydest, const char *yysrc) +# else +yystpcpy (yydest, yysrc) + char *yydest; + const char *yysrc; +# endif +{ + register char *yyd = yydest; + register const char *yys = yysrc; + + while ((*yyd++ = *yys++) != '\0') + continue; + + return yyd - 1; +} +# endif +# endif + +#endif /* !YYERROR_VERBOSE */ + + + +#if YYDEBUG +/*--------------------------------. +| Print this symbol on YYOUTPUT. | +`--------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yysymprint (FILE *yyoutput, int yytype, YYSTYPE *yyvaluep) +#else +static void +yysymprint (yyoutput, yytype, yyvaluep) + FILE *yyoutput; + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + /* Pacify ``unused variable'' warnings. */ + (void) yyvaluep; + + if (yytype < YYNTOKENS) + { + YYFPRINTF (yyoutput, "token %s (", yytname[yytype]); +# ifdef YYPRINT + YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep); +# endif + } + else + YYFPRINTF (yyoutput, "nterm %s (", yytname[yytype]); + + switch (yytype) + { + default: + break; + } + YYFPRINTF (yyoutput, ")"); +} + +#endif /* ! YYDEBUG */ +/*-----------------------------------------------. +| Release the memory associated to this symbol. | +`-----------------------------------------------*/ + +#if defined (__STDC__) || defined (__cplusplus) +static void +yydestruct (int yytype, YYSTYPE *yyvaluep) +#else +static void +yydestruct (yytype, yyvaluep) + int yytype; + YYSTYPE *yyvaluep; +#endif +{ + /* Pacify ``unused variable'' warnings. */ + (void) yyvaluep; + + switch (yytype) + { + + default: + break; + } +} + + +/* Prevent warnings from -Wmissing-prototypes. */ + +#ifdef YYPARSE_PARAM +# if defined (__STDC__) || defined (__cplusplus) +int yyparse (void *YYPARSE_PARAM); +# else +int yyparse (); +# endif +#else /* ! YYPARSE_PARAM */ +#if defined (__STDC__) || defined (__cplusplus) +int yyparse (void); +#else +int yyparse (); +#endif +#endif /* ! YYPARSE_PARAM */ + + + + + + +/*----------. +| yyparse. | +`----------*/ + +#ifdef YYPARSE_PARAM +# if defined (__STDC__) || defined (__cplusplus) +int yyparse (void *YYPARSE_PARAM) +# else +int yyparse (YYPARSE_PARAM) + void *YYPARSE_PARAM; +# endif +#else /* ! YYPARSE_PARAM */ +#if defined (__STDC__) || defined (__cplusplus) +int +yyparse (void) +#else +int +yyparse () + +#endif +#endif +{ + /* The lookahead symbol. */ +int yychar; + +/* The semantic value of the lookahead symbol. */ +YYSTYPE yylval; + +/* Number of syntax errors so far. */ +int yynerrs; + + register int yystate; + register int yyn; + int yyresult; + /* Number of tokens to shift before error messages enabled. */ + int yyerrstatus; + /* Lookahead token as an internal (translated) token number. */ + int yytoken = 0; + + /* Three stacks and their tools: + `yyss': related to states, + `yyvs': related to semantic values, + `yyls': related to locations. + + Refer to the stacks thru separate pointers, to allow yyoverflow + to reallocate them elsewhere. */ + + /* The state stack. */ + short yyssa[YYINITDEPTH]; + short *yyss = yyssa; + register short *yyssp; + + /* The semantic value stack. */ + YYSTYPE yyvsa[YYINITDEPTH]; + YYSTYPE *yyvs = yyvsa; + register YYSTYPE *yyvsp; + + + +#define YYPOPSTACK (yyvsp--, yyssp--) + + YYSIZE_T yystacksize = YYINITDEPTH; + + /* The variables used to return semantic value and location from the + action routines. */ + YYSTYPE yyval; + + + /* When reducing, the number of symbols on the RHS of the reduced + rule. */ + int yylen; + + YYDPRINTF ((stderr, "Starting parse\n")); + + yystate = 0; + yyerrstatus = 0; + yynerrs = 0; + yychar = YYEMPTY; /* Cause a token to be read. */ + + /* Initialize stack pointers. + Waste one element of value and location stack + so that they stay on the same level as the state stack. + The wasted elements are never initialized. */ + + yyssp = yyss; + yyvsp = yyvs; + + goto yysetstate; + +/*------------------------------------------------------------. +| yynewstate -- Push a new state, which is found in yystate. | +`------------------------------------------------------------*/ + yynewstate: + /* In all cases, when you get here, the value and location stacks + have just been pushed. so pushing a state here evens the stacks. + */ + yyssp++; + + yysetstate: + *yyssp = yystate; + + if (yyss + yystacksize - 1 <= yyssp) + { + /* Get the current used size of the three stacks, in elements. */ + YYSIZE_T yysize = yyssp - yyss + 1; + +#ifdef yyoverflow + { + /* Give user a chance to reallocate the stack. Use copies of + these so that the &'s don't force the real ones into + memory. */ + YYSTYPE *yyvs1 = yyvs; + short *yyss1 = yyss; + + + /* Each stack pointer address is followed by the size of the + data in use in that stack, in bytes. This used to be a + conditional around just the two extra args, but that might + be undefined if yyoverflow is a macro. */ + yyoverflow ("parser stack overflow", + &yyss1, yysize * sizeof (*yyssp), + &yyvs1, yysize * sizeof (*yyvsp), + + &yystacksize); + + yyss = yyss1; + yyvs = yyvs1; + } +#else /* no yyoverflow */ +# ifndef YYSTACK_RELOCATE + goto yyoverflowlab; +# else + /* Extend the stack our own way. */ + if (YYMAXDEPTH <= yystacksize) + goto yyoverflowlab; + yystacksize *= 2; + if (YYMAXDEPTH < yystacksize) + yystacksize = YYMAXDEPTH; + + { + short *yyss1 = yyss; + union yyalloc *yyptr = + (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize)); + if (! yyptr) + goto yyoverflowlab; + YYSTACK_RELOCATE (yyss); + YYSTACK_RELOCATE (yyvs); + +# undef YYSTACK_RELOCATE + if (yyss1 != yyssa) + YYSTACK_FREE (yyss1); + } +# endif +#endif /* no yyoverflow */ + + yyssp = yyss + yysize - 1; + yyvsp = yyvs + yysize - 1; + + + YYDPRINTF ((stderr, "Stack size increased to %lu\n", + (unsigned long int) yystacksize)); + + if (yyss + yystacksize - 1 <= yyssp) + YYABORT; + } + + YYDPRINTF ((stderr, "Entering state %d\n", yystate)); + + goto yybackup; + +/*-----------. +| yybackup. | +`-----------*/ +yybackup: + +/* Do appropriate processing given the current state. */ +/* Read a lookahead token if we need one and don't already have one. */ +/* yyresume: */ + + /* First try to decide what to do without reference to lookahead token. */ + + yyn = yypact[yystate]; + if (yyn == YYPACT_NINF) + goto yydefault; + + /* Not known => get a lookahead token if don't already have one. */ + + /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol. */ + if (yychar == YYEMPTY) + { + YYDPRINTF ((stderr, "Reading a token: ")); + yychar = YYLEX; + } + + if (yychar <= YYEOF) + { + yychar = yytoken = YYEOF; + YYDPRINTF ((stderr, "Now at end of input.\n")); + } + else + { + yytoken = YYTRANSLATE (yychar); + YYDSYMPRINTF ("Next token is", yytoken, &yylval, &yylloc); + } + + /* If the proper action on seeing token YYTOKEN is to reduce or to + detect an error, take that action. */ + yyn += yytoken; + if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken) + goto yydefault; + yyn = yytable[yyn]; + if (yyn <= 0) + { + if (yyn == 0 || yyn == YYTABLE_NINF) + goto yyerrlab; + yyn = -yyn; + goto yyreduce; + } + + if (yyn == YYFINAL) + YYACCEPT; + + /* Shift the lookahead token. */ + YYDPRINTF ((stderr, "Shifting token %s, ", yytname[yytoken])); + + /* Discard the token being shifted unless it is eof. */ + if (yychar != YYEOF) + yychar = YYEMPTY; + + *++yyvsp = yylval; + + + /* Count tokens shifted since error; after three, turn off error + status. */ + if (yyerrstatus) + yyerrstatus--; + + yystate = yyn; + goto yynewstate; + + +/*-----------------------------------------------------------. +| yydefault -- do the default action for the current state. | +`-----------------------------------------------------------*/ +yydefault: + yyn = yydefact[yystate]; + if (yyn == 0) + goto yyerrlab; + goto yyreduce; + + +/*-----------------------------. +| yyreduce -- Do a reduction. | +`-----------------------------*/ +yyreduce: + /* yyn is the number of a rule to reduce with. */ + yylen = yyr2[yyn]; + + /* If YYLEN is nonzero, implement the default value of the action: + `$$ = $1'. + + Otherwise, the following line sets YYVAL to garbage. + This behavior is undocumented and Bison + users should not rely upon it. Assigning to YYVAL + unconditionally makes the parser a bit smaller, and it avoids a + GCC warning that YYVAL may be used uninitialized. */ + yyval = yyvsp[1-yylen]; + + + YY_REDUCE_PRINT (yyn); + switch (yyn) + { + case 2: +#line 139 "./sql.y" + { + SQL_input* sql = (SQL_input*) info; + *sql->view = yyvsp[0].query; + ;} + break; + + case 8: +#line 155 "./sql.y" + { + SQL_input *sql = (SQL_input*) info; + MSIVIEW *insert = NULL; + UINT r; + + r = INSERT_CreateView( sql->db, &insert, yyvsp[-7].string, yyvsp[-5].column_list, yyvsp[-1].column_list, FALSE ); + if( !insert ) + YYABORT; + yyval.query = insert; + ;} + break; + + case 9: +#line 166 "./sql.y" + { + SQL_input *sql = (SQL_input*) info; + MSIVIEW *insert = NULL; + + INSERT_CreateView( sql->db, &insert, yyvsp[-8].string, yyvsp[-6].column_list, yyvsp[-2].column_list, TRUE ); + if( !insert ) + YYABORT; + yyval.query = insert; + ;} + break; + + case 10: +#line 179 "./sql.y" + { + SQL_input* sql = (SQL_input*) info; + MSIVIEW *create = NULL; + + if( !yyvsp[-1].column_list ) + YYABORT; + CREATE_CreateView( sql->db, &create, yyvsp[-3].string, yyvsp[-1].column_list, FALSE ); + if( !create ) + YYABORT; + yyval.query = create; + ;} + break; + + case 11: +#line 191 "./sql.y" + { + SQL_input* sql = (SQL_input*) info; + MSIVIEW *create = NULL; + + if( !yyvsp[-2].column_list ) + YYABORT; + CREATE_CreateView( sql->db, &create, yyvsp[-4].string, yyvsp[-2].column_list, TRUE ); + if( !create ) + YYABORT; + yyval.query = create; + ;} + break; + + case 12: +#line 206 "./sql.y" + { + SQL_input* sql = (SQL_input*) info; + MSIVIEW *update = NULL; + + UPDATE_CreateView( sql->db, &update, yyvsp[-4].string, yyvsp[-2].column_list, yyvsp[0].expr ); + if( !update ) + YYABORT; + yyval.query = update; + ;} + break; + + case 13: +#line 219 "./sql.y" + { + SQL_input* sql = (SQL_input*) info; + MSIVIEW *delete = NULL; + + DELETE_CreateView( sql->db, &delete, yyvsp[0].query ); + if( !delete ) + YYABORT; + yyval.query = delete; + ;} + break; + + case 14: +#line 232 "./sql.y" + { + if( SQL_MarkPrimaryKeys( yyvsp[-3].column_list, yyvsp[0].column_list ) ) + yyval.column_list = yyvsp[-3].column_list; + else + yyval.column_list = NULL; + ;} + break; + + case 15: +#line 242 "./sql.y" + { + column_info *ci; + + for( ci = yyvsp[-2].column_list; ci->next; ci = ci->next ) + ; + + ci->next = yyvsp[0].column_list; + yyval.column_list = yyvsp[-2].column_list; + ;} + break; + + case 16: +#line 252 "./sql.y" + { + yyval.column_list = yyvsp[0].column_list; + ;} + break; + + case 17: +#line 259 "./sql.y" + { + yyval.column_list = yyvsp[-1].column_list; + yyval.column_list->type = yyvsp[0].column_type | MSITYPE_VALID; + ;} + break; + + case 18: +#line 267 "./sql.y" + { + yyval.column_type = yyvsp[0].column_type; + ;} + break; + + case 19: +#line 271 "./sql.y" + { + yyval.column_type = yyvsp[-1].column_type | MSITYPE_LOCALIZABLE; + ;} + break; + + case 20: +#line 278 "./sql.y" + { + yyval.column_type |= MSITYPE_NULLABLE; + ;} + break; + + case 21: +#line 282 "./sql.y" + { + yyval.column_type = yyvsp[-2].column_type; + ;} + break; + + case 22: +#line 289 "./sql.y" + { + yyval.column_type = MSITYPE_STRING | 1; + ;} + break; + + case 23: +#line 293 "./sql.y" + { + yyval.column_type = MSITYPE_STRING | 0x400 | yyvsp[-1].column_type; + ;} + break; + + case 24: +#line 297 "./sql.y" + { + yyval.column_type = 2; + ;} + break; + + case 25: +#line 301 "./sql.y" + { + yyval.column_type = 2; + ;} + break; + + case 26: +#line 305 "./sql.y" + { + yyval.column_type = 2; + ;} + break; + + case 27: +#line 309 "./sql.y" + { + yyval.column_type = 4; + ;} + break; + + case 28: +#line 313 "./sql.y" + { + yyval.column_type = MSITYPE_STRING | MSITYPE_VALID; + ;} + break; + + case 29: +#line 320 "./sql.y" + { + if( ( yyvsp[0].integer > 255 ) || ( yyvsp[0].integer < 0 ) ) + YYABORT; + yyval.column_type = yyvsp[0].integer; + ;} + break; + + case 30: +#line 329 "./sql.y" + { + SQL_input* sql = (SQL_input*) info; + + yyval.query = NULL; + if( yyvsp[0].column_list ) + ORDER_CreateView( sql->db, &yyval.query, yyvsp[-3].query, yyvsp[0].column_list ); + else + yyval.query = yyvsp[-3].query; + if( !yyval.query ) + YYABORT; + ;} + break; + + case 32: +#line 345 "./sql.y" + { + yyval.query = yyvsp[0].query; + ;} + break; + + case 33: +#line 349 "./sql.y" + { + SQL_input* sql = (SQL_input*) info; + UINT r; + + yyval.query = NULL; + r = DISTINCT_CreateView( sql->db, &yyval.query, yyvsp[0].query ); + if (r != ERROR_SUCCESS) + { + yyvsp[0].query->ops->delete(yyvsp[0].query); + YYABORT; + } + ;} + break; + + case 34: +#line 365 "./sql.y" + { + SQL_input* sql = (SQL_input*) info; + UINT r; + + yyval.query = NULL; + if( yyvsp[-1].column_list ) + { + r = SELECT_CreateView( sql->db, &yyval.query, yyvsp[0].query, yyvsp[-1].column_list ); + if (r != ERROR_SUCCESS) + { + yyvsp[0].query->ops->delete(yyvsp[0].query); + YYABORT; + } + } + else + yyval.query = yyvsp[0].query; + ;} + break; + + case 36: +#line 387 "./sql.y" + { + yyvsp[-2].column_list->next = yyvsp[0].column_list; + ;} + break; + + case 37: +#line 391 "./sql.y" + { + yyval.column_list = NULL; + ;} + break; + + case 39: +#line 399 "./sql.y" + { + SQL_input* sql = (SQL_input*) info; + UINT r; + + yyval.query = NULL; + r = WHERE_CreateView( sql->db, &yyval.query, yyvsp[-2].query, yyvsp[0].expr ); + if( r != ERROR_SUCCESS ) + { + yyvsp[-2].query->ops->delete( yyvsp[-2].query ); + YYABORT; + } + ;} + break; + + case 40: +#line 415 "./sql.y" + { + SQL_input* sql = (SQL_input*) info; + UINT r; + + yyval.query = NULL; + r = TABLE_CreateView( sql->db, yyvsp[0].string, &yyval.query ); + if( r != ERROR_SUCCESS || !yyval.query ) + YYABORT; + ;} + break; + + case 41: +#line 428 "./sql.y" + { + yyval.expr = yyvsp[-1].expr; + if( !yyval.expr ) + YYABORT; + ;} + break; + + case 42: +#line 434 "./sql.y" + { + yyval.expr = EXPR_complex( info, yyvsp[-2].expr, OP_EQ, yyvsp[0].expr ); + if( !yyval.expr ) + YYABORT; + ;} + break; + + case 43: +#line 440 "./sql.y" + { + yyval.expr = EXPR_complex( info, yyvsp[-2].expr, OP_AND, yyvsp[0].expr ); + if( !yyval.expr ) + YYABORT; + ;} + break; + + case 44: +#line 446 "./sql.y" + { + yyval.expr = EXPR_complex( info, yyvsp[-2].expr, OP_OR, yyvsp[0].expr ); + if( !yyval.expr ) + YYABORT; + ;} + break; + + case 45: +#line 452 "./sql.y" + { + yyval.expr = EXPR_complex( info, yyvsp[-2].expr, OP_EQ, yyvsp[0].expr ); + if( !yyval.expr ) + YYABORT; + ;} + break; + + case 46: +#line 458 "./sql.y" + { + yyval.expr = EXPR_complex( info, yyvsp[-2].expr, OP_GT, yyvsp[0].expr ); + if( !yyval.expr ) + YYABORT; + ;} + break; + + case 47: +#line 464 "./sql.y" + { + yyval.expr = EXPR_complex( info, yyvsp[-2].expr, OP_LT, yyvsp[0].expr ); + if( !yyval.expr ) + YYABORT; + ;} + break; + + case 48: +#line 470 "./sql.y" + { + yyval.expr = EXPR_complex( info, yyvsp[-2].expr, OP_LE, yyvsp[0].expr ); + if( !yyval.expr ) + YYABORT; + ;} + break; + + case 49: +#line 476 "./sql.y" + { + yyval.expr = EXPR_complex( info, yyvsp[-2].expr, OP_GE, yyvsp[0].expr ); + if( !yyval.expr ) + YYABORT; + ;} + break; + + case 50: +#line 482 "./sql.y" + { + yyval.expr = EXPR_complex( info, yyvsp[-2].expr, OP_NE, yyvsp[0].expr ); + if( !yyval.expr ) + YYABORT; + ;} + break; + + case 51: +#line 488 "./sql.y" + { + yyval.expr = EXPR_complex( info, yyvsp[-2].expr, OP_ISNULL, NULL ); + if( !yyval.expr ) + YYABORT; + ;} + break; + + case 52: +#line 494 "./sql.y" + { + yyval.expr = EXPR_complex( info, yyvsp[-3].expr, OP_NOTNULL, NULL ); + if( !yyval.expr ) + YYABORT; + ;} + break; + + case 55: +#line 508 "./sql.y" + { + yyval.column_list = parser_alloc_column( info, NULL, NULL ); + if( !yyval.column_list ) + YYABORT; + yyval.column_list->val = yyvsp[0].expr; + ;} + break; + + case 56: +#line 515 "./sql.y" + { + yyval.column_list = parser_alloc_column( info, NULL, NULL ); + if( !yyval.column_list ) + YYABORT; + yyval.column_list->val = yyvsp[-2].expr; + yyval.column_list->next = yyvsp[0].column_list; + ;} + break; + + case 58: +#line 527 "./sql.y" + { + yyval.column_list = yyvsp[-2].column_list; + yyval.column_list->next = yyvsp[0].column_list; + ;} + break; + + case 59: +#line 535 "./sql.y" + { + yyval.column_list = yyvsp[-2].column_list; + yyval.column_list->val = yyvsp[0].expr; + ;} + break; + + case 60: +#line 543 "./sql.y" + { + yyval.expr = EXPR_ival( info, yyvsp[0].integer ); + if( !yyval.expr ) + YYABORT; + ;} + break; + + case 61: +#line 549 "./sql.y" + { + yyval.expr = EXPR_ival( info, -yyvsp[0].integer ); + if( !yyval.expr ) + YYABORT; + ;} + break; + + case 62: +#line 555 "./sql.y" + { + yyval.expr = EXPR_sval( info, &yyvsp[0].str ); + if( !yyval.expr ) + YYABORT; + ;} + break; + + case 63: +#line 561 "./sql.y" + { + yyval.expr = EXPR_wildcard( info ); + if( !yyval.expr ) + YYABORT; + ;} + break; + + case 64: +#line 570 "./sql.y" + { + yyval.expr = EXPR_column( info, yyvsp[0].column_list ); + if( !yyval.expr ) + YYABORT; + ;} + break; + + case 65: +#line 579 "./sql.y" + { + yyval.column_list = parser_alloc_column( info, yyvsp[-2].string, yyvsp[0].string ); + if( !yyval.column_list ) + YYABORT; + ;} + break; + + case 66: +#line 585 "./sql.y" + { + yyval.column_list = parser_alloc_column( info, NULL, yyvsp[0].string ); + if( !yyval.column_list ) + YYABORT; + ;} + break; + + case 67: +#line 594 "./sql.y" + { + yyval.string = yyvsp[0].string; + ;} + break; + + case 68: +#line 601 "./sql.y" + { + yyval.string = SQL_getstring( info, &yyvsp[0].str ); + if( !yyval.string ) + YYABORT; + ;} + break; + + case 69: +#line 610 "./sql.y" + { + yyval.integer = SQL_getint( info ); + ;} + break; + + + } + +/* Line 1000 of yacc.c. */ +#line 2054 "sql.tab.c" + + yyvsp -= yylen; + yyssp -= yylen; + + + YY_STACK_PRINT (yyss, yyssp); + + *++yyvsp = yyval; + + + /* Now `shift' the result of the reduction. Determine what state + that goes to, based on the state we popped back to and the rule + number reduced by. */ + + yyn = yyr1[yyn]; + + yystate = yypgoto[yyn - YYNTOKENS] + *yyssp; + if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp) + yystate = yytable[yystate]; + else + yystate = yydefgoto[yyn - YYNTOKENS]; + + goto yynewstate; + + +/*------------------------------------. +| yyerrlab -- here on detecting error | +`------------------------------------*/ +yyerrlab: + /* If not already recovering from an error, report this error. */ + if (!yyerrstatus) + { + ++yynerrs; +#if YYERROR_VERBOSE + yyn = yypact[yystate]; + + if (YYPACT_NINF < yyn && yyn < YYLAST) + { + YYSIZE_T yysize = 0; + int yytype = YYTRANSLATE (yychar); + const char* yyprefix; + char *yymsg; + int yyx; + + /* Start YYX at -YYN if negative to avoid negative indexes in + YYCHECK. */ + int yyxbegin = yyn < 0 ? -yyn : 0; + + /* Stay within bounds of both yycheck and yytname. */ + int yychecklim = YYLAST - yyn; + int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS; + int yycount = 0; + + yyprefix = ", expecting "; + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + yysize += yystrlen (yyprefix) + yystrlen (yytname [yyx]); + yycount += 1; + if (yycount == 5) + { + yysize = 0; + break; + } + } + yysize += (sizeof ("syntax error, unexpected ") + + yystrlen (yytname[yytype])); + yymsg = (char *) YYSTACK_ALLOC (yysize); + if (yymsg != 0) + { + char *yyp = yystpcpy (yymsg, "syntax error, unexpected "); + yyp = yystpcpy (yyp, yytname[yytype]); + + if (yycount < 5) + { + yyprefix = ", expecting "; + for (yyx = yyxbegin; yyx < yyxend; ++yyx) + if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR) + { + yyp = yystpcpy (yyp, yyprefix); + yyp = yystpcpy (yyp, yytname[yyx]); + yyprefix = " or "; + } + } + yyerror (yymsg); + YYSTACK_FREE (yymsg); + } + else + yyerror ("syntax error; also virtual memory exhausted"); + } + else +#endif /* YYERROR_VERBOSE */ + yyerror ("syntax error"); + } + + + + if (yyerrstatus == 3) + { + /* If just tried and failed to reuse lookahead token after an + error, discard it. */ + + if (yychar <= YYEOF) + { + /* If at end of input, pop the error token, + then the rest of the stack, then return failure. */ + if (yychar == YYEOF) + for (;;) + { + YYPOPSTACK; + if (yyssp == yyss) + YYABORT; + YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp); + yydestruct (yystos[*yyssp], yyvsp); + } + } + else + { + YYDSYMPRINTF ("Error: discarding", yytoken, &yylval, &yylloc); + yydestruct (yytoken, &yylval); + yychar = YYEMPTY; + + } + } + + /* Else will try to reuse lookahead token after shifting the error + token. */ + goto yyerrlab1; + + +/*---------------------------------------------------. +| yyerrorlab -- error raised explicitly by YYERROR. | +`---------------------------------------------------*/ +yyerrorlab: + +#ifdef __GNUC__ + /* Pacify GCC when the user code never invokes YYERROR and the label + yyerrorlab therefore never appears in user code. */ + if (0) + goto yyerrorlab; +#endif + + yyvsp -= yylen; + yyssp -= yylen; + yystate = *yyssp; + goto yyerrlab1; + + +/*-------------------------------------------------------------. +| yyerrlab1 -- common code for both syntax error and YYERROR. | +`-------------------------------------------------------------*/ +yyerrlab1: + yyerrstatus = 3; /* Each real token shifted decrements this. */ + + for (;;) + { + yyn = yypact[yystate]; + if (yyn != YYPACT_NINF) + { + yyn += YYTERROR; + if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR) + { + yyn = yytable[yyn]; + if (0 < yyn) + break; + } + } + + /* Pop the current state because it cannot handle the error token. */ + if (yyssp == yyss) + YYABORT; + + YYDSYMPRINTF ("Error: popping", yystos[*yyssp], yyvsp, yylsp); + yydestruct (yystos[yystate], yyvsp); + YYPOPSTACK; + yystate = *yyssp; + YY_STACK_PRINT (yyss, yyssp); + } + + if (yyn == YYFINAL) + YYACCEPT; + + YYDPRINTF ((stderr, "Shifting error token, ")); + + *++yyvsp = yylval; + + + yystate = yyn; + goto yynewstate; + + +/*-------------------------------------. +| yyacceptlab -- YYACCEPT comes here. | +`-------------------------------------*/ +yyacceptlab: + yyresult = 0; + goto yyreturn; + +/*-----------------------------------. +| yyabortlab -- YYABORT comes here. | +`-----------------------------------*/ +yyabortlab: + yyresult = 1; + goto yyreturn; + +#ifndef yyoverflow +/*----------------------------------------------. +| yyoverflowlab -- parser overflow comes here. | +`----------------------------------------------*/ +yyoverflowlab: + yyerror ("parser stack overflow"); + yyresult = 2; + /* Fall through. */ +#endif + +yyreturn: +#ifndef yyoverflow + if (yyss != yyssa) + YYSTACK_FREE (yyss); +#endif + return yyresult; +} + + +#line 615 "./sql.y" + + +static void *parser_alloc( void *info, unsigned int sz ) +{ + SQL_input* sql = (SQL_input*) info; + struct list *mem; + + mem = msi_alloc( sizeof (struct list) + sz ); + list_add_tail( sql->mem, mem ); + return &mem[1]; +} + +static column_info *parser_alloc_column( void *info, LPCWSTR table, LPCWSTR column ) +{ + column_info *col; + + col = parser_alloc( info, sizeof (*col) ); + if( col ) + { + col->table = table; + col->column = column; + col->val = NULL; + col->type = 0; + col->next = NULL; + } + + return col; +} + +int SQL_lex( void *SQL_lval, SQL_input *sql ) +{ + int token; + struct sql_str * str = SQL_lval; + + do + { + sql->n += sql->len; + if( ! sql->command[sql->n] ) + return 0; /* end of input */ + + /* TRACE("string : %s\n", debugstr_w(&sql->command[sql->n])); */ + sql->len = sqliteGetToken( &sql->command[sql->n], &token ); + if( sql->len==0 ) + break; + str->data = &sql->command[sql->n]; + str->len = sql->len; + } + while( token == TK_SPACE ); + + /* TRACE("token : %d (%s)\n", token, debugstr_wn(&sql->command[sql->n], sql->len)); */ + + return token; +} + +LPWSTR SQL_getstring( void *info, struct sql_str *strdata ) +{ + LPCWSTR p = strdata->data; + UINT len = strdata->len; + LPWSTR str; + + /* if there's quotes, remove them */ + if( ( (p[0]=='`') && (p[len-1]=='`') ) || + ( (p[0]=='\'') && (p[len-1]=='\'') ) ) + { + p++; + len -= 2; + } + str = parser_alloc( info, (len + 1)*sizeof(WCHAR) ); + if( !str ) + return str; + memcpy( str, p, len*sizeof(WCHAR) ); + str[len]=0; + + return str; +} + +INT SQL_getint( void *info ) +{ + SQL_input* sql = (SQL_input*) info; + LPCWSTR p = &sql->command[sql->n]; + INT i, r = 0; + + for( i=0; ilen; i++ ) + { + if( '0' > p[i] || '9' < p[i] ) + { + ERR("should only be numbers here!\n"); + break; + } + r = (p[i]-'0') + r*10; + } + + return r; +} + +int SQL_error( const char *str ) +{ + return 0; +} + +static struct expr * EXPR_wildcard( void *info ) +{ + struct expr *e = parser_alloc( info, sizeof *e ); + if( e ) + { + e->type = EXPR_WILDCARD; + } + return e; +} + +static struct expr * EXPR_complex( void *info, struct expr *l, UINT op, struct expr *r ) +{ + struct expr *e = parser_alloc( info, sizeof *e ); + if( e ) + { + e->type = EXPR_COMPLEX; + e->u.expr.left = l; + e->u.expr.op = op; + e->u.expr.right = r; + } + return e; +} + +static struct expr * EXPR_column( void *info, column_info *column ) +{ + struct expr *e = parser_alloc( info, sizeof *e ); + if( e ) + { + e->type = EXPR_COLUMN; + e->u.sval = column->column; + } + return e; +} + +static struct expr * EXPR_ival( void *info, int val ) +{ + struct expr *e = parser_alloc( info, sizeof *e ); + if( e ) + { + e->type = EXPR_IVAL; + e->u.ival = val; + } + return e; +} + +static struct expr * EXPR_sval( void *info, struct sql_str *str ) +{ + struct expr *e = parser_alloc( info, sizeof *e ); + if( e ) + { + e->type = EXPR_SVAL; + e->u.sval = SQL_getstring( info, str ); + } + return e; +} + +static BOOL SQL_MarkPrimaryKeys( column_info *cols, + column_info *keys ) +{ + column_info *k; + BOOL found = TRUE; + + for( k = keys; k && found; k = k->next ) + { + column_info *c; + + found = FALSE; + for( c = cols; c && !found; c = c->next ) + { + if( lstrcmpW( k->column, c->column ) ) + continue; + c->type |= MSITYPE_KEY; + found = TRUE; + } + } + + return found; +} + +UINT MSI_ParseSQL( MSIDATABASE *db, LPCWSTR command, MSIVIEW **phview, + struct list *mem ) +{ + SQL_input sql; + int r; + + *phview = NULL; + + sql.db = db; + sql.command = command; + sql.n = 0; + sql.len = 0; + sql.view = phview; + sql.mem = mem; + + r = SQL_parse(&sql); + + TRACE("Parse returned %d\n", r); + if( r ) + { + *sql.view = NULL; + return ERROR_BAD_QUERY_SYNTAX; + } + + return ERROR_SUCCESS; +} + diff --git a/reactos/dll/msi/sql.tab.h b/reactos/dll/msi/sql.tab.h new file mode 100644 index 00000000000..4726cbd15e3 --- /dev/null +++ b/reactos/dll/msi/sql.tab.h @@ -0,0 +1,345 @@ +/* A Bison parser, made by GNU Bison 1.875c. */ + +/* Skeleton parser for Yacc-like parsing with Bison, + Copyright (C) 1984, 1989, 1990, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2, or (at your option) + any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, + Boston, MA 02111-1307, USA. */ + +/* As a special exception, when this file is copied by Bison into a + Bison output file, you may use that output file without restriction. + This special exception was added by the Free Software Foundation + in version 1.24 of Bison. */ + +/* Tokens. */ +#ifndef YYTOKENTYPE +# define YYTOKENTYPE + /* Put the tokens into the symbol table, so that GDB and other debuggers + know about them. */ + enum yytokentype { + TK_ABORT = 258, + TK_AFTER = 259, + TK_AGG_FUNCTION = 260, + TK_ALL = 261, + TK_AND = 262, + TK_AS = 263, + TK_ASC = 264, + TK_BEFORE = 265, + TK_BEGIN = 266, + TK_BETWEEN = 267, + TK_BITAND = 268, + TK_BITNOT = 269, + TK_BITOR = 270, + TK_BY = 271, + TK_CASCADE = 272, + TK_CASE = 273, + TK_CHAR = 274, + TK_CHECK = 275, + TK_CLUSTER = 276, + TK_COLLATE = 277, + TK_COLUMN = 278, + TK_COMMA = 279, + TK_COMMENT = 280, + TK_COMMIT = 281, + TK_CONCAT = 282, + TK_CONFLICT = 283, + TK_CONSTRAINT = 284, + TK_COPY = 285, + TK_CREATE = 286, + TK_DEFAULT = 287, + TK_DEFERRABLE = 288, + TK_DEFERRED = 289, + TK_DELETE = 290, + TK_DELIMITERS = 291, + TK_DESC = 292, + TK_DISTINCT = 293, + TK_DOT = 294, + TK_DROP = 295, + TK_EACH = 296, + TK_ELSE = 297, + TK_END = 298, + TK_END_OF_FILE = 299, + TK_EQ = 300, + TK_EXCEPT = 301, + TK_EXPLAIN = 302, + TK_FAIL = 303, + TK_FLOAT = 304, + TK_FOR = 305, + TK_FOREIGN = 306, + TK_FROM = 307, + TK_FUNCTION = 308, + TK_GE = 309, + TK_GLOB = 310, + TK_GROUP = 311, + TK_GT = 312, + TK_HAVING = 313, + TK_HOLD = 314, + TK_IGNORE = 315, + TK_ILLEGAL = 316, + TK_IMMEDIATE = 317, + TK_IN = 318, + TK_INDEX = 319, + TK_INITIALLY = 320, + TK_ID = 321, + TK_INSERT = 322, + TK_INSTEAD = 323, + TK_INT = 324, + TK_INTEGER = 325, + TK_INTERSECT = 326, + TK_INTO = 327, + TK_IS = 328, + TK_ISNULL = 329, + TK_JOIN = 330, + TK_JOIN_KW = 331, + TK_KEY = 332, + TK_LE = 333, + TK_LIKE = 334, + TK_LIMIT = 335, + TK_LONG = 336, + TK_LONGCHAR = 337, + TK_LP = 338, + TK_LSHIFT = 339, + TK_LT = 340, + TK_LOCALIZABLE = 341, + TK_MATCH = 342, + TK_MINUS = 343, + TK_NE = 344, + TK_NOT = 345, + TK_NOTNULL = 346, + TK_NULL = 347, + TK_OBJECT = 348, + TK_OF = 349, + TK_OFFSET = 350, + TK_ON = 351, + TK_OR = 352, + TK_ORACLE_OUTER_JOIN = 353, + TK_ORDER = 354, + TK_PLUS = 355, + TK_PRAGMA = 356, + TK_PRIMARY = 357, + TK_RAISE = 358, + TK_REFERENCES = 359, + TK_REM = 360, + TK_REPLACE = 361, + TK_RESTRICT = 362, + TK_ROLLBACK = 363, + TK_ROW = 364, + TK_RP = 365, + TK_RSHIFT = 366, + TK_SELECT = 367, + TK_SEMI = 368, + TK_SET = 369, + TK_SHORT = 370, + TK_SLASH = 371, + TK_SPACE = 372, + TK_STAR = 373, + TK_STATEMENT = 374, + TK_STRING = 375, + TK_TABLE = 376, + TK_TEMP = 377, + TK_THEN = 378, + TK_TRANSACTION = 379, + TK_TRIGGER = 380, + TK_UMINUS = 381, + TK_UNCLOSED_STRING = 382, + TK_UNION = 383, + TK_UNIQUE = 384, + TK_UPDATE = 385, + TK_UPLUS = 386, + TK_USING = 387, + TK_VACUUM = 388, + TK_VALUES = 389, + TK_VIEW = 390, + TK_WHEN = 391, + TK_WHERE = 392, + TK_WILDCARD = 393, + COLUMN = 395, + FUNCTION = 396, + COMMENT = 397, + UNCLOSED_STRING = 398, + SPACE = 399, + ILLEGAL = 400, + END_OF_FILE = 401 + }; +#endif +#define TK_ABORT 258 +#define TK_AFTER 259 +#define TK_AGG_FUNCTION 260 +#define TK_ALL 261 +#define TK_AND 262 +#define TK_AS 263 +#define TK_ASC 264 +#define TK_BEFORE 265 +#define TK_BEGIN 266 +#define TK_BETWEEN 267 +#define TK_BITAND 268 +#define TK_BITNOT 269 +#define TK_BITOR 270 +#define TK_BY 271 +#define TK_CASCADE 272 +#define TK_CASE 273 +#define TK_CHAR 274 +#define TK_CHECK 275 +#define TK_CLUSTER 276 +#define TK_COLLATE 277 +#define TK_COLUMN 278 +#define TK_COMMA 279 +#define TK_COMMENT 280 +#define TK_COMMIT 281 +#define TK_CONCAT 282 +#define TK_CONFLICT 283 +#define TK_CONSTRAINT 284 +#define TK_COPY 285 +#define TK_CREATE 286 +#define TK_DEFAULT 287 +#define TK_DEFERRABLE 288 +#define TK_DEFERRED 289 +#define TK_DELETE 290 +#define TK_DELIMITERS 291 +#define TK_DESC 292 +#define TK_DISTINCT 293 +#define TK_DOT 294 +#define TK_DROP 295 +#define TK_EACH 296 +#define TK_ELSE 297 +#define TK_END 298 +#define TK_END_OF_FILE 299 +#define TK_EQ 300 +#define TK_EXCEPT 301 +#define TK_EXPLAIN 302 +#define TK_FAIL 303 +#define TK_FLOAT 304 +#define TK_FOR 305 +#define TK_FOREIGN 306 +#define TK_FROM 307 +#define TK_FUNCTION 308 +#define TK_GE 309 +#define TK_GLOB 310 +#define TK_GROUP 311 +#define TK_GT 312 +#define TK_HAVING 313 +#define TK_HOLD 314 +#define TK_IGNORE 315 +#define TK_ILLEGAL 316 +#define TK_IMMEDIATE 317 +#define TK_IN 318 +#define TK_INDEX 319 +#define TK_INITIALLY 320 +#define TK_ID 321 +#define TK_INSERT 322 +#define TK_INSTEAD 323 +#define TK_INT 324 +#define TK_INTEGER 325 +#define TK_INTERSECT 326 +#define TK_INTO 327 +#define TK_IS 328 +#define TK_ISNULL 329 +#define TK_JOIN 330 +#define TK_JOIN_KW 331 +#define TK_KEY 332 +#define TK_LE 333 +#define TK_LIKE 334 +#define TK_LIMIT 335 +#define TK_LONG 336 +#define TK_LONGCHAR 337 +#define TK_LP 338 +#define TK_LSHIFT 339 +#define TK_LT 340 +#define TK_LOCALIZABLE 341 +#define TK_MATCH 342 +#define TK_MINUS 343 +#define TK_NE 344 +#define TK_NOT 345 +#define TK_NOTNULL 346 +#define TK_NULL 347 +#define TK_OBJECT 348 +#define TK_OF 349 +#define TK_OFFSET 350 +#define TK_ON 351 +#define TK_OR 352 +#define TK_ORACLE_OUTER_JOIN 353 +#define TK_ORDER 354 +#define TK_PLUS 355 +#define TK_PRAGMA 356 +#define TK_PRIMARY 357 +#define TK_RAISE 358 +#define TK_REFERENCES 359 +#define TK_REM 360 +#define TK_REPLACE 361 +#define TK_RESTRICT 362 +#define TK_ROLLBACK 363 +#define TK_ROW 364 +#define TK_RP 365 +#define TK_RSHIFT 366 +#define TK_SELECT 367 +#define TK_SEMI 368 +#define TK_SET 369 +#define TK_SHORT 370 +#define TK_SLASH 371 +#define TK_SPACE 372 +#define TK_STAR 373 +#define TK_STATEMENT 374 +#define TK_STRING 375 +#define TK_TABLE 376 +#define TK_TEMP 377 +#define TK_THEN 378 +#define TK_TRANSACTION 379 +#define TK_TRIGGER 380 +#define TK_UMINUS 381 +#define TK_UNCLOSED_STRING 382 +#define TK_UNION 383 +#define TK_UNIQUE 384 +#define TK_UPDATE 385 +#define TK_UPLUS 386 +#define TK_USING 387 +#define TK_VACUUM 388 +#define TK_VALUES 389 +#define TK_VIEW 390 +#define TK_WHEN 391 +#define TK_WHERE 392 +#define TK_WILDCARD 393 +#define COLUMN 395 +#define FUNCTION 396 +#define COMMENT 397 +#define UNCLOSED_STRING 398 +#define SPACE 399 +#define ILLEGAL 400 +#define END_OF_FILE 401 + + + + +#if ! defined (YYSTYPE) && ! defined (YYSTYPE_IS_DECLARED) +#line 72 "./sql.y" +typedef union YYSTYPE { + struct sql_str str; + LPWSTR string; + column_info *column_list; + MSIVIEW *query; + struct expr *expr; + USHORT column_type; + int integer; +} YYSTYPE; +/* Line 1275 of yacc.c. */ +#line 337 "sql.tab.h" +# define yystype YYSTYPE /* obsolescent; will be withdrawn */ +# define YYSTYPE_IS_DECLARED 1 +# define YYSTYPE_IS_TRIVIAL 1 +#endif + + + + + diff --git a/reactos/dll/msi/sql.y b/reactos/dll/msi/sql.y new file mode 100644 index 00000000000..6669441c2b4 --- /dev/null +++ b/reactos/dll/msi/sql.y @@ -0,0 +1,819 @@ +%{ + +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002-2004 Mike McCormack 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + + +#include "config.h" + +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "query.h" +#include "wine/list.h" +#include "wine/debug.h" + +#define YYLEX_PARAM info +#define YYPARSE_PARAM info + +extern int SQL_error(const char *str); + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +typedef struct tag_SQL_input +{ + MSIDATABASE *db; + LPCWSTR command; + DWORD n, len; + MSIVIEW **view; /* view structure for the resulting query */ + struct list *mem; +} SQL_input; + +static LPWSTR SQL_getstring( void *info, struct sql_str *str ); +static INT SQL_getint( void *info ); +static int SQL_lex( void *SQL_lval, SQL_input *info ); + +static void *parser_alloc( void *info, unsigned int sz ); +static column_info *parser_alloc_column( void *info, LPCWSTR table, LPCWSTR column ); + +static BOOL SQL_MarkPrimaryKeys( column_info *cols, column_info *keys); + +static struct expr * EXPR_complex( void *info, struct expr *l, UINT op, struct expr *r ); +static struct expr * EXPR_column( void *info, column_info *column ); +static struct expr * EXPR_ival( void *info, int val ); +static struct expr * EXPR_sval( void *info, struct sql_str * ); +static struct expr * EXPR_wildcard( void *info ); + +%} + +%pure-parser + +%union +{ + struct sql_str str; + LPWSTR string; + column_info *column_list; + MSIVIEW *query; + struct expr *expr; + USHORT column_type; + int integer; +} + +%token TK_ABORT TK_AFTER TK_AGG_FUNCTION TK_ALL TK_AND TK_AS TK_ASC +%token TK_BEFORE TK_BEGIN TK_BETWEEN TK_BITAND TK_BITNOT TK_BITOR TK_BY +%token TK_CASCADE TK_CASE TK_CHAR TK_CHECK TK_CLUSTER TK_COLLATE TK_COLUMN +%token TK_COMMA TK_COMMENT TK_COMMIT TK_CONCAT TK_CONFLICT +%token TK_CONSTRAINT TK_COPY TK_CREATE +%token TK_DEFAULT TK_DEFERRABLE TK_DEFERRED TK_DELETE TK_DELIMITERS TK_DESC +%token TK_DISTINCT TK_DOT TK_DROP TK_EACH +%token TK_ELSE TK_END TK_END_OF_FILE TK_EQ TK_EXCEPT TK_EXPLAIN +%token TK_FAIL TK_FLOAT TK_FOR TK_FOREIGN TK_FROM TK_FUNCTION +%token TK_GE TK_GLOB TK_GROUP TK_GT +%token TK_HAVING TK_HOLD +%token TK_IGNORE TK_ILLEGAL TK_IMMEDIATE TK_IN TK_INDEX TK_INITIALLY +%token TK_ID +%token TK_INSERT TK_INSTEAD TK_INT +%token TK_INTEGER +%token TK_INTERSECT TK_INTO TK_IS +%token TK_ISNULL +%token TK_JOIN TK_JOIN_KW +%token TK_KEY +%token TK_LE TK_LIKE TK_LIMIT TK_LONG TK_LONGCHAR TK_LP TK_LSHIFT TK_LT +%token TK_LOCALIZABLE +%token TK_MATCH TK_MINUS +%token TK_NE TK_NOT TK_NOTNULL TK_NULL +%token TK_OBJECT TK_OF TK_OFFSET TK_ON TK_OR TK_ORACLE_OUTER_JOIN TK_ORDER +%token TK_PLUS TK_PRAGMA TK_PRIMARY +%token TK_RAISE TK_REFERENCES TK_REM TK_REPLACE TK_RESTRICT TK_ROLLBACK +%token TK_ROW TK_RP TK_RSHIFT +%token TK_SELECT TK_SEMI TK_SET TK_SHORT TK_SLASH TK_SPACE TK_STAR TK_STATEMENT +%token TK_STRING +%token TK_TABLE TK_TEMP TK_THEN TK_TRANSACTION TK_TRIGGER +%token TK_UMINUS TK_UNCLOSED_STRING TK_UNION TK_UNIQUE +%token TK_UPDATE TK_UPLUS TK_USING +%token TK_VACUUM TK_VALUES TK_VIEW +%token TK_WHEN TK_WHERE TK_WILDCARD + +/* + * These are extra tokens used by the lexer but never seen by the + * parser. We put them in a rule so that the parser generator will + * add them to the parse.h output file. + * + */ +%nonassoc END_OF_FILE ILLEGAL SPACE UNCLOSED_STRING COMMENT FUNCTION + COLUMN AGG_FUNCTION. + +%type table id +%type selcollist column column_and_type column_def table_def +%type column_assignment update_assign_list constlist +%type query from fromtable selectfrom unorderedsel +%type oneupdate onedelete oneselect onequery onecreate oneinsert +%type expr val column_val const_val +%type column_type data_type data_type_l data_count +%type number + +%% + +query: + onequery + { + SQL_input* sql = (SQL_input*) info; + *sql->view = $1; + } + ; + +onequery: + oneselect + | onecreate + | oneinsert + | oneupdate + | onedelete + ; + +oneinsert: + TK_INSERT TK_INTO table TK_LP selcollist TK_RP TK_VALUES TK_LP constlist TK_RP + { + SQL_input *sql = (SQL_input*) info; + MSIVIEW *insert = NULL; + UINT r; + + r = INSERT_CreateView( sql->db, &insert, $3, $5, $9, FALSE ); + if( !insert ) + YYABORT; + $$ = insert; + } + | TK_INSERT TK_INTO table TK_LP selcollist TK_RP TK_VALUES TK_LP constlist TK_RP TK_TEMP + { + SQL_input *sql = (SQL_input*) info; + MSIVIEW *insert = NULL; + + INSERT_CreateView( sql->db, &insert, $3, $5, $9, TRUE ); + if( !insert ) + YYABORT; + $$ = insert; + } + ; + +onecreate: + TK_CREATE TK_TABLE table TK_LP table_def TK_RP + { + SQL_input* sql = (SQL_input*) info; + MSIVIEW *create = NULL; + + if( !$5 ) + YYABORT; + CREATE_CreateView( sql->db, &create, $3, $5, FALSE ); + if( !create ) + YYABORT; + $$ = create; + } + | TK_CREATE TK_TABLE table TK_LP table_def TK_RP TK_HOLD + { + SQL_input* sql = (SQL_input*) info; + MSIVIEW *create = NULL; + + if( !$5 ) + YYABORT; + CREATE_CreateView( sql->db, &create, $3, $5, TRUE ); + if( !create ) + YYABORT; + $$ = create; + } + ; + +oneupdate: + TK_UPDATE table TK_SET update_assign_list TK_WHERE expr + { + SQL_input* sql = (SQL_input*) info; + MSIVIEW *update = NULL; + + UPDATE_CreateView( sql->db, &update, $2, $4, $6 ); + if( !update ) + YYABORT; + $$ = update; + } + ; + +onedelete: + TK_DELETE from + { + SQL_input* sql = (SQL_input*) info; + MSIVIEW *delete = NULL; + + DELETE_CreateView( sql->db, &delete, $2 ); + if( !delete ) + YYABORT; + $$ = delete; + } + ; + +table_def: + column_def TK_PRIMARY TK_KEY selcollist + { + if( SQL_MarkPrimaryKeys( $1, $4 ) ) + $$ = $1; + else + $$ = NULL; + } + ; + +column_def: + column_def TK_COMMA column_and_type + { + column_info *ci; + + for( ci = $1; ci->next; ci = ci->next ) + ; + + ci->next = $3; + $$ = $1; + } + | column_and_type + { + $$ = $1; + } + ; + +column_and_type: + column column_type + { + $$ = $1; + $$->type = $2 | MSITYPE_VALID; + } + ; + +column_type: + data_type_l + { + $$ = $1; + } + | data_type_l TK_LOCALIZABLE + { + $$ = $1 | MSITYPE_LOCALIZABLE; + } + ; + +data_type_l: + data_type + { + $$ |= MSITYPE_NULLABLE; + } + | data_type TK_NOT TK_NULL + { + $$ = $1; + } + ; + +data_type: + TK_CHAR + { + $$ = MSITYPE_STRING | 1; + } + | TK_CHAR TK_LP data_count TK_RP + { + $$ = MSITYPE_STRING | 0x400 | $3; + } + | TK_LONGCHAR + { + $$ = 2; + } + | TK_SHORT + { + $$ = 2; + } + | TK_INT + { + $$ = 2; + } + | TK_LONG + { + $$ = 4; + } + | TK_OBJECT + { + $$ = MSITYPE_STRING | MSITYPE_VALID; + } + ; + +data_count: + number + { + if( ( $1 > 255 ) || ( $1 < 0 ) ) + YYABORT; + $$ = $1; + } + ; + +oneselect: + unorderedsel TK_ORDER TK_BY selcollist + { + SQL_input* sql = (SQL_input*) info; + + $$ = NULL; + if( $4 ) + ORDER_CreateView( sql->db, &$$, $1, $4 ); + else + $$ = $1; + if( !$$ ) + YYABORT; + } + | unorderedsel + ; + +unorderedsel: + TK_SELECT selectfrom + { + $$ = $2; + } + | TK_SELECT TK_DISTINCT selectfrom + { + SQL_input* sql = (SQL_input*) info; + UINT r; + + $$ = NULL; + r = DISTINCT_CreateView( sql->db, &$$, $3 ); + if (r != ERROR_SUCCESS) + { + $3->ops->delete($3); + YYABORT; + } + } + ; + +selectfrom: + selcollist from + { + SQL_input* sql = (SQL_input*) info; + UINT r; + + $$ = NULL; + if( $1 ) + { + r = SELECT_CreateView( sql->db, &$$, $2, $1 ); + if (r != ERROR_SUCCESS) + { + $2->ops->delete($2); + YYABORT; + } + } + else + $$ = $2; + } + ; + +selcollist: + column + | column TK_COMMA selcollist + { + $1->next = $3; + } + | TK_STAR + { + $$ = NULL; + } + ; + +from: + fromtable + | fromtable TK_WHERE expr + { + SQL_input* sql = (SQL_input*) info; + UINT r; + + $$ = NULL; + r = WHERE_CreateView( sql->db, &$$, $1, $3 ); + if( r != ERROR_SUCCESS ) + { + $1->ops->delete( $1 ); + YYABORT; + } + } + ; + +fromtable: + TK_FROM table + { + SQL_input* sql = (SQL_input*) info; + UINT r; + + $$ = NULL; + r = TABLE_CreateView( sql->db, $2, &$$ ); + if( r != ERROR_SUCCESS || !$$ ) + YYABORT; + } + ; + +expr: + TK_LP expr TK_RP + { + $$ = $2; + if( !$$ ) + YYABORT; + } + | column_val TK_EQ column_val + { + $$ = EXPR_complex( info, $1, OP_EQ, $3 ); + if( !$$ ) + YYABORT; + } + | expr TK_AND expr + { + $$ = EXPR_complex( info, $1, OP_AND, $3 ); + if( !$$ ) + YYABORT; + } + | expr TK_OR expr + { + $$ = EXPR_complex( info, $1, OP_OR, $3 ); + if( !$$ ) + YYABORT; + } + | column_val TK_EQ val + { + $$ = EXPR_complex( info, $1, OP_EQ, $3 ); + if( !$$ ) + YYABORT; + } + | column_val TK_GT val + { + $$ = EXPR_complex( info, $1, OP_GT, $3 ); + if( !$$ ) + YYABORT; + } + | column_val TK_LT val + { + $$ = EXPR_complex( info, $1, OP_LT, $3 ); + if( !$$ ) + YYABORT; + } + | column_val TK_LE val + { + $$ = EXPR_complex( info, $1, OP_LE, $3 ); + if( !$$ ) + YYABORT; + } + | column_val TK_GE val + { + $$ = EXPR_complex( info, $1, OP_GE, $3 ); + if( !$$ ) + YYABORT; + } + | column_val TK_NE val + { + $$ = EXPR_complex( info, $1, OP_NE, $3 ); + if( !$$ ) + YYABORT; + } + | column_val TK_IS TK_NULL + { + $$ = EXPR_complex( info, $1, OP_ISNULL, NULL ); + if( !$$ ) + YYABORT; + } + | column_val TK_IS TK_NOT TK_NULL + { + $$ = EXPR_complex( info, $1, OP_NOTNULL, NULL ); + if( !$$ ) + YYABORT; + } + ; + +val: + column_val + | const_val + ; + +constlist: + const_val + { + $$ = parser_alloc_column( info, NULL, NULL ); + if( !$$ ) + YYABORT; + $$->val = $1; + } + | const_val TK_COMMA constlist + { + $$ = parser_alloc_column( info, NULL, NULL ); + if( !$$ ) + YYABORT; + $$->val = $1; + $$->next = $3; + } + ; + +update_assign_list: + column_assignment + | column_assignment TK_COMMA update_assign_list + { + $$ = $1; + $$->next = $3; + } + ; + +column_assignment: + column TK_EQ const_val + { + $$ = $1; + $$->val = $3; + } + ; + +const_val: + number + { + $$ = EXPR_ival( info, $1 ); + if( !$$ ) + YYABORT; + } + | TK_MINUS number + { + $$ = EXPR_ival( info, -$2 ); + if( !$$ ) + YYABORT; + } + | TK_STRING + { + $$ = EXPR_sval( info, &$1 ); + if( !$$ ) + YYABORT; + } + | TK_WILDCARD + { + $$ = EXPR_wildcard( info ); + if( !$$ ) + YYABORT; + } + ; + +column_val: + column + { + $$ = EXPR_column( info, $1 ); + if( !$$ ) + YYABORT; + } + ; + +column: + table TK_DOT id + { + $$ = parser_alloc_column( info, $1, $3 ); + if( !$$ ) + YYABORT; + } + | id + { + $$ = parser_alloc_column( info, NULL, $1 ); + if( !$$ ) + YYABORT; + } + ; + +table: + id + { + $$ = $1; + } + ; + +id: + TK_ID + { + $$ = SQL_getstring( info, &$1 ); + if( !$$ ) + YYABORT; + } + ; + +number: + TK_INTEGER + { + $$ = SQL_getint( info ); + } + ; + +%% + +static void *parser_alloc( void *info, unsigned int sz ) +{ + SQL_input* sql = (SQL_input*) info; + struct list *mem; + + mem = msi_alloc( sizeof (struct list) + sz ); + list_add_tail( sql->mem, mem ); + return &mem[1]; +} + +static column_info *parser_alloc_column( void *info, LPCWSTR table, LPCWSTR column ) +{ + column_info *col; + + col = parser_alloc( info, sizeof (*col) ); + if( col ) + { + col->table = table; + col->column = column; + col->val = NULL; + col->type = 0; + col->next = NULL; + } + + return col; +} + +int SQL_lex( void *SQL_lval, SQL_input *sql ) +{ + int token; + struct sql_str * str = SQL_lval; + + do + { + sql->n += sql->len; + if( ! sql->command[sql->n] ) + return 0; /* end of input */ + + /* TRACE("string : %s\n", debugstr_w(&sql->command[sql->n])); */ + sql->len = sqliteGetToken( &sql->command[sql->n], &token ); + if( sql->len==0 ) + break; + str->data = &sql->command[sql->n]; + str->len = sql->len; + } + while( token == TK_SPACE ); + + /* TRACE("token : %d (%s)\n", token, debugstr_wn(&sql->command[sql->n], sql->len)); */ + + return token; +} + +LPWSTR SQL_getstring( void *info, struct sql_str *strdata ) +{ + LPCWSTR p = strdata->data; + UINT len = strdata->len; + LPWSTR str; + + /* if there's quotes, remove them */ + if( ( (p[0]=='`') && (p[len-1]=='`') ) || + ( (p[0]=='\'') && (p[len-1]=='\'') ) ) + { + p++; + len -= 2; + } + str = parser_alloc( info, (len + 1)*sizeof(WCHAR) ); + if( !str ) + return str; + memcpy( str, p, len*sizeof(WCHAR) ); + str[len]=0; + + return str; +} + +INT SQL_getint( void *info ) +{ + SQL_input* sql = (SQL_input*) info; + LPCWSTR p = &sql->command[sql->n]; + INT i, r = 0; + + for( i=0; ilen; i++ ) + { + if( '0' > p[i] || '9' < p[i] ) + { + ERR("should only be numbers here!\n"); + break; + } + r = (p[i]-'0') + r*10; + } + + return r; +} + +int SQL_error( const char *str ) +{ + return 0; +} + +static struct expr * EXPR_wildcard( void *info ) +{ + struct expr *e = parser_alloc( info, sizeof *e ); + if( e ) + { + e->type = EXPR_WILDCARD; + } + return e; +} + +static struct expr * EXPR_complex( void *info, struct expr *l, UINT op, struct expr *r ) +{ + struct expr *e = parser_alloc( info, sizeof *e ); + if( e ) + { + e->type = EXPR_COMPLEX; + e->u.expr.left = l; + e->u.expr.op = op; + e->u.expr.right = r; + } + return e; +} + +static struct expr * EXPR_column( void *info, column_info *column ) +{ + struct expr *e = parser_alloc( info, sizeof *e ); + if( e ) + { + e->type = EXPR_COLUMN; + e->u.sval = column->column; + } + return e; +} + +static struct expr * EXPR_ival( void *info, int val ) +{ + struct expr *e = parser_alloc( info, sizeof *e ); + if( e ) + { + e->type = EXPR_IVAL; + e->u.ival = val; + } + return e; +} + +static struct expr * EXPR_sval( void *info, struct sql_str *str ) +{ + struct expr *e = parser_alloc( info, sizeof *e ); + if( e ) + { + e->type = EXPR_SVAL; + e->u.sval = SQL_getstring( info, str ); + } + return e; +} + +static BOOL SQL_MarkPrimaryKeys( column_info *cols, + column_info *keys ) +{ + column_info *k; + BOOL found = TRUE; + + for( k = keys; k && found; k = k->next ) + { + column_info *c; + + found = FALSE; + for( c = cols; c && !found; c = c->next ) + { + if( lstrcmpW( k->column, c->column ) ) + continue; + c->type |= MSITYPE_KEY; + found = TRUE; + } + } + + return found; +} + +UINT MSI_ParseSQL( MSIDATABASE *db, LPCWSTR command, MSIVIEW **phview, + struct list *mem ) +{ + SQL_input sql; + int r; + + *phview = NULL; + + sql.db = db; + sql.command = command; + sql.n = 0; + sql.len = 0; + sql.view = phview; + sql.mem = mem; + + r = SQL_parse(&sql); + + TRACE("Parse returned %d\n", r); + if( r ) + { + *sql.view = NULL; + return ERROR_BAD_QUERY_SYNTAX; + } + + return ERROR_SUCCESS; +} diff --git a/reactos/dll/msi/string.c b/reactos/dll/msi/string.c new file mode 100644 index 00000000000..a5ec138b987 --- /dev/null +++ b/reactos/dll/msi/string.c @@ -0,0 +1,456 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002-2004, Mike McCormack 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "wine/unicode.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" +#include "winnls.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msidb); + +typedef struct _msistring +{ + UINT hash; + UINT refcount; + LPWSTR str; +} msistring; + +struct string_table +{ + UINT maxcount; /* the number of strings */ + UINT freeslot; + UINT codepage; + msistring *strings; /* an array of strings (in the tree) */ +}; + +static UINT msistring_makehash( const WCHAR *str ) +{ + UINT hash = 0; + + if (str==NULL) + return hash; + + while( *str ) + { + hash ^= *str++; + hash *= 53; + hash = (hash<<5) | (hash>>27); + } + return hash; +} + +string_table *msi_init_stringtable( int entries, UINT codepage ) +{ + string_table *st; + + st = msi_alloc( sizeof (string_table) ); + if( !st ) + return NULL; + if( entries < 1 ) + entries = 1; + st->strings = msi_alloc_zero( sizeof (msistring) * entries ); + if( !st ) + { + msi_free( st ); + return NULL; + } + st->maxcount = entries; + st->freeslot = 1; + st->codepage = codepage; + + return st; +} + +VOID msi_destroy_stringtable( string_table *st ) +{ + UINT i; + + for( i=0; imaxcount; i++ ) + { + if( st->strings[i].refcount ) + msi_free( st->strings[i].str ); + } + msi_free( st->strings ); + msi_free( st ); +} + +static int st_find_free_entry( string_table *st ) +{ + UINT i, sz; + msistring *p; + + TRACE("%p\n", st); + + if( st->freeslot ) + { + for( i = st->freeslot; i < st->maxcount; i++ ) + if( !st->strings[i].refcount ) + return i; + } + for( i = 1; i < st->maxcount; i++ ) + if( !st->strings[i].refcount ) + return i; + + /* dynamically resize */ + sz = st->maxcount + 1 + st->maxcount/2; + p = msi_realloc_zero( st->strings, sz*sizeof(msistring) ); + if( !p ) + return -1; + st->strings = p; + st->freeslot = st->maxcount; + st->maxcount = sz; + if( st->strings[st->freeslot].refcount ) + ERR("oops. expected freeslot to be free...\n"); + return st->freeslot; +} + +static void st_mark_entry_used( string_table *st, UINT n ) +{ + if( n >= st->maxcount ) + return; + st->freeslot = n + 1; +} + +int msi_addstring( string_table *st, UINT n, const CHAR *data, int len, UINT refcount ) +{ + int sz; + + if( !data ) + return 0; + if( !data[0] ) + return 0; + if( n > 0 ) + { + if( st->strings[n].refcount ) + return -1; + } + else + { + if( ERROR_SUCCESS == msi_string2idA( st, data, &n ) ) + { + st->strings[n].refcount++; + return n; + } + n = st_find_free_entry( st ); + if( n < 0 ) + return -1; + } + + if( n < 1 ) + { + ERR("invalid index adding %s (%d)\n", debugstr_a( data ), n ); + return -1; + } + + /* allocate a new string */ + if( len < 0 ) + len = strlen(data); + sz = MultiByteToWideChar( st->codepage, 0, data, len, NULL, 0 ); + st->strings[n].str = msi_alloc( (sz+1)*sizeof(WCHAR) ); + if( !st->strings[n].str ) + return -1; + MultiByteToWideChar( st->codepage, 0, data, len, st->strings[n].str, sz ); + st->strings[n].str[sz] = 0; + st->strings[n].refcount = 1; + st->strings[n].hash = msistring_makehash( st->strings[n].str ); + + st_mark_entry_used( st, n ); + + return n; +} + +int msi_addstringW( string_table *st, UINT n, const WCHAR *data, int len, UINT refcount ) +{ + /* TRACE("[%2d] = %s\n", string_no, debugstr_an(data,len) ); */ + + if( !data ) + return 0; + if( !data[0] ) + return 0; + if( n > 0 ) + { + if( st->strings[n].refcount ) + return -1; + } + else + { + if( ERROR_SUCCESS == msi_string2idW( st, data, &n ) ) + { + st->strings[n].refcount++; + return n; + } + n = st_find_free_entry( st ); + if( n < 0 ) + return -1; + } + + if( n < 1 ) + { + ERR("invalid index adding %s (%d)\n", debugstr_w( data ), n ); + return -1; + } + + /* allocate a new string */ + if(len<0) + len = strlenW(data); + TRACE("%s, n = %d len = %d\n", debugstr_w(data), n, len ); + + st->strings[n].str = msi_alloc( (len+1)*sizeof(WCHAR) ); + if( !st->strings[n].str ) + return -1; + TRACE("%d\n",__LINE__); + memcpy( st->strings[n].str, data, len*sizeof(WCHAR) ); + st->strings[n].str[len] = 0; + st->strings[n].refcount = 1; + st->strings[n].hash = msistring_makehash( st->strings[n].str ); + + st_mark_entry_used( st, n ); + + return n; +} + +/* find the string identified by an id - return null if there's none */ +const WCHAR *msi_string_lookup_id( string_table *st, UINT id ) +{ + static const WCHAR zero[] = { 0 }; + if( id == 0 ) + return zero; + + if( id >= st->maxcount ) + return NULL; + + if( id && !st->strings[id].refcount ) + return NULL; + + return st->strings[id].str; +} + +/* + * msi_id2stringW + * + * [in] st - pointer to the string table + * [in] id - id of the string to retrieve + * [out] buffer - destination of the string + * [in/out] sz - number of bytes available in the buffer on input + * number of bytes used on output + * + * The size includes the terminating nul character. Short buffers + * will be filled, but not nul terminated. + */ +UINT msi_id2stringW( string_table *st, UINT id, LPWSTR buffer, UINT *sz ) +{ + UINT len; + const WCHAR *str; + + TRACE("Finding string %d of %d\n", id, st->maxcount); + + str = msi_string_lookup_id( st, id ); + if( !str ) + return ERROR_FUNCTION_FAILED; + + len = strlenW( str ) + 1; + + if( !buffer ) + { + *sz = len; + return ERROR_SUCCESS; + } + + if( *sz < len ) + *sz = len; + memcpy( buffer, str, (*sz)*sizeof(WCHAR) ); + *sz = len; + + return ERROR_SUCCESS; +} + +/* + * msi_id2stringA + * + * [in] st - pointer to the string table + * [in] id - id of the string to retrieve + * [out] buffer - destination of the UTF8 string + * [in/out] sz - number of bytes available in the buffer on input + * number of bytes used on output + * + * The size includes the terminating nul character. Short buffers + * will be filled, but not nul terminated. + */ +UINT msi_id2stringA( string_table *st, UINT id, LPSTR buffer, UINT *sz ) +{ + UINT len; + const WCHAR *str; + int n; + + TRACE("Finding string %d of %d\n", id, st->maxcount); + + str = msi_string_lookup_id( st, id ); + if( !str ) + return ERROR_FUNCTION_FAILED; + + len = WideCharToMultiByte( st->codepage, 0, str, -1, NULL, 0, NULL, NULL ); + + if( !buffer ) + { + *sz = len; + return ERROR_SUCCESS; + } + + if( len > *sz ) + { + n = strlenW( str ) + 1; + while( n && (len > *sz) ) + len = WideCharToMultiByte( st->codepage, 0, + str, --n, NULL, 0, NULL, NULL ); + } + else + n = -1; + + *sz = WideCharToMultiByte( st->codepage, 0, str, n, buffer, len, NULL, NULL ); + + return ERROR_SUCCESS; +} + +/* + * msi_string2idW + * + * [in] st - pointer to the string table + * [in] str - string to find in the string table + * [out] id - id of the string, if found + */ +UINT msi_string2idW( string_table *st, LPCWSTR str, UINT *id ) +{ + UINT hash; + UINT i, r = ERROR_INVALID_PARAMETER; + + hash = msistring_makehash( str ); + for( i=0; imaxcount; i++ ) + { + if ( (str == NULL && st->strings[i].str == NULL) || + ( ( st->strings[i].hash == hash ) && + !strcmpW( st->strings[i].str, str ) )) + { + r = ERROR_SUCCESS; + *id = i; + break; + } + } + + return r; +} + +UINT msi_string2idA( string_table *st, LPCSTR buffer, UINT *id ) +{ + DWORD sz; + UINT r = ERROR_INVALID_PARAMETER; + LPWSTR str; + + TRACE("Finding string %s in string table\n", debugstr_a(buffer) ); + + if( buffer[0] == 0 ) + { + *id = 0; + return ERROR_SUCCESS; + } + + sz = MultiByteToWideChar( st->codepage, 0, buffer, -1, NULL, 0 ); + if( sz <= 0 ) + return r; + str = msi_alloc( sz*sizeof(WCHAR) ); + if( !str ) + return ERROR_NOT_ENOUGH_MEMORY; + MultiByteToWideChar( st->codepage, 0, buffer, -1, str, sz ); + + r = msi_string2idW( st, str, id ); + msi_free( str ); + + return r; +} + +UINT msi_strcmp( string_table *st, UINT lval, UINT rval, UINT *res ) +{ + const WCHAR *l_str, *r_str; + + l_str = msi_string_lookup_id( st, lval ); + if( !l_str ) + return ERROR_INVALID_PARAMETER; + + r_str = msi_string_lookup_id( st, rval ); + if( !r_str ) + return ERROR_INVALID_PARAMETER; + + /* does this do the right thing for all UTF-8 strings? */ + *res = strcmpW( l_str, r_str ); + + return ERROR_SUCCESS; +} + +UINT msi_string_count( string_table *st ) +{ + return st->maxcount; +} + +UINT msi_id_refcount( string_table *st, UINT i ) +{ + if( i >= st->maxcount ) + return 0; + return st->strings[i].refcount; +} + +UINT msi_string_totalsize( string_table *st, UINT *total ) +{ + UINT size = 0, i, len; + + if( st->strings[0].str || st->strings[0].refcount ) + ERR("oops. element 0 has a string\n"); + *total = 0; + for( i=1; imaxcount; i++ ) + { + if( st->strings[i].str ) + { + TRACE("[%u] = %s\n", i, debugstr_w(st->strings[i].str)); + len = WideCharToMultiByte( st->codepage, 0, + st->strings[i].str, -1, NULL, 0, NULL, NULL); + if( len ) + len--; + size += len; + *total = (i+1); + } + } + TRACE("%u/%u strings %u bytes codepage %x\n", *total, st->maxcount, size, st->codepage ); + return size; +} + +UINT msi_string_get_codepage( string_table *st ) +{ + return st->codepage; +} diff --git a/reactos/dll/msi/suminfo.c b/reactos/dll/msi/suminfo.c new file mode 100644 index 00000000000..8a5d973a306 --- /dev/null +++ b/reactos/dll/msi/suminfo.c @@ -0,0 +1,761 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002, 2005 Mike McCormack 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#define COBJMACROS +#define NONAMELESSUNION + +#define PRSPEC_PROPID (1) + +#include "windef.h" +#include "winbase.h" +#include "winreg.h" +#include "winnls.h" +#include "shlwapi.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "msidefs.h" +#include "msipriv.h" +#include "objidl.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +#include "pshpack1.h" + +typedef struct { + WORD wByteOrder; + WORD wFormat; + DWORD dwOSVer; + CLSID clsID; + DWORD reserved; +} PROPERTYSETHEADER; + +typedef struct { + FMTID fmtid; + DWORD dwOffset; +} FORMATIDOFFSET; + +typedef struct { + DWORD cbSection; + DWORD cProperties; +} PROPERTYSECTIONHEADER; + +typedef struct { + DWORD propid; + DWORD dwOffset; +} PROPERTYIDOFFSET; + +typedef struct { + DWORD type; + union { + INT i4; + SHORT i2; + FILETIME ft; + struct { + DWORD len; + BYTE str[1]; + } str; + } u; +} PROPERTY_DATA; + +#include "poppack.h" + +#define SECT_HDR_SIZE (sizeof(PROPERTYSECTIONHEADER)) + +static const WCHAR szSumInfo[] = { 5 ,'S','u','m','m','a','r','y', + 'I','n','f','o','r','m','a','t','i','o','n',0 }; + +static void free_prop( PROPVARIANT *prop ) +{ + if (prop->vt == VT_LPSTR ) + msi_free( prop->u.pszVal ); + prop->vt = VT_EMPTY; +} + +static void MSI_CloseSummaryInfo( MSIOBJECTHDR *arg ) +{ + MSISUMMARYINFO *si = (MSISUMMARYINFO *) arg; + DWORD i; + + for( i = 0; i < MSI_MAX_PROPS; i++ ) + free_prop( &si->property[i] ); + msiobj_release( &si->db->hdr ); +} + +static UINT get_type( UINT uiProperty ) +{ + switch( uiProperty ) + { + case PID_CODEPAGE: + return VT_I2; + + case PID_SUBJECT: + case PID_AUTHOR: + case PID_KEYWORDS: + case PID_COMMENTS: + case PID_TEMPLATE: + case PID_LASTAUTHOR: + case PID_REVNUMBER: + case PID_APPNAME: + case PID_TITLE: + return VT_LPSTR; + + case PID_LASTPRINTED: + case PID_CREATE_DTM: + case PID_LASTSAVE_DTM: + return VT_FILETIME; + + case PID_WORDCOUNT: + case PID_CHARCOUNT: + case PID_SECURITY: + case PID_PAGECOUNT: + return VT_I4; + } + return VT_EMPTY; +} + +static UINT get_property_count( PROPVARIANT *property ) +{ + UINT i, n = 0; + + if( !property ) + return n; + for( i = 0; i < MSI_MAX_PROPS; i++ ) + if( property[i].vt != VT_EMPTY ) + n++; + return n; +} + +/* FIXME: doesn't deal with endian conversion */ +static void read_properties_from_data( PROPVARIANT *prop, LPBYTE data, DWORD sz ) +{ + UINT type; + DWORD i; + int size; + PROPERTY_DATA *propdata; + PROPVARIANT *property; + PROPERTYIDOFFSET *idofs; + PROPERTYSECTIONHEADER *section_hdr; + + section_hdr = (PROPERTYSECTIONHEADER*) &data[0]; + idofs = (PROPERTYIDOFFSET*) &data[SECT_HDR_SIZE]; + + /* now set all the properties */ + for( i = 0; i < section_hdr->cProperties; i++ ) + { + type = get_type( idofs[i].propid ); + if( type == VT_EMPTY ) + { + ERR("propid %ld has unknown type\n", idofs[i].propid); + break; + } + + propdata = (PROPERTY_DATA*) &data[ idofs[i].dwOffset ]; + + /* check the type is the same as we expect */ + if( type != propdata->type ) + { + ERR("wrong type %d != %ld\n", type, propdata->type); + break; + } + + /* check we don't run off the end of the data */ + size = sz - idofs[i].dwOffset - sizeof(DWORD); + if( sizeof(DWORD) > size || + ( type == VT_FILETIME && sizeof(FILETIME) > size ) || + ( type == VT_LPSTR && (propdata->u.str.len + sizeof(DWORD)) > size ) ) + { + ERR("not enough data\n"); + break; + } + + if( idofs[i].propid >= MSI_MAX_PROPS ) + { + ERR("Unknown property ID %ld\n", idofs[i].propid ); + break; + } + + property = &prop[ idofs[i].propid ]; + property->vt = type; + + if( type == VT_LPSTR ) + { + LPSTR str = msi_alloc( propdata->u.str.len ); + memcpy( str, propdata->u.str.str, propdata->u.str.len ); + str[ propdata->u.str.len - 1 ] = 0; + property->u.pszVal = str; + } + else if( type == VT_FILETIME ) + property->u.filetime = propdata->u.ft; + else if( type == VT_I2 ) + property->u.iVal = propdata->u.i2; + else if( type == VT_I4 ) + property->u.lVal = propdata->u.i4; + } +} + +static UINT load_summary_info( MSISUMMARYINFO *si, IStream *stm ) +{ + UINT ret = ERROR_FUNCTION_FAILED; + PROPERTYSETHEADER set_hdr; + FORMATIDOFFSET format_hdr; + PROPERTYSECTIONHEADER section_hdr; + LPBYTE data = NULL; + LARGE_INTEGER ofs; + ULONG count, sz; + HRESULT r; + + TRACE("%p %p\n", si, stm); + + /* read the header */ + sz = sizeof set_hdr; + r = IStream_Read( stm, &set_hdr, sz, &count ); + if( FAILED(r) || count != sz ) + return ret; + + if( set_hdr.wByteOrder != 0xfffe ) + { + ERR("property set not big-endian %04X\n", set_hdr.wByteOrder); + return ret; + } + + sz = sizeof format_hdr; + r = IStream_Read( stm, &format_hdr, sz, &count ); + if( FAILED(r) || count != sz ) + return ret; + + /* check the format id is correct */ + if( !IsEqualGUID( &FMTID_SummaryInformation, &format_hdr.fmtid ) ) + return ret; + + /* seek to the location of the section */ + ofs.QuadPart = format_hdr.dwOffset; + r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, NULL ); + if( FAILED(r) ) + return ret; + + /* read the section itself */ + sz = SECT_HDR_SIZE; + r = IStream_Read( stm, §ion_hdr, sz, &count ); + if( FAILED(r) || count != sz ) + return ret; + + if( section_hdr.cProperties > MSI_MAX_PROPS ) + { + ERR("too many properties %ld\n", section_hdr.cProperties); + return ret; + } + + data = msi_alloc( section_hdr.cbSection); + if( !data ) + return ret; + + memcpy( data, §ion_hdr, SECT_HDR_SIZE ); + + /* read all the data in one go */ + sz = section_hdr.cbSection - SECT_HDR_SIZE; + r = IStream_Read( stm, &data[ SECT_HDR_SIZE ], sz, &count ); + if( SUCCEEDED(r) && count == sz ) + read_properties_from_data( si->property, data, sz + SECT_HDR_SIZE ); + else + ERR("failed to read properties %ld %ld\n", count, sz); + + msi_free( data ); + return ret; +} + +static DWORD write_dword( LPBYTE data, DWORD ofs, DWORD val ) +{ + if( data ) + { + data[ofs++] = val&0xff; + data[ofs++] = (val>>8)&0xff; + data[ofs++] = (val>>16)&0xff; + data[ofs++] = (val>>24)&0xff; + } + return 4; +} + +static DWORD write_filetime( LPBYTE data, DWORD ofs, LPFILETIME ft ) +{ + write_dword( data, ofs, ft->dwLowDateTime ); + write_dword( data, ofs + 4, ft->dwHighDateTime ); + return 8; +} + +static DWORD write_string( LPBYTE data, DWORD ofs, LPCSTR str ) +{ + DWORD len = lstrlenA( str ) + 1; + write_dword( data, ofs, len ); + if( data ) + memcpy( &data[ofs + 4], str, len ); + return (7 + len) & ~3; +} + +static UINT write_property_to_data( PROPVARIANT *prop, LPBYTE data ) +{ + DWORD sz = 0; + + if( prop->vt == VT_EMPTY ) + return sz; + + /* add the type */ + sz += write_dword( data, sz, prop->vt ); + switch( prop->vt ) + { + case VT_I2: + sz += write_dword( data, sz, prop->u.iVal ); + break; + case VT_I4: + sz += write_dword( data, sz, prop->u.lVal ); + break; + case VT_FILETIME: + sz += write_filetime( data, sz, &prop->u.filetime ); + break; + case VT_LPSTR: + sz += write_string( data, sz, prop->u.pszVal ); + break; + } + return sz; +} + +static UINT save_summary_info( MSISUMMARYINFO * si, IStream *stm ) +{ + UINT ret = ERROR_FUNCTION_FAILED; + PROPERTYSETHEADER set_hdr; + FORMATIDOFFSET format_hdr; + PROPERTYSECTIONHEADER section_hdr; + PROPERTYIDOFFSET idofs[MSI_MAX_PROPS]; + LPBYTE data = NULL; + ULONG count, sz; + HRESULT r; + int i, n; + + /* write the header */ + sz = sizeof set_hdr; + memset( &set_hdr, 0, sz ); + set_hdr.wByteOrder = 0xfffe; + set_hdr.wFormat = 0; + set_hdr.dwOSVer = 0x00020005; /* build 5, platform id 2 */ + /* set_hdr.clsID is {00000000-0000-0000-0000-000000000000} */ + set_hdr.reserved = 1; + r = IStream_Write( stm, &set_hdr, sz, &count ); + if( FAILED(r) || count != sz ) + return ret; + + /* write the format header */ + sz = sizeof format_hdr; + memcpy( &format_hdr.fmtid, &FMTID_SummaryInformation, sizeof (FMTID) ); + format_hdr.dwOffset = sizeof format_hdr + sizeof set_hdr; + r = IStream_Write( stm, &format_hdr, sz, &count ); + if( FAILED(r) || count != sz ) + return ret; + + /* add up how much space the data will take and calculate the offsets */ + section_hdr.cbSection = sizeof section_hdr; + section_hdr.cbSection += (get_property_count( si->property ) * sizeof idofs[0]); + section_hdr.cProperties = 0; + n = 0; + for( i = 0; i < MSI_MAX_PROPS; i++ ) + { + sz = write_property_to_data( &si->property[i], NULL ); + if( !sz ) + continue; + idofs[ section_hdr.cProperties ].propid = i; + idofs[ section_hdr.cProperties ].dwOffset = section_hdr.cbSection; + section_hdr.cProperties++; + section_hdr.cbSection += sz; + } + + data = msi_alloc_zero( section_hdr.cbSection ); + + sz = 0; + memcpy( &data[sz], §ion_hdr, sizeof section_hdr ); + sz += sizeof section_hdr; + + memcpy( &data[sz], idofs, section_hdr.cProperties * sizeof idofs[0] ); + sz += section_hdr.cProperties * sizeof idofs[0]; + + /* write out the data */ + for( i = 0; i < MSI_MAX_PROPS; i++ ) + sz += write_property_to_data( &si->property[i], &data[sz] ); + + r = IStream_Write( stm, data, sz, &count ); + msi_free( data ); + if( FAILED(r) || count != sz ) + return ret; + + return ERROR_SUCCESS; +} + +MSISUMMARYINFO *MSI_GetSummaryInformationW( MSIDATABASE *db, UINT uiUpdateCount ) +{ + IStream *stm = NULL; + MSISUMMARYINFO *si; + DWORD grfMode; + HRESULT r; + + TRACE("%p %d\n", db, uiUpdateCount ); + + si = alloc_msiobject( MSIHANDLETYPE_SUMMARYINFO, + sizeof (MSISUMMARYINFO), MSI_CloseSummaryInfo ); + if( !si ) + return si; + + msiobj_addref( &db->hdr ); + si->db = db; + memset( &si->property, 0, sizeof si->property ); + si->update_count = uiUpdateCount; + + /* read the stream... if we fail, we'll start with an empty property set */ + grfMode = STGM_READ | STGM_SHARE_EXCLUSIVE; + r = IStorage_OpenStream( si->db->storage, szSumInfo, 0, grfMode, 0, &stm ); + if( SUCCEEDED(r) ) + { + load_summary_info( si, stm ); + IStream_Release( stm ); + } + + return si; +} + +UINT WINAPI MsiGetSummaryInformationW( MSIHANDLE hDatabase, + LPCWSTR szDatabase, UINT uiUpdateCount, MSIHANDLE *pHandle ) +{ + MSISUMMARYINFO *si; + MSIDATABASE *db; + UINT ret = ERROR_FUNCTION_FAILED; + + TRACE("%ld %s %d %p\n", hDatabase, debugstr_w(szDatabase), + uiUpdateCount, pHandle); + + if( !pHandle ) + return ERROR_INVALID_PARAMETER; + + if( szDatabase ) + { + ret = MSI_OpenDatabaseW( szDatabase, NULL, &db ); + if( ret != ERROR_SUCCESS ) + return ret; + } + else + { + db = msihandle2msiinfo( hDatabase, MSIHANDLETYPE_DATABASE ); + if( !db ) + return ERROR_INVALID_PARAMETER; + } + + si = MSI_GetSummaryInformationW( db, uiUpdateCount ); + if (si) + { + *pHandle = alloc_msihandle( &si->hdr ); + if( *pHandle ) + ret = ERROR_SUCCESS; + msiobj_release( &si->hdr ); + } + + if( db ) + msiobj_release( &db->hdr ); + + return ret; +} + +UINT WINAPI MsiGetSummaryInformationA(MSIHANDLE hDatabase, + LPCSTR szDatabase, UINT uiUpdateCount, MSIHANDLE *pHandle) +{ + LPWSTR szwDatabase = NULL; + UINT ret; + + TRACE("%ld %s %d %p\n", hDatabase, debugstr_a(szDatabase), + uiUpdateCount, pHandle); + + if( szDatabase ) + { + szwDatabase = strdupAtoW( szDatabase ); + if( !szwDatabase ) + return ERROR_FUNCTION_FAILED; + } + + ret = MsiGetSummaryInformationW(hDatabase, szwDatabase, uiUpdateCount, pHandle); + + msi_free( szwDatabase ); + + return ret; +} + +UINT WINAPI MsiSummaryInfoGetPropertyCount(MSIHANDLE hSummaryInfo, UINT *pCount) +{ + MSISUMMARYINFO *si; + + TRACE("%ld %p\n",hSummaryInfo, pCount); + + si = msihandle2msiinfo( hSummaryInfo, MSIHANDLETYPE_SUMMARYINFO ); + if( !si ) + return ERROR_INVALID_HANDLE; + + if( pCount ) + *pCount = get_property_count( si->property ); + msiobj_release( &si->hdr ); + + return ERROR_SUCCESS; +} + +static UINT get_prop( MSIHANDLE handle, UINT uiProperty, UINT *puiDataType, + INT *piValue, FILETIME *pftValue, awstring *str, DWORD *pcchValueBuf) +{ + MSISUMMARYINFO *si; + PROPVARIANT *prop; + UINT ret = ERROR_SUCCESS; + + TRACE("%ld %d %p %p %p %p %p\n", handle, uiProperty, puiDataType, + piValue, pftValue, str, pcchValueBuf); + + si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO ); + if( !si ) + return ERROR_INVALID_HANDLE; + + if ( uiProperty >= MSI_MAX_PROPS ) + { + *puiDataType = VT_EMPTY; + return ret; + } + + prop = &si->property[uiProperty]; + + if( puiDataType ) + *puiDataType = prop->vt; + + switch( prop->vt ) + { + case VT_I2: + if( piValue ) + *piValue = prop->u.iVal; + break; + case VT_I4: + if( piValue ) + *piValue = prop->u.lVal; + break; + case VT_LPSTR: + if( pcchValueBuf ) + { + DWORD len = 0; + + if( str->unicode ) + { + len = MultiByteToWideChar( CP_ACP, 0, prop->u.pszVal, -1, + str->str.w, *pcchValueBuf ); + len--; + } + else + { + len = lstrlenA( prop->u.pszVal ); + if( str->str.a ) + lstrcpynA(str->str.a, prop->u.pszVal, *pcchValueBuf ); + } + if (len >= *pcchValueBuf) + ret = ERROR_MORE_DATA; + *pcchValueBuf = len; + } + break; + case VT_FILETIME: + if( pftValue ) + memcpy(pftValue, &prop->u.filetime, sizeof (FILETIME) ); + break; + case VT_EMPTY: + break; + default: + FIXME("Unknown property variant type\n"); + break; + } + msiobj_release( &si->hdr ); + return ret; +} + +LPWSTR msi_suminfo_dup_string( MSISUMMARYINFO *si, UINT uiProperty ) +{ + PROPVARIANT *prop; + + if ( uiProperty >= MSI_MAX_PROPS ) + return NULL; + prop = &si->property[uiProperty]; + if( prop->vt != VT_LPSTR ) + return NULL; + return strdupAtoW( prop->u.pszVal ); +} + +UINT WINAPI MsiSummaryInfoGetPropertyA( + MSIHANDLE handle, UINT uiProperty, UINT *puiDataType, INT *piValue, + FILETIME *pftValue, LPSTR szValueBuf, DWORD *pcchValueBuf) +{ + awstring str; + + TRACE("%ld %d %p %p %p %p %p\n", handle, uiProperty, puiDataType, + piValue, pftValue, szValueBuf, pcchValueBuf ); + + str.unicode = FALSE; + str.str.a = szValueBuf; + + return get_prop( handle, uiProperty, puiDataType, piValue, + pftValue, &str, pcchValueBuf ); +} + +UINT WINAPI MsiSummaryInfoGetPropertyW( + MSIHANDLE handle, UINT uiProperty, UINT *puiDataType, INT *piValue, + FILETIME *pftValue, LPWSTR szValueBuf, DWORD *pcchValueBuf) +{ + awstring str; + + TRACE("%ld %d %p %p %p %p %p\n", handle, uiProperty, puiDataType, + piValue, pftValue, szValueBuf, pcchValueBuf ); + + str.unicode = TRUE; + str.str.w = szValueBuf; + + return get_prop( handle, uiProperty, puiDataType, piValue, + pftValue, &str, pcchValueBuf ); +} + +static UINT set_prop( MSIHANDLE handle, UINT uiProperty, UINT uiDataType, + INT iValue, FILETIME* pftValue, awcstring *str ) +{ + MSISUMMARYINFO *si; + PROPVARIANT *prop; + UINT type, len, ret = ERROR_SUCCESS; + + TRACE("%ld %u %u %i %p %p\n", handle, uiProperty, uiDataType, + iValue, pftValue, str ); + + type = get_type( uiProperty ); + if( type == VT_EMPTY || type != uiDataType ) + return ERROR_DATATYPE_MISMATCH; + + if( uiDataType == VT_LPSTR && !str->str.w ) + return ERROR_INVALID_PARAMETER; + + if( uiDataType == VT_FILETIME && !pftValue ) + return ERROR_INVALID_PARAMETER; + + si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO ); + if( !si ) + return ERROR_INVALID_HANDLE; + + prop = &si->property[uiProperty]; + + if( prop->vt == VT_EMPTY ) + { + if( !si->update_count ) + { + ret = ERROR_FUNCTION_FAILED; + goto end; + } + si->update_count--; + } + else if( prop->vt != type ) + goto end; + + free_prop( prop ); + prop->vt = type; + switch( type ) + { + case VT_I4: + prop->u.lVal = iValue; + break; + case VT_I2: + prop->u.iVal = iValue; + break; + case VT_FILETIME: + memcpy( &prop->u.filetime, pftValue, sizeof prop->u.filetime ); + break; + case VT_LPSTR: + if( str->unicode ) + { + len = WideCharToMultiByte( CP_ACP, 0, str->str.w, -1, + NULL, 0, NULL, NULL ); + prop->u.pszVal = msi_alloc( len ); + WideCharToMultiByte( CP_ACP, 0, str->str.w, -1, + prop->u.pszVal, len, NULL, NULL ); + } + else + { + len = lstrlenA( str->str.a ) + 1; + prop->u.pszVal = msi_alloc( len ); + lstrcpyA( prop->u.pszVal, str->str.a ); + } + break; + } + +end: + msiobj_release( &si->hdr ); + return ret; +} + +UINT WINAPI MsiSummaryInfoSetPropertyW( MSIHANDLE handle, UINT uiProperty, + UINT uiDataType, INT iValue, FILETIME* pftValue, LPCWSTR szValue ) +{ + awcstring str; + + TRACE("%ld %u %u %i %p %s\n", handle, uiProperty, uiDataType, + iValue, pftValue, debugstr_w(szValue) ); + + str.unicode = TRUE; + str.str.w = szValue; + return set_prop( handle, uiProperty, uiDataType, iValue, pftValue, &str ); +} + +UINT WINAPI MsiSummaryInfoSetPropertyA( MSIHANDLE handle, UINT uiProperty, + UINT uiDataType, INT iValue, FILETIME* pftValue, LPCSTR szValue ) +{ + awcstring str; + + TRACE("%ld %u %u %i %p %s\n", handle, uiProperty, uiDataType, + iValue, pftValue, debugstr_a(szValue) ); + + str.unicode = FALSE; + str.str.a = szValue; + return set_prop( handle, uiProperty, uiDataType, iValue, pftValue, &str ); +} + +UINT WINAPI MsiSummaryInfoPersist( MSIHANDLE handle ) +{ + IStream *stm = NULL; + MSISUMMARYINFO *si; + DWORD grfMode; + HRESULT r; + UINT ret = ERROR_FUNCTION_FAILED; + + TRACE("%ld\n", handle ); + + si = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO ); + if( !si ) + return ERROR_INVALID_HANDLE; + + grfMode = STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE; + r = IStorage_CreateStream( si->db->storage, szSumInfo, grfMode, 0, 0, &stm ); + if( SUCCEEDED(r) ) + { + ret = save_summary_info( si, stm ); + IStream_Release( stm ); + } + msiobj_release( &si->hdr ); + + return ret; +} diff --git a/reactos/dll/msi/table.c b/reactos/dll/msi/table.c new file mode 100644 index 00000000000..a3becc6770e --- /dev/null +++ b/reactos/dll/msi/table.c @@ -0,0 +1,1861 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002-2005 Mike McCormack 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#define COBJMACROS +#define NONAMELESSUNION +#define NONAMELESSSTRUCT + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" +#include "winnls.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msidb); + +typedef struct tagMSICOLUMNINFO +{ + LPCWSTR tablename; + UINT number; + LPCWSTR colname; + UINT type; + UINT offset; +} MSICOLUMNINFO; + +struct tagMSITABLE +{ + USHORT **data; + UINT row_count; + struct list entry; + WCHAR name[1]; +}; + +typedef struct tagMSITRANSFORM { + struct list entry; + IStorage *stg; +} MSITRANSFORM; + +#define MAX_STREAM_NAME 0x1f + +static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name, + MSICOLUMNINFO **pcols, UINT *pcount ); +static UINT get_tablecolumns( MSIDATABASE *db, + LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz); +static void msi_free_colinfo( MSICOLUMNINFO *colinfo, UINT count ); + +static inline UINT bytes_per_column( const MSICOLUMNINFO *col ) +{ + if( col->type & MSITYPE_STRING ) + return 2; + if( (col->type & 0xff) > 4 ) + ERR("Invalid column size!\n"); + return col->type & 0xff; +} + +static int utf2mime(int x) +{ + if( (x>='0') && (x<='9') ) + return x-'0'; + if( (x>='A') && (x<='Z') ) + return x-'A'+10; + if( (x>='a') && (x<='z') ) + return x-'a'+10+26; + if( x=='.' ) + return 10+26+26; + if( x=='_' ) + return 10+26+26+1; + return -1; +} + +static LPWSTR encode_streamname(BOOL bTable, LPCWSTR in) +{ + DWORD count = MAX_STREAM_NAME; + DWORD ch, next; + LPWSTR out, p; + + if( !bTable ) + count = lstrlenW( in )+2; + out = msi_alloc( count*sizeof(WCHAR) ); + p = out; + + if( bTable ) + { + *p++ = 0x4840; + count --; + } + while( count -- ) + { + ch = *in++; + if( !ch ) + { + *p = ch; + return out; + } + if( ( ch < 0x80 ) && ( utf2mime(ch) >= 0 ) ) + { + ch = utf2mime(ch) + 0x4800; + next = *in; + if( next && (next<0x80) ) + { + next = utf2mime(next); + if( next >= 0 ) + { + next += 0x3ffffc0; + ch += (next<<6); + in++; + } + } + } + *p++ = ch; + } + ERR("Failed to encode stream name (%s)\n",debugstr_w(in)); + msi_free( out ); + return NULL; +} + +static int mime2utf(int x) +{ + if( x<10 ) + return x + '0'; + if( x<(10+26)) + return x - 10 + 'A'; + if( x<(10+26+26)) + return x - 10 - 26 + 'a'; + if( x == (10+26+26) ) + return '.'; + return '_'; +} + +static BOOL decode_streamname(LPWSTR in, LPWSTR out) +{ + WCHAR ch; + DWORD count = 0; + + while ( (ch = *in++) ) + { + if( (ch >= 0x3800 ) && (ch < 0x4840 ) ) + { + if( ch >= 0x4800 ) + ch = mime2utf(ch-0x4800); + else + { + ch -= 0x3800; + *out++ = mime2utf(ch&0x3f); + count++; + ch = mime2utf((ch>>6)&0x3f); + } + } + *out++ = ch; + count++; + } + *out = 0; + return count; +} + +void enum_stream_names( IStorage *stg ) +{ + IEnumSTATSTG *stgenum = NULL; + HRESULT r; + STATSTG stat; + ULONG n, count; + WCHAR name[0x40]; + + r = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum ); + if( FAILED( r ) ) + return; + + n = 0; + while( 1 ) + { + count = 0; + r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count ); + if( FAILED( r ) || !count ) + break; + decode_streamname( stat.pwcsName, name ); + TRACE("stream %2ld -> %s %s\n", n, + debugstr_w(stat.pwcsName), debugstr_w(name) ); + n++; + } + + IEnumSTATSTG_Release( stgenum ); +} + +static UINT read_stream_data( IStorage *stg, LPCWSTR stname, + USHORT **pdata, UINT *psz ) +{ + HRESULT r; + UINT ret = ERROR_FUNCTION_FAILED; + VOID *data; + ULONG sz, count; + IStream *stm = NULL; + STATSTG stat; + LPWSTR encname; + + encname = encode_streamname(TRUE, stname); + + TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname)); + + r = IStorage_OpenStream(stg, encname, NULL, + STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm); + msi_free( encname ); + if( FAILED( r ) ) + { + WARN("open stream failed r = %08lx - empty table?\n",r); + return ret; + } + + r = IStream_Stat(stm, &stat, STATFLAG_NONAME ); + if( FAILED( r ) ) + { + WARN("open stream failed r = %08lx!\n",r); + goto end; + } + + if( stat.cbSize.QuadPart >> 32 ) + { + WARN("Too big!\n"); + goto end; + } + + sz = stat.cbSize.QuadPart; + data = msi_alloc( sz ); + if( !data ) + { + WARN("couldn't allocate memory r=%08lx!\n",r); + ret = ERROR_NOT_ENOUGH_MEMORY; + goto end; + } + + r = IStream_Read(stm, data, sz, &count ); + if( FAILED( r ) || ( count != sz ) ) + { + msi_free( data ); + WARN("read stream failed r = %08lx!\n",r); + goto end; + } + + *pdata = data; + *psz = sz; + ret = ERROR_SUCCESS; + +end: + IStream_Release( stm ); + + return ret; +} + +UINT db_get_raw_stream( MSIDATABASE *db, LPCWSTR stname, IStream **stm ) +{ + LPWSTR encname; + HRESULT r; + + encname = encode_streamname(FALSE, stname); + + TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname)); + + r = IStorage_OpenStream(db->storage, encname, NULL, + STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm); + if( FAILED( r ) ) + { + MSITRANSFORM *transform; + + LIST_FOR_EACH_ENTRY( transform, &db->transforms, MSITRANSFORM, entry ) + { + TRACE("looking for %s in transform storage\n", debugstr_w(stname) ); + r = IStorage_OpenStream( transform->stg, encname, NULL, + STGM_READ | STGM_SHARE_EXCLUSIVE, 0, stm ); + if (SUCCEEDED(r)) + break; + } + } + + msi_free( encname ); + + return SUCCEEDED(r) ? ERROR_SUCCESS : ERROR_FUNCTION_FAILED; +} + +UINT read_raw_stream_data( MSIDATABASE *db, LPCWSTR stname, + USHORT **pdata, UINT *psz ) +{ + HRESULT r; + UINT ret = ERROR_FUNCTION_FAILED; + VOID *data; + ULONG sz, count; + IStream *stm = NULL; + STATSTG stat; + + r = db_get_raw_stream( db, stname, &stm ); + if( r != ERROR_SUCCESS) + return ret; + r = IStream_Stat(stm, &stat, STATFLAG_NONAME ); + if( FAILED( r ) ) + { + WARN("open stream failed r = %08lx!\n",r); + goto end; + } + + if( stat.cbSize.QuadPart >> 32 ) + { + WARN("Too big!\n"); + goto end; + } + + sz = stat.cbSize.QuadPart; + data = msi_alloc( sz ); + if( !data ) + { + WARN("couldn't allocate memory r=%08lx!\n",r); + ret = ERROR_NOT_ENOUGH_MEMORY; + goto end; + } + + r = IStream_Read(stm, data, sz, &count ); + if( FAILED( r ) || ( count != sz ) ) + { + msi_free( data ); + WARN("read stream failed r = %08lx!\n",r); + goto end; + } + + *pdata = data; + *psz = sz; + ret = ERROR_SUCCESS; + +end: + IStream_Release( stm ); + + return ret; +} + +static UINT write_stream_data( IStorage *stg, LPCWSTR stname, + LPVOID data, UINT sz ) +{ + HRESULT r; + UINT ret = ERROR_FUNCTION_FAILED; + ULONG count; + IStream *stm = NULL; + ULARGE_INTEGER size; + LARGE_INTEGER pos; + LPWSTR encname; + + encname = encode_streamname(TRUE, stname ); + r = IStorage_OpenStream( stg, encname, NULL, + STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &stm); + if( FAILED(r) ) + { + r = IStorage_CreateStream( stg, encname, + STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm); + } + msi_free( encname ); + if( FAILED( r ) ) + { + WARN("open stream failed r = %08lx\n",r); + return ret; + } + + size.QuadPart = sz; + r = IStream_SetSize( stm, size ); + if( FAILED( r ) ) + { + WARN("Failed to SetSize\n"); + goto end; + } + + pos.QuadPart = 0; + r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL ); + if( FAILED( r ) ) + { + WARN("Failed to Seek\n"); + goto end; + } + + r = IStream_Write(stm, data, sz, &count ); + if( FAILED( r ) || ( count != sz ) ) + { + WARN("Failed to Write\n"); + goto end; + } + + ret = ERROR_SUCCESS; + +end: + IStream_Release( stm ); + + return ret; +} + +static void free_table( MSITABLE *table ) +{ + int i; + for( i=0; irow_count; i++ ) + msi_free( table->data[i] ); + msi_free( table->data ); + msi_free( table ); +} + +static UINT msi_table_get_row_size( const MSICOLUMNINFO *cols, UINT count ) +{ + const MSICOLUMNINFO *last_col = &cols[count-1]; + if (!count) + return 0; + return last_col->offset + bytes_per_column( last_col ); +} + +/* add this table to the list of cached tables in the database */ +static MSITABLE *read_table_from_storage( IStorage *stg, LPCWSTR name, + const MSICOLUMNINFO *cols, UINT num_cols ) +{ + MSITABLE *t; + USHORT *rawdata = NULL; + UINT rawsize = 0, i, j, row_size = 0; + + TRACE("%s\n",debugstr_w(name)); + + /* nonexistent tables should be interpreted as empty tables */ + t = msi_alloc( sizeof (MSITABLE) + lstrlenW(name)*sizeof (WCHAR) ); + if( !t ) + return t; + + row_size = msi_table_get_row_size( cols, num_cols ); + + t->row_count = 0; + t->data = NULL; + lstrcpyW( t->name, name ); + + /* if we can't read the table, just assume that it's empty */ + read_stream_data( stg, name, &rawdata, &rawsize ); + if( !rawdata ) + return t; + + TRACE("Read %d bytes\n", rawsize ); + + if( rawsize % row_size ) + { + WARN("Table size is invalid %d/%d\n", rawsize, row_size ); + goto err; + } + + t->row_count = rawsize / row_size; + t->data = msi_alloc_zero( t->row_count * sizeof (USHORT*) ); + if( !t->data ) + goto err; + + /* transpose all the data */ + TRACE("Transposing data from %d columns\n", t->row_count ); + for( i=0; irow_count; i++ ) + { + t->data[i] = msi_alloc( row_size ); + if( !t->data[i] ) + goto err; + + for( j=0; jdata[i][ofs] = rawdata[ofs*t->row_count + i ]; + break; + case 4: + t->data[i][ofs] = rawdata[ofs*t->row_count + i*2 ]; + t->data[i][ofs+1] = rawdata[ofs*t->row_count + i*2 + 1]; + break; + default: + ERR("oops - unknown column width %d\n", n); + goto err; + } + } + } + + msi_free( rawdata ); + return t; +err: + msi_free( rawdata ); + free_table( t ); + return NULL; +} + +void free_cached_tables( MSIDATABASE *db ) +{ + while( !list_empty( &db->tables ) ) + { + MSITABLE *t = LIST_ENTRY( list_head( &db->tables ), MSITABLE, entry ); + + list_remove( &t->entry ); + free_table( t ); + } +} + +static MSITABLE *find_cached_table( MSIDATABASE *db, LPCWSTR name ) +{ + MSITABLE *t; + + LIST_FOR_EACH_ENTRY( t, &db->tables, MSITABLE, entry ) + if( !lstrcmpW( name, t->name ) ) + return t; + + return NULL; +} + +static UINT table_get_column_info( MSIDATABASE *db, LPCWSTR name, MSICOLUMNINFO **pcols, UINT *pcount ) +{ + UINT r, column_count = 0; + MSICOLUMNINFO *columns; + + /* get the number of columns in this table */ + column_count = 0; + r = get_tablecolumns( db, name, NULL, &column_count ); + if( r != ERROR_SUCCESS ) + return r; + + /* if there's no columns, there's no table */ + if( column_count == 0 ) + return ERROR_INVALID_PARAMETER; + + TRACE("Table %s found\n", debugstr_w(name) ); + + columns = msi_alloc( column_count*sizeof (MSICOLUMNINFO) ); + if( !columns ) + return ERROR_FUNCTION_FAILED; + + r = get_tablecolumns( db, name, columns, &column_count ); + if( r != ERROR_SUCCESS ) + { + msi_free( columns ); + return ERROR_FUNCTION_FAILED; + } + + *pcols = columns; + *pcount = column_count; + + return r; +} + +static MSITABLE *get_table( MSIDATABASE *db, LPCWSTR name, + const MSICOLUMNINFO *cols, UINT num_cols ) +{ + MSITABLE *table; + + /* first, see if the table is cached */ + table = find_cached_table( db, name ); + if( table ) + return table; + + table = read_table_from_storage( db->storage, name, cols, num_cols ); + if( table ) + list_add_head( &db->tables, &table->entry ); + + return table; +} + +static UINT save_table( MSIDATABASE *db, MSITABLE *t ) +{ + USHORT *rawdata = NULL, *p; + UINT rawsize, r, i, j, row_size, num_cols = 0; + MSICOLUMNINFO *cols = NULL; + + TRACE("Saving %s\n", debugstr_w( t->name ) ); + + r = table_get_column_info( db, t->name, &cols, &num_cols ); + if( r != ERROR_SUCCESS ) + return r; + + row_size = msi_table_get_row_size( cols, num_cols ); + + rawsize = t->row_count * row_size; + rawdata = msi_alloc_zero( rawsize ); + if( !rawdata ) + { + r = ERROR_NOT_ENOUGH_MEMORY; + goto err; + } + + p = rawdata; + for( i=0; irow_count; j++ ) + { + UINT offset = cols[i].offset; + + *p++ = t->data[j][offset/2]; + if( 4 == bytes_per_column( &cols[i] ) ) + *p++ = t->data[j][offset/2+1]; + } + } + + TRACE("writing %d bytes\n", rawsize); + r = write_stream_data( db->storage, t->name, rawdata, rawsize ); + +err: + msi_free_colinfo( cols, num_cols ); + msi_free( cols ); + msi_free( rawdata ); + + return r; +} + +HRESULT init_string_table( IStorage *stg ) +{ + HRESULT r; + static const WCHAR szStringData[] = { + '_','S','t','r','i','n','g','D','a','t','a',0 }; + static const WCHAR szStringPool[] = { + '_','S','t','r','i','n','g','P','o','o','l',0 }; + USHORT zero[2] = { 0, 0 }; + ULONG count = 0; + IStream *stm = NULL; + LPWSTR encname; + + encname = encode_streamname(TRUE, szStringPool ); + + /* create the StringPool stream... add the zero string to it*/ + r = IStorage_CreateStream( stg, encname, + STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm); + msi_free( encname ); + if( r ) + { + TRACE("Failed\n"); + return r; + } + + r = IStream_Write(stm, zero, sizeof zero, &count ); + IStream_Release( stm ); + + if( FAILED( r ) || ( count != sizeof zero ) ) + { + TRACE("Failed\n"); + return E_FAIL; + } + + /* create the StringData stream... make it zero length */ + encname = encode_streamname(TRUE, szStringData ); + r = IStorage_CreateStream( stg, encname, + STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, 0, &stm); + msi_free( encname ); + if( r ) + { + TRACE("Failed\n"); + return E_FAIL; + } + IStream_Release( stm ); + + return r; +} + +string_table *load_string_table( IStorage *stg ) +{ + string_table *st = NULL; + CHAR *data; + USHORT *pool; + UINT r, datasize = 0, poolsize = 0, codepage; + DWORD i, count, offset, len, n; + static const WCHAR szStringData[] = { + '_','S','t','r','i','n','g','D','a','t','a',0 }; + static const WCHAR szStringPool[] = { + '_','S','t','r','i','n','g','P','o','o','l',0 }; + + r = read_stream_data( stg, szStringPool, &pool, &poolsize ); + if( r != ERROR_SUCCESS) + goto end; + r = read_stream_data( stg, szStringData, (USHORT**)&data, &datasize ); + if( r != ERROR_SUCCESS) + goto end; + + count = poolsize/4; + if( poolsize > 4 ) + codepage = pool[0] | ( pool[1] << 16 ); + else + codepage = CP_ACP; + st = msi_init_stringtable( count, codepage ); + + offset = 0; + n = 1; + for( i=1; i datasize ) + { + ERR("string table corrupt?\n"); + break; + } + + /* don't add the high word of a string's length as a string */ + if ( len || !pool[i*2+1] ) + { + r = msi_addstring( st, n, data+offset, len, pool[i*2+1] ); + if( r != n ) + ERR("Failed to add string %ld\n", n ); + n++; + } + + offset += len; + } + + if ( datasize != offset ) + ERR("string table load failed! (%08x != %08lx)\n", datasize, offset ); + + TRACE("Loaded %ld strings\n", count); + +end: + msi_free( pool ); + msi_free( data ); + + return st; +} + +static UINT save_string_table( MSIDATABASE *db ) +{ + UINT i, count, datasize, poolsize, sz, used, r, codepage; + UINT ret = ERROR_FUNCTION_FAILED; + static const WCHAR szStringData[] = { + '_','S','t','r','i','n','g','D','a','t','a',0 }; + static const WCHAR szStringPool[] = { + '_','S','t','r','i','n','g','P','o','o','l',0 }; + CHAR *data = NULL; + USHORT *pool = NULL; + + TRACE("\n"); + + /* construct the new table in memory first */ + datasize = msi_string_totalsize( db->strings, &count ); + poolsize = count*2*sizeof(USHORT); + + pool = msi_alloc( poolsize ); + if( ! pool ) + { + WARN("Failed to alloc pool %d bytes\n", poolsize ); + goto err; + } + data = msi_alloc( datasize ); + if( ! data ) + { + WARN("Failed to alloc data %d bytes\n", poolsize ); + goto err; + } + + used = 0; + codepage = msi_string_get_codepage( db->strings ); + pool[0]=codepage&0xffff; + pool[1]=(codepage>>16); + for( i=1; istrings, i, data+used, &sz ); + if( r != ERROR_SUCCESS ) + { + ERR("failed to fetch string\n"); + sz = 0; + } + if( sz && (sz < (datasize - used ) ) ) + sz--; + TRACE("adding %u bytes %s\n", sz, debugstr_a(data+used) ); + pool[ i*2 ] = sz; + pool[ i*2 + 1 ] = msi_id_refcount( db->strings, i ); + used += sz; + if( used > datasize ) + { + ERR("oops overran %d >= %d\n", used, datasize); + goto err; + } + } + + if( used != datasize ) + { + ERR("oops used %d != datasize %d\n", used, datasize); + goto err; + } + + /* write the streams */ + r = write_stream_data( db->storage, szStringData, data, datasize ); + TRACE("Wrote StringData r=%08x\n", r); + if( r ) + goto err; + r = write_stream_data( db->storage, szStringPool, pool, poolsize ); + TRACE("Wrote StringPool r=%08x\n", r); + if( r ) + goto err; + + ret = ERROR_SUCCESS; + +err: + msi_free( data ); + msi_free( pool ); + + return ret; +} + +/* information for default tables */ +static const WCHAR szTables[] = { '_','T','a','b','l','e','s',0 }; +static const WCHAR szTable[] = { 'T','a','b','l','e',0 }; +static const WCHAR szName[] = { 'N','a','m','e',0 }; +static const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 }; +static const WCHAR szColumn[] = { 'C','o','l','u','m','n',0 }; +static const WCHAR szNumber[] = { 'N','u','m','b','e','r',0 }; +static const WCHAR szType[] = { 'T','y','p','e',0 }; + +static const MSICOLUMNINFO _Columns_cols[4] = { + { szColumns, 1, szTable, MSITYPE_VALID | MSITYPE_STRING | 64, 0 }, + { szColumns, 2, szNumber, MSITYPE_VALID | 2, 2 }, + { szColumns, 3, szName, MSITYPE_VALID | MSITYPE_STRING | 64, 4 }, + { szColumns, 4, szType, MSITYPE_VALID | 2, 6 }, +}; +static const MSICOLUMNINFO _Tables_cols[1] = { + { szTables, 1, szName, MSITYPE_VALID | MSITYPE_STRING | 64, 0 }, +}; + +static UINT get_defaulttablecolumns( LPCWSTR name, MSICOLUMNINFO *colinfo, UINT *sz) +{ + const MSICOLUMNINFO *p; + DWORD i, n; + + TRACE("%s\n", debugstr_w(name)); + + if (!lstrcmpW( name, szTables )) + { + p = _Tables_cols; + n = 1; + } + else if (!lstrcmpW( name, szColumns )) + { + p = _Columns_cols; + n = 4; + } + else + return ERROR_FUNCTION_FAILED; + + /* duplicate the string data so we can free it in msi_free_colinfo */ + for (i=0; i= *sz) ) + break; + } + *sz = n; + return ERROR_SUCCESS; +} + +static void msi_free_colinfo( MSICOLUMNINFO *colinfo, UINT count ) +{ + UINT i; + + for( i=0; istrings, stringid, NULL, &sz ); + if( r != ERROR_SUCCESS ) + return NULL; + str = msi_alloc( sz*sizeof (WCHAR) ); + if( !str ) + return str; + r = msi_id2stringW( db->strings, stringid, str, &sz ); + if( r == ERROR_SUCCESS ) + return str; + msi_free( str ); + return NULL; +} + +static UINT get_tablecolumns( MSIDATABASE *db, + LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz) +{ + UINT r, i, n=0, table_id, count, maxcount = *sz; + MSITABLE *table = NULL; + + /* first check if there is a default table with that name */ + r = get_defaulttablecolumns( szTableName, colinfo, sz ); + if( ( r == ERROR_SUCCESS ) && *sz ) + return r; + + table = get_table( db, szColumns, _Columns_cols, 4 ); + if( !table ) + { + ERR("couldn't load _Columns table\n"); + return ERROR_FUNCTION_FAILED; + } + + /* convert table and column names to IDs from the string table */ + r = msi_string2idW( db->strings, szTableName, &table_id ); + if( r != ERROR_SUCCESS ) + { + WARN("Couldn't find id for %s\n", debugstr_w(szTableName)); + return r; + } + + TRACE("Table id is %d\n", table_id); + + count = table->row_count; + for( i=0; idata[ i ][ 0 ] != table_id ) + continue; + if( colinfo ) + { + UINT id = table->data[ i ] [ 2 ]; + colinfo[n].tablename = MSI_makestring( db, table_id ); + colinfo[n].number = table->data[ i ][ 1 ] - (1<<15); + colinfo[n].colname = MSI_makestring( db, id ); + colinfo[n].type = table->data[ i ] [ 3 ] ^ 0x8000; + /* this assumes that columns are in order in the table */ + if( n ) + colinfo[n].offset = colinfo[n-1].offset + + bytes_per_column( &colinfo[n-1] ); + else + colinfo[n].offset = 0; + TRACE("table %s column %d is [%s] (%d) with type %08x " + "offset %d at row %d\n", debugstr_w(szTableName), + colinfo[n].number, debugstr_w(colinfo[n].colname), + id, colinfo[n].type, colinfo[n].offset, i); + if( n != (colinfo[n].number-1) ) + { + ERR("oops. data in the _Columns table isn't in the right " + "order for table %s\n", debugstr_w(szTableName)); + return ERROR_FUNCTION_FAILED; + } + } + n++; + if( colinfo && ( n >= maxcount ) ) + break; + } + *sz = n; + + return ERROR_SUCCESS; +} + +/* try to find the table name in the _Tables table */ +BOOL TABLE_Exists( MSIDATABASE *db, LPWSTR name ) +{ + UINT r, table_id = 0, i, count; + MSITABLE *table = NULL; + + if( !lstrcmpW( name, szTables ) ) + return TRUE; + if( !lstrcmpW( name, szColumns ) ) + return TRUE; + + r = msi_string2idW( db->strings, name, &table_id ); + if( r != ERROR_SUCCESS ) + { + TRACE("Couldn't find id for %s\n", debugstr_w(name)); + return FALSE; + } + + table = get_table( db, szTables, _Tables_cols, 1 ); + if( !table ) + { + TRACE("table %s not available\n", debugstr_w(szTables)); + return FALSE; + } + + /* count = table->size/2; */ + count = table->row_count; + for( i=0; idata[ i ][ 0 ] == table_id ) + break; + + if (i!=count) + return TRUE; + + TRACE("Searched %d tables, but %d was not found\n", count, table_id ); + + return FALSE; +} + +/* below is the query interface to a table */ + +typedef struct tagMSITABLEVIEW +{ + MSIVIEW view; + MSIDATABASE *db; + MSITABLE *table; + MSICOLUMNINFO *columns; + UINT num_cols; + UINT row_size; + WCHAR name[1]; +} MSITABLEVIEW; + +static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + UINT offset, num_rows, n; + + if( !tv->table ) + return ERROR_INVALID_PARAMETER; + + if( (col==0) || (col>tv->num_cols) ) + return ERROR_INVALID_PARAMETER; + + /* how many rows are there ? */ + num_rows = tv->table->row_count; + if( row >= num_rows ) + return ERROR_NO_MORE_ITEMS; + + if( tv->columns[col-1].offset >= tv->row_size ) + { + ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size ); + ERR("%p %p\n", tv, tv->columns ); + return ERROR_FUNCTION_FAILED; + } + + offset = row + (tv->columns[col-1].offset/2) * num_rows; + n = bytes_per_column( &tv->columns[col-1] ); + switch( n ) + { + case 4: + offset = tv->columns[col-1].offset/2; + *val = tv->table->data[row][offset] + + (tv->table->data[row][offset + 1] << 16); + break; + case 2: + offset = tv->columns[col-1].offset/2; + *val = tv->table->data[row][offset]; + break; + default: + ERR("oops! what is %d bytes per column?\n", n ); + return ERROR_FUNCTION_FAILED; + } + + /* TRACE("Data [%d][%d] = %d\n", row, col, *val ); */ + + return ERROR_SUCCESS; +} + +/* + * We need a special case for streams, as we need to reference column with + * the name of the stream in the same table, and the table name + * which may not be available at higher levels of the query + */ +static UINT TABLE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + UINT ival = 0, refcol = 0, r; + LPWSTR sval; + LPWSTR full_name; + DWORD len; + static const WCHAR szDot[] = { '.', 0 }; + + if( !view->ops->fetch_int ) + return ERROR_INVALID_PARAMETER; + + /* + * The column marked with the type stream data seems to have a single number + * which references the column containing the name of the stream data + * + * Fetch the column to reference first. + */ + r = view->ops->fetch_int( view, row, col, &ival ); + if( r != ERROR_SUCCESS ) + return r; + + /* now get the column with the name of the stream */ + r = view->ops->fetch_int( view, row, ival, &refcol ); + if( r != ERROR_SUCCESS ) + return r; + + /* lookup the string value from the string table */ + sval = MSI_makestring( tv->db, refcol ); + if( !sval ) + return ERROR_INVALID_PARAMETER; + + len = lstrlenW( tv->name ) + 2 + lstrlenW( sval ); + full_name = msi_alloc( len*sizeof(WCHAR) ); + lstrcpyW( full_name, tv->name ); + lstrcatW( full_name, szDot ); + lstrcatW( full_name, sval ); + + r = db_get_raw_stream( tv->db, full_name, stm ); + if( r ) + ERR("fetching stream %s, error = %d\n",debugstr_w(full_name), r); + msi_free( full_name ); + msi_free( sval ); + + return r; +} + +static UINT TABLE_set_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT val ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + UINT offset, n; + + if( !tv->table ) + return ERROR_INVALID_PARAMETER; + + if( (col==0) || (col>tv->num_cols) ) + return ERROR_INVALID_PARAMETER; + + if( tv->columns[col-1].offset >= tv->row_size ) + { + ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size ); + ERR("%p %p\n", tv, tv->columns ); + return ERROR_FUNCTION_FAILED; + } + + n = bytes_per_column( &tv->columns[col-1] ); + switch( n ) + { + case 4: + offset = tv->columns[col-1].offset/2; + tv->table->data[row][offset] = val & 0xffff; + tv->table->data[row][offset + 1] = (val>>16)&0xffff; + break; + case 2: + offset = tv->columns[col-1].offset/2; + tv->table->data[row][offset] = val; + break; + default: + ERR("oops! what is %d bytes per column?\n", n ); + return ERROR_FUNCTION_FAILED; + } + return ERROR_SUCCESS; +} + +static UINT table_create_new_row( struct tagMSIVIEW *view, UINT *num ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + USHORT **p, *row; + UINT sz; + + TRACE("%p\n", view); + + if( !tv->table ) + return ERROR_INVALID_PARAMETER; + + row = msi_alloc_zero( tv->row_size ); + if( !row ) + return ERROR_NOT_ENOUGH_MEMORY; + + sz = (tv->table->row_count + 1) * sizeof (UINT*); + if( tv->table->data ) + p = msi_realloc( tv->table->data, sz ); + else + p = msi_alloc( sz ); + if( !p ) + { + msi_free( row ); + return ERROR_NOT_ENOUGH_MEMORY; + } + + tv->table->data = p; + tv->table->data[tv->table->row_count] = row; + *num = tv->table->row_count; + tv->table->row_count++; + + return ERROR_SUCCESS; +} + +static UINT TABLE_execute( struct tagMSIVIEW *view, MSIRECORD *record ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + + TRACE("%p %p\n", tv, record); + + TRACE("There are %d columns\n", tv->num_cols ); + tv->table = get_table( tv->db, tv->name, tv->columns, tv->num_cols ); + if( !tv->table ) + return ERROR_FUNCTION_FAILED; + + return ERROR_SUCCESS; +} + +static UINT TABLE_close( struct tagMSIVIEW *view ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + + TRACE("%p\n", view ); + + if( !tv->table ) + return ERROR_FUNCTION_FAILED; + + tv->table = NULL; + + return ERROR_SUCCESS; +} + +static UINT TABLE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + + TRACE("%p %p %p\n", view, rows, cols ); + + if( cols ) + *cols = tv->num_cols; + if( rows ) + { + if( !tv->table ) + return ERROR_INVALID_PARAMETER; + *rows = tv->table->row_count; + } + + return ERROR_SUCCESS; +} + +static UINT TABLE_get_column_info( struct tagMSIVIEW *view, + UINT n, LPWSTR *name, UINT *type ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + + TRACE("%p %d %p %p\n", tv, n, name, type ); + + if( ( n == 0 ) || ( n > tv->num_cols ) ) + return ERROR_INVALID_PARAMETER; + + if( name ) + { + *name = strdupW( tv->columns[n-1].colname ); + if( !*name ) + return ERROR_FUNCTION_FAILED; + } + if( type ) + *type = tv->columns[n-1].type; + + return ERROR_SUCCESS; +} + +static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row ); + +static UINT table_validate_new( MSITABLEVIEW *tv, MSIRECORD *rec ) +{ + UINT r, row; + + r = msi_table_find_row( tv, rec, &row ); + if (r != ERROR_SUCCESS) + return ERROR_SUCCESS; + return ERROR_INVALID_DATA; +} + +static UINT msi_table_modify_row( MSITABLEVIEW *tv, MSIRECORD *rec, + UINT row, UINT mask ) +{ + UINT i, val, r = ERROR_SUCCESS; + + TRACE("%p %p %u %08x\n", tv, rec, row, mask ); + + for( i = 0; i < tv->num_cols; i++ ) + { + /* set keys or values specified in the mask */ + if( (~tv->columns[i].type & MSITYPE_KEY) && (~mask & (1<columns[i].type & MSITYPE_STRING) && + ! MSITYPE_IS_BINARY(tv->columns[i].type) ) + { + const WCHAR *str = MSI_RecordGetString( rec, i+1 ); + val = msi_addstringW( tv->db->strings, 0, str, -1, 1 ); + } + else + { + val = MSI_RecordGetInteger( rec, i+1 ); + if ( 2 == bytes_per_column( &tv->columns[i] ) ) + val ^= 0x8000; + } + r = TABLE_set_int( &tv->view, row, i+1, val ); + if( r ) + break; + } + + return r; +} + +static UINT TABLE_insert_row( struct tagMSIVIEW *view, MSIRECORD *rec ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + UINT r, row = -1; + + TRACE("%p %p\n", tv, rec ); + + r = table_create_new_row( view, &row ); + TRACE("insert_row returned %08x\n", r); + if( r != ERROR_SUCCESS ) + return r; + + return msi_table_modify_row( tv, rec, row, ~0 ); +} + +static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, + MSIRECORD *rec) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + UINT r; + + TRACE("%p %d %p\n", view, eModifyMode, rec ); + + if (!tv->table) + { + r = TABLE_execute( view, NULL ); + if( r ) + return r; + } + + switch (eModifyMode) + { + case MSIMODIFY_VALIDATE_NEW: + r = table_validate_new( tv, rec ); + break; + + case MSIMODIFY_INSERT_TEMPORARY: + r = table_validate_new( tv, rec ); + if (r != ERROR_SUCCESS) + break; + r = TABLE_insert_row( view, rec ); + break; + + case MSIMODIFY_REFRESH: + case MSIMODIFY_INSERT: + case MSIMODIFY_UPDATE: + case MSIMODIFY_ASSIGN: + case MSIMODIFY_REPLACE: + case MSIMODIFY_MERGE: + case MSIMODIFY_DELETE: + case MSIMODIFY_VALIDATE: + case MSIMODIFY_VALIDATE_FIELD: + case MSIMODIFY_VALIDATE_DELETE: + FIXME("%p %d %p - mode not implemented\n", view, eModifyMode, rec ); + r = ERROR_CALL_NOT_IMPLEMENTED; + break; + + default: + r = ERROR_INVALID_DATA; + } + + return r; +} + +static UINT TABLE_delete( struct tagMSIVIEW *view ) +{ + MSITABLEVIEW *tv = (MSITABLEVIEW*)view; + + TRACE("%p\n", view ); + + tv->table = NULL; + + if( tv->columns ) + { + msi_free_colinfo( tv->columns, tv->num_cols ); + msi_free( tv->columns ); + } + tv->columns = NULL; + + msi_free( tv ); + + return ERROR_SUCCESS; +} + + +MSIVIEWOPS table_ops = +{ + TABLE_fetch_int, + TABLE_fetch_stream, + TABLE_set_int, + TABLE_insert_row, + TABLE_execute, + TABLE_close, + TABLE_get_dimensions, + TABLE_get_column_info, + TABLE_modify, + TABLE_delete +}; + +UINT TABLE_CreateView( MSIDATABASE *db, LPCWSTR name, MSIVIEW **view ) +{ + MSITABLEVIEW *tv ; + UINT r, sz, column_count; + MSICOLUMNINFO *columns; + + TRACE("%p %s %p\n", db, debugstr_w(name), view ); + + /* get the number of columns in this table */ + column_count = 0; + r = get_tablecolumns( db, name, NULL, &column_count ); + if( r != ERROR_SUCCESS ) + return r; + + /* if there's no columns, there's no table */ + if( column_count == 0 ) + return ERROR_INVALID_PARAMETER; + + TRACE("Table found\n"); + + sz = sizeof *tv + lstrlenW(name)*sizeof name[0] ; + tv = msi_alloc_zero( sz ); + if( !tv ) + return ERROR_FUNCTION_FAILED; + + columns = msi_alloc( column_count*sizeof (MSICOLUMNINFO)); + if( !columns ) + { + msi_free( tv ); + return ERROR_FUNCTION_FAILED; + } + + r = get_tablecolumns( db, name, columns, &column_count ); + if( r != ERROR_SUCCESS ) + { + msi_free( columns ); + msi_free( tv ); + return ERROR_FUNCTION_FAILED; + } + + TRACE("Table has %d columns\n", column_count); + + /* fill the structure */ + tv->view.ops = &table_ops; + tv->db = db; + tv->columns = columns; + tv->num_cols = column_count; + tv->table = NULL; + tv->row_size = msi_table_get_row_size( columns, column_count ); + + TRACE("%s one row is %d bytes\n", debugstr_w(name), tv->row_size ); + + *view = (MSIVIEW*) tv; + lstrcpyW( tv->name, name ); + + return ERROR_SUCCESS; +} + +UINT MSI_CommitTables( MSIDATABASE *db ) +{ + UINT r; + MSITABLE *table = NULL; + + TRACE("%p\n",db); + + r = save_string_table( db ); + if( r != ERROR_SUCCESS ) + { + WARN("failed to save string table r=%08x\n",r); + return r; + } + + LIST_FOR_EACH_ENTRY( table, &db->tables, MSITABLE, entry ) + { + r = save_table( db, table ); + if( r != ERROR_SUCCESS ) + { + WARN("failed to save table %s (r=%08x)\n", + debugstr_w(table->name), r); + return r; + } + } + + /* force everything to reload next time */ + free_cached_tables( db ); + + return ERROR_SUCCESS; +} + +static MSIRECORD *msi_get_transform_record( MSITABLEVIEW *tv, string_table *st, USHORT *rawdata ) +{ + UINT i, val, ofs = 0; + USHORT mask = *rawdata++; + MSICOLUMNINFO *columns = tv->columns; + MSIRECORD *rec; + const int debug_transform = 0; + + rec = MSI_CreateRecord( tv->num_cols ); + if( !rec ) + return rec; + + if( debug_transform ) MESSAGE("row -> "); + for( i=0; inum_cols; i++ ) + { + UINT n = bytes_per_column( &columns[i] ); + + if ( (mask&1) && (i>=(mask>>8)) ) + break; + /* all keys must be present */ + if ( (~mask&1) && (~columns[i].type & MSITYPE_KEY) && ((1<columns[i].type) ) + { + LPCWSTR sval = msi_string_lookup_id( st, val ); + MSI_RecordSetStringW( rec, i+1, sval ); + if( debug_transform ) MESSAGE("[%s]", debugstr_w(sval)); + } + else + { + val ^= 0x8000; + MSI_RecordSetInteger( rec, i+1, val ); + if( debug_transform) MESSAGE("[0x%04x]", val ); + } + break; + case 4: + val = rawdata[ofs] + (rawdata[ofs + 1]<<16); + /* val ^= 0x80000000; */ + MSI_RecordSetInteger( rec, i+1, val ); + if( debug_transform ) MESSAGE("[0x%08x]", val ); + break; + default: + ERR("oops - unknown column width %d\n", n); + break; + } + ofs += n/2; + } + if( debug_transform) MESSAGE("\n"); + return rec; +} + +static void dump_record( MSIRECORD *rec ) +{ + UINT i, n; + + MESSAGE("row -> "); + n = MSI_RecordGetFieldCount( rec ); + for( i=1; i<=n; i++ ) + { + LPCWSTR sval = MSI_RecordGetString( rec, i ); + + if( MSI_RecordIsNull( rec, i ) ) + MESSAGE("[]"); + else if( (sval = MSI_RecordGetString( rec, i )) ) + MESSAGE("[%s]", debugstr_w(sval)); + else + MESSAGE("[0x%08x]", MSI_RecordGetInteger( rec, i ) ); + } + MESSAGE("\n"); +} + +static void dump_table( string_table *st, USHORT *rawdata, UINT rawsize ) +{ + LPCWSTR sval; + UINT i; + + for( i=0; i<(rawsize/2); i++ ) + { + sval = msi_string_lookup_id( st, rawdata[i] ); + if( !sval ) sval = (WCHAR[]) {0}; + MESSAGE(" %04x %s\n", rawdata[i], debugstr_w(sval) ); + } +} + +static UINT* msi_record_to_row( MSITABLEVIEW *tv, MSIRECORD *rec ) +{ + LPCWSTR str; + UINT i, r, *data; + + data = msi_alloc( tv->num_cols *sizeof (UINT) ); + for( i=0; inum_cols; i++ ) + { + data[i] = 0; + + if ( ~tv->columns[i].type & MSITYPE_KEY ) + continue; + + /* turn the transform column value into a row value */ + if ( ( tv->columns[i].type & MSITYPE_STRING ) && + ! MSITYPE_IS_BINARY(tv->columns[i].type) ) + { + str = MSI_RecordGetString( rec, i+1 ); + r = msi_string2idW( tv->db->strings, str, &data[i] ); + + /* if there's no matching string in the string table, + these keys can't match any record, so fail now. */ + if( ERROR_SUCCESS != r ) + { + msi_free( data ); + return NULL; + } + } + else + data[i] = MSI_RecordGetInteger( rec, i+1 ); + } + return data; +} + +static UINT msi_row_matches( MSITABLEVIEW *tv, UINT row, UINT *data ) +{ + UINT i, r, x, ret = ERROR_FUNCTION_FAILED; + + for( i=0; inum_cols; i++ ) + { + if ( ~tv->columns[i].type & MSITYPE_KEY ) + continue; + + /* turn the transform column value into a row value */ + r = TABLE_fetch_int( &tv->view, row, i+1, &x ); + if ( r != ERROR_SUCCESS ) + { + ERR("TABLE_fetch_int shouldn't fail here\n"); + break; + } + + /* if this key matches, move to the next column */ + if ( x != data[i] ) + { + ret = ERROR_FUNCTION_FAILED; + break; + } + + ret = ERROR_SUCCESS; + } + + return ret; +} + +static UINT msi_table_find_row( MSITABLEVIEW *tv, MSIRECORD *rec, UINT *row ) +{ + UINT i, r = ERROR_FUNCTION_FAILED, *data; + + data = msi_record_to_row( tv, rec ); + if( !data ) + return r; + for( i=0; itable->row_count; i++ ) + { + r = msi_row_matches( tv, i, data ); + if( r == ERROR_SUCCESS ) + { + *row = i; + break; + } + } + msi_free( data ); + return r; +} + +static UINT msi_delete_row( MSITABLEVIEW *tv, UINT row ) +{ + UINT i; + for( i=1; i<=tv->num_cols; i++ ) + tv->view.ops->set_int( &tv->view, row, i, 0 ); + return ERROR_SUCCESS; +} + +static UINT msi_table_load_transform( MSIDATABASE *db, IStorage *stg, + string_table *st, LPCWSTR name ) +{ + UINT rawsize = 0; + USHORT *rawdata = NULL; + MSITABLEVIEW *tv = NULL; + UINT r, n, sz, i, mask; + MSIRECORD *rec = NULL; + const int debug_transform = 0; + + TRACE("%p %p %p %s\n", db, stg, st, debugstr_w(name) ); + + /* create a table view */ + r = TABLE_CreateView( db, name, (MSIVIEW**) &tv ); + if( r != ERROR_SUCCESS ) + goto err; + + r = tv->view.ops->execute( &tv->view, NULL ); + if( r != ERROR_SUCCESS ) + goto err; + + /* read the transform data */ + r = ERROR_FUNCTION_FAILED; + read_stream_data( stg, name, &rawdata, &rawsize ); + if( !rawdata || (rawsize < 2) ) + { + ERR("odd sized transform for table %s\n", debugstr_w(name)); + goto err; + } + + TRACE("name = %s columns = %u row_size = %u raw size = %u\n", + debugstr_w(name), tv->num_cols, tv->row_size, rawsize ); + + /* interpret the data */ + r = ERROR_SUCCESS; + for( n=0; n < (rawsize/2); ) + { + mask = rawdata[n]; + + if (mask&1) + { + /* + * if the low bit is set, columns are continuous and + * the number of columns is specified in the high byte + */ + sz = 2 + tv->row_size; + } + else + { + /* + * If the low bit is not set, rowdata[n] is a bitmask. + * Excepting for key fields, which are always present, + * each bit indicates that a field is present in the transform record. + * + * rawdata[n] == 0 is a special case ... only the keys will be present + * and it means that this row should be deleted. + */ + sz = 2; + for( i=0; inum_cols; i++ ) + { + if( (tv->columns[i].type & MSITYPE_KEY) || ((1<columns[i] ); + } + } + + /* check we didn't run of the end of the table */ + if ( (n+sz) > rawsize ) + { + ERR("borked.\n"); + dump_table( st, rawdata, rawsize ); + break; + } + + rec = msi_get_transform_record( tv, st, &rawdata[n] ); + if (rec) + { + UINT row = 0; + + r = msi_table_find_row( tv, rec, &row ); + + if( rawdata[n] & 1) + { + if( debug_transform ) MESSAGE("insert [%d]: ", row); + TABLE_insert_row( &tv->view, rec ); + } + else if( mask & 0xff ) + { + if( debug_transform ) MESSAGE("modify [%d]: ", row); + msi_table_modify_row( tv, rec, row, mask ); + } + else + { + if( debug_transform ) MESSAGE("delete [%d]: ", row); + msi_delete_row( tv, row ); + } + if( debug_transform ) dump_record( rec ); + msiobj_release( &rec->hdr ); + } + + n += sz/2; + + } + +err: + /* no need to free the table, it's associated with the database */ + msi_free( rawdata ); + if( tv ) + tv->view.ops->delete( &tv->view ); + + return ERROR_SUCCESS; +} + +/* + * msi_table_apply_transform + * + * Enumerate the table transforms in a transform storage and apply each one. + */ +UINT msi_table_apply_transform( MSIDATABASE *db, IStorage *stg ) +{ + IEnumSTATSTG *stgenum = NULL; + HRESULT r; + STATSTG stat; + ULONG n, count; + WCHAR name[0x40]; + string_table *strings; + UINT ret = ERROR_FUNCTION_FAILED; + + TRACE("%p %p\n", db, stg ); + + strings = load_string_table( stg ); + if( !strings ) + goto end; + + r = IStorage_EnumElements( stg, 0, NULL, 0, &stgenum ); + if( FAILED( r ) ) + goto end; + + n = 0; + ret = ERROR_SUCCESS; + + while( r == ERROR_SUCCESS ) + { + count = 0; + r = IEnumSTATSTG_Next( stgenum, 1, &stat, &count ); + if( FAILED( r ) || !count ) + break; + decode_streamname( stat.pwcsName, name ); + if( ( name[0] == 0x4840 ) && ( name[1] != '_' ) ) + ret = msi_table_load_transform( db, stg, strings, name+1 ); + else + TRACE("transform contains stream %s\n", debugstr_w(name)); + n++; + } + + if ( ret == ERROR_SUCCESS ) + { + MSITRANSFORM *t; + + t = msi_alloc( sizeof *t ); + t->stg = stg; + IStorage_AddRef( stg ); + list_add_tail( &db->transforms, &t->entry ); + } + +end: + if ( stgenum ) + IEnumSTATSTG_Release( stgenum ); + if ( strings ) + msi_destroy_stringtable( strings ); + + return ret; +} + +void msi_free_transforms( MSIDATABASE *db ) +{ + while( !list_empty( &db->transforms ) ) + { + MSITRANSFORM *t = LIST_ENTRY( list_head( &db->transforms ), + MSITRANSFORM, entry ); + list_remove( &t->entry ); + IStorage_Release( t->stg ); + msi_free( t ); + } +} diff --git a/reactos/dll/msi/tokenize.c b/reactos/dll/msi/tokenize.c new file mode 100644 index 00000000000..05c3cda38e2 --- /dev/null +++ b/reactos/dll/msi/tokenize.c @@ -0,0 +1,400 @@ +/* +** 2001 September 15 +** +** The author disclaims copyright to this source code. In place of +** a legal notice, here is a blessing: +** +** May you do good and not evil. +** May you find forgiveness for yourself and forgive others. +** May you share freely, never taking more than you give. +** +************************************************************************* +** An tokenizer for SQL +** +** This file contains C code that splits an SQL input string up into +** individual tokens and sends those tokens one-by-one over to the +** parser for analysis. +*/ + +#include +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "wine/debug.h" +#include "winnls.h" +#include "query.h" +#include "sql.tab.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +/* +** All the keywords of the SQL language are stored as in a hash +** table composed of instances of the following structure. +*/ +typedef struct Keyword Keyword; +struct Keyword { + const char *zName; /* The keyword name */ + int tokenType; /* The token value for this keyword */ +}; + +/* +** These are the keywords +*/ +static const Keyword aKeywordTable[] = { + { "ABORT", TK_ABORT }, + { "AFTER", TK_AFTER }, + { "ALL", TK_ALL }, + { "AND", TK_AND }, + { "AS", TK_AS }, + { "ASC", TK_ASC }, + { "BEFORE", TK_BEFORE }, + { "BEGIN", TK_BEGIN }, + { "BETWEEN", TK_BETWEEN }, + { "BY", TK_BY }, + { "CASCADE", TK_CASCADE }, + { "CASE", TK_CASE }, + { "CHAR", TK_CHAR }, + { "CHARACTER", TK_CHAR }, + { "CHECK", TK_CHECK }, + { "CLUSTER", TK_CLUSTER }, + { "COLLATE", TK_COLLATE }, + { "COMMIT", TK_COMMIT }, + { "CONFLICT", TK_CONFLICT }, + { "CONSTRAINT", TK_CONSTRAINT }, + { "COPY", TK_COPY }, + { "CREATE", TK_CREATE }, + { "CROSS", TK_JOIN_KW }, + { "DEFAULT", TK_DEFAULT }, + { "DEFERRED", TK_DEFERRED }, + { "DEFERRABLE", TK_DEFERRABLE }, + { "DELETE", TK_DELETE }, + { "DELIMITERS", TK_DELIMITERS }, + { "DESC", TK_DESC }, + { "DISTINCT", TK_DISTINCT }, + { "DROP", TK_DROP }, + { "END", TK_END }, + { "EACH", TK_EACH }, + { "ELSE", TK_ELSE }, + { "EXCEPT", TK_EXCEPT }, + { "EXPLAIN", TK_EXPLAIN }, + { "FAIL", TK_FAIL }, + { "FOR", TK_FOR }, + { "FOREIGN", TK_FOREIGN }, + { "FROM", TK_FROM }, + { "FULL", TK_JOIN_KW }, + { "GLOB", TK_GLOB }, + { "GROUP", TK_GROUP }, + { "HAVING", TK_HAVING }, + { "HOLD", TK_HOLD }, + { "IGNORE", TK_IGNORE }, + { "IMMEDIATE", TK_IMMEDIATE }, + { "IN", TK_IN }, + { "INDEX", TK_INDEX }, + { "INITIALLY", TK_INITIALLY }, + { "INNER", TK_JOIN_KW }, + { "INSERT", TK_INSERT }, + { "INSTEAD", TK_INSTEAD }, + { "INT", TK_INT }, + { "INTERSECT", TK_INTERSECT }, + { "INTO", TK_INTO }, + { "IS", TK_IS }, + { "ISNULL", TK_ISNULL }, + { "JOIN", TK_JOIN }, + { "KEY", TK_KEY }, + { "LEFT", TK_JOIN_KW }, + { "LIKE", TK_LIKE }, + { "LIMIT", TK_LIMIT }, + { "LOCALIZABLE", TK_LOCALIZABLE }, + { "LONG", TK_LONG }, + { "LONGCHAR", TK_LONGCHAR }, + { "MATCH", TK_MATCH }, + { "NATURAL", TK_JOIN_KW }, + { "NOT", TK_NOT }, + { "NOTNULL", TK_NOTNULL }, + { "NULL", TK_NULL }, + { "OBJECT", TK_OBJECT }, + { "OF", TK_OF }, + { "OFFSET", TK_OFFSET }, + { "ON", TK_ON }, + { "OR", TK_OR }, + { "ORDER", TK_ORDER }, + { "OUTER", TK_JOIN_KW }, + { "PRAGMA", TK_PRAGMA }, + { "PRIMARY", TK_PRIMARY }, + { "RAISE", TK_RAISE }, + { "REFERENCES", TK_REFERENCES }, + { "REPLACE", TK_REPLACE }, + { "RESTRICT", TK_RESTRICT }, + { "RIGHT", TK_JOIN_KW }, + { "ROLLBACK", TK_ROLLBACK }, + { "ROW", TK_ROW }, + { "SELECT", TK_SELECT }, + { "SET", TK_SET }, + { "SHORT", TK_SHORT }, + { "STATEMENT", TK_STATEMENT }, + { "TABLE", TK_TABLE }, + { "TEMP", TK_TEMP }, + { "TEMPORARY", TK_TEMP }, + { "THEN", TK_THEN }, + { "TRANSACTION", TK_TRANSACTION }, + { "TRIGGER", TK_TRIGGER }, + { "UNION", TK_UNION }, + { "UNIQUE", TK_UNIQUE }, + { "UPDATE", TK_UPDATE }, + { "USING", TK_USING }, + { "VACUUM", TK_VACUUM }, + { "VALUES", TK_VALUES }, + { "VIEW", TK_VIEW }, + { "WHEN", TK_WHEN }, + { "WHERE", TK_WHERE }, +}; + +#define KEYWORD_COUNT ( sizeof aKeywordTable/sizeof (Keyword) ) + +/* +** This function looks up an identifier to determine if it is a +** keyword. If it is a keyword, the token code of that keyword is +** returned. If the input is not a keyword, TK_ID is returned. +*/ +static int sqliteKeywordCode(const WCHAR *z, int n){ + UINT i, len; + char buffer[0x10]; + + len = WideCharToMultiByte( CP_ACP, 0, z, n, buffer, sizeof buffer, NULL, NULL ); + for(i=0; i' ){ + *tokenType = TK_NE; + return 2; + }else if( z[1]=='<' ){ + *tokenType = TK_LSHIFT; + return 2; + }else{ + *tokenType = TK_LT; + return 1; + } + } + case '>': { + if( z[1]=='=' ){ + *tokenType = TK_GE; + return 2; + }else if( z[1]=='>' ){ + *tokenType = TK_RSHIFT; + return 2; + }else{ + *tokenType = TK_GT; + return 1; + } + } + case '!': { + if( z[1]!='=' ){ + *tokenType = TK_ILLEGAL; + return 2; + }else{ + *tokenType = TK_NE; + return 2; + } + } + case '|': { + if( z[1]!='|' ){ + *tokenType = TK_BITOR; + return 1; + }else{ + *tokenType = TK_CONCAT; + return 2; + } + } + case '?': { + *tokenType = TK_WILDCARD; + return 1; + } + case ',': { + *tokenType = TK_COMMA; + return 1; + } + case '&': { + *tokenType = TK_BITAND; + return 1; + } + case '~': { + *tokenType = TK_BITNOT; + return 1; + } + case '`': case '\'': case '"': { + int delim = z[0]; + for(i=1; z[i]; i++){ + if( z[i]==delim ){ + if( z[i+1]==delim ){ + i++; + }else{ + break; + } + } + } + if( z[i] ) i++; + if( delim == '`' ) + *tokenType = TK_ID; + else + *tokenType = TK_STRING; + return i; + } + case '.': { + if( !isdigit(z[1]) ){ + *tokenType = TK_DOT; + return 1; + } + /* Fall thru into the next case */ + } + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': { + *tokenType = TK_INTEGER; + for(i=1; isdigit(z[i]); i++){} + if( z[i]=='.' ){ + i++; + while( isdigit(z[i]) ){ i++; } + *tokenType = TK_FLOAT; + } + if( (z[i]=='e' || z[i]=='E') && + ( isdigit(z[i+1]) + || ((z[i+1]=='+' || z[i+1]=='-') && isdigit(z[i+2])) + ) + ){ + i += 2; + while( isdigit(z[i]) ){ i++; } + *tokenType = TK_FLOAT; + }else if( z[0]=='.' ){ + *tokenType = TK_FLOAT; + } + return i; + } + case '[': { + for(i=1; z[i] && z[i-1]!=']'; i++){} + *tokenType = TK_ID; + return i; + } + default: { + if( !isIdChar[*z] ){ + break; + } + for(i=1; isIdChar[z[i]]; i++){} + *tokenType = sqliteKeywordCode(z, i); + return i; + } + } + *tokenType = TK_ILLEGAL; + return 1; +} diff --git a/reactos/dll/msi/update.c b/reactos/dll/msi/update.c new file mode 100644 index 00000000000..a36c5196483 --- /dev/null +++ b/reactos/dll/msi/update.c @@ -0,0 +1,237 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2004 Mike McCormack 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 receuved 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 + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" +#include "winnls.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msidb); + + +/* below is the query interface to a table */ + +typedef struct tagMSIUPDATEVIEW +{ + MSIVIEW view; + MSIDATABASE *db; + MSIVIEW *wv; + column_info *vals; +} MSIUPDATEVIEW; + +static UINT UPDATE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ) +{ + MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view; + + TRACE("%p %d %d %p\n", uv, row, col, val ); + + return ERROR_FUNCTION_FAILED; +} + +static UINT UPDATE_execute( struct tagMSIVIEW *view, MSIRECORD *record ) +{ + MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view; + UINT n, type, val, r, row, col_count = 0, row_count = 0; + MSIVIEW *wv; + + TRACE("%p %p\n", uv, record ); + + if( !record ) + return ERROR_FUNCTION_FAILED; + + wv = uv->wv; + if( !wv ) + return ERROR_FUNCTION_FAILED; + + r = wv->ops->execute( wv, 0 ); + TRACE("tv execute returned %x\n", r); + if( r ) + return r; + + r = wv->ops->get_dimensions( wv, &row_count, &col_count ); + if( r ) + goto err; + + for( row = 0; row < row_count; row++ ) + { + for( n = 1; n <= col_count; n++ ) + { + r = wv->ops->get_column_info( wv, n, NULL, &type ); + if( r ) + break; + + if( type & MSITYPE_STRING ) + { + const WCHAR *str = MSI_RecordGetString( record, n ); + val = msi_addstringW( uv->db->strings, 0, str, -1, 1 ); + } + else + { + val = MSI_RecordGetInteger( record, n ); + val |= 0x8000; + } + r = wv->ops->set_int( wv, row, n, val ); + if( r ) + break; + } + } + +err: + return ERROR_SUCCESS; +} + + +static UINT UPDATE_close( struct tagMSIVIEW *view ) +{ + MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view; + MSIVIEW *wv; + + TRACE("%p\n", uv); + + wv = uv->wv; + if( !wv ) + return ERROR_FUNCTION_FAILED; + + return wv->ops->close( wv ); +} + +static UINT UPDATE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols ) +{ + MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view; + MSIVIEW *wv; + + TRACE("%p %p %p\n", uv, rows, cols ); + + wv = uv->wv; + if( !wv ) + return ERROR_FUNCTION_FAILED; + + return wv->ops->get_dimensions( wv, rows, cols ); +} + +static UINT UPDATE_get_column_info( struct tagMSIVIEW *view, + UINT n, LPWSTR *name, UINT *type ) +{ + MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view; + MSIVIEW *wv; + + TRACE("%p %d %p %p\n", uv, n, name, type ); + + wv = uv->wv; + if( !wv ) + return ERROR_FUNCTION_FAILED; + + return wv->ops->get_column_info( wv, n, name, type ); +} + +static UINT UPDATE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, + MSIRECORD *rec ) +{ + MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view; + + TRACE("%p %d %p\n", uv, eModifyMode, rec ); + + return ERROR_FUNCTION_FAILED; +} + +static UINT UPDATE_delete( struct tagMSIVIEW *view ) +{ + MSIUPDATEVIEW *uv = (MSIUPDATEVIEW*)view; + MSIVIEW *wv; + + TRACE("%p\n", uv ); + + wv = uv->wv; + if( wv ) + wv->ops->delete( wv ); + msiobj_release( &uv->db->hdr ); + msi_free( uv ); + + return ERROR_SUCCESS; +} + + +static MSIVIEWOPS update_ops = +{ + UPDATE_fetch_int, + NULL, + NULL, + NULL, + UPDATE_execute, + UPDATE_close, + UPDATE_get_dimensions, + UPDATE_get_column_info, + UPDATE_modify, + UPDATE_delete +}; + +UINT UPDATE_CreateView( MSIDATABASE *db, MSIVIEW **view, LPWSTR table, + column_info *columns, struct expr *expr ) +{ + MSIUPDATEVIEW *uv = NULL; + UINT r; + MSIVIEW *tv = NULL, *sv = NULL, *wv = NULL; + + TRACE("%p\n", uv ); + + r = TABLE_CreateView( db, table, &tv ); + if( r != ERROR_SUCCESS ) + return r; + + /* add conditions first */ + r = WHERE_CreateView( db, &wv, tv, expr ); + if( r != ERROR_SUCCESS ) + { + tv->ops->delete( tv ); + return r; + } + + /* then select the columns we want */ + r = SELECT_CreateView( db, &sv, wv, columns ); + if( r != ERROR_SUCCESS ) + { + wv->ops->delete( wv ); + return r; + } + + uv = msi_alloc_zero( sizeof *uv ); + if( !uv ) + return ERROR_FUNCTION_FAILED; + + /* fill the structure */ + uv->view.ops = &update_ops; + msiobj_addref( &db->hdr ); + uv->db = db; + uv->vals = columns; + uv->wv = sv; + *view = (MSIVIEW*) uv; + + return ERROR_SUCCESS; +} diff --git a/reactos/dll/msi/upgrade.c b/reactos/dll/msi/upgrade.c new file mode 100644 index 00000000000..3851abb2a4c --- /dev/null +++ b/reactos/dll/msi/upgrade.c @@ -0,0 +1,226 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2005 Aric Stewart 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +/* + * Actions focused on in this module + * + * FindRelatedProducts + * MigrateFeatureStates (TODO) + * RemoveExistingProducts (TODO) + */ + +#include + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "winreg.h" +#include "wine/debug.h" +#include "msidefs.h" +#include "msipriv.h" +#include "winuser.h" +#include "action.h" +#include "wine/unicode.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +extern const WCHAR szFindRelatedProducts[]; +extern const WCHAR szMigrateFeatureStates[]; +extern const WCHAR szRemoveExistingProducts[]; + +static BOOL check_language(DWORD lang1, LPCWSTR lang2, DWORD attributes) +{ + DWORD langdword; + + if (!lang2 || lang2[0]==0) + return TRUE; + + langdword = atoiW(lang2); + + if (attributes & msidbUpgradeAttributesLanguagesExclusive) + return (lang1 != langdword); + else + return (lang1 == langdword); +} + +static void append_productcode(MSIPACKAGE* package, LPCWSTR action_property, + LPCWSTR productid) +{ + LPWSTR prop; + LPWSTR newprop; + DWORD len; + static const WCHAR separator[] = {';',0}; + + prop = msi_dup_property(package, action_property ); + if (prop) + len = strlenW(prop); + else + len = 0; + + /*separator*/ + len ++; + + len += strlenW(productid); + + /*null*/ + len++; + + newprop = msi_alloc( len*sizeof(WCHAR) ); + + if (prop) + { + strcpyW(newprop,prop); + strcatW(newprop,separator); + } + else + newprop[0] = 0; + strcatW(newprop,productid); + + MSI_SetPropertyW(package, action_property, newprop); + TRACE("Found Related Product... %s now %s\n",debugstr_w(action_property), + debugstr_w(newprop)); + msi_free( prop ); + msi_free( newprop ); +} + +static UINT ITERATE_FindRelatedProducts(MSIRECORD *rec, LPVOID param) +{ + MSIPACKAGE *package = (MSIPACKAGE*)param; + WCHAR product[GUID_SIZE]; + DWORD index = 0; + DWORD attributes = 0; + DWORD sz = GUID_SIZE; + LPCWSTR upgrade_code; + HKEY hkey = 0; + UINT rc = ERROR_SUCCESS; + MSIRECORD *uirow; + + upgrade_code = MSI_RecordGetString(rec,1); + + rc = MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey, FALSE); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + uirow = MSI_CreateRecord(1); + attributes = MSI_RecordGetInteger(rec,5); + + while (rc == ERROR_SUCCESS) + { + rc = RegEnumValueW(hkey, index, product, &sz, NULL, NULL, NULL, NULL); + TRACE("Looking at (%li) %s\n",index,debugstr_w(product)); + if (rc == ERROR_SUCCESS) + { + WCHAR productid[GUID_SIZE]; + LPCWSTR ver; + LPCWSTR language; + LPCWSTR action_property; + DWORD check = 0x00000000; + DWORD comp_ver = 0x00000000; + DWORD sz = 0x100; + HKEY hukey; + INT r; + + unsquash_guid(product,productid); + rc = MSIREG_OpenUserProductsKey(productid, &hukey, FALSE); + if (rc != ERROR_SUCCESS) + { + rc = ERROR_SUCCESS; + index ++; + continue; + } + + sz = sizeof(DWORD); + RegQueryValueExW(hukey, INSTALLPROPERTY_VERSIONW, NULL, NULL, + (LPBYTE)&check, &sz); + /* check min */ + ver = MSI_RecordGetString(rec,2); + comp_ver = build_version_dword(ver); + r = check - comp_ver; + if (r < 0 || (r == 0 && !(attributes & + msidbUpgradeAttributesVersionMinInclusive))) + { + RegCloseKey(hukey); + index ++; + continue; + } + + /* check max */ + ver = MSI_RecordGetString(rec,3); + comp_ver = build_version_dword(ver); + r = check - comp_ver; + if (r > 0 || (r == 0 && !(attributes & + msidbUpgradeAttributesVersionMaxInclusive))) + { + RegCloseKey(hukey); + index ++; + continue; + } + + /* check language*/ + sz = sizeof(DWORD); + RegQueryValueExW(hukey, INSTALLPROPERTY_LANGUAGEW, NULL, NULL, + (LPBYTE)&check, &sz); + RegCloseKey(hukey); + language = MSI_RecordGetString(rec,4); + TRACE("Checking languages 0x%lx and %s\n", check, + debugstr_w(language)); + if (!check_language(check, language, attributes)) + { + index ++; + continue; + } + + action_property = MSI_RecordGetString(rec,7); + append_productcode(package,action_property,productid); + ui_actiondata(package,szFindRelatedProducts,uirow); + } + index ++; + } + RegCloseKey(hkey); + msiobj_release( &uirow->hdr); + + return ERROR_SUCCESS; +} + +UINT ACTION_FindRelatedProducts(MSIPACKAGE *package) +{ + static const WCHAR Query[] = + {'S','E','L','E','C','T',' ','*',' ','F','R','O','M', + ' ','`','U','p','g','r','a','d','e','`',0}; + UINT rc = ERROR_SUCCESS; + MSIQUERY *view; + + if (check_unique_action(package,szFindRelatedProducts)) + { + TRACE("Skipping FindRelatedProducts action: already done on client side\n"); + return ERROR_SUCCESS; + } + else + register_unique_action(package,szFindRelatedProducts); + + rc = MSI_DatabaseOpenViewW(package->db, Query, &view); + if (rc != ERROR_SUCCESS) + return ERROR_SUCCESS; + + rc = MSI_IterateRecords(view, NULL, ITERATE_FindRelatedProducts, package); + msiobj_release(&view->hdr); + + return rc; +} diff --git a/reactos/dll/msi/version.rc b/reactos/dll/msi/version.rc new file mode 100644 index 00000000000..d5164a4e08a --- /dev/null +++ b/reactos/dll/msi/version.rc @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2004 Christian Costa + * + * 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 + */ + +#define WINE_FILEDESCRIPTION_STR "Wine MSI dll" +#define WINE_FILENAME_STR "msi.dll" +#define WINE_FILEVERSION 3,1,4000,2435 +#define WINE_FILEVERSION_STR "3.1.4000.2435" +#define WINE_PRODUCTVERSION 3,1,4000,2435 +#define WINE_PRODUCTVERSION_STR "3.1.4000.2435" + +#include "wine/wine_common_ver.rc" diff --git a/reactos/dll/msi/where.c b/reactos/dll/msi/where.c new file mode 100644 index 00000000000..b11ebe79f13 --- /dev/null +++ b/reactos/dll/msi/where.c @@ -0,0 +1,482 @@ +/* + * Implementation of the Microsoft Installer (msi.dll) + * + * Copyright 2002 Mike McCormack 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "msi.h" +#include "msiquery.h" +#include "objbase.h" +#include "objidl.h" +#include "msipriv.h" +#include "winnls.h" + +#include "query.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msidb); + + +/* below is the query interface to a table */ + +typedef struct tagMSIWHEREVIEW +{ + MSIVIEW view; + MSIDATABASE *db; + MSIVIEW *table; + UINT row_count; + UINT *reorder; + struct expr *cond; +} MSIWHEREVIEW; + +static UINT WHERE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val ) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view; + + TRACE("%p %d %d %p\n", wv, row, col, val ); + + if( !wv->table ) + return ERROR_FUNCTION_FAILED; + + if( row > wv->row_count ) + return ERROR_NO_MORE_ITEMS; + + row = wv->reorder[ row ]; + + return wv->table->ops->fetch_int( wv->table, row, col, val ); +} + +static UINT WHERE_fetch_stream( struct tagMSIVIEW *view, UINT row, UINT col, IStream **stm ) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view; + + TRACE("%p %d %d %p\n", wv, row, col, stm ); + + if( !wv->table ) + return ERROR_FUNCTION_FAILED; + + if( row > wv->row_count ) + return ERROR_NO_MORE_ITEMS; + + row = wv->reorder[ row ]; + + return wv->table->ops->fetch_stream( wv->table, row, col, stm ); +} + +static UINT WHERE_set_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT val ) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view; + + TRACE("%p %d %d %04x\n", wv, row, col, val ); + + if( !wv->table ) + return ERROR_FUNCTION_FAILED; + + if( row > wv->row_count ) + return ERROR_NO_MORE_ITEMS; + + row = wv->reorder[ row ]; + + return wv->table->ops->set_int( wv->table, row, col, val ); +} + +static UINT INT_evaluate( UINT lval, UINT op, UINT rval ) +{ + switch( op ) + { + case OP_EQ: + return ( lval == rval ); + case OP_AND: + return ( lval && rval ); + case OP_OR: + return ( lval || rval ); + case OP_GT: + return ( lval > rval ); + case OP_LT: + return ( lval < rval ); + case OP_LE: + return ( lval <= rval ); + case OP_GE: + return ( lval >= rval ); + case OP_NE: + return ( lval != rval ); + case OP_ISNULL: + return ( !lval ); + case OP_NOTNULL: + return ( lval ); + default: + ERR("Unknown operator %d\n", op ); + } + return 0; +} + +static const WCHAR *STRING_evaluate( string_table *st, + MSIVIEW *table, UINT row, struct expr *expr, MSIRECORD *record ) +{ + UINT val = 0, r; + + switch( expr->type ) + { + case EXPR_COL_NUMBER_STRING: + r = table->ops->fetch_int( table, row, expr->u.col_number, &val ); + if( r != ERROR_SUCCESS ) + return NULL; + return msi_string_lookup_id( st, val ); + + case EXPR_SVAL: + return expr->u.sval; + + case EXPR_WILDCARD: + return MSI_RecordGetString( record, 1 ); + + default: + ERR("Invalid expression type\n"); + break; + } + return NULL; +} + +static UINT STRCMP_Evaluate( string_table *st, MSIVIEW *table, UINT row, + struct expr *cond, UINT *val, MSIRECORD *record ) +{ + int sr; + const WCHAR *l_str, *r_str; + + l_str = STRING_evaluate( st, table, row, cond->u.expr.left, record ); + r_str = STRING_evaluate( st, table, row, cond->u.expr.right, record ); + if( l_str == r_str ) + sr = 0; + else if( l_str && ! r_str ) + sr = 1; + else if( r_str && ! l_str ) + sr = -1; + else + sr = lstrcmpW( l_str, r_str ); + + *val = ( cond->u.expr.op == OP_EQ && ( sr == 0 ) ) || + ( cond->u.expr.op == OP_LT && ( sr < 0 ) ) || + ( cond->u.expr.op == OP_GT && ( sr > 0 ) ); + + return ERROR_SUCCESS; +} + +static UINT WHERE_evaluate( MSIDATABASE *db, MSIVIEW *table, UINT row, + struct expr *cond, UINT *val, MSIRECORD *record ) +{ + UINT r, lval, rval; + + if( !cond ) + return ERROR_SUCCESS; + + switch( cond->type ) + { + case EXPR_COL_NUMBER_STRING: + case EXPR_COL_NUMBER: + return table->ops->fetch_int( table, row, cond->u.col_number, val ); + + case EXPR_UVAL: + *val = cond->u.uval; + return ERROR_SUCCESS; + + case EXPR_COMPLEX: + r = WHERE_evaluate( db, table, row, cond->u.expr.left, &lval, record ); + if( r != ERROR_SUCCESS ) + return r; + r = WHERE_evaluate( db, table, row, cond->u.expr.right, &rval, record ); + if( r != ERROR_SUCCESS ) + return r; + *val = INT_evaluate( lval, cond->u.expr.op, rval ); + return ERROR_SUCCESS; + + case EXPR_STRCMP: + return STRCMP_Evaluate( db->strings, table, row, cond, val, record ); + + case EXPR_WILDCARD: + *val = MSI_RecordGetInteger( record, 1 ); + return ERROR_SUCCESS; + + default: + ERR("Invalid expression type\n"); + break; + } + + return ERROR_SUCCESS; + +} + +static UINT WHERE_execute( struct tagMSIVIEW *view, MSIRECORD *record ) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view; + UINT count = 0, r, val, i; + MSIVIEW *table = wv->table; + + TRACE("%p %p\n", wv, record); + + if( !table ) + return ERROR_FUNCTION_FAILED; + + r = table->ops->execute( table, record ); + if( r != ERROR_SUCCESS ) + return r; + + r = table->ops->get_dimensions( table, &count, NULL ); + if( r != ERROR_SUCCESS ) + return r; + + msi_free( wv->reorder ); + wv->reorder = msi_alloc( count*sizeof(UINT) ); + if( !wv->reorder ) + return ERROR_FUNCTION_FAILED; + + wv->row_count = 0; + for( i=0; idb, table, i, wv->cond, &val, record ); + if( r != ERROR_SUCCESS ) + return r; + if( val ) + wv->reorder[ wv->row_count ++ ] = i; + } + + return ERROR_SUCCESS; +} + +static UINT WHERE_close( struct tagMSIVIEW *view ) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view; + + TRACE("%p\n", wv ); + + if( !wv->table ) + return ERROR_FUNCTION_FAILED; + + msi_free( wv->reorder ); + wv->reorder = NULL; + + return wv->table->ops->close( wv->table ); +} + +static UINT WHERE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols ) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view; + + TRACE("%p %p %p\n", wv, rows, cols ); + + if( !wv->table ) + return ERROR_FUNCTION_FAILED; + + if( rows ) + { + if( !wv->reorder ) + return ERROR_FUNCTION_FAILED; + *rows = wv->row_count; + } + + return wv->table->ops->get_dimensions( wv->table, NULL, cols ); +} + +static UINT WHERE_get_column_info( struct tagMSIVIEW *view, + UINT n, LPWSTR *name, UINT *type ) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view; + + TRACE("%p %d %p %p\n", wv, n, name, type ); + + if( !wv->table ) + return ERROR_FUNCTION_FAILED; + + return wv->table->ops->get_column_info( wv->table, n, name, type ); +} + +static UINT WHERE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, + MSIRECORD *rec ) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view; + + TRACE("%p %d %p\n", wv, eModifyMode, rec ); + + if( !wv->table ) + return ERROR_FUNCTION_FAILED; + + return wv->table->ops->modify( wv->table, eModifyMode, rec ); +} + +static UINT WHERE_delete( struct tagMSIVIEW *view ) +{ + MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view; + + TRACE("%p\n", wv ); + + if( wv->table ) + wv->table->ops->delete( wv->table ); + wv->table = 0; + + msi_free( wv->reorder ); + wv->reorder = NULL; + wv->row_count = 0; + + msiobj_release( &wv->db->hdr ); + msi_free( wv ); + + return ERROR_SUCCESS; +} + + +MSIVIEWOPS where_ops = +{ + WHERE_fetch_int, + WHERE_fetch_stream, + WHERE_set_int, + NULL, + WHERE_execute, + WHERE_close, + WHERE_get_dimensions, + WHERE_get_column_info, + WHERE_modify, + WHERE_delete +}; + +static UINT WHERE_VerifyCondition( MSIDATABASE *db, MSIVIEW *table, struct expr *cond, + UINT *valid ) +{ + UINT r, val = 0; + + switch( cond->type ) + { + case EXPR_COLUMN: + r = VIEW_find_column( table, cond->u.column, &val ); + if( r == ERROR_SUCCESS ) + { + UINT type = 0; + r = table->ops->get_column_info( table, val, NULL, &type ); + if( r == ERROR_SUCCESS ) + { + if (type&MSITYPE_STRING) + cond->type = EXPR_COL_NUMBER_STRING; + else + cond->type = EXPR_COL_NUMBER; + cond->u.col_number = val; + *valid = 1; + } + else + *valid = 0; + } + else + { + *valid = 0; + ERR("Couldn't find column %s\n", debugstr_w( cond->u.column ) ); + } + break; + case EXPR_COMPLEX: + r = WHERE_VerifyCondition( db, table, cond->u.expr.left, valid ); + if( r != ERROR_SUCCESS ) + return r; + if( !*valid ) + return ERROR_SUCCESS; + r = WHERE_VerifyCondition( db, table, cond->u.expr.right, valid ); + if( r != ERROR_SUCCESS ) + return r; + + /* check the type of the comparison */ + if( ( cond->u.expr.left->type == EXPR_SVAL ) || + ( cond->u.expr.left->type == EXPR_COL_NUMBER_STRING ) || + ( cond->u.expr.right->type == EXPR_SVAL ) || + ( cond->u.expr.right->type == EXPR_COL_NUMBER_STRING ) ) + { + switch( cond->u.expr.op ) + { + case OP_EQ: + case OP_GT: + case OP_LT: + break; + default: + *valid = FALSE; + return ERROR_INVALID_PARAMETER; + } + + /* FIXME: check we're comparing a string to a column */ + + cond->type = EXPR_STRCMP; + } + + break; + case EXPR_IVAL: + *valid = 1; + cond->type = EXPR_UVAL; + cond->u.uval = cond->u.ival + (1<<15); + break; + case EXPR_WILDCARD: + *valid = 1; + break; + case EXPR_SVAL: + *valid = 1; + break; + default: + ERR("Invalid expression type\n"); + *valid = 0; + break; + } + + return ERROR_SUCCESS; +} + +UINT WHERE_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table, + struct expr *cond ) +{ + MSIWHEREVIEW *wv = NULL; + UINT count = 0, r, valid = 0; + + TRACE("%p\n", table ); + + r = table->ops->get_dimensions( table, NULL, &count ); + if( r != ERROR_SUCCESS ) + { + ERR("can't get table dimensions\n"); + return r; + } + + if( cond ) + { + r = WHERE_VerifyCondition( db, table, cond, &valid ); + if( r != ERROR_SUCCESS ) + return r; + if( !valid ) + return ERROR_FUNCTION_FAILED; + } + + wv = msi_alloc_zero( sizeof *wv ); + if( !wv ) + return ERROR_FUNCTION_FAILED; + + /* fill the structure */ + wv->view.ops = &where_ops; + msiobj_addref( &db->hdr ); + wv->db = db; + wv->table = table; + wv->row_count = 0; + wv->reorder = NULL; + wv->cond = cond; + *view = (MSIVIEW*) wv; + + return ERROR_SUCCESS; +} diff --git a/reactos/dll/msimg32/Makefile.in b/reactos/dll/msimg32/Makefile.in new file mode 100644 index 00000000000..3d097d3e85a --- /dev/null +++ b/reactos/dll/msimg32/Makefile.in @@ -0,0 +1,13 @@ +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = ../.. +SRCDIR = @srcdir@ +VPATH = @srcdir@ +MODULE = msimg32.dll +IMPORTLIB = libmsimg32.$(IMPLIBEXT) +IMPORTS = gdi32 kernel32 + +C_SRCS = msimg32_main.c + +@MAKE_DLL_RULES@ + +### Dependencies: diff --git a/reactos/dll/msimg32/msimg32.spec b/reactos/dll/msimg32/msimg32.spec new file mode 100644 index 00000000000..76b5075f8e9 --- /dev/null +++ b/reactos/dll/msimg32/msimg32.spec @@ -0,0 +1,5 @@ +@ stdcall AlphaBlend(long long long long long long long long long long long) gdi32.GdiAlphaBlend +@ stdcall -private DllInitialize(long long ptr) DllMain +@ stdcall GradientFill(long ptr long ptr long long) gdi32.GdiGradientFill +@ stdcall TransparentBlt(long long long long long long long long long long long) gdi32.GdiTransparentBlt +@ stdcall vSetDdrawflag() diff --git a/reactos/dll/msimg32/msimg32.xml b/reactos/dll/msimg32/msimg32.xml new file mode 100644 index 00000000000..7fd1e798638 --- /dev/null +++ b/reactos/dll/msimg32/msimg32.xml @@ -0,0 +1,12 @@ + + + . + include + + + ntdll + kernel32 + gdi32 + msimg32_main.c + msimg32.spec + diff --git a/reactos/dll/msimg32/msimg32_main.c b/reactos/dll/msimg32/msimg32_main.c new file mode 100644 index 00000000000..4586a3046d9 --- /dev/null +++ b/reactos/dll/msimg32/msimg32_main.c @@ -0,0 +1,49 @@ +/* + * Copyright 2002 Uwe Bonnes + * + * 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 + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winerror.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msimg32); + +/*********************************************************************** + * DllInitialize (MSIMG32.@) + * + * MSIMG32 initialisation routine. + */ +BOOL WINAPI DllMain( HINSTANCE inst, DWORD reason, LPVOID reserved ) +{ + if (reason == DLL_PROCESS_ATTACH) DisableThreadLibraryCalls( inst ); + return TRUE; +} + + +/****************************************************************************** + * vSetDdrawflag (MSIMG32.@) + */ +void WINAPI vSetDdrawflag(void) +{ + static unsigned int vDrawflag=1; + FIXME("stub: vSetDrawFlag %u\n", vDrawflag); + SetLastError(ERROR_CALL_NOT_IMPLEMENTED); +} diff --git a/reactos/dll/msvcrt/dllmain.c b/reactos/dll/msvcrt/dllmain.c new file mode 100644 index 00000000000..a2b30a71c2c --- /dev/null +++ b/reactos/dll/msvcrt/dllmain.c @@ -0,0 +1,142 @@ +/* $Id$ + * + * dllmain.c + * + * ReactOS MSVCRT.DLL Compatibility Library + * + * THIS SOFTWARE IS NOT COPYRIGHTED + * + * This source code is offered for use in the public domain. You may + * use, modify or distribute it freely. + * + * This code is distributed in the hope that it will be useful but + * WITHOUT ANY WARRANTY. ALL WARRENTIES, EXPRESS OR IMPLIED ARE HEREBY + * DISCLAMED. This includes but is not limited to warrenties of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * + * $Revision: 1.24 $ + * $Author$ + * $Date$ + * + */ + +#include +#include + +#define NDEBUG +#include + + +/* EXTERNAL PROTOTYPES ********************************************************/ + +//void __fileno_init(void); +extern BOOL __fileno_init(void); +extern int BlockEnvToEnvironA(void); +extern int BlockEnvToEnvironW(void); +extern void FreeEnvironment(char **environment); +extern void _atexit_cleanup(void); + +extern unsigned int _osver; +extern unsigned int _winminor; +extern unsigned int _winmajor; +extern unsigned int _winver; + +extern char* _acmdln; /* pointer to ascii command line */ +extern wchar_t* _wcmdln; /* pointer to wide character command line */ +#undef _environ +extern char** _environ; /* pointer to environment block */ +extern char** __initenv; /* pointer to initial environment block */ +extern wchar_t** _wenviron; /* pointer to environment block */ +extern wchar_t** __winitenv; /* pointer to initial environment block */ + + +/* LIBRARY GLOBAL VARIABLES ***************************************************/ + +HANDLE hHeap = NULL; /* handle for heap */ + + +/* LIBRARY ENTRY POINT ********************************************************/ + +BOOL +STDCALL +DllMain(PVOID hinstDll, ULONG dwReason, PVOID reserved) +{ + switch (dwReason) + { + case DLL_PROCESS_ATTACH://1 + /* initialize version info */ + //DPRINT1("Process Attach %d\n", nAttachCount); + //DPRINT1("Process Attach\n"); + _osver = GetVersion(); + _winmajor = (_osver >> 8) & 0xFF; + _winminor = _osver & 0xFF; + _winver = (_winmajor << 8) + _winminor; + _osver = (_osver >> 16) & 0xFFFF; + hHeap = HeapCreate(0, 100000, 0); + if (hHeap == NULL) + return FALSE; + if (!__fileno_init()) + return FALSE; + + /* create tls stuff */ + if (!CreateThreadData()) + return FALSE; + + if (BlockEnvToEnvironA() < 0) + return FALSE; + + if (BlockEnvToEnvironW() < 0) + { + FreeEnvironment(_environ); + return FALSE; + } + + _acmdln = _strdup(GetCommandLineA()); + _wcmdln = _wcsdup(GetCommandLineW()); + + /* FIXME: more initializations... */ + + /* FIXME: Initialization of the WINE code */ + msvcrt_init_mt_locks(); + + DPRINT("Attach done\n"); + break; + + case DLL_THREAD_ATTACH://2 + break; + + case DLL_THREAD_DETACH://4 + FreeThreadData(NULL); + break; + + case DLL_PROCESS_DETACH://0 + //DPRINT1("Detach %d\n", nAttachCount); + //DPRINT("Detach\n"); + /* FIXME: more cleanup... */ + _fcloseall(); + _atexit_cleanup(); + + /* destroy tls stuff */ + DestroyThreadData(); + + if (__winitenv && __winitenv != _wenviron) + FreeEnvironment((char**)__winitenv); + if (_wenviron) + FreeEnvironment((char**)_wenviron); + + if (__initenv && __initenv != _environ) + FreeEnvironment(__initenv); + if (_environ) + FreeEnvironment(_environ); + + /* destroy heap */ + HeapDestroy(hHeap); + + DPRINT("Detach done\n"); + break; + } + + return TRUE; +} + +/* EOF */ diff --git a/reactos/dll/msvcrt/msvcrt.def b/reactos/dll/msvcrt/msvcrt.def new file mode 100644 index 00000000000..a19d38ddab0 --- /dev/null +++ b/reactos/dll/msvcrt/msvcrt.def @@ -0,0 +1,846 @@ +; $Id$ +; +; ReactOS MSVCRT Compatibility Library +; +LIBRARY MSVCRT.DLL +EXPORTS +;---------------------------------------------------------------------- +; C++ Mangled Symbols +;---------------------------------------------------------------------- +$I10_OUTPUT=MSVCRT_I10_OUTPUT @1 +??0__non_rtti_object@@QAE@ABV0@@Z=__thiscall_MSVCRT___non_rtti_object_copy_ctor @2 +??0__non_rtti_object@@QAE@PBD@Z=__thiscall_MSVCRT___non_rtti_object_ctor @3 +??0bad_cast@@QAE@ABQBD@Z=__thiscall_MSVCRT_bad_cast_ctor @4 +??0bad_cast@@QAE@ABV0@@Z=__thiscall_MSVCRT_bad_cast_copy_ctor @5 +??0bad_typeid@@QAE@ABV0@@Z=__thiscall_MSVCRT_bad_typeid_copy_ctor @6 +??0bad_typeid@@QAE@PBD@Z=__thiscall_MSVCRT_bad_typeid_ctor @7 +??0exception@@QAE@ABQBD@Z=__thiscall_MSVCRT_exception_ctor @8 +??0exception@@QAE@ABV0@@Z=__thiscall_MSVCRT_exception_copy_ctor @9 +??0exception@@QAE@XZ=__thiscall_MSVCRT_exception_default_ctor @10 +??1__non_rtti_object@@UAE@XZ=__thiscall_MSVCRT___non_rtti_object_dtor @11 +??1bad_cast@@UAE@XZ=__thiscall_MSVCRT_bad_cast_dtor @12 +??1bad_typeid@@UAE@XZ=__thiscall_MSVCRT_bad_typeid_dtor @13 +??1exception@@UAE@XZ=__thiscall_MSVCRT_exception_dtor @14 +??1type_info@@UAE@XZ=__thiscall_MSVCRT_type_info_dtor @15 +??2@YAPAXI@Z=MSVCRT_operator_new @16 +??_U@YAPAXI@Z=MSVCRT_operator_new @36 +??3@YAXPAX@Z=MSVCRT_operator_delete @17 +??_V@YAXPAX@Z=MSVCRT_operator_delete @37 +??4__non_rtti_object@@QAEAAV0@ABV0@@Z=__thiscall_MSVCRT___non_rtti_object_opequals @18 +??4bad_cast@@QAEAAV0@ABV0@@Z=__thiscall_MSVCRT_bad_cast_opequals @19 +??4bad_typeid@@QAEAAV0@ABV0@@Z=__thiscall_MSVCRT_bad_typeid_opequals @20 +??4exception@@QAEAAV0@ABV0@@Z=__thiscall_MSVCRT_exception_opequals @21 +??8type_info@@QBEHABV0@@Z=__thiscall_MSVCRT_type_info_opequals_equals @22 +??9type_info@@QBEHABV0@@Z=__thiscall_MSVCRT_type_info_opnot_equals @23 +??_7__non_rtti_object@@6B@=MSVCRT___non_rtti_object_vtable @24 DATA +??_7bad_cast@@6B@=MSVCRT_bad_cast_vtable @25 DATA +??_7bad_typeid@@6B@=MSVCRT_bad_typeid_vtable @26 DATA +??_7exception@@6B@=MSVCRT_exception_vtable @27 DATA +??_E__non_rtti_object@@UAEPAXI@Z=__thiscall_MSVCRT___non_rtti_object_vector_dtor @28 +??_Ebad_cast@@UAEPAXI@Z=__thiscall_MSVCRT_bad_cast_vector_dtor @29 +??_Ebad_typeid@@UAEPAXI@Z=__thiscall_MSVCRT_bad_typeid_vector_dtor @30 +??_Eexception@@UAEPAXI@Z=__thiscall_MSVCRT_exception_vector_dtor @31 +??_G__non_rtti_object@@UAEPAXI@Z=__thiscall_MSVCRT___non_rtti_object_scalar_dtor @32 +??_Gbad_cast@@UAEPAXI@Z=__thiscall_MSVCRT_bad_cast_scalar_dtor @33 +??_Gbad_typeid@@UAEPAXI@Z=__thiscall_MSVCRT_bad_typeid_scalar_dtor @34 +??_Gexception@@UAEPAXI@Z=__thiscall_MSVCRT_exception_scalar_dtor @35 +?_query_new_handler@@YAP6AHI@ZXZ=MSVCRT__query_new_handler @38 +?_query_new_mode@@YAHXZ=MSVCRT__query_new_mode @39 +?_set_new_handler@@YAP6AHI@ZP6AHI@Z@Z=MSVCRT__set_new_handler @40 +?_set_new_mode@@YAHH@Z=MSVCRT__set_new_mode @41 +?_set_se_translator@@YAP6AXIPAU_EXCEPTION_POINTERS@@@ZP6AXI0@Z@Z=MSVCRT__set_se_translator @42 +?before@type_info@@QBEHABV1@@Z=__thiscall_MSVCRT_type_info_before @43 +?raw_name@type_info@@QBEPBDXZ=__thiscall_MSVCRT_type_info_raw_name @45 +?set_new_handler@@YAP6AXXZP6AXXZ@Z=MSVCRT__set_new_handler @46 +?set_terminate@@YAP6AXXZP6AXXZ@Z=MSVCRT_set_terminate @47 +?set_unexpected@@YAP6AXXZP6AXXZ@Z=MSVCRT_set_unexpected @48 +?terminate@@YAXXZ=MSVCRT_terminate @49 +?unexpected@@YAXXZ=MSVCRT_unexpected @50 +?what@exception@@UBEPBDXZ=__thiscall_MSVCRT_what_exception @51 +??1type_info@@UAE@XZ=MSVCRT_type_info_dtor@4 + +?name@type_info@@QBEPBDXZ=__thiscall_MSVCRT_type_info_name @44 +;??0bad_cast@@QAE@PBD@Z +;??0bad_cast@@AAE@PBQBD@Z +;??_Fbad_cast@@QAEXXZ +;??_Fbad_typeid@@QAEXXZ + +;---------------------------------------------------------------------- +; C Undecorated Symbols +;---------------------------------------------------------------------- +_CIacos +_CIasin +_CIatan +_CIatan2 +_CIcos +_CIcosh +_CIexp +_CIfmod +_CIlog +_CIlog10 +_CIpow +_CIsin +_CIsinh +_CIsqrt +_CItan +_CItanh +_CxxThrowException +_EH_prolog +_Getdays +_Getmonths +_Gettnames +_HUGE DATA +_Strftime +_XcptFilter +__CxxCallUnwindDtor=stub +__CxxDetectRethrow +__CxxExceptionFilter=stub +__CxxFrameHandler +__CxxLongjmpUnwind=stub +__CxxQueryExceptionSize +__CxxRegisterExceptionObject=stub +__CxxUnregisterExceptionObject=stub +__DestructExceptionObject=stub +__RTCastToVoid=MSVCRT___RTCastToVoid +__RTDynamicCast=MSVCRT___RTDynamicCast +__RTtypeid=MSVCRT___RTtypeid +__STRINGTOLD=stub +__argc DATA +__argv DATA +__badioinfo DATA +__crtCompareStringA +__crtGetLocaleInfoW +__crtLCMapStringA +__dllonexit +__doserrno +__fpecode +__getmainargs +__initenv DATA +__isascii=NTDLL.__isascii +__iscsym=NTDLL.__iscsym +__iscsymf=NTDLL.__iscsymf +__lc_codepage +__lc_collate_cp +__lc_handle +__lconv_init +__mb_cur_max DATA +__p___argc +__p___argv +__p___wargv +__p___initenv +__p___mb_cur_max +__p___winitenv +__p__acmdln +__p__amblksiz +__p__commode +__p__daylight +__p__dstbias +__p__environ +__p__fileinfo=stub +__p__fmode +__p__iob +__p__mbcasemap=stub +__p__mbctype=stub +__p__osver +__p__pctype +__p__pgmptr +__p__pwctype +__p__timezone +__p__tzname=stub +__p__wcmdln +__p__wenviron +__p__winmajor +__p__winminor +__p__winver +__p__wpgmptr +__pioinfo +__pxcptinfoptrs +__set_app_type +__setlc_active DATA +__setusermatherr +__threadhandle +__threadid +__toascii=NTDLL.__toascii +__unDName=stub +__unDNameEx=stub +__unguarded_readlc_active DATA +__wargv +__wgetmainargs +__winitenv +___lc_codepage_func=stub +___lc_handle_func=stub +___mb_cur_max_func=stub +___setlc_active_func=stub +___unguarded_readlc_active_add_func=stub +__crtCompareStringW=stub +__crtGetStringTypeW=stub +__crtLCMapStringW=stub +__iob_func=stub +__pctype_func=stub +__uncaught_exception=stub +__wcserror=stub +_abnormal_termination +_access +_acmdln DATA +_adj_fdiv_m16i=stub +_adj_fdiv_m32 = _adj_fdiv_m32@4 +_adj_fdiv_m32i = _adj_fdiv_m32i@4 +_adj_fdiv_m64 = _adj_fdiv_m64@8 +_adj_fdiv_r +_adj_fdivr_m16i=stub +_adj_fdivr_m32 = _adj_fdivr_m32@4 +_adj_fdivr_m32i = _adj_fdivr_m32i@4 +_adj_fdivr_m64 = _adj_fdivr_m64@8 +_adj_fpatan +_adj_fprem=stub +_adj_fprem1=stub +_adj_fptan=stub +_adjust_fdiv DATA +_aexit_rtn +_aligned_free=stub +_aligned_malloc=stub +_aligned_offset_malloc=stub +_aligned_offset_realloc=stub +_aligned_realloc=stub +_amsg_exit +_assert +_atodbl=stub +_atoi64=NTDLL._atoi64 +_atoldbl=stub +_beep +_beginthread +_beginthreadex +_c_exit +_cabs +_callnewh=stub +_cexit +_cgets +_cgetws=stub +_chdir +_chdrive +_chgsign +_chkesp=stub +_chmod +_chsize +_clearfp +_close +_commit +_commode DATA +_control87 +_controlfp +_copysign +_cprintf +_cputs +_cputws=stub +_creat +_cscanf +_ctime64=stub +_ctype DATA +_cwait +_cwprintf=stub +_cwscanf=stub +_daylight DATA +_dstbias=stub +_dup +_dup2 +_ecvt +_endthread +_endthreadex +_environ=stub +_eof +_errno +_except_handler2 +_except_handler3 +_execl +_execle +_execlp +_execlpe +_execv +_execve +_execvp +_execvpe +_exit +_expand +_fcloseall +_fcvt +_fdopen +_fgetchar +_fgetwchar +_filbuf +_fileinfo=stub DATA +_filelength +_filelengthi64 +_fileno +_findclose +_findfirst +_findfirst64=stub +_findfirsti64 +_findnext +_findnext64=stub +_findnexti64 +_finite +_flsbuf +_flushall +_fmode DATA +_fpclass +_fpieee_flt +_fpreset +_fputchar +_fputwchar +_fsopen +_fstat +_fstat64=stub +_fstati64 +_ftime +_ftime64=stub +_ftol=NTDLL._ftol +_fullpath +_futime +_futime64=stub +_gcvt +_get_osfhandle +_get_sbh_threshold=stub +_getch +_getche +_getcwd +_getdcwd +_getdiskfree +_getdllprocaddr +_getdrive +_getdrives +_get_heap_handle=stub +_getmaxstdio=stub +_getmbcp=stub +_getpid +_getsystime +_getw +_getwch=stub +_getwche=stub +_getws=stub +_gmtime64=stub +_global_unwind2 +_heapadd +_heapchk +_heapmin +_heapset +_heapused=stub +_heapwalk +_hypot +_i64toa=NTDLL._i64toa +_i64tow=NTDLL._i64tow +_initterm +_inp=stub +_inpd=stub +_inpw=stub +_iob +_isatty +_isctype +_ismbbalnum +_ismbbalpha +_ismbbgraph +_ismbbkalnum +_ismbbkana +_ismbbkprint=stub +_ismbbkpunct +_ismbblead +_ismbbprint +_ismbbpunct +_ismbbtrail +_ismbcalnum +_ismbcalpha +_ismbcdigit +_ismbcgraph=stub +_ismbchira +_ismbckata +_ismbcl0 +_ismbcl1 +_ismbcl2 +_ismbclegal +_ismbclower +_ismbcprint +_ismbcpunct=stub +_ismbcspace +_ismbcsymbol +_ismbcupper +_ismbslead +_ismbstrail +_isnan +_itoa=NTDLL._itoa +_itow=NTDLL._itow +_j0 +_j1 +_jn +_kbhit +_localtime64=stub +_lfind +_loaddll +_local_unwind2 +_lock +_locking +_logb +_longjmpex=stub +_lrotl +_lrotr +_lsearch +_lseek +_lseeki64 +_ltoa=NTDLL._ltoa +_ltow +_makepath +_mbbtombc +_mbbtype +_mbcasemap=stub +_mbccpy +_mbcjistojms +_mbcjmstojis +_mbclen +_mbctohira +_mbctokata +_mbctolower +_mbctombb +_mbctoupper +_mbctype DATA +_mbsbtype +_mbscat +_mbschr +_mbscmp +_mbscoll +_mbscpy +_mbscspn +_mbsdec +_mbsdup +_mbsicmp +_mbsicoll +_mbsinc +_mbslen +_mbslwr +_mbsnbcat +_mbsnbcmp +_mbsnbcnt +_mbsnbcoll +_mbsnbcpy +_mbsnbicmp +_mbsnbicoll +_mbsnbset +_mbsncat +_mbsnccnt +_mbsncmp +_mbsncoll +_mbsncpy +_mbsnextc +_mbsnicmp +_mbsnicoll +_mbsninc +_mbsnset +_mbspbrk +_mbsrchr +_mbsrev +_mbsset +_mbsspn +_mbsspnp +_mbsstr +_mbstok +_mbstrlen +_mbsupr +_memccpy +_memicmp=NTDLL._memicmp +_mkdir +_mktemp +_mktime64=stub +_msize +_nextafter +_onexit +_open +_open_osfhandle +_osver DATA +_osplatform=stub +_outp=stub +_outpd=stub +_outpw=stub +_pclose +_pctype DATA +_pgmptr DATA +_pipe +_popen +_purecall +_putch +_putenv +_putw +_putwch=stub +_putws +_pwctype DATA +_read +_resetstkoflw=stub +_rmdir +_rmtmp +_rotl +_rotr +_safe_fdiv=stub +_safe_fdivr=stub +_safe_fprem=stub +_safe_fprem1=stub +_scalb +_scprintf=stub +_scwprintf=stub +_searchenv +_seh_longjmp_unwind=_seh_longjmp_unwind@4 +_set_error_mode=stub +_set_SSE2_enable=stub +_set_sbh_threshold=stub +_seterrormode +_setjmp +_setjmp3 +_setmaxstdio=stub +_setmbcp +_setmode +_setsystime +_sleep +_snprintf=crt__snprintf +_snscanf=stub +_snwscanf=stub +_snwprintf=crt__snwprintf +_sopen +_spawnl +_spawnle +_spawnlp +_spawnlpe +_spawnv +_spawnve +_spawnvp +_spawnvpe +_splitpath=NTDLL._splitpath +_stat +_stat64=stub +_stati64 +_statusfp +_strcmpi +_strdate +_strdup +_strerror +_stricmp=NTDLL._stricmp +_stricoll +_strlwr=NTDLL._strlwr +_strncoll +_strnicmp=NTDLL._strnicmp +_strnicoll +_strnset +_strrev +_strset +_strtime +_strtoi64=stub +_strtoui64=strtoull +_strupr +_swab +_sys_errlist DATA +_sys_nerr DATA +_tell +_telli64 +_tempnam +_time64=stub +_timezone DATA +_tolower=NTDLL._tolower +_toupper=NTDLL._toupper +_tzname DATA +_tzset +_ui64toa=NTDLL._i64toa +_ui64tow=NTDLL._i64tow +_ultoa=NTDLL._ultoa +_ultow=NTDLL._ultow +_umask +_ungetch +_unlink +_unloaddll +_unlock +_utime +_utime64=stub +_vscprintf=stub +_vscwprintf=stub +_vsnprintf +_vsnwprintf +_waccess +_wasctime +_wchdir +_wchmod +_wcmdln DATA +_wcreat +_wcsdup +_wcserror=stub +_wcsicmp +_wcsicoll +_wcslwr +_wcsncoll +_wcsnicmp +_wcsnicoll +_wcsnset +_wcsrev +_wcsset +_wcstoi64=stub +_wcstoui64=stub +_wctime64=stub +_wcsupr +_wctime +_wenviron=stub +_wexecl +_wexecle +_wexeclp +_wexeclpe +_wexecv +_wexecve +_wexecvp +_wexecvpe +_wfdopen +_wfindfirst +_wfindfirst64=stub +_wfindfirsti64 +_wfindnext +_wfindnext64=stub +_wfindnexti64 +_wfopen +_wfreopen +_wfsopen +_wfullpath +_wgetcwd +_wgetdcwd +_wgetenv +_winmajor DATA +_winminor DATA +_winver DATA +_wmakepath +_wmkdir +_wmktemp +_wopen +_wperror +_wpgmptr=stub DATA +_wpopen +_wputenv +_wremove +_wrename +_write +_wrmdir +_wsearchenv +_wsetlocale=stub +_wsopen +_wspawnl +_wspawnle +_wspawnlp +_wspawnlpe +_wspawnv +_wspawnve +_wspawnvp +_wspawnvpe +_wsplitpath +_wstat +_wstat64=stub +_wstati64 +_wstrdate +_wstrtime +_wsystem=stub +_wtempnam +_wtmpnam +_wtof=stub +_wtoi=NTDLL._wtoi +_wtoi64=NTDLL._wtoi64 +_wtol +_wunlink +_wutime +_wutime64=stub +_y0 +_y1 +_yn +abort +abs=NTDLL.abs +acos +asctime +asin +atan=NTDLL.atan +atan2 +atexit +atof +atoi=NTDLL.atoi +atol=NTDLL.atol +bsearch=NTDLL.bsearch +calloc +ceil=NTDLL.ceil +clearerr +clock +cos=NTDLL.cos +cosh +ctime +difftime +div +exit +exp +fabs=NTDLL.fabs +fclose +feof +ferror +fflush +fgetc +fgetpos +fgets +fgetwc +fgetws +floor=NTDLL.floor +fmod +fopen +fprintf +fputc +fputs +fputwc +fputws +fread +free +freopen +frexp +fscanf +fseek +fsetpos +ftell +fwprintf +fwrite +fwscanf +getc +getchar +getenv +gets +getwc +getwchar +gmtime +is_wctype +isalnum=NTDLL.isalnum +isalpha=NTDLL.isalpha +iscntrl=NTDLL.iscntrl +isdigit=NTDLL.isdigit +isgraph=NTDLL.isgraph +isleadbyte +islower=NTDLL.islower +isprint=NTDLL.isprint +ispunct=NTDLL.ispunct +isspace=NTDLL.isspace +isupper=NTDLL.isupper +iswalnum +iswalpha=NTDLL.iswalpha +iswascii +iswcntrl +iswctype=NTDLL.iswctype +iswdigit=NTDLL.iswdigit +iswgraph +iswlower=NTDLL.iswlower +iswprint +iswpunct +iswspace=NTDLL.iswspace +iswupper +iswxdigit=NTDLL.iswxdigit +isxdigit=NTDLL.isxdigit +labs=NTDLL.labs +ldexp +ldiv +localeconv +localtime +log=NTDLL.log +log10 +longjmp +malloc +mblen +mbstowcs=NTDLL.mbstowcs +mbtowc +memchr +memcmp +memcpy=memmove +memmove +memset +mktime +modf +perror +pow=NTDLL.pow +printf +putc +putchar +puts +putwc +putwchar +qsort=NTDLL.qsort +raise +rand +realloc +remove +rename +rewind +scanf +setbuf +setlocale +setvbuf +signal +sin=NTDLL.sin +sinh +sprintf=crt_sprintf +sqrt=NTDLL.sqrt +srand +sscanf=crt_sscanf +strcat +strchr +strcmp=NTDLL.strcmp +strcoll +strcpy +strcspn +strerror +strftime +strlen +strncat +strncmp +strncpy +strpbrk +strrchr +strspn +strstr +strtod +strtok +strtol=NTDLL.strtol +strtoul +strxfrm +swprintf=crt_swprintf +swscanf +system +tan=NTDLL.tan +tanh +time +tmpfile +tmpnam +tolower +toupper=NTDLL.toupper +towlower=NTDLL.towlower +towupper=NTDLL.towupper +ungetc +ungetwc +vfprintf +vfwprintf +vprintf +vsprintf +vswprintf +vwprintf +wcscat +wcschr +wcscmp +wcscoll +wcscpy +wcscspn +wcsftime +wcslen +wcsncat +wcsncmp +wcsncpy +wcspbrk +wcsrchr +wcsspn +wcsstr +wcstod +wcstok +wcstol=NTDLL.wcstol +wcstombs +wcstoul=NTDLL.wcstoul +wcsxfrm +wctomb +wprintf +wscanf + +; EOF diff --git a/reactos/dll/msvcrt/msvcrt.rc b/reactos/dll/msvcrt/msvcrt.rc new file mode 100644 index 00000000000..46b222c09ee --- /dev/null +++ b/reactos/dll/msvcrt/msvcrt.rc @@ -0,0 +1,5 @@ +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "MSVCRT.DLL Compatibility Library\0" +#define REACTOS_STR_INTERNAL_NAME "msvcrt\0" +#define REACTOS_STR_ORIGINAL_FILENAME "msvcrt.dll\0" +#include diff --git a/reactos/dll/msvcrt/msvcrt.xml b/reactos/dll/msvcrt/msvcrt.xml new file mode 100644 index 00000000000..b2c4e1ac6b3 --- /dev/null +++ b/reactos/dll/msvcrt/msvcrt.xml @@ -0,0 +1,25 @@ + + -nostartfiles + --enable-stdcall-fixup + -nostdlib + -lgcc + + . + include + + + 0x600 + 0x501 + + + + + crt + string + kernel32 + ntdll + wine + precomp.h + dllmain.c + msvcrt.rc + diff --git a/reactos/dll/msvcrt/precomp.h b/reactos/dll/msvcrt/precomp.h new file mode 100644 index 00000000000..acfc877e21c --- /dev/null +++ b/reactos/dll/msvcrt/precomp.h @@ -0,0 +1,9 @@ +#ifndef _CRT_PRECOMP_H +#define _CRT_PRECOMP_H + +#include +#include +#include +#include + +#endif /* _CRT_PRECOMP_H */ diff --git a/reactos/dll/msvcrt20/msvcrt20.c b/reactos/dll/msvcrt20/msvcrt20.c new file mode 100644 index 00000000000..18693a6ea7c --- /dev/null +++ b/reactos/dll/msvcrt20/msvcrt20.c @@ -0,0 +1,51 @@ +/* + * msvcrt20 implementation + * + * Copyright 2002 Alexandre Julliard + * + * 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 + +#include "windef.h" +#include "winbase.h" + +extern void __getmainargs(int *argc, char** *argv, char** *envp, + int expand_wildcards, int *new_mode); +extern void __wgetmainargs(int *argc, WCHAR** *wargv, WCHAR** *wenvp, + int expand_wildcards, int *new_mode); + +/********************************************************************* + * __getmainargs (MSVCRT20.@) + * + * new_mode is not a pointer in msvcrt20. + */ +void MSVCRT20__getmainargs( int *argc, char** *argv, char** *envp, + int expand_wildcards, int new_mode ) +{ + __getmainargs( argc, argv, envp, expand_wildcards, &new_mode ); +} + +/********************************************************************* + * __wgetmainargs (MSVCRT20.@) + * + * new_mode is not a pointer in msvcrt20. + */ +void MSVCRT20__wgetmainargs( int *argc, WCHAR** *wargv, WCHAR** *wenvp, + int expand_wildcards, int new_mode ) +{ + __wgetmainargs( argc, wargv, wenvp, expand_wildcards, &new_mode ); +} diff --git a/reactos/dll/msvcrt20/msvcrt20.def b/reactos/dll/msvcrt20/msvcrt20.def new file mode 100644 index 00000000000..0fc2525eb53 --- /dev/null +++ b/reactos/dll/msvcrt20/msvcrt20.def @@ -0,0 +1,708 @@ +; File generated automatically from msvcrt20/msvcrt20.spec; do not edit! + +LIBRARY msvcrt20.dll + +EXPORTS + ??2@YAPAXI@Z=msvcrt.??2@YAPAXI@Z @80 + ??3@YAXPAX@Z=msvcrt.??3@YAXPAX@Z @81 + ?_query_new_handler@@YAP6AHI@ZXZ=msvcrt.?_query_new_handler@@YAP6AHI@ZXZ @227 + ?_query_new_mode@@YAHXZ=msvcrt.?_query_new_mode@@YAHXZ @228 + ?_set_new_handler@@YAP6AHI@ZP6AHI@Z@Z=msvcrt.?_set_new_handler@@YAP6AHI@ZP6AHI@Z@Z @229 + ?_set_new_mode@@YAHH@Z=msvcrt.?_set_new_mode@@YAHH@Z @230 + ?_set_se_translator@@YAP6AXIPAU_EXCEPTION_POINTERS@@@ZP6AXI0@Z@Z=msvcrt.?_set_se_translator@@YAP6AXIPAU_EXCEPTION_POINTERS@@@ZP6AXI0@Z@Z @231 + ?set_terminate@@YAP6AXXZP6AXXZ@Z=msvcrt.?set_terminate@@YAP6AXXZP6AXXZ@Z @366 + ?set_unexpected@@YAP6AXXZP6AXXZ@Z=msvcrt.?set_unexpected@@YAP6AXXZP6AXXZ@Z @367 + ?terminate@@YAXXZ=msvcrt.?terminate@@YAXXZ @410 + ?unexpected@@YAXXZ=msvcrt.?unexpected@@YAXXZ @419 + _CIacos=msvcrt._CIacos @439 + _CIasin=msvcrt._CIasin @440 + _CIatan=msvcrt._CIatan @441 + _CIatan2=msvcrt._CIatan2 @442 + _CIcos=msvcrt._CIcos @443 + _CIcosh=msvcrt._CIcosh @444 + _CIexp=msvcrt._CIexp @445 + _CIfmod=msvcrt._CIfmod @446 + _CIlog=msvcrt._CIlog @447 + _CIlog10=msvcrt._CIlog10 @448 + _CIpow=msvcrt._CIpow @449 + _CIsin=msvcrt._CIsin @450 + _CIsinh=msvcrt._CIsinh @451 + _CIsqrt=msvcrt._CIsqrt @452 + _CItan=msvcrt._CItan @453 + _CItanh=msvcrt._CItanh @454 + _CxxThrowException=msvcrt._CxxThrowException @455 + _HUGE=msvcrt._HUGE @456 DATA + _XcptFilter=msvcrt._XcptFilter @457 + __CxxFrameHandler=msvcrt.__CxxFrameHandler @458 + __CxxLongjmpUnwind@4=msvcrt.__CxxLongjmpUnwind @459 + __STRINGTOLD=msvcrt.__STRINGTOLD @460 + __argc=msvcrt.__argc @461 DATA + __argv=msvcrt.__argv @462 DATA + __dllonexit=msvcrt.__dllonexit @463 + __doserrno=msvcrt.__doserrno @464 + __fpecode=msvcrt.__fpecode @465 + __getmainargs=MSVCRT20__getmainargs @466 + __initenv=msvcrt.__initenv @467 DATA + __isascii=msvcrt.__isascii @468 + __iscsym=msvcrt.__iscsym @469 + __iscsymf=msvcrt.__iscsymf @470 + __lconv_init=msvcrt.__lconv_init @471 + __mb_cur_max=msvcrt.__mb_cur_max @472 DATA + __p___argc=msvcrt.__p___argc @473 + __p___argv=msvcrt.__p___argv @474 + __p___initenv=msvcrt.__p___initenv @475 + __p___mb_cur_max=msvcrt.__p___mb_cur_max @476 + __p___wargv=msvcrt.__p___wargv @477 + __p___winitenv=msvcrt.__p___winitenv @478 + __p__acmdln=msvcrt.__p__acmdln @479 + __p__amblksiz=msvcrt.__p__amblksiz @480 + __p__commode=msvcrt.__p__commode @481 + __p__daylight=msvcrt.__p__daylight @482 + __p__environ=msvcrt.__p__environ @483 + __p__fmode=msvcrt.__p__fmode @484 + __p__iob=msvcrt.__p__iob @485 + __p__mbctype=msvcrt.__p__mbctype @486 + __p__osver=msvcrt.__p__osver @487 + __p__pctype=msvcrt.__p__pctype @488 + __p__pgmptr=msvcrt.__p__pgmptr @489 + __p__pwctype=msvcrt.__p__pwctype @490 + __p__timezone=msvcrt.__p__timezone @491 + __p__tzname=msvcrt.__p__tzname @492 + __p__wcmdln=msvcrt.__p__wcmdln @493 + __p__wenviron=msvcrt.__p__wenviron @494 + __p__winmajor=msvcrt.__p__winmajor @495 + __p__winminor=msvcrt.__p__winminor @496 + __p__winver=msvcrt.__p__winver @497 + __p__wpgmptr=msvcrt.__p__wpgmptr @498 + __pxcptinfoptrs=msvcrt.__pxcptinfoptrs @499 + __threadhandle=msvcrt.__threadhandle @501 + __threadid=msvcrt.__threadid @502 + __toascii=msvcrt.__toascii @503 + __wargv=msvcrt.__wargv @504 DATA + __wgetmainargs=MSVCRT20__wgetmainargs @505 + __winitenv=msvcrt.__winitenv @506 DATA + _abnormal_termination=msvcrt._abnormal_termination @507 + _access=msvcrt._access @508 + _acmdln=msvcrt._acmdln @509 DATA + _adj_fdiv_m16i=msvcrt._adj_fdiv_m16i @510 + _adj_fdiv_m32=msvcrt._adj_fdiv_m32 @511 + _adj_fdiv_m32i=msvcrt._adj_fdiv_m32i @512 + _adj_fdiv_m64=msvcrt._adj_fdiv_m64 @513 + _adj_fdiv_r=msvcrt._adj_fdiv_r @514 + _adj_fdivr_m16i=msvcrt._adj_fdivr_m16i @515 + _adj_fdivr_m32=msvcrt._adj_fdivr_m32 @516 + _adj_fdivr_m32i=msvcrt._adj_fdivr_m32i @517 + _adj_fdivr_m64=msvcrt._adj_fdivr_m64 @518 + _adj_fpatan=msvcrt._adj_fpatan @519 + _adj_fprem=msvcrt._adj_fprem @520 + _adj_fprem1=msvcrt._adj_fprem1 @521 + _adj_fptan=msvcrt._adj_fptan @522 + _adjust_fdiv=msvcrt._adjust_fdiv @523 + _aexit_rtn=msvcrt._aexit_rtn @524 DATA + _amsg_exit=msvcrt._amsg_exit @525 + _assert=msvcrt._assert @526 + _atodbl=msvcrt._atodbl @527 + _atoi64=msvcrt._atoi64 @528 + _atoldbl=msvcrt._atoldbl @529 + _beep=msvcrt._beep @530 + _beginthread=msvcrt._beginthread @531 + _beginthreadex=msvcrt._beginthreadex @532 + _c_exit=msvcrt._c_exit @533 + _cabs=msvcrt._cabs @534 + _cexit=msvcrt._cexit @535 + _cgets=msvcrt._cgets @536 + _chdir=msvcrt._chdir @537 + _chdrive=msvcrt._chdrive @538 + _chgsign=msvcrt._chgsign @539 + _chmod=msvcrt._chmod @540 + _chsize=msvcrt._chsize @541 + _clearfp=msvcrt._clearfp @542 + _close=msvcrt._close @543 + _commit=msvcrt._commit @544 + _commode=msvcrt._commode @545 DATA + _control87=msvcrt._control87 @546 + _controlfp=msvcrt._controlfp @547 + _copysign=msvcrt._copysign @548 + _cprintf=msvcrt._cprintf @549 + _cputs=msvcrt._cputs @550 + _creat=msvcrt._creat @551 + _cscanf=msvcrt._cscanf @552 + _ctype=msvcrt._ctype @553 DATA + _cwait=msvcrt._cwait @554 + _daylight=msvcrt._daylight @555 DATA + _dup=msvcrt._dup @556 + _dup2=msvcrt._dup2 @557 + _ecvt=msvcrt._ecvt @558 + _endthread=msvcrt._endthread @559 + _endthreadex=msvcrt._endthreadex @560 + _environ=msvcrt._environ @561 DATA + _eof=msvcrt._eof @562 + _errno=msvcrt._errno @563 + _except_handler2=msvcrt._except_handler2 @564 + _except_handler3=msvcrt._except_handler3 @565 + _execl=msvcrt._execl @566 + _execle=msvcrt._execle @567 + _execlp=msvcrt._execlp @568 + _execlpe=msvcrt._execlpe @569 + _execv=msvcrt._execv @570 + _execve=msvcrt._execve @571 + _execvp=msvcrt._execvp @572 + _execvpe=msvcrt._execvpe @573 + _exit=msvcrt._exit @574 + _expand=msvcrt._expand @575 + _fcloseall=msvcrt._fcloseall @576 + _fcvt=msvcrt._fcvt @577 + _fdopen=msvcrt._fdopen @578 + _fgetchar=msvcrt._fgetchar @579 + _fgetwchar=msvcrt._fgetwchar @580 + _filbuf=msvcrt._filbuf @581 + _fileinfo=msvcrt._fileinfo @582 DATA + _filelength=msvcrt._filelength @583 + _fileno=msvcrt._fileno @584 + _findclose=msvcrt._findclose @585 + _findfirst=msvcrt._findfirst @586 + _findnext=msvcrt._findnext @587 + _finite=msvcrt._finite @588 + _flsbuf=msvcrt._flsbuf @589 + _flushall=msvcrt._flushall @590 + _fmode=msvcrt._fmode @591 DATA + _fpclass=msvcrt._fpclass @592 + _fpieee_flt=msvcrt._fpieee_flt @593 + _fpreset=msvcrt._fpreset @594 + _fputchar=msvcrt._fputchar @595 + _fputwchar=msvcrt._fputwchar @596 + _fsopen=msvcrt._fsopen @597 + _fstat=msvcrt._fstat @598 + _ftime=msvcrt._ftime @599 + _ftol=msvcrt._ftol @600 + _fullpath=msvcrt._fullpath @601 + _futime=msvcrt._futime @602 + _gcvt=msvcrt._gcvt @603 + _get_osfhandle=msvcrt._get_osfhandle @604 + _getch=msvcrt._getch @605 + _getche=msvcrt._getche @606 + _getcwd=msvcrt._getcwd @607 + _getdcwd=msvcrt._getdcwd @608 + _getdiskfree=msvcrt._getdiskfree @609 + _getdllprocaddr=msvcrt._getdllprocaddr @610 + _getdrive=msvcrt._getdrive @611 + _getdrives=msvcrt._getdrives @612 + _getmbcp=msvcrt._getmbcp @613 + _getpid=msvcrt._getpid @614 + _getsystime=msvcrt._getsystime @615 + _getw=msvcrt._getw @616 + _getws=msvcrt._getws @617 + _global_unwind2=msvcrt._global_unwind2 @618 + _heapadd=msvcrt._heapadd @619 + _heapchk=msvcrt._heapchk @620 + _heapmin=msvcrt._heapmin @621 + _heapset=msvcrt._heapset @622 + _heapused=msvcrt._heapused @623 + _heapwalk=msvcrt._heapwalk @624 + _hypot=msvcrt._hypot @625 + _i64toa=msvcrt._i64toa @626 + _i64tow=msvcrt._i64tow @627 + _initterm=msvcrt._initterm @628 + _iob=msvcrt._iob @629 DATA + _isatty=msvcrt._isatty @630 + _isctype=msvcrt._isctype @631 + _ismbbalnum=msvcrt._ismbbalnum @632 + _ismbbalpha=msvcrt._ismbbalpha @633 + _ismbbgraph=msvcrt._ismbbgraph @634 + _ismbbkalnum=msvcrt._ismbbkalnum @635 + _ismbbkana=msvcrt._ismbbkana @636 + _ismbbkprint=msvcrt._ismbbkprint @637 + _ismbbkpunct=msvcrt._ismbbkpunct @638 + _ismbblead=msvcrt._ismbblead @639 + _ismbbprint=msvcrt._ismbbprint @640 + _ismbbpunct=msvcrt._ismbbpunct @641 + _ismbbtrail=msvcrt._ismbbtrail @642 + _ismbcalnum=msvcrt._ismbcalnum @643 + _ismbcalpha=msvcrt._ismbcalpha @644 + _ismbcdigit=msvcrt._ismbcdigit @645 + _ismbcgraph=msvcrt._ismbcgraph @646 + _ismbchira=msvcrt._ismbchira @647 + _ismbckata=msvcrt._ismbckata @648 + _ismbcl0=msvcrt._ismbcl0 @649 + _ismbcl1=msvcrt._ismbcl1 @650 + _ismbcl2=msvcrt._ismbcl2 @651 + _ismbclegal=msvcrt._ismbclegal @652 + _ismbclower=msvcrt._ismbclower @653 + _ismbcprint=msvcrt._ismbcprint @654 + _ismbcpunct=msvcrt._ismbcpunct @655 + _ismbcspace=msvcrt._ismbcspace @656 + _ismbcsymbol=msvcrt._ismbcsymbol @657 + _ismbcupper=msvcrt._ismbcupper @658 + _ismbslead=msvcrt._ismbslead @659 + _ismbstrail=msvcrt._ismbstrail @660 + _isnan=msvcrt._isnan @661 + _itoa=msvcrt._itoa @662 + _itow=msvcrt._itow @663 + _j0=msvcrt._j0 @664 + _j1=msvcrt._j1 @665 + _jn=msvcrt._jn @666 + _kbhit=msvcrt._kbhit @667 + _lfind=msvcrt._lfind @668 + _loaddll=msvcrt._loaddll @669 + _local_unwind2=msvcrt._local_unwind2 @670 + _locking=msvcrt._locking @671 + _logb=msvcrt._logb @672 + _longjmpex=msvcrt._longjmpex @673 + _lrotl=msvcrt._lrotl @674 + _lrotr=msvcrt._lrotr @675 + _lsearch=msvcrt._lsearch @676 + _lseek=msvcrt._lseek @677 + _ltoa=msvcrt._ltoa @678 + _ltow=msvcrt._ltow @679 + _makepath=msvcrt._makepath @680 + _matherr=msvcrt._matherr @681 + _mbbtombc=msvcrt._mbbtombc @682 + _mbbtype=msvcrt._mbbtype @683 + _mbccpy=msvcrt._mbccpy @684 + _mbcjistojms=msvcrt._mbcjistojms @685 + _mbcjmstojis=msvcrt._mbcjmstojis @686 + _mbclen=msvcrt._mbclen @687 + _mbctohira=msvcrt._mbctohira @688 + _mbctokata=msvcrt._mbctokata @689 + _mbctolower=msvcrt._mbctolower @690 + _mbctombb=msvcrt._mbctombb @691 + _mbctoupper=msvcrt._mbctoupper @692 + _mbctype=msvcrt._mbctype @693 DATA + _mbsbtype=msvcrt._mbsbtype @694 + _mbscat=msvcrt._mbscat @695 + _mbschr=msvcrt._mbschr @696 + _mbscmp=msvcrt._mbscmp @697 + _mbscoll=msvcrt._mbscoll @698 + _mbscpy=msvcrt._mbscpy @699 + _mbscspn=msvcrt._mbscspn @700 + _mbsdec=msvcrt._mbsdec @701 + _mbsdup=msvcrt._mbsdup @702 + _mbsicmp=msvcrt._mbsicmp @703 + _mbsicoll=msvcrt._mbsicoll @704 + _mbsinc=msvcrt._mbsinc @705 + _mbslen=msvcrt._mbslen @706 + _mbslwr=msvcrt._mbslwr @707 + _mbsnbcat=msvcrt._mbsnbcat @708 + _mbsnbcmp=msvcrt._mbsnbcmp @709 + _mbsnbcnt=msvcrt._mbsnbcnt @710 + _mbsnbcoll=msvcrt._mbsnbcoll @711 + _mbsnbcpy=msvcrt._mbsnbcpy @712 + _mbsnbicmp=msvcrt._mbsnbicmp @713 + _mbsnbicoll=msvcrt._mbsnbicoll @714 + _mbsnbset=msvcrt._mbsnbset @715 + _mbsncat=msvcrt._mbsncat @716 + _mbsnccnt=msvcrt._mbsnccnt @717 + _mbsncmp=msvcrt._mbsncmp @718 + _mbsncoll=msvcrt._mbsncoll @719 + _mbsncpy=msvcrt._mbsncpy @720 + _mbsnextc=msvcrt._mbsnextc @721 + _mbsnicmp=msvcrt._mbsnicmp @722 + _mbsnicoll=msvcrt._mbsnicoll @723 + _mbsninc=msvcrt._mbsninc @724 + _mbsnset=msvcrt._mbsnset @725 + _mbspbrk=msvcrt._mbspbrk @726 + _mbsrchr=msvcrt._mbsrchr @727 + _mbsrev=msvcrt._mbsrev @728 + _mbsset=msvcrt._mbsset @729 + _mbsspn=msvcrt._mbsspn @730 + _mbsspnp=msvcrt._mbsspnp @731 + _mbsstr=msvcrt._mbsstr @732 + _mbstok=msvcrt._mbstok @733 + _mbstrlen=msvcrt._mbstrlen @734 + _mbsupr=msvcrt._mbsupr @735 + _memccpy=msvcrt._memccpy @736 + _memicmp=msvcrt._memicmp @737 + _mkdir=msvcrt._mkdir @738 + _mktemp=msvcrt._mktemp @739 + _msize=msvcrt._msize @740 + _nextafter=msvcrt._nextafter @743 + _onexit=msvcrt._onexit @744 + _open=msvcrt._open @745 + _open_osfhandle=msvcrt._open_osfhandle @746 + _osver=msvcrt._osver @747 DATA + _pclose=msvcrt._pclose @748 + _pctype=msvcrt._pctype @749 DATA + _pgmptr=msvcrt._pgmptr @750 DATA + _pipe=msvcrt._pipe @751 + _popen=msvcrt._popen @752 + _purecall=msvcrt._purecall @753 + _putch=msvcrt._putch @754 + _putenv=msvcrt._putenv @755 + _putw=msvcrt._putw @756 + _putws=msvcrt._putws @757 + _pwctype=msvcrt._pwctype @758 DATA + _read=msvcrt._read @759 + _rmdir=msvcrt._rmdir @760 + _rmtmp=msvcrt._rmtmp @761 + _rotl=msvcrt._rotl @762 + _rotr=msvcrt._rotr @763 + _safe_fdiv=msvcrt._safe_fdiv @764 + _safe_fdivr=msvcrt._safe_fdivr @765 + _safe_fprem=msvcrt._safe_fprem @766 + _safe_fprem1=msvcrt._safe_fprem1 @767 + _scalb=msvcrt._scalb @768 + _searchenv=msvcrt._searchenv @769 + __seh_longjmp_unwind@4=msvcrt._seh_longjmp_unwind @500 + _seterrormode=msvcrt._seterrormode @770 + _setjmp=msvcrt._setjmp @771 + _setjmp3=msvcrt._setjmp3 @772 + _setmbcp=msvcrt._setmbcp @773 + _setmode=msvcrt._setmode @774 + _setsystime=msvcrt._setsystime @775 + _sleep=msvcrt._sleep @776 + _snprintf=msvcrt._snprintf @777 + _snwprintf=msvcrt._snwprintf @778 + _sopen=msvcrt._sopen @779 + _spawnl=msvcrt._spawnl @780 + _spawnle=msvcrt._spawnle @781 + _spawnlp=msvcrt._spawnlp @782 + _spawnlpe=msvcrt._spawnlpe @783 + _spawnv=msvcrt._spawnv @784 + _spawnve=msvcrt._spawnve @785 + _spawnvp=msvcrt._spawnvp @786 + _spawnvpe=msvcrt._spawnvpe @787 + _splitpath=msvcrt._splitpath @788 + _stat=msvcrt._stat @789 + _statusfp=msvcrt._statusfp @790 + _strcmpi=msvcrt._strcmpi @791 + _strdate=msvcrt._strdate @792 + _strdup=msvcrt._strdup @793 + _strerror=msvcrt._strerror @794 + _stricmp=msvcrt._stricmp @795 + _stricoll=msvcrt._stricoll @796 + _strlwr=msvcrt._strlwr @797 + _strncoll=msvcrt._strncoll @798 + _strnicmp=msvcrt._strnicmp @799 + _strnicoll=msvcrt._strnicoll @800 + _strnset=msvcrt._strnset @801 + _strrev=msvcrt._strrev @802 + _strset=msvcrt._strset @803 + _strtime=msvcrt._strtime @804 + _strupr=msvcrt._strupr @805 + _swab=msvcrt._swab @806 + _sys_errlist=msvcrt._sys_errlist @807 DATA + _sys_nerr=msvcrt._sys_nerr @808 DATA + _tccpy=msvcrt._mbccpy @809 + _tclen=msvcrt._mbclen @810 + _tcschr=msvcrt._mbschr @811 + _tcsclen=msvcrt._mbslen @812 + _tcscmp=msvcrt._mbscmp @813 + _tcscspn=msvcrt._mbscspn @814 + _tcsdec=msvcrt._mbsdec @815 + _tcsicmp=msvcrt._mbsicmp @816 + _tcsinc=msvcrt._mbsinc @817 + _tcslwr=msvcrt._mbslwr @818 + _tcsnbcnt=msvcrt._mbsnbcnt @819 + _tcsncat=msvcrt._mbsnbcat @820 + _tcsnccat=msvcrt._mbsncat @821 + _tcsnccmp=msvcrt._mbsncmp @822 + _tcsnccnt=msvcrt._mbsnccnt @823 + _tcsnccpy=msvcrt._mbsncpy @824 + _tcsncicmp=msvcrt._mbsnicmp @825 + _tcsncmp=msvcrt._mbsnbcmp @826 + _tcsncpy=msvcrt._mbsnbcpy @827 + _tcsncset=msvcrt._mbsnset @828 + _tcsnextc=msvcrt._mbsnextc @829 + _tcsnicmp=msvcrt._mbsnbicmp @830 + _tcsninc=msvcrt._mbsninc @831 + _tcsnset=msvcrt._mbsnbset @832 + _tcspbrk=msvcrt._mbspbrk @833 + _tcsrchr=msvcrt._mbsrchr @834 + _tcsrev=msvcrt._mbsrev @835 + _tcsset=msvcrt._mbsset @836 + _tcsspn=msvcrt._mbsspn @837 + _tcsspnp=msvcrt._mbsspnp @838 + _tcsstr=msvcrt._mbsstr @839 + _tcstok=msvcrt._mbstok @840 + _tcsupr=msvcrt._mbsupr @841 + _tell=msvcrt._tell @842 + _tempnam=msvcrt._tempnam @843 + _timezone=msvcrt._timezone @844 DATA + _tolower=msvcrt._tolower @845 + _toupper=msvcrt._toupper @846 + _tzname=msvcrt._tzname @847 DATA + _tzset=msvcrt._tzset @848 + _ui64toa=msvcrt._ui64toa @849 + _ui64tow=msvcrt._ui64tow @850 + _ultoa=msvcrt._ultoa @851 + _ultow=msvcrt._ultow @852 + _umask=msvcrt._umask @853 + _ungetch=msvcrt._ungetch @854 + _unlink=msvcrt._unlink @855 + _unloaddll=msvcrt._unloaddll @856 + _utime=msvcrt._utime @857 + _vsnprintf=msvcrt._vsnprintf @858 + _vsnwprintf=msvcrt._vsnwprintf @859 + _waccess=msvcrt._waccess @860 + _wasctime=msvcrt._wasctime @861 + _wchdir=msvcrt._wchdir @862 + _wchmod=msvcrt._wchmod @863 + _wcmdln=msvcrt._wcmdln @864 DATA + _wcreat=msvcrt._wcreat @865 + _wcsdup=msvcrt._wcsdup @866 + _wcsicmp=msvcrt._wcsicmp @867 + _wcsicoll=msvcrt._wcsicoll @868 + _wcslwr=msvcrt._wcslwr @869 + _wcsncoll=msvcrt._wcsncoll @870 + _wcsnicmp=msvcrt._wcsnicmp @871 + _wcsnicoll=msvcrt._wcsnicoll @872 + _wcsnset=msvcrt._wcsnset @873 + _wcsrev=msvcrt._wcsrev @874 + _wcsset=msvcrt._wcsset @875 + _wcsupr=msvcrt._wcsupr @876 + _wctime=msvcrt._wctime @877 + _wenviron=msvcrt._wenviron @878 DATA + _wexecl=msvcrt._wexecl @879 + _wexecle=msvcrt._wexecle @880 + _wexeclp=msvcrt._wexeclp @881 + _wexeclpe=msvcrt._wexeclpe @882 + _wexecv=msvcrt._wexecv @883 + _wexecve=msvcrt._wexecve @884 + _wexecvp=msvcrt._wexecvp @885 + _wexecvpe=msvcrt._wexecvpe @886 + _wfdopen=msvcrt._wfdopen @887 + _wfindfirst=msvcrt._wfindfirst @888 + _wfindnext=msvcrt._wfindnext @889 + _wfopen=msvcrt._wfopen @890 + _wfreopen=msvcrt._wfreopen @891 + _wfsopen=msvcrt._wfsopen @892 + _wfullpath=msvcrt._wfullpath @893 + _wgetcwd=msvcrt._wgetcwd @894 + _wgetdcwd=msvcrt._wgetdcwd @895 + _wgetenv=msvcrt._wgetenv @896 + _winmajor=msvcrt._winmajor @897 DATA + _winminor=msvcrt._winminor @898 DATA + _winver=msvcrt._winver @899 DATA + _wmakepath=msvcrt._wmakepath @900 + _wmkdir=msvcrt._wmkdir @901 + _wmktemp=msvcrt._wmktemp @902 + _wopen=msvcrt._wopen @903 + _wperror=msvcrt._wperror @904 + _wpgmptr=msvcrt._wpgmptr @905 DATA + _wpopen=msvcrt._wpopen @906 + _wputenv=msvcrt._wputenv @907 + _wremove=msvcrt._wremove @908 + _wrename=msvcrt._wrename @909 + _write=msvcrt._write @910 + _wrmdir=msvcrt._wrmdir @911 + _wsearchenv=msvcrt._wsearchenv @912 + _wsetlocale=msvcrt._wsetlocale @913 + _wsopen=msvcrt._wsopen @914 + _wspawnl=msvcrt._wspawnl @915 + _wspawnle=msvcrt._wspawnle @916 + _wspawnlp=msvcrt._wspawnlp @917 + _wspawnlpe=msvcrt._wspawnlpe @918 + _wspawnv=msvcrt._wspawnv @919 + _wspawnve=msvcrt._wspawnve @920 + _wspawnvp=msvcrt._wspawnvp @921 + _wspawnvpe=msvcrt._wspawnvpe @922 + _wsplitpath=msvcrt._wsplitpath @923 + _wstat=msvcrt._wstat @924 + _wstrdate=msvcrt._wstrdate @925 + _wstrtime=msvcrt._wstrtime @926 + _wsystem=msvcrt._wsystem @927 + _wtempnam=msvcrt._wtempnam @928 + _wtmpnam=msvcrt._wtmpnam @929 + _wtoi=msvcrt._wtoi @930 + _wtoi64=msvcrt._wtoi64 @931 + _wtol=msvcrt._wtol @932 + _wunlink=msvcrt._wunlink @933 + _wutime=msvcrt._wutime @934 + _y0=msvcrt._y0 @935 + _y1=msvcrt._y1 @936 + _yn=msvcrt._yn @937 + abort=msvcrt.abort @938 + abs=msvcrt.abs @939 + acos=msvcrt.acos @940 + asctime=msvcrt.asctime @941 + asin=msvcrt.asin @942 + atan=msvcrt.atan @943 + atan2=msvcrt.atan2 @944 + atexit=msvcrt.atexit @945 + atof=msvcrt.atof @946 + atoi=msvcrt.atoi @947 + atol=msvcrt.atol @948 + bsearch=msvcrt.bsearch @949 + calloc=msvcrt.calloc @950 + ceil=msvcrt.ceil @951 + clearerr=msvcrt.clearerr @952 + clock=msvcrt.clock @953 + cos=msvcrt.cos @954 + cosh=msvcrt.cosh @955 + ctime=msvcrt.ctime @956 + difftime=msvcrt.difftime @957 + div=msvcrt.div @958 + exit=msvcrt.exit @959 + exp=msvcrt.exp @960 + fabs=msvcrt.fabs @961 + fclose=msvcrt.fclose @962 + feof=msvcrt.feof @963 + ferror=msvcrt.ferror @964 + fflush=msvcrt.fflush @965 + fgetc=msvcrt.fgetc @966 + fgetpos=msvcrt.fgetpos @967 + fgets=msvcrt.fgets @968 + fgetwc=msvcrt.fgetwc @969 + fgetws=msvcrt.fgetws @970 + floor=msvcrt.floor @971 + fmod=msvcrt.fmod @972 + fopen=msvcrt.fopen @973 + fprintf=msvcrt.fprintf @974 + fputc=msvcrt.fputc @975 + fputs=msvcrt.fputs @976 + fputwc=msvcrt.fputwc @977 + fputws=msvcrt.fputws @978 + fread=msvcrt.fread @979 + free=msvcrt.free @980 + freopen=msvcrt.freopen @981 + frexp=msvcrt.frexp @982 + fscanf=msvcrt.fscanf @983 + fseek=msvcrt.fseek @984 + fsetpos=msvcrt.fsetpos @985 + ftell=msvcrt.ftell @986 + fwprintf=msvcrt.fwprintf @987 + fwrite=msvcrt.fwrite @988 + fwscanf=msvcrt.fwscanf @989 + getc=msvcrt.getc @990 + getchar=msvcrt.getchar @991 + getenv=msvcrt.getenv @992 + gets=msvcrt.gets @993 + getwc=msvcrt.getwc @994 + getwchar=msvcrt.getwchar @995 + gmtime=msvcrt.gmtime @996 + is_wctype=msvcrt.is_wctype @997 + isalnum=msvcrt.isalnum @998 + isalpha=msvcrt.isalpha @999 + iscntrl=msvcrt.iscntrl @1000 + isdigit=msvcrt.isdigit @1001 + isgraph=msvcrt.isgraph @1002 + isleadbyte=msvcrt.isleadbyte @1003 + islower=msvcrt.islower @1004 + isprint=msvcrt.isprint @1005 + ispunct=msvcrt.ispunct @1006 + isspace=msvcrt.isspace @1007 + isupper=msvcrt.isupper @1008 + iswalnum=msvcrt.iswalnum @1009 + iswalpha=msvcrt.iswalpha @1010 + iswascii=msvcrt.iswascii @1011 + iswcntrl=msvcrt.iswcntrl @1012 + iswctype=msvcrt.iswctype @1013 + iswdigit=msvcrt.iswdigit @1014 + iswgraph=msvcrt.iswgraph @1015 + iswlower=msvcrt.iswlower @1016 + iswprint=msvcrt.iswprint @1017 + iswpunct=msvcrt.iswpunct @1018 + iswspace=msvcrt.iswspace @1019 + iswupper=msvcrt.iswupper @1020 + iswxdigit=msvcrt.iswxdigit @1021 + isxdigit=msvcrt.isxdigit @1022 + labs=msvcrt.labs @1023 + ldexp=msvcrt.ldexp @1024 + ldiv=msvcrt.ldiv @1025 + localeconv=msvcrt.localeconv @1026 + localtime=msvcrt.localtime @1027 + log=msvcrt.log @1028 + log10=msvcrt.log10 @1029 + longjmp=msvcrt.longjmp @1030 + malloc=msvcrt.malloc @1031 + mblen=msvcrt.mblen @1032 + mbstowcs=msvcrt.mbstowcs @1033 + mbtowc=msvcrt.mbtowc @1034 + memchr=msvcrt.memchr @1035 + memcmp=msvcrt.memcmp @1036 + memcpy=msvcrt.memcpy @1037 + memmove=msvcrt.memmove @1038 + memset=msvcrt.memset @1039 + mktime=msvcrt.mktime @1040 + modf=msvcrt.modf @1041 + perror=msvcrt.perror @1042 + pow=msvcrt.pow @1043 + printf=msvcrt.printf @1044 + putc=msvcrt.putc @1045 + putchar=msvcrt.putchar @1046 + puts=msvcrt.puts @1047 + putwc=msvcrt.putwc @1048 + putwchar=msvcrt.putwchar @1049 + qsort=msvcrt.qsort @1050 + raise=msvcrt.raise @1051 + rand=msvcrt.rand @1052 + realloc=msvcrt.realloc @1053 + remove=msvcrt.remove @1054 + rename=msvcrt.rename @1055 + rewind=msvcrt.rewind @1056 + scanf=msvcrt.scanf @1057 + setbuf=msvcrt.setbuf @1058 + setlocale=msvcrt.setlocale @1059 + setvbuf=msvcrt.setvbuf @1060 + signal=msvcrt.signal @1061 + sin=msvcrt.sin @1062 + sinh=msvcrt.sinh @1063 + sprintf=msvcrt.sprintf @1064 + sqrt=msvcrt.sqrt @1065 + srand=msvcrt.srand @1066 + sscanf=msvcrt.sscanf @1067 + strcat=msvcrt.strcat @1068 + strchr=msvcrt.strchr @1069 + strcmp=msvcrt.strcmp @1070 + strcoll=msvcrt.strcoll @1071 + strcpy=msvcrt.strcpy @1072 + strcspn=msvcrt.strcspn @1073 + strerror=msvcrt.strerror @1074 + strftime=msvcrt.strftime @1075 + strlen=msvcrt.strlen @1076 + strncat=msvcrt.strncat @1077 + strncmp=msvcrt.strncmp @1078 + strncpy=msvcrt.strncpy @1079 + strpbrk=msvcrt.strpbrk @1080 + strrchr=msvcrt.strrchr @1081 + strspn=msvcrt.strspn @1082 + strstr=msvcrt.strstr @1083 + strtod=msvcrt.strtod @1084 + strtok=msvcrt.strtok @1085 + strtol=msvcrt.strtol @1086 + strtoul=msvcrt.strtoul @1087 + strxfrm=msvcrt.strxfrm @1088 + swprintf=msvcrt.swprintf @1089 + swscanf=msvcrt.swscanf @1090 + system=msvcrt.system @1091 + tan=msvcrt.tan @1092 + tanh=msvcrt.tanh @1093 + time=msvcrt.time @1094 + tmpfile=msvcrt.tmpfile @1095 + tmpnam=msvcrt.tmpnam @1096 + tolower=msvcrt.tolower @1097 + toupper=msvcrt.toupper @1098 + towlower=msvcrt.towlower @1099 + towupper=msvcrt.towupper @1100 + ungetc=msvcrt.ungetc @1101 + ungetwc=msvcrt.ungetwc @1102 + vfprintf=msvcrt.vfprintf @1103 + vfwprintf=msvcrt.vfwprintf @1104 + vprintf=msvcrt.vprintf @1105 + vsprintf=msvcrt.vsprintf @1106 + vswprintf=msvcrt.vswprintf @1107 + vwprintf=msvcrt.vwprintf @1108 + wcscat=msvcrt.wcscat @1109 + wcschr=msvcrt.wcschr @1110 + wcscmp=msvcrt.wcscmp @1111 + wcscoll=msvcrt.wcscoll @1112 + wcscpy=msvcrt.wcscpy @1113 + wcscspn=msvcrt.wcscspn @1114 + wcsftime=msvcrt.wcsftime @1115 + wcslen=msvcrt.wcslen @1116 + wcsncat=msvcrt.wcsncat @1117 + wcsncmp=msvcrt.wcsncmp @1118 + wcsncpy=msvcrt.wcsncpy @1119 + wcspbrk=msvcrt.wcspbrk @1120 + wcsrchr=msvcrt.wcsrchr @1121 + wcsspn=msvcrt.wcsspn @1122 + wcsstr=msvcrt.wcsstr @1123 + wcstod=msvcrt.wcstod @1124 + wcstok=msvcrt.wcstok @1125 + wcstol=msvcrt.wcstol @1126 + wcstombs=msvcrt.wcstombs @1127 + wcstoul=msvcrt.wcstoul @1128 + wcsxfrm=msvcrt.wcsxfrm @1129 + wctomb=msvcrt.wctomb @1130 + wprintf=msvcrt.wprintf @1131 + wscanf=msvcrt.wscanf @1132 diff --git a/reactos/dll/msvcrt20/msvcrt20.rc b/reactos/dll/msvcrt20/msvcrt20.rc new file mode 100644 index 00000000000..514d1465d32 --- /dev/null +++ b/reactos/dll/msvcrt20/msvcrt20.rc @@ -0,0 +1,5 @@ +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "MSVCRT20.DLL Compatibility Library\0" +#define REACTOS_STR_INTERNAL_NAME "msvcrt20\0" +#define REACTOS_STR_ORIGINAL_FILENAME "msvcrt20.dll\0" +#include diff --git a/reactos/dll/msvcrt20/msvcrt20.xml b/reactos/dll/msvcrt20/msvcrt20.xml new file mode 100644 index 00000000000..5d00b03278b --- /dev/null +++ b/reactos/dll/msvcrt20/msvcrt20.xml @@ -0,0 +1,17 @@ + + + . + + + + 0x600 + 0x501 + + + wine + string + ntdll + kernel32 + msvcrt20.c + msvcrt20.rc + diff --git a/reactos/dll/msvideo/Makefile.in b/reactos/dll/msvideo/Makefile.in new file mode 100644 index 00000000000..b52a912c7be --- /dev/null +++ b/reactos/dll/msvideo/Makefile.in @@ -0,0 +1,22 @@ +TOPSRCDIR = @top_srcdir@ +TOPOBJDIR = ../.. +SRCDIR = @srcdir@ +VPATH = @srcdir@ +MODULE = msvfw32.dll +IMPORTS = winmm comctl32 version user32 gdi32 advapi32 kernel32 ntdll +ALTNAMES = msvideo.dll +EXTRALIBS = $(LIBUNICODE) + +SPEC_SRCS16 = $(ALTNAMES:.dll=.spec) + +C_SRCS = \ + mciwnd.c \ + msvideo_main.c \ + drawdib.c + +C_SRCS16 = \ + msvideo16.c + +@MAKE_DLL_RULES@ + +### Dependencies: diff --git a/reactos/dll/msvideo/drawdib.c b/reactos/dll/msvideo/drawdib.c new file mode 100644 index 00000000000..fa4c92ff35a --- /dev/null +++ b/reactos/dll/msvideo/drawdib.c @@ -0,0 +1,463 @@ +/* + * Copyright 2000 Bradley Baetz + * + * 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 + * + * FIXME: Some flags are ignored + * + * Handle palettes + */ + +#include +#include "msvideo_private.h" + +#include "wingdi.h" +#include "winuser.h" + +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msvideo); + +typedef struct tagWINE_HDD { + HDC hdc; + INT dxDst; + INT dyDst; + LPBITMAPINFOHEADER lpbi; + INT dxSrc; + INT dySrc; + HPALETTE hpal; /* Palette to use for the DIB */ + BOOL begun; /* DrawDibBegin has been called */ + LPBITMAPINFOHEADER lpbiOut; /* Output format */ + HIC hic; /* HIC for decompression */ + HDC hMemDC; /* DC for buffering */ + HBITMAP hOldDib; /* Original Dib */ + HBITMAP hDib; /* DibSection */ + LPVOID lpvbits; /* Buffer for holding decompressed dib */ + HDRAWDIB hSelf; + struct tagWINE_HDD* next; +} WINE_HDD; + +int num_colours(LPBITMAPINFOHEADER lpbi) +{ + if(lpbi->biClrUsed) + return lpbi->biClrUsed; + if(lpbi->biBitCount<=8) + return 1<biBitCount; + return 0; +} + +static WINE_HDD* HDD_FirstHdd /* = NULL */; + +static WINE_HDD* MSVIDEO_GetHddPtr(HDRAWDIB hd) +{ + WINE_HDD* hdd; + + for (hdd = HDD_FirstHdd; hdd != NULL && hdd->hSelf != hd; hdd = hdd->next); + return hdd; +} + +static DWORD HDD_HandleRef = 1; + +/*********************************************************************** + * DrawDibOpen [MSVFW32.@] + */ +HDRAWDIB VFWAPI DrawDibOpen(void) +{ + WINE_HDD* whdd; + + TRACE("(void)\n"); + + whdd = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WINE_HDD)); + TRACE("=> %p\n", whdd); + + while (MSVIDEO_GetHddPtr((HDRAWDIB)HDD_HandleRef) != NULL) HDD_HandleRef++; + whdd->hSelf = (HDRAWDIB)HDD_HandleRef++; + + whdd->next = HDD_FirstHdd; + HDD_FirstHdd = whdd; + + return whdd->hSelf; +} + +/*********************************************************************** + * DrawDibClose [MSVFW32.@] + */ +BOOL VFWAPI DrawDibClose(HDRAWDIB hdd) +{ + WINE_HDD* whdd = MSVIDEO_GetHddPtr(hdd); + WINE_HDD** p; + + TRACE("(%p)\n", hdd); + + if (!whdd) return FALSE; + + if (whdd->begun) DrawDibEnd(hdd); + + for (p = &HDD_FirstHdd; *p != NULL; p = &((*p)->next)) + { + if (*p == whdd) + { + *p = whdd->next; + break; + } + } + + HeapFree(GetProcessHeap(), 0, whdd); + + return TRUE; +} + +/*********************************************************************** + * DrawDibEnd [MSVFW32.@] + */ +BOOL VFWAPI DrawDibEnd(HDRAWDIB hdd) +{ + BOOL ret = TRUE; + WINE_HDD *whdd = MSVIDEO_GetHddPtr(hdd); + + TRACE("(%p)\n", hdd); + + whdd->hpal = 0; /* Do not free this */ + whdd->hdc = 0; + if (whdd->lpbi) + { + HeapFree(GetProcessHeap(), 0, whdd->lpbi); + whdd->lpbi = NULL; + } + if (whdd->lpbiOut) + { + HeapFree(GetProcessHeap(), 0, whdd->lpbiOut); + whdd->lpbiOut = NULL; + } + + whdd->begun = FALSE; + + /*if (whdd->lpvbits) + HeapFree(GetProcessHeap(), 0, whdd->lpvbuf);*/ + + if (whdd->hMemDC) + { + SelectObject(whdd->hMemDC, whdd->hOldDib); + DeleteDC(whdd->hMemDC); + whdd->hMemDC = 0; + } + + if (whdd->hDib) DeleteObject(whdd->hDib); + whdd->hDib = 0; + + if (whdd->hic) + { + ICDecompressEnd(whdd->hic); + ICClose(whdd->hic); + whdd->hic = 0; + } + + whdd->lpvbits = NULL; + + return ret; +} + +/*********************************************************************** + * DrawDibBegin [MSVFW32.@] + */ +BOOL VFWAPI DrawDibBegin(HDRAWDIB hdd, + HDC hdc, + INT dxDst, + INT dyDst, + LPBITMAPINFOHEADER lpbi, + INT dxSrc, + INT dySrc, + UINT wFlags) +{ + BOOL ret = TRUE; + WINE_HDD *whdd; + + TRACE("(%p,%p,%d,%d,%p,%d,%d,0x%08lx)\n", + hdd, hdc, dxDst, dyDst, lpbi, dxSrc, dySrc, (DWORD)wFlags); + + TRACE("lpbi: %ld,%ld/%ld,%d,%d,%ld,%ld,%ld,%ld,%ld,%ld\n", + lpbi->biSize, lpbi->biWidth, lpbi->biHeight, lpbi->biPlanes, + lpbi->biBitCount, lpbi->biCompression, lpbi->biSizeImage, + lpbi->biXPelsPerMeter, lpbi->biYPelsPerMeter, lpbi->biClrUsed, + lpbi->biClrImportant); + + if (wFlags & ~(DDF_BUFFER)) + FIXME("wFlags == 0x%08x not handled\n", wFlags & ~(DDF_BUFFER)); + + whdd = MSVIDEO_GetHddPtr(hdd); + if (!whdd) return FALSE; + + if (whdd->begun) DrawDibEnd(hdd); + + if (lpbi->biCompression) + { + DWORD size = 0; + + whdd->hic = ICOpen(ICTYPE_VIDEO, lpbi->biCompression, ICMODE_DECOMPRESS); + if (!whdd->hic) + { + WARN("Could not open IC. biCompression == 0x%08lx\n", lpbi->biCompression); + ret = FALSE; + } + + if (ret) + { + size = ICDecompressGetFormat(whdd->hic, lpbi, NULL); + if (size == ICERR_UNSUPPORTED) + { + WARN("Codec doesn't support GetFormat, giving up.\n"); + ret = FALSE; + } + } + + if (ret) + { + whdd->lpbiOut = HeapAlloc(GetProcessHeap(), 0, size); + + if (ICDecompressGetFormat(whdd->hic, lpbi, whdd->lpbiOut) != ICERR_OK) + ret = FALSE; + } + + if (ret) + { + /* FIXME: Use Ex functions if available? */ + if (ICDecompressBegin(whdd->hic, lpbi, whdd->lpbiOut) != ICERR_OK) + ret = FALSE; + + TRACE("biSizeImage == %ld\n", whdd->lpbiOut->biSizeImage); + TRACE("biCompression == %ld\n", whdd->lpbiOut->biCompression); + TRACE("biBitCount == %d\n", whdd->lpbiOut->biBitCount); + } + } + else + { + DWORD dwSize; + /* No compression */ + TRACE("Not compressed!\n"); + dwSize = lpbi->biSize + num_colours(lpbi)*sizeof(RGBQUAD); + whdd->lpbiOut = HeapAlloc(GetProcessHeap(), 0, dwSize); + memcpy(whdd->lpbiOut, lpbi, dwSize); + } + + if (ret) + { + /*whdd->lpvbuf = HeapAlloc(GetProcessHeap(), 0, whdd->lpbiOut->biSizeImage);*/ + + whdd->hMemDC = CreateCompatibleDC(hdc); + TRACE("Creating: %ld, %p\n", whdd->lpbiOut->biSize, whdd->lpvbits); + whdd->hDib = CreateDIBSection(whdd->hMemDC, (BITMAPINFO *)whdd->lpbiOut, DIB_RGB_COLORS, &(whdd->lpvbits), 0, 0); + if (!whdd->hDib) + { + TRACE("Error: %ld\n", GetLastError()); + } + TRACE("Created: %p,%p\n", whdd->hDib, whdd->lpvbits); + whdd->hOldDib = SelectObject(whdd->hMemDC, whdd->hDib); + } + + if (ret) + { + whdd->hdc = hdc; + whdd->dxDst = dxDst; + whdd->dyDst = dyDst; + whdd->lpbi = HeapAlloc(GetProcessHeap(), 0, lpbi->biSize); + memcpy(whdd->lpbi, lpbi, lpbi->biSize); + whdd->dxSrc = dxSrc; + whdd->dySrc = dySrc; + whdd->begun = TRUE; + whdd->hpal = 0; + } + else + { + if (whdd->hic) + ICClose(whdd->hic); + if (whdd->lpbiOut) + { + HeapFree(GetProcessHeap(), 0, whdd->lpbiOut); + whdd->lpbiOut = NULL; + } + } + + return ret; +} + +/********************************************************************** + * DrawDibDraw [MSVFW32.@] + */ +BOOL VFWAPI DrawDibDraw(HDRAWDIB hdd, HDC hdc, + INT xDst, INT yDst, INT dxDst, INT dyDst, + LPBITMAPINFOHEADER lpbi, + LPVOID lpBits, + INT xSrc, INT ySrc, INT dxSrc, INT dySrc, + UINT wFlags) +{ + WINE_HDD *whdd; + BOOL ret = TRUE; + + TRACE("(%p,%p,%d,%d,%d,%d,%p,%p,%d,%d,%d,%d,0x%08lx)\n", + hdd, hdc, xDst, yDst, dxDst, dyDst, lpbi, lpBits, xSrc, ySrc, dxSrc, dySrc, (DWORD)wFlags); + + whdd = MSVIDEO_GetHddPtr(hdd); + if (!whdd) return FALSE; + + if (wFlags & ~(DDF_SAME_HDC | DDF_SAME_DRAW | DDF_NOTKEYFRAME | DDF_UPDATE | DDF_DONTDRAW)) + FIXME("wFlags == 0x%08lx not handled\n", (DWORD)wFlags); + + if (!lpBits) + { + /* Undocumented? */ + lpBits = (LPSTR)lpbi + (WORD)(lpbi->biSize) + (WORD)(num_colours(lpbi)*sizeof(RGBQUAD)); + } + + +#define CHANGED(x) (whdd->x != x) + + if ((!whdd->begun) || + (!(wFlags & DDF_SAME_HDC) && CHANGED(hdc)) || + (!(wFlags & DDF_SAME_DRAW) && (CHANGED(lpbi) || CHANGED(dxSrc) || CHANGED(dySrc) || CHANGED(dxDst) || CHANGED(dyDst)))) + { + TRACE("Something changed!\n"); + ret = DrawDibBegin(hdd, hdc, dxDst, dyDst, lpbi, dxSrc, dySrc, 0); + } + +#undef CHANGED + + if ((dxDst == -1) && (dyDst == -1)) + { + dxDst = dxSrc; + dyDst = dySrc; + } + + if (!(wFlags & DDF_UPDATE)) + { + /* biSizeImage may be set to 0 for BI_RGB (uncompressed) bitmaps */ + if ((lpbi->biCompression == BI_RGB) && (lpbi->biSizeImage == 0)) + lpbi->biSizeImage = ((lpbi->biWidth * lpbi->biBitCount + 31) / 32) * 4 * lpbi->biHeight; + + if (lpbi->biCompression) + { + DWORD flags = 0; + + TRACE("Compression == 0x%08lx\n", lpbi->biCompression); + + if (wFlags & DDF_NOTKEYFRAME) + flags |= ICDECOMPRESS_NOTKEYFRAME; + + ICDecompress(whdd->hic, flags, lpbi, lpBits, whdd->lpbiOut, whdd->lpvbits); + } + else + { + memcpy(whdd->lpvbits, lpBits, lpbi->biSizeImage); + } + } + if (!(wFlags & DDF_DONTDRAW) && whdd->hpal) + SelectPalette(hdc, whdd->hpal, FALSE); + + if (!(StretchBlt(whdd->hdc, xDst, yDst, dxDst, dyDst, whdd->hMemDC, xSrc, ySrc, dxSrc, dySrc, SRCCOPY))) + ret = FALSE; + + return ret; +} + +/************************************************************************* + * DrawDibStart [MSVFW32.@] + */ +BOOL VFWAPI DrawDibStart(HDRAWDIB hdd, DWORD rate) { + FIXME("(%p, %ld), stub\n", hdd, rate); + return TRUE; +} + +/************************************************************************* + * DrawDibStop [MSVFW32.@] + */ +BOOL VFWAPI DrawDibStop(HDRAWDIB hdd) { + FIXME("(%p), stub\n", hdd); + return TRUE; +} + +/*********************************************************************** + * DrawDibSetPalette [MSVFW32.@] + */ +BOOL VFWAPI DrawDibSetPalette(HDRAWDIB hdd, HPALETTE hpal) +{ + WINE_HDD *whdd; + + TRACE("(%p, %p)\n", hdd, hpal); + + whdd = MSVIDEO_GetHddPtr(hdd); + if (!whdd) return FALSE; + + whdd->hpal = hpal; + + if (whdd->begun) + { + SelectPalette(whdd->hdc, hpal, 0); + RealizePalette(whdd->hdc); + } + + return TRUE; +} + +/*********************************************************************** + * DrawDibGetPalette [MSVFW32.@] + */ +HPALETTE VFWAPI DrawDibGetPalette(HDRAWDIB hdd) +{ + WINE_HDD *whdd; + + TRACE("(%p)\n", hdd); + + whdd = MSVIDEO_GetHddPtr(hdd); + if (!whdd) return FALSE; + + return whdd->hpal; +} + +/*********************************************************************** + * DrawDibRealize [MSVFW32.@] + */ +UINT VFWAPI DrawDibRealize(HDRAWDIB hdd, HDC hdc, BOOL fBackground) +{ + WINE_HDD *whdd; + HPALETTE oldPal; + UINT ret = 0; + + FIXME("(%p, %p, %d), stub\n", hdd, hdc, fBackground); + + whdd = MSVIDEO_GetHddPtr(hdd); + if (!whdd) return FALSE; + + if (!whdd || !(whdd->begun)) + { + ret = 0; + goto out; + } + + if (!whdd->hpal) + whdd->hpal = CreateHalftonePalette(hdc); + + oldPal = SelectPalette(hdc, whdd->hpal, fBackground); + ret = RealizePalette(hdc); + + out: + TRACE("=> %u\n", ret); + return ret; +} + +DWORD VFWAPI DrawDibProfileDisplay(LPBITMAPINFOHEADER lpbi) +{ + FIXME("(%p) stub\n", lpbi); + + return PD_CAN_DRAW_DIB; +} diff --git a/reactos/dll/msvideo/mciwnd.c b/reactos/dll/msvideo/mciwnd.c new file mode 100644 index 00000000000..9260c01e719 --- /dev/null +++ b/reactos/dll/msvideo/mciwnd.c @@ -0,0 +1,1419 @@ +/* + * Copyright 2000 Eric Pouech + * Copyright 2003 Dmitry Timoshkov + * + * 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 + * + * FIXME: + * Add support for all remaining MCI_ commands and MCIWNDM_ messages. + * Add support for MCIWNDF_RECORD. + */ + +#include + +#include "windef.h" +#include "winbase.h" +#include "winnls.h" +#include "wingdi.h" +#include "winuser.h" +#include "winreg.h" +#include "winternl.h" +#include "vfw.h" +#include "digitalv.h" +#include "commctrl.h" +#include "wine/unicode.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(mci); + +extern HMODULE MSVFW32_hModule; +static const WCHAR mciWndClassW[] = {'M','C','I','W','n','d','C','l','a','s','s',0}; + +typedef struct +{ + DWORD dwStyle; + MCIDEVICEID mci; + HDRVR hdrv; + int alias; + UINT dev_type; + UINT mode; + long position; + SIZE size; /* size of the original frame rect */ + int zoom; + LPWSTR lpName; + HWND hWnd, hwndOwner; + UINT uTimer; + MCIERROR lasterror; + WCHAR return_string[128]; + WORD active_timer, inactive_timer; +} MCIWndInfo; + +static LRESULT WINAPI MCIWndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam); + +#define CTL_PLAYSTOP 0x3200 +#define CTL_MENU 0x3201 +#define CTL_TRACKBAR 0x3202 + +/*********************************************************************** + * MCIWndRegisterClass [MSVFW32.@] + * + * NOTE: Native always uses its own hInstance + */ +BOOL VFWAPIV MCIWndRegisterClass(void) +{ + WNDCLASSW wc; + + /* Since we are going to register a class belonging to MSVFW32 + * and later we will create windows with a different hInstance + * CS_GLOBALCLASS is needed. And because the second attempt + * to register a global class will fail we need to test whether + * the class was already registered. + */ + wc.style = CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS | CS_OWNDC | CS_GLOBALCLASS; + wc.lpfnWndProc = MCIWndProc; + wc.cbClsExtra = 0; + wc.cbWndExtra = sizeof(MCIWndInfo*); + wc.hInstance = MSVFW32_hModule; + wc.hIcon = 0; + wc.hCursor = LoadCursorW(0, MAKEINTRESOURCEW(IDC_ARROW)); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); + wc.lpszMenuName = NULL; + wc.lpszClassName = mciWndClassW; + + if (RegisterClassW(&wc)) return TRUE; + if (GetLastError() == ERROR_CLASS_ALREADY_EXISTS) return TRUE; + + return FALSE; +} + +/*********************************************************************** + * MCIWndCreateW [MSVFW32.@] + */ +HWND VFWAPIV MCIWndCreateW(HWND hwndParent, HINSTANCE hInstance, + DWORD dwStyle, LPCWSTR szFile) +{ + TRACE("%p %p %lx %s\n", hwndParent, hInstance, dwStyle, debugstr_w(szFile)); + + MCIWndRegisterClass(); + + if (!hInstance) hInstance = GetModuleHandleW(0); + + if (hwndParent) + dwStyle |= WS_VISIBLE | WS_BORDER /*| WS_CHILD*/; + else + dwStyle |= WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX; + + return CreateWindowExW(0, mciWndClassW, NULL, + dwStyle | WS_CLIPSIBLINGS | WS_CLIPCHILDREN, + 0, 0, 300, 0, + hwndParent, 0, hInstance, (LPVOID)szFile); +} + +/*********************************************************************** + * MCIWndCreate [MSVFW32.@] + * MCIWndCreateA [MSVFW32.@] + */ +HWND VFWAPIV MCIWndCreateA(HWND hwndParent, HINSTANCE hInstance, + DWORD dwStyle, LPCSTR szFile) +{ + HWND ret; + UNICODE_STRING fileW; + + if (szFile) + RtlCreateUnicodeStringFromAsciiz(&fileW, szFile); + else + fileW.Buffer = NULL; + + ret = MCIWndCreateW(hwndParent, hInstance, dwStyle, fileW.Buffer); + + RtlFreeUnicodeString(&fileW); + return ret; +} + +static inline void MCIWND_notify_mode(MCIWndInfo *mwi) +{ + if (mwi->dwStyle & MCIWNDF_NOTIFYMODE) + { + UINT new_mode = SendMessageW(mwi->hWnd, MCIWNDM_GETMODEW, 0, 0); + if (new_mode != mwi->mode) + { + mwi->mode = new_mode; + SendMessageW(mwi->hwndOwner, MCIWNDM_NOTIFYMODE, (WPARAM)mwi->hWnd, new_mode); + } + } +} + +static inline void MCIWND_notify_pos(MCIWndInfo *mwi) +{ + if (mwi->dwStyle & MCIWNDF_NOTIFYPOS) + { + long new_pos = SendMessageW(mwi->hWnd, MCIWNDM_GETPOSITIONW, 0, 0); + if (new_pos != mwi->position) + { + mwi->position = new_pos; + SendMessageW(mwi->hwndOwner, MCIWNDM_NOTIFYPOS, (WPARAM)mwi->hWnd, new_pos); + } + } +} + +static inline void MCIWND_notify_size(MCIWndInfo *mwi) +{ + if (mwi->dwStyle & MCIWNDF_NOTIFYSIZE) + SendMessageW(mwi->hwndOwner, MCIWNDM_NOTIFYSIZE, (WPARAM)mwi->hWnd, 0); +} + +static inline void MCIWND_notify_error(MCIWndInfo *mwi) +{ + if (mwi->dwStyle & MCIWNDF_NOTIFYERROR) + SendMessageW(mwi->hwndOwner, MCIWNDM_NOTIFYERROR, (WPARAM)mwi->hWnd, (LPARAM)mwi->lasterror); +} + +static void MCIWND_UpdateState(MCIWndInfo *mwi) +{ + WCHAR buffer[1024]; + + if (!mwi->mci) + { + /* FIXME: get this from resources */ + static const WCHAR no_deviceW[] = {'N','o',' ','D','e','v','i','c','e',0}; + SetWindowTextW(mwi->hWnd, no_deviceW); + return; + } + + MCIWND_notify_pos(mwi); + + if (!(mwi->dwStyle & MCIWNDF_NOPLAYBAR)) + SendDlgItemMessageW(mwi->hWnd, CTL_TRACKBAR, TBM_SETPOS, TRUE, mwi->position); + + if (!(mwi->dwStyle & MCIWNDF_SHOWALL)) + return; + + if ((mwi->dwStyle & MCIWNDF_SHOWNAME) && mwi->lpName) + strcpyW(buffer, mwi->lpName); + else + *buffer = 0; + + if (mwi->dwStyle & (MCIWNDF_SHOWPOS|MCIWNDF_SHOWMODE)) + { + static const WCHAR spaceW[] = {' ',0}; + static const WCHAR l_braceW[] = {'(',0}; + + if (*buffer) strcatW(buffer, spaceW); + strcatW(buffer, l_braceW); + } + + if (mwi->dwStyle & MCIWNDF_SHOWPOS) + { + WCHAR posW[64]; + + posW[0] = 0; + SendMessageW(mwi->hWnd, MCIWNDM_GETPOSITIONW, 64, (LPARAM)posW); + strcatW(buffer, posW); + } + + if ((mwi->dwStyle & (MCIWNDF_SHOWPOS|MCIWNDF_SHOWMODE)) == (MCIWNDF_SHOWPOS|MCIWNDF_SHOWMODE)) + { + static const WCHAR dashW[] = {' ','-',' ',0}; + strcatW(buffer, dashW); + } + + if (mwi->dwStyle & MCIWNDF_SHOWMODE) + { + WCHAR modeW[64]; + + modeW[0] = 0; + SendMessageW(mwi->hWnd, MCIWNDM_GETMODEW, 64, (LPARAM)modeW); + strcatW(buffer, modeW); + } + + if (mwi->dwStyle & (MCIWNDF_SHOWPOS|MCIWNDF_SHOWMODE)) + { + static const WCHAR r_braceW[] = {')',0}; + strcatW(buffer, r_braceW); + } + + TRACE("=> '%s'\n", debugstr_w(buffer)); + SetWindowTextW(mwi->hWnd, buffer); +} + +static LRESULT MCIWND_Create(HWND hWnd, LPCREATESTRUCTW cs) +{ + HWND hChld; + MCIWndInfo *mwi; + static const WCHAR buttonW[] = {'b','u','t','t','o','n',0}; + + mwi = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*mwi)); + if (!mwi) return -1; + + SetWindowLongW(hWnd, 0, (LPARAM)mwi); + + mwi->dwStyle = cs->style; + /* There is no need to show stats if there is no caption */ + if ((mwi->dwStyle & WS_CAPTION) != WS_CAPTION) + mwi->dwStyle &= ~MCIWNDF_SHOWALL; + + mwi->hWnd = hWnd; + mwi->hwndOwner = cs->hwndParent; + mwi->active_timer = 500; + mwi->inactive_timer = 2000; + mwi->mode = MCI_MODE_NOT_READY; + mwi->position = -1; + mwi->zoom = 100; + + if (!(mwi->dwStyle & MCIWNDF_NOMENU)) + { + static const WCHAR menuW[] = {'M','e','n','u',0}; + + hChld = CreateWindowExW(0, buttonW, menuW, WS_CHILD|WS_VISIBLE, 32, cs->cy, 32, 32, + hWnd, (HMENU)CTL_MENU, cs->hInstance, 0L); + TRACE("Get Button2: %p\n", hChld); + } + + if (!(mwi->dwStyle & MCIWNDF_NOPLAYBAR)) + { + INITCOMMONCONTROLSEX init; + static const WCHAR playW[] = {'P','l','a','y',0}; + + /* adding the other elements: play/stop button, menu button, status */ + hChld = CreateWindowExW(0, buttonW, playW, WS_CHILD|WS_VISIBLE, 0, cs->cy, 32, 32, + hWnd, (HMENU)CTL_PLAYSTOP, cs->hInstance, 0L); + TRACE("Get Button1: %p\n", hChld); + + init.dwSize = sizeof(init); + init.dwICC = ICC_BAR_CLASSES; + InitCommonControlsEx(&init); + + hChld = CreateWindowExW(0, TRACKBAR_CLASSW, NULL, WS_CHILD|WS_VISIBLE, 64, cs->cy, cs->cx - 64, 32, + hWnd, (HMENU)CTL_TRACKBAR, cs->hInstance, 0L); + TRACE("Get status: %p\n", hChld); + } + + /* This sets the default window size */ + SendMessageW(hWnd, MCI_CLOSE, 0, 0); + + if (cs->lpCreateParams) + { + LPARAM lParam; + + /* MCI wnd class is prepared to be embedded as an MDI child window */ + if (cs->dwExStyle & WS_EX_MDICHILD) + { + MDICREATESTRUCTW *mdics = (MDICREATESTRUCTW *)cs->lpCreateParams; + lParam = mdics->lParam; + } + else + lParam = (LPARAM)cs->lpCreateParams; + + /* If it's our internal class pointer, file name is a unicode string */ + if (cs->lpszClass == mciWndClassW) + SendMessageW(hWnd, MCIWNDM_OPENW, 0, lParam); + else + { + /* Otherwise let's try to figure out what string format is used */ + HWND parent = cs->hwndParent; + if (!parent) parent = GetWindow(hWnd, GW_OWNER); + + SendMessageW(hWnd, IsWindowUnicode(parent) ? MCIWNDM_OPENW : MCIWNDM_OPENA, 0, lParam); + } + } + + return 0; +} + +static void MCIWND_ToggleState(MCIWndInfo *mwi) +{ + switch (SendMessageW(mwi->hWnd, MCIWNDM_GETMODEW, 0, 0)) + { + case MCI_MODE_NOT_READY: + case MCI_MODE_RECORD: + case MCI_MODE_SEEK: + case MCI_MODE_OPEN: + TRACE("Cannot do much...\n"); + break; + + case MCI_MODE_PAUSE: + SendMessageW(mwi->hWnd, MCI_RESUME, 0, 0); + break; + + case MCI_MODE_PLAY: + SendMessageW(mwi->hWnd, MCI_PAUSE, 0, 0); + break; + + case MCI_MODE_STOP: + SendMessageW(mwi->hWnd, MCI_STOP, 0, 0); + break; + } +} + +static LRESULT MCIWND_Command(MCIWndInfo *mwi, WPARAM wParam, LPARAM lParam) +{ + switch (LOWORD(wParam)) + { + case CTL_PLAYSTOP: MCIWND_ToggleState(mwi); break; + case CTL_MENU: + case CTL_TRACKBAR: + default: + FIXME("support for command %04x not implement yet\n", LOWORD(wParam)); + } + return 0L; +} + +static void MCIWND_notify_media(MCIWndInfo *mwi) +{ + if (mwi->dwStyle & (MCIWNDF_NOTIFYMEDIAA | MCIWNDF_NOTIFYMEDIAW)) + { + if (!mwi->lpName) + { + static const WCHAR empty_str[1]; + SendMessageW(mwi->hwndOwner, MCIWNDM_NOTIFYMEDIA, (WPARAM)mwi->hWnd, (LPARAM)empty_str); + } + else + { + if (mwi->dwStyle & MCIWNDF_NOTIFYANSI) + { + char *ansi_name; + int len; + + len = WideCharToMultiByte(CP_ACP, 0, mwi->lpName, -1, NULL, 0, NULL, NULL); + ansi_name = HeapAlloc(GetProcessHeap(), 0, len); + WideCharToMultiByte(CP_ACP, 0, mwi->lpName, -1, ansi_name, len, NULL, NULL); + + SendMessageW(mwi->hwndOwner, MCIWNDM_NOTIFYMEDIA, (WPARAM)mwi->hWnd, (LPARAM)ansi_name); + + HeapFree(GetProcessHeap(), 0, ansi_name); + } + else + SendMessageW(mwi->hwndOwner, MCIWNDM_NOTIFYMEDIA, (WPARAM)mwi->hWnd, (LPARAM)mwi->lpName); + } + } +} + +static MCIERROR mci_generic_command(MCIWndInfo *mwi, UINT cmd) +{ + MCI_GENERIC_PARMS mci_generic; + + mci_generic.dwCallback = 0; + mwi->lasterror = mciSendCommandW(mwi->mci, cmd, 0, (DWORD_PTR)&mci_generic); + + if (mwi->lasterror) + return mwi->lasterror; + + MCIWND_notify_mode(mwi); + MCIWND_UpdateState(mwi); + return 0; +} + +static LRESULT mci_get_devcaps(MCIWndInfo *mwi, UINT cap) +{ + MCI_GETDEVCAPS_PARMS mci_devcaps; + + mci_devcaps.dwItem = cap; + mwi->lasterror = mciSendCommandW(mwi->mci, MCI_GETDEVCAPS, + MCI_GETDEVCAPS_ITEM, + (DWORD_PTR)&mci_devcaps); + if (mwi->lasterror) + return 0; + + return mci_devcaps.dwReturn; +} + +static LRESULT WINAPI MCIWndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam) +{ + MCIWndInfo *mwi; + + TRACE("%p %04x %08x %08lx\n", hWnd, wMsg, wParam, lParam); + + mwi = (MCIWndInfo*)GetWindowLongW(hWnd, 0); + if (!mwi && wMsg != WM_CREATE) + return DefWindowProcW(hWnd, wMsg, wParam, lParam); + + switch (wMsg) + { + case WM_CREATE: + MCIWND_Create(hWnd, (CREATESTRUCTW *)lParam); + break; + + case WM_DESTROY: + if (mwi->uTimer) + KillTimer(hWnd, mwi->uTimer); + + if (mwi->mci) + SendMessageW(hWnd, MCI_CLOSE, 0, 0); + + HeapFree(GetProcessHeap(), 0, mwi); + + DestroyWindow(GetDlgItem(hWnd, CTL_MENU)); + DestroyWindow(GetDlgItem(hWnd, CTL_PLAYSTOP)); + DestroyWindow(GetDlgItem(hWnd, CTL_TRACKBAR)); + break; + + case WM_PAINT: + { + MCI_DGV_UPDATE_PARMS mci_update; + PAINTSTRUCT ps; + + mci_update.hDC = (wParam) ? (HDC)wParam : BeginPaint(hWnd, &ps); + + mciSendCommandW(mwi->mci, MCI_UPDATE, + MCI_DGV_UPDATE_HDC | MCI_DGV_UPDATE_PAINT, + (DWORD_PTR)&mci_update); + + if (!wParam) EndPaint(hWnd, &ps); + return 1; + } + + case WM_COMMAND: + return MCIWND_Command(mwi, wParam, lParam); + + case WM_NCACTIVATE: + if (mwi->uTimer) + { + KillTimer(hWnd, mwi->uTimer); + mwi->uTimer = SetTimer(hWnd, 1, wParam ? mwi->active_timer : mwi->inactive_timer, NULL); + } + break; + + case WM_TIMER: + MCIWND_UpdateState(mwi); + return 0; + + case WM_SIZE: + SetWindowPos(GetDlgItem(hWnd, CTL_PLAYSTOP), 0, 0, HIWORD(lParam) - 32, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE); + SetWindowPos(GetDlgItem(hWnd, CTL_MENU), 0, 32, HIWORD(lParam) - 32, 0, 0, SWP_NOSIZE | SWP_NOACTIVATE); + SetWindowPos(GetDlgItem(hWnd, CTL_TRACKBAR), 0, 64, HIWORD(lParam) - 32, LOWORD(lParam) - 64, 32, SWP_NOACTIVATE); + + if (!(mwi->dwStyle & MCIWNDF_NOAUTOSIZEMOVIE)) + { + RECT rc; + + rc.left = rc.top = 0; + rc.right = LOWORD(lParam); + rc.bottom = HIWORD(lParam); + if (!(mwi->dwStyle & MCIWNDF_NOPLAYBAR)) + rc.bottom -= 32; /* subtract the height of the playbar */ + SendMessageW(hWnd, MCIWNDM_PUT_DEST, 0, (LPARAM)&rc); + } + MCIWND_notify_size(mwi); + break; + + case MM_MCINOTIFY: + MCIWND_notify_mode(mwi); + MCIWND_UpdateState(mwi); + return 0; + + case MCIWNDM_OPENA: + { + UNICODE_STRING nameW; + TRACE("MCIWNDM_OPENA %s\n", debugstr_a((LPSTR)lParam)); + RtlCreateUnicodeStringFromAsciiz(&nameW, (LPCSTR)lParam); + lParam = (LPARAM)nameW.Buffer; + } + /* fall through */ + case MCIWNDM_OPENW: + { + RECT rc; + HCURSOR hCursor; + MCI_OPEN_PARMSW mci_open; + MCI_GETDEVCAPS_PARMS mci_devcaps; + WCHAR aliasW[64]; + WCHAR drv_name[MAX_PATH]; + static const WCHAR formatW[] = {'%','d',0}; + static const WCHAR mci32W[] = {'m','c','i','3','2',0}; + static const WCHAR system_iniW[] = {'s','y','s','t','e','m','.','i','n','i',0}; + + TRACE("MCIWNDM_OPENW %s\n", debugstr_w((LPWSTR)lParam)); + + if (wParam == MCIWNDOPENF_NEW) + { + SendMessageW(hWnd, MCIWNDM_NEWW, 0, lParam); + goto end_of_mci_open; + } + + if (mwi->uTimer) + { + KillTimer(hWnd, mwi->uTimer); + mwi->uTimer = 0; + } + + hCursor = LoadCursorW(0, (LPWSTR)IDC_WAIT); + hCursor = SetCursor(hCursor); + + mci_open.lpstrElementName = (LPWSTR)lParam; + wsprintfW(aliasW, formatW, (int)hWnd + 1); + mci_open.lpstrAlias = aliasW; + mwi->lasterror = mciSendCommandW(mwi->mci, MCI_OPEN, + MCI_OPEN_ELEMENT | MCI_OPEN_ALIAS | MCI_WAIT, + (DWORD_PTR)&mci_open); + SetCursor(hCursor); + + if (mwi->lasterror && !(mwi->dwStyle & MCIWNDF_NOERRORDLG)) + { + /* FIXME: get the caption from resources */ + static const WCHAR caption[] = {'M','C','I',' ','E','r','r','o','r',0}; + WCHAR error_str[MAXERRORLENGTH]; + + mciGetErrorStringW(mwi->lasterror, error_str, MAXERRORLENGTH); + MessageBoxW(hWnd, error_str, caption, MB_ICONEXCLAMATION | MB_OK); + MCIWND_notify_error(mwi); + goto end_of_mci_open; + } + + mwi->mci = mci_open.wDeviceID; + mwi->alias = (int)hWnd + 1; + + mwi->lpName = HeapAlloc(GetProcessHeap(), 0, (strlenW((LPWSTR)lParam) + 1) * sizeof(WCHAR)); + strcpyW(mwi->lpName, (LPWSTR)lParam); + + MCIWND_UpdateState(mwi); + + mci_devcaps.dwItem = MCI_GETDEVCAPS_DEVICE_TYPE; + mwi->lasterror = mciSendCommandW(mwi->mci, MCI_GETDEVCAPS, + MCI_GETDEVCAPS_ITEM, + (DWORD_PTR)&mci_devcaps); + if (mwi->lasterror) + { + MCIWND_notify_error(mwi); + goto end_of_mci_open; + } + + mwi->dev_type = mci_devcaps.dwReturn; + + drv_name[0] = 0; + SendMessageW(hWnd, MCIWNDM_GETDEVICEW, 256, (LPARAM)drv_name); + if (drv_name[0] && GetPrivateProfileStringW(mci32W, drv_name, NULL, + drv_name, MAX_PATH, system_iniW)) + mwi->hdrv = OpenDriver(drv_name, NULL, 0); + + if (mwi->dev_type == MCI_DEVTYPE_DIGITAL_VIDEO) + { + MCI_DGV_WINDOW_PARMSW mci_window; + + mci_window.hWnd = hWnd; + mwi->lasterror = mciSendCommandW(mwi->mci, MCI_WINDOW, + MCI_DGV_WINDOW_HWND, + (DWORD_PTR)&mci_window); + if (mwi->lasterror) + { + MCIWND_notify_error(mwi); + goto end_of_mci_open; + } + } + + if (SendMessageW(hWnd, MCIWNDM_GET_DEST, 0, (LPARAM)&rc) == 0) + { + mwi->size.cx = rc.right - rc.left; + mwi->size.cy = rc.bottom - rc.top; + + rc.right = MulDiv(mwi->size.cx, mwi->zoom, 100); + rc.bottom = MulDiv(mwi->size.cy, mwi->zoom, 100); + SendMessageW(hWnd, MCIWNDM_PUT_DEST, 0, (LPARAM)&rc); + } + else + { + GetClientRect(hWnd, &rc); + rc.bottom = rc.top; + } + + if (!(mwi->dwStyle & MCIWNDF_NOPLAYBAR)) + rc.bottom += 32; /* add the height of the playbar */ + AdjustWindowRect(&rc, GetWindowLongW(hWnd, GWL_STYLE), FALSE); + SetWindowPos(hWnd, 0, 0, 0, rc.right - rc.left, + rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); + + SendDlgItemMessageW(hWnd, CTL_TRACKBAR, TBM_SETRANGEMIN, 0L, 0L); + SendDlgItemMessageW(hWnd, CTL_TRACKBAR, TBM_SETRANGEMAX, 1, + SendMessageW(hWnd, MCIWNDM_GETLENGTH, 0, 0)); + mwi->uTimer = SetTimer(hWnd, 1, mwi->active_timer, NULL); + + MCIWND_notify_media(mwi); + +end_of_mci_open: + if (wMsg == MCIWNDM_OPENA) + HeapFree(GetProcessHeap(), 0, (void *)lParam); + return mwi->lasterror; + } + + case MCIWNDM_GETDEVICEID: + TRACE("MCIWNDM_GETDEVICEID\n"); + return mwi->mci; + + case MCIWNDM_GETALIAS: + TRACE("MCIWNDM_GETALIAS\n"); + return mwi->alias; + + case MCIWNDM_GET_SOURCE: + { + MCI_DGV_RECT_PARMS mci_rect; + + mwi->lasterror = mciSendCommandW(mwi->mci, MCI_WHERE, + MCI_DGV_WHERE_SOURCE, + (DWORD_PTR)&mci_rect); + if (mwi->lasterror) + { + MCIWND_notify_error(mwi); + return mwi->lasterror; + } + *(RECT *)lParam = mci_rect.rc; + TRACE("MCIWNDM_GET_SOURCE: %s\n", wine_dbgstr_rect(&mci_rect.rc)); + return 0; + } + + case MCIWNDM_GET_DEST: + { + MCI_DGV_RECT_PARMS mci_rect; + + mwi->lasterror = mciSendCommandW(mwi->mci, MCI_WHERE, + MCI_DGV_WHERE_DESTINATION, + (DWORD_PTR)&mci_rect); + if (mwi->lasterror) + { + MCIWND_notify_error(mwi); + return mwi->lasterror; + } + *(RECT *)lParam = mci_rect.rc; + TRACE("MCIWNDM_GET_DEST: %s\n", wine_dbgstr_rect(&mci_rect.rc)); + return 0; + } + + case MCIWNDM_PUT_SOURCE: + { + MCI_DGV_PUT_PARMS mci_put; + + mci_put.rc = *(RECT *)lParam; + TRACE("MCIWNDM_PUT_SOURCE: %s\n", wine_dbgstr_rect(&mci_put.rc)); + mwi->lasterror = mciSendCommandW(mwi->mci, MCI_PUT, + MCI_DGV_PUT_SOURCE, + (DWORD_PTR)&mci_put); + if (mwi->lasterror) + { + MCIWND_notify_error(mwi); + return mwi->lasterror; + } + return 0; + } + + case MCIWNDM_PUT_DEST: + { + MCI_DGV_PUT_PARMS mci_put; + + mci_put.rc = *(RECT *)lParam; + TRACE("MCIWNDM_PUT_DEST: %s\n", wine_dbgstr_rect(&mci_put.rc)); + + mwi->lasterror = mciSendCommandW(mwi->mci, MCI_PUT, + MCI_DGV_PUT_DESTINATION | MCI_DGV_RECT, + (DWORD_PTR)&mci_put); + if (mwi->lasterror) + { + MCIWND_notify_error(mwi); + return mwi->lasterror; + } + return 0; + } + + case MCIWNDM_GETLENGTH: + { + MCI_STATUS_PARMS mci_status; + + mci_status.dwItem = MCI_STATUS_LENGTH; + mwi->lasterror = mciSendCommandW(mwi->mci, MCI_STATUS, + MCI_STATUS_ITEM, + (DWORD_PTR)&mci_status); + if (mwi->lasterror) + { + MCIWND_notify_error(mwi); + return 0; + } + TRACE("MCIWNDM_GETLENGTH: %ld\n", mci_status.dwReturn); + return mci_status.dwReturn; + } + + case MCIWNDM_GETSTART: + { + MCI_STATUS_PARMS mci_status; + + mci_status.dwItem = MCI_STATUS_POSITION; + mwi->lasterror = mciSendCommandW(mwi->mci, MCI_STATUS, + MCI_STATUS_ITEM | MCI_STATUS_START, + (DWORD_PTR)&mci_status); + if (mwi->lasterror) + { + MCIWND_notify_error(mwi); + return 0; + } + TRACE("MCIWNDM_GETSTART: %ld\n", mci_status.dwReturn); + return mci_status.dwReturn; + } + + case MCIWNDM_GETEND: + { + LRESULT start, length; + + start = SendMessageW(hWnd, MCIWNDM_GETSTART, 0, 0); + length = SendMessageW(hWnd, MCIWNDM_GETLENGTH, 0, 0); + TRACE("MCIWNDM_GETEND: %ld\n", start + length); + return (start + length); + } + + case MCIWNDM_GETPOSITIONA: + case MCIWNDM_GETPOSITIONW: + { + MCI_STATUS_PARMS mci_status; + + TRACE("MCIWNDM_GETPOSITION\n"); + + /* get position string if requested */ + if (wParam && lParam) + { + if (wMsg == MCIWNDM_GETPOSITIONA) + { + char cmd[64]; + + wsprintfA(cmd, "status %d position", mwi->alias); + mwi->lasterror = mciSendStringA(cmd, (LPSTR)lParam, wParam, 0); + } + else + { + + WCHAR cmdW[64]; + static const WCHAR formatW[] = {'s','t','a','t','u','s',' ','%','d',' ','p','o','s','i','t','i','o','n',0}; + + wsprintfW(cmdW, formatW, mwi->alias); + mwi->lasterror = mciSendStringW(cmdW, (LPWSTR)lParam, wParam, 0); + } + + if (mwi->lasterror) + return 0; + } + + mci_status.dwItem = MCI_STATUS_POSITION; + mwi->lasterror = mciSendCommandW(mwi->mci, MCI_STATUS, + MCI_STATUS_ITEM, + (DWORD_PTR)&mci_status); + if (mwi->lasterror) + return 0; + + return mci_status.dwReturn; + } + + case MCIWNDM_GETMODEA: + case MCIWNDM_GETMODEW: + { + MCI_STATUS_PARMS mci_status; + + TRACE("MCIWNDM_GETMODE\n"); + + if (!mwi->mci) + return MCI_MODE_NOT_READY; + + /* get mode string if requested */ + if (wParam && lParam) + { + if (wMsg == MCIWNDM_GETMODEA) + { + char cmd[64]; + + wsprintfA(cmd, "status %d mode", mwi->alias); + mwi->lasterror = mciSendStringA(cmd, (LPSTR)lParam, wParam, 0); + } + else + { + + WCHAR cmdW[64]; + static const WCHAR formatW[] = {'s','t','a','t','u','s',' ','%','d',' ','m','o','d','e',0}; + + wsprintfW(cmdW, formatW, mwi->alias); + mwi->lasterror = mciSendStringW(cmdW, (LPWSTR)lParam, wParam, 0); + } + + if (mwi->lasterror) + return MCI_MODE_NOT_READY; + } + + mci_status.dwItem = MCI_STATUS_MODE; + mwi->lasterror = mciSendCommandW(mwi->mci, MCI_STATUS, + MCI_STATUS_ITEM, + (DWORD_PTR)&mci_status); + if (mwi->lasterror) + return MCI_MODE_NOT_READY; + + return mci_status.dwReturn; + } + + case MCIWNDM_PLAYFROM: + { + MCI_PLAY_PARMS mci_play; + + TRACE("MCIWNDM_PLAYFROM %08lx\n", lParam); + + mci_play.dwCallback = (DWORD_PTR)hWnd; + mci_play.dwFrom = lParam; + mwi->lasterror = mciSendCommandW(mwi->mci, MCI_PLAY, + MCI_FROM | MCI_NOTIFY, + (DWORD_PTR)&mci_play); + if (mwi->lasterror) + { + MCIWND_notify_error(mwi); + return mwi->lasterror; + } + + MCIWND_notify_mode(mwi); + MCIWND_UpdateState(mwi); + return 0; + } + + case MCIWNDM_PLAYTO: + { + MCI_PLAY_PARMS mci_play; + + TRACE("MCIWNDM_PLAYTO %08lx\n", lParam); + + mci_play.dwCallback = (DWORD_PTR)hWnd; + mci_play.dwTo = lParam; + mwi->lasterror = mciSendCommandW(mwi->mci, MCI_PLAY, + MCI_TO | MCI_NOTIFY, + (DWORD_PTR)&mci_play); + if (mwi->lasterror) + { + MCIWND_notify_error(mwi); + return mwi->lasterror; + } + + MCIWND_notify_mode(mwi); + MCIWND_UpdateState(mwi); + return 0; + } + + case MCIWNDM_PLAYREVERSE: + { + MCI_PLAY_PARMS mci_play; + DWORD flags = MCI_NOTIFY; + + TRACE("MCIWNDM_PLAYREVERSE %08lx\n", lParam); + + mci_play.dwCallback = (DWORD_PTR)hWnd; + mci_play.dwFrom = lParam; + switch (mwi->dev_type) + { + default: + case MCI_DEVTYPE_ANIMATION: + flags |= MCI_ANIM_PLAY_REVERSE; + break; + + case MCI_DEVTYPE_DIGITAL_VIDEO: + flags |= MCI_DGV_PLAY_REVERSE; + break; + +#ifdef MCI_VCR_PLAY_REVERSE + case MCI_DEVTYPE_VCR: + flags |= MCI_VCR_PLAY_REVERSE; + break; +#endif + + case MCI_DEVTYPE_VIDEODISC: + flags |= MCI_VD_PLAY_REVERSE; + break; + + } + mwi->lasterror = mciSendCommandW(mwi->mci, MCI_PLAY, + flags, (DWORD_PTR)&mci_play); + if (mwi->lasterror) + { + MCIWND_notify_error(mwi); + return mwi->lasterror; + } + + MCIWND_notify_mode(mwi); + MCIWND_UpdateState(mwi); + return 0; + } + + case MCIWNDM_GETERRORA: + mciGetErrorStringA(mwi->lasterror, (LPSTR)lParam, wParam); + TRACE("MCIWNDM_GETERRORA: %s\n", debugstr_an((LPSTR)lParam, wParam)); + return mwi->lasterror; + + case MCIWNDM_GETERRORW: + mciGetErrorStringW(mwi->lasterror, (LPWSTR)lParam, wParam); + TRACE("MCIWNDM_GETERRORW: %s\n", debugstr_wn((LPWSTR)lParam, wParam)); + return mwi->lasterror; + + case MCIWNDM_SETOWNER: + TRACE("MCIWNDM_SETOWNER %p\n", (HWND)wParam); + mwi->hwndOwner = (HWND)wParam; + return 0; + + case MCIWNDM_SENDSTRINGA: + { + UNICODE_STRING stringW; + + TRACE("MCIWNDM_SENDSTRINGA %s\n", debugstr_a((LPCSTR)lParam)); + + RtlCreateUnicodeStringFromAsciiz(&stringW, (LPCSTR)lParam); + lParam = (LPARAM)stringW.Buffer; + } + /* fall through */ + case MCIWNDM_SENDSTRINGW: + { + WCHAR *cmdW, *p; + + TRACE("MCIWNDM_SENDSTRINGW %s\n", debugstr_w((LPCWSTR)lParam)); + + p = strchrW((LPCWSTR)lParam, ' '); + if (p) + { + static const WCHAR formatW[] = {'%','d',' ',0}; + int len, pos; + + pos = p - (WCHAR *)lParam + 1; + len = lstrlenW((LPCWSTR)lParam) + 64; + + cmdW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + + memcpy(cmdW, (void *)lParam, pos * sizeof(WCHAR)); + wsprintfW(cmdW + pos, formatW, mwi->alias); + strcatW(cmdW, (WCHAR *)lParam + pos); + } + else + cmdW = (LPWSTR)lParam; + + mwi->lasterror = mciSendStringW(cmdW, mwi->return_string, + sizeof(mwi->return_string)/sizeof(mwi->return_string[0]), + 0); + if (mwi->lasterror) + MCIWND_notify_error(mwi); + + if (cmdW != (LPWSTR)lParam) + HeapFree(GetProcessHeap(), 0, cmdW); + + if (wMsg == MCIWNDM_SENDSTRINGA) + HeapFree(GetProcessHeap(), 0, (void *)lParam); + + MCIWND_UpdateState(mwi); + return mwi->lasterror; + } + + case MCIWNDM_RETURNSTRINGA: + WideCharToMultiByte(CP_ACP, 0, mwi->return_string, -1, (LPSTR)lParam, wParam, NULL, NULL); + TRACE("MCIWNDM_RETURNTRINGA %s\n", debugstr_an((LPSTR)lParam, wParam)); + return mwi->lasterror; + + case MCIWNDM_RETURNSTRINGW: + strncpyW((LPWSTR)lParam, mwi->return_string, wParam); + TRACE("MCIWNDM_RETURNTRINGW %s\n", debugstr_wn((LPWSTR)lParam, wParam)); + return mwi->lasterror; + + case MCIWNDM_SETTIMERS: + TRACE("MCIWNDM_SETTIMERS active %d ms, inactive %d ms\n", (int)wParam, (int)lParam); + mwi->active_timer = (WORD)wParam; + mwi->inactive_timer = (WORD)lParam; + return 0; + + case MCIWNDM_SETACTIVETIMER: + TRACE("MCIWNDM_SETACTIVETIMER %d ms\n", (int)wParam); + mwi->active_timer = (WORD)wParam; + return 0; + + case MCIWNDM_SETINACTIVETIMER: + TRACE("MCIWNDM_SETINACTIVETIMER %d ms\n", (int)wParam); + mwi->inactive_timer = (WORD)wParam; + return 0; + + case MCIWNDM_GETACTIVETIMER: + TRACE("MCIWNDM_GETACTIVETIMER: %d ms\n", mwi->active_timer); + return mwi->active_timer; + + case MCIWNDM_GETINACTIVETIMER: + TRACE("MCIWNDM_GETINACTIVETIMER: %d ms\n", mwi->inactive_timer); + return mwi->inactive_timer; + + case MCIWNDM_CHANGESTYLES: + TRACE("MCIWNDM_CHANGESTYLES mask %08x, set %08lx\n", wParam, lParam); + /* FIXME: update the visual window state as well: + * add/remove trackbar, autosize, etc. + */ + mwi->dwStyle &= ~wParam; + mwi->dwStyle |= lParam & wParam; + return 0; + + case MCIWNDM_GETSTYLES: + TRACE("MCIWNDM_GETSTYLES: %08lx\n", mwi->dwStyle & 0xffff); + return mwi->dwStyle & 0xffff; + + case MCIWNDM_GETDEVICEA: + { + MCI_SYSINFO_PARMSA mci_sysinfo; + + mci_sysinfo.lpstrReturn = (LPSTR)lParam; + mci_sysinfo.dwRetSize = wParam; + mwi->lasterror = mciSendCommandA(mwi->mci, MCI_SYSINFO, + MCI_SYSINFO_INSTALLNAME, + (DWORD_PTR)&mci_sysinfo); + TRACE("MCIWNDM_GETDEVICEA: %s\n", debugstr_an((LPSTR)lParam, wParam)); + return 0; + } + + case MCIWNDM_GETDEVICEW: + { + MCI_SYSINFO_PARMSW mci_sysinfo; + + mci_sysinfo.lpstrReturn = (LPWSTR)lParam; + mci_sysinfo.dwRetSize = wParam; + mwi->lasterror = mciSendCommandW(mwi->mci, MCI_SYSINFO, + MCI_SYSINFO_INSTALLNAME, + (DWORD_PTR)&mci_sysinfo); + TRACE("MCIWNDM_GETDEVICEW: %s\n", debugstr_wn((LPWSTR)lParam, wParam)); + return 0; + } + + case MCIWNDM_VALIDATEMEDIA: + TRACE("MCIWNDM_VALIDATEMEDIA\n"); + if (mwi->mci) + { + SendMessageW(hWnd, MCIWNDM_GETSTART, 0, 0); + SendMessageW(hWnd, MCIWNDM_GETLENGTH, 0, 0); + } + return 0; + + case MCIWNDM_GETFILENAMEA: + TRACE("MCIWNDM_GETFILENAMEA: %s\n", debugstr_w(mwi->lpName)); + if (mwi->lpName) + WideCharToMultiByte(CP_ACP, 0, mwi->lpName, -1, (LPSTR)lParam, wParam, NULL, NULL); + return 0; + + case MCIWNDM_GETFILENAMEW: + TRACE("MCIWNDM_GETFILENAMEW: %s\n", debugstr_w(mwi->lpName)); + if (mwi->lpName) + strncpyW((LPWSTR)lParam, mwi->lpName, wParam); + return 0; + + case MCIWNDM_GETTIMEFORMATA: + case MCIWNDM_GETTIMEFORMATW: + { + MCI_STATUS_PARMS mci_status; + + TRACE("MCIWNDM_GETTIMEFORMAT %08x %08lx\n", wParam, lParam); + + /* get format string if requested */ + if (wParam && lParam) + { + if (wMsg == MCIWNDM_GETTIMEFORMATA) + { + char cmd[64]; + + wsprintfA(cmd, "status %d time format", mwi->alias); + mwi->lasterror = mciSendStringA(cmd, (LPSTR)lParam, wParam, 0); + if (mwi->lasterror) + return 0; + } + else + { + WCHAR cmdW[64]; + static const WCHAR formatW[] = {'s','t','a','t','u','s',' ','%','d',' ','t','i','m','e',' ','f','o','r','m','a','t',0}; + + wsprintfW(cmdW, formatW, mwi->alias); + mwi->lasterror = mciSendStringW(cmdW, (LPWSTR)lParam, wParam, 0); + if (mwi->lasterror) + return 0; + } + } + + mci_status.dwItem = MCI_STATUS_TIME_FORMAT ; + mwi->lasterror = mciSendCommandW(mwi->mci, MCI_STATUS, + MCI_STATUS_ITEM, + (DWORD_PTR)&mci_status); + if (mwi->lasterror) + return 0; + + return mci_status.dwReturn; + } + + case MCIWNDM_SETTIMEFORMATA: + { + UNICODE_STRING stringW; + + TRACE("MCIWNDM_SETTIMEFORMATA %s\n", debugstr_a((LPSTR)lParam)); + + RtlCreateUnicodeStringFromAsciiz(&stringW, (LPCSTR)lParam); + lParam = (LPARAM)stringW.Buffer; + } + /* fall through */ + case MCIWNDM_SETTIMEFORMATW: + { + static const WCHAR formatW[] = {'s','e','t',' ','%','d',' ','t','i','m','e',' ','f','o','r','m','a','t',' ',0}; + WCHAR *cmdW; + + TRACE("MCIWNDM_SETTIMEFORMATW %s\n", debugstr_w((LPWSTR)lParam)); + + if (mwi->mci) + { + cmdW = HeapAlloc(GetProcessHeap(), 0, (lstrlenW((LPCWSTR)lParam) + 64) * sizeof(WCHAR)); + wsprintfW(cmdW, formatW, mwi->alias); + strcatW(cmdW, (WCHAR *)lParam); + + mwi->lasterror = mciSendStringW(cmdW, NULL, 0, 0); + + /* fix the range tracking according to the new time format */ + if (!mwi->lasterror) + SendDlgItemMessageW(hWnd, CTL_TRACKBAR, TBM_SETRANGEMAX, 1, + SendMessageW(hWnd, MCIWNDM_GETLENGTH, 0, 0)); + } + + if (wMsg == MCIWNDM_SETTIMEFORMATA) + HeapFree(GetProcessHeap(), 0, (void *)lParam); + + return 0; + } + + case MCIWNDM_CAN_PLAY: + TRACE("MCIWNDM_CAN_PLAY\n"); + if (mwi->mci) + return mci_get_devcaps(mwi, MCI_GETDEVCAPS_CAN_PLAY); + return 0; + + case MCIWNDM_CAN_RECORD: + TRACE("MCIWNDM_CAN_RECORD\n"); + if (mwi->mci) + return mci_get_devcaps(mwi, MCI_GETDEVCAPS_CAN_RECORD); + return 0; + + case MCIWNDM_CAN_SAVE: + TRACE("MCIWNDM_CAN_SAVE\n"); + if (mwi->mci) + return mci_get_devcaps(mwi, MCI_GETDEVCAPS_CAN_SAVE); + return 0; + + case MCIWNDM_CAN_EJECT: + TRACE("MCIWNDM_CAN_EJECT\n"); + if (mwi->mci) + return mci_get_devcaps(mwi, MCI_GETDEVCAPS_CAN_EJECT); + return 0; + + case MCIWNDM_CAN_WINDOW: + TRACE("MCIWNDM_CAN_WINDOW\n"); + switch (mwi->dev_type) + { + case MCI_DEVTYPE_ANIMATION: + case MCI_DEVTYPE_DIGITAL_VIDEO: + case MCI_DEVTYPE_OVERLAY: + return 1; + } + return 0; + + case MCIWNDM_CAN_CONFIG: + TRACE("MCIWNDM_CAN_CONFIG\n"); + if (mwi->hdrv) + return SendDriverMessage(mwi->hdrv, DRV_QUERYCONFIGURE, 0, 0); + return 0; + + case MCIWNDM_SETZOOM: + TRACE("MCIWNDM_SETZOOM %ld\n", lParam); + mwi->zoom = lParam; + + if (mwi->mci && !(mwi->dwStyle & MCIWNDF_NOAUTOSIZEWINDOW)) + { + RECT rc; + + rc.left = rc.top = 0; + rc.right = MulDiv(mwi->size.cx, mwi->zoom, 100); + rc.bottom = MulDiv(mwi->size.cy, mwi->zoom, 100); + + if (!(mwi->dwStyle & MCIWNDF_NOPLAYBAR)) + rc.bottom += 32; /* add the height of the playbar */ + AdjustWindowRect(&rc, GetWindowLongW(hWnd, GWL_STYLE), FALSE); + SetWindowPos(hWnd, 0, 0, 0, rc.right - rc.left, rc.bottom - rc.top, + SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); + } + return 0; + + case MCIWNDM_GETZOOM: + TRACE("MCIWNDM_GETZOOM: %d\n", mwi->zoom); + return mwi->zoom; + + case MCIWNDM_EJECT: + { + MCI_SET_PARMS mci_set; + + TRACE("MCIWNDM_EJECT\n"); + + mci_set.dwCallback = (DWORD_PTR)hWnd; + mwi->lasterror = mciSendCommandW(mwi->mci, MCI_SET, + MCI_SET_DOOR_OPEN | MCI_NOTIFY, + (DWORD_PTR)&mci_set); + MCIWND_notify_mode(mwi); + MCIWND_UpdateState(mwi); + return mwi->lasterror; + } + + case MCIWNDM_SETVOLUME: + case MCIWNDM_GETVOLUME: + case MCIWNDM_SETSPEED: + case MCIWNDM_GETSPEED: + case MCIWNDM_SETREPEAT: + case MCIWNDM_GETREPEAT: + case MCIWNDM_REALIZE: + case MCIWNDM_GETPALETTE: + case MCIWNDM_SETPALETTE: + case MCIWNDM_NEWA: + case MCIWNDM_NEWW: + case MCIWNDM_PALETTEKICK: + case MCIWNDM_OPENINTERFACE: + FIXME("support for MCIWNDM_ message WM_USER+%d not implemented\n", wMsg - WM_USER); + return 0; + + case MCI_PLAY: + { + LRESULT end = SendMessageW(hWnd, MCIWNDM_GETEND, 0, 0); + return SendMessageW(hWnd, MCIWNDM_PLAYTO, 0, end); + } + + case MCI_SEEK: + { + MCI_SEEK_PARMS mci_seek; + + switch (lParam) + { + case MCIWND_START: + lParam = SendMessageW(hWnd, MCIWNDM_GETSTART, 0, 0); + break; + + case MCIWND_END: + lParam = SendMessageW(hWnd, MCIWNDM_GETEND, 0, 0); + break; + } + + mci_seek.dwTo = lParam; + mwi->lasterror = mciSendCommandW(mwi->mci, MCI_SEEK, + MCI_TO, (DWORD_PTR)&mci_seek); + if (mwi->lasterror) + { + MCIWND_notify_error(mwi); + return mwi->lasterror; + } + /* update window to reflect the state */ + InvalidateRect(hWnd, NULL, TRUE); + return 0; + } + + case MCI_CLOSE: + { + RECT rc; + MCI_GENERIC_PARMS mci_generic; + + if (mwi->hdrv) + { + CloseDriver(mwi->hdrv, 0, 0); + mwi->hdrv = 0; + } + + if (mwi->mci) + { + mci_generic.dwCallback = 0; + mwi->lasterror = mciSendCommandW(mwi->mci, MCI_CLOSE, + 0, (DWORD_PTR)&mci_generic); + mwi->mci = 0; + } + + mwi->mode = MCI_MODE_NOT_READY; + mwi->position = -1; + + if (mwi->lpName) + { + HeapFree(GetProcessHeap(), 0, mwi->lpName); + mwi->lpName = NULL; + } + MCIWND_UpdateState(mwi); + + GetClientRect(hWnd, &rc); + rc.bottom = rc.top; + if (!(mwi->dwStyle & MCIWNDF_NOPLAYBAR)) + rc.bottom += 32; /* add the height of the playbar */ + AdjustWindowRect(&rc, GetWindowLongW(hWnd, GWL_STYLE), FALSE); + SetWindowPos(hWnd, 0, 0, 0, rc.right - rc.left, + rc.bottom - rc.top, SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE); + + MCIWND_notify_media(mwi); + return 0; + } + + case MCI_PAUSE: + case MCI_STEP: + case MCI_STOP: + case MCI_RESUME: + mci_generic_command(mwi, wMsg); + if (wMsg == MCI_STEP && !mwi->lasterror) + { + /* update window to reflect the state */ + InvalidateRect(hWnd, NULL, TRUE); + } + return mwi->lasterror; + + case MCI_CONFIGURE: + if (mwi->hdrv) + SendDriverMessage(mwi->hdrv, DRV_CONFIGURE, (LPARAM)hWnd, 0); + return 0; + + case MCI_BREAK: + case MCI_CAPTURE: + case MCI_COPY: + case MCI_CUE: + case MCI_CUT: + case MCI_DELETE: + case MCI_ESCAPE: + case MCI_FREEZE: + case MCI_GETDEVCAPS: + /*case MCI_INDEX:*/ + case MCI_INFO: + case MCI_LIST: + case MCI_LOAD: + /*case MCI_MARK:*/ + case MCI_MONITOR: + case MCI_OPEN: + case MCI_PASTE: + case MCI_PUT: + case MCI_QUALITY: + case MCI_REALIZE: + case MCI_RECORD: + case MCI_RESERVE: + case MCI_RESTORE: + case MCI_SAVE: + case MCI_SET: + case MCI_SETAUDIO: + /*case MCI_SETTIMECODE:*/ + /*case MCI_SETTUNER:*/ + case MCI_SETVIDEO: + case MCI_SIGNAL: + case MCI_SPIN: + case MCI_STATUS: + case MCI_SYSINFO: + case MCI_UNDO: + case MCI_UNFREEZE: + case MCI_UPDATE: + case MCI_WHERE: + case MCI_WINDOW: + FIXME("support for MCI_ command %04x not implemented\n", wMsg); + return 0; + } + + if (wMsg >= WM_USER) + { + FIXME("support for MCIWNDM_ message WM_USER+%d not implemented\n", wMsg - WM_USER); + return 0; + } + + if (GetWindowLongW(hWnd, GWL_EXSTYLE) & WS_EX_MDICHILD) + return DefMDIChildProcW(hWnd, wMsg, wParam, lParam); + + return DefWindowProcW(hWnd, wMsg, wParam, lParam); +} diff --git a/reactos/dll/msvideo/msvfw32.rc b/reactos/dll/msvideo/msvfw32.rc new file mode 100644 index 00000000000..75c98f04be1 --- /dev/null +++ b/reactos/dll/msvideo/msvfw32.rc @@ -0,0 +1,5 @@ +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "MSvideo support\0" +#define REACTOS_STR_INTERNAL_NAME "msvfw32\0" +#define REACTOS_STR_ORIGINAL_FILENAME "msvfw32.dll\0" +#include diff --git a/reactos/dll/msvideo/msvfw32.spec b/reactos/dll/msvideo/msvfw32.spec new file mode 100644 index 00000000000..9590f91bf3a --- /dev/null +++ b/reactos/dll/msvideo/msvfw32.spec @@ -0,0 +1,51 @@ +# Yes, ICCompress,ICDecompress,MCIWnd* and ICDraw* are cdecl (VFWAPIV). +# The rest is stdcall (VFWAPI) however. -Marcus Meissner, 990124 + +2 stdcall VideoForWindowsVersion() + +@ stdcall DrawDibBegin(long long long long ptr long long long) +@ stub DrawDibChangePalette +@ stdcall DrawDibClose(long) +@ stdcall DrawDibDraw(long long long long long long ptr ptr long long long long long) +@ stdcall DrawDibEnd(long) +@ stub DrawDibGetBuffer +@ stdcall DrawDibGetPalette(long) +@ stdcall DrawDibOpen() +@ stdcall DrawDibProfileDisplay(ptr) +@ stdcall DrawDibRealize(long long long) +@ stdcall DrawDibSetPalette(long long) +@ stdcall DrawDibStart(long long) +@ stdcall DrawDibStop(long) +@ stub DrawDibTime +@ stub GetOpenFileNamePreview +@ stdcall GetOpenFileNamePreviewA(ptr) +@ stdcall GetOpenFileNamePreviewW(ptr) +@ stdcall GetSaveFileNamePreviewA(ptr) +@ stdcall GetSaveFileNamePreviewW(ptr) +@ stdcall ICClose(long) +@ cdecl ICCompress(long long ptr ptr ptr ptr ptr ptr long long long ptr ptr) +@ stdcall ICCompressorChoose(long long ptr ptr ptr ptr) +@ stdcall ICCompressorFree(ptr) +@ cdecl ICDecompress(long long ptr ptr ptr ptr) +@ cdecl ICDraw(long long ptr ptr long long) +@ cdecl ICDrawBegin(long long long long long long long long long ptr long long long long long long) +@ stdcall ICGetDisplayFormat(long ptr ptr long long long) +@ stdcall ICGetInfo(long ptr long) +@ stdcall ICImageCompress(long long ptr ptr ptr long ptr) +@ stdcall ICImageDecompress(long long ptr ptr ptr) +@ stdcall ICInfo(long long ptr) +@ stdcall ICInstall(long long ptr str long) +@ stdcall ICLocate(long long ptr ptr long) +@ stub ICMThunk +@ stdcall ICOpen(long long long) +@ stdcall ICOpenFunction(long long long ptr) +@ stdcall ICRemove(long long long) +@ stdcall ICSendMessage(long long long long) +@ stub ICSeqCompressFrame +@ stub ICSeqCompressFrameEnd +@ stub ICSeqCompressFrameStart +@ cdecl MCIWndCreate (long long long str) MCIWndCreateA +@ cdecl MCIWndCreateA (long long long str) +@ cdecl MCIWndCreateW (long long long wstr) +@ cdecl MCIWndRegisterClass() +@ stub StretchDIB diff --git a/reactos/dll/msvideo/msvfw32.xml b/reactos/dll/msvideo/msvfw32.xml new file mode 100644 index 00000000000..d8bc0c948c1 --- /dev/null +++ b/reactos/dll/msvideo/msvfw32.xml @@ -0,0 +1,20 @@ + + + include/wine + + + 0x600 + 0x501 + 0x501 + winmm + gdi32 + comctl32 + kernel32 + ntdll + wine + drawdib.c + mciwnd.c + msvfw32.rc + msvideo_main.c + msvfw32.spec + diff --git a/reactos/dll/msvideo/msvideo.spec b/reactos/dll/msvideo/msvideo.spec new file mode 100644 index 00000000000..a6f9a876f20 --- /dev/null +++ b/reactos/dll/msvideo/msvideo.spec @@ -0,0 +1,68 @@ +2 pascal VideoForWindowsVersion() VideoForWindowsVersion +20 stub VIDEOGETNUMDEVS +21 stub VIDEOGETERRORTEXT +22 pascal VideoCapDriverDescAndVer(word ptr word ptr word) VideoCapDriverDescAndVer16 +28 stub VIDEOOPEN +29 stub VIDEOCLOSE +30 stub VIDEODIALOG +31 stub VIDEOFRAME +32 stub VIDEOCONFIGURE +33 stub VIDEOCONFIGURESTORAGE +34 stub VIDEOGETCHANNELCAPS +35 stub VIDEOUPDATE +40 stub VIDEOSTREAMADDBUFFER +41 stub VIDEOSTREAMFINI +42 stub VIDEOSTREAMGETERROR +43 stub VIDEOSTREAMGETPOSITION +44 stub VIDEOSTREAMINIT +46 stub VIDEOSTREAMPREPAREHEADER +47 stub VIDEOSTREAMRESET +49 stub VIDEOSTREAMSTART +50 stub VIDEOSTREAMSTOP +51 stub VIDEOSTREAMUNPREPAREHEADER +52 stub VIDEOSTREAMALLOCHDRANDBUFFER +53 stub VIDEOSTREAMFREEHDRANDBUFFER +60 stub VIDEOMESSAGE +102 pascal -ret16 DrawDibOpen() DrawDibOpen16 +103 pascal -ret16 DrawDibClose(word) DrawDibClose16 +104 pascal -ret16 DrawDibBegin(word word s_word s_word ptr s_word s_word word) DrawDibBegin16 +105 pascal -ret16 DrawDibEnd(word) DrawDibEnd16 +106 pascal -ret16 DrawDibDraw(word word s_word s_word s_word s_word ptr ptr s_word s_word s_word s_word word) DrawDibDraw16 +108 pascal -ret16 DrawDibGetPalette(word) DrawDibGetPalette16 +110 pascal -ret16 DrawDibSetPalette(word word) DrawDibSetPalette16 +111 stub DRAWDIBCHANGEPALETTE +112 pascal -ret16 DrawDibRealize(word word word) DrawDibRealize16 +113 stub DRAWDIBTIME +114 stub DRAWDIBPROFILEDISPLAY +115 stub STRETCHDIB +118 pascal -ret16 DrawDibStart(word long) DrawDibStart16 +119 pascal -ret16 DrawDibStop(word) DrawDibStop16 +120 stub DRAWDIBGETBUFFER +200 pascal -ret16 ICInfo(long long segptr) ICInfo16 +201 stub ICINSTALL +202 stub ICREMOVE +203 pascal -ret16 ICOpen(long long word) ICOpen16 +204 pascal ICClose(word) ICClose16 +205 pascal ICSendMessage(word word long long) ICSendMessage16 +206 pascal -ret16 ICOpenFunction(long long word segptr) ICOpenFunction16 +207 varargs _ICMessage(word word word) ICMessage16 +212 pascal ICGetInfo(word segptr long) ICGetInfo16 +213 pascal -ret16 ICLocate(long long ptr ptr word) ICLocate16 +224 cdecl _ICCompress(word long segptr segptr segptr segptr segptr segptr long long long segptr segptr) ICCompress16 +230 cdecl _ICDecompress(word long segptr segptr segptr segptr) ICDecompress16 +232 cdecl _ICDrawBegin(word long word word word s_word s_word s_word s_word segptr s_word s_word s_word s_word long long) ICDrawBegin16 +234 cdecl _ICDraw(word long segptr segptr long long) ICDraw16 +239 pascal -ret16 ICGetDisplayFormat(word ptr ptr s_word s_word s_word) ICGetDisplayFormat16 +240 stub ICIMAGECOMPRESS +241 stub ICIMAGEDECOMPRESS +242 stub ICCOMPRESSORCHOOSE +243 stub ICCOMPRESSORFREE +244 stub ICSEQCOMPRESSFRAMESTART +245 stub ICSEQCOMPRESSFRAMEEND +246 stub ICSEQCOMPRESSFRAME +250 stub _MCIWNDCREATE +251 stub _MCIWNDREGISTERCLASS +252 stub GETOPENFILENAMEPREVIEW +253 stub GETSAVEFILENAMEPREVIEW + +300 pascal DllEntryPoint(long word word word long word) VIDEO_LibMain diff --git a/reactos/dll/msvideo/msvideo16.c b/reactos/dll/msvideo/msvideo16.c new file mode 100644 index 00000000000..2d5596e9b9f --- /dev/null +++ b/reactos/dll/msvideo/msvideo16.c @@ -0,0 +1,910 @@ +/* + * msvideo 16-bit functions + * + * Copyright 1998 Marcus Meissner + * Copyright 2000 Bradley Baetz + * + * 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 + */ + +#define COM_NO_WINDOWS_H +#include +#include + +#include "msvideo_private.h" +#include "winver.h" +#include "winnls.h" +#include "winreg.h" +#include "vfw16.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msvideo); + +/* Drivers32 settings */ +#define HKLM_DRIVERS32 "Software\\Microsoft\\Windows NT\\CurrentVersion\\Drivers32" + +/*********************************************************************** + * DrawDibOpen [MSVIDEO.102] + */ +HDRAWDIB16 VFWAPI DrawDibOpen16(void) +{ + return HDRAWDIB_16(DrawDibOpen()); +} + +/*********************************************************************** + * DrawDibClose [MSVIDEO.103] + */ +BOOL16 VFWAPI DrawDibClose16(HDRAWDIB16 hdd) +{ + return DrawDibClose(HDRAWDIB_32(hdd)); +} + +/************************************************************************ + * DrawDibBegin [MSVIDEO.104] + */ +BOOL16 VFWAPI DrawDibBegin16(HDRAWDIB16 hdd, HDC16 hdc, INT16 dxDst, + INT16 dyDst, LPBITMAPINFOHEADER lpbi, INT16 dxSrc, + INT16 dySrc, UINT16 wFlags) +{ + return DrawDibBegin(HDRAWDIB_32(hdd), HDC_32(hdc), dxDst, dyDst, lpbi, + dxSrc, dySrc, wFlags); +} + +/*********************************************************************** + * DrawDibEnd [MSVIDEO.105] + */ +BOOL16 VFWAPI DrawDibEnd16(HDRAWDIB16 hdd) +{ + return DrawDibEnd(HDRAWDIB_32(hdd)); +} + +/********************************************************************** + * DrawDibDraw [MSVIDEO.106] + */ +BOOL16 VFWAPI DrawDibDraw16(HDRAWDIB16 hdd, HDC16 hdc, INT16 xDst, INT16 yDst, + INT16 dxDst, INT16 dyDst, LPBITMAPINFOHEADER lpbi, + LPVOID lpBits, INT16 xSrc, INT16 ySrc, INT16 dxSrc, + INT16 dySrc, UINT16 wFlags) +{ + return DrawDibDraw(HDRAWDIB_32(hdd), HDC_32(hdc), xDst, yDst, dxDst, + dyDst, lpbi, lpBits, xSrc, ySrc, dxSrc, dySrc, wFlags); +} + +/*********************************************************************** + * DrawDibGetPalette [MSVIDEO.108] + */ +HPALETTE16 VFWAPI DrawDibGetPalette16(HDRAWDIB16 hdd) +{ + return HPALETTE_16(DrawDibGetPalette(HDRAWDIB_32(hdd))); +} + +/*********************************************************************** + * DrawDibSetPalette [MSVIDEO.110] + */ +BOOL16 VFWAPI DrawDibSetPalette16(HDRAWDIB16 hdd, HPALETTE16 hpal) +{ + return DrawDibSetPalette(HDRAWDIB_32(hdd), HPALETTE_32(hpal)); +} + +/*********************************************************************** + * DrawDibRealize [MSVIDEO.112] + */ +UINT16 VFWAPI DrawDibRealize16(HDRAWDIB16 hdd, HDC16 hdc, + BOOL16 fBackground) +{ + return (UINT16)DrawDibRealize(HDRAWDIB_32(hdd), HDC_32(hdc), fBackground); +} + +/************************************************************************* + * DrawDibStart [MSVIDEO.118] + */ +BOOL16 VFWAPI DrawDibStart16(HDRAWDIB16 hdd, DWORD rate) +{ + return DrawDibStart(HDRAWDIB_32(hdd), rate); +} + +/************************************************************************* + * DrawDibStop [MSVIDEO.119] + */ +BOOL16 DrawDibStop16(HDRAWDIB16 hdd) +{ + return DrawDibStop(HDRAWDIB_32(hdd)); +} + +/*********************************************************************** + * ICOpen [MSVIDEO.203] + */ +HIC16 VFWAPI ICOpen16(DWORD fccType, DWORD fccHandler, UINT16 wMode) +{ + return HIC_16(ICOpen(fccType, fccHandler, wMode)); +} + +/*********************************************************************** + * ICClose [MSVIDEO.204] + */ +LRESULT WINAPI ICClose16(HIC16 hic) +{ + return ICClose(HIC_32(hic)); +} + +/*********************************************************************** + * _ICMessage [MSVIDEO.207] + */ +LRESULT VFWAPIV ICMessage16( HIC16 hic, UINT16 msg, UINT16 cb, VA_LIST16 valist ) +{ + LPWORD lpData; + SEGPTR segData; + LRESULT ret; + UINT16 i; + + lpData = HeapAlloc(GetProcessHeap(), 0, cb); + + TRACE("0x%08lx, %u, %u, ...)\n", (DWORD) hic, msg, cb); + + for (i = 0; i < cb / sizeof(WORD); i++) + { + lpData[i] = VA_ARG16(valist, WORD); + } + + segData = MapLS(lpData); + ret = ICSendMessage16(hic, msg, segData, (DWORD) cb); + UnMapLS(segData); + HeapFree(GetProcessHeap(), 0, lpData); + return ret; +} + +/*********************************************************************** + * ICGetInfo [MSVIDEO.212] + */ +LRESULT VFWAPI ICGetInfo16(HIC16 hic, ICINFO16 * picinfo, DWORD cb) +{ + LRESULT ret; + + TRACE("(0x%08lx,%p,%ld)\n", (DWORD) hic, picinfo, cb); + ret = ICSendMessage16(hic, ICM_GETINFO, (DWORD) picinfo, cb); + TRACE(" -> 0x%08lx\n", ret); + return ret; +} + +/*********************************************************************** + * ICLocate [MSVIDEO.213] + */ +HIC16 VFWAPI ICLocate16(DWORD fccType, DWORD fccHandler, + LPBITMAPINFOHEADER lpbiIn, LPBITMAPINFOHEADER lpbiOut, + WORD wFlags) +{ + return HIC_16(ICLocate(fccType, fccHandler, lpbiIn, lpbiOut, wFlags)); +} + +/*********************************************************************** + * _ICCompress [MSVIDEO.224] + */ +DWORD VFWAPIV ICCompress16(HIC16 hic, DWORD dwFlags, + LPBITMAPINFOHEADER lpbiOutput, LPVOID lpData, + LPBITMAPINFOHEADER lpbiInput, LPVOID lpBits, + LPDWORD lpckid, LPDWORD lpdwFlags, + LONG lFrameNum, DWORD dwFrameSize, + DWORD dwQuality, LPBITMAPINFOHEADER lpbiPrev, + LPVOID lpPrev) +{ + DWORD ret; + ICCOMPRESS iccmp; + SEGPTR seg_iccmp; + + TRACE("(0x%08lx,%ld,%p,%p,%p,%p,...)\n", (DWORD) hic, dwFlags, + lpbiOutput, lpData, lpbiInput, lpBits); + + iccmp.dwFlags = dwFlags; + + iccmp.lpbiOutput = lpbiOutput; + iccmp.lpOutput = lpData; + iccmp.lpbiInput = lpbiInput; + iccmp.lpInput = lpBits; + + iccmp.lpckid = lpckid; + iccmp.lpdwFlags = lpdwFlags; + iccmp.lFrameNum = lFrameNum; + iccmp.dwFrameSize = dwFrameSize; + iccmp.dwQuality = dwQuality; + iccmp.lpbiPrev = lpbiPrev; + iccmp.lpPrev = lpPrev; + seg_iccmp = MapLS(&iccmp); + ret = ICSendMessage16(hic, ICM_COMPRESS, seg_iccmp, sizeof(ICCOMPRESS)); + UnMapLS(seg_iccmp); + return ret; +} + +/*********************************************************************** + * _ICDecompress [MSVIDEO.230] + */ +DWORD VFWAPIV ICDecompress16(HIC16 hic, DWORD dwFlags, + LPBITMAPINFOHEADER lpbiFormat, LPVOID lpData, + LPBITMAPINFOHEADER lpbi, LPVOID lpBits) +{ + ICDECOMPRESS icd; + SEGPTR segptr; + DWORD ret; + + TRACE("(0x%08lx,%ld,%p,%p,%p,%p)\n", (DWORD) hic, dwFlags, lpbiFormat, + lpData, lpbi, lpBits); + + icd.dwFlags = dwFlags; + icd.lpbiInput = lpbiFormat; + icd.lpInput = lpData; + icd.lpbiOutput = lpbi; + icd.lpOutput = lpBits; + icd.ckid = 0; + segptr = MapLS(&icd); + ret = ICSendMessage16(hic, ICM_DECOMPRESS, segptr, sizeof(ICDECOMPRESS)); + UnMapLS(segptr); + return ret; +} + +/*********************************************************************** + * _ICDrawBegin [MSVIDEO.232] + */ +DWORD VFWAPIV ICDrawBegin16(HIC16 hic, /* [in] */ + DWORD dwFlags, /* [in] flags */ + HPALETTE16 hpal, /* [in] palette to draw with */ + HWND16 hwnd, /* [in] window to draw to */ + HDC16 hdc, /* [in] HDC to draw to */ + INT16 xDst, /* [in] destination rectangle */ + INT16 yDst, /* [in] */ + INT16 dxDst, /* [in] */ + INT16 dyDst, /* [in] */ + LPBITMAPINFOHEADER lpbi, /* [in] format of frame to draw NOTE: SEGPTR */ + INT16 xSrc, /* [in] source rectangle */ + INT16 ySrc, /* [in] */ + INT16 dxSrc, /* [in] */ + INT16 dySrc, /* [in] */ + DWORD dwRate, /* [in] frames/second = (dwRate/dwScale) */ + DWORD dwScale) /* [in] */ +{ + DWORD ret; + ICDRAWBEGIN16 icdb; + SEGPTR seg_icdb; + + TRACE ("(0x%08lx,%ld,0x%08lx,0x%08lx,0x%08lx,%u,%u,%u,%u,%p,%u,%u,%u,%u,%ld,%ld)\n", + (DWORD) hic, dwFlags, (DWORD) hpal, (DWORD) hwnd, (DWORD) hdc, + xDst, yDst, dxDst, dyDst, lpbi, xSrc, ySrc, dxSrc, dySrc, dwRate, + dwScale); + + icdb.dwFlags = dwFlags; + icdb.hpal = hpal; + icdb.hwnd = hwnd; + icdb.hdc = hdc; + icdb.xDst = xDst; + icdb.yDst = yDst; + icdb.dxDst = dxDst; + icdb.dyDst = dyDst; + icdb.lpbi = lpbi; /* Keep this as SEGPTR for the mapping code to deal with */ + icdb.xSrc = xSrc; + icdb.ySrc = ySrc; + icdb.dxSrc = dxSrc; + icdb.dySrc = dySrc; + icdb.dwRate = dwRate; + icdb.dwScale = dwScale; + seg_icdb = MapLS(&icdb); + ret = (DWORD) ICSendMessage16(hic, ICM_DRAW_BEGIN, seg_icdb, + sizeof(ICDRAWBEGIN16)); + UnMapLS(seg_icdb); + return ret; +} + +/*********************************************************************** + * _ICDraw [MSVIDEO.234] + */ +DWORD VFWAPIV ICDraw16(HIC16 hic, DWORD dwFlags, + LPVOID lpFormat, /* [???] NOTE: SEGPTR */ + LPVOID lpData, /* [???] NOTE: SEGPTR */ + DWORD cbData, LONG lTime) +{ + DWORD ret; + ICDRAW icd; + SEGPTR seg_icd; + + TRACE("(0x%08lx,0x%08lx,%p,%p,%ld,%ld)\n", (DWORD) hic, dwFlags, + lpFormat, lpData, cbData, lTime); + icd.dwFlags = dwFlags; + icd.lpFormat = lpFormat; + icd.lpData = lpData; + icd.cbData = cbData; + icd.lTime = lTime; + seg_icd = MapLS(&icd); + ret = ICSendMessage16(hic, ICM_DRAW, seg_icd, sizeof(ICDRAW)); + UnMapLS(seg_icd); + return ret; +} + +/*********************************************************************** + * ICGetDisplayFormat [MSVIDEO.239] + */ +HIC16 VFWAPI ICGetDisplayFormat16(HIC16 hic, LPBITMAPINFOHEADER lpbiIn, + LPBITMAPINFOHEADER lpbiOut, INT16 depth, + INT16 dx, INT16 dy) +{ + return HIC_16(ICGetDisplayFormat(HIC_32(hic), lpbiIn, lpbiOut, depth, + dx, dy)); +} + +#define COPY(x,y) (x->y = x##16->y); +#define COPYPTR(x,y) (x->y = MapSL((SEGPTR)x##16->y)); + +/****************************************************************** + * MSVIDEO_MapICDEX16To32 + * + * + */ +static LPVOID MSVIDEO_MapICDEX16To32(LPDWORD lParam) +{ + LPVOID ret; + + ICDECOMPRESSEX *icdx = HeapAlloc(GetProcessHeap(), 0, sizeof(ICDECOMPRESSEX)); + ICDECOMPRESSEX16 *icdx16 = MapSL(*lParam); + ret = icdx16; + + COPY(icdx, dwFlags); + COPYPTR(icdx, lpbiSrc); + COPYPTR(icdx, lpSrc); + COPYPTR(icdx, lpbiDst); + COPYPTR(icdx, lpDst); + COPY(icdx, xDst); + COPY(icdx, yDst); + COPY(icdx, dxDst); + COPY(icdx, dyDst); + COPY(icdx, xSrc); + COPY(icdx, ySrc); + COPY(icdx, dxSrc); + COPY(icdx, dySrc); + + *lParam = (DWORD)(icdx); + return ret; +} + +/****************************************************************** + * MSVIDEO_MapMsg16To32 + * + * + */ +static LPVOID MSVIDEO_MapMsg16To32(UINT msg, LPDWORD lParam1, LPDWORD lParam2) +{ + LPVOID ret = 0; + + TRACE("Mapping %d\n", msg); + + switch (msg) + { + case DRV_LOAD: + case DRV_ENABLE: + case DRV_CLOSE: + case DRV_DISABLE: + case DRV_FREE: + case ICM_ABOUT: + case ICM_CONFIGURE: + case ICM_COMPRESS_END: + case ICM_DECOMPRESS_END: + case ICM_DECOMPRESSEX_END: + case ICM_SETQUALITY: + case ICM_DRAW_START_PLAY: + case ICM_DRAW_STOP_PLAY: + case ICM_DRAW_REALIZE: + case ICM_DRAW_RENDERBUFFER: + case ICM_DRAW_END: + break; + case DRV_OPEN: + case ICM_GETDEFAULTQUALITY: + case ICM_GETQUALITY: + case ICM_SETSTATE: + case ICM_DRAW_WINDOW: + case ICM_GETBUFFERSWANTED: + *lParam1 = (DWORD)MapSL(*lParam1); + break; + case ICM_GETINFO: + { + ICINFO *ici = HeapAlloc(GetProcessHeap(), 0, sizeof(ICINFO)); + ICINFO16 *ici16; + + ici16 = MapSL(*lParam1); + ret = ici16; + + ici->dwSize = sizeof(ICINFO); + COPY(ici, fccType); + COPY(ici, fccHandler); + COPY(ici, dwFlags); + COPY(ici, dwVersion); + COPY(ici, dwVersionICM); + MultiByteToWideChar( CP_ACP, 0, ici16->szName, -1, ici->szName, 16 ); + MultiByteToWideChar( CP_ACP, 0, ici16->szDescription, -1, ici->szDescription, 128 ); + MultiByteToWideChar( CP_ACP, 0, ici16->szDriver, -1, ici->szDriver, 128 ); + *lParam1 = (DWORD)(ici); + *lParam2 = sizeof(ICINFO); + } + break; + case ICM_COMPRESS: + { + ICCOMPRESS *icc = HeapAlloc(GetProcessHeap(), 0, sizeof(ICCOMPRESS)); + ICCOMPRESS *icc16; + + icc16 = MapSL(*lParam1); + ret = icc16; + + COPY(icc, dwFlags); + COPYPTR(icc, lpbiOutput); + COPYPTR(icc, lpOutput); + COPYPTR(icc, lpbiInput); + COPYPTR(icc, lpInput); + COPYPTR(icc, lpckid); + COPYPTR(icc, lpdwFlags); + COPY(icc, lFrameNum); + COPY(icc, dwFrameSize); + COPY(icc, dwQuality); + COPYPTR(icc, lpbiPrev); + COPYPTR(icc, lpPrev); + + *lParam1 = (DWORD)(icc); + *lParam2 = sizeof(ICCOMPRESS); + } + break; + case ICM_DECOMPRESS: + { + ICDECOMPRESS *icd = HeapAlloc(GetProcessHeap(), 0, sizeof(ICDECOMPRESS)); + ICDECOMPRESS *icd16; /* Same structure except for the pointers */ + + icd16 = MapSL(*lParam1); + ret = icd16; + + COPY(icd, dwFlags); + COPYPTR(icd, lpbiInput); + COPYPTR(icd, lpInput); + COPYPTR(icd, lpbiOutput); + COPYPTR(icd, lpOutput); + COPY(icd, ckid); + + *lParam1 = (DWORD)(icd); + *lParam2 = sizeof(ICDECOMPRESS); + } + break; + case ICM_COMPRESS_BEGIN: + case ICM_COMPRESS_GET_FORMAT: + case ICM_COMPRESS_GET_SIZE: + case ICM_COMPRESS_QUERY: + case ICM_DECOMPRESS_GET_FORMAT: + case ICM_DECOMPRESS_QUERY: + case ICM_DECOMPRESS_BEGIN: + case ICM_DECOMPRESS_SET_PALETTE: + case ICM_DECOMPRESS_GET_PALETTE: + *lParam1 = (DWORD)MapSL(*lParam1); + *lParam2 = (DWORD)MapSL(*lParam2); + break; + case ICM_DECOMPRESSEX_QUERY: + if ((*lParam2 != sizeof(ICDECOMPRESSEX16)) && (*lParam2 != 0)) + WARN("*lParam2 has unknown value %p\n", (ICDECOMPRESSEX16*)*lParam2); + /* FIXME: *lParm2 is meant to be 0 or an ICDECOMPRESSEX16*, but is sizeof(ICDECOMRPESSEX16) + * This is because of ICMessage(). Special case it? + { + LPVOID* addr = HeapAlloc(GetProcessHeap(), 0, 2*sizeof(LPVOID)); + addr[0] = MSVIDEO_MapICDEX16To32(lParam1); + if (*lParam2) + addr[1] = MSVIDEO_MapICDEX16To32(lParam2); + else + addr[1] = 0; + + ret = addr; + } + break;*/ + case ICM_DECOMPRESSEX_BEGIN: + case ICM_DECOMPRESSEX: + ret = MSVIDEO_MapICDEX16To32(lParam1); + *lParam2 = sizeof(ICDECOMPRESSEX); + break; + case ICM_DRAW_BEGIN: + { + ICDRAWBEGIN *icdb = HeapAlloc(GetProcessHeap(), 0, sizeof(ICDRAWBEGIN)); + ICDRAWBEGIN16 *icdb16 = MapSL(*lParam1); + ret = icdb16; + + COPY(icdb, dwFlags); + icdb->hpal = HPALETTE_32(icdb16->hpal); + icdb->hwnd = HWND_32(icdb16->hwnd); + icdb->hdc = HDC_32(icdb16->hdc); + COPY(icdb, xDst); + COPY(icdb, yDst); + COPY(icdb, dxDst); + COPY(icdb, dyDst); + COPYPTR(icdb, lpbi); + COPY(icdb, xSrc); + COPY(icdb, ySrc); + COPY(icdb, dxSrc); + COPY(icdb, dySrc); + COPY(icdb, dwRate); + COPY(icdb, dwScale); + + *lParam1 = (DWORD)(icdb); + *lParam2 = sizeof(ICDRAWBEGIN); + } + break; + case ICM_DRAW_SUGGESTFORMAT: + { + ICDRAWSUGGEST *icds = HeapAlloc(GetProcessHeap(), 0, sizeof(ICDRAWSUGGEST)); + ICDRAWSUGGEST16 *icds16 = MapSL(*lParam1); + + ret = icds16; + + COPY(icds, dwFlags); + COPYPTR(icds, lpbiIn); + COPYPTR(icds, lpbiSuggest); + COPY(icds, dxSrc); + COPY(icds, dySrc); + COPY(icds, dxDst); + COPY(icds, dyDst); + icds->hicDecompressor = HIC_32(icds16->hicDecompressor); + + *lParam1 = (DWORD)(icds); + *lParam2 = sizeof(ICDRAWSUGGEST); + } + break; + case ICM_DRAW: + { + ICDRAW *icd = HeapAlloc(GetProcessHeap(), 0, sizeof(ICDRAW)); + ICDRAW *icd16 = MapSL(*lParam1); + ret = icd16; + + COPY(icd, dwFlags); + COPYPTR(icd, lpFormat); + COPYPTR(icd, lpData); + COPY(icd, cbData); + COPY(icd, lTime); + + *lParam1 = (DWORD)(icd); + *lParam2 = sizeof(ICDRAW); + } + break; + case ICM_DRAW_START: + case ICM_DRAW_STOP: + break; + default: + FIXME("%d is not yet handled. Expect a crash.\n", msg); + } + return ret; +} + +#undef COPY +#undef COPYPTR + +/****************************************************************** + * MSVIDEO_UnmapMsg16To32 + * + * + */ +static void MSVIDEO_UnmapMsg16To32(UINT msg, LPVOID data16, LPDWORD lParam1, LPDWORD lParam2) +{ + TRACE("Unmapping %d\n", msg); + +#define UNCOPY(x, y) (x##16->y = x->y); + + switch (msg) + { + case ICM_GETINFO: + { + ICINFO *ici = (ICINFO*)(*lParam1); + ICINFO16 *ici16 = (ICINFO16*)data16; + + UNCOPY(ici, fccType); + UNCOPY(ici, fccHandler); + UNCOPY(ici, dwFlags); + UNCOPY(ici, dwVersion); + UNCOPY(ici, dwVersionICM); + WideCharToMultiByte( CP_ACP, 0, ici->szName, -1, ici16->szName, + sizeof(ici16->szName), NULL, NULL ); + ici16->szName[sizeof(ici16->szName)-1] = 0; + WideCharToMultiByte( CP_ACP, 0, ici->szDescription, -1, ici16->szDescription, + sizeof(ici16->szDescription), NULL, NULL ); + ici16->szDescription[sizeof(ici16->szDescription)-1] = 0; + /* This just gives garbage for some reason - BB + lstrcpynWtoA(ici16->szDriver, ici->szDriver, 128);*/ + + HeapFree(GetProcessHeap(), 0, ici); + } + break; + case ICM_DECOMPRESS_QUERY: + /*{ + LPVOID* x = data16; + HeapFree(GetProcessHeap(), 0, x[0]); + if (x[1]) + HeapFree(GetProcessHeap(), 0, x[1]); + } + break;*/ + case ICM_COMPRESS: + case ICM_DECOMPRESS: + case ICM_DECOMPRESSEX_QUERY: + case ICM_DECOMPRESSEX_BEGIN: + case ICM_DECOMPRESSEX: + case ICM_DRAW_BEGIN: + case ICM_DRAW_SUGGESTFORMAT: + case ICM_DRAW: + HeapFree(GetProcessHeap(), 0, data16); + break; + default: + ERR("Unmapping unmapped msg %d\n", msg); + } +#undef UNCOPY +} + +/*********************************************************************** + * ICInfo [MSVIDEO.200] + */ +BOOL16 VFWAPI ICInfo16(DWORD fccType, DWORD fccHandler, ICINFO16 *lpicinfo) +{ + BOOL16 ret; + LPVOID lpv; + DWORD lParam = (DWORD)lpicinfo; + DWORD size = ((ICINFO*)(MapSL((SEGPTR)lpicinfo)))->dwSize; + + /* Use the mapping functions to map the ICINFO structure */ + lpv = MSVIDEO_MapMsg16To32(ICM_GETINFO, &lParam, &size); + + ret = ICInfo(fccType, fccHandler, (ICINFO*)lParam); + + MSVIDEO_UnmapMsg16To32(ICM_GETINFO, lpv, &lParam, &size); + + return ret; +} + +/****************************************************************** + * IC_Callback3216 + * + * + */ +static LRESULT CALLBACK IC_Callback3216(HIC hic, HDRVR hdrv, UINT msg, DWORD lp1, DWORD lp2) +{ + WINE_HIC* whic; + LRESULT ret = 0; + WORD args[8]; + + whic = MSVIDEO_GetHicPtr(hic); + if (whic) + { + switch (msg) + { + case DRV_OPEN: + lp2 = (DWORD)MapLS((void*)lp2); + break; + } + args[7] = HIWORD(hic); + args[6] = LOWORD(hic); + args[5] = HDRVR_16(whic->hdrv); + args[4] = msg; + args[3] = HIWORD(lp1); + args[2] = LOWORD(lp1); + args[1] = HIWORD(lp2); + args[0] = LOWORD(lp2); + WOWCallback16Ex( (DWORD)whic->driverproc16, WCB16_PASCAL, sizeof(args), args, &ret ); + + switch (msg) + { + case DRV_OPEN: + UnMapLS(lp2); + break; + } + } + else ret = ICERR_BADHANDLE; + return ret; +} + +/*********************************************************************** + * ICOpenFunction [MSVIDEO.206] + */ +HIC16 VFWAPI ICOpenFunction16(DWORD fccType, DWORD fccHandler, UINT16 wMode, FARPROC16 lpfnHandler) +{ + HIC hic32; + + hic32 = MSVIDEO_OpenFunction(fccType, fccHandler, wMode, + (DRIVERPROC)IC_Callback3216, (DWORD)lpfnHandler); + return HIC_16(hic32); +} + +/*********************************************************************** + * ICSendMessage [MSVIDEO.205] + */ +LRESULT VFWAPI ICSendMessage16(HIC16 hic, UINT16 msg, DWORD lParam1, DWORD lParam2) +{ + LRESULT ret = ICERR_BADHANDLE; + WINE_HIC* whic; + + whic = MSVIDEO_GetHicPtr(HIC_32(hic)); + if (whic) + { + /* we've got a 16 bit driver proc... call it directly */ + if (whic->driverproc16) + { + WORD args[8]; + + /* FIXME: original code was passing hdrv first and hic second */ + /* but this doesn't match what IC_Callback3216 does */ + args[7] = HIWORD(hic); + args[6] = LOWORD(hic); + args[5] = HDRVR_16(whic->hdrv); + args[4] = msg; + args[3] = HIWORD(lParam1); + args[2] = LOWORD(lParam1); + args[1] = HIWORD(lParam2); + args[0] = LOWORD(lParam2); + WOWCallback16Ex( (DWORD)whic->driverproc16, WCB16_PASCAL, sizeof(args), args, &ret ); + } + else + { + /* map the message for a 32 bit infrastructure, and pass it along */ + void* data16 = MSVIDEO_MapMsg16To32(msg, &lParam1, &lParam2); + + ret = MSVIDEO_SendMessage(whic, msg, lParam1, lParam2); + if (data16) + MSVIDEO_UnmapMsg16To32(msg, data16, &lParam1, &lParam2); + } + } + return ret; +} + +/*********************************************************************** + * VideoCapDriverDescAndVer [MSVIDEO.22] + */ +DWORD WINAPI VideoCapDriverDescAndVer16(WORD nr, LPSTR buf1, WORD buf1len, + LPSTR buf2, WORD buf2len) +{ + DWORD verhandle; + DWORD infosize; + UINT subblocklen; + char *s, buf[2048], fn[260]; + LPBYTE infobuf; + LPVOID subblock; + DWORD i, cnt = 0, lRet; + DWORD bufLen, fnLen; + FILETIME lastWrite; + HKEY hKey; + BOOL found = FALSE; + + TRACE("(%d,%p,%d,%p,%d)\n", nr, buf1, buf1len, buf2, buf2len); + lRet = RegOpenKeyExA(HKEY_LOCAL_MACHINE, HKLM_DRIVERS32, 0, KEY_QUERY_VALUE, &hKey); + if (lRet == ERROR_SUCCESS) + { + RegQueryInfoKeyA( hKey, 0, 0, 0, &cnt, 0, 0, 0, 0, 0, 0, 0); + for (i = 0; i < cnt; i++) + { + bufLen = sizeof(buf) / sizeof(buf[0]); + lRet = RegEnumKeyExA(hKey, i, buf, &bufLen, 0, 0, 0, &lastWrite); + if (lRet != ERROR_SUCCESS) continue; + if (strncasecmp(buf, "vid", 3)) continue; + if (nr--) continue; + fnLen = sizeof(fn); + lRet = RegQueryValueExA(hKey, buf, 0, 0, fn, &fnLen); + if (lRet == ERROR_SUCCESS) found = TRUE; + break; + } + RegCloseKey( hKey ); + } + + /* search system.ini if not found in the registry */ + if (!found && GetPrivateProfileStringA("drivers32", NULL, NULL, buf, sizeof(buf), "system.ini")) + { + for (s = buf; *s; s += strlen(s) + 1) + { + if (strncasecmp(s, "vid", 3)) continue; + if (nr--) continue; + if (GetPrivateProfileStringA("drivers32", s, NULL, fn, sizeof(fn), "system.ini")) + found = TRUE; + break; + } + } + + if (nr || !found) + { + TRACE("No more VID* entries found nr=%d\n", nr); + return 20; + } + infosize = GetFileVersionInfoSizeA(fn, &verhandle); + if (!infosize) + { + TRACE("%s has no fileversioninfo.\n", fn); + return 18; + } + infobuf = HeapAlloc(GetProcessHeap(), 0, infosize); + if (GetFileVersionInfoA(fn, verhandle, infosize, infobuf)) + { + char vbuf[200]; + /* Yes, two space behind : */ + /* FIXME: test for buflen */ + sprintf(vbuf, "Version: %d.%d.%d.%d\n", + ((WORD*)infobuf)[0x0f], + ((WORD*)infobuf)[0x0e], + ((WORD*)infobuf)[0x11], + ((WORD*)infobuf)[0x10] + ); + TRACE("version of %s is %s\n", fn, vbuf); + strncpy(buf2, vbuf, buf2len); + } + else + { + TRACE("GetFileVersionInfoA failed for %s.\n", fn); + strncpy(buf2, fn, buf2len); /* msvideo.dll appears to copy fn*/ + } + /* FIXME: language problem? */ + if (VerQueryValueA( infobuf, + "\\StringFileInfo\\040904E4\\FileDescription", + &subblock, + &subblocklen + )) + { + TRACE("VQA returned %s\n", (LPCSTR)subblock); + strncpy(buf1, subblock, buf1len); + } + else + { + TRACE("VQA did not return on query \\StringFileInfo\\040904E4\\FileDescription?\n"); + strncpy(buf1, fn, buf1len); /* msvideo.dll appears to copy fn*/ + } + HeapFree(GetProcessHeap(), 0, infobuf); + return 0; +} + +/****************************************************************** + * IC_CallTo16 + * + * + */ +static LRESULT CALLBACK IC_CallTo16(HDRVR hdrv, HIC hic, UINT msg, LPARAM lp1, LPARAM lp2) +{ +#if 0 + WINE_HIC* whic = IC_GetPtr(hic); + LRESULT ret = 0; + + + if (whic->driverproc) + { + ret = whic->driverproc(hic, whic->hdrv, msg, lParam1, lParam2); + } + else + { + ret = SendDriverMessage(whic->hdrv, msg, lParam1, lParam2); + } +#else + FIXME("No 32=>16 conversion yet\n"); +#endif + return 0; +} + +/************************************************************************** + * DllEntryPoint (MSVIDEO.300) + * + * MSVIDEO DLL entry point + * + */ +BOOL WINAPI VIDEO_LibMain(DWORD fdwReason, HINSTANCE hinstDLL, WORD ds, + WORD wHeapSize, DWORD dwReserved1, WORD wReserved2) +{ + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + /* hook in our 16 bit management functions */ + pFnCallTo16 = IC_CallTo16; + break; + case DLL_PROCESS_DETACH: + /* remove our 16 bit management functions */ + pFnCallTo16 = NULL; + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + break; + } + return TRUE; +} diff --git a/reactos/dll/msvideo/msvideo_main.c b/reactos/dll/msvideo/msvideo_main.c new file mode 100644 index 00000000000..0d918edb32c --- /dev/null +++ b/reactos/dll/msvideo/msvideo_main.c @@ -0,0 +1,1184 @@ +/* + * Copyright 1998 Marcus Meissner + * Copyright 2000 Bradley Baetz + * Copyright 2003 Michael Günnewig + * + * 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 + * + * FIXME: This all assumes 32 bit codecs + * Win95 appears to prefer 32 bit codecs, even from 16 bit code. + * There is the ICOpenFunction16 to worry about still, though. + * + * TODO + * - no thread safety + */ + +#include +#include + +#include "msvideo_private.h" +#include "winnls.h" +#include "wingdi.h" +#include "winuser.h" +#include "winreg.h" + +#include "windowsx.h" + +#include "wine/debug.h" + +/* Drivers32 settings */ +#define HKLM_DRIVERS32 "Software\\Microsoft\\Windows NT\\CurrentVersion\\Drivers32" + +WINE_DEFAULT_DEBUG_CHANNEL(msvideo); + +static inline const char *wine_dbgstr_fcc( DWORD fcc ) +{ + return wine_dbg_sprintf("%c%c%c%c", + LOBYTE(LOWORD(fcc)), HIBYTE(LOWORD(fcc)), + LOBYTE(HIWORD(fcc)), HIBYTE(HIWORD(fcc))); +} + +LRESULT (CALLBACK *pFnCallTo16)(HDRVR, HIC, UINT, LPARAM, LPARAM) = NULL; + +static WINE_HIC* MSVIDEO_FirstHic /* = NULL */; + +typedef struct _reg_driver reg_driver; +struct _reg_driver +{ + DWORD fccType; + DWORD fccHandler; + DRIVERPROC proc; + LPWSTR name; + reg_driver* next; +}; + +static reg_driver* reg_driver_list = NULL; + +/* This one is a macro such that it works for both ASCII and Unicode */ +#define fourcc_to_string(str, fcc) do { \ + (str)[0] = LOBYTE(LOWORD(fcc)); \ + (str)[1] = HIBYTE(LOWORD(fcc)); \ + (str)[2] = LOBYTE(HIWORD(fcc)); \ + (str)[3] = HIBYTE(HIWORD(fcc)); \ + } while(0) + +HMODULE MSVFW32_hModule; + +BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved ) +{ + TRACE("%p,%lx,%p\n", hinst, reason, reserved); + + switch(reason) + { + case DLL_PROCESS_ATTACH: + DisableThreadLibraryCalls(hinst); + MSVFW32_hModule = (HMODULE)hinst; + break; + } + return TRUE; +} + +static int compare_fourcc(DWORD fcc1, DWORD fcc2) +{ + char fcc_str1[4]; + char fcc_str2[4]; + fourcc_to_string(fcc_str1, fcc1); + fourcc_to_string(fcc_str2, fcc2); + return strncasecmp(fcc_str1, fcc_str2, 4); +} + +typedef BOOL (*enum_handler_t)(const char*, int, void*); + +static BOOL enum_drivers(DWORD fccType, enum_handler_t handler, void* param) +{ + CHAR buf[2048], fccTypeStr[5], *s; + DWORD i, cnt = 0, bufLen, lRet; + BOOL result = FALSE; + FILETIME lastWrite; + HKEY hKey; + + fourcc_to_string(fccTypeStr, fccType); + fccTypeStr[4] = '.'; + + /* first, go through the registry entries */ + lRet = RegOpenKeyExA(HKEY_LOCAL_MACHINE, HKLM_DRIVERS32, 0, KEY_QUERY_VALUE, &hKey); + if (lRet == ERROR_SUCCESS) + { + RegQueryInfoKeyA( hKey, 0, 0, 0, &cnt, 0, 0, 0, 0, 0, 0, 0); + for (i = 0; i < cnt; i++) + { + bufLen = sizeof(buf) / sizeof(buf[0]); + lRet = RegEnumKeyExA(hKey, i, buf, &bufLen, 0, 0, 0, &lastWrite); + if (lRet != ERROR_SUCCESS) continue; + if (strncasecmp(buf, fccTypeStr, 5) || buf[9] != '=') continue; + if ((result = handler(buf, i, param))) break; + } + RegCloseKey( hKey ); + } + if (result) return result; + + /* if that didn't work, go through the values in system.ini */ + if (GetPrivateProfileSectionA("drivers32", buf, sizeof(buf), "system.ini")) + { + for (s = buf; *s; cnt++, s += strlen(s) + 1) + { + if (strncasecmp(s, fccTypeStr, 5) || s[9] != '=') continue; + if ((result = handler(s, cnt, param))) break; + } + } + + return result; +} + +/****************************************************************** + * MSVIDEO_GetHicPtr + * + * + */ +WINE_HIC* MSVIDEO_GetHicPtr(HIC hic) +{ + WINE_HIC* whic; + + for (whic = MSVIDEO_FirstHic; whic && whic->hic != hic; whic = whic->next); + return whic; +} + +/*********************************************************************** + * VideoForWindowsVersion [MSVFW32.2] + * VideoForWindowsVersion [MSVIDEO.2] + * Returns the version in major.minor form. + * In Windows95 this returns 0x040003b6 (4.950) + */ +DWORD WINAPI VideoForWindowsVersion(void) +{ + return 0x040003B6; /* 4.950 */ +} + +static BOOL ICInfo_enum_handler(const char *drv, int nr, void *param) +{ + ICINFO *lpicinfo = (ICINFO *)param; + DWORD fccHandler = mmioStringToFOURCCA(drv + 5, 0); + + /* exact match of fccHandler or nth driver found */ + if ((lpicinfo->fccHandler != nr) && (lpicinfo->fccHandler != fccHandler)) + return FALSE; + + lpicinfo->fccType = mmioStringToFOURCCA(drv, 0); + lpicinfo->fccHandler = fccHandler; + lpicinfo->dwFlags = 0; + lpicinfo->dwVersion = 0; + lpicinfo->dwVersionICM = 0x104; + lpicinfo->szName[0] = 0; + lpicinfo->szDescription[0] = 0; + MultiByteToWideChar(CP_ACP, 0, drv + 10, -1, lpicinfo->szDriver, + sizeof(lpicinfo->szDriver)/sizeof(WCHAR)); + + return TRUE; +} + +/*********************************************************************** + * ICInfo [MSVFW32.@] + * Get information about an installable compressor. Return TRUE if there + * is one. + * + * PARAMS + * fccType [I] type of compressor (e.g. 'vidc') + * fccHandler [I] real fcc for handler or th compressor + * lpicinfo [O] information about compressor + */ +BOOL VFWAPI ICInfo( DWORD fccType, DWORD fccHandler, ICINFO *lpicinfo) +{ + TRACE("(%s,%s/%08lx,%p)\n", + wine_dbgstr_fcc(fccType), wine_dbgstr_fcc(fccHandler), fccHandler, lpicinfo); + + lpicinfo->fccHandler = fccHandler; + return enum_drivers(fccType, ICInfo_enum_handler, lpicinfo); +} + +static DWORD IC_HandleRef = 1; + +/*********************************************************************** + * ICInstall [MSVFW32.@] + */ +BOOL VFWAPI ICInstall(DWORD fccType, DWORD fccHandler, LPARAM lParam, LPSTR szDesc, UINT wFlags) +{ + reg_driver* driver; + unsigned len; + + TRACE("(%s,%s,%p,%p,0x%08x)\n", wine_dbgstr_fcc(fccType), wine_dbgstr_fcc(fccHandler), (void*)lParam, szDesc, wFlags); + + /* Check if a driver is already registered */ + for (driver = reg_driver_list; driver; driver = driver->next) + { + if (!compare_fourcc(fccType, driver->fccType) && + !compare_fourcc(fccHandler, driver->fccHandler)) + break; + } + if (driver) return FALSE; + + /* Register the driver */ + driver = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(reg_driver)); + if (!driver) goto oom; + driver->fccType = fccType; + driver->fccHandler = fccHandler; + + switch(wFlags) + { + case ICINSTALL_FUNCTION: + driver->proc = (DRIVERPROC)lParam; + driver->name = NULL; + break; + case ICINSTALL_DRIVER: + driver->proc = NULL; + len = MultiByteToWideChar(CP_ACP, 0, (char*)lParam, -1, NULL, 0); + driver->name = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + if (!driver->name) goto oom; + MultiByteToWideChar(CP_ACP, 0, (char*)lParam, -1, driver->name, len); + break; + default: + ERR("Invalid flags!\n"); + HeapFree(GetProcessHeap(), 0, driver); + return FALSE; + } + + /* Insert our driver in the list*/ + driver->next = reg_driver_list; + reg_driver_list = driver; + + return TRUE; + oom: + if (driver) HeapFree(GetProcessHeap(), 0, driver); + return FALSE; +} + +/*********************************************************************** + * ICRemove [MSVFW32.@] + */ +BOOL VFWAPI ICRemove(DWORD fccType, DWORD fccHandler, UINT wFlags) +{ + reg_driver** pdriver; + + TRACE("(%s,%s,0x%08x)\n", wine_dbgstr_fcc(fccType), wine_dbgstr_fcc(fccHandler), wFlags); + + /* Check if a driver is already registered */ + for (pdriver = ®_driver_list; *pdriver; pdriver = &(*pdriver)->next) + { + if (!compare_fourcc(fccType, (*pdriver)->fccType) && + !compare_fourcc(fccHandler, (*pdriver)->fccHandler)) + break; + } + if (!*pdriver) + return FALSE; + + /* Remove the driver from the list */ + *pdriver = (*pdriver)->next; + if ((*pdriver)->name) + HeapFree(GetProcessHeap(), 0, (*pdriver)->name); + HeapFree(GetProcessHeap(), 0, *pdriver); + + return TRUE; +} + + +/*********************************************************************** + * ICOpen [MSVFW32.@] + * Opens an installable compressor. Return special handle. + */ +HIC VFWAPI ICOpen(DWORD fccType, DWORD fccHandler, UINT wMode) +{ + WCHAR codecname[10]; + ICOPEN icopen; + HDRVR hdrv; + WINE_HIC* whic; + BOOL bIs16; + static const WCHAR drv32W[] = {'d','r','i','v','e','r','s','3','2','\0'}; + reg_driver* driver; + + TRACE("(%s,%s,0x%08x)\n", wine_dbgstr_fcc(fccType), wine_dbgstr_fcc(fccHandler), wMode); + + /* Check if there is a registered driver that matches */ + driver = reg_driver_list; + while(driver) + if (!compare_fourcc(fccType, driver->fccType) && + !compare_fourcc(fccHandler, driver->fccHandler)) + break; + else + driver = driver->next; + + if (driver && driver->proc) + /* The driver has been registered at runtime with its driverproc */ + return MSVIDEO_OpenFunction(fccType, fccHandler, wMode, (DRIVERPROC)driver->proc, (DWORD)NULL); + + /* Well, lParam2 is in fact a LPVIDEO_OPEN_PARMS, but it has the + * same layout as ICOPEN + */ + icopen.dwSize = sizeof(ICOPEN); + icopen.fccType = fccType; + icopen.fccHandler = fccHandler; + icopen.dwVersion = 0x00001000; /* FIXME */ + icopen.dwFlags = wMode; + icopen.dwError = 0; + icopen.pV1Reserved = NULL; + icopen.pV2Reserved = NULL; + icopen.dnDevNode = 0; /* FIXME */ + + if (!driver) { + /* The driver is registered in the registry */ + fourcc_to_string(codecname, fccType); + codecname[4] = '.'; + fourcc_to_string(codecname + 5, fccHandler); + codecname[9] = '\0'; + + hdrv = OpenDriver(codecname, drv32W, (LPARAM)&icopen); + if (!hdrv) + { + if (fccType == streamtypeVIDEO) + { + codecname[0] = 'v'; + codecname[1] = 'i'; + codecname[2] = 'd'; + codecname[3] = 'c'; + + fccType = ICTYPE_VIDEO; + hdrv = OpenDriver(codecname, drv32W, (LPARAM)&icopen); + } + if (!hdrv) + return 0; + } + } else { + /* The driver has been registered at runtime with its name */ + hdrv = OpenDriver(driver->name, NULL, (LPARAM)&icopen); + if (!hdrv) + return 0; + } + bIs16 = GetDriverFlags(hdrv) & 0x10000000; /* undocumented flag: WINE_GDF_16BIT */ + + if (bIs16 && !pFnCallTo16) + { + FIXME("Got a 16 bit driver, but no 16 bit support in msvfw\n"); + return 0; + } + whic = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_HIC)); + if (!whic) + { + CloseDriver(hdrv, 0, 0); + return FALSE; + } + whic->hdrv = hdrv; + /* FIXME: is the signature the real one ? */ + whic->driverproc = bIs16 ? (DRIVERPROC)pFnCallTo16 : NULL; + whic->driverproc16 = 0; + whic->type = fccType; + whic->handler = fccHandler; + while (MSVIDEO_GetHicPtr(HIC_32(IC_HandleRef)) != NULL) IC_HandleRef++; + whic->hic = HIC_32(IC_HandleRef++); + whic->next = MSVIDEO_FirstHic; + MSVIDEO_FirstHic = whic; + + TRACE("=> %p\n", whic->hic); + return whic->hic; +} + +/*********************************************************************** + * MSVIDEO_OpenFunction + */ +HIC MSVIDEO_OpenFunction(DWORD fccType, DWORD fccHandler, UINT wMode, + DRIVERPROC lpfnHandler, DWORD lpfnHandler16) +{ + ICOPEN icopen; + WINE_HIC* whic; + + TRACE("(%s,%s,%d,%p,%08lx)\n", + wine_dbgstr_fcc(fccType), wine_dbgstr_fcc(fccHandler), wMode, lpfnHandler, lpfnHandler16); + + icopen.dwSize = sizeof(ICOPEN); + icopen.fccType = fccType; + icopen.fccHandler = fccHandler; + icopen.dwVersion = 0x00001000; /* FIXME */ + icopen.dwFlags = wMode; + icopen.dwError = 0; + icopen.pV1Reserved = NULL; + icopen.pV2Reserved = NULL; + icopen.dnDevNode = 0; /* FIXME */ + + whic = HeapAlloc(GetProcessHeap(), 0, sizeof(WINE_HIC)); + if (!whic) return 0; + + whic->driverproc = lpfnHandler; + whic->driverproc16 = lpfnHandler16; + while (MSVIDEO_GetHicPtr(HIC_32(IC_HandleRef)) != NULL) IC_HandleRef++; + whic->hic = HIC_32(IC_HandleRef++); + whic->next = MSVIDEO_FirstHic; + MSVIDEO_FirstHic = whic; + + /* Now try opening/loading the driver. Taken from DRIVER_AddToList */ + /* What if the function is used more than once? */ + + if (MSVIDEO_SendMessage(whic, DRV_LOAD, 0L, 0L) != DRV_SUCCESS) + { + WARN("DRV_LOAD failed for hic %p\n", whic->hic); + MSVIDEO_FirstHic = whic->next; + HeapFree(GetProcessHeap(), 0, whic); + return 0; + } + /* return value is not checked */ + MSVIDEO_SendMessage(whic, DRV_ENABLE, 0L, 0L); + + whic->driverId = (DWORD)MSVIDEO_SendMessage(whic, DRV_OPEN, 0, (DWORD)&icopen); + /* FIXME: What should we put here? */ + whic->hdrv = (HDRVR)0; + + if (whic->driverId == 0) + { + WARN("DRV_OPEN failed for hic %p\n", whic->hic); + MSVIDEO_FirstHic = whic->next; + HeapFree(GetProcessHeap(), 0, whic); + return 0; + } + + TRACE("=> %p\n", whic->hic); + return whic->hic; +} + +/*********************************************************************** + * ICOpenFunction [MSVFW32.@] + */ +HIC VFWAPI ICOpenFunction(DWORD fccType, DWORD fccHandler, UINT wMode, FARPROC lpfnHandler) +{ + return MSVIDEO_OpenFunction(fccType, fccHandler, wMode, (DRIVERPROC)lpfnHandler, 0); +} + +/*********************************************************************** + * ICGetInfo [MSVFW32.@] + */ +LRESULT VFWAPI ICGetInfo(HIC hic, ICINFO *picinfo, DWORD cb) +{ + LRESULT ret; + WINE_HIC* whic = MSVIDEO_GetHicPtr(hic); + + TRACE("(%p,%p,%ld)\n", hic, picinfo, cb); + + whic = MSVIDEO_GetHicPtr(hic); + if (!whic) return ICERR_BADHANDLE; + if (!picinfo) return MMSYSERR_INVALPARAM; + + /* (WS) The field szDriver should be initialized because the driver + * is not obliged and often will not do it. Some applications, like + * VirtualDub, rely on this field and will occasionally crash if it + * goes unitialized. + */ + if (cb >= sizeof(ICINFO)) picinfo->szDriver[0] = '\0'; + + ret = ICSendMessage(hic, ICM_GETINFO, (DWORD)picinfo, cb); + + /* (WS) When szDriver was not supplied by the driver itself, apparently + * Windows will set its value equal to the driver file name. This can + * be obtained from the registry as we do here. + */ + if (cb >= sizeof(ICINFO) && picinfo->szDriver[0] == 0) + { + ICINFO ii; + + memset(&ii, 0, sizeof(ii)); + ii.dwSize = sizeof(ii); + ICInfo(picinfo->fccType, picinfo->fccHandler, &ii); + lstrcpyW(picinfo->szDriver, ii.szDriver); + } + + TRACE(" -> 0x%08lx\n", ret); + return ret; +} + +typedef struct { + DWORD fccType; + DWORD fccHandler; + LPBITMAPINFOHEADER lpbiIn; + LPBITMAPINFOHEADER lpbiOut; + WORD wMode; + DWORD querymsg; + HIC hic; +} driver_info_t; + +static HIC try_driver(driver_info_t *info) +{ + HIC hic; + + if ((hic = ICOpen(info->fccType, info->fccHandler, info->wMode))) + { + if (!ICSendMessage(hic, info->querymsg, (DWORD)info->lpbiIn, (DWORD)info->lpbiOut)) + return hic; + ICClose(hic); + } + return 0; +} + +static BOOL ICLocate_enum_handler(const char *drv, int nr, void *param) +{ + driver_info_t *info = (driver_info_t *)param; + info->fccHandler = mmioStringToFOURCCA(drv + 5, 0); + info->hic = try_driver(info); + return info->hic != 0; +} + +/*********************************************************************** + * ICLocate [MSVFW32.@] + */ +HIC VFWAPI ICLocate(DWORD fccType, DWORD fccHandler, LPBITMAPINFOHEADER lpbiIn, + LPBITMAPINFOHEADER lpbiOut, WORD wMode) +{ + driver_info_t info; + + TRACE("(%s,%s,%p,%p,0x%04x)\n", + wine_dbgstr_fcc(fccType), wine_dbgstr_fcc(fccHandler), lpbiIn, lpbiOut, wMode); + + info.fccType = fccType; + info.fccHandler = fccHandler; + info.lpbiIn = lpbiIn; + info.lpbiOut = lpbiOut; + info.wMode = wMode; + + switch (wMode) + { + case ICMODE_FASTCOMPRESS: + case ICMODE_COMPRESS: + info.querymsg = ICM_COMPRESS_QUERY; + break; + case ICMODE_FASTDECOMPRESS: + case ICMODE_DECOMPRESS: + info.querymsg = ICM_DECOMPRESS_QUERY; + break; + case ICMODE_DRAW: + info.querymsg = ICM_DRAW_QUERY; + break; + default: + WARN("Unknown mode (%d)\n", wMode); + return 0; + } + + /* Easy case: handler/type match, we just fire a query and return */ + info.hic = try_driver(&info); + /* If it didn't work, try each driver in turn. 32 bit codecs only. */ + /* FIXME: Move this to an init routine? */ + if (!info.hic) enum_drivers(fccType, ICLocate_enum_handler, &info); + + if (info.hic) + { + TRACE("=> %p\n", info.hic); + return info.hic; + } + + if (fccType == streamtypeVIDEO) + return ICLocate(ICTYPE_VIDEO, fccHandler, lpbiIn, lpbiOut, wMode); + + WARN("(%s,%s,%p,%p,0x%04x) not found!\n", + wine_dbgstr_fcc(fccType), wine_dbgstr_fcc(fccHandler), lpbiIn, lpbiOut, wMode); + return 0; +} + +/*********************************************************************** + * ICGetDisplayFormat [MSVFW32.@] + */ +HIC VFWAPI ICGetDisplayFormat( + HIC hic,LPBITMAPINFOHEADER lpbiIn,LPBITMAPINFOHEADER lpbiOut, + INT depth,INT dx,INT dy) +{ + HIC tmphic = hic; + + TRACE("(%p,%p,%p,%d,%d,%d)!\n",hic,lpbiIn,lpbiOut,depth,dx,dy); + + if (!tmphic) { + tmphic=ICLocate(ICTYPE_VIDEO,0,lpbiIn,NULL,ICMODE_DECOMPRESS); + if (!tmphic) + return tmphic; + } + if ((dy == lpbiIn->biHeight) && (dx == lpbiIn->biWidth)) + dy = dx = 0; /* no resize needed */ + + /* Can we decompress it ? */ + if (ICDecompressQuery(tmphic,lpbiIn,NULL) != 0) + goto errout; /* no, sorry */ + + ICDecompressGetFormat(tmphic,lpbiIn,lpbiOut); + + if (lpbiOut->biCompression != 0) { + FIXME("Ooch, how come decompressor outputs compressed data (%ld)??\n", + lpbiOut->biCompression); + } + if (lpbiOut->biSize < sizeof(*lpbiOut)) { + FIXME("Ooch, size of output BIH is too small (%ld)\n", + lpbiOut->biSize); + lpbiOut->biSize = sizeof(*lpbiOut); + } + if (!depth) { + HDC hdc; + + hdc = GetDC(0); + depth = GetDeviceCaps(hdc,BITSPIXEL)*GetDeviceCaps(hdc,PLANES); + ReleaseDC(0,hdc); + if (depth==15) depth = 16; + if (depth<8) depth = 8; + } + if (lpbiIn->biBitCount == 8) + depth = 8; + + TRACE("=> %p\n", tmphic); + return tmphic; +errout: + if (hic!=tmphic) + ICClose(tmphic); + + TRACE("=> 0\n"); + return 0; +} + +/*********************************************************************** + * ICCompress [MSVFW32.@] + */ +DWORD VFWAPIV +ICCompress( + HIC hic,DWORD dwFlags,LPBITMAPINFOHEADER lpbiOutput,LPVOID lpData, + LPBITMAPINFOHEADER lpbiInput,LPVOID lpBits,LPDWORD lpckid, + LPDWORD lpdwFlags,LONG lFrameNum,DWORD dwFrameSize,DWORD dwQuality, + LPBITMAPINFOHEADER lpbiPrev,LPVOID lpPrev) +{ + ICCOMPRESS iccmp; + + TRACE("(%p,%ld,%p,%p,%p,%p,...)\n",hic,dwFlags,lpbiOutput,lpData,lpbiInput,lpBits); + + iccmp.dwFlags = dwFlags; + + iccmp.lpbiOutput = lpbiOutput; + iccmp.lpOutput = lpData; + iccmp.lpbiInput = lpbiInput; + iccmp.lpInput = lpBits; + + iccmp.lpckid = lpckid; + iccmp.lpdwFlags = lpdwFlags; + iccmp.lFrameNum = lFrameNum; + iccmp.dwFrameSize = dwFrameSize; + iccmp.dwQuality = dwQuality; + iccmp.lpbiPrev = lpbiPrev; + iccmp.lpPrev = lpPrev; + return ICSendMessage(hic,ICM_COMPRESS,(DWORD)&iccmp,sizeof(iccmp)); +} + +/*********************************************************************** + * ICDecompress [MSVFW32.@] + */ +DWORD VFWAPIV ICDecompress(HIC hic,DWORD dwFlags,LPBITMAPINFOHEADER lpbiFormat, + LPVOID lpData,LPBITMAPINFOHEADER lpbi,LPVOID lpBits) +{ + ICDECOMPRESS icd; + DWORD ret; + + TRACE("(%p,%ld,%p,%p,%p,%p)\n",hic,dwFlags,lpbiFormat,lpData,lpbi,lpBits); + + TRACE("lpBits[0] == %lx\n",((LPDWORD)lpBits)[0]); + + icd.dwFlags = dwFlags; + icd.lpbiInput = lpbiFormat; + icd.lpInput = lpData; + + icd.lpbiOutput = lpbi; + icd.lpOutput = lpBits; + icd.ckid = 0; + ret = ICSendMessage(hic,ICM_DECOMPRESS,(DWORD)&icd,sizeof(ICDECOMPRESS)); + + TRACE("lpBits[0] == %lx\n",((LPDWORD)lpBits)[0]); + + TRACE("-> %ld\n",ret); + + return ret; +} + + +/*********************************************************************** + * ICCompressorChoose [MSVFW32.@] + */ +BOOL VFWAPI ICCompressorChoose(HWND hwnd, UINT uiFlags, LPVOID pvIn, + LPVOID lpData, PCOMPVARS pc, LPSTR lpszTitle) +{ + FIXME("(%p,0x%X,%p,%p,%p,%s),stub!\n",hwnd,uiFlags,pvIn,lpData,pc,lpszTitle); + + if (pc == NULL || pc->cbSize != sizeof(COMPVARS)) + return FALSE; + + if ((pc->dwFlags & ICMF_COMPVARS_VALID) == 0) { + pc->dwFlags = 0; + pc->fccType = pc->fccHandler = 0; + pc->hic = NULL; + pc->lpbiOut = NULL; + pc->lpBitsOut = pc->lpBitsPrev = pc->lpState = NULL; + pc->lQ = ICQUALITY_DEFAULT; + pc->lKey = -1; + pc->lDataRate = 300; /* kB */ + pc->lpState = NULL; + pc->cbState = 0; + } + if (pc->fccType == 0) + pc->fccType = ICTYPE_VIDEO; + + /* FIXME */ + + return FALSE; +} + + +/*********************************************************************** + * ICCompressorFree [MSVFW32.@] + */ +void VFWAPI ICCompressorFree(PCOMPVARS pc) +{ + TRACE("(%p)\n",pc); + + if (pc != NULL && pc->cbSize == sizeof(COMPVARS)) { + if (pc->hic != NULL) { + ICClose(pc->hic); + pc->hic = NULL; + } + if (pc->lpbiOut != NULL) { + GlobalFreePtr(pc->lpbiOut); + pc->lpbiOut = NULL; + } + if (pc->lpBitsOut != NULL) { + GlobalFreePtr(pc->lpBitsOut); + pc->lpBitsOut = NULL; + } + if (pc->lpBitsPrev != NULL) { + GlobalFreePtr(pc->lpBitsPrev); + pc->lpBitsPrev = NULL; + } + if (pc->lpState != NULL) { + GlobalFreePtr(pc->lpBitsPrev); + pc->lpState = NULL; + } + pc->dwFlags = 0; + } +} + + +/****************************************************************** + * MSVIDEO_SendMessage + * + * + */ +LRESULT MSVIDEO_SendMessage(WINE_HIC* whic, UINT msg, DWORD lParam1, DWORD lParam2) +{ + LRESULT ret; + +#define XX(x) case x: TRACE("(%p,"#x",0x%08lx,0x%08lx)\n",whic,lParam1,lParam2); break; + + switch (msg) { + /* DRV_* */ + XX(DRV_LOAD); + XX(DRV_ENABLE); + XX(DRV_OPEN); + XX(DRV_CLOSE); + XX(DRV_DISABLE); + XX(DRV_FREE); + /* ICM_RESERVED+X */ + XX(ICM_ABOUT); + XX(ICM_CONFIGURE); + XX(ICM_GET); + XX(ICM_GETINFO); + XX(ICM_GETDEFAULTQUALITY); + XX(ICM_GETQUALITY); + XX(ICM_GETSTATE); + XX(ICM_SETQUALITY); + XX(ICM_SET); + XX(ICM_SETSTATE); + /* ICM_USER+X */ + XX(ICM_COMPRESS_FRAMES_INFO); + XX(ICM_COMPRESS_GET_FORMAT); + XX(ICM_COMPRESS_GET_SIZE); + XX(ICM_COMPRESS_QUERY); + XX(ICM_COMPRESS_BEGIN); + XX(ICM_COMPRESS); + XX(ICM_COMPRESS_END); + XX(ICM_DECOMPRESS_GET_FORMAT); + XX(ICM_DECOMPRESS_QUERY); + XX(ICM_DECOMPRESS_BEGIN); + XX(ICM_DECOMPRESS); + XX(ICM_DECOMPRESS_END); + XX(ICM_DECOMPRESS_SET_PALETTE); + XX(ICM_DECOMPRESS_GET_PALETTE); + XX(ICM_DRAW_QUERY); + XX(ICM_DRAW_BEGIN); + XX(ICM_DRAW_GET_PALETTE); + XX(ICM_DRAW_START); + XX(ICM_DRAW_STOP); + XX(ICM_DRAW_END); + XX(ICM_DRAW_GETTIME); + XX(ICM_DRAW); + XX(ICM_DRAW_WINDOW); + XX(ICM_DRAW_SETTIME); + XX(ICM_DRAW_REALIZE); + XX(ICM_DRAW_FLUSH); + XX(ICM_DRAW_RENDERBUFFER); + XX(ICM_DRAW_START_PLAY); + XX(ICM_DRAW_STOP_PLAY); + XX(ICM_DRAW_SUGGESTFORMAT); + XX(ICM_DRAW_CHANGEPALETTE); + XX(ICM_GETBUFFERSWANTED); + XX(ICM_GETDEFAULTKEYFRAMERATE); + XX(ICM_DECOMPRESSEX_BEGIN); + XX(ICM_DECOMPRESSEX_QUERY); + XX(ICM_DECOMPRESSEX); + XX(ICM_DECOMPRESSEX_END); + XX(ICM_SET_STATUS_PROC); + default: + FIXME("(%p,0x%08lx,0x%08lx,0x%08lx) unknown message\n",whic,(DWORD)msg,lParam1,lParam2); + } + +#undef XX + + if (whic->driverproc) { + /* dwDriverId parameter is the value returned by the DRV_OPEN */ + ret = whic->driverproc(whic->driverId, whic->hdrv, msg, lParam1, lParam2); + } else { + ret = SendDriverMessage(whic->hdrv, msg, lParam1, lParam2); + } + + TRACE(" -> 0x%08lx\n", ret); + return ret; +} + +/*********************************************************************** + * ICSendMessage [MSVFW32.@] + */ +LRESULT VFWAPI ICSendMessage(HIC hic, UINT msg, DWORD lParam1, DWORD lParam2) +{ + WINE_HIC* whic = MSVIDEO_GetHicPtr(hic); + + if (!whic) return ICERR_BADHANDLE; + return MSVIDEO_SendMessage(whic, msg, lParam1, lParam2); +} + +/*********************************************************************** + * ICDrawBegin [MSVFW32.@] + */ +DWORD VFWAPIV ICDrawBegin( + HIC hic, /* [in] */ + DWORD dwFlags, /* [in] flags */ + HPALETTE hpal, /* [in] palette to draw with */ + HWND hwnd, /* [in] window to draw to */ + HDC hdc, /* [in] HDC to draw to */ + INT xDst, /* [in] destination rectangle */ + INT yDst, /* [in] */ + INT dxDst, /* [in] */ + INT dyDst, /* [in] */ + LPBITMAPINFOHEADER lpbi, /* [in] format of frame to draw */ + INT xSrc, /* [in] source rectangle */ + INT ySrc, /* [in] */ + INT dxSrc, /* [in] */ + INT dySrc, /* [in] */ + DWORD dwRate, /* [in] frames/second = (dwRate/dwScale) */ + DWORD dwScale) /* [in] */ +{ + + ICDRAWBEGIN icdb; + + TRACE("(%p,%ld,%p,%p,%p,%u,%u,%u,%u,%p,%u,%u,%u,%u,%ld,%ld)\n", + hic, dwFlags, hpal, hwnd, hdc, xDst, yDst, dxDst, dyDst, + lpbi, xSrc, ySrc, dxSrc, dySrc, dwRate, dwScale); + + icdb.dwFlags = dwFlags; + icdb.hpal = hpal; + icdb.hwnd = hwnd; + icdb.hdc = hdc; + icdb.xDst = xDst; + icdb.yDst = yDst; + icdb.dxDst = dxDst; + icdb.dyDst = dyDst; + icdb.lpbi = lpbi; + icdb.xSrc = xSrc; + icdb.ySrc = ySrc; + icdb.dxSrc = dxSrc; + icdb.dySrc = dySrc; + icdb.dwRate = dwRate; + icdb.dwScale = dwScale; + return ICSendMessage(hic,ICM_DRAW_BEGIN,(DWORD)&icdb,sizeof(icdb)); +} + +/*********************************************************************** + * ICDraw [MSVFW32.@] + */ +DWORD VFWAPIV ICDraw(HIC hic, DWORD dwFlags, LPVOID lpFormat, LPVOID lpData, DWORD cbData, LONG lTime) { + ICDRAW icd; + + TRACE("(%p,%ld,%p,%p,%ld,%ld)\n",hic,dwFlags,lpFormat,lpData,cbData,lTime); + + icd.dwFlags = dwFlags; + icd.lpFormat = lpFormat; + icd.lpData = lpData; + icd.cbData = cbData; + icd.lTime = lTime; + + return ICSendMessage(hic,ICM_DRAW,(DWORD)&icd,sizeof(icd)); +} + +/*********************************************************************** + * ICClose [MSVFW32.@] + */ +LRESULT WINAPI ICClose(HIC hic) +{ + WINE_HIC* whic = MSVIDEO_GetHicPtr(hic); + WINE_HIC** p; + + TRACE("(%p)\n",hic); + + if (!whic) return ICERR_BADHANDLE; + + if (whic->driverproc) + { + MSVIDEO_SendMessage(whic, DRV_CLOSE, 0, 0); + MSVIDEO_SendMessage(whic, DRV_DISABLE, 0, 0); + MSVIDEO_SendMessage(whic, DRV_FREE, 0, 0); + } + else + { + CloseDriver(whic->hdrv, 0, 0); + } + + /* remove whic from list */ + for (p = &MSVIDEO_FirstHic; *p != NULL; p = &((*p)->next)) + { + if ((*p) == whic) + { + *p = whic->next; + break; + } + } + + HeapFree(GetProcessHeap(), 0, whic); + return 0; +} + + + +/*********************************************************************** + * ICImageCompress [MSVFW32.@] + */ +HANDLE VFWAPI ICImageCompress( + HIC hic, UINT uiFlags, + LPBITMAPINFO lpbiIn, LPVOID lpBits, + LPBITMAPINFO lpbiOut, LONG lQuality, + LONG* plSize) +{ + FIXME("(%p,%08x,%p,%p,%p,%ld,%p)\n", + hic, uiFlags, lpbiIn, lpBits, lpbiOut, lQuality, plSize); + + return NULL; +} + +/*********************************************************************** + * ICImageDecompress [MSVFW32.@] + */ + +HANDLE VFWAPI ICImageDecompress( + HIC hic, UINT uiFlags, LPBITMAPINFO lpbiIn, + LPVOID lpBits, LPBITMAPINFO lpbiOut) +{ + HGLOBAL hMem = NULL; + BYTE* pMem = NULL; + BOOL bReleaseIC = FALSE; + BYTE* pHdr = NULL; + LONG cbHdr = 0; + BOOL bSucceeded = FALSE; + BOOL bInDecompress = FALSE; + DWORD biSizeImage; + + TRACE("(%p,%08x,%p,%p,%p)\n", + hic, uiFlags, lpbiIn, lpBits, lpbiOut); + + if ( hic == NULL ) + { + hic = ICDecompressOpen( ICTYPE_VIDEO, 0, &lpbiIn->bmiHeader, (lpbiOut != NULL) ? &lpbiOut->bmiHeader : NULL ); + if ( hic == NULL ) + { + WARN("no handler\n" ); + goto err; + } + bReleaseIC = TRUE; + } + if ( uiFlags != 0 ) + { + FIXME( "unknown flag %08x\n", uiFlags ); + goto err; + } + if ( lpbiIn == NULL || lpBits == NULL ) + { + WARN("invalid argument\n"); + goto err; + } + + if ( lpbiOut != NULL ) + { + if ( lpbiOut->bmiHeader.biSize != sizeof(BITMAPINFOHEADER) ) + goto err; + cbHdr = sizeof(BITMAPINFOHEADER); + if ( lpbiOut->bmiHeader.biCompression == 3 ) + cbHdr += sizeof(DWORD)*3; + else + if ( lpbiOut->bmiHeader.biBitCount <= 8 ) + { + if ( lpbiOut->bmiHeader.biClrUsed == 0 ) + cbHdr += sizeof(RGBQUAD) * (1<bmiHeader.biBitCount); + else + cbHdr += sizeof(RGBQUAD) * lpbiOut->bmiHeader.biClrUsed; + } + } + else + { + TRACE( "get format\n" ); + + cbHdr = ICDecompressGetFormatSize(hic,lpbiIn); + if ( cbHdr < sizeof(BITMAPINFOHEADER) ) + goto err; + pHdr = (BYTE*)HeapAlloc(GetProcessHeap(),0,cbHdr+sizeof(RGBQUAD)*256); + if ( pHdr == NULL ) + goto err; + ZeroMemory( pHdr, cbHdr+sizeof(RGBQUAD)*256 ); + if ( ICDecompressGetFormat( hic, lpbiIn, (BITMAPINFO*)pHdr ) != ICERR_OK ) + goto err; + lpbiOut = (BITMAPINFO*)pHdr; + if ( lpbiOut->bmiHeader.biBitCount <= 8 && + ICDecompressGetPalette( hic, lpbiIn, lpbiOut ) != ICERR_OK && + lpbiIn->bmiHeader.biBitCount == lpbiOut->bmiHeader.biBitCount ) + { + if ( lpbiIn->bmiHeader.biClrUsed == 0 ) + memcpy( lpbiOut->bmiColors, lpbiIn->bmiColors, sizeof(RGBQUAD)*(1<bmiHeader.biBitCount) ); + else + memcpy( lpbiOut->bmiColors, lpbiIn->bmiColors, sizeof(RGBQUAD)*lpbiIn->bmiHeader.biClrUsed ); + } + if ( lpbiOut->bmiHeader.biBitCount <= 8 && + lpbiOut->bmiHeader.biClrUsed == 0 ) + lpbiOut->bmiHeader.biClrUsed = 1<bmiHeader.biBitCount; + + lpbiOut->bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + cbHdr = sizeof(BITMAPINFOHEADER) + sizeof(RGBQUAD)*lpbiOut->bmiHeader.biClrUsed; + } + + biSizeImage = lpbiOut->bmiHeader.biSizeImage; + if ( biSizeImage == 0 ) + biSizeImage = ((((lpbiOut->bmiHeader.biWidth * lpbiOut->bmiHeader.biBitCount + 7) >> 3) + 3) & (~3)) * abs(lpbiOut->bmiHeader.biHeight); + + TRACE( "call ICDecompressBegin\n" ); + + if ( ICDecompressBegin( hic, lpbiIn, lpbiOut ) != ICERR_OK ) + goto err; + bInDecompress = TRUE; + + TRACE( "cbHdr %ld, biSizeImage %ld\n", cbHdr, biSizeImage ); + + hMem = GlobalAlloc( GMEM_MOVEABLE|GMEM_ZEROINIT, cbHdr + biSizeImage ); + if ( hMem == NULL ) + { + WARN( "out of memory\n" ); + goto err; + } + pMem = (BYTE*)GlobalLock( hMem ); + if ( pMem == NULL ) + goto err; + memcpy( pMem, lpbiOut, cbHdr ); + + TRACE( "call ICDecompress\n" ); + if ( ICDecompress( hic, 0, &lpbiIn->bmiHeader, lpBits, &lpbiOut->bmiHeader, pMem+cbHdr ) != ICERR_OK ) + goto err; + + bSucceeded = TRUE; +err: + if ( bInDecompress ) + ICDecompressEnd( hic ); + if ( bReleaseIC ) + ICClose(hic); + if ( pHdr != NULL ) + HeapFree(GetProcessHeap(),0,pHdr); + if ( pMem != NULL ) + GlobalUnlock( hMem ); + if ( !bSucceeded && hMem != NULL ) + { + GlobalFree(hMem); hMem = NULL; + } + + return (HANDLE)hMem; +} + +static BOOL GetFileNamePreview(LPVOID lpofn,BOOL bSave,BOOL bUnicode) +{ + CHAR szFunctionName[20]; + BOOL (*fnGetFileName)(LPVOID); + HMODULE hComdlg32; + BOOL ret; + + FIXME("(%p,%d,%d), semi-stub!\n",lpofn,bSave,bUnicode); + + lstrcpyA(szFunctionName, (bSave ? "GetSaveFileName" : "GetOpenFileName")); + lstrcatA(szFunctionName, (bUnicode ? "W" : "A")); + + hComdlg32 = LoadLibraryA("COMDLG32.DLL"); + if (hComdlg32 == NULL) + return FALSE; + + fnGetFileName = (LPVOID)GetProcAddress(hComdlg32, szFunctionName); + if (fnGetFileName == NULL) + return FALSE; + + /* FIXME: need to add OFN_ENABLEHOOK and our own handler */ + ret = fnGetFileName(lpofn); + + FreeLibrary(hComdlg32); + return ret; +} + +/*********************************************************************** + * GetOpenFileNamePreviewA [MSVFW32.@] + */ +BOOL WINAPI GetOpenFileNamePreviewA(LPOPENFILENAMEA lpofn) +{ + FIXME("(%p), semi-stub!\n", lpofn); + + return GetFileNamePreview(lpofn, FALSE, FALSE); +} + +/*********************************************************************** + * GetOpenFileNamePreviewW [MSVFW32.@] + */ +BOOL WINAPI GetOpenFileNamePreviewW(LPOPENFILENAMEW lpofn) +{ + FIXME("(%p), semi-stub!\n", lpofn); + + return GetFileNamePreview(lpofn, FALSE, TRUE); +} + +/*********************************************************************** + * GetSaveFileNamePreviewA [MSVFW32.@] + */ +BOOL WINAPI GetSaveFileNamePreviewA(LPOPENFILENAMEA lpofn) +{ + FIXME("(%p), semi-stub!\n", lpofn); + + return GetFileNamePreview(lpofn, TRUE, FALSE); +} + +/*********************************************************************** + * GetSaveFileNamePreviewW [MSVFW32.@] + */ +BOOL WINAPI GetSaveFileNamePreviewW(LPOPENFILENAMEW lpofn) +{ + FIXME("(%p), semi-stub!\n", lpofn); + + return GetFileNamePreview(lpofn, TRUE, TRUE); +} diff --git a/reactos/dll/msvideo/msvideo_private.h b/reactos/dll/msvideo/msvideo_private.h new file mode 100644 index 00000000000..bf0544f2f5e --- /dev/null +++ b/reactos/dll/msvideo/msvideo_private.h @@ -0,0 +1,65 @@ +/* + * Copyright 1999 Marcus Meissner + * + * 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 + */ + +#ifndef __WINE_MSVIDEO_PRIVATE_H +#define __WINE_MSVIDEO_PRIVATE_H + +#define COM_NO_WINDOWS_H +#include +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "winuser.h" +#include "commdlg.h" +#include "vfw.h" + +/* HIC struct (same layout as Win95 one) */ +typedef struct tagWINE_HIC { + DWORD magic; /* 00: 'Smag' */ + HANDLE curthread; /* 04: */ + DWORD type; /* 08: */ + DWORD handler; /* 0C: */ + HDRVR hdrv; /* 10: */ + DWORD private; /* 14:(handled by SendDriverMessage)*/ + DRIVERPROC driverproc; /* 18:(handled by SendDriverMessage)*/ + DWORD x1; /* 1c: name? */ + WORD x2; /* 20: */ + DWORD x3; /* 22: */ + /* 26: */ + DWORD driverproc16; /* Wine specific flags */ + HIC hic; + DWORD driverId; + struct tagWINE_HIC* next; +} WINE_HIC; + +HIC MSVIDEO_OpenFunction(DWORD, DWORD, UINT, DRIVERPROC, DWORD); +LRESULT MSVIDEO_SendMessage(WINE_HIC*, UINT, DWORD, DWORD); +WINE_HIC* MSVIDEO_GetHicPtr(HIC); + +extern LRESULT (CALLBACK *pFnCallTo16)(HDRVR, HIC, UINT, LPARAM, LPARAM); + +/* handle16 --> handle conversions */ +#define HDRAWDIB_32(h16) ((HDRAWDIB)(ULONG_PTR)(h16)) +#define HIC_32(h16) ((HIC)(ULONG_PTR)(h16)) + +/* handle --> handle16 conversions */ +#define HDRVR_16(h32) (LOWORD(h32)) +#define HDRAWDIB_16(h32) (LOWORD(h32)) +#define HIC_16(h32) (LOWORD(h32)) + +#endif /* __WINE_MSVIDEO_PRIVATE_H */ diff --git a/reactos/dll/msvideo/vfw16.h b/reactos/dll/msvideo/vfw16.h new file mode 100644 index 00000000000..3257ed00039 --- /dev/null +++ b/reactos/dll/msvideo/vfw16.h @@ -0,0 +1,130 @@ +/* + * Copyright 1999 Marcus Meissner + * + * 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 + */ + +#ifndef __WINE_VFW16_H +#define __WINE_VFW16_H + +#include + +#include "windef.h" +#include "winbase.h" +#include "wingdi.h" +#include "vfw.h" +#include "wownt32.h" +#include "wine/windef16.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +typedef HANDLE16 HDRAWDIB16; + +#include "pshpack1.h" + +typedef struct { + DWORD dwSize; + DWORD fccType; + DWORD fccHandler; + DWORD dwFlags; + DWORD dwVersion; + DWORD dwVersionICM; + /* + * under Win16, normal chars are used + */ + CHAR szName[16]; + CHAR szDescription[128]; + CHAR szDriver[128]; +} ICINFO16; + +typedef struct { + DWORD dwFlags; + LPBITMAPINFOHEADER lpbiSrc; + LPVOID lpSrc; + LPBITMAPINFOHEADER lpbiDst; + LPVOID lpDst; + + INT16 xDst; /* destination rectangle */ + INT16 yDst; + INT16 dxDst; + INT16 dyDst; + + INT16 xSrc; /* source rectangle */ + INT16 ySrc; + INT16 dxSrc; + INT16 dySrc; +} ICDECOMPRESSEX16; + +typedef struct { + DWORD dwFlags; + HPALETTE16 hpal; + HWND16 hwnd; + HDC16 hdc; + INT16 xDst; + INT16 yDst; + INT16 dxDst; + INT16 dyDst; + LPBITMAPINFOHEADER lpbi; + INT16 xSrc; + INT16 ySrc; + INT16 dxSrc; + INT16 dySrc; + DWORD dwRate; + DWORD dwScale; +} ICDRAWBEGIN16; + +#include "poppack.h" + +typedef struct { + DWORD dwFlags; + LPBITMAPINFOHEADER lpbiIn; + LPBITMAPINFOHEADER lpbiSuggest; + INT16 dxSrc; + INT16 dySrc; + INT16 dxDst; + INT16 dyDst; + HIC16 hicDecompressor; +} ICDRAWSUGGEST16; + +DWORD VFWAPIV ICDraw16(HIC16,DWORD,LPVOID,LPVOID,DWORD,LONG); +DWORD VFWAPIV ICDrawBegin16(HIC16,DWORD,HPALETTE16,HWND16,HDC16,INT16, + INT16,INT16,INT16,LPBITMAPINFOHEADER, + INT16,INT16,INT16,INT16,DWORD,DWORD); +LRESULT WINAPI ICClose16(HIC16); +DWORD VFWAPIV ICCompress16(HIC16,DWORD,LPBITMAPINFOHEADER,LPVOID, + LPBITMAPINFOHEADER,LPVOID,LPDWORD, + LPDWORD,LONG,DWORD,DWORD, + LPBITMAPINFOHEADER,LPVOID); +DWORD VFWAPIV ICDecompress16(HIC16,DWORD,LPBITMAPINFOHEADER,LPVOID, + LPBITMAPINFOHEADER,LPVOID); +HIC16 VFWAPI ICGetDisplayFormat16(HIC16,LPBITMAPINFOHEADER, + LPBITMAPINFOHEADER,INT16,INT16, + INT16); +LRESULT VFWAPI ICGetInfo16(HIC16,ICINFO16 *,DWORD); +BOOL16 VFWAPI ICInfo16(DWORD,DWORD,ICINFO16 *); +HIC16 VFWAPI ICLocate16(DWORD,DWORD,LPBITMAPINFOHEADER, + LPBITMAPINFOHEADER,WORD); +LRESULT VFWAPIV ICMessage16( HIC16 hic, UINT16 msg, UINT16 cb, VA_LIST16 valist ); +HIC16 VFWAPI ICOpen16(DWORD,DWORD,UINT16); +HIC16 VFWAPI ICOpenFunction16(DWORD,DWORD,UINT16,FARPROC16); +LRESULT VFWAPI ICSendMessage16(HIC16,UINT16,DWORD,DWORD); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __WINE_VFW16_H */ diff --git a/reactos/dll/mswsock/extensions.c b/reactos/dll/mswsock/extensions.c new file mode 100644 index 00000000000..0e79d5e1e74 --- /dev/null +++ b/reactos/dll/mswsock/extensions.c @@ -0,0 +1,54 @@ +/* $Id: stubs.c 12852 2005-01-06 13:58:04Z mf $ + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS WinSock DLL + * FILE: stubs.c + * PURPOSE: WSAIoctl wrappers for Microsoft extensions to Winsock + * PROGRAMMERS: KJK::Hyperion + * REVISIONS: + */ + +#include +#include +#include + +/* + * @implemented + */ +BOOL +STDCALL +TransmitFile(SOCKET Socket, + HANDLE File, + DWORD NumberOfBytesToWrite, + DWORD NumberOfBytesPerSend, + LPOVERLAPPED Overlapped, + LPTRANSMIT_FILE_BUFFERS TransmitBuffers, + DWORD Flags) +{ + static GUID TransmitFileGUID = WSAID_TRANSMITFILE; + LPFN_TRANSMITFILE pfnTransmitFile; + DWORD cbBytesReturned; + + if (WSAIoctl(Socket, + SIO_GET_EXTENSION_FUNCTION_POINTER, + &TransmitFileGUID, + sizeof(TransmitFileGUID), + &pfnTransmitFile, + sizeof(pfnTransmitFile), + &cbBytesReturned, + NULL, + NULL) == SOCKET_ERROR) + { + return FALSE; + } + + return pfnTransmitFile(Socket, + File, + NumberOfBytesToWrite, + NumberOfBytesPerSend, + Overlapped, + TransmitBuffers, + Flags); +} + +/* EOF */ diff --git a/reactos/dll/mswsock/mswsock.def b/reactos/dll/mswsock/mswsock.def new file mode 100644 index 00000000000..3fca87120a5 --- /dev/null +++ b/reactos/dll/mswsock/mswsock.def @@ -0,0 +1,39 @@ +; $Id$ +; +; MSWSOCK.DLL - Windows Sockets 2 DLL + +LIBRARY mswsock.dll + +EXPORTS +ServiceMain@8 @0 +SvchostPushServiceGlobals@4 @1 +AcceptEx@32 @2 +EnumProtocolsA@12 @3 +EnumProtocolsW@12 @4 +GetAcceptExSockaddrs@32 @5 +GetAddressByNameA@40 @6 +GetAddressByNameW@40 @7 +GetNameByTypeA@12 @8 +GetNameByTypeW@12 @9 +GetServiceA@28 @10 +GetServiceW@28 @11 +GetTypeByNameA@8 @12 +GetTypeByNameW@8 @13 +MigrateWinsockConfiguration@12 @14 +NPLoadNameSpaces@12 @15 +NSPStartup@8 @16 +SetServiceA@24 @17 +SetServiceW@24 @18 +StartWsdpService@0 @19 +StopWsdpService@0 @20 +TransmitFile@28 @21 +WSARecvEx@16 @22 +WSPStartup@76 @23 +dn_expand@20 @24 +getnetbyname@4 @25 +inet_network@4 @26 +rcmd@24 @27 +rexec@24 @28 +rresvport@4 @29 +s_perror@4 @30 +sethostname@8 @31 diff --git a/reactos/dll/mswsock/mswsock.rc b/reactos/dll/mswsock/mswsock.rc new file mode 100644 index 00000000000..36b56163852 --- /dev/null +++ b/reactos/dll/mswsock/mswsock.rc @@ -0,0 +1,7 @@ +/* $Id$ */ + +#define REACTOS_VERSION_DLL +#define REACTOS_STR_FILE_DESCRIPTION "Windows Sockets 2 DLL\0" +#define REACTOS_STR_INTERNAL_NAME "mswsock\0" +#define REACTOS_STR_ORIGINAL_FILENAME "mswsock.dll\0" +#include diff --git a/reactos/dll/mswsock/mswsock.xml b/reactos/dll/mswsock/mswsock.xml new file mode 100644 index 00000000000..a661f240637 --- /dev/null +++ b/reactos/dll/mswsock/mswsock.xml @@ -0,0 +1,11 @@ + + + + + + kernel32 + ws2_32 + extensions.c + stubs.c + mswsock.rc + diff --git a/reactos/dll/mswsock/stubs.c b/reactos/dll/mswsock/stubs.c new file mode 100644 index 00000000000..dc910121f31 --- /dev/null +++ b/reactos/dll/mswsock/stubs.c @@ -0,0 +1,517 @@ +/* $Id$ + * + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS WinSock DLL + * FILE: stubs.c + * PURPOSE: Stub functions + * PROGRAMMERS: Ge van Geldorp (ge@gse.nl) + * REVISIONS: + */ + +#include +#include +#include +#include +#include +#include + +typedef DWORD (* LPFN_NSPAPI)(VOID); +typedef struct _NS_ROUTINE { + DWORD dwFunctionCount; + LPFN_NSPAPI *alpfnFunctions; + DWORD dwNameSpace; + DWORD dwPriority; +} NS_ROUTINE, *PNS_ROUTINE, * FAR LPNS_ROUTINE; + +/* + * @unimplemented + */ +BOOL +STDCALL +AcceptEx(SOCKET ListenSocket, + SOCKET AcceptSocket, + PVOID OutputBuffer, + DWORD ReceiveDataLength, + DWORD LocalAddressLength, + DWORD RemoteAddressLength, + LPDWORD BytesReceived, + LPOVERLAPPED Overlapped) +{ + OutputDebugStringW(L"w32sock AcceptEx stub called\n"); + + return FALSE; +} + + +/* + * @unimplemented + */ +INT +STDCALL +EnumProtocolsA(LPINT ProtocolCount, + LPVOID ProtocolBuffer, + LPDWORD BufferLength) +{ + OutputDebugStringW(L"w32sock EnumProtocolsA stub called\n"); + + return SOCKET_ERROR; +} + + +/* + * @unimplemented + */ +INT +STDCALL +EnumProtocolsW(LPINT ProtocolCount, + LPVOID ProtocolBuffer, + LPDWORD BufferLength) +{ + OutputDebugStringW(L"w32sock EnumProtocolsW stub called\n"); + + return SOCKET_ERROR; +} + + +/* + * @unimplemented + */ +VOID +STDCALL +GetAcceptExSockaddrs(PVOID OutputBuffer, + DWORD ReceiveDataLength, + DWORD LocalAddressLength, + DWORD RemoteAddressLength, + LPSOCKADDR* LocalSockaddr, + LPINT LocalSockaddrLength, + LPSOCKADDR* RemoteSockaddr, + LPINT RemoteSockaddrLength) +{ + OutputDebugStringW(L"w32sock GetAcceptExSockaddrs stub called\n"); +} + + +/* + * @unimplemented + */ +INT +STDCALL +GetAddressByNameA(DWORD NameSpace, + LPGUID ServiceType, + LPSTR ServiceName, + LPINT Protocols, + DWORD Resolution, + LPSERVICE_ASYNC_INFO ServiceAsyncInfo, + LPVOID CsaddrBuffer, + LPDWORD BufferLength, + LPSTR AliasBuffer, + LPDWORD AliasBufferLength) +{ + OutputDebugStringW(L"w32sock GetAddressByNameA stub called\n"); + + return SOCKET_ERROR; +} + + +/* + * @unimplemented + */ +INT +STDCALL +GetAddressByNameW(DWORD NameSpace, + LPGUID ServiceType, + LPWSTR ServiceName, + LPINT Protocols, + DWORD Resolution, + LPSERVICE_ASYNC_INFO ServiceAsyncInfo, + LPVOID CsaddrBuffer, + LPDWORD BufferLength, + LPWSTR AliasBuffer, + LPDWORD AliasBufferLength) +{ + OutputDebugStringW(L"w32sock GetAddressByNameW stub called\n"); + + return SOCKET_ERROR; +} + + +/* + * @unimplemented + */ +INT +STDCALL +GetServiceA(DWORD NameSpace, + LPGUID Guid, + LPSTR ServiceName, + DWORD Properties, + LPVOID Buffer, + LPDWORD BufferSize, + LPSERVICE_ASYNC_INFO ServiceAsyncInfo) +{ + OutputDebugStringW(L"w32sock GetServiceA stub called\n"); + + return SOCKET_ERROR; +} + + +/* + * @unimplemented + */ +INT +STDCALL +GetServiceW(DWORD NameSpace, + LPGUID Guid, + LPWSTR ServiceName, + DWORD Properties, + LPVOID Buffer, + LPDWORD BufferSize, + LPSERVICE_ASYNC_INFO ServiceAsyncInfo) +{ + OutputDebugStringW(L"w32sock GetServiceW stub called\n"); + + return SOCKET_ERROR; +} + + +/* + * @unimplemented + */ +INT +STDCALL +GetTypeByNameA(LPSTR ServiceName, + LPGUID ServiceType) +{ + OutputDebugStringW(L"w32sock GetTypeByNameA stub called\n"); + + return SOCKET_ERROR; +} + + +/* + * @unimplemented + */ +INT +STDCALL +GetTypeByNameW(LPWSTR ServiceName, + LPGUID ServiceType) +{ + OutputDebugStringW(L"w32sock GetTypeByNameW stub called\n"); + + return SOCKET_ERROR; +} + + +/* + * @unimplemented + */ +INT +STDCALL +MigrateWinsockConfiguration(DWORD Unknown1, + DWORD Unknown2, + DWORD Unknown3) +{ + OutputDebugStringW(L"w32sock MigrateWinsockConfiguration stub called\n"); + + return SOCKET_ERROR; +} + + +/* + * @unimplemented + */ +INT +STDCALL +SetServiceA(DWORD NameSpace, + DWORD Operation, + DWORD Flags, + LPSERVICE_INFOA ServiceInfo, + LPSERVICE_ASYNC_INFO ServiceAsyncInfo, + LPDWORD dwStatusFlags) +{ + OutputDebugStringW(L"w32sock SetServiceA stub called\n"); + + return SOCKET_ERROR; +} + + +/* + * @unimplemented + */ +INT +STDCALL +SetServiceW(DWORD NameSpace, + DWORD Operation, + DWORD Flags, + LPSERVICE_INFOW ServiceInfo, + LPSERVICE_ASYNC_INFO ServiceAsyncInfo, + LPDWORD dwStatusFlags) +{ + OutputDebugStringW(L"w32sock SetServiceW stub called\n"); + + return SOCKET_ERROR; +} + + +/* + * @unimplemented + */ +int +STDCALL +WSARecvEx(SOCKET Sock, + char *Buf, + int Len, + int *Flags) +{ + OutputDebugStringW(L"w32sock WSARecvEx stub called\n"); + + return SOCKET_ERROR; +} + + +/* + * @unimplemented + */ +int +STDCALL +dn_expand(unsigned char *MessagePtr, + unsigned char *EndofMesOrig, + unsigned char *CompDomNam, + unsigned char *ExpandDomNam, + int Length) +{ + OutputDebugStringW(L"w32sock dn_expand stub called\n"); + + return SOCKET_ERROR; +} + + +/* + * @unimplemented + */ +struct netent * +STDCALL +getnetbyname(const char *name) +{ + OutputDebugStringW(L"w32sock getnetbyname stub called\n"); + + return NULL; +} + + +/* + * @unimplemented + */ +UINT +STDCALL +inet_network(const char *cp) +{ + OutputDebugStringW(L"w32sock inet_network stub called\n"); + + return INADDR_NONE; +} + + +/* + * @unimplemented + */ +SOCKET +STDCALL +rcmd(char **AHost, + USHORT InPort, + char *LocUser, + char *RemUser, + char *Cmd, + int *Fd2p) +{ + OutputDebugStringW(L"w32sock rcmd stub called\n"); + + return INVALID_SOCKET; +} + + +/* + * @unimplemented + */ +SOCKET +STDCALL +rexec(char **AHost, + int InPort, + char *User, + char *Passwd, + char *Cmd, + int *Fd2p) +{ + OutputDebugStringW(L"w32sock rexec stub called\n"); + + return INVALID_SOCKET; +} + + +/* + * @unimplemented + */ +SOCKET +STDCALL +rresvport(int *port) +{ + OutputDebugStringW(L"w32sock rresvport stub called\n"); + + return INVALID_SOCKET; +} + + +/* + * @unimplemented + */ +void +STDCALL +s_perror(const char *str) +{ + OutputDebugStringW(L"w32sock s_perror stub called\n"); +} + + +/* + * @unimplemented + */ +int +STDCALL +sethostname(char *Name, int NameLen) +{ + OutputDebugStringW(L"w32sock sethostname stub called\n"); + + return SOCKET_ERROR; +} + + +/* + * @unimplemented + */ +BOOL +STDCALL +DllMain(HINSTANCE InstDLL, + DWORD Reason, + LPVOID Reserved) +{ + return TRUE; +} + + +/* + * @unimplemented + */ +INT +STDCALL +GetNameByTypeA(LPGUID lpServiceType,LPSTR lpServiceName,DWORD dwNameLength) +{ + OutputDebugStringW(L"w32sock GetNameByTypeA stub called\n"); + return TRUE; +} + + +/* + * @unimplemented + */ +INT +STDCALL +GetNameByTypeW(LPGUID lpServiceType,LPWSTR lpServiceName,DWORD dwNameLength) +{ + OutputDebugStringW(L"w32sock GetNameByTypeW stub called\n"); + return TRUE; +} + + +/* + * @unimplemented + */ +INT +STDCALL +NSPStartup( + LPGUID lpProviderId, + LPNSP_ROUTINE lpnspRoutines + ) +{ + return TRUE; +} + + +/* + * @unimplemented + */ +int +STDCALL +WSPStartup( + IN WORD wVersionRequested, + OUT LPWSPDATA lpWSPData, + IN LPWSAPROTOCOL_INFOW lpProtocolInfo, + IN WSPUPCALLTABLE UpcallTable, + OUT LPWSPPROC_TABLE lpProcTable + ) +{ + return TRUE; +} + + +/* + * @unimplemented + */ +INT +STDCALL +NPLoadNameSpaces( + IN OUT LPDWORD lpdwVersion, + IN OUT LPNS_ROUTINE nsrBuffer, + IN OUT LPDWORD lpdwBufferLength + ) +{ + OutputDebugStringW(L"mswsock NPLoadNameSpaces stub called\n"); + + *lpdwVersion = 1; + + return TRUE; +} + + +/* + * @unimplemented + */ +VOID +STDCALL +StartWsdpService() +{ + OutputDebugStringW(L"mswsock StartWsdpService stub called\n"); +} + + +/* + * @unimplemented + */ +VOID +STDCALL +StopWsdpService() +{ + OutputDebugStringW(L"mswsock StopWsdpService stub called\n"); +} + + +/* + * @unimplemented + */ +DWORD +STDCALL +SvchostPushServiceGlobals(DWORD Value) +{ + OutputDebugStringW(L"mswsock SvchostPushServiceGlobals stub called\n"); + + return 0; +} + + +/* + * @unimplemented + */ +VOID +STDCALL +ServiceMain(DWORD Unknown1, DWORD Unknown2) +{ + OutputDebugStringW(L"mswsock ServiceMain stub called\n"); +} diff --git a/reactos/dll/netapi32/access.c b/reactos/dll/netapi32/access.c new file mode 100644 index 00000000000..e3e0a36a74d --- /dev/null +++ b/reactos/dll/netapi32/access.c @@ -0,0 +1,580 @@ +/* + * Copyright 2002 Andriy Palamarchuk + * + * netapi32 access functions + * + * 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 + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "lmcons.h" +#include "lmaccess.h" +#include "lmapibuf.h" +#include "lmerr.h" +#include "netapi32_misc.h" +#include "wine/debug.h" +#include "wine/unicode.h" + +WINE_DEFAULT_DEBUG_CHANNEL(netapi32); + +static const WCHAR sAdminUserName[] = {'A','d','m','i','n','i','s','t','r','a','t', + 'o','r',0}; +static const WCHAR sGuestUserName[] = {'G','u','e','s','t',0}; + +/************************************************************ + * NETAPI_ValidateServername + * + * Validates server name + */ +static NET_API_STATUS NETAPI_ValidateServername(LPCWSTR ServerName) +{ + if (ServerName) + { + if (ServerName[0] == 0) + return ERROR_BAD_NETPATH; + else if ( + ((ServerName[0] == '\\') && + (ServerName[1] != '\\')) + || + ((ServerName[0] == '\\') && + (ServerName[1] == '\\') && + (ServerName[2] == 0)) + ) + return ERROR_INVALID_NAME; + } + return NERR_Success; +} + +/************************************************************ + * NETAPI_IsKnownUser + * + * Checks whether the user name indicates current user. + */ +static BOOL NETAPI_IsKnownUser(LPCWSTR UserName) +{ + DWORD dwSize = UNLEN + 1; + BOOL Result; + LPWSTR buf; + + if (!lstrcmpW(UserName, sAdminUserName) || + !lstrcmpW(UserName, sGuestUserName)) + return TRUE; + NetApiBufferAllocate(dwSize * sizeof(WCHAR), (LPVOID *) &buf); + Result = GetUserNameW(buf, &dwSize); + + Result = Result && !lstrcmpW(UserName, buf); + NetApiBufferFree(buf); + + return Result; +} + +#define NETAPI_ForceKnownUser(UserName, FailureCode) \ + if (!NETAPI_IsKnownUser(UserName)) \ + { \ + FIXME("Can't find information for user %s\n", \ + debugstr_w(UserName)); \ + return FailureCode; \ + } + +/************************************************************ + * NetUserAdd (NETAPI32.@) + */ +NET_API_STATUS WINAPI NetUserAdd(LPCWSTR servername, + DWORD level, LPBYTE bufptr, LPDWORD parm_err) +{ + NET_API_STATUS status; + FIXME("(%s, %ld, %p, %p) stub!\n", debugstr_w(servername), level, bufptr, parm_err); + + status = NETAPI_ValidateServername(servername); + if (status != NERR_Success) + return status; + + if ((bufptr != NULL) && (level > 0) && (level <= 4)) + { + PUSER_INFO_1 ui = (PUSER_INFO_1) bufptr; + TRACE("usri%ld_name: %s\n", level, debugstr_w(ui->usri1_name)); + TRACE("usri%ld_password: %s\n", level, debugstr_w(ui->usri1_password)); + TRACE("usri%ld_comment: %s\n", level, debugstr_w(ui->usri1_comment)); + } + return status; +} + +/************************************************************ + * NetUserDel (NETAPI32.@) + */ +NET_API_STATUS WINAPI NetUserDel(LPCWSTR servername, LPCWSTR username) +{ + NET_API_STATUS status; + FIXME("(%s, %s) stub!\n", debugstr_w(servername), debugstr_w(username)); + + status = NETAPI_ValidateServername(servername); + if (status != NERR_Success) + return status; + + if (!NETAPI_IsKnownUser(username)) + return NERR_UserNotFound; + + /* Delete the user here */ + return status; +} + +/************************************************************ + * NetUserGetInfo (NETAPI32.@) + */ +NET_API_STATUS WINAPI +NetUserGetInfo(LPCWSTR servername, LPCWSTR username, DWORD level, + LPBYTE* bufptr) +{ + NET_API_STATUS status; + TRACE("(%s, %s, %ld, %p)\n", debugstr_w(servername), debugstr_w(username), + level, bufptr); + status = NETAPI_ValidateServername(servername); + if (status != NERR_Success) + return status; + NETAPI_ForceLocalComputer(servername, NERR_InvalidComputer); + NETAPI_ForceKnownUser(username, NERR_UserNotFound); + + switch (level) + { + case 0: + { + PUSER_INFO_0 ui; + int name_sz; + + name_sz = lstrlenW(username) + 1; + + /* set up buffer */ + NetApiBufferAllocate(sizeof(USER_INFO_0) + name_sz * sizeof(WCHAR), + (LPVOID *) bufptr); + + ui = (PUSER_INFO_0) *bufptr; + ui->usri0_name = (LPWSTR) (*bufptr + sizeof(USER_INFO_0)); + + /* get data */ + lstrcpyW(ui->usri0_name, username); + break; + } + + case 10: + { + PUSER_INFO_10 ui; + PUSER_INFO_0 ui0; + NET_API_STATUS status; + /* sizes of the field buffers in WCHARS */ + int name_sz, comment_sz, usr_comment_sz, full_name_sz; + + comment_sz = 1; + usr_comment_sz = 1; + full_name_sz = 1; + + /* get data */ + status = NetUserGetInfo(servername, username, 0, (LPBYTE *) &ui0); + if (status != NERR_Success) + { + NetApiBufferFree(ui0); + return status; + } + name_sz = lstrlenW(ui0->usri0_name) + 1; + + /* set up buffer */ + NetApiBufferAllocate(sizeof(USER_INFO_10) + + (name_sz + comment_sz + usr_comment_sz + + full_name_sz) * sizeof(WCHAR), + (LPVOID *) bufptr); + ui = (PUSER_INFO_10) *bufptr; + ui->usri10_name = (LPWSTR) (*bufptr + sizeof(USER_INFO_10)); + ui->usri10_comment = (LPWSTR) ( + ((PBYTE) ui->usri10_name) + name_sz * sizeof(WCHAR)); + ui->usri10_usr_comment = (LPWSTR) ( + ((PBYTE) ui->usri10_comment) + comment_sz * sizeof(WCHAR)); + ui->usri10_full_name = (LPWSTR) ( + ((PBYTE) ui->usri10_usr_comment) + usr_comment_sz * sizeof(WCHAR)); + + /* set data */ + lstrcpyW(ui->usri10_name, ui0->usri0_name); + NetApiBufferFree(ui0); + ui->usri10_comment[0] = 0; + ui->usri10_usr_comment[0] = 0; + ui->usri10_full_name[0] = 0; + break; + } + + case 1: + { + static const WCHAR homedirW[] = {'H','O','M','E',0}; + PUSER_INFO_1 ui; + PUSER_INFO_0 ui0; + NET_API_STATUS status; + /* sizes of the field buffers in WCHARS */ + int name_sz, password_sz, home_dir_sz, comment_sz, script_path_sz; + + password_sz = 1; /* not filled out for security reasons for NetUserGetInfo*/ + comment_sz = 1; + script_path_sz = 1; + + /* get data */ + status = NetUserGetInfo(servername, username, 0, (LPBYTE *) &ui0); + if (status != NERR_Success) + { + NetApiBufferFree(ui0); + return status; + } + name_sz = lstrlenW(ui0->usri0_name) + 1; + home_dir_sz = GetEnvironmentVariableW(homedirW, NULL,0); + /* set up buffer */ + NetApiBufferAllocate(sizeof(USER_INFO_1) + + (name_sz + password_sz + home_dir_sz + + comment_sz + script_path_sz) * sizeof(WCHAR), + (LPVOID *) bufptr); + + ui = (PUSER_INFO_1) *bufptr; + ui->usri1_name = (LPWSTR) (ui + 1); + ui->usri1_password = ui->usri1_name + name_sz; + ui->usri1_home_dir = ui->usri1_password + password_sz; + ui->usri1_comment = ui->usri1_home_dir + home_dir_sz; + ui->usri1_script_path = ui->usri1_comment + comment_sz; + /* set data */ + lstrcpyW(ui->usri1_name, ui0->usri0_name); + NetApiBufferFree(ui0); + ui->usri1_password[0] = 0; + ui->usri1_password_age = 0; + ui->usri1_priv = 0; + GetEnvironmentVariableW(homedirW, ui->usri1_home_dir,home_dir_sz); + ui->usri1_comment[0] = 0; + ui->usri1_flags = 0; + ui->usri1_script_path[0] = 0; + break; + } + case 2: + case 3: + case 4: + case 11: + case 20: + case 23: + case 1003: + case 1005: + case 1006: + case 1007: + case 1008: + case 1009: + case 1010: + case 1011: + case 1012: + case 1013: + case 1014: + case 1017: + case 1018: + case 1020: + case 1023: + case 1024: + case 1025: + case 1051: + case 1052: + case 1053: + { + FIXME("Level %ld is not implemented\n", level); + break; + } + default: + ERR("Invalid level %ld is specified\n", level); + return ERROR_INVALID_LEVEL; + } + return NERR_Success; +} + + + +/************************************************************ + * NetUserEnum (NETAPI32.@) + */ +NET_API_STATUS WINAPI +NetUserEnum(LPCWSTR servername, DWORD level, DWORD filter, LPBYTE* bufptr, + DWORD prefmaxlen, LPDWORD entriesread, LPDWORD totalentries, + LPDWORD resume_handle) +{ + FIXME("(%s,%ld, 0x%ld,%p,%ld,%p,%p,%p) stub!\n", debugstr_w(servername), level, + filter, bufptr, prefmaxlen, entriesread, totalentries, resume_handle); + + return ERROR_ACCESS_DENIED; +} + +/************************************************************ + * ACCESS_QueryAdminDisplayInformation + * + * Creates a buffer with information for the Admin User + */ +static void ACCESS_QueryAdminDisplayInformation(PNET_DISPLAY_USER *buf, PDWORD pdwSize) +{ + static const WCHAR sAdminUserName[] = { + 'A','d','m','i','n','i','s','t','r','a','t','o','r',0}; + + /* sizes of the field buffers in WCHARS */ + int name_sz, comment_sz, full_name_sz; + PNET_DISPLAY_USER usr; + + /* set up buffer */ + name_sz = lstrlenW(sAdminUserName); + comment_sz = 1; + full_name_sz = 1; + + *pdwSize = sizeof(NET_DISPLAY_USER); + *pdwSize += (name_sz + comment_sz + full_name_sz) * sizeof(WCHAR); + NetApiBufferAllocate(*pdwSize, (LPVOID *) buf); + + usr = *buf; + usr->usri1_name = (LPWSTR) ((PBYTE) usr + sizeof(NET_DISPLAY_USER)); + usr->usri1_comment = (LPWSTR) ( + ((PBYTE) usr->usri1_name) + name_sz * sizeof(WCHAR)); + usr->usri1_full_name = (LPWSTR) ( + ((PBYTE) usr->usri1_comment) + comment_sz * sizeof(WCHAR)); + + /* set data */ + lstrcpyW(usr->usri1_name, sAdminUserName); + usr->usri1_comment[0] = 0; + usr->usri1_flags = UF_SCRIPT | UF_NORMAL_ACCOUNT | UF_DONT_EXPIRE_PASSWD; + usr->usri1_full_name[0] = 0; + usr->usri1_user_id = 500; + usr->usri1_next_index = 0; +} + +/************************************************************ + * ACCESS_QueryGuestDisplayInformation + * + * Creates a buffer with information for the Guest User + */ +static void ACCESS_QueryGuestDisplayInformation(PNET_DISPLAY_USER *buf, PDWORD pdwSize) +{ + static const WCHAR sGuestUserName[] = { + 'G','u','e','s','t',0 }; + + /* sizes of the field buffers in WCHARS */ + int name_sz, comment_sz, full_name_sz; + PNET_DISPLAY_USER usr; + + /* set up buffer */ + name_sz = lstrlenW(sGuestUserName); + comment_sz = 1; + full_name_sz = 1; + + *pdwSize = sizeof(NET_DISPLAY_USER); + *pdwSize += (name_sz + comment_sz + full_name_sz) * sizeof(WCHAR); + NetApiBufferAllocate(*pdwSize, (LPVOID *) buf); + + usr = *buf; + usr->usri1_name = (LPWSTR) ((PBYTE) usr + sizeof(NET_DISPLAY_USER)); + usr->usri1_comment = (LPWSTR) ( + ((PBYTE) usr->usri1_name) + name_sz * sizeof(WCHAR)); + usr->usri1_full_name = (LPWSTR) ( + ((PBYTE) usr->usri1_comment) + comment_sz * sizeof(WCHAR)); + + /* set data */ + lstrcpyW(usr->usri1_name, sGuestUserName); + usr->usri1_comment[0] = 0; + usr->usri1_flags = UF_ACCOUNTDISABLE | UF_SCRIPT | UF_NORMAL_ACCOUNT | + UF_DONT_EXPIRE_PASSWD; + usr->usri1_full_name[0] = 0; + usr->usri1_user_id = 500; + usr->usri1_next_index = 0; +} + +/************************************************************ + * NetQueryDisplayInformation (NETAPI32.@) + * Copies NET_DISPLAY_USER record. + */ +static void ACCESS_CopyDisplayUser(PNET_DISPLAY_USER dest, LPWSTR *dest_buf, + PNET_DISPLAY_USER src) +{ + LPWSTR str = *dest_buf; + + src->usri1_name = str; + lstrcpyW(src->usri1_name, dest->usri1_name); + str = (LPWSTR) ( + ((PBYTE) str) + (lstrlenW(str) + 1) * sizeof(WCHAR)); + + src->usri1_comment = str; + lstrcpyW(src->usri1_comment, dest->usri1_comment); + str = (LPWSTR) ( + ((PBYTE) str) + (lstrlenW(str) + 1) * sizeof(WCHAR)); + + src->usri1_flags = dest->usri1_flags; + + src->usri1_full_name = str; + lstrcpyW(src->usri1_full_name, dest->usri1_full_name); + str = (LPWSTR) ( + ((PBYTE) str) + (lstrlenW(str) + 1) * sizeof(WCHAR)); + + src->usri1_user_id = dest->usri1_user_id; + src->usri1_next_index = dest->usri1_next_index; + *dest_buf = str; +} + +/************************************************************ + * NetQueryDisplayInformation (NETAPI32.@) + * + * The buffer structure: + * - array of fixed size record of the level type + * - strings, referenced by the record of the level type + */ +NET_API_STATUS WINAPI +NetQueryDisplayInformation( + LPCWSTR ServerName, DWORD Level, DWORD Index, DWORD EntriesRequested, + DWORD PreferredMaximumLength, LPDWORD ReturnedEntryCount, + PVOID *SortedBuffer) +{ + TRACE("(%s, %ld, %ld, %ld, %ld, %p, %p)\n", debugstr_w(ServerName), + Level, Index, EntriesRequested, PreferredMaximumLength, + ReturnedEntryCount, SortedBuffer); + NETAPI_ForceLocalComputer(ServerName, ERROR_ACCESS_DENIED); + switch (Level) + { + case 1: + { + /* current record */ + PNET_DISPLAY_USER inf; + /* current available strings buffer */ + LPWSTR str; + PNET_DISPLAY_USER admin, guest; + DWORD admin_size, guest_size; + LPWSTR name = NULL; + DWORD dwSize; + + /* sizes of the field buffers in WCHARS */ + int name_sz, comment_sz, full_name_sz; + + /* number of the records, returned in SortedBuffer + 3 - for current user, Administrator and Guest users + */ + int records = 3; + + FIXME("Level %ld partially implemented\n", Level); + *ReturnedEntryCount = records; + comment_sz = 1; + full_name_sz = 1; + + /* get data */ + dwSize = UNLEN + 1; + NetApiBufferAllocate(dwSize, (LPVOID *) &name); + if (!GetUserNameW(name, &dwSize)) + { + NetApiBufferFree(name); + return ERROR_ACCESS_DENIED; + } + name_sz = dwSize; + ACCESS_QueryAdminDisplayInformation(&admin, &admin_size); + ACCESS_QueryGuestDisplayInformation(&guest, &guest_size); + + /* set up buffer */ + dwSize = sizeof(NET_DISPLAY_USER) * records; + dwSize += (name_sz + comment_sz + full_name_sz) * sizeof(WCHAR); + + NetApiBufferAllocate(dwSize + + admin_size - sizeof(NET_DISPLAY_USER) + + guest_size - sizeof(NET_DISPLAY_USER), + (LPVOID *) SortedBuffer); + inf = (PNET_DISPLAY_USER) *SortedBuffer; + str = (LPWSTR) ((PBYTE) inf + sizeof(NET_DISPLAY_USER) * records); + inf->usri1_name = str; + str = (LPWSTR) ( + ((PBYTE) str) + name_sz * sizeof(WCHAR)); + inf->usri1_comment = str; + str = (LPWSTR) ( + ((PBYTE) str) + comment_sz * sizeof(WCHAR)); + inf->usri1_full_name = str; + str = (LPWSTR) ( + ((PBYTE) str) + full_name_sz * sizeof(WCHAR)); + + /* set data */ + lstrcpyW(inf->usri1_name, name); + NetApiBufferFree(name); + inf->usri1_comment[0] = 0; + inf->usri1_flags = + UF_SCRIPT | UF_NORMAL_ACCOUNT | UF_DONT_EXPIRE_PASSWD; + inf->usri1_full_name[0] = 0; + inf->usri1_user_id = 0; + inf->usri1_next_index = 0; + + inf++; + ACCESS_CopyDisplayUser(admin, &str, inf); + NetApiBufferFree(admin); + + inf++; + ACCESS_CopyDisplayUser(guest, &str, inf); + NetApiBufferFree(guest); + break; + } + + case 2: + case 3: + { + FIXME("Level %ld is not implemented\n", Level); + break; + } + + default: + ERR("Invalid level %ld is specified\n", Level); + return ERROR_INVALID_LEVEL; + } + return NERR_Success; +} + +/************************************************************ + * NetGetDCName (NETAPI32.@) + * + * Return the name of the primary domain controller (PDC) + */ + +NET_API_STATUS WINAPI +NetGetDCName(LPCWSTR servername, LPCWSTR domainname, LPBYTE *bufptr) +{ + FIXME("(%s, %s, %p) stub!\n", debugstr_w(servername), + debugstr_w(domainname), bufptr); + return NERR_DCNotFound; /* say we can't find a domain controller */ +} + + +/************************************************************ + * NetUserModalsGet (NETAPI32.@) + */ +NET_API_STATUS WINAPI NetUserModalsGet(LPCWSTR szServer, DWORD level, LPBYTE *pbuffer) +{ + FIXME("(%s %ld %p) stub!\n", debugstr_w(szServer), level, pbuffer); + return NERR_InternalError; +} + +/************************************************************ + * NetLocalGroupAdd (NETAPI32.@) + */ +NET_API_STATUS WINAPI NetLocalGroupAdd(LPCWSTR servername, DWORD level, + LPBYTE buf, LPDWORD parm_err) +{ + FIXME("(%s %ld %p %p) stub!\n", debugstr_w(servername), level, buf, parm_err); + return NERR_Success; +} + +/************************************************************ + * NetLocalGroupSetMember (NETAPI32.@) + */ + +NET_API_STATUS WINAPI NetLocalGroupSetMembers(LPCWSTR servername, + LPCWSTR groupname, DWORD level, LPBYTE buf, DWORD totalentries) +{ + FIXME("(%s %s %ld %p %ld) stub!\n", debugstr_w(servername), + debugstr_w(groupname),level, buf, totalentries); + return NERR_Success; +} diff --git a/reactos/dll/netapi32/apibuf.c b/reactos/dll/netapi32/apibuf.c new file mode 100644 index 00000000000..6a8b8db1bde --- /dev/null +++ b/reactos/dll/netapi32/apibuf.c @@ -0,0 +1,97 @@ +/* + * Copyright 2002 Andriy Palamarchuk + * + * Net API buffer calls + * + * 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 + +#include "windef.h" +#include "winbase.h" +#include "lmcons.h" +#include "lmapibuf.h" +#include "lmerr.h" +#include "winerror.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(netapi32); + +/************************************************************ + * NetApiBufferAllocate (NETAPI32.@) + */ +NET_API_STATUS WINAPI NetApiBufferAllocate(DWORD ByteCount, LPVOID* Buffer) +{ + TRACE("(%ld, %p)\n", ByteCount, Buffer); + *Buffer = HeapAlloc(GetProcessHeap(), 0, ByteCount); + if (*Buffer) + return NERR_Success; + else + return GetLastError(); +} + +/************************************************************ + * NetApiBufferFree (NETAPI32.@) + */ +NET_API_STATUS WINAPI NetApiBufferFree(LPVOID Buffer) +{ + TRACE("(%p)\n", Buffer); + HeapFree(GetProcessHeap(), 0, Buffer); + return NERR_Success; +} + +/************************************************************ + * NetApiBufferReallocate (NETAPI32.@) + */ +NET_API_STATUS WINAPI NetApiBufferReallocate(LPVOID OldBuffer, DWORD NewByteCount, + LPVOID* NewBuffer) +{ + TRACE("(%p, %ld, %p)\n", OldBuffer, NewByteCount, NewBuffer); + if (NewByteCount) + { + if (OldBuffer) + *NewBuffer = HeapReAlloc(GetProcessHeap(), 0, OldBuffer, NewByteCount); + else + *NewBuffer = HeapAlloc(GetProcessHeap(), 0, NewByteCount); + return *NewBuffer ? NERR_Success : GetLastError(); + } + else + { + if (!HeapFree(GetProcessHeap(), 0, OldBuffer)) return GetLastError(); + *NewBuffer = 0; + return NERR_Success; + } +} + +/************************************************************ + * NetApiBufferSize (NETAPI32.@) + */ +NET_API_STATUS WINAPI NetApiBufferSize(LPVOID Buffer, LPDWORD ByteCount) +{ + DWORD dw; + + TRACE("(%p, %p)\n", Buffer, ByteCount); + if (Buffer == NULL) + return ERROR_INVALID_PARAMETER; + dw = HeapSize(GetProcessHeap(), 0, Buffer); + TRACE("size: %ld\n", dw); + if (dw != 0xFFFFFFFF) + *ByteCount = dw; + else + *ByteCount = 0; + + return NERR_Success; +} diff --git a/reactos/dll/netapi32/browsr.c b/reactos/dll/netapi32/browsr.c new file mode 100644 index 00000000000..b0ed893a749 --- /dev/null +++ b/reactos/dll/netapi32/browsr.c @@ -0,0 +1,62 @@ +/* + * Copyright 2002 Andriy Palamarchuk + * + * netapi32 browser functions + * + * 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 + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "lmcons.h" +#include "lmbrowsr.h" +#include "lmshare.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(netapi32); + +/************************************************************ + * I_BrowserSetNetlogonState (NETAPI32.@) + */ +NET_API_STATUS WINAPI I_BrowserSetNetlogonState( + LPWSTR ServerName, LPWSTR DomainName, LPWSTR EmulatedServerName, + DWORD Role) +{ + return ERROR_NOT_SUPPORTED; +} + +/************************************************************ + * I_BrowserQueryEmulatedDomains (NETAPI32.@) + */ +NET_API_STATUS WINAPI I_BrowserQueryEmulatedDomains( + LPWSTR ServerName, PBROWSER_EMULATED_DOMAIN *EmulatedDomains, + LPDWORD EntriesRead) +{ + return ERROR_NOT_SUPPORTED; +} + +/************************************************************ + * NetShareEnum (NETAPI32.@) + */ +NET_API_STATUS WINAPI NetShareEnum( LPWSTR servername, DWORD level, LPBYTE* bufptr, + DWORD prefmaxlen, LPDWORD entriesread, LPDWORD totalentries, LPDWORD resume_handle) +{ + FIXME("%s %ld %p %ld %p %p %p\n", debugstr_w(servername), level, bufptr, + prefmaxlen, entriesread, totalentries, resume_handle); + return ERROR_NOT_SUPPORTED; +} diff --git a/reactos/dll/netapi32/ds.c b/reactos/dll/netapi32/ds.c new file mode 100644 index 00000000000..15582778c4c --- /dev/null +++ b/reactos/dll/netapi32/ds.c @@ -0,0 +1,70 @@ +/* + * Copyright 2005 Paul Vriens + * + * netapi32 directory service functions + * + * 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 + +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "wine/debug.h" +#include "dsrole.h" + +WINE_DEFAULT_DEBUG_CHANNEL(ds); + +/************************************************************ + * DsRoleFreeMemory (NETAPI32.@) + * + * PARAMS + * Buffer [I] Pointer to the to-be-freed buffer. + * + * RETURNS + * Nothing + */ +VOID WINAPI DsRoleFreeMemory(PVOID Buffer) +{ + FIXME("(%p) stub\n", Buffer); +} + +/************************************************************ + * DsRoleGetPrimaryDomainInformation (NETAPI32.@) + * + * PARAMS + * lpServer [I] Pointer to UNICODE string with Computername + * InfoLevel [I] Type of data to retrieve + * Buffer [O] Pointer to to the requested data + * + * RETURNS + * + * NOTES + * When lpServer is NULL, use the local computer + */ +DWORD WINAPI DsRoleGetPrimaryDomainInformation( + LPCWSTR lpServer, DSROLE_PRIMARY_DOMAIN_INFO_LEVEL InfoLevel, + PBYTE* Buffer) +{ + FIXME("(%p, %d, %p) stub\n", lpServer, InfoLevel, Buffer); + + /* Check some input parameters */ + + if (!Buffer) return ERROR_INVALID_PARAMETER; + if ((InfoLevel < DsRolePrimaryDomainInfoBasic) || (InfoLevel > DsRoleOperationState)) return ERROR_INVALID_PARAMETER; + + return E_NOTIMPL; +} diff --git a/reactos/dll/netapi32/nbcmdqueue.c b/reactos/dll/netapi32/nbcmdqueue.c new file mode 100644 index 00000000000..d8948f79e3d --- /dev/null +++ b/reactos/dll/netapi32/nbcmdqueue.c @@ -0,0 +1,199 @@ +/* Copyright (c) 2003 Juan Lang + * + * 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 "config.h" +#include "wine/debug.h" +#include "nbcmdqueue.h" + +WINE_DEFAULT_DEBUG_CHANNEL(netbios); + +struct NBCmdQueue +{ + HANDLE heap; + CRITICAL_SECTION cs; + PNCB head; +}; + +#define CANCEL_EVENT_PTR(ncb) (PHANDLE)((ncb)->ncb_reserve) +#define NEXT_PTR(ncb) (PNCB *)((ncb)->ncb_reserve + sizeof(HANDLE)) + +/* The reserved area of an ncb will be used for the following data: + * - a cancelled flag (BOOL, 4 bytes??) + * - a handle to an event that's set by a cancelled command on completion + * (HANDLE, 4 bytes) + * These members are used in the following way + * - on cancel, set the event member of the reserved field (with create event) + * - NBCmdComplete will delete the ncb from the queue of there's no event; + * otherwise it will set the event and not delete the ncb + * - cancel must lock the queue before finding the ncb in it, and can unlock it + * once it's set the event (and the cancelled flag) + * - NBCmdComplete must lock the queue before attempting to remove the ncb or + * check the event + * - NBCmdQueueCancelAll will lock the queue, and cancel all ncb's in the queue. + * It'll then unlock the queue, and wait on the event in the head of the queue + * until there's no more ncb's in the queue. + * Space optimization: use the handle as a boolean. NULL == 0 => not cancelled. + * Non-NULL == valid handle => cancelled. This allows storing a next pointer + * in the ncb's reserved field as well, avoiding a memory alloc for a new + * command (cool). + */ + +struct NBCmdQueue *NBCmdQueueCreate(HANDLE heap) +{ + struct NBCmdQueue *queue; + + if (heap == NULL) + heap = GetProcessHeap(); + queue = HeapAlloc(heap, 0, sizeof(struct NBCmdQueue)); + if (queue) + { + queue->heap = heap; + InitializeCriticalSection(&queue->cs); + queue->head = NULL; + } + return queue; +} + +UCHAR NBCmdQueueAdd(struct NBCmdQueue *queue, PNCB ncb) +{ + UCHAR ret; + + TRACE(": queue %p, ncb %p\n", queue, ncb); + + if (!queue) + return NRC_BADDR; + if (!ncb) + return NRC_INVADDRESS; + + *CANCEL_EVENT_PTR(ncb) = NULL; + EnterCriticalSection(&queue->cs); + *NEXT_PTR(ncb) = queue->head; + queue->head = ncb; + ret = NRC_GOODRET; + LeaveCriticalSection(&queue->cs); + TRACE("returning 0x%02x\n", ret); + return ret; +} + +static PNCB *NBCmdQueueFindNBC(struct NBCmdQueue *queue, PNCB ncb) +{ + PNCB *ret; + + if (!queue || !ncb) + ret = NULL; + else + { + ret = &queue->head; + while (ret && *ret != ncb) + ret = NEXT_PTR(*ret); + } + return ret; +} + +UCHAR NBCmdQueueCancel(struct NBCmdQueue *queue, PNCB ncb) +{ + UCHAR ret; + PNCB *spot; + + TRACE(": queue %p, ncb %p\n", queue, ncb); + + if (!queue) + return NRC_BADDR; + if (!ncb) + return NRC_INVADDRESS; + + EnterCriticalSection(&queue->cs); + spot = NBCmdQueueFindNBC(queue, ncb); + if (spot) + { + *CANCEL_EVENT_PTR(*spot) = CreateEventW(NULL, FALSE, FALSE, NULL); + WaitForSingleObject(*CANCEL_EVENT_PTR(*spot), INFINITE); + CloseHandle(*CANCEL_EVENT_PTR(*spot)); + *spot = *NEXT_PTR(*spot); + if (ncb->ncb_retcode == NRC_CMDCAN) + ret = NRC_CMDCAN; + else + ret = NRC_CANOCCR; + } + else + ret = NRC_INVADDRESS; + LeaveCriticalSection(&queue->cs); + TRACE("returning 0x%02x\n", ret); + return ret; +} + +UCHAR NBCmdQueueComplete(struct NBCmdQueue *queue, PNCB ncb, UCHAR retcode) +{ + UCHAR ret; + PNCB *spot; + + TRACE(": queue %p, ncb %p\n", queue, ncb); + + if (!queue) + return NRC_BADDR; + if (!ncb) + return NRC_INVADDRESS; + + EnterCriticalSection(&queue->cs); + spot = NBCmdQueueFindNBC(queue, ncb); + if (spot) + { + if (*CANCEL_EVENT_PTR(*spot)) + SetEvent(*CANCEL_EVENT_PTR(*spot)); + else + *spot = *NEXT_PTR(*spot); + ret = NRC_GOODRET; + } + else + ret = NRC_INVADDRESS; + LeaveCriticalSection(&queue->cs); + TRACE("returning 0x%02x\n", ret); + return ret; +} + +UCHAR NBCmdQueueCancelAll(struct NBCmdQueue *queue) +{ + UCHAR ret; + + TRACE(": queue %p\n", queue); + + if (!queue) + return NRC_BADDR; + + EnterCriticalSection(&queue->cs); + while (queue->head) + { + TRACE(": waiting for ncb %p (command 0x%02x)\n", queue->head, + queue->head->ncb_command); + NBCmdQueueCancel(queue, queue->head); + } + LeaveCriticalSection(&queue->cs); + ret = NRC_GOODRET; + TRACE("returning 0x%02x\n", ret); + return ret; +} + +void NBCmdQueueDestroy(struct NBCmdQueue *queue) +{ + TRACE(": queue %p\n", queue); + + if (queue) + { + NBCmdQueueCancelAll(queue); + DeleteCriticalSection(&queue->cs); + HeapFree(queue->heap, 0, queue); + } +} diff --git a/reactos/dll/netapi32/nbcmdqueue.h b/reactos/dll/netapi32/nbcmdqueue.h new file mode 100644 index 00000000000..f06f95b539e --- /dev/null +++ b/reactos/dll/netapi32/nbcmdqueue.h @@ -0,0 +1,66 @@ +/* Copyright (c) 2003 Juan Lang + * + * 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 + */ +#ifndef __NBCMDQUEUE_H__ +#define __NBCMDQUEUE_H__ + +#include +#include "windef.h" +#include "winbase.h" +#include "nb30.h" + +/* This file defines a queue of pending NetBIOS commands. The queue operations + * are thread safe, with the exception of NBCmdQueueDestroy: ensure no other + * threads are manipulating the queue when calling NBCmdQueueDestroy. + */ + +struct NBCmdQueue; + +/* Allocates a new command queue from heap. */ +struct NBCmdQueue *NBCmdQueueCreate(HANDLE heap); + +/* Adds ncb to queue. Assumes queue is not NULL, and ncb is not already in the + * queue. If ncb is already in the queue, returns NRC_TOOMANY. + */ +UCHAR NBCmdQueueAdd(struct NBCmdQueue *queue, PNCB ncb); + +/* Cancels the given ncb. Blocks until the command completes. Implicitly + * removes ncb from the queue. Assumes queue and ncb are not NULL, and that + * ncb has been added to queue previously. + * Returns NRC_CMDCAN on a successful cancellation, NRC_CMDOCCR if the command + * completed before it could be cancelled, and various other return values for + * different failures. + */ +UCHAR NBCmdQueueCancel(struct NBCmdQueue *queue, PNCB ncb); + +/* Sets the return code of the given ncb, and implicitly removes the command + * from the queue. Assumes queue and ncb are not NULL, and that ncb has been + * added to queue previously. + * Returns NRC_GOODRET on success. + */ +UCHAR NBCmdQueueComplete(struct NBCmdQueue *queue, PNCB ncb, UCHAR retcode); + +/* Cancels all pending commands in the queue (useful for a RESET or a shutdown). + * Returns when all commands have been completed. + */ +UCHAR NBCmdQueueCancelAll(struct NBCmdQueue *queue); + +/* Frees all memory associated with the queue. Blocks until all commands + * pending in the queue have been completed. + */ +void NBCmdQueueDestroy(struct NBCmdQueue *queue); + +#endif /* __NBCMDQUEUE_H__ */ diff --git a/reactos/dll/netapi32/nbnamecache.c b/reactos/dll/netapi32/nbnamecache.c new file mode 100644 index 00000000000..1fdb70553d4 --- /dev/null +++ b/reactos/dll/netapi32/nbnamecache.c @@ -0,0 +1,215 @@ +/* Copyright (c) 2003 Juan Lang + * + * 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 + * + * This implementation uses a linked list, because I don't have a decent + * hash table implementation handy. This is somewhat inefficient, but it's + * rather more efficient than not having a name cache at all. + */ + +#include "config.h" +#include "wine/port.h" +#include "wine/debug.h" + +#include "nbnamecache.h" + +WINE_DEFAULT_DEBUG_CHANNEL(netbios); + +typedef struct _NBNameCacheNode +{ + DWORD expireTime; + NBNameCacheEntry *entry; + struct _NBNameCacheNode *next; +} NBNameCacheNode; + +struct NBNameCache +{ + HANDLE heap; + CRITICAL_SECTION cs; + DWORD entryExpireTimeMS; + NBNameCacheNode *head; +}; + +/* Unlinks the node pointed to by *prev, and frees any associated memory. + * If that node's next pointed to another node, *prev now points to it. + * Assumes the caller owns cache's lock. + */ +static void NBNameCacheUnlinkNode(struct NBNameCache *cache, + NBNameCacheNode **prev) +{ + if (cache && prev && *prev) + { + NBNameCacheNode *next = (*prev)->next; + + HeapFree(cache->heap, 0, (*prev)->entry); + HeapFree(cache->heap, 0, *prev); + *prev = next; + } +} + +/* Walks the list beginning with cache->head looking for the node with name + * name. If the node is found, returns a pointer to the next pointer of the + * node _prior_ to the found node (or head if head points to it). Thus, if the + * node's all you want, dereference the return value twice. If you want to + * modify the list, modify the referent of the return value. + * While it's at it, deletes nodes whose time has expired (except the node + * you're looking for, of course). + * Returns NULL if the node isn't found. + * Assumes the caller owns cache's lock. + */ +static NBNameCacheNode **NBNameCacheWalk(struct NBNameCache *cache, + const char name[NCBNAMSZ]) +{ + NBNameCacheNode **ret = NULL; + + if (cache && cache->head) + { + NBNameCacheNode **ptr; + + ptr = &cache->head; + while (ptr && *ptr && (*ptr)->entry) + { + if (!memcmp((*ptr)->entry->name, name, NCBNAMSZ - 1)) + ret = ptr; + else + { + if (GetTickCount() > (*ptr)->expireTime) + NBNameCacheUnlinkNode(cache, ptr); + } + if (*ptr) + ptr = &(*ptr)->next; + } + } + return ret; +} + +struct NBNameCache *NBNameCacheCreate(HANDLE heap, DWORD entryExpireTimeMS) +{ + struct NBNameCache *cache; + + + if (!heap) + heap = GetProcessHeap(); + cache = HeapAlloc(heap, 0, sizeof(struct NBNameCache)); + if (cache) + { + cache->heap = heap; + InitializeCriticalSection(&cache->cs); + cache->entryExpireTimeMS = entryExpireTimeMS; + cache->head = NULL; + } + return cache; +} + +BOOL NBNameCacheAddEntry(struct NBNameCache *cache, NBNameCacheEntry *entry) +{ + BOOL ret; + + if (cache && entry) + { + NBNameCacheNode **node; + + EnterCriticalSection(&cache->cs); + node = NBNameCacheWalk(cache, (char*)entry->name); + if (node) + { + (*node)->expireTime = GetTickCount() + + cache->entryExpireTimeMS; + HeapFree(cache->heap, 0, (*node)->entry); + (*node)->entry = entry; + ret = TRUE; + } + else + { + NBNameCacheNode *newNode = HeapAlloc(cache->heap, 0, sizeof(NBNameCacheNode)); + if (newNode) + { + newNode->expireTime = GetTickCount() + + cache->entryExpireTimeMS; + newNode->entry = entry; + newNode->next = cache->head; + cache->head = newNode; + ret = TRUE; + } + else + ret = FALSE; + } + LeaveCriticalSection(&cache->cs); + } + else + ret = FALSE; + return ret; +} + +const NBNameCacheEntry *NBNameCacheFindEntry(struct NBNameCache *cache, + const UCHAR name[NCBNAMSZ]) +{ + const NBNameCacheEntry *ret; + UCHAR printName[NCBNAMSZ]; + + memcpy(printName, name, NCBNAMSZ - 1); + printName[NCBNAMSZ - 1] = '\0'; + if (cache) + { + NBNameCacheNode **node; + + EnterCriticalSection(&cache->cs); + node = NBNameCacheWalk(cache, (char*)name); + if (node) + ret = (*node)->entry; + else + ret = NULL; + LeaveCriticalSection(&cache->cs); + } + else + ret = NULL; + return ret; +} + +BOOL NBNameCacheUpdateNBName(struct NBNameCache *cache, + const UCHAR name[NCBNAMSZ], const UCHAR nbname[NCBNAMSZ]) +{ + BOOL ret; + + if (cache) + { + NBNameCacheNode **node; + + EnterCriticalSection(&cache->cs); + node = NBNameCacheWalk(cache, (char*)name); + if (node && *node && (*node)->entry) + { + memcpy((*node)->entry->nbname, nbname, NCBNAMSZ); + ret = TRUE; + } + else + ret = FALSE; + LeaveCriticalSection(&cache->cs); + } + else + ret = FALSE; + return ret; +} + +void NBNameCacheDestroy(struct NBNameCache *cache) +{ + if (cache) + { + DeleteCriticalSection(&cache->cs); + while (cache->head) + NBNameCacheUnlinkNode(cache, &cache->head); + HeapFree(cache->heap, 0, cache); + } +} diff --git a/reactos/dll/netapi32/nbnamecache.h b/reactos/dll/netapi32/nbnamecache.h new file mode 100644 index 00000000000..04e366314ee --- /dev/null +++ b/reactos/dll/netapi32/nbnamecache.h @@ -0,0 +1,82 @@ +/* Copyright (c) 2003 Juan Lang + * + * 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 + */ +#ifndef __WINE_NBNAMECACHE_H +#define __WINE_NBNAMECACHE_H + +#include + +#include "windef.h" +#include "winbase.h" +#include "nb30.h" + +struct NBNameCache; + +/* Represents an entry in the name cache. If the NetBIOS name is known, it's + * in nbname. Otherwise, nbname begins with '*'. numAddresses defines the + * number of addresses in addresses. + * Notice that it allows multiple addresses per name, but doesn't explicitly + * allow group names. That's because all names so far are unique; if a use for + * group names comes up, adding a flag here is simple enough. + * Also, only the first NCBNAMSZ - 1 bytes are considered significant. This is + * because a name may have been resolved using DNS, and the suffix byte is + * always truncated for DNS lookups. + */ +typedef struct _NBNameCacheEntry +{ + UCHAR name[NCBNAMSZ]; + UCHAR nbname[NCBNAMSZ]; + DWORD numAddresses; + DWORD addresses[1]; +} NBNameCacheEntry; + +/* Functions that create, manipulate, and destroy a name cache. Thread-safe, + * with the exception of NBNameCacheDestroy--ensure that no other threads are + * manipulating the cache before destoying it. + */ + +/* Allocates a new name cache from heap, and sets the expire time on new + * entries to entryExpireTimeMS after a cache entry is added. + */ +struct NBNameCache *NBNameCacheCreate(HANDLE heap, DWORD entryExpireTimeMS); + +/* Adds an entry to the cache. The entry is assumed to have been allocated + * from the same heap as the name cache; the name cache will own the entry + * from now on. The entry's expire time is initialized at this time to + * entryExpireTimeMS + the current time in MS. If an existing entry with the + * same name was in the cache, the entry is replaced. Returns TRUE on success + * or FALSE on failure. + */ +BOOL NBNameCacheAddEntry(struct NBNameCache *cache, NBNameCacheEntry *entry); + +/* Finds the entry with name name in the cache and returns a pointer to it, or + * NULL if it isn't found. + */ +const NBNameCacheEntry *NBNameCacheFindEntry(struct NBNameCache *cache, + const UCHAR name[NCBNAMSZ]); + +/* If the entry with name name is in the cache, updates its nbname member to + * nbname. The entry's expire time is implicitly updated to entryExpireTimeMS + * + the current time in MS, since getting the NetBIOS name meant validating + * the name and address anyway. + * Returns TRUE on success or FALSE on failure. + */ +BOOL NBNameCacheUpdateNBName(struct NBNameCache *cache, + const UCHAR name[NCBNAMSZ], const UCHAR nbname[NCBNAMSZ]); + +void NBNameCacheDestroy(struct NBNameCache *cache); + +#endif /* ndef __WINE_NBNAMECACHE_H */ diff --git a/reactos/dll/netapi32/nbt.c b/reactos/dll/netapi32/nbt.c new file mode 100644 index 00000000000..3125df0c592 --- /dev/null +++ b/reactos/dll/netapi32/nbt.c @@ -0,0 +1,1553 @@ +/* Copyright (c) 2003 Juan Lang + * + * 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 + * + * I am heavily indebted to Chris Hertel's excellent Implementing CIFS, + * http://ubiqx.org/cifs/ , for whatever understanding I have of NBT. + * I also stole from Mike McCormack's smb.c and netapi32.c, although little of + * that code remains. + * Lack of understanding and bugs are my fault. + * + * FIXME: + * - Of the NetBIOS session functions, only client functions are supported, and + * it's likely they'll be the only functions supported. NBT requires session + * servers to listen on TCP/139. This requires root privilege, and Samba is + * likely to be listening here already. This further restricts NetBIOS + * applications, both explicit users and implicit ones: CreateNamedPipe + * won't actually create a listening pipe, for example, so applications can't + * act as RPC servers using a named pipe protocol binding, DCOM won't be able + * to support callbacks or servers over the named pipe protocol, etc. + * + * - Datagram support is omitted for the same reason. To send a NetBIOS + * datagram, you must include the NetBIOS name by which your application is + * known. This requires you to have registered the name previously, and be + * able to act as a NetBIOS datagram server (listening on UDP/138). + * + * - Name registration functions are omitted for the same reason--registering a + * name requires you to be able to defend it, and this means listening on + * UDP/137. + * Win98 requires you either use your computer's NetBIOS name (with the NULL + * suffix byte) as the calling name when creating a session, or to register + * a new name before creating one: it disallows '*' as the calling name. + * Win2K initially starts with an empty name table, and doesn't allow you to + * use the machine's NetBIOS name (with the NULL suffix byte) as the calling + * name. Although it allows sessions to be created with '*' as the calling + * name, doing so results in timeouts for all receives, because the + * application never gets them. + * So, a well-behaved Netbios application will typically want to register a + * name. I should probably support a do-nothing name list that allows + * NCBADDNAME to add to it, but doesn't actually register the name, or does + * attempt to register it without being able to defend it. + * + * - Name lookups may not behave quite as you'd expect/like if you have + * multiple LANAs. If a name is resolvable through DNS, or if you're using + * WINS, it'll resolve on _any_ LANA. So, a Call will succeed on any LANA as + * well. + * I'm not sure how Windows behaves in this case. I could try to force + * lookups to the correct adapter by using one of the GetPreferred* + * functions, but with the possibility of multiple adapters in the same + * same subnet, there's no guarantee that what IpHlpApi thinks is the + * preferred adapter will actually be a LANA. (It's highly probable because + * this is an unusual configuration, but not guaranteed.) + * + * See also other FIXMEs in the code. + */ + +#include "config.h" +#include + +#include "winsock2.h" +#include "windef.h" +#include "winbase.h" +#include "wine/debug.h" +#include "winreg.h" +#include "iphlpapi.h" + +#include "netbios.h" +#include "nbnamecache.h" + +WINE_DEFAULT_DEBUG_CHANNEL(netbios); + +#define PORT_NBNS 137 +#define PORT_NBDG 138 +#define PORT_NBSS 139 + +#ifndef INADDR_NONE +#define INADDR_NONE ~0UL +#endif + +#define NBR_ADDWORD(p,word) (*(WORD *)(p)) = htons(word) +#define NBR_GETWORD(p) ntohs(*(WORD *)(p)) + +#define MIN_QUERIES 1 +#define MAX_QUERIES 0xffff +#define MIN_QUERY_TIMEOUT 100 +#define MAX_QUERY_TIMEOUT 0xffffffff +#define BCAST_QUERIES 3 +#define BCAST_QUERY_TIMEOUT 750 +#define WINS_QUERIES 3 +#define WINS_QUERY_TIMEOUT 750 +#define MAX_WINS_SERVERS 2 +#define MIN_CACHE_TIMEOUT 60000 +#define CACHE_TIMEOUT 360000 + +#define MAX_NBT_NAME_SZ (NCBNAMSZ * 2 + MAX_DOMAIN_NAME_LEN + 2) +#define SIMPLE_NAME_QUERY_PKT_SIZE 26 + MAX_NBT_NAME_SZ + +#define DEFAULT_NBT_SESSIONS 16 + +#define NBNS_TYPE_NB 0x0020 +#define NBNS_TYPE_NBSTAT 0x0021 +#define NBNS_CLASS_INTERNET 0x00001 +#define NBNS_HEADER_SIZE (sizeof(WORD) * 6) +#define NBNS_RESPONSE_AND_OPCODE 0xf800 +#define NBNS_RESPONSE_AND_QUERY 0x8000 +#define NBNS_REPLYCODE 0x0f + +#define NBSS_HDRSIZE 4 + +#define NBSS_MSG 0x00 +#define NBSS_REQ 0x81 +#define NBSS_ACK 0x82 +#define NBSS_NACK 0x83 +#define NBSS_RETARGET 0x84 +#define NBSS_KEEPALIVE 0x85 + +#define NBSS_ERR_NOT_LISTENING_ON_NAME 0x80 +#define NBSS_ERR_NOT_LISTENING_FOR_CALLER 0x81 +#define NBSS_ERR_BAD_NAME 0x82 +#define NBSS_ERR_INSUFFICIENT_RESOURCES 0x83 + +#define NBSS_EXTENSION 0x01 + +typedef struct _NetBTSession +{ + CRITICAL_SECTION cs; + SOCKET fd; + DWORD bytesPending; +} NetBTSession; + +typedef struct _NetBTAdapter +{ + MIB_IPADDRROW ipr; + WORD nameQueryXID; + struct NBNameCache *nameCache; + DWORD xmit_success; + DWORD recv_success; +} NetBTAdapter; + +static ULONG gTransportID; +static BOOL gEnableDNS; +static DWORD gBCastQueries; +static DWORD gBCastQueryTimeout; +static DWORD gWINSQueries; +static DWORD gWINSQueryTimeout; +static DWORD gWINSServers[MAX_WINS_SERVERS]; +static int gNumWINSServers; +static char gScopeID[MAX_DOMAIN_NAME_LEN]; +static DWORD gCacheTimeout; +static struct NBNameCache *gNameCache; + +/* Converts from a NetBIOS name into a Second Level Encoding-formatted name. + * Assumes p is not NULL and is either NULL terminated or has at most NCBNAMSZ + * bytes, and buffer has at least MAX_NBT_NAME_SZ bytes. Pads with space bytes + * if p is NULL-terminated. Returns the number of bytes stored in buffer. + */ +static int NetBTNameEncode(const UCHAR *p, UCHAR *buffer) +{ + int i,len=0; + + if (!p) return 0; + if (!buffer) return 0; + + buffer[len++] = NCBNAMSZ * 2; + for (i = 0; p[i] && i < NCBNAMSZ; i++) + { + buffer[len++] = ((p[i] & 0xf0) >> 4) + 'A'; + buffer[len++] = (p[i] & 0x0f) + 'A'; + } + while (len < NCBNAMSZ * 2) + { + buffer[len++] = 'C'; + buffer[len++] = 'A'; + } + if (*gScopeID) + { + int scopeIDLen = strlen(gScopeID); + + memcpy(buffer + len, gScopeID, scopeIDLen); + len += scopeIDLen; + } + buffer[len++] = 0; /* add second terminator */ + return len; +} + +/* Creates a NBT name request packet for name in buffer. If broadcast is true, + * creates a broadcast request, otherwise creates a unicast request. + * Returns the number of bytes stored in buffer. + */ +static DWORD NetBTNameReq(const UCHAR name[NCBNAMSZ], WORD xid, WORD qtype, + BOOL broadcast, UCHAR *buffer, int len) +{ + int i = 0; + + if (len < SIMPLE_NAME_QUERY_PKT_SIZE) return 0; + + NBR_ADDWORD(&buffer[i],xid); i+=2; /* transaction */ + if (broadcast) + { + NBR_ADDWORD(&buffer[i],0x0110); /* flags: r=req,op=query,rd=1,b=1 */ + i+=2; + } + else + { + NBR_ADDWORD(&buffer[i],0x0100); /* flags: r=req,op=query,rd=1,b=0 */ + i+=2; + } + NBR_ADDWORD(&buffer[i],0x0001); i+=2; /* one name query */ + NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero answers */ + NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero authorities */ + NBR_ADDWORD(&buffer[i],0x0000); i+=2; /* zero additional */ + + i += NetBTNameEncode(name, &buffer[i]); + + NBR_ADDWORD(&buffer[i],qtype); i+=2; + NBR_ADDWORD(&buffer[i],NBNS_CLASS_INTERNET); i+=2; + + return i; +} + +/* Sends a name query request for name on fd to destAddr. Sets SO_BROADCAST on + * fd if broadcast is TRUE. Assumes fd is not INVALID_SOCKET, and name is not + * NULL. + * Returns 0 on success, -1 on failure. + */ +static int NetBTSendNameQuery(SOCKET fd, const UCHAR name[NCBNAMSZ], WORD xid, + WORD qtype, DWORD destAddr, BOOL broadcast) +{ + int ret = 0, on = 1; + struct in_addr addr; + + addr.s_addr = destAddr; + TRACE("name %s, dest addr %s\n", name, inet_ntoa(addr)); + + if (broadcast) + ret = setsockopt(fd, SOL_SOCKET, SO_BROADCAST, (const char*)&on, sizeof(on)); + if(ret == 0) + { + WSABUF wsaBuf; + UCHAR buf[SIMPLE_NAME_QUERY_PKT_SIZE]; + struct sockaddr_in sin; + + memset(&sin, 0, sizeof(sin)); + sin.sin_addr.s_addr = destAddr; + sin.sin_family = AF_INET; + sin.sin_port = htons(PORT_NBNS); + + wsaBuf.buf = (CHAR*)buf; + wsaBuf.len = NetBTNameReq(name, xid, qtype, broadcast, buf, + sizeof(buf)); + if (wsaBuf.len > 0) + { + DWORD bytesSent; + + ret = WSASendTo(fd, &wsaBuf, 1, &bytesSent, 0, + (struct sockaddr*)&sin, sizeof(sin), NULL, NULL); + if (ret < 0 || bytesSent < wsaBuf.len) + ret = -1; + else + ret = 0; + } + else + ret = -1; + } + return ret; +} + +typedef BOOL (*NetBTAnswerCallback)(void *data, WORD answerCount, + WORD answerIndex, PUCHAR rData, WORD rdLength); + +/* Waits on fd until GetTickCount() returns a value greater than or equal to + * waitUntil for a name service response. If a name response matching xid + * is received, calls answerCallback once for each answer resource record in + * the response. (The callback's answerCount will be the total number of + * answers to expect, and answerIndex will be the 0-based index that's being + * sent this time.) Quits parsing if answerCallback returns FALSE. + * Returns NRC_GOODRET on timeout or a valid response received, something else + * on error. + */ +static UCHAR NetBTWaitForNameResponse(NetBTAdapter *adapter, SOCKET fd, + DWORD waitUntil, NetBTAnswerCallback answerCallback, void *data) +{ + BOOL found = FALSE; + DWORD now; + UCHAR ret = NRC_GOODRET; + + if (!adapter) return NRC_BADDR; + if (fd == INVALID_SOCKET) return NRC_BADDR; + if (!answerCallback) return NRC_BADDR; + + while (!found && ret == NRC_GOODRET && (now = GetTickCount()) < waitUntil) + { + DWORD msToWait = waitUntil - now; + struct fd_set fds; + struct timeval timeout = { msToWait / 1000, msToWait % 1000 }; + int r; + + FD_ZERO(&fds); + FD_SET(fd, &fds); + r = select(fd + 1, &fds, NULL, NULL, &timeout); + if (r < 0) + ret = NRC_SYSTEM; + else if (r == 1) + { + /* FIXME: magic #, is this always enough? */ + UCHAR buffer[256]; + int fromsize; + struct sockaddr_in fromaddr; + WORD respXID, flags, queryCount, answerCount; + WSABUF wsaBuf = { sizeof(buffer), (CHAR*)buffer }; + DWORD bytesReceived, recvFlags = 0; + + fromsize = sizeof(fromaddr); + r = WSARecvFrom(fd, &wsaBuf, 1, &bytesReceived, &recvFlags, + (struct sockaddr*)&fromaddr, &fromsize, NULL, NULL); + if(r < 0) + { + ret = NRC_SYSTEM; + break; + } + + if (bytesReceived < NBNS_HEADER_SIZE) + continue; + + respXID = NBR_GETWORD(buffer); + if (adapter->nameQueryXID != respXID) + continue; + + flags = NBR_GETWORD(buffer + 2); + queryCount = NBR_GETWORD(buffer + 4); + answerCount = NBR_GETWORD(buffer + 6); + + /* a reply shouldn't contain a query, ignore bad packet */ + if (queryCount > 0) + continue; + + if ((flags & NBNS_RESPONSE_AND_OPCODE) == NBNS_RESPONSE_AND_QUERY) + { + if ((flags & NBNS_REPLYCODE) != 0) + ret = NRC_NAMERR; + else if ((flags & NBNS_REPLYCODE) == 0 && answerCount > 0) + { + PUCHAR ptr = buffer + NBNS_HEADER_SIZE; + BOOL shouldContinue = TRUE; + WORD answerIndex = 0; + + found = TRUE; + /* decode one answer at a time */ + while (ret == NRC_GOODRET && answerIndex < answerCount && + ptr - buffer < bytesReceived && shouldContinue) + { + WORD rLen; + + /* scan past name */ + for (; ptr[0] && ptr - buffer < bytesReceived; ) + ptr += ptr[0] + 1; + ptr++; + ptr += 2; /* scan past type */ + if (ptr - buffer < bytesReceived && ret == NRC_GOODRET + && NBR_GETWORD(ptr) == NBNS_CLASS_INTERNET) + ptr += sizeof(WORD); + else + ret = NRC_SYSTEM; /* parse error */ + ptr += sizeof(DWORD); /* TTL */ + rLen = NBR_GETWORD(ptr); + rLen = min(rLen, bytesReceived - (ptr - buffer)); + ptr += sizeof(WORD); + shouldContinue = answerCallback(data, answerCount, + answerIndex, ptr, rLen); + ptr += rLen; + answerIndex++; + } + } + } + } + } + TRACE("returning 0x%02x\n", ret); + return ret; +} + +typedef struct _NetBTNameQueryData { + NBNameCacheEntry *cacheEntry; + UCHAR ret; +} NetBTNameQueryData; + +/* Name query callback function for NetBTWaitForNameResponse, creates a cache + * entry on the first answer, adds each address as it's called again (as long + * as there's space). If there's an error that should be propagated as the + * NetBIOS error, modifies queryData's ret member to the proper return code. + */ +static BOOL NetBTFindNameAnswerCallback(void *pVoid, WORD answerCount, + WORD answerIndex, PUCHAR rData, WORD rLen) +{ + NetBTNameQueryData *queryData = (NetBTNameQueryData *)pVoid; + BOOL ret; + + if (queryData) + { + if (queryData->cacheEntry == NULL) + { + queryData->cacheEntry = HeapAlloc( + GetProcessHeap(), 0, sizeof(NBNameCacheEntry) + + (answerCount - 1) * sizeof(DWORD)); + if (queryData->cacheEntry) + queryData->cacheEntry->numAddresses = 0; + else + { + ret = FALSE; + queryData->ret = NRC_OSRESNOTAV; + } + } + if (rLen == 6 && queryData->cacheEntry && + queryData->cacheEntry->numAddresses < answerCount) + { + queryData->cacheEntry->addresses[queryData->cacheEntry-> + numAddresses++] = *(PDWORD)(rData + 2); + ret = queryData->cacheEntry->numAddresses < answerCount; + } + else + ret = FALSE; + } + else + ret = FALSE; + return ret; +} + +/* Workhorse NetBT name lookup function. Sends a name lookup query for + * ncb->ncb_callname to sendTo, as a broadcast if broadcast is TRUE, using + * adapter->nameQueryXID as the transaction ID. Waits up to timeout + * milliseconds, and retries up to maxQueries times, waiting for a reply. + * If a valid response is received, stores the looked up addresses as a + * NBNameCacheEntry in *cacheEntry. + * Returns NRC_GOODRET on success, though this may not mean the name was + * resolved--check whether *cacheEntry is NULL. + */ +static UCHAR NetBTNameWaitLoop(NetBTAdapter *adapter, SOCKET fd, PNCB ncb, + DWORD sendTo, BOOL broadcast, DWORD timeout, DWORD maxQueries, + NBNameCacheEntry **cacheEntry) +{ + unsigned int queries; + NetBTNameQueryData queryData; + + if (!adapter) return NRC_BADDR; + if (fd == INVALID_SOCKET) return NRC_BADDR; + if (!ncb) return NRC_BADDR; + if (!cacheEntry) return NRC_BADDR; + + queryData.cacheEntry = NULL; + queryData.ret = NRC_GOODRET; + for (queries = 0; queryData.cacheEntry == NULL && queries < maxQueries; + queries++) + { + if (!NCB_CANCELLED(ncb)) + { + int r = NetBTSendNameQuery(fd, ncb->ncb_callname, + adapter->nameQueryXID, NBNS_TYPE_NB, sendTo, broadcast); + + if (r == 0) + queryData.ret = NetBTWaitForNameResponse(adapter, fd, + GetTickCount() + timeout, NetBTFindNameAnswerCallback, + &queryData); + else + queryData.ret = NRC_SYSTEM; + } + else + queryData.ret = NRC_CMDCAN; + } + if (queryData.cacheEntry) + { + memcpy(queryData.cacheEntry->name, ncb->ncb_callname, NCBNAMSZ); + memcpy(queryData.cacheEntry->nbname, ncb->ncb_callname, NCBNAMSZ); + } + *cacheEntry = queryData.cacheEntry; + return queryData.ret; +} + +/* Attempts to add cacheEntry to the name cache in *nameCache; if *nameCache + * has not yet been created, creates it, using gCacheTimeout as the cache + * entry timeout. If memory allocation fails, or if NBNameCacheAddEntry fails, + * frees cacheEntry. + * Returns NRC_GOODRET on success, and something else on failure. + */ +static UCHAR NetBTStoreCacheEntry(struct NBNameCache **nameCache, + NBNameCacheEntry *cacheEntry) +{ + UCHAR ret; + + if (!nameCache) return NRC_BADDR; + if (!cacheEntry) return NRC_BADDR; + + if (!*nameCache) + *nameCache = NBNameCacheCreate(GetProcessHeap(), gCacheTimeout); + if (*nameCache) + ret = NBNameCacheAddEntry(*nameCache, cacheEntry) + ? NRC_GOODRET : NRC_OSRESNOTAV; + else + { + HeapFree(GetProcessHeap(), 0, cacheEntry); + ret = NRC_OSRESNOTAV; + } + return ret; +} + +/* Attempts to resolve name using inet_addr(), then gethostbyname() if + * gEnableDNS is TRUE, if the suffix byte is either <00> or <20>. If the name + * can be looked up, returns 0 and stores the looked up addresses as a + * NBNameCacheEntry in *cacheEntry. + * Returns NRC_GOODRET on success, though this may not mean the name was + * resolved--check whether *cacheEntry is NULL. Returns something else on + * error. + */ +static UCHAR NetBTinetResolve(const UCHAR name[NCBNAMSZ], + NBNameCacheEntry **cacheEntry) +{ + UCHAR ret = NRC_GOODRET; + + TRACE("name %s, cacheEntry %p\n", name, cacheEntry); + + if (!name) return NRC_BADDR; + if (!cacheEntry) return NRC_BADDR; + + if (isalnum(name[0]) && (name[NCBNAMSZ - 1] == 0 || + name[NCBNAMSZ - 1] == 0x20)) + { + CHAR toLookup[NCBNAMSZ]; + unsigned int i; + + for (i = 0; i < NCBNAMSZ - 1 && name[i] && name[i] != ' '; i++) + toLookup[i] = name[i]; + toLookup[i] = '\0'; + + if (isdigit(toLookup[0])) + { + unsigned long addr = inet_addr(toLookup); + + if (addr != INADDR_NONE) + { + *cacheEntry = HeapAlloc(GetProcessHeap(), + 0, sizeof(NBNameCacheEntry)); + if (*cacheEntry) + { + memcpy((*cacheEntry)->name, name, NCBNAMSZ); + memset((*cacheEntry)->nbname, 0, NCBNAMSZ); + (*cacheEntry)->nbname[0] = '*'; + (*cacheEntry)->numAddresses = 1; + (*cacheEntry)->addresses[0] = addr; + } + else + ret = NRC_OSRESNOTAV; + } + } + if (gEnableDNS && ret == NRC_GOODRET && !*cacheEntry) + { + struct hostent *host; + + if ((host = gethostbyname(toLookup)) != NULL) + { + for (i = 0; ret == NRC_GOODRET && host->h_addr_list && + host->h_addr_list[i]; i++) + ; + if (host->h_addr_list && host->h_addr_list[0]) + { + *cacheEntry = HeapAlloc( + GetProcessHeap(), 0, sizeof(NBNameCacheEntry) + + (i - 1) * sizeof(DWORD)); + if (*cacheEntry) + { + memcpy((*cacheEntry)->name, name, NCBNAMSZ); + memset((*cacheEntry)->nbname, 0, NCBNAMSZ); + (*cacheEntry)->nbname[0] = '*'; + (*cacheEntry)->numAddresses = i; + for (i = 0; i < (*cacheEntry)->numAddresses; i++) + (*cacheEntry)->addresses[i] = + (DWORD)host->h_addr_list[i]; + } + else + ret = NRC_OSRESNOTAV; + } + } + } + } + + TRACE("returning 0x%02x\n", ret); + return ret; +} + +/* Looks up the name in ncb->ncb_callname, first in the name caches (global + * and this adapter's), then using gethostbyname(), next by WINS if configured, + * and finally using broadcast NetBT name resolution. In NBT parlance, this + * makes this an "H-node". Stores an entry in the appropriate name cache for a + * found node, and returns it as *cacheEntry. + * Assumes data, ncb, and cacheEntry are not NULL. + * Returns NRC_GOODRET on success--which doesn't mean the name was resolved, + * just that all name lookup operations completed successfully--and something + * else on failure. *cacheEntry will be NULL if the name was not found. + */ +static UCHAR NetBTInternalFindName(NetBTAdapter *adapter, PNCB ncb, + const NBNameCacheEntry **cacheEntry) +{ + UCHAR ret = NRC_GOODRET; + + TRACE("adapter %p, ncb %p, cacheEntry %p\n", adapter, ncb, cacheEntry); + + if (!cacheEntry) return NRC_BADDR; + *cacheEntry = NULL; + + if (!adapter) return NRC_BADDR; + if (!ncb) return NRC_BADDR; + + if (ncb->ncb_callname[0] == '*') + ret = NRC_NOWILD; + else + { + *cacheEntry = NBNameCacheFindEntry(gNameCache, ncb->ncb_callname); + if (!*cacheEntry) + *cacheEntry = NBNameCacheFindEntry(adapter->nameCache, + ncb->ncb_callname); + if (!*cacheEntry) + { + NBNameCacheEntry *newEntry = NULL; + + ret = NetBTinetResolve(ncb->ncb_callname, &newEntry); + if (ret == NRC_GOODRET && newEntry) + { + ret = NetBTStoreCacheEntry(&gNameCache, newEntry); + if (ret != NRC_GOODRET) + newEntry = NULL; + } + else + { + SOCKET fd = WSASocketA(PF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, + 0, WSA_FLAG_OVERLAPPED); + + if(fd == INVALID_SOCKET) + ret = NRC_OSRESNOTAV; + else + { + int winsNdx; + + adapter->nameQueryXID++; + for (winsNdx = 0; ret == NRC_GOODRET && *cacheEntry == NULL + && winsNdx < gNumWINSServers; winsNdx++) + ret = NetBTNameWaitLoop(adapter, fd, ncb, + gWINSServers[winsNdx], FALSE, gWINSQueryTimeout, + gWINSQueries, &newEntry); + if (ret == NRC_GOODRET && newEntry) + { + ret = NetBTStoreCacheEntry(&gNameCache, newEntry); + if (ret != NRC_GOODRET) + newEntry = NULL; + } + if (ret == NRC_GOODRET && *cacheEntry == NULL) + { + DWORD bcastAddr = + adapter->ipr.dwAddr & adapter->ipr.dwMask; + + if (adapter->ipr.dwBCastAddr) + bcastAddr |= ~adapter->ipr.dwMask; + ret = NetBTNameWaitLoop(adapter, fd, ncb, bcastAddr, + TRUE, gBCastQueryTimeout, gBCastQueries, &newEntry); + if (ret == NRC_GOODRET && newEntry) + { + ret = NetBTStoreCacheEntry(&adapter->nameCache, + newEntry); + if (ret != NRC_GOODRET) + newEntry = NULL; + } + } + closesocket(fd); + } + } + *cacheEntry = newEntry; + } + } + TRACE("returning 0x%02x\n", ret); + return ret; +} + +typedef struct _NetBTNodeQueryData +{ + BOOL gotResponse; + PADAPTER_STATUS astat; + WORD astatLen; +} NetBTNodeQueryData; + +/* Callback function for NetBTAstatRemote, parses the rData for the node + * status and name list of the remote node. Always returns FALSE, since + * there's never more than one answer we care about in a node status response. + */ +static BOOL NetBTNodeStatusAnswerCallback(void *pVoid, WORD answerCount, + WORD answerIndex, PUCHAR rData, WORD rLen) +{ + NetBTNodeQueryData *data = (NetBTNodeQueryData *)pVoid; + + if (data && !data->gotResponse && rData && rLen >= 1) + { + /* num names is first byte; each name is NCBNAMSZ + 2 bytes */ + if (rLen >= rData[0] * (NCBNAMSZ + 2)) + { + WORD i; + PUCHAR src; + PNAME_BUFFER dst; + + data->gotResponse = TRUE; + data->astat->name_count = rData[0]; + for (i = 0, src = rData + 1, + dst = (PNAME_BUFFER)((PUCHAR)data->astat + + sizeof(ADAPTER_STATUS)); + i < data->astat->name_count && src - rData < rLen && + (PUCHAR)dst - (PUCHAR)data->astat < data->astatLen; + i++, dst++, src += NCBNAMSZ + 2) + { + UCHAR flags = *(src + NCBNAMSZ); + + memcpy(dst->name, src, NCBNAMSZ); + /* we won't actually see a registering name in the returned + * response. It's useful to see if no other flags are set; if + * none are, then the name is registered. */ + dst->name_flags = REGISTERING; + if (flags & 0x80) + dst->name_flags |= GROUP_NAME; + if (flags & 0x10) + dst->name_flags |= DEREGISTERED; + if (flags & 0x08) + dst->name_flags |= DUPLICATE; + if (dst->name_flags == REGISTERING) + dst->name_flags = REGISTERED; + } + /* arbitrarily set HW type to Ethernet */ + data->astat->adapter_type = 0xfe; + if (src - rData < rLen) + memcpy(data->astat->adapter_address, src, + min(rLen - (src - rData), 6)); + } + } + return FALSE; +} + +/* This uses the WINS timeout and query values, as they're the + * UCAST_REQ_RETRY_TIMEOUT and UCAST_REQ_RETRY_COUNT according to the RFCs. + */ +static UCHAR NetBTAstatRemote(NetBTAdapter *adapter, PNCB ncb) +{ + UCHAR ret = NRC_GOODRET; + const NBNameCacheEntry *cacheEntry = NULL; + + TRACE("adapter %p, NCB %p\n", adapter, ncb); + + if (!adapter) return NRC_BADDR; + if (!ncb) return NRC_INVADDRESS; + + ret = NetBTInternalFindName(adapter, ncb, &cacheEntry); + if (ret == NRC_GOODRET && cacheEntry) + { + if (cacheEntry->numAddresses > 0) + { + SOCKET fd = WSASocketA(PF_INET, SOCK_DGRAM, IPPROTO_UDP, NULL, 0, + WSA_FLAG_OVERLAPPED); + + if(fd == INVALID_SOCKET) + ret = NRC_OSRESNOTAV; + else + { + NetBTNodeQueryData queryData; + DWORD queries; + PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer; + + adapter->nameQueryXID++; + astat->name_count = 0; + queryData.gotResponse = FALSE; + queryData.astat = astat; + queryData.astatLen = ncb->ncb_length; + for (queries = 0; !queryData.gotResponse && + queries < gWINSQueries; queries++) + { + if (!NCB_CANCELLED(ncb)) + { + int r = NetBTSendNameQuery(fd, ncb->ncb_callname, + adapter->nameQueryXID, NBNS_TYPE_NBSTAT, + cacheEntry->addresses[0], FALSE); + + if (r == 0) + ret = NetBTWaitForNameResponse(adapter, fd, + GetTickCount() + gWINSQueryTimeout, + NetBTNodeStatusAnswerCallback, &queryData); + else + ret = NRC_SYSTEM; + } + else + ret = NRC_CMDCAN; + } + closesocket(fd); + } + } + else + ret = NRC_CMDTMO; + } + else if (ret == NRC_CMDCAN) + ; /* do nothing, we were cancelled */ + else + ret = NRC_CMDTMO; + TRACE("returning 0x%02x\n", ret); + return ret; +} + +static UCHAR NetBTAstat(void *adapt, PNCB ncb) +{ + NetBTAdapter *adapter = (NetBTAdapter *)adapt; + UCHAR ret; + + TRACE("adapt %p, NCB %p\n", adapt, ncb); + + if (!adapter) return NRC_ENVNOTDEF; + if (!ncb) return NRC_INVADDRESS; + if (!ncb->ncb_buffer) return NRC_BADDR; + if (ncb->ncb_length < sizeof(ADAPTER_STATUS)) return NRC_BUFLEN; + + if (ncb->ncb_callname[0] == '*') + { + DWORD physAddrLen; + MIB_IFROW ifRow; + PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer; + + memset(astat, 0, sizeof(ADAPTER_STATUS)); + astat->rev_major = 3; + ifRow.dwIndex = adapter->ipr.dwIndex; + if (GetIfEntry(&ifRow) != NO_ERROR) + ret = NRC_BRIDGE; + else + { + physAddrLen = min(ifRow.dwPhysAddrLen, 6); + if (physAddrLen > 0) + memcpy(astat->adapter_address, ifRow.bPhysAddr, physAddrLen); + /* doubt anyone cares, but why not.. */ + if (ifRow.dwType == MIB_IF_TYPE_TOKENRING) + astat->adapter_type = 0xff; + else + astat->adapter_type = 0xfe; /* for Ethernet */ + astat->max_sess_pkt_size = 0xffff; + astat->xmit_success = adapter->xmit_success; + astat->recv_success = adapter->recv_success; + } + ret = NRC_GOODRET; + } + else + ret = NetBTAstatRemote(adapter, ncb); + TRACE("returning 0x%02x\n", ret); + return ret; +} + +static UCHAR NetBTFindName(void *adapt, PNCB ncb) +{ + NetBTAdapter *adapter = (NetBTAdapter *)adapt; + UCHAR ret; + const NBNameCacheEntry *cacheEntry = NULL; + PFIND_NAME_HEADER foundName; + + TRACE("adapt %p, NCB %p\n", adapt, ncb); + + if (!adapter) return NRC_ENVNOTDEF; + if (!ncb) return NRC_INVADDRESS; + if (!ncb->ncb_buffer) return NRC_BADDR; + if (ncb->ncb_length < sizeof(FIND_NAME_HEADER)) return NRC_BUFLEN; + + foundName = (PFIND_NAME_HEADER)ncb->ncb_buffer; + memset(foundName, 0, sizeof(FIND_NAME_HEADER)); + + ret = NetBTInternalFindName(adapter, ncb, &cacheEntry); + if (ret == NRC_GOODRET) + { + if (cacheEntry) + { + DWORD spaceFor = min((ncb->ncb_length - sizeof(FIND_NAME_HEADER)) / + sizeof(FIND_NAME_BUFFER), cacheEntry->numAddresses); + DWORD ndx; + + for (ndx = 0; ndx < spaceFor; ndx++) + { + PFIND_NAME_BUFFER findNameBuffer; + + findNameBuffer = + (PFIND_NAME_BUFFER)((PUCHAR)foundName + + sizeof(FIND_NAME_HEADER) + foundName->node_count * + sizeof(FIND_NAME_BUFFER)); + memset(findNameBuffer->destination_addr, 0, 2); + memcpy(findNameBuffer->destination_addr + 2, + &adapter->ipr.dwAddr, sizeof(DWORD)); + memset(findNameBuffer->source_addr, 0, 2); + memcpy(findNameBuffer->source_addr + 2, + &cacheEntry->addresses[ndx], sizeof(DWORD)); + foundName->node_count++; + } + if (spaceFor < cacheEntry->numAddresses) + ret = NRC_BUFLEN; + } + else + ret = NRC_CMDTMO; + } + TRACE("returning 0x%02x\n", ret); + return ret; +} + +static UCHAR NetBTSessionReq(SOCKET fd, const UCHAR *calledName, + const UCHAR *callingName) +{ + UCHAR buffer[NBSS_HDRSIZE + MAX_DOMAIN_NAME_LEN * 2], ret; + int r; + unsigned int len = 0; + DWORD bytesSent, bytesReceived, recvFlags = 0; + WSABUF wsaBuf; + + buffer[0] = NBSS_REQ; + buffer[1] = 0; + + len += NetBTNameEncode(calledName, &buffer[NBSS_HDRSIZE]); + len += NetBTNameEncode(callingName, &buffer[NBSS_HDRSIZE + len]); + + NBR_ADDWORD(&buffer[2], len); + + wsaBuf.len = len + NBSS_HDRSIZE; + wsaBuf.buf = (char*)buffer; + + r = WSASend(fd, &wsaBuf, 1, &bytesSent, 0, NULL, NULL); + if(r < 0 || bytesSent < len + NBSS_HDRSIZE) + { + ERR("send failed\n"); + return NRC_SABORT; + } + + /* I've already set the recv timeout on this socket (if it supports it), so + * just block. Hopefully we'll always receive the session acknowledgement + * within one timeout. + */ + wsaBuf.len = NBSS_HDRSIZE + 1; + r = WSARecv(fd, &wsaBuf, 1, &bytesReceived, &recvFlags, NULL, NULL); + if (r < 0 || bytesReceived < NBSS_HDRSIZE) + ret = NRC_SABORT; + else if (buffer[0] == NBSS_NACK) + { + if (r == NBSS_HDRSIZE + 1) + { + switch (buffer[NBSS_HDRSIZE]) + { + case NBSS_ERR_INSUFFICIENT_RESOURCES: + ret = NRC_REMTFUL; + break; + default: + ret = NRC_NOCALL; + } + } + else + ret = NRC_NOCALL; + } + else if (buffer[0] == NBSS_RETARGET) + { + FIXME("Got a session retarget, can't deal\n"); + ret = NRC_NOCALL; + } + else if (buffer[0] == NBSS_ACK) + ret = NRC_GOODRET; + else + ret = NRC_SYSTEM; + + TRACE("returning 0x%02x\n", ret); + return ret; +} + +static UCHAR NetBTCall(void *adapt, PNCB ncb, void **sess) +{ + NetBTAdapter *adapter = (NetBTAdapter *)adapt; + UCHAR ret; + const NBNameCacheEntry *cacheEntry = NULL; + + TRACE("adapt %p, ncb %p\n", adapt, ncb); + + if (!adapter) return NRC_ENVNOTDEF; + if (!ncb) return NRC_INVADDRESS; + if (!sess) return NRC_BADDR; + + ret = NetBTInternalFindName(adapter, ncb, &cacheEntry); + if (ret == NRC_GOODRET) + { + if (cacheEntry && cacheEntry->numAddresses > 0) + { + SOCKET fd; + + fd = WSASocketA(PF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, + WSA_FLAG_OVERLAPPED); + if (fd != INVALID_SOCKET) + { + DWORD timeout; + struct sockaddr_in sin; + + if (ncb->ncb_rto > 0) + { + timeout = ncb->ncb_rto * 500; + setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout, + sizeof(timeout)); + } + if (ncb->ncb_rto > 0) + { + timeout = ncb->ncb_sto * 500; + setsockopt(fd, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout, + sizeof(timeout)); + } + + memset(&sin, 0, sizeof(sin)); + memcpy(&sin.sin_addr, &cacheEntry->addresses[0], + sizeof(sin.sin_addr)); + sin.sin_family = AF_INET; + sin.sin_port = htons(PORT_NBSS); + /* FIXME: use nonblocking mode for the socket, check the + * cancel flag periodically + */ + if (connect(fd, (struct sockaddr *)&sin, sizeof(sin)) + == SOCKET_ERROR) + ret = NRC_CMDTMO; + else + { + static UCHAR fakedCalledName[] = "*SMBSERVER"; + const UCHAR *calledParty = cacheEntry->nbname[0] == '*' + ? fakedCalledName : cacheEntry->nbname; + + ret = NetBTSessionReq(fd, calledParty, ncb->ncb_name); + if (ret != NRC_GOODRET && calledParty[0] == '*') + { + FIXME("NBT session to \"*SMBSERVER\" refused,\n"); + FIXME("should try finding name using ASTAT\n"); + } + } + if (ret != NRC_GOODRET) + closesocket(fd); + else + { + NetBTSession *session = HeapAlloc( + GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NetBTSession)); + + if (session) + { + session->fd = fd; + InitializeCriticalSection(&session->cs); + *sess = session; + } + else + { + ret = NRC_OSRESNOTAV; + closesocket(fd); + } + } + } + else + ret = NRC_OSRESNOTAV; + } + else + ret = NRC_NAMERR; + } + TRACE("returning 0x%02x\n", ret); + return ret; +} + +/* Notice that I don't protect against multiple thread access to NetBTSend. + * This is because I don't update any data in the adapter, and I only make a + * single call to WSASend, which I assume to act atomically (not interleaving + * data from other threads). + * I don't lock, because I only depend on the fd being valid, and this won't be + * true until a session setup is completed. + */ +static UCHAR NetBTSend(void *adapt, void *sess, PNCB ncb) +{ + NetBTAdapter *adapter = (NetBTAdapter *)adapt; + NetBTSession *session = (NetBTSession *)sess; + UCHAR buffer[NBSS_HDRSIZE], ret; + int r; + WSABUF wsaBufs[2]; + DWORD bytesSent; + + TRACE("adapt %p, session %p, NCB %p\n", adapt, session, ncb); + + if (!adapter) return NRC_ENVNOTDEF; + if (!ncb) return NRC_INVADDRESS; + if (!ncb->ncb_buffer) return NRC_BADDR; + if (!session) return NRC_SNUMOUT; + if (session->fd == INVALID_SOCKET) return NRC_SNUMOUT; + + buffer[0] = NBSS_MSG; + buffer[1] = 0; + NBR_ADDWORD(&buffer[2], ncb->ncb_length); + + wsaBufs[0].len = NBSS_HDRSIZE; + wsaBufs[0].buf = (char*)buffer; + wsaBufs[1].len = ncb->ncb_length; + wsaBufs[1].buf = (char*)ncb->ncb_buffer; + + r = WSASend(session->fd, wsaBufs, sizeof(wsaBufs) / sizeof(wsaBufs[0]), + &bytesSent, 0, NULL, NULL); + if (r == SOCKET_ERROR) + { + NetBIOSHangupSession(ncb); + ret = NRC_SABORT; + } + else if (bytesSent < NBSS_HDRSIZE + ncb->ncb_length) + { + FIXME("Only sent %ld bytes (of %d), hanging up session\n", bytesSent, + NBSS_HDRSIZE + ncb->ncb_length); + NetBIOSHangupSession(ncb); + ret = NRC_SABORT; + } + else + { + ret = NRC_GOODRET; + adapter->xmit_success++; + } + TRACE("returning 0x%02x\n", ret); + return ret; +} + +static UCHAR NetBTRecv(void *adapt, void *sess, PNCB ncb) +{ + NetBTAdapter *adapter = (NetBTAdapter *)adapt; + NetBTSession *session = (NetBTSession *)sess; + UCHAR buffer[NBSS_HDRSIZE], ret; + int r; + WSABUF wsaBufs[2]; + DWORD bufferCount, bytesReceived, flags; + + TRACE("adapt %p, session %p, NCB %p\n", adapt, session, ncb); + + if (!adapter) return NRC_ENVNOTDEF; + if (!ncb) return NRC_BADDR; + if (!ncb->ncb_buffer) return NRC_BADDR; + if (!session) return NRC_SNUMOUT; + if (session->fd == INVALID_SOCKET) return NRC_SNUMOUT; + + EnterCriticalSection(&session->cs); + bufferCount = 0; + if (session->bytesPending == 0) + { + bufferCount++; + wsaBufs[0].len = NBSS_HDRSIZE; + wsaBufs[0].buf = (char*)buffer; + } + wsaBufs[bufferCount].len = ncb->ncb_length; + wsaBufs[bufferCount].buf = (char*)ncb->ncb_buffer; + bufferCount++; + + flags = 0; + /* FIXME: should poll a bit so I can check the cancel flag */ + r = WSARecv(session->fd, wsaBufs, bufferCount, &bytesReceived, &flags, + NULL, NULL); + if (r == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK) + { + LeaveCriticalSection(&session->cs); + ERR("Receive error, WSAGetLastError() returns %d\n", WSAGetLastError()); + NetBIOSHangupSession(ncb); + ret = NRC_SABORT; + } + else if (NCB_CANCELLED(ncb)) + { + LeaveCriticalSection(&session->cs); + ret = NRC_CMDCAN; + } + else + { + if (bufferCount == 2) + { + if (buffer[0] == NBSS_KEEPALIVE) + { + LeaveCriticalSection(&session->cs); + FIXME("Oops, received a session keepalive and lost my place\n"); + /* need to read another session header until we get a session + * message header. */ + NetBIOSHangupSession(ncb); + ret = NRC_SABORT; + } + else if (buffer[0] != NBSS_MSG) + { + LeaveCriticalSection(&session->cs); + FIXME("Received unexpected session msg type %d\n", buffer[0]); + NetBIOSHangupSession(ncb); + ret = NRC_SABORT; + } + else + { + if (buffer[1] & NBSS_EXTENSION) + { + LeaveCriticalSection(&session->cs); + FIXME("Received a message that's too long for my taste\n"); + NetBIOSHangupSession(ncb); + ret = NRC_SABORT; + } + else + { + session->bytesPending = NBSS_HDRSIZE + + NBR_GETWORD(&buffer[2]) - bytesReceived; + ncb->ncb_length = bytesReceived - NBSS_HDRSIZE; + LeaveCriticalSection(&session->cs); + } + } + } + else + { + if (bytesReceived < session->bytesPending) + session->bytesPending -= bytesReceived; + else + session->bytesPending = 0; + LeaveCriticalSection(&session->cs); + ncb->ncb_length = bytesReceived; + } + if (session->bytesPending > 0) + ret = NRC_INCOMP; + else + { + ret = NRC_GOODRET; + adapter->recv_success++; + } + } + TRACE("returning 0x%02x\n", ret); + return ret; +} + +static UCHAR NetBTHangup(void *adapt, void *sess) +{ + NetBTSession *session = (NetBTSession *)sess; + + TRACE("adapt %p, session %p\n", adapt, session); + + if (!session) return NRC_SNUMOUT; + + /* I don't lock the session, because NetBTRecv knows not to decrement + * past 0, so if a receive completes after this it should still deal. + */ + closesocket(session->fd); + session->fd = INVALID_SOCKET; + session->bytesPending = 0; + DeleteCriticalSection(&session->cs); + HeapFree(GetProcessHeap(), 0, session); + + return NRC_GOODRET; +} + +static void NetBTCleanupAdapter(void *adapt) +{ + TRACE("adapt %p\n", adapt); + if (adapt) + { + NetBTAdapter *adapter = (NetBTAdapter *)adapt; + + if (adapter->nameCache) + NBNameCacheDestroy(adapter->nameCache); + HeapFree(GetProcessHeap(), 0, adapt); + } +} + +static void NetBTCleanup(void) +{ + TRACE("\n"); + if (gNameCache) + { + NBNameCacheDestroy(gNameCache); + gNameCache = NULL; + } +} + +static UCHAR NetBTRegisterAdapter(PMIB_IPADDRROW ipRow) +{ + UCHAR ret; + NetBTAdapter *adapter; + + if (!ipRow) return NRC_BADDR; + + adapter = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(NetBTAdapter)); + if (adapter) + { + memcpy(&adapter->ipr, ipRow, sizeof(MIB_IPADDRROW)); + if (!NetBIOSRegisterAdapter(gTransportID, ipRow->dwIndex, adapter)) + { + NetBTCleanupAdapter(adapter); + ret = NRC_SYSTEM; + } + else + ret = NRC_GOODRET; + } + else + ret = NRC_OSRESNOTAV; + return ret; +} + +/* Callback for NetBIOS adapter enumeration. Assumes closure is a pointer to + * a MIB_IPADDRTABLE containing all the IP adapters needed to be added to the + * NetBIOS adapter table. For each callback, checks if the passed-in adapt + * has an entry in the table; if so, this adapter was enumerated previously, + * and it's enabled. As a flag, the table's dwAddr entry is changed to + * INADDR_LOOPBACK, since this is an invalid address for a NetBT adapter. + * The NetBTEnum function will add any remaining adapters from the + * MIB_IPADDRTABLE to the NetBIOS adapter table. + */ +static BOOL NetBTEnumCallback(UCHAR totalLANAs, UCHAR lanaIndex, + ULONG transport, const NetBIOSAdapterImpl *data, void *closure) +{ + BOOL ret; + PMIB_IPADDRTABLE table = (PMIB_IPADDRTABLE)closure; + + if (table && data) + { + DWORD ndx; + + ret = FALSE; + for (ndx = 0; !ret && ndx < table->dwNumEntries; ndx++) + { + const NetBTAdapter *adapter = (const NetBTAdapter *)data->data; + + if (table->table[ndx].dwIndex == adapter->ipr.dwIndex) + { + NetBIOSEnableAdapter(data->lana); + table->table[ndx].dwAddr = INADDR_LOOPBACK; + ret = TRUE; + } + } + } + else + ret = FALSE; + return ret; +} + +/* Enumerates adapters by: + * - retrieving the IP address table for the local machine + * - eliminating loopback addresses from the table + * - eliminating redundant addresses, that is, multiple addresses on the same + * subnet + * Calls NetBIOSEnumAdapters, passing the resulting table as the callback + * data. The callback reenables each adapter that's already in the NetBIOS + * table. After NetBIOSEnumAdapters returns, this function adds any remaining + * adapters to the NetBIOS table. + */ +static UCHAR NetBTEnum(void) +{ + UCHAR ret; + DWORD size = 0; + + TRACE("\n"); + + if (GetIpAddrTable(NULL, &size, FALSE) == ERROR_INSUFFICIENT_BUFFER) + { + PMIB_IPADDRTABLE ipAddrs, coalesceTable = NULL; + DWORD numIPAddrs = (size - sizeof(MIB_IPADDRTABLE)) / + sizeof(MIB_IPADDRROW) + 1; + + ipAddrs = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size); + if (ipAddrs) + coalesceTable = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, sizeof(MIB_IPADDRTABLE) + + (min(numIPAddrs, MAX_LANA + 1) - 1) * sizeof(MIB_IPADDRROW)); + if (ipAddrs && coalesceTable) + { + if (GetIpAddrTable(ipAddrs, &size, FALSE) == ERROR_SUCCESS) + { + DWORD ndx; + + for (ndx = 0; ndx < ipAddrs->dwNumEntries; ndx++) + { + if ((ipAddrs->table[ndx].dwAddr & + ipAddrs->table[ndx].dwMask) != + htonl((INADDR_LOOPBACK & IN_CLASSA_NET))) + { + BOOL newNetwork = TRUE; + DWORD innerIndex; + + /* make sure we don't have more than one entry + * for a subnet */ + for (innerIndex = 0; newNetwork && + innerIndex < coalesceTable->dwNumEntries; innerIndex++) + if ((ipAddrs->table[ndx].dwAddr & + ipAddrs->table[ndx].dwMask) == + (coalesceTable->table[innerIndex].dwAddr + & coalesceTable->table[innerIndex].dwMask)) + newNetwork = FALSE; + + if (newNetwork) + memcpy(&coalesceTable->table[ + coalesceTable->dwNumEntries++], + &ipAddrs->table[ndx], sizeof(MIB_IPADDRROW)); + } + } + + NetBIOSEnumAdapters(gTransportID, NetBTEnumCallback, + coalesceTable); + ret = NRC_GOODRET; + for (ndx = 0; ret == NRC_GOODRET && + ndx < coalesceTable->dwNumEntries; ndx++) + if (coalesceTable->table[ndx].dwAddr != INADDR_LOOPBACK) + ret = NetBTRegisterAdapter(&coalesceTable->table[ndx]); + } + else + ret = NRC_SYSTEM; + HeapFree(GetProcessHeap(), 0, ipAddrs); + HeapFree(GetProcessHeap(), 0, coalesceTable); + } + else + ret = NRC_OSRESNOTAV; + } + else + ret = NRC_SYSTEM; + TRACE("returning 0x%02x\n", ret); + return ret; +} + +static const WCHAR VxD_MSTCPW[] = { 'S','Y','S','T','E','M','\\','C','u','r', + 'r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\','S','e','r','v', + 'i','c','e','s','\\','V','x','D','\\','M','S','T','C','P','\0' }; +static const WCHAR NetBT_ParametersW[] = { 'S','Y','S','T','E','M','\\','C','u', + 'r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\','S','e','r', + 'v','i','c','e','s','\\','N','e','t','B','T','\\','P','a','r','a','m','e','t', + 'e','r','s','\0' }; +static const WCHAR EnableDNSW[] = { 'E','n','a','b','l','e','D','N','S','\0' }; +static const WCHAR BcastNameQueryCountW[] = { 'B','c','a','s','t','N','a','m', + 'e','Q','u','e','r','y','C','o','u','n','t','\0' }; +static const WCHAR BcastNameQueryTimeoutW[] = { 'B','c','a','s','t','N','a','m', + 'e','Q','u','e','r','y','T','i','m','e','o','u','t','\0' }; +static const WCHAR NameSrvQueryCountW[] = { 'N','a','m','e','S','r','v', + 'Q','u','e','r','y','C','o','u','n','t','\0' }; +static const WCHAR NameSrvQueryTimeoutW[] = { 'N','a','m','e','S','r','v', + 'Q','u','e','r','y','T','i','m','e','o','u','t','\0' }; +static const WCHAR ScopeIDW[] = { 'S','c','o','p','e','I','D','\0' }; +static const WCHAR CacheTimeoutW[] = { 'C','a','c','h','e','T','i','m','e','o', + 'u','t','\0' }; +static const WCHAR Config_NetworkW[] = { 'S','o','f','t','w','a','r','e','\\', + 'W','i','n','e','\\','N','e','t','w','o','r','k','\0' }; + +/* Initializes global variables and registers the NetBT transport */ +void NetBTInit(void) +{ + HKEY hKey; + NetBIOSTransport transport; + LONG ret; + + TRACE("\n"); + + gEnableDNS = TRUE; + gBCastQueries = BCAST_QUERIES; + gBCastQueryTimeout = BCAST_QUERY_TIMEOUT; + gWINSQueries = WINS_QUERIES; + gWINSQueryTimeout = WINS_QUERY_TIMEOUT; + gNumWINSServers = 0; + memset(gWINSServers, 0, sizeof(gWINSServers)); + gScopeID[0] = '\0'; + gCacheTimeout = CACHE_TIMEOUT; + + /* Try to open the Win9x NetBT configuration key */ + ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, VxD_MSTCPW, 0, KEY_READ, &hKey); + /* If that fails, try the WinNT NetBT configuration key */ + if (ret != ERROR_SUCCESS) + ret = RegOpenKeyExW(HKEY_LOCAL_MACHINE, NetBT_ParametersW, 0, KEY_READ, + &hKey); + if (ret == ERROR_SUCCESS) + { + DWORD dword, size; + + size = sizeof(dword); + if (RegQueryValueExW(hKey, EnableDNSW, NULL, NULL, + (LPBYTE)&dword, &size) == ERROR_SUCCESS) + gEnableDNS = dword; + size = sizeof(dword); + if (RegQueryValueExW(hKey, BcastNameQueryCountW, NULL, NULL, + (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERIES + && dword <= MAX_QUERIES) + gBCastQueries = dword; + size = sizeof(dword); + if (RegQueryValueExW(hKey, BcastNameQueryTimeoutW, NULL, NULL, + (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERY_TIMEOUT + && dword <= MAX_QUERY_TIMEOUT) + gBCastQueryTimeout = dword; + size = sizeof(dword); + if (RegQueryValueExW(hKey, NameSrvQueryCountW, NULL, NULL, + (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERIES + && dword <= MAX_QUERIES) + gWINSQueries = dword; + size = sizeof(dword); + if (RegQueryValueExW(hKey, NameSrvQueryTimeoutW, NULL, NULL, + (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_QUERY_TIMEOUT + && dword <= MAX_QUERY_TIMEOUT) + gWINSQueryTimeout = dword; + size = MAX_DOMAIN_NAME_LEN - 1; + if (RegQueryValueExW(hKey, ScopeIDW, NULL, NULL, (LPBYTE)gScopeID + 1, &size) + == ERROR_SUCCESS) + { + /* convert into L2-encoded version, suitable for use by + NetBTNameEncode */ + char *ptr, *lenPtr; + + for (ptr = gScopeID + 1; *ptr && + ptr - gScopeID < MAX_DOMAIN_NAME_LEN; ) + { + for (lenPtr = ptr - 1, *lenPtr = 0; *ptr && *ptr != '.' && + ptr - gScopeID < MAX_DOMAIN_NAME_LEN; ptr++) + *lenPtr += 1; + ptr++; + } + } + if (RegQueryValueExW(hKey, CacheTimeoutW, NULL, NULL, + (LPBYTE)&dword, &size) == ERROR_SUCCESS && dword >= MIN_CACHE_TIMEOUT) + gCacheTimeout = dword; + RegCloseKey(hKey); + } + /* WINE-specific NetBT registry settings. Because our adapter naming is + * different than MS', we can't do per-adapter WINS configuration in the + * same place. Just do a global WINS configuration instead. + */ + /* @@ Wine registry key: HKCU\Software\Wine\Network */ + if (RegOpenKeyW(HKEY_CURRENT_USER, Config_NetworkW, &hKey) == ERROR_SUCCESS) + { + static const char *nsValueNames[] = { "WinsServer", "BackupWinsServer" }; + char nsString[16]; + DWORD size, ndx; + + for (ndx = 0; ndx < sizeof(nsValueNames) / sizeof(nsValueNames[0]); + ndx++) + { + size = sizeof(nsString) / sizeof(char); + if (RegQueryValueExA(hKey, nsValueNames[ndx], NULL, NULL, + (LPBYTE)nsString, &size) == ERROR_SUCCESS) + { + unsigned long addr = inet_addr(nsString); + + if (addr != INADDR_NONE && gNumWINSServers < MAX_WINS_SERVERS) + gWINSServers[gNumWINSServers++] = addr; + } + } + RegCloseKey(hKey); + } + + transport.enumerate = NetBTEnum; + transport.astat = NetBTAstat; + transport.findName = NetBTFindName; + transport.call = NetBTCall; + transport.send = NetBTSend; + transport.recv = NetBTRecv; + transport.hangup = NetBTHangup; + transport.cleanupAdapter = NetBTCleanupAdapter; + transport.cleanup = NetBTCleanup; + memcpy(&gTransportID, TRANSPORT_NBT, sizeof(ULONG)); + NetBIOSRegisterTransport(gTransportID, &transport); +} diff --git a/reactos/dll/netapi32/netapi32.c b/reactos/dll/netapi32/netapi32.c new file mode 100644 index 00000000000..e01b9292b64 --- /dev/null +++ b/reactos/dll/netapi32/netapi32.c @@ -0,0 +1,136 @@ +/* Copyright 2001 Mike McCormack + * Copyright 2003 Juan Lang + * + * 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 "config.h" + +#include "wine/debug.h" +#include "lm.h" +#include "netbios.h" + +WINE_DEFAULT_DEBUG_CHANNEL(netbios); + +HMODULE NETAPI32_hModule = 0; + +BOOL WINAPI DllMain (HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + TRACE("%p,%lx,%p\n", hinstDLL, fdwReason, lpvReserved); + + switch (fdwReason) { + case DLL_PROCESS_ATTACH: + { + DisableThreadLibraryCalls(hinstDLL); + NETAPI32_hModule = hinstDLL; + NetBIOSInit(); + NetBTInit(); + break; + } + case DLL_PROCESS_DETACH: + { + NetBIOSShutdown(); + break; + } + } + + return TRUE; +} + +NET_API_STATUS WINAPI NetServerEnum( + LPCWSTR servername, + DWORD level, + LPBYTE* bufptr, + DWORD prefmaxlen, + LPDWORD entriesread, + LPDWORD totalentries, + DWORD servertype, + LPCWSTR domain, + LPDWORD resume_handle +) +{ + FIXME("Stub (%s %ld %p %ld %p %p %ld %s %p)\n", debugstr_w(servername), + level, bufptr, prefmaxlen, entriesread, totalentries, servertype, + debugstr_w(domain), resume_handle); + + return ERROR_NO_BROWSER_SERVERS_FOUND; +} + + +/************************************************************ + * NetServerGetInfo (NETAPI32.@) + */ +NET_API_STATUS WINAPI NetServerGetInfo(LMSTR servername, DWORD level, LPBYTE* bufptr) +{ + FIXME("stub (%p, %ld, %p)\n", servername, level, bufptr); + return ERROR_ACCESS_DENIED; +} + + +/************************************************************ + * NetStatisticsGet (NETAPI32.@) + */ +NET_API_STATUS WINAPI NetStatisticsGet(LPWSTR server, LPWSTR service, + DWORD level, DWORD options, + LPBYTE *bufptr) +{ + TRACE("(%p, %p, %ld, %ld, %p)\n", server, service, level, options, bufptr); + return NERR_InternalError; +} + +DWORD WINAPI NetpNetBiosStatusToApiStatus(DWORD nrc) +{ + DWORD ret; + + switch (nrc) + { + case NRC_GOODRET: + ret = NO_ERROR; + break; + case NRC_NORES: + ret = NERR_NoNetworkResource; + break; + case NRC_DUPNAME: + ret = NERR_AlreadyExists; + break; + case NRC_NAMTFUL: + ret = NERR_TooManyNames; + break; + case NRC_ACTSES: + ret = NERR_DeleteLater; + break; + case NRC_REMTFUL: + ret = ERROR_REM_NOT_LIST; + break; + case NRC_NOCALL: + ret = NERR_NameNotFound; + break; + case NRC_NOWILD: + ret = ERROR_INVALID_PARAMETER; + break; + case NRC_INUSE: + ret = NERR_DuplicateName; + break; + case NRC_NAMERR: + ret = ERROR_INVALID_PARAMETER; + break; + case NRC_NAMCONF: + ret = NERR_DuplicateName; + break; + default: + ret = NERR_NetworkError; + } + return ret; +} diff --git a/reactos/dll/netapi32/netapi32.spec b/reactos/dll/netapi32/netapi32.spec new file mode 100644 index 00000000000..dbb42c49747 --- /dev/null +++ b/reactos/dll/netapi32/netapi32.spec @@ -0,0 +1,290 @@ +@ stub DsAddressToSiteNames +@ stub DsAddressToSiteNamesEx +@ stub DsDeregisterDnsHostRecords +@ stub DsEnumerateDomainTrusts +@ stub DsGetDcClose +@ stub DsGetDcName +@ stub DsGetDcNext +@ stub DsGetDcOpen +@ stub DsGetDcSiteCoverage +@ stub DsGetForestTrustInformationW +@ stub DsGetSiteName +@ stub DsMergeForestTrustInformationW +@ stdcall DsRoleFreeMemory(ptr) +@ stdcall DsRoleGetPrimaryDomainInformation(wstr long ptr) +@ stub DsValidateSubnetName +@ stub I_BrowserDebugCall +@ stub I_BrowserDebugTrace +@ stdcall I_BrowserQueryEmulatedDomains(wstr ptr ptr) +@ stub I_BrowserQueryOtherDomains +@ stub I_BrowserQueryStatistics +@ stub I_BrowserResetNetlogonState +@ stub I_BrowserResetStatistics +@ stub I_BrowserServerEnum +@ stdcall I_BrowserSetNetlogonState(wstr wstr wstr long) +@ stub I_NetAccountDeltas +@ stub I_NetAccountSync +@ stub I_NetDatabaseDeltas +@ stub I_NetDatabaseRedo +@ stub I_NetDatabaseSync2 +@ stub I_NetDatabaseSync +@ stub I_NetDfsCreateExitPoint +@ stub I_NetDfsCreateLocalPartition +@ stub I_NetDfsDeleteExitPoint +@ stub I_NetDfsDeleteLocalPartition +@ stub I_NetDfsFixLocalVolume +@ stub I_NetDfsGetVersion +@ stub I_NetDfsIsThisADomainName +@ stub I_NetDfsModifyPrefix +@ stub I_NetDfsSetLocalVolumeState +@ stub I_NetDfsSetServerInfo +@ stub I_NetGetDCList +@ stub I_NetListCanonicalize +@ stub I_NetListTraverse +@ stub I_NetLogonControl2 +@ stub I_NetLogonControl +@ stub I_NetLogonSamLogoff +@ stub I_NetLogonSamLogon +@ stub I_NetLogonUasLogoff +@ stub I_NetLogonUasLogon +@ stub I_NetNameCanonicalize +@ stdcall I_NetNameCompare(ptr wstr wstr ptr ptr) +@ stdcall I_NetNameValidate(ptr wstr ptr ptr) +@ stub I_NetPathCanonicalize +@ stub I_NetPathCompare +@ stub I_NetPathType +@ stub I_NetServerAuthenticate2 +@ stub I_NetServerAuthenticate +@ stub I_NetServerPasswordSet +@ stub I_NetServerReqChallenge +@ stub I_NetServerSetServiceBits +@ stub I_NetServerSetServiceBitsEx +@ stub NetAlertRaise +@ stub NetAlertRaiseEx +@ stdcall NetApiBufferAllocate(long ptr) +@ stdcall NetApiBufferFree(ptr) +@ stdcall NetApiBufferReallocate(ptr long ptr) +@ stdcall NetApiBufferSize(ptr ptr) +@ stub NetAuditClear +@ stub NetAuditRead +@ stub NetAuditWrite +@ stub NetBrowserStatisticsGet +@ stub NetConfigGet +@ stub NetConfigGetAll +@ stub NetConfigSet +@ stub NetConnectionEnum +@ stub NetDfsAdd +@ stub NetDfsEnum +@ stub NetDfsGetInfo +@ stub NetDfsManagerGetConfigInfo +@ stub NetDfsMove +@ stub NetDfsRemove +@ stub NetDfsRename +@ stub NetDfsSetInfo +@ stub NetEnumerateTrustedDomains +@ stub NetErrorLogClear +@ stub NetErrorLogRead +@ stub NetErrorLogWrite +@ stub NetFileClose +@ stub NetFileEnum +@ stub NetFileGetInfo +@ stub NetGetAnyDCName +@ stdcall NetGetDCName(wstr wstr ptr) +@ stub NetGetDisplayInformationIndex +@ stdcall NetGetJoinInformation(wstr ptr ptr) +@ stub NetGroupAdd +@ stub NetGroupAddUser +@ stub NetGroupDel +@ stub NetGroupDelUser +@ stub NetGroupEnum +@ stub NetGroupGetInfo +@ stub NetGroupGetUsers +@ stub NetGroupSetInfo +@ stub NetGroupSetUsers +@ stdcall NetLocalGroupAdd(wstr long ptr ptr) +@ stub NetLocalGroupAddMember +@ stub NetLocalGroupAddMembers +@ stub NetLocalGroupDel +@ stub NetLocalGroupDelMember +@ stub NetLocalGroupDelMembers +@ stub NetLocalGroupEnum +@ stub NetLocalGroupGetInfo +@ stub NetLocalGroupGetMembers +@ stub NetLocalGroupSetInfo +@ stdcall NetLocalGroupSetMembers(wstr wstr long ptr ptr) +@ stub NetMessageBufferSend +@ stub NetMessageNameAdd +@ stub NetMessageNameDel +@ stub NetMessageNameEnum +@ stub NetMessageNameGetInfo +@ stdcall NetQueryDisplayInformation(wstr long long long long ptr ptr) +@ stub NetRemoteComputerSupports +@ stub NetRemoteTOD +@ stub NetReplExportDirAdd +@ stub NetReplExportDirDel +@ stub NetReplExportDirEnum +@ stub NetReplExportDirGetInfo +@ stub NetReplExportDirLock +@ stub NetReplExportDirSetInfo +@ stub NetReplExportDirUnlock +@ stub NetReplGetInfo +@ stub NetReplImportDirAdd +@ stub NetReplImportDirDel +@ stub NetReplImportDirEnum +@ stub NetReplImportDirGetInfo +@ stub NetReplImportDirLock +@ stub NetReplImportDirUnlock +@ stub NetReplSetInfo +@ stub NetRplAdapterAdd +@ stub NetRplAdapterDel +@ stub NetRplAdapterEnum +@ stub NetRplBootAdd +@ stub NetRplBootDel +@ stub NetRplBootEnum +@ stub NetRplClose +@ stub NetRplConfigAdd +@ stub NetRplConfigDel +@ stub NetRplConfigEnum +@ stub NetRplGetInfo +@ stub NetRplOpen +@ stub NetRplProfileAdd +@ stub NetRplProfileClone +@ stub NetRplProfileDel +@ stub NetRplProfileEnum +@ stub NetRplProfileGetInfo +@ stub NetRplProfileSetInfo +@ stub NetRplSetInfo +@ stub NetRplSetSecurity +@ stub NetRplVendorAdd +@ stub NetRplVendorDel +@ stub NetRplVendorEnum +@ stub NetRplWkstaAdd +@ stub NetRplWkstaClone +@ stub NetRplWkstaDel +@ stub NetRplWkstaEnum +@ stub NetRplWkstaGetInfo +@ stub NetRplWkstaSetInfo +@ stub NetScheduleJobAdd +@ stub NetScheduleJobDel +@ stub NetScheduleJobEnum +@ stub NetScheduleJobGetInfo +@ stub NetServerComputerNameAdd +@ stub NetServerComputerNameDel +@ stub NetServerDiskEnum +@ stdcall NetServerEnum(wstr long ptr long ptr ptr long wstr ptr) +@ stub NetServerEnumEx +@ stdcall NetServerGetInfo(wstr long ptr) +@ stub NetServerSetInfo +@ stub NetServerTransportAdd +@ stub NetServerTransportAddEx +@ stub NetServerTransportDel +@ stub NetServerTransportEnum +@ stub NetServiceControl +@ stub NetServiceEnum +@ stub NetServiceGetInfo +@ stub NetServiceInstall +@ stub NetSessionDel +@ stub NetSessionEnum +@ stub NetSessionGetInfo +@ stub NetShareAdd +@ stub NetShareCheck +@ stub NetShareDel +@ stub NetShareDelSticky +@ stdcall NetShareEnum(wstr long ptr long ptr ptr ptr) +@ stub NetShareEnumSticky +@ stub NetShareGetInfo +@ stub NetShareSetInfo +@ stdcall NetStatisticsGet(wstr wstr long long ptr) +@ stub NetUseAdd +@ stub NetUseDel +@ stub NetUseEnum +@ stub NetUseGetInfo +@ stdcall NetUserAdd(wstr long ptr ptr) +@ stub NetUserChangePassword +@ stdcall NetUserDel(wstr wstr) +@ stdcall NetUserEnum(wstr long long ptr long ptr ptr ptr) +@ stub NetUserGetGroups +@ stdcall NetUserGetInfo(wstr wstr long ptr) +@ stub NetUserGetLocalGroups +@ stdcall NetUserModalsGet(wstr long ptr) +@ stub NetUserModalsSet +@ stub NetUserSetGroups +@ stub NetUserSetInfo +@ stdcall NetWkstaGetInfo(wstr long ptr) +@ stub NetWkstaSetInfo +@ stub NetWkstaTransportAdd +@ stub NetWkstaTransportDel +@ stdcall NetWkstaTransportEnum (wstr long ptr long ptr ptr ptr) +@ stub NetWkstaUserEnum +@ stdcall NetWkstaUserGetInfo(wstr long ptr) +@ stub NetWkstaUserSetInfo +@ stdcall NetapipBufferAllocate(long ptr) NetApiBufferAllocate +@ stdcall Netbios(ptr) +@ stub NetpAccessCheck +@ stub NetpAccessCheckAndAudit +@ stub NetpAllocConfigName +@ stub NetpAllocStrFromStr +@ stub NetpAllocStrFromWStr +@ stub NetpAllocTStrFromString +@ stub NetpAllocWStrFromStr +@ stub NetpAllocWStrFromWStr +@ stub NetpApiStatusToNtStatus +@ stub NetpAssertFailed +@ stub NetpCloseConfigData +@ stub NetpCopyStringToBuffer +@ stub NetpCreateSecurityObject +@ stub NetpDbgDisplayServerInfo +@ stub NetpDbgPrint +@ stdcall NetpDeleteSecurityObject(long) ntdll.RtlDeleteSecurityObject +@ stdcall NetpGetComputerName(ptr) +@ stub NetpGetConfigBool +@ stub NetpGetConfigDword +@ stub NetpGetConfigTStrArray +@ stub NetpGetConfigValue +@ stub NetpGetDomainName +@ stub NetpGetFileSecurity +@ stub NetpGetPrivilege +@ stub NetpHexDump +@ stdcall NetpInitOemString(ptr str) ntdll.RtlInitAnsiString +@ stub NetpIsRemote +@ stub NetpIsUncComputerNameValid +@ stub NetpLocalTimeZoneOffset +@ stub NetpLogonPutUnicodeString +@ stub NetpNetBiosAddName +@ stub NetpNetBiosCall +@ stub NetpNetBiosDelName +@ stub NetpNetBiosGetAdapterNumbers +@ stub NetpNetBiosHangup +@ stub NetpNetBiosReceive +@ stub NetpNetBiosReset +@ stub NetpNetBiosSend +@ stdcall NetpNetBiosStatusToApiStatus(long) +@ stub NetpNtStatusToApiStatus +@ stub NetpOpenConfigData +@ stub NetpPackString +@ stub NetpReleasePrivilege +@ stub NetpSetConfigBool +@ stub NetpSetConfigDword +@ stub NetpSetConfigTStrArray +@ stub NetpSetFileSecurity +@ stub NetpSmbCheck +@ stub NetpStringToNetBiosName +@ stub NetpTStrArrayEntryCount +@ stub NetpwNameCanonicalize +@ stub NetpwNameCompare +@ stub NetpwNameValidate +@ stub NetpwPathCanonicalize +@ stub NetpwPathCompare +@ stub NetpwPathType +@ stub NlBindingAddServerToCache +@ stub NlBindingRemoveServerFromCache +@ stub NlBindingSetAuthInfo +@ stub RxNetAccessAdd +@ stub RxNetAccessDel +@ stub RxNetAccessEnum +@ stub RxNetAccessGetInfo +@ stub RxNetAccessGetUserPerms +@ stub RxNetAccessSetInfo +@ stub RxNetServerEnum +@ stub RxNetUserPasswordSet +@ stub RxRemoteApi diff --git a/reactos/dll/netapi32/netapi32.xml b/reactos/dll/netapi32/netapi32.xml new file mode 100644 index 00000000000..04796c2b3a8 --- /dev/null +++ b/reactos/dll/netapi32/netapi32.xml @@ -0,0 +1,28 @@ + + + . + include/wine + + + 0x600 + 0x501 + 0x501 + + wine + ntdll + kernel32 + advapi32 + ws2_32 + iphlpapi + access.c + apibuf.c + browsr.c + ds.c + nbcmdqueue.c + nbnamecache.c + nbt.c + netapi32.c + netbios.c + wksta.c + netapi32.spec + diff --git a/reactos/dll/netapi32/netapi32_misc.h b/reactos/dll/netapi32/netapi32_misc.h new file mode 100644 index 00000000000..92cbc26d07a --- /dev/null +++ b/reactos/dll/netapi32/netapi32_misc.h @@ -0,0 +1,35 @@ +/* + * Copyright 2002 Andriy Palamarchuk + * + * netapi32 internal functions. + * + * 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, writ +e to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef __WINE_NETAPI32_MISC_H +#define __WINE_NETAPI32_MISC_H + +extern BOOL NETAPI_IsLocalComputer(LPCWSTR ServerName); + +#define NETAPI_ForceLocalComputer(ServerName, FailureCode) \ + if (!NETAPI_IsLocalComputer(ServerName)) \ + { \ + FIXME("Action Implemented for local computer only. " \ + "Requested for server %s\n", debugstr_w(ServerName)); \ + return FailureCode; \ + } + +#endif diff --git a/reactos/dll/netapi32/netbios.c b/reactos/dll/netapi32/netbios.c new file mode 100644 index 00000000000..71f503a2714 --- /dev/null +++ b/reactos/dll/netapi32/netbios.c @@ -0,0 +1,857 @@ +/* Copyright (c) 2003 Juan Lang + * + * 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 "config.h" +#include "wine/debug.h" +#include "nbcmdqueue.h" +#include "netbios.h" + +WINE_DEFAULT_DEBUG_CHANNEL(netbios); + +/* This file provides a NetBIOS emulator that implements the NetBIOS interface, + * including thread safety and asynchronous call support. The protocol + * implementation is separate, with blocking (synchronous) functions. + */ + +#define ADAPTERS_INCR 8 +#define DEFAULT_NUM_SESSIONS 16 + +typedef struct _NetBIOSTransportTableEntry +{ + ULONG id; + NetBIOSTransport transport; +} NetBIOSTransportTableEntry; + +typedef struct _NetBIOSSession +{ + BOOL inUse; + UCHAR state; + UCHAR local_name[NCBNAMSZ]; + UCHAR remote_name[NCBNAMSZ]; + void *data; +} NetBIOSSession; + +/* This struct needs a little explanation, unfortunately. enabled is only + * used by nbInternalEnum (see). If transport_id is not 0 and transport + * is not NULL, the adapter is considered valid. (transport is a pointer to + * an entry in a NetBIOSTransportTableEntry.) data has data for the callers of + * NetBIOSEnumAdapters to be able to see. The lana is repeated there, even + * though I don't use it internally--it's for transports to use reenabling + * adapters using NetBIOSEnableAdapter. + */ +typedef struct _NetBIOSAdapter +{ + BOOL enabled; + BOOL shuttingDown; + LONG resetting; + ULONG transport_id; + NetBIOSTransport *transport; + NetBIOSAdapterImpl impl; + struct NBCmdQueue *cmdQueue; + CRITICAL_SECTION cs; + DWORD sessionsLen; + NetBIOSSession *sessions; +} NetBIOSAdapter; + +typedef struct _NetBIOSAdapterTable { + CRITICAL_SECTION cs; + BOOL enumerated; + BOOL enumerating; + UCHAR tableSize; + NetBIOSAdapter *table; +} NetBIOSAdapterTable; + +/* Just enough space for NBT right now */ +static NetBIOSTransportTableEntry gTransports[1]; +static UCHAR gNumTransports = 0; +static NetBIOSAdapterTable gNBTable; + +static UCHAR nbResizeAdapterTable(UCHAR newSize) +{ + UCHAR ret; + + if (gNBTable.table) + gNBTable.table = HeapReAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, gNBTable.table, + newSize * sizeof(NetBIOSAdapter)); + else + gNBTable.table = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, newSize * sizeof(NetBIOSAdapter)); + if (gNBTable.table) + { + gNBTable.tableSize = newSize; + ret = NRC_GOODRET; + } + else + ret = NRC_OSRESNOTAV; + return ret; +} + +void NetBIOSInit(void) +{ + memset(&gNBTable, 0, sizeof(gNBTable)); + InitializeCriticalSection(&gNBTable.cs); +} + +void NetBIOSShutdown(void) +{ + UCHAR i; + + EnterCriticalSection(&gNBTable.cs); + for (i = 0; i < gNBTable.tableSize; i++) + { + if (gNBTable.table[i].transport && + gNBTable.table[i].transport->cleanupAdapter) + gNBTable.table[i].transport->cleanupAdapter( + gNBTable.table[i].impl.data); + } + for (i = 0; i < gNumTransports; i++) + if (gTransports[i].transport.cleanup) + gTransports[i].transport.cleanup(); + LeaveCriticalSection(&gNBTable.cs); + DeleteCriticalSection(&gNBTable.cs); + HeapFree(GetProcessHeap(), 0, gNBTable.table); +} + +BOOL NetBIOSRegisterTransport(ULONG id, NetBIOSTransport *transport) +{ + BOOL ret; + + TRACE(": transport 0x%08lx, p %p\n", id, transport); + if (!transport) + ret = FALSE; + else if (gNumTransports >= sizeof(gTransports) / sizeof(gTransports[0])) + { + FIXME("You tried to add %d transports, but I only have space for %d\n", + gNumTransports + 1, sizeof(gTransports) / sizeof(gTransports[0])); + ret = FALSE; + } + else + { + UCHAR i; + + ret = FALSE; + for (i = 0; !ret && i < gNumTransports; i++) + { + if (gTransports[i].id == id) + { + WARN("Replacing NetBIOS transport ID %ld\n", id); + memcpy(&gTransports[i].transport, transport, + sizeof(NetBIOSTransport)); + ret = TRUE; + } + } + if (!ret) + { + gTransports[gNumTransports].id = id; + memcpy(&gTransports[gNumTransports].transport, transport, + sizeof(NetBIOSTransport)); + gNumTransports++; + ret = TRUE; + } + } + TRACE("returning %d\n", ret); + return ret; +} + +/* In this, I acquire the table lock to make sure no one else is modifying it. + * This is _probably_ overkill since it should only be called during the + * context of a NetBIOSEnum call, but just to be safe.. + */ +BOOL NetBIOSRegisterAdapter(ULONG transport, DWORD ifIndex, void *data) +{ + BOOL ret; + UCHAR i; + + TRACE(": transport 0x%08lx, ifIndex 0x%08lx, data %p\n", transport, ifIndex, + data); + for (i = 0; i < gNumTransports && gTransports[i].id != transport; i++) + ; + if (gTransports[i].id == transport) + { + NetBIOSTransport *transportPtr = &gTransports[i].transport; + + TRACE(": found transport %p for id 0x%08lx\n", transportPtr, transport); + + EnterCriticalSection(&gNBTable.cs); + ret = FALSE; + for (i = 0; i < gNBTable.tableSize && + gNBTable.table[i].transport != 0; i++) + ; + if (i == gNBTable.tableSize && gNBTable.tableSize < MAX_LANA + 1) + { + UCHAR newSize; + + if (gNBTable.tableSize < (MAX_LANA + 1) - ADAPTERS_INCR) + newSize = gNBTable.tableSize + ADAPTERS_INCR; + else + newSize = MAX_LANA + 1; + nbResizeAdapterTable(newSize); + } + if (i < gNBTable.tableSize && gNBTable.table[i].transport == 0) + { + TRACE(": registering as LANA %d\n", i); + gNBTable.table[i].transport_id = transport; + gNBTable.table[i].transport = transportPtr; + gNBTable.table[i].impl.lana = i; + gNBTable.table[i].impl.ifIndex = ifIndex; + gNBTable.table[i].impl.data = data; + gNBTable.table[i].cmdQueue = NBCmdQueueCreate(GetProcessHeap()); + InitializeCriticalSection(&gNBTable.table[i].cs); + gNBTable.table[i].enabled = TRUE; + ret = TRUE; + } + LeaveCriticalSection(&gNBTable.cs); + } + else + ret = FALSE; + TRACE("returning %d\n", ret); + return ret; +} + +/* In this, I acquire the table lock to make sure no one else is modifying it. + * This is _probably_ overkill since it should only be called during the + * context of a NetBIOSEnum call, but just to be safe.. + */ +void NetBIOSEnableAdapter(UCHAR lana) +{ + TRACE(": %d\n", lana); + if (lana < gNBTable.tableSize) + { + EnterCriticalSection(&gNBTable.cs); + if (gNBTable.table[lana].transport != 0) + gNBTable.table[lana].enabled = TRUE; + LeaveCriticalSection(&gNBTable.cs); + } +} + +static void nbShutdownAdapter(NetBIOSAdapter *adapter) +{ + if (adapter) + { + adapter->shuttingDown = TRUE; + NBCmdQueueCancelAll(adapter->cmdQueue); + if (adapter->transport->cleanupAdapter) + adapter->transport->cleanupAdapter(adapter->impl.data); + NBCmdQueueDestroy(adapter->cmdQueue); + DeleteCriticalSection(&adapter->cs); + memset(adapter, 0, sizeof(NetBIOSAdapter)); + } +} + +static void nbInternalEnum(void) +{ + UCHAR i; + + EnterCriticalSection(&gNBTable.cs); + TRACE("before mark\n"); + /* mark: */ + for (i = 0; i < gNBTable.tableSize; i++) + if (gNBTable.table[i].enabled && gNBTable.table[i].transport != 0) + gNBTable.table[i].enabled = FALSE; + + TRACE("marked, before store, %d transports\n", gNumTransports); + /* store adapters: */ + for (i = 0; i < gNumTransports; i++) + if (gTransports[i].transport.enumerate) + gTransports[i].transport.enumerate(); + + TRACE("before sweep\n"); + /* sweep: */ + for (i = 0; i < gNBTable.tableSize; i++) + if (!gNBTable.table[i].enabled && gNBTable.table[i].transport != 0) + nbShutdownAdapter(&gNBTable.table[i]); + gNBTable.enumerated = TRUE; + LeaveCriticalSection(&gNBTable.cs); +} + +UCHAR NetBIOSNumAdapters(void) +{ + UCHAR ret, i; + + if (!gNBTable.enumerated) + nbInternalEnum(); + for (i = 0, ret = 0; i < gNBTable.tableSize; i++) + if (gNBTable.table[i].transport != 0) + ret++; + return ret; +} + +void NetBIOSEnumAdapters(ULONG transport, NetBIOSEnumAdaptersCallback cb, + void *closure) +{ + TRACE("transport 0x%08lx, callback %p, closure %p\n", transport, cb, + closure); + if (cb) + { + BOOL enumAll = memcmp(&transport, ALL_TRANSPORTS, sizeof(ULONG)) == 0; + UCHAR i, numLANAs = 0; + + EnterCriticalSection(&gNBTable.cs); + if (!gNBTable.enumerating) + { + gNBTable.enumerating = TRUE; + nbInternalEnum(); + gNBTable.enumerating = FALSE; + } + for (i = 0; i < gNBTable.tableSize; i++) + if (enumAll || gNBTable.table[i].transport_id == transport) + numLANAs++; + if (numLANAs > 0) + { + UCHAR lanaIndex = 0; + + for (i = 0; i < gNBTable.tableSize; i++) + if (gNBTable.table[i].transport_id != 0 && + (enumAll || gNBTable.table[i].transport_id == transport)) + cb(numLANAs, lanaIndex++, gNBTable.table[i].transport_id, + &gNBTable.table[i].impl, closure); + } + LeaveCriticalSection(&gNBTable.cs); + } +} + +static NetBIOSAdapter *nbGetAdapter(UCHAR lana) +{ + NetBIOSAdapter *ret = NULL; + + TRACE(": lana %d, num allocated adapters %d\n", lana, gNBTable.tableSize); + if (lana < gNBTable.tableSize && gNBTable.table[lana].transport_id != 0 + && gNBTable.table[lana].transport) + ret = &gNBTable.table[lana]; + TRACE("returning %p\n", ret); + return ret; +} + +static UCHAR nbEnum(PNCB ncb) +{ + PLANA_ENUM lanas = (PLANA_ENUM)ncb->ncb_buffer; + UCHAR i, ret; + + TRACE(": ncb %p\n", ncb); + + if (!lanas) + ret = NRC_BUFLEN; + else if (ncb->ncb_length < sizeof(LANA_ENUM)) + ret = NRC_BUFLEN; + else + { + nbInternalEnum(); + lanas->length = 0; + for (i = 0; i < gNBTable.tableSize; i++) + if (gNBTable.table[i].transport) + { + lanas->length++; + lanas->lana[i] = i; + } + ret = NRC_GOODRET; + } + TRACE("returning 0x%02x\n", ret); + return ret; +} + +static UCHAR nbInternalHangup(NetBIOSAdapter *adapter, NetBIOSSession *session); + +static UCHAR nbCancel(NetBIOSAdapter *adapter, PNCB ncb) +{ + UCHAR ret; + + TRACE(": adapter %p, ncb %p\n", adapter, ncb); + + if (!adapter) return NRC_BRIDGE; + if (!ncb) return NRC_INVADDRESS; + + switch (ncb->ncb_command & 0x7f) + { + case NCBCANCEL: + case NCBADDNAME: + case NCBADDGRNAME: + case NCBDELNAME: + case NCBRESET: + case NCBSSTAT: + ret = NRC_CANCEL; + break; + + /* NCBCALL, NCBCHAINSEND/NCBSEND, NCBHANGUP all close the associated + * session if cancelled */ + case NCBCALL: + case NCBSEND: + case NCBCHAINSEND: + case NCBSENDNA: + case NCBCHAINSENDNA: + case NCBHANGUP: + { + if (ncb->ncb_lsn >= adapter->sessionsLen) + ret = NRC_SNUMOUT; + else if (!adapter->sessions[ncb->ncb_lsn].inUse) + ret = NRC_SNUMOUT; + else + { + ret = NBCmdQueueCancel(adapter->cmdQueue, ncb); + if (ret == NRC_CMDCAN || ret == NRC_CANOCCR) + nbInternalHangup(adapter, &adapter->sessions[ncb->ncb_lsn]); + } + break; + } + + default: + ret = NBCmdQueueCancel(adapter->cmdQueue, ncb); + } + TRACE("returning 0x%02x\n", ret); + return ret; +} + +/* Resizes adapter to contain space for at least sessionsLen sessions. + * If allocating more space for sessions, sets the adapter's sessionsLen to + * sessionsLen. If the adapter's sessionsLen was already at least sessionsLen, + * does nothing. Does not modify existing sessions. Assumes the adapter is + * locked. + * Returns NRC_GOODRET on success, and something else on failure. + */ +static UCHAR nbResizeAdapter(NetBIOSAdapter *adapter, UCHAR sessionsLen) +{ + UCHAR ret = NRC_GOODRET; + + if (adapter && adapter->sessionsLen < sessionsLen) + { + NetBIOSSession *newSessions; + + if (adapter->sessions) + newSessions = HeapReAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, adapter->sessions, sessionsLen * + sizeof(NetBIOSSession)); + else + newSessions = HeapAlloc(GetProcessHeap(), + HEAP_ZERO_MEMORY, sessionsLen * sizeof(NetBIOSSession)); + if (newSessions) + { + adapter->sessions = newSessions; + adapter->sessionsLen = sessionsLen; + } + else + ret = NRC_OSRESNOTAV; + } + return ret; +} + +static UCHAR nbReset(NetBIOSAdapter *adapter, PNCB ncb) +{ + UCHAR ret; + + TRACE(": adapter %p, ncb %p\n", adapter, ncb); + + if (!adapter) return NRC_BRIDGE; + if (!ncb) return NRC_INVADDRESS; + + if (InterlockedIncrement(&adapter->resetting) == 1) + { + UCHAR i, resizeTo; + + NBCmdQueueCancelAll(adapter->cmdQueue); + + EnterCriticalSection(&adapter->cs); + for (i = 0; i < adapter->sessionsLen; i++) + if (adapter->sessions[i].inUse) + nbInternalHangup(adapter, &adapter->sessions[i]); + if (!ncb->ncb_lsn) + resizeTo = ncb->ncb_callname[0] == 0 ? DEFAULT_NUM_SESSIONS : + ncb->ncb_callname[0]; + else if (adapter->sessionsLen == 0) + resizeTo = DEFAULT_NUM_SESSIONS; + else + resizeTo = 0; + if (resizeTo > 0) + ret = nbResizeAdapter(adapter, resizeTo); + else + ret = NRC_GOODRET; + LeaveCriticalSection(&adapter->cs); + } + else + ret = NRC_TOOMANY; + InterlockedDecrement(&adapter->resetting); + TRACE("returning 0x%02x\n", ret); + return ret; +} + +static UCHAR nbSStat(NetBIOSAdapter *adapter, PNCB ncb) +{ + UCHAR ret, i, spaceFor; + PSESSION_HEADER sstat; + + TRACE(": adapter %p, NCB %p\n", adapter, ncb); + + if (!adapter) return NRC_BADDR; + if (adapter->sessionsLen == 0) return NRC_ENVNOTDEF; + if (!ncb) return NRC_INVADDRESS; + if (!ncb->ncb_buffer) return NRC_BADDR; + if (ncb->ncb_length < sizeof(SESSION_HEADER)) return NRC_BUFLEN; + + sstat = (PSESSION_HEADER)ncb->ncb_buffer; + ret = NRC_GOODRET; + memset(sstat, 0, sizeof(SESSION_HEADER)); + spaceFor = (ncb->ncb_length - sizeof(SESSION_HEADER)) / + sizeof(SESSION_BUFFER); + EnterCriticalSection(&adapter->cs); + for (i = 0; ret == NRC_GOODRET && i < adapter->sessionsLen; i++) + { + if (adapter->sessions[i].inUse && (ncb->ncb_name[0] == '*' || + !memcmp(ncb->ncb_name, adapter->sessions[i].local_name, NCBNAMSZ))) + { + if (sstat->num_sess < spaceFor) + { + PSESSION_BUFFER buf; + + buf = (PSESSION_BUFFER)((PUCHAR)sstat + sizeof(SESSION_HEADER) + + sstat->num_sess * sizeof(SESSION_BUFFER)); + buf->lsn = i; + buf->state = adapter->sessions[i].state; + memcpy(buf->local_name, adapter->sessions[i].local_name, + NCBNAMSZ); + memcpy(buf->remote_name, adapter->sessions[i].remote_name, + NCBNAMSZ); + buf->rcvs_outstanding = buf->sends_outstanding = 0; + sstat->num_sess++; + } + else + ret = NRC_BUFLEN; + } + } + LeaveCriticalSection(&adapter->cs); + + TRACE("returning 0x%02x\n", ret); + return ret; +} + +static UCHAR nbCall(NetBIOSAdapter *adapter, PNCB ncb) +{ + UCHAR ret, i; + + TRACE(": adapter %p, NCB %p\n", adapter, ncb); + + if (!adapter) return NRC_BRIDGE; + if (adapter->sessionsLen == 0) return NRC_ENVNOTDEF; + if (!adapter->transport->call) return NRC_ILLCMD; + if (!ncb) return NRC_INVADDRESS; + + EnterCriticalSection(&adapter->cs); + for (i = 0; i < adapter->sessionsLen && adapter->sessions[i].inUse; i++) + ; + if (i < adapter->sessionsLen) + { + adapter->sessions[i].inUse = TRUE; + adapter->sessions[i].state = CALL_PENDING; + memcpy(adapter->sessions[i].local_name, ncb->ncb_name, NCBNAMSZ); + memcpy(adapter->sessions[i].remote_name, ncb->ncb_callname, NCBNAMSZ); + ret = NRC_GOODRET; + } + else + ret = NRC_LOCTFUL; + LeaveCriticalSection(&adapter->cs); + + if (ret == NRC_GOODRET) + { + ret = adapter->transport->call(adapter->impl.data, ncb, + &adapter->sessions[i].data); + if (ret == NRC_GOODRET) + { + ncb->ncb_lsn = i; + adapter->sessions[i].state = SESSION_ESTABLISHED; + } + else + { + adapter->sessions[i].inUse = FALSE; + adapter->sessions[i].state = 0; + } + } + TRACE("returning 0x%02x\n", ret); + return ret; +} + +static UCHAR nbSend(NetBIOSAdapter *adapter, PNCB ncb) +{ + UCHAR ret; + NetBIOSSession *session; + + if (!adapter) return NRC_BRIDGE; + if (!adapter->transport->send) return NRC_ILLCMD; + if (!ncb) return NRC_INVADDRESS; + if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT; + if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT; + if (!ncb->ncb_buffer) return NRC_BADDR; + + session = &adapter->sessions[ncb->ncb_lsn]; + if (session->state != SESSION_ESTABLISHED) + ret = NRC_SNUMOUT; + else + ret = adapter->transport->send(adapter->impl.data, session->data, ncb); + return ret; +} + +static UCHAR nbRecv(NetBIOSAdapter *adapter, PNCB ncb) +{ + UCHAR ret; + NetBIOSSession *session; + + if (!adapter) return NRC_BRIDGE; + if (!adapter->transport->recv) return NRC_ILLCMD; + if (!ncb) return NRC_INVADDRESS; + if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT; + if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT; + if (!ncb->ncb_buffer) return NRC_BADDR; + + session = &adapter->sessions[ncb->ncb_lsn]; + if (session->state != SESSION_ESTABLISHED) + ret = NRC_SNUMOUT; + else + ret = adapter->transport->recv(adapter->impl.data, session->data, ncb); + return ret; +} + +static UCHAR nbInternalHangup(NetBIOSAdapter *adapter, NetBIOSSession *session) +{ + UCHAR ret; + + if (!adapter) return NRC_BRIDGE; + if (!session) return NRC_SNUMOUT; + + if (adapter->transport->hangup) + ret = adapter->transport->hangup(adapter->impl.data, session->data); + else + ret = NRC_ILLCMD; + EnterCriticalSection(&adapter->cs); + memset(session, 0, sizeof(NetBIOSSession)); + LeaveCriticalSection(&adapter->cs); + return NRC_GOODRET; +} + +static UCHAR nbHangup(NetBIOSAdapter *adapter, PNCB ncb) +{ + UCHAR ret; + NetBIOSSession *session; + + if (!adapter) return NRC_BRIDGE; + if (!ncb) return NRC_INVADDRESS; + if (ncb->ncb_lsn >= adapter->sessionsLen) return NRC_SNUMOUT; + if (!adapter->sessions[ncb->ncb_lsn].inUse) return NRC_SNUMOUT; + + session = &adapter->sessions[ncb->ncb_lsn]; + if (session->state != SESSION_ESTABLISHED) + ret = NRC_SNUMOUT; + else + { + session->state = HANGUP_PENDING; + ret = nbInternalHangup(adapter, session); + } + return ret; +} + +void NetBIOSHangupSession(PNCB ncb) +{ + NetBIOSAdapter *adapter; + + if (!ncb) return; + + adapter = nbGetAdapter(ncb->ncb_lana_num); + if (adapter) + { + if (ncb->ncb_lsn < adapter->sessionsLen && + adapter->sessions[ncb->ncb_lsn].inUse) + nbHangup(adapter, ncb); + } +} + +static UCHAR nbAStat(NetBIOSAdapter *adapter, PNCB ncb) +{ + UCHAR ret; + + if (!adapter) return NRC_BRIDGE; + if (!adapter->transport->astat) return NRC_ILLCMD; + if (!ncb) return NRC_INVADDRESS; + if (!ncb->ncb_buffer) return NRC_BADDR; + if (ncb->ncb_length < sizeof(ADAPTER_STATUS)) return NRC_BUFLEN; + + ret = adapter->transport->astat(adapter->impl.data, ncb); + if (ncb->ncb_callname[0] == '*') + { + PADAPTER_STATUS astat = (PADAPTER_STATUS)ncb->ncb_buffer; + + astat->max_sess = astat->max_cfg_sess = adapter->sessionsLen; + } + return ret; +} + +static UCHAR nbDispatch(NetBIOSAdapter *adapter, PNCB ncb) +{ + UCHAR ret, cmd; + + TRACE(": adapter %p, ncb %p\n", adapter, ncb); + + if (!adapter) return NRC_BRIDGE; + if (!ncb) return NRC_INVADDRESS; + + cmd = ncb->ncb_command & 0x7f; + if (cmd == NCBRESET) + ret = nbReset(adapter, ncb); + else + { + ret = NBCmdQueueAdd(adapter->cmdQueue, ncb); + if (ret == NRC_GOODRET) + { + switch (cmd) + { + case NCBCALL: + ret = nbCall(adapter, ncb); + break; + + /* WinNT doesn't chain sends, it always sends immediately. + * Doubt there's any real significance to the NA variants. + */ + case NCBSEND: + case NCBSENDNA: + case NCBCHAINSEND: + case NCBCHAINSENDNA: + ret = nbSend(adapter, ncb); + break; + + case NCBRECV: + ret = nbRecv(adapter, ncb); + break; + + case NCBHANGUP: + ret = nbHangup(adapter, ncb); + break; + + case NCBASTAT: + ret = nbAStat(adapter, ncb); + break; + + case NCBFINDNAME: + if (adapter->transport->findName) + ret = adapter->transport->findName(adapter->impl.data, + ncb); + else + ret = NRC_ILLCMD; + break; + + default: + FIXME("(%p): command code 0x%02x\n", ncb, ncb->ncb_command); + ret = NRC_ILLCMD; + } + NBCmdQueueComplete(adapter->cmdQueue, ncb, ret); + } + } + TRACE("returning 0x%02x\n", ret); + return ret; +} + +static DWORD WINAPI nbCmdThread(LPVOID lpVoid) +{ + PNCB ncb = (PNCB)lpVoid; + + if (ncb) + { + UCHAR ret; + NetBIOSAdapter *adapter = nbGetAdapter(ncb->ncb_lana_num); + + if (adapter) + ret = nbDispatch(adapter, ncb); + else + ret = NRC_BRIDGE; + ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret; + if (ncb->ncb_post) + ncb->ncb_post(ncb); + else if (ncb->ncb_event) + SetEvent(ncb->ncb_event); + } + return 0; +} + +UCHAR WINAPI Netbios(PNCB ncb) +{ + UCHAR ret, cmd; + + TRACE("ncb = %p\n", ncb); + + if (!ncb) return NRC_INVADDRESS; + + TRACE("ncb_command 0x%02x, ncb_lana_num %d, ncb_buffer %p, ncb_length %d\n", + ncb->ncb_command, ncb->ncb_lana_num, ncb->ncb_buffer, ncb->ncb_length); + cmd = ncb->ncb_command & 0x7f; + + if (cmd == NCBENUM) + ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = nbEnum(ncb); + else if (cmd == NCBADDNAME) + { + FIXME("NCBADDNAME: stub, returning success"); + ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = NRC_GOODRET; + } + else + { + NetBIOSAdapter *adapter; + + /* Apps not specifically written for WinNT won't do an NCBENUM first, + * so make sure the table has been enumerated at least once + */ + if (!gNBTable.enumerated) + nbInternalEnum(); + adapter = nbGetAdapter(ncb->ncb_lana_num); + if (!adapter) + ret = NRC_BRIDGE; + else + { + if (adapter->shuttingDown) + ret = NRC_IFBUSY; + else if (adapter->resetting) + ret = NRC_TOOMANY; + else + { + /* non-asynch commands first */ + if (cmd == NCBCANCEL) + ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = + nbCancel(adapter, ncb); + else if (cmd == NCBSSTAT) + ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = + nbSStat(adapter, ncb); + else + { + if (ncb->ncb_command & ASYNCH) + { + HANDLE thread = CreateThread(NULL, 0, nbCmdThread, ncb, + CREATE_SUSPENDED, NULL); + + if (thread != NULL) + { + ncb->ncb_retcode = ncb->ncb_cmd_cplt = NRC_PENDING; + if (ncb->ncb_event) + ResetEvent(ncb->ncb_event); + ResumeThread(thread); + CloseHandle(thread); + ret = NRC_GOODRET; + } + else + ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = + NRC_OSRESNOTAV; + } + else + ncb->ncb_retcode = ncb->ncb_cmd_cplt = ret = + nbDispatch(adapter, ncb); + } + } + } + } + TRACE("returning 0x%02x\n", ret); + return ret; +} diff --git a/reactos/dll/netapi32/netbios.h b/reactos/dll/netapi32/netbios.h new file mode 100644 index 00000000000..0130aaf701e --- /dev/null +++ b/reactos/dll/netapi32/netbios.h @@ -0,0 +1,183 @@ +/* Copyright (c) 2003 Juan Lang + * + * 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 + */ +#ifndef __WINE_NETBIOS_H__ +#define __WINE_NETBIOS_H__ + +#include +#include "windef.h" +#include "winbase.h" +#include "lm.h" +#include "nb30.h" + +/* This file describes the interface WINE's NetBIOS implementation uses to + * interact with a transport implementation (where a transport might be + * NetBIOS-over-TCP/IP (aka NetBT, NBT), NetBIOS-over-IPX, etc.) + */ + +/** + * Public functions + */ + +void NetBIOSInit(void); +void NetBIOSShutdown(void); + +struct _NetBIOSTransport; + +/* A transport should register itself during its init function (see below) with + * a unique id (the transport_id of ACTION_HEADER, for example) and an + * implementation. Returns TRUE on success, and FALSE on failure. + */ +BOOL NetBIOSRegisterTransport(ULONG id, struct _NetBIOSTransport *transport); + +/* Registers an adapter with the given transport and ifIndex with NetBIOS. + * ifIndex is an interface index usable by the IpHlpApi. ifIndex is not + * required to be unique, but is required so that NetWkstaTransportEnum can use + * GetIfEntry to get the name and hardware address of the adapter. + * Returns TRUE on success, FALSE on failure. + * FIXME: need functions for retrieving the name and hardware index, rather + * than assuming a correlation with IpHlpApi. + */ +BOOL NetBIOSRegisterAdapter(ULONG transport, DWORD ifIndex, void *adapter); + +/* During enumeration, all adapters from your transport are disabled + * internally. If an adapter is still valid, reenable it with this function. + * Adapters you don't enable will have their transport's NetBIOSCleanupAdapter + * function (see below) called on them, and will be removed from the table. + * (This is to deal with lack of plug-and-play--sorry.) + */ +void NetBIOSEnableAdapter(UCHAR lana); + +/* Gets a quick count of the number of NetBIOS adapters. Not guaranteed not + * to change from one call to the next, depending on what's been enumerated + * lately. See also NetBIOSEnumAdapters. + */ +UCHAR NetBIOSNumAdapters(void); + +typedef struct _NetBIOSAdapterImpl { + UCHAR lana; + DWORD ifIndex; + void *data; +} NetBIOSAdapterImpl; + +typedef BOOL (*NetBIOSEnumAdaptersCallback)(UCHAR totalLANAs, UCHAR lanaIndex, + ULONG transport, const NetBIOSAdapterImpl *data, void *closure); + +/* Enumerates all NetBIOS adapters for the transport transport, or for all + * transports if transport is ALL_TRANSPORTS. Your callback will be called + * once for every enumerated adapter, with a count of how many adapters have + * been enumerated, a 0-based index relative to that count, the adapter's + * transport, and its ifIndex. + * Your callback should return FALSE if it no longer wishes to be called. + */ +void NetBIOSEnumAdapters(ULONG transport, NetBIOSEnumAdaptersCallback cb, + void *closure); + +/* Hangs up the session identified in the NCB; the NCB need not be a NCBHANGUP. + * Will result in the transport's hangup function being called, so release any + * locks you own before calling to avoid deadlock. + * This function is intended for use by a transport, if the session is closed + * by some error in the transport layer. + */ +void NetBIOSHangupSession(PNCB ncb); + +/** + * Functions a transport implementation must implement + */ + +/* This function is called to ask a transport implementation to enumerate any + * LANAs into the NetBIOS adapter table by: + * - calling NetBIOSRegisterAdapter for any new adapters + * - calling NetBIOSEnableAdapter for any existing adapters + * NetBIOSEnumAdapters (see) may be of use to determine which adapters already + * exist. + * A transport can assume no other thread is modifying the NetBIOS adapter + * table during the lifetime of its NetBIOSEnum function (and, therefore, that + * this function won't be called reentrantly). + */ +typedef UCHAR (*NetBIOSEnum)(void); + +/* A cleanup function for a transport. This is the last function called on a + * transport. + */ +typedef void (*NetBIOSCleanup)(void); + +/* Adapter functions */ + +/* Functions with direct mappings to the Netbios interface. These functions + * are expected to be synchronous, although the first four bytes of the + * reserved member of the ncb are a cancel flag. A long-running function + * should check whether this is not FALSE from time to time (see the + * NCB_CANCELLED macro), and return NRC_CMDCAN if it's been cancelled. (The + * remainder of the NCB's reserved field is, well, reserved.) + */ + +/* Used to see whether the pointer to an NCB has been cancelled. The NetBIOS + * interface designates certain functions as non-cancellable functions, but I + * use this flag for all NCBs. Support it if you can. + * FIXME: this isn't enough, need to support an EVENT or some such, because + * some calls (recv) will block indefinitely, so a reset, shutdown, etc. will + * never occur. + */ +#define NCB_CANCELLED(pncb) *(PBOOL)((pncb)->ncb_reserve) + +typedef UCHAR (*NetBIOSAstat)(void *adapter, PNCB ncb); +typedef UCHAR (*NetBIOSFindName)(void *adapter, PNCB ncb); + +/* Functions to support the session service */ + +/* Implement to support the NCBCALL command. If you need data stored for the + * session, return it in *session. You can clean it up in your NetBIOSHangup + * function (see). + */ +typedef UCHAR (*NetBIOSCall)(void *adapter, PNCB ncb, void **session); +typedef UCHAR (*NetBIOSSend)(void *adapter, void *session, PNCB ncb); +typedef UCHAR (*NetBIOSRecv)(void *adapter, void *session, PNCB ncb); +typedef UCHAR (*NetBIOSHangup)(void *adapter, void *session); + +/* The last function called on an adapter; it is not called reentrantly, and + * no new calls will be made on the adapter once this has been entered. Clean + * up any resources allocated for the adapter here. + */ +typedef void (*NetBIOSCleanupAdapter)(void *adapter); + +typedef struct _NetBIOSTransport +{ + NetBIOSEnum enumerate; + NetBIOSAstat astat; + NetBIOSFindName findName; + NetBIOSCall call; + NetBIOSSend send; + NetBIOSRecv recv; + NetBIOSHangup hangup; + NetBIOSCleanupAdapter cleanupAdapter; + NetBIOSCleanup cleanup; +} NetBIOSTransport; + +/* Transport-specific functions. When adding a transport, add a call to its + * init function in netapi32's DllMain. The transport can do any global + * initialization it needs here. It should call NetBIOSRegisterTransport to + * register itself with NetBIOS. + */ + +/* NetBIOS-over-TCP/IP (NetBT) functions */ + +/* Not defined by MS, so make my own private define: */ +#define TRANSPORT_NBT "MNBT" + +void NetBTInit(void); + +#endif /* ndef __WINE_NETBIOS_H__ */ diff --git a/reactos/dll/netapi32/wksta.c b/reactos/dll/netapi32/wksta.c new file mode 100644 index 00000000000..425a9893c55 --- /dev/null +++ b/reactos/dll/netapi32/wksta.c @@ -0,0 +1,565 @@ +/* Copyright 2002 Andriy Palamarchuk + * Copyright (c) 2003 Juan Lang + * + * netapi32 user functions + * + * 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 "config.h" +#include "wine/port.h" + +#include +#include +#include "ntstatus.h" +#define WIN32_NO_STATUS +#include "windef.h" +#include "winbase.h" +#include "winsock2.h" +#include "nb30.h" +#include "lmcons.h" +#include "lmapibuf.h" +#include "lmerr.h" +#include "lmwksta.h" +#include "iphlpapi.h" +#include "winerror.h" +#include "winreg.h" +#include "ntsecapi.h" +#include "netbios.h" +#include "wine/debug.h" + +WINE_DEFAULT_DEBUG_CHANNEL(netapi32); + +/************************************************************ + * NETAPI_IsLocalComputer + * + * Checks whether the server name indicates local machine. + */ +BOOL NETAPI_IsLocalComputer(LPCWSTR ServerName) +{ + if (!ServerName) + { + return TRUE; + } + else if (ServerName[0] == '\0') + return TRUE; + else + { + DWORD dwSize = MAX_COMPUTERNAME_LENGTH + 1; + BOOL Result; + LPWSTR buf; + + NetApiBufferAllocate(dwSize * sizeof(WCHAR), (LPVOID *) &buf); + Result = GetComputerNameW(buf, &dwSize); + if (Result && (ServerName[0] == '\\') && (ServerName[1] == '\\')) + ServerName += 2; + Result = Result && !lstrcmpW(ServerName, buf); + NetApiBufferFree(buf); + + return Result; + } +} + +static void wprint_mac(WCHAR* buffer, int len, PMIB_IFROW ifRow) +{ + int i; + unsigned char val; + + if (!buffer) + return; + if (len < 1) + return; + if (!ifRow) + { + *buffer = '\0'; + return; + } + + for (i = 0; i < ifRow->dwPhysAddrLen && 2 * i < len; i++) + { + val = ifRow->bPhysAddr[i]; + if ((val >>4) >9) + buffer[2*i] = (WCHAR)((val >>4) + 'A' - 10); + else + buffer[2*i] = (WCHAR)((val >>4) + '0'); + if ((val & 0xf ) >9) + buffer[2*i+1] = (WCHAR)((val & 0xf) + 'A' - 10); + else + buffer[2*i+1] = (WCHAR)((val & 0xf) + '0'); + } + buffer[2*i]=(WCHAR)0; +} + +/* Theoretically this could be too short, except that MS defines + * MAX_ADAPTER_NAME as 128, and MAX_INTERFACE_NAME_LEN as 256, and both + * represent a count of WCHARs, so even with an extroardinarily long header + * this will be plenty + */ +#define MAX_TRANSPORT_NAME MAX_INTERFACE_NAME_LEN +#define MAX_TRANSPORT_ADDR 13 + +#define NBT_TRANSPORT_NAME_HEADER "\\Device\\NetBT_Tcpip_" +#define UNKNOWN_TRANSPORT_NAME_HEADER "\\Device\\UnknownTransport_" + +static void wprint_name(WCHAR *buffer, int len, ULONG transport, + PMIB_IFROW ifRow) +{ + WCHAR *ptr1, *ptr2; + const char *name; + + if (!buffer) + return; + if (!ifRow) + { + *buffer = '\0'; + return; + } + + if (!memcmp(&transport, TRANSPORT_NBT, sizeof(ULONG))) + name = NBT_TRANSPORT_NAME_HEADER; + else + name = UNKNOWN_TRANSPORT_NAME_HEADER; + + for (ptr1 = buffer; *name && ptr1 < buffer + len; ptr1++, name++) + *ptr1 = *name; + for (ptr2 = ifRow->wszName; *ptr2 && ptr1 < buffer + len; ptr1++, ptr2++) + *ptr1 = *ptr2; + *ptr1 = '\0'; +} + +/*********************************************************************** + * NetWkstaTransportEnum (NETAPI32.@) + */ + +struct WkstaTransportEnumData +{ + UCHAR n_adapt; + UCHAR n_read; + DWORD prefmaxlen; + LPBYTE *pbuf; + NET_API_STATUS ret; +}; + +/**********************************************************************/ + +static BOOL WkstaEnumAdaptersCallback(UCHAR totalLANAs, UCHAR lanaIndex, + ULONG transport, const NetBIOSAdapterImpl *data, void *closure) +{ + BOOL ret; + struct WkstaTransportEnumData *enumData = (struct WkstaTransportEnumData *) + closure; + + if (enumData && enumData->pbuf) + { + if (lanaIndex == 0) + { + DWORD toAllocate; + + enumData->n_adapt = totalLANAs; + enumData->n_read = 0; + + toAllocate = totalLANAs * (sizeof(WKSTA_TRANSPORT_INFO_0) + + MAX_TRANSPORT_NAME * sizeof(WCHAR) + + MAX_TRANSPORT_ADDR * sizeof(WCHAR)); + if (enumData->prefmaxlen != MAX_PREFERRED_LENGTH) + toAllocate = enumData->prefmaxlen; + NetApiBufferAllocate(toAllocate, (LPVOID *)enumData->pbuf); + } + if (*(enumData->pbuf)) + { + UCHAR spaceFor; + + if (enumData->prefmaxlen == MAX_PREFERRED_LENGTH) + spaceFor = totalLANAs; + else + spaceFor = enumData->prefmaxlen / + (sizeof(WKSTA_TRANSPORT_INFO_0) + (MAX_TRANSPORT_NAME + + MAX_TRANSPORT_ADDR) * sizeof(WCHAR)); + if (enumData->n_read < spaceFor) + { + PWKSTA_TRANSPORT_INFO_0 ti; + LPWSTR transport_name, transport_addr; + MIB_IFROW ifRow; + + ti = (PWKSTA_TRANSPORT_INFO_0)(*(enumData->pbuf) + + enumData->n_read * sizeof(WKSTA_TRANSPORT_INFO_0)); + transport_name = (LPWSTR)(*(enumData->pbuf) + + totalLANAs * sizeof(WKSTA_TRANSPORT_INFO_0) + + enumData->n_read * MAX_TRANSPORT_NAME * sizeof(WCHAR)); + transport_addr = (LPWSTR)(*(enumData->pbuf) + + totalLANAs * (sizeof(WKSTA_TRANSPORT_INFO_0) + + MAX_TRANSPORT_NAME * sizeof(WCHAR)) + + enumData->n_read * MAX_TRANSPORT_ADDR * sizeof(WCHAR)); + + ifRow.dwIndex = data->ifIndex; + GetIfEntry(&ifRow); + ti->wkti0_quality_of_service = 0; + ti->wkti0_number_of_vcs = 0; + ti->wkti0_transport_name = transport_name; + wprint_name(ti->wkti0_transport_name, MAX_TRANSPORT_NAME, + transport, &ifRow); + ti->wkti0_transport_address = transport_addr; + wprint_mac(ti->wkti0_transport_address, MAX_TRANSPORT_ADDR, + &ifRow); + if (!memcmp(&transport, TRANSPORT_NBT, sizeof(ULONG))) + ti->wkti0_wan_ish = TRUE; + else + ti->wkti0_wan_ish = FALSE; + TRACE("%d of %d:ti at %p\n", lanaIndex, totalLANAs, ti); + TRACE("transport_name at %p %s\n", + ti->wkti0_transport_name, + debugstr_w(ti->wkti0_transport_name)); + TRACE("transport_address at %p %s\n", + ti->wkti0_transport_address, + debugstr_w(ti->wkti0_transport_address)); + enumData->n_read++; + enumData->ret = NERR_Success; + ret = TRUE; + } + else + { + enumData->ret = ERROR_MORE_DATA; + ret = FALSE; + } + } + else + { + enumData->ret = ERROR_OUTOFMEMORY; + ret = FALSE; + } + } + else + ret = FALSE; + return ret; +} + +/**********************************************************************/ + +NET_API_STATUS WINAPI +NetWkstaTransportEnum(LPWSTR ServerName, DWORD level, PBYTE* pbuf, + DWORD prefmaxlen, LPDWORD read_entries, + PDWORD total_entries, PDWORD hresume) +{ + NET_API_STATUS ret; + + TRACE(":%s, 0x%08lx, %p, 0x%08lx, %p, %p, %p\n", debugstr_w(ServerName), + level, pbuf, prefmaxlen, read_entries, total_entries,hresume); + if (!NETAPI_IsLocalComputer(ServerName)) + { + FIXME(":not implemented for non-local computers\n"); + ret = ERROR_INVALID_LEVEL; + } + else + { + if (hresume && *hresume) + { + FIXME(":resume handle not implemented\n"); + return ERROR_INVALID_LEVEL; + } + + switch (level) + { + case 0: /* transport info */ + { + ULONG allTransports; + struct WkstaTransportEnumData enumData; + + if (NetBIOSNumAdapters() == 0) + return ERROR_NETWORK_UNREACHABLE; + if (!read_entries) + return STATUS_ACCESS_VIOLATION; + if (!total_entries || !pbuf) + return RPC_X_NULL_REF_POINTER; + + enumData.prefmaxlen = prefmaxlen; + enumData.pbuf = pbuf; + memcpy(&allTransports, ALL_TRANSPORTS, sizeof(ULONG)); + NetBIOSEnumAdapters(allTransports, WkstaEnumAdaptersCallback, + &enumData); + *read_entries = enumData.n_read; + *total_entries = enumData.n_adapt; + if (hresume) *hresume= 0; + ret = enumData.ret; + break; + } + default: + ERR("Invalid level %ld is specified\n", level); + ret = ERROR_INVALID_LEVEL; + } + } + return ret; +} + + +/************************************************************ + * NetWkstaUserGetInfo (NETAPI32.@) + */ +NET_API_STATUS WINAPI NetWkstaUserGetInfo(LPWSTR reserved, DWORD level, + PBYTE* bufptr) +{ + TRACE("(%s, %ld, %p)\n", debugstr_w(reserved), level, bufptr); + switch (level) + { + case 0: + { + PWKSTA_USER_INFO_0 ui; + DWORD dwSize = UNLEN + 1; + + /* set up buffer */ + NetApiBufferAllocate(sizeof(WKSTA_USER_INFO_0) + dwSize * sizeof(WCHAR), + (LPVOID *) bufptr); + + ui = (PWKSTA_USER_INFO_0) *bufptr; + ui->wkui0_username = (LPWSTR) (*bufptr + sizeof(WKSTA_USER_INFO_0)); + + /* get data */ + if (!GetUserNameW(ui->wkui0_username, &dwSize)) + { + NetApiBufferFree(ui); + return ERROR_NOT_ENOUGH_MEMORY; + } + else + NetApiBufferReallocate( + *bufptr, sizeof(WKSTA_USER_INFO_0) + + (lstrlenW(ui->wkui0_username) + 1) * sizeof(WCHAR), + (LPVOID *) bufptr); + break; + } + + case 1: + { + PWKSTA_USER_INFO_1 ui; + PWKSTA_USER_INFO_0 ui0; + DWORD dwSize; + LSA_OBJECT_ATTRIBUTES ObjectAttributes; + LSA_HANDLE PolicyHandle; + PPOLICY_ACCOUNT_DOMAIN_INFO DomainInfo; + NTSTATUS NtStatus; + + /* sizes of the field buffers in WCHARS */ + int username_sz, logon_domain_sz, oth_domains_sz, logon_server_sz; + + FIXME("Level 1 processing is partially implemented\n"); + oth_domains_sz = 1; + logon_server_sz = 1; + + /* get some information first to estimate size of the buffer */ + ui0 = NULL; + NetWkstaUserGetInfo(NULL, 0, (PBYTE *) &ui0); + username_sz = lstrlenW(ui0->wkui0_username) + 1; + + ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes)); + NtStatus = LsaOpenPolicy(NULL, &ObjectAttributes, + POLICY_VIEW_LOCAL_INFORMATION, + &PolicyHandle); + if (NtStatus != STATUS_SUCCESS) + { + ERR("LsaOpenPolicyFailed with NT status %lx\n", + LsaNtStatusToWinError(NtStatus)); + NetApiBufferFree(ui0); + return ERROR_NOT_ENOUGH_MEMORY; + } + LsaQueryInformationPolicy(PolicyHandle, PolicyAccountDomainInformation, + (PVOID*) &DomainInfo); + logon_domain_sz = lstrlenW(DomainInfo->DomainName.Buffer) + 1; + LsaClose(PolicyHandle); + + /* set up buffer */ + NetApiBufferAllocate(sizeof(WKSTA_USER_INFO_1) + + (username_sz + logon_domain_sz + + oth_domains_sz + logon_server_sz) * sizeof(WCHAR), + (LPVOID *) bufptr); + ui = (WKSTA_USER_INFO_1 *) *bufptr; + ui->wkui1_username = (LPWSTR) (*bufptr + sizeof(WKSTA_USER_INFO_1)); + ui->wkui1_logon_domain = (LPWSTR) ( + ((PBYTE) ui->wkui1_username) + username_sz * sizeof(WCHAR)); + ui->wkui1_oth_domains = (LPWSTR) ( + ((PBYTE) ui->wkui1_logon_domain) + + logon_domain_sz * sizeof(WCHAR)); + ui->wkui1_logon_server = (LPWSTR) ( + ((PBYTE) ui->wkui1_oth_domains) + + oth_domains_sz * sizeof(WCHAR)); + + /* get data */ + dwSize = username_sz; + lstrcpyW(ui->wkui1_username, ui0->wkui0_username); + NetApiBufferFree(ui0); + + lstrcpynW(ui->wkui1_logon_domain, DomainInfo->DomainName.Buffer, + logon_domain_sz); + LsaFreeMemory(DomainInfo); + + /* FIXME. Not implemented. Populated with empty strings */ + ui->wkui1_oth_domains[0] = 0; + ui->wkui1_logon_server[0] = 0; + break; + } + case 1101: + { + PWKSTA_USER_INFO_1101 ui; + DWORD dwSize = 1; + + FIXME("Stub. Level 1101 processing is not implemented\n"); + /* FIXME see also wkui1_oth_domains for level 1 */ + + /* set up buffer */ + NetApiBufferAllocate(sizeof(WKSTA_USER_INFO_1101) + dwSize * sizeof(WCHAR), + (LPVOID *) bufptr); + + ui = (PWKSTA_USER_INFO_1101) *bufptr; + ui->wkui1101_oth_domains = (LPWSTR)(ui + 1); + + /* get data */ + ui->wkui1101_oth_domains[0] = 0; + break; + } + default: + ERR("Invalid level %ld is specified\n", level); + return ERROR_INVALID_LEVEL; + } + return NERR_Success; +} + +/************************************************************ + * NetpGetComputerName (NETAPI32.@) + */ +NET_API_STATUS WINAPI NetpGetComputerName(LPWSTR *Buffer) +{ + DWORD dwSize = MAX_COMPUTERNAME_LENGTH + 1; + + TRACE("(%p)\n", Buffer); + NetApiBufferAllocate(dwSize * sizeof(WCHAR), (LPVOID *) Buffer); + if (GetComputerNameW(*Buffer, &dwSize)) + { + NetApiBufferReallocate( + *Buffer, dwSize * sizeof(WCHAR), + (LPVOID *) Buffer); + return NERR_Success; + } + else + { + NetApiBufferFree(*Buffer); + return ERROR_NOT_ENOUGH_MEMORY; + } +} + +NET_API_STATUS WINAPI I_NetNameCompare(LPVOID p1, LPWSTR wkgrp, LPWSTR comp, + LPVOID p4, LPVOID p5) +{ + FIXME("(%p %s %s %p %p): stub\n", p1, debugstr_w(wkgrp), debugstr_w(comp), + p4, p5); + return ERROR_INVALID_PARAMETER; +} + +NET_API_STATUS WINAPI I_NetNameValidate(LPVOID p1, LPWSTR wkgrp, LPVOID p3, + LPVOID p4) +{ + FIXME("(%p %s %p %p): stub\n", p1, debugstr_w(wkgrp), p3, p4); + return ERROR_INVALID_PARAMETER; +} + +NET_API_STATUS WINAPI NetWkstaGetInfo( LPWSTR servername, DWORD level, + LPBYTE* bufptr) +{ + NET_API_STATUS ret; + + TRACE("%s %ld %p\n", debugstr_w( servername ), level, bufptr ); + if (servername) + { + if (!NETAPI_IsLocalComputer(servername)) + { + FIXME("remote computers not supported\n"); + return ERROR_INVALID_LEVEL; + } + } + if (!bufptr) return ERROR_INVALID_PARAMETER; + + switch (level) + { + case 100: + { + DWORD computerNameLen, domainNameLen, size; + WCHAR computerName[MAX_COMPUTERNAME_LENGTH + 1]; + LSA_OBJECT_ATTRIBUTES ObjectAttributes; + LSA_HANDLE PolicyHandle; + NTSTATUS NtStatus; + + computerNameLen = MAX_COMPUTERNAME_LENGTH + 1; + GetComputerNameW(computerName, &computerNameLen); + computerNameLen++; /* include NULL terminator */ + + ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes)); + NtStatus = LsaOpenPolicy(NULL, &ObjectAttributes, + POLICY_VIEW_LOCAL_INFORMATION, &PolicyHandle); + if (NtStatus != STATUS_SUCCESS) + ret = LsaNtStatusToWinError(NtStatus); + else + { + PPOLICY_ACCOUNT_DOMAIN_INFO DomainInfo; + + LsaQueryInformationPolicy(PolicyHandle, + PolicyAccountDomainInformation, (PVOID*)&DomainInfo); + domainNameLen = lstrlenW(DomainInfo->DomainName.Buffer) + 1; + size = sizeof(WKSTA_INFO_100) + computerNameLen * sizeof(WCHAR) + + domainNameLen * sizeof(WCHAR); + ret = NetApiBufferAllocate(size, (LPVOID *)bufptr); + if (ret == NERR_Success) + { + PWKSTA_INFO_100 info = (PWKSTA_INFO_100)*bufptr; + OSVERSIONINFOW verInfo; + + info->wki100_platform_id = PLATFORM_ID_NT; + info->wki100_computername = (LPWSTR)(*bufptr + + sizeof(WKSTA_INFO_100)); + memcpy(info->wki100_computername, computerName, + computerNameLen * sizeof(WCHAR)); + info->wki100_langroup = (LPWSTR)(*bufptr + + sizeof(WKSTA_INFO_100) + computerNameLen * sizeof(WCHAR)); + memcpy(info->wki100_langroup, DomainInfo->DomainName.Buffer, + domainNameLen * sizeof(WCHAR)); + memset(&verInfo, 0, sizeof(verInfo)); + verInfo.dwOSVersionInfoSize = sizeof(verInfo); + GetVersionExW(&verInfo); + info->wki100_ver_major = verInfo.dwMajorVersion; + info->wki100_ver_minor = verInfo.dwMinorVersion; + } + LsaFreeMemory(DomainInfo); + LsaClose(PolicyHandle); + } + break; + } + + default: + FIXME("level %ld unimplemented\n", level); + ret = ERROR_INVALID_LEVEL; + } + return ret; +} + +/************************************************************ + * NetGetJoinInformation (NETAPI32.@) + */ +NET_API_STATUS NET_API_FUNCTION NetGetJoinInformation( + LPCWSTR Server, + LPWSTR *Name, + PNETSETUP_JOIN_STATUS type) +{ + FIXME("Stub %s %p %p\n", wine_dbgstr_w(Server), Name, type); + + *Name = NULL; + *type = NetSetupUnknownStatus; + + return NERR_Success; +} diff --git a/reactos/dll/netcfgx/netcfgx.c b/reactos/dll/netcfgx/netcfgx.c new file mode 100644 index 00000000000..22cb4e023cf --- /dev/null +++ b/reactos/dll/netcfgx/netcfgx.c @@ -0,0 +1,376 @@ +/* + * COPYRIGHT: See COPYING in the top level directory + * PROJECT: ReactOS Configuration of networkd devices + * FILE: lib/netcfgx/netcfgx.c + * PURPOSE: Network devices installer + * + * PROGRAMMERS: Hervé Poussineau (hpoussin@reactos.org) + */ + +#define NDEBUG +#include + +#include "netcfgx.h" + +/* Append a REG_SZ to an existing REG_MULTI_SZ string in the registry. + * If the value doesn't exist, create it. + * Returns ERROR_SUCCESS if success. Othewise, returns an error code + */ +static LONG +AppendStringToMultiSZ( + IN HKEY hKey, + IN PCWSTR ValueName, + IN PCWSTR ValueToAppend) +{ + PWSTR Buffer = NULL; + DWORD dwRegType; + DWORD dwRequired, dwLength; + DWORD dwTmp; + LONG rc; + + rc = RegQueryValueExW( + hKey, + ValueName, + NULL, + &dwRegType, + NULL, + &dwRequired); + if (rc != ERROR_FILE_NOT_FOUND) + { + if (rc != ERROR_SUCCESS) + goto cleanup; + if (dwRegType != REG_MULTI_SZ) + { + rc = ERROR_GEN_FAILURE; + goto cleanup; + } + + dwTmp = dwLength = dwRequired + wcslen(ValueToAppend) * sizeof(WCHAR) + sizeof(UNICODE_NULL); + Buffer = HeapAlloc(GetProcessHeap(), 0, dwLength); + if (!Buffer) + { + rc = ERROR_NOT_ENOUGH_MEMORY; + goto cleanup; + } + rc = RegQueryValueExW( + hKey, + ValueName, + NULL, + NULL, + (BYTE*)Buffer, + &dwTmp); + if (rc != ERROR_SUCCESS) + goto cleanup; + } + else + { + dwRequired = sizeof(WCHAR); + dwLength = wcslen(ValueToAppend) * sizeof(WCHAR) + 2 * sizeof(UNICODE_NULL); + Buffer = HeapAlloc(GetProcessHeap(), 0, dwLength); + if (!Buffer) + { + rc = ERROR_NOT_ENOUGH_MEMORY; + goto cleanup; + } + } + + /* Append the value */ + wcscpy(&Buffer[dwRequired / sizeof(WCHAR) - 1], ValueToAppend); + /* Terminate the REG_MULTI_SZ string */ + Buffer[dwLength / sizeof(WCHAR) - 1] = UNICODE_NULL; + + rc = RegSetValueExW( + hKey, + ValueName, + 0, + REG_MULTI_SZ, + (const BYTE*)Buffer, + dwLength); + +cleanup: + HeapFree(GetProcessHeap(), 0, Buffer); + return rc; +} + +DWORD WINAPI +NetClassInstaller( + IN DI_FUNCTION InstallFunction, + IN HDEVINFO DeviceInfoSet, + IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL) +{ + RPC_STATUS RpcStatus; + UUID Uuid; + LPWSTR InstanceId = NULL; + LPWSTR UuidRpcString = NULL; + LPWSTR UuidString = NULL; + LPWSTR DeviceName = NULL; + LPWSTR ExportName = NULL; + LONG rc; + DWORD dwShowIcon, dwLength; + HKEY hKey = INVALID_HANDLE_VALUE; + HKEY hLinkageKey = INVALID_HANDLE_VALUE; + HKEY hNetworkKey = INVALID_HANDLE_VALUE; + HKEY hConnectionKey = INVALID_HANDLE_VALUE; + + if (InstallFunction != DIF_INSTALLDEVICE) + return ERROR_DI_DO_DEFAULT; + + DPRINT("%lu %p %p\n", InstallFunction, DeviceInfoSet, DeviceInfoData); + + /* Get Instance ID */ + if (SetupDiGetDeviceInstanceIdW(DeviceInfoSet, DeviceInfoData, NULL, 0, &dwLength)) + { + DPRINT("SetupDiGetDeviceInstanceIdW() returned TRUE. FALSE expected\n"); + rc = ERROR_GEN_FAILURE; + goto cleanup; + } + InstanceId = HeapAlloc(GetProcessHeap(), 0, dwLength); + if (!InstanceId) + { + DPRINT("HeapAlloc() failed\n"); + rc = ERROR_NOT_ENOUGH_MEMORY; + goto cleanup; + } + if (!SetupDiGetDeviceInstanceIdW(DeviceInfoSet, DeviceInfoData, InstanceId, dwLength, NULL)) + { + rc = GetLastError(); + DPRINT("SetupDiGetDeviceInstanceIdW() failed with error 0x%lx\n", rc); + goto cleanup; + } + + /* Create a new UUID */ + RpcStatus = UuidCreate(&Uuid); + if (RpcStatus != RPC_S_OK && RpcStatus != RPC_S_UUID_LOCAL_ONLY) + { + DPRINT("UuidCreate() failed with RPC status 0x%lx\n", RpcStatus); + rc = ERROR_GEN_FAILURE; + goto cleanup; + } + RpcStatus = UuidToStringW(&Uuid, &UuidRpcString); + if (RpcStatus != RPC_S_OK) + { + DPRINT("UuidToStringW() failed with RPC status 0x%lx\n", RpcStatus); + rc = ERROR_GEN_FAILURE; + goto cleanup; + } + + /* Add curly braces around Uuid */ + UuidString = HeapAlloc(GetProcessHeap(), 0, (2 + wcslen(UuidRpcString)) * sizeof(WCHAR) + sizeof(UNICODE_NULL)); + if (!UuidString) + { + DPRINT("HeapAlloc() failed\n"); + rc = ERROR_NOT_ENOUGH_MEMORY; + goto cleanup; + } + wcscpy(UuidString, L"{"); + wcscat(UuidString, UuidRpcString); + wcscat(UuidString, L"}"); + + /* Create device name */ + DeviceName = HeapAlloc(GetProcessHeap(), 0, (wcslen(L"\\Device\\") + wcslen(UuidString)) * sizeof(WCHAR) + sizeof(UNICODE_NULL)); + if (!DeviceName) + { + DPRINT("HeapAlloc() failed\n"); + rc = ERROR_NOT_ENOUGH_MEMORY; + goto cleanup; + } + wcscpy(DeviceName, L"\\Device\\"); + wcscat(DeviceName, UuidString); + + /* Create export name */ + ExportName = HeapAlloc(GetProcessHeap(), 0, (wcslen(L"\\Device\\Tcpip_") + wcslen(UuidString)) * sizeof(WCHAR) + sizeof(UNICODE_NULL)); + if (!ExportName) + { + DPRINT("HeapAlloc() failed\n"); + rc = ERROR_NOT_ENOUGH_MEMORY; + goto cleanup; + } + wcscpy(ExportName, L"\\Device\\Tcpip_"); + wcscat(ExportName, UuidString); + + /* Write Tcpip parameters in new service Key */ + rc = RegCreateKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services", 0, NULL, REG_OPTION_NON_VOLATILE, 0, NULL, &hKey, NULL); + if (rc != ERROR_SUCCESS) + { + DPRINT("RegCreateKeyExW() failed with error 0x%lx\n", rc); + goto cleanup; + } + rc = RegCreateKeyExW(hKey, UuidString, 0, NULL, REG_OPTION_NON_VOLATILE, 0, NULL, &hNetworkKey, NULL); + if (rc != ERROR_SUCCESS) + { + DPRINT("RegCreateKeyExW() failed with error 0x%lx\n", rc); + goto cleanup; + } + RegCloseKey(hKey); + hKey = INVALID_HANDLE_VALUE; + rc = RegCreateKeyExW(hNetworkKey, L"Parameters\\Tcpip", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hKey, NULL); + if (rc != ERROR_SUCCESS) + { + DPRINT("RegCreateKeyExW() failed with error 0x%lx\n", rc); + goto cleanup; + } + RegCloseKey(hNetworkKey); + hNetworkKey = INVALID_HANDLE_VALUE; + rc = RegSetValueExW(hKey, L"DefaultGateway", 0, REG_SZ, (const BYTE*)L"0.0.0.0", (wcslen(L"0.0.0.0") + 1) * sizeof(WCHAR)); + if (rc != ERROR_SUCCESS) + { + DPRINT("RegSetValueExW() failed with error 0x%lx\n", rc); + goto cleanup; + } + rc = RegSetValueExW(hKey, L"IPAddress", 0, REG_SZ, (const BYTE*)L"0.0.0.0", (wcslen(L"0.0.0.0") + 1) * sizeof(WCHAR)); + if (rc != ERROR_SUCCESS) + { + DPRINT("RegSetValueExW() failed with error 0x%lx\n", rc); + goto cleanup; + } + rc = RegSetValueExW(hKey, L"SubnetMask", 0, REG_SZ, (const BYTE*)L"0.0.0.0", (wcslen(L"0.0.0.0") + 1) * sizeof(WCHAR)); + if (rc != ERROR_SUCCESS) + { + DPRINT("RegSetValueExW() failed with error 0x%lx\n", rc); + goto cleanup; + } + RegCloseKey(hKey); + hKey = INVALID_HANDLE_VALUE; + + /* Write 'Linkage' key in hardware key */ +#if _WIN32_WINNT >= 0x502 + hKey = SetupDiOpenDevRegKey(DeviceInfoSet, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_READ | KEY_WRITE); +#else + hKey = SetupDiOpenDevRegKey(DeviceInfoSet, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, KEY_ALL_ACCESS); +#endif + if (hKey == INVALID_HANDLE_VALUE && GetLastError() == ERROR_FILE_NOT_FOUND) + hKey = SetupDiCreateDevRegKeyW(DeviceInfoSet, DeviceInfoData, DICS_FLAG_GLOBAL, 0, DIREG_DRV, NULL, NULL); + if (hKey == INVALID_HANDLE_VALUE) + { + rc = GetLastError(); + DPRINT("SetupDiCreateDevRegKeyW() failed with error 0x%lx\n", rc); + goto cleanup; + } + rc = RegSetValueExW(hKey, L"NetCfgInstanceId", 0, REG_SZ, (const BYTE*)UuidString, (wcslen(UuidString) + 1) * sizeof(WCHAR)); + if (rc != ERROR_SUCCESS) + { + DPRINT("RegSetValueExW() failed with error 0x%lx\n", rc); + goto cleanup; + } + rc = RegCreateKeyExW(hKey, L"Linkage", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hLinkageKey, NULL); + if (rc != ERROR_SUCCESS) + { + DPRINT("RegCreateKeyExW() failed with error 0x%lx\n", rc); + goto cleanup; + } + rc = RegSetValueExW(hLinkageKey, L"Export", 0, REG_SZ, (const BYTE*)DeviceName, (wcslen(DeviceName) + 1) * sizeof(WCHAR)); + if (rc != ERROR_SUCCESS) + { + DPRINT("RegSetValueExW() failed with error 0x%lx\n", rc); + goto cleanup; + } + rc = RegSetValueExW(hLinkageKey, L"RootDevice", 0, REG_SZ, (const BYTE*)UuidString, (wcslen(UuidString) + 1) * sizeof(WCHAR)); + if (rc != ERROR_SUCCESS) + { + DPRINT("RegSetValueExW() failed with error 0x%lx\n", rc); + goto cleanup; + } + rc = RegSetValueExW(hLinkageKey, L"UpperBind", 0, REG_SZ, (const BYTE*)L"Tcpip", (wcslen(L"Tcpip") + 1) * sizeof(WCHAR)); + if (rc != ERROR_SUCCESS) + { + DPRINT("RegSetValueExW() failed with error 0x%lx\n", rc); + goto cleanup; + } + RegCloseKey(hKey); + hKey = INVALID_HANDLE_VALUE; + + /* Write connection information in network subkey */ + rc = RegCreateKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Control\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}", 0, NULL, REG_OPTION_NON_VOLATILE, 0, NULL, &hNetworkKey, NULL); + if (rc != ERROR_SUCCESS) + { + DPRINT("RegCreateKeyExW() failed with error 0x%lx\n", rc); + goto cleanup; + } + rc = RegCreateKeyExW(hNetworkKey, UuidString, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_CREATE_SUB_KEY, NULL, &hKey, NULL); + if (rc != ERROR_SUCCESS) + { + DPRINT("RegCreateKeyExW() failed with error 0x%lx\n", rc); + goto cleanup; + } + rc = RegCreateKeyExW(hKey, L"Connection", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hConnectionKey, NULL); + RegCloseKey(hKey); + hKey = INVALID_HANDLE_VALUE; + if (rc != ERROR_SUCCESS) + { + DPRINT("RegCreateKeyExW() failed with error 0x%lx\n", rc); + goto cleanup; + } + rc = RegSetValueExW(hConnectionKey, L"Name", 0, REG_SZ, (const BYTE*)L"Network connection", (wcslen(L"Network connection") + 1) * sizeof(WCHAR)); + if (rc != ERROR_SUCCESS) + { + DPRINT("RegSetValueExW() failed with error 0x%lx\n", rc); + goto cleanup; + } + rc = RegSetValueExW(hConnectionKey, L"PnpInstanceId", 0, REG_SZ, (const BYTE*)InstanceId, (wcslen(InstanceId) + 1) * sizeof(WCHAR)); + if (rc != ERROR_SUCCESS) + { + DPRINT("RegSetValueExW() failed with error 0x%lx\n", rc); + goto cleanup; + } + dwShowIcon = 1; + rc = RegSetValueExW(hConnectionKey, L"ShowIcon", 0, REG_DWORD, (const BYTE*)&dwShowIcon, sizeof(dwShowIcon)); + if (rc != ERROR_SUCCESS) + { + DPRINT("RegSetValueExW() failed with error 0x%lx\n", rc); + goto cleanup; + } + + /* Write linkage information in Tcpip service */ + rc = RegCreateKeyExW(HKEY_LOCAL_MACHINE, L"SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Linkage", 0, NULL, REG_OPTION_NON_VOLATILE, KEY_QUERY_VALUE | KEY_SET_VALUE, NULL, &hKey, NULL); + if (rc != ERROR_SUCCESS) + { + DPRINT("RegCreateKeyExW() failed with error 0x%lx\n", rc); + goto cleanup; + } + rc = AppendStringToMultiSZ(hKey, L"Bind", DeviceName); + if (rc != ERROR_SUCCESS) + { + DPRINT("AppendStringToMultiSZ() failed with error 0x%lx\n", rc); + goto cleanup; + } + rc = AppendStringToMultiSZ(hKey, L"Export", ExportName); + if (rc != ERROR_SUCCESS) + { + DPRINT("AppendStringToMultiSZ() failed with error 0x%lx\n", rc); + goto cleanup; + } + rc = AppendStringToMultiSZ(hKey, L"Route", UuidString); + if (rc != ERROR_SUCCESS) + { + DPRINT("AppendStringToMultiSZ() failed with error 0x%lx\n", rc); + goto cleanup; + } + + /* HACK: hpoussin, Dec 2005. TCP/IP driver is not able to manage devices + * which are installed after its startup. So, we have to reboot to take + * this new netcard into account. + */ + MessageBox(NULL, TEXT("You need to reboot to finish the installation of your network card."), TEXT("Reboot required"), MB_OK | MB_ICONWARNING); + rc = ERROR_SUCCESS; + +cleanup: + if (UuidRpcString != NULL) + RpcStringFreeW(&UuidRpcString); + HeapFree(GetProcessHeap(), 0, InstanceId); + HeapFree(GetProcessHeap(), 0, UuidString); + HeapFree(GetProcessHeap(), 0, DeviceName); + HeapFree(GetProcessHeap(), 0, ExportName); + if (hKey != INVALID_HANDLE_VALUE) + RegCloseKey(hKey); + if (hLinkageKey != INVALID_HANDLE_VALUE) + RegCloseKey(hLinkageKey); + if (hNetworkKey != INVALID_HANDLE_VALUE) + RegCloseKey(hNetworkKey); + if (hConnectionKey != INVALID_HANDLE_VALUE) + RegCloseKey(hConnectionKey); + + if (rc == ERROR_SUCCESS) + rc = ERROR_DI_DO_DEFAULT; + DPRINT("Returning 0x%lx\n", rc); + return rc; +} diff --git a/reactos/dll/netcfgx/netcfgx.def b/reactos/dll/netcfgx/netcfgx.def new file mode 100644 index 00000000000..0f866b5f9d6 --- /dev/null +++ b/reactos/dll/netcfgx/netcfgx.def @@ -0,0 +1,6 @@ +LIBRARY netcfgx.dll + +EXPORTS +NetClassInstaller@12 + +;EOF \ No newline at end of file diff --git a/reactos/dll/netcfgx/netcfgx.h b/reactos/dll/netcfgx/netcfgx.h new file mode 100644 index 00000000000..518182042ca --- /dev/null +++ b/reactos/dll/netcfgx/netcfgx.h @@ -0,0 +1,5 @@ +#include +#include + +ULONG DbgPrint(PCH Format,...); + diff --git a/reactos/dll/netcfgx/netcfgx.xml b/reactos/dll/netcfgx/netcfgx.xml new file mode 100644 index 00000000000..d974d63f983 --- /dev/null +++ b/reactos/dll/netcfgx/netcfgx.xml @@ -0,0 +1,8 @@ + + + + netcfgx.c + ntdll + rpcrt4 + setupapi + diff --git a/reactos/dll/newdev/De.rc b/reactos/dll/newdev/De.rc new file mode 100644 index 00000000000..dbcf62957d6 --- /dev/null +++ b/reactos/dll/newdev/De.rc @@ -0,0 +1,74 @@ +LANGUAGE LANG_GERMAN, SUBLANG_NEUTRAL + +IDD_WELCOMEPAGE DIALOG DISCARDABLE 0, 0, 317, 193 +STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +CAPTION "Hardwareinstallation" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Willkommen",IDC_WELCOMETITLE,120,8,195,24 + LTEXT "Dieser Assistent installiert einen neuen Gerätetreiber für:", + IDC_STATIC,120,40,195,16 + LTEXT "Klicken Sie auf Weiter um fortzufahren.",IDC_STATIC,120,169,195,17 + LTEXT "UNKNOWN DEVICE",IDC_DEVICE,134,55,164,11 + CONTROL "Treiber automatisch installieren",IDC_RADIO_AUTO,"Button", + BS_AUTORADIOBUTTON,120,112,178,13 + CONTROL "Treiber von einer bestimmten Position installieren",IDC_RADIO_MANUAL, + "Button",BS_AUTORADIOBUTTON,120,133,164,14 +END + +IDD_NODRIVER DIALOG DISCARDABLE 0, 0, 317, 193 +STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +CAPTION "Hardwareinstallation" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Installation fehlgeschlagen",IDC_FINISHTITLE,120,8,195,24 + LTEXT "Der Gerät konnte nicht installiert werden, da kein passender Treiber gefunden werden konnte.", + IDC_STATIC,120,40,195,19 + LTEXT "Klicken Sie auf Zurück, wenn Sie eine Treiber CD oder Diskette haben oder den Pfad zum Treiber kennen.", + IDC_STATIC,120,98,181,24 + CONTROL "Diesen Dialog nicht mehr anzeigen",IDC_DONOTSHOWDLG,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,120,170,180,11 +END + +IDD_CHSOURCE DIALOG DISCARDABLE 0, 0, 317, 143 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Hardwareinstallation" +FONT 8, "MS Shell Dlg" +BEGIN + CONTROL "Diese Pfade durchsuchen",IDC_RADIO_SEARCHHERE,"Button", + BS_AUTORADIOBUTTON,30,11,239,13 + CONTROL "Treiber manuell auswählen",IDC_RADIO_CHOOSE,"Button", + BS_AUTORADIOBUTTON,30,98,171,12 + CONTROL "Wechseldatenträger durchsuchen",IDC_CHECK_MEDIA,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,47,33,163,9 + CONTROL "Diesen Pfad ebenfalls durchsuchen",IDC_CHECK_PATH,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,50,54,162,10 + PUSHBUTTON "Durchsuchen",IDC_BROWSE,248,69,50,14 + COMBOBOX IDC_COMBO_PATH,64,71,176,12,CBS_DROPDOWN | CBS_SORT | + WS_VSCROLL | WS_TABSTOP +END + +IDD_SEARCHDRV DIALOG DISCARDABLE 0, 0, 317, 143 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Hardwareinstallation" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "UNKNOWN DEVICE",IDC_DEVICE,51,20,169,16 +END + +IDD_FINISHPAGE DIALOG DISCARDABLE 0, 0, 317, 193 +STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +CAPTION "Hardwareinstallation" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Installation abgeschlossen",IDC_FINISHTITLE,120,8,195,11 + LTEXT "Der Assistent hat die Installation erfolgreich abgeschlossen.", + IDC_STATIC,120,32,195,19 + LTEXT "Klicken Sie zum Beenden der Installation auf Fertigstellen.",IDC_STATIC,120,174, + 190,11 + LTEXT "UNKNOWN DEVICE",IDC_DEVICE,148,53,147,12 +END + diff --git a/reactos/dll/newdev/En.rc b/reactos/dll/newdev/En.rc new file mode 100644 index 00000000000..13839dbb21f --- /dev/null +++ b/reactos/dll/newdev/En.rc @@ -0,0 +1,74 @@ +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +IDD_WELCOMEPAGE DIALOG DISCARDABLE 0, 0, 317, 193 +STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +CAPTION "Device installation" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Welcome",IDC_WELCOMETITLE,120,8,195,24 + LTEXT "This wizard installs a new device driver for:", + IDC_STATIC,120,40,195,16 + LTEXT "Click Next to continue.",IDC_STATIC,120,169,195,17 + LTEXT "UNKNOWN DEVICE",IDC_DEVICE,134,55,164,11 + CONTROL "Install driver automatically",IDC_RADIO_AUTO,"Button", + BS_AUTORADIOBUTTON,120,112,178,13 + CONTROL "Install driver from specific location",IDC_RADIO_MANUAL, + "Button",BS_AUTORADIOBUTTON,120,133,164,14 +END + +IDD_NODRIVER DIALOG DISCARDABLE 0, 0, 317, 193 +STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +CAPTION "Device installation" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Installation failed",IDC_FINISHTITLE,120,8,195,24 + LTEXT "The device could not be installed because the driver could not be found.", + IDC_STATIC,120,40,195,19 + LTEXT "Click on Back if you have a driver disk or know the path to the driver.", + IDC_STATIC,120,98,181,24 + CONTROL "Do not show this dialog anymore",IDC_DONOTSHOWDLG,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,120,170,180,11 +END + +IDD_CHSOURCE DIALOG DISCARDABLE 0, 0, 317, 143 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Device installation" +FONT 8, "MS Shell Dlg" +BEGIN + CONTROL "Search in these locations",IDC_RADIO_SEARCHHERE,"Button", + BS_AUTORADIOBUTTON,27,11,239,13 + CONTROL "Choose the driver manually",IDC_RADIO_CHOOSE,"Button", + BS_AUTORADIOBUTTON,27,98,171,12 + CONTROL "Search removeable media",IDC_CHECK_MEDIA,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,47,33,163,9 + CONTROL "Include this path",IDC_CHECK_PATH,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,47,54,162,10 + PUSHBUTTON "Browse",IDC_BROWSE,248,69,50,14 + COMBOBOX IDC_COMBO_PATH,61,71,176,12,CBS_DROPDOWN | CBS_SORT | + WS_VSCROLL | WS_TABSTOP +END + +IDD_SEARCHDRV DIALOG DISCARDABLE 0, 0, 317, 143 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Device installation" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "UNKNOWN DEVICE",IDC_DEVICE,51,20,169,16 +END + +IDD_FINISHPAGE DIALOG DISCARDABLE 0, 0, 317, 193 +STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +CAPTION "Device installation" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Installation complete",IDC_FINISHTITLE,120,8,195,11 + LTEXT "The wizard has finished installing the driver for:", + IDC_STATIC,120,32,195,19 + LTEXT "Click Finish to close the wizard.",IDC_STATIC,120,174, + 179,11 + LTEXT "UNKNOWN DEVICE",IDC_DEVICE,148,53,147,12 +END + diff --git a/reactos/dll/newdev/Es.rc b/reactos/dll/newdev/Es.rc new file mode 100644 index 00000000000..26f688985d7 --- /dev/null +++ b/reactos/dll/newdev/Es.rc @@ -0,0 +1,74 @@ +LANGUAGE LANG_SPANISH, SUBLANG_NEUTRAL + +IDD_WELCOMEPAGE DIALOG DISCARDABLE 0, 0, 317, 193 +STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +CAPTION "Instalación de Dispositivos" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Bienvenido",IDC_WELCOMETITLE,120,8,195,24 + LTEXT "Este asistente lo ayudará a instalar un nuevo controlador de dispositivos para:", + IDC_STATIC,120,40,195,16 + LTEXT "Haga click en Siguiente para continuar.",IDC_STATIC,120,169,195,17 + LTEXT "DISPOSITIVO DESCONOCIDO",IDC_DEVICE,134,55,164,11 + CONTROL "Instalar controlador automáticamente",IDC_RADIO_AUTO,"Button", + BS_AUTORADIOBUTTON,120,112,178,13 + CONTROL "Instalar controlador desde una ubicación específica",IDC_RADIO_MANUAL, + "Button",BS_AUTORADIOBUTTON,120,133,164,14 +END + +IDD_NODRIVER DIALOG DISCARDABLE 0, 0, 317, 193 +STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +CAPTION "Instalación de Dispositivos" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "La instalación ha fallado",IDC_FINISHTITLE,120,8,195,24 + LTEXT "El dispositivo no pudo ser instalado porque el controlador no fue encontrado.", + IDC_STATIC,120,40,195,19 + LTEXT "Haga click en Atrás si tiene un CDROM del controlador o conoce dónde encontrar el controlador.", + IDC_STATIC,120,98,181,24 + CONTROL "No volver a mostrar esta ventana",IDC_DONOTSHOWDLG,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,120,170,180,11 +END + +IDD_CHSOURCE DIALOG DISCARDABLE 0, 0, 317, 143 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Instalación de Dispositivos" +FONT 8, "MS Shell Dlg" +BEGIN + CONTROL "Buscar en controlador en estas ubicaciones",IDC_RADIO_SEARCHHERE,"Button", + BS_AUTORADIOBUTTON,27,11,239,13 + CONTROL "Seleccionar el controlador manualmente",IDC_RADIO_CHOOSE,"Button", + BS_AUTORADIOBUTTON,27,98,171,12 + CONTROL "Buscar en medios extraíbles",IDC_CHECK_MEDIA,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,47,33,163,9 + CONTROL "Incluir esta ubicación de búsqueda",IDC_CHECK_PATH,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,47,54,162,10 + PUSHBUTTON "Examinar",IDC_BROWSE,248,69,50,14 + COMBOBOX IDC_COMBO_PATH,61,71,176,12,CBS_DROPDOWN | CBS_SORT | + WS_VSCROLL | WS_TABSTOP +END + +IDD_SEARCHDRV DIALOG DISCARDABLE 0, 0, 317, 143 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Device installation" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "DISPOSITIVO DESCONOCIDO",IDC_DEVICE,51,20,169,16 +END + +IDD_FINISHPAGE DIALOG DISCARDABLE 0, 0, 317, 193 +STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +CAPTION "Device installation" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Instalación completa.",IDC_FINISHTITLE,120,8,195,11 + LTEXT "El asistente ha finalizado la instalación del controlador para el dispositivo:", + IDC_STATIC,120,32,195,19 + LTEXT "Haga click en Finalizar parar cerrar el asistente.",IDC_STATIC,120,174, + 179,11 + LTEXT "DISPOSITIVO DESCONOCIDO",IDC_DEVICE,148,53,147,12 +END + diff --git a/reactos/dll/newdev/Hu.rc b/reactos/dll/newdev/Hu.rc new file mode 100644 index 00000000000..9e960d25e42 --- /dev/null +++ b/reactos/dll/newdev/Hu.rc @@ -0,0 +1,62 @@ +// Hungarian resource file by Robert Horvath - talley at cubeclub.hu +LANGUAGE LANG_HUNGARIAN, SUBLANG_NEUTRAL + +IDD_WELCOMEPAGE DIALOG DISCARDABLE 0, 0, 317, 193 +STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +CAPTION "Hardver telepítõ" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "welcome??",IDC_WELCOMETITLE,115,8,195,24 + LTEXT "A telepítõ a következõ hardverhez telepít fel eszközmeghatjtót:",IDC_STATIC,115,40,195,16 + LTEXT "Kattints a Tovább gombra a folytatáshoz.",IDC_STATIC,115,169,195,17 + LTEXT "Ismeretlen eszköz!",IDC_DEVICE,129,55,164,11 + CONTROL "Automatikus telepítés",IDC_RADIO_AUTO,"Button",BS_AUTORADIOBUTTON,115,112,178,13 + CONTROL "Eszközmeghajtó kiválasztása",IDC_RADIO_MANUAL,"Button",BS_AUTORADIOBUTTON,115,133,164,14 +END + +IDD_NODRIVER DIALOG DISCARDABLE 0, 0, 317, 193 +STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +CAPTION "Hardver telepítõ" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "A telepítés sikertelen.",IDC_FINISHTITLE,115,8,195,24 + LTEXT "Az eszközmeghajtó nem található.",IDC_STATIC,115,40,195,19 + LTEXT "Kattints a Vissza gombra, és az Eszközmeghajtó kiválasztása gombra, ha tudod, hol van az eszközmeghajtó.",IDC_STATIC,113,98,181,24 + CONTROL "Ne mutasd ezt többször",IDC_DONOTSHOWDLG,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,114,170,180,11 +END + +IDD_CHSOURCE DIALOG DISCARDABLE 0, 0, 317, 143 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Hardver telepítõ" +FONT 8, "MS Shell Dlg" +BEGIN + CONTROL "Keresés ezeken a helyeken",IDC_RADIO_SEARCHHERE,"Button",BS_AUTORADIOBUTTON,27,11,239,13 + CONTROL "Eszközmeghajtó kiválasztása",IDC_RADIO_CHOOSE,"Button",BS_AUTORADIOBUTTON,27,98,171,12 + CONTROL "Keresés cserélhetõ lemezeken",IDC_CHECK_MEDIA,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,47,33,163,9 + CONTROL "A következõ útvonalon keressen",IDC_CHECK_PATH,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,47,54,162,10 + PUSHBUTTON "Böngészés",IDC_BROWSE,248,69,45,14 + COMBOBOX IDC_COMBO_PATH,61,71,176,12,CBS_DROPDOWN | CBS_SORT | WS_VSCROLL | WS_TABSTOP +END + +IDD_SEARCHDRV DIALOG DISCARDABLE 0, 0, 317, 143 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Hardver telepítõ" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Ismeretlen eszköz!",IDC_DEVICE,51,20,169,16 +END + +IDD_FINISHPAGE DIALOG DISCARDABLE 0, 0, 317, 193 +STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +CAPTION "Hardver telepítõ" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "A telepítés sikeresen befejezõdött",IDC_FINISHTITLE,115,8,195,11 + LTEXT "A telepítõ feltelepítette az eszközmeghajtókat a következõ eszközhöz:",IDC_STATIC,115,32,195,19 + LTEXT "A kilépéshez kattints a Befejezés gombra.",IDC_STATIC,115,174,179,11 + LTEXT "Ismeretlen eszköz!",IDC_DEVICE,148,53,147,12 +END + diff --git a/reactos/dll/newdev/Ru.rc b/reactos/dll/newdev/Ru.rc new file mode 100644 index 00000000000..4125e50fdf8 --- /dev/null +++ b/reactos/dll/newdev/Ru.rc @@ -0,0 +1,74 @@ +LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT + +IDD_WELCOMEPAGE DIALOG DISCARDABLE 0, 0, 317, 193 +STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +CAPTION "Óñòàíîâêà îáîðóäîâàíèÿ" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Âàñ ïðèâåòñòâóåò ìàñòåð óñòàíîâêè íîâîãî îáîðóäîâàíèÿ",IDC_WELCOMETITLE,120,8,195,24 + LTEXT "Ýòîò ìàñòåð óñòàíàâëèâàåò äðàéâåð äëÿ:", + IDC_STATIC,120,40,195,16 + LTEXT "Äëÿ ïðîäîëæåíèÿ íàæìèòå êíîïêó ""Äàëåå"".",IDC_STATIC,120,169,195,17 + LTEXT "Íåèçâåñòíîå óñòðîéñòâî",IDC_DEVICE,134,55,164,11 + CONTROL "Óñòàíîâèòü äðàéâåð àâòîìàòè÷åñêè",IDC_RADIO_AUTO,"Button", + BS_AUTORADIOBUTTON,120,112,178,13 + CONTROL "Óñòàíîâèòü äðàéâåð èç óêàçàííîãî ìåñòà",IDC_RADIO_MANUAL, + "Button",BS_AUTORADIOBUTTON,120,133,164,14 +END + +IDD_NODRIVER DIALOG DISCARDABLE 0, 0, 317, 193 +STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +CAPTION "Óñòàíîâêà îáîðóäîâàíèÿ" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Óñòàíîâêà íå óäàëàñü",IDC_FINISHTITLE,120,8,195,24 + LTEXT "Óñòðîéñòâî íå óñòàíîâëåíî, ïîòîìó ÷òî äðàéâåð íå íàéäåí.", + IDC_STATIC,120,40,195,19 + LTEXT "Íàæìèòå êíîïêó ""Íàçàä"", åñëè ó Âàñ åñòü óñòàíîâî÷íûé äèñê ñ äðàéâåðîì, èëè ÷òîáû óêàçàòü ìåñòîïîëîæåíèå äðàéâåðà", + IDC_STATIC,120,98,181,24 + CONTROL "Íå ïîêàçûâàòü ýòîò äèàëîã â áóäóùåì",IDC_DONOTSHOWDLG,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,120,170,180,11 +END + +IDD_CHSOURCE DIALOG DISCARDABLE 0, 0, 317, 143 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Óñòàíîâêà îáîðóäîâàíèÿ" +FONT 8, "MS Shell Dlg" +BEGIN + CONTROL "Èñêàòü ïî ýòèì ïóòÿì",IDC_RADIO_SEARCHHERE,"Button", + BS_AUTORADIOBUTTON,27,11,239,13 + CONTROL "Âûáðàòü äðàéâåð âðó÷íóþ",IDC_RADIO_CHOOSE,"Button", + BS_AUTORADIOBUTTON,27,98,171,12 + CONTROL "Èñêàòü íà ñìåííûõ íîñèòåëÿõ",IDC_CHECK_MEDIA,"Button", + BS_AUTOCHECKBOX | WS_TABSTOP,47,33,163,9 + CONTROL "Âêëþ÷èòü ýòîò ïóòü",IDC_CHECK_PATH,"Button",BS_AUTOCHECKBOX | + WS_TABSTOP,47,54,162,10 + PUSHBUTTON "Îáçîð",IDC_BROWSE,248,69,50,14 + COMBOBOX IDC_COMBO_PATH,61,71,176,12,CBS_DROPDOWN | CBS_SORT | + WS_VSCROLL | WS_TABSTOP +END + +IDD_SEARCHDRV DIALOG DISCARDABLE 0, 0, 317, 143 +STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU +CAPTION "Óñòàíîâêà îáîðóäîâàíèÿ" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Íåèçâåñòíîå óñòðîéñòâî",IDC_DEVICE,51,20,169,16 +END + +IDD_FINISHPAGE DIALOG DISCARDABLE 0, 0, 317, 193 +STYLE DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | + WS_SYSMENU +CAPTION "Óñòàíîâêà îáîðóäîâàíèÿ" +FONT 8, "MS Shell Dlg" +BEGIN + LTEXT "Óñòàíîâêà çàâåðøåíà",IDC_FINISHTITLE,120,8,195,11 + LTEXT "Ìàñòåð çàêîí÷èë óñòàíîâêó äðàéâåðà äëÿ:", + IDC_STATIC,120,32,195,19 + LTEXT "Äëÿ âûõîäà èç ìàñòåðà íàæìèòå êíîïêó ""Ãîòîâî"".",IDC_STATIC,120,174, + 179,11 + LTEXT "Íåèçâåñòíîå óñòðîéñòâî",IDC_DEVICE,148,53,147,12 +END + diff --git a/reactos/dll/newdev/newdev.c b/reactos/dll/newdev/newdev.c new file mode 100644 index 00000000000..7ded0173316 --- /dev/null +++ b/reactos/dll/newdev/newdev.c @@ -0,0 +1,1241 @@ +/* + * New device installer (newdev.dll) + * + * Copyright 2005 Hervé Poussineau (hpoussin@reactos.org) + * 2005 Christoph von Wittich (Christoph@ActiveVB.de) + * + * 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 + */ + +#define YDEBUG +#include "newdev.h" + +WINE_DEFAULT_DEBUG_CHANNEL(newdev); + +static BOOL SearchDriver ( PDEVINSTDATA DevInstData, LPCTSTR Path ); +static BOOL InstallDriver ( PDEVINSTDATA DevInstData ); +static DWORD WINAPI FindDriverProc( LPVOID lpParam ); +static BOOL FindDriver ( PDEVINSTDATA DevInstData ); + +static DEVINSTDATA DevInstData; +HINSTANCE hDllInstance; +HANDLE hThread; + +static BOOL +CanDisableDevice(IN DEVINST DevInst, + IN HMACHINE hMachine, + OUT BOOL *CanDisable) +{ +#if 0 + /* hpoussin, Dec 2005. I've disabled this code because + * ntoskrnl never sets the DN_DISABLEABLE flag. + */ + CONFIGRET cr; + ULONG Status, ProblemNumber; + BOOL Ret = FALSE; + + cr = CM_Get_DevNode_Status_Ex(&Status, + &ProblemNumber, + DevInst, + 0, + hMachine); + if (cr == CR_SUCCESS) + { + *CanDisable = ((Status & DN_DISABLEABLE) != 0); + Ret = TRUE; + } + + return Ret; +#else + *CanDisable = TRUE; + return TRUE; +#endif +} + + +static BOOL +IsDeviceStarted(IN DEVINST DevInst, + IN HMACHINE hMachine, + OUT BOOL *IsEnabled) +{ + CONFIGRET cr; + ULONG Status, ProblemNumber; + BOOL Ret = FALSE; + + cr = CM_Get_DevNode_Status_Ex(&Status, + &ProblemNumber, + DevInst, + 0, + hMachine); + if (cr == CR_SUCCESS) + { + *IsEnabled = ((Status & DN_STARTED) != 0); + Ret = TRUE; + } + + return Ret; +} + + +static BOOL +StartDevice(IN HDEVINFO DeviceInfoSet, + IN PSP_DEVINFO_DATA DevInfoData OPTIONAL, + IN BOOL bEnable, + IN DWORD HardwareProfile OPTIONAL, + OUT BOOL *bNeedReboot OPTIONAL) +{ + SP_PROPCHANGE_PARAMS pcp; + SP_DEVINSTALL_PARAMS dp; + DWORD LastErr; + BOOL Ret = FALSE; + + pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER); + pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE; + pcp.HwProfile = HardwareProfile; + + if (bEnable) + { + /* try to enable the device on the global profile */ + pcp.StateChange = DICS_ENABLE; + pcp.Scope = DICS_FLAG_GLOBAL; + + /* ignore errors */ + LastErr = GetLastError(); + if (SetupDiSetClassInstallParams(DeviceInfoSet, + DevInfoData, + &pcp.ClassInstallHeader, + sizeof(SP_PROPCHANGE_PARAMS))) + { + SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, + DeviceInfoSet, + DevInfoData); + } + SetLastError(LastErr); + } + + /* try config-specific */ + pcp.StateChange = (bEnable ? DICS_ENABLE : DICS_DISABLE); + pcp.Scope = DICS_FLAG_CONFIGSPECIFIC; + + if (SetupDiSetClassInstallParams(DeviceInfoSet, + DevInfoData, + &pcp.ClassInstallHeader, + sizeof(SP_PROPCHANGE_PARAMS)) && + SetupDiCallClassInstaller(DIF_PROPERTYCHANGE, + DeviceInfoSet, + DevInfoData)) + { + dp.cbSize = sizeof(SP_DEVINSTALL_PARAMS); + if (SetupDiGetDeviceInstallParams(DeviceInfoSet, + DevInfoData, + &dp)) + { + if (bNeedReboot != NULL) + { + *bNeedReboot = ((dp.Flags & (DI_NEEDRESTART | DI_NEEDREBOOT)) != 0); + } + + Ret = TRUE; + } + } + return Ret; +} + +/* +* @unimplemented +*/ +BOOL WINAPI +UpdateDriverForPlugAndPlayDevicesW( + IN HWND hwndParent, + IN LPCWSTR HardwareId, + IN LPCWSTR FullInfPath, + IN DWORD InstallFlags, + OUT PBOOL bRebootRequired OPTIONAL) +{ + UNIMPLEMENTED; + SetLastError(ERROR_GEN_FAILURE); + return FALSE; +} + +/* +* @implemented +*/ +BOOL WINAPI +UpdateDriverForPlugAndPlayDevicesA( + IN HWND hwndParent, + IN LPCSTR HardwareId, + IN LPCSTR FullInfPath, + IN DWORD InstallFlags, + OUT PBOOL bRebootRequired OPTIONAL) +{ + BOOL Result; + LPWSTR HardwareIdW = NULL; + LPWSTR FullInfPathW = NULL; + + int len = MultiByteToWideChar(CP_ACP, 0, HardwareId, -1, NULL, 0); + HardwareIdW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + if (!HardwareIdW) + { + SetLastError(ERROR_GEN_FAILURE); + return FALSE; + } + MultiByteToWideChar(CP_ACP, 0, HardwareId, -1, HardwareIdW, len); + + len = MultiByteToWideChar(CP_ACP, 0, FullInfPath, -1, NULL, 0); + FullInfPathW = HeapAlloc(GetProcessHeap(), 0, len * sizeof(WCHAR)); + if (!FullInfPathW) + { + HeapFree(GetProcessHeap(), 0, HardwareIdW); + SetLastError(ERROR_GEN_FAILURE); + return FALSE; + } + MultiByteToWideChar(CP_ACP, 0, FullInfPath, -1, FullInfPathW, len); + + Result = UpdateDriverForPlugAndPlayDevicesW(hwndParent, + HardwareIdW, + FullInfPathW, + InstallFlags, + bRebootRequired); + + HeapFree(GetProcessHeap(), 0, HardwareIdW); + HeapFree(GetProcessHeap(), 0, FullInfPathW); + + return Result; +} + + +static HFONT +CreateTitleFont(VOID) +{ + NONCLIENTMETRICS ncm; + LOGFONT LogFont; + HDC hdc; + INT FontSize; + HFONT hFont; + + ncm.cbSize = sizeof(NONCLIENTMETRICS); + SystemParametersInfo(SPI_GETNONCLIENTMETRICS, 0, &ncm, 0); + + LogFont = ncm.lfMessageFont; + LogFont.lfWeight = FW_BOLD; + _tcscpy(LogFont.lfFaceName, _T("MS Shell Dlg")); + + hdc = GetDC(NULL); + FontSize = 12; + LogFont.lfHeight = 0 - GetDeviceCaps (hdc, LOGPIXELSY) * FontSize / 72; + hFont = CreateFontIndirect(&LogFont); + ReleaseDC(NULL, hdc); + + return hFont; +} + +static VOID +CenterWindow(HWND hWnd) +{ + HWND hWndParent; + RECT rcParent; + RECT rcWindow; + + hWndParent = GetParent(hWnd); + if (hWndParent == NULL) + hWndParent = GetDesktopWindow(); + + GetWindowRect(hWndParent, &rcParent); + GetWindowRect(hWnd, &rcWindow); + + SetWindowPos(hWnd, + HWND_TOP, + ((rcParent.right - rcParent.left) - (rcWindow.right - rcWindow.left)) / 2, + ((rcParent.bottom - rcParent.top) - (rcWindow.bottom - rcWindow.top)) / 2, + 0, + 0, + SWP_NOSIZE); +} + +static INT_PTR CALLBACK +WelcomeDlgProc( + IN HWND hwndDlg, + IN UINT uMsg, + IN WPARAM wParam, + IN LPARAM lParam) +{ + + PDEVINSTDATA DevInstData; + + /* Retrieve pointer to the global setup data */ + DevInstData = (PDEVINSTDATA)GetWindowLongPtr (hwndDlg, GWL_USERDATA); + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND hwndControl; + DWORD dwStyle; + + /* Get pointer to the global setup data */ + DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam; + SetWindowLongPtr(hwndDlg, GWL_USERDATA, (DWORD_PTR)DevInstData); + + hwndControl = GetParent(hwndDlg); + + /* Center the wizard window */ + CenterWindow (hwndControl); + + /* Hide the system menu */ + dwStyle = GetWindowLong(hwndControl, GWL_STYLE); + SetWindowLong(hwndControl, GWL_STYLE, dwStyle & ~WS_SYSMENU); + + /* Set title font */ + SendDlgItemMessage(hwndDlg, + IDC_WELCOMETITLE, + WM_SETFONT, + (WPARAM)DevInstData->hTitleFont, + (LPARAM)TRUE); + + SendDlgItemMessage(hwndDlg, + IDC_DEVICE, + WM_SETTEXT, + 0, + (LPARAM) DevInstData->buffer); + + SendDlgItemMessage(hwndDlg, + IDC_RADIO_AUTO, + BM_SETCHECK, + (WPARAM) TRUE, + (LPARAM) 0); + + + } + break; + + + case WM_NOTIFY: + { + LPNMHDR lpnm = (LPNMHDR)lParam; + + switch (lpnm->code) + { + case PSN_SETACTIVE: + /* Enable the Next button */ + PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT); + break; + + case PSN_WIZNEXT: + /* Handle a Next button click, if necessary */ + + if (SendDlgItemMessage(hwndDlg, IDC_RADIO_AUTO, BM_GETCHECK, (WPARAM) 0, (LPARAM) 0) == BST_CHECKED) + PropSheet_SetCurSel(GetParent(hwndDlg), 0, IDD_SEARCHDRV); + + break; + + default: + break; + } + } + break; + + default: + break; + } + + return FALSE; +} + +static INT_PTR CALLBACK +CHSourceDlgProc( + IN HWND hwndDlg, + IN UINT uMsg, + IN WPARAM wParam, + IN LPARAM lParam) +{ + + PDEVINSTDATA DevInstData; + + /* Retrieve pointer to the global setup data */ + DevInstData = (PDEVINSTDATA)GetWindowLongPtr (hwndDlg, GWL_USERDATA); + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND hwndControl; + DWORD dwStyle; + + /* Get pointer to the global setup data */ + DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam; + SetWindowLongPtr(hwndDlg, GWL_USERDATA, (DWORD_PTR)DevInstData); + + hwndControl = GetParent(hwndDlg); + + /* Center the wizard window */ + CenterWindow (hwndControl); + + /* Hide the system menu */ + dwStyle = GetWindowLong(hwndControl, GWL_STYLE); + SetWindowLong(hwndControl, GWL_STYLE, dwStyle & ~WS_SYSMENU); + + SendDlgItemMessage(hwndDlg, + IDC_RADIO_SEARCHHERE, + BM_SETCHECK, + (WPARAM) TRUE, + (LPARAM) 0); + + } + break; + + + case WM_NOTIFY: + { + LPNMHDR lpnm = (LPNMHDR)lParam; + + switch (lpnm->code) + { + case PSN_SETACTIVE: + /* Enable the Next and Back buttons */ + PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_NEXT | PSWIZB_BACK); + break; + + case PSN_WIZNEXT: + /* Handle a Next button click, if necessary */ + PropSheet_SetCurSel(GetParent(hwndDlg), 0, 4); + break; + + default: + break; + } + } + break; + + default: + break; + } + + return FALSE; +} + +static INT_PTR CALLBACK +SearchDrvDlgProc( + IN HWND hwndDlg, + IN UINT uMsg, + IN WPARAM wParam, + IN LPARAM lParam) +{ + + PDEVINSTDATA DevInstData; + DWORD dwThreadId; + + /* Retrieve pointer to the global setup data */ + DevInstData = (PDEVINSTDATA)GetWindowLongPtr (hwndDlg, GWL_USERDATA); + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND hwndControl; + DWORD dwStyle; + + /* Get pointer to the global setup data */ + DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam; + SetWindowLongPtr(hwndDlg, GWL_USERDATA, (DWORD_PTR)DevInstData); + + DevInstData->hDialog = hwndDlg; + hwndControl = GetParent(hwndDlg); + + /* Center the wizard window */ + CenterWindow (hwndControl); + + SendDlgItemMessage(hwndDlg, + IDC_DEVICE, + WM_SETTEXT, + 0, + (LPARAM) DevInstData->buffer); + + /* Hide the system menu */ + dwStyle = GetWindowLong(hwndControl, GWL_STYLE); + SetWindowLong(hwndControl, GWL_STYLE, dwStyle & ~WS_SYSMENU); + } + break; + + case WM_SEARCH_FINISHED: + { + CloseHandle(hThread); + hThread = 0; + if (wParam == 0) + PropSheet_SetCurSel(GetParent(hwndDlg), 0, IDD_NODRIVER); + else + PropSheet_SetCurSel(GetParent(hwndDlg), 0, IDD_FINISHPAGE); + break; + } + case WM_NOTIFY: + { + LPNMHDR lpnm = (LPNMHDR)lParam; + + switch (lpnm->code) + { + case PSN_SETACTIVE: + PropSheet_SetWizButtons(GetParent(hwndDlg), !PSWIZB_NEXT | !PSWIZB_BACK); + hThread = CreateThread( NULL, 0, FindDriverProc, DevInstData, 0, &dwThreadId); + break; + + case PSN_KILLACTIVE: + if (hThread != 0) + { + SetWindowLong ( hwndDlg, DWL_MSGRESULT, TRUE); + return TRUE; + } + break; + case PSN_WIZNEXT: + /* Handle a Next button click, if necessary */ + break; + + default: + break; + } + } + break; + + default: + break; + } + + return FALSE; +} + +static DWORD WINAPI +FindDriverProc( + IN LPVOID lpParam) +{ + TCHAR drive[] = {'?',':',0}; + size_t nType; + DWORD dwDrives; + PDEVINSTDATA DevInstData; + DWORD config_flags; + UINT i = 1; + + DevInstData = (PDEVINSTDATA)lpParam; + + dwDrives = GetLogicalDrives(); + for (drive[0] = 'A'; drive[0] <= 'Z'; drive[0]++) + { + if (dwDrives & i) + { + nType = GetDriveType( drive ); + if ((nType == DRIVE_CDROM)) + //if ((nType == DRIVE_CDROM) || (nType == DRIVE_FIXED)) + { + /* search for inf file */ + if (SearchDriver ( DevInstData, drive )) + { + /* if we found a valid driver inf... */ + if (FindDriver ( DevInstData )) + { + InstallDriver ( DevInstData ); + PostMessage(DevInstData->hDialog, WM_SEARCH_FINISHED, 1, 0); + return 0; + } + } + } + } + i <<= 1; + } + + /* update device configuration */ + if(SetupDiGetDeviceRegistryProperty(DevInstData->hDevInfo, + &DevInstData->devInfoData, + SPDRP_CONFIGFLAGS, + NULL, + (BYTE *)&config_flags, + sizeof(config_flags), + NULL)) + { + config_flags |= CONFIGFLAG_FAILEDINSTALL; + SetupDiSetDeviceRegistryProperty( + DevInstData->hDevInfo, + &DevInstData->devInfoData, + SPDRP_CONFIGFLAGS, + (BYTE *)&config_flags, sizeof(config_flags) ); + } + + PostMessage(DevInstData->hDialog, WM_SEARCH_FINISHED, 0, 0); + return 0; +} + +static INT_PTR CALLBACK +FinishDlgProc( + IN HWND hwndDlg, + IN UINT uMsg, + IN WPARAM wParam, + IN LPARAM lParam) +{ + + PDEVINSTDATA DevInstData; + + /* Retrieve pointer to the global setup data */ + DevInstData = (PDEVINSTDATA)GetWindowLongPtr (hwndDlg, GWL_USERDATA); + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND hwndControl; + + /* Get pointer to the global setup data */ + DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam; + SetWindowLongPtr(hwndDlg, GWL_USERDATA, (DWORD_PTR)DevInstData); + + hwndControl = GetDlgItem(GetParent(hwndDlg), IDCANCEL); + ShowWindow (hwndControl, SW_HIDE); + EnableWindow (hwndControl, FALSE); + + SendDlgItemMessage(hwndDlg, + IDC_DEVICE, + WM_SETTEXT, + 0, + (LPARAM) DevInstData->drvInfoData.Description); + + /* Set title font */ + SendDlgItemMessage(hwndDlg, + IDC_FINISHTITLE, + WM_SETFONT, + (WPARAM)DevInstData->hTitleFont, + (LPARAM)TRUE); + } + break; + + case WM_NOTIFY: + { + LPNMHDR lpnm = (LPNMHDR)lParam; + + switch (lpnm->code) + { + case PSN_SETACTIVE: + /* Enable the correct buttons on for the active page */ + PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_FINISH); + break; + + case PSN_WIZBACK: + /* Handle a Back button click, if necessary */ + break; + + case PSN_WIZFINISH: + /* Handle a Finish button click, if necessary */ + break; + + default: + break; + } + } + break; + + default: + break; + } + + return FALSE; +} + +static INT_PTR CALLBACK +InstFailDlgProc( + IN HWND hwndDlg, + IN UINT uMsg, + IN WPARAM wParam, + IN LPARAM lParam) +{ + + PDEVINSTDATA DevInstData; + + /* Get pointer to the global setup data */ + DevInstData = (PDEVINSTDATA)GetWindowLongPtr (hwndDlg, GWL_USERDATA); + + switch (uMsg) + { + case WM_INITDIALOG: + { + HWND hwndControl; + BOOL DisableableDevice = FALSE; + + DevInstData = (PDEVINSTDATA)((LPPROPSHEETPAGE)lParam)->lParam; + SetWindowLongPtr(hwndDlg, GWL_USERDATA, (DWORD_PTR)DevInstData); + + hwndControl = GetDlgItem(GetParent(hwndDlg), IDCANCEL); + ShowWindow (hwndControl, SW_HIDE); + EnableWindow (hwndControl, FALSE); + + /* Set title font */ + SendDlgItemMessage(hwndDlg, + IDC_FINISHTITLE, + WM_SETFONT, + (WPARAM)DevInstData->hTitleFont, + (LPARAM)TRUE); + + /* disable the "do not show this dialog anymore" checkbox + if the device cannot be disabled */ + CanDisableDevice(DevInstData->devInfoData.DevInst, + NULL, + &DisableableDevice); + EnableWindow(GetDlgItem(hwndDlg, + IDC_DONOTSHOWDLG), + DisableableDevice); + } + break; + + case WM_NOTIFY: + { + LPNMHDR lpnm = (LPNMHDR)lParam; + + switch (lpnm->code) + { + case PSN_SETACTIVE: + /* Enable the correct buttons on for the active page */ + PropSheet_SetWizButtons(GetParent(hwndDlg), PSWIZB_BACK | PSWIZB_FINISH); + break; + + case PSN_WIZBACK: + PropSheet_SetCurSel(GetParent(hwndDlg), 0, IDD_WELCOMEPAGE); + /* Handle a Back button click, if necessary */ + break; + + case PSN_WIZFINISH: + { + BOOL DisableableDevice = FALSE; + BOOL IsStarted = FALSE; + + if (CanDisableDevice(DevInstData->devInfoData.DevInst, + NULL, + &DisableableDevice) && + DisableableDevice && + IsDeviceStarted(DevInstData->devInfoData.DevInst, + NULL, + &IsStarted) && + !IsStarted && + SendDlgItemMessage(hwndDlg, IDC_DONOTSHOWDLG, BM_GETCHECK, (WPARAM) 0, (LPARAM) 0) == BST_CHECKED) + { + /* disable the device */ + StartDevice(DevInstData->hDevInfo, + &DevInstData->devInfoData, + FALSE, + 0, + NULL); + } + break; + } + + default: + break; + } + } + break; + + default: + break; + } + + return FALSE; +} + + +static BOOL +FindDriver( + IN PDEVINSTDATA DevInstData) +{ + SP_DEVINSTALL_PARAMS DevInstallParams = {0,}; + BOOL ret; + + DevInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS); + if (!SetupDiGetDeviceInstallParams(DevInstData->hDevInfo, &DevInstData->devInfoData, &DevInstallParams)) + { + TRACE("SetupDiGetDeviceInstallParams() failed with error 0x%lx\n", GetLastError()); + return FALSE; + } + DevInstallParams.FlagsEx |= DI_FLAGSEX_ALLOWEXCLUDEDDRVS; + if (!SetupDiSetDeviceInstallParams(DevInstData->hDevInfo, &DevInstData->devInfoData, &DevInstallParams)) + { + TRACE("SetupDiSetDeviceInstallParams() failed with error 0x%lx\n", GetLastError()); + return FALSE; + } + + ret = SetupDiBuildDriverInfoList(DevInstData->hDevInfo, &DevInstData->devInfoData, SPDIT_COMPATDRIVER); + if (!ret) + { + TRACE("SetupDiBuildDriverInfoList() failed with error 0x%lx\n", GetLastError()); + return FALSE; + } + + DevInstData->drvInfoData.cbSize = sizeof(SP_DRVINFO_DATA); + ret = SetupDiEnumDriverInfo( + DevInstData->hDevInfo, + &DevInstData->devInfoData, + SPDIT_COMPATDRIVER, + 0, + &DevInstData->drvInfoData); + if (!ret) + { + if (GetLastError() == ERROR_NO_MORE_ITEMS) + return FALSE; + TRACE("SetupDiEnumDriverInfo() failed with error 0x%lx\n", GetLastError()); + return FALSE; + } + TRACE("Installing driver %S: %S\n", DevInstData->drvInfoData.MfgName, DevInstData->drvInfoData.Description); + + return TRUE; +} + + +static BOOL +IsDots(IN LPCTSTR str) +{ + if(_tcscmp(str, _T(".")) && _tcscmp(str, _T(".."))) return FALSE; + return TRUE; +} + +static LPTSTR +GetFileExt(IN LPTSTR FileName) +{ + if (FileName == 0) + return _T(""); + + int i = _tcsclen(FileName); + while ((i >= 0) && (FileName[i] != _T('.'))) + i--; + + FileName = _tcslwr(FileName); + + if (i >= 0) + return &FileName[i]; + else + return _T(""); +} + +static BOOL +SearchDriver( + IN PDEVINSTDATA DevInstData, + IN LPCTSTR Path) +{ + WIN32_FIND_DATA wfd; + SP_DEVINSTALL_PARAMS DevInstallParams; + TCHAR DirPath[MAX_PATH]; + TCHAR FileName[MAX_PATH]; + TCHAR FullPath[MAX_PATH]; + TCHAR LastDirPath[MAX_PATH] = _T(""); + TCHAR PathWithPattern[MAX_PATH]; + BOOL ok = TRUE; + BOOL ret; + HANDLE hFindFile; + + _tcscpy(DirPath, Path); + + if (DirPath[_tcsclen(DirPath) - 1] != '\\') + _tcscat(DirPath, _T("\\")); + + _tcscpy(PathWithPattern, DirPath); + _tcscat(PathWithPattern, _T("\\*")); + + for (hFindFile = FindFirstFile(PathWithPattern, &wfd); ((hFindFile != INVALID_HANDLE_VALUE) && ok); ok = FindNextFile(hFindFile, &wfd)) + { + + _tcscpy(FileName, wfd.cFileName); + if (IsDots(FileName)) continue; + + if((wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) + { + _tcscpy(FullPath, DirPath); + _tcscat(FullPath, FileName); + if(SearchDriver(DevInstData, FullPath)) + break; + } + else + { + LPCTSTR pszExtension = GetFileExt(FileName); + + if ((_tcscmp(pszExtension, _T(".inf")) == 0) && (_tcscmp(LastDirPath, DirPath) != 0)) + { + _tcscpy(LastDirPath, DirPath); + ZeroMemory (&DevInstallParams, sizeof(SP_DEVINSTALL_PARAMS)); + DevInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS); + + ret = SetupDiGetDeviceInstallParams( + DevInstData->hDevInfo, + &DevInstData->devInfoData, + &DevInstallParams); + + if (_tcsclen(DirPath) <= MAX_PATH) + { + memcpy(DevInstallParams.DriverPath, DirPath, (_tcsclen(DirPath) + 1) * sizeof(TCHAR)); + } + + ret = SetupDiSetDeviceInstallParams( + DevInstData->hDevInfo, + &DevInstData->devInfoData, + &DevInstallParams); + + if ( FindDriver ( DevInstData ) ) + { + if (hFindFile != INVALID_HANDLE_VALUE) + FindClose(hFindFile); + return TRUE; + } + + } + } + } + + if (hFindFile != INVALID_HANDLE_VALUE) + FindClose(hFindFile); + + return FALSE; +} + +static BOOL +InstallDriver( + IN PDEVINSTDATA DevInstData) +{ + + BOOL ret; + + ret = SetupDiCallClassInstaller( + DIF_SELECTBESTCOMPATDRV, + DevInstData->hDevInfo, + &DevInstData->devInfoData); + if (!ret) + { + TRACE("SetupDiCallClassInstaller(DIF_SELECTBESTCOMPATDRV) failed with error 0x%lx\n", GetLastError()); + return FALSE; + } + + ret = SetupDiCallClassInstaller( + DIF_ALLOW_INSTALL, + DevInstData->hDevInfo, + &DevInstData->devInfoData); + if (!ret) + { + TRACE("SetupDiCallClassInstaller(DIF_ALLOW_INSTALL) failed with error 0x%lx\n", GetLastError()); + return FALSE; + } + + ret = SetupDiCallClassInstaller( + DIF_NEWDEVICEWIZARD_PREANALYZE, + DevInstData->hDevInfo, + &DevInstData->devInfoData); + if (!ret) + { + TRACE("SetupDiCallClassInstaller(DIF_NEWDEVICEWIZARD_PREANALYZE) failed with error 0x%lx\n", GetLastError()); + return FALSE; + } + + ret = SetupDiCallClassInstaller( + DIF_NEWDEVICEWIZARD_POSTANALYZE, + DevInstData->hDevInfo, + &DevInstData->devInfoData); + if (!ret) + { + TRACE("SetupDiCallClassInstaller(DIF_NEWDEVICEWIZARD_POSTANALYZE) failed with error 0x%lx\n", GetLastError()); + return FALSE; + } + + ret = SetupDiCallClassInstaller( + DIF_INSTALLDEVICEFILES, + DevInstData->hDevInfo, + &DevInstData->devInfoData); + if (!ret) + { + TRACE("SetupDiCallClassInstaller(DIF_INSTALLDEVICEFILES) failed with error 0x%lx\n", GetLastError()); + return FALSE; + } + + ret = SetupDiCallClassInstaller( + DIF_REGISTER_COINSTALLERS, + DevInstData->hDevInfo, + &DevInstData->devInfoData); + if (!ret) + { + TRACE("SetupDiCallClassInstaller(DIF_REGISTER_COINSTALLERS) failed with error 0x%lx\n", GetLastError()); + return FALSE; + } + + ret = SetupDiCallClassInstaller( + DIF_INSTALLINTERFACES, + DevInstData->hDevInfo, + &DevInstData->devInfoData); + if (!ret) + { + TRACE("SetupDiCallClassInstaller(DIF_INSTALLINTERFACES) failed with error 0x%lx\n", GetLastError()); + return FALSE; + } + + ret = SetupDiCallClassInstaller( + DIF_INSTALLDEVICE, + DevInstData->hDevInfo, + &DevInstData->devInfoData); + if (!ret) + { + TRACE("SetupDiCallClassInstaller(DIF_INSTALLDEVICE) failed with error 0x%lx\n", GetLastError()); + return FALSE; + } + + ret = SetupDiCallClassInstaller( + DIF_NEWDEVICEWIZARD_FINISHINSTALL, + DevInstData->hDevInfo, + &DevInstData->devInfoData); + if (!ret) + { + TRACE("SetupDiCallClassInstaller(DIF_NEWDEVICEWIZARD_FINISHINSTALL) failed with error 0x%lx\n", GetLastError()); + return FALSE; + } + + ret = SetupDiCallClassInstaller( + DIF_DESTROYPRIVATEDATA, + DevInstData->hDevInfo, + &DevInstData->devInfoData); + if (!ret) + { + TRACE("SetupDiCallClassInstaller(DIF_DESTROYPRIVATEDATA) failed with error 0x%lx\n", GetLastError()); + return FALSE; + } + + return TRUE; + +} + +static VOID +CleanUp(VOID) +{ + + if (DevInstData.devInfoData.cbSize != 0) + { + if (!SetupDiDestroyDriverInfoList(DevInstData.hDevInfo, &DevInstData.devInfoData, SPDIT_COMPATDRIVER)) + TRACE("SetupDiDestroyDriverInfoList() failed with error 0x%lx\n", GetLastError()); + } + + if (DevInstData.hDevInfo != INVALID_HANDLE_VALUE) + { + if (!SetupDiDestroyDeviceInfoList(DevInstData.hDevInfo)) + TRACE("SetupDiDestroyDeviceInfoList() failed with error 0x%lx\n", GetLastError()); + } + + if (DevInstData.buffer) + HeapFree(GetProcessHeap(), 0, DevInstData.buffer); + +} + +/* +* @implemented +*/ +BOOL WINAPI +DevInstallW( + IN HWND hWndParent, + IN HINSTANCE hInstance, + IN LPCWSTR InstanceId, + IN INT Show) +{ + + PROPSHEETHEADER psh; + HPROPSHEETPAGE ahpsp[5]; + PROPSHEETPAGE psp; + BOOL ret; + DWORD config_flags; + + if (!IsUserAdmin()) + { + /* XP kills the process... */ + ExitProcess(ERROR_ACCESS_DENIED); + } + + /* Clear devinst data */ + ZeroMemory(&DevInstData, sizeof(DEVINSTDATA)); + DevInstData.devInfoData.cbSize = 0; /* Tell if the devInfoData is valid */ + + + DevInstData.hDevInfo = SetupDiCreateDeviceInfoListExW(NULL, NULL, NULL, NULL); + if (DevInstData.hDevInfo == INVALID_HANDLE_VALUE) + { + TRACE("SetupDiCreateDeviceInfoListExW() failed with error 0x%lx\n", GetLastError()); + CleanUp(); + return FALSE; + } + + DevInstData.devInfoData.cbSize = sizeof(SP_DEVINFO_DATA); + ret = SetupDiOpenDeviceInfoW( + DevInstData.hDevInfo, + InstanceId, + NULL, + 0, /* Open flags */ + &DevInstData.devInfoData); + if (!ret) + { + TRACE("SetupDiOpenDeviceInfoW() failed with error 0x%lx (InstanceId %S)\n", GetLastError(), InstanceId); + DevInstData.devInfoData.cbSize = 0; + CleanUp(); + return FALSE; + } + + SetLastError(ERROR_GEN_FAILURE); + ret = SetupDiGetDeviceRegistryProperty( + DevInstData.hDevInfo, + &DevInstData.devInfoData, + SPDRP_DEVICEDESC, + &DevInstData.regDataType, + NULL, 0, + &DevInstData.requiredSize); + + if (!ret && GetLastError() == ERROR_INSUFFICIENT_BUFFER && DevInstData.regDataType == REG_SZ) + { + DevInstData.buffer = HeapAlloc(GetProcessHeap(), 0, DevInstData.requiredSize); + if (!DevInstData.buffer) + { + TRACE("HeapAlloc() failed\n"); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + } + else + { + ret = SetupDiGetDeviceRegistryProperty( + DevInstData.hDevInfo, + &DevInstData.devInfoData, + SPDRP_DEVICEDESC, + &DevInstData.regDataType, + DevInstData.buffer, DevInstData.requiredSize, + &DevInstData.requiredSize); + } + } + if (!ret) + { + TRACE("SetupDiGetDeviceRegistryProperty() failed with error 0x%lx (InstanceId %S)\n", GetLastError(), InstanceId); + CleanUp(); + return FALSE; + } + + if(SetupDiGetDeviceRegistryProperty(DevInstData.hDevInfo, + &DevInstData.devInfoData, + SPDRP_CONFIGFLAGS, + NULL, + (BYTE *)&config_flags, + sizeof(config_flags), + NULL)) + { + if (config_flags & CONFIGFLAG_FAILEDINSTALL) + { + CleanUp(); + return TRUE; + } + } + /* + else + { + swprintf(buf, _T("%ld"), GetLastError()); + MessageBox(0,buf,buf,0); + } + */ + + TRACE("Installing %S (%S)\n", DevInstData.buffer, InstanceId); + + if ((!FindDriver(&DevInstData)) && (Show != SW_HIDE)) + { + + /* Create the Welcome page */ + ZeroMemory (&psp, sizeof(PROPSHEETPAGE)); + psp.dwSize = sizeof(PROPSHEETPAGE); + psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER; + psp.hInstance = hDllInstance; + psp.lParam = (LPARAM)&DevInstData; + psp.pfnDlgProc = WelcomeDlgProc; + psp.pszTemplate = MAKEINTRESOURCE(IDD_WELCOMEPAGE); + ahpsp[IDD_WELCOMEPAGE] = CreatePropertySheetPage(&psp); + + /* Create the Select Source page */ + psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE; + psp.pfnDlgProc = CHSourceDlgProc; + psp.pszTemplate = MAKEINTRESOURCE(IDD_CHSOURCE); + ahpsp[IDD_CHSOURCE] = CreatePropertySheetPage(&psp); + + /* Create the Search driver page */ + psp.dwFlags = PSP_DEFAULT | PSP_USEHEADERTITLE | PSP_USEHEADERSUBTITLE; + psp.pfnDlgProc = SearchDrvDlgProc; + psp.pszTemplate = MAKEINTRESOURCE(IDD_SEARCHDRV); + ahpsp[IDD_SEARCHDRV] = CreatePropertySheetPage(&psp); + + /* Create the Finish page */ + psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER; + psp.pfnDlgProc = FinishDlgProc; + psp.pszTemplate = MAKEINTRESOURCE(IDD_FINISHPAGE); + ahpsp[IDD_FINISHPAGE] = CreatePropertySheetPage(&psp); + + /* Create the Install failed page */ + psp.dwFlags = PSP_DEFAULT | PSP_HIDEHEADER; + psp.pfnDlgProc = InstFailDlgProc; + psp.pszTemplate = MAKEINTRESOURCE(IDD_NODRIVER); + ahpsp[IDD_NODRIVER] = CreatePropertySheetPage(&psp); + + /* Create the property sheet */ + psh.dwSize = sizeof(PROPSHEETHEADER); + psh.dwFlags = PSH_WIZARD97 | PSH_WATERMARK | PSH_HEADER; + psh.hInstance = hDllInstance; + psh.hwndParent = NULL; + psh.nPages = 5; + psh.nStartPage = 0; + psh.phpage = ahpsp; + psh.pszbmWatermark = MAKEINTRESOURCE(IDB_WATERMARK); + psh.pszbmHeader = MAKEINTRESOURCE(IDB_HEADER); + + /* Create title font */ + DevInstData.hTitleFont = CreateTitleFont(); + + /* Display the wizard */ + PropertySheet(&psh); + + DeleteObject(DevInstData.hTitleFont); + + } + else + { + InstallDriver ( &DevInstData ); + } + + CleanUp(); + return TRUE; +} + +/* +* @unimplemented +*/ +BOOL WINAPI +ClientSideInstallW(IN HWND hWndOwner, + IN DWORD dwUnknownFlags, + IN LPWSTR lpNamedPipeName) +{ + /* NOTE: pNamedPipeName is in the format: + * "\\.\pipe\PNP_Device_Install_Pipe_0.{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}" + */ + return FALSE; +} + +BOOL WINAPI +DllMain( + IN HINSTANCE hInstance, + IN DWORD dwReason, + IN LPVOID lpReserved) +{ + if (dwReason == DLL_PROCESS_ATTACH) + { + INITCOMMONCONTROLSEX InitControls; + + DisableThreadLibraryCalls(hInstance); + + InitControls.dwSize = sizeof(INITCOMMONCONTROLSEX); + InitControls.dwICC = ICC_PROGRESS_CLASS; + InitCommonControlsEx(&InitControls); + hDllInstance = hInstance; + } + + return TRUE; +} diff --git a/reactos/dll/newdev/newdev.h b/reactos/dll/newdev/newdev.h new file mode 100644 index 00000000000..b2504eef4c8 --- /dev/null +++ b/reactos/dll/newdev/newdev.h @@ -0,0 +1,25 @@ +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "resource.h" + +typedef struct _DEVINSTDATA +{ + HFONT hTitleFont; + PBYTE buffer; + DWORD requiredSize; + DWORD regDataType; + HWND hDialog; + HDEVINFO hDevInfo; + SP_DEVINFO_DATA devInfoData; + SP_DRVINFO_DATA drvInfoData; +} DEVINSTDATA, *PDEVINSTDATA; + +#define WM_SEARCH_FINISHED (WM_USER + 10) diff --git a/reactos/dll/newdev/newdev.rc b/reactos/dll/newdev/newdev.rc new file mode 100644 index 00000000000..587778b1f42 --- /dev/null +++ b/reactos/dll/newdev/newdev.rc @@ -0,0 +1,13 @@ +#include +#include "resource.h" + +LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL + +IDB_WATERMARK BITMAP "resources/watermark.bmp" +IDB_HEADER BITMAP "resources/header.bmp" + +#include "En.rc" +#include "Es.rc" +#include "De.rc" +#include "Hu.rc" +#include "Ru.rc" diff --git a/reactos/dll/newdev/newdev.spec b/reactos/dll/newdev/newdev.spec new file mode 100644 index 00000000000..1a2c99306e9 --- /dev/null +++ b/reactos/dll/newdev/newdev.spec @@ -0,0 +1,11 @@ +@ stdcall ClientSideInstallW(ptr long wstr) +@ stdcall DevInstallW(ptr ptr wstr long) +@ stub InstallDevInst +@ stub InstallDevInstEx +@ stdcall InstallNewDevice(ptr ptr ptr) +@ stub InstallSelectedDevice +@ stdcall InstallSelectedDriverW(ptr ptr wstr long ptr) +@ stub InstallWindowsUpdateDriver +@ stub RollbackDriver +@ stdcall UpdateDriverForPlugAndPlayDevicesA(ptr str str long ptr) +@ stdcall UpdateDriverForPlugAndPlayDevicesW(ptr wstr wstr long ptr) diff --git a/reactos/dll/newdev/newdev.xml b/reactos/dll/newdev/newdev.xml new file mode 100644 index 00000000000..45cfebaeb0b --- /dev/null +++ b/reactos/dll/newdev/newdev.xml @@ -0,0 +1,17 @@ + + . + + + + 0x0600 + 0x0501 + newdev.c + stubs.c + newdev.rc + newdev.spec + wine + gdi32 + comctl32 + ntdll + setupapi + diff --git a/reactos/dll/newdev/resource.h b/reactos/dll/newdev/resource.h new file mode 100644 index 00000000000..fad5d93a7ec --- /dev/null +++ b/reactos/dll/newdev/resource.h @@ -0,0 +1,28 @@ +#ifndef RESOURCE_H +#define RESOURCE_H + +#define IDB_WATERMARK 100 +#define IDB_HEADER 101 + +#define IDC_STATIC -1 + +#define IDC_DEVICE 2000 +#define IDC_RADIO_AUTO 2001 +#define IDC_RADIO_MANUAL 2002 +#define IDC_DONOTSHOWDLG 2003 +#define IDC_RADIO_SEARCHHERE 2004 +#define IDC_RADIO_CHOOSE 2005 +#define IDC_CHECK_MEDIA 2006 +#define IDC_CHECK_PATH 2007 +#define IDC_WELCOMETITLE 2008 +#define IDC_BROWSE 2009 +#define IDC_COMBO_PATH 2010 +#define IDC_FINISHTITLE 2011 + +#define IDD_WELCOMEPAGE 0 +#define IDD_CHSOURCE 1 +#define IDD_SEARCHDRV 2 +#define IDD_FINISHPAGE 3 +#define IDD_NODRIVER 4 + +#endif /* RESOURCE_H */ diff --git a/reactos/dll/newdev/resources/header.bmp b/reactos/dll/newdev/resources/header.bmp new file mode 100644 index 00000000000..af7bf7a5935 Binary files /dev/null and b/reactos/dll/newdev/resources/header.bmp differ diff --git a/reactos/dll/newdev/resources/watermark.bmp b/reactos/dll/newdev/resources/watermark.bmp new file mode 100644 index 00000000000..1b42e5b4bd0 Binary files /dev/null and b/reactos/dll/newdev/resources/watermark.bmp differ diff --git a/reactos/dll/newdev/stubs.c b/reactos/dll/newdev/stubs.c new file mode 100644 index 00000000000..226a87e0bf0 --- /dev/null +++ b/reactos/dll/newdev/stubs.c @@ -0,0 +1,53 @@ +/* + * New device installer (newdev.dll) + * + * Copyright 2005 Hervé Poussineau (hpoussin@reactos.org) + * + * 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 "newdev.h" + +WINE_DEFAULT_DEBUG_CHANNEL(newdev); + +/* +* @unimplemented +*/ +BOOL WINAPI +InstallNewDevice( + IN HWND hwndParent, + IN LPGUID ClassGuid OPTIONAL, + OUT PDWORD Reboot) +{ + UNIMPLEMENTED; + SetLastError(ERROR_GEN_FAILURE); + return FALSE; +} + +/* +* @unimplemented +*/ +BOOL WINAPI +InstallSelectedDriverW( + IN HWND hwndParent, + IN HDEVINFO DeviceInfoSet, + IN LPCWSTR Reserved, + IN BOOL Backup, + OUT PDWORD pReboot) +{ + UNIMPLEMENTED; + SetLastError(ERROR_GEN_FAILURE); + return FALSE; +}