From dea323dcecc2de2e66df04b48eaf3858fdddb426 Mon Sep 17 00:00:00 2001 From: Hans Leidekker Date: Fri, 4 Nov 2016 13:28:04 +0100 Subject: [PATCH] msi: Handle descriptors without component in MsiProvideQualifiedComponentEx. Signed-off-by: Hans Leidekker Signed-off-by: Alexandre Julliard --- dlls/msi/msi.c | 79 +++++++++++++++++------- dlls/msi/tests/msi.c | 141 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 198 insertions(+), 22 deletions(-) diff --git a/dlls/msi/msi.c b/dlls/msi/msi.c index e812ebb68ef..2fe1538740e 100644 --- a/dlls/msi/msi.c +++ b/dlls/msi/msi.c @@ -3363,6 +3363,26 @@ INSTALLSTATE WINAPI MsiUseFeatureA( LPCSTR szProduct, LPCSTR szFeature ) return MsiUseFeatureExA(szProduct, szFeature, 0, 0); } +WCHAR *reg_get_multisz( HKEY hkey, const WCHAR *name ) +{ + WCHAR *ret; + DWORD len, type; + if (RegQueryValueExW( hkey, name, NULL, &type, NULL, &len ) || type != REG_MULTI_SZ) return NULL; + if ((ret = msi_alloc( len ))) RegQueryValueExW( hkey, name, NULL, NULL, (BYTE *)ret, &len ); + return ret; +} + +WCHAR *reg_get_sz( HKEY hkey, const WCHAR *name ) +{ + WCHAR *ret; + DWORD len, type; + if (RegQueryValueExW( hkey, name, NULL, &type, NULL, &len ) || type != REG_SZ) return NULL; + if ((ret = msi_alloc( len ))) RegQueryValueExW( hkey, name, NULL, NULL, (BYTE *)ret, &len ); + return ret; +} + +#define BASE85_SIZE 20 + /*********************************************************************** * MSI_ProvideQualifiedComponentEx [internal] */ @@ -3371,39 +3391,54 @@ static UINT MSI_ProvideQualifiedComponentEx(LPCWSTR szComponent, DWORD Unused1, DWORD Unused2, awstring *lpPathBuf, LPDWORD pcchPathBuf) { - WCHAR product[MAX_FEATURE_CHARS+1], component[MAX_FEATURE_CHARS+1], - feature[MAX_FEATURE_CHARS+1]; - LPWSTR info; + WCHAR product[MAX_FEATURE_CHARS+1], comp[MAX_FEATURE_CHARS+1], feature[MAX_FEATURE_CHARS+1]; + WCHAR *desc; HKEY hkey; - DWORD sz; - UINT rc; + DWORD size; + UINT ret; INSTALLSTATE state; - rc = MSIREG_OpenUserComponentsKey(szComponent, &hkey, FALSE); - if (rc != ERROR_SUCCESS) - return ERROR_INDEX_ABSENT; + if (MSIREG_OpenUserComponentsKey( szComponent, &hkey, FALSE )) return ERROR_UNKNOWN_COMPONENT; - info = msi_reg_get_val_str( hkey, szQualifier ); + desc = reg_get_multisz( hkey, szQualifier ); RegCloseKey(hkey); + if (!desc) return ERROR_INDEX_ABSENT; - if (!info) - return ERROR_INDEX_ABSENT; + /* FIXME: handle multiple descriptors */ + ret = MsiDecomposeDescriptorW( desc, product, feature, comp, &size ); + msi_free( desc ); + if (ret != ERROR_SUCCESS) return ret; - MsiDecomposeDescriptorW(info, product, feature, component, &sz); + if (!szProduct) szProduct = product; + if (!comp[0]) + { + MSIINSTALLCONTEXT ctx; + WCHAR *components; + GUID guid; - if (!szProduct) - state = MSI_GetComponentPath(product, component, lpPathBuf, pcchPathBuf); - else - state = MSI_GetComponentPath(szProduct, component, lpPathBuf, pcchPathBuf); + /* use the first component of the feature if the descriptor component is empty */ + if ((ret = msi_locate_product( szProduct, &ctx ))) return ret; + if ((ret = MSIREG_OpenUserDataFeaturesKey( szProduct, NULL, ctx, &hkey, FALSE ))) + { + return ERROR_FILE_NOT_FOUND; + } + components = reg_get_sz( hkey, feature ); + RegCloseKey( hkey ); + if (!components) return ERROR_FILE_NOT_FOUND; - msi_free( info ); + if (strlenW( components ) < BASE85_SIZE || !decode_base85_guid( components, &guid )) + { + msi_free( components ); + return ERROR_FILE_NOT_FOUND; + } + msi_free( components ); + StringFromGUID2( &guid, comp, sizeof(comp)/sizeof(comp[0]) ); + } - if (state == INSTALLSTATE_MOREDATA) - return ERROR_MORE_DATA; - - if (state != INSTALLSTATE_LOCAL) - return ERROR_FILE_NOT_FOUND; + state = MSI_GetComponentPath( szProduct, comp, lpPathBuf, pcchPathBuf ); + if (state == INSTALLSTATE_MOREDATA) return ERROR_MORE_DATA; + if (state != INSTALLSTATE_LOCAL) return ERROR_FILE_NOT_FOUND; return ERROR_SUCCESS; } diff --git a/dlls/msi/tests/msi.c b/dlls/msi/tests/msi.c index 49d8f28e9d8..02298dfb2e9 100644 --- a/dlls/msi/tests/msi.c +++ b/dlls/msi/tests/msi.c @@ -3523,6 +3523,146 @@ static void test_MsiProvideComponent(void) DeleteFileA(msifile); } +static void test_MsiProvideQualifiedComponentEx(void) +{ + UINT r; + INSTALLSTATE state; + char comp[39], comp_squashed[33], comp2[39], comp2_base85[21], comp2_squashed[33]; + char prod[39], prod_base85[21], prod_squashed[33]; + char desc[MAX_PATH], buf[MAX_PATH], keypath[MAX_PATH], path[MAX_PATH]; + DWORD len = sizeof(buf); + REGSAM access = KEY_ALL_ACCESS; + HKEY hkey, hkey2, hkey3, hkey4, hkey5; + LONG res; + + if (is_process_limited()) + { + skip( "process is limited\n" ); + return; + } + + create_test_guid( comp, comp_squashed ); + compose_base85_guid( comp2, comp2_base85, comp2_squashed ); + compose_base85_guid( prod, prod_base85, prod_squashed ); + + r = MsiProvideQualifiedComponentExA( comp, "qualifier", INSTALLMODE_EXISTING, prod, 0, 0, buf, &len ); + ok( r == ERROR_UNKNOWN_COMPONENT, "got %u\n", r ); + + lstrcpyA( keypath, "Software\\Classes\\Installer\\Components\\" ); + lstrcatA( keypath, comp_squashed ); + + if (is_wow64) access |= KEY_WOW64_64KEY; + res = RegCreateKeyExA( HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, &hkey, NULL ); + ok( res == ERROR_SUCCESS, "got %d\n", res ); + + lstrcpyA( desc, prod_base85 ); + memcpy( desc + lstrlenA(desc), "feature<\0", sizeof("feature<\0") ); + res = RegSetValueExA( hkey, "qualifier", 0, REG_MULTI_SZ, (const BYTE *)desc, + lstrlenA(prod_base85) + sizeof("feature<\0") ); + ok( res == ERROR_SUCCESS, "got %d\n", res ); + + r = MsiProvideQualifiedComponentExA( comp, "qualifier", INSTALLMODE_EXISTING, prod, 0, 0, buf, &len ); + ok( r == ERROR_UNKNOWN_PRODUCT, "got %u\n", r ); + + r = MsiProvideQualifiedComponentExA( comp, "qualifier", INSTALLMODE_EXISTING, NULL, 0, 0, buf, &len ); + ok( r == ERROR_UNKNOWN_PRODUCT, "got %u\n", r ); + + state = MsiQueryProductStateA( prod ); + ok( state == INSTALLSTATE_UNKNOWN, "got %d\n", state ); + + lstrcpyA( keypath, "Software\\Classes\\Installer\\Products\\" ); + lstrcatA( keypath, prod_squashed ); + + res = RegCreateKeyExA( HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, &hkey2, NULL ); + ok( res == ERROR_SUCCESS, "got %d\n", res ); + + state = MsiQueryProductStateA( prod ); + ok( state == INSTALLSTATE_ADVERTISED, "got %d\n", state ); + + r = MsiProvideQualifiedComponentExA( comp, "qualifier", INSTALLMODE_EXISTING, prod, 0, 0, buf, &len ); + todo_wine ok( r == ERROR_UNKNOWN_FEATURE, "got %u\n", r ); + + lstrcpyA( keypath, "Software\\Classes\\Installer\\Features\\" ); + lstrcatA( keypath, prod_squashed ); + + res = RegCreateKeyExA( HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, &hkey3, NULL ); + ok( res == ERROR_SUCCESS, "got %d\n", res ); + + state = MsiQueryFeatureStateA( prod, "feature" ); + ok( state == INSTALLSTATE_UNKNOWN, "got %d\n", state ); + + res = RegSetValueExA( hkey3, "feature", 0, REG_SZ, (const BYTE *)"", 1 ); + ok( res == ERROR_SUCCESS, "got %d\n", res ); + + state = MsiQueryFeatureStateA( prod, "feature" ); + ok( state == INSTALLSTATE_ADVERTISED, "got %d\n", state ); + + r = MsiProvideQualifiedComponentExA( comp, "qualifier", INSTALLMODE_EXISTING, prod, 0, 0, buf, &len ); + ok( r == ERROR_FILE_NOT_FOUND, "got %u\n", r ); + + len = sizeof(buf); + r = MsiProvideQualifiedComponentExA( comp, "qualifier", INSTALLMODE_EXISTING, NULL, 0, 0, buf, &len ); + ok( r == ERROR_FILE_NOT_FOUND, "got %u\n", r ); + + lstrcpyA( keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\S-1-5-18\\Products\\" ); + lstrcatA( keypath, prod_squashed ); + lstrcatA( keypath, "\\Features" ); + + res = RegCreateKeyExA( HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, &hkey4, NULL ); + ok( res == ERROR_SUCCESS, "got %d\n", res ); + + res = RegSetValueExA( hkey4, "feature", 0, REG_SZ, (const BYTE *)comp2_base85, sizeof(comp2_base85) ); + ok( res == ERROR_SUCCESS, "got %d\n", res ); + + state = MsiQueryFeatureStateA( prod, "feature" ); + ok( state == INSTALLSTATE_ADVERTISED, "got %d\n", state ); + + lstrcpyA( keypath, "Software\\Microsoft\\Windows\\CurrentVersion\\Installer\\UserData\\S-1-5-18\\Components\\" ); + lstrcatA( keypath, comp2_squashed ); + + res = RegCreateKeyExA( HKEY_LOCAL_MACHINE, keypath, 0, NULL, 0, access, NULL, &hkey5, NULL ); + ok( res == ERROR_SUCCESS, "got %d\n", res ); + + res = RegSetValueExA( hkey5, prod_squashed, 0, REG_SZ, (const BYTE *)"c:\\nosuchfile", sizeof("c:\\nosuchfile") ); + ok( res == ERROR_SUCCESS, "got %d\n", res ); + + state = MsiQueryFeatureStateA( prod, "feature" ); + ok( state == INSTALLSTATE_LOCAL, "got %d\n", state ); + + r = MsiProvideQualifiedComponentExA( comp, "qualifier", INSTALLMODE_EXISTING, prod, 0, 0, buf, &len ); + ok( r == ERROR_FILE_NOT_FOUND, "got %u\n", r ); + + GetCurrentDirectoryA( MAX_PATH, path ); + lstrcatA( path, "\\msitest" ); + CreateDirectoryA( path, NULL ); + lstrcatA( path, "\\test.txt" ); + create_file( path, "test", 100 ); + + res = RegSetValueExA( hkey5, prod_squashed, 0, REG_SZ, (const BYTE *)path, lstrlenA(path) + 1 ); + ok( res == ERROR_SUCCESS, "got %d\n", res ); + + buf[0] = 0; + len = sizeof(buf); + r = MsiProvideQualifiedComponentExA( comp, "qualifier", INSTALLMODE_EXISTING, prod, 0, 0, buf, &len ); + ok( r == ERROR_SUCCESS, "got %u\n", r ); + ok( len == lstrlenA(path), "got %u\n", len ); + ok( !lstrcmpA( path, buf ), "got '%s'\n", buf ); + + DeleteFileA( "msitest\\text.txt" ); + RemoveDirectoryA( "msitest" ); + + delete_key( hkey5, "", access & KEY_WOW64_64KEY ); + RegCloseKey( hkey5 ); + delete_key( hkey4, "", access & KEY_WOW64_64KEY ); + RegCloseKey( hkey4 ); + delete_key( hkey3, "", access & KEY_WOW64_64KEY ); + RegCloseKey( hkey3 ); + delete_key( hkey2, "", access & KEY_WOW64_64KEY ); + RegCloseKey( hkey2 ); + delete_key( hkey, "", access & KEY_WOW64_64KEY ); + RegCloseKey( hkey ); +} + static void test_MsiGetProductCode(void) { HKEY compkey, prodkey; @@ -14447,6 +14587,7 @@ START_TEST(msi) if (pMsiGetComponentPathExA) test_concurrentinstall(); test_command_line_parsing(); + test_MsiProvideQualifiedComponentEx(); SetCurrentDirectoryA(prev_path); }