push 0a0aa53cd365a71ca6121b6df157ca635450378f
[wine/hacks.git] / dlls / msi / action.c
blobc8249045943d9fd92ca28c06246321b860337250
1 /*
2 * Implementation of the Microsoft Installer (msi.dll)
4 * Copyright 2004,2005 Aric Stewart for CodeWeavers
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.
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.
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
21 #include <stdarg.h>
23 #define COBJMACROS
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "winreg.h"
29 #include "winsvc.h"
30 #include "odbcinst.h"
31 #include "wine/debug.h"
32 #include "msidefs.h"
33 #include "msipriv.h"
34 #include "winuser.h"
35 #include "shlobj.h"
36 #include "objbase.h"
37 #include "mscoree.h"
38 #include "fusion.h"
39 #include "shlwapi.h"
40 #include "wine/unicode.h"
41 #include "winver.h"
43 #define REG_PROGRESS_VALUE 13200
44 #define COMPONENT_PROGRESS_VALUE 24000
46 WINE_DEFAULT_DEBUG_CHANNEL(msi);
49 * Prototypes
51 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran);
52 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package);
53 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI);
54 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action, UINT* rc, BOOL force);
57 * consts and values used
59 static const WCHAR c_colon[] = {'C',':','\\',0};
61 static const WCHAR szCreateFolders[] =
62 {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
63 static const WCHAR szCostFinalize[] =
64 {'C','o','s','t','F','i','n','a','l','i','z','e',0};
65 const WCHAR szInstallFiles[] =
66 {'I','n','s','t','a','l','l','F','i','l','e','s',0};
67 const WCHAR szDuplicateFiles[] =
68 {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
69 static const WCHAR szWriteRegistryValues[] =
70 {'W','r','i','t','e','R','e','g','i','s','t','r','y',
71 'V','a','l','u','e','s',0};
72 static const WCHAR szCostInitialize[] =
73 {'C','o','s','t','I','n','i','t','i','a','l','i','z','e',0};
74 static const WCHAR szFileCost[] =
75 {'F','i','l','e','C','o','s','t',0};
76 static const WCHAR szInstallInitialize[] =
77 {'I','n','s','t','a','l','l','I','n','i','t','i','a','l','i','z','e',0};
78 static const WCHAR szInstallValidate[] =
79 {'I','n','s','t','a','l','l','V','a','l','i','d','a','t','e',0};
80 static const WCHAR szLaunchConditions[] =
81 {'L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','s',0};
82 static const WCHAR szProcessComponents[] =
83 {'P','r','o','c','e','s','s','C','o','m','p','o','n','e','n','t','s',0};
84 static const WCHAR szRegisterTypeLibraries[] =
85 {'R','e','g','i','s','t','e','r','T','y','p','e',
86 'L','i','b','r','a','r','i','e','s',0};
87 const WCHAR szRegisterClassInfo[] =
88 {'R','e','g','i','s','t','e','r','C','l','a','s','s','I','n','f','o',0};
89 const WCHAR szRegisterProgIdInfo[] =
90 {'R','e','g','i','s','t','e','r','P','r','o','g','I','d','I','n','f','o',0};
91 static const WCHAR szCreateShortcuts[] =
92 {'C','r','e','a','t','e','S','h','o','r','t','c','u','t','s',0};
93 static const WCHAR szPublishProduct[] =
94 {'P','u','b','l','i','s','h','P','r','o','d','u','c','t',0};
95 static const WCHAR szWriteIniValues[] =
96 {'W','r','i','t','e','I','n','i','V','a','l','u','e','s',0};
97 static const WCHAR szSelfRegModules[] =
98 {'S','e','l','f','R','e','g','M','o','d','u','l','e','s',0};
99 static const WCHAR szPublishFeatures[] =
100 {'P','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
101 static const WCHAR szRegisterProduct[] =
102 {'R','e','g','i','s','t','e','r','P','r','o','d','u','c','t',0};
103 static const WCHAR szInstallExecute[] =
104 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',0};
105 static const WCHAR szInstallExecuteAgain[] =
106 {'I','n','s','t','a','l','l','E','x','e','c','u','t','e',
107 'A','g','a','i','n',0};
108 static const WCHAR szInstallFinalize[] =
109 {'I','n','s','t','a','l','l','F','i','n','a','l','i','z','e',0};
110 static const WCHAR szForceReboot[] =
111 {'F','o','r','c','e','R','e','b','o','o','t',0};
112 static const WCHAR szResolveSource[] =
113 {'R','e','s','o','l','v','e','S','o','u','r','c','e',0};
114 static const WCHAR szAppSearch[] =
115 {'A','p','p','S','e','a','r','c','h',0};
116 static const WCHAR szAllocateRegistrySpace[] =
117 {'A','l','l','o','c','a','t','e','R','e','g','i','s','t','r','y',
118 'S','p','a','c','e',0};
119 static const WCHAR szBindImage[] =
120 {'B','i','n','d','I','m','a','g','e',0};
121 static const WCHAR szCCPSearch[] =
122 {'C','C','P','S','e','a','r','c','h',0};
123 static const WCHAR szDeleteServices[] =
124 {'D','e','l','e','t','e','S','e','r','v','i','c','e','s',0};
125 static const WCHAR szDisableRollback[] =
126 {'D','i','s','a','b','l','e','R','o','l','l','b','a','c','k',0};
127 static const WCHAR szExecuteAction[] =
128 {'E','x','e','c','u','t','e','A','c','t','i','o','n',0};
129 const WCHAR szFindRelatedProducts[] =
130 {'F','i','n','d','R','e','l','a','t','e','d',
131 'P','r','o','d','u','c','t','s',0};
132 static const WCHAR szInstallAdminPackage[] =
133 {'I','n','s','t','a','l','l','A','d','m','i','n',
134 'P','a','c','k','a','g','e',0};
135 static const WCHAR szInstallSFPCatalogFile[] =
136 {'I','n','s','t','a','l','l','S','F','P','C','a','t','a','l','o','g',
137 'F','i','l','e',0};
138 static const WCHAR szIsolateComponents[] =
139 {'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t','s',0};
140 const WCHAR szMigrateFeatureStates[] =
141 {'M','i','g','r','a','t','e','F','e','a','t','u','r','e',
142 'S','t','a','t','e','s',0};
143 const WCHAR szMoveFiles[] =
144 {'M','o','v','e','F','i','l','e','s',0};
145 static const WCHAR szMsiPublishAssemblies[] =
146 {'M','s','i','P','u','b','l','i','s','h',
147 'A','s','s','e','m','b','l','i','e','s',0};
148 static const WCHAR szMsiUnpublishAssemblies[] =
149 {'M','s','i','U','n','p','u','b','l','i','s','h',
150 'A','s','s','e','m','b','l','i','e','s',0};
151 static const WCHAR szInstallODBC[] =
152 {'I','n','s','t','a','l','l','O','D','B','C',0};
153 static const WCHAR szInstallServices[] =
154 {'I','n','s','t','a','l','l','S','e','r','v','i','c','e','s',0};
155 const WCHAR szPatchFiles[] =
156 {'P','a','t','c','h','F','i','l','e','s',0};
157 static const WCHAR szPublishComponents[] =
158 {'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t','s',0};
159 static const WCHAR szRegisterComPlus[] =
160 {'R','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
161 const WCHAR szRegisterExtensionInfo[] =
162 {'R','e','g','i','s','t','e','r','E','x','t','e','n','s','i','o','n',
163 'I','n','f','o',0};
164 static const WCHAR szRegisterFonts[] =
165 {'R','e','g','i','s','t','e','r','F','o','n','t','s',0};
166 const WCHAR szRegisterMIMEInfo[] =
167 {'R','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
168 static const WCHAR szRegisterUser[] =
169 {'R','e','g','i','s','t','e','r','U','s','e','r',0};
170 const WCHAR szRemoveDuplicateFiles[] =
171 {'R','e','m','o','v','e','D','u','p','l','i','c','a','t','e',
172 'F','i','l','e','s',0};
173 static const WCHAR szRemoveEnvironmentStrings[] =
174 {'R','e','m','o','v','e','E','n','v','i','r','o','n','m','e','n','t',
175 'S','t','r','i','n','g','s',0};
176 const WCHAR szRemoveExistingProducts[] =
177 {'R','e','m','o','v','e','E','x','i','s','t','i','n','g',
178 'P','r','o','d','u','c','t','s',0};
179 const WCHAR szRemoveFiles[] =
180 {'R','e','m','o','v','e','F','i','l','e','s',0};
181 static const WCHAR szRemoveFolders[] =
182 {'R','e','m','o','v','e','F','o','l','d','e','r','s',0};
183 static const WCHAR szRemoveIniValues[] =
184 {'R','e','m','o','v','e','I','n','i','V','a','l','u','e','s',0};
185 static const WCHAR szRemoveODBC[] =
186 {'R','e','m','o','v','e','O','D','B','C',0};
187 static const WCHAR szRemoveRegistryValues[] =
188 {'R','e','m','o','v','e','R','e','g','i','s','t','r','y',
189 'V','a','l','u','e','s',0};
190 static const WCHAR szRemoveShortcuts[] =
191 {'R','e','m','o','v','e','S','h','o','r','t','c','u','t','s',0};
192 static const WCHAR szRMCCPSearch[] =
193 {'R','M','C','C','P','S','e','a','r','c','h',0};
194 static const WCHAR szScheduleReboot[] =
195 {'S','c','h','e','d','u','l','e','R','e','b','o','o','t',0};
196 static const WCHAR szSelfUnregModules[] =
197 {'S','e','l','f','U','n','r','e','g','M','o','d','u','l','e','s',0};
198 static const WCHAR szSetODBCFolders[] =
199 {'S','e','t','O','D','B','C','F','o','l','d','e','r','s',0};
200 static const WCHAR szStartServices[] =
201 {'S','t','a','r','t','S','e','r','v','i','c','e','s',0};
202 static const WCHAR szStopServices[] =
203 {'S','t','o','p','S','e','r','v','i','c','e','s',0};
204 static const WCHAR szUnpublishComponents[] =
205 {'U','n','p','u','b','l','i','s','h',
206 'C','o','m','p','o','n','e','n','t','s',0};
207 static const WCHAR szUnpublishFeatures[] =
208 {'U','n','p','u','b','l','i','s','h','F','e','a','t','u','r','e','s',0};
209 const WCHAR szUnregisterClassInfo[] =
210 {'U','n','r','e','g','i','s','t','e','r','C','l','a','s','s',
211 'I','n','f','o',0};
212 static const WCHAR szUnregisterComPlus[] =
213 {'U','n','r','e','g','i','s','t','e','r','C','o','m','P','l','u','s',0};
214 const WCHAR szUnregisterExtensionInfo[] =
215 {'U','n','r','e','g','i','s','t','e','r',
216 'E','x','t','e','n','s','i','o','n','I','n','f','o',0};
217 static const WCHAR szUnregisterFonts[] =
218 {'U','n','r','e','g','i','s','t','e','r','F','o','n','t','s',0};
219 const WCHAR szUnregisterMIMEInfo[] =
220 {'U','n','r','e','g','i','s','t','e','r','M','I','M','E','I','n','f','o',0};
221 const WCHAR szUnregisterProgIdInfo[] =
222 {'U','n','r','e','g','i','s','t','e','r','P','r','o','g','I','d',
223 'I','n','f','o',0};
224 static const WCHAR szUnregisterTypeLibraries[] =
225 {'U','n','r','e','g','i','s','t','e','r','T','y','p','e',
226 'L','i','b','r','a','r','i','e','s',0};
227 static const WCHAR szValidateProductID[] =
228 {'V','a','l','i','d','a','t','e','P','r','o','d','u','c','t','I','D',0};
229 static const WCHAR szWriteEnvironmentStrings[] =
230 {'W','r','i','t','e','E','n','v','i','r','o','n','m','e','n','t',
231 'S','t','r','i','n','g','s',0};
233 /* action handlers */
234 typedef UINT (*STANDARDACTIONHANDLER)(MSIPACKAGE*);
236 struct _actions {
237 LPCWSTR action;
238 STANDARDACTIONHANDLER handler;
242 /********************************************************
243 * helper functions
244 ********************************************************/
246 static void ui_actionstart(MSIPACKAGE *package, LPCWSTR action)
248 static const WCHAR Query_t[] =
249 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
250 '`','A','c','t','i','o', 'n','T','e','x','t','`',' ',
251 'W','H','E','R','E', ' ','`','A','c','t','i','o','n','`',' ','=',
252 ' ','\'','%','s','\'',0};
253 MSIRECORD * row;
255 row = MSI_QueryGetRecord( package->db, Query_t, action );
256 if (!row)
257 return;
258 MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONSTART, row);
259 msiobj_release(&row->hdr);
262 static void ui_actioninfo(MSIPACKAGE *package, LPCWSTR action, BOOL start,
263 UINT rc)
265 MSIRECORD * row;
266 static const WCHAR template_s[]=
267 {'A','c','t','i','o','n',' ','s','t','a','r','t',' ','%','s',':',' ',
268 '%','s', '.',0};
269 static const WCHAR template_e[]=
270 {'A','c','t','i','o','n',' ','e','n','d','e','d',' ','%','s',':',' ',
271 '%','s', '.',' ','R','e','t','u','r','n',' ','v','a','l','u','e',' ',
272 '%','i','.',0};
273 static const WCHAR format[] =
274 {'H','H','\'',':','\'','m','m','\'',':','\'','s','s',0};
275 WCHAR message[1024];
276 WCHAR timet[0x100];
278 GetTimeFormatW(LOCALE_USER_DEFAULT, 0, NULL, format, timet, 0x100);
279 if (start)
280 sprintfW(message,template_s,timet,action);
281 else
282 sprintfW(message,template_e,timet,action,rc);
284 row = MSI_CreateRecord(1);
285 MSI_RecordSetStringW(row,1,message);
287 MSI_ProcessMessage(package, INSTALLMESSAGE_INFO, row);
288 msiobj_release(&row->hdr);
291 UINT msi_parse_command_line( MSIPACKAGE *package, LPCWSTR szCommandLine,
292 BOOL preserve_case )
294 LPCWSTR ptr,ptr2;
295 BOOL quote;
296 DWORD len;
297 LPWSTR prop = NULL, val = NULL;
299 if (!szCommandLine)
300 return ERROR_SUCCESS;
302 ptr = szCommandLine;
304 while (*ptr)
306 if (*ptr==' ')
308 ptr++;
309 continue;
312 TRACE("Looking at %s\n",debugstr_w(ptr));
314 ptr2 = strchrW(ptr,'=');
315 if (!ptr2)
317 ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
318 break;
321 quote = FALSE;
323 len = ptr2-ptr;
324 prop = msi_alloc((len+1)*sizeof(WCHAR));
325 memcpy(prop,ptr,len*sizeof(WCHAR));
326 prop[len]=0;
328 if (!preserve_case)
329 struprW(prop);
331 ptr2++;
333 len = 0;
334 ptr = ptr2;
335 while (*ptr && (quote || (!quote && *ptr!=' ')))
337 if (*ptr == '"')
338 quote = !quote;
339 ptr++;
340 len++;
343 if (*ptr2=='"')
345 ptr2++;
346 len -= 2;
348 val = msi_alloc((len+1)*sizeof(WCHAR));
349 memcpy(val,ptr2,len*sizeof(WCHAR));
350 val[len] = 0;
352 if (lstrlenW(prop) > 0)
354 TRACE("Found commandline property (%s) = (%s)\n",
355 debugstr_w(prop), debugstr_w(val));
356 MSI_SetPropertyW(package,prop,val);
358 msi_free(val);
359 msi_free(prop);
362 return ERROR_SUCCESS;
366 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
368 LPCWSTR pc;
369 LPWSTR p, *ret = NULL;
370 UINT count = 0;
372 if (!str)
373 return ret;
375 /* count the number of substrings */
376 for ( pc = str, count = 0; pc; count++ )
378 pc = strchrW( pc, sep );
379 if (pc)
380 pc++;
383 /* allocate space for an array of substring pointers and the substrings */
384 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
385 (lstrlenW(str)+1) * sizeof(WCHAR) );
386 if (!ret)
387 return ret;
389 /* copy the string and set the pointers */
390 p = (LPWSTR) &ret[count+1];
391 lstrcpyW( p, str );
392 for( count = 0; (ret[count] = p); count++ )
394 p = strchrW( p, sep );
395 if (p)
396 *p++ = 0;
399 return ret;
402 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
404 static const WCHAR szProductCode[] =
405 { 'P','r','o','d','u','c','t','C','o','d','e',0 };
406 static const WCHAR szSystemLanguageID[] =
407 { 'S','y','s','t','e','m','L','a','n','g','u','a','g','e','I','D',0 };
409 LPWSTR prod_code, patch_product, langid = NULL, template = NULL;
410 UINT ret = ERROR_FUNCTION_FAILED;
412 prod_code = msi_dup_property( package, szProductCode );
413 patch_product = msi_get_suminfo_product( patch );
415 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
417 if ( strstrW( patch_product, prod_code ) )
419 static const WCHAR zero[] = {'0',0};
420 MSISUMMARYINFO *si;
421 const WCHAR *p;
423 si = MSI_GetSummaryInformationW( patch, 0 );
424 if (!si)
426 ERR("no summary information!\n");
427 goto end;
430 template = msi_suminfo_dup_string( si, PID_TEMPLATE );
431 if (!template)
433 ERR("no template property!\n");
434 msiobj_release( &si->hdr );
435 goto end;
438 if (!template[0])
440 ret = ERROR_SUCCESS;
441 msiobj_release( &si->hdr );
442 goto end;
445 langid = msi_dup_property( package, szSystemLanguageID );
446 if (!langid)
448 msiobj_release( &si->hdr );
449 goto end;
452 p = strchrW( template, ';' );
453 if (p && (!strcmpW( p + 1, langid ) || !strcmpW( p + 1, zero )))
455 TRACE("applicable transform\n");
456 ret = ERROR_SUCCESS;
459 /* FIXME: check platform */
461 msiobj_release( &si->hdr );
464 end:
465 msi_free( patch_product );
466 msi_free( prod_code );
467 msi_free( template );
468 msi_free( langid );
470 return ret;
473 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
474 MSIDATABASE *patch_db, LPCWSTR name )
476 UINT ret = ERROR_FUNCTION_FAILED;
477 IStorage *stg = NULL;
478 HRESULT r;
480 TRACE("%p %s\n", package, debugstr_w(name) );
482 if (*name++ != ':')
484 ERR("expected a colon in %s\n", debugstr_w(name));
485 return ERROR_FUNCTION_FAILED;
488 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
489 if (SUCCEEDED(r))
491 ret = msi_check_transform_applicable( package, stg );
492 if (ret == ERROR_SUCCESS)
493 msi_table_apply_transform( package->db, stg );
494 else
495 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
496 IStorage_Release( stg );
498 else
499 ERR("failed to open substorage %s\n", debugstr_w(name));
501 return ERROR_SUCCESS;
504 static UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
506 static const WCHAR szProdCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
507 LPWSTR guid_list, *guids, product_code;
508 UINT i, ret = ERROR_FUNCTION_FAILED;
510 product_code = msi_dup_property( package, szProdCode );
511 if (!product_code)
513 /* FIXME: the property ProductCode should be written into the DB somewhere */
514 ERR("no product code to check\n");
515 return ERROR_SUCCESS;
518 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
519 guids = msi_split_string( guid_list, ';' );
520 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
522 if (!lstrcmpW( guids[i], product_code ))
523 ret = ERROR_SUCCESS;
525 msi_free( guids );
526 msi_free( guid_list );
527 msi_free( product_code );
529 return ret;
532 static UINT msi_set_media_source_prop(MSIPACKAGE *package)
534 MSIQUERY *view;
535 MSIRECORD *rec = NULL;
536 LPWSTR patch;
537 LPCWSTR prop;
538 UINT r;
540 static const WCHAR szPatch[] = {'P','A','T','C','H',0};
541 static const WCHAR query[] = {'S','E','L','E','C','T',' ',
542 '`','S','o','u','r','c','e','`',' ','F','R','O','M',' ',
543 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
544 '`','S','o','u','r','c','e','`',' ','I','S',' ',
545 'N','O','T',' ','N','U','L','L',0};
547 r = MSI_DatabaseOpenViewW(package->db, query, &view);
548 if (r != ERROR_SUCCESS)
549 return r;
551 r = MSI_ViewExecute(view, 0);
552 if (r != ERROR_SUCCESS)
553 goto done;
555 if (MSI_ViewFetch(view, &rec) == ERROR_SUCCESS)
557 prop = MSI_RecordGetString(rec, 1);
558 patch = msi_dup_property(package, szPatch);
559 MSI_SetPropertyW(package, prop, patch);
560 msi_free(patch);
563 done:
564 if (rec) msiobj_release(&rec->hdr);
565 msiobj_release(&view->hdr);
567 return r;
570 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
572 MSISUMMARYINFO *si;
573 LPWSTR str, *substorage;
574 UINT i, r = ERROR_SUCCESS;
576 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
577 if (!si)
578 return ERROR_FUNCTION_FAILED;
580 if (msi_check_patch_applicable( package, si ) != ERROR_SUCCESS)
582 TRACE("Patch not applicable\n");
583 return ERROR_SUCCESS;
586 package->patch = msi_alloc(sizeof(MSIPATCHINFO));
587 if (!package->patch)
588 return ERROR_OUTOFMEMORY;
590 package->patch->patchcode = msi_suminfo_dup_string(si, PID_REVNUMBER);
591 if (!package->patch->patchcode)
592 return ERROR_OUTOFMEMORY;
594 /* enumerate the substorage */
595 str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
596 package->patch->transforms = str;
598 substorage = msi_split_string( str, ';' );
599 for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
600 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
602 msi_free( substorage );
603 msiobj_release( &si->hdr );
605 msi_set_media_source_prop(package);
607 return r;
610 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
612 MSIDATABASE *patch_db = NULL;
613 UINT r;
615 TRACE("%p %s\n", package, debugstr_w( file ) );
617 /* FIXME:
618 * We probably want to make sure we only open a patch collection here.
619 * Patch collections (.msp) and databases (.msi) have different GUIDs
620 * but currently MSI_OpenDatabaseW will accept both.
622 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
623 if ( r != ERROR_SUCCESS )
625 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
626 return r;
629 msi_parse_patch_summary( package, patch_db );
632 * There might be a CAB file in the patch package,
633 * so append it to the list of storage to search for streams.
635 append_storage_to_db( package->db, patch_db->storage );
637 msiobj_release( &patch_db->hdr );
639 return ERROR_SUCCESS;
642 /* get the PATCH property, and apply all the patches it specifies */
643 static UINT msi_apply_patches( MSIPACKAGE *package )
645 static const WCHAR szPatch[] = { 'P','A','T','C','H',0 };
646 LPWSTR patch_list, *patches;
647 UINT i, r = ERROR_SUCCESS;
649 patch_list = msi_dup_property( package, szPatch );
651 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
653 patches = msi_split_string( patch_list, ';' );
654 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
655 r = msi_apply_patch_package( package, patches[i] );
657 msi_free( patches );
658 msi_free( patch_list );
660 return r;
663 static UINT msi_apply_transforms( MSIPACKAGE *package )
665 static const WCHAR szTransforms[] = {
666 'T','R','A','N','S','F','O','R','M','S',0 };
667 LPWSTR xform_list, *xforms;
668 UINT i, r = ERROR_SUCCESS;
670 xform_list = msi_dup_property( package, szTransforms );
671 xforms = msi_split_string( xform_list, ';' );
673 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
675 if (xforms[i][0] == ':')
676 r = msi_apply_substorage_transform( package, package->db, xforms[i] );
677 else
678 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
681 msi_free( xforms );
682 msi_free( xform_list );
684 return r;
687 static BOOL ui_sequence_exists( MSIPACKAGE *package )
689 MSIQUERY *view;
690 UINT rc;
692 static const WCHAR ExecSeqQuery [] =
693 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
694 '`','I','n','s','t','a','l','l',
695 'U','I','S','e','q','u','e','n','c','e','`',
696 ' ','W','H','E','R','E',' ',
697 '`','S','e','q','u','e','n','c','e','`',' ',
698 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
699 '`','S','e','q','u','e','n','c','e','`',0};
701 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
702 if (rc == ERROR_SUCCESS)
704 msiobj_release(&view->hdr);
705 return TRUE;
708 return FALSE;
711 static UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
713 LPWSTR p, db;
714 LPWSTR source, check;
715 DWORD len;
717 static const WCHAR szOriginalDatabase[] =
718 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
720 db = msi_dup_property( package, szOriginalDatabase );
721 if (!db)
722 return ERROR_OUTOFMEMORY;
724 p = strrchrW( db, '\\' );
725 if (!p)
727 p = strrchrW( db, '/' );
728 if (!p)
730 msi_free(db);
731 return ERROR_SUCCESS;
735 len = p - db + 2;
736 source = msi_alloc( len * sizeof(WCHAR) );
737 lstrcpynW( source, db, len );
739 check = msi_dup_property( package, cszSourceDir );
740 if (!check || replace)
741 MSI_SetPropertyW( package, cszSourceDir, source );
743 msi_free( check );
745 check = msi_dup_property( package, cszSOURCEDIR );
746 if (!check || replace)
747 MSI_SetPropertyW( package, cszSOURCEDIR, source );
749 msi_free( check );
750 msi_free( source );
751 msi_free( db );
753 return ERROR_SUCCESS;
756 static UINT msi_set_context(MSIPACKAGE *package)
758 WCHAR val[10];
759 DWORD sz = 10;
760 DWORD num;
761 UINT r;
763 static const WCHAR szOne[] = {'1',0};
764 static const WCHAR szAllUsers[] = {'A','L','L','U','S','E','R','S',0};
766 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
768 r = MSI_GetPropertyW(package, szAllUsers, val, &sz);
769 if (r == ERROR_SUCCESS)
771 num = atolW(val);
772 if (num == 1 || num == 2)
773 package->Context = MSIINSTALLCONTEXT_MACHINE;
776 MSI_SetPropertyW(package, szAllUsers, szOne);
777 return ERROR_SUCCESS;
780 /****************************************************
781 * TOP level entry points
782 *****************************************************/
784 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
785 LPCWSTR szCommandLine )
787 UINT rc;
788 BOOL ui = FALSE, ui_exists;
789 static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
790 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
791 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
793 MSI_SetPropertyW(package, szAction, szInstall);
795 package->script = msi_alloc_zero(sizeof(MSISCRIPT));
797 package->script->InWhatSequence = SEQUENCE_INSTALL;
799 if (szPackagePath)
801 LPWSTR p, dir;
802 LPCWSTR file;
804 dir = strdupW(szPackagePath);
805 p = strrchrW(dir, '\\');
806 if (p)
808 *(++p) = 0;
809 file = szPackagePath + (p - dir);
811 else
813 msi_free(dir);
814 dir = msi_alloc(MAX_PATH*sizeof(WCHAR));
815 GetCurrentDirectoryW(MAX_PATH, dir);
816 lstrcatW(dir, cszbs);
817 file = szPackagePath;
820 msi_free( package->PackagePath );
821 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
822 if (!package->PackagePath)
824 msi_free(dir);
825 return ERROR_OUTOFMEMORY;
828 lstrcpyW(package->PackagePath, dir);
829 lstrcatW(package->PackagePath, file);
830 msi_free(dir);
832 msi_set_sourcedir_props(package, FALSE);
835 msi_parse_command_line( package, szCommandLine, FALSE );
837 msi_apply_transforms( package );
838 msi_apply_patches( package );
840 /* properties may have been added by a transform */
841 msi_clone_properties( package );
842 msi_set_context( package );
844 if ( (msi_get_property_int(package, szUILevel, 0) & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED )
846 package->script->InWhatSequence |= SEQUENCE_UI;
847 rc = ACTION_ProcessUISequence(package);
848 ui = TRUE;
849 ui_exists = ui_sequence_exists(package);
850 if (rc == ERROR_SUCCESS || !ui_exists)
852 package->script->InWhatSequence |= SEQUENCE_EXEC;
853 rc = ACTION_ProcessExecSequence(package,ui_exists);
856 else
857 rc = ACTION_ProcessExecSequence(package,FALSE);
859 package->script->CurrentlyScripting= FALSE;
861 /* process the ending type action */
862 if (rc == ERROR_SUCCESS)
863 ACTION_PerformActionSequence(package,-1,ui);
864 else if (rc == ERROR_INSTALL_USEREXIT)
865 ACTION_PerformActionSequence(package,-2,ui);
866 else if (rc == ERROR_INSTALL_SUSPEND)
867 ACTION_PerformActionSequence(package,-4,ui);
868 else /* failed */
869 ACTION_PerformActionSequence(package,-3,ui);
871 /* finish up running custom actions */
872 ACTION_FinishCustomActions(package);
874 return rc;
877 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
879 UINT rc = ERROR_SUCCESS;
880 MSIRECORD * row = 0;
881 static const WCHAR ExecSeqQuery[] =
882 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
883 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
884 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
885 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
887 static const WCHAR UISeqQuery[] =
888 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
889 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
890 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
891 ' ', '=',' ','%','i',0};
893 if (UI)
894 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
895 else
896 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
898 if (row)
900 LPCWSTR action, cond;
902 TRACE("Running the actions\n");
904 /* check conditions */
905 cond = MSI_RecordGetString(row,2);
907 /* this is a hack to skip errors in the condition code */
908 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
909 goto end;
911 action = MSI_RecordGetString(row,1);
912 if (!action)
914 ERR("failed to fetch action\n");
915 rc = ERROR_FUNCTION_FAILED;
916 goto end;
919 if (UI)
920 rc = ACTION_PerformUIAction(package,action,-1);
921 else
922 rc = ACTION_PerformAction(package,action,-1,FALSE);
923 end:
924 msiobj_release(&row->hdr);
926 else
927 rc = ERROR_SUCCESS;
929 return rc;
932 typedef struct {
933 MSIPACKAGE* package;
934 BOOL UI;
935 } iterate_action_param;
937 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
939 iterate_action_param *iap = param;
940 UINT rc;
941 LPCWSTR cond, action;
943 action = MSI_RecordGetString(row,1);
944 if (!action)
946 ERR("Error is retrieving action name\n");
947 return ERROR_FUNCTION_FAILED;
950 /* check conditions */
951 cond = MSI_RecordGetString(row,2);
953 /* this is a hack to skip errors in the condition code */
954 if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
956 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
957 return ERROR_SUCCESS;
960 if (iap->UI)
961 rc = ACTION_PerformUIAction(iap->package,action,-1);
962 else
963 rc = ACTION_PerformAction(iap->package,action,-1,FALSE);
965 msi_dialog_check_messages( NULL );
967 if (iap->package->CurrentInstallState != ERROR_SUCCESS )
968 rc = iap->package->CurrentInstallState;
970 if (rc == ERROR_FUNCTION_NOT_CALLED)
971 rc = ERROR_SUCCESS;
973 if (rc != ERROR_SUCCESS)
974 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
976 return rc;
979 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
981 MSIQUERY * view;
982 UINT r;
983 static const WCHAR query[] =
984 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
985 '`','%','s','`',
986 ' ','W','H','E','R','E',' ',
987 '`','S','e','q','u','e','n','c','e','`',' ',
988 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
989 '`','S','e','q','u','e','n','c','e','`',0};
990 iterate_action_param iap;
993 * FIXME: probably should be checking UILevel in the
994 * ACTION_PerformUIAction/ACTION_PerformAction
995 * rather than saving the UI level here. Those
996 * two functions can be merged too.
998 iap.package = package;
999 iap.UI = TRUE;
1001 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
1003 r = MSI_OpenQuery( package->db, &view, query, szTable );
1004 if (r == ERROR_SUCCESS)
1006 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
1007 msiobj_release(&view->hdr);
1010 return r;
1013 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
1015 MSIQUERY * view;
1016 UINT rc;
1017 static const WCHAR ExecSeqQuery[] =
1018 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1019 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
1020 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
1021 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
1022 'O','R','D','E','R',' ', 'B','Y',' ',
1023 '`','S','e','q','u','e','n','c','e','`',0 };
1024 MSIRECORD * row = 0;
1025 static const WCHAR IVQuery[] =
1026 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
1027 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
1028 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
1029 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
1030 ' ','\'', 'I','n','s','t','a','l','l',
1031 'V','a','l','i','d','a','t','e','\'', 0};
1032 INT seq = 0;
1033 iterate_action_param iap;
1035 iap.package = package;
1036 iap.UI = FALSE;
1038 if (package->script->ExecuteSequenceRun)
1040 TRACE("Execute Sequence already Run\n");
1041 return ERROR_SUCCESS;
1044 package->script->ExecuteSequenceRun = TRUE;
1046 /* get the sequence number */
1047 if (UIran)
1049 row = MSI_QueryGetRecord(package->db, IVQuery);
1050 if( !row )
1051 return ERROR_FUNCTION_FAILED;
1052 seq = MSI_RecordGetInteger(row,1);
1053 msiobj_release(&row->hdr);
1056 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
1057 if (rc == ERROR_SUCCESS)
1059 TRACE("Running the actions\n");
1061 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
1062 msiobj_release(&view->hdr);
1065 return rc;
1068 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
1070 MSIQUERY * view;
1071 UINT rc;
1072 static const WCHAR ExecSeqQuery [] =
1073 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
1074 '`','I','n','s','t','a','l','l',
1075 'U','I','S','e','q','u','e','n','c','e','`',
1076 ' ','W','H','E','R','E',' ',
1077 '`','S','e','q','u','e','n','c','e','`',' ',
1078 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
1079 '`','S','e','q','u','e','n','c','e','`',0};
1080 iterate_action_param iap;
1082 iap.package = package;
1083 iap.UI = TRUE;
1085 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
1087 if (rc == ERROR_SUCCESS)
1089 TRACE("Running the actions\n");
1091 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
1092 msiobj_release(&view->hdr);
1095 return rc;
1098 /********************************************************
1099 * ACTION helper functions and functions that perform the actions
1100 *******************************************************/
1101 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
1102 UINT* rc, UINT script, BOOL force )
1104 BOOL ret=FALSE;
1105 UINT arc;
1107 arc = ACTION_CustomAction(package, action, script, force);
1109 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1111 *rc = arc;
1112 ret = TRUE;
1114 return ret;
1118 * A lot of actions are really important even if they don't do anything
1119 * explicit... Lots of properties are set at the beginning of the installation
1120 * CostFinalize does a bunch of work to translate the directories and such
1122 * But until I get write access to the database that is hard, so I am going to
1123 * hack it to see if I can get something to run.
1125 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
1127 UINT rc = ERROR_SUCCESS;
1128 BOOL handled;
1130 TRACE("Performing action (%s)\n",debugstr_w(action));
1132 handled = ACTION_HandleStandardAction(package, action, &rc, force);
1134 if (!handled)
1135 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
1137 if (!handled)
1139 WARN("unhandled msi action %s\n",debugstr_w(action));
1140 rc = ERROR_FUNCTION_NOT_CALLED;
1143 return rc;
1146 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
1148 UINT rc = ERROR_SUCCESS;
1149 BOOL handled = FALSE;
1151 TRACE("Performing action (%s)\n",debugstr_w(action));
1153 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1155 if (!handled)
1156 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
1158 if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1159 handled = TRUE;
1161 if (!handled)
1163 WARN("unhandled msi action %s\n",debugstr_w(action));
1164 rc = ERROR_FUNCTION_NOT_CALLED;
1167 return rc;
1172 * Actual Action Handlers
1175 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1177 MSIPACKAGE *package = param;
1178 LPCWSTR dir;
1179 LPWSTR full_path;
1180 MSIRECORD *uirow;
1181 MSIFOLDER *folder;
1183 dir = MSI_RecordGetString(row,1);
1184 if (!dir)
1186 ERR("Unable to get folder id\n");
1187 return ERROR_SUCCESS;
1190 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1191 if (!full_path)
1193 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1194 return ERROR_SUCCESS;
1197 TRACE("Folder is %s\n",debugstr_w(full_path));
1199 /* UI stuff */
1200 uirow = MSI_CreateRecord(1);
1201 MSI_RecordSetStringW(uirow,1,full_path);
1202 ui_actiondata(package,szCreateFolders,uirow);
1203 msiobj_release( &uirow->hdr );
1205 if (folder->State == 0)
1206 create_full_pathW(full_path);
1208 folder->State = 3;
1210 msi_free(full_path);
1211 return ERROR_SUCCESS;
1214 /* FIXME: probably should merge this with the above function */
1215 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1217 UINT rc = ERROR_SUCCESS;
1218 MSIFOLDER *folder;
1219 LPWSTR install_path;
1221 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
1222 if (!install_path)
1223 return ERROR_FUNCTION_FAILED;
1225 /* create the path */
1226 if (folder->State == 0)
1228 create_full_pathW(install_path);
1229 folder->State = 2;
1231 msi_free(install_path);
1233 return rc;
1236 UINT msi_create_component_directories( MSIPACKAGE *package )
1238 MSICOMPONENT *comp;
1240 /* create all the folders required by the components are going to install */
1241 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1243 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1244 continue;
1245 msi_create_directory( package, comp->Directory );
1248 return ERROR_SUCCESS;
1252 * Also we cannot enable/disable components either, so for now I am just going
1253 * to do all the directories for all the components.
1255 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1257 static const WCHAR ExecSeqQuery[] =
1258 {'S','E','L','E','C','T',' ',
1259 '`','D','i','r','e','c','t','o','r','y','_','`',
1260 ' ','F','R','O','M',' ',
1261 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1262 UINT rc;
1263 MSIQUERY *view;
1265 /* create all the empty folders specified in the CreateFolder table */
1266 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1267 if (rc != ERROR_SUCCESS)
1268 return ERROR_SUCCESS;
1270 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1271 msiobj_release(&view->hdr);
1273 msi_create_component_directories( package );
1275 return rc;
1278 static UINT load_component( MSIRECORD *row, LPVOID param )
1280 MSIPACKAGE *package = param;
1281 MSICOMPONENT *comp;
1283 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1284 if (!comp)
1285 return ERROR_FUNCTION_FAILED;
1287 list_add_tail( &package->components, &comp->entry );
1289 /* fill in the data */
1290 comp->Component = msi_dup_record_field( row, 1 );
1292 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1294 comp->ComponentId = msi_dup_record_field( row, 2 );
1295 comp->Directory = msi_dup_record_field( row, 3 );
1296 comp->Attributes = MSI_RecordGetInteger(row,4);
1297 comp->Condition = msi_dup_record_field( row, 5 );
1298 comp->KeyPath = msi_dup_record_field( row, 6 );
1300 comp->Installed = INSTALLSTATE_UNKNOWN;
1301 msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
1303 return ERROR_SUCCESS;
1306 static UINT load_all_components( MSIPACKAGE *package )
1308 static const WCHAR query[] = {
1309 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1310 '`','C','o','m','p','o','n','e','n','t','`',0 };
1311 MSIQUERY *view;
1312 UINT r;
1314 if (!list_empty(&package->components))
1315 return ERROR_SUCCESS;
1317 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1318 if (r != ERROR_SUCCESS)
1319 return r;
1321 r = MSI_IterateRecords(view, NULL, load_component, package);
1322 msiobj_release(&view->hdr);
1323 return r;
1326 typedef struct {
1327 MSIPACKAGE *package;
1328 MSIFEATURE *feature;
1329 } _ilfs;
1331 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1333 ComponentList *cl;
1335 cl = msi_alloc( sizeof (*cl) );
1336 if ( !cl )
1337 return ERROR_NOT_ENOUGH_MEMORY;
1338 cl->component = comp;
1339 list_add_tail( &feature->Components, &cl->entry );
1341 return ERROR_SUCCESS;
1344 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1346 FeatureList *fl;
1348 fl = msi_alloc( sizeof(*fl) );
1349 if ( !fl )
1350 return ERROR_NOT_ENOUGH_MEMORY;
1351 fl->feature = child;
1352 list_add_tail( &parent->Children, &fl->entry );
1354 return ERROR_SUCCESS;
1357 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1359 _ilfs* ilfs = param;
1360 LPCWSTR component;
1361 MSICOMPONENT *comp;
1363 component = MSI_RecordGetString(row,1);
1365 /* check to see if the component is already loaded */
1366 comp = get_loaded_component( ilfs->package, component );
1367 if (!comp)
1369 ERR("unknown component %s\n", debugstr_w(component));
1370 return ERROR_FUNCTION_FAILED;
1373 add_feature_component( ilfs->feature, comp );
1374 comp->Enabled = TRUE;
1376 return ERROR_SUCCESS;
1379 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1381 MSIFEATURE *feature;
1383 if ( !name )
1384 return NULL;
1386 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1388 if ( !lstrcmpW( feature->Feature, name ) )
1389 return feature;
1392 return NULL;
1395 static UINT load_feature(MSIRECORD * row, LPVOID param)
1397 MSIPACKAGE* package = param;
1398 MSIFEATURE* feature;
1399 static const WCHAR Query1[] =
1400 {'S','E','L','E','C','T',' ',
1401 '`','C','o','m','p','o','n','e','n','t','_','`',
1402 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1403 'C','o','m','p','o','n','e','n','t','s','`',' ',
1404 'W','H','E','R','E',' ',
1405 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1406 MSIQUERY * view;
1407 UINT rc;
1408 _ilfs ilfs;
1410 /* fill in the data */
1412 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1413 if (!feature)
1414 return ERROR_NOT_ENOUGH_MEMORY;
1416 list_init( &feature->Children );
1417 list_init( &feature->Components );
1419 feature->Feature = msi_dup_record_field( row, 1 );
1421 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1423 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1424 feature->Title = msi_dup_record_field( row, 3 );
1425 feature->Description = msi_dup_record_field( row, 4 );
1427 if (!MSI_RecordIsNull(row,5))
1428 feature->Display = MSI_RecordGetInteger(row,5);
1430 feature->Level= MSI_RecordGetInteger(row,6);
1431 feature->Directory = msi_dup_record_field( row, 7 );
1432 feature->Attributes = MSI_RecordGetInteger(row,8);
1434 feature->Installed = INSTALLSTATE_UNKNOWN;
1435 msi_feature_set_state(package, feature, INSTALLSTATE_UNKNOWN);
1437 list_add_tail( &package->features, &feature->entry );
1439 /* load feature components */
1441 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1442 if (rc != ERROR_SUCCESS)
1443 return ERROR_SUCCESS;
1445 ilfs.package = package;
1446 ilfs.feature = feature;
1448 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1449 msiobj_release(&view->hdr);
1451 return ERROR_SUCCESS;
1454 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1456 MSIPACKAGE* package = param;
1457 MSIFEATURE *parent, *child;
1459 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1460 if (!child)
1461 return ERROR_FUNCTION_FAILED;
1463 if (!child->Feature_Parent)
1464 return ERROR_SUCCESS;
1466 parent = find_feature_by_name( package, child->Feature_Parent );
1467 if (!parent)
1468 return ERROR_FUNCTION_FAILED;
1470 add_feature_child( parent, child );
1471 return ERROR_SUCCESS;
1474 static UINT load_all_features( MSIPACKAGE *package )
1476 static const WCHAR query[] = {
1477 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1478 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1479 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1480 MSIQUERY *view;
1481 UINT r;
1483 if (!list_empty(&package->features))
1484 return ERROR_SUCCESS;
1486 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1487 if (r != ERROR_SUCCESS)
1488 return r;
1490 r = MSI_IterateRecords( view, NULL, load_feature, package );
1491 if (r != ERROR_SUCCESS)
1492 return r;
1494 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1495 msiobj_release( &view->hdr );
1497 return r;
1500 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1502 if (!p)
1503 return p;
1504 p = strchrW(p, ch);
1505 if (!p)
1506 return p;
1507 *p = 0;
1508 return p+1;
1511 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1513 static const WCHAR query[] = {
1514 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1515 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1516 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1517 MSIQUERY *view = NULL;
1518 MSIRECORD *row = NULL;
1519 UINT r;
1521 TRACE("%s\n", debugstr_w(file->File));
1523 r = MSI_OpenQuery(package->db, &view, query, file->File);
1524 if (r != ERROR_SUCCESS)
1525 goto done;
1527 r = MSI_ViewExecute(view, NULL);
1528 if (r != ERROR_SUCCESS)
1529 goto done;
1531 r = MSI_ViewFetch(view, &row);
1532 if (r != ERROR_SUCCESS)
1533 goto done;
1535 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1536 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1537 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1538 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1539 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1541 done:
1542 if (view) msiobj_release(&view->hdr);
1543 if (row) msiobj_release(&row->hdr);
1544 return r;
1547 static UINT load_file(MSIRECORD *row, LPVOID param)
1549 MSIPACKAGE* package = param;
1550 LPCWSTR component;
1551 MSIFILE *file;
1553 /* fill in the data */
1555 file = msi_alloc_zero( sizeof (MSIFILE) );
1556 if (!file)
1557 return ERROR_NOT_ENOUGH_MEMORY;
1559 file->File = msi_dup_record_field( row, 1 );
1561 component = MSI_RecordGetString( row, 2 );
1562 file->Component = get_loaded_component( package, component );
1564 if (!file->Component)
1566 WARN("Component not found: %s\n", debugstr_w(component));
1567 msi_free(file->File);
1568 msi_free(file);
1569 return ERROR_SUCCESS;
1572 file->FileName = msi_dup_record_field( row, 3 );
1573 reduce_to_longfilename( file->FileName );
1575 file->ShortName = msi_dup_record_field( row, 3 );
1576 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1578 file->FileSize = MSI_RecordGetInteger( row, 4 );
1579 file->Version = msi_dup_record_field( row, 5 );
1580 file->Language = msi_dup_record_field( row, 6 );
1581 file->Attributes = MSI_RecordGetInteger( row, 7 );
1582 file->Sequence = MSI_RecordGetInteger( row, 8 );
1584 file->state = msifs_invalid;
1586 /* if the compressed bits are not set in the file attributes,
1587 * then read the information from the package word count property
1589 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1591 file->IsCompressed = FALSE;
1593 else if (file->Attributes &
1594 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1596 file->IsCompressed = TRUE;
1598 else if (file->Attributes & msidbFileAttributesNoncompressed)
1600 file->IsCompressed = FALSE;
1602 else
1604 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1607 load_file_hash(package, file);
1609 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1611 list_add_tail( &package->files, &file->entry );
1613 return ERROR_SUCCESS;
1616 static UINT load_all_files(MSIPACKAGE *package)
1618 MSIQUERY * view;
1619 UINT rc;
1620 static const WCHAR Query[] =
1621 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1622 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1623 '`','S','e','q','u','e','n','c','e','`', 0};
1625 if (!list_empty(&package->files))
1626 return ERROR_SUCCESS;
1628 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1629 if (rc != ERROR_SUCCESS)
1630 return ERROR_SUCCESS;
1632 rc = MSI_IterateRecords(view, NULL, load_file, package);
1633 msiobj_release(&view->hdr);
1635 return ERROR_SUCCESS;
1638 static UINT load_folder( MSIRECORD *row, LPVOID param )
1640 MSIPACKAGE *package = param;
1641 static const WCHAR szDot[] = { '.',0 };
1642 static WCHAR szEmpty[] = { 0 };
1643 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1644 MSIFOLDER *folder;
1646 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1647 if (!folder)
1648 return ERROR_NOT_ENOUGH_MEMORY;
1650 folder->Directory = msi_dup_record_field( row, 1 );
1652 TRACE("%s\n", debugstr_w(folder->Directory));
1654 p = msi_dup_record_field(row, 3);
1656 /* split src and target dir */
1657 tgt_short = p;
1658 src_short = folder_split_path( p, ':' );
1660 /* split the long and short paths */
1661 tgt_long = folder_split_path( tgt_short, '|' );
1662 src_long = folder_split_path( src_short, '|' );
1664 /* check for no-op dirs */
1665 if (!lstrcmpW(szDot, tgt_short))
1666 tgt_short = szEmpty;
1667 if (!lstrcmpW(szDot, src_short))
1668 src_short = szEmpty;
1670 if (!tgt_long)
1671 tgt_long = tgt_short;
1673 if (!src_short) {
1674 src_short = tgt_short;
1675 src_long = tgt_long;
1678 if (!src_long)
1679 src_long = src_short;
1681 /* FIXME: use the target short path too */
1682 folder->TargetDefault = strdupW(tgt_long);
1683 folder->SourceShortPath = strdupW(src_short);
1684 folder->SourceLongPath = strdupW(src_long);
1685 msi_free(p);
1687 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1688 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1689 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1691 folder->Parent = msi_dup_record_field( row, 2 );
1693 folder->Property = msi_dup_property( package, folder->Directory );
1695 list_add_tail( &package->folders, &folder->entry );
1697 TRACE("returning %p\n", folder);
1699 return ERROR_SUCCESS;
1702 static UINT load_all_folders( MSIPACKAGE *package )
1704 static const WCHAR query[] = {
1705 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1706 '`','D','i','r','e','c','t','o','r','y','`',0 };
1707 MSIQUERY *view;
1708 UINT r;
1710 if (!list_empty(&package->folders))
1711 return ERROR_SUCCESS;
1713 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1714 if (r != ERROR_SUCCESS)
1715 return r;
1717 r = MSI_IterateRecords(view, NULL, load_folder, package);
1718 msiobj_release(&view->hdr);
1719 return r;
1723 * I am not doing any of the costing functionality yet.
1724 * Mostly looking at doing the Component and Feature loading
1726 * The native MSI does A LOT of modification to tables here. Mostly adding
1727 * a lot of temporary columns to the Feature and Component tables.
1729 * note: Native msi also tracks the short filename. But I am only going to
1730 * track the long ones. Also looking at this directory table
1731 * it appears that the directory table does not get the parents
1732 * resolved base on property only based on their entries in the
1733 * directory table.
1735 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1737 static const WCHAR szCosting[] =
1738 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1739 static const WCHAR szZero[] = { '0', 0 };
1741 MSI_SetPropertyW(package, szCosting, szZero);
1742 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1744 load_all_folders( package );
1745 load_all_components( package );
1746 load_all_features( package );
1747 load_all_files( package );
1749 return ERROR_SUCCESS;
1752 static UINT execute_script(MSIPACKAGE *package, UINT script )
1754 UINT i;
1755 UINT rc = ERROR_SUCCESS;
1757 TRACE("Executing Script %i\n",script);
1759 if (!package->script)
1761 ERR("no script!\n");
1762 return ERROR_FUNCTION_FAILED;
1765 for (i = 0; i < package->script->ActionCount[script]; i++)
1767 LPWSTR action;
1768 action = package->script->Actions[script][i];
1769 ui_actionstart(package, action);
1770 TRACE("Executing Action (%s)\n",debugstr_w(action));
1771 rc = ACTION_PerformAction(package, action, script, TRUE);
1772 if (rc != ERROR_SUCCESS)
1773 break;
1775 msi_free_action_script(package, script);
1776 return rc;
1779 static UINT ACTION_FileCost(MSIPACKAGE *package)
1781 return ERROR_SUCCESS;
1784 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1786 MSICOMPONENT *comp;
1787 INSTALLSTATE state;
1788 UINT r;
1790 state = MsiQueryProductStateW(package->ProductCode);
1792 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1794 if (!comp->ComponentId)
1795 continue;
1797 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1798 comp->Installed = INSTALLSTATE_ABSENT;
1799 else
1801 r = MsiQueryComponentStateW(package->ProductCode, NULL,
1802 package->Context, comp->ComponentId,
1803 &comp->Installed);
1804 if (r != ERROR_SUCCESS)
1805 comp->Installed = INSTALLSTATE_ABSENT;
1810 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1812 MSIFEATURE *feature;
1813 INSTALLSTATE state;
1815 state = MsiQueryProductStateW(package->ProductCode);
1817 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1819 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1820 feature->Installed = INSTALLSTATE_ABSENT;
1821 else
1823 feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1824 feature->Feature);
1829 static BOOL process_state_property(MSIPACKAGE* package, int level,
1830 LPCWSTR property, INSTALLSTATE state)
1832 static const WCHAR all[]={'A','L','L',0};
1833 static const WCHAR remove[] = {'R','E','M','O','V','E',0};
1834 static const WCHAR reinstall[] = {'R','E','I','N','S','T','A','L','L',0};
1835 LPWSTR override;
1836 MSIFEATURE *feature;
1838 override = msi_dup_property( package, property );
1839 if (!override)
1840 return FALSE;
1842 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1844 if (lstrcmpW(property, remove) &&
1845 (feature->Level <= 0 || feature->Level > level))
1846 continue;
1848 if (!strcmpW(property, reinstall)) state = feature->Installed;
1850 if (strcmpiW(override,all)==0)
1851 msi_feature_set_state(package, feature, state);
1852 else
1854 LPWSTR ptr = override;
1855 LPWSTR ptr2 = strchrW(override,',');
1857 while (ptr)
1859 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1860 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1862 msi_feature_set_state(package, feature, state);
1863 break;
1865 if (ptr2)
1867 ptr=ptr2+1;
1868 ptr2 = strchrW(ptr,',');
1870 else
1871 break;
1875 msi_free(override);
1877 return TRUE;
1880 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1882 int level;
1883 static const WCHAR szlevel[] =
1884 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1885 static const WCHAR szAddLocal[] =
1886 {'A','D','D','L','O','C','A','L',0};
1887 static const WCHAR szAddSource[] =
1888 {'A','D','D','S','O','U','R','C','E',0};
1889 static const WCHAR szRemove[] =
1890 {'R','E','M','O','V','E',0};
1891 static const WCHAR szReinstall[] =
1892 {'R','E','I','N','S','T','A','L','L',0};
1893 static const WCHAR szAdvertise[] =
1894 {'A','D','V','E','R','T','I','S','E',0};
1895 BOOL override = FALSE;
1896 MSICOMPONENT* component;
1897 MSIFEATURE *feature;
1900 /* I do not know if this is where it should happen.. but */
1902 TRACE("Checking Install Level\n");
1904 level = msi_get_property_int(package, szlevel, 1);
1906 /* ok here is the _real_ rub
1907 * all these activation/deactivation things happen in order and things
1908 * later on the list override things earlier on the list.
1909 * 0) INSTALLLEVEL processing
1910 * 1) ADDLOCAL
1911 * 2) REMOVE
1912 * 3) ADDSOURCE
1913 * 4) ADDDEFAULT
1914 * 5) REINSTALL
1915 * 6) ADVERTISE
1916 * 7) COMPADDLOCAL
1917 * 8) COMPADDSOURCE
1918 * 9) FILEADDLOCAL
1919 * 10) FILEADDSOURCE
1920 * 11) FILEADDDEFAULT
1922 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1923 * REMOVE are the big ones, since we don't handle administrative installs
1924 * yet anyway.
1926 override |= process_state_property(package, level, szAddLocal, INSTALLSTATE_LOCAL);
1927 override |= process_state_property(package, level, szRemove, INSTALLSTATE_ABSENT);
1928 override |= process_state_property(package, level, szAddSource, INSTALLSTATE_SOURCE);
1929 override |= process_state_property(package, level, szReinstall, INSTALLSTATE_UNKNOWN);
1930 override |= process_state_property(package, level, szAdvertise, INSTALLSTATE_ADVERTISED);
1932 if (!override)
1934 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1936 BOOL feature_state = ((feature->Level > 0) &&
1937 (feature->Level <= level));
1939 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1941 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1942 msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1943 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1944 msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1945 else
1946 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1950 /* disable child features of unselected parent features */
1951 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1953 FeatureList *fl;
1955 if (feature->Level > 0 && feature->Level <= level)
1956 continue;
1958 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1959 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1962 else
1964 /* set the Preselected Property */
1965 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1966 static const WCHAR szOne[] = { '1', 0 };
1968 MSI_SetPropertyW(package,szPreselected,szOne);
1972 * now we want to enable or disable components base on feature
1975 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1977 ComponentList *cl;
1979 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1980 debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1982 if (!feature->Level)
1983 continue;
1985 /* features with components that have compressed files are made local */
1986 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1988 if (cl->component->Enabled &&
1989 cl->component->ForceLocalState &&
1990 feature->Action == INSTALLSTATE_SOURCE)
1992 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1993 break;
1997 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1999 component = cl->component;
2001 if (!component->Enabled)
2002 continue;
2004 switch (feature->Action)
2006 case INSTALLSTATE_ABSENT:
2007 component->anyAbsent = 1;
2008 break;
2009 case INSTALLSTATE_ADVERTISED:
2010 component->hasAdvertiseFeature = 1;
2011 break;
2012 case INSTALLSTATE_SOURCE:
2013 component->hasSourceFeature = 1;
2014 break;
2015 case INSTALLSTATE_LOCAL:
2016 component->hasLocalFeature = 1;
2017 break;
2018 case INSTALLSTATE_DEFAULT:
2019 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
2020 component->hasAdvertiseFeature = 1;
2021 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
2022 component->hasSourceFeature = 1;
2023 else
2024 component->hasLocalFeature = 1;
2025 break;
2026 default:
2027 break;
2032 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
2034 /* if the component isn't enabled, leave it alone */
2035 if (!component->Enabled)
2036 continue;
2038 /* check if it's local or source */
2039 if (!(component->Attributes & msidbComponentAttributesOptional) &&
2040 (component->hasLocalFeature || component->hasSourceFeature))
2042 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
2043 !component->ForceLocalState)
2044 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
2045 else
2046 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
2047 continue;
2050 /* if any feature is local, the component must be local too */
2051 if (component->hasLocalFeature)
2053 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
2054 continue;
2057 if (component->hasSourceFeature)
2059 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
2060 continue;
2063 if (component->hasAdvertiseFeature)
2065 msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
2066 continue;
2069 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
2070 if (component->anyAbsent)
2071 msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
2074 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
2076 if (component->Action == INSTALLSTATE_DEFAULT)
2078 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
2079 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
2082 TRACE("Result: Component %s (Installed %i, Action %i)\n",
2083 debugstr_w(component->Component), component->Installed, component->Action);
2087 return ERROR_SUCCESS;
2090 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
2092 MSIPACKAGE *package = param;
2093 LPCWSTR name;
2094 LPWSTR path;
2095 MSIFOLDER *f;
2097 name = MSI_RecordGetString(row,1);
2099 f = get_loaded_folder(package, name);
2100 if (!f) return ERROR_SUCCESS;
2102 /* reset the ResolvedTarget */
2103 msi_free(f->ResolvedTarget);
2104 f->ResolvedTarget = NULL;
2106 /* This helper function now does ALL the work */
2107 TRACE("Dir %s ...\n",debugstr_w(name));
2108 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
2109 TRACE("resolves to %s\n",debugstr_w(path));
2110 msi_free(path);
2112 return ERROR_SUCCESS;
2115 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2117 MSIPACKAGE *package = param;
2118 LPCWSTR name;
2119 MSIFEATURE *feature;
2121 name = MSI_RecordGetString( row, 1 );
2123 feature = get_loaded_feature( package, name );
2124 if (!feature)
2125 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2126 else
2128 LPCWSTR Condition;
2129 Condition = MSI_RecordGetString(row,3);
2131 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2133 int level = MSI_RecordGetInteger(row,2);
2134 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2135 feature->Level = level;
2138 return ERROR_SUCCESS;
2141 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
2143 static const WCHAR name_fmt[] =
2144 {'%','u','.','%','u','.','%','u','.','%','u',0};
2145 static const WCHAR name[] = {'\\',0};
2146 VS_FIXEDFILEINFO *lpVer;
2147 WCHAR filever[0x100];
2148 LPVOID version;
2149 DWORD versize;
2150 DWORD handle;
2151 UINT sz;
2153 TRACE("%s\n", debugstr_w(filename));
2155 versize = GetFileVersionInfoSizeW( filename, &handle );
2156 if (!versize)
2157 return NULL;
2159 version = msi_alloc( versize );
2160 GetFileVersionInfoW( filename, 0, versize, version );
2162 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2164 msi_free( version );
2165 return NULL;
2168 sprintfW( filever, name_fmt,
2169 HIWORD(lpVer->dwFileVersionMS),
2170 LOWORD(lpVer->dwFileVersionMS),
2171 HIWORD(lpVer->dwFileVersionLS),
2172 LOWORD(lpVer->dwFileVersionLS));
2174 msi_free( version );
2176 return strdupW( filever );
2179 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2181 LPWSTR file_version;
2182 MSIFILE *file;
2184 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2186 MSICOMPONENT* comp = file->Component;
2187 LPWSTR p;
2189 if (!comp)
2190 continue;
2192 if (file->IsCompressed)
2193 comp->ForceLocalState = TRUE;
2195 /* calculate target */
2196 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2198 msi_free(file->TargetPath);
2200 TRACE("file %s is named %s\n",
2201 debugstr_w(file->File), debugstr_w(file->FileName));
2203 file->TargetPath = build_directory_name(2, p, file->FileName);
2205 msi_free(p);
2207 TRACE("file %s resolves to %s\n",
2208 debugstr_w(file->File), debugstr_w(file->TargetPath));
2210 /* don't check files of components that aren't installed */
2211 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2212 comp->Installed == INSTALLSTATE_ABSENT)
2214 file->state = msifs_missing; /* assume files are missing */
2215 continue;
2218 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2220 file->state = msifs_missing;
2221 comp->Cost += file->FileSize;
2222 continue;
2225 if (file->Version &&
2226 (file_version = msi_get_disk_file_version( file->TargetPath )))
2228 TRACE("new %s old %s\n", debugstr_w(file->Version),
2229 debugstr_w(file_version));
2230 /* FIXME: seems like a bad way to compare version numbers */
2231 if (lstrcmpiW(file_version, file->Version)<0)
2233 file->state = msifs_overwrite;
2234 comp->Cost += file->FileSize;
2236 else
2237 file->state = msifs_present;
2238 msi_free( file_version );
2240 else
2241 file->state = msifs_present;
2244 return ERROR_SUCCESS;
2248 * A lot is done in this function aside from just the costing.
2249 * The costing needs to be implemented at some point but for now I am going
2250 * to focus on the directory building
2253 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2255 static const WCHAR ExecSeqQuery[] =
2256 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2257 '`','D','i','r','e','c','t','o','r','y','`',0};
2258 static const WCHAR ConditionQuery[] =
2259 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2260 '`','C','o','n','d','i','t','i','o','n','`',0};
2261 static const WCHAR szCosting[] =
2262 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2263 static const WCHAR szlevel[] =
2264 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2265 static const WCHAR szOutOfDiskSpace[] =
2266 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2267 static const WCHAR szOne[] = { '1', 0 };
2268 static const WCHAR szZero[] = { '0', 0 };
2269 MSICOMPONENT *comp;
2270 UINT rc;
2271 MSIQUERY * view;
2272 LPWSTR level;
2274 TRACE("Building Directory properties\n");
2276 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2277 if (rc == ERROR_SUCCESS)
2279 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2280 package);
2281 msiobj_release(&view->hdr);
2284 /* read components states from the registry */
2285 ACTION_GetComponentInstallStates(package);
2286 ACTION_GetFeatureInstallStates(package);
2288 TRACE("File calculations\n");
2289 msi_check_file_install_states( package );
2291 TRACE("Evaluating Condition Table\n");
2293 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2294 if (rc == ERROR_SUCCESS)
2296 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2297 package);
2298 msiobj_release(&view->hdr);
2301 TRACE("Enabling or Disabling Components\n");
2302 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2304 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2306 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2307 comp->Enabled = FALSE;
2309 else
2310 comp->Enabled = TRUE;
2313 MSI_SetPropertyW(package,szCosting,szOne);
2314 /* set default run level if not set */
2315 level = msi_dup_property( package, szlevel );
2316 if (!level)
2317 MSI_SetPropertyW(package,szlevel, szOne);
2318 msi_free(level);
2320 /* FIXME: check volume disk space */
2321 MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
2323 return MSI_SetFeatureStates(package);
2326 /* OK this value is "interpreted" and then formatted based on the
2327 first few characters */
2328 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2329 DWORD *size)
2331 LPSTR data = NULL;
2333 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2335 if (value[1]=='x')
2337 LPWSTR ptr;
2338 CHAR byte[5];
2339 LPWSTR deformated = NULL;
2340 int count;
2342 deformat_string(package, &value[2], &deformated);
2344 /* binary value type */
2345 ptr = deformated;
2346 *type = REG_BINARY;
2347 if (strlenW(ptr)%2)
2348 *size = (strlenW(ptr)/2)+1;
2349 else
2350 *size = strlenW(ptr)/2;
2352 data = msi_alloc(*size);
2354 byte[0] = '0';
2355 byte[1] = 'x';
2356 byte[4] = 0;
2357 count = 0;
2358 /* if uneven pad with a zero in front */
2359 if (strlenW(ptr)%2)
2361 byte[2]= '0';
2362 byte[3]= *ptr;
2363 ptr++;
2364 data[count] = (BYTE)strtol(byte,NULL,0);
2365 count ++;
2366 TRACE("Uneven byte count\n");
2368 while (*ptr)
2370 byte[2]= *ptr;
2371 ptr++;
2372 byte[3]= *ptr;
2373 ptr++;
2374 data[count] = (BYTE)strtol(byte,NULL,0);
2375 count ++;
2377 msi_free(deformated);
2379 TRACE("Data %i bytes(%i)\n",*size,count);
2381 else
2383 LPWSTR deformated;
2384 LPWSTR p;
2385 DWORD d = 0;
2386 deformat_string(package, &value[1], &deformated);
2388 *type=REG_DWORD;
2389 *size = sizeof(DWORD);
2390 data = msi_alloc(*size);
2391 p = deformated;
2392 if (*p == '-')
2393 p++;
2394 while (*p)
2396 if ( (*p < '0') || (*p > '9') )
2397 break;
2398 d *= 10;
2399 d += (*p - '0');
2400 p++;
2402 if (deformated[0] == '-')
2403 d = -d;
2404 *(LPDWORD)data = d;
2405 TRACE("DWORD %i\n",*(LPDWORD)data);
2407 msi_free(deformated);
2410 else
2412 static const WCHAR szMulti[] = {'[','~',']',0};
2413 LPCWSTR ptr;
2414 *type=REG_SZ;
2416 if (value[0]=='#')
2418 if (value[1]=='%')
2420 ptr = &value[2];
2421 *type=REG_EXPAND_SZ;
2423 else
2424 ptr = &value[1];
2426 else
2427 ptr=value;
2429 if (strstrW(value,szMulti))
2430 *type = REG_MULTI_SZ;
2432 /* remove initial delimiter */
2433 if (!strncmpW(value, szMulti, 3))
2434 ptr = value + 3;
2436 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2438 /* add double NULL terminator */
2439 if (*type == REG_MULTI_SZ)
2441 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2442 data = msi_realloc_zero(data, *size);
2445 return data;
2448 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2450 MSIPACKAGE *package = param;
2451 static const WCHAR szHCR[] =
2452 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2453 'R','O','O','T','\\',0};
2454 static const WCHAR szHCU[] =
2455 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2456 'U','S','E','R','\\',0};
2457 static const WCHAR szHLM[] =
2458 {'H','K','E','Y','_','L','O','C','A','L','_',
2459 'M','A','C','H','I','N','E','\\',0};
2460 static const WCHAR szHU[] =
2461 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2463 LPSTR value_data = NULL;
2464 HKEY root_key, hkey;
2465 DWORD type,size;
2466 LPWSTR deformated;
2467 LPCWSTR szRoot, component, name, key, value;
2468 MSICOMPONENT *comp;
2469 MSIRECORD * uirow;
2470 LPWSTR uikey;
2471 INT root;
2472 BOOL check_first = FALSE;
2473 UINT rc;
2475 ui_progress(package,2,0,0,0);
2477 value = NULL;
2478 key = NULL;
2479 uikey = NULL;
2480 name = NULL;
2482 component = MSI_RecordGetString(row, 6);
2483 comp = get_loaded_component(package,component);
2484 if (!comp)
2485 return ERROR_SUCCESS;
2487 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2489 TRACE("Skipping write due to disabled component %s\n",
2490 debugstr_w(component));
2492 comp->Action = comp->Installed;
2494 return ERROR_SUCCESS;
2497 comp->Action = INSTALLSTATE_LOCAL;
2499 name = MSI_RecordGetString(row, 4);
2500 if( MSI_RecordIsNull(row,5) && name )
2502 /* null values can have special meanings */
2503 if (name[0]=='-' && name[1] == 0)
2504 return ERROR_SUCCESS;
2505 else if ((name[0]=='+' && name[1] == 0) ||
2506 (name[0] == '*' && name[1] == 0))
2507 name = NULL;
2508 check_first = TRUE;
2511 root = MSI_RecordGetInteger(row,2);
2512 key = MSI_RecordGetString(row, 3);
2514 /* get the root key */
2515 switch (root)
2517 case -1:
2519 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2520 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2521 if (all_users && all_users[0] == '1')
2523 root_key = HKEY_LOCAL_MACHINE;
2524 szRoot = szHLM;
2526 else
2528 root_key = HKEY_CURRENT_USER;
2529 szRoot = szHCU;
2531 msi_free(all_users);
2533 break;
2534 case 0: root_key = HKEY_CLASSES_ROOT;
2535 szRoot = szHCR;
2536 break;
2537 case 1: root_key = HKEY_CURRENT_USER;
2538 szRoot = szHCU;
2539 break;
2540 case 2: root_key = HKEY_LOCAL_MACHINE;
2541 szRoot = szHLM;
2542 break;
2543 case 3: root_key = HKEY_USERS;
2544 szRoot = szHU;
2545 break;
2546 default:
2547 ERR("Unknown root %i\n",root);
2548 root_key=NULL;
2549 szRoot = NULL;
2550 break;
2552 if (!root_key)
2553 return ERROR_SUCCESS;
2555 deformat_string(package, key , &deformated);
2556 size = strlenW(deformated) + strlenW(szRoot) + 1;
2557 uikey = msi_alloc(size*sizeof(WCHAR));
2558 strcpyW(uikey,szRoot);
2559 strcatW(uikey,deformated);
2561 if (RegCreateKeyW( root_key, deformated, &hkey))
2563 ERR("Could not create key %s\n",debugstr_w(deformated));
2564 msi_free(deformated);
2565 msi_free(uikey);
2566 return ERROR_SUCCESS;
2568 msi_free(deformated);
2570 value = MSI_RecordGetString(row,5);
2571 if (value)
2572 value_data = parse_value(package, value, &type, &size);
2573 else
2575 static const WCHAR szEmpty[] = {0};
2576 value_data = (LPSTR)strdupW(szEmpty);
2577 size = sizeof(szEmpty);
2578 type = REG_SZ;
2581 deformat_string(package, name, &deformated);
2583 if (!check_first)
2585 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2586 debugstr_w(uikey));
2587 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2589 else
2591 DWORD sz = 0;
2592 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2593 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2595 TRACE("value %s of %s checked already exists\n",
2596 debugstr_w(deformated), debugstr_w(uikey));
2598 else
2600 TRACE("Checked and setting value %s of %s\n",
2601 debugstr_w(deformated), debugstr_w(uikey));
2602 if (deformated || size)
2603 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2606 RegCloseKey(hkey);
2608 uirow = MSI_CreateRecord(3);
2609 MSI_RecordSetStringW(uirow,2,deformated);
2610 MSI_RecordSetStringW(uirow,1,uikey);
2612 if (type == REG_SZ)
2613 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2614 else
2615 MSI_RecordSetStringW(uirow,3,value);
2617 ui_actiondata(package,szWriteRegistryValues,uirow);
2618 msiobj_release( &uirow->hdr );
2620 msi_free(value_data);
2621 msi_free(deformated);
2622 msi_free(uikey);
2624 return ERROR_SUCCESS;
2627 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2629 UINT rc;
2630 MSIQUERY * view;
2631 static const WCHAR ExecSeqQuery[] =
2632 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2633 '`','R','e','g','i','s','t','r','y','`',0 };
2635 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2636 if (rc != ERROR_SUCCESS)
2637 return ERROR_SUCCESS;
2639 /* increment progress bar each time action data is sent */
2640 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2642 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2644 msiobj_release(&view->hdr);
2645 return rc;
2648 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2650 package->script->CurrentlyScripting = TRUE;
2652 return ERROR_SUCCESS;
2656 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2658 MSICOMPONENT *comp;
2659 DWORD progress = 0;
2660 DWORD total = 0;
2661 static const WCHAR q1[]=
2662 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2663 '`','R','e','g','i','s','t','r','y','`',0};
2664 UINT rc;
2665 MSIQUERY * view;
2666 MSIFEATURE *feature;
2667 MSIFILE *file;
2669 TRACE("InstallValidate\n");
2671 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2672 if (rc == ERROR_SUCCESS)
2674 MSI_IterateRecords( view, &progress, NULL, package );
2675 msiobj_release( &view->hdr );
2676 total += progress * REG_PROGRESS_VALUE;
2679 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2680 total += COMPONENT_PROGRESS_VALUE;
2682 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2683 total += file->FileSize;
2685 ui_progress(package,0,total,0,0);
2687 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2689 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2690 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2691 feature->ActionRequest);
2694 return ERROR_SUCCESS;
2697 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2699 MSIPACKAGE* package = param;
2700 LPCWSTR cond = NULL;
2701 LPCWSTR message = NULL;
2702 UINT r;
2704 static const WCHAR title[]=
2705 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2707 cond = MSI_RecordGetString(row,1);
2709 r = MSI_EvaluateConditionW(package,cond);
2710 if (r == MSICONDITION_FALSE)
2712 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2714 LPWSTR deformated;
2715 message = MSI_RecordGetString(row,2);
2716 deformat_string(package,message,&deformated);
2717 MessageBoxW(NULL,deformated,title,MB_OK);
2718 msi_free(deformated);
2721 return ERROR_INSTALL_FAILURE;
2724 return ERROR_SUCCESS;
2727 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2729 UINT rc;
2730 MSIQUERY * view = NULL;
2731 static const WCHAR ExecSeqQuery[] =
2732 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2733 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2735 TRACE("Checking launch conditions\n");
2737 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2738 if (rc != ERROR_SUCCESS)
2739 return ERROR_SUCCESS;
2741 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2742 msiobj_release(&view->hdr);
2744 return rc;
2747 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2750 if (!cmp->KeyPath)
2751 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2753 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2755 MSIRECORD * row = 0;
2756 UINT root,len;
2757 LPWSTR deformated,buffer,deformated_name;
2758 LPCWSTR key,name;
2759 static const WCHAR ExecSeqQuery[] =
2760 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2761 '`','R','e','g','i','s','t','r','y','`',' ',
2762 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2763 ' ','=',' ' ,'\'','%','s','\'',0 };
2764 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2765 static const WCHAR fmt2[]=
2766 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2768 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2769 if (!row)
2770 return NULL;
2772 root = MSI_RecordGetInteger(row,2);
2773 key = MSI_RecordGetString(row, 3);
2774 name = MSI_RecordGetString(row, 4);
2775 deformat_string(package, key , &deformated);
2776 deformat_string(package, name, &deformated_name);
2778 len = strlenW(deformated) + 6;
2779 if (deformated_name)
2780 len+=strlenW(deformated_name);
2782 buffer = msi_alloc( len *sizeof(WCHAR));
2784 if (deformated_name)
2785 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2786 else
2787 sprintfW(buffer,fmt,root,deformated);
2789 msi_free(deformated);
2790 msi_free(deformated_name);
2791 msiobj_release(&row->hdr);
2793 return buffer;
2795 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2797 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2798 return NULL;
2800 else
2802 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2804 if (file)
2805 return strdupW( file->TargetPath );
2807 return NULL;
2810 static HKEY openSharedDLLsKey(void)
2812 HKEY hkey=0;
2813 static const WCHAR path[] =
2814 {'S','o','f','t','w','a','r','e','\\',
2815 'M','i','c','r','o','s','o','f','t','\\',
2816 'W','i','n','d','o','w','s','\\',
2817 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2818 'S','h','a','r','e','d','D','L','L','s',0};
2820 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2821 return hkey;
2824 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2826 HKEY hkey;
2827 DWORD count=0;
2828 DWORD type;
2829 DWORD sz = sizeof(count);
2830 DWORD rc;
2832 hkey = openSharedDLLsKey();
2833 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2834 if (rc != ERROR_SUCCESS)
2835 count = 0;
2836 RegCloseKey(hkey);
2837 return count;
2840 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2842 HKEY hkey;
2844 hkey = openSharedDLLsKey();
2845 if (count > 0)
2846 msi_reg_set_val_dword( hkey, path, count );
2847 else
2848 RegDeleteValueW(hkey,path);
2849 RegCloseKey(hkey);
2850 return count;
2854 * Return TRUE if the count should be written out and FALSE if not
2856 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2858 MSIFEATURE *feature;
2859 INT count = 0;
2860 BOOL write = FALSE;
2862 /* only refcount DLLs */
2863 if (comp->KeyPath == NULL ||
2864 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2865 comp->Attributes & msidbComponentAttributesODBCDataSource)
2866 write = FALSE;
2867 else
2869 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2870 write = (count > 0);
2872 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2873 write = TRUE;
2876 /* increment counts */
2877 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2879 ComponentList *cl;
2881 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2882 continue;
2884 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2886 if ( cl->component == comp )
2887 count++;
2891 /* decrement counts */
2892 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2894 ComponentList *cl;
2896 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2897 continue;
2899 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2901 if ( cl->component == comp )
2902 count--;
2906 /* ref count all the files in the component */
2907 if (write)
2909 MSIFILE *file;
2911 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2913 if (file->Component == comp)
2914 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2918 /* add a count for permanent */
2919 if (comp->Attributes & msidbComponentAttributesPermanent)
2920 count ++;
2922 comp->RefCount = count;
2924 if (write)
2925 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2928 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2930 WCHAR squished_pc[GUID_SIZE];
2931 WCHAR squished_cc[GUID_SIZE];
2932 UINT rc;
2933 MSICOMPONENT *comp;
2934 HKEY hkey;
2936 TRACE("\n");
2938 squash_guid(package->ProductCode,squished_pc);
2939 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2941 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2943 MSIRECORD * uirow;
2945 ui_progress(package,2,0,0,0);
2946 if (!comp->ComponentId)
2947 continue;
2949 squash_guid(comp->ComponentId,squished_cc);
2951 msi_free(comp->FullKeypath);
2952 comp->FullKeypath = resolve_keypath( package, comp );
2954 ACTION_RefCountComponent( package, comp );
2956 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2957 debugstr_w(comp->Component),
2958 debugstr_w(squished_cc),
2959 debugstr_w(comp->FullKeypath),
2960 comp->RefCount);
2962 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL) ||
2963 ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE))
2965 if (!comp->FullKeypath)
2966 continue;
2968 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2969 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, szLocalSid,
2970 &hkey, TRUE);
2971 else
2972 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, NULL,
2973 &hkey, TRUE);
2975 if (rc != ERROR_SUCCESS)
2976 continue;
2978 if (comp->Attributes & msidbComponentAttributesPermanent)
2980 static const WCHAR szPermKey[] =
2981 { '0','0','0','0','0','0','0','0','0','0','0','0',
2982 '0','0','0','0','0','0','0','0','0','0','0','0',
2983 '0','0','0','0','0','0','0','0',0 };
2985 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
2988 if (comp->Action == INSTALLSTATE_LOCAL)
2989 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
2990 else
2992 MSIFILE *file;
2993 MSIRECORD *row;
2994 LPWSTR ptr, ptr2;
2995 WCHAR source[MAX_PATH];
2996 WCHAR base[MAX_PATH];
2997 LPWSTR sourcepath;
2999 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
3000 static const WCHAR query[] = {
3001 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
3002 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
3003 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
3004 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
3005 '`','D','i','s','k','I','d','`',0};
3007 file = get_loaded_file(package, comp->KeyPath);
3008 if (!file)
3009 continue;
3011 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
3012 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
3013 ptr2 = strrchrW(source, '\\') + 1;
3014 msiobj_release(&row->hdr);
3016 lstrcpyW(base, package->PackagePath);
3017 ptr = strrchrW(base, '\\');
3018 *(ptr + 1) = '\0';
3020 sourcepath = resolve_file_source(package, file);
3021 ptr = sourcepath + lstrlenW(base);
3022 lstrcpyW(ptr2, ptr);
3023 msi_free(sourcepath);
3025 msi_reg_set_val_str(hkey, squished_pc, source);
3027 RegCloseKey(hkey);
3029 else if (ACTION_VerifyComponentForAction(comp, INSTALLSTATE_ABSENT))
3031 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3032 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, szLocalSid);
3033 else
3034 MSIREG_DeleteUserDataComponentKey(comp->ComponentId, NULL);
3037 /* UI stuff */
3038 uirow = MSI_CreateRecord(3);
3039 MSI_RecordSetStringW(uirow,1,package->ProductCode);
3040 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
3041 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
3042 ui_actiondata(package,szProcessComponents,uirow);
3043 msiobj_release( &uirow->hdr );
3046 return ERROR_SUCCESS;
3049 typedef struct {
3050 CLSID clsid;
3051 LPWSTR source;
3053 LPWSTR path;
3054 ITypeLib *ptLib;
3055 } typelib_struct;
3057 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
3058 LPWSTR lpszName, LONG_PTR lParam)
3060 TLIBATTR *attr;
3061 typelib_struct *tl_struct = (typelib_struct*) lParam;
3062 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
3063 int sz;
3064 HRESULT res;
3066 if (!IS_INTRESOURCE(lpszName))
3068 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
3069 return TRUE;
3072 sz = strlenW(tl_struct->source)+4;
3073 sz *= sizeof(WCHAR);
3075 if ((INT_PTR)lpszName == 1)
3076 tl_struct->path = strdupW(tl_struct->source);
3077 else
3079 tl_struct->path = msi_alloc(sz);
3080 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
3083 TRACE("trying %s\n", debugstr_w(tl_struct->path));
3084 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
3085 if (FAILED(res))
3087 msi_free(tl_struct->path);
3088 tl_struct->path = NULL;
3090 return TRUE;
3093 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
3094 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
3096 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3097 return FALSE;
3100 msi_free(tl_struct->path);
3101 tl_struct->path = NULL;
3103 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
3104 ITypeLib_Release(tl_struct->ptLib);
3106 return TRUE;
3109 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
3111 MSIPACKAGE* package = param;
3112 LPCWSTR component;
3113 MSICOMPONENT *comp;
3114 MSIFILE *file;
3115 typelib_struct tl_struct;
3116 ITypeLib *tlib;
3117 HMODULE module;
3118 HRESULT hr;
3120 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
3122 component = MSI_RecordGetString(row,3);
3123 comp = get_loaded_component(package,component);
3124 if (!comp)
3125 return ERROR_SUCCESS;
3127 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3129 TRACE("Skipping typelib reg due to disabled component\n");
3131 comp->Action = comp->Installed;
3133 return ERROR_SUCCESS;
3136 comp->Action = INSTALLSTATE_LOCAL;
3138 file = get_loaded_file( package, comp->KeyPath );
3139 if (!file)
3140 return ERROR_SUCCESS;
3142 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3143 if (module)
3145 LPCWSTR guid;
3146 guid = MSI_RecordGetString(row,1);
3147 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3148 tl_struct.source = strdupW( file->TargetPath );
3149 tl_struct.path = NULL;
3151 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3152 (LONG_PTR)&tl_struct);
3154 if (tl_struct.path)
3156 LPWSTR help = NULL;
3157 LPCWSTR helpid;
3158 HRESULT res;
3160 helpid = MSI_RecordGetString(row,6);
3162 if (helpid)
3163 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3164 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3165 msi_free(help);
3167 if (FAILED(res))
3168 ERR("Failed to register type library %s\n",
3169 debugstr_w(tl_struct.path));
3170 else
3172 ui_actiondata(package,szRegisterTypeLibraries,row);
3174 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3177 ITypeLib_Release(tl_struct.ptLib);
3178 msi_free(tl_struct.path);
3180 else
3181 ERR("Failed to load type library %s\n",
3182 debugstr_w(tl_struct.source));
3184 FreeLibrary(module);
3185 msi_free(tl_struct.source);
3187 else
3189 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3190 if (FAILED(hr))
3192 ERR("Failed to load type library: %08x\n", hr);
3193 return ERROR_FUNCTION_FAILED;
3196 ITypeLib_Release(tlib);
3199 return ERROR_SUCCESS;
3202 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3205 * OK this is a bit confusing.. I am given a _Component key and I believe
3206 * that the file that is being registered as a type library is the "key file
3207 * of that component" which I interpret to mean "The file in the KeyPath of
3208 * that component".
3210 UINT rc;
3211 MSIQUERY * view;
3212 static const WCHAR Query[] =
3213 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3214 '`','T','y','p','e','L','i','b','`',0};
3216 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3217 if (rc != ERROR_SUCCESS)
3218 return ERROR_SUCCESS;
3220 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3221 msiobj_release(&view->hdr);
3222 return rc;
3225 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3227 MSIPACKAGE *package = param;
3228 LPWSTR target_file, target_folder, filename;
3229 LPCWSTR buffer, extension;
3230 MSICOMPONENT *comp;
3231 static const WCHAR szlnk[]={'.','l','n','k',0};
3232 IShellLinkW *sl = NULL;
3233 IPersistFile *pf = NULL;
3234 HRESULT res;
3236 buffer = MSI_RecordGetString(row,4);
3237 comp = get_loaded_component(package,buffer);
3238 if (!comp)
3239 return ERROR_SUCCESS;
3241 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3243 TRACE("Skipping shortcut creation due to disabled component\n");
3245 comp->Action = comp->Installed;
3247 return ERROR_SUCCESS;
3250 comp->Action = INSTALLSTATE_LOCAL;
3252 ui_actiondata(package,szCreateShortcuts,row);
3254 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3255 &IID_IShellLinkW, (LPVOID *) &sl );
3257 if (FAILED( res ))
3259 ERR("CLSID_ShellLink not available\n");
3260 goto err;
3263 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3264 if (FAILED( res ))
3266 ERR("QueryInterface(IID_IPersistFile) failed\n");
3267 goto err;
3270 buffer = MSI_RecordGetString(row,2);
3271 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3273 /* may be needed because of a bug somewhere else */
3274 create_full_pathW(target_folder);
3276 filename = msi_dup_record_field( row, 3 );
3277 reduce_to_longfilename(filename);
3279 extension = strchrW(filename,'.');
3280 if (!extension || strcmpiW(extension,szlnk))
3282 int len = strlenW(filename);
3283 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3284 memcpy(filename + len, szlnk, sizeof(szlnk));
3286 target_file = build_directory_name(2, target_folder, filename);
3287 msi_free(target_folder);
3288 msi_free(filename);
3290 buffer = MSI_RecordGetString(row,5);
3291 if (strchrW(buffer,'['))
3293 LPWSTR deformated;
3294 deformat_string(package,buffer,&deformated);
3295 IShellLinkW_SetPath(sl,deformated);
3296 msi_free(deformated);
3298 else
3300 FIXME("poorly handled shortcut format, advertised shortcut\n");
3301 IShellLinkW_SetPath(sl,comp->FullKeypath);
3304 if (!MSI_RecordIsNull(row,6))
3306 LPWSTR deformated;
3307 buffer = MSI_RecordGetString(row,6);
3308 deformat_string(package,buffer,&deformated);
3309 IShellLinkW_SetArguments(sl,deformated);
3310 msi_free(deformated);
3313 if (!MSI_RecordIsNull(row,7))
3315 buffer = MSI_RecordGetString(row,7);
3316 IShellLinkW_SetDescription(sl,buffer);
3319 if (!MSI_RecordIsNull(row,8))
3320 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3322 if (!MSI_RecordIsNull(row,9))
3324 LPWSTR Path;
3325 INT index;
3327 buffer = MSI_RecordGetString(row,9);
3329 Path = build_icon_path(package,buffer);
3330 index = MSI_RecordGetInteger(row,10);
3332 /* no value means 0 */
3333 if (index == MSI_NULL_INTEGER)
3334 index = 0;
3336 IShellLinkW_SetIconLocation(sl,Path,index);
3337 msi_free(Path);
3340 if (!MSI_RecordIsNull(row,11))
3341 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3343 if (!MSI_RecordIsNull(row,12))
3345 LPWSTR Path;
3346 buffer = MSI_RecordGetString(row,12);
3347 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3348 if (Path)
3349 IShellLinkW_SetWorkingDirectory(sl,Path);
3350 msi_free(Path);
3353 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3354 IPersistFile_Save(pf,target_file,FALSE);
3356 msi_free(target_file);
3358 err:
3359 if (pf)
3360 IPersistFile_Release( pf );
3361 if (sl)
3362 IShellLinkW_Release( sl );
3364 return ERROR_SUCCESS;
3367 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3369 UINT rc;
3370 HRESULT res;
3371 MSIQUERY * view;
3372 static const WCHAR Query[] =
3373 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3374 '`','S','h','o','r','t','c','u','t','`',0};
3376 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3377 if (rc != ERROR_SUCCESS)
3378 return ERROR_SUCCESS;
3380 res = CoInitialize( NULL );
3381 if (FAILED (res))
3383 ERR("CoInitialize failed\n");
3384 return ERROR_FUNCTION_FAILED;
3387 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3388 msiobj_release(&view->hdr);
3390 CoUninitialize();
3392 return rc;
3395 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3397 MSIPACKAGE* package = param;
3398 HANDLE the_file;
3399 LPWSTR FilePath;
3400 LPCWSTR FileName;
3401 CHAR buffer[1024];
3402 DWORD sz;
3403 UINT rc;
3404 MSIRECORD *uirow;
3406 FileName = MSI_RecordGetString(row,1);
3407 if (!FileName)
3409 ERR("Unable to get FileName\n");
3410 return ERROR_SUCCESS;
3413 FilePath = build_icon_path(package,FileName);
3415 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3417 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3418 FILE_ATTRIBUTE_NORMAL, NULL);
3420 if (the_file == INVALID_HANDLE_VALUE)
3422 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3423 msi_free(FilePath);
3424 return ERROR_SUCCESS;
3429 DWORD write;
3430 sz = 1024;
3431 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3432 if (rc != ERROR_SUCCESS)
3434 ERR("Failed to get stream\n");
3435 CloseHandle(the_file);
3436 DeleteFileW(FilePath);
3437 break;
3439 WriteFile(the_file,buffer,sz,&write,NULL);
3440 } while (sz == 1024);
3442 msi_free(FilePath);
3444 CloseHandle(the_file);
3446 uirow = MSI_CreateRecord(1);
3447 MSI_RecordSetStringW(uirow,1,FileName);
3448 ui_actiondata(package,szPublishProduct,uirow);
3449 msiobj_release( &uirow->hdr );
3451 return ERROR_SUCCESS;
3454 static UINT msi_publish_icons(MSIPACKAGE *package)
3456 UINT r;
3457 MSIQUERY *view;
3459 static const WCHAR query[]= {
3460 'S','E','L','E','C','T',' ','*',' ',
3461 'F','R','O','M',' ','`','I','c','o','n','`',0};
3463 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3464 if (r == ERROR_SUCCESS)
3466 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3467 msiobj_release(&view->hdr);
3470 return ERROR_SUCCESS;
3473 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3475 UINT r;
3476 HKEY source;
3477 LPWSTR buffer;
3478 MSIMEDIADISK *disk;
3479 MSISOURCELISTINFO *info;
3481 static const WCHAR szEmpty[] = {0};
3482 static const WCHAR szSourceList[] = {'S','o','u','r','c','e','L','i','s','t',0};
3484 r = RegCreateKeyW(hkey, szSourceList, &source);
3485 if (r != ERROR_SUCCESS)
3486 return r;
3488 RegCloseKey(source);
3490 buffer = strrchrW(package->PackagePath, '\\') + 1;
3491 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3492 package->Context, MSICODE_PRODUCT,
3493 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3494 if (r != ERROR_SUCCESS)
3495 return r;
3497 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3498 package->Context, MSICODE_PRODUCT,
3499 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3500 if (r != ERROR_SUCCESS)
3501 return r;
3503 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3504 package->Context, MSICODE_PRODUCT,
3505 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3506 if (r != ERROR_SUCCESS)
3507 return r;
3509 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3511 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3512 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3513 info->options, info->value);
3514 else
3515 MsiSourceListSetInfoW(package->ProductCode, NULL,
3516 info->context, info->options,
3517 info->property, info->value);
3520 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3522 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3523 disk->context, disk->options,
3524 disk->disk_id, disk->volume_label, disk->disk_prompt);
3527 return ERROR_SUCCESS;
3530 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3532 MSIHANDLE hdb, suminfo;
3533 WCHAR guids[MAX_PATH];
3534 WCHAR packcode[SQUISH_GUID_SIZE];
3535 LPWSTR buffer;
3536 LPWSTR ptr;
3537 DWORD langid;
3538 DWORD size;
3539 UINT r;
3541 static const WCHAR szProductLanguage[] =
3542 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3543 static const WCHAR szARPProductIcon[] =
3544 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3545 static const WCHAR szProductVersion[] =
3546 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3547 static const WCHAR szAssignment[] =
3548 {'A','s','s','i','g','n','m','e','n','t',0};
3549 static const WCHAR szAdvertiseFlags[] =
3550 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3551 static const WCHAR szClients[] =
3552 {'C','l','i','e','n','t','s',0};
3553 static const WCHAR szColon[] = {':',0};
3555 buffer = msi_dup_property(package, INSTALLPROPERTY_PRODUCTNAMEW);
3556 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3557 msi_free(buffer);
3559 langid = msi_get_property_int(package, szProductLanguage, 0);
3560 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3562 /* FIXME */
3563 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3565 buffer = msi_dup_property(package, szARPProductIcon);
3566 if (buffer)
3568 LPWSTR path = build_icon_path(package,buffer);
3569 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3570 msi_free(path);
3571 msi_free(buffer);
3574 buffer = msi_dup_property(package, szProductVersion);
3575 if (buffer)
3577 DWORD verdword = msi_version_str_to_dword(buffer);
3578 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3579 msi_free(buffer);
3582 msi_reg_set_val_dword(hkey, szAssignment, 0);
3583 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3584 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3585 msi_reg_set_val_str(hkey, szClients, szColon);
3587 hdb = alloc_msihandle(&package->db->hdr);
3588 if (!hdb)
3589 return ERROR_NOT_ENOUGH_MEMORY;
3591 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3592 MsiCloseHandle(hdb);
3593 if (r != ERROR_SUCCESS)
3594 goto done;
3596 size = MAX_PATH;
3597 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3598 NULL, guids, &size);
3599 if (r != ERROR_SUCCESS)
3600 goto done;
3602 ptr = strchrW(guids, ';');
3603 if (ptr) *ptr = 0;
3604 squash_guid(guids, packcode);
3605 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3607 done:
3608 MsiCloseHandle(suminfo);
3609 return ERROR_SUCCESS;
3612 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3614 UINT r;
3615 HKEY hkey;
3616 LPWSTR upgrade;
3617 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3619 static const WCHAR szUpgradeCode[] =
3620 {'U','p','g','r','a','d','e','C','o','d','e',0};
3622 upgrade = msi_dup_property(package, szUpgradeCode);
3623 if (!upgrade)
3624 return ERROR_SUCCESS;
3626 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3628 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3629 if (r != ERROR_SUCCESS)
3630 goto done;
3632 else
3634 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3635 if (r != ERROR_SUCCESS)
3636 goto done;
3639 squash_guid(package->ProductCode, squashed_pc);
3640 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3642 RegCloseKey(hkey);
3644 done:
3645 msi_free(upgrade);
3646 return r;
3649 static BOOL msi_check_publish(MSIPACKAGE *package)
3651 MSIFEATURE *feature;
3653 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3655 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3656 return TRUE;
3659 return FALSE;
3662 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3664 MSIFEATURE *feature;
3666 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3668 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3669 return FALSE;
3672 return TRUE;
3675 static UINT msi_publish_patch(MSIPACKAGE *package, HKEY prodkey, HKEY hudkey)
3677 WCHAR patch_squashed[GUID_SIZE];
3678 HKEY patches;
3679 LONG res;
3680 UINT r = ERROR_FUNCTION_FAILED;
3682 static const WCHAR szPatches[] = {'P','a','t','c','h','e','s',0};
3684 res = RegCreateKeyExW(prodkey, szPatches, 0, NULL, 0, KEY_ALL_ACCESS, NULL,
3685 &patches, NULL);
3686 if (res != ERROR_SUCCESS)
3687 return ERROR_FUNCTION_FAILED;
3689 squash_guid(package->patch->patchcode, patch_squashed);
3691 res = RegSetValueExW(patches, szPatches, 0, REG_MULTI_SZ,
3692 (const BYTE *)patch_squashed,
3693 (lstrlenW(patch_squashed) + 1) * sizeof(WCHAR));
3694 if (res != ERROR_SUCCESS)
3695 goto done;
3697 res = RegSetValueExW(patches, patch_squashed, 0, REG_SZ,
3698 (const BYTE *)package->patch->transforms,
3699 (lstrlenW(package->patch->transforms) + 1) * sizeof(WCHAR));
3700 if (res == ERROR_SUCCESS)
3701 r = ERROR_SUCCESS;
3703 done:
3704 RegCloseKey(patches);
3705 return r;
3709 * 99% of the work done here is only done for
3710 * advertised installs. However this is where the
3711 * Icon table is processed and written out
3712 * so that is what I am going to do here.
3714 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3716 UINT rc;
3717 HKEY hukey=0;
3718 HKEY hudkey=0;
3720 /* FIXME: also need to publish if the product is in advertise mode */
3721 if (!msi_check_publish(package))
3722 return ERROR_SUCCESS;
3724 rc = MSIREG_OpenProductKey(package->ProductCode, NULL, package->Context,
3725 &hukey, TRUE);
3726 if (rc != ERROR_SUCCESS)
3727 goto end;
3729 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, package->Context,
3730 NULL, &hudkey, TRUE);
3731 if (rc != ERROR_SUCCESS)
3732 goto end;
3734 rc = msi_publish_upgrade_code(package);
3735 if (rc != ERROR_SUCCESS)
3736 goto end;
3738 if (package->patch)
3740 rc = msi_publish_patch(package, hukey, hudkey);
3741 if (rc != ERROR_SUCCESS)
3742 goto end;
3745 rc = msi_publish_product_properties(package, hukey);
3746 if (rc != ERROR_SUCCESS)
3747 goto end;
3749 rc = msi_publish_sourcelist(package, hukey);
3750 if (rc != ERROR_SUCCESS)
3751 goto end;
3753 rc = msi_publish_icons(package);
3755 end:
3756 RegCloseKey(hukey);
3757 RegCloseKey(hudkey);
3759 return rc;
3762 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3764 MSIPACKAGE *package = param;
3765 LPCWSTR component, section, key, value, identifier, dirproperty;
3766 LPWSTR deformated_section, deformated_key, deformated_value;
3767 LPWSTR folder, filename, fullname = NULL;
3768 LPCWSTR filenameptr;
3769 MSIRECORD * uirow;
3770 INT action;
3771 MSICOMPONENT *comp;
3772 static const WCHAR szWindowsFolder[] =
3773 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3775 component = MSI_RecordGetString(row, 8);
3776 comp = get_loaded_component(package,component);
3778 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3780 TRACE("Skipping ini file due to disabled component %s\n",
3781 debugstr_w(component));
3783 comp->Action = comp->Installed;
3785 return ERROR_SUCCESS;
3788 comp->Action = INSTALLSTATE_LOCAL;
3790 identifier = MSI_RecordGetString(row,1);
3791 dirproperty = MSI_RecordGetString(row,3);
3792 section = MSI_RecordGetString(row,4);
3793 key = MSI_RecordGetString(row,5);
3794 value = MSI_RecordGetString(row,6);
3795 action = MSI_RecordGetInteger(row,7);
3797 deformat_string(package,section,&deformated_section);
3798 deformat_string(package,key,&deformated_key);
3799 deformat_string(package,value,&deformated_value);
3801 filename = msi_dup_record_field(row, 2);
3802 if (filename && (filenameptr = strchrW(filename, '|')))
3803 filenameptr++;
3804 else
3805 filenameptr = filename;
3807 if (dirproperty)
3809 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3810 if (!folder)
3811 folder = msi_dup_property( package, dirproperty );
3813 else
3814 folder = msi_dup_property( package, szWindowsFolder );
3816 if (!folder)
3818 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3819 goto cleanup;
3822 fullname = build_directory_name(2, folder, filenameptr);
3824 if (action == 0)
3826 TRACE("Adding value %s to section %s in %s\n",
3827 debugstr_w(deformated_key), debugstr_w(deformated_section),
3828 debugstr_w(fullname));
3829 WritePrivateProfileStringW(deformated_section, deformated_key,
3830 deformated_value, fullname);
3832 else if (action == 1)
3834 WCHAR returned[10];
3835 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3836 returned, 10, fullname);
3837 if (returned[0] == 0)
3839 TRACE("Adding value %s to section %s in %s\n",
3840 debugstr_w(deformated_key), debugstr_w(deformated_section),
3841 debugstr_w(fullname));
3843 WritePrivateProfileStringW(deformated_section, deformated_key,
3844 deformated_value, fullname);
3847 else if (action == 3)
3848 FIXME("Append to existing section not yet implemented\n");
3850 uirow = MSI_CreateRecord(4);
3851 MSI_RecordSetStringW(uirow,1,identifier);
3852 MSI_RecordSetStringW(uirow,2,deformated_section);
3853 MSI_RecordSetStringW(uirow,3,deformated_key);
3854 MSI_RecordSetStringW(uirow,4,deformated_value);
3855 ui_actiondata(package,szWriteIniValues,uirow);
3856 msiobj_release( &uirow->hdr );
3858 cleanup:
3859 msi_free(filename);
3860 msi_free(fullname);
3861 msi_free(folder);
3862 msi_free(deformated_key);
3863 msi_free(deformated_value);
3864 msi_free(deformated_section);
3865 return ERROR_SUCCESS;
3868 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3870 UINT rc;
3871 MSIQUERY * view;
3872 static const WCHAR ExecSeqQuery[] =
3873 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3874 '`','I','n','i','F','i','l','e','`',0};
3876 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3877 if (rc != ERROR_SUCCESS)
3879 TRACE("no IniFile table\n");
3880 return ERROR_SUCCESS;
3883 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3884 msiobj_release(&view->hdr);
3885 return rc;
3888 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3890 MSIPACKAGE *package = param;
3891 LPCWSTR filename;
3892 LPWSTR FullName;
3893 MSIFILE *file;
3894 DWORD len;
3895 static const WCHAR ExeStr[] =
3896 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3897 static const WCHAR close[] = {'\"',0};
3898 STARTUPINFOW si;
3899 PROCESS_INFORMATION info;
3900 BOOL brc;
3901 MSIRECORD *uirow;
3902 LPWSTR uipath, p;
3904 memset(&si,0,sizeof(STARTUPINFOW));
3906 filename = MSI_RecordGetString(row,1);
3907 file = get_loaded_file( package, filename );
3909 if (!file)
3911 ERR("Unable to find file id %s\n",debugstr_w(filename));
3912 return ERROR_SUCCESS;
3915 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3917 FullName = msi_alloc(len*sizeof(WCHAR));
3918 strcpyW(FullName,ExeStr);
3919 strcatW( FullName, file->TargetPath );
3920 strcatW(FullName,close);
3922 TRACE("Registering %s\n",debugstr_w(FullName));
3923 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3924 &si, &info);
3926 if (brc)
3928 CloseHandle(info.hThread);
3929 msi_dialog_check_messages(info.hProcess);
3930 CloseHandle(info.hProcess);
3933 msi_free(FullName);
3935 /* the UI chunk */
3936 uirow = MSI_CreateRecord( 2 );
3937 uipath = strdupW( file->TargetPath );
3938 p = strrchrW(uipath,'\\');
3939 if (p)
3940 p[0]=0;
3941 MSI_RecordSetStringW( uirow, 1, &p[1] );
3942 MSI_RecordSetStringW( uirow, 2, uipath);
3943 ui_actiondata( package, szSelfRegModules, uirow);
3944 msiobj_release( &uirow->hdr );
3945 msi_free( uipath );
3946 /* FIXME: call ui_progress? */
3948 return ERROR_SUCCESS;
3951 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3953 UINT rc;
3954 MSIQUERY * view;
3955 static const WCHAR ExecSeqQuery[] =
3956 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3957 '`','S','e','l','f','R','e','g','`',0};
3959 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3960 if (rc != ERROR_SUCCESS)
3962 TRACE("no SelfReg table\n");
3963 return ERROR_SUCCESS;
3966 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3967 msiobj_release(&view->hdr);
3969 return ERROR_SUCCESS;
3972 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3974 MSIFEATURE *feature;
3975 UINT rc;
3976 HKEY hkey;
3977 HKEY userdata = NULL;
3979 if (!msi_check_publish(package))
3980 return ERROR_SUCCESS;
3982 rc = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
3983 &hkey, TRUE);
3984 if (rc != ERROR_SUCCESS)
3985 goto end;
3987 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
3988 &userdata, TRUE);
3989 if (rc != ERROR_SUCCESS)
3990 goto end;
3992 /* here the guids are base 85 encoded */
3993 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3995 ComponentList *cl;
3996 LPWSTR data = NULL;
3997 GUID clsid;
3998 INT size;
3999 BOOL absent = FALSE;
4000 MSIRECORD *uirow;
4002 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
4003 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
4004 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
4005 absent = TRUE;
4007 size = 1;
4008 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4010 size += 21;
4012 if (feature->Feature_Parent)
4013 size += strlenW( feature->Feature_Parent )+2;
4015 data = msi_alloc(size * sizeof(WCHAR));
4017 data[0] = 0;
4018 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
4020 MSICOMPONENT* component = cl->component;
4021 WCHAR buf[21];
4023 buf[0] = 0;
4024 if (component->ComponentId)
4026 TRACE("From %s\n",debugstr_w(component->ComponentId));
4027 CLSIDFromString(component->ComponentId, &clsid);
4028 encode_base85_guid(&clsid,buf);
4029 TRACE("to %s\n",debugstr_w(buf));
4030 strcatW(data,buf);
4034 if (feature->Feature_Parent)
4036 static const WCHAR sep[] = {'\2',0};
4037 strcatW(data,sep);
4038 strcatW(data,feature->Feature_Parent);
4041 msi_reg_set_val_str( userdata, feature->Feature, data );
4042 msi_free(data);
4044 size = 0;
4045 if (feature->Feature_Parent)
4046 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
4047 if (!absent)
4049 static const WCHAR emptyW[] = {0};
4050 size += sizeof(WCHAR);
4051 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4052 (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : emptyW),size);
4054 else
4056 size += 2*sizeof(WCHAR);
4057 data = msi_alloc(size);
4058 data[0] = 0x6;
4059 data[1] = 0;
4060 if (feature->Feature_Parent)
4061 strcpyW( &data[1], feature->Feature_Parent );
4062 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
4063 (LPBYTE)data,size);
4064 msi_free(data);
4067 /* the UI chunk */
4068 uirow = MSI_CreateRecord( 1 );
4069 MSI_RecordSetStringW( uirow, 1, feature->Feature );
4070 ui_actiondata( package, szPublishFeatures, uirow);
4071 msiobj_release( &uirow->hdr );
4072 /* FIXME: call ui_progress? */
4075 end:
4076 RegCloseKey(hkey);
4077 RegCloseKey(userdata);
4078 return rc;
4081 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
4083 UINT r;
4084 HKEY hkey;
4086 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
4088 r = MSIREG_OpenFeaturesKey(package->ProductCode, package->Context,
4089 &hkey, FALSE);
4090 if (r == ERROR_SUCCESS)
4092 RegDeleteValueW(hkey, feature->Feature);
4093 RegCloseKey(hkey);
4096 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, package->Context,
4097 &hkey, FALSE);
4098 if (r == ERROR_SUCCESS)
4100 RegDeleteValueW(hkey, feature->Feature);
4101 RegCloseKey(hkey);
4104 return ERROR_SUCCESS;
4107 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
4109 MSIFEATURE *feature;
4111 if (!msi_check_unpublish(package))
4112 return ERROR_SUCCESS;
4114 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4116 msi_unpublish_feature(package, feature);
4119 return ERROR_SUCCESS;
4122 static UINT msi_get_local_package_name( LPWSTR path )
4124 static const WCHAR szInstaller[] = {
4125 '\\','I','n','s','t','a','l','l','e','r','\\',0};
4126 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
4127 DWORD time, len, i;
4128 HANDLE handle;
4130 time = GetTickCount();
4131 GetWindowsDirectoryW( path, MAX_PATH );
4132 lstrcatW( path, szInstaller );
4133 CreateDirectoryW( path, NULL );
4135 len = lstrlenW(path);
4136 for (i=0; i<0x10000; i++)
4138 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
4139 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
4140 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
4141 if (handle != INVALID_HANDLE_VALUE)
4143 CloseHandle(handle);
4144 break;
4146 if (GetLastError() != ERROR_FILE_EXISTS &&
4147 GetLastError() != ERROR_SHARING_VIOLATION)
4148 return ERROR_FUNCTION_FAILED;
4151 return ERROR_SUCCESS;
4154 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
4156 WCHAR packagefile[MAX_PATH];
4157 UINT r;
4159 r = msi_get_local_package_name( packagefile );
4160 if (r != ERROR_SUCCESS)
4161 return r;
4163 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
4165 r = CopyFileW( package->db->path, packagefile, FALSE);
4167 if (!r)
4169 ERR("Unable to copy package (%s -> %s) (error %d)\n",
4170 debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
4171 return ERROR_FUNCTION_FAILED;
4174 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
4176 return ERROR_SUCCESS;
4179 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4181 LPWSTR prop, val, key;
4182 SYSTEMTIME systime;
4183 DWORD size, langid;
4184 WCHAR date[9];
4185 LPWSTR buffer;
4187 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4188 static const WCHAR szWindowsInstaller[] =
4189 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4190 static const WCHAR modpath_fmt[] =
4191 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4192 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4193 static const WCHAR szModifyPath[] =
4194 {'M','o','d','i','f','y','P','a','t','h',0};
4195 static const WCHAR szUninstallString[] =
4196 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4197 static const WCHAR szEstimatedSize[] =
4198 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4199 static const WCHAR szProductLanguage[] =
4200 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4201 static const WCHAR szProductVersion[] =
4202 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4203 static const WCHAR szProductName[] =
4204 {'P','r','o','d','u','c','t','N','a','m','e',0};
4205 static const WCHAR szDisplayName[] =
4206 {'D','i','s','p','l','a','y','N','a','m','e',0};
4207 static const WCHAR szDisplayVersion[] =
4208 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4209 static const WCHAR szManufacturer[] =
4210 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4212 static const LPCSTR propval[] = {
4213 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
4214 "ARPCONTACT", "Contact",
4215 "ARPCOMMENTS", "Comments",
4216 "ProductName", "DisplayName",
4217 "ProductVersion", "DisplayVersion",
4218 "ARPHELPLINK", "HelpLink",
4219 "ARPHELPTELEPHONE", "HelpTelephone",
4220 "ARPINSTALLLOCATION", "InstallLocation",
4221 "SourceDir", "InstallSource",
4222 "Manufacturer", "Publisher",
4223 "ARPREADME", "Readme",
4224 "ARPSIZE", "Size",
4225 "ARPURLINFOABOUT", "URLInfoAbout",
4226 "ARPURLUPDATEINFO", "URLUpdateInfo",
4227 NULL,
4229 const LPCSTR *p = propval;
4231 while (*p)
4233 prop = strdupAtoW(*p++);
4234 key = strdupAtoW(*p++);
4235 val = msi_dup_property(package, prop);
4236 msi_reg_set_val_str(hkey, key, val);
4237 msi_free(val);
4238 msi_free(key);
4239 msi_free(prop);
4242 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4244 size = deformat_string(package, modpath_fmt, &buffer);
4245 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4246 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4247 msi_free(buffer);
4249 /* FIXME: Write real Estimated Size when we have it */
4250 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4252 buffer = msi_dup_property(package, szProductName);
4253 msi_reg_set_val_str(hkey, szDisplayName, buffer);
4254 msi_free(buffer);
4256 buffer = msi_dup_property(package, cszSourceDir);
4257 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4258 msi_free(buffer);
4260 buffer = msi_dup_property(package, szManufacturer);
4261 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
4262 msi_free(buffer);
4264 GetLocalTime(&systime);
4265 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4266 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4268 langid = msi_get_property_int(package, szProductLanguage, 0);
4269 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4271 buffer = msi_dup_property(package, szProductVersion);
4272 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4273 if (buffer)
4275 DWORD verdword = msi_version_str_to_dword(buffer);
4277 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4278 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4279 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4280 msi_free(buffer);
4283 return ERROR_SUCCESS;
4286 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4288 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4289 LPWSTR upgrade_code;
4290 HKEY hkey, props;
4291 HKEY upgrade;
4292 UINT rc;
4294 static const WCHAR szUpgradeCode[] = {
4295 'U','p','g','r','a','d','e','C','o','d','e',0};
4297 /* FIXME: also need to publish if the product is in advertise mode */
4298 if (!msi_check_publish(package))
4299 return ERROR_SUCCESS;
4301 rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4302 if (rc != ERROR_SUCCESS)
4303 return rc;
4305 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4306 NULL, &props, TRUE);
4307 if (rc != ERROR_SUCCESS)
4308 goto done;
4310 msi_make_package_local(package, props);
4312 rc = msi_publish_install_properties(package, hkey);
4313 if (rc != ERROR_SUCCESS)
4314 goto done;
4316 rc = msi_publish_install_properties(package, props);
4317 if (rc != ERROR_SUCCESS)
4318 goto done;
4320 upgrade_code = msi_dup_property(package, szUpgradeCode);
4321 if (upgrade_code)
4323 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4324 squash_guid(package->ProductCode, squashed_pc);
4325 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4326 RegCloseKey(upgrade);
4327 msi_free(upgrade_code);
4330 done:
4331 RegCloseKey(hkey);
4333 return ERROR_SUCCESS;
4336 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4338 return execute_script(package,INSTALL_SCRIPT);
4341 static UINT msi_unpublish_product(MSIPACKAGE *package)
4343 LPWSTR upgrade;
4344 LPWSTR remove = NULL;
4345 LPWSTR *features = NULL;
4346 BOOL full_uninstall = TRUE;
4347 MSIFEATURE *feature;
4349 static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4350 static const WCHAR szAll[] = {'A','L','L',0};
4351 static const WCHAR szUpgradeCode[] =
4352 {'U','p','g','r','a','d','e','C','o','d','e',0};
4354 remove = msi_dup_property(package, szRemove);
4355 if (!remove)
4356 return ERROR_SUCCESS;
4358 features = msi_split_string(remove, ',');
4359 if (!features)
4361 msi_free(remove);
4362 ERR("REMOVE feature list is empty!\n");
4363 return ERROR_FUNCTION_FAILED;
4366 if (!lstrcmpW(features[0], szAll))
4367 full_uninstall = TRUE;
4368 else
4370 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4372 if (feature->Action != INSTALLSTATE_ABSENT)
4373 full_uninstall = FALSE;
4377 if (!full_uninstall)
4378 goto done;
4380 MSIREG_DeleteProductKey(package->ProductCode);
4381 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4382 MSIREG_DeleteUninstallKey(package->ProductCode);
4384 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4386 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4387 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4389 else
4391 MSIREG_DeleteUserProductKey(package->ProductCode);
4392 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4395 upgrade = msi_dup_property(package, szUpgradeCode);
4396 if (upgrade)
4398 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4399 msi_free(upgrade);
4402 done:
4403 msi_free(remove);
4404 msi_free(features);
4405 return ERROR_SUCCESS;
4408 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4410 UINT rc;
4412 rc = msi_unpublish_product(package);
4413 if (rc != ERROR_SUCCESS)
4414 return rc;
4416 /* turn off scheduling */
4417 package->script->CurrentlyScripting= FALSE;
4419 /* first do the same as an InstallExecute */
4420 rc = ACTION_InstallExecute(package);
4421 if (rc != ERROR_SUCCESS)
4422 return rc;
4424 /* then handle Commit Actions */
4425 rc = execute_script(package,COMMIT_SCRIPT);
4427 return rc;
4430 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4432 static const WCHAR RunOnce[] = {
4433 'S','o','f','t','w','a','r','e','\\',
4434 'M','i','c','r','o','s','o','f','t','\\',
4435 'W','i','n','d','o','w','s','\\',
4436 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4437 'R','u','n','O','n','c','e',0};
4438 static const WCHAR InstallRunOnce[] = {
4439 'S','o','f','t','w','a','r','e','\\',
4440 'M','i','c','r','o','s','o','f','t','\\',
4441 'W','i','n','d','o','w','s','\\',
4442 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4443 'I','n','s','t','a','l','l','e','r','\\',
4444 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4446 static const WCHAR msiexec_fmt[] = {
4447 '%','s',
4448 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4449 '\"','%','s','\"',0};
4450 static const WCHAR install_fmt[] = {
4451 '/','I',' ','\"','%','s','\"',' ',
4452 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4453 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4454 WCHAR buffer[256], sysdir[MAX_PATH];
4455 HKEY hkey;
4456 WCHAR squished_pc[100];
4458 squash_guid(package->ProductCode,squished_pc);
4460 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4461 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4462 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4463 squished_pc);
4465 msi_reg_set_val_str( hkey, squished_pc, buffer );
4466 RegCloseKey(hkey);
4468 TRACE("Reboot command %s\n",debugstr_w(buffer));
4470 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4471 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4473 msi_reg_set_val_str( hkey, squished_pc, buffer );
4474 RegCloseKey(hkey);
4476 return ERROR_INSTALL_SUSPEND;
4479 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4481 DWORD attrib;
4482 UINT rc;
4485 * We are currently doing what should be done here in the top level Install
4486 * however for Administrative and uninstalls this step will be needed
4488 if (!package->PackagePath)
4489 return ERROR_SUCCESS;
4491 msi_set_sourcedir_props(package, TRUE);
4493 attrib = GetFileAttributesW(package->db->path);
4494 if (attrib == INVALID_FILE_ATTRIBUTES)
4496 LPWSTR prompt;
4497 LPWSTR msg;
4498 DWORD size = 0;
4500 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4501 package->Context, MSICODE_PRODUCT,
4502 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4503 if (rc == ERROR_MORE_DATA)
4505 prompt = msi_alloc(size * sizeof(WCHAR));
4506 MsiSourceListGetInfoW(package->ProductCode, NULL,
4507 package->Context, MSICODE_PRODUCT,
4508 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4510 else
4511 prompt = strdupW(package->db->path);
4513 msg = generate_error_string(package,1302,1,prompt);
4514 while(attrib == INVALID_FILE_ATTRIBUTES)
4516 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4517 if (rc == IDCANCEL)
4519 rc = ERROR_INSTALL_USEREXIT;
4520 break;
4522 attrib = GetFileAttributesW(package->db->path);
4524 msi_free(prompt);
4525 rc = ERROR_SUCCESS;
4527 else
4528 return ERROR_SUCCESS;
4530 return rc;
4533 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4535 HKEY hkey=0;
4536 LPWSTR buffer;
4537 LPWSTR productid;
4538 UINT rc,i;
4540 static const WCHAR szPropKeys[][80] =
4542 {'P','r','o','d','u','c','t','I','D',0},
4543 {'U','S','E','R','N','A','M','E',0},
4544 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4545 {0},
4548 static const WCHAR szRegKeys[][80] =
4550 {'P','r','o','d','u','c','t','I','D',0},
4551 {'R','e','g','O','w','n','e','r',0},
4552 {'R','e','g','C','o','m','p','a','n','y',0},
4553 {0},
4556 if (msi_check_unpublish(package))
4558 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4559 return ERROR_SUCCESS;
4562 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4563 if (!productid)
4564 return ERROR_SUCCESS;
4566 rc = MSIREG_OpenInstallProps(package->ProductCode, package->Context,
4567 NULL, &hkey, TRUE);
4568 if (rc != ERROR_SUCCESS)
4569 goto end;
4571 for( i = 0; szPropKeys[i][0]; i++ )
4573 buffer = msi_dup_property( package, szPropKeys[i] );
4574 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4575 msi_free( buffer );
4578 end:
4579 msi_free(productid);
4580 RegCloseKey(hkey);
4582 /* FIXME: call ui_actiondata */
4584 return rc;
4588 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4590 UINT rc;
4592 package->script->InWhatSequence |= SEQUENCE_EXEC;
4593 rc = ACTION_ProcessExecSequence(package,FALSE);
4594 return rc;
4598 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4600 MSIPACKAGE *package = param;
4601 LPCWSTR compgroupid=NULL;
4602 LPCWSTR feature=NULL;
4603 LPCWSTR text = NULL;
4604 LPCWSTR qualifier = NULL;
4605 LPCWSTR component = NULL;
4606 LPWSTR advertise = NULL;
4607 LPWSTR output = NULL;
4608 HKEY hkey;
4609 UINT rc = ERROR_SUCCESS;
4610 MSICOMPONENT *comp;
4611 DWORD sz = 0;
4612 MSIRECORD *uirow;
4614 component = MSI_RecordGetString(rec,3);
4615 comp = get_loaded_component(package,component);
4617 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4618 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4619 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4621 TRACE("Skipping: Component %s not scheduled for install\n",
4622 debugstr_w(component));
4624 return ERROR_SUCCESS;
4627 compgroupid = MSI_RecordGetString(rec,1);
4628 qualifier = MSI_RecordGetString(rec,2);
4630 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4631 if (rc != ERROR_SUCCESS)
4632 goto end;
4634 text = MSI_RecordGetString(rec,4);
4635 feature = MSI_RecordGetString(rec,5);
4637 advertise = create_component_advertise_string(package, comp, feature);
4639 sz = strlenW(advertise);
4641 if (text)
4642 sz += lstrlenW(text);
4644 sz+=3;
4645 sz *= sizeof(WCHAR);
4647 output = msi_alloc_zero(sz);
4648 strcpyW(output,advertise);
4649 msi_free(advertise);
4651 if (text)
4652 strcatW(output,text);
4654 msi_reg_set_val_multi_str( hkey, qualifier, output );
4656 end:
4657 RegCloseKey(hkey);
4658 msi_free(output);
4660 /* the UI chunk */
4661 uirow = MSI_CreateRecord( 2 );
4662 MSI_RecordSetStringW( uirow, 1, compgroupid );
4663 MSI_RecordSetStringW( uirow, 2, qualifier);
4664 ui_actiondata( package, szPublishComponents, uirow);
4665 msiobj_release( &uirow->hdr );
4666 /* FIXME: call ui_progress? */
4668 return rc;
4672 * At present I am ignorning the advertised components part of this and only
4673 * focusing on the qualified component sets
4675 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4677 UINT rc;
4678 MSIQUERY * view;
4679 static const WCHAR ExecSeqQuery[] =
4680 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4681 '`','P','u','b','l','i','s','h',
4682 'C','o','m','p','o','n','e','n','t','`',0};
4684 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4685 if (rc != ERROR_SUCCESS)
4686 return ERROR_SUCCESS;
4688 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4689 msiobj_release(&view->hdr);
4691 return rc;
4694 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4696 MSIPACKAGE *package = param;
4697 MSIRECORD *row;
4698 MSIFILE *file;
4699 SC_HANDLE hscm, service = NULL;
4700 LPCWSTR comp, depends, pass;
4701 LPWSTR name = NULL, disp = NULL;
4702 LPCWSTR load_order, serv_name, key;
4703 DWORD serv_type, start_type;
4704 DWORD err_control;
4706 static const WCHAR query[] =
4707 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4708 '`','C','o','m','p','o','n','e','n','t','`',' ',
4709 'W','H','E','R','E',' ',
4710 '`','C','o','m','p','o','n','e','n','t','`',' ',
4711 '=','\'','%','s','\'',0};
4713 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4714 if (!hscm)
4716 ERR("Failed to open the SC Manager!\n");
4717 goto done;
4720 start_type = MSI_RecordGetInteger(rec, 5);
4721 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4722 goto done;
4724 depends = MSI_RecordGetString(rec, 8);
4725 if (depends && *depends)
4726 FIXME("Dependency list unhandled!\n");
4728 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4729 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4730 serv_type = MSI_RecordGetInteger(rec, 4);
4731 err_control = MSI_RecordGetInteger(rec, 6);
4732 load_order = MSI_RecordGetString(rec, 7);
4733 serv_name = MSI_RecordGetString(rec, 9);
4734 pass = MSI_RecordGetString(rec, 10);
4735 comp = MSI_RecordGetString(rec, 12);
4737 /* fetch the service path */
4738 row = MSI_QueryGetRecord(package->db, query, comp);
4739 if (!row)
4741 ERR("Control query failed!\n");
4742 goto done;
4745 key = MSI_RecordGetString(row, 6);
4747 file = get_loaded_file(package, key);
4748 msiobj_release(&row->hdr);
4749 if (!file)
4751 ERR("Failed to load the service file\n");
4752 goto done;
4755 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4756 start_type, err_control, file->TargetPath,
4757 load_order, NULL, NULL, serv_name, pass);
4758 if (!service)
4760 if (GetLastError() != ERROR_SERVICE_EXISTS)
4761 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4764 done:
4765 CloseServiceHandle(service);
4766 CloseServiceHandle(hscm);
4767 msi_free(name);
4768 msi_free(disp);
4770 return ERROR_SUCCESS;
4773 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4775 UINT rc;
4776 MSIQUERY * view;
4777 static const WCHAR ExecSeqQuery[] =
4778 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4779 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4781 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4782 if (rc != ERROR_SUCCESS)
4783 return ERROR_SUCCESS;
4785 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4786 msiobj_release(&view->hdr);
4788 return rc;
4791 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4792 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4794 LPCWSTR *vector, *temp_vector;
4795 LPWSTR p, q;
4796 DWORD sep_len;
4798 static const WCHAR separator[] = {'[','~',']',0};
4800 *numargs = 0;
4801 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4803 if (!args)
4804 return NULL;
4806 vector = msi_alloc(sizeof(LPWSTR));
4807 if (!vector)
4808 return NULL;
4810 p = args;
4813 (*numargs)++;
4814 vector[*numargs - 1] = p;
4816 if ((q = strstrW(p, separator)))
4818 *q = '\0';
4820 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4821 if (!temp_vector)
4823 msi_free(vector);
4824 return NULL;
4826 vector = temp_vector;
4828 p = q + sep_len;
4830 } while (q);
4832 return vector;
4835 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4837 MSIPACKAGE *package = param;
4838 MSICOMPONENT *comp;
4839 SC_HANDLE scm, service = NULL;
4840 LPCWSTR name, *vector = NULL;
4841 LPWSTR args;
4842 DWORD event, numargs;
4843 UINT r = ERROR_FUNCTION_FAILED;
4845 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4846 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4847 return ERROR_SUCCESS;
4849 name = MSI_RecordGetString(rec, 2);
4850 event = MSI_RecordGetInteger(rec, 3);
4851 args = strdupW(MSI_RecordGetString(rec, 4));
4853 if (!(event & msidbServiceControlEventStart))
4854 return ERROR_SUCCESS;
4856 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4857 if (!scm)
4859 ERR("Failed to open the service control manager\n");
4860 goto done;
4863 service = OpenServiceW(scm, name, SERVICE_START);
4864 if (!service)
4866 ERR("Failed to open service %s\n", debugstr_w(name));
4867 goto done;
4870 vector = msi_service_args_to_vector(args, &numargs);
4872 if (!StartServiceW(service, numargs, vector))
4874 ERR("Failed to start service %s\n", debugstr_w(name));
4875 goto done;
4878 r = ERROR_SUCCESS;
4880 done:
4881 CloseServiceHandle(service);
4882 CloseServiceHandle(scm);
4884 msi_free(args);
4885 msi_free(vector);
4886 return r;
4889 static UINT ACTION_StartServices( MSIPACKAGE *package )
4891 UINT rc;
4892 MSIQUERY *view;
4894 static const WCHAR query[] = {
4895 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4896 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4898 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4899 if (rc != ERROR_SUCCESS)
4900 return ERROR_SUCCESS;
4902 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4903 msiobj_release(&view->hdr);
4905 return rc;
4908 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4910 DWORD i, needed, count;
4911 ENUM_SERVICE_STATUSW *dependencies;
4912 SERVICE_STATUS ss;
4913 SC_HANDLE depserv;
4915 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4916 0, &needed, &count))
4917 return TRUE;
4919 if (GetLastError() != ERROR_MORE_DATA)
4920 return FALSE;
4922 dependencies = msi_alloc(needed);
4923 if (!dependencies)
4924 return FALSE;
4926 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4927 needed, &needed, &count))
4928 goto error;
4930 for (i = 0; i < count; i++)
4932 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4933 SERVICE_STOP | SERVICE_QUERY_STATUS);
4934 if (!depserv)
4935 goto error;
4937 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4938 goto error;
4941 return TRUE;
4943 error:
4944 msi_free(dependencies);
4945 return FALSE;
4948 static UINT ITERATE_StopService(MSIRECORD *rec, LPVOID param)
4950 MSIPACKAGE *package = param;
4951 MSICOMPONENT *comp;
4952 SERVICE_STATUS status;
4953 SERVICE_STATUS_PROCESS ssp;
4954 SC_HANDLE scm = NULL, service = NULL;
4955 LPWSTR name, args;
4956 DWORD event, needed;
4958 event = MSI_RecordGetInteger(rec, 3);
4959 if (!(event & msidbServiceControlEventStop))
4960 return ERROR_SUCCESS;
4962 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4963 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4964 return ERROR_SUCCESS;
4966 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4967 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4968 args = strdupW(MSI_RecordGetString(rec, 4));
4970 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4971 if (!scm)
4973 WARN("Failed to open the SCM: %d\n", GetLastError());
4974 goto done;
4977 service = OpenServiceW(scm, name,
4978 SERVICE_STOP |
4979 SERVICE_QUERY_STATUS |
4980 SERVICE_ENUMERATE_DEPENDENTS);
4981 if (!service)
4983 WARN("Failed to open service (%s): %d\n",
4984 debugstr_w(name), GetLastError());
4985 goto done;
4988 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4989 sizeof(SERVICE_STATUS_PROCESS), &needed))
4991 WARN("Failed to query service status (%s): %d\n",
4992 debugstr_w(name), GetLastError());
4993 goto done;
4996 if (ssp.dwCurrentState == SERVICE_STOPPED)
4997 goto done;
4999 stop_service_dependents(scm, service);
5001 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
5002 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
5004 done:
5005 CloseServiceHandle(service);
5006 CloseServiceHandle(scm);
5007 msi_free(name);
5008 msi_free(args);
5010 return ERROR_SUCCESS;
5013 static UINT ACTION_StopServices( MSIPACKAGE *package )
5015 UINT rc;
5016 MSIQUERY *view;
5018 static const WCHAR query[] = {
5019 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5020 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
5022 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
5023 if (rc != ERROR_SUCCESS)
5024 return ERROR_SUCCESS;
5026 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
5027 msiobj_release(&view->hdr);
5029 return rc;
5032 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
5034 MSIFILE *file;
5036 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
5038 if (!lstrcmpW(file->File, filename))
5039 return file;
5042 return NULL;
5045 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
5047 MSIPACKAGE *package = param;
5048 LPWSTR driver, driver_path, ptr;
5049 WCHAR outpath[MAX_PATH];
5050 MSIFILE *driver_file, *setup_file;
5051 LPCWSTR desc;
5052 DWORD len, usage;
5053 UINT r = ERROR_SUCCESS;
5055 static const WCHAR driver_fmt[] = {
5056 'D','r','i','v','e','r','=','%','s',0};
5057 static const WCHAR setup_fmt[] = {
5058 'S','e','t','u','p','=','%','s',0};
5059 static const WCHAR usage_fmt[] = {
5060 'F','i','l','e','U','s','a','g','e','=','1',0};
5062 desc = MSI_RecordGetString(rec, 3);
5064 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5065 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5067 if (!driver_file || !setup_file)
5069 ERR("ODBC Driver entry not found!\n");
5070 return ERROR_FUNCTION_FAILED;
5073 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
5074 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
5075 lstrlenW(usage_fmt) + 1;
5076 driver = msi_alloc(len * sizeof(WCHAR));
5077 if (!driver)
5078 return ERROR_OUTOFMEMORY;
5080 ptr = driver;
5081 lstrcpyW(ptr, desc);
5082 ptr += lstrlenW(ptr) + 1;
5084 sprintfW(ptr, driver_fmt, driver_file->FileName);
5085 ptr += lstrlenW(ptr) + 1;
5087 sprintfW(ptr, setup_fmt, setup_file->FileName);
5088 ptr += lstrlenW(ptr) + 1;
5090 lstrcpyW(ptr, usage_fmt);
5091 ptr += lstrlenW(ptr) + 1;
5092 *ptr = '\0';
5094 driver_path = strdupW(driver_file->TargetPath);
5095 ptr = strrchrW(driver_path, '\\');
5096 if (ptr) *ptr = '\0';
5098 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
5099 NULL, ODBC_INSTALL_COMPLETE, &usage))
5101 ERR("Failed to install SQL driver!\n");
5102 r = ERROR_FUNCTION_FAILED;
5105 msi_free(driver);
5106 msi_free(driver_path);
5108 return r;
5111 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
5113 MSIPACKAGE *package = param;
5114 LPWSTR translator, translator_path, ptr;
5115 WCHAR outpath[MAX_PATH];
5116 MSIFILE *translator_file, *setup_file;
5117 LPCWSTR desc;
5118 DWORD len, usage;
5119 UINT r = ERROR_SUCCESS;
5121 static const WCHAR translator_fmt[] = {
5122 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
5123 static const WCHAR setup_fmt[] = {
5124 'S','e','t','u','p','=','%','s',0};
5126 desc = MSI_RecordGetString(rec, 3);
5128 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5129 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5131 if (!translator_file || !setup_file)
5133 ERR("ODBC Translator entry not found!\n");
5134 return ERROR_FUNCTION_FAILED;
5137 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
5138 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
5139 translator = msi_alloc(len * sizeof(WCHAR));
5140 if (!translator)
5141 return ERROR_OUTOFMEMORY;
5143 ptr = translator;
5144 lstrcpyW(ptr, desc);
5145 ptr += lstrlenW(ptr) + 1;
5147 sprintfW(ptr, translator_fmt, translator_file->FileName);
5148 ptr += lstrlenW(ptr) + 1;
5150 sprintfW(ptr, setup_fmt, setup_file->FileName);
5151 ptr += lstrlenW(ptr) + 1;
5152 *ptr = '\0';
5154 translator_path = strdupW(translator_file->TargetPath);
5155 ptr = strrchrW(translator_path, '\\');
5156 if (ptr) *ptr = '\0';
5158 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5159 NULL, ODBC_INSTALL_COMPLETE, &usage))
5161 ERR("Failed to install SQL translator!\n");
5162 r = ERROR_FUNCTION_FAILED;
5165 msi_free(translator);
5166 msi_free(translator_path);
5168 return r;
5171 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5173 LPWSTR attrs;
5174 LPCWSTR desc, driver;
5175 WORD request = ODBC_ADD_SYS_DSN;
5176 INT registration;
5177 DWORD len;
5178 UINT r = ERROR_SUCCESS;
5180 static const WCHAR attrs_fmt[] = {
5181 'D','S','N','=','%','s',0 };
5183 desc = MSI_RecordGetString(rec, 3);
5184 driver = MSI_RecordGetString(rec, 4);
5185 registration = MSI_RecordGetInteger(rec, 5);
5187 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5188 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5190 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
5191 attrs = msi_alloc(len * sizeof(WCHAR));
5192 if (!attrs)
5193 return ERROR_OUTOFMEMORY;
5195 sprintfW(attrs, attrs_fmt, desc);
5196 attrs[len - 1] = '\0';
5198 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5200 ERR("Failed to install SQL data source!\n");
5201 r = ERROR_FUNCTION_FAILED;
5204 msi_free(attrs);
5206 return r;
5209 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5211 UINT rc;
5212 MSIQUERY *view;
5214 static const WCHAR driver_query[] = {
5215 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5216 'O','D','B','C','D','r','i','v','e','r',0 };
5218 static const WCHAR translator_query[] = {
5219 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5220 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5222 static const WCHAR source_query[] = {
5223 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5224 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5226 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5227 if (rc != ERROR_SUCCESS)
5228 return ERROR_SUCCESS;
5230 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5231 msiobj_release(&view->hdr);
5233 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5234 if (rc != ERROR_SUCCESS)
5235 return ERROR_SUCCESS;
5237 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5238 msiobj_release(&view->hdr);
5240 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5241 if (rc != ERROR_SUCCESS)
5242 return ERROR_SUCCESS;
5244 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5245 msiobj_release(&view->hdr);
5247 return rc;
5250 #define ENV_ACT_SETALWAYS 0x1
5251 #define ENV_ACT_SETABSENT 0x2
5252 #define ENV_ACT_REMOVE 0x4
5253 #define ENV_ACT_REMOVEMATCH 0x8
5255 #define ENV_MOD_MACHINE 0x20000000
5256 #define ENV_MOD_APPEND 0x40000000
5257 #define ENV_MOD_PREFIX 0x80000000
5258 #define ENV_MOD_MASK 0xC0000000
5260 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5262 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5264 LPCWSTR cptr = *name;
5265 LPCWSTR ptr = *value;
5267 static const WCHAR prefix[] = {'[','~',']',0};
5268 static const int prefix_len = 3;
5270 *flags = 0;
5271 while (*cptr)
5273 if (*cptr == '=')
5274 *flags |= ENV_ACT_SETALWAYS;
5275 else if (*cptr == '+')
5276 *flags |= ENV_ACT_SETABSENT;
5277 else if (*cptr == '-')
5278 *flags |= ENV_ACT_REMOVE;
5279 else if (*cptr == '!')
5280 *flags |= ENV_ACT_REMOVEMATCH;
5281 else if (*cptr == '*')
5282 *flags |= ENV_MOD_MACHINE;
5283 else
5284 break;
5286 cptr++;
5287 (*name)++;
5290 if (!*cptr)
5292 ERR("Missing environment variable\n");
5293 return ERROR_FUNCTION_FAILED;
5296 if (!strncmpW(ptr, prefix, prefix_len))
5298 *flags |= ENV_MOD_APPEND;
5299 *value += lstrlenW(prefix);
5301 else if (lstrlenW(*value) >= prefix_len)
5303 ptr += lstrlenW(ptr) - prefix_len;
5304 if (!lstrcmpW(ptr, prefix))
5306 *flags |= ENV_MOD_PREFIX;
5307 /* the "[~]" will be removed by deformat_string */;
5311 if (!*flags ||
5312 check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5313 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5314 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5315 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5317 ERR("Invalid flags: %08x\n", *flags);
5318 return ERROR_FUNCTION_FAILED;
5321 return ERROR_SUCCESS;
5324 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5326 MSIPACKAGE *package = param;
5327 LPCWSTR name, value;
5328 LPWSTR data = NULL, newval = NULL;
5329 LPWSTR deformatted = NULL, ptr;
5330 DWORD flags, type, size;
5331 LONG res;
5332 HKEY env = NULL, root;
5333 LPCWSTR environment;
5335 static const WCHAR user_env[] =
5336 {'E','n','v','i','r','o','n','m','e','n','t',0};
5337 static const WCHAR machine_env[] =
5338 {'S','y','s','t','e','m','\\',
5339 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5340 'C','o','n','t','r','o','l','\\',
5341 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5342 'E','n','v','i','r','o','n','m','e','n','t',0};
5343 static const WCHAR semicolon[] = {';',0};
5345 name = MSI_RecordGetString(rec, 2);
5346 value = MSI_RecordGetString(rec, 3);
5348 res = env_set_flags(&name, &value, &flags);
5349 if (res != ERROR_SUCCESS)
5350 goto done;
5352 deformat_string(package, value, &deformatted);
5353 if (!deformatted)
5355 res = ERROR_OUTOFMEMORY;
5356 goto done;
5359 value = deformatted;
5361 if (flags & ENV_MOD_MACHINE)
5363 environment = machine_env;
5364 root = HKEY_LOCAL_MACHINE;
5366 else
5368 environment = user_env;
5369 root = HKEY_CURRENT_USER;
5372 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5373 KEY_ALL_ACCESS, NULL, &env, NULL);
5374 if (res != ERROR_SUCCESS)
5375 goto done;
5377 if (flags & ENV_ACT_REMOVE)
5378 FIXME("Not removing environment variable on uninstall!\n");
5380 size = 0;
5381 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5382 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5383 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5384 goto done;
5386 if (res != ERROR_FILE_NOT_FOUND)
5388 if (flags & ENV_ACT_SETABSENT)
5390 res = ERROR_SUCCESS;
5391 goto done;
5394 data = msi_alloc(size);
5395 if (!data)
5397 RegCloseKey(env);
5398 return ERROR_OUTOFMEMORY;
5401 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5402 if (res != ERROR_SUCCESS)
5403 goto done;
5405 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5407 res = RegDeleteKeyW(env, name);
5408 goto done;
5411 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5412 newval = msi_alloc(size);
5413 ptr = newval;
5414 if (!newval)
5416 res = ERROR_OUTOFMEMORY;
5417 goto done;
5420 if (!(flags & ENV_MOD_MASK))
5421 lstrcpyW(newval, value);
5422 else
5424 if (flags & ENV_MOD_PREFIX)
5426 lstrcpyW(newval, value);
5427 lstrcatW(newval, semicolon);
5428 ptr = newval + lstrlenW(value) + 1;
5431 lstrcpyW(ptr, data);
5433 if (flags & ENV_MOD_APPEND)
5435 lstrcatW(newval, semicolon);
5436 lstrcatW(newval, value);
5440 else
5442 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5443 newval = msi_alloc(size);
5444 if (!newval)
5446 res = ERROR_OUTOFMEMORY;
5447 goto done;
5450 lstrcpyW(newval, value);
5453 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5454 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5456 done:
5457 if (env) RegCloseKey(env);
5458 msi_free(deformatted);
5459 msi_free(data);
5460 msi_free(newval);
5461 return res;
5464 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5466 UINT rc;
5467 MSIQUERY * view;
5468 static const WCHAR ExecSeqQuery[] =
5469 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5470 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5471 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5472 if (rc != ERROR_SUCCESS)
5473 return ERROR_SUCCESS;
5475 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5476 msiobj_release(&view->hdr);
5478 return rc;
5481 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5483 typedef struct
5485 struct list entry;
5486 LPWSTR sourcename;
5487 LPWSTR destname;
5488 LPWSTR source;
5489 LPWSTR dest;
5490 } FILE_LIST;
5492 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5494 BOOL ret;
5496 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5497 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5499 WARN("Source or dest is directory, not moving\n");
5500 return FALSE;
5503 if (options == msidbMoveFileOptionsMove)
5505 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5506 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5507 if (!ret)
5509 WARN("MoveFile failed: %d\n", GetLastError());
5510 return FALSE;
5513 else
5515 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5516 ret = CopyFileW(source, dest, FALSE);
5517 if (!ret)
5519 WARN("CopyFile failed: %d\n", GetLastError());
5520 return FALSE;
5524 return TRUE;
5527 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5529 LPWSTR path, ptr;
5530 DWORD dirlen, pathlen;
5532 ptr = strrchrW(wildcard, '\\');
5533 dirlen = ptr - wildcard + 1;
5535 pathlen = dirlen + lstrlenW(filename) + 1;
5536 path = msi_alloc(pathlen * sizeof(WCHAR));
5538 lstrcpynW(path, wildcard, dirlen + 1);
5539 lstrcatW(path, filename);
5541 return path;
5544 static void free_file_entry(FILE_LIST *file)
5546 msi_free(file->source);
5547 msi_free(file->dest);
5548 msi_free(file);
5551 static void free_list(FILE_LIST *list)
5553 while (!list_empty(&list->entry))
5555 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5557 list_remove(&file->entry);
5558 free_file_entry(file);
5562 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5564 FILE_LIST *new, *file;
5565 LPWSTR ptr, filename;
5566 DWORD size;
5568 new = msi_alloc_zero(sizeof(FILE_LIST));
5569 if (!new)
5570 return FALSE;
5572 new->source = strdupW(source);
5573 ptr = strrchrW(dest, '\\') + 1;
5574 filename = strrchrW(new->source, '\\') + 1;
5576 new->sourcename = filename;
5578 if (*ptr)
5579 new->destname = ptr;
5580 else
5581 new->destname = new->sourcename;
5583 size = (ptr - dest) + lstrlenW(filename) + 1;
5584 new->dest = msi_alloc(size * sizeof(WCHAR));
5585 if (!new->dest)
5587 free_file_entry(new);
5588 return FALSE;
5591 lstrcpynW(new->dest, dest, ptr - dest + 1);
5592 lstrcatW(new->dest, filename);
5594 if (list_empty(&files->entry))
5596 list_add_head(&files->entry, &new->entry);
5597 return TRUE;
5600 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5602 if (lstrcmpW(source, file->source) < 0)
5604 list_add_before(&file->entry, &new->entry);
5605 return TRUE;
5609 list_add_after(&file->entry, &new->entry);
5610 return TRUE;
5613 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5615 WIN32_FIND_DATAW wfd;
5616 HANDLE hfile;
5617 LPWSTR path;
5618 BOOL res;
5619 FILE_LIST files, *file;
5620 DWORD size;
5622 hfile = FindFirstFileW(source, &wfd);
5623 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5625 list_init(&files.entry);
5627 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5629 if (is_dot_dir(wfd.cFileName)) continue;
5631 path = wildcard_to_file(source, wfd.cFileName);
5632 if (!path)
5634 res = FALSE;
5635 goto done;
5638 add_wildcard(&files, path, dest);
5639 msi_free(path);
5642 /* no files match the wildcard */
5643 if (list_empty(&files.entry))
5644 goto done;
5646 /* only the first wildcard match gets renamed to dest */
5647 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5648 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5649 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5650 if (!file->dest)
5652 res = FALSE;
5653 goto done;
5656 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5658 while (!list_empty(&files.entry))
5660 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5662 msi_move_file(file->source, file->dest, options);
5664 list_remove(&file->entry);
5665 free_file_entry(file);
5668 res = TRUE;
5670 done:
5671 free_list(&files);
5672 FindClose(hfile);
5673 return res;
5676 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5678 MSIPACKAGE *package = param;
5679 MSICOMPONENT *comp;
5680 LPCWSTR sourcename;
5681 LPWSTR destname = NULL;
5682 LPWSTR sourcedir = NULL, destdir = NULL;
5683 LPWSTR source = NULL, dest = NULL;
5684 int options;
5685 DWORD size;
5686 BOOL ret, wildcards;
5688 static const WCHAR backslash[] = {'\\',0};
5690 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5691 if (!comp || !comp->Enabled ||
5692 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5694 TRACE("Component not set for install, not moving file\n");
5695 return ERROR_SUCCESS;
5698 sourcename = MSI_RecordGetString(rec, 3);
5699 options = MSI_RecordGetInteger(rec, 7);
5701 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5702 if (!sourcedir)
5703 goto done;
5705 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5706 if (!destdir)
5707 goto done;
5709 if (!sourcename)
5711 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5712 goto done;
5714 source = strdupW(sourcedir);
5715 if (!source)
5716 goto done;
5718 else
5720 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5721 source = msi_alloc(size * sizeof(WCHAR));
5722 if (!source)
5723 goto done;
5725 lstrcpyW(source, sourcedir);
5726 if (source[lstrlenW(source) - 1] != '\\')
5727 lstrcatW(source, backslash);
5728 lstrcatW(source, sourcename);
5731 wildcards = strchrW(source, '*') || strchrW(source, '?');
5733 if (MSI_RecordIsNull(rec, 4))
5735 if (!wildcards)
5737 destname = strdupW(sourcename);
5738 if (!destname)
5739 goto done;
5742 else
5744 destname = strdupW(MSI_RecordGetString(rec, 4));
5745 if (destname)
5746 reduce_to_longfilename(destname);
5749 size = 0;
5750 if (destname)
5751 size = lstrlenW(destname);
5753 size += lstrlenW(destdir) + 2;
5754 dest = msi_alloc(size * sizeof(WCHAR));
5755 if (!dest)
5756 goto done;
5758 lstrcpyW(dest, destdir);
5759 if (dest[lstrlenW(dest) - 1] != '\\')
5760 lstrcatW(dest, backslash);
5762 if (destname)
5763 lstrcatW(dest, destname);
5765 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5767 ret = CreateDirectoryW(destdir, NULL);
5768 if (!ret)
5770 WARN("CreateDirectory failed: %d\n", GetLastError());
5771 return ERROR_SUCCESS;
5775 if (!wildcards)
5776 msi_move_file(source, dest, options);
5777 else
5778 move_files_wildcard(source, dest, options);
5780 done:
5781 msi_free(sourcedir);
5782 msi_free(destdir);
5783 msi_free(destname);
5784 msi_free(source);
5785 msi_free(dest);
5787 return ERROR_SUCCESS;
5790 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5792 UINT rc;
5793 MSIQUERY *view;
5795 static const WCHAR ExecSeqQuery[] =
5796 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5797 '`','M','o','v','e','F','i','l','e','`',0};
5799 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5800 if (rc != ERROR_SUCCESS)
5801 return ERROR_SUCCESS;
5803 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5804 msiobj_release(&view->hdr);
5806 return rc;
5809 typedef struct tagMSIASSEMBLY
5811 struct list entry;
5812 MSICOMPONENT *component;
5813 MSIFEATURE *feature;
5814 MSIFILE *file;
5815 LPWSTR manifest;
5816 LPWSTR application;
5817 DWORD attributes;
5818 BOOL installed;
5819 } MSIASSEMBLY;
5821 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
5822 DWORD dwReserved);
5823 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
5824 LPVOID pvReserved, HMODULE *phModDll);
5826 static BOOL init_functionpointers(void)
5828 HRESULT hr;
5829 HMODULE hfusion;
5830 HMODULE hmscoree;
5832 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
5834 hmscoree = LoadLibraryA("mscoree.dll");
5835 if (!hmscoree)
5837 WARN("mscoree.dll not available\n");
5838 return FALSE;
5841 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
5842 if (!pLoadLibraryShim)
5844 WARN("LoadLibraryShim not available\n");
5845 FreeLibrary(hmscoree);
5846 return FALSE;
5849 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
5850 if (FAILED(hr))
5852 WARN("fusion.dll not available\n");
5853 FreeLibrary(hmscoree);
5854 return FALSE;
5857 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
5859 FreeLibrary(hmscoree);
5860 return TRUE;
5863 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
5864 LPWSTR path)
5866 IAssemblyCache *cache;
5867 HRESULT hr;
5868 UINT r = ERROR_FUNCTION_FAILED;
5870 TRACE("installing assembly: %s\n", debugstr_w(path));
5872 if (assembly->feature)
5873 msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
5875 if (assembly->manifest)
5876 FIXME("Manifest unhandled\n");
5878 if (assembly->application)
5880 FIXME("Assembly should be privately installed\n");
5881 return ERROR_SUCCESS;
5884 if (assembly->attributes == msidbAssemblyAttributesWin32)
5886 FIXME("Win32 assemblies not handled\n");
5887 return ERROR_SUCCESS;
5890 hr = pCreateAssemblyCache(&cache, 0);
5891 if (FAILED(hr))
5892 goto done;
5894 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
5895 if (FAILED(hr))
5896 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
5898 r = ERROR_SUCCESS;
5900 done:
5901 IAssemblyCache_Release(cache);
5902 return r;
5905 typedef struct tagASSEMBLY_LIST
5907 MSIPACKAGE *package;
5908 IAssemblyCache *cache;
5909 struct list *assemblies;
5910 } ASSEMBLY_LIST;
5912 typedef struct tagASSEMBLY_NAME
5914 LPWSTR name;
5915 LPWSTR version;
5916 LPWSTR culture;
5917 LPWSTR pubkeytoken;
5918 } ASSEMBLY_NAME;
5920 static UINT parse_assembly_name(MSIRECORD *rec, LPVOID param)
5922 ASSEMBLY_NAME *asmname = param;
5923 LPCWSTR name = MSI_RecordGetString(rec, 2);
5924 LPWSTR val = msi_dup_record_field(rec, 3);
5926 static const WCHAR Name[] = {'N','a','m','e',0};
5927 static const WCHAR Version[] = {'V','e','r','s','i','o','n',0};
5928 static const WCHAR Culture[] = {'C','u','l','t','u','r','e',0};
5929 static const WCHAR PublicKeyToken[] = {
5930 'P','u','b','l','i','c','K','e','y','T','o','k','e','n',0};
5932 if (!strcmpiW(name, Name))
5933 asmname->name = val;
5934 else if (!strcmpiW(name, Version))
5935 asmname->version = val;
5936 else if (!strcmpiW(name, Culture))
5937 asmname->culture = val;
5938 else if (!strcmpiW(name, PublicKeyToken))
5939 asmname->pubkeytoken = val;
5940 else
5941 msi_free(val);
5943 return ERROR_SUCCESS;
5946 static void append_str(LPWSTR *str, DWORD *size, LPCWSTR append)
5948 if (!*str)
5950 *size = lstrlenW(append) + 1;
5951 *str = msi_alloc((*size) * sizeof(WCHAR));
5952 lstrcpyW(*str, append);
5953 return;
5956 (*size) += lstrlenW(append);
5957 *str = msi_realloc(*str, (*size) * sizeof(WCHAR));
5958 lstrcatW(*str, append);
5961 static BOOL check_assembly_installed(MSIDATABASE *db, IAssemblyCache *cache,
5962 MSICOMPONENT *comp)
5964 ASSEMBLY_INFO asminfo;
5965 ASSEMBLY_NAME name;
5966 MSIQUERY *view;
5967 LPWSTR disp;
5968 DWORD size;
5969 BOOL found;
5970 UINT r;
5972 static const WCHAR separator[] = {',',' ',0};
5973 static const WCHAR Version[] = {'V','e','r','s','i','o','n','=',0};
5974 static const WCHAR Culture[] = {'C','u','l','t','u','r','e','=',0};
5975 static const WCHAR PublicKeyToken[] = {
5976 'P','u','b','l','i','c','K','e','y','T','o','k','e','n','=',0};
5977 static const WCHAR query[] = {
5978 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5979 '`','M','s','i','A','s','s','e','m','b','l','y','N','a','m','e','`',' ',
5980 'W','H','E','R','E',' ','`','C','o','m','p','o','n','e','n','t','_','`',
5981 '=','\'','%','s','\'',0};
5983 disp = NULL;
5984 found = FALSE;
5985 ZeroMemory(&name, sizeof(ASSEMBLY_NAME));
5986 ZeroMemory(&asminfo, sizeof(ASSEMBLY_INFO));
5988 r = MSI_OpenQuery(db, &view, query, comp->Component);
5989 if (r != ERROR_SUCCESS)
5990 return ERROR_SUCCESS;
5992 MSI_IterateRecords(view, NULL, parse_assembly_name, &name);
5993 msiobj_release(&view->hdr);
5995 if (!name.name)
5997 ERR("No assembly name specified!\n");
5998 goto done;
6001 append_str(&disp, &size, name.name);
6003 if (name.version)
6005 append_str(&disp, &size, separator);
6006 append_str(&disp, &size, Version);
6007 append_str(&disp, &size, name.version);
6010 if (name.culture)
6012 append_str(&disp, &size, separator);
6013 append_str(&disp, &size, Culture);
6014 append_str(&disp, &size, name.culture);
6017 if (name.pubkeytoken)
6019 append_str(&disp, &size, separator);
6020 append_str(&disp, &size, PublicKeyToken);
6021 append_str(&disp, &size, name.pubkeytoken);
6024 asminfo.cbAssemblyInfo = sizeof(ASSEMBLY_INFO);
6025 IAssemblyCache_QueryAssemblyInfo(cache, QUERYASMINFO_FLAG_VALIDATE,
6026 disp, &asminfo);
6027 found = (asminfo.dwAssemblyFlags == ASSEMBLYINFO_FLAG_INSTALLED);
6029 done:
6030 msi_free(disp);
6031 msi_free(name.name);
6032 msi_free(name.version);
6033 msi_free(name.culture);
6034 msi_free(name.pubkeytoken);
6036 return found;
6039 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
6041 ASSEMBLY_LIST *list = param;
6042 MSIASSEMBLY *assembly;
6044 assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
6045 if (!assembly)
6046 return ERROR_OUTOFMEMORY;
6048 assembly->component = get_loaded_component(list->package, MSI_RecordGetString(rec, 1));
6050 if (!assembly->component || !assembly->component->Enabled ||
6051 !(assembly->component->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
6053 TRACE("Component not set for install, not publishing assembly\n");
6054 msi_free(assembly);
6055 return ERROR_SUCCESS;
6058 assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
6059 assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
6061 if (!assembly->file)
6063 ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
6064 return ERROR_FUNCTION_FAILED;
6067 assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
6068 assembly->application = strdupW(MSI_RecordGetString(rec, 4));
6069 assembly->attributes = MSI_RecordGetInteger(rec, 5);
6071 if (assembly->application)
6073 WCHAR version[24];
6074 DWORD size = sizeof(version)/sizeof(WCHAR);
6076 /* FIXME: we should probably check the manifest file here */
6078 if (!MsiGetFileVersionW(assembly->file->TargetPath, version, &size, NULL, NULL) &&
6079 (!assembly->file->Version || strcmpW(version, assembly->file->Version) >= 0))
6081 assembly->installed = TRUE;
6084 else
6085 assembly->installed = check_assembly_installed(list->package->db,
6086 list->cache,
6087 assembly->component);
6089 list_add_head(list->assemblies, &assembly->entry);
6090 return ERROR_SUCCESS;
6093 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
6095 IAssemblyCache *cache = NULL;
6096 ASSEMBLY_LIST list;
6097 MSIQUERY *view;
6098 HRESULT hr;
6099 UINT r;
6101 static const WCHAR query[] =
6102 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
6103 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
6105 r = MSI_DatabaseOpenViewW(package->db, query, &view);
6106 if (r != ERROR_SUCCESS)
6107 return ERROR_SUCCESS;
6109 hr = pCreateAssemblyCache(&cache, 0);
6110 if (FAILED(hr))
6111 return ERROR_FUNCTION_FAILED;
6113 list.package = package;
6114 list.cache = cache;
6115 list.assemblies = assemblies;
6117 r = MSI_IterateRecords(view, NULL, load_assembly, &list);
6118 msiobj_release(&view->hdr);
6120 IAssemblyCache_Release(cache);
6122 return r;
6125 static void free_assemblies(struct list *assemblies)
6127 struct list *item, *cursor;
6129 LIST_FOR_EACH_SAFE(item, cursor, assemblies)
6131 MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
6133 list_remove(&assembly->entry);
6134 msi_free(assembly->application);
6135 msi_free(assembly->manifest);
6136 msi_free(assembly);
6140 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
6142 MSIASSEMBLY *assembly;
6144 LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
6146 if (!lstrcmpW(assembly->file->File, file))
6148 *out = assembly;
6149 return TRUE;
6153 return FALSE;
6156 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
6157 LPWSTR *path, DWORD *attrs, PVOID user)
6159 MSIASSEMBLY *assembly;
6160 WCHAR temppath[MAX_PATH];
6161 struct list *assemblies = user;
6162 UINT r;
6164 if (!find_assembly(assemblies, file, &assembly))
6165 return FALSE;
6167 GetTempPathW(MAX_PATH, temppath);
6168 PathAddBackslashW(temppath);
6169 lstrcatW(temppath, assembly->file->FileName);
6171 if (action == MSICABEXTRACT_BEGINEXTRACT)
6173 if (assembly->installed)
6174 return FALSE;
6176 *path = strdupW(temppath);
6177 *attrs = assembly->file->Attributes;
6179 else if (action == MSICABEXTRACT_FILEEXTRACTED)
6181 assembly->installed = TRUE;
6183 r = install_assembly(package, assembly, temppath);
6184 if (r != ERROR_SUCCESS)
6185 ERR("Failed to install assembly\n");
6188 return TRUE;
6191 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
6193 UINT r;
6194 struct list assemblies = LIST_INIT(assemblies);
6195 MSIASSEMBLY *assembly;
6196 MSIMEDIAINFO *mi;
6198 if (!init_functionpointers() || !pCreateAssemblyCache)
6199 return ERROR_FUNCTION_FAILED;
6201 r = load_assemblies(package, &assemblies);
6202 if (r != ERROR_SUCCESS)
6203 goto done;
6205 if (list_empty(&assemblies))
6206 goto done;
6208 mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
6209 if (!mi)
6211 r = ERROR_OUTOFMEMORY;
6212 goto done;
6215 LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
6217 if (assembly->installed && !mi->is_continuous)
6218 continue;
6220 if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
6221 (assembly->file->IsCompressed && !mi->is_extracted))
6223 MSICABDATA data;
6225 r = ready_media(package, assembly->file, mi);
6226 if (r != ERROR_SUCCESS)
6228 ERR("Failed to ready media\n");
6229 break;
6232 data.mi = mi;
6233 data.package = package;
6234 data.cb = installassembly_cb;
6235 data.user = &assemblies;
6237 if (assembly->file->IsCompressed &&
6238 !msi_cabextract(package, mi, &data))
6240 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
6241 r = ERROR_FUNCTION_FAILED;
6242 break;
6246 if (!assembly->file->IsCompressed)
6248 LPWSTR source = resolve_file_source(package, assembly->file);
6250 r = install_assembly(package, assembly, source);
6251 if (r != ERROR_SUCCESS)
6252 ERR("Failed to install assembly\n");
6254 msi_free(source);
6257 /* FIXME: write Installer assembly reg values */
6260 done:
6261 free_assemblies(&assemblies);
6262 return r;
6265 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
6266 LPCSTR action, LPCWSTR table )
6268 static const WCHAR query[] = {
6269 'S','E','L','E','C','T',' ','*',' ',
6270 'F','R','O','M',' ','`','%','s','`',0 };
6271 MSIQUERY *view = NULL;
6272 DWORD count = 0;
6273 UINT r;
6275 r = MSI_OpenQuery( package->db, &view, query, table );
6276 if (r == ERROR_SUCCESS)
6278 r = MSI_IterateRecords(view, &count, NULL, package);
6279 msiobj_release(&view->hdr);
6282 if (count)
6283 FIXME("%s -> %u ignored %s table values\n",
6284 action, count, debugstr_w(table));
6286 return ERROR_SUCCESS;
6289 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6291 TRACE("%p\n", package);
6292 return ERROR_SUCCESS;
6295 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
6297 static const WCHAR table[] =
6298 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
6299 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
6302 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
6304 static const WCHAR table[] = { 'P','a','t','c','h',0 };
6305 return msi_unimplemented_action_stub( package, "PatchFiles", table );
6308 static UINT ACTION_BindImage( MSIPACKAGE *package )
6310 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
6311 return msi_unimplemented_action_stub( package, "BindImage", table );
6314 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
6316 static const WCHAR table[] = {
6317 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
6318 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
6321 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
6323 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6324 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
6327 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
6329 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
6330 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
6333 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6335 static const WCHAR table[] = {
6336 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
6337 return msi_unimplemented_action_stub( package, "DeleteServices", table );
6339 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6341 static const WCHAR table[] = {
6342 'P','r','o','d','u','c','t','I','D',0 };
6343 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
6346 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6348 static const WCHAR table[] = {
6349 'E','n','v','i','r','o','n','m','e','n','t',0 };
6350 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
6353 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
6355 static const WCHAR table[] = {
6356 'M','s','i','A','s','s','e','m','b','l','y',0 };
6357 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
6360 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
6362 static const WCHAR table[] = { 'F','o','n','t',0 };
6363 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
6366 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
6368 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
6369 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
6372 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
6374 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6375 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
6378 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
6380 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6381 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
6384 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
6386 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
6387 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
6390 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
6392 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
6393 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
6396 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6398 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6399 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
6402 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
6404 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
6405 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
6408 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6410 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
6411 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
6414 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
6416 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
6417 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
6420 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
6422 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
6423 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
6426 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
6428 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
6429 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
6432 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
6434 static const WCHAR table[] = { 'A','p','p','I','d',0 };
6435 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
6438 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
6440 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
6441 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
6444 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
6446 static const WCHAR table[] = { 'M','I','M','E',0 };
6447 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
6450 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
6452 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
6453 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
6456 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
6458 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
6459 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
6462 static const struct _actions StandardActions[] = {
6463 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
6464 { szAppSearch, ACTION_AppSearch },
6465 { szBindImage, ACTION_BindImage },
6466 { szCCPSearch, ACTION_CCPSearch },
6467 { szCostFinalize, ACTION_CostFinalize },
6468 { szCostInitialize, ACTION_CostInitialize },
6469 { szCreateFolders, ACTION_CreateFolders },
6470 { szCreateShortcuts, ACTION_CreateShortcuts },
6471 { szDeleteServices, ACTION_DeleteServices },
6472 { szDisableRollback, NULL },
6473 { szDuplicateFiles, ACTION_DuplicateFiles },
6474 { szExecuteAction, ACTION_ExecuteAction },
6475 { szFileCost, ACTION_FileCost },
6476 { szFindRelatedProducts, ACTION_FindRelatedProducts },
6477 { szForceReboot, ACTION_ForceReboot },
6478 { szInstallAdminPackage, NULL },
6479 { szInstallExecute, ACTION_InstallExecute },
6480 { szInstallExecuteAgain, ACTION_InstallExecute },
6481 { szInstallFiles, ACTION_InstallFiles},
6482 { szInstallFinalize, ACTION_InstallFinalize },
6483 { szInstallInitialize, ACTION_InstallInitialize },
6484 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
6485 { szInstallValidate, ACTION_InstallValidate },
6486 { szIsolateComponents, ACTION_IsolateComponents },
6487 { szLaunchConditions, ACTION_LaunchConditions },
6488 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
6489 { szMoveFiles, ACTION_MoveFiles },
6490 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
6491 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
6492 { szInstallODBC, ACTION_InstallODBC },
6493 { szInstallServices, ACTION_InstallServices },
6494 { szPatchFiles, ACTION_PatchFiles },
6495 { szProcessComponents, ACTION_ProcessComponents },
6496 { szPublishComponents, ACTION_PublishComponents },
6497 { szPublishFeatures, ACTION_PublishFeatures },
6498 { szPublishProduct, ACTION_PublishProduct },
6499 { szRegisterClassInfo, ACTION_RegisterClassInfo },
6500 { szRegisterComPlus, ACTION_RegisterComPlus},
6501 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
6502 { szRegisterFonts, ACTION_RegisterFonts },
6503 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
6504 { szRegisterProduct, ACTION_RegisterProduct },
6505 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
6506 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
6507 { szRegisterUser, ACTION_RegisterUser },
6508 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
6509 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
6510 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
6511 { szRemoveFiles, ACTION_RemoveFiles },
6512 { szRemoveFolders, ACTION_RemoveFolders },
6513 { szRemoveIniValues, ACTION_RemoveIniValues },
6514 { szRemoveODBC, ACTION_RemoveODBC },
6515 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
6516 { szRemoveShortcuts, ACTION_RemoveShortcuts },
6517 { szResolveSource, ACTION_ResolveSource },
6518 { szRMCCPSearch, ACTION_RMCCPSearch },
6519 { szScheduleReboot, NULL },
6520 { szSelfRegModules, ACTION_SelfRegModules },
6521 { szSelfUnregModules, ACTION_SelfUnregModules },
6522 { szSetODBCFolders, NULL },
6523 { szStartServices, ACTION_StartServices },
6524 { szStopServices, ACTION_StopServices },
6525 { szUnpublishComponents, ACTION_UnpublishComponents },
6526 { szUnpublishFeatures, ACTION_UnpublishFeatures },
6527 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6528 { szUnregisterComPlus, ACTION_UnregisterComPlus },
6529 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6530 { szUnregisterFonts, ACTION_UnregisterFonts },
6531 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6532 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6533 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6534 { szValidateProductID, ACTION_ValidateProductID },
6535 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6536 { szWriteIniValues, ACTION_WriteIniValues },
6537 { szWriteRegistryValues, ACTION_WriteRegistryValues },
6538 { NULL, NULL },
6541 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
6542 UINT* rc, BOOL force )
6544 BOOL ret = FALSE;
6545 BOOL run = force;
6546 int i;
6548 if (!run && !package->script->CurrentlyScripting)
6549 run = TRUE;
6551 if (!run)
6553 if (strcmpW(action,szInstallFinalize) == 0 ||
6554 strcmpW(action,szInstallExecute) == 0 ||
6555 strcmpW(action,szInstallExecuteAgain) == 0)
6556 run = TRUE;
6559 i = 0;
6560 while (StandardActions[i].action != NULL)
6562 if (strcmpW(StandardActions[i].action, action)==0)
6564 if (!run)
6566 ui_actioninfo(package, action, TRUE, 0);
6567 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6568 ui_actioninfo(package, action, FALSE, *rc);
6570 else
6572 ui_actionstart(package, action);
6573 if (StandardActions[i].handler)
6575 *rc = StandardActions[i].handler(package);
6577 else
6579 FIXME("unhandled standard action %s\n",debugstr_w(action));
6580 *rc = ERROR_SUCCESS;
6583 ret = TRUE;
6584 break;
6586 i++;
6588 return ret;