user32: Handle all kinds of values returned by WIN_GetPtr.
[wine/multimedia.git] / dlls / msi / action.c
blob37a7de3d7ed9c675f460f40f6de4d2594208511c
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 )
293 LPCWSTR ptr,ptr2;
294 BOOL quote;
295 DWORD len;
296 LPWSTR prop = NULL, val = NULL;
298 if (!szCommandLine)
299 return ERROR_SUCCESS;
301 ptr = szCommandLine;
303 while (*ptr)
305 if (*ptr==' ')
307 ptr++;
308 continue;
311 TRACE("Looking at %s\n",debugstr_w(ptr));
313 ptr2 = strchrW(ptr,'=');
314 if (!ptr2)
316 ERR("command line contains unknown string : %s\n", debugstr_w(ptr));
317 break;
320 quote = FALSE;
322 len = ptr2-ptr;
323 prop = msi_alloc((len+1)*sizeof(WCHAR));
324 memcpy(prop,ptr,len*sizeof(WCHAR));
325 prop[len]=0;
326 struprW(prop);
327 ptr2++;
329 len = 0;
330 ptr = ptr2;
331 while (*ptr && (quote || (!quote && *ptr!=' ')))
333 if (*ptr == '"')
334 quote = !quote;
335 ptr++;
336 len++;
339 if (*ptr2=='"')
341 ptr2++;
342 len -= 2;
344 val = msi_alloc((len+1)*sizeof(WCHAR));
345 memcpy(val,ptr2,len*sizeof(WCHAR));
346 val[len] = 0;
348 if (lstrlenW(prop) > 0)
350 TRACE("Found commandline property (%s) = (%s)\n",
351 debugstr_w(prop), debugstr_w(val));
352 MSI_SetPropertyW(package,prop,val);
354 msi_free(val);
355 msi_free(prop);
358 return ERROR_SUCCESS;
362 static LPWSTR* msi_split_string( LPCWSTR str, WCHAR sep )
364 LPCWSTR pc;
365 LPWSTR p, *ret = NULL;
366 UINT count = 0;
368 if (!str)
369 return ret;
371 /* count the number of substrings */
372 for ( pc = str, count = 0; pc; count++ )
374 pc = strchrW( pc, sep );
375 if (pc)
376 pc++;
379 /* allocate space for an array of substring pointers and the substrings */
380 ret = msi_alloc( (count+1) * sizeof (LPWSTR) +
381 (lstrlenW(str)+1) * sizeof(WCHAR) );
382 if (!ret)
383 return ret;
385 /* copy the string and set the pointers */
386 p = (LPWSTR) &ret[count+1];
387 lstrcpyW( p, str );
388 for( count = 0; (ret[count] = p); count++ )
390 p = strchrW( p, sep );
391 if (p)
392 *p++ = 0;
395 return ret;
398 static UINT msi_check_transform_applicable( MSIPACKAGE *package, IStorage *patch )
400 WCHAR szProductCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
401 LPWSTR prod_code, patch_product;
402 UINT ret;
404 prod_code = msi_dup_property( package, szProductCode );
405 patch_product = msi_get_suminfo_product( patch );
407 TRACE("db = %s patch = %s\n", debugstr_w(prod_code), debugstr_w(patch_product));
409 if ( strstrW( patch_product, prod_code ) )
410 ret = ERROR_SUCCESS;
411 else
412 ret = ERROR_FUNCTION_FAILED;
414 msi_free( patch_product );
415 msi_free( prod_code );
417 return ret;
420 static UINT msi_apply_substorage_transform( MSIPACKAGE *package,
421 MSIDATABASE *patch_db, LPCWSTR name )
423 UINT ret = ERROR_FUNCTION_FAILED;
424 IStorage *stg = NULL;
425 HRESULT r;
427 TRACE("%p %s\n", package, debugstr_w(name) );
429 if (*name++ != ':')
431 ERR("expected a colon in %s\n", debugstr_w(name));
432 return ERROR_FUNCTION_FAILED;
435 r = IStorage_OpenStorage( patch_db->storage, name, NULL, STGM_SHARE_EXCLUSIVE, NULL, 0, &stg );
436 if (SUCCEEDED(r))
438 ret = msi_check_transform_applicable( package, stg );
439 if (ret == ERROR_SUCCESS)
440 msi_table_apply_transform( package->db, stg );
441 else
442 TRACE("substorage transform %s wasn't applicable\n", debugstr_w(name));
443 IStorage_Release( stg );
445 else
446 ERR("failed to open substorage %s\n", debugstr_w(name));
448 return ERROR_SUCCESS;
451 static UINT msi_check_patch_applicable( MSIPACKAGE *package, MSISUMMARYINFO *si )
453 static const WCHAR szProdCode[] = { 'P','r','o','d','u','c','t','C','o','d','e',0 };
454 LPWSTR guid_list, *guids, product_code;
455 UINT i, ret = ERROR_FUNCTION_FAILED;
457 product_code = msi_dup_property( package, szProdCode );
458 if (!product_code)
460 /* FIXME: the property ProductCode should be written into the DB somewhere */
461 ERR("no product code to check\n");
462 return ERROR_SUCCESS;
465 guid_list = msi_suminfo_dup_string( si, PID_TEMPLATE );
466 guids = msi_split_string( guid_list, ';' );
467 for ( i = 0; guids[i] && ret != ERROR_SUCCESS; i++ )
469 if (!lstrcmpW( guids[i], product_code ))
470 ret = ERROR_SUCCESS;
472 msi_free( guids );
473 msi_free( guid_list );
474 msi_free( product_code );
476 return ret;
479 static UINT msi_parse_patch_summary( MSIPACKAGE *package, MSIDATABASE *patch_db )
481 MSISUMMARYINFO *si;
482 LPWSTR str, *substorage;
483 UINT i, r = ERROR_SUCCESS;
485 si = MSI_GetSummaryInformationW( patch_db->storage, 0 );
486 if (!si)
487 return ERROR_FUNCTION_FAILED;
489 msi_check_patch_applicable( package, si );
491 /* enumerate the substorage */
492 str = msi_suminfo_dup_string( si, PID_LASTAUTHOR );
493 substorage = msi_split_string( str, ';' );
494 for ( i = 0; substorage && substorage[i] && r == ERROR_SUCCESS; i++ )
495 r = msi_apply_substorage_transform( package, patch_db, substorage[i] );
496 msi_free( substorage );
497 msi_free( str );
499 /* FIXME: parse the sources in PID_REVNUMBER and do something with them... */
501 msiobj_release( &si->hdr );
503 return r;
506 static UINT msi_apply_patch_package( MSIPACKAGE *package, LPCWSTR file )
508 MSIDATABASE *patch_db = NULL;
509 UINT r;
511 TRACE("%p %s\n", package, debugstr_w( file ) );
513 /* FIXME:
514 * We probably want to make sure we only open a patch collection here.
515 * Patch collections (.msp) and databases (.msi) have different GUIDs
516 * but currently MSI_OpenDatabaseW will accept both.
518 r = MSI_OpenDatabaseW( file, MSIDBOPEN_READONLY, &patch_db );
519 if ( r != ERROR_SUCCESS )
521 ERR("failed to open patch collection %s\n", debugstr_w( file ) );
522 return r;
525 msi_parse_patch_summary( package, patch_db );
528 * There might be a CAB file in the patch package,
529 * so append it to the list of storage to search for streams.
531 append_storage_to_db( package->db, patch_db->storage );
533 msiobj_release( &patch_db->hdr );
535 return ERROR_SUCCESS;
538 /* get the PATCH property, and apply all the patches it specifies */
539 static UINT msi_apply_patches( MSIPACKAGE *package )
541 static const WCHAR szPatch[] = { 'P','A','T','C','H',0 };
542 LPWSTR patch_list, *patches;
543 UINT i, r = ERROR_SUCCESS;
545 patch_list = msi_dup_property( package, szPatch );
547 TRACE("patches to be applied: %s\n", debugstr_w( patch_list ) );
549 patches = msi_split_string( patch_list, ';' );
550 for( i=0; patches && patches[i] && r == ERROR_SUCCESS; i++ )
551 r = msi_apply_patch_package( package, patches[i] );
553 msi_free( patches );
554 msi_free( patch_list );
556 return r;
559 static UINT msi_apply_transforms( MSIPACKAGE *package )
561 static const WCHAR szTransforms[] = {
562 'T','R','A','N','S','F','O','R','M','S',0 };
563 LPWSTR xform_list, *xforms;
564 UINT i, r = ERROR_SUCCESS;
566 xform_list = msi_dup_property( package, szTransforms );
567 xforms = msi_split_string( xform_list, ';' );
569 for( i=0; xforms && xforms[i] && r == ERROR_SUCCESS; i++ )
571 if (xforms[i][0] == ':')
572 r = msi_apply_substorage_transform( package, package->db, xforms[i] );
573 else
574 r = MSI_DatabaseApplyTransformW( package->db, xforms[i], 0 );
577 msi_free( xforms );
578 msi_free( xform_list );
580 return r;
583 static BOOL ui_sequence_exists( MSIPACKAGE *package )
585 MSIQUERY *view;
586 UINT rc;
588 static const WCHAR ExecSeqQuery [] =
589 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
590 '`','I','n','s','t','a','l','l',
591 'U','I','S','e','q','u','e','n','c','e','`',
592 ' ','W','H','E','R','E',' ',
593 '`','S','e','q','u','e','n','c','e','`',' ',
594 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
595 '`','S','e','q','u','e','n','c','e','`',0};
597 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
598 if (rc == ERROR_SUCCESS)
600 msiobj_release(&view->hdr);
601 return TRUE;
604 return FALSE;
607 static UINT msi_set_sourcedir_props(MSIPACKAGE *package, BOOL replace)
609 LPWSTR p, db;
610 LPWSTR source, check;
611 DWORD len;
613 static const WCHAR szOriginalDatabase[] =
614 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
616 db = msi_dup_property( package, szOriginalDatabase );
617 if (!db)
618 return ERROR_OUTOFMEMORY;
620 p = strrchrW( db, '\\' );
621 if (!p)
623 p = strrchrW( db, '/' );
624 if (!p)
626 msi_free(db);
627 return ERROR_SUCCESS;
631 len = p - db + 2;
632 source = msi_alloc( len * sizeof(WCHAR) );
633 lstrcpynW( source, db, len );
635 check = msi_dup_property( package, cszSourceDir );
636 if (!check || replace)
637 MSI_SetPropertyW( package, cszSourceDir, source );
639 msi_free( check );
641 check = msi_dup_property( package, cszSOURCEDIR );
642 if (!check || replace)
643 MSI_SetPropertyW( package, cszSOURCEDIR, source );
645 msi_free( check );
646 msi_free( source );
647 msi_free( db );
649 return ERROR_SUCCESS;
652 static UINT msi_set_context(MSIPACKAGE *package)
654 WCHAR val[10];
655 DWORD sz = 10;
656 DWORD num;
657 UINT r;
659 static const WCHAR szOne[] = {'1',0};
660 static const WCHAR szAllUsers[] = {'A','L','L','U','S','E','R','S',0};
662 package->Context = MSIINSTALLCONTEXT_USERUNMANAGED;
664 r = MSI_GetPropertyW(package, szAllUsers, val, &sz);
665 if (r == ERROR_SUCCESS)
667 num = atolW(val);
668 if (num == 1 || num == 2)
669 package->Context = MSIINSTALLCONTEXT_MACHINE;
672 MSI_SetPropertyW(package, szAllUsers, szOne);
673 return ERROR_SUCCESS;
676 /****************************************************
677 * TOP level entry points
678 *****************************************************/
680 UINT MSI_InstallPackage( MSIPACKAGE *package, LPCWSTR szPackagePath,
681 LPCWSTR szCommandLine )
683 UINT rc;
684 BOOL ui = FALSE, ui_exists;
685 static const WCHAR szUILevel[] = {'U','I','L','e','v','e','l',0};
686 static const WCHAR szAction[] = {'A','C','T','I','O','N',0};
687 static const WCHAR szInstall[] = {'I','N','S','T','A','L','L',0};
689 MSI_SetPropertyW(package, szAction, szInstall);
691 package->script = msi_alloc_zero(sizeof(MSISCRIPT));
693 package->script->InWhatSequence = SEQUENCE_INSTALL;
695 if (szPackagePath)
697 LPWSTR p, dir;
698 LPCWSTR file;
700 dir = strdupW(szPackagePath);
701 p = strrchrW(dir, '\\');
702 if (p)
704 *(++p) = 0;
705 file = szPackagePath + (p - dir);
707 else
709 msi_free(dir);
710 dir = msi_alloc(MAX_PATH*sizeof(WCHAR));
711 GetCurrentDirectoryW(MAX_PATH, dir);
712 lstrcatW(dir, cszbs);
713 file = szPackagePath;
716 msi_free( package->PackagePath );
717 package->PackagePath = msi_alloc((lstrlenW(dir) + lstrlenW(file) + 1) * sizeof(WCHAR));
718 if (!package->PackagePath)
720 msi_free(dir);
721 return ERROR_OUTOFMEMORY;
724 lstrcpyW(package->PackagePath, dir);
725 lstrcatW(package->PackagePath, file);
726 msi_free(dir);
728 msi_set_sourcedir_props(package, FALSE);
731 msi_parse_command_line( package, szCommandLine );
733 msi_apply_transforms( package );
734 msi_apply_patches( package );
736 /* properties may have been added by a transform */
737 msi_clone_properties( package );
738 msi_set_context( package );
740 if ( (msi_get_property_int(package, szUILevel, 0) & INSTALLUILEVEL_MASK) >= INSTALLUILEVEL_REDUCED )
742 package->script->InWhatSequence |= SEQUENCE_UI;
743 rc = ACTION_ProcessUISequence(package);
744 ui = TRUE;
745 ui_exists = ui_sequence_exists(package);
746 if (rc == ERROR_SUCCESS || !ui_exists)
748 package->script->InWhatSequence |= SEQUENCE_EXEC;
749 rc = ACTION_ProcessExecSequence(package,ui_exists);
752 else
753 rc = ACTION_ProcessExecSequence(package,FALSE);
755 package->script->CurrentlyScripting= FALSE;
757 /* process the ending type action */
758 if (rc == ERROR_SUCCESS)
759 ACTION_PerformActionSequence(package,-1,ui);
760 else if (rc == ERROR_INSTALL_USEREXIT)
761 ACTION_PerformActionSequence(package,-2,ui);
762 else if (rc == ERROR_INSTALL_SUSPEND)
763 ACTION_PerformActionSequence(package,-4,ui);
764 else /* failed */
765 ACTION_PerformActionSequence(package,-3,ui);
767 /* finish up running custom actions */
768 ACTION_FinishCustomActions(package);
770 return rc;
773 static UINT ACTION_PerformActionSequence(MSIPACKAGE *package, UINT seq, BOOL UI)
775 UINT rc = ERROR_SUCCESS;
776 MSIRECORD * row = 0;
777 static const WCHAR ExecSeqQuery[] =
778 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
779 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
780 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
781 '`','S','e','q','u','e','n','c','e','`',' ', '=',' ','%','i',0};
783 static const WCHAR UISeqQuery[] =
784 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
785 '`','I','n','s','t','a','l','l','U','I','S','e','q','u','e','n','c','e',
786 '`', ' ', 'W','H','E','R','E',' ','`','S','e','q','u','e','n','c','e','`',
787 ' ', '=',' ','%','i',0};
789 if (UI)
790 row = MSI_QueryGetRecord(package->db, UISeqQuery, seq);
791 else
792 row = MSI_QueryGetRecord(package->db, ExecSeqQuery, seq);
794 if (row)
796 LPCWSTR action, cond;
798 TRACE("Running the actions\n");
800 /* check conditions */
801 cond = MSI_RecordGetString(row,2);
803 /* this is a hack to skip errors in the condition code */
804 if (MSI_EvaluateConditionW(package, cond) == MSICONDITION_FALSE)
805 goto end;
807 action = MSI_RecordGetString(row,1);
808 if (!action)
810 ERR("failed to fetch action\n");
811 rc = ERROR_FUNCTION_FAILED;
812 goto end;
815 if (UI)
816 rc = ACTION_PerformUIAction(package,action,-1);
817 else
818 rc = ACTION_PerformAction(package,action,-1,FALSE);
819 end:
820 msiobj_release(&row->hdr);
822 else
823 rc = ERROR_SUCCESS;
825 return rc;
828 typedef struct {
829 MSIPACKAGE* package;
830 BOOL UI;
831 } iterate_action_param;
833 static UINT ITERATE_Actions(MSIRECORD *row, LPVOID param)
835 iterate_action_param *iap= (iterate_action_param*)param;
836 UINT rc;
837 LPCWSTR cond, action;
839 action = MSI_RecordGetString(row,1);
840 if (!action)
842 ERR("Error is retrieving action name\n");
843 return ERROR_FUNCTION_FAILED;
846 /* check conditions */
847 cond = MSI_RecordGetString(row,2);
849 /* this is a hack to skip errors in the condition code */
850 if (MSI_EvaluateConditionW(iap->package, cond) == MSICONDITION_FALSE)
852 TRACE("Skipping action: %s (condition is false)\n", debugstr_w(action));
853 return ERROR_SUCCESS;
856 if (iap->UI)
857 rc = ACTION_PerformUIAction(iap->package,action,-1);
858 else
859 rc = ACTION_PerformAction(iap->package,action,-1,FALSE);
861 msi_dialog_check_messages( NULL );
863 if (iap->package->CurrentInstallState != ERROR_SUCCESS )
864 rc = iap->package->CurrentInstallState;
866 if (rc == ERROR_FUNCTION_NOT_CALLED)
867 rc = ERROR_SUCCESS;
869 if (rc != ERROR_SUCCESS)
870 ERR("Execution halted, action %s returned %i\n", debugstr_w(action), rc);
872 return rc;
875 UINT MSI_Sequence( MSIPACKAGE *package, LPCWSTR szTable, INT iSequenceMode )
877 MSIQUERY * view;
878 UINT r;
879 static const WCHAR query[] =
880 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
881 '`','%','s','`',
882 ' ','W','H','E','R','E',' ',
883 '`','S','e','q','u','e','n','c','e','`',' ',
884 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
885 '`','S','e','q','u','e','n','c','e','`',0};
886 iterate_action_param iap;
889 * FIXME: probably should be checking UILevel in the
890 * ACTION_PerformUIAction/ACTION_PerformAction
891 * rather than saving the UI level here. Those
892 * two functions can be merged too.
894 iap.package = package;
895 iap.UI = TRUE;
897 TRACE("%p %s %i\n", package, debugstr_w(szTable), iSequenceMode );
899 r = MSI_OpenQuery( package->db, &view, query, szTable );
900 if (r == ERROR_SUCCESS)
902 r = MSI_IterateRecords( view, NULL, ITERATE_Actions, &iap );
903 msiobj_release(&view->hdr);
906 return r;
909 static UINT ACTION_ProcessExecSequence(MSIPACKAGE *package, BOOL UIran)
911 MSIQUERY * view;
912 UINT rc;
913 static const WCHAR ExecSeqQuery[] =
914 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
915 '`','I','n','s','t','a','l','l','E','x','e','c','u','t','e',
916 'S','e','q','u','e','n','c','e','`',' ', 'W','H','E','R','E',' ',
917 '`','S','e','q','u','e','n','c','e','`',' ', '>',' ','%','i',' ',
918 'O','R','D','E','R',' ', 'B','Y',' ',
919 '`','S','e','q','u','e','n','c','e','`',0 };
920 MSIRECORD * row = 0;
921 static const WCHAR IVQuery[] =
922 {'S','E','L','E','C','T',' ','`','S','e','q','u','e','n','c','e','`',
923 ' ', 'F','R','O','M',' ','`','I','n','s','t','a','l','l',
924 'E','x','e','c','u','t','e','S','e','q','u','e','n','c','e','`',' ',
925 'W','H','E','R','E',' ','`','A','c','t','i','o','n','`',' ','=',
926 ' ','\'', 'I','n','s','t','a','l','l',
927 'V','a','l','i','d','a','t','e','\'', 0};
928 INT seq = 0;
929 iterate_action_param iap;
931 iap.package = package;
932 iap.UI = FALSE;
934 if (package->script->ExecuteSequenceRun)
936 TRACE("Execute Sequence already Run\n");
937 return ERROR_SUCCESS;
940 package->script->ExecuteSequenceRun = TRUE;
942 /* get the sequence number */
943 if (UIran)
945 row = MSI_QueryGetRecord(package->db, IVQuery);
946 if( !row )
947 return ERROR_FUNCTION_FAILED;
948 seq = MSI_RecordGetInteger(row,1);
949 msiobj_release(&row->hdr);
952 rc = MSI_OpenQuery(package->db, &view, ExecSeqQuery, seq);
953 if (rc == ERROR_SUCCESS)
955 TRACE("Running the actions\n");
957 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
958 msiobj_release(&view->hdr);
961 return rc;
964 static UINT ACTION_ProcessUISequence(MSIPACKAGE *package)
966 MSIQUERY * view;
967 UINT rc;
968 static const WCHAR ExecSeqQuery [] =
969 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
970 '`','I','n','s','t','a','l','l',
971 'U','I','S','e','q','u','e','n','c','e','`',
972 ' ','W','H','E','R','E',' ',
973 '`','S','e','q','u','e','n','c','e','`',' ',
974 '>',' ','0',' ','O','R','D','E','R',' ','B','Y',' ',
975 '`','S','e','q','u','e','n','c','e','`',0};
976 iterate_action_param iap;
978 iap.package = package;
979 iap.UI = TRUE;
981 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
983 if (rc == ERROR_SUCCESS)
985 TRACE("Running the actions\n");
987 rc = MSI_IterateRecords(view, NULL, ITERATE_Actions, &iap);
988 msiobj_release(&view->hdr);
991 return rc;
994 /********************************************************
995 * ACTION helper functions and functions that perform the actions
996 *******************************************************/
997 static BOOL ACTION_HandleCustomAction( MSIPACKAGE* package, LPCWSTR action,
998 UINT* rc, UINT script, BOOL force )
1000 BOOL ret=FALSE;
1001 UINT arc;
1003 arc = ACTION_CustomAction(package, action, script, force);
1005 if (arc != ERROR_CALL_NOT_IMPLEMENTED)
1007 *rc = arc;
1008 ret = TRUE;
1010 return ret;
1014 * A lot of actions are really important even if they don't do anything
1015 * explicit... Lots of properties are set at the beginning of the installation
1016 * CostFinalize does a bunch of work to translate the directories and such
1018 * But until I get write access to the database that is hard, so I am going to
1019 * hack it to see if I can get something to run.
1021 UINT ACTION_PerformAction(MSIPACKAGE *package, const WCHAR *action, UINT script, BOOL force)
1023 UINT rc = ERROR_SUCCESS;
1024 BOOL handled;
1026 TRACE("Performing action (%s)\n",debugstr_w(action));
1028 handled = ACTION_HandleStandardAction(package, action, &rc, force);
1030 if (!handled)
1031 handled = ACTION_HandleCustomAction(package, action, &rc, script, force);
1033 if (!handled)
1035 WARN("unhandled msi action %s\n",debugstr_w(action));
1036 rc = ERROR_FUNCTION_NOT_CALLED;
1039 return rc;
1042 UINT ACTION_PerformUIAction(MSIPACKAGE *package, const WCHAR *action, UINT script)
1044 UINT rc = ERROR_SUCCESS;
1045 BOOL handled = FALSE;
1047 TRACE("Performing action (%s)\n",debugstr_w(action));
1049 handled = ACTION_HandleStandardAction(package, action, &rc,TRUE);
1051 if (!handled)
1052 handled = ACTION_HandleCustomAction(package, action, &rc, script, FALSE);
1054 if( !handled && ACTION_DialogBox(package,action) == ERROR_SUCCESS )
1055 handled = TRUE;
1057 if (!handled)
1059 WARN("unhandled msi action %s\n",debugstr_w(action));
1060 rc = ERROR_FUNCTION_NOT_CALLED;
1063 return rc;
1068 * Actual Action Handlers
1071 static UINT ITERATE_CreateFolders(MSIRECORD *row, LPVOID param)
1073 MSIPACKAGE *package = (MSIPACKAGE*)param;
1074 LPCWSTR dir;
1075 LPWSTR full_path;
1076 MSIRECORD *uirow;
1077 MSIFOLDER *folder;
1079 dir = MSI_RecordGetString(row,1);
1080 if (!dir)
1082 ERR("Unable to get folder id\n");
1083 return ERROR_SUCCESS;
1086 full_path = resolve_folder(package,dir,FALSE,FALSE,TRUE,&folder);
1087 if (!full_path)
1089 ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
1090 return ERROR_SUCCESS;
1093 TRACE("Folder is %s\n",debugstr_w(full_path));
1095 /* UI stuff */
1096 uirow = MSI_CreateRecord(1);
1097 MSI_RecordSetStringW(uirow,1,full_path);
1098 ui_actiondata(package,szCreateFolders,uirow);
1099 msiobj_release( &uirow->hdr );
1101 if (folder->State == 0)
1102 create_full_pathW(full_path);
1104 folder->State = 3;
1106 msi_free(full_path);
1107 return ERROR_SUCCESS;
1110 /* FIXME: probably should merge this with the above function */
1111 static UINT msi_create_directory( MSIPACKAGE* package, LPCWSTR dir )
1113 UINT rc = ERROR_SUCCESS;
1114 MSIFOLDER *folder;
1115 LPWSTR install_path;
1117 install_path = resolve_folder(package, dir, FALSE, FALSE, TRUE, &folder);
1118 if (!install_path)
1119 return ERROR_FUNCTION_FAILED;
1121 /* create the path */
1122 if (folder->State == 0)
1124 create_full_pathW(install_path);
1125 folder->State = 2;
1127 msi_free(install_path);
1129 return rc;
1132 UINT msi_create_component_directories( MSIPACKAGE *package )
1134 MSICOMPONENT *comp;
1136 /* create all the folders required by the components are going to install */
1137 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
1139 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
1140 continue;
1141 msi_create_directory( package, comp->Directory );
1144 return ERROR_SUCCESS;
1148 * Also we cannot enable/disable components either, so for now I am just going
1149 * to do all the directories for all the components.
1151 static UINT ACTION_CreateFolders(MSIPACKAGE *package)
1153 static const WCHAR ExecSeqQuery[] =
1154 {'S','E','L','E','C','T',' ',
1155 '`','D','i','r','e','c','t','o','r','y','_','`',
1156 ' ','F','R','O','M',' ',
1157 '`','C','r','e','a','t','e','F','o','l','d','e','r','`',0 };
1158 UINT rc;
1159 MSIQUERY *view;
1161 /* create all the empty folders specified in the CreateFolder table */
1162 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view );
1163 if (rc != ERROR_SUCCESS)
1164 return ERROR_SUCCESS;
1166 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateFolders, package);
1167 msiobj_release(&view->hdr);
1169 msi_create_component_directories( package );
1171 return rc;
1174 static UINT load_component( MSIRECORD *row, LPVOID param )
1176 MSIPACKAGE *package = param;
1177 MSICOMPONENT *comp;
1179 comp = msi_alloc_zero( sizeof(MSICOMPONENT) );
1180 if (!comp)
1181 return ERROR_FUNCTION_FAILED;
1183 list_add_tail( &package->components, &comp->entry );
1185 /* fill in the data */
1186 comp->Component = msi_dup_record_field( row, 1 );
1188 TRACE("Loading Component %s\n", debugstr_w(comp->Component));
1190 comp->ComponentId = msi_dup_record_field( row, 2 );
1191 comp->Directory = msi_dup_record_field( row, 3 );
1192 comp->Attributes = MSI_RecordGetInteger(row,4);
1193 comp->Condition = msi_dup_record_field( row, 5 );
1194 comp->KeyPath = msi_dup_record_field( row, 6 );
1196 comp->Installed = INSTALLSTATE_UNKNOWN;
1197 msi_component_set_state(package, comp, INSTALLSTATE_UNKNOWN);
1199 return ERROR_SUCCESS;
1202 static UINT load_all_components( MSIPACKAGE *package )
1204 static const WCHAR query[] = {
1205 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1206 '`','C','o','m','p','o','n','e','n','t','`',0 };
1207 MSIQUERY *view;
1208 UINT r;
1210 if (!list_empty(&package->components))
1211 return ERROR_SUCCESS;
1213 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1214 if (r != ERROR_SUCCESS)
1215 return r;
1217 r = MSI_IterateRecords(view, NULL, load_component, package);
1218 msiobj_release(&view->hdr);
1219 return r;
1222 typedef struct {
1223 MSIPACKAGE *package;
1224 MSIFEATURE *feature;
1225 } _ilfs;
1227 static UINT add_feature_component( MSIFEATURE *feature, MSICOMPONENT *comp )
1229 ComponentList *cl;
1231 cl = msi_alloc( sizeof (*cl) );
1232 if ( !cl )
1233 return ERROR_NOT_ENOUGH_MEMORY;
1234 cl->component = comp;
1235 list_add_tail( &feature->Components, &cl->entry );
1237 return ERROR_SUCCESS;
1240 static UINT add_feature_child( MSIFEATURE *parent, MSIFEATURE *child )
1242 FeatureList *fl;
1244 fl = msi_alloc( sizeof(*fl) );
1245 if ( !fl )
1246 return ERROR_NOT_ENOUGH_MEMORY;
1247 fl->feature = child;
1248 list_add_tail( &parent->Children, &fl->entry );
1250 return ERROR_SUCCESS;
1253 static UINT iterate_load_featurecomponents(MSIRECORD *row, LPVOID param)
1255 _ilfs* ilfs= (_ilfs*)param;
1256 LPCWSTR component;
1257 MSICOMPONENT *comp;
1259 component = MSI_RecordGetString(row,1);
1261 /* check to see if the component is already loaded */
1262 comp = get_loaded_component( ilfs->package, component );
1263 if (!comp)
1265 ERR("unknown component %s\n", debugstr_w(component));
1266 return ERROR_FUNCTION_FAILED;
1269 add_feature_component( ilfs->feature, comp );
1270 comp->Enabled = TRUE;
1272 return ERROR_SUCCESS;
1275 static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
1277 MSIFEATURE *feature;
1279 if ( !name )
1280 return NULL;
1282 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1284 if ( !lstrcmpW( feature->Feature, name ) )
1285 return feature;
1288 return NULL;
1291 static UINT load_feature(MSIRECORD * row, LPVOID param)
1293 MSIPACKAGE* package = (MSIPACKAGE*)param;
1294 MSIFEATURE* feature;
1295 static const WCHAR Query1[] =
1296 {'S','E','L','E','C','T',' ',
1297 '`','C','o','m','p','o','n','e','n','t','_','`',
1298 ' ','F','R','O','M',' ','`','F','e','a','t','u','r','e',
1299 'C','o','m','p','o','n','e','n','t','s','`',' ',
1300 'W','H','E','R','E',' ',
1301 '`','F','e', 'a','t','u','r','e','_','`',' ','=','\'','%','s','\'',0};
1302 MSIQUERY * view;
1303 UINT rc;
1304 _ilfs ilfs;
1306 /* fill in the data */
1308 feature = msi_alloc_zero( sizeof (MSIFEATURE) );
1309 if (!feature)
1310 return ERROR_NOT_ENOUGH_MEMORY;
1312 list_init( &feature->Children );
1313 list_init( &feature->Components );
1315 feature->Feature = msi_dup_record_field( row, 1 );
1317 TRACE("Loading feature %s\n",debugstr_w(feature->Feature));
1319 feature->Feature_Parent = msi_dup_record_field( row, 2 );
1320 feature->Title = msi_dup_record_field( row, 3 );
1321 feature->Description = msi_dup_record_field( row, 4 );
1323 if (!MSI_RecordIsNull(row,5))
1324 feature->Display = MSI_RecordGetInteger(row,5);
1326 feature->Level= MSI_RecordGetInteger(row,6);
1327 feature->Directory = msi_dup_record_field( row, 7 );
1328 feature->Attributes = MSI_RecordGetInteger(row,8);
1330 feature->Installed = INSTALLSTATE_UNKNOWN;
1331 msi_feature_set_state(package, feature, INSTALLSTATE_UNKNOWN);
1333 list_add_tail( &package->features, &feature->entry );
1335 /* load feature components */
1337 rc = MSI_OpenQuery( package->db, &view, Query1, feature->Feature );
1338 if (rc != ERROR_SUCCESS)
1339 return ERROR_SUCCESS;
1341 ilfs.package = package;
1342 ilfs.feature = feature;
1344 MSI_IterateRecords(view, NULL, iterate_load_featurecomponents , &ilfs);
1345 msiobj_release(&view->hdr);
1347 return ERROR_SUCCESS;
1350 static UINT find_feature_children(MSIRECORD * row, LPVOID param)
1352 MSIPACKAGE* package = (MSIPACKAGE*)param;
1353 MSIFEATURE *parent, *child;
1355 child = find_feature_by_name( package, MSI_RecordGetString( row, 1 ) );
1356 if (!child)
1357 return ERROR_FUNCTION_FAILED;
1359 if (!child->Feature_Parent)
1360 return ERROR_SUCCESS;
1362 parent = find_feature_by_name( package, child->Feature_Parent );
1363 if (!parent)
1364 return ERROR_FUNCTION_FAILED;
1366 add_feature_child( parent, child );
1367 return ERROR_SUCCESS;
1370 static UINT load_all_features( MSIPACKAGE *package )
1372 static const WCHAR query[] = {
1373 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1374 '`','F','e','a','t','u','r','e','`',' ','O','R','D','E','R',
1375 ' ','B','Y',' ','`','D','i','s','p','l','a','y','`',0};
1376 MSIQUERY *view;
1377 UINT r;
1379 if (!list_empty(&package->features))
1380 return ERROR_SUCCESS;
1382 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1383 if (r != ERROR_SUCCESS)
1384 return r;
1386 r = MSI_IterateRecords( view, NULL, load_feature, package );
1387 if (r != ERROR_SUCCESS)
1388 return r;
1390 r = MSI_IterateRecords( view, NULL, find_feature_children, package );
1391 msiobj_release( &view->hdr );
1393 return r;
1396 static LPWSTR folder_split_path(LPWSTR p, WCHAR ch)
1398 if (!p)
1399 return p;
1400 p = strchrW(p, ch);
1401 if (!p)
1402 return p;
1403 *p = 0;
1404 return p+1;
1407 static UINT load_file_hash(MSIPACKAGE *package, MSIFILE *file)
1409 static const WCHAR query[] = {
1410 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1411 '`','M','s','i','F','i','l','e','H','a','s','h','`',' ',
1412 'W','H','E','R','E',' ','`','F','i','l','e','_','`',' ','=',' ','\'','%','s','\'',0};
1413 MSIQUERY *view = NULL;
1414 MSIRECORD *row = NULL;
1415 UINT r;
1417 TRACE("%s\n", debugstr_w(file->File));
1419 r = MSI_OpenQuery(package->db, &view, query, file->File);
1420 if (r != ERROR_SUCCESS)
1421 goto done;
1423 r = MSI_ViewExecute(view, NULL);
1424 if (r != ERROR_SUCCESS)
1425 goto done;
1427 r = MSI_ViewFetch(view, &row);
1428 if (r != ERROR_SUCCESS)
1429 goto done;
1431 file->hash.dwFileHashInfoSize = sizeof(MSIFILEHASHINFO);
1432 file->hash.dwData[0] = MSI_RecordGetInteger(row, 3);
1433 file->hash.dwData[1] = MSI_RecordGetInteger(row, 4);
1434 file->hash.dwData[2] = MSI_RecordGetInteger(row, 5);
1435 file->hash.dwData[3] = MSI_RecordGetInteger(row, 6);
1437 done:
1438 if (view) msiobj_release(&view->hdr);
1439 if (row) msiobj_release(&row->hdr);
1440 return r;
1443 static UINT load_file(MSIRECORD *row, LPVOID param)
1445 MSIPACKAGE* package = (MSIPACKAGE*)param;
1446 LPCWSTR component;
1447 MSIFILE *file;
1449 /* fill in the data */
1451 file = msi_alloc_zero( sizeof (MSIFILE) );
1452 if (!file)
1453 return ERROR_NOT_ENOUGH_MEMORY;
1455 file->File = msi_dup_record_field( row, 1 );
1457 component = MSI_RecordGetString( row, 2 );
1458 file->Component = get_loaded_component( package, component );
1460 if (!file->Component)
1462 WARN("Component not found: %s\n", debugstr_w(component));
1463 msi_free(file->File);
1464 msi_free(file);
1465 return ERROR_SUCCESS;
1468 file->FileName = msi_dup_record_field( row, 3 );
1469 reduce_to_longfilename( file->FileName );
1471 file->ShortName = msi_dup_record_field( row, 3 );
1472 file->LongName = strdupW( folder_split_path(file->ShortName, '|'));
1474 file->FileSize = MSI_RecordGetInteger( row, 4 );
1475 file->Version = msi_dup_record_field( row, 5 );
1476 file->Language = msi_dup_record_field( row, 6 );
1477 file->Attributes = MSI_RecordGetInteger( row, 7 );
1478 file->Sequence = MSI_RecordGetInteger( row, 8 );
1480 file->state = msifs_invalid;
1482 /* if the compressed bits are not set in the file attributes,
1483 * then read the information from the package word count property
1485 if (package->WordCount & msidbSumInfoSourceTypeAdminImage)
1487 file->IsCompressed = FALSE;
1489 else if (file->Attributes &
1490 (msidbFileAttributesCompressed | msidbFileAttributesPatchAdded))
1492 file->IsCompressed = TRUE;
1494 else if (file->Attributes & msidbFileAttributesNoncompressed)
1496 file->IsCompressed = FALSE;
1498 else
1500 file->IsCompressed = package->WordCount & msidbSumInfoSourceTypeCompressed;
1503 load_file_hash(package, file);
1505 TRACE("File Loaded (%s)\n",debugstr_w(file->File));
1507 list_add_tail( &package->files, &file->entry );
1509 return ERROR_SUCCESS;
1512 static UINT load_all_files(MSIPACKAGE *package)
1514 MSIQUERY * view;
1515 UINT rc;
1516 static const WCHAR Query[] =
1517 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
1518 '`','F','i','l','e','`',' ', 'O','R','D','E','R',' ','B','Y',' ',
1519 '`','S','e','q','u','e','n','c','e','`', 0};
1521 if (!list_empty(&package->files))
1522 return ERROR_SUCCESS;
1524 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
1525 if (rc != ERROR_SUCCESS)
1526 return ERROR_SUCCESS;
1528 rc = MSI_IterateRecords(view, NULL, load_file, package);
1529 msiobj_release(&view->hdr);
1531 return ERROR_SUCCESS;
1534 static UINT load_folder( MSIRECORD *row, LPVOID param )
1536 MSIPACKAGE *package = param;
1537 static const WCHAR szDot[] = { '.',0 };
1538 static WCHAR szEmpty[] = { 0 };
1539 LPWSTR p, tgt_short, tgt_long, src_short, src_long;
1540 MSIFOLDER *folder;
1542 folder = msi_alloc_zero( sizeof (MSIFOLDER) );
1543 if (!folder)
1544 return ERROR_NOT_ENOUGH_MEMORY;
1546 folder->Directory = msi_dup_record_field( row, 1 );
1548 TRACE("%s\n", debugstr_w(folder->Directory));
1550 p = msi_dup_record_field(row, 3);
1552 /* split src and target dir */
1553 tgt_short = p;
1554 src_short = folder_split_path( p, ':' );
1556 /* split the long and short paths */
1557 tgt_long = folder_split_path( tgt_short, '|' );
1558 src_long = folder_split_path( src_short, '|' );
1560 /* check for no-op dirs */
1561 if (!lstrcmpW(szDot, tgt_short))
1562 tgt_short = szEmpty;
1563 if (!lstrcmpW(szDot, src_short))
1564 src_short = szEmpty;
1566 if (!tgt_long)
1567 tgt_long = tgt_short;
1569 if (!src_short) {
1570 src_short = tgt_short;
1571 src_long = tgt_long;
1574 if (!src_long)
1575 src_long = src_short;
1577 /* FIXME: use the target short path too */
1578 folder->TargetDefault = strdupW(tgt_long);
1579 folder->SourceShortPath = strdupW(src_short);
1580 folder->SourceLongPath = strdupW(src_long);
1581 msi_free(p);
1583 TRACE("TargetDefault = %s\n",debugstr_w( folder->TargetDefault ));
1584 TRACE("SourceLong = %s\n", debugstr_w( folder->SourceLongPath ));
1585 TRACE("SourceShort = %s\n", debugstr_w( folder->SourceShortPath ));
1587 folder->Parent = msi_dup_record_field( row, 2 );
1589 folder->Property = msi_dup_property( package, folder->Directory );
1591 list_add_tail( &package->folders, &folder->entry );
1593 TRACE("returning %p\n", folder);
1595 return ERROR_SUCCESS;
1598 static UINT load_all_folders( MSIPACKAGE *package )
1600 static const WCHAR query[] = {
1601 'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
1602 '`','D','i','r','e','c','t','o','r','y','`',0 };
1603 MSIQUERY *view;
1604 UINT r;
1606 if (!list_empty(&package->folders))
1607 return ERROR_SUCCESS;
1609 r = MSI_DatabaseOpenViewW( package->db, query, &view );
1610 if (r != ERROR_SUCCESS)
1611 return r;
1613 r = MSI_IterateRecords(view, NULL, load_folder, package);
1614 msiobj_release(&view->hdr);
1615 return r;
1619 * I am not doing any of the costing functionality yet.
1620 * Mostly looking at doing the Component and Feature loading
1622 * The native MSI does A LOT of modification to tables here. Mostly adding
1623 * a lot of temporary columns to the Feature and Component tables.
1625 * note: Native msi also tracks the short filename. But I am only going to
1626 * track the long ones. Also looking at this directory table
1627 * it appears that the directory table does not get the parents
1628 * resolved base on property only based on their entries in the
1629 * directory table.
1631 static UINT ACTION_CostInitialize(MSIPACKAGE *package)
1633 static const WCHAR szCosting[] =
1634 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
1635 static const WCHAR szZero[] = { '0', 0 };
1637 MSI_SetPropertyW(package, szCosting, szZero);
1638 MSI_SetPropertyW(package, cszRootDrive, c_colon);
1640 load_all_folders( package );
1641 load_all_components( package );
1642 load_all_features( package );
1643 load_all_files( package );
1645 return ERROR_SUCCESS;
1648 static UINT execute_script(MSIPACKAGE *package, UINT script )
1650 UINT i;
1651 UINT rc = ERROR_SUCCESS;
1653 TRACE("Executing Script %i\n",script);
1655 if (!package->script)
1657 ERR("no script!\n");
1658 return ERROR_FUNCTION_FAILED;
1661 for (i = 0; i < package->script->ActionCount[script]; i++)
1663 LPWSTR action;
1664 action = package->script->Actions[script][i];
1665 ui_actionstart(package, action);
1666 TRACE("Executing Action (%s)\n",debugstr_w(action));
1667 rc = ACTION_PerformAction(package, action, script, TRUE);
1668 if (rc != ERROR_SUCCESS)
1669 break;
1671 msi_free_action_script(package, script);
1672 return rc;
1675 static UINT ACTION_FileCost(MSIPACKAGE *package)
1677 return ERROR_SUCCESS;
1680 static void ACTION_GetComponentInstallStates(MSIPACKAGE *package)
1682 MSICOMPONENT *comp;
1683 INSTALLSTATE state;
1684 UINT r;
1686 state = MsiQueryProductStateW(package->ProductCode);
1688 LIST_FOR_EACH_ENTRY(comp, &package->components, MSICOMPONENT, entry)
1690 if (!comp->ComponentId)
1691 continue;
1693 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1694 comp->Installed = INSTALLSTATE_ABSENT;
1695 else
1697 r = MsiQueryComponentStateW(package->ProductCode, NULL,
1698 package->Context, comp->ComponentId,
1699 &comp->Installed);
1700 if (r != ERROR_SUCCESS)
1701 comp->Installed = INSTALLSTATE_ABSENT;
1706 static void ACTION_GetFeatureInstallStates(MSIPACKAGE *package)
1708 MSIFEATURE *feature;
1709 INSTALLSTATE state;
1711 state = MsiQueryProductStateW(package->ProductCode);
1713 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1715 if (state != INSTALLSTATE_LOCAL && state != INSTALLSTATE_DEFAULT)
1716 feature->Installed = INSTALLSTATE_ABSENT;
1717 else
1719 feature->Installed = MsiQueryFeatureStateW(package->ProductCode,
1720 feature->Feature);
1725 static BOOL process_state_property(MSIPACKAGE* package, int level,
1726 LPCWSTR property, INSTALLSTATE state)
1728 static const WCHAR all[]={'A','L','L',0};
1729 static const WCHAR remove[] = {'R','E','M','O','V','E',0};
1730 LPWSTR override;
1731 MSIFEATURE *feature;
1733 override = msi_dup_property( package, property );
1734 if (!override)
1735 return FALSE;
1737 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1739 if (lstrcmpW(property, remove) &&
1740 (feature->Level <= 0 || feature->Level > level))
1741 continue;
1743 if (strcmpiW(override,all)==0)
1744 msi_feature_set_state(package, feature, state);
1745 else
1747 LPWSTR ptr = override;
1748 LPWSTR ptr2 = strchrW(override,',');
1750 while (ptr)
1752 if ((ptr2 && strncmpW(ptr,feature->Feature, ptr2-ptr)==0)
1753 || (!ptr2 && strcmpW(ptr,feature->Feature)==0))
1755 msi_feature_set_state(package, feature, state);
1756 break;
1758 if (ptr2)
1760 ptr=ptr2+1;
1761 ptr2 = strchrW(ptr,',');
1763 else
1764 break;
1768 msi_free(override);
1770 return TRUE;
1773 UINT MSI_SetFeatureStates(MSIPACKAGE *package)
1775 int level;
1776 static const WCHAR szlevel[] =
1777 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
1778 static const WCHAR szAddLocal[] =
1779 {'A','D','D','L','O','C','A','L',0};
1780 static const WCHAR szAddSource[] =
1781 {'A','D','D','S','O','U','R','C','E',0};
1782 static const WCHAR szRemove[] =
1783 {'R','E','M','O','V','E',0};
1784 static const WCHAR szReinstall[] =
1785 {'R','E','I','N','S','T','A','L','L',0};
1786 BOOL override = FALSE;
1787 MSICOMPONENT* component;
1788 MSIFEATURE *feature;
1791 /* I do not know if this is where it should happen.. but */
1793 TRACE("Checking Install Level\n");
1795 level = msi_get_property_int(package, szlevel, 1);
1797 /* ok here is the _real_ rub
1798 * all these activation/deactivation things happen in order and things
1799 * later on the list override things earlier on the list.
1800 * 1) INSTALLLEVEL processing
1801 * 2) ADDLOCAL
1802 * 3) REMOVE
1803 * 4) ADDSOURCE
1804 * 5) ADDDEFAULT
1805 * 6) REINSTALL
1806 * 7) COMPADDLOCAL
1807 * 8) COMPADDSOURCE
1808 * 9) FILEADDLOCAL
1809 * 10) FILEADDSOURCE
1810 * 11) FILEADDDEFAULT
1812 * I am still ignoring a lot of these. But that is ok for now, ADDLOCAL and
1813 * REMOVE are the big ones, since we don't handle administrative installs
1814 * yet anyway.
1816 override |= process_state_property(package, level, szAddLocal, INSTALLSTATE_LOCAL);
1817 override |= process_state_property(package, level, szRemove, INSTALLSTATE_ABSENT);
1818 override |= process_state_property(package, level, szAddSource, INSTALLSTATE_SOURCE);
1819 override |= process_state_property(package, level, szReinstall, INSTALLSTATE_LOCAL);
1821 if (!override)
1823 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1825 BOOL feature_state = ((feature->Level > 0) &&
1826 (feature->Level <= level));
1828 if ((feature_state) && (feature->Action == INSTALLSTATE_UNKNOWN))
1830 if (feature->Attributes & msidbFeatureAttributesFavorSource)
1831 msi_feature_set_state(package, feature, INSTALLSTATE_SOURCE);
1832 else if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1833 msi_feature_set_state(package, feature, INSTALLSTATE_ADVERTISED);
1834 else
1835 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1839 /* disable child features of unselected parent features */
1840 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1842 FeatureList *fl;
1844 if (feature->Level > 0 && feature->Level <= level)
1845 continue;
1847 LIST_FOR_EACH_ENTRY( fl, &feature->Children, FeatureList, entry )
1848 msi_feature_set_state(package, fl->feature, INSTALLSTATE_UNKNOWN);
1851 else
1853 /* set the Preselected Property */
1854 static const WCHAR szPreselected[] = {'P','r','e','s','e','l','e','c','t','e','d',0};
1855 static const WCHAR szOne[] = { '1', 0 };
1857 MSI_SetPropertyW(package,szPreselected,szOne);
1861 * now we want to enable or disable components base on feature
1864 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
1866 ComponentList *cl;
1868 TRACE("Examining Feature %s (Level %i, Installed %i, Action %i)\n",
1869 debugstr_w(feature->Feature), feature->Level, feature->Installed, feature->Action);
1871 if (!feature->Level)
1872 continue;
1874 /* features with components that have compressed files are made local */
1875 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1877 if (cl->component->Enabled &&
1878 cl->component->ForceLocalState &&
1879 feature->Action == INSTALLSTATE_SOURCE)
1881 msi_feature_set_state(package, feature, INSTALLSTATE_LOCAL);
1882 break;
1886 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
1888 component = cl->component;
1890 if (!component->Enabled)
1891 continue;
1893 switch (feature->Action)
1895 case INSTALLSTATE_ABSENT:
1896 component->anyAbsent = 1;
1897 break;
1898 case INSTALLSTATE_ADVERTISED:
1899 component->hasAdvertiseFeature = 1;
1900 break;
1901 case INSTALLSTATE_SOURCE:
1902 component->hasSourceFeature = 1;
1903 break;
1904 case INSTALLSTATE_LOCAL:
1905 component->hasLocalFeature = 1;
1906 break;
1907 case INSTALLSTATE_DEFAULT:
1908 if (feature->Attributes & msidbFeatureAttributesFavorAdvertise)
1909 component->hasAdvertiseFeature = 1;
1910 else if (feature->Attributes & msidbFeatureAttributesFavorSource)
1911 component->hasSourceFeature = 1;
1912 else
1913 component->hasLocalFeature = 1;
1914 break;
1915 default:
1916 break;
1921 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1923 /* if the component isn't enabled, leave it alone */
1924 if (!component->Enabled)
1925 continue;
1927 /* check if it's local or source */
1928 if (!(component->Attributes & msidbComponentAttributesOptional) &&
1929 (component->hasLocalFeature || component->hasSourceFeature))
1931 if ((component->Attributes & msidbComponentAttributesSourceOnly) &&
1932 !component->ForceLocalState)
1933 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1934 else
1935 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1936 continue;
1939 /* if any feature is local, the component must be local too */
1940 if (component->hasLocalFeature)
1942 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1943 continue;
1946 if (component->hasSourceFeature)
1948 msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
1949 continue;
1952 if (component->hasAdvertiseFeature)
1954 msi_component_set_state(package, component, INSTALLSTATE_ADVERTISED);
1955 continue;
1958 TRACE("nobody wants component %s\n", debugstr_w(component->Component));
1959 if (component->anyAbsent)
1960 msi_component_set_state(package, component, INSTALLSTATE_ABSENT);
1963 LIST_FOR_EACH_ENTRY( component, &package->components, MSICOMPONENT, entry )
1965 if (component->Action == INSTALLSTATE_DEFAULT)
1967 TRACE("%s was default, setting to local\n", debugstr_w(component->Component));
1968 msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
1971 TRACE("Result: Component %s (Installed %i, Action %i)\n",
1972 debugstr_w(component->Component), component->Installed, component->Action);
1976 return ERROR_SUCCESS;
1979 static UINT ITERATE_CostFinalizeDirectories(MSIRECORD *row, LPVOID param)
1981 MSIPACKAGE *package = (MSIPACKAGE*)param;
1982 LPCWSTR name;
1983 LPWSTR path;
1984 MSIFOLDER *f;
1986 name = MSI_RecordGetString(row,1);
1988 f = get_loaded_folder(package, name);
1989 if (!f) return ERROR_SUCCESS;
1991 /* reset the ResolvedTarget */
1992 msi_free(f->ResolvedTarget);
1993 f->ResolvedTarget = NULL;
1995 /* This helper function now does ALL the work */
1996 TRACE("Dir %s ...\n",debugstr_w(name));
1997 path = resolve_folder(package,name,FALSE,TRUE,TRUE,NULL);
1998 TRACE("resolves to %s\n",debugstr_w(path));
1999 msi_free(path);
2001 return ERROR_SUCCESS;
2004 static UINT ITERATE_CostFinalizeConditions(MSIRECORD *row, LPVOID param)
2006 MSIPACKAGE *package = (MSIPACKAGE*)param;
2007 LPCWSTR name;
2008 MSIFEATURE *feature;
2010 name = MSI_RecordGetString( row, 1 );
2012 feature = get_loaded_feature( package, name );
2013 if (!feature)
2014 ERR("FAILED to find loaded feature %s\n",debugstr_w(name));
2015 else
2017 LPCWSTR Condition;
2018 Condition = MSI_RecordGetString(row,3);
2020 if (MSI_EvaluateConditionW(package,Condition) == MSICONDITION_TRUE)
2022 int level = MSI_RecordGetInteger(row,2);
2023 TRACE("Resetting feature %s to level %i\n", debugstr_w(name), level);
2024 feature->Level = level;
2027 return ERROR_SUCCESS;
2030 static LPWSTR msi_get_disk_file_version( LPCWSTR filename )
2032 static const WCHAR name_fmt[] =
2033 {'%','u','.','%','u','.','%','u','.','%','u',0};
2034 static const WCHAR name[] = {'\\',0};
2035 VS_FIXEDFILEINFO *lpVer;
2036 WCHAR filever[0x100];
2037 LPVOID version;
2038 DWORD versize;
2039 DWORD handle;
2040 UINT sz;
2042 TRACE("%s\n", debugstr_w(filename));
2044 versize = GetFileVersionInfoSizeW( filename, &handle );
2045 if (!versize)
2046 return NULL;
2048 version = msi_alloc( versize );
2049 GetFileVersionInfoW( filename, 0, versize, version );
2051 if (!VerQueryValueW( version, name, (LPVOID*)&lpVer, &sz ))
2053 msi_free( version );
2054 return NULL;
2057 sprintfW( filever, name_fmt,
2058 HIWORD(lpVer->dwFileVersionMS),
2059 LOWORD(lpVer->dwFileVersionMS),
2060 HIWORD(lpVer->dwFileVersionLS),
2061 LOWORD(lpVer->dwFileVersionLS));
2063 msi_free( version );
2065 return strdupW( filever );
2068 static UINT msi_check_file_install_states( MSIPACKAGE *package )
2070 LPWSTR file_version;
2071 MSIFILE *file;
2073 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2075 MSICOMPONENT* comp = file->Component;
2076 LPWSTR p;
2078 if (!comp)
2079 continue;
2081 if (file->IsCompressed)
2082 comp->ForceLocalState = TRUE;
2084 /* calculate target */
2085 p = resolve_folder(package, comp->Directory, FALSE, FALSE, TRUE, NULL);
2087 msi_free(file->TargetPath);
2089 TRACE("file %s is named %s\n",
2090 debugstr_w(file->File), debugstr_w(file->FileName));
2092 file->TargetPath = build_directory_name(2, p, file->FileName);
2094 msi_free(p);
2096 TRACE("file %s resolves to %s\n",
2097 debugstr_w(file->File), debugstr_w(file->TargetPath));
2099 /* don't check files of components that aren't installed */
2100 if (comp->Installed == INSTALLSTATE_UNKNOWN ||
2101 comp->Installed == INSTALLSTATE_ABSENT)
2103 file->state = msifs_missing; /* assume files are missing */
2104 continue;
2107 if (GetFileAttributesW(file->TargetPath) == INVALID_FILE_ATTRIBUTES)
2109 file->state = msifs_missing;
2110 comp->Cost += file->FileSize;
2111 continue;
2114 if (file->Version &&
2115 (file_version = msi_get_disk_file_version( file->TargetPath )))
2117 TRACE("new %s old %s\n", debugstr_w(file->Version),
2118 debugstr_w(file_version));
2119 /* FIXME: seems like a bad way to compare version numbers */
2120 if (lstrcmpiW(file_version, file->Version)<0)
2122 file->state = msifs_overwrite;
2123 comp->Cost += file->FileSize;
2125 else
2126 file->state = msifs_present;
2127 msi_free( file_version );
2129 else
2130 file->state = msifs_present;
2133 return ERROR_SUCCESS;
2137 * A lot is done in this function aside from just the costing.
2138 * The costing needs to be implemented at some point but for now I am going
2139 * to focus on the directory building
2142 static UINT ACTION_CostFinalize(MSIPACKAGE *package)
2144 static const WCHAR ExecSeqQuery[] =
2145 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2146 '`','D','i','r','e','c','t','o','r','y','`',0};
2147 static const WCHAR ConditionQuery[] =
2148 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2149 '`','C','o','n','d','i','t','i','o','n','`',0};
2150 static const WCHAR szCosting[] =
2151 {'C','o','s','t','i','n','g','C','o','m','p','l','e','t','e',0 };
2152 static const WCHAR szlevel[] =
2153 {'I','N','S','T','A','L','L','L','E','V','E','L',0};
2154 static const WCHAR szOutOfDiskSpace[] =
2155 {'O','u','t','O','f','D','i','s','k','S','p','a','c','e',0};
2156 static const WCHAR szOne[] = { '1', 0 };
2157 static const WCHAR szZero[] = { '0', 0 };
2158 MSICOMPONENT *comp;
2159 UINT rc;
2160 MSIQUERY * view;
2161 LPWSTR level;
2163 TRACE("Building Directory properties\n");
2165 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2166 if (rc == ERROR_SUCCESS)
2168 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeDirectories,
2169 package);
2170 msiobj_release(&view->hdr);
2173 /* read components states from the registry */
2174 ACTION_GetComponentInstallStates(package);
2175 ACTION_GetFeatureInstallStates(package);
2177 TRACE("File calculations\n");
2178 msi_check_file_install_states( package );
2180 TRACE("Evaluating Condition Table\n");
2182 rc = MSI_DatabaseOpenViewW(package->db, ConditionQuery, &view);
2183 if (rc == ERROR_SUCCESS)
2185 rc = MSI_IterateRecords(view, NULL, ITERATE_CostFinalizeConditions,
2186 package);
2187 msiobj_release(&view->hdr);
2190 TRACE("Enabling or Disabling Components\n");
2191 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2193 if (MSI_EvaluateConditionW(package, comp->Condition) == MSICONDITION_FALSE)
2195 TRACE("Disabling component %s\n", debugstr_w(comp->Component));
2196 comp->Enabled = FALSE;
2198 else
2199 comp->Enabled = TRUE;
2202 MSI_SetPropertyW(package,szCosting,szOne);
2203 /* set default run level if not set */
2204 level = msi_dup_property( package, szlevel );
2205 if (!level)
2206 MSI_SetPropertyW(package,szlevel, szOne);
2207 msi_free(level);
2209 /* FIXME: check volume disk space */
2210 MSI_SetPropertyW(package, szOutOfDiskSpace, szZero);
2212 return MSI_SetFeatureStates(package);
2215 /* OK this value is "interpreted" and then formatted based on the
2216 first few characters */
2217 static LPSTR parse_value(MSIPACKAGE *package, LPCWSTR value, DWORD *type,
2218 DWORD *size)
2220 LPSTR data = NULL;
2222 if (value[0]=='#' && value[1]!='#' && value[1]!='%')
2224 if (value[1]=='x')
2226 LPWSTR ptr;
2227 CHAR byte[5];
2228 LPWSTR deformated = NULL;
2229 int count;
2231 deformat_string(package, &value[2], &deformated);
2233 /* binary value type */
2234 ptr = deformated;
2235 *type = REG_BINARY;
2236 if (strlenW(ptr)%2)
2237 *size = (strlenW(ptr)/2)+1;
2238 else
2239 *size = strlenW(ptr)/2;
2241 data = msi_alloc(*size);
2243 byte[0] = '0';
2244 byte[1] = 'x';
2245 byte[4] = 0;
2246 count = 0;
2247 /* if uneven pad with a zero in front */
2248 if (strlenW(ptr)%2)
2250 byte[2]= '0';
2251 byte[3]= *ptr;
2252 ptr++;
2253 data[count] = (BYTE)strtol(byte,NULL,0);
2254 count ++;
2255 TRACE("Uneven byte count\n");
2257 while (*ptr)
2259 byte[2]= *ptr;
2260 ptr++;
2261 byte[3]= *ptr;
2262 ptr++;
2263 data[count] = (BYTE)strtol(byte,NULL,0);
2264 count ++;
2266 msi_free(deformated);
2268 TRACE("Data %i bytes(%i)\n",*size,count);
2270 else
2272 LPWSTR deformated;
2273 LPWSTR p;
2274 DWORD d = 0;
2275 deformat_string(package, &value[1], &deformated);
2277 *type=REG_DWORD;
2278 *size = sizeof(DWORD);
2279 data = msi_alloc(*size);
2280 p = deformated;
2281 if (*p == '-')
2282 p++;
2283 while (*p)
2285 if ( (*p < '0') || (*p > '9') )
2286 break;
2287 d *= 10;
2288 d += (*p - '0');
2289 p++;
2291 if (deformated[0] == '-')
2292 d = -d;
2293 *(LPDWORD)data = d;
2294 TRACE("DWORD %i\n",*(LPDWORD)data);
2296 msi_free(deformated);
2299 else
2301 static const WCHAR szMulti[] = {'[','~',']',0};
2302 LPCWSTR ptr;
2303 *type=REG_SZ;
2305 if (value[0]=='#')
2307 if (value[1]=='%')
2309 ptr = &value[2];
2310 *type=REG_EXPAND_SZ;
2312 else
2313 ptr = &value[1];
2315 else
2316 ptr=value;
2318 if (strstrW(value,szMulti))
2319 *type = REG_MULTI_SZ;
2321 /* remove initial delimiter */
2322 if (!strncmpW(value, szMulti, 3))
2323 ptr = value + 3;
2325 *size = deformat_string(package, ptr,(LPWSTR*)&data);
2327 /* add double NULL terminator */
2328 if (*type == REG_MULTI_SZ)
2330 *size += 2 * sizeof(WCHAR); /* two NULL terminators */
2331 data = msi_realloc_zero(data, *size);
2334 return data;
2337 static UINT ITERATE_WriteRegistryValues(MSIRECORD *row, LPVOID param)
2339 MSIPACKAGE *package = (MSIPACKAGE*)param;
2340 static const WCHAR szHCR[] =
2341 {'H','K','E','Y','_','C','L','A','S','S','E','S','_',
2342 'R','O','O','T','\\',0};
2343 static const WCHAR szHCU[] =
2344 {'H','K','E','Y','_','C','U','R','R','E','N','T','_',
2345 'U','S','E','R','\\',0};
2346 static const WCHAR szHLM[] =
2347 {'H','K','E','Y','_','L','O','C','A','L','_',
2348 'M','A','C','H','I','N','E','\\',0};
2349 static const WCHAR szHU[] =
2350 {'H','K','E','Y','_','U','S','E','R','S','\\',0};
2352 LPSTR value_data = NULL;
2353 HKEY root_key, hkey;
2354 DWORD type,size;
2355 LPWSTR deformated;
2356 LPCWSTR szRoot, component, name, key, value;
2357 MSICOMPONENT *comp;
2358 MSIRECORD * uirow;
2359 LPWSTR uikey;
2360 INT root;
2361 BOOL check_first = FALSE;
2362 UINT rc;
2364 ui_progress(package,2,0,0,0);
2366 value = NULL;
2367 key = NULL;
2368 uikey = NULL;
2369 name = NULL;
2371 component = MSI_RecordGetString(row, 6);
2372 comp = get_loaded_component(package,component);
2373 if (!comp)
2374 return ERROR_SUCCESS;
2376 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
2378 TRACE("Skipping write due to disabled component %s\n",
2379 debugstr_w(component));
2381 comp->Action = comp->Installed;
2383 return ERROR_SUCCESS;
2386 comp->Action = INSTALLSTATE_LOCAL;
2388 name = MSI_RecordGetString(row, 4);
2389 if( MSI_RecordIsNull(row,5) && name )
2391 /* null values can have special meanings */
2392 if (name[0]=='-' && name[1] == 0)
2393 return ERROR_SUCCESS;
2394 else if ((name[0]=='+' && name[1] == 0) ||
2395 (name[0] == '*' && name[1] == 0))
2396 name = NULL;
2397 check_first = TRUE;
2400 root = MSI_RecordGetInteger(row,2);
2401 key = MSI_RecordGetString(row, 3);
2403 /* get the root key */
2404 switch (root)
2406 case -1:
2408 static const WCHAR szALLUSER[] = {'A','L','L','U','S','E','R','S',0};
2409 LPWSTR all_users = msi_dup_property( package, szALLUSER );
2410 if (all_users && all_users[0] == '1')
2412 root_key = HKEY_LOCAL_MACHINE;
2413 szRoot = szHLM;
2415 else
2417 root_key = HKEY_CURRENT_USER;
2418 szRoot = szHCU;
2420 msi_free(all_users);
2422 break;
2423 case 0: root_key = HKEY_CLASSES_ROOT;
2424 szRoot = szHCR;
2425 break;
2426 case 1: root_key = HKEY_CURRENT_USER;
2427 szRoot = szHCU;
2428 break;
2429 case 2: root_key = HKEY_LOCAL_MACHINE;
2430 szRoot = szHLM;
2431 break;
2432 case 3: root_key = HKEY_USERS;
2433 szRoot = szHU;
2434 break;
2435 default:
2436 ERR("Unknown root %i\n",root);
2437 root_key=NULL;
2438 szRoot = NULL;
2439 break;
2441 if (!root_key)
2442 return ERROR_SUCCESS;
2444 deformat_string(package, key , &deformated);
2445 size = strlenW(deformated) + strlenW(szRoot) + 1;
2446 uikey = msi_alloc(size*sizeof(WCHAR));
2447 strcpyW(uikey,szRoot);
2448 strcatW(uikey,deformated);
2450 if (RegCreateKeyW( root_key, deformated, &hkey))
2452 ERR("Could not create key %s\n",debugstr_w(deformated));
2453 msi_free(deformated);
2454 msi_free(uikey);
2455 return ERROR_SUCCESS;
2457 msi_free(deformated);
2459 value = MSI_RecordGetString(row,5);
2460 if (value)
2461 value_data = parse_value(package, value, &type, &size);
2462 else
2464 static const WCHAR szEmpty[] = {0};
2465 value_data = (LPSTR)strdupW(szEmpty);
2466 size = sizeof(szEmpty);
2467 type = REG_SZ;
2470 deformat_string(package, name, &deformated);
2472 if (!check_first)
2474 TRACE("Setting value %s of %s\n",debugstr_w(deformated),
2475 debugstr_w(uikey));
2476 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE)value_data, size);
2478 else
2480 DWORD sz = 0;
2481 rc = RegQueryValueExW(hkey, deformated, NULL, NULL, NULL, &sz);
2482 if (rc == ERROR_SUCCESS || rc == ERROR_MORE_DATA)
2484 TRACE("value %s of %s checked already exists\n",
2485 debugstr_w(deformated), debugstr_w(uikey));
2487 else
2489 TRACE("Checked and setting value %s of %s\n",
2490 debugstr_w(deformated), debugstr_w(uikey));
2491 if (deformated || size)
2492 RegSetValueExW(hkey, deformated, 0, type, (LPBYTE) value_data, size);
2495 RegCloseKey(hkey);
2497 uirow = MSI_CreateRecord(3);
2498 MSI_RecordSetStringW(uirow,2,deformated);
2499 MSI_RecordSetStringW(uirow,1,uikey);
2501 if (type == REG_SZ)
2502 MSI_RecordSetStringW(uirow,3,(LPWSTR)value_data);
2503 else
2504 MSI_RecordSetStringW(uirow,3,value);
2506 ui_actiondata(package,szWriteRegistryValues,uirow);
2507 msiobj_release( &uirow->hdr );
2509 msi_free(value_data);
2510 msi_free(deformated);
2511 msi_free(uikey);
2513 return ERROR_SUCCESS;
2516 static UINT ACTION_WriteRegistryValues(MSIPACKAGE *package)
2518 UINT rc;
2519 MSIQUERY * view;
2520 static const WCHAR ExecSeqQuery[] =
2521 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2522 '`','R','e','g','i','s','t','r','y','`',0 };
2524 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2525 if (rc != ERROR_SUCCESS)
2526 return ERROR_SUCCESS;
2528 /* increment progress bar each time action data is sent */
2529 ui_progress(package,1,REG_PROGRESS_VALUE,1,0);
2531 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteRegistryValues, package);
2533 msiobj_release(&view->hdr);
2534 return rc;
2537 static UINT ACTION_InstallInitialize(MSIPACKAGE *package)
2539 package->script->CurrentlyScripting = TRUE;
2541 return ERROR_SUCCESS;
2545 static UINT ACTION_InstallValidate(MSIPACKAGE *package)
2547 MSICOMPONENT *comp;
2548 DWORD progress = 0;
2549 DWORD total = 0;
2550 static const WCHAR q1[]=
2551 {'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2552 '`','R','e','g','i','s','t','r','y','`',0};
2553 UINT rc;
2554 MSIQUERY * view;
2555 MSIFEATURE *feature;
2556 MSIFILE *file;
2558 TRACE("InstallValidate\n");
2560 rc = MSI_DatabaseOpenViewW(package->db, q1, &view);
2561 if (rc == ERROR_SUCCESS)
2563 MSI_IterateRecords( view, &progress, NULL, package );
2564 msiobj_release( &view->hdr );
2565 total += progress * REG_PROGRESS_VALUE;
2568 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2569 total += COMPONENT_PROGRESS_VALUE;
2571 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2572 total += file->FileSize;
2574 ui_progress(package,0,total,0,0);
2576 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2578 TRACE("Feature: %s; Installed: %i; Action %i; Request %i\n",
2579 debugstr_w(feature->Feature), feature->Installed, feature->Action,
2580 feature->ActionRequest);
2583 return ERROR_SUCCESS;
2586 static UINT ITERATE_LaunchConditions(MSIRECORD *row, LPVOID param)
2588 MSIPACKAGE* package = (MSIPACKAGE*)param;
2589 LPCWSTR cond = NULL;
2590 LPCWSTR message = NULL;
2591 UINT r;
2593 static const WCHAR title[]=
2594 {'I','n','s','t','a','l','l',' ','F','a', 'i','l','e','d',0};
2596 cond = MSI_RecordGetString(row,1);
2598 r = MSI_EvaluateConditionW(package,cond);
2599 if (r == MSICONDITION_FALSE)
2601 if ((gUILevel & INSTALLUILEVEL_MASK) != INSTALLUILEVEL_NONE)
2603 LPWSTR deformated;
2604 message = MSI_RecordGetString(row,2);
2605 deformat_string(package,message,&deformated);
2606 MessageBoxW(NULL,deformated,title,MB_OK);
2607 msi_free(deformated);
2610 return ERROR_INSTALL_FAILURE;
2613 return ERROR_SUCCESS;
2616 static UINT ACTION_LaunchConditions(MSIPACKAGE *package)
2618 UINT rc;
2619 MSIQUERY * view = NULL;
2620 static const WCHAR ExecSeqQuery[] =
2621 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2622 '`','L','a','u','n','c','h','C','o','n','d','i','t','i','o','n','`',0};
2624 TRACE("Checking launch conditions\n");
2626 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
2627 if (rc != ERROR_SUCCESS)
2628 return ERROR_SUCCESS;
2630 rc = MSI_IterateRecords(view, NULL, ITERATE_LaunchConditions, package);
2631 msiobj_release(&view->hdr);
2633 return rc;
2636 static LPWSTR resolve_keypath( MSIPACKAGE* package, MSICOMPONENT *cmp )
2639 if (!cmp->KeyPath)
2640 return resolve_folder(package,cmp->Directory,FALSE,FALSE,TRUE,NULL);
2642 if (cmp->Attributes & msidbComponentAttributesRegistryKeyPath)
2644 MSIRECORD * row = 0;
2645 UINT root,len;
2646 LPWSTR deformated,buffer,deformated_name;
2647 LPCWSTR key,name;
2648 static const WCHAR ExecSeqQuery[] =
2649 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
2650 '`','R','e','g','i','s','t','r','y','`',' ',
2651 'W','H','E','R','E',' ', '`','R','e','g','i','s','t','r','y','`',
2652 ' ','=',' ' ,'\'','%','s','\'',0 };
2653 static const WCHAR fmt[]={'%','0','2','i',':','\\','%','s','\\',0};
2654 static const WCHAR fmt2[]=
2655 {'%','0','2','i',':','\\','%','s','\\','%','s',0};
2657 row = MSI_QueryGetRecord(package->db, ExecSeqQuery,cmp->KeyPath);
2658 if (!row)
2659 return NULL;
2661 root = MSI_RecordGetInteger(row,2);
2662 key = MSI_RecordGetString(row, 3);
2663 name = MSI_RecordGetString(row, 4);
2664 deformat_string(package, key , &deformated);
2665 deformat_string(package, name, &deformated_name);
2667 len = strlenW(deformated) + 6;
2668 if (deformated_name)
2669 len+=strlenW(deformated_name);
2671 buffer = msi_alloc( len *sizeof(WCHAR));
2673 if (deformated_name)
2674 sprintfW(buffer,fmt2,root,deformated,deformated_name);
2675 else
2676 sprintfW(buffer,fmt,root,deformated);
2678 msi_free(deformated);
2679 msi_free(deformated_name);
2680 msiobj_release(&row->hdr);
2682 return buffer;
2684 else if (cmp->Attributes & msidbComponentAttributesODBCDataSource)
2686 FIXME("UNIMPLEMENTED keypath as ODBC Source\n");
2687 return NULL;
2689 else
2691 MSIFILE *file = get_loaded_file( package, cmp->KeyPath );
2693 if (file)
2694 return strdupW( file->TargetPath );
2696 return NULL;
2699 static HKEY openSharedDLLsKey(void)
2701 HKEY hkey=0;
2702 static const WCHAR path[] =
2703 {'S','o','f','t','w','a','r','e','\\',
2704 'M','i','c','r','o','s','o','f','t','\\',
2705 'W','i','n','d','o','w','s','\\',
2706 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
2707 'S','h','a','r','e','d','D','L','L','s',0};
2709 RegCreateKeyW(HKEY_LOCAL_MACHINE,path,&hkey);
2710 return hkey;
2713 static UINT ACTION_GetSharedDLLsCount(LPCWSTR dll)
2715 HKEY hkey;
2716 DWORD count=0;
2717 DWORD type;
2718 DWORD sz = sizeof(count);
2719 DWORD rc;
2721 hkey = openSharedDLLsKey();
2722 rc = RegQueryValueExW(hkey, dll, NULL, &type, (LPBYTE)&count, &sz);
2723 if (rc != ERROR_SUCCESS)
2724 count = 0;
2725 RegCloseKey(hkey);
2726 return count;
2729 static UINT ACTION_WriteSharedDLLsCount(LPCWSTR path, UINT count)
2731 HKEY hkey;
2733 hkey = openSharedDLLsKey();
2734 if (count > 0)
2735 msi_reg_set_val_dword( hkey, path, count );
2736 else
2737 RegDeleteValueW(hkey,path);
2738 RegCloseKey(hkey);
2739 return count;
2743 * Return TRUE if the count should be written out and FALSE if not
2745 static void ACTION_RefCountComponent( MSIPACKAGE* package, MSICOMPONENT *comp )
2747 MSIFEATURE *feature;
2748 INT count = 0;
2749 BOOL write = FALSE;
2751 /* only refcount DLLs */
2752 if (comp->KeyPath == NULL ||
2753 comp->Attributes & msidbComponentAttributesRegistryKeyPath ||
2754 comp->Attributes & msidbComponentAttributesODBCDataSource)
2755 write = FALSE;
2756 else
2758 count = ACTION_GetSharedDLLsCount( comp->FullKeypath);
2759 write = (count > 0);
2761 if (comp->Attributes & msidbComponentAttributesSharedDllRefCount)
2762 write = TRUE;
2765 /* increment counts */
2766 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2768 ComponentList *cl;
2770 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ))
2771 continue;
2773 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2775 if ( cl->component == comp )
2776 count++;
2780 /* decrement counts */
2781 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
2783 ComponentList *cl;
2785 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ABSENT ))
2786 continue;
2788 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
2790 if ( cl->component == comp )
2791 count--;
2795 /* ref count all the files in the component */
2796 if (write)
2798 MSIFILE *file;
2800 LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
2802 if (file->Component == comp)
2803 ACTION_WriteSharedDLLsCount( file->TargetPath, count );
2807 /* add a count for permanent */
2808 if (comp->Attributes & msidbComponentAttributesPermanent)
2809 count ++;
2811 comp->RefCount = count;
2813 if (write)
2814 ACTION_WriteSharedDLLsCount( comp->FullKeypath, comp->RefCount );
2817 static UINT ACTION_ProcessComponents(MSIPACKAGE *package)
2819 WCHAR squished_pc[GUID_SIZE];
2820 WCHAR squished_cc[GUID_SIZE];
2821 UINT rc;
2822 MSICOMPONENT *comp;
2823 HKEY hkey;
2825 TRACE("\n");
2827 squash_guid(package->ProductCode,squished_pc);
2828 ui_progress(package,1,COMPONENT_PROGRESS_VALUE,1,0);
2830 LIST_FOR_EACH_ENTRY( comp, &package->components, MSICOMPONENT, entry )
2832 MSIRECORD * uirow;
2834 ui_progress(package,2,0,0,0);
2835 if (!comp->ComponentId)
2836 continue;
2838 squash_guid(comp->ComponentId,squished_cc);
2840 msi_free(comp->FullKeypath);
2841 comp->FullKeypath = resolve_keypath( package, comp );
2843 ACTION_RefCountComponent( package, comp );
2845 TRACE("Component %s (%s), Keypath=%s, RefCount=%i\n",
2846 debugstr_w(comp->Component),
2847 debugstr_w(squished_cc),
2848 debugstr_w(comp->FullKeypath),
2849 comp->RefCount);
2851 if (ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL) ||
2852 ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE))
2854 if (!comp->FullKeypath)
2855 continue;
2857 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2858 rc = MSIREG_OpenLocalUserDataComponentKey(comp->ComponentId, &hkey, TRUE);
2859 else
2860 rc = MSIREG_OpenUserDataComponentKey(comp->ComponentId, &hkey, TRUE);
2862 if (rc != ERROR_SUCCESS)
2863 continue;
2865 if (comp->Attributes & msidbComponentAttributesPermanent)
2867 static const WCHAR szPermKey[] =
2868 { '0','0','0','0','0','0','0','0','0','0','0','0',
2869 '0','0','0','0','0','0','0','0','0','0','0','0',
2870 '0','0','0','0','0','0','0','0',0 };
2872 msi_reg_set_val_str(hkey, szPermKey, comp->FullKeypath);
2875 if (comp->Action == INSTALLSTATE_LOCAL)
2876 msi_reg_set_val_str(hkey, squished_pc, comp->FullKeypath);
2877 else
2879 MSIFILE *file;
2880 MSIRECORD *row;
2881 LPWSTR ptr, ptr2;
2882 WCHAR source[MAX_PATH];
2883 WCHAR base[MAX_PATH];
2884 LPWSTR sourcepath;
2886 static const WCHAR fmt[] = {'%','0','2','d','\\',0};
2887 static const WCHAR query[] = {
2888 'S','E','L','E','C','T',' ','*',' ', 'F','R','O','M',' ',
2889 '`','M','e','d','i','a','`',' ','W','H','E','R','E',' ',
2890 '`','L','a','s','t','S','e','q','u','e','n','c','e','`',' ',
2891 '>','=',' ','%','i',' ','O','R','D','E','R',' ','B','Y',' ',
2892 '`','D','i','s','k','I','d','`',0};
2894 file = get_loaded_file(package, comp->KeyPath);
2895 if (!file)
2896 continue;
2898 row = MSI_QueryGetRecord(package->db, query, file->Sequence);
2899 sprintfW(source, fmt, MSI_RecordGetInteger(row, 1));
2900 ptr2 = strrchrW(source, '\\') + 1;
2901 msiobj_release(&row->hdr);
2903 lstrcpyW(base, package->PackagePath);
2904 ptr = strrchrW(base, '\\');
2905 *(ptr + 1) = '\0';
2907 sourcepath = resolve_file_source(package, file);
2908 ptr = sourcepath + lstrlenW(base);
2909 lstrcpyW(ptr2, ptr);
2910 msi_free(sourcepath);
2912 msi_reg_set_val_str(hkey, squished_pc, source);
2914 RegCloseKey(hkey);
2916 else if (ACTION_VerifyComponentForAction(comp, INSTALLSTATE_ABSENT))
2918 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
2919 MSIREG_DeleteLocalUserDataComponentKey(comp->ComponentId);
2920 else
2921 MSIREG_DeleteUserDataComponentKey(comp->ComponentId);
2924 /* UI stuff */
2925 uirow = MSI_CreateRecord(3);
2926 MSI_RecordSetStringW(uirow,1,package->ProductCode);
2927 MSI_RecordSetStringW(uirow,2,comp->ComponentId);
2928 MSI_RecordSetStringW(uirow,3,comp->FullKeypath);
2929 ui_actiondata(package,szProcessComponents,uirow);
2930 msiobj_release( &uirow->hdr );
2933 return ERROR_SUCCESS;
2936 typedef struct {
2937 CLSID clsid;
2938 LPWSTR source;
2940 LPWSTR path;
2941 ITypeLib *ptLib;
2942 } typelib_struct;
2944 static BOOL CALLBACK Typelib_EnumResNameProc( HMODULE hModule, LPCWSTR lpszType,
2945 LPWSTR lpszName, LONG_PTR lParam)
2947 TLIBATTR *attr;
2948 typelib_struct *tl_struct = (typelib_struct*) lParam;
2949 static const WCHAR fmt[] = {'%','s','\\','%','i',0};
2950 int sz;
2951 HRESULT res;
2953 if (!IS_INTRESOURCE(lpszName))
2955 ERR("Not Int Resource Name %s\n",debugstr_w(lpszName));
2956 return TRUE;
2959 sz = strlenW(tl_struct->source)+4;
2960 sz *= sizeof(WCHAR);
2962 if ((INT_PTR)lpszName == 1)
2963 tl_struct->path = strdupW(tl_struct->source);
2964 else
2966 tl_struct->path = msi_alloc(sz);
2967 sprintfW(tl_struct->path,fmt,tl_struct->source, lpszName);
2970 TRACE("trying %s\n", debugstr_w(tl_struct->path));
2971 res = LoadTypeLib(tl_struct->path,&tl_struct->ptLib);
2972 if (FAILED(res))
2974 msi_free(tl_struct->path);
2975 tl_struct->path = NULL;
2977 return TRUE;
2980 ITypeLib_GetLibAttr(tl_struct->ptLib, &attr);
2981 if (IsEqualGUID(&(tl_struct->clsid),&(attr->guid)))
2983 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2984 return FALSE;
2987 msi_free(tl_struct->path);
2988 tl_struct->path = NULL;
2990 ITypeLib_ReleaseTLibAttr(tl_struct->ptLib, attr);
2991 ITypeLib_Release(tl_struct->ptLib);
2993 return TRUE;
2996 static UINT ITERATE_RegisterTypeLibraries(MSIRECORD *row, LPVOID param)
2998 MSIPACKAGE* package = (MSIPACKAGE*)param;
2999 LPCWSTR component;
3000 MSICOMPONENT *comp;
3001 MSIFILE *file;
3002 typelib_struct tl_struct;
3003 ITypeLib *tlib;
3004 HMODULE module;
3005 HRESULT hr;
3007 static const WCHAR szTYPELIB[] = {'T','Y','P','E','L','I','B',0};
3009 component = MSI_RecordGetString(row,3);
3010 comp = get_loaded_component(package,component);
3011 if (!comp)
3012 return ERROR_SUCCESS;
3014 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3016 TRACE("Skipping typelib reg due to disabled component\n");
3018 comp->Action = comp->Installed;
3020 return ERROR_SUCCESS;
3023 comp->Action = INSTALLSTATE_LOCAL;
3025 file = get_loaded_file( package, comp->KeyPath );
3026 if (!file)
3027 return ERROR_SUCCESS;
3029 module = LoadLibraryExW( file->TargetPath, NULL, LOAD_LIBRARY_AS_DATAFILE );
3030 if (module)
3032 LPCWSTR guid;
3033 guid = MSI_RecordGetString(row,1);
3034 CLSIDFromString((LPWSTR)guid, &tl_struct.clsid);
3035 tl_struct.source = strdupW( file->TargetPath );
3036 tl_struct.path = NULL;
3038 EnumResourceNamesW(module, szTYPELIB, Typelib_EnumResNameProc,
3039 (LONG_PTR)&tl_struct);
3041 if (tl_struct.path)
3043 LPWSTR help = NULL;
3044 LPCWSTR helpid;
3045 HRESULT res;
3047 helpid = MSI_RecordGetString(row,6);
3049 if (helpid)
3050 help = resolve_folder(package,helpid,FALSE,FALSE,TRUE,NULL);
3051 res = RegisterTypeLib(tl_struct.ptLib,tl_struct.path,help);
3052 msi_free(help);
3054 if (FAILED(res))
3055 ERR("Failed to register type library %s\n",
3056 debugstr_w(tl_struct.path));
3057 else
3059 ui_actiondata(package,szRegisterTypeLibraries,row);
3061 TRACE("Registered %s\n", debugstr_w(tl_struct.path));
3064 ITypeLib_Release(tl_struct.ptLib);
3065 msi_free(tl_struct.path);
3067 else
3068 ERR("Failed to load type library %s\n",
3069 debugstr_w(tl_struct.source));
3071 FreeLibrary(module);
3072 msi_free(tl_struct.source);
3074 else
3076 hr = LoadTypeLibEx(file->TargetPath, REGKIND_REGISTER, &tlib);
3077 if (FAILED(hr))
3079 ERR("Failed to load type library: %08x\n", hr);
3080 return ERROR_FUNCTION_FAILED;
3083 ITypeLib_Release(tlib);
3086 return ERROR_SUCCESS;
3089 static UINT ACTION_RegisterTypeLibraries(MSIPACKAGE *package)
3092 * OK this is a bit confusing.. I am given a _Component key and I believe
3093 * that the file that is being registered as a type library is the "key file
3094 * of that component" which I interpret to mean "The file in the KeyPath of
3095 * that component".
3097 UINT rc;
3098 MSIQUERY * view;
3099 static const WCHAR Query[] =
3100 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3101 '`','T','y','p','e','L','i','b','`',0};
3103 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3104 if (rc != ERROR_SUCCESS)
3105 return ERROR_SUCCESS;
3107 rc = MSI_IterateRecords(view, NULL, ITERATE_RegisterTypeLibraries, package);
3108 msiobj_release(&view->hdr);
3109 return rc;
3112 static UINT ITERATE_CreateShortcuts(MSIRECORD *row, LPVOID param)
3114 MSIPACKAGE *package = (MSIPACKAGE*)param;
3115 LPWSTR target_file, target_folder, filename;
3116 LPCWSTR buffer, extension;
3117 MSICOMPONENT *comp;
3118 static const WCHAR szlnk[]={'.','l','n','k',0};
3119 IShellLinkW *sl = NULL;
3120 IPersistFile *pf = NULL;
3121 HRESULT res;
3123 buffer = MSI_RecordGetString(row,4);
3124 comp = get_loaded_component(package,buffer);
3125 if (!comp)
3126 return ERROR_SUCCESS;
3128 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ))
3130 TRACE("Skipping shortcut creation due to disabled component\n");
3132 comp->Action = comp->Installed;
3134 return ERROR_SUCCESS;
3137 comp->Action = INSTALLSTATE_LOCAL;
3139 ui_actiondata(package,szCreateShortcuts,row);
3141 res = CoCreateInstance( &CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
3142 &IID_IShellLinkW, (LPVOID *) &sl );
3144 if (FAILED( res ))
3146 ERR("CLSID_ShellLink not available\n");
3147 goto err;
3150 res = IShellLinkW_QueryInterface( sl, &IID_IPersistFile,(LPVOID*) &pf );
3151 if (FAILED( res ))
3153 ERR("QueryInterface(IID_IPersistFile) failed\n");
3154 goto err;
3157 buffer = MSI_RecordGetString(row,2);
3158 target_folder = resolve_folder(package, buffer,FALSE,FALSE,TRUE,NULL);
3160 /* may be needed because of a bug somewhere else */
3161 create_full_pathW(target_folder);
3163 filename = msi_dup_record_field( row, 3 );
3164 reduce_to_longfilename(filename);
3166 extension = strchrW(filename,'.');
3167 if (!extension || strcmpiW(extension,szlnk))
3169 int len = strlenW(filename);
3170 filename = msi_realloc(filename, len * sizeof(WCHAR) + sizeof(szlnk));
3171 memcpy(filename + len, szlnk, sizeof(szlnk));
3173 target_file = build_directory_name(2, target_folder, filename);
3174 msi_free(target_folder);
3175 msi_free(filename);
3177 buffer = MSI_RecordGetString(row,5);
3178 if (strchrW(buffer,'['))
3180 LPWSTR deformated;
3181 deformat_string(package,buffer,&deformated);
3182 IShellLinkW_SetPath(sl,deformated);
3183 msi_free(deformated);
3185 else
3187 FIXME("poorly handled shortcut format, advertised shortcut\n");
3188 IShellLinkW_SetPath(sl,comp->FullKeypath);
3191 if (!MSI_RecordIsNull(row,6))
3193 LPWSTR deformated;
3194 buffer = MSI_RecordGetString(row,6);
3195 deformat_string(package,buffer,&deformated);
3196 IShellLinkW_SetArguments(sl,deformated);
3197 msi_free(deformated);
3200 if (!MSI_RecordIsNull(row,7))
3202 buffer = MSI_RecordGetString(row,7);
3203 IShellLinkW_SetDescription(sl,buffer);
3206 if (!MSI_RecordIsNull(row,8))
3207 IShellLinkW_SetHotkey(sl,MSI_RecordGetInteger(row,8));
3209 if (!MSI_RecordIsNull(row,9))
3211 LPWSTR Path;
3212 INT index;
3214 buffer = MSI_RecordGetString(row,9);
3216 Path = build_icon_path(package,buffer);
3217 index = MSI_RecordGetInteger(row,10);
3219 /* no value means 0 */
3220 if (index == MSI_NULL_INTEGER)
3221 index = 0;
3223 IShellLinkW_SetIconLocation(sl,Path,index);
3224 msi_free(Path);
3227 if (!MSI_RecordIsNull(row,11))
3228 IShellLinkW_SetShowCmd(sl,MSI_RecordGetInteger(row,11));
3230 if (!MSI_RecordIsNull(row,12))
3232 LPWSTR Path;
3233 buffer = MSI_RecordGetString(row,12);
3234 Path = resolve_folder(package, buffer, FALSE, FALSE, TRUE, NULL);
3235 if (Path)
3236 IShellLinkW_SetWorkingDirectory(sl,Path);
3237 msi_free(Path);
3240 TRACE("Writing shortcut to %s\n",debugstr_w(target_file));
3241 IPersistFile_Save(pf,target_file,FALSE);
3243 msi_free(target_file);
3245 err:
3246 if (pf)
3247 IPersistFile_Release( pf );
3248 if (sl)
3249 IShellLinkW_Release( sl );
3251 return ERROR_SUCCESS;
3254 static UINT ACTION_CreateShortcuts(MSIPACKAGE *package)
3256 UINT rc;
3257 HRESULT res;
3258 MSIQUERY * view;
3259 static const WCHAR Query[] =
3260 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3261 '`','S','h','o','r','t','c','u','t','`',0};
3263 rc = MSI_DatabaseOpenViewW(package->db, Query, &view);
3264 if (rc != ERROR_SUCCESS)
3265 return ERROR_SUCCESS;
3267 res = CoInitialize( NULL );
3268 if (FAILED (res))
3270 ERR("CoInitialize failed\n");
3271 return ERROR_FUNCTION_FAILED;
3274 rc = MSI_IterateRecords(view, NULL, ITERATE_CreateShortcuts, package);
3275 msiobj_release(&view->hdr);
3277 CoUninitialize();
3279 return rc;
3282 static UINT ITERATE_PublishIcon(MSIRECORD *row, LPVOID param)
3284 MSIPACKAGE* package = (MSIPACKAGE*)param;
3285 HANDLE the_file;
3286 LPWSTR FilePath;
3287 LPCWSTR FileName;
3288 CHAR buffer[1024];
3289 DWORD sz;
3290 UINT rc;
3291 MSIRECORD *uirow;
3293 FileName = MSI_RecordGetString(row,1);
3294 if (!FileName)
3296 ERR("Unable to get FileName\n");
3297 return ERROR_SUCCESS;
3300 FilePath = build_icon_path(package,FileName);
3302 TRACE("Creating icon file at %s\n",debugstr_w(FilePath));
3304 the_file = CreateFileW(FilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
3305 FILE_ATTRIBUTE_NORMAL, NULL);
3307 if (the_file == INVALID_HANDLE_VALUE)
3309 ERR("Unable to create file %s\n",debugstr_w(FilePath));
3310 msi_free(FilePath);
3311 return ERROR_SUCCESS;
3316 DWORD write;
3317 sz = 1024;
3318 rc = MSI_RecordReadStream(row,2,buffer,&sz);
3319 if (rc != ERROR_SUCCESS)
3321 ERR("Failed to get stream\n");
3322 CloseHandle(the_file);
3323 DeleteFileW(FilePath);
3324 break;
3326 WriteFile(the_file,buffer,sz,&write,NULL);
3327 } while (sz == 1024);
3329 msi_free(FilePath);
3331 CloseHandle(the_file);
3333 uirow = MSI_CreateRecord(1);
3334 MSI_RecordSetStringW(uirow,1,FileName);
3335 ui_actiondata(package,szPublishProduct,uirow);
3336 msiobj_release( &uirow->hdr );
3338 return ERROR_SUCCESS;
3341 static UINT msi_publish_icons(MSIPACKAGE *package)
3343 UINT r;
3344 MSIQUERY *view;
3346 static const WCHAR query[]= {
3347 'S','E','L','E','C','T',' ','*',' ',
3348 'F','R','O','M',' ','`','I','c','o','n','`',0};
3350 r = MSI_DatabaseOpenViewW(package->db, query, &view);
3351 if (r == ERROR_SUCCESS)
3353 MSI_IterateRecords(view, NULL, ITERATE_PublishIcon, package);
3354 msiobj_release(&view->hdr);
3357 return ERROR_SUCCESS;
3360 static UINT msi_publish_sourcelist(MSIPACKAGE *package, HKEY hkey)
3362 UINT r;
3363 HKEY source;
3364 LPWSTR buffer;
3365 MSIMEDIADISK *disk;
3366 MSISOURCELISTINFO *info;
3368 static const WCHAR szEmpty[] = {0};
3369 static const WCHAR szSourceList[] = {'S','o','u','r','c','e','L','i','s','t',0};
3371 r = RegCreateKeyW(hkey, szSourceList, &source);
3372 if (r != ERROR_SUCCESS)
3373 return r;
3375 RegCloseKey(source);
3377 buffer = strrchrW(package->PackagePath, '\\') + 1;
3378 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3379 package->Context, MSICODE_PRODUCT,
3380 INSTALLPROPERTY_PACKAGENAMEW, buffer);
3381 if (r != ERROR_SUCCESS)
3382 return r;
3384 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3385 package->Context, MSICODE_PRODUCT,
3386 INSTALLPROPERTY_MEDIAPACKAGEPATHW, szEmpty);
3387 if (r != ERROR_SUCCESS)
3388 return r;
3390 r = MsiSourceListSetInfoW(package->ProductCode, NULL,
3391 package->Context, MSICODE_PRODUCT,
3392 INSTALLPROPERTY_DISKPROMPTW, szEmpty);
3393 if (r != ERROR_SUCCESS)
3394 return r;
3396 LIST_FOR_EACH_ENTRY(info, &package->sourcelist_info, MSISOURCELISTINFO, entry)
3398 if (!lstrcmpW(info->property, INSTALLPROPERTY_LASTUSEDSOURCEW))
3399 msi_set_last_used_source(package->ProductCode, NULL, info->context,
3400 info->options, info->value);
3401 else
3402 MsiSourceListSetInfoW(package->ProductCode, NULL,
3403 info->context, info->options,
3404 info->property, info->value);
3407 LIST_FOR_EACH_ENTRY(disk, &package->sourcelist_media, MSIMEDIADISK, entry)
3409 MsiSourceListAddMediaDiskW(package->ProductCode, NULL,
3410 disk->context, disk->options,
3411 disk->disk_id, disk->volume_label, disk->disk_prompt);
3414 return ERROR_SUCCESS;
3417 static UINT msi_publish_product_properties(MSIPACKAGE *package, HKEY hkey)
3419 MSIHANDLE hdb, suminfo;
3420 WCHAR guids[MAX_PATH];
3421 WCHAR packcode[SQUISH_GUID_SIZE];
3422 LPWSTR buffer;
3423 LPWSTR ptr;
3424 DWORD langid;
3425 DWORD size;
3426 UINT r;
3428 static const WCHAR szProductLanguage[] =
3429 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
3430 static const WCHAR szARPProductIcon[] =
3431 {'A','R','P','P','R','O','D','U','C','T','I','C','O','N',0};
3432 static const WCHAR szProductVersion[] =
3433 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
3434 static const WCHAR szAssignment[] =
3435 {'A','s','s','i','g','n','m','e','n','t',0};
3436 static const WCHAR szAdvertiseFlags[] =
3437 {'A','d','v','e','r','t','i','s','e','F','l','a','g','s',0};
3438 static const WCHAR szClients[] =
3439 {'C','l','i','e','n','t','s',0};
3440 static const WCHAR szColon[] = {':',0};
3442 buffer = msi_dup_property(package, INSTALLPROPERTY_PRODUCTNAMEW);
3443 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTNAMEW, buffer);
3444 msi_free(buffer);
3446 langid = msi_get_property_int(package, szProductLanguage, 0);
3447 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
3449 ptr = strrchrW(package->PackagePath, '\\' ) + 1;
3450 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGENAMEW, ptr);
3452 /* FIXME */
3453 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_AUTHORIZED_LUA_APPW, 0);
3455 buffer = msi_dup_property(package, szARPProductIcon);
3456 if (buffer)
3458 LPWSTR path = build_icon_path(package,buffer);
3459 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PRODUCTICONW, path);
3460 msi_free(path);
3461 msi_free(buffer);
3464 buffer = msi_dup_property(package, szProductVersion);
3465 if (buffer)
3467 DWORD verdword = msi_version_str_to_dword(buffer);
3468 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
3469 msi_free(buffer);
3472 msi_reg_set_val_dword(hkey, szAssignment, 0);
3473 msi_reg_set_val_dword(hkey, szAdvertiseFlags, 0x184);
3474 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_INSTANCETYPEW, 0);
3475 msi_reg_set_val_str(hkey, szClients, szColon);
3477 hdb = alloc_msihandle(&package->db->hdr);
3478 if (!hdb)
3479 return ERROR_NOT_ENOUGH_MEMORY;
3481 r = MsiGetSummaryInformationW(hdb, NULL, 0, &suminfo);
3482 MsiCloseHandle(hdb);
3483 if (r != ERROR_SUCCESS)
3484 goto done;
3486 size = MAX_PATH;
3487 r = MsiSummaryInfoGetPropertyW(suminfo, PID_REVNUMBER, NULL, NULL,
3488 NULL, guids, &size);
3489 if (r != ERROR_SUCCESS)
3490 goto done;
3492 ptr = strchrW(guids, ';');
3493 if (ptr) *ptr = 0;
3494 squash_guid(guids, packcode);
3495 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PACKAGECODEW, packcode);
3497 done:
3498 MsiCloseHandle(suminfo);
3499 return ERROR_SUCCESS;
3502 static UINT msi_publish_upgrade_code(MSIPACKAGE *package)
3504 UINT r;
3505 HKEY hkey;
3506 LPWSTR upgrade;
3507 WCHAR squashed_pc[SQUISH_GUID_SIZE];
3509 static const WCHAR szUpgradeCode[] =
3510 {'U','p','g','r','a','d','e','C','o','d','e',0};
3512 upgrade = msi_dup_property(package, szUpgradeCode);
3513 if (!upgrade)
3514 return ERROR_SUCCESS;
3516 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3518 r = MSIREG_OpenClassesUpgradeCodesKey(upgrade, &hkey, TRUE);
3519 if (r != ERROR_SUCCESS)
3520 goto done;
3522 else
3524 r = MSIREG_OpenUserUpgradeCodesKey(upgrade, &hkey, TRUE);
3525 if (r != ERROR_SUCCESS)
3526 goto done;
3529 squash_guid(package->ProductCode, squashed_pc);
3530 msi_reg_set_val_str(hkey, squashed_pc, NULL);
3532 RegCloseKey(hkey);
3534 done:
3535 msi_free(upgrade);
3536 return r;
3539 static BOOL msi_check_publish(MSIPACKAGE *package)
3541 MSIFEATURE *feature;
3543 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3545 if (feature->ActionRequest == INSTALLSTATE_LOCAL)
3546 return TRUE;
3549 return FALSE;
3552 static BOOL msi_check_unpublish(MSIPACKAGE *package)
3554 MSIFEATURE *feature;
3556 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3558 if (feature->ActionRequest != INSTALLSTATE_ABSENT)
3559 return FALSE;
3562 return TRUE;
3566 * 99% of the work done here is only done for
3567 * advertised installs. However this is where the
3568 * Icon table is processed and written out
3569 * so that is what I am going to do here.
3571 static UINT ACTION_PublishProduct(MSIPACKAGE *package)
3573 UINT rc;
3574 HKEY hukey=0;
3575 HKEY hudkey=0;
3577 /* FIXME: also need to publish if the product is in advertise mode */
3578 if (!msi_check_publish(package))
3579 return ERROR_SUCCESS;
3581 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3583 rc = MSIREG_OpenLocalClassesProductKey(package->ProductCode, &hukey, TRUE);
3584 if (rc != ERROR_SUCCESS)
3585 goto end;
3587 rc = MSIREG_OpenLocalUserDataProductKey(package->ProductCode, &hudkey, TRUE);
3588 if (rc != ERROR_SUCCESS)
3589 goto end;
3591 else
3593 rc = MSIREG_OpenUserProductsKey(package->ProductCode, &hukey, TRUE);
3594 if (rc != ERROR_SUCCESS)
3595 goto end;
3597 rc = MSIREG_OpenUserDataProductKey(package->ProductCode, &hudkey, TRUE);
3598 if (rc != ERROR_SUCCESS)
3599 goto end;
3602 rc = msi_publish_upgrade_code(package);
3603 if (rc != ERROR_SUCCESS)
3604 goto end;
3606 rc = msi_publish_product_properties(package, hukey);
3607 if (rc != ERROR_SUCCESS)
3608 goto end;
3610 rc = msi_publish_sourcelist(package, hukey);
3611 if (rc != ERROR_SUCCESS)
3612 goto end;
3614 rc = msi_publish_icons(package);
3616 end:
3617 RegCloseKey(hukey);
3618 RegCloseKey(hudkey);
3620 return rc;
3623 static UINT ITERATE_WriteIniValues(MSIRECORD *row, LPVOID param)
3625 MSIPACKAGE *package = (MSIPACKAGE*)param;
3626 LPCWSTR component, section, key, value, identifier, dirproperty;
3627 LPWSTR deformated_section, deformated_key, deformated_value;
3628 LPWSTR folder, filename, fullname = NULL;
3629 LPCWSTR filenameptr;
3630 MSIRECORD * uirow;
3631 INT action;
3632 MSICOMPONENT *comp;
3633 static const WCHAR szWindowsFolder[] =
3634 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
3636 component = MSI_RecordGetString(row, 8);
3637 comp = get_loaded_component(package,component);
3639 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL))
3641 TRACE("Skipping ini file due to disabled component %s\n",
3642 debugstr_w(component));
3644 comp->Action = comp->Installed;
3646 return ERROR_SUCCESS;
3649 comp->Action = INSTALLSTATE_LOCAL;
3651 identifier = MSI_RecordGetString(row,1);
3652 dirproperty = MSI_RecordGetString(row,3);
3653 section = MSI_RecordGetString(row,4);
3654 key = MSI_RecordGetString(row,5);
3655 value = MSI_RecordGetString(row,6);
3656 action = MSI_RecordGetInteger(row,7);
3658 deformat_string(package,section,&deformated_section);
3659 deformat_string(package,key,&deformated_key);
3660 deformat_string(package,value,&deformated_value);
3662 filename = msi_dup_record_field(row, 2);
3663 if (filename && (filenameptr = strchrW(filename, '|')))
3664 filenameptr++;
3665 else
3666 filenameptr = filename;
3668 if (dirproperty)
3670 folder = resolve_folder(package, dirproperty, FALSE, FALSE, TRUE, NULL);
3671 if (!folder)
3672 folder = msi_dup_property( package, dirproperty );
3674 else
3675 folder = msi_dup_property( package, szWindowsFolder );
3677 if (!folder)
3679 ERR("Unable to resolve folder! (%s)\n",debugstr_w(dirproperty));
3680 goto cleanup;
3683 fullname = build_directory_name(2, folder, filenameptr);
3685 if (action == 0)
3687 TRACE("Adding value %s to section %s in %s\n",
3688 debugstr_w(deformated_key), debugstr_w(deformated_section),
3689 debugstr_w(fullname));
3690 WritePrivateProfileStringW(deformated_section, deformated_key,
3691 deformated_value, fullname);
3693 else if (action == 1)
3695 WCHAR returned[10];
3696 GetPrivateProfileStringW(deformated_section, deformated_key, NULL,
3697 returned, 10, fullname);
3698 if (returned[0] == 0)
3700 TRACE("Adding value %s to section %s in %s\n",
3701 debugstr_w(deformated_key), debugstr_w(deformated_section),
3702 debugstr_w(fullname));
3704 WritePrivateProfileStringW(deformated_section, deformated_key,
3705 deformated_value, fullname);
3708 else if (action == 3)
3709 FIXME("Append to existing section not yet implemented\n");
3711 uirow = MSI_CreateRecord(4);
3712 MSI_RecordSetStringW(uirow,1,identifier);
3713 MSI_RecordSetStringW(uirow,2,deformated_section);
3714 MSI_RecordSetStringW(uirow,3,deformated_key);
3715 MSI_RecordSetStringW(uirow,4,deformated_value);
3716 ui_actiondata(package,szWriteIniValues,uirow);
3717 msiobj_release( &uirow->hdr );
3719 cleanup:
3720 msi_free(filename);
3721 msi_free(fullname);
3722 msi_free(folder);
3723 msi_free(deformated_key);
3724 msi_free(deformated_value);
3725 msi_free(deformated_section);
3726 return ERROR_SUCCESS;
3729 static UINT ACTION_WriteIniValues(MSIPACKAGE *package)
3731 UINT rc;
3732 MSIQUERY * view;
3733 static const WCHAR ExecSeqQuery[] =
3734 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3735 '`','I','n','i','F','i','l','e','`',0};
3737 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3738 if (rc != ERROR_SUCCESS)
3740 TRACE("no IniFile table\n");
3741 return ERROR_SUCCESS;
3744 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteIniValues, package);
3745 msiobj_release(&view->hdr);
3746 return rc;
3749 static UINT ITERATE_SelfRegModules(MSIRECORD *row, LPVOID param)
3751 MSIPACKAGE *package = (MSIPACKAGE*)param;
3752 LPCWSTR filename;
3753 LPWSTR FullName;
3754 MSIFILE *file;
3755 DWORD len;
3756 static const WCHAR ExeStr[] =
3757 {'r','e','g','s','v','r','3','2','.','e','x','e',' ','\"',0};
3758 static const WCHAR close[] = {'\"',0};
3759 STARTUPINFOW si;
3760 PROCESS_INFORMATION info;
3761 BOOL brc;
3762 MSIRECORD *uirow;
3763 LPWSTR uipath, p;
3765 memset(&si,0,sizeof(STARTUPINFOW));
3767 filename = MSI_RecordGetString(row,1);
3768 file = get_loaded_file( package, filename );
3770 if (!file)
3772 ERR("Unable to find file id %s\n",debugstr_w(filename));
3773 return ERROR_SUCCESS;
3776 len = strlenW(ExeStr) + strlenW( file->TargetPath ) + 2;
3778 FullName = msi_alloc(len*sizeof(WCHAR));
3779 strcpyW(FullName,ExeStr);
3780 strcatW( FullName, file->TargetPath );
3781 strcatW(FullName,close);
3783 TRACE("Registering %s\n",debugstr_w(FullName));
3784 brc = CreateProcessW(NULL, FullName, NULL, NULL, FALSE, 0, NULL, c_colon,
3785 &si, &info);
3787 if (brc)
3789 CloseHandle(info.hThread);
3790 msi_dialog_check_messages(info.hProcess);
3791 CloseHandle(info.hProcess);
3794 msi_free(FullName);
3796 /* the UI chunk */
3797 uirow = MSI_CreateRecord( 2 );
3798 uipath = strdupW( file->TargetPath );
3799 p = strrchrW(uipath,'\\');
3800 if (p)
3801 p[0]=0;
3802 MSI_RecordSetStringW( uirow, 1, &p[1] );
3803 MSI_RecordSetStringW( uirow, 2, uipath);
3804 ui_actiondata( package, szSelfRegModules, uirow);
3805 msiobj_release( &uirow->hdr );
3806 msi_free( uipath );
3807 /* FIXME: call ui_progress? */
3809 return ERROR_SUCCESS;
3812 static UINT ACTION_SelfRegModules(MSIPACKAGE *package)
3814 UINT rc;
3815 MSIQUERY * view;
3816 static const WCHAR ExecSeqQuery[] =
3817 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
3818 '`','S','e','l','f','R','e','g','`',0};
3820 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
3821 if (rc != ERROR_SUCCESS)
3823 TRACE("no SelfReg table\n");
3824 return ERROR_SUCCESS;
3827 MSI_IterateRecords(view, NULL, ITERATE_SelfRegModules, package);
3828 msiobj_release(&view->hdr);
3830 return ERROR_SUCCESS;
3833 static UINT ACTION_PublishFeatures(MSIPACKAGE *package)
3835 MSIFEATURE *feature;
3836 UINT rc;
3837 HKEY hkey;
3838 HKEY userdata = NULL;
3840 if (!msi_check_publish(package))
3841 return ERROR_SUCCESS;
3843 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
3845 rc = MSIREG_OpenLocalClassesFeaturesKey(package->ProductCode,
3846 &hkey, TRUE);
3847 if (rc != ERROR_SUCCESS)
3848 goto end;
3850 rc = MSIREG_OpenLocalUserDataFeaturesKey(package->ProductCode,
3851 &userdata, TRUE);
3852 if (rc != ERROR_SUCCESS)
3853 goto end;
3855 else
3857 rc = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, TRUE);
3858 if (rc != ERROR_SUCCESS)
3859 goto end;
3861 rc = MSIREG_OpenUserDataFeaturesKey(package->ProductCode,
3862 &userdata, TRUE);
3863 if (rc != ERROR_SUCCESS)
3864 goto end;
3867 /* here the guids are base 85 encoded */
3868 LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
3870 ComponentList *cl;
3871 LPWSTR data = NULL;
3872 GUID clsid;
3873 INT size;
3874 BOOL absent = FALSE;
3875 MSIRECORD *uirow;
3877 if (!ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_LOCAL ) &&
3878 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_SOURCE ) &&
3879 !ACTION_VerifyFeatureForAction( feature, INSTALLSTATE_ADVERTISED ))
3880 absent = TRUE;
3882 size = 1;
3883 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3885 size += 21;
3887 if (feature->Feature_Parent)
3888 size += strlenW( feature->Feature_Parent )+2;
3890 data = msi_alloc(size * sizeof(WCHAR));
3892 data[0] = 0;
3893 LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
3895 MSICOMPONENT* component = cl->component;
3896 WCHAR buf[21];
3898 buf[0] = 0;
3899 if (component->ComponentId)
3901 TRACE("From %s\n",debugstr_w(component->ComponentId));
3902 CLSIDFromString(component->ComponentId, &clsid);
3903 encode_base85_guid(&clsid,buf);
3904 TRACE("to %s\n",debugstr_w(buf));
3905 strcatW(data,buf);
3909 if (feature->Feature_Parent)
3911 static const WCHAR sep[] = {'\2',0};
3912 strcatW(data,sep);
3913 strcatW(data,feature->Feature_Parent);
3916 msi_reg_set_val_str( userdata, feature->Feature, data );
3917 msi_free(data);
3919 size = 0;
3920 if (feature->Feature_Parent)
3921 size = strlenW(feature->Feature_Parent)*sizeof(WCHAR);
3922 if (!absent)
3924 static const WCHAR emptyW[] = {0};
3925 size += sizeof(WCHAR);
3926 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3927 (LPBYTE)(feature->Feature_Parent ? feature->Feature_Parent : emptyW),size);
3929 else
3931 size += 2*sizeof(WCHAR);
3932 data = msi_alloc(size);
3933 data[0] = 0x6;
3934 data[1] = 0;
3935 if (feature->Feature_Parent)
3936 strcpyW( &data[1], feature->Feature_Parent );
3937 RegSetValueExW(hkey,feature->Feature,0,REG_SZ,
3938 (LPBYTE)data,size);
3939 msi_free(data);
3942 /* the UI chunk */
3943 uirow = MSI_CreateRecord( 1 );
3944 MSI_RecordSetStringW( uirow, 1, feature->Feature );
3945 ui_actiondata( package, szPublishFeatures, uirow);
3946 msiobj_release( &uirow->hdr );
3947 /* FIXME: call ui_progress? */
3950 end:
3951 RegCloseKey(hkey);
3952 RegCloseKey(userdata);
3953 return rc;
3956 static UINT msi_unpublish_feature(MSIPACKAGE *package, MSIFEATURE *feature)
3958 UINT r;
3959 HKEY hkey;
3961 TRACE("unpublishing feature %s\n", debugstr_w(feature->Feature));
3963 r = MSIREG_OpenUserFeaturesKey(package->ProductCode, &hkey, FALSE);
3964 if (r == ERROR_SUCCESS)
3966 RegDeleteValueW(hkey, feature->Feature);
3967 RegCloseKey(hkey);
3970 r = MSIREG_OpenUserDataFeaturesKey(package->ProductCode, &hkey, FALSE);
3971 if (r == ERROR_SUCCESS)
3973 RegDeleteValueW(hkey, feature->Feature);
3974 RegCloseKey(hkey);
3977 return ERROR_SUCCESS;
3980 static UINT ACTION_UnpublishFeatures(MSIPACKAGE *package)
3982 MSIFEATURE *feature;
3984 if (!msi_check_unpublish(package))
3985 return ERROR_SUCCESS;
3987 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
3989 msi_unpublish_feature(package, feature);
3992 return ERROR_SUCCESS;
3995 static UINT msi_get_local_package_name( LPWSTR path )
3997 static const WCHAR szInstaller[] = {
3998 '\\','I','n','s','t','a','l','l','e','r','\\',0};
3999 static const WCHAR fmt[] = { '%','x','.','m','s','i',0};
4000 DWORD time, len, i;
4001 HANDLE handle;
4003 time = GetTickCount();
4004 GetWindowsDirectoryW( path, MAX_PATH );
4005 lstrcatW( path, szInstaller );
4006 CreateDirectoryW( path, NULL );
4008 len = lstrlenW(path);
4009 for (i=0; i<0x10000; i++)
4011 snprintfW( &path[len], MAX_PATH - len, fmt, (time+i)&0xffff );
4012 handle = CreateFileW( path, GENERIC_WRITE, 0, NULL,
4013 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0 );
4014 if (handle != INVALID_HANDLE_VALUE)
4016 CloseHandle(handle);
4017 break;
4019 if (GetLastError() != ERROR_FILE_EXISTS &&
4020 GetLastError() != ERROR_SHARING_VIOLATION)
4021 return ERROR_FUNCTION_FAILED;
4024 return ERROR_SUCCESS;
4027 static UINT msi_make_package_local( MSIPACKAGE *package, HKEY hkey )
4029 WCHAR packagefile[MAX_PATH];
4030 UINT r;
4032 r = msi_get_local_package_name( packagefile );
4033 if (r != ERROR_SUCCESS)
4034 return r;
4036 TRACE("Copying to local package %s\n",debugstr_w(packagefile));
4038 r = CopyFileW( package->db->path, packagefile, FALSE);
4040 if (!r)
4042 ERR("Unable to copy package (%s -> %s) (error %d)\n",
4043 debugstr_w(package->db->path), debugstr_w(packagefile), GetLastError());
4044 return ERROR_FUNCTION_FAILED;
4047 msi_reg_set_val_str( hkey, INSTALLPROPERTY_LOCALPACKAGEW, packagefile );
4049 return ERROR_SUCCESS;
4052 static UINT msi_publish_install_properties(MSIPACKAGE *package, HKEY hkey)
4054 LPWSTR prop, val, key;
4055 SYSTEMTIME systime;
4056 DWORD size, langid;
4057 WCHAR date[9];
4058 LPWSTR buffer;
4060 static const WCHAR date_fmt[] = {'%','i','%','0','2','i','%','0','2','i',0};
4061 static const WCHAR szWindowsInstaller[] =
4062 {'W','i','n','d','o','w','s','I','n','s','t','a','l','l','e','r',0};
4063 static const WCHAR modpath_fmt[] =
4064 {'M','s','i','E','x','e','c','.','e','x','e',' ',
4065 '/','I','[','P','r','o','d','u','c','t','C','o','d','e',']',0};
4066 static const WCHAR szModifyPath[] =
4067 {'M','o','d','i','f','y','P','a','t','h',0};
4068 static const WCHAR szUninstallString[] =
4069 {'U','n','i','n','s','t','a','l','l','S','t','r','i','n','g',0};
4070 static const WCHAR szEstimatedSize[] =
4071 {'E','s','t','i','m','a','t','e','d','S','i','z','e',0};
4072 static const WCHAR szProductLanguage[] =
4073 {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
4074 static const WCHAR szProductVersion[] =
4075 {'P','r','o','d','u','c','t','V','e','r','s','i','o','n',0};
4076 static const WCHAR szProductName[] =
4077 {'P','r','o','d','u','c','t','N','a','m','e',0};
4078 static const WCHAR szDisplayName[] =
4079 {'D','i','s','p','l','a','y','N','a','m','e',0};
4080 static const WCHAR szDisplayVersion[] =
4081 {'D','i','s','p','l','a','y','V','e','r','s','i','o','n',0};
4082 static const WCHAR szManufacturer[] =
4083 {'M','a','n','u','f','a','c','t','u','r','e','r',0};
4085 static const LPCSTR propval[] = {
4086 "ARPAUTHORIZEDCDFPREFIX", "AuthorizedCDFPrefix",
4087 "ARPCONTACT", "Contact",
4088 "ARPCOMMENTS", "Comments",
4089 "ProductName", "DisplayName",
4090 "ProductVersion", "DisplayVersion",
4091 "ARPHELPLINK", "HelpLink",
4092 "ARPHELPTELEPHONE", "HelpTelephone",
4093 "ARPINSTALLLOCATION", "InstallLocation",
4094 "SourceDir", "InstallSource",
4095 "Manufacturer", "Publisher",
4096 "ARPREADME", "Readme",
4097 "ARPSIZE", "Size",
4098 "ARPURLINFOABOUT", "URLInfoAbout",
4099 "ARPURLUPDATEINFO", "URLUpdateInfo",
4100 NULL,
4102 const LPCSTR *p = propval;
4104 while (*p)
4106 prop = strdupAtoW(*p++);
4107 key = strdupAtoW(*p++);
4108 val = msi_dup_property(package, prop);
4109 msi_reg_set_val_str(hkey, key, val);
4110 msi_free(val);
4111 msi_free(key);
4112 msi_free(prop);
4115 msi_reg_set_val_dword(hkey, szWindowsInstaller, 1);
4117 size = deformat_string(package, modpath_fmt, &buffer);
4118 RegSetValueExW(hkey, szModifyPath, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4119 RegSetValueExW(hkey, szUninstallString, 0, REG_EXPAND_SZ, (LPBYTE)buffer, size);
4120 msi_free(buffer);
4122 /* FIXME: Write real Estimated Size when we have it */
4123 msi_reg_set_val_dword(hkey, szEstimatedSize, 0);
4125 buffer = msi_dup_property(package, szProductName);
4126 msi_reg_set_val_str(hkey, szDisplayName, buffer);
4127 msi_free(buffer);
4129 buffer = msi_dup_property(package, cszSourceDir);
4130 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLSOURCEW, buffer);
4131 msi_free(buffer);
4133 buffer = msi_dup_property(package, szManufacturer);
4134 msi_reg_set_val_str(hkey, INSTALLPROPERTY_PUBLISHERW, buffer);
4135 msi_free(buffer);
4137 GetLocalTime(&systime);
4138 sprintfW(date, date_fmt, systime.wYear, systime.wMonth, systime.wDay);
4139 msi_reg_set_val_str(hkey, INSTALLPROPERTY_INSTALLDATEW, date);
4141 langid = msi_get_property_int(package, szProductLanguage, 0);
4142 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_LANGUAGEW, langid);
4144 buffer = msi_dup_property(package, szProductVersion);
4145 msi_reg_set_val_str(hkey, szDisplayVersion, buffer);
4146 if (buffer)
4148 DWORD verdword = msi_version_str_to_dword(buffer);
4150 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONW, verdword);
4151 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMAJORW, verdword >> 24);
4152 msi_reg_set_val_dword(hkey, INSTALLPROPERTY_VERSIONMINORW, (verdword >> 16) & 0xFF);
4153 msi_free(buffer);
4156 return ERROR_SUCCESS;
4159 static UINT ACTION_RegisterProduct(MSIPACKAGE *package)
4161 WCHAR squashed_pc[SQUISH_GUID_SIZE];
4162 LPWSTR upgrade_code;
4163 HKEY hkey, props;
4164 HKEY upgrade;
4165 UINT rc;
4167 static const WCHAR szUpgradeCode[] = {
4168 'U','p','g','r','a','d','e','C','o','d','e',0};
4170 /* FIXME: also need to publish if the product is in advertise mode */
4171 if (!msi_check_publish(package))
4172 return ERROR_SUCCESS;
4174 rc = MSIREG_OpenUninstallKey(package->ProductCode, &hkey, TRUE);
4175 if (rc != ERROR_SUCCESS)
4176 return rc;
4178 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4180 rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &props, TRUE);
4181 if (rc != ERROR_SUCCESS)
4182 goto done;
4184 else
4186 rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &props, TRUE);
4187 if (rc != ERROR_SUCCESS)
4188 goto done;
4191 msi_make_package_local(package, props);
4193 rc = msi_publish_install_properties(package, hkey);
4194 if (rc != ERROR_SUCCESS)
4195 goto done;
4197 rc = msi_publish_install_properties(package, props);
4198 if (rc != ERROR_SUCCESS)
4199 goto done;
4201 upgrade_code = msi_dup_property(package, szUpgradeCode);
4202 if (upgrade_code)
4204 MSIREG_OpenUpgradeCodesKey(upgrade_code, &upgrade, TRUE);
4205 squash_guid(package->ProductCode, squashed_pc);
4206 msi_reg_set_val_str(upgrade, squashed_pc, NULL);
4207 RegCloseKey(upgrade);
4208 msi_free(upgrade_code);
4211 done:
4212 RegCloseKey(hkey);
4214 return ERROR_SUCCESS;
4217 static UINT ACTION_InstallExecute(MSIPACKAGE *package)
4219 return execute_script(package,INSTALL_SCRIPT);
4222 static UINT msi_unpublish_product(MSIPACKAGE *package)
4224 LPWSTR upgrade;
4225 LPWSTR remove = NULL;
4226 LPWSTR *features = NULL;
4227 BOOL full_uninstall = TRUE;
4228 MSIFEATURE *feature;
4230 static const WCHAR szRemove[] = {'R','E','M','O','V','E',0};
4231 static const WCHAR szAll[] = {'A','L','L',0};
4232 static const WCHAR szUpgradeCode[] =
4233 {'U','p','g','r','a','d','e','C','o','d','e',0};
4235 remove = msi_dup_property(package, szRemove);
4236 if (!remove)
4237 return ERROR_SUCCESS;
4239 features = msi_split_string(remove, ',');
4240 if (!features)
4242 msi_free(remove);
4243 ERR("REMOVE feature list is empty!\n");
4244 return ERROR_FUNCTION_FAILED;
4247 if (!lstrcmpW(features[0], szAll))
4248 full_uninstall = TRUE;
4249 else
4251 LIST_FOR_EACH_ENTRY(feature, &package->features, MSIFEATURE, entry)
4253 if (feature->Action != INSTALLSTATE_ABSENT)
4254 full_uninstall = FALSE;
4258 if (!full_uninstall)
4259 goto done;
4261 MSIREG_DeleteProductKey(package->ProductCode);
4262 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4263 MSIREG_DeleteUninstallKey(package->ProductCode);
4265 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4267 MSIREG_DeleteLocalClassesProductKey(package->ProductCode);
4268 MSIREG_DeleteLocalClassesFeaturesKey(package->ProductCode);
4270 else
4272 MSIREG_DeleteUserProductKey(package->ProductCode);
4273 MSIREG_DeleteUserFeaturesKey(package->ProductCode);
4276 upgrade = msi_dup_property(package, szUpgradeCode);
4277 if (upgrade)
4279 MSIREG_DeleteUserUpgradeCodesKey(upgrade);
4280 msi_free(upgrade);
4283 done:
4284 msi_free(remove);
4285 msi_free(features);
4286 return ERROR_SUCCESS;
4289 static UINT ACTION_InstallFinalize(MSIPACKAGE *package)
4291 UINT rc;
4293 rc = msi_unpublish_product(package);
4294 if (rc != ERROR_SUCCESS)
4295 return rc;
4297 /* turn off scheduling */
4298 package->script->CurrentlyScripting= FALSE;
4300 /* first do the same as an InstallExecute */
4301 rc = ACTION_InstallExecute(package);
4302 if (rc != ERROR_SUCCESS)
4303 return rc;
4305 /* then handle Commit Actions */
4306 rc = execute_script(package,COMMIT_SCRIPT);
4308 return rc;
4311 UINT ACTION_ForceReboot(MSIPACKAGE *package)
4313 static const WCHAR RunOnce[] = {
4314 'S','o','f','t','w','a','r','e','\\',
4315 'M','i','c','r','o','s','o','f','t','\\',
4316 'W','i','n','d','o','w','s','\\',
4317 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4318 'R','u','n','O','n','c','e',0};
4319 static const WCHAR InstallRunOnce[] = {
4320 'S','o','f','t','w','a','r','e','\\',
4321 'M','i','c','r','o','s','o','f','t','\\',
4322 'W','i','n','d','o','w','s','\\',
4323 'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
4324 'I','n','s','t','a','l','l','e','r','\\',
4325 'R','u','n','O','n','c','e','E','n','t','r','i','e','s',0};
4327 static const WCHAR msiexec_fmt[] = {
4328 '%','s',
4329 '\\','M','s','i','E','x','e','c','.','e','x','e',' ','/','@',' ',
4330 '\"','%','s','\"',0};
4331 static const WCHAR install_fmt[] = {
4332 '/','I',' ','\"','%','s','\"',' ',
4333 'A','F','T','E','R','R','E','B','O','O','T','=','1',' ',
4334 'R','U','N','O','N','C','E','E','N','T','R','Y','=','\"','%','s','\"',0};
4335 WCHAR buffer[256], sysdir[MAX_PATH];
4336 HKEY hkey;
4337 WCHAR squished_pc[100];
4339 squash_guid(package->ProductCode,squished_pc);
4341 GetSystemDirectoryW(sysdir, sizeof(sysdir)/sizeof(sysdir[0]));
4342 RegCreateKeyW(HKEY_LOCAL_MACHINE,RunOnce,&hkey);
4343 snprintfW(buffer,sizeof(buffer)/sizeof(buffer[0]),msiexec_fmt,sysdir,
4344 squished_pc);
4346 msi_reg_set_val_str( hkey, squished_pc, buffer );
4347 RegCloseKey(hkey);
4349 TRACE("Reboot command %s\n",debugstr_w(buffer));
4351 RegCreateKeyW(HKEY_LOCAL_MACHINE,InstallRunOnce,&hkey);
4352 sprintfW(buffer,install_fmt,package->ProductCode,squished_pc);
4354 msi_reg_set_val_str( hkey, squished_pc, buffer );
4355 RegCloseKey(hkey);
4357 return ERROR_INSTALL_SUSPEND;
4360 static UINT ACTION_ResolveSource(MSIPACKAGE* package)
4362 DWORD attrib;
4363 UINT rc;
4366 * We are currently doing what should be done here in the top level Install
4367 * however for Administrative and uninstalls this step will be needed
4369 if (!package->PackagePath)
4370 return ERROR_SUCCESS;
4372 msi_set_sourcedir_props(package, TRUE);
4374 attrib = GetFileAttributesW(package->db->path);
4375 if (attrib == INVALID_FILE_ATTRIBUTES)
4377 LPWSTR prompt;
4378 LPWSTR msg;
4379 DWORD size = 0;
4381 rc = MsiSourceListGetInfoW(package->ProductCode, NULL,
4382 package->Context, MSICODE_PRODUCT,
4383 INSTALLPROPERTY_DISKPROMPTW,NULL,&size);
4384 if (rc == ERROR_MORE_DATA)
4386 prompt = msi_alloc(size * sizeof(WCHAR));
4387 MsiSourceListGetInfoW(package->ProductCode, NULL,
4388 package->Context, MSICODE_PRODUCT,
4389 INSTALLPROPERTY_DISKPROMPTW,prompt,&size);
4391 else
4392 prompt = strdupW(package->db->path);
4394 msg = generate_error_string(package,1302,1,prompt);
4395 while(attrib == INVALID_FILE_ATTRIBUTES)
4397 rc = MessageBoxW(NULL,msg,NULL,MB_OKCANCEL);
4398 if (rc == IDCANCEL)
4400 rc = ERROR_INSTALL_USEREXIT;
4401 break;
4403 attrib = GetFileAttributesW(package->db->path);
4405 msi_free(prompt);
4406 rc = ERROR_SUCCESS;
4408 else
4409 return ERROR_SUCCESS;
4411 return rc;
4414 static UINT ACTION_RegisterUser(MSIPACKAGE *package)
4416 HKEY hkey=0;
4417 LPWSTR buffer;
4418 LPWSTR productid;
4419 UINT rc,i;
4421 static const WCHAR szPropKeys[][80] =
4423 {'P','r','o','d','u','c','t','I','D',0},
4424 {'U','S','E','R','N','A','M','E',0},
4425 {'C','O','M','P','A','N','Y','N','A','M','E',0},
4426 {0},
4429 static const WCHAR szRegKeys[][80] =
4431 {'P','r','o','d','u','c','t','I','D',0},
4432 {'R','e','g','O','w','n','e','r',0},
4433 {'R','e','g','C','o','m','p','a','n','y',0},
4434 {0},
4437 if (msi_check_unpublish(package))
4439 MSIREG_DeleteUserDataProductKey(package->ProductCode);
4440 return ERROR_SUCCESS;
4443 productid = msi_dup_property( package, INSTALLPROPERTY_PRODUCTIDW );
4444 if (!productid)
4445 return ERROR_SUCCESS;
4447 if (package->Context == MSIINSTALLCONTEXT_MACHINE)
4448 rc = MSIREG_OpenLocalSystemInstallProps(package->ProductCode, &hkey, TRUE);
4449 else
4450 rc = MSIREG_OpenCurrentUserInstallProps(package->ProductCode, &hkey, TRUE);
4452 if (rc != ERROR_SUCCESS)
4453 goto end;
4455 for( i = 0; szPropKeys[i][0]; i++ )
4457 buffer = msi_dup_property( package, szPropKeys[i] );
4458 msi_reg_set_val_str( hkey, szRegKeys[i], buffer );
4459 msi_free( buffer );
4462 end:
4463 msi_free(productid);
4464 RegCloseKey(hkey);
4466 /* FIXME: call ui_actiondata */
4468 return rc;
4472 static UINT ACTION_ExecuteAction(MSIPACKAGE *package)
4474 UINT rc;
4476 package->script->InWhatSequence |= SEQUENCE_EXEC;
4477 rc = ACTION_ProcessExecSequence(package,FALSE);
4478 return rc;
4482 static UINT ITERATE_PublishComponent(MSIRECORD *rec, LPVOID param)
4484 MSIPACKAGE *package = (MSIPACKAGE*)param;
4485 LPCWSTR compgroupid=NULL;
4486 LPCWSTR feature=NULL;
4487 LPCWSTR text = NULL;
4488 LPCWSTR qualifier = NULL;
4489 LPCWSTR component = NULL;
4490 LPWSTR advertise = NULL;
4491 LPWSTR output = NULL;
4492 HKEY hkey;
4493 UINT rc = ERROR_SUCCESS;
4494 MSICOMPONENT *comp;
4495 DWORD sz = 0;
4496 MSIRECORD *uirow;
4498 component = MSI_RecordGetString(rec,3);
4499 comp = get_loaded_component(package,component);
4501 if (!ACTION_VerifyComponentForAction( comp, INSTALLSTATE_LOCAL ) &&
4502 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_SOURCE ) &&
4503 !ACTION_VerifyComponentForAction( comp, INSTALLSTATE_ADVERTISED ))
4505 TRACE("Skipping: Component %s not scheduled for install\n",
4506 debugstr_w(component));
4508 return ERROR_SUCCESS;
4511 compgroupid = MSI_RecordGetString(rec,1);
4512 qualifier = MSI_RecordGetString(rec,2);
4514 rc = MSIREG_OpenUserComponentsKey(compgroupid, &hkey, TRUE);
4515 if (rc != ERROR_SUCCESS)
4516 goto end;
4518 text = MSI_RecordGetString(rec,4);
4519 feature = MSI_RecordGetString(rec,5);
4521 advertise = create_component_advertise_string(package, comp, feature);
4523 sz = strlenW(advertise);
4525 if (text)
4526 sz += lstrlenW(text);
4528 sz+=3;
4529 sz *= sizeof(WCHAR);
4531 output = msi_alloc_zero(sz);
4532 strcpyW(output,advertise);
4533 msi_free(advertise);
4535 if (text)
4536 strcatW(output,text);
4538 msi_reg_set_val_multi_str( hkey, qualifier, output );
4540 end:
4541 RegCloseKey(hkey);
4542 msi_free(output);
4544 /* the UI chunk */
4545 uirow = MSI_CreateRecord( 2 );
4546 MSI_RecordSetStringW( uirow, 1, compgroupid );
4547 MSI_RecordSetStringW( uirow, 2, qualifier);
4548 ui_actiondata( package, szPublishComponents, uirow);
4549 msiobj_release( &uirow->hdr );
4550 /* FIXME: call ui_progress? */
4552 return rc;
4556 * At present I am ignorning the advertised components part of this and only
4557 * focusing on the qualified component sets
4559 static UINT ACTION_PublishComponents(MSIPACKAGE *package)
4561 UINT rc;
4562 MSIQUERY * view;
4563 static const WCHAR ExecSeqQuery[] =
4564 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4565 '`','P','u','b','l','i','s','h',
4566 'C','o','m','p','o','n','e','n','t','`',0};
4568 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4569 if (rc != ERROR_SUCCESS)
4570 return ERROR_SUCCESS;
4572 rc = MSI_IterateRecords(view, NULL, ITERATE_PublishComponent, package);
4573 msiobj_release(&view->hdr);
4575 return rc;
4578 static UINT ITERATE_InstallService(MSIRECORD *rec, LPVOID param)
4580 MSIPACKAGE *package = (MSIPACKAGE*)param;
4581 MSIRECORD *row;
4582 MSIFILE *file;
4583 SC_HANDLE hscm, service = NULL;
4584 LPCWSTR comp, depends, pass;
4585 LPWSTR name = NULL, disp = NULL;
4586 LPCWSTR load_order, serv_name, key;
4587 DWORD serv_type, start_type;
4588 DWORD err_control;
4590 static const WCHAR query[] =
4591 {'S','E','L','E','C','T',' ','*',' ','F','R', 'O','M',' ',
4592 '`','C','o','m','p','o','n','e','n','t','`',' ',
4593 'W','H','E','R','E',' ',
4594 '`','C','o','m','p','o','n','e','n','t','`',' ',
4595 '=','\'','%','s','\'',0};
4597 hscm = OpenSCManagerW(NULL, SERVICES_ACTIVE_DATABASEW, GENERIC_WRITE);
4598 if (!hscm)
4600 ERR("Failed to open the SC Manager!\n");
4601 goto done;
4604 start_type = MSI_RecordGetInteger(rec, 5);
4605 if (start_type == SERVICE_BOOT_START || start_type == SERVICE_SYSTEM_START)
4606 goto done;
4608 depends = MSI_RecordGetString(rec, 8);
4609 if (depends && *depends)
4610 FIXME("Dependency list unhandled!\n");
4612 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4613 deformat_string(package, MSI_RecordGetString(rec, 3), &disp);
4614 serv_type = MSI_RecordGetInteger(rec, 4);
4615 err_control = MSI_RecordGetInteger(rec, 6);
4616 load_order = MSI_RecordGetString(rec, 7);
4617 serv_name = MSI_RecordGetString(rec, 9);
4618 pass = MSI_RecordGetString(rec, 10);
4619 comp = MSI_RecordGetString(rec, 12);
4621 /* fetch the service path */
4622 row = MSI_QueryGetRecord(package->db, query, comp);
4623 if (!row)
4625 ERR("Control query failed!\n");
4626 goto done;
4629 key = MSI_RecordGetString(row, 6);
4631 file = get_loaded_file(package, key);
4632 msiobj_release(&row->hdr);
4633 if (!file)
4635 ERR("Failed to load the service file\n");
4636 goto done;
4639 service = CreateServiceW(hscm, name, disp, GENERIC_ALL, serv_type,
4640 start_type, err_control, file->TargetPath,
4641 load_order, NULL, NULL, serv_name, pass);
4642 if (!service)
4644 if (GetLastError() != ERROR_SERVICE_EXISTS)
4645 ERR("Failed to create service %s: %d\n", debugstr_w(name), GetLastError());
4648 done:
4649 CloseServiceHandle(service);
4650 CloseServiceHandle(hscm);
4651 msi_free(name);
4652 msi_free(disp);
4654 return ERROR_SUCCESS;
4657 static UINT ACTION_InstallServices( MSIPACKAGE *package )
4659 UINT rc;
4660 MSIQUERY * view;
4661 static const WCHAR ExecSeqQuery[] =
4662 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4663 'S','e','r','v','i','c','e','I','n','s','t','a','l','l',0};
4665 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
4666 if (rc != ERROR_SUCCESS)
4667 return ERROR_SUCCESS;
4669 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallService, package);
4670 msiobj_release(&view->hdr);
4672 return rc;
4675 /* converts arg1[~]arg2[~]arg3 to a list of ptrs to the strings */
4676 static LPCWSTR *msi_service_args_to_vector(LPWSTR args, DWORD *numargs)
4678 LPCWSTR *vector, *temp_vector;
4679 LPWSTR p, q;
4680 DWORD sep_len;
4682 static const WCHAR separator[] = {'[','~',']',0};
4684 *numargs = 0;
4685 sep_len = sizeof(separator) / sizeof(WCHAR) - 1;
4687 if (!args)
4688 return NULL;
4690 vector = msi_alloc(sizeof(LPWSTR));
4691 if (!vector)
4692 return NULL;
4694 p = args;
4697 (*numargs)++;
4698 vector[*numargs - 1] = p;
4700 if ((q = strstrW(p, separator)))
4702 *q = '\0';
4704 temp_vector = msi_realloc(vector, (*numargs + 1) * sizeof(LPWSTR));
4705 if (!temp_vector)
4707 msi_free(vector);
4708 return NULL;
4710 vector = temp_vector;
4712 p = q + sep_len;
4714 } while (q);
4716 return vector;
4719 static UINT ITERATE_StartService(MSIRECORD *rec, LPVOID param)
4721 MSIPACKAGE *package = (MSIPACKAGE *)param;
4722 MSICOMPONENT *comp;
4723 SC_HANDLE scm, service = NULL;
4724 LPCWSTR name, *vector = NULL;
4725 LPWSTR args;
4726 DWORD event, numargs;
4727 UINT r = ERROR_FUNCTION_FAILED;
4729 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4730 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4731 return ERROR_SUCCESS;
4733 name = MSI_RecordGetString(rec, 2);
4734 event = MSI_RecordGetInteger(rec, 3);
4735 args = strdupW(MSI_RecordGetString(rec, 4));
4737 if (!(event & msidbServiceControlEventStart))
4738 return ERROR_SUCCESS;
4740 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_CONNECT);
4741 if (!scm)
4743 ERR("Failed to open the service control manager\n");
4744 goto done;
4747 service = OpenServiceW(scm, name, SERVICE_START);
4748 if (!service)
4750 ERR("Failed to open service %s\n", debugstr_w(name));
4751 goto done;
4754 vector = msi_service_args_to_vector(args, &numargs);
4756 if (!StartServiceW(service, numargs, vector))
4758 ERR("Failed to start service %s\n", debugstr_w(name));
4759 goto done;
4762 r = ERROR_SUCCESS;
4764 done:
4765 CloseServiceHandle(service);
4766 CloseServiceHandle(scm);
4768 msi_free(args);
4769 msi_free(vector);
4770 return r;
4773 static UINT ACTION_StartServices( MSIPACKAGE *package )
4775 UINT rc;
4776 MSIQUERY *view;
4778 static const WCHAR query[] = {
4779 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4780 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4782 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4783 if (rc != ERROR_SUCCESS)
4784 return ERROR_SUCCESS;
4786 rc = MSI_IterateRecords(view, NULL, ITERATE_StartService, package);
4787 msiobj_release(&view->hdr);
4789 return rc;
4792 static BOOL stop_service_dependents(SC_HANDLE scm, SC_HANDLE service)
4794 DWORD i, needed, count;
4795 ENUM_SERVICE_STATUSW *dependencies;
4796 SERVICE_STATUS ss;
4797 SC_HANDLE depserv;
4799 if (EnumDependentServicesW(service, SERVICE_ACTIVE, NULL,
4800 0, &needed, &count))
4801 return TRUE;
4803 if (GetLastError() != ERROR_MORE_DATA)
4804 return FALSE;
4806 dependencies = msi_alloc(needed);
4807 if (!dependencies)
4808 return FALSE;
4810 if (!EnumDependentServicesW(service, SERVICE_ACTIVE, dependencies,
4811 needed, &needed, &count))
4812 goto error;
4814 for (i = 0; i < count; i++)
4816 depserv = OpenServiceW(scm, dependencies[i].lpServiceName,
4817 SERVICE_STOP | SERVICE_QUERY_STATUS);
4818 if (!depserv)
4819 goto error;
4821 if (!ControlService(depserv, SERVICE_CONTROL_STOP, &ss))
4822 goto error;
4825 return TRUE;
4827 error:
4828 msi_free(dependencies);
4829 return FALSE;
4832 static UINT ITERATE_StopService(MSIRECORD *rec, LPVOID param)
4834 MSIPACKAGE *package = (MSIPACKAGE *)param;
4835 MSICOMPONENT *comp;
4836 SERVICE_STATUS status;
4837 SERVICE_STATUS_PROCESS ssp;
4838 SC_HANDLE scm = NULL, service = NULL;
4839 LPWSTR name, args;
4840 DWORD event, needed;
4842 event = MSI_RecordGetInteger(rec, 3);
4843 if (!(event & msidbServiceControlEventStop))
4844 return ERROR_SUCCESS;
4846 comp = get_loaded_component(package, MSI_RecordGetString(rec, 6));
4847 if (!comp || comp->Action == INSTALLSTATE_UNKNOWN || comp->Action == INSTALLSTATE_ABSENT)
4848 return ERROR_SUCCESS;
4850 deformat_string(package, MSI_RecordGetString(rec, 2), &name);
4851 deformat_string(package, MSI_RecordGetString(rec, 4), &args);
4852 args = strdupW(MSI_RecordGetString(rec, 4));
4854 scm = OpenSCManagerW(NULL, NULL, SC_MANAGER_ALL_ACCESS);
4855 if (!scm)
4857 WARN("Failed to open the SCM: %d\n", GetLastError());
4858 goto done;
4861 service = OpenServiceW(scm, name,
4862 SERVICE_STOP |
4863 SERVICE_QUERY_STATUS |
4864 SERVICE_ENUMERATE_DEPENDENTS);
4865 if (!service)
4867 WARN("Failed to open service (%s): %d\n",
4868 debugstr_w(name), GetLastError());
4869 goto done;
4872 if (!QueryServiceStatusEx(service, SC_STATUS_PROCESS_INFO, (LPBYTE)&ssp,
4873 sizeof(SERVICE_STATUS_PROCESS), &needed))
4875 WARN("Failed to query service status (%s): %d\n",
4876 debugstr_w(name), GetLastError());
4877 goto done;
4880 if (ssp.dwCurrentState == SERVICE_STOPPED)
4881 goto done;
4883 stop_service_dependents(scm, service);
4885 if (!ControlService(service, SERVICE_CONTROL_STOP, &status))
4886 WARN("Failed to stop service (%s): %d\n", debugstr_w(name), GetLastError());
4888 done:
4889 CloseServiceHandle(service);
4890 CloseServiceHandle(scm);
4891 msi_free(name);
4892 msi_free(args);
4894 return ERROR_SUCCESS;
4897 static UINT ACTION_StopServices( MSIPACKAGE *package )
4899 UINT rc;
4900 MSIQUERY *view;
4902 static const WCHAR query[] = {
4903 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
4904 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
4906 rc = MSI_DatabaseOpenViewW(package->db, query, &view);
4907 if (rc != ERROR_SUCCESS)
4908 return ERROR_SUCCESS;
4910 rc = MSI_IterateRecords(view, NULL, ITERATE_StopService, package);
4911 msiobj_release(&view->hdr);
4913 return rc;
4916 static MSIFILE *msi_find_file( MSIPACKAGE *package, LPCWSTR filename )
4918 MSIFILE *file;
4920 LIST_FOR_EACH_ENTRY(file, &package->files, MSIFILE, entry)
4922 if (!lstrcmpW(file->File, filename))
4923 return file;
4926 return NULL;
4929 static UINT ITERATE_InstallODBCDriver( MSIRECORD *rec, LPVOID param )
4931 MSIPACKAGE *package = (MSIPACKAGE*)param;
4932 LPWSTR driver, driver_path, ptr;
4933 WCHAR outpath[MAX_PATH];
4934 MSIFILE *driver_file, *setup_file;
4935 LPCWSTR desc;
4936 DWORD len, usage;
4937 UINT r = ERROR_SUCCESS;
4939 static const WCHAR driver_fmt[] = {
4940 'D','r','i','v','e','r','=','%','s',0};
4941 static const WCHAR setup_fmt[] = {
4942 'S','e','t','u','p','=','%','s',0};
4943 static const WCHAR usage_fmt[] = {
4944 'F','i','l','e','U','s','a','g','e','=','1',0};
4946 desc = MSI_RecordGetString(rec, 3);
4948 driver_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
4949 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
4951 if (!driver_file || !setup_file)
4953 ERR("ODBC Driver entry not found!\n");
4954 return ERROR_FUNCTION_FAILED;
4957 len = lstrlenW(desc) + lstrlenW(driver_fmt) + lstrlenW(driver_file->FileName) +
4958 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) +
4959 lstrlenW(usage_fmt) + 1;
4960 driver = msi_alloc(len * sizeof(WCHAR));
4961 if (!driver)
4962 return ERROR_OUTOFMEMORY;
4964 ptr = driver;
4965 lstrcpyW(ptr, desc);
4966 ptr += lstrlenW(ptr) + 1;
4968 sprintfW(ptr, driver_fmt, driver_file->FileName);
4969 ptr += lstrlenW(ptr) + 1;
4971 sprintfW(ptr, setup_fmt, setup_file->FileName);
4972 ptr += lstrlenW(ptr) + 1;
4974 lstrcpyW(ptr, usage_fmt);
4975 ptr += lstrlenW(ptr) + 1;
4976 *ptr = '\0';
4978 driver_path = strdupW(driver_file->TargetPath);
4979 ptr = strrchrW(driver_path, '\\');
4980 if (ptr) *ptr = '\0';
4982 if (!SQLInstallDriverExW(driver, driver_path, outpath, MAX_PATH,
4983 NULL, ODBC_INSTALL_COMPLETE, &usage))
4985 ERR("Failed to install SQL driver!\n");
4986 r = ERROR_FUNCTION_FAILED;
4989 msi_free(driver);
4990 msi_free(driver_path);
4992 return r;
4995 static UINT ITERATE_InstallODBCTranslator( MSIRECORD *rec, LPVOID param )
4997 MSIPACKAGE *package = (MSIPACKAGE*)param;
4998 LPWSTR translator, translator_path, ptr;
4999 WCHAR outpath[MAX_PATH];
5000 MSIFILE *translator_file, *setup_file;
5001 LPCWSTR desc;
5002 DWORD len, usage;
5003 UINT r = ERROR_SUCCESS;
5005 static const WCHAR translator_fmt[] = {
5006 'T','r','a','n','s','l','a','t','o','r','=','%','s',0};
5007 static const WCHAR setup_fmt[] = {
5008 'S','e','t','u','p','=','%','s',0};
5010 desc = MSI_RecordGetString(rec, 3);
5012 translator_file = msi_find_file(package, MSI_RecordGetString(rec, 4));
5013 setup_file = msi_find_file(package, MSI_RecordGetString(rec, 5));
5015 if (!translator_file || !setup_file)
5017 ERR("ODBC Translator entry not found!\n");
5018 return ERROR_FUNCTION_FAILED;
5021 len = lstrlenW(desc) + lstrlenW(translator_fmt) + lstrlenW(translator_file->FileName) +
5022 lstrlenW(setup_fmt) + lstrlenW(setup_file->FileName) + 1;
5023 translator = msi_alloc(len * sizeof(WCHAR));
5024 if (!translator)
5025 return ERROR_OUTOFMEMORY;
5027 ptr = translator;
5028 lstrcpyW(ptr, desc);
5029 ptr += lstrlenW(ptr) + 1;
5031 sprintfW(ptr, translator_fmt, translator_file->FileName);
5032 ptr += lstrlenW(ptr) + 1;
5034 sprintfW(ptr, setup_fmt, setup_file->FileName);
5035 ptr += lstrlenW(ptr) + 1;
5036 *ptr = '\0';
5038 translator_path = strdupW(translator_file->TargetPath);
5039 ptr = strrchrW(translator_path, '\\');
5040 if (ptr) *ptr = '\0';
5042 if (!SQLInstallTranslatorExW(translator, translator_path, outpath, MAX_PATH,
5043 NULL, ODBC_INSTALL_COMPLETE, &usage))
5045 ERR("Failed to install SQL translator!\n");
5046 r = ERROR_FUNCTION_FAILED;
5049 msi_free(translator);
5050 msi_free(translator_path);
5052 return r;
5055 static UINT ITERATE_InstallODBCDataSource( MSIRECORD *rec, LPVOID param )
5057 LPWSTR attrs;
5058 LPCWSTR desc, driver;
5059 WORD request = ODBC_ADD_SYS_DSN;
5060 INT registration;
5061 DWORD len;
5062 UINT r = ERROR_SUCCESS;
5064 static const WCHAR attrs_fmt[] = {
5065 'D','S','N','=','%','s',0 };
5067 desc = MSI_RecordGetString(rec, 3);
5068 driver = MSI_RecordGetString(rec, 4);
5069 registration = MSI_RecordGetInteger(rec, 5);
5071 if (registration == msidbODBCDataSourceRegistrationPerMachine) request = ODBC_ADD_SYS_DSN;
5072 else if (registration == msidbODBCDataSourceRegistrationPerUser) request = ODBC_ADD_DSN;
5074 len = lstrlenW(attrs_fmt) + lstrlenW(desc) + 1 + 1;
5075 attrs = msi_alloc(len * sizeof(WCHAR));
5076 if (!attrs)
5077 return ERROR_OUTOFMEMORY;
5079 sprintfW(attrs, attrs_fmt, desc);
5080 attrs[len - 1] = '\0';
5082 if (!SQLConfigDataSourceW(NULL, request, driver, attrs))
5084 ERR("Failed to install SQL data source!\n");
5085 r = ERROR_FUNCTION_FAILED;
5088 msi_free(attrs);
5090 return r;
5093 static UINT ACTION_InstallODBC( MSIPACKAGE *package )
5095 UINT rc;
5096 MSIQUERY *view;
5098 static const WCHAR driver_query[] = {
5099 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5100 'O','D','B','C','D','r','i','v','e','r',0 };
5102 static const WCHAR translator_query[] = {
5103 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5104 'O','D','B','C','T','r','a','n','s','l','a','t','o','r',0 };
5106 static const WCHAR source_query[] = {
5107 'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5108 'O','D','B','C','D','a','t','a','S','o','u','r','c','e',0 };
5110 rc = MSI_DatabaseOpenViewW(package->db, driver_query, &view);
5111 if (rc != ERROR_SUCCESS)
5112 return ERROR_SUCCESS;
5114 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDriver, package);
5115 msiobj_release(&view->hdr);
5117 rc = MSI_DatabaseOpenViewW(package->db, translator_query, &view);
5118 if (rc != ERROR_SUCCESS)
5119 return ERROR_SUCCESS;
5121 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCTranslator, package);
5122 msiobj_release(&view->hdr);
5124 rc = MSI_DatabaseOpenViewW(package->db, source_query, &view);
5125 if (rc != ERROR_SUCCESS)
5126 return ERROR_SUCCESS;
5128 rc = MSI_IterateRecords(view, NULL, ITERATE_InstallODBCDataSource, package);
5129 msiobj_release(&view->hdr);
5131 return rc;
5134 #define ENV_ACT_SETALWAYS 0x1
5135 #define ENV_ACT_SETABSENT 0x2
5136 #define ENV_ACT_REMOVE 0x4
5137 #define ENV_ACT_REMOVEMATCH 0x8
5139 #define ENV_MOD_MACHINE 0x20000000
5140 #define ENV_MOD_APPEND 0x40000000
5141 #define ENV_MOD_PREFIX 0x80000000
5142 #define ENV_MOD_MASK 0xC0000000
5144 #define check_flag_combo(x, y) ((x) & ~(y)) == (y)
5146 static LONG env_set_flags( LPCWSTR *name, LPCWSTR *value, DWORD *flags )
5148 LPCWSTR cptr = *name;
5149 LPCWSTR ptr = *value;
5151 static const WCHAR prefix[] = {'[','~',']',0};
5152 static const int prefix_len = 3;
5154 *flags = 0;
5155 while (*cptr)
5157 if (*cptr == '=')
5158 *flags |= ENV_ACT_SETALWAYS;
5159 else if (*cptr == '+')
5160 *flags |= ENV_ACT_SETABSENT;
5161 else if (*cptr == '-')
5162 *flags |= ENV_ACT_REMOVE;
5163 else if (*cptr == '!')
5164 *flags |= ENV_ACT_REMOVEMATCH;
5165 else if (*cptr == '*')
5166 *flags |= ENV_MOD_MACHINE;
5167 else
5168 break;
5170 cptr++;
5171 (*name)++;
5174 if (!*cptr)
5176 ERR("Missing environment variable\n");
5177 return ERROR_FUNCTION_FAILED;
5180 if (!strncmpW(ptr, prefix, prefix_len))
5182 *flags |= ENV_MOD_APPEND;
5183 *value += lstrlenW(prefix);
5185 else if (lstrlenW(*value) >= prefix_len)
5187 ptr += lstrlenW(ptr) - prefix_len;
5188 if (!lstrcmpW(ptr, prefix))
5190 *flags |= ENV_MOD_PREFIX;
5191 /* the "[~]" will be removed by deformat_string */;
5195 if (!*flags ||
5196 check_flag_combo(*flags, ENV_ACT_SETALWAYS | ENV_ACT_SETABSENT) ||
5197 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETABSENT) ||
5198 check_flag_combo(*flags, ENV_ACT_REMOVEMATCH | ENV_ACT_SETALWAYS) ||
5199 check_flag_combo(*flags, ENV_ACT_SETABSENT | ENV_MOD_MASK))
5201 ERR("Invalid flags: %08x\n", *flags);
5202 return ERROR_FUNCTION_FAILED;
5205 return ERROR_SUCCESS;
5208 static UINT ITERATE_WriteEnvironmentString( MSIRECORD *rec, LPVOID param )
5210 MSIPACKAGE *package = param;
5211 LPCWSTR name, value;
5212 LPWSTR data = NULL, newval = NULL;
5213 LPWSTR deformatted = NULL, ptr;
5214 DWORD flags, type, size;
5215 LONG res;
5216 HKEY env = NULL, root;
5217 LPCWSTR environment;
5219 static const WCHAR user_env[] =
5220 {'E','n','v','i','r','o','n','m','e','n','t',0};
5221 static const WCHAR machine_env[] =
5222 {'S','y','s','t','e','m','\\',
5223 'C','u','r','r','e','n','t','C','o','n','t','r','o','l','S','e','t','\\',
5224 'C','o','n','t','r','o','l','\\',
5225 'S','e','s','s','i','o','n',' ','M','a','n','a','g','e','r','\\',
5226 'E','n','v','i','r','o','n','m','e','n','t',0};
5227 static const WCHAR semicolon[] = {';',0};
5229 name = MSI_RecordGetString(rec, 2);
5230 value = MSI_RecordGetString(rec, 3);
5232 res = env_set_flags(&name, &value, &flags);
5233 if (res != ERROR_SUCCESS)
5234 goto done;
5236 deformat_string(package, value, &deformatted);
5237 if (!deformatted)
5239 res = ERROR_OUTOFMEMORY;
5240 goto done;
5243 value = deformatted;
5245 if (flags & ENV_MOD_MACHINE)
5247 environment = machine_env;
5248 root = HKEY_LOCAL_MACHINE;
5250 else
5252 environment = user_env;
5253 root = HKEY_CURRENT_USER;
5256 res = RegCreateKeyExW(root, environment, 0, NULL, 0,
5257 KEY_ALL_ACCESS, NULL, &env, NULL);
5258 if (res != ERROR_SUCCESS)
5259 goto done;
5261 if (flags & ENV_ACT_REMOVE)
5262 FIXME("Not removing environment variable on uninstall!\n");
5264 size = 0;
5265 res = RegQueryValueExW(env, name, NULL, &type, NULL, &size);
5266 if ((res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND) ||
5267 (res == ERROR_SUCCESS && type != REG_SZ && type != REG_EXPAND_SZ))
5268 goto done;
5270 if (res != ERROR_FILE_NOT_FOUND)
5272 if (flags & ENV_ACT_SETABSENT)
5274 res = ERROR_SUCCESS;
5275 goto done;
5278 data = msi_alloc(size);
5279 if (!data)
5281 RegCloseKey(env);
5282 return ERROR_OUTOFMEMORY;
5285 res = RegQueryValueExW(env, name, NULL, &type, (LPVOID)data, &size);
5286 if (res != ERROR_SUCCESS)
5287 goto done;
5289 if (flags & ENV_ACT_REMOVEMATCH && (!value || !lstrcmpW(data, value)))
5291 res = RegDeleteKeyW(env, name);
5292 goto done;
5295 size = (lstrlenW(value) + 1 + size) * sizeof(WCHAR);
5296 newval = msi_alloc(size);
5297 ptr = newval;
5298 if (!newval)
5300 res = ERROR_OUTOFMEMORY;
5301 goto done;
5304 if (!(flags & ENV_MOD_MASK))
5305 lstrcpyW(newval, value);
5306 else
5308 if (flags & ENV_MOD_PREFIX)
5310 lstrcpyW(newval, value);
5311 lstrcatW(newval, semicolon);
5312 ptr = newval + lstrlenW(value) + 1;
5315 lstrcpyW(ptr, data);
5317 if (flags & ENV_MOD_APPEND)
5319 lstrcatW(newval, semicolon);
5320 lstrcatW(newval, value);
5324 else
5326 size = (lstrlenW(value) + 1) * sizeof(WCHAR);
5327 newval = msi_alloc(size);
5328 if (!newval)
5330 res = ERROR_OUTOFMEMORY;
5331 goto done;
5334 lstrcpyW(newval, value);
5337 TRACE("setting %s to %s\n", debugstr_w(name), debugstr_w(newval));
5338 res = RegSetValueExW(env, name, 0, type, (LPVOID)newval, size);
5340 done:
5341 if (env) RegCloseKey(env);
5342 msi_free(deformatted);
5343 msi_free(data);
5344 msi_free(newval);
5345 return res;
5348 static UINT ACTION_WriteEnvironmentStrings( MSIPACKAGE *package )
5350 UINT rc;
5351 MSIQUERY * view;
5352 static const WCHAR ExecSeqQuery[] =
5353 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5354 '`','E','n','v','i','r','o','n','m','e','n','t','`',0};
5355 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5356 if (rc != ERROR_SUCCESS)
5357 return ERROR_SUCCESS;
5359 rc = MSI_IterateRecords(view, NULL, ITERATE_WriteEnvironmentString, package);
5360 msiobj_release(&view->hdr);
5362 return rc;
5365 #define is_dot_dir(x) ((x[0] == '.') && ((x[1] == 0) || ((x[1] == '.') && (x[2] == 0))))
5367 typedef struct
5369 struct list entry;
5370 LPWSTR sourcename;
5371 LPWSTR destname;
5372 LPWSTR source;
5373 LPWSTR dest;
5374 } FILE_LIST;
5376 static BOOL msi_move_file(LPCWSTR source, LPCWSTR dest, int options)
5378 BOOL ret;
5380 if (GetFileAttributesW(source) == FILE_ATTRIBUTE_DIRECTORY ||
5381 GetFileAttributesW(dest) == FILE_ATTRIBUTE_DIRECTORY)
5383 WARN("Source or dest is directory, not moving\n");
5384 return FALSE;
5387 if (options == msidbMoveFileOptionsMove)
5389 TRACE("moving %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5390 ret = MoveFileExW(source, dest, MOVEFILE_REPLACE_EXISTING);
5391 if (!ret)
5393 WARN("MoveFile failed: %d\n", GetLastError());
5394 return FALSE;
5397 else
5399 TRACE("copying %s -> %s\n", debugstr_w(source), debugstr_w(dest));
5400 ret = CopyFileW(source, dest, FALSE);
5401 if (!ret)
5403 WARN("CopyFile failed: %d\n", GetLastError());
5404 return FALSE;
5408 return TRUE;
5411 static LPWSTR wildcard_to_file(LPWSTR wildcard, LPWSTR filename)
5413 LPWSTR path, ptr;
5414 DWORD dirlen, pathlen;
5416 ptr = strrchrW(wildcard, '\\');
5417 dirlen = ptr - wildcard + 1;
5419 pathlen = dirlen + lstrlenW(filename) + 1;
5420 path = msi_alloc(pathlen * sizeof(WCHAR));
5422 lstrcpynW(path, wildcard, dirlen + 1);
5423 lstrcatW(path, filename);
5425 return path;
5428 static void free_file_entry(FILE_LIST *file)
5430 msi_free(file->source);
5431 msi_free(file->dest);
5432 msi_free(file);
5435 static void free_list(FILE_LIST *list)
5437 while (!list_empty(&list->entry))
5439 FILE_LIST *file = LIST_ENTRY(list_head(&list->entry), FILE_LIST, entry);
5441 list_remove(&file->entry);
5442 free_file_entry(file);
5446 static BOOL add_wildcard(FILE_LIST *files, LPWSTR source, LPWSTR dest)
5448 FILE_LIST *new, *file;
5449 LPWSTR ptr, filename;
5450 DWORD size;
5452 new = msi_alloc_zero(sizeof(FILE_LIST));
5453 if (!new)
5454 return FALSE;
5456 new->source = strdupW(source);
5457 ptr = strrchrW(dest, '\\') + 1;
5458 filename = strrchrW(new->source, '\\') + 1;
5460 new->sourcename = filename;
5462 if (*ptr)
5463 new->destname = ptr;
5464 else
5465 new->destname = new->sourcename;
5467 size = (ptr - dest) + lstrlenW(filename) + 1;
5468 new->dest = msi_alloc(size * sizeof(WCHAR));
5469 if (!new->dest)
5471 free_file_entry(new);
5472 return FALSE;
5475 lstrcpynW(new->dest, dest, ptr - dest + 1);
5476 lstrcatW(new->dest, filename);
5478 if (list_empty(&files->entry))
5480 list_add_head(&files->entry, &new->entry);
5481 return TRUE;
5484 LIST_FOR_EACH_ENTRY(file, &files->entry, FILE_LIST, entry)
5486 if (lstrcmpW(source, file->source) < 0)
5488 list_add_before(&file->entry, &new->entry);
5489 return TRUE;
5493 list_add_after(&file->entry, &new->entry);
5494 return TRUE;
5497 static BOOL move_files_wildcard(LPWSTR source, LPWSTR dest, int options)
5499 WIN32_FIND_DATAW wfd;
5500 HANDLE hfile;
5501 LPWSTR path;
5502 BOOL res;
5503 FILE_LIST files, *file;
5504 DWORD size;
5506 hfile = FindFirstFileW(source, &wfd);
5507 if (hfile == INVALID_HANDLE_VALUE) return FALSE;
5509 list_init(&files.entry);
5511 for (res = TRUE; res; res = FindNextFileW(hfile, &wfd))
5513 if (is_dot_dir(wfd.cFileName)) continue;
5515 path = wildcard_to_file(source, wfd.cFileName);
5516 if (!path)
5518 res = FALSE;
5519 goto done;
5522 add_wildcard(&files, path, dest);
5523 msi_free(path);
5526 /* no files match the wildcard */
5527 if (list_empty(&files.entry))
5528 goto done;
5530 /* only the first wildcard match gets renamed to dest */
5531 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5532 size = (strrchrW(file->dest, '\\') - file->dest) + lstrlenW(file->destname) + 2;
5533 file->dest = msi_realloc(file->dest, size * sizeof(WCHAR));
5534 if (!file->dest)
5536 res = FALSE;
5537 goto done;
5540 lstrcpyW(strrchrW(file->dest, '\\') + 1, file->destname);
5542 while (!list_empty(&files.entry))
5544 file = LIST_ENTRY(list_head(&files.entry), FILE_LIST, entry);
5546 msi_move_file((LPCWSTR)file->source, (LPCWSTR)file->dest, options);
5548 list_remove(&file->entry);
5549 free_file_entry(file);
5552 res = TRUE;
5554 done:
5555 free_list(&files);
5556 FindClose(hfile);
5557 return res;
5560 static UINT ITERATE_MoveFiles( MSIRECORD *rec, LPVOID param )
5562 MSIPACKAGE *package = param;
5563 MSICOMPONENT *comp;
5564 LPCWSTR sourcename;
5565 LPWSTR destname = NULL;
5566 LPWSTR sourcedir = NULL, destdir = NULL;
5567 LPWSTR source = NULL, dest = NULL;
5568 int options;
5569 DWORD size;
5570 BOOL ret, wildcards;
5572 static const WCHAR backslash[] = {'\\',0};
5574 comp = get_loaded_component(package, MSI_RecordGetString(rec, 2));
5575 if (!comp || !comp->Enabled ||
5576 !(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5578 TRACE("Component not set for install, not moving file\n");
5579 return ERROR_SUCCESS;
5582 sourcename = MSI_RecordGetString(rec, 3);
5583 options = MSI_RecordGetInteger(rec, 7);
5585 sourcedir = msi_dup_property(package, MSI_RecordGetString(rec, 5));
5586 if (!sourcedir)
5587 goto done;
5589 destdir = msi_dup_property(package, MSI_RecordGetString(rec, 6));
5590 if (!destdir)
5591 goto done;
5593 if (!sourcename)
5595 if (GetFileAttributesW(sourcedir) == INVALID_FILE_ATTRIBUTES)
5596 goto done;
5598 source = strdupW(sourcedir);
5599 if (!source)
5600 goto done;
5602 else
5604 size = lstrlenW(sourcedir) + lstrlenW(sourcename) + 2;
5605 source = msi_alloc(size * sizeof(WCHAR));
5606 if (!source)
5607 goto done;
5609 lstrcpyW(source, sourcedir);
5610 if (source[lstrlenW(source) - 1] != '\\')
5611 lstrcatW(source, backslash);
5612 lstrcatW(source, sourcename);
5615 wildcards = strchrW(source, '*') || strchrW(source, '?');
5617 if (MSI_RecordIsNull(rec, 4))
5619 if (!wildcards)
5621 destname = strdupW(sourcename);
5622 if (!destname)
5623 goto done;
5626 else
5628 destname = strdupW(MSI_RecordGetString(rec, 4));
5629 if (destname)
5630 reduce_to_longfilename(destname);
5633 size = 0;
5634 if (destname)
5635 size = lstrlenW(destname);
5637 size += lstrlenW(destdir) + 2;
5638 dest = msi_alloc(size * sizeof(WCHAR));
5639 if (!dest)
5640 goto done;
5642 lstrcpyW(dest, destdir);
5643 if (dest[lstrlenW(dest) - 1] != '\\')
5644 lstrcatW(dest, backslash);
5646 if (destname)
5647 lstrcatW(dest, destname);
5649 if (GetFileAttributesW(destdir) == INVALID_FILE_ATTRIBUTES)
5651 ret = CreateDirectoryW(destdir, NULL);
5652 if (!ret)
5654 WARN("CreateDirectory failed: %d\n", GetLastError());
5655 return ERROR_SUCCESS;
5659 if (!wildcards)
5660 msi_move_file(source, dest, options);
5661 else
5662 move_files_wildcard(source, dest, options);
5664 done:
5665 msi_free(sourcedir);
5666 msi_free(destdir);
5667 msi_free(destname);
5668 msi_free(source);
5669 msi_free(dest);
5671 return ERROR_SUCCESS;
5674 static UINT ACTION_MoveFiles( MSIPACKAGE *package )
5676 UINT rc;
5677 MSIQUERY *view;
5679 static const WCHAR ExecSeqQuery[] =
5680 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5681 '`','M','o','v','e','F','i','l','e','`',0};
5683 rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
5684 if (rc != ERROR_SUCCESS)
5685 return ERROR_SUCCESS;
5687 rc = MSI_IterateRecords(view, NULL, ITERATE_MoveFiles, package);
5688 msiobj_release(&view->hdr);
5690 return rc;
5693 typedef struct tagMSIASSEMBLY
5695 struct list entry;
5696 MSICOMPONENT *component;
5697 MSIFEATURE *feature;
5698 MSIFILE *file;
5699 LPWSTR manifest;
5700 LPWSTR application;
5701 DWORD attributes;
5702 BOOL installed;
5703 } MSIASSEMBLY;
5705 static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
5706 DWORD dwReserved);
5707 static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
5708 LPVOID pvReserved, HMODULE *phModDll);
5710 static BOOL init_functionpointers(void)
5712 HRESULT hr;
5713 HMODULE hfusion;
5714 HMODULE hmscoree;
5716 static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
5718 hmscoree = LoadLibraryA("mscoree.dll");
5719 if (!hmscoree)
5721 WARN("mscoree.dll not available\n");
5722 return FALSE;
5725 pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
5726 if (!pLoadLibraryShim)
5728 WARN("LoadLibraryShim not available\n");
5729 FreeLibrary(hmscoree);
5730 return FALSE;
5733 hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
5734 if (FAILED(hr))
5736 WARN("fusion.dll not available\n");
5737 FreeLibrary(hmscoree);
5738 return FALSE;
5741 pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
5743 FreeLibrary(hmscoree);
5744 return TRUE;
5747 static UINT install_assembly(MSIPACKAGE *package, MSIASSEMBLY *assembly,
5748 LPWSTR path)
5750 IAssemblyCache *cache;
5751 HRESULT hr;
5752 UINT r = ERROR_FUNCTION_FAILED;
5754 TRACE("installing assembly: %s\n", debugstr_w(path));
5756 if (assembly->feature)
5757 msi_feature_set_state(package, assembly->feature, INSTALLSTATE_LOCAL);
5759 if (assembly->manifest)
5760 FIXME("Manifest unhandled\n");
5762 if (assembly->application)
5764 FIXME("Assembly should be privately installed\n");
5765 return ERROR_SUCCESS;
5768 if (assembly->attributes == msidbAssemblyAttributesWin32)
5770 FIXME("Win32 assemblies not handled\n");
5771 return ERROR_SUCCESS;
5774 if (!init_functionpointers() || !pCreateAssemblyCache)
5775 return ERROR_FUNCTION_FAILED;
5777 hr = pCreateAssemblyCache(&cache, 0);
5778 if (FAILED(hr))
5779 goto done;
5781 hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
5782 if (FAILED(hr))
5783 ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
5785 r = ERROR_SUCCESS;
5787 done:
5788 IAssemblyCache_Release(cache);
5789 return r;
5792 typedef struct tagASSEMBLY_LIST
5794 MSIPACKAGE *package;
5795 struct list *assemblies;
5796 } ASSEMBLY_LIST;
5798 static UINT load_assembly(MSIRECORD *rec, LPVOID param)
5800 ASSEMBLY_LIST *list = (ASSEMBLY_LIST *)param;
5801 MSIASSEMBLY *assembly;
5803 assembly = msi_alloc_zero(sizeof(MSIASSEMBLY));
5804 if (!assembly)
5805 return ERROR_OUTOFMEMORY;
5807 assembly->component = get_loaded_component(list->package, MSI_RecordGetString(rec, 1));
5809 if (!assembly->component || !assembly->component->Enabled ||
5810 !(assembly->component->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
5812 TRACE("Component not set for install, not publishing assembly\n");
5813 msi_free(assembly);
5814 return ERROR_SUCCESS;
5817 assembly->feature = find_feature_by_name(list->package, MSI_RecordGetString(rec, 2));
5818 assembly->file = msi_find_file(list->package, assembly->component->KeyPath);
5820 if (!assembly->file)
5822 ERR("File %s not found\n", debugstr_w(assembly->component->KeyPath));
5823 return ERROR_FUNCTION_FAILED;
5826 assembly->manifest = strdupW(MSI_RecordGetString(rec, 3));
5827 assembly->application = strdupW(MSI_RecordGetString(rec, 4));
5828 assembly->attributes = MSI_RecordGetInteger(rec, 5);
5829 assembly->installed = FALSE;
5831 list_add_head(list->assemblies, &assembly->entry);
5833 return ERROR_SUCCESS;
5836 static UINT load_assemblies(MSIPACKAGE *package, struct list *assemblies)
5838 MSIQUERY *view;
5839 ASSEMBLY_LIST list;
5840 UINT r;
5842 static const WCHAR query[] =
5843 {'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
5844 '`','M','s','i','A','s','s','e','m','b','l','y','`',0};
5846 r = MSI_DatabaseOpenViewW(package->db, query, &view);
5847 if (r != ERROR_SUCCESS)
5848 return ERROR_SUCCESS;
5850 list.package = package;
5851 list.assemblies = assemblies;
5853 r = MSI_IterateRecords(view, NULL, load_assembly, &list);
5854 msiobj_release(&view->hdr);
5856 return r;
5859 static void free_assemblies(struct list *assemblies)
5861 struct list *item, *cursor;
5863 LIST_FOR_EACH_SAFE(item, cursor, assemblies)
5865 MSIASSEMBLY *assembly = LIST_ENTRY(item, MSIASSEMBLY, entry);
5867 list_remove(&assembly->entry);
5868 msi_free(assembly->application);
5869 msi_free(assembly->manifest);
5870 msi_free(assembly);
5874 static BOOL find_assembly(struct list *assemblies, LPCWSTR file, MSIASSEMBLY **out)
5876 MSIASSEMBLY *assembly;
5878 LIST_FOR_EACH_ENTRY(assembly, assemblies, MSIASSEMBLY, entry)
5880 if (!lstrcmpW(assembly->file->File, file))
5882 *out = assembly;
5883 return TRUE;
5887 return FALSE;
5890 static BOOL installassembly_cb(MSIPACKAGE *package, LPCWSTR file, DWORD action,
5891 LPWSTR *path, DWORD *attrs, PVOID user)
5893 MSIASSEMBLY *assembly;
5894 WCHAR temppath[MAX_PATH];
5895 struct list *assemblies = (struct list *)user;
5896 UINT r;
5898 if (!find_assembly(assemblies, file, &assembly))
5899 return FALSE;
5901 GetTempPathW(MAX_PATH, temppath);
5902 PathAddBackslashW(temppath);
5903 lstrcatW(temppath, assembly->file->FileName);
5905 if (action == MSICABEXTRACT_BEGINEXTRACT)
5907 if (assembly->installed)
5908 return FALSE;
5910 *path = strdupW(temppath);
5911 *attrs = assembly->file->Attributes;
5913 else if (action == MSICABEXTRACT_FILEEXTRACTED)
5915 assembly->installed = TRUE;
5917 r = install_assembly(package, assembly, temppath);
5918 if (r != ERROR_SUCCESS)
5919 ERR("Failed to install assembly\n");
5922 return TRUE;
5925 static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
5927 UINT r;
5928 struct list assemblies = LIST_INIT(assemblies);
5929 MSIASSEMBLY *assembly;
5930 MSIMEDIAINFO *mi;
5932 r = load_assemblies(package, &assemblies);
5933 if (r != ERROR_SUCCESS)
5934 goto done;
5936 if (list_empty(&assemblies))
5937 goto done;
5939 mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
5940 if (!mi)
5942 r = ERROR_OUTOFMEMORY;
5943 goto done;
5946 LIST_FOR_EACH_ENTRY(assembly, &assemblies, MSIASSEMBLY, entry)
5948 if (assembly->installed && !mi->is_continuous)
5949 continue;
5951 if (assembly->file->Sequence > mi->last_sequence || mi->is_continuous ||
5952 (assembly->file->IsCompressed && !mi->is_extracted))
5954 MSICABDATA data;
5956 r = ready_media(package, assembly->file, mi);
5957 if (r != ERROR_SUCCESS)
5959 ERR("Failed to ready media\n");
5960 break;
5963 data.mi = mi;
5964 data.package = package;
5965 data.cb = installassembly_cb;
5966 data.user = &assemblies;
5968 if (assembly->file->IsCompressed &&
5969 !msi_cabextract(package, mi, &data))
5971 ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
5972 r = ERROR_FUNCTION_FAILED;
5973 break;
5977 if (!assembly->file->IsCompressed)
5979 LPWSTR source = resolve_file_source(package, assembly->file);
5981 r = install_assembly(package, assembly, source);
5982 if (r != ERROR_SUCCESS)
5983 ERR("Failed to install assembly\n");
5985 msi_free(source);
5988 /* FIXME: write Installer assembly reg values */
5991 done:
5992 free_assemblies(&assemblies);
5993 return r;
5996 static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
5997 LPCSTR action, LPCWSTR table )
5999 static const WCHAR query[] = {
6000 'S','E','L','E','C','T',' ','*',' ',
6001 'F','R','O','M',' ','`','%','s','`',0 };
6002 MSIQUERY *view = NULL;
6003 DWORD count = 0;
6004 UINT r;
6006 r = MSI_OpenQuery( package->db, &view, query, table );
6007 if (r == ERROR_SUCCESS)
6009 r = MSI_IterateRecords(view, &count, NULL, package);
6010 msiobj_release(&view->hdr);
6013 if (count)
6014 FIXME("%s -> %u ignored %s table values\n",
6015 action, count, debugstr_w(table));
6017 return ERROR_SUCCESS;
6020 static UINT ACTION_AllocateRegistrySpace( MSIPACKAGE *package )
6022 TRACE("%p\n", package);
6023 return ERROR_SUCCESS;
6026 static UINT ACTION_RemoveIniValues( MSIPACKAGE *package )
6028 static const WCHAR table[] =
6029 {'R','e','m','o','v','e','I','n','i','F','i','l','e',0 };
6030 return msi_unimplemented_action_stub( package, "RemoveIniValues", table );
6033 static UINT ACTION_PatchFiles( MSIPACKAGE *package )
6035 static const WCHAR table[] = { 'P','a','t','c','h',0 };
6036 return msi_unimplemented_action_stub( package, "PatchFiles", table );
6039 static UINT ACTION_BindImage( MSIPACKAGE *package )
6041 static const WCHAR table[] = { 'B','i','n','d','I','m','a','g','e',0 };
6042 return msi_unimplemented_action_stub( package, "BindImage", table );
6045 static UINT ACTION_IsolateComponents( MSIPACKAGE *package )
6047 static const WCHAR table[] = {
6048 'I','s','o','l','a','t','e','C','o','m','p','o','n','e','n','t',0 };
6049 return msi_unimplemented_action_stub( package, "IsolateComponents", table );
6052 static UINT ACTION_MigrateFeatureStates( MSIPACKAGE *package )
6054 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6055 return msi_unimplemented_action_stub( package, "MigrateFeatureStates", table );
6058 static UINT ACTION_SelfUnregModules( MSIPACKAGE *package )
6060 static const WCHAR table[] = { 'S','e','l','f','R','e','g',0 };
6061 return msi_unimplemented_action_stub( package, "SelfUnregModules", table );
6064 static UINT ACTION_DeleteServices( MSIPACKAGE *package )
6066 static const WCHAR table[] = {
6067 'S','e','r','v','i','c','e','C','o','n','t','r','o','l',0 };
6068 return msi_unimplemented_action_stub( package, "DeleteServices", table );
6070 static UINT ACTION_ValidateProductID( MSIPACKAGE *package )
6072 static const WCHAR table[] = {
6073 'P','r','o','d','u','c','t','I','D',0 };
6074 return msi_unimplemented_action_stub( package, "ValidateProductID", table );
6077 static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
6079 static const WCHAR table[] = {
6080 'E','n','v','i','r','o','n','m','e','n','t',0 };
6081 return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
6084 static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
6086 static const WCHAR table[] = {
6087 'M','s','i','A','s','s','e','m','b','l','y',0 };
6088 return msi_unimplemented_action_stub( package, "MsiUnpublishAssemblies", table );
6091 static UINT ACTION_UnregisterFonts( MSIPACKAGE *package )
6093 static const WCHAR table[] = { 'F','o','n','t',0 };
6094 return msi_unimplemented_action_stub( package, "UnregisterFonts", table );
6097 static UINT ACTION_RMCCPSearch( MSIPACKAGE *package )
6099 static const WCHAR table[] = { 'C','C','P','S','e','a','r','c','h',0 };
6100 return msi_unimplemented_action_stub( package, "RMCCPSearch", table );
6103 static UINT ACTION_RegisterComPlus( MSIPACKAGE *package )
6105 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6106 return msi_unimplemented_action_stub( package, "RegisterComPlus", table );
6109 static UINT ACTION_UnregisterComPlus( MSIPACKAGE *package )
6111 static const WCHAR table[] = { 'C','o','m','p','l','u','s',0 };
6112 return msi_unimplemented_action_stub( package, "UnregisterComPlus", table );
6115 static UINT ACTION_InstallSFPCatalogFile( MSIPACKAGE *package )
6117 static const WCHAR table[] = { 'S','F','P','C','a','t','a','l','o','g',0 };
6118 return msi_unimplemented_action_stub( package, "InstallSFPCatalogFile", table );
6121 static UINT ACTION_RemoveDuplicateFiles( MSIPACKAGE *package )
6123 static const WCHAR table[] = { 'D','u','p','l','i','c','a','t','e','F','i','l','e',0 };
6124 return msi_unimplemented_action_stub( package, "RemoveDuplicateFiles", table );
6127 static UINT ACTION_RemoveExistingProducts( MSIPACKAGE *package )
6129 static const WCHAR table[] = { 'U','p','g','r','a','d','e',0 };
6130 return msi_unimplemented_action_stub( package, "RemoveExistingProducts", table );
6133 static UINT ACTION_RemoveFolders( MSIPACKAGE *package )
6135 static const WCHAR table[] = { 'C','r','e','a','t','e','F','o','l','d','e','r',0 };
6136 return msi_unimplemented_action_stub( package, "RemoveFolders", table );
6139 static UINT ACTION_RemoveODBC( MSIPACKAGE *package )
6141 static const WCHAR table[] = { 'O','D','B','C','D','r','i','v','e','r',0 };
6142 return msi_unimplemented_action_stub( package, "RemoveODBC", table );
6145 static UINT ACTION_RemoveRegistryValues( MSIPACKAGE *package )
6147 static const WCHAR table[] = { 'R','e','m','o','v','e','R','e','g','i','s','t','r','y',0 };
6148 return msi_unimplemented_action_stub( package, "RemoveRegistryValues", table );
6151 static UINT ACTION_RemoveShortcuts( MSIPACKAGE *package )
6153 static const WCHAR table[] = { 'S','h','o','r','t','c','u','t',0 };
6154 return msi_unimplemented_action_stub( package, "RemoveShortcuts", table );
6157 static UINT ACTION_UnpublishComponents( MSIPACKAGE *package )
6159 static const WCHAR table[] = { 'P','u','b','l','i','s','h','C','o','m','p','o','n','e','n','t',0 };
6160 return msi_unimplemented_action_stub( package, "UnpublishComponents", table );
6163 static UINT ACTION_UnregisterClassInfo( MSIPACKAGE *package )
6165 static const WCHAR table[] = { 'A','p','p','I','d',0 };
6166 return msi_unimplemented_action_stub( package, "UnregisterClassInfo", table );
6169 static UINT ACTION_UnregisterExtensionInfo( MSIPACKAGE *package )
6171 static const WCHAR table[] = { 'E','x','t','e','n','s','i','o','n',0 };
6172 return msi_unimplemented_action_stub( package, "UnregisterExtensionInfo", table );
6175 static UINT ACTION_UnregisterMIMEInfo( MSIPACKAGE *package )
6177 static const WCHAR table[] = { 'M','I','M','E',0 };
6178 return msi_unimplemented_action_stub( package, "UnregisterMIMEInfo", table );
6181 static UINT ACTION_UnregisterProgIdInfo( MSIPACKAGE *package )
6183 static const WCHAR table[] = { 'P','r','o','g','I','d',0 };
6184 return msi_unimplemented_action_stub( package, "UnregisterProgIdInfo", table );
6187 static UINT ACTION_UnregisterTypeLibraries( MSIPACKAGE *package )
6189 static const WCHAR table[] = { 'T','y','p','e','L','i','b',0 };
6190 return msi_unimplemented_action_stub( package, "UnregisterTypeLibraries", table );
6193 static const struct _actions StandardActions[] = {
6194 { szAllocateRegistrySpace, ACTION_AllocateRegistrySpace },
6195 { szAppSearch, ACTION_AppSearch },
6196 { szBindImage, ACTION_BindImage },
6197 { szCCPSearch, ACTION_CCPSearch },
6198 { szCostFinalize, ACTION_CostFinalize },
6199 { szCostInitialize, ACTION_CostInitialize },
6200 { szCreateFolders, ACTION_CreateFolders },
6201 { szCreateShortcuts, ACTION_CreateShortcuts },
6202 { szDeleteServices, ACTION_DeleteServices },
6203 { szDisableRollback, NULL },
6204 { szDuplicateFiles, ACTION_DuplicateFiles },
6205 { szExecuteAction, ACTION_ExecuteAction },
6206 { szFileCost, ACTION_FileCost },
6207 { szFindRelatedProducts, ACTION_FindRelatedProducts },
6208 { szForceReboot, ACTION_ForceReboot },
6209 { szInstallAdminPackage, NULL },
6210 { szInstallExecute, ACTION_InstallExecute },
6211 { szInstallExecuteAgain, ACTION_InstallExecute },
6212 { szInstallFiles, ACTION_InstallFiles},
6213 { szInstallFinalize, ACTION_InstallFinalize },
6214 { szInstallInitialize, ACTION_InstallInitialize },
6215 { szInstallSFPCatalogFile, ACTION_InstallSFPCatalogFile },
6216 { szInstallValidate, ACTION_InstallValidate },
6217 { szIsolateComponents, ACTION_IsolateComponents },
6218 { szLaunchConditions, ACTION_LaunchConditions },
6219 { szMigrateFeatureStates, ACTION_MigrateFeatureStates },
6220 { szMoveFiles, ACTION_MoveFiles },
6221 { szMsiPublishAssemblies, ACTION_MsiPublishAssemblies },
6222 { szMsiUnpublishAssemblies, ACTION_MsiUnpublishAssemblies },
6223 { szInstallODBC, ACTION_InstallODBC },
6224 { szInstallServices, ACTION_InstallServices },
6225 { szPatchFiles, ACTION_PatchFiles },
6226 { szProcessComponents, ACTION_ProcessComponents },
6227 { szPublishComponents, ACTION_PublishComponents },
6228 { szPublishFeatures, ACTION_PublishFeatures },
6229 { szPublishProduct, ACTION_PublishProduct },
6230 { szRegisterClassInfo, ACTION_RegisterClassInfo },
6231 { szRegisterComPlus, ACTION_RegisterComPlus},
6232 { szRegisterExtensionInfo, ACTION_RegisterExtensionInfo },
6233 { szRegisterFonts, ACTION_RegisterFonts },
6234 { szRegisterMIMEInfo, ACTION_RegisterMIMEInfo },
6235 { szRegisterProduct, ACTION_RegisterProduct },
6236 { szRegisterProgIdInfo, ACTION_RegisterProgIdInfo },
6237 { szRegisterTypeLibraries, ACTION_RegisterTypeLibraries },
6238 { szRegisterUser, ACTION_RegisterUser },
6239 { szRemoveDuplicateFiles, ACTION_RemoveDuplicateFiles },
6240 { szRemoveEnvironmentStrings, ACTION_RemoveEnvironmentStrings },
6241 { szRemoveExistingProducts, ACTION_RemoveExistingProducts },
6242 { szRemoveFiles, ACTION_RemoveFiles },
6243 { szRemoveFolders, ACTION_RemoveFolders },
6244 { szRemoveIniValues, ACTION_RemoveIniValues },
6245 { szRemoveODBC, ACTION_RemoveODBC },
6246 { szRemoveRegistryValues, ACTION_RemoveRegistryValues },
6247 { szRemoveShortcuts, ACTION_RemoveShortcuts },
6248 { szResolveSource, ACTION_ResolveSource },
6249 { szRMCCPSearch, ACTION_RMCCPSearch },
6250 { szScheduleReboot, NULL },
6251 { szSelfRegModules, ACTION_SelfRegModules },
6252 { szSelfUnregModules, ACTION_SelfUnregModules },
6253 { szSetODBCFolders, NULL },
6254 { szStartServices, ACTION_StartServices },
6255 { szStopServices, ACTION_StopServices },
6256 { szUnpublishComponents, ACTION_UnpublishComponents },
6257 { szUnpublishFeatures, ACTION_UnpublishFeatures },
6258 { szUnregisterClassInfo, ACTION_UnregisterClassInfo },
6259 { szUnregisterComPlus, ACTION_UnregisterComPlus },
6260 { szUnregisterExtensionInfo, ACTION_UnregisterExtensionInfo },
6261 { szUnregisterFonts, ACTION_UnregisterFonts },
6262 { szUnregisterMIMEInfo, ACTION_UnregisterMIMEInfo },
6263 { szUnregisterProgIdInfo, ACTION_UnregisterProgIdInfo },
6264 { szUnregisterTypeLibraries, ACTION_UnregisterTypeLibraries },
6265 { szValidateProductID, ACTION_ValidateProductID },
6266 { szWriteEnvironmentStrings, ACTION_WriteEnvironmentStrings },
6267 { szWriteIniValues, ACTION_WriteIniValues },
6268 { szWriteRegistryValues, ACTION_WriteRegistryValues },
6269 { NULL, NULL },
6272 static BOOL ACTION_HandleStandardAction(MSIPACKAGE *package, LPCWSTR action,
6273 UINT* rc, BOOL force )
6275 BOOL ret = FALSE;
6276 BOOL run = force;
6277 int i;
6279 if (!run && !package->script->CurrentlyScripting)
6280 run = TRUE;
6282 if (!run)
6284 if (strcmpW(action,szInstallFinalize) == 0 ||
6285 strcmpW(action,szInstallExecute) == 0 ||
6286 strcmpW(action,szInstallExecuteAgain) == 0)
6287 run = TRUE;
6290 i = 0;
6291 while (StandardActions[i].action != NULL)
6293 if (strcmpW(StandardActions[i].action, action)==0)
6295 if (!run)
6297 ui_actioninfo(package, action, TRUE, 0);
6298 *rc = schedule_action(package,INSTALL_SCRIPT,action);
6299 ui_actioninfo(package, action, FALSE, *rc);
6301 else
6303 ui_actionstart(package, action);
6304 if (StandardActions[i].handler)
6306 *rc = StandardActions[i].handler(package);
6308 else
6310 FIXME("unhandled standard action %s\n",debugstr_w(action));
6311 *rc = ERROR_SUCCESS;
6314 ret = TRUE;
6315 break;
6317 i++;
6319 return ret;