1 /* 2 * Implementation of the Microsoft Installer (msi.dll) 3 * 4 * Copyright 2005 Aric Stewart for CodeWeavers 5 * 6 * This library is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU Lesser General Public 8 * License as published by the Free Software Foundation; either 9 * version 2.1 of the License, or (at your option) any later version. 10 * 11 * This library is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 * Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public 17 * License along with this library; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 19 */ 20 21 /* 22 * Actions focused on in this module 23 * 24 * FindRelatedProducts 25 * MigrateFeatureStates (TODO) 26 * RemoveExistingProducts (TODO) 27 */ 28 29 #include <stdarg.h> 30 31 #include "windef.h" 32 #include "winbase.h" 33 #include "winerror.h" 34 #include "winreg.h" 35 #include "wine/debug.h" 36 #include "msidefs.h" 37 #include "msipriv.h" 38 #include "winuser.h" 39 40 WINE_DEFAULT_DEBUG_CHANNEL(msi); 41 42 static BOOL check_language(DWORD lang1, LPCWSTR lang2, DWORD attributes) 43 { 44 DWORD langdword; 45 46 if (!lang2 || lang2[0]==0) 47 return TRUE; 48 49 langdword = wcstol(lang2, NULL, 10); 50 51 if (attributes & msidbUpgradeAttributesLanguagesExclusive) 52 return (lang1 != langdword); 53 else 54 return (lang1 == langdword); 55 } 56 57 static BOOL find_product( const WCHAR *list, const WCHAR *product ) 58 { 59 const WCHAR *p = list, *q; 60 61 if (!list) return FALSE; 62 for (;;) 63 { 64 while (*p && *p != '{') p++; 65 if (*p != '{') return FALSE; 66 q = p; 67 while (*q && *q != '}') q++; 68 if (*q != '}') return FALSE; 69 q++; 70 if (q - p < lstrlenW( product )) return FALSE; 71 if (!memcmp( p, product, (q - p) * sizeof(WCHAR) )) return TRUE; 72 p = q + 1; 73 while (*p && *p != ';') p++; 74 if (*p != ';') break; 75 } 76 77 return FALSE; 78 } 79 80 static void append_productcode( MSIPACKAGE *package, const WCHAR *action_prop, const WCHAR *product ) 81 { 82 WCHAR *prop, *newprop; 83 DWORD len = 0; 84 UINT r; 85 86 prop = msi_dup_property( package->db, action_prop ); 87 if (find_product( prop, product )) 88 { 89 TRACE( "related product property %s already contains %s\n", debugstr_w(action_prop), debugstr_w(product) ); 90 msi_free( prop ); 91 return; 92 } 93 94 if (prop) len += lstrlenW( prop ); 95 len += lstrlenW( product ) + 2; 96 if (!(newprop = msi_alloc( len * sizeof(WCHAR) ))) return; 97 if (prop) 98 { 99 lstrcpyW( newprop, prop ); 100 lstrcatW( newprop, L";" ); 101 } 102 else newprop[0] = 0; 103 lstrcatW( newprop, product ); 104 105 r = msi_set_property( package->db, action_prop, newprop, -1 ); 106 if (r == ERROR_SUCCESS && !wcscmp( action_prop, L"SourceDir" )) 107 msi_reset_source_folders( package ); 108 109 TRACE( "related product property %s now %s\n", debugstr_w(action_prop), debugstr_w(newprop) ); 110 111 msi_free( prop ); 112 msi_free( newprop ); 113 } 114 115 static UINT ITERATE_FindRelatedProducts(MSIRECORD *rec, LPVOID param) 116 { 117 MSIPACKAGE *package = param; 118 WCHAR product[SQUASHED_GUID_SIZE]; 119 DWORD index = 0, attributes = 0, sz = ARRAY_SIZE(product); 120 LPCWSTR upgrade_code; 121 HKEY hkey = 0; 122 UINT rc = ERROR_SUCCESS; 123 MSIRECORD *uirow; 124 125 upgrade_code = MSI_RecordGetString(rec,1); 126 127 rc = MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey, FALSE); 128 if (rc != ERROR_SUCCESS) 129 return ERROR_SUCCESS; 130 131 uirow = MSI_CreateRecord(1); 132 attributes = MSI_RecordGetInteger(rec,5); 133 134 while (rc == ERROR_SUCCESS) 135 { 136 rc = RegEnumValueW(hkey, index, product, &sz, NULL, NULL, NULL, NULL); 137 if (rc == ERROR_SUCCESS) 138 { 139 WCHAR productid[GUID_SIZE]; 140 LPCWSTR ver, language, action_property; 141 DWORD check = 0, comp_ver, sz = 0x100; 142 HKEY hukey; 143 INT r; 144 145 TRACE( "looking at index %lu product %s\n", index, debugstr_w(product) ); 146 147 unsquash_guid(product, productid); 148 if (MSIREG_OpenProductKey(productid, NULL, MSIINSTALLCONTEXT_USERMANAGED, &hukey, FALSE) && 149 MSIREG_OpenProductKey(productid, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, &hukey, FALSE) && 150 MSIREG_OpenProductKey(productid, NULL, MSIINSTALLCONTEXT_MACHINE, &hukey, FALSE)) 151 { 152 TRACE("product key not found\n"); 153 rc = ERROR_SUCCESS; 154 index ++; 155 continue; 156 } 157 158 sz = sizeof(DWORD); 159 RegQueryValueExW(hukey, INSTALLPROPERTY_VERSIONW, NULL, NULL, (LPBYTE)&check, &sz); 160 161 /* check version minimum */ 162 ver = MSI_RecordGetString(rec,2); 163 if (ver) 164 { 165 comp_ver = msi_version_str_to_dword(ver); 166 r = check - comp_ver; 167 if (r < 0 || (r == 0 && !(attributes & msidbUpgradeAttributesVersionMinInclusive))) 168 { 169 TRACE("version below minimum\n"); 170 RegCloseKey(hukey); 171 index ++; 172 continue; 173 } 174 } 175 176 /* check version maximum */ 177 ver = MSI_RecordGetString(rec,3); 178 if (ver) 179 { 180 comp_ver = msi_version_str_to_dword(ver); 181 r = check - comp_ver; 182 if (r > 0 || (r == 0 && !(attributes & msidbUpgradeAttributesVersionMaxInclusive))) 183 { 184 RegCloseKey(hukey); 185 index ++; 186 continue; 187 } 188 TRACE("version above maximum\n"); 189 } 190 191 /* check language */ 192 sz = sizeof(DWORD); 193 RegQueryValueExW(hukey, INSTALLPROPERTY_LANGUAGEW, NULL, NULL, (LPBYTE)&check, &sz); 194 RegCloseKey(hukey); 195 language = MSI_RecordGetString(rec,4); 196 if (!check_language(check, language, attributes)) 197 { 198 index ++; 199 TRACE("language doesn't match\n"); 200 continue; 201 } 202 TRACE("found related product\n"); 203 204 action_property = MSI_RecordGetString(rec, 7); 205 append_productcode(package, action_property, productid); 206 MSI_RecordSetStringW(uirow, 1, productid); 207 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow); 208 } 209 index ++; 210 } 211 RegCloseKey(hkey); 212 msiobj_release( &uirow->hdr); 213 214 return ERROR_SUCCESS; 215 } 216 217 UINT ACTION_FindRelatedProducts(MSIPACKAGE *package) 218 { 219 MSIQUERY *view; 220 UINT rc; 221 222 if (msi_get_property_int(package->db, L"Installed", 0)) 223 { 224 TRACE("Skipping FindRelatedProducts action: product already installed\n"); 225 return ERROR_SUCCESS; 226 } 227 if (msi_action_is_unique(package, L"FindRelatedProducts")) 228 { 229 TRACE("Skipping FindRelatedProducts action: already done in UI sequence\n"); 230 return ERROR_SUCCESS; 231 } 232 else 233 msi_register_unique_action(package, L"FindRelatedProducts"); 234 235 rc = MSI_DatabaseOpenViewW(package->db, L"SELECT * FROM `Upgrade`", &view); 236 if (rc != ERROR_SUCCESS) 237 return ERROR_SUCCESS; 238 239 rc = MSI_IterateRecords(view, NULL, ITERATE_FindRelatedProducts, package); 240 msiobj_release(&view->hdr); 241 return rc; 242 } 243