xref: /reactos/dll/win32/msi/upgrade.c (revision 8a978a17)
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 #include "wine/unicode.h"
40 
41 WINE_DEFAULT_DEBUG_CHANNEL(msi);
42 
43 static BOOL check_language(DWORD lang1, LPCWSTR lang2, DWORD attributes)
44 {
45     DWORD langdword;
46 
47     if (!lang2 || lang2[0]==0)
48         return TRUE;
49 
50     langdword = atoiW(lang2);
51 
52     if (attributes & msidbUpgradeAttributesLanguagesExclusive)
53         return (lang1 != langdword);
54     else
55         return (lang1 == langdword);
56 }
57 
58 static void append_productcode(MSIPACKAGE* package, LPCWSTR action_property,
59                                LPCWSTR productid)
60 {
61     LPWSTR prop;
62     LPWSTR newprop;
63     DWORD len;
64     UINT r;
65 
66     prop = msi_dup_property(package->db, action_property );
67     if (prop)
68         len = strlenW(prop);
69     else
70         len = 0;
71 
72     /*separator*/
73     len ++;
74 
75     len += strlenW(productid);
76 
77     /*null*/
78     len++;
79 
80     newprop = msi_alloc( len*sizeof(WCHAR) );
81 
82     if (prop)
83     {
84         strcpyW(newprop,prop);
85         strcatW(newprop,szSemiColon);
86     }
87     else
88         newprop[0] = 0;
89     strcatW(newprop,productid);
90 
91     r = msi_set_property( package->db, action_property, newprop, -1 );
92     if (r == ERROR_SUCCESS && !strcmpW( action_property, szSourceDir ))
93         msi_reset_folders( package, TRUE );
94 
95     TRACE("Found Related Product... %s now %s\n",
96           debugstr_w(action_property), debugstr_w(newprop));
97 
98     msi_free( prop );
99     msi_free( newprop );
100 }
101 
102 static UINT ITERATE_FindRelatedProducts(MSIRECORD *rec, LPVOID param)
103 {
104     MSIPACKAGE *package = param;
105     WCHAR product[SQUASHED_GUID_SIZE];
106     DWORD index = 0, attributes = 0, sz = sizeof(product)/sizeof(product[0]);
107     LPCWSTR upgrade_code;
108     HKEY hkey = 0;
109     UINT rc = ERROR_SUCCESS;
110     MSIRECORD *uirow;
111 
112     upgrade_code = MSI_RecordGetString(rec,1);
113 
114     rc = MSIREG_OpenUpgradeCodesKey(upgrade_code, &hkey, FALSE);
115     if (rc != ERROR_SUCCESS)
116         return ERROR_SUCCESS;
117 
118     uirow = MSI_CreateRecord(1);
119     attributes = MSI_RecordGetInteger(rec,5);
120 
121     while (rc == ERROR_SUCCESS)
122     {
123         rc = RegEnumValueW(hkey, index, product, &sz, NULL, NULL, NULL, NULL);
124         if (rc == ERROR_SUCCESS)
125         {
126             WCHAR productid[GUID_SIZE];
127             LPCWSTR ver, language, action_property;
128             DWORD check = 0, comp_ver, sz = 0x100;
129             HKEY hukey;
130             INT r;
131 
132             TRACE("Looking at index %u product %s\n", index, debugstr_w(product));
133 
134             unsquash_guid(product, productid);
135             if (MSIREG_OpenProductKey(productid, NULL, MSIINSTALLCONTEXT_USERMANAGED, &hukey, FALSE) &&
136                 MSIREG_OpenProductKey(productid, NULL, MSIINSTALLCONTEXT_USERUNMANAGED, &hukey, FALSE) &&
137                 MSIREG_OpenProductKey(productid, NULL, MSIINSTALLCONTEXT_MACHINE, &hukey, FALSE))
138             {
139                 TRACE("product key not found\n");
140                 rc = ERROR_SUCCESS;
141                 index ++;
142                 continue;
143             }
144 
145             sz = sizeof(DWORD);
146             RegQueryValueExW(hukey, INSTALLPROPERTY_VERSIONW, NULL, NULL, (LPBYTE)&check, &sz);
147 
148             /* check version minimum */
149             ver = MSI_RecordGetString(rec,2);
150             if (ver)
151             {
152                 comp_ver = msi_version_str_to_dword(ver);
153                 r = check - comp_ver;
154                 if (r < 0 || (r == 0 && !(attributes & msidbUpgradeAttributesVersionMinInclusive)))
155                 {
156                     TRACE("version below minimum\n");
157                     RegCloseKey(hukey);
158                     index ++;
159                     continue;
160                 }
161             }
162 
163             /* check version maximum */
164             ver = MSI_RecordGetString(rec,3);
165             if (ver)
166             {
167                 comp_ver = msi_version_str_to_dword(ver);
168                 r = check - comp_ver;
169                 if (r > 0 || (r == 0 && !(attributes & msidbUpgradeAttributesVersionMaxInclusive)))
170                 {
171                     RegCloseKey(hukey);
172                     index ++;
173                     continue;
174                 }
175                 TRACE("version above maximum\n");
176             }
177 
178             /* check language */
179             sz = sizeof(DWORD);
180             RegQueryValueExW(hukey, INSTALLPROPERTY_LANGUAGEW, NULL, NULL, (LPBYTE)&check, &sz);
181             RegCloseKey(hukey);
182             language = MSI_RecordGetString(rec,4);
183             if (!check_language(check, language, attributes))
184             {
185                 index ++;
186                 TRACE("language doesn't match\n");
187                 continue;
188             }
189             TRACE("found related product\n");
190 
191             action_property = MSI_RecordGetString(rec, 7);
192             append_productcode(package, action_property, productid);
193             MSI_RecordSetStringW(uirow, 1, productid);
194             MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, uirow);
195         }
196         index ++;
197     }
198     RegCloseKey(hkey);
199     msiobj_release( &uirow->hdr);
200 
201     return ERROR_SUCCESS;
202 }
203 
204 UINT ACTION_FindRelatedProducts(MSIPACKAGE *package)
205 {
206     static const WCHAR query[] = {
207         'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
208         '`','U','p','g','r','a','d','e','`',0};
209     MSIQUERY *view;
210     UINT rc;
211 
212     if (msi_get_property_int(package->db, szInstalled, 0))
213     {
214         TRACE("Skipping FindRelatedProducts action: product already installed\n");
215         return ERROR_SUCCESS;
216     }
217     if (msi_action_is_unique(package, szFindRelatedProducts))
218     {
219         TRACE("Skipping FindRelatedProducts action: already done in UI sequence\n");
220         return ERROR_SUCCESS;
221     }
222     else
223         msi_register_unique_action(package, szFindRelatedProducts);
224 
225     rc = MSI_DatabaseOpenViewW(package->db, query, &view);
226     if (rc != ERROR_SUCCESS)
227         return ERROR_SUCCESS;
228 
229     rc = MSI_IterateRecords(view, NULL, ITERATE_FindRelatedProducts, package);
230     msiobj_release(&view->hdr);
231     return rc;
232 }
233